diff --git a/.gitignore b/.gitignore index bf5ee6e01cd4..929054df5212 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,7 @@ modules.order !.gitignore !.kunitconfig !.mailmap +!.pylintrc !.rustfmt.toml # diff --git a/.mailmap b/.mailmap index b77cd34cf852..9642a2ee5614 100644 --- a/.mailmap +++ b/.mailmap @@ -138,6 +138,7 @@ Benjamin Poirier Benjamin Tissoires Benjamin Tissoires Benno Lossin +Bernard Metzler Bingwu Zhang Bingwu Zhang Bjorn Andersson @@ -163,6 +164,8 @@ Casey Connolly Casey Connolly Can Guo Carl Huang +Carl Vanderlip +Carl Vanderlip Carlos Bilbao Carlos Bilbao Carlos Bilbao @@ -197,6 +200,7 @@ Daniel Borkmann Daniel Borkmann Daniel Borkmann Daniel Borkmann +Danilo Krummrich David Brownell David Collins David Heidelberg @@ -222,6 +226,10 @@ Dmitry Safonov <0x7f454c46@gmail.com> Dmitry Safonov <0x7f454c46@gmail.com> Domen Puncer Douglas Gilbert +Drew Fustini + +Easwar Hariharan +Easwar Hariharan Ed L. Cashin Elliot Berman Enric Balletbo i Serra @@ -282,8 +290,10 @@ Gustavo Padovan Gustavo Padovan Hamza Mahfooz Hanjun Guo -Hans Verkuil -Hans Verkuil +Hans de Goede +Hans Verkuil +Hans Verkuil +Hans Verkuil Harry Yoo <42.hyeyoo@gmail.com> Heiko Carstens Heiko Carstens @@ -412,6 +422,7 @@ Kenneth W Chen Kenneth Westfield Kiran Gunda Kirill Tkhai +Kirill A. Shutemov Kishon Vijay Abraham I Konrad Dybcio Konrad Dybcio @@ -580,6 +591,7 @@ Nikolay Aleksandrov Nikolay Aleksandrov Nikolay Aleksandrov Nikolay Aleksandrov +Nobuhiro Iwamatsu Odelu Kukatla Oleksandr Natalenko Oleksij Rempel @@ -666,6 +678,7 @@ Muchun Song Ross Zwisler Rudolf Marek Rui Saraiva +Sachin Mokashi Sachin P Sant Sai Prakash Ranjan Sakari Ailus @@ -689,11 +702,16 @@ Sedat Dilek Senthilkumar N L Serge Hallyn Serge Hallyn +Sergey Senozhatsky +Sergey Senozhatsky +Sergey Senozhatsky +Sergey Senozhatsky Seth Forshee Shakeel Butt -Shannon Nelson -Shannon Nelson -Shannon Nelson +Shannon Nelson +Shannon Nelson +Shannon Nelson +Shannon Nelson Sharath Chandra Vurukala Shiraz Hashim Shuah Khan @@ -827,3 +845,6 @@ Yosry Ahmed Yusuke Goda Zack Rusin Zhu Yanjun +Zijun Hu +Zijun Hu +Zijun Hu diff --git a/CREDITS b/CREDITS index 45446ae322ec..a687c3c35c4c 100644 --- a/CREDITS +++ b/CREDITS @@ -1397,6 +1397,10 @@ N: Thomas Gleixner E: tglx@linutronix.de D: NAND flash hardware support, JFFS2 on NAND flash +N: Jérôme Glisse +E: jglisse@redhat.com +D: HMM - Heterogeneous Memory Management + N: Richard E. Gooch E: rgooch@atnf.csiro.au D: parent process death signal to children @@ -2981,6 +2985,11 @@ S: 521 Pleasant Valley Road S: Potsdam, New York 13676 S: USA +N: Shannon Nelson +E: sln@onemain.com +D: Worked on several network drivers including +D: ixgbe, i40e, ionic, pds_core, pds_vdpa, pds_fwctl + N: Dave Neuer E: dave.neuer@pobox.com D: Helped implement support for Compaq's H31xx series iPAQs @@ -3213,6 +3222,10 @@ D: AIC5800 IEEE 1394, RAW I/O on 1394 D: Starter of Linux1394 effort S: ask per mail for current address +N: Boris Pismenny +E: borisp@mellanox.com +D: Kernel TLS implementation and offload support. + N: Nicolas Pitre E: nico@fluxnic.net D: StrongARM SA1100 support integrator & hacker @@ -4159,6 +4172,9 @@ S: 1513 Brewster Dr. S: Carrollton, TX 75010 S: USA +N: Dave Watson +D: Kernel TLS implementation. + N: Tim Waugh E: tim@cyberelk.net D: Co-architect of the parallel-port sharing system @@ -4369,6 +4385,12 @@ S: 542 West 112th Street, 5N S: New York, New York 10025 S: USA +N: Masahiro Yamada +E: masahiroy@kernel.org +D: Kbuild Maintainer 2017-2025 +D: Kconfig Maintainer 2018-2025 +S: Japan + N: Li Yang E: leoli@freescale.com D: Freescale Highspeed USB device driver diff --git a/Documentation/ABI/README b/Documentation/ABI/README index ef0e6d11e919..315fffe1f831 100644 --- a/Documentation/ABI/README +++ b/Documentation/ABI/README @@ -46,7 +46,9 @@ Every file in these directories will contain the following information: What: Short description of the interface Date: Date created -KernelVersion: Kernel version this feature first showed up in. +KernelVersion: (Optional) Kernel version this feature first showed up in. + Note: git history often provides more accurate version + info, so this field may be omitted. Contact: Primary contact for this interface (may be a mailing list) Description: Long description of the interface and how to use it. Users: All users of this interface who wish to be notified when diff --git a/Documentation/ABI/obsolete/automount-tracefs-debugfs b/Documentation/ABI/obsolete/automount-tracefs-debugfs new file mode 100644 index 000000000000..a5196ec78cb5 --- /dev/null +++ b/Documentation/ABI/obsolete/automount-tracefs-debugfs @@ -0,0 +1,20 @@ +What: /sys/kernel/debug/tracing +Date: May 2008 +KernelVersion: 2.6.27 +Contact: linux-trace-kernel@vger.kernel.org +Description: + + The ftrace was first added to the kernel, its interface was placed + into the debugfs file system under the "tracing" directory. Access + to the files were in /sys/kernel/debug/tracing. As systems wanted + access to the tracing interface without having to enable debugfs, a + new interface was created called "tracefs". This was a stand alone + file system and was usually mounted in /sys/kernel/tracing. + + To allow older tooling to continue to operate, when mounting + debugfs, the tracefs file system would automatically get mounted in + the "tracing" directory of debugfs. The tracefs interface was added + in January 2015 in the v4.1 kernel. + + All tooling should now be using tracefs directly and the "tracing" + directory in debugfs should be removed by January 2030. diff --git a/Documentation/ABI/obsolete/sysfs-bus-iio b/Documentation/ABI/obsolete/sysfs-bus-iio index b64394b0b374..a13523561958 100644 --- a/Documentation/ABI/obsolete/sysfs-bus-iio +++ b/Documentation/ABI/obsolete/sysfs-bus-iio @@ -48,10 +48,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en @@ -73,10 +69,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_type What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type What: /sys/.../iio:deviceX/scan_elements/in_voltage_type What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type What: /sys/.../iio:deviceX/scan_elements/in_pressure_type @@ -110,10 +102,6 @@ Description: What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index diff --git a/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop b/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop new file mode 100644 index 000000000000..204c3f3a1d78 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop @@ -0,0 +1,10 @@ +What: /sys/devices/platform/samsung/battery_life_extender +Date: December 1, 2011 +KernelVersion: 3.3 +Contact: Corentin Chary +Description: Max battery charge level can be modified, battery cycle + life can be extended by reducing the max battery charge + level. + + - 0 means normal battery mode (100% charge) + - 1 means battery life extender mode (80% charge) diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index da1345d854b4..0d3f12c4dcbd 100644 --- a/Documentation/ABI/obsolete/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -19,14 +19,22 @@ Description: /export ... asks the kernel to export a GPIO to userspace /unexport ... to return a GPIO to the kernel /gpioN ... for each exported GPIO #N OR - / ... for a properly named GPIO line /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write: high, low /edge ... r/w as: none, falling, rising, both + /active_low ... r/w as: 0, 1 /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N - /label ... (r/o) descriptive, not necessarily unique + /label ... (r/o) descriptive chip name /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) + /gpio + /value ... always readable, writes fail for input GPIOs + /direction ... r/w as: in, out (default low); write: high, low + /chipX ... for each gpiochip; #X is the gpio device ID + /export ... asks the kernel to export a GPIO at HW offset X to userspace + /unexport ... to return a GPIO at HW offset X to the kernel + /label ... (r/o) descriptive chip name + /ngpio ... (r/o) number of GPIOs exposed by the chip This ABI is obsoleted by Documentation/ABI/testing/gpio-cdev and will be removed after 2020. diff --git a/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop b/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop new file mode 100644 index 000000000000..c1dbd19c679c --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/VPC2004:*/conservation_mode +Date: Aug 2017 +KernelVersion: 4.14 +Contact: platform-driver-x86@vger.kernel.org +Description: + Controls whether the conservation mode is enabled or not. + This feature limits the maximum battery charge percentage to + around 50-60% in order to prolong the lifetime of the battery. diff --git a/Documentation/ABI/stable/sysfs-block b/Documentation/ABI/stable/sysfs-block index 4ba771b56b3b..0ddffc9133d0 100644 --- a/Documentation/ABI/stable/sysfs-block +++ b/Documentation/ABI/stable/sysfs-block @@ -731,7 +731,7 @@ Contact: linux-block@vger.kernel.org Description: [RW] If the device is registered for writeback throttling, then this file shows the target minimum read latency. If this latency - is exceeded in a given window of time (see wb_window_usec), then + is exceeded in a given window of time (see curr_win_nsec), then the writeback throttling will start scaling back writes. Writing a value of '0' to this file disables the feature. Writing a value of '-1' to this file resets the value to the default @@ -778,6 +778,39 @@ Description: 0, write zeroes is not supported by the device. +What: /sys/block//queue/write_zeroes_unmap_max_hw_bytes +Date: January 2025 +Contact: Zhang Yi +Description: + [RO] This file indicates whether a device supports zeroing data + in a specified block range without incurring the cost of + physically writing zeroes to the media for each individual + block. If this parameter is set to write_zeroes_max_bytes, the + device implements a zeroing operation which opportunistically + avoids writing zeroes to media while still guaranteeing that + subsequent reads from the specified block range will return + zeroed data. This operation is a best-effort optimization, a + device may fall back to physically writing zeroes to the media + due to other factors such as misalignment or being asked to + clear a block range smaller than the device's internal + allocation unit. If this parameter is set to 0, the device may + have to write each logical block media during a zeroing + operation. + + +What: /sys/block//queue/write_zeroes_unmap_max_bytes +Date: January 2025 +Contact: Zhang Yi +Description: + [RW] While write_zeroes_unmap_max_hw_bytes is the hardware limit + for the device, this setting is the software limit. Since the + unmap write zeroes operation is a best-effort optimization, some + devices may still physically writing zeroes to media. So the + speed of this operation is not guaranteed. Writing a value of + '0' to this file disables this operation. Otherwise, this + parameter should be equal to write_zeroes_unmap_max_hw_bytes. + + What: /sys/block//queue/zone_append_max_bytes Date: May 2020 Contact: linux-block@vger.kernel.org diff --git a/Documentation/ABI/stable/sysfs-devices-node b/Documentation/ABI/stable/sysfs-devices-node index a02707cb7cbc..2d0e023f22a7 100644 --- a/Documentation/ABI/stable/sysfs-devices-node +++ b/Documentation/ABI/stable/sysfs-devices-node @@ -227,3 +227,12 @@ Contact: Jiaqi Yan Description: Of the raw poisoned pages on a NUMA node, how many pages are recovered by memory error recovery attempt. + +What: /sys/devices/system/node/nodeX/reclaim +Date: June 2025 +Contact: Linux Memory Management list +Description: + Perform user-triggered proactive reclaim on a NUMA node. + This interface is equivalent to the memcg variant. + + See Documentation/admin-guide/cgroup-v2.rst diff --git a/Documentation/ABI/stable/sysfs-kernel-time-aux-clocks b/Documentation/ABI/stable/sysfs-kernel-time-aux-clocks new file mode 100644 index 000000000000..825508f42af6 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-kernel-time-aux-clocks @@ -0,0 +1,5 @@ +What: /sys/kernel/time/aux_clocks//enable +Date: May 2025 +Contact: Thomas Gleixner +Description: + Controls the enablement of auxiliary clock timekeepers. diff --git a/Documentation/ABI/testing/debugfs-amd-iommu b/Documentation/ABI/testing/debugfs-amd-iommu new file mode 100644 index 000000000000..5621a66aa693 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-amd-iommu @@ -0,0 +1,131 @@ +What: /sys/kernel/debug/iommu/amd/iommu/mmio +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file provides read/write access for user input. Users specify the + MMIO register offset for iommu, and the file outputs the corresponding + MMIO register value of iommu + + Example:: + + $ echo "0x18" > /sys/kernel/debug/iommu/amd/iommu00/mmio + $ cat /sys/kernel/debug/iommu/amd/iommu00/mmio + + Output:: + + Offset:0x18 Value:0x000c22000003f48d + +What: /sys/kernel/debug/iommu/amd/iommu/capability +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file provides read/write access for user input. Users specify the + capability register offset for iommu, and the file outputs the + corresponding capability register value of iommu. + + Example:: + + $ echo "0x10" > /sys/kernel/debug/iommu/amd/iommu00/capability + $ cat /sys/kernel/debug/iommu/amd/iommu00/capability + + Output:: + + Offset:0x10 Value:0x00203040 + +What: /sys/kernel/debug/iommu/amd/iommu/cmdbuf +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file is a read-only output file containing iommu command + buffer entries. + + Examples:: + + $ cat /sys/kernel/debug/iommu/amd/iommu/cmdbuf + + Output:: + + CMD Buffer Head Offset:339 Tail Offset:339 + 0: 00835001 10000001 00003c00 00000000 + 1: 00000000 30000005 fffff003 7fffffff + 2: 00835001 10000001 00003c01 00000000 + 3: 00000000 30000005 fffff003 7fffffff + 4: 00835001 10000001 00003c02 00000000 + 5: 00000000 30000005 fffff003 7fffffff + 6: 00835001 10000001 00003c03 00000000 + 7: 00000000 30000005 fffff003 7fffffff + 8: 00835001 10000001 00003c04 00000000 + 9: 00000000 30000005 fffff003 7fffffff + 10: 00835001 10000001 00003c05 00000000 + 11: 00000000 30000005 fffff003 7fffffff + [...] + +What: /sys/kernel/debug/iommu/amd/devid +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file provides read/write access for user input. Users specify the + device ID, which can be used to dump IOMMU data structures such as the + interrupt remapping table and device table. + + Example: + + 1. + :: + + $ echo 0000:01:00.0 > /sys/kernel/debug/iommu/amd/devid + $ cat /sys/kernel/debug/iommu/amd/devid + + Output:: + + 0000:01:00.0 + + 2. + :: + + $ echo 01:00.0 > /sys/kernel/debug/iommu/amd/devid + $ cat /sys/kernel/debug/iommu/amd/devid + + Output:: + + 0000:01:00.0 + +What: /sys/kernel/debug/iommu/amd/devtbl +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file is a read-only output file containing the device table entry + for the device ID provided in /sys/kernel/debug/iommu/amd/devid. + + Example:: + + $ cat /sys/kernel/debug/iommu/amd/devtbl + + Output:: + + DeviceId QWORD[3] QWORD[2] QWORD[1] QWORD[0] iommu + 0000:01:00.0 0000000000000000 20000001373b8013 0000000000000038 6000000114d7b603 iommu3 + +What: /sys/kernel/debug/iommu/amd/irqtbl +Date: January 2025 +Contact: Dheeraj Kumar Srivastava +Description: + This file is a read-only output file containing valid IRT table entries + for the device ID provided in /sys/kernel/debug/iommu/amd/devid. + + Example:: + + $ cat /sys/kernel/debug/iommu/amd/irqtbl + + Output:: + + DeviceId 0000:01:00.0 + IRT[0000] 0000000000000020 0000000000000241 + IRT[0001] 0000000000000020 0000000000000841 + IRT[0002] 0000000000000020 0000000000002041 + IRT[0003] 0000000000000020 0000000000008041 + IRT[0004] 0000000000000020 0000000000020041 + IRT[0005] 0000000000000020 0000000000080041 + IRT[0006] 0000000000000020 0000000000200041 + IRT[0007] 0000000000000020 0000000000800041 + [...] diff --git a/Documentation/ABI/testing/debugfs-cxl b/Documentation/ABI/testing/debugfs-cxl index 12488c14be64..e95e21f131e9 100644 --- a/Documentation/ABI/testing/debugfs-cxl +++ b/Documentation/ABI/testing/debugfs-cxl @@ -20,7 +20,7 @@ Description: visible for devices supporting the capability. -What: /sys/kernel/debug/memX/clear_poison +What: /sys/kernel/debug/cxl/memX/clear_poison Date: April, 2023 KernelVersion: v6.4 Contact: linux-cxl@vger.kernel.org diff --git a/Documentation/ABI/testing/debugfs-driver-qat b/Documentation/ABI/testing/debugfs-driver-qat index bd6793760f29..3f1efbbad6ca 100644 --- a/Documentation/ABI/testing/debugfs-driver-qat +++ b/Documentation/ABI/testing/debugfs-driver-qat @@ -67,7 +67,7 @@ Contact: qat-linux@intel.com Description: (RO) Read returns power management information specific to the QAT device. - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/kernel/debug/qat__/cnv_errors Date: January 2024 diff --git a/Documentation/ABI/testing/debugfs-driver-qat_telemetry b/Documentation/ABI/testing/debugfs-driver-qat_telemetry index eacee2072088..0dfd8d97e169 100644 --- a/Documentation/ABI/testing/debugfs-driver-qat_telemetry +++ b/Documentation/ABI/testing/debugfs-driver-qat_telemetry @@ -32,7 +32,7 @@ Description: (RW) Enables/disables the reporting of telemetry metrics. echo 0 > /sys/kernel/debug/qat_4xxx_0000:6b:00.0/telemetry/control - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/kernel/debug/qat__/telemetry/device_data Date: March 2024 @@ -67,6 +67,10 @@ Description: (RO) Reports device telemetry counters. exec_xlt execution count of Translator slice N util_dcpr utilization of Decompression slice N [%] exec_dcpr execution count of Decompression slice N + util_cnv utilization of Compression and verify slice N [%] + exec_cnv execution count of Compression and verify slice N + util_dcprz utilization of Decompression slice N [%] + exec_dcprz execution count of Decompression slice N util_pke utilization of PKE N [%] exec_pke execution count of PKE N util_ucs utilization of UCS slice N [%] @@ -100,7 +104,7 @@ Description: (RO) Reports device telemetry counters. If a device lacks of a specific accelerator, the corresponding attribute is not reported. - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/kernel/debug/qat__/telemetry/rp__data Date: March 2024 @@ -225,4 +229,4 @@ Description: (RW) Selects up to 4 Ring Pairs (RP) to monitor, one per file, ``rp2srv`` from sysfs. See Documentation/ABI/testing/sysfs-driver-qat for details. - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. diff --git a/Documentation/ABI/testing/debugfs-pktcdvd b/Documentation/ABI/testing/debugfs-pktcdvd deleted file mode 100644 index f6f65a4faea0..000000000000 --- a/Documentation/ABI/testing/debugfs-pktcdvd +++ /dev/null @@ -1,18 +0,0 @@ -What: /sys/kernel/debug/pktcdvd/pktcdvd[0-7] -Date: Oct. 2006 -KernelVersion: 2.6.20 -Contact: Thomas Maier -Description: - -The pktcdvd module (packet writing driver) creates -these files in debugfs: - -/sys/kernel/debug/pktcdvd/pktcdvd[0-7]/ - - ==== ====== ==================================== - info 0444 Lots of driver statistics and infos. - ==== ====== ==================================== - -Example:: - - cat /sys/kernel/debug/pktcdvd/pktcdvd0/info diff --git a/Documentation/ABI/testing/sysfs-bus-acpi b/Documentation/ABI/testing/sysfs-bus-acpi index 58abacf59b2a..6f2b907a8013 100644 --- a/Documentation/ABI/testing/sysfs-bus-acpi +++ b/Documentation/ABI/testing/sysfs-bus-acpi @@ -1,6 +1,6 @@ What: /sys/bus/acpi/devices/.../path Date: December 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the full path of ACPI namespace object associated with the device object. For example, @@ -12,7 +12,7 @@ Description: What: /sys/bus/acpi/devices/.../modalias Date: July 2007 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the PNP IDs of the device object. That is acpi:HHHHHHHH:[CCCCCCC:]. Where each HHHHHHHH or @@ -20,7 +20,7 @@ Description: What: /sys/bus/acpi/devices/.../hid Date: April 2005 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the hardware ID (_HID) of the device object. For example, PNP0103. @@ -29,14 +29,14 @@ Description: What: /sys/bus/acpi/devices/.../description Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _STR control method, if present. What: /sys/bus/acpi/devices/.../adr Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _ADR control method, which is present for ACPI device @@ -45,14 +45,14 @@ Description: What: /sys/bus/acpi/devices/.../uid Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _UID control method, if present. What: /sys/bus/acpi/devices/.../eject Date: December 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: Writing 1 to this attribute will trigger hot removal of this device object. This file exists for every device @@ -60,7 +60,7 @@ Description: What: /sys/bus/acpi/devices/.../status Date: Jan, 2014 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: (RO) Returns the ACPI device status: enabled, disabled or functioning or present, if the method _STA is present. @@ -90,7 +90,7 @@ Description: What: /sys/bus/acpi/devices/.../hrv Date: Apr, 2016 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: (RO) Allows users to read the hardware version of non-PCI hardware, if the _HRV control method is present. It is mostly diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 190bfcc1e836..7e31b8cd49b3 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -141,8 +141,6 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -417,18 +415,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_q_offset +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset What: /sys/bus/iio/devices/iio:deviceX/in_current_offset -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_offset -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_current_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_current_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset @@ -456,21 +450,15 @@ Description: to the _raw output. What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_scale -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_scale -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_scale -What: /sys/bus/iio/devices/iio:deviceX/in_current_i_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale @@ -559,6 +547,30 @@ Description: - a small discrete set of values like "0 2 4 6 8" - a range specified as "[min step max]" +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay +KernelVersion: 6.17 +Contact: linux-iio@vger.kernel.org +Description: + Delay of start of conversion from common reference point shared + by all channels. Can be writable when used to compensate for + delay variation introduced by external filters feeding a + simultaneous sampling ADC. + + E.g., for the ad7606 ADC series, this value is intended as a + configurable time delay in seconds, to correct delay introduced + by an optional external filtering circuit. + +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay_available +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + Available values of convdelay. Maybe expressed as: + + - a range specified as "[min step max]" + + If shared across all channels, _convdelay_available + is used. + What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale @@ -579,11 +591,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibscale What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibscale @@ -805,7 +813,11 @@ Description: all the other channels, since it involves changing the VCO fundamental output frequency. +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_phase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_i_phase +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_q_phase KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org Description: @@ -1434,10 +1446,6 @@ What: /sys/.../iio:deviceX/bufferY/in_timestamp_en What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_en What: /sys/.../iio:deviceX/bufferY/in_voltageY_en What: /sys/.../iio:deviceX/bufferY/in_voltageY-voltageZ_en -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_en -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_en -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_en -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_en What: /sys/.../iio:deviceX/bufferY/in_incli_x_en What: /sys/.../iio:deviceX/bufferY/in_incli_y_en What: /sys/.../iio:deviceX/bufferY/in_pressureY_en @@ -1458,10 +1466,6 @@ What: /sys/.../iio:deviceX/bufferY/in_incli_type What: /sys/.../iio:deviceX/bufferY/in_voltageY_type What: /sys/.../iio:deviceX/bufferY/in_voltage_type What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_type -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_type -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_type -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_type -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_type What: /sys/.../iio:deviceX/bufferY/in_timestamp_type What: /sys/.../iio:deviceX/bufferY/in_pressureY_type What: /sys/.../iio:deviceX/bufferY/in_pressure_type @@ -1499,10 +1503,6 @@ Description: What: /sys/.../iio:deviceX/bufferY/in_voltageY_index What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_index -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_index -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_index What: /sys/.../iio:deviceX/bufferY/in_accel_x_index What: /sys/.../iio:deviceX/bufferY/in_accel_y_index What: /sys/.../iio:deviceX/bufferY/in_accel_z_index @@ -1692,8 +1692,6 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_raw -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_raw KernelVersion: 3.17 Contact: linux-iio@vger.kernel.org Description: @@ -2278,6 +2276,9 @@ Description: Reading returns a list with the possible filter modes. Options for the attribute: + * "none" - Filter is disabled/bypassed. + * "sinc1" - The digital sinc1 filter. Fast 1st + conversion time. Poor noise performance. * "sinc3" - The digital sinc3 filter. Moderate 1st conversion time. Good noise performance. * "sinc4" - Sinc 4. Excellent noise performance. Long @@ -2293,6 +2294,8 @@ Description: * "sinc3+pf2" - Sinc3 + device specific Post Filter 2. * "sinc3+pf3" - Sinc3 + device specific Post Filter 3. * "sinc3+pf4" - Sinc3 + device specific Post Filter 4. + * "sinc5+pf1" - Sinc5 + device specific Post Filter 1. + * "sinc5+avg" - Sinc5 + averaging by 4. * "wideband" - filter with wideband low ripple passband and sharp transition band. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 index de1e323e5d47..9cf8cd0dd2df 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 @@ -1,10 +1,10 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_i_calibphase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_i_calibphase KernelVersion: Contact: linux-iio@vger.kernel.org Description: Read/write unscaled value for the Local Oscillatior path quadrature I phase shift. -What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_q_calibphase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_q_calibphase KernelVersion: Contact: linux-iio@vger.kernel.org Description: diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 1e7e0bb4c14e..df8ba88b9f6a 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -132,3 +132,12 @@ Description: A list of governors that support the node: - simple_ondemand + +What: /sys/class/devfreq/.../related_cpus +Date: June 2025 +Contact: Linux power management list +Description: The list of CPUs whose performance is closely related to the + frequency of this devfreq domain. + + This file is only present if a specific devfreq device is + closely associated with a subset of CPUs. diff --git a/Documentation/ABI/testing/sysfs-class-drm b/Documentation/ABI/testing/sysfs-class-drm new file mode 100644 index 000000000000..d23fed5e29a7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-drm @@ -0,0 +1,8 @@ +What: /sys/class/drm/.../boot_display +Date: January 2026 +Contact: Linux DRI developers +Description: + This file indicates that displays connected to the device were + used to display the boot sequence. If a display connected to + the device was used to display the boot sequence the file will + be present and contain "1". diff --git a/Documentation/ABI/testing/sysfs-class-intel_pmt-features b/Documentation/ABI/testing/sysfs-class-intel_pmt-features new file mode 100644 index 000000000000..cddb30e5bdf6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-intel_pmt-features @@ -0,0 +1,134 @@ +What: /sys/class/intel_pmt/features-/ +Date: 2025-04-24 +KernelVersion: 6.16 +Contact: david.e.box@linux.intel.com +Description: + The `features-/` directory represents the "features" + capability exposed by Intel PMT (Platform Monitoring Technology) + for the given PCI device. + + Each directory corresponds to a PMT feature and contains + attributes describing the available telemetry, monitoring, or + control functionalities. + +Directory Structure: + + /sys/class/intel_pmt/features-/ + ├── accelerator_telemetry/ # Per-accelerator telemetry data + ├── crash_log/ # Contains system crash telemetry logs + ├── per_core_environment_telemetry/ # Environmental telemetry per core + ├── per_core_performance_telemetry/ # Performance telemetry per core + ├── per_rmid_energy_telemetry/ # Energy telemetry for RMIDs + ├── per_rmid_perf_telemetry/ # Performance telemetry for RMIDs + ├── tpmi_control/ # TPMI-related controls and telemetry + ├── tracing/ # PMT tracing features + └── uncore_telemetry/ # Uncore telemetry data + +Common Files (Present in all feature directories): + + caps + - Read-only + - Lists available capabilities for this feature. + + guids + - Read-only + - Lists GUIDs associated with this feature. + +Additional Attributes (Conditional Presence): + + max_command_size + - Read-only + - Present if the feature supports out-of-band MCTP access. + - Maximum supported MCTP command size for out-of-band PMT access (bytes). + + max_stream_size + - Read-only + - Present if the feature supports out-of-band MCTP access. + - Maximum supported MCTP stream size (bytes). + + min_watcher_period_ms + - Read-only + - Present if the feature supports the watcher API. + The watcher API provides a writable control interface that allows user + configuration of monitoring behavior, such as setting the sampling or + reporting interval. + - Minimum supported time period for the watcher interface (milliseconds). + + num_rmids + - Read-only + - Present if the feature supports RMID (Resource Monitoring ID) telemetry. + RMIDs are identifiers used by hardware to track and report resource usage, + such as memory bandwidth or energy consumption, on a per-logical-entity + basis (e.g., per core, thread, or process group). + - Maximum number of RMIDs tracked simultaneously. + +Example: +For a device with PCI BDF `0000:00:03.1`, the directory tree could look like: + + /sys/class/intel_pmt/features-0000:00:03.1/ + ├── accelerator_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + ├── crash_log/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + ├── per_core_environment_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + ├── per_rmid_energy_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + │ ├── num_rmids + ├── tpmi_control/ + │ ├── caps + │ ├── guids + ├── tracing/ + │ ├── caps + │ ├── guids + ├── uncore_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + +Notes: + - Some attributes are only present if the corresponding feature supports + the capability (e.g., `max_command_size` for MCTP-capable features). + - Features supporting RMIDs include `num_rmids`. + - Features supporting the watcher API include `min_watcher_period_ms`. + - The `caps` file provides additional information about the functionality + of the feature. + +Example 'caps' content for the 'tracing' feature: + + /sys/class/intel_pmt/features-0000:00:03.1/ + ├── tracing/ + │ ├── caps + + telemetry Available: No + watcher Available: Yes + crashlog Available: No + streaming Available: No + threashold Available: No + window Available: No + config Available: Yes + tracing Available: No + inband Available: Yes + oob Available: Yes + secure_chan Available: No + pmt_sp Available: Yes + pmt_sp_policy Available: Yes + mailbox Available: Yes + bios_lock Available: Yes diff --git a/Documentation/ABI/testing/sysfs-class-net-phydev b/Documentation/ABI/testing/sysfs-class-net-phydev index ac722dd5e694..31615c59bff9 100644 --- a/Documentation/ABI/testing/sysfs-class-net-phydev +++ b/Documentation/ABI/testing/sysfs-class-net-phydev @@ -26,6 +26,16 @@ Description: This ID is used to match the device with the appropriate driver. +What: /sys/class/mdio_bus///c45_phy_ids/mmd_device_id +Date: June 2025 +KernelVersion: 6.17 +Contact: netdev@vger.kernel.org +Description: + This attribute contains the 32-bit PHY Identifier as reported + by the device during bus enumeration, encoded in hexadecimal. + These C45 IDs are used to match the device with the appropriate + driver. These files are invisible to the C22 device. + What: /sys/class/mdio_bus///phy_interface Date: February 2014 KernelVersion: 3.15 diff --git a/Documentation/ABI/testing/sysfs-class-pktcdvd b/Documentation/ABI/testing/sysfs-class-pktcdvd deleted file mode 100644 index ba1ce626591d..000000000000 --- a/Documentation/ABI/testing/sysfs-class-pktcdvd +++ /dev/null @@ -1,97 +0,0 @@ -sysfs interface ---------------- -The pktcdvd module (packet writing driver) creates the following files in the -sysfs: ( is in the format major:minor) - -What: /sys/class/pktcdvd/add -What: /sys/class/pktcdvd/remove -What: /sys/class/pktcdvd/device_map -Date: Oct. 2006 -KernelVersion: 2.6.20 -Contact: Thomas Maier -Description: - - ========== ============================================== - add (WO) Write a block device id (major:minor) to - create a new pktcdvd device and map it to the - block device. - - remove (WO) Write the pktcdvd device id (major:minor) - to remove the pktcdvd device. - - device_map (RO) Shows the device mapping in format: - pktcdvd[0-7] - ========== ============================================== - - -What: /sys/class/pktcdvd/pktcdvd[0-7]/dev -What: /sys/class/pktcdvd/pktcdvd[0-7]/uevent -Date: Oct. 2006 -KernelVersion: 2.6.20 -Contact: Thomas Maier -Description: - dev: (RO) Device id - - uevent: (WO) To send a uevent - - -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/packets_started -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/packets_finished -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_written -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_read -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_read_gather -What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/reset -Date: Oct. 2006 -KernelVersion: 2.6.20 -Contact: Thomas Maier -Description: - packets_started: (RO) Number of started packets. - - packets_finished: (RO) Number of finished packets. - - kb_written: (RO) kBytes written. - - kb_read: (RO) kBytes read. - - kb_read_gather: (RO) kBytes read to fill write packets. - - reset: (WO) Write any value to it to reset - pktcdvd device statistic values, like - bytes read/written. - - -What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/size -What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/congestion_off -What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/congestion_on -Date: Oct. 2006 -KernelVersion: 2.6.20 -Contact: Thomas Maier -Description: - ============== ================================================ - size (RO) Contains the size of the bio write queue. - - congestion_off (RW) If bio write queue size is below this mark, - accept new bio requests from the block layer. - - congestion_on (RW) If bio write queue size is higher as this - mark, do no longer accept bio write requests - from the block layer and wait till the pktcdvd - device has processed enough bio's so that bio - write queue size is below congestion off mark. - A value of <= 0 disables congestion control. - ============== ================================================ - - -Example: --------- -To use the pktcdvd sysfs interface directly, you can do:: - - # create a new pktcdvd device mapped to /dev/hdc - echo "22:0" >/sys/class/pktcdvd/add - cat /sys/class/pktcdvd/device_map - # assuming device pktcdvd0 was created, look at stat's - cat /sys/class/pktcdvd/pktcdvd0/stat/kb_written - # print the device id of the mapped block device - fgrep pktcdvd0 /sys/class/pktcdvd/device_map - # remove device, using pktcdvd0 device id 253:0 - echo "253:0" >/sys/class/pktcdvd/remove diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 54195530e97a..e4ec5de9a5dd 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -1,6 +1,6 @@ What: /sys/devices/.../power/ Date: January 2009 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power directory contains attributes allowing the user space to check and modify some power @@ -8,7 +8,7 @@ Description: What: /sys/devices/.../power/wakeup Date: January 2009 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/wakeup attribute allows the user space to check if the device is enabled to wake up the system @@ -34,7 +34,7 @@ Description: What: /sys/devices/.../power/control Date: January 2009 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/control attribute allows the user space to control the run-time power management of the device. @@ -53,10 +53,10 @@ Description: What: /sys/devices/.../power/async Date: January 2009 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../async attribute allows the user space to - enable or diasble the device's suspend and resume callbacks to + enable or disable the device's suspend and resume callbacks to be executed asynchronously (ie. in separate threads, in parallel with the main suspend/resume thread) during system-wide power transitions (eg. suspend to RAM, hibernation). @@ -79,7 +79,7 @@ Description: What: /sys/devices/.../power/wakeup_count Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_count attribute contains the number of signaled wakeup events associated with the device. This @@ -90,7 +90,7 @@ Description: What: /sys/devices/.../power/wakeup_active_count Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_active_count attribute contains the number of times the processing of wakeup events associated with @@ -102,7 +102,7 @@ Description: What: /sys/devices/.../power/wakeup_abort_count Date: February 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_abort_count attribute contains the number of times the processing of a wakeup event associated with @@ -114,7 +114,7 @@ Description: What: /sys/devices/.../power/wakeup_expire_count Date: February 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_expire_count attribute contains the number of times a wakeup event associated with the device has @@ -126,7 +126,7 @@ Description: What: /sys/devices/.../power/wakeup_active Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_active attribute contains either 1, or 0, depending on whether or not a wakeup event associated with @@ -138,7 +138,7 @@ Description: What: /sys/devices/.../power/wakeup_total_time_ms Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_total_time_ms attribute contains the total time of processing wakeup events associated with the @@ -149,7 +149,7 @@ Description: What: /sys/devices/.../power/wakeup_max_time_ms Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_max_time_ms attribute contains the maximum time of processing a single wakeup event associated @@ -161,7 +161,7 @@ Description: What: /sys/devices/.../power/wakeup_last_time_ms Date: September 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_last_time_ms attribute contains the value of the monotonic clock corresponding to the time of @@ -173,7 +173,7 @@ Description: What: /sys/devices/.../power/wakeup_prevent_sleep_time_ms Date: February 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute contains the total time the device has been preventing @@ -203,7 +203,7 @@ Description: What: /sys/devices/.../power/pm_qos_resume_latency_us Date: March 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/pm_qos_resume_latency_us attribute contains the PM QoS resume latency limit for the given device, @@ -223,7 +223,7 @@ Description: What: /sys/devices/.../power/pm_qos_latency_tolerance_us Date: January 2014 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/pm_qos_latency_tolerance_us attribute contains the PM QoS active state latency tolerance limit for the @@ -248,7 +248,7 @@ Description: What: /sys/devices/.../power/pm_qos_no_power_off Date: September 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/pm_qos_no_power_off attribute is used for manipulating the PM QoS "no power off" flag. If @@ -263,7 +263,7 @@ Description: What: /sys/devices/.../power/runtime_status Date: April 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/devices/.../power/runtime_status attribute contains the current runtime PM status of the device, which may be diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index bf85f4de6862..8aed6d94c4cd 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -584,7 +584,9 @@ What: /sys/devices/system/cpu/vulnerabilities /sys/devices/system/cpu/vulnerabilities/spectre_v1 /sys/devices/system/cpu/vulnerabilities/spectre_v2 /sys/devices/system/cpu/vulnerabilities/srbds + /sys/devices/system/cpu/vulnerabilities/tsa /sys/devices/system/cpu/vulnerabilities/tsx_async_abort + /sys/devices/system/cpu/vulnerabilities/vmscape Date: January 2018 Contact: Linux kernel mailing list Description: Information about CPU vulnerabilities diff --git a/Documentation/ABI/testing/sysfs-driver-qat b/Documentation/ABI/testing/sysfs-driver-qat index f290e77cd590..b0561b9fc4eb 100644 --- a/Documentation/ABI/testing/sysfs-driver-qat +++ b/Documentation/ABI/testing/sysfs-driver-qat @@ -14,7 +14,7 @@ Description: (RW) Reports the current state of the QAT device. Write to It is possible to transition the device from up to down only if the device is up and vice versa. - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat/cfg_services Date: June 2022 @@ -23,24 +23,28 @@ Contact: qat-linux@intel.com Description: (RW) Reports the current configuration of the QAT device. Write to the file to change the configured services. - The values are: + One or more services can be enabled per device. + Certain configurations are restricted to specific device types; + where applicable this is explicitly indicated, for example + (qat_6xxx) denotes applicability exclusively to that device series. - * sym;asym: the device is configured for running crypto - services - * asym;sym: identical to sym;asym - * dc: the device is configured for running compression services - * dcc: identical to dc but enables the dc chaining feature, - hash then compression. If this is not required chose dc - * sym: the device is configured for running symmetric crypto - services - * asym: the device is configured for running asymmetric crypto - services - * asym;dc: the device is configured for running asymmetric - crypto services and compression services - * dc;asym: identical to asym;dc - * sym;dc: the device is configured for running symmetric crypto - services and compression services - * dc;sym: identical to sym;dc + The available services include: + + * sym: Configures the device for symmetric cryptographic operations. + * asym: Configures the device for asymmetric cryptographic operations. + * dc: Configures the device for compression and decompression + operations. + * dcc: Similar to dc, but with the additional dc chaining feature + enabled, cipher then compress (qat_6xxx), hash then compression. + If this is not required choose dc. + * decomp: Configures the device for decompression operations (qat_6xxx). + + Service combinations are permitted for all services except dcc. + On QAT GEN4 devices (qat_4xxx driver) a maximum of two services can be + combined and on QAT GEN6 devices (qat_6xxx driver ) a maximum of three + services can be combined. + The order of services is not significant. For instance, sym;asym is + functionally equivalent to asym;sym. It is possible to set the configuration only if the device is in the `down` state (see /sys/bus/pci/devices//qat/state) @@ -59,7 +63,7 @@ Description: (RW) Reports the current configuration of the QAT device. # cat /sys/bus/pci/devices//qat/cfg_services dc - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat/pm_idle_enabled Date: June 2023 @@ -94,7 +98,7 @@ Description: (RW) This configuration option provides a way to force the device i # cat /sys/bus/pci/devices//qat/pm_idle_enabled 0 - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat/rp2srv Date: January 2024 @@ -126,7 +130,7 @@ Description: # cat /sys/bus/pci/devices//qat/rp2srv sym - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat/num_rps Date: January 2024 @@ -140,7 +144,7 @@ Description: # cat /sys/bus/pci/devices//qat/num_rps 64 - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat/auto_reset Date: May 2024 @@ -160,4 +164,4 @@ Description: (RW) Reports the current state of the autoreset feature * 0/Nn/off: auto reset disabled. If the device encounters an unrecoverable error, it will not be reset. - This attribute is only available for qat_4xxx devices. + This attribute is available for qat_4xxx and qat_6xxx devices. diff --git a/Documentation/ABI/testing/sysfs-driver-qat_rl b/Documentation/ABI/testing/sysfs-driver-qat_rl index 8c282ae3155d..d534f89b4971 100644 --- a/Documentation/ABI/testing/sysfs-driver-qat_rl +++ b/Documentation/ABI/testing/sysfs-driver-qat_rl @@ -31,7 +31,7 @@ Description: * rm_all: Removes all the configured SLAs. * Inputs: None - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/rp Date: January 2024 @@ -68,7 +68,7 @@ Description: ## Write # echo 0x5 > /sys/bus/pci/devices//qat_rl/rp - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/id Date: January 2024 @@ -101,7 +101,7 @@ Description: # cat /sys/bus/pci/devices//qat_rl/rp 0x5 ## ring pair ID 0 and ring pair ID 2 - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/cir Date: January 2024 @@ -135,7 +135,7 @@ Description: # cat /sys/bus/pci/devices//qat_rl/cir 500 - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/pir Date: January 2024 @@ -169,7 +169,7 @@ Description: # cat /sys/bus/pci/devices//qat_rl/pir 750 - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/srv Date: January 2024 @@ -202,7 +202,7 @@ Description: # cat /sys/bus/pci/devices//qat_rl/srv dc - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. What: /sys/bus/pci/devices//qat_rl/cap_rem Date: January 2024 @@ -223,4 +223,4 @@ Description: # cat /sys/bus/pci/devices//qat_rl/cap_rem 0 - This attribute is only available for qat_4xxx devices. + This attribute is only available for qat_4xxx and qat_6xxx devices. diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop index 28c9c040de5d..408cb0ddf4aa 100644 --- a/Documentation/ABI/testing/sysfs-driver-samsung-laptop +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -20,17 +20,6 @@ Description: Some Samsung laptops have different "performance levels" and it's still unknown if this value even changes anything, other than making the user feel a bit better. -What: /sys/devices/platform/samsung/battery_life_extender -Date: December 1, 2011 -KernelVersion: 3.3 -Contact: Corentin Chary -Description: Max battery charge level can be modified, battery cycle - life can be extended by reducing the max battery charge - level. - - - 0 means normal battery mode (100% charge) - - 1 means battery life extender mode (80% charge) - What: /sys/devices/platform/samsung/usb_charge Date: December 1, 2011 KernelVersion: 3.3 diff --git a/Documentation/ABI/testing/sysfs-driver-typec-displayport b/Documentation/ABI/testing/sysfs-driver-typec-displayport index 256c87c5219a..314acd54e13e 100644 --- a/Documentation/ABI/testing/sysfs-driver-typec-displayport +++ b/Documentation/ABI/testing/sysfs-driver-typec-displayport @@ -62,3 +62,13 @@ Description: by VESA DisplayPort Alt Mode on USB Type-C Standard. - 0 when HPD’s logical state is low (HPD_Low) as defined by VESA DisplayPort Alt Mode on USB Type-C Standard. + +What: /sys/bus/typec/devices/.../displayport/irq_hpd +Date: June 2025 +Contact: RD Babiera +Description: + IRQ_HPD events are sent over the USB PD protocol in Status Update and + Attention messages. IRQ_HPD can only be asserted when HPD is high, + and is asserted when an IRQ_HPD has been issued since the last Status + Update. This is a read only node that returns the number of IRQ events + raised in the driver's lifetime. diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index d4140dc6c5ba..a90612ab5780 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -711,7 +711,7 @@ Description: This file shows the thin provisioning type. This is one of The file is read only. -What: /sys/class/scsi_device/*/device/unit_descriptor/physical_memory_resourse_count +What: /sys/class/scsi_device/*/device/unit_descriptor/physical_memory_resource_count Date: February 2018 Contact: Stanislav Nijnikov Description: This file shows the total physical memory resources. This is @@ -1685,3 +1685,86 @@ Description: ================ ======================================== The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/analysis_trigger +What: /sys/bus/platform/devices/*.ufs/hid/analysis_trigger +Date: May 2025 +Contact: Huan Tang +Description: + The host can enable or disable HID analysis operation. + + ======= ========================================= + disable disable HID analysis operation + enable enable HID analysis operation + ======= ========================================= + + The file is write only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/defrag_trigger +What: /sys/bus/platform/devices/*.ufs/hid/defrag_trigger +Date: May 2025 +Contact: Huan Tang +Description: + The host can enable or disable HID defragmentation operation. + + ======= ========================================= + disable disable HID defragmentation operation + enable enable HID defragmentation operation + ======= ========================================= + + The attribute is write only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/fragmented_size +What: /sys/bus/platform/devices/*.ufs/hid/fragmented_size +Date: May 2025 +Contact: Huan Tang +Description: + The total fragmented size in the device is reported through + this attribute. + + The attribute is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/defrag_size +What: /sys/bus/platform/devices/*.ufs/hid/defrag_size +Date: May 2025 +Contact: Huan Tang +Description: + The host sets the size to be defragmented by an HID + defragmentation operation. + + The attribute is read/write. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/progress_ratio +What: /sys/bus/platform/devices/*.ufs/hid/progress_ratio +Date: May 2025 +Contact: Huan Tang +Description: + Defragmentation progress is reported by this attribute, + indicates the ratio of the completed defragmentation size + over the requested defragmentation size. + + ==== ============================================ + 1 1% + ... + 100 100% + ==== ============================================ + + The attribute is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/state +What: /sys/bus/platform/devices/*.ufs/hid/state +Date: May 2025 +Contact: Huan Tang +Description: + The HID state is reported by this attribute. + + ==================== =========================== + idle Idle (analysis required) + analysis_in_progress Analysis in progress + defrag_required Defrag required + defrag_in_progress Defrag in progress + defrag_completed Defrag completed + defrag_not_required Defrag is not required + ==================== =========================== + + The attribute is read only. diff --git a/Documentation/ABI/testing/sysfs-edac-scrub b/Documentation/ABI/testing/sysfs-edac-scrub index c43be90deab4..ab6014743da5 100644 --- a/Documentation/ABI/testing/sysfs-edac-scrub +++ b/Documentation/ABI/testing/sysfs-edac-scrub @@ -49,6 +49,12 @@ Description: (RO) Supported minimum scrub cycle duration in seconds by the memory scrubber. + Device-based scrub: returns the minimum scrub cycle + supported by the memory device. + + Region-based scrub: returns the max of minimum scrub cycles + supported by individual memory devices that back the region. + What: /sys/bus/edac/devices//scrubX/max_cycle_duration Date: March 2025 KernelVersion: 6.15 @@ -57,6 +63,16 @@ Description: (RO) Supported maximum scrub cycle duration in seconds by the memory scrubber. + Device-based scrub: returns the maximum scrub cycle supported + by the memory device. + + Region-based scrub: returns the min of maximum scrub cycles + supported by individual memory devices that back the region. + + If the memory device does not provide maximum scrub cycle + information, return the maximum supported value of the scrub + cycle field. + What: /sys/bus/edac/devices//scrubX/current_cycle_duration Date: March 2025 KernelVersion: 6.15 diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index f4de60c4134d..72e7c9161ce7 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -108,15 +108,15 @@ Description: number of a "General Purpose Events" (GPE). A GPE vectors to a specified handler in AML, which - can do a anything the BIOS writer wants from + can do anything the BIOS writer wants from OS context. GPE 0x12, for example, would vector to a level or edge handler called _L12 or _E12. The handler may do its business and return. - Or the handler may send send a Notify event + Or the handler may send a Notify event to a Linux device driver registered on an ACPI device, such as a battery, or a processor. - To figure out where all the SCI's are coming from, + To figure out where all the SCIs are coming from, /sys/firmware/acpi/interrupts contains a file listing every possible source, and the count of how many times it has triggered:: diff --git a/Documentation/ABI/testing/sysfs-firmware-efi b/Documentation/ABI/testing/sysfs-firmware-efi index 5e4d0b27cdfe..927e362d4974 100644 --- a/Documentation/ABI/testing/sysfs-firmware-efi +++ b/Documentation/ABI/testing/sysfs-firmware-efi @@ -36,3 +36,10 @@ Description: Displays the content of the Runtime Configuration Interface Table version 2 on Dell EMC PowerEdge systems in binary format Users: It is used by Dell EMC OpenManage Server Administrator tool to populate BIOS setup page. + +What: /sys/firmware/efi/ovmf_debug_log +Date: July 2025 +Contact: Gerd Hoffmann , linux-efi@vger.kernel.org +Description: Displays the content of the OVMF debug log buffer. The file is + only present in case the firmware supports logging to a memory + buffer. diff --git a/Documentation/ABI/testing/sysfs-fs-erofs b/Documentation/ABI/testing/sysfs-fs-erofs index bf3b6299c15e..76d9808ed581 100644 --- a/Documentation/ABI/testing/sysfs-fs-erofs +++ b/Documentation/ABI/testing/sysfs-fs-erofs @@ -5,7 +5,7 @@ Description: Shows all enabled kernel features. Supported features: zero_padding, compr_cfgs, big_pcluster, chunked_file, device_table, compr_head2, sb_chksum, ztailpacking, - dedupe, fragments. + dedupe, fragments, 48bit, metabox. What: /sys/fs/erofs//sync_decompress Date: November 2021 @@ -35,3 +35,11 @@ Description: Used to set or show hardware accelerators in effect and multiple accelerators are separated by '\n'. Supported accelerator(s): qat_deflate. Disable all accelerators with an empty string (echo > accel). + +What: /sys/fs/erofs//dir_ra_bytes +Date: July 2025 +Contact: "Chao Yu" +Description: Used to set or show readahead bytes during readdir(), by + default the value is 16384. + + - 0: disable readahead. diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index bf03263b9f46..bc0e7fefc39d 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -861,3 +861,25 @@ Description: This is a read-only entry to show the value of sb.s_encoding_flags, SB_ENC_STRICT_MODE_FL 0x00000001 SB_ENC_NO_COMPAT_FALLBACK_FL 0x00000002 ============================ ========== + +What: /sys/fs/f2fs//reserved_pin_section +Date: June 2025 +Contact: "Chao Yu" +Description: This threshold is used to control triggering garbage collection while + fallocating on pinned file, so, it can guarantee there is enough free + reserved section before preallocating on pinned file. + By default, the value is ovp_sections, especially, for zoned ufs, the + value is 1. + +What: /sys/fs/f2fs//gc_boost_gc_multiple +Date: June 2025 +Contact: "Daeho Jeong" +Description: Set a multiplier for the background GC migration window when F2FS GC is + boosted. The range should be from 1 to the segment count in a section. + Default: 5 + +What: /sys/fs/f2fs//gc_boost_gc_greedy +Date: June 2025 +Contact: "Daeho Jeong" +Description: Control GC algorithm for boost GC. 0: cost benefit, 1: greedy + Default: 1 diff --git a/Documentation/ABI/testing/sysfs-kernel-address_bits b/Documentation/ABI/testing/sysfs-kernel-address_bits index 5d09ff84d4d6..3b72e48086aa 100644 --- a/Documentation/ABI/testing/sysfs-kernel-address_bits +++ b/Documentation/ABI/testing/sysfs-kernel-address_bits @@ -1,4 +1,4 @@ -What: /sys/kernel/address_bit +What: /sys/kernel/address_bits Date: May 2023 KernelVersion: 6.3 Contact: Thomas Weißschuh diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index 5697ab154c1f..6791d879759e 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -44,6 +44,13 @@ Contact: SeongJae Park Description: Reading this file returns the pid of the kdamond if it is running. +What: /sys/kernel/mm/damon/admin/kdamonds//refresh_ms +Date: Jul 2025 +Contact: SeongJae Park +Description: Writing a value to this file sets the time interval for + automatic DAMON status file contents update. Writing '0' + disables the update. Reading this file returns the value. + What: /sys/kernel/mm/damon/admin/kdamonds//contexts/nr_contexts Date: Mar 2022 Contact: SeongJae Park @@ -431,6 +438,28 @@ Description: Directory for DAMON operations set layer-handled DAMOS filters. /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters directory. +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests/nr_dests +Date: Jul 2025 +Contact: SeongJae Park +Description: Writing a number 'N' to this file creates the number of + directories for setting action destinations of the scheme named + '0' to 'N-1' under the dests/ directory. + +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests//id +Date: Jul 2025 +Contact: SeongJae Park +Description: Writing to and reading from this file sets and gets the id of + the DAMOS action destination. For DAMOS_MIGRATE_{HOT,COLD} + actions, the destination node's node id can be written and + read. + +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests//weight +Date: Jul 2025 +Contact: SeongJae Park +Description: Writing to and reading from this file sets and gets the weight + of the DAMOS action destination to select as the destination of + each action among the destinations. + What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/nr_tried Date: Mar 2022 Contact: SeongJae Park diff --git a/Documentation/ABI/testing/sysfs-kernel-slab b/Documentation/ABI/testing/sysfs-kernel-slab index 658999be5164..b26e4299f822 100644 --- a/Documentation/ABI/testing/sysfs-kernel-slab +++ b/Documentation/ABI/testing/sysfs-kernel-slab @@ -37,7 +37,8 @@ Description: The alloc_calls file is read-only and lists the kernel code locations from which allocations for this cache were performed. The alloc_calls file only contains information if debugging is - enabled for that cache (see Documentation/mm/slub.rst). + enabled for that cache (see + Documentation/admin-guide/mm/slab.rst). What: /sys/kernel/slab//alloc_fastpath Date: February 2008 @@ -219,7 +220,7 @@ Contact: Pekka Enberg , Description: The free_calls file is read-only and lists the locations of object frees if slab debugging is enabled (see - Documentation/mm/slub.rst). + Documentation/admin-guide/mm/slab.rst). What: /sys/kernel/slab//free_fastpath Date: February 2008 diff --git a/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi b/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi index 1f1f274a6979..b4da7b2ea0ca 100644 --- a/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi +++ b/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi @@ -1,4 +1,4 @@ -What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type +What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_supported_type Date: Apr 2021 KernelVersion: 5.13 Contact: "" @@ -29,12 +29,12 @@ Description: For example to check which privacy devices are supported:: - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_supported_type [Microphone Mute] [supported] [Camera Shutter] [supported] [ePrivacy Screen] [unsupported] -What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state +What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_current_state Date: Apr 2021 KernelVersion: 5.13 Contact: "" @@ -66,6 +66,6 @@ Description: For example to check all supported current privacy device states:: - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_current_state [Microphone] [unmuted] [Camera Shutter] [unmuted] diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop index 4989ab266682..5ec0dee9e707 100644 --- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop @@ -27,15 +27,6 @@ Description: * 1 -> Switched On * 0 -> Switched Off -What: /sys/bus/platform/devices/VPC2004:*/conservation_mode -Date: Aug 2017 -KernelVersion: 4.14 -Contact: platform-driver-x86@vger.kernel.org -Description: - Controls whether the conservation mode is enabled or not. - This feature limits the maximum battery charge percentage to - around 50-60% in order to prolong the lifetime of the battery. - What: /sys/bus/platform/devices/VPC2004:*/fn_lock Date: May 2018 KernelVersion: 4.18 diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update b/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update index 02ae1e9bbfc8..7ffd1579b8f7 100644 --- a/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update +++ b/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update @@ -1,4 +1,4 @@ -What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request +What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651[-X]/firmware_update_request Date: April 2020 KernelVersion: 5.7 Contact: "Jithu Joseph" diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt index fd3a7ec79760..10ef1282c9d2 100644 --- a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt +++ b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt @@ -1,4 +1,4 @@ -What: /sys/devices/platform//force_power +What: /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341[-X]/force_power Date: September 2017 KernelVersion: 4.15 Contact: "Mario Limonciello" diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index 2192478e83cf..4d8e1ad020f0 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -1,6 +1,6 @@ What: /sys/power/ Date: August 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power directory will contain files that will provide a unified interface to the power management @@ -8,7 +8,7 @@ Description: What: /sys/power/state Date: November 2016 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/state file controls system sleep states. Reading from this file returns the available sleep state @@ -23,7 +23,7 @@ Description: What: /sys/power/mem_sleep Date: November 2016 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/mem_sleep file controls the operating mode of system suspend. Reading from it returns the available modes @@ -41,7 +41,7 @@ Description: What: /sys/power/disk Date: September 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/disk file controls the operating mode of the suspend-to-disk mechanism. Reading from this file returns @@ -90,7 +90,7 @@ Description: What: /sys/power/image_size Date: August 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/image_size file controls the size of the image created by the suspend-to-disk mechanism. It can be written a @@ -107,7 +107,7 @@ Description: What: /sys/power/pm_trace Date: August 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/pm_trace file controls the code which saves the last PM event point in the RTC across reboots, so that you can @@ -156,7 +156,7 @@ Description: What: /sys/power/pm_async Date: January 2009 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/pm_async file controls the switch allowing the user space to enable or disable asynchronous suspend and resume @@ -169,7 +169,7 @@ Description: What: /sys/power/wakeup_count Date: July 2010 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/wakeup_count file allows user space to put the system into a sleep state while taking into account the @@ -184,7 +184,7 @@ Description: What: /sys/power/reserved_size Date: May 2011 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/reserved_size file allows user space to control the amount of memory reserved for allocations made by device @@ -198,7 +198,7 @@ Description: What: /sys/power/autosleep Date: April 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/autosleep file can be written one of the strings returned by reads from /sys/power/state. If that happens, a @@ -215,7 +215,7 @@ Description: What: /sys/power/wake_lock Date: February 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/wake_lock file allows user space to create wakeup source objects and activate them on demand (if one of @@ -242,7 +242,7 @@ Description: What: /sys/power/wake_unlock Date: February 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/wake_unlock file allows user space to deactivate wakeup sources created with the help of /sys/power/wake_lock. @@ -283,7 +283,7 @@ Description: What: /sys/power/pm_debug_messages Date: July 2017 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: The /sys/power/pm_debug_messages file controls the printing of debug messages from the system suspend/hiberbation diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar index 857cf12b0904..1016967a730f 100644 --- a/Documentation/ABI/testing/sysfs-secvar +++ b/Documentation/ABI/testing/sysfs-secvar @@ -22,9 +22,13 @@ Description: A string indicating which backend is in use by the firmware. and is expected to be "ibm,edk2-compat-v1". On pseries/PLPKS, this is generated by the kernel based on the - version number in the SB_VERSION variable in the keystore, and - has the form "ibm,plpks-sb-v", or - "ibm,plpks-sb-unknown" if there is no SB_VERSION variable. + version number in the SB_VERSION variable in the keystore. The + version numbering in the SB_VERSION variable starts from 1. The + format string takes the form "ibm,plpks-sb-v" in the + case of dynamic key management mode. If the SB_VERSION variable + does not exist (or there is an error while reading it), it takes + the form "ibm,plpks-sb-v0", indicating that the key management + mode is static. What: /sys/firmware/secvar/vars/ Date: August 2019 @@ -34,6 +38,13 @@ Description: Each secure variable is represented as a directory named as representation. The data and size can be determined by reading their respective attribute files. + Only secvars relevant to the key management mode are exposed. + Only in the dynamic key management mode should the user have + access (read and write) to the secure boot secvars db, dbx, + grubdb, grubdbx, and sbat. These secvars are not consumed in the + static key management mode. PK, trustedcadb and moduledb are the + secvars common to both static and dynamic key management modes. + What: /sys/firmware/secvar/vars//size Date: August 2019 Contact: Nayna Jain diff --git a/Documentation/Makefile b/Documentation/Makefile index d30d66ddf1ad..b98477df5ddf 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -5,6 +5,7 @@ # for cleaning subdir- := devicetree/bindings +ifneq ($(MAKECMDGOALS),cleandocs) # Check for broken documentation file references ifeq ($(CONFIG_WARN_MISSING_DOCUMENTS),y) $(shell $(srctree)/scripts/documentation-file-ref-check --warn) @@ -14,6 +15,7 @@ endif ifeq ($(CONFIG_WARN_ABI_ERRORS),y) $(shell $(srctree)/scripts/get_abi.py --dir $(srctree)/Documentation/ABI validate) endif +endif # You can set these variables from the command line. SPHINXBUILD = sphinx-build diff --git a/Documentation/PCI/endpoint/pci-test-howto.rst b/Documentation/PCI/endpoint/pci-test-howto.rst index aafc17ef3fd3..dd66858cde46 100644 --- a/Documentation/PCI/endpoint/pci-test-howto.rst +++ b/Documentation/PCI/endpoint/pci-test-howto.rst @@ -203,3 +203,18 @@ controllers, it is advisable to skip this testcase using this command:: # pci_endpoint_test -f pci_ep_bar -f pci_ep_basic -v memcpy -T COPY_TEST -v dma + +Kselftest EP Doorbell +~~~~~~~~~~~~~~~~~~~~~ + +If the Endpoint MSI controller is used for the doorbell usecase, run below +command for testing it: + + # pci_endpoint_test -f pcie_ep_doorbell + + # Starting 1 tests from 1 test cases. + # RUN pcie_ep_doorbell.DOORBELL_TEST ... + # OK pcie_ep_doorbell.DOORBELL_TEST + ok 1 pcie_ep_doorbell.DOORBELL_TEST + # PASSED: 1 / 1 tests passed. + # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 diff --git a/Documentation/RCU/Design/Data-Structures/Data-Structures.rst b/Documentation/RCU/Design/Data-Structures/Data-Structures.rst index 04e16775c752..1b0aad184dd7 100644 --- a/Documentation/RCU/Design/Data-Structures/Data-Structures.rst +++ b/Documentation/RCU/Design/Data-Structures/Data-Structures.rst @@ -286,6 +286,39 @@ in order to detect the beginnings and ends of grace periods in a distributed fashion. The values flow from ``rcu_state`` to ``rcu_node`` (down the tree from the root to the leaves) to ``rcu_data``. ++-----------------------------------------------------------------------+ +| **Quick Quiz**: | ++-----------------------------------------------------------------------+ +| Given that the root rcu_node structure has a gp_seq field, | +| why does RCU maintain a separate gp_seq in the rcu_state structure? | +| Why not just use the root rcu_node's gp_seq as the official record | +| and update it directly when starting a new grace period? | ++-----------------------------------------------------------------------+ +| **Answer**: | ++-----------------------------------------------------------------------+ +| On single-node RCU trees (where the root node is also a leaf), | +| updating the root node's gp_seq immediately would create unnecessary | +| lock contention. Here's why: | +| | +| If we did rcu_seq_start() directly on the root node's gp_seq: | +| | +| 1. All CPUs would immediately see their node's gp_seq from their rdp's| +| gp_seq, in rcu_pending(). They would all then invoke the RCU-core. | +| 2. Which calls note_gp_changes() and try to acquire the node lock. | +| 3. But rnp->qsmask isn't initialized yet (happens later in | +| rcu_gp_init()) | +| 4. So each CPU would acquire the lock, find it can't determine if it | +| needs to report quiescent state (no qsmask), update rdp->gp_seq, | +| and release the lock. | +| 5. Result: Lots of lock acquisitions with no grace period progress | +| | +| By having a separate rcu_state.gp_seq, we can increment the official | +| grace period counter without immediately affecting what CPUs see in | +| their nodes. The hierarchical propagation in rcu_gp_init() then | +| updates the root node's gp_seq and qsmask together under the same lock| +| acquisition, avoiding this useless contention. | ++-----------------------------------------------------------------------+ + Miscellaneous ''''''''''''' diff --git a/Documentation/RCU/Design/Requirements/Requirements.rst b/Documentation/RCU/Design/Requirements/Requirements.rst index 6125e7068d2c..b0395540296b 100644 --- a/Documentation/RCU/Design/Requirements/Requirements.rst +++ b/Documentation/RCU/Design/Requirements/Requirements.rst @@ -1970,6 +1970,134 @@ corresponding CPU's leaf node lock is held. This avoids race conditions between RCU's hotplug notifier hooks, the grace period initialization code, and the FQS loop, all of which refer to or modify this bookkeeping. +Note that grace period initialization (rcu_gp_init()) must carefully sequence +CPU hotplug scanning with grace period state changes. For example, the +following race could occur in rcu_gp_init() if rcu_seq_start() were to happen +after the CPU hotplug scanning. + +.. code-block:: none + + CPU0 (rcu_gp_init) CPU1 CPU2 + --------------------- ---- ---- + // Hotplug scan first (WRONG ORDER) + rcu_for_each_leaf_node(rnp) { + rnp->qsmaskinit = rnp->qsmaskinitnext; + } + rcutree_report_cpu_starting() + rnp->qsmaskinitnext |= mask; + rcu_read_lock() + r0 = *X; + r1 = *X; + X = NULL; + cookie = get_state_synchronize_rcu(); + // cookie = 8 (future GP) + rcu_seq_start(&rcu_state.gp_seq); + // gp_seq = 5 + + // CPU1 now invisible to this GP! + rcu_for_each_node_breadth_first() { + rnp->qsmask = rnp->qsmaskinit; + // CPU1 not included! + } + + // GP completes without CPU1 + rcu_seq_end(&rcu_state.gp_seq); + // gp_seq = 8 + poll_state_synchronize_rcu(cookie); + // Returns true! + kfree(r1); + r2 = *r0; // USE-AFTER-FREE! + +By incrementing gp_seq first, CPU1's RCU read-side critical section +is guaranteed to not be missed by CPU2. + +**Concurrent Quiescent State Reporting for Offline CPUs** + +RCU must ensure that CPUs going offline report quiescent states to avoid +blocking grace periods. This requires careful synchronization to handle +race conditions + +**Race condition causing Offline CPU to hang GP** + +A race between CPU offlining and new GP initialization (gp_init) may occur +because `rcu_report_qs_rnp()` in `rcutree_report_cpu_dead()` must temporarily +release the `rcu_node` lock to wake the RCU grace-period kthread: + +.. code-block:: none + + CPU1 (going offline) CPU0 (GP kthread) + -------------------- ----------------- + rcutree_report_cpu_dead() + rcu_report_qs_rnp() + // Must release rnp->lock to wake GP kthread + raw_spin_unlock_irqrestore_rcu_node() + // Wakes up and starts new GP + rcu_gp_init() + // First loop: + copies qsmaskinitnext->qsmaskinit + // CPU1 still in qsmaskinitnext! + + // Second loop: + rnp->qsmask = rnp->qsmaskinit + mask = rnp->qsmask & ~rnp->qsmaskinitnext + // mask is 0! CPU1 still in both masks + // Reacquire lock (but too late) + rnp->qsmaskinitnext &= ~mask // Finally clears bit + +Without `ofl_lock`, the new grace period includes the offline CPU and waits +forever for its quiescent state causing a GP hang. + +**A solution with ofl_lock** + +The `ofl_lock` (offline lock) prevents `rcu_gp_init()` from running during +the vulnerable window when `rcu_report_qs_rnp()` has released `rnp->lock`: + +.. code-block:: none + + CPU0 (rcu_gp_init) CPU1 (rcutree_report_cpu_dead) + ------------------ ------------------------------ + rcu_for_each_leaf_node(rnp) { + arch_spin_lock(&ofl_lock) -----> arch_spin_lock(&ofl_lock) [BLOCKED] + + // Safe: CPU1 can't interfere + rnp->qsmaskinit = rnp->qsmaskinitnext + + arch_spin_unlock(&ofl_lock) ---> // Now CPU1 can proceed + } // But snapshot already taken + +**Another race causing GP hangs in rcu_gpu_init(): Reporting QS for Now-offline CPUs** + +After the first loop takes an atomic snapshot of online CPUs, as shown above, +the second loop in `rcu_gp_init()` detects CPUs that went offline between +releasing `ofl_lock` and acquiring the per-node `rnp->lock`. This detection is +crucial because: + +1. The CPU might have gone offline after the snapshot but before the second loop +2. The offline CPU cannot report its own QS if it's already dead +3. Without this detection, the grace period would wait forever for CPUs that + are now offline. + +The second loop performs this detection safely: + +.. code-block:: none + + rcu_for_each_node_breadth_first(rnp) { + raw_spin_lock_irqsave_rcu_node(rnp, flags); + rnp->qsmask = rnp->qsmaskinit; // Apply the snapshot + + // Detect CPUs offline after snapshot + mask = rnp->qsmask & ~rnp->qsmaskinitnext; + + if (mask && rcu_is_leaf_node(rnp)) + rcu_report_qs_rnp(mask, ...) // Report QS for offline CPUs + } + +This approach ensures atomicity: quiescent state reporting for offline CPUs +happens either in `rcu_gp_init()` (second loop) or in `rcutree_report_cpu_dead()`, +never both and never neither. The `rnp->lock` held throughout the sequence +prevents races - `rcutree_report_cpu_dead()` also acquires this lock when +clearing `qsmaskinitnext`, ensuring mutual exclusion. + Scheduler and RCU ~~~~~~~~~~~~~~~~~ diff --git a/Documentation/accel/amdxdna/amdnpu.rst b/Documentation/accel/amdxdna/amdnpu.rst index fbe0a7585345..42e54904f9a8 100644 --- a/Documentation/accel/amdxdna/amdnpu.rst +++ b/Documentation/accel/amdxdna/amdnpu.rst @@ -223,13 +223,13 @@ Userspace components Compiler -------- -Peano is an LLVM based open-source compiler for AMD XDNA Array compute tile -available at: +Peano is an LLVM based open-source single core compiler for AMD XDNA Array +compute tile. Peano is available at: https://github.com/Xilinx/llvm-aie -The open-source IREE compiler supports graph compilation of ML models for AMD -NPU and uses Peano underneath. It is available at: -https://github.com/nod-ai/iree-amd-aie +IRON is an open-source array compiler for AMD XDNA Array based NPU which uses +Peano underneath. IRON is available at: +https://github.com/Xilinx/mlir-aie Usermode Driver (UMD) --------------------- diff --git a/Documentation/accel/index.rst b/Documentation/accel/index.rst index bc85f26533d8..d8fa332d60a8 100644 --- a/Documentation/accel/index.rst +++ b/Documentation/accel/index.rst @@ -10,6 +10,7 @@ Compute Accelerators introduction amdxdna/index qaic/index + rocket/index .. only:: subproject and html diff --git a/Documentation/accel/rocket/index.rst b/Documentation/accel/rocket/index.rst new file mode 100644 index 000000000000..70f97bccf100 --- /dev/null +++ b/Documentation/accel/rocket/index.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +===================================== + accel/rocket Rockchip NPU driver +===================================== + +The accel/rocket driver supports the Neural Processing Units (NPUs) inside some +Rockchip SoCs such as the RK3588. Rockchip calls it RKNN and sometimes RKNPU. + +The hardware is described in chapter 36 in the RK3588 TRM. + +This driver just powers the hardware on and off, allocates and maps buffers to +the device and submits jobs to the frontend unit. Everything else is done in +userspace, as a Gallium driver (also called rocket) that is part of the Mesa3D +project. + +Hardware currently supported: + +* RK3588 diff --git a/Documentation/accounting/delay-accounting.rst b/Documentation/accounting/delay-accounting.rst index 210c194d4a7b..8ccc5af5ea1e 100644 --- a/Documentation/accounting/delay-accounting.rst +++ b/Documentation/accounting/delay-accounting.rst @@ -131,3 +131,59 @@ Get IO accounting for pid 1, it works only with -p:: linuxrc: read=65536, write=0, cancelled_write=0 The above command can be used with -v to get more debug information. + +After the system starts, use `delaytop` to get the system-wide delay information, +which includes system-wide PSI information and Top-N high-latency tasks. + +`delaytop` supports sorting by CPU latency in descending order by default, +displays the top 20 high-latency tasks by default, and refreshes the latency +data every 2 seconds by default. + +Get PSI information and Top-N tasks delay, since system boot:: + + bash# ./delaytop + System Pressure Information: (avg10/avg60/avg300/total) + CPU some: 0.0%/ 0.0%/ 0.0%/ 345(ms) + CPU full: 0.0%/ 0.0%/ 0.0%/ 0(ms) + Memory full: 0.0%/ 0.0%/ 0.0%/ 0(ms) + Memory some: 0.0%/ 0.0%/ 0.0%/ 0(ms) + IO full: 0.0%/ 0.0%/ 0.0%/ 65(ms) + IO some: 0.0%/ 0.0%/ 0.0%/ 79(ms) + IRQ full: 0.0%/ 0.0%/ 0.0%/ 0(ms) + Top 20 processes (sorted by CPU delay): + PID TGID COMMAND CPU(ms) IO(ms) SWAP(ms) RCL(ms) THR(ms) CMP(ms) WP(ms) IRQ(ms) + ---------------------------------------------------------------------------------------------- + 161 161 zombie_memcg_re 1.40 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 130 130 blkcg_punt_bio 1.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 444 444 scsi_tmf_0 0.73 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 1280 1280 rsyslogd 0.53 0.04 0.00 0.00 0.00 0.00 0.00 0.00 + 12 12 ksoftirqd/0 0.47 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 1277 1277 nbd-server 0.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 308 308 kworker/2:2-sys 0.41 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 55 55 netns 0.36 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 1187 1187 acpid 0.31 0.03 0.00 0.00 0.00 0.00 0.00 0.00 + 6184 6184 kworker/1:2-sys 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 186 186 kaluad 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 18 18 ksoftirqd/1 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 185 185 kmpath_rdacd 0.23 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 190 190 kstrp 0.23 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 2759 2759 agetty 0.20 0.03 0.00 0.00 0.00 0.00 0.00 0.00 + 1190 1190 kworker/0:3-sys 0.19 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 1272 1272 sshd 0.15 0.04 0.00 0.00 0.00 0.00 0.00 0.00 + 1156 1156 license 0.15 0.11 0.00 0.00 0.00 0.00 0.00 0.00 + 134 134 md 0.13 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + 6142 6142 kworker/3:2-xfs 0.13 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + +Dynamic interactive interface of delaytop:: + + # ./delaytop -p pid + Print delayacct stats + + # ./delaytop -P num + Display the top N tasks + + # ./delaytop -n num + Set delaytop refresh frequency (num times) + + # ./delaytop -d secs + Specify refresh interval as secs diff --git a/Documentation/admin-guide/LSM/SELinux.rst b/Documentation/admin-guide/LSM/SELinux.rst index 520a1c2c6fd2..cdd65164ca96 100644 --- a/Documentation/admin-guide/LSM/SELinux.rst +++ b/Documentation/admin-guide/LSM/SELinux.rst @@ -2,6 +2,17 @@ SELinux ======= +Information about the SELinux kernel subsystem can be found at the +following links: + + https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/tree/README.md + + https://github.com/selinuxproject/selinux-kernel/wiki + +Information about the SELinux userspace can be found at: + + https://github.com/SELinuxProject/selinux/wiki + If you want to use SELinux, chances are you will want to use the distro-provided policies, or install the latest reference policy release from diff --git a/Documentation/admin-guide/blockdev/zoned_loop.rst b/Documentation/admin-guide/blockdev/zoned_loop.rst index 9c7aa3b482f3..64dcfde7450a 100644 --- a/Documentation/admin-guide/blockdev/zoned_loop.rst +++ b/Documentation/admin-guide/blockdev/zoned_loop.rst @@ -79,7 +79,7 @@ zone_capacity_mb Device zone capacity (must always be equal to or lower than the zone size. Default: zone size. conv_zones Total number of conventioanl zones starting from sector 0. Default: 8. -base_dir Path to the base directoy where to create the directory +base_dir Path to the base directory where to create the directory containing the zone files of the device. Default=/var/local/zloop. The device directory containing the zone files is always diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst index 91339efdcb54..7a86042c9b6d 100644 --- a/Documentation/admin-guide/bootconfig.rst +++ b/Documentation/admin-guide/bootconfig.rst @@ -265,7 +265,7 @@ The final kernel cmdline will be the following:: Config File Limitation ====================== -Currently the maximum config size size is 32KB and the total key-words (not +Currently the maximum config size is 32KB and the total key-words (not key-value entries) must be under 1024 nodes. Note: this is not the number of entries but nodes, an entry must consume more than 2 nodes (a key-word and a value). So theoretically, it will be diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 0cc35a14afbe..51c0bc4c2dc5 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -435,6 +435,15 @@ both cgroups. Controlling Controllers ----------------------- +Availability +~~~~~~~~~~~~ + +A controller is available in a cgroup when it is supported by the kernel (i.e., +compiled in, not disabled and not attached to a v1 hierarchy) and listed in the +"cgroup.controllers" file. Availability means the controller's interface files +are exposed in the cgroup’s directory, allowing the distribution of the target +resource to be observed or controlled within that cgroup. + Enabling and Disabling ~~~~~~~~~~~~~~~~~~~~~~ @@ -1732,12 +1741,6 @@ The following nested keys are defined. numa_hint_faults (npn) Number of NUMA hinting faults. - numa_task_migrated (npn) - Number of task migration by NUMA balancing. - - numa_task_swapped (npn) - Number of task swap by NUMA balancing. - pgdemote_kswapd Number of pages demoted by kswapd. diff --git a/Documentation/admin-guide/device-mapper/thin-provisioning.rst b/Documentation/admin-guide/device-mapper/thin-provisioning.rst index bafebf79da4b..b2fa49a5608a 100644 --- a/Documentation/admin-guide/device-mapper/thin-provisioning.rst +++ b/Documentation/admin-guide/device-mapper/thin-provisioning.rst @@ -80,11 +80,11 @@ less sharing than average you'll need a larger-than-average metadata device. As a guide, we suggest you calculate the number of bytes to use in the metadata device as 48 * $data_dev_size / $data_block_size but round it up -to 2MB if the answer is smaller. If you're creating large numbers of +to 2MiB if the answer is smaller. If you're creating large numbers of snapshots which are recording large amounts of change, you may find you need to increase this. -The largest size supported is 16GB: If the device is larger, +The largest size supported is 16GiB: If the device is larger, a warning will be issued and the excess space will not be used. Reloading a pool table @@ -107,13 +107,13 @@ Using an existing pool device $data_block_size gives the smallest unit of disk space that can be allocated at a time expressed in units of 512-byte sectors. -$data_block_size must be between 128 (64KB) and 2097152 (1GB) and a -multiple of 128 (64KB). $data_block_size cannot be changed after the +$data_block_size must be between 128 (64KiB) and 2097152 (1GiB) and a +multiple of 128 (64KiB). $data_block_size cannot be changed after the thin-pool is created. People primarily interested in thin provisioning -may want to use a value such as 1024 (512KB). People doing lots of -snapshotting may want a smaller value such as 128 (64KB). If you are +may want to use a value such as 1024 (512KiB). People doing lots of +snapshotting may want a smaller value such as 128 (64KiB). If you are not zeroing newly-allocated data, a larger $data_block_size in the -region of 256000 (128MB) is suggested. +region of 262144 (128MiB) is suggested. $low_water_mark is expressed in blocks of size $data_block_size. If free space on the data device drops below this level then a dm event @@ -291,7 +291,7 @@ i) Constructor error_if_no_space: Error IOs, instead of queueing, if no space. - Data block size must be between 64KB (128 sectors) and 1GB + Data block size must be between 64KiB (128 sectors) and 1GiB (2097152 sectors) inclusive. diff --git a/Documentation/admin-guide/gpio/gpio-sim.rst b/Documentation/admin-guide/gpio/gpio-sim.rst index 35d49ccd49e0..f5135a14ef2e 100644 --- a/Documentation/admin-guide/gpio/gpio-sim.rst +++ b/Documentation/admin-guide/gpio/gpio-sim.rst @@ -50,8 +50,11 @@ the number of lines exposed by this bank. **Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/name`` -This group represents a single line at the offset Y. The 'name' attribute -allows to set the line name as represented by the 'gpio-line-names' property. +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/valid`` + +This group represents a single line at the offset Y. The ``valid`` attribute +indicates whether the line can be used as GPIO. The ``name`` attribute allows +to set the line name as represented by the 'gpio-line-names' property. **Item:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/hog`` diff --git a/Documentation/admin-guide/hw-vuln/attack_vector_controls.rst b/Documentation/admin-guide/hw-vuln/attack_vector_controls.rst new file mode 100644 index 000000000000..5964901d66e3 --- /dev/null +++ b/Documentation/admin-guide/hw-vuln/attack_vector_controls.rst @@ -0,0 +1,235 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Attack Vector Controls +====================== + +Attack vector controls provide a simple method to configure only the mitigations +for CPU vulnerabilities which are relevant given the intended use of a system. +Administrators are encouraged to consider which attack vectors are relevant and +disable all others in order to recoup system performance. + +When new relevant CPU vulnerabilities are found, they will be added to these +attack vector controls so administrators will likely not need to reconfigure +their command line parameters as mitigations will continue to be correctly +applied based on the chosen attack vector controls. + +Attack Vectors +-------------- + +There are 5 sets of attack-vector mitigations currently supported by the kernel: + +#. :ref:`user_kernel` +#. :ref:`user_user` +#. :ref:`guest_host` +#. :ref:`guest_guest` +#. :ref:`smt` + +To control the enabled attack vectors, see :ref:`cmdline`. + +.. _user_kernel: + +User-to-Kernel +^^^^^^^^^^^^^^ + +The user-to-kernel attack vector involves a malicious userspace program +attempting to leak kernel data into userspace by exploiting a CPU vulnerability. +The kernel data involved might be limited to certain kernel memory, or include +all memory in the system, depending on the vulnerability exploited. + +If no untrusted userspace applications are being run, such as with single-user +systems, consider disabling user-to-kernel mitigations. + +Note that the CPU vulnerabilities mitigated by Linux have generally not been +shown to be exploitable from browser-based sandboxes. User-to-kernel +mitigations are therefore mostly relevant if unknown userspace applications may +be run by untrusted users. + +*user-to-kernel mitigations are enabled by default* + +.. _user_user: + +User-to-User +^^^^^^^^^^^^ + +The user-to-user attack vector involves a malicious userspace program attempting +to influence the behavior of another unsuspecting userspace program in order to +exfiltrate data. The vulnerability of a userspace program is based on the +program itself and the interfaces it provides. + +If no untrusted userspace applications are being run, consider disabling +user-to-user mitigations. + +Note that because the Linux kernel contains a mapping of all physical memory, +preventing a malicious userspace program from leaking data from another +userspace program requires mitigating user-to-kernel attacks as well for +complete protection. + +*user-to-user mitigations are enabled by default* + +.. _guest_host: + +Guest-to-Host +^^^^^^^^^^^^^ + +The guest-to-host attack vector involves a malicious VM attempting to leak +hypervisor data into the VM. The data involved may be limited, or may +potentially include all memory in the system, depending on the vulnerability +exploited. + +If no untrusted VMs are being run, consider disabling guest-to-host mitigations. + +*guest-to-host mitigations are enabled by default if KVM support is present* + +.. _guest_guest: + +Guest-to-Guest +^^^^^^^^^^^^^^ + +The guest-to-guest attack vector involves a malicious VM attempting to influence +the behavior of another unsuspecting VM in order to exfiltrate data. The +vulnerability of a VM is based on the code inside the VM itself and the +interfaces it provides. + +If no untrusted VMs, or only a single VM is being run, consider disabling +guest-to-guest mitigations. + +Similar to the user-to-user attack vector, preventing a malicious VM from +leaking data from another VM requires mitigating guest-to-host attacks as well +due to the Linux kernel phys map. + +*guest-to-guest mitigations are enabled by default if KVM support is present* + +.. _smt: + +Cross-Thread +^^^^^^^^^^^^ + +The cross-thread attack vector involves a malicious userspace program or +malicious VM either observing or attempting to influence the behavior of code +running on the SMT sibling thread in order to exfiltrate data. + +Many cross-thread attacks can only be mitigated if SMT is disabled, which will +result in reduced CPU core count and reduced performance. + +If cross-thread mitigations are fully enabled ('auto,nosmt'), all mitigations +for cross-thread attacks will be enabled. SMT may be disabled depending on +which vulnerabilities are present in the CPU. + +If cross-thread mitigations are partially enabled ('auto'), mitigations for +cross-thread attacks will be enabled but SMT will not be disabled. + +If cross-thread mitigations are disabled, no mitigations for cross-thread +attacks will be enabled. + +Cross-thread mitigation may not be required if core-scheduling or similar +techniques are used to prevent untrusted workloads from running on SMT siblings. + +*cross-thread mitigations default to partially enabled* + +.. _cmdline: + +Command Line Controls +--------------------- + +Attack vectors are controlled through the mitigations= command line option. The +value provided begins with a global option and then may optionally include one +or more options to disable various attack vectors. + +Format: + | ``mitigations=[global]`` + | ``mitigations=[global],[attack vectors]`` + +Global options: + +============ ============================================================= +Option Description +============ ============================================================= +'off' All attack vectors disabled. +'auto' All attack vectors enabled, partial cross-thread mitigations. +'auto,nosmt' All attack vectors enabled, full cross-thread mitigations. +============ ============================================================= + +Attack vector options: + +================= ======================================= +Option Description +================= ======================================= +'no_user_kernel' Disables user-to-kernel mitigations. +'no_user_user' Disables user-to-user mitigations. +'no_guest_host' Disables guest-to-host mitigations. +'no_guest_guest' Disables guest-to-guest mitigations +'no_cross_thread' Disables all cross-thread mitigations. +================= ======================================= + +Multiple attack vector options may be specified in a comma-separated list. If +the global option is not specified, it defaults to 'auto'. The global option +'off' is equivalent to disabling all attack vectors. + +Examples: + | ``mitigations=auto,no_user_kernel`` + + Enable all attack vectors except user-to-kernel. Partial cross-thread + mitigations. + + | ``mitigations=auto,nosmt,no_guest_host,no_guest_guest`` + + Enable all attack vectors and cross-thread mitigations except for + guest-to-host and guest-to-guest mitigations. + + | ``mitigations=,no_cross_thread`` + + Enable all attack vectors but not cross-thread mitigations. + +Interactions with command-line options +-------------------------------------- + +Vulnerability-specific controls (e.g. "retbleed=off") take precedence over all +attack vector controls. Mitigations for individual vulnerabilities may be +turned on or off via their command-line options regardless of the attack vector +controls. + +Summary of attack-vector mitigations +------------------------------------ + +When a vulnerability is mitigated due to an attack-vector control, the default +mitigation option for that particular vulnerability is used. To use a different +mitigation, please use the vulnerability-specific command line option. + +The table below summarizes which vulnerabilities are mitigated when different +attack vectors are enabled and assuming the CPU is vulnerable. + +=============== ============== ============ ============= ============== ============ ======== +Vulnerability User-to-Kernel User-to-User Guest-to-Host Guest-to-Guest Cross-Thread Notes +=============== ============== ============ ============= ============== ============ ======== +BHI X X +ITS X X +GDS X X X X * (Note 1) +L1TF X X * (Note 2) +MDS X X X X * (Note 2) +MMIO X X X X * (Note 2) +Meltdown X +Retbleed X X * (Note 3) +RFDS X X X X +Spectre_v1 X +Spectre_v2 X X +Spectre_v2_user X X * (Note 1) +SRBDS X X X X +SRSO X X X X +SSB X +TAA X X X X * (Note 2) +TSA X X X X +=============== ============== ============ ============= ============== ============ ======== + +Notes: + 1 -- Can be mitigated without disabling SMT. + + 2 -- Disables SMT if cross-thread mitigations are fully enabled and the CPU + is vulnerable + + 3 -- Disables SMT if cross-thread mitigations are fully enabled, the CPU is + vulnerable, and STIBP is not supported + +When an attack-vector is disabled, all mitigations for the vulnerabilities +listed in the above table are disabled, unless mitigation is required for a +different enabled attack-vector or a mitigation is explicitly selected via a +vulnerability-specific command line option. diff --git a/Documentation/admin-guide/hw-vuln/index.rst b/Documentation/admin-guide/hw-vuln/index.rst index 09890a8f3ee9..55d747511f83 100644 --- a/Documentation/admin-guide/hw-vuln/index.rst +++ b/Documentation/admin-guide/hw-vuln/index.rst @@ -9,6 +9,7 @@ are configurable at compile, boot or run time. .. toctree:: :maxdepth: 1 + attack_vector_controls spectre l1tf mds @@ -25,3 +26,4 @@ are configurable at compile, boot or run time. rsb old_microcode indirect-target-selection + vmscape diff --git a/Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst b/Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst index 1302fd1b55e8..6dba18dbb9ab 100644 --- a/Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst +++ b/Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst @@ -157,9 +157,7 @@ This is achieved by using the otherwise unused and obsolete VERW instruction in combination with a microcode update. The microcode clears the affected CPU buffers when the VERW instruction is executed. -Kernel reuses the MDS function to invoke the buffer clearing: - - mds_clear_cpu_buffers() +Kernel does the buffer clearing with x86_clear_cpu_buffers(). On MDS affected CPUs, the kernel already invokes CPU buffer clear on kernel/userspace, hypervisor/guest and C-state (idle) transitions. No diff --git a/Documentation/admin-guide/hw-vuln/vmscape.rst b/Documentation/admin-guide/hw-vuln/vmscape.rst new file mode 100644 index 000000000000..d9b9a2b6c114 --- /dev/null +++ b/Documentation/admin-guide/hw-vuln/vmscape.rst @@ -0,0 +1,110 @@ +.. SPDX-License-Identifier: GPL-2.0 + +VMSCAPE +======= + +VMSCAPE is a vulnerability that may allow a guest to influence the branch +prediction in host userspace. It particularly affects hypervisors like QEMU. + +Even if a hypervisor may not have any sensitive data like disk encryption keys, +guest-userspace may be able to attack the guest-kernel using the hypervisor as +a confused deputy. + +Affected processors +------------------- + +The following CPU families are affected by VMSCAPE: + +**Intel processors:** + - Skylake generation (Parts without Enhanced-IBRS) + - Cascade Lake generation - (Parts affected by ITS guest/host separation) + - Alder Lake and newer (Parts affected by BHI) + +Note that, BHI affected parts that use BHB clearing software mitigation e.g. +Icelake are not vulnerable to VMSCAPE. + +**AMD processors:** + - Zen series (families 0x17, 0x19, 0x1a) + +** Hygon processors:** + - Family 0x18 + +Mitigation +---------- + +Conditional IBPB +---------------- + +Kernel tracks when a CPU has run a potentially malicious guest and issues an +IBPB before the first exit to userspace after VM-exit. If userspace did not run +between VM-exit and the next VM-entry, no IBPB is issued. + +Note that the existing userspace mitigation against Spectre-v2 is effective in +protecting the userspace. They are insufficient to protect the userspace VMMs +from a malicious guest. This is because Spectre-v2 mitigations are applied at +context switch time, while the userspace VMM can run after a VM-exit without a +context switch. + +Vulnerability enumeration and mitigation is not applied inside a guest. This is +because nested hypervisors should already be deploying IBPB to isolate +themselves from nested guests. + +SMT considerations +------------------ + +When Simultaneous Multi-Threading (SMT) is enabled, hypervisors can be +vulnerable to cross-thread attacks. For complete protection against VMSCAPE +attacks in SMT environments, STIBP should be enabled. + +The kernel will issue a warning if SMT is enabled without adequate STIBP +protection. Warning is not issued when: + +- SMT is disabled +- STIBP is enabled system-wide +- Intel eIBRS is enabled (which implies STIBP protection) + +System information and options +------------------------------ + +The sysfs file showing VMSCAPE mitigation status is: + + /sys/devices/system/cpu/vulnerabilities/vmscape + +The possible values in this file are: + + * 'Not affected': + + The processor is not vulnerable to VMSCAPE attacks. + + * 'Vulnerable': + + The processor is vulnerable and no mitigation has been applied. + + * 'Mitigation: IBPB before exit to userspace': + + Conditional IBPB mitigation is enabled. The kernel tracks when a CPU has + run a potentially malicious guest and issues an IBPB before the first + exit to userspace after VM-exit. + + * 'Mitigation: IBPB on VMEXIT': + + IBPB is issued on every VM-exit. This occurs when other mitigations like + RETBLEED or SRSO are already issuing IBPB on VM-exit. + +Mitigation control on the kernel command line +---------------------------------------------- + +The mitigation can be controlled via the ``vmscape=`` command line parameter: + + * ``vmscape=off``: + + Disable the VMSCAPE mitigation. + + * ``vmscape=ibpb``: + + Enable conditional IBPB mitigation (default when CONFIG_MITIGATION_VMSCAPE=y). + + * ``vmscape=force``: + + Force vulnerability detection and mitigation even on processors that are + not known to be affected. diff --git a/Documentation/admin-guide/kdump/kdump.rst b/Documentation/admin-guide/kdump/kdump.rst index 20fabdf6567e..9c6cd52f69cf 100644 --- a/Documentation/admin-guide/kdump/kdump.rst +++ b/Documentation/admin-guide/kdump/kdump.rst @@ -311,6 +311,27 @@ crashkernel syntax crashkernel=0,low +4) crashkernel=size,cma + + Reserve additional crash kernel memory from CMA. This reservation is + usable by the first system's userspace memory and kernel movable + allocations (memory balloon, zswap). Pages allocated from this memory + range will not be included in the vmcore so this should not be used if + dumping of userspace memory is intended and it has to be expected that + some movable kernel pages may be missing from the dump. + + A standard crashkernel reservation, as described above, is still needed + to hold the crash kernel and initrd. + + This option increases the risk of a kdump failure: DMA transfers + configured by the first kernel may end up corrupting the second + kernel's memory. + + This reservation method is intended for systems that can't afford to + sacrifice enough memory for standard crashkernel reservation and where + less reliable and possibly incomplete kdump is preferable to no kdump at + all. + Boot into System Kernel ----------------------- 1) Update the boot loader (such as grub, yaboot, or lilo) configuration diff --git a/Documentation/admin-guide/kdump/vmcoreinfo.rst b/Documentation/admin-guide/kdump/vmcoreinfo.rst index 8cf4614385b7..404a15f6782c 100644 --- a/Documentation/admin-guide/kdump/vmcoreinfo.rst +++ b/Documentation/admin-guide/kdump/vmcoreinfo.rst @@ -325,14 +325,14 @@ NR_FREE_PAGES On linux-2.6.21 or later, the number of free pages is in vm_stat[NR_FREE_PAGES]. Used to get the number of free pages. -PG_lru|PG_private|PG_swapcache|PG_swapbacked|PG_slab|PG_hwpoision|PG_head_mask|PG_hugetlb ------------------------------------------------------------------------------------------ +PG_lru|PG_private|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask +-------------------------------------------------------------------------- Page attributes. These flags are used to filter various unnecessary for dumping pages. -PAGE_BUDDY_MAPCOUNT_VALUE(~PG_buddy)|PAGE_OFFLINE_MAPCOUNT_VALUE(~PG_offline)|PAGE_OFFLINE_MAPCOUNT_VALUE(~PG_unaccepted) -------------------------------------------------------------------------------------------------------------------------- +PAGE_SLAB_MAPCOUNT_VALUE|PAGE_BUDDY_MAPCOUNT_VALUE|PAGE_OFFLINE_MAPCOUNT_VALUE|PAGE_HUGETLB_MAPCOUNT_VALUE|PAGE_UNACCEPTED_MAPCOUNT_VALUE +------------------------------------------------------------------------------------------------------------------------------------------ More page attributes. These flags are used to filter various unnecessary for dumping pages. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index f1f2c0874da9..5a7a83c411e9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -633,6 +633,14 @@ named mounts. Specifying both "all" and "named" disables all v1 hierarchies. + cgroup_v1_proc= [KNL] Show also missing controllers in /proc/cgroups + Format: { "true" | "false" } + /proc/cgroups lists only v1 controllers by default. + This compatibility option enables listing also v2 + controllers (whose v1 code is not compiled!), so that + semi-legacy software can check this file to decide + about usage of v2 (sic) controllers. + cgroup_favordynmods= [KNL] Enable or Disable favordynmods. Format: { "true" | "false" } Defaults to the value of CONFIG_CGROUP_FAVOR_DYNMODS. @@ -986,6 +994,28 @@ 0: to disable low allocation. It will be ignored when crashkernel=X,high is not used or memory reserved is below 4G. + crashkernel=size[KMG],cma + [KNL, X86] Reserve additional crash kernel memory from + CMA. This reservation is usable by the first system's + userspace memory and kernel movable allocations (memory + balloon, zswap). Pages allocated from this memory range + will not be included in the vmcore so this should not + be used if dumping of userspace memory is intended and + it has to be expected that some movable kernel pages + may be missing from the dump. + + A standard crashkernel reservation, as described above, + is still needed to hold the crash kernel and initrd. + + This option increases the risk of a kdump failure: DMA + transfers configured by the first kernel may end up + corrupting the second kernel's memory. + + This reservation method is intended for systems that + can't afford to sacrifice enough memory for standard + crashkernel reservation and where less reliable and + possibly incomplete kdump is preferable to no kdump at + all. cryptomgr.notests [KNL] Disable crypto self-tests @@ -1798,6 +1828,27 @@ backtraces on all cpus. Format: 0 | 1 + hash_pointers= + [KNL,EARLY] + By default, when pointers are printed to the console + or buffers via the %p format string, that pointer is + "hashed", i.e. obscured by hashing the pointer value. + This is a security feature that hides actual kernel + addresses from unprivileged users, but it also makes + debugging the kernel more difficult since unequal + pointers can no longer be compared. The choices are: + Format: { auto | always | never } + Default: auto + + auto - Hash pointers unless slab_debug is enabled. + always - Always hash pointers (even if slab_debug is + enabled). + never - Never hash pointers. This option should only + be specified when debugging the kernel. Do + not use on production kernels. The boot + param "no_hash_pointers" is an alias for + this mode. + hashdist= [KNL,NUMA] Large hashes allocated during boot are distributed across NUMA nodes. Defaults on for 64-bit NUMA, off otherwise. @@ -2212,6 +2263,11 @@ 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" + Note that disabling IMA is limited to kdump kernel. + indirect_target_selection= [X86,Intel] Mitigation control for Indirect Target Selection(ITS) bug in Intel CPUs. Updated microcode is also required for a fix in IBPB. @@ -2538,6 +2594,13 @@ requires the kernel to be built with CONFIG_ARM64_PSEUDO_NMI. + irqchip.riscv_imsic_noipi + [RISC-V,EARLY] + Force the kernel to not use IMSIC software injected MSIs + as IPIs. Intended for system where IMSIC is trap-n-emulated, + and thus want to reduce MMIO traps when triggering IPIs + to multiple harts. + irqfixup [HW] When an interrupt is not handled search all handlers for it. Intended to get systems with badly broken @@ -3766,6 +3829,7 @@ srbds=off [X86,INTEL] ssbd=force-off [ARM64] tsx_async_abort=off [X86] + vmscape=off [X86] Exceptions: This does not have any effect on @@ -3790,6 +3854,10 @@ mmio_stale_data=full,nosmt [X86] retbleed=auto,nosmt [X86] + [X86] After one of the above options, additionally + supports attack-vector based controls as documented in + Documentation/admin-guide/hw-vuln/attack_vector_controls.rst + mminit_loglevel= [KNL,EARLY] When CONFIG_DEBUG_MEMORY_INIT is set, this parameter allows control of the logging verbosity for @@ -4170,18 +4238,7 @@ no_hash_pointers [KNL,EARLY] - Force pointers printed to the console or buffers to be - unhashed. By default, when a pointer is printed via %p - format string, that pointer is "hashed", i.e. obscured - by hashing the pointer value. This is a security feature - that hides actual kernel addresses from unprivileged - users, but it also makes debugging the kernel more - difficult since unequal pointers can no longer be - compared. However, if this command-line option is - specified, then all normal pointers will have their true - value printed. This option should only be specified when - debugging the kernel. Please do not use on production - kernels. + Alias for "hash_pointers=never". nohibernate [HIBERNATION] Disable hibernation and resume. @@ -4533,7 +4590,7 @@ bit 2: print timer info bit 3: print locks info if CONFIG_LOCKDEP is on bit 4: print ftrace buffer - bit 5: print all printk messages in buffer + bit 5: replay all messages on consoles at the end of panic bit 6: print all CPUs backtrace (if available in the arch) bit 7: print only tasks in uninterruptible (blocked) state *Be aware* that this option may print a _lot_ of lines, @@ -4541,6 +4598,25 @@ Use this option carefully, maybe worth to setup a bigger log buffer with "log_buf_len" along with this. + panic_sys_info= A comma separated list of extra information to be dumped + on panic. + Format: val[,val...] + Where @val can be any of the following: + + tasks: print all tasks info + mem: print system memory info + timers: print timers info + locks: print locks info if CONFIG_LOCKDEP is on + ftrace: print ftrace buffer + all_bt: print all CPUs backtrace (if available in the arch) + blocked_tasks: print only tasks in uninterruptible (blocked) state + + This is a human readable alternative to the 'panic_print' option. + + panic_console_replay + When panic happens, replay all kernel messages on + consoles at the end of panic. + parkbd.port= [HW] Parallel port number the keyboard adapter is connected to, default is 0. Format: @@ -5000,6 +5076,18 @@ that number, otherwise (e.g., 'pmu_override=on'), MMCR1 remains 0. + pm_async= [PM] + Format: off + This parameter sets the initial value of the + /sys/power/pm_async sysfs knob at boot time. + If set to "off", disables asynchronous suspend and + resume of devices during system-wide power transitions. + This can be useful on platforms where device + dependencies are not well-defined, or for debugging + power management issues. Asynchronous operations are + enabled by default. + + pm_debug_messages [SUSPEND,KNL] Enable suspend/resume debug messages during boot up. @@ -5485,7 +5573,8 @@ echo 1 > /sys/module/rcutree/parameters/rcu_normal_wake_from_gp or pass a boot parameter "rcutree.rcu_normal_wake_from_gp=1" - Default is 0. + Default is 1 if num_possible_cpus() <= 16 and it is not explicitly + disabled by the boot parameter passing 0. rcuscale.gp_async= [KNL] Measure performance of asynchronous @@ -6387,6 +6476,11 @@ sa1100ir [NET] See drivers/net/irda/sa1100_ir.c. + sched_proxy_exec= [KNL] + Enables or disables "proxy execution" style + solution to mutex-based priority inversion. + Format: + sched_verbose [KNL,EARLY] Enables verbose scheduler debug messages. schedstats= [KNL,X86] Enable or disable scheduled statistics. @@ -6558,14 +6652,18 @@ slab_debug can create guard zones around objects and may poison objects when not in use. Also tracks the last alloc / free. For more information see - Documentation/mm/slub.rst. + Documentation/admin-guide/mm/slab.rst. (slub_debug legacy name also accepted for now) + Using this option implies the "no_hash_pointers" + option which can be undone by adding the + "hash_pointers=always" option. + slab_max_order= [MM] Determines the maximum allowed order for slabs. A high setting may cause OOMs due to memory fragmentation. For more information see - Documentation/mm/slub.rst. + Documentation/admin-guide/mm/slab.rst. (slub_max_order legacy name also accepted for now) slab_merge [MM] @@ -6580,13 +6678,14 @@ the number of objects indicated. The higher the number of objects the smaller the overhead of tracking slabs and the less frequently locks need to be acquired. - For more information see Documentation/mm/slub.rst. + For more information see + Documentation/admin-guide/mm/slab.rst. (slub_min_objects legacy name also accepted for now) slab_min_order= [MM] Determines the minimum page order for slabs. Must be lower or equal to slab_max_order. For more information see - Documentation/mm/slub.rst. + Documentation/admin-guide/mm/slab.rst. (slub_min_order legacy name also accepted for now) slab_nomerge [MM] @@ -6600,7 +6699,8 @@ cache (risks via metadata attacks are mostly unchanged). Debug options disable merging on their own. - For more information see Documentation/mm/slub.rst. + For more information see + Documentation/admin-guide/mm/slab.rst. (slub_nomerge legacy name also accepted for now) slab_strict_numa [MM] @@ -6988,6 +7088,11 @@ consumed by the stack hash table. By default this is set to false. + stack_depot_max_pools= [KNL,EARLY] + Specify the maximum number of pools to use for storing + stack traces. Pools are allocated on-demand up to this + limit. Default value is 8191 pools. + stacktrace [FTRACE] Enabled the stack tracer on boot up. @@ -7214,6 +7319,14 @@ causing a major performance hit, and the space where machines are deployed is by other means guarded. + tpm_crb_ffa.busy_timeout_ms= [ARM64,TPM] + Maximum time in milliseconds to retry sending a message + to the TPM service before giving up. This parameter controls + how long the system will continue retrying when the TPM + service is busy. + Format: + Default: 2000 (2 seconds) + tpm_suspend_pcr=[HW,TPM] Format: integer pcr id Specify that at suspend time, the tpm driver @@ -7488,6 +7601,19 @@ having this key zero'ed is acceptable. E.g. in testing scenarios. + tsa= [X86] Control mitigation for Transient Scheduler + Attacks on AMD CPUs. Search the following in your + favourite search engine for more details: + + "Technical guidance for mitigating transient scheduler + attacks". + + off - disable the mitigation + on - enable the mitigation (default) + user - mitigate only user/kernel transitions + vm - mitigate only guest/host transitions + + tsc= Disable clocksource stability checks for TSC. Format: [x86] reliable: mark tsc clocksource as reliable, this @@ -7916,6 +8042,16 @@ vmpoff= [KNL,S390] Perform z/VM CP command after power off. Format: + vmscape= [X86] Controls mitigation for VMscape attacks. + VMscape attacks can leak information from a userspace + hypervisor to a guest via speculative side-channels. + + off - disable the mitigation + ibpb - use Indirect Branch Prediction Barrier + (IBPB) mitigation (default) + force - force vulnerability detection even on + unaffected processors + vsyscall= [X86-64,EARLY] Controls the behavior of vsyscalls (i.e. calls to fixed addresses of 0xffffffffff600x00 from legacy diff --git a/Documentation/admin-guide/mm/damon/index.rst b/Documentation/admin-guide/mm/damon/index.rst index bc7e976120e0..3ce3164480c7 100644 --- a/Documentation/admin-guide/mm/damon/index.rst +++ b/Documentation/admin-guide/mm/damon/index.rst @@ -14,3 +14,4 @@ access monitoring and access-aware system operations. usage reclaim lru_sort + stat diff --git a/Documentation/admin-guide/mm/damon/stat.rst b/Documentation/admin-guide/mm/damon/stat.rst new file mode 100644 index 000000000000..4c517c2c219a --- /dev/null +++ b/Documentation/admin-guide/mm/damon/stat.rst @@ -0,0 +1,69 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================== +Data Access Monitoring Results Stat +=================================== + +Data Access Monitoring Results Stat (DAMON_STAT) is a static kernel module that +is aimed to be used for simple access pattern monitoring. It monitors accesses +on the system's entire physical memory using DAMON, and provides simplified +access monitoring results statistics, namely idle time percentiles and +estimated memory bandwidth. + +Monitoring Accuracy and Overhead +================================ + +DAMON_STAT uses monitoring intervals :ref:`auto-tuning +` to make its accuracy high and +overhead minimum. It auto-tunes the intervals aiming 4 % of observable access +events to be captured in each snapshot, while limiting the resulting sampling +events to be 5 milliseconds in minimum and 10 seconds in maximum. On a few +production server systems, it resulted in consuming only 0.x % single CPU time, +while capturing reasonable quality of access patterns. + +Interface: Module Parameters +============================ + +To use this feature, you should first ensure your system is running on a kernel +that is built with ``CONFIG_DAMON_STAT=y``. The feature can be enabled by +default at build time, by setting ``CONFIG_DAMON_STAT_ENABLED_DEFAULT`` true. + +To let sysadmins enable or disable it at boot and/or runtime, and read the +monitoring results, DAMON_STAT provides module parameters. Following +sections are descriptions of the parameters. + +enabled +------- + +Enable or disable DAMON_STAT. + +You can enable DAMON_STAT by setting the value of this parameter as ``Y``. +Setting it as ``N`` disables DAMON_STAT. The default value is set by +``CONFIG_DAMON_STAT_ENABLED_DEFAULT`` build config option. + +estimated_memory_bandwidth +-------------------------- + +Estimated memory bandwidth consumption (bytes per second) of the system. + +DAMON_STAT reads observed access events on the current DAMON results snapshot +and converts it to memory bandwidth consumption estimation in bytes per second. +The resulting metric is exposed to user via this read-only parameter. Because +DAMON uses sampling, this is only an estimation of the access intensity rather +than accurate memory bandwidth. + +memory_idle_ms_percentiles +-------------------------- + +Per-byte idle time (milliseconds) percentiles of the system. + +DAMON_STAT calculates how long each byte of the memory was not accessed until +now (idle time), based on the current DAMON results snapshot. If DAMON found a +region of access frequency (nr_accesses) larger than zero, every byte of the +region gets zero idle time. If a region has zero access frequency +(nr_accesses), how long the region was keeping the zero access frequency (age) +becomes the idle time of every byte of the region. Then, DAMON_STAT exposes +the percentiles of the idle time values via this read-only parameter. Reading +the parameter returns 101 idle time values in milliseconds, separated by comma. +Each value represents 0-th, 1st, 2nd, 3rd, ..., 99th and 100th percentile idle +times. diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index d960aba72b82..ff3a2dda1f02 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -59,7 +59,7 @@ comma (","). :ref:`/sys/kernel/mm/damon `/admin │ :ref:`kdamonds `/nr_kdamonds - │ │ :ref:`0 `/state,pid + │ │ :ref:`0 `/state,pid,refresh_ms │ │ │ :ref:`contexts `/nr_contexts │ │ │ │ :ref:`0 `/avail_operations,operations │ │ │ │ │ :ref:`monitoring_attrs `/ @@ -85,6 +85,8 @@ comma (","). │ │ │ │ │ │ │ :ref:`watermarks `/metric,interval_us,high,mid,low │ │ │ │ │ │ │ :ref:`{core_,ops_,}filters `/nr_filters │ │ │ │ │ │ │ │ 0/type,matching,allow,memcg_path,addr_start,addr_end,target_idx,min,max + │ │ │ │ │ │ │ :ref:`dests `/nr_dests + │ │ │ │ │ │ │ │ 0/id,weight │ │ │ │ │ │ │ :ref:`stats `/nr_tried,sz_tried,nr_applied,sz_applied,sz_ops_filter_passed,qt_exceeds │ │ │ │ │ │ │ :ref:`tried_regions `/total_bytes │ │ │ │ │ │ │ │ 0/start,end,nr_accesses,age,sz_filter_passed @@ -121,8 +123,8 @@ kdamond. kdamonds// ------------- -In each kdamond directory, two files (``state`` and ``pid``) and one directory -(``contexts``) exist. +In each kdamond directory, three files (``state``, ``pid`` and ``refresh_ms``) +and one directory (``contexts``) exist. Reading ``state`` returns ``on`` if the kdamond is currently running, or ``off`` if it is not running. @@ -159,6 +161,13 @@ Users can write below commands for the kdamond to the ``state`` file. If the state is ``on``, reading ``pid`` shows the pid of the kdamond thread. +Users can ask the kernel to periodically update files showing auto-tuned +parameters and DAMOS stats instead of manually writing +``update_tuned_intervals`` like keywords to ``state`` file. For this, users +should write the desired update time interval in milliseconds to ``refresh_ms`` +file. If the interval is zero, the periodic update is disabled. Reading the +file shows currently set time interval. + ``contexts`` directory contains files for controlling the monitoring contexts that this kdamond will execute. @@ -307,10 +316,10 @@ to ``N-1``. Each directory represents each DAMON-based operation scheme. schemes// ------------ -In each scheme directory, seven directories (``access_pattern``, ``quotas``, -``watermarks``, ``core_filters``, ``ops_filters``, ``filters``, ``stats``, and -``tried_regions``) and three files (``action``, ``target_nid`` and -``apply_interval``) exist. +In each scheme directory, eight directories (``access_pattern``, ``quotas``, +``watermarks``, ``core_filters``, ``ops_filters``, ``filters``, ``dests``, +``stats``, and ``tried_regions``) and three files (``action``, ``target_nid`` +and ``apply_interval``) exist. The ``action`` file is for setting and getting the scheme's :ref:`action `. The keywords that can be written to and read @@ -484,6 +493,29 @@ Refer to the :ref:`DAMOS filters design documentation of different ``allow`` works, when each of the filters are supported, and differences on stats. +.. _damon_sysfs_dests: + +schemes//dests/ +------------------ + +Directory for specifying the destinations of given DAMON-based operation +scheme's action. This directory is ignored if the action of the given scheme +is not supporting multiple destinations. Only ``DAMOS_MIGRATE_{HOT,COLD}`` +actions are supporting multiple destinations. + +In the beginning, the directory has only one file, ``nr_dests``. Writing a +number (``N``) to the file creates the number of child directories named ``0`` +to ``N-1``. Each directory represents each action destination. + +Each destination directory contains two files, namely ``id`` and ``weight``. +Users can write and read the identifier of the destination to ``id`` file. +For ``DAMOS_MIGRATE_{HOT,COLD}`` actions, the migrate destination node's node +id should be written to ``id`` file. Users can write and read the weight of +the destination among the given destinations to the ``weight`` file. The +weight can be an arbitrary integer. When DAMOS apply the action to each entity +of the memory region, it will select the destination of the action based on the +relative weights of the destinations. + .. _sysfs_schemes_stats: schemes//stats/ diff --git a/Documentation/admin-guide/mm/index.rst b/Documentation/admin-guide/mm/index.rst index 2d2f6c222308..ebc83ca20fdc 100644 --- a/Documentation/admin-guide/mm/index.rst +++ b/Documentation/admin-guide/mm/index.rst @@ -37,6 +37,7 @@ the Linux memory management. numaperf pagemap shrinker_debugfs + slab soft-dirty swap_numa transhuge diff --git a/Documentation/mm/slub.rst b/Documentation/admin-guide/mm/slab.rst similarity index 97% rename from Documentation/mm/slub.rst rename to Documentation/admin-guide/mm/slab.rst index 84ca1dc94e5e..14429ab90611 100644 --- a/Documentation/mm/slub.rst +++ b/Documentation/admin-guide/mm/slab.rst @@ -1,13 +1,12 @@ -========================== -Short users guide for SLUB -========================== +======================================== +Short users guide for the slab allocator +======================================== -The basic philosophy of SLUB is very different from SLAB. SLAB -requires rebuilding the kernel to activate debug options for all -slab caches. SLUB always includes full debugging but it is off by default. -SLUB can enable debugging only for selected slabs in order to avoid -an impact on overall system performance which may make a bug more -difficult to find. +The slab allocator includes full debugging support (when built with +CONFIG_SLUB_DEBUG=y) but it is off by default (unless built with +CONFIG_SLUB_DEBUG_ON=y). You can enable debugging only for selected +slabs in order to avoid an impact on overall system performance which +may make a bug more difficult to find. In order to switch debugging on one can add an option ``slab_debug`` to the kernel command line. That will enable full debugging for diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index dff8d5985f0f..370fba113460 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -107,7 +107,7 @@ sysfs Global THP controls ------------------- -Transparent Hugepage Support for anonymous memory can be entirely disabled +Transparent Hugepage Support for anonymous memory can be disabled (mostly for debugging purposes) or only enabled inside MADV_HUGEPAGE regions (to avoid the risk of consuming more memory resources) or enabled system wide. This can be achieved per-supported-THP-size with one of:: @@ -119,6 +119,11 @@ system wide. This can be achieved per-supported-THP-size with one of:: where is the hugepage size being addressed, the available sizes for which vary by system. +.. note:: Setting "never" in all sysfs THP controls does **not** disable + Transparent Huge Pages globally. This is because ``madvise(..., + MADV_COLLAPSE)`` ignores these settings and collapses ranges to + PMD-sized huge pages unconditionally. + For example:: echo always >/sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled @@ -187,7 +192,9 @@ madvise behaviour. never - should be self-explanatory. + should be self-explanatory. Note that ``madvise(..., + MADV_COLLAPSE)`` can still cause transparent huge pages to be + obtained even if this mode is specified everywhere. By default kernel tries to use huge, PMD-mappable zero page on read page fault to anonymous mapping. It's possible to disable huge zero @@ -378,7 +385,9 @@ always Attempt to allocate huge pages every time we need a new page; never - Do not allocate huge pages; + Do not allocate huge pages. Note that ``madvise(..., MADV_COLLAPSE)`` + can still cause transparent huge pages to be obtained even if this mode + is specified everywhere; within_size Only allocate huge page if it will be fully within i_size. @@ -434,7 +443,9 @@ inherit have enabled="inherit" and all other hugepage sizes have enabled="never"; never - Do not allocate huge pages; + Do not allocate huge pages. Note that ``madvise(..., + MADV_COLLAPSE)`` can still cause transparent huge pages to be obtained + even if this mode is specified everywhere; within_size Only allocate huge page if it will be fully within i_size. diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst index 412423c54f25..e1771f2225d5 100644 --- a/Documentation/admin-guide/pm/amd-pstate.rst +++ b/Documentation/admin-guide/pm/amd-pstate.rst @@ -72,7 +72,7 @@ to manage each performance update behavior. :: Lowest non- | | | | linear perf ------>+-----------------------+ +-----------------------+ | | | | - | | Lowest perf ---->| | + | | Min perf ---->| | | | | | Lowest perf ------>+-----------------------+ +-----------------------+ | | | | diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 2d74af7f0efe..cacb9f0307dd 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -398,7 +398,9 @@ policy limits change after that. This governor does not do anything by itself. Instead, it allows user space to set the CPU frequency for the policy it is attached to by writing to the -``scaling_setspeed`` attribute of that policy. +``scaling_setspeed`` attribute of that policy. Though the intention may be to +set an exact frequency for the policy, the actual frequency may vary depending +on hardware coordination, thermal and power limits, and other factors. ``schedutil`` ------------- diff --git a/Documentation/admin-guide/syscall-user-dispatch.rst b/Documentation/admin-guide/syscall-user-dispatch.rst index e3cfffef5a63..c1768d9e80fa 100644 --- a/Documentation/admin-guide/syscall-user-dispatch.rst +++ b/Documentation/admin-guide/syscall-user-dispatch.rst @@ -53,20 +53,25 @@ following prctl: prctl(PR_SET_SYSCALL_USER_DISPATCH, , , , [selector]) - is either PR_SYS_DISPATCH_ON or PR_SYS_DISPATCH_OFF, to enable and -disable the mechanism globally for that thread. When -PR_SYS_DISPATCH_OFF is used, the other fields must be zero. + is either PR_SYS_DISPATCH_EXCLUSIVE_ON/PR_SYS_DISPATCH_INCLUSIVE_ON +or PR_SYS_DISPATCH_OFF, to enable and disable the mechanism globally for +that thread. When PR_SYS_DISPATCH_OFF is used, the other fields must be zero. -[, +) delimit a memory region interval -from which syscalls are always executed directly, regardless of the -userspace selector. This provides a fast path for the C library, which -includes the most common syscall dispatchers in the native code -applications, and also provides a way for the signal handler to return +For PR_SYS_DISPATCH_EXCLUSIVE_ON [, +) delimit +a memory region interval from which syscalls are always executed directly, +regardless of the userspace selector. This provides a fast path for the +C library, which includes the most common syscall dispatchers in the native +code applications, and also provides a way for the signal handler to return without triggering a nested SIGSYS on (rt\_)sigreturn. Users of this interface should make sure that at least the signal trampoline code is included in this region. In addition, for syscalls that implement the trampoline code on the vDSO, that trampoline is never intercepted. +For PR_SYS_DISPATCH_INCLUSIVE_ON [, +) delimit +a memory region interval from which syscalls are dispatched based on +the userspace selector. Syscalls from outside of the range are always +executed directly. + [selector] is a pointer to a char-sized region in the process memory region, that provides a quick way to enable disable syscall redirection thread-wide, without the need to invoke the kernel directly. selector diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index dd49a89a62d3..8b49eab937d0 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -177,6 +177,7 @@ core_pattern %E executable path %c maximum size of core file by resource limit RLIMIT_CORE %C CPU the task ran on + %F pidfd number % both are dropped ======== ========================================== @@ -889,7 +890,7 @@ bit 1 print system memory info bit 2 print timer info bit 3 print locks info if ``CONFIG_LOCKDEP`` is on bit 4 print ftrace buffer -bit 5 print all printk messages in buffer +bit 5 replay all messages on consoles at the end of panic bit 6 print all CPUs backtrace (if available in the arch) bit 7 print only tasks in uninterruptible (blocked) state ===== ============================================ @@ -899,6 +900,24 @@ So for example to print tasks and memory info on panic, user can:: echo 3 > /proc/sys/kernel/panic_print +panic_sys_info +============== + +A comma separated list of extra information to be dumped on panic, +for example, "tasks,mem,timers,...". It is a human readable alternative +to 'panic_print'. Possible values are: + +============= =================================================== +tasks print all tasks info +mem print system memory info +timer print timers info +lock print locks info if CONFIG_LOCKDEP is on +ftrace print ftrace buffer +all_bt print all CPUs backtrace (if available in the arch) +blocked_tasks print only tasks in uninterruptible (blocked) state +============= =================================================== + + panic_on_rcu_stall ================== @@ -1014,30 +1033,26 @@ perf_user_access (arm64 and riscv only) Controls user space access for reading perf event counters. -arm64 -===== +* for arm64 + The default value is 0 (access disabled). -The default value is 0 (access disabled). + When set to 1, user space can read performance monitor counter registers + directly. -When set to 1, user space can read performance monitor counter registers -directly. + See Documentation/arch/arm64/perf.rst for more information. -See Documentation/arch/arm64/perf.rst for more information. +* for riscv + When set to 0, user space access is disabled. -riscv -===== + The default value is 1, user space can read performance monitor counter + registers through perf, any direct access without perf intervention will trigger + an illegal instruction. -When set to 0, user space access is disabled. + When set to 2, which enables legacy mode (user space has direct access to cycle + and insret CSRs only). Note that this legacy value is deprecated and will be + removed once all user space applications are fixed. -The default value is 1, user space can read performance monitor counter -registers through perf, any direct access without perf intervention will trigger -an illegal instruction. - -When set to 2, which enables legacy mode (user space has direct access to cycle -and insret CSRs only). Note that this legacy value is deprecated and will be -removed once all user space applications are fixed. - -Note that the time CSR is always directly accessible to all modes. + Note that the time CSR is always directly accessible to all modes. pid_max ======= @@ -1110,7 +1125,8 @@ printk_ratelimit_burst While long term we enforce one message per `printk_ratelimit`_ seconds, we do allow a burst of messages to pass through. ``printk_ratelimit_burst`` specifies the number of messages we can -send before ratelimiting kicks in. +send before ratelimiting kicks in. After `printk_ratelimit`_ seconds +have elapsed, another burst of messages may be sent. The default value is 10 messages. @@ -1465,7 +1481,7 @@ stack_erasing ============= This parameter can be used to control kernel stack erasing at the end -of syscalls for kernels built with ``CONFIG_GCC_PLUGIN_STACKLEAK``. +of syscalls for kernels built with ``CONFIG_KSTACK_ERASE``. That erasing reduces the information which kernel stack leak bugs can reveal and blocks some uninitialized stack variable attacks. @@ -1473,7 +1489,7 @@ The tradeoff is the performance impact: on a single CPU system kernel compilation sees a 1% slowdown, other systems and workloads may vary. = ==================================================================== -0 Kernel stack erasing is disabled, STACKLEAK_METRICS are not updated. +0 Kernel stack erasing is disabled, KSTACK_ERASE_METRICS are not updated. 1 Kernel stack erasing is enabled (default), it is performed before returning to the userspace at the end of syscalls. = ==================================================================== diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst index 9bef46151d53..4d71211fdad8 100644 --- a/Documentation/admin-guide/sysctl/vm.rst +++ b/Documentation/admin-guide/sysctl/vm.rst @@ -465,8 +465,8 @@ The minimum value is 1 (1/1 -> 100%). The value less than 1 completely disables protection of the pages. -max_map_count: -============== +max_map_count +============= This file contains the maximum number of memory map areas a process may have. Memory map areas are used as a side-effect of calling @@ -495,8 +495,8 @@ memory allocations. The default value depends on CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT. -memory_failure_early_kill: -========================== +memory_failure_early_kill +========================= Control how to kill processes when uncorrected memory error (typically a 2bit error in a memory module) is detected in the background by hardware diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 240fee618e06..102c693c8f81 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -358,12 +358,7 @@ Forcing power Many OEMs include a method that can be used to force the power of a Thunderbolt controller to an "On" state even if nothing is connected. If supported by your machine this will be exposed by the WMI bus with -a sysfs attribute called "force_power". - -For example the intel-wmi-thunderbolt driver exposes this attribute in: - /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power - - To force the power to on, write 1 to this attribute file. - To disable force power, write 0 to this attribute file. +a sysfs attribute called "force_power", see +Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt for details. Note: it's currently not possible to query the force power state of a platform. diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst index dee7b6de864f..2f666a7c303c 100644 --- a/Documentation/arch/arm64/booting.rst +++ b/Documentation/arch/arm64/booting.rst @@ -223,6 +223,47 @@ Before jumping into the kernel, the following conditions must be met: - SCR_EL3.HCE (bit 8) must be initialised to 0b1. + For systems with a GICv5 interrupt controller to be used in v5 mode: + + - If the kernel is entered at EL1 and EL2 is present: + + - ICH_HFGRTR_EL2.ICC_PPI_ACTIVERn_EL1 (bit 20) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_PPI_PRIORITYRn_EL1 (bit 19) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_PPI_PENDRn_EL1 (bit 18) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_PPI_ENABLERn_EL1 (bit 17) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_PPI_HMRn_EL1 (bit 16) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_IAFFIDR_EL1 (bit 7) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_ICSR_EL1 (bit 6) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_PCR_EL1 (bit 5) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_HPPIR_EL1 (bit 4) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_HAPR_EL1 (bit 3) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_CR0_EL1 (bit 2) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_IDRn_EL1 (bit 1) must be initialised to 0b1. + - ICH_HFGRTR_EL2.ICC_APR_EL1 (bit 0) must be initialised to 0b1. + + - ICH_HFGWTR_EL2.ICC_PPI_ACTIVERn_EL1 (bit 20) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_PPI_PRIORITYRn_EL1 (bit 19) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_PPI_PENDRn_EL1 (bit 18) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_PPI_ENABLERn_EL1 (bit 17) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_ICSR_EL1 (bit 6) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_PCR_EL1 (bit 5) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_CR0_EL1 (bit 2) must be initialised to 0b1. + - ICH_HFGWTR_EL2.ICC_APR_EL1 (bit 0) must be initialised to 0b1. + + - ICH_HFGITR_EL2.GICRCDNMIA (bit 10) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICRCDIA (bit 9) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDDI (bit 8) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDEOI (bit 7) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDHM (bit 6) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDRCFG (bit 5) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDPEND (bit 4) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDAFF (bit 3) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDPRI (bit 2) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDDIS (bit 1) must be initialised to 0b1. + - ICH_HFGITR_EL2.GICCDEN (bit 0) must be initialised to 0b1. + + - The DT or ACPI tables must describe a GICv5 interrupt controller. + For systems with a GICv3 interrupt controller to be used in v3 mode: - If EL3 is present: @@ -234,7 +275,7 @@ Before jumping into the kernel, the following conditions must be met: - If the kernel is entered at EL1: - - ICC.SRE_EL2.Enable (bit 3) must be initialised to 0b1 + - ICC_SRE_EL2.Enable (bit 3) must be initialised to 0b1 - ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b1. - The DT or ACPI tables must describe a GICv3 interrupt controller. @@ -388,6 +429,27 @@ Before jumping into the kernel, the following conditions must be met: - SMCR_EL2.EZT0 (bit 30) must be initialised to 0b1. + For CPUs with the Branch Record Buffer Extension (FEAT_BRBE): + + - If EL3 is present: + + - MDCR_EL3.SBRBE (bits 33:32) must be initialised to 0b01 or 0b11. + + - If the kernel is entered at EL1 and EL2 is present: + + - BRBCR_EL2.CC (bit 3) must be initialised to 0b1. + - BRBCR_EL2.MPRED (bit 4) must be initialised to 0b1. + + - HDFGRTR_EL2.nBRBDATA (bit 61) must be initialised to 0b1. + - HDFGRTR_EL2.nBRBCTL (bit 60) must be initialised to 0b1. + - HDFGRTR_EL2.nBRBIDR (bit 59) must be initialised to 0b1. + + - HDFGWTR_EL2.nBRBDATA (bit 61) must be initialised to 0b1. + - HDFGWTR_EL2.nBRBCTL (bit 60) must be initialised to 0b1. + + - HFGITR_EL2.nBRBIALL (bit 56) must be initialised to 0b1. + - HFGITR_EL2.nBRBINJ (bit 55) must be initialised to 0b1. + For CPUs with the Performance Monitors Extension (FEAT_PMUv3p9): - If EL3 is present: diff --git a/Documentation/arch/arm64/elf_hwcaps.rst b/Documentation/arch/arm64/elf_hwcaps.rst index 69d7afe56853..f58ada4d6cb2 100644 --- a/Documentation/arch/arm64/elf_hwcaps.rst +++ b/Documentation/arch/arm64/elf_hwcaps.rst @@ -435,6 +435,12 @@ HWCAP2_SME_SF8DP4 HWCAP2_POE Functionality implied by ID_AA64MMFR3_EL1.S1POE == 0b0001. +HWCAP3_MTE_FAR + Functionality implied by ID_AA64PFR2_EL1.MTEFAR == 0b0001. + +HWCAP3_MTE_STORE_ONLY + Functionality implied by ID_AA64PFR2_EL1.MTESTOREONLY == 0b0001. + 4. Unused AT_HWCAP bits ----------------------- diff --git a/Documentation/arch/arm64/tagged-pointers.rst b/Documentation/arch/arm64/tagged-pointers.rst index 81b6c2a770dd..f87a925ca9a5 100644 --- a/Documentation/arch/arm64/tagged-pointers.rst +++ b/Documentation/arch/arm64/tagged-pointers.rst @@ -60,11 +60,12 @@ that signal handlers in applications making use of tags cannot rely on the tag information for user virtual addresses being maintained in these fields unless the flag was set. -Due to architecture limitations, bits 63:60 of the fault address -are not preserved in response to synchronous tag check faults -(SEGV_MTESERR) even if SA_EXPOSE_TAGBITS was set. Applications should -treat the values of these bits as undefined in order to accommodate -future architecture revisions which may preserve the bits. +If FEAT_MTE_TAGGED_FAR (Armv8.9) is supported, bits 63:60 of the fault address +are preserved in response to synchronous tag check faults (SEGV_MTESERR) +otherwise not preserved even if SA_EXPOSE_TAGBITS was set. +Applications should interpret the values of these bits based on +the support for the HWCAP3_MTE_FAR. If the support is not present, +the values of these bits should be considered as undefined otherwise valid. For signals raised in response to watchpoint debug exceptions, the tag information will be preserved regardless of the SA_EXPOSE_TAGBITS diff --git a/Documentation/arch/powerpc/index.rst b/Documentation/arch/powerpc/index.rst index 0560cbae5fa1..53fc9f89f3e4 100644 --- a/Documentation/arch/powerpc/index.rst +++ b/Documentation/arch/powerpc/index.rst @@ -19,6 +19,7 @@ powerpc elf_hwcaps elfnote firmware-assisted-dump + htm hvcs imc isa-versions diff --git a/Documentation/arch/s390/driver-model.rst b/Documentation/arch/s390/driver-model.rst index ad18f129fb0b..e7488f02bb78 100644 --- a/Documentation/arch/s390/driver-model.rst +++ b/Documentation/arch/s390/driver-model.rst @@ -305,24 +305,3 @@ xpram shows up under devices/system/ as 'xpram'. For each cpu, a directory is created under devices/system/cpu/. Each cpu has an attribute 'online' which can be 0 or 1. - - -4. Other devices ----------------- - -4.1 Netiucv ------------ - -The netiucv driver creates an attribute 'connection' under -bus/iucv/drivers/netiucv. Piping to this attribute creates a new netiucv -connection to the specified host. - -Netiucv connections show up under devices/iucv/ as "netiucv". The interface -number is assigned sequentially to the connections defined via the 'connection' -attribute. - -user - - shows the connection partner. - -buffer - - maximum buffer size. Pipe to it to change buffer size. diff --git a/Documentation/arch/x86/amd-hfi.rst b/Documentation/arch/x86/amd-hfi.rst new file mode 100644 index 000000000000..bf3d3a1985a2 --- /dev/null +++ b/Documentation/arch/x86/amd-hfi.rst @@ -0,0 +1,133 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================================================== +Hardware Feedback Interface For Hetero Core Scheduling On AMD Platform +====================================================================== + +:Copyright: 2025 Advanced Micro Devices, Inc. All Rights Reserved. + +:Author: Perry Yuan +:Author: Mario Limonciello + +Overview +-------- + +AMD Heterogeneous Core implementations are comprised of more than one +architectural class and CPUs are comprised of cores of various efficiency and +power capabilities: performance-oriented *classic cores* and power-efficient +*dense cores*. As such, power management strategies must be designed to +accommodate the complexities introduced by incorporating different core types. +Heterogeneous systems can also extend to more than two architectural classes +as well. The purpose of the scheduling feedback mechanism is to provide +information to the operating system scheduler in real time such that the +scheduler can direct threads to the optimal core. + +The goal of AMD's heterogeneous architecture is to attain power benefit by +sending background threads to the dense cores while sending high priority +threads to the classic cores. From a performance perspective, sending +background threads to dense cores can free up power headroom and allow the +classic cores to optimally service demanding threads. Furthermore, the area +optimized nature of the dense cores allows for an increasing number of +physical cores. This improved core density will have positive multithreaded +performance impact. + +AMD Heterogeneous Core Driver +----------------------------- + +The ``amd_hfi`` driver delivers the operating system a performance and energy +efficiency capability data for each CPU in the system. The scheduler can use +the ranking data from the HFI driver to make task placement decisions. + +Thread Classification and Ranking Table Interaction +---------------------------------------------------- + +The thread classification is used to select into a ranking table that +describes an efficiency and performance ranking for each classification. + +Threads are classified during runtime into enumerated classes. The classes +represent thread performance/power characteristics that may benefit from +special scheduling behaviors. The below table depicts an example of thread +classification and a preference where a given thread should be scheduled +based on its thread class. The real time thread classification is consumed +by the operating system and is used to inform the scheduler of where the +thread should be placed. + +Thread Classification Example Table +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++----------+----------------+-------------------------------+---------------------+---------+ +| class ID | Classification | Preferred scheduling behavior | Preemption priority | Counter | ++----------+----------------+-------------------------------+---------------------+---------+ +| 0 | Default | Performant | Highest | | ++----------+----------------+-------------------------------+---------------------+---------+ +| 1 | Non-scalable | Efficient | Lowest | PMCx1A1 | ++----------+----------------+-------------------------------+---------------------+---------+ +| 2 | I/O bound | Efficient | Lowest | PMCx044 | ++----------+----------------+-------------------------------+---------------------+---------+ + +Thread classification is performed by the hardware each time that the thread is switched out. +Threads that don't meet any hardware specified criteria are classified as "default". + +AMD Hardware Feedback Interface +-------------------------------- + +The Hardware Feedback Interface provides to the operating system information +about the performance and energy efficiency of each CPU in the system. Each +capability is given as a unit-less quantity in the range [0-255]. A higher +performance value indicates higher performance capability, and a higher +efficiency value indicates more efficiency. Energy efficiency and performance +are reported in separate capabilities in the shared memory based ranking table. + +These capabilities may change at runtime as a result of changes in the +operating conditions of the system or the action of external factors. +Power Management firmware is responsible for detecting events that require +a reordering of the performance and efficiency ranking. Table updates happen +relatively infrequently and occur on the time scale of seconds or more. + +The following events trigger a table update: + * Thermal Stress Events + * Silent Compute + * Extreme Low Battery Scenarios + +The kernel or a userspace policy daemon can use these capabilities to modify +task placement decisions. For instance, if either the performance or energy +capabilities of a given logical processor becomes zero, it is an indication +that the hardware recommends to the operating system to not schedule any tasks +on that processor for performance or energy efficiency reasons, respectively. + +Implementation details for Linux +-------------------------------- + +The implementation of threads scheduling consists of the following steps: + +1. A thread is spawned and scheduled to the ideal core using the default + heterogeneous scheduling policy. +2. The processor profiles thread execution and assigns an enumerated + classification ID. + This classification is communicated to the OS via logical processor + scope MSR. +3. During the thread context switch out the operating system consumes the + workload (WL) classification which resides in a logical processor scope MSR. +4. The OS triggers the hardware to clear its history by writing to an MSR, + after consuming the WL classification and before switching in the new thread. +5. If due to the classification, ranking table, and processor availability, + the thread is not on its ideal processor, the OS will then consider + scheduling the thread on its ideal processor (if available). + +Ranking Table +------------- +The ranking table is a shared memory region that is used to communicate the +performance and energy efficiency capabilities of each CPU in the system. + +The ranking table design includes rankings for each APIC ID in the system and +rankings both for performance and efficiency for each workload classification. + +.. kernel-doc:: drivers/platform/x86/amd/hfi/hfi.c + :doc: amd_shmem_info + +Ranking Table update +--------------------------- +The power management firmware issues an platform interrupt after updating the +ranking table and is ready for the operating system to consume it. CPUs receive +such interrupt and read new ranking table from shared memory which PCCT table +has provided, then ``amd_hfi`` driver parses the new table to provide new +consume data for scheduling decisions. diff --git a/Documentation/arch/x86/index.rst b/Documentation/arch/x86/index.rst index 8ea762494bcc..f88bcfceb7f2 100644 --- a/Documentation/arch/x86/index.rst +++ b/Documentation/arch/x86/index.rst @@ -28,6 +28,7 @@ x86-specific Documentation amd-debugging amd-memory-encryption amd_hsmp + amd-hfi tdx pti mds diff --git a/Documentation/arch/x86/mds.rst b/Documentation/arch/x86/mds.rst index 5a2e6c0ef04a..3518671e1a85 100644 --- a/Documentation/arch/x86/mds.rst +++ b/Documentation/arch/x86/mds.rst @@ -93,7 +93,7 @@ enters a C-state. The kernel provides a function to invoke the buffer clearing: - mds_clear_cpu_buffers() + x86_clear_cpu_buffers() Also macro CLEAR_CPU_BUFFERS can be used in ASM late in exit-to-user path. Other than CFLAGS.ZF, this macro doesn't clobber any registers. @@ -185,9 +185,9 @@ Mitigation points idle clearing would be a window dressing exercise and is therefore not activated. - The invocation is controlled by the static key mds_idle_clear which is - switched depending on the chosen mitigation mode and the SMT state of - the system. + The invocation is controlled by the static key cpu_buf_idle_clear which is + switched depending on the chosen mitigation mode and the SMT state of the + system. The buffer clear is only invoked before entering the C-State to prevent that stale data from the idling CPU from spilling to the Hyper-Thread diff --git a/Documentation/arch/x86/x86_64/mm.rst b/Documentation/arch/x86/x86_64/mm.rst index f2db178b353f..a6cf05d51bd8 100644 --- a/Documentation/arch/x86/x86_64/mm.rst +++ b/Documentation/arch/x86/x86_64/mm.rst @@ -176,5 +176,5 @@ Be very careful vs. KASLR when changing anything here. The KASLR address range must not overlap with anything except the KASAN shadow area, which is correct as KASAN disables KASLR. -For both 4- and 5-level layouts, the STACKLEAK_POISON value in the last 2MB +For both 4- and 5-level layouts, the KSTACK_ERASE_POISON value in the last 2MB hole: ffffffffffff4111 diff --git a/Documentation/bpf/bpf_devel_QA.rst b/Documentation/bpf/bpf_devel_QA.rst index 0acb4c9b8d90..45bc5c5cd793 100644 --- a/Documentation/bpf/bpf_devel_QA.rst +++ b/Documentation/bpf/bpf_devel_QA.rst @@ -611,9 +611,10 @@ Q: I have added a new BPF instruction to the kernel, how can I integrate it into LLVM? A: LLVM has a ``-mcpu`` selector for the BPF back end in order to allow -the selection of BPF instruction set extensions. By default the -``generic`` processor target is used, which is the base instruction set -(v1) of BPF. +the selection of BPF instruction set extensions. Before llvm version 20, +the ``generic`` processor target is used, which is the base instruction +set (v1) of BPF. Since llvm 20, the default processor target has changed +to instruction set v3. LLVM has an option to select ``-mcpu=probe`` where it will probe the host kernel for supported BPF instruction set extensions and selects the diff --git a/Documentation/bpf/map_hash.rst b/Documentation/bpf/map_hash.rst index d2343952f2cb..8606bf958a8c 100644 --- a/Documentation/bpf/map_hash.rst +++ b/Documentation/bpf/map_hash.rst @@ -233,10 +233,16 @@ attempts in order to enforce the LRU property which have increasing impacts on other CPUs involved in the following operation attempts: - Attempt to use CPU-local state to batch operations -- Attempt to fetch free nodes from global lists +- Attempt to fetch ``target_free`` free nodes from global lists - Attempt to pull any node from a global list and remove it from the hashmap - Attempt to pull any node from any CPU's list and remove it from the hashmap +The number of nodes to borrow from the global list in a batch, ``target_free``, +depends on the size of the map. Larger batch size reduces lock contention, but +may also exhaust the global structure. The value is computed at map init to +avoid exhaustion, by limiting aggregate reservation by all CPUs to half the map +size. With a minimum of a single element and maximum budget of 128 at a time. + This algorithm is described visually in the following diagram. See the description in commit 3a08c2fd7634 ("bpf: LRU List") for a full explanation of the corresponding operations: diff --git a/Documentation/bpf/map_lru_hash_update.dot b/Documentation/bpf/map_lru_hash_update.dot index a0fee349d29c..ab10058f5b79 100644 --- a/Documentation/bpf/map_lru_hash_update.dot +++ b/Documentation/bpf/map_lru_hash_update.dot @@ -35,18 +35,18 @@ digraph { fn_bpf_lru_list_pop_free_to_local [shape=rectangle,fillcolor=2, label="Flush local pending, Rotate Global list, move - LOCAL_FREE_TARGET + target_free from global -> local"] // Also corresponds to: // fn__local_list_flush() // fn_bpf_lru_list_rotate() fn___bpf_lru_node_move_to_free[shape=diamond,fillcolor=2, - label="Able to free\nLOCAL_FREE_TARGET\nnodes?"] + label="Able to free\ntarget_free\nnodes?"] fn___bpf_lru_list_shrink_inactive [shape=rectangle,fillcolor=3, label="Shrink inactive list up to remaining - LOCAL_FREE_TARGET + target_free (global LRU -> local)"] fn___bpf_lru_list_shrink [shape=diamond,fillcolor=2, label="> 0 entries in\nlocal free list?"] diff --git a/Documentation/bpf/standardization/instruction-set.rst b/Documentation/bpf/standardization/instruction-set.rst index fbe975585236..39c74611752b 100644 --- a/Documentation/bpf/standardization/instruction-set.rst +++ b/Documentation/bpf/standardization/instruction-set.rst @@ -350,9 +350,9 @@ Underflow and overflow are allowed during arithmetic operations, meaning the 64-bit or 32-bit value will wrap. If BPF program execution would result in division by zero, the destination register is instead set to zero. Otherwise, for ``ALU64``, if execution would result in ``LLONG_MIN`` -dividing -1, the desination register is instead set to ``LLONG_MIN``. For -``ALU``, if execution would result in ``INT_MIN`` dividing -1, the -desination register is instead set to ``INT_MIN``. +divided by -1, the destination register is instead set to ``LLONG_MIN``. For +``ALU``, if execution would result in ``INT_MIN`` divided by -1, the +destination register is instead set to ``INT_MIN``. If execution would result in modulo by zero, for ``ALU64`` the value of the destination register is unchanged whereas for ``ALU`` the upper diff --git a/Documentation/cdrom/cdrom-standard.rst b/Documentation/cdrom/cdrom-standard.rst index 6c1303cff159..b97a4e9b9bd3 100644 --- a/Documentation/cdrom/cdrom-standard.rst +++ b/Documentation/cdrom/cdrom-standard.rst @@ -273,7 +273,6 @@ The drive-specific, minor-like information that is registered with __u8 media_written; /* dirty flag, DVD+RW bookkeeping */ unsigned short mmc3_profile; /* current MMC3 profile */ int for_data; /* unknown:TBD */ - int (*exit)(struct cdrom_device_info *);/* unknown:TBD */ int mrw_mode_page; /* which MRW mode page is in use */ }; diff --git a/Documentation/cdrom/index.rst b/Documentation/cdrom/index.rst index e9b022d70939..3ac4f716612f 100644 --- a/Documentation/cdrom/index.rst +++ b/Documentation/cdrom/index.rst @@ -8,7 +8,6 @@ CD-ROM :maxdepth: 1 cdrom-standard - packet-writing .. only:: subproject and html diff --git a/Documentation/cdrom/packet-writing.rst b/Documentation/cdrom/packet-writing.rst deleted file mode 100644 index 43db58c50d29..000000000000 --- a/Documentation/cdrom/packet-writing.rst +++ /dev/null @@ -1,139 +0,0 @@ -============== -Packet writing -============== - -Getting started quick ---------------------- - -- Select packet support in the block device section and UDF support in - the file system section. - -- Compile and install kernel and modules, reboot. - -- You need the udftools package (pktsetup, mkudffs, cdrwtool). - Download from https://github.com/pali/udftools - -- Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute - as appropriate):: - - # cdrwtool -d /dev/hdc -q - -- Setup your writer:: - - # pktsetup dev_name /dev/hdc - -- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy:: - - # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime - - -Packet writing for DVD-RW media -------------------------------- - -DVD-RW discs can be written to much like CD-RW discs if they are in -the so called "restricted overwrite" mode. To put a disc in restricted -overwrite mode, run:: - - # dvd+rw-format /dev/hdc - -You can then use the disc the same way you would use a CD-RW disc:: - - # pktsetup dev_name /dev/hdc - # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime - - -Packet writing for DVD+RW media -------------------------------- - -According to the DVD+RW specification, a drive supporting DVD+RW discs -shall implement "true random writes with 2KB granularity", which means -that it should be possible to put any filesystem with a block size >= -2KB on such a disc. For example, it should be possible to do:: - - # dvd+rw-format /dev/hdc (only needed if the disc has never - been formatted) - # mkudffs /dev/hdc - # mount /dev/hdc /cdrom -t udf -o rw,noatime - -However, some drives don't follow the specification and expect the -host to perform aligned writes at 32KB boundaries. Other drives do -follow the specification, but suffer bad performance problems if the -writes are not 32KB aligned. - -Both problems can be solved by using the pktcdvd driver, which always -generates aligned writes:: - - # dvd+rw-format /dev/hdc - # pktsetup dev_name /dev/hdc - # mkudffs /dev/pktcdvd/dev_name - # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime - - -Packet writing for DVD-RAM media --------------------------------- - -DVD-RAM discs are random writable, so using the pktcdvd driver is not -necessary. However, using the pktcdvd driver can improve performance -in the same way it does for DVD+RW media. - - -Notes ------ - -- CD-RW media can usually not be overwritten more than about 1000 - times, so to avoid unnecessary wear on the media, you should always - use the noatime mount option. - -- Defect management (ie automatic remapping of bad sectors) has not - been implemented yet, so you are likely to get at least some - filesystem corruption if the disc wears out. - -- Since the pktcdvd driver makes the disc appear as a regular block - device with a 2KB block size, you can put any filesystem you like on - the disc. For example, run:: - - # /sbin/mke2fs /dev/pktcdvd/dev_name - - to create an ext2 filesystem on the disc. - - -Using the pktcdvd sysfs interface ---------------------------------- - -Since Linux 2.6.20, the pktcdvd module has a sysfs interface -and can be controlled by it. For example the "pktcdvd" tool uses -this interface. (see http://tom.ist-im-web.de/linux/software/pktcdvd ) - -"pktcdvd" works similar to "pktsetup", e.g.:: - - # pktcdvd -a dev_name /dev/hdc - # mkudffs /dev/pktcdvd/dev_name - # mount -t udf -o rw,noatime /dev/pktcdvd/dev_name /dvdram - # cp files /dvdram - # umount /dvdram - # pktcdvd -r dev_name - - -For a description of the sysfs interface look into the file: - - Documentation/ABI/testing/sysfs-class-pktcdvd - - -Using the pktcdvd debugfs interface ------------------------------------ - -To read pktcdvd device infos in human readable form, do:: - - # cat /sys/kernel/debug/pktcdvd/pktcdvd[0-7]/info - -For a description of the debugfs interface look into the file: - - Documentation/ABI/testing/debugfs-pktcdvd - - - -Links ------ - -See http://fy.chalmers.se/~appro/linux/DVD+RW/ for more information -about DVD writing. diff --git a/Documentation/conf.py b/Documentation/conf.py index 12de52a2b17e..700516238d3f 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -1,25 +1,87 @@ -# -*- coding: utf-8 -*- -# -# The Linux Kernel documentation build configuration file, created by -# sphinx-quickstart on Fri Feb 12 13:51:46 2016. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +# SPDX-License-Identifier: GPL-2.0-only +# pylint: disable=C0103,C0209 + +""" +The Linux Kernel documentation build configuration file. +""" -import sys import os -import sphinx import shutil +import sys + +import sphinx + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath("sphinx")) + +from load_config import loadConfig # pylint: disable=C0413,E0401 + +# Minimal supported version +needs_sphinx = "3.4.3" + +# Get Sphinx version +major, minor, patch = sphinx.version_info[:3] # pylint: disable=I1101 + +# Include_patterns were added on Sphinx 5.1 +if (major < 5) or (major == 5 and minor < 1): + has_include_patterns = False +else: + has_include_patterns = True + # Include patterns that don't contain directory names, in glob format + include_patterns = ["**.rst"] + +# Location of Documentation/ directory +doctree = os.path.abspath(".") + +# Exclude of patterns that don't contain directory names, in glob format. +exclude_patterns = [] + +# List of patterns that contain directory names in glob format. +dyn_include_patterns = [] +dyn_exclude_patterns = ["output"] + +# Properly handle include/exclude patterns +# ---------------------------------------- + +def update_patterns(app, config): + """ + On Sphinx, all directories are relative to what it is passed as + SOURCEDIR parameter for sphinx-build. Due to that, all patterns + that have directory names on it need to be dynamically set, after + converting them to a relative patch. + + As Sphinx doesn't include any patterns outside SOURCEDIR, we should + exclude relative patterns that start with "../". + """ + + # setup include_patterns dynamically + if has_include_patterns: + for p in dyn_include_patterns: + full = os.path.join(doctree, p) + + rel_path = os.path.relpath(full, start=app.srcdir) + if rel_path.startswith("../"): + continue + + config.include_patterns.append(rel_path) + + # setup exclude_patterns dynamically + for p in dyn_exclude_patterns: + full = os.path.join(doctree, p) + + rel_path = os.path.relpath(full, start=app.srcdir) + if rel_path.startswith("../"): + continue + + config.exclude_patterns.append(rel_path) + # helper # ------ + def have_command(cmd): """Search ``cmd`` in the ``PATH`` environment. @@ -28,24 +90,23 @@ def have_command(cmd): """ return shutil.which(cmd) is not None -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('sphinx')) -from load_config import loadConfig # -- General configuration ------------------------------------------------ -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '3.4.3' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', - 'kfigure', 'sphinx.ext.ifconfig', 'automarkup', - 'maintainers_include', 'sphinx.ext.autosectionlabel', - 'kernel_abi', 'kernel_feat', 'translations'] +# Add any Sphinx extensions in alphabetic order +extensions = [ + "automarkup", + "kernel_abi", + "kerneldoc", + "kernel_feat", + "kernel_include", + "kfigure", + "maintainers_include", + "rstFlatTable", + "sphinx.ext.autosectionlabel", + "sphinx.ext.ifconfig", + "translations", +] # Since Sphinx version 3, the C function parser is more pedantic with regards # to type checking. Due to that, having macros at c:function cause problems. @@ -120,28 +181,28 @@ autosectionlabel_maxdepth = 2 # Load math renderer: # For html builder, load imgmath only when its dependencies are met. # mathjax is the default math renderer since Sphinx 1.8. -have_latex = have_command('latex') -have_dvipng = have_command('dvipng') +have_latex = have_command("latex") +have_dvipng = have_command("dvipng") load_imgmath = have_latex and have_dvipng # Respect SPHINX_IMGMATH (for html docs only) -if 'SPHINX_IMGMATH' in os.environ: - env_sphinx_imgmath = os.environ['SPHINX_IMGMATH'] - if 'yes' in env_sphinx_imgmath: +if "SPHINX_IMGMATH" in os.environ: + env_sphinx_imgmath = os.environ["SPHINX_IMGMATH"] + if "yes" in env_sphinx_imgmath: load_imgmath = True - elif 'no' in env_sphinx_imgmath: + elif "no" in env_sphinx_imgmath: load_imgmath = False else: sys.stderr.write("Unknown env SPHINX_IMGMATH=%s ignored.\n" % env_sphinx_imgmath) if load_imgmath: extensions.append("sphinx.ext.imgmath") - math_renderer = 'imgmath' + math_renderer = "imgmath" else: - math_renderer = 'mathjax' + math_renderer = "mathjax" # Add any paths that contain templates here, relative to this directory. -templates_path = ['sphinx/templates'] +templates_path = ["sphinx/templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: @@ -149,15 +210,15 @@ templates_path = ['sphinx/templates'] source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'The Linux Kernel' -copyright = 'The kernel development community' -author = 'The kernel development community' +project = "The Linux Kernel" +copyright = "The kernel development community" # pylint: disable=W0622 +author = "The kernel development community" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -172,86 +233,86 @@ author = 'The kernel development community' try: makefile_version = None makefile_patchlevel = None - for line in open('../Makefile'): - key, val = [x.strip() for x in line.split('=', 2)] - if key == 'VERSION': - makefile_version = val - elif key == 'PATCHLEVEL': - makefile_patchlevel = val - if makefile_version and makefile_patchlevel: - break -except: + with open("../Makefile", encoding="utf=8") as fp: + for line in fp: + key, val = [x.strip() for x in line.split("=", 2)] + if key == "VERSION": + makefile_version = val + elif key == "PATCHLEVEL": + makefile_patchlevel = val + if makefile_version and makefile_patchlevel: + break +except Exception: pass finally: if makefile_version and makefile_patchlevel: - version = release = makefile_version + '.' + makefile_patchlevel + version = release = makefile_version + "." + makefile_patchlevel else: version = release = "unknown version" -# -# HACK: there seems to be no easy way for us to get at the version and -# release information passed in from the makefile...so go pawing through the -# command-line options and find it for ourselves. -# + def get_cline_version(): - c_version = c_release = '' + """ + HACK: There seems to be no easy way for us to get at the version and + release information passed in from the makefile...so go pawing through the + command-line options and find it for ourselves. + """ + + c_version = c_release = "" for arg in sys.argv: - if arg.startswith('version='): + if arg.startswith("version="): c_version = arg[8:] - elif arg.startswith('release='): + elif arg.startswith("release="): c_release = arg[8:] if c_version: if c_release: - return c_version + '-' + c_release + return c_version + "-" + c_release return c_version - return version # Whatever we came up with before + return version # Whatever we came up with before + # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['output'] +# today_fmt = '%B %d, %Y' # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False -primary_domain = 'c' -highlight_language = 'none' +primary_domain = "c" +highlight_language = "none" # -- Options for HTML output ---------------------------------------------- @@ -259,43 +320,45 @@ highlight_language = 'none' # a list of builtin themes. # Default theme -html_theme = 'alabaster' +html_theme = "alabaster" html_css_files = [] if "DOCS_THEME" in os.environ: html_theme = os.environ["DOCS_THEME"] -if html_theme == 'sphinx_rtd_theme' or html_theme == 'sphinx_rtd_dark_mode': +if html_theme in ["sphinx_rtd_theme", "sphinx_rtd_dark_mode"]: # Read the Docs theme try: import sphinx_rtd_theme + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_css_files = [ - 'theme_overrides.css', + "theme_overrides.css", ] # Read the Docs dark mode override theme - if html_theme == 'sphinx_rtd_dark_mode': + if html_theme == "sphinx_rtd_dark_mode": try: - import sphinx_rtd_dark_mode - extensions.append('sphinx_rtd_dark_mode') - except ImportError: - html_theme == 'sphinx_rtd_theme' + import sphinx_rtd_dark_mode # pylint: disable=W0611 - if html_theme == 'sphinx_rtd_theme': - # Add color-specific RTD normal mode - html_css_files.append('theme_rtd_colors.css') + extensions.append("sphinx_rtd_dark_mode") + except ImportError: + html_theme = "sphinx_rtd_theme" + + if html_theme == "sphinx_rtd_theme": + # Add color-specific RTD normal mode + html_css_files.append("theme_rtd_colors.css") html_theme_options = { - 'navigation_depth': -1, + "navigation_depth": -1, } except ImportError: - html_theme = 'alabaster' + html_theme = "alabaster" if "DOCS_CSS" in os.environ: css = os.environ["DOCS_CSS"].split(" ") @@ -303,14 +366,14 @@ if "DOCS_CSS" in os.environ: for l in css: html_css_files.append(l) -if html_theme == 'alabaster': +if html_theme == "alabaster": html_theme_options = { - 'description': get_cline_version(), - 'page_width': '65em', - 'sidebar_width': '15em', - 'fixed_sidebar': 'true', - 'font_size': 'inherit', - 'font_family': 'serif', + "description": get_cline_version(), + "page_width": "65em", + "sidebar_width": "15em", + "fixed_sidebar": "true", + "font_size": "inherit", + "font_family": "serif", } sys.stderr.write("Using %s theme\n" % html_theme) @@ -318,104 +381,79 @@ sys.stderr.write("Using %s theme\n" % html_theme) # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['sphinx-static'] +html_static_path = ["sphinx-static"] # If true, Docutils "smart quotes" will be used to convert quotes and dashes # to typographically correct entities. However, conversion of "--" to "—" # is not always what we want, so enable only quotes. -smartquotes_action = 'q' +smartquotes_action = "q" # Custom sidebar templates, maps document names to template names. # Note that the RTD theme ignores this -html_sidebars = { '**': ['searchbox.html', 'kernel-toc.html', 'sourcelink.html']} +html_sidebars = {"**": ["searchbox.html", + "kernel-toc.html", + "sourcelink.html"]} # about.html is available for alabaster theme. Add it at the front. -if html_theme == 'alabaster': - html_sidebars['**'].insert(0, 'about.html') +if html_theme == "alabaster": + html_sidebars["**"].insert(0, "about.html") # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'images/logo.svg' +html_logo = "images/logo.svg" # Output file base name for HTML help builder. -htmlhelp_basename = 'TheLinuxKerneldoc' +htmlhelp_basename = "TheLinuxKerneldoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - 'papersize': 'a4paper', - + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). - 'pointsize': '11pt', - + "pointsize": "11pt", # Latex figure (float) alignment - #'figure_align': 'htbp', - + # 'figure_align': 'htbp', # Don't mangle with UTF-8 chars - 'inputenc': '', - 'utf8extra': '', - + "inputenc": "", + "utf8extra": "", # Set document margins - 'sphinxsetup': ''' + "sphinxsetup": """ hmargin=0.5in, vmargin=1in, parsedliteralwraps=true, verbatimhintsturnover=false, - ''', - + """, # # Some of our authors are fond of deep nesting; tell latex to # cope. # - 'maxlistdepth': '10', - + "maxlistdepth": "10", # For CJK One-half spacing, need to be in front of hyperref - 'extrapackages': r'\usepackage{setspace}', - + "extrapackages": r"\usepackage{setspace}", # Additional stuff for the LaTeX preamble. - 'preamble': ''' + "preamble": """ % Use some font with UTF-8 support with XeLaTeX \\usepackage{fontspec} \\setsansfont{DejaVu Sans} \\setromanfont{DejaVu Serif} \\setmonofont{DejaVu Sans Mono} - ''', + """, } # Load kerneldoc specific LaTeX settings -latex_elements['preamble'] += ''' +latex_elements["preamble"] += """ % Load kerneldoc specific LaTeX settings - \\input{kerneldoc-preamble.sty} -''' - -# With Sphinx 1.6, it is possible to change the Bg color directly -# by using: -# \definecolor{sphinxnoteBgColor}{RGB}{204,255,255} -# \definecolor{sphinxwarningBgColor}{RGB}{255,204,204} -# \definecolor{sphinxattentionBgColor}{RGB}{255,255,204} -# \definecolor{sphinximportantBgColor}{RGB}{192,255,204} -# -# However, it require to use sphinx heavy box with: -# -# \renewenvironment{sphinxlightbox} {% -# \\begin{sphinxheavybox} -# } -# \\end{sphinxheavybox} -# } -# -# Unfortunately, the implementation is buggy: if a note is inside a -# table, it isn't displayed well. So, for now, let's use boring -# black and white notes. + \\input{kerneldoc-preamble.sty} +""" # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). # Sorted in alphabetical order -latex_documents = [ -] +latex_documents = [] # Add all other index files from Documentation/ subdirectories -for fn in os.listdir('.'): +for fn in os.listdir("."): doc = os.path.join(fn, "index") if os.path.exists(doc + ".rst"): has = False @@ -424,34 +462,39 @@ for fn in os.listdir('.'): has = True break if not has: - latex_documents.append((doc, fn + '.tex', - 'Linux %s Documentation' % fn.capitalize(), - 'The kernel development community', - 'manual')) + latex_documents.append( + ( + doc, + fn + ".tex", + "Linux %s Documentation" % fn.capitalize(), + "The kernel development community", + "manual", + ) + ) # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # Additional LaTeX stuff to be copied to build directory latex_additional_files = [ - 'sphinx/kerneldoc-preamble.sty', + "sphinx/kerneldoc-preamble.sty", ] @@ -460,12 +503,11 @@ latex_additional_files = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'thelinuxkernel', 'The Linux Kernel Documentation', - [author], 1) + (master_doc, "thelinuxkernel", "The Linux Kernel Documentation", [author], 1) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -473,11 +515,15 @@ man_pages = [ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'TheLinuxKernel', 'The Linux Kernel Documentation', - author, 'TheLinuxKernel', 'One line description of project.', - 'Miscellaneous'), -] +texinfo_documents = [( + master_doc, + "TheLinuxKernel", + "The Linux Kernel Documentation", + author, + "TheLinuxKernel", + "One line description of project.", + "Miscellaneous", + ),] # -- Options for Epub output ---------------------------------------------- @@ -488,9 +534,9 @@ epub_publisher = author epub_copyright = copyright # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] -#======= +# ======= # rst2pdf # # Grouping the document tree into PDF files. List of tuples @@ -502,17 +548,23 @@ epub_exclude_files = ['search.html'] # multiple PDF files here actually tries to get the cross-referencing right # *between* PDF files. pdf_documents = [ - ('kernel-documentation', u'Kernel', u'Kernel', u'J. Random Bozo'), + ("kernel-documentation", "Kernel", "Kernel", "J. Random Bozo"), ] # kernel-doc extension configuration for running Sphinx directly (e.g. by Read # the Docs). In a normal build, these are supplied from the Makefile via command # line arguments. -kerneldoc_bin = '../scripts/kernel-doc.py' -kerneldoc_srctree = '..' +kerneldoc_bin = "../scripts/kernel-doc.py" +kerneldoc_srctree = ".." # ------------------------------------------------------------------------------ # Since loadConfig overwrites settings from the global namespace, it has to be # the last statement in the conf.py file # ------------------------------------------------------------------------------ loadConfig(globals()) + + +def setup(app): + """Patterns need to be updated at init time on older Sphinx versions""" + + app.connect('config-inited', update_patterns) diff --git a/Documentation/core-api/dma-api-howto.rst b/Documentation/core-api/dma-api-howto.rst index 0bf31b6c4383..96fce2a9aa90 100644 --- a/Documentation/core-api/dma-api-howto.rst +++ b/Documentation/core-api/dma-api-howto.rst @@ -155,7 +155,7 @@ a device with limitations, it needs to be decreased. Special note about PCI: PCI-X specification requires PCI-X devices to support 64-bit addressing (DAC) for all transactions. And at least one platform (SGI -SN2) requires 64-bit consistent allocations to operate correctly when the IO +SN2) requires 64-bit coherent allocations to operate correctly when the IO bus is in PCI-X mode. For correct operation, you must set the DMA mask to inform the kernel about @@ -174,7 +174,7 @@ used instead: int dma_set_mask(struct device *dev, u64 mask); - The setup for consistent allocations is performed via a call + The setup for coherent allocations is performed via a call to dma_set_coherent_mask():: int dma_set_coherent_mask(struct device *dev, u64 mask); @@ -241,7 +241,7 @@ it would look like this:: The coherent mask will always be able to set the same or a smaller mask as the streaming mask. However for the rare case that a device driver only -uses consistent allocations, one would have to check the return value from +uses coherent allocations, one would have to check the return value from dma_set_coherent_mask(). Finally, if your device can only drive the low 24-bits of @@ -298,20 +298,20 @@ Types of DMA mappings There are two types of DMA mappings: -- Consistent DMA mappings which are usually mapped at driver +- Coherent DMA mappings which are usually mapped at driver initialization, unmapped at the end and for which the hardware should guarantee that the device and the CPU can access the data in parallel and will see updates made by each other without any explicit software flushing. - Think of "consistent" as "synchronous" or "coherent". + Think of "coherent" as "synchronous". - The current default is to return consistent memory in the low 32 + The current default is to return coherent memory in the low 32 bits of the DMA space. However, for future compatibility you should - set the consistent mask even if this default is fine for your + set the coherent mask even if this default is fine for your driver. - Good examples of what to use consistent mappings for are: + Good examples of what to use coherent mappings for are: - Network card DMA ring descriptors. - SCSI adapter mailbox command data structures. @@ -320,13 +320,13 @@ There are two types of DMA mappings: The invariant these examples all require is that any CPU store to memory is immediately visible to the device, and vice - versa. Consistent mappings guarantee this. + versa. Coherent mappings guarantee this. .. important:: - Consistent DMA memory does not preclude the usage of + Coherent DMA memory does not preclude the usage of proper memory barriers. The CPU may reorder stores to - consistent memory just as it may normal memory. Example: + coherent memory just as it may normal memory. Example: if it is important for the device to see the first word of a descriptor updated before the second, you must do something like:: @@ -365,10 +365,10 @@ Also, systems with caches that aren't DMA-coherent will work better when the underlying buffers don't share cache lines with other data. -Using Consistent DMA mappings -============================= +Using Coherent DMA mappings +=========================== -To allocate and map large (PAGE_SIZE or so) consistent DMA regions, +To allocate and map large (PAGE_SIZE or so) coherent DMA regions, you should do:: dma_addr_t dma_handle; @@ -385,10 +385,10 @@ __get_free_pages() (but takes size instead of a page order). If your driver needs regions sized smaller than a page, you may prefer using the dma_pool interface, described below. -The consistent DMA mapping interfaces, will by default return a DMA address +The coherent DMA mapping interfaces, will by default return a DMA address which is 32-bit addressable. Even if the device indicates (via the DMA mask) -that it may address the upper 32-bits, consistent allocation will only -return > 32-bit addresses for DMA if the consistent DMA mask has been +that it may address the upper 32-bits, coherent allocation will only +return > 32-bit addresses for DMA if the coherent DMA mask has been explicitly changed via dma_set_coherent_mask(). This is true of the dma_pool interface as well. @@ -497,7 +497,7 @@ program address space. Such platforms can and do report errors in the kernel logs when the DMA controller hardware detects violation of the permission setting. -Only streaming mappings specify a direction, consistent mappings +Only streaming mappings specify a direction, coherent mappings implicitly have a direction attribute setting of DMA_BIDIRECTIONAL. diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst index 2ad08517e626..3087bea715ed 100644 --- a/Documentation/core-api/dma-api.rst +++ b/Documentation/core-api/dma-api.rst @@ -8,15 +8,15 @@ This document describes the DMA API. For a more gentle introduction of the API (and actual examples), see Documentation/core-api/dma-api-howto.rst. This API is split into two pieces. Part I describes the basic API. -Part II describes extensions for supporting non-consistent memory +Part II describes extensions for supporting non-coherent memory machines. Unless you know that your driver absolutely has to support -non-consistent platforms (this is usually only legacy platforms) you +non-coherent platforms (this is usually only legacy platforms) you should only use the API described in part I. -Part I - dma_API +Part I - DMA API ---------------- -To get the dma_API, you must #include . This +To get the DMA API, you must #include . This provides dma_addr_t and the interfaces described below. A dma_addr_t can hold any valid DMA address for the platform. It can be @@ -33,13 +33,13 @@ Part Ia - Using large DMA-coherent buffers dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) -Consistent memory is memory for which a write by either the device or +Coherent memory is memory for which a write by either the device or the processor can immediately be read by the processor or device without having to worry about caching effects. (You may however need to make sure to flush the processor's write buffers before telling devices to read that memory.) -This routine allocates a region of bytes of consistent memory. +This routine allocates a region of bytes of coherent memory. It returns a pointer to the allocated region (in the processor's virtual address space) or NULL if the allocation failed. @@ -48,15 +48,14 @@ It also returns a which may be cast to an unsigned integer the same width as the bus and given to the device as the DMA address base of the region. -Note: consistent memory can be expensive on some platforms, and the +Note: coherent memory can be expensive on some platforms, and the minimum allocation length may be as big as a page, so you should -consolidate your requests for consistent memory as much as possible. +consolidate your requests for coherent memory as much as possible. The simplest way to do that is to use the dma_pool calls (see below). -The flag parameter (dma_alloc_coherent() only) allows the caller to -specify the ``GFP_`` flags (see kmalloc()) for the allocation (the -implementation may choose to ignore flags that affect the location of -the returned memory, like GFP_DMA). +The flag parameter allows the caller to specify the ``GFP_`` flags (see +kmalloc()) for the allocation (the implementation may ignore flags that affect +the location of the returned memory, like GFP_DMA). :: @@ -64,19 +63,18 @@ the returned memory, like GFP_DMA). dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle) -Free a region of consistent memory you previously allocated. dev, -size and dma_handle must all be the same as those passed into -dma_alloc_coherent(). cpu_addr must be the virtual address returned by -the dma_alloc_coherent(). +Free a previously allocated region of coherent memory. dev, size and dma_handle +must all be the same as those passed into dma_alloc_coherent(). cpu_addr must +be the virtual address returned by dma_alloc_coherent(). -Note that unlike their sibling allocation calls, these routines -may only be called with IRQs enabled. +Note that unlike the sibling allocation call, this routine may only be called +with IRQs enabled. Part Ib - Using small DMA-coherent buffers ------------------------------------------ -To get this part of the dma_API, you must #include +To get this part of the DMA API, you must #include Many drivers need lots of small DMA-coherent memory regions for DMA descriptors or I/O buffers. Rather than allocating in units of a page @@ -85,78 +83,29 @@ much like a struct kmem_cache, except that they use the DMA-coherent allocator, not __get_free_pages(). Also, they understand common hardware constraints for alignment, like queue heads needing to be aligned on N-byte boundaries. +.. kernel-doc:: mm/dmapool.c + :export: -:: - - struct dma_pool * - dma_pool_create(const char *name, struct device *dev, - size_t size, size_t align, size_t alloc); - -dma_pool_create() initializes a pool of DMA-coherent buffers -for use with a given device. It must be called in a context which -can sleep. - -The "name" is for diagnostics (like a struct kmem_cache name); dev and size -are like what you'd pass to dma_alloc_coherent(). The device's hardware -alignment requirement for this type of data is "align" (which is expressed -in bytes, and must be a power of two). If your device has no boundary -crossing restrictions, pass 0 for alloc; passing 4096 says memory allocated -from this pool must not cross 4KByte boundaries. - -:: - - void * - dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags, - dma_addr_t *handle) - -Wraps dma_pool_alloc() and also zeroes the returned memory if the -allocation attempt succeeded. - - -:: - - void * - dma_pool_alloc(struct dma_pool *pool, gfp_t gfp_flags, - dma_addr_t *dma_handle); - -This allocates memory from the pool; the returned memory will meet the -size and alignment requirements specified at creation time. Pass -GFP_ATOMIC to prevent blocking, or if it's permitted (not -in_interrupt, not holding SMP locks), pass GFP_KERNEL to allow -blocking. Like dma_alloc_coherent(), this returns two values: an -address usable by the CPU, and the DMA address usable by the pool's -device. - -:: - - void - dma_pool_free(struct dma_pool *pool, void *vaddr, - dma_addr_t addr); - -This puts memory back into the pool. The pool is what was passed to -dma_pool_alloc(); the CPU (vaddr) and DMA addresses are what -were returned when that routine allocated the memory being freed. - -:: - - void - dma_pool_destroy(struct dma_pool *pool); - -dma_pool_destroy() frees the resources of the pool. It must be -called in a context which can sleep. Make sure you've freed all allocated -memory back to the pool before you destroy it. +.. kernel-doc:: include/linux/dmapool.h Part Ic - DMA addressing limitations ------------------------------------ +DMA mask is a bit mask of the addressable region for the device. In other words, +if applying the DMA mask (a bitwise AND operation) to the DMA address of a +memory region does not clear any bits in the address, then the device can +perform DMA to that memory region. + +All the below functions which set a DMA mask may fail if the requested mask +cannot be used with the device, or if the device is not capable of doing DMA. + :: int dma_set_mask_and_coherent(struct device *dev, u64 mask) -Checks to see if the mask is possible and updates the device -streaming and coherent DMA mask parameters if it is. +Updates both streaming and coherent DMA masks. Returns: 0 if successful and a negative error if not. @@ -165,8 +114,7 @@ Returns: 0 if successful and a negative error if not. int dma_set_mask(struct device *dev, u64 mask) -Checks to see if the mask is possible and updates the device -parameters if it is. +Updates only the streaming DMA mask. Returns: 0 if successful and a negative error if not. @@ -175,8 +123,7 @@ Returns: 0 if successful and a negative error if not. int dma_set_coherent_mask(struct device *dev, u64 mask) -Checks to see if the mask is possible and updates the device -parameters if it is. +Updates only the coherent DMA mask. Returns: 0 if successful and a negative error if not. @@ -231,12 +178,32 @@ transfer memory ownership. Returns %false if those calls can be skipped. unsigned long dma_get_merge_boundary(struct device *dev); -Returns the DMA merge boundary. If the device cannot merge any the DMA address +Returns the DMA merge boundary. If the device cannot merge any DMA address segments, the function returns 0. Part Id - Streaming DMA mappings -------------------------------- +Streaming DMA allows to map an existing buffer for DMA transfers and then +unmap it when finished. Map functions are not guaranteed to succeed, so the +return value must be checked. + +.. note:: + + In particular, mapping may fail for memory not addressable by the + device, e.g. if it is not within the DMA mask of the device and/or a + connecting bus bridge. Streaming DMA functions try to overcome such + addressing constraints, either by using an IOMMU (a device which maps + I/O DMA addresses to physical memory addresses), or by copying the + data to/from a bounce buffer if the kernel is configured with a + :doc:`SWIOTLB `. However, these methods are not always + available, and even if they are, they may still fail for a number of + reasons. + + In short, a device driver may need to be wary of where buffers are + located in physical memory, especially if the DMA mask is less than 32 + bits. + :: dma_addr_t @@ -246,9 +213,7 @@ Part Id - Streaming DMA mappings Maps a piece of processor virtual memory so it can be accessed by the device and returns the DMA address of the memory. -The direction for both APIs may be converted freely by casting. -However the dma_API uses a strongly typed enumerator for its -direction: +The DMA API uses a strongly typed enumerator for its direction: ======================= ============================================= DMA_NONE no direction (used for debugging) @@ -259,31 +224,13 @@ DMA_BIDIRECTIONAL direction isn't known .. note:: - Not all memory regions in a machine can be mapped by this API. - Further, contiguous kernel virtual space may not be contiguous as + Contiguous kernel virtual space may not be contiguous as physical memory. Since this API does not provide any scatter/gather capability, it will fail if the user tries to map a non-physically contiguous piece of memory. For this reason, memory to be mapped by this API should be obtained from sources which guarantee it to be physically contiguous (like kmalloc). - Further, the DMA address of the memory must be within the - dma_mask of the device (the dma_mask is a bit mask of the - addressable region for the device, i.e., if the DMA address of - the memory ANDed with the dma_mask is still equal to the DMA - address, then the device can perform DMA to the memory). To - ensure that the memory allocated by kmalloc is within the dma_mask, - the driver may specify various platform-dependent flags to restrict - the DMA address range of the allocation (e.g., on x86, GFP_DMA - guarantees to be within the first 16MB of available DMA addresses, - as required by ISA devices). - - Note also that the above constraints on physical contiguity and - dma_mask may not apply if the platform has an IOMMU (a device which - maps an I/O DMA address to a physical memory address). However, to be - portable, device driver writers may *not* assume that such an IOMMU - exists. - .. warning:: Memory coherency operates at a granularity called the cache @@ -325,8 +272,7 @@ DMA_BIDIRECTIONAL direction isn't known enum dma_data_direction direction) Unmaps the region previously mapped. All the parameters passed in -must be identical to those passed in (and returned) by the mapping -API. +must be identical to those passed to (and returned by) dma_map_single(). :: @@ -376,10 +322,10 @@ action (e.g. reduce current DMA mapping usage or delay and try again later). dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) -Returns: the number of DMA address segments mapped (this may be shorter -than passed in if some elements of the scatter/gather list are -physically or virtually adjacent and an IOMMU maps them with a single -entry). +Maps a scatter/gather list for DMA. Returns the number of DMA address segments +mapped, which may be smaller than passed in if several consecutive +sglist entries are merged (e.g. with an IOMMU, or if some adjacent segments +just happen to be physically contiguous). Please note that the sg cannot be mapped again if it has been mapped once. The mapping process is allowed to destroy information in the sg. @@ -403,9 +349,8 @@ With scatterlists, you use the resulting mapping like this:: where nents is the number of entries in the sglist. The implementation is free to merge several consecutive sglist entries -into one (e.g. with an IOMMU, or if several pages just happen to be -physically contiguous) and returns the actual number of sg entries it -mapped them to. On failure 0, is returned. +into one. The returned number is the actual number of sg entries it +mapped them to. On failure, 0 is returned. Then you should loop count times (note: this can be less than nents times) and use sg_dma_address() and sg_dma_len() macros where you previously @@ -775,19 +720,19 @@ memory or doing partial flushes. of two for easy alignment. -Part III - Debug drivers use of the DMA-API +Part III - Debug drivers use of the DMA API ------------------------------------------- -The DMA-API as described above has some constraints. DMA addresses must be +The DMA API as described above has some constraints. DMA addresses must be released with the corresponding function with the same size for example. With the advent of hardware IOMMUs it becomes more and more important that drivers do not violate those constraints. In the worst case such a violation can result in data corruption up to destroyed filesystems. -To debug drivers and find bugs in the usage of the DMA-API checking code can +To debug drivers and find bugs in the usage of the DMA API checking code can be compiled into the kernel which will tell the developer about those violations. If your architecture supports it you can select the "Enable -debugging of DMA-API usage" option in your kernel configuration. Enabling this +debugging of DMA API usage" option in your kernel configuration. Enabling this option has a performance impact. Do not enable it in production kernels. If you boot the resulting kernel will contain code which does some bookkeeping @@ -826,7 +771,7 @@ example warning message may look like this:: <4>---[ end trace f6435a98e2a38c0e ]--- The driver developer can find the driver and the device including a stacktrace -of the DMA-API call which caused this warning. +of the DMA API call which caused this warning. Per default only the first error will result in a warning message. All other errors will only silently counted. This limitation exist to prevent the code @@ -834,7 +779,7 @@ from flooding your kernel log. To support debugging a device driver this can be disabled via debugfs. See the debugfs interface documentation below for details. -The debugfs directory for the DMA-API debugging code is called dma-api/. In +The debugfs directory for the DMA API debugging code is called dma-api/. In this directory the following files can currently be found: =============================== =============================================== @@ -882,7 +827,7 @@ dma-api/driver_filter You can write a name of a driver into this file If you have this code compiled into your kernel it will be enabled by default. If you want to boot without the bookkeeping anyway you can provide -'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. +'dma_debug=off' as a boot parameter. This will disable DMA API debugging. Notice that you can not enable it again at runtime. You have to reboot to do so. @@ -915,3 +860,9 @@ the driver. When driver does unmap, debug_dma_unmap() checks the flag and if this flag is still set, prints warning message that includes call trace that leads up to the unmap. This interface can be called from dma_mapping_error() routines to enable DMA mapping error check debugging. + +Functions and structures +======================== + +.. kernel-doc:: include/linux/scatterlist.h +.. kernel-doc:: lib/scatterlist.c diff --git a/Documentation/core-api/entry.rst b/Documentation/core-api/entry.rst index a15f9b1767a2..71d8eedc0549 100644 --- a/Documentation/core-api/entry.rst +++ b/Documentation/core-api/entry.rst @@ -105,7 +105,7 @@ has to do extra work between the various steps. In such cases it has to ensure that enter_from_user_mode() is called first on entry and exit_to_user_mode() is called last on exit. -Do not nest syscalls. Nested systcalls will cause RCU and/or context tracking +Do not nest syscalls. Nested syscalls will cause RCU and/or context tracking to print a warning. KVM @@ -115,8 +115,8 @@ Entering or exiting guest mode is very similar to syscalls. From the host kernel point of view the CPU goes off into user space when entering the guest and returns to the kernel on exit. -kvm_guest_enter_irqoff() is a KVM-specific variant of exit_to_user_mode() -and kvm_guest_exit_irqoff() is the KVM variant of enter_from_user_mode(). +guest_state_enter_irqoff() is a KVM-specific variant of exit_to_user_mode() +and guest_state_exit_irqoff() is the KVM variant of enter_from_user_mode(). The state operations have the same ordering. Task work handling is done separately for guest at the boundary of the diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst index 7a4ca18ca6e2..a03a99c2cac5 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -54,6 +54,7 @@ Library functionality that is used throughout the kernel. union_find min_heap parser + list Low level entry and exit ======================== diff --git a/Documentation/core-api/kernel-api.rst b/Documentation/core-api/kernel-api.rst index ae92a2571388..e8211c4ca662 100644 --- a/Documentation/core-api/kernel-api.rst +++ b/Documentation/core-api/kernel-api.rst @@ -3,12 +3,6 @@ The Linux Kernel API ==================== -List Management Functions -========================= - -.. kernel-doc:: include/linux/list.h - :internal: - Basic C Library Functions ========================= @@ -136,26 +130,28 @@ Arithmetic Overflow Checking CRC Functions ------------- -.. kernel-doc:: lib/crc4.c +.. kernel-doc:: lib/crc/crc4.c :export: -.. kernel-doc:: lib/crc7.c +.. kernel-doc:: lib/crc/crc7.c :export: -.. kernel-doc:: lib/crc8.c +.. kernel-doc:: lib/crc/crc8.c :export: -.. kernel-doc:: lib/crc16.c +.. kernel-doc:: lib/crc/crc16.c :export: -.. kernel-doc:: lib/crc32.c - -.. kernel-doc:: lib/crc-ccitt.c +.. kernel-doc:: lib/crc/crc-ccitt.c :export: -.. kernel-doc:: lib/crc-itu-t.c +.. kernel-doc:: lib/crc/crc-itu-t.c :export: +.. kernel-doc:: include/linux/crc32.h + +.. kernel-doc:: include/linux/crc64.h + Base 2 log and power Functions ------------------------------ diff --git a/Documentation/core-api/list.rst b/Documentation/core-api/list.rst new file mode 100644 index 000000000000..86873ce9adbf --- /dev/null +++ b/Documentation/core-api/list.rst @@ -0,0 +1,776 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +===================== +Linked Lists in Linux +===================== + +:Author: Nicolas Frattaroli + +.. contents:: + +Introduction +============ + +Linked lists are one of the most basic data structures used in many programs. +The Linux kernel implements several different flavours of linked lists. The +purpose of this document is not to explain linked lists in general, but to show +new kernel developers how to use the Linux kernel implementations of linked +lists. + +Please note that while linked lists certainly are ubiquitous, they are rarely +the best data structure to use in cases where a simple array doesn't already +suffice. In particular, due to their poor data locality, linked lists are a bad +choice in situations where performance may be of consideration. Familiarizing +oneself with other in-kernel generic data structures, especially for concurrent +accesses, is highly encouraged. + +Linux implementation of doubly linked lists +=========================================== + +Linux's linked list implementations can be used by including the header file +````. + +The doubly-linked list will likely be the most familiar to many readers. It's a +list that can efficiently be traversed forwards and backwards. + +The Linux kernel's doubly-linked list is circular in nature. This means that to +get from the head node to the tail, we can just travel one edge backwards. +Similarly, to get from the tail node to the head, we can simply travel forwards +"beyond" the tail and arrive back at the head. + +Declaring a node +---------------- + +A node in a doubly-linked list is declared by adding a struct list_head +member to the data structure you wish to be contained in the list: + +.. code-block:: c + + struct clown { + unsigned long long shoe_size; + const char *name; + struct list_head node; /* the aforementioned member */ + }; + +This may be an unfamiliar approach to some, as the classical explanation of a +linked list is a list node data structure with pointers to the previous and next +list node, as well the payload data. Linux chooses this approach because it +allows for generic list modification code regardless of what data structure is +contained within the list. Since the struct list_head member is not a pointer +but part of the data structure proper, the container_of() pattern can be used by +the list implementation to access the payload data regardless of its type, while +staying oblivious to what said type actually is. + +Declaring and initializing a list +--------------------------------- + +A doubly-linked list can then be declared as just another struct list_head, +and initialized with the LIST_HEAD_INIT() macro during initial assignment, or +with the INIT_LIST_HEAD() function later: + +.. code-block:: c + + struct clown_car { + int tyre_pressure[4]; + struct list_head clowns; /* Looks like a node! */ + }; + + /* ... Somewhere later in our driver ... */ + + static int circus_init(struct circus_priv *circus) + { + struct clown_car other_car = { + .tyre_pressure = {10, 12, 11, 9}, + .clowns = LIST_HEAD_INIT(other_car.clowns) + }; + + INIT_LIST_HEAD(&circus->car.clowns); + + return 0; + } + +A further point of confusion to some may be that the list itself doesn't really +have its own type. The concept of the entire linked list and a +struct list_head member that points to other entries in the list are one and +the same. + +Adding nodes to the list +------------------------ + +Adding a node to the linked list is done through the list_add() macro. + +We'll return to our clown car example to illustrate how nodes get added to the +list: + +.. code-block:: c + + static int circus_fill_car(struct circus_priv *circus) + { + struct clown_car *car = &circus->car; + struct clown *grock; + struct clown *dimitri; + + /* State 1 */ + + grock = kzalloc(sizeof(*grock), GFP_KERNEL); + if (!grock) + return -ENOMEM; + grock->name = "Grock"; + grock->shoe_size = 1000; + + /* Note that we're adding the "node" member */ + list_add(&grock->node, &car->clowns); + + /* State 2 */ + + dimitri = kzalloc(sizeof(*dimitri), GFP_KERNEL); + if (!dimitri) + return -ENOMEM; + dimitri->name = "Dimitri"; + dimitri->shoe_size = 50; + + list_add(&dimitri->node, &car->clowns); + + /* State 3 */ + + return 0; + } + +In State 1, our list of clowns is still empty:: + + .------. + v | + .--------. | + | clowns |--' + '--------' + +This diagram shows the singular "clowns" node pointing at itself. In this +diagram, and all following diagrams, only the forward edges are shown, to aid in +clarity. + +In State 2, we've added Grock after the list head:: + + .--------------------. + v | + .--------. .-------. | + | clowns |---->| Grock |--' + '--------' '-------' + +This diagram shows the "clowns" node pointing at a new node labeled "Grock". +The Grock node is pointing back at the "clowns" node. + +In State 3, we've added Dimitri after the list head, resulting in the following:: + + .------------------------------------. + v | + .--------. .---------. .-------. | + | clowns |---->| Dimitri |---->| Grock |--' + '--------' '---------' '-------' + +This diagram shows the "clowns" node pointing at a new node labeled "Dimitri", +which then points at the node labeled "Grock". The "Grock" node still points +back at the "clowns" node. + +If we wanted to have Dimitri inserted at the end of the list instead, we'd use +list_add_tail(). Our code would then look like this: + +.. code-block:: c + + static int circus_fill_car(struct circus_priv *circus) + { + /* ... */ + + list_add_tail(&dimitri->node, &car->clowns); + + /* State 3b */ + + return 0; + } + +This results in the following list:: + + .------------------------------------. + v | + .--------. .-------. .---------. | + | clowns |---->| Grock |---->| Dimitri |--' + '--------' '-------' '---------' + +This diagram shows the "clowns" node pointing at the node labeled "Grock", +which points at the new node labeled "Dimitri". The node labeled "Dimitri" +points back at the "clowns" node. + +Traversing the list +------------------- + +To iterate the list, we can loop through all nodes within the list with +list_for_each(). + +In our clown example, this results in the following somewhat awkward code: + +.. code-block:: c + + static unsigned long long circus_get_max_shoe_size(struct circus_priv *circus) + { + unsigned long long res = 0; + struct clown *e; + struct list_head *cur; + + list_for_each(cur, &circus->car.clowns) { + e = list_entry(cur, struct clown, node); + if (e->shoe_size > res) + res = e->shoe_size; + } + + return res; + } + +The list_entry() macro internally uses the aforementioned container_of() to +retrieve the data structure instance that ``node`` is a member of. + +Note how the additional list_entry() call is a little awkward here. It's only +there because we're iterating through the ``node`` members, but we really want +to iterate through the payload, i.e. the ``struct clown`` that contains each +node's struct list_head. For this reason, there is a second macro: +list_for_each_entry() + +Using it would change our code to something like this: + +.. code-block:: c + + static unsigned long long circus_get_max_shoe_size(struct circus_priv *circus) + { + unsigned long long res = 0; + struct clown *e; + + list_for_each_entry(e, &circus->car.clowns, node) { + if (e->shoe_size > res) + res = e->shoe_size; + } + + return res; + } + +This eliminates the need for the list_entry() step, and our loop cursor is now +of the type of our payload. The macro is given the member name that corresponds +to the list's struct list_head within the clown data structure so that it can +still walk the list. + +Removing nodes from the list +---------------------------- + +The list_del() function can be used to remove entries from the list. It not only +removes the given entry from the list, but poisons the entry's ``prev`` and +``next`` pointers, so that unintended use of the entry after removal does not +go unnoticed. + +We can extend our previous example to remove one of the entries: + +.. code-block:: c + + static int circus_fill_car(struct circus_priv *circus) + { + /* ... */ + + list_add(&dimitri->node, &car->clowns); + + /* State 3 */ + + list_del(&dimitri->node); + + /* State 4 */ + + return 0; + } + +The result of this would be this:: + + .--------------------. + v | + .--------. .-------. | .---------. + | clowns |---->| Grock |--' | Dimitri | + '--------' '-------' '---------' + +This diagram shows the "clowns" node pointing at the node labeled "Grock", +which points back at the "clowns" node. Off to the side is a lone node labeled +"Dimitri", which has no arrows pointing anywhere. + +Note how the Dimitri node does not point to itself; its pointers are +intentionally set to a "poison" value that the list code refuses to traverse. + +If we wanted to reinitialize the removed node instead to make it point at itself +again like an empty list head, we can use list_del_init() instead: + +.. code-block:: c + + static int circus_fill_car(struct circus_priv *circus) + { + /* ... */ + + list_add(&dimitri->node, &car->clowns); + + /* State 3 */ + + list_del_init(&dimitri->node); + + /* State 4b */ + + return 0; + } + +This results in the deleted node pointing to itself again:: + + .--------------------. .-------. + v | v | + .--------. .-------. | .---------. | + | clowns |---->| Grock |--' | Dimitri |--' + '--------' '-------' '---------' + +This diagram shows the "clowns" node pointing at the node labeled "Grock", +which points back at the "clowns" node. Off to the side is a lone node labeled +"Dimitri", which points to itself. + +Traversing whilst removing nodes +-------------------------------- + +Deleting entries while we're traversing the list will cause problems if we use +list_for_each() and list_for_each_entry(), as deleting the current entry would +modify the ``next`` pointer of it, which means the traversal can't properly +advance to the next list entry. + +There is a solution to this however: list_for_each_safe() and +list_for_each_entry_safe(). These take an additional parameter of a pointer to +a struct list_head to use as temporary storage for the next entry during +iteration, solving the issue. + +An example of how to use it: + +.. code-block:: c + + static void circus_eject_insufficient_clowns(struct circus_priv *circus) + { + struct clown *e; + struct clown *n; /* temporary storage for safe iteration */ + + list_for_each_entry_safe(e, n, &circus->car.clowns, node) { + if (e->shoe_size < 500) + list_del(&e->node); + } + } + +Proper memory management (i.e. freeing the deleted node while making sure +nothing still references it) in this case is left as an exercise to the reader. + +Cutting a list +-------------- + +There are two helper functions to cut lists with. Both take elements from the +list ``head``, and replace the contents of the list ``list``. + +The first such function is list_cut_position(). It removes all list entries from +``head`` up to and including ``entry``, placing them in ``list`` instead. + +In this example, it's assumed we start with the following list:: + + .----------------------------------------------------------------. + v | + .--------. .-------. .---------. .-----. .---------. | + | clowns |---->| Grock |---->| Dimitri |---->| Pic |---->| Alfredo |--' + '--------' '-------' '---------' '-----' '---------' + +With the following code, every clown up to and including "Pic" is moved from +the "clowns" list head to a separate struct list_head initialized at local +stack variable ``retirement``: + +.. code-block:: c + + static void circus_retire_clowns(struct circus_priv *circus) + { + struct list_head retirement = LIST_HEAD_INIT(retirement); + struct clown *grock, *dimitri, *pic, *alfredo; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + list_cut_position(&retirement, &car->clowns, &pic->node); + + /* State 1 */ + } + +The resulting ``car->clowns`` list would be this:: + + .----------------------. + v | + .--------. .---------. | + | clowns |---->| Alfredo |--' + '--------' '---------' + +Meanwhile, the ``retirement`` list is transformed to the following:: + + .--------------------------------------------------. + v | + .------------. .-------. .---------. .-----. | + | retirement |---->| Grock |---->| Dimitri |---->| Pic |--' + '------------' '-------' '---------' '-----' + +The second function, list_cut_before(), is much the same, except it cuts before +the ``entry`` node, i.e. it removes all list entries from ``head`` up to but +excluding ``entry``, placing them in ``list`` instead. This example assumes the +same initial starting list as the previous example: + +.. code-block:: c + + static void circus_retire_clowns(struct circus_priv *circus) + { + struct list_head retirement = LIST_HEAD_INIT(retirement); + struct clown *grock, *dimitri, *pic, *alfredo; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + list_cut_before(&retirement, &car->clowns, &pic->node); + + /* State 1b */ + } + +The resulting ``car->clowns`` list would be this:: + + .----------------------------------. + v | + .--------. .-----. .---------. | + | clowns |---->| Pic |---->| Alfredo |--' + '--------' '-----' '---------' + +Meanwhile, the ``retirement`` list is transformed to the following:: + + .--------------------------------------. + v | + .------------. .-------. .---------. | + | retirement |---->| Grock |---->| Dimitri |--' + '------------' '-------' '---------' + +It should be noted that both functions will destroy links to any existing nodes +in the destination ``struct list_head *list``. + +Moving entries and partial lists +-------------------------------- + +The list_move() and list_move_tail() functions can be used to move an entry +from one list to another, to either the start or end respectively. + +In the following example, we'll assume we start with two lists ("clowns" and +"sidewalk" in the following initial state "State 0":: + + .----------------------------------------------------------------. + v | + .--------. .-------. .---------. .-----. .---------. | + | clowns |---->| Grock |---->| Dimitri |---->| Pic |---->| Alfredo |--' + '--------' '-------' '---------' '-----' '---------' + + .-------------------. + v | + .----------. .-----. | + | sidewalk |---->| Pio |--' + '----------' '-----' + +We apply the following example code to the two lists: + +.. code-block:: c + + static void circus_clowns_exit_car(struct circus_priv *circus) + { + struct list_head sidewalk = LIST_HEAD_INIT(sidewalk); + struct clown *grock, *dimitri, *pic, *alfredo, *pio; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + /* State 0 */ + + list_move(&pic->node, &sidewalk); + + /* State 1 */ + + list_move_tail(&dimitri->node, &sidewalk); + + /* State 2 */ + } + +In State 1, we arrive at the following situation:: + + .-----------------------------------------------------. + | | + v | + .--------. .-------. .---------. .---------. | + | clowns |---->| Grock |---->| Dimitri |---->| Alfredo |--' + '--------' '-------' '---------' '---------' + + .-------------------------------. + v | + .----------. .-----. .-----. | + | sidewalk |---->| Pic |---->| Pio |--' + '----------' '-----' '-----' + +In State 2, after we've moved Dimitri to the tail of sidewalk, the situation +changes as follows:: + + .-------------------------------------. + | | + v | + .--------. .-------. .---------. | + | clowns |---->| Grock |---->| Alfredo |--' + '--------' '-------' '---------' + + .-----------------------------------------------. + v | + .----------. .-----. .-----. .---------. | + | sidewalk |---->| Pic |---->| Pio |---->| Dimitri |--' + '----------' '-----' '-----' '---------' + +As long as the source and destination list head are part of the same list, we +can also efficiently bulk move a segment of the list to the tail end of the +list. We continue the previous example by adding a list_bulk_move_tail() after +State 2, moving Pic and Pio to the tail end of the sidewalk list. + +.. code-block:: c + + static void circus_clowns_exit_car(struct circus_priv *circus) + { + struct list_head sidewalk = LIST_HEAD_INIT(sidewalk); + struct clown *grock, *dimitri, *pic, *alfredo, *pio; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + /* State 0 */ + + list_move(&pic->node, &sidewalk); + + /* State 1 */ + + list_move_tail(&dimitri->node, &sidewalk); + + /* State 2 */ + + list_bulk_move_tail(&sidewalk, &pic->node, &pio->node); + + /* State 3 */ + } + +For the sake of brevity, only the altered "sidewalk" list at State 3 is depicted +in the following diagram:: + + .-----------------------------------------------. + v | + .----------. .---------. .-----. .-----. | + | sidewalk |---->| Dimitri |---->| Pic |---->| Pio |--' + '----------' '---------' '-----' '-----' + +Do note that list_bulk_move_tail() does not do any checking as to whether all +three supplied ``struct list_head *`` parameters really do belong to the same +list. If you use it outside the constraints the documentation gives, then the +result is a matter between you and the implementation. + +Rotating entries +---------------- + +A common write operation on lists, especially when using them as queues, is +to rotate it. A list rotation means entries at the front are sent to the back. + +For rotation, Linux provides us with two functions: list_rotate_left() and +list_rotate_to_front(). The former can be pictured like a bicycle chain, taking +the entry after the supplied ``struct list_head *`` and moving it to the tail, +which in essence means the entire list, due to its circular nature, rotates by +one position. + +The latter, list_rotate_to_front(), takes the same concept one step further: +instead of advancing the list by one entry, it advances it *until* the specified +entry is the new front. + +In the following example, our starting state, State 0, is the following:: + + .-----------------------------------------------------------------. + v | + .--------. .-------. .---------. .-----. .---------. .-----. | + | clowns |-->| Grock |-->| Dimitri |-->| Pic |-->| Alfredo |-->| Pio |-' + '--------' '-------' '---------' '-----' '---------' '-----' + +The example code being used to demonstrate list rotations is the following: + +.. code-block:: c + + static void circus_clowns_rotate(struct circus_priv *circus) + { + struct clown *grock, *dimitri, *pic, *alfredo, *pio; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + /* State 0 */ + + list_rotate_left(&car->clowns); + + /* State 1 */ + + list_rotate_to_front(&alfredo->node, &car->clowns); + + /* State 2 */ + + } + +In State 1, we arrive at the following situation:: + + .-----------------------------------------------------------------. + v | + .--------. .---------. .-----. .---------. .-----. .-------. | + | clowns |-->| Dimitri |-->| Pic |-->| Alfredo |-->| Pio |-->| Grock |-' + '--------' '---------' '-----' '---------' '-----' '-------' + +Next, after the list_rotate_to_front() call, we arrive in the following +State 2:: + + .-----------------------------------------------------------------. + v | + .--------. .---------. .-----. .-------. .---------. .-----. | + | clowns |-->| Alfredo |-->| Pio |-->| Grock |-->| Dimitri |-->| Pic |-' + '--------' '---------' '-----' '-------' '---------' '-----' + +As is hopefully evident from the diagrams, the entries in front of "Alfredo" +were cycled to the tail end of the list. + +Swapping entries +---------------- + +Another common operation is that two entries need to be swapped with each other. + +For this, Linux provides us with list_swap(). + +In the following example, we have a list with three entries, and swap two of +them. This is our starting state in "State 0":: + + .-----------------------------------------. + v | + .--------. .-------. .---------. .-----. | + | clowns |-->| Grock |-->| Dimitri |-->| Pic |-' + '--------' '-------' '---------' '-----' + +.. code-block:: c + + static void circus_clowns_swap(struct circus_priv *circus) + { + struct clown *grock, *dimitri, *pic; + struct clown_car *car = &circus->car; + + /* ... clown initialization, list adding ... */ + + /* State 0 */ + + list_swap(&dimitri->node, &pic->node); + + /* State 1 */ + } + +The resulting list at State 1 is the following:: + + .-----------------------------------------. + v | + .--------. .-------. .-----. .---------. | + | clowns |-->| Grock |-->| Pic |-->| Dimitri |-' + '--------' '-------' '-----' '---------' + +As is evident by comparing the diagrams, the "Pic" and "Dimitri" nodes have +traded places. + +Splicing two lists together +--------------------------- + +Say we have two lists, in the following example one represented by a list head +we call "knie" and one we call "stey". In a hypothetical circus acquisition, +the two list of clowns should be spliced together. The following is our +situation in "State 0":: + + .-----------------------------------------. + | | + v | + .------. .-------. .---------. .-----. | + | knie |-->| Grock |-->| Dimitri |-->| Pic |--' + '------' '-------' '---------' '-----' + + .-----------------------------. + v | + .------. .---------. .-----. | + | stey |-->| Alfredo |-->| Pio |--' + '------' '---------' '-----' + +The function to splice these two lists together is list_splice(). Our example +code is as follows: + +.. code-block:: c + + static void circus_clowns_splice(void) + { + struct clown *grock, *dimitri, *pic, *alfredo, *pio; + struct list_head knie = LIST_HEAD_INIT(knie); + struct list_head stey = LIST_HEAD_INIT(stey); + + /* ... Clown allocation and initialization here ... */ + + list_add_tail(&grock->node, &knie); + list_add_tail(&dimitri->node, &knie); + list_add_tail(&pic->node, &knie); + list_add_tail(&alfredo->node, &stey); + list_add_tail(&pio->node, &stey); + + /* State 0 */ + + list_splice(&stey, &dimitri->node); + + /* State 1 */ + } + +The list_splice() call here adds all the entries in ``stey`` to the list +``dimitri``'s ``node`` list_head is in, after the ``node`` of ``dimitri``. A +somewhat surprising diagram of the resulting "State 1" follows:: + + .-----------------------------------------------------------------. + | | + v | + .------. .-------. .---------. .---------. .-----. .-----. | + | knie |-->| Grock |-->| Dimitri |-->| Alfredo |-->| Pio |-->| Pic |--' + '------' '-------' '---------' '---------' '-----' '-----' + ^ + .-------------------------------' + | + .------. | + | stey |--' + '------' + +Traversing the ``stey`` list no longer results in correct behavior. A call of +list_for_each() on ``stey`` results in an infinite loop, as it never returns +back to the ``stey`` list head. + +This is because list_splice() did not reinitialize the list_head it took +entries from, leaving its pointer pointing into what is now a different list. + +If we want to avoid this situation, list_splice_init() can be used. It does the +same thing as list_splice(), except reinitalizes the donor list_head after the +transplant. + +Concurrency considerations +-------------------------- + +Concurrent access and modification of a list needs to be protected with a lock +in most cases. Alternatively and preferably, one may use the RCU primitives for +lists in read-mostly use-cases, where read accesses to the list are common but +modifications to the list less so. See Documentation/RCU/listRCU.rst for more +details. + +Further reading +--------------- + +* `How does the kernel implements Linked Lists? - KernelNewbies `_ + +Full List API +============= + +.. kernel-doc:: include/linux/list.h + :internal: diff --git a/Documentation/core-api/memory-hotplug.rst b/Documentation/core-api/memory-hotplug.rst index 682259ee633a..8fc97c2379de 100644 --- a/Documentation/core-api/memory-hotplug.rst +++ b/Documentation/core-api/memory-hotplug.rst @@ -9,6 +9,9 @@ Memory hotplug event notifier Hotplugging events are sent to a notification queue. +Memory notifier +---------------- + There are six types of notification defined in ``include/linux/memory.h``: MEM_GOING_ONLINE @@ -56,20 +59,18 @@ The third argument (arg) passes a pointer of struct memory_notify:: struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; - int status_change_nid_normal; - int status_change_nid; } - start_pfn is start_pfn of online/offline memory. - nr_pages is # of pages of online/offline memory. -- status_change_nid_normal is set node id when N_NORMAL_MEMORY of nodemask - is (will be) set/clear, if this is -1, then nodemask status is not changed. -- status_change_nid is set node id when N_MEMORY of nodemask is (will be) - set/clear. It means a new(memoryless) node gets new memory by online and a - node loses all memory. If this is -1, then nodemask status is not changed. - If status_changed_nid* >= 0, callback should create/discard structures for the - node if necessary. +It is possible to get notified for MEM_CANCEL_ONLINE without having been notified +for MEM_GOING_ONLINE, and the same applies to MEM_CANCEL_OFFLINE and +MEM_GOING_OFFLINE. +This can happen when a consumer fails, meaning we break the callchain and we +stop calling the remaining consumers of the notifier. +It is then important that users of memory_notify make no assumptions and get +prepared to handle such cases. The callback routine shall return one of the values NOTIFY_DONE, NOTIFY_OK, NOTIFY_BAD, NOTIFY_STOP @@ -83,6 +84,78 @@ further processing of the notification queue. NOTIFY_STOP stops further processing of the notification queue. +Numa node notifier +------------------ + +There are six types of notification defined in ``include/linux/node.h``: + +NODE_ADDING_FIRST_MEMORY + Generated before memory becomes available to this node for the first time. + +NODE_CANCEL_ADDING_FIRST_MEMORY + Generated if NODE_ADDING_FIRST_MEMORY fails. + +NODE_ADDED_FIRST_MEMORY + Generated when memory has become available fo this node for the first time. + +NODE_REMOVING_LAST_MEMORY + Generated when the last memory available to this node is about to be offlined. + +NODE_CANCEL_REMOVING_LAST_MEMORY + Generated when NODE_CANCEL_REMOVING_LAST_MEMORY fails. + +NODE_REMOVED_LAST_MEMORY + Generated when the last memory available to this node has been offlined. + +A callback routine can be registered by calling:: + + hotplug_node_notifier(callback_func, priority) + +Callback functions with higher values of priority are called before callback +functions with lower values. + +A callback function must have the following prototype:: + + int callback_func( + + struct notifier_block *self, unsigned long action, void *arg); + +The first argument of the callback function (self) is a pointer to the block +of the notifier chain that points to the callback function itself. +The second argument (action) is one of the event types described above. +The third argument (arg) passes a pointer of struct node_notify:: + + struct node_notify { + int nid; + } + +- nid is the node we are adding or removing memory to. + +It is possible to get notified for NODE_CANCEL_ADDING_FIRST_MEMORY without +having been notified for NODE_ADDING_FIRST_MEMORY, and the same applies to +NODE_CANCEL_REMOVING_LAST_MEMORY and NODE_REMOVING_LAST_MEMORY. +This can happen when a consumer fails, meaning we break the callchain and we +stop calling the remaining consumers of the notifier. +It is then important that users of node_notify make no assumptions and get +prepared to handle such cases. + +The callback routine shall return one of the values +NOTIFY_DONE, NOTIFY_OK, NOTIFY_BAD, NOTIFY_STOP +defined in ``include/linux/notifier.h`` + +NOTIFY_DONE and NOTIFY_OK have no effect on the further processing. + +NOTIFY_BAD is used as response to the NODE_ADDING_FIRST_MEMORY, +NODE_REMOVING_LAST_MEMORY, NODE_ADDED_FIRST_MEMORY or +NODE_REMOVED_LAST_MEMORY action to cancel hotplugging. +It stops further processing of the notification queue. + +NOTIFY_STOP stops further processing of the notification queue. + +Please note that we should not fail for NODE_ADDED_FIRST_MEMORY / +NODE_REMOVED_FIRST_MEMORY, as memory_hotplug code cannot rollback at that +point anymore. + Locking Internals ================= diff --git a/Documentation/core-api/mm-api.rst b/Documentation/core-api/mm-api.rst index af8151db88b2..5063179cfc70 100644 --- a/Documentation/core-api/mm-api.rst +++ b/Documentation/core-api/mm-api.rst @@ -91,12 +91,6 @@ Memory pools .. kernel-doc:: mm/mempool.c :export: -DMA pools -========= - -.. kernel-doc:: mm/dmapool.c - :export: - More Memory Management Functions ================================ @@ -139,4 +133,3 @@ More Memory Management Functions .. kernel-doc:: mm/mmu_notifier.c .. kernel-doc:: mm/balloon_compaction.c .. kernel-doc:: mm/huge_memory.c -.. kernel-doc:: mm/io-mapping.c diff --git a/Documentation/core-api/packing.rst b/Documentation/core-api/packing.rst index 0ce2078c8e13..f68f1e08fef9 100644 --- a/Documentation/core-api/packing.rst +++ b/Documentation/core-api/packing.rst @@ -319,7 +319,7 @@ Here is an example of how to use the fields APIs: #define SIZE 13 - typdef struct __packed { u8 buf[SIZE]; } packed_buf_t; + typedef struct __packed { u8 buf[SIZE]; } packed_buf_t; static const struct packed_field_u8 fields[] = { PACKED_FIELD(100, 90, struct data, field1), diff --git a/Documentation/core-api/symbol-namespaces.rst b/Documentation/core-api/symbol-namespaces.rst index 32fc73dc5529..034898e81ba2 100644 --- a/Documentation/core-api/symbol-namespaces.rst +++ b/Documentation/core-api/symbol-namespaces.rst @@ -76,20 +76,21 @@ unit as preprocessor statement. The above example would then read:: within the corresponding compilation unit before the #include for . Typically it's placed before the first #include statement. -Using the EXPORT_SYMBOL_GPL_FOR_MODULES() macro ------------------------------------------------ +Using the EXPORT_SYMBOL_FOR_MODULES() macro +------------------------------------------- Symbols exported using this macro are put into a module namespace. This -namespace cannot be imported. +namespace cannot be imported. These exports are GPL-only as they are only +intended for in-tree modules. The macro takes a comma separated list of module names, allowing only those modules to access this symbol. Simple tail-globs are supported. For example:: - EXPORT_SYMBOL_GPL_FOR_MODULES(preempt_notifier_inc, "kvm,kvm-*") + EXPORT_SYMBOL_FOR_MODULES(preempt_notifier_inc, "kvm,kvm-*") -will limit usage of this symbol to modules whoes name matches the given +will limit usage of this symbol to modules whose name matches the given patterns. How to use Symbols exported in Namespaces diff --git a/Documentation/core-api/workqueue.rst b/Documentation/core-api/workqueue.rst index e295835fc116..165ca73e8351 100644 --- a/Documentation/core-api/workqueue.rst +++ b/Documentation/core-api/workqueue.rst @@ -183,6 +183,12 @@ resources, scheduled and executed. BH work items cannot sleep. All other features such as delayed queueing, flushing and canceling are supported. +``WQ_PERCPU`` + Work items queued to a per-cpu wq are bound to a specific CPU. + This flag is the right choice when cpu locality is important. + + This flag is the complement of ``WQ_UNBOUND``. + ``WQ_UNBOUND`` Work items queued to an unbound wq are served by the special worker-pools which host workers which are not bound to any diff --git a/Documentation/crypto/crypto_engine.rst b/Documentation/crypto/crypto_engine.rst index d562ea17d994..7ef850e28016 100644 --- a/Documentation/crypto/crypto_engine.rst +++ b/Documentation/crypto/crypto_engine.rst @@ -36,12 +36,6 @@ engine using ``crypto_engine_stop()`` and destroy the engine with Before transferring any request, you have to fill the context enginectx by providing functions for the following: -* ``prepare_crypt_hardware``: Called once before any prepare functions are - called. - -* ``unprepare_crypt_hardware``: Called once after all unprepare functions have - been called. - * ``prepare_cipher_request``/``prepare_hash_request``: Called before each corresponding request is performed. If some processing or other preparatory work is required, do it here. diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index 76bd0ddb0041..d5c47e560324 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -495,6 +495,15 @@ Comments See: https://lore.kernel.org/lkml/20131006222342.GT19510@leaf/ + **UNCOMMENTED_RGMII_MODE** + Historically, the RGMII PHY modes specified in Device Trees have been + used inconsistently, often referring to the usage of delays on the PHY + side rather than describing the board. + + PHY modes "rgmii", "rgmii-rxid" and "rgmii-txid" modes require the clock + signal to be delayed on the PCB; this unusual configuration should be + described in a comment. If they are not (meaning that the delay is realized + internally in the MAC or PHY), "rgmii-id" is the correct PHY mode. Commit message -------------- diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index 038f480074fd..066ecda1dd98 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -699,7 +699,7 @@ the template below. #include #include ... - MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); ... // Use do_interesting_thing() in tests diff --git a/Documentation/devicetree/bindings/arm/amlogic.yaml b/Documentation/devicetree/bindings/arm/amlogic.yaml index 05edf22e6c30..2a096e060ed3 100644 --- a/Documentation/devicetree/bindings/arm/amlogic.yaml +++ b/Documentation/devicetree/bindings/arm/amlogic.yaml @@ -135,6 +135,7 @@ properties: - minix,neo-u9h - nexbox,a1 - tronsmart,vega-s96 + - ugoos,am3 - videostrong,gxm-kiii-pro - wetek,core2 - const: amlogic,s912 diff --git a/Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml b/Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml index 87128e7b7d28..f5b54b4fc55d 100644 --- a/Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml +++ b/Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml @@ -41,10 +41,10 @@ additionalProperties: false examples: - | - #include + #include - trbe { - compatible = "arm,trace-buffer-extension"; - interrupts = ; - }; + trbe { + compatible = "arm,trace-buffer-extension"; + interrupts = ; + }; ... diff --git a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml index 01333ac111fb..456dbf7b5ec8 100644 --- a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml +++ b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml @@ -87,6 +87,7 @@ properties: - facebook,greatlakes-bmc - facebook,harma-bmc - facebook,minerva-cmc + - facebook,santabarbara-bmc - facebook,yosemite4-bmc - ibm,blueridge-bmc - ibm,everest-bmc @@ -98,6 +99,7 @@ properties: - inventec,starscream-bmc - inventec,transformer-bmc - jabil,rbp-bmc + - nvidia,gb200nvl-bmc - qcom,dc-scm-v1-bmc - quanta,s6q-bmc - ufispace,ncplite-bmc diff --git a/Documentation/devicetree/bindings/arm/axiado.yaml b/Documentation/devicetree/bindings/arm/axiado.yaml new file mode 100644 index 000000000000..bfabe7b32e65 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/axiado.yaml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/axiado.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axiado Platforms + +maintainers: + - Harshit Shah + +properties: + $nodename: + const: '/' + compatible: + oneOf: + - description: AX3000 based boards + items: + - enum: + - axiado,ax3000-evk # Axiado AX3000 Evaluation Board + - const: axiado,ax3000 # Axiado AX3000 SoC + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/arm/cix.yaml b/Documentation/devicetree/bindings/arm/cix.yaml new file mode 100644 index 000000000000..114dab4bc4d2 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/cix.yaml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/cix.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CIX platforms + +maintainers: + - Peter Chen + - Fugang Duan + +properties: + $nodename: + const: '/' + compatible: + oneOf: + + - description: Radxa Orion O6 + items: + - const: radxa,orion-o6 + - const: cix,sky1 + +additionalProperties: true + +... diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 2e9ab9583005..5bd517befb68 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -200,6 +200,7 @@ properties: - qcom,kryo385 - qcom,kryo465 - qcom,kryo468 + - qcom,kryo470 - qcom,kryo485 - qcom,kryo560 - qcom,kryo570 diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt deleted file mode 100644 index 6dd6f399236d..000000000000 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt +++ /dev/null @@ -1,30 +0,0 @@ -Freescale Vybrid Miscellaneous System Control - Interrupt Router - -The MSCM IP contains multiple sub modules, this binding describes the second -block of registers which control the interrupt router. The interrupt router -allows to configure the recipient of each peripheral interrupt. Furthermore -it controls the directed processor interrupts. The module is available in all -Vybrid SoC's but is only really useful in dual core configurations (VF6xx -which comes with a Cortex-A5/Cortex-M4 combination). - -Required properties: -- compatible: "fsl,vf610-mscm-ir" -- reg: the register range of the MSCM Interrupt Router -- fsl,cpucfg: The handle to the MSCM CPU configuration node, required - to get the current CPU ID -- interrupt-controller: Identifies the node as an interrupt controller -- #interrupt-cells: Two cells, interrupt number and cells. - The hardware interrupt number according to interrupt - assignment of the interrupt router is required. - Flags get passed only when using GIC as parent. Flags - encoding as documented by the GIC bindings. - -Example: - mscm_ir: interrupt-controller@40001800 { - compatible = "fsl,vf610-mscm-ir"; - reg = <0x40001800 0x400>; - fsl,cpucfg = <&mscm_cpucfg>; - interrupt-controller; - #interrupt-cells = <2>; - interrupt-parent = <&intc>; - } diff --git a/Documentation/devicetree/bindings/arm/fsl.yaml b/Documentation/devicetree/bindings/arm/fsl.yaml index d3b5e6923e41..a3e9f9e0735a 100644 --- a/Documentation/devicetree/bindings/arm/fsl.yaml +++ b/Documentation/devicetree/bindings/arm/fsl.yaml @@ -89,6 +89,7 @@ properties: - description: i.MX28 based Boards items: - enum: + - amarula,imx28-rmm - armadeus,imx28-apf28 # APF28 SoM - bluegiga,apx4devkit # Bluegiga APx4 SoM on dev board - crystalfontz,cfa10036 # Crystalfontz CFA-10036 SoM @@ -769,6 +770,15 @@ properties: - const: dh,imx6ull-dhcor-som - const: fsl,imx6ull + - description: i.MX6ULL Engicam MicroGEA SoM based boards + items: + - enum: + - engicam,microgea-imx6ull-bmm # i.MX6ULL Engicam MicroGEA BMM Board + - engicam,microgea-imx6ull-gtw # i.MX6ULL Engicam MicroGEA GTW Board + - engicam,microgea-imx6ull-rmm # i.MX6ULL Engicam MicroGEA RMM Board + - const: engicam,microgea-imx6ull # i.MX6ULL Engicam MicroGEA SoM + - const: fsl,imx6ull + - description: i.MX6ULL PHYTEC phyBOARD-Segin items: - enum: @@ -1095,6 +1105,7 @@ properties: - gateworks,imx8mp-gw74xx # i.MX8MP Gateworks Board - gateworks,imx8mp-gw75xx-2x # i.MX8MP Gateworks Board - gateworks,imx8mp-gw82xx-2x # i.MX8MP Gateworks Board + - gocontroll,moduline-display # GOcontroll Moduline Display controller - skov,imx8mp-skov-basic # SKOV i.MX8MP baseboard without frontplate - skov,imx8mp-skov-revb-hdmi # SKOV i.MX8MP climate control without panel - skov,imx8mp-skov-revb-lt6 # SKOV i.MX8MP climate control with 7” panel @@ -1395,6 +1406,13 @@ properties: - fsl,imx95-19x19-evk # i.MX95 19x19 EVK Board - const: fsl,imx95 + - description: PHYTEC i.MX 95 FPSC based Boards + items: + - enum: + - phytec,imx95-libra-rdk-fpsc # Libra-i.MX 95 FPSC + - const: phytec,imx95-phycore-fpsc # phyCORE-i.MX 95 FPSC + - const: fsl,imx95 + - description: i.MXRT1050 based Boards items: - enum: diff --git a/Documentation/devicetree/bindings/arm/mediatek.yaml b/Documentation/devicetree/bindings/arm/mediatek.yaml index a7e0a72f6e4c..19ed9448c9c2 100644 --- a/Documentation/devicetree/bindings/arm/mediatek.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek.yaml @@ -27,6 +27,11 @@ properties: - enum: - mediatek,mt2712-evb - const: mediatek,mt2712 + - items: + - enum: + - jty,d101 + - lenovo,a369i + - const: mediatek,mt6572 - items: - enum: - mediatek,mt6580-evbp1 @@ -302,6 +307,10 @@ properties: - const: google,steelix-sku196608 - const: google,steelix - const: mediatek,mt8186 + - description: Google Squirtle (Acer Chromebook Spin 311 (R724T) + items: + - const: google,squirtle + - const: mediatek,mt8186 - description: Google Starmie (ASUS Chromebook Enterprise CM30 (CM3001)) items: - const: google,starmie-sku0 @@ -350,9 +359,6 @@ properties: - const: mediatek,mt8186 - description: Google Voltorb (Acer Chromebook 311 C723/C732T) items: - - enum: - - google,voltorb-sku589824 - - google,voltorb-sku589825 - const: google,voltorb - const: mediatek,mt8186 - items: diff --git a/Documentation/devicetree/bindings/arm/mrvl/mrvl.yaml b/Documentation/devicetree/bindings/arm/mrvl/mrvl.yaml index 4c43eaf3632e..f73bb8ec3a1a 100644 --- a/Documentation/devicetree/bindings/arm/mrvl/mrvl.yaml +++ b/Documentation/devicetree/bindings/arm/mrvl/mrvl.yaml @@ -35,6 +35,11 @@ properties: - enum: - dell,wyse-ariel - const: marvell,mmp3 + - description: PXA1908 based boards + items: + - enum: + - samsung,coreprimevelte + - const: marvell,pxa1908 additionalProperties: true diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index 56f78f0f3803..ae43b3556580 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -209,6 +209,7 @@ properties: - samsung,hlte - sony,xperia-amami - sony,xperia-honami + - sony,xperia-togari - const: qcom,msm8974 - items: @@ -230,6 +231,11 @@ properties: - const: qcom,msm8974pro - const: qcom,msm8974 + - items: + - enum: + - longcheer,l9360 + - const: qcom,msm8976 + - items: - enum: - acer,a1-724 diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml index 5772d905f390..28db6bd6aa5b 100644 --- a/Documentation/devicetree/bindings/arm/rockchip.yaml +++ b/Documentation/devicetree/bindings/arm/rockchip.yaml @@ -258,6 +258,11 @@ properties: - const: firefly,rk3566-roc-pc - const: rockchip,rk3566 + - description: Firefly Station M3 + items: + - const: firefly,rk3588s-roc-pc + - const: rockchip,rk3588s + - description: Firefly Station P2 items: - const: firefly,rk3568-roc-pc @@ -295,6 +300,12 @@ properties: - friendlyarm,nanopi-r4s-enterprise - const: rockchip,rk3399 + - description: FriendlyElec NanoPi M5 series boards + items: + - enum: + - friendlyarm,nanopi-m5 + - const: rockchip,rk3576 + - description: FriendlyElec NanoPi R5 series boards items: - enum: @@ -715,6 +726,13 @@ properties: - const: lckfb,tspi-rk3566 - const: rockchip,rk3566 + - description: Luckfox Core3576 Module based boards + items: + - enum: + - luckfox,omni3576 + - const: luckfox,core3576 + - const: rockchip,rk3576 + - description: Lunzn FastRhino R66S / R68S items: - enum: @@ -961,6 +979,11 @@ properties: - const: radxa,rock-s0 - const: rockchip,rk3308 + - description: Radxa ROCK 5T + items: + - const: radxa,rock-5t + - const: rockchip,rk3588 + - description: Radxa ZERO 3W/3E items: - enum: @@ -1109,6 +1132,11 @@ properties: - const: rockchip,rk3588-toybrick-x0 - const: rockchip,rk3588 + - description: Sakura Pi RK3308B + items: + - const: sakurapi,rk3308-sakurapi-rk3308b + - const: rockchip,rk3308 + - description: Sinovoip RK3308 Banana Pi P2 Pro items: - const: sinovoip,rk3308-bpi-p2pro diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml index 46c1af851be7..55b2200d6e75 100644 --- a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml +++ b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml @@ -25,6 +25,7 @@ select: - rockchip,rk3288-pmu - rockchip,rk3368-pmu - rockchip,rk3399-pmu + - rockchip,rk3528-pmu - rockchip,rk3562-pmu - rockchip,rk3568-pmu - rockchip,rk3576-pmu @@ -44,6 +45,7 @@ properties: - rockchip,rk3288-pmu - rockchip,rk3368-pmu - rockchip,rk3399-pmu + - rockchip,rk3528-pmu - rockchip,rk3562-pmu - rockchip,rk3568-pmu - rockchip,rk3576-pmu diff --git a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml index b3be184c7e56..26fe899badc5 100644 --- a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml +++ b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml @@ -45,6 +45,12 @@ properties: - const: samsung,aries - const: samsung,s5pv210 + - description: Exynos2200 based boards + items: + - enum: + - samsung,g0s # Samsung Galaxy S22+ (SM-S906B) + - const: samsung,exynos2200 + - description: Exynos3250 based boards items: - enum: diff --git a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml index 3e996346b264..4970b9167d1c 100644 --- a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml @@ -55,17 +55,17 @@ unevaluatedProperties: false examples: - | ahb { - compatible = "st,mlahb", "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges; - dma-ranges = <0x00000000 0x38000000 0x10000>, - <0x10000000 0x10000000 0x60000>, - <0x30000000 0x30000000 0x60000>; + compatible = "st,mlahb", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + dma-ranges = <0x00000000 0x38000000 0x10000>, + <0x10000000 0x10000000 0x60000>, + <0x30000000 0x30000000 0x60000>; - m4_rproc: m4@10000000 { - reg = <0x10000000 0x40000>; - }; + m4_rproc: m4@10000000 { + reg = <0x10000000 0x40000>; + }; }; ... diff --git a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml index ed97652c8492..95d2319afe23 100644 --- a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml @@ -36,20 +36,31 @@ properties: clocks: maxItems: 1 + "#clock-cells": + const: 0 + required: - compatible - reg -if: - properties: - compatible: - contains: - enum: - - st,stm32mp157-syscfg - - st,stm32f4-gcan -then: - required: - - clocks +allOf: + - if: + properties: + compatible: + contains: + enum: + - st,stm32mp157-syscfg + - st,stm32f4-gcan + then: + required: + - clocks + - if: + properties: + compatible: + const: st,stm32mp25-syscfg + then: + required: + - "#clock-cells" additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml index 408532504a24..ad144c02eb7e 100644 --- a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml @@ -121,6 +121,7 @@ properties: - st,stm32mp157a-dk1-scmi - st,stm32mp157c-dk2 - st,stm32mp157c-dk2-scmi + - st,stm32mp157f-dk2 - const: st,stm32mp157 - items: diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml index 7807ea613258..c25a22fe4d25 100644 --- a/Documentation/devicetree/bindings/arm/sunxi.yaml +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml @@ -341,15 +341,11 @@ properties: - const: allwinner,i12-tvbox - const: allwinner,sun7i-a20 - - description: ICnova A20 ADB4006 + - description: ICnova A20 items: - - const: incircuit,icnova-a20-adb4006 - - const: incircuit,icnova-a20 - - const: allwinner,sun7i-a20 - - - description: ICNova A20 SWAC - items: - - const: incircuit,icnova-a20-swac + - enum: + - incircuit,icnova-a20-adb4006 + - incircuit,icnova-a20-swac - const: incircuit,icnova-a20 - const: allwinner,sun7i-a20 @@ -760,21 +756,12 @@ properties: - const: pine64,pinebook - const: allwinner,sun50i-a64 - - description: Pine64 PinePhone Developer Batch (1.0) + - description: Pine64 PinePhone items: - - const: pine64,pinephone-1.0 - - const: pine64,pinephone - - const: allwinner,sun50i-a64 - - - description: Pine64 PinePhone Braveheart (1.1) - items: - - const: pine64,pinephone-1.1 - - const: pine64,pinephone - - const: allwinner,sun50i-a64 - - - description: Pine64 PinePhone (1.2) - items: - - const: pine64,pinephone-1.2 + - enum: + - pine64,pinephone-1.0 # Developer Batch (1.0) + - pine64,pinephone-1.1 # Braveheart (1.1) + - pine64,pinephone-1.2 - const: pine64,pinephone - const: allwinner,sun50i-a64 @@ -996,6 +983,11 @@ properties: - const: xunlong,orangepi-3 - const: allwinner,sun50i-h6 + - description: Xunlong OrangePi 4A + items: + - const: xunlong,orangepi-4a + - const: allwinner,sun55i-t527 + - description: Xunlong OrangePi Lite items: - const: xunlong,orangepi-lite diff --git a/Documentation/devicetree/bindings/arm/tegra.yaml b/Documentation/devicetree/bindings/arm/tegra.yaml index 9cae3268a827..1634dab53269 100644 --- a/Documentation/devicetree/bindings/arm/tegra.yaml +++ b/Documentation/devicetree/bindings/arm/tegra.yaml @@ -52,6 +52,10 @@ properties: - nvidia,cardhu-a04 - const: nvidia,cardhu - const: nvidia,tegra30 + - description: ASUS Portable AiO P1801-T + items: + - const: asus,p1801-t + - const: nvidia,tegra30 - description: ASUS Transformers Device family items: - enum: @@ -61,6 +65,10 @@ properties: - asus,tf300tl - asus,tf700t - const: nvidia,tegra30 + - description: Asus VivoTab RT + items: + - const: asus,tf600t + - const: nvidia,tegra30 - description: LG Optimus 4X P880 items: - const: lg,p880 @@ -242,5 +250,10 @@ properties: - const: nvidia,p3768-0000+p3767-0005 - const: nvidia,p3767-0005 - const: nvidia,tegra234 + - description: NVIDIA P3971-0089+P3834-0008 Engineering Reference Platform + items: + - const: nvidia,p3971-0089+p3834-0008 + - const: nvidia,p3834-0008 + - const: nvidia,tegra264 additionalProperties: true diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.yaml b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.yaml index ea4fbf655220..be70819020c5 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.yaml +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.yaml @@ -16,6 +16,7 @@ properties: - nvidia,tegra186-pmc - nvidia,tegra194-pmc - nvidia,tegra234-pmc + - nvidia,tegra264-pmc reg: minItems: 4 diff --git a/Documentation/devicetree/bindings/arm/ti/k3.yaml b/Documentation/devicetree/bindings/arm/ti/k3.yaml index bf6003d8fb76..e80c653fa438 100644 --- a/Documentation/devicetree/bindings/arm/ti/k3.yaml +++ b/Documentation/devicetree/bindings/arm/ti/k3.yaml @@ -25,6 +25,12 @@ properties: - ti,am62a7-sk - const: ti,am62a7 + - description: K3 AM62D2 SoC and Boards + items: + - enum: + - ti,am62d2-evm + - const: ti,am62d2 + - description: K3 AM62A7 SoC PHYTEC phyBOARD-Lyra items: - const: phytec,am62a7-phyboard-lyra-rdk diff --git a/Documentation/devicetree/bindings/arm/ti/omap.yaml b/Documentation/devicetree/bindings/arm/ti/omap.yaml index 3603edd7361d..aa5df4692e37 100644 --- a/Documentation/devicetree/bindings/arm/ti/omap.yaml +++ b/Documentation/devicetree/bindings/arm/ti/omap.yaml @@ -107,6 +107,7 @@ properties: - compulab,cm-t335 - moxa,uc-8100-me-t - novatech,am335x-lxm + - seeed,am335x-bone-green-eco - ti,am335x-bone - ti,am335x-evm - ti,am3359-icev2 diff --git a/Documentation/devicetree/bindings/bus/fsl,imx8mp-aipstz.yaml b/Documentation/devicetree/bindings/bus/fsl,imx8mp-aipstz.yaml new file mode 100644 index 000000000000..993293ebc4d3 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/fsl,imx8mp-aipstz.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/fsl,imx8mp-aipstz.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Secure AHB to IP Slave bus (AIPSTZ) bridge + +description: + The secure AIPS bridge (AIPSTZ) acts as a bridge for AHB masters issuing + transactions to IP Slave peripherals. Additionally, this module offers access + control configurations meant to restrict which peripherals a master can + access. + +maintainers: + - Laurentiu Mihalcea + +properties: + compatible: + const: fsl,imx8mp-aipstz + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + "#access-controller-cells": + const: 3 + description: + First cell - consumer ID + Second cell - consumer type (master or peripheral) + Third cell - configuration value + + ranges: true + +# borrowed from simple-bus.yaml, no additional requirements for children +patternProperties: + "@(0|[1-9a-f][0-9a-f]*)$": + type: object + additionalProperties: true + properties: + reg: + items: + minItems: 2 + maxItems: 4 + minItems: 1 + maxItems: 1024 + ranges: + oneOf: + - items: + minItems: 3 + maxItems: 7 + minItems: 1 + maxItems: 1024 + - $ref: /schemas/types.yaml#/definitions/flag + anyOf: + - required: + - reg + - required: + - ranges + +required: + - compatible + - reg + - power-domains + - "#address-cells" + - "#size-cells" + - "#access-controller-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + #include + + bus@30df0000 { + compatible = "fsl,imx8mp-aipstz"; + reg = <0x30df0000 0x10000>; + ranges = <0x30c00000 0x30c00000 0x400000>; + power-domains = <&pgc_audio>; + #address-cells = <1>; + #size-cells = <1>; + #access-controller-cells = <3>; + + dma-controller@30e00000 { + compatible = "fsl,imx8mp-sdma", "fsl,imx8mq-sdma"; + reg = <0x30e00000 0x10000>; + #dma-cells = <3>; + clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_SDMA3_ROOT>, + <&clk IMX8MP_CLK_AUDIO_ROOT>; + clock-names = "ipg", "ahb"; + interrupts = ; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; + }; + }; diff --git a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml index 7e1ffc551046..4adbb7afa889 100644 --- a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml +++ b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml @@ -103,11 +103,14 @@ examples: clock-names = "msi", "ahb"; power-domains = <&pd IMX_SC_R_DC_0>; - syscon@56221000 { - compatible = "fsl,imx8qxp-mipi-lvds-csr", "syscon", "simple-mfd"; + bus@56221000 { + compatible = "simple-pm-bus", "syscon"; reg = <0x56221000 0x1000>; clocks = <&mipi_lvds_0_di_mipi_lvds_regs_lpcg IMX_LPCG_CLK_4>; clock-names = "ipg"; + #address-cells = <1>; + #size-cells = <1>; + ranges; pxl2dpi { compatible = "fsl,imx8qxp-pxl2dpi"; diff --git a/Documentation/devicetree/bindings/clock/alphascale,acc.txt b/Documentation/devicetree/bindings/clock/alphascale,acc.txt deleted file mode 100644 index c9fb9324c634..000000000000 --- a/Documentation/devicetree/bindings/clock/alphascale,acc.txt +++ /dev/null @@ -1,114 +0,0 @@ -Alphascale Clock Controller - -The ACC (Alphascale Clock Controller) is responsible for choosing proper -clock source, setting dividers and clock gates. - -Required properties for the ACC node: - - compatible: must be "alphascale,asm9260-clock-controller" - - reg: must contain the ACC register base and size - - #clock-cells : shall be set to 1. - -Simple one-cell clock specifier format is used, where the only cell is used -as an index of the clock inside the provider. -It is encouraged to use dt-binding for clock index definitions. SoC specific -dt-binding should be included to the device tree descriptor. For example -Alphascale ASM9260: -#include - -This binding contains two types of clock providers: - _AHB_ - AHB gate; - _SYS_ - adjustable clock source. Not all peripheral have _SYS_ clock provider. -All clock specific details can be found in the SoC documentation. -CLKID_AHB_ROM 0 -CLKID_AHB_RAM 1 -CLKID_AHB_GPIO 2 -CLKID_AHB_MAC 3 -CLKID_AHB_EMI 4 -CLKID_AHB_USB0 5 -CLKID_AHB_USB1 6 -CLKID_AHB_DMA0 7 -CLKID_AHB_DMA1 8 -CLKID_AHB_UART0 9 -CLKID_AHB_UART1 10 -CLKID_AHB_UART2 11 -CLKID_AHB_UART3 12 -CLKID_AHB_UART4 13 -CLKID_AHB_UART5 14 -CLKID_AHB_UART6 15 -CLKID_AHB_UART7 16 -CLKID_AHB_UART8 17 -CLKID_AHB_UART9 18 -CLKID_AHB_I2S0 19 -CLKID_AHB_I2C0 20 -CLKID_AHB_I2C1 21 -CLKID_AHB_SSP0 22 -CLKID_AHB_IOCONFIG 23 -CLKID_AHB_WDT 24 -CLKID_AHB_CAN0 25 -CLKID_AHB_CAN1 26 -CLKID_AHB_MPWM 27 -CLKID_AHB_SPI0 28 -CLKID_AHB_SPI1 29 -CLKID_AHB_QEI 30 -CLKID_AHB_QUADSPI0 31 -CLKID_AHB_CAMIF 32 -CLKID_AHB_LCDIF 33 -CLKID_AHB_TIMER0 34 -CLKID_AHB_TIMER1 35 -CLKID_AHB_TIMER2 36 -CLKID_AHB_TIMER3 37 -CLKID_AHB_IRQ 38 -CLKID_AHB_RTC 39 -CLKID_AHB_NAND 40 -CLKID_AHB_ADC0 41 -CLKID_AHB_LED 42 -CLKID_AHB_DAC0 43 -CLKID_AHB_LCD 44 -CLKID_AHB_I2S1 45 -CLKID_AHB_MAC1 46 - -CLKID_SYS_CPU 47 -CLKID_SYS_AHB 48 -CLKID_SYS_I2S0M 49 -CLKID_SYS_I2S0S 50 -CLKID_SYS_I2S1M 51 -CLKID_SYS_I2S1S 52 -CLKID_SYS_UART0 53 -CLKID_SYS_UART1 54 -CLKID_SYS_UART2 55 -CLKID_SYS_UART3 56 -CLKID_SYS_UART4 56 -CLKID_SYS_UART5 57 -CLKID_SYS_UART6 58 -CLKID_SYS_UART7 59 -CLKID_SYS_UART8 60 -CLKID_SYS_UART9 61 -CLKID_SYS_SPI0 62 -CLKID_SYS_SPI1 63 -CLKID_SYS_QUADSPI 64 -CLKID_SYS_SSP0 65 -CLKID_SYS_NAND 66 -CLKID_SYS_TRACE 67 -CLKID_SYS_CAMM 68 -CLKID_SYS_WDT 69 -CLKID_SYS_CLKOUT 70 -CLKID_SYS_MAC 71 -CLKID_SYS_LCD 72 -CLKID_SYS_ADCANA 73 - -Example of clock consumer with _SYS_ and _AHB_ sinks. -uart4: serial@80010000 { - compatible = "alphascale,asm9260-uart"; - reg = <0x80010000 0x4000>; - clocks = <&acc CLKID_SYS_UART4>, <&acc CLKID_AHB_UART4>; - interrupts = <19>; -}; - -Clock consumer with only one, _AHB_ sink. -timer0: timer@80088000 { - compatible = "alphascale,asm9260-timer"; - reg = <0x80088000 0x4000>; - clocks = <&acc CLKID_AHB_TIMER0>; - interrupts = <29>; -}; - diff --git a/Documentation/devicetree/bindings/clock/alphascale,asm9260-clock-controller.yaml b/Documentation/devicetree/bindings/clock/alphascale,asm9260-clock-controller.yaml new file mode 100644 index 000000000000..1caad419ce9d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/alphascale,asm9260-clock-controller.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/alphascale,asm9260-clock-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Alphascale Clock Controller + +maintainers: + - Oleksij Rempel + +description: | + The ACC (Alphascale Clock Controller) is responsible for choosing proper + clock source, setting dividers and clock gates. + + Simple one-cell clock specifier format is used, where the only cell is used + as an index of the clock inside the provider. + It is encouraged to use dt-binding for clock index definitions. SoC specific + dt-binding should be included to the device tree descriptor. For example + Alphascale ASM9260: + + #include + + This binding contains two types of clock providers: + + _AHB_ - AHB gate; + _SYS_ - adjustable clock source. Not all peripheral have _SYS_ clock provider. + + All clock specific details can be found in the SoC documentation. + +properties: + compatible: + const: alphascale,asm9260-clock-controller + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/apm,xgene-device-clock.yaml b/Documentation/devicetree/bindings/clock/apm,xgene-device-clock.yaml new file mode 100644 index 000000000000..b27bcb2a9ee0 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/apm,xgene-device-clock.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/apm,xgene-device-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene SoC device clocks + +maintainers: + - Khuong Dinh + +properties: + compatible: + const: apm,xgene-device-clock + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + items: + - enum: [ csr-reg, div-reg ] + - const: div-reg + minItems: 1 + + clocks: + maxItems: 1 + + "#clock-cells": + const: 1 + + clock-output-names: + maxItems: 1 + + clock-names: + maxItems: 1 + + csr-offset: + description: Offset to the CSR reset register + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + + csr-mask: + description: CSR reset mask bit + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0xf + + enable-offset: + description: Offset to the enable register + $ref: /schemas/types.yaml#/definitions/uint32 + default: 8 + + enable-mask: + description: CSR enable mask bit + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0xf + + divider-offset: + description: Offset to the divider register + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + + divider-width: + description: Width of the divider register + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + + divider-shift: + description: Bit shift of the divider register + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + - clock-output-names + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/apm,xgene-socpll-clock.yaml b/Documentation/devicetree/bindings/clock/apm,xgene-socpll-clock.yaml new file mode 100644 index 000000000000..bdd4a6b92bbd --- /dev/null +++ b/Documentation/devicetree/bindings/clock/apm,xgene-socpll-clock.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/apm,xgene-socpll-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene SoC PLL, PCPPLL, and PMD clocks + +maintainers: + - Khuong Dinh + +properties: + compatible: + items: + - enum: + - apm,xgene-pcppll-clock + - apm,xgene-pcppll-v2-clock + - apm,xgene-pmd-clock + - apm,xgene-socpll-clock + - apm,xgene-socpll-v2-clock + + reg: + maxItems: 1 + + reg-names: + items: + - enum: [ csr-reg, div-reg ] + - const: div-reg + minItems: 1 + + clocks: + maxItems: 1 + + clock-names: + enum: [ pcppll, socpll ] + + "#clock-cells": + const: 1 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + - clock-output-names + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/armada3700-periph-clock.txt b/Documentation/devicetree/bindings/clock/armada3700-periph-clock.txt deleted file mode 100644 index fbf58c443c04..000000000000 --- a/Documentation/devicetree/bindings/clock/armada3700-periph-clock.txt +++ /dev/null @@ -1,71 +0,0 @@ -* Peripheral Clock bindings for Marvell Armada 37xx SoCs - -Marvell Armada 37xx SoCs provide peripheral clocks which are -used as clock source for the peripheral of the SoC. - -There are two different blocks associated to north bridge and south -bridge. - -The peripheral clock consumer should specify the desired clock by -having the clock ID in its "clocks" phandle cell. - -The following is a list of provided IDs for Armada 3700 North bridge clocks: -ID Clock name Description ------------------------------------ -0 mmc MMC controller -1 sata_host Sata Host -2 sec_at Security AT -3 sac_dap Security DAP -4 tsecm Security Engine -5 setm_tmx Serial Embedded Trace Module -6 avs Adaptive Voltage Scaling -7 sqf SPI -8 pwm PWM -9 i2c_2 I2C 2 -10 i2c_1 I2C 1 -11 ddr_phy DDR PHY -12 ddr_fclk DDR F clock -13 trace Trace -14 counter Counter -15 eip97 EIP 97 -16 cpu CPU - -The following is a list of provided IDs for Armada 3700 South bridge clocks: -ID Clock name Description ------------------------------------ -0 gbe-50 50 MHz parent clock for Gigabit Ethernet -1 gbe-core parent clock for Gigabit Ethernet core -2 gbe-125 125 MHz parent clock for Gigabit Ethernet -3 gbe1-50 50 MHz clock for Gigabit Ethernet port 1 -4 gbe0-50 50 MHz clock for Gigabit Ethernet port 0 -5 gbe1-125 125 MHz clock for Gigabit Ethernet port 1 -6 gbe0-125 125 MHz clock for Gigabit Ethernet port 0 -7 gbe1-core Gigabit Ethernet core port 1 -8 gbe0-core Gigabit Ethernet core port 0 -9 gbe-bm Gigabit Ethernet Buffer Manager -10 sdio SDIO -11 usb32-sub2-sys USB 2 clock -12 usb32-ss-sys USB 3 clock -13 pcie PCIe controller - -Required properties: - -- compatible : shall be "marvell,armada-3700-periph-clock-nb" for the - north bridge block, or - "marvell,armada-3700-periph-clock-sb" for the south bridge block -- reg : must be the register address of North/South Bridge Clock register -- #clock-cells : from common clock binding; shall be set to 1 - -- clocks : list of the parent clock phandle in the following order: - TBG-A P, TBG-B P, TBG-A S, TBG-B S and finally the xtal clock. - - -Example: - -nb_perih_clk: nb-periph-clk@13000{ - compatible = "marvell,armada-3700-periph-clock-nb"; - reg = <0x13000 0x1000>; - clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, - <&tbg 3>, <&xtalclk>; - #clock-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/clock/armada3700-tbg-clock.txt b/Documentation/devicetree/bindings/clock/armada3700-tbg-clock.txt deleted file mode 100644 index ed1df32c577a..000000000000 --- a/Documentation/devicetree/bindings/clock/armada3700-tbg-clock.txt +++ /dev/null @@ -1,27 +0,0 @@ -* Time Base Generator Clock bindings for Marvell Armada 37xx SoCs - -Marvell Armada 37xx SoCs provide Time Base Generator clocks which are -used as parent clocks for the peripheral clocks. - -The TBG clock consumer should specify the desired clock by having the -clock ID in its "clocks" phandle cell. - -The following is a list of provided IDs and clock names on Armada 3700: - 0 = TBG A P - 1 = TBG B P - 2 = TBG A S - 3 = TBG B S - -Required properties: -- compatible : shall be "marvell,armada-3700-tbg-clock" -- reg : must be the register address of North Bridge PLL register -- #clock-cells : from common clock binding; shall be set to 1 - -Example: - -tbg: tbg@13200 { - compatible = "marvell,armada-3700-tbg-clock"; - reg = <0x13200 0x1000>; - clocks = <&xtalclk>; - #clock-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/clock/artpec6.txt b/Documentation/devicetree/bindings/clock/artpec6.txt deleted file mode 100644 index dff9cdf0009c..000000000000 --- a/Documentation/devicetree/bindings/clock/artpec6.txt +++ /dev/null @@ -1,41 +0,0 @@ -* Clock bindings for Axis ARTPEC-6 chip - -The bindings are based on the clock provider binding in -Documentation/devicetree/bindings/clock/clock-bindings.txt - -External clocks: ----------------- - -There are two external inputs to the main clock controller which should be -provided using the common clock bindings. -- "sys_refclk": External 50 Mhz oscillator (required) -- "i2s_refclk": Alternate audio reference clock (optional). - -Main clock controller ---------------------- - -Required properties: -- #clock-cells: Should be <1> - See dt-bindings/clock/axis,artpec6-clkctrl.h for the list of valid identifiers. -- compatible: Should be "axis,artpec6-clkctrl" -- reg: Must contain the base address and length of the system controller -- clocks: Must contain a phandle entry for each clock in clock-names -- clock-names: Must include the external oscillator ("sys_refclk"). Optional - ones are the audio reference clock ("i2s_refclk") and the audio fractional - dividers ("frac_clk0" and "frac_clk1"). - -Examples: - -ext_clk: ext_clk { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <50000000>; -}; - -clkctrl: clkctrl@f8000000 { - #clock-cells = <1>; - compatible = "axis,artpec6-clkctrl"; - reg = <0xf8000000 0x48>; - clocks = <&ext_clk>; - clock-names = "sys_refclk"; -}; diff --git a/Documentation/devicetree/bindings/clock/axis,artpec6-clkctrl.yaml b/Documentation/devicetree/bindings/clock/axis,artpec6-clkctrl.yaml new file mode 100644 index 000000000000..a78269369df8 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/axis,artpec6-clkctrl.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/axis,artpec6-clkctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axis ARTPEC-6 clock controller + +maintainers: + - Lars Persson + +properties: + compatible: + const: axis,artpec6-clkctrl + + reg: + maxItems: 1 + + "#clock-cells": + const: 1 + + clocks: + minItems: 1 + items: + - description: external 50 MHz oscillator. + - description: optional audio reference clock. + - description: fractional audio clock divider 0. + - description: fractional audio clock divider 1. + + clock-names: + minItems: 1 + items: + - const: sys_refclk + - const: i2s_refclk + - const: frac_clk0 + - const: frac_clk1 + +required: + - compatible + - reg + - "#clock-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + clock-controller@f8000000 { + compatible = "axis,artpec6-clkctrl"; + reg = <0xf8000000 0x48>; + #clock-cells = <1>; + clocks = <&ext_clk>; + clock-names = "sys_refclk"; + }; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt b/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt deleted file mode 100644 index 9e0b03a6519b..000000000000 --- a/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt +++ /dev/null @@ -1,60 +0,0 @@ -Broadcom BCM2835 CPRMAN clocks - -This binding uses the common clock binding: - Documentation/devicetree/bindings/clock/clock-bindings.txt - -The CPRMAN clock controller generates clocks in the audio power domain -of the BCM2835. There is a level of PLLs deriving from an external -oscillator, a level of PLL dividers that produce channels off of the -few PLLs, and a level of mostly-generic clock generators sourcing from -the PLL channels. Most other hardware components source from the -clock generators, but a few (like the ARM or HDMI) will source from -the PLL dividers directly. - -Required properties: -- compatible: should be one of the following, - "brcm,bcm2711-cprman" - "brcm,bcm2835-cprman" -- #clock-cells: Should be <1>. The permitted clock-specifier values can be - found in include/dt-bindings/clock/bcm2835.h -- reg: Specifies base physical address and size of the registers -- clocks: phandles to the parent clocks used as input to the module, in - the following order: - - - External oscillator - - DSI0 byte clock - - DSI0 DDR2 clock - - DSI0 DDR clock - - DSI1 byte clock - - DSI1 DDR2 clock - - DSI1 DDR clock - - Only external oscillator is required. The DSI clocks may - not be present, in which case their children will be - unusable. - -Example: - - clk_osc: clock@3 { - compatible = "fixed-clock"; - reg = <3>; - #clock-cells = <0>; - clock-output-names = "osc"; - clock-frequency = <19200000>; - }; - - clocks: cprman@7e101000 { - compatible = "brcm,bcm2835-cprman"; - #clock-cells = <1>; - reg = <0x7e101000 0x2000>; - clocks = <&clk_osc>; - }; - - i2c0: i2c@7e205000 { - compatible = "brcm,bcm2835-i2c"; - reg = <0x7e205000 0x1000>; - interrupts = <2 21>; - clocks = <&clocks BCM2835_CLOCK_VPU>; - #address-cells = <1>; - #size-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.yaml b/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.yaml new file mode 100644 index 000000000000..b0cf76c74bc7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/brcm,bcm2835-cprman.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM2835 CPRMAN clocks + +maintainers: + - Stefan Wahren + - Raspberry Pi Kernel Maintenance + +description: + The CPRMAN clock controller generates clocks in the audio power domain of the + BCM2835. There is a level of PLLs deriving from an external oscillator, a + level of PLL dividers that produce channels off of the few PLLs, and a level + of mostly-generic clock generators sourcing from the PLL channels. Most other + hardware components source from the clock generators, but a few (like the ARM + or HDMI) will source from the PLL dividers directly. + +properties: + compatible: + enum: + - brcm,bcm2711-cprman + - brcm,bcm2835-cprman + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + minItems: 1 + items: + - description: External oscillator clock. + - description: DSI0 byte clock. + - description: DSI0 DDR2 clock. + - description: DSI0 DDR clock. + - description: DSI1 byte clock. + - description: DSI1 DDR2 clock. + - description: DSI1 DDR clock. + +additionalProperties: false + +required: + - compatible + - '#clock-cells' + - reg + - clocks + +examples: + - | + clock-controller@7e101000 { + compatible = "brcm,bcm2835-cprman"; + reg = <0x7e101000 0x2000>; + #clock-cells = <1>; + clocks = <&clk_osc>; + }; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.txt b/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.txt deleted file mode 100644 index 2ebb107331dd..000000000000 --- a/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.txt +++ /dev/null @@ -1,36 +0,0 @@ -Broadcom BCM53573 ILP clock -=========================== - -This binding uses the common clock binding: - Documentation/devicetree/bindings/clock/clock-bindings.txt - -This binding is used for ILP clock (sometimes referred as "slow clock") -on Broadcom BCM53573 devices using Cortex-A7 CPU. - -ILP's rate has to be calculated on runtime and it depends on ALP clock -which has to be referenced. - -This clock is part of PMU (Power Management Unit), a Broadcom's device -handing power-related aspects. Its node must be sub-node of the PMU -device. - -Required properties: -- compatible: "brcm,bcm53573-ilp" -- clocks: has to reference an ALP clock -- #clock-cells: should be <0> -- clock-output-names: from common clock bindings, should contain clock - name - -Example: - -pmu@18012000 { - compatible = "simple-mfd", "syscon"; - reg = <0x18012000 0x00001000>; - - ilp { - compatible = "brcm,bcm53573-ilp"; - clocks = <&alp>; - #clock-cells = <0>; - clock-output-names = "ilp"; - }; -}; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.yaml b/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.yaml new file mode 100644 index 000000000000..cd291f428a8d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.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/brcm,bcm53573-ilp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM53573 ILP clock + +maintainers: + - Rafał Miłecki + +description: > + ILP clock (sometimes referred as "slow clock") on Broadcom BCM53573 devices + using Cortex-A7 CPU. + + ILP's rate has to be calculated on runtime and it depends on ALP clock which + has to be referenced. + + This clock is part of PMU (Power Management Unit), a Broadcom device handling + power-related aspects. Its node must be sub-node of the PMU device. + +properties: + compatible: + items: + - const: brcm,bcm53573-ilp + + clocks: + maxItems: 1 + + '#clock-cells': + const: 0 + + clock-output-names: + items: + - const: ilp + +additionalProperties: false + +examples: + - | + ilp { + compatible = "brcm,bcm53573-ilp"; + clocks = <&alp>; + #clock-cells = <0>; + clock-output-names = "ilp"; + }; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt b/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt deleted file mode 100644 index 3e7ca5530775..000000000000 --- a/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt +++ /dev/null @@ -1,24 +0,0 @@ -Gated Clock Controller Bindings for MIPS based BCM63XX SoCs - -Required properties: -- compatible: must be one of: - "brcm,bcm3368-clocks" - "brcm,bcm6318-clocks" - "brcm,bcm6318-ubus-clocks" - "brcm,bcm6328-clocks" - "brcm,bcm6358-clocks" - "brcm,bcm6362-clocks" - "brcm,bcm6368-clocks" - "brcm,bcm63268-clocks" - -- reg: Address and length of the register set -- #clock-cells: must be <1> - - -Example: - -clkctl: clock-controller@10000004 { - compatible = "brcm,bcm6328-clocks"; - reg = <0x10000004 0x4>; - #clock-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.yaml b/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.yaml new file mode 100644 index 000000000000..56909ea499a2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/brcm,bcm63xx-clocks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MIPS based BCM63XX SoCs Gated Clock Controller + +maintainers: + - Álvaro Fernández Rojas + - Jonas Gorski + +properties: + compatible: + enum: + - brcm,bcm3368-clocks + - brcm,bcm6318-clocks + - brcm,bcm6318-ubus-clocks + - brcm,bcm6328-clocks + - brcm,bcm6358-clocks + - brcm,bcm6362-clocks + - brcm,bcm6368-clocks + - brcm,bcm63268-clocks + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@10000004 { + compatible = "brcm,bcm6328-clocks"; + reg = <0x10000004 0x4>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/cirrus,ep7209-clk.yaml b/Documentation/devicetree/bindings/clock/cirrus,ep7209-clk.yaml new file mode 100644 index 000000000000..fbd0d50d46a8 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/cirrus,ep7209-clk.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/cirrus,ep7209-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CLPS711X Clock Controller + +maintainers: + - Alexander Shiyan + +description: + See include/dt-bindings/clock/clps711x-clock.h for the full list of CLPS711X + clock IDs. + +properties: + compatible: + items: + - const: cirrus,ep7312-clk + - const: cirrus,ep7209-clk + + reg: + maxItems: 1 + + startup-frequency: + description: Factory set CPU startup frequency in HZ. + $ref: /schemas/types.yaml#/definitions/uint32 + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - startup-frequency + - "#clock-cells" + +additionalProperties: false + +examples: + - | + clock-controller@80000000 { + compatible = "cirrus,ep7312-clk", "cirrus,ep7209-clk"; + reg = <0x80000000 0xc000>; + #clock-cells = <1>; + startup-frequency = <73728000>; + }; diff --git a/Documentation/devicetree/bindings/clock/clps711x-clock.txt b/Documentation/devicetree/bindings/clock/clps711x-clock.txt deleted file mode 100644 index f1bd53f79d91..000000000000 --- a/Documentation/devicetree/bindings/clock/clps711x-clock.txt +++ /dev/null @@ -1,19 +0,0 @@ -* Clock bindings for the Cirrus Logic CLPS711X CPUs - -Required properties: -- compatible : Shall contain "cirrus,ep7209-clk". -- reg : Address of the internal register set. -- startup-frequency: Factory set CPU startup frequency in HZ. -- #clock-cells : Should be <1>. - -The clock consumer should specify the desired clock by having the clock -ID in its "clocks" phandle cell. See include/dt-bindings/clock/clps711x-clock.h -for the full list of CLPS711X clock IDs. - -Example: - clks: clks@80000000 { - #clock-cells = <1>; - compatible = "cirrus,ep7312-clk", "cirrus,ep7209-clk"; - reg = <0x80000000 0xc000>; - startup-frequency = <73728000>; - }; diff --git a/Documentation/devicetree/bindings/clock/dove-divider-clock.txt b/Documentation/devicetree/bindings/clock/dove-divider-clock.txt deleted file mode 100644 index 217871f483c0..000000000000 --- a/Documentation/devicetree/bindings/clock/dove-divider-clock.txt +++ /dev/null @@ -1,28 +0,0 @@ -PLL divider based Dove clocks - -Marvell Dove has a 2GHz PLL, which feeds into a set of dividers to provide -high speed clocks for a number of peripherals. These dividers are part of -the PMU, and thus this node should be a child of the PMU node. - -The following clocks are provided: - -ID Clock -------------- -0 AXI bus clock -1 GPU clock -2 VMeta clock -3 LCD clock - -Required properties: -- compatible : shall be "marvell,dove-divider-clock" -- reg : shall be the register address of the Core PLL and Clock Divider - Control 0 register. This will cover that register, as well as the - Core PLL and Clock Divider Control 1 register. Thus, it will have - a size of 8. -- #clock-cells : from common clock binding; shall be set to 1 - -divider_clk: core-clock@64 { - compatible = "marvell,dove-divider-clock"; - reg = <0x0064 0x8>; - #clock-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/clock/img,pistachio-clk.yaml b/Documentation/devicetree/bindings/clock/img,pistachio-clk.yaml new file mode 100644 index 000000000000..e70feee8e894 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/img,pistachio-clk.yaml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/img,pistachio-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Imagination Technologies Pistachio SoC clock controllers + +maintainers: + - Andrew Bresticker + +description: | + Pistachio has four clock controllers (core clock, peripheral clock, peripheral + general control, and top general control) which are instantiated individually + from the device-tree. + + Core clock controller: + + The core clock controller generates clocks for the CPU, RPU (WiFi + BT + co-processor), audio, and several peripherals. + + Peripheral clock controller: + + The peripheral clock controller generates clocks for the DDR, ROM, and other + peripherals. The peripheral system clock ("periph_sys") generated by the core + clock controller is the input clock to the peripheral clock controller. + + Peripheral general control: + + The peripheral general control block generates system interface clocks and + resets for various peripherals. It also contains miscellaneous peripheral + control registers. + + Top-level general control: + + The top-level general control block contains miscellaneous control registers + and gates for the external clocks "audio_clk_in" and "enet_clk_in". + +properties: + compatible: + items: + - enum: + - img,pistachio-clk + - img,pistachio-clk-periph + - img,pistachio-cr-periph + - img,pistachio-cr-top + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + minItems: 1 + maxItems: 3 + +required: + - compatible + - reg + - '#clock-cells' + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + contains: + const: img,pistachio-clk + then: + properties: + clocks: + items: + - description: External 52Mhz oscillator + - description: Alternate audio reference clock + - description: Alternate ethernet PHY clock + + clock-names: + items: + - const: xtal + - const: audio_refclk_ext_gate + - const: ext_enet_in_gate + + - if: + properties: + compatible: + contains: + const: img,pistachio-clk-periph + then: + properties: + clocks: + items: + - description: Peripheral system clock + + clock-names: + items: + - const: periph_sys_core + + - if: + properties: + compatible: + contains: + const: img,pistachio-cr-periph + then: + properties: + clocks: + items: + - description: System interface clock + + clock-names: + items: + - const: sys + + - if: + properties: + compatible: + contains: + const: img,pistachio-cr-top + then: + properties: + clocks: + items: + - description: External audio reference clock + - description: External ethernet PHY clock + + clock-names: + items: + - const: audio_clk_in + - const: enet_clk_in + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt b/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt deleted file mode 100644 index 8cf8f0ecdd16..000000000000 --- a/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt +++ /dev/null @@ -1,77 +0,0 @@ -* NXP LPC1850 Clock Control Unit (CCU) - -Each CGU base clock has several clock branches which can be turned on -or off independently by the Clock Control Units CCU1 or CCU2. The -branch clocks are distributed between CCU1 and CCU2. - - - Above text taken from NXP LPC1850 User Manual. - -This binding uses the common clock binding: - Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible: - Should be "nxp,lpc1850-ccu" -- reg: - Shall define the base and range of the address space - containing clock control registers -- #clock-cells: - Shall have value <1>. The permitted clock-specifier values - are the branch clock names defined in table below. -- clocks: - Shall contain a list of phandles for the base clocks routed - from the CGU to the specific CCU. See mapping of base clocks - and CCU in table below. -- clock-names: - Shall contain a list of names for the base clock routed - from the CGU to the specific CCU. Valid CCU clock names: - "base_usb0_clk", "base_periph_clk", "base_usb1_clk", - "base_cpu_clk", "base_spifi_clk", "base_spi_clk", - "base_apb1_clk", "base_apb3_clk", "base_adchs_clk", - "base_sdio_clk", "base_ssp0_clk", "base_ssp1_clk", - "base_uart0_clk", "base_uart1_clk", "base_uart2_clk", - "base_uart3_clk", "base_audio_clk" - -Which branch clocks that are available on the CCU depends on the -specific LPC part. Check the user manual for your specific part. - -A list of CCU clocks can be found in dt-bindings/clock/lpc18xx-ccu.h. - -Example board file: - -soc { - ccu1: clock-controller@40051000 { - compatible = "nxp,lpc1850-ccu"; - reg = <0x40051000 0x1000>; - #clock-cells = <1>; - clocks = <&cgu BASE_APB3_CLK>, <&cgu BASE_APB1_CLK>, - <&cgu BASE_SPIFI_CLK>, <&cgu BASE_CPU_CLK>, - <&cgu BASE_PERIPH_CLK>, <&cgu BASE_USB0_CLK>, - <&cgu BASE_USB1_CLK>, <&cgu BASE_SPI_CLK>; - clock-names = "base_apb3_clk", "base_apb1_clk", - "base_spifi_clk", "base_cpu_clk", - "base_periph_clk", "base_usb0_clk", - "base_usb1_clk", "base_spi_clk"; - }; - - ccu2: clock-controller@40052000 { - compatible = "nxp,lpc1850-ccu"; - reg = <0x40052000 0x1000>; - #clock-cells = <1>; - clocks = <&cgu BASE_AUDIO_CLK>, <&cgu BASE_UART3_CLK>, - <&cgu BASE_UART2_CLK>, <&cgu BASE_UART1_CLK>, - <&cgu BASE_UART0_CLK>, <&cgu BASE_SSP1_CLK>, - <&cgu BASE_SSP0_CLK>, <&cgu BASE_SDIO_CLK>; - clock-names = "base_audio_clk", "base_uart3_clk", - "base_uart2_clk", "base_uart1_clk", - "base_uart0_clk", "base_ssp1_clk", - "base_ssp0_clk", "base_sdio_clk"; - }; - - /* A user of CCU branch clocks */ - uart1: serial@40082000 { - ... - clocks = <&ccu2 CLK_APB0_UART1>, <&ccu1 CLK_CPU_UART1>; - ... - }; -}; diff --git a/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt b/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt deleted file mode 100644 index 2cc32a9a945a..000000000000 --- a/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt +++ /dev/null @@ -1,131 +0,0 @@ -* NXP LPC1850 Clock Generation Unit (CGU) - -The CGU generates multiple independent clocks for the core and the -peripheral blocks of the LPC18xx. Each independent clock is called -a base clock and itself is one of the inputs to the two Clock -Control Units (CCUs) which control the branch clocks to the -individual peripherals. - -The CGU selects the inputs to the clock generators from multiple -clock sources, controls the clock generation, and routes the outputs -of the clock generators through the clock source bus to the output -stages. Each output stage provides an independent clock source and -corresponds to one of the base clocks for the LPC18xx. - - - Above text taken from NXP LPC1850 User Manual. - - -This binding uses the common clock binding: - Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible: - Should be "nxp,lpc1850-cgu" -- reg: - Shall define the base and range of the address space - containing clock control registers -- #clock-cells: - Shall have value <1>. The permitted clock-specifier values - are the base clock numbers defined below. -- clocks: - Shall contain a list of phandles for the external input - sources to the CGU. The list shall be in the following - order: xtal, 32khz, enet_rx_clk, enet_tx_clk, gp_clkin. -- clock-indices: - Shall be an ordered list of numbers defining the base clock - number provided by the CGU. -- clock-output-names: - Shall be an ordered list of strings defining the names of - the clocks provided by the CGU. - -Which base clocks that are available on the CGU depends on the -specific LPC part. Base clocks are numbered from 0 to 27. - -Number: Name: Description: - 0 BASE_SAFE_CLK Base safe clock (always on) for WWDT - 1 BASE_USB0_CLK Base clock for USB0 - 2 BASE_PERIPH_CLK Base clock for Cortex-M0SUB subsystem, - SPI, and SGPIO - 3 BASE_USB1_CLK Base clock for USB1 - 4 BASE_CPU_CLK System base clock for ARM Cortex-M core - and APB peripheral blocks #0 and #2 - 5 BASE_SPIFI_CLK Base clock for SPIFI - 6 BASE_SPI_CLK Base clock for SPI - 7 BASE_PHY_RX_CLK Base clock for Ethernet PHY Receive clock - 8 BASE_PHY_TX_CLK Base clock for Ethernet PHY Transmit clock - 9 BASE_APB1_CLK Base clock for APB peripheral block # 1 -10 BASE_APB3_CLK Base clock for APB peripheral block # 3 -11 BASE_LCD_CLK Base clock for LCD -12 BASE_ADCHS_CLK Base clock for ADCHS -13 BASE_SDIO_CLK Base clock for SD/MMC -14 BASE_SSP0_CLK Base clock for SSP0 -15 BASE_SSP1_CLK Base clock for SSP1 -16 BASE_UART0_CLK Base clock for UART0 -17 BASE_UART1_CLK Base clock for UART1 -18 BASE_UART2_CLK Base clock for UART2 -19 BASE_UART3_CLK Base clock for UART3 -20 BASE_OUT_CLK Base clock for CLKOUT pin -24-21 - Reserved -25 BASE_AUDIO_CLK Base clock for audio system (I2S) -26 BASE_CGU_OUT0_CLK Base clock for CGU_OUT0 clock output -27 BASE_CGU_OUT1_CLK Base clock for CGU_OUT1 clock output - -BASE_PERIPH_CLK and BASE_SPI_CLK is only available on LPC43xx. -BASE_ADCHS_CLK is only available on LPC4370. - - -Example board file: - -/ { - clocks { - xtal: xtal { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <12000000>; - }; - - xtal32: xtal32 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <32768>; - }; - - enet_rx_clk: enet_rx_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - clock-output-names = "enet_rx_clk"; - }; - - enet_tx_clk: enet_tx_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - clock-output-names = "enet_tx_clk"; - }; - - gp_clkin: gp_clkin { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - clock-output-names = "gp_clkin"; - }; - }; - - soc { - cgu: clock-controller@40050000 { - compatible = "nxp,lpc1850-cgu"; - reg = <0x40050000 0x1000>; - #clock-cells = <1>; - clocks = <&xtal>, <&creg_clk 1>, <&enet_rx_clk>, <&enet_tx_clk>, <&gp_clkin>; - }; - - /* A CGU and CCU clock consumer */ - lcdc: lcdc@40008000 { - ... - clocks = <&cgu BASE_LCD_CLK>, <&ccu1 CLK_CPU_LCD>; - clock-names = "clcdclk", "apb_pclk"; - ... - }; - }; -}; diff --git a/Documentation/devicetree/bindings/clock/lpc1850-creg-clk.txt b/Documentation/devicetree/bindings/clock/lpc1850-creg-clk.txt deleted file mode 100644 index b6b2547a3d17..000000000000 --- a/Documentation/devicetree/bindings/clock/lpc1850-creg-clk.txt +++ /dev/null @@ -1,52 +0,0 @@ -* NXP LPC1850 CREG clocks - -The NXP LPC18xx/43xx CREG (Configuration Registers) block contains -control registers for two low speed clocks. One of the clocks is a -32 kHz oscillator driver with power up/down and clock gating. Next -is a fixed divider that creates a 1 kHz clock from the 32 kHz osc. - -These clocks are used by the RTC and the Event Router peripherals. -The 32 kHz can also be routed to other peripherals to enable low -power modes. - -This binding uses the common clock binding: - Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible: - Should be "nxp,lpc1850-creg-clk" -- #clock-cells: - Shall have value <1>. -- clocks: - Shall contain a phandle to the fixed 32 kHz crystal. - -The creg-clk node must be a child of the creg syscon node. - -The following clocks are available from the clock node. - -Clock ID Name - 0 1 kHz clock - 1 32 kHz Oscillator - -Example: -soc { - creg: syscon@40043000 { - compatible = "nxp,lpc1850-creg", "syscon", "simple-mfd"; - reg = <0x40043000 0x1000>; - - creg_clk: clock-controller { - compatible = "nxp,lpc1850-creg-clk"; - clocks = <&xtal32>; - #clock-cells = <1>; - }; - - ... - }; - - rtc: rtc@40046000 { - ... - clocks = <&creg_clk 0>, <&ccu1 CLK_CPU_BUS>; - clock-names = "rtc", "reg"; - ... - }; -}; diff --git a/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt deleted file mode 100644 index 3ce97cfe999b..000000000000 --- a/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt +++ /dev/null @@ -1,29 +0,0 @@ -AXM5516 clock driver bindings ------------------------------ - -Required properties : -- compatible : shall contain "lsi,axm5516-clks" -- reg : shall contain base register location and length -- #clock-cells : shall contain 1 - -The consumer specifies the desired clock by having the clock ID in its "clocks" -phandle cell. See for the list of -supported clock IDs. - -Example: - - clks: clock-controller@2010020000 { - compatible = "lsi,axm5516-clks"; - #clock-cells = <1>; - reg = <0x20 0x10020000 0 0x20000>; - }; - - serial0: uart@2010080000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x20 0x10080000 0 0x1000>; - interrupts = ; - clocks = <&clks AXXIA_CLK_PER>; - clock-names = "apb_pclk"; - }; - }; - diff --git a/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.yaml b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.yaml new file mode 100644 index 000000000000..7a792dbeffb3 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 LSI +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/lsi,axm5516-clks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LSI AXM5516 Clock Controller + +maintainers: + - Anders Berg + +description: + See for the list of supported clock IDs. + +properties: + compatible: + const: lsi,axm5516-clks + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <1>; + clock-controller@2010020000 { + compatible = "lsi,axm5516-clks"; + #clock-cells = <1>; + reg = <0x20 0x10020000 0x20000>; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/lsi,nspire-cx-clock.yaml b/Documentation/devicetree/bindings/clock/lsi,nspire-cx-clock.yaml new file mode 100644 index 000000000000..52c217d210d0 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lsi,nspire-cx-clock.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/lsi,nspire-cx-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI-NSPIRE Clocks + +maintainers: + - Daniel Tang + +properties: + compatible: + enum: + - lsi,nspire-cx-ahb-divider + - lsi,nspire-classic-ahb-divider + - lsi,nspire-cx-clock + - lsi,nspire-classic-clock + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#clock-cells': + const: 0 + +additionalProperties: false + +required: + - compatible + - reg diff --git a/Documentation/devicetree/bindings/clock/marvell,armada-370-corediv-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,armada-370-corediv-clock.yaml new file mode 100644 index 000000000000..9d766558cdb9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,armada-370-corediv-clock.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/marvell,armada-370-corediv-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell MVEBU Core Divider Clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +properties: + compatible: + oneOf: + - enum: + - marvell,armada-370-corediv-clock + - marvell,armada-375-corediv-clock + - marvell,armada-380-corediv-clock + - marvell,mv98dx3236-corediv-clock + - items: + - const: marvell,armada-390-corediv-clock + - const: marvell,armada-380-corediv-clock + + reg: + maxItems: 1 + + "#clock-cells": + const: 1 + + clocks: + maxItems: 1 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - reg + - "#clock-cells" + - clocks + +additionalProperties: false + +examples: + - | + clock-controller@18740 { + compatible = "marvell,armada-370-corediv-clock"; + reg = <0x18740 0xc>; + #clock-cells = <1>; + clocks = <&pll>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,armada-3700-periph-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,armada-3700-periph-clock.yaml new file mode 100644 index 000000000000..87e8e4ca111a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,armada-3700-periph-clock.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/marvell,armada-3700-periph-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 37xx SoCs Peripheral Clocks + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: > + Marvell Armada 37xx SoCs provide peripheral clocks which are used as clock + source for the peripheral of the SoC. + + There are two different blocks associated to north bridge and south bridge. + + The following is a list of provided IDs for Armada 3700 North bridge clocks: + + ID Clock name Description + ----------------------------------- + 0 mmc MMC controller + 1 sata_host Sata Host + 2 sec_at Security AT + 3 sac_dap Security DAP + 4 tsecm Security Engine + 5 setm_tmx Serial Embedded Trace Module + 6 avs Adaptive Voltage Scaling + 7 sqf SPI + 8 pwm PWM + 9 i2c_2 I2C 2 + 10 i2c_1 I2C 1 + 11 ddr_phy DDR PHY + 12 ddr_fclk DDR F clock + 13 trace Trace + 14 counter Counter + 15 eip97 EIP 97 + 16 cpu CPU + + The following is a list of provided IDs for Armada 3700 South bridge clocks: + + ID Clock name Description + ----------------------------------- + 0 gbe-50 50 MHz parent clock for Gigabit Ethernet + 1 gbe-core parent clock for Gigabit Ethernet core + 2 gbe-125 125 MHz parent clock for Gigabit Ethernet + 3 gbe1-50 50 MHz clock for Gigabit Ethernet port 1 + 4 gbe0-50 50 MHz clock for Gigabit Ethernet port 0 + 5 gbe1-125 125 MHz clock for Gigabit Ethernet port 1 + 6 gbe0-125 125 MHz clock for Gigabit Ethernet port 0 + 7 gbe1-core Gigabit Ethernet core port 1 + 8 gbe0-core Gigabit Ethernet core port 0 + 9 gbe-bm Gigabit Ethernet Buffer Manager + 10 sdio SDIO + 11 usb32-sub2-sys USB 2 clock + 12 usb32-ss-sys USB 3 clock + 13 pcie PCIe controller + +properties: + compatible: + oneOf: + - const: marvell,armada-3700-periph-clock-sb + - items: + - const: marvell,armada-3700-periph-clock-nb + - const: syscon + reg: + maxItems: 1 + + clocks: + items: + - description: TBG-A P clock and specifier + - description: TBG-B P clock and specifier + - description: TBG-A S clock and specifier + - description: TBG-B S clock and specifier + - description: Xtal clock and specifier + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@13000{ + compatible = "marvell,armada-3700-periph-clock-sb"; + reg = <0x13000 0x1000>; + clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, <&tbg 3>, <&xtalclk>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,armada-3700-tbg-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,armada-3700-tbg-clock.yaml new file mode 100644 index 000000000000..7fd1d758f794 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,armada-3700-tbg-clock.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/marvell,armada-3700-tbg-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 3700 Time Base Generator Clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: > + Marvell Armada 37xx SoCs provide Time Base Generator clocks which are used as + parent clocks for the peripheral clocks. + + The TBG clock consumer should specify the desired clock by having the clock ID + in its "clocks" phandle cell. + + The following is a list of provided IDs and clock names on Armada 3700: + + 0 = TBG A P + 1 = TBG B P + 2 = TBG A S + 3 = TBG B S + +properties: + compatible: + const: marvell,armada-3700-tbg-clock + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@13200 { + compatible = "marvell,armada-3700-tbg-clock"; + reg = <0x13200 0x1000>; + clocks = <&xtalclk>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,armada-xp-cpu-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,armada-xp-cpu-clock.yaml new file mode 100644 index 000000000000..f2ac6741da9a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,armada-xp-cpu-clock.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +--- +$schema: http://devicetree.org/meta-schemas/core.yaml# +$id: http://devicetree.org/schemas/clock/marvell,armada-xp-cpu-clock.yaml# + +title: Marvell EBU CPU Clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +properties: + compatible: + enum: + - marvell,armada-xp-cpu-clock + - marvell,mv98dx3236-cpu-clock + + reg: + items: + - description: Clock complex registers + - description: PMU DFS registers + + '#clock-cells': + const: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - '#clock-cells' + - clocks + +additionalProperties: false + +examples: + - | + clock-controller@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clock"; + reg = <0xd0018700 0xa0>, <0x1c054 0x10>; + clocks = <&coreclk 1>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,berlin.txt b/Documentation/devicetree/bindings/clock/marvell,berlin.txt deleted file mode 100644 index c611c495f3ff..000000000000 --- a/Documentation/devicetree/bindings/clock/marvell,berlin.txt +++ /dev/null @@ -1,31 +0,0 @@ -Device Tree Clock bindings for Marvell Berlin - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Clock related registers are spread among the chip control registers. Berlin -clock node should be a sub-node of the chip controller node. Marvell Berlin2 -(BG2, BG2CD, BG2Q) SoCs share the same IP for PLLs and clocks, with some -minor differences in features and register layout. - -Required properties: -- compatible: must be "marvell,berlin2-clk" or "marvell,berlin2q-clk" -- #clock-cells: must be 1 -- clocks: must be the input parent clock phandle -- clock-names: name of the input parent clock - Allowed clock-names for the reference clocks are - "refclk" for the SoCs oscillator input on all SoCs, - and SoC-specific input clocks for - BG2/BG2CD: "video_ext0" for the external video clock input - - -Example: - -chip_clk: clock { - compatible = "marvell,berlin2q-clk"; - - #clock-cells = <1>; - clocks = <&refclk>; - clock-names = "refclk"; -}; diff --git a/Documentation/devicetree/bindings/clock/marvell,berlin2-clk.yaml b/Documentation/devicetree/bindings/clock/marvell,berlin2-clk.yaml new file mode 100644 index 000000000000..8d48a2c7e381 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,berlin2-clk.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/marvell,berlin2-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Berlin Clock Controller + +maintainers: + - Jisheng Zhang + +description: + Clock related registers are spread among the chip control registers. Berlin + clock node should be a sub-node of the chip controller node. Marvell Berlin2 + (BG2, BG2CD, BG2Q) SoCs share the same IP for PLLs and clocks, with some minor + differences in features and register layout. + +properties: + compatible: + enum: + - marvell,berlin2-clk + - marvell,berlin2q-clk + + '#clock-cells': + const: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - enum: + - refclk + - video_ext0 + +required: + - compatible + - '#clock-cells' + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + clock-controller { + compatible = "marvell,berlin2q-clk"; + #clock-cells = <1>; + clocks = <&refclk>; + clock-names = "refclk"; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,dove-divider-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,dove-divider-clock.yaml new file mode 100644 index 000000000000..7a8e0e281b63 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,dove-divider-clock.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/marvell,dove-divider-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Dove PLL Divider Clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: > + Marvell Dove has a 2GHz PLL, which feeds into a set of dividers to provide + high speed clocks for a number of peripherals. These dividers are part of the + PMU, and thus this node should be a child of the PMU node. + + The following clocks are provided: + + ID Clock + ------------- + 0 AXI bus clock + 1 GPU clock + 2 VMeta clock + 3 LCD clock + +properties: + compatible: + const: marvell,dove-divider-clock + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@64 { + compatible = "marvell,dove-divider-clock"; + reg = <0x0064 0x8>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,mvebu-core-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,mvebu-core-clock.yaml new file mode 100644 index 000000000000..215bcd9080c3 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,mvebu-core-clock.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/marvell,mvebu-core-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell MVEBU SoC core clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: > + Marvell MVEBU SoCs usually allow to determine core clock frequencies by + reading the Sample-At-Reset (SAR) register. The core clock consumer should + specify the desired clock by having the clock ID in its "clocks" phandle cell. + + The following is a list of provided IDs and clock names on Armada 370/XP: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = nbclk (L2 Cache clock) + 3 = hclk (DRAM control clock) + 4 = dramclk (DDR clock) + + The following is a list of provided IDs and clock names on Armada 375: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = l2clk (L2 Cache clock) + 3 = ddrclk (DDR clock) + + The following is a list of provided IDs and clock names on Armada 380/385: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = l2clk (L2 Cache clock) + 3 = ddrclk (DDR clock) + + The following is a list of provided IDs and clock names on Armada 39x: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = nbclk (Coherent Fabric clock) + 3 = hclk (SDRAM Controller Internal Clock) + 4 = dclk (SDRAM Interface Clock) + 5 = refclk (Reference Clock) + + The following is a list of provided IDs and clock names on 98dx3236: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = ddrclk (DDR clock) + 3 = mpll (MPLL Clock) + + The following is a list of provided IDs and clock names on Kirkwood and Dove: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = l2clk (L2 Cache clock derived from CPU0 clock) + 3 = ddrclk (DDR controller clock derived from CPU0 clock) + + The following is a list of provided IDs and clock names on Orion5x: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = ddrclk (DDR controller clock derived from CPU0 clock) + +properties: + compatible: + enum: + - marvell,armada-370-core-clock + - marvell,armada-375-core-clock + - marvell,armada-380-core-clock + - marvell,armada-390-core-clock + - marvell,armada-xp-core-clock + - marvell,dove-core-clock + - marvell,kirkwood-core-clock + - marvell,mv88f5181-core-clock + - marvell,mv88f5182-core-clock + - marvell,mv88f5281-core-clock + - marvell,mv88f6180-core-clock + - marvell,mv88f6183-core-clock + - marvell,mv98dx1135-core-clock + - marvell,mv98dx3236-core-clock + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clock-output-names: + description: Overwrite default clock output names. + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/marvell-armada-370-gating-clock.yaml b/Documentation/devicetree/bindings/clock/marvell-armada-370-gating-clock.yaml new file mode 100644 index 000000000000..0475360d2b6a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell-armada-370-gating-clock.yaml @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +--- +$id: http://devicetree.org/schemas/clock/marvell-armada-370-gating-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell EBU SoC gating-clock + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: > + Marvell Armada 370/375/380/385/39x/XP, Dove and Kirkwood allow some peripheral + clocks to be gated to save some power. The clock ID is directly mapped to the + corresponding clock gating control bit in HW to ease manual clock lookup in + datasheet. + + The following is a list of provided IDs for Armada 370: + + ID Clock Peripheral + ----------------------------------- + 0 Audio AC97 Cntrl + 1 pex0_en PCIe 0 Clock out + 2 pex1_en PCIe 1 Clock out + 3 ge1 Gigabit Ethernet 1 + 4 ge0 Gigabit Ethernet 0 + 5 pex0 PCIe Cntrl 0 + 9 pex1 PCIe Cntrl 1 + 15 sata0 SATA Host 0 + 17 sdio SDHCI Host + 23 crypto CESA (crypto engine) + 25 tdm Time Division Mplx + 28 ddr DDR Cntrl + 30 sata1 SATA Host 0 + + The following is a list of provided IDs for Armada 375: + + ID Clock Peripheral + ----------------------------------- + 2 mu Management Unit + 3 pp Packet Processor + 4 ptp PTP + 5 pex0 PCIe 0 Clock out + 6 pex1 PCIe 1 Clock out + 8 audio Audio Cntrl + 11 nd_clk Nand Flash Cntrl + 14 sata0_link SATA 0 Link + 15 sata0_core SATA 0 Core + 16 usb3 USB3 Host + 17 sdio SDHCI Host + 18 usb USB Host + 19 gop Gigabit Ethernet MAC + 20 sata1_link SATA 1 Link + 21 sata1_core SATA 1 Core + 22 xor0 XOR DMA 0 + 23 xor1 XOR DMA 0 + 24 copro Coprocessor + 25 tdm Time Division Mplx + 28 crypto0_enc Cryptographic Unit Port 0 Encryption + 29 crypto0_core Cryptographic Unit Port 0 Core + 30 crypto1_enc Cryptographic Unit Port 1 Encryption + 31 crypto1_core Cryptographic Unit Port 1 Core + + The following is a list of provided IDs for Armada 380/385: + + ID Clock Peripheral + ----------------------------------- + 0 audio Audio + 2 ge2 Gigabit Ethernet 2 + 3 ge1 Gigabit Ethernet 1 + 4 ge0 Gigabit Ethernet 0 + 5 pex1 PCIe 1 + 6 pex2 PCIe 2 + 7 pex3 PCIe 3 + 8 pex0 PCIe 0 + 9 usb3h0 USB3 Host 0 + 10 usb3h1 USB3 Host 1 + 11 usb3d USB3 Device + 13 bm Buffer Management + 14 crypto0z Cryptographic 0 Z + 15 sata0 SATA 0 + 16 crypto1z Cryptographic 1 Z + 17 sdio SDIO + 18 usb2 USB 2 + 21 crypto1 Cryptographic 1 + 22 xor0 XOR 0 + 23 crypto0 Cryptographic 0 + 25 tdm Time Division Multiplexing + 28 xor1 XOR 1 + 30 sata1 SATA 1 + + The following is a list of provided IDs for Armada 39x: + + ID Clock Peripheral + ----------------------------------- + 5 pex1 PCIe 1 + 6 pex2 PCIe 2 + 7 pex3 PCIe 3 + 8 pex0 PCIe 0 + 9 usb3h0 USB3 Host 0 + 10 usb3h1 USB3 Host 1 + 15 sata0 SATA 0 + 17 sdio SDIO + 22 xor0 XOR 0 + 28 xor1 XOR 1 + + The following is a list of provided IDs for Armada XP: + + ID Clock Peripheral + ----------------------------------- + 0 audio Audio Cntrl + 1 ge3 Gigabit Ethernet 3 + 2 ge2 Gigabit Ethernet 2 + 3 ge1 Gigabit Ethernet 1 + 4 ge0 Gigabit Ethernet 0 + 5 pex0 PCIe Cntrl 0 + 6 pex1 PCIe Cntrl 1 + 7 pex2 PCIe Cntrl 2 + 8 pex3 PCIe Cntrl 3 + 13 bp + 14 sata0lnk + 15 sata0 SATA Host 0 + 16 lcd LCD Cntrl + 17 sdio SDHCI Host + 18 usb0 USB Host 0 + 19 usb1 USB Host 1 + 20 usb2 USB Host 2 + 22 xor0 XOR DMA 0 + 23 crypto CESA engine + 25 tdm Time Division Mplx + 28 xor1 XOR DMA 1 + 29 sata1lnk + 30 sata1 SATA Host 1 + + The following is a list of provided IDs for 98dx3236: + + ID Clock Peripheral + ----------------------------------- + 3 ge1 Gigabit Ethernet 1 + 4 ge0 Gigabit Ethernet 0 + 5 pex0 PCIe Cntrl 0 + 17 sdio SDHCI Host + 18 usb0 USB Host 0 + 22 xor0 XOR DMA 0 + + The following is a list of provided IDs for Dove: + + ID Clock Peripheral + ----------------------------------- + 0 usb0 USB Host 0 + 1 usb1 USB Host 1 + 2 ge Gigabit Ethernet + 3 sata SATA Host + 4 pex0 PCIe Cntrl 0 + 5 pex1 PCIe Cntrl 1 + 8 sdio0 SDHCI Host 0 + 9 sdio1 SDHCI Host 1 + 10 nand NAND Cntrl + 11 camera Camera Cntrl + 12 i2s0 I2S Cntrl 0 + 13 i2s1 I2S Cntrl 1 + 15 crypto CESA engine + 21 ac97 AC97 Cntrl + 22 pdma Peripheral DMA + 23 xor0 XOR DMA 0 + 24 xor1 XOR DMA 1 + 30 gephy Gigabit Ethernet PHY + Note: gephy(30) is implemented as a parent clock of ge(2) + + The following is a list of provided IDs for Kirkwood: + + ID Clock Peripheral + ----------------------------------- + 0 ge0 Gigabit Ethernet 0 + 2 pex0 PCIe Cntrl 0 + 3 usb0 USB Host 0 + 4 sdio SDIO Cntrl + 5 tsu Transp. Stream Unit + 6 dunit SDRAM Cntrl + 7 runit Runit + 8 xor0 XOR DMA 0 + 9 audio I2S Cntrl 0 + 14 sata0 SATA Host 0 + 15 sata1 SATA Host 1 + 16 xor1 XOR DMA 1 + 17 crypto CESA engine + 18 pex1 PCIe Cntrl 1 + 19 ge1 Gigabit Ethernet 1 + 20 tdm Time Division Mplx + +properties: + compatible: + enum: + - marvell,armada-370-gating-clock + - marvell,armada-375-gating-clock + - marvell,armada-380-gating-clock + - marvell,armada-390-gating-clock + - marvell,armada-xp-gating-clock + - marvell,mv98dx3236-gating-clock + - marvell,dove-gating-clock + - marvell,kirkwood-gating-clock + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + /* default parent clock is tclk */ + clocks = <&core_clk 0>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max9485.txt b/Documentation/devicetree/bindings/clock/maxim,max9485.txt deleted file mode 100644 index b8f5c3bbf12b..000000000000 --- a/Documentation/devicetree/bindings/clock/maxim,max9485.txt +++ /dev/null @@ -1,59 +0,0 @@ -Devicetree bindings for Maxim MAX9485 Programmable Audio Clock Generator - -This device exposes 4 clocks in total: - -- MAX9485_MCLKOUT: A gated, buffered output of the input clock of 27 MHz -- MAX9485_CLKOUT: A PLL that can be configured to 16 different discrete - frequencies -- MAX9485_CLKOUT[1,2]: Two gated outputs for MAX9485_CLKOUT - -MAX9485_CLKOUT[1,2] are children of MAX9485_CLKOUT which upchain all rate set -requests. - -Required properties: -- compatible: "maxim,max9485" -- clocks: Input clock, must provide 27.000 MHz -- clock-names: Must be set to "xclk" -- #clock-cells: From common clock binding; shall be set to 1 - -Optional properties: -- reset-gpios: GPIO descriptor connected to the #RESET input pin -- vdd-supply: A regulator node for Vdd -- clock-output-names: Name of output clocks, as defined in common clock - bindings - -If not explicitly set, the output names are "mclkout", "clkout", "clkout1" -and "clkout2". - -Clocks are defined as preprocessor macros in the dt-binding header. - -Example: - - #include - - xo-27mhz: xo-27mhz { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <27000000>; - }; - - &i2c0 { - max9485: audio-clock@63 { - reg = <0x63>; - compatible = "maxim,max9485"; - clock-names = "xclk"; - clocks = <&xo-27mhz>; - reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; - vdd-supply = <&3v3-reg>; - #clock-cells = <1>; - }; - }; - - // Clock consumer node - - foo@0 { - compatible = "bar,foo"; - /* ... */ - clock-names = "foo-input-clk"; - clocks = <&max9485 MAX9485_CLKOUT1>; - }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max9485.yaml b/Documentation/devicetree/bindings/clock/maxim,max9485.yaml new file mode 100644 index 000000000000..f9d8941c7235 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/maxim,max9485.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/maxim,max9485.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX9485 Programmable Audio Clock Generator + +maintainers: + - Daniel Mack + +description: > + Maxim MAX9485 Programmable Audio Clock Generator exposes 4 clocks in total: + + - MAX9485_MCLKOUT: A gated, buffered output of the input clock of 27 MHz + - MAX9485_CLKOUT: A PLL that can be configured to 16 different discrete + frequencies + - MAX9485_CLKOUT[1,2]: Two gated outputs for MAX9485_CLKOUT + + MAX9485_CLKOUT[1,2] are children of MAX9485_CLKOUT which upchain all rate set + requests. + +properties: + compatible: + const: maxim,max9485 + + reg: + maxItems: 1 + + clocks: + description: Input clock. Must provide 27 MHz + maxItems: 1 + + clock-names: + items: + - const: xclk + + '#clock-cells': + const: 1 + + reset-gpios: + description: > + GPIO descriptor connected to the #RESET input pin + + vdd-supply: + description: A regulator node for Vdd + + clock-output-names: + description: Name of output clocks, as defined in common clock bindings + items: + - const: mclkout + - const: clkout + - const: clkout1 + - const: clkout2 + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + clock-controller@63 { + compatible = "maxim,max9485"; + reg = <0x63>; + #clock-cells = <1>; + clock-names = "xclk"; + clocks = <&xo_27mhz>; + reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + vdd-supply = <®_3v3>; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8188-clock.yaml b/Documentation/devicetree/bindings/clock/mediatek,mt8188-clock.yaml index 2985c8c717d7..5403242545ab 100644 --- a/Documentation/devicetree/bindings/clock/mediatek,mt8188-clock.yaml +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8188-clock.yaml @@ -52,6 +52,9 @@ properties: '#clock-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/clock/mediatek,mtmips-sysc.yaml b/Documentation/devicetree/bindings/clock/mediatek,mtmips-sysc.yaml index 83c1803ffd16..56bbd69b16d9 100644 --- a/Documentation/devicetree/bindings/clock/mediatek,mtmips-sysc.yaml +++ b/Documentation/devicetree/bindings/clock/mediatek,mtmips-sysc.yaml @@ -26,18 +26,22 @@ description: | properties: compatible: - items: - - enum: - - ralink,mt7620-sysc - - ralink,mt7628-sysc - - ralink,mt7688-sysc - - ralink,rt2880-sysc - - ralink,rt3050-sysc - - ralink,rt3052-sysc - - ralink,rt3352-sysc - - ralink,rt3883-sysc - - ralink,rt5350-sysc - - const: syscon + oneOf: + - items: + - enum: + - ralink,mt7620-sysc + - ralink,mt7688-sysc + - ralink,rt2880-sysc + - ralink,rt3050-sysc + - ralink,rt3052-sysc + - ralink,rt3352-sysc + - ralink,rt3883-sysc + - ralink,rt5350-sysc + - const: syscon + - items: + - const: ralink,mt7628-sysc + - const: ralink,mt7688-sysc + - const: syscon reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/clock/microchip,pic32.txt b/Documentation/devicetree/bindings/clock/microchip,pic32.txt deleted file mode 100644 index c93d88fdd858..000000000000 --- a/Documentation/devicetree/bindings/clock/microchip,pic32.txt +++ /dev/null @@ -1,39 +0,0 @@ -Microchip PIC32 Clock Controller Binding ----------------------------------------- -Microchip clock controller is consists of few oscillators, PLL, multiplexer -and few divider modules. - -This binding uses common clock bindings. -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible: shall be "microchip,pic32mzda-clk". -- reg: shall contain base address and length of clock registers. -- #clock-cells: shall be 1. - -Optional properties: -- microchip,pic32mzda-sosc: shall be added only if platform has - secondary oscillator connected. - -Example: - rootclk: clock-controller@1f801200 { - compatible = "microchip,pic32mzda-clk"; - reg = <0x1f801200 0x200>; - #clock-cells = <1>; - /* optional */ - microchip,pic32mzda-sosc; - }; - - -The clock consumer shall specify the desired clock-output of the clock -controller (as defined in [2]) by specifying output-id in its "clock" -phandle cell. -[2] include/dt-bindings/clock/microchip,pic32-clock.h - -For example for UART2: -uart2: serial@2 { - compatible = "microchip,pic32mzda-uart"; - reg = <>; - interrupts = <>; - clocks = <&rootclk PB2CLK>; -}; diff --git a/Documentation/devicetree/bindings/clock/microchip,pic32mzda-clk.yaml b/Documentation/devicetree/bindings/clock/microchip,pic32mzda-clk.yaml new file mode 100644 index 000000000000..a14a838140f1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/microchip,pic32mzda-clk.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/microchip,pic32mzda-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PIC32MZDA Clock Controller + +maintainers: + - Purna Chandra Mandal + +description: + Microchip clock controller consists of a few oscillators, PLL, multiplexer + and divider modules. + +properties: + compatible: + const: microchip,pic32mzda-clk + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + microchip,pic32mzda-sosc: + description: Presence of secondary oscillator. + type: boolean + +required: + - compatible + - reg + - "#clock-cells" + +additionalProperties: false + +examples: + - | + clock-controller@1f801200 { + compatible = "microchip,pic32mzda-clk"; + reg = <0x1f801200 0x200>; + #clock-cells = <1>; + /* optional */ + microchip,pic32mzda-sosc; + }; diff --git a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt deleted file mode 100644 index fedea84314a1..000000000000 --- a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt +++ /dev/null @@ -1,48 +0,0 @@ -Device Tree Clock bindings for arch-moxart - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -MOXA ART SoCs allow to determine PLL output and APB frequencies -by reading registers holding multiplier and divisor information. - - -PLL: - -Required properties: -- compatible : Must be "moxa,moxart-pll-clock" -- #clock-cells : Should be 0 -- reg : Should contain registers location and length -- clocks : Should contain phandle + clock-specifier for the parent clock - -Optional properties: -- clock-output-names : Should contain clock name - - -APB: - -Required properties: -- compatible : Must be "moxa,moxart-apb-clock" -- #clock-cells : Should be 0 -- reg : Should contain registers location and length -- clocks : Should contain phandle + clock-specifier for the parent clock - -Optional properties: -- clock-output-names : Should contain clock name - - -For example: - - clk_pll: clk_pll@98100000 { - compatible = "moxa,moxart-pll-clock"; - #clock-cells = <0>; - reg = <0x98100000 0x34>; - }; - - clk_apb: clk_apb@98100000 { - compatible = "moxa,moxart-apb-clock"; - #clock-cells = <0>; - reg = <0x98100000 0x34>; - clocks = <&clk_pll>; - }; diff --git a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.yaml b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.yaml new file mode 100644 index 000000000000..bcf7cc240eba --- /dev/null +++ b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/moxa,moxart-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MOXA ART Clock Controllers + +maintainers: + - Krzysztof Kozlowski + +description: + MOXA ART SoCs allow to determine PLL output and APB frequencies by reading + registers holding multiplier and divisor information. + +properties: + compatible: + enum: + - moxa,moxart-apb-clock + - moxa,moxart-pll-clock + + "#clock-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-output-names: true + +additionalProperties: false + +required: + - compatible + - "#clock-cells" + - reg diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt deleted file mode 100644 index d8f5c490f893..000000000000 --- a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt +++ /dev/null @@ -1,87 +0,0 @@ -* Core Clock bindings for Marvell MVEBU SoCs - -Marvell MVEBU SoCs usually allow to determine core clock frequencies by -reading the Sample-At-Reset (SAR) register. The core clock consumer should -specify the desired clock by having the clock ID in its "clocks" phandle cell. - -The following is a list of provided IDs and clock names on Armada 370/XP: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU clock) - 2 = nbclk (L2 Cache clock) - 3 = hclk (DRAM control clock) - 4 = dramclk (DDR clock) - -The following is a list of provided IDs and clock names on Armada 375: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU clock) - 2 = l2clk (L2 Cache clock) - 3 = ddrclk (DDR clock) - -The following is a list of provided IDs and clock names on Armada 380/385: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU clock) - 2 = l2clk (L2 Cache clock) - 3 = ddrclk (DDR clock) - -The following is a list of provided IDs and clock names on Armada 39x: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU clock) - 2 = nbclk (Coherent Fabric clock) - 3 = hclk (SDRAM Controller Internal Clock) - 4 = dclk (SDRAM Interface Clock) - 5 = refclk (Reference Clock) - -The following is a list of provided IDs and clock names on 98dx3236: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU clock) - 2 = ddrclk (DDR clock) - 3 = mpll (MPLL Clock) - -The following is a list of provided IDs and clock names on Kirkwood and Dove: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU0 clock) - 2 = l2clk (L2 Cache clock derived from CPU0 clock) - 3 = ddrclk (DDR controller clock derived from CPU0 clock) - -The following is a list of provided IDs and clock names on Orion5x: - 0 = tclk (Internal Bus clock) - 1 = cpuclk (CPU0 clock) - 2 = ddrclk (DDR controller clock derived from CPU0 clock) - -Required properties: -- compatible : shall be one of the following: - "marvell,armada-370-core-clock" - For Armada 370 SoC core clocks - "marvell,armada-375-core-clock" - For Armada 375 SoC core clocks - "marvell,armada-380-core-clock" - For Armada 380/385 SoC core clocks - "marvell,armada-390-core-clock" - For Armada 39x SoC core clocks - "marvell,armada-xp-core-clock" - For Armada XP SoC core clocks - "marvell,mv98dx3236-core-clock" - For 98dx3236 family SoC core clocks - "marvell,dove-core-clock" - for Dove SoC core clocks - "marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180) - "marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC - "marvell,mv98dx1135-core-clock" - for Kirkwood 98dx1135 SoC - "marvell,mv88f5181-core-clock" - for Orion MV88F5181 SoC - "marvell,mv88f5182-core-clock" - for Orion MV88F5182 SoC - "marvell,mv88f5281-core-clock" - for Orion MV88F5281 SoC - "marvell,mv88f6183-core-clock" - for Orion MV88F6183 SoC -- reg : shall be the register address of the Sample-At-Reset (SAR) register -- #clock-cells : from common clock binding; shall be set to 1 - -Optional properties: -- clock-output-names : from common clock binding; allows overwrite default clock - output names ("tclk", "cpuclk", "l2clk", "ddrclk") - -Example: - -core_clk: core-clocks@d0214 { - compatible = "marvell,dove-core-clock"; - reg = <0xd0214 0x4>; - #clock-cells = <1>; -}; - -spi0: spi@10600 { - compatible = "marvell,orion-spi"; - /* ... */ - /* get tclk from core clock provider */ - clocks = <&core_clk 0>; -}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt deleted file mode 100644 index c7b4e3a6b2c6..000000000000 --- a/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt +++ /dev/null @@ -1,23 +0,0 @@ -* Core Divider Clock bindings for Marvell MVEBU SoCs - -The following is a list of provided IDs and clock names on Armada 370/XP: - 0 = nand (NAND clock) - -Required properties: -- compatible : must be "marvell,armada-370-corediv-clock", - "marvell,armada-375-corediv-clock", - "marvell,armada-380-corediv-clock", - "marvell,mv98dx3236-corediv-clock", - -- reg : must be the register address of Core Divider control register -- #clock-cells : from common clock binding; shall be set to 1 -- clocks : must be set to the parent's phandle - -Example: - -corediv_clk: corediv-clocks@18740 { - compatible = "marvell,armada-370-corediv-clock"; - reg = <0x18740 0xc>; - #clock-cells = <1>; - clocks = <&pll>; -}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt deleted file mode 100644 index 7f28506eaee7..000000000000 --- a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt +++ /dev/null @@ -1,23 +0,0 @@ -Device Tree Clock bindings for cpu clock of Marvell EBU platforms - -Required properties: -- compatible : shall be one of the following: - "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP - "marvell,mv98dx3236-cpu-clock" - cpu clocks for 98DX3236 SoC -- reg : Address and length of the clock complex register set, followed - by address and length of the PMU DFS registers -- #clock-cells : should be set to 1. -- clocks : shall be the input parent clock phandle for the clock. - -cpuclk: clock-complex@d0018700 { - #clock-cells = <1>; - compatible = "marvell,armada-xp-cpu-clock"; - reg = <0xd0018700 0xA0>, <0x1c054 0x10>; - clocks = <&coreclk 1>; -} - -cpu@0 { - compatible = "marvell,sheeva-v7"; - reg = <0>; - clocks = <&cpuclk 0>; -}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt deleted file mode 100644 index de562da2ae77..000000000000 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ /dev/null @@ -1,205 +0,0 @@ -* Gated Clock bindings for Marvell EBU SoCs - -Marvell Armada 370/375/380/385/39x/XP, Dove and Kirkwood allow some -peripheral clocks to be gated to save some power. The clock consumer -should specify the desired clock by having the clock ID in its -"clocks" phandle cell. The clock ID is directly mapped to the -corresponding clock gating control bit in HW to ease manual clock -lookup in datasheet. - -The following is a list of provided IDs for Armada 370: -ID Clock Peripheral ------------------------------------ -0 Audio AC97 Cntrl -1 pex0_en PCIe 0 Clock out -2 pex1_en PCIe 1 Clock out -3 ge1 Gigabit Ethernet 1 -4 ge0 Gigabit Ethernet 0 -5 pex0 PCIe Cntrl 0 -9 pex1 PCIe Cntrl 1 -15 sata0 SATA Host 0 -17 sdio SDHCI Host -23 crypto CESA (crypto engine) -25 tdm Time Division Mplx -28 ddr DDR Cntrl -30 sata1 SATA Host 0 - -The following is a list of provided IDs for Armada 375: -ID Clock Peripheral ------------------------------------ -2 mu Management Unit -3 pp Packet Processor -4 ptp PTP -5 pex0 PCIe 0 Clock out -6 pex1 PCIe 1 Clock out -8 audio Audio Cntrl -11 nd_clk Nand Flash Cntrl -14 sata0_link SATA 0 Link -15 sata0_core SATA 0 Core -16 usb3 USB3 Host -17 sdio SDHCI Host -18 usb USB Host -19 gop Gigabit Ethernet MAC -20 sata1_link SATA 1 Link -21 sata1_core SATA 1 Core -22 xor0 XOR DMA 0 -23 xor1 XOR DMA 0 -24 copro Coprocessor -25 tdm Time Division Mplx -28 crypto0_enc Cryptographic Unit Port 0 Encryption -29 crypto0_core Cryptographic Unit Port 0 Core -30 crypto1_enc Cryptographic Unit Port 1 Encryption -31 crypto1_core Cryptographic Unit Port 1 Core - -The following is a list of provided IDs for Armada 380/385: -ID Clock Peripheral ------------------------------------ -0 audio Audio -2 ge2 Gigabit Ethernet 2 -3 ge1 Gigabit Ethernet 1 -4 ge0 Gigabit Ethernet 0 -5 pex1 PCIe 1 -6 pex2 PCIe 2 -7 pex3 PCIe 3 -8 pex0 PCIe 0 -9 usb3h0 USB3 Host 0 -10 usb3h1 USB3 Host 1 -11 usb3d USB3 Device -13 bm Buffer Management -14 crypto0z Cryptographic 0 Z -15 sata0 SATA 0 -16 crypto1z Cryptographic 1 Z -17 sdio SDIO -18 usb2 USB 2 -21 crypto1 Cryptographic 1 -22 xor0 XOR 0 -23 crypto0 Cryptographic 0 -25 tdm Time Division Multiplexing -28 xor1 XOR 1 -30 sata1 SATA 1 - -The following is a list of provided IDs for Armada 39x: -ID Clock Peripheral ------------------------------------ -5 pex1 PCIe 1 -6 pex2 PCIe 2 -7 pex3 PCIe 3 -8 pex0 PCIe 0 -9 usb3h0 USB3 Host 0 -10 usb3h1 USB3 Host 1 -15 sata0 SATA 0 -17 sdio SDIO -22 xor0 XOR 0 -28 xor1 XOR 1 - -The following is a list of provided IDs for Armada XP: -ID Clock Peripheral ------------------------------------ -0 audio Audio Cntrl -1 ge3 Gigabit Ethernet 3 -2 ge2 Gigabit Ethernet 2 -3 ge1 Gigabit Ethernet 1 -4 ge0 Gigabit Ethernet 0 -5 pex0 PCIe Cntrl 0 -6 pex1 PCIe Cntrl 1 -7 pex2 PCIe Cntrl 2 -8 pex3 PCIe Cntrl 3 -13 bp -14 sata0lnk -15 sata0 SATA Host 0 -16 lcd LCD Cntrl -17 sdio SDHCI Host -18 usb0 USB Host 0 -19 usb1 USB Host 1 -20 usb2 USB Host 2 -22 xor0 XOR DMA 0 -23 crypto CESA engine -25 tdm Time Division Mplx -28 xor1 XOR DMA 1 -29 sata1lnk -30 sata1 SATA Host 1 - -The following is a list of provided IDs for 98dx3236: -ID Clock Peripheral ------------------------------------ -3 ge1 Gigabit Ethernet 1 -4 ge0 Gigabit Ethernet 0 -5 pex0 PCIe Cntrl 0 -17 sdio SDHCI Host -18 usb0 USB Host 0 -22 xor0 XOR DMA 0 - -The following is a list of provided IDs for Dove: -ID Clock Peripheral ------------------------------------ -0 usb0 USB Host 0 -1 usb1 USB Host 1 -2 ge Gigabit Ethernet -3 sata SATA Host -4 pex0 PCIe Cntrl 0 -5 pex1 PCIe Cntrl 1 -8 sdio0 SDHCI Host 0 -9 sdio1 SDHCI Host 1 -10 nand NAND Cntrl -11 camera Camera Cntrl -12 i2s0 I2S Cntrl 0 -13 i2s1 I2S Cntrl 1 -15 crypto CESA engine -21 ac97 AC97 Cntrl -22 pdma Peripheral DMA -23 xor0 XOR DMA 0 -24 xor1 XOR DMA 1 -30 gephy Gigabit Ethernel PHY -Note: gephy(30) is implemented as a parent clock of ge(2) - -The following is a list of provided IDs for Kirkwood: -ID Clock Peripheral ------------------------------------ -0 ge0 Gigabit Ethernet 0 -2 pex0 PCIe Cntrl 0 -3 usb0 USB Host 0 -4 sdio SDIO Cntrl -5 tsu Transp. Stream Unit -6 dunit SDRAM Cntrl -7 runit Runit -8 xor0 XOR DMA 0 -9 audio I2S Cntrl 0 -14 sata0 SATA Host 0 -15 sata1 SATA Host 1 -16 xor1 XOR DMA 1 -17 crypto CESA engine -18 pex1 PCIe Cntrl 1 -19 ge1 Gigabit Ethernet 1 -20 tdm Time Division Mplx - -Required properties: -- compatible : shall be one of the following: - "marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating - "marvell,armada-375-gating-clock" - for Armada 375 SoC clock gating - "marvell,armada-380-gating-clock" - for Armada 380/385 SoC clock gating - "marvell,armada-390-gating-clock" - for Armada 39x SoC clock gating - "marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating - "marvell,mv98dx3236-gating-clock" - for 98dx3236 SoC clock gating - "marvell,dove-gating-clock" - for Dove SoC clock gating - "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating -- reg : shall be the register address of the Clock Gating Control register -- #clock-cells : from common clock binding; shall be set to 1 - -Optional properties: -- clocks : default parent clock phandle (e.g. tclk) - -Example: - -gate_clk: clock-gating-control@d0038 { - compatible = "marvell,dove-gating-clock"; - reg = <0xd0038 0x4>; - /* default parent clock is tclk */ - clocks = <&core_clk 0>; - #clock-cells = <1>; -}; - -sdio0: sdio@92000 { - compatible = "marvell,dove-sdhci"; - /* get clk gate bit 8 (sdio0) */ - clocks = <&gate_clk 8>; -}; diff --git a/Documentation/devicetree/bindings/clock/nspire-clock.txt b/Documentation/devicetree/bindings/clock/nspire-clock.txt deleted file mode 100644 index 7c3bc8bb5b9f..000000000000 --- a/Documentation/devicetree/bindings/clock/nspire-clock.txt +++ /dev/null @@ -1,24 +0,0 @@ -TI-NSPIRE Clocks - -Required properties: -- compatible: Valid compatible properties include: - "lsi,nspire-cx-ahb-divider" for the AHB divider in the CX model - "lsi,nspire-classic-ahb-divider" for the AHB divider in the older model - "lsi,nspire-cx-clock" for the base clock in the CX model - "lsi,nspire-classic-clock" for the base clock in the older model - -- reg: Physical base address of the controller and length of memory mapped - region. - -Optional: -- clocks: For the "nspire-*-ahb-divider" compatible clocks, this is the parent - clock where it divides the rate from. - -Example: - -ahb_clk { - #clock-cells = <0>; - compatible = "lsi,nspire-cx-clock"; - reg = <0x900B0000 0x4>; - clocks = <&base_clk>; -}; diff --git a/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt b/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt deleted file mode 100644 index f82064546d11..000000000000 --- a/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt +++ /dev/null @@ -1,100 +0,0 @@ -* Nuvoton NPCM7XX Clock Controller - -Nuvoton Poleg BMC NPCM7XX contains an integrated clock controller, which -generates and supplies clocks to all modules within the BMC. - -External clocks: - -There are six fixed clocks that are generated outside the BMC. All clocks are of -a known fixed value that cannot be changed. clk_refclk, clk_mcbypck and -clk_sysbypck are inputs to the clock controller. -clk_rg1refck, clk_rg2refck and clk_xin are external clocks suppling the -network. They are set on the device tree, but not used by the clock module. The -network devices use them directly. -Example can be found below. - -All available clocks are defined as preprocessor macros in: -dt-bindings/clock/nuvoton,npcm7xx-clock.h -and can be reused as DT sources. - -Required Properties of clock controller: - - - compatible: "nuvoton,npcm750-clk" : for clock controller of Nuvoton - Poleg BMC NPCM750 - - - reg: physical base address of the clock controller and length of - memory mapped region. - - - #clock-cells: should be 1. - -Example: Clock controller node: - - clk: clock-controller@f0801000 { - compatible = "nuvoton,npcm750-clk"; - #clock-cells = <1>; - reg = <0xf0801000 0x1000>; - clock-names = "refclk", "sysbypck", "mcbypck"; - clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>; - }; - -Example: Required external clocks for network: - - /* external reference clock */ - clk_refclk: clk-refclk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <25000000>; - clock-output-names = "refclk"; - }; - - /* external reference clock for cpu. float in normal operation */ - clk_sysbypck: clk-sysbypck { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <800000000>; - clock-output-names = "sysbypck"; - }; - - /* external reference clock for MC. float in normal operation */ - clk_mcbypck: clk-mcbypck { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <800000000>; - clock-output-names = "mcbypck"; - }; - - /* external clock signal rg1refck, supplied by the phy */ - clk_rg1refck: clk-rg1refck { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <125000000>; - clock-output-names = "clk_rg1refck"; - }; - - /* external clock signal rg2refck, supplied by the phy */ - clk_rg2refck: clk-rg2refck { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <125000000>; - clock-output-names = "clk_rg2refck"; - }; - - clk_xin: clk-xin { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <50000000>; - clock-output-names = "clk_xin"; - }; - - -Example: GMAC controller node that consumes two clocks: a generated clk by the -clock controller and a fixed clock from DT (clk_rg1refck). - - ethernet0: ethernet@f0802000 { - compatible = "snps,dwmac"; - reg = <0xf0802000 0x2000>; - interrupts = <0 14 4>; - interrupt-names = "macirq"; - clocks = <&clk_rg1refck>, <&clk NPCM7XX_CLK_AHB>; - clock-names = "stmmaceth", "clk_gmac"; - }; diff --git a/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.yaml new file mode 100644 index 000000000000..694dac68619c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/nuvoton,npcm750-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton NPCM7XX Clock Controller + +maintainers: + - Tali Perry + +description: > + Nuvoton Poleg BMC NPCM7XX contains an integrated clock controller, which + generates and supplies clocks to all modules within the BMC. + + External clocks: + + There are six fixed clocks that are generated outside the BMC. All clocks are of + a known fixed value that cannot be changed. clk_refclk, clk_mcbypck and + clk_sysbypck are inputs to the clock controller. + clk_rg1refck, clk_rg2refck and clk_xin are external clocks suppling the + network. They are set on the device tree, but not used by the clock module. The + network devices use them directly. + + All available clocks are defined as preprocessor macros in: + dt-bindings/clock/nuvoton,npcm7xx-clock.h + and can be reused as DT sources. + +properties: + compatible: + const: nuvoton,npcm750-clk + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clock-names: + items: + - const: refclk + - const: sysbypck + - const: mcbypck + + clocks: + items: + - description: refclk + - description: sysbypck + - description: mcbypck + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@f0801000 { + compatible = "nuvoton,npcm750-clk"; + #clock-cells = <1>; + reg = <0xf0801000 0x1000>; + clock-names = "refclk", "sysbypck", "mcbypck"; + clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>; + }; diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml index d0291bfff23a..27403b4c52d6 100644 --- a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml +++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml @@ -13,6 +13,8 @@ properties: compatible: items: - enum: + - nxp,imx94-display-csr + - nxp,imx94-lvds-csr - nxp,imx95-camera-csr - nxp,imx95-display-csr - nxp,imx95-hsio-blk-ctl diff --git a/Documentation/devicetree/bindings/clock/nxp,lpc1850-ccu.yaml b/Documentation/devicetree/bindings/clock/nxp,lpc1850-ccu.yaml new file mode 100644 index 000000000000..5459038cc954 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nxp,lpc1850-ccu.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/nxp,lpc1850-ccu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC1850 Clock Control Unit (CCU) + +description: + Each CGU base clock has several clock branches which can be turned on + or off independently by the Clock Control Units CCU1 or CCU2. The + branch clocks are distributed between CCU1 and CCU2. + + Above text taken from NXP LPC1850 User Manual + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-ccu + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + minItems: 1 + maxItems: 8 + + clock-names: + minItems: 1 + maxItems: 8 + items: + enum: + - base_usb0_clk + - base_periph_clk + - base_usb1_clk + - base_cpu_clk + - base_spifi_clk + - base_spi_clk + - base_apb1_clk + - base_apb3_clk + - base_adchs_clk + - base_sdio_clk + - base_ssp0_clk + - base_ssp1_clk + - base_uart0_clk + - base_uart1_clk + - base_uart2_clk + - base_uart3_clk + - base_audio_clk + description: + Which branch clocks that are available on the CCU depends on the + specific LPC part. Check the user manual for your specific part. + + A list of CCU clocks can be found in dt-bindings/clock/lpc18xx-ccu.h. + +required: + - compatible + - reg + - '#clock-cells' + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + clock-controller@40051000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40051000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_APB3_CLK>, <&cgu BASE_APB1_CLK>, + <&cgu BASE_SPIFI_CLK>, <&cgu BASE_CPU_CLK>, + <&cgu BASE_PERIPH_CLK>, <&cgu BASE_USB0_CLK>, + <&cgu BASE_USB1_CLK>, <&cgu BASE_SPI_CLK>; + clock-names = "base_apb3_clk", "base_apb1_clk", + "base_spifi_clk", "base_cpu_clk", + "base_periph_clk", "base_usb0_clk", + "base_usb1_clk", "base_spi_clk"; + }; + + - | + #include + + clock-controller@40052000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40052000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_AUDIO_CLK>, <&cgu BASE_UART3_CLK>, + <&cgu BASE_UART2_CLK>, <&cgu BASE_UART1_CLK>, + <&cgu BASE_UART0_CLK>, <&cgu BASE_SSP1_CLK>, + <&cgu BASE_SSP0_CLK>, <&cgu BASE_SDIO_CLK>; + clock-names = "base_audio_clk", "base_uart3_clk", + "base_uart2_clk", "base_uart1_clk", + "base_uart0_clk", "base_ssp1_clk", + "base_ssp0_clk", "base_sdio_clk"; + }; + diff --git a/Documentation/devicetree/bindings/clock/nxp,lpc1850-cgu.yaml b/Documentation/devicetree/bindings/clock/nxp,lpc1850-cgu.yaml new file mode 100644 index 000000000000..ed178c7df00c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nxp,lpc1850-cgu.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/nxp,lpc1850-cgu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC1850 Clock Generation Unit (CGU) + +description: > + The CGU generates multiple independent clocks for the core and the + peripheral blocks of the LPC18xx. Each independent clock is called + a base clock and itself is one of the inputs to the two Clock + Control Units (CCUs) which control the branch clocks to the + individual peripherals. + + The CGU selects the inputs to the clock generators from multiple + clock sources, controls the clock generation, and routes the outputs + of the clock generators through the clock source bus to the output + stages. Each output stage provides an independent clock source and + corresponds to one of the base clocks for the LPC18xx. + + Above text taken from NXP LPC1850 User Manual. + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-cgu + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + description: | + Which base clocks that are available on the CGU depends on the + specific LPC part. Base clocks are numbered from 0 to 27. + + Number: Name: Description: + 0 BASE_SAFE_CLK Base safe clock (always on) for WWDT + 1 BASE_USB0_CLK Base clock for USB0 + 2 BASE_PERIPH_CLK Base clock for Cortex-M0SUB subsystem, + SPI, and SGPIO + 3 BASE_USB1_CLK Base clock for USB1 + 4 BASE_CPU_CLK System base clock for ARM Cortex-M core + and APB peripheral blocks #0 and #2 + 5 BASE_SPIFI_CLK Base clock for SPIFI + 6 BASE_SPI_CLK Base clock for SPI + 7 BASE_PHY_RX_CLK Base clock for Ethernet PHY Receive clock + 8 BASE_PHY_TX_CLK Base clock for Ethernet PHY Transmit clock + 9 BASE_APB1_CLK Base clock for APB peripheral block # 1 + 10 BASE_APB3_CLK Base clock for APB peripheral block # 3 + 11 BASE_LCD_CLK Base clock for LCD + 12 BASE_ADCHS_CLK Base clock for ADCHS + 13 BASE_SDIO_CLK Base clock for SD/MMC + 14 BASE_SSP0_CLK Base clock for SSP0 + 15 BASE_SSP1_CLK Base clock for SSP1 + 16 BASE_UART0_CLK Base clock for UART0 + 17 BASE_UART1_CLK Base clock for UART1 + 18 BASE_UART2_CLK Base clock for UART2 + 19 BASE_UART3_CLK Base clock for UART3 + 20 BASE_OUT_CLK Base clock for CLKOUT pin + 24-21 - Reserved + 25 BASE_AUDIO_CLK Base clock for audio system (I2S) + 26 BASE_CGU_OUT0_CLK Base clock for CGU_OUT0 clock output + 27 BASE_CGU_OUT1_CLK Base clock for CGU_OUT1 clock output + + BASE_PERIPH_CLK and BASE_SPI_CLK is only available on LPC43xx. + BASE_ADCHS_CLK is only available on LPC4370. + + clocks: + maxItems: 5 + + clock-indices: + minItems: 1 + maxItems: 28 + + clock-output-names: + minItems: 1 + maxItems: 28 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@40050000 { + compatible = "nxp,lpc1850-cgu"; + reg = <0x40050000 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>, <&creg_clk 1>, <&enet_rx_clk>, <&enet_tx_clk>, <&gp_clkin>; + }; + diff --git a/Documentation/devicetree/bindings/clock/pistachio-clock.txt b/Documentation/devicetree/bindings/clock/pistachio-clock.txt deleted file mode 100644 index 868db499eed2..000000000000 --- a/Documentation/devicetree/bindings/clock/pistachio-clock.txt +++ /dev/null @@ -1,123 +0,0 @@ -Imagination Technologies Pistachio SoC clock controllers -======================================================== - -Pistachio has four clock controllers (core clock, peripheral clock, peripheral -general control, and top general control) which are instantiated individually -from the device-tree. - -External clocks: ----------------- - -There are three external inputs to the clock controllers which should be -defined with the following clock-output-names: -- "xtal": External 52Mhz oscillator (required) -- "audio_clk_in": Alternate audio reference clock (optional) -- "enet_clk_in": Alternate ethernet PHY clock (optional) - -Core clock controller: ----------------------- - -The core clock controller generates clocks for the CPU, RPU (WiFi + BT -co-processor), audio, and several peripherals. - -Required properties: -- compatible: Must be "img,pistachio-clk". -- reg: Must contain the base address and length of the core clock controller. -- #clock-cells: Must be 1. The single cell is the clock identifier. - See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. -- clocks: Must contain an entry for each clock in clock-names. -- clock-names: Must include "xtal" (see "External clocks") and - "audio_clk_in_gate", "enet_clk_in_gate" which are generated by the - top-level general control. - -Example: - clk_core: clock-controller@18144000 { - compatible = "img,pistachio-clk"; - reg = <0x18144000 0x800>; - clocks = <&xtal>, <&cr_top EXT_CLK_AUDIO_IN>, - <&cr_top EXT_CLK_ENET_IN>; - clock-names = "xtal", "audio_clk_in_gate", "enet_clk_in_gate"; - - #clock-cells = <1>; - }; - -Peripheral clock controller: ----------------------------- - -The peripheral clock controller generates clocks for the DDR, ROM, and other -peripherals. The peripheral system clock ("periph_sys") generated by the core -clock controller is the input clock to the peripheral clock controller. - -Required properties: -- compatible: Must be "img,pistachio-periph-clk". -- reg: Must contain the base address and length of the peripheral clock - controller. -- #clock-cells: Must be 1. The single cell is the clock identifier. - See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. -- clocks: Must contain an entry for each clock in clock-names. -- clock-names: Must include "periph_sys", the peripheral system clock generated - by the core clock controller. - -Example: - clk_periph: clock-controller@18144800 { - compatible = "img,pistachio-clk-periph"; - reg = <0x18144800 0x800>; - clocks = <&clk_core CLK_PERIPH_SYS>; - clock-names = "periph_sys"; - - #clock-cells = <1>; - }; - -Peripheral general control: ---------------------------- - -The peripheral general control block generates system interface clocks and -resets for various peripherals. It also contains miscellaneous peripheral -control registers. The system clock ("sys") generated by the peripheral clock -controller is the input clock to the system clock controller. - -Required properties: -- compatible: Must include "img,pistachio-periph-cr" and "syscon". -- reg: Must contain the base address and length of the peripheral general - control registers. -- #clock-cells: Must be 1. The single cell is the clock identifier. - See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. -- clocks: Must contain an entry for each clock in clock-names. -- clock-names: Must include "sys", the system clock generated by the peripheral - clock controller. - -Example: - cr_periph: syscon@18144800 { - compatible = "img,pistachio-cr-periph", "syscon"; - reg = <0x18148000 0x1000>; - clocks = <&clock_periph PERIPH_CLK_PERIPH_SYS>; - clock-names = "sys"; - - #clock-cells = <1>; - }; - -Top-level general control: --------------------------- - -The top-level general control block contains miscellaneous control registers and -gates for the external clocks "audio_clk_in" and "enet_clk_in". - -Required properties: -- compatible: Must include "img,pistachio-cr-top" and "syscon". -- reg: Must contain the base address and length of the top-level - control registers. -- clocks: Must contain an entry for each clock in clock-names. -- clock-names: Two optional clocks, "audio_clk_in" and "enet_clk_in" (see - "External clocks"). -- #clock-cells: Must be 1. The single cell is the clock identifier. - See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. - -Example: - cr_top: syscon@18144800 { - compatible = "img,pistachio-cr-top", "syscon"; - reg = <0x18149000 0x200>; - clocks = <&audio_refclk>, <&ext_enet_in>; - clock-names = "audio_clk_in", "enet_clk_in"; - - #clock-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/clock/qca,ath79-pll.txt b/Documentation/devicetree/bindings/clock/qca,ath79-pll.txt deleted file mode 100644 index 241fb0545b9e..000000000000 --- a/Documentation/devicetree/bindings/clock/qca,ath79-pll.txt +++ /dev/null @@ -1,33 +0,0 @@ -Binding for Qualcomm Atheros AR7xxx/AR9XXX PLL controller - -The PPL controller provides the 3 main clocks of the SoC: CPU, DDR and AHB. - -Required Properties: -- compatible: has to be "qca,-pll" and one of the following - fallbacks: - - "qca,ar7100-pll" - - "qca,ar7240-pll" - - "qca,ar9130-pll" - - "qca,ar9330-pll" - - "qca,ar9340-pll" - - "qca,qca9550-pll" -- reg: Base address and size of the controllers memory area -- clock-names: Name of the input clock, has to be "ref" -- clocks: phandle of the external reference clock -- #clock-cells: has to be one - -Optional properties: -- clock-output-names: should be "cpu", "ddr", "ahb" - -Example: - - pll-controller@18050000 { - compatible = "qca,ar9132-pll", "qca,ar9130-pll"; - reg = <0x18050000 0x20>; - - clock-names = "ref"; - clocks = <&extosc>; - - #clock-cells = <1>; - clock-output-names = "cpu", "ddr", "ahb"; - }; diff --git a/Documentation/devicetree/bindings/clock/qca,ath79-pll.yaml b/Documentation/devicetree/bindings/clock/qca,ath79-pll.yaml new file mode 100644 index 000000000000..69863e8a4648 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qca,ath79-pll.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qca,ath79-pll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Atheros ATH79 PLL controller + +maintainers: + - Alban Bedel + - Antony Pavlov + +description: > + The PLL controller provides the 3 main clocks of the SoC: CPU, DDR and AHB. + +properties: + compatible: + oneOf: + - items: + - const: qca,ar9132-pll + - const: qca,ar9130-pll + - items: + - enum: + - qca,ar7100-pll + - qca,ar7240-pll + - qca,ar9130-pll + - qca,ar9330-pll + - qca,ar9340-pll + - qca,qca9530-pll + - qca,qca9550-pll + - qca,qca9560-pll + + reg: + maxItems: 1 + + clock-names: + items: + - const: ref + + clocks: + maxItems: 1 + + '#clock-cells': + const: 1 + + clock-output-names: + items: + - const: cpu + - const: ddr + - const: ahb + +required: + - compatible + - reg + - clock-names + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@18050000 { + compatible = "qca,ar9132-pll", "qca,ar9130-pll"; + reg = <0x18050000 0x20>; + clock-names = "ref"; + clocks = <&extosc>; + #clock-cells = <1>; + clock-output-names = "cpu", "ddr", "ahb"; + }; diff --git a/Documentation/devicetree/bindings/clock/qcom,camcc-sm8250.yaml b/Documentation/devicetree/bindings/clock/qcom,camcc-sm8250.yaml index 3fd3dc1069fb..5c3ff37ec0d7 100644 --- a/Documentation/devicetree/bindings/clock/qcom,camcc-sm8250.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,camcc-sm8250.yaml @@ -13,7 +13,7 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SM8250. - See also:: include/dt-bindings/clock/qcom,camcc-sm8250.h + See also: include/dt-bindings/clock/qcom,camcc-sm8250.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml index 0a3ef7fd03fa..ef2b1e204430 100644 --- a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks and power domains on SM6125. - See also:: include/dt-bindings/clock/qcom,dispcc-sm6125.h + See also: include/dt-bindings/clock/qcom,dispcc-sm6125.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6350.yaml b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6350.yaml index 46403b98411f..a602e882e964 100644 --- a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6350.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6350.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SM6350. - See also:: include/dt-bindings/clock/qcom,dispcc-sm6350.h + See also: include/dt-bindings/clock/qcom,dispcc-sm6350.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq4019.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq4019.yaml index 012048921f92..c91039dc100e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq4019.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq4019.yaml @@ -15,7 +15,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on IPQ4019. - See also:: include/dt-bindings/clock/qcom,gcc-ipq4019.h + See also: include/dt-bindings/clock/qcom,gcc-ipq4019.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml index 38b9e4283900..00d7df75b3d6 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on IPQ8074. - See also:: include/dt-bindings/clock/qcom,gcc-ipq8074.h + See also: include/dt-bindings/clock/qcom,gcc-ipq8074.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8976.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8976.yaml index cd49704dcb95..92195091a919 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8976.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8976.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on MSM8976. - See also:: include/dt-bindings/clock/qcom,gcc-msm8976.h + See also: include/dt-bindings/clock/qcom,gcc-msm8976.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8994.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8994.yaml index 10afe984e2fb..93bcd61461e7 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8994.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8994.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on MSM8994 and MSM8992. - See also:: include/dt-bindings/clock/qcom,gcc-msm8994.h + See also: include/dt-bindings/clock/qcom,gcc-msm8994.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml index 013fd074a8d5..64796f45f294 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module which provides the clocks, resets and power domains on MSM8996. - See also:: include/dt-bindings/clock/qcom,gcc-msm8996.h + See also: include/dt-bindings/clock/qcom,gcc-msm8996.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8998.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8998.yaml index abae658c0ed9..d882f2b6620e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8998.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8998.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on MSM8998. - See also:: include/dt-bindings/clock/qcom,gcc-msm8998.h + See also: include/dt-bindings/clock/qcom,gcc-msm8998.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-qcm2290.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-qcm2290.yaml index 38c4c8c61b3a..b9194fa11e47 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-qcm2290.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-qcm2290.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on QCM2290. - See also:: include/dt-bindings/clock/qcom,gcc-qcm2290.h + See also: include/dt-bindings/clock/qcom,gcc-qcm2290.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-qcs404.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-qcs404.yaml index 94755465c1fb..6b35a3c080a2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-qcs404.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-qcs404.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on QCS404. - See also:: include/dt-bindings/clock/qcom,gcc-qcs404.h + See also: include/dt-bindings/clock/qcom,gcc-qcs404.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sc7180.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sc7180.yaml index 1847bbeaa9d1..e30d1df3eeb5 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sc7180.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sc7180.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SC7180. - See also:: include/dt-bindings/clock/qcom,gcc-sc7180.h + See also: include/dt-bindings/clock/qcom,gcc-sc7180.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sc7280.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sc7280.yaml index 4e4f68b9f6d2..5ddaf27bb1f4 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sc7280.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sc7280.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SC7280. - See also:: include/dt-bindings/clock/qcom,gcc-sc7280.h + See also: include/dt-bindings/clock/qcom,gcc-sc7280.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sc8180x.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sc8180x.yaml index b4784ecaf58d..82c2ef39934d 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sc8180x.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sc8180x.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SC8180x. - See also:: include/dt-bindings/clock/qcom,gcc-sc8180x.h + See also: include/dt-bindings/clock/qcom,gcc-sc8180x.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sc8280xp.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sc8280xp.yaml index 5cfde8a4de4e..c1eeccef66b4 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sc8280xp.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sc8280xp.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SC8280xp. - See also:: include/dt-bindings/clock/qcom,gcc-sc8280xp.h + See also: include/dt-bindings/clock/qcom,gcc-sc8280xp.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml index ef0a20456e8a..a7523a414341 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SDM670 and SDM845 - See also:: include/dt-bindings/clock/qcom,gcc-sdm845.h + See also: include/dt-bindings/clock/qcom,gcc-sdm845.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sdx55.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sdx55.yaml index 30819f3d85c6..320e4f5b2b18 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sdx55.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sdx55.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SDX55 - See also:: include/dt-bindings/clock/qcom,gcc-sdx55.h + See also: include/dt-bindings/clock/qcom,gcc-sdx55.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sdx65.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sdx65.yaml index 915449228668..9242e6e19139 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sdx65.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sdx65.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SDX65 - See also:: include/dt-bindings/clock/qcom,gcc-sdx65.h + See also: include/dt-bindings/clock/qcom,gcc-sdx65.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6115.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6115.yaml index ecb69c707f09..c926630907c5 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6115.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6115.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM4250/6115. - See also:: include/dt-bindings/clock/qcom,gcc-sm6115.h + See also: include/dt-bindings/clock/qcom,gcc-sm6115.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6125.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6125.yaml index 1fe68e07a2b2..5bd422e94a38 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6125.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6125.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM6125. - See also:: include/dt-bindings/clock/qcom,gcc-sm6125.h + See also: include/dt-bindings/clock/qcom,gcc-sm6125.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6350.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6350.yaml index 78e232fa95dc..819e855eaf9a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm6350.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm6350.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM6350. - See also:: include/dt-bindings/clock/qcom,gcc-sm6350.h + See also: include/dt-bindings/clock/qcom,gcc-sm6350.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8150.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8150.yaml index 1dcf97c0c064..5f3f69fe9ddb 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8150.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8150.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8150. - See also:: include/dt-bindings/clock/qcom,gcc-sm8150.h + See also: include/dt-bindings/clock/qcom,gcc-sm8150.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml index 979ff0a8bf68..f4cd5a509c60 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8250. - See also:: include/dt-bindings/clock/qcom,gcc-sm8250.h + See also: include/dt-bindings/clock/qcom,gcc-sm8250.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8350.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8350.yaml index 594e87f5ba09..97ffae3b5522 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8350.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8350.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8350. - See also:: include/dt-bindings/clock/qcom,gcc-sm8350.h + See also: include/dt-bindings/clock/qcom,gcc-sm8350.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8450.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8450.yaml index 77273aee5d52..3169ac05e1d8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8450.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8450.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8450 - See also:: include/dt-bindings/clock/qcom,gcc-sm8450.h + See also: include/dt-bindings/clock/qcom,gcc-sm8450.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml index f869b3739be8..817d51135fbf 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml @@ -24,6 +24,8 @@ description: properties: compatible: enum: + - qcom,ipq5018-cmn-pll + - qcom,ipq5424-cmn-pll - qcom,ipq9574-cmn-pll reg: diff --git a/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt deleted file mode 100644 index 030ba60dab08..000000000000 --- a/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt +++ /dev/null @@ -1,34 +0,0 @@ -Krait Clock Controller - -PROPERTIES - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,krait-cc-v1" - "qcom,krait-cc-v2" - -- #clock-cells: - Usage: required - Value type: - Definition: must be 1 - -- clocks: - Usage: required - Value type: - Definition: reference to the clock parents of hfpll, secondary muxes. - -- clock-names: - Usage: required - Value type: - Definition: must be "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb". - -Example: - - kraitcc: clock-controller { - compatible = "qcom,krait-cc-v1"; - clocks = <&hfpll0>, <&hfpll1>, <&acpu0_aux>, <&acpu1_aux>, ; - clock-names = "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb"; - #clock-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/clock/qcom,krait-cc.yaml b/Documentation/devicetree/bindings/clock/qcom,krait-cc.yaml new file mode 100644 index 000000000000..d6a019371fcf --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,krait-cc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Krait Clock Controller + +maintainers: + - Stephen Boyd + +properties: + compatible: + enum: + - qcom,krait-cc-v1 + - qcom,krait-cc-v2 + + '#clock-cells': + const: 1 + + clocks: + items: + - description: Parent clock phandle for hfpll0 + - description: Parent clock phandle for hfpll1 + - description: Parent clock phandle for acpu0_aux + - description: Parent clock phandle for acpu1_aux + - description: Parent clock phandle for qsb + + clock-names: + items: + - const: hfpll0 + - const: hfpll1 + - const: acpu0_aux + - const: acpu1_aux + - const: qsb + +required: + - compatible + - '#clock-cells' + - clocks + - clock-names + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml new file mode 100644 index 000000000000..f63149ecf3e1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,milos-camcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Camera Clock & Reset Controller on Milos + +maintainers: + - Luca Weiss + +description: | + Qualcomm camera clock control module provides the clocks, resets and power + domains on Milos. + + See also: include/dt-bindings/clock/qcom,milos-camcc.h + +properties: + compatible: + const: qcom,milos-camcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: Camera AHB clock from GCC + +required: + - compatible + - clocks + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@adb0000 { + compatible = "qcom,milos-camcc"; + reg = <0x0adb0000 0x40000>; + clocks = <&bi_tcxo_div2>, + <&sleep_clk>, + <&gcc GCC_CAMERA_AHB_CLK>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-dispcc.yaml new file mode 100644 index 000000000000..94908804756b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-dispcc.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,milos-dispcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Display Clock & Reset Controller on Milos + +maintainers: + - Luca Weiss + +description: | + Qualcomm display clock control module provides the clocks, resets and power + domains on Milos. + + See also: include/dt-bindings/clock/qcom,milos-dispcc.h + +properties: + compatible: + const: qcom,milos-dispcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: Display's AHB clock + - description: GPLL0 source from GCC + - description: Byte clock from DSI PHY0 + - description: Pixel clock from DSI PHY0 + - description: Link clock from DP PHY0 + - description: VCO DIV clock from DP PHY0 + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + clock-controller@af00000 { + compatible = "qcom,milos-dispcc"; + reg = <0x0af00000 0x20000>; + clocks = <&bi_tcxo_div2>, + <&sleep_clk>, + <&gcc GCC_DISP_AHB_CLK>, + <&gcc GCC_DISP_GPLL0_DIV_CLK_SRC>, + <&mdss_dsi0_phy 0>, + <&mdss_dsi0_phy 1>, + <&usb_dp_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml new file mode 100644 index 000000000000..cf244c155f9a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-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,milos-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller on Milos + +maintainers: + - Luca Weiss + +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 + +properties: + compatible: + const: qcom,milos-gcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: PCIE 0 Pipe clock source + - description: PCIE 1 Pipe 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 wrapper pipe clock source + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@100000 { + compatible = "qcom,milos-gcc"; + reg = <0x00100000 0x1f4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>, + <&pcie0_phy>, + <&pcie1_phy>, + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>, + <&usb_1_qmpphy>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-videocc.yaml new file mode 100644 index 000000000000..14c31efe1308 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-videocc.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,milos-videocc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Video Clock & Reset Controller on Milos + +maintainers: + - Luca Weiss + +description: | + Qualcomm video clock control module provides the clocks, resets and power + domains on Milos. + + See also: include/dt-bindings/clock/qcom,milos-videocc.h + +properties: + compatible: + const: qcom,milos-videocc + + clocks: + items: + - description: Board XO source + - description: Board active XO source + - description: Sleep clock source + - description: Video AHB clock from GCC + +required: + - compatible + - clocks + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@aaf0000 { + compatible = "qcom,milos-videocc"; + reg = <0x0aaf0000 0x10000>; + clocks = <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&sleep_clk>, + <&gcc GCC_VIDEO_AHB_CLK>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,mmcc.yaml b/Documentation/devicetree/bindings/clock/qcom,mmcc.yaml index 59ac288ca5f1..53ceec9673a8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,mmcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,mmcc.yaml @@ -38,36 +38,16 @@ properties: minItems: 7 maxItems: 13 - '#clock-cells': - const: 1 - - '#reset-cells': - const: 1 - - '#power-domain-cells': - const: 1 - - reg: - maxItems: 1 - - protected-clocks: - description: - Protected clock specifier list as per common clock binding - vdd-gfx-supply: description: Regulator supply for the GPU_GX GDSC required: - compatible - - reg - - '#clock-cells' - - '#reset-cells' - '#power-domain-cells' -additionalProperties: false - allOf: + - $ref: qcom,gcc.yaml# - if: properties: compatible: @@ -351,6 +331,8 @@ allOf: - const: dp_link_2x_clk_divsel_five - const: dp_vco_divided_clk_src_mux +unevaluatedProperties: false + examples: # Example for MMCC for MSM8960: - | diff --git a/Documentation/devicetree/bindings/clock/qcom,msm8998-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,msm8998-gpucc.yaml index b9b218ef9b68..374de7a6f8d9 100644 --- a/Documentation/devicetree/bindings/clock/qcom,msm8998-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,msm8998-gpucc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm graphics clock control module provides the clocks, resets and power domains on MSM8998. - See also:: include/dt-bindings/clock/qcom,gpucc-msm8998.h + See also: include/dt-bindings/clock/qcom,gpucc-msm8998.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,qcm2290-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,qcm2290-dispcc.yaml index 243be4f76db3..4a533b45eec2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,qcm2290-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,qcm2290-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on qcm2290. - See also:: include/dt-bindings/clock/qcom,dispcc-qcm2290.h + See also: include/dt-bindings/clock/qcom,dispcc-qcm2290.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,qcs615-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,qcs615-dispcc.yaml new file mode 100644 index 000000000000..d566f19beb0d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,qcs615-dispcc.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,qcs615-dispcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Display Clock & Reset Controller on QCS615 + +maintainers: + - Taniya Das + +description: | + Qualcomm display clock control module provides the clocks, resets and power + domains on QCS615. + + See also: include/dt-bindings/clock/qcom,qcs615-dispcc.h + +properties: + compatible: + const: qcom,qcs615-dispcc + + clocks: + items: + - description: Board XO source + - description: GPLL0 clock source from GCC + - description: Byte clock from DSI PHY0 + - description: Pixel clock from DSI PHY0 + - description: Pixel clock from DSI PHY1 + - description: Display port PLL link clock + - description: Display port PLL VCO DIV clock + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + clock-controller@af00000 { + compatible = "qcom,qcs615-dispcc"; + reg = <0x0af00000 0x20000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_DISP_GPLL0_DIV_CLK_SRC>, + <&mdss_dsi0_phy 0>, + <&mdss_dsi0_phy 1>, + <&mdss_dsi1_phy 0>, + <&mdss_dp_phy 0>, + <&mdss_dp_vco 0>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,qcs615-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,qcs615-gpucc.yaml new file mode 100644 index 000000000000..5f7d83d1a7be --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,qcs615-gpucc.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,qcs615-gpucc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Graphics Clock & Reset Controller on QCS615 + +maintainers: + - Taniya Das + +description: | + Qualcomm graphics clock control module provides clocks, resets and power + domains on QCS615 Qualcomm SoCs. + + See also: include/dt-bindings/clock/qcom,qcs615-gpucc.h + +properties: + compatible: + const: qcom,qcs615-gpucc + + clocks: + items: + - description: Board XO source + - description: GPLL0 main branch source + - description: GPLL0 GPUCC div branch source + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + clock-controller@5090000 { + compatible = "qcom,qcs615-gpucc"; + reg = <0x5090000 0x9000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GPLL0>, + <&gcc GCC_GPU_GPLL0_DIV_CLK_SRC>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,qcs615-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,qcs615-videocc.yaml new file mode 100644 index 000000000000..f51b69de1047 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,qcs615-videocc.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,qcs615-videocc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Video Clock & Reset Controller on QCS615 + +maintainers: + - Taniya Das + +description: | + Qualcomm video clock control module provides clocks, resets and power + domains on QCS615 Qualcomm SoCs. + + See also: include/dt-bindings/clock/qcom,qcs615-videocc.h + +properties: + compatible: + const: qcom,qcs615-videocc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + clock-controller@ab00000 { + compatible = "qcom,qcs615-videocc"; + reg = <0xab00000 0x10000>; + 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,qdu1000-ecpricc.yaml b/Documentation/devicetree/bindings/clock/qcom,qdu1000-ecpricc.yaml index fd21df0e7697..3038307ff2c5 100644 --- a/Documentation/devicetree/bindings/clock/qcom,qdu1000-ecpricc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,qdu1000-ecpricc.yaml @@ -14,7 +14,7 @@ description: | Qualcomm ECPRI Specification V2.0 Common Public Radio Interface clock control module which supports the clocks, resets on QDU1000 and QRU1000 - See also:: include/dt-bindings/clock/qcom,qdu1000-ecpricc.h + See also: include/dt-bindings/clock/qcom,qdu1000-ecpricc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,qdu1000-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,qdu1000-gcc.yaml index 86befef02650..2c5a9ef4fe4d 100644 --- a/Documentation/devicetree/bindings/clock/qcom,qdu1000-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,qdu1000-gcc.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module which supports the clocks, resets and power domains on QDU1000 and QRU1000 - See also:: include/dt-bindings/clock/qcom,qdu1000-gcc.h + See also: include/dt-bindings/clock/qcom,qdu1000-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml index dcb872b9cf3e..a4414ba0b287 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml @@ -17,6 +17,7 @@ description: | properties: compatible: enum: + - qcom,milos-rpmh-clk - qcom,qcs615-rpmh-clk - qcom,qdu1000-rpmh-clk - qcom,sa8775p-rpmh-clk diff --git a/Documentation/devicetree/bindings/clock/qcom,sa8775p-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sa8775p-camcc.yaml index 81623f59d11d..f42ccb6627a3 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sa8775p-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sa8775p-camcc.yaml @@ -17,12 +17,14 @@ description: | See also: include/dt-bindings/clock/qcom,qcs8300-camcc.h include/dt-bindings/clock/qcom,sa8775p-camcc.h + include/dt-bindings/clock/qcom,sc8280xp-camcc.h properties: compatible: enum: - qcom,qcs8300-camcc - qcom,sa8775p-camcc + - qcom,sc8280xp-camcc clocks: items: @@ -35,6 +37,11 @@ properties: maxItems: 1 description: MMCX power domain + required-opps: + description: + OPP node describing required MMCX performance point. + maxItems: 1 + required: - compatible - clocks @@ -43,6 +50,14 @@ required: allOf: - $ref: qcom,gcc.yaml# + - if: + properties: + compatible: + contains: + const: qcom,sc8280xp-camcc + then: + required: + - required-opps unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/clock/qcom,sa8775p-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sa8775p-gcc.yaml index addbd323fa6d..c641aac8c451 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sa8775p-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sa8775p-gcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on sa8775p. - See also:: include/dt-bindings/clock/qcom,sa8775p-gcc.h + See also: include/dt-bindings/clock/qcom,sa8775p-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7180-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7180-camcc.yaml index c7fe6400ea13..98ee9be84794 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7180-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7180-camcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SC7180. - See also:: include/dt-bindings/clock/qcom,camcc-sc7180.h + See also: include/dt-bindings/clock/qcom,camcc-sc7180.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7180-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7180-dispcc.yaml index 0d8ea44d8141..f147d06ad2ef 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7180-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7180-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SC7180. - See also:: include/dt-bindings/clock/qcom,dispcc-sc7180.h + See also: include/dt-bindings/clock/qcom,dispcc-sc7180.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7180-lpasscorecc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7180-lpasscorecc.yaml index fdfb389083c1..ad360debef7c 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7180-lpasscorecc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7180-lpasscorecc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm LPASS core clock control module provides the clocks and power domains on SC7180. - See also:: include/dt-bindings/clock/qcom,lpasscorecc-sc7180.h + See also: include/dt-bindings/clock/qcom,lpasscorecc-sc7180.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7280-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7280-camcc.yaml index dcef8de3a905..2f28be58e82e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7280-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7280-camcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SC7280. - See also:: include/dt-bindings/clock/qcom,camcc-sc7280.h + See also: include/dt-bindings/clock/qcom,camcc-sc7280.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7280-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7280-dispcc.yaml index 23177661be40..95b1e4f48c4f 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7280-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7280-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SC7280. - See also:: include/dt-bindings/clock/qcom,dispcc-sc7280.h + See also: include/dt-bindings/clock/qcom,dispcc-sc7280.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sc7280-lpasscc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc7280-lpasscc.yaml index f44c5c130d2d..a90961d8656c 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sc7280-lpasscc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sc7280-lpasscc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm LPASS core clock control module provides the clocks and power domains on SC7280. - See also:: include/dt-bindings/clock/qcom,lpass-sc7280.h + See also: include/dt-bindings/clock/qcom,lpass-sc7280.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sc8180x-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sc8180x-camcc.yaml new file mode 100644 index 000000000000..477ee687520e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,sc8180x-camcc.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,sc8180x-camcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Camera Clock & Reset Controller on SC8180X + +maintainers: + - Satya Priya Kakitapalli + +description: | + Qualcomm camera clock control module provides the clocks, resets and + power domains on SC8180X. + + See also: include/dt-bindings/clock/qcom,sc8180x-camcc.h + +properties: + compatible: + const: qcom,sc8180x-camcc + + clocks: + items: + - description: Camera AHB clock from GCC + - description: Board XO source + - description: Sleep clock source + + power-domains: + maxItems: 1 + description: + A phandle and PM domain specifier for the MMCX power domain. + + required-opps: + maxItems: 1 + description: + A phandle to an OPP node describing required MMCX performance point. + +required: + - compatible + - clocks + - power-domains + - required-opps + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + clock-controller@ad00000 { + compatible = "qcom,sc8180x-camcc"; + reg = <0x0ad00000 0x20000>; + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + power-domains = <&rpmhpd SC8180X_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,sdm845-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sdm845-camcc.yaml index fa95c3a1ba3a..6214e41eec1f 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sdm845-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sdm845-camcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SDM845. - See also:: include/dt-bindings/clock/qcom,camcc-sm845.h + See also: include/dt-bindings/clock/qcom,camcc-sm845.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,sdm845-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sdm845-dispcc.yaml index 220f4004f7fd..854c391c8307 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sdm845-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sdm845-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SDM845. - See also:: include/dt-bindings/clock/qcom,dispcc-sdm845.h + See also: include/dt-bindings/clock/qcom,dispcc-sdm845.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sdm845-lpasscc.yaml b/Documentation/devicetree/bindings/clock/qcom,sdm845-lpasscc.yaml index a96fd837c70a..f9feb7049b21 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sdm845-lpasscc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sdm845-lpasscc.yaml @@ -12,7 +12,7 @@ maintainers: description: | Qualcomm SDM845 LPASS (Low Power Audio SubSystem) Clock Controller. - See also:: include/dt-bindings/clock/qcom,lpass-sdm845.h + See also: include/dt-bindings/clock/qcom,lpass-sdm845.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sdx75-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sdx75-gcc.yaml index 567182aba300..29a0b29bcb81 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sdx75-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sdx75-gcc.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SDX75 - See also:: include/dt-bindings/clock/qcom,sdx75-gcc.h + See also: include/dt-bindings/clock/qcom,sdx75-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm4450-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm4450-camcc.yaml index f54ce865880d..70f025b26736 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm4450-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm4450-camcc.yaml @@ -14,38 +14,26 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SM4450 - See also:: include/dt-bindings/clock/qcom,sm4450-camcc.h + See also: include/dt-bindings/clock/qcom,sm4450-camcc.h properties: compatible: const: qcom,sm4450-camcc - reg: - maxItems: 1 - clocks: items: - description: Board XO source - description: Camera AHB clock source from GCC - '#clock-cells': - const: 1 - - '#reset-cells': - const: 1 - - '#power-domain-cells': - const: 1 - required: - compatible - - reg - clocks - - '#clock-cells' - - '#reset-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/clock/qcom,sm4450-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm4450-dispcc.yaml index 2aa05353eff1..d977788bdc8a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm4450-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm4450-dispcc.yaml @@ -14,15 +14,12 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SM4450 - See also:: include/dt-bindings/clock/qcom,sm4450-dispcc.h + See also: include/dt-bindings/clock/qcom,sm4450-dispcc.h properties: compatible: const: qcom,sm4450-dispcc - reg: - maxItems: 1 - clocks: items: - description: Board XO source @@ -32,24 +29,15 @@ properties: - description: Byte clock from DSI PHY0 - description: Pixel clock from DSI PHY0 - '#clock-cells': - const: 1 - - '#reset-cells': - const: 1 - - '#power-domain-cells': - const: 1 - required: - compatible - - reg - clocks - - '#clock-cells' - - '#reset-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/clock/qcom,sm4450-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm4450-gcc.yaml index 0ac92d7871e1..9cfe859bacc9 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm4450-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm4450-gcc.yaml @@ -14,7 +14,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM4450 - See also:: include/dt-bindings/clock/qcom,sm4450-gcc.h + See also: include/dt-bindings/clock/qcom,sm4450-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6115-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6115-dispcc.yaml index 00be36683eb5..b31424306f49 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6115-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6115-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks and power domains on SM6115. - See also:: include/dt-bindings/clock/qcom,sm6115-dispcc.h + See also: include/dt-bindings/clock/qcom,sm6115-dispcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6115-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6115-gpucc.yaml index 4ff17a91344b..104ba10ca573 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6115-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6115-gpucc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm graphics clock control module provides clocks, resets and power domains on Qualcomm SoCs. - See also:: include/dt-bindings/clock/qcom,sm6115-gpucc.h + See also: include/dt-bindings/clock/qcom,sm6115-gpucc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6125-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6125-gpucc.yaml index 10a9c96a97b6..12d6f0cdbcd8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6125-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6125-gpucc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm graphics clock control module provides clocks and power domains on Qualcomm SoCs. - See also:: include/dt-bindings/clock/qcom,sm6125-gpucc.h + See also: include/dt-bindings/clock/qcom,sm6125-gpucc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6350-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6350-camcc.yaml index c03b30f64f35..e31cd4300f7d 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6350-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6350-camcc.yaml @@ -8,16 +8,21 @@ title: Qualcomm Camera Clock & Reset Controller on SM6350 maintainers: - Konrad Dybcio + - Taniya Das description: | Qualcomm camera clock control module provides the clocks, resets and power - domains on SM6350. + domains on SM6350 and QCS615 SoC. - See also:: include/dt-bindings/clock/qcom,sm6350-camcc.h + See also: + include/dt-bindings/clock/qcom,qcs615-camcc.h + include/dt-bindings/clock/qcom,sm6350-camcc.h properties: compatible: - const: qcom,sm6350-camcc + enum: + - qcom,qcs615-camcc + - qcom,sm6350-camcc clocks: items: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6375-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6375-dispcc.yaml index 3cd422a645fd..519ea76cb052 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6375-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6375-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SM6375. - See also:: include/dt-bindings/clock/qcom,dispcc-sm6375.h + See also: include/dt-bindings/clock/qcom,dispcc-sm6375.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6375-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6375-gcc.yaml index de4e9066eeb8..66dfa72fa975 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6375-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6375-gcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM6375 - See also:: include/dt-bindings/clock/qcom,sm6375-gcc.h + See also: include/dt-bindings/clock/qcom,sm6375-gcc.h allOf: - $ref: qcom,gcc.yaml# diff --git a/Documentation/devicetree/bindings/clock/qcom,sm6375-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm6375-gpucc.yaml index d9dd479c17bd..3aad6b5bb1c5 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm6375-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm6375-gpucc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm graphics clock control module provides clocks, resets and power domains on Qualcomm SoCs. - See also:: include/dt-bindings/clock/qcom,sm6375-gpucc.h + See also: include/dt-bindings/clock/qcom,sm6375-gpucc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm7150-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm7150-camcc.yaml index 7be4b10c430c..b96091c28c5a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm7150-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm7150-camcc.yaml @@ -15,7 +15,7 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SM7150. - See also:: include/dt-bindings/clock/qcom,sm7150-camcc.h + See also: include/dt-bindings/clock/qcom,sm7150-camcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm7150-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm7150-dispcc.yaml index b8d6e1d05ce2..13ab3359b592 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm7150-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm7150-dispcc.yaml @@ -15,7 +15,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SM7150. - See also:: include/dt-bindings/clock/qcom,sm7150-dispcc.h + See also: include/dt-bindings/clock/qcom,sm7150-dispcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm7150-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm7150-gcc.yaml index 4d7bbbf4ce8a..3878808f811e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm7150-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm7150-gcc.yaml @@ -15,7 +15,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM7150 - See also:: include/dt-bindings/clock/qcom,sm7150-gcc.h + See also: include/dt-bindings/clock/qcom,sm7150-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm7150-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm7150-videocc.yaml index 037ffc71e70e..9f7928730386 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm7150-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm7150-videocc.yaml @@ -15,7 +15,7 @@ description: | Qualcomm video clock control module provides the clocks, resets and power domains on SM7150. - See also:: include/dt-bindings/clock/qcom,videocc-sm7150.h + See also: include/dt-bindings/clock/qcom,videocc-sm7150.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8150-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8150-camcc.yaml index 5e9f62d7866c..a55e30a4975e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8150-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8150-camcc.yaml @@ -13,15 +13,12 @@ description: | Qualcomm camera clock control module provides the clocks, resets and power domains on SM8150. - See also:: include/dt-bindings/clock/qcom,sm8150-camcc.h + See also: include/dt-bindings/clock/qcom,sm8150-camcc.h properties: compatible: const: qcom,sm8150-camcc - reg: - maxItems: 1 - clocks: items: - description: Board XO source @@ -37,26 +34,17 @@ properties: description: A phandle to an OPP node describing required MMCX performance point. - '#clock-cells': - const: 1 - - '#reset-cells': - const: 1 - - '#power-domain-cells': - const: 1 - required: - compatible - - reg - clocks - power-domains - required-opps - - '#clock-cells' - - '#reset-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-camcc.yaml index 9e79f8fec437..c1e06f39431e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-camcc.yaml @@ -15,7 +15,6 @@ description: | domains on SM8450. See also: - include/dt-bindings/clock/qcom,sc8280xp-camcc.h include/dt-bindings/clock/qcom,sm8450-camcc.h include/dt-bindings/clock/qcom,sm8550-camcc.h include/dt-bindings/clock/qcom,sm8650-camcc.h @@ -23,7 +22,6 @@ description: | properties: compatible: enum: - - qcom,sc8280xp-camcc - qcom,sm8450-camcc - qcom,sm8475-camcc - qcom,sm8550-camcc @@ -37,14 +35,18 @@ properties: - description: Sleep clock source power-domains: - maxItems: 1 description: - A phandle and PM domain specifier for the MMCX power domain. + Power domains required for the clock controller to operate + items: + - description: MMCX power domain + - description: MXC power domain required-opps: - maxItems: 1 description: - A phandle to an OPP node describing required MMCX performance point. + OPP nodes that describe required performance points on power domains + items: + - description: MMCX performance point + - description: MXC performance point reg: maxItems: 1 @@ -82,8 +84,10 @@ examples: <&rpmhcc RPMH_CXO_CLK>, <&rpmhcc RPMH_CXO_CLK_A>, <&sleep_clk>; - power-domains = <&rpmhpd RPMHPD_MMCX>; - required-opps = <&rpmhpd_opp_low_svs>; + 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>; diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-dispcc.yaml index e9123bbfd491..bd131a1ff165 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-dispcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm display clock control module provides the clocks, resets and power domains on SM8450. - See also:: include/dt-bindings/clock/qcom,sm8450-dispcc.h + See also: include/dt-bindings/clock/qcom,sm8450-dispcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml index 02968632fb3a..44380f6f8136 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml @@ -14,6 +14,7 @@ description: | domains on Qualcomm SoCs. See also:: + include/dt-bindings/clock/qcom,milos-gpucc.h include/dt-bindings/clock/qcom,sar2130p-gpucc.h include/dt-bindings/clock/qcom,sm4450-gpucc.h include/dt-bindings/clock/qcom,sm8450-gpucc.h @@ -25,6 +26,7 @@ description: | properties: compatible: enum: + - qcom,milos-gpucc - qcom,sar2130p-gpucc - qcom,sm4450-gpucc - qcom,sm8450-gpucc diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml index 62714fa54db8..fcd2727dae46 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml @@ -25,6 +25,7 @@ properties: - qcom,sm8475-videocc - qcom,sm8550-videocc - qcom,sm8650-videocc + - qcom,x1e80100-videocc clocks: items: @@ -32,14 +33,18 @@ properties: - description: Video AHB clock from GCC power-domains: - maxItems: 1 description: - MMCX power domain. + Power domains required for the clock controller to operate + items: + - description: MMCX power domain + - description: MXC power domain required-opps: - maxItems: 1 description: - A phandle to an OPP node describing required MMCX performance point. + OPP nodes that describe required performance points on power domains + items: + - description: MMCX performance point + - description: MXC performance point required: - compatible @@ -72,8 +77,10 @@ examples: reg = <0x0aaf0000 0x10000>; clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_VIDEO_AHB_CLK>; - power-domains = <&rpmhpd RPMHPD_MMCX>; - required-opps = <&rpmhpd_opp_low_svs>; + 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>; diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-gcc.yaml index d83b64dcce4f..c4e9b9bb63f5 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8550-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-gcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8550 - See also:: include/dt-bindings/clock/qcom,sm8550-gcc.h + See also: include/dt-bindings/clock/qcom,sm8550-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml index f3afbb25e868..2ed7d59722fc 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml @@ -22,6 +22,7 @@ properties: compatible: items: - enum: + - qcom,milos-tcsr - qcom,sar2130p-tcsr - qcom,sm8550-tcsr - qcom,sm8650-tcsr diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8650-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8650-gcc.yaml index 976f29cce809..c7143e2abc80 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8650-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8650-gcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on SM8650 - See also:: include/dt-bindings/clock/qcom,sm8650-gcc.h + See also: include/dt-bindings/clock/qcom,sm8650-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/qcom,x1e80100-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,x1e80100-gcc.yaml index 28797d0c5d8d..68dde0720c71 100644 --- a/Documentation/devicetree/bindings/clock/qcom,x1e80100-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,x1e80100-gcc.yaml @@ -13,7 +13,7 @@ description: | Qualcomm global clock control module provides the clocks, resets and power domains on X1E80100 - See also:: include/dt-bindings/clock/qcom,x1e80100-gcc.h + See also: include/dt-bindings/clock/qcom,x1e80100-gcc.h properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/raspberrypi,rp1-clocks.yaml b/Documentation/devicetree/bindings/clock/raspberrypi,rp1-clocks.yaml new file mode 100644 index 000000000000..cc4491f7ee5f --- /dev/null +++ b/Documentation/devicetree/bindings/clock/raspberrypi,rp1-clocks.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/raspberrypi,rp1-clocks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RaspberryPi RP1 clock generator + +maintainers: + - A. della Porta + +description: | + The RP1 contains a clock generator designed as three PLLs (CORE, AUDIO, + VIDEO), and each PLL output can be programmed through dividers to generate + the clocks to drive the sub-peripherals embedded inside the chipset. + + Link to datasheet: + https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf + +properties: + compatible: + const: raspberrypi,rp1-clocks + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + description: + The available clocks are defined in + include/dt-bindings/clock/raspberrypi,rp1-clocks.h. + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - '#clock-cells' + - clocks + +additionalProperties: false + +examples: + - | + #include + + rp1 { + #address-cells = <2>; + #size-cells = <2>; + + clocks@c040018000 { + compatible = "raspberrypi,rp1-clocks"; + reg = <0xc0 0x40018000 0x0 0x10038>; + #clock-cells = <1>; + clocks = <&clk_rp1_xosc>; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml index 77ce3615c65a..bc2fd3761328 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml @@ -52,9 +52,16 @@ properties: - renesas,r8a779f0-cpg-mssr # R-Car S4-8 - renesas,r8a779g0-cpg-mssr # R-Car V4H - renesas,r8a779h0-cpg-mssr # R-Car V4M + - renesas,r9a09g077-cpg-mssr # RZ/T2H + - renesas,r9a09g087-cpg-mssr # RZ/N2H reg: - maxItems: 1 + minItems: 1 + items: + - description: base address of register block 0 + - description: base address of register block 1 + description: base addresses of clock controller. Some controllers + (like r9a09g077) use two blocks instead of a single one. clocks: minItems: 1 @@ -92,16 +99,6 @@ properties: the datasheet. const: 1 -if: - not: - properties: - compatible: - items: - enum: - - renesas,r7s9210-cpg-mssr -then: - required: - - '#reset-cells' required: - compatible @@ -111,6 +108,36 @@ required: - '#clock-cells' - '#power-domain-cells' +allOf: + - if: + properties: + compatible: + contains: + enum: + - renesas,r9a09g077-cpg-mssr + - renesas,r9a09g087-cpg-mssr + then: + properties: + reg: + minItems: 2 + clock-names: + items: + - const: extal + else: + properties: + reg: + maxItems: 1 + - if: + not: + properties: + compatible: + items: + enum: + - renesas,r7s9210-cpg-mssr + then: + required: + - '#reset-cells' + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml index 0440f23da059..8c18616e5c4d 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml @@ -57,8 +57,7 @@ properties: can be power-managed through Module Standby should refer to the CPG device node in their "power-domains" property, as documented by the generic PM Domain bindings in Documentation/devicetree/bindings/power/power-domain.yaml. - The power domain specifiers defined in could - be used to reference individual CPG power domains. + const: 0 '#reset-cells': description: @@ -77,21 +76,6 @@ required: additionalProperties: false -allOf: - - if: - properties: - compatible: - contains: - const: renesas,r9a08g045-cpg - then: - properties: - '#power-domain-cells': - const: 1 - else: - properties: - '#power-domain-cells': - const: 0 - examples: - | cpg: clock-controller@11010000 { diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml index 6961a68098f4..72f59db73f76 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml @@ -32,23 +32,24 @@ description: | properties: compatible: enum: - - samsung,exynosautov920-cmu-top - samsung,exynosautov920-cmu-cpucl0 - samsung,exynosautov920-cmu-cpucl1 - samsung,exynosautov920-cmu-cpucl2 - - samsung,exynosautov920-cmu-peric0 - - samsung,exynosautov920-cmu-peric1 - - samsung,exynosautov920-cmu-misc - samsung,exynosautov920-cmu-hsi0 - samsung,exynosautov920-cmu-hsi1 + - samsung,exynosautov920-cmu-hsi2 + - samsung,exynosautov920-cmu-misc + - samsung,exynosautov920-cmu-peric0 + - samsung,exynosautov920-cmu-peric1 + - samsung,exynosautov920-cmu-top clocks: minItems: 1 - maxItems: 4 + maxItems: 5 clock-names: minItems: 1 - maxItems: 4 + maxItems: 5 "#clock-cells": const: 1 @@ -201,6 +202,30 @@ allOf: - const: usbdrd - const: mmc_card + - if: + properties: + compatible: + contains: + const: samsung,exynosautov920-cmu-hsi2 + + then: + properties: + clocks: + items: + - description: External reference clock (38.4 MHz) + - description: CMU_HSI2 NOC clock (from CMU_TOP) + - description: CMU_HSI2 NOC UFS clock (from CMU_TOP) + - description: CMU_HSI2 UFS EMBD clock (from CMU_TOP) + - description: CMU_HSI2 ETHERNET clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: noc + - const: ufs + - const: embd + - const: ethernet + required: - compatible - "#clock-cells" diff --git a/Documentation/devicetree/bindings/clock/ti/autoidle.txt b/Documentation/devicetree/bindings/clock/ti/autoidle.txt deleted file mode 100644 index 05645a10a9e3..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/autoidle.txt +++ /dev/null @@ -1,37 +0,0 @@ -Binding for Texas Instruments autoidle clock. - -This binding uses the common clock binding[1]. It assumes a register mapped -clock which can be put to idle automatically by hardware based on the usage -and a configuration bit setting. Autoidle clock is never an individual -clock, it is always a derivative of some basic clock like a gate, divider, -or fixed-factor. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- reg : offset for the register controlling the autoidle -- ti,autoidle-shift : bit shift of the autoidle enable bit -- ti,invert-autoidle-bit : autoidle is enabled by setting the bit to 0 - -Examples: - dpll_core_m4_ck: dpll_core_m4_ck { - #clock-cells = <0>; - compatible = "ti,divider-clock"; - clocks = <&dpll_core_x2_ck>; - ti,max-div = <31>; - ti,autoidle-shift = <8>; - reg = <0x2d38>; - ti,index-starts-at-one; - ti,invert-autoidle-bit; - }; - - dpll_usb_clkdcoldo_ck: dpll_usb_clkdcoldo_ck { - #clock-cells = <0>; - compatible = "ti,fixed-factor-clock"; - clocks = <&dpll_usb_ck>; - ti,clock-div = <1>; - ti,autoidle-shift = <8>; - reg = <0x01b4>; - ti,clock-mult = <1>; - ti,invert-autoidle-bit; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/fixed-factor-clock.txt b/Documentation/devicetree/bindings/clock/ti/fixed-factor-clock.txt deleted file mode 100644 index dc69477b6e98..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/fixed-factor-clock.txt +++ /dev/null @@ -1,42 +0,0 @@ -Binding for TI fixed factor rate clock sources. - -This binding uses the common clock binding[1], and also uses the autoidle -support from TI autoidle clock [2]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt -[2] Documentation/devicetree/bindings/clock/ti/autoidle.txt - -Required properties: -- compatible : shall be "ti,fixed-factor-clock". -- #clock-cells : from common clock binding; shall be set to 0. -- ti,clock-div: fixed divider. -- ti,clock-mult: fixed multiplier. -- clocks: parent clock. - -Optional properties: -- clock-output-names : from common clock binding. -- ti,autoidle-shift: bit shift of the autoidle enable bit for the clock, - see [2] -- reg: offset for the autoidle register of this clock, see [2] -- ti,invert-autoidle-bit: autoidle is enabled by setting the bit to 0, see [2] -- ti,set-rate-parent: clk_set_rate is propagated to parent - -Example: - clock { - compatible = "ti,fixed-factor-clock"; - clocks = <&parentclk>; - #clock-cells = <0>; - ti,clock-div = <2>; - ti,clock-mult = <1>; - }; - - dpll_usb_clkdcoldo_ck: dpll_usb_clkdcoldo_ck { - #clock-cells = <0>; - compatible = "ti,fixed-factor-clock"; - clocks = <&dpll_usb_ck>; - ti,clock-div = <1>; - ti,autoidle-shift = <8>; - reg = <0x01b4>; - ti,clock-mult = <1>; - ti,invert-autoidle-bit; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,autoidle.yaml b/Documentation/devicetree/bindings/clock/ti/ti,autoidle.yaml new file mode 100644 index 000000000000..ed1bf182b64d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,autoidle.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,autoidle.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI autoidle clock + +maintainers: + - Tero Kristo + - Sukrut Bellary + +description: + Some clocks in TI SoC support the autoidle feature. These properties are + applicable only if the clock supports autoidle feature. It assumes a register + mapped clock which can be put to idle automatically by hardware based on + usage and configuration bit setting. Autoidle clock is never an individual + clock, it is always a derivative of some basic clock like a gate, divider, or + fixed-factor. + +properties: + ti,autoidle-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + bit shift of the autoidle enable bit for the clock + maximum: 31 + default: 0 + + ti,invert-autoidle-bit: + type: boolean + description: + autoidle is enabled by setting the bit to 0 + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/clock/ti/ti,divider-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,divider-clock.yaml index 3fbe236eb565..6729fcb839d2 100644 --- a/Documentation/devicetree/bindings/clock/ti/ti,divider-clock.yaml +++ b/Documentation/devicetree/bindings/clock/ti/ti,divider-clock.yaml @@ -55,9 +55,10 @@ description: | is missing it is the same as supplying a zero shift. This binding can also optionally provide support to the hardware autoidle - feature, see [1]. + feature. - [1] Documentation/devicetree/bindings/clock/ti/autoidle.txt +allOf: + - $ref: ti,autoidle.yaml# properties: compatible: @@ -97,7 +98,6 @@ properties: minimum: 1 default: 1 - ti,max-div: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -116,20 +116,6 @@ properties: valid divisor programming must be a power of two, only valid if ti,dividers is not defined. - ti,autoidle-shift: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - bit shift of the autoidle enable bit for the clock, - see [1]. - maximum: 31 - default: 0 - - ti,invert-autoidle-bit: - type: boolean - description: - autoidle is enabled by setting the bit to 0, - see [1] - ti,set-rate-parent: type: boolean description: @@ -156,7 +142,7 @@ required: - clocks - reg -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/clock/ti/ti,fixed-factor-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,fixed-factor-clock.yaml new file mode 100644 index 000000000000..7a63b0992976 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,fixed-factor-clock.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,fixed-factor-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI fixed factor rate clock sources + +maintainers: + - Tero Kristo + - Sukrut Bellary + +description: + This consists of a divider and a multiplier used to generate a fixed rate + clock. This also uses the autoidle support from TI autoidle clock. + +allOf: + - $ref: ti,autoidle.yaml# + +properties: + compatible: + const: ti,fixed-factor-clock + + "#clock-cells": + const: 0 + + reg: + maxItems: 1 + + ti,clock-div: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Fixed divider + minimum: 1 + + ti,clock-mult: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Fixed multiplier + minimum: 1 + + clocks: + maxItems: 1 + + clock-output-names: + maxItems: 1 + + ti,set-rate-parent: + description: + Propagate to parent clock + type: boolean + +required: + - compatible + - clocks + - "#clock-cells" + - ti,clock-mult + - ti,clock-div + +unevaluatedProperties: false + +examples: + - | + bus{ + #address-cells = <1>; + #size-cells = <0>; + + clock@1b4 { + compatible = "ti,fixed-factor-clock"; + reg = <0x1b4>; + clocks = <&dpll_usb_ck>; + #clock-cells = <0>; + ti,clock-mult = <1>; + ti,clock-div = <1>; + ti,autoidle-shift = <8>; + ti,invert-autoidle-bit; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/xgene.txt b/Documentation/devicetree/bindings/clock/xgene.txt deleted file mode 100644 index 8233e771711b..000000000000 --- a/Documentation/devicetree/bindings/clock/xgene.txt +++ /dev/null @@ -1,131 +0,0 @@ -Device Tree Clock bindings for APM X-Gene - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be one of the following: - "apm,xgene-socpll-clock" - for a X-Gene SoC PLL clock - "apm,xgene-pcppll-clock" - for a X-Gene PCP PLL clock - "apm,xgene-pmd-clock" - for a X-Gene PMD clock - "apm,xgene-device-clock" - for a X-Gene device clock - "apm,xgene-socpll-v2-clock" - for a X-Gene SoC PLL v2 clock - "apm,xgene-pcppll-v2-clock" - for a X-Gene PCP PLL v2 clock - -Required properties for SoC or PCP PLL clocks: -- reg : shall be the physical PLL register address for the pll clock. -- clocks : shall be the input parent clock phandle for the clock. This should - be the reference clock. -- #clock-cells : shall be set to 1. -- clock-output-names : shall be the name of the PLL referenced by derive - clock. -Optional properties for PLL clocks: -- clock-names : shall be the name of the PLL. If missing, use the device name. - -Required properties for PMD clocks: -- reg : shall be the physical register address for the pmd clock. -- clocks : shall be the input parent clock phandle for the clock. -- #clock-cells : shall be set to 1. -- clock-output-names : shall be the name of the clock referenced by derive - clock. -Optional properties for PLL clocks: -- clock-names : shall be the name of the clock. If missing, use the device name. - -Required properties for device clocks: -- reg : shall be a list of address and length pairs describing the CSR - reset and/or the divider. Either may be omitted, but at least - one must be present. - - reg-names : shall be a string list describing the reg resource. This - may include "csr-reg" and/or "div-reg". If this property - is not present, the reg property is assumed to describe - only "csr-reg". -- clocks : shall be the input parent clock phandle for the clock. -- #clock-cells : shall be set to 1. -- clock-output-names : shall be the name of the device referenced. -Optional properties for device clocks: -- clock-names : shall be the name of the device clock. If missing, use the - device name. -- csr-offset : Offset to the CSR reset register from the reset address base. - Default is 0. -- csr-mask : CSR reset mask bit. Default is 0xF. -- enable-offset : Offset to the enable register from the reset address base. - Default is 0x8. -- enable-mask : CSR enable mask bit. Default is 0xF. -- divider-offset : Offset to the divider CSR register from the divider base. - Default is 0x0. -- divider-width : Width of the divider register. Default is 0. -- divider-shift : Bit shift of the divider register. Default is 0. - -For example: - - pcppll: pcppll@17000100 { - compatible = "apm,xgene-pcppll-clock"; - #clock-cells = <1>; - clocks = <&refclk 0>; - clock-names = "pcppll"; - reg = <0x0 0x17000100 0x0 0x1000>; - clock-output-names = "pcppll"; - type = <0>; - }; - - pmd0clk: pmd0clk@7e200200 { - compatible = "apm,xgene-pmd-clock"; - #clock-cells = <1>; - clocks = <&pmdpll 0>; - reg = <0x0 0x7e200200 0x0 0x10>; - clock-output-names = "pmd0clk"; - }; - - socpll: socpll@17000120 { - compatible = "apm,xgene-socpll-clock"; - #clock-cells = <1>; - clocks = <&refclk 0>; - clock-names = "socpll"; - reg = <0x0 0x17000120 0x0 0x1000>; - clock-output-names = "socpll"; - type = <1>; - }; - - qmlclk: qmlclk { - compatible = "apm,xgene-device-clock"; - #clock-cells = <1>; - clocks = <&socplldiv2 0>; - clock-names = "qmlclk"; - reg = <0x0 0x1703C000 0x0 0x1000>; - reg-name = "csr-reg"; - clock-output-names = "qmlclk"; - }; - - ethclk: ethclk { - compatible = "apm,xgene-device-clock"; - #clock-cells = <1>; - clocks = <&socplldiv2 0>; - clock-names = "ethclk"; - reg = <0x0 0x17000000 0x0 0x1000>; - reg-names = "div-reg"; - divider-offset = <0x238>; - divider-width = <0x9>; - divider-shift = <0x0>; - clock-output-names = "ethclk"; - }; - - apbclk: apbclk { - compatible = "apm,xgene-device-clock"; - #clock-cells = <1>; - clocks = <&ahbclk 0>; - clock-names = "apbclk"; - reg = <0x0 0x1F2AC000 0x0 0x1000 - 0x0 0x1F2AC000 0x0 0x1000>; - reg-names = "csr-reg", "div-reg"; - csr-offset = <0x0>; - csr-mask = <0x200>; - enable-offset = <0x8>; - enable-mask = <0x200>; - divider-offset = <0x10>; - divider-width = <0x2>; - divider-shift = <0x0>; - flags = <0x8>; - clock-output-names = "apbclk"; - }; - diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml index 7dc0748444fd..19010f90198a 100644 --- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml +++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml @@ -15,7 +15,9 @@ properties: oneOf: - const: atmel,at91sam9g46-aes - items: - - const: microchip,sam9x7-aes + - enum: + - microchip,sam9x7-aes + - microchip,sama7d65-aes - const: atmel,at91sam9g46-aes reg: diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml index d378c53314dd..39e076b275b3 100644 --- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml +++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml @@ -15,7 +15,9 @@ properties: oneOf: - const: atmel,at91sam9g46-sha - items: - - const: microchip,sam9x7-sha + - enum: + - microchip,sam9x7-sha + - microchip,sama7d65-sha - const: atmel,at91sam9g46-sha reg: diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml index 6a441f79efea..6f16008c4251 100644 --- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml +++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml @@ -15,7 +15,9 @@ properties: oneOf: - const: atmel,at91sam9g46-tdes - items: - - const: microchip,sam9x7-tdes + - enum: + - microchip,sam9x7-tdes + - microchip,sama7d65-tdes - const: atmel,at91sam9g46-tdes reg: diff --git a/Documentation/devicetree/bindings/crypto/fsl,sec-v4.0.yaml b/Documentation/devicetree/bindings/crypto/fsl,sec-v4.0.yaml index 75afa441e019..dcc755d2709a 100644 --- a/Documentation/devicetree/bindings/crypto/fsl,sec-v4.0.yaml +++ b/Documentation/devicetree/bindings/crypto/fsl,sec-v4.0.yaml @@ -46,6 +46,8 @@ properties: - items: - enum: - fsl,imx6ul-caam + - fsl,imx8qm-caam + - fsl,imx8qxp-caam - fsl,sec-v5.0 - const: fsl,sec-v4.0 - const: fsl,sec-v4.0 @@ -77,6 +79,9 @@ properties: interrupts: maxItems: 1 + power-domains: + maxItems: 1 + fsl,sec-era: description: Defines the 'ERA' of the SEC device. $ref: /schemas/types.yaml#/definitions/uint32 @@ -106,7 +111,10 @@ patternProperties: - const: fsl,sec-v5.0-job-ring - const: fsl,sec-v4.0-job-ring - items: - - const: fsl,sec-v5.0-job-ring + - enum: + - fsl,imx8qm-job-ring + - fsl,imx8qxp-job-ring + - fsl,sec-v5.0-job-ring - const: fsl,sec-v4.0-job-ring - const: fsl,sec-v4.0-job-ring @@ -116,6 +124,9 @@ patternProperties: interrupts: maxItems: 1 + power-domains: + maxItems: 1 + fsl,liodn: description: Specifies the LIODN to be used in conjunction with the ppid-to-liodn @@ -125,6 +136,20 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/uint32-array items: - maximum: 0xfff + allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-job-ring + - fsl,imx8qxp-job-ring + then: + required: + - power-domains + else: + properties: + power-domains: false '^rtic@[0-9a-f]+$': type: object @@ -212,6 +237,20 @@ required: - reg - ranges +if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-caam + - fsl,imx8qxp-caam +then: + required: + - power-domains +else: + properties: + power-domains: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/crypto/omap-aes.txt b/Documentation/devicetree/bindings/crypto/omap-aes.txt deleted file mode 100644 index fd9717653cbb..000000000000 --- a/Documentation/devicetree/bindings/crypto/omap-aes.txt +++ /dev/null @@ -1,31 +0,0 @@ -OMAP SoC AES crypto Module - -Required properties: - -- compatible : Should contain entries for this and backward compatible - AES versions: - - "ti,omap2-aes" for OMAP2. - - "ti,omap3-aes" for OMAP3. - - "ti,omap4-aes" for OMAP4 and AM33XX. - Note that the OMAP2 and 3 versions are compatible (OMAP3 supports - more algorithms) but they are incompatible with OMAP4. -- ti,hwmods: Name of the hwmod associated with the AES module -- reg : Offset and length of the register set for the module -- interrupts : the interrupt-specifier for the AES module. - -Optional properties: -- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt -- dma-names: DMA request names should include "tx" and "rx" if present. - -Example: - /* AM335x */ - aes: aes@53500000 { - compatible = "ti,omap4-aes"; - ti,hwmods = "aes"; - reg = <0x53500000 0xa0>; - interrupts = <102>; - dmas = <&edma 6>, - <&edma 5>; - dma-names = "tx", "rx"; - }; diff --git a/Documentation/devicetree/bindings/crypto/omap-des.txt b/Documentation/devicetree/bindings/crypto/omap-des.txt deleted file mode 100644 index e8c63bf2e16d..000000000000 --- a/Documentation/devicetree/bindings/crypto/omap-des.txt +++ /dev/null @@ -1,30 +0,0 @@ -OMAP SoC DES crypto Module - -Required properties: - -- compatible : Should contain "ti,omap4-des" -- ti,hwmods: Name of the hwmod associated with the DES module -- reg : Offset and length of the register set for the module -- interrupts : the interrupt-specifier for the DES module -- clocks : A phandle to the functional clock node of the DES module - corresponding to each entry in clock-names -- clock-names : Name of the functional clock, should be "fck" - -Optional properties: -- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt - Each entry corresponds to an entry in dma-names -- dma-names: DMA request names should include "tx" and "rx" if present - -Example: - /* DRA7xx SoC */ - des: des@480a5000 { - compatible = "ti,omap4-des"; - ti,hwmods = "des"; - reg = <0x480a5000 0xa0>; - interrupts = ; - dmas = <&sdma 117>, <&sdma 116>; - dma-names = "tx", "rx"; - clocks = <&l3_iclk_div>; - clock-names = "fck"; - }; diff --git a/Documentation/devicetree/bindings/crypto/ti,omap2-aes.yaml b/Documentation/devicetree/bindings/crypto/ti,omap2-aes.yaml new file mode 100644 index 000000000000..90e92050ad2e --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/ti,omap2-aes.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ti,omap2-aes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OMAP SoC AES crypto Module + +maintainers: + - Aaro Koskinen + - Andreas Kemnade + - Kevin Hilman + - Roger Quadros + - Tony Lindgren + +properties: + compatible: + enum: + - ti,omap2-aes + - ti,omap3-aes + - ti,omap4-aes + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + maxItems: 2 + + dma-names: + items: + - const: tx + - const: rx + + ti,hwmods: + description: Name of the hwmod associated with the AES module + const: aes + deprecated: true + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + aes@53500000 { + compatible = "ti,omap4-aes"; + reg = <0x53500000 0xa0>; + interrupts = <102>; + dmas = <&edma 6>, + <&edma 5>; + dma-names = "tx", "rx"; + }; diff --git a/Documentation/devicetree/bindings/crypto/ti,omap4-des.yaml b/Documentation/devicetree/bindings/crypto/ti,omap4-des.yaml new file mode 100644 index 000000000000..f02f1e141218 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/ti,omap4-des.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ti,omap4-des.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OMAP4 DES crypto Module + +maintainers: + - Aaro Koskinen + - Andreas Kemnade + - Kevin Hilman + - Roger Quadros + - Tony Lindgren + +properties: + compatible: + const: ti,omap4-des + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + maxItems: 2 + + dma-names: + items: + - const: tx + - const: rx + + clocks: + maxItems: 1 + + clock-names: + items: + - const: fck + +dependencies: + dmas: [ dma-names ] + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + des@480a5000 { + compatible = "ti,omap4-des"; + reg = <0x480a5000 0xa0>; + interrupts = ; + clocks = <&l3_iclk_div>; + clock-names = "fck"; + dmas = <&sdma 117>, <&sdma 116>; + dma-names = "tx", "rx"; + }; diff --git a/Documentation/devicetree/bindings/display/arm,pl11x.yaml b/Documentation/devicetree/bindings/display/arm,pl11x.yaml index 6cc9045e5c68..a43c1c9d9113 100644 --- a/Documentation/devicetree/bindings/display/arm,pl11x.yaml +++ b/Documentation/devicetree/bindings/display/arm,pl11x.yaml @@ -78,6 +78,9 @@ properties: If not present, the memory interface is fast enough to handle all possible video modes. + resets: + maxItems: 1 + port: $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml index 43cf4df9811a..9ef587d46506 100644 --- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml +++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml @@ -28,6 +28,8 @@ properties: - enum: - adi,adv7123 - dumb-vga-dac + - radxa,ra620 + - realtek,rtd2171 - ti,opa362 - ti,ths8134 - ti,ths8135 diff --git a/Documentation/devicetree/bindings/display/bridge/solomon,ssd2825.yaml b/Documentation/devicetree/bindings/display/bridge/solomon,ssd2825.yaml new file mode 100644 index 000000000000..e2d293d623b8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/solomon,ssd2825.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/solomon,ssd2825.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Solomon SSD2825 RGB to MIPI-DSI bridge + +maintainers: + - Svyatoslav Ryhel + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: solomon,ssd2825 + + reg: + maxItems: 1 + + reset-gpios: true + + dvdd-supply: + description: Regulator for 1.2V digital power supply. + + avdd-supply: + description: Regulator for 1.2V analog power supply. + + vddio-supply: + description: Regulator for 1.8V IO power supply. + + spi-max-frequency: + maximum: 1000000 + + spi-cpha: true + spi-cpol: true + + clocks: + maxItems: 1 + description: Reference TX_CLK used before PLL is locked. + + solomon,hs-zero-delay-ns: + description: + HS zero delay period + minimum: 0 + maximum: 1700 + default: 133 + + solomon,hs-prep-delay-ns: + description: + HS prep delay period + minimum: 0 + maximum: 1728 + default: 40 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Video port for RGB input + + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + properties: + bus-width: + enum: [ 16, 18, 24 ] + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Video port for DSI output (panel or connector) + + required: + - port@0 + - port@1 + +required: + - compatible + - ports + +additionalProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dsi@2 { + compatible = "solomon,ssd2825"; + reg = <2>; + + spi-max-frequency = <1000000>; + + spi-cpha; + spi-cpol; + + reset-gpios = <&gpio 114 GPIO_ACTIVE_LOW>; + + dvdd-supply = <&vdd_1v2>; + avdd-supply = <&vdd_1v2>; + vddio-supply = <&vdd_1v8_io>; + + solomon,hs-zero-delay-ns = <300>; + solomon,hs-prep-delay-ns = <65>; + + clocks = <&ssd2825_tx_clk>; + + 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>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml index 9b5f3f3eab19..e69b6343a8eb 100644 --- a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml @@ -118,15 +118,11 @@ $defs: ti,lvds-vod-swing-clock-microvolt: description: LVDS diferential output voltage for clock lanes in microvolts. - $ref: /schemas/types.yaml#/definitions/uint32-array - minItems: 2 maxItems: 2 ti,lvds-vod-swing-data-microvolt: description: LVDS diferential output voltage for data lanes in microvolts. - $ref: /schemas/types.yaml#/definitions/uint32-array - minItems: 2 maxItems: 2 allOf: diff --git a/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml b/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml new file mode 100644 index 000000000000..5e8498c8303d --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/waveshare,dsi2dpi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Waveshare MIPI-DSI to DPI Converter bridge + +maintainers: + - Joseph Guo + +description: + Waveshare bridge board is part of Waveshare panel which converts DSI to DPI. + +properties: + compatible: + const: waveshare,dsi2dpi + + reg: + maxItems: 1 + description: base I2C address of the device + + power-supply: true + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Video port for MIPI DSI input + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: array of physical DSI data lane indexes. + items: + - const: 1 + - const: 2 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Video port for MIPI DPI output panel. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - ports + - power-supply + +additionalProperties: false + +examples: + - | + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + bridge@45 { + compatible = "waveshare,dsi2dpi"; + reg = <0x45>; + power-supply = <®_3p3v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + waveshare_from_dsim: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&dsim_to_waveshare>; + }; + }; + + port@1 { + reg = <1>; + + waveshare_to_panel: endpoint { + remote-endpoint = <&panel_to_waveshare>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/fsl,dcu.txt b/Documentation/devicetree/bindings/display/fsl,dcu.txt deleted file mode 100644 index 63ec2a624aa9..000000000000 --- a/Documentation/devicetree/bindings/display/fsl,dcu.txt +++ /dev/null @@ -1,34 +0,0 @@ -Device Tree bindings for Freescale DCU DRM Driver - -Required properties: -- compatible: Should be one of - * "fsl,ls1021a-dcu". - * "fsl,vf610-dcu". - -- reg: Address and length of the register set for dcu. -- clocks: Handle to "dcu" and "pix" clock (in the order below) - This can be the same clock (e.g. LS1021a) - See ../clocks/clock-bindings.txt for details. -- clock-names: Should be "dcu" and "pix" - See ../clocks/clock-bindings.txt for details. -- big-endian Boolean property, LS1021A DCU registers are big-endian. -- port Video port for the panel output - -Optional properties: -- fsl,tcon: The phandle to the timing controller node. - -Examples: -dcu: dcu@2ce0000 { - compatible = "fsl,ls1021a-dcu"; - reg = <0x0 0x2ce0000 0x0 0x10000>; - clocks = <&platform_clk 0>, <&platform_clk 0>; - clock-names = "dcu", "pix"; - big-endian; - fsl,tcon = <&tcon>; - - port { - dcu_out: endpoint { - remote-endpoint = <&panel_out>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/display/fsl,lcdif.yaml b/Documentation/devicetree/bindings/display/fsl,lcdif.yaml index 8e3a98aeec32..2dd0411ec651 100644 --- a/Documentation/devicetree/bindings/display/fsl,lcdif.yaml +++ b/Documentation/devicetree/bindings/display/fsl,lcdif.yaml @@ -71,12 +71,23 @@ properties: $ref: /schemas/graph.yaml#/properties/port description: The LCDIF output port + display: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to display panel + deprecated: true + + display0: + $ref: panel/panel-common.yaml# + deprecated: true + + lcd-supply: + deprecated: true + required: - compatible - reg - clocks - interrupts - - port additionalProperties: false @@ -175,6 +186,12 @@ allOf: properties: dmas: false dma-names: false + display: false + display0: false + lcd-supply: false + + required: + - port examples: - | diff --git a/Documentation/devicetree/bindings/display/fsl,ls1021a-dcu.yaml b/Documentation/devicetree/bindings/display/fsl,ls1021a-dcu.yaml new file mode 100644 index 000000000000..72d14babe993 --- /dev/null +++ b/Documentation/devicetree/bindings/display/fsl,ls1021a-dcu.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/fsl,ls1021a-dcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale DCU DRM Driver + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - fsl,ls1021a-dcu + - fsl,vf610-dcu + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: dcu + - const: pix + + big-endian: true + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Video port for the panel output + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + fsl,tcon: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle to the timing controller node. + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + display-controller@2ce0000 { + compatible = "fsl,ls1021a-dcu"; + reg = <0x2ce0000 0x10000>; + clocks = <&platform_clk 0>, <&platform_clk 0>; + clock-names = "dcu", "pix"; + big-endian; + fsl,tcon = <&tcon>; + + port { + endpoint { + remote-endpoint = <&panel_out>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/himax,hx8357.yaml b/Documentation/devicetree/bindings/display/himax,hx8357.yaml new file mode 100644 index 000000000000..34c3b89bf003 --- /dev/null +++ b/Documentation/devicetree/bindings/display/himax,hx8357.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/himax,hx8357.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Himax HX8357D display panel + +description: + Display panels using a Himax HX8357D controller in SPI + mode, such as the Adafruit 3.5" TFT for Raspberry Pi. + +maintainers: + - Frank Li + +properties: + compatible: + oneOf: + - items: + - enum: + - adafruit,yx350hv15 + - himax,hx8357b + - const: himax,hx8357 + - items: + - enum: + - himax,hx8369a + - const: himax,hx8369 + + reg: + maxItems: 1 + + dc-gpios: + maxItems: 1 + description: D/C pin + + rotation: + enum: [0, 90, 180, 270] + + backlight: + description: + phandle of the backlight device attached to the panel + + im-gpios: + maxItems: 3 + + reset-gpios: + maxItems: 1 + + spi-cpha: true + + spi-cpol: true + +required: + - compatible + - reg + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + display@0 { + compatible = "adafruit,yx350hv15", "himax,hx8357"; + reg = <0>; + spi-max-frequency = <32000000>; + dc-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; + rotation = <90>; + backlight = <&backlight>; + }; + }; diff --git a/Documentation/devicetree/bindings/display/himax,hx8357d.txt b/Documentation/devicetree/bindings/display/himax,hx8357d.txt deleted file mode 100644 index e641f664763d..000000000000 --- a/Documentation/devicetree/bindings/display/himax,hx8357d.txt +++ /dev/null @@ -1,26 +0,0 @@ -Himax HX8357D display panels - -This binding is for display panels using a Himax HX8357D controller in SPI -mode, such as the Adafruit 3.5" TFT for Raspberry Pi. - -Required properties: -- compatible: "adafruit,yx350hv15", "himax,hx8357d" -- dc-gpios: D/C pin -- reg: address of the panel on the SPI bus - -The node for this driver must be a child node of a SPI controller, hence -all mandatory properties described in ../spi/spi-bus.txt must be specified. - -Optional properties: -- rotation: panel rotation in degrees counter clockwise (0,90,180,270) -- backlight: phandle of the backlight device attached to the panel - -Example: - display@0{ - compatible = "adafruit,yx350hv15", "himax,hx8357d"; - reg = <0>; - spi-max-frequency = <32000000>; - dc-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; - rotation = <90>; - backlight = <&backlight>; - }; diff --git a/Documentation/devicetree/bindings/display/mayqueen,pixpaper.yaml b/Documentation/devicetree/bindings/display/mayqueen,pixpaper.yaml new file mode 100644 index 000000000000..cd27f8ba5ae1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/mayqueen,pixpaper.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/mayqueen,pixpaper.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mayqueen Pixpaper e-ink display panel + +maintainers: + - LiangCheng Wang + +description: + The Pixpaper is an e-ink display panel controlled via an SPI interface. + The panel has a resolution of 122x250 pixels and requires GPIO pins for + reset, busy, and data/command control. + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: mayqueen,pixpaper + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 1000000 + default: 1000000 + + reset-gpios: + maxItems: 1 + + busy-gpios: + maxItems: 1 + + dc-gpios: + maxItems: 1 + +required: + - compatible + - reg + - reset-gpios + - busy-gpios + - dc-gpios + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + display@0 { + compatible = "mayqueen,pixpaper"; + reg = <0>; + spi-max-frequency = <1000000>; + reset-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; + busy-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>; + dc-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml index 75ce92f4a5fd..274f590807ca 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml @@ -45,6 +45,9 @@ properties: '#sound-dai-cells': const: 0 + aux-bus: + $ref: /schemas/display/dp-aux-bus.yaml# + ports: $ref: /schemas/graph.yaml#/properties/ports properties: diff --git a/Documentation/devicetree/bindings/display/msm/qcom,mdp5.yaml b/Documentation/devicetree/bindings/display/msm/qcom,mdp5.yaml index e153f8d26e7a..2735c78b0b67 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,mdp5.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,mdp5.yaml @@ -60,7 +60,6 @@ properties: - const: bus - const: core - const: vsync - - const: lut - const: tbu - const: tbu_rt # MSM8996 has additional iommu clock diff --git a/Documentation/devicetree/bindings/display/panel/hydis,hv101hd1.yaml b/Documentation/devicetree/bindings/display/panel/hydis,hv101hd1.yaml new file mode 100644 index 000000000000..f429e84ee65d --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/hydis,hv101hd1.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/hydis,hv101hd1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hydis HV101HD1 DSI Display Panel + +maintainers: + - Svyatoslav Ryhel + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: hydis,hv101hd1 + + reg: + maxItems: 1 + + vdd-supply: true + vio-supply: true + + backlight: true + port: true + +required: + - compatible + - vdd-supply + - vio-supply + - backlight + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "hydis,hv101hd1"; + reg = <0>; + + vdd-supply = <&vdd_lcd>; + vio-supply = <&vddio_lcd>; + + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml index a51af61d4846..434cc6af9c95 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml @@ -18,6 +18,7 @@ properties: - enum: - ampire,am8001280g - bananapi,lhr050h41 + - bestar,bsd1218-a101kl68 - feixin,k101-im2byl02 - raspberrypi,dsi-7inch - startek,kd050hdfia020 diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml index fcb5834f799a..4388d5375851 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml @@ -41,11 +41,15 @@ properties: - enum: # Admatec 9904379 10.1" 1024x600 LVDS panel - admatec,9904379 + # Ampire AMP19201200B5TZQW-T03 10.1" WUXGA (1920x1200) color TFT LCD panel + - ampire,amp19201200b5tzqw-t03 - auo,b101ew05 # AUO G084SN05 V9 8.4" 800x600 LVDS panel - auo,g084sn05 # Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel - chunghwa,claa070wp03xg + # EDT ETML0700Z8DHA 7.0" Full HD (1920x1080) color TFT LCD LVDS panel + - edt,etml0700z8dha # EDT ETML0700Z9NDHA 7.0" WSVGA (1024x600) color TFT LCD LVDS panel - edt,etml0700z9ndha # HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 1ac1f0219079..5e8dc9afa1fd 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -236,6 +236,8 @@ properties: - okaya,rs800480t-7x0gp # Olimex 4.3" TFT LCD panel - olimex,lcd-olinuxino-43-ts + # Olimex 5.0" TFT LCD panel + - olimex,lcd-olinuxino-5-cts # On Tat Industrial Company 5" DPI TFT panel. - ontat,kd50g21-40nt-a1 # On Tat Industrial Company 7" DPI TFT panel. @@ -321,6 +323,10 @@ properties: - vivax,tpc9150-panel # VXT 800x480 color TFT LCD panel - vxt,vl050-8048nt-c01 + # Waveshare 13.3" FHD (1920x1080) LCD panel + - waveshare,13.3inch-panel + # Waveshare 7.0" WSVGA (1024x600) LCD panel + - waveshare,7.0inch-c-panel # Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel - winstar,wf35ltiacd # Yes Optoelectronics YTC700TLAG-05-201C 7" TFT LCD panel diff --git a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml index e36659340ef3..ccb574caed28 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml @@ -21,6 +21,10 @@ properties: - enum: # Samsung 13" 3K (2880×1920 pixels) eDP AMOLED panel - samsung,atna30dw01 + # Samsung 14" FHD+ (1920x1200 pixels) eDP AMOLED panel + - samsung,atna40ct06 + # Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel + - samsung,atna40cu11 # Samsung 14" WQXGA+ (2880×1800 pixels) eDP AMOLED panel - samsung,atna40yk20 # Samsung 14.5" WQXGA+ (2880x1800 pixels) eDP AMOLED panel diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml new file mode 100644 index 000000000000..eccfc66d7fe2 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung AMS561RA01 panel with S6E8AA5X01 controller + +maintainers: + - Kaustabh Chakraborty + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: samsung,s6e8aa5x01-ams561ra01 + + reg: + maxItems: 1 + + vdd-supply: + description: core voltage supply + + vci-supply: + description: voltage supply for analog circuits + + reset-gpios: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "samsung,s6e8aa5x01-ams561ra01"; + reg = <0>; + + vdd-supply = <&panel_vdd_reg>; + vci-supply = <&panel_vci_reg>; + + reset-gpios = <&gpd3 4 GPIO_ACTIVE_HIGH>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml new file mode 100644 index 000000000000..a8a008717997 --- /dev/null +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml @@ -0,0 +1,150 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/rockchip/rockchip,dw-dp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip DW DisplayPort Transmitter + +maintainers: + - Andy Yan + +description: | + The Rockchip RK3588 SoC integrates the Synopsys DesignWare DPTX controller + which is compliant with the DisplayPort Specification Version 1.4 with the + following features: + + * DisplayPort 1.4a + * Main Link: 1/2/4 lanes + * Main Link Support 1.62Gbps, 2.7Gbps, 5.4Gbps and 8.1Gbps + * AUX channel 1Mbps + * Single Stream Transport(SST) + * Multistream Transport (MST) + * Type-C support (alternate mode) + * HDCP 2.2, HDCP 1.3 + * Supports up to 8/10 bits per color component + * Supports RBG, YCbCr4:4:4, YCbCr4:2:2, YCbCr4:2:0 + * Pixel clock up to 594MHz + * I2S, SPDIF audio interface + +allOf: + - $ref: /schemas/sound/dai-common.yaml# + +properties: + compatible: + enum: + - rockchip,rk3588-dp + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Peripheral/APB bus clock + - description: DisplayPort AUX clock + - description: HDCP clock + - description: I2S interface clock + - description: SPDIF interfce clock + + clock-names: + items: + - const: apb + - const: aux + - const: hdcp + - const: i2s + - const: spdif + + phys: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Video port for RGB/YUV input. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Video port for DP output. + + required: + - port@0 + - port@1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - phys + - ports + - resets + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dp@fde50000 { + compatible = "rockchip,rk3588-dp"; + reg = <0x0 0xfde50000 0x0 0x4000>; + interrupts = ; + clocks = <&cru PCLK_DP0>, <&cru CLK_AUX16M_0>, + <&cru CLK_DP0>, <&cru MCLK_I2S4_8CH_TX>, + <&cru MCLK_SPDIF2_DP0>; + clock-names = "apb", "aux", "hdcp", "i2s", "spdif"; + assigned-clocks = <&cru CLK_AUX16M_0>; + assigned-clock-rates = <16000000>; + resets = <&cru SRST_DP0>; + phys = <&usbdp_phy0 PHY_TYPE_DP>; + power-domains = <&power RK3588_PD_VO0>; + #sound-dai-cells = <0>; + + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dp0_in_vp2: endpoint { + remote-endpoint = <&vp2_out_dp0>; + }; + }; + + port@1 { + reg = <1>; + + dp0_out_con0: endpoint { + remote-endpoint = <&dp_con0_in>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml index 53384e47b507..75cd1c13fa52 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml @@ -12,6 +12,7 @@ maintainers: properties: compatible: enum: + - rockchip,rk3576-mipi-dsi2 - rockchip,rk3588-mipi-dsi2 reg: diff --git a/Documentation/devicetree/bindings/display/sitronix,st7567.yaml b/Documentation/devicetree/bindings/display/sitronix,st7567.yaml index e8a5b8ad18fe..2eb6d00b5a25 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7567.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7567.yaml @@ -23,6 +23,11 @@ properties: reg: maxItems: 1 + sitronix,inverted: + type: boolean + description: + Display pixels are inverted, i.e. 0 is white and 1 is black. + width-mm: true height-mm: true panel-timing: true diff --git a/Documentation/devicetree/bindings/display/sitronix,st7571.yaml b/Documentation/devicetree/bindings/display/sitronix,st7571.yaml index 4fea782fccd7..b83721eb4b7f 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7571.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7571.yaml @@ -28,6 +28,11 @@ properties: description: Display supports 4-level grayscale. + sitronix,inverted: + type: boolean + description: + Display pixels are inverted, i.e. 0 is white and 1 is black. + reset-gpios: true width-mm: true height-mm: true diff --git a/Documentation/devicetree/bindings/display/sitronix,st7586.txt b/Documentation/devicetree/bindings/display/sitronix,st7586.txt deleted file mode 100644 index 1d0dad1210d3..000000000000 --- a/Documentation/devicetree/bindings/display/sitronix,st7586.txt +++ /dev/null @@ -1,22 +0,0 @@ -Sitronix ST7586 display panel - -Required properties: -- compatible: "lego,ev3-lcd". -- a0-gpios: The A0 signal (since this binding is for serial mode, this is - the pin labeled D1 on the controller, not the pin labeled A0) -- reset-gpios: Reset pin - -The node for this driver must be a child node of a SPI controller, hence -all mandatory properties described in ../spi/spi-bus.txt must be specified. - -Optional properties: -- rotation: panel rotation in degrees counter clockwise (0,90,180,270) - -Example: - display@0{ - compatible = "lego,ev3-lcd"; - reg = <0>; - spi-max-frequency = <10000000>; - a0-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>; - }; diff --git a/Documentation/devicetree/bindings/display/sitronix,st7586.yaml b/Documentation/devicetree/bindings/display/sitronix,st7586.yaml new file mode 100644 index 000000000000..566aaf1aeac8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sitronix,st7586.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sitronix,st7586.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sitronix ST7586 Display Controller + +maintainers: + - David Lechner + +description: + Sitronix ST7586 is a driver and controller for 4-level gray + scale and monochrome dot matrix LCD panels. + https://topwaydisplay.com/sites/default/files/2020-04/ST7586S.pdf + +$ref: panel/panel-common.yaml# + +additionalProperties: false + +properties: + compatible: + const: lego,ev3-lcd + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 50000000 + + a0-gpios: + description: + The A0 signal (for serial mode, this is the pin labeled D1 on the + controller, not the pin labeled A0) + maxItems: 1 + + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - a0-gpios + - reset-gpios + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + display@0 { + compatible = "lego,ev3-lcd"; + reg = <0>; + spi-max-frequency = <10000000>; + a0-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml index 4ebea60b8c5b..8c52fa0ea5f8 100644 --- a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml +++ b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml @@ -25,7 +25,7 @@ properties: maxItems: 1 clocks: - minItems: 2 + maxItems: 2 clock-names: items: diff --git a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml index bc5594d18643..300bf2252c3e 100644 --- a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml +++ b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml @@ -20,7 +20,7 @@ properties: maxItems: 2 clocks: - minItems: 1 + maxItems: 1 clock-names: items: diff --git a/Documentation/devicetree/bindings/display/st,stm32-ltdc.yaml b/Documentation/devicetree/bindings/display/st,stm32-ltdc.yaml index d6ea4d62a2cf..77058a5ccf68 100644 --- a/Documentation/devicetree/bindings/display/st,stm32-ltdc.yaml +++ b/Documentation/devicetree/bindings/display/st,stm32-ltdc.yaml @@ -12,7 +12,10 @@ maintainers: properties: compatible: - const: st,stm32-ltdc + enum: + - st,stm32-ltdc + - st,stm32mp251-ltdc + - st,stm32mp255-ltdc reg: maxItems: 1 @@ -24,15 +27,23 @@ properties: minItems: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 4 clock-names: items: - const: lcd + - const: bus + - const: ref + - const: lvds + minItems: 1 resets: maxItems: 1 + access-controllers: + maxItems: 1 + port: $ref: /schemas/graph.yaml#/properties/port description: | @@ -51,6 +62,46 @@ required: - resets - port +allOf: + - if: + properties: + compatible: + contains: + enum: + - st,stm32-ltdc + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - st,stm32mp251-ltdc + then: + properties: + clocks: + minItems: 2 + maxItems: 2 + clock-names: + minItems: 2 + maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - st,stm32mp255-ltdc + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/display/st,stm32mp25-lvds.yaml b/Documentation/devicetree/bindings/display/st,stm32mp25-lvds.yaml index 6736f93256b5..14e042156179 100644 --- a/Documentation/devicetree/bindings/display/st,stm32mp25-lvds.yaml +++ b/Documentation/devicetree/bindings/display/st,stm32mp25-lvds.yaml @@ -31,7 +31,12 @@ description: | properties: compatible: - const: st,stm32mp25-lvds + oneOf: + - items: + - enum: + - st,stm32mp255-lvds + - const: st,stm32mp25-lvds + - const: st,stm32mp25-lvds "#clock-cells": const: 0 @@ -54,6 +59,12 @@ properties: resets: maxItems: 1 + access-controllers: + maxItems: 1 + + power-domains: + maxItems: 1 + ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/dma/brcm,iproc-sba.txt b/Documentation/devicetree/bindings/dma/brcm,iproc-sba.txt deleted file mode 100644 index 092913a28457..000000000000 --- a/Documentation/devicetree/bindings/dma/brcm,iproc-sba.txt +++ /dev/null @@ -1,29 +0,0 @@ -* Broadcom SBA RAID engine - -Required properties: -- compatible: Should be one of the following - "brcm,iproc-sba" - "brcm,iproc-sba-v2" - The "brcm,iproc-sba" has support for only 6 PQ coefficients - The "brcm,iproc-sba-v2" has support for only 30 PQ coefficients -- mboxes: List of phandle and mailbox channel specifiers - -Example: - -raid_mbox: mbox@67400000 { - ... - #mbox-cells = <3>; - ... -}; - -raid0 { - compatible = "brcm,iproc-sba-v2"; - mboxes = <&raid_mbox 0 0x1 0xffff>, - <&raid_mbox 1 0x1 0xffff>, - <&raid_mbox 2 0x1 0xffff>, - <&raid_mbox 3 0x1 0xffff>, - <&raid_mbox 4 0x1 0xffff>, - <&raid_mbox 5 0x1 0xffff>, - <&raid_mbox 6 0x1 0xffff>, - <&raid_mbox 7 0x1 0xffff>; -}; diff --git a/Documentation/devicetree/bindings/dma/brcm,iproc-sba.yaml b/Documentation/devicetree/bindings/dma/brcm,iproc-sba.yaml new file mode 100644 index 000000000000..f3fed576cacf --- /dev/null +++ b/Documentation/devicetree/bindings/dma/brcm,iproc-sba.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/brcm,iproc-sba.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom SBA RAID engine + +maintainers: + - Ray Jui + - Scott Branden + +properties: + compatible: + enum: + - brcm,iproc-sba + - brcm,iproc-sba-v2 + + mboxes: + minItems: 1 + maxItems: 8 + +required: + - compatible + - mboxes + +additionalProperties: false + +examples: + - | + raid0 { + compatible = "brcm,iproc-sba-v2"; + mboxes = <&raid_mbox 0 0x1 0xffff>, + <&raid_mbox 1 0x1 0xffff>, + <&raid_mbox 2 0x1 0xffff>, + <&raid_mbox 3 0x1 0xffff>, + <&raid_mbox 4 0x1 0xffff>, + <&raid_mbox 5 0x1 0xffff>, + <&raid_mbox 6 0x1 0xffff>, + <&raid_mbox 7 0x1 0xffff>; + }; diff --git a/Documentation/devicetree/bindings/dma/fsl,mxs-dma.yaml b/Documentation/devicetree/bindings/dma/fsl,mxs-dma.yaml index 75a7d9556699..9102b615dbd6 100644 --- a/Documentation/devicetree/bindings/dma/fsl,mxs-dma.yaml +++ b/Documentation/devicetree/bindings/dma/fsl,mxs-dma.yaml @@ -23,6 +23,35 @@ allOf: properties: power-domains: false + - if: + properties: + compatible: + contains: + const: fsl,imx23-dma-apbx + then: + properties: + interrupt-names: + items: + - const: audio-adc + - const: audio-dac + - const: spdif-tx + - const: i2c + - const: saif0 + - const: empty0 + - const: auart0-rx + - const: auart0-tx + - const: auart1-rx + - const: auart1-tx + - const: saif1 + - const: empty1 + - const: empty2 + - const: empty3 + - const: empty4 + - const: empty5 + else: + properties: + interrupt-names: false + properties: compatible: oneOf: @@ -54,6 +83,10 @@ properties: minItems: 4 maxItems: 16 + interrupt-names: + minItems: 4 + maxItems: 16 + "#dma-cells": const: 1 diff --git a/Documentation/devicetree/bindings/dma/lpc1850-dmamux.txt b/Documentation/devicetree/bindings/dma/lpc1850-dmamux.txt deleted file mode 100644 index 87740adb2995..000000000000 --- a/Documentation/devicetree/bindings/dma/lpc1850-dmamux.txt +++ /dev/null @@ -1,54 +0,0 @@ -NXP LPC18xx/43xx DMA MUX (DMA request router) - -Required properties: -- compatible: "nxp,lpc1850-dmamux" -- reg: Memory map for accessing module -- #dma-cells: Should be set to <3>. - * 1st cell contain the master dma request signal - * 2nd cell contain the mux value (0-3) for the peripheral - * 3rd cell contain either 1 or 2 depending on the AHB - master used. -- dma-requests: Number of DMA requests for the mux -- dma-masters: phandle pointing to the DMA controller - -The DMA controller node need to have the following poroperties: -- dma-requests: Number of DMA requests the controller can handle - -Example: - -dmac: dma@40002000 { - compatible = "nxp,lpc1850-gpdma", "arm,pl080", "arm,primecell"; - arm,primecell-periphid = <0x00041080>; - reg = <0x40002000 0x1000>; - interrupts = <2>; - clocks = <&ccu1 CLK_CPU_DMA>; - clock-names = "apb_pclk"; - #dma-cells = <2>; - dma-channels = <8>; - dma-requests = <16>; - lli-bus-interface-ahb1; - lli-bus-interface-ahb2; - mem-bus-interface-ahb1; - mem-bus-interface-ahb2; - memcpy-burst-size = <256>; - memcpy-bus-width = <32>; -}; - -dmamux: dma-mux { - compatible = "nxp,lpc1850-dmamux"; - #dma-cells = <3>; - dma-requests = <64>; - dma-masters = <&dmac>; -}; - -uart0: serial@40081000 { - compatible = "nxp,lpc1850-uart", "ns16550a"; - reg = <0x40081000 0x1000>; - reg-shift = <2>; - interrupts = <24>; - clocks = <&ccu2 CLK_APB0_UART0>, <&ccu1 CLK_CPU_UART0>; - clock-names = "uartclk", "reg"; - dmas = <&dmamux 1 1 2 - &dmamux 2 1 2>; - dma-names = "tx", "rx"; -}; diff --git a/Documentation/devicetree/bindings/dma/marvell,orion-xor.yaml b/Documentation/devicetree/bindings/dma/marvell,orion-xor.yaml new file mode 100644 index 000000000000..add08257ec59 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/marvell,orion-xor.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/marvell,orion-xor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell XOR engine + +maintainers: + - Andrew Lunn + - Gregory Clement + +properties: + compatible: + oneOf: + - items: + - const: marvell,armada-380-xor + - const: marvell,orion-xor + - enum: + - marvell,armada-3700-xor + - marvell,orion-xor + + reg: + items: + - description: Low registers for the XOR engine + - description: High registers for the XOR engine + + clocks: + maxItems: 1 + +patternProperties: + "^(channel|xor)[0-9]+$": + description: XOR channel sub-node + type: object + additionalProperties: false + + properties: + interrupts: + description: Interrupt specifier for the XOR channel + items: + - description: Interrupt for this channel + + dmacap,memcpy: + type: boolean + deprecated: true + description: + Indicates that the XOR channel is capable of memcpy operations + + dmacap,memset: + type: boolean + deprecated: true + description: + Indicates that the XOR channel is capable of memset operations + + dmacap,xor: + type: boolean + deprecated: true + description: + Indicates that the XOR channel is capable of xor operations + + required: + - interrupts + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + xor@d0060900 { + compatible = "marvell,orion-xor"; + reg = <0xd0060900 0x100>, + <0xd0060b00 0x100>; + clocks = <&coreclk 0>; + + xor00 { + interrupts = <51>; + }; + xor01 { + interrupts = <52>; + }; + }; diff --git a/Documentation/devicetree/bindings/dma/mv-xor.txt b/Documentation/devicetree/bindings/dma/mv-xor.txt deleted file mode 100644 index 0ffb4d8766a8..000000000000 --- a/Documentation/devicetree/bindings/dma/mv-xor.txt +++ /dev/null @@ -1,40 +0,0 @@ -* Marvell XOR engines - -Required properties: -- compatible: Should be one of the following: - - "marvell,orion-xor" - - "marvell,armada-380-xor" - - "marvell,armada-3700-xor". -- reg: Should contain registers location and length (two sets) - the first set is the low registers, the second set the high - registers for the XOR engine. -- clocks: pointer to the reference clock - -The DT node must also contains sub-nodes for each XOR channel that the -XOR engine has. Those sub-nodes have the following required -properties: -- interrupts: interrupt of the XOR channel - -The sub-nodes used to contain one or several of the following -properties, but they are now deprecated: -- dmacap,memcpy to indicate that the XOR channel is capable of memcpy operations -- dmacap,memset to indicate that the XOR channel is capable of memset operations -- dmacap,xor to indicate that the XOR channel is capable of xor operations -- dmacap,interrupt to indicate that the XOR channel is capable of - generating interrupts - -Example: - -xor@d0060900 { - compatible = "marvell,orion-xor"; - reg = <0xd0060900 0x100 - 0xd0060b00 0x100>; - clocks = <&coreclk 0>; - - xor00 { - interrupts = <51>; - }; - xor01 { - interrupts = <52>; - }; -}; diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index a790e5687844..0dabe9bbb219 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -24,6 +24,7 @@ properties: - const: nvidia,tegra186-gpcdma - items: - enum: + - nvidia,tegra264-gpcdma - nvidia,tegra234-gpcdma - nvidia,tegra194-gpcdma - const: nvidia,tegra186-gpcdma diff --git a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml index f2f87f0f545b..6493a6968bb4 100644 --- a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml @@ -92,8 +92,12 @@ required: anyOf: - required: - qcom,powered-remotely + - num-channels + - qcom,num-ees - required: - qcom,controlled-remotely + - num-channels + - qcom,num-ees - required: - clocks - clock-names diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml index 7052468b15c8..bbe4da2a1105 100644 --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -24,12 +24,14 @@ properties: - qcom,sm6350-gpi-dma - items: - enum: + - qcom,milos-gpi-dma - qcom,qcm2290-gpi-dma - qcom,qcs8300-gpi-dma - qcom,qdu1000-gpi-dma - qcom,sa8775p-gpi-dma - qcom,sar2130p-gpi-dma - qcom,sc7280-gpi-dma + - qcom,sc8280xp-gpi-dma - qcom,sdx75-gpi-dma - qcom,sm6115-gpi-dma - qcom,sm6375-gpi-dma diff --git a/Documentation/devicetree/bindings/dma/sophgo,cv1800b-dmamux.yaml b/Documentation/devicetree/bindings/dma/sophgo,cv1800b-dmamux.yaml new file mode 100644 index 000000000000..011002942235 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/sophgo,cv1800b-dmamux.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/sophgo,cv1800b-dmamux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo CV1800/SG200 Series DMA multiplexer + +maintainers: + - Inochi Amaoto + +description: + The DMA multiplexer of CV1800 is a subdevice of the system + controller. It support mapping 8 channels, but each channel + can be mapped only once. + +allOf: + - $ref: dma-router.yaml# + +properties: + compatible: + const: sophgo,cv1800b-dmamux + + reg: + items: + - description: DMA channal remapping register + - description: DMA channel interrupt mapping register + + '#dma-cells': + const: 2 + description: + The first cells is device id. The second one is the cpu id. + + dma-masters: + maxItems: 1 + +required: + - reg + - '#dma-cells' + - dma-masters + +additionalProperties: false + +examples: + - | + dma-router@154 { + compatible = "sophgo,cv1800b-dmamux"; + reg = <0x154 0x8>, <0x298 0x4>; + #dma-cells = <2>; + dma-masters = <&dmac>; + }; diff --git a/Documentation/devicetree/bindings/dpll/dpll-device.yaml b/Documentation/devicetree/bindings/dpll/dpll-device.yaml new file mode 100644 index 000000000000..fb8d7a9a3693 --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/dpll-device.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/dpll-device.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Digital Phase-Locked Loop (DPLL) Device + +maintainers: + - Ivan Vecera + +description: + Digital Phase-Locked Loop (DPLL) device is used for precise clock + synchronization in networking and telecom hardware. The device can + have one or more channels (DPLLs) and one or more physical input and + output pins. Each DPLL channel can either produce pulse-per-clock signal + or drive ethernet equipment clock. The type of each channel can be + indicated by dpll-types property. + +properties: + $nodename: + pattern: "^dpll(@.*)?$" + + "#address-cells": + const: 0 + + "#size-cells": + const: 0 + + dpll-types: + description: List of DPLL channel types, one per DPLL instance. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + items: + enum: [pps, eec] + + input-pins: + type: object + description: DPLL input pins + unevaluatedProperties: false + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + patternProperties: + "^pin@[0-9a-f]+$": + $ref: /schemas/dpll/dpll-pin.yaml + unevaluatedProperties: false + + required: + - "#address-cells" + - "#size-cells" + + output-pins: + type: object + description: DPLL output pins + unevaluatedProperties: false + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + patternProperties: + "^pin@[0-9]+$": + $ref: /schemas/dpll/dpll-pin.yaml + unevaluatedProperties: false + + required: + - "#address-cells" + - "#size-cells" + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/dpll/dpll-pin.yaml b/Documentation/devicetree/bindings/dpll/dpll-pin.yaml new file mode 100644 index 000000000000..51db93b77306 --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/dpll-pin.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/dpll-pin.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DPLL Pin + +maintainers: + - Ivan Vecera + +description: | + The DPLL pin is either a physical input or output pin that is provided + by a DPLL( Digital Phase-Locked Loop) device. The pin is identified by + its physical order number that is stored in reg property and can have + an additional set of properties like supported (allowed) frequencies, + label, type and may support embedded sync. + + Note that the pin in this context has nothing to do with pinctrl. + +properties: + reg: + description: Hardware index of the DPLL pin. + maxItems: 1 + + connection-type: + description: Connection type of the pin + $ref: /schemas/types.yaml#/definitions/string + enum: [ext, gnss, int, mux, synce] + + esync-control: + description: Indicates whether the pin supports embedded sync functionality. + type: boolean + + label: + description: String exposed as the pin board label + $ref: /schemas/types.yaml#/definitions/string + + supported-frequencies-hz: + description: List of supported frequencies for this pin, expressed in Hz. + +required: + - reg + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml new file mode 100644 index 000000000000..17747f754b84 --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/microchip,zl30731.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Azurite DPLL device + +maintainers: + - Ivan Vecera + +description: + Microchip Azurite DPLL (ZL3073x) is a family of DPLL devices that + provides up to 5 independent DPLL channels, up to 10 differential or + single-ended inputs and 10 differential or 20 single-ended outputs. + These devices support both I2C and SPI interfaces. + +properties: + compatible: + enum: + - microchip,zl30731 + - microchip,zl30732 + - microchip,zl30733 + - microchip,zl30734 + - microchip,zl30735 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +allOf: + - $ref: /schemas/dpll/dpll-device.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + dpll@70 { + compatible = "microchip,zl30732"; + reg = <0x70>; + dpll-types = "pps", "eec"; + + input-pins { + #address-cells = <1>; + #size-cells = <0>; + + pin@0 { /* REF0P */ + reg = <0>; + connection-type = "ext"; + label = "Input 0"; + supported-frequencies-hz = /bits/ 64 <1 1000>; + }; + }; + + output-pins { + #address-cells = <1>; + #size-cells = <0>; + + pin@3 { /* OUT1N */ + reg = <3>; + connection-type = "gnss"; + esync-control; + label = "Output 1"; + supported-frequencies-hz = /bits/ 64 <1 10000>; + }; + }; + }; + }; + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + dpll@70 { + compatible = "microchip,zl30731"; + reg = <0x70>; + spi-max-frequency = <12500000>; + + dpll-types = "pps"; + + input-pins { + #address-cells = <1>; + #size-cells = <0>; + + pin@0 { /* REF0P */ + reg = <0>; + connection-type = "ext"; + label = "Input 0"; + supported-frequencies-hz = /bits/ 64 <1 1000>; + }; + }; + + output-pins { + #address-cells = <1>; + #size-cells = <0>; + + pin@3 { /* OUT1N */ + reg = <3>; + connection-type = "gnss"; + esync-control; + label = "Output 1"; + supported-frequencies-hz = /bits/ 64 <1 10000>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml index b8693e4b4b0d..e610b7636a08 100644 --- a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml +++ b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml @@ -91,6 +91,9 @@ properties: - const: runstall - const: softreset + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml b/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml index ca8d8661f872..abc52978be7a 100644 --- a/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml +++ b/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml @@ -81,25 +81,25 @@ examples: #include #include dsp@10803000 { - compatible = "mediatek,mt8195-dsp"; - reg = <0x10803000 0x1000>, - <0x10840000 0x40000>; - reg-names = "cfg", "sram"; - clocks = <&topckgen 10>, //CLK_TOP_ADSP - <&clk26m>, - <&topckgen 107>, //CLK_TOP_AUDIO_LOCAL_BUS - <&topckgen 136>, //CLK_TOP_MAINPLL_D7_D2 - <&scp_adsp 0>, //CLK_SCP_ADSP_AUDIODSP - <&topckgen 34>; //CLK_TOP_AUDIO_H - clock-names = "adsp_sel", - "clk26m_ck", - "audio_local_bus", - "mainpll_d7_d2", - "scp_adsp_audiodsp", - "audio_h"; - memory-region = <&adsp_dma_mem_reserved>, - <&adsp_mem_reserved>; - power-domains = <&spm 6>; //MT8195_POWER_DOMAIN_ADSP - mbox-names = "rx", "tx"; - mboxes = <&adsp_mailbox0>, <&adsp_mailbox1>; + compatible = "mediatek,mt8195-dsp"; + reg = <0x10803000 0x1000>, + <0x10840000 0x40000>; + reg-names = "cfg", "sram"; + clocks = <&topckgen 10>, //CLK_TOP_ADSP + <&clk26m>, + <&topckgen 107>, //CLK_TOP_AUDIO_LOCAL_BUS + <&topckgen 136>, //CLK_TOP_MAINPLL_D7_D2 + <&scp_adsp 0>, //CLK_SCP_ADSP_AUDIODSP + <&topckgen 34>; //CLK_TOP_AUDIO_H + clock-names = "adsp_sel", + "clk26m_ck", + "audio_local_bus", + "mainpll_d7_d2", + "scp_adsp_audiodsp", + "audio_h"; + memory-region = <&adsp_dma_mem_reserved>, + <&adsp_mem_reserved>; + power-domains = <&spm 6>; //MT8195_POWER_DOMAIN_ADSP + mbox-names = "rx", "tx"; + mboxes = <&adsp_mailbox0>, <&adsp_mailbox1>; }; diff --git a/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml b/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml index e6bed7d93e2d..50f1f08744a1 100644 --- a/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml +++ b/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml @@ -62,33 +62,33 @@ examples: #include npe: npe@c8006000 { - compatible = "intel,ixp4xx-network-processing-engine"; - reg = <0xc8006000 0x1000>, <0xc8007000 0x1000>, <0xc8008000 0x1000>; - #address-cells = <1>; - #size-cells = <0>; + compatible = "intel,ixp4xx-network-processing-engine"; + reg = <0xc8006000 0x1000>, <0xc8007000 0x1000>, <0xc8008000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; - hss@0 { - compatible = "intel,ixp4xx-hss"; - reg = <0>; - intel,npe-handle = <&npe 0>; - intel,queue-chl-rxtrig = <&qmgr 12>; - intel,queue-chl-txready = <&qmgr 34>; - intel,queue-pkt-rx = <&qmgr 13>; - intel,queue-pkt-tx = <&qmgr 14>, <&qmgr 15>, <&qmgr 16>, <&qmgr 17>; - intel,queue-pkt-rxfree = <&qmgr 18>, <&qmgr 19>, <&qmgr 20>, <&qmgr 21>; - intel,queue-pkt-txdone = <&qmgr 22>; - cts-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; - rts-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; - dcd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; - dtr-gpios = <&gpio_74 2 GPIO_ACTIVE_LOW>; - clk-internal-gpios = <&gpio_74 0 GPIO_ACTIVE_HIGH>; - }; + hss@0 { + compatible = "intel,ixp4xx-hss"; + reg = <0>; + intel,npe-handle = <&npe 0>; + intel,queue-chl-rxtrig = <&qmgr 12>; + intel,queue-chl-txready = <&qmgr 34>; + intel,queue-pkt-rx = <&qmgr 13>; + intel,queue-pkt-tx = <&qmgr 14>, <&qmgr 15>, <&qmgr 16>, <&qmgr 17>; + intel,queue-pkt-rxfree = <&qmgr 18>, <&qmgr 19>, <&qmgr 20>, <&qmgr 21>; + intel,queue-pkt-txdone = <&qmgr 22>; + cts-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; + rts-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; + dcd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + dtr-gpios = <&gpio_74 2 GPIO_ACTIVE_LOW>; + clk-internal-gpios = <&gpio_74 0 GPIO_ACTIVE_HIGH>; + }; - crypto { - compatible = "intel,ixp4xx-crypto"; - intel,npe-handle = <&npe 2>; - queue-rx = <&qmgr 30>; - queue-txready = <&qmgr 29>; - }; + crypto { + compatible = "intel,ixp4xx-crypto"; + intel,npe-handle = <&npe 2>; + queue-rx = <&qmgr 30>; + queue-txready = <&qmgr 29>; + }; }; ... diff --git a/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.yaml b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.yaml index c43d17f6e96b..3c44fe607e12 100644 --- a/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.yaml +++ b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.yaml @@ -70,6 +70,7 @@ properties: - enum: - nvidia,tegra194-bpmp - nvidia,tegra234-bpmp + - nvidia,tegra264-bpmp - const: nvidia,tegra186-bpmp - const: nvidia,tegra186-bpmp diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 8cdaac8011ba..b913192219e4 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -32,6 +32,7 @@ properties: - qcom,scm-ipq8074 - qcom,scm-ipq9574 - qcom,scm-mdm9607 + - qcom,scm-milos - qcom,scm-msm8226 - qcom,scm-msm8660 - qcom,scm-msm8916 @@ -198,6 +199,7 @@ allOf: compatible: contains: enum: + - qcom,scm-milos - qcom,scm-sm8450 - qcom,scm-sm8550 - qcom,scm-sm8650 diff --git a/Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml b/Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml index bbc183200400..3365124c7fd4 100644 --- a/Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml +++ b/Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml @@ -32,6 +32,13 @@ properties: items: - const: aon + resets: + maxItems: 1 + + reset-names: + items: + - const: gpu-clkgen + "#power-domain-cells": const: 1 diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.yaml b/Documentation/devicetree/bindings/fpga/fpga-region.yaml index 77554885a6c4..7d2d3b7aa4b7 100644 --- a/Documentation/devicetree/bindings/fpga/fpga-region.yaml +++ b/Documentation/devicetree/bindings/fpga/fpga-region.yaml @@ -316,6 +316,7 @@ examples: reg = <0x40000000 0x10000>; gpio-controller; #gpio-cells = <2>; + clocks = <&clk>; }; }; diff --git a/Documentation/devicetree/bindings/fpga/xlnx,versal-fpga.yaml b/Documentation/devicetree/bindings/fpga/xlnx,versal-fpga.yaml index 80833462f620..41b368d54557 100644 --- a/Documentation/devicetree/bindings/fpga/xlnx,versal-fpga.yaml +++ b/Documentation/devicetree/bindings/fpga/xlnx,versal-fpga.yaml @@ -27,7 +27,7 @@ additionalProperties: false examples: - | versal_fpga: versal-fpga { - compatible = "xlnx,versal-fpga"; + compatible = "xlnx,versal-fpga"; }; ... diff --git a/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml b/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml index 7d4b6d49e5ee..c0c2bfaa606f 100644 --- a/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml +++ b/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml @@ -18,10 +18,14 @@ description: > properties: compatible: - enum: - - u-blox,neo-6m - - u-blox,neo-8 - - u-blox,neo-m8 + oneOf: + - enum: + - u-blox,neo-6m + - u-blox,neo-8 + - u-blox,neo-m8 + - items: + - const: u-blox,neo-m9 + - const: u-blox,neo-m8 reg: description: > diff --git a/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt b/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt deleted file mode 100644 index 973362eb3f1e..000000000000 --- a/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt +++ /dev/null @@ -1,72 +0,0 @@ -GPIO controllers on MPC8xxx SoCs - -This is for the non-QE/CPM/GUTs GPIO controllers as found on -8349, 8572, 8610 and compatible. - -Every GPIO controller node must have #gpio-cells property defined, -this information will be used to translate gpio-specifiers. -See bindings/gpio/gpio.txt for details of how to specify GPIO -information for devices. - -The GPIO module usually is connected to the SoC's internal interrupt -controller, see bindings/interrupt-controller/interrupts.txt (the -interrupt client nodes section) for details how to specify this GPIO -module's interrupt. - -The GPIO module may serve as another interrupt controller (cascaded to -the SoC's internal interrupt controller). See the interrupt controller -nodes section in bindings/interrupt-controller/interrupts.txt for -details. - -Required properties: -- compatible: "fsl,-gpio" followed by "fsl,mpc8349-gpio" - for 83xx, "fsl,mpc8572-gpio" for 85xx, or - "fsl,mpc8610-gpio" for 86xx. -- #gpio-cells: Should be two. The first cell is the pin number - and the second cell is used to specify optional - parameters (currently unused). -- interrupts: Interrupt mapping for GPIO IRQ. -- gpio-controller: Marks the port as GPIO controller. - -Optional properties: -- interrupt-controller: Empty boolean property which marks the GPIO - module as an IRQ controller. -- #interrupt-cells: Should be two. Defines the number of integer - cells required to specify an interrupt within - this interrupt controller. The first cell - defines the pin number, the second cell - defines additional flags (trigger type, - trigger polarity). Note that the available - set of trigger conditions supported by the - GPIO module depends on the actual SoC. - -Example of gpio-controller nodes for a MPC8347 SoC: - - gpio1: gpio-controller@c00 { - #gpio-cells = <2>; - compatible = "fsl,mpc8347-gpio", "fsl,mpc8349-gpio"; - reg = <0xc00 0x100>; - interrupt-parent = <&ipic>; - interrupts = <74 0x8>; - gpio-controller; - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpio2: gpio-controller@d00 { - #gpio-cells = <2>; - compatible = "fsl,mpc8347-gpio", "fsl,mpc8349-gpio"; - reg = <0xd00 0x100>; - interrupt-parent = <&ipic>; - interrupts = <75 0x8>; - gpio-controller; - }; - -Example of a peripheral using the GPIO module as an IRQ controller: - - funkyfpga@0 { - compatible = "funky-fpga"; - ... - interrupt-parent = <&gpio1>; - interrupts = <4 3>; - }; diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt deleted file mode 100644 index ce19c5660aca..000000000000 --- a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Abilis TB10x GPIO controller - -Required Properties: -- compatible: Should be "abilis,tb10x-gpio" -- reg: Address and length of the register set for the device -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be <2>. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted). -- abilis,ngpio: the number of GPIO pins this driver controls. - -Optional Properties: -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges. -- interrupts: Defines the interrupt line connecting this GPIO controller to - its parent interrupt controller. - -GPIO ranges are specified as described in -Documentation/devicetree/bindings/gpio/gpio.txt - -Example: - - gpioa: gpio@ff140000 { - compatible = "abilis,tb10x-gpio"; - interrupt-controller; - #interrupt-cells = <1>; - interrupt-parent = <&tb10x_ictl>; - interrupts = <27 2>; - reg = <0xFF140000 0x1000>; - gpio-controller; - #gpio-cells = <2>; - abilis,ngpio = <3>; - gpio-ranges = <&iomux 0 0 0>; - gpio-ranges-group-names = "gpioa_pins"; - }; diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml new file mode 100644 index 000000000000..c93ec0f16bcd --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/abilis,tb10x-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Abilis TB10x GPIO controller + +maintainers: + - Christian Ruppert + +properties: + compatible: + const: abilis,tb10x-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio-ranges: true + + gpio-ranges-group-names: true + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + description: Interrupts are triggered on both edges + + interrupts: + maxItems: 1 + + abilis,ngpio: + description: Number of GPIO pins this driver controls + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - abilis,ngpio + +additionalProperties: false + +examples: + - | + gpio@ff140000 { + compatible = "abilis,tb10x-gpio"; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <27 2>; + reg = <0xff140000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + abilis,ngpio = <3>; + gpio-ranges = <&iomux 0 0 0>; + gpio-ranges-group-names = "gpioa_pins"; + }; diff --git a/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml b/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml new file mode 100644 index 000000000000..18afed324198 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/altr-pio-1.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera GPIO controller + +maintainers: + - Dinh Nguyen + - Marek Vasut + - Mathieu Malaterre + - Tien Hock Loh + +properties: + compatible: + const: altr,pio-1.0 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: + First cell is the GPIO offset number. Second cell is reserved and + currently unused. + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + altr,ngpio: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width of the GPIO bank. + default: 32 + + altr,interrupt-type: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + Specifies the interrupt trigger type synthesized by hardware. + Values defined in . + enum: [1, 2, 3, 4] + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + +additionalProperties: false + +examples: + - | + #include + + gpio@ff200000 { + compatible = "altr,pio-1.0"; + reg = <0xff200000 0x10>; + interrupts = <45 4>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + altr,ngpio = <32>; + altr,interrupt-type = ; + }; diff --git a/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml b/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml new file mode 100644 index 000000000000..d205dd7b492c --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/apm,xgene-gpio-sb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene Standby GPIO controller + +maintainers: + - Khuong Dinh + +description: | + This is a gpio controller in the standby domain. It also supports interrupt in + some particular pins which are sourced to its parent interrupt controller + as diagram below: + +-----------------+ + | X-Gene standby | + | GPIO controller +------ GPIO_0 + +------------+ | | ... + | Parent IRQ | EXT_INT_0 | +------ GPIO_8/EXT_INT_0 + | controller | (SPI40) | | ... + | (GICv2) +--------------+ +------ GPIO_[N+8]/EXT_INT_N + | | ... | | + | | EXT_INT_N | +------ GPIO_[N+9] + | | (SPI[40 + N])| | ... + | +--------------+ +------ GPIO_MAX + +------------+ +-----------------+ + +properties: + compatible: + const: apm,xgene-gpio-sb + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + interrupts: + description: + List of interrupt specifiers for EXT_INT_0 through EXT_INT_N. The first + entry must correspond to EXT_INT_0. + + '#interrupt-cells': + const: 2 + description: + First cell selects EXT_INT_N (0-N), second cell specifies flags + + interrupt-controller: true + + apm,nr-gpios: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of GPIO pins + + apm,nr-irqs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of interrupt pins + + apm,irq-start: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Lowest GPIO pin supporting interrupts + +required: + - compatible + - reg + - '#gpio-cells' + - gpio-controller + - interrupts + - '#interrupt-cells' + - interrupt-controller + +additionalProperties: false + +examples: + - | + gpio@17001000 { + compatible = "apm,xgene-gpio-sb"; + reg = <0x17001000 0x400>; + #gpio-cells = <2>; + gpio-controller; + interrupts = <0x0 0x28 0x1>, + <0x0 0x29 0x1>, + <0x0 0x2a 0x1>, + <0x0 0x2b 0x1>, + <0x0 0x2c 0x1>, + <0x0 0x2d 0x1>; + #interrupt-cells = <2>; + interrupt-controller; + apm,nr-gpios = <22>; + apm,nr-irqs = <6>; + apm,irq-start = <8>; + }; diff --git a/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml b/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml new file mode 100644 index 000000000000..42b1bc0a10c9 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/apple,smc-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Mac System Management Controller GPIO + +maintainers: + - Sven Peter + +description: + Apple Mac System Management Controller GPIO block. + +properties: + compatible: + const: apple,smc-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml b/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml new file mode 100644 index 000000000000..35155b900655 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cavium,octeon-3860-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cavium Octeon 3860 GPIO controller + +maintainers: + - Bartosz Golaszewski + +properties: + compatible: + const: cavium,octeon-3860-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + interrupts: + maxItems: 16 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - interrupt-controller + - '#interrupt-cells' + - interrupts + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + gpio@1070000000800 { + compatible = "cavium,octeon-3860-gpio"; + reg = <0x10700 0x00000800 0x0 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + /* The GPIO pin connect to 16 consecutive CUI bits */ + interrupts = <0 16>, <0 17>, <0 18>, <0 19>, + <0 20>, <0 21>, <0 22>, <0 23>, + <0 24>, <0 25>, <0 26>, <0 27>, + <0 28>, <0 29>, <0 30>, <0 31>; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt b/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt deleted file mode 100644 index 9d6dcd3fe7f9..000000000000 --- a/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt +++ /dev/null @@ -1,49 +0,0 @@ -* General Purpose Input Output (GPIO) bus. - -Properties: -- compatible: "cavium,octeon-3860-gpio" - - Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. - -- reg: The base address of the GPIO unit's register bank. - -- gpio-controller: This is a GPIO controller. - -- #gpio-cells: Must be <2>. The first cell is the GPIO pin. - -- interrupt-controller: The GPIO controller is also an interrupt - controller, many of its pins may be configured as an interrupt - source. - -- #interrupt-cells: Must be <2>. The first cell is the GPIO pin - connected to the interrupt source. The second cell is the interrupt - triggering protocol and may have one of four values: - 1 - edge triggered on the rising edge. - 2 - edge triggered on the falling edge - 4 - level triggered active high. - 8 - level triggered active low. - -- interrupts: Interrupt routing for each pin. - -Example: - - gpio-controller@1070000000800 { - #gpio-cells = <2>; - compatible = "cavium,octeon-3860-gpio"; - reg = <0x10700 0x00000800 0x0 0x100>; - gpio-controller; - /* Interrupts are specified by two parts: - * 1) GPIO pin number (0..15) - * 2) Triggering (1 - edge rising - * 2 - edge falling - * 4 - level active high - * 8 - level active low) - */ - interrupt-controller; - #interrupt-cells = <2>; - /* The GPIO pin connect to 16 consecutive CUI bits */ - interrupts = <0 16>, <0 17>, <0 18>, <0 19>, - <0 20>, <0 21>, <0 22>, <0 23>, - <0 24>, <0 25>, <0 26>, <0 27>, - <0 28>, <0 29>, <0 30>, <0 31>; - }; diff --git a/Documentation/devicetree/bindings/gpio/cdns,gpio.txt b/Documentation/devicetree/bindings/gpio/cdns,gpio.txt deleted file mode 100644 index 706ef00f5c64..000000000000 --- a/Documentation/devicetree/bindings/gpio/cdns,gpio.txt +++ /dev/null @@ -1,43 +0,0 @@ -Cadence GPIO controller bindings - -Required properties: -- compatible: should be "cdns,gpio-r1p02". -- reg: the register base address and size. -- #gpio-cells: should be 2. - * first cell is the GPIO number. - * second cell specifies the GPIO flags, as defined in - . Only the GPIO_ACTIVE_HIGH - and GPIO_ACTIVE_LOW flags are supported. -- gpio-controller: marks the device as a GPIO controller. -- clocks: should contain one entry referencing the peripheral clock driving - the GPIO controller. - -Optional properties: -- ngpios: integer number of gpio lines supported by this controller, up to 32. -- interrupts: interrupt specifier for the controllers interrupt. -- interrupt-controller: marks the device as an interrupt controller. When - defined, interrupts, interrupt-parent and #interrupt-cells - are required. -- interrupt-cells: should be 2. - * first cell is the GPIO number you want to use as an IRQ source. - * second cell specifies the IRQ type, as defined in - . - Currently only level sensitive IRQs are supported. - - -Example: - gpio0: gpio-controller@fd060000 { - compatible = "cdns,gpio-r1p02"; - reg =<0xfd060000 0x1000>; - - clocks = <&gpio_clk>; - - interrupt-parent = <&gic>; - interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml new file mode 100644 index 000000000000..a84d60b39459 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/cdns,gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence GPIO Controller + +maintainers: + - Jan Kotas + +properties: + compatible: + oneOf: + - const: cdns,gpio-r1p02 + - items: + - enum: + - axiado,ax3000-gpio + - const: cdns,gpio-r1p02 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + ngpios: + minimum: 1 + maximum: 32 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + - First cell is the GPIO line number. + - Second cell is flags as defined in , + only GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW supported. + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + description: | + - First cell is the GPIO line number used as IRQ. + - Second cell is the trigger type, as defined in + . + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - gpio-controller + - "#gpio-cells" + +if: + required: [interrupt-controller] +then: + required: + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + gpio0: gpio-controller@fd060000 { + compatible = "cdns,gpio-r1p02"; + reg = <0xfd060000 0x1000>; + clocks = <&gpio_clk>; + + interrupt-parent = <&gic>; + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt deleted file mode 100644 index fd42e7280f72..000000000000 --- a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt +++ /dev/null @@ -1,17 +0,0 @@ -* ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs - -Required properties: -- compatible: Should contain "cirrus,ep7209-mctrl-gpio". -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = Active high, - 1 = Active low. - -Example: - sysgpio: sysgpio { - compatible = "cirrus,ep7312-mctrl-gpio", - "cirrus,ep7209-mctrl-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml new file mode 100644 index 000000000000..bdffca817f1b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/cirrus,clps711x-mctrl-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs + +maintainers: + - Alexander Shiyan + +properties: + compatible: + oneOf: + - items: + - const: cirrus,ep7312-mctrl-gpio + - const: cirrus,ep7209-mctrl-gpio + - const: cirrus,ep7209-mctrl-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio,syscon-dev: + description: + Phandle and offset of device's specific registers within the syscon state + control registers + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon + - description: register offset within state control registers + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + sysgpio: sysgpio { + compatible = "cirrus,ep7312-mctrl-gpio", + "cirrus,ep7209-mctrl-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml b/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml new file mode 100644 index 000000000000..053134faf475 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/exar,xra1403.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: XRA1403 16-bit GPIO Expander with Reset Input + +maintainers: + - Nandor Han + +description: > + The XRA1403 is an 16-bit GPIO expander with an SPI interface. Features + available: + + - Individually programmable inputs: + - Internal pull-up resistors + - Polarity inversion + - Individual interrupt enable + - Rising edge and/or Falling edge interrupt + - Input filter + - Individually programmable outputs: + - Output Level Control + - Output Three-State Control + +properties: + compatible: + const: exar,xra1403 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + reset-gpios: + description: Control line for the device reset. + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@2 { + compatible = "exar,xra1403"; + reg = <2>; + spi-max-frequency = <1000000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml b/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml deleted file mode 100644 index b74fa81e7d05..000000000000 --- a/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/fcs,fxl6408.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Fairchild FXL6408 I2C GPIO Expander - -maintainers: - - Emanuele Ghidoli - -properties: - compatible: - enum: - - fcs,fxl6408 - - reg: - maxItems: 1 - - "#gpio-cells": - const: 2 - - gpio-controller: true - - gpio-line-names: - minItems: 1 - maxItems: 8 - -patternProperties: - "^(hog-[0-9]+|.+-hog(-[0-9]+)?)$": - type: object - required: - - gpio-hog - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - gpio_expander_43: gpio-expander@43 { - compatible = "fcs,fxl6408"; - reg = <0x43>; - gpio-controller; - #gpio-cells = <2>; - gpio-line-names = "Wi-Fi_W_DISABLE", "Wi-Fi_WKUP_WLAN", - "PWR_EN_+V3.3_WiFi_N", "PCIe_REF_CLK_EN", - "USB_RESET_N", "USB_BYPASS_N", "Wi-Fi_PDn", - "Wi-Fi_WKUP_BT"; - }; - }; diff --git a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml index f1b60ab3f356..4cb2a6b9fabf 100644 --- a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml @@ -29,6 +29,13 @@ properties: - fsl,ls1088a-gpio - fsl,ls2080a-gpio - const: fsl,qoriq-gpio + - items: + - enum: + - fsl,mpc8308-gpio + - fsl,mpc8377-gpio + - fsl,mpc8378-gpio + - fsl,mpc8379-gpio + - const: fsl,mpc8349-gpio reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt b/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt deleted file mode 100644 index d543fd1b8b23..000000000000 --- a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt +++ /dev/null @@ -1,19 +0,0 @@ -Gateworks PLD GPIO controller bindings - -The GPIO controller should be a child node on an I2C bus. - -Required properties: -- compatible: Should be "gateworks,pld-gpio" -- reg: I2C slave address -- gpio-controller: Marks the device node as a GPIO controller. -- #gpio-cells: Should be <2>. The first cell is the gpio number and - the second cell is used to specify optional parameters. - -Example: - -pld@56 { - compatible = "gateworks,pld-gpio"; - reg = <0x56>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt b/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt deleted file mode 100644 index 7bb1a9d60133..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt +++ /dev/null @@ -1,30 +0,0 @@ -* 74XX MMIO GPIO driver - -Required properties: -- compatible: Should contain one of the following: - "ti,741g125": for 741G125 (1-bit Input), - "ti,741g174": for 741G74 (1-bit Output), - "ti,742g125": for 742G125 (2-bit Input), - "ti,7474" : for 7474 (2-bit Output), - "ti,74125" : for 74125 (4-bit Input), - "ti,74175" : for 74175 (4-bit Output), - "ti,74365" : for 74365 (6-bit Input), - "ti,74174" : for 74174 (6-bit Output), - "ti,74244" : for 74244 (8-bit Input), - "ti,74273" : for 74273 (8-bit Output), - "ti,741624" : for 741624 (16-bit Input), - "ti,7416374": for 7416374 (16-bit Output). -- reg: Physical base address and length where IC resides. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the GPIO polarity: - 0 = Active High, - 1 = Active Low. - -Example: - ctrl: gpio@30008004 { - compatible = "ti,74174"; - reg = <0x30008004 0x1>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt deleted file mode 100644 index 2a80e272cd66..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-altera.txt +++ /dev/null @@ -1,44 +0,0 @@ -Altera GPIO controller bindings - -Required properties: -- compatible: - - "altr,pio-1.0" -- reg: Physical base address and length of the controller's registers. -- #gpio-cells : Should be 2 - - The first cell is the gpio offset number. - - The second cell is reserved and is currently unused. -- gpio-controller : Marks the device node as a GPIO controller. -- interrupt-controller: Mark the device node as an interrupt controller -- #interrupt-cells : Should be 2. The interrupt type is fixed in the hardware. - - The first cell is the GPIO offset number within the GPIO controller. - - The second cell is the interrupt trigger type and level flags. -- interrupts: Specify the interrupt. -- altr,interrupt-type: Specifies the interrupt trigger type the GPIO - hardware is synthesized. This field is required if the Altera GPIO controller - used has IRQ enabled as the interrupt type is not software controlled, - but hardware synthesized. Required if GPIO is used as an interrupt - controller. The value is defined in - Only the following flags are supported: - IRQ_TYPE_EDGE_RISING - IRQ_TYPE_EDGE_FALLING - IRQ_TYPE_EDGE_BOTH - IRQ_TYPE_LEVEL_HIGH - -Optional properties: -- altr,ngpio: Width of the GPIO bank. This defines how many pins the - GPIO device has. Ranges between 1-32. Optional and defaults to 32 if not - specified. - -Example: - -gpio_altr: gpio@ff200000 { - compatible = "altr,pio-1.0"; - reg = <0xff200000 0x10>; - interrupts = <0 45 4>; - altr,ngpio = <32>; - altr,interrupt-type = ; - #gpio-cells = <2>; - gpio-controller; - #interrupt-cells = <2>; - interrupt-controller; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-ath79.txt b/Documentation/devicetree/bindings/gpio/gpio-ath79.txt deleted file mode 100644 index cf71f3ec969d..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ath79.txt +++ /dev/null @@ -1,37 +0,0 @@ -Binding for Qualcomm Atheros AR7xxx/AR9xxx GPIO controller - -Required properties: -- compatible: has to be "qca,-gpio" and one of the following - fallbacks: - - "qca,ar7100-gpio" - - "qca,ar9340-gpio" -- reg: Base address and size of the controllers memory area -- gpio-controller : Marks the device node as a GPIO controller. -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters. -- ngpios: Should be set to the number of GPIOs available on the SoC. - -Optional properties: -- interrupts: Interrupt specifier for the controllers interrupt. -- interrupt-controller : Identifies the node as an interrupt controller -- #interrupt-cells : Specifies the number of cells needed to encode interrupt - source, should be 2 - -Please refer to interrupts.txt in this directory for details of the common -Interrupt Controllers bindings used by client devices. - -Example: - - gpio@18040000 { - compatible = "qca,ar9132-gpio", "qca,ar7100-gpio"; - reg = <0x18040000 0x30>; - interrupts = <2>; - - ngpios = <22>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt b/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt deleted file mode 100644 index 0a304ad29d81..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt +++ /dev/null @@ -1,28 +0,0 @@ -Cirrus Logic CLPS711X GPIO controller - -Required properties: -- compatible: Should be "cirrus,ep7209-gpio" -- reg: Physical base GPIO controller registers location and length. - There should be two registers, first is DATA register, the second - is DIRECTION. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low - -Note: Each GPIO port should have an alias correctly numbered in "aliases" -node. - -Example: - -aliases { - gpio0 = &porta; -}; - -porta: gpio@80000000 { - compatible = "cirrus,ep7312-gpio","cirrus,ep7209-gpio"; - reg = <0x80000000 0x1>, <0x80000040 0x1>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt b/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt deleted file mode 100644 index 0423699d74c7..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt +++ /dev/null @@ -1,39 +0,0 @@ -Keystone 2 DSP GPIO controller bindings - -HOST OS userland running on ARM can send interrupts to DSP cores using -the DSP GPIO controller IP. It provides 28 IRQ signals per each DSP core. -This is one of the component used by the IPC mechanism used on Keystone SOCs. - -For example TCI6638K2K SoC has 8 DSP GPIO controllers: - - 8 for C66x CorePacx CPUs 0-7 - -Keystone 2 DSP GPIO controller has specific features: -- each GPIO can be configured only as output pin; -- setting GPIO value to 1 causes IRQ generation on target DSP core; -- reading pin value returns 0 - if IRQ was handled or 1 - IRQ is still - pending. - -Required Properties: -- compatible: should be "ti,keystone-dsp-gpio" -- ti,syscon-dev: phandle/offset pair. The phandle to syscon used to - access device state control registers and the offset of device's specific - registers within device state control registers range. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be 2. - -Please refer to gpio.txt in this directory for details of the common GPIO -bindings used by client devices. - -Example: - dspgpio0: keystone_dsp_gpio@2620240 { - compatible = "ti,keystone-dsp-gpio"; - ti,syscon-dev = <&devctrl 0x240>; - gpio-controller; - #gpio-cells = <2>; - }; - - dsp0: dsp0 { - compatible = "linux,rproc-user"; - ... - kick-gpio = <&dspgpio0 27>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt b/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt deleted file mode 100644 index 80fcb7d70e13..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt +++ /dev/null @@ -1,37 +0,0 @@ -TI/National Semiconductor LP3943 GPIO controller - -Required properties: - - compatible: "ti,lp3943-gpio" - - gpio-controller: Marks the device node as a GPIO controller. - - #gpio-cells: Should be 2. See gpio.txt in this directory for a - description of the cells format. - -Example: -Simple LED controls with LP3943 GPIO controller - -&i2c4 { - lp3943@60 { - compatible = "ti,lp3943"; - reg = <0x60>; - - gpioex: gpio { - compatible = "ti,lp3943-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - }; -}; - -leds { - compatible = "gpio-leds"; - indicator1 { - label = "indi1"; - gpios = <&gpioex 9 GPIO_ACTIVE_LOW>; - }; - - indicator2 { - label = "indi2"; - gpios = <&gpioex 10 GPIO_ACTIVE_LOW>; - default-state = "off"; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt b/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt deleted file mode 100644 index b3a6444b8f45..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt +++ /dev/null @@ -1,59 +0,0 @@ -GPIO driver for Maxim MAX3191x industrial serializer - -Required properties: - - compatible: Must be one of: - "maxim,max31910" - "maxim,max31911" - "maxim,max31912" - "maxim,max31913" - "maxim,max31953" - "maxim,max31963" - - reg: Chip select number. - - gpio-controller: Marks the device node as a GPIO controller. - - #gpio-cells: Should be two. For consumer use see gpio.txt. - -Optional properties: - - #daisy-chained-devices: - Number of chips in the daisy-chain (default is 1). - - maxim,modesel-gpios: GPIO pins to configure modesel of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - (if each chip is driven by a separate pin) or 1 - (if all chips are wired to the same pin). - - maxim,fault-gpios: GPIO pins to read fault of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - or 1. - - maxim,db0-gpios: GPIO pins to configure debounce of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - or 1. - - maxim,db1-gpios: GPIO pins to configure debounce of each chip. - The number of GPIOs must equal "maxim,db0-gpios". - - maxim,modesel-8bit: Boolean whether the modesel pin of the chips is - pulled high (8-bit mode). Use this if the modesel pin - is hardwired and consequently "maxim,modesel-gpios" - cannot be specified. By default if neither this nor - "maxim,modesel-gpios" is given, the driver assumes - that modesel is pulled low (16-bit mode). - - maxim,ignore-undervoltage: - Boolean whether to ignore undervoltage alarms signaled - by the "maxim,fault-gpios" or by the status byte - (in 16-bit mode). Use this if the chips are powered - through 5VOUT instead of VCC24V, in which case they - will constantly signal undervoltage. - -For other required and optional properties of SPI slave nodes please refer to -../spi/spi-bus.txt. - -Example: - gpio@0 { - compatible = "maxim,max31913"; - reg = <0>; - gpio-controller; - #gpio-cells = <2>; - - maxim,modesel-gpios = <&gpio2 23>; - maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>; - maxim,db0-gpios = <&gpio2 25>; - maxim,db1-gpios = <&gpio2 26>; - - spi-max-frequency = <25000000>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-max77620.txt b/Documentation/devicetree/bindings/gpio/gpio-max77620.txt deleted file mode 100644 index 410e716fd3d2..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-max77620.txt +++ /dev/null @@ -1,25 +0,0 @@ -GPIO 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. - -Required properties: -------------------- -- gpio-controller : Marks the device node as a gpio controller. -- #gpio-cells : Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -For more details, please refer generic GPIO DT binding document -. - -Example: --------- -#include -... -max77620@3c { - compatible = "maxim,max77620"; - - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt b/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt deleted file mode 100644 index f93d51478d5a..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt +++ /dev/null @@ -1,38 +0,0 @@ -Lantiq SoC External Bus memory mapped GPIO controller - -By attaching hardware latches to the EBU it is possible to create output -only gpios. This driver configures a special memory address, which when -written to outputs 16 bit to the latches. - -The node describing the memory mapped GPIOs needs to be a child of the node -describing the "lantiq,localbus". - -Required properties: -- compatible : Should be "lantiq,gpio-mm-lantiq" -- reg : Address and length of the register set for the device -- #gpio-cells : Should be two. The first cell is the pin number and - the second cell is used to specify optional parameters (currently - unused). -- gpio-controller : Marks the device node as a gpio controller. - -Optional properties: -- lantiq,shadow : The default value that we shall assume as already set on the - shift register cascade. - -Example: - -localbus@0 { - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x0 0x3ffffff /* addrsel0 */ - 1 0 0x4000000 0x4000010>; /* addsel1 */ - compatible = "lantiq,localbus", "simple-bus"; - - gpio_mm0: gpio@4000000 { - compatible = "lantiq,gpio-mm"; - reg = <1 0x0 0x10>; - gpio-controller; - #gpio-cells = <2>; - lantiq,shadow = <0x77f> - }; -} diff --git a/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt b/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt deleted file mode 100644 index 410759de9f09..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt +++ /dev/null @@ -1,18 +0,0 @@ -Turris Mox Moxtet GPIO expander via Moxtet bus - -Required properties: - - compatible : Should be "cznic,moxtet-gpio". - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Other properties are required for a Moxtet bus device, please refer to -Documentation/devicetree/bindings/bus/moxtet.txt. - -Example: - - moxtet_sfp: gpio@0 { - compatible = "cznic,moxtet-gpio"; - gpio-controller; - #gpio-cells = <2>; - reg = <0>; - } diff --git a/Documentation/devicetree/bindings/gpio/gpio-palmas.txt b/Documentation/devicetree/bindings/gpio/gpio-palmas.txt deleted file mode 100644 index 08b5b52a3ae0..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-palmas.txt +++ /dev/null @@ -1,27 +0,0 @@ -Palmas GPIO controller bindings - -Required properties: -- compatible: - - "ti,palams-gpio" for palma series of the GPIO controller - - "ti,tps80036-gpio" for Palma series device TPS80036. - - "ti,tps65913-gpio" for palma series device TPS65913. - - "ti,tps65914-gpio" for palma series device TPS65914. -- #gpio-cells : Should be two. - - first cell is the gpio pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller : Marks the device node as a GPIO controller. - -Note: This gpio node will be sub node of palmas node. - -Example: - palmas: tps65913@58 { - ::::::::::: - palmas_gpio: palmas_gpio { - compatible = "ti,palmas-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - ::::::::::: - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml deleted file mode 100644 index 6f73961001b7..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/gpio-pca9570.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: PCA9570 I2C GPO expander - -maintainers: - - Sungbo Eo - -properties: - compatible: - enum: - - dlg,slg7xl45106 - - nxp,pca9570 - - nxp,pca9571 - - reg: - maxItems: 1 - - gpio-controller: true - - '#gpio-cells': - const: 2 - - gpio-line-names: - minItems: 4 - maxItems: 8 - - label: - description: A descriptive name for this device. - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - gpio@24 { - compatible = "nxp,pca9570"; - reg = <0x24>; - gpio-controller; - #gpio-cells = <2>; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 4d3f52f8d1b8..12134c737ad8 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -68,6 +68,7 @@ properties: - ti,pca9536 - ti,tca6408 - ti,tca6416 + - ti,tca6418 - ti,tca6424 - ti,tca9535 - ti,tca9538 diff --git a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt b/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt deleted file mode 100644 index fba3c61f6a5b..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt +++ /dev/null @@ -1,34 +0,0 @@ -Generic Parallel-in/Serial-out Shift Register GPIO Driver - -This binding describes generic parallel-in/serial-out shift register -devices that can be used for GPI (General Purpose Input). This includes -SN74165 serial-out shift registers and the SN65HVS88x series of -industrial serializers. - -Required properties: - - compatible : Should be "pisosr-gpio". - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Optional properties: - - ngpios : Number of used GPIO lines (0..n-1), default is 8. - - load-gpios : GPIO pin specifier attached to load enable, this - pin is pulsed before reading from the device to - load input pin values into the device. - -For other required and optional properties of SPI slave -nodes please refer to ../spi/spi-bus.txt. - -Example: - - gpio@0 { - compatible = "ti,sn65hvs882", "pisosr-gpio"; - gpio-controller; - #gpio-cells = <2>; - - load-gpios = <&gpio2 23 GPIO_ACTIVE_LOW>; - - reg = <0>; - spi-max-frequency = <1000000>; - spi-cpol; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml deleted file mode 100644 index 157969bc4c46..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/gpio-tpic2810.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: TPIC2810 GPIO controller - -maintainers: - - Aswath Govindraju - -properties: - compatible: - enum: - - ti,tpic2810 - - reg: - maxItems: 1 - - gpio-controller: true - - "#gpio-cells": - const: 2 - - gpio-line-names: - minItems: 1 - maxItems: 32 - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - gpio@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - gpio-line-names = "LED A", "LED B", "LED C"; - }; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt deleted file mode 100644 index 92ea9c8f6399..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt +++ /dev/null @@ -1,20 +0,0 @@ -* TS-4800 FPGA's GPIO controller bindings - -Required properties: -- compatible: Must be "technologic,ts4800-gpio". -- #gpio-cells: Should be two. The first cell is the pin number. -- reg: Physical base address of the controller and length - of memory mapped region. - -Optional property: -- ngpios: See "gpio.txt" - -Example: - -gpio1: gpio { - compatible = "technologic,ts4800-gpio"; - reg = <0x10020 0x6>; - ngpios = <8>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt deleted file mode 100644 index 3f8e71b1ab2a..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Technologic Systems I2C-FPGA's GPIO controller bindings - -This bindings describes the GPIO controller for Technologic's FPGA core. -TS-4900's FPGA encodes the GPIO state on 3 bits, whereas the TS-7970's FPGA -uses 2 bits: it doesn't use a dedicated input bit. - -Required properties: -- compatible: Should be one of the following - "technologic,ts4900-gpio" - "technologic,ts7970-gpio" -- reg: Physical base address of the controller and length - of memory mapped region. -- #gpio-cells: Should be two. The first cell is the pin number. -- gpio-controller: Marks the device node as a gpio controller. - -Optional property: -- ngpios: Number of GPIOs this controller is instantiated with, - the default is 32. See gpio.txt for more details. - -Example: - -&i2c2 { - gpio8: gpio@28 { - compatible = "technologic,ts4900-gpio"; - reg = <0x28>; - #gpio-cells = <2>; - gpio-controller; - ngpios = <32>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt deleted file mode 100644 index 66788fda1db3..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt +++ /dev/null @@ -1,29 +0,0 @@ -twl4030 GPIO controller bindings - -Required properties: -- compatible: - - "ti,twl4030-gpio" for twl4030 GPIO controller -- #gpio-cells : Should be two. - - first cell is the pin number - - second cell is used to specify optional parameters (unused) -- gpio-controller : Marks the device node as a GPIO controller. -- #interrupt-cells : Should be 2. -- interrupt-controller: Mark the device node as an interrupt controller - The first cell is the GPIO number. - The second cell is not used. -- ti,use-leds : Enables LEDA and LEDB outputs if set -- ti,debounce : if n-th bit is set, debounces GPIO-n -- ti,mmc-cd : if n-th bit is set, GPIO-n controls VMMC(n+1) -- ti,pullups : if n-th bit is set, set a pullup on GPIO-n -- ti,pulldowns : if n-th bit is set, set a pulldown on GPIO-n - -Example: - -twl_gpio: gpio { - compatible = "ti,twl4030-gpio"; - #gpio-cells = <2>; - gpio-controller; - #interrupt-cells = <2>; - interrupt-controller; - ti,use-leds; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt deleted file mode 100644 index 7ddf292db144..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt +++ /dev/null @@ -1,64 +0,0 @@ -APM X-Gene Standby GPIO controller bindings - -This is a gpio controller in the standby domain. It also supports interrupt in -some particular pins which are sourced to its parent interrupt controller -as diagram below: - +-----------------+ - | X-Gene standby | - | GPIO controller +------ GPIO_0 -+------------+ | | ... -| Parent IRQ | EXT_INT_0 | +------ GPIO_8/EXT_INT_0 -| controller | (SPI40) | | ... -| (GICv2) +--------------+ +------ GPIO_[N+8]/EXT_INT_N -| | ... | | -| | EXT_INT_N | +------ GPIO_[N+9] -| | (SPI[40 + N])| | ... -| +--------------+ +------ GPIO_MAX -+------------+ +-----------------+ - -Required properties: -- compatible: "apm,xgene-gpio-sb" for the X-Gene Standby GPIO controller -- reg: Physical base address and size of the controller's registers -- #gpio-cells: Should be two. - - first cell is the pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller: Marks the device node as a GPIO controller. -- interrupts: The EXT_INT_0 parent interrupt resource must be listed first. -- interrupt-cells: Should be two. - - first cell is 0-N corresponding for EXT_INT_0 to EXT_INT_N. - - second cell is used to specify flags. -- interrupt-controller: Marks the device node as an interrupt controller. -- apm,nr-gpios: Optional, specify number of gpios pin. -- apm,nr-irqs: Optional, specify number of interrupt pins. -- apm,irq-start: Optional, specify lowest gpio pin support interrupt. - -Example: - sbgpio: gpio@17001000{ - compatible = "apm,xgene-gpio-sb"; - reg = <0x0 0x17001000 0x0 0x400>; - #gpio-cells = <2>; - gpio-controller; - interrupts = <0x0 0x28 0x1>, - <0x0 0x29 0x1>, - <0x0 0x2a 0x1>, - <0x0 0x2b 0x1>, - <0x0 0x2c 0x1>, - <0x0 0x2d 0x1>; - interrupt-parent = <&gic>; - #interrupt-cells = <2>; - interrupt-controller; - apm,nr-gpios = <22>; - apm,nr-irqs = <6>; - apm,irq-start = <8>; - }; - - testuser { - compatible = "example,testuser"; - /* Use the GPIO_13/EXT_INT_5 line as an active high triggered - * level interrupt - */ - interrupts = <5 4>; - interrupt-parent = <&sbgpio>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene.txt deleted file mode 100644 index 86dbb05e7758..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xgene.txt +++ /dev/null @@ -1,22 +0,0 @@ -APM X-Gene SoC GPIO controller bindings - -This is a gpio controller that is part of the flash controller. -This gpio controller controls a total of 48 gpios. - -Required properties: -- compatible: "apm,xgene-gpio" for X-Gene GPIO controller -- reg: Physical base address and size of the controller's registers -- #gpio-cells: Should be two. - - first cell is the pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller: Marks the device node as a GPIO controller. - -Example: - gpio0: gpio0@1701c000 { - compatible = "apm,xgene-gpio"; - reg = <0x0 0x1701c000 0x0 0x40>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt b/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt deleted file mode 100644 index e13cc399b363..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt +++ /dev/null @@ -1,46 +0,0 @@ -GPIO Driver for XRA1403 16-BIT GPIO Expander With Reset Input from EXAR - -The XRA1403 is an 16-bit GPIO expander with an SPI interface. Features available: - - Individually programmable inputs: - - Internal pull-up resistors - - Polarity inversion - - Individual interrupt enable - - Rising edge and/or Falling edge interrupt - - Input filter - - Individually programmable outputs - - Output Level Control - - Output Three-State Control - -Properties ----------- -Check documentation for SPI and GPIO controllers regarding properties needed to configure the node. - - - compatible = "exar,xra1403". - - reg - SPI id of the device. - - gpio-controller - marks the node as gpio. - - #gpio-cells - should be two where the first cell is the pin number - and the second one is used for optional parameters. - -Optional properties: -------------------- - - reset-gpios: in case available used to control the device reset line. - - interrupt-controller - marks the node as interrupt controller. - - #interrupt-cells - should be two and represents the number of cells - needed to encode interrupt source. - -Example --------- - - gpioxra0: gpio@2 { - compatible = "exar,xra1403"; - reg = <2>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - - reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; - spi-max-frequency = <1000000>; - }; diff --git a/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt b/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt deleted file mode 100644 index d58b3958f3ea..000000000000 --- a/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt +++ /dev/null @@ -1,24 +0,0 @@ -* IBM/AMCC/APM GPIO Controller for PowerPC 4XX series and compatible SoCs - -All GPIOs are pin-shared with other functions. DCRs control whether a -particular pin that has GPIO capabilities acts as a GPIO or is used for -another purpose. GPIO outputs are separately programmable to emulate -an open-drain driver. - -Required properties: - - compatible: must be "ibm,ppc4xx-gpio" - - reg: address and length of the register set for the device - - #gpio-cells: must be set to 2. The first cell is the pin number - and the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low - - gpio-controller: marks the device node as a gpio controller. - -Example: - -GPIO0: gpio@ef600b00 { - compatible = "ibm,ppc4xx-gpio"; - reg = <0xef600b00 0x00000048>; - #gpio-cells = <2>; - gpio-controller; -}; diff --git a/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml b/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml new file mode 100644 index 000000000000..42021ee14125 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/lacie,netxbig-gpio-ext.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NetxBig GPIO extension bus + +maintainers: + - Simon Guinot + +description: > + GPIO extension bus found on some LaCie/Seagate boards + (Example: 2Big/5Big Network v2, 2Big NAS). + +properties: + compatible: + items: + - const: lacie,netxbig-gpio-ext + + addr-gpios: + description: GPIOs representing the address register (LSB->MSB). + items: + - description: bit 0 (LSB) + - description: bit 1 + - description: bit 2 (MSB) + + data-gpios: + description: GPIOs representing the data register (LSB->MSB). + items: + - description: bit 0 (LSB) + - description: bit 1 + - description: bit 2 (MSB) + + enable-gpio: + description: Latches the new configuration (address, data) on raising edge. + maxItems: 1 + +required: + - compatible + - addr-gpios + - data-gpios + - enable-gpio + +additionalProperties: false + +examples: + - | + #include + + gpio { + compatible = "lacie,netxbig-gpio-ext"; + addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH + &gpio1 16 GPIO_ACTIVE_HIGH + &gpio1 17 GPIO_ACTIVE_HIGH>; + data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH + &gpio1 13 GPIO_ACTIVE_HIGH + &gpio1 14 GPIO_ACTIVE_HIGH>; + enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml b/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml new file mode 100644 index 000000000000..eaf53a89542a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/lantiq,gpio-mm-lantiq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq SoC External Bus memory mapped GPIO controller + +maintainers: + - John Crispin + +description: | + By attaching hardware latches to the EBU it is possible to create output + only gpios. This driver configures a special memory address, which when + written to outputs 16 bit to the latches. + + The node describing the memory mapped GPIOs needs to be a child of the node + describing the "lantiq,localbus". + +properties: + compatible: + enum: + - lantiq,gpio-mm-lantiq + - lantiq,gpio-mm + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + lantiq,shadow: + description: The default value that we shall assume as already set on the shift register cascade. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - '#gpio-cells' + - gpio-controller + +additionalProperties: false + +examples: + - | + gpio@4000000 { + compatible = "lantiq,gpio-mm-lantiq"; + reg = <0x4000000 0x10>; + gpio-controller; + #gpio-cells = <2>; + lantiq,shadow = <0x77f>; + }; diff --git a/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml b/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml deleted file mode 100644 index 1a472c05697c..000000000000 --- a/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/loongson,ls1x-gpio.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Loongson-1 GPIO controller - -maintainers: - - Keguang Zhang - -properties: - compatible: - const: loongson,ls1x-gpio - - reg: - maxItems: 1 - - gpio-controller: true - - "#gpio-cells": - const: 2 - - ngpios: - minimum: 1 - maximum: 32 - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - - ngpios - -additionalProperties: false - -examples: - - | - gpio0: gpio@1fd010c0 { - compatible = "loongson,ls1x-gpio"; - reg = <0x1fd010c0 0x4>; - - gpio-controller; - #gpio-cells = <2>; - - ngpios = <32>; - }; - -... diff --git a/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml b/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml new file mode 100644 index 000000000000..82a190a715f9 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/maxim,max31910.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX3191x GPIO serializer + +maintainers: + - Lukas Wunner + +properties: + compatible: + enum: + - maxim,max31910 + - maxim,max31911 + - maxim,max31912 + - maxim,max31913 + - maxim,max31953 + - maxim,max31963 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + '#daisy-chained-devices': + description: Number of chips in the daisy-chain. + default: 1 + + maxim,modesel-gpios: + description: + GPIO pins to configure modesel of each chip. The number of GPIOs must + equal "#daisy-chained-devices" (if each chip is driven by a separate pin) + or 1 (if all chips are wired to the same pin). + + maxim,fault-gpios: + description: + GPIO pins to read fault of each chip. The number of GPIOs must equal + "#daisy-chained-devices" or 1. + + maxim,db0-gpios: + description: + GPIO pins to configure debounce of each chip. The number of GPIOs must + equal "#daisy-chained-devices" or 1. + + maxim,db1-gpios: + description: + GPIO pins to configure debounce of each chip. The number of GPIOs must + equal "maxim,db0-gpios". + + maxim,modesel-8bit: + description: + Boolean whether the modesel pin of the chips is pulled high (8-bit mode). + Use this if the modesel pin is hardwired and consequently + "maxim,modesel-gpios" cannot be specified. By default if neither this nor + "maxim,modesel-gpios" is given, the driver assumes that modesel is pulled + low (16-bit mode). + type: boolean + + maxim,ignore-undervoltage: + description: + Boolean whether to ignore undervoltage alarms signaled by the + "maxim,fault-gpios" or by the status byte (in 16-bit mode). Use this if + the chips are powered through 5VOUT instead of VCC24V, in which case they + will constantly signal undervoltage. + type: boolean + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@0 { + compatible = "maxim,max31913"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + + maxim,modesel-gpios = <&gpio2 23>; + maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>; + maxim,db0-gpios = <&gpio2 25>; + maxim,db1-gpios = <&gpio2 26>; + + spi-max-frequency = <25000000>; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt deleted file mode 100644 index dd031fc93b55..000000000000 --- a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt +++ /dev/null @@ -1,49 +0,0 @@ -* Microchip PIC32 GPIO devices (PIO). - -Required properties: - - compatible: "microchip,pic32mzda-gpio" - - reg: Base address and length for the device. - - interrupts: The port interrupt shared by all pins. - - gpio-controller: Marks the port as GPIO controller. - - #gpio-cells: Two. The first cell is the pin number and - the second cell is used to specify the gpio polarity as defined in - defined in : - 0 = GPIO_ACTIVE_HIGH - 1 = GPIO_ACTIVE_LOW - 2 = GPIO_OPEN_DRAIN - - interrupt-controller: Marks the device node as an interrupt controller. - - #interrupt-cells: Two. The first cell is the GPIO number and second cell - is used to specify the trigger type as defined in - : - IRQ_TYPE_EDGE_RISING - IRQ_TYPE_EDGE_FALLING - IRQ_TYPE_EDGE_BOTH - - clocks: Clock specifier (see clock bindings for details). - - microchip,gpio-bank: Specifies which bank a controller owns. - - gpio-ranges: Interaction with the PINCTRL subsystem. - -Example: - -/* PORTA */ -gpio0: gpio0@1f860000 { - compatible = "microchip,pic32mzda-gpio"; - reg = <0x1f860000 0x100>; - interrupts = <118 IRQ_TYPE_LEVEL_HIGH>; - #gpio-cells = <2>; - gpio-controller; - interrupt-controller; - #interrupt-cells = <2>; - clocks = <&rootclk PB4CLK>; - microchip,gpio-bank = <0>; - gpio-ranges = <&pic32_pinctrl 0 0 16>; -}; - -keys { - ... - - button@sw1 { - label = "ESC"; - linux,code = <1>; - gpios = <&gpio0 12 0>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml new file mode 100644 index 000000000000..d8d932c86697 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/microchip,pic32mzda-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PIC32 GPIO controller + +maintainers: + - Joshua Henderson + - Purna Chandra Mandal + +properties: + compatible: + const: microchip,pic32mzda-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + gpio-ranges: true + + "#gpio-cells": + const: 2 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + clocks: + maxItems: 1 + + microchip,gpio-bank: + description: Bank index owned by the controller + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - gpio-ranges + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + - clocks + - microchip,gpio-bank + +additionalProperties: false + +examples: + - | + #include + + gpio@1f860000 { + compatible = "microchip,pic32mzda-gpio"; + reg = <0x1f860000 0x100>; + interrupts = <118 IRQ_TYPE_LEVEL_HIGH>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&rootclk 11>; + microchip,gpio-bank = <0>; + gpio-ranges = <&pic32_pinctrl 0 0 16>; + }; diff --git a/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt b/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt deleted file mode 100644 index 50ec2e690701..000000000000 --- a/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt +++ /dev/null @@ -1,22 +0,0 @@ -Binding for the GPIO extension bus found on some LaCie/Seagate boards -(Example: 2Big/5Big Network v2, 2Big NAS). - -Required properties: -- compatible: "lacie,netxbig-gpio-ext". -- addr-gpios: GPIOs representing the address register (LSB -> MSB). -- data-gpios: GPIOs representing the data register (LSB -> MSB). -- enable-gpio: latches the new configuration (address, data) on raising edge. - -Example: - -netxbig_gpio_ext: netxbig-gpio-ext { - compatible = "lacie,netxbig-gpio-ext"; - - addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH - &gpio1 16 GPIO_ACTIVE_HIGH - &gpio1 17 GPIO_ACTIVE_HIGH>; - data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH - &gpio1 13 GPIO_ACTIVE_HIGH - &gpio1 14 GPIO_ACTIVE_HIGH>; - enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>; -}; diff --git a/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt b/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt deleted file mode 100644 index df63da46309c..000000000000 --- a/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt +++ /dev/null @@ -1,26 +0,0 @@ -Nintendo Wii (Hollywood) GPIO controller - -Required properties: -- compatible: "nintendo,hollywood-gpio" -- reg: Physical base address and length of the controller's registers. -- gpio-controller: Marks the device node as a GPIO controller. -- #gpio-cells: Should be <2>. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted). - -Optional properties: -- ngpios: see Documentation/devicetree/bindings/gpio/gpio.txt -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: Should be two. -- interrupts: Interrupt specifier for the controller's Broadway (PowerPC) - interrupt. - -Example: - - GPIO: gpio@d8000c0 { - #gpio-cells = <2>; - compatible = "nintendo,hollywood-gpio"; - reg = <0x0d8000c0 0x40>; - gpio-controller; - ngpios = <24>; - } diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt deleted file mode 100644 index 627efc78ecf2..000000000000 --- a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt +++ /dev/null @@ -1,59 +0,0 @@ -NXP LPC18xx/43xx GPIO controller Device Tree Bindings ------------------------------------------------------ - -Required properties: -- compatible : Should be "nxp,lpc1850-gpio" -- reg : List of addresses and lengths of the GPIO controller - register sets -- reg-names : Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and - "gpio-gpoup1-ic" -- clocks : Phandle and clock specifier pair for GPIO controller -- resets : Phandle and reset specifier pair for GPIO controller -- gpio-controller : Marks the device node as a GPIO controller -- #gpio-cells : Should be two: - - The first cell is the GPIO line number - - The second cell is used to specify polarity -- interrupt-controller : Marks the device node as an interrupt controller -- #interrupt-cells : Should be two: - - The first cell is an interrupt number within - 0..9 range, for GPIO pin interrupts it is equal - to 'nxp,gpio-pin-interrupt' property value of - GPIO pin configuration, 8 is for GPIO GROUP0 - interrupt, 9 is for GPIO GROUP1 interrupt - - The second cell is used to specify interrupt type - -Optional properties: -- gpio-ranges : Mapping between GPIO and pinctrl - -Example: -#define LPC_GPIO(port, pin) (port * 32 + pin) -#define LPC_PIN(port, pin) (0x##port * 32 + pin) - -gpio: gpio@400f4000 { - compatible = "nxp,lpc1850-gpio"; - reg = <0x400f4000 0x4000>, <0x40087000 0x1000>, - <0x40088000 0x1000>, <0x40089000 0x1000>; - reg-names = "gpio", "gpio-pin-ic", - "gpio-group0-ic", "gpio-gpoup1-ic"; - clocks = <&ccu1 CLK_CPU_GPIO>; - resets = <&rgu 28>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>, - ... - <&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>; -}; - -gpio_joystick { - compatible = "gpio-keys"; - ... - - button0 { - ... - interrupt-parent = <&gpio>; - interrupts = <1 IRQ_TYPE_EDGE_BOTH>; - gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml new file mode 100644 index 000000000000..0ef5f90f69ff --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/nxp,lpc1850-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx/43xx GPIO controller + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-gpio + + reg: + minItems: 1 + maxItems: 4 + + reg-names: + minItems: 1 + items: + - const: gpio + - const: gpio-pin-ic + - const: gpio-group0-ic + - const: gpio-gpoup1-ic + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + description: | + - The first cell is an interrupt number within + 0..9 range, for GPIO pin interrupts it is equal + to 'nxp,gpio-pin-interrupt' property value of + GPIO pin configuration, 8 is for GPIO GROUP0 + interrupt, 9 is for GPIO GROUP1 interrupt + - The second cell is used to specify interrupt type + + gpio-ranges: true + +required: + - compatible + - reg + - clocks + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + #include + + gpio@400f4000 { + compatible = "nxp,lpc1850-gpio"; + reg = <0x400f4000 0x4000>, <0x40087000 0x1000>, + <0x40088000 0x1000>, <0x40089000 0x1000>; + reg-names = "gpio", "gpio-pin-ic", "gpio-group0-ic", "gpio-gpoup1-ic"; + clocks = <&ccu1 CLK_CPU_GPIO>; + resets = <&rgu 28>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + diff --git a/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml b/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml new file mode 100644 index 000000000000..db98ba413fb6 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/pisosr-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic Parallel-in/Serial-out Shift Register GPIO Driver + +description: + This binding describes generic parallel-in/serial-out shift register + devices that can be used for GPI (General Purpose Input). This includes + SN74165 serial-out shift registers and the SN65HVS88x series of + industrial serializers. + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - pisosr-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ngpios: + maximum: 32 + default: 8 + + load-gpios: + description: + GPIO pin specifier attached to load enable, this + pin is pulsed before reading from the device to + load input pin values into the device. + + spi-cpol: true + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@0 { + compatible = "pisosr-gpio"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + load-gpios = <&gpio2 23 GPIO_ACTIVE_LOW>; + spi-max-frequency = <1000000>; + spi-cpol; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml index bd35cbf7fa09..c51e10680c0a 100644 --- a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml @@ -60,9 +60,6 @@ properties: required: - compatible - reg - - interrupts - - interrupt-controller - - "#interrupt-cells" - clocks - "#gpio-cells" - gpio-controller diff --git a/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml b/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml new file mode 100644 index 000000000000..519c4c2158f7 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/qca,ar7100-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Atheros AR7xxx/AR9xxx GPIO controller + +maintainers: + - Alban Bedel + +properties: + compatible: + oneOf: + - items: + - const: qca,ar9132-gpio + - const: qca,ar7100-gpio + - enum: + - qca,ar7100-gpio + - qca,ar9340-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ngpios: true + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - ngpios + +additionalProperties: false + +examples: + - | + gpio@18040000 { + compatible = "qca,ar9132-gpio", "qca,ar7100-gpio"; + reg = <0x18040000 0x30>; + interrupts = <2>; + ngpios = <22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index d76987ce8e50..bdd83f42615c 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -41,6 +41,9 @@ properties: "#interrupt-cells": const: 2 + power-domains: + maxItems: 1 + patternProperties: "^.+-hog(-[0-9]+)?$": type: object diff --git a/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml b/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml deleted file mode 100644 index d8cce73ea0ae..000000000000 --- a/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/rockchip,rk3328-grf-gpio.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Rockchip RK3328 General Register Files GPIO controller - -description: - The Rockchip RK3328 General Register File (GRF) outputs only the - GPIO_MUTE pin, originally for codec mute control, but it can also be used - for general purpose. It is manipulated by the GRF_SOC_CON10 register. - If needed in the future support for the HDMI pins can also be added. - The GPIO node should be declared as the child of the GRF node. - - The GPIO_MUTE pin is referred to in the format - - <&grf_gpio 0 GPIO_ACTIVE_LOW> - - The first cell is the pin number and - the second cell is used to specify the GPIO polarity - 0 = Active high - 1 = Active low - -maintainers: - - Heiko Stuebner - -properties: - compatible: - const: rockchip,rk3328-grf-gpio - - gpio-controller: true - - "#gpio-cells": - const: 2 - -required: - - compatible - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - grf_gpio: gpio { - compatible = "rockchip,rk3328-grf-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt b/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt deleted file mode 100644 index 1b30812b015b..000000000000 --- a/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt +++ /dev/null @@ -1,21 +0,0 @@ -Synopsys GPIO via CREG (Control REGisters) driver - -Required properties: -- compatible : "snps,creg-gpio-hsdk" or "snps,creg-gpio-axs10x". -- reg : Exactly one register range with length 0x4. -- #gpio-cells : Since the generic GPIO binding is used, the - amount of cells must be specified as 2. The first cell is the - pin number, the second cell is used to specify optional parameters: - See "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt. -- gpio-controller : Marks the device node as a GPIO controller. -- ngpios: Number of GPIO pins. - -Example: - -gpio: gpio@f00014b0 { - compatible = "snps,creg-gpio-hsdk"; - reg = <0xf00014b0 0x4>; - gpio-controller; - #gpio-cells = <2>; - ngpios = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/spear_spics.txt b/Documentation/devicetree/bindings/gpio/spear_spics.txt deleted file mode 100644 index dd04d96e6ff1..000000000000 --- a/Documentation/devicetree/bindings/gpio/spear_spics.txt +++ /dev/null @@ -1,49 +0,0 @@ -=== ST Microelectronics SPEAr SPI CS Driver === - -SPEAr platform provides a provision to control chipselects of ARM PL022 Prime -Cell spi controller through its system registers, which otherwise remains under -PL022 control. If chipselect remain under PL022 control then they would be -released as soon as transfer is over and TxFIFO becomes empty. This is not -desired by some of the device protocols above spi which expect (multiple) -transfers without releasing their chipselects. - -Chipselects can be controlled by software by turning them as GPIOs. SPEAr -provides another interface through system registers through which software can -directly control each PL022 chipselect. Hence, it is natural for SPEAr to export -the control of this interface as gpio. - -Required properties: - - * compatible: should be defined as "st,spear-spics-gpio" - * reg: mentioning address range of spics controller - * st-spics,peripcfg-reg: peripheral configuration register offset - * st-spics,sw-enable-bit: bit offset to enable sw control - * st-spics,cs-value-bit: bit offset to drive chipselect low or high - * st-spics,cs-enable-mask: chip select number bit mask - * st-spics,cs-enable-shift: chip select number program offset - * gpio-controller: Marks the device node as gpio controller - * #gpio-cells: should be 1 and will mention chip select number - -All the above bit offsets are within peripcfg register. - -Example: -------- -spics: spics@e0700000{ - compatible = "st,spear-spics-gpio"; - reg = <0xe0700000 0x1000>; - st-spics,peripcfg-reg = <0x3b0>; - st-spics,sw-enable-bit = <12>; - st-spics,cs-value-bit = <11>; - st-spics,cs-enable-mask = <3>; - st-spics,cs-enable-shift = <8>; - gpio-controller; - #gpio-cells = <2>; -}; - - -spi0: spi@e0100000 { - num-cs = <3>; - cs-gpios = <&gpio1 7 0>, <&spics 0>, - <&spics 1>; - ... -} diff --git a/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml b/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml new file mode 100644 index 000000000000..3b0d2112da79 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/st,spear-spics-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST Microelectronics SPEAr SPI CS GPIO Controller + +maintainers: + - Viresh Kumar + +description: > + SPEAr platform provides a provision to control chipselects of ARM PL022 Prime + Cell spi controller through its system registers, which otherwise remains + under PL022 control. If chipselect remain under PL022 control then they would + be released as soon as transfer is over and TxFIFO becomes empty. This is not + desired by some of the device protocols above spi which expect (multiple) + transfers without releasing their chipselects. + + Chipselects can be controlled by software by turning them as GPIOs. SPEAr + provides another interface through system registers through which software can + directly control each PL022 chipselect. Hence, it is natural for SPEAr to + export the control of this interface as gpio. + +properties: + compatible: + const: st,spear-spics-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + st-spics,peripcfg-reg: + description: Offset of the peripcfg register. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,sw-enable-bit: + description: Bit offset to enable software chipselect control. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-value-bit: + description: Bit offset to drive chipselect low or high. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-enable-mask: + description: Bitmask selecting which chipselects to enable. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-enable-shift: + description: Bit shift for programming chipselect number. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - st-spics,peripcfg-reg + - st-spics,sw-enable-bit + - st-spics,cs-value-bit + - st-spics,cs-enable-mask + - st-spics,cs-enable-shift + +additionalProperties: false + +examples: + - | + gpio@e0700000 { + compatible = "st,spear-spics-gpio"; + reg = <0xe0700000 0x1000>; + st-spics,peripcfg-reg = <0x3b0>; + st-spics,sw-enable-bit = <12>; + st-spics,cs-value-bit = <11>; + st-spics,cs-enable-mask = <3>; + st-spics,cs-enable-shift = <8>; + gpio-controller; + #gpio-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml b/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml new file mode 100644 index 000000000000..59f81621408b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/ti,keystone-dsp-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Keystone 2 DSP GPIO controller + +maintainers: + - Grygorii Strashko + +description: | + HOST OS userland running on ARM can send interrupts to DSP cores using + the DSP GPIO controller IP. It provides 28 IRQ signals per each DSP core. + This is one of the component used by the IPC mechanism used on Keystone SOCs. + + For example TCI6638K2K SoC has 8 DSP GPIO controllers: + - 8 for C66x CorePacx CPUs 0-7 + + Keystone 2 DSP GPIO controller has specific features: + - each GPIO can be configured only as output pin; + - setting GPIO value to 1 causes IRQ generation on target DSP core; + - reading pin value returns 0 - if IRQ was handled or 1 - IRQ is still + pending. + +properties: + compatible: + const: ti,keystone-dsp-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio,syscon-dev: + description: + Phandle and offset of device's specific registers within the syscon state + control registers + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon + - description: register offset within state control registers + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - gpio,syscon-dev + +additionalProperties: false + +examples: + - | + gpio@240 { + compatible = "ti,keystone-dsp-gpio"; + reg = <0x240 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio,syscon-dev = <&devctrl 0x240>; + }; diff --git a/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml new file mode 100644 index 000000000000..5e3e199fd9a4 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ti,twl4030-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI TWL4030 GPIO controller + +maintainers: + - Aaro Koskinen + - Andreas Kemnade + - Kevin Hilman + - Roger Quadros + - Tony Lindgren + +properties: + compatible: + const: ti,twl4030-gpio + + '#gpio-cells': + const: 2 + + gpio-controller: true + + '#interrupt-cells': + const: 1 + + interrupt-controller: true + + ti,debounce: + description: Debounce control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,mmc-cd: + description: MMC card detect control bits. Each bit corresponds to a GPIO pin for VMMC(n+1). + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,pullups: + description: Pull-up control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,pulldowns: + description: Pull-down control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,use-leds: + type: boolean + description: Enables LEDA and LEDB outputs if set + +additionalProperties: false + +examples: + - | + gpio { + compatible = "ti,twl4030-gpio"; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <1>; + interrupt-controller; + ti,use-leds; + }; diff --git a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml new file mode 100644 index 000000000000..0299d4a25086 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trivial-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Trivial 2-cell GPIO controllers + +maintainers: + - Bartosz Golaszewski + +properties: + compatible: + oneOf: + - items: + - enum: + - cirrus,ep7312-gpio + - const: cirrus,ep7209-gpio + - enum: + - apm,xgene-gpio + - cirrus,ep7209-gpio + - cznic,moxtet-gpio + - dlg,slg7xl45106 + - fcs,fxl6408 + - gateworks,pld-gpio + - ibm,ppc4xx-gpio + - loongson,ls1x-gpio + - maxim,max77620 + - nintendo,hollywood-gpio + - nxp,pca9570 + - nxp,pca9571 + - rockchip,rk3328-grf-gpio + - snps,creg-gpio-hsdk + - technologic,ts4800-gpio + - technologic,ts4900-gpio + - technologic,ts7970-gpio + - ti,741g125 # for 741G125 (1-bit Input), + - ti,741g174 # for 741G74 (1-bit Output), + - ti,742g125 # for 742G125 (2-bit Input), + - ti,7474 # for 7474 (2-bit Output), + - ti,74125 # for 74125 (4-bit Input), + - ti,74175 # for 74175 (4-bit Output), + - ti,74365 # for 74365 (6-bit Input), + - ti,74174 # for 74174 (6-bit Output), + - ti,74244 # for 74244 (8-bit Input), + - ti,74273 # for 74273 (8-bit Output), + - ti,741624 # for 741624 (16-bit Input), + - ti,7416374 # for 7416374 (16-bit Output). + - ti,lp3943-gpio + - ti,palmas-gpio + - ti,tpic2810 + - ti,tps80036-gpio + - ti,tps65913-gpio + - ti,tps65914-gpio + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + gpio-line-names: true + + ngpios: true + + # Don't add more properties + +patternProperties: + "^(hog-[0-9]+|.+-hog(-[0-9]+)?)$": + type: object + required: + - gpio-hog + +required: + - compatible + - '#gpio-cells' + - gpio-controller + +allOf: + - if: + properties: + compatible: + contains: + enum: + - maxim,max77620 + - rockchip,rk3328-grf-gpio + - ti,lp3943-gpio + - ti,palmas-gpio + - ti,tps80036-gpio + - ti,tps65913-gpio + - ti,tps65914-gpio + then: + properties: + reg: false + else: + required: + - reg + +additionalProperties: false + +examples: + - | + gpio@1701c000 { + compatible = "apm,xgene-gpio"; + reg = <0x1701c000 0x40>; + gpio-controller; + #gpio-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml index 8fbf12ca067e..7af4eb2d1858 100644 --- a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml +++ b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml @@ -117,6 +117,7 @@ properties: required: - reg - compatible + - clocks - gpio-controller - "#gpio-cells" diff --git a/Documentation/devicetree/bindings/gpu/apple,agx.yaml b/Documentation/devicetree/bindings/gpu/apple,agx.yaml new file mode 100644 index 000000000000..51629b3833b0 --- /dev/null +++ b/Documentation/devicetree/bindings/gpu/apple,agx.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpu/apple,agx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SoC GPU + +maintainers: + - Sasha Finkelstein + +properties: + compatible: + oneOf: + - enum: + - apple,agx-g13g + - apple,agx-g13s + - apple,agx-g14g + - items: + - enum: + - apple,agx-g13c + - apple,agx-g13d + - const: apple,agx-g13s + + reg: + items: + - description: GPU coprocessor control registers + - description: GPU block MMIO registers + + reg-names: + items: + - const: asc + - const: sgx + + power-domains: + maxItems: 1 + + mboxes: + maxItems: 1 + + memory-region: + items: + - description: Region containing GPU MMU TTBs + - description: Region containing GPU MMU page tables + - description: + Region containing a shared handoff structure for VM + management coordination + - description: Calibration blob. Mostly power-related configuration + - description: Calibration blob. Mostly GPU-related configuration + - description: Shared global variables with GPU firmware + + memory-region-names: + items: + - const: ttbs + - const: pagetables + - const: handoff + - const: hw-cal-a + - const: hw-cal-b + - const: globals + + apple,firmware-abi: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 3 + description: + macOS version the current firmware is paired with, used to pick + the version of firmware ABI to be used. + Bootloader will overwrite this + +required: + - compatible + - reg + - mboxes + - memory-region + - apple,firmware-abi + +additionalProperties: false + +examples: + - | + gpu@6400000 { + compatible = "apple,agx-g13g"; + reg = <0x6400000 0x40000>, + <0x4000000 0x1000000>; + reg-names = "asc", "sgx"; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, + <&gpu_hw_cal_a>, <&gpu_hw_cal_b>, <&gpu_globals>; + memory-region-names = "ttbs", "pagetables", "handoff", + "hw-cal-a", "hw-cal-b", "globals"; + + apple,firmware-abi = <0 0 0>; + }; +... diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml index 7e947016dee9..be198182dbfe 100644 --- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml +++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml @@ -40,6 +40,7 @@ properties: - const: arm,mali-valhall-jm # Mali Valhall GPU model/revision is fully discoverable - items: - enum: + - allwinner,sun55i-a523-mali - mediatek,mt8188-mali - mediatek,mt8192-mali - mediatek,mt8370-mali diff --git a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml index 4450e2e73b3c..c87d7bece0ec 100644 --- a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml +++ b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml @@ -21,6 +21,11 @@ properties: # work with newer dts. - const: img,img-axe - const: img,img-rogue + - items: + - enum: + - thead,th1520-gpu + - const: img,img-bxm-4-64 + - const: img,img-rogue - items: - enum: - ti,j721s2-gpu @@ -77,18 +82,6 @@ required: additionalProperties: false allOf: - # Constraints added alongside the new compatible strings that would otherwise - # create an ABI break. - - if: - properties: - compatible: - contains: - const: img,img-rogue - then: - required: - - power-domains - - power-domain-names - - if: properties: compatible: @@ -97,9 +90,32 @@ allOf: then: properties: power-domains: - maxItems: 1 + items: + - description: Power domain A power-domain-names: maxItems: 1 + required: + - power-domains + - power-domain-names + + - if: + properties: + compatible: + contains: + const: thead,th1520-gpu + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + power-domains: + items: + - description: The single, unified power domain for the GPU on the + TH1520 SoC, integrating all internal IP power domains. + power-domain-names: false + required: + - power-domains - if: properties: @@ -109,9 +125,14 @@ allOf: then: properties: power-domains: - minItems: 2 + items: + - description: Power domain A + - description: Power domain B power-domain-names: minItems: 2 + required: + - power-domains + - power-domain-names - if: properties: diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml index 4f8e11bd5142..fe87a592de45 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml @@ -8,7 +8,7 @@ title: Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and Fault Recording maintainers: - - Alexandru Tachici + - Cedric Encarnacion description: | Analog Devices ADM1266 Cascadable Super Sequencer with Margin diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml index 0ad12d245656..38a8f3a14c02 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Linear Technology 2992 Power Monitor maintainers: - - Alexandru Tachici + - Cedric Encarnacion description: | Linear Technology 2992 Dual Wide Range Power Monitor diff --git a/Documentation/devicetree/bindings/hwmon/adt7475.yaml b/Documentation/devicetree/bindings/hwmon/adt7475.yaml index 79e8d62fa3b3..43e9fe225870 100644 --- a/Documentation/devicetree/bindings/hwmon/adt7475.yaml +++ b/Documentation/devicetree/bindings/hwmon/adt7475.yaml @@ -53,7 +53,10 @@ properties: default: 1 "#pwm-cells": - const: 4 + oneOf: + - const: 3 + - const: 4 + deprecated: true description: | Number of cells in a PWM specifier. - 0: The PWM channel @@ -68,7 +71,7 @@ properties: - 11363636 (88 Hz) - 44444 (22 kHz) - 2: PWM flags 0 or PWM_POLARITY_INVERTED - - 3: The default PWM duty cycle in nanoseconds + - 3: The default PWM duty cycle in nanoseconds, defaults to period. patternProperties: "^adi,bypass-attenuator-in[0-4]$": @@ -124,15 +127,15 @@ examples: adi,bypass-attenuator-in1 = <0>; adi,pin10-function = "smbalert#"; adi,pin14-function = "tach4"; - #pwm-cells = <4>; + #pwm-cells = <3>; - /* PWMs at 22.5 kHz frequency, 50% duty*/ + /* PWMs at 22.5 kHz frequency */ fan-0 { - pwms = <&pwm 0 44444 0 22222>; + pwms = <&pwm 0 44444 0>; }; fan-1 { - pwms = <&pwm 2 44444 0 22222>; + pwms = <&pwm 2 44444 0>; }; }; }; diff --git a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml index aa801ef1640b..ea8b1553a3e9 100644 --- a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml +++ b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml @@ -28,6 +28,7 @@ properties: - lltc,ltc3886 - lltc,ltc3887 - lltc,ltc3889 + - lltc,ltc7132 - lltc,ltc7841 - lltc,ltc7880 - lltc,ltm2987 @@ -55,6 +56,7 @@ properties: * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 * ltc2978 : vout0 - vout7 * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 + * ltc7132 : vout0 - vout1 * ltc7841 : vout0 * ltc7880 : vout0 - vout1 * ltc3883 : vout0 diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml index 8af0d7458e62..8588d97ba6ec 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml @@ -25,6 +25,7 @@ description: | properties: compatible: enum: + - maxim,max20710 - maxim,max20730 - maxim,max20734 - maxim,max20743 diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml index 4feb76919404..1b871f166e79 100644 --- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml +++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml @@ -20,6 +20,7 @@ properties: - dallas,max6646 - dallas,max6647 - dallas,max6649 + - dallas,max6654 - dallas,max6657 - dallas,max6658 - dallas,max6659 @@ -36,6 +37,9 @@ properties: - nuvoton,nct7717 - nuvoton,nct7718 - nxp,sa56004 + - onnn,nct72 + - onnn,nct214 + - onnn,nct218 - onnn,nct1008 - ti,tmp451 - ti,tmp461 @@ -118,6 +122,7 @@ allOf: - dallas,max6646 - dallas,max6647 - dallas,max6649 + - dallas,max6654 - dallas,max6657 - dallas,max6658 - dallas,max6659 @@ -139,6 +144,9 @@ allOf: - adi,adt7461 - adi,adt7461a - adi,adt7481 + - onnn,nct72 + - onnn,nct214 + - onnn,nct218 - onnn,nct1008 then: patternProperties: diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,adp1050.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,adp1050.yaml index 10c2204bc3df..af7530093942 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,adp1050.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,adp1050.yaml @@ -10,16 +10,27 @@ maintainers: - Radu Sabau description: | - The ADP1050 is used to monitor system voltages, currents and temperatures. + The ADP1050 and similar devices are used to monitor system voltages, + currents, power, and temperatures. + Through the PMBus interface, the ADP1050 targets isolated power supplies and has four individual monitors for input/output voltage, input current and temperature. Datasheet: https://www.analog.com/en/products/adp1050.html + https://www.analog.com/en/products/adp1051.html + https://www.analog.com/en/products/adp1055.html + https://www.analog.com/en/products/ltp8800-1a.html + https://www.analog.com/en/products/ltp8800-2.html + https://www.analog.com/en/products/ltp8800-4a.html properties: compatible: - const: adi,adp1050 + enum: + - adi,adp1050 + - adi,adp1051 + - adi,adp1055 + - adi,ltp8800 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml index bac5f8e352aa..3dc7f15484d2 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml @@ -56,6 +56,7 @@ properties: - renesas,raa228228 - renesas,raa229001 - renesas,raa229004 + - renesas,raa229621 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml index f8bea1c0e94a..8f9ce00079df 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml @@ -23,7 +23,13 @@ description: | properties: compatible: enum: + - ti,ucd9000 + - ti,ucd9090 + - ti,ucd90120 + - ti,ucd90124 + - ti,ucd90160 - ti,ucd90320 + - ti,ucd90910 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml b/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml index 9ca7356760a7..eb00756988be 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml @@ -32,6 +32,12 @@ properties: $ref: fan-common.yaml# unevaluatedProperties: false + properties: + cooling-levels: + description: PWM duty cycle values corresponding to thermal cooling states. + items: + maximum: 255 + "#pwm-cells": const: 2 description: | diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml index d1fb7b9abda0..fa68b99ef2e2 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml @@ -25,6 +25,7 @@ properties: - ti,ina219 - ti,ina220 - ti,ina226 + - ti,ina228 - ti,ina230 - ti,ina231 - ti,ina233 @@ -107,6 +108,7 @@ allOf: - ti,ina219 - ti,ina220 - ti,ina226 + - ti,ina228 - ti,ina230 - ti,ina231 - ti,ina237 diff --git a/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml b/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml index 63d8cf467806..5c0cdc0091b5 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml @@ -18,7 +18,9 @@ description: | properties: compatible: - const: ti,lm87 + enum: + - adi,adm1024 + - ti,lm87 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/i2c/apple,i2c.yaml b/Documentation/devicetree/bindings/i2c/apple,i2c.yaml index 077d2a539c83..fed3e1b8c43f 100644 --- a/Documentation/devicetree/bindings/i2c/apple,i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/apple,i2c.yaml @@ -22,6 +22,11 @@ properties: compatible: items: - enum: + - apple,s5l8960x-i2c + - apple,t7000-i2c + - apple,s8000-i2c + - apple,t8010-i2c + - apple,t8015-i2c - apple,t8103-i2c - apple,t8112-i2c - apple,t6000-i2c diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml b/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml index 8d47b290b4ed..7ae8c7b1d006 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml @@ -36,6 +36,7 @@ properties: - items: - enum: - google,gs101-hsi2c + - samsung,exynos2200-hsi2c - samsung,exynos850-hsi2c - const: samsung,exynosautov9-hsi2c - const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420 diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml index 2f1e97969c3f..4ac5a40a3886 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml @@ -105,6 +105,9 @@ properties: (t(f) in the I2C specification). If not specified we will use the SCL value since they are the same in nearly all cases. + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml index b57ae6963e62..6b6f6762d122 100644 --- a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml @@ -97,7 +97,10 @@ properties: resets: items: - - description: module reset + - description: + Module reset. This property is optional for controllers in Tegra194, + Tegra234 etc where an internal software reset is available as an + alternative. reset-names: items: @@ -116,6 +119,13 @@ properties: - const: rx - const: tx +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + allOf: - $ref: /schemas/i2c/i2c-controller.yaml - if: @@ -169,6 +179,18 @@ allOf: properties: power-domains: false + - if: + not: + properties: + compatible: + contains: + enum: + - nvidia,tegra194-i2c + then: + required: + - resets + - reset-names + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml b/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml index 798a6939b894..e645784b77d3 100644 --- a/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml @@ -22,6 +22,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + clock-frequency: default: 100000 diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml index eddfd329c67b..69ac5db8b914 100644 --- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml @@ -26,7 +26,8 @@ properties: - const: realtek,rtl9301-i2c reg: - description: Register offset and size this I2C controller. + items: + - description: Register offset and size this I2C controller. "#address-cells": const: 1 diff --git a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml index cc39511a49d6..6876eade431b 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml @@ -10,9 +10,6 @@ maintainers: - Chris Brandt - Wolfram Sang -allOf: - - $ref: /schemas/i2c/i2c-controller.yaml# - properties: compatible: oneOf: @@ -32,32 +29,50 @@ properties: - renesas,riic-r9a09g056 # RZ/V2N - const: renesas,riic-r9a09g057 # RZ/V2H(P) - - const: renesas,riic-r9a09g057 # RZ/V2H(P) + - enum: + - renesas,riic-r9a09g057 # RZ/V2H(P) + - renesas,riic-r9a09g077 # RZ/T2H + + - items: + - const: renesas,riic-r9a09g087 # RZ/N2H + - const: renesas,riic-r9a09g077 # RZ/T2H reg: maxItems: 1 interrupts: - items: - - description: Transmit End Interrupt - - description: Receive Data Full Interrupt - - description: Transmit Data Empty Interrupt - - description: Stop Condition Detection Interrupt - - description: Start Condition Detection Interrupt - - description: NACK Reception Interrupt - - description: Arbitration-Lost Interrupt - - description: Timeout Interrupt + oneOf: + - items: + - description: Transmit End Interrupt + - description: Receive Data Full Interrupt + - description: Transmit Data Empty Interrupt + - description: Stop Condition Detection Interrupt + - description: Start Condition Detection Interrupt + - description: NACK Reception Interrupt + - description: Arbitration-Lost Interrupt + - description: Timeout Interrupt + - items: + - description: Transfer Error Or Event Generation + - description: Receive Data Full Interrupt + - description: Transmit Data Empty Interrupt + - description: Transmit End Interrupt interrupt-names: - items: - - const: tei - - const: ri - - const: ti - - const: spi - - const: sti - - const: naki - - const: ali - - const: tmoi + oneOf: + - items: + - const: tei + - const: ri + - const: ti + - const: spi + - const: sti + - const: naki + - const: ali + - const: tmoi + - items: + - const: eei + - const: rxi + - const: txi + - const: tei clock-frequency: description: @@ -84,18 +99,40 @@ required: - '#address-cells' - '#size-cells' -if: - properties: - compatible: - contains: - enum: - - renesas,riic-r9a07g043 - - renesas,riic-r9a07g044 - - renesas,riic-r9a07g054 - - renesas,riic-r9a09g057 -then: - required: - - resets +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + + - if: + properties: + compatible: + contains: + const: renesas,riic-r9a09g077 + then: + properties: + interrupts: + maxItems: 4 + interrupt-names: + maxItems: 4 + resets: false + else: + properties: + interrupts: + minItems: 8 + interrupt-names: + minItems: 8 + + - if: + properties: + compatible: + contains: + enum: + - renesas,riic-r9a07g043 + - renesas,riic-r9a07g044 + - renesas,riic-r9a07g054 + - renesas,riic-r9a09g057 + then: + required: + - resets unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml index cad6d53d0e2e..6fa3078074d0 100644 --- a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml @@ -14,7 +14,12 @@ allOf: properties: compatible: - const: cdns,i3c-master + oneOf: + - const: cdns,i3c-master + - items: + - enum: + - axiado,ax3000-i3c + - const: cdns,i3c-master reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/i3c/renesas,i3c.yaml b/Documentation/devicetree/bindings/i3c/renesas,i3c.yaml new file mode 100644 index 000000000000..fe2e9633c46f --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/renesas,i3c.yaml @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i3c/renesas,i3c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/G3S and RZ/G3E I3C Bus Interface + +maintainers: + - Wolfram Sang + - Tommaso Merciai + +properties: + compatible: + items: + - enum: + - renesas,r9a08g045-i3c # RZ/G3S + - renesas,r9a09g047-i3c # RZ/G3E + + reg: + maxItems: 1 + + interrupts: + items: + - description: Non-recoverable internal error interrupt + - description: Normal transfer error interrupt + - description: Normal transfer abort interrupt + - description: Normal response status buffer full interrupt + - description: Normal command buffer empty interrupt + - description: Normal IBI status buffer full interrupt + - description: Normal Rx data buffer full interrupt + - description: Normal Tx data buffer empty interrupt + - description: Normal receive status buffer full interrupt + - description: START condition detection interrupt + - description: STOP condition detection interrupt + - description: Transmit end interrupt + - description: NACK detection interrupt + - description: Arbitration lost interrupt + - description: Timeout detection interrupt + - description: Wake-up condition detection interrupt + - description: HDR Exit Pattern detection interrupt + minItems: 16 + + interrupt-names: + items: + - const: ierr + - const: terr + - const: abort + - const: resp + - const: cmd + - const: ibi + - const: rx + - const: tx + - const: rcv + - const: st + - const: sp + - const: tend + - const: nack + - const: al + - const: tmo + - const: wu + - const: exit + minItems: 16 + + clocks: + items: + - description: APB bus clock + - description: transfer clock + - description: SFRs clock + minItems: 2 + + clock-names: + items: + - const: pclk + - const: tclk + - const: pclkrw + minItems: 2 + + power-domains: + maxItems: 1 + + resets: + items: + - description: Reset signal + - description: APB interface reset signal/SCAN reset signal + + reset-names: + items: + - const: presetn + - const: tresetn + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clock-names + - clocks + - power-domains + - resets + - reset-names + +allOf: + - $ref: i3c.yaml# + + - if: + properties: + compatible: + contains: + const: renesas,r9a08g045-i3c + then: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + interrupts: + minItems: 17 + interrupt-names: + minItems: 17 + + - if: + properties: + compatible: + contains: + const: renesas,r9a09g047-i3c + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + interrupts: + maxItems: 16 + interrupt-names: + maxItems: 16 + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i3c@1005b000 { + compatible = "renesas,r9a08g045-i3c"; + reg = <0x1005b000 0x1000>; + clocks = <&cpg CPG_MOD R9A08G045_I3C_PCLK>, + <&cpg CPG_MOD R9A08G045_I3C_TCLK>; + clock-names = "pclk", "tclk"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "ierr", "terr", "abort", "resp", + "cmd", "ibi", "rx", "tx", "rcv", + "st", "sp", "tend", "nack", + "al", "tmo", "wu", "exit"; + resets = <&cpg R9A08G045_I3C_PRESETN>, + <&cpg R9A08G045_I3C_TRESETN>; + reset-names = "presetn", "tresetn"; + power-domains = <&cpg>; + #address-cells = <3>; + #size-cells = <0>; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml new file mode 100644 index 000000000000..ed849ba1b77b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4080.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4080 20-Bit, 40 MSPS, Differential SAR ADC + +maintainers: + - Antoniu Miclaus + +description: | + The AD4080 is a high speed, low noise, low distortion, 20-bit, Easy Drive, + successive approximation register (SAR) analog-to-digital converter (ADC). + Maintaining high performance (signal-to-noise and distortion (SINAD) ratio + > 90 dBFS) at signal frequencies in excess of 1 MHz enables the AD4080 to + service a wide variety of precision, wide bandwidth data acquisition + applications. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4080.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,ad4080 + + reg: + maxItems: 1 + + spi-max-frequency: + description: Configuration of the SPI bus. + maximum: 50000000 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: cnv + + vdd33-supply: true + + vdd11-supply: true + + vddldo-supply: true + + iovdd-supply: true + + vrefin-supply: true + + io-backends: + maxItems: 1 + + adi,lvds-cnv-enable: + description: Enable the LVDS signal type on the CNV pin. Default is CMOS. + type: boolean + + adi,num-lanes: + description: + Number of lanes on which the data is sent on the output (DA, DB pins). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + default: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - vdd33-supply + - vrefin-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4080"; + reg = <0>; + spi-max-frequency = <10000000>; + vdd33-supply = <&vdd33>; + vddldo-supply = <&vddldo>; + vrefin-supply = <&vrefin>; + clocks = <&cnv>; + clock-names = "cnv"; + io-backends = <&iio_backend>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml new file mode 100644 index 000000000000..da93213d12d4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml @@ -0,0 +1,554 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4170-4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4170-4 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt + +description: | + Analog Devices AD4170-4 series of Sigma-delta Analog to Digital Converters. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4190-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4195-4.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, + resistive/capacitive combinations of those inputs can cause DC gain + errors if the reference inputs go unbuffered into the ADC. Enable + reference buffering if the provided reference source has dynamic high + impedance output. Note the absolute voltage allowed on REFINn+ and REFINn- + inputs is from AVSS - 50 mV to AVDD + 50 mV when the reference buffers are + disabled but narrows to AVSS to AVDD when reference buffering is enabled + or in precharge mode. + $ref: /schemas/types.yaml#/definitions/string + enum: [ precharge, full, disabled ] + default: full + +properties: + compatible: + enum: + - adi,ad4170-4 + - adi,ad4190-4 + - adi,ad4195-4 + + avss-supply: + description: + Reference voltage supply for AVSS. A −2.625V minimum and 0V maximum supply + that powers the chip. If not provided, AVSS is assumed to be at system + ground (0V). + + avdd-supply: + description: + A supply of 4.75V to 5.25V relative to AVSS that powers the chip (AVDD). + + iovdd-supply: + description: 1.7V to 5.25V reference supply to the serial interface (IOVDD). + + refin1p-supply: + description: REFIN+ supply that can be used as reference for conversion. + + refin1n-supply: + description: REFIN- supply that can be used as reference for conversion. + + refin2p-supply: + description: REFIN2+ supply that can be used as reference for conversion. + + refin2n-supply: + description: REFIN2- supply that can be used as reference for conversion. + + spi-cpol: true + + spi-cpha: true + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The data + ready signal (RDY) used as interrupt is by default provided on the SDO + pin. Alternatively, it can be provided on the DIG_AUX1 pin in which case + the chip disables the RDY function on SDO. Thus, there can be only one + data ready interrupt enabled at a time. + + interrupt-names: + description: + Specify which pin should be configured as Data Ready interrupt. + enum: + - sdo + - dig_aux1 + + clocks: + maxItems: 1 + description: + Optional external clock source. Can specify either an external clock or + external crystal. + + clock-names: + enum: + - ext-clk + - xtal + default: ext-clk + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + + ldac-gpios: + description: + GPIO connected to DIG_AUX2 pin to be used as LDAC toggle to control the + transfer of data from the DAC_INPUT_A register to the DAC. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + adi,vbias-pins: + description: Analog inputs to apply a voltage bias of (AVDD − AVSS) / 2 to. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + minimum: 0 + maximum: 8 + +allOf: + # Some devices don't have integrated DAC + - if: + properties: + compatible: + contains: + enum: + - adi,ad4190-4 + - adi,ad4195-4 + then: + properties: + ldac-gpios: false + + # Require to specify the interrupt pin when using interrupts + - if: + required: + - interrupts + then: + required: + - interrupt-names + + # If an external clock is set, the internal clock cannot go out and vice versa + - oneOf: + - required: [clocks] + properties: + '#clock-cells': false + - required: ['#clock-cells'] + properties: + clocks: false + +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + - spi-cpol + - spi-cpha + +unevaluatedProperties: false + +patternProperties: + "^channel@[0-9a-f]$": + $ref: /schemas/iio/adc/adc.yaml# + unevaluatedProperties: false + description: + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: + The channel number. + minimum: 0 + maximum: 15 + + diff-channels: + description: | + This property is used for defining the inputs of a differential + voltage channel. The first value is the positive input and the second + value is the negative input of the channel. + + Besides the analog input pins AIN0 to AIN8, there are special inputs + that can be selected with the following values: + 17: Internal temperature sensor + 18: (AVDD-AVSS)/5 + 19: (IOVDD-DGND)/5 + 20: DAC output + 21: ALDO + 22: DLDO + 23: AVSS + 24: DGND + 25: REFIN+ + 26: REFIN- + 27: REFIN2+ + 28: REFIN2- + 29: REFOUT + For the internal temperature sensor, use the input number for both + inputs (i.e. diff-channels = <17 17>). + items: + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29] + + adi,reference-select: + description: | + Select the reference source to use when converting on the + specific channel. Valid values are: + 0: REFIN+/REFIN- + 1: REFIN2+/REFIN2− + 2: REFOUT/AVSS (internal reference) + 3: AVDD/AVSS + If not specified, REFOUT/AVSS is used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + default: 1 + + adi,positive-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,negative-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,sensor-type: + description: + The AD4170-4 and similar designs have features to aid interfacing with + load cell weigh scale, RTD, and thermocouple sensors. Each of those + sensor types requires either distinct wiring configuration or + external circuitry for proper sensor operation and can use different + ADC chip functionality on their setups. A key characteristic of those + external sensors is that they must be excited either by voltage supply + or by ADC chip excitation signals. The sensor can then be read through + a pair of analog inputs. This property specifies which particular + sensor type is connected to the ADC so it can be properly setup and + handled. Omit this property for conventional (not weigh scale, RTD, or + thermocouple) ADC channel setups. + $ref: /schemas/types.yaml#/definitions/string + enum: [ weighscale, rtd, thermocouple ] + + adi,excitation-pin-0: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-1: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-2: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-3: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-1-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-1 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-2-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-2 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-3-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-3 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-ac: + type: boolean + description: + Whether the external sensor has to be AC or DC excited. When omitted, + it is DC excited. + + allOf: + - oneOf: + - required: [single-channel, common-mode-channel] + properties: + diff-channels: false + - required: [diff-channels] + properties: + single-channel: false + common-mode-channel: false + # Usual ADC channels don't need external circuitry excitation. + - if: + not: + required: + - adi,sensor-type + then: + properties: + adi,excitation-pin-0: false + adi,excitation-pin-1: false + adi,excitation-pin-2: false + adi,excitation-pin-3: false + adi,excitation-current-0-microamp: false + adi,excitation-current-1-microamp: false + adi,excitation-current-2-microamp: false + adi,excitation-current-3-microamp: false + adi,excitation-ac: false + # Weigh scale bridge AC excited with one pair of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + # Weigh scale bridge AC excited with two pairs of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-0 + - adi,excitation-pin-1 + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-0-microamp + - adi,excitation-current-1-microamp + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-0: + const: 17 + adi,excitation-pin-1: + const: 18 + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170-4"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + clocks = <&clk>; + clock-names = "xtal"; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "dig_aux1"; + adi,vbias-pins = <8>; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@0 { + reg = <0>; + single-channel = <0>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Weigh scale sensor + channel@1 { + reg = <1>; + bipolar; + diff-channels = <1 2>; + adi,reference-select = <0>; + adi,positive-reference-buffer = "precharge"; + adi,negative-reference-buffer = "precharge"; + adi,sensor-type = "weighscale"; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + // RTD sensor + channel@2 { + reg = <2>; + bipolar; + diff-channels = <3 4>; + adi,reference-select = <0>; + adi,sensor-type = "rtd"; + adi,excitation-pin-0 = <5>; + adi,excitation-pin-1 = <6>; + adi,excitation-current-0-microamp = <500>; + adi,excitation-current-1-microamp = <500>; + adi,excitation-ac; + }; + // Thermocouple sensor + channel@3 { + reg = <3>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "thermocouple"; + adi,excitation-pin-0 = <18>; + adi,excitation-current-0-microamp = <500>; + }; + }; + }; + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170-4"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + #clock-cells = <0>; + clock-output-names = "ad4170-clk16mhz"; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "dig_aux1"; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input range + // Differential bipolar. If AVSS < 0V, differential true bipolar + channel@0 { + reg = <0>; + bipolar; + diff-channels = <0 1>; + adi,reference-select = <3>; + }; + // Sample AIN2 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@1 { + reg = <1>; + single-channel = <2>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@2 { + reg = <2>; + bipolar; + single-channel = <3>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN4 with respect to DGND throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@3 { + reg = <3>; + bipolar; + single-channel = <4>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential unipolar (AD4170-4 datasheet page 46 example) + channel@4 { + reg = <4>; + single-channel = <5>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- input range + // Pseudo-differential bipolar + channel@5 { + reg = <5>; + bipolar; + single-channel = <6>; + common-mode-channel = <29>; + adi,reference-select = <0>; + }; + // Weigh scale sensor + channel@6 { + reg = <6>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "weighscale"; + adi,excitation-pin-0 = <17>; + adi,excitation-pin-1 = <18>; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml index c6676d91b4e6..b107322e0ea3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml @@ -69,6 +69,8 @@ properties: spi-max-frequency: maximum: 25000000 + spi-3wire: true + '#address-cells': const: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml new file mode 100644 index 000000000000..57f097025705 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7405.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD7405 family + +maintainers: + - Dragos Bogdan + - Pop Ioan Daniel + +description: | + Analog Devices AD7405 is a high performance isolated ADC, 1-channel, + 16-bit with a second-order Σ-Δ modulator that converts an analog input signal + into a high speed, single-bit data stream. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7405.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adum7701.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adum7702.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ADuM7703.pdf + +properties: + compatible: + enum: + - adi,ad7405 + - adi,adum7701 + - adi,adum7702 + - adi,adum7703 + + clocks: + maxItems: 1 + + vdd1-supply: true + + vdd2-supply: true + + io-backends: + maxItems: 1 + +required: + - compatible + - clocks + - vdd1-supply + - vdd2-supply + - io-backends + +additionalProperties: false + +examples: + - | + adc { + compatible = "adi,ad7405"; + clocks = <&axi_clk_gen 0>; + vdd1-supply = <&vdd1>; + vdd2-supply = <&vdd2>; + io-backends = <&axi_adc>; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 29f12d650442..1180d2ffbf84 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -204,6 +204,15 @@ patternProperties: considered a bipolar differential channel. Otherwise it is bipolar single-ended. + adi,rfilter-ohms: + description: + For ADCs that supports gain calibration, this property must be set to + the value of the external RFilter resistor. Proper gain error + correction is applied based on this value. + default: 0 + minimum: 0 + maximum: 64512 + required: - reg - bipolar @@ -223,12 +232,6 @@ allOf: - required: - pwms - - oneOf: - - required: - - interrupts - - required: - - io-backends - - if: properties: compatible: @@ -256,6 +259,25 @@ allOf: properties: adi,oversampling-ratio-gpios: false + - if: + properties: + compatible: + contains: + enum: + - adi,ad7605-4 + - adi,ad7606-4 + - adi,ad7606-6 + - adi,ad7606-8 + - adi,ad7607 + - adi,ad7608 + - adi,ad7609 + - adi,ad7616 + then: + patternProperties: + "^channel@[0-9a-f]+$": + properties: + adi,rfilter-ohms: false + - if: properties: compatible: @@ -398,6 +420,7 @@ examples: reg = <8>; diff-channels = <8 8>; bipolar; + adi,rfilter-ohms = <2048>; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index 3ce59d4d065f..c06d0fc791d3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -26,7 +26,26 @@ properties: clock-names: const: mclk + trigger-sources: + $ref: /schemas/types.yaml#/definitions/phandle-array + minItems: 1 + maxItems: 2 + description: | + A list of phandles referencing trigger source providers. Each entry + represents a trigger source for the ADC: + + - First entry specifies the device responsible for driving the + synchronization (SYNC_IN) pin, as an alternative to adi,sync-in-gpios. + This can be a `gpio-trigger` or another `ad7768-1` device. If the + device's own SYNC_OUT pin is internally connected to its SYNC_IN pin, + reference the device itself or omit this property. + - Second entry optionally defines a GPIO3 pin used as a START signal trigger. + + Use the accompanying trigger source cell to identify the type of each entry. + interrupts: + description: + DRDY (Data Ready) pin, which signals conversion results are available. maxItems: 1 '#address-cells': @@ -47,6 +66,19 @@ properties: in any way, for example if the filter decimation rate changes. As the line is active low, it should be marked GPIO_ACTIVE_LOW. + regulators: + type: object + description: + list of regulators provided by this controller. + + properties: + vcm-output: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + additionalProperties: false + reset-gpios: maxItems: 1 @@ -57,6 +89,23 @@ properties: "#io-channel-cells": const: 1 + "#trigger-source-cells": + description: | + Cell indicates the trigger output signal: 0 = SYNC_OUT, 1 = GPIO3, + 2 = DRDY. + + For better readability, macros for these values are available in + dt-bindings/iio/adc/adi,ad7768-1.h. + const: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + required: - compatible - reg @@ -65,7 +114,16 @@ required: - vref-supply - spi-cpol - spi-cpha - - adi,sync-in-gpios + +dependencies: + adi,sync-in-gpios: + not: + required: + - trigger-sources + trigger-sources: + not: + required: + - adi,sync-in-gpios patternProperties: "^channel@([0-9]|1[0-5])$": @@ -105,6 +163,8 @@ examples: spi-max-frequency = <2000000>; spi-cpol; spi-cpha; + gpio-controller; + #gpio-cells = <2>; vref-supply = <&adc_vref>; interrupts = <25 IRQ_TYPE_EDGE_RISING>; interrupt-parent = <&gpio>; @@ -120,6 +180,12 @@ examples: reg = <0>; label = "channel_0"; }; + + regulators { + vcm_reg: vcm-output { + regulator-name = "ad7768-1-vcm"; + }; + }; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml index cf74f84d6103..e91e421a3d6b 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml @@ -27,6 +27,7 @@ description: | the ad7606 family. https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + https://analogdevicesinc.github.io/hdl/library/axi_ad408x/index.html https://analogdevicesinc.github.io/hdl/library/axi_ad485x/index.html http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html @@ -34,6 +35,7 @@ properties: compatible: enum: - adi,axi-adc-10.0.a + - adi,axi-ad408x - adi,axi-ad7606x - adi,axi-ad485x diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml index b489c984c1bb..14363389f30a 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml @@ -32,6 +32,10 @@ properties: - enum: - mediatek,mt7623-auxadc - const: mediatek,mt2701-auxadc + - items: + - enum: + - mediatek,mt7981-auxadc + - const: mediatek,mt7986-auxadc - items: - enum: - mediatek,mt6893-auxadc diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml index 6497c416094d..5d4ab701f51a 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml @@ -22,6 +22,8 @@ properties: - mediatek,mt6357-auxadc - mediatek,mt6358-auxadc - mediatek,mt6359-auxadc + - mediatek,mt6363-auxadc + - mediatek,mt6373-auxadc "#io-channel-cells": const: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml index 2c5032be83bd..fd815ab30df1 100644 --- a/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml @@ -22,6 +22,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + vref-supply: true "#io-channel-cells": diff --git a/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml new file mode 100644 index 000000000000..dd9ec3038703 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/st,spear600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST SPEAr ADC device driver + +maintainers: + - Jonathan Cameron + +description: | + Integrated ADC inside the ST SPEAr SoC, SPEAr600, supporting + 10-bit resolution. Datasheet can be found here: + https://www.st.com/resource/en/datasheet/spear600.pdf + +properties: + compatible: + enum: + - st,spear600-adc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + sampling-frequency: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2500000 + maximum: 20000000 + description: + Default sampling frequency of the ADC in Hz. + + vref-external: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1000 + maximum: 2800 + description: + External voltage reference in milli-volts. If omitted the internal voltage + reference will be used. + + average-samples: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + default: 0 + description: + Number of samples to generate an average value. If omitted, single data + conversion will be used. + +required: + - compatible + - reg + - interrupts + - sampling-frequency + +additionalProperties: false + +examples: + - | + adc@d8200000 { + compatible = "st,spear600-adc"; + reg = <0xd8200000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <6>; + sampling-frequency = <5000000>; + vref-external = <2500>; /* 2.5V VRef */ + }; diff --git a/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml b/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml new file mode 100644 index 000000000000..4d8abf8ac2c8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/gyroscope/invensense,itg3200.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Invensense ITG-3200 Gyroscope + +maintainers: + - Jonathan Cameron + +description: | + Triple-axis, digital output gyroscope with a three 16-bit analog-to-digital + converters (ADCs) for digitizing the gyro outputs, a user-selectable internal + low-pass filter bandwidth, and a Fast-Mode I2C. + +properties: + compatible: + const: invensense,itg3200 + + reg: + maxItems: 1 + + vdd-supply: true + + vlogic-supply: true + + interrupts: + maxItems: 1 + + mount-matrix: + description: an optional 3x3 mounting rotation matrix. + + clocks: + maxItems: 1 + + clock-names: + items: + - const: ext_clock + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + gyroscope@68 { + compatible = "invensense,itg3200"; + reg = <0x68>; + interrupt-parent = <&gpio2>; + interrupts = <24 IRQ_TYPE_EDGE_FALLING>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.yaml b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.yaml index d1a6103fc37a..f3242dc0e7e6 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.yaml @@ -21,7 +21,7 @@ properties: vlogic-supply: true interrupts: - minItems: 1 + maxItems: 1 description: Interrupt mapping for the trigger interrupt from the internal oscillator. diff --git a/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.yaml b/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.yaml new file mode 100644 index 000000000000..65d9b44fcd5e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/nicera,d3323aa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nicera D3-323-AA PIR sensor + +maintainers: + - Waqar Hameed + +description: | + PIR sensor for human detection. + Datasheet: https://www.endrich.com/Datenbl%C3%A4tter/Sensoren/D3-323-AA_e.pdf + +properties: + compatible: + const: nicera,d3323aa + + vdd-supply: + description: + Supply voltage (1.8 to 5.5 V). + + vout-clk-gpios: + maxItems: 1 + description: + GPIO for clock and detection. + After reset, the device signals with two falling edges on this pin that it + is ready for configuration (within 1.2 s). + During configuration, it is used as clock for data reading and writing (on + data-gpios). + After all this, when device is in operational mode, it signals on this pin + for any detections. + + data-gpios: + maxItems: 1 + description: + GPIO for data reading and writing. This is denoted "DO (SI)" in datasheet. + During configuration, this pin is used for writing and reading + configuration data (together with vout-clk-gpios as clock). + After this, during operational mode, the device will output serial data on + this GPIO. + +required: + - compatible + - vdd-supply + - vout-clk-gpios + - data-gpios + +additionalProperties: false + +examples: + - | + #include + + proximity { + compatible = "nicera,d3323aa"; + vdd-supply = <®ulator_3v3>; + vout-clk-gpios = <&gpio 78 GPIO_ACTIVE_HIGH>; + data-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>; + }; +... diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index cb3e1801b0d3..0840e4ab28b7 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/input/elan,ekth6915.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Elan eKTH6915 touchscreen controller +title: Elan I2C-HID touchscreen controllers maintainers: - Douglas Anderson description: - Supports the Elan eKTH6915 touchscreen controller. - This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + Supports the Elan eKTH6915 and other I2C-HID touchscreen controllers. + These touchscreen controller use the i2c-hid protocol with a reset GPIO. allOf: - $ref: /schemas/input/touchscreen/touchscreen.yaml# @@ -23,12 +23,14 @@ properties: - enum: - elan,ekth5015m - const: elan,ekth6915 + - items: + - const: elan,ekth8d18 + - const: elan,ekth6a12nay - enum: - elan,ekth6915 - elan,ekth6a12nay - reg: - const: 0x10 + reg: true interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/input/syna,rmi4.yaml b/Documentation/devicetree/bindings/input/syna,rmi4.yaml index b522c8d3ce0d..f369385ffaf0 100644 --- a/Documentation/devicetree/bindings/input/syna,rmi4.yaml +++ b/Documentation/devicetree/bindings/input/syna,rmi4.yaml @@ -89,6 +89,24 @@ properties: required: - reg + rmi4-f1a@1a: + type: object + additionalProperties: false + $ref: input.yaml# + description: + RMI4 Function 1A is for capacitive keys. + + properties: + reg: + maxItems: 1 + + linux,keycodes: + minItems: 1 + maxItems: 4 + + required: + - reg + patternProperties: "^rmi4-f1[12]@1[12]$": type: object @@ -201,6 +219,7 @@ allOf: examples: - | + #include #include i2c { @@ -234,6 +253,7 @@ examples: rmi4-f1a@1a { reg = <0x1a>; + linux,keycodes = ; }; }; }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index ab821490284a..7d3edb58f72d 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -43,6 +43,7 @@ properties: - focaltech,ft5452 - focaltech,ft6236 - focaltech,ft8201 + - focaltech,ft8716 - focaltech,ft8719 reg: diff --git a/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt b/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt deleted file mode 100644 index 41cbf4b7a670..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt +++ /dev/null @@ -1,16 +0,0 @@ -* NXP LPC32xx SoC Touchscreen Controller (TSC) - -Required properties: -- compatible: must be "nxp,lpc3220-tsc" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: The TSC/ADC interrupt - -Example: - - tsc@40048000 { - compatible = "nxp,lpc3220-tsc"; - reg = <0x40048000 0x1000>; - interrupt-parent = <&mic>; - interrupts = <39 0>; - }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml b/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml new file mode 100644 index 000000000000..b6feda127c7b --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/nxp,lpc3220-tsc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32xx SoC Touchscreen Controller (TSC) + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc3220-tsc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +additionalProperties: false + +examples: + - | + #include + + touchscreen@40048000 { + compatible = "nxp,lpc3220-tsc"; + reg = <0x40048000 0x1000>; + interrupt-parent = <&mic>; + interrupts = <39 0>; + clocks = <&clk LPC32XX_CLK_ADC>; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml index 1d8ca19fd37a..e7ee7a0d74c4 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml @@ -37,6 +37,7 @@ unevaluatedProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -46,5 +47,33 @@ examples: reg = <0x55>; interrupts = <2 0>; gpios = <&gpio1 166 0>; + + touch-overlay { + segment-0 { + label = "Touchscreen"; + x-origin = <0>; + x-size = <240>; + y-origin = <40>; + y-size = <280>; + }; + + segment-1a { + label = "Camera light"; + linux,code = ; + x-origin = <40>; + x-size = <40>; + y-origin = <0>; + y-size = <40>; + }; + + segment-2a { + label = "Power"; + linux,code = ; + x-origin = <160>; + x-size = <40>; + y-origin = <0>; + y-size = <40>; + }; + }; }; }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml b/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml new file mode 100644 index 000000000000..8bb4bc7df4fa --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/ti.tsc2007.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments tsc2007 touchscreen controller + +maintainers: + - Frank Li + +properties: + compatible: + const: ti,tsc2007 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + ti,x-plate-ohms: + description: X-plate resistance in ohms. + + gpios: true + + pendown-gpio: true + + ti,max-rt: + $ref: /schemas/types.yaml#/definitions/uint32 + description: maximum pressure. + + ti,fuzzx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + specifies the absolute input fuzz x value. + If set, it will permit noise in the data up to +- the value given to the fuzz + parameter, that is used to filter noise from the event stream. + + ti,fuzzy: + $ref: /schemas/types.yaml#/definitions/uint32 + description: specifies the absolute input fuzz y value. + + ti,fuzzz: + $ref: /schemas/types.yaml#/definitions/uint32 + description: specifies the absolute input fuzz z value. + + ti,poll-period: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + how much time to wait (in milliseconds) before reading again the + values from the tsc2007. + +required: + - compatible + - reg + - ti,x-plate-ohms + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touch@49 { + compatible = "ti,tsc2007"; + reg = <0x49>; + interrupt-parent = <&gpio4>; + interrupts = <0x0 0x8>; + gpios = <&gpio4 0 0>; + ti,x-plate-ohms = <180>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml index 431c13335c40..3e3572aa483a 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml @@ -87,6 +87,125 @@ properties: touchscreen-y-plate-ohms: description: Resistance of the Y-plate in Ohms + touch-overlay: + description: | + List of nodes defining segments (touch areas) on the touchscreen. + + This object can be used to describe a series of segments to restrict + the region within touch events are reported or buttons with a specific + functionality. + + This is of special interest if the touchscreen is shipped with a physical + overlay on top of it with a frame that hides some part of the original + touchscreen area. Printed buttons on that overlay are also a typical + use case. + + A new touchscreen area is defined as a sub-node without a key code. If a + key code is defined in the sub-node, it will be interpreted as a button. + + The x-origin and y-origin properties of a touchscreen area define the + offset of a new origin from where the touchscreen events are referenced. + This offset is applied to the events accordingly. The x-size and y-size + properties define the size of the touchscreen effective area. + + The following example shows a new touchscreen area with the new origin + (0',0') for the touch events generated by the device. + + Touchscreen (full area) + ┌────────────────────────────────────────┐ + │ ┌───────────────────────────────┐ │ + │ │ │ │ + │ ├ y-size │ │ + │ │ │ │ + │ │ touchscreen area │ │ + │ │ (no key code) │ │ + │ │ │ │ + │ │ x-size │ │ + │ ┌└──────────────┴────────────────┘ │ + │(0',0') │ + ┌└────────────────────────────────────────┘ + (0,0) + + where (0',0') = (0+x-origin,0+y-origin) + + Sub-nodes with key codes report the touch events on their surface as key + events instead. + + The following example shows a touchscreen with a single button on it. + + Touchscreen (full area) + ┌───────────────────────────────────┐ + │ │ + │ │ + │ ┌─────────┐ │ + │ │button 0 │ │ + │ │KEY_POWER│ │ + │ └─────────┘ │ + │ │ + │ │ + ┌└───────────────────────────────────┘ + (0,0) + + Segments defining buttons and clipped toushcreen areas can be combined + as shown in the following example. + In that case only the events within the touchscreen area are reported + as touch events. Events within the button areas report their associated + key code. Any events outside the defined areas are ignored. + + Touchscreen (full area) + ┌─────────┬──────────────────────────────┐ + │ │ │ + │ │ ┌───────────────────────┐ │ + │ button 0│ │ │ │ + │KEY_POWER│ │ │ │ + │ │ │ │ │ + ├─────────┤ │ touchscreen area │ │ + │ │ │ (no key code) │ │ + │ │ │ │ │ + │ button 1│ │ │ │ + │ KEY_INFO│ ┌└───────────────────────┘ │ + │ │(0',0') │ + ┌└─────────┴──────────────────────────────┘ + (0,0) + + type: object + + patternProperties: + '^segment-': + type: object + description: + Each segment is represented as a sub-node. + properties: + x-origin: + description: horizontal origin of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + y-origin: + description: vertical origin of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + x-size: + description: horizontal resolution of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + y-size: + description: vertical resolution of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + label: + description: descriptive name of the segment + $ref: /schemas/types.yaml#/definitions/string + + linux,code: true + + required: + - x-origin + - y-origin + - x-size + - y-size + + unevaluatedProperties: false + dependencies: touchscreen-size-x: [ touchscreen-size-y ] touchscreen-size-y: [ touchscreen-size-x ] diff --git a/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt b/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt deleted file mode 100644 index 210486a3fb11..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt +++ /dev/null @@ -1,39 +0,0 @@ -* Texas Instruments tsc2007 touchscreen controller - -Required properties: -- compatible: must be "ti,tsc2007". -- reg: I2C address of the chip. -- ti,x-plate-ohms: X-plate resistance in ohms. - -Optional properties: -- gpios: the interrupt gpio the chip is connected to (through the penirq pin). - The penirq pin goes to low when the panel is touched. - (see GPIO binding[1] for more details). -- interrupts: (gpio) interrupt to which the chip is connected - (see interrupt binding[0]). -- ti,max-rt: maximum pressure. -- ti,fuzzx: specifies the absolute input fuzz x value. - If set, it will permit noise in the data up to +- the value given to the fuzz - parameter, that is used to filter noise from the event stream. -- ti,fuzzy: specifies the absolute input fuzz y value. -- ti,fuzzz: specifies the absolute input fuzz z value. -- ti,poll-period: how much time to wait (in milliseconds) before reading again the - values from the tsc2007. - -[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt -[1]: Documentation/devicetree/bindings/gpio/gpio.txt - -Example: - &i2c1 { - /* ... */ - tsc2007@49 { - compatible = "ti,tsc2007"; - reg = <0x49>; - interrupt-parent = <&gpio4>; - interrupts = <0x0 0x8>; - gpios = <&gpio4 0 0>; - ti,x-plate-ohms = <180>; - }; - - /* ... */ - }; diff --git a/Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml b/Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml index 58611ba2a0f4..4d72525f407e 100644 --- a/Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml +++ b/Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml @@ -17,9 +17,14 @@ description: | properties: compatible: - enum: - - mediatek,mt8183-cci - - mediatek,mt8186-cci + oneOf: + - enum: + - mediatek,mt8183-cci + - mediatek,mt8186-cci + - items: + - enum: + - mediatek,mt7988-cci + - const: mediatek,mt8183-cci clocks: items: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml new file mode 100644 index 000000000000..00b7a4108d45 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,milos-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Milos SoC + +maintainers: + - Luca Weiss + +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,milos-rpmh.h + +properties: + compatible: + enum: + - qcom,milos-aggre1-noc + - qcom,milos-aggre2-noc + - qcom,milos-clk-virt + - qcom,milos-cnoc-cfg + - qcom,milos-cnoc-main + - qcom,milos-gem-noc + - qcom,milos-lpass-ag-noc + - qcom,milos-mc-virt + - qcom,milos-mmss-noc + - qcom,milos-nsp-noc + - qcom,milos-pcie-anoc + - qcom,milos-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-clk-virt + - qcom,milos-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-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,milos-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre USB3 PRIM AXI clock + - description: aggre UFS PHY AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-aggre1-noc + - qcom,milos-aggre2-noc + - qcom,milos-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + #include + + interconnect-0 { + compatible = "qcom,milos-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + interconnect@16e0000 { + compatible = "qcom,milos-aggre1-noc"; + reg = <0x016e0000 0x16400>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 83bcf0575cd3..256de140c03d 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -76,6 +76,8 @@ properties: minItems: 1 maxItems: 2 + nonposted-mmio: true + required: - compatible - interconnects diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index cd4bb912e0dc..ab5a921c3495 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -36,6 +36,11 @@ properties: - qcom,sm8350-epss-l3 - qcom,sm8650-epss-l3 - const: qcom,epss-l3 + - items: + - enum: + - qcom,qcs8300-epss-l3 + - const: qcom,sa8775p-epss-l3 + - const: qcom,epss-l3 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml index 2e0c0bc7a376..db19fd5c5708 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml @@ -13,7 +13,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sa8775p.h + See also: include/dt-bindings/interconnect/qcom,sa8775p.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml index 4647dac740e9..f5d3d0c5df73 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml @@ -18,7 +18,7 @@ description: | 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,sar2130p-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml index 78210791496f..81c3dff53992 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sc7280.h + See also: include/dt-bindings/interconnect/qcom,sc7280.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml index 100c68636909..2a5a7594bafd 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sc8280xp.h + See also: include/dt-bindings/interconnect/qcom,sc8280xp.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml index b565d1a382f6..978930324bbf 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml @@ -13,7 +13,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h allOf: - $ref: qcom,rpmh-common.yaml# diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml index 300640a533dd..6a46dc7d473e 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sm8450.h + See also: include/dt-bindings/interconnect/qcom,sm8450.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml index 716bd21f6041..5325ebe23c77 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml @@ -18,7 +18,7 @@ description: | 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,sm8550-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8550-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml index f9322de7cd61..199fe7b232af 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml @@ -18,7 +18,7 @@ description: | 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,sm8650-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8650-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml index a816acc301e1..366f40d980c2 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml @@ -18,7 +18,7 @@ description: | 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,sm8750-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8750-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml index 08b0210e0e59..0840b0ec6e27 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml @@ -18,7 +18,7 @@ description: | 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,x1e80100-rpmh.h + See also: include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interrupt-controller/andestech,plicsw.yaml b/Documentation/devicetree/bindings/interrupt-controller/andestech,plicsw.yaml new file mode 100644 index 000000000000..eb2eb611ac09 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/andestech,plicsw.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/andestech,plicsw.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes machine-level software interrupt controller + +description: + In the Andes platform such as QiLai SoC, the PLIC module is instantiated a + second time with all interrupt sources tied to zero as the software interrupt + controller (PLIC_SW). PLIC_SW directly connects to the machine-mode + inter-processor interrupt lines of CPUs, so RISC-V per-CPU local interrupt + controller is the parent interrupt controller for PLIC_SW. PLIC_SW can + generate machine-mode inter-processor interrupts through programming its + registers. + +maintainers: + - Ben Zong-You Xie + +properties: + compatible: + items: + - enum: + - andestech,qilai-plicsw + - const: andestech,plicsw + + reg: + maxItems: 1 + + interrupts-extended: + minItems: 1 + maxItems: 15872 + description: + Specifies which harts are connected to the PLIC_SW. Each item must points + to a riscv,cpu-intc node, which has a riscv cpu node as parent. + +additionalProperties: false + +required: + - compatible + - reg + - interrupts-extended + +examples: + - | + interrupt-controller@400000 { + compatible = "andestech,qilai-plicsw", "andestech,plicsw"; + reg = <0x400000 0x400000>; + interrupts-extended = <&cpu0intc 3>, + <&cpu1intc 3>, + <&cpu2intc 3>, + <&cpu3intc 3>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/apm,xgene1-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/apm,xgene1-msi.yaml new file mode 100644 index 000000000000..49db952697f3 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/apm,xgene1-msi.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/apm,xgene1-msi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AppliedMicro X-Gene v1 PCIe MSI controller + +maintainers: + - Toan Le + +properties: + compatible: + const: apm,xgene1-msi + + msi-controller: true + + reg: + maxItems: 1 + + interrupts: + maxItems: 16 + +required: + - compatible + - msi-controller + - reg + - interrupts + +additionalProperties: false + +examples: + - | + msi@79000000 { + compatible = "apm,xgene1-msi"; + msi-controller; + reg = <0x79000000 0x900000>; + interrupts = <0x0 0x10 0x4>, + <0x0 0x11 0x4>, + <0x0 0x12 0x4>, + <0x0 0x13 0x4>, + <0x0 0x14 0x4>, + <0x0 0x15 0x4>, + <0x0 0x16 0x4>, + <0x0 0x17 0x4>, + <0x0 0x18 0x4>, + <0x0 0x19 0x4>, + <0x0 0x1a 0x4>, + <0x0 0x1b 0x4>, + <0x0 0x1c 0x4>, + <0x0 0x1d 0x4>, + <0x0 0x1e 0x4>, + <0x0 0x1f 0x4>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml new file mode 100644 index 000000000000..99a266a62385 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5-iwb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Generic Interrupt Controller, version 5 Interrupt Wire Bridge (IWB) + +maintainers: + - Lorenzo Pieralisi + - Marc Zyngier + +description: | + The GICv5 architecture defines the guidelines to implement GICv5 + compliant interrupt controllers for AArch64 systems. + + The GICv5 specification can be found at + https://developer.arm.com/documentation/aes0070 + + GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible + for translating wire signals into interrupt messages to the GICv5 ITS. + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: arm,gic-v5-iwb + + reg: + items: + - description: IWB control frame + + "#address-cells": + const: 0 + + "#interrupt-cells": + description: | + The 1st cell corresponds to the IWB wire. + + The 2nd cell is the flags, encoded as follows: + bits[3:0] trigger type and level flags. + + 1 = low-to-high edge triggered + 2 = high-to-low edge triggered + 4 = active high level-sensitive + 8 = active low level-sensitive + + const: 2 + + interrupt-controller: true + + msi-parent: + maxItems: 1 + +required: + - compatible + - reg + - "#interrupt-cells" + - interrupt-controller + - msi-parent + +additionalProperties: false + +examples: + - | + interrupt-controller@2f000000 { + compatible = "arm,gic-v5-iwb"; + reg = <0x2f000000 0x10000>; + + #address-cells = <0>; + + #interrupt-cells = <2>; + interrupt-controller; + + msi-parent = <&its0 64>; + }; +... diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml new file mode 100644 index 000000000000..86ca7f3ac281 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml @@ -0,0 +1,267 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Generic Interrupt Controller, version 5 + +maintainers: + - Lorenzo Pieralisi + - Marc Zyngier + +description: | + The GICv5 architecture defines the guidelines to implement GICv5 + compliant interrupt controllers for AArch64 systems. + + The GICv5 specification can be found at + https://developer.arm.com/documentation/aes0070 + + The GICv5 architecture is composed of multiple components: + - one or more IRS (Interrupt Routing Service) + - zero or more ITS (Interrupt Translation Service) + + The architecture defines: + - PE-Private Peripheral Interrupts (PPI) + - Shared Peripheral Interrupts (SPI) + - Logical Peripheral Interrupts (LPI) + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: arm,gic-v5 + + "#address-cells": + enum: [ 1, 2 ] + + "#size-cells": + enum: [ 1, 2 ] + + ranges: true + + "#interrupt-cells": + description: | + The 1st cell corresponds to the INTID.Type field in the INTID; 1 for PPI, + 3 for SPI. LPI interrupts must not be described in the bindings since + they are allocated dynamically by the software component managing them. + + The 2nd cell contains the interrupt INTID.ID field. + + The 3rd cell is the flags, encoded as follows: + bits[3:0] trigger type and level flags. + + 1 = low-to-high edge triggered + 2 = high-to-low edge triggered + 4 = active high level-sensitive + 8 = active low level-sensitive + + const: 3 + + interrupt-controller: true + + interrupts: + description: + The VGIC maintenance interrupt. + maxItems: 1 + +required: + - compatible + - "#address-cells" + - "#size-cells" + - ranges + - "#interrupt-cells" + - interrupt-controller + +patternProperties: + "^irs@[0-9a-f]+$": + type: object + description: + GICv5 has one or more Interrupt Routing Services (IRS) that are + responsible for handling IRQ state and routing. + + additionalProperties: false + + properties: + compatible: + const: arm,gic-v5-irs + + reg: + minItems: 1 + items: + - description: IRS config frames + - description: IRS setlpi frames + + reg-names: + description: + Describe config and setlpi frames that are present. + "ns-" stands for non-secure, "s-" for secure, "realm-" for realm + and "el3-" for EL3. + minItems: 1 + maxItems: 8 + items: + enum: [ ns-config, s-config, realm-config, el3-config, ns-setlpi, + s-setlpi, realm-setlpi, el3-setlpi ] + + "#address-cells": + enum: [ 1, 2 ] + + "#size-cells": + enum: [ 1, 2 ] + + ranges: true + + dma-noncoherent: + description: + Present if the GIC IRS permits programming shareability and + cacheability attributes but is connected to a non-coherent + downstream interconnect. + + cpus: + description: + CPUs managed by the IRS. + + arm,iaffids: + $ref: /schemas/types.yaml#/definitions/uint16-array + description: + Interrupt AFFinity ID (IAFFID) associated with the CPU whose + CPU node phandle is at the same index in the cpus array. + + patternProperties: + "^its@[0-9a-f]+$": + type: object + description: + GICv5 has zero or more Interrupt Translation Services (ITS) that are + used to route Message Signalled Interrupts (MSI) to the CPUs. Each + ITS is connected to an IRS. + additionalProperties: false + + properties: + compatible: + const: arm,gic-v5-its + + reg: + items: + - description: ITS config frames + + reg-names: + description: + Describe config frames that are present. + "ns-" stands for non-secure, "s-" for secure, "realm-" for realm + and "el3-" for EL3. + minItems: 1 + maxItems: 4 + items: + enum: [ ns-config, s-config, realm-config, el3-config ] + + "#address-cells": + enum: [ 1, 2 ] + + "#size-cells": + enum: [ 1, 2 ] + + ranges: true + + dma-noncoherent: + description: + Present if the GIC ITS permits programming shareability and + cacheability attributes but is connected to a non-coherent + downstream interconnect. + + patternProperties: + "^msi-controller@[0-9a-f]+$": + type: object + description: + GICv5 ITS has one or more translate register frames. + additionalProperties: false + + properties: + reg: + items: + - description: ITS translate frames + + reg-names: + description: + Describe translate frames that are present. + "ns-" stands for non-secure, "s-" for secure, "realm-" for realm + and "el3-" for EL3. + minItems: 1 + maxItems: 4 + items: + enum: [ ns-translate, s-translate, realm-translate, el3-translate ] + + "#msi-cells": + description: + The single msi-cell is the DeviceID of the device which will + generate the MSI. + const: 1 + + msi-controller: true + + required: + - reg + - reg-names + - "#msi-cells" + - msi-controller + + required: + - compatible + - reg + - reg-names + + required: + - compatible + - reg + - reg-names + - cpus + - arm,iaffids + +additionalProperties: false + +examples: + - | + interrupt-controller { + compatible = "arm,gic-v5"; + + #interrupt-cells = <3>; + interrupt-controller; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupts = <1 25 4>; + + irs@2f1a0000 { + compatible = "arm,gic-v5-irs"; + reg = <0x2f1a0000 0x10000>; // IRS_CONFIG_FRAME + reg-names = "ns-config"; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + cpus = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>, <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>; + arm,iaffids = /bits/ 16 <0 1 2 3 4 5 6 7>; + + its@2f120000 { + compatible = "arm,gic-v5-its"; + reg = <0x2f120000 0x10000>; // ITS_CONFIG_FRAME + reg-names = "ns-config"; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + msi-controller@2f130000 { + reg = <0x2f130000 0x10000>; // ITS_TRANSLATE_FRAME + reg-names = "ns-translate"; + + #msi-cells = <1>; + msi-controller; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,nvic.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,nvic.yaml index d89eca956c5f..32dfa2bf05d8 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,nvic.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,nvic.yaml @@ -17,6 +17,7 @@ description: properties: compatible: enum: + - arm,armv7m-nvic # deprecated - arm,v6m-nvic - arm,v7m-nvic - arm,v8m-nvic @@ -30,7 +31,7 @@ properties: interrupt-controller: true '#interrupt-cells': - const: 2 + enum: [1, 2] description: | Number of cells to encode an interrupt source: first = interrupt number, second = priority. diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,icoll.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,icoll.yaml new file mode 100644 index 000000000000..7b09fd7d588f --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,icoll.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,icoll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MXS icoll Interrupt controller + +maintainers: + - Frank Li + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,imx23-icoll + - fsl,imx28-icoll + - const: fsl,icoll + + reg: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + +required: + - compatible + - reg + - interrupt-controller + - '#interrupt-cells' + +additionalProperties: false + +examples: + - | + interrupt-controller@80000000 { + compatible = "fsl,imx28-icoll", "fsl,icoll"; + reg = <0x80000000 0x2000>; + interrupt-controller; + #interrupt-cells = <1>; + }; + diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,mpic-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,mpic-msi.yaml new file mode 100644 index 000000000000..78d784973661 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,mpic-msi.yaml @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,mpic-msi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MSI interrupt controller + +description: | + The Freescale hypervisor and msi-address-64 + ------------------------------------------- + + Normally, PCI devices have access to all of CCSR via an ATMU mapping. The + Freescale MSI driver calculates the address of MSIIR (in the MSI register + block) and sets that address as the MSI message address. + + In a virtualized environment, the hypervisor may need to create an IOMMU + mapping for MSIIR. The Freescale ePAPR hypervisor has this requirement + because of hardware limitations of the Peripheral Access Management Unit + (PAMU), which is currently the only IOMMU that the hypervisor supports. + The ATMU is programmed with the guest physical address, and the PAMU + intercepts transactions and reroutes them to the true physical address. + + In the PAMU, each PCI controller is given only one primary window. The + PAMU restricts DMA operations so that they can only occur within a window. + Because PCI devices must be able to DMA to memory, the primary window must + be used to cover all of the guest's memory space. + + PAMU primary windows can be divided into 256 subwindows, and each + subwindow can have its own address mapping ("guest physical" to "true + physical"). However, each subwindow has to have the same alignment, which + means they cannot be located at just any address. Because of these + restrictions, it is usually impossible to create a 4KB subwindow that + covers MSIIR where it's normally located. + + Therefore, the hypervisor has to create a subwindow inside the same + primary window used for memory, but mapped to the MSIR block (where MSIIR + lives). The first subwindow after the end of guest memory is used for + this. The address specified in the msi-address-64 property is the PCI + address of MSIIR. The hypervisor configures the PAMU to map that address to + the true physical address of MSIIR. + +maintainers: + - J. Neuschäfer + +properties: + compatible: + oneOf: + - enum: + - fsl,mpic-msi + - fsl,mpic-msi-v4.3 + - fsl,ipic-msi + - fsl,vmpic-msi + - fsl,vmpic-msi-v4.3 + - items: + - enum: + - fsl,mpc8572-msi + - fsl,mpc8610-msi + - fsl,mpc8641-msi + - const: fsl,mpic-msi + + reg: + minItems: 1 + items: + - description: Address and length of the shared message interrupt + register set + - description: Address of aliased MSIIR or MSIIR1 register for platforms + that have such an alias. If using MSIIR1, the second region must be + added because different MSI group has different MSIIR1 offset. + + interrupts: + minItems: 1 + maxItems: 16 + description: + Each one of the interrupts here is one entry per 32 MSIs, and routed to + the host interrupt controller. The interrupts should be set as edge + sensitive. If msi-available-ranges is present, only the interrupts that + correspond to available ranges shall be present. + + msi-available-ranges: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: First MSI interrupt in this range + - description: Number of MSI interrupts in this range + description: + Define which MSI interrupt can be used in the 256 MSI interrupts. + If not specified, all the MSI interrupts can be used. + Each available range must begin and end on a multiple of 32 (i.e. no + splitting an individual MSI register or the associated PIC interrupt). + + msi-address-64: + $ref: /schemas/types.yaml#/definitions/uint64 + description: + 64-bit PCI address of the MSIIR register. The MSIIR register is used for + MSI messaging. The address of MSIIR in PCI address space is the MSI + message address. + + This property may be used in virtualized environments where the hypervisor + has created an alternate mapping for the MSIR block. See the top-level + description for an explanation. + +required: + - compatible + - reg + - interrupts + +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,mpic-msi-v4.3 + - fsl,vmpic-msi-v4.3 + then: + properties: + interrupts: + minItems: 16 + description: + Version 4.3 implies that there are 16 shared interrupts, and they + are configured through MSIIR1. + + # MPIC v4.3 does not support this property because the 32 interrupts of + # an individual register are not continuous when using MSIIR1. + msi-available-ranges: false + + reg: + minItems: 2 + + else: + properties: + interrupts: + maxItems: 8 + description: + In versions before 4.3, only 8 shared interrupts are available, and + they are configured through MSIIR. + +unevaluatedProperties: false + +examples: + - | + msi@41600 { + compatible = "fsl,mpc8610-msi", "fsl,mpic-msi"; + reg = <0x41600 0x80>; + msi-available-ranges = <0 0x100>; + interrupts = <0xe0 0>, <0xe1 0>, <0xe2 0>, <0xe3 0>, + <0xe4 0>, <0xe5 0>, <0xe6 0>, <0xe7 0>; + }; + + - | + msi@41600 { + compatible = "fsl,mpic-msi-v4.3"; + reg = <0x41600 0x200>, <0x44148 4>; + interrupts = <0xe0 0 0 0>, <0xe1 0 0 0>, <0xe2 0 0 0>, <0xe3 0 0 0>, + <0xe4 0 0 0>, <0xe5 0 0 0>, <0xe6 0 0 0>, <0xe7 0 0 0>, + <0x100 0 0 0>, <0x101 0 0 0>, <0x102 0 0 0>, <0x103 0 0 0>, + <0x104 0 0 0>, <0x105 0 0 0>, <0x106 0 0 0>, <0x107 0 0 0>; + }; + +... diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml new file mode 100644 index 000000000000..fdc254f8d013 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,vf610-mscm-ir.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Vybrid Miscellaneous System Control - Interrupt Router + +description: + The MSCM IP contains multiple sub modules, this binding describes the second + block of registers which control the interrupt router. The interrupt router + allows to configure the recipient of each peripheral interrupt. Furthermore + it controls the directed processor interrupts. The module is available in all + Vybrid SoC's but is only really useful in dual core configurations (VF6xx + which comes with a Cortex-A5/Cortex-M4 combination). + + +maintainers: + - Frank Li + +properties: + compatible: + const: fsl,vf610-mscm-ir + + reg: + maxItems: 1 + + fsl,cpucfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The handle to the MSCM CPU configuration node, required + to get the current CPU ID + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + description: + Two cells, interrupt number and cells. + The hardware interrupt number according to interrupt + assignment of the interrupt router is required. + Flags get passed only when using GIC as parent. Flags + encoding as documented by the GIC bindings. + +required: + - compatible + - reg + - fsl,cpucfg + - interrupt-controller + - '#interrupt-cells' + +additionalProperties: false + +examples: + - | + interrupt-controller@40001800 { + compatible = "fsl,vf610-mscm-ir"; + reg = <0x40001800 0x400>; + fsl,cpucfg = <&mscm_cpucfg>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,mt6577-sysirq.yaml b/Documentation/devicetree/bindings/interrupt-controller/mediatek,mt6577-sysirq.yaml index 123d24b05556..30d76692ca87 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,mt6577-sysirq.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,mt6577-sysirq.yaml @@ -21,6 +21,7 @@ properties: - enum: - mediatek,mt2701-sysirq - mediatek,mt2712-sysirq + - mediatek,mt6572-sysirq - mediatek,mt6580-sysirq - mediatek,mt6582-sysirq - mediatek,mt6589-sysirq diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml index ffc4768bad06..5b827bc24301 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml @@ -53,6 +53,7 @@ properties: oneOf: - items: - enum: + - andestech,qilai-plic - renesas,r9a07g043-plic - const: andestech,nceplic100 - items: diff --git a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml index 8d330906bbbd..c1ab865fcd64 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml @@ -4,23 +4,32 @@ $id: http://devicetree.org/schemas/interrupt-controller/thead,c900-aclint-sswi.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: T-HEAD C900 ACLINT Supervisor-level Software Interrupt Device +title: ACLINT Supervisor-level Software Interrupt Device maintainers: - Inochi Amaoto description: - The SSWI device is a part of the THEAD ACLINT device. It provides - supervisor-level IPI functionality for a set of HARTs on a THEAD - platform. It provides a register to set an IPI (SETSSIP) for each - HART connected to the SSWI device. + The SSWI device is a part of the ACLINT device. It provides + supervisor-level IPI functionality for a set of HARTs on a supported + platforms. It provides a register to set an IPI (SETSSIP) for each + HART connected to the SSWI device. See draft specification + https://github.com/riscvarchive/riscv-aclint + + Following variants of the SSWI ACLINT supported, using dedicated + compatible string + - THEAD C900 + - MIPS P8700 properties: compatible: - items: - - enum: - - sophgo,sg2044-aclint-sswi - - const: thead,c900-aclint-sswi + oneOf: + - items: + - enum: + - sophgo,sg2044-aclint-sswi + - const: thead,c900-aclint-sswi + - items: + - const: mips,p8700-aclint-sswi reg: maxItems: 1 @@ -34,6 +43,14 @@ properties: minItems: 1 maxItems: 4095 + riscv,hart-indexes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4095 + description: + A list of hart indexes that APLIC should use to address each hart + that is mentioned in the "interrupts-extended" + additionalProperties: false required: @@ -43,8 +60,22 @@ required: - interrupt-controller - interrupts-extended +allOf: + - if: + properties: + compatible: + contains: + const: mips,p8700-aclint-sswi + then: + required: + - riscv,hart-indexes + else: + properties: + riscv,hart-indexes: false + examples: - | + //Example 1 interrupt-controller@94000000 { compatible = "sophgo,sg2044-aclint-sswi", "thead,c900-aclint-sswi"; reg = <0x94000000 0x00004000>; @@ -55,4 +86,19 @@ examples: <&cpu3intc 1>, <&cpu4intc 1>; }; + + - | + //Example 2 + interrupt-controller@94000000 { + compatible = "mips,p8700-aclint-sswi"; + reg = <0x94000000 0x00004000>; + #interrupt-cells = <0>; + interrupt-controller; + interrupts-extended = <&cpu1intc 1>, + <&cpu2intc 1>, + <&cpu3intc 1>, + <&cpu4intc 1>; + riscv,hart-indexes = <0x0 0x1 0x10 0x11>; + }; + ... diff --git a/Documentation/devicetree/bindings/interrupt-controller/xlnx,intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/xlnx,intc.yaml new file mode 100644 index 000000000000..b4f58ed25993 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/xlnx,intc.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/xlnx,intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Interrupt Controller + +maintainers: + - Michal Simek + +description: + The controller is a soft IP core that is configured at build time for the + number of interrupts and the type of each interrupt. These details cannot + be changed at run time. + +properties: + compatible: + const: xlnx,xps-intc-1.00.a + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + "#interrupt-cells": + const: 2 + description: + Specifies the number of cells needed to encode an interrupt source. + The value shall be a minimum of 1. The Xilinx device trees typically + use 2 but the 2nd value is not used. + + interrupt-controller: true + + interrupts: + maxItems: 1 + description: + Specifies the interrupt of the parent controller from which it is chained. + + xlnx,kind-of-intr: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + A 32 bit value specifying the interrupt type for each possible interrupt + (1 = edge, 0 = level). The interrupt type typically comes in thru + the device tree node of the interrupt generating device, but in this case + the interrupt type is determined by the interrupt controller based on how + it was implemented. + + xlnx,num-intr-inputs: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + description: + Specifies the number of interrupts supported by the specific + implementation of the controller. + +required: + - reg + - "#interrupt-cells" + - interrupt-controller + - xlnx,kind-of-intr + - xlnx,num-intr-inputs + +additionalProperties: false + +examples: + - | + interrupt-controller@41800000 { + compatible = "xlnx,xps-intc-1.00.a"; + reg = <0x41800000 0x10000>; + #interrupt-cells = <2>; + interrupt-controller; + xlnx,kind-of-intr = <0x1>; + xlnx,num-intr-inputs = <1>; + }; diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml index 7b9d5507d6cc..89495f094d52 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml @@ -35,6 +35,7 @@ properties: - description: Qcom SoCs implementing "qcom,smmu-500" and "arm,mmu-500" items: - enum: + - qcom,milos-smmu-500 - qcom,qcm2290-smmu-500 - qcom,qcs615-smmu-500 - qcom,qcs8300-smmu-500 @@ -88,6 +89,7 @@ properties: - description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500" items: - enum: + - qcom,milos-smmu-500 - qcom,qcm2290-smmu-500 - qcom,qcs615-smmu-500 - qcom,qcs8300-smmu-500 @@ -132,10 +134,6 @@ properties: - qcom,sm7150-smmu-v2 - const: qcom,adreno-smmu - const: qcom,smmu-v2 - - description: Qcom Adreno GPUs on Google Cheza platform - items: - - const: qcom,sdm845-smmu-v2 - - const: qcom,smmu-v2 - description: Marvell SoCs implementing "arm,mmu-500" items: - const: marvell,ap806-smmu-500 @@ -534,6 +532,7 @@ allOf: compatible: items: - enum: + - qcom,milos-smmu-500 - qcom,sar2130p-smmu-500 - qcom,sm8550-smmu-500 - qcom,sm8650-smmu-500 diff --git a/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml b/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml index 5d015eeb06d0..d4838c3b3741 100644 --- a/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/riscv,iommu.yaml @@ -139,9 +139,9 @@ examples: /* The IOMMU programming interface uses slot 00:01.0 */ iommu0: iommu@1,0 { - compatible = "pci1efd,edf1", "riscv,pci-iommu"; - reg = <0x800 0 0 0 0>; - #iommu-cells = <1>; + compatible = "pci1efd,edf1", "riscv,pci-iommu"; + reg = <0x800 0 0 0 0>; + #iommu-cells = <1>; }; }; }; diff --git a/Documentation/devicetree/bindings/ipmi/ipmb-dev.yaml b/Documentation/devicetree/bindings/ipmi/ipmb-dev.yaml new file mode 100644 index 000000000000..8b0d71901195 --- /dev/null +++ b/Documentation/devicetree/bindings/ipmi/ipmb-dev.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ipmi/ipmb-dev.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The Intelligent Platform Management Bus(IPMB) Device + +description: | + The IPMB is an I2C bus which provides interconnection between a Baseboard + Management Controller(BMC) and chassis electronics. The BMC sends IPMI + requests to intelligent controllers like Satellite Management Controller(MC) + devices via IPMB and the device sends responses back to the BMC. + This device uses an I2C slave device to send and receive IPMB messages, + either on a BMC or other MC. A miscellaneous device provices a user space + program to communicate with the kernel and the backend device. Some IPMB + devices only support the I2C protocol and not the SMB protocol. + + IPMB communications protocol Specification V1.0 + https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmp-spec-v1.0.pdf + +maintainers: + - Ninad Palsule + +properties: + compatible: + enum: + - ipmb-dev + + reg: + maxItems: 1 + + i2c-protocol: + description: + Use I2C block transfer instead of SMBUS block transfer. + type: boolean + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ipmb-dev@10 { + compatible = "ipmb-dev"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; + i2c-protocol; + }; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml index 402c25424525..23f809906ba7 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml @@ -81,7 +81,12 @@ patternProperties: properties: reg: - maxItems: 1 + items: + - minimum: 0 + maximum: 2 + + description: + This property denotes the index within the LED bank. required: - reg @@ -138,18 +143,18 @@ examples: color = ; function = LED_FUNCTION_STANDBY; - led@3 { - reg = <0x3>; + led@0 { + reg = <0x0>; color = ; }; - led@4 { - reg = <0x4>; + led@1 { + reg = <0x1>; color = ; }; - led@5 { - reg = <0x5>; + led@2 { + reg = <0x2>; color = ; }; }; diff --git a/Documentation/devicetree/bindings/leds/leds-mt6360.yaml b/Documentation/devicetree/bindings/leds/leds-mt6360.yaml index d84e28e616d7..d2e1d8afc302 100644 --- a/Documentation/devicetree/bindings/leds/leds-mt6360.yaml +++ b/Documentation/devicetree/bindings/leds/leds-mt6360.yaml @@ -87,106 +87,105 @@ additionalProperties: false examples: - | - #include - led-controller { - compatible = "mediatek,mt6360-led"; - #address-cells = <1>; - #size-cells = <0>; + #include + led-controller { + compatible = "mediatek,mt6360-led"; + #address-cells = <1>; + #size-cells = <0>; - multi-led@0 { - reg = <0>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <24000>; - #address-cells = <1>; - #size-cells = <0>; - led@0 { - reg = <0>; - color = ; - }; - led@1 { - reg = <1>; - color = ; - }; - led@2 { - reg = <2>; - color = ; - }; - }; - led@3 { - reg = <3>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <150000>; - }; - led@4 { - reg = <4>; - function = LED_FUNCTION_FLASH; - color = ; - function-enumerator = <1>; - led-max-microamp = <200000>; - flash-max-microamp = <500000>; - flash-max-timeout-us = <1024000>; - }; - led@5 { - reg = <5>; - function = LED_FUNCTION_FLASH; - color = ; - function-enumerator = <2>; - led-max-microamp = <200000>; - flash-max-microamp = <500000>; - flash-max-timeout-us = <1024000>; - }; - }; + multi-led@0 { + reg = <0>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <24000>; + #address-cells = <1>; + #size-cells = <0>; + led@0 { + reg = <0>; + color = ; + }; + led@1 { + reg = <1>; + color = ; + }; + led@2 { + reg = <2>; + color = ; + }; + }; + led@3 { + reg = <3>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <150000>; + }; + led@4 { + reg = <4>; + function = LED_FUNCTION_FLASH; + color = ; + function-enumerator = <1>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1024000>; + }; + led@5 { + reg = <5>; + function = LED_FUNCTION_FLASH; + color = ; + function-enumerator = <2>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1024000>; + }; + }; - | + led-controller { + compatible = "mediatek,mt6360-led"; + #address-cells = <1>; + #size-cells = <0>; - led-controller { - compatible = "mediatek,mt6360-led"; - #address-cells = <1>; - #size-cells = <0>; - - led@0 { - reg = <0>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <24000>; - }; - led@1 { - reg = <1>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <24000>; - }; - led@2 { - reg = <2>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <24000>; - }; - led@3 { - reg = <3>; - function = LED_FUNCTION_INDICATOR; - color = ; - led-max-microamp = <150000>; - }; - led@4 { - reg = <4>; - function = LED_FUNCTION_FLASH; - color = ; - function-enumerator = <1>; - led-max-microamp = <200000>; - flash-max-microamp = <500000>; - flash-max-timeout-us = <1024000>; - }; - led@5 { - reg = <5>; - function = LED_FUNCTION_FLASH; - color = ; - function-enumerator = <2>; - led-max-microamp = <200000>; - flash-max-microamp = <500000>; - flash-max-timeout-us = <1024000>; - }; - }; + led@0 { + reg = <0>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <24000>; + }; + led@1 { + reg = <1>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <24000>; + }; + led@2 { + reg = <2>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <24000>; + }; + led@3 { + reg = <3>; + function = LED_FUNCTION_INDICATOR; + color = ; + led-max-microamp = <150000>; + }; + led@4 { + reg = <4>; + function = LED_FUNCTION_FLASH; + color = ; + function-enumerator = <1>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1024000>; + }; + led@5 { + reg = <5>; + function = LED_FUNCTION_FLASH; + color = ; + function-enumerator = <2>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1024000>; + }; + }; ... diff --git a/Documentation/devicetree/bindings/leds/onnn,ncp5623.yaml b/Documentation/devicetree/bindings/leds/onnn,ncp5623.yaml index 9c9f3a682ba2..11d45c7f741d 100644 --- a/Documentation/devicetree/bindings/leds/onnn,ncp5623.yaml +++ b/Documentation/devicetree/bindings/leds/onnn,ncp5623.yaml @@ -19,7 +19,9 @@ properties: - onnn,ncp5623 reg: - const: 0x38 + enum: + - 0x38 + - 0x39 multi-led: type: object diff --git a/Documentation/devicetree/bindings/mailbox/allwinner,sun6i-a31-msgbox.yaml b/Documentation/devicetree/bindings/mailbox/allwinner,sun6i-a31-msgbox.yaml index 75d5d97305e1..87d31963c1b7 100644 --- a/Documentation/devicetree/bindings/mailbox/allwinner,sun6i-a31-msgbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/allwinner,sun6i-a31-msgbox.yaml @@ -68,13 +68,13 @@ examples: #include msgbox: mailbox@1c17000 { - compatible = "allwinner,sun8i-h3-msgbox", - "allwinner,sun6i-a31-msgbox"; - reg = <0x01c17000 0x1000>; - clocks = <&ccu CLK_BUS_MSGBOX>; - resets = <&ccu RST_BUS_MSGBOX>; - interrupts = ; - #mbox-cells = <1>; + compatible = "allwinner,sun8i-h3-msgbox", + "allwinner,sun6i-a31-msgbox"; + reg = <0x01c17000 0x1000>; + clocks = <&ccu CLK_BUS_MSGBOX>; + resets = <&ccu RST_BUS_MSGBOX>; + interrupts = ; + #mbox-cells = <1>; }; ... diff --git a/Documentation/devicetree/bindings/mailbox/amlogic,meson-gxbb-mhu.yaml b/Documentation/devicetree/bindings/mailbox/amlogic,meson-gxbb-mhu.yaml index 385809ed1569..79963c9878ba 100644 --- a/Documentation/devicetree/bindings/mailbox/amlogic,meson-gxbb-mhu.yaml +++ b/Documentation/devicetree/bindings/mailbox/amlogic,meson-gxbb-mhu.yaml @@ -27,7 +27,7 @@ properties: maxItems: 1 interrupts: - minItems: 3 + maxItems: 3 description: Contains the interrupt information corresponding to each of the 3 links of MHU. @@ -46,8 +46,8 @@ additionalProperties: false examples: - | mailbox@c883c404 { - compatible = "amlogic,meson-gxbb-mhu"; - reg = <0xc883c404 0x4c>; - interrupts = <208>, <209>, <210>; - #mbox-cells = <1>; + compatible = "amlogic,meson-gxbb-mhu"; + reg = <0xc883c404 0x4c>; + interrupts = <208>, <209>, <210>; + #mbox-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml b/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml index 4c0668e5f0bd..474c1a0f99f3 100644 --- a/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml @@ -78,11 +78,11 @@ additionalProperties: false examples: - | - mailbox@77408000 { - compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; - reg = <0x77408000 0x4000>; - interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>; - interrupt-names = "send-empty", "send-not-empty", - "recv-empty", "recv-not-empty"; - #mbox-cells = <0>; - }; + mailbox@77408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x77408000 0x4000>; + interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/mailbox/aspeed,ast2700-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/aspeed,ast2700-mailbox.yaml new file mode 100644 index 000000000000..600e2d63fccd --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/aspeed,ast2700-mailbox.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/aspeed,ast2700-mailbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED AST2700 mailbox controller + +maintainers: + - Jammy Huang + +description: > + ASPEED AST2700 has multiple processors that need to communicate with each + other. The mailbox controller provides a way for these processors to send + messages to each other. It is a hardware-based inter-processor communication + mechanism that allows processors to send and receive messages through + dedicated channels. + + The mailbox's tx/rx are independent, meaning that one processor can send a + message while another processor is receiving a message simultaneously. + There are 4 channels available for both tx and rx operations. Each channel + has a FIFO buffer that can hold messages of a fixed size (32 bytes in this + case). + + The mailbox controller also supports interrupt generation, allowing + processors to notify each other when a message is available or when an event + occurs. + +properties: + compatible: + const: aspeed,ast2700-mailbox + + reg: + items: + - description: TX control register + - description: RX control register + + reg-names: + items: + - const: tx + - const: rx + + interrupts: + maxItems: 1 + + "#mbox-cells": + const: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - "#mbox-cells" + +additionalProperties: false + +examples: + - | + #include + + mailbox@12c1c200 { + compatible = "aspeed,ast2700-mailbox"; + reg = <0x12c1c200 0x100>, <0x12c1c300 0x100>; + reg-names = "tx", "rx"; + interrupts = ; + #mbox-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/mailbox/brcm,bcm74110-mbox.yaml b/Documentation/devicetree/bindings/mailbox/brcm,bcm74110-mbox.yaml new file mode 100644 index 000000000000..750cc96edb46 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/brcm,bcm74110-mbox.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/brcm,bcm74110-mbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM74110 Mailbox + +maintainers: + - Justin Chen + - Florian Fainelli + +description: Broadcom mailbox hardware first introduced with 74110 + +properties: + compatible: + enum: + - brcm,bcm74110-mbox + + reg: + maxItems: 1 + + interrupts: + items: + - description: RX doorbell and watermark interrupts + - description: TX doorbell and watermark interrupts + + "#mbox-cells": + const: 2 + description: + The first cell is channel type and second cell is shared memory slot + + brcm,rx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: RX Mailbox number + + brcm,tx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TX Mailbox number + +required: + - compatible + - reg + - interrupts + - "#mbox-cells" + - brcm,rx + - brcm,tx + +additionalProperties: false + +examples: + - | + #include + #include + + mailbox@a552000 { + compatible = "brcm,bcm74110-mbox"; + reg = <0xa552000 0x1104>; + interrupts = , + ; + #mbox-cells = <0x2>; + brcm,rx = <0x7>; + brcm,tx = <0x6>; + }; diff --git a/Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml b/Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml new file mode 100644 index 000000000000..66d75b7bc8c8 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/cix,sky1-mbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cixtech mailbox controller + +maintainers: + - Guomin Chen + +description: + The Cixtech mailbox controller, used in the Cixtech Sky1 SoC, + is used for message transmission between multiple processors + within the SoC, such as the AP, PM, audio DSP, SensorHub MCU, + and others + + Each Cixtech mailbox controller is unidirectional, so they are + typically used in pairs-one for receiving and one for transmitting. + + Each Cixtech mailbox supports 11 channels with different transmission modes + channel 0-7 - Fast channel with 32bit transmit register and IRQ support + channel 8 - Doorbell mode,using the mailbox as an interrupt-generating + mechanism. + channel 9 - Fifo based channel with 32*32bit depth fifo and IRQ support + channel 10 - Reg based channel with 32*32bit transmit register and + Doorbell+transmit acknowledgment IRQ support + + In the CIX Sky1 SoC use case, there are 4 pairs of mailbox controllers + AP <--> PM - using Doorbell transfer mode + AP <--> SE - using REG transfer mode + AP <--> DSP - using FIFO transfer mode + AP <--> SensorHub - using FIFO transfer mode + +properties: + compatible: + const: cix,sky1-mbox + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#mbox-cells": + const: 1 + + cix,mbox-dir: + $ref: /schemas/types.yaml#/definitions/string + description: Direction of the mailbox relative to the AP + enum: [tx, rx] + +required: + - compatible + - reg + - interrupts + - "#mbox-cells" + - cix,mbox-dir + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + mbox_ap2pm: mailbox@30000000 { + compatible = "cix,sky1-mbox"; + reg = <0 0x30000000 0 0x10000>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "tx"; + }; + }; diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.yaml b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.yaml index 2d14fc948999..f833b845de0d 100644 --- a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.yaml +++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.yaml @@ -59,14 +59,12 @@ description: | properties: - $nodename: - pattern: "^hsp@[0-9a-f]+$" - compatible: oneOf: - - const: nvidia,tegra186-hsp - - const: nvidia,tegra194-hsp - - const: nvidia,tegra264-hsp + - enum: + - nvidia,tegra186-hsp + - nvidia,tegra194-hsp + - nvidia,tegra264-hsp - items: - const: nvidia,tegra234-hsp - const: nvidia,tegra194-hsp @@ -76,7 +74,7 @@ properties: interrupts: minItems: 1 - maxItems: 9 + maxItems: 17 interrupt-names: oneOf: @@ -84,6 +82,25 @@ properties: - items: - const: doorbell + - items: + - const: doorbell + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - pattern: "^shared([0-9]|1[0-5])$" + - items: - const: doorbell - pattern: "^shared[0-7]$" @@ -111,14 +128,10 @@ examples: #include #include - hsp_top0: hsp@3c00000 { + mailbox@3c00000 { compatible = "nvidia,tegra186-hsp"; reg = <0x03c00000 0xa0000>; interrupts = ; interrupt-names = "doorbell"; #mbox-cells = <2>; }; - - client { - mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_CCPLEX>; - }; diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index ac726136f7e5..615ed103b7e6 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -251,7 +251,7 @@ examples: # Example apcs with msm8996 - | #include - apcs_glb: mailbox@9820000 { + mailbox@9820000 { compatible = "qcom,msm8996-apcs-hmss-global"; reg = <0x9820000 0x1000>; @@ -259,13 +259,6 @@ examples: #clock-cells = <0>; }; - rpm-glink { - compatible = "qcom,glink-rpm"; - interrupts = ; - qcom,rpm-msg-ram = <&rpm_msg_ram>; - mboxes = <&apcs_glb 0>; - }; - # Example apcs with qcs404 - | #define GCC_APSS_AHB_CLK_SRC 1 diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index f69c0ec5d19d..e5c423130db6 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,milos-ipcc - qcom,qcs8300-ipcc - qcom,qdu1000-ipcc - qcom,sa8255p-ipcc diff --git a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml index 1a2001e58880..8504ceb64806 100644 --- a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml @@ -242,7 +242,7 @@ examples: - | /* OMAP4 */ #include - mailbox: mailbox@4a0f4000 { + mailbox@4a0f4000 { compatible = "ti,omap4-mailbox"; reg = <0x4a0f4000 0x200>; interrupts = ; @@ -260,13 +260,9 @@ examples: }; }; - dsp { - mboxes = <&mailbox &mbox_dsp>; - }; - - | /* AM33xx */ - mailbox1: mailbox@480c8000 { + mailbox@480c8000 { compatible = "ti,omap4-mailbox"; reg = <0x480c8000 0x200>; interrupts = <77>; @@ -283,7 +279,7 @@ examples: - | /* AM65x */ - mailbox0_cluster0: mailbox@31f80000 { + mailbox@31f80000 { compatible = "ti,am654-mailbox"; reg = <0x31f80000 0x200>; #mbox-cells = <1>; diff --git a/Documentation/devicetree/bindings/mailbox/ti,secure-proxy.yaml b/Documentation/devicetree/bindings/mailbox/ti,secure-proxy.yaml index eea822861804..c321b69f0ccd 100644 --- a/Documentation/devicetree/bindings/mailbox/ti,secure-proxy.yaml +++ b/Documentation/devicetree/bindings/mailbox/ti,secure-proxy.yaml @@ -36,7 +36,7 @@ properties: - const: scfg reg: - minItems: 3 + maxItems: 3 interrupt-names: minItems: 1 @@ -68,12 +68,12 @@ examples: - | #include secure_proxy: mailbox@32c00000 { - compatible = "ti,am654-secure-proxy"; - #mbox-cells = <1>; - reg-names = "target_data", "rt", "scfg"; - reg = <0x32c00000 0x100000>, - <0x32400000 0x100000>, - <0x32800000 0x100000>; - interrupt-names = "rx_011"; - interrupts = ; + compatible = "ti,am654-secure-proxy"; + #mbox-cells = <1>; + reg-names = "target_data", "rt", "scfg"; + reg = <0x32c00000 0x100000>, + <0x32400000 0x100000>, + <0x32800000 0x100000>; + interrupt-names = "rx_011"; + interrupts = ; }; diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml index b3d6db922693..1aa5775ba2bc 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml @@ -110,7 +110,7 @@ examples: reg = <0x01cb4000 0x1000>; interrupts = ; clocks = <&ccu CLK_BUS_CSI>, - <&ccu CLK_CSI1_SCLK>, + <&ccu CLK_CSI_SCLK>, <&ccu CLK_DRAM_CSI>; clock-names = "bus", "mod", diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml index a61a76bb611c..3ea4a4290f23 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml @@ -79,7 +79,7 @@ examples: reg = <0x01cb8000 0x1000>; interrupts = ; clocks = <&ccu CLK_BUS_CSI>, - <&ccu CLK_CSI1_SCLK>, + <&ccu CLK_CSI_SCLK>, <&ccu CLK_DRAM_CSI>; clock-names = "bus", "mod", "ram"; resets = <&ccu RST_BUS_CSI>; diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml index 54e15ab8a7f5..627b28e94354 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-mipi-csi2.yaml @@ -103,7 +103,7 @@ examples: reg = <0x01cb1000 0x1000>; interrupts = ; clocks = <&ccu CLK_BUS_CSI>, - <&ccu CLK_CSI1_SCLK>; + <&ccu CLK_CSI_SCLK>; clock-names = "bus", "mod"; resets = <&ccu RST_BUS_CSI>; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml index 2008a47c0580..6ed9a5621064 100644 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -24,6 +24,14 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 2 + + interrupt-names: + items: + - const: error_irq + - const: irq + clocks: items: - description: CSI2Rx system clock diff --git a/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml b/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml new file mode 100644 index 000000000000..511ac0d67a7f --- /dev/null +++ b/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/fsl,imx6q-vdoa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Video Data Order Adapter + +description: + The Video Data Order Adapter (VDOA) is present on the i.MX6q. Its sole purpose + is to reorder video data from the macroblock tiled order produced by the CODA + 960 VPU to the conventional raster-scan order for scanout. + +maintainers: + - Frank Li + +properties: + compatible: + const: "fsl,imx6q-vdoa" + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +additionalProperties: false + +examples: + - | + #include + #include + + vdoa@21e4000 { + compatible = "fsl,imx6q-vdoa"; + reg = <0x021e4000 0x4000>; + interrupts = <0 18 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6QDL_CLK_VDOA>; + }; diff --git a/Documentation/devicetree/bindings/media/fsl,imx8qm-isi.yaml b/Documentation/devicetree/bindings/media/fsl,imx8qm-isi.yaml new file mode 100644 index 000000000000..93f527e223af --- /dev/null +++ b/Documentation/devicetree/bindings/media/fsl,imx8qm-isi.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/fsl,imx8qm-isi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX8QM Image Sensing Interface + +maintainers: + - Frank Li + +description: + The Image Sensing Interface (ISI) combines image processing pipelines with + DMA engines to process and capture frames originating from a variety of + sources. The inputs to the ISI go through Pixel Link interfaces, and their + number and nature is SoC-dependent. They cover both capture interfaces (MIPI + CSI-2 RX, HDMI RX, ...) and display engine outputs for writeback support. + +properties: + compatible: + enum: + - fsl,imx8qm-isi + + reg: + maxItems: 1 + + clocks: + maxItems: 8 + + clock-names: + items: + - const: per0 + - const: per1 + - const: per2 + - const: per3 + - const: per4 + - const: per5 + - const: per6 + - const: per7 + + interrupts: + maxItems: 8 + + power-domains: + maxItems: 8 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: MIPI CSI-2 RX 0 + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: MIPI CSI-2 RX 1 + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: HDMI RX + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - power-domains + - ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + image-controller@58100000 { + compatible = "fsl,imx8qm-isi"; + reg = <0x58100000 0x80000>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&pdma0_lpcg IMX_LPCG_CLK_0>, + <&pdma1_lpcg IMX_LPCG_CLK_0>, + <&pdma2_lpcg IMX_LPCG_CLK_0>, + <&pdma3_lpcg IMX_LPCG_CLK_0>, + <&pdma4_lpcg IMX_LPCG_CLK_0>, + <&pdma5_lpcg IMX_LPCG_CLK_0>, + <&pdma6_lpcg IMX_LPCG_CLK_0>, + <&pdma7_lpcg IMX_LPCG_CLK_0>; + clock-names = "per0", "per1", "per2", "per3", + "per4", "per5", "per6", "per7"; + power-domains = <&pd IMX_SC_R_ISI_CH0>, <&pd IMX_SC_R_ISI_CH1>, + <&pd IMX_SC_R_ISI_CH2>, <&pd IMX_SC_R_ISI_CH3>, + <&pd IMX_SC_R_ISI_CH4>, <&pd IMX_SC_R_ISI_CH5>, + <&pd IMX_SC_R_ISI_CH6>, <&pd IMX_SC_R_ISI_CH7>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + endpoint { + remote-endpoint = <&mipi_csi0_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/fsl,imx8qxp-isi.yaml b/Documentation/devicetree/bindings/media/fsl,imx8qxp-isi.yaml new file mode 100644 index 000000000000..bb41996bd2e3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/fsl,imx8qxp-isi.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/fsl,imx8qxp-isi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX8QXP Image Sensing Interface + +maintainers: + - Frank Li + +description: + The Image Sensing Interface (ISI) combines image processing pipelines with + DMA engines to process and capture frames originating from a variety of + sources. The inputs to the ISI go through Pixel Link interfaces, and their + number and nature is SoC-dependent. They cover both capture interfaces (MIPI + CSI-2 RX, HDMI RX, ...) and display engine outputs for writeback support. + +properties: + compatible: + enum: + - fsl,imx8qxp-isi + + reg: + maxItems: 1 + + clocks: + maxItems: 6 + + clock-names: + items: + - const: per0 + - const: per1 + - const: per2 + - const: per3 + - const: per4 + - const: per5 + + interrupts: + maxItems: 6 + + power-domains: + maxItems: 6 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: MIPI CSI-2 RX 0 + port@6: + $ref: /schemas/graph.yaml#/properties/port + description: CSI-2 Parallel RX + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - power-domains + - ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + image-controller@58100000 { + compatible = "fsl,imx8qxp-isi"; + reg = <0x58100000 0x60000>; + interrupts = , + , + , + , + , + ; + clocks = <&pdma0_lpcg IMX_LPCG_CLK_0>, + <&pdma1_lpcg IMX_LPCG_CLK_0>, + <&pdma2_lpcg IMX_LPCG_CLK_0>, + <&pdma3_lpcg IMX_LPCG_CLK_0>, + <&pdma4_lpcg IMX_LPCG_CLK_0>, + <&pdma5_lpcg IMX_LPCG_CLK_0>; + clock-names = "per0", "per1", "per2", "per3", "per4", "per5"; + power-domains = <&pd IMX_SC_R_ISI_CH0>, <&pd IMX_SC_R_ISI_CH1>, + <&pd IMX_SC_R_ISI_CH2>, <&pd IMX_SC_R_ISI_CH3>, + <&pd IMX_SC_R_ISI_CH4>, <&pd IMX_SC_R_ISI_CH5>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + endpoint { + remote-endpoint = <&mipi_csi0_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/fsl-vdoa.txt b/Documentation/devicetree/bindings/media/fsl-vdoa.txt deleted file mode 100644 index 6c5628530bb7..000000000000 --- a/Documentation/devicetree/bindings/media/fsl-vdoa.txt +++ /dev/null @@ -1,21 +0,0 @@ -Freescale Video Data Order Adapter -================================== - -The Video Data Order Adapter (VDOA) is present on the i.MX6q. Its sole purpose -is to reorder video data from the macroblock tiled order produced by the CODA -960 VPU to the conventional raster-scan order for scanout. - -Required properties: -- compatible: must be "fsl,imx6q-vdoa" -- reg: the register base and size for the device registers -- interrupts: the VDOA interrupt -- clocks: the vdoa clock - -Example: - -vdoa@21e4000 { - compatible = "fsl,imx6q-vdoa"; - reg = <0x021e4000 0x4000>; - interrupts = <0 18 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks IMX6QDL_CLK_VDOA>; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml index f8ace8cbccdb..bc664a016396 100644 --- a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml +++ b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml @@ -23,6 +23,9 @@ description: More detailed documentation can be found in Documentation/devicetree/bindings/media/video-interfaces.txt . +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + properties: compatible: oneOf: @@ -58,16 +61,10 @@ properties: documentation. maxItems: 1 - flash-leds: - description: Flash LED phandles. See ../video-interfaces.txt for details. - - lens-focus: - description: Lens focus controller phandles. See ../video-interfaces.txt - for details. + flash-leds: true + lens-focus: true rotation: - description: Rotation of the sensor. See ../video-interfaces.txt for - details. enum: [ 0, 180 ] port: diff --git a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml index f6b87892068a..a89f740214f7 100644 --- a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml +++ b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml @@ -70,6 +70,15 @@ properties: - bus-type - link-frequencies + slew-rate: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Slew rate ot the output pads DOUT[7:0], LINE_VALID, FRAME_VALID and + PIXCLK. Higher values imply steeper voltage-flanks on the pads. + minimum: 0 + maximum: 7 + default: 7 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov8858.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov8858.yaml index a65f921ec0fd..491f2931e6bc 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov8858.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov8858.yaml @@ -15,6 +15,8 @@ description: | controlled through an I2C-compatible SCCB bus. The sensor transmits images on a MIPI CSI-2 output interface with up to 4 data lanes. +$ref: /schemas/media/video-interface-devices.yaml# + properties: compatible: const: ovti,ov8858 @@ -69,7 +71,7 @@ required: - clocks - port -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml index 0162eec8ca99..aea99ebf8e9e 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml @@ -33,20 +33,21 @@ properties: clock-frequency: description: Frequency of the xclk clock in Hz. + deprecated: true enable-gpios: description: GPIO descriptor for the enable pin. maxItems: 1 - vdddo-supply: - description: Chip digital IO regulator (1.8V). - vdda-supply: description: Chip analog regulator (2.7V). vddd-supply: description: Chip digital core regulator (1.12V). + vdddo-supply: + description: Chip digital IO regulator (1.8V). + flash-leds: true lens-focus: true @@ -84,11 +85,10 @@ required: - compatible - reg - clocks - - clock-frequency - enable-gpios - - vdddo-supply - vdda-supply - vddd-supply + - vdddo-supply - port unevaluatedProperties: false @@ -104,22 +104,25 @@ examples: camera-sensor@1a { compatible = "sony,imx214"; reg = <0x1a>; - vdddo-supply = <&pm8994_lvs1>; - vddd-supply = <&camera_vddd_1v12>; - vdda-supply = <&pm8994_l17>; - lens-focus = <&ad5820>; - enable-gpios = <&msmgpio 25 GPIO_ACTIVE_HIGH>; + clocks = <&camera_clk>; - clock-frequency = <24000000>; + assigned-clocks = <&camera_clk>; + assigned-clock-rates = <24000000>; + + enable-gpios = <&msmgpio 25 GPIO_ACTIVE_HIGH>; + + vdda-supply = <&pm8994_l17>; + vddd-supply = <&camera_vddd_1v12>; + vdddo-supply = <&pm8994_lvs1>; + + lens-focus = <&ad5820>; port { imx214_ep: endpoint { data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <480000000>; + link-frequencies = /bits/ 64 <600000000>; remote-endpoint = <&csiphy0_ep>; }; }; }; }; - -... diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml index 975c1d77c8e5..421b935b52bc 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml @@ -18,6 +18,8 @@ description: |- The camera module does not expose the model through registers, so the exact model needs to be specified. +$ref: /schemas/media/video-interface-devices.yaml# + properties: compatible: enum: @@ -81,7 +83,7 @@ required: - reg - port -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml index 2be30c5fdc83..4cba42ba7cf7 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml @@ -22,10 +22,14 @@ properties: - nxp,imx8qxp-jpgdec - nxp,imx8qxp-jpgenc - items: - - const: nxp,imx8qm-jpgdec + - enum: + - nxp,imx8qm-jpgdec + - nxp,imx95-jpgdec - const: nxp,imx8qxp-jpgdec - items: - - const: nxp,imx8qm-jpgenc + - enum: + - nxp,imx8qm-jpgenc + - nxp,imx95-jpgenc - const: nxp,imx8qxp-jpgenc reg: @@ -48,7 +52,7 @@ properties: description: List of phandle and PM domain specifier as documented in Documentation/devicetree/bindings/power/power_domain.txt - minItems: 2 # Wrapper and 1 slot + minItems: 1 # Wrapper and all slots maxItems: 5 # Wrapper and 4 slots required: @@ -58,6 +62,24 @@ required: - interrupts - power-domains +allOf: + - if: + properties: + compatible: + contains: + enum: + - nxp,imx95-jpgenc + - nxp,imx95-jpgdec + then: + properties: + power-domains: + maxItems: 1 + else: + properties: + power-domains: + minItems: 2 # Wrapper and 1 slot + + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml index 2a14e3b0e004..3389bab266a9 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml @@ -16,11 +16,19 @@ description: |- properties: compatible: - enum: - - fsl,imx8mq-mipi-csi2 + oneOf: + - enum: + - fsl,imx8mq-mipi-csi2 + - fsl,imx8qxp-mipi-csi2 + - items: + - const: fsl,imx8qm-mipi-csi2 + - const: fsl,imx8qxp-mipi-csi2 reg: - maxItems: 1 + items: + - description: MIPI CSI-2 RX host controller register. + - description: MIPI CSI-2 control and status register (csr). + minItems: 1 clocks: items: @@ -46,6 +54,7 @@ properties: - description: CORE_RESET reset register bit definition - description: PHY_REF_RESET reset register bit definition - description: ESC_RESET reset register bit definition + minItems: 1 fsl,mipi-phy-gpr: description: | @@ -113,9 +122,30 @@ required: - clock-names - power-domains - resets - - fsl,mipi-phy-gpr - ports +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qxp-mipi-csi2 + then: + properties: + reg: + minItems: 2 + resets: + maxItems: 1 + else: + properties: + reg: + maxItems: 1 + resets: + minItems: 3 + required: + - fsl,mipi-phy-gpr + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml index 113565cf2a99..b075341caafc 100644 --- a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml @@ -133,7 +133,7 @@ properties: CSI input ports. patternProperties: - "^port@[0-3]+$": + "^port@[0-3]$": $ref: /schemas/graph.yaml#/$defs/port-base unevaluatedProperties: false @@ -146,15 +146,16 @@ properties: unevaluatedProperties: false properties: - clock-lanes: - maxItems: 1 - data-lanes: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - - clock-lanes - data-lanes required: @@ -189,7 +190,7 @@ examples: #address-cells = <2>; #size-cells = <2>; - camss: isp@acb6000 { + camss: isp@acb7000 { compatible = "qcom,x1e80100-camss"; reg = <0 0x0acb7000 0 0x2000>, @@ -357,7 +358,6 @@ examples: port@0 { reg = <0>; csiphy_ep0: endpoint { - clock-lanes = <7>; data-lanes = <0 1>; remote-endpoint = <&sensor_ep>; }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index 7bf1266223e8..cf92dfe69637 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -30,6 +30,7 @@ properties: - renesas,r9a07g043u-fcpvd # RZ/G2UL - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L + - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index fcf7219b1f40..07a97dd87a5b 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -25,6 +25,7 @@ properties: - enum: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L + - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback diff --git a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml index 08b02ec16755..96b6c8938768 100644 --- a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml @@ -10,13 +10,15 @@ maintainers: - Heiko Stuebner description: |- - The Rockchip rk3399 has a stateless Video Decoder that can decodes H.264, - HEVC an VP9 streams. + Rockchip SoCs have variants of the same stateless Video Decoder that can + decodes H.264, HEVC, VP9 and AV1 streams, depending on the variant. properties: compatible: oneOf: - const: rockchip,rk3399-vdec + - const: rockchip,rk3576-vdec + - const: rockchip,rk3588-vdec - items: - enum: - rockchip,rk3228-vdec @@ -24,35 +26,72 @@ properties: - const: rockchip,rk3399-vdec reg: - maxItems: 1 + minItems: 1 + items: + - description: The function configuration registers base + - description: The link table configuration registers base + - description: The cache configuration registers base + + reg-names: + items: + - const: function + - const: link + - const: cache interrupts: maxItems: 1 clocks: + minItems: 4 items: - description: The Video Decoder AXI interface clock - description: The Video Decoder AHB interface clock - description: The Video Decoded CABAC clock - description: The Video Decoder core clock + - description: The Video decoder HEVC CABAC clock clock-names: + minItems: 4 items: - const: axi - const: ahb - const: cabac - const: core + - const: hevc_cabac assigned-clocks: true assigned-clock-rates: true + resets: + items: + - description: The Video Decoder AXI interface reset + - description: The Video Decoder AHB interface reset + - description: The Video Decoded CABAC reset + - description: The Video Decoder core reset + - description: The Video decoder HEVC CABAC reset + + reset-names: + items: + - const: axi + - const: ahb + - const: cabac + - const: core + - const: hevc_cabac + power-domains: maxItems: 1 iommus: maxItems: 1 + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + phandle to a reserved on-chip SRAM regions. + Some SoCs, like rk3588 provide on-chip SRAM to store temporary + buffers during decoding. + required: - compatible - reg @@ -61,6 +100,41 @@ required: - clock-names - power-domains +allOf: + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3576-vdec + - rockchip,rk3588-vdec + then: + properties: + reg: + minItems: 3 + reg-names: + minItems: 3 + clocks: + minItems: 5 + clock-names: + minItems: 5 + resets: + minItems: 5 + reset-names: + minItems: 5 + else: + properties: + reg: + maxItems: 1 + reg-names: false + clocks: + maxItems: 4 + clock-names: + maxItems: 4 + resets: false + reset-names: false + sram: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt deleted file mode 100644 index 22b77ee02f58..000000000000 --- a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt +++ /dev/null @@ -1,127 +0,0 @@ -* Device tree bindings for ARM PL172/PL175/PL176 MultiPort Memory Controller - -Required properties: - -- compatible: Must be "arm,primecell" and exactly one from - "arm,pl172", "arm,pl175" or "arm,pl176". - -- reg: Must contains offset/length value for controller. - -- #address-cells: Must be 2. The partition number has to be encoded in the - first address cell and it may accept values 0..N-1 - (N - total number of partitions). The second cell is the - offset into the partition. - -- #size-cells: Must be set to 1. - -- ranges: Must contain one or more chip select memory regions. - -- clocks: Must contain references to controller clocks. - -- clock-names: Must contain "mpmcclk" and "apb_pclk". - -- clock-ranges: Empty property indicating that child nodes can inherit - named clocks. Required only if clock tree data present - in device tree. - See clock-bindings.txt - -Child chip-select (cs) nodes contain the memory devices nodes connected to -such as NOR (e.g. cfi-flash) and NAND. - -Required child cs node properties: - -- #address-cells: Must be 2. - -- #size-cells: Must be 1. - -- ranges: Empty property indicating that child nodes can inherit - memory layout. - -- clock-ranges: Empty property indicating that child nodes can inherit - named clocks. Required only if clock tree data present - in device tree. - -- mpmc,cs: Chip select number. Indicates to the pl0172 driver - which chipselect is used for accessing the memory. - -- mpmc,memory-width: Width of the chip select memory. Must be equal to - either 8, 16 or 32. - -Optional child cs node config properties: - -- mpmc,async-page-mode: Enable asynchronous page mode. - -- mpmc,cs-active-high: Set chip select polarity to active high. - -- mpmc,byte-lane-low: Set byte lane state to low. - -- mpmc,extended-wait: Enable extended wait. - -- mpmc,buffer-enable: Enable write buffer, option is not supported by - PL175 and PL176 controllers. - -- mpmc,write-protect: Enable write protect. - -Optional child cs node timing properties: - -- mpmc,write-enable-delay: Delay from chip select assertion to write - enable (WE signal) in nano seconds. - -- mpmc,output-enable-delay: Delay from chip select assertion to output - enable (OE signal) in nano seconds. - -- mpmc,write-access-delay: Delay from chip select assertion to write - access in nano seconds. - -- mpmc,read-access-delay: Delay from chip select assertion to read - access in nano seconds. - -- mpmc,page-mode-read-delay: Delay for asynchronous page mode sequential - accesses in nano seconds. - -- mpmc,turn-round-delay: Delay between access to memory banks in nano - seconds. - -If any of the above timing parameters are absent, current parameter value will -be taken from the corresponding HW reg. - -Example for pl172 with nor flash on chip select 0 shown below. - -emc: memory-controller@40005000 { - compatible = "arm,pl172", "arm,primecell"; - reg = <0x40005000 0x1000>; - clocks = <&ccu1 CLK_CPU_EMCDIV>, <&ccu1 CLK_CPU_EMC>; - clock-names = "mpmcclk", "apb_pclk"; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x1c000000 0x1000000 - 1 0 0x1d000000 0x1000000 - 2 0 0x1e000000 0x1000000 - 3 0 0x1f000000 0x1000000>; - - cs0 { - #address-cells = <2>; - #size-cells = <1>; - ranges; - - mpmc,cs = <0>; - mpmc,memory-width = <16>; - mpmc,byte-lane-low; - mpmc,write-enable-delay = <0>; - mpmc,output-enable-delay = <0>; - mpmc,read-enable-delay = <70>; - mpmc,page-mode-read-delay = <70>; - - flash@0,0 { - compatible = "sst,sst39vf320", "cfi-flash"; - reg = <0 0 0x400000>; - bank-width = <2>; - #address-cells = <1>; - #size-cells = <1>; - partition@0 { - label = "data"; - reg = <0 0x400000>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml new file mode 100644 index 000000000000..c1b702669bd9 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml @@ -0,0 +1,222 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/arm,pl172.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM PL172/PL175/PL176 MultiPort Memory Controller + +maintainers: + - Frank Li + +# We need a select here so we don't match all nodes with 'arm,primecell' +select: + properties: + compatible: + contains: + enum: + - arm,pl172 + - arm,pl175 + - arm,pl176 + required: + - compatible + +properties: + compatible: + items: + - enum: + - arm,pl172 + - arm,pl175 + - arm,pl176 + - const: arm,primecell + + reg: + maxItems: 1 + + '#address-cells': + const: 2 + + '#size-cells': + const: 1 + + ranges: true + + clocks: + maxItems: 2 + + clock-names: + items: + - const: mpmcclk + - const: apb_pclk + + clock-ranges: true + + resets: + maxItems: 1 + +patternProperties: + "^cs[0-9]$": + type: object + additionalProperties: false + patternProperties: + "^flash@[0-9],[0-9a-f]+$": + type: object + $ref: /schemas/mtd/mtd-physmap.yaml# + unevaluatedProperties: false + + "^(gpio|sram)@[0-9],[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + '#address-cells': + const: 2 + + '#size-cells': + const: 1 + + ranges: true + + clocks: + maxItems: 2 + + clock-ranges: true + + mpmc,cs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Chip select number. Indicates to the pl0172 driver + which chipselect is used for accessing the memory. + + mpmc,memory-width: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 16, 32] + description: + Width of the chip select memory. Must be equal to either 8, 16 or 32. + + mpmc,async-page-mode: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable asynchronous page mode. + + mpmc,cs-active-high: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set chip select polarity to active high. + + mpmc,byte-lane-low: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set byte lane state to low. + + mpmc,extended-wait: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable extended wait. + + mpmc,buffer-enable: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable write buffer, option is not supported by + PL175 and PL176 controllers. + + mpmc,write-protect: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable write protect. + + mpmc,read-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to read + enable (RE signal) in nano seconds. + + mpmc,write-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to write + enable (WE signal) in nano seconds. + + mpmc,output-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to output + enable (OE signal) in nano seconds. + + mpmc,write-access-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to write + access in nano seconds. + + mpmc,read-access-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to read + access in nano seconds. + + mpmc,page-mode-read-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay for asynchronous page mode sequential + accesses in nano seconds. + + mpmc,turn-round-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay between access to memory banks in nano + seconds. + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + - ranges + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + memory-controller@40005000 { + compatible = "arm,pl172", "arm,primecell"; + reg = <0x40005000 0x1000>; + clocks = <&ccu1 CLK_CPU_EMCDIV>, <&ccu1 CLK_CPU_EMC>; + clock-names = "mpmcclk", "apb_pclk"; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x1c000000 0x1000000 + 1 0 0x1d000000 0x1000000 + 2 0 0x1e000000 0x1000000 + 3 0 0x1f000000 0x1000000>; + + cs0 { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + mpmc,cs = <0>; + mpmc,memory-width = <16>; + mpmc,byte-lane-low; + mpmc,write-enable-delay = <0>; + mpmc,output-enable-delay = <0>; + mpmc,read-enable-delay = <70>; + mpmc,page-mode-read-delay = <70>; + + flash@0,0 { + compatible = "sst,sst39vf320", "cfi-flash"; + reg = <0 0 0x400000>; + bank-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + partition@0 { + label = "data"; + reg = <0 0x400000>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml index 4b072c879b02..b935894bd4fc 100644 --- a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml @@ -11,25 +11,37 @@ maintainers: properties: compatible: - items: - - enum: - - brcm,brcmstb-memc-ddr-rev-b.1.x - - brcm,brcmstb-memc-ddr-rev-b.2.0 - - brcm,brcmstb-memc-ddr-rev-b.2.1 - - brcm,brcmstb-memc-ddr-rev-b.2.2 - - brcm,brcmstb-memc-ddr-rev-b.2.3 - - brcm,brcmstb-memc-ddr-rev-b.2.5 - - brcm,brcmstb-memc-ddr-rev-b.2.6 - - brcm,brcmstb-memc-ddr-rev-b.2.7 - - brcm,brcmstb-memc-ddr-rev-b.2.8 - - brcm,brcmstb-memc-ddr-rev-b.3.0 - - brcm,brcmstb-memc-ddr-rev-b.3.1 - - brcm,brcmstb-memc-ddr-rev-c.1.0 - - brcm,brcmstb-memc-ddr-rev-c.1.1 - - brcm,brcmstb-memc-ddr-rev-c.1.2 - - brcm,brcmstb-memc-ddr-rev-c.1.3 - - brcm,brcmstb-memc-ddr-rev-c.1.4 - - const: brcm,brcmstb-memc-ddr + oneOf: + - description: Revision > 2.1 controllers + items: + - enum: + - brcm,brcmstb-memc-ddr-rev-b.2.2 + - brcm,brcmstb-memc-ddr-rev-b.2.3 + - brcm,brcmstb-memc-ddr-rev-b.2.5 + - brcm,brcmstb-memc-ddr-rev-b.2.6 + - brcm,brcmstb-memc-ddr-rev-b.2.7 + - brcm,brcmstb-memc-ddr-rev-b.2.8 + - brcm,brcmstb-memc-ddr-rev-b.3.0 + - brcm,brcmstb-memc-ddr-rev-b.3.1 + - brcm,brcmstb-memc-ddr-rev-c.1.0 + - brcm,brcmstb-memc-ddr-rev-c.1.1 + - brcm,brcmstb-memc-ddr-rev-c.1.2 + - brcm,brcmstb-memc-ddr-rev-c.1.3 + - brcm,brcmstb-memc-ddr-rev-c.1.4 + - const: brcm,brcmstb-memc-ddr-rev-b.2.1 + - const: brcm,brcmstb-memc-ddr + - description: Revision 2.1 controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.2.1 + - const: brcm,brcmstb-memc-ddr + - description: Revision 2.0 controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.2.0 + - const: brcm,brcmstb-memc-ddr + - description: Revision 1.x controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.1.x + - const: brcm,brcmstb-memc-ddr reg: maxItems: 1 @@ -46,7 +58,9 @@ additionalProperties: false examples: - | memory-controller@9902000 { - compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", "brcm,brcmstb-memc-ddr"; + compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", + "brcm,brcmstb-memc-ddr-rev-b.2.1", + "brcm,brcmstb-memc-ddr"; reg = <0x9902000 0x600>; clock-frequency = <2133000000>; }; diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml index 935d63d181d9..b901f1b3e0fc 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml @@ -32,6 +32,7 @@ properties: - nvidia,tegra186-mc - nvidia,tegra194-mc - nvidia,tegra234-mc + - nvidia,tegra264-mc reg: minItems: 6 @@ -42,8 +43,12 @@ properties: maxItems: 18 interrupts: - items: - - description: MC general interrupt + minItems: 1 + maxItems: 8 + + interrupt-names: + minItems: 1 + maxItems: 8 "#address-cells": const: 2 @@ -74,6 +79,7 @@ patternProperties: - nvidia,tegra186-emc - nvidia,tegra194-emc - nvidia,tegra234-emc + - nvidia,tegra264-emc reg: minItems: 1 @@ -127,6 +133,15 @@ patternProperties: reg: minItems: 2 + - if: + properties: + compatible: + const: nvidia,tegra264-emc + then: + properties: + reg: + minItems: 2 + additionalProperties: false required: @@ -158,6 +173,12 @@ allOf: - const: ch2 - const: ch3 + interrupts: + items: + - description: MC general interrupt + + interrupt-names: false + - if: properties: compatible: @@ -189,6 +210,12 @@ allOf: - const: ch14 - const: ch15 + interrupts: + items: + - description: MC general interrupt + + interrupt-names: false + - if: properties: compatible: @@ -220,6 +247,59 @@ allOf: - const: ch14 - const: ch15 + interrupts: + items: + - description: MC general interrupt + + interrupt-names: false + + - if: + properties: + compatible: + const: nvidia,tegra264-mc + then: + properties: + reg: + minItems: 17 + maxItems: 17 + description: 17 memory controller channels + + reg-names: + items: + - const: broadcast + - 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 + + interrupts: + minItems: 8 + maxItems: 8 + description: One interrupt line for each MC component + + interrupt-names: + items: + - const: mcf + - const: hub1 + - const: hub2 + - const: hub3 + - const: hub4 + - const: hub5 + - const: sbs + - const: channel + additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml index 2bfe63ec62dc..7a84f5bb7284 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml @@ -23,7 +23,14 @@ allOf: properties: compatible: - const: renesas,r9a09g047-xspi # RZ/G3E + oneOf: + - const: renesas,r9a09g047-xspi # RZ/G3E + + - items: + - enum: + - renesas,r9a09g056-xspi # RZ/V2N + - renesas,r9a09g057-xspi # RZ/V2H(P) + - const: renesas,r9a09g047-xspi reg: items: diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml index ee2272f754a3..2d4ecee3f254 100644 --- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml +++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml @@ -15,14 +15,21 @@ description: properties: compatible: - items: - - enum: - - adi,adp5585-00 # Default - - adi,adp5585-01 # 11 GPIOs - - adi,adp5585-02 # No pull-up resistors by default on special pins - - adi,adp5585-03 # Alternate I2C address - - adi,adp5585-04 # Pull-down resistors on all pins by default - - const: adi,adp5585 + oneOf: + - items: + - enum: + - adi,adp5585-00 # Default + - adi,adp5585-01 # 11 GPIOs + - adi,adp5585-02 # No pull-up resistors by default on special pins + - adi,adp5585-03 # Alternate I2C address + - adi,adp5585-04 # Pull-down resistors on all pins by default + - const: adi,adp5585 + - items: + - enum: + - adi,adp5589-00 # Default + - adi,adp5589-01 # R4 defaulted to RESET1 output + - adi,adp5589-02 # Pull-down resistors by default on special pins + - const: adi,adp5589 reg: maxItems: 1 @@ -32,6 +39,9 @@ properties: vdd-supply: true + reset-gpios: + maxItems: 1 + gpio-controller: true '#gpio-cells': @@ -42,6 +52,84 @@ properties: "#pwm-cells": const: 3 + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + poll-interval: + enum: [10, 20, 30, 40] + default: 10 + + adi,keypad-pins: + description: Specifies the pins used for the keypad matrix. + $ref: /schemas/types.yaml#/definitions/uint32-array + + adi,unlock-events: + description: + Specifies a maximum of 2 events that can be used to unlock the keypad. + If this property is set, the keyboard will be locked and only unlocked + after these keys/gpis are pressed. The value 127 serves as a wildcard which + means any key can be used for unlocking. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 2 + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 + - const: 127 + + adi,unlock-trigger-sec: + description: + Defines the time in which the second unlock event must occur after the + first unlock event has occurred. + maximum: 7 + default: 0 + + adi,reset1-events: + description: + Defines the trigger events (key/gpi presses) that can generate reset + conditions one the reset1 block. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 3 + + adi,reset2-events: + description: + Defines the trigger events (key/gpi presses) that can generate reset + conditions one the reset2 block. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 2 + + adi,reset1-active-high: + description: Sets the reset1 signal as active high. + type: boolean + + adi,reset2-active-high: + description: Sets the reset2 signal as active high. + type: boolean + + adi,rst-passthrough-enable: + description: Allows the RST pin to override (OR with) the reset1 signal. + type: boolean + + adi,reset-trigger-ms: + description: + Defines the length of time that the reset events must be active before a + reset signal is generated. All events must be active at the same time for + the same duration. + enum: [0, 1000, 1500, 2000, 2500, 3000, 3500, 4000] + default: 0 + + adi,reset-pulse-width-us: + description: Defines the pulse width of the reset signals. + enum: [500, 1000, 2000, 10000] + default: 500 + patternProperties: "-hog(-[0-9]+)?$": type: object @@ -49,14 +137,28 @@ patternProperties: required: - gpio-hog +dependencies: + linux,keymap: + - adi,keypad-pins + - interrupts + interrupt-controller: + - interrupts + adi,unlock-trigger-sec: + - adi,unlock-events + adi,reset1-active-high: + - adi,reset1-events + adi,rst-passtrough-enable: + - adi,reset1-events + adi,reset2-active-high: + - adi,reset2-events + required: - compatible - reg - - gpio-controller - - "#gpio-cells" - - "#pwm-cells" allOf: + - $ref: /schemas/input/matrix-keymap.yaml# + - $ref: /schemas/input/input.yaml# - if: properties: compatible: @@ -64,9 +166,60 @@ allOf: const: adi,adp5585-01 then: properties: + adi,unlock-events: false + adi,unlock-trigger-sec: false gpio-reserved-ranges: false - else: + reset-gpios: false + adi,keypad-pins: + minItems: 2 + maxItems: 11 + items: + minimum: 0 + maximum: 10 + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 30 + - minimum: 37 + maximum: 47 + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 30 + - minimum: 37 + maximum: 47 + - if: properties: + compatible: + contains: + enum: + - adi,adp5585-00 + - adi,adp5585-02 + - adi,adp5585-03 + - adi,adp5585-04 + then: + properties: + adi,unlock-events: false + adi,unlock-trigger-sec: false + adi,keypad-pins: + minItems: 2 + maxItems: 10 + items: + enum: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10] + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 25 + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47] + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 25 + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47] gpio-reserved-ranges: maxItems: 1 items: @@ -74,10 +227,44 @@ allOf: - const: 5 - const: 1 -additionalProperties: false + - if: + properties: + compatible: + contains: + enum: + - adi,adp5589-00 + - adi,adp5589-01 + - adi,adp5589-02 + then: + properties: + gpio-reserved-ranges: false + adi,keypad-pins: + minItems: 2 + maxItems: 19 + items: + minimum: 0 + maximum: 18 + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 + +unevaluatedProperties: false examples: - | + #include + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -93,6 +280,33 @@ examples: gpio-reserved-ranges = <5 1>; #pwm-cells = <3>; + + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + + adi,reset1-events = <1 43>; + adi,reset2-events = <2 3>; + adi,reset-trigger-ms = <2000>; + + /* + * col0, col1, col2 + * row0, row1, row2 + */ + adi,keypad-pins = <0 1 2 6 7 8>; + + linux,keymap = < + MATRIX_KEY(0x00, 0x00, KEY_1) + MATRIX_KEY(0x00, 0x01, KEY_2) + MATRIX_KEY(0x00, 0x02, KEY_3) + + MATRIX_KEY(0x01, 0x00, KEY_A) + MATRIX_KEY(0x01, 0x01, KEY_B) + MATRIX_KEY(0x01, 0x02, KEY_C) + + MATRIX_KEY(0x02, 0x00, BTN_1) + MATRIX_KEY(0x02, 0x01, BTN_2) + MATRIX_KEY(0x02, 0x02, BTN_3) + >; }; }; diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml new file mode 100644 index 000000000000..8a10e270d421 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/apple,smc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Mac System Management Controller + +maintainers: + - Sven Peter + +description: + Apple Mac System Management Controller implements various functions + such as GPIO, RTC, power, reboot. + +properties: + compatible: + items: + - enum: + - apple,t6000-smc + - apple,t8103-smc + - apple,t8112-smc + - const: apple,smc + + reg: + items: + - description: SMC area + - description: SRAM area + + reg-names: + items: + - const: smc + - const: sram + + mboxes: + maxItems: 1 + + gpio: + $ref: /schemas/gpio/apple,smc-gpio.yaml + + reboot: + $ref: /schemas/power/reset/apple,smc-reboot.yaml + +additionalProperties: false + +required: + - compatible + - reg + - reg-names + - mboxes + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + smc@23e400000 { + compatible = "apple,t8103-smc", "apple,smc"; + reg = <0x2 0x3e400000 0x0 0x4000>, + <0x2 0x3fe00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + compatible = "apple,smc-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + reboot { + compatible = "apple,smc-reboot"; + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml b/Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml deleted file mode 100644 index 20067002cc4a..000000000000 --- a/Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml +++ /dev/null @@ -1,192 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/mfd/fsl,imx8qxp-csr.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Freescale i.MX8qm/qxp Control and Status Registers Module - -maintainers: - - Liu Ying - -description: | - As a system controller, the Freescale i.MX8qm/qxp Control and Status - Registers(CSR) module represents a set of miscellaneous registers of a - specific subsystem. It may provide control and/or status report interfaces - to a mix of standalone hardware devices within that subsystem. One typical - use-case is for some other nodes to acquire a reference to the syscon node - by phandle, and the other typical use-case is that the operating system - should consider all subnodes of the CSR module as separate child devices. - -properties: - $nodename: - pattern: "^syscon@[0-9a-f]+$" - - compatible: - items: - - enum: - - fsl,imx8qxp-mipi-lvds-csr - - fsl,imx8qm-lvds-csr - - const: syscon - - const: simple-mfd - - reg: - maxItems: 1 - - clocks: - maxItems: 1 - - clock-names: - const: ipg - -patternProperties: - "^(ldb|phy|pxl2dpi)$": - type: object - description: The possible child devices of the CSR module. - -required: - - compatible - - reg - - clocks - - clock-names - -allOf: - - if: - properties: - compatible: - contains: - const: fsl,imx8qxp-mipi-lvds-csr - then: - required: - - pxl2dpi - - ldb - - - if: - properties: - compatible: - contains: - const: fsl,imx8qm-lvds-csr - then: - required: - - phy - - ldb - -additionalProperties: false - -examples: - - | - #include - #include - mipi_lvds_0_csr: syscon@56221000 { - compatible = "fsl,imx8qxp-mipi-lvds-csr", "syscon", "simple-mfd"; - reg = <0x56221000 0x1000>; - clocks = <&mipi_lvds_0_di_mipi_lvds_regs_lpcg IMX_LPCG_CLK_4>; - clock-names = "ipg"; - - mipi_lvds_0_pxl2dpi: pxl2dpi { - compatible = "fsl,imx8qxp-pxl2dpi"; - fsl,sc-resource = ; - power-domains = <&pd IMX_SC_R_MIPI_0>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - - mipi_lvds_0_pxl2dpi_dc0_pixel_link0: endpoint@0 { - reg = <0>; - remote-endpoint = <&dc0_pixel_link0_mipi_lvds_0_pxl2dpi>; - }; - - mipi_lvds_0_pxl2dpi_dc0_pixel_link1: endpoint@1 { - reg = <1>; - remote-endpoint = <&dc0_pixel_link1_mipi_lvds_0_pxl2dpi>; - }; - }; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - - mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0: endpoint@0 { - reg = <0>; - remote-endpoint = <&mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi>; - }; - - mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1: endpoint@1 { - reg = <1>; - remote-endpoint = <&mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi>; - }; - }; - }; - }; - - mipi_lvds_0_ldb: ldb { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,imx8qxp-ldb"; - clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_MISC2>, - <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_BYPASS>; - clock-names = "pixel", "bypass"; - power-domains = <&pd IMX_SC_R_LVDS_0>; - - channel@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - phys = <&mipi_lvds_0_phy>; - phy-names = "lvds_phy"; - - port@0 { - reg = <0>; - - mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi: endpoint { - remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0>; - }; - }; - - port@1 { - reg = <1>; - - /* ... */ - }; - }; - - channel@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - phys = <&mipi_lvds_0_phy>; - phy-names = "lvds_phy"; - - port@0 { - reg = <0>; - - mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi: endpoint { - remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1>; - }; - }; - - port@1 { - reg = <1>; - - /* ... */ - }; - }; - }; - }; - - mipi_lvds_0_phy: phy@56228300 { - compatible = "fsl,imx8qxp-mipi-dphy"; - reg = <0x56228300 0x100>; - clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_PHY>; - clock-names = "phy_ref"; - #phy-cells = <0>; - fsl,syscon = <&mipi_lvds_0_csr>; - power-domains = <&pd IMX_SC_R_MIPI_0>; - }; diff --git a/Documentation/devicetree/bindings/mfd/lp3943.txt b/Documentation/devicetree/bindings/mfd/lp3943.txt index e8591d6b11b4..ca5324ed0df4 100644 --- a/Documentation/devicetree/bindings/mfd/lp3943.txt +++ b/Documentation/devicetree/bindings/mfd/lp3943.txt @@ -7,7 +7,7 @@ Required properties: LP3943 consists of two sub-devices, lp3943-gpio and lp3943-pwm. For the LP3943 GPIO properties please refer to: -Documentation/devicetree/bindings/gpio/gpio-lp3943.txt +Documentation/devicetree/bindings/gpio/trivial-gpio.yaml For the LP3943 PWM properties please refer to: Documentation/devicetree/bindings/pwm/pwm-lp3943.txt diff --git a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt index f00827c9b67f..18c3fc26ca93 100644 --- a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt +++ b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt @@ -19,7 +19,7 @@ which are described in the following files: - Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml - Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml - Documentation/devicetree/bindings/regulator/cpcap-regulator.txt -- Documentation/devicetree/bindings/phy/phy-cpcap-usb.txt +- Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml - Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt - Documentation/devicetree/bindings/rtc/cpcap-rtc.txt - Documentation/devicetree/bindings/leds/leds-cpcap.txt diff --git a/Documentation/devicetree/bindings/mfd/mxs-lradc.txt b/Documentation/devicetree/bindings/mfd/mxs-lradc.txt deleted file mode 100644 index 755cbef0647d..000000000000 --- a/Documentation/devicetree/bindings/mfd/mxs-lradc.txt +++ /dev/null @@ -1,45 +0,0 @@ -* Freescale MXS LRADC device driver - -Required properties: -- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc" - for i.MX28 SoC -- reg: Address and length of the register set for the device -- interrupts: Should contain the LRADC interrupts - -Optional properties: -- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen - to LRADC. Valid value is either 4 or 5. If this - property is not present, then the touchscreen is - disabled. 5 wires is valid for i.MX28 SoC only. -- fsl,ave-ctrl: number of samples per direction to calculate an average value. - Allowed value is 1 ... 32, default is 4 -- fsl,ave-delay: delay between consecutive samples. Allowed value is - 2 ... 2048. It is used if 'fsl,ave-ctrl' > 1, counts at - 2 kHz and its default is 2 (= 1 ms) -- fsl,settling: delay between plate switch to next sample. Allowed value is - 1 ... 2047. It counts at 2 kHz and its default is - 10 (= 5 ms) - -Example for i.MX23 SoC: - - lradc@80050000 { - compatible = "fsl,imx23-lradc"; - reg = <0x80050000 0x2000>; - interrupts = <36 37 38 39 40 41 42 43 44>; - fsl,lradc-touchscreen-wires = <4>; - fsl,ave-ctrl = <4>; - fsl,ave-delay = <2>; - fsl,settling = <10>; - }; - -Example for i.MX28 SoC: - - lradc@80050000 { - compatible = "fsl,imx28-lradc"; - reg = <0x80050000 0x2000>; - interrupts = <10 14 15 16 17 18 19 20 21 22 23 24 25>; - fsl,lradc-touchscreen-wires = <5>; - fsl,ave-ctrl = <4>; - fsl,ave-delay = <2>; - fsl,settling = <10>; - }; diff --git a/Documentation/devicetree/bindings/mfd/mxs-lradc.yaml b/Documentation/devicetree/bindings/mfd/mxs-lradc.yaml new file mode 100644 index 000000000000..782b2f4005a0 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/mxs-lradc.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/mxs-lradc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MXS Low-Resolution ADC (LRADC) + +maintainers: + - Dario Binacchi + +description: + The LRADC provides 16 physical channels of 12-bit resolution for + analog-to-digital conversion and includes an integrated 4-wire/5-wire + touchscreen controller. + +properties: + compatible: + items: + - enum: + - fsl,imx23-lradc + - fsl,imx28-lradc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + + interrupts: + minItems: 9 + maxItems: 13 + + fsl,lradc-touchscreen-wires: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [4, 5] + description: > + Number of wires used to connect the touchscreen to LRADC. + + If this property is not present, then the touchscreen is disabled. + + fsl,ave-ctrl: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + default: 4 + description: + Number of samples per direction to calculate an average value. + + fsl,ave-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 2048 + default: 2 + description: > + Delay between consecutive samples. + + It is used if 'fsl,ave-ctrl' > 1, counts at 2 kHz and its default value (2) + is 1 ms. + + fsl,settling: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 2047 + default: 10 + description: > + Delay between plate switch to next sample. + + It counts at 2 kHz and its default (10) is 5 ms. + + "#io-channel-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +if: + properties: + compatible: + contains: + enum: + - fsl,imx23-lradc +then: + properties: + interrupts: + items: + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + - description: channel 4 + - description: channel 5 + - description: touchscreen + - description: channel 6 + - description: channel 7 + fsl,lradc-touchscreen-wires: + const: 4 +else: + properties: + interrupts: + items: + - description: threshold 0 + - description: threshold 1 + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + - description: channel 4 + - description: channel 5 + - description: button 0 + - description: button 1 + - description: touchscreen + - description: channel 6 + - description: channel 7 + +additionalProperties: false + +examples: + - | + lradc@80050000 { + compatible = "fsl,imx23-lradc"; + reg = <0x80050000 0x2000>; + interrupts = <36>, <37>, <38>, <39>, <40>, + <41>, <42>, <43>, <44>; + clocks = <&clks 26>; + #io-channel-cells = <1>; + fsl,lradc-touchscreen-wires = <4>; + fsl,ave-ctrl = <4>; + fsl,ave-delay = <2>; + fsl,settling = <10>; + }; diff --git a/Documentation/devicetree/bindings/mfd/nxp,lpc1850-creg.yaml b/Documentation/devicetree/bindings/mfd/nxp,lpc1850-creg.yaml new file mode 100644 index 000000000000..89b4892e9ca7 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/nxp,lpc1850-creg.yaml @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/nxp,lpc1850-creg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The NXP LPC18xx/43xx CREG (Configuration Registers) block + +maintainers: + - Frank Li + +properties: + compatible: + items: + - enum: + - nxp,lpc1850-creg + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + clock-controller: + type: object + description: + The NXP LPC18xx/43xx CREG (Configuration Registers) block contains + control registers for two low speed clocks. One of the clocks is a + 32 kHz oscillator driver with power up/down and clock gating. Next + is a fixed divider that creates a 1 kHz clock from the 32 kHz osc. + + These clocks are used by the RTC and the Event Router peripherals. + The 32 kHz can also be routed to other peripherals to enable low + power modes. + + properties: + compatible: + const: nxp,lpc1850-creg-clk + + clocks: + maxItems: 1 + + '#clock-cells': + const: 1 + description: | + 0 1 kHz clock + 1 32 kHz Oscillator + + required: + - compatible + - clocks + - '#clock-cells' + + additionalProperties: false + + phy: + type: object + description: the internal USB OTG PHY in NXP LPC18xx and LPC43xx SoCs + properties: + compatible: + const: nxp,lpc1850-usb-otg-phy + + clocks: + maxItems: 1 + + '#phy-cells': + const: 0 + + required: + - compatible + - clocks + - '#phy-cells' + + additionalProperties: false + + dma-mux: + type: object + description: NXP LPC18xx/43xx DMA MUX (DMA request router) + properties: + compatible: + const: nxp,lpc1850-dmamux + + '#dma-cells': + const: 3 + description: | + Should be set to <3>. + * 1st cell contain the master dma request signal + * 2nd cell contain the mux value (0-3) for the peripheral + * 3rd cell contain either 1 or 2 depending on the AHB master used. + + dma-requests: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + description: Number of DMA requests the controller can handle + + dma-masters: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle pointing to the DMA controller + + required: + - compatible + - '#dma-cells' + - dma-masters + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - resets + +additionalProperties: false + +examples: + - | + #include + + syscon@40043000 { + compatible = "nxp,lpc1850-creg", "syscon", "simple-mfd"; + reg = <0x40043000 0x1000>; + clocks = <&ccu1 CLK_CPU_CREG>; + resets = <&rgu 5>; + + clock-controller { + compatible = "nxp,lpc1850-creg-clk"; + clocks = <&xtal32>; + #clock-cells = <1>; + }; + + phy { + compatible = "nxp,lpc1850-usb-otg-phy"; + clocks = <&ccu1 CLK_USB0>; + #phy-cells = <0>; + }; + + dma-mux { + compatible = "nxp,lpc1850-dmamux"; + #dma-cells = <3>; + dma-requests = <64>; + dma-masters = <&dmac>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/rockchip,rk806.yaml b/Documentation/devicetree/bindings/mfd/rockchip,rk806.yaml index 3c2b06629b75..eb5bca31948e 100644 --- a/Documentation/devicetree/bindings/mfd/rockchip,rk806.yaml +++ b/Documentation/devicetree/bindings/mfd/rockchip,rk806.yaml @@ -31,6 +31,27 @@ properties: system-power-controller: true + rockchip,reset-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + description: + Mode to use when a reset of the PMIC is triggered. + + The reset can be triggered either programmatically, via one of + the PWRCTRL pins (provided additional configuration) or + asserting RESETB pin low. + + The following modes are supported + + - 0; restart PMU, + - 1; reset all power off reset registers and force state to + switch to ACTIVE mode, + - 2; same as mode 1 and also pull RESETB pin down for 5ms, + + For example, some hardware may require a full restart (mode 0) + in order to function properly as regulators are shortly + interrupted in this mode. + vcc1-supply: description: The input supply for dcdc-reg1. diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml index d6b9e2914796..31d544a9c05c 100644 --- a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml +++ b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml @@ -81,6 +81,9 @@ allOf: samsung,s2mps11-acokb-ground: false samsung,s2mps11-wrstbi-ground: false + # oneOf is required, because dtschema's fixups.py doesn't handle this + # nesting here. Its special treatment to allow either interrupt property + # when only one is specified in the binding works at the top level only. oneOf: - required: [interrupts] - required: [interrupts-extended] diff --git a/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml new file mode 100644 index 000000000000..a2668fc30a7b --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml @@ -0,0 +1,318 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ti,tps65910.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI TPS65910 Power Management Integrated Circuit + +maintainers: + - Shree Ramamoorthy + +description: + TPS65910 device is a Power Management IC that provides 3 step-down converters, + 1 stepup converter, and 8 LDOs. The device contains an embedded power controller (EPC), + 1 GPIO, and an RTC. + +properties: + compatible: + enum: + - ti,tps65910 + - ti,tps65911 + + reg: + description: I2C slave address + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + description: | + The first cell is the GPIO number. + The second cell is used to specify additional options . + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + description: Specifies the IRQ number and flags + const: 2 + + ti,vmbch-threshold: + description: | + (TPS65911) Main battery charged threshold comparator. + See VMBCH_VSEL in TPS65910 datasheet. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + ti,vmbch2-threshold: + description: | + (TPS65911) Main battery discharged threshold comparator. + See VMBCH_VSEL in TPS65910 datasheet. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + ti,en-ck32k-xtal: + type: boolean + description: Enable external 32-kHz crystal oscillator. + + ti,en-gpio-sleep: + description: | + Enable sleep control for gpios. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 9 + maxItems: 9 + items: + minimum: 0 + maximum: 1 + + ti,system-power-controller: + type: boolean + description: Identify whether or not this pmic controls the system power + + ti,sleep-enable: + type: boolean + description: Enable SLEEP state. + + ti,sleep-keep-therm: + type: boolean + description: Keep thermal monitoring on in sleep state. + + ti,sleep-keep-ck32k: + type: boolean + description: Keep the 32KHz clock output on in sleep state. + + ti,sleep-keep-hsclk: + type: boolean + description: Keep high speed internal clock on in sleep state. + + regulators: + type: object + additionalProperties: false + description: List of regulators provided by this controller. + + patternProperties: + "^(vrtc|vio|vpll|vdac|vmmc|vbb|vddctrl)$": + type: object + $ref: /schemas/regulator/regulator.yaml# + properties: + ti,regulator-ext-sleep-control: + description: | + Enable external sleep control through external inputs: + [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)]. + If this property is not defined, it defaults to 0 (not enabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 4, 8] + unevaluatedProperties: false + + "^(vdd[1-3]|vaux([1-2]|33)|vdig[1-2])$": + type: object + $ref: /schemas/regulator/regulator.yaml# + properties: + ti,regulator-ext-sleep-control: + description: | + Enable external sleep control through external inputs: + [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)]. + If this property is not defined, it defaults to 0 (not enabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 4, 8] + unevaluatedProperties: false + + "^ldo[1-8]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + properties: + ti,regulator-ext-sleep-control: + description: | + Enable external sleep control through external inputs: + [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)]. + If this property is not defined, it defaults to 0 (not enabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 4, 8] + unevaluatedProperties: false + +patternProperties: + "^(vcc(io|[1-7])-supply)$": + description: | + Input voltage supply phandle for regulators. + These entries are required if PMIC regulators are enabled, or else it + can cause the regulator registration to fail. + + If some input supply is powered through battery or always-on supply, then + it is also required to have these parameters with the proper node handle for always-on + power supply. + tps65910: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: VAUX33 and VMMC input. + vcc4-supply: VAUX1 and VAUX2 input. + vcc5-supply: VPLL and VDAC input. + vcc6-supply: VDIG1 and VDIG2 input. + vcc7-supply: VRTC and VBB input. + vccio-supply: VIO input. + tps65911: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: LDO6, LDO7 and LDO8 input. + vcc4-supply: LDO5 input. + vcc5-supply: LDO3 and LDO4 input. + vcc6-supply: LDO1 and LDO2 input. + vcc7-supply: VRTC input. + vccio-supply: VIO input. + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - '#interrupt-cells' + - gpio-controller + - '#gpio-cells' + - regulators + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + enum: + - ti,tps65910 + then: + properties: + regulators: + patternProperties: + "^(ldo[1-8]|vddctrl)$": false + - if: + properties: + compatible: + contains: + enum: + - ti,tps65911 + then: + properties: + regulators: + patternProperties: + "^(vdd3|vaux([1-2]|33)|vdig[1-2])$": false + "^(vpll|vdac|vmmc|vbb)$": false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic: tps65910@2d { + compatible = "ti,tps65910"; + reg = <0x2d>; + interrupt-parent = <&intc>; + interrupts = < 0 118 0x04 >; + + #gpio-cells = <2>; + gpio-controller; + + #interrupt-cells = <2>; + interrupt-controller; + + ti,system-power-controller; + + ti,vmbch-threshold = <0>; + ti,vmbch2-threshold = <0>; + ti,en-ck32k-xtal; + ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + + vcc1-supply = <®_parent>; + vcc2-supply = <&some_reg>; + vcc3-supply = <&vbat>; + vcc4-supply = <&vbat>; + vcc5-supply = <&vbat>; + vcc6-supply = <&vbat>; + vcc7-supply = <&vbat>; + vccio-supply = <&vbat>; + + regulators { + vio_reg: vio { + regulator-name = "vio"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + vdd1_reg: vdd1 { + regulator-name = "vdd1"; + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <0>; + }; + vdd2_reg: vdd2 { + regulator-name = "vdd2"; + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + }; + vdd3_reg: vdd3 { + regulator-name = "vdd3"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + vdig1_reg: vdig1 { + regulator-name = "vdig1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <2700000>; + regulator-always-on; + }; + vdig2_reg: vdig2 { + regulator-name = "vdig2"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vpll_reg: vpll { + regulator-name = "vpll"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + vdac_reg: vdac { + regulator-name = "vdac"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + }; + vaux1_reg: vaux1 { + regulator-name = "vaux1"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + }; + vaux2_reg: vaux2 { + regulator-name = "vaux2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vaux33_reg: vaux33 { + regulator-name = "vaux33"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vmmc_reg: vmmc { + regulator-name = "vmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml index 6341b6070366..a48cb00afe43 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml @@ -22,6 +22,7 @@ properties: - ti,tps6593-q1 - ti,tps6594-q1 - ti,tps65224-q1 + - ti,tps652g1 reg: description: I2C slave address or SPI chip select number. diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt deleted file mode 100644 index a5ced46bbde9..000000000000 --- a/Documentation/devicetree/bindings/mfd/tps65910.txt +++ /dev/null @@ -1,205 +0,0 @@ -TPS65910 Power Management Integrated Circuit - -Required properties: -- compatible: "ti,tps65910" or "ti,tps65911" -- reg: I2C slave address -- interrupts: the interrupt outputs of the controller -- #gpio-cells: number of cells to describe a GPIO, this should be 2. - The first cell is the GPIO number. - The second cell is used to specify additional options . -- gpio-controller: mark the device as a GPIO controller -- #interrupt-cells: the number of cells to describe an IRQ, this should be 2. - The first cell is the IRQ number. - The second cell is the flags, encoded as the trigger masks from - Documentation/devicetree/bindings/interrupt-controller/interrupts.txt -- regulators: This is the list of child nodes that specify the regulator - initialization data for defined regulators. Not all regulators for the given - device need to be present. The definition for each of these nodes is defined - using the standard binding for regulators found at - Documentation/devicetree/bindings/regulator/regulator.txt. - The regulator is matched with the regulator-compatible. - - The valid regulator-compatible values are: - tps65910: vrtc, vio, vdd1, vdd2, vdd3, vdig1, vdig2, vpll, vdac, vaux1, - vaux2, vaux33, vmmc, vbb - tps65911: vrtc, vio, vdd1, vdd2, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5, - ldo6, ldo7, ldo8 - -- xxx-supply: Input voltage supply regulator. - These entries are required if regulators are enabled for a device. Missing these - properties can cause the regulator registration to fail. - If some of input supply is powered through battery or always-on supply then - also it is require to have these parameters with proper node handle of always - on power supply. - tps65910: - vcc1-supply: VDD1 input. - vcc2-supply: VDD2 input. - vcc3-supply: VAUX33 and VMMC input. - vcc4-supply: VAUX1 and VAUX2 input. - vcc5-supply: VPLL and VDAC input. - vcc6-supply: VDIG1 and VDIG2 input. - vcc7-supply: VRTC and VBB input. - vccio-supply: VIO input. - tps65911: - vcc1-supply: VDD1 input. - vcc2-supply: VDD2 input. - vcc3-supply: LDO6, LDO7 and LDO8 input. - vcc4-supply: LDO5 input. - vcc5-supply: LDO3 and LDO4 input. - vcc6-supply: LDO1 and LDO2 input. - vcc7-supply: VRTC input. - vccio-supply: VIO input. - -Optional properties: -- ti,vmbch-threshold: (tps65911) main battery charged threshold - comparator. (see VMBCH_VSEL in TPS65910 datasheet) -- ti,vmbch2-threshold: (tps65911) main battery discharged threshold - comparator. (see VMBCH_VSEL in TPS65910 datasheet) -- ti,en-ck32k-xtal: enable external 32-kHz crystal oscillator (see CK32K_CTRL - in TPS6591X datasheet) -- ti,en-gpio-sleep: enable sleep control for gpios - There should be 9 entries here, one for each gpio. -- ti,system-power-controller: Telling whether or not this pmic is controlling - the system power. -- ti,sleep-enable: Enable SLEEP state. -- ti,sleep-keep-therm: Keep thermal monitoring on in sleep state. -- ti,sleep-keep-ck32k: Keep the 32KHz clock output on in sleep state. -- ti,sleep-keep-hsclk: Keep high speed internal clock on in sleep state. - -Regulator Optional properties: -- ti,regulator-ext-sleep-control: enable external sleep - control through external inputs [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)] - If this property is not defined, it defaults to 0 (not enabled). - -Example: - - pmu: tps65910@d2 { - compatible = "ti,tps65910"; - reg = <0xd2>; - interrupt-parent = <&intc>; - interrupts = < 0 118 0x04 >; - - #gpio-cells = <2>; - gpio-controller; - - #interrupt-cells = <2>; - interrupt-controller; - - ti,system-power-controller; - - ti,vmbch-threshold = 0; - ti,vmbch2-threshold = 0; - ti,en-ck32k-xtal; - ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; - - vcc1-supply = <®_parent>; - vcc2-supply = <&some_reg>; - vcc3-supply = <...>; - vcc4-supply = <...>; - vcc5-supply = <...>; - vcc6-supply = <...>; - vcc7-supply = <...>; - vccio-supply = <...>; - - regulators { - #address-cells = <1>; - #size-cells = <0>; - - vdd1_reg: regulator@0 { - regulator-compatible = "vdd1"; - reg = <0>; - regulator-min-microvolt = < 600000>; - regulator-max-microvolt = <1500000>; - regulator-always-on; - regulator-boot-on; - ti,regulator-ext-sleep-control = <0>; - }; - vdd2_reg: regulator@1 { - regulator-compatible = "vdd2"; - reg = <1>; - regulator-min-microvolt = < 600000>; - regulator-max-microvolt = <1500000>; - regulator-always-on; - regulator-boot-on; - ti,regulator-ext-sleep-control = <4>; - }; - vddctrl_reg: regulator@2 { - regulator-compatible = "vddctrl"; - reg = <2>; - regulator-min-microvolt = < 600000>; - regulator-max-microvolt = <1400000>; - regulator-always-on; - regulator-boot-on; - ti,regulator-ext-sleep-control = <0>; - }; - vio_reg: regulator@3 { - regulator-compatible = "vio"; - reg = <3>; - regulator-min-microvolt = <1500000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - regulator-boot-on; - ti,regulator-ext-sleep-control = <1>; - }; - ldo1_reg: regulator@4 { - regulator-compatible = "ldo1"; - reg = <4>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3300000>; - ti,regulator-ext-sleep-control = <0>; - }; - ldo2_reg: regulator@5 { - regulator-compatible = "ldo2"; - reg = <5>; - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1050000>; - ti,regulator-ext-sleep-control = <0>; - }; - ldo3_reg: regulator@6 { - regulator-compatible = "ldo3"; - reg = <6>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3300000>; - ti,regulator-ext-sleep-control = <0>; - }; - ldo4_reg: regulator@7 { - regulator-compatible = "ldo4"; - reg = <7>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - ti,regulator-ext-sleep-control = <0>; - }; - ldo5_reg: regulator@8 { - regulator-compatible = "ldo5"; - reg = <8>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3300000>; - ti,regulator-ext-sleep-control = <0>; - }; - ldo6_reg: regulator@9 { - regulator-compatible = "ldo6"; - reg = <9>; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - ti,regulator-ext-sleep-control = <0>; - }; - ldo7_reg: regulator@10 { - regulator-compatible = "ldo7"; - reg = <10>; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-always-on; - regulator-boot-on; - ti,regulator-ext-sleep-control = <1>; - }; - ldo8_reg: regulator@11 { - regulator-compatible = "ldo8"; - reg = <11>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - ti,regulator-ext-sleep-control = <1>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/mips/brcm/soc.yaml b/Documentation/devicetree/bindings/mips/brcm/soc.yaml index 0cc634482a6a..461a8c063313 100644 --- a/Documentation/devicetree/bindings/mips/brcm/soc.yaml +++ b/Documentation/devicetree/bindings/mips/brcm/soc.yaml @@ -92,29 +92,29 @@ additionalProperties: true examples: - | - / { - compatible = "brcm,bcm3368"; - #address-cells = <1>; - #size-cells = <1>; - model = "Broadcom 3368"; + / { + compatible = "brcm,bcm3368"; + #address-cells = <1>; + #size-cells = <1>; + model = "Broadcom 3368"; - cpus { - #address-cells = <1>; - #size-cells = <0>; + cpus { + #address-cells = <1>; + #size-cells = <0>; - mips-hpt-frequency = <150000000>; + mips-hpt-frequency = <150000000>; - cpu@0 { - compatible = "brcm,bmips4350"; - device_type = "cpu"; - reg = <0>; - }; + cpu@0 { + compatible = "brcm,bmips4350"; + device_type = "cpu"; + reg = <0>; + }; - cpu@1 { - compatible = "brcm,bmips4350"; - device_type = "cpu"; - reg = <1>; - }; - }; - }; + cpu@1 { + compatible = "brcm,bmips4350"; + device_type = "cpu"; + reg = <1>; + }; + }; + }; ... diff --git a/Documentation/devicetree/bindings/misc/intel,ixp4xx-ahb-queue-manager.yaml b/Documentation/devicetree/bindings/misc/intel,ixp4xx-ahb-queue-manager.yaml index 36a9dbdf3f03..aab89946b04f 100644 --- a/Documentation/devicetree/bindings/misc/intel,ixp4xx-ahb-queue-manager.yaml +++ b/Documentation/devicetree/bindings/misc/intel,ixp4xx-ahb-queue-manager.yaml @@ -45,7 +45,7 @@ examples: #include qmgr: queue-manager@60000000 { - compatible = "intel,ixp4xx-ahb-queue-manager"; - reg = <0x60000000 0x4000>; - interrupts = <3 IRQ_TYPE_LEVEL_HIGH>, <4 IRQ_TYPE_LEVEL_HIGH>; + compatible = "intel,ixp4xx-ahb-queue-manager"; + reg = <0x60000000 0x4000>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>, <4 IRQ_TYPE_LEVEL_HIGH>; }; diff --git a/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.yaml b/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.yaml index cacb845868f4..87fcce7cbb40 100644 --- a/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.yaml +++ b/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.yaml @@ -20,6 +20,7 @@ properties: - nvidia,tegra186-misc - nvidia,tegra194-misc - nvidia,tegra234-misc + - nvidia,tegra264-misc reg: items: diff --git a/Documentation/devicetree/bindings/misc/pci1de4,1.yaml b/Documentation/devicetree/bindings/misc/pci1de4,1.yaml new file mode 100644 index 000000000000..2f9a7a554ed8 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/pci1de4,1.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/pci1de4,1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RaspberryPi RP1 MFD PCI device + +maintainers: + - A. della Porta + +description: + The RaspberryPi RP1 is a PCI multi function device containing + peripherals ranging from Ethernet to USB controller, I2C, SPI + and others. + The peripherals are accessed by addressing the PCI BAR1 region. + +allOf: + - $ref: /schemas/pci/pci-ep-bus.yaml + +properties: + compatible: + additionalItems: true + maxItems: 3 + items: + - const: pci1de4,1 + + '#interrupt-cells': + const: 2 + description: | + Specifies respectively the interrupt number and flags as defined + in include/dt-bindings/interrupt-controller/irq.h. + Since all interrupts are active high, only IRQ_TYPE_LEVEL_HIGH + and IRQ_TYPE_EDGE_RISING can be specified as type flags. + The supported values for the interrupt number are: + - IO BANK0: 0 + - IO BANK1: 1 + - IO BANK2: 2 + - AUDIO IN: 3 + - AUDIO OUT: 4 + - PWM0: 5 + - ETH: 6 + - I2C0: 7 + - I2C1: 8 + - I2C2: 9 + - I2C3: 10 + - I2C4: 11 + - I2C5: 12 + - I2C6: 13 + - I2S0: 14 + - I2S1: 15 + - I2S2: 16 + - SDIO0: 17 + - SDIO1: 18 + - SPI0: 19 + - SPI1: 20 + - SPI2: 21 + - SPI3: 22 + - SPI4: 23 + - SPI5: 24 + - UART0: 25 + - TIMER0: 26 + - TIMER1: 27 + - TIMER2: 28 + - TIMER3: 29 + - USB HOST0: 30 + - USB HOST0-0: 31 + - USB HOST0-1: 32 + - USB HOST0-2: 33 + - USB HOST0-3: 34 + - USB HOST1: 35 + - USB HOST1-0: 36 + - USB HOST1-1: 37 + - USB HOST1-2: 38 + - USB HOST1-3: 39 + - DMA: 40 + - PWM1: 41 + - UART1: 42 + - UART2: 43 + - UART3: 44 + - UART4: 45 + - UART5: 46 + - MIPI0: 47 + - MIPI1: 48 + - VIDEO OUT: 49 + - PIO0: 50 + - PIO1: 51 + - ADC FIFO: 52 + - PCIE OUT: 53 + - SPI6: 54 + - SPI7: 55 + - SPI8: 56 + - PROC MISC: 57 + - SYSCFG: 58 + - CLOCKS DEFAULT: 59 + - VBUSCTRL: 60 + + interrupt-controller: true + +unevaluatedProperties: false + +required: + - compatible + - '#interrupt-cells' + - interrupt-controller + - pci-ep-bus@1 + +examples: + - | + pci { + #address-cells = <3>; + #size-cells = <2>; + + rp1@0,0 { + compatible = "pci1de4,1"; + ranges = <0x01 0x00 0x00000000 0x82010000 0x00 0x00 0x00 0x400000>; + #address-cells = <3>; + #size-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + pci_ep_bus: pci-ep-bus@1 { + compatible = "simple-bus"; + ranges = <0x00 0x40000000 0x01 0x00 0x00000000 0x00 0x00400000>; + dma-ranges = <0x10 0x00000000 0x43000000 0x10 0x00000000 0x10 0x00000000>; + #address-cells = <2>; + #size-cells = <2>; + + rp1_clocks: clocks@40018000 { + compatible = "raspberrypi,rp1-clocks"; + reg = <0x00 0x40018000 0x0 0x10038>; + #clock-cells = <1>; + clocks = <&clk_rp1_xosc>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml index 0432cc96f7ca..ac75d694611a 100644 --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml @@ -16,6 +16,7 @@ properties: - amd,pensando-elba-sd4hc - microchip,mpfs-sd4hc - microchip,pic64gx-sd4hc + - mobileye,eyeq-sd4hc - socionext,uniphier-sd4hc - const: cdns,sd4hc diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml new file mode 100644 index 000000000000..c142421bc723 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/loongson,ls2k0500-mmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs + +description: + The MMC host controller on the Loongson-2K0500/2K1000 (using an externally + shared apbdma controller) provides the SD and SDIO device interfaces. + The two MMC host controllers on the Loongson-2K2000 are similar, + except that they use internal exclusive DMA. one controller provides + the eMMC interface and the other provides the SD/SDIO interface. + +maintainers: + - Binbin Zhou + +allOf: + - $ref: mmc-controller.yaml# + +properties: + compatible: + enum: + - loongson,ls2k0500-mmc + - loongson,ls2k1000-mmc + - loongson,ls2k2000-mmc + + reg: + minItems: 1 + items: + - description: Loongson-2K MMC controller registers. + - description: APB DMA config register for Loongson-2K MMC controller. + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + const: rx-tx + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k0500-mmc + - loongson,ls2k1000-mmc + +then: + properties: + reg: + minItems: 2 + + required: + - dmas + - dma-names + +else: + properties: + reg: + maxItems: 1 + +examples: + - | + #include + #include + #include + + mmc@1fe2c000 { + compatible = "loongson,ls2k1000-mmc"; + reg = <0x1fe2c000 0x68>, + <0x1fe00438 0x8>; + interrupt-parent = <&liointc0>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + dmas = <&apbdma1 0>; + dma-names = "rx-tx"; + bus-width = <4>; + cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + }; + + - | + #include + #include + + mmc@79990000 { + compatible = "loongson,ls2k2000-mmc"; + reg = <0x79990000 0x1000>; + interrupt-parent = <&pic>; + interrupts = <51 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_EMMC_CLK>; + bus-width = <8>; + non-removable; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sd; + no-sdio; + }; diff --git a/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml b/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml index 32e512a68ed6..df07ea3b81d1 100644 --- a/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml @@ -17,7 +17,7 @@ description: | and the properties used by the mxsmmc driver. allOf: - - $ref: mmc-controller.yaml + - $ref: mmc-controller-common.yaml# properties: compatible: @@ -31,6 +31,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + dmas: maxItems: 1 @@ -41,6 +44,7 @@ required: - compatible - reg - interrupts + - clocks - dmas - dma-names @@ -52,6 +56,7 @@ examples: compatible = "fsl,imx28-mmc"; reg = <0x80010000 2000>; interrupts = <96>; + clocks = <&clks 46>; dmas = <&dma_apbh 0>; dma-names = "rx-tx"; bus-width = <8>; diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 7563623876fc..c754ea71f51f 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -72,6 +72,8 @@ properties: - enum: - renesas,sdhi-r9a09g047 # RZ/G3E - renesas,sdhi-r9a09g056 # RZ/V2N + - renesas,sdhi-r9a09g077 # RZ/T2H + - renesas,sdhi-r9a09g087 # RZ/N2H - const: renesas,sdhi-r9a09g057 # RZ/V2H(P) reg: @@ -129,59 +131,78 @@ allOf: compatible: contains: enum: - - renesas,sdhi-r9a09g057 - - renesas,rzg2l-sdhi + - renesas,sdhi-r9a09g077 + - renesas,sdhi-r9a09g087 then: properties: + resets: false clocks: items: - - description: IMCLK, SDHI channel main clock1. - - description: CLK_HS, SDHI channel High speed clock which operates - 4 times that of SDHI channel main clock1. - - description: IMCLK2, SDHI channel main clock2. When this clock is - turned off, external SD card detection cannot be - detected. - - description: ACLK, SDHI channel bus clock. + - description: ACLK, IMCLK, SDHI channel bus and main clocks. + - description: CLK_HS, SDHI channel High speed clock. clock-names: items: - - const: core - - const: clkh - - const: cd - const: aclk - required: - - clock-names - - resets + - const: clkh else: if: properties: compatible: contains: enum: - - renesas,rcar-gen2-sdhi - - renesas,rcar-gen3-sdhi - - renesas,rcar-gen4-sdhi + - renesas,sdhi-r9a09g057 + - renesas,rzg2l-sdhi then: properties: clocks: - minItems: 1 - maxItems: 3 + items: + - description: IMCLK, SDHI channel main clock1. + - description: CLK_HS, SDHI channel High speed clock which operates + 4 times that of SDHI channel main clock1. + - description: IMCLK2, SDHI channel main clock2. When this clock is + turned off, external SD card detection cannot be + detected. + - description: ACLK, SDHI channel bus clock. clock-names: - minItems: 1 - uniqueItems: true items: - const: core - - enum: [ clkh, cd ] + - const: clkh - const: cd + - const: aclk + required: + - clock-names + - resets else: - properties: - clocks: - minItems: 1 - maxItems: 2 - clock-names: - minItems: 1 - items: - - const: core - - const: cd + if: + properties: + compatible: + contains: + enum: + - renesas,rcar-gen2-sdhi + - renesas,rcar-gen3-sdhi + - renesas,rcar-gen4-sdhi + then: + properties: + clocks: + minItems: 1 + maxItems: 3 + clock-names: + minItems: 1 + uniqueItems: true + items: + - const: core + - enum: [ clkh, cd ] + - const: cd + else: + properties: + clocks: + minItems: 1 + maxItems: 2 + clock-names: + minItems: 1 + items: + - const: core + - const: cd - if: properties: @@ -245,49 +266,49 @@ examples: #include sdhi0: mmc@ee100000 { - compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; - reg = <0xee100000 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 314>; + compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; + reg = <0xee100000 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 314>; }; sdhi1: mmc@ee120000 { - compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; - reg = <0xee120000 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 313>; - dmas = <&dmac0 0xc9>, <&dmac0 0xca>, <&dmac1 0xc9>, <&dmac1 0xca>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 313>; + compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; + reg = <0xee120000 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 313>; + dmas = <&dmac0 0xc9>, <&dmac0 0xca>, <&dmac1 0xc9>, <&dmac1 0xca>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 313>; }; sdhi2: mmc@ee140000 { - compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; - reg = <0xee140000 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 312>; - }; - - sdhi3: mmc@ee160000 { - compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; - reg = <0xee160000 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 311>; + compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; + reg = <0xee140000 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 312>; + }; + + sdhi3: mmc@ee160000 { + compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi"; + reg = <0xee160000 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 311>; }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index 2b2cbce2458b..22d1f50c3fd1 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -42,9 +42,11 @@ properties: - qcom,ipq5424-sdhci - qcom,ipq6018-sdhci - qcom,ipq9574-sdhci + - qcom,milos-sdhci - qcom,qcm2290-sdhci - qcom,qcs404-sdhci - qcom,qcs615-sdhci + - qcom,qcs8300-sdhci - qcom,qdu1000-sdhci - qcom,sar2130p-sdhci - qcom,sc7180-sdhci diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml b/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml index 4869ddef36fd..e7c06032048a 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml @@ -30,6 +30,26 @@ allOf: maxItems: 1 reg-names: maxItems: 1 + - if: + properties: + compatible: + contains: + const: mrvl,pxav1-mmc + then: + properties: + pinctrl-names: + description: + Optional for supporting PXA168 SDIO IRQ errata to switch CMD pin between + SDIO CMD and GPIO mode. + items: + - const: default + - const: state_cmd_gpio + pinctrl-0: + description: + Should contain default pinctrl. + pinctrl-1: + description: + Should switch CMD pin to GPIO mode as a high output. properties: compatible: @@ -62,22 +82,6 @@ properties: - const: io - const: core - pinctrl-names: - description: - Optional for supporting PXA168 SDIO IRQ errata to switch CMD pin between - SDIO CMD and GPIO mode. - items: - - const: default - - const: state_cmd_gpio - - pinctrl-0: - description: - Should contain default pinctrl. - - pinctrl-1: - description: - Should switch CMD pin to GPIO mode as a high output. - mrvl,clk-delay-cycles: description: Specify a number of cycles to delay for tuning. $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml index 335f8204aa1e..587af4968255 100644 --- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml @@ -20,7 +20,7 @@ properties: - pattern: "^((((micron|spansion|st),)?\ (m25p(40|80|16|32|64|128)|\ n25q(32b|064|128a11|128a13|256a|512a|164k)))|\ - atmel,at25df(321a|641|081a)|\ + atmel,at(25|26)df(321a|641|081a)|\ everspin,mr25h(10|40|128|256)|\ (mxicy|macronix),mx25l(4005a|1606e|6405d|8005|12805d|25635e)|\ (mxicy|macronix),mx25u(4033|4035)|\ diff --git a/Documentation/devicetree/bindings/mtd/nxp,lpc1773-spifi.yaml b/Documentation/devicetree/bindings/mtd/nxp,lpc1773-spifi.yaml new file mode 100644 index 000000000000..d6efb9417b7a --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nxp,lpc1773-spifi.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/nxp,lpc1773-spifi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP SPI Flash Interface (SPIFI) + +description: + NXP SPIFI is a specialized SPI interface for serial Flash devices. + It supports one Flash device with 1-, 2- and 4-bits width in SPI + mode 0 or 3. The controller operates in either command or memory + mode. In memory mode the Flash is accessible from the CPU as + normal memory. + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1773-spifi + + reg: + maxItems: 2 + + reg-names: + items: + - const: spifi + - const: flash + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: spifi + - const: reg + + resets: + maxItems: 1 + + spi-cpol: + enum: [0, 3] + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + - clock-names + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi@40003000 { + compatible = "nxp,lpc1773-spifi"; + reg = <0x40003000 0x1000>, <0x14000000 0x4000000>; + reg-names = "spifi", "flash"; + interrupts = <30>; + clocks = <&ccu1 CLK_SPIFI>, <&ccu1 CLK_CPU_SPIFI>; + clock-names = "spifi", "reg"; + resets = <&rgu 53>; + }; + diff --git a/Documentation/devicetree/bindings/mtd/nxp-spifi.txt b/Documentation/devicetree/bindings/mtd/nxp-spifi.txt deleted file mode 100644 index f8b6b250654e..000000000000 --- a/Documentation/devicetree/bindings/mtd/nxp-spifi.txt +++ /dev/null @@ -1,58 +0,0 @@ -* NXP SPI Flash Interface (SPIFI) - -NXP SPIFI is a specialized SPI interface for serial Flash devices. -It supports one Flash device with 1-, 2- and 4-bits width in SPI -mode 0 or 3. The controller operates in either command or memory -mode. In memory mode the Flash is accessible from the CPU as -normal memory. - -Required properties: - - compatible : Should be "nxp,lpc1773-spifi" - - reg : the first contains the register location and length, - the second contains the memory mapping address and length - - reg-names: Should contain the reg names "spifi" and "flash" - - interrupts : Should contain the interrupt for the device - - clocks : The clocks needed by the SPIFI controller - - clock-names : Should contain the clock names "spifi" and "reg" - -Optional properties: - - resets : phandle + reset specifier - -The SPI Flash must be a child of the SPIFI node and must have a -compatible property as specified in bindings/mtd/jedec,spi-nor.txt - -Optionally it can also contain the following properties. - - spi-cpol : Controller only supports mode 0 and 3 so either - both spi-cpol and spi-cpha should be present or - none of them - - spi-cpha : See above - - spi-rx-bus-width : Used to select how many pins that are used - for input on the controller - -See bindings/spi/spi-bus.txt for more information. - -Example: -spifi: spifi@40003000 { - compatible = "nxp,lpc1773-spifi"; - reg = <0x40003000 0x1000>, <0x14000000 0x4000000>; - reg-names = "spifi", "flash"; - interrupts = <30>; - clocks = <&ccu1 CLK_SPIFI>, <&ccu1 CLK_CPU_SPIFI>; - clock-names = "spifi", "reg"; - resets = <&rgu 53>; - - flash@0 { - compatible = "jedec,spi-nor"; - spi-cpol; - spi-cpha; - spi-rx-bus-width = <4>; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "data"; - reg = <0 0x200000>; - }; - }; -}; - diff --git a/Documentation/devicetree/bindings/mtd/technologic,nand.yaml b/Documentation/devicetree/bindings/mtd/technologic,nand.yaml index f9d87c46094b..a3c316436317 100644 --- a/Documentation/devicetree/bindings/mtd/technologic,nand.yaml +++ b/Documentation/devicetree/bindings/mtd/technologic,nand.yaml @@ -40,6 +40,6 @@ examples: #address-cells = <1>; #size-cells = <0>; nand@0 { - reg = <0>; + reg = <0>; }; }; diff --git a/Documentation/devicetree/bindings/net/adi,adin.yaml b/Documentation/devicetree/bindings/net/adi,adin.yaml index 929cf8c0b0fd..c425a9f1886d 100644 --- a/Documentation/devicetree/bindings/net/adi,adin.yaml +++ b/Documentation/devicetree/bindings/net/adi,adin.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices ADIN1200/ADIN1300 PHY maintainers: - - Alexandru Tachici + - Marcelo Schmitt description: | Bindings for Analog Devices Industrial Ethernet PHYs diff --git a/Documentation/devicetree/bindings/net/adi,adin1110.yaml b/Documentation/devicetree/bindings/net/adi,adin1110.yaml index 9de865295d7a..0a73e01d7f97 100644 --- a/Documentation/devicetree/bindings/net/adi,adin1110.yaml +++ b/Documentation/devicetree/bindings/net/adi,adin1110.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: ADI ADIN1110 MAC-PHY maintainers: - - Alexandru Tachici + - Marcelo Schmitt description: | The ADIN1110 is a low power single port 10BASE-T1L MAC- diff --git a/Documentation/devicetree/bindings/net/airoha,an7583-mdio.yaml b/Documentation/devicetree/bindings/net/airoha,an7583-mdio.yaml new file mode 100644 index 000000000000..3e7e68ec1560 --- /dev/null +++ b/Documentation/devicetree/bindings/net/airoha,an7583-mdio.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/airoha,an7583-mdio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha AN7583 Dedicated MDIO Controller + +maintainers: + - Christian Marangi + +description: + Airoha AN7583 SoC have 3 different MDIO Controller. + + One comes from the intergated Switch based on MT7530. + + The other 2 (that this schema describe) live under the SCU + register supporting both C22 and C45 PHYs. + +$ref: mdio.yaml# + +properties: + compatible: + const: airoha,an7583-mdio + + reg: + enum: [0xc8, 0xcc] + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + clock-frequency: + default: 2500000 + +required: + - compatible + - reg + - clocks + - resets + +unevaluatedProperties: false + +examples: + - | + system-controller { + #address-cells = <1>; + #size-cells = <0>; + + mdio-bus@c8 { + compatible = "airoha,an7583-mdio"; + reg = <0xc8>; + + clocks = <&scu>; + resets = <&scu>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml index 7b6a2fde8175..2ac709a4c472 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml @@ -21,9 +21,10 @@ properties: - items: - enum: - allwinner,sun20i-d1-emac + - allwinner,sun50i-a100-emac - allwinner,sun50i-h6-emac - allwinner,sun50i-h616-emac0 - - allwinner,sun55i-a523-emac0 + - allwinner,sun55i-a523-gmac0 - const: allwinner,sun50i-a64-emac reg: diff --git a/Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml b/Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml new file mode 100644 index 000000000000..aafb6447b6c2 --- /dev/null +++ b/Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +# Copyright (C) 2025 Altera Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/altr,gmii-to-sgmii-2.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera GMII to SGMII Converter + +maintainers: + - Matthew Gerlach + +description: + This binding describes the Altera GMII to SGMII converter. + +properties: + compatible: + const: altr,gmii-to-sgmii-2.0 + + reg: + items: + - description: Registers for the emac splitter IP + - description: Registers for the GMII to SGMII converter. + - description: Registers for TSE control. + + reg-names: + items: + - const: hps_emac_interface_splitter_avalon_slave + - const: gmii_to_sgmii_adapter_avalon_slave + - const: eth_tse_control_port + +required: + - compatible + - reg + - reg-names + +unevaluatedProperties: false + +examples: + - | + phy@ff000240 { + compatible = "altr,gmii-to-sgmii-2.0"; + reg = <0xff000240 0x00000008>, + <0xff000200 0x00000040>, + <0xff000250 0x00000008>; + reg-names = "hps_emac_interface_splitter_avalon_slave", + "gmii_to_sgmii_adapter_avalon_slave", + "eth_tse_control_port"; + }; diff --git a/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml b/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml new file mode 100644 index 000000000000..3a22d35db778 --- /dev/null +++ b/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml @@ -0,0 +1,171 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/altr,socfpga-stmmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera SOCFPGA SoC DWMAC controller + +maintainers: + - Matthew Gerlach + +description: + This binding describes the Altera SOCFPGA SoC implementation of the + Synopsys DWMAC for the Cyclone5, Arria5, Stratix10, Agilex5 and Agilex7 + families of chips. + # TODO: Determine how to handle the Arria10 reset-name, stmmaceth-ocp, that + # does not validate against net/snps,dwmac.yaml. + +select: + properties: + compatible: + contains: + enum: + - altr,socfpga-stmmac + - altr,socfpga-stmmac-a10-s10 + - altr,socfpga-stmmac-agilex5 + + required: + - compatible + +properties: + compatible: + oneOf: + - items: + - const: altr,socfpga-stmmac + - const: snps,dwmac-3.70a + - const: snps,dwmac + - items: + - const: altr,socfpga-stmmac-a10-s10 + - const: snps,dwmac-3.72a + - const: snps,dwmac + - items: + - const: altr,socfpga-stmmac-a10-s10 + - const: snps,dwmac-3.74a + - const: snps,dwmac + - items: + - const: altr,socfpga-stmmac-agilex5 + - const: snps,dwxgmac-2.10 + + clocks: + minItems: 1 + items: + - description: GMAC main clock + - description: + PTP reference clock. This clock is used for programming the + Timestamp Addend Register. If not passed then the system + clock will be used and this is fine on some platforms. + + clock-names: + minItems: 1 + items: + - const: stmmaceth + - const: ptp_ref + + iommus: + minItems: 1 + maxItems: 2 + + phy-mode: + enum: + - gmii + - mii + - rgmii + - rgmii-id + - rgmii-rxid + - rgmii-txid + - sgmii + - 1000base-x + + rxc-skew-ps: + description: Skew control of RXC pad + + rxd0-skew-ps: + description: Skew control of RX data 0 pad + + rxd1-skew-ps: + description: Skew control of RX data 1 pad + + rxd2-skew-ps: + description: Skew control of RX data 2 pad + + rxd3-skew-ps: + description: Skew control of RX data 3 pad + + rxdv-skew-ps: + description: Skew control of RX CTL pad + + txc-skew-ps: + description: Skew control of TXC pad + + txen-skew-ps: + description: Skew control of TXC pad + + altr,emac-splitter: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Should be the phandle to the emac splitter soft IP node if DWMAC + controller is connected an emac splitter. + + altr,f2h_ptp_ref_clk: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to Precision Time Protocol reference clock. This clock is + common to gmac instances and defaults to osc1. + + altr,gmii-to-sgmii-converter: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Should be the phandle to the gmii to sgmii converter soft IP. + + altr,sysmgr-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Should be the phandle to the system manager node that encompass + the glue register, the register offset, and the register shift. + On Cyclone5/Arria5, the register shift represents the PHY mode + bits, while on the Arria10/Stratix10/Agilex platforms, the + register shift represents bit for each emac to enable/disable + signals from the FPGA fabric to the EMAC modules. + items: + - items: + - description: phandle to the system manager node + - description: offset of the control register + - description: shift within the control register + +patternProperties: + "^mdio[0-9]$": + type: object + +required: + - compatible + - clocks + - clock-names + - altr,sysmgr-syscon + +allOf: + - $ref: snps,dwmac.yaml# + +unevaluatedProperties: false + +examples: + + - | + #include + #include + soc { + #address-cells = <1>; + #size-cells = <1>; + ethernet@ff700000 { + compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", + "snps,dwmac"; + altr,sysmgr-syscon = <&sysmgr 0x60 0>; + reg = <0xff700000 0x2000>; + interrupts = ; + interrupt-names = "macirq"; + mac-address = [00 00 00 00 00 00]; /* Filled in by U-Boot */ + clocks = <&emac_0_clk>; + clock-names = "stmmaceth"; + phy-mode = "sgmii"; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml index 3ab60c70286f..857c6234ba9b 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml @@ -34,6 +34,13 @@ properties: This property depends on the module vendor's configuration. + max-speed: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 3000000 + - 4000000 + default: 3000000 + firmware-name: maxItems: 1 @@ -65,6 +72,14 @@ properties: description: The GPIO number of the NXP chipset used for BT_WAKE_OUT. + vcc-supply: + description: + phandle of the regulator that provides the supply voltage. + + reset-gpios: + description: + Chip powerdown/reset signal (PDn). + required: - compatible @@ -78,10 +93,13 @@ examples: bluetooth { compatible = "nxp,88w8987-bt"; fw-init-baudrate = <3000000>; + max-speed = <4000000>; firmware-name = "uartuart8987_bt_v0.bin"; device-wakeup-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>; nxp,wakein-pin = /bits/ 8 <18>; nxp,wakeout-pin = /bits/ 8 <19>; + vcc-supply = <&nxp_iw612_supply>; + reset-gpios = <&gpioctrl 2 GPIO_ACTIVE_LOW>; local-bd-address = [66 55 44 33 22 11]; interrupt-parent = <&gpio>; interrupts = <8 IRQ_TYPE_EDGE_FALLING>; diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml index 8d69846b2e09..559d0f733e7e 100644 --- a/Documentation/devicetree/bindings/net/cdns,macb.yaml +++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml @@ -62,6 +62,7 @@ properties: - items: - enum: - microchip,sam9x7-gem # Microchip SAM9X7 gigabit ethernet interface + - microchip,sama7d65-gem # Microchip SAMA7D65 gigabit ethernet interface - const: microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface reg: @@ -114,6 +115,13 @@ properties: power-domains: maxItems: 1 + cdns,refclk-ext: + type: boolean + description: + This selects if the REFCLK for RMII is provided by an external source. + For RGMII mode this selects if the 125MHz REF clock is provided by an external + source. + cdns,rx-watermark: $ref: /schemas/types.yaml#/definitions/uint32 description: diff --git a/Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml b/Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml index d6c957a33b48..fbab3a1a8d3e 100644 --- a/Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml +++ b/Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml @@ -66,6 +66,12 @@ properties: - brcm,bcm63268-switch - const: brcm,bcm63xx-switch + brcm,gpio-ctrl: + description: + A phandle to the syscon node of the bcm63xx gpio controller + which contains phy control registers + $ref: /schemas/types.yaml#/definitions/phandle + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml index 51205f9f2985..815a90808901 100644 --- a/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml +++ b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml @@ -136,6 +136,16 @@ properties: See Documentation/devicetree/bindings/regulator/mt6323-regulator.txt for details for the regulator setup on these boards. + mdio: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + + properties: + mediatek,pio: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle pointing to the mediatek pinctrl node. + mediatek,mcm: type: boolean description: @@ -190,6 +200,18 @@ required: - reg $defs: + builtin-dsa-port: + patternProperties: + "^(ethernet-)?ports$": + patternProperties: + "^(ethernet-)?port@[0-6]$": + if: + required: [ ethernet ] + then: + properties: + phy-mode: + const: internal + mt7530-dsa-port: patternProperties: "^(ethernet-)?ports$": @@ -297,7 +319,7 @@ allOf: - airoha,en7581-switch - airoha,an7583-switch then: - $ref: "#/$defs/mt7530-dsa-port" + $ref: "#/$defs/builtin-dsa-port" properties: gpio-controller: false mediatek,mcm: false diff --git a/Documentation/devicetree/bindings/net/dsa/micrel,ks8995.yaml b/Documentation/devicetree/bindings/net/dsa/micrel,ks8995.yaml new file mode 100644 index 000000000000..854808ff5ad5 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/micrel,ks8995.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/micrel,ks8995.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Micrel KS8995 Family DSA Switches + +maintainers: + - Linus Walleij + +description: + The Micrel KS8995 DSA Switches are 100 Mbit switches that were produced in + the early-to-mid 2000s. The chip features a CPU port and four outgoing ports, + each with an internal PHY. The chip itself is managed over SPI, but all the + PHYs need to be accessed from an external MDIO channel. + + Further, a fifth PHY is available and can be used separately from the switch + fabric, connected to an external MII interface name MII-P5. This is + unrelated from the CPU-facing port 5 which is used for DSA MII traffic. + +properties: + compatible: + enum: + - micrel,ks8995 + - micrel,ksz8795 + - micrel,ksz8864 + + reg: + maxItems: 1 + + reset-gpios: + description: GPIO to be used to reset the whole device + maxItems: 1 + +allOf: + - $ref: dsa.yaml#/$defs/ethernet-ports + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-switch@0 { + compatible = "micrel,ks8995"; + reg = <0>; + spi-max-frequency = <25000000>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-port@0 { + reg = <0>; + label = "lan1"; + }; + ethernet-port@1 { + reg = <1>; + label = "lan2"; + }; + ethernet-port@2 { + reg = <2>; + label = "lan3"; + }; + ethernet-port@3 { + reg = <3>; + label = "lan4"; + }; + ethernet-port@4 { + reg = <4>; + ethernet = <&mac2>; + phy-mode = "mii"; + fixed-link { + speed = <100>; + full-duplex; + }; + }; + }; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + + /* The WAN port connected on MII-P5 */ + ethernet-port@1000 { + reg = <0x00001000 0x1000>; + label = "wan"; + phy-mode = "mii"; + phy-handle = <&phy5>; + }; + + mac2: ethernet-port@2000 { + reg = <0x00002000 0x1000>; + phy-mode = "mii"; + fixed-link { + speed = <100>; + full-duplex; + }; + }; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + /* LAN PHYs 1-4 accessible over external MDIO */ + phy1: ethernet-phy@1 { + reg = <1>; + }; + phy2: ethernet-phy@2 { + reg = <2>; + }; + phy3: ethernet-phy@3 { + reg = <3>; + }; + phy4: ethernet-phy@4 { + reg = <4>; + }; + /* WAN PHY accessible over external MDIO */ + phy5: ethernet-phy@5 { + reg = <5>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml index 62ca63e8a26f..eb4607460db7 100644 --- a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -18,6 +18,7 @@ properties: # required and optional properties. compatible: enum: + - microchip,ksz8463 - microchip,ksz8765 - microchip,ksz8794 - microchip,ksz8795 diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 7cbf11bbe99c..66b1cfbbfe22 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -39,6 +39,7 @@ properties: # MAC. - internal - mii + - mii-lite - gmii - sgmii - psgmii diff --git a/Documentation/devicetree/bindings/net/faraday,ftgmac100.yaml b/Documentation/devicetree/bindings/net/faraday,ftgmac100.yaml index 55d6a8379025..d14410018bcf 100644 --- a/Documentation/devicetree/bindings/net/faraday,ftgmac100.yaml +++ b/Documentation/devicetree/bindings/net/faraday,ftgmac100.yaml @@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Faraday Technology FTGMAC100 gigabit ethernet controller -allOf: - - $ref: ethernet-controller.yaml# - maintainers: - Po-Yu Chuang @@ -35,6 +32,9 @@ properties: - description: MAC IP clock - description: RMII RCLK gate for AST2500/2600 + resets: + maxItems: 1 + clock-names: minItems: 1 items: @@ -74,6 +74,21 @@ required: - reg - interrupts +allOf: + - $ref: ethernet-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - aspeed,ast2600-mac + then: + properties: + resets: true + else: + properties: + resets: false + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt b/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt deleted file mode 100644 index 168f1be50912..000000000000 --- a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt +++ /dev/null @@ -1,27 +0,0 @@ -* AT86RF230 IEEE 802.15.4 * - -Required properties: - - compatible: should be "atmel,at86rf230", "atmel,at86rf231", - "atmel,at86rf233" or "atmel,at86rf212" - - spi-max-frequency: maximal bus speed, should be set to 7500000 depends - sync or async operation mode - - reg: the chipselect index - - interrupts: the interrupt generated by the device. Non high-level - can occur deadlocks while handling isr. - -Optional properties: - - reset-gpio: GPIO spec for the rstn pin - - sleep-gpio: GPIO spec for the slp_tr pin - - xtal-trim: u8 value for fine tuning the internal capacitance - arrays of xtal pins: 0 = +0 pF, 0xf = +4.5 pF - -Example: - - at86rf231@0 { - compatible = "atmel,at86rf231"; - spi-max-frequency = <7500000>; - reg = <0>; - interrupts = <19 4>; - interrupt-parent = <&gpio3>; - xtal-trim = /bits/ 8 <0x06>; - }; diff --git a/Documentation/devicetree/bindings/net/ieee802154/atmel,at86rf233.yaml b/Documentation/devicetree/bindings/net/ieee802154/atmel,at86rf233.yaml new file mode 100644 index 000000000000..32cdc30009cc --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/atmel,at86rf233.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/ieee802154/atmel,at86rf233.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AT86RF230 IEEE 802.15.4 + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - atmel,at86rf212 + - atmel,at86rf230 + - atmel,at86rf231 + - atmel,at86rf233 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpio: + maxItems: 1 + + sleep-gpio: + maxItems: 1 + + spi-max-frequency: + maximum: 7500000 + + xtal-trim: + $ref: /schemas/types.yaml#/definitions/uint8 + maximum: 0xf + description: | + Fine tuning the internal capacitance arrays of xtal pins: + 0 = +0 pF, 0xf = +4.5 pF + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + zigbee@0 { + compatible = "atmel,at86rf231"; + reg = <0>; + spi-max-frequency = <7500000>; + interrupts = <19 4>; + interrupt-parent = <&gpio3>; + xtal-trim = /bits/ 8 <0x06>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/intel,ixp4xx-ethernet.yaml b/Documentation/devicetree/bindings/net/intel,ixp4xx-ethernet.yaml index 4fdc5328826c..8689de1aaea1 100644 --- a/Documentation/devicetree/bindings/net/intel,ixp4xx-ethernet.yaml +++ b/Documentation/devicetree/bindings/net/intel,ixp4xx-ethernet.yaml @@ -47,6 +47,8 @@ properties: phy-handle: true + fixed-link: true + intel,npe-handle: $ref: /schemas/types.yaml#/definitions/phandle-array items: diff --git a/Documentation/devicetree/bindings/net/lpc-eth.txt b/Documentation/devicetree/bindings/net/lpc-eth.txt deleted file mode 100644 index cfe0e5991d46..000000000000 --- a/Documentation/devicetree/bindings/net/lpc-eth.txt +++ /dev/null @@ -1,28 +0,0 @@ -* NXP LPC32xx SoC Ethernet Controller - -Required properties: -- compatible: Should be "nxp,lpc-eth" -- reg: Address and length of the register set for the device -- interrupts: Should contain ethernet controller interrupt - -Optional properties: -- phy-mode: See ethernet.txt file in the same directory. If the property is - absent, "rmii" is assumed. -- use-iram: Use LPC32xx internal SRAM (IRAM) for DMA buffering - -Optional subnodes: -- mdio : specifies the mdio bus, used as a container for phy nodes according to - phy.txt in the same directory - - -Example: - - mac: ethernet@31060000 { - compatible = "nxp,lpc-eth"; - reg = <0x31060000 0x1000>; - interrupt-parent = <&mic>; - interrupts = <29 0>; - - phy-mode = "rmii"; - use-iram; - }; diff --git a/Documentation/devicetree/bindings/net/marvell,armada-370-neta.yaml b/Documentation/devicetree/bindings/net/marvell,armada-370-neta.yaml new file mode 100644 index 000000000000..8814977da024 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,armada-370-neta.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,armada-370-neta.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 370/XP/3700/AC5 Ethernet Controller (NETA) + +maintainers: + - Marcin Wojtas + +allOf: + - $ref: /schemas/net/ethernet-controller.yaml# + +properties: + compatible: + enum: + - marvell,armada-370-neta + - marvell,armada-xp-neta + - marvell,armada-3700-neta + - marvell,armada-ac5-neta + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: core + - const: bus + + phys: + maxItems: 1 + + tx-csum-limit: + description: Maximum MTU in bytes for Tx checksum offload; default is 1600 for + armada-370-neta and 9800 for others. + $ref: /schemas/types.yaml#/definitions/uint32 + + buffer-manager: + description: Phandle to hardware buffer manager. + $ref: /schemas/types.yaml#/definitions/phandle + + bm,pool-long: + description: Pool ID for packets larger than the short threshold. + $ref: /schemas/types.yaml#/definitions/uint32 + + bm,pool-short: + description: Pool ID for packets smaller than the long threshold. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + ethernet@70000 { + compatible = "marvell,armada-370-neta"; + reg = <0x70000 0x2500>; + interrupts = <8>; + clocks = <&gate_clk 4>; + tx-csum-limit = <9800>; + phy = <&phy0>; + phy-mode = "rgmii-id"; + buffer-manager = <&bm>; + bm,pool-long = <0>; + bm,pool-short = <1>; + }; diff --git a/Documentation/devicetree/bindings/net/marvell,armada-380-neta-bm.yaml b/Documentation/devicetree/bindings/net/marvell,armada-380-neta-bm.yaml new file mode 100644 index 000000000000..9392e7126e3e --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,armada-380-neta-bm.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,armada-380-neta-bm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 380/XP Buffer Manager (BM) + +maintainers: + - Marcin Wojtas + +description: + In order to see how to hook the BM to a given ethernet port, please refer to + Documentation/devicetree/bindings/net/marvell,armada-370-neta.yaml. + +properties: + compatible: + const: marvell,armada-380-neta-bm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + internal-mem: + description: Phandle to internal SRAM region + $ref: /schemas/types.yaml#/definitions/phandle + +patternProperties: + "^pool[0-3],capacity$": + description: + size of external buffer pointers' ring maintained in DRAM for pool 0-3 + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 128 + maximum: 16352 + + "^pool[0-3],pkt-size$": + description: + maximum packet size for a short buffer pool entry (pool 0-3) + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - clocks + - internal-mem + +additionalProperties: false + +examples: + - | + bm@c8000 { + compatible = "marvell,armada-380-neta-bm"; + reg = <0xc8000 0xac>; + clocks = <&gateclk 13>; + internal-mem = <&bm_bppi>; + pool2,capacity = <4096>; + pool1,pkt-size = <512>; + }; diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt deleted file mode 100644 index 2bf31572b08d..000000000000 --- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt +++ /dev/null @@ -1,50 +0,0 @@ -* Marvell Armada 370 / Armada XP / Armada 3700 Ethernet Controller (NETA) - -Required properties: -- compatible: could be one of the following: - "marvell,armada-370-neta" - "marvell,armada-xp-neta" - "marvell,armada-3700-neta" - "marvell,armada-ac5-neta" -- reg: address and length of the register set for the device. -- interrupts: interrupt for the device -- phy: See ethernet.txt file in the same directory. -- phy-mode: See ethernet.txt file in the same directory -- clocks: List of clocks for this device. At least one clock is - mandatory for the core clock. If several clocks are given, then the - clock-names property must be used to identify them. - -Optional properties: -- tx-csum-limit: maximum mtu supported by port that allow TX checksum. - Value is presented in bytes. If not used, by default 1600B is set for - "marvell,armada-370-neta" and 9800B for others. -- clock-names: List of names corresponding to clocks property; shall be - "core" for core clock and "bus" for the optional bus clock. -- phys: comphy for the ethernet port, see ../phy/phy-bindings.txt - -Optional properties (valid only for Armada XP/38x): - -- buffer-manager: a phandle to a buffer manager node. Please refer to - Documentation/devicetree/bindings/net/marvell-neta-bm.txt -- bm,pool-long: ID of a pool, that will accept all packets of a size - higher than 'short' pool's threshold (if set) and up to MTU value. - Obligatory, when the port is supposed to use hardware - buffer management. -- bm,pool-short: ID of a pool, that will be used for accepting - packets of a size lower than given threshold. If not set, the port - will use a single 'long' pool for all packets, as defined above. - -Example: - -ethernet@70000 { - compatible = "marvell,armada-370-neta"; - reg = <0x70000 0x2500>; - interrupts = <8>; - clocks = <&gate_clk 4>; - tx-csum-limit = <9800> - phy = <&phy0>; - phy-mode = "rgmii-id"; - buffer-manager = <&bm>; - bm,pool-long = <0>; - bm,pool-short = <1>; -}; diff --git a/Documentation/devicetree/bindings/net/marvell-neta-bm.txt b/Documentation/devicetree/bindings/net/marvell-neta-bm.txt deleted file mode 100644 index 07b31050dbe5..000000000000 --- a/Documentation/devicetree/bindings/net/marvell-neta-bm.txt +++ /dev/null @@ -1,47 +0,0 @@ -* Marvell Armada 380/XP Buffer Manager driver (BM) - -Required properties: - -- compatible: should be "marvell,armada-380-neta-bm". -- reg: address and length of the register set for the device. -- clocks: a pointer to the reference clock for this device. -- internal-mem: a phandle to BM internal SRAM definition. - -Optional properties (port): - -- pool<0 : 3>,capacity: size of external buffer pointers' ring maintained - in DRAM. Can be set for each pool (id 0 : 3) separately. The value has - to be chosen between 128 and 16352 and it also has to be aligned to 32. - Otherwise the driver would adjust a given number or choose default if - not set. -- pool<0 : 3>,pkt-size: maximum size of a packet accepted by a given buffer - pointers' pool (id 0 : 3). It will be taken into consideration only when pool - type is 'short'. For 'long' ones it would be overridden by port's MTU. - If not set a driver will choose a default value. - -In order to see how to hook the BM to a given ethernet port, please -refer to Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt. - -Example: - -- main node: - -bm: bm@c8000 { - compatible = "marvell,armada-380-neta-bm"; - reg = <0xc8000 0xac>; - clocks = <&gateclk 13>; - internal-mem = <&bm_bppi>; - pool2,capacity = <4096>; - pool1,pkt-size = <512>; -}; - -- internal SRAM node: - -bm_bppi: bm-bppi { - compatible = "mmio-sram"; - reg = ; - ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>; - #address-cells = <1>; - #size-cells = <1>; - clocks = <&gateclk 13>; -}; diff --git a/Documentation/devicetree/bindings/net/mediatek,net.yaml b/Documentation/devicetree/bindings/net/mediatek,net.yaml index 9e02fd80af83..b45f67f92e80 100644 --- a/Documentation/devicetree/bindings/net/mediatek,net.yaml +++ b/Documentation/devicetree/bindings/net/mediatek,net.yaml @@ -40,7 +40,19 @@ properties: interrupts: minItems: 1 - maxItems: 4 + maxItems: 8 + + interrupt-names: + minItems: 1 + items: + - const: fe0 + - const: fe1 + - const: fe2 + - const: fe3 + - const: pdma0 + - const: pdma1 + - const: pdma2 + - const: pdma3 power-domains: maxItems: 1 @@ -54,6 +66,10 @@ properties: - const: gmac - const: ppe + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to mmio SRAM + mediatek,ethsys: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -135,6 +151,10 @@ allOf: minItems: 3 maxItems: 3 + interrupt-names: + minItems: 3 + maxItems: 3 + clocks: minItems: 4 maxItems: 4 @@ -146,6 +166,8 @@ allOf: - const: gp1 - const: gp2 + sram: false + mediatek,infracfg: false mediatek,wed: false @@ -166,6 +188,9 @@ allOf: interrupts: maxItems: 1 + interrupt-names: + maxItems: 1 + clocks: minItems: 2 maxItems: 2 @@ -175,6 +200,8 @@ allOf: - const: ethif - const: fe + sram: false + mediatek,infracfg: false mediatek,wed: false @@ -192,6 +219,10 @@ allOf: minItems: 3 maxItems: 3 + interrupt-names: + minItems: 3 + maxItems: 3 + clocks: minItems: 11 maxItems: 11 @@ -210,6 +241,8 @@ allOf: - const: sgmii_ck - const: eth2pll + sram: false + mediatek,infracfg: false mediatek,sgmiisys: @@ -232,6 +265,10 @@ allOf: minItems: 3 maxItems: 3 + interrupt-names: + minItems: 3 + maxItems: 3 + clocks: minItems: 17 maxItems: 17 @@ -256,6 +293,8 @@ allOf: - const: sgmii_ck - const: eth2pll + sram: false + mediatek,sgmiisys: minItems: 2 maxItems: 2 @@ -272,7 +311,10 @@ allOf: then: properties: interrupts: - minItems: 4 + minItems: 8 + + interrupt-names: + minItems: 8 clocks: minItems: 15 @@ -310,7 +352,10 @@ allOf: then: properties: interrupts: - minItems: 4 + minItems: 8 + + interrupt-names: + minItems: 8 clocks: minItems: 15 @@ -348,7 +393,10 @@ allOf: then: properties: interrupts: - minItems: 4 + minItems: 8 + + interrupt-names: + minItems: 8 clocks: minItems: 24 @@ -382,7 +430,7 @@ allOf: - const: xgp3 patternProperties: - "^mac@[0-1]$": + "^mac@[0-2]$": type: object unevaluatedProperties: false allOf: @@ -507,7 +555,11 @@ examples: interrupts = , , , - ; + , + , + , + , + ; clocks = <ðsys CLK_ETH_FE_EN>, <ðsys CLK_ETH_GP2_EN>, <ðsys CLK_ETH_GP1_EN>, diff --git a/Documentation/devicetree/bindings/net/micrel-ks8995.txt b/Documentation/devicetree/bindings/net/micrel-ks8995.txt deleted file mode 100644 index 281bc2498d12..000000000000 --- a/Documentation/devicetree/bindings/net/micrel-ks8995.txt +++ /dev/null @@ -1,20 +0,0 @@ -Micrel KS8995 SPI controlled Ethernet Switch families - -Required properties (according to spi-bus.txt): -- compatible: either "micrel,ks8995", "micrel,ksz8864" or "micrel,ksz8795" - -Optional properties: -- reset-gpios : phandle of gpio that will be used to reset chip during probe - -Example: - -spi-master { - ... - switch@0 { - compatible = "micrel,ksz8795"; - - reg = <0>; - spi-max-frequency = <50000000>; - reset-gpios = <&gpio0 46 GPIO_ACTIVE_LOW>; - }; -}; diff --git a/Documentation/devicetree/bindings/net/nfc/ti,trf7970a.yaml b/Documentation/devicetree/bindings/net/nfc/ti,trf7970a.yaml index d0332eb76ad2..5f49bd9ac5e6 100644 --- a/Documentation/devicetree/bindings/net/nfc/ti,trf7970a.yaml +++ b/Documentation/devicetree/bindings/net/nfc/ti,trf7970a.yaml @@ -55,6 +55,12 @@ properties: description: | Regulator for supply voltage to VIN pin + ti,rx-gain-reduction-db: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Specify an RX gain reduction to reduce antenna sensitivity with 5dB per + increment, with a maximum of 15dB. Supported values: [0, 5, 10, 15]. + required: - compatible - interrupts @@ -95,5 +101,6 @@ examples: irq-status-read-quirk; en2-rf-quirk; clock-frequency = <27120000>; + ti,rx-gain-reduction-db = <15>; }; }; diff --git a/Documentation/devicetree/bindings/net/nxp,lpc-eth.yaml b/Documentation/devicetree/bindings/net/nxp,lpc-eth.yaml new file mode 100644 index 000000000000..dfe9446a5375 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nxp,lpc-eth.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/nxp,lpc-eth.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32xx SoC Ethernet Controller + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc-eth + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + use-iram: + $ref: /schemas/types.yaml#/definitions/flag + description: Use LPC32xx internal SRAM (IRAM) for DMA buffering + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: ethernet-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + ethernet@31060000 { + compatible = "nxp,lpc-eth"; + reg = <0x31060000 0x1000>; + interrupt-parent = <&mic>; + interrupts = <29 0>; + phy-mode = "rmii"; + use-iram; + }; diff --git a/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.txt b/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.txt deleted file mode 100644 index 7edba1264f6f..000000000000 --- a/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.txt +++ /dev/null @@ -1,20 +0,0 @@ -* NXP LPC1850 GMAC ethernet controller - -This device is a platform glue layer for stmmac. -Please see stmmac.txt for the other unchanged properties. - -Required properties: - - compatible: Should contain "nxp,lpc1850-dwmac" - -Examples: - -mac: ethernet@40010000 { - compatible = "nxp,lpc1850-dwmac", "snps,dwmac-3.611", "snps,dwmac"; - reg = <0x40010000 0x2000>; - interrupts = <5>; - interrupt-names = "macirq"; - clocks = <&ccu1 CLK_CPU_ETHERNET>; - clock-names = "stmmaceth"; - resets = <&rgu 22>; - reset-names = "stmmaceth"; -} diff --git a/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml b/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml new file mode 100644 index 000000000000..05acd9bc7616 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/nxp,lpc1850-dwmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC1850 GMAC ethernet controller + +maintainers: + - Frank Li + +# We need a select here so we don't match all nodes with 'snps,dwmac' +select: + properties: + compatible: + contains: + enum: + - nxp,lpc1850-dwmac + required: + - compatible + +properties: + compatible: + items: + - enum: + - nxp,lpc1850-dwmac + - const: snps,dwmac-3.611 + - const: snps,dwmac + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: stmmaceth + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: macirq + + resets: + maxItems: 1 + + reset-names: + items: + - const: stmmaceth + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + +allOf: + - $ref: snps,dwmac.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + ethernet@40010000 { + compatible = "nxp,lpc1850-dwmac", "snps,dwmac-3.611", "snps,dwmac"; + reg = <0x40010000 0x2000>; + interrupts = <5>; + interrupt-names = "macirq"; + clocks = <&ccu1 CLK_CPU_ETHERNET>; + clock-names = "stmmaceth"; + resets = <&rgu 22>; + reset-names = "stmmaceth"; + rx-fifo-depth = <256>; + tx-fifo-depth = <256>; + snps,pbl = <4>; + snps,force_thresh_dma_mode; + phy-mode = "rgmii-id"; + }; diff --git a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml index fd4244fceced..ca61cc37a790 100644 --- a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml +++ b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml @@ -22,6 +22,12 @@ properties: reg: maxItems: 1 + vdd-supply: + description: Regulator that provides 3.3V VDD power supply. + + vdda-supply: + description: Regulator that provides 3.3V VDDA power supply. + managers: type: object additionalProperties: false @@ -68,6 +74,15 @@ properties: "#size-cells": const: 0 + vmain-supply: + description: Regulator that provides 44-57V VMAIN power supply. + + vaux5-supply: + description: Regulator that provides 5V VAUX5 power supply. + + vaux3p3-supply: + description: Regulator that provides 3.3V VAUX3P3 power supply. + patternProperties: '^port@[0-7]$': type: object @@ -106,10 +121,11 @@ examples: #address-cells = <1>; #size-cells = <0>; - manager@0 { + manager0: manager@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; + vmain-supply = <&pse1_supply>; phys0: port@0 { reg = <0>; @@ -161,7 +177,7 @@ examples: pairset-names = "alternative-a", "alternative-b"; pairsets = <&phys0>, <&phys1>; polarity-supported = "MDI", "S"; - vpwr-supply = <&vpwr1>; + vpwr-supply = <&manager0>; }; pse_pi1: pse-pi@1 { reg = <1>; @@ -169,7 +185,7 @@ examples: pairset-names = "alternative-a"; pairsets = <&phys2>; polarity-supported = "MDI"; - vpwr-supply = <&vpwr2>; + vpwr-supply = <&manager0>; }; }; }; diff --git a/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml b/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml index d08abcb01211..bb1ee3398655 100644 --- a/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml +++ b/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml @@ -20,6 +20,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + '#pse-cells': const: 1 @@ -27,10 +30,12 @@ properties: maxItems: 1 channels: - description: each set of 8 ports can be assigned to one physical - channels or two for PoE4. This parameter describes the configuration - of the ports conversion matrix that establishes relationship between - the logical ports and the physical channels. + description: | + Defines the 8 physical delivery channels on the controller that can + be referenced by PSE PIs through their "pairsets" property. The actual + port matrix mapping is created when PSE PIs reference these channels in + their pairsets. For 4-pair operation, two channels from the same group + (0-3 or 4-7) must be referenced by a single PSE PI. type: object additionalProperties: false @@ -62,9 +67,12 @@ unevaluatedProperties: false required: - compatible - reg + - interrupts examples: - | + #include + i2c { #address-cells = <1>; #size-cells = <0>; @@ -72,6 +80,8 @@ examples: ethernet-pse@20 { compatible = "ti,tps23881"; reg = <0x20>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gpiog>; channels { #address-cells = <1>; diff --git a/Documentation/devicetree/bindings/net/qca,ar803x.yaml b/Documentation/devicetree/bindings/net/qca,ar803x.yaml index 3acd09f0da86..7ae5110e7aa2 100644 --- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml +++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml @@ -16,8 +16,37 @@ description: | allOf: - $ref: ethernet-phy.yaml# + - if: + properties: + compatible: + contains: + enum: + - ethernet-phy-id004d.d0c0 + + then: + properties: + reg: + const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC + + resets: + items: + - description: + GE PHY MISC reset which triggers a reset across MDC, DSP, RX, and TX lines. + + qcom,dac-preset-short-cable: + description: + Set if this phy is connected to another phy to adjust the values for + MDAC and EDAC to adjust amplitude, bias current settings, and error + detection and correction algorithm to accommodate for short cable length. + If not set, DAC values are not modified and it is assumed the MDI output pins + of this PHY are directly connected to an RJ45 connector. + type: boolean properties: + compatible: + enum: + - ethernet-phy-id004d.d0c0 + qca,clk-out-frequency: description: Clock output frequency in Hertz. $ref: /schemas/types.yaml#/definitions/uint32 @@ -132,3 +161,17 @@ examples: }; }; }; + - | + #include + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ge_phy: ethernet-phy@7 { + compatible = "ethernet-phy-id004d.d0c0"; + reg = <7>; + + resets = <&gcc GCC_GEPHY_MISC_ARES>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/qca,qca7000.txt b/Documentation/devicetree/bindings/net/qca,qca7000.txt deleted file mode 100644 index 8f5ae0b84eec..000000000000 --- a/Documentation/devicetree/bindings/net/qca,qca7000.txt +++ /dev/null @@ -1,87 +0,0 @@ -* Qualcomm QCA7000 - -The QCA7000 is a serial-to-powerline bridge with a host interface which could -be configured either as SPI or UART slave. This configuration is done by -the QCA7000 firmware. - -(a) Ethernet over SPI - -In order to use the QCA7000 as SPI device it must be defined as a child of a -SPI master in the device tree. - -Required properties: -- compatible : Should be "qca,qca7000" -- reg : Should specify the SPI chip select -- interrupts : The first cell should specify the index of the source - interrupt and the second cell should specify the trigger - type as rising edge -- spi-cpha : Must be set -- spi-cpol : Must be set - -Optional properties: -- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at. - Numbers smaller than 1000000 or greater than 16000000 - are invalid. Missing the property will set the SPI - frequency to 8000000 Hertz. -- qca,legacy-mode : Set the SPI data transfer of the QCA7000 to legacy mode. - In this mode the SPI master must toggle the chip select - between each data word. In burst mode these gaps aren't - necessary, which is faster. This setting depends on how - the QCA7000 is setup via GPIO pin strapping. If the - property is missing the driver defaults to burst mode. - -The MAC address will be determined using the optional properties -defined in ethernet.txt. - -SPI Example: - -/* Freescale i.MX28 SPI master*/ -ssp2: spi@80014000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,imx28-spi"; - pinctrl-names = "default"; - pinctrl-0 = <&spi2_pins_a>; - - qca7000: ethernet@0 { - compatible = "qca,qca7000"; - reg = <0x0>; - interrupt-parent = <&gpio3>; /* GPIO Bank 3 */ - interrupts = <25 0x1>; /* Index: 25, rising edge */ - spi-cpha; /* SPI mode: CPHA=1 */ - spi-cpol; /* SPI mode: CPOL=1 */ - spi-max-frequency = <8000000>; /* freq: 8 MHz */ - local-mac-address = [ A0 B0 C0 D0 E0 F0 ]; - }; -}; - -(b) Ethernet over UART - -In order to use the QCA7000 as UART slave it must be defined as a child of a -UART master in the device tree. It is possible to preconfigure the UART -settings of the QCA7000 firmware, but it's not possible to change them during -runtime. - -Required properties: -- compatible : Should be "qca,qca7000" - -Optional properties: -- local-mac-address : see ./ethernet.txt -- current-speed : current baud rate of QCA7000 which defaults to 115200 - if absent, see also ../serial/serial.yaml - -UART Example: - -/* Freescale i.MX28 UART */ -auart0: serial@8006a000 { - compatible = "fsl,imx28-auart", "fsl,imx23-auart"; - reg = <0x8006a000 0x2000>; - pinctrl-names = "default"; - pinctrl-0 = <&auart0_2pins_a>; - - qca7000: ethernet { - compatible = "qca,qca7000"; - local-mac-address = [ A0 B0 C0 D0 E0 F0 ]; - current-speed = <38400>; - }; -}; diff --git a/Documentation/devicetree/bindings/net/qca,qca7000.yaml b/Documentation/devicetree/bindings/net/qca,qca7000.yaml new file mode 100644 index 000000000000..b503c3aa3616 --- /dev/null +++ b/Documentation/devicetree/bindings/net/qca,qca7000.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/qca,qca7000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA7000 + +maintainers: + - Frank Li + +description: | + The QCA7000 is a serial-to-powerline bridge with a host interface which could + be configured either as SPI or UART slave. This configuration is done by + the QCA7000 firmware. + + (a) Ethernet over SPI + + In order to use the QCA7000 as SPI device it must be defined as a child of a + SPI master in the device tree. + + (b) Ethernet over UART + + In order to use the QCA7000 as UART slave it must be defined as a child of a + UART master in the device tree. It is possible to preconfigure the UART + settings of the QCA7000 firmware, but it's not possible to change them during + runtime + +properties: + compatible: + const: qca,qca7000 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + qca,legacy-mode: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set the SPI data transfer of the QCA7000 to legacy mode. + In this mode the SPI master must toggle the chip select + between each data word. In burst mode these gaps aren't + necessary, which is faster. This setting depends on how + the QCA7000 is setup via GPIO pin strapping. If the + property is missing the driver defaults to burst mode. + +allOf: + - $ref: ethernet-controller.yaml# + + - if: + required: + - reg + + then: + properties: + spi-cpha: true + + spi-cpol: true + + spi-max-frequency: + default: 8000000 + maximum: 16000000 + minimum: 1000000 + + allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + + else: + properties: + current-speed: + default: 115200 + + qca,legacy-mode: false + + allOf: + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + ethernet@0 { + compatible = "qca,qca7000"; + reg = <0x0>; + interrupt-parent = <&gpio3>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + spi-cpha; + spi-cpol; + spi-max-frequency = <8000000>; + local-mac-address = [ a0 b0 c0 d0 e0 f0 ]; + }; + }; + + - | + serial { + ethernet { + compatible = "qca,qca7000"; + local-mac-address = [ a0 b0 c0 d0 e0 f0 ]; + current-speed = <38400>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/renesas,r9a09g057-gbeth.yaml b/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml similarity index 97% rename from Documentation/devicetree/bindings/net/renesas,r9a09g057-gbeth.yaml rename to Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml index c498a9999289..23e39bcea96b 100644 --- a/Documentation/devicetree/bindings/net/renesas,r9a09g057-gbeth.yaml +++ b/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/net/renesas,r9a09g057-gbeth.yaml# +$id: http://devicetree.org/schemas/net/renesas,rzv2h-gbeth.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: GBETH glue layer for Renesas RZ/V2H(P) (and similar SoCs) @@ -14,6 +14,7 @@ select: compatible: contains: enum: + - renesas,r9a09g047-gbeth - renesas,r9a09g056-gbeth - renesas,r9a09g057-gbeth - renesas,rzv2h-gbeth @@ -24,6 +25,7 @@ properties: compatible: items: - enum: + - renesas,r9a09g047-gbeth # RZ/G3E - renesas,r9a09g056-gbeth # RZ/V2N - renesas,r9a09g057-gbeth # RZ/V2H(P) - const: renesas,rzv2h-gbeth diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 90b79283e228..4e3cbaa06229 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -30,6 +30,7 @@ select: - snps,dwmac-4.00 - snps,dwmac-4.10a - snps,dwmac-4.20a + - snps,dwmac-5.00a - snps,dwmac-5.10a - snps,dwmac-5.20 - snps,dwmac-5.30a @@ -98,11 +99,13 @@ properties: - snps,dwmac-4.00 - snps,dwmac-4.10a - snps,dwmac-4.20a + - snps,dwmac-5.00a - snps,dwmac-5.10a - snps,dwmac-5.20 - snps,dwmac-5.30a - snps,dwxgmac - snps,dwxgmac-2.10 + - sophgo,sg2042-dwmac - sophgo,sg2044-dwmac - starfive,jh7100-dwmac - starfive,jh7110-dwmac @@ -641,6 +644,7 @@ allOf: - snps,dwmac-4.00 - snps,dwmac-4.10a - snps,dwmac-4.20a + - snps,dwmac-5.00a - snps,dwmac-5.10a - snps,dwmac-5.20 - snps,dwmac-5.30a diff --git a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt deleted file mode 100644 index 612a8e8abc88..000000000000 --- a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt +++ /dev/null @@ -1,57 +0,0 @@ -Altera SOCFPGA SoC DWMAC controller - -This is a variant of the dwmac/stmmac driver an inherits all descriptions -present in Documentation/devicetree/bindings/net/stmmac.txt. - -The device node has additional properties: - -Required properties: - - compatible : For Cyclone5/Arria5 SoCs it should contain - "altr,socfpga-stmmac". For Arria10/Agilex/Stratix10 SoCs - "altr,socfpga-stmmac-a10-s10". - Along with "snps,dwmac" and any applicable more detailed - designware version numbers documented in stmmac.txt - - altr,sysmgr-syscon : Should be the phandle to the system manager node that - encompasses the glue register, the register offset, and the register shift. - On Cyclone5/Arria5, the register shift represents the PHY mode bits, while - on the Arria10/Stratix10/Agilex platforms, the register shift represents - bit for each emac to enable/disable signals from the FPGA fabric to the - EMAC modules. - - altr,f2h_ptp_ref_clk use f2h_ptp_ref_clk instead of default eosc1 clock - for ptp ref clk. This affects all emacs as the clock is common. - -Optional properties: -altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if - DWMAC controller is connected emac splitter. -phy-mode: The phy mode the ethernet operates in -altr,sgmii-to-sgmii-converter: phandle to the TSE SGMII converter - -This device node has additional phandle dependency, the sgmii converter: - -Required properties: - - compatible : Should be altr,gmii-to-sgmii-2.0 - - reg-names : Should be "eth_tse_control_port" - -Example: - -gmii_to_sgmii_converter: phy@100000240 { - compatible = "altr,gmii-to-sgmii-2.0"; - reg = <0x00000001 0x00000240 0x00000008>, - <0x00000001 0x00000200 0x00000040>; - reg-names = "eth_tse_control_port"; - clocks = <&sgmii_1_clk_0 &emac1 1 &sgmii_clk_125 &sgmii_clk_125>; - clock-names = "tse_pcs_ref_clk_clock_connection", "tse_rx_cdr_refclk"; -}; - -gmac0: ethernet@ff700000 { - compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", "snps,dwmac"; - altr,sysmgr-syscon = <&sysmgr 0x60 0>; - reg = <0xff700000 0x2000>; - interrupts = <0 115 4>; - interrupt-names = "macirq"; - mac-address = [00 00 00 00 00 00];/* Filled in by U-Boot */ - clocks = <&emac_0_clk>; - clock-names = "stmmaceth"; - phy-mode = "sgmii"; - altr,gmii-to-sgmii-converter = <&gmii_to_sgmii_converter>; -}; diff --git a/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml b/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml new file mode 100644 index 000000000000..b89456f0ef83 --- /dev/null +++ b/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/sophgo,cv1800b-dwmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo CV1800B DWMAC glue layer + +maintainers: + - Inochi Amaoto + +select: + properties: + compatible: + contains: + enum: + - sophgo,cv1800b-dwmac + required: + - compatible + +properties: + compatible: + items: + - const: sophgo,cv1800b-dwmac + - const: snps,dwmac-3.70a + + reg: + maxItems: 1 + + clocks: + items: + - description: GMAC main clock + - description: PTP clock + + clock-names: + items: + - const: stmmaceth + - const: ptp_ref + + interrupts: + maxItems: 1 + + interrupt-names: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: stmmaceth + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - resets + - reset-names + +allOf: + - $ref: snps,dwmac.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + ethernet@4070000 { + compatible = "sophgo,cv1800b-dwmac", "snps,dwmac-3.70a"; + reg = <0x04070000 0x10000>; + clocks = <&clk 35>, <&clk 36>; + clock-names = "stmmaceth", "ptp_ref"; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + phy-handle = <&internal_ephy>; + phy-mode = "internal"; + resets = <&rst 12>; + reset-names = "stmmaceth"; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,multicast-filter-bins = <0>; + snps,perfect-filter-entries = <1>; + snps,aal; + snps,txpbl = <8>; + snps,rxpbl = <8>; + snps,mtl-rx-config = <&gmac0_mtl_rx_setup>; + snps,mtl-tx-config = <&gmac0_mtl_tx_setup>; + snps,axi-config = <&gmac0_stmmac_axi_setup>; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac0_mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <1>; + queue0 {}; + }; + + gmac0_mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <1>; + queue0 {}; + }; + + gmac0_stmmac_axi_setup: stmmac-axi-config { + snps,blen = <16 8 4 0 0 0 0>; + snps,rd_osr_lmt = <2>; + snps,wr_osr_lmt = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml index 4dd2dc9c678b..ce21979a2d9a 100644 --- a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml @@ -15,14 +15,19 @@ select: contains: enum: - sophgo,sg2044-dwmac + - sophgo,sg2042-dwmac required: - compatible properties: compatible: - items: - - const: sophgo,sg2044-dwmac - - const: snps,dwmac-5.30a + oneOf: + - items: + - const: sophgo,sg2042-dwmac + - const: snps,dwmac-5.00a + - items: + - const: sophgo,sg2044-dwmac + - const: snps,dwmac-5.30a reg: maxItems: 1 @@ -80,6 +85,8 @@ examples: interrupt-parent = <&intc>; interrupts = <296 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "macirq"; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; resets = <&rst 30>; reset-names = "stmmaceth"; snps,multicast-filter-bins = <0>; @@ -91,7 +98,6 @@ examples: snps,mtl-rx-config = <&gmac0_mtl_rx_setup>; snps,mtl-tx-config = <&gmac0_mtl_tx_setup>; snps,axi-config = <&gmac0_stmmac_axi_setup>; - status = "disabled"; gmac0_mtl_rx_setup: rx-queues-config { snps,rx-queues-to-use = <8>; diff --git a/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml b/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml index 6d9de3303762..b3492a9aa4ef 100644 --- a/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml +++ b/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml @@ -62,11 +62,13 @@ properties: items: - description: GMAC main clock - description: Peripheral registers interface clock + - description: APB glue registers interface clock clock-names: items: - const: stmmaceth - const: pclk + - const: apb interrupts: items: @@ -88,8 +90,8 @@ examples: compatible = "thead,th1520-gmac", "snps,dwmac-3.70a"; reg = <0xe7070000 0x2000>, <0xec003000 0x1000>; reg-names = "dwmac", "apb"; - clocks = <&clk 1>, <&clk 2>; - clock-names = "stmmaceth", "pclk"; + clocks = <&clk 1>, <&clk 2>, <&clk 3>; + clock-names = "stmmaceth", "pclk", "apb"; interrupts = <66>; interrupt-names = "macirq"; phy-mode = "rgmii-id"; diff --git a/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml b/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml index 7b3d948f187d..a959c1d7e643 100644 --- a/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml +++ b/Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml @@ -284,7 +284,7 @@ examples: ti,syscon-efuse = <&mcu_conf 0x200>; phys = <&phy_gmii_sel 1>; - phy-mode = "rgmii-rxid"; + phy-mode = "rgmii-id"; phy-handle = <&phy0>; }; }; diff --git a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml index 0e5412cff2bc..d16ca8e0a25d 100644 --- a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml @@ -12,7 +12,7 @@ maintainers: description: | This node provides properties for configuring the ath9k wireless device. The node is expected to be specified as a child node of the PCI controller - to which the wireless chip is connected. + or AHB bus to which the wireless chip is connected. allOf: - $ref: ieee80211.yaml# @@ -35,6 +35,12 @@ properties: - pci168c,0034 # AR9462 - pci168c,0036 # AR9565 - pci168c,0037 # AR1111 and AR9485 + - qca,ar9130-wifi + - qca,ar9330-wifi + - qca,ar9340-wifi + - qca,qca9530-wifi + - qca,qca9550-wifi + - qca,qca9560-wifi reg: maxItems: 1 @@ -88,3 +94,13 @@ examples: nvmem-cell-names = "mac-address", "calibration"; }; }; + - | + ahb { + #address-cells = <1>; + #size-cells = <1>; + wifi@180c0000 { + compatible = "qca,ar9130-wifi"; + reg = <0x180c0000 0x230000>; + interrupts = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml index 653b319fee88..e34d42a30192 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml @@ -35,6 +35,12 @@ properties: string to uniquely identify variant of the calibration data for designs with colliding bus and device ids + firmware-name: + maxItems: 1 + description: + If present, a board or platform specific string used to lookup + usecase-specific firmware files for the device. + vddrfacmn-supply: description: VDD_RFA_CMN supply regulator handle diff --git a/Documentation/devicetree/bindings/net/wireless/ralink,rt2880.yaml b/Documentation/devicetree/bindings/net/wireless/ralink,rt2880.yaml new file mode 100644 index 000000000000..04dc5bb2edcc --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/ralink,rt2880.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/ralink,rt2880.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ralink RT2880 wireless device + +maintainers: + - Stanislaw Gruszka + +description: | + This node provides properties for configuring RT2880 SOC wifi devices. + The node is expected to be specified as a root node of the device. + +allOf: + - $ref: ieee80211.yaml# + +properties: + compatible: + enum: + - ralink,rt2880-wifi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +additionalProperties: false + +examples: + - | + wifi@110180000 { + compatible = "ralink,rt2880-wifi"; + reg = <0x10180000 0x40000>; + clocks = <&sysc 16>; + interrupt-parent = <&cpuintc>; + interrupts = <6>; + }; diff --git a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml new file mode 100644 index 000000000000..caca2a4903cd --- /dev/null +++ b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/npu/rockchip,rk3588-rknn-core.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Neural Processing Unit IP from Rockchip + +maintainers: + - Tomeu Vizoso + +description: + Rockchip IP for accelerating inference of neural networks. + + There is to be a node per each NPU core in the SoC, and each core should reference all the + resources that it needs to function, such as clocks, power domains, and resets. + +properties: + $nodename: + pattern: '^npu@[a-f0-9]+$' + + compatible: + enum: + - rockchip,rk3588-rknn-core + + reg: + maxItems: 3 + + reg-names: + items: + - const: pc # Program Control-related registers + - const: cna # Convolution Neural Network Accelerator registers + - const: core # Main NPU core processing unit registers + + clocks: + maxItems: 4 + + clock-names: + items: + - const: aclk + - const: hclk + - const: npu + - const: pclk + + interrupts: + maxItems: 1 + + iommus: + maxItems: 1 + + npu-supply: true + + power-domains: + maxItems: 1 + + resets: + maxItems: 2 + + reset-names: + items: + - const: srst_a + - const: srst_h + + sram-supply: true + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - iommus + - power-domains + - resets + - reset-names + - npu-supply + - sram-supply + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + npu@fdab0000 { + compatible = "rockchip,rk3588-rknn-core"; + reg = <0x0 0xfdab0000 0x0 0x1000>, + <0x0 0xfdab1000 0x0 0x1000>, + <0x0 0xfdab3000 0x0 0x1000>; + reg-names = "pc", "cna", "core"; + clocks = <&cru ACLK_NPU0>, <&cru HCLK_NPU0>, + <&scmi_clk SCMI_CLK_NPU>, <&cru PCLK_NPU_ROOT>; + clock-names = "aclk", "hclk", "npu", "pclk"; + interrupts = ; + iommus = <&rknn_mmu_0>; + npu-supply = <&vdd_npu_s0>; + power-domains = <&power RK3588_PD_NPUTOP>; + resets = <&cru SRST_A_RKNN0>, <&cru SRST_H_RKNN0>; + reset-names = "srst_a", "srst_h"; + sram-supply = <&vdd_npu_mem_s0>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml index 4424c3c5e75c..f67470b8a2ed 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -27,6 +27,7 @@ properties: - enum: - allwinner,sun50i-a100-sid - allwinner,sun50i-h616-sid + - allwinner,sun55i-a523-sid - const: allwinner,sun50i-a64-sid - const: allwinner,sun50i-h5-sid - const: allwinner,sun50i-h6-sid diff --git a/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml b/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml index b5cf740f96fa..9879d521842e 100644 --- a/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml @@ -53,6 +53,6 @@ examples: }; temperature_calib: calib@1f4 { - reg = <0x1f4 0x4>; + reg = <0x1f4 0x4>; }; }; diff --git a/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml new file mode 100644 index 000000000000..5aef86a752a6 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/fsl,vf610-ocotp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: On-Chip OTP Memory for Freescale Vybrid + +maintainers: + - Frank Li + +allOf: + - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml + +properties: + compatible: + items: + - enum: + - fsl,vf610-ocotp + - const: syscon + + reg: + maxItems: 1 + + clocks: + items: + - description: ipg clock we associate with the OCOTP peripheral + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + + ocotp@400a5000 { + compatible = "fsl,vf610-ocotp", "syscon"; + reg = <0x400a5000 0xcf0>; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&clks VF610_CLK_OCOTP>; + }; diff --git a/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml b/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml index 9bd34bd5af30..b01567f99284 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml @@ -27,7 +27,7 @@ properties: const: 1 patternProperties: - "@[a-f0-9]+$": + "@[a-f0-9]+(,[0-7])?$": type: object $ref: fixed-cell.yaml unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt b/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt deleted file mode 100644 index 809df68f6e14..000000000000 --- a/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt +++ /dev/null @@ -1,28 +0,0 @@ -* NXP LPC18xx EEPROM memory NVMEM driver - -Required properties: - - compatible: Should be "nxp,lpc1857-eeprom" - - reg: Must contain an entry with the physical base address and length - for each entry in reg-names. - - reg-names: Must include the following entries. - - reg: EEPROM registers. - - mem: EEPROM address space. - - clocks: Must contain an entry for each entry in clock-names. - - clock-names: Must include the following entries. - - eeprom: EEPROM operating clock. - - resets: Should contain a reference to the reset controller asserting - the EEPROM in reset. - - interrupts: Should contain EEPROM interrupt. - -Example: - - eeprom: eeprom@4000e000 { - compatible = "nxp,lpc1857-eeprom"; - reg = <0x4000e000 0x1000>, - <0x20040000 0x4000>; - reg-names = "reg", "mem"; - clocks = <&ccu1 CLK_CPU_EEPROM>; - clock-names = "eeprom"; - resets = <&rgu 27>; - interrupts = <4>; - }; diff --git a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml index 32b8c1eb4e80..4dc0d42df3e6 100644 --- a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml @@ -24,6 +24,21 @@ properties: compatible: oneOf: + - items: + - const: mediatek,mt8188-efuse + - const: mediatek,mt8186-efuse + - const: mediatek,mt8186-efuse + + - items: + - enum: + - mediatek,mt8186-efuse + - mediatek,mt8188-efuse + - const: mediatek,efuse + deprecated: true + description: Some compatibles also imply a decoding scheme for the + "gpu-speedbin" cell, and thus are not backward compatible to the + generic "mediatek,efuse" compatible. + - items: - enum: - mediatek,mt7622-efuse @@ -33,8 +48,6 @@ properties: - mediatek,mt7988-efuse - mediatek,mt8173-efuse - mediatek,mt8183-efuse - - mediatek,mt8186-efuse - - mediatek,mt8188-efuse - mediatek,mt8192-efuse - mediatek,mt8195-efuse - mediatek,mt8516-efuse diff --git a/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml b/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml new file mode 100644 index 000000000000..24c71252846f --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/nxp,lpc1857-eeprom.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx EEPROM memory + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1857-eeprom + + reg: + maxItems: 2 + + reg-names: + items: + - const: reg + - const: mem + + clocks: + maxItems: 1 + + clock-names: + items: + - const: eeprom + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - resets + +additionalProperties: false + +examples: + - | + #include + + eeprom@4000e000 { + compatible = "nxp,lpc1857-eeprom"; + reg = <0x4000e000 0x1000>, + <0x20040000 0x4000>; + reg-names = "reg", "mem"; + clocks = <&ccu1 CLK_CPU_EEPROM>; + clock-names = "eeprom"; + resets = <&rgu 27>; + interrupts = <4>; + }; diff --git a/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt b/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt deleted file mode 100644 index 72ba628f6d0b..000000000000 --- a/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt +++ /dev/null @@ -1,19 +0,0 @@ -On-Chip OTP Memory for Freescale Vybrid - -Required Properties: - compatible: - - "fsl,vf610-ocotp", "syscon" for VF5xx/VF6xx - #address-cells : Should be 1 - #size-cells : Should be 1 - reg : Address and length of OTP controller and fuse map registers - clocks : ipg clock we associate with the OCOTP peripheral - -Example for Vybrid VF5xx/VF6xx: - - ocotp: ocotp@400a5000 { - compatible = "fsl,vf610-ocotp", "syscon"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x400a5000 0xCF0>; - clocks = <&clks VF610_CLK_OCOTP>; - }; diff --git a/Documentation/devicetree/bindings/pci/83xx-512x-pci.txt b/Documentation/devicetree/bindings/pci/83xx-512x-pci.txt deleted file mode 100644 index 3abeecf4983f..000000000000 --- a/Documentation/devicetree/bindings/pci/83xx-512x-pci.txt +++ /dev/null @@ -1,39 +0,0 @@ -* Freescale 83xx and 512x PCI bridges - -Freescale 83xx and 512x SOCs include the same PCI bridge core. - -83xx/512x specific notes: -- reg: should contain two address length tuples - The first is for the internal PCI bridge registers - The second is for the PCI config space access registers - -Example (MPC8313ERDB) - pci0: pci@e0008500 { - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - /* IDSEL 0x0E -mini PCI */ - 0x7000 0x0 0x0 0x1 &ipic 18 0x8 - 0x7000 0x0 0x0 0x2 &ipic 18 0x8 - 0x7000 0x0 0x0 0x3 &ipic 18 0x8 - 0x7000 0x0 0x0 0x4 &ipic 18 0x8 - - /* IDSEL 0x0F - PCI slot */ - 0x7800 0x0 0x0 0x1 &ipic 17 0x8 - 0x7800 0x0 0x0 0x2 &ipic 18 0x8 - 0x7800 0x0 0x0 0x3 &ipic 17 0x8 - 0x7800 0x0 0x0 0x4 &ipic 18 0x8>; - interrupt-parent = <&ipic>; - interrupts = <66 0x8>; - bus-range = <0x0 0x0>; - ranges = <0x02000000 0x0 0x90000000 0x90000000 0x0 0x10000000 - 0x42000000 0x0 0x80000000 0x80000000 0x0 0x10000000 - 0x01000000 0x0 0x00000000 0xe2000000 0x0 0x00100000>; - clock-frequency = <66666666>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <0xe0008500 0x100 /* internal registers */ - 0xe0008300 0x8>; /* config space access registers */ - compatible = "fsl,mpc8349-pci"; - device_type = "pci"; - }; diff --git a/Documentation/devicetree/bindings/pci/aardvark-pci.txt b/Documentation/devicetree/bindings/pci/aardvark-pci.txt deleted file mode 100644 index 2b8ca920a7fa..000000000000 --- a/Documentation/devicetree/bindings/pci/aardvark-pci.txt +++ /dev/null @@ -1,59 +0,0 @@ -Aardvark PCIe controller - -This PCIe controller is used on the Marvell Armada 3700 ARM64 SoC. - -The Device Tree node describing an Aardvark PCIe controller must -contain the following properties: - - - compatible: Should be "marvell,armada-3700-pcie" - - reg: range of registers for the PCIe controller - - interrupts: the interrupt line of the PCIe controller - - #address-cells: set to <3> - - #size-cells: set to <2> - - device_type: set to "pci" - - ranges: ranges for the PCI memory and I/O regions - - #interrupt-cells: set to <1> - - msi-controller: indicates that the PCIe controller can itself - handle MSI interrupts - - msi-parent: pointer to the MSI controller to be used - - interrupt-map-mask and interrupt-map: standard PCI properties to - define the mapping of the PCIe interface to interrupt numbers. - - bus-range: PCI bus numbers covered - - phys: the PCIe PHY handle - - max-link-speed: see pci.txt - - reset-gpios: see pci.txt - -In addition, the Device Tree describing an Aardvark PCIe controller -must include a sub-node that describes the legacy interrupt controller -built into the PCIe controller. This sub-node must have the following -properties: - - - interrupt-controller - - #interrupt-cells: set to <1> - -Example: - - pcie0: pcie@d0070000 { - compatible = "marvell,armada-3700-pcie"; - device_type = "pci"; - reg = <0 0xd0070000 0 0x20000>; - #address-cells = <3>; - #size-cells = <2>; - bus-range = <0x00 0xff>; - interrupts = ; - #interrupt-cells = <1>; - msi-controller; - msi-parent = <&pcie0>; - ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ - 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0 0 0 1 &pcie_intc 0>, - <0 0 0 2 &pcie_intc 1>, - <0 0 0 3 &pcie_intc 2>, - <0 0 0 4 &pcie_intc 3>; - phys = <&comphy1 0>; - pcie_intc: interrupt-controller { - interrupt-controller; - #interrupt-cells = <1>; - }; - }; diff --git a/Documentation/devicetree/bindings/pci/amazon,al-alpine-v3-pcie.yaml b/Documentation/devicetree/bindings/pci/amazon,al-alpine-v3-pcie.yaml new file mode 100644 index 000000000000..45244cad5f30 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/amazon,al-alpine-v3-pcie.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/amazon,al-alpine-v3-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amazon Annapurna Labs Alpine v3 PCIe Host Bridge + +maintainers: + - Jonathan Chocron + +description: + Amazon's Annapurna Labs PCIe Host Controller is based on the Synopsys + DesignWare PCI controller. + +allOf: + - $ref: snps,dw-pcie.yaml# + +properties: + compatible: + enum: + - amazon,al-alpine-v2-pcie + - amazon,al-alpine-v3-pcie + + reg: + items: + - description: PCIe ECAM space + - description: AL proprietary registers + - description: Designware PCIe registers + + reg-names: + items: + - const: config + - const: controller + - const: dbi + + interrupts: + maxItems: 1 + +unevaluatedProperties: false + +required: + - compatible + - reg + - reg-names + +examples: + - | + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie@fb600000 { + compatible = "amazon,al-alpine-v3-pcie"; + reg = <0x0 0xfb600000 0x0 0x00100000 + 0x0 0xfd800000 0x0 0x00010000 + 0x0 0xfd810000 0x0 0x00001000>; + reg-names = "config", "controller", "dbi"; + bus-range = <0 255>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + interrupts = ; + interrupt-map-mask = <0x00 0 0 7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>; /* INTa */ + ranges = <0x02000000 0x0 0xc0010000 0x0 0xc0010000 0x0 0x07ff0000>; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/apm,xgene-pcie.yaml b/Documentation/devicetree/bindings/pci/apm,xgene-pcie.yaml new file mode 100644 index 000000000000..2504b8235889 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/apm,xgene-pcie.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/apm,xgene-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AppliedMicro X-Gene PCIe interface + +maintainers: + - Toan Le + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + +properties: + compatible: + oneOf: + - items: + - const: apm,xgene-storm-pcie + - const: apm,xgene-pcie + - items: + - const: apm,xgene-pcie + + reg: + items: + - description: Controller configuration registers + - description: PCI configuration space registers + + reg-names: + items: + - const: csr + - const: cfg + + clocks: + maxItems: 1 + + clock-names: + items: + - const: pcie + + dma-coherent: true + + msi-parent: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - '#interrupt-cells' + - interrupt-map-mask + - interrupt-map + - clocks + +unevaluatedProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie@1f2b0000 { + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0x00 0x1f2b0000 0x0 0x00010000>, /* Controller registers */ + <0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000>, /* io */ + <0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000>, + <0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1>, + <0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1>, + <0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1>, + <0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; + dma-coherent; + clocks = <&pcie0clk 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.txt b/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.txt deleted file mode 100644 index cc6dcdb676b9..000000000000 --- a/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.txt +++ /dev/null @@ -1,50 +0,0 @@ -* Axis ARTPEC-6 PCIe interface - -This PCIe host controller is based on the Synopsys DesignWare PCIe IP -and thus inherits all the common properties defined in snps,dw-pcie.yaml. - -Required properties: -- compatible: "axis,artpec6-pcie", "snps,dw-pcie" for ARTPEC-6 in RC mode; - "axis,artpec6-pcie-ep", "snps,dw-pcie" for ARTPEC-6 in EP mode; - "axis,artpec7-pcie", "snps,dw-pcie" for ARTPEC-7 in RC mode; - "axis,artpec7-pcie-ep", "snps,dw-pcie" for ARTPEC-7 in EP mode; -- reg: base addresses and lengths of the PCIe controller (DBI), - the PHY controller, and configuration address space. -- reg-names: Must include the following entries: - - "dbi" - - "phy" - - "config" -- interrupts: A list of interrupt outputs of the controller. Must contain an - entry for each entry in the interrupt-names property. -- interrupt-names: Must include the following entries: - - "msi": The interrupt that is asserted when an MSI is received -- axis,syscon-pcie: A phandle pointing to the ARTPEC-6 system controller, - used to enable and control the Synopsys IP. - -Example: - - pcie@f8050000 { - compatible = "axis,artpec6-pcie", "snps,dw-pcie"; - reg = <0xf8050000 0x2000 - 0xf8040000 0x1000 - 0xc0000000 0x2000>; - reg-names = "dbi", "phy", "config"; - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - /* downstream I/O */ - ranges = <0x81000000 0 0 0xc0002000 0 0x00010000 - /* non-prefetchable memory */ - 0x82000000 0 0xc0012000 0xc0012000 0 0x1ffee000>; - num-lanes = <2>; - bus-range = <0x00 0xff>; - interrupts = ; - interrupt-names = "msi"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>; - axis,syscon-pcie = <&syscon>; - }; diff --git a/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.yaml b/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.yaml new file mode 100644 index 000000000000..dcc5661aa004 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/axis,artpec6-pcie.yaml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Axis AB +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/axis,artpec6-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axis ARTPEC-6 PCIe host controller + +maintainers: + - Jesper Nilsson + +description: + This PCIe host controller is based on the Synopsys DesignWare PCIe IP. + +select: + properties: + compatible: + contains: + enum: + - axis,artpec6-pcie + - axis,artpec6-pcie-ep + - axis,artpec7-pcie + - axis,artpec7-pcie-ep + required: + - compatible + +properties: + compatible: + items: + - enum: + - axis,artpec6-pcie + - axis,artpec6-pcie-ep + - axis,artpec7-pcie + - axis,artpec7-pcie-ep + - const: snps,dw-pcie + + reg: + minItems: 3 + maxItems: 4 + + reg-names: + minItems: 3 + maxItems: 4 + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + + axis,syscon-pcie: + $ref: /schemas/types.yaml#/definitions/phandle + description: + System controller phandle used to enable and control the Synopsys IP. + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - axis,syscon-pcie + +oneOf: + - $ref: snps,dw-pcie.yaml# + properties: + reg: + maxItems: 3 + + reg-names: + items: + - const: dbi + - const: phy + - const: config + + - $ref: snps,dw-pcie-ep.yaml# + properties: + reg: + minItems: 4 + + reg-names: + items: + - const: dbi + - const: dbi2 + - const: phy + - const: addr_space + +unevaluatedProperties: false + +examples: + - | + #include + + pcie@f8050000 { + compatible = "axis,artpec6-pcie", "snps,dw-pcie"; + device_type = "pci"; + reg = <0xf8050000 0x2000 + 0xf8040000 0x1000 + 0xc0000000 0x2000>; + reg-names = "dbi", "phy", "config"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x81000000 0 0 0xc0002000 0 0x00010000>, + <0x82000000 0 0xc0012000 0xc0012000 0 0x1ffee000>; + num-lanes = <2>; + bus-range = <0x00 0xff>; + interrupts = ; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>; + axis,syscon-pcie = <&syscon>; + }; diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index c4f9674e8695..812ef5957cfc 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -107,6 +107,10 @@ properties: - const: bridge - const: swinit + num-lanes: + default: 1 + maximum: 4 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/pci/marvell,armada-3700-pcie.yaml b/Documentation/devicetree/bindings/pci/marvell,armada-3700-pcie.yaml new file mode 100644 index 000000000000..68090b3ca419 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/marvell,armada-3700-pcie.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/marvell,armada-3700-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 3700 (Aardvark) PCIe Controller + +maintainers: + - Thomas Petazzoni + - Pali Rohár + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + +properties: + compatible: + const: marvell,armada-3700-pcie + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + msi-controller: true + + msi-parent: + maxItems: 1 + + phys: + maxItems: 1 + + reset-gpios: + description: PCIe reset GPIO signals. + + interrupt-controller: + type: object + additionalProperties: false + + properties: + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + required: + - interrupt-controller + - '#interrupt-cells' + +required: + - compatible + - reg + - interrupts + - '#interrupt-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie@d0070000 { + compatible = "marvell,armada-3700-pcie"; + device_type = "pci"; + reg = <0 0xd0070000 0 0x20000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + interrupts = ; + msi-controller; + msi-parent = <&pcie0>; + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000>, + <0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + phys = <&comphy1 0>; + max-link-speed = <2>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/pci-ep.yaml b/Documentation/devicetree/bindings/pci/pci-ep.yaml index 214caa4ec3d5..1868a10d5b10 100644 --- a/Documentation/devicetree/bindings/pci/pci-ep.yaml +++ b/Documentation/devicetree/bindings/pci/pci-ep.yaml @@ -51,7 +51,7 @@ properties: max-link-speed: $ref: /schemas/types.yaml#/definitions/uint32 - enum: [ 1, 2, 3, 4 ] + enum: [ 1, 2, 3, 4, 5, 6 ] msi-map: description: | diff --git a/Documentation/devicetree/bindings/pci/pcie-al.txt b/Documentation/devicetree/bindings/pci/pcie-al.txt deleted file mode 100644 index 2ad1fe466eab..000000000000 --- a/Documentation/devicetree/bindings/pci/pcie-al.txt +++ /dev/null @@ -1,46 +0,0 @@ -* Amazon Annapurna Labs PCIe host bridge - -Amazon's Annapurna Labs PCIe Host Controller is based on the Synopsys DesignWare -PCI core. It inherits common properties defined in -Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml. - -Properties of the host controller node that differ from it are: - -- compatible: - Usage: required - Value type: - Definition: Value should contain - - "amazon,al-alpine-v2-pcie" for alpine_v2 - - "amazon,al-alpine-v3-pcie" for alpine_v3 - -- reg: - Usage: required - Value type: - Definition: Register ranges as listed in the reg-names property - -- reg-names: - Usage: required - Value type: - Definition: Must include the following entries - - "config" PCIe ECAM space - - "controller" AL proprietary registers - - "dbi" Designware PCIe registers - -Example: - - pcie-external0: pcie@fb600000 { - compatible = "amazon,al-alpine-v3-pcie"; - reg = <0x0 0xfb600000 0x0 0x00100000 - 0x0 0xfd800000 0x0 0x00010000 - 0x0 0xfd810000 0x0 0x00001000>; - reg-names = "config", "controller", "dbi"; - bus-range = <0 255>; - device_type = "pci"; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - interrupts = ; - interrupt-map-mask = <0x00 0 0 7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>; /* INTa */ - ranges = <0x02000000 0x0 0xc0010000 0x0 0xc0010000 0x0 0x07ff0000>; - }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml index 0480c58f7d99..ab2509ec1c4b 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml @@ -51,10 +51,18 @@ properties: phys: maxItems: 1 + deprecated: true + description: + This property is deprecated, instead of referencing this property from + the host bridge node, use the property from the PCIe root port node. phy-names: items: - const: pciephy + deprecated: true + description: + Phandle to the register map node. This property is deprecated, and not + required to add in the root port also, as the root port has only one phy. power-domains: maxItems: 1 @@ -71,12 +79,18 @@ properties: maxItems: 12 perst-gpios: - description: GPIO controlled connection to PERST# signal + description: GPIO controlled connection to PERST# signal. This property is + deprecated, instead of referencing this property from the host bridge node, + use the reset-gpios property from the root port node. maxItems: 1 + deprecated: true wake-gpios: - description: GPIO controlled connection to WAKE# signal + description: GPIO controlled connection to WAKE# signal. This property is + deprecated, instead of referencing this property from the host bridge node, + use the property from the PCIe root port node. maxItems: 1 + deprecated: true vddpe-3v3-supply: description: PCIe endpoint power supply @@ -85,6 +99,20 @@ properties: opp-table: type: object +patternProperties: + "^pcie@": + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + phys: + maxItems: 1 + + unevaluatedProperties: false + required: - reg - reg-names diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml new file mode 100644 index 000000000000..ef705a02fcd9 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-sa8255p.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SA8255p based firmware managed and ECAM compliant PCIe Root Complex + +maintainers: + - Bjorn Andersson + - Manivannan Sadhasivam + +description: + Qualcomm SA8255p SoC PCIe root complex controller is based on the Synopsys + DesignWare PCIe IP which is managed by firmware, and configured in ECAM mode. + +properties: + compatible: + const: qcom,pcie-sa8255p + + reg: + description: + The base address and size of the ECAM area for accessing PCI + Configuration Space, as accessed from the parent bus. The base + address corresponds to the first bus in the "bus-range" property. If + no "bus-range" is specified, this will be bus 0 (the default). + maxItems: 1 + + ranges: + description: + As described in IEEE Std 1275-1994, but must provide at least a + definition of non-prefetchable memory. One or both of prefetchable Memory + may also be provided. + minItems: 1 + maxItems: 2 + + interrupts: + minItems: 8 + maxItems: 8 + + interrupt-names: + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + + power-domains: + maxItems: 1 + + dma-coherent: true + iommu-map: true + +required: + - compatible + - reg + - ranges + - power-domains + - interrupts + - interrupt-names + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pci@1c00000 { + compatible = "qcom,pcie-sa8255p"; + reg = <0x4 0x00000000 0 0x10000000>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x02000000 0x0 0x40100000 0x0 0x40100000 0x0 0x1ff00000>, + <0x43000000 0x4 0x10100000 0x4 0x10100000 0x0 0x40000000>; + bus-range = <0x00 0xff>; + dma-coherent; + linux,pci-domain = <0>; + power-domains = <&scmi5_pd 0>; + iommu-map = <0x0 &pcie_smmu 0x0000 0x1>, + <0x100 &pcie_smmu 0x0001 0x1>; + interrupt-parent = <&intc>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "msi0", "msi1", "msi2", "msi3", + "msi4", "msi5", "msi6", "msi7"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml index e3fa232da2ca..19afe2a03409 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml @@ -16,7 +16,12 @@ description: properties: compatible: - const: qcom,pcie-sa8775p + oneOf: + - const: qcom,pcie-sa8775p + - items: + - enum: + - qcom,pcie-qcs8300 + - const: qcom,pcie-sa8775p reg: minItems: 6 @@ -61,11 +66,14 @@ properties: - const: global resets: - maxItems: 1 + items: + - description: PCIe controller reset + - description: PCIe link down reset reset-names: items: - const: pci + - const: link_down required: - interconnects @@ -161,8 +169,10 @@ examples: power-domains = <&gcc PCIE_0_GDSC>; - resets = <&gcc GCC_PCIE_0_BCR>; - reset-names = "pci"; + resets = <&gcc GCC_PCIE_0_BCR>, + <&gcc GCC_PCIE_0_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; perst-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 0 GPIO_ACTIVE_HIGH>; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml index ff508f592a1a..4d0a91556603 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml @@ -165,9 +165,6 @@ examples: iommu-map = <0x0 &apps_smmu 0x1c80 0x1>, <0x100 &apps_smmu 0x1c81 0x1>; - phys = <&pcie1_phy>; - phy-names = "pciephy"; - pinctrl-names = "default"; pinctrl-0 = <&pcie1_clkreq_n>; @@ -176,7 +173,18 @@ examples: resets = <&gcc GCC_PCIE_1_BCR>; reset-names = "pci"; - perst-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; vddpe-3v3-supply = <&pp3300_ssd>; + pcie1_port0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + phys = <&pcie1_phy>; + + reset-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; + }; }; }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml index 331fc25d7a17..34a4d7b2c845 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml @@ -33,8 +33,8 @@ properties: - const: mhi # MHI registers clocks: - minItems: 8 - maxItems: 8 + minItems: 6 + maxItems: 6 clock-names: items: @@ -44,8 +44,6 @@ properties: - const: bus_master # Master AXI clock - const: bus_slave # Slave AXI clock - const: slave_q2a # Slave Q2A clock - - const: ref # REFERENCE clock - - const: tbu # PCIe TBU clock interrupts: minItems: 8 @@ -117,17 +115,13 @@ examples: <&gcc GCC_PCIE_0_CFG_AHB_CLK>, <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, - <&gcc GCC_PCIE_0_CLKREF_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "ref", - "tbu"; + "slave_q2a"; dma-coherent; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml index a604f2a79de3..26b247a41785 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml @@ -16,7 +16,12 @@ description: properties: compatible: - const: qcom,pcie-sm8150 + oneOf: + - const: qcom,pcie-sm8150 + - items: + - enum: + - qcom,pcie-qcs615 + - const: qcom,pcie-sm8150 reg: minItems: 5 @@ -33,8 +38,8 @@ properties: - const: mhi # MHI registers clocks: - minItems: 8 - maxItems: 8 + minItems: 6 + maxItems: 6 clock-names: items: @@ -44,8 +49,6 @@ properties: - const: bus_master # Master AXI clock - const: bus_slave # Slave AXI clock - const: slave_q2a # Slave Q2A clock - - const: tbu # PCIe TBU clock - - const: ref # REFERENCE clock interrupts: minItems: 8 @@ -111,17 +114,13 @@ examples: <&gcc GCC_PCIE_0_CFG_AHB_CLK>, <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "tbu", - "ref"; + "slave_q2a"; interrupts = , , diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index 69e82f438f58..b3216141881c 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -108,7 +108,7 @@ properties: - description: See native 'dbi' CSR region for details. enum: [ ctrl ] - description: See native 'elbi/app' CSR region for details. - enum: [ apb, mgmt, link, ulreg, appl ] + enum: [ apb, mgmt, link, ulreg, appl, controller ] - description: See native 'atu' CSR region for details. enum: [ atu_dma ] - description: Syscon-related CSR regions. diff --git a/Documentation/devicetree/bindings/pci/sophgo,sg2044-pcie.yaml b/Documentation/devicetree/bindings/pci/sophgo,sg2044-pcie.yaml new file mode 100644 index 000000000000..ff1133bae3ba --- /dev/null +++ b/Documentation/devicetree/bindings/pci/sophgo,sg2044-pcie.yaml @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/sophgo,sg2044-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DesignWare based PCIe Root Complex controller on Sophgo SoCs + +maintainers: + - Inochi Amaoto + +description: + SG2044 SoC PCIe Root Complex controller is based on the Synopsys DesignWare + PCIe IP and thus inherits all the common properties defined in + snps,dw-pcie.yaml. + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + const: sophgo,sg2044-pcie + + reg: + items: + - description: Data Bus Interface (DBI) registers + - description: iATU registers + - description: Config registers + - description: Sophgo designed configuration registers + + reg-names: + items: + - const: dbi + - const: atu + - const: config + - const: app + + clocks: + items: + - description: core clk + + clock-names: + items: + - const: core + + interrupt-controller: + description: Interrupt controller node for handling legacy PCI interrupts. + type: object + + properties: + "#address-cells": + const: 0 + + "#interrupt-cells": + const: 1 + + interrupt-controller: true + + interrupts: + items: + - description: combined legacy interrupt + + required: + - "#address-cells" + - "#interrupt-cells" + - interrupt-controller + - interrupts + + additionalProperties: false + + msi-parent: true + + ranges: + maxItems: 5 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@6c00400000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x00400000 0x0 0x00001000>, + <0x6c 0x00700000 0x0 0x00004000>, + <0x40 0x00000000 0x0 0x00001000>, + <0x6c 0x00780c00 0x0 0x00000400>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + clocks = <&clk 0>; + clock-names = "core"; + device_type = "pci"; + linux,pci-domain = <0>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x40 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x00000000 0x0 0x00000000 0x0 0x04000000>, + <0x02000000 0x0 0x04000000 0x0 0x04000000 0x0 0x04000000>, + <0x43000000 0x42 0x00000000 0x42 0x00000000 0x2 0x00000000>, + <0x03000000 0x41 0x00000000 0x41 0x00000000 0x1 0x00000000>; + + interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + interrupt-parent = <&intc>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pci/spear13xx-pcie.txt b/Documentation/devicetree/bindings/pci/spear13xx-pcie.txt deleted file mode 100644 index d5a14f5dad46..000000000000 --- a/Documentation/devicetree/bindings/pci/spear13xx-pcie.txt +++ /dev/null @@ -1,14 +0,0 @@ -SPEAr13XX PCIe DT detail: -================================ - -SPEAr13XX uses the Synopsys DesignWare PCIe controller and ST MiPHY as PHY -controller. - -Required properties: -- compatible : should be "st,spear1340-pcie", "snps,dw-pcie". -- phys : phandle to PHY node associated with PCIe controller -- phy-names : must be "pcie-phy" -- All other definitions as per generic PCI bindings - - Optional properties: -- st,pcie-is-gen1 indicates that forced gen1 initialization is needed. diff --git a/Documentation/devicetree/bindings/pci/st,spear1340-pcie.yaml b/Documentation/devicetree/bindings/pci/st,spear1340-pcie.yaml new file mode 100644 index 000000000000..784f97b3cb7a --- /dev/null +++ b/Documentation/devicetree/bindings/pci/st,spear1340-pcie.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/st,spear1340-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST SPEAr1340 PCIe controller + +maintainers: + - Pratyush Anand + +description: + SPEAr13XX uses the Synopsys DesignWare PCIe controller and ST MiPHY as PHY + controller. + +select: + properties: + compatible: + contains: + const: st,spear1340-pcie + required: + - compatible + +properties: + compatible: + items: + - const: st,spear1340-pcie + - const: snps,dw-pcie + + phys: + maxItems: 1 + + st,pcie-is-gen1: + type: boolean + description: Indicates forced gen1 initialization is needed. + +required: + - compatible + - phys + - phy-names + +allOf: + - $ref: snps,dw-pcie.yaml# + +unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml index 97f2579ea908..29580cbd1767 100644 --- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml @@ -123,21 +123,21 @@ examples: #size-cells = <2>; pcie0_ep: pcie-ep@d000000 { - compatible = "ti,j721e-pcie-ep"; - reg = <0x00 0x02900000 0x00 0x1000>, - <0x00 0x02907000 0x00 0x400>, - <0x00 0x0d000000 0x00 0x00800000>, - <0x00 0x10000000 0x00 0x08000000>; - reg-names = "intd_cfg", "user_cfg", "reg", "mem"; - ti,syscon-pcie-ctrl = <&pcie0_ctrl 0x4070>; - max-link-speed = <3>; - num-lanes = <2>; - power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; - clocks = <&k3_clks 239 1>; - clock-names = "fck"; - max-functions = /bits/ 8 <6>; - dma-coherent; - phys = <&serdes0_pcie_link>; - phy-names = "pcie-phy"; - }; + compatible = "ti,j721e-pcie-ep"; + reg = <0x00 0x02900000 0x00 0x1000>, + <0x00 0x02907000 0x00 0x400>, + <0x00 0x0d000000 0x00 0x00800000>, + <0x00 0x10000000 0x00 0x08000000>; + reg-names = "intd_cfg", "user_cfg", "reg", "mem"; + ti,syscon-pcie-ctrl = <&pcie0_ctrl 0x4070>; + max-link-speed = <3>; + num-lanes = <2>; + power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 239 1>; + clock-names = "fck"; + max-functions = /bits/ 8 <6>; + dma-coherent; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + }; }; diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt deleted file mode 100644 index 85d9b95234f7..000000000000 --- a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt +++ /dev/null @@ -1,68 +0,0 @@ -* AppliedMicro X-Gene v1 PCIe MSI controller - -Required properties: - -- compatible: should be "apm,xgene1-msi" to identify - X-Gene v1 PCIe MSI controller block. -- msi-controller: indicates that this is an X-Gene v1 PCIe MSI controller node -- reg: physical base address (0x79000000) and length (0x900000) for controller - registers. These registers include the MSI termination address and data - registers as well as the MSI interrupt status registers. -- reg-names: not required -- interrupts: A list of 16 interrupt outputs of the controller, starting from - interrupt number 0x10 to 0x1f. -- interrupt-names: not required - -Each PCIe node needs to have property msi-parent that points to an MSI -controller node - -Examples: - -SoC DTSI: - - + MSI node: - msi@79000000 { - compatible = "apm,xgene1-msi"; - msi-controller; - reg = <0x00 0x79000000 0x0 0x900000>; - interrupts = <0x0 0x10 0x4> - <0x0 0x11 0x4> - <0x0 0x12 0x4> - <0x0 0x13 0x4> - <0x0 0x14 0x4> - <0x0 0x15 0x4> - <0x0 0x16 0x4> - <0x0 0x17 0x4> - <0x0 0x18 0x4> - <0x0 0x19 0x4> - <0x0 0x1a 0x4> - <0x0 0x1b 0x4> - <0x0 0x1c 0x4> - <0x0 0x1d 0x4> - <0x0 0x1e 0x4> - <0x0 0x1f 0x4>; - }; - - + PCIe controller node with msi-parent property pointing to MSI node: - pcie0: pcie@1f2b0000 { - device_type = "pci"; - compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ - 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ - reg-names = "csr", "cfg"; - ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ - 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ - dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 - 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 - 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 - 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 - 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; - dma-coherent; - clocks = <&pcie0clk 0>; - msi-parent= <&msi>; - }; diff --git a/Documentation/devicetree/bindings/pci/xgene-pci.txt b/Documentation/devicetree/bindings/pci/xgene-pci.txt deleted file mode 100644 index 92490330dc1c..000000000000 --- a/Documentation/devicetree/bindings/pci/xgene-pci.txt +++ /dev/null @@ -1,50 +0,0 @@ -* AppliedMicro X-Gene PCIe interface - -Required properties: -- device_type: set to "pci" -- compatible: should contain "apm,xgene-pcie" to identify the core. -- reg: A list of physical base address and length for each set of controller - registers. Must contain an entry for each entry in the reg-names - property. -- reg-names: Must include the following entries: - "csr": controller configuration registers. - "cfg": PCIe configuration space registers. -- #address-cells: set to <3> -- #size-cells: set to <2> -- ranges: ranges for the outbound memory, I/O regions. -- dma-ranges: ranges for the inbound memory regions. -- #interrupt-cells: set to <1> -- interrupt-map-mask and interrupt-map: standard PCI properties - to define the mapping of the PCIe interface to interrupt - numbers. -- clocks: from common clock binding: handle to pci clock. - -Optional properties: -- status: Either "ok" or "disabled". -- dma-coherent: Present if DMA operations are coherent - -Example: - - pcie0: pcie@1f2b0000 { - status = "disabled"; - device_type = "pci"; - compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ - 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ - reg-names = "csr", "cfg"; - ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ - 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ - dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 - 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 - 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 - 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 - 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; - dma-coherent; - clocks = <&pcie0clk 0>; - }; - diff --git a/Documentation/devicetree/bindings/phy/apm,xgene-phy.yaml b/Documentation/devicetree/bindings/phy/apm,xgene-phy.yaml new file mode 100644 index 000000000000..0674391feeae --- /dev/null +++ b/Documentation/devicetree/bindings/phy/apm,xgene-phy.yaml @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/apm,xgene-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene 15Gbps Multi-purpose PHY + +maintainers: + - Khuong Dinh + +description: + PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each + PHY (pair of lanes) has its own node. + +properties: + compatible: + items: + - const: apm,xgene-phy + + reg: + maxItems: 1 + + '#phy-cells': + description: + Possible values are 0 (SATA), 1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI). + const: 1 + + clocks: + maxItems: 1 + + apm,tx-eye-tuning: + description: + Manual control to fine tune the capture of the serial bit lines from the + automatic calibrated position. Two set of 3-tuple setting for each + supported link speed on the host. Range from 0 to 127 in unit of one bit + period. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 127 + default: 10 + + apm,tx-eye-direction: + description: + Eye tuning manual control direction. 0 means sample data earlier than the + nominal sampling point. 1 means sample data later than the nominal + sampling point. Two set of 3-tuple setting for each supported link speed + on the host. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + enum: [0, 1] + default: 0 + + apm,tx-boost-gain: + description: + Frequency boost AC (LSB 3-bit) and DC (2-bit) gain control. Two set of + 3-tuple setting for each supported link speed on the host. Range is + between 0 to 31 in unit of dB. Default is 3. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 31 + + apm,tx-amplitude: + description: + Amplitude control. Two set of 3-tuple setting for each supported link + speed on the host. Range is between 0 to 199500 in unit of uV. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 199500 + default: 199500 + + apm,tx-pre-cursor1: + description: + 1st pre-cursor emphasis taps control. Two set of 3-tuple setting for + each supported link speed on the host. Range is 0 to 273000 in unit of + uV. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 273000 + default: 0 + + apm,tx-pre-cursor2: + description: + 2nd pre-cursor emphasis taps control. Two set of 3-tuple setting for + each supported link speed on the host. Range is 0 to 127400 in unit uV. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 127400 + default: 0 + + apm,tx-post-cursor: + description: | + Post-cursor emphasis taps control. Two set of 3-tuple setting for Gen1, + Gen2, and Gen3 link speeds. Range is between 0 to 31 in unit of 18.2mV. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 2 + maxItems: 2 + items: + minItems: 3 + maxItems: 3 + items: + minimum: 0 + maximum: 31 + default: 0xf + + apm,tx-speed: + description: > + Tx operating speed. One set of 3-tuple for each supported link speed on + the host: + + 0 = 1-2Gbps + 1 = 2-4Gbps (1st tuple default) + 2 = 4-8Gbps + 3 = 8-15Gbps (2nd tuple default) + 4 = 2.5-4Gbps + 5 = 4-5Gbps + 6 = 5-6Gbps + 7 = 6-16Gbps (3rd tuple default). + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 3 + maxItems: 3 + items: + maximum: 7 + +additionalProperties: false + +examples: + - | + phy@1f21a000 { + compatible = "apm,xgene-phy"; + reg = <0x1f21a000 0x100>; + #phy-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt b/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt deleted file mode 100644 index 602cf952b92b..000000000000 --- a/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt +++ /dev/null @@ -1,76 +0,0 @@ -* APM X-Gene 15Gbps Multi-purpose PHY nodes - -PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each -PHY (pair of lanes) has its own node. - -Required properties: -- compatible : Shall be "apm,xgene-phy". -- reg : PHY memory resource is the SDS PHY access resource. -- #phy-cells : Shall be 1 as it expects one argument for setting - the mode of the PHY. Possible values are 0 (SATA), - 1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI). - -Optional properties: -- status : Shall be "ok" if enabled or "disabled" if disabled. - Default is "ok". -- clocks : Reference to the clock entry. -- apm,tx-eye-tuning : Manual control to fine tune the capture of the serial - bit lines from the automatic calibrated position. - Two set of 3-tuple setting for each (up to 3) - supported link speed on the host. Range from 0 to - 127 in unit of one bit period. Default is 10. -- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample - data earlier than the nominal sampling point. 1 means - sample data later than the nominal sampling point. - Two set of 3-tuple setting for each (up to 3) - supported link speed on the host. Default is 0. -- apm,tx-boost-gain : Frequency boost AC (LSB 3-bit) and DC (2-bit) - gain control. Two set of 3-tuple setting for each - (up to 3) supported link speed on the host. Range is - between 0 to 31 in unit of dB. Default is 3. -- apm,tx-amplitude : Amplitude control. Two set of 3-tuple setting for - each (up to 3) supported link speed on the host. - Range is between 0 to 199500 in unit of uV. - Default is 199500 uV. -- apm,tx-pre-cursor1 : 1st pre-cursor emphasis taps control. Two set of - 3-tuple setting for each (up to 3) supported link - speed on the host. Range is 0 to 273000 in unit of - uV. Default is 0. -- apm,tx-pre-cursor2 : 2nd pre-cursor emphasis taps control. Two set of - 3-tuple setting for each (up to 3) supported link - speed on the host. Range is 0 to 127400 in unit uV. - Default is 0x0. -- apm,tx-post-cursor : Post-cursor emphasis taps control. Two set of - 3-tuple setting for Gen1, Gen2, and Gen3. Range is - between 0 to 0x1f in unit of 18.2mV. Default is 0xf. -- apm,tx-speed : Tx operating speed. One set of 3-tuple for each - supported link speed on the host. - 0 = 1-2Gbps - 1 = 2-4Gbps (1st tuple default) - 2 = 4-8Gbps - 3 = 8-15Gbps (2nd tuple default) - 4 = 2.5-4Gbps - 5 = 4-5Gbps - 6 = 5-6Gbps - 7 = 6-16Gbps (3rd tuple default) - -NOTE: PHY override parameters are board specific setting. - -Example: - phy1: phy@1f21a000 { - compatible = "apm,xgene-phy"; - reg = <0x0 0x1f21a000 0x0 0x100>; - #phy-cells = <1>; - }; - - phy2: phy@1f22a000 { - compatible = "apm,xgene-phy"; - reg = <0x0 0x1f22a000 0x0 0x100>; - #phy-cells = <1>; - }; - - phy3: phy@1f23a000 { - compatible = "apm,xgene-phy"; - reg = <0x0 0x1f23a000 0x0 0x100>; - #phy-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt deleted file mode 100644 index c0155f842f62..000000000000 --- a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt +++ /dev/null @@ -1,36 +0,0 @@ -Berlin SATA PHY ---------------- - -Required properties: -- compatible: should be one of - "marvell,berlin2-sata-phy" - "marvell,berlin2q-sata-phy" -- address-cells: should be 1 -- size-cells: should be 0 -- phy-cells: from the generic PHY bindings, must be 1 -- reg: address and length of the register -- clocks: reference to the clock entry - -Sub-nodes: -Each PHY should be represented as a sub-node. - -Sub-nodes required properties: -- reg: the PHY number - -Example: - sata_phy: phy@f7e900a0 { - compatible = "marvell,berlin2q-sata-phy"; - reg = <0xf7e900a0 0x200>; - clocks = <&chip CLKID_SATA>; - #address-cells = <1>; - #size-cells = <0>; - #phy-cells = <1>; - - sata-phy@0 { - reg = <0>; - }; - - sata-phy@1 { - reg = <1>; - }; - }; diff --git a/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt b/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt deleted file mode 100644 index be33780f668e..000000000000 --- a/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt +++ /dev/null @@ -1,16 +0,0 @@ -* Marvell Berlin USB PHY - -Required properties: -- compatible: "marvell,berlin2-usb-phy" or "marvell,berlin2cd-usb-phy" -- reg: base address and length of the registers -- #phys-cells: should be 0 -- resets: reference to the reset controller - -Example: - - usb-phy@f774000 { - compatible = "marvell,berlin2-usb-phy"; - reg = <0xf774000 0x128>; - #phy-cells = <0>; - resets = <&chip 0x104 14>; - }; diff --git a/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt b/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt deleted file mode 100644 index 04f063aa7883..000000000000 --- a/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt +++ /dev/null @@ -1,30 +0,0 @@ -BROADCOM NORTHSTAR2 USB2 (DUAL ROLE DEVICE) PHY - -Required properties: - - compatible: brcm,ns2-drd-phy - - reg: offset and length of the NS2 PHY related registers. - - reg-names - The below registers must be provided. - icfg - for DRD ICFG configurations - rst-ctrl - for DRD IDM reset - crmu-ctrl - for CRMU core vdd, PHY and PHY PLL reset - usb2-strap - for port over current polarity reversal - - #phy-cells: Must be 0. No args required. - - vbus-gpios: vbus gpio binding - - id-gpios: id gpio binding - -Refer to phy/phy-bindings.txt for the generic PHY binding properties - -Example: - usbdrd_phy: phy@66000960 { - #phy-cells = <0>; - compatible = "brcm,ns2-drd-phy"; - reg = <0x66000960 0x24>, - <0x67012800 0x4>, - <0x6501d148 0x4>, - <0x664d0700 0x4>; - reg-names = "icfg", "rst-ctrl", - "crmu-ctrl", "usb2-strap"; - id-gpios = <&gpio_g 30 0>; - vbus-gpios = <&gpio_g 31 0>; - }; diff --git a/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.yaml b/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.yaml new file mode 100644 index 000000000000..1fab97de5c0d --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/brcm,ns2-drd-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Northstar2 USB2 Dual Role Device PHY + +maintainers: + - Florian Fainelli + - Hauke Mehrtens + - Rafał Miłecki + +properties: + compatible: + const: brcm,ns2-drd-phy + + reg: + items: + - description: DRD ICFG configurations + - description: DRD IDM reset + - description: CRMU core vdd, PHY and PHY PLL reset + - description: Port over current polarity reversal + + reg-names: + items: + - const: icfg + - const: rst-ctrl + - const: crmu-ctrl + - const: usb2-strap + + '#phy-cells': + const: 0 + + id-gpios: + maxItems: 1 + description: ID GPIO line + + vbus-gpios: + maxItems: 1 + description: VBUS GPIO line + +required: + - '#phy-cells' + - compatible + - reg + - reg-names + - id-gpios + - vbus-gpios + +additionalProperties: false + +examples: + - | + phy@66000960 { + #phy-cells = <0>; + compatible = "brcm,ns2-drd-phy"; + reg = <0x66000960 0x24>, <0x67012800 0x4>, <0x6501d148 0x4>, <0x664d0700 0x4>; + reg-names = "icfg", "rst-ctrl", "crmu-ctrl", "usb2-strap"; + id-gpios = <&gpio_g 30 0>; + vbus-gpios = <&gpio_g 31 0>; + }; diff --git a/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt b/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt deleted file mode 100644 index e8d82286beb9..000000000000 --- a/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt +++ /dev/null @@ -1,41 +0,0 @@ -Broadcom Stingray PCIe PHY - -Required properties: -- compatible: must be "brcm,sr-pcie-phy" -- reg: base address and length of the PCIe SS register space -- brcm,sr-cdru: phandle to the CDRU syscon node -- brcm,sr-mhb: phandle to the MHB syscon node -- #phy-cells: Must be 1, denotes the PHY index - -For PAXB based root complex, one can have a configuration of up to 8 PHYs -PHY index goes from 0 to 7 - -For the internal PAXC based root complex, PHY index is always 8 - -Example: - mhb: syscon@60401000 { - compatible = "brcm,sr-mhb", "syscon"; - reg = <0 0x60401000 0 0x38c>; - }; - - cdru: syscon@6641d000 { - compatible = "brcm,sr-cdru", "syscon"; - reg = <0 0x6641d000 0 0x400>; - }; - - pcie_phy: phy@40000000 { - compatible = "brcm,sr-pcie-phy"; - reg = <0 0x40000000 0 0x800>; - brcm,sr-cdru = <&cdru>; - brcm,sr-mhb = <&mhb>; - #phy-cells = <1>; - }; - - /* users of the PCIe PHY */ - - pcie0: pcie@48000000 { - ... - ... - phys = <&pcie_phy 0>; - phy-names = "pcie-phy"; - }; diff --git a/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.yaml new file mode 100644 index 000000000000..60ccc0813ed5 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/brcm,sr-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Stingray PCIe PHY + +maintainers: + - Ray Jui + +description: > + For PAXB based root complex, one can have a configuration of up to 8 PHYs. + PHY index goes from 0 to 7. + + For the internal PAXC based root complex, PHY index is always 8. + +properties: + compatible: + const: brcm,sr-pcie-phy + + reg: + maxItems: 1 + + '#phy-cells': + const: 1 + + brcm,sr-cdru: + description: phandle to the CDRU syscon node + $ref: /schemas/types.yaml#/definitions/phandle + + brcm,sr-mhb: + description: phandle to the MHB syscon node + $ref: /schemas/types.yaml#/definitions/phandle + +additionalProperties: false + +examples: + - | + phy@40000000 { + compatible = "brcm,sr-pcie-phy"; + reg = <0x40000000 0x800>; + brcm,sr-cdru = <&cdru>; + brcm,sr-mhb = <&mhb>; + #phy-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/phy/brcm,sr-usb-combo-phy.yaml b/Documentation/devicetree/bindings/phy/brcm,sr-usb-combo-phy.yaml new file mode 100644 index 000000000000..6224ba0f2990 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,sr-usb-combo-phy.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/brcm,sr-usb-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Stingray USB PHY + +maintainers: + - Ray Jui + - Scott Branden + +properties: + compatible: + enum: + - brcm,sr-usb-combo-phy + - brcm,sr-usb-hs-phy + + reg: + maxItems: 1 + + '#phy-cells': + description: PHY cell count indicating PHY type + enum: [ 0, 1 ] + +required: + - compatible + - reg + - '#phy-cells' + +allOf: + - if: + properties: + compatible: + contains: + const: brcm,sr-usb-combo-phy + then: + properties: + '#phy-cells': + const: 1 + - if: + properties: + compatible: + contains: + const: brcm,sr-usb-hs-phy + then: + properties: + '#phy-cells': + const: 0 + +additionalProperties: false + +examples: + - | + usb-phy@0 { + compatible = "brcm,sr-usb-combo-phy"; + reg = <0x00000000 0x100>; + #phy-cells = <1>; + }; + - | + usb-phy@20000 { + compatible = "brcm,sr-usb-hs-phy"; + reg = <0x00020000 0x100>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/brcm,stingray-usb-phy.txt b/Documentation/devicetree/bindings/phy/brcm,stingray-usb-phy.txt deleted file mode 100644 index 4ba298966af9..000000000000 --- a/Documentation/devicetree/bindings/phy/brcm,stingray-usb-phy.txt +++ /dev/null @@ -1,32 +0,0 @@ -Broadcom Stingray USB PHY - -Required properties: - - compatible : should be one of the listed compatibles - - "brcm,sr-usb-combo-phy" is combo PHY has two PHYs, one SS and one HS. - - "brcm,sr-usb-hs-phy" is a single HS PHY. - - reg: offset and length of the PHY blocks registers - - #phy-cells: - - Must be 1 for brcm,sr-usb-combo-phy as it expects one argument to indicate - the PHY number of two PHYs. 0 for HS PHY and 1 for SS PHY. - - Must be 0 for brcm,sr-usb-hs-phy. - -Refer to phy/phy-bindings.txt for the generic PHY binding properties - -Example: - usbphy0: usb-phy@0 { - compatible = "brcm,sr-usb-combo-phy"; - reg = <0x00000000 0x100>; - #phy-cells = <1>; - }; - - usbphy1: usb-phy@10000 { - compatible = "brcm,sr-usb-combo-phy"; - reg = <0x00010000 0x100>, - #phy-cells = <1>; - }; - - usbphy2: usb-phy@20000 { - compatible = "brcm,sr-usb-hs-phy"; - reg = <0x00020000 0x100>, - #phy-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/phy/dm816x-phy.txt b/Documentation/devicetree/bindings/phy/dm816x-phy.txt deleted file mode 100644 index 2fe3d11d063d..000000000000 --- a/Documentation/devicetree/bindings/phy/dm816x-phy.txt +++ /dev/null @@ -1,24 +0,0 @@ -Device tree binding documentation for am816x USB PHY -========================= - -Required properties: -- compatible : should be "ti,dm816x-usb-phy" -- reg : offset and length of the PHY register set. -- reg-names : name for the phy registers -- clocks : phandle to the clock -- clock-names : name of the clock -- syscon: phandle for the syscon node to access misc registers -- #phy-cells : from the generic PHY bindings, must be 1 -- syscon: phandle for the syscon node to access misc registers - -Example: - -usb_phy0: usb-phy@20 { - compatible = "ti,dm8168-usb-phy"; - reg = <0x20 0x8>; - reg-names = "phy"; - clocks = <&main_fapll 6>; - clock-names = "refclk"; - #phy-cells = <0>; - syscon = <&scm_conf>; -}; diff --git a/Documentation/devicetree/bindings/phy/hisilicon,hi6220-usb-phy.yaml b/Documentation/devicetree/bindings/phy/hisilicon,hi6220-usb-phy.yaml new file mode 100644 index 000000000000..376586a666e7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/hisilicon,hi6220-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/hisilicon,hi6220-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon hi6220 USB PHY + +maintainers: + - Zhangfei Gao + +properties: + compatible: + const: hisilicon,hi6220-usb-phy + + '#phy-cells': + const: 0 + + phy-supply: + description: PHY power supply. + + hisilicon,peripheral-syscon: + description: Phandle to the system controller for PHY control. + $ref: /schemas/types.yaml#/definitions/phandle + +additionalProperties: false + +examples: + - | + usbphy { + compatible = "hisilicon,hi6220-usb-phy"; + #phy-cells = <0>; + phy-supply = <&fixed_5v_hub>; + hisilicon,peripheral-syscon = <&sys_ctrl>; + }; diff --git a/Documentation/devicetree/bindings/phy/hisilicon,hix5hd2-sata-phy.yaml b/Documentation/devicetree/bindings/phy/hisilicon,hix5hd2-sata-phy.yaml new file mode 100644 index 000000000000..2993dd6b40a8 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/hisilicon,hix5hd2-sata-phy.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/hisilicon,hix5hd2-sata-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon hix5hd2 SATA PHY + +maintainers: + - Jiancheng Xue + +properties: + compatible: + const: hisilicon,hix5hd2-sata-phy + + reg: + maxItems: 1 + + '#phy-cells': + const: 0 + + hisilicon,peripheral-syscon: + description: Phandle of syscon used to control peripheral + $ref: /schemas/types.yaml#/definitions/phandle + + hisilicon,power-reg: + description: Offset and bit number within peripheral-syscon register controlling SATA power supply + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Offset within peripheral-syscon register + - description: Bit number controlling SATA power supply + +required: + - compatible + - reg + - '#phy-cells' + +additionalProperties: false + +examples: + - | + phy@f9900000 { + compatible = "hisilicon,hix5hd2-sata-phy"; + reg = <0xf9900000 0x10000>; + #phy-cells = <0>; + hisilicon,peripheral-syscon = <&peripheral_ctrl>; + hisilicon,power-reg = <0x8 10>; + }; diff --git a/Documentation/devicetree/bindings/phy/hisilicon,inno-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/hisilicon,inno-usb2-phy.yaml new file mode 100644 index 000000000000..51ea0e54ce35 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/hisilicon,inno-usb2-phy.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/hisilicon,inno-usb2-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon INNO USB2 PHY + +maintainers: + - Pengcheng Li + +description: + The INNO USB2 PHY device should be a child node of peripheral controller that + contains the PHY configuration register, and each device supports up to 2 PHY + ports which are represented as child nodes of INNO USB2 PHY device. + +properties: + compatible: + enum: + - hisilicon,hi3798cv200-usb2-phy + - hisilicon,hi3798mv100-usb2-phy + - hisilicon,inno-usb2-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^phy@[0-1]$": + description: PHY port subnode + type: object + additionalProperties: false + + properties: + reg: + maximum: 1 + + "#phy-cells": + const: 0 + + resets: + maxItems: 1 + + required: + - reg + - "#phy-cells" + - resets + +required: + - compatible + - reg + - clocks + - resets + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + #include + + usb2-phy@120 { + compatible = "hisilicon,hi3798cv200-usb2-phy"; + reg = <0x120 0x4>; + clocks = <&crg HISTB_USB2_PHY1_REF_CLK>; + resets = <&crg 0xbc 4>; + #address-cells = <1>; + #size-cells = <0>; + + phy@0 { + reg = <0>; + #phy-cells = <0>; + resets = <&crg 0xbc 8>; + }; + + phy@1 { + reg = <1>; + #phy-cells = <0>; + resets = <&crg 0xbc 9>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt deleted file mode 100644 index 296168b74d24..000000000000 --- a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt +++ /dev/null @@ -1,22 +0,0 @@ -Hisilicon hix5hd2 SATA PHY ------------------------ - -Required properties: -- compatible: should be "hisilicon,hix5hd2-sata-phy" -- reg: offset and length of the PHY registers -- #phy-cells: must be 0 -Refer to phy/phy-bindings.txt for the generic PHY binding properties - -Optional Properties: -- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. -- hisilicon,power-reg: offset and bit number within peripheral-syscon, - register of controlling sata power supply. - -Example: - sata_phy: phy@f9900000 { - compatible = "hisilicon,hix5hd2-sata-phy"; - reg = <0xf9900000 0x10000>; - #phy-cells = <0>; - hisilicon,peripheral-syscon = <&peripheral_ctrl>; - hisilicon,power-reg = <0x8 10>; - }; diff --git a/Documentation/devicetree/bindings/phy/img,pistachio-usb-phy.yaml b/Documentation/devicetree/bindings/phy/img,pistachio-usb-phy.yaml new file mode 100644 index 000000000000..bcc19bc68297 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/img,pistachio-usb-phy.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/img,pistachio-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Imagination Pistachio USB PHY + +maintainers: + - Andrew Bresticker + +properties: + compatible: + const: img,pistachio-usb-phy + + clocks: + maxItems: 1 + + clock-names: + items: + - const: usb_phy + + '#phy-cells': + const: 0 + + phy-supply: + description: USB VBUS supply. Must supply 5.0V. + + img,refclk: + description: + Reference clock source for the USB PHY. See + for valid values. + $ref: /schemas/types.yaml#/definitions/uint32 + + img,cr-top: + description: CR_TOP syscon phandle. + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - clocks + - clock-names + - '#phy-cells' + - img,refclk + - img,cr-top + +additionalProperties: false + +examples: + - | + #include + #include + + usb-phy { + compatible = "img,pistachio-usb-phy"; + clocks = <&clk_core CLK_USB_PHY>; + clock-names = "usb_phy"; + #phy-cells = <0>; + phy-supply = <&usb_vbus>; + img,refclk = ; + img,cr-top = <&cr_top>; + }; diff --git a/Documentation/devicetree/bindings/phy/keystone-usb-phy.txt b/Documentation/devicetree/bindings/phy/keystone-usb-phy.txt deleted file mode 100644 index 300830dda0bf..000000000000 --- a/Documentation/devicetree/bindings/phy/keystone-usb-phy.txt +++ /dev/null @@ -1,19 +0,0 @@ -TI Keystone USB PHY - -Required properties: - - compatible: should be "ti,keystone-usbphy". - - #address-cells, #size-cells : should be '1' if the device has sub-nodes - with 'reg' property. - - reg : Address and length of the usb phy control register set. - -The main purpose of this PHY driver is to enable the USB PHY reference clock -gate on the Keystone SOC for both the USB2 and USB3 PHY. Otherwise it is just -an NOP PHY driver. Hence this node is referenced as both the usb2 and usb3 -phy node in the USB Glue layer driver node. - -usb_phy: usb_phy@2620738 { - compatible = "ti,keystone-usbphy"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x2620738 32>; -}; diff --git a/Documentation/devicetree/bindings/phy/lantiq,ase-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/lantiq,ase-usb2-phy.yaml new file mode 100644 index 000000000000..99b5da705ca4 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/lantiq,ase-usb2-phy.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/lantiq,ase-usb2-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq XWAY SoC RCU USB 1.1/2.0 PHY + +maintainers: + - Hauke Mehrtens + +description: + This node has to be a sub node of the Lantiq RCU block. + +properties: + compatible: + items: + - enum: + - lantiq,ase-usb2-phy + - lantiq,danube-usb2-phy + - lantiq,xrx100-usb2-phy + - lantiq,xrx200-usb2-phy + - lantiq,xrx300-usb2-phy + + reg: + items: + - description: Offset of the USB PHY configuration register + - description: Offset of the USB Analog configuration register + + clocks: + maxItems: 1 + + clock-names: + items: + - const: phy + + resets: + minItems: 1 + maxItems: 2 + + reset-names: + minItems: 1 + items: + - enum: [ phy, ctrl ] + - const: ctrl + + '#phy-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - '#phy-cells' + +additionalProperties: false + +examples: + - | + usb2-phy@18 { + compatible = "lantiq,xrx200-usb2-phy"; + reg = <0x18 4>, <0x38 4>; + clocks = <&pmu 1>; + clock-names = "phy"; + resets = <&reset1 4 4>, <&reset0 4 4>; + reset-names = "phy", "ctrl"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,armada-375-usb-cluster.yaml b/Documentation/devicetree/bindings/phy/marvell,armada-375-usb-cluster.yaml new file mode 100644 index 000000000000..1706c31644e1 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,armada-375-usb-cluster.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,armada-375-usb-cluster.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Armada 375 USB Cluster + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: + Control register for the Armada 375 USB cluster, managing USB2 and USB3 features. + +properties: + compatible: + const: marvell,armada-375-usb-cluster + + reg: + maxItems: 1 + + '#phy-cells': + description: Number of PHY cells in specifier. 1 for USB2, 2 for USB3. + const: 1 + +required: + - compatible + - reg + - '#phy-cells' + +additionalProperties: false + +examples: + - | + usbcluster: usb-cluster@18400 { + compatible = "marvell,armada-375-usb-cluster"; + reg = <0x18400 0x4>; + #phy-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,armada-380-comphy.yaml b/Documentation/devicetree/bindings/phy/marvell,armada-380-comphy.yaml new file mode 100644 index 000000000000..dcb4c0007832 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,armada-380-comphy.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,armada-380-comphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Armada 38x COMPHY controller + +maintainers: + - Andrew Lunn + - Gregory Clement + +description: + This comphy controller can be found on Marvell Armada 38x. It provides a + number of shared PHYs used by various interfaces (network, sata, usb, + PCIe...). + +properties: + compatible: + items: + - const: marvell,armada-380-comphy + + reg: + items: + - description: COMPHY register location and length + - description: Configuration register location and length + + reg-names: + items: + - const: comphy + - const: conf + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^phy@[0-5]$': + description: A COMPHY lane + type: object + additionalProperties: false + + properties: + reg: + maximum: 1 + + '#phy-cells': + description: Input port index for the PHY lane + const: 1 + + required: + - reg + - '#phy-cells' + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + comphy: phy@18300 { + compatible = "marvell,armada-380-comphy"; + reg = <0x18300 0x100>, <0x18460 4>; + reg-names = "comphy", "conf"; + #address-cells = <1>; + #size-cells = <0>; + + cpm_comphy0: phy@0 { + reg = <0>; + #phy-cells = <1>; + }; + + cpm_comphy1: phy@1 { + reg = <1>; + #phy-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,berlin2-sata-phy.yaml b/Documentation/devicetree/bindings/phy/marvell,berlin2-sata-phy.yaml new file mode 100644 index 000000000000..6fc9ff96e682 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,berlin2-sata-phy.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,berlin2-sata-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Berlin SATA PHY + +maintainers: + - Antoine Tenart + +properties: + compatible: + enum: + - marvell,berlin2-sata-phy + - marvell,berlin2q-sata-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + '#phy-cells': + const: 1 + +patternProperties: + '^sata-phy@[0-1]$': + description: A SATA PHY sub-node. + type: object + additionalProperties: false + + properties: + reg: + maximum: 1 + description: PHY index number. + + required: + - reg + +required: + - compatible + - reg + - clocks + - '#address-cells' + - '#size-cells' + - '#phy-cells' + +additionalProperties: false + +examples: + - | + #include + + phy@f7e900a0 { + compatible = "marvell,berlin2q-sata-phy"; + reg = <0xf7e900a0 0x200>; + clocks = <&chip CLKID_SATA>; + #address-cells = <1>; + #size-cells = <0>; + #phy-cells = <1>; + + sata-phy@0 { + reg = <0>; + }; + + sata-phy@1 { + reg = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,berlin2-usb-phy.yaml b/Documentation/devicetree/bindings/phy/marvell,berlin2-usb-phy.yaml new file mode 100644 index 000000000000..b401e12a600c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,berlin2-usb-phy.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,berlin2-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Berlin USB PHY + +maintainers: + - Antoine Tenart + +properties: + compatible: + enum: + - marvell,berlin2-usb-phy + - marvell,berlin2cd-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - "#phy-cells" + - resets + +additionalProperties: false + +examples: + - | + usb-phy@f774000 { + compatible = "marvell,berlin2-usb-phy"; + reg = <0xf774000 0x128>; + #phy-cells = <0>; + resets = <&chip 0x104 14>; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,comphy-cp110.yaml b/Documentation/devicetree/bindings/phy/marvell,comphy-cp110.yaml new file mode 100644 index 000000000000..c35d31642805 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,comphy-cp110.yaml @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,comphy-cp110.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell MVEBU COMPHY Controller + +maintainers: + - Miquel Raynal + +description: > + COMPHY controllers can be found on the following Marvell MVEBU SoCs: + + * Armada 7k/8k (on the CP110) + * Armada 3700 + + It provides a number of shared PHYs used by various interfaces (network, SATA, + USB, PCIe...). + +properties: + compatible: + enum: + - marvell,comphy-cp110 + - marvell,comphy-a3700 + + reg: + minItems: 1 + items: + - description: Generic COMPHY registers + - description: Lane 1 (PCIe/GbE) registers (Armada 3700) + - description: Lane 0 (USB3/GbE) registers (Armada 3700) + - description: Lane 2 (SATA/USB3) registers (Armada 3700) + + reg-names: + minItems: 1 + items: + - const: comphy + - const: lane1_pcie_gbe + - const: lane0_usb3_gbe + - const: lane2_sata_usb3 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + minItems: 1 + maxItems: 3 + + marvell,system-controller: + description: Phandle to the Marvell system controller (CP110 only) + $ref: /schemas/types.yaml#/definitions/phandle + +patternProperties: + '^phy@[0-5]$': + description: A COMPHY lane child node + type: object + additionalProperties: false + + properties: + reg: + description: COMPHY lane number + maximum: 5 + + '#phy-cells': + const: 1 + + connector: + type: object + + required: + - reg + - '#phy-cells' + +required: + - compatible + - reg + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + const: marvell,comphy-a3700 + + then: + properties: + clocks: + maxItems: 1 + clock-names: + const: xtal + + required: + - reg-names + + else: + properties: + clocks: + minItems: 3 + clock-names: + items: + - const: mg_clk + - const: mg_core_clk + - const: axi_clk + + required: + - marvell,system-controller + +examples: + - | + phy@120000 { + compatible = "marvell,comphy-cp110"; + reg = <0x120000 0x6000>; + clocks = <&clk 1 5>, <&clk 1 6>, <&clk 1 18>; + clock-names = "mg_clk", "mg_core_clk", "axi_clk"; + #address-cells = <1>; + #size-cells = <0>; + marvell,system-controller = <&syscon0>; + + phy@0 { + reg = <0>; + #phy-cells = <1>; + }; + + phy@1 { + reg = <1>; + #phy-cells = <1>; + }; + }; + + - | + phy@18300 { + compatible = "marvell,comphy-a3700"; + reg = <0x18300 0x300>, + <0x1F000 0x400>, + <0x5C000 0x400>, + <0xe0178 0x8>; + reg-names = "comphy", + "lane1_pcie_gbe", + "lane0_usb3_gbe", + "lane2_sata_usb3"; + #address-cells = <1>; + #size-cells = <0>; + + comphy0: phy@0 { + reg = <0>; + #phy-cells = <1>; + }; + + comphy1: phy@1 { + reg = <1>; + #phy-cells = <1>; + }; + + comphy2: phy@2 { + reg = <2>; + #phy-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,mmp2-usb-phy.yaml b/Documentation/devicetree/bindings/phy/marvell,mmp2-usb-phy.yaml new file mode 100644 index 000000000000..af1ae2406f65 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,mmp2-usb-phy.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,mmp2-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell MMP2/PXA USB PHY + +maintainers: + - Lubomir Rintel + +properties: + compatible: + enum: + - marvell,mmp2-usb-phy + - marvell,pxa910-usb-phy + - marvell,pxa168-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - "#phy-cells" + +additionalProperties: false + +examples: + - | + usbphy@d4207000 { + compatible = "marvell,mmp2-usb-phy"; + reg = <0xd4207000 0x40>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/marvell,mvebu-sata-phy.yaml b/Documentation/devicetree/bindings/phy/marvell,mvebu-sata-phy.yaml new file mode 100644 index 000000000000..81e942428911 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/marvell,mvebu-sata-phy.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/marvell,mvebu-sata-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell MVEBU SATA PHY + +maintainers: + - Andrew Lunn + - Gregory Clement + +properties: + compatible: + const: marvell,mvebu-sata-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: sata + + '#phy-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - '#phy-cells' + +additionalProperties: false + +examples: + - | + sata-phy@84000 { + compatible = "marvell,mvebu-sata-phy"; + reg = <0x84000 0x0334>; + clocks = <&gate_clk 15>; + clock-names = "sata"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/mixel,mipi-dsi-phy.yaml b/Documentation/devicetree/bindings/phy/mixel,mipi-dsi-phy.yaml index 3c28ec50f097..286a4fcc977d 100644 --- a/Documentation/devicetree/bindings/phy/mixel,mipi-dsi-phy.yaml +++ b/Documentation/devicetree/bindings/phy/mixel,mipi-dsi-phy.yaml @@ -72,11 +72,6 @@ allOf: contains: const: fsl,imx8qxp-mipi-dphy then: - properties: - assigned-clocks: false - assigned-clock-parents: false - assigned-clock-rates: false - required: - fsl,syscon diff --git a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml new file mode 100644 index 000000000000..0febd04a61f4 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/motorola,cpcap-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Motorola CPCAP PMIC USB PHY + +maintainers: + - Tony Lindgren + +properties: + compatible: + enum: + - motorola,cpcap-usb-phy + - motorola,mapphone-cpcap-usb-phy + + '#phy-cells': + const: 0 + + interrupts: + description: CPCAP PMIC interrupts used by the USB PHY + items: + - description: id_ground interrupt + - description: id_float interrupt + - description: se0conn interrupt + - description: vbusvld interrupt + - description: sessvld interrupt + - description: sessend interrupt + - description: se1 interrupt + - description: dm interrupt + - description: dp interrupt + + interrupt-names: + description: Interrupt names + items: + - const: id_ground + - const: id_float + - const: se0conn + - const: vbusvld + - const: sessvld + - const: sessend + - const: se1 + - const: dm + - const: dp + + io-channels: + description: IIO ADC channels used by the USB PHY + items: + - description: vbus channel + - description: id channel + + io-channel-names: + items: + - const: vbus + - const: id + + vusb-supply: true + + pinctrl-names: + items: + - const: default + - const: ulpi + - const: utmi + - const: uart + + mode-gpios: + description: Optional GPIOs for configuring alternate modes + items: + - description: "mode selection GPIO #0" + - description: "mode selection GPIO #1" + +required: + - compatible + - '#phy-cells' + - interrupts-extended + - interrupt-names + - io-channels + - io-channel-names + - vusb-supply + +additionalProperties: false + +examples: + - | + #include + + phy { + compatible = "motorola,mapphone-cpcap-usb-phy"; + #phy-cells = <0>; + interrupts-extended = < + &cpcap 15 0 &cpcap 14 0 &cpcap 28 0 &cpcap 19 0 + &cpcap 18 0 &cpcap 17 0 &cpcap 16 0 &cpcap 49 0 + &cpcap 48 1 + >; + interrupt-names = "id_ground", "id_float", "se0conn", "vbusvld", + "sessvld", "sessend", "se1", "dm", "dp"; + io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>; + io-channel-names = "vbus", "id"; + vusb-supply = <&vusb>; + pinctrl-0 = <&usb_gpio_mux_sel1 &usb_gpio_mux_sel2>; + pinctrl-1 = <&usb_ulpi_pins>; + pinctrl-2 = <&usb_utmi_pins>; + pinctrl-3 = <&uart3_pins>; + pinctrl-names = "default", "ulpi", "utmi", "uart"; + mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, <&gpio1 0 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/phy/motorola,mapphone-mdm6600.yaml b/Documentation/devicetree/bindings/phy/motorola,mapphone-mdm6600.yaml new file mode 100644 index 000000000000..cb6544b3478d --- /dev/null +++ b/Documentation/devicetree/bindings/phy/motorola,mapphone-mdm6600.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/motorola,mapphone-mdm6600.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Motorola Mapphone MDM6600 USB PHY + +maintainers: + - Tony Lindgren + +properties: + compatible: + items: + - const: motorola,mapphone-mdm6600 + + enable-gpios: + description: GPIO to enable the USB PHY + maxItems: 1 + + power-gpios: + description: GPIO to power on the device + maxItems: 1 + + reset-gpios: + description: GPIO to reset the device + maxItems: 1 + + motorola,mode-gpios: + description: Two GPIOs to configure MDM6600 USB start-up mode for normal mode versus USB flashing mode + items: + - description: normal mode select GPIO + - description: USB flashing mode select GPIO + + motorola,cmd-gpios: + description: Three GPIOs to control the power state of the MDM6600 + items: + - description: power state control GPIO 0 + - description: power state control GPIO 1 + - description: power state control GPIO 2 + + motorola,status-gpios: + description: Three GPIOs to read the power state of the MDM6600 + items: + - description: power state read GPIO 0 + - description: power state read GPIO 1 + - description: power state read GPIO 2 + + '#phy-cells': + const: 0 + +required: + - compatible + - enable-gpios + - power-gpios + - reset-gpios + - motorola,mode-gpios + - motorola,cmd-gpios + - motorola,status-gpios + +additionalProperties: false + +examples: + - | + #include + + usb-phy { + compatible = "motorola,mapphone-mdm6600"; + enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>; + power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; + motorola,mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>, + <&gpio5 21 GPIO_ACTIVE_HIGH>; + motorola,cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>, + <&gpio4 8 GPIO_ACTIVE_HIGH>, + <&gpio5 14 GPIO_ACTIVE_HIGH>; + motorola,status-gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>, + <&gpio2 21 GPIO_ACTIVE_HIGH>, + <&gpio2 23 GPIO_ACTIVE_HIGH>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt deleted file mode 100644 index 8b5a7a28a35b..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt +++ /dev/null @@ -1,48 +0,0 @@ -mvebu armada 38x comphy driver ------------------------------- - -This comphy controller can be found on Marvell Armada 38x. It provides a -number of shared PHYs used by various interfaces (network, sata, usb, -PCIe...). - -Required properties: - -- compatible: should be "marvell,armada-380-comphy" -- reg: should contain the comphy register location and length. -- #address-cells: should be 1. -- #size-cells: should be 0. - -Optional properties: - -- reg-names: must be "comphy" as the first name, and "conf". -- reg: must contain the comphy register location and length as the first - pair, followed by an optional configuration register address and - length pair. - -A sub-node is required for each comphy lane provided by the comphy. - -Required properties (child nodes): - -- reg: comphy lane number. -- #phy-cells : from the generic phy bindings, must be 1. Defines the - input port to use for a given comphy lane. - -Example: - - comphy: phy@18300 { - compatible = "marvell,armada-380-comphy"; - reg-names = "comphy", "conf"; - reg = <0x18300 0x100>, <0x18460 4>; - #address-cells = <1>; - #size-cells = <0>; - - cpm_comphy0: phy@0 { - reg = <0>; - #phy-cells = <1>; - }; - - cpm_comphy1: phy@1 { - reg = <1>; - #phy-cells = <1>; - }; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-ath79-usb.txt b/Documentation/devicetree/bindings/phy/phy-ath79-usb.txt deleted file mode 100644 index c3a29c5feea3..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-ath79-usb.txt +++ /dev/null @@ -1,18 +0,0 @@ -* Atheros AR71XX/9XXX USB PHY - -Required properties: -- compatible: "qca,ar7100-usb-phy" -- #phys-cells: should be 0 -- reset-names: "phy"[, "suspend-override"] -- resets: references to the reset controllers - -Example: - - usb-phy { - compatible = "qca,ar7100-usb-phy"; - - reset-names = "phy", "suspend-override"; - resets = <&rst 4>, <&rst 3>; - - #phy-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-cpcap-usb.txt b/Documentation/devicetree/bindings/phy/phy-cpcap-usb.txt deleted file mode 100644 index 2eb9b2b69037..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-cpcap-usb.txt +++ /dev/null @@ -1,40 +0,0 @@ -Motorola CPCAP PMIC USB PHY binding - -Required properties: -compatible: Shall be either "motorola,cpcap-usb-phy" or - "motorola,mapphone-cpcap-usb-phy" -#phy-cells: Shall be 0 -interrupts: CPCAP PMIC interrupts used by the USB PHY -interrupt-names: Interrupt names -io-channels: IIO ADC channels used by the USB PHY -io-channel-names: IIO ADC channel names -vusb-supply: Regulator for the PHY - -Optional properties: -pinctrl: Optional alternate pin modes for the PHY -pinctrl-names: Names for optional pin modes -mode-gpios: Optional GPIOs for configuring alternate modes - -Example: -cpcap_usb2_phy: phy { - compatible = "motorola,mapphone-cpcap-usb-phy"; - pinctrl-0 = <&usb_gpio_mux_sel1 &usb_gpio_mux_sel2>; - pinctrl-1 = <&usb_ulpi_pins>; - pinctrl-2 = <&usb_utmi_pins>; - pinctrl-3 = <&uart3_pins>; - pinctrl-names = "default", "ulpi", "utmi", "uart"; - #phy-cells = <0>; - interrupts-extended = < - &cpcap 15 0 &cpcap 14 0 &cpcap 28 0 &cpcap 19 0 - &cpcap 18 0 &cpcap 17 0 &cpcap 16 0 &cpcap 49 0 - &cpcap 48 1 - >; - interrupt-names = - "id_ground", "id_float", "se0conn", "vbusvld", - "sessvld", "sessend", "se1", "dm", "dp"; - mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH - &gpio1 0 GPIO_ACTIVE_HIGH>; - io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>; - io-channel-names = "vbus", "id"; - vusb-supply = <&vusb>; -}; diff --git a/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt b/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt deleted file mode 100644 index c26478be391b..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt +++ /dev/null @@ -1,40 +0,0 @@ -TI DA8xx/OMAP-L1xx/AM18xx USB PHY - -Required properties: - - compatible: must be "ti,da830-usb-phy". - - #phy-cells: must be 1. - -This device controls the PHY for both the USB 1.1 OHCI and USB 2.0 OTG -controllers on DA8xx SoCs. Consumers of this device should use index 0 for -the USB 2.0 phy device and index 1 for the USB 1.1 phy device. - -It also requires a "syscon" node with compatible = "ti,da830-cfgchip", "syscon" -to access the CFGCHIP2 register. - -Example: - - cfgchip: cfgchip@1417c { - compatible = "ti,da830-cfgchip", "syscon"; - reg = <0x1417c 0x14>; - }; - - usb_phy: usb-phy { - compatible = "ti,da830-usb-phy"; - #phy-cells = <1>; - }; - - usb20: usb@200000 { - compatible = "ti,da830-musb"; - reg = <0x200000 0x1000>; - interrupts = <58>; - phys = <&usb_phy 0>; - phy-names = "usb-phy"; - }; - - usb11: usb@225000 { - compatible = "ti,da830-ohci"; - reg = <0x225000 0x1000>; - interrupts = <59>; - phys = <&usb_phy 1>; - phy-names = "usb-phy"; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt b/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt deleted file mode 100644 index f17a56e2152f..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt +++ /dev/null @@ -1,16 +0,0 @@ -Hisilicon hi6220 usb PHY ------------------------ - -Required properties: -- compatible: should be "hisilicon,hi6220-usb-phy" -- #phy-cells: must be 0 -- hisilicon,peripheral-syscon: phandle of syscon used to control phy. -Refer to phy/phy-bindings.txt for the generic PHY binding properties - -Example: - usb_phy: usbphy { - compatible = "hisilicon,hi6220-usb-phy"; - #phy-cells = <0>; - phy-supply = <&fixed_5v_hub>; - hisilicon,peripheral-syscon = <&sys_ctrl>; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt deleted file mode 100644 index 104953e849e7..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt +++ /dev/null @@ -1,71 +0,0 @@ -Device tree bindings for HiSilicon INNO USB2 PHY - -Required properties: -- compatible: Should be one of the following strings: - "hisilicon,inno-usb2-phy", - "hisilicon,hi3798cv200-usb2-phy". -- reg: Should be the address space for PHY configuration register in peripheral - controller, e.g. PERI_USB0 for USB 2.0 PHY01 on Hi3798CV200 SoC. -- clocks: The phandle and clock specifier pair for INNO USB2 PHY device - reference clock. -- resets: The phandle and reset specifier pair for INNO USB2 PHY device reset - signal. -- #address-cells: Must be 1. -- #size-cells: Must be 0. - -The INNO USB2 PHY device should be a child node of peripheral controller that -contains the PHY configuration register, and each device supports up to 2 PHY -ports which are represented as child nodes of INNO USB2 PHY device. - -Required properties for PHY port node: -- reg: The PHY port instance number. -- #phy-cells: Defined by generic PHY bindings. Must be 0. -- resets: The phandle and reset specifier pair for PHY port reset signal. - -Refer to phy/phy-bindings.txt for the generic PHY binding properties - -Example: - -perictrl: peripheral-controller@8a20000 { - compatible = "hisilicon,hi3798cv200-perictrl", "simple-mfd"; - reg = <0x8a20000 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x0 0x8a20000 0x1000>; - - usb2_phy1: usb2-phy@120 { - compatible = "hisilicon,hi3798cv200-usb2-phy"; - reg = <0x120 0x4>; - clocks = <&crg HISTB_USB2_PHY1_REF_CLK>; - resets = <&crg 0xbc 4>; - #address-cells = <1>; - #size-cells = <0>; - - usb2_phy1_port0: phy@0 { - reg = <0>; - #phy-cells = <0>; - resets = <&crg 0xbc 8>; - }; - - usb2_phy1_port1: phy@1 { - reg = <1>; - #phy-cells = <0>; - resets = <&crg 0xbc 9>; - }; - }; - - usb2_phy2: usb2-phy@124 { - compatible = "hisilicon,hi3798cv200-usb2-phy"; - reg = <0x124 0x4>; - clocks = <&crg HISTB_USB2_PHY2_REF_CLK>; - resets = <&crg 0xbc 6>; - #address-cells = <1>; - #size-cells = <0>; - - usb2_phy2_port0: phy@0 { - reg = <0>; - #phy-cells = <0>; - resets = <&crg 0xbc 10>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/phy/phy-lantiq-rcu-usb2.txt b/Documentation/devicetree/bindings/phy/phy-lantiq-rcu-usb2.txt deleted file mode 100644 index 643948b6b576..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-lantiq-rcu-usb2.txt +++ /dev/null @@ -1,40 +0,0 @@ -Lantiq XWAY SoC RCU USB 1.1/2.0 PHY binding -=========================================== - -This binding describes the USB PHY hardware provided by the RCU module on the -Lantiq XWAY SoCs. - -This node has to be a sub node of the Lantiq RCU block. - -------------------------------------------------------------------------------- -Required properties (controller (parent) node): -- compatible : Should be one of - "lantiq,ase-usb2-phy" - "lantiq,danube-usb2-phy" - "lantiq,xrx100-usb2-phy" - "lantiq,xrx200-usb2-phy" - "lantiq,xrx300-usb2-phy" -- reg : Defines the following sets of registers in the parent - syscon device - - Offset of the USB PHY configuration register - - Offset of the USB Analog configuration - register (only for xrx200 and xrx200) -- clocks : References to the (PMU) "phy" clk gate. -- clock-names : Must be "phy" -- resets : References to the RCU USB configuration reset bits. -- reset-names : Must be one of the following: - "phy" (optional) - "ctrl" (shared) - -------------------------------------------------------------------------------- -Example for the USB PHYs on an xRX200 SoC: - usb_phy0: usb2-phy@18 { - compatible = "lantiq,xrx200-usb2-phy"; - reg = <0x18 4>, <0x38 4>; - - clocks = <&pmu PMU_GATE_USB0_PHY>; - clock-names = "phy"; - resets = <&reset1 4 4>, <&reset0 4 4>; - reset-names = "phy", "ctrl"; - #phy-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt b/Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt deleted file mode 100644 index 3bb821cd6a7f..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt +++ /dev/null @@ -1,26 +0,0 @@ -NXP LPC18xx/43xx internal USB OTG PHY binding ---------------------------------------------- - -This file contains documentation for the internal USB OTG PHY found -in NXP LPC18xx and LPC43xx SoCs. - -Required properties: -- compatible : must be "nxp,lpc1850-usb-otg-phy" -- clocks : must be exactly one entry -See: Documentation/devicetree/bindings/clock/clock-bindings.txt -- #phy-cells : must be 0 for this phy -See: Documentation/devicetree/bindings/phy/phy-bindings.txt - -The phy node must be a child of the creg syscon node. - -Example: -creg: syscon@40043000 { - compatible = "nxp,lpc1850-creg", "syscon", "simple-mfd"; - reg = <0x40043000 0x1000>; - - usb0_otg_phy: phy { - compatible = "nxp,lpc1850-usb-otg-phy"; - clocks = <&ccu1 CLK_USB0>; - #phy-cells = <0>; - }; -}; diff --git a/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt deleted file mode 100644 index 29427d4f047a..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt +++ /dev/null @@ -1,29 +0,0 @@ -Device tree binding documentation for Motorola Mapphone MDM6600 USB PHY - -Required properties: -- compatible Must be "motorola,mapphone-mdm6600" -- enable-gpios GPIO to enable the USB PHY -- power-gpios GPIO to power on the device -- reset-gpios GPIO to reset the device -- motorola,mode-gpios Two GPIOs to configure MDM6600 USB start-up mode for - normal mode versus USB flashing mode -- motorola,cmd-gpios Three GPIOs to control the power state of the MDM6600 -- motorola,status-gpios Three GPIOs to read the power state of the MDM6600 - -Example: - -usb-phy { - compatible = "motorola,mapphone-mdm6600"; - enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>; - power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; - motorola,mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>, - <&gpio5 21 GPIO_ACTIVE_HIGH>; - motorola,cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>, - <&gpio4 8 GPIO_ACTIVE_HIGH>, - <&gpio5 14 GPIO_ACTIVE_HIGH>; - motorola,status-gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>, - <&gpio2 21 GPIO_ACTIVE_HIGH>, - <&gpio2 23 GPIO_ACTIVE_HIGH>; - #phy-cells = <0>; -}; diff --git a/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt b/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt deleted file mode 100644 index 5ffd0f55d010..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt +++ /dev/null @@ -1,94 +0,0 @@ -MVEBU comphy drivers --------------------- - -COMPHY controllers can be found on the following Marvell MVEBU SoCs: -* Armada 7k/8k (on the CP110) -* Armada 3700 -It provides a number of shared PHYs used by various interfaces (network, SATA, -USB, PCIe...). - -Required properties: - -- compatible: should be one of: - * "marvell,comphy-cp110" for Armada 7k/8k - * "marvell,comphy-a3700" for Armada 3700 -- reg: should contain the COMPHY register(s) location(s) and length(s). - * 1 entry for Armada 7k/8k - * 4 entries for Armada 3700 along with the corresponding reg-names - properties, memory areas are: - * Generic COMPHY registers - * Lane 1 (PCIe/GbE) - * Lane 0 (USB3/GbE) - * Lane 2 (SATA/USB3) -- marvell,system-controller: should contain a phandle to the system - controller node (only for Armada 7k/8k) -- #address-cells: should be 1. -- #size-cells: should be 0. - -Optional properlties: - -- clocks: pointers to the reference clocks for this device (CP110 only), - consequently: MG clock, MG Core clock, AXI clock. -- clock-names: names of used clocks for CP110 only, must be : - "mg_clk", "mg_core_clk" and "axi_clk". - -A sub-node is required for each comphy lane provided by the comphy. - -Required properties (child nodes): - -- reg: COMPHY lane number. -- #phy-cells : from the generic PHY bindings, must be 1. Defines the - input port to use for a given comphy lane. - -Examples: - - CP11X_LABEL(comphy): phy@120000 { - compatible = "marvell,comphy-cp110"; - reg = <0x120000 0x6000>; - marvell,system-controller = <&CP11X_LABEL(syscon0)>; - clocks = <&CP11X_LABEL(clk) 1 5>, <&CP11X_LABEL(clk) 1 6>, - <&CP11X_LABEL(clk) 1 18>; - clock-names = "mg_clk", "mg_core_clk", "axi_clk"; - #address-cells = <1>; - #size-cells = <0>; - - CP11X_LABEL(comphy0): phy@0 { - reg = <0>; - #phy-cells = <1>; - }; - - CP11X_LABEL(comphy1): phy@1 { - reg = <1>; - #phy-cells = <1>; - }; - }; - - comphy: phy@18300 { - compatible = "marvell,comphy-a3700"; - reg = <0x18300 0x300>, - <0x1F000 0x400>, - <0x5C000 0x400>, - <0xe0178 0x8>; - reg-names = "comphy", - "lane1_pcie_gbe", - "lane0_usb3_gbe", - "lane2_sata_usb3"; - #address-cells = <1>; - #size-cells = <0>; - - - comphy0: phy@0 { - reg = <0>; - #phy-cells = <1>; - }; - - comphy1: phy@1 { - reg = <1>; - #phy-cells = <1>; - }; - - comphy2: phy@2 { - reg = <2>; - #phy-cells = <1>; - }; - }; diff --git a/Documentation/devicetree/bindings/phy/phy-mvebu.txt b/Documentation/devicetree/bindings/phy/phy-mvebu.txt deleted file mode 100644 index 64afdd13d91d..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-mvebu.txt +++ /dev/null @@ -1,42 +0,0 @@ -* Marvell MVEBU SATA PHY - -Power control for the SATA phy found on Marvell MVEBU SoCs. - -This document extends the binding described in phy-bindings.txt - -Required properties : - - - reg : Offset and length of the register set for the SATA device - - compatible : Should be "marvell,mvebu-sata-phy" - - clocks : phandle of clock and specifier that supplies the device - - clock-names : Should be "sata" - -Example: - sata-phy@84000 { - compatible = "marvell,mvebu-sata-phy"; - reg = <0x84000 0x0334>; - clocks = <&gate_clk 15>; - clock-names = "sata"; - #phy-cells = <0>; - }; - -Armada 375 USB cluster ----------------------- - -Armada 375 comes with an USB2 host and device controller and an USB3 -controller. The USB cluster control register allows to manage common -features of both USB controllers. - -Required properties: - -- compatible: "marvell,armada-375-usb-cluster" -- reg: Should contain usb cluster register location and length. -- #phy-cells : from the generic phy bindings, must be 1. Possible -values are 1 (USB2), 2 (USB3). - -Example: - usbcluster: usb-cluster@18400 { - compatible = "marvell,armada-375-usb-cluster"; - reg = <0x18400 0x4>; - #phy-cells = <1> - }; diff --git a/Documentation/devicetree/bindings/phy/phy-pxa-usb.txt b/Documentation/devicetree/bindings/phy/phy-pxa-usb.txt deleted file mode 100644 index d80e36a77ec5..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-pxa-usb.txt +++ /dev/null @@ -1,18 +0,0 @@ -Marvell PXA USB PHY -------------------- - -Required properties: -- compatible: one of: "marvell,mmp2-usb-phy", "marvell,pxa910-usb-phy", - "marvell,pxa168-usb-phy", -- #phy-cells: must be 0 - -Example: - usb-phy: usbphy@d4207000 { - compatible = "marvell,mmp2-usb-phy"; - reg = <0xd4207000 0x40>; - #phy-cells = <0>; - status = "okay"; - }; - -This document explains the device tree binding. For general -information about PHY subsystem refer to Documentation/driver-api/phy/phy.rst diff --git a/Documentation/devicetree/bindings/phy/pistachio-usb-phy.txt b/Documentation/devicetree/bindings/phy/pistachio-usb-phy.txt deleted file mode 100644 index c7970c07ee32..000000000000 --- a/Documentation/devicetree/bindings/phy/pistachio-usb-phy.txt +++ /dev/null @@ -1,29 +0,0 @@ -IMG Pistachio USB PHY -===================== - -Required properties: --------------------- - - compatible: Must be "img,pistachio-usb-phy". - - #phy-cells: Must be 0. See ./phy-bindings.txt for details. - - clocks: Must contain an entry for each entry in clock-names. - See ../clock/clock-bindings.txt for details. - - clock-names: Must include "usb_phy". - - img,cr-top: Must contain a phandle to the CR_TOP syscon node. - - img,refclk: Indicates the reference clock source for the USB PHY. - See for a list of valid values. - -Optional properties: --------------------- - - phy-supply: USB VBUS supply. Must supply 5.0V. - -Example: --------- -usb_phy: usb-phy { - compatible = "img,pistachio-usb-phy"; - clocks = <&clk_core CLK_USB_PHY>; - clock-names = "usb_phy"; - phy-supply = <&usb_vbus>; - img,refclk = ; - img,cr-top = <&cr_top>; - #phy-cells = <0>; -}; diff --git a/Documentation/devicetree/bindings/phy/qca,ar7100-usb-phy.yaml b/Documentation/devicetree/bindings/phy/qca,ar7100-usb-phy.yaml new file mode 100644 index 000000000000..029665530829 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qca,ar7100-usb-phy.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qca,ar7100-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atheros AR71XX/9XXX USB PHY + +maintainers: + - Alban Bedel + +properties: + compatible: + items: + - const: qca,ar7100-usb-phy + + reset-names: + description: Names of reset lines in order. + minItems: 1 + items: + - const: phy + - const: suspend-override + + resets: + description: References to the reset controllers. + minItems: 1 + items: + - description: Reset controller for phy + - description: Reset controller for suspend-override + + '#phy-cells': + const: 0 + +required: + - compatible + - reset-names + - resets + - '#phy-cells' + +additionalProperties: false + +examples: + - | + usb-phy { + compatible = "qca,ar7100-usb-phy"; + reset-names = "phy", "suspend-override"; + resets = <&rst 4>, <&rst 3>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml new file mode 100644 index 000000000000..c84c62d0e8cb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,m31-eusb2-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm M31 eUSB2 phy + +maintainers: + - Wesley Cheng + +description: + M31 based eUSB2 controller, which supports LS/FS/HS usb connectivity + on Qualcomm chipsets. It is paired with a eUSB2 repeater. + +properties: + compatible: + items: + - enum: + - qcom,sm8750-m31-eusb2-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: reference clock + + clock-names: + items: + - const: ref + + resets: + maxItems: 1 + + phys: + maxItems: 1 + description: + Phandle to eUSB2 repeater + + vdd-supply: + description: + Phandle to 0.88V regulator supply to PHY digital circuit. + + vdda12-supply: + description: + Phandle to 1.2V regulator supply to PHY refclk pll block. + +required: + - compatible + - reg + - "#phy-cells" + - clocks + - clock-names + - resets + - vdd-supply + - vdda12-supply + +additionalProperties: false + +examples: + - | + usb_1_hsphy: phy@88e3000 { + compatible = "qcom,sm8750-m31-eusb2-phy"; + reg = <0x88e3000 0x29c>; + + clocks = <&tcsrcc_usb2_clkref_en>; + clock-names = "ref"; + + resets = <&gcc_qusb2phy_prim_bcr>; + + #phy-cells = <0>; + + vdd-supply = <&vreg_l2d_0p88>; + vdda12-supply = <&vreg_l3g_1p2>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 2c6c9296e4c0..b6f140bf5b3b 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -145,6 +145,7 @@ allOf: compatible: contains: enum: + - qcom,qcs615-qmp-gen3x1-pcie-phy - qcom,sar2130p-qmp-gen3x2-pcie-phy - qcom,sc8180x-qmp-pcie-phy - qcom,sdm845-qhp-pcie-phy @@ -175,7 +176,8 @@ allOf: compatible: contains: enum: - - qcom,qcs615-qmp-gen3x1-pcie-phy + - qcom,sa8775p-qmp-gen4x2-pcie-phy + - qcom,sa8775p-qmp-gen4x4-pcie-phy - qcom,sc8280xp-qmp-gen3x1-pcie-phy - qcom,sc8280xp-qmp-gen3x2-pcie-phy - qcom,sc8280xp-qmp-gen3x4-pcie-phy @@ -197,8 +199,6 @@ allOf: contains: enum: - qcom,qcs8300-qmp-gen4x2-pcie-phy - - qcom,sa8775p-qmp-gen4x2-pcie-phy - - qcom,sa8775p-qmp-gen4x4-pcie-phy then: properties: clocks: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index 358a6736a951..38ce04c35d94 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -29,6 +29,7 @@ properties: - qcom,sm8450-qmp-usb3-dp-phy - qcom,sm8550-qmp-usb3-dp-phy - qcom,sm8650-qmp-usb3-dp-phy + - qcom,sm8750-qmp-usb3-dp-phy - qcom,x1e80100-qmp-usb3-dp-phy reg: @@ -133,6 +134,7 @@ allOf: - qcom,sm6350-qmp-usb3-dp-phy - qcom,sm8550-qmp-usb3-dp-phy - qcom,sm8650-qmp-usb3-dp-phy + - qcom,sm8750-qmp-usb3-dp-phy - qcom,x1e80100-qmp-usb3-dp-phy then: required: diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml index 142b3c8839d6..854f70af0a6c 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml @@ -17,6 +17,7 @@ properties: oneOf: - items: - enum: + - qcom,milos-snps-eusb2-phy - qcom,sar2130p-snps-eusb2-phy - qcom,sdx75-snps-eusb2-phy - qcom,sm8650-snps-eusb2-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml index d16a543a7848..27f064a71c9f 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml @@ -39,21 +39,18 @@ properties: description: High-Speed disconnect threshold minimum: 0 maximum: 7 - default: 0 qcom,tune-usb2-amplitude: $ref: /schemas/types.yaml#/definitions/uint8 description: High-Speed transmit amplitude minimum: 0 maximum: 15 - default: 8 qcom,tune-usb2-preem: $ref: /schemas/types.yaml#/definitions/uint8 description: High-Speed TX pre-emphasis tuning minimum: 0 maximum: 7 - default: 5 required: - compatible diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml index 2822dce8d9f4..f45c5f039ae8 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml @@ -40,6 +40,10 @@ properties: - renesas,usb2-phy-r9a07g054 # RZ/V2L - const: renesas,rzg2l-usb2-phy + - items: + - const: renesas,usb2-phy-r9a09g056 # RZ/V2N + - const: renesas,usb2-phy-r9a09g057 + reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/samsung,mipi-video-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,mipi-video-phy.yaml index b2250e4a6b1b..16967ef8e9ec 100644 --- a/Documentation/devicetree/bindings/phy/samsung,mipi-video-phy.yaml +++ b/Documentation/devicetree/bindings/phy/samsung,mipi-video-phy.yaml @@ -29,6 +29,7 @@ properties: - samsung,s5pv210-mipi-video-phy - samsung,exynos5420-mipi-video-phy - samsung,exynos5433-mipi-video-phy + - samsung,exynos7870-mipi-video-phy "#phy-cells": const: 1 @@ -46,19 +47,20 @@ properties: deprecated: true description: Phandle to PMU system controller interface, valid for - samsung,exynos5433-mipi-video-phy (if not a child of PMU). + samsung,exynos5433-mipi-video-phy and samsung,exynos7870-mipi-video-phy + (if not a child of PMU). samsung,disp-sysreg: $ref: /schemas/types.yaml#/definitions/phandle description: Phandle to DISP system controller interface, valid for - samsung,exynos5433-mipi-video-phy. + samsung,exynos5433-mipi-video-phy and samsung,exynos7870-mipi-video-phy. samsung,cam0-sysreg: $ref: /schemas/types.yaml#/definitions/phandle description: Phandle to CAM0 system controller interface, valid for - samsung,exynos5433-mipi-video-phy. + samsung,exynos5433-mipi-video-phy and samsung,exynos7870-mipi-video-phy. samsung,cam1-sysreg: $ref: /schemas/types.yaml#/definitions/phandle @@ -84,7 +86,13 @@ allOf: samsung,disp-sysreg: false samsung,cam0-sysreg: false samsung,cam1-sysreg: false - else: + + - if: + properties: + compatible: + contains: + const: samsung,exynos5433-mipi-video-phy + then: properties: syscon: false required: @@ -92,6 +100,19 @@ allOf: - samsung,cam0-sysreg - samsung,cam1-sysreg + - if: + properties: + compatible: + contains: + const: samsung,exynos7870-mipi-video-phy + then: + properties: + syscon: false + samsung,cam1-sysreg: false + required: + - samsung,disp-sysreg + - samsung,cam0-sysreg + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml index cc60d2f6f70e..e906403208c0 100644 --- a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml +++ b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml @@ -33,6 +33,7 @@ properties: - samsung,exynos7-usbdrd-phy - samsung,exynos7870-usbdrd-phy - samsung,exynos850-usbdrd-phy + - samsung,exynos990-usbdrd-phy clocks: minItems: 1 @@ -217,6 +218,7 @@ allOf: - samsung,exynos5420-usbdrd-phy - samsung,exynos7870-usbdrd-phy - samsung,exynos850-usbdrd-phy + - samsung,exynos990-usbdrd-phy then: properties: clocks: diff --git a/Documentation/devicetree/bindings/phy/st,spear1310-miphy.yaml b/Documentation/devicetree/bindings/phy/st,spear1310-miphy.yaml new file mode 100644 index 000000000000..32f81615ddad --- /dev/null +++ b/Documentation/devicetree/bindings/phy/st,spear1310-miphy.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/st,spear1310-miphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST SPEAr miphy + +maintainers: + - Pratyush Anand + +description: + ST Microelectronics SPEAr miphy is a phy controller supporting PCIe and SATA. + +properties: + compatible: + enum: + - st,spear1310-miphy + - st,spear1340-miphy + + reg: + maxItems: 1 + + misc: + description: Phandle for the syscon node to access misc registers. + $ref: /schemas/types.yaml#/definitions/phandle + + '#phy-cells': + description: > + Cell[0] indicates interface type: 0 = SATA, 1 = PCIe. + const: 1 + + phy-id: + description: Instance id of the phy. Required when multiple PHYs are present. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - misc + - '#phy-cells' + +additionalProperties: false + +examples: + - | + miphy@1000 { + compatible = "st,spear1310-miphy"; + reg = <0x1000 0x100>; + misc = <&syscon>; + #phy-cells = <1>; + phy-id = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/st-spear-miphy.txt b/Documentation/devicetree/bindings/phy/st-spear-miphy.txt deleted file mode 100644 index 2a6bfdcc09b3..000000000000 --- a/Documentation/devicetree/bindings/phy/st-spear-miphy.txt +++ /dev/null @@ -1,15 +0,0 @@ -ST SPEAr miphy DT details -========================= - -ST Microelectronics SPEAr miphy is a phy controller supporting PCIe and SATA. - -Required properties: -- compatible : should be "st,spear1310-miphy" or "st,spear1340-miphy" -- reg : offset and length of the PHY register set. -- misc: phandle for the syscon node to access misc registers -- #phy-cells : from the generic PHY bindings, must be 1. - - cell[1]: 0 if phy used for SATA, 1 for PCIe. - -Optional properties: -- phy-id: Instance id of the phy. Only required when there are multiple phys - present on a implementation. diff --git a/Documentation/devicetree/bindings/phy/ti,da830-usb-phy.yaml b/Documentation/devicetree/bindings/phy/ti,da830-usb-phy.yaml new file mode 100644 index 000000000000..e168cbce8fd1 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,da830-usb-phy.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/ti,da830-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI DA8xx/OMAP-L1xx/AM18xx USB PHY + +maintainers: + - David Lechner + +description: > + This device controls the PHY for both the USB 1.1 OHCI and USB 2.0 OTG + controllers on DA8xx SoCs. + + It also requires a "syscon" node with compatible = "ti,da830-cfgchip", "syscon" + to access the CFGCHIP2 register. + +properties: + compatible: + items: + - const: ti,da830-usb-phy + + '#phy-cells': + const: 1 + description: + Consumers of this device should use index 0 for the USB 2.0 phy device and + index 1 for the USB 1.1 phy device. + + clocks: + maxItems: 2 + + clock-names: + items: + - const: usb0_clk48 + - const: usb1_clk48 + +required: + - compatible + - '#phy-cells' + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + usb-phy { + compatible = "ti,da830-usb-phy"; + #phy-cells = <1>; + clocks = <&usb_phy_clk 0>, <&usb_phy_clk 1>; + clock-names = "usb0_clk48", "usb1_clk48"; + }; diff --git a/Documentation/devicetree/bindings/phy/ti,dm8168-usb-phy.yaml b/Documentation/devicetree/bindings/phy/ti,dm8168-usb-phy.yaml new file mode 100644 index 000000000000..673dc1d37dcb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,dm8168-usb-phy.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/ti,dm8168-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI DM8168 USB PHY + +maintainers: + - Tony Lindgren + +properties: + compatible: + const: ti,dm8168-usb-phy + + reg: + maxItems: 1 + + reg-names: + items: + - const: phy + + clocks: + maxItems: 1 + + clock-names: + items: + - const: refclk + + '#phy-cells': + const: 0 + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle for the syscon node to access misc registers. + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - '#phy-cells' + - syscon + +additionalProperties: false + +examples: + - | + usb-phy@20 { + compatible = "ti,dm8168-usb-phy"; + reg = <0x20 0x8>; + reg-names = "phy"; + clocks = <&main_fapll 6>; + clock-names = "refclk"; + #phy-cells = <0>; + syscon = <&scm_conf>; + }; diff --git a/Documentation/devicetree/bindings/phy/ti,keystone-usbphy.yaml b/Documentation/devicetree/bindings/phy/ti,keystone-usbphy.yaml new file mode 100644 index 000000000000..08dc18e7feea --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,keystone-usbphy.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/ti,keystone-usbphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI Keystone USB PHY + +maintainers: + - Nishanth Menon + - Santosh Shilimkar + +description: + The main purpose of this PHY driver is to enable the USB PHY reference clock + gate on the Keystone SOC for both the USB2 and USB3 PHY. Otherwise it is just + an NOP PHY driver. Hence this node is referenced as both the usb2 and usb3 + phy node in the USB Glue layer driver node. + +properties: + compatible: + const: ti,keystone-usbphy + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + usb-phy@2620738 { + compatible = "ti,keystone-usbphy"; + reg = <0x2620738 32>; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml b/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml index a6ef4797e5c5..6ba66c2033b4 100644 --- a/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml +++ b/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml @@ -15,11 +15,18 @@ allOf: properties: compatible: oneOf: - - const: amlogic,pinctrl-a4 + - enum: + - amlogic,pinctrl-a4 + - amlogic,pinctrl-s6 + - amlogic,pinctrl-s7 - items: - enum: - amlogic,pinctrl-a5 - const: amlogic,pinctrl-a4 + - items: + - enum: + - amlogic,pinctrl-s7d + - const: amlogic,pinctrl-s7 "#address-cells": const: 2 diff --git a/Documentation/devicetree/bindings/pinctrl/eswin,eic7700-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/eswin,eic7700-pinctrl.yaml new file mode 100644 index 000000000000..d46e7ee6372d --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/eswin,eic7700-pinctrl.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/eswin,eic7700-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Eswin Eic7700 Pinctrl + +maintainers: + - Yulin Lu + +allOf: + - $ref: pinctrl.yaml# + +description: | + eic7700 pin configuration nodes act as a container for an arbitrary number of + subnodes. Each of these subnodes represents some desired configuration for one or + more pins. This configuration can include the mux function to select on those pin(s), + and various pin configuration parameters, such as input-enable, pull-up, etc. + +properties: + compatible: + const: eswin,eic7700-pinctrl + + reg: + maxItems: 1 + + vrgmii-supply: + description: + Regulator supply for the RGMII interface IO power domain. + This property should reference a regulator that provides either 1.8V or 3.3V, + depending on the board-level voltage configuration required by the RGMII interface. + +patternProperties: + '-grp$': + type: object + additionalProperties: false + + patternProperties: + '-pins$': + type: object + + properties: + pins: + description: + For eic7700, specifies the name(s) of one or more pins to be configured by + this node. + items: + enum: [ chip_mode, mode_set0, mode_set1, mode_set2, mode_set3, xin, + rst_out_n, key_reset_n, gpio0, por_sel, jtag0_tck, jtag0_tms, + jtag0_tdi, jtag0_tdo, gpio5, spi2_cs0_n, jtag1_tck, jtag1_tms, + jtag1_tdi, jtag1_tdo, gpio11, spi2_cs1_n, pcie_clkreq_n, + pcie_wake_n, pcie_perst_n, hdmi_scl, hdmi_sda, hdmi_cec, + jtag2_trst, rgmii0_clk_125, rgmii0_txen, rgmii0_txclk, + rgmii0_txd0, rgmii0_txd1, rgmii0_txd2, rgmii0_txd3, i2s0_bclk, + i2s0_wclk, i2s0_sdi, i2s0_sdo, i2s_mclk, rgmii0_rxclk, + rgmii0_rxdv, rgmii0_rxd0, rgmii0_rxd1, rgmii0_rxd2, rgmii0_rxd3, + i2s2_bclk, i2s2_wclk, i2s2_sdi, i2s2_sdo, gpio27, gpio28, gpio29, + rgmii0_mdc, rgmii0_mdio, rgmii0_intb, rgmii1_clk_125, rgmii1_txen, + rgmii1_txclk, rgmii1_txd0, rgmii1_txd1, rgmii1_txd2, rgmii1_txd3, + i2s1_bclk, i2s1_wclk, i2s1_sdi, i2s1_sdo, gpio34, rgmii1_rxclk, + rgmii1_rxdv, rgmii1_rxd0, rgmii1_rxd1, rgmii1_rxd2, rgmii1_rxd3, + spi1_cs0_n, spi1_clk, spi1_d0, spi1_d1, spi1_d2, spi1_d3, spi1_cs1_n, + rgmii1_mdc, rgmii1_mdio, rgmii1_intb, usb0_pwren, usb1_pwren, + i2c0_scl, i2c0_sda, i2c1_scl, i2c1_sda, i2c2_scl, i2c2_sda, + i2c3_scl, i2c3_sda, i2c4_scl, i2c4_sda, i2c5_scl, i2c5_sda, + uart0_tx, uart0_rx, uart1_tx, uart1_rx, uart1_cts, uart1_rts, + uart2_tx, uart2_rx, jtag2_tck, jtag2_tms, jtag2_tdi, jtag2_tdo, + fan_pwm, fan_tach, mipi_csi0_xvs, mipi_csi0_xhs, mipi_csi0_mclk, + mipi_csi1_xvs, mipi_csi1_xhs, mipi_csi1_mclk, mipi_csi2_xvs, + mipi_csi2_xhs, mipi_csi2_mclk, mipi_csi3_xvs, mipi_csi3_xhs, + mipi_csi3_mclk, mipi_csi4_xvs, mipi_csi4_xhs, mipi_csi4_mclk, + mipi_csi5_xvs, mipi_csi5_xhs, mipi_csi5_mclk, spi3_cs_n, spi3_clk, + spi3_di, spi3_do, gpio92, gpio93, s_mode, gpio95, spi0_cs_n, + spi0_clk, spi0_d0, spi0_d1, spi0_d2, spi0_d3, i2c10_scl, + i2c10_sda, i2c11_scl, i2c11_sda, gpio106, boot_sel0, boot_sel1, + boot_sel2, boot_sel3, gpio111, lpddr_ref_clk ] + + function: + description: + Specify the alternative function to be configured for the + given pins. + enum: [ disabled, boot_sel, chip_mode, emmc, fan_tach, + gpio, hdmi, i2c, i2s, jtag, ddr_ref_clk_sel, + lpddr_ref_clk, mipi_csi, osc, pcie, pwm, + rgmii, reset, sata, sdio, spi, s_mode, uart, usb ] + + input-schmitt-enable: true + + input-schmitt-disable: true + + bias-disable: true + + bias-pull-down: true + + bias-pull-up: true + + input-enable: true + + input-disable: true + + drive-strength-microamp: true + + required: + - pins + + additionalProperties: false + + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + - if: + properties: + pins: + anyOf: + - pattern: '^rgmii' + - const: lpddr_ref_clk + then: + properties: + drive-strength-microamp: + enum: [3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000] + else: + properties: + drive-strength-microamp: + enum: [6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000] + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + pinctrl@51600080 { + compatible = "eswin,eic7700-pinctrl"; + reg = <0x51600080 0x1fff80>; + vrgmii-supply = <&vcc_1v8>; + + dev-active-grp { + /* group node defining 1 standard pin */ + gpio10-pins { + pins = "jtag1_tdo"; + function = "gpio"; + input-enable; + bias-pull-up; + }; + + /* group node defining 2 I2C pins */ + i2c6-pins { + pins = "uart1_cts", "uart1_rts"; + function = "i2c"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8189-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8189-pinctrl.yaml new file mode 100644 index 000000000000..32e4653da5db --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8189-pinctrl.yaml @@ -0,0 +1,213 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/mediatek,mt8189-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT8189 Pin Controller + +maintainers: + - Lei Xue + - Cathy Xu + +description: + The MediaTek's MT8189 Pin controller is used to control SoC pins. + +properties: + compatible: + const: mediatek,mt8189-pinctrl + + reg: + items: + - description: gpio base + - description: lm group IO + - description: rb0 group IO + - description: rb1 group IO + - description: bm0 group IO + - description: bm1 group IO + - description: bm2 group IO + - description: lt0 group IO + - description: lt1 group IO + - description: rt group IO + - description: eint0 group IO + - description: eint1 group IO + - description: eint2 group IO + - description: eint3 group IO + - description: eint4 group IO + + reg-names: + items: + - const: base + - const: lm + - const: rb0 + - const: rb1 + - const: bm0 + - const: bm1 + - const: bm2 + - const: lt0 + - const: lt1 + - const: rt + - const: eint0 + - const: eint1 + - const: eint2 + - const: eint3 + - const: eint4 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio-ranges: + maxItems: 1 + + gpio-line-names: true + +# PIN CONFIGURATION NODES +patternProperties: + '-pins$': + type: object + additionalProperties: false + + patternProperties: + '^pins': + type: object + $ref: /schemas/pinctrl/pincfg-node.yaml + additionalProperties: false + description: + A pinctrl node should contain at least one subnode representing the + pinctrl groups available on the machine. Each subnode will list the + pins it needs, and how they should be configured, with regard to muxer + configuration, pullups, drive strength, input enable/disable and input + schmitt. + + properties: + pinmux: + description: + Integer array, represents gpio pin number and mux setting. + Supported pin number and mux varies for different SoCs, and are + defined as macros in arch/arm64/boot/dts/mediatek/mt8189-pinfunc.h + directly, for this SoC. + + drive-strength: + enum: [2, 4, 6, 8, 10, 12, 14, 16] + + bias-pull-down: + oneOf: + - type: boolean + - enum: [100, 101, 102, 103] + description: mt8189 pull down PUPD/R0/R1 type define value. + - enum: [75000, 5000] + description: mt8189 pull down RSEL type si unit value(ohm). + description: | + For pull down type is normal, it doesn't need add R1R0 define + and resistance value. + + For pull down type is PUPD/R0/R1 type, it can add R1R0 define to + set different resistance. It can support "MTK_PUPD_SET_R1R0_00" & + "MTK_PUPD_SET_R1R0_01" & "MTK_PUPD_SET_R1R0_10" & + "MTK_PUPD_SET_R1R0_11" define in mt8189. + + For pull down type is PD/RSEL, it can add resistance value(ohm) + to set different resistance by identifying property + "mediatek,rsel-resistance-in-si-unit". + + bias-pull-up: + oneOf: + - type: boolean + - enum: [100, 101, 102, 103] + description: mt8189 pull up PUPD/R0/R1 type define value. + - enum: [1000, 1500, 2000, 3000, 4000, 5000, 75000] + description: mt8189 pull up RSEL type si unit value(ohm). + description: | + For pull up type is normal, it don't need add R1R0 define + and resistance value. + + For pull up type is PUPD/R0/R1 type, it can add R1R0 define to + set different resistance. It can support "MTK_PUPD_SET_R1R0_00" & + "MTK_PUPD_SET_R1R0_01" & "MTK_PUPD_SET_R1R0_10" & + "MTK_PUPD_SET_R1R0_11" define in mt8189. + + For pull up type is PU/RSEL, it can add resistance value(ohm) + to set different resistance by identifying property + "mediatek,rsel-resistance-in-si-unit". + + bias-disable: true + + output-high: true + + output-low: true + + input-enable: true + + input-disable: true + + input-schmitt-enable: true + + input-schmitt-disable: true + + required: + - pinmux + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - '#interrupt-cells' + - gpio-controller + - '#gpio-cells' + - gpio-ranges + +additionalProperties: false + +examples: + - | + #include + #include + #define PINMUX_GPIO51__FUNC_SCL0 (MTK_PIN_NO(51) | 2) + #define PINMUX_GPIO52__FUNC_SDA0 (MTK_PIN_NO(52) | 2) + + pio: pinctrl@10005000 { + compatible = "mediatek,mt8189-pinctrl"; + reg = <0x10005000 0x1000>, + <0x11b50000 0x1000>, + <0x11c50000 0x1000>, + <0x11c60000 0x1000>, + <0x11d20000 0x1000>, + <0x11d30000 0x1000>, + <0x11d40000 0x1000>, + <0x11e20000 0x1000>, + <0x11e30000 0x1000>, + <0x11f20000 0x1000>, + <0x11ce0000 0x1000>, + <0x11de0000 0x1000>, + <0x11e60000 0x1000>, + <0x1c01e000 0x1000>, + <0x11f00000 0x1000>; + reg-names = "base", "lm", "rb0", "rb1", "bm0" , "bm1", + "bm2", "lt0", "lt1", "rt", "eint0", "eint1", + "eint2", "eint3", "eint4"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 182>; + interrupt-controller; + interrupts = ; + #interrupt-cells = <2>; + + i2c0-pins { + pins { + pinmux = , + ; + bias-disable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.txt b/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.txt deleted file mode 100644 index bd8b0c69fa44..000000000000 --- a/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.txt +++ /dev/null @@ -1,71 +0,0 @@ -NXP LPC18xx/43xx SCU pin controller Device Tree Bindings --------------------------------------------------------- - -Required properties: -- compatible : Should be "nxp,lpc1850-scu" -- reg : Address and length of the register set for the device -- clocks : Clock specifier (see clock bindings for details) - -The lpc1850-scu driver uses the generic pin multiplexing and generic pin -configuration documented in pinctrl-bindings.txt. - -The following generic nodes are supported: - - function - - pins - - bias-disable - - bias-pull-up - - bias-pull-down - - drive-strength - - input-enable - - input-disable - - input-schmitt-enable - - input-schmitt-disable - - slew-rate - -NXP specific properties: - - nxp,gpio-pin-interrupt : Assign pin to gpio pin interrupt controller - irq number 0 to 7. See example below. - -Not all pins support all properties so either refer to the NXP 1850/4350 -user manual or the pin table in the pinctrl-lpc18xx driver for supported -pin properties. - -Example: -pinctrl: pinctrl@40086000 { - compatible = "nxp,lpc1850-scu"; - reg = <0x40086000 0x1000>; - clocks = <&ccu1 CLK_CPU_SCU>; - - i2c0_pins: i2c0-pins { - i2c0_pins_cfg { - pins = "i2c0_scl", "i2c0_sda"; - function = "i2c0"; - input-enable; - }; - }; - - uart0_pins: uart0-pins { - uart0_rx_cfg { - pins = "pf_11"; - function = "uart0"; - bias-disable; - input-enable; - }; - - uart0_tx_cfg { - pins = "pf_10"; - function = "uart0"; - bias-disable; - }; - }; - - gpio_joystick_pins: gpio-joystick-pins { - gpio_joystick_1_cfg { - pins = "p9_0"; - function = "gpio"; - nxp,gpio-pin-interrupt = <0>; - input-enable; - bias-disable; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.yaml b/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.yaml new file mode 100644 index 000000000000..11f41359b5c8 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nxp,lpc1850-scu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx/43xx SCU pin controller + +description: + Not all pins support all pin generic node properties so either refer to + the NXP 1850/4350 user manual or the pin table in the pinctrl-lpc18xx + driver for supported pin properties. + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-scu + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +patternProperties: + '-pins$': + type: object + additionalProperties: false + + patternProperties: + '_cfg$': + type: object + + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + unevaluatedProperties: false + + properties: + nxp,gpio-pin-interrupt: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + description: + Assign pin to gpio pin interrupt controller + irq number 0 to 7. See example below. + +required: + - compatible + - reg + - clocks + +allOf: + - $ref: pinctrl.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + pinctrl@40086000 { + compatible = "nxp,lpc1850-scu"; + reg = <0x40086000 0x1000>; + clocks = <&ccu1 CLK_CPU_SCU>; + + gpio-joystick-pins { + gpio-joystick-1_cfg { + pins = "p9_0"; + function = "gpio"; + nxp,gpio-pin-interrupt = <0>; + input-enable; + bias-disable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,milos-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,milos-tlmm.yaml new file mode 100644 index 000000000000..0091204df20a --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,milos-tlmm.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,milos-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. Milos TLMM block + +maintainers: + - Luca Weiss + +description: + Top Level Mode Multiplexer pin controller in Qualcomm Milos SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,milos-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 84 + + gpio-line-names: + maxItems: 167 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-milos-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-milos-tlmm-state" + additionalProperties: false + +$defs: + qcom-milos-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-5][0-9]|16[0-7])$" + - 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_mclk0, + audio_ext_mclk1, audio_ref_clk, cam_mclk, cci_async_in0, + cci_i2c_scl, cci_i2c_sda, cci_timer, coex_uart1_rx, + coex_uart1_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, host2wlan_sol, i2s0_data0, + i2s0_data1, i2s0_sck, i2s0_ws, ibi_i3c, jitter_bist, mdp_vsync, + mdp_vsync0_out, mdp_vsync1_out, mdp_vsync2_out, mdp_vsync3_out, + mdp_vsync_e, nav_gpio0, nav_gpio1, nav_gpio2, 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, qlink0_enable, qlink0_request, qlink0_wmss, + qlink1_enable, qlink1_request, qlink1_wmss, qspi0, qup0_se0, + qup0_se1, qup0_se2, qup0_se3, qup0_se4, qup0_se5, qup0_se6, + qup1_se0, qup1_se1, qup1_se2, qup1_se3, qup1_se4, qup1_se5, + qup1_se6, resout_gpio_n, sd_write_protect, sdc1_clk, sdc1_cmd, + sdc1_data, sdc1_rclk, sdc2_clk, sdc2_cmd, sdc2_data, + sdc2_fb_clk, tb_trig_sdc1, tb_trig_sdc2, tgu_ch0_trigout, + tgu_ch1_trigout, tmess_prng0, tmess_prng1, tmess_prng2, + tmess_prng3, tsense_pwm1, tsense_pwm2, uim0_clk, uim0_data, + uim0_present, uim0_reset, uim1_clk_mira, uim1_clk_mirb, + uim1_data_mira, uim1_data_mirb, uim1_present_mira, + uim1_present_mirb, uim1_reset_mira, uim1_reset_mirb, usb0_hs, + usb0_phy_ps, vfr_0, vfr_1, vsense_trigger_mirnat, wcn_sw, + wcn_sw_ctrl ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + tlmm: pinctrl@f100000 { + compatible = "qcom,milos-tlmm"; + reg = <0x0f100000 0x300000>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-ranges = <&tlmm 0 0 168>; + + gpio-wo-state { + pins = "gpio1"; + function = "gpio"; + }; + + qup-uart5-default-state { + pins = "gpio25", "gpio26"; + function = "qup0_se5"; + drive-strength = <2>; + bias-disable; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml index 055cea5452eb..5e6dfcc3fe9b 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml @@ -27,6 +27,7 @@ properties: - qcom,pm6450-gpio - qcom,pm7250b-gpio - qcom,pm7325-gpio + - qcom,pm7550-gpio - qcom,pm7550ba-gpio - qcom,pm8005-gpio - qcom,pm8018-gpio @@ -64,6 +65,7 @@ properties: - qcom,pmi8994-gpio - qcom,pmi8998-gpio - qcom,pmih0108-gpio + - qcom,pmiv0104-gpio - qcom,pmk8350-gpio - qcom,pmk8550-gpio - qcom,pmm8155au-gpio @@ -228,6 +230,7 @@ allOf: - qcom,pmc8180-gpio - qcom,pmc8380-gpio - qcom,pmi8994-gpio + - qcom,pmiv0104-gpio - qcom,pmm8155au-gpio then: properties: @@ -261,6 +264,7 @@ allOf: - qcom,pm660l-gpio - qcom,pm6150l-gpio - qcom,pm7250b-gpio + - qcom,pm7550-gpio - qcom,pm8038-gpio - qcom,pm8150b-gpio - qcom,pm8150l-gpio diff --git a/Documentation/devicetree/bindings/pinctrl/raspberrypi,rp1-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/raspberrypi,rp1-gpio.yaml new file mode 100644 index 000000000000..eec9a9b58542 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/raspberrypi,rp1-gpio.yaml @@ -0,0 +1,198 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/raspberrypi,rp1-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RaspberryPi RP1 GPIO/Pinconf/Pinmux Controller submodule + +maintainers: + - A. della Porta + +description: + The RP1 chipset is a Multi Function Device containing, among other + sub-peripherals, a gpio/pinconf/mux controller whose 54 pins are grouped + into 3 banks. + It works also as an interrupt controller for those gpios. + +properties: + compatible: + const: raspberrypi,rp1-gpio + + reg: + maxItems: 3 + description: One reg specifier for each one of the 3 pin banks. + + '#gpio-cells': + description: The first cell is the pin number and the second cell is used + to specify the flags (see include/dt-bindings/gpio/gpio.h). + const: 2 + + gpio-controller: true + + gpio-ranges: + maxItems: 1 + + gpio-line-names: + maxItems: 54 + + interrupts: + maxItems: 3 + description: One interrupt specifier for each one of the 3 pin banks. + + '#interrupt-cells': + description: + Specifies the Bank number [0, 1, 2] and Flags as defined in + include/dt-bindings/interrupt-controller/irq.h. + const: 2 + + interrupt-controller: true + +patternProperties: + '-state$': + oneOf: + - $ref: '#/$defs/raspberrypi-rp1-state' + - patternProperties: + '-pins$': + $ref: '#/$defs/raspberrypi-rp1-state' + additionalProperties: false + +$defs: + raspberrypi-rp1-state: + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + description: + Pin controller client devices use pin configuration subnodes (children + and grandchildren) for desired pin configuration. + Client device subnodes use below standard properties. + + 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])$' + + function: + enum: [ alt0, alt1, alt2, alt3, alt4, gpio, alt6, alt7, alt8, none, + aaud, dcd0, dpi, dsi0_te_ext, dsi1_te_ext, dsr0, dtr0, gpclk0, + gpclk1, gpclk2, gpclk3, gpclk4, gpclk5, i2c0, i2c1, i2c2, i2c3, + i2c4, i2c5, i2c6, i2s0, i2s1, i2s2, ir, mic, pcie_clkreq_n, + pio, proc_rio, pwm0, pwm1, ri0, sd0, sd1, spi0, spi1, spi2, + spi3, spi4, spi5, spi6, spi7, spi8, uart0, uart1, uart2, uart3, + uart4, uart5, vbus0, vbus1, vbus2, vbus3 ] + + description: + Specify the alternative function to be configured for the specified + pins. + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + input-enable: true + input-schmitt-enable: true + output-enable: true + output-high: true + output-low: true + slew-rate: + description: 0 is slow slew rate, 1 is fast slew rate + enum: [ 0, 1 ] + drive-strength: + enum: [ 2, 4, 8, 12 ] + + additionalProperties: false + +allOf: + - $ref: pinctrl.yaml# + +required: + - reg + - compatible + - '#gpio-cells' + - gpio-controller + - interrupts + - '#interrupt-cells' + - interrupt-controller + +unevaluatedProperties: false + +examples: + - | + #include + + rp1 { + #address-cells = <2>; + #size-cells = <2>; + + rp1_gpio: pinctrl@c0400d0000 { + reg = <0xc0 0x400d0000 0x0 0xc000>, + <0xc0 0x400e0000 0x0 0xc000>, + <0xc0 0x400f0000 0x0 0xc000>; + compatible = "raspberrypi,rp1-gpio"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>, + <1 IRQ_TYPE_LEVEL_HIGH>, + <2 IRQ_TYPE_LEVEL_HIGH>; + gpio-line-names = + "ID_SDA", // GPIO0 + "ID_SCL", // 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", + "PCIE_RP1_WAKE", // GPIO28 + "FAN_TACH", // GPIO29 + "HOST_SDA", // GPIO30 + "HOST_SCL", // GPIO31 + "ETH_RST_N", // GPIO32 + "", // GPIO33 + "CD0_IO0_MICCLK", // GPIO34 + "CD0_IO0_MICDAT0", // GPIO35 + "RP1_PCIE_CLKREQ_N", // GPIO36 + "", // GPIO37 + "CD0_SDA", // GPIO38 + "CD0_SCL", // GPIO39 + "CD1_SDA", // GPIO40 + "CD1_SCL", // GPIO41 + "USB_VBUS_EN", // GPIO42 + "USB_OC_N", // GPIO43 + "RP1_STAT_LED", // GPIO44 + "FAN_PWM", // GPIO45 + "CD1_IO0_MICCLK", // GPIO46 + "2712_WAKE", // GPIO47 + "CD1_IO1_MICDAT1", // GPIO48 + "EN_MAX_USB_CUR", // GPIO49 + "", // GPIO50 + "", // GPIO51 + "", // GPIO52 + ""; // GPIO53 + + rp1-i2s0-default-state { + function = "i2s0"; + pins = "gpio18", "gpio19", "gpio20", "gpio21"; + bias-disable; + }; + + rp1-uart0-default-state { + txd-pins { + function = "uart0"; + pins = "gpio14"; + bias-disable; + }; + + rxd-pins { + function = "uart0"; + pins = "gpio15"; + bias-pull-up; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml index 960758dc417f..125af766b992 100644 --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml @@ -135,7 +135,7 @@ additionalProperties: description: Pin bank index. - minimum: 0 - maximum: 13 + maximum: 14 description: Mux 0 means GPIO and mux 1 to N means the specific device function. diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml new file mode 100644 index 000000000000..845b6b7b7552 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml @@ -0,0 +1,187 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) STMicroelectronics 2025. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/st,stm32-hdp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STM32 Hardware Debug Port Mux/Config + +maintainers: + - Clément LE GOFFIC + +description: + STMicroelectronics's STM32 MPUs integrate a Hardware Debug Port (HDP). + It allows to output internal signals on SoC's GPIO. + +properties: + compatible: + enum: + - st,stm32mp131-hdp + - st,stm32mp151-hdp + - st,stm32mp251-hdp + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +patternProperties: + "^hdp[0-7]-pins$": + type: object + $ref: pinmux-node.yaml# + additionalProperties: false + + properties: + pins: + pattern: '^HDP[0-7]$' + + function: true + + required: + - function + - pins + +allOf: + - $ref: pinctrl.yaml# + - if: + properties: + compatible: + contains: + const: st,stm32mp131-hdp + then: + patternProperties: + "^hdp[0-7]-pins$": + properties: + function: + enum: [ pwr_pwrwake_sys, pwr_stop_forbidden, pwr_stdby_wakeup, pwr_encomp_vddcore, + bsec_out_sec_niden, aiec_sys_wakeup, none, ddrctrl_lp_req, + pwr_ddr_ret_enable_n, dts_clk_ptat, sram3ctrl_tamp_erase_act, gpoval0, + pwr_sel_vth_vddcpu, pwr_mpu_ram_lowspeed, ca7_naxierrirq, pwr_okin_mr, + bsec_out_sec_dbgen, aiec_c1_wakeup, rcc_pwrds_mpu, ddrctrl_dfi_ctrlupd_req, + ddrctrl_cactive_ddrc_asr, sram3ctrl_hw_erase_act, nic400_s0_bready, gpoval1, + pwr_pwrwake_mpu, pwr_mpu_clock_disable_ack, ca7_ndbgreset_i, + bsec_in_rstcore_n, bsec_out_sec_bsc_dis, ddrctrl_dfi_init_complete, + ddrctrl_perf_op_is_refresh, ddrctrl_gskp_dfi_lp_req, sram3ctrl_sw_erase_act, + nic400_s0_bvalid, gpoval2, pwr_sel_vth_vddcore, pwr_mpu_clock_disable_req, + ca7_npmuirq0, ca7_nfiqout0, bsec_out_sec_dftlock, bsec_out_sec_jtag_dis, + rcc_pwrds_sys, sram3ctrl_tamp_erase_req, ddrctrl_stat_ddrc_reg_selfref_type0, + dts_valobus1_0, dts_valobus2_0, tamp_potential_tamp_erfcfg, nic400_s0_wready, + nic400_s0_rready, gpoval3, pwr_stop2_active, ca7_nl2reset_i, + ca7_npreset_varm_i, bsec_out_sec_dften, bsec_out_sec_dbgswenable, + eth1_out_pmt_intr_o, eth2_out_pmt_intr_o, ddrctrl_stat_ddrc_reg_selfref_type1, + ddrctrl_cactive_0, dts_valobus1_1, dts_valobus2_1, tamp_nreset_sram_ercfg, + nic400_s0_wlast, nic400_s0_rlast, gpoval4, ca7_standbywfil2, + pwr_vth_vddcore_ack, ca7_ncorereset_i, ca7_nirqout0, bsec_in_pwrok, + bsec_out_sec_deviceen, eth1_out_lpi_intr_o, eth2_out_lpi_intr_o, + ddrctrl_cactive_ddrc, ddrctrl_wr_credit_cnt, dts_valobus1_2, dts_valobus2_2, + pka_pka_itamp_out, nic400_s0_wvalid, nic400_s0_rvalid, gpoval5, + ca7_standbywfe0, pwr_vth_vddcpu_ack, ca7_evento, bsec_in_tamper_det, + bsec_out_sec_spniden, eth1_out_mac_speed_o1, eth2_out_mac_speed_o1, + ddrctrl_csysack_ddrc, ddrctrl_lpr_credit_cnt, dts_valobus1_3, dts_valobus2_3, + saes_tamper_out, nic400_s0_awready, nic400_s0_arready, gpoval6, + ca7_standbywfi0, pwr_rcc_vcpu_rdy, ca7_eventi, ca7_dbgack0, bsec_out_fuse_ok, + bsec_out_sec_spiden, eth1_out_mac_speed_o0, eth2_out_mac_speed_o0, + ddrctrl_csysreq_ddrc, ddrctrl_hpr_credit_cnt, dts_valobus1_4, dts_valobus2_4, + rng_tamper_out, nic400_s0_awavalid, nic400_s0_aravalid, gpoval7 ] + - if: + properties: + compatible: + contains: + const: st,stm32mp151-hdp + then: + patternProperties: + "^hdp[0-7]-pins$": + properties: + function: + enum: [ pwr_pwrwake_sys, cm4_sleepdeep, pwr_stdby_wkup, pwr_encomp_vddcore, + bsec_out_sec_niden, none, rcc_cm4_sleepdeep, gpu_dbg7, ddrctrl_lp_req, + pwr_ddr_ret_enable_n, dts_clk_ptat, gpoval0, pwr_pwrwake_mcu, cm4_halted, + ca7_naxierrirq, pwr_okin_mr, bsec_out_sec_dbgen, exti_sys_wakeup, + rcc_pwrds_mpu, gpu_dbg6, ddrctrl_dfi_ctrlupd_req, ddrctrl_cactive_ddrc_asr, + gpoval1, pwr_pwrwake_mpu, cm4_rxev, ca7_npmuirq1, ca7_nfiqout1, + bsec_in_rstcore_n, exti_c2_wakeup, rcc_pwrds_mcu, gpu_dbg5, + ddrctrl_dfi_init_complete, ddrctrl_perf_op_is_refresh, + ddrctrl_gskp_dfi_lp_req, gpoval2, pwr_sel_vth_vddcore, cm4_txev, ca7_npmuirq0, + ca7_nfiqout0, bsec_out_sec_dftlock, exti_c1_wakeup, rcc_pwrds_sys, gpu_dbg4, + ddrctrl_stat_ddrc_reg_selfref_type0, ddrctrl_cactive_1, dts_valobus1_0, + dts_valobus2_0, gpoval3, pwr_mpu_pdds_not_cstbydis, cm4_sleeping, ca7_nreset1, + ca7_nirqout1, bsec_out_sec_dften, bsec_out_sec_dbgswenable, + eth_out_pmt_intr_o, gpu_dbg3, ddrctrl_stat_ddrc_reg_selfref_type1, + ddrctrl_cactive_0, dts_valobus1_1, dts_valobus2_1, gpoval4, ca7_standbywfil2, + pwr_vth_vddcore_ack, ca7_nreset0, ca7_nirqout0, bsec_in_pwrok, + bsec_out_sec_deviceen, eth_out_lpi_intr_o, gpu_dbg2, ddrctrl_cactive_ddrc, + ddrctrl_wr_credit_cnt, dts_valobus1_2, dts_valobus2_2, gpoval5, + ca7_standbywfi1, ca7_standbywfe1, ca7_evento, ca7_dbgack1, + bsec_out_sec_spniden, eth_out_mac_speed_o1, gpu_dbg1, ddrctrl_csysack_ddrc, + ddrctrl_lpr_credit_cnt, dts_valobus1_3, dts_valobus2_3, gpoval6, + ca7_standbywfi0, ca7_standbywfe0, ca7_dbgack0, bsec_out_fuse_ok, + bsec_out_sec_spiden, eth_out_mac_speed_o0, gpu_dbg0, ddrctrl_csysreq_ddrc, + ddrctrl_hpr_credit_cnt, dts_valobus1_4, dts_valobus2_4, gpoval7 ] + - if: + properties: + compatible: + contains: + const: st,stm32mp251-hdp + then: + patternProperties: + "^hdp[0-7]-pins$": + properties: + function: + enum: [ pwr_pwrwake_sys, cpu2_sleep_deep, bsec_out_tst_sdr_unlock_or_disable_scan, + bsec_out_nidenm, bsec_out_nidena, cpu2_state_0, rcc_pwrds_sys, gpu_dbg7, + ddrss_csysreq_ddrc, ddrss_dfi_phyupd_req, cpu3_sleep_deep, + d2_gbl_per_clk_bus_req, pcie_usb_cxpl_debug_info_ei_0, + pcie_usb_cxpl_debug_info_ei_8, d3_state_0, gpoval0, pwr_pwrwake_cpu2, + cpu2_halted, cpu2_state_1, bsec_out_dbgenm, bsec_out_dbgena, exti1_sys_wakeup, + rcc_pwrds_cpu2, gpu_dbg6, ddrss_csysack_ddrc, ddrss_dfi_phymstr_req, + cpu3_halted, d2_gbl_per_dma_req, pcie_usb_cxpl_debug_info_ei_1, + pcie_usb_cxpl_debug_info_ei_9, d3_state_1, gpoval1, pwr_pwrwake_cpu1, + cpu2_rxev, cpu1_npumirq1, cpu1_nfiqout1, bsec_out_shdbgen, exti1_cpu2_wakeup, + rcc_pwrds_cpu1, gpu_dbg5, ddrss_cactive_ddrc, ddrss_dfi_lp_req, cpu3_rxev, + hpdma1_clk_bus_req, pcie_usb_cxpl_debug_info_ei_2, + pcie_usb_cxpl_debug_info_ei_10, d3_state_2, gpoval2, pwr_sel_vth_vddcpu, + cpu2_txev, cpu1_npumirq0, cpu1_nfiqout0, bsec_out_ddbgen, exti1_cpu1_wakeup, + cpu3_state_0, gpu_dbg4, ddrss_mcdcg_en, ddrss_dfi_freq_0, cpu3_txev, + hpdma2_clk_bus_req, pcie_usb_cxpl_debug_info_ei_3, + pcie_usb_cxpl_debug_info_ei_11, d1_state_0, gpoval3, pwr_sel_vth_vddcore, + cpu2_sleeping, cpu1_evento, cpu1_nirqout1, bsec_out_spnidena, exti2_d3_wakeup, + eth1_out_pmt_intr_o, gpu_dbg3, ddrss_dphycg_en, ddrss_obsp0, cpu3_sleeping, + hpdma3_clk_bus_req, pcie_usb_cxpl_debug_info_ei_4, + pcie_usb_cxpl_debug_info_ei_12, d1_state_1, gpoval4, cpu1_standby_wfil2, + none, cpu1_nirqout0, bsec_out_spidena, exti2_cpu3_wakeup, eth1_out_lpi_intr_o, + gpu_dbg2, ddrctrl_dfi_init_start, ddrss_obsp1, cpu3_state_1, + d3_gbl_per_clk_bus_req, pcie_usb_cxpl_debug_info_ei_5, + pcie_usb_cxpl_debug_info_ei_13, d1_state_2, gpoval5, cpu1_standby_wfi1, + cpu1_standby_wfe1, cpu1_halted1, cpu1_naxierrirq, bsec_out_spnidenm, + exti2_cpu2_wakeup, eth2_out_pmt_intr_o, gpu_dbg1, ddrss_dfi_init_complete, + ddrss_obsp2, d2_state_0, d3_gbl_per_dma_req, pcie_usb_cxpl_debug_info_ei_6, + pcie_usb_cxpl_debug_info_ei_14, cpu1_state_0, gpoval6, cpu1_standby_wfi0, + cpu1_standby_wfe0, cpu1_halted0, bsec_out_spidenm, exti2_cpu1__wakeup, + eth2_out_lpi_intr_o, gpu_dbg0, ddrss_dfi_ctrlupd_req, ddrss_obsp3, d2_state_1, + lpdma1_clk_bus_req, pcie_usb_cxpl_debug_info_ei_7, + pcie_usb_cxpl_debug_info_ei_15, cpu1_state_1, gpoval7 ] + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + #include + + pinctrl@54090000 { + compatible = "st,stm32mp151-hdp"; + reg = <0x54090000 0x400>; + clocks = <&rcc HDP>; + pinctrl-names = "default"; + pinctrl-0 = <&hdp2_gpo>; + hdp2_gpo: hdp2-pins { + function = "gpoval2"; + pins = "HDP2"; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml index a28d77748095..961161c2ab62 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml @@ -32,13 +32,16 @@ properties: '#address-cells': const: 1 + '#size-cells': const: 1 ranges: true + pins-are-numbered: $ref: /schemas/types.yaml#/definitions/flag deprecated: true + hwlocks: true interrupts: @@ -67,22 +70,29 @@ patternProperties: additionalProperties: false properties: gpio-controller: true + '#gpio-cells': const: 2 + interrupt-controller: true '#interrupt-cells': const: 2 reg: maxItems: 1 + clocks: maxItems: 1 + resets: maxItems: 1 + gpio-line-names: true + gpio-ranges: minItems: 1 maxItems: 16 + ngpios: description: Number of available gpios in a bank. @@ -160,9 +170,13 @@ patternProperties: * ... * 16 : Alternate Function 15 * 17 : Analog + * 18 : Reserved To simplify the usage, macro is available to generate "pinmux" field. This macro is available here: - include/dt-bindings/pinctrl/stm32-pinfunc.h + Setting the pinmux's function to the Reserved (RSVD) value is used to inform + the driver that it shall not apply the mux setting. This can be used to + reserve some pins, for example to a co-processor not running Linux. Some examples of using macro: /* GPIO A9 set as alternate function 2 */ ... { @@ -176,21 +190,32 @@ patternProperties: ... { pinmux = ; }; + /* GPIO A9 reserved for co-processor */ + ... { + pinmux = ; + }; bias-disable: type: boolean + bias-pull-down: type: boolean + bias-pull-up: type: boolean + drive-push-pull: type: boolean + drive-open-drain: type: boolean + output-low: type: boolean + output-high: type: boolean + slew-rate: description: | 0: Low speed diff --git a/Documentation/devicetree/bindings/pmem/pmem-region.txt b/Documentation/devicetree/bindings/pmem/pmem-region.txt deleted file mode 100644 index cd79975e85ec..000000000000 --- a/Documentation/devicetree/bindings/pmem/pmem-region.txt +++ /dev/null @@ -1,65 +0,0 @@ -Device-tree bindings for persistent memory regions ------------------------------------------------------ - -Persistent memory refers to a class of memory devices that are: - - a) Usable as main system memory (i.e. cacheable), and - b) Retain their contents across power failure. - -Given b) it is best to think of persistent memory as a kind of memory mapped -storage device. To ensure data integrity the operating system needs to manage -persistent regions separately to the normal memory pool. To aid with that this -binding provides a standardised interface for discovering where persistent -memory regions exist inside the physical address space. - -Bindings for the region nodes: ------------------------------ - -Required properties: - - compatible = "pmem-region" - - - reg = ; - The reg property should specify an address range that is - translatable to a system physical address range. This address - range should be mappable as normal system memory would be - (i.e cacheable). - - If the reg property contains multiple address ranges - each address range will be treated as though it was specified - in a separate device node. Having multiple address ranges in a - node implies no special relationship between the two ranges. - -Optional properties: - - Any relevant NUMA associativity properties for the target platform. - - - volatile; This property indicates that this region is actually - backed by non-persistent memory. This lets the OS know that it - may skip the cache flushes required to ensure data is made - persistent after a write. - - If this property is absent then the OS must assume that the region - is backed by non-volatile memory. - -Examples: --------------------- - - /* - * This node specifies one 4KB region spanning from - * 0x5000 to 0x5fff that is backed by non-volatile memory. - */ - pmem@5000 { - compatible = "pmem-region"; - reg = <0x00005000 0x00001000>; - }; - - /* - * This node specifies two 4KB regions that are backed by - * volatile (normal) memory. - */ - pmem@6000 { - compatible = "pmem-region"; - reg = < 0x00006000 0x00001000 - 0x00008000 0x00001000 >; - volatile; - }; - diff --git a/Documentation/devicetree/bindings/pmem/pmem-region.yaml b/Documentation/devicetree/bindings/pmem/pmem-region.yaml new file mode 100644 index 000000000000..bd0f0c793f03 --- /dev/null +++ b/Documentation/devicetree/bindings/pmem/pmem-region.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pmem-region.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +maintainers: + - Oliver O'Halloran + +title: Persistent Memory Regions + +description: | + Persistent memory refers to a class of memory devices that are: + + a) Usable as main system memory (i.e. cacheable), and + b) Retain their contents across power failure. + + Given b) it is best to think of persistent memory as a kind of memory mapped + storage device. To ensure data integrity the operating system needs to manage + persistent regions separately to the normal memory pool. To aid with that this + binding provides a standardised interface for discovering where persistent + memory regions exist inside the physical address space. + +properties: + compatible: + const: pmem-region + + reg: + maxItems: 1 + + volatile: + description: + Indicates the region is volatile (non-persistent) and the OS can skip + cache flushes for writes + type: boolean + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + pmem@5000 { + compatible = "pmem-region"; + reg = <0x00005000 0x00001000>; + }; diff --git a/Documentation/devicetree/bindings/power/allwinner,sun20i-d1-ppu.yaml b/Documentation/devicetree/bindings/power/allwinner,sun20i-d1-ppu.yaml index f578be6a3bc8..a28e75a9cb6a 100644 --- a/Documentation/devicetree/bindings/power/allwinner,sun20i-d1-ppu.yaml +++ b/Documentation/devicetree/bindings/power/allwinner,sun20i-d1-ppu.yaml @@ -16,8 +16,10 @@ description: properties: compatible: enum: - - allwinner,sun20i-d1-ppu - allwinner,sun8i-v853-ppu + - allwinner,sun20i-d1-ppu + - allwinner,sun55i-a523-pck-600 + - allwinner,sun55i-a523-ppu reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml index 8fdb529d560b..b1147dbf2e73 100644 --- a/Documentation/devicetree/bindings/power/power-domain.yaml +++ b/Documentation/devicetree/bindings/power/power-domain.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Generic PM domains maintainers: - - Rafael J. Wysocki + - Rafael J. Wysocki - Kevin Hilman - Ulf Hansson diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 1bf65f2a583a..af5fef872529 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -17,7 +17,9 @@ properties: compatible: oneOf: - enum: + - qcom,glymur-rpmhpd - qcom,mdm9607-rpmpd + - qcom,milos-rpmhpd - qcom,msm8226-rpmpd - qcom,msm8909-rpmpd - qcom,msm8916-rpmpd diff --git a/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml b/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml new file mode 100644 index 000000000000..ce5ed88493cd --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/reset/apple,smc-reboot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SMC Reboot Controller + +description: + The Apple System Management Controller (SMC) provides reboot functionality + on Apple Silicon SoCs. It uses NVMEM cells to store and track various + system state information related to boot, shutdown, and panic events. + +maintainers: + - Sven Peter + +properties: + compatible: + const: apple,smc-reboot + + nvmem-cells: + items: + - description: Flag indicating shutdown (as opposed to reboot) + - description: Stage at which the boot process stopped (0x30 for normal boot) + - description: Counter for boot errors + - description: Counter for system panics + + nvmem-cell-names: + items: + - const: shutdown_flag + - const: boot_stage + - const: boot_error_count + - const: panic_count + +required: + - compatible + - nvmem-cells + - nvmem-cell-names + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml index 3da3d02a6690..979a377cb4ff 100644 --- a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml +++ b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml @@ -115,40 +115,40 @@ allOf: examples: - | - #include - #include - #include + #include + #include + #include - spmi@c440000 { - reg = <0x0c440000 0x1100>; - #address-cells = <2>; - #size-cells = <0>; + spmi@c440000 { + reg = <0x0c440000 0x1100>; + #address-cells = <2>; + #size-cells = <0>; - pmic@0 { - reg = <0x0 SPMI_USID>; - #address-cells = <1>; - #size-cells = <0>; + pmic@0 { + reg = <0x0 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; - pon@800 { - compatible = "qcom,pm8998-pon"; - reg = <0x800>; + pon@800 { + compatible = "qcom,pm8998-pon"; + reg = <0x800>; - pwrkey { - compatible = "qcom,pm8941-pwrkey"; - interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>; - debounce = <15625>; - bias-pull-up; - linux,code = ; - }; + pwrkey { + compatible = "qcom,pm8941-pwrkey"; + interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>; + debounce = <15625>; + bias-pull-up; + linux,code = ; + }; - resin { - compatible = "qcom,pm8941-resin"; - interrupts = <0x0 0x8 1 IRQ_TYPE_EDGE_BOTH>; - debounce = <15625>; - bias-pull-up; - linux,code = ; - }; - }; - }; - }; + resin { + compatible = "qcom,pm8941-resin"; + interrupts = <0x0 0x8 1 IRQ_TYPE_EDGE_BOTH>; + debounce = <15625>; + bias-pull-up; + linux,code = ; + }; + }; + }; + }; ... diff --git a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml index f494b7710c09..a884e49c995f 100644 --- a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml +++ b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml @@ -40,6 +40,7 @@ properties: - rockchip,rk3366-power-controller - rockchip,rk3368-power-controller - rockchip,rk3399-power-controller + - rockchip,rk3528-power-controller - rockchip,rk3562-power-controller - rockchip,rk3568-power-controller - rockchip,rk3576-power-controller diff --git a/Documentation/devicetree/bindings/power/supply/bq24190.yaml b/Documentation/devicetree/bindings/power/supply/bq24190.yaml index 307c99c07721..ac9a76fc5876 100644 --- a/Documentation/devicetree/bindings/power/supply/bq24190.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq24190.yaml @@ -48,7 +48,6 @@ properties: battery device. monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle description: | phandle to a "simple-battery" compatible node. diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml index 845822c87f2a..0e99a218e662 100644 --- a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml @@ -53,15 +53,16 @@ properties: minimum: 50000 maximum: 500000 - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to the battery node being monitored + monitored-battery: true required: - compatible - reg - monitored-battery +allOf: + - $ref: power-supply.yaml# + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml index a76afe3ca299..8cee37b9879e 100644 --- a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml @@ -58,9 +58,7 @@ properties: minimum: 100000 maximum: 3200000 - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to the battery node being monitored + monitored-battery: true interrupts: maxItems: 1 @@ -78,6 +76,7 @@ required: - monitored-battery allOf: + - $ref: power-supply.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/power/supply/bq25980.yaml b/Documentation/devicetree/bindings/power/supply/bq25980.yaml index 256adbef55eb..0b5d005dc780 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25980.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq25980.yaml @@ -73,9 +73,7 @@ properties: description: | Indicates that the device state has changed. - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to the battery node being monitored + monitored-battery: true required: - compatible diff --git a/Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml b/Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml index dc697b6147b2..f7bde324153d 100644 --- a/Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml @@ -43,10 +43,7 @@ properties: minItems: 1 maxItems: 8 # Should be enough - monitored-battery: - description: - Specifies the phandle of a simple-battery connected to this gauge - $ref: /schemas/types.yaml#/definitions/phandle + monitored-battery: true required: - compatible diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml index 90c7dc7632c5..70f5cd6eaeab 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml @@ -38,9 +38,7 @@ properties: - const: usbin_i - const: usbin_v - monitored-battery: - description: phandle to the simple-battery node - $ref: /schemas/types.yaml#/definitions/phandle + monitored-battery: true required: - compatible @@ -51,6 +49,9 @@ required: - io-channel-names - monitored-battery +allOf: + - $ref: power-supply.yaml# + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt5033-charger.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt5033-charger.yaml index 5b3edd79a523..d91eced9f5fb 100644 --- a/Documentation/devicetree/bindings/power/supply/richtek,rt5033-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/richtek,rt5033-charger.yaml @@ -18,7 +18,6 @@ properties: const: richtek,rt5033-charger monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle description: | Phandle to the monitored battery according to battery.yaml. The battery node needs to contain five parameters. @@ -54,6 +53,9 @@ properties: required: - monitored-battery +allOf: + - $ref: power-supply.yaml# + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml index 525abdfb3e2d..c464aa82255a 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml @@ -17,9 +17,7 @@ properties: compatible: const: stericsson,ab8500-btemp - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to battery node + monitored-battery: true battery: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml index 10bbdcfc87b6..39914b9e0cf5 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml @@ -17,9 +17,7 @@ properties: compatible: const: stericsson,ab8500-chargalg - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to battery node + monitored-battery: true battery: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml index e33329b3af61..994fac12c8da 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml @@ -17,9 +17,7 @@ properties: compatible: const: stericsson,ab8500-charger - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to battery node + monitored-battery: true battery: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml index 6a724ca90e99..92e4eb08fd61 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml @@ -17,9 +17,7 @@ properties: compatible: const: stericsson,ab8500-fg - monitored-battery: - $ref: /schemas/types.yaml#/definitions/phandle - description: phandle to battery node + monitored-battery: true battery: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml index 2d552becbfe6..65ed92bb05f3 100644 --- a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml @@ -23,9 +23,7 @@ properties: interrupts: maxItems: 1 - monitored-battery: - description: phandle to the battery node - $ref: /schemas/types.yaml#/definitions/phandle + monitored-battery: true summit,enable-usb-charging: type: boolean @@ -94,6 +92,7 @@ properties: unevaluatedProperties: false allOf: + - $ref: power-supply.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml index 3504c76a01d8..a90d558e7f86 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml @@ -26,11 +26,7 @@ properties: - const: x-powers,axp813-battery-power-supply - const: x-powers,axp813-battery-power-supply - monitored-battery: - description: - Specifies the phandle of an optional simple-battery connected to - this gauge. - $ref: /schemas/types.yaml#/definitions/phandle + monitored-battery: true x-powers,no-thermistor: type: boolean diff --git a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt deleted file mode 100644 index f8d2b7fe06d6..000000000000 --- a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt +++ /dev/null @@ -1,111 +0,0 @@ -* Freescale MSI interrupt controller - -Required properties: -- compatible : compatible list, may contain one or two entries - The first is "fsl,CHIP-msi", where CHIP is the processor(mpc8610, mpc8572, - etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" or - "fsl,mpic-msi-v4.3" depending on the parent type and version. If mpic - version is 4.3, the number of MSI registers is increased to 16, MSIIR1 is - provided to access these 16 registers, and compatible "fsl,mpic-msi-v4.3" - should be used. The first entry is optional; the second entry is - required. - -- reg : It may contain one or two regions. The first region should contain - the address and the length of the shared message interrupt register set. - The second region should contain the address of aliased MSIIR or MSIIR1 - register for platforms that have such an alias, if using MSIIR1, the second - region must be added because different MSI group has different MSIIR1 offset. - -- interrupts : each one of the interrupts here is one entry per 32 MSIs, - and routed to the host interrupt controller. the interrupts should - be set as edge sensitive. If msi-available-ranges is present, only - the interrupts that correspond to available ranges shall be present. - -Optional properties: -- msi-available-ranges: use style section to define which - msi interrupt can be used in the 256 msi interrupts. This property is - optional, without this, all the MSI interrupts can be used. - Each available range must begin and end on a multiple of 32 (i.e. - no splitting an individual MSI register or the associated PIC interrupt). - MPIC v4.3 does not support this property because the 32 interrupts of an - individual register are not continuous when using MSIIR1. - -- msi-address-64: 64-bit PCI address of the MSIIR register. The MSIIR register - is used for MSI messaging. The address of MSIIR in PCI address space is - the MSI message address. - - This property may be used in virtualized environments where the hypervisor - has created an alternate mapping for the MSIR block. See below for an - explanation. - - -Example: - msi@41600 { - compatible = "fsl,mpc8610-msi", "fsl,mpic-msi"; - reg = <0x41600 0x80>; - msi-available-ranges = <0 0x100>; - interrupts = < - 0xe0 0 - 0xe1 0 - 0xe2 0 - 0xe3 0 - 0xe4 0 - 0xe5 0 - 0xe6 0 - 0xe7 0>; - interrupt-parent = <&mpic>; - }; - - msi@41600 { - compatible = "fsl,mpic-msi-v4.3"; - reg = <0x41600 0x200 0x44148 4>; - interrupts = < - 0xe0 0 0 0 - 0xe1 0 0 0 - 0xe2 0 0 0 - 0xe3 0 0 0 - 0xe4 0 0 0 - 0xe5 0 0 0 - 0xe6 0 0 0 - 0xe7 0 0 0 - 0x100 0 0 0 - 0x101 0 0 0 - 0x102 0 0 0 - 0x103 0 0 0 - 0x104 0 0 0 - 0x105 0 0 0 - 0x106 0 0 0 - 0x107 0 0 0>; - }; - -The Freescale hypervisor and msi-address-64 -------------------------------------------- -Normally, PCI devices have access to all of CCSR via an ATMU mapping. The -Freescale MSI driver calculates the address of MSIIR (in the MSI register -block) and sets that address as the MSI message address. - -In a virtualized environment, the hypervisor may need to create an IOMMU -mapping for MSIIR. The Freescale ePAPR hypervisor has this requirement -because of hardware limitations of the Peripheral Access Management Unit -(PAMU), which is currently the only IOMMU that the hypervisor supports. -The ATMU is programmed with the guest physical address, and the PAMU -intercepts transactions and reroutes them to the true physical address. - -In the PAMU, each PCI controller is given only one primary window. The -PAMU restricts DMA operations so that they can only occur within a window. -Because PCI devices must be able to DMA to memory, the primary window must -be used to cover all of the guest's memory space. - -PAMU primary windows can be divided into 256 subwindows, and each -subwindow can have its own address mapping ("guest physical" to "true -physical"). However, each subwindow has to have the same alignment, which -means they cannot be located at just any address. Because of these -restrictions, it is usually impossible to create a 4KB subwindow that -covers MSIIR where it's normally located. - -Therefore, the hypervisor has to create a subwindow inside the same -primary window used for memory, but mapped to the MSIR block (where MSIIR -lives). The first subwindow after the end of guest memory is used for -this. The address specified in the msi-address-64 property is the PCI -address of MSIIR. The hypervisor configures the PAMU to map that address to -the true physical address of MSIIR. diff --git a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt index 6f69a9dfe198..df060a0d7d4a 100644 --- a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt +++ b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt @@ -139,10 +139,6 @@ Nintendo Wii device tree - interrupt-controller - interrupts : should contain the cascade interrupt of the "flipper" pic -1.l) The General Purpose I/O (GPIO) controller node - - see Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt - 1.m) The control node Represents the control interface used to setup several miscellaneous diff --git a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml index 5575c58357d6..e4c2d5186ded 100644 --- a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml +++ b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml @@ -14,7 +14,7 @@ description: The Analog Devices AXI PWM generator can generate PWM signals with variable pulse width and period. - https://wiki.analog.com/resources/fpga/docs/axi_pwm_gen + https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html allOf: - $ref: pwm.yaml# diff --git a/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml b/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml new file mode 100644 index 000000000000..7dbc7c2cd802 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/argon40,fan-hat.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Argon40 Fan HAT PWM controller + +maintainers: + - Marek Vasut + +description: + The trivial PWM on Argon40 Fan HAT, which is a RaspberryPi blower fan + hat which can be controlled over I2C, generates a fixed 30 kHz period + PWM signal with configurable 0..100% duty cycle to control the fan + speed. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: argon40,fan-hat + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pwm@1a { + compatible = "argon40,fan-hat"; + reg = <0x1a>; + #pwm-cells = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt deleted file mode 100644 index 43d9f4f08a2e..000000000000 --- a/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt +++ /dev/null @@ -1,20 +0,0 @@ -* NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver - -Required properties: - - compatible: Should be "nxp,lpc1850-sct-pwm" - - reg: Should contain physical base address and length of pwm registers. - - clocks: Must contain an entry for each entry in clock-names. - See ../clock/clock-bindings.txt for details. - - clock-names: Must include the following entries. - - pwm: PWM operating clock. - - #pwm-cells: Should be 3. See pwm.yaml in this directory for the description - of the cells format. - -Example: - pwm: pwm@40000000 { - compatible = "nxp,lpc1850-sct-pwm"; - reg = <0x40000000 0x1000>; - clocks =<&ccu1 CLK_CPU_SCT>; - clock-names = "pwm"; - #pwm-cells = <3>; - }; diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt deleted file mode 100644 index 74b5bc5dd19a..000000000000 --- a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt +++ /dev/null @@ -1,17 +0,0 @@ -LPC32XX PWM controller - -Required properties: -- compatible: should be "nxp,lpc3220-pwm" -- reg: physical base address and length of the controller's registers - -Examples: - -pwm@4005c000 { - compatible = "nxp,lpc3220-pwm"; - reg = <0x4005c000 0x4>; -}; - -pwm@4005c004 { - compatible = "nxp,lpc3220-pwm"; - reg = <0x4005c004 0x4>; -}; diff --git a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml index 9ee1946dc2e1..8df327e52810 100644 --- a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml @@ -11,26 +11,47 @@ maintainers: allOf: - $ref: pwm.yaml# + - if: + properties: + compatible: + contains: + const: spacemit,k1-pwm + then: + properties: + "#pwm-cells": + const: 3 + else: + properties: + "#pwm-cells": + const: 1 + description: | + Used for specifying the period length in nanoseconds. properties: compatible: - enum: - - marvell,pxa250-pwm - - marvell,pxa270-pwm - - marvell,pxa168-pwm - - marvell,pxa910-pwm + oneOf: + - enum: + - marvell,pxa250-pwm + - marvell,pxa270-pwm + - marvell,pxa168-pwm + - marvell,pxa910-pwm + - items: + - const: spacemit,k1-pwm + - const: marvell,pxa910-pwm reg: # Length should be 0x10 maxItems: 1 "#pwm-cells": - # Used for specifying the period length in nanoseconds - const: 1 + description: Number of cells in a pwm specifier. clocks: maxItems: 1 + resets: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml index d515c09e1021..fc31758a40b0 100644 --- a/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml @@ -18,6 +18,7 @@ properties: - enum: - mediatek,mt2712-pwm - mediatek,mt6795-pwm + - mediatek,mt6991-pwm - mediatek,mt7622-pwm - mediatek,mt7623-pwm - mediatek,mt7628-pwm @@ -32,6 +33,10 @@ properties: - enum: - mediatek,mt8195-pwm - const: mediatek,mt8183-pwm + - items: + - enum: + - mediatek,mt8196-pwm + - const: mediatek,mt6991-pwm reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml b/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml new file mode 100644 index 000000000000..ffda0123878e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/nxp,lpc1850-sct-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx State Configurable Timer + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-sct-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: pwm + + '#pwm-cells': + const: 3 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#pwm-cells' + +allOf: + - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + pwm@40000000 { + compatible = "nxp,lpc1850-sct-pwm"; + reg = <0x40000000 0x1000>; + clocks =<&ccu1 CLK_CPU_SCT>; + clock-names = "pwm"; + #pwm-cells = <3>; + }; diff --git a/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml b/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml new file mode 100644 index 000000000000..d8ebb0735c96 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/nxp,lpc3220-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32XX PWM controller + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-pwm + - nxp,lpc3220-motor-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#pwm-cells': + const: 3 + +required: + - compatible + - reg + - '#pwm-cells' + +allOf: + - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: + - | + pwm@4005c000 { + compatible = "nxp,lpc3220-pwm"; + reg = <0x4005c000 0x4>; + #pwm-cells = <3>; + }; + diff --git a/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml b/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml index bbb6326d47d7..e0e91aa237ec 100644 --- a/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml @@ -17,7 +17,9 @@ allOf: properties: compatible: - const: sophgo,sg2042-pwm + enum: + - sophgo,sg2042-pwm + - sophgo,sg2044-pwm reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/regulator/infineon,ir38060.yaml b/Documentation/devicetree/bindings/regulator/infineon,ir38060.yaml index e6ffbc2a2298..57ff6bf1e188 100644 --- a/Documentation/devicetree/bindings/regulator/infineon,ir38060.yaml +++ b/Documentation/devicetree/bindings/regulator/infineon,ir38060.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Infineon Buck Regulators with PMBUS interfaces maintainers: - - Not Me. + - Guenter Roeck allOf: - $ref: regulator.yaml# diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index 704828687970..685ccf9cf4d4 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -17,9 +17,11 @@ properties: compatible: enum: - mediatek,mt6873-dvfsrc-regulator + - mediatek,mt6893-dvfsrc-regulator - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator + - mediatek,mt8196-dvfsrc-regulator dvfsrc-vcore: description: DVFSRC-controlled SoC Vcore regulator diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml index 4ffe5c3faea0..a5486c36830f 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml @@ -100,6 +100,15 @@ properties: PMIC default "STANDBY" state voltage in uV. Only Buck1~3 have such dvs(dynamic voltage scaling) property. + regulator-allowed-modes: + description: | + Buck regulator operating modes allowed. Valid values below. + Users should use the macros from dt-bindings/regulator/nxp,pca9450-regulator.h + 0 (PCA9450_BUCK_MODE_AUTO): Auto PFM/PWM mode + 1 (PCA9450_BUCK_MODE_FORCE_PWM): Forced PWM mode + items: + enum: [ 0, 1 ] + unevaluatedProperties: false additionalProperties: false @@ -143,6 +152,7 @@ allOf: examples: - | #include + #include i2c { #address-cells = <1>; @@ -179,6 +189,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-initial-mode = ; + regulator-allowed-modes = ; }; buck5: BUCK5 { regulator-name = "BUCK5"; @@ -186,6 +198,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-allowed-modes = ; }; buck6: BUCK6 { regulator-name = "BUCK6"; diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3a5a0a6cf5cc..4c5b0629aa3e 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -40,6 +40,7 @@ description: | For PM660, smps1 - smps6, ldo1 - ldo3, ldo5 - ldo19 For PM660L, smps1 - smps3, smps5, ldo1 - ldo8, bob For PM7325, smps1 - smps8, ldo1 - ldo19 + For PM7550, smps1 - smps6, ldo1 - ldo23, bob For PM8005, smps1 - smps4 For PM8009, smps1 - smps2, ldo1 - ldo7 For PM8010, ldo1 - ldo7 @@ -53,6 +54,7 @@ description: | For PMI8998, bob For PMC8380, smps1 - smps8, ldo1 - lodo3 For PMR735A, smps1 - smps3, ldo1 - ldo7 + For PMR735B, ldo1 - ldo12 For PMX55, smps1 - smps7, ldo1 - ldo16 For PMX65, smps1 - smps8, ldo1 - ldo21 For PMX75, smps1 - smps10, ldo1 - ldo21 @@ -66,6 +68,7 @@ properties: - qcom,pm660-rpmh-regulators - qcom,pm660l-rpmh-regulators - qcom,pm7325-rpmh-regulators + - qcom,pm7550-rpmh-regulators - qcom,pm8005-rpmh-regulators - qcom,pm8009-rpmh-regulators - qcom,pm8009-1-rpmh-regulators @@ -87,6 +90,7 @@ properties: - qcom,pmm8155au-rpmh-regulators - qcom,pmm8654au-rpmh-regulators - qcom,pmr735a-rpmh-regulators + - qcom,pmr735b-rpmh-regulators - qcom,pmx55-rpmh-regulators - qcom,pmx65-rpmh-regulators - qcom,pmx75-rpmh-regulators @@ -218,6 +222,25 @@ allOf: "^vdd-l[358]-supply$": true "^vdd-s[1-8]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pm7550-rpmh-regulators + then: + properties: + vdd-bob-supply: + description: BOB regulator parent supply phandle. + vdd-l2-l3-supply: true + vdd-l4-l5-supply: true + vdd-l9-l10-supply: true + vdd-l12-l14-supply: true + vdd-l13-l16-supply: true + vdd-l15-l17-l18-l19-l20-l21-l22-l23-supply: true + patternProperties: + "^vdd-l(1|[6-8]|11)-supply$": true + "^vdd-s[1-6]-supply$": true + - if: properties: compatible: @@ -424,6 +447,18 @@ allOf: patternProperties: "^vdd-s[1-3]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pmr735b-rpmh-regulators + then: + properties: + vdd-l1-l2-supply: true + vdd-l7-l8-supply: true + patternProperties: + "^vdd-l([3-6]|9|1[0-2])-supply$": true + - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml index 41678400e63f..18944d39d08f 100644 --- a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml @@ -12,14 +12,17 @@ maintainers: description: | The RaspberryPi 7" display has an ATTINY88-based regulator/backlight controller on the PCB, which is used to turn the display unit on/off - and control the backlight. + and control the backlight. The V2 supports 5" and 7" panels and also + offers PWM backlight control. allOf: - $ref: regulator.yaml# properties: compatible: - const: raspberrypi,7inch-touchscreen-panel-regulator + enum: + - raspberrypi,7inch-touchscreen-panel-regulator + - raspberrypi,touchscreen-panel-regulator-v2 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml index a66007951d58..188a25194000 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml @@ -144,8 +144,8 @@ examples: interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>, <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>, - <&smp2p_adsp_in 2 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>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml index 5dcc2a32c080..a8cddf7e2fe1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml @@ -15,17 +15,26 @@ description: properties: compatible: - enum: - - qcom,sc8180x-adsp-pas - - qcom,sc8180x-cdsp-pas - - qcom,sc8180x-slpi-pas - - qcom,sm8150-adsp-pas - - qcom,sm8150-cdsp-pas - - qcom,sm8150-mpss-pas - - qcom,sm8150-slpi-pas - - qcom,sm8250-adsp-pas - - qcom,sm8250-cdsp-pas - - qcom,sm8250-slpi-pas + oneOf: + - items: + - enum: + - qcom,qcs615-adsp-pas + - const: qcom,sm8150-adsp-pas + - items: + - enum: + - qcom,qcs615-cdsp-pas + - const: qcom,sm8150-cdsp-pas + - enum: + - qcom,sc8180x-adsp-pas + - qcom,sc8180x-cdsp-pas + - qcom,sc8180x-slpi-pas + - qcom,sm8150-adsp-pas + - qcom,sm8150-cdsp-pas + - qcom,sm8150-mpss-pas + - qcom,sm8150-slpi-pas + - qcom,sm8250-adsp-pas + - qcom,sm8250-cdsp-pas + - qcom,sm8250-slpi-pas reg: maxItems: 1 @@ -62,16 +71,17 @@ allOf: - if: properties: compatible: - enum: - - qcom,sc8180x-adsp-pas - - qcom,sc8180x-cdsp-pas - - qcom,sc8180x-slpi-pas - - qcom,sm8150-adsp-pas - - qcom,sm8150-cdsp-pas - - qcom,sm8150-slpi-pas - - qcom,sm8250-adsp-pas - - qcom,sm8250-cdsp-pas - - qcom,sm8250-slpi-pas + contains: + enum: + - qcom,sc8180x-adsp-pas + - qcom,sc8180x-cdsp-pas + - qcom,sc8180x-slpi-pas + - qcom,sm8150-adsp-pas + - qcom,sm8150-cdsp-pas + - qcom,sm8150-slpi-pas + - qcom,sm8250-adsp-pas + - qcom,sm8250-cdsp-pas + - qcom,sm8250-slpi-pas then: properties: interrupts: @@ -88,12 +98,13 @@ allOf: - if: properties: compatible: - enum: - - qcom,sc8180x-adsp-pas - - qcom,sc8180x-cdsp-pas - - qcom,sm8150-adsp-pas - - qcom,sm8150-cdsp-pas - - qcom,sm8250-cdsp-pas + contains: + enum: + - qcom,sc8180x-adsp-pas + - qcom,sc8180x-cdsp-pas + - qcom,sm8150-adsp-pas + - qcom,sm8150-cdsp-pas + - qcom,sm8250-cdsp-pas then: properties: power-domains: diff --git a/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra264-bpmp-shmem.yaml b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra264-bpmp-shmem.yaml index f9b2f0fdc282..4380f622f9a9 100644 --- a/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra264-bpmp-shmem.yaml +++ b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra264-bpmp-shmem.yaml @@ -36,12 +36,13 @@ required: examples: - | reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - dram_cpu_bpmp_mail: shmem@f1be0000 { - compatible = "nvidia,tegra264-bpmp-shmem"; - reg = <0x0 0xf1be0000 0x0 0x2000>; - no-map; - }; + #address-cells = <2>; + #size-cells = <2>; + + shmem@f1be0000 { + compatible = "nvidia,tegra264-bpmp-shmem"; + reg = <0x0 0xf1be0000 0x0 0x2000>; + no-map; + }; }; ... diff --git a/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml b/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml new file mode 100644 index 000000000000..d352d0e12d81 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/canaan,k230-rst.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan Kendryte K230 Reset Controller + +maintainers: + - Junhui Liu + +description: + The Canaan Kendryte K230 reset controller is part of the SoC's system + controller and controls the reset registers for CPUs and various peripherals. + +properties: + compatible: + const: canaan,k230-rst + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + reset-controller@91101000 { + compatible = "canaan,k230-rst"; + reg = <0x91101000 0x1000>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt deleted file mode 100644 index 05d5be48dae4..000000000000 --- a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt +++ /dev/null @@ -1,83 +0,0 @@ -NXP LPC1850 Reset Generation Unit (RGU) -======================================== - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: -- compatible: Should be "nxp,lpc1850-rgu" -- reg: register base and length -- clocks: phandle and clock specifier to RGU clocks -- clock-names: should contain "delay" and "reg" -- #reset-cells: should be 1 - -See table below for valid peripheral reset numbers. Numbers not -in the table below are either reserved or not applicable for -normal operation. - -Reset Peripheral - 9 System control unit (SCU) - 12 ARM Cortex-M0 subsystem core (LPC43xx only) - 13 CPU core - 16 LCD controller - 17 USB0 - 18 USB1 - 19 DMA - 20 SDIO - 21 External memory controller (EMC) - 22 Ethernet - 25 Flash bank A - 27 EEPROM - 28 GPIO - 29 Flash bank B - 32 Timer0 - 33 Timer1 - 34 Timer2 - 35 Timer3 - 36 Repetitive Interrupt timer (RIT) - 37 State Configurable Timer (SCT) - 38 Motor control PWM (MCPWM) - 39 QEI - 40 ADC0 - 41 ADC1 - 42 DAC - 44 USART0 - 45 UART1 - 46 USART2 - 47 USART3 - 48 I2C0 - 49 I2C1 - 50 SSP0 - 51 SSP1 - 52 I2S0 and I2S1 - 53 Serial Flash Interface (SPIFI) - 54 C_CAN1 - 55 C_CAN0 - 56 ARM Cortex-M0 application core (LPC4370 only) - 57 SGPIO (LPC43xx only) - 58 SPI (LPC43xx only) - 60 ADCHS (12-bit ADC) (LPC4370 only) - -Refer to NXP LPC18xx or LPC43xx user manual for more details about -the reset signals and the connected block/peripheral. - -Reset provider example: -rgu: reset-controller@40053000 { - compatible = "nxp,lpc1850-rgu"; - reg = <0x40053000 0x1000>; - clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_BUS>; - clock-names = "delay", "reg"; - #reset-cells = <1>; -}; - -Reset consumer example: -mac: ethernet@40010000 { - compatible = "nxp,lpc1850-dwmac", "snps,dwmac-3.611", "snps,dwmac"; - reg = <0x40010000 0x2000>; - interrupts = <5>; - interrupt-names = "macirq"; - clocks = <&ccu1 CLK_CPU_ETHERNET>; - clock-names = "stmmaceth"; - resets = <&rgu 22>; - reset-names = "stmmaceth"; -}; diff --git a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml new file mode 100644 index 000000000000..9c3c13c543c7 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/nxp,lpc1850-rgu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC1850 Reset Generation Unit (RGU) + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-rgu + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: delay + - const: reg + + '#reset-cells': + const: 1 + description: | + See table below for valid peripheral reset numbers. Numbers not + in the table below are either reserved or not applicable for + normal operation. + + Reset Peripheral + 9 System control unit (SCU) + 12 ARM Cortex-M0 subsystem core (LPC43xx only) + 13 CPU core + 16 LCD controller + 17 USB0 + 18 USB1 + 19 DMA + 20 SDIO + 21 External memory controller (EMC) + 22 Ethernet + 25 Flash bank A + 27 EEPROM + 28 GPIO + 29 Flash bank B + 32 Timer0 + 33 Timer1 + 34 Timer2 + 35 Timer3 + 36 Repetitive Interrupt timer (RIT) + 37 State Configurable Timer (SCT) + 38 Motor control PWM (MCPWM) + 39 QEI + 40 ADC0 + 41 ADC1 + 42 DAC + 44 USART0 + 45 UART1 + 46 USART2 + 47 USART3 + 48 I2C0 + 49 I2C1 + 50 SSP0 + 51 SSP1 + 52 I2S0 and I2S1 + 53 Serial Flash Interface (SPIFI) + 54 C_CAN1 + 55 C_CAN0 + 56 ARM Cortex-M0 application core (LPC4370 only) + 57 SGPIO (LPC43xx only) + 58 SPI (LPC43xx only) + 60 ADCHS (12-bit ADC) (LPC4370 only) + + Refer to NXP LPC18xx or LPC43xx user manual for more details about + the reset signals and the connected block/peripheral. + +required: + - compatible + - reg + - clocks + - clock-names + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + reset-controller@40053000 { + compatible = "nxp,lpc1850-rgu"; + reg = <0x40053000 0x1000>; + clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_BUS>; + clock-names = "delay", "reg"; + #reset-cells = <1>; + }; + diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml index c79f61c2373b..c1b800a10b53 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml +++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml @@ -15,7 +15,12 @@ description: properties: compatible: - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) + oneOf: + - items: + - const: renesas,r9a09g056-usb2phy-reset # RZ/V2N + - const: renesas,r9a09g057-usb2phy-reset + + - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/reset/snps,dw-reset.txt b/Documentation/devicetree/bindings/reset/snps,dw-reset.txt deleted file mode 100644 index 0c241d4aae76..000000000000 --- a/Documentation/devicetree/bindings/reset/snps,dw-reset.txt +++ /dev/null @@ -1,30 +0,0 @@ -Synopsys DesignWare Reset controller -======================================= - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: - -- compatible: should be one of the following. - "snps,dw-high-reset" - for active high configuration - "snps,dw-low-reset" - for active low configuration - -- reg: physical base address of the controller and length of memory mapped - region. - -- #reset-cells: must be 1. - -example: - - dw_rst_1: reset-controller@0000 { - compatible = "snps,dw-high-reset"; - reg = <0x0000 0x4>; - #reset-cells = <1>; - }; - - dw_rst_2: reset-controller@1000 { - compatible = "snps,dw-low-reset"; - reg = <0x1000 0x8>; - #reset-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml b/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml new file mode 100644 index 000000000000..1dde7b6e8623 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/snps,dw-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare Reset controller + +maintainers: + - Philipp Zabel + +properties: + compatible: + enum: + - snps,dw-high-reset + - snps,dw-low-reset + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + + reset-controller: true + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + reset-controller@0 { + compatible = "snps,dw-high-reset"; + reg = <0x0000 0x4>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml index 1d1b84575960..08d28313b870 100644 --- a/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml +++ b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml @@ -16,7 +16,9 @@ properties: - enum: - sophgo,sg2044-reset - const: sophgo,sg2042-reset - - const: sophgo,sg2042-reset + - enum: + - sophgo,cv1800b-reset + - sophgo,sg2042-reset reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/riscv/andes.yaml b/Documentation/devicetree/bindings/riscv/andes.yaml new file mode 100644 index 000000000000..aa1edf1fdec7 --- /dev/null +++ b/Documentation/devicetree/bindings/riscv/andes.yaml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/riscv/andes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes SoC-based boards + +maintainers: + - Ben Zong-You Xie + +description: + Andes SoC-based boards + +properties: + $nodename: + const: '/' + compatible: + oneOf: + - items: + - enum: + - andestech,voyager + - const: andestech,qilai + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index 2c72f148a74b..1a0cf0702a45 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -45,6 +45,7 @@ properties: - items: - enum: - amd,mbv32 + - amd,mbv64 - andestech,ax45mp - canaan,k210 - sifive,bullet0 diff --git a/Documentation/devicetree/bindings/rng/atmel,at91-trng.yaml b/Documentation/devicetree/bindings/rng/atmel,at91-trng.yaml index b38f8252342e..f78614100ea8 100644 --- a/Documentation/devicetree/bindings/rng/atmel,at91-trng.yaml +++ b/Documentation/devicetree/bindings/rng/atmel,at91-trng.yaml @@ -24,6 +24,7 @@ properties: - items: - enum: - microchip,sam9x7-trng + - microchip,sama7d65-trng - const: microchip,sam9x60-trng clocks: diff --git a/Documentation/devicetree/bindings/rng/brcm,iproc-rng200.yaml b/Documentation/devicetree/bindings/rng/brcm,iproc-rng200.yaml index 827983008ecf..817cbdaa2b2d 100644 --- a/Documentation/devicetree/bindings/rng/brcm,iproc-rng200.yaml +++ b/Documentation/devicetree/bindings/rng/brcm,iproc-rng200.yaml @@ -20,11 +20,17 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + additionalProperties: false examples: - | + #include + rng@18032000 { compatible = "brcm,iproc-rng200"; reg = <0x18032000 0x28>; + interrupts = ; }; diff --git a/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml b/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml index 5d3ac737abcb..e61f22eca85b 100644 --- a/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml @@ -16,9 +16,14 @@ allOf: properties: compatible: - enum: - - amlogic,a4-rtc - - amlogic,a5-rtc + oneOf: + - enum: + - amlogic,a4-rtc + - amlogic,a5-rtc + - items: + - enum: + - amlogic,c3-rtc + - const: amlogic,a5-rtc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.yaml b/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.yaml index 17d6280e5515..a86e926ae3d1 100644 --- a/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.yaml @@ -28,6 +28,7 @@ properties: - nvidia,tegra186-rtc - nvidia,tegra194-rtc - nvidia,tegra234-rtc + - nvidia,tegra264-rtc - const: nvidia,tegra20-rtc reg: diff --git a/Documentation/devicetree/bindings/rtc/nxp,lpc1788-rtc.yaml b/Documentation/devicetree/bindings/rtc/nxp,lpc1788-rtc.yaml index e88b847a1cc5..e896ba59302a 100644 --- a/Documentation/devicetree/bindings/rtc/nxp,lpc1788-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/nxp,lpc1788-rtc.yaml @@ -18,7 +18,12 @@ allOf: properties: compatible: - const: nxp,lpc1788-rtc + oneOf: + - items: + - enum: + - nxp,lpc1850-rtc + - const: nxp,lpc1788-rtc + - const: nxp,lpc1788-rtc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/nxp,lpc3220-rtc.yaml b/Documentation/devicetree/bindings/rtc/nxp,lpc3220-rtc.yaml new file mode 100644 index 000000000000..53353de4cb37 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,lpc3220-rtc.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/nxp,lpc3220-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32xx SoC Real-time Clock + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-rtc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + start-year: true + +required: + - compatible + - reg + +allOf: + - $ref: rtc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + rtc@40024000 { + compatible = "nxp,lpc3220-rtc"; + reg = <0x40024000 0x1000>; + interrupt-parent = <&sic1>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_RTC>; + }; + diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml index 2f892f8640d1..1e6277e524c2 100644 --- a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml @@ -12,6 +12,7 @@ maintainers: properties: compatible: enum: + - microcrystal,rv8063 - microcrystal,rv8263 - nxp,pcf85063 - nxp,pcf85063a @@ -44,13 +45,19 @@ properties: wakeup-source: true + spi-cs-high: true + + spi-3wire: true + allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - $ref: rtc.yaml# - if: properties: compatible: contains: enum: + - microcrystal,rv8063 - microcrystal,rv8263 then: properties: @@ -65,12 +72,23 @@ allOf: properties: quartz-load-femtofarads: const: 7000 + - if: + properties: + compatible: + not: + contains: + enum: + - microcrystal,rv8063 + then: + properties: + spi-cs-high: false + spi-3wire: false required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -90,3 +108,16 @@ examples: }; }; }; + + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + rtc@0 { + compatible = "microcrystal,rv8063"; + reg = <0>; + spi-cs-high; + spi-3wire; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml index f6fdcc7090b6..1860f0e4c31a 100644 --- a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml @@ -61,14 +61,14 @@ examples: #include #include rtc@40006000 { - compatible = "renesas,r9a06g032-rtc", "renesas,rzn1-rtc"; - reg = <0x40006000 0x1000>; - interrupts = , - , - ; - interrupt-names = "alarm", "timer", "pps"; - clocks = <&sysctrl R9A06G032_HCLK_RTC>; - clock-names = "hclk"; - power-domains = <&sysctrl>; - start-year = <2000>; - }; + compatible = "renesas,r9a06g032-rtc", "renesas,rzn1-rtc"; + reg = <0x40006000 0x1000>; + interrupts = , + , + ; + interrupt-names = "alarm", "timer", "pps"; + clocks = <&sysctrl R9A06G032_HCLK_RTC>; + clock-names = "hclk"; + power-domains = <&sysctrl>; + start-year = <2000>; + }; diff --git a/Documentation/devicetree/bindings/soc/sophgo/sophgo,cv1800b-rtc.yaml b/Documentation/devicetree/bindings/rtc/sophgo,cv1800b-rtc.yaml similarity index 96% rename from Documentation/devicetree/bindings/soc/sophgo/sophgo,cv1800b-rtc.yaml rename to Documentation/devicetree/bindings/rtc/sophgo,cv1800b-rtc.yaml index 5cf186c396c9..c695d2ff9fcc 100644 --- a/Documentation/devicetree/bindings/soc/sophgo/sophgo,cv1800b-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/sophgo,cv1800b-rtc.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sophgo/sophgo,cv1800b-rtc.yaml# +$id: http://devicetree.org/schemas/rtc/sophgo,cv1800b-rtc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Real Time Clock of the Sophgo CV1800 SoC diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index 7330a7200831..5e0c7cd25cc6 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -63,8 +63,6 @@ properties: - microcrystal,rv3029 # Real Time Clock - microcrystal,rv8523 - # NXP LPC32xx SoC Real-time Clock - - nxp,lpc3220-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 33d2016b6509..b243afa69a1a 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -45,9 +45,53 @@ allOf: - ns16550 - ns16550a then: - anyOf: + oneOf: - required: [ clock-frequency ] - required: [ clocks ] + - if: + properties: + compatible: + contains: + const: nxp,lpc1850-uart + then: + properties: + clock-names: + items: + - const: uartclk + - const: reg + - if: + properties: + compatible: + contains: + const: spacemit,k1-uart + then: + properties: + clock-names: + items: + - const: core + - const: bus + - if: + properties: + compatible: + contains: + enum: + - spacemit,k1-uart + - nxp,lpc1850-uart + then: + required: + - clocks + - clock-names + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 properties: compatible: @@ -142,9 +186,25 @@ properties: clock-names: minItems: 1 - items: - - const: core - - const: bus + maxItems: 2 + oneOf: + - enum: + - main + - uart + - items: + - const: core + - const: bus + - items: + - const: uartclk + - const: reg + + dmas: + minItems: 1 + maxItems: 4 + + dma-names: + minItems: 1 + maxItems: 4 resets: maxItems: 1 @@ -233,25 +293,6 @@ required: - reg - interrupts -if: - properties: - compatible: - contains: - const: spacemit,k1-uart -then: - required: [clock-names] - properties: - clocks: - minItems: 2 - clock-names: - minItems: 2 -else: - properties: - clocks: - maxItems: 1 - clock-names: - maxItems: 1 - unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/serial/altera_jtaguart.txt b/Documentation/devicetree/bindings/serial/altera_jtaguart.txt deleted file mode 100644 index 55a901051e8f..000000000000 --- a/Documentation/devicetree/bindings/serial/altera_jtaguart.txt +++ /dev/null @@ -1,5 +0,0 @@ -Altera JTAG UART - -Required properties: -- compatible : should be "ALTR,juart-1.0" -- compatible : should be "altr,juart-1.0" diff --git a/Documentation/devicetree/bindings/serial/altera_uart.txt b/Documentation/devicetree/bindings/serial/altera_uart.txt deleted file mode 100644 index 81bf7ffb1a81..000000000000 --- a/Documentation/devicetree/bindings/serial/altera_uart.txt +++ /dev/null @@ -1,8 +0,0 @@ -Altera UART - -Required properties: -- compatible : should be "ALTR,uart-1.0" -- compatible : should be "altr,uart-1.0" - -Optional properties: -- clock-frequency : frequency of the clock input to the UART diff --git a/Documentation/devicetree/bindings/serial/altr,juart-1.0.yaml b/Documentation/devicetree/bindings/serial/altr,juart-1.0.yaml new file mode 100644 index 000000000000..02e20fa591da --- /dev/null +++ b/Documentation/devicetree/bindings/serial/altr,juart-1.0.yaml @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/altr,juart-1.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera JTAG UART + +maintainers: + - Dinh Nguyen + +properties: + compatible: + const: altr,juart-1.0 + +required: + - compatible + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/serial/altr,uart-1.0.yaml b/Documentation/devicetree/bindings/serial/altr,uart-1.0.yaml new file mode 100644 index 000000000000..72d4972e1e22 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/altr,uart-1.0.yaml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/altr,uart-1.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera UART + +maintainers: + - Dinh Nguyen + +allOf: + - $ref: /schemas/serial/serial.yaml# + +properties: + compatible: + const: altr,uart-1.0 + + clock-frequency: + description: Frequency of the clock input to the UART. + +required: + - compatible + +unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml b/Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml index 89c462653e2d..8cc848ae11cb 100644 --- a/Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml +++ b/Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml @@ -41,7 +41,7 @@ properties: - const: dma_intr2 clocks: - minItems: 1 + maxItems: 1 clock-names: const: sw_baud diff --git a/Documentation/devicetree/bindings/serial/cdns,uart.yaml b/Documentation/devicetree/bindings/serial/cdns,uart.yaml index d7f047b0bf24..9d3e5c1d8502 100644 --- a/Documentation/devicetree/bindings/serial/cdns,uart.yaml +++ b/Documentation/devicetree/bindings/serial/cdns,uart.yaml @@ -16,9 +16,10 @@ properties: items: - const: xlnx,xuartps - const: cdns,uart-r1p8 - - description: UART controller for Zynq Ultrascale+ MPSoC - items: - - const: xlnx,zynqmp-uart + - items: + - enum: + - axiado,ax3000-uart + - xlnx,zynqmp-uart - const: cdns,uart-r1p12 reg: diff --git a/Documentation/devicetree/bindings/serial/mediatek,uart.yaml b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml index c55d9a0efa19..5bd8a8853ae0 100644 --- a/Documentation/devicetree/bindings/serial/mediatek,uart.yaml +++ b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml @@ -25,6 +25,7 @@ properties: - enum: - mediatek,mt2701-uart - mediatek,mt2712-uart + - mediatek,mt6572-uart - mediatek,mt6580-uart - mediatek,mt6582-uart - mediatek,mt6589-uart diff --git a/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml b/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml new file mode 100644 index 000000000000..c8f01923cb25 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/qcom,sa8255p-geni-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Geni based QUP UART interface + +maintainers: + - Praveen Talari + +allOf: + - $ref: /schemas/serial/serial.yaml# + +properties: + compatible: + enum: + - qcom,sa8255p-geni-uart + - qcom,sa8255p-geni-debug-uart + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + items: + - description: UART core irq + - description: Wakeup irq (RX GPIO) + + interrupt-names: + description: + The UART interrupt and optionally the RX in-band wakeup interrupt + as not all UART instances have a wakeup-capable interrupt routed + via the PDC. + minItems: 1 + items: + - const: uart + - const: wakeup + + power-domains: + minItems: 2 + maxItems: 2 + + power-domain-names: + items: + - const: power + - const: perf + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + +unevaluatedProperties: false + +examples: + - | + #include + + serial@990000 { + compatible = "qcom,sa8255p-geni-uart"; + reg = <0x990000 0x4000>; + interrupts = ; + power-domains = <&scmi0_pd 0>, <&scmi0_dvfs 0>; + power-domain-names = "power", "perf"; + }; +... diff --git a/Documentation/devicetree/bindings/serial/renesas,hscif.yaml b/Documentation/devicetree/bindings/serial/renesas,hscif.yaml index 9480ed30915c..4b3f98a46cd9 100644 --- a/Documentation/devicetree/bindings/serial/renesas,hscif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,hscif.yaml @@ -63,6 +63,12 @@ properties: - const: renesas,rcar-gen4-hscif # R-Car Gen4 - const: renesas,hscif # generic HSCIF compatible UART + - items: + - enum: + - renesas,hscif-r8a78000 # R-Car X5H + - const: renesas,rcar-gen5-hscif # R-Car Gen5 + - const: renesas,hscif # generic HSCIF compatible UART + reg: maxItems: 1 @@ -120,6 +126,7 @@ if: - renesas,rcar-gen2-hscif - renesas,rcar-gen3-hscif - renesas,rcar-gen4-hscif + - renesas,rcar-gen5-hscif then: required: - resets diff --git a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml index ea879db5f485..f50d8e02f476 100644 --- a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml @@ -8,14 +8,20 @@ title: Renesas RSCI Serial Communication Interface maintainers: - Geert Uytterhoeven - - Thierry Bultel + - Lad Prabhakar allOf: - $ref: serial.yaml# properties: compatible: - const: renesas,r9a09g077-rsci # RZ/T2H + oneOf: + - items: + - const: renesas,r9a09g087-rsci # RZ/N2H + - const: renesas,r9a09g077-rsci # RZ/T2H + + - items: + - const: renesas,r9a09g077-rsci # RZ/T2H reg: maxItems: 1 @@ -35,10 +41,15 @@ properties: - const: tei clocks: - maxItems: 1 + minItems: 2 + maxItems: 3 clock-names: - const: fck # UART functional clock + minItems: 2 + items: + - const: operation + - const: bus + - const: sck # optional external clock input power-domains: maxItems: 1 @@ -60,10 +71,6 @@ examples: #include #include - aliases { - serial0 = &sci0; - }; - sci0: serial@80005000 { compatible = "renesas,r9a09g077-rsci"; reg = <0x80005000 0x400>; @@ -72,7 +79,7 @@ examples: , ; interrupt-names = "eri", "rxi", "txi", "tei"; - clocks = <&cpg CPG_MOD 108>; - clock-names = "fck"; + clocks = <&cpg CPG_MOD 8>, <&cpg CPG_CORE 13>; + clock-names = "operation", "bus"; power-domains = <&cpg>; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,scif.yaml b/Documentation/devicetree/bindings/serial/renesas,scif.yaml index 8e82999e6acb..e925cd4c3ac8 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scif.yaml @@ -70,6 +70,12 @@ properties: - const: renesas,rcar-gen4-scif # R-Car Gen4 - const: renesas,scif # generic SCIF compatible UART + - items: + - enum: + - renesas,scif-r8a78000 # R-Car X5H + - const: renesas,rcar-gen5-scif # R-Car Gen5 + - const: renesas,scif # generic SCIF compatible UART + - items: - enum: - renesas,scif-r9a07g044 # RZ/G2{L,LC} @@ -86,6 +92,7 @@ properties: - items: - enum: - renesas,scif-r9a09g047 # RZ/G3E + - renesas,scif-r9a09g056 # RZ/V2N - const: renesas,scif-r9a09g057 # RZ/V2H fallback reg: @@ -174,6 +181,7 @@ allOf: - renesas,rcar-gen2-scif - renesas,rcar-gen3-scif - renesas,rcar-gen4-scif + - renesas,rcar-gen5-scif - renesas,scif-r9a07g044 - renesas,scif-r9a09g057 then: diff --git a/Documentation/devicetree/bindings/serial/samsung_uart.yaml b/Documentation/devicetree/bindings/serial/samsung_uart.yaml index 83d9986d8e98..1a1f991d5364 100644 --- a/Documentation/devicetree/bindings/serial/samsung_uart.yaml +++ b/Documentation/devicetree/bindings/serial/samsung_uart.yaml @@ -28,6 +28,10 @@ properties: - samsung,exynos5433-uart - samsung,exynos850-uart - samsung,exynos8895-uart + - items: + - enum: + - samsung,exynos2200-uart + - const: google,gs101-uart - items: - enum: - samsung,exynos7-uart diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 8f1b7f704c5b..cb9da6c97afc 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -108,6 +108,9 @@ properties: parameter. Define this if your UART does not implement the busy functionality. type: boolean + power-domains: + maxItems: 1 + resets: minItems: 1 maxItems: 2 diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml index 3dc66f1de023..f3a85c67ce8a 100644 --- a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml +++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml @@ -186,22 +186,22 @@ examples: }; power-controller { - compatible = "amlogic,meson-axg-pwrc"; - #power-domain-cells = <1>; - amlogic,ao-sysctrl = <&sysctrl_AO>; + compatible = "amlogic,meson-axg-pwrc"; + #power-domain-cells = <1>; + amlogic,ao-sysctrl = <&sysctrl_AO>; - resets = <&reset_viu>, - <&reset_venc>, - <&reset_vcbus>, - <&reset_vencl>, - <&reset_vid_lock>; - reset-names = "viu", "venc", "vcbus", "vencl", "vid_lock"; - clocks = <&clk_vpu>, <&clk_vapb>; - clock-names = "vpu", "vapb"; + resets = <&reset_viu>, + <&reset_venc>, + <&reset_vcbus>, + <&reset_vencl>, + <&reset_vid_lock>; + reset-names = "viu", "venc", "vcbus", "vencl", "vid_lock"; + clocks = <&clk_vpu>, <&clk_vapb>; + clock-names = "vpu", "vapb"; }; phy { - compatible = "amlogic,axg-mipi-pcie-analog-phy"; - #phy-cells = <0>; + compatible = "amlogic,axg-mipi-pcie-analog-phy"; + #phy-cells = <0>; }; }; diff --git a/Documentation/devicetree/bindings/soc/fsl/fsl,imx23-digctl.yaml b/Documentation/devicetree/bindings/soc/fsl/fsl,imx23-digctl.yaml new file mode 100644 index 000000000000..3de135a70579 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/fsl/fsl,imx23-digctl.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/fsl/fsl,imx23-digctl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale mxs digctrl for i.MX23/i.MX28 + +description: | + The digital control block provides overall control of various items within + the top digital block of the chip, including: + - Default first-level page table (DFLPT) controls + - HCLK performance counter + - Free-running microseconds counter + - Entropy control + - BIST controls for ARM Core and On-Chip RAM + - Chip Revision register + - USB loop back congtrol + - Other miscellaneous controls + +maintainers: + - Frank Li + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,imx28-digctl + - const: fsl,imx23-digctl + - const: fsl,imx23-digctl + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + digctl@8001c000 { + compatible = "fsl,imx28-digctl", "fsl,imx23-digctl"; + reg = <0x8001c000 0x2000>; + interrupts = <89>; + }; + diff --git a/Documentation/devicetree/bindings/soc/fsl/fsl,ls1028a-reset.yaml b/Documentation/devicetree/bindings/soc/fsl/fsl,ls1028a-reset.yaml index 234089b5954d..b43df10c5ef4 100644 --- a/Documentation/devicetree/bindings/soc/fsl/fsl,ls1028a-reset.yaml +++ b/Documentation/devicetree/bindings/soc/fsl/fsl,ls1028a-reset.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas//soc/fsl/fsl,ls1028a-reset.yaml# +$id: http://devicetree.org/schemas/soc/fsl/fsl,ls1028a-reset.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale Layerscape Reset Registers Module diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 41fbbe059d80..851a1260f8dc 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -25,6 +25,7 @@ properties: compatible: items: - enum: + - qcom,milos-aoss-qmp - qcom,qcs615-aoss-qmp - qcom,qcs8300-aoss-qmp - qcom,qdu1000-aoss-qmp @@ -38,6 +39,7 @@ properties: - qcom,sdx75-aoss-qmp - qcom,sdm845-aoss-qmp - qcom,sm6350-aoss-qmp + - qcom,sm7150-aoss-qmp - qcom,sm8150-aoss-qmp - qcom,sm8250-aoss-qmp - qcom,sm8350-aoss-qmp diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml index ce7e20dd22c9..fdc6fc17ed71 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml @@ -18,6 +18,7 @@ properties: compatible: items: - enum: + - qcom,sm7150-dcc - qcom,sm8150-dcc - qcom,sc7280-dcc - qcom,sc7180-dcc diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml index f2c5ec7e6437..84218636c0d8 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml @@ -55,25 +55,25 @@ additionalProperties: false examples: - | eud@88e0000 { - compatible = "qcom,sc7280-eud", "qcom,eud"; - reg = <0x88e0000 0x2000>, - <0x88e2000 0x1000>; + compatible = "qcom,sc7280-eud", "qcom,eud"; + reg = <0x88e0000 0x2000>, + <0x88e2000 0x1000>; - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - eud_ep: endpoint { - remote-endpoint = <&usb2_role_switch>; - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + eud_ep: endpoint { + remote-endpoint = <&usb2_role_switch>; + }; + }; - port@1 { - reg = <1>; - eud_con: endpoint { - remote-endpoint = <&con_eud>; - }; - }; - }; + port@1 { + reg = <1>; + eud_con: endpoint { + remote-endpoint = <&con_eud>; + }; + }; + }; }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml index 4c9e78f29523..48114bb0c927 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml @@ -37,6 +37,7 @@ properties: - const: qcom,pmic-glink - items: - enum: + - qcom,milos-pmic-glink - qcom,sm8650-pmic-glink - qcom,sm8750-pmic-glink - qcom,x1e80100-pmic-glink diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml new file mode 100644 index 000000000000..352af3426d34 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,sa8255p-geni-se-qup.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GENI Serial Engine QUP Wrapper Controller + +maintainers: + - Praveen Talari + +description: + Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper + is a programmable module for supporting a wide range of serial interfaces + like UART, SPI, I2C, I3C, etc. A single QUP module can provide up to 8 Serial + Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP + Wrapper controller is modeled as a node with zero or more child nodes each + representing a serial engine. + +properties: + compatible: + const: qcom,sa8255p-geni-se-qup + + reg: + description: QUP wrapper common register address and length. + maxItems: 1 + + "#address-cells": + const: 2 + + "#size-cells": + const: 2 + + ranges: true + + iommus: + maxItems: 1 + + dma-coherent: true + +patternProperties: + "spi@[0-9a-f]+$": + type: object + description: GENI serial engine based SPI controller. SPI in master mode + supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol + variants. + additionalProperties: true + + properties: + compatible: + const: qcom,sa8255p-geni-spi + + "i2c@[0-9a-f]+$": + type: object + description: GENI serial engine based I2C controller. + additionalProperties: true + + properties: + compatible: + const: qcom,sa8255p-geni-i2c + + "serial@[0-9a-f]+$": + type: object + description: GENI Serial Engine based UART Controller. + additionalProperties: true + + properties: + compatible: + enum: + - qcom,sa8255p-geni-uart + - qcom,sa8255p-geni-debug-uart + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + geniqup@9c0000 { + compatible = "qcom,sa8255p-geni-se-qup"; + reg = <0 0x9c0000 0 0x6000>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + serial@990000 { + compatible = "qcom,sa8255p-geni-uart"; + reg = <0 0x990000 0 0x4000>; + interrupts = ; + power-domains = <&scmi0_pd 0>, <&scmi0_dvfs 0>; + power-domain-names = "power", "perf"; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/soc/renesas/renesas.yaml b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml index 5e6e6e6208dc..5f9d541d177a 100644 --- a/Documentation/devicetree/bindings/soc/renesas/renesas.yaml +++ b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml @@ -388,6 +388,13 @@ properties: - renesas,gray-hawk-single # Gray Hawk Single board (RTP8A779H0ASKB0F10S) - const: renesas,r8a779h0 + - description: R-Car V4M-7 (R8A779H2) + items: + - enum: + - renesas,gray-hawk-single # Gray Hawk Single board (RTP8A779H2ASKB0F10SA001) + - const: renesas,r8a779h2 # ES2.x + - const: renesas,r8a779h0 + - description: R-Car H3e (R8A779M0) items: - enum: @@ -576,7 +583,7 @@ properties: - description: RZ/V2H(P) (R9A09G057) items: - enum: - - renesas,rzv2h-evk # RZ/V2H EVK + - renesas,rzv2h-evk # RZ/V2H EVK (RTK0EF0168C06001BJ) - enum: - renesas,r9a09g057h41 # RZ/V2H - renesas,r9a09g057h42 # RZ/V2H with Mali-G31 support @@ -595,13 +602,23 @@ properties: - description: RZ/T2H (R9A09G077) items: - enum: - - renesas,rzt2h-evk # RZ/T2H Evaluation Board + - renesas,rzt2h-evk # RZ/T2H Evaluation Board (RTK9RZT2H0S00000BJ) - enum: - renesas,r9a09g077m04 # RZ/T2H with Single Cortex-A55 + Dual Cortex-R52 - no security - renesas,r9a09g077m24 # RZ/T2H with Dual Cortex-A55 + Dual Cortex-R52 - no security - renesas,r9a09g077m44 # RZ/T2H with Quad Cortex-A55 + Dual Cortex-R52 - no security - const: renesas,r9a09g077 + - description: RZ/N2H (R9A09G087) + items: + - enum: + - renesas,rzn2h-evk # RZ/N2H Evaluation Board (RTK9RZN2H0S00000BJ) + - enum: + - renesas,r9a09g087m04 # RZ/N2H with Single Cortex-A55 + Dual Cortex-R52 - no security + - renesas,r9a09g087m24 # RZ/N2H with Dual Cortex-A55 + Dual Cortex-R52 - no security + - renesas,r9a09g087m44 # RZ/N2H with Quad Cortex-A55 + Dual Cortex-R52 - no security + - const: renesas,r9a09g087 + additionalProperties: true ... diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index ccdcc889ba8e..1ab0b092e2a5 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -179,10 +179,12 @@ allOf: properties: gpio: type: object + properties: + compatible: + contains: + const: rockchip,rk3328-grf-gpio - $ref: /schemas/gpio/rockchip,rk3328-grf-gpio.yaml# - - unevaluatedProperties: false + additionalProperties: true power-controller: type: object diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index 3109df43d502..f0fb24156da9 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -203,6 +203,9 @@ allOf: then: required: - google,pmu-intr-gen-syscon + else: + properties: + google,pmu-intr-gen-syscon: false examples: - | diff --git a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml index d27ed6c9d61e..d8b302f97547 100644 --- a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml @@ -30,6 +30,7 @@ properties: - samsung,exynos8895-fsys1-sysreg - samsung,exynos8895-peric0-sysreg - samsung,exynos8895-peric1-sysreg + - samsung,exynosautov920-hsi2-sysreg - samsung,exynosautov920-peric0-sysreg - samsung,exynosautov920-peric1-sysreg - tesla,fsd-cam-sysreg diff --git a/Documentation/devicetree/bindings/riscv/sophgo.yaml b/Documentation/devicetree/bindings/soc/sophgo/sophgo.yaml similarity index 76% rename from Documentation/devicetree/bindings/riscv/sophgo.yaml rename to Documentation/devicetree/bindings/soc/sophgo/sophgo.yaml index b4c4d7a7d7ad..1c502618de51 100644 --- a/Documentation/devicetree/bindings/riscv/sophgo.yaml +++ b/Documentation/devicetree/bindings/soc/sophgo/sophgo.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/riscv/sophgo.yaml# +$id: http://devicetree.org/schemas/soc/sophgo/sophgo.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Sophgo SoC-based boards @@ -26,6 +26,11 @@ properties: - enum: - sophgo,huashan-pi - const: sophgo,cv1812h + - items: + - enum: + - milkv,duo-module-01-evb + - const: milkv,duo-module-01 + - const: sophgo,sg2000 - items: - enum: - sipeed,licheerv-nano-b @@ -34,6 +39,8 @@ properties: - items: - enum: - milkv,pioneer + - sophgo,sg2042-evb-v1 + - sophgo,sg2042-evb-v2 - const: sophgo,sg2042 - items: - enum: diff --git a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml index 30aaf49da03d..133a391ee68c 100644 --- a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml +++ b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml @@ -19,6 +19,9 @@ properties: - spacemit,k1-syscon-apbc - spacemit,k1-syscon-apmu - spacemit,k1-syscon-mpmu + - spacemit,k1-syscon-rcpu + - spacemit,k1-syscon-rcpu2 + - spacemit,k1-syscon-apbc2 reg: maxItems: 1 @@ -47,9 +50,6 @@ properties: required: - compatible - reg - - clocks - - clock-names - - "#clock-cells" - "#reset-cells" allOf: @@ -57,13 +57,28 @@ allOf: properties: compatible: contains: - const: spacemit,k1-syscon-apbc + enum: + - spacemit,k1-syscon-apmu + - spacemit,k1-syscon-mpmu then: - properties: - "#power-domain-cells": false - else: required: - "#power-domain-cells" + else: + properties: + "#power-domain-cells": false + - if: + properties: + compatible: + contains: + enum: + - spacemit,k1-syscon-apbc + - spacemit,k1-syscon-apmu + - spacemit,k1-syscon-mpmu + then: + required: + - clocks + - clock-names + - "#clock-cells" additionalProperties: false diff --git a/Documentation/devicetree/bindings/soc/ti/ti,j784s4-bist.yaml b/Documentation/devicetree/bindings/soc/ti/ti,j784s4-bist.yaml new file mode 100644 index 000000000000..a73691cf5624 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/ti/ti,j784s4-bist.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/ti/ti,j784s4-bist.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments K3 BIST + +maintainers: + - Neha Malcom Francis + +allOf: + - $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml# + +description: + The BIST (Built-In Self Test) module is an IP block present in K3 devices + that support triggering of BIST tests, both PBIST (Memory BIST) and LBIST + (Logic BIST) on a core. Both tests are destructive in nature. At boot, BIST + is executed by hardware for the MCU domain automatically as part of HW POST. + +properties: + compatible: + const: ti,j784s4-bist + + reg: + maxItems: 2 + + reg-names: + items: + - const: cfg + - const: ctrl_mmr + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - ti,sci-dev-id + +unevaluatedProperties: false + +examples: + - | + #include + bus { + #address-cells = <2>; + #size-cells = <2>; + safety-selftest@33c0000 { + compatible = "ti,j784s4-bist"; + reg = <0x00 0x033c0000 0x00 0x400>, + <0x00 0x0010c1a0 0x00 0x01c>; + reg-names = "cfg", "ctrl_mmr"; + clocks = <&k3_clks 237 7>; + power-domains = <&k3_pds 237 TI_SCI_PD_EXCLUSIVE>; + ti,sci-dev-id = <234>; + }; + }; diff --git a/Documentation/devicetree/bindings/soc/ti/wkup-m3-ipc.yaml b/Documentation/devicetree/bindings/soc/ti/wkup-m3-ipc.yaml index 0df41c4f60c1..56b16183c885 100644 --- a/Documentation/devicetree/bindings/soc/ti/wkup-m3-ipc.yaml +++ b/Documentation/devicetree/bindings/soc/ti/wkup-m3-ipc.yaml @@ -121,13 +121,13 @@ examples: }; wkup_m3_ipc@1324 { - compatible = "ti,am3352-wkup-m3-ipc"; - reg = <0x1324 0x24>; - interrupts = <78>; - ti,rproc = <&wkup_m3>; - mboxes = <&am335x_mailbox &mbox_wkupm3>; - ti,vtt-gpio-pin = <7>; - firmware-name = "am335x-evm-scale-data.bin"; + compatible = "ti,am3352-wkup-m3-ipc"; + reg = <0x1324 0x24>; + interrupts = <78>; + ti,rproc = <&wkup_m3>; + mboxes = <&am335x_mailbox &mbox_wkupm3>; + ti,vtt-gpio-pin = <7>; + firmware-name = "am335x-evm-scale-data.bin"; }; }; @@ -155,20 +155,20 @@ examples: pinctrl-0 = <&ddr3_vtt_toggle_default>; ddr3_vtt_toggle_default: ddr_vtt_toggle_default { - pinctrl-single,pins = < + pinctrl-single,pins = < 0x25C (DS0_PULL_UP_DOWN_EN | PIN_OUTPUT_PULLUP | DS0_FORCE_OFF_MODE | MUX_MODE7) - >; + >; }; }; wkup_m3_ipc@1324 { - compatible = "ti,am4372-wkup-m3-ipc"; - reg = <0x1324 0x24>; - interrupts = <78>; - ti,rproc = <&wkup_m3>; - mboxes = <&am437x_mailbox &mbox_wkupm3>; - ti,set-io-isolation; - firmware-name = "am43x-evm-scale-data.bin"; + compatible = "ti,am4372-wkup-m3-ipc"; + reg = <0x1324 0x24>; + interrupts = <78>; + ti,rproc = <&wkup_m3>; + mboxes = <&am437x_mailbox &mbox_wkupm3>; + ti,set-io-isolation; + firmware-name = "am43x-evm-scale-data.bin"; }; }; diff --git a/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml b/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml index a05e61431824..ce99c2d8c35d 100644 --- a/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml +++ b/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml @@ -16,9 +16,14 @@ description: properties: compatible: - enum: - - atmel,at91rm9200-ssc - - atmel,at91sam9g45-ssc + oneOf: + - enum: + - atmel,at91rm9200-ssc + - atmel,at91sam9g45-ssc + - items: + - enum: + - microchip,sam9x7-ssc + - const: atmel,at91sam9g45-ssc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml index 725b47e82062..cd47905eb20a 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml @@ -41,6 +41,10 @@ properties: description: This pin is connected to the chip's RESET pin. maxItems: 1 + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml b/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml new file mode 100644 index 000000000000..d12774b42a11 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/fsl,mxs-audio-sgtl5000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MXS audio complex with SGTL5000 codec + +maintainers: + - Frank Li + +properties: + compatible: + items: + - enum: + - bluegiga,apx4devkit-sgtl5000 + - denx,m28evk-sgtl5000 + - fsl,imx28-evk-sgtl5000 + - fsl,imx28-mbmx28lc-sgtl5000 + - fsl,imx28-tx28-sgtl5000 + - const: fsl,mxs-audio-sgtl5000 + + model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex + + saif-controllers: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: The phandle list of the MXS SAIF controller + + audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the SGTL5000 audio codec + + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: | + A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. Valid names could be power supplies, SGTL5000 + pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + SGTL5000 pins: + * MIC_IN + * LINE_IN + * HP_OUT + * LINE_OUT + + Board connectors: + * Mic Jack + * Line In Jack + * Headphone Jack + * Line Out Jack + * Ext Spk + +required: + - compatible + - saif-controllers + - audio-codec + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "fsl,imx28-evk-sgtl5000", "fsl,mxs-audio-sgtl5000"; + model = "imx28-evk-sgtl5000"; + saif-controllers = <&saif0 &saif1>; + audio-codec = <&sgtl5000>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + }; diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml new file mode 100644 index 000000000000..d8993b5d457a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt8173-afe-pcm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek AFE PCM controller for MT8173 + +maintainers: + - Trevor Wu + +properties: + compatible: + const: mediatek,mt8173-afe-pcm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: audio infra sys clock + - description: audio top mux + - description: audio intbus mux + - description: apll1 clock + - description: apll2 clock + - description: i2s0 mclk mux + - description: i2s1 mclk mux + - description: i2s2 mclk mux + - description: i2s3 mclk mux + - description: i2s3 bclk mux + + clock-names: + items: + - const: infra_sys_audio_clk + - const: top_pdn_audio + - const: top_pdn_aud_intbus + - const: bck0 + - const: bck1 + - const: i2s0_m + - const: i2s1_m + - const: i2s2_m + - const: i2s3_m + - const: i2s3_b + + power-domains: + maxItems: 1 + + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + mt8173-afe-pcm@11220000 { + compatible = "mediatek,mt8173-afe-pcm"; + reg = <0x11220000 0x1000>; + interrupts = ; + power-domains = <&spm MT8173_POWER_DOMAIN_AUDIO>; + clocks = <&infracfg CLK_INFRA_AUDIO>, + <&topckgen CLK_TOP_AUDIO_SEL>, + <&topckgen CLK_TOP_AUD_INTBUS_SEL>, + <&topckgen CLK_TOP_APLL1_DIV0>, + <&topckgen CLK_TOP_APLL2_DIV0>, + <&topckgen CLK_TOP_I2S0_M_SEL>, + <&topckgen CLK_TOP_I2S1_M_SEL>, + <&topckgen CLK_TOP_I2S2_M_SEL>, + <&topckgen CLK_TOP_I2S3_M_SEL>, + <&topckgen CLK_TOP_I2S3_B_SEL>; + clock-names = "infra_sys_audio_clk", + "top_pdn_audio", + "top_pdn_aud_intbus", + "bck0", + "bck1", + "i2s0_m", + "i2s1_m", + "i2s2_m", + "i2s3_m", + "i2s3_b"; + memory-region = <&afe_dma_mem>; + }; diff --git a/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml index 7fe85b08f9df..f5af2cf18158 100644 --- a/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml @@ -25,6 +25,10 @@ properties: reset-names: const: audiosys + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + mediatek,apmixedsys: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of the mediatek apmixedsys controller @@ -170,6 +174,7 @@ examples: "top_apll12_div_tdm", "top_mux_audio_h", "top_clk26m_clk"; + memory-region = <&afe_dma_mem>; }; ... diff --git a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml index 064ef172bef4..8ddf49b0040d 100644 --- a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml @@ -23,6 +23,10 @@ properties: reset-names: const: audiosys + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + mediatek,apmixedsys: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of the mediatek apmixedsys controller @@ -95,6 +99,7 @@ examples: "aud_dac_predis_clk", "aud_infra_clk", "aud_infra_26m_clk"; + memory-region = <&afe_dma_mem>; }; ... diff --git a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt deleted file mode 100644 index e302c7f43b95..000000000000 --- a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt +++ /dev/null @@ -1,45 +0,0 @@ -Mediatek AFE PCM controller - -Required properties: -- compatible = "mediatek,mt8173-afe-pcm"; -- reg: register location and size -- interrupts: Should contain AFE interrupt -- clock-names: should have these clock names: - "infra_sys_audio_clk", - "top_pdn_audio", - "top_pdn_aud_intbus", - "bck0", - "bck1", - "i2s0_m", - "i2s1_m", - "i2s2_m", - "i2s3_m", - "i2s3_b"; - -Example: - - afe: mt8173-afe-pcm@11220000 { - compatible = "mediatek,mt8173-afe-pcm"; - reg = <0 0x11220000 0 0x1000>; - interrupts = ; - clocks = <&infracfg INFRA_AUDIO>, - <&topckgen TOP_AUDIO_SEL>, - <&topckgen TOP_AUD_INTBUS_SEL>, - <&topckgen TOP_APLL1_DIV0>, - <&topckgen TOP_APLL2_DIV0>, - <&topckgen TOP_I2S0_M_CK_SEL>, - <&topckgen TOP_I2S1_M_CK_SEL>, - <&topckgen TOP_I2S2_M_CK_SEL>, - <&topckgen TOP_I2S3_M_CK_SEL>, - <&topckgen TOP_I2S3_B_CK_SEL>; - clock-names = "infra_sys_audio_clk", - "top_pdn_audio", - "top_pdn_aud_intbus", - "bck0", - "bck1", - "i2s0_m", - "i2s1_m", - "i2s2_m", - "i2s3_m", - "i2s3_b"; - }; diff --git a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt deleted file mode 100644 index 4eb980bd0287..000000000000 --- a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt +++ /dev/null @@ -1,42 +0,0 @@ -* Freescale MXS audio complex with SGTL5000 codec - -Required properties: -- compatible : "fsl,mxs-audio-sgtl5000" -- model : The user-visible name of this sound complex -- saif-controllers : The phandle list of the MXS SAIF controller -- audio-codec : The phandle of the SGTL5000 audio codec -- audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names could be power supplies, SGTL5000 - pins, and the jacks on the board: - - Power supplies: - * Mic Bias - - SGTL5000 pins: - * MIC_IN - * LINE_IN - * HP_OUT - * LINE_OUT - - Board connectors: - * Mic Jack - * Line In Jack - * Headphone Jack - * Line Out Jack - * Ext Spk - -Example: - -sound { - compatible = "fsl,imx28-evk-sgtl5000", - "fsl,mxs-audio-sgtl5000"; - model = "imx28-evk-sgtl5000"; - saif-controllers = <&saif0 &saif1>; - audio-codec = <&sgtl5000>; - audio-routing = - "MIC_IN", "Mic Jack", - "Mic Jack", "Mic Bias", - "Headphone Jack", "HP_OUT"; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index f41deaa6f4df..dd549db6c841 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -40,7 +40,11 @@ properties: clock-names: minItems: 1 - maxItems: 4 + items: + - const: mclk + - const: macro + - const: dcodec + - const: npl clock-output-names: maxItems: 1 @@ -80,8 +84,7 @@ allOf: clocks: maxItems: 1 clock-names: - items: - - const: mclk + maxItems: 1 - if: properties: @@ -94,10 +97,8 @@ allOf: minItems: 3 maxItems: 3 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec + minItems: 3 + maxItems: 3 - if: properties: @@ -112,11 +113,8 @@ allOf: minItems: 4 maxItems: 4 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec - - const: npl + minItems: 4 + maxItems: 4 - if: properties: @@ -130,10 +128,8 @@ allOf: minItems: 3 maxItems: 3 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec + minItems: 3 + maxItems: 3 unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml index 297aa362aa54..268f7073d797 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml @@ -29,6 +29,12 @@ properties: unevaluatedProperties: false description: Qualcomm DSP audio ports + usbd: + type: object + $ref: /schemas/sound/qcom,q6usb.yaml# + unevaluatedProperties: false + description: Qualcomm DSP USB audio ports + required: - compatible - dais @@ -64,5 +70,12 @@ examples: qcom,sd-lines = <0 1 2 3>; }; }; + + usbd { + compatible = "qcom,q6usb"; + #sound-dai-cells = <1>; + iommus = <&apps_smmu 0x180f 0x0>; + qcom,usb-audio-intr-idx = /bits/ 16 <2>; + }; }; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 590eb177f57a..5d3dbb6cb1ae 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -28,10 +28,12 @@ properties: - qcom,sm8750-sndcard - const: qcom,sm8450-sndcard - enum: + - fairphone,fp4-sndcard - fairphone,fp5-sndcard - qcom,apq8096-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs6490-rb3gen2-sndcard + - qcom,qcs8275-sndcard - qcom,qcs9075-sndcard - qcom,qcs9100-sndcard - qcom,qrb4210-rb2-sndcard diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml index c69291f4d575..85283f94465d 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml @@ -45,6 +45,9 @@ properties: purpose of handling altmode muxing and orientation switching to detect and enable Audio Accessory Mode. + vdd-px-supply: + description: A reference to the 1.2V PX supply + required: - compatible diff --git a/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml b/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml index 5acb05cdfefd..819ca06203b1 100644 --- a/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml +++ b/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/sound/richtek,rt9123.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Richtek RT9123 Audio Amplifier +title: Richtek RT9123/RTQ9124 Audio Amplifier maintainers: - ChiYuan Huang @@ -15,6 +15,12 @@ description: support various formats, including I2S, left-justified, right-justified, and TDM formats. + RTQ9124 is an ultra-low output noise, digital input, mono-channel Class-D + power amplifier that supports a 2.1MHz switching frequency. It integrates + both DC and AC load diagnostics, as well as real-time load monitoring to + assess speaker condition. The device operates from 4.5V to 18V and delivers + up to 30W output power. + allOf: - $ref: dai-common.yaml# @@ -22,6 +28,7 @@ properties: compatible: enum: - richtek,rt9123 + - richtek,rtq9124 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml index 74f7d02b424b..0b013a34e2c1 100644 --- a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml @@ -18,6 +18,7 @@ properties: - ti,tas5719 - ti,tas5721 - ti,tas5733 + - ti,tas5753 reg: maxItems: 1 @@ -98,6 +99,7 @@ allOf: contains: enum: - ti,tas5721 + - ti,tas5753 then: properties: HPVDD-supply: false diff --git a/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml new file mode 100644 index 000000000000..9bfb8089f7ea --- /dev/null +++ b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/amlogic,a4-spisg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic SPI Scatter-Gather Controller + +maintainers: + - Xianwei Zhao + - Sunny Luo + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: amlogic,a4-spisg + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: pclk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + spi@50000 { + compatible = "amlogic,a4-spisg"; + reg = <0x50000 0x38>; + interrupts = ; + clocks = <&clkc 37>, + <&clkc 93>; + clock-names = "core", "pclk"; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml index bf9cce53c48d..8dbda1ffb5eb 100644 --- a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml @@ -23,6 +23,7 @@ properties: - fsl,ls2080a-dspi - fsl,ls2085a-dspi - fsl,lx2160a-dspi + - nxp,s32g2-dspi - items: - enum: - fsl,ls1012a-dspi @@ -37,6 +38,9 @@ properties: - items: - const: fsl,lx2160a-dspi - const: fsl,ls2085a-dspi + - items: + - const: nxp,s32g3-dspi + - const: nxp,s32g2-dspi reg: maxItems: 1 @@ -114,3 +118,17 @@ examples: spi-cs-hold-delay-ns = <50>; }; }; + # S32G3 in target mode + - | + spi@401d4000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401d4000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <8>; + bus-num = <0>; + dmas = <&edma0 0 7>, <&edma0 0 8>; + dma-names = "tx", "rx"; + spi-slave; + }; diff --git a/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml new file mode 100644 index 000000000000..7f5ec1d7f59b --- /dev/null +++ b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/marvell,orion-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Orion SPI controller + +maintainers: + - Andrew Lunn + - Gregory CLEMENT + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + oneOf: + - enum: + - marvell,orion-spi + - marvell,armada-380-spi # For ap80x and cp11x + - items: + - enum: + - marvell,armada-370-spi + - marvell,armada-375-spi + - marvell,armada-380-spi + - marvell,armada-390-spi + - marvell,armada-xp-spi + - const: marvell,orion-spi + + cell-index: + description: Instance id for the SPI controller + deprecated: true + + reg: + minItems: 1 + items: + - description: control registers + - description: CS0 MBUS target/attribute registers for direct mode + - description: CS1 MBUS target/attribute registers for direct mode + - description: CS2 MBUS target/attribute registers for direct mode + - description: CS3 MBUS target/attribute registers for direct mode + - description: CS4 MBUS target/attribute registers for direct mode + - description: CS5 MBUS target/attribute registers for direct mode + - description: CS6 MBUS target/attribute registers for direct mode + - description: CS7 MBUS target/attribute registers for direct mode + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: core + - const: axi + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = <0x10600 0x28>; + clocks = <&coreclk 0>; + interrupts = <23>; + }; + - | + #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + + bus { + #address-cells = <2>; + #size-cells = <1>; + + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = , /* control */ + , /* CS0 */ + , /* CS1 */ + , /* CS2 */ + , /* CS3 */ + , /* CS4 */ + , /* CS5 */ + , /* CS6 */ + ; /* CS7 */ + clocks = <&coreclk 0>; + interrupts = <23>; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml index ed17815263a8..3bf3eb1f8728 100644 --- a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml +++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml @@ -39,6 +39,10 @@ properties: - mediatek,mt7988-spi-single - mediatek,mt8188-spi-ipm - const: mediatek,spi-ipm + - items: + - enum: + - mediatek,mt8196-spi + - const: mediatek,mt6991-spi - items: - enum: - mediatek,mt2701-spi @@ -46,6 +50,7 @@ properties: - mediatek,mt6589-spi - mediatek,mt6765-spi - mediatek,mt6893-spi + - mediatek,mt6991-spi - mediatek,mt7622-spi - mediatek,mt8135-spi - mediatek,mt8173-spi diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.yaml b/Documentation/devicetree/bindings/spi/mxs-spi.yaml index e2512166c1cd..0cf8e7269ba9 100644 --- a/Documentation/devicetree/bindings/spi/mxs-spi.yaml +++ b/Documentation/devicetree/bindings/spi/mxs-spi.yaml @@ -24,6 +24,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + dmas: maxItems: 1 diff --git a/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml new file mode 100644 index 000000000000..d5f780912f21 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/nxp,lpc3220-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC3220 SPI controller + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +allOf: + - $ref: spi-controller.yaml# + +unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + +examples: + - | + #include + + spi@20088000 { + compatible = "nxp,lpc3220-spi"; + reg = <0x20088000 0x1000>; + clocks = <&clk LPC32XX_CLK_SPI1>; + #address-cells = <1>; + #size-cells = <0>; + }; + diff --git a/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml new file mode 100644 index 000000000000..ab27fefc3c3a --- /dev/null +++ b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/renesas,rzv2h-rspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI) + +maintainers: + - Fabrizio Castro + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: renesas,r9a09g057-rspi # RZ/V2H(P) + + reg: + maxItems: 1 + + interrupts: + items: + - description: Idle Interrupt + - description: Error Interrupt + - description: Communication End Interrupt + - description: Receive Buffer Full Interrupt + - description: Transmit Buffer Empty Interrupt + + interrupt-names: + items: + - const: idle + - const: error + - const: end + - const: rx + - const: tx + + clocks: + maxItems: 3 + + clock-names: + items: + - const: pclk + - const: pclk_sfr + - const: tclk + + resets: + maxItems: 2 + + reset-names: + items: + - const: presetn + - const: tresetn + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi@12800800 { + compatible = "renesas,r9a09g057-rspi"; + + reg = <0x12800800 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"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml index a65a42ccaafe..a82360bed188 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml @@ -20,6 +20,7 @@ properties: - enum: - fsl,imx7ulp-spi - fsl,imx8qxp-spi + - nxp,s32g2-lpspi - items: - enum: - fsl,imx8ulp-spi @@ -27,6 +28,10 @@ properties: - fsl,imx94-spi - fsl,imx95-spi - const: fsl,imx7ulp-spi + - items: + - const: nxp,s32g3-lpspi + - const: nxp,s32g2-lpspi + reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/spi/spi-mux.yaml b/Documentation/devicetree/bindings/spi/spi-mux.yaml index fb2a6039928c..b1e2a97be699 100644 --- a/Documentation/devicetree/bindings/spi/spi-mux.yaml +++ b/Documentation/devicetree/bindings/spi/spi-mux.yaml @@ -46,7 +46,6 @@ properties: required: - compatible - reg - - spi-max-frequency - mux-controls unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt deleted file mode 100644 index 8434a65fc12a..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ /dev/null @@ -1,79 +0,0 @@ -Marvell Orion SPI device - -Required properties: -- compatible : should be on of the following: - - "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs - - "marvell,armada-370-spi", for the Armada 370 SoCs - - "marvell,armada-375-spi", for the Armada 375 SoCs - - "marvell,armada-380-spi", for the Armada 38x SoCs - - "marvell,armada-390-spi", for the Armada 39x SoCs - - "marvell,armada-xp-spi", for the Armada XP SoCs -- reg : offset and length of the register set for the device. - This property can optionally have additional entries to configure - the SPI direct access mode that some of the Marvell SoCs support - additionally to the normal indirect access (PIO) mode. The values - for the MBus "target" and "attribute" are defined in the Marvell - SoC "Functional Specifications" Manual in the chapter "Marvell - Core Processor Address Decoding". - The eight register sets following the control registers refer to - chip-select lines 0 through 7 respectively. -- cell-index : Which of multiple SPI controllers is this. -- clocks : pointers to the reference clocks for this device, the first - one is the one used for the clock on the spi bus, the - second one is optional and is the clock used for the - functional part of the controller - -Optional properties: -- interrupts : Is currently not used. -- clock-names : names of used clocks, mandatory if the second clock is - used, the name must be "core", and "axi" (the latter - is only for Armada 7K/8K). - - -Example: - spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = <0x10600 0x28>; - interrupts = <23>; - }; - -Example with SPI direct mode support (optionally): - spi0: spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = , /* control */ - , /* CS0 */ - , /* CS1 */ - , /* CS2 */ - , /* CS3 */ - , /* CS4 */ - , /* CS5 */ - , /* CS6 */ - ; /* CS7 */ - interrupts = <23>; - }; - -To enable the direct mode, the board specific 'ranges' property in the -'soc' node needs to add the entries for the desired SPI controllers -and its chip-selects that are used in the direct mode instead of PIO -mode. Here an example for this (SPI controller 0, device 1 and SPI -controller 1, device 2 are used in direct mode. All other SPI device -are used in the default indirect (PIO) mode): - soc { - /* - * Enable the SPI direct access by configuring an entry - * here in the board-specific ranges property - */ - ranges = , /* internal regs */ - , /* BootROM */ - , /* SPI0-DEV1 */ - ; /* SPI1-DEV2 */ - -For further information on the MBus bindings, please see the MBus -DT documentation: -Documentation/devicetree/bindings/bus/mvebu-mbus.txt diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 8fc17e16efb2..8b6e8fc009db 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -115,6 +115,7 @@ properties: maxItems: 4 st,spi-midi-ns: + deprecated: true description: | Only for STM32H7, (Master Inter-Data Idleness) minimum time delay in nanoseconds inserted between two consecutive data frames. diff --git a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml index 66e54dedab14..0e7ead763705 100644 --- a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml +++ b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml @@ -14,12 +14,9 @@ allOf: properties: compatible: - oneOf: - - const: sophgo,sg2044-spifmc-nor - - items: - - enum: - - sophgo,sg2042-spifmc-nor - - const: sophgo,sg2044-spifmc-nor + enum: + - sophgo,sg2042-spifmc-nor + - sophgo,sg2044-spifmc-nor reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml index 76e43c0ce36c..ca880a226afa 100644 --- a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml +++ b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml @@ -18,6 +18,38 @@ maintainers: allOf: - $ref: spi-controller.yaml# + - if: + properties: + compatible: + contains: + const: st,stm32f4-spi + + then: + properties: + st,spi-midi-ns: false + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx + + - if: + properties: + compatible: + contains: + const: st,stm32mp25-spi + + then: + properties: + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx properties: compatible: @@ -41,16 +73,28 @@ properties: dmas: description: | - DMA specifiers for tx and rx dma. DMA fifo mode must be used. See - the STM32 DMA controllers bindings Documentation/devicetree/bindings/dma/stm32/*.yaml. + DMA specifiers for tx and rx channels. DMA fifo mode must be used. See + the STM32 DMA bindings Documentation/devicetree/bindings/dma/stm32/st,*dma.yaml + minItems: 2 items: - description: rx DMA channel - description: tx DMA channel + - description: rxm2m MDMA channel dma-names: + minItems: 2 items: - const: rx - const: tx + - const: rxm2m + + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandles to a reserved SRAM region which is used as temporary + storage memory between DMA and MDMA engines. + The region should be defined as child node of the AHB SRAM node + as per the generic bindings in Documentation/devicetree/bindings/sram/sram.yaml access-controllers: minItems: 1 diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml index 2711f90d9664..72d35e30c439 100644 --- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -22,17 +22,32 @@ properties: - qcom,msm8974-imem - qcom,msm8976-imem - qcom,qcs404-imem + - qcom,qcs615-imem - qcom,qcs8300-imem - qcom,qdu1000-imem - qcom,sa8775p-imem + - qcom,sar2130p-imem - qcom,sc7180-imem - qcom,sc7280-imem + - qcom,sc8280xp-imem - qcom,sdm630-imem - qcom,sdm845-imem - qcom,sdx55-imem - qcom,sdx65-imem + - qcom,sdx75-imem + - qcom,sm6115-imem + - qcom,sm6125-imem + - qcom,sm6350-imem - qcom,sm6375-imem + - qcom,sm7150-imem + - qcom,sm8150-imem + - qcom,sm8250-imem + - qcom,sm8350-imem - qcom,sm8450-imem + - qcom,sm8550-imem + - qcom,sm8650-imem + - qcom,sm8750-imem + - qcom,x1e80100-imem - const: syscon - const: simple-mfd diff --git a/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt b/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt deleted file mode 100644 index 88bc94fe1f6d..000000000000 --- a/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt +++ /dev/null @@ -1,24 +0,0 @@ -* ST SPEAr ADC device driver - -Required properties: -- compatible: Should be "st,spear600-adc" -- reg: Address and length of the register set for the device -- interrupts: Should contain the ADC interrupt -- sampling-frequency: Default sampling frequency - -Optional properties: -- vref-external: External voltage reference in milli-volts. If omitted - the internal voltage reference will be used. -- average-samples: Number of samples to generate an average value. If - omitted, single data conversion will be used. - -Examples: - - adc: adc@d8200000 { - compatible = "st,spear600-adc"; - reg = <0xd8200000 0x1000>; - interrupt-parent = <&vic1>; - interrupts = <6>; - sampling-frequency = <5000000>; - vref-external = <2500>; /* 2.5V VRef */ - }; diff --git a/Documentation/devicetree/bindings/submitting-patches.rst b/Documentation/devicetree/bindings/submitting-patches.rst index f3e23e69a638..46d0b036c97e 100644 --- a/Documentation/devicetree/bindings/submitting-patches.rst +++ b/Documentation/devicetree/bindings/submitting-patches.rst @@ -21,8 +21,16 @@ I. For patch submitters ": dt-bindings: ..." The 80 characters of the subject are precious. It is recommended to not - use "Documentation" or "doc" because that is implied. All bindings are - docs. Repeating "binding" again should also be avoided. + use "Documentation", "doc" or "YAML" because that is implied. All + bindings are docs and all new bindings are supposed to be in Devicetree + schema format. Repeating "binding" again should also be avoided, so for + a new device it is often enough for example:: + + "dt-bindings: iio: adc: Add ROHM BD79100G" + + Conversion of other formats to DT schema:: + + "dt-bindings: iio: adc: adi,ad7476: Convert to DT schema" 2) DT binding files are written in DT schema format using json-schema vocabulary and YAML file format. The DT binding files must pass validation diff --git a/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml index d96a2e32bd8f..7bd0955e6d04 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml @@ -20,16 +20,23 @@ allOf: properties: compatible: - enum: - - mediatek,mt2701-thermal - - mediatek,mt2712-thermal - - mediatek,mt7622-thermal - - mediatek,mt7981-thermal - - mediatek,mt7986-thermal - - mediatek,mt8173-thermal - - mediatek,mt8183-thermal - - mediatek,mt8365-thermal - - mediatek,mt8516-thermal + oneOf: + - enum: + - mediatek,mt2701-thermal + - mediatek,mt2712-thermal + - mediatek,mt7622-thermal + - mediatek,mt7986-thermal + - mediatek,mt8173-thermal + - mediatek,mt8183-thermal + - mediatek,mt8365-thermal + - items: + - enum: + - mediatek,mt8516-thermal + - const: mediatek,mt2701-thermal + - items: + - enum: + - mediatek,mt7981-thermal + - const: mediatek,mt7986-thermal reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml index 19bb1f324183..cf47a1f3b384 100644 --- a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml +++ b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml @@ -21,6 +21,7 @@ properties: - nvidia,tegra124-soctherm - nvidia,tegra132-soctherm - nvidia,tegra210-soctherm + - nvidia,tegra210b01-soctherm reg: maxItems: 2 @@ -207,6 +208,7 @@ allOf: enum: - nvidia,tegra124-soctherm - nvidia,tegra210-soctherm + - nvidia,tegra210b01-soctherm then: properties: reg: diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index 0e653bbe9884..94311ebd7652 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -49,6 +49,7 @@ properties: - description: v2 of TSENS items: - enum: + - qcom,milos-tsens - qcom,msm8953-tsens - qcom,msm8996-tsens - qcom,msm8998-tsens diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml index b717ea8261ca..573f447cc26e 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml @@ -21,6 +21,7 @@ properties: - rockchip,rk3368-tsadc - rockchip,rk3399-tsadc - rockchip,rk3568-tsadc + - rockchip,rk3576-tsadc - rockchip,rk3588-tsadc - rockchip,rv1108-tsadc @@ -39,6 +40,17 @@ properties: - const: tsadc - const: apb_pclk + nvmem-cells: + items: + - description: cell handle to where the trim's base temperature is stored + - description: + cell handle to where the trim's tenths of Celsius base value is stored + + nvmem-cell-names: + items: + - const: trim_base + - const: trim_base_frac + resets: minItems: 1 maxItems: 3 @@ -50,6 +62,12 @@ properties: - const: tsadc - const: tsadc-phy + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + "#thermal-sensor-cells": const: 1 @@ -71,6 +89,27 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] +patternProperties: + "@[0-9a-f]+$": + type: object + properties: + reg: + maxItems: 1 + description: sensor ID, a.k.a. channel number + + nvmem-cells: + items: + - description: handle of cell containing calibration data + + nvmem-cell-names: + items: + - const: trim + + required: + - reg + + unevaluatedProperties: false + required: - compatible - reg @@ -79,6 +118,29 @@ required: - clock-names - resets +allOf: + - if: + not: + properties: + compatible: + contains: + const: rockchip,rk3568-tsadc + then: + properties: + nvmem-cells: false + nvmem-cell-names: false + - if: + not: + properties: + compatible: + contains: + enum: + - rockchip,rk3568-tsadc + - rockchip,rk3576-tsadc + then: + patternProperties: + "@[0-9a-f]+$": false + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/timer/andestech,plmt0.yaml b/Documentation/devicetree/bindings/timer/andestech,plmt0.yaml new file mode 100644 index 000000000000..90b612096004 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/andestech,plmt0.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/andestech,plmt0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes machine-level timer + +description: + The Andes machine-level timer device (PLMT0) provides machine-level timer + functionality for a set of HARTs on a RISC-V platform. It has a single + fixed-frequency monotonic time counter (MTIME) register and a time compare + register (MTIMECMP) for each HART connected to the PLMT0. A timer interrupt is + generated if MTIME >= MTIMECMP. + +maintainers: + - Ben Zong-You Xie + +properties: + compatible: + items: + - enum: + - andestech,qilai-plmt + - const: andestech,plmt0 + + reg: + maxItems: 1 + + interrupts-extended: + minItems: 1 + maxItems: 32 + description: + Specifies which harts are connected to the PLMT0. Each item must points + to a riscv,cpu-intc node, which has a riscv cpu node as parent. The + PLMT0 supports 1 hart up to 32 harts. + +additionalProperties: false + +required: + - compatible + - reg + - interrupts-extended + +examples: + - | + interrupt-controller@100000 { + compatible = "andestech,qilai-plmt", "andestech,plmt0"; + reg = <0x100000 0x100000>; + interrupts-extended = <&cpu0intc 7>, + <&cpu1intc 7>, + <&cpu2intc 7>, + <&cpu3intc 7>; + }; diff --git a/Documentation/devicetree/bindings/timer/via,vt8500-timer.txt b/Documentation/devicetree/bindings/timer/via,vt8500-timer.txt deleted file mode 100644 index 901c73f0d8ef..000000000000 --- a/Documentation/devicetree/bindings/timer/via,vt8500-timer.txt +++ /dev/null @@ -1,15 +0,0 @@ -VIA/Wondermedia VT8500 Timer ------------------------------------------------------ - -Required properties: -- compatible : "via,vt8500-timer" -- reg : Should contain 1 register ranges(address and length) -- interrupts : interrupt for the timer - -Example: - - timer@d8130100 { - compatible = "via,vt8500-timer"; - reg = <0xd8130100 0x28>; - interrupts = <36>; - }; diff --git a/Documentation/devicetree/bindings/timer/via,vt8500-timer.yaml b/Documentation/devicetree/bindings/timer/via,vt8500-timer.yaml new file mode 100644 index 000000000000..e748149948f3 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/via,vt8500-timer.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/via,vt8500-timer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: VIA/Wondermedia VT8500 Timer + +description: + This is the timer block that is a standalone part of the system power + management controller on VIA/WonderMedia SoCs (VIA VT8500 and alike). + The hardware has a single 32-bit counter running at 3 MHz and four match + registers, each of which is associated with a dedicated match interrupt, + and the first of which can also serve as the system watchdog (if the + watchdog function is enabled, it will reset the system upon match instead + of triggering its respective interrupt) + +maintainers: + - Alexey Charkov + +properties: + compatible: + const: via,vt8500-timer + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + items: + - description: Channel 0 match. Note that if the watchdog function + is enabled, this interrupt will not fire and the system will + reboot instead once the counter reaches match register 0 value + - description: Channel 1 match + - description: Channel 2 match + - description: Channel 3 match + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + timer@d8130100 { + compatible = "via,vt8500-timer"; + reg = <0xd8130100 0x28>; + interrupts = <36>; + }; diff --git a/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml new file mode 100644 index 000000000000..ea466179551c --- /dev/null +++ b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2025 Analog Devices, Inc. +# Copyright (c) 2025 BayLibre, SAS + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trigger-source/adi,util-sigma-delta-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Util Sigma-Delta SPI IP Core + +maintainers: + - David Lechner + +description: + The Util Sigma-Delta SPI is an FPGA IP core from Analog Devices that provides + a SPI offload trigger from the RDY signal of the combined DOUT/RDY pin of + the sigma-delta family of ADCs. + https://analogdevicesinc.github.io/hdl/library/util_sigma_delta_spi/index.html + +properties: + compatible: + const: adi,util-sigma-delta-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#trigger-source-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - '#trigger-source-cells' + +additionalProperties: false + +examples: + - | + trigger@40000 { + reg = <0x40000 0x1000>; + compatible = "adi,util-sigma-delta-spi"; + clocks = <&clk 0>; + #trigger-source-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml b/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml new file mode 100644 index 000000000000..1331d153ee82 --- /dev/null +++ b/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trigger-source/gpio-trigger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic trigger source using GPIO + +description: A GPIO used as a trigger source. + +maintainers: + - Jonathan Santos + +properties: + compatible: + const: gpio-trigger + + '#trigger-source-cells': + const: 0 + + gpios: + maxItems: 1 + description: GPIO to be used as a trigger source. + +required: + - compatible + - '#trigger-source-cells' + - gpios + +additionalProperties: false + +examples: + - | + #include + + trigger { + compatible = "gpio-trigger"; + #trigger-source-cells = <0>; + gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 27930708ccd5..f3dd18681aa6 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -30,6 +30,8 @@ properties: items: # Entries are sorted alphanumerically by the compatible - enum: + # ABB register based spi sensors + - abb,spi-sensor # Acbel fsg032 power supply - acbel,fsg032 # SMBus/I2C Digital Temperature Sensor in 6-Pin SOT with SMBus Alert and Over Temperature Pin @@ -39,8 +41,14 @@ properties: - ad,adm9240 # AD5110 - Nonvolatile Digital Potentiometer - adi,ad5110 - # Analog Devices ADP5589 Keypad Decoder and I/O Expansion - - adi,adp5589 + # Temperature sensor with integrated fan control + - adi,adm1027 + # Analog Devices ADT7411 Temperature Sensor and 8-channel ADC + - adi,adt7411 + # Temperature sensor with integrated fan control + - adi,adt7463 + # Temperature sensor with integrated fan control + - adi,adt7468 # Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher - adi,lt7182s # AMS iAQ-Core VOC Sensor @@ -291,6 +299,8 @@ properties: - mps,mp2891 # Monolithic Power Systems Inc. multi-phase controller mp2993 - mps,mp2993 + # Monolithic Power Systems Inc. hot-swap protection device + - mps,mp5023 # Monolithic Power Systems Inc. multi-phase hot-swap controller mp5920 - mps,mp5920 # Monolithic Power Systems Inc. multi-phase hot-swap controller mp5990 @@ -299,16 +309,30 @@ properties: - mps,mp9941 # Temperature sensor with integrated fan control - national,lm63 + # Temperature sensor with integrated fan control + - national,lm64 + # Temperature sensor + - national,lm95235 + # Temperature sensor + - national,lm95245 + # Temperature sensor with integrated fan control + - national,lm96163 # Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor - national,lm80 # Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor - national,lm81 # Temperature sensor with integrated fan control - national,lm85 + # Temperature sensor with integrated fan control + - national,lm85b + # Temperature sensor with integrated fan control + - national,lm85c # I2C ±0.33°C Accurate, 12-Bit + Sign Temperature Sensor and Thermal Window Comparator - national,lm92 # Nuvoton Temperature Sensor - nuvoton,w83773g + # NXP ISP1301 USB transceiver + - nxp,isp1301 # OKI ML86V7667 video decoder - oki,ml86v7667 # ON Semiconductor ADT7462 Temperature, Voltage Monitor and Fan Controller @@ -357,12 +381,38 @@ properties: - silabs,si7020 # Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply - skyworks,sky81452 + # Temperature sensor with integrated fan control + - smsc,emc6d100 + # Temperature sensor with integrated fan control + - smsc,emc6d101 + # Temperature sensor with integrated fan control + - smsc,emc6d102 + # Temperature sensor with integrated fan control + - smsc,emc6d103 + # Temperature sensor with integrated fan control + - smsc,emc6d103s # SparkFun Qwiic Joystick (COM-15168) with i2c interface - sparkfun,qwiic-joystick # Sierra Wireless mangOH Green SPI IoT interface - swir,mangoh-iotport-spi # Ambient Light Sensor with SMBUS/Two Wire Serial Interface - taos,tsl2550 + # Digital PWM System Controller PMBus + - ti,cd9200 + # Digital PWM System Controller PMBus + - ti,cd9220 + # Digital PWM System Controller PMBus + - ti,cd9222 + # Digital PWM System Controller PMBus + - ti,cd9224 + # Digital PWM System Controller PMBus + - ti,cd9240 + # Digital PWM System Controller PMBus + - ti,cd9244 + # Digital PWM System Controller PMBus + - ti,cd9246 + # Digital PWM System Controller PMBus + - ti,cd9248 # Temperature and humidity sensor with i2c interface - ti,hdc1000 # Temperature and humidity sensor with i2c interface @@ -390,12 +440,18 @@ properties: - ti,tmp125 # TI DC-DC converter on PMBus - ti,tps40400 + # TI DCAP+ multiphase controller + - ti,tps53647 + # TI DCAP+ multiphase controller + - ti,tps53667 # TI Dual channel DCAP+ multiphase controller TPS53676 with AVSBus - ti,tps53676 # TI Dual channel DCAP+ multiphase controller TPS53679 - ti,tps53679 # TI Dual channel DCAP+ multiphase controller TPS53681 - ti,tps53681 + # TI Dual channel DCAP+ multiphase controller TPS53685 with AMD-SVI3 + - ti,tps53685 # TI Dual channel DCAP+ multiphase controller TPS53688 - ti,tps53688 # TI DC-DC converters on PMBus diff --git a/Documentation/devicetree/bindings/ufs/mediatek,ufs.yaml b/Documentation/devicetree/bindings/ufs/mediatek,ufs.yaml index 32fd535a514a..1dec54fb00f3 100644 --- a/Documentation/devicetree/bindings/ufs/mediatek,ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/mediatek,ufs.yaml @@ -9,21 +9,20 @@ title: Mediatek Universal Flash Storage (UFS) Controller maintainers: - Stanley Chu -allOf: - - $ref: ufs-common.yaml - properties: compatible: enum: - mediatek,mt8183-ufshci - mediatek,mt8192-ufshci + - mediatek,mt8195-ufshci clocks: - maxItems: 1 + minItems: 1 + maxItems: 8 clock-names: - items: - - const: ufs + minItems: 1 + maxItems: 8 phys: maxItems: 1 @@ -33,6 +32,10 @@ properties: vcc-supply: true + mediatek,ufs-disable-mcq: + $ref: /schemas/types.yaml#/definitions/flag + description: The mask to disable MCQ (Multi-Circular Queue) for UFS host. + required: - compatible - clocks @@ -43,6 +46,37 @@ required: unevaluatedProperties: false +allOf: + - $ref: ufs-common.yaml + + - if: + properties: + compatible: + contains: + enum: + - mediatek,mt8195-ufshci + then: + properties: + clocks: + minItems: 8 + clock-names: + items: + - const: ufs + - const: ufs_aes + - const: ufs_tick + - const: unipro_sysclk + - const: unipro_tick + - const: unipro_mp_bclk + - const: ufs_tx_symbol + - const: ufs_mem_sub + else: + properties: + clocks: + maxItems: 1 + clock-names: + items: + - const: ufs + examples: - | #include diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index cc5787a8cfa3..691d6cf02c27 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -23,6 +23,7 @@ properties: - nvidia,tegra30-udc - nvidia,tegra114-udc - nvidia,tegra124-udc + - nxp,s32g2-usb - qcom,ci-hdrc - items: - enum: @@ -37,6 +38,10 @@ properties: - enum: - nuvoton,npcm845-udc - const: nuvoton,npcm750-udc + - items: + - enum: + - nxp,s32g3-usb + - const: nxp,s32g2-usb clocks: minItems: 1 diff --git a/Documentation/devicetree/bindings/usb/dwc2.yaml b/Documentation/devicetree/bindings/usb/dwc2.yaml index e83d30a91b88..6c3a10991b8b 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.yaml +++ b/Documentation/devicetree/bindings/usb/dwc2.yaml @@ -59,7 +59,7 @@ properties: - const: amcc,dwc-otg - const: apm,apm82181-dwc-otg - const: snps,dwc2 - - const: sophgo,cv1800-usb + - const: sophgo,cv1800b-usb - const: st,stm32f4x9-fsotg - const: st,stm32f4x9-hsotg - const: st,stm32f7-hsotg diff --git a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml index 019435540df0..ca677d1a8274 100644 --- a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml @@ -21,6 +21,8 @@ properties: - fsl,imx53-usbmisc - fsl,imx6q-usbmisc - fsl,vf610-usbmisc + - nxp,s32g2-usbmisc + - nxp,s32g3-usbmisc - items: - enum: - fsl,imx6ul-usbmisc diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index 6fe2d356dcbd..9a94b2a74a1e 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -26,19 +26,26 @@ properties: description: The regulator that provides 3.3V or 5.0V core power to the hub. - peer-hub: - $ref: /schemas/types.yaml#/definitions/phandle - description: - For onboard hub controllers that support USB 3.x and USB 2.0 hubs - with shared resets and power supplies, this property is used to identify - the hubs with which these are shared. + peer-hub: true + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@': + $ref: /schemas/graph.yaml#/properties/port + + properties: + reg: + minimum: 1 + maximum: 4 required: - compatible - reg allOf: - - $ref: usb-device.yaml# + - $ref: usb-hub.yaml# - if: properties: compatible: @@ -62,13 +69,6 @@ allOf: peer-hub: true vdd-supply: true -patternProperties: - "^.*@[0-9a-f]{1,2}$": - description: The hard wired USB devices - type: object - $ref: /schemas/usb/usb-device.yaml - additionalProperties: true - unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/usb/isp1301.txt b/Documentation/devicetree/bindings/usb/isp1301.txt deleted file mode 100644 index ecd607dacba5..000000000000 --- a/Documentation/devicetree/bindings/usb/isp1301.txt +++ /dev/null @@ -1,24 +0,0 @@ -* NXP ISP1301 USB transceiver - -Required properties: -- compatible: must be "nxp,isp1301" -- reg: I2C address of the ISP1301 device - -Optional properties of devices using ISP1301: -- transceiver: phandle of isp1301 - this helps the ISP1301 driver to find the - ISP1301 instance associated with the respective USB driver - -Example: - - isp1301: usb-transceiver@2c { - compatible = "nxp,isp1301"; - reg = <0x2c>; - }; - - usbd@31020000 { - compatible = "nxp,lpc3220-udc"; - reg = <0x31020000 0x300>; - interrupt-parent = <&mic>; - interrupts = <0x3d 0>, <0x3e 0>, <0x3c 0>, <0x3a 0>; - transceiver = <&isp1301>; - }; diff --git a/Documentation/devicetree/bindings/usb/lpc32xx-udc.txt b/Documentation/devicetree/bindings/usb/lpc32xx-udc.txt deleted file mode 100644 index 29f12a533f66..000000000000 --- a/Documentation/devicetree/bindings/usb/lpc32xx-udc.txt +++ /dev/null @@ -1,28 +0,0 @@ -* NXP LPC32xx SoC USB Device Controller (UDC) - -Required properties: -- compatible: Must be "nxp,lpc3220-udc" -- reg: Physical base address of the controller and length of memory mapped - region. -- interrupts: The USB interrupts: - * USB Device Low Priority Interrupt - * USB Device High Priority Interrupt - * USB Device DMA Interrupt - * External USB Transceiver Interrupt (OTG ATX) -- transceiver: phandle of the associated ISP1301 device - this is necessary for - the UDC controller for connecting to the USB physical layer - -Example: - - isp1301: usb-transceiver@2c { - compatible = "nxp,isp1301"; - reg = <0x2c>; - }; - - usbd@31020000 { - compatible = "nxp,lpc3220-udc"; - reg = <0x31020000 0x300>; - interrupt-parent = <&mic>; - interrupts = <0x3d 0>, <0x3e 0>, <0x3c 0>, <0x3a 0>; - transceiver = <&isp1301>; - }; diff --git a/Documentation/devicetree/bindings/usb/nxp,lpc3220-udc.yaml b/Documentation/devicetree/bindings/usb/nxp,lpc3220-udc.yaml new file mode 100644 index 000000000000..be0457a06861 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/nxp,lpc3220-udc.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/nxp,lpc3220-udc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32xx SoC USB Device Controller (UDC) + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc3220-udc + + reg: + maxItems: 1 + + interrupts: + items: + - description: USB Device Low Priority Interrupt + - description: USB Device High Priority Interrupt + - description: USB Device DMA Interrupt + - description: External USB Transceiver Interrupt (OTG ATX) + + clocks: + maxItems: 1 + + transceiver: + description: + phandle of the associated ISP1301 device - this is necessary for + the UDC controller for connecting to the USB physical layer + +required: + - compatible + - reg + - interrupts + - transceiver + +additionalProperties: false + +examples: + - | + usbd@31020000 { + compatible = "nxp,lpc3220-udc"; + reg = <0x31020000 0x300>; + interrupt-parent = <&mic>; + interrupts = <0x3d 0>, <0x3e 0>, <0x3c 0>, <0x3a 0>; + transceiver = <&isp1301>; + }; diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index 8dac5eba61b4..dfd084ed9024 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -32,6 +32,7 @@ properties: - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 - qcom,ipq9574-dwc3 + - qcom,milos-dwc3 - qcom,msm8953-dwc3 - qcom,msm8994-dwc3 - qcom,msm8996-dwc3 @@ -338,6 +339,7 @@ allOf: compatible: contains: enum: + - qcom,milos-dwc3 - qcom,qcm2290-dwc3 - qcom,qcs615-dwc3 - qcom,sar2130p-dwc3 @@ -453,6 +455,7 @@ allOf: compatible: contains: enum: + - qcom,milos-dwc3 - qcom,x1e80100-dwc3 then: properties: diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index 6f4d41ba6ca7..a19816bbb1fd 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -27,6 +27,7 @@ properties: - renesas,usbhs-r9a07g044 # RZ/G2{L,LC} - renesas,usbhs-r9a07g054 # RZ/V2L - renesas,usbhs-r9a08g045 # RZ/G3S + - renesas,usbhs-r9a09g056 # RZ/V2N - renesas,usbhs-r9a09g057 # RZ/V2H(P) - const: renesas,rzg2l-usbhs diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 9d5253369c0a..49a5117d2bbb 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -21,6 +21,7 @@ patternProperties: "^(pciclass|pinctrl-single|#pinctrl-single|PowerPC),.*": true "^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true "^(simple-audio-card|st-plgpio|st-spics|ts),.*": true + "^pool[0-3],.*": true # Keep list in alphabetical order. "^100ask,.*": @@ -149,6 +150,8 @@ patternProperties: description: Arctic Sand "^arcx,.*": description: arcx Inc. / Archronix Inc. + "^argon40,.*": + description: Argon 40 Technologies Limited "^ariaboard,.*": description: Shanghai Novotech Co., Ltd. (Ariaboard) "^aries,.*": @@ -200,6 +203,8 @@ patternProperties: description: Shanghai Awinic Technology Co., Ltd. "^axentia,.*": description: Axentia Technologies AB + "^axiado,.*": + description: Axiado Corporation "^axis,.*": description: Axis Communications AB "^azoteq,.*": @@ -216,6 +221,8 @@ patternProperties: description: BeagleBoard.org Foundation "^belling,.*": description: Shanghai Belling Co., Ltd. + "^bestar,.*": + description: Shenzhen Bestar Electronic Technology Co., Ltd. "^bhf,.*": description: Beckhoff Automation GmbH & Co. KG "^bigtreetech,.*": @@ -306,6 +313,8 @@ patternProperties: description: Cirrus Logic, Inc. "^cisco,.*": description: Cisco Systems, Inc. + "^cix,.*": + description: CIX Technology Group Co., Ltd. "^clockwork,.*": description: Clockwork Tech LLC "^cloos,.*": @@ -500,6 +509,8 @@ patternProperties: description: Espressif Systems Co. Ltd. "^est,.*": description: ESTeem Wireless Modems + "^eswin,.*": + description: Beijing ESWIN Technology Group Co. Ltd. "^ettus,.*": description: NI Ettus Research "^eukrea,.*": @@ -790,6 +801,8 @@ patternProperties: description: Jide Tech "^joz,.*": description: JOZ BV + "^jty,.*": + description: JTY "^kam,.*": description: Kamstrup A/S "^karo,.*": @@ -894,6 +907,8 @@ patternProperties: description: Nanjing Loongmasses Ltd. "^lsi,.*": description: LSI Corp. (LSI Logic) + "^luckfox,.*": + description: Shenzhen Luckfox Technology Co., Ltd. "^lunzn,.*": description: Shenzhen Lunzn Technology Co., Ltd. "^luxul,.*": @@ -920,6 +935,8 @@ patternProperties: description: Maxim Integrated Products "^maxlinear,.*": description: MaxLinear Inc. + "^mayqueen,.*": + description: Mayqueen Technologies Ltd. "^mbvl,.*": description: Mobiveil Inc. "^mcube,.*": @@ -1071,6 +1088,8 @@ patternProperties: description: Next Thing Co. "^ni,.*": description: National Instruments + "^nicera,.*": + description: Nippon Ceramic Co., Ltd. "^nintendo,.*": description: Nintendo "^nlt,.*": @@ -1306,6 +1325,8 @@ patternProperties: description: Recharge Véhicule Électrique (RVE) inc. "^saef,.*": description: Saef Technology Limited + "^sakurapi,.*": + description: SakuraPi.org "^samsung,.*": description: Samsung Semiconductor "^samtec,.*": diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml index 8a6c3a75a547..34951783a633 100644 --- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml @@ -37,6 +37,7 @@ properties: - fsl,ls1012a-wdt - fsl,ls1021a-wdt - fsl,ls1043a-wdt + - fsl,ls1046a-wdt - fsl,vf610-wdt - const: fsl,imx21-wdt @@ -105,6 +106,7 @@ allOf: - fsl,ls1012a-wdt - fsl,ls1021a-wdt - fsl,ls1043a-wdt + - fsl,ls1046a-wdt then: properties: big-endian: false diff --git a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml index 8d2520241e37..ba0bfd73ab62 100644 --- a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml @@ -34,6 +34,7 @@ properties: - items: - enum: - mediatek,mt2701-wdt + - mediatek,mt6572-wdt - mediatek,mt6582-wdt - mediatek,mt6797-wdt - mediatek,mt7622-wdt diff --git a/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml b/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml index 35ef940cbabe..8964c1c5d522 100644 --- a/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml @@ -19,6 +19,9 @@ properties: reg: maxItems: 1 + clocks: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/writing-bindings.rst b/Documentation/devicetree/bindings/writing-bindings.rst index 1ad081de2dd0..f8e0293a7c06 100644 --- a/Documentation/devicetree/bindings/writing-bindings.rst +++ b/Documentation/devicetree/bindings/writing-bindings.rst @@ -39,10 +39,22 @@ Overall design Properties ========== -- DO make 'compatible' properties specific. DON'T use wildcards in compatible - strings. DO use fallback compatibles when devices are the same as or a subset - of prior implementations. DO add new compatibles in case there are new - features or bugs. +- DO make 'compatible' properties specific. + + - DON'T use wildcards or device-family names in compatible strings. + + - DO use fallback compatibles when devices are the same as or a superset of + prior implementations. + + - DO add new compatibles in case there are new features or bugs. + + - DO use a SoC-specific compatible for all SoC devices, followed by a + fallback if appropriate. SoC-specific compatibles are also preferred for + the fallbacks. + + - DON'T use bus suffixes to encode the type of interface device is using. + The parent bus node already implies that interface. DON'T add the type of + device, if the device cannot be anything else. - DO use a vendor prefix on device-specific property names. Consider if properties could be common among devices of the same class. Check other @@ -51,12 +63,21 @@ Properties - DON'T redefine common properties. Just reference the definition and define constraints specific to the device. +- DON'T add properties to avoid a specific compatible. DON'T add properties if + they are implied by (deducible from) the compatible. + - DO use common property unit suffixes for properties with scientific units. Recommended suffixes are listed at https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/property-units.yaml - DO define properties in terms of constraints. How many entries? What are - possible values? What is the order? + possible values? What is the order? All these constraints represent the ABI + as well. + +- DON'T make changes that break the ABI without explicit and detailed rationale + for why the changes have to be made and their impact. ABI impact goes beyond + the Linux kernel, because it also covers other open-source upstream projects. + Typical cases and caveats ========================= @@ -64,7 +85,7 @@ Typical cases and caveats - Phandle entries, like clocks/dmas/interrupts/resets, should always be explicitly ordered. Include the {clock,dma,interrupt,reset}-names if there is more than one phandle. When used, both of these fields need the same - constraints (e.g. list of items). + constraints (e.g. list of items). - For names used in {clock,dma,interrupt,reset}-names, do not add any suffix, e.g.: "tx" instead of "txirq" (for interrupt). @@ -84,6 +105,15 @@ Typical cases and caveats - "syscon" is not a generic property. Use vendor and type, e.g. "vendor,power-manager-syscon". +- Do not add instance index (IDs) properties or custom OF aliases. If the + devices have different programming model, they might need different + compatibles. If such devices use some other device in a different way, e.g. + they program the phy differently, use cell/phandle arguments. + +- Bindings files should be named like compatible: vendor,device.yaml. In case + of multiple compatibles in the binding, use one of the fallbacks or a more + generic name, yet still matching compatible style. + Board/SoC .dts Files ==================== diff --git a/Documentation/devicetree/bindings/writing-schema.rst b/Documentation/devicetree/bindings/writing-schema.rst index fc73072f12fc..470d1521fa17 100644 --- a/Documentation/devicetree/bindings/writing-schema.rst +++ b/Documentation/devicetree/bindings/writing-schema.rst @@ -171,6 +171,9 @@ Coding style Use YAML coding style (two-space indentation). For DTS examples in the schema, preferred is four-space indentation. +Place entries in 'properties' and 'required' sections in the same order, using +style from Documentation/devicetree/bindings/dts-coding-style.rst. + Testing ------- diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst index 5a91df105141..607589592bfb 100644 --- a/Documentation/doc-guide/sphinx.rst +++ b/Documentation/doc-guide/sphinx.rst @@ -131,6 +131,29 @@ It supports two optional parameters: ``--no-virtualenv`` Use OS packaging for Sphinx instead of Python virtual environment. +Installing Sphinx Minimal Version +--------------------------------- + +When changing Sphinx build system, it is important to ensure that +the minimal version will still be supported. Nowadays, it is +becoming harder to do that on modern distributions, as it is not +possible to install with Python 3.13 and above. + +Testing with the lowest supported Python version as defined at +Documentation/process/changes.rst can be done by creating +a venv with it with, and install minimal requirements with:: + + /usr/bin/python3.9 -m venv sphinx_min + . sphinx_min/bin/activate + pip install -r Documentation/sphinx/min_requirements.txt + +A more comprehensive test can be done by using: + + scripts/test_doc_build.py + +Such script create one Python venv per supported version, +optionally building documentation for a range of Sphinx versions. + Sphinx Build ============ diff --git a/Documentation/driver-api/cxl/conventions.rst b/Documentation/driver-api/cxl/conventions.rst new file mode 100644 index 000000000000..da347a81a237 --- /dev/null +++ b/Documentation/driver-api/cxl/conventions.rst @@ -0,0 +1,47 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: + +======================================= +Compute Express Link: Linux Conventions +======================================= + +There exists shipping platforms that bend or break CXL specification +expectations. Record the details and the rationale for those deviations. +Borrow the ACPI Code First template format to capture the assumptions +and tradeoffs such that multiple platform implementations can follow the +same convention. + +<(template) Title> +================== + +Document +-------- +CXL Revision , Version + +License +------- +SPDX-License Identifier: CC-BY-4.0 + +Creator/Contributors +-------------------- + +Summary of the Change +--------------------- + + + + +Benefits of the Change +---------------------- + + + +References +---------- + +Detailed Description of the Change +---------------------------------- + + diff --git a/Documentation/driver-api/cxl/devices/device-types.rst b/Documentation/driver-api/cxl/devices/device-types.rst index f5e4330c1cfe..923f5d89bc04 100644 --- a/Documentation/driver-api/cxl/devices/device-types.rst +++ b/Documentation/driver-api/cxl/devices/device-types.rst @@ -63,13 +63,13 @@ A Type-2 CXL Device: * Supports cxl.io, cxl.cache, and cxl.mem protocols * Optionally implements coherent cache and Host-Managed Device Memory -* Is typically an accelerator device w/ high bandwidth memory. +* Is typically an accelerator device with high bandwidth memory. The primary difference between a type-1 and type-2 device is the presence of host-managed device memory, which allows the device to operate on a -local memory bank - while the CPU sill has coherent DMA to the same memory. +local memory bank - while the CPU still has coherent DMA to the same memory. -The allows things like GPUs to expose their memory via DAX devices or file +This allows things like GPUs to expose their memory via DAX devices or file descriptors, allows drivers and programs direct access to device memory rather than use block-transfer semantics. @@ -89,7 +89,7 @@ basic coherent DMA. Switch ------ -A CXL switch is a device capacity of routing any CXL (and by extension, PCIe) +A CXL switch is a device capable of routing any CXL (and by extension, PCIe) protocol between an upstream, downstream, or peer devices. Many devices, such as Multi-Logical Devices, imply the presence of switching in some manner. @@ -103,7 +103,7 @@ A Single-Logical Device (SLD) is a device which presents a single device to one or more heads. A Multi-Logical Device (MLD) is a device which may present multiple devices -to one or more devices. +to one or more upstream devices. A Single-Headed Device exposes only a single physical connection. diff --git a/Documentation/driver-api/cxl/index.rst b/Documentation/driver-api/cxl/index.rst index 9e1414ad3357..c1106a68b67c 100644 --- a/Documentation/driver-api/cxl/index.rst +++ b/Documentation/driver-api/cxl/index.rst @@ -14,6 +14,7 @@ that have impacts on each other. The docs here break up configurations steps. theory-of-operation maturity-map + conventions .. toctree:: :maxdepth: 2 diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst index 9759e90c3cf1..dd6dd17dc536 100644 --- a/Documentation/driver-api/cxl/linux/cxl-driver.rst +++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst @@ -20,7 +20,7 @@ The CXL driver is split into a number of drivers. * cxl_port - initializes root and provides port enumeration interface. * cxl_acpi - initializes root decoders and interacts with ACPI data. * cxl_p/mem - initializes memory devices -* cxl_pci - uses cxl_port to enumates the actual fabric hierarchy. +* cxl_pci - uses cxl_port to enumerate the actual fabric hierarchy. Driver Devices ============== diff --git a/Documentation/driver-api/cxl/theory-of-operation.rst b/Documentation/driver-api/cxl/theory-of-operation.rst index 40793dad3630..257f513e320c 100644 --- a/Documentation/driver-api/cxl/theory-of-operation.rst +++ b/Documentation/driver-api/cxl/theory-of-operation.rst @@ -29,8 +29,8 @@ Platform firmware enumerates a menu of interleave options at the "CXL root port" (Linux term for the top of the CXL decode topology). From there, PCIe topology dictates which endpoints can participate in which Host Bridge decode regimes. Each PCIe Switch in the path between the root and an endpoint introduces a point -at which the interleave can be split. For example platform firmware may say at a -given range only decodes to 1 one Host Bridge, but that Host Bridge may in turn +at which the interleave can be split. For example, platform firmware may say a +given range only decodes to one Host Bridge, but that Host Bridge may in turn interleave cycles across multiple Root Ports. An intervening Switch between a port and an endpoint may interleave cycles across multiple Downstream Switch Ports, etc. @@ -187,7 +187,7 @@ decodes them to "ports", "ports" decode to "endpoints", and "endpoints" represent the decode from SPA (System Physical Address) to DPA (Device Physical Address). -Continuing the RAID analogy, disks have both topology metadata and on device +Continuing the RAID analogy, disks have both topology metadata and on-device metadata that determine RAID set assembly. CXL Port topology and CXL Port link status is metadata for CXL.mem set assembly. The CXL Port topology is enumerated by the arrival of a CXL.mem device. I.e. unless and until the PCIe core attaches @@ -197,7 +197,7 @@ the Linux PCI core to tear down switch-level CXL resources because the endpoint ->remove() event cleans up the port data that was established to support that Memory Expander. -The port metadata and potential decode schemes that a give memory device may +The port metadata and potential decode schemes that a given memory device may participate can be determined via a command like:: # cxl list -BDMu -d root -m mem3 @@ -249,8 +249,8 @@ participate can be determined via a command like:: ...which queries the CXL topology to ask "given CXL Memory Expander with a kernel device name of 'mem3' which platform level decode ranges may this device participate". A given expander can participate in multiple CXL.mem interleave -sets simultaneously depending on how many decoder resource it has. In this -example mem3 can participate in one or more of a PMEM interleave that spans to +sets simultaneously depending on how many decoder resources it has. In this +example mem3 can participate in one or more of a PMEM interleave that spans two Host Bridges, a PMEM interleave that targets a single Host Bridge, a Volatile memory interleave that spans 2 Host Bridges, and a Volatile memory interleave that only targets a single Host Bridge. diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst index e6855cd37e85..eca72d9b9ed8 100644 --- a/Documentation/driver-api/dpll.rst +++ b/Documentation/driver-api/dpll.rst @@ -214,6 +214,24 @@ offset values are fractional with 3-digit decimal places and shell be divided with ``DPLL_PIN_PHASE_OFFSET_DIVIDER`` to get integer part and modulo divided to get fractional part. +Phase offset monitor +==================== + +Phase offset measurement is typically performed against the current active +source. However, some DPLL (Digital Phase-Locked Loop) devices may offer +the capability to monitor phase offsets across all available inputs. +The attribute and current feature state shall be included in the response +message of the ``DPLL_CMD_DEVICE_GET`` command for supported DPLL devices. +In such cases, users can also control the feature using the +``DPLL_CMD_DEVICE_SET`` command by setting the ``enum dpll_feature_state`` +values for the attribute. +Once enabled the phase offset measurements for the input shall be returned +in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute. + + =============================== ======================== + ``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature + =============================== ======================== + Embedded SYNC ============= @@ -235,6 +253,31 @@ the pin. ``DPLL_A_PIN_ESYNC_PULSE`` pulse type of Embedded SYNC ========================================= ================================= +Reference SYNC +============== + +The device may support the Reference SYNC feature, which allows the combination +of two inputs into a input pair. In this configuration, clock signals +from both inputs are used to synchronize the DPLL device. The higher frequency +signal is utilized for the loop bandwidth of the DPLL, while the lower frequency +signal is used to syntonize the output signal of the DPLL device. This feature +enables the provision of a high-quality loop bandwidth signal from an external +source. + +A capable input provides a list of inputs that can be bound with to create +Reference SYNC. To control this feature, the user must request a desired +state for a target pin: use ``DPLL_PIN_STATE_CONNECTED`` to enable or +``DPLL_PIN_STATE_DISCONNECTED`` to disable the feature. An input pin can be +bound to only one other pin at any given time. + + ============================== ========================================== + ``DPLL_A_PIN_REFERENCE_SYNC`` nested attribute for providing info or + requesting configuration of the Reference + SYNC feature + ``DPLL_A_PIN_ID`` target pin id for Reference SYNC feature + ``DPLL_A_PIN_STATE`` state of Reference SYNC connection + ============================== ========================================== + Configuration commands group ============================ diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 3d56f94ac2ee..2b36ebde9cec 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -275,7 +275,6 @@ GPIO devm_gpiod_put() devm_gpiod_unhinge() devm_gpiochip_add_data() - devm_gpio_request() devm_gpio_request_one() I2C diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index ae433261e11a..85d86f92c41b 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -750,7 +750,7 @@ compliance: - Test your driver with the appropriate in-kernel real-time test cases for both level and edge IRQs -* [1] http://www.spinics.net/lists/linux-omap/msg120425.html +* [1] https://lore.kernel.org/r/1437496011-11486-1-git-send-email-bigeasy@linutronix.de/ * [2] https://lore.kernel.org/r/1443209283-20781-2-git-send-email-grygorii.strashko@ti.com * [3] https://lore.kernel.org/r/1443209283-20781-3-git-send-email-grygorii.strashko@ti.com diff --git a/Documentation/driver-api/libata.rst b/Documentation/driver-api/libata.rst index 5da27a749246..93d97fe78e3f 100644 --- a/Documentation/driver-api/libata.rst +++ b/Documentation/driver-api/libata.rst @@ -283,18 +283,25 @@ interrupts, start DMA engine, etc. ``->error_handler()`` is a driver's hook into probe, hotplug, and recovery and other exceptional conditions. The primary responsibility of an -implementation is to call :c:func:`ata_do_eh` or :c:func:`ata_bmdma_drive_eh` -with a set of EH hooks as arguments: +implementation is to call :c:func:`ata_std_error_handler`. -'prereset' hook (may be NULL) is called during an EH reset, before any -other actions are taken. +:c:func:`ata_std_error_handler` will perform a standard error handling sequence +to resurect failed devices, detach lost devices and add new devices (if any). +This function will call the various reset operations for a port, as needed. +These operations are as follows. -'postreset' hook (may be NULL) is called after the EH reset is -performed. Based on existing conditions, severity of the problem, and -hardware capabilities, +* The 'prereset' operation (which may be NULL) is called during an EH reset, + before any other action is taken. -Either 'softreset' (may be NULL) or 'hardreset' (may be NULL) will be -called to perform the low-level EH reset. +* The 'postreset' hook (which may be NULL) is called after the EH reset is + performed. Based on existing conditions, severity of the problem, and hardware + capabilities, + +* Either the 'softreset' operation or the 'hardreset' operation will be called + to perform the low-level EH reset. If both operations are defined, + 'hardreset' is preferred and used. If both are not defined, no low-level reset + is performed and EH assumes that an ATA class device is connected through the + link. :: diff --git a/Documentation/driver-api/media/v4l2-controls.rst b/Documentation/driver-api/media/v4l2-controls.rst index b2e91804829b..fc04907589ab 100644 --- a/Documentation/driver-api/media/v4l2-controls.rst +++ b/Documentation/driver-api/media/v4l2-controls.rst @@ -110,6 +110,7 @@ For sub-device drivers: v4l2_ctrl_handler_free(&foo->ctrl_handler); +:c:func:`v4l2_ctrl_handler_free` does not touch the handler's ``error`` field. 2) Add controls: @@ -191,12 +192,8 @@ These functions are typically called right after the V4L2_CID_TEST_PATTERN, ARRAY_SIZE(test_pattern) - 1, 0, 0, test_pattern); ... - if (foo->ctrl_handler.error) { - int err = foo->ctrl_handler.error; - - v4l2_ctrl_handler_free(&foo->ctrl_handler); - return err; - } + if (foo->ctrl_handler.error) + return v4l2_ctrl_handler_free(&foo->ctrl_handler); The :c:func:`v4l2_ctrl_new_std` function returns the v4l2_ctrl pointer to the new control, but if you do not need to access the pointer outside the diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index b41b1c56477f..0d27a40f5818 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -173,10 +173,15 @@ Locking ------- The PWM core list manipulations are protected by a mutex, so pwm_get() -and pwm_put() may not be called from an atomic context. Currently the -PWM core does not enforce any locking to pwm_enable(), pwm_disable() and -pwm_config(), so the calling context is currently driver specific. This -is an issue derived from the former barebone API and should be fixed soon. +and pwm_put() may not be called from an atomic context. +Most functions in the PWM consumer API might sleep and so must not be called +from atomic context. The notable exception is pwm_apply_atomic() which has the +same semantics as pwm_apply_might_sleep() but can be called from atomic context. +(The price for that is that it doesn't work for all PWM devices, use +pwm_might_sleep() to check if a given PWM supports atomic operation. + +Locking in the PWM core ensures that callbacks related to a single chip are +serialized. Helpers ------- diff --git a/Documentation/driver-api/serial/driver.rst b/Documentation/driver-api/serial/driver.rst index fa1ebfcd4472..c1db6a1a67c4 100644 --- a/Documentation/driver-api/serial/driver.rst +++ b/Documentation/driver-api/serial/driver.rst @@ -24,9 +24,8 @@ console support. Console Support --------------- -The serial core provides a few helper functions. This includes identifying -the correct port structure (via uart_get_console()) and decoding command line -arguments (uart_parse_options()). +The serial core provides a few helper functions. This includes +decoding command line arguments (uart_parse_options()). There is also a helper function (uart_console_write()) which performs a character by character write, translating newlines to CRLF sequences. @@ -76,7 +75,7 @@ Other functions uart_add_one_port uart_remove_one_port uart_console_write uart_parse_earlycon uart_parse_options uart_set_options uart_get_lsr_info uart_handle_dcd_change uart_handle_cts_change - uart_try_toggle_sysrq uart_get_console + uart_try_toggle_sysrq .. kernel-doc:: include/linux/serial_core.h :identifiers: uart_port_tx_limited uart_port_tx diff --git a/Documentation/driver-api/soundwire/bra.rst b/Documentation/driver-api/soundwire/bra.rst index 8500253fa3e8..c08ab2591496 100644 --- a/Documentation/driver-api/soundwire/bra.rst +++ b/Documentation/driver-api/soundwire/bra.rst @@ -333,4 +333,4 @@ FIFO sizes to avoid xruns. Alignment requirements are currently not enforced at the core level but at the platform-level, e.g. for Intel the data sizes must be -multiples of 32 bytes. +equal to or larger than 16 bytes. diff --git a/Documentation/driver-api/thermal/intel_dptf.rst b/Documentation/driver-api/thermal/intel_dptf.rst index ec5769accae0..c51ac793dc06 100644 --- a/Documentation/driver-api/thermal/intel_dptf.rst +++ b/Documentation/driver-api/thermal/intel_dptf.rst @@ -206,6 +206,15 @@ All these controls needs admin privilege to update. Update a new temperature target in milli degree celsius for hardware to use for the temperature control. +``thermal_tolerance`` (RW) + This attribute ranges from 0 to 7, where 0 represents + the most aggressive control to avoid any temperature overshoots, and + 7 represents a more graceful approach, favoring performance even at + the expense of temperature overshoots. + Note: This level may not scale linearly. For example, a value of 3 does + not necessarily imply a 50% improvement in performance compared to a + value of 0. + Given that this is platform temperature control, it is expected that a single user-level manager owns and manages the controls. If multiple user-level software applications attempt to write different targets, it diff --git a/Documentation/driver-api/tty/tty_port.rst b/Documentation/driver-api/tty/tty_port.rst index 5cb90e954fcf..504a353f2682 100644 --- a/Documentation/driver-api/tty/tty_port.rst +++ b/Documentation/driver-api/tty/tty_port.rst @@ -42,9 +42,10 @@ TTY Refcounting TTY Helpers ----------- +.. kernel-doc:: include/linux/tty_port.h + :identifiers: tty_port_tty_hangup tty_port_tty_vhangup .. kernel-doc:: drivers/tty/tty_port.c - :identifiers: tty_port_tty_hangup tty_port_tty_wakeup - + :identifiers: tty_port_tty_wakeup Modem Signals ------------- diff --git a/Documentation/driver-api/usb/anchors.rst b/Documentation/driver-api/usb/anchors.rst index 4b248e691bd6..5a93d171e76c 100644 --- a/Documentation/driver-api/usb/anchors.rst +++ b/Documentation/driver-api/usb/anchors.rst @@ -45,17 +45,6 @@ This function kills all URBs associated with an anchor. The URBs are called in the reverse temporal order they were submitted. This way no data can be reordered. -:c:func:`usb_unlink_anchored_urbs` ----------------------------------- - - -This function unlinks all URBs associated with an anchor. The URBs -are processed in the reverse temporal order they were submitted. -This is similar to :c:func:`usb_kill_anchored_urbs`, but it will not sleep. -Therefore no guarantee is made that the URBs have been unlinked when -the call returns. They may be unlinked later but will be unlinked in -finite time. - :c:func:`usb_scuttle_anchored_urbs` ----------------------------------- diff --git a/Documentation/fault-injection/fault-injection.rst b/Documentation/fault-injection/fault-injection.rst index 1c14ba08fbfc..c2d3996b5b40 100644 --- a/Documentation/fault-injection/fault-injection.rst +++ b/Documentation/fault-injection/fault-injection.rst @@ -2,7 +2,7 @@ Fault injection capabilities infrastructure =========================================== -See also drivers/md/md-faulty.c and "every_nth" module option for scsi_debug. +See also "every_nth" module option for scsi_debug. Available fault injection capabilities diff --git a/Documentation/filesystems/dax.rst b/Documentation/filesystems/dax.rst index 08dd5e254cc5..5b283f3d1eb1 100644 --- a/Documentation/filesystems/dax.rst +++ b/Documentation/filesystems/dax.rst @@ -206,7 +206,6 @@ stall the CPU for an extended period, you should also not attempt to implement direct_access. These block devices may be used for inspiration: -- brd: RAM backed block device driver - pmem: NVDIMM persistent memory driver diff --git a/Documentation/filesystems/ext4/atomic_writes.rst b/Documentation/filesystems/ext4/atomic_writes.rst index f65767df3620..aeb47ace738d 100644 --- a/Documentation/filesystems/ext4/atomic_writes.rst +++ b/Documentation/filesystems/ext4/atomic_writes.rst @@ -148,10 +148,10 @@ reserved during: only required to handle a split extent across leaf blocks. How to ------- +~~~~~~ Creating Filesystems with Atomic Write Support -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First check the atomic write units supported by block device. See :ref:`atomic_write_bdev_support` for more details. @@ -176,7 +176,7 @@ Where ``-b`` specifies the block size, ``-C`` specifies the cluster size in byte and ``-O bigalloc`` enables the bigalloc feature. Application Interface -~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^ Applications can use the ``pwritev2()`` system call with the ``RWF_ATOMIC`` flag to perform atomic writes: @@ -204,7 +204,7 @@ writes are supported. .. _atomic_write_bdev_support: Hardware Support ----------------- +~~~~~~~~~~~~~~~~ The underlying storage device must support atomic write operations. Modern NVMe and SCSI devices often provide this capability. @@ -217,7 +217,7 @@ Nonzero values for these attributes indicate that the device supports atomic writes. See Also --------- +~~~~~~~~ * :doc:`bigalloc` - Documentation on the bigalloc feature * :doc:`allocators` - Documentation on block allocation in ext4 diff --git a/Documentation/filesystems/ext4/bitmaps.rst b/Documentation/filesystems/ext4/bitmaps.rst index 91c45d86e9bb..9d7d7b083a25 100644 --- a/Documentation/filesystems/ext4/bitmaps.rst +++ b/Documentation/filesystems/ext4/bitmaps.rst @@ -19,10 +19,3 @@ necessarily the case that no blocks are in use -- if ``meta_bg`` is set, the bitmaps and group descriptor live inside the group. Unfortunately, ext2fs_test_block_bitmap2() will return '0' for those locations, which produces confusing debugfs output. - -Inode Table ------------ -Inode tables are statically allocated at mkfs time. Each block group -descriptor points to the start of the table, and the superblock records -the number of inodes per group. See the section on inodes for more -information. diff --git a/Documentation/filesystems/ext4/blockgroup.rst b/Documentation/filesystems/ext4/blockgroup.rst index ed5a5cac6d40..7cbf0b2b778e 100644 --- a/Documentation/filesystems/ext4/blockgroup.rst +++ b/Documentation/filesystems/ext4/blockgroup.rst @@ -1,7 +1,10 @@ .. SPDX-License-Identifier: GPL-2.0 +Block Groups +------------ + Layout ------- +~~~~~~ The layout of a standard block group is approximately as follows (each of these fields is discussed in a separate section below): @@ -60,7 +63,7 @@ groups (flex_bg). Leftover space is used for file data blocks, indirect block maps, extent tree blocks, and extended attributes. Flexible Block Groups ---------------------- +~~~~~~~~~~~~~~~~~~~~~ Starting in ext4, there is a new feature called flexible block groups (flex_bg). In a flex_bg, several block groups are tied together as one @@ -78,7 +81,7 @@ if flex_bg is enabled. The number of block groups that make up a flex_bg is given by 2 ^ ``sb.s_log_groups_per_flex``. Meta Block Groups ------------------ +~~~~~~~~~~~~~~~~~ Without the option META_BG, for safety concerns, all block group descriptors copies are kept in the first block group. Given the default @@ -117,7 +120,7 @@ Please see an important note about ``BLOCK_UNINIT`` in the section about block and inode bitmaps. Lazy Block Group Initialization -------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A new feature for ext4 are three block group descriptor flags that enable mkfs to skip initializing other parts of the block group diff --git a/Documentation/filesystems/ext4/dynamic.rst b/Documentation/filesystems/ext4/dynamic.rst index bb0c84333341..bbad439aada2 100644 --- a/Documentation/filesystems/ext4/dynamic.rst +++ b/Documentation/filesystems/ext4/dynamic.rst @@ -6,7 +6,9 @@ Dynamic Structures Dynamic metadata are created on the fly when files and blocks are allocated to files. -.. include:: inodes.rst -.. include:: ifork.rst -.. include:: directory.rst -.. include:: attributes.rst +.. toctree:: + + inodes + ifork + directory + attributes diff --git a/Documentation/filesystems/ext4/globals.rst b/Documentation/filesystems/ext4/globals.rst index b17418974fd3..c6a6abce818a 100644 --- a/Documentation/filesystems/ext4/globals.rst +++ b/Documentation/filesystems/ext4/globals.rst @@ -6,9 +6,12 @@ Global Structures The filesystem is sharded into a number of block groups, each of which have static metadata at fixed locations. -.. include:: super.rst -.. include:: group_descr.rst -.. include:: bitmaps.rst -.. include:: mmp.rst -.. include:: journal.rst -.. include:: orphan.rst +.. toctree:: + + super + group_descr + bitmaps + inode_table + mmp + journal + orphan diff --git a/Documentation/filesystems/ext4/index.rst b/Documentation/filesystems/ext4/index.rst index 705d813d558f..1ff8150c50e9 100644 --- a/Documentation/filesystems/ext4/index.rst +++ b/Documentation/filesystems/ext4/index.rst @@ -5,7 +5,7 @@ ext4 Data Structures and Algorithms =================================== .. toctree:: - :maxdepth: 6 + :maxdepth: 2 :numbered: about diff --git a/Documentation/filesystems/ext4/inode_table.rst b/Documentation/filesystems/ext4/inode_table.rst new file mode 100644 index 000000000000..f7900a52c0d5 --- /dev/null +++ b/Documentation/filesystems/ext4/inode_table.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Inode Table +----------- + +Inode tables are statically allocated at mkfs time. Each block group +descriptor points to the start of the table, and the superblock records +the number of inodes per group. See :doc:`inode documentation ` +for more information on inode table layout. diff --git a/Documentation/filesystems/ext4/overview.rst b/Documentation/filesystems/ext4/overview.rst index 9d4054c17ecb..171c3963d7f6 100644 --- a/Documentation/filesystems/ext4/overview.rst +++ b/Documentation/filesystems/ext4/overview.rst @@ -16,13 +16,15 @@ All fields in ext4 are written to disk in little-endian order. HOWEVER, all fields in jbd2 (the journal) are written to disk in big-endian order. -.. include:: blocks.rst -.. include:: blockgroup.rst -.. include:: special_inodes.rst -.. include:: allocators.rst -.. include:: checksums.rst -.. include:: bigalloc.rst -.. include:: inlinedata.rst -.. include:: eainode.rst -.. include:: verity.rst -.. include:: atomic_writes.rst +.. toctree:: + + blocks + blockgroup + special_inodes + allocators + checksums + bigalloc + inlinedata + eainode + verity + atomic_writes diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 440e4ae74e44..e5bb89452aff 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -218,7 +218,7 @@ mode=%s Control block allocation mode which supports "adaptive" fragmentation/after-GC situation itself. The developers use these modes to understand filesystem fragmentation/after-GC condition well, and eventually get some insights to handle them better. - In "fragment:segment", f2fs allocates a new segment in ramdom + In "fragment:segment", f2fs allocates a new segment in random position. With this, we can simulate the after-GC condition. In "fragment:block", we can scatter block allocation with "max_fragment_chunk" and "max_fragment_hole" sysfs nodes. @@ -238,9 +238,9 @@ usrjquota= Appoint specified file and type during mount, so that quota grpjquota= information can be properly updated during recovery flow, prjjquota= : must be in root directory; jqfmt= : [vfsold,vfsv0,vfsv1]. -offusrjquota Turn off user journalled quota. -offgrpjquota Turn off group journalled quota. -offprjjquota Turn off project journalled quota. +usrjquota= Turn off user journalled quota. +grpjquota= Turn off group journalled quota. +prjjquota= Turn off project journalled quota. quota Enable plain user disk quota accounting. noquota Disable all plain disk quota option. alloc_mode=%s Adjust block allocation policy, which supports "reuse" @@ -261,7 +261,7 @@ test_dummy_encryption=%s The argument may be either "v1" or "v2", in order to select the corresponding fscrypt policy version. checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable" - to reenable checkpointing. Is enabled by default. While + to re-enable checkpointing. Is enabled by default. While disabled, any unmounting or unexpected shutdowns will cause the filesystem contents to appear as they did when the filesystem was mounted with that option. diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 29e84d125e02..696a5844bfa3 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -147,9 +147,8 @@ However, these ioctls have some limitations: were wiped. To partially solve this, you can add init_on_free=1 to your kernel command line. However, this has a performance cost. -- Secret keys might still exist in CPU registers, in crypto - accelerator hardware (if used by the crypto API to implement any of - the algorithms), or in other places not explicitly considered here. +- Secret keys might still exist in CPU registers or in other places + not explicitly considered here. Full system compromise ~~~~~~~~~~~~~~~~~~~~~~ @@ -406,9 +405,12 @@ the work is done by XChaCha12, which is much faster than AES when AES acceleration is unavailable. For more information about Adiantum, see `the Adiantum paper `_. -The (AES-128-CBC-ESSIV, AES-128-CBC-CTS) pair exists only to support -systems whose only form of AES acceleration is an off-CPU crypto -accelerator such as CAAM or CESA that does not support XTS. +The (AES-128-CBC-ESSIV, AES-128-CBC-CTS) pair was added to try to +provide a more efficient option for systems that lack AES instructions +in the CPU but do have a non-inline crypto engine such as CAAM or CESA +that supports AES-CBC (and not AES-XTS). This is deprecated. It has +been shown that just doing AES on the CPU is actually faster. +Moreover, Adiantum is faster still and is recommended on such systems. The remaining mode pairs are the "national pride ciphers": @@ -468,14 +470,6 @@ API, but the filenames mode still does. - Recommended: - AES-CBC acceleration -fscrypt also uses HMAC-SHA512 for key derivation, so enabling SHA-512 -acceleration is recommended: - -- SHA-512 - - Recommended: - - arm64: CONFIG_CRYPTO_SHA512_ARM64_CE - - x86: CONFIG_CRYPTO_SHA512_SSSE3 - Contents encryption ------------------- @@ -1326,22 +1320,13 @@ this by validating all top-level encryption policies prior to access. Inline encryption support ========================= -By default, fscrypt uses the kernel crypto API for all cryptographic -operations (other than HKDF, which fscrypt partially implements -itself). The kernel crypto API supports hardware crypto accelerators, -but only ones that work in the traditional way where all inputs and -outputs (e.g. plaintexts and ciphertexts) are in memory. fscrypt can -take advantage of such hardware, but the traditional acceleration -model isn't particularly efficient and fscrypt hasn't been optimized -for it. - -Instead, many newer systems (especially mobile SoCs) have *inline -encryption hardware* that can encrypt/decrypt data while it is on its -way to/from the storage device. Linux supports inline encryption -through a set of extensions to the block layer called *blk-crypto*. -blk-crypto allows filesystems to attach encryption contexts to bios -(I/O requests) to specify how the data will be encrypted or decrypted -in-line. For more information about blk-crypto, see +Many newer systems (especially mobile SoCs) have *inline encryption +hardware* that can encrypt/decrypt data while it is on its way to/from +the storage device. Linux supports inline encryption through a set of +extensions to the block layer called *blk-crypto*. blk-crypto allows +filesystems to attach encryption contexts to bios (I/O requests) to +specify how the data will be encrypted or decrypted in-line. For more +information about blk-crypto, see :ref:`Documentation/block/inline-encryption.rst `. On supported filesystems (currently ext4 and f2fs), fscrypt can use diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index dacdbc1149e6..412cf11e3298 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -185,8 +185,7 @@ FS_IOC_ENABLE_VERITY can fail with the following errors: - ``ENOKEY``: the ".fs-verity" keyring doesn't contain the certificate needed to verify the builtin signature - ``ENOPKG``: fs-verity recognizes the hash algorithm, but it's not - available in the kernel's crypto API as currently configured (e.g. - for SHA-512, missing CONFIG_CRYPTO_SHA512). + available in the kernel as currently configured - ``ENOTTY``: this type of filesystem does not implement fs-verity - ``EOPNOTSUPP``: the kernel was not configured with fs-verity support; or the filesystem superblock has not had the 'verity' diff --git a/Documentation/filesystems/iomap/design.rst b/Documentation/filesystems/iomap/design.rst index f2df9b6df988..0f7672676c0b 100644 --- a/Documentation/filesystems/iomap/design.rst +++ b/Documentation/filesystems/iomap/design.rst @@ -167,7 +167,6 @@ structure below: struct dax_device *dax_dev; void *inline_data; void *private; - const struct iomap_folio_ops *folio_ops; u64 validity_cookie; }; @@ -292,8 +291,6 @@ The fields are as follows: `_. This value will be passed unchanged to ``->iomap_end``. - * ``folio_ops`` will be covered in the section on pagecache operations. - * ``validity_cookie`` is a magic freshness value set by the filesystem that should be used to detect stale mappings. For pagecache operations this is critical for correct operation diff --git a/Documentation/filesystems/iomap/operations.rst b/Documentation/filesystems/iomap/operations.rst index 3b628e370d88..067ed8e14ef3 100644 --- a/Documentation/filesystems/iomap/operations.rst +++ b/Documentation/filesystems/iomap/operations.rst @@ -57,21 +57,19 @@ The following address space operations can be wrapped easily: * ``bmap`` * ``swap_activate`` -``struct iomap_folio_ops`` +``struct iomap_write_ops`` -------------------------- -The ``->iomap_begin`` function for pagecache operations may set the -``struct iomap::folio_ops`` field to an ops structure to override -default behaviors of iomap: - .. code-block:: c - struct iomap_folio_ops { + struct iomap_write_ops { struct folio *(*get_folio)(struct iomap_iter *iter, loff_t pos, unsigned len); void (*put_folio)(struct inode *inode, loff_t pos, unsigned copied, struct folio *folio); bool (*iomap_valid)(struct inode *inode, const struct iomap *iomap); + int (*read_folio_range)(const struct iomap_iter *iter, + struct folio *folio, loff_t pos, size_t len); }; iomap calls these functions: @@ -127,6 +125,10 @@ iomap calls these functions: ``->iomap_valid``, then the iomap should considered stale and the validation failed. + - ``read_folio_range``: Called to synchronously read in the range that will + be written to. If this function is not provided, iomap will default to + submitting a bio read request. + These ``struct kiocb`` flags are significant for buffered I/O with iomap: * ``IOCB_NOWAIT``: Turns on ``IOMAP_NOWAIT``. @@ -271,7 +273,7 @@ writeback. It does not lock ``i_rwsem`` or ``invalidate_lock``. The dirty bit will be cleared for all folios run through the -``->map_blocks`` machinery described below even if the writeback fails. +``->writeback_range`` machinery described below even if the writeback fails. This is to prevent dirty folio clots when storage devices fail; an ``-EIO`` is recorded for userspace to collect via ``fsync``. @@ -283,15 +285,14 @@ The ``ops`` structure must be specified and is as follows: .. code-block:: c struct iomap_writeback_ops { - int (*map_blocks)(struct iomap_writepage_ctx *wpc, struct inode *inode, - loff_t offset, unsigned len); - int (*submit_ioend)(struct iomap_writepage_ctx *wpc, int status); - void (*discard_folio)(struct folio *folio, loff_t pos); + int (*writeback_range)(struct iomap_writepage_ctx *wpc, + struct folio *folio, u64 pos, unsigned int len, u64 end_pos); + int (*writeback_submit)(struct iomap_writepage_ctx *wpc, int error); }; The fields are as follows: - - ``map_blocks``: Sets ``wpc->iomap`` to the space mapping of the file + - ``writeback_range``: Sets ``wpc->iomap`` to the space mapping of the file range (in bytes) given by ``offset`` and ``len``. iomap calls this function for each dirty fs block in each dirty folio, though it will `reuse mappings @@ -306,27 +307,26 @@ The fields are as follows: This revalidation must be open-coded by the filesystem; it is unclear if ``iomap::validity_cookie`` can be reused for this purpose. - This function must be supplied by the filesystem. - - ``submit_ioend``: Allows the file systems to hook into writeback bio - submission. - This might include pre-write space accounting updates, or installing - a custom ``->bi_end_io`` function for internal purposes, such as - deferring the ioend completion to a workqueue to run metadata update - transactions from process context before submitting the bio. - This function is optional. - - - ``discard_folio``: iomap calls this function after ``->map_blocks`` - fails to schedule I/O for any part of a dirty folio. - The function should throw away any reservations that may have been - made for the write. + If this methods fails to schedule I/O for any part of a dirty folio, it + should throw away any reservations that may have been made for the write. The folio will be marked clean and an ``-EIO`` recorded in the pagecache. Filesystems can use this callback to `remove `_ delalloc reservations to avoid having delalloc reservations for clean pagecache. - This function is optional. + This function must be supplied by the filesystem. + + - ``writeback_submit``: Submit the previous built writeback context. + Block based file systems should use the iomap_ioend_writeback_submit + helper, other file system can implement their own. + File systems can optionall to hook into writeback bio submission. + This might include pre-write space accounting updates, or installing + a custom ``->bi_end_io`` function for internal purposes, such as + deferring the ioend completion to a workqueue to run metadata update + transactions from process context before submitting the bio. + This function must be supplied by the filesystem. Pagecache Writeback Completion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -340,10 +340,9 @@ If the write failed, it will also set the error bits on the folios and the address space. This can happen in interrupt or process context, depending on the storage device. - Filesystems that need to update internal bookkeeping (e.g. unwritten -extent conversions) should provide a ``->submit_ioend`` function to -set ``struct iomap_end::bio::bi_end_io`` to its own function. +extent conversions) should set their own bi_end_io on the bios +submitted by ``->submit_writeback`` This function should call ``iomap_finish_ioends`` after finishing its own work (e.g. unwritten extent conversion). diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index 2e567e341c3b..aa287ccdac2f 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -87,8 +87,8 @@ prototypes:: int (*tmpfile) (struct mnt_idmap *, struct inode *, struct file *, umode_t); int (*fileattr_set)(struct mnt_idmap *idmap, - struct dentry *dentry, struct fileattr *fa); - int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa); + struct dentry *dentry, struct file_kattr *fa); + int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa); struct posix_acl * (*get_acl)(struct mnt_idmap *, struct dentry *, int); struct offset_ctx *(*get_offset_ctx)(struct inode *inode); @@ -253,10 +253,10 @@ prototypes:: int (*writepages)(struct address_space *, struct writeback_control *); bool (*dirty_folio)(struct address_space *, struct folio *folio); void (*readahead)(struct readahead_control *); - int (*write_begin)(struct file *, struct address_space *mapping, + int (*write_begin)(const struct kiocb *, struct address_space *mapping, loff_t pos, unsigned len, struct folio **foliop, void **fsdata); - int (*write_end)(struct file *, struct address_space *mapping, + int (*write_end)(const struct kiocb *, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct folio *folio, void *fsdata); sector_t (*bmap)(struct address_space *, sector_t); diff --git a/Documentation/filesystems/overlayfs.rst b/Documentation/filesystems/overlayfs.rst index 4133a336486d..ab989807a2cb 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -9,7 +9,7 @@ Overlay Filesystem This document describes a prototype for a new approach to providing overlay-filesystem functionality in Linux (sometimes referred to as union-filesystems). An overlay-filesystem tries to present a -filesystem which is the result over overlaying one filesystem on top +filesystem which is the result of overlaying one filesystem on top of the other. @@ -61,7 +61,7 @@ Inode properties |Configuration | Persistent | Uniform | st_ino == d_ino | d_ino == i_ino | | | st_ino | st_dev | | [*] | +==============+=====+======+=====+======+========+========+========+=======+ -| | dir | !dir | dir | !dir | dir + !dir | dir | !dir | +| | dir | !dir | dir | !dir | dir | !dir | dir | !dir | +--------------+-----+------+-----+------+--------+--------+--------+-------+ | All layers | Y | Y | Y | Y | Y | Y | Y | Y | | on same fs | | | | | | | | | @@ -425,7 +425,7 @@ of information from up to three different layers: The "lower data" file can be on any lower layer, except from the top most lower layer. -Below the top most lower layer, any number of lower most layers may be defined +Below the topmost lower layer, any number of lowermost layers may be defined as "data-only" lower layers, using double colon ("::") separators. A normal lower layer is not allowed to be below a data-only layer, so single colon separators are not allowed to the right of double colon ("::") separators. @@ -445,8 +445,8 @@ to the absolute path of the "lower data" file in the "data-only" lower layer. Instead of explicitly enabling "metacopy=on" it is sufficient to specify at least one data-only layer to enable redirection of data to a data-only layer. -In this case other forms of metacopy are rejected. Note: this way data-only -layers may be used toghether with "userxattr", in which case careful attention +In this case other forms of metacopy are rejected. Note: this way, data-only +layers may be used together with "userxattr", in which case careful attention must be given to privileges needed to change the "user.overlay.redirect" xattr to prevent misuse. @@ -515,7 +515,7 @@ supports these values: The metacopy digest is never generated or used. This is the default if verity option is not specified. - "on": - Whenever a metacopy files specifies an expected digest, the + Whenever a metacopy file specifies an expected digest, the corresponding data file must match the specified digest. When generating a metacopy file the verity digest will be set in it based on the source file (if it has one). @@ -537,7 +537,7 @@ Using an upper layer path and/or a workdir path that are already used by another overlay mount is not allowed and may fail with EBUSY. Using partially overlapping paths is not allowed and may fail with EBUSY. If files are accessed from two overlayfs mounts which share or overlap the -upper layer and/or workdir path the behavior of the overlay is undefined, +upper layer and/or workdir path, the behavior of the overlay is undefined, though it will not result in a crash or deadlock. Mounting an overlay using an upper layer path, where the upper layer path @@ -778,7 +778,7 @@ controlled by the "uuid" mount option, which supports these values: - "auto": (default) UUID is taken from xattr "trusted.overlay.uuid" if it exists. Upgrade to "uuid=on" on first time mount of new overlay filesystem that - meets the prerequites. + meets the prerequisites. Downgrade to "uuid=null" for existing overlay filesystems that were never mounted with "uuid=on". @@ -794,20 +794,20 @@ without significant effort. The advantage of mounting with the "volatile" option is that all forms of sync calls to the upper filesystem are omitted. -In order to avoid a giving a false sense of safety, the syncfs (and fsync) +In order to avoid giving a false sense of safety, the syncfs (and fsync) semantics of volatile mounts are slightly different than that of the rest of VFS. If any writeback error occurs on the upperdir's filesystem after a volatile mount takes place, all sync functions will return an error. Once this condition is reached, the filesystem will not recover, and every subsequent sync -call will return an error, even if the upperdir has not experience a new error +call will return an error, even if the upperdir has not experienced a new error since the last sync call. When overlay is mounted with "volatile" option, the directory "$workdir/work/incompat/volatile" is created. During next mount, overlay checks for this directory and refuses to mount if present. This is a strong -indicator that user should throw away upper and work directories and create -fresh one. In very limited cases where the user knows that the system has -not crashed and contents of upperdir are intact, The "volatile" directory +indicator that the user should discard upper and work directories and create +fresh ones. In very limited cases where the user knows that the system has +not crashed and contents of upperdir are intact, the "volatile" directory can be removed. diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index 3616d7161dab..85f590254f07 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1224,9 +1224,6 @@ lookup_noperm_unlocked(), lookup_noperm_positive_unlocked(). They now take a qstr instead of separate name and length. QSTR() can be used when strlen() is needed for the length. -For try_lookup_noperm() a reference to the qstr is passed in case the -hash might subsequently be needed. - These function no longer do any permission checking - they previously checked that the caller has 'X' permission on the parent. They must ONLY be used internally by a filesystem on itself when it knows that @@ -1249,3 +1246,42 @@ Using try_lookup_noperm() will require linux/namei.h to be included. Calling conventions for ->d_automount() have changed; we should *not* grab an extra reference to new mount - it should be returned with refcount 1. + +--- + +collect_mounts()/drop_collected_mounts()/iterate_mounts() are gone now. +Replacement is collect_paths()/drop_collected_path(), with no special +iterator needed. Instead of a cloned mount tree, the new interface returns +an array of struct path, one for each mount collect_mounts() would've +created. These struct path point to locations in the caller's namespace +that would be roots of the cloned mounts. + +--- + +**mandatory** + +If your filesystem sets the default dentry_operations, use set_default_d_op() +rather than manually setting sb->s_d_op. + +--- + +**mandatory** + +d_set_d_op() is no longer exported (or public, for that matter); _if_ +your filesystem really needed that, make use of d_splice_alias_ops() +to have them set. Better yet, think hard whether you need different +->d_op for different dentries - if not, just use set_default_d_op() +at mount time and be done with that. Currently procfs is the only +thing that really needs ->d_op varying between dentries. + +--- + +**highly recommended** + +The file operations mmap() callback is deprecated in favour of +mmap_prepare(). This passes a pointer to a vm_area_desc to the callback +rather than a VMA, as the VMA at this stage is not yet valid. + +The vm_area_desc provides the minimum required information for a filesystem +to initialise state upon memory mapping of a file-backed region, and output +parameters for the file system to set this state. diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index 5236cb52e357..2971551b7235 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -1196,12 +1196,14 @@ SecPageTables Memory consumed by secondary page tables, this currently includes KVM mmu and IOMMU allocations on x86 and arm64. NFS_Unstable - Always zero. Previous counted pages which had been written to + Always zero. Previously counted pages which had been written to the server, but has not been committed to stable storage. Bounce - Memory used for block device "bounce buffers" + Always zero. Previously memory used for block device + "bounce buffers". WritebackTmp - Memory used by FUSE for temporary writeback buffers + Always zero. Previously memory used by FUSE for temporary + writeback buffers. CommitLimit Based on the overcommit ratio ('vm.overcommit_ratio'), this is the total amount of memory currently available to diff --git a/Documentation/filesystems/propagate_umount.txt b/Documentation/filesystems/propagate_umount.txt new file mode 100644 index 000000000000..c90349e5b889 --- /dev/null +++ b/Documentation/filesystems/propagate_umount.txt @@ -0,0 +1,484 @@ + Notes on propagate_umount() + +Umount propagation starts with a set of mounts we are already going to +take out. Ideally, we would like to add all downstream cognates to +that set - anything with the same mountpoint as one of the removed +mounts and with parent that would receive events from the parent of that +mount. However, there are some constraints the resulting set must +satisfy. + +It is convenient to define several properties of sets of mounts: + +1) A set S of mounts is non-shifting if for any mount X belonging +to S all subtrees mounted strictly inside of X (i.e. not overmounting +the root of X) contain only elements of S. + +2) A set S is non-revealing if all locked mounts that belong to S have +parents that also belong to S. + +3) A set S is closed if it contains all children of its elements. + +The set of mounts taken out by umount(2) must be non-shifting and +non-revealing; the first constraint is what allows to reparent +any remaining mounts and the second is what prevents the exposure +of any concealed mountpoints. + +propagate_umount() takes the original set as an argument and tries to +extend that set. The original set is a full subtree and its root is +unlocked; what matters is that it's closed and non-revealing. +Resulting set may not be closed; there might still be mounts outside +of that set, but only on top of stacks of root-overmounting elements +of set. They can be reparented to the place where the bottom of +stack is attached to a mount that will survive. NOTE: doing that +will violate a constraint on having no more than one mount with +the same parent/mountpoint pair; however, the caller (umount_tree()) +will immediately remedy that - it may keep unmounted element attached +to parent, but only if the parent itself is unmounted. Since all +conflicts created by reparenting have common parent *not* in the +set and one side of the conflict (bottom of the stack of overmounts) +is in the set, it will be resolved. However, we rely upon umount_tree() +doing that pretty much immediately after the call of propagate_umount(). + +Algorithm is based on two statements: + 1) for any set S, there is a maximal non-shifting subset of S +and it can be calculated in O(#S) time. + 2) for any non-shifting set S, there is a maximal non-revealing +subset of S. That subset is also non-shifting and it can be calculated +in O(#S) time. + + Finding candidates. + +We are given a closed set U and we want to find all mounts that have +the same mountpoint as some mount m in U *and* whose parent receives +propagation from the parent of the same mount m. Naive implementation +would be + S = {} + for each m in U + add m to S + p = parent(m) + for each q in Propagation(p) - {p} + child = look_up(q, mountpoint(m)) + if child + add child to S +but that can lead to excessive work - there might be propagation among the +subtrees of U, in which case we'd end up examining the same candidates +many times. Since propagation is transitive, the same will happen to +everything downstream of that candidate and it's not hard to construct +cases where the approach above leads to the time quadratic by the actual +number of candidates. + +Note that if we run into a candidate we'd already seen, it must've been +added on an earlier iteration of the outer loop - all additions made +during one iteration of the outer loop have different parents. So +if we find a child already added to the set, we know that everything +in Propagation(parent(child)) with the same mountpoint has been already +added. + S = {} + for each m in U + if m in S + continue + add m to S + p = parent(m) + q = propagation_next(p, p) + while q + child = look_up(q, mountpoint(m)) + if child + if child in S + q = skip_them(q, p) + continue; + add child to S + q = propagation_next(q, p) +where +skip_them(q, p) + keep walking Propagation(p) from q until we find something + not in Propagation(q) + +would get rid of that problem, but we need a sane implementation of +skip_them(). That's not hard to do - split propagation_next() into +"down into mnt_slave_list" and "forward-and-up" parts, with the +skip_them() being "repeat the forward-and-up part until we get NULL +or something that isn't a peer of the one we are skipping". + +Note that there can be no absolute roots among the extra candidates - +they all come from mount lookups. Absolute root among the original +set is _currently_ impossible, but it might be worth protecting +against. + + Maximal non-shifting subsets. + +Let's call a mount m in a set S forbidden in that set if there is a +subtree mounted strictly inside m and containing mounts that do not +belong to S. + +The set is non-shifting when none of its elements are forbidden in it. + +If mount m is forbidden in a set S, it is forbidden in any subset S' it +belongs to. In other words, it can't belong to any of the non-shifting +subsets of S. If we had a way to find a forbidden mount or show that +there's none, we could use it to find the maximal non-shifting subset +simply by finding and removing them until none remain. + +Suppose mount m is forbidden in S; then any mounts forbidden in S - {m} +must have been forbidden in S itself. Indeed, since m has descendents +that do not belong to S, any subtree that fits into S will fit into +S - {m} as well. + +So in principle we could go through elements of S, checking if they +are forbidden in S and removing the ones that are. Removals will +not invalidate the checks done for earlier mounts - if they were not +forbidden at the time we checked, they won't become forbidden later. +It's too costly to be practical, but there is a similar approach that +is linear by size of S. + +Let's say that mount x in a set S is forbidden by mount y, if + * both x and y belong to S. + * there is a chain of mounts starting at x and leaving S + immediately after passing through y, with the first + mountpoint strictly inside x. +Note 1: x may be equal to y - that's the case when something not +belonging to S is mounted strictly inside x. +Note 2: if y does not belong to S, it can't forbid anything in S. +Note 3: if y has no children outside of S, it can't forbid anything in S. + +It's easy to show that mount x is forbidden in S if and only if x is +forbidden in S by some mount y. And it's easy to find all mounts in S +forbidden by a given mount. + +Consider the following operation: + Trim(S, m) = S - {x : x is forbidden by m in S} + +Note that if m does not belong to S or has no children outside of S we +are guaranteed that Trim(S, m) is equal to S. + +The following is true: if x is forbidden by y in Trim(S, m), it was +already forbidden by y in S. + +Proof: Suppose x is forbidden by y in Trim(S, m). Then there is a +chain of mounts (x_0 = x, ..., x_k = y, x_{k+1} = r), such that x_{k+1} +is the first element that doesn't belong to Trim(S, m) and the +mountpoint of x_1 is strictly inside x. If mount r belongs to S, it must +have been removed by Trim(S, m), i.e. it was forbidden in S by m. +Then there was a mount chain from r to some child of m that stayed in +S all the way until m, but that's impossible since x belongs to Trim(S, m) +and prepending (x_0, ..., x_k) to that chain demonstrates that x is also +forbidden in S by m, and thus can't belong to Trim(S, m). +Therefore r can not belong to S and our chain demonstrates that +x is forbidden by y in S. QED. + +Corollary: no mount is forbidden by m in Trim(S, m). Indeed, any +such mount would have been forbidden by m in S and thus would have been +in the part of S removed in Trim(S, m). + +Corollary: no mount is forbidden by m in Trim(Trim(S, m), n). Indeed, +any such would have to have been forbidden by m in Trim(S, m), which +is impossible. + +Corollary: after + S = Trim(S, x_1) + S = Trim(S, x_2) + ... + S = Trim(S, x_k) +no mount remaining in S will be forbidden by either of x_1,...,x_k. + +The following will reduce S to its maximal non-shifting subset: + visited = {} + while S contains elements not belonging to visited + let m be an arbitrary such element of S + S = Trim(S, m) + add m to visited + +S never grows, so the number of elements of S not belonging to visited +decreases at least by one on each iteration. When the loop terminates, +all mounts remaining in S belong to visited. It's easy to see that at +the beginning of each iteration no mount remaining in S will be forbidden +by any element of visited. In other words, no mount remaining in S will +be forbidden, i.e. final value of S will be non-shifting. It will be +the maximal non-shifting subset, since we were removing only forbidden +elements. + + There are two difficulties in implementing the above in linear +time, both due to the fact that Trim() might need to remove more than one +element. Naive implementation of Trim() is vulnerable to running into a +long chain of mounts, each mounted on top of parent's root. Nothing in +that chain is forbidden, so nothing gets removed from it. We need to +recognize such chains and avoid walking them again on subsequent calls of +Trim(), otherwise we will end up with worst-case time being quadratic by +the number of elements in S. Another difficulty is in implementing the +outer loop - we need to iterate through all elements of a shrinking set. +That would be trivial if we never removed more than one element at a time +(linked list, with list_for_each_entry_safe for iterator), but we may +need to remove more than one entry, possibly including the ones we have +already visited. + + Let's start with naive algorithm for Trim(): + +Trim_one(m) + found = false + for each n in children(m) + if n not in S + found = true + if (mountpoint(n) != root(m)) + remove m from S + break + if found + Trim_ancestors(m) + +Trim_ancestors(m) + for (; parent(m) in S; m = parent(m)) { + if (mountpoint(m) != root(parent(m))) + remove parent(m) from S + } + +If m belongs to S, Trim_one(m) will replace S with Trim(S, m). +Proof: + Consider the chains excluding elements from Trim(S, m). The last +two elements in such chain are m and some child of m that does not belong +to S. If m has no such children, Trim(S, m) is equal to S. + m itself is removed if and only if the chain has exactly two +elements, i.e. when the last element does not overmount the root of m. +In other words, that happens when m has a child not in S that does not +overmount the root of m. + All other elements to remove will be ancestors of m, such that +the entire descent chain from them to m is contained in S. Let +(x_0, x_1, ..., x_k = m) be the longest such chain. x_i needs to be +removed if and only if x_{i+1} does not overmount its root. It's easy +to see that Trim_ancestors(m) will iterate through that chain from +x_k to x_1 and that it will remove exactly the elements that need to be +removed. + + Note that if the loop in Trim_ancestors() walks into an already +visited element, we are guaranteed that remaining iterations will see +only elements that had already been visited and remove none of them. +That's the weakness that makes it vulnerable to long chains of full +overmounts. + + It's easy to deal with, if we can afford setting marks on +elements of S; we would mark all elements already visited by +Trim_ancestors() and have it bail out as soon as it sees an already +marked element. + + The problems with iterating through the set can be dealt with in +several ways, depending upon the representation we choose for our set. +One useful observation is that we are given a closed subset in S - the +original set passed to propagate_umount(). Its elements can neither +forbid anything nor be forbidden by anything - all their descendents +belong to S, so they can not occur anywhere in any excluding chain. +In other words, the elements of that subset will remain in S until +the end and Trim_one(S, m) is a no-op for all m from that subset. + + That suggests keeping S as a disjoint union of a closed set U +('will be unmounted, no matter what') and the set of all elements of +S that do not belong to U. That set ('candidates') is all we need +to iterate through. Let's represent it as a subset in a cyclic list, +consisting of all list elements that are marked as candidates (initially - +all of them). Then we could have Trim_ancestors() only remove the mark, +leaving the elements on the list. Then Trim_one() would never remove +anything other than its argument from the containing list, allowing to +use list_for_each_entry_safe() as iterator. + + Assuming that representation we get the following: + + list_for_each_entry_safe(m, ..., Candidates, ...) + Trim_one(m) +where +Trim_one(m) + if (m is not marked as a candidate) + strip the "seen by Trim_ancestors" mark from m + remove m from the Candidates list + return + + remove_this = false + found = false + for each n in children(m) + if n not in S + found = true + if (mountpoint(n) != root(m)) + remove_this = true + break + if found + Trim_ancestors(m) + if remove_this + strip the "seen by Trim_ancestors" mark from m + strip the "candidate" mark from m + remove m from the Candidate list + +Trim_ancestors(m) + for (p = parent(m); p is marked as candidate ; m = p, p = parent(p)) { + if m is marked as seen by Trim_ancestors + return + mark m as seen by Trim_ancestors + if (mountpoint(m) != root(p)) + strip the "candidate" mark from p + } + + Terminating condition in the loop in Trim_ancestors() is correct, +since that that loop will never run into p belonging to U - p is always +an ancestor of argument of Trim_one() and since U is closed, the argument +of Trim_one() would also have to belong to U. But Trim_one() is never +called for elements of U. In other words, p belongs to S if and only +if it belongs to candidates. + + Time complexity: +* we get no more than O(#S) calls of Trim_one() +* the loop over children in Trim_one() never looks at the same child +twice through all the calls. +* iterations of that loop for children in S are no more than O(#S) +in the worst case +* at most two children that are not elements of S are considered per +call of Trim_one(). +* the loop in Trim_ancestors() sets its mark once per iteration and +no element of S has is set more than once. + + In the end we may have some elements excluded from S by +Trim_ancestors() still stuck on the list. We could do a separate +loop removing them from the list (also no worse than O(#S) time), +but it's easier to leave that until the next phase - there we will +iterate through the candidates anyway. + + The caller has already removed all elements of U from their parents' +lists of children, which means that checking if child belongs to S is +equivalent to checking if it's marked as a candidate; we'll never see +the elements of U in the loop over children in Trim_one(). + + What's more, if we see that children(m) is empty and m is not +locked, we can immediately move m into the committed subset (remove +from the parent's list of children, etc.). That's one fewer mount we'll +have to look into when we check the list of children of its parent *and* +when we get to building the non-revealing subset. + + Maximal non-revealing subsets + +If S is not a non-revealing subset, there is a locked element x in S +such that parent of x is not in S. + +Obviously, no non-revealing subset of S may contain x. Removing such +elements one by one will obviously end with the maximal non-revealing +subset (possibly empty one). Note that removal of an element will +require removal of all its locked children, etc. + +If the set had been non-shifting, it will remain non-shifting after +such removals. +Proof: suppose S was non-shifting, x is a locked element of S, parent of x +is not in S and S - {x} is not non-shifting. Then there is an element m +in S - {x} and a subtree mounted strictly inside m, such that m contains +an element not in in S - {x}. Since S is non-shifting, everything in +that subtree must belong to S. But that means that this subtree must +contain x somewhere *and* that parent of x either belongs that subtree +or is equal to m. Either way it must belong to S. Contradiction. + +// same representation as for finding maximal non-shifting subsets: +// S is a disjoint union of a non-revealing set U (the ones we are committed +// to unmount) and a set of candidates, represented as a subset of list +// elements that have "is a candidate" mark on them. +// Elements of U are removed from their parents' lists of children. +// In the end candidates becomes empty and maximal non-revealing non-shifting +// subset of S is now in U + while (Candidates list is non-empty) + handle_locked(first(Candidates)) + +handle_locked(m) + if m is not marked as a candidate + strip the "seen by Trim_ancestors" mark from m + remove m from the list + return + cutoff = m + for (p = m; p in candidates; p = parent(p)) { + strip the "seen by Trim_ancestors" mark from p + strip the "candidate" mark from p + remove p from the Candidates list + if (!locked(p)) + cutoff = parent(p) + } + if p in U + cutoff = p + while m != cutoff + remove m from children(parent(m)) + add m to U + m = parent(m) + +Let (x_0, ..., x_n = m) be the maximal chain of descent of m within S. +* If it contains some elements of U, let x_k be the last one of those. +Then union of U with {x_{k+1}, ..., x_n} is obviously non-revealing. +* otherwise if all its elements are locked, then none of {x_0, ..., x_n} +may be elements of a non-revealing subset of S. +* otherwise let x_k be the first unlocked element of the chain. Then none +of {x_0, ..., x_{k-1}} may be an element of a non-revealing subset of +S and union of U and {x_k, ..., x_n} is non-revealing. + +handle_locked(m) finds which of these cases applies and adjusts Candidates +and U accordingly. U remains non-revealing, union of Candidates and +U still contains any non-revealing subset of S and after the call of +handle_locked(m) m is guaranteed to be not in Candidates list. So having +it called for each element of S would suffice to empty Candidates, +leaving U the maximal non-revealing subset of S. + +However, handle_locked(m) is a no-op when m belongs to U, so it's enough +to have it called for elements of Candidates list until none remain. + +Time complexity: number of calls of handle_locked() is limited by +#Candidates, each iteration of the first loop in handle_locked() removes +an element from the list, so their total number of executions is also +limited by #Candidates; number of iterations in the second loop is no +greater than the number of iterations of the first loop. + + + Reparenting + +After we'd calculated the final set, we still need to deal with +reparenting - if an element of the final set has a child not in it, +we need to reparent such child. + +Such children can only be root-overmounting (otherwise the set wouldn't +be non-shifting) and their parents can not belong to the original set, +since the original is guaranteed to be closed. + + + Putting all of that together + +The plan is to + * find all candidates + * trim down to maximal non-shifting subset + * trim down to maximal non-revealing subset + * reparent anything that needs to be reparented + * return the resulting set to the caller + +For the 2nd and 3rd steps we want to separate the set into growing +non-revealing subset, initially containing the original set ("U" in +terms of the pseudocode above) and everything we are still not sure about +("candidates"). It means that for the output of the 1st step we'd like +the extra candidates separated from the stuff already in the original set. +For the 4th step we would like the additions to U separate from the +original set. + +So let's go for + * original set ("set"). Linkage via mnt_list + * undecided candidates ("candidates"). Subset of a list, +consisting of all its elements marked with a new flag (T_UMOUNT_CANDIDATE). +Initially all elements of the list will be marked that way; in the +end the list will become empty and no mounts will remain marked with +that flag. + * Reuse T_MARKED for "has been already seen by trim_ancestors()". + * anything in U that hadn't been in the original set - elements of +candidates will gradually be either discarded or moved there. In other +words, it's the candidates we have already decided to unmount. Its role +is reasonably close to the old "to_umount", so let's use that name. +Linkage via mnt_list. + +For gather_candidates() we'll need to maintain both candidates (S - +set) and intersection of S with set. Use T_UMOUNT_CANDIDATE for +all elements we encounter, putting the ones not already in the original +set into the list of candidates. When we are done, strip that flag from +all elements of the original set. That gives a cheap way to check +if element belongs to S (in gather_candidates) and to candidates +itself (at later stages). Call that predicate is_candidate(); it would +be m->mnt_t_flags & T_UMOUNT_CANDIDATE. + +All elements of the original set are marked with MNT_UMOUNT and we'll +need the same for elements added when joining the contents of to_umount +to set in the end. Let's set MNT_UMOUNT at the time we add an element +to to_umount; that's close to what the old 'umount_one' is doing, so +let's keep that name. It also gives us another predicate we need - +"belongs to union of set and to_umount"; will_be_unmounted() for now. + +Removals from the candidates list should strip both T_MARKED and +T_UMOUNT_CANDIDATE; call it remove_from_candidates_list(). diff --git a/Documentation/filesystems/ubifs-authentication.rst b/Documentation/filesystems/ubifs-authentication.rst index 3d85ee88719a..106bb9c056f6 100644 --- a/Documentation/filesystems/ubifs-authentication.rst +++ b/Documentation/filesystems/ubifs-authentication.rst @@ -443,6 +443,6 @@ References [DM-VERITY] https://www.kernel.org/doc/Documentation/device-mapper/verity.rst -[FSCRYPT-POLICY2] https://www.spinics.net/lists/linux-ext4/msg58710.html +[FSCRYPT-POLICY2] https://lore.kernel.org/r/20171023214058.128121-1-ebiggers3@gmail.com/ [UBIFS-WP] http://www.linux-mtd.infradead.org/doc/ubifs_whitepaper.pdf diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst index fd32a9a17bfb..486a91633474 100644 --- a/Documentation/filesystems/vfs.rst +++ b/Documentation/filesystems/vfs.rst @@ -515,8 +515,8 @@ As of kernel 2.6.22, the following members are defined: struct posix_acl * (*get_acl)(struct mnt_idmap *, struct dentry *, int); int (*set_acl)(struct mnt_idmap *, struct dentry *, struct posix_acl *, int); int (*fileattr_set)(struct mnt_idmap *idmap, - struct dentry *dentry, struct fileattr *fa); - int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa); + struct dentry *dentry, struct file_kattr *fa); + int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa); struct offset_ctx *(*get_offset_ctx)(struct inode *inode); }; @@ -758,8 +758,9 @@ process is more complicated and uses write_begin/write_end or dirty_folio to write data into the address_space, and writepages to writeback data to storage. -Adding and removing pages to/from an address_space is protected by the -inode's i_mutex. +Removing pages from an address_space requires holding the inode's i_rwsem +exclusively, while adding pages to the address_space requires holding the +inode's i_mapping->invalidate_lock exclusively. When data is written to a page, the PG_Dirty flag should be set. It typically remains set until writepages asks for it to be written. This @@ -822,10 +823,10 @@ cache in your filesystem. The following members are defined: int (*writepages)(struct address_space *, struct writeback_control *); bool (*dirty_folio)(struct address_space *, struct folio *); void (*readahead)(struct readahead_control *); - int (*write_begin)(struct file *, struct address_space *mapping, + int (*write_begin)(const struct kiocb *, struct address_space *mapping, loff_t pos, unsigned len, - struct page **pagep, void **fsdata); - int (*write_end)(struct file *, struct address_space *mapping, + struct page **pagep, void **fsdata); + int (*write_end)(const struct kiocb *, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct folio *folio, void *fsdata); sector_t (*bmap)(struct address_space *, sector_t); @@ -1071,12 +1072,14 @@ This describes how the VFS can manipulate an open file. As of kernel struct file_operations { struct module *owner; + fop_flags_t fop_flags; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); - int (*iopoll)(struct kiocb *kiocb, bool spin); + int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *, + unsigned int flags); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); @@ -1093,18 +1096,24 @@ This describes how the VFS can manipulate an open file. As of kernel int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); - int (*setlease)(struct file *, long, struct file_lock **, void **); + void (*splice_eof)(struct file *file); + int (*setlease)(struct file *, int, struct file_lease **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif - ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); + ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, + loff_t, size_t, unsigned int); loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, loff_t len, unsigned int remap_flags); int (*fadvise)(struct file *, loff_t, loff_t, int); + int (*uring_cmd)(struct io_uring_cmd *ioucmd, unsigned int issue_flags); + int (*uring_cmd_iopoll)(struct io_uring_cmd *, struct io_comp_batch *, + unsigned int poll_flags); + int (*mmap_prepare)(struct vm_area_desc *); }; Again, all methods are called without any locks being held, unless @@ -1144,7 +1153,8 @@ otherwise noted. used on 64 bit kernels. ``mmap`` - called by the mmap(2) system call + called by the mmap(2) system call. Deprecated in favour of + ``mmap_prepare``. ``open`` called by the VFS when an inode should be opened. When the VFS @@ -1221,6 +1231,11 @@ otherwise noted. ``fadvise`` possibly called by the fadvise64() system call. +``mmap_prepare`` + Called by the mmap(2) system call. Allows a VFS to set up a + file-backed memory mapping, most notably establishing relevant + private state and VMA callbacks. + Note that the file operations are implemented by the specific filesystem in which the inode resides. When opening a device node (character or block special) most filesystems will call special diff --git a/Documentation/firmware-guide/acpi/apei/einj.rst b/Documentation/firmware-guide/acpi/apei/einj.rst index c52b9da08fa9..7d8435d35a18 100644 --- a/Documentation/firmware-guide/acpi/apei/einj.rst +++ b/Documentation/firmware-guide/acpi/apei/einj.rst @@ -59,6 +59,9 @@ The following files belong to it: 0x00000200 Platform Correctable 0x00000400 Platform Uncorrectable non-fatal 0x00000800 Platform Uncorrectable fatal + V2_0x00000001 EINJV2 Processor Error + V2_0x00000002 EINJV2 Memory Error + V2_0x00000004 EINJV2 PCI Express Error ================ =================================== The format of the file contents are as above, except present are only @@ -88,6 +91,8 @@ The following files belong to it: Memory address and mask valid (param1 and param2). Bit 2 PCIe (seg,bus,dev,fn) valid (see param4 below). + Bit 3 + EINJv2 extension structure is valid If set to zero, legacy behavior is mimicked where the type of injection specifies just one bit set, and param1 is multiplexed. @@ -122,6 +127,13 @@ The following files belong to it: this actually works depends on what operations the BIOS actually includes in the trigger phase. +- component_id0 .. component_idN, component_syndrome0 .. component_syndromeN + + These files are used to set the "Component Array" field + of the EINJv2 Extension Structure. Each holds a 128-bit + hex value. Writing just a newline to any of these files + sets an invalid (all-ones) value. + CXL error types are supported from ACPI 6.5 onwards (given a CXL port is present). The EINJ user interface for CXL error types is at /cxl. The following files belong to it: @@ -194,6 +206,27 @@ An error injection example:: # echo 0x8 > error_type # Choose correctable memory error # echo 1 > error_inject # Inject now +An EINJv2 error injection example:: + + # cd /sys/kernel/debug/apei/einj + # cat available_error_type # See which errors can be injected + 0x00000002 Processor Uncorrectable non-fatal + 0x00000008 Memory Correctable + 0x00000010 Memory Uncorrectable non-fatal + V2_0x00000001 EINJV2 Processor Error + V2_0x00000002 EINJV2 Memory Error + + # echo 0x12345000 > param1 # Set memory address for injection + # echo 0xfffffffffffff000 > param2 # Range - anywhere in this page + # echo 0x1 > component_id0 # First device ID + # echo 0x4 > component_syndrome0 # First error syndrome + # echo 0x2 > component_id1 # Second device ID + # echo 0x4 > component_syndrome1 # Second error syndrome + # echo '' > component_id2 # Mark id2 invalid to terminate list + # echo V2_0x2 > error_type # Choose EINJv2 memory error + # echo 0xa > flags # set flags to indicate EINJv2 + # echo 1 > error_inject # Inject now + You should see something like this in dmesg:: [22715.830801] EDAC sbridge MC3: HANDLING MCE MEMORY ERROR diff --git a/Documentation/firmware-guide/acpi/gpio-properties.rst b/Documentation/firmware-guide/acpi/gpio-properties.rst index db0c0b1f3700..a0983b4213ea 100644 --- a/Documentation/firmware-guide/acpi/gpio-properties.rst +++ b/Documentation/firmware-guide/acpi/gpio-properties.rst @@ -6,7 +6,7 @@ _DSD Device Properties Related to GPIO With the release of ACPI 5.1, the _DSD configuration object finally allows names to be given to GPIOs (and other things as well) returned -by _CRS. Previously, we were only able to use an integer index to find +by _CRS. Previously we were only able to use an integer index to find the corresponding GPIO, which is pretty error prone (it depends on the _CRS output ordering, for example). @@ -49,11 +49,11 @@ index pin Pin in the GpioIo()/GpioInt() resource. Typically this is zero. active_low - If 1, the GPIO is marked as active_low. + If 1, the GPIO is marked as active-low. Since ACPI GpioIo() resource does not have a field saying whether it is -active low or high, the "active_low" argument can be used here. Setting -it to 1 marks the GPIO as active low. +active-low or active-high, the "active_low" argument can be used here. +Setting it to 1 marks the GPIO as active-low. Note, active_low in _DSD does not make sense for GpioInt() resource and must be 0. GpioInt() resource has its own means of defining it. @@ -92,8 +92,8 @@ and polarity settings. The table below shows the expectations: | | Low | as low, assuming active | +-------------+-------------+-----------------------------------------------+ -That said, for our above example the both GPIOs, since the bias setting -is explicit and _DSD is present, will be treated as active with a high +That said, for our above example, since the bias setting is explicit and +_DSD is present, both GPIOs will be treated as active with a high polarity and Linux will configure the pins in this state until a driver reprograms them differently. @@ -231,8 +231,8 @@ In those cases ACPI device identification objects, _HID, _CID, _CLS, _SUB, _HRV, available to the driver can be used to identify the device and that is supposed to be sufficient to determine the meaning and purpose of all of the GPIO lines listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words, -the driver is supposed to know what to use the GpioIo()/GpioInt() resources for -once it has identified the device. Having done that, it can simply assign names +the driver is supposed to know what to use from the GpioIo()/GpioInt() resources +for once it has identified the device. Having done that, it can simply assign names to the GPIO lines it is going to use and provide the GPIO subsystem with a mapping between those names and the ACPI GPIO resources corresponding to them. @@ -252,9 +252,9 @@ question would look like this:: static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false }; static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = { - { "reset-gpios", &reset_gpio, 1 }, - { "shutdown-gpios", &shutdown_gpio, 1 }, - { } + { "reset-gpios", &reset_gpio, 1 }, + { "shutdown-gpios", &shutdown_gpio, 1 }, + { } }; Next, the mapping table needs to be passed as the second argument to @@ -270,7 +270,7 @@ Using the _CRS fallback If a device does not have _DSD or the driver does not create ACPI GPIO mapping, the Linux GPIO framework refuses to return any GPIOs. This is -because the driver does not know what it actually gets. For example if we +because the driver does not know what it actually gets. For example, if we have a device like below:: Device (BTH) @@ -292,7 +292,7 @@ The driver might expect to get the right GPIO when it does:: ...error handling... but since there is no way to know the mapping between "reset" and -the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT). +the GpioIo() in _CRS the desc will hold ERR_PTR(-ENOENT). The driver author can solve this by passing the mapping explicitly (this is the recommended way and it's documented in the above chapter). @@ -318,15 +318,15 @@ Case 1:: desc = gpiod_get(dev, "non-null-connection-id", flags); desc = gpiod_get_index(dev, "non-null-connection-id", index, flags); +Case 1 assumes that corresponding ACPI device description must have +defined device properties and will prevent from getting any GPIO resources +otherwise. + Case 2:: desc = gpiod_get(dev, NULL, flags); desc = gpiod_get_index(dev, NULL, index, flags); -Case 1 assumes that corresponding ACPI device description must have -defined device properties and will prevent to getting any GPIO resources -otherwise. - Case 2 explicitly tells GPIO core to look for resources in _CRS. Be aware that gpiod_get_index() in cases 1 and 2, assuming that there diff --git a/Documentation/firmware-guide/acpi/i2c-muxes.rst b/Documentation/firmware-guide/acpi/i2c-muxes.rst index 3a8997ccd7c4..f366539acd79 100644 --- a/Documentation/firmware-guide/acpi/i2c-muxes.rst +++ b/Documentation/firmware-guide/acpi/i2c-muxes.rst @@ -14,7 +14,7 @@ Consider this topology:: | | | 0x70 |--CH01--> i2c client B (0x50) +------+ +------+ -which corresponds to the following ASL:: +which corresponds to the following ASL (in the scope of \_SB):: Device (SMB1) { @@ -24,7 +24,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^SMB1", 0x00, + AddressingMode7Bit, "\\_SB.SMB1", 0x00, ResourceConsumer,,) } @@ -37,7 +37,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^CH00", 0x00, + AddressingMode7Bit, "\\_SB.SMB1.CH00", 0x00, ResourceConsumer,,) } } @@ -52,7 +52,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^CH01", 0x00, + AddressingMode7Bit, "\\_SB.SMB1.CH01", 0x00, ResourceConsumer,,) } } diff --git a/Documentation/gpu/amdgpu/amd-hardware-list-info.rst b/Documentation/gpu/amdgpu/amd-hardware-list-info.rst index 1786544fe7c1..e72f4ff770c4 100644 --- a/Documentation/gpu/amdgpu/amd-hardware-list-info.rst +++ b/Documentation/gpu/amdgpu/amd-hardware-list-info.rst @@ -10,7 +10,7 @@ Accelerated Processing Units (APU) Info .. csv-table:: :header-rows: 1 - :widths: 3, 2, 2, 1, 1, 1, 1 + :widths: 3, 2, 2, 1, 1, 1, 1, 1 :file: ./apu-asic-info-table.csv Discrete GPU Info @@ -18,6 +18,6 @@ Discrete GPU Info .. csv-table:: :header-rows: 1 - :widths: 3, 2, 2, 1, 1, 1 + :widths: 3, 2, 2, 1, 1, 1, 1, 1 :file: ./dgpu-asic-info-table.csv diff --git a/Documentation/gpu/amdgpu/apu-asic-info-table.csv b/Documentation/gpu/amdgpu/apu-asic-info-table.csv index 1d50b539677f..b479c5629146 100644 --- a/Documentation/gpu/amdgpu/apu-asic-info-table.csv +++ b/Documentation/gpu/amdgpu/apu-asic-info-table.csv @@ -1,17 +1,17 @@ -Product Name, Code Reference, DCN/DCE version, GC version, VCE/UVD/VCN version, SDMA version, MP0 version -Radeon R* Graphics, CARRIZO/STONEY, DCE 11, 8, VCE 3 / UVD 6, 3, n/a -Ryzen 3000 series / AMD Ryzen Embedded V1*/R1* with Radeon Vega Gfx, RAVEN/PICASSO, DCN 1.0, 9.1.0, VCN 1.0, 4.1.0, 10.0.0 -Ryzen 4000 series, RENOIR, DCN 2.1, 9.3, VCN 2.2, 4.1.2, 11.0.3 -Ryzen 3000 series / AMD Ryzen Embedded V1*/R1* with Radeon Vega Gfx, RAVEN2, DCN 1.0, 9.2.2, VCN 1.0.1, 4.1.1, 10.0.1 -SteamDeck, VANGOGH, DCN 3.0.1, 10.3.1, VCN 3.1.0, 5.2.1, 11.5.0 -Ryzen 5000 series / Ryzen 7x30 series, GREEN SARDINE / Cezanne / Barcelo / Barcelo-R, DCN 2.1, 9.3, VCN 2.2, 4.1.1, 12.0.1 -Ryzen 6000 series / Ryzen 7x35 series / Ryzen 7x36 series, YELLOW CARP / Rembrandt / Rembrandt-R, 3.1.2, 10.3.3, VCN 3.1.1, 5.2.3, 13.0.3 -Ryzen 7000 series (AM5), Raphael, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5 -Ryzen 9000 series (AM5), Granite Ridge, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5 -Ryzen 7x45 series (FL1), Dragon Range, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5 -Ryzen 7x20 series, Mendocino, 3.1.6, 10.3.7, 3.1.1, 5.2.7, 13.0.8 -Ryzen 7x40 series, Phoenix, 3.1.4, 11.0.1 / 11.0.4, 4.0.2, 6.0.1, 13.0.4 / 13.0.11 -Ryzen 8x40 series, Hawk Point, 3.1.4, 11.0.1 / 11.0.4, 4.0.2, 6.0.1, 13.0.4 / 13.0.11 -Ryzen AI 300 series, Strix Point, 3.5.0, 11.5.0, 4.0.5, 6.1.0, 14.0.0 -Ryzen AI 350 series, Krackan Point, 3.5.0, 11.5.2, 4.0.5, 6.1.2, 14.0.4 -Ryzen AI Max 300 series, Strix Halo, 3.5.1, 11.5.1, 4.0.6, 6.1.1, 14.0.1 +Product Name, Code Reference, DCN/DCE version, GC version, VCE/UVD/VCN version, SDMA version, MP0 version, MP1 version +Radeon R* Graphics, CARRIZO/STONEY, DCE 11, 8, VCE 3 / UVD 6, 3, n/a, 8 +Ryzen 3000 series / AMD Ryzen Embedded V1*/R1* with Radeon Vega Gfx, RAVEN/PICASSO, DCN 1.0, 9.1.0, VCN 1.0, 4.1.0, 10.0.0, 10.0.0 +Ryzen 4000 series, RENOIR, DCN 2.1, 9.3, VCN 2.2, 4.1.2, 11.0.3, 12.0.1 +Ryzen 3000 series / AMD Ryzen Embedded V1*/R1* with Radeon Vega Gfx, RAVEN2, DCN 1.0, 9.2.2, VCN 1.0.1, 4.1.1, 10.0.1, 10.0.1 +SteamDeck, VANGOGH, DCN 3.0.1, 10.3.1, VCN 3.1.0, 5.2.1, 11.5.0, 11.5.0 +Ryzen 5000 series / Ryzen 7x30 series, GREEN SARDINE / Cezanne / Barcelo / Barcelo-R, DCN 2.1, 9.3, VCN 2.2, 4.1.1, 12.0.1, 12.0.1 +Ryzen 6000 series / Ryzen 7x35 series / Ryzen 7x36 series, YELLOW CARP / Rembrandt / Rembrandt-R, 3.1.2, 10.3.3, VCN 3.1.1, 5.2.3, 13.0.3, 13.0.3 +Ryzen 7000 series (AM5), Raphael, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5, 13.0.5 +Ryzen 9000 series (AM5), Granite Ridge, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5, 13.0.5 +Ryzen 7x45 series (FL1), Dragon Range, 3.1.5, 10.3.6, 3.1.2, 5.2.6, 13.0.5, 13.0.5 +Ryzen 7x20 series, Mendocino, 3.1.6, 10.3.7, 3.1.1, 5.2.7, 13.0.8, 13.0.8 +Ryzen 7x40 series, Phoenix, 3.1.4, 11.0.1 / 11.0.4, 4.0.2, 6.0.1, 13.0.4 / 13.0.11, 13.0.4 / 13.0.11 +Ryzen 8x40 series, Hawk Point, 3.1.4, 11.0.1 / 11.0.4, 4.0.2, 6.0.1, 13.0.4 / 13.0.11, 13.0.4 / 13.0.11 +Ryzen AI 300 series, Strix Point, 3.5.0, 11.5.0, 4.0.5, 6.1.0, 14.0.0, 14.0.0 +Ryzen AI 350 series, Krackan Point, 3.5.0, 11.5.2, 4.0.5, 6.1.2, 14.0.4, 14.0.4 +Ryzen AI Max 300 series, Strix Halo, 3.5.1, 11.5.1, 4.0.6, 6.1.1, 14.0.1, 14.0.1 diff --git a/Documentation/gpu/amdgpu/debugfs.rst b/Documentation/gpu/amdgpu/debugfs.rst index 5150d0a95658..151d8bfc79e2 100644 --- a/Documentation/gpu/amdgpu/debugfs.rst +++ b/Documentation/gpu/amdgpu/debugfs.rst @@ -94,7 +94,7 @@ amdgpu_error_ ------------------- Provides an interface to set an error code on the dma fences associated with -ring . The error code specified is propogated to all fences associated +ring . The error code specified is propagated to all fences associated with the ring. Use this to inject a fence error into a ring. amdgpu_pm_info @@ -165,7 +165,7 @@ GTT memory. amdgpu_regs_* ------------- -Provides direct access to various register aperatures on the GPU. Used +Provides direct access to various register apertures on the GPU. Used by tools like UMR to access GPU registers. amdgpu_regs2 diff --git a/Documentation/gpu/amdgpu/dgpu-asic-info-table.csv b/Documentation/gpu/amdgpu/dgpu-asic-info-table.csv index d2f10ee69dfc..bfd44c6e052a 100644 --- a/Documentation/gpu/amdgpu/dgpu-asic-info-table.csv +++ b/Documentation/gpu/amdgpu/dgpu-asic-info-table.csv @@ -1,28 +1,30 @@ -Product Name, Code Reference, DCN/DCE version, GC version, VCN version, SDMA version -AMD Radeon (TM) HD 8500M/ 8600M /M200 /M320 /M330 /M335 Series, HAINAN, --, 6, --, -- -AMD Radeon HD 7800 /7900 /FireGL Series, TAHITI, DCE 6, 6, VCE 1 / UVD 3, -- -AMD Radeon R7 (TM|HD) M265 /M370 /8500M /8600 /8700 /8700M, OLAND, DCE 6, 6, VCE 1 / UVD 3, -- -AMD Radeon (TM) (HD|R7) 7800 /7970 /8800 /8970 /370/ Series, PITCAIRN, DCE 6, 6, VCE 1 / UVD 3, -- -AMD Radeon (TM|R7|R9|HD) E8860 /M360 /7700 /7800 /8800 /9000(M) /W4100 Series, VERDE, DCE 6, 6, VCE 1 / UVD 3, -- -AMD Radeon HD M280X /M380 /7700 /8950 /W5100, BONAIRE, DCE 8, 7, VCE 2 / UVD 4.2, 1 -AMD Radeon (R9|TM) 200 /390 /W8100 /W9100 Series, HAWAII, DCE 8, 7, VCE 2 / UVD 4.2, 1 -AMD Radeon (TM) R(5|7) M315 /M340 /M360, TOPAZ, *, 8, --, 2 -AMD Radeon (TM) R9 200 /380 /W7100 /S7150 /M390 /M395 Series, TONGA, DCE 10, 8, VCE 3 / UVD 5, 3 -AMD Radeon (FirePro) (TM) R9 Fury Series, FIJI, DCE 10, 8, VCE 3 / UVD 6, 3 -Radeon RX 470 /480 /570 /580 /590 Series - AMD Radeon (TM) (Pro WX) 5100 /E9390 /E9560 /E9565 /V7350 /7100 /P30PH, POLARIS10, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3 -Radeon (TM) (RX|Pro WX) E9260 /460 /V5300X /550 /560(X) Series, POLARIS11, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3 -Radeon (RX/Pro) 500 /540(X) /550 /640 /WX2100 /WX3100 /WX200 Series, POLARIS12, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3 -Radeon (RX|TM) (PRO|WX) Vega /MI25 /V320 /V340L /8200 /9100 /SSG MxGPU, VEGA10, DCE 12, 9.0.1, VCE 4.0.0 / UVD 7.0.0, 4.0.0 -AMD Radeon (Pro) VII /MI50 /MI60, VEGA20, DCE 12, 9.4.0, VCE 4.1.0 / UVD 7.2.0, 4.2.0 -MI100, ARCTURUS, *, 9.4.1, VCN 2.5.0, 4.2.2 -MI200 Series, ALDEBARAN, *, 9.4.2, VCN 2.6.0, 4.4.0 -MI300 Series, AQUA_VANJARAM, *, 9.4.3, VCN 4.0.3, 4.4.2 -AMD Radeon (RX|Pro) 5600(M|XT) /5700 (M|XT|XTB) /W5700, NAVI10, DCN 2.0.0, 10.1.10, VCN 2.0.0, 5.0.0 -AMD Radeon (Pro) 5300 /5500XTB/5500(XT|M) /W5500M /W5500, NAVI14, DCN 2.0.0, 10.1.1, VCN 2.0.2, 5.0.2 -AMD Radeon RX 6800(XT) /6900(XT) /W6800, SIENNA_CICHLID, DCN 3.0.0, 10.3.0, VCN 3.0.0, 5.2.0 -AMD Radeon RX 6700 XT / 6800M / 6700M, NAVY_FLOUNDER, DCN 3.0.0, 10.3.2, VCN 3.0.0, 5.2.2 -AMD Radeon RX 6600(XT) /6600M /W6600 /W6600M, DIMGREY_CAVEFISH, DCN 3.0.2, 10.3.4, VCN 3.0.16, 5.2.4 -AMD Radeon RX 6500M /6300M /W6500M /W6300M, BEIGE_GOBY, DCN 3.0.3, 10.3.5, VCN 3.0.33, 5.2.5 -AMD Radeon RX 7900 XT /XTX, , DCN 3.2.0, 11.0.0, VCN 4.0.0, 6.0.0 -AMD Radeon RX 7800 XT, , DCN 3.2.0, 11.0.3, VCN 4.0.0, 6.0.3 -AMD Radeon RX 7600M (XT) /7700S /7600S, , DCN 3.2.1, 11.0.2, VCN 4.0.4, 6.0.2 +Product Name, Code Reference, DCN/DCE version, GC version, VCN version, SDMA version, MP0 version, MP1 version +AMD Radeon (TM) HD 8500M/ 8600M /M200 /M320 /M330 /M335 Series, HAINAN, --, 6, --, --, --, 6 +AMD Radeon HD 7800 /7900 /FireGL Series, TAHITI, DCE 6, 6, VCE 1 / UVD 3, --, --, 6 +AMD Radeon R7 (TM|HD) M265 /M370 /8500M /8600 /8700 /8700M, OLAND, DCE 6, 6, -- / UVD 3, --, --, 6 +AMD Radeon (TM) (HD|R7) 7800 /7970 /8800 /8970 /370/ Series, PITCAIRN, DCE 6, 6, VCE 1 / UVD 3, --, --, 6 +AMD Radeon (TM|R7|R9|HD) E8860 /M360 /7700 /7800 /8800 /9000(M) /W4100 Series, VERDE, DCE 6, 6, VCE 1 / UVD 3, --, --, 6 +AMD Radeon HD M280X /M380 /7700 /8950 /W5100, BONAIRE, DCE 8, 7, VCE 2 / UVD 4.2, 1, --, 7 +AMD Radeon (R9|TM) 200 /390 /W8100 /W9100 Series, HAWAII, DCE 8, 7, VCE 2 / UVD 4.2, 1, --, 7 +AMD Radeon (TM) R(5|7) M315 /M340 /M360, TOPAZ, *, 8, --, 2, n/a, 7 +AMD Radeon (TM) R9 200 /380 /W7100 /S7150 /M390 /M395 Series, TONGA, DCE 10, 8, VCE 3 / UVD 5, 3, n/a, 7 +AMD Radeon (FirePro) (TM) R9 Fury Series, FIJI, DCE 10, 8, VCE 3 / UVD 6, 3, n/a, 7 +Radeon RX 470 /480 /570 /580 /590 Series - AMD Radeon (TM) (Pro WX) 5100 /E9390 /E9560 /E9565 /V7350 /7100 /P30PH, POLARIS10, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3, n/a, 7 +Radeon (TM) (RX|Pro WX) E9260 /460 /V5300X /550 /560(X) Series, POLARIS11, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3, n/a, 7 +Radeon (RX/Pro) 500 /540(X) /550 /640 /WX2100 /WX3100 /WX200 Series, POLARIS12, DCE 11.2, 8, VCE 3.4 / UVD 6.3, 3, n/a, 7 +Radeon (RX|TM) (PRO|WX) Vega /MI25 /V320 /V340L /8200 /9100 /SSG MxGPU, VEGA10, DCE 12, 9.0.1, VCE 4.0.0 / UVD 7.0.0, 4.0.0, 9.0.0, 9.0.0 +AMD Radeon (Pro) VII /MI50 /MI60, VEGA20, DCE 12, 9.4.0, VCE 4.1.0 / UVD 7.2.0, 4.2.0, 11.0.2, 11.0.2 +MI100, ARCTURUS, *, 9.4.1, VCN 2.5.0, 4.2.2, 11.0.4, 11.0.2 +MI200 Series, ALDEBARAN, *, 9.4.2, VCN 2.6.0, 4.4.0, 13.0.2, 13.0.2 +MI300 Series, AQUA_VANJARAM, *, 9.4.3, VCN 4.0.3, 4.4.2, 13.0.6, 13.0.6 +AMD Radeon (RX|Pro) 5600(M|XT) /5700 (M|XT|XTB) /W5700, NAVI10, DCN 2.0.0, 10.1.10, VCN 2.0.0, 5.0.0, 11.0.0, 11.0.0 +AMD Radeon (Pro) 5300 /5500XTB/5500(XT|M) /W5500M /W5500, NAVI14, DCN 2.0.0, 10.1.1, VCN 2.0.2, 5.0.2, 11.0.5, 11.0.5 +AMD Radeon RX 6800(XT) /6900(XT) /W6800, SIENNA_CICHLID, DCN 3.0.0, 10.3.0, VCN 3.0.0, 5.2.0, 11.0.7, 11.0.7 +AMD Radeon RX 6700 XT / 6800M / 6700M, NAVY_FLOUNDER, DCN 3.0.0, 10.3.2, VCN 3.0.0, 5.2.2, 11.0.11, 11.0.11 +AMD Radeon RX 6600(XT) /6600M /W6600 /W6600M, DIMGREY_CAVEFISH, DCN 3.0.2, 10.3.4, VCN 3.0.16, 5.2.4, 11.0.12, 11.0.12 +AMD Radeon RX 6500M /6300M /W6500M /W6300M, BEIGE_GOBY, DCN 3.0.3, 10.3.5, VCN 3.0.33, 5.2.5, 11.0.13, 11.0.13 +AMD Radeon RX 7900 XT /XTX, , DCN 3.2.0, 11.0.0, VCN 4.0.0, 6.0.0, 13.0.0, 13.0.0 +AMD Radeon RX 7800 XT, , DCN 3.2.0, 11.0.3, VCN 4.0.0, 6.0.3, 13.0.10, 13.0.10 +AMD Radeon RX 7600M (XT) /7700S /7600S, , DCN 3.2.1, 11.0.2, VCN 4.0.4, 6.0.2, 13.0.7, 13.0.7 +AMD Radeon RX 9070 (XT), , DCN 4.0.1, 12.0.1, VCN 5.0.0, 7.0.1, 14.0.3, 14.0.3 +AMD Radeon RX 9060 XT, , DCN 4.0.1, 12.0.0, VCN 5.0.0, 7.0.0, 14.0.2, 14.0.2 diff --git a/Documentation/gpu/amdgpu/display/dc-glossary.rst b/Documentation/gpu/amdgpu/display/dc-glossary.rst index 7dc034e9e586..cbe737d1fcea 100644 --- a/Documentation/gpu/amdgpu/display/dc-glossary.rst +++ b/Documentation/gpu/amdgpu/display/dc-glossary.rst @@ -5,7 +5,7 @@ DC Glossary On this page, we try to keep track of acronyms related to the display component. If you do not find what you are looking for, look at the 'Documentation/gpu/amdgpu/amdgpu-glossary.rst'; if you cannot find it anywhere, -consider asking in the amdgfx and update this page. +consider asking on the amd-gfx mailing list and update this page. .. glossary:: diff --git a/Documentation/gpu/amdgpu/display/display-contributing.rst b/Documentation/gpu/amdgpu/display/display-contributing.rst index 36f3077eee00..2f741c52dce5 100644 --- a/Documentation/gpu/amdgpu/display/display-contributing.rst +++ b/Documentation/gpu/amdgpu/display/display-contributing.rst @@ -9,8 +9,8 @@ contribution to the display code, and for that, we say thank you :) This page summarizes some of the issues you can help with; keep in mind that this is a static page, and it is always a good idea to try to reach developers -in the amdgfx or some of the maintainers. Finally, this page follows the DRM -way of creating a TODO list; for more information, check +on the amd-gfx mailing list or some of the maintainers. Finally, this page +follows the DRM way of creating a TODO list; for more information, check 'Documentation/gpu/todo.rst'. Gitlab issues diff --git a/Documentation/gpu/amdgpu/display/programming-model-dcn.rst b/Documentation/gpu/amdgpu/display/programming-model-dcn.rst index c1b48d49fb0b..bc7de97a746f 100644 --- a/Documentation/gpu/amdgpu/display/programming-model-dcn.rst +++ b/Documentation/gpu/amdgpu/display/programming-model-dcn.rst @@ -100,7 +100,7 @@ represents the connected display. For historical reasons, we used the name `dc_link`, which gives the wrong impression that this abstraction only deals with physical connections that the developer can easily manipulate. However, this also covers - conections like eDP or cases where the output is connected to other devices. + connections like eDP or cases where the output is connected to other devices. There are two structs that are not represented in the diagram since they were elaborated in the DCN overview page (check the DCN block diagram :ref:`Display diff --git a/Documentation/gpu/amdgpu/driver-core.rst b/Documentation/gpu/amdgpu/driver-core.rst index 81256318e93c..bd4be32f2725 100644 --- a/Documentation/gpu/amdgpu/driver-core.rst +++ b/Documentation/gpu/amdgpu/driver-core.rst @@ -65,7 +65,7 @@ SDMA (System DMA) GC (Graphics and Compute) This is the graphics and compute engine, i.e., the block that - encompasses the 3D pipeline and and shader blocks. This is by far the + encompasses the 3D pipeline and shader blocks. This is by far the largest block on the GPU. The 3D pipeline has tons of sub-blocks. In addition to that, it also contains the CP microcontrollers (ME, PFP, CE, MEC) and the RLC microcontroller. It's exposed to userspace for user mode diff --git a/Documentation/gpu/amdgpu/process-isolation.rst b/Documentation/gpu/amdgpu/process-isolation.rst index 6b6d70e357a7..25b06ffefc33 100644 --- a/Documentation/gpu/amdgpu/process-isolation.rst +++ b/Documentation/gpu/amdgpu/process-isolation.rst @@ -26,7 +26,7 @@ Example of enabling enforce isolation on a GPU with multiple partitions: $ cat /sys/class/drm/card0/device/enforce_isolation 1 0 1 0 -The output indicates that enforce isolation is enabled on zeroth and second parition and disabled on first and fourth parition. +The output indicates that enforce isolation is enabled on zeroth and second partition and disabled on first and third partition. For devices with a single partition or those that do not support partitions, there will be only one element: diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 843facf01b2d..d98428a592f1 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -418,13 +418,12 @@ needed. Recovery -------- -Current implementation defines three recovery methods, out of which, drivers +Current implementation defines four recovery methods, out of which, drivers can use any one, multiple or none. Method(s) of choice will be sent in the uevent environment as ``WEDGED=[,..,]`` in order of less to -more side-effects. If driver is unsure about recovery or method is unknown -(like soft/hard system reboot, firmware flashing, physical device replacement -or any other procedure which can't be attempted on the fly), ``WEDGED=unknown`` -will be sent instead. +more side-effects. See the section `Vendor Specific Recovery`_ +for ``WEDGED=vendor-specific``. If driver is unsure about recovery or +method is unknown, ``WEDGED=unknown`` will be sent instead. Userspace consumers can parse this event and attempt recovery as per the following expectations. @@ -435,6 +434,7 @@ following expectations. none optional telemetry collection rebind unbind + bind driver bus-reset unbind + bus reset/re-enumeration + bind + vendor-specific vendor specific recovery method unknown consumer policy =============== ======================================== @@ -446,6 +446,35 @@ telemetry information (devcoredump, syslog). This is useful because the first hang is usually the most critical one which can result in consequential hangs or complete wedging. + +Vendor Specific Recovery +------------------------ + +When ``WEDGED=vendor-specific`` is sent, it indicates that the device requires +a recovery procedure specific to the hardware vendor and is not one of the +standardized approaches. + +``WEDGED=vendor-specific`` may be used to indicate different cases within a +single vendor driver, each requiring a distinct recovery procedure. +In such scenarios, the vendor driver must provide comprehensive documentation +that describes each case, include additional hints to identify specific case and +outline the corresponding recovery procedure. The documentation includes: + +Case - A list of all cases that sends the ``WEDGED=vendor-specific`` recovery method. + +Hints - Additional Information to assist the userspace consumer in identifying and +differentiating between different cases. This can be exposed through sysfs, debugfs, +traces, dmesg etc. + +Recovery Procedure - Clear instructions and guidance for recovering each case. +This may include userspace scripts, tools needed for the recovery procedure. + +It is the responsibility of the admin/userspace consumer to identify the case and +verify additional identification hints before attempting a recovery procedure. + +Example: If the device uses the Xe driver, then userspace consumer should refer to +:ref:`Xe Device Wedging ` for the detailed documentation. + Task information ---------------- @@ -472,8 +501,12 @@ erroring out, all device memory should be unmapped and file descriptors should be closed to prevent leaks or undefined behaviour. The idea here is to clear the device of all user context beforehand and set the stage for a clean recovery. -Example -------- +For ``WEDGED=vendor-specific`` recovery method, it is the responsibility of the +consumer to check the driver documentation and the usecase before attempting +a recovery. + +Example - rebind +---------------- Udev rule:: diff --git a/Documentation/gpu/nouveau.rst b/Documentation/gpu/nouveau.rst index b8c801e0068c..cab2e81013bc 100644 --- a/Documentation/gpu/nouveau.rst +++ b/Documentation/gpu/nouveau.rst @@ -25,7 +25,7 @@ providing a consistent API to upper layers of the driver stack. GSP Support ------------------------ -.. kernel-doc:: drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c +.. kernel-doc:: drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c :doc: GSP message queue element .. kernel-doc:: drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index be8637da3fe9..92db80793bba 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -497,19 +497,19 @@ Contact: Douglas Anderson Level: Intermediate -Transition away from using mipi_dsi_*_write_seq() -------------------------------------------------- +Transition away from using deprecated MIPI DSI functions +-------------------------------------------------------- -The macros mipi_dsi_generic_write_seq() and mipi_dsi_dcs_write_seq() are -non-intuitive because, if there are errors, they return out of the *caller's* -function. We should move all callers to use mipi_dsi_generic_write_seq_multi() -and mipi_dsi_dcs_write_seq_multi() macros instead. +There are many functions defined in ``drm_mipi_dsi.c`` which have been +deprecated. Each deprecated function was deprecated in favor of its `multi` +variant (e.g. `mipi_dsi_generic_write()` and `mipi_dsi_generic_write_multi()`). +The `multi` variant of a function includes improved error handling and logic +which makes it more convenient to make several calls in a row, as most MIPI +drivers do. -Once all callers are transitioned, the macros and the functions that they call, -mipi_dsi_generic_write_chatty() and mipi_dsi_dcs_write_buffer_chatty(), can -probably be removed. Alternatively, if people feel like the _multi() variants -are overkill for some use cases, we could keep the mipi_dsi_*_write_seq() -variants but change them not to return out of the caller. +Drivers should be updated to use undeprecated functions. Once all usages of the +deprecated MIPI DSI functions have been removed, their definitions may be +removed from ``drm_mipi_dsi.c``. Contact: Douglas Anderson diff --git a/Documentation/gpu/xe/index.rst b/Documentation/gpu/xe/index.rst index 42ba6c263cd0..88b22fad880e 100644 --- a/Documentation/gpu/xe/index.rst +++ b/Documentation/gpu/xe/index.rst @@ -25,5 +25,6 @@ DG2, etc is provided to prototype the driver. xe_tile xe_debugging xe_devcoredump + xe_device xe-drm-usage-stats.rst xe_configfs diff --git a/Documentation/gpu/xe/xe_device.rst b/Documentation/gpu/xe/xe_device.rst new file mode 100644 index 000000000000..39a937b97cd3 --- /dev/null +++ b/Documentation/gpu/xe/xe_device.rst @@ -0,0 +1,10 @@ +.. SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +.. _xe-device-wedging: + +================== +Xe Device Wedging +================== + +.. kernel-doc:: drivers/gpu/drm/xe/xe_device.c + :doc: Xe Device Wedging diff --git a/Documentation/gpu/xe/xe_pcode.rst b/Documentation/gpu/xe/xe_pcode.rst index 5937ef3599b0..2a43601123cb 100644 --- a/Documentation/gpu/xe/xe_pcode.rst +++ b/Documentation/gpu/xe/xe_pcode.rst @@ -13,9 +13,11 @@ Internal API .. kernel-doc:: drivers/gpu/drm/xe/xe_pcode.c :internal: +.. _xe-survivability-mode: + ================== -Boot Survivability +Survivability Mode ================== .. kernel-doc:: drivers/gpu/drm/xe/xe_survivability_mode.c - :doc: Xe Boot Survivability + :doc: Survivability Mode diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst index dc9250787fc5..8b378c57b5aa 100644 --- a/Documentation/hid/intel-thc-hid.rst +++ b/Documentation/hid/intel-thc-hid.rst @@ -188,6 +188,34 @@ Control register. Reset line is controlled by BIOS (or EFI) through ACPI _RST method, driver needs to call this device ACPI _RST method to reset touch IC during initialization. +2.3 Max input size control +-------------------------- + +This is a new feature introduced in Panther Lake platform, THC hardware allows driver to set +a max input size for RxDMA. After this max size gets set and enabled, for every input report +packet reading, THC hardware sequencer will first read incoming input packet size, then compare +input packet size with the given max size: + +- if input packet size <= max size, THC continues using input packet size to finish the reading +- if input packet size > max size, there is potential input data crash risk during + transferring, THC will use max size instead of input packet size for reading + +This feature is used to avoid data corruption which will cause RxDMA buffer overrun issue for +I2C bus, and enhance whole system stability. + +2.4 Interrupt delay +------------------- + +Because of MCU performance limitation, some touch devices cannot de-assert interrupt pin +immediately after input data is transferred, which cause an interrupt toggle delay. But THC +always detects next interrupt immediately after last input interrupt is handled. In this +case, the delayed interrupt de-assertion will be recognized as a new interrupt signal by THC, +and causes THC to start an input report reading spuriously. + +In order to avoid this situation, THC introduced interrupt delay new feature in Panther Lake +platform, where THC allows driver to set an interrupt delay. After this feature is enabled, +THC will delay this given time for next interrupt detection. + 3. High level concept ===================== diff --git a/Documentation/hwmon/adp1050.rst b/Documentation/hwmon/adp1050.rst index 8fa937064886..32514084fbdc 100644 --- a/Documentation/hwmon/adp1050.rst +++ b/Documentation/hwmon/adp1050.rst @@ -13,6 +13,32 @@ Supported chips: Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADP1050.pdf + * Analog Devices ADP1051 + + Prefix: 'adp1051' + + Addresses scanned: I2C 0x70 - 0x77 + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADP1051.pdf + + * Analog Devices ADP1055 + + Prefix: 'adp1055' + + Addresses scanned: I2C 0x4B - 0x77 + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADP1055.pdf + + * Analog Devices LTP8800-1A/-2/-4A + + Prefix: 'ltp8800' + + Addresses scanned: - + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/LTP8800-1A.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/LTP8800-2.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/LTP8800-4A.pdf + Authors: - Radu Sabau @@ -21,14 +47,17 @@ Authors: Description ----------- -This driver supprts hardware monitoring for Analog Devices ADP1050 Digital -Controller for Isolated Power Supply with PMBus interface. +This driver supports hardware monitoring for Analog Devices ADP1050, ADP1051, +and ADP1055 Digital Controller for Isolated Power Supply with PMBus interface, +and the LTP8800 step-down μModule regulators. -The ADP1050 is an advanced digital controller with a PMBus™ +The ADP1050, ADP1051, and ADP1055 are advanced digital controllers with PMBus™ interface targeting high density, high efficiency dc-to-dc power -conversion used to monitor system temperatures, voltages and currents. -Through the PMBus interface, the device can monitor input/output voltages, -input current and temperature. +conversion used to monitor system temperatures, voltages and currents. The +LTP8800 is a family of step-down μModule regulators that provides microprocessor +core voltage from 54V power distribution architecture. Through the PMBus +interface, the device can monitor input/output voltages, input current and +temperature. Usage Notes ----------- @@ -49,16 +78,46 @@ Sysfs Attributes in1_label "vin" in1_input Measured input voltage in1_alarm Input voltage alarm +in1_crit Critical maximum input voltage +in1_crit_alarm Input voltage high alarm +in1_lcrit Critical minimum input voltage +in1_lcrit_alarm Input voltage critical low alarm in2_label "vout1" in2_input Measured output voltage in2_crit Critical maximum output voltage in2_crit_alarm Output voltage high alarm in2_lcrit Critical minimum output voltage in2_lcrit_alarm Output voltage critical low alarm +in2_max Critical maximum output voltage +in2_max_alarm Output voltage critical max alarm +in2_min Critical minimum output voltage +in2_min_alarm Output voltage critical min alarm curr1_label "iin" curr1_input Measured input current. curr1_alarm Input current alarm +curr1_crit Critical maximum input current +curr1_crit_alarm Input current high alarm +curr2_label "iout1" +curr2_input Measured output current +curr2_alarm Output current alarm +curr2_crit Critical maximum output current +curr2_crit_alarm Output current high alarm +curr2_lcrit Critical minimum output current +curr2_lcrit_alarm Output current critical low alarm +curr2_max Critical maximum output current +curr2_max_alarm Output current critical max alarm +power1_label "pout1" +power1_input Measured output power +power1_crit Critical maximum output power +power1_crit_alarm Output power high alarm temp1_input Measured temperature temp1_crit Critical high temperature temp1_crit_alarm Chip temperature critical high alarm +temp1_max Critical maximum temperature +temp1_max_alarm Temperature critical max alarm +temp2_input Measured temperature +temp2_crit Critical high temperature +temp2_crit_alarm Chip temperature critical high alarm +temp2_max Critical maximum temperature +temp2_max_alarm Temperature critical max alarm ================= ======================================== diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 816d1f9947ea..de2f2985f06f 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -11,6 +11,7 @@ Supported boards: * Pro WS X570-ACE * ProArt X570-CREATOR WIFI * ProArt X670E-CREATOR WIFI + * ProArt X870E-CREATOR WIFI * ProArt B550-CREATOR * ROG CROSSHAIR VIII DARK HERO * ROG CROSSHAIR VIII HERO (WI-FI) @@ -29,6 +30,7 @@ Supported boards: * ROG STRIX X570-F GAMING * ROG STRIX X570-I GAMING * ROG STRIX Z390-F GAMING + * ROG STRIX Z490-F GAMING * ROG STRIX Z690-A GAMING WIFI D4 * ROG ZENITH II EXTREME * ROG ZENITH II EXTREME ALPHA diff --git a/Documentation/hwmon/corsair-psu.rst b/Documentation/hwmon/corsair-psu.rst index 7ed794087f84..2e99cfd556a0 100644 --- a/Documentation/hwmon/corsair-psu.rst +++ b/Documentation/hwmon/corsair-psu.rst @@ -17,7 +17,7 @@ Supported devices: Corsair HX1000i (Legacy and Series 2023) - Corsair HX1200i (Legacy and Series 2023) + Corsair HX1200i (Legacy, Series 2023 and Series 2025) Corsair HX1500i (Legacy and Series 2023) diff --git a/Documentation/hwmon/ina238.rst b/Documentation/hwmon/ina238.rst index d1b93cf8627f..9a24da4786a4 100644 --- a/Documentation/hwmon/ina238.rst +++ b/Documentation/hwmon/ina238.rst @@ -65,7 +65,7 @@ Additional sysfs entries for sq52206 ------------------------------------ ======================= ======================================================= -energy1_input Energy measurement (mJ) +energy1_input Energy measurement (uJ) power1_input_highest Peak Power (uW) ======================= ======================================================= diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index b45bfb4ebf30..d292a86ac5da 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -167,6 +167,7 @@ Hardware Monitoring Kernel Drivers max77705 max8688 mc13783-adc + mc33xs2410_hwmon mc34vr500 mcp3021 menf21bmc diff --git a/Documentation/hwmon/mc33xs2410_hwmon.rst b/Documentation/hwmon/mc33xs2410_hwmon.rst new file mode 100644 index 000000000000..8a2136ef9139 --- /dev/null +++ b/Documentation/hwmon/mc33xs2410_hwmon.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mc33xs2410_hwmon +============================== + +Supported devices: + + * NXPs MC33XS2410 + + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf + +Authors: + + Dimitri Fedrau + +Description +----------- + +The MC33XS2410 is a four channel self-protected high-side switch featuring +hardware monitoring functions such as temperature, current and voltages for each +of the four channels. + +Sysfs entries +------------- + +======================= ====================================================== +temp1_label "Central die temperature" +temp1_input Measured temperature of central die + +temp[2-5]_label "Channel [1-4] temperature" +temp[2-5]_input Measured temperature of a single channel +temp[2-5]_alarm Temperature alarm +temp[2-5]_max Maximal temperature +======================= ====================================================== diff --git a/Documentation/hwmon/tps53679.rst b/Documentation/hwmon/tps53679.rst index 3b9561648c24..dd5e4a37375d 100644 --- a/Documentation/hwmon/tps53679.rst +++ b/Documentation/hwmon/tps53679.rst @@ -43,6 +43,14 @@ Supported chips: Datasheet: https://www.ti.com/lit/gpn/TPS53681 + * Texas Instruments TPS53685 + + Prefix: 'tps53685' + + Addresses scanned: - + + Datasheet: https://www.ti.com/lit/gpn/TPS53685 + * Texas Instruments TPS53688 Prefix: 'tps53688' diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst new file mode 100644 index 000000000000..966e72c0109a --- /dev/null +++ b/Documentation/iio/adxl313.rst @@ -0,0 +1,293 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +ADXL313 driver +=============== + +This driver supports Analog Device's ADXL313 on SPI/I2C bus. + +1. Supported devices +==================== + +* `ADXL313 `_ + +The ADXL313is a low noise density, low power, 3-axis accelerometer with +selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and +±4 g ranges. + +2. Device attributes +==================== + +Accelerometer measurements are always provided. + +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 +the IIO ABI documentation. + +The following tables show the adxl313 related device files, found in the +specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. + ++---------------------------------------------------+----------------------------------------------------------+ +| 3-Axis Accelerometer related device files | Description | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_scale | Scale for the accelerometer channels. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_raw | Raw X-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_calibbias | y-axis acceleration offset correction | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_raw | Raw Y-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_raw | Raw Z-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ + ++---------------------------------------+----------------------------------------------+ +| Miscellaneous device files | Description | ++---------------------------------------+----------------------------------------------+ +| name | Name of the IIO device. | ++---------------------------------------+----------------------------------------------+ +| in_accel_sampling_frequency | Currently selected sample rate. | ++---------------------------------------+----------------------------------------------+ +| in_accel_sampling_frequency_available | Available sampling frequency configurations. | ++---------------------------------------+----------------------------------------------+ + +The iio event related settings, found in ``/sys/bus/iio/devices/iio:deviceX/events``. + ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_falling_period | AC coupled inactivity time. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_rising_value | AC coupled activity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_falling_period | Inactivity time. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_falling_value | Inactivity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_rising_value | Activity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\&y\&z_mag_adaptive_falling_en | Enable or disable AC coupled inactivity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\|y\|z_mag_adaptive_rising_en | Enable or disable AC coupled activity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\&y\&z_mag_falling_en | Enable or disable inactivity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\|y\|z_mag_rising_en | Enable or disable activity events. | ++---------------------------------------------------+----------------------------------------------------------+ + +The default coupling is DC coupled events. In this case the threshold will +be in place as such, where for the AC coupled case an adaptive threshold +(described in the datasheet) will be applied by the sensor. In general activity, +i.e. ``ACTIVITY`` or ``ACTIVITY_AC`` and inactivity i.e. ``INACTIVITY`` or +``INACTIVITY_AC``, will be linked with auto-sleep enabled when both are enabled. +This means in particular ``ACTIVITY`` can also be linked to ``INACTIVITY_AC`` +and vice versa, without problem. + +Note here, that ``ACTIVITY`` and ``ACTIVITY_AC`` are mutually exclusive. This +means, that the most recent configuration will be set. For instance, if +``ACTIVITY`` is enabled, and ``ACTIVITY_AC`` will be enabled, the sensor driver +will have ``ACTIVITY`` disabled, but ``ACTIVITY_AC`` enabled. The same is valid +for inactivity. In case of turning off an event, it has to match to what is +actually enabled, i.e. enabling ``ACTIVITY_AC`` and then disabling ``ACTIVITY`` +is simply ignored as it is already disabled. Or, as if it was any other not +enabled event, too. + +Channels processed values +------------------------- + +A channel value can be read from its _raw attribute. The value returned is the +raw value as reported by the devices. To get the processed value of the channel, +apply the following formula: + +.. code-block:: + + processed value = (_raw + _offset) * _scale + +Where _offset and _scale are device attributes. If no _offset attribute is +present, simply assume its value is 0. + +The ADXL313 driver offers data for a single types of channels, the table below +shows the measurement units for the processed value, which are defined by the +IIO framework: + ++-------------------------------------+---------------------------+ +| Channel type | Measurement unit | ++-------------------------------------+---------------------------+ +| Acceleration on X, Y, and Z axis | Meters per Second squared | ++-------------------------------------+---------------------------+ + +Usage examples +-------------- + +Show device name: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat name + adxl313 + +Show accelerometer channels value: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw + 2 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw + -57 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw + 2 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale + 0.009576806 + +The accelerometer values will be: + +- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.0191536 m/s^2 +- Y-axis acceleration = in_accel_y_raw * in_accel_scale = -0.5458779 m/s^2 +- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 0.0191536 m/s^2 + +Set calibration offset for accelerometer channels. Note, that the calibration +will be rounded according to the graduation of LSB units: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 0 + + root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 48 + +Set sampling frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency + 100.000000 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency_available + 6.250000 12.500000 25.000000 50.000000 100.000000 200.000000 400.000000 800.000000 1600.000000 3200.000000 + + root:/sys/bus/iio/devices/iio:device0> echo 400 > in_accel_sampling_frequency + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency + 400.000000 + +3. Device buffers and triggers +============================== + +This driver supports IIO buffers. + +All devices support retrieving the raw acceleration measurements using buffers. + +Usage examples +-------------- + +Select channels for buffer read: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en + +Set the number of samples to be stored in the buffer: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length + +Enable buffer readings: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable + +Obtain buffered data: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0 + ... + 000000d0 01 fc 31 00 c7 ff 03 fc 31 00 c7 ff 04 fc 33 00 |..1.....1.....3.| + 000000e0 c8 ff 03 fc 32 00 c5 ff ff fc 32 00 c7 ff 0a fc |....2.....2.....| + 000000f0 30 00 c8 ff 06 fc 33 00 c7 ff 01 fc 2f 00 c8 ff |0.....3...../...| + 00000100 02 fc 32 00 c6 ff 04 fc 33 00 c8 ff 05 fc 33 00 |..2.....3.....3.| + 00000110 ca ff 02 fc 31 00 c7 ff 02 fc 30 00 c9 ff 09 fc |....1.....0.....| + 00000120 35 00 c9 ff 08 fc 35 00 c8 ff 02 fc 31 00 c5 ff |5.....5.....1...| + 00000130 03 fc 32 00 c7 ff 04 fc 32 00 c7 ff 02 fc 31 00 |..2.....2.....1.| + 00000140 c7 ff 08 fc 30 00 c7 ff 02 fc 32 00 c5 ff ff fc |....0.....2.....| + 00000150 31 00 c5 ff 04 fc 31 00 c8 ff 03 fc 32 00 c8 ff |1.....1.....2...| + 00000160 01 fc 31 00 c7 ff 05 fc 31 00 c3 ff 04 fc 31 00 |..1.....1.....1.| + 00000170 c5 ff 04 fc 30 00 c7 ff 03 fc 31 00 c9 ff 03 fc |....0.....1.....| + ... + +Enabling activity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + + Event: time: 1748795762298351281, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + Event: time: 1748795762302653704, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + Event: time: 1748795762304340726, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + ... + +Disabling activity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 0 > ./events/in_accel_x\|y\|z_mag_rising_en + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + + +Enabling inactivity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.234375 > ./events/in_accel_mag_falling_value + root:/sys/bus/iio/devices/iio:device0> echo 5 > ./events/in_accel_mag_falling_period + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\&y\&z_mag_falling_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + Event: time: 1748796324115962975, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + Event: time: 1748796329329981772, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + Event: time: 1748796334543399706, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + ... + + +Now, enabling activity, e.g. the AC coupled counter-part ``ACTIVITY_AC`` + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + + Event: time: 1748796880354686777, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising + <5s of inactivity, then> + Event: time: 1748796885543252017, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + + Event: time: 1748796887756634678, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising + + Event: time: 1748796892964368352, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + + +Note, when AC coupling is in place, the event type will be of ``mag_adaptive``. +AC- or DC-coupled (the default) events are used similarly. + +4. IIO Interfacing Tools +======================== + +See Documentation/iio/iio_tools.rst for the description of the available IIO +interfacing tools. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 2d6afc5a8ed5..c106402a91f7 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers adis16475 adis16480 adis16550 + adxl313 adxl380 bno055 ep93xx_adc diff --git a/Documentation/input/devices/edt-ft5x06.rst b/Documentation/input/devices/edt-ft5x06.rst index 1ccc94b192b7..e410d73d4841 100644 --- a/Documentation/input/devices/edt-ft5x06.rst +++ b/Documentation/input/devices/edt-ft5x06.rst @@ -29,8 +29,25 @@ The driver allows configuration of the touch screen via a set of sysfs files: For debugging purposes the driver provides a few files in the debug -filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06 -you'll find the following files: +filesystem (if available in the kernel). They are located in: + + /sys/kernel/debug/i2c/// + +If you don't know the bus and device numbers, you can look them up with this +command: + + $ ls -l /sys/bus/i2c/drivers/edt_ft5x06 + +The dereference of the symlink will contain the needed information. You will +need the last two elements of its path: + + 0-0038 -> ../../../../devices/platform/soc/fcfee800.i2c/i2c-0/0-0038 + +So in this case, the location for the debug files is: + + /sys/kernel/debug/i2c/i2c-0/0-0038/ + +There, you'll find the following files: num_x, num_y: (readonly) contains the number of sensor fields in X- and diff --git a/Documentation/input/gamepad.rst b/Documentation/input/gamepad.rst index eca17a7f5258..0c918b6f288b 100644 --- a/Documentation/input/gamepad.rst +++ b/Documentation/input/gamepad.rst @@ -190,8 +190,21 @@ Gamepads report the following events: Rumble is advertised as FF_RUMBLE. +- Grip buttons: + + Many pads include buttons on the rear, usually referred to as either grip or + rear buttons, or paddles. These are often reprogrammable by the firmware to + appear as "normal" buttons, but are sometimes exposed to software too. Some + notable examples of this are the Steam Deck, which has R4, R5, L4, and L5 on + the back; the Xbox Elite pads, which have P1-P4; and the Switch 2 Pro + Controller, which has GL and GR. + + For these controllers, BTN_GRIPR and BTN_GRIPR2 should be used for the top + and bottom (if present) right grip button(s), and BTN_GRIPL and BTN_GRIPL2 + should be used for the top and bottom (if present) left grip button(s). + - Profile: - Some pads provide a multi-value profile selection switch. An example is the - XBox Adaptive and the XBox Elite 2 controllers. When the active profile is - switched, its newly selected value is emitted as an ABS_PROFILE event. + Some pads provide a multi-value profile selection switch. Examples include + the Xbox Adaptive and the Xbox Elite 2 controllers. When the active profile + is switched, its newly selected value is emitted as an ABS_PROFILE event. diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index fc4e845bc249..d213c4f599a4 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -67,12 +67,12 @@ Environment variables for ``*config``: with its value when saving the configuration, instead of using the default, ``CONFIG_``. -Environment variables for ``{allyes/allmod/allno/rand}config``: +Environment variables for ``{allyes/allmod/allno/alldef/rand}config``: ``KCONFIG_ALLCONFIG`` - The allyesconfig/allmodconfig/allnoconfig/randconfig variants can also - use the environment variable KCONFIG_ALLCONFIG as a flag or a filename - that contains config symbols that the user requires to be set to a + The allyesconfig/allmodconfig/alldefconfig/allnoconfig/randconfig variants + can also use the environment variable KCONFIG_ALLCONFIG as a flag or a + filename that contains config symbols that the user requires to be set to a specific value. If KCONFIG_ALLCONFIG is used without a filename where KCONFIG_ALLCONFIG == "" or KCONFIG_ALLCONFIG == "1", ``make *config`` checks for a file named "all{yes/mod/no/def/random}.config" diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 93d58d9a428b..1d164e005776 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -2192,6 +2192,8 @@ interpolate the memory barrier in the right place: wait_event_timeout(); wait_on_bit(); wait_on_bit_lock(); + wait_event_cmd(); + wait_event_exclusive_cmd(); Secondly, code that performs a wake up normally follows something like this: diff --git a/Documentation/mm/arch_pgtable_helpers.rst b/Documentation/mm/arch_pgtable_helpers.rst index af245161d8e7..ba2f658bc241 100644 --- a/Documentation/mm/arch_pgtable_helpers.rst +++ b/Documentation/mm/arch_pgtable_helpers.rst @@ -30,8 +30,6 @@ PTE Page Table Helpers +---------------------------+--------------------------------------------------+ | pte_protnone | Tests a PROT_NONE PTE | +---------------------------+--------------------------------------------------+ -| pte_devmap | Tests a ZONE_DEVICE mapped PTE | -+---------------------------+--------------------------------------------------+ | pte_soft_dirty | Tests a soft dirty PTE | +---------------------------+--------------------------------------------------+ | pte_swp_soft_dirty | Tests a soft dirty swapped PTE | @@ -104,8 +102,6 @@ PMD Page Table Helpers +---------------------------+--------------------------------------------------+ | pmd_protnone | Tests a PROT_NONE PMD | +---------------------------+--------------------------------------------------+ -| pmd_devmap | Tests a ZONE_DEVICE mapped PMD | -+---------------------------+--------------------------------------------------+ | pmd_soft_dirty | Tests a soft dirty PMD | +---------------------------+--------------------------------------------------+ | pmd_swp_soft_dirty | Tests a soft dirty swapped PMD | @@ -177,8 +173,6 @@ PUD Page Table Helpers +---------------------------+--------------------------------------------------+ | pud_write | Tests a writable PUD | +---------------------------+--------------------------------------------------+ -| pud_devmap | Tests a ZONE_DEVICE mapped PUD | -+---------------------------+--------------------------------------------------+ | pud_mkyoung | Creates a young PUD | +---------------------------+--------------------------------------------------+ | pud_mkold | Creates an old PUD | @@ -242,13 +236,13 @@ SWAP Page Table Helpers ======================== +---------------------------+--------------------------------------------------+ -| __pte_to_swp_entry | Creates a swapped entry (arch) from a mapped PTE | +| __pte_to_swp_entry | Creates a swp_entry_t (arch) from a swap PTE | +---------------------------+--------------------------------------------------+ -| __swp_to_pte_entry | Creates a mapped PTE from a swapped entry (arch) | +| __swp_entry_to_pte | Creates a swap PTE from a swp_entry_t (arch) | +---------------------------+--------------------------------------------------+ -| __pmd_to_swp_entry | Creates a swapped entry (arch) from a mapped PMD | +| __pmd_to_swp_entry | Creates a swp_entry_t (arch) from a swap PMD | +---------------------------+--------------------------------------------------+ -| __swp_to_pmd_entry | Creates a mapped PMD from a swapped entry (arch) | +| __swp_entry_to_pmd | Creates a swap PMD from a swp_entry_t (arch) | +---------------------------+--------------------------------------------------+ | is_migration_entry | Tests a migration (read or write) swapped entry | +-------------------------------+----------------------------------------------+ diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index ddc50db3afa4..03f8137256f5 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -452,9 +452,9 @@ that supports each action are as below. - ``lru_deprio``: Deprioritize the region on its LRU lists. Supported by ``paddr`` operations set. - ``migrate_hot``: Migrate the regions prioritizing warmer regions. - Supported by ``paddr`` operations set. + Supported by ``vaddr``, ``fvaddr`` and ``paddr`` operations set. - ``migrate_cold``: Migrate the regions prioritizing colder regions. - Supported by ``paddr`` operations set. + Supported by ``vaddr``, ``fvaddr`` and ``paddr`` operations set. - ``stat``: Do nothing but count the statistics. Supported by all operations sets. diff --git a/Documentation/mm/damon/maintainer-profile.rst b/Documentation/mm/damon/maintainer-profile.rst index ce3e98458339..5cd07905a193 100644 --- a/Documentation/mm/damon/maintainer-profile.rst +++ b/Documentation/mm/damon/maintainer-profile.rst @@ -7,9 +7,9 @@ The DAMON subsystem covers the files that are listed in 'DATA ACCESS MONITOR' section of 'MAINTAINERS' file. The mailing lists for the subsystem are damon@lists.linux.dev and -linux-mm@kvack.org. Patches should be made against the `mm-unstable tree -`_ whenever possible and posted -to the mailing lists. +linux-mm@kvack.org. Patches should be made against the `mm-new tree +`_ whenever possible and posted to the +mailing lists. SCM Trees --------- @@ -17,17 +17,19 @@ SCM Trees There are multiple Linux trees for DAMON development. Patches under development or testing are queued in `damon/next `_ by the DAMON maintainer. -Sufficiently reviewed patches will be queued in `mm-unstable -`_ by the memory management -subsystem maintainer. After more sufficient tests, the patches will be queued -in `mm-stable `_, and finally -pull-requested to the mainline by the memory management subsystem maintainer. +Sufficiently reviewed patches will be queued in `mm-new +`_ by the memory management subsystem +maintainer. As more sufficient tests are done, the patches will move to +`mm-unstable `_ and then to +`mm-stable `_. And finally those +will be pull-requested to the mainline by the memory management subsystem +maintainer. -Note again the patches for `mm-unstable tree -`_ are queued by the memory -management subsystem maintainer. If the patches requires some patches in -`damon/next tree `_ which not yet merged -in mm-unstable, please make sure the requirement is clearly specified. +Note again the patches for `mm-new tree +`_ are queued by the memory management +subsystem maintainer. If the patches requires some patches in `damon/next tree +`_ which not yet merged in mm-new, +please make sure the requirement is clearly specified. Submit checklist addendum ------------------------- @@ -53,8 +55,9 @@ Further doing below and putting the results will be helpful. Key cycle dates --------------- -Patches can be sent anytime. Key cycle dates of the `mm-unstable -`_ and `mm-stable +Patches can be sent anytime. Key cycle dates of the `mm-new +`_, `mm-unstable +`_and `mm-stable `_ trees depend on the memory management subsystem maintainer. diff --git a/Documentation/mm/index.rst b/Documentation/mm/index.rst index d3ada3e45e10..fb45acba16ac 100644 --- a/Documentation/mm/index.rst +++ b/Documentation/mm/index.rst @@ -56,7 +56,6 @@ documentation, or deleted if it has served its purpose. page_owner page_table_check remap_file_pages - slub split_page_table_lock transhuge unevictable-lru diff --git a/Documentation/mm/page_migration.rst b/Documentation/mm/page_migration.rst index 519b35a4caf5..34602b254aa6 100644 --- a/Documentation/mm/page_migration.rst +++ b/Documentation/mm/page_migration.rst @@ -146,18 +146,33 @@ Steps: 18. The new page is moved to the LRU and can be scanned by the swapper, etc. again. -Non-LRU page migration -====================== +movable_ops page migration +========================== -Although migration originally aimed for reducing the latency of memory -accesses for NUMA, compaction also uses migration to create high-order -pages. For compaction purposes, it is also useful to be able to move -non-LRU pages, such as zsmalloc and virtio-balloon pages. +Selected typed, non-folio pages (e.g., pages inflated in a memory balloon, +zsmalloc pages) can be migrated using the movable_ops migration framework. -If a driver wants to make its pages movable, it should define a struct -movable_operations. It then needs to call __SetPageMovable() on each -page that it may be able to move. This uses the ``page->mapping`` field, -so this field is not available for the driver to use for other purposes. +The "struct movable_operations" provide callbacks specific to a page type +for isolating, migrating and un-isolating (putback) these pages. + +Once a page is indicated as having movable_ops, that condition must not +change until the page was freed back to the buddy. This includes not +changing/clearing the page type and not changing/clearing the +PG_movable_ops page flag. + +Arbitrary drivers cannot currently make use of this framework, as it +requires: + +(a) a page type +(b) indicating them as possibly having movable_ops in page_has_movable_ops() + based on the page type +(c) returning the movable_ops from page_movable_ops() based on the page + type +(d) not reusing the PG_movable_ops and PG_movable_ops_isolated page flags + for other purposes + +For example, balloon drivers can make use of this framework through the +balloon-compaction infrastructure residing in the core kernel. Monitoring Migration ===================== diff --git a/Documentation/mm/physical_memory.rst b/Documentation/mm/physical_memory.rst index d3ac106e6b14..9af11b5bd145 100644 --- a/Documentation/mm/physical_memory.rst +++ b/Documentation/mm/physical_memory.rst @@ -584,7 +584,7 @@ Compaction control ``compact_blockskip_flush`` Set to true when compaction migration scanner and free scanner meet, which - means the ``PB_migrate_skip`` bits should be cleared. + means the ``PB_compact_skip`` bits should be cleared. ``contiguous`` Set to true when the zone is contiguous (in other words, no hole). diff --git a/Documentation/mm/process_addrs.rst b/Documentation/mm/process_addrs.rst index e6756e78b476..be49e2a269e4 100644 --- a/Documentation/mm/process_addrs.rst +++ b/Documentation/mm/process_addrs.rst @@ -303,7 +303,9 @@ There are four key operations typically performed on page tables: 1. **Traversing** page tables - Simply reading page tables in order to traverse them. This only requires that the VMA is kept stable, so a lock which establishes this suffices for traversal (there are also lockless variants - which eliminate even this requirement, such as :c:func:`!gup_fast`). + which eliminate even this requirement, such as :c:func:`!gup_fast`). There is + also a special case of page table traversal for non-VMA regions which we + consider separately below. 2. **Installing** page table mappings - Whether creating a new mapping or modifying an existing one in such a way as to change its identity. This requires that the VMA is kept stable via an mmap or VMA lock (explicitly not @@ -335,15 +337,13 @@ ahead and perform these operations on page tables (though internally, kernel operations that perform writes also acquire internal page table locks to serialise - see the page table implementation detail section for more details). +.. note:: We free empty PTE tables on zap under the RCU lock - this does not + change the aforementioned locking requirements around zapping. + When **installing** page table entries, the mmap or VMA lock must be held to keep the VMA stable. We explore why this is in the page table locking details section below. -.. warning:: Page tables are normally only traversed in regions covered by VMAs. - If you want to traverse page tables in areas that might not be - covered by VMAs, heavier locking is required. - See :c:func:`!walk_page_range_novma` for details. - **Freeing** page tables is an entirely internal memory management operation and has special requirements (see the page freeing section below for more details). @@ -355,6 +355,44 @@ has special requirements (see the page freeing section below for more details). from the reverse mappings, but no other VMAs can be permitted to be accessible and span the specified range. +Traversing non-VMA page tables +------------------------------ + +We've focused above on traversal of page tables belonging to VMAs. It is also +possible to traverse page tables which are not represented by VMAs. + +Kernel page table mappings themselves are generally managed but whatever part of +the kernel established them and the aforementioned locking rules do not apply - +for instance vmalloc has its own set of locks which are utilised for +establishing and tearing down page its page tables. + +However, for convenience we provide the :c:func:`!walk_kernel_page_table_range` +function which is synchronised via the mmap lock on the :c:macro:`!init_mm` +kernel instantiation of the :c:struct:`!struct mm_struct` metadata object. + +If an operation requires exclusive access, a write lock is used, but if not, a +read lock suffices - we assert only that at least a read lock has been acquired. + +Since, aside from vmalloc and memory hot plug, kernel page tables are not torn +down all that often - this usually suffices, however any caller of this +functionality must ensure that any additionally required locks are acquired in +advance. + +We also permit a truly unusual case is the traversal of non-VMA ranges in +**userland** ranges, as provided for by :c:func:`!walk_page_range_debug`. + +This has only one user - the general page table dumping logic (implemented in +:c:macro:`!mm/ptdump.c`) - which seeks to expose all mappings for debug purposes +even if they are highly unusual (possibly architecture-specific) and are not +backed by a VMA. + +We must take great care in this case, as the :c:func:`!munmap` implementation +detaches VMAs under an mmap write lock before tearing down page tables under a +downgraded mmap read lock. + +This means such an operation could race with this, and thus an mmap **write** +lock is required. + Lock ordering ------------- @@ -461,6 +499,10 @@ Locking Implementation Details Page table locking details -------------------------- +.. note:: This section explores page table locking requirements for page tables + encompassed by a VMA. See the above section on non-VMA page table + traversal for details on how we handle that case. + In addition to the locks described in the terminology section above, we have additional locks dedicated to page tables: diff --git a/Documentation/mm/slab.rst b/Documentation/mm/slab.rst index 87d5a5bb172f..2bcc58ada302 100644 --- a/Documentation/mm/slab.rst +++ b/Documentation/mm/slab.rst @@ -3,3 +3,10 @@ =============== Slab Allocation =============== + +Functions and structures +======================== + +.. kernel-doc:: mm/slab.h +.. kernel-doc:: mm/slub.c + :internal: diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index 4cbfe666e6f5..b29d62eefa16 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -6,6 +6,9 @@ $schema: https://json-schema.org/draft-07/schema # Common defines $defs: + name: + type: string + pattern: ^[0-9a-z-]+$ uint: type: integer minimum: 0 @@ -76,7 +79,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' header: description: For C-compatible languages, header which already defines this value. type: string @@ -103,7 +106,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' value: type: integer doc: @@ -132,7 +135,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' type: description: The netlink attribute type enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string, binary ] @@ -169,7 +172,7 @@ properties: name: description: | Name used when referring to this space in other definitions, not used outside of the spec. - type: string + $ref: '#/$defs/name' name-prefix: description: | Prefix for the C enum name of the attributes. Default family[name]-set[name]-a- @@ -206,7 +209,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' type: &attr-type description: The netlink attribute type enum: [ unused, pad, flag, binary, bitfield32, @@ -348,7 +351,7 @@ properties: properties: name: description: Name of the operation, also defining its C enum value in uAPI. - type: string + $ref: '#/$defs/name' doc: description: Documentation for the command. type: string diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml index 40efbbad76ab..7b1ec153e834 100644 --- a/Documentation/netlink/genetlink.yaml +++ b/Documentation/netlink/genetlink.yaml @@ -6,6 +6,9 @@ $schema: https://json-schema.org/draft-07/schema # Common defines $defs: + name: + type: string + pattern: ^[0-9a-z-]+$ uint: type: integer minimum: 0 @@ -29,7 +32,7 @@ additionalProperties: False properties: name: description: Name of the genetlink family. - type: string + $ref: '#/$defs/name' doc: type: string protocol: @@ -48,7 +51,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' header: description: For C-compatible languages, header which already defines this value. type: string @@ -75,7 +78,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' value: type: integer doc: @@ -96,7 +99,7 @@ properties: name: description: | Name used when referring to this space in other definitions, not used outside of the spec. - type: string + $ref: '#/$defs/name' name-prefix: description: | Prefix for the C enum name of the attributes. Default family[name]-set[name]-a- @@ -121,7 +124,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' type: &attr-type enum: [ unused, pad, flag, binary, uint, sint, u8, u16, u32, u64, s8, s16, s32, s64, @@ -243,7 +246,7 @@ properties: properties: name: description: Name of the operation, also defining its C enum value in uAPI. - type: string + $ref: '#/$defs/name' doc: description: Documentation for the command. type: string @@ -327,7 +330,7 @@ properties: name: description: | The name for the group, used to form the define and the value of the define. - type: string + $ref: '#/$defs/name' flags: *cmd_flags kernel-family: diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml index e34bf23897fa..246fa07bccf6 100644 --- a/Documentation/netlink/netlink-raw.yaml +++ b/Documentation/netlink/netlink-raw.yaml @@ -6,6 +6,12 @@ $schema: https://json-schema.org/draft-07/schema # Common defines $defs: + name: + type: string + pattern: ^[0-9a-z-]+$ + name-cap: + type: string + pattern: ^[0-9a-zA-Z-]+$ uint: type: integer minimum: 0 @@ -71,7 +77,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' header: description: For C-compatible languages, header which already defines this value. type: string @@ -98,7 +104,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' value: type: integer doc: @@ -124,7 +130,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name-cap' type: description: | The netlink attribute type. Members of type 'binary' or 'pad' @@ -183,7 +189,7 @@ properties: name: description: | Name used when referring to this space in other definitions, not used outside of the spec. - type: string + $ref: '#/$defs/name' name-prefix: description: | Prefix for the C enum name of the attributes. Default family[name]-set[name]-a- @@ -220,7 +226,7 @@ properties: additionalProperties: False properties: name: - type: string + $ref: '#/$defs/name' type: &attr-type description: The netlink attribute type enum: [ unused, pad, flag, binary, bitfield32, @@ -408,7 +414,7 @@ properties: properties: name: description: Name of the operation, also defining its C enum value in uAPI. - type: string + $ref: '#/$defs/name' doc: description: Documentation for the command. type: string diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml index 840dc4504216..c6832633ab7b 100644 --- a/Documentation/netlink/specs/conntrack.yaml +++ b/Documentation/netlink/specs/conntrack.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: conntrack protocol: netlink-raw protonum: 12 @@ -195,17 +195,17 @@ attribute-sets: - name: tuple-attrs attributes: - - + - name: tuple-ip type: nest nested-attributes: tuple-ip-attrs doc: conntrack l3 information - - + - name: tuple-proto type: nest nested-attributes: tuple-proto-attrs doc: conntrack l4 information - - + - name: tuple-zone type: u16 byte-order: big-endian @@ -213,74 +213,74 @@ attribute-sets: - name: protoinfo-tcp-attrs attributes: - - + - name: tcp-state type: u8 enum: nf-ct-tcp-state doc: tcp connection state - - + - name: tcp-wscale-original type: u8 doc: window scaling factor in original direction - - + - name: tcp-wscale-reply type: u8 doc: window scaling factor in reply direction - - + - name: tcp-flags-original type: binary struct: nf-ct-tcp-flags-mask - - + - name: tcp-flags-reply type: binary struct: nf-ct-tcp-flags-mask - name: protoinfo-dccp-attrs attributes: - - + - name: dccp-state type: u8 doc: dccp connection state - - + - name: dccp-role type: u8 - - + - name: dccp-handshake-seq type: u64 byte-order: big-endian - - + - name: dccp-pad type: pad - name: protoinfo-sctp-attrs attributes: - - + - name: sctp-state type: u8 doc: sctp connection state enum: nf-ct-sctp-state - - + - name: vtag-original type: u32 byte-order: big-endian - - + - name: vtag-reply type: u32 byte-order: big-endian - name: protoinfo-attrs attributes: - - + - name: protoinfo-tcp type: nest nested-attributes: protoinfo-tcp-attrs doc: conntrack tcp state information - - + - name: protoinfo-dccp type: nest nested-attributes: protoinfo-dccp-attrs doc: conntrack dccp state information - - + - name: protoinfo-sctp type: nest nested-attributes: protoinfo-sctp-attrs diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 05fee1b7fe19..bb87111d5e16 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: devlink protocol: genetlink-legacy @@ -38,15 +38,15 @@ definitions: - name: dsa - - name: pci_pf + name: pci-pf - - name: pci_vf + name: pci-vf - name: virtual - name: unused - - name: pci_sf + name: pci-sf - type: enum name: port-fn-state @@ -220,10 +220,14 @@ definitions: - name: flag - - name: nul_string + name: nul-string value: 10 - name: binary + - + name: rate-tc-index-max + type: const + value: 7 attribute-sets: - @@ -744,7 +748,7 @@ attribute-sets: name: flash-update-overwrite-mask type: bitfield32 enum: flash-overwrite - enum-as-flags: True + enum-as-flags: true - name: reload-action type: u8 @@ -753,12 +757,12 @@ attribute-sets: name: reload-actions-performed type: bitfield32 enum: reload-action - enum-as-flags: True + enum-as-flags: true - name: reload-limits type: bitfield32 enum: reload-action - enum-as-flags: True + enum-as-flags: true - name: dev-stats type: nest @@ -812,14 +816,14 @@ attribute-sets: name: rate-parent-node-name type: string - - name: region-max-snapshots - type: u32 + name: region-max-snapshots + type: u32 - name: linecard-index type: u32 - - name: linecard-state - type: u8 + name: linecard-state + type: u8 - name: linecard-type type: string @@ -844,7 +848,11 @@ attribute-sets: - name: region-direct type: flag - + - + name: rate-tc-bws + type: nest + multi-attr: true + nested-attributes: dl-rate-tc-bws - name: dl-dev-stats subset-of: devlink @@ -917,7 +925,7 @@ attribute-sets: name: caps type: bitfield32 enum: port-fn-attr-cap - enum-as-flags: True + enum-as-flags: true - name: dl-dpipe-tables @@ -1139,7 +1147,7 @@ attribute-sets: - name: param-type - # TODO: fill in the attribute param-value-list + # TODO: fill in the attribute param-value-list - name: dl-region-snapshots @@ -1249,6 +1257,22 @@ attribute-sets: - name: flash type: flag + - + name: dl-rate-tc-bws + name-prefix: devlink-rate-tc-attr- + attributes: + - + name: index + type: u8 + checks: + max: rate-tc-index-max + - + name: bw + type: u32 + doc: | + Specifies the bandwidth share assigned to the Traffic Class. + The bandwidth for the traffic class is determined + in proportion to the sum of the shares of all configured classes. operations: enum-model: directional @@ -1257,7 +1281,7 @@ operations: name: get doc: Get devlink instances. attribute-set: devlink - dont-validate: [ strict, dump ] + dont-validate: [strict, dump] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1266,7 +1290,7 @@ operations: attributes: &dev-id-attrs - bus-name - dev-name - reply: &get-reply + reply: &get-reply value: 3 attributes: - bus-name @@ -1280,7 +1304,7 @@ operations: name: port-get doc: Get devlink port instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1304,8 +1328,8 @@ operations: name: port-set doc: Set devlink port instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1321,8 +1345,8 @@ operations: name: port-new doc: Create devlink port instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1343,8 +1367,8 @@ operations: name: port-del doc: Delete devlink port instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1355,8 +1379,8 @@ operations: name: port-split doc: Split devlink port instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1371,8 +1395,8 @@ operations: name: port-unsplit doc: Unplit devlink port instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1383,7 +1407,7 @@ operations: name: sb-get doc: Get shared buffer instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1405,7 +1429,7 @@ operations: name: sb-pool-get doc: Get shared buffer pool instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1428,8 +1452,8 @@ operations: name: sb-pool-set doc: Set shared buffer pool instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1446,7 +1470,7 @@ operations: name: sb-port-pool-get doc: Get shared buffer port-pool combinations and threshold. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1470,8 +1494,8 @@ operations: name: sb-port-pool-set doc: Set shared buffer port-pool combinations and threshold. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1488,7 +1512,7 @@ operations: name: sb-tc-pool-bind-get doc: Get shared buffer port-TC to pool bindings and threshold. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1513,8 +1537,8 @@ operations: name: sb-tc-pool-bind-set doc: Set shared buffer port-TC to pool bindings and threshold. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1533,8 +1557,8 @@ operations: name: sb-occ-snapshot doc: Take occupancy snapshot of shared buffer. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1549,8 +1573,8 @@ operations: name: sb-occ-max-clear doc: Clear occupancy watermarks of shared buffer. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1564,8 +1588,8 @@ operations: name: eswitch-get doc: Get eswitch attributes. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1584,8 +1608,8 @@ operations: name: eswitch-set doc: Set eswitch attributes. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1596,7 +1620,7 @@ operations: name: dpipe-table-get doc: Get dpipe table attributes. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1616,7 +1640,7 @@ operations: name: dpipe-entries-get doc: Get dpipe entries attributes. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1635,7 +1659,7 @@ operations: name: dpipe-headers-get doc: Get dpipe headers attributes. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1653,8 +1677,8 @@ operations: name: dpipe-table-counters-set doc: Set dpipe counter attributes. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1669,8 +1693,8 @@ operations: name: resource-set doc: Set resource attributes. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1685,7 +1709,7 @@ operations: name: resource-dump doc: Get resource attributes. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1704,8 +1728,8 @@ operations: name: reload doc: Reload devlink. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-dev-lock post: devlink-nl-post-doit-dev-lock @@ -1728,7 +1752,7 @@ operations: name: param-get doc: Get param instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1748,8 +1772,8 @@ operations: name: param-set doc: Set param instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1766,7 +1790,7 @@ operations: name: region-get doc: Get region instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1789,8 +1813,8 @@ operations: name: region-new doc: Create region snapshot. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1810,8 +1834,8 @@ operations: name: region-del doc: Delete region snapshot. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1822,8 +1846,8 @@ operations: name: region-read doc: Read region data. attribute-set: devlink - dont-validate: [ dump-strict ] - flags: [ admin-perm ] + dont-validate: [dump-strict] + flags: [admin-perm] dump: request: attributes: @@ -1847,7 +1871,7 @@ operations: name: port-param-get doc: Get port param instances. attribute-set: devlink - dont-validate: [ strict, dump-strict ] + dont-validate: [strict, dump-strict] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1863,8 +1887,8 @@ operations: name: port-param-set doc: Set port param instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port post: devlink-nl-post-doit @@ -1873,9 +1897,11 @@ operations: - name: info-get - doc: Get device information, like driver name, hardware and firmware versions etc. + doc: | + Get device information, like driver name, hardware and firmware versions + etc. attribute-set: devlink - dont-validate: [ strict, dump ] + dont-validate: [strict, dump] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -1900,7 +1926,7 @@ operations: name: health-reporter-get doc: Get health reporter instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1921,8 +1947,8 @@ operations: name: health-reporter-set doc: Set health reporter instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1940,8 +1966,8 @@ operations: name: health-reporter-recover doc: Recover health reporter instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1952,8 +1978,8 @@ operations: name: health-reporter-diagnose doc: Diagnose health reporter instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1964,8 +1990,8 @@ operations: name: health-reporter-dump-get doc: Dump health reporter instances. attribute-set: devlink - dont-validate: [ dump-strict ] - flags: [ admin-perm ] + dont-validate: [dump-strict] + flags: [admin-perm] dump: request: attributes: *health-reporter-id-attrs @@ -1978,8 +2004,8 @@ operations: name: health-reporter-dump-clear doc: Clear dump of health reporter instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -1990,8 +2016,8 @@ operations: name: flash-update doc: Flash update devlink instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2007,7 +2033,7 @@ operations: name: trap-get doc: Get trap instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2029,8 +2055,8 @@ operations: name: trap-set doc: Set trap instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2045,7 +2071,7 @@ operations: name: trap-group-get doc: Get trap group instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2067,8 +2093,8 @@ operations: name: trap-group-set doc: Set trap group instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2084,7 +2110,7 @@ operations: name: trap-policer-get doc: Get trap policer instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2106,8 +2132,8 @@ operations: name: trap-policer-set doc: Get trap policer instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2123,8 +2149,8 @@ operations: name: health-reporter-test doc: Test health reporter instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit-port-optional post: devlink-nl-post-doit @@ -2136,7 +2162,7 @@ operations: name: rate-get doc: Get rate instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2159,8 +2185,8 @@ operations: name: rate-set doc: Set rate instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2174,13 +2200,14 @@ operations: - rate-tx-priority - rate-tx-weight - rate-parent-node-name + - rate-tc-bws - name: rate-new doc: Create rate instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2194,13 +2221,14 @@ operations: - rate-tx-priority - rate-tx-weight - rate-parent-node-name + - rate-tc-bws - name: rate-del doc: Delete rate instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2214,7 +2242,7 @@ operations: name: linecard-get doc: Get line card instances. attribute-set: devlink - dont-validate: [ strict ] + dont-validate: [strict] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2236,8 +2264,8 @@ operations: name: linecard-set doc: Set line card instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2252,7 +2280,7 @@ operations: name: selftests-get doc: Get device selftest instances. attribute-set: devlink - dont-validate: [ strict, dump ] + dont-validate: [strict, dump] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit @@ -2269,8 +2297,8 @@ operations: name: selftests-run doc: Run device selftest instances. attribute-set: devlink - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: pre: devlink-nl-pre-doit post: devlink-nl-post-doit diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 8feefeae5376..5decee61a2c4 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: dpll doc: DPLL subsystem. @@ -188,7 +188,7 @@ definitions: value: 10000 - type: const - name: pin-frequency-77_5-khz + name: pin-frequency-77-5-khz value: 77500 - type: const @@ -240,6 +240,20 @@ definitions: integer part of a measured phase offset value. Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a fractional part of a measured phase offset value. + - + type: enum + name: feature-state + doc: | + Allow control (enable/disable) and status checking over features. + entries: + - + name: disable + doc: | + feature shall be disabled + - + name: enable + doc: | + feature shall be enabled attribute-sets: - @@ -293,6 +307,14 @@ attribute-sets: be put to message multiple times to indicate possible parallel quality levels (e.g. one specified by ITU option 1 and another one specified by option 2). + - + name: phase-offset-monitor + type: u32 + enum: feature-state + doc: Receive or request state of phase offset monitor feature. + If enabled, dpll device shall monitor and notify all currently + available inputs for changes of their phase offset against the + dpll device. - name: pin enum-name: dpll_a_pin @@ -406,6 +428,15 @@ attribute-sets: doc: | A ratio of high to low state of a SYNC signal pulse embedded into base clock frequency. Value is in percents. + - + name: reference-sync + type: nest + multi-attr: true + nested-attributes: reference-sync + doc: | + Capable pin provides list of pins that can be bound to create a + reference-sync pin pair. + - name: pin-parent-device subset-of: pin @@ -436,6 +467,14 @@ attribute-sets: name: frequency-min - name: frequency-max + - + name: reference-sync + subset-of: pin + attributes: + - + name: id + - + name: state operations: enum-name: dpll_cmd @@ -445,7 +484,7 @@ operations: doc: | Get id of dpll device that matches given attributes attribute-set: dpll - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-lock-doit @@ -464,7 +503,7 @@ operations: doc: | Get list of DPLL devices (dump) or attributes of a single dpll device attribute-set: dpll - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-pre-doit @@ -483,6 +522,7 @@ operations: - temp - clock-id - type + - phase-offset-monitor dump: reply: *dev-attrs @@ -491,7 +531,7 @@ operations: name: device-set doc: Set attributes for a DPLL device attribute-set: dpll - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-pre-doit @@ -499,6 +539,7 @@ operations: request: attributes: - id + - phase-offset-monitor - name: device-create-ntf doc: Notification about device appearing @@ -519,7 +560,7 @@ operations: doc: | Get id of a pin that matches given attributes attribute-set: pin - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-lock-doit @@ -547,7 +588,7 @@ operations: a given dpll device - do request with target dpll and target pin - single pin attributes attribute-set: pin - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-pin-pre-doit @@ -574,6 +615,7 @@ operations: - esync-frequency - esync-frequency-supported - esync-pulse + - reference-sync dump: request: @@ -585,7 +627,7 @@ operations: name: pin-set doc: Set attributes of a target pin attribute-set: pin - flags: [ admin-perm ] + flags: [admin-perm] do: pre: dpll-pin-pre-doit @@ -601,6 +643,7 @@ operations: - parent-pin - phase-adjust - esync-frequency + - reference-sync - name: pin-create-ntf doc: Notification about pin appearing diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index 9f98715a6512..1bc1bd7d33c2 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: ethtool protocol: genetlink-legacy @@ -7,19 +7,22 @@ protocol: genetlink-legacy doc: Partial family for Ethtool Netlink. uapi-header: linux/ethtool_netlink_generated.h +c-family-name: ethtool-genl-name +c-version-name: ethtool-genl-version + definitions: - name: udp-tunnel-type enum-name: type: enum - entries: [ vxlan, geneve, vxlan-gpe ] + entries: [vxlan, geneve, vxlan-gpe] enum-cnt-name: __ethtool-udp-tunnel-type-cnt render-max: true - name: stringset type: enum entries: [] - header: linux/ethtool.h # skip rendering, no actual definition + header: linux/ethtool.h # skip rendering, no actual definition - name: header-flags type: flags @@ -45,7 +48,7 @@ definitions: name: started doc: The firmware flashing process has started. - - name: in_progress + name: in-progress doc: The firmware flashing process is in progress. - name: completed @@ -55,49 +58,51 @@ definitions: doc: The firmware flashing process was stopped due to an error. - name: c33-pse-ext-state - doc: "groups of PSE extended states functions. IEEE 802.3-2022 33.2.4.4 Variables" + doc: | + "groups of PSE extended states functions. IEEE 802.3-2022 33.2.4.4 + Variables" type: enum name-prefix: ethtool-c33-pse-ext-state- header: linux/ethtool.h entries: - - - name: none - doc: none - - - name: error-condition - doc: Group of error_condition states - - - name: mr-mps-valid - doc: Group of mr_mps_valid states - - - name: mr-pse-enable - doc: Group of mr_pse_enable states - - - name: option-detect-ted - doc: Group of option_detect_ted states - - - name: option-vport-lim - doc: Group of option_vport_lim states - - - name: ovld-detected - doc: Group of ovld_detected states - - - name: power-not-available - doc: Group of power_not_available states - - - name: short-detected - doc: Group of short_detected states + - + name: none + doc: none + - + name: error-condition + doc: Group of error_condition states + - + name: mr-mps-valid + doc: Group of mr_mps_valid states + - + name: mr-pse-enable + doc: Group of mr_pse_enable states + - + name: option-detect-ted + doc: Group of option_detect_ted states + - + name: option-vport-lim + doc: Group of option_vport_lim states + - + name: ovld-detected + doc: Group of ovld_detected states + - + name: power-not-available + doc: Group of power_not_available states + - + name: short-detected + doc: Group of short_detected states - name: phy-upstream-type enum-name: phy-upstream header: linux/ethtool.h type: enum name-prefix: phy-upstream - entries: [ mac, phy ] + entries: [mac, phy] - name: tcp-data-split type: enum - entries: [ unknown, disabled, enabled ] + entries: [unknown, disabled, enabled] - name: hwtstamp-source doc: Source of the hardware timestamp @@ -116,6 +121,92 @@ definitions: doc: | Hardware timestamp comes from one PHY device of the network topology + - + name: pse-event + doc: PSE event list for the PSE controller + type: flags + name-prefix: ethtool- + entries: + - + name: pse-event-over-current + doc: PSE output current is too high + - + name: pse-event-over-temp + doc: PSE in over temperature state + - + name: c33-pse-event-detection + doc: | + detection process occur on the PSE. IEEE 802.3-2022 33.2.5 and + 145.2.6 PSE detection of PDs. IEEE 802.3-202 30.9.1.1.5 + aPSEPowerDetectionStatus + - + name: c33-pse-event-classification + doc: | + classification process occur on the PSE. IEEE 802.3-2022 33.2.6 + and 145.2.8 classification of PDs mutual identification. + IEEE 802.3-2022 30.9.1.1.8 aPSEPowerClassification. + - + name: c33-pse-event-disconnection + doc: | + PD has been disconnected on the PSE. IEEE 802.3-2022 33.3.8 + and 145.3.9 PD Maintain Power Signature. IEEE 802.3-2022 + 33.5.1.2.9 MPS Absent. IEEE 802.3-2022 30.9.1.1.20 + aPSEMPSAbsentCounter. + - + name: pse-event-over-budget + doc: PSE turned off due to over budget situation + - + name: pse-event-sw-pw-control-error + doc: PSE faced an error managing the power control from software + - + name: input-xfrm + doc: RSS hash function transformations. + type: flags + enum-name: + name-prefix: rxh-xfrm- + header: linux/ethtool.h + entries: + - + name: sym-xor + doc: >- + XOR the corresponding source and destination fields of each specified + protocol. Both copies of the XOR'ed fields are fed into the RSS and + RXHASH calculation. Note that this XORing reduces the input set + entropy and could be exploited to reduce the RSS queue spread. + - + name: sym-or-xor + doc: >- + Similar to SYM_XOR, except that one copy of the XOR'ed fields is + replaced by an OR of the same fields. + - + name: rxfh-fields + name-prefix: rxh- + enum-name: + header: linux/ethtool.h + type: flags + entries: + - + name: l2da + value: 1 + - + name: vlan + - + name: l3-proto + - + name: ip-src + - + name: ip-dst + - + name: l4-b-0-1 + doc: src port in case of TCP/UDP/SCTP + - + name: l4-b-2-3 + doc: dst port in case of TCP/UDP/SCTP + - + name: gtp-teid + - + name: discard + value: 31 attribute-sets: - @@ -1224,7 +1315,7 @@ attribute-sets: - name: stat type: u64 - type-value: [ id ] + type-value: [id] - name: hist-rx type: nest @@ -1393,6 +1484,135 @@ attribute-sets: type: nest multi-attr: true nested-attributes: c33-pse-pw-limit + - + name: pse-pw-d-id + type: u32 + name-prefix: ethtool-a- + - + name: pse-prio-max + type: u32 + name-prefix: ethtool-a- + - + name: pse-prio + type: u32 + name-prefix: ethtool-a- + - + name: flow + attr-cnt-name: --ethtool-a-flow-cnt + doc: | + Flow types, corresponding to those defined in the old + ethtool header for RXFH and RXNFC as ${PROTO}_FLOW. + The values are not matching the old ones to avoid carrying + into Netlink the IP_USER_FLOW vs IPV4_FLOW vs IPV4_USER_FLOW confusion. + attributes: + - + name: ether + type: uint + enum: rxfh-fields + - + name: ip4 + type: uint + enum: rxfh-fields + - + name: ip6 + type: uint + enum: rxfh-fields + - + name: tcp4 + type: uint + enum: rxfh-fields + - + name: tcp6 + type: uint + enum: rxfh-fields + - + name: udp4 + type: uint + enum: rxfh-fields + - + name: udp6 + type: uint + enum: rxfh-fields + - + name: sctp4 + type: uint + enum: rxfh-fields + - + name: sctp6 + type: uint + enum: rxfh-fields + - + name: ah4 + type: uint + enum: rxfh-fields + - + name: ah6 + type: uint + enum: rxfh-fields + - + name: esp4 + type: uint + enum: rxfh-fields + - + name: esp6 + type: uint + enum: rxfh-fields + - + name: ah-esp4 + type: uint + enum: rxfh-fields + - + name: ah-esp6 + type: uint + enum: rxfh-fields + - + name: gtpu4 + type: uint + enum: rxfh-fields + - + name: gtpu6 + type: uint + enum: rxfh-fields + - + name: gtpc4 + type: uint + enum: rxfh-fields + - + name: gtpc6 + type: uint + enum: rxfh-fields + - + name: gtpc-teid4 + type: uint + enum: rxfh-fields + - + name: gtpc-teid6 + type: uint + enum: rxfh-fields + - + name: gtpu-eh4 + type: uint + enum: rxfh-fields + - + name: gtpu-eh6 + type: uint + enum: rxfh-fields + - + name: gtpu-ul4 + type: uint + enum: rxfh-fields + - + name: gtpu-ul6 + type: uint + enum: rxfh-fields + - + name: gtpu-dl4 + type: uint + enum: rxfh-fields + - + name: gtpu-dl6 + type: uint + enum: rxfh-fields - name: rss attr-cnt-name: __ethtool-a-rss-cnt @@ -1419,11 +1639,16 @@ attribute-sets: name: hkey type: binary - - name: input_xfrm + name: input-xfrm type: u32 + enum: input-xfrm - name: start-context type: u32 + - + name: flow-hash + type: nest + nested-attributes: flow - name: plca attr-cnt-name: __ethtool-a-plca-cnt @@ -1553,6 +1778,19 @@ attribute-sets: name: hwtstamp-flags type: nest nested-attributes: bitset + - + name: pse-ntf + attr-cnt-name: --ethtool-a-pse-ntf-cnt + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: events + type: uint + enum: pse-event + doc: List of events reported by the PSE controller operations: enum-model: directional @@ -2104,9 +2342,6 @@ operations: do: &module-eeprom-get-op request: - attributes: - - header - reply: attributes: - header - offset @@ -2114,6 +2349,9 @@ operations: - page - bank - i2c-address + reply: + attributes: + - header - data dump: *module-eeprom-get-op - @@ -2203,6 +2441,9 @@ operations: - c33-pse-ext-substate - c33-pse-avail-pw-limit - c33-pse-pw-limit-ranges + - pse-pw-d-id + - pse-prio-max + - pse-prio dump: *pse-get-op - name: pse-set @@ -2217,6 +2458,7 @@ operations: - podl-pse-admin-control - c33-pse-admin-control - c33-pse-avail-pw-limit + - pse-prio - name: rss-get doc: Get RSS params. @@ -2235,7 +2477,8 @@ operations: - hfunc - indir - hkey - - input_xfrm + - input-xfrm + - flow-hash dump: request: attributes: @@ -2411,3 +2654,79 @@ operations: attributes: *tsconfig reply: attributes: *tsconfig + - + name: pse-ntf + doc: Notification for PSE events. + + attribute-set: pse-ntf + + event: + attributes: + - header + - events + - + name: rss-set + doc: Set RSS params. + + attribute-set: rss + + do: + request: + attributes: + - header + - context + - hfunc + - indir + - hkey + - input-xfrm + - flow-hash + - + name: rss-ntf + doc: | + Notification for change in RSS configuration. + For additional contexts only modifications use this notification, + creation and deletion have dedicated messages. + notify: rss-get + - + name: rss-create-act + doc: Create an RSS context. + attribute-set: rss + do: + request: &rss-create-attrs + attributes: + - header + - context + - hfunc + - indir + - hkey + - input-xfrm + reply: *rss-create-attrs + - + name: rss-create-ntf + doc: | + Notification for creation of an additional RSS context. + notify: rss-create-act + - + name: rss-delete-act + doc: Delete an RSS context. + attribute-set: rss + do: + request: + attributes: + - header + - context + - + name: rss-delete-ntf + doc: | + Notification for deletion of an additional RSS context. + attribute-set: rss + event: + attributes: + - header + - context + +mcast-groups: + list: + - + name: monitor + c-define-name: ethtool-mcgrp-monitor-name diff --git a/Documentation/netlink/specs/fou.yaml b/Documentation/netlink/specs/fou.yaml index 0af5ab842c04..57735726262e 100644 --- a/Documentation/netlink/specs/fou.yaml +++ b/Documentation/netlink/specs/fou.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: fou protocol: genetlink-legacy @@ -15,10 +15,10 @@ kernel-policy: global definitions: - type: enum - name: encap_type + name: encap-type name-prefix: fou-encap- enum-name: - entries: [ unspec, direct, gue ] + entries: [unspec, direct, gue] attribute-sets: - @@ -43,26 +43,26 @@ attribute-sets: name: type type: u8 - - name: remcsum_nopartial + name: remcsum-nopartial type: flag - - name: local_v4 + name: local-v4 type: u32 - - name: local_v6 + name: local-v6 type: binary checks: min-len: 16 - - name: peer_v4 + name: peer-v4 type: u32 - - name: peer_v6 + name: peer-v6 type: binary checks: min-len: 16 - - name: peer_port + name: peer-port type: u16 byte-order: big-endian - @@ -81,8 +81,8 @@ operations: doc: Add port. attribute-set: fou - dont-validate: [ strict, dump ] - flags: [ admin-perm ] + dont-validate: [strict, dump] + flags: [admin-perm] do: request: &all_attrs @@ -90,12 +90,12 @@ operations: - port - ipproto - type - - remcsum_nopartial - - local_v4 - - peer_v4 - - local_v6 - - peer_v6 - - peer_port + - remcsum-nopartial + - local-v4 + - peer-v4 + - local-v6 + - peer-v6 + - peer-port - ifindex - @@ -103,8 +103,8 @@ operations: doc: Delete port. attribute-set: fou - dont-validate: [ strict, dump ] - flags: [ admin-perm ] + dont-validate: [strict, dump] + flags: [admin-perm] do: request: &select_attrs @@ -112,17 +112,17 @@ operations: - af - ifindex - port - - peer_port - - local_v4 - - peer_v4 - - local_v6 - - peer_v6 + - peer-port + - local-v4 + - peer-v4 + - local-v6 + - peer-v6 - name: get doc: Get tunnel info. attribute-set: fou - dont-validate: [ strict, dump ] + dont-validate: [strict, dump] do: request: *select_attrs diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index b934cc513e3d..95c3fade7a8d 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -4,7 +4,7 @@ # # Copyright (c) 2023, Oracle and/or its affiliates. # - +--- name: handshake protocol: genetlink @@ -16,17 +16,17 @@ definitions: type: enum name: handler-class value-start: 0 - entries: [ none, tlshd, max ] + entries: [none, tlshd, max] - type: enum name: msg-type value-start: 0 - entries: [ unspec, clienthello, serverhello ] + entries: [unspec, clienthello, serverhello] - type: enum name: auth value-start: 0 - entries: [ unspec, unauth, psk, x509 ] + entries: [unspec, unauth, psk, x509] attribute-sets: - @@ -71,6 +71,9 @@ attribute-sets: - name: peername type: string + - + name: keyring + type: u32 - name: done attributes: @@ -95,7 +98,7 @@ operations: name: accept doc: Handler retrieves next queued handshake request attribute-set: accept - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -109,6 +112,7 @@ operations: - peer-identity - certificate - peername + - keyring - name: done doc: Handler reports handshake completion diff --git a/Documentation/netlink/specs/lockd.yaml b/Documentation/netlink/specs/lockd.yaml index bbd4da5fe54b..3dc4ac1a051b 100644 --- a/Documentation/netlink/specs/lockd.yaml +++ b/Documentation/netlink/specs/lockd.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: lockd protocol: genetlink uapi-header: linux/lockd_netlink.h @@ -26,7 +26,7 @@ operations: name: server-set doc: set the lockd server parameters attribute-set: server - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: diff --git a/Documentation/netlink/specs/mptcp_pm.yaml b/Documentation/netlink/specs/mptcp_pm.yaml index dfd017780d2f..d15335684ec3 100644 --- a/Documentation/netlink/specs/mptcp_pm.yaml +++ b/Documentation/netlink/specs/mptcp_pm.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: mptcp_pm protocol: genetlink-legacy doc: Multipath TCP. @@ -17,72 +17,72 @@ definitions: enum-name: mptcp-event-type name-prefix: mptcp-event- entries: - - - name: unspec - doc: unused event - - - name: created - doc: >- - A new MPTCP connection has been created. It is the good time to - allocate memory and send ADD_ADDR if needed. Depending on the - traffic-patterns it can take a long time until the - MPTCP_EVENT_ESTABLISHED is sent. - Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport, - dport, server-side. - - - name: established - doc: >- - A MPTCP connection is established (can start new subflows). - Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport, - dport, server-side. - - - name: closed - doc: >- - A MPTCP connection has stopped. - Attribute: token. - - - name: announced - value: 6 - doc: >- - A new address has been announced by the peer. - Attributes: token, rem_id, family, daddr4 | daddr6 [, dport]. - - - name: removed - doc: >- - An address has been lost by the peer. - Attributes: token, rem_id. - - - name: sub-established - value: 10 - doc: >- - A new subflow has been established. 'error' should not be set. - Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | - daddr6, sport, dport, backup, if_idx [, error]. - - - name: sub-closed - doc: >- - A subflow has been closed. An error (copy of sk_err) could be set if an - error has been detected for this subflow. - Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | - daddr6, sport, dport, backup, if_idx [, error]. - - - name: sub-priority - value: 13 - doc: >- - The priority of a subflow has changed. 'error' should not be set. - Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | - daddr6, sport, dport, backup, if_idx [, error]. - - - name: listener-created - value: 15 - doc: >- - A new PM listener is created. - Attributes: family, sport, saddr4 | saddr6. - - - name: listener-closed - doc: >- - A PM listener is closed. - Attributes: family, sport, saddr4 | saddr6. + - + name: unspec + doc: unused event + - + name: created + doc: >- + A new MPTCP connection has been created. It is the good time to + allocate memory and send ADD_ADDR if needed. Depending on the + traffic-patterns it can take a long time until the + MPTCP_EVENT_ESTABLISHED is sent. + Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport, + dport, server-side. + - + name: established + doc: >- + A MPTCP connection is established (can start new subflows). + Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport, + dport, server-side. + - + name: closed + doc: >- + A MPTCP connection has stopped. + Attribute: token. + - + name: announced + value: 6 + doc: >- + A new address has been announced by the peer. + Attributes: token, rem_id, family, daddr4 | daddr6 [, dport]. + - + name: removed + doc: >- + An address has been lost by the peer. + Attributes: token, rem_id. + - + name: sub-established + value: 10 + doc: >- + A new subflow has been established. 'error' should not be set. + Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | + daddr6, sport, dport, backup, if-idx [, error]. + - + name: sub-closed + doc: >- + A subflow has been closed. An error (copy of sk_err) could be set if + an error has been detected for this subflow. + Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | + daddr6, sport, dport, backup, if-idx [, error]. + - + name: sub-priority + value: 13 + doc: >- + The priority of a subflow has changed. 'error' should not be set. + Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | + daddr6, sport, dport, backup, if-idx [, error]. + - + name: listener-created + value: 15 + doc: >- + A new PM listener is created. + Attributes: family, sport, saddr4 | saddr6. + - + name: listener-closed + doc: >- + A PM listener is closed. + Attributes: family, sport, saddr4 | saddr6. attribute-sets: - @@ -255,8 +255,8 @@ attribute-sets: name: timeout type: u32 - - name: if_idx - type: u32 + name: if-idx + type: s32 - name: reset-reason type: u32 @@ -277,8 +277,8 @@ operations: name: add-addr doc: Add endpoint attribute-set: endpoint - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: &add-addr-attrs request: attributes: @@ -287,39 +287,39 @@ operations: name: del-addr doc: Delete endpoint attribute-set: endpoint - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: *add-addr-attrs - name: get-addr doc: Get endpoint information attribute-set: attr - dont-validate: [ strict ] + dont-validate: [strict] do: &get-addr-attrs request: attributes: - - addr - - token + - addr + - token reply: attributes: - - addr + - addr dump: reply: - attributes: - - addr + attributes: + - addr - name: flush-addrs doc: Flush addresses attribute-set: endpoint - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: *add-addr-attrs - name: set-limits doc: Set protocol limits attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: &mptcp-limits request: attributes: @@ -329,10 +329,10 @@ operations: name: get-limits doc: Get protocol limits attribute-set: attr - dont-validate: [ strict ] + dont-validate: [strict] do: &mptcp-get-limits request: - attributes: + attributes: - rcv-add-addrs - subflows reply: @@ -343,8 +343,8 @@ operations: name: set-flags doc: Change endpoint flags attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: &mptcp-set-flags request: attributes: @@ -355,8 +355,8 @@ operations: name: announce doc: Announce new address attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: &announce-add request: attributes: @@ -366,19 +366,19 @@ operations: name: remove doc: Announce removal attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: request: - attributes: - - token - - loc-id + attributes: + - token + - loc-id - name: subflow-create doc: Create subflow attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: &sf-create request: attributes: @@ -389,6 +389,6 @@ operations: name: subflow-destroy doc: Destroy subflow attribute-set: attr - dont-validate: [ strict ] - flags: [ uns-admin-perm ] + dont-validate: [strict] + flags: [uns-admin-perm] do: *sf-create diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml index 8ebad0d02904..0b1b54be48f9 100644 --- a/Documentation/netlink/specs/net_shaper.yaml +++ b/Documentation/netlink/specs/net_shaper.yaml @@ -1,4 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +--- name: net-shaper doc: | @@ -243,7 +244,7 @@ operations: The set operation can't be used to create a @node scope shaper, use the @group operation instead. attribute-set: net-shaper - flags: [ admin-perm ] + flags: [admin-perm] do: pre: net-shaper-nl-pre-doit @@ -274,7 +275,7 @@ operations: node with infinite bandwidth. The queue's implicit node feeds an implicit RR node at the root of the hierarchy. attribute-set: net-shaper - flags: [ admin-perm ] + flags: [admin-perm] do: pre: net-shaper-nl-pre-doit @@ -305,7 +306,7 @@ operations: full identifier, comprising @binding and @handle, is provided as the reply. attribute-set: net-shaper - flags: [ admin-perm ] + flags: [admin-perm] do: pre: net-shaper-nl-pre-doit diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml index c0ef6d0d7786..c035dc0f64fd 100644 --- a/Documentation/netlink/specs/netdev.yaml +++ b/Documentation/netlink/specs/netdev.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: netdev doc: @@ -31,7 +31,7 @@ definitions: - name: hw-offload doc: - This feature informs if netdev supports XDP hw offloading. + This feature informs if netdev supports XDP hw offloading. - name: rx-sg doc: @@ -48,16 +48,19 @@ definitions: entries: - name: timestamp - doc: - Device is capable of exposing receive HW timestamp via bpf_xdp_metadata_rx_timestamp(). + doc: | + Device is capable of exposing receive HW timestamp via + bpf_xdp_metadata_rx_timestamp(). - name: hash - doc: - Device is capable of exposing receive packet hash via bpf_xdp_metadata_rx_hash(). + doc: | + Device is capable of exposing receive packet hash via + bpf_xdp_metadata_rx_hash(). - name: vlan-tag - doc: - Device is capable of exposing receive packet VLAN tag via bpf_xdp_metadata_rx_vlan_tag(). + doc: | + Device is capable of exposing receive packet VLAN tag via + bpf_xdp_metadata_rx_vlan_tag(). - type: flags name: xsk-flags @@ -77,11 +80,15 @@ definitions: - name: queue-type type: enum - entries: [ rx, tx ] + entries: [rx, tx] - name: qstats-scope type: flags - entries: [ queue ] + entries: [queue] + - + name: napi-threaded + type: enum + entries: [disabled, enabled] attribute-sets: - @@ -205,7 +212,7 @@ attribute-sets: - name: alloc-fast type: uint - value: 8 # reserve some attr ids in case we need more metadata later + value: 8 # reserve some attr ids in case we need more metadata later - name: alloc-slow type: uint @@ -280,6 +287,13 @@ attribute-sets: doc: The timeout, in nanoseconds, of how long to suspend irq processing, if event polling finds events type: uint + - + name: threaded + doc: Whether the NAPI is configured to operate in threaded polling + mode. If this is set to enabled then the NAPI context operates + in threaded polling mode. + type: u32 + enum: napi-threaded - name: xsk-info attributes: [] @@ -367,7 +381,7 @@ attribute-sets: For drivers supporting XDP, XDP is considered the first layer of the stack, so packets consumed by XDP are still counted here. type: uint - value: 8 # reserve some attr ids in case we need more metadata later + value: 8 # reserve some attr ids in case we need more metadata later - name: rx-bytes doc: Successfully received bytes, see `rx-packets`. @@ -425,9 +439,9 @@ attribute-sets: - name: rx-hw-gro-packets doc: | - Number of packets that were coalesced from smaller packets by the device. - Counts only packets coalesced with the HW-GRO netdevice feature, - LRO-coalesced packets are not counted. + Number of packets that were coalesced from smaller packets by the + device. Counts only packets coalesced with the HW-GRO netdevice + feature, LRO-coalesced packets are not counted. type: uint - name: rx-hw-gro-bytes @@ -436,8 +450,8 @@ attribute-sets: - name: rx-hw-gro-wire-packets doc: | - Number of packets that were coalesced to bigger packetss with the HW-GRO - netdevice feature. LRO-coalesced packets are not counted. + Number of packets that were coalesced to bigger packetss with the + HW-GRO netdevice feature. LRO-coalesced packets are not counted. type: uint - name: rx-hw-gro-wire-bytes @@ -691,6 +705,7 @@ operations: - defer-hard-irqs - gro-flush-timeout - irq-suspend-timeout + - threaded dump: request: attributes: @@ -721,7 +736,7 @@ operations: name: bind-rx doc: Bind dmabuf to netdev attribute-set: dmabuf - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -735,7 +750,7 @@ operations: name: napi-set doc: Set configurable NAPI instance settings. attribute-set: napi - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -743,6 +758,7 @@ operations: - defer-hard-irqs - gro-flush-timeout - irq-suspend-timeout + - threaded - name: bind-tx doc: Bind dmabuf to netdev for TX @@ -757,7 +773,7 @@ operations: - id kernel-family: - headers: [ "net/netdev_netlink.h"] + headers: ["net/netdev_netlink.h"] sock-priv: struct netdev_nl_sock mcast-groups: diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index c87658114852..100363029e82 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: nfsd protocol: genetlink uapi-header: linux/nfsd_netlink.h @@ -27,7 +27,7 @@ attribute-sets: name: proc type: u32 - - name: service_time + name: service-time type: s64 - name: pad @@ -139,7 +139,7 @@ operations: - prog - version - proc - - service_time + - service-time - saddr4 - daddr4 - saddr6 @@ -151,7 +151,7 @@ operations: name: threads-set doc: set the number of running threads attribute-set: server - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -174,7 +174,7 @@ operations: name: version-set doc: set nfs enabled versions attribute-set: server-proto - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -191,7 +191,7 @@ operations: name: listener-set doc: set nfs running sockets attribute-set: server-sock - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: @@ -208,7 +208,7 @@ operations: name: pool-mode-set doc: set the current server pool-mode attribute-set: pool-mode - flags: [ admin-perm ] + flags: [admin-perm] do: request: attributes: diff --git a/Documentation/netlink/specs/nftables.yaml b/Documentation/netlink/specs/nftables.yaml index bd938bd01b6b..2ee10d92d644 100644 --- a/Documentation/netlink/specs/nftables.yaml +++ b/Documentation/netlink/specs/nftables.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: nftables protocol: netlink-raw protonum: 12 @@ -1205,7 +1205,9 @@ operations: - name - name: destroytable - doc: Delete an existing table with destroy semantics (ignoring ENOENT errors). + doc: | + Delete an existing table with destroy semantics (ignoring ENOENT + errors). attribute-set: table-attrs fixed-header: nfgenmsg do: @@ -1249,7 +1251,9 @@ operations: - name - name: destroychain - doc: Delete an existing chain with destroy semantics (ignoring ENOENT errors). + doc: | + Delete an existing chain with destroy semantics (ignoring ENOENT + errors). attribute-set: chain-attrs fixed-header: nfgenmsg do: @@ -1307,7 +1311,8 @@ operations: - name - name: destroyrule - doc: Delete an existing rule with destroy semantics (ignoring ENOENT errors). + doc: | + Delete an existing rule with destroy semantics (ignoring ENOENT errors). attribute-set: rule-attrs fixed-header: nfgenmsg do: @@ -1351,7 +1356,8 @@ operations: - name - name: destroyset - doc: Delete an existing set with destroy semantics (ignoring ENOENT errors). + doc: | + Delete an existing set with destroy semantics (ignoring ENOENT errors). attribute-set: set-attrs fixed-header: nfgenmsg do: diff --git a/Documentation/netlink/specs/nl80211.yaml b/Documentation/netlink/specs/nl80211.yaml index 3611b11a7d8f..610fdd5e000e 100644 --- a/Documentation/netlink/specs/nl80211.yaml +++ b/Documentation/netlink/specs/nl80211.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: nl80211 protocol: genetlink-legacy @@ -285,7 +285,7 @@ attribute-sets: type: u16 - name: sta-flags - type: binary # TODO: nest + type: binary # TODO: nest - name: sta-listen-interval type: u16 @@ -297,14 +297,14 @@ attribute-sets: type: u32 - name: sta-info - type: binary # TODO: nest + type: binary # TODO: nest - name: wiphy-bands type: nest nested-attributes: wiphy-bands - name: mntr-flags - type: binary # TODO: nest + type: binary # TODO: nest - name: mesh-id type: binary @@ -317,7 +317,7 @@ attribute-sets: display-hint: mac - name: mpath-info - type: binary # TODO: nest + type: binary # TODO: nest - name: bss-cts-prot type: u8 @@ -339,16 +339,16 @@ attribute-sets: type: binary - name: reg-rules - type: binary # TODO: nest + type: binary # TODO: nest - name: mesh-config - type: binary # TODO: nest + type: binary # TODO: nest - name: bss-basic-rates type: binary - name: wiphy-txq-params - type: binary # TODO: nest + type: binary # TODO: nest - name: wiphy-freq type: u32 @@ -370,16 +370,16 @@ attribute-sets: type: u8 - name: scan-frequencies - type: binary # TODO: nest + type: binary # TODO: nest - name: scan-ssids - type: binary # TODO: nest + type: binary # TODO: nest - name: generation type: u32 - name: bss - type: binary # TODO: nest + type: binary # TODO: nest - name: reg-initiator type: u8 @@ -416,10 +416,10 @@ attribute-sets: display-hint: hex - name: freq-before - type: binary # TODO: nest + type: binary # TODO: nest - name: freq-after - type: binary # TODO: nest + type: binary # TODO: nest - name: freq-fixed type: flag @@ -483,10 +483,10 @@ attribute-sets: type: binary - name: key - type: binary # TODO: nest + type: binary # TODO: nest - name: keys - type: binary # TODO: nest + type: binary # TODO: nest - name: pid type: u32 @@ -495,7 +495,7 @@ attribute-sets: type: u8 - name: survey-info - type: binary # TODO: nest + type: binary # TODO: nest - name: pmkid type: binary @@ -513,7 +513,7 @@ attribute-sets: type: u8 - name: tx-rates - type: binary # TODO: nest + type: binary # TODO: nest - name: frame-match type: binary @@ -525,7 +525,7 @@ attribute-sets: type: u32 - name: cqm - type: binary # TODO: nest + type: binary # TODO: nest - name: local-state-change type: flag @@ -575,13 +575,13 @@ attribute-sets: type: u16 - name: key-default-types - type: binary # TODO: nest + type: binary # TODO: nest - name: max-remain-on-channel-duration type: u32 - name: mesh-setup - type: binary # TODO: nest + type: binary # TODO: nest - name: wiphy-antenna-avail-tx type: u32 @@ -596,7 +596,7 @@ attribute-sets: type: u8 - name: wowlan-triggers - type: binary # TODO: nest + type: binary # TODO: nest - name: wowlan-triggers-supported type: nest @@ -615,7 +615,7 @@ attribute-sets: nested-attributes: supported-iftypes - name: rekey-data - type: binary # TODO: nest + type: binary # TODO: nest - name: max-num-sched-scan-ssids type: u8 @@ -624,7 +624,7 @@ attribute-sets: type: u16 - name: scan-supp-rates - type: binary # TODO: nest + type: binary # TODO: nest - name: hidden-ssid type: u32 @@ -636,7 +636,7 @@ attribute-sets: type: binary - name: sta-wme - type: binary # TODO: nest + type: binary # TODO: nest - name: support-ap-uapsd type: flag @@ -645,13 +645,13 @@ attribute-sets: type: flag - name: sched-scan-match - type: binary # TODO: nest + type: binary # TODO: nest - name: max-match-sets type: u8 - name: pmksa-candidate - type: binary # TODO: nest + type: binary # TODO: nest - name: tx-no-cck-rate type: flag @@ -680,7 +680,7 @@ attribute-sets: name: feature-flags type: u32 enum: feature-flags - enum-as-flags: True + enum-as-flags: true - name: probe-resp-offload type: u32 @@ -749,7 +749,7 @@ attribute-sets: type: u32 - name: mac-addrs - type: binary # TODO: nest + type: binary # TODO: nest - name: mac-acl-max type: u32 @@ -798,7 +798,7 @@ attribute-sets: type: u16 - name: coalesce-rule - type: binary # TODO: nest + type: binary # TODO: nest - name: ch-switch-count type: u32 @@ -807,7 +807,7 @@ attribute-sets: type: flag - name: csa-ies - type: binary # TODO: nest + type: binary # TODO: nest - name: cntdwn-offs-beacon type: binary @@ -929,13 +929,13 @@ attribute-sets: type: u32 - name: sched-scan-plans - type: binary # TODO: nest + type: binary # TODO: nest - name: pbss type: flag - name: bss-select - type: binary # TODO: nest + type: binary # TODO: nest - name: sta-support-p2p-ps type: u8 @@ -944,7 +944,7 @@ attribute-sets: type: binary - name: iftype-ext-capa - type: binary # TODO: nest + type: binary # TODO: nest - name: mu-mimo-group-data type: binary @@ -975,10 +975,10 @@ attribute-sets: type: u32 - name: nan-func - type: binary # TODO: nest + type: binary # TODO: nest - name: nan-match - type: binary # TODO: nest + type: binary # TODO: nest - name: fils-kek type: binary @@ -1067,16 +1067,16 @@ attribute-sets: type: binary - name: ftm-responder - type: binary # TODO: nest + type: binary # TODO: nest - name: ftm-responder-stats - type: binary # TODO: nest + type: binary # TODO: nest - name: timeout type: u32 - name: peer-measurements - type: binary # TODO: nest + type: binary # TODO: nest - name: airtime-weight type: u16 @@ -1094,7 +1094,7 @@ attribute-sets: type: flag - name: he-obss-pd - type: binary # TODO: nest + type: binary # TODO: nest - name: wiphy-edmg-channels type: u8 @@ -1106,13 +1106,13 @@ attribute-sets: type: u16 - name: he-bss-color - type: binary # TODO: nest + type: binary # TODO: nest - name: iftype-akm-suites - type: binary # TODO: nest + type: binary # TODO: nest - name: tid-config - type: binary # TODO: nest + type: binary # TODO: nest - name: control-port-no-preauth type: flag @@ -1133,16 +1133,16 @@ attribute-sets: type: u32 - name: scan-freq-khz - type: binary # TODO: nest + type: binary # TODO: nest - name: he-6ghz-capability type: binary - name: fils-discovery - type: binary # TOOD: nest + type: binary # TOOD: nest - name: unsol-bcast-probe-resp - type: binary # TOOD: nest + type: binary # TOOD: nest - name: s1g-capability type: binary @@ -1173,13 +1173,13 @@ attribute-sets: type: u8 - name: color-change-elems - type: binary # TODO: nest + type: binary # TODO: nest - name: mbssid-config - type: binary # TODO: nest + type: binary # TODO: nest - name: mbssid-elems - type: binary # TODO: nest + type: binary # TODO: nest - name: radar-background type: flag @@ -1194,7 +1194,7 @@ attribute-sets: type: flag - name: mlo-links - type: binary # TODO: nest + type: binary # TODO: nest - name: mlo-link-id type: u8 @@ -1234,7 +1234,7 @@ attribute-sets: type: flag - name: ema-rnr-elems - type: binary # TODO: nest + type: binary # TODO: nest - name: mlo-link-disabled type: flag @@ -1252,10 +1252,10 @@ attribute-sets: type: flag - name: wiphy-radios - type: binary # TODO: nest + type: binary # TODO: nest - name: wiphy-interface-combinations - type: binary # TODO: nest + type: binary # TODO: nest - name: vif-radio-mask type: u32 @@ -1799,8 +1799,9 @@ operations: - name: get-wiphy doc: | - Get information about a wiphy or dump a list of all wiphys. Requests to dump get-wiphy - should unconditionally include the split-wiphy-dump flag in the request. + Get information about a wiphy or dump a list of all wiphys. Requests to + dump get-wiphy should unconditionally include the split-wiphy-dump flag + in the request. attribute-set: nl80211-attrs do: request: diff --git a/Documentation/netlink/specs/nlctrl.yaml b/Documentation/netlink/specs/nlctrl.yaml index a36535350bdb..8b4472a6aa36 100644 --- a/Documentation/netlink/specs/nlctrl.yaml +++ b/Documentation/netlink/specs/nlctrl.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: nlctrl protocol: genetlink-legacy uapi-header: linux/genetlink.h @@ -76,12 +76,12 @@ attribute-sets: - name: policy type: nest-type-value - type-value: [ policy-id, attr-id ] + type-value: [policy-id, attr-id] nested-attributes: policy-attrs - name: op-policy type: nest-type-value - type-value: [ op-id ] + type-value: [op-id] nested-attributes: op-policy-attrs - name: op diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index 096c51f0c69a..1b91045cee2e 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -4,7 +4,7 @@ # # Copyright (c) 2024-2025, OpenVPN Inc. # - +--- name: ovpn protocol: genetlink @@ -19,7 +19,7 @@ definitions: - type: enum name: cipher-alg - entries: [ none, aes-gcm, chacha20-poly1305 ] + entries: [none, aes-gcm, chacha20-poly1305] - type: enum name: del-peer-reason @@ -32,7 +32,7 @@ definitions: - type: enum name: key-slot - entries: [ primary, secondary ] + entries: [primary, secondary] attribute-sets: - @@ -42,8 +42,8 @@ attribute-sets: name: id type: u32 doc: >- - The unique ID of the peer in the device context. To be used to identify - peers during operations for a specific device + The unique ID of the peer in the device context. To be used to + identify peers during operations for a specific device checks: max: 0xFFFFFF - @@ -160,6 +160,66 @@ attribute-sets: name: link-tx-packets type: uint doc: Number of packets transmitted at the transport level + - + name: peer-new-input + subset-of: peer + attributes: + - + name: id + - + name: remote-ipv4 + - + name: remote-ipv6 + - + name: remote-ipv6-scope-id + - + name: remote-port + - + name: socket + - + name: vpn-ipv4 + - + name: vpn-ipv6 + - + name: local-ipv4 + - + name: local-ipv6 + - + name: keepalive-interval + - + name: keepalive-timeout + - + name: peer-set-input + subset-of: peer + attributes: + - + name: id + - + name: remote-ipv4 + - + name: remote-ipv6 + - + name: remote-ipv6-scope-id + - + name: remote-port + - + name: vpn-ipv4 + - + name: vpn-ipv6 + - + name: local-ipv4 + - + name: local-ipv6 + - + name: keepalive-interval + - + name: keepalive-timeout + - + name: peer-del-input + subset-of: peer + attributes: + - + name: id - name: keyconf attributes: @@ -216,6 +276,33 @@ attribute-sets: obtain the actual cipher IV checks: exact-len: nonce-tail-size + + - + name: keyconf-get + subset-of: keyconf + attributes: + - + name: peer-id + - + name: slot + - + name: key-id + - + name: cipher-alg + - + name: keyconf-swap-input + subset-of: keyconf + attributes: + - + name: peer-id + - + name: keyconf-del-input + subset-of: keyconf + attributes: + - + name: peer-id + - + name: slot - name: ovpn attributes: @@ -235,13 +322,67 @@ attribute-sets: type: nest doc: Peer specific cipher configuration nested-attributes: keyconf + - + name: ovpn-peer-new-input + subset-of: ovpn + attributes: + - + name: ifindex + - + name: peer + nested-attributes: peer-new-input + - + name: ovpn-peer-set-input + subset-of: ovpn + attributes: + - + name: ifindex + - + name: peer + nested-attributes: peer-set-input + - + name: ovpn-peer-del-input + subset-of: ovpn + attributes: + - + name: ifindex + - + name: peer + nested-attributes: peer-del-input + - + name: ovpn-keyconf-get + subset-of: ovpn + attributes: + - + name: ifindex + - + name: keyconf + nested-attributes: keyconf-get + - + name: ovpn-keyconf-swap-input + subset-of: ovpn + attributes: + - + name: ifindex + - + name: keyconf + nested-attributes: keyconf-swap-input + - + name: ovpn-keyconf-del-input + subset-of: ovpn + attributes: + - + name: ifindex + - + name: keyconf + nested-attributes: keyconf-del-input operations: list: - name: peer-new - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-peer-new-input + flags: [admin-perm] doc: Add a remote peer do: pre: ovpn-nl-pre-doit @@ -252,8 +393,8 @@ operations: - peer - name: peer-set - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-peer-set-input + flags: [admin-perm] doc: modify a remote peer do: pre: ovpn-nl-pre-doit @@ -265,7 +406,7 @@ operations: - name: peer-get attribute-set: ovpn - flags: [ admin-perm ] + flags: [admin-perm] doc: Retrieve data about existing remote peers (or a specific one) do: pre: ovpn-nl-pre-doit @@ -286,8 +427,8 @@ operations: - peer - name: peer-del - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-peer-del-input + flags: [admin-perm] doc: Delete existing remote peer do: pre: ovpn-nl-pre-doit @@ -305,7 +446,7 @@ operations: - name: key-new attribute-set: ovpn - flags: [ admin-perm ] + flags: [admin-perm] doc: Add a cipher key for a specific peer do: pre: ovpn-nl-pre-doit @@ -316,8 +457,8 @@ operations: - keyconf - name: key-get - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-keyconf-get + flags: [admin-perm] doc: Retrieve non-sensitive data about peer key and cipher do: pre: ovpn-nl-pre-doit @@ -331,8 +472,8 @@ operations: - keyconf - name: key-swap - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-keyconf-swap-input + flags: [admin-perm] doc: Swap primary and secondary session keys for a specific peer do: pre: ovpn-nl-pre-doit @@ -350,8 +491,8 @@ operations: mcgrp: peers - name: key-del - attribute-set: ovpn - flags: [ admin-perm ] + attribute-set: ovpn-keyconf-del-input + flags: [admin-perm] doc: Delete cipher key for a specific peer do: pre: ovpn-nl-pre-doit diff --git a/Documentation/netlink/specs/ovs_datapath.yaml b/Documentation/netlink/specs/ovs_datapath.yaml index df6a8f94975e..0c0abf3f9f05 100644 --- a/Documentation/netlink/specs/ovs_datapath.yaml +++ b/Documentation/netlink/specs/ovs_datapath.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: ovs_datapath version: 2 protocol: genetlink-legacy diff --git a/Documentation/netlink/specs/ovs_flow.yaml b/Documentation/netlink/specs/ovs_flow.yaml index 46f5d1cd8a5f..2dac9c8add57 100644 --- a/Documentation/netlink/specs/ovs_flow.yaml +++ b/Documentation/netlink/specs/ovs_flow.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: ovs_flow version: 1 protocol: genetlink-legacy @@ -216,7 +216,7 @@ definitions: type: struct members: - - name: nd_target + name: nd-target type: binary len: 16 byte-order: big-endian @@ -258,12 +258,12 @@ definitions: type: struct members: - - name: vlan_tpid + name: vlan-tpid type: u16 byte-order: big-endian doc: Tag protocol identifier (TPID) to push. - - name: vlan_tci + name: vlan-tci type: u16 byte-order: big-endian doc: Tag control identifier (TCI) to push. @@ -293,9 +293,10 @@ definitions: enum-name: ovs-hash-alg type: enum doc: | - Data path hash algorithm for computing Datapath hash. The algorithm type only specifies - the fields in a flow will be used as part of the hash. Each datapath is free to use its - own hash algorithm. The hash value will be opaque to the user space daemon. + Data path hash algorithm for computing Datapath hash. The algorithm type + only specifies the fields in a flow will be used as part of the hash. Each + datapath is free to use its own hash algorithm. The hash value will be + opaque to the user space daemon. entries: - ovs-hash-alg-l4 @@ -615,7 +616,9 @@ attribute-sets: name: set type: nest nested-attributes: key-attrs - doc: Replaces the contents of an existing header. The single nested attribute specifies a header to modify and its value. + doc: | + Replaces the contents of an existing header. The single nested + attribute specifies a header to modify and its value. - name: push-vlan type: binary @@ -630,7 +633,8 @@ attribute-sets: type: nest nested-attributes: sample-attrs doc: | - Probabilistically executes actions, as specified in the nested attributes. + Probabilistically executes actions, as specified in the nested + attributes. - name: recirc type: u32 diff --git a/Documentation/netlink/specs/ovs_vport.yaml b/Documentation/netlink/specs/ovs_vport.yaml index 306da6bb842d..da47e65fd574 100644 --- a/Documentation/netlink/specs/ovs_vport.yaml +++ b/Documentation/netlink/specs/ovs_vport.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: ovs_vport version: 2 protocol: genetlink-legacy @@ -21,7 +21,7 @@ definitions: type: enum enum-name: ovs-vport-type name-prefix: ovs-vport-type- - entries: [ unspec, netdev, internal, gre, vxlan, geneve ] + entries: [unspec, netdev, internal, gre, vxlan, geneve] - name: ovs-vport-stats type: struct diff --git a/Documentation/netlink/specs/rt-addr.yaml b/Documentation/netlink/specs/rt-addr.yaml index 4f86aa1075da..bafe3bfeabfb 100644 --- a/Documentation/netlink/specs/rt-addr.yaml +++ b/Documentation/netlink/specs/rt-addr.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: rt-addr protocol: netlink-raw uapi-header: linux/rtnetlink.h diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index b41b31eebcae..210394c188a3 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: rt-link protocol: netlink-raw uapi-header: linux/rtnetlink.h @@ -603,7 +603,7 @@ definitions: name: optmask type: u32 - - name: if_stats_msg + name: if-stats-msg type: struct members: - @@ -2486,7 +2486,7 @@ operations: name: getstats doc: Get / dump link stats. attribute-set: stats-attrs - fixed-header: if_stats_msg + fixed-header: if-stats-msg do: request: value: 94 diff --git a/Documentation/netlink/specs/rt-neigh.yaml b/Documentation/netlink/specs/rt-neigh.yaml index e9cba164e3d1..30a9ee16f128 100644 --- a/Documentation/netlink/specs/rt-neigh.yaml +++ b/Documentation/netlink/specs/rt-neigh.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: rt-neigh protocol: netlink-raw uapi-header: linux/rtnetlink.h @@ -79,6 +79,7 @@ definitions: entries: - managed - locked + - ext-validated - name: rtm-type type: enum diff --git a/Documentation/netlink/specs/rt-route.yaml b/Documentation/netlink/specs/rt-route.yaml index 800f3a823d47..5b514ddeff1d 100644 --- a/Documentation/netlink/specs/rt-route.yaml +++ b/Documentation/netlink/specs/rt-route.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: rt-route protocol: netlink-raw uapi-header: linux/rtnetlink.h @@ -117,7 +117,7 @@ attribute-sets: name: multipath type: binary - - name: protoinfo # not used + name: protoinfo # not used type: binary - name: flow @@ -127,10 +127,10 @@ attribute-sets: type: binary struct: rta-cacheinfo - - name: session # not used + name: session # not used type: binary - - name: mp-algo # not used + name: mp-algo # not used type: binary - name: table @@ -155,7 +155,7 @@ attribute-sets: type: u16 - name: encap - type: binary # tunnel specific nest + type: binary # tunnel specific nest - name: expires type: u32 diff --git a/Documentation/netlink/specs/rt-rule.yaml b/Documentation/netlink/specs/rt-rule.yaml index 003707ca4a3e..46b1d426e7e8 100644 --- a/Documentation/netlink/specs/rt-rule.yaml +++ b/Documentation/netlink/specs/rt-rule.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: rt-rule protocol: netlink-raw uapi-header: linux/fib_rules.h diff --git a/Documentation/netlink/specs/tc.yaml b/Documentation/netlink/specs/tc.yaml index cb7ea7d62e56..b1afc7ab3539 100644 --- a/Documentation/netlink/specs/tc.yaml +++ b/Documentation/netlink/specs/tc.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: tc protocol: netlink-raw uapi-header: linux/pkt_cls.h @@ -56,6 +56,23 @@ definitions: - tundf - tunoam - tuncrit + - + name: dualpi2-drop-overload + type: enum + entries: [overflow, drop] + - + name: dualpi2-drop-early + type: enum + entries: [drop-dequeue, drop-enqueue] + - + name: dualpi2-ecn-mask + type: enum + value-start: 1 + entries: [l4s-ect, cla-ect, any-ect] + - + name: dualpi2-split-gso + type: enum + entries: [no-split-gso, split-gso] - name: tc-stats type: struct @@ -76,7 +93,8 @@ definitions: name: overlimits type: u32 doc: | - Number of throttle events when this flow goes out of allocated bandwidth + Number of throttle events when this flow goes out of allocated + bandwidth - name: bps type: u32 @@ -232,7 +250,7 @@ definitions: type: u8 doc: log(P_max / (qth-max - qth-min)) - - name: Scell_log + name: Scell-log type: u8 doc: cell size for idle damping - @@ -253,7 +271,7 @@ definitions: name: DPs type: u32 - - name: def_DP + name: def-DP type: u32 - name: grio @@ -751,7 +769,9 @@ definitions: - name: count type: u32 - doc: How many drops we've done since the last time we entered dropping state + doc: | + How many drops we've done since the last time we entered dropping + state - name: lastcount type: u32 @@ -822,6 +842,58 @@ definitions: - name: drop-overmemory type: u32 + - + name: tc-dualpi2-xstats + type: struct + members: + - + name: prob + type: u32 + doc: Current base PI probability + - + name: delay-c + type: u32 + doc: Current C-queue delay in microseconds + - + name: delay-l + type: u32 + doc: Current L-queue delay in microseconds + - + name: pkts-in-c + type: u32 + doc: Number of packets enqueued in the C-queue + - + name: pkts-in-l + type: u32 + doc: Number of packets enqueued in the L-queue + - + name: maxq + type: u32 + doc: Maximum number of packets seen by the DualPI2 + - + name: ecn-mark + type: u32 + doc: All packets marked with ECN + - + name: step-mark + type: u32 + doc: Only packets marked with ECN due to L-queue step AQM + - + name: credit + type: s32 + doc: Current credit value for WRR + - + name: memory-used + type: u32 + doc: Memory used in bytes by the DualPI2 + - + name: max-memory-used + type: u32 + doc: Maximum memory used in bytes by the DualPI2 + - + name: memory-limit + type: u32 + doc: Memory limit in bytes - name: tc-fq-pie-xstats type: struct @@ -845,7 +917,7 @@ definitions: - name: ecn-mark type: u32 - doc: Packets marked with ecn + doc: Packets marked with ECN - name: new-flow-count type: u32 @@ -988,7 +1060,7 @@ definitions: - name: ecn-mark type: u32 - doc: Packets marked with ecn + doc: Packets marked with ECN - name: tc-red-xstats type: struct @@ -1161,7 +1233,7 @@ definitions: - name: keys type: binary - struct: tc-u32-key # TODO: array + struct: tc-u32-key # TODO: array - name: tc-u32-pcnt type: struct @@ -1174,7 +1246,7 @@ definitions: type: u64 - name: kcnts - type: u64 # TODO: array + type: u64 # TODO: array - name: tcf-t type: struct @@ -1336,7 +1408,7 @@ definitions: - name: keys type: binary - struct: tc-pedit-key # TODO: array + struct: tc-pedit-key # TODO: array - name: tc-pedit-key type: struct @@ -2281,6 +2353,78 @@ attribute-sets: - name: quantum type: u32 + - + name: dualpi2-attrs + name-prefix: tca-dualpi2- + attributes: + - + name: limit + type: u32 + doc: Limit of total number of packets in queue + - + name: memory-limit + type: u32 + doc: Memory limit of total number of packets in queue + - + name: target + type: u32 + doc: Classic target delay in microseconds + - + name: tupdate + type: u32 + doc: Drop probability update interval time in microseconds + - + name: alpha + type: u32 + doc: Integral gain factor in Hz for PI controller + - + name: beta + type: u32 + doc: Proportional gain factor in Hz for PI controller + - + name: step-thresh-pkts + type: u32 + doc: L4S step marking threshold in packets + - + name: step-thresh-us + type: u32 + doc: L4S Step marking threshold in microseconds + - + name: min-qlen-step + type: u32 + doc: Packets enqueued to the L-queue can apply the step threshold + when the queue length of L-queue is larger than this value. + (0 is recommended) + - + name: coupling + type: u8 + doc: Probability coupling factor between Classic and L4S + (2 is recommended) + - + name: drop-overload + type: u8 + doc: Control the overload strategy (drop to preserve latency or + let the queue overflow) + enum: dualpi2-drop-overload + - + name: drop-early + type: u8 + doc: Decide where the Classic packets are PI-based dropped or marked + enum: dualpi2-drop-early + - + name: c-protection + type: u8 + doc: Classic WRR weight in percentage (from 0 to 100) + - + name: ecn-mask + type: u8 + doc: Configure the L-queue ECN classifier + enum: dualpi2-ecn-mask + - + name: split-gso + type: u8 + doc: Split aggregated skb or not + enum: dualpi2-split-gso - name: ematch-attrs name-prefix: tca-ematch- @@ -2885,7 +3029,7 @@ attribute-sets: attributes: - name: parms - type: binary # array of struct: tc-gred-qopt + type: binary # array of struct: tc-gred-qopt - name: stab type: binary @@ -3335,10 +3479,10 @@ attribute-sets: struct: tc-police - name: rate - type: binary # TODO + type: binary # TODO - name: peakrate - type: binary # TODO + type: binary # TODO - name: avrate type: u32 @@ -3698,13 +3842,16 @@ sub-messages: value: choke attribute-set: choke-attrs - - value: clsact # no content + value: clsact # no content - value: codel attribute-set: codel-attrs - value: drr attribute-set: drr-attrs + - + value: dualpi2 + attribute-set: dualpi2-attrs - value: etf attribute-set: etf-attrs @@ -3742,12 +3889,12 @@ sub-messages: value: htb attribute-set: htb-attrs - - value: ingress # no content + value: ingress # no content - value: matchall attribute-set: matchall-attrs - - value: mq # no content + value: mq # no content - value: mqprio fixed-header: tc-mqprio-qopt @@ -3872,6 +4019,9 @@ sub-messages: - value: codel fixed-header: tc-codel-xstats + - + value: dualpi2 + fixed-header: tc-dualpi2-xstats - value: fq fixed-header: tc-fq-qd-stats diff --git a/Documentation/netlink/specs/tcp_metrics.yaml b/Documentation/netlink/specs/tcp_metrics.yaml index 1bd94f43e526..13144aeed31a 100644 --- a/Documentation/netlink/specs/tcp_metrics.yaml +++ b/Documentation/netlink/specs/tcp_metrics.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: tcp_metrics protocol: genetlink-legacy @@ -133,7 +133,7 @@ operations: doc: Retrieve metrics. attribute-set: tcp-metrics - dont-validate: [ strict, dump ] + dont-validate: [strict, dump] do: request: &sel_attrs @@ -162,8 +162,8 @@ operations: doc: Delete metrics. attribute-set: tcp-metrics - dont-validate: [ strict, dump ] - flags: [ admin-perm ] + dont-validate: [strict, dump] + flags: [admin-perm] do: request: *sel_attrs diff --git a/Documentation/netlink/specs/team.yaml b/Documentation/netlink/specs/team.yaml index c13529e011c9..cf02d47d12a4 100644 --- a/Documentation/netlink/specs/team.yaml +++ b/Documentation/netlink/specs/team.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) - +--- name: team protocol: genetlink-legacy @@ -152,7 +152,7 @@ operations: doc: No operation value: 0 attribute-set: team - dont-validate: [ strict ] + dont-validate: [strict] do: # Actually it only reply the team netlink family @@ -164,8 +164,8 @@ operations: name: options-set doc: Set team options attribute-set: team - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: request: &option_attrs @@ -178,8 +178,8 @@ operations: name: options-get doc: Get team options info attribute-set: team - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: request: @@ -191,8 +191,8 @@ operations: name: port-list-get doc: Get team ports info attribute-set: team - dont-validate: [ strict ] - flags: [ admin-perm ] + dont-validate: [strict] + flags: [admin-perm] do: request: diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst index dceeb0d763aa..50d92084a49c 100644 --- a/Documentation/networking/af_xdp.rst +++ b/Documentation/networking/af_xdp.rst @@ -209,13 +209,10 @@ Libbpf Libbpf is a helper library for eBPF and XDP that makes using these technologies a lot simpler. It also contains specific helper functions -in tools/lib/bpf/xsk.h for facilitating the use of AF_XDP. It -contains two types of functions: those that can be used to make the -setup of AF_XDP socket easier and ones that can be used in the data -plane to access the rings safely and quickly. To see an example on how -to use this API, please take a look at the sample application in -samples/bpf/xdpsock_usr.c which uses libbpf for both setup and data -plane operations. +in tools/testing/selftests/bpf/xsk.h for facilitating the use of +AF_XDP. It contains two types of functions: those that can be used to +make the setup of AF_XDP socket easier and ones that can be used in the +data plane to access the rings safely and quickly. We recommend that you use this library unless you have become a power user. It will make your program a lot simpler. @@ -372,9 +369,8 @@ needs to explicitly notify the kernel to send any packets put on the TX ring. This can be accomplished either by a poll() call, as in the RX path, or by calling sendto(). -An example of how to use this flag can be found in -samples/bpf/xdpsock_user.c. An example with the use of libbpf helpers -would look like this for the TX path: +An example with the use of libbpf helpers would look like this for the +TX path: .. code-block:: c @@ -442,6 +438,15 @@ is created by a privileged process and passed to a non-privileged one. Once the option is set, kernel will refuse attempts to bind that socket to a different interface. Updating the value requires CAP_NET_RAW. +XDP_MAX_TX_SKB_BUDGET setsockopt +-------------------------------- + +This setsockopt sets the maximum number of descriptors that can be handled +and passed to the driver at one send syscall. It is applied in the copy +mode to allow application to tune the per-socket maximum iteration for +better throughput and less frequency of send syscall. +Allowed range is [32, xs->tx->nentries]. + XDP_STATISTICS getsockopt ------------------------- @@ -549,12 +554,12 @@ later in this document. Usage ----- -In order to use AF_XDP sockets two parts are needed. The -user-space application and the XDP program. For a complete setup and -usage example, please refer to the sample application. The user-space -side is xdpsock_user.c and the XDP side is part of libbpf. +In order to use AF_XDP sockets two parts are needed. The user-space +application and the XDP program. For a complete setup and usage example, +please refer to the xdp-project at +https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example. -The XDP code sample included in tools/lib/bpf/xsk.c is the following: +The XDP code sample is the following: .. code-block:: c @@ -752,11 +757,12 @@ to facilitate extending a zero-copy driver with multi-buffer support. Sample application ================== - -There is a xdpsock benchmarking/test application included that -demonstrates how to use AF_XDP sockets with private UMEMs. Say that -you would like your UDP traffic from port 4242 to end up in queue 16, -that we will enable AF_XDP on. Here, we use ethtool for this:: +There is a xdpsock benchmarking/test application that can be found at +https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example +that demonstrates how to use AF_XDP sockets with private +UMEMs. Say that you would like your UDP traffic from port 4242 to end +up in queue 16, that we will enable AF_XDP on. Here, we use ethtool +for this:: ethtool -N p3p2 rx-flow-hash udp4 fn ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \ @@ -773,7 +779,7 @@ can be displayed with "-h", as usual. This sample application uses libbpf to make the setup and usage of AF_XDP simpler. If you want to know how the raw uapi of AF_XDP is really used to make something more advanced, take a look at the libbpf -code in tools/lib/bpf/xsk.[ch]. +code in tools/testing/selftests/bpf/xsk.[ch]. FAQ ======= diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index a4c1291d2561..f8f5766703d4 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -562,6 +562,12 @@ lacp_rate The default is slow. +broadcast_neighbor + + Option specifying whether to broadcast ARP/ND packets to all + active slaves. This option has no effect in modes other than + 802.3ad mode. The default is off (0). + max_bonds Specifies the number of bonding devices to create for this @@ -767,8 +773,9 @@ num_unsol_na greater than 1. The valid range is 0 - 255; the default value is 1. These options - affect only the active-backup mode. These options were added for - bonding versions 3.3.0 and 3.4.0 respectively. + affect the active-backup or 802.3ad (broadcast_neighbor enabled) mode. + These options were added for bonding versions 3.3.0 and 3.4.0 + respectively. From Linux 3.0 and bonding version 3.7.1, these notifications are generated by the ipv4 and ipv6 code and the numbers of diff --git a/Documentation/networking/can.rst b/Documentation/networking/can.rst index b018ce346392..7650c4b5be5f 100644 --- a/Documentation/networking/can.rst +++ b/Documentation/networking/can.rst @@ -742,7 +742,7 @@ The broadcast manager sends responses to user space in the same form: struct timeval ival1, ival2; /* count and subsequent interval */ canid_t can_id; /* unique can_id for task */ __u32 nframes; /* number of can_frames following */ - struct can_frame frames[0]; + struct can_frame frames[]; }; The aligned payload 'frames' uses the same basic CAN frame structure defined @@ -1104,15 +1104,12 @@ for writing CAN network device driver are described below: General Settings ---------------- +CAN network device drivers can use alloc_candev_mqs() and friends instead of +alloc_netdev_mqs(), to automatically take care of CAN-specific setup: + .. code-block:: C - dev->type = ARPHRD_CAN; /* the netdevice hardware type */ - dev->flags = IFF_NOARP; /* CAN has no arp */ - - dev->mtu = CAN_MTU; /* sizeof(struct can_frame) -> Classical CAN interface */ - - or alternative, when the controller supports CAN with flexible data rate: - dev->mtu = CANFD_MTU; /* sizeof(struct canfd_frame) -> CAN FD interface */ + dev = alloc_candev_mqs(...); The struct can_frame or struct canfd_frame is the payload of each socket buffer (skbuff) in the protocol family PF_CAN. diff --git a/Documentation/networking/device_drivers/ethernet/amazon/ena.rst b/Documentation/networking/device_drivers/ethernet/amazon/ena.rst index 4561e8ab9e08..14784a0a6a8a 100644 --- a/Documentation/networking/device_drivers/ethernet/amazon/ena.rst +++ b/Documentation/networking/device_drivers/ethernet/amazon/ena.rst @@ -56,6 +56,9 @@ ena_netdev.[ch] Main Linux kernel driver. ena_ethtool.c ethtool callbacks. ena_xdp.[ch] XDP files ena_pci_id_tbl.h Supported device IDs. +ena_phc.[ch] PTP hardware clock infrastructure (see `PHC`_ for more info) +ena_devlink.[ch] devlink files. +ena_debugfs.[ch] debugfs files. ================= ====================================================== Management Interface: @@ -221,6 +224,99 @@ descriptor it was received on would be recycled. When a packet smaller than RX copybreak bytes is received, it is copied into a new memory buffer and the RX descriptor is returned to HW. +.. _`PHC`: + +PTP Hardware Clock (PHC) +======================== +.. _`ptp-userspace-api`: https://docs.kernel.org/driver-api/ptp.html#ptp-hardware-clock-user-space-api +.. _`testptp`: https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/ptp/testptp.c + +ENA Linux driver supports PTP hardware clock providing timestamp reference to achieve nanosecond resolution. + +**PHC support** + +PHC depends on the PTP module, which needs to be either loaded as a module or compiled into the kernel. + +Verify if the PTP module is present: + +.. code-block:: shell + + grep -w '^CONFIG_PTP_1588_CLOCK=[ym]' /boot/config-`uname -r` + +- If no output is provided, the ENA driver cannot be loaded with PHC support. + +**PHC activation** + +The feature is turned off by default, in order to turn the feature on, the ENA driver +can be loaded in the following way: + +- devlink: + +.. code-block:: shell + + sudo devlink dev param set pci/ name enable_phc value true cmode driverinit + sudo devlink dev reload pci/ + # for example: + sudo devlink dev param set pci/0000:00:06.0 name enable_phc value true cmode driverinit + sudo devlink dev reload pci/0000:00:06.0 + +All available PTP clock sources can be tracked here: + +.. code-block:: shell + + ls /sys/class/ptp + +PHC support and capabilities can be verified using ethtool: + +.. code-block:: shell + + ethtool -T + +**PHC timestamp** + +To retrieve PHC timestamp, use `ptp-userspace-api`_, usage example using `testptp`_: + +.. code-block:: shell + + testptp -d /dev/ptp$(ethtool -T | awk '/PTP Hardware Clock:/ {print $NF}') -k 1 + +PHC get time requests should be within reasonable bounds, +avoid excessive utilization to ensure optimal performance and efficiency. +The ENA device restricts the frequency of PHC get time requests to a maximum +of 125 requests per second. If this limit is surpassed, the get time request +will fail, leading to an increment in the phc_err_ts statistic. + +**PHC statistics** + +PHC can be monitored using debugfs (if mounted): + +.. code-block:: shell + + sudo cat /sys/kernel/debug//phc_stats + + # for example: + sudo cat /sys/kernel/debug/0000:00:06.0/phc_stats + +PHC errors must remain below 1% of all PHC requests to maintain the desired level of accuracy and reliability + +================= ====================================================== +**phc_cnt** | Number of successful retrieved timestamps (below expire timeout). +**phc_exp** | Number of expired retrieved timestamps (above expire timeout). +**phc_skp** | Number of skipped get time attempts (during block period). +**phc_err_dv** | Number of failed get time attempts due to device errors (entering into block state). +**phc_err_ts** | Number of failed get time attempts due to timestamp errors (entering into block state), + | This occurs if driver exceeded the request limit or device received an invalid timestamp. +================= ====================================================== + +PHC timeouts: + +================= ====================================================== +**expire** | Max time for a valid timestamp retrieval, passing this threshold will fail + | the get time request and block new requests until block timeout. +**block** | Blocking period starts once get time request expires or fails, + | all get time requests during block period will be skipped. +================= ====================================================== + Statistics ========== @@ -268,6 +364,18 @@ RSS - The user can provide a hash key, hash function, and configure the indirection table through `ethtool(8)`. +DEVLINK SUPPORT +=============== +.. _`devlink`: https://www.kernel.org/doc/html/latest/networking/devlink/index.html + +`devlink`_ supports reloading the driver and initiating re-negotiation with the ENA device + +.. code-block:: shell + + sudo devlink dev reload pci/ + # for example: + sudo devlink dev reload pci/0000:00:06.0 + DATA PATH ========= diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst index 139b4c75a191..40ac552641a3 100644 --- a/Documentation/networking/device_drivers/ethernet/index.rst +++ b/Documentation/networking/device_drivers/ethernet/index.rst @@ -58,7 +58,9 @@ Contents: ti/tlan ti/icssg_prueth wangxun/txgbe + wangxun/txgbevf wangxun/ngbe + wangxun/ngbevf .. only:: subproject and html diff --git a/Documentation/networking/device_drivers/ethernet/intel/ice.rst b/Documentation/networking/device_drivers/ethernet/intel/ice.rst index 3c46a48d99ba..0bca293cf9cb 100644 --- a/Documentation/networking/device_drivers/ethernet/intel/ice.rst +++ b/Documentation/networking/device_drivers/ethernet/intel/ice.rst @@ -927,6 +927,19 @@ To enable/disable UDP Segmentation Offload, issue the following command:: # ethtool -K tx-udp-segmentation [off|on] +PTP pin interface +----------------- +All adapters support standard PTP pin interface. SDPs (Software Definable Pin) +are single ended pins with both periodic output and external timestamp +supported. There are also specific differential input/output pins (TIME_SYNC, +1PPS) with only one of the functions supported. + +There are adapters with DPLL, where pins are connected to the DPLL instead of +being exposed on the board. You have to be aware that in those configurations, +only SDP pins are exposed and each pin has its own fixed direction. +To see input signal on those PTP pins, you need to configure DPLL properly. +Output signal is only visible on DPLL and to send it to the board SMA/U.FL pins, +DPLL output pins have to be manually configured. GNSS module ----------- diff --git a/Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst b/Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst index af7db0e91f6b..a52850602cd8 100644 --- a/Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst +++ b/Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst @@ -66,7 +66,7 @@ Admin Function driver As mentioned above RVU PF0 is called the admin function (AF), this driver supports resource provisioning and configuration of functional blocks. Doesn't handle any I/O. It sets up few basic stuff but most of the -funcionality is achieved via configuration requests from PFs and VFs. +functionality is achieved via configuration requests from PFs and VFs. PF/VFs communicates with AF via a shared memory region (mailbox). Upon receiving requests AF does resource provisioning and other HW configuration. diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/counters.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/counters.rst index 43d72c8b713b..754c81436408 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/counters.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/counters.rst @@ -1341,3 +1341,35 @@ Device Counters - The number of times the device owned queue had not enough buffers allocated. - Error + + * - `pci_bw_inbound_high` + - The number of times the device crossed the high inbound pcie bandwidth + threshold. To be compared to pci_bw_inbound_low to check if the device + is in a congested state. + If pci_bw_inbound_high == pci_bw_inbound_low then the device is not congested. + If pci_bw_inbound_high > pci_bw_inbound_low then the device is congested. + - Tnformative + + * - `pci_bw_inbound_low` + - The number of times the device crossed the low inbound PCIe bandwidth + threshold. To be compared to pci_bw_inbound_high to check if the device + is in a congested state. + If pci_bw_inbound_high == pci_bw_inbound_low then the device is not congested. + If pci_bw_inbound_high > pci_bw_inbound_low then the device is congested. + - Informative + + * - `pci_bw_outbound_high` + - The number of times the device crossed the high outbound pcie bandwidth + threshold. To be compared to pci_bw_outbound_low to check if the device + is in a congested state. + If pci_bw_outbound_high == pci_bw_outbound_low then the device is not congested. + If pci_bw_outbound_high > pci_bw_outbound_low then the device is congested. + - Informative + + * - `pci_bw_outbound_low` + - The number of times the device crossed the low outbound PCIe bandwidth + threshold. To be compared to pci_bw_outbound_high to check if the device + is in a congested state. + If pci_bw_outbound_high == pci_bw_outbound_low then the device is not congested. + If pci_bw_outbound_high > pci_bw_outbound_low then the device is congested. + - Informative diff --git a/Documentation/networking/device_drivers/ethernet/meta/fbnic.rst b/Documentation/networking/device_drivers/ethernet/meta/fbnic.rst index f8592dec8851..afb8353daefd 100644 --- a/Documentation/networking/device_drivers/ethernet/meta/fbnic.rst +++ b/Documentation/networking/device_drivers/ethernet/meta/fbnic.rst @@ -28,6 +28,36 @@ devlink dev info provides version information for all three components. In addition to the version the hg commit hash of the build is included as a separate entry. +Configuration +------------- + +Ringparams (ethtool -g / -G) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +fbnic has two submission (host -> device) rings for every completion +(device -> host) ring. The three ring objects together form a single +"queue" as used by higher layer software (a Rx, or a Tx queue). + +For Rx the two submission rings are used to pass empty pages to the NIC. +Ring 0 is the Header Page Queue (HPQ), NIC will use its pages to place +L2-L4 headers (or full frames if frame is not header-data split). +Ring 1 is the Payload Page Queue (PPQ) and used for packet payloads. +The completion ring is used to receive packet notifications / metadata. +ethtool ``rx`` ringparam maps to the size of the completion ring, +``rx-mini`` to the HPQ, and ``rx-jumbo`` to the PPQ. + +For Tx both submission rings can be used to submit packets, the completion +ring carries notifications for both. fbnic uses one of the submission +rings for normal traffic from the stack and the second one for XDP frames. +ethtool ``tx`` ringparam controls both the size of the submission rings +and the completion ring. + +Every single entry on the HPQ and PPQ (``rx-mini``, ``rx-jumbo``) +corresponds to 4kB of allocated memory, while entries on the remaining +rings are in units of descriptors (8B). The ideal ratio of submission +and completion ring sizes will depend on the workload, as for small packets +multiple packets will fit into a single page. + Upgrading Firmware ------------------ diff --git a/Documentation/networking/device_drivers/ethernet/ti/cpsw.rst b/Documentation/networking/device_drivers/ethernet/ti/cpsw.rst index a88946bd188b..d3e130455043 100644 --- a/Documentation/networking/device_drivers/ethernet/ti/cpsw.rst +++ b/Documentation/networking/device_drivers/ethernet/ti/cpsw.rst @@ -268,14 +268,14 @@ Example 1: One port tx AVB configuration scheme for target board // Run your appropriate tools with socket option "SO_PRIORITY" // to 3 for class A and/or to 2 for class B - // (I took at https://www.spinics.net/lists/netdev/msg460869.html) + // (I took at https://lore.kernel.org/r/20171017010128.22141-1-vinicius.gomes@intel.com/) ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500& ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500& 13) :: // run your listener on workstation (should be in same vlan) - // (I took at https://www.spinics.net/lists/netdev/msg460869.html) + // (I took at https://lore.kernel.org/r/20171017010128.22141-1-vinicius.gomes@intel.com/) ./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500 Receiving data rate: 39012 kbps Receiving data rate: 39012 kbps @@ -555,7 +555,7 @@ Example 2: Two port tx AVB configuration scheme for target board 20) :: // run your listener on workstation (should be in same vlan) - // (I took at https://www.spinics.net/lists/netdev/msg460869.html) + // (I took at https://lore.kernel.org/r/20171017010128.22141-1-vinicius.gomes@intel.com/) ./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500 Receiving data rate: 39012 kbps Receiving data rate: 39012 kbps diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/ngbevf.rst b/Documentation/networking/device_drivers/ethernet/wangxun/ngbevf.rst new file mode 100644 index 000000000000..a39e3d5a1038 --- /dev/null +++ b/Documentation/networking/device_drivers/ethernet/wangxun/ngbevf.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +================================================================== +Linux Base Virtual Function Driver for Wangxun(R) Gigabit Ethernet +================================================================== + +WangXun Gigabit Virtual Function Linux driver. +Copyright(c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. + +Support +======= +For general information, go to the website at: +https://www.net-swift.com + +If you got any problem, contact Wangxun support team via nic-support@net-swift.com +and Cc: netdev. diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbevf.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbevf.rst new file mode 100644 index 000000000000..b2f759b7b518 --- /dev/null +++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbevf.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +=========================================================================== +Linux Base Virtual Function Driver for Wangxun(R) 10/25/40 Gigabit Ethernet +=========================================================================== + +WangXun 10/25/40 Gigabit Virtual Function Linux driver. +Copyright(c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. + +Support +======= +For general information, go to the website at: +https://www.net-swift.com + +If you got any problem, contact Wangxun support team via nic-support@net-swift.com +and Cc: netdev. diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst index 4e01dc32bc08..211b58177e12 100644 --- a/Documentation/networking/devlink/devlink-params.rst +++ b/Documentation/networking/devlink/devlink-params.rst @@ -137,3 +137,9 @@ own name. * - ``event_eq_size`` - u32 - Control the size of asynchronous control events EQ. + * - ``enable_phc`` + - Boolean + - Enable PHC (PTP Hardware Clock) functionality in the device. + * - ``clock_id`` + - u64 + - Clock ID used by the device for registering DPLL devices and pins. diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 9d22d41a7cd1..5e397798a402 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -418,6 +418,14 @@ API allows to configure following rate object's parameters: to all node children limits. ``tx_max`` is an upper limit for children. ``tx_share`` is a total bandwidth distributed among children. +``tc_bw`` + Allow users to set the bandwidth allocation per traffic class on rate + objects. This enables fine-grained QoS configurations by assigning a relative + share value to each traffic class. The bandwidth is distributed in proportion + to the share value for each class, relative to the sum of all shares. + When applied to a non-leaf node, tc_bw determines how bandwidth is shared + among its child elements. + ``tx_priority`` and ``tx_weight`` can be used simultaneously. In that case nodes with the same priority form a WFQ subgroup in the sibling group and arbitration among them is based on assigned weights. diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index 8319f43b5933..270a65a01411 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -85,6 +85,8 @@ parameters, info versions, and other features it supports. ionic ice ixgbe + kvaser_pciefd + kvaser_usb mlx4 mlx5 mlxsw @@ -98,3 +100,4 @@ parameters, info versions, and other features it supports. iosm octeontx2 sfc + zl3073x diff --git a/Documentation/networking/devlink/kvaser_pciefd.rst b/Documentation/networking/devlink/kvaser_pciefd.rst new file mode 100644 index 000000000000..075edd2a508a --- /dev/null +++ b/Documentation/networking/devlink/kvaser_pciefd.rst @@ -0,0 +1,24 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================= +kvaser_pciefd devlink support +============================= + +This document describes the devlink features implemented by the +``kvaser_pciefd`` device driver. + +Info versions +============= + +The ``kvaser_pciefd`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the device. Also available + through ``ethtool -i`` as ``firmware-version``. diff --git a/Documentation/networking/devlink/kvaser_usb.rst b/Documentation/networking/devlink/kvaser_usb.rst new file mode 100644 index 000000000000..403db3766cb4 --- /dev/null +++ b/Documentation/networking/devlink/kvaser_usb.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +kvaser_usb devlink support +========================== + +This document describes the devlink features implemented by the +``kvaser_usb`` device driver. + +Info versions +============= + +The ``kvaser_usb`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the device. Also available + through ``ethtool -i`` as ``firmware-version``. + * - ``board.rev`` + - fixed + - The device hardware revision. + * - ``board.id`` + - fixed + - The device EAN (product number). + * - ``serial_number`` + - fixed + - The device serial number. diff --git a/Documentation/networking/devlink/netdevsim.rst b/Documentation/networking/devlink/netdevsim.rst index 88482725422c..3932004eae82 100644 --- a/Documentation/networking/devlink/netdevsim.rst +++ b/Documentation/networking/devlink/netdevsim.rst @@ -62,7 +62,7 @@ Rate objects The ``netdevsim`` driver supports rate objects management, which includes: -- registerging/unregistering leaf rate objects per VF devlink port; +- registering/unregistering leaf rate objects per VF devlink port; - creation/deletion node rate objects; - setting tx_share and tx_max rate values for any rate object type; - setting parent node for any rate object type. diff --git a/Documentation/networking/devlink/zl3073x.rst b/Documentation/networking/devlink/zl3073x.rst new file mode 100644 index 000000000000..4b6cfaf38643 --- /dev/null +++ b/Documentation/networking/devlink/zl3073x.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================= +zl3073x devlink support +======================= + +This document describes the devlink features implemented by the ``zl3073x`` +device driver. + +Parameters +========== + +.. list-table:: Generic parameters implemented + :widths: 5 5 90 + + * - Name + - Mode + - Notes + * - ``clock_id`` + - driverinit + - Set the clock ID that is used by the driver for registering DPLL devices + and pins. + +Info versions +============= + +The ``zl3073x`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 5 90 + + * - Name + - Type + - Example + - Description + * - ``asic.id`` + - fixed + - 1E94 + - Chip identification number + * - ``asic.rev`` + - fixed + - 300 + - Chip revision number + * - ``fw`` + - running + - 7006 + - Firmware version number + * - ``custom_cfg`` + - running + - 1.3.0.1 + - Device configuration version customized by OEM diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index b6e9af4d0f1b..ab20c644af24 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -239,6 +239,9 @@ Userspace to kernel: ``ETHTOOL_MSG_PHY_GET`` get Ethernet PHY information ``ETHTOOL_MSG_TSCONFIG_GET`` get hw timestamping configuration ``ETHTOOL_MSG_TSCONFIG_SET`` set hw timestamping configuration + ``ETHTOOL_MSG_RSS_SET`` set RSS settings + ``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context + ``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context ===================================== ================================= Kernel to userspace: @@ -281,6 +284,7 @@ Kernel to userspace: ``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters ``ETHTOOL_MSG_PSE_GET_REPLY`` PSE parameters ``ETHTOOL_MSG_RSS_GET_REPLY`` RSS settings + ``ETHTOOL_MSG_RSS_NTF`` RSS settings ``ETHTOOL_MSG_PLCA_GET_CFG_REPLY`` PLCA RS parameters ``ETHTOOL_MSG_PLCA_GET_STATUS_REPLY`` PLCA RS status ``ETHTOOL_MSG_PLCA_NTF`` PLCA RS parameters @@ -290,6 +294,11 @@ Kernel to userspace: ``ETHTOOL_MSG_PHY_NTF`` Ethernet PHY information change ``ETHTOOL_MSG_TSCONFIG_GET_REPLY`` hw timestamping configuration ``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration + ``ETHTOOL_MSG_PSE_NTF`` PSE events notification + ``ETHTOOL_MSG_RSS_NTF`` RSS settings notification + ``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY`` create an additional RSS context + ``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created + ``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted ======================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -1788,6 +1797,11 @@ Kernel response contents: limit of the PoE PSE. ``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested Supported power limit configuration ranges. + ``ETHTOOL_A_PSE_PW_D_ID`` u32 Index of the PSE power domain + ``ETHTOOL_A_PSE_PRIO_MAX`` u32 Priority maximum configurable + on the PoE PSE + ``ETHTOOL_A_PSE_PRIO`` u32 Priority of the PoE PSE + currently configured ========================================== ====== ============================= When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies @@ -1861,6 +1875,15 @@ identifies the C33 PSE power limit ranges through If the controller works with fixed classes, the min and max values will be equal. +The ``ETHTOOL_A_PSE_PW_D_ID`` attribute identifies the index of PSE power +domain. + +When set, the optional ``ETHTOOL_A_PSE_PRIO_MAX`` attribute identifies +the PSE maximum priority value. +When set, the optional ``ETHTOOL_A_PSE_PRIO`` attributes is used to +identifies the currently configured PSE priority. +For a description of PSE priority attributes, see ``PSE_SET``. + PSE_SET ======= @@ -1874,6 +1897,8 @@ Request contents: ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` u32 Control PSE Admin state ``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` u32 Control PoE PSE available power limit + ``ETHTOOL_A_PSE_PRIO`` u32 Control priority of the + PoE PSE ====================================== ====== ============================= When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used @@ -1896,6 +1921,38 @@ various existing products that document power consumption in watts rather than classes. If power limit configuration based on classes is needed, the conversion can be done in user space, for example by ethtool. +When set, the optional ``ETHTOOL_A_PSE_PRIO`` attributes is used to +control the PSE priority. Allowed priority value are between zero and +the value of ``ETHTOOL_A_PSE_PRIO_MAX`` attribute. + +A lower value indicates a higher priority, meaning that a priority value +of 0 corresponds to the highest port priority. +Port priority serves two functions: + + - Power-up Order: After a reset, ports are powered up in order of their + priority from highest to lowest. Ports with higher priority + (lower values) power up first. + - Shutdown Order: When the power budget is exceeded, ports with lower + priority (higher values) are turned off first. + +PSE_NTF +======= + +Notify PSE events. + +Notification contents: + + =============================== ====== ======================== + ``ETHTOOL_A_PSE_HEADER`` nested request header + ``ETHTOOL_A_PSE_EVENTS`` bitset PSE events + =============================== ====== ======================== + +When set, the optional ``ETHTOOL_A_PSE_EVENTS`` attribute identifies the +PSE events. + +.. kernel-doc:: include/uapi/linux/ethtool_netlink_generated.h + :identifiers: ethtool_pse_event + RSS_GET ======= @@ -1919,14 +1976,15 @@ used to ignore context 0s and only dump additional contexts). Kernel response contents: -===================================== ====== ========================== +===================================== ====== =============================== ``ETHTOOL_A_RSS_HEADER`` nested reply header ``ETHTOOL_A_RSS_CONTEXT`` u32 context number ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation -===================================== ====== ========================== + ``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash +===================================== ====== =============================== ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function being used. Current supported options are toeplitz, xor or crc32. @@ -1935,6 +1993,67 @@ indicates queue number. ETHTOOL_A_RSS_INPUT_XFRM attribute is a bitmap indicating the type of transformation applied to the input protocol fields before given to the RSS hfunc. Current supported options are symmetric-xor and symmetric-or-xor. +ETHTOOL_A_RSS_FLOW_HASH carries per-flow type bitmask of which header +fields are included in the hash calculation. + +RSS_SET +======= + +Request contents: + +===================================== ====== ============================== + ``ETHTOOL_A_RSS_HEADER`` nested request header + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number + ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func + ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes + ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes + ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation + ``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash +===================================== ====== ============================== + +``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and +the device driver may replicate the table if its smaller than smallest table +size supported by the device. For example if user requests ``[0, 1]`` but the +device needs at least 8 entries - the real table in use will end up being +``[0, 1, 0, 1, 0, 1, 0, 1]``. Most devices require the table size to be power +of 2, so tables which size is not a power of 2 will likely be rejected. +Using table of size 0 will reset the indirection table to the default. + +RSS_CREATE_ACT +============== + +Request contents: + +===================================== ====== ============================== + ``ETHTOOL_A_RSS_HEADER`` nested request header + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number + ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func + ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes + ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes + ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation +===================================== ====== ============================== + +Kernel response contents: + +===================================== ====== ============================== + ``ETHTOOL_A_RSS_HEADER`` nested request header + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number +===================================== ====== ============================== + +Create an additional RSS context, if ``ETHTOOL_A_RSS_CONTEXT`` is not +specified kernel will allocate one automatically. + +RSS_DELETE_ACT +============== + +Request contents: + +===================================== ====== ============================== + ``ETHTOOL_A_RSS_HEADER`` nested request header + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number +===================================== ====== ============================== + +Delete an additional RSS context. PLCA_GET_CFG ============ @@ -2386,8 +2505,8 @@ are netlink only. ``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET`` ``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET`` - ``ETHTOOL_GRXFH`` n/a - ``ETHTOOL_SRXFH`` n/a + ``ETHTOOL_GRXFH`` ``ETHTOOL_MSG_RSS_GET`` + ``ETHTOOL_SRXFH`` ``ETHTOOL_MSG_RSS_SET`` ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GRXRINGS`` n/a @@ -2401,8 +2520,8 @@ are netlink only. ``ETHTOOL_SRXNTUPLE`` n/a ``ETHTOOL_GRXNTUPLE`` n/a ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` - ``ETHTOOL_GRXFHINDIR`` n/a - ``ETHTOOL_SRXFHINDIR`` n/a + ``ETHTOOL_GRXFHINDIR`` ``ETHTOOL_MSG_RSS_GET`` + ``ETHTOOL_SRXFHINDIR`` ``ETHTOOL_MSG_RSS_SET`` ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GCHANNELS`` ``ETHTOOL_MSG_CHANNELS_GET`` diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 0f1251cce314..9756d16e3df1 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -8,15 +8,19 @@ IP Sysctl ============================== ip_forward - BOOLEAN - - 0 - disabled (default) - - not 0 - enabled - Forward Packets between interfaces. This variable is special, its change resets all configuration parameters to their default state (RFC1122 for hosts, RFC1812 for routers) + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + ip_default_ttl - INTEGER Default value of TTL field (Time To Live) for outgoing (but not forwarded) IP packets. Should be between 1 and 255 inclusive. @@ -62,20 +66,25 @@ ip_forward_use_pmtu - BOOLEAN kernel honoring this information. This is normally not the case. - Default: 0 (disabled) - Possible values: - - 0 - disabled - - 1 - enabled + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) fwmark_reflect - BOOLEAN Controls the fwmark of kernel-generated IPv4 reply packets that are not associated with a socket for example, TCP RSTs or ICMP echo replies). - If unset, these packets have a fwmark of zero. If set, they have the + If disabled, these packets have a fwmark of zero. If enabled, they have the fwmark of the packet they are replying to. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) fib_multipath_use_neigh - BOOLEAN Use status of existing neighbor entry when determining nexthop for @@ -83,12 +92,12 @@ fib_multipath_use_neigh - BOOLEAN packets could be directed to a failed nexthop. Only valid for kernels built with CONFIG_IP_ROUTE_MULTIPATH enabled. - Default: 0 (disabled) - Possible values: - - 0 - disabled - - 1 - enabled + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) fib_multipath_hash_policy - INTEGER Controls which hash policy to use for multipath routes. Only valid @@ -368,7 +377,12 @@ tcp_autocorking - BOOLEAN queue. Applications can still use TCP_CORK for optimal behavior when they know how/when to uncork their sockets. - Default : 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_available_congestion_control - STRING Shows the available congestion control choices that are registered. @@ -408,9 +422,16 @@ tcp_congestion_control - STRING tcp_dsack - BOOLEAN Allows TCP to send "duplicate" SACKs. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) + tcp_early_retrans - INTEGER Tail loss probe (TLP) converts RTOs occurring due to tail - losses into fast recovery (draft-ietf-tcpm-rack). Note that + losses into fast recovery (RFC8985). Note that TLP requires RACK to function properly (see tcp_recovery below) Possible values: @@ -447,7 +468,12 @@ tcp_ecn_fallback - BOOLEAN knob. The value is not used, if tcp_ecn or per route (or congestion control) ECN settings are disabled. - Default: 1 (fallback enabled) + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_fack - BOOLEAN This is a legacy option, it has no effect anymore. @@ -474,7 +500,7 @@ tcp_frto - INTEGER By default it's enabled with a non-zero value. 0 disables F-RTO. tcp_fwmark_accept - BOOLEAN - If set, incoming connections to listening sockets that do not have a + If enabled, incoming connections to listening sockets that do not have a socket mark will set the mark of the accepting socket to the fwmark of the incoming SYN packet. This will cause all packets on that connection (starting from the first SYNACK) to be sent with that fwmark. The @@ -482,7 +508,12 @@ tcp_fwmark_accept - BOOLEAN have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are unaffected. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_invalid_ratelimit - INTEGER Limit the maximal rate for sending duplicate acknowledgments @@ -528,6 +559,11 @@ tcp_l3mdev_accept - BOOLEAN which the packets originated. Only valid when the kernel was compiled with CONFIG_NET_L3_MASTER_DEV. + Possible values: + + - 0 (disabled) + - 1 (enabled) + Default: 0 (disabled) tcp_low_latency - BOOLEAN @@ -593,10 +629,16 @@ tcp_min_rtt_wlen - INTEGER Default: 300 tcp_moderate_rcvbuf - BOOLEAN - If set, TCP performs receive buffer auto-tuning, attempting to + If enabled, TCP performs receive buffer auto-tuning, attempting to automatically size the buffer (no greater than tcp_rmem[2]) to - match the size required by the path for full throughput. Enabled by - default. + match the size required by the path for full throughput. + + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_mtu_probing - INTEGER Controls TCP Packetization-Layer Path MTU Discovery. Takes three @@ -621,13 +663,26 @@ tcp_no_metrics_save - BOOLEAN when the connection closes, so that connections established in the near future can use these to set initial conditions. Usually, this increases overall performance, but may sometimes cause performance - degradation. If set, TCP will not cache metrics on closing + degradation. If enabled, TCP will not cache metrics on closing connections. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + tcp_no_ssthresh_metrics_save - BOOLEAN Controls whether TCP saves ssthresh metrics in the route cache. + If enabled, ssthresh metrics are disabled. - Default is 1, which disables ssthresh metrics. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_orphan_retries - INTEGER This value influences the timeout of a locally closed TCP connection, @@ -645,9 +700,11 @@ tcp_recovery - INTEGER features. ========= ============================================================= - RACK: 0x1 enables the RACK loss detection for fast detection of lost - retransmissions and tail drops. It also subsumes and disables - RFC6675 recovery for SACK connections. + RACK: 0x1 enables RACK loss detection, for fast detection of lost + retransmissions and tail drops, and resilience to + reordering. currently, setting this bit to 0 has no + effect, since RACK is the only supported loss detection + algorithm. RACK: 0x2 makes RACK's reordering window static (min_rtt/4). @@ -664,6 +721,11 @@ tcp_reflect_tos - BOOLEAN This options affects both IPv4 and IPv6. + Possible values: + + - 0 (disabled) + - 1 (enabled) + Default: 0 (disabled) tcp_reordering - INTEGER @@ -685,6 +747,13 @@ tcp_retrans_collapse - BOOLEAN On retransmit try to send bigger packets to work around bugs in certain TCP stacks. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) + tcp_retries1 - INTEGER This value influences the time, after which TCP decides, that something is wrong due to unacknowledged RTO retransmissions, @@ -712,11 +781,16 @@ tcp_retries2 - INTEGER which corresponds to a value of at least 8. tcp_rfc1337 - BOOLEAN - If set, the TCP stack behaves conforming to RFC1337. If unset, + If enabled, the TCP stack behaves conforming to RFC1337. If unset, we are not conforming to RFC, but prevent TCP TIME_WAIT assassination. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_rmem - vector of 3 INTEGERs: min, default, max min: Minimal size of receive buffer used by TCP sockets. @@ -740,6 +814,13 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max tcp_sack - BOOLEAN Enable select acknowledgments (SACKS). + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) + tcp_comp_sack_delay_ns - LONG INTEGER TCP tries to reduce number of SACK sent, using a timer based on 5% of SRTT, capped by this sysctl, in nano seconds. @@ -762,26 +843,41 @@ tcp_comp_sack_nr - INTEGER Default : 44 tcp_backlog_ack_defer - BOOLEAN - If set, user thread processing socket backlog tries sending + If enabled, user thread processing socket backlog tries sending one ACK for the whole queue. This helps to avoid potential long latencies at end of a TCP socket syscall. - Default : true + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_slow_start_after_idle - BOOLEAN - If set, provide RFC2861 behavior and time out the congestion + If enabled, provide RFC2861 behavior and time out the congestion window after an idle period. An idle period is defined at the current RTO. If unset, the congestion window will not be timed out after an idle period. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) tcp_stdurg - BOOLEAN Use the Host requirements interpretation of the TCP urgent pointer field. - Most hosts use the older BSD interpretation, so if you turn this on + Most hosts use the older BSD interpretation, so if enabled, Linux might not communicate correctly with them. - Default: FALSE + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_synack_retries - INTEGER Number of times SYNACKs for a passive TCP connection attempt will @@ -838,7 +934,12 @@ tcp_migrate_req - BOOLEAN migration by returning SK_DROP in the type of eBPF program, or disable this option. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_fastopen - INTEGER Enable TCP Fast Open (RFC7413) to send and accept data in the opening @@ -1019,6 +1120,13 @@ tcp_tw_reuse_delay - UNSIGNED INTEGER tcp_window_scaling - BOOLEAN Enable window scaling as defined in RFC1323. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) + tcp_shrink_window - BOOLEAN This changes how the TCP receive window is calculated. @@ -1026,13 +1134,15 @@ tcp_shrink_window - BOOLEAN window can be offered, and that TCP implementations MUST ensure that they handle a shrinking window, as specified in RFC 1122. - - 0 - Disabled. The window is never shrunk. - - 1 - Enabled. The window is shrunk when necessary to remain within - the memory limit set by autotuning (sk_rcvbuf). - This only occurs if a non-zero receive window - scaling factor is also in effect. + Possible values: - Default: 0 + - 0 (disabled) - The window is never shrunk. + - 1 (enabled) - The window is shrunk when necessary to remain within + the memory limit set by autotuning (sk_rcvbuf). + This only occurs if a non-zero receive window + scaling factor is also in effect. + + Default: 0 (disabled) tcp_wmem - vector of 3 INTEGERs: min, default, max min: Amount of memory reserved for send buffers for TCP sockets. @@ -1069,16 +1179,21 @@ tcp_notsent_lowat - UNSIGNED INTEGER Default: UINT_MAX (0xFFFFFFFF) tcp_workaround_signed_windows - BOOLEAN - If set, assume no receipt of a window scaling option means the + If enabled, assume no receipt of a window scaling option means the remote TCP is broken and treats the window as a signed quantity. - If unset, assume the remote TCP is not broken even if we do + If disabled, assume the remote TCP is not broken even if we do not receive a window scaling option from them. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_thin_linear_timeouts - BOOLEAN Enable dynamic triggering of linear timeouts for thin streams. - If set, a check is performed upon retransmission by timeout to + If enabled, a check is performed upon retransmission by timeout to determine if the stream is thin (less than 4 packets in flight). As long as the stream is found to be thin, up to 6 linear timeouts may be performed before exponential backoff mode is @@ -1087,7 +1202,12 @@ tcp_thin_linear_timeouts - BOOLEAN For more information on thin streams, see Documentation/networking/tcp-thin.rst - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_limit_output_bytes - INTEGER Controls TCP Small Queue limit per tcp socket. @@ -1139,7 +1259,7 @@ tcp_child_ehash_entries - INTEGER Default: 0 tcp_plb_enabled - BOOLEAN - If set and the underlying congestion control (e.g. DCTCP) supports + If enabled and the underlying congestion control (e.g. DCTCP) supports and enables PLB feature, TCP PLB (Protective Load Balancing) is enabled. PLB is described in the following paper: https://doi.org/10.1145/3544216.3544226. Based on PLB parameters, @@ -1155,12 +1275,17 @@ tcp_plb_enabled - BOOLEAN by switches to determine next hop. In either case, further host and switch side changes will be needed. - When set, PLB assumes that congestion signal (e.g. ECN) is made + If enabled, PLB assumes that congestion signal (e.g. ECN) is made available and used by congestion control module to estimate a congestion measure (e.g. ce_ratio). PLB needs a congestion measure to make repathing decisions. - Default: FALSE + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tcp_plb_idle_rehash_rounds - INTEGER Number of consecutive congested rounds (RTT) seen after which @@ -1260,6 +1385,11 @@ udp_l3mdev_accept - BOOLEAN originated. Only valid when the kernel was compiled with CONFIG_NET_L3_MASTER_DEV. + Possible values: + + - 0 (disabled) + - 1 (enabled) + Default: 0 (disabled) udp_mem - vector of 3 INTEGERs: min, pressure, max @@ -1290,7 +1420,7 @@ udp_hash_entries - INTEGER A negative value means the networking namespace does not own its hash buckets and shares the initial networking namespace's one. -udp_child_ehash_entries - INTEGER +udp_child_hash_entries - INTEGER Control the number of hash buckets for UDP sockets in the child networking namespace, which must be set before clone() or unshare(). @@ -1320,19 +1450,29 @@ raw_l3mdev_accept - BOOLEAN originated. Only valid when the kernel was compiled with CONFIG_NET_L3_MASTER_DEV. + Possible values: + + - 0 (disabled) + - 1 (enabled) + Default: 1 (enabled) CIPSOv4 Variables ================= cipso_cache_enable - BOOLEAN - If set, enable additions to and lookups from the CIPSO label mapping - cache. If unset, additions are ignored and lookups always result in a + If enabled, enable additions to and lookups from the CIPSO label mapping + cache. If disabled, additions are ignored and lookups always result in a miss. However, regardless of the setting the cache is still invalidated when required when means you can safely toggle this on and off and the cache will always be "safe". - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) cipso_cache_bucket_size - INTEGER The CIPSO label cache consists of a fixed size hash table with each @@ -1350,17 +1490,27 @@ cipso_rbm_optfmt - BOOLEAN This means that when set the CIPSO tag will be padded with empty categories in order to make the packet data 32-bit aligned. - Default: 0 + Possible values: -cipso_rbm_structvalid - BOOLEAN - If set, do a very strict check of the CIPSO option when - ip_options_compile() is called. If unset, relax the checks done during + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + +cipso_rbm_strictvalid - BOOLEAN + If enabled, do a very strict check of the CIPSO option when + ip_options_compile() is called. If disabled, relax the checks done during ip_options_compile(). Either way is "safe" as errors are caught else where in the CIPSO processing code but setting this to 0 (False) should result in less work (i.e. it should be faster) but could cause problems with other implementations that require strict checking. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) IP Variables ============ @@ -1417,10 +1567,15 @@ ip_unprivileged_port_start - INTEGER Default: 1024 ip_nonlocal_bind - BOOLEAN - If set, allows processes to bind() to non-local IP addresses, + If enabled, allows processes to bind() to non-local IP addresses, which can be quite useful - but may break some applications. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) ip_autobind_reuse - BOOLEAN By default, bind() does not select the ports automatically even if @@ -1429,7 +1584,13 @@ ip_autobind_reuse - BOOLEAN when you use bind()+connect(), but may break some applications. The preferred solution is to use IP_BIND_ADDRESS_NO_PORT and this option should only be set by experts. - Default: 0 + + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) ip_dynaddr - INTEGER If set non-zero, enables support for dynamic addresses. @@ -1447,7 +1608,12 @@ ip_early_demux - BOOLEAN It may add an additional cost for pure routing workloads that reduces overall throughput, in such case you should disable it. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) ping_group_range - 2 INTEGERS Restrict ICMP_PROTO datagram sockets to users in the group range. @@ -1459,31 +1625,56 @@ ping_group_range - 2 INTEGERS tcp_early_demux - BOOLEAN Enable early demux for established TCP sockets. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) udp_early_demux - BOOLEAN Enable early demux for connected UDP sockets. Disable this if your system could experience more unconnected load. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) icmp_echo_ignore_all - BOOLEAN - If set non-zero, then the kernel will ignore all ICMP ECHO + If enabled, then the kernel will ignore all ICMP ECHO requests sent to it. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) icmp_echo_enable_probe - BOOLEAN - If set to one, then the kernel will respond to RFC 8335 PROBE + If enabled, then the kernel will respond to RFC 8335 PROBE requests sent to it. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) icmp_echo_ignore_broadcasts - BOOLEAN - If set non-zero, then the kernel will ignore all ICMP ECHO and + If enabled, then the kernel will ignore all ICMP ECHO and TIMESTAMP requests sent to it via broadcast/multicast. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) icmp_ratelimit - INTEGER Limit the maximal rates for sending ICMP packets whose type matches @@ -1540,17 +1731,22 @@ icmp_ratemask - INTEGER icmp_ignore_bogus_error_responses - BOOLEAN Some routers violate RFC1122 by sending bogus responses to broadcast frames. Such violations are normally logged via a kernel warning. - If this is set to TRUE, the kernel will not give such warnings, which + If enabled, the kernel will not give such warnings, which will avoid log file clutter. - Default: 1 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) icmp_errors_use_inbound_ifaddr - BOOLEAN - If zero, icmp error messages are sent with the primary address of + If disabled, icmp error messages are sent with the primary address of the exiting interface. - If non-zero, the message will be sent with the primary address of + If enabled, the message will be sent with the primary address of the interface that received the packet that caused the icmp error. This is the behaviour many network administrators will expect from a router. And it can make debugging complicated network layouts @@ -1560,7 +1756,12 @@ icmp_errors_use_inbound_ifaddr - BOOLEAN then the primary address of the first non-loopback interface that has one will be used regardless of this setting. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) igmp_max_memberships - INTEGER Change the maximum number of multicast groups we can subscribe to. @@ -1690,10 +1891,10 @@ proxy_arp_pvlan - BOOLEAN This technology is known by different names: - In RFC 3069 it is called VLAN Aggregation. - Cisco and Allied Telesyn call it Private VLAN. - Hewlett-Packard call it Source-Port filtering or port-isolation. - Ericsson call it MAC-Forced Forwarding (RFC Draft). + - In RFC 3069 it is called VLAN Aggregation. + - Cisco and Allied Telesyn call it Private VLAN. + - Hewlett-Packard call it Source-Port filtering or port-isolation. + - Ericsson call it MAC-Forced Forwarding (RFC Draft). proxy_delay - INTEGER Delay proxy response. @@ -1910,8 +2111,12 @@ arp_evict_nocarrier - BOOLEAN between access points on the same network. In most cases this should remain as the default (1). - - 1 - (default): Clear the ARP cache on NOCARRIER events - - 0 - Do not clear ARP cache on NOCARRIER events + Possible values: + + - 0 (disabled) - Do not clear ARP cache on NOCARRIER events + - 1 (enabled) - Clear the ARP cache on NOCARRIER events + + Default: 1 (enabled) mcast_solicit - INTEGER The maximum number of multicast probes in INCOMPLETE state, @@ -1934,9 +2139,23 @@ mcast_resolicit - INTEGER disable_policy - BOOLEAN Disable IPSEC policy (SPD) for this interface + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + disable_xfrm - BOOLEAN Disable IPSEC encryption on this interface, whatever the policy + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + igmpv2_unsolicited_report_interval - INTEGER The interval in milliseconds in which the next unsolicited IGMPv1 or IGMPv2 report retransmit will take place. @@ -1952,11 +2171,25 @@ igmpv3_unsolicited_report_interval - INTEGER ignore_routes_with_linkdown - BOOLEAN Ignore routes whose link is down when performing a FIB lookup. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + promote_secondaries - BOOLEAN When a primary IP address is removed from this interface promote a corresponding secondary IP address instead of removing all the corresponding secondary IP addresses. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + drop_unicast_in_l2_multicast - BOOLEAN Drop any unicast IP packets that are received in link-layer multicast (or broadcast) frames. @@ -1964,14 +2197,24 @@ drop_unicast_in_l2_multicast - BOOLEAN This behavior (for multicast) is actually a SHOULD in RFC 1122, but is disabled by default for compatibility reasons. - Default: off (0) + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) drop_gratuitous_arp - BOOLEAN Drop all gratuitous ARP frames, for example if there's a known good ARP proxy on the network and such frames need not be used (or in the case of 802.11, must not be used to prevent attacks.) - Default: off (0) + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) tag - INTEGER @@ -2015,20 +2258,24 @@ bindv6only - BOOLEAN which restricts use of the IPv6 socket to IPv6 communication only. - - TRUE: disable IPv4-mapped address feature - - FALSE: enable IPv4-mapped address feature + Possible values: - Default: FALSE (as specified in RFC3493) + - 0 (disabled) - enable IPv4-mapped address feature + - 1 (enabled) - disable IPv4-mapped address feature + + Default: 0 (disabled) flowlabel_consistency - BOOLEAN Protect the consistency (and unicity) of flow label. You have to disable it to use IPV6_FL_F_REFLECT flag on the flow label manager. - - TRUE: enabled - - FALSE: disabled + Possible values: - Default: TRUE + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) auto_flowlabels - INTEGER Automatically generate flow labels based on a flow hash of the @@ -2054,10 +2301,13 @@ flowlabel_state_ranges - BOOLEAN reserved for the IPv6 flow manager facility, 0x80000-0xFFFFF is reserved for stateless flow labels as described in RFC6437. - - TRUE: enabled - - FALSE: disabled + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) - Default: true flowlabel_reflect - INTEGER Control flow label reflection. Needed for Path MTU @@ -2125,10 +2375,13 @@ anycast_src_echo_reply - BOOLEAN Controls the use of anycast addresses as source addresses for ICMPv6 echo reply - - TRUE: enabled - - FALSE: disabled + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) - Default: FALSE idgen_delay - INTEGER Controls the delay in seconds after which time to retry @@ -2185,7 +2438,12 @@ skip_notify_on_dev_down - BOOLEAN to true skips the message, making IPv4 and IPv6 on par in relying on userspace caches to track link events and evict routes. - Default: false (generate message) + Possible values: + + - 0 (disabled) - generate the message + - 1 (enabled) - skip generating the message + + Default: 0 (disabled) nexthop_compat_mode - BOOLEAN New nexthop API provides a means for managing nexthops independent of @@ -2229,8 +2487,10 @@ fib_notify_on_flag_change - INTEGER ioam6_id - INTEGER Define the IOAM id of this node. Uses only 24 bits out of 32 in total. - Min: 0 - Max: 0xFFFFFF + Possible value range: + + - Min: 0 + - Max: 0xFFFFFF Default: 0xFFFFFF @@ -2238,8 +2498,10 @@ ioam6_id_wide - LONG INTEGER Define the wide IOAM id of this node. Uses only 56 bits out of 64 in total. Can be different from ioam6_id. - Min: 0 - Max: 0xFFFFFFFFFFFFFF + Possible value range: + + - Min: 0 + - Max: 0xFFFFFFFFFFFFFF Default: 0xFFFFFFFFFFFFFF @@ -2281,8 +2543,8 @@ conf/all/disable_ipv6 - BOOLEAN conf/all/forwarding - BOOLEAN Enable global IPv6 forwarding between all interfaces. - IPv4 and IPv6 work differently here; e.g. netfilter must be used - to control which interfaces may forward packets and which not. + IPv4 and IPv6 work differently here; the ``force_forwarding`` flag must + be used to control which interfaces may forward packets. This also sets all interfaces' Host/Router setting 'forwarding' to the specified value. See below for details. @@ -2292,13 +2554,30 @@ conf/all/forwarding - BOOLEAN proxy_ndp - BOOLEAN Do proxy ndp. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + +force_forwarding - BOOLEAN + Enable forwarding on this interface only -- regardless of the setting on + ``conf/all/forwarding``. When setting ``conf.all.forwarding`` to 0, + the ``force_forwarding`` flag will be reset on all interfaces. + fwmark_reflect - BOOLEAN Controls the fwmark of kernel-generated IPv6 reply packets that are not associated with a socket for example, TCP RSTs or ICMPv6 echo replies). - If unset, these packets have a fwmark of zero. If set, they have the + If disabled, these packets have a fwmark of zero. If enabled, they have the fwmark of the packet they are replying to. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) ``conf/interface/*``: Change special settings per interface. @@ -2389,9 +2668,11 @@ ra_honor_pio_life - BOOLEAN lifetime of an address matching a prefix sent in a Router Advertisement Prefix Information Option. - - If enabled, the PIO valid lifetime will always be honored. - - If disabled, RFC4862 section 5.5.3e is used to determine + Possible values: + + - 0 (disabled) - RFC4862 section 5.5.3e is used to determine the valid lifetime of the address. + - 1 (enabled) - the PIO valid lifetime will always be honored. Default: 0 (disabled) @@ -2403,8 +2684,10 @@ ra_honor_pio_pflag - BOOLEAN P-flag suppresses any effects of the A-flag within the same PIO. For a given PIO, P=1 and A=1 is treated as A=0. - - If disabled, the P-flag is ignored. - - If enabled, the P-flag will disable SLAAC autoconfiguration + Possible values: + + - 0 (disabled) - the P-flag is ignored. + - 1 (enabled) - the P-flag will disable SLAAC autoconfiguration for the given Prefix Information Option. Default: 0 (disabled) @@ -2526,10 +2809,15 @@ mtu - INTEGER Default: 1280 (IPv6 required minimum) ip_nonlocal_bind - BOOLEAN - If set, allows processes to bind() to non-local IPv6 addresses, + If enabled, allows processes to bind() to non-local IPv6 addresses, which can be quite useful - but may break some applications. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) router_probe_interval - INTEGER Minimum interval (in seconds) between Router Probing described @@ -2559,7 +2847,12 @@ use_oif_addrs_only - BOOLEAN routed via this interface are restricted to the set of addresses configured on this interface (vis. RFC 6724, section 4). - Default: false + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) use_tempaddr - INTEGER Preference for Privacy Extensions (RFC3041). @@ -2684,10 +2977,14 @@ force_tllao - BOOLEAN ndisc_notify - BOOLEAN Define mode for notification of address and device changes. - * 0 - (default): do nothing - * 1 - Generate unsolicited neighbour advertisements when device is brought + Possible values: + + - 0 (disabled) - do nothing + - 1 (enabled) - Generate unsolicited neighbour advertisements when device is brought up or hardware address changes. + Default: 0 (disabled) + ndisc_tclass - INTEGER The IPv6 Traffic Class to use by default when sending IPv6 Neighbor Discovery (Router Solicitation, Router Advertisement, Neighbor @@ -2704,8 +3001,12 @@ ndisc_evict_nocarrier - BOOLEAN not be cleared when roaming between access points on the same network. In most cases this should remain as the default (1). - - 1 - (default): Clear neighbor discover cache on NOCARRIER events. - - 0 - Do not clear neighbor discovery cache on NOCARRIER events. + Possible values: + + - 0 (disabled) - Do not clear neighbor discovery cache on NOCARRIER events. + - 1 (enabled) - Clear neighbor discover cache on NOCARRIER events. + + Default: 1 (enabled) mldv1_unsolicited_report_interval - INTEGER The interval in milliseconds in which the next unsolicited @@ -2734,25 +3035,34 @@ suppress_frag_ndisc - INTEGER optimistic_dad - BOOLEAN Whether to perform Optimistic Duplicate Address Detection (RFC 4429). - * 0: disabled (default) - * 1: enabled - Optimistic Duplicate Address Detection for the interface will be enabled if at least one of conf/{all,interface}/optimistic_dad is set to 1, it will be disabled otherwise. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + + use_optimistic - BOOLEAN If enabled, do not classify optimistic addresses as deprecated during source address selection. Preferred addresses will still be chosen before optimistic addresses, subject to other ranking in the source address selection algorithm. - * 0: disabled (default) - * 1: enabled - This will be enabled if at least one of conf/{all,interface}/use_optimistic is set to 1, disabled otherwise. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) + stable_secret - IPv6 address This IPv6 address will be used as a secret to generate IPv6 addresses for link-local addresses and autoconfigured @@ -2783,14 +3093,24 @@ drop_unicast_in_l2_multicast - BOOLEAN Drop any unicast IPv6 packets that are received in link-layer multicast (or broadcast) frames. - By default this is turned off. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) drop_unsolicited_na - BOOLEAN Drop all unsolicited neighbor advertisements, for example if there's a known good NA proxy on the network and such frames need not be used (or in the case of 802.11, must not be used to prevent attacks.) - By default this is turned off. + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled). accept_untracked_na - INTEGER Define behavior for accepting neighbor advertisements from devices that @@ -2831,7 +3151,12 @@ enhanced_dad - BOOLEAN The nonce option will be sent on an interface unless both of conf/{all,interface}/enhanced_dad are set to FALSE. - Default: TRUE + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 1 (enabled) ``icmp/*``: =========== @@ -2860,29 +3185,49 @@ ratemask - list of comma separated ranges Default: 0-1,3-127 (rate limit ICMPv6 errors except Packet Too Big) echo_ignore_all - BOOLEAN - If set non-zero, then the kernel will ignore all ICMP ECHO + If enabled, then the kernel will ignore all ICMP ECHO requests sent to it over the IPv6 protocol. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) echo_ignore_multicast - BOOLEAN - If set non-zero, then the kernel will ignore all ICMP ECHO + If enabled, then the kernel will ignore all ICMP ECHO requests sent to it over the IPv6 protocol via multicast. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) echo_ignore_anycast - BOOLEAN - If set non-zero, then the kernel will ignore all ICMP ECHO + If enabled, then the kernel will ignore all ICMP ECHO requests sent to it over the IPv6 protocol destined to anycast address. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) error_anycast_as_unicast - BOOLEAN - If set to 1, then the kernel will respond with ICMP Errors + If enabled, then the kernel will respond with ICMP Errors resulting from requests sent to it over the IPv6 protocol destined to anycast address essentially treating anycast as unicast. - Default: 0 + Possible values: + + - 0 (disabled) + - 1 (enabled) + + Default: 0 (disabled) xfrm6_gc_thresh - INTEGER (Obsolete since linux-4.14) @@ -2900,34 +3245,49 @@ YOSHIFUJI Hideaki / USAGI Project ================================= bridge-nf-call-arptables - BOOLEAN - - 1 : pass bridged ARP traffic to arptables' FORWARD chain. - - 0 : disable this. - Default: 1 + Possible values: + + - 0 (disabled) - disable this. + - 1 (enabled) - pass bridged ARP traffic to arptables' FORWARD chain. + + Default: 1 (enabled) bridge-nf-call-iptables - BOOLEAN - - 1 : pass bridged IPv4 traffic to iptables' chains. - - 0 : disable this. - Default: 1 + Possible values: + + - 0 (disabled) - disable this. + - 1 (enabled) - pass bridged IPv4 traffic to iptables' chains. + + Default: 1 (enabled) bridge-nf-call-ip6tables - BOOLEAN - - 1 : pass bridged IPv6 traffic to ip6tables' chains. - - 0 : disable this. - Default: 1 + Possible values: + + - 0 (disabled) - disable this. + - 1 (enabled) - pass bridged IPv6 traffic to ip6tables' chains. + + Default: 1 (enabled) bridge-nf-filter-vlan-tagged - BOOLEAN - - 1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables. - - 0 : disable this. - Default: 0 + Possible values: + + - 0 (disabled) - disable this. + - 1 (enabled) - pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables + + Default: 0 (disabled) bridge-nf-filter-pppoe-tagged - BOOLEAN - - 1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables. - - 0 : disable this. - Default: 0 + Possible values: + + - 0 (disabled) - disable this. + - 1 (enabled) - pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables. + + Default: 0 (disabled) bridge-nf-pass-vlan-input-dev - BOOLEAN - 1: if bridge-nf-filter-vlan-tagged is enabled, try to find a vlan @@ -2950,11 +3310,12 @@ addip_enable - BOOLEAN the ability to dynamically add and remove new addresses for the SCTP associations. - 1: Enable extension. + Possible values: - 0: Disable extension. + - 0 (disabled) - disable extension. + - 1 (enabled) - enable extension - Default: 0 + Default: 0 (disabled) pf_enable - INTEGER Enable or disable pf (pf is short for potentially failed) state. A value @@ -2969,31 +3330,27 @@ pf_enable - INTEGER https://datatracker.ietf.org/doc/draft-ietf-tsvwg-sctp-failover for details. - 1: Enable pf. + Possible values: - 0: Disable pf. + - 1: Enable pf. + - 0: Disable pf. Default: 1 pf_expose - INTEGER Unset or enable/disable pf (pf is short for potentially failed) state exposure. Applications can control the exposure of the PF path state - in the SCTP_PEER_ADDR_CHANGE event and the SCTP_GET_PEER_ADDR_INFO - sockopt. When it's unset, no SCTP_PEER_ADDR_CHANGE event with - SCTP_ADDR_PF state will be sent and a SCTP_PF-state transport info - can be got via SCTP_GET_PEER_ADDR_INFO sockopt; When it's enabled, - a SCTP_PEER_ADDR_CHANGE event will be sent for a transport becoming - SCTP_PF state and a SCTP_PF-state transport info can be got via - SCTP_GET_PEER_ADDR_INFO sockopt; When it's disabled, no - SCTP_PEER_ADDR_CHANGE event will be sent and it returns -EACCES when - trying to get a SCTP_PF-state transport info via SCTP_GET_PEER_ADDR_INFO - sockopt. + in the SCTP_PEER_ADDR_CHANGE event and access of SCTP_PF-state + transport info via SCTP_GET_PEER_ADDR_INFO sockopt. - 0: Unset pf state exposure, Compatible with old applications. + Possible values: - 1: Disable pf state exposure. - - 2: Enable pf state exposure. + - 0: Unset pf state exposure (compatible with old applications). No + event will be sent but the transport info can be queried. + - 1: Disable pf state exposure. No event will be sent and trying to + obtain transport info will return -EACCESS. + - 2: Enable pf state exposure. The event will be sent for a transport + becoming SCTP_PF state and transport info can be obtained. Default: 0 @@ -3023,19 +3380,23 @@ auth_enable - BOOLEAN required for secure operation of Dynamic Address Reconfiguration (ADD-IP) extension. - - 1: Enable this extension. - - 0: Disable this extension. + Possible values: - Default: 0 + - 0 (disabled) - disable extension. + - 1 (enabled) - enable extension + + Default: 0 (disabled) prsctp_enable - BOOLEAN Enable or disable the Partial Reliability extension (RFC3758) which is used to notify peers that a given DATA should no longer be expected. - - 1: Enable extension - - 0: Disable + Possible values: - Default: 1 + - 0 (disabled) - disable extension. + - 1 (enabled) - enable extension + + Default: 1 (enabled) max_burst - INTEGER The limit of the number of new packets that can be initially sent. It @@ -3135,10 +3496,12 @@ cookie_preserve_enable - BOOLEAN Enable or disable the ability to extend the lifetime of the SCTP cookie that is used during the establishment phase of SCTP association - - 1: Enable cookie lifetime extension. - - 0: Disable + Possible values: - Default: 1 + - 0 (disabled) - disable. + - 1 (enabled) - enable cookie lifetime extension. + + Default: 1 (enabled) cookie_hmac_alg - STRING Select the hmac algorithm used when generating the cookie value sent by @@ -3183,13 +3546,11 @@ sndbuf_policy - INTEGER sctp_mem - vector of 3 INTEGERs: min, pressure, max Number of pages allowed for queueing by all SCTP sockets. - min: Below this number of pages SCTP is not bothered about its - memory appetite. When amount of memory allocated by SCTP exceeds - this number, SCTP starts to moderate memory usage. - - pressure: This value was introduced to follow format of tcp_mem. - - max: Number of pages allowed for queueing by all SCTP sockets. + * min: Below this number of pages SCTP is not bothered about its + memory usage. When amount of memory allocated by SCTP exceeds + this number, SCTP starts to moderate memory usage. + * pressure: This value was introduced to follow format of tcp_mem. + * max: Maximum number of allowed pages. Default is calculated at boot time from amount of available memory. @@ -3197,9 +3558,9 @@ sctp_rmem - vector of 3 INTEGERs: min, default, max Only the first value ("min") is used, "default" and "max" are ignored. - min: Minimal size of receive buffer used by SCTP socket. - It is guaranteed to each SCTP socket (but not association) even - under moderate memory pressure. + * min: Minimal size of receive buffer used by SCTP socket. + It is guaranteed to each SCTP socket (but not association) even + under moderate memory pressure. Default: 4K @@ -3207,14 +3568,16 @@ sctp_wmem - vector of 3 INTEGERs: min, default, max Only the first value ("min") is used, "default" and "max" are ignored. - min: Minimum size of send buffer that can be used by SCTP sockets. - It is guaranteed to each SCTP socket (but not association) even - under moderate memory pressure. + * min: Minimum size of send buffer that can be used by SCTP sockets. + It is guaranteed to each SCTP socket (but not association) even + under moderate memory pressure. Default: 4K addr_scope_policy - INTEGER - Control IPv4 address scoping - draft-stewart-tsvwg-sctp-ipv4-00 + Control IPv4 address scoping (see + https://datatracker.ietf.org/doc/draft-stewart-tsvwg-sctp-ipv4/00/ + for details). - 0 - Disable IPv4 address scoping - 1 - Enable IPv4 address scoping @@ -3272,10 +3635,12 @@ reconf_enable - BOOLEAN a stream, and it includes the Parameters of "Outgoing/Incoming SSN Reset", "SSN/TSN Reset" and "Add Outgoing/Incoming Streams". - - 1: Enable extension. - - 0: Disable extension. + Possible values: - Default: 0 + - 0 (disabled) - Disable extension. + - 1 (enabled) - Enable extension. + + Default: 0 (disabled) intl_enable - BOOLEAN Enable or disable extension of User Message Interleaving functionality @@ -3286,10 +3651,12 @@ intl_enable - BOOLEAN to 1 and also needs to set socket options SCTP_FRAGMENT_INTERLEAVE to 2 and SCTP_INTERLEAVING_SUPPORTED to 1. - - 1: Enable extension. - - 0: Disable extension. + Possible values: - Default: 0 + - 0 (disabled) - Disable extension. + - 1 (enabled) - Enable extension. + + Default: 0 (disabled) ecn_enable - BOOLEAN Control use of Explicit Congestion Notification (ECN) by SCTP. @@ -3298,10 +3665,12 @@ ecn_enable - BOOLEAN due to congestion by allowing supporting routers to signal congestion before having to drop packets. - 1: Enable ecn. - 0: Disable ecn. + Possible values: - Default: 1 + - 0 (disabled) - Disable ecn. + - 1 (enabled) - Enable ecn. + + Default: 1 (enabled) l3mdev_accept - BOOLEAN Enabling this option allows a "global" bound socket to work @@ -3310,6 +3679,11 @@ l3mdev_accept - BOOLEAN originated. Only valid when the kernel was compiled with CONFIG_NET_L3_MASTER_DEV. + Possible values: + + - 0 (disabled) + - 1 (enabled) + Default: 1 (enabled) diff --git a/Documentation/networking/mptcp-sysctl.rst b/Documentation/networking/mptcp-sysctl.rst index 5bfab01eff5a..1683c139821e 100644 --- a/Documentation/networking/mptcp-sysctl.rst +++ b/Documentation/networking/mptcp-sysctl.rst @@ -12,6 +12,8 @@ add_addr_timeout - INTEGER (seconds) resent to an MPTCP peer that has not acknowledged a previous ADD_ADDR message. + Do not retransmit if set to 0. + The default value matches TCP_RTO_MAX. This is a per-namespace sysctl. diff --git a/Documentation/networking/mptcp.rst b/Documentation/networking/mptcp.rst index 17f2bab61164..2e31038d6462 100644 --- a/Documentation/networking/mptcp.rst +++ b/Documentation/networking/mptcp.rst @@ -60,10 +60,10 @@ address announcements. Typically, it is the client side that initiates subflows, and the server side that announces additional addresses via the ``ADD_ADDR`` and ``REMOVE_ADDR`` options. -Path managers are controlled by the ``net.mptcp.pm_type`` sysctl knob -- see -mptcp-sysctl.rst. There are two types: the in-kernel one (type ``0``) where the -same rules are applied for all the connections (see: ``ip mptcp``) ; and the -userspace one (type ``1``), controlled by a userspace daemon (i.e. `mptcpd +Path managers are controlled by the ``net.mptcp.path_manager`` sysctl knob -- +see mptcp-sysctl.rst. There are two types: the in-kernel one (``kernel``) where +the same rules are applied for all the connections (see: ``ip mptcp``) ; and the +userspace one (``userspace``), controlled by a userspace daemon (i.e. `mptcpd `_) where different rules can be applied for each connection. The path managers can be controlled via a Netlink API; see netlink_spec/mptcp_pm.rst. diff --git a/Documentation/networking/napi.rst b/Documentation/networking/napi.rst index d0e3953cae6a..7dd60366f4ff 100644 --- a/Documentation/networking/napi.rst +++ b/Documentation/networking/napi.rst @@ -433,9 +433,8 @@ Threaded NAPI Threaded NAPI is an operating mode that uses dedicated kernel threads rather than software IRQ context for NAPI processing. -The configuration is per netdevice and will affect all -NAPI instances of that device. Each NAPI instance will spawn a separate -thread (called ``napi/${ifc-name}-${napi-id}``). +Each threaded NAPI instance will spawn a separate thread +(called ``napi/${ifc-name}-${napi-id}``). It is recommended to pin each kernel thread to a single CPU, the same CPU as the CPU which services the interrupt. Note that the mapping @@ -444,7 +443,14 @@ dependent). The NAPI instance IDs will be assigned in the opposite order than the process IDs of the kernel threads. Threaded NAPI is controlled by writing 0/1 to the ``threaded`` file in -netdev's sysfs directory. +netdev's sysfs directory. It can also be enabled for a specific NAPI using +netlink interface. + +For example, using the script: + +.. code-block:: bash + + $ ynl --family netdev --do napi-set --json='{"id": 66, "threaded": 1}' .. rubric:: Footnotes diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index c69cc89c958e..1c19bb7705df 100644 --- a/Documentation/networking/net_cachelines/net_device.rst +++ b/Documentation/networking/net_cachelines/net_device.rst @@ -68,6 +68,7 @@ unsigned_char addr_assign_type unsigned_char addr_len unsigned_char upper_level unsigned_char lower_level +u8 threaded napi_poll(napi_enable,netif_set_threaded) unsigned_short neigh_priv_len unsigned_short padded unsigned_short dev_id @@ -165,7 +166,6 @@ struct sfp_bus* sfp_bus struct lock_class_key* qdisc_tx_busylock bool proto_down unsigned:1 wol_enabled -unsigned:1 threaded napi_poll(napi_enable,dev_set_threaded) unsigned_long:1 see_all_hwtstamp_requests unsigned_long:1 change_proto_down unsigned_long:1 netns_immutable diff --git a/Documentation/networking/net_cachelines/snmp.rst b/Documentation/networking/net_cachelines/snmp.rst index bd44b3eebbef..bce4eb35ec48 100644 --- a/Documentation/networking/net_cachelines/snmp.rst +++ b/Documentation/networking/net_cachelines/snmp.rst @@ -36,6 +36,7 @@ unsigned_long LINUX_MIB_TIMEWAITRECYCLED unsigned_long LINUX_MIB_TIMEWAITKILLED unsigned_long LINUX_MIB_PAWSACTIVEREJECTED unsigned_long LINUX_MIB_PAWSESTABREJECTED +unsigned_long LINUX_MIB_BEYOND_WINDOW unsigned_long LINUX_MIB_TSECR_REJECTED unsigned_long LINUX_MIB_PAWS_OLD_ACK unsigned_long LINUX_MIB_PAWS_TW_REJECTED diff --git a/Documentation/networking/net_cachelines/tcp_sock.rst b/Documentation/networking/net_cachelines/tcp_sock.rst index bc9b2131bf7a..7bbda5944ee2 100644 --- a/Documentation/networking/net_cachelines/tcp_sock.rst +++ b/Documentation/networking/net_cachelines/tcp_sock.rst @@ -115,7 +115,6 @@ u32 lost_out read_mostly read_m u32 sacked_out read_mostly read_mostly tcp_left_out(tx);tcp_packets_in_flight(tx/rx);tcp_clean_rtx_queue(rx) struct hrtimer pacing_timer struct hrtimer compressed_ack_timer -struct sk_buff* lost_skb_hint read_mostly tcp_clean_rtx_queue struct sk_buff* retransmit_skb_hint read_mostly tcp_clean_rtx_queue struct rb_root out_of_order_queue read_mostly tcp_data_queue,tcp_fast_path_check struct sk_buff* ooo_last_skb @@ -123,7 +122,6 @@ struct tcp_sack_block[1] duplicate_sack struct tcp_sack_block[4] selective_acks struct tcp_sack_block[4] recv_sack_cache struct sk_buff* highest_sack read_write tcp_event_new_data_sent -int lost_cnt_hint u32 prior_ssthresh u32 high_seq u32 retrans_stamp diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst index a0076b542e9c..59cb9982afe6 100644 --- a/Documentation/networking/netconsole.rst +++ b/Documentation/networking/netconsole.rst @@ -340,6 +340,38 @@ In this example, the message was sent by CPU 42. cpu=42 # kernel-populated value +Message ID auto population in userdata +-------------------------------------- + +Within the netconsole configfs hierarchy, there is a file named `msgid_enabled` +located in the `userdata` directory. This file controls the message ID +auto-population feature, which assigns a numeric id to each message sent to a +given target and appends the ID to userdata dictionary in every message sent. + +The message ID is generated using a per-target 32 bit counter that is +incremented for every message sent to the target. Note that this counter will +eventually wrap around after reaching uint32_t max value, so the message ID is +not globally unique over time. However, it can still be used by the target to +detect if messages were dropped before reaching the target by identifying gaps +in the sequence of IDs. + +It is important to distinguish message IDs from the message field. +Some kernel messages may never reach netconsole (for example, due to printk +rate limiting). Thus, a gap in cannot be solely relied upon to +indicate that a message was dropped during transmission, as it may never have +been sent via netconsole. The message ID, on the other hand, is only assigned +to messages that are actually transmitted via netconsole. + +Example:: + + echo "This is message #1" > /dev/kmsg + echo "This is message #2" > /dev/kmsg + 13,434,54928466,-;This is message #1 + msgid=1 + 13,435,54934019,-;This is message #2 + msgid=2 + + Extended console: ================= diff --git a/Documentation/networking/nf_conntrack-sysctl.rst b/Documentation/networking/nf_conntrack-sysctl.rst index 238b66d0e059..35f889259fcd 100644 --- a/Documentation/networking/nf_conntrack-sysctl.rst +++ b/Documentation/networking/nf_conntrack-sysctl.rst @@ -85,7 +85,6 @@ nf_conntrack_log_invalid - INTEGER - 1 - log ICMP packets - 6 - log TCP packets - 17 - log UDP packets - - 33 - log DCCP packets - 41 - log ICMPv6 packets - 136 - log UDPLITE packets - 255 - log packets of any protocol diff --git a/Documentation/networking/phy.rst b/Documentation/networking/phy.rst index f64641417c54..7f159043ad5a 100644 --- a/Documentation/networking/phy.rst +++ b/Documentation/networking/phy.rst @@ -333,6 +333,13 @@ Some of the interface modes are described below: SerDes lane, each port having speeds of 2.5G / 1G / 100M / 10M achieved through symbol replication. The PCS expects the standard USXGMII code word. +``PHY_INTERFACE_MODE_MIILITE`` + Non-standard, simplified MII mode, without TXER, RXER, CRS and COL signals + as defined for the MII. The absence of COL signal makes half-duplex link + modes impossible but does not interfere with BroadR-Reach link modes on + Broadcom (and other two-wire Ethernet) PHYs, because they are full-duplex + only. + Pause frames / flow control =========================== diff --git a/Documentation/networking/tls.rst b/Documentation/networking/tls.rst index c7904a1bc167..36cc7afc2527 100644 --- a/Documentation/networking/tls.rst +++ b/Documentation/networking/tls.rst @@ -16,11 +16,13 @@ User interface Creating a TLS connection ------------------------- -First create a new TCP socket and set the TLS ULP. +First create a new TCP socket and once the connection is established set the +TLS ULP. .. code-block:: c sock = socket(AF_INET, SOCK_STREAM, 0); + connect(sock, addr, addrlen); setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); Setting the TLS ULP allows us to set/get TLS socket options. Currently diff --git a/Documentation/networking/xdp-rx-metadata.rst b/Documentation/networking/xdp-rx-metadata.rst index a6e0ece18be5..ce96f4c99505 100644 --- a/Documentation/networking/xdp-rx-metadata.rst +++ b/Documentation/networking/xdp-rx-metadata.rst @@ -120,6 +120,39 @@ It is possible to query which kfunc the particular netdev implements via netlink. See ``xdp-rx-metadata-features`` attribute set in ``Documentation/netlink/specs/netdev.yaml``. +Driver Implementation +===================== + +Certain devices may prepend metadata to received packets. However, as of now, +``AF_XDP`` lacks the ability to communicate the size of the ``data_meta`` area +to the consumer. Therefore, it is the responsibility of the driver to copy any +device-reserved metadata out from the metadata area and ensure that +``xdp_buff->data_meta`` is pointing to ``xdp_buff->data`` before presenting the +frame to the XDP program. This is necessary so that, after the XDP program +adjusts the metadata area, the consumer can reliably retrieve the metadata +address using ``METADATA_SIZE`` offset. + +The following diagram shows how custom metadata is positioned relative to the +packet data and how pointers are adjusted for metadata access:: + + |<-- bpf_xdp_adjust_meta(xdp_buff, -METADATA_SIZE) --| + new xdp_buff->data_meta old xdp_buff->data_meta + | | + | xdp_buff->data + | | + +----------+----------------------------------------------------+------+ + | headroom | custom metadata | data | + +----------+----------------------------------------------------+------+ + | | + | xdp_desc->addr + |<------ xsk_umem__get_data() - METADATA_SIZE -------| + +``bpf_xdp_adjust_meta`` ensures that ``METADATA_SIZE`` is aligned to 4 bytes, +does not exceed 252 bytes, and leaves sufficient space for building the +xdp_frame. If these conditions are not met, it returns a negative error. In this +case, the BPF program should not proceed to populate data into the ``data_meta`` +area. + Example ======= diff --git a/Documentation/nvme/nvme-pci-endpoint-target.rst b/Documentation/nvme/nvme-pci-endpoint-target.rst index b699595d1762..69edf44f0d9a 100644 --- a/Documentation/nvme/nvme-pci-endpoint-target.rst +++ b/Documentation/nvme/nvme-pci-endpoint-target.rst @@ -6,21 +6,21 @@ NVMe PCI Endpoint Function Target :Author: Damien Le Moal -The NVMe PCI endpoint function target driver implements a NVMe PCIe controller -using a NVMe fabrics target controller configured with the PCI transport type. +The NVMe PCI endpoint function target driver implements an NVMe PCIe controller +using an NVMe fabrics target controller configured with the PCI transport type. Overview ======== -The NVMe PCI endpoint function target driver allows exposing a NVMe target +The NVMe PCI endpoint function target driver allows exposing an NVMe target controller over a PCIe link, thus implementing an NVMe PCIe device similar to a regular M.2 SSD. The target controller is created in the same manner as when using NVMe over fabrics: the controller represents the interface to an NVMe subsystem using a port. The port transfer type must be configured to be "pci". The subsystem can be configured to have namespaces backed by regular files or block devices, or can use NVMe passthrough to expose to the PCI host an -existing physical NVMe device or a NVMe fabrics host controller (e.g. a NVMe TCP -host controller). +existing physical NVMe device or an NVMe fabrics host controller (e.g. a NVMe +TCP host controller). The NVMe PCI endpoint function target driver relies as much as possible on the NVMe target core code to parse and execute NVMe commands submitted by the PCIe @@ -181,10 +181,10 @@ Creating an NVMe endpoint device is a two step process. First, an NVMe target subsystem and port must be defined. Second, the NVMe PCI endpoint device must be setup and bound to the subsystem and port created. -Creating a NVMe Subsystem and Port ----------------------------------- +Creating an NVMe Subsystem and Port +----------------------------------- -Details about how to configure a NVMe target subsystem and port are outside the +Details about how to configure an NVMe target subsystem and port are outside the scope of this document. The following only provides a simple example of a port and subsystem with a single namespace backed by a null_blk device. @@ -234,8 +234,8 @@ Finally, create the target port and link it to the subsystem:: # ln -s /sys/kernel/config/nvmet/subsystems/nvmepf.0.nqn \ /sys/kernel/config/nvmet/ports/1/subsystems/nvmepf.0.nqn -Creating a NVMe PCI Endpoint Device ------------------------------------ +Creating an NVMe PCI Endpoint Device +------------------------------------ With the NVMe target subsystem and port ready for use, the NVMe PCI endpoint device can now be created and enabled. The NVMe PCI endpoint target driver @@ -303,7 +303,7 @@ device controller:: nvmet_pci_epf nvmet_pci_epf.0: Enabling controller -On the host side, the NVMe PCI endpoint function target device will is +On the host side, the NVMe PCI endpoint function target device is discoverable as a PCI device, with the vendor ID and device ID as configured:: # lspci -n diff --git a/Documentation/power/pm_qos_interface.rst b/Documentation/power/pm_qos_interface.rst index 69b0fe3e2542..5019c79c7710 100644 --- a/Documentation/power/pm_qos_interface.rst +++ b/Documentation/power/pm_qos_interface.rst @@ -52,13 +52,6 @@ int cpu_latency_qos_request_active(handle): Returns if the request is still active, i.e. it has not been removed from the CPU latency QoS list. -int cpu_latency_qos_add_notifier(notifier): - Adds a notification callback function to the CPU latency QoS. The callback is - called when the aggregated value for the CPU latency QoS is changed. - -int cpu_latency_qos_remove_notifier(notifier): - Removes the notification callback function from the CPU latency QoS. - From user space: diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst index 63344bea8393..c8dbdb8595e5 100644 --- a/Documentation/power/runtime_pm.rst +++ b/Documentation/power/runtime_pm.rst @@ -154,11 +154,9 @@ suspending the device are satisfied) and to queue up a suspend request for the device in that case. If there is no idle callback, or if the callback returns 0, then the PM core will attempt to carry out a runtime suspend of the device, also respecting devices configured for autosuspend. In essence this means a -call to pm_runtime_autosuspend() (do note that drivers needs to update the -device last busy mark, pm_runtime_mark_last_busy(), to control the delay under -this circumstance). To prevent this (for example, if the callback routine has -started a delayed suspend), the routine must return a non-zero value. Negative -error return codes are ignored by the PM core. +call to pm_runtime_autosuspend(). To prevent this (for example, if the callback +routine has started a delayed suspend), the routine must return a non-zero +value. Negative error return codes are ignored by the PM core. The helper functions provided by the PM core, described in Section 4, guarantee that the following constraints are met with respect to runtime PM callbacks for @@ -330,10 +328,9 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: 'power.disable_depth' is different from 0 `int pm_runtime_autosuspend(struct device *dev);` - - same as pm_runtime_suspend() except that the autosuspend delay is taken - `into account;` if pm_runtime_autosuspend_expiration() says the delay has - not yet expired then an autosuspend is scheduled for the appropriate time - and 0 is returned + - same as pm_runtime_suspend() except that a call to + pm_runtime_mark_last_busy() is made and an autosuspend is scheduled for + the appropriate time and 0 is returned `int pm_runtime_resume(struct device *dev);` - execute the subsystem-level resume callback for the device; returns 0 on @@ -357,9 +354,9 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: success or error code if the request has not been queued up `int pm_request_autosuspend(struct device *dev);` - - schedule the execution of the subsystem-level suspend callback for the - device when the autosuspend delay has expired; if the delay has already - expired then the work item is queued up immediately + - Call pm_runtime_mark_last_busy() and schedule the execution of the + subsystem-level suspend callback for the device when the autosuspend delay + expires `int pm_schedule_suspend(struct device *dev, unsigned int delay);` - schedule the execution of the subsystem-level suspend callback for the @@ -411,8 +408,9 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: pm_request_idle(dev) and return its result `int pm_runtime_put_autosuspend(struct device *dev);` - - does the same as __pm_runtime_put_autosuspend() for now, but in the - future, will also call pm_runtime_mark_last_busy() as well, DO NOT USE! + - set the power.last_busy field to the current time and decrement the + device's usage counter; if the result is 0 then run + pm_request_autosuspend(dev) and return its result `int __pm_runtime_put_autosuspend(struct device *dev);` - decrement the device's usage counter; if the result is 0 then run @@ -427,7 +425,8 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: pm_runtime_suspend(dev) and return its result `int pm_runtime_put_sync_autosuspend(struct device *dev);` - - decrement the device's usage counter; if the result is 0 then run + - set the power.last_busy field to the current time and decrement the + device's usage counter; if the result is 0 then run pm_runtime_autosuspend(dev) and return its result `void pm_runtime_enable(struct device *dev);` @@ -870,11 +869,9 @@ device is automatically suspended (the subsystem or driver still has to call the appropriate PM routines); rather it means that runtime suspends will automatically be delayed until the desired period of inactivity has elapsed. -Inactivity is determined based on the power.last_busy field. Drivers should -call pm_runtime_mark_last_busy() to update this field after carrying out I/O, -typically just before calling __pm_runtime_put_autosuspend(). The desired -length of the inactivity period is a matter of policy. Subsystems can set this -length initially by calling pm_runtime_set_autosuspend_delay(), but after device +Inactivity is determined based on the power.last_busy field. The desired length +of the inactivity period is a matter of policy. Subsystems can set this length +initially by calling pm_runtime_set_autosuspend_delay(), but after device registration the length should be controlled by user space, using the /sys/devices/.../power/autosuspend_delay_ms attribute. @@ -885,12 +882,13 @@ instead of the non-autosuspend counterparts:: Instead of: pm_runtime_suspend use: pm_runtime_autosuspend; Instead of: pm_schedule_suspend use: pm_request_autosuspend; - Instead of: pm_runtime_put use: __pm_runtime_put_autosuspend; + Instead of: pm_runtime_put use: pm_runtime_put_autosuspend; Instead of: pm_runtime_put_sync use: pm_runtime_put_sync_autosuspend. Drivers may also continue to use the non-autosuspend helper functions; they will behave normally, which means sometimes taking the autosuspend delay into -account (see pm_runtime_idle). +account (see pm_runtime_idle). The autosuspend variants of the functions also +call pm_runtime_mark_last_busy(). Under some circumstances a driver or subsystem may want to prevent a device from autosuspending immediately, even though the usage counter is zero and the @@ -922,12 +920,10 @@ Here is a schematic pseudo-code example:: foo_io_completion(struct foo_priv *foo, void *req) { lock(&foo->private_lock); - if (--foo->num_pending_requests == 0) { - pm_runtime_mark_last_busy(&foo->dev); - __pm_runtime_put_autosuspend(&foo->dev); - } else { + if (--foo->num_pending_requests == 0) + pm_runtime_put_autosuspend(&foo->dev); + else foo_process_next_request(foo); - } unlock(&foo->private_lock); /* Send req result back to the user ... */ } diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index b14bd5b7cbc9..bccfa19b45df 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -43,7 +43,6 @@ util-linux 2.10o mount --version kmod 13 depmod -V e2fsprogs 1.41.4 e2fsck -V jfsutils 1.1.3 fsck.jfs -V -reiserfsprogs 3.6.3 reiserfsck -V xfsprogs 2.6.0 xfs_db -V squashfs-tools 4.0 mksquashfs -version btrfs-progs 0.18 btrfs --version @@ -262,14 +261,6 @@ The following utilities are available: - other file system utilities are also available in this package. -Reiserfsprogs -------------- - -The reiserfsprogs package should be used for reiserfs-3.6.x -(Linux kernels 2.4.x). It is a combined package and contains working -versions of ``mkreiserfs``, ``resize_reiserfs``, ``debugreiserfs`` and -``reiserfsck``. These utils work on both i386 and alpha platforms. - Xfsprogs -------- @@ -493,11 +484,6 @@ JFSutils - -Reiserfsprogs -------------- - -- - Xfsprogs -------- diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index 19d2ed47ff79..d1a8e5465ed9 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -614,7 +614,10 @@ it. When commenting the kernel API functions, please use the kernel-doc format. See the files at :ref:`Documentation/doc-guide/ ` and -``scripts/kernel-doc`` for details. +``scripts/kernel-doc`` for details. Note that the danger of over-commenting +applies to kernel-doc comments all the same. Do not add boilerplate +kernel-doc which simply reiterates what's obvious from the signature +of the function. The preferred style for long (multi-line) comments is: diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index da6bf0f6d01e..34e00848e0da 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -290,6 +290,7 @@ an involved disclosed party. The current ambassadors list: AMD Tom Lendacky Ampere Darren Hart ARM Catalin Marinas + IBM Power Madhavan Srinivasan IBM Z Christian Borntraeger Intel Tony Luck Qualcomm Trilok Soni diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst index 1ac62dc3a66f..e1755610b4bc 100644 --- a/Documentation/process/maintainer-netdev.rst +++ b/Documentation/process/maintainer-netdev.rst @@ -312,7 +312,7 @@ Posting as one thread is discouraged because it confuses patchwork (as of patchwork 2.2.2). Co-posting selftests --------------------- +~~~~~~~~~~~~~~~~~~~~ Selftests should be part of the same series as the code changes. Specifically for fixes both code change and related test should go into diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 56c560a00b37..84657e7d2e5b 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -8,8 +8,22 @@ like to know when a security bug is found so that it can be fixed and disclosed as quickly as possible. Please report security bugs to the Linux kernel security team. -Contact -------- +The security team and maintainers almost always require additional +information beyond what was initially provided in a report and rely on +active and efficient collaboration with the reporter to perform further +testing (e.g., verifying versions, configuration options, mitigations, or +patches). Before contacting the security team, the reporter must ensure +they are available to explain their findings, engage in discussions, and +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. + +As it is with any bug, the more information provided the easier it +will be to diagnose and fix. Please review the procedure outlined in +'Documentation/admin-guide/reporting-issues.rst' if you are unclear about what +information is helpful. Any exploit code is very helpful and will not +be released without consent from the reporter unless it has already been +made public. The Linux kernel security team can be contacted by email at . This is a private list of security officers @@ -19,13 +33,6 @@ that can speed up the process considerably. It is possible that the security team will bring in extra help from area maintainers to understand and fix the security vulnerability. -As it is with any bug, the more information provided the easier it -will be to diagnose and fix. Please review the procedure outlined in -'Documentation/admin-guide/reporting-issues.rst' if you are unclear about what -information is helpful. Any exploit code is very helpful and will not -be released without consent from the reporter unless it has already been -made public. - Please send plain text emails without attachments where possible. It is much harder to have a context-quoted discussion about a complex issue if all the details are hidden away in attachments. Think of it like a diff --git a/Documentation/scheduler/sched-deadline.rst b/Documentation/scheduler/sched-deadline.rst index a727827b8dd5..ec543a12f848 100644 --- a/Documentation/scheduler/sched-deadline.rst +++ b/Documentation/scheduler/sched-deadline.rst @@ -20,7 +20,8 @@ Deadline Task Scheduling 4.3 Default behavior 4.4 Behavior of sched_yield() 5. Tasks CPU affinity - 5.1 SCHED_DEADLINE and cpusets HOWTO + 5.1 Using cgroup v1 cpuset controller + 5.2 Using cgroup v2 cpuset controller 6. Future plans A. Test suite B. Minimal main() @@ -671,15 +672,17 @@ Deadline Task Scheduling 5. Tasks CPU affinity ===================== - -deadline tasks cannot have an affinity mask smaller that the entire - root_domain they are created on. However, affinities can be specified - through the cpuset facility (Documentation/admin-guide/cgroup-v1/cpusets.rst). + Deadline tasks cannot have a cpu affinity mask smaller than the root domain they + are created on. So, using ``sched_setaffinity(2)`` won't work. Instead, the + the deadline task should be created in a restricted root domain. This can be + done using the cpuset controller of either cgroup v1 (deprecated) or cgroup v2. + See :ref:`Documentation/admin-guide/cgroup-v1/cpusets.rst ` and + :ref:`Documentation/admin-guide/cgroup-v2.rst ` for more information. -5.1 SCHED_DEADLINE and cpusets HOWTO ------------------------------------- +5.1 Using cgroup v1 cpuset controller +------------------------------------- - An example of a simple configuration (pin a -deadline task to CPU0) - follows (rt-app is used to create a -deadline task):: + An example of a simple configuration (pin a -deadline task to CPU0) follows:: mkdir /dev/cpuset mount -t cgroup -o cpuset cpuset /dev/cpuset @@ -692,8 +695,20 @@ Deadline Task Scheduling echo 1 > cpu0/cpuset.cpu_exclusive echo 1 > cpu0/cpuset.mem_exclusive echo $$ > cpu0/tasks - rt-app -t 100000:10000:d:0 -D5 # it is now actually superfluous to specify - # task affinity + chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null + +5.2 Using cgroup v2 cpuset controller +------------------------------------- + + Assuming the cgroup v2 root is mounted at ``/sys/fs/cgroup``. + + cd /sys/fs/cgroup + echo '+cpuset' > cgroup.subtree_control + mkdir deadline_group + echo 0 > deadline_group/cpuset.cpus + echo 'root' > deadline_group/cpuset.cpus.partition + echo $$ > deadline_group/cgroup.procs + chrt --sched-runtime 100000 --sched-period 200000 --deadline 0 yes > /dev/null 6. Future plans =============== @@ -731,24 +746,38 @@ Appendix A. Test suite behaves under such workloads. In this way, results are easily reproducible. rt-app is available at: https://github.com/scheduler-tools/rt-app. - Thread parameters can be specified from the command line, with something like - this:: + rt-app does not accept command line arguments, and instead reads from a JSON + configuration file. Here is an example ``config.json``: - # rt-app -t 100000:10000:d -t 150000:20000:f:10 -D5 + .. code-block:: json - The above creates 2 threads. The first one, scheduled by SCHED_DEADLINE, - executes for 10ms every 100ms. The second one, scheduled at SCHED_FIFO - priority 10, executes for 20ms every 150ms. The test will run for a total - of 5 seconds. + { + "tasks": { + "dl_task": { + "policy": "SCHED_DEADLINE", + "priority": 0, + "dl-runtime": 10000, + "dl-period": 100000, + "dl-deadline": 100000 + }, + "fifo_task": { + "policy": "SCHED_FIFO", + "priority": 10, + "runtime": 20000, + "sleep": 130000 + } + }, + "global": { + "duration": 5 + } + } - More interestingly, configurations can be described with a json file that - can be passed as input to rt-app with something like this:: + On running ``rt-app config.json``, it creates 2 threads. The first one, + scheduled by SCHED_DEADLINE, executes for 10ms every 100ms. The second one, + scheduled at SCHED_FIFO priority 10, executes for 20ms every 150ms. The test + will run for a total of 5 seconds. - # rt-app my_config.json - - The parameters that can be specified with the second method are a superset - of the command line options. Please refer to rt-app documentation for more - details (`/doc/*.json`). + Please refer to the rt-app documentation for the JSON schema and more examples. The second testing application is done using chrt which has support for SCHED_DEADLINE. diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst index a1869c38046e..404fe6126a76 100644 --- a/Documentation/scheduler/sched-ext.rst +++ b/Documentation/scheduler/sched-ext.rst @@ -313,16 +313,21 @@ by a sched_ext scheduler: ops.runnable(); /* Task becomes ready to run */ while (task is runnable) { - if (task is not in a DSQ) { + if (task is not in a DSQ && task->scx.slice == 0) { ops.enqueue(); /* Task can be added to a DSQ */ - /* A CPU becomes available */ + /* Any usable CPU becomes available */ ops.dispatch(); /* Task is moved to a local DSQ */ } ops.running(); /* Task starts running on its assigned CPU */ - ops.tick(); /* Called every 1/HZ seconds */ + while (task->scx.slice > 0 && task is runnable) + ops.tick(); /* Called every 1/HZ seconds */ ops.stopping(); /* Task stops running (time slice expires or wait) */ + + /* Task's CPU becomes available */ + + ops.dispatch(); /* task->scx.slice can be refilled */ } ops.quiescent(); /* Task releases its assigned CPU (wait) */ diff --git a/Documentation/scheduler/sched-stats.rst b/Documentation/scheduler/sched-stats.rst index d82e7d2b54f0..9d6a337755f4 100644 --- a/Documentation/scheduler/sched-stats.rst +++ b/Documentation/scheduler/sched-stats.rst @@ -86,13 +86,16 @@ Domain statistics ----------------- One of these is produced per domain for each cpu described. (Note that if CONFIG_SMP is not defined, *no* domains are utilized and these lines -will not appear in the output. is an extension to the domain field -that prints the name of the corresponding sched domain. It can appear in -schedstat version 17 and above. +will not appear in the output.) domain 1 2 3 4 5 6 7 8 9 10 11 12 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 45 -The first field is a bit mask indicating what cpus this domain operates over. +The field prints the name of the sched domain and is only supported +with schedstat version >= 17. On previous versions, is the first +field. + +The field is a bit mask indicating what cpus this domain operates +over. The next 33 are a variety of sched_balance_rq() statistics in grouped into types of idleness (busy, idle and newly idle): @@ -103,12 +106,13 @@ of idleness (busy, idle and newly idle): load did not require balancing when busy 3) # of times in this domain sched_balance_rq() tried to move one or more tasks and failed, when the cpu was busy - 4) Total imbalance in load when the cpu was busy - 5) Total imbalance in utilization when the cpu was busy - 6) Total imbalance in number of tasks when the cpu was busy - 7) Total imbalance due to misfit tasks when the cpu was busy - 8) # of times in this domain pull_task() was called when busy - 9) # of times in this domain pull_task() was called even though the + 4) Total imbalance in load in this domain when the cpu was busy + 5) Total imbalance in utilization in this domain when the cpu was busy + 6) Total imbalance in number of tasks in this domain when the cpu was busy + 7) Total imbalance due to misfit tasks in this domain when the cpu was + busy + 8) # of times in this domain detach_task() was called when busy + 9) # of times in this domain detach_task() was called even though the target task was cache-hot when busy 10) # of times in this domain sched_balance_rq() was called but did not find a busier queue while the cpu was busy @@ -121,13 +125,14 @@ of idleness (busy, idle and newly idle): the load did not require balancing when the cpu was idle 14) # of times in this domain sched_balance_rq() tried to move one or more tasks and failed, when the cpu was idle - 15) Total imbalance in load when the cpu was idle - 16) Total imbalance in utilization when the cpu was idle - 17) Total imbalance in number of tasks when the cpu was idle - 18) Total imbalance due to misfit tasks when the cpu was idle - 19) # of times in this domain pull_task() was called when the cpu + 15) Total imbalance in load in this domain when the cpu was idle + 16) Total imbalance in utilization in this domain when the cpu was idle + 17) Total imbalance in number of tasks in this domain when the cpu was idle + 18) Total imbalance due to misfit tasks in this domain when the cpu was + idle + 19) # of times in this domain detach_task() was called when the cpu was idle - 20) # of times in this domain pull_task() was called even though + 20) # of times in this domain detach_task() was called even though the target task was cache-hot when idle 21) # of times in this domain sched_balance_rq() was called but did not find a busier queue while the cpu was idle @@ -140,12 +145,16 @@ of idleness (busy, idle and newly idle): load did not require balancing when the cpu was just becoming idle 25) # of times in this domain sched_balance_rq() tried to move one or more tasks and failed, when the cpu was just becoming idle - 26) Total imbalance in load when the cpu was just becoming idle - 27) Total imbalance in utilization when the cpu was just becoming idle - 28) Total imbalance in number of tasks when the cpu was just becoming idle - 29) Total imbalance due to misfit tasks when the cpu was just becoming idle - 30) # of times in this domain pull_task() was called when newly idle - 31) # of times in this domain pull_task() was called even though the + 26) Total imbalance in load in this domain when the cpu was just becoming + idle + 27) Total imbalance in utilization in this domain when the cpu was just + becoming idle + 28) Total imbalance in number of tasks in this domain when the cpu was just + becoming idle + 29) Total imbalance due to misfit tasks in this domain when the cpu was + just becoming idle + 30) # of times in this domain detach_task() was called when newly idle + 31) # of times in this domain detach_task() was called even though the target task was cache-hot when just becoming idle 32) # of times in this domain sched_balance_rq() was called but did not find a busier queue while the cpu was just becoming idle diff --git a/Documentation/scsi/scsi_fc_transport.rst b/Documentation/scsi/scsi_fc_transport.rst index e3ddcfb7f8fd..5ef75575924e 100644 --- a/Documentation/scsi/scsi_fc_transport.rst +++ b/Documentation/scsi/scsi_fc_transport.rst @@ -30,7 +30,40 @@ This file is found at Documentation/scsi/scsi_fc_transport.rst FC Remote Ports (rports) ======================== -<< To Be Supplied >> + + In the Fibre Channel (FC) subsystem, a remote port (rport) refers to a + remote Fibre Channel node that the local port can communicate with. + These are typically storage targets (e.g., arrays, tapes) that respond + to SCSI commands over FC transport. + + In Linux, rports are managed by the FC transport class and are + represented in sysfs under: + + /sys/class/fc_remote_ports/ + + Each rport directory contains attributes describing the remote port, + such as port ID, node name, port state, and link speed. + + rports are typically created by the FC transport when a new device is + discovered during a fabric login or scan, and they persist until the + device is removed or the link is lost. + + Common attributes: + - node_name: World Wide Node Name (WWNN). + - port_name: World Wide Port Name (WWPN). + - port_id: FC address of the remote port. + - roles: Indicates if the port is an initiator, target, or both. + - port_state: Shows the current operational state. + + After discovering a remote port, the driver typically populates a + fc_rport_identifiers structure and invokes fc_remote_port_add() to + create and register the remote port with the SCSI subsystem via the + Fibre Channel (FC) transport class. + + rports are also visible via sysfs as children of the FC host adapter. + + For developers: use fc_remote_port_add() and fc_remote_port_delete() when + implementing a driver that interacts with the FC transport class. FC Virtual Ports (vports) diff --git a/Documentation/security/credentials.rst b/Documentation/security/credentials.rst index 2aa0791bcefe..d0191c8b8060 100644 --- a/Documentation/security/credentials.rst +++ b/Documentation/security/credentials.rst @@ -555,5 +555,5 @@ the VFS, and that can be done by calling into such as ``vfs_mkdir()`` with a different set of credentials. This is done in the following places: * ``sys_faccessat()``. - * ``do_coredump()``. + * ``vfs_coredump()``. * nfs4recover.c. diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst index 910668e665cb..a32ca23c21b0 100644 --- a/Documentation/security/self-protection.rst +++ b/Documentation/security/self-protection.rst @@ -303,7 +303,7 @@ Memory poisoning When releasing memory, it is best to poison the contents, to avoid reuse attacks that rely on the old contents of memory. E.g., clear stack on a -syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a +syscall return (``CONFIG_KSTACK_ERASE``), wipe heap memory on a free. This frustrates many uninitialized variable attacks, stack content exposures, heap content exposures, and use-after-free attacks. diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index a45174d165eb..062b86522e4d 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2253,8 +2253,15 @@ device_setup Default: 0x0000 ignore_ctl_error Ignore any USB-controller regarding mixer interface (default: no) + ``ignore_ctl_error=1`` may help when you get an error at accessing + the mixer element such as URB error -22. This happens on some + buggy USB device or the controller. This workaround corresponds to + the ``quirk_flags`` bit 14, too. autoclock Enable auto-clock selection for UAC2 devices (default: yes) +lowlatency + Enable low latency playback mode (default: yes). + Could disable it to switch back to the old mode if face a regression. quirk_alias Quirk alias list, pass strings like ``0123abcd:5678beef``, which applies the existing quirk for the device 5678:beef to a new @@ -2284,6 +2291,11 @@ delayed_register The driver prints a message like "Found post-registration device assignment: 1234abcd:04" for such a device, so that user can notice the need. +skip_validation + Skip unit descriptor validation (default: no). + The option is used to ignores the validation errors with the hexdump + of the unit descriptor instead of a driver probe error, so that we + can check its details. quirk_flags Contains the bit flags for various device specific workarounds. Applied to the corresponding card index. @@ -2307,6 +2319,16 @@ quirk_flags * bit 16: Set up the interface at first like UAC1 * bit 17: Apply the generic implicit feedback sync mode * bit 18: Don't apply implicit feedback sync mode + * bit 19: Don't closed interface during setting sample rate + * bit 20: Force an interface reset whenever stopping & restarting + a stream + * bit 21: Do not set PCM rate (frequency) when only one rate is + available for the given endpoint. + * bit 22: Set the fixed resolution 16 for Mic Capture Volume + * bit 23: Set the fixed resolution 384 for Mic Capture Volume + * bit 24: Set minimum volume control value as mute for devices + where the lowest playback value represents muted state instead + of minimum audible volume This module supports multiple devices, autoprobe and hotplugging. @@ -2314,10 +2336,9 @@ NB: ``nrpacks`` parameter can be modified dynamically via sysfs. Don't put the value over 20. Changing via sysfs has no sanity check. -NB: ``ignore_ctl_error=1`` may help when you get an error at accessing -the mixer element such as URB error -22. This happens on some -buggy USB device or the controller. This workaround corresponds to -the ``quirk_flags`` bit 14, too. +NB: ``ignore_ctl_error=1`` just provides a quick way to work around the +issues. If you have a buggy device that requires these quirks, please +report it to the upstream. NB: ``quirk_alias`` option is provided only for testing / development. If you want to have a proper support, contact to upstream for diff --git a/Documentation/sound/codecs/cs35l56.rst b/Documentation/sound/codecs/cs35l56.rst index 98c6f6c74394..57d1964453e1 100644 --- a/Documentation/sound/codecs/cs35l56.rst +++ b/Documentation/sound/codecs/cs35l56.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0-only -===================================================================== -Audio drivers for Cirrus Logic CS35L54/56/57 Boosted Smart Amplifiers -===================================================================== +======================================================================== +Audio drivers for Cirrus Logic CS35L54/56/57/63 Boosted Smart Amplifiers +======================================================================== :Copyright: 2025 Cirrus Logic, Inc. and Cirrus Logic International Semiconductor Ltd. @@ -13,11 +13,11 @@ Summary The high-level summary of this document is: -**If you have a laptop that uses CS35L54/56/57 amplifiers but audio is not +**If you have a laptop that uses CS35L54/56/57/63 amplifiers but audio is not working, DO NOT ATTEMPT TO USE FIRMWARE AND SETTINGS FROM ANOTHER LAPTOP, EVEN IF THAT LAPTOP SEEMS SIMILAR.** -The CS35L54/56/57 amplifiers must be correctly configured for the power +The CS35L54/56/57/63 amplifiers must be correctly configured for the power supply voltage, speaker impedance, maximum speaker voltage/current, and other external hardware connections. @@ -34,6 +34,7 @@ The cs35l56 drivers support: * CS35L54 * CS35L56 * CS35L57 +* CS35L63 There are two drivers in the kernel @@ -104,6 +105,13 @@ In this example the SSID is 10280c63. The format of the firmware file names is: +SoundWire (except CS35L56 Rev B0): + cs35lxx-b0-dsp1-misc-SSID[-spkidX]-l?u? + +SoundWire CS35L56 Rev B0: + cs35lxx-b0-dsp1-misc-SSID[-spkidX]-ampN + +Non-SoundWire (HDA and I2S): cs35lxx-b0-dsp1-misc-SSID[-spkidX]-ampN Where: @@ -111,12 +119,18 @@ Where: * cs35lxx-b0 is the amplifier model and silicon revision. This information is logged by the driver during initialization. * SSID is the 8-digit hexadecimal SSID value. + * l?u? is the physical address on the SoundWire bus of the amp this + file applies to. * ampN is the amplifier number (for example amp1). This is the same as the prefix on the ALSA control names except that it is always lower-case in the file name. * spkidX is an optional part, used for laptops that have firmware configurations for different makes and models of internal speakers. +The CS35L56 Rev B0 continues to use the old filename scheme because a +large number of firmware files have already been published with these +names. + Sound Open Firmware and ALSA topology files ------------------------------------------- diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css index f4285417c71a..06cedbae095c 100644 --- a/Documentation/sphinx-static/custom.css +++ b/Documentation/sphinx-static/custom.css @@ -136,3 +136,18 @@ div.language-selection:hover ul { div.language-selection ul li:hover { background: #dddddd; } + +/* Make xrefs more universally visible */ +a.reference, a.reference:hover { + border-bottom: none; + text-decoration: underline; + text-underline-offset: 0.3em; +} + +/* Slightly different style for sidebar links */ +div.sphinxsidebar a { border-bottom: none; } +div.sphinxsidebar a:hover { + border-bottom: none; + text-decoration: underline; + text-underline-offset: 0.3em; +} diff --git a/Documentation/sphinx/automarkup.py b/Documentation/sphinx/automarkup.py index fd633f7a0bc3..563033f764bb 100644 --- a/Documentation/sphinx/automarkup.py +++ b/Documentation/sphinx/automarkup.py @@ -22,12 +22,6 @@ from kernel_abi import get_kernel_abi # RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(\))', flags=re.ASCII) -# -# Sphinx 2 uses the same :c:type role for struct, union, enum and typedef -# -RE_generic_type = re.compile(r'\b(struct|union|enum|typedef)\s+([a-zA-Z_]\w+)', - flags=re.ASCII) - # # Sphinx 3 uses a different C role for each one of struct, union, enum and # typedef @@ -150,20 +144,12 @@ def markup_func_ref_sphinx3(docname, app, match): return target_text def markup_c_ref(docname, app, match): - class_str = {# Sphinx 2 only - RE_function: 'c-func', - RE_generic_type: 'c-type', - # Sphinx 3+ only - RE_struct: 'c-struct', + class_str = {RE_struct: 'c-struct', RE_union: 'c-union', RE_enum: 'c-enum', RE_typedef: 'c-type', } - reftype_str = {# Sphinx 2 only - RE_function: 'function', - RE_generic_type: 'type', - # Sphinx 3+ only - RE_struct: 'struct', + reftype_str = {RE_struct: 'struct', RE_union: 'union', RE_enum: 'enum', RE_typedef: 'type', @@ -249,8 +235,13 @@ def add_and_resolve_xref(app, docname, domain, reftype, target, contnode=None): if xref: return xref - - return None + # + # We didn't find the xref; if a container node was supplied, + # mark it as a broken xref + # + if contnode: + contnode['classes'].append("broken_xref") + return contnode # # Variant of markup_abi_ref() that warns whan a reference is not found diff --git a/Documentation/sphinx/cdomain.py b/Documentation/sphinx/cdomain.py index e8ea80d4324c..3dc285dc70f5 100644 --- a/Documentation/sphinx/cdomain.py +++ b/Documentation/sphinx/cdomain.py @@ -1,4 +1,5 @@ # -*- coding: utf-8; mode: python -*- +# SPDX-License-Identifier: GPL-2.0 # pylint: disable=W0141,C0113,C0103,C0325 """ cdomain diff --git a/Documentation/sphinx/kernel_abi.py b/Documentation/sphinx/kernel_abi.py index db6f0380de94..4c4375201b9e 100644 --- a/Documentation/sphinx/kernel_abi.py +++ b/Documentation/sphinx/kernel_abi.py @@ -146,8 +146,10 @@ class KernelCmd(Directive): n += 1 if f != old_f: - # Add the file to Sphinx build dependencies - env.note_dependency(os.path.abspath(f)) + # Add the file to Sphinx build dependencies if the file exists + fname = os.path.join(srctree, f) + if os.path.isfile(fname): + env.note_dependency(fname) old_f = f diff --git a/Documentation/sphinx/kernel_include.py b/Documentation/sphinx/kernel_include.py index 8db176045bc5..1e566e87ebcd 100755 --- a/Documentation/sphinx/kernel_include.py +++ b/Documentation/sphinx/kernel_include.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8; mode: python -*- +# SPDX-License-Identifier: GPL-2.0 # pylint: disable=R0903, C0330, R0914, R0912, E0401 """ diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerneldoc.py index b818d4c77924..2586b4d4e494 100644 --- a/Documentation/sphinx/kerneldoc.py +++ b/Documentation/sphinx/kerneldoc.py @@ -1,4 +1,5 @@ # coding=utf-8 +# SPDX-License-Identifier: MIT # # Copyright © 2016 Intel Corporation # @@ -24,8 +25,6 @@ # Authors: # Jani Nikula # -# Please make sure this works on both python2 and python3. -# import codecs import os diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py index f1a7f13c9c60..ad495c0da270 100644 --- a/Documentation/sphinx/kfigure.py +++ b/Documentation/sphinx/kfigure.py @@ -1,4 +1,5 @@ # -*- coding: utf-8; mode: python -*- +# SPDX-License-Identifier: GPL-2.0 # pylint: disable=C0103, R0903, R0912, R0915 """ scalable figure and image handling diff --git a/Documentation/sphinx/load_config.py b/Documentation/sphinx/load_config.py index ec50e1ee5223..1afb0c97f06b 100644 --- a/Documentation/sphinx/load_config.py +++ b/Documentation/sphinx/load_config.py @@ -1,4 +1,5 @@ # -*- coding: utf-8; mode: python -*- +# SPDX-License-Identifier: GPL-2.0 # pylint: disable=R0903, C0330, R0914, R0912, E0401 import os diff --git a/Documentation/sphinx/min_requirements.txt b/Documentation/sphinx/min_requirements.txt new file mode 100644 index 000000000000..96b5e0bfa3d7 --- /dev/null +++ b/Documentation/sphinx/min_requirements.txt @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +alabaster >=0.7,<0.8 +docutils>=0.15,<0.18 +jinja2>=2.3,<3.1 +PyYAML>=5.1,<6.1 +Sphinx==3.4.3 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.1 +sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-qthelp==1.0.2 +sphinxcontrib-serializinghtml==1.1.4 diff --git a/Documentation/sphinx/parse-headers.pl b/Documentation/sphinx/parse-headers.pl index b063f2f1cfb2..7b1458544e2e 100755 --- a/Documentation/sphinx/parse-headers.pl +++ b/Documentation/sphinx/parse-headers.pl @@ -1,4 +1,7 @@ #!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016 by Mauro Carvalho Chehab . + use strict; use Text::Tabs; use Getopt::Long; @@ -391,7 +394,7 @@ Report bugs to Mauro Carvalho Chehab =head1 COPYRIGHT -Copyright (c) 2016 by Mauro Carvalho Chehab . +Copyright (c) 2016 by Mauro Carvalho Chehab . License GPLv2: GNU GPL version 2 . diff --git a/Documentation/sphinx/requirements.txt b/Documentation/sphinx/requirements.txt index 5017f307c8a4..76b4255061d0 100644 --- a/Documentation/sphinx/requirements.txt +++ b/Documentation/sphinx/requirements.txt @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 alabaster Sphinx pyyaml diff --git a/Documentation/sphinx/rstFlatTable.py b/Documentation/sphinx/rstFlatTable.py index 180fbb50c337..3d19569e5728 100755 --- a/Documentation/sphinx/rstFlatTable.py +++ b/Documentation/sphinx/rstFlatTable.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8; mode: python -*- +# SPDX-License-Identifier: GPL-2.0 # pylint: disable=C0330, R0903, R0912 """ diff --git a/Documentation/tools/rtla/common_appendix.rst b/Documentation/tools/rtla/common_appendix.rst index b5cf2dc223df..53cae7537537 100644 --- a/Documentation/tools/rtla/common_appendix.rst +++ b/Documentation/tools/rtla/common_appendix.rst @@ -1,3 +1,14 @@ +.. SPDX-License-Identifier: GPL-2.0 + +EXIT STATUS +=========== + +:: + + 0 Passed: the test did not hit the stop tracing condition + 1 Error: invalid argument + 2 Failed: the test hit the stop tracing condition + REPORTING BUGS ============== Report bugs to diff --git a/Documentation/tools/rtla/common_timerlat_options.rst b/Documentation/tools/rtla/common_timerlat_options.rst index 10dc802f8d65..7854368f1827 100644 --- a/Documentation/tools/rtla/common_timerlat_options.rst +++ b/Documentation/tools/rtla/common_timerlat_options.rst @@ -55,3 +55,67 @@ Set timerlat to run without workload, waiting for the user to dispatch a per-cpu task that waits for a new period on the tracing/osnoise/per_cpu/cpu$ID/timerlat_fd. See linux/tools/rtla/sample/timerlat_load.py for an example of user-load code. + +**--on-threshold** *action* + + Defines an action to be executed when tracing is stopped on a latency threshold + specified by **-i/--irq** or **-T/--thread**. + + Multiple --on-threshold actions may be specified, and they will be executed in + the order they are provided. If any action fails, subsequent actions in the list + will not be executed. + + Supported actions are: + + - *trace[,file=]* + + Saves trace output, optionally taking a filename. Alternative to -t/--trace. + Note that nlike -t/--trace, specifying this multiple times will result in + the trace being saved multiple times. + + - *signal,num=,pid=* + + Sends signal to process. "parent" might be specified in place of pid to target + the parent process of rtla. + + - *shell,command=* + + Execute shell command. + + - *continue* + + Continue tracing after actions are executed instead of stopping. + + Example: + + $ rtla timerlat -T 20 --on-threshold trace + --on-threshold shell,command="grep ipi_send timerlat_trace.txt" + --on-threshold signal,num=2,pid=parent + + This will save a trace with the default filename "timerlat_trace.txt", print its + lines that contain the text "ipi_send" on standard output, and send signal 2 + (SIGINT) to the parent process. + + Performance Considerations: + + For time-sensitive actions, it is recommended to run **rtla timerlat** with BPF + support and RT priority. Note that due to implementational limitations, actions + might be delayed up to one second after tracing is stopped if BPF mode is not + available or disabled. + +**--on-end** *action* + + Defines an action to be executed at the end of **rtla timerlat** tracing. + + Multiple --on-end actions can be specified, and they will be executed in the order + they are provided. If any action fails, subsequent actions in the list will not be + executed. + + See the documentation for **--on-threshold** for the list of supported actions, with + the exception that *continue* has no effect. + + Example: + + $ rtla timerlat -d 5s --on-end trace + + This runs rtla timerlat with default options and save trace output at the end. diff --git a/Documentation/tools/rtla/rtla-timerlat-hist.rst b/Documentation/tools/rtla/rtla-timerlat-hist.rst index 03b7f3deb069..b2d8726271b3 100644 --- a/Documentation/tools/rtla/rtla-timerlat-hist.rst +++ b/Documentation/tools/rtla/rtla-timerlat-hist.rst @@ -107,3 +107,5 @@ SEE ALSO AUTHOR ====== Written by Daniel Bristot de Oliveira + +.. include:: common_appendix.rst diff --git a/Documentation/trace/boottime-trace.rst b/Documentation/trace/boottime-trace.rst index d594597201fd..3efac10adb36 100644 --- a/Documentation/trace/boottime-trace.rst +++ b/Documentation/trace/boottime-trace.rst @@ -198,8 +198,8 @@ Most of the subsystems and architecture dependent drivers will be initialized after that (arch_initcall or subsys_initcall). Thus, you can trace those with boot-time tracing. If you want to trace events before core_initcall, you can use the options -starting with ``kernel``. Some of them will be enabled eariler than the initcall -processing (for example,. ``kernel.ftrace=function`` and ``kernel.trace_event`` +starting with ``kernel``. Some of them will be enabled earlier than the initcall +processing (for example, ``kernel.ftrace=function`` and ``kernel.trace_event`` will start before the initcall.) diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst new file mode 100644 index 000000000000..89b5157cfab8 --- /dev/null +++ b/Documentation/trace/eprobetrace.rst @@ -0,0 +1,269 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================== +Eprobe - Event-based Probe Tracing +================================== + +:Author: Steven Rostedt + +- Written for v6.17 + +Overview +======== + +Eprobes are dynamic events that are placed on existing events to either +dereference a field that is a pointer, or simply to limit what fields are +recorded in the trace event. + +Eprobes depend on kprobe events so to enable this feature, build your kernel +with CONFIG_EPROBE_EVENTS=y. + +Eprobes are created via the /sys/kernel/tracing/dynamic_events file. + +Synopsis of eprobe_events +------------------------- +:: + + e[:[EGRP/][EEVENT]] GRP.EVENT [FETCHARGS] : Set a probe + -:[EGRP/][EEVENT] : Clear a probe + + EGRP : Group name of the new event. If omitted, use "eprobes" for it. + EEVENT : Event name. If omitted, the event name is generated and will + be the same event name as the event it attached to. + GRP : Group name of the event to attach to. + EVENT : Event name of the event to attach to. + + FETCHARGS : Arguments. Each probe can have up to 128 args. + $FIELD : Fetch the value of the event field called FIELD. + @ADDR : Fetch memory at ADDR (ADDR should be in kernel) + @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) + $comm : Fetch current task comm. + +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4) + \IMM : Store an immediate value to the argument. + NAME=FETCHARG : Set NAME as the argument name of FETCHARG. + FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types + (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types + (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char", + "string", "ustring", "symbol", "symstr" and "bitfield" are + supported. + +Types +----- +The FETCHARGS above is very similar to the kprobe events as described in +Documentation/trace/kprobetrace.rst. + +The difference between eprobes and kprobes FETCHARGS is that eprobes has a +$FIELD command that returns the content of the event field of the event +that is attached. Eprobes do not have access to registers, stacks and function +arguments that kprobes has. + +If a field argument is a pointer, it may be dereferenced just like a memory +address using the FETCHARGS syntax. + + +Attaching to dynamic events +--------------------------- + +Eprobes may attach to dynamic events as well as to normal events. It may +attach to a kprobe event, a synthetic event or a fprobe event. This is useful +if the type of a field needs to be changed. See Example 2 below. + +Usage examples +============== + +Example 1 +--------- + +The basic usage of eprobes is to limit the data that is being recorded into +the tracing buffer. For example, a common event to trace is the sched_switch +trace event. That has a format of:: + + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:char prev_comm[16]; offset:8; size:16; signed:0; + field:pid_t prev_pid; offset:24; size:4; signed:1; + field:int prev_prio; offset:28; size:4; signed:1; + field:long prev_state; offset:32; size:8; signed:1; + field:char next_comm[16]; offset:40; size:16; signed:0; + field:pid_t next_pid; offset:56; size:4; signed:1; + field:int next_prio; offset:60; size:4; signed:1; + +The first four fields are common to all events and can not be limited. But the +rest of the event has 60 bytes of information. It records the names of the +previous and next tasks being scheduled out and in, as well as their pids and +priorities. It also records the state of the previous task. If only the pids +of the tasks are of interest, why waste the ring buffer with all the other +fields? + +An eprobe can limit what gets recorded. Note, it does not help in performance, +as all the fields are recorded in a temporary buffer to process the eprobe. +:: + + # echo 'e:sched/switch sched.sched_switch prev=$prev_pid:u32 next=$next_pid:u32' >> /sys/kernel/tracing/dynamic_events + # echo 1 > /sys/kernel/tracing/events/sched/switch/enable + # cat /sys/kernel/tracing/trace + + # tracer: nop + # + # entries-in-buffer/entries-written: 2721/2721 #P:8 + # + # _-----=> irqs-off/BH-disabled + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / _-=> migrate-disable + # |||| / delay + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + sshd-session-1082 [004] d..4. 5041.239906: switch: (sched.sched_switch) prev=1082 next=0 + bash-1085 [001] d..4. 5041.240198: switch: (sched.sched_switch) prev=1085 next=141 + kworker/u34:5-141 [001] d..4. 5041.240259: switch: (sched.sched_switch) prev=141 next=1085 + -0 [004] d..4. 5041.240354: switch: (sched.sched_switch) prev=0 next=1082 + bash-1085 [001] d..4. 5041.240385: switch: (sched.sched_switch) prev=1085 next=141 + kworker/u34:5-141 [001] d..4. 5041.240410: switch: (sched.sched_switch) prev=141 next=1085 + bash-1085 [001] d..4. 5041.240478: switch: (sched.sched_switch) prev=1085 next=0 + sshd-session-1082 [004] d..4. 5041.240526: switch: (sched.sched_switch) prev=1082 next=0 + -0 [001] d..4. 5041.247524: switch: (sched.sched_switch) prev=0 next=90 + -0 [002] d..4. 5041.247545: switch: (sched.sched_switch) prev=0 next=16 + kworker/1:1-90 [001] d..4. 5041.247580: switch: (sched.sched_switch) prev=90 next=0 + rcu_sched-16 [002] d..4. 5041.247591: switch: (sched.sched_switch) prev=16 next=0 + -0 [002] d..4. 5041.257536: switch: (sched.sched_switch) prev=0 next=16 + rcu_sched-16 [002] d..4. 5041.257573: switch: (sched.sched_switch) prev=16 next=0 + +Note, without adding the "u32" after the prev_pid and next_pid, the values +would default showing in hexadecimal. + +Example 2 +--------- + +If a specific system call is to be recorded but the syscalls events are not +enabled, the raw_syscalls can still be used (syscalls are system call +events are not normal events, but are created from the raw_syscalls events +within the kernel). In order to trace the openat system call, one can create +an event probe on top of the raw_syscalls event: +:: + + # cd /sys/kernel/tracing + # cat events/raw_syscalls/sys_enter/format + name: sys_enter + ID: 395 + format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:long id; offset:8; size:8; signed:1; + field:unsigned long args[6]; offset:16; size:48; signed:0; + + print fmt: "NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)", REC->id, REC->args[0], REC->args[1], REC->args[2], REC->args[3], REC->args[4], REC->args[5] + +From the source code, the sys_openat() has: +:: + + int sys_openat(int dirfd, const char *path, int flags, mode_t mode) + { + return my_syscall4(__NR_openat, dirfd, path, flags, mode); + } + +The path is the second parameter, and that is what is wanted. +:: + + # echo 'e:openat raw_syscalls.sys_enter nr=$id filename=+8($args):ustring' >> dynamic_events + +This is being run on x86_64 where the word size is 8 bytes and the openat +system call __NR_openat is set at 257. +:: + + # echo 'nr == 257' > events/eprobes/openat/filter + +Now enable the event and look at the trace. +:: + + # echo 1 > events/eprobes/openat/enable + # cat trace + + # tracer: nop + # + # entries-in-buffer/entries-written: 4/4 #P:8 + # + # _-----=> irqs-off/BH-disabled + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / _-=> migrate-disable + # |||| / delay + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + cat-1298 [003] ...2. 2060.875970: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault) + cat-1298 [003] ...2. 2060.876197: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault) + cat-1298 [003] ...2. 2060.879126: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault) + cat-1298 [003] ...2. 2060.879639: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault) + +The filename shows "(fault)". This is likely because the filename has not been +pulled into memory yet and currently trace events cannot fault in memory that +is not present. When an eprobe tries to read memory that has not been faulted +in yet, it will show the "(fault)" text. + +To get around this, as the kernel will likely pull in this filename and make +it present, attaching it to a synthetic event that can pass the address of the +filename from the entry of the event to the end of the event, this can be used +to show the filename when the system call returns. + +Remove the old eprobe:: + + # echo 1 > events/eprobes/openat/enable + # echo '-:openat' >> dynamic_events + +This time make an eprobe where the address of the filename is saved:: + + # echo 'e:openat_start raw_syscalls.sys_enter nr=$id filename=+8($args):x64' >> dynamic_events + +Create a synthetic event that passes the address of the filename to the +end of the event:: + + # echo 's:filename u64 file' >> dynamic_events + # echo 'hist:keys=common_pid:f=filename if nr == 257' > events/eprobes/openat_start/trigger + # echo 'hist:keys=common_pid:file=$f:onmatch(eprobes.openat_start).trace(filename,$file) if id == 257' > events/raw_syscalls/sys_exit/trigger + +Now that the address of the filename has been passed to the end of the +system call, create another eprobe to attach to the exit event to show the +string:: + + # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events + # echo 1 > events/eprobes/openat/enable + # cat trace + + # tracer: nop + # + # entries-in-buffer/entries-written: 4/4 #P:8 + # + # _-----=> irqs-off/BH-disabled + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / _-=> migrate-disable + # |||| / delay + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + cat-1331 [001] ...5. 2944.787977: openat: (synthetic.filename) filename="/etc/ld.so.cache" + cat-1331 [001] ...5. 2944.788480: openat: (synthetic.filename) filename="/lib/x86_64-linux-gnu/libc.so.6" + cat-1331 [001] ...5. 2944.793426: openat: (synthetic.filename) filename="/usr/lib/locale/locale-archive" + cat-1331 [001] ...5. 2944.831362: openat: (synthetic.filename) filename="trace" + +Example 3 +--------- + +If syscall trace events are available, the above would not need the first +eprobe, but it would still need the last one:: + + # echo 's:filename u64 file' >> dynamic_events + # echo 'hist:keys=common_pid:f=filename' > events/syscalls/sys_enter_openat/trigger + # echo 'hist:keys=common_pid:file=$f:onmatch(syscalls.sys_enter_openat).trace(filename,$file)' > events/syscalls/sys_exit_openat/trigger + # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events + # echo 1 > events/eprobes/openat/enable + +And this would produce the same result as Example 2. diff --git a/Documentation/trace/ftrace-design.rst b/Documentation/trace/ftrace-design.rst index dc82d64b3a44..8f4fab3f9324 100644 --- a/Documentation/trace/ftrace-design.rst +++ b/Documentation/trace/ftrace-design.rst @@ -238,19 +238,15 @@ You need very few things to get the syscalls tracing in an arch. - Tag this arch as HAVE_SYSCALL_TRACEPOINTS. -HAVE_FTRACE_MCOUNT_RECORD -------------------------- +HAVE_DYNAMIC_FTRACE +------------------- See scripts/recordmcount.pl for more info. Just fill in the arch-specific details for how to locate the addresses of mcount call sites via objdump. This option doesn't make much sense without also implementing dynamic ftrace. - -HAVE_DYNAMIC_FTRACE -------------------- - -You will first need HAVE_FTRACE_MCOUNT_RECORD and HAVE_FUNCTION_TRACER, so -scroll your reader back up if you got over eager. +You will first need HAVE_FUNCTION_TRACER, so scroll your reader back up if you +got over eager. Once those are out of the way, you will need to implement: - asm/ftrace.h: diff --git a/Documentation/trace/histogram.rst b/Documentation/trace/histogram.rst index 0aada18c38c6..2b98c1720a54 100644 --- a/Documentation/trace/histogram.rst +++ b/Documentation/trace/histogram.rst @@ -249,7 +249,7 @@ Extended error information table, it should keep a running total of the number of bytes requested by that call_site. - We'll let it run for awhile and then dump the contents of the 'hist' + We'll let it run for a while and then dump the contents of the 'hist' file in the kmalloc event's subdirectory (for readability, a number of entries have been omitted):: diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index cc1dc5a087e8..b4a429dc4f7a 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -36,6 +36,7 @@ the Linux kernel. kprobes kprobetrace fprobetrace + eprobetrace fprobe ring-buffer-design diff --git a/Documentation/trace/rv/da_monitor_synthesis.rst b/Documentation/trace/rv/da_monitor_synthesis.rst deleted file mode 100644 index 0a92729c8a9b..000000000000 --- a/Documentation/trace/rv/da_monitor_synthesis.rst +++ /dev/null @@ -1,147 +0,0 @@ -Deterministic Automata Monitor Synthesis -======================================== - -The starting point for the application of runtime verification (RV) techniques -is the *specification* or *modeling* of the desired (or undesired) behavior -of the system under scrutiny. - -The formal representation needs to be then *synthesized* into a *monitor* -that can then be used in the analysis of the trace of the system. The -*monitor* connects to the system via an *instrumentation* that converts -the events from the *system* to the events of the *specification*. - - -In Linux terms, the runtime verification monitors are encapsulated inside -the *RV monitor* abstraction. The RV monitor includes a set of instances -of the monitor (per-cpu monitor, per-task monitor, and so on), the helper -functions that glue the monitor to the system reference model, and the -trace output as a reaction to event parsing and exceptions, as depicted -below:: - - Linux +----- RV Monitor ----------------------------------+ Formal - Realm | | Realm - +-------------------+ +----------------+ +-----------------+ - | Linux kernel | | Monitor | | Reference | - | Tracing | -> | Instance(s) | <- | Model | - | (instrumentation) | | (verification) | | (specification) | - +-------------------+ +----------------+ +-----------------+ - | | | - | V | - | +----------+ | - | | Reaction | | - | +--+--+--+-+ | - | | | | | - | | | +-> trace output ? | - +------------------------|--|----------------------+ - | +----> panic ? - +-------> - -DA monitor synthesis --------------------- - -The synthesis of automata-based models into the Linux *RV monitor* abstraction -is automated by the dot2k tool and the rv/da_monitor.h header file that -contains a set of macros that automatically generate the monitor's code. - -dot2k ------ - -The dot2k utility leverages dot2c by converting an automaton model in -the DOT format into the C representation [1] and creating the skeleton of -a kernel monitor in C. - -For example, it is possible to transform the wip.dot model present in -[1] into a per-cpu monitor with the following command:: - - $ dot2k -d wip.dot -t per_cpu - -This will create a directory named wip/ with the following files: - -- wip.h: the wip model in C -- wip.c: the RV monitor - -The wip.c file contains the monitor declaration and the starting point for -the system instrumentation. - -Monitor macros --------------- - -The rv/da_monitor.h enables automatic code generation for the *Monitor -Instance(s)* using C macros. - -The benefits of the usage of macro for monitor synthesis are 3-fold as it: - -- Reduces the code duplication; -- Facilitates the bug fix/improvement; -- Avoids the case of developers changing the core of the monitor code - to manipulate the model in a (let's say) non-standard way. - -This initial implementation presents three different types of monitor instances: - -- ``#define DECLARE_DA_MON_GLOBAL(name, type)`` -- ``#define DECLARE_DA_MON_PER_CPU(name, type)`` -- ``#define DECLARE_DA_MON_PER_TASK(name, type)`` - -The first declares the functions for a global deterministic automata monitor, -the second for monitors with per-cpu instances, and the third with per-task -instances. - -In all cases, the 'name' argument is a string that identifies the monitor, and -the 'type' argument is the data type used by dot2k on the representation of -the model in C. - -For example, the wip model with two states and three events can be -stored in an 'unsigned char' type. Considering that the preemption control -is a per-cpu behavior, the monitor declaration in the 'wip.c' file is:: - - DECLARE_DA_MON_PER_CPU(wip, unsigned char); - -The monitor is executed by sending events to be processed via the functions -presented below:: - - da_handle_event_$(MONITOR_NAME)($(event from event enum)); - da_handle_start_event_$(MONITOR_NAME)($(event from event enum)); - da_handle_start_run_event_$(MONITOR_NAME)($(event from event enum)); - -The function ``da_handle_event_$(MONITOR_NAME)()`` is the regular case where -the event will be processed if the monitor is processing events. - -When a monitor is enabled, it is placed in the initial state of the automata. -However, the monitor does not know if the system is in the *initial state*. - -The ``da_handle_start_event_$(MONITOR_NAME)()`` function is used to notify the -monitor that the system is returning to the initial state, so the monitor can -start monitoring the next event. - -The ``da_handle_start_run_event_$(MONITOR_NAME)()`` function is used to notify -the monitor that the system is known to be in the initial state, so the -monitor can start monitoring and monitor the current event. - -Using the wip model as example, the events "preempt_disable" and -"sched_waking" should be sent to monitor, respectively, via [2]:: - - da_handle_event_wip(preempt_disable_wip); - da_handle_event_wip(sched_waking_wip); - -While the event "preempt_enabled" will use:: - - da_handle_start_event_wip(preempt_enable_wip); - -To notify the monitor that the system will be returning to the initial state, -so the system and the monitor should be in sync. - -Final remarks -------------- - -With the monitor synthesis in place using the rv/da_monitor.h and -dot2k, the developer's work should be limited to the instrumentation -of the system, increasing the confidence in the overall approach. - -[1] For details about deterministic automata format and the translation -from one representation to another, see:: - - Documentation/trace/rv/deterministic_automata.rst - -[2] dot2k appends the monitor's name suffix to the events enums to -avoid conflicting variables when exporting the global vmlinux.h -use by BPF programs. diff --git a/Documentation/trace/rv/index.rst b/Documentation/trace/rv/index.rst index e80e0057feb4..a2812ac5cfeb 100644 --- a/Documentation/trace/rv/index.rst +++ b/Documentation/trace/rv/index.rst @@ -8,8 +8,10 @@ Runtime Verification runtime-verification.rst deterministic_automata.rst - da_monitor_synthesis.rst + linear_temporal_logic.rst + monitor_synthesis.rst da_monitor_instrumentation.rst monitor_wip.rst monitor_wwnr.rst monitor_sched.rst + monitor_rtapp.rst diff --git a/Documentation/trace/rv/linear_temporal_logic.rst b/Documentation/trace/rv/linear_temporal_logic.rst new file mode 100644 index 000000000000..9eee09d9cacf --- /dev/null +++ b/Documentation/trace/rv/linear_temporal_logic.rst @@ -0,0 +1,134 @@ +Linear temporal logic +===================== + +Introduction +------------ + +Runtime verification monitor is a verification technique which checks that the +kernel follows a specification. It does so by using tracepoints to monitor the +kernel's execution trace, and verifying that the execution trace sastifies the +specification. + +Initially, the specification can only be written in the form of deterministic +automaton (DA). However, while attempting to implement DA monitors for some +complex specifications, deterministic automaton is found to be inappropriate as +the specification language. The automaton is complicated, hard to understand, +and error-prone. + +Thus, RV monitors based on linear temporal logic (LTL) are introduced. This type +of monitor uses LTL as specification instead of DA. For some cases, writing the +specification as LTL is more concise and intuitive. + +Many materials explain LTL in details. One book is:: + + Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT + Press, 2008. + +Grammar +------- + +Unlike some existing syntax, kernel's implementation of LTL is more verbose. +This is motivated by considering that the people who read the LTL specifications +may not be well-versed in LTL. + +Grammar: + ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl + +Operands (opd): + true, false, user-defined names consisting of upper-case characters, digits, + and underscore. + +Unary Operators (unop): + always + eventually + next + not + +Binary Operators (binop): + until + and + or + imply + equivalent + +This grammar is ambiguous: operator precedence is not defined. Parentheses must +be used. + +Example linear temporal logic +----------------------------- +.. code-block:: + + RAIN imply (GO_OUTSIDE imply HAVE_UMBRELLA) + +means: if it is raining, going outside means having an umbrella. + +.. code-block:: + + RAIN imply (WET until not RAIN) + +means: if it is raining, it is going to be wet until the rain stops. + +.. code-block:: + + RAIN imply eventually not RAIN + +means: if it is raining, rain will eventually stop. + +The above examples are referring to the current time instance only. For kernel +verification, the `always` operator is usually desirable, to specify that +something is always true at the present and for all future. For example:: + + always (RAIN imply eventually not RAIN) + +means: *all* rain eventually stops. + +In the above examples, `RAIN`, `GO_OUTSIDE`, `HAVE_UMBRELLA` and `WET` are the +"atomic propositions". + +Monitor synthesis +----------------- + +To synthesize an LTL into a kernel monitor, the `rvgen` tool can be used: +`tools/verification/rvgen`. The specification needs to be provided as a file, +and it must have a "RULE = LTL" assignment. For example:: + + RULE = always (ACQUIRE imply ((not KILLED and not CRASHED) until RELEASE)) + +which says: if `ACQUIRE`, then `RELEASE` must happen before `KILLED` or +`CRASHED`. + +The LTL can be broken down using sub-expressions. The above is equivalent to: + + .. code-block:: + + RULE = always (ACQUIRE imply (ALIVE until RELEASE)) + ALIVE = not KILLED and not CRASHED + +From this specification, `rvgen` generates the C implementation of a Buchi +automaton - a non-deterministic state machine which checks the satisfiability of +the LTL. See Documentation/trace/rv/monitor_synthesis.rst for details on using +`rvgen`. + +References +---------- + +One book covering model checking and linear temporal logic is:: + + Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT + Press, 2008. + +For an example of using linear temporal logic in software testing, see:: + + Ruijie Meng, Zhen Dong, Jialin Li, Ivan Beschastnikh, and Abhik Roychoudhury. + 2022. Linear-time temporal logic guided greybox fuzzing. In Proceedings of the + 44th International Conference on Software Engineering (ICSE '22). Association + for Computing Machinery, New York, NY, USA, 1343–1355. + https://doi.org/10.1145/3510003.3510082 + +The kernel's LTL monitor implementation is based on:: + + Gerth, R., Peled, D., Vardi, M.Y., Wolper, P. (1996). Simple On-the-fly + Automatic Verification of Linear Temporal Logic. In: Dembiński, P., Średniawa, + M. (eds) Protocol Specification, Testing and Verification XV. PSTV 1995. IFIP + Advances in Information and Communication Technology. Springer, Boston, MA. + https://doi.org/10.1007/978-0-387-34892-6_1 diff --git a/Documentation/trace/rv/monitor_rtapp.rst b/Documentation/trace/rv/monitor_rtapp.rst new file mode 100644 index 000000000000..c8104eda924a --- /dev/null +++ b/Documentation/trace/rv/monitor_rtapp.rst @@ -0,0 +1,133 @@ +Real-time application monitors +============================== + +- Name: rtapp +- Type: container for multiple monitors +- Author: Nam Cao + +Description +----------- + +Real-time applications may have design flaws such that they experience +unexpected latency and fail to meet their time requirements. Often, these flaws +follow a few patterns: + + - Page faults: A real-time thread may access memory that does not have a + mapped physical backing or must first be copied (such as for copy-on-write). + Thus a page fault is raised and the kernel must first perform the expensive + action. This causes significant delays to the real-time thread + - Priority inversion: A real-time thread blocks waiting for a lower-priority + thread. This causes the real-time thread to effectively take on the + scheduling priority of the lower-priority thread. For example, the real-time + thread needs to access a shared resource that is protected by a + non-pi-mutex, but the mutex is currently owned by a non-real-time thread. + +The `rtapp` monitor detects these patterns. It aids developers to identify +reasons for unexpected latency with real-time applications. It is a container of +multiple sub-monitors described in the following sections. + +Monitor pagefault ++++++++++++++++++ + +The `pagefault` monitor reports real-time tasks raising page faults. Its +specification is:: + + RULE = always (RT imply not PAGEFAULT) + +To fix warnings reported by this monitor, `mlockall()` or `mlock()` can be used +to ensure physical backing for memory. + +This monitor may have false negatives because the pages used by the real-time +threads may just happen to be directly available during testing. To minimize +this, the system can be put under memory pressure (e.g. invoking the OOM killer +using a program that does `ptr = malloc(SIZE_OF_RAM); memset(ptr, 0, +SIZE_OF_RAM);`) so that the kernel executes aggressive strategies to recycle as +much physical memory as possible. + +Monitor sleep ++++++++++++++ + +The `sleep` monitor reports real-time threads sleeping in a manner that may +cause undesirable latency. Real-time applications should only put a real-time +thread to sleep for one of the following reasons: + + - Cyclic work: real-time thread sleeps waiting for the next cycle. For this + case, only the `clock_nanosleep` syscall should be used with `TIMER_ABSTIME` + (to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being + changed). No other method is safe for real-time. For example, threads + waiting for timerfd can be woken by softirq which provides no real-time + guarantee. + - Real-time thread waiting for something to happen (e.g. another thread + releasing shared resources, or a completion signal from another thread). In + this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of + FUTEX_WAIT_*) should be used. Applications usually do not use futexes + directly, but use PI mutexes and PI condition variables which are built on + top of futexes. Be aware that the C library might not implement conditional + variables as safe for real-time. As an alternative, the librtpi library + exists to provide a conditional variable implementation that is correct for + real-time applications in Linux. + +Beside the reason for sleeping, the eventual waker should also be +real-time-safe. Namely, one of: + + - An equal-or-higher-priority thread + - Hard interrupt handler + - Non-maskable interrupt handler + +This monitor's warning usually means one of the following: + + - Real-time thread is blocked by a non-real-time thread (e.g. due to + contention on a mutex without priority inheritance). This is priority + inversion. + - Time-critical work waits for something which is not safe for real-time (e.g. + timerfd). + - The work executed by the real-time thread does not need to run at real-time + priority at all. This is not a problem for the real-time thread itself, but + it is potentially taking the CPU away from other important real-time work. + +Application developers may purposely choose to have their real-time application +sleep in a way that is not safe for real-time. It is debatable whether that is a +problem. Application developers must analyze the warnings to make a proper +assessment. + +The monitor's specification is:: + + RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST)) + + RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD) + and ((not WAKE) until RT_FRIENDLY_WAKE) + + RT_VALID_SLEEP_REASON = FUTEX_WAIT + or RT_FRIENDLY_NANOSLEEP + + RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP + and NANOSLEEP_TIMER_ABSTIME + and NANOSLEEP_CLOCK_MONOTONIC + + RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO + or WOKEN_BY_HARDIRQ + or WOKEN_BY_NMI + or KTHREAD_SHOULD_STOP + + ALLOWLIST = BLOCK_ON_RT_MUTEX + or FUTEX_LOCK_PI + or TASK_IS_RCU + or TASK_IS_MIGRATION + +Beside the scenarios described above, this specification also handle some +special cases: + + - `KERNEL_THREAD`: kernel tasks do not have any pattern that can be recognized + as valid real-time sleeping reasons. Therefore sleeping reason is not + checked for kernel tasks. + - `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel + thread by waking it and waiting for it to exit (`kthread_stop()`). This + wakeup is safe for real-time. + - `ALLOWLIST`: to handle known false positives with the kernel. + - `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its implementation. + In the release path of rt_mutex, a boosted task is de-boosted before waking + the rt_mutex's waiter. Consequently, the monitor may see a real-time-unsafe + wakeup (e.g. non-real-time task waking real-time task). This is actually + real-time-safe because preemption is disabled for the duration. + - `FUTEX_LOCK_PI` is included in the allowlist for the same reason as + `BLOCK_ON_RT_MUTEX`. diff --git a/Documentation/trace/rv/monitor_sched.rst b/Documentation/trace/rv/monitor_sched.rst index 24b2c62a3bc2..3f8381ad9ec7 100644 --- a/Documentation/trace/rv/monitor_sched.rst +++ b/Documentation/trace/rv/monitor_sched.rst @@ -40,26 +40,6 @@ defined in by Daniel Bristot in [1]. Currently we included the following: -Monitor tss -~~~~~~~~~~~ - -The task switch while scheduling (tss) monitor ensures a task switch happens -only in scheduling context, that is inside a call to `__schedule`:: - - | - | - v - +-----------------+ - | thread | <+ - +-----------------+ | - | | - | schedule_entry | schedule_exit - v | - sched_switch | - +--------------- | - | sched | - +--------------> -+ - Monitor sco ~~~~~~~~~~~ @@ -144,26 +124,277 @@ does not enable preemption:: | scheduling_contex -+ -Monitor sncid -~~~~~~~~~~~~~ +Monitor sts +~~~~~~~~~~~ -The schedule not called with interrupt disabled (sncid) monitor ensures -schedule is not called with interrupt disabled:: +The schedule implies task switch (sts) monitor ensures a task switch happens +only in scheduling context and up to once, as well as scheduling occurs with +interrupts enabled but no task switch can happen before interrupts are +disabled. When the next task picked for execution is the same as the previously +running one, no real task switch occurs but interrupts are disabled nonetheless:: - | - | - v - schedule_entry +--------------+ - schedule_exit | | - +----------------- | can_sched | - | | | - +----------------> | | <+ - +--------------+ | - | | - | irq_disable | irq_enable - v | - | - cant_sched -+ + irq_entry | + +----+ | + v | v + +------------+ irq_enable #===================# irq_disable + | | ------------> H H irq_entry + | cant_sched | <------------ H H irq_enable + | | irq_disable H can_sched H --------------+ + +------------+ H H | + H H | + +---------------> H H <-------------+ + | #===================# + | | + schedule_exit | schedule_entry + | v + | +-------------------+ irq_enable + | | scheduling | <---------------+ + | +-------------------+ | + | | | + | | irq_disable +--------+ irq_entry + | v | | --------+ + | +-------------------+ irq_entry | in_irq | | + | | | -----------> | | <-------+ + | | disable_to_switch | +--------+ + | | | --+ + | +-------------------+ | + | | | + | | sched_switch | + | v | + | +-------------------+ | + | | switching | | irq_enable + | +-------------------+ | + | | | + | | irq_enable | + | v | + | +-------------------+ | + +-- | enable_to_exit | <-+ + +-------------------+ + ^ | irq_disable + | | irq_entry + +---------------+ irq_enable + +Monitor nrp +----------- + +The need resched preempts (nrp) monitor ensures preemption requires +``need_resched``. Only kernel preemption is considered, since preemption +while returning to userspace, for this monitor, is indistinguishable from +``sched_switch_yield`` (described in the sssw monitor). +A kernel preemption is whenever ``__schedule`` is called with the preemption +flag set to true (e.g. from preempt_enable or exiting from interrupts). This +type of preemption occurs after the need for ``rescheduling`` has been set. +This is not valid for the *lazy* variant of the flag, which causes only +userspace preemption. +A ``schedule_entry_preempt`` may involve a task switch or not, in the latter +case, a task goes through the scheduler from a preemption context but it is +picked as the next task to run. Since the scheduler runs, this clears the need +to reschedule. The ``any_thread_running`` state does not imply the monitored +task is not running as this monitor does not track the outcome of scheduling. + +In theory, a preemption can only occur after the ``need_resched`` flag is set. In +practice, however, it is possible to see a preemption where the flag is not +set. This can happen in one specific condition:: + + need_resched + preempt_schedule() + preempt_schedule_irq() + __schedule() + !need_resched + __schedule() + +In the situation above, standard preemption starts (e.g. from preempt_enable +when the flag is set), an interrupt occurs before scheduling and, on its exit +path, it schedules, which clears the ``need_resched`` flag. +When the preempted task runs again, the standard preemption started earlier +resumes, although the flag is no longer set. The monitor considers this a +``nested_preemption``, this allows another preemption without re-setting the +flag. This condition relaxes the monitor constraints and may catch false +negatives (i.e. no real ``nested_preemptions``) but makes the monitor more +robust and able to validate other scenarios. +For simplicity, the monitor starts in ``preempt_irq``, although no interrupt +occurred, as the situation above is hard to pinpoint:: + + schedule_entry + irq_entry #===========================================# + +-------------------------- H H + | H H + +-------------------------> H any_thread_running H + H H + +-------------------------> H H + | #===========================================# + | schedule_entry | ^ + | schedule_entry_preempt | sched_need_resched | schedule_entry + | | schedule_entry_preempt + | v | + | +----------------------+ | + | +--- | | | + | sched_need_resched | | rescheduling | -+ + | +--> | | + | +----------------------+ + | | irq_entry + | v + | +----------------------+ + | | | ---+ + | ---> | | | sched_need_resched + | | preempt_irq | | irq_entry + | | | <--+ + | | | <--+ + | +----------------------+ | + | | schedule_entry | sched_need_resched + | | schedule_entry_preempt | + | v | + | +-----------------------+ | + +-------------------------- | nested_preempt | --+ + +-----------------------+ + ^ irq_entry | + +-------------------+ + +Due to how the ``need_resched`` flag on the preemption count works on arm64, +this monitor is unstable on that architecture, as it often records preemption +when the flag is not set, even in presence of the workaround above. +For the time being, the monitor is disabled by default on arm64. + +Monitor sssw +------------ + +The set state sleep and wakeup (sssw) monitor ensures ``set_state`` to +sleepable leads to sleeping and sleeping tasks require wakeup. It includes the +following types of switch: + +* ``switch_suspend``: + a task puts itself to sleep, this can happen only after explicitly setting + the task to ``sleepable``. After a task is suspended, it needs to be woken up + (``waking`` state) before being switched in again. + Setting the task's state to ``sleepable`` can be reverted before switching if it + is woken up or set to ``runnable``. +* ``switch_blocking``: + a special case of a ``switch_suspend`` where the task is waiting on a + sleeping RT lock (``PREEMPT_RT`` only), it is common to see wakeup and set + state events racing with each other and this leads the model to perceive this + type of switch when the task is not set to sleepable. This is a limitation of + the model in SMP system and workarounds may slow down the system. +* ``switch_preempt``: + a task switch as a result of kernel preemption (``schedule_entry_preempt`` in + the nrp model). +* ``switch_yield``: + a task explicitly calls the scheduler or is preempted while returning to + userspace. It can happen after a ``yield`` system call, from the idle task or + if the ``need_resched`` flag is set. By definition, a task cannot yield while + ``sleepable`` as that would be a suspension. A special case of a yield occurs + when a task in ``TASK_INTERRUPTIBLE`` calls the scheduler while a signal is + pending. The task doesn't go through the usual blocking/waking and is set + back to runnable, the resulting switch (if there) looks like a yield to the + ``signal_wakeup`` state and is followed by the signal delivery. From this + state, the monitor expects a signal even if it sees a wakeup event, although + not necessary, to rule out false negatives. + +This monitor doesn't include a running state, ``sleepable`` and ``runnable`` +are only referring to the task's desired state, which could be scheduled out +(e.g. due to preemption). However, it does include the event +``sched_switch_in`` to represent when a task is allowed to become running. This +can be triggered also by preemption, but cannot occur after the task got to +``sleeping`` before a ``wakeup`` occurs:: + + +--------------------------------------------------------------------------+ + | | + | | + | switch_suspend | | + | switch_blocking | | + v v | + +----------+ #==========================# set_state_runnable | + | | H H wakeup | + | | H H switch_in | + | | H H switch_yield | + | sleeping | H H switch_preempt | + | | H H signal_deliver | + | | switch_ H H ------+ | + | | _blocking H runnable H | | + | | <----------- H H <-----+ | + +----------+ H H | + | wakeup H H | + +---------------------> H H | + H H | + +---------> H H | + | #==========================# | + | | ^ | + | | | set_state_runnable | + | | | wakeup | + | set_state_sleepable | +------------------------+ + | v | | + | +--------------------------+ set_state_sleepable + | | | switch_in + | | | switch_preempt + signal_deliver | sleepable | signal_deliver + | | | ------+ + | | | | + | | | <-----+ + | +--------------------------+ + | | ^ + | switch_yield | set_state_sleepable + | v | + | +---------------+ | + +---------- | signal_wakeup | -+ + +---------------+ + ^ | switch_in + | | switch_preempt + | | switch_yield + +-----------+ wakeup + +Monitor opid +------------ + +The operations with preemption and irq disabled (opid) monitor ensures +operations like ``wakeup`` and ``need_resched`` occur with interrupts and +preemption disabled or during interrupt context, in such case preemption may +not be disabled explicitly. +``need_resched`` can be set by some RCU internals functions, in which case it +doesn't match a task wakeup and might occur with only interrupts disabled:: + + | sched_need_resched + | sched_waking + | irq_entry + | +--------------------+ + v v | + +------------------------------------------------------+ + +----------- | disabled | <+ + | +------------------------------------------------------+ | + | | ^ | + | | preempt_disable sched_need_resched | + | preempt_enable | +--------------------+ | + | v | v | | + | +------------------------------------------------------+ | + | | irq_disabled | | + | +------------------------------------------------------+ | + | | | ^ | + | irq_entry irq_entry | | | + | sched_need_resched v | irq_disable | + | sched_waking +--------------+ | | | + | +----- | | irq_enable | | + | | | in_irq | | | | + | +----> | | | | | + | +--------------+ | | irq_disable + | | | | | + | irq_enable | irq_enable | | | + | v v | | + | #======================================================# | + | H enabled H | + | #======================================================# | + | | ^ ^ preempt_enable | | + | preempt_disable preempt_enable +--------------------+ | + | v | | + | +------------------+ | | + +----------> | preempt_disabled | -+ | + +------------------+ | + | | + +-------------------------------------------------------+ + +This monitor is designed to work on ``PREEMPT_RT`` kernels, the special case of +events occurring in interrupt context is a shortcut to identify valid scenarios +where the preemption tracepoints might not be visible, during interrupts +preemption is always disabled. On non- ``PREEMPT_RT`` kernels, the interrupts +might invoke a softirq to set ``need_resched`` and wake up a task. This is +another special case that is currently not supported by the monitor. References ---------- diff --git a/Documentation/trace/rv/monitor_synthesis.rst b/Documentation/trace/rv/monitor_synthesis.rst new file mode 100644 index 000000000000..ac808a7554f5 --- /dev/null +++ b/Documentation/trace/rv/monitor_synthesis.rst @@ -0,0 +1,271 @@ +Runtime Verification Monitor Synthesis +====================================== + +The starting point for the application of runtime verification (RV) techniques +is the *specification* or *modeling* of the desired (or undesired) behavior +of the system under scrutiny. + +The formal representation needs to be then *synthesized* into a *monitor* +that can then be used in the analysis of the trace of the system. The +*monitor* connects to the system via an *instrumentation* that converts +the events from the *system* to the events of the *specification*. + + +In Linux terms, the runtime verification monitors are encapsulated inside +the *RV monitor* abstraction. The RV monitor includes a set of instances +of the monitor (per-cpu monitor, per-task monitor, and so on), the helper +functions that glue the monitor to the system reference model, and the +trace output as a reaction to event parsing and exceptions, as depicted +below:: + + Linux +----- RV Monitor ----------------------------------+ Formal + Realm | | Realm + +-------------------+ +----------------+ +-----------------+ + | Linux kernel | | Monitor | | Reference | + | Tracing | -> | Instance(s) | <- | Model | + | (instrumentation) | | (verification) | | (specification) | + +-------------------+ +----------------+ +-----------------+ + | | | + | V | + | +----------+ | + | | Reaction | | + | +--+--+--+-+ | + | | | | | + | | | +-> trace output ? | + +------------------------|--|----------------------+ + | +----> panic ? + +-------> + +RV monitor synthesis +-------------------- + +The synthesis of a specification into the Linux *RV monitor* abstraction is +automated by the rvgen tool and the header file containing common code for +creating monitors. The header files are: + + * rv/da_monitor.h for deterministic automaton monitor. + * rv/ltl_monitor.h for linear temporal logic monitor. + +rvgen +----- + +The rvgen utility converts a specification into the C presentation and creating +the skeleton of a kernel monitor in C. + +For example, it is possible to transform the wip.dot model present in +[1] into a per-cpu monitor with the following command:: + + $ rvgen monitor -c da -s wip.dot -t per_cpu + +This will create a directory named wip/ with the following files: + +- wip.h: the wip model in C +- wip.c: the RV monitor + +The wip.c file contains the monitor declaration and the starting point for +the system instrumentation. + +Similarly, a linear temporal logic monitor can be generated with the following +command:: + + $ rvgen monitor -c ltl -s pagefault.ltl -t per_task + +This generates pagefault/ directory with: + +- pagefault.h: The Buchi automaton (the non-deterministic state machine to + verify the specification) +- pagefault.c: The skeleton for the RV monitor + +Monitor header files +-------------------- + +The header files: + +- `rv/da_monitor.h` for deterministic automaton monitor +- `rv/ltl_monitor` for linear temporal logic monitor + +include common macros and static functions for implementing *Monitor +Instance(s)*. + +The benefits of having all common functionalities in a single header file are +3-fold: + + - Reduce the code duplication; + - Facilitate the bug fix/improvement; + - Avoid the case of developers changing the core of the monitor code to + manipulate the model in a (let's say) non-standard way. + +rv/da_monitor.h ++++++++++++++++ + +This initial implementation presents three different types of monitor instances: + +- ``#define DECLARE_DA_MON_GLOBAL(name, type)`` +- ``#define DECLARE_DA_MON_PER_CPU(name, type)`` +- ``#define DECLARE_DA_MON_PER_TASK(name, type)`` + +The first declares the functions for a global deterministic automata monitor, +the second for monitors with per-cpu instances, and the third with per-task +instances. + +In all cases, the 'name' argument is a string that identifies the monitor, and +the 'type' argument is the data type used by rvgen on the representation of +the model in C. + +For example, the wip model with two states and three events can be +stored in an 'unsigned char' type. Considering that the preemption control +is a per-cpu behavior, the monitor declaration in the 'wip.c' file is:: + + DECLARE_DA_MON_PER_CPU(wip, unsigned char); + +The monitor is executed by sending events to be processed via the functions +presented below:: + + da_handle_event_$(MONITOR_NAME)($(event from event enum)); + da_handle_start_event_$(MONITOR_NAME)($(event from event enum)); + da_handle_start_run_event_$(MONITOR_NAME)($(event from event enum)); + +The function ``da_handle_event_$(MONITOR_NAME)()`` is the regular case where +the event will be processed if the monitor is processing events. + +When a monitor is enabled, it is placed in the initial state of the automata. +However, the monitor does not know if the system is in the *initial state*. + +The ``da_handle_start_event_$(MONITOR_NAME)()`` function is used to notify the +monitor that the system is returning to the initial state, so the monitor can +start monitoring the next event. + +The ``da_handle_start_run_event_$(MONITOR_NAME)()`` function is used to notify +the monitor that the system is known to be in the initial state, so the +monitor can start monitoring and monitor the current event. + +Using the wip model as example, the events "preempt_disable" and +"sched_waking" should be sent to monitor, respectively, via [2]:: + + da_handle_event_wip(preempt_disable_wip); + da_handle_event_wip(sched_waking_wip); + +While the event "preempt_enabled" will use:: + + da_handle_start_event_wip(preempt_enable_wip); + +To notify the monitor that the system will be returning to the initial state, +so the system and the monitor should be in sync. + +rv/ltl_monitor.h +++++++++++++++++ +This file must be combined with the $(MODEL_NAME).h file (generated by `rvgen`) +to be complete. For example, for the `pagefault` monitor, the `pagefault.c` +source file must include:: + + #include "pagefault.h" + #include + +(the skeleton monitor file generated by `rvgen` already does this). + +`$(MODEL_NAME).h` (`pagefault.h` in the above example) includes the +implementation of the Buchi automaton - a non-deterministic state machine that +verifies the LTL specification. While `rv/ltl_monitor.h` includes the common +helper functions to interact with the Buchi automaton and to implement an RV +monitor. An important definition in `$(MODEL_NAME).h` is:: + + enum ltl_atom { + LTL_$(FIRST_ATOMIC_PROPOSITION), + LTL_$(SECOND_ATOMIC_PROPOSITION), + ... + LTL_NUM_ATOM + }; + +which is the list of atomic propositions present in the LTL specification +(prefixed with "LTL\_" to avoid name collision). This `enum` is passed to the +functions interacting with the Buchi automaton. + +While generating code, `rvgen` cannot understand the meaning of the atomic +propositions. Thus, that task is left for manual work. The recommended pratice +is adding tracepoints to places where the atomic propositions change; and in the +tracepoints' handlers: the Buchi automaton is executed using:: + + void ltl_atom_update(struct task_struct *task, enum ltl_atom atom, bool value) + +which tells the Buchi automaton that the atomic proposition `atom` is now +`value`. The Buchi automaton checks whether the LTL specification is still +satisfied, and invokes the monitor's error tracepoint and the reactor if +violation is detected. + +Tracepoints and `ltl_atom_update()` should be used whenever possible. However, +it is sometimes not the most convenient. For some atomic propositions which are +changed in multiple places in the kernel, it is cumbersome to trace all those +places. Furthermore, it may not be important that the atomic propositions are +updated at precise times. For example, considering the following linear temporal +logic:: + + RULE = always (RT imply not PAGEFAULT) + +This LTL states that a real-time task does not raise page faults. For this +specification, it is not important when `RT` changes, as long as it has the +correct value when `PAGEFAULT` is true. Motivated by this case, another +function is introduced:: + + void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon) + +This function is called whenever the Buchi automaton is triggered. Therefore, it +can be manually implemented to "fetch" `RT`:: + + void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon) + { + ltl_atom_set(mon, LTL_RT, rt_task(task)); + } + +Effectively, whenever `PAGEFAULT` is updated with a call to `ltl_atom_update()`, +`RT` is also fetched. Thus, the LTL specification can be verified without +tracing `RT` everywhere. + +For atomic propositions which act like events, they usually need to be set (or +cleared) and then immediately cleared (or set). A convenient function is +provided:: + + void ltl_atom_pulse(struct task_struct *task, enum ltl_atom atom, bool value) + +which is equivalent to:: + + ltl_atom_update(task, atom, value); + ltl_atom_update(task, atom, !value); + +To initialize the atomic propositions, the following function must be +implemented:: + + ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) + +This function is called for all running tasks when the monitor is enabled. It is +also called for new tasks created after the enabling the monitor. It should +initialize as many atomic propositions as possible, for example:: + + void ltl_atom_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) + { + ltl_atom_set(mon, LTL_RT, rt_task(task)); + if (task_creation) + ltl_atom_set(mon, LTL_PAGEFAULT, false); + } + +Atomic propositions not initialized by `ltl_atom_init()` will stay in the +unknown state until relevant tracepoints are hit, which can take some time. As +monitoring for a task cannot be done until all atomic propositions is known for +the task, the monitor may need some time to start validating tasks which have +been running before the monitor is enabled. Therefore, it is recommended to +start the tasks of interest after enabling the monitor. + +Final remarks +------------- + +With the monitor synthesis in place using the header files and +rvgen, the developer's work should be limited to the instrumentation +of the system, increasing the confidence in the overall approach. + +[1] For details about deterministic automata format and the translation +from one representation to another, see:: + + Documentation/trace/rv/deterministic_automata.rst + +[2] rvgen appends the monitor's name suffix to the events enums to +avoid conflicting variables when exporting the global vmlinux.h +use by BPF programs. diff --git a/Documentation/translations/zh_CN/core-api/memory-hotplug.rst b/Documentation/translations/zh_CN/core-api/memory-hotplug.rst index 9b2841fb9a5f..c2a4122ae221 100644 --- a/Documentation/translations/zh_CN/core-api/memory-hotplug.rst +++ b/Documentation/translations/zh_CN/core-api/memory-hotplug.rst @@ -62,7 +62,6 @@ memory_notify结构体的指针:: struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; - int status_change_nid_normal; int status_change_nid; } @@ -70,8 +69,6 @@ memory_notify结构体的指针:: - nr_pages是在线/离线内存的页数。 -- status_change_nid_normal是当nodemask的N_NORMAL_MEMORY被设置/清除时设置节 - 点id,如果是-1,则nodemask状态不改变。 - status_change_nid是当nodemask的N_MEMORY被(将)设置/清除时设置的节点id。这 意味着一个新的(没上线的)节点通过联机获得新的内存,而一个节点失去了所有的内 diff --git a/Documentation/translations/zh_CN/how-to.rst b/Documentation/translations/zh_CN/how-to.rst index 569b0209385a..ddd99c0f9b4d 100644 --- a/Documentation/translations/zh_CN/how-to.rst +++ b/Documentation/translations/zh_CN/how-to.rst @@ -1,19 +1,19 @@ .. SPDX-License-Identifier: GPL-2.0 -========================= -Linux内核中文文档翻译规范 -========================= +========================== +Linux 内核中文文档翻译规范 +========================== 修订记录: - - v1.0 2025年3月28日,司延腾、慕冬亮共同编写了该规范。 + - v1.0 2025 年 3 月 28 日,司延腾、慕冬亮共同编写了该规范。 制定规范的背景 ============== 过去几年,在广大社区爱好者的友好合作下,Linux 内核中文文档迎来了蓬勃的发 展。在翻译的早期,一切都是混乱的,社区对译稿只有一个准确翻译的要求,以鼓 -励更多的开发者参与进来,这是从0到1的必然过程,所以早期的中文文档目录更加 -具有多样性,不过好在文档不多,维护上并没有过大的压力。 +励更多的开发者参与进来,这是从 0 到 1 的必然过程,所以早期的中文文档目录 +更加具有多样性,不过好在文档不多,维护上并没有过大的压力。 然而,世事变幻,不觉有年,现在内核中文文档在前进的道路上越走越远,很多潜 在的问题逐渐浮出水面,而且随着中文文档数量的增加,翻译更多的文档与提高中 @@ -34,7 +34,7 @@ reviewer 们只能耐心地指导他们如何与社区更好地合作,但是 ======== 工欲善其事必先利其器,如果您目前对内核文档翻译满怀热情,并且会独立地安装 -linux 发行版和简单地使用 linux 命令行,那么可以迅速开始了。若您尚不具备该 +Linux 发行版和简单地使用 Linux 命令行,那么可以迅速开始了。若您尚不具备该 能力,很多网站上会有详细的手把手教程,最多一个上午,您应该就能掌握对应技 能。您需要注意的一点是,请不要使用 root 用户进行后续步骤和文档翻译。 @@ -66,11 +66,18 @@ linux 发行版和简单地使用 linux 命令行,那么可以迅速开始了 cd linux ./scripts/sphinx-pre-install -以Fedora为例,它的输出是这样的:: +以 Fedora 为例,它的输出是这样的:: You should run: - sudo dnf install -y dejavu-sans-fonts dejavu-sans-mono-fonts dejavu-serif-fonts google-noto-sans-cjk-fonts graphviz-gd latexmk librsvg2-tools texlive-anyfontsize texlive-capt-of texlive-collection-fontsrecommended texlive-ctex texlive-eqparbox texlive-fncychap texlive-framed texlive-luatex85 texlive-multirow texlive-needspace texlive-tabulary texlive-threeparttable texlive-upquote texlive-wrapfig texlive-xecjk + sudo dnf install -y dejavu-sans-fonts dejavu-sans-mono-fonts \ + dejavu-serif-fonts google-noto-sans-cjk-fonts graphviz-gd \ + latexmk librsvg2-tools texlive-anyfontsize texlive-capt-of \ + texlive-collection-fontsrecommended texlive-ctex \ + texlive-eqparbox texlive-fncychap texlive-framed \ + texlive-luatex85 texlive-multirow texlive-needspace \ + texlive-tabulary texlive-threeparttable texlive-upquote \ + texlive-wrapfig texlive-xecjk Sphinx needs to be installed either: 1) via pip/pypi with: @@ -92,7 +99,8 @@ linux 发行版和简单地使用 linux 命令行,那么可以迅速开始了 https://github.com/sphinx-doc/sphinx/pull/8313 请您按照提示复制打印的命令到命令行执行,您必须具备 root 权限才能执行 sudo -开头的命令。 +开头的命令。**请注意**,最新版本 Sphinx 的文档编译速度有极大提升,强烈建议 +您通过 pip/pypi 安装最新版本 Sphinx。 如果您处于一个多用户环境中,为了避免对其他人造成影响,建议您配置单用户 sphinx 虚拟环境,即只需要执行:: @@ -126,11 +134,11 @@ sphinx 虚拟环境,即只需要执行:: 检查编译结果 ------------ -编译输出在Documentation/output/目录下,请用浏览器打开该目录下对应 +编译输出在 Documentation/output/ 目录下,请用浏览器打开该目录下对应 的文件进行检查。 -git和邮箱配置 -------------- +Git 和邮箱配置 +-------------- 打开命令行执行:: @@ -150,11 +158,11 @@ git和邮箱配置 smtpencryption = ssl smtpserver = smtp.migadu.com smtpuser = si.yanteng@linux.dev - smtppass = # 建议使用第三方客户端专用密码 + smtppass = # 建议使用第三方客户端专用密码 chainreplyto = false smtpserverport = 465 -关于邮件客户端的配置,请查阅Documentation/translations/zh_CN/process/email-clients.rst。 +关于邮件客户端的配置,请查阅 Documentation/translations/zh_CN/process/email-clients.rst。 开始翻译文档 ============ @@ -162,8 +170,8 @@ git和邮箱配置 文档索引结构 ------------ -目前中文文档是在Documentation/translations/zh_CN/目录下进行,该 -目录结构最终会与Documentation/结构一致,所以您只需要将您感兴趣的英文 +目前中文文档是在 Documentation/translations/zh_CN/ 目录下进行,该 +目录结构最终会与 Documentation/ 结构一致,所以您只需要将您感兴趣的英文 文档文件和对应的 index.rst 复制到 zh_CN 目录下对应的位置,然后修改更 上一级的 index 即可开始您的翻译。 @@ -177,13 +185,12 @@ git和邮箱配置 请执行以下命令,新建开发分支:: git checkout docs-next - git branch my-trans - git checkout my-trans + git checkout -b my-trans 译文格式要求 ------------ - - 每行长度最多不超过40个字符 + - 每行长度最多不超过 40 个字符 - 每行长度请保持一致 - 标题的下划线长度请按照一个英文一个字符、一个中文两个字符与标题对齐 - 其它的修饰符请与英文文档保持一致 @@ -192,7 +199,7 @@ git和邮箱配置 .. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_CN.rst #您需要了解该文件的路径,根 - 据您实际翻译的文档灵活调整 + 据您实际翻译的文档灵活调整 :Original: Documentation/xxx/xxx.rst #替换为您翻译的英文文档路径 @@ -203,11 +210,11 @@ git和邮箱配置 翻译技巧 -------- -中文文档有每行40字符限制,因为一个中文字符等于2个英文字符。但是社区并没有 -那么严格,一个诀窍是将您的翻译的内容与英文原文的每行长度对齐即可,这样, +中文文档有每行 40 字符限制,因为一个中文字符等于 2 个英文字符。但是社区并 +没有那么严格,一个诀窍是将您的翻译的内容与英文原文的每行长度对齐即可,这样, 您也不必总是检查有没有超限。 -如果您的英文阅读能力有限,可以考虑使用辅助翻译工具,例如 deepseek 。但是您 +如果您的英文阅读能力有限,可以考虑使用辅助翻译工具,例如 deepseek。但是您 必须仔细地打磨,使译文达到“信达雅”的标准。 **请注意** 社区不接受纯机器翻译的文档,社区工作建立在信任的基础上,请认真对待。 @@ -248,14 +255,17 @@ git和邮箱配置 Translate .../security/self-protection.rst into Chinese. - Update the translation through commit b080e52110ea #请执行git log <您翻译的英文文档路径> 复制最顶部第一个补丁的sha值的前12位,替换掉12位sha值。 + Update the translation through commit b080e52110ea ("docs: update self-protection __ro_after_init status") + # 请执行 git log --oneline <您翻译的英文文档路径>,并替换上述内容 - Signed-off-by: Yanteng Si #如果您前面的步骤正确执行,该行会自动显示,否则请检查gitconfig文件。 + Signed-off-by: Yanteng Si + # 如果您前面的步骤正确执行,该行会自动显示,否则请检查 gitconfig 文件 保存并退出。 -**请注意** 以上四行,缺少任何一行,您都将会在第一轮审阅后返工,如果您需要一个更加明确的示例,请对 zh_CN 目录执行 git log。 +**请注意** 以上四行,缺少任何一行,您都将会在第一轮审阅后返工,如果您需要一个 +更加明确的示例,请对 zh_CN 目录执行 git log。 导出补丁和制作封面 ------------------ @@ -263,6 +273,7 @@ git和邮箱配置 这个时候,可以导出补丁,做发送邮件列表最后的准备了。命令行执行:: git format-patch -N + # N 要替换为补丁数量,一般 N 大于等于 1 然后命令行会输出类似下面的内容:: @@ -286,13 +297,13 @@ warning 不需要解决:: 然后执行以下命令为补丁追加更改:: git checkout docs-next - git branch test-trans + git checkout -b test-trans-new git am 0001-xxxxx.patch ./scripts/checkpatch.pl 0001-xxxxx.patch - 直接修改您的翻译 + # 直接修改您的翻译 git add . git am --amend - 保存退出 + # 保存退出 git am 0002-xxxxx.patch …… @@ -301,28 +312,30 @@ warning 不需要解决:: 最后,如果检测时没有 warning 和 error 需要被处理或者您只有一个补丁,请跳 过下面这个步骤,否则请重新导出补丁制作封面:: - git format-patch -N --cover-letter --thread=shallow #N为您的补丁数量,N一般要大于1。 + git format-patch -N --cover-letter --thread=shallow + # N 要替换为补丁数量,一般 N 大于 1 然后命令行会输出类似下面的内容:: 0000-cover-letter.patch 0001-docs-zh_CN-add-xxxxxxxx.patch 0002-docs-zh_CN-add-xxxxxxxx.patch + …… -您需要用编辑器打开0号补丁,修改两处内容:: +您需要用编辑器打开 0 号补丁,修改两处内容:: vim 0000-cover-letter.patch ... - Subject: [PATCH 0/1] *** SUBJECT HERE *** #修改该字段,概括您的补丁集都做了哪些事情 + Subject: [PATCH 0/N] *** SUBJECT HERE *** #修改该字段,概括您的补丁集都做了哪些事情 - *** BLURB HERE *** #修改该字段,详细描述您的补丁集做了哪些事情 + *** BLURB HERE *** #修改该字段,详细描述您的补丁集做了哪些事情 Yanteng Si (1): docs/zh_CN: add xxxxx ... -如果您只有一个补丁,则可以不制作封面,即0号补丁,只需要执行:: +如果您只有一个补丁,则可以不制作封面,即 0 号补丁,只需要执行:: git format-patch -1 @@ -345,9 +358,10 @@ warning 不需要解决:: 打开上面您保存的邮件地址,执行:: - git send-email *.patch --to --cc #一个to对应一个地址,一个cc对应一个地址,有几个就写几个。 + git send-email *.patch --to --cc + # 一个 to 对应一个地址,一个 cc 对应一个地址,有几个就写几个 -执行该命令时,请确保网络通常,邮件发送成功一般会返回250。 +执行该命令时,请确保网络通常,邮件发送成功一般会返回 250。 您可以先发送给自己,尝试发出的 patch 是否可以用 'git am' 工具正常打上。 如果检查正常, 您就可以放心的发送到社区评审了。 @@ -382,15 +396,15 @@ reviewer 的评论,做到每条都有回复,每个回复都落实到位。 每次迭代一个补丁,不要一次多个:: git am <您要修改的补丁> - 直接对文件进行您的修改 + # 直接对文件进行您的修改 git add . git commit --amend 当您将所有的评论落实到位后,导出第二版补丁,并修改封面:: - git format-patch -N -v 2 --cover-letter --thread=shallow + git format-patch -N -v 2 --cover-letter --thread=shallow -打开0号补丁,在 BLURB HERE 处编写相较于上个版本,您做了哪些改动。 +打开 0 号补丁,在 BLURB HERE 处编写相较于上个版本,您做了哪些改动。 然后执行:: @@ -414,7 +428,7 @@ reviewer 的评论,做到每条都有回复,每个回复都落实到位。 如果您发送到邮件列表之后。发现发错了补丁集,尤其是在多个版本迭代的过程中; 自己发现了一些不妥的翻译;发送错了邮件列表…… -git email默认会抄送给您一份,所以您可以切换为审阅者的角色审查自己的补丁, +git email 默认会抄送给您一份,所以您可以切换为审阅者的角色审查自己的补丁, 并留下评论,描述有何不妥,将在下个版本怎么改,并付诸行动,重新提交,但是 注意频率,每天提交的次数不要超过两次。 @@ -425,9 +439,9 @@ git email默认会抄送给您一份,所以您可以切换为审阅者的角 ./script/checktransupdate.py -l zh_CN`` -该命令会列出需要翻译或更新的英文文档。 +该命令会列出需要翻译或更新的英文文档,结果同时保存在 checktransupdate.log 中。 -关于详细操作说明,请参考: Documentation/translations/zh_CN/doc-guide/checktransupdate.rst\ +关于详细操作说明,请参考:Documentation/translations/zh_CN/doc-guide/checktransupdate.rst。 进阶 ---- @@ -439,8 +453,8 @@ git email默认会抄送给您一份,所以您可以切换为审阅者的角 常见的问题 ========== -Maintainer回复补丁不能正常apply -------------------------------- +Maintainer 回复补丁不能正常 apply +--------------------------------- 这通常是因为您的补丁与邮件列表其他人的补丁产生了冲突,别人的补丁先被 apply 了, 您的补丁集就无法成功 apply 了,这需要您更新本地分支,在本地解决完冲突后再次提交。 @@ -455,5 +469,5 @@ Maintainer回复补丁不能正常apply 大部分情况下,是由于您发送了非纯文本格式的信件,请尽量避免使用 webmail,推荐 使用邮件客户端,比如 thunderbird,记得在设置中的回信配置那改为纯文本发送。 -如果超过了24小时,您依旧没有在发现您的邮 -件,请联系您的网络管理员帮忙解决。 +如果超过了 24 小时,您依旧没有在发现您的 +邮件,请联系您的网络管理员帮忙解决。 diff --git a/Documentation/translations/zh_CN/networking/alias.rst b/Documentation/translations/zh_CN/networking/alias.rst new file mode 100644 index 000000000000..e024d9eac50e --- /dev/null +++ b/Documentation/translations/zh_CN/networking/alias.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/alias.rst + +:翻译: + + 邱禹潭 Qiu Yutan + +:校译: + +====== +IP别名 +====== + +IP别名是管理每个接口存在多个IP地址/子网掩码的一种过时方法。 +虽然更新的工具如iproute2支持每个接口多个地址/前缀, +但为了向后兼容性,别名仍被支持。 + +别名通过在使用 ifconfig 时在接口名后添加冒号和一个字符串来创建。 +这个字符串通常是数字,但并非必须。 + + +别名创建 +======== + +别名的创建是通过“特殊的”接口命名机制完成的:例如, +要为eth0创建一个 200.1.1.1 的别名... +:: + + # ifconfig eth0:0 200.1.1.1 等等 + ~~ -> 请求为eth0创建别名#0(如果尚不存在) + +该命令也会设置相应的路由表项。请注意:路由表项始终指向基础接口。 + + +别名删除 +======== + +通过关闭别名即可将其删除:: + + # ifconfig eth0:0 down + ~~~~~~~~~~ -> 将删除别名 + + +别名(重新)配置 +================ + +别名不是真实的设备,但程序应该能够正常配置和引用它们(ifconfig、route等)。 + + +与主设备的关系 +============== + +如果基础设备被关闭,则其上添加的所有别名也将被删除。 diff --git a/Documentation/translations/zh_CN/networking/index.rst b/Documentation/translations/zh_CN/networking/index.rst index d07dd69f980b..bb0edcffd144 100644 --- a/Documentation/translations/zh_CN/networking/index.rst +++ b/Documentation/translations/zh_CN/networking/index.rst @@ -21,6 +21,12 @@ :maxdepth: 1 msg_zerocopy + napi + vxlan + netif-msg + xfrm_proc + netmem + alias Todolist: @@ -45,7 +51,6 @@ Todolist: * page_pool * phy * sfp-phylink -* alias * bridge * snmp_counter * checksum-offloads @@ -94,14 +99,11 @@ Todolist: * mptcp-sysctl * multiqueue * multi-pf-netdev -* napi * net_cachelines/index * netconsole * netdev-features * netdevices * netfilter-sysctl -* netif-msg -* netmem * nexthop-group-resilient * nf_conntrack-sysctl * nf_flowtable @@ -142,11 +144,9 @@ Todolist: * tuntap * udplite * vrf -* vxlan * x25 * x25-iface * xfrm_device -* xfrm_proc * xfrm_sync * xfrm_sysctl * xdp-rx-metadata diff --git a/Documentation/translations/zh_CN/networking/napi.rst b/Documentation/translations/zh_CN/networking/napi.rst new file mode 100644 index 000000000000..619971c3dea3 --- /dev/null +++ b/Documentation/translations/zh_CN/networking/napi.rst @@ -0,0 +1,362 @@ +.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/napi.rst + +:翻译: + + 王亚鑫 Yaxin Wang + +==== +NAPI +==== + +NAPI 是 Linux 网络堆栈中使用的事件处理机制。NAPI 的名称现在不再代表任何特定含义 [#]_。 + +在基本操作中,设备通过中断通知主机有新事件发生。主机随后调度 NAPI 实例来处理这些事件。 +该设备也可以通过 NAPI 进行事件轮询,而无需先接收中断信号(:ref:`忙轮询`)。 + +NAPI 处理通常发生在软中断上下文中,但有一个选项,可以使用 :ref:`单独的内核线程` +来进行 NAPI 处理。 + +总的来说,NAPI 为驱动程序抽象了事件(数据包接收和发送)处理的上下文环境和配置情况。 + +驱动程序API +=========== + +NAPI 最重要的两个元素是 struct napi_struct 和关联的 poll 方法。struct napi_struct +持有 NAPI 实例的状态,而方法则是与驱动程序相关的事件处理器。该方法通常会释放已传输的发送 +(Tx)数据包并处理新接收的数据包。 + +.. _drv_ctrl_zh_CN: + +控制API +------- + +netif_napi_add() 和 netif_napi_del() 用于向系统中添加/删除一个 NAPI 实例。实例会被 +附加到作为参数传递的 netdevice上(并在 netdevice 注销时自动删除)。实例在添加时处于禁 +用状态。 + +napi_enable() 和 napi_disable() 管理禁用状态。禁用的 NAPI 不会被调度,并且保证其 +poll 方法不会被调用。napi_disable() 会等待 NAPI 实例的所有权被释放。 + +这些控制 API 并非幂等的。控制 API 调用在面对数据路径 API 的并发使用时是安全的,但控制 +API 调用顺序错误可能会导致系统崩溃、死锁或竞态条件。例如,连续多次调用 napi_disable() +会造成死锁。 + +数据路径API +----------- + +napi_schedule() 是调度 NAPI 轮询的基本方法。驱动程序应在其中断处理程序中调用此函数 +(更多信息请参见 :ref:`drv_sched_zh_CN`)。成功的 napi_schedule() 调用将获得 NAPI 实例 +的所有权。 + +之后,在 NAPI 被调度后,驱动程序的 poll 方法将被调用以处理事件/数据包。该方法接受一个 +``budget`` 参数 - 驱动程序可以处理任意数量的发送 (Tx) 数据包完成,但处理最多处理 +``budget`` 个接收 (Rx) 数据包。处理接收数据包通常开销更大。 + +换句话说,对于接收数据包的处理,``budget`` 参数限制了驱动程序在单次轮询中能够处理的数 +据包数量。当 ``budget`` 为 0 时,像页面池或 XDP 这类专门用于接收的 API 根本无法使用。 +无论 ``budget`` 的值是多少,skb 的发送处理都应该进行,但是如果 ``budget`` 参数为 0, +驱动程序就不能调用任何 XDP(或页面池)API。 + +.. warning:: + + 如果内核仅尝试处理skb的发送完成情况,而不处理接收 (Rx) 或 XDP 数据包,那么 ``budget`` + 参数可能为 0。 + +轮询方法会返回已完成的工作量。如果驱动程序仍有未完成的工作(例如,``budget`` 已用完), +轮询方法应精确返回 ``budget`` 的值。在这种情况下,NAPI 实例将再次被处理 / 轮询(无需 +重新调度)。 + +如果事件处理已完成(所有未处理的数据包都已处理完毕),轮询方法在返回之前应调用 napi_complete_done()。 +napi_complete_done() 会释放实例的所有权。 + +.. warning:: + + 当出现既完成了所有事件处理,又恰好达到了 ``budget`` 数量的情况时,必须谨慎处理。因为没 + 有办法将这种(很少出现的)情况报告给协议栈,所以驱动程序要么不调用 napi_complete_done() + 并等待再次被调用,要么返回 ``budget - 1``。 + + 当 ``budget`` 为 0 时,napi_complete_done() 绝对不能被调用。 + +调用序列 +-------- + +驱动程序不应假定调用的顺序是固定不变的。即使驱动程序没有调度该实例,轮询方法也可能会被调用 +(除非该实例处于禁用状态)。同样,即便 napi_schedule() 调用成功,也不能保证轮询方法一定 +会被调用(例如,如果该实例被禁用)。 + +正如在 :ref:`drv_ctrl_zh_CN` 部分所提到的,napi_disable() 以及后续对轮询方法的调用, +仅会等待该实例的所有权被释放,而不会等待轮询方法退出。这意味着,驱动程序在调用 napi_complete_done() +之后,应避免访问任何数据结构。 + +.. _drv_sched_zh_CN: + +调度与IRQ屏蔽 +------------- + +驱动程序应在调度 NAPI 实例后保持中断屏蔽 - 直到 NAPI 轮询完成,任何进一步的中断都是不必要的。 + +显式屏蔽中断的驱动程序(而非设备自动屏蔽 IRQ)应使用 napi_schedule_prep() 和 +__napi_schedule() 调用: + +.. code-block:: c + + if (napi_schedule_prep(&v->napi)) { + mydrv_mask_rxtx_irq(v->idx); + /* 在屏蔽后调度以避免竞争 */ + __napi_schedule(&v->napi); + } + +IRQ 仅应在成功调用 napi_complete_done() 后取消屏蔽: + +.. code-block:: c + + if (budget && napi_complete_done(&v->napi, work_done)) { + mydrv_unmask_rxtx_irq(v->idx); + return min(work_done, budget - 1); + } + +napi_schedule_irqoff() 是 napi_schedule() 的一个变体,它利用了在中断请求(IRQ)上下文 +环境中调用所带来的特性(无需屏蔽中断)。如果中断请求(IRQ)是通过线程处理的(例如启用了 +``PREEMPT_RT`` 时的情况),napi_schedule_irqoff() 会回退为使用 napi_schedule() 。 + +实例到队列的映射 +---------------- + +现代设备每个接口有多个 NAPI 实例(struct napi_struct)。关于实例如何映射到队列和中断没有 +严格要求。NAPI 主要是事件处理/轮询抽象,没有用户可见的语义。也就是说,大多数网络设备最终以 +非常相似的方式使用 NAPI。 + +NAPI 实例最常以 1:1:1 映射到中断和队列对(队列对是由一个接收队列和一个发送队列组成的一组 +队列)。 + +在不太常见的情况下,一个 NAPI 实例可能会用于处理多个队列,或者在单个内核上,接收(Rx)队列 +和发送(Tx)队列可以由不同的 NAPI 实例来处理。不过,无论队列如何分配,通常 NAPI 实例和中断 +之间仍然保持一一对应的关系。 + +值得注意的是,ethtool API 使用了 “通道” 这一术语,每个通道可以是 ``rx`` (接收)、``tx`` +(发送)或 ``combined`` (组合)类型。目前尚不清楚一个通道具体由什么构成,建议的理解方式是 +将一个通道视为一个为特定类型队列提供服务的 IRQ(中断请求)/ NAPI 实例。例如,配置为 1 个 +``rx`` 通道、1 个 ``tx`` 通道和 1 个 ``combined`` 通道的情况下,预计会使用 3 个中断、 +2 个接收队列和 2 个发送队列。 + +持久化NAPI配置 +-------------- + +驱动程序常常会动态地分配和释放 NAPI 实例。这就导致每当 NAPI 实例被重新分配时,与 NAPI 相关 +的用户配置就会丢失。netif_napi_add_config() API接口通过将每个 NAPI 实例与基于驱动程序定义 +的索引值(如队列编号)的持久化 NAPI 配置相关联,从而避免了这种配置丢失的情况。 + +使用此 API 可实现持久化的 NAPI 标识符(以及其他设置),这对于使用 ``SO_INCOMING_NAPI_ID`` +的用户空间程序来说是有益的。有关其他 NAPI 配置的设置,请参阅以下章节。 + +驱动程序应尽可能尝试使用 netif_napi_add_config()。 + +用户API +======= + +用户与 NAPI 的交互依赖于 NAPI 实例 ID。这些实例 ID 仅通过 ``SO_INCOMING_NAPI_ID`` 套接字 +选项对用户可见。 + +用户可以使用 Netlink 来查询某个设备或设备队列的 NAPI 标识符。这既可以在用户应用程序中通过编程 +方式实现,也可以使用内核源代码树中包含的一个脚本:tools/net/ynl/pyynl/cli.py 来完成。 + +例如,使用该脚本转储某个设备的所有队列(这将显示每个队列的 NAPI 标识符): + + +.. code-block:: bash + + $ kernel-source/tools/net/ynl/pyynl/cli.py \ + --spec Documentation/netlink/specs/netdev.yaml \ + --dump queue-get \ + --json='{"ifindex": 2}' + +有关可用操作和属性的更多详细信息,请参阅 ``Documentation/netlink/specs/netdev.yaml``。 + +软件IRQ合并 +----------- + +默认情况下,NAPI 不执行任何显式的事件合并。在大多数场景中,数据包的批量处理得益于设备进行 +的中断请求(IRQ)合并。不过,在某些情况下,软件层面的合并操作也很有帮助。 + +可以将 NAPI 配置为设置一个重新轮询定时器,而不是在处理完所有数据包后立即取消屏蔽硬件中断。 +网络设备的 ``gro_flush_timeout`` sysfs 配置项可用于控制该定时器的延迟时间,而 ``napi_defer_hard_irqs`` +则用于控制在 NAPI 放弃并重新启用硬件中断之前,连续进行空轮询的次数。 + +上述参数也可以通过 Netlink 的 netdev-genl 接口,基于每个 NAPI 实例进行设置。当通过 +Netlink 进行配置且是基于每个 NAPI 实例设置时,上述参数使用连字符(-)而非下划线(_) +来命名,即 ``gro-flush-timeout`` 和 ``napi-defer-hard-irqs``。 + +基于每个 NAPI 实例的配置既可以在用户应用程序中通过编程方式完成,也可以使用内核源代码树中的 +一个脚本实现,该脚本为 ``tools/net/ynl/pyynl/cli.py``。 + +例如,通过如下方式使用该脚本: + +.. code-block:: bash + + $ kernel-source/tools/net/ynl/pyynl/cli.py \ + --spec Documentation/netlink/specs/netdev.yaml \ + --do napi-set \ + --json='{"id": 345, + "defer-hard-irqs": 111, + "gro-flush-timeout": 11111}' + +类似地,参数 ``irq-suspend-timeout`` 也可以通过 netlink 的 netdev-genl 设置。没有全局 +的 sysfs 参数可用于设置这个值。 + +``irq-suspend-timeout`` 用于确定应用程序可以完全挂起 IRQ 的时长。与 SO_PREFER_BUSY_POLL +结合使用,后者可以通过 ``EPIOCSPARAMS`` ioctl 在每个 epoll 上下文中设置。 + +.. _poll_zh_CN: + +忙轮询 +------ + +忙轮询允许用户进程在设备中断触发前检查传入的数据包。与其他忙轮询一样,它以 CPU 周期换取更低 +的延迟(生产环境中 NAPI 忙轮询的使用尚不明确)。 + +通过在选定套接字上设置 ``SO_BUSY_POLL`` 或使用全局 ``net.core.busy_poll`` 和 ``net.core.busy_read`` +等 sysctls 启用忙轮询。还存在基于 io_uring 的 NAPI 忙轮询 API 可使用。 + +基于epoll的忙轮询 +----------------- + +可以从 ``epoll_wait`` 调用直接触发数据包处理。为了使用此功能,用户应用程序必须确保添加到 +epoll 上下文的所有文件描述符具有相同的 NAPI ID。 + +如果应用程序使用专用的 acceptor 线程,那么该应用程序可以获取传入连接的 NAPI ID(使用 +SO_INCOMING_NAPI_ID)然后将该文件描述符分发给工作线程。工作线程将该文件描述符添加到其 +epoll 上下文。这确保了每个工作线程的 epoll 上下文中所包含的文件描述符具有相同的 NAPI ID。 + +或者,如果应用程序使用 SO_REUSEPORT,可以插入 bpf 或 ebpf 程序来分发传入连接,使得每个 +线程只接收具有相同 NAPI ID 的连接。但是必须谨慎处理系统中可能存在多个网卡的情况。 + +为了启用忙轮询,有两种选择: + +1. ``/proc/sys/net/core/busy_poll`` 可以设置为微秒数以在忙循环中等待事件。这是一个系统 + 范围的设置,将导致所有基于 epoll 的应用程序在调用 epoll_wait 时忙轮询。这可能不是理想 + 的情况,因为许多应用程序可能不需要忙轮询。 + +2. 使用最新内核的应用程序可以在 epoll 上下文的文件描述符上发出 ioctl 来设置(``EPIOCSPARAMS``) + 或获取(``EPIOCGPARAMS``) ``struct epoll_params``,用户程序定义如下: + +.. code-block:: c + + struct epoll_params { + uint32_t busy_poll_usecs; + uint16_t busy_poll_budget; + uint8_t prefer_busy_poll; + + /* 将结构填充到 64 位的倍数 */ + uint8_t __pad; + }; + +IRQ缓解 +------- + +虽然忙轮询旨在用于低延迟应用,但类似的机制可用于减少中断请求。 + +每秒高请求的应用程序(尤其是路由/转发应用程序和特别使用 AF_XDP 套接字的应用程序) +可能希望在处理完一个请求或一批数据包之前不被中断。 + +此类应用程序可以向内核承诺会定期执行忙轮询操作,而驱动程序应将设备的中断请求永久屏蔽。 +通过使用 ``SO_PREFER_BUSY_POLL`` 套接字选项可启用此模式。为避免系统出现异常,如果 +在 ``gro_flush_timeout`` 时间内没有进行任何忙轮询调用,该承诺将被撤销。对于基于 +epoll 的忙轮询应用程序,可以将 ``struct epoll_params`` 结构体中的 ``prefer_busy_poll`` +字段设置为 1,并使用 ``EPIOCSPARAMS`` 输入 / 输出控制(ioctl)操作来启用此模式。 +更多详情请参阅上述章节。 + +NAPI 忙轮询的 budget 低于默认值(这符合正常忙轮询的低延迟意图)。减少中断请求的场景中 +并非如此,因此 budget 可以通过 ``SO_BUSY_POLL_BUDGET`` 套接字选项进行调整。对于基于 +epoll 的忙轮询应用程序,可以通过调整 ``struct epoll_params`` 中的 ``busy_poll_budget`` +字段为特定值,并使用 ``EPIOCSPARAMS`` ioctl 在特定 epoll 上下文中设置。更多详细信 +息请参见上述部分。 + +需要注意的是,为 ``gro_flush_timeout`` 选择较大的值会延迟中断请求,以实现更好的批 +量处理,但在系统未满载时会增加延迟。为 ``gro_flush_timeout`` 选择较小的值可能会因 +设备中断请求和软中断处理而干扰尝试进行忙轮询的用户应用程序。应权衡这些因素后谨慎选择 +该值。基于 epoll 的忙轮询应用程序可以通过为 ``maxevents`` 选择合适的值来减少用户 +处理的干扰。 + +用户可能需要考虑使用另一种方法,IRQ 挂起,以帮助应对这些权衡问题。 + +IRQ挂起 +------- + +IRQ 挂起是一种机制,其中设备 IRQ 在 epoll 触发 NAPI 数据包处理期间被屏蔽。 + +只要应用程序对 epoll_wait 的调用成功获取事件,内核就会推迟 IRQ 挂起定时器。如果 +在忙轮询期间没有获取任何事件(例如,因为网络流量减少),则会禁用IRQ挂起功能,并启 +用上述减少中断请求的策略。 + +这允许用户在 CPU 消耗和网络处理效率之间取得平衡。 + +要使用此机制: + + 1. 每个 NAPI 的配置参数 ``irq-suspend-timeout`` 应设置为应用程序可以挂起 + IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 + 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 + 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 + 序可通过在调用 epoll_wait 时设置 ``max_events`` 来控制获取的数据量。 + + 2. sysfs 参数或每个 NAPI 的配置参数 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` + 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。 + + 3. 必须将 ``prefer_busy_poll`` 标志设置为 true。如前文所述,可使用 ``EPIOCSPARAMS`` + ioctl操作来完成此设置。 + + 4. 应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。 + +如上所述,只要后续对 epoll_wait 的调用向用户空间返回事件,``irq-suspend-timeout`` +就会被推迟并且 IRQ 会被禁用。这允许应用程序在无干扰的情况下处理数据。 + +一旦 epoll_wait 的调用没有找到任何事件,IRQ 挂起会被自动禁用,并且 ``gro_flush_timeout`` +和 ``napi_defer_hard_irqs`` 缓解机制将开始起作用。 + +预期是 ``irq-suspend-timeout`` 的设置值会远大于 ``gro_flush_timeout``,因为 ``irq-suspend-timeout`` +应在一个用户空间处理周期内暂停中断请求。 + +虽然严格来说不必通过 ``napi_defer_hard_irqs`` 和 ``gro_flush_timeout`` 来执行 IRQ 挂起, +但强烈建议这样做。 + +中断请求挂起会使系统在轮询模式和由中断驱动的数据包传输模式之间切换。在网络繁忙期间,``irq-suspend-timeout`` +会覆盖 ``gro_flush_timeout``,使系统保持忙轮询状态,但是当 epoll 未发现任何事件时,``gro_flush_timeout`` +和 ``napi_defer_hard_irqs`` 的设置将决定下一步的操作。 + +有三种可能的网络处理和数据包交付循环: + +1) 硬中断 -> 软中断 -> NAPI 轮询;基本中断交付 +2) 定时器 -> 软中断 -> NAPI 轮询;延迟的 IRQ 处理 +3) epoll -> 忙轮询 -> NAPI 轮询;忙循环 + +循环 2 可以接管循环 1,如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``。 + +如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 2 和 3 将互相“争夺”控制权。 + +在繁忙时期,``irq-suspend-timeout`` 用作循环 2 的定时器,这基本上使网络处理倾向于循环 3。 + +如果不设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 3 无法从循环 1 接管。 + +因此,建议设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,因为若不这样做,设置 +``irq-suspend-timeout`` 可能不会有明显效果。 + +.. _threaded_zh_CN: + +线程化NAPI +---------- + +线程化 NAPI 是一种操作模式,它使用专用的内核线程而非软件中断上下文来进行 NAPI 处理。这种配置 +是针对每个网络设备的,并且会影响该设备的所有 NAPI 实例。每个 NAPI 实例将生成一个单独的线程 +(称为 ``napi/${ifc-name}-${napi-id}`` )。 + +建议将每个内核线程固定到单个 CPU 上,这个 CPU 与处理中断的 CPU 相同。请注意,中断请求(IRQ) +和 NAPI 实例之间的映射关系可能并不简单(并且取决于驱动程序)。NAPI 实例 ID 的分配顺序将与内 +核线程的进程 ID 顺序相反。 + +线程化 NAPI 是通过向网络设备的 sysfs 目录中的 ``threaded`` 文件写入 0 或 1 来控制的。 + +.. rubric:: 脚注 + +.. [#] NAPI 最初在 2.4 Linux 中被称为 New API。 diff --git a/Documentation/translations/zh_CN/networking/netif-msg.rst b/Documentation/translations/zh_CN/networking/netif-msg.rst new file mode 100644 index 000000000000..877399b169fe --- /dev/null +++ b/Documentation/translations/zh_CN/networking/netif-msg.rst @@ -0,0 +1,92 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/netif-msg.rst + +:翻译: + + 王亚鑫 Wang Yaxin + +================ +网络接口消息级别 +================ + +网络接口消息级别设置的设计方案。 + +历史背景 +-------- + +调试消息接口的设计遵循并受制于向后兼容性及历史实践。理解其发展历史有助于把握 +当前实践,并将其与旧版驱动代码相关联。 + +自Linux诞生之初,每个网络设备驱动均包含一个本地整型变量以控制调试消息级别。 +消息级别范围为0至7,数值越大表示输出越详细。 + +消息级别的定义在3级之后未明确细化,但实际实现通常与指定级别相差±1。驱动程序 +成熟后,冗余的详细级别消息常被移除。 + + - 0 最简消息,仅显示致命错误的关键信息。 + - 1 标准消息,初始化状态。无运行时消息。 + - 2 特殊介质选择消息,通常由定时器驱动。 + - 3 接口开启和停止消息,包括正常状态信息。 + - 4 Tx/Rx帧错误消息及异常驱动操作。 + - 5 Tx数据包队列信息、中断事件。 + - 6 每个完成的Tx数据包和接收的Rx数据包状态。 + - 7 Tx/Rx数据包初始内容。 + +最初,该消息级别变量在各驱动中具有唯一名称(如"lance_debug"),便于通过 +内核符号调试器定位和修改其设置。模块化内核出现后,变量统一重命名为"debug", +并作为模块参数设置。 + +这种方法效果良好。然而,人们始终对附加功能存在需求。多年来,以下功能逐渐 +成为合理且易于实现的增强方案: + + - 通过ioctl()调用修改消息级别。 + - 按接口而非驱动设置消息级别。 + - 对发出的消息类型进行更具选择性的控制。 + +netif_msg 建议添加了这些功能,仅带来了轻微的复杂性增加和代码规模增长。 + +推荐方案如下: + + - 保留驱动级整型变量"debug"作为模块参数,默认值为'1'。 + + - 添加一个名为 "msg_enable" 的接口私有变量。该变量是位图而非级别, + 并按如下方式初始化:: + + 1 << debug + + 或更精确地说:: + + debug < 0 ? 0 : 1 << min(sizeof(int)-1, debug) + + 消息应从以下形式更改:: + + if (debug > 1) + printk(MSG_DEBUG "%s: ... + + 改为:: + + if (np->msg_enable & NETIF_MSG_LINK) + printk(MSG_DEBUG "%s: ... + +消息级别命名对应关系 + + + ========= =================== ============ + 旧级别 名称 位位置 + ========= =================== ============ + 1 NETIF_MSG_PROBE 0x0002 + 2 NETIF_MSG_LINK 0x0004 + 2 NETIF_MSG_TIMER 0x0004 + 3 NETIF_MSG_IFDOWN 0x0008 + 3 NETIF_MSG_IFUP 0x0008 + 4 NETIF_MSG_RX_ERR 0x0010 + 4 NETIF_MSG_TX_ERR 0x0010 + 5 NETIF_MSG_TX_QUEUED 0x0020 + 5 NETIF_MSG_INTR 0x0020 + 6 NETIF_MSG_TX_DONE 0x0040 + 6 NETIF_MSG_RX_STATUS 0x0040 + 7 NETIF_MSG_PKTDATA 0x0080 + ========= =================== ============ diff --git a/Documentation/translations/zh_CN/networking/netmem.rst b/Documentation/translations/zh_CN/networking/netmem.rst new file mode 100644 index 000000000000..fe351a240f02 --- /dev/null +++ b/Documentation/translations/zh_CN/networking/netmem.rst @@ -0,0 +1,92 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/netmem.rst + +:翻译: + + 王亚鑫 Wang Yaxin + +================== +网络驱动支持Netmem +================== + +本文档概述了网络驱动支持netmem(一种抽象内存类型)的要求,该内存类型 +支持设备内存 TCP 等功能。通过支持netmem,驱动可以灵活适配不同底层内 +存类型(如设备内存TCP),且无需或仅需少量修改。 + +Netmem的优势: + +* 灵活性:netmem 可由不同内存类型(如 struct page、DMA-buf)支持, + 使驱动程序能够支持设备内存 TCP 等各种用例。 +* 前瞻性:支持netmem的驱动可无缝适配未来依赖此功能的新特性。 +* 简化开发:驱动通过统一API与netmem交互,无需关注底层内存的实现差异。 + +驱动RX要求 +========== + +1. 驱动必须支持page_pool。 + +2. 驱动必须支持tcp-data-split ethtool选项。 + +3. 驱动必须使用page_pool netmem API处理有效载荷内存。当前netmem API + 与page API一一对应。转换时需要将page API替换为netmem API,并用驱动 + 中的netmem_refs跟踪内存而非 `struct page *`: + + - page_pool_alloc -> page_pool_alloc_netmem + - page_pool_get_dma_addr -> page_pool_get_dma_addr_netmem + - page_pool_put_page -> page_pool_put_netmem + + 目前并非所有页 pageAPI 都有对应的 netmem 等效接口。如果你的驱动程序 + 依赖某个尚未实现的 netmem API,请直接实现并提交至 netdev@邮件列表, + 或联系维护者及 almasrymina@google.com 协助添加该 netmem API。 + +4. 驱动必须设置以下PP_FLAGS: + + - PP_FLAG_DMA_MAP:驱动程序无法对 netmem 执行 DMA 映射。此时驱动 + 程序必须将 DMA 映射操作委托给 page_pool,由其判断何时适合(或不适合) + 进行 DMA 映射。 + - PP_FLAG_DMA_SYNC_DEV:驱动程序无法保证 netmem 的 DMA 地址一定能 + 完成 DMA 同步。此时驱动程序必须将 DMA 同步操作委托给 page_pool,由 + 其判断何时适合(或不适合)进行 DMA 同步。 + - PP_FLAG_ALLOW_UNREADABLE_NETMEM:仅当启用 tcp-data-split 时, + 驱动程序必须显式设置此标志。 + +5. 驱动不得假设netmem可读或基于页。当netmem_address()返回NULL时,表示 +内存不可读。驱动需正确处理不可读的netmem,例如,当netmem_address()返回 +NULL时,避免访问内容。 + + 理想情况下,驱动程序不应通过netmem_is_net_iov()等辅助函数检查底层 + netmem 类型,也不应通过netmem_to_page()或netmem_to_net_iov()将 + netmem 转换为其底层类型。在大多数情况下,系统会提供抽象这些复杂性的 + netmem 或 page_pool 辅助函数(并可根据需要添加更多)。 + +6. 驱动程序必须使用page_pool_dma_sync_netmem_for_cpu()代替dma_sync_single_range_for_cpu()。 +对于某些内存提供者,CPU 的 DMA 同步将由 page_pool 完成;而对于其他提供者 +(特别是 dmabuf 内存提供者),CPU 的 DMA 同步由使用 dmabuf API 的用户空 +间负责。驱动程序必须将整个 DMA 同步操作委托给 page_pool,以确保操作正确执行。 + +7. 避免在 page_pool 之上实现特定于驱动程序内存回收机制。由于 netmem 可能 +不由struct page支持,驱动程序不能保留struct page来进行自定义回收。不过, +可为此目的通过page_pool_fragment_netmem()或page_pool_ref_netmem()保留 +page_pool 引用,但需注意某些 netmem 类型的循环时间可能更长(例如零拷贝场景 +下用户空间持有引用的情况)。 + +驱动TX要求 +========== + +1. 驱动程序绝对不能直接把 netmem 的 dma_addr 传递给任何 dma-mapping API。这 +是由于 netmem 的 dma_addr 可能源自 dma-buf 这类和 dma-mapping API 不兼容的 +源头。 + +应当使用netmem_dma_unmap_page_attrs()和netmem_dma_unmap_addr_set()等辅助 +函数来替代dma_unmap_page[_attrs]()、dma_unmap_addr_set()。不管 dma_addr +来源如何,netmem 的这些变体都能正确处理 netmem dma_addr,在合适的时候会委托给 +dma-mapping API 去处理。 + +目前,并非所有的 dma-mapping API 都有对应的 netmem 版本。要是你的驱动程序需要 +使用某个还不存在的 netmem API,你可以自行添加并提交到 netdev@,也可以联系维护 +人员或者发送邮件至 almasrymina@google.com 寻求帮助。 + +2. 驱动程序应通过设置 netdev->netmem_tx = true 来表明自身支持 netmem 功能。 diff --git a/Documentation/translations/zh_CN/networking/vxlan.rst b/Documentation/translations/zh_CN/networking/vxlan.rst new file mode 100644 index 000000000000..e319eddfcdbe --- /dev/null +++ b/Documentation/translations/zh_CN/networking/vxlan.rst @@ -0,0 +1,85 @@ +.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/vxlan.rst + +:翻译: + + 范雨 Fan Yu + +:校译: + + - 邱禹潭 Qiu Yutan + - 徐鑫 xu xin + +========================== +虚拟扩展本地局域网协议文档 +========================== + +VXLAN 协议是一种隧道协议,旨在解决 IEEE 802.1q 中 VLAN ID(4096)有限的问题。 +VXLAN 将标识符的大小扩展到 24 位(16777216)。 + +VXLAN 在 IETF RFC 7348 中进行了描述,并已由多家供应商设计实现。 +该协议通过 UDP 协议运行,并使用特定目的端口。 +本文档介绍了 Linux 内核隧道设备,Openvswitch 也有单独的 VXLAN 实现。 + +与大多数隧道不同,VXLAN 是 1 对 N 的网络,而不仅仅是点对点网络。 +VXLAN 设备可以通过类似于学习桥接器的方式动态学习另一端点的 IP 地址,也可以利用静态配置的转发条目。 + +VXLAN 的管理方式与它的两个近邻 GRE 和 VLAN 相似。 +配置 VXLAN 需要 iproute2 的版本与 VXLAN 首次向上游合并的内核版本相匹配。 + +1. 创建 vxlan 设备:: + + # ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth1 dstport 4789 + +这将创建一个名为 vxlan0 的网络设备,该设备通过 eth1 使用组播组 239.1.1.1 处理转发表中没有对应条目的流量。 +目标端口号设置为 IANA 分配的值 4789,VXLAN 的 Linux 实现早于 IANA 选择标准目的端口号的时间。 +因此默认使用 Linux 选择的值,以保持向后兼容性。 + +2. 删除 vxlan 设备:: + + # ip link delete vxlan0 + +3. 查看 vxlan 设备信息:: + + # ip -d link show vxlan0 + +使用新的 bridge 命令可以创建、销毁和显示 vxlan 转发表。 + +1. 创建vxlan转发表项:: + + # bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0 + +2. 删除vxlan转发表项:: + + # bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0 + +3. 显示vxlan转发表项:: + + # bridge fdb show dev vxlan0 + +以下网络接口控制器特性可能表明对 UDP 隧道相关的卸载支持(最常见的是 VXLAN 功能, +但是对特定封装协议的支持取决于网络接口控制器): + + - `tx-udp_tnl-segmentation` + - `tx-udp_tnl-csum-segmentation` + 对 UDP 封装帧执行 TCP 分段卸载的能力 + + - `rx-udp_tunnel-port-offload` + 在接收端解析 UDP 封装帧,使网络接口控制器能够执行协议感知卸载, + 例如内部帧的校验和验证卸载(只有不带协议感知卸载的网络接口控制器才需要) + +对于支持 `rx-udp_tunnel-port-offload` 的设备,可使用 `ethtool` 查询当前卸载端口的列表:: + + $ ethtool --show-tunnels eth0 + Tunnel information for eth0: + UDP port table 0: + Size: 4 + Types: vxlan + No entries + UDP port table 1: + Size: 4 + Types: geneve, vxlan-gpe + Entries (1): + port 1230, vxlan-gpe diff --git a/Documentation/translations/zh_CN/networking/xfrm_proc.rst b/Documentation/translations/zh_CN/networking/xfrm_proc.rst new file mode 100644 index 000000000000..a2ae86c44707 --- /dev/null +++ b/Documentation/translations/zh_CN/networking/xfrm_proc.rst @@ -0,0 +1,126 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/networking/xfrm_proc.rst + +:翻译: + + 王亚鑫 Wang Yaxin + +================================= +XFRM proc - /proc/net/xfrm_* 文件 +================================= + +作者:Masahide NAKAMURA + + +转换统计信息 +------------ + +`xfrm_proc` 提供一组统计计数器,显示转换过程中丢弃的数据包及其原因。 +这些计数器属于Linux私有MIB的一部分,可通过 `/proc/net/xfrm_stat` +查看。 + +入站错误 +~~~~~~~~ + +XfrmInError: + 未匹配其他类别的所有错误 + +XfrmInBufferError: + 缓冲区不足 + +XfrmInHdrError: + 头部错误 + +XfrmInNoStates: + 未找到状态 + (入站SPI、地址或SA的IPsec协议不匹配) + +XfrmInStateProtoError: + 转换协议相关的错误 + (如SA密钥错误) + +XfrmInStateModeError: + 转换模式相关的错误 + +XfrmInStateSeqError: + 序列号错误 + 序列号超出窗口范围 + +XfrmInStateExpired: + 状态已过期 + +XfrmInStateMismatch: + 状态选项不匹配 + (如UDP封装类型不匹配) + +XfrmInStateInvalid: + 无效状态 + +XfrmInTmplMismatch: + 状态模板不匹配 + (如入站SA正确但SP规则错误) + +XfrmInNoPols: + 未找到状态的对应策略 + (如入站SA正确但无SP规则) + +XfrmInPolBlock: + 丢弃的策略 + +XfrmInPolError: + 错误的策略 + +XfrmAcquireError: + 状态未完全获取即被使用 + +XfrmFwdHdrError: + 转发路由禁止 + +XfrmInStateDirError: + 状态方向不匹配 + (输入路径查找到输出状态,预期是输入状态或者无方向) + +出站错误 +~~~~~~~~ +XfrmOutError: + 未匹配其他类别的所有错误 + +XfrmOutBundleGenError: + 捆绑包生成错误 + +XfrmOutBundleCheckError: + 捆绑包校验错误 + +XfrmOutNoStates: + 未找到状态 + +XfrmOutStateProtoError: + 转换协议特定错误 + +XfrmOutStateModeError: + 转换模式特定错误 + +XfrmOutStateSeqError: + 序列号错误 + (序列号溢出) + +XfrmOutStateExpired: + 状态已过期 + +XfrmOutPolBlock: + 丢弃策略 + +XfrmOutPolDead: + 失效策略 + +XfrmOutPolError: + 错误策略 + +XfrmOutStateInvalid: + 无效状态(可能已过期) + +XfrmOutStateDirError: + 状态方向不匹配(输出路径查找到输入状态,预期为输出状态或无方向) diff --git a/Documentation/translations/zh_CN/process/1.Intro.rst b/Documentation/translations/zh_CN/process/1.Intro.rst index 4f9284cbe33b..e314cce49d27 100644 --- a/Documentation/translations/zh_CN/process/1.Intro.rst +++ b/Documentation/translations/zh_CN/process/1.Intro.rst @@ -182,11 +182,11 @@ Andrew Morton, Andrew Price, Tsugikazu Shibata 和 Jochen Voß 。 可以获得所有版权所有者的同意(或者从内核中删除他们的代码)。因此,尤其是在 可预见的将来,许可证不大可能迁移到GPL的版本3。 -所有贡献给内核的代码都必须是合法的免费软件。因此,不接受匿名(或化名)贡献 -者的代码。所有贡献者都需要在他们的代码上“sign off(签发)”,声明代码可以 -在GPL下与内核一起分发。无法提供未被其所有者许可为免费软件的代码,或可能为 -内核造成版权相关问题的代码(例如,由缺乏适当保护的反向工程工作派生的代码) -不能被接受。 +所有贡献给内核的代码都必须是合法的免费软件。因此,出于这个原因,身份不明的 +贡献者或匿名贡献者提交的代码将不予接受。所有贡献者都需要在他们的代码上 +“sign off(签发)”,声明代码可以在GPL下与内核一起分发。无法提供未被其所有者 +许可为免费软件的代码,或可能为内核造成版权相关问题的代码(例如,由缺乏适当 +保护的反向工程工作派生的代码)不能被接受。 有关版权问题的提问在Linux开发邮件列表中很常见。这样的问题通常会得到不少答案, 但请记住,回答这些问题的人不是律师,不能提供法律咨询。如果您有关于Linux源代码 diff --git a/Documentation/translations/zh_CN/process/2.Process.rst b/Documentation/translations/zh_CN/process/2.Process.rst index e68c9de0f7f8..31b0e2c994f6 100644 --- a/Documentation/translations/zh_CN/process/2.Process.rst +++ b/Documentation/translations/zh_CN/process/2.Process.rst @@ -292,12 +292,11 @@ Quilt 是一个补丁管理系统,而不是源代码管理系统。它不会 一个潜在的危险,他们可能会被一堆电子邮件淹没、违反Linux列表上使用的约定, 或者两者兼而有之。 -大多数内核邮件列表都在vger.kernel.org上运行;主列表位于: +大多数内核邮件列表都托管在 kernel.org;主列表位于: - http://vger.kernel.org/vger-lists.html + https://subspace.kernel.org -不过,也有一些列表托管在别处;其中一些列表位于 -redhat.com/mailman/listinfo。 +其他地方也有邮件列表;请查看 MAINTAINERS 文件,获取与特定子系统相关的列表。 当然,内核开发的核心邮件列表是linux-kernel。这个列表是一个令人生畏的地方: 每天的信息量可以达到500条,噪音很高,谈话技术性很强,且参与者并不总是表现出 diff --git a/Documentation/translations/zh_CN/process/5.Posting.rst b/Documentation/translations/zh_CN/process/5.Posting.rst index 6c83a8f40310..ce37cf6a60e2 100644 --- a/Documentation/translations/zh_CN/process/5.Posting.rst +++ b/Documentation/translations/zh_CN/process/5.Posting.rst @@ -177,10 +177,21 @@ - Reported-by: 指定报告此补丁修复的问题的用户;此标记用于表示感谢。 + - Suggested-by: 表示该补丁思路由所提及的人提出,确保其创意贡献获得认可。 + 这有望激励他们在未来继续提供帮助。 + - Cc:指定某人收到了补丁的副本,并有机会对此发表评论。 在补丁中添加标签时要小心:只有Cc:才适合在没有指定人员明确许可的情况下添加。 +在补丁中添加上述标签时需谨慎,因为除了 Cc:、Reported-by: 和 Suggested-by:, +所有其他标签都需要被提及者的明确许可。对于这三个标签,若根据 lore 归档或提交 +历史记录,相关人员使用该姓名和电子邮件地址为 Linux 内核做出过贡献,则隐含许可 +已足够 -- 对于 Reported-by: 和 Suggested-by:,需确保报告或建议是公开进行的。 +请注意,从这个意义上讲,bugzilla.kernel.org 属于公开场合,但其使用的电子邮件地址 +属于私人信息;因此,除非相关人员曾在早期贡献中使用过这些邮箱,否则请勿在标签中 +公开它们。 + 寄送补丁 -------- diff --git a/Documentation/translations/zh_CN/process/6.Followthrough.rst b/Documentation/translations/zh_CN/process/6.Followthrough.rst index 2a127e737b6a..3d19c59ca6e4 100644 --- a/Documentation/translations/zh_CN/process/6.Followthrough.rst +++ b/Documentation/translations/zh_CN/process/6.Followthrough.rst @@ -49,6 +49,11 @@ 变。他们真的,几乎毫无例外地,致力于创造他们所能做到的最好的内核;他们并 没有试图给雇主的竞争对手造成不适。 + - 请准备好应对看似“愚蠢”的代码风格修改请求,以及将部分代码拆分到内核 + 共享模块的要求。维护者的职责之一是保持整体风格的一致性。有时这意味着, + 你在驱动中为解决某一问题而采用的巧妙取巧方案,实际上需要被提炼为通用的 + 内核特性,以便未来复用。 + 所有这些归根结底就是,当审阅者向您发送评论时,您需要注意他们正在进行的技术 评论。不要让他们的表达方式或你自己的骄傲阻止此事。当你在一个补丁上得到评论 时,花点时间去理解评论人想说什么。如果可能的话,请修复审阅者要求您修复的内 diff --git a/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst b/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst index 57beca02181c..92cc06dd5f4e 100644 --- a/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst +++ b/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst @@ -113,6 +113,8 @@ Git提供了一些强大的工具,可以让您重写开发历史。一个不 更改。在这方面 git request-pull 命令非常有用;它将按照其他开发人员所期望的 格式化请求,并检查以确保您已记得将这些更改推送到公共服务器。 +.. _cn_development_advancedtopics_reviews: + 审阅补丁 -------- @@ -126,8 +128,20 @@ Git提供了一些强大的工具,可以让您重写开发历史。一个不 的建议是:把审阅评论当成问题而不是批评。询问“在这条路径中如何释放锁?” 总是比说“这里的锁是错误的”更好。 +当出现分歧时,另一个有用的技巧是邀请他人参与讨论。如果交流数次后讨论陷入僵局, +可征求其他评审者或维护者的意见。通常,与某一评审者意见一致的人往往会保持沉默, +除非被主动询问。众人意见会产生成倍的影响力。 + 不同的开发人员将从不同的角度审查代码。部分人会主要关注代码风格以及代码行是 否有尾随空格。其他人会主要关注补丁作为一个整体实现的变更是否对内核有好处。 同时也有人会检查是否存在锁问题、堆栈使用过度、可能的安全问题、在其他地方 发现的代码重复、足够的文档、对性能的不利影响、用户空间ABI更改等。所有类型 的检查,只要它们能引导更好的代码进入内核,都是受欢迎和值得的。 + +使用诸如 ``Reviewed-by`` 这类特定标签并无严格要求。事实上,即便提供了标签,也 +更鼓励用平实的英文撰写评审意见,因为这样的内容信息量更大,例如,“我查看了此次 +提交中 A、B、C 等方面的内容,认为没有问题。”显然,以某种形式提供评审信息或回复 +是必要的,否则维护者将完全无法知晓评审者是否已查看过补丁! + +最后但同样重要的是,补丁评审可能会变成一个聚焦于指出问题的负面过程。请偶尔给予 +称赞,尤其是对新手贡献者! diff --git a/Documentation/translations/zh_CN/security/credentials.rst b/Documentation/translations/zh_CN/security/credentials.rst index 91c353dfb622..88fcd9152ffe 100644 --- a/Documentation/translations/zh_CN/security/credentials.rst +++ b/Documentation/translations/zh_CN/security/credentials.rst @@ -475,5 +475,5 @@ const指针上操作,因此不需要进行类型转换,但需要临时放弃 如 ``vfs_mkdir()`` 来实现。以下是一些进行此操作的位置: * ``sys_faccessat()``. - * ``do_coredump()``. + * ``vfs_coredump()``. * nfs4recover.c. diff --git a/Documentation/translations/zh_CN/security/self-protection.rst b/Documentation/translations/zh_CN/security/self-protection.rst index 3c8a68b1e1be..93de9cee5c1a 100644 --- a/Documentation/translations/zh_CN/security/self-protection.rst +++ b/Documentation/translations/zh_CN/security/self-protection.rst @@ -259,7 +259,7 @@ KALLSYSM,则会直接打印原始地址。 -------- 在释放内存时,最好对内存内容进行清除处理,以防止攻击者重用内存中以前 -的内容。例如,在系统调用返回时清除堆栈(CONFIG_GCC_PLUGIN_STACKLEAK), +的内容。例如,在系统调用返回时清除堆栈(CONFIG_KSTACK_ERASE), 在释放堆内容是清除其内容。这有助于防止许多未初始化变量攻击、堆栈内容 泄露、堆内容泄露以及使用后释放攻击(user-after-free)。 diff --git a/Documentation/translations/zh_CN/staging/index.rst b/Documentation/translations/zh_CN/staging/index.rst index bb55c81c84a3..6d68fabce175 100644 --- a/Documentation/translations/zh_CN/staging/index.rst +++ b/Documentation/translations/zh_CN/staging/index.rst @@ -13,6 +13,7 @@ .. toctree:: :maxdepth: 2 + speculation xz TODOList: @@ -21,6 +22,5 @@ TODOList: * lzo * remoteproc * rpmsg -* speculation * static-keys * tee diff --git a/Documentation/translations/zh_CN/staging/speculation.rst b/Documentation/translations/zh_CN/staging/speculation.rst new file mode 100644 index 000000000000..c36d33f67897 --- /dev/null +++ b/Documentation/translations/zh_CN/staging/speculation.rst @@ -0,0 +1,85 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/staging/speculation.rst + +:翻译: + + 崔巍 Cui Wei + +======== +推测执行 +======== + +本文档解释了推测执行的潜在影响,以及如何使用通用API来减轻不良影响。 + +------------------------------------------------------------------------------ + +为提高性能并减少平均延迟,许多现代处理器都采用分支预测等推测执行技术,执行结果 +可能在后续阶段被丢弃。 + +通常情况下,我们无法从架构状态(如寄存器内容)观察到推测执行。然而,在某些情况 +下从微架构状态观察其影响是可能的,例如数据是否存在于缓存中。这种状态可能会形成 +侧信道,通过观察侧信道可以提取秘密信息。 + +例如,在分支预测存在的情况下,边界检查可能被推测执行的代码忽略。考虑以下代码:: + + int load_array(int *array, unsigned int index) + { + if (index >= MAX_ARRAY_ELEMS) + return 0; + else + return array[index]; + } + +在arm64上,可以编译成如下汇编序列:: + + CMP , #MAX_ARRAY_ELEMS + B.LT less + MOV , #0 + RET + less: + LDR , [, ] + RET + +处理器有可能误预测条件分支,并推测性装载array[index],即使index >= MAX_ARRAY_ELEMS。 +这个值随后会被丢弃,但推测的装载可能会影响微架构状态,随后可被测量到。 + +涉及多个依赖内存访问的更复杂序列可能会导致敏感信息泄露。以前面的示例为基础,考虑 +以下代码:: + + int load_dependent_arrays(int *arr1, int *arr2, int index) + { + int val1, val2, + + val1 = load_array(arr1, index); + val2 = load_array(arr2, val1); + + return val2; + } + +根据推测,对load_array()的第一次调用可能会返回一个越界地址的值,而第二次调用将影响 +依赖于该值的微架构状态。这可能会提供一个任意读取原语。 + +缓解推测执行侧信道 +================== + +内核提供了一个通用API以确保即使在推测情况下也能遵守边界检查。受推测执行侧信道影响 +的架构应当实现这些原语。 + +中的array_index_nospec()辅助函数可用于防止信息通过侧信道泄漏。 + +调用array_index_nospec(index, size)将返回一个经过净化的索引值,即使在CPU推测执行 +条件下,该值也会被严格限制在[0, size)范围内。 + +这可以用来保护前面的load_array()示例:: + + int load_array(int *array, unsigned int index) + { + if (index >= MAX_ARRAY_ELEMS) + return 0; + else { + index = array_index_nospec(index, MAX_ARRAY_ELEMS); + return array[index]; + } + } diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 1998dc146c56..5f90af1fb573 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -874,7 +874,7 @@ where uvc-gadget is this program: with these patches: - http://www.spinics.net/lists/linux-usb/msg99220.html + https://lore.kernel.org/r/1386675637-18243-1-git-send-email-r.baldyga@samsung.com/ host:: diff --git a/Documentation/usb/gadget_configfs.rst b/Documentation/usb/gadget_configfs.rst index 868e118a2644..ada57c0e34aa 100644 --- a/Documentation/usb/gadget_configfs.rst +++ b/Documentation/usb/gadget_configfs.rst @@ -92,7 +92,7 @@ Then the strings can be specified:: Further custom string descriptors can be created as directories within the language's directory, with the string text being written to the "s" attribute -within the string's directory: +within the string's directory:: $ mkdir strings/0x409/xu.0 $ echo > strings/0x409/xu.0/s @@ -104,9 +104,9 @@ string descriptors to associate those strings with class descriptors. ------------------------------ Each gadget will consist of a number of configurations, their corresponding -directories must be created: +directories must be created:: -$ mkdir configs/. + $ mkdir configs/. where can be any string which is legal in a filesystem and the is the configuration's number, e.g.:: @@ -246,7 +246,7 @@ a symlink to a function being removed from the configuration, e.g.:: ... ... -Remove strings directories in configurations: +Remove strings directories in configurations:: $ rmdir configs/./strings/ @@ -270,7 +270,7 @@ e.g.:: ... ... -Remove functions (function modules are not unloaded, though): +Remove functions (function modules are not unloaded, though):: $ rmdir functions/. @@ -369,18 +369,18 @@ For more information on configfs please see The concepts described above translate to USB gadgets like this: 1. A gadget has its config group, which has some attributes (idVendor, -idProduct etc) and default sub-groups (configs, functions, strings). -Writing to the attributes causes the information to be stored in -appropriate locations. In the configs, functions and strings sub-groups -a user can create their sub-groups to represent configurations, functions, -and groups of strings in a given language. + idProduct etc) and default sub-groups (configs, functions, strings). + Writing to the attributes causes the information to be stored in appropriate + locations. In the configs, functions and strings sub-groups a user can + create their sub-groups to represent configurations, functions, and groups + of strings in a given language. 2. The user creates configurations and functions, in the configurations -creates symbolic links to functions. This information is used when the -gadget's UDC attribute is written to, which means binding the gadget -to the UDC. The code in drivers/usb/gadget/configfs.c iterates over -all configurations, and in each configuration it iterates over all -functions and binds them. This way the whole gadget is bound. + creates symbolic links to functions. This information is used when the + gadget's UDC attribute is written to, which means binding the gadget to the + UDC. The code in drivers/usb/gadget/configfs.c iterates over all + configurations, and in each configuration it iterates over all functions and + binds them. This way the whole gadget is bound. 3. The file drivers/usb/gadget/configfs.c contains code for @@ -388,13 +388,12 @@ functions and binds them. This way the whole gadget is bound. - gadget's default groups (configs, functions, strings) - associating functions with configurations (symlinks) -4. Each USB function naturally has its own view of what it wants -configured, so config_groups for particular functions are defined -in the functions implementation files drivers/usb/gadget/f_*.c. +4. Each USB function naturally has its own view of what it wants configured, so + config_groups for particular functions are defined in the functions + implementation files drivers/usb/gadget/f_*.c. 5. Function's code is written in such a way that it uses - -usb_get_function_instance(), which, in turn, calls request_module. -So, provided that modprobe works, modules for particular functions -are loaded automatically. Please note that the converse is not true: -after a gadget is disabled and torn down, the modules remain loaded. + usb_get_function_instance(), which, in turn, calls request_module. So, + provided that modprobe works, modules for particular functions are loaded + automatically. Please note that the converse is not true: after a gadget is + disabled and torn down, the modules remain loaded. diff --git a/Documentation/userspace-api/fwctl/fwctl.rst b/Documentation/userspace-api/fwctl/fwctl.rst index fdcfe418a83f..a74eab8d14c6 100644 --- a/Documentation/userspace-api/fwctl/fwctl.rst +++ b/Documentation/userspace-api/fwctl/fwctl.rst @@ -54,7 +54,7 @@ operated by the block layer but also comes with a set of RPCs to administer the construction of drives within the HW RAID. In the past when devices were more single function, individual subsystems would -grow different approaches to solving some of these common problems. For instance +grow different approaches to solving some of these common problems. For instance, monitoring device health, manipulating its FLASH, debugging the FW, provisioning, all have various unique interfaces across the kernel. @@ -87,7 +87,7 @@ device today may broadly have several function-level scopes: 3. Multiple VM functions tightly scoped within the VM The device may create a logical parent/child relationship between these scopes. -For instance a child VM's FW may be within the scope of the hypervisor FW. It is +For instance, a child VM's FW may be within the scope of the hypervisor FW. It is quite common in the VFIO world that the hypervisor environment has a complex provisioning/profiling/configuration responsibility for the function VFIO assigns to the VM. @@ -105,19 +105,19 @@ some general scopes of action (see enum fwctl_rpc_scope): 3. Write access to function & child debug information strictly compatible with the principles of kernel lockdown and kernel integrity protection. Triggers - a kernel Taint. + a kernel taint. - 4. Full debug device access. Triggers a kernel Taint, requires CAP_SYS_RAWIO. + 4. Full debug device access. Triggers a kernel taint, requires CAP_SYS_RAWIO. User space will provide a scope label on each RPC and the kernel must enforce the above CAPs and taints based on that scope. A combination of kernel and FW can enforce that RPCs are placed in the correct scope by user space. -Denied behavior ---------------- +Disallowed behavior +------------------- There are many things this interface must not allow user space to do (without a -Taint or CAP), broadly derived from the principles of kernel lockdown. Some +taint or CAP), broadly derived from the principles of kernel lockdown. Some examples: 1. DMA to/from arbitrary memory, hang the system, compromise FW integrity with @@ -138,8 +138,8 @@ examples: fwctl is not a replacement for device direct access subsystems like uacce or VFIO. -Operations exposed through fwctl's non-taining interfaces should be fully -sharable with other users of the device. For instance exposing a RPC through +Operations exposed through fwctl's non-tainting interfaces should be fully +sharable with other users of the device. For instance, exposing a RPC through fwctl should never prevent a kernel subsystem from also concurrently using that same RPC or hardware unit down the road. In such cases fwctl will be less important than proper kernel subsystems that eventually emerge. Mistakes in this @@ -225,12 +225,12 @@ subsystems. Each device type must be mindful of Linux's philosophy for stable ABI. The FW RPC interface does not have to meet a strictly stable ABI, but it does need to -meet an expectation that userspace tools that are deployed and in significant +meet an expectation that user space tools that are deployed and in significant use don't needlessly break. FW upgrade and kernel upgrade should keep widely deployed tooling working. Development and debugging focused RPCs under more permissive scopes can have -less stabilitiy if the tools using them are only run under exceptional +less stability if the tools using them are only run under exceptional circumstances and not for every day use of the device. Debugging tools may even require exact version matching as they may require something similar to DWARF debug information from the FW binary. @@ -261,7 +261,7 @@ Some examples: - HW RAID controllers. This includes RPCs to do things like compose drives into a RAID volume, configure RAID parameters, monitor the HW and more. - - Baseboard managers. RPCs for configuring settings in the device and more + - Baseboard managers. RPCs for configuring settings in the device and more. - NVMe vendor command capsules. nvme-cli provides access to some monitoring functions that different products have defined, but more exist. @@ -269,15 +269,15 @@ Some examples: - CXL also has a NVMe-like vendor command system. - DRM allows user space drivers to send commands to the device via kernel - mediation + mediation. - RDMA allows user space drivers to directly push commands to the device - without kernel involvement + without kernel involvement. - Various “raw” APIs, raw HID (SDL2), raw USB, NVMe Generic Interface, etc. The first 4 are examples of areas that fwctl intends to cover. The latter three -are examples of denied behavior as they fully overlap with the primary purpose +are examples of disallowed behavior as they fully overlap with the primary purpose of a kernel subsystem. Some key lessons learned from these past efforts are the importance of having a diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index bc91756bde73..406a9f4d0869 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -10,12 +10,14 @@ Michael Elizabeth Chastain If you are adding new ioctl's to the kernel, you should use the _IO macros defined in : - ====== == ============================================ - _IO an ioctl with no parameters - _IOW an ioctl with write parameters (copy_from_user) - _IOR an ioctl with read parameters (copy_to_user) - _IOWR an ioctl with both write and read parameters. - ====== == ============================================ + ====== =========================== + macro parameters + ====== =========================== + _IO none + _IOW write (read from userspace) + _IOR read (write to userpace) + _IOWR write and read + ====== =========================== 'Write' and 'read' are from the user's point of view, just like the system calls 'write' and 'read'. For example, a SET_FOO ioctl would @@ -23,9 +25,9 @@ be _IOW, although the kernel would actually read data from user space; a GET_FOO ioctl would be _IOR, although the kernel would actually write data to user space. -The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter -or number from the table below. Because of the large number of drivers, -many drivers share a partial letter with other drivers. +The first argument to the macros is an identifying letter or number from +the table below. Because of the large number of drivers, many drivers +share a partial letter with other drivers. If you are writing a driver for a new device and need a letter, pick an unused block with enough room for expansion: 32 to 256 ioctl commands @@ -33,12 +35,14 @@ should suffice. You can register the block by patching this file and submitting the patch through :doc:`usual patch submission process `. -The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number -to distinguish ioctls from each other. The third argument to _IOW, -_IOR, or _IOWR is the type of the data going into the kernel or coming -out of the kernel (e.g. 'int' or 'struct foo'). NOTE! Do NOT use -sizeof(arg) as the third argument as this results in your ioctl thinking -it passes an argument of type size_t. +The second argument is a sequence number to distinguish ioctls from each +other. The third argument (not applicable to _IO) is the type of the data +going into the kernel or coming out of the kernel (e.g. 'int' or +'struct foo'). + +.. note:: + Do NOT use sizeof(arg) as the third argument as this results in your + ioctl thinking it passes an argument of type size_t. Some devices use their major number as the identifier; this is OK, as long as it is unique. Some devices are irregular and don't follow any @@ -51,7 +55,7 @@ Following this convention is good because: error rather than some unexpected behaviour. (2) The 'strace' build procedure automatically finds ioctl numbers - defined with _IO, _IOW, _IOR, or _IOWR. + defined with the macros. (3) 'strace' can decode numbers back into useful names when the numbers are unique. @@ -65,345 +69,344 @@ Following this convention is good because: This table lists ioctls visible from userland, excluding ones from drivers/staging/. -==== ===== ======================================================= ================================================================ -Code Seq# Include File Comments +==== ===== ========================================================= ================================================================ +Code Seq# Include File Comments (hex) -==== ===== ======================================================= ================================================================ -0x00 00-1F linux/fs.h conflict! -0x00 00-1F scsi/scsi_ioctl.h conflict! -0x00 00-1F linux/fb.h conflict! -0x00 00-1F linux/wavefront.h conflict! +==== ===== ========================================================= ================================================================ +0x00 00-1F linux/fs.h conflict! +0x00 00-1F scsi/scsi_ioctl.h conflict! +0x00 00-1F linux/fb.h conflict! +0x00 00-1F linux/wavefront.h conflict! 0x02 all linux/fd.h 0x03 all linux/hdreg.h -0x04 D2-DC linux/umsdos_fs.h Dead since 2.6.11, but don't reuse these. +0x04 D2-DC linux/umsdos_fs.h Dead since 2.6.11, but don't reuse these. 0x06 all linux/lp.h 0x07 9F-D0 linux/vmw_vmci_defs.h, uapi/linux/vm_sockets.h 0x09 all linux/raid/md_u.h 0x10 00-0F drivers/char/s390/vmcp.h 0x10 10-1F arch/s390/include/uapi/sclp_ctl.h 0x10 20-2F arch/s390/include/uapi/asm/hypfs.h -0x12 all linux/fs.h BLK* ioctls +0x12 all linux/fs.h BLK* ioctls linux/blkpg.h linux/blkzoned.h linux/blk-crypto.h -0x15 all linux/fs.h FS_IOC_* ioctls -0x1b all InfiniBand Subsystem - +0x15 all linux/fs.h FS_IOC_* ioctls +0x1b all InfiniBand Subsystem + 0x20 all drivers/cdrom/cm206.h 0x22 all scsi/sg.h -0x3E 00-0F linux/counter.h +0x3E 00-0F linux/counter.h '!' 00-1F uapi/linux/seccomp.h -'#' 00-3F IEEE 1394 Subsystem - Block for the entire subsystem +'#' 00-3F IEEE 1394 Subsystem + Block for the entire subsystem '$' 00-0F linux/perf_counter.h, linux/perf_event.h -'%' 00-0F include/uapi/linux/stm.h System Trace Module subsystem - +'%' 00-0F include/uapi/linux/stm.h System Trace Module subsystem + '&' 00-07 drivers/firewire/nosy-user.h -'*' 00-1F uapi/linux/user_events.h User Events Subsystem - -'1' 00-1F linux/timepps.h PPS kit from Ulrich Windl - +'*' 00-1F uapi/linux/user_events.h User Events Subsystem + +'1' 00-1F linux/timepps.h PPS kit from Ulrich Windl + '2' 01-04 linux/i2o.h -'3' 00-0F drivers/s390/char/raw3270.h conflict! -'3' 00-1F linux/suspend_ioctls.h, conflict! +'3' 00-0F drivers/s390/char/raw3270.h conflict! +'3' 00-1F linux/suspend_ioctls.h, conflict! kernel/power/user.c -'8' all SNP8023 advanced NIC card - +'8' all SNP8023 advanced NIC card + ';' 64-7F linux/vfio.h ';' 80-FF linux/iommufd.h -'=' 00-3f uapi/linux/ptp_clock.h -'@' 00-0F linux/radeonfb.h conflict! -'@' 00-0F drivers/video/aty/aty128fb.c conflict! -'A' 00-1F linux/apm_bios.h conflict! -'A' 00-0F linux/agpgart.h, conflict! +'=' 00-3f uapi/linux/ptp_clock.h +'@' 00-0F linux/radeonfb.h conflict! +'@' 00-0F drivers/video/aty/aty128fb.c conflict! +'A' 00-1F linux/apm_bios.h conflict! +'A' 00-0F linux/agpgart.h, conflict! drivers/char/agp/compat_ioctl.h -'A' 00-7F sound/asound.h conflict! -'B' 00-1F linux/cciss_ioctl.h conflict! -'B' 00-0F include/linux/pmu.h conflict! -'B' C0-FF advanced bbus -'B' 00-0F xen/xenbus_dev.h conflict! -'C' all linux/soundcard.h conflict! -'C' 01-2F linux/capi.h conflict! -'C' F0-FF drivers/net/wan/cosa.h conflict! +'A' 00-7F sound/asound.h conflict! +'B' 00-1F linux/cciss_ioctl.h conflict! +'B' 00-0F include/linux/pmu.h conflict! +'B' C0-FF advanced bbus +'B' 00-0F xen/xenbus_dev.h conflict! +'C' all linux/soundcard.h conflict! +'C' 01-2F linux/capi.h conflict! +'C' F0-FF drivers/net/wan/cosa.h conflict! 'D' all arch/s390/include/asm/dasd.h -'D' 40-5F drivers/scsi/dpt/dtpi_ioctl.h Dead since 2022 +'D' 40-5F drivers/scsi/dpt/dtpi_ioctl.h Dead since 2022 'D' 05 drivers/scsi/pmcraid.h -'E' all linux/input.h conflict! -'E' 00-0F xen/evtchn.h conflict! -'F' all linux/fb.h conflict! -'F' 01-02 drivers/scsi/pmcraid.h conflict! -'F' 20 drivers/video/fsl-diu-fb.h conflict! -'F' 20 linux/ivtvfb.h conflict! -'F' 20 linux/matroxfb.h conflict! -'F' 20 drivers/video/aty/atyfb_base.c conflict! -'F' 00-0F video/da8xx-fb.h conflict! -'F' 80-8F linux/arcfb.h conflict! -'F' DD video/sstfb.h conflict! -'G' 00-3F drivers/misc/sgi-gru/grulib.h conflict! -'G' 00-0F xen/gntalloc.h, xen/gntdev.h conflict! -'H' 00-7F linux/hiddev.h conflict! -'H' 00-0F linux/hidraw.h conflict! -'H' 01 linux/mei.h conflict! -'H' 02 linux/mei.h conflict! -'H' 03 linux/mei.h conflict! -'H' 00-0F sound/asound.h conflict! -'H' 20-40 sound/asound_fm.h conflict! -'H' 80-8F sound/sfnt_info.h conflict! -'H' 10-8F sound/emu10k1.h conflict! -'H' 10-1F sound/sb16_csp.h conflict! -'H' 10-1F sound/hda_hwdep.h conflict! -'H' 40-4F sound/hdspm.h conflict! -'H' 40-4F sound/hdsp.h conflict! +'E' all linux/input.h conflict! +'E' 00-0F xen/evtchn.h conflict! +'F' all linux/fb.h conflict! +'F' 01-02 drivers/scsi/pmcraid.h conflict! +'F' 20 drivers/video/fsl-diu-fb.h conflict! +'F' 20 linux/ivtvfb.h conflict! +'F' 20 linux/matroxfb.h conflict! +'F' 20 drivers/video/aty/atyfb_base.c conflict! +'F' 00-0F video/da8xx-fb.h conflict! +'F' 80-8F linux/arcfb.h conflict! +'F' DD video/sstfb.h conflict! +'G' 00-3F drivers/misc/sgi-gru/grulib.h conflict! +'G' 00-0F xen/gntalloc.h, xen/gntdev.h conflict! +'H' 00-7F linux/hiddev.h conflict! +'H' 00-0F linux/hidraw.h conflict! +'H' 01 linux/mei.h conflict! +'H' 02 linux/mei.h conflict! +'H' 03 linux/mei.h conflict! +'H' 00-0F sound/asound.h conflict! +'H' 20-40 sound/asound_fm.h conflict! +'H' 80-8F sound/sfnt_info.h conflict! +'H' 10-8F sound/emu10k1.h conflict! +'H' 10-1F sound/sb16_csp.h conflict! +'H' 10-1F sound/hda_hwdep.h conflict! +'H' 40-4F sound/hdspm.h conflict! +'H' 40-4F sound/hdsp.h conflict! 'H' 90 sound/usb/usx2y/usb_stream.h -'H' 00-0F uapi/misc/habanalabs.h conflict! +'H' 00-0F uapi/misc/habanalabs.h conflict! 'H' A0 uapi/linux/usb/cdc-wdm.h -'H' C0-F0 net/bluetooth/hci.h conflict! -'H' C0-DF net/bluetooth/hidp/hidp.h conflict! -'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! -'H' C0-DF net/bluetooth/bnep/bnep.h conflict! -'H' F1 linux/hid-roccat.h +'H' C0-F0 net/bluetooth/hci.h conflict! +'H' C0-DF net/bluetooth/hidp/hidp.h conflict! +'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! +'H' C0-DF net/bluetooth/bnep/bnep.h conflict! +'H' F1 linux/hid-roccat.h 'H' F8-FA sound/firewire.h -'I' all linux/isdn.h conflict! -'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! -'I' 40-4F linux/mISDNif.h conflict! +'I' all linux/isdn.h conflict! +'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! +'I' 40-4F linux/mISDNif.h conflict! 'K' all linux/kd.h -'L' 00-1F linux/loop.h conflict! -'L' 10-1F drivers/scsi/mpt3sas/mpt3sas_ctl.h conflict! -'L' E0-FF linux/ppdd.h encrypted disk device driver - -'M' all linux/soundcard.h conflict! -'M' 01-16 mtd/mtd-abi.h conflict! +'L' 00-1F linux/loop.h conflict! +'L' 10-1F drivers/scsi/mpt3sas/mpt3sas_ctl.h conflict! +'L' E0-FF linux/ppdd.h encrypted disk device driver + +'M' all linux/soundcard.h conflict! +'M' 01-16 mtd/mtd-abi.h conflict! and drivers/mtd/mtdchar.c 'M' 01-03 drivers/scsi/megaraid/megaraid_sas.h -'M' 00-0F drivers/video/fsl-diu-fb.h conflict! +'M' 00-0F drivers/video/fsl-diu-fb.h conflict! 'N' 00-1F drivers/usb/scanner.h 'N' 40-7F drivers/block/nvme.c -'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives - -'O' 00-06 mtd/ubi-user.h UBI -'P' all linux/soundcard.h conflict! -'P' 60-6F sound/sscape_ioctl.h conflict! -'P' 00-0F drivers/usb/class/usblp.c conflict! -'P' 01-09 drivers/misc/pci_endpoint_test.c conflict! -'P' 00-0F xen/privcmd.h conflict! -'P' 00-05 linux/tps6594_pfsm.h conflict! +'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives + +'O' 00-06 mtd/ubi-user.h UBI +'P' all linux/soundcard.h conflict! +'P' 60-6F sound/sscape_ioctl.h conflict! +'P' 00-0F drivers/usb/class/usblp.c conflict! +'P' 01-09 drivers/misc/pci_endpoint_test.c conflict! +'P' 00-0F xen/privcmd.h conflict! +'P' 00-05 linux/tps6594_pfsm.h conflict! 'Q' all linux/soundcard.h -'R' 00-1F linux/random.h conflict! -'R' 01 linux/rfkill.h conflict! +'R' 00-1F linux/random.h conflict! +'R' 01 linux/rfkill.h conflict! 'R' 20-2F linux/trace_mmap.h 'R' C0-DF net/bluetooth/rfcomm.h 'R' E0 uapi/linux/fsl_mc.h -'S' all linux/cdrom.h conflict! -'S' 80-81 scsi/scsi_ioctl.h conflict! -'S' 82-FF scsi/scsi.h conflict! -'S' 00-7F sound/asequencer.h conflict! -'T' all linux/soundcard.h conflict! -'T' 00-AF sound/asound.h conflict! -'T' all arch/x86/include/asm/ioctls.h conflict! -'T' C0-DF linux/if_tun.h conflict! -'U' all sound/asound.h conflict! -'U' 00-CF linux/uinput.h conflict! +'S' all linux/cdrom.h conflict! +'S' 80-81 scsi/scsi_ioctl.h conflict! +'S' 82-FF scsi/scsi.h conflict! +'S' 00-7F sound/asequencer.h conflict! +'T' all linux/soundcard.h conflict! +'T' 00-AF sound/asound.h conflict! +'T' all arch/x86/include/asm/ioctls.h conflict! +'T' C0-DF linux/if_tun.h conflict! +'U' all sound/asound.h conflict! +'U' 00-CF linux/uinput.h conflict! 'U' 00-EF linux/usbdevice_fs.h 'U' C0-CF drivers/bluetooth/hci_uart.h -'V' all linux/vt.h conflict! -'V' all linux/videodev2.h conflict! -'V' C0 linux/ivtvfb.h conflict! -'V' C0 linux/ivtv.h conflict! -'V' C0 media/si4713.h conflict! -'W' 00-1F linux/watchdog.h conflict! -'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) -'W' 00-3F sound/asound.h conflict! +'V' all linux/vt.h conflict! +'V' all linux/videodev2.h conflict! +'V' C0 linux/ivtvfb.h conflict! +'V' C0 linux/ivtv.h conflict! +'V' C0 media/si4713.h conflict! +'W' 00-1F linux/watchdog.h conflict! +'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) +'W' 00-3F sound/asound.h conflict! 'W' 40-5F drivers/pci/switch/switchtec.c 'W' 60-61 linux/watch_queue.h -'X' all fs/xfs/xfs_fs.h, conflict! +'X' all fs/xfs/xfs_fs.h, conflict! fs/xfs/linux-2.6/xfs_ioctl32.h, include/linux/falloc.h, linux/fs.h, -'X' all fs/ocfs2/ocfs_fs.h conflict! -'X' 01 linux/pktcdvd.h conflict! +'X' all fs/ocfs2/ocfs_fs.h conflict! 'Z' 14-15 drivers/message/fusion/mptctl.h -'[' 00-3F linux/usb/tmc.h USB Test and Measurement Devices - -'a' all linux/atm*.h, linux/sonet.h ATM on linux - -'a' 00-0F drivers/crypto/qat/qat_common/adf_cfg_common.h conflict! qat driver -'b' 00-FF conflict! bit3 vme host bridge - -'b' 00-0F linux/dma-buf.h conflict! -'c' 00-7F linux/comstats.h conflict! -'c' 00-7F linux/coda.h conflict! -'c' 00-1F linux/chio.h conflict! -'c' 80-9F arch/s390/include/asm/chsc.h conflict! +'[' 00-3F linux/usb/tmc.h USB Test and Measurement Devices + +'a' all linux/atm*.h, linux/sonet.h ATM on linux + +'a' 00-0F drivers/crypto/qat/qat_common/adf_cfg_common.h conflict! qat driver +'b' 00-FF conflict! bit3 vme host bridge + +'b' 00-0F linux/dma-buf.h conflict! +'c' 00-7F linux/comstats.h conflict! +'c' 00-7F linux/coda.h conflict! +'c' 00-1F linux/chio.h conflict! +'c' 80-9F arch/s390/include/asm/chsc.h conflict! 'c' A0-AF arch/x86/include/asm/msr.h conflict! -'d' 00-FF linux/char/drm/drm.h conflict! -'d' 02-40 pcmcia/ds.h conflict! +'d' 00-FF linux/char/drm/drm.h conflict! +'d' 02-40 pcmcia/ds.h conflict! 'd' F0-FF linux/digi1.h -'e' all linux/digi1.h conflict! -'f' 00-1F linux/ext2_fs.h conflict! -'f' 00-1F linux/ext3_fs.h conflict! -'f' 00-0F fs/jfs/jfs_dinode.h conflict! -'f' 00-0F fs/ext4/ext4.h conflict! -'f' 00-0F linux/fs.h conflict! -'f' 00-0F fs/ocfs2/ocfs2_fs.h conflict! +'e' all linux/digi1.h conflict! +'f' 00-1F linux/ext2_fs.h conflict! +'f' 00-1F linux/ext3_fs.h conflict! +'f' 00-0F fs/jfs/jfs_dinode.h conflict! +'f' 00-0F fs/ext4/ext4.h conflict! +'f' 00-0F linux/fs.h conflict! +'f' 00-0F fs/ocfs2/ocfs2_fs.h conflict! 'f' 13-27 linux/fscrypt.h 'f' 81-8F linux/fsverity.h 'g' 00-0F linux/usb/gadgetfs.h 'g' 20-2F linux/usb/g_printer.h -'h' 00-7F conflict! Charon filesystem - -'h' 00-1F linux/hpet.h conflict! +'h' 00-7F conflict! Charon filesystem + +'h' 00-1F linux/hpet.h conflict! 'h' 80-8F fs/hfsplus/ioctl.c -'i' 00-3F linux/i2o-dev.h conflict! -'i' 0B-1F linux/ipmi.h conflict! +'i' 00-3F linux/i2o-dev.h conflict! +'i' 0B-1F linux/ipmi.h conflict! 'i' 80-8F linux/i8k.h -'i' 90-9F `linux/iio/*.h` IIO +'i' 90-9F `linux/iio/*.h` IIO 'j' 00-3F linux/joystick.h -'k' 00-0F linux/spi/spidev.h conflict! -'k' 00-05 video/kyro.h conflict! -'k' 10-17 linux/hsi/hsi_char.h HSI character device -'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system - -'l' 40-7F linux/udf_fs_i.h in development: - -'m' 00-09 linux/mmtimer.h conflict! -'m' all linux/mtio.h conflict! -'m' all linux/soundcard.h conflict! -'m' all linux/synclink.h conflict! -'m' 00-19 drivers/message/fusion/mptctl.h conflict! -'m' 00 drivers/scsi/megaraid/megaraid_ioctl.h conflict! +'k' 00-0F linux/spi/spidev.h conflict! +'k' 00-05 video/kyro.h conflict! +'k' 10-17 linux/hsi/hsi_char.h HSI character device +'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system + +'l' 40-7F linux/udf_fs_i.h in development: + +'m' 00-09 linux/mmtimer.h conflict! +'m' all linux/mtio.h conflict! +'m' all linux/soundcard.h conflict! +'m' all linux/synclink.h conflict! +'m' 00-19 drivers/message/fusion/mptctl.h conflict! +'m' 00 drivers/scsi/megaraid/megaraid_ioctl.h conflict! 'n' 00-7F linux/ncp_fs.h and fs/ncpfs/ioctl.c -'n' 80-8F uapi/linux/nilfs2_api.h NILFS2 -'n' E0-FF linux/matroxfb.h matroxfb -'o' 00-1F fs/ocfs2/ocfs2_fs.h OCFS2 -'o' 00-03 mtd/ubi-user.h conflict! (OCFS2 and UBI overlaps) -'o' 40-41 mtd/ubi-user.h UBI -'o' 01-A1 `linux/dvb/*.h` DVB -'p' 00-0F linux/phantom.h conflict! (OpenHaptics needs this) -'p' 00-1F linux/rtc.h conflict! +'n' 80-8F uapi/linux/nilfs2_api.h NILFS2 +'n' E0-FF linux/matroxfb.h matroxfb +'o' 00-1F fs/ocfs2/ocfs2_fs.h OCFS2 +'o' 00-03 mtd/ubi-user.h conflict! (OCFS2 and UBI overlaps) +'o' 40-41 mtd/ubi-user.h UBI +'o' 01-A1 `linux/dvb/*.h` DVB +'p' 00-0F linux/phantom.h conflict! (OpenHaptics needs this) +'p' 00-1F linux/rtc.h conflict! 'p' 40-7F linux/nvram.h -'p' 80-9F linux/ppdev.h user-space parport - -'p' A1-A5 linux/pps.h LinuxPPS -'p' B1-B3 linux/pps_gen.h LinuxPPS - +'p' 80-9F linux/ppdev.h user-space parport + +'p' A1-A5 linux/pps.h LinuxPPS +'p' B1-B3 linux/pps_gen.h LinuxPPS + 'q' 00-1F linux/serio.h -'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK - linux/ixjuser.h +'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK + linux/ixjuser.h 'r' 00-1F linux/msdos_fs.h and fs/fat/dir.c 's' all linux/cdk.h 't' 00-7F linux/ppp-ioctl.h 't' 80-8F linux/isdn_ppp.h -'t' 90-91 linux/toshiba.h toshiba and toshiba_acpi SMM -'u' 00-1F linux/smb_fs.h gone -'u' 00-2F linux/ublk_cmd.h conflict! -'u' 20-3F linux/uvcvideo.h USB video class host driver -'u' 40-4f linux/udmabuf.h userspace dma-buf misc device -'v' 00-1F linux/ext2_fs.h conflict! -'v' 00-1F linux/fs.h conflict! -'v' 00-0F linux/sonypi.h conflict! -'v' 00-0F media/v4l2-subdev.h conflict! -'v' 20-27 arch/powerpc/include/uapi/asm/vas-api.h VAS API -'v' C0-FF linux/meye.h conflict! -'w' all CERN SCI driver -'y' 00-1F packet based user level communications - -'z' 00-3F CAN bus card conflict! - -'z' 40-7F CAN bus card conflict! - -'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! +'t' 90-91 linux/toshiba.h toshiba and toshiba_acpi SMM +'u' 00-1F linux/smb_fs.h gone +'u' 00-2F linux/ublk_cmd.h conflict! +'u' 20-3F linux/uvcvideo.h USB video class host driver +'u' 40-4f linux/udmabuf.h userspace dma-buf misc device +'v' 00-1F linux/ext2_fs.h conflict! +'v' 00-1F linux/fs.h conflict! +'v' 00-0F linux/sonypi.h conflict! +'v' 00-0F media/v4l2-subdev.h conflict! +'v' 20-27 arch/powerpc/include/uapi/asm/vas-api.h VAS API +'v' C0-FF linux/meye.h conflict! +'w' all CERN SCI driver +'y' 00-1F packet based user level communications + +'z' 00-3F CAN bus card conflict! + +'z' 40-7F CAN bus card conflict! + +'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! '|' 00-7F linux/media.h -'|' 80-9F samples/ Any sample and example drivers +'|' 80-9F samples/ Any sample and example drivers 0x80 00-1F linux/fb.h 0x81 00-1F linux/vduse.h 0x89 00-06 arch/x86/include/asm/sockios.h 0x89 0B-DF linux/sockios.h -0x89 E0-EF linux/sockios.h SIOCPROTOPRIVATE range -0x89 F0-FF linux/sockios.h SIOCDEVPRIVATE range +0x89 E0-EF linux/sockios.h SIOCPROTOPRIVATE range +0x89 F0-FF linux/sockios.h SIOCDEVPRIVATE range 0x8A 00-1F linux/eventpoll.h 0x8B all linux/wireless.h -0x8C 00-3F WiNRADiO driver - +0x8C 00-3F WiNRADiO driver + 0x90 00 drivers/cdrom/sbpcd.h 0x92 00-0F drivers/usb/mon/mon_bin.c 0x93 60-7F linux/auto_fs.h -0x94 all fs/btrfs/ioctl.h Btrfs filesystem - and linux/fs.h some lifted to vfs/generic -0x97 00-7F fs/ceph/ioctl.h Ceph file system -0x99 00-0F 537-Addinboard driver - +0x94 all fs/btrfs/ioctl.h Btrfs filesystem + and linux/fs.h some lifted to vfs/generic +0x97 00-7F fs/ceph/ioctl.h Ceph file system +0x99 00-0F 537-Addinboard driver + 0x9A 00-0F include/uapi/fwctl/fwctl.h -0xA0 all linux/sdp/sdp.h Industrial Device Project - -0xA1 0 linux/vtpm_proxy.h TPM Emulator Proxy Driver -0xA2 all uapi/linux/acrn.h ACRN hypervisor -0xA3 80-8F Port ACL in development: - +0xA0 all linux/sdp/sdp.h Industrial Device Project + +0xA1 0 linux/vtpm_proxy.h TPM Emulator Proxy Driver +0xA2 all uapi/linux/acrn.h ACRN hypervisor +0xA3 80-8F Port ACL in development: + 0xA3 90-9F linux/dtlk.h -0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem -0xA4 00-1F uapi/asm/sgx.h -0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator - -0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver - +0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem +0xA4 00-1F uapi/asm/sgx.h +0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator + +0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver + 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h -0xAD 00 Netfilter device in development: - -0xAE 00-1F linux/kvm.h Kernel-based Virtual Machine - -0xAE 40-FF linux/kvm.h Kernel-based Virtual Machine - -0xAE 20-3F linux/nitro_enclaves.h Nitro Enclaves -0xAF 00-1F linux/fsl_hypervisor.h Freescale hypervisor -0xB0 all RATIO devices in development: - -0xB1 00-1F PPPoX - -0xB2 00 arch/powerpc/include/uapi/asm/papr-vpd.h powerpc/pseries VPD API - -0xB2 01-02 arch/powerpc/include/uapi/asm/papr-sysparm.h powerpc/pseries system parameter API - -0xB2 03-05 arch/powerpc/include/uapi/asm/papr-indices.h powerpc/pseries indices API - -0xB2 06-07 arch/powerpc/include/uapi/asm/papr-platform-dump.h powerpc/pseries Platform Dump API - -0xB2 08 powerpc/include/uapi/asm/papr-physical-attestation.h powerpc/pseries Physical Attestation API - +0xAD 00 Netfilter device in development: + +0xAE 00-1F linux/kvm.h Kernel-based Virtual Machine + +0xAE 40-FF linux/kvm.h Kernel-based Virtual Machine + +0xAE 20-3F linux/nitro_enclaves.h Nitro Enclaves +0xAF 00-1F linux/fsl_hypervisor.h Freescale hypervisor +0xB0 all RATIO devices in development: + +0xB1 00-1F PPPoX + +0xB2 00 arch/powerpc/include/uapi/asm/papr-vpd.h powerpc/pseries VPD API + +0xB2 01-02 arch/powerpc/include/uapi/asm/papr-sysparm.h powerpc/pseries system parameter API + +0xB2 03-05 arch/powerpc/include/uapi/asm/papr-indices.h powerpc/pseries indices API + +0xB2 06-07 arch/powerpc/include/uapi/asm/papr-platform-dump.h powerpc/pseries Platform Dump API + +0xB2 08 arch/powerpc/include/uapi/asm/papr-physical-attestation.h powerpc/pseries Physical Attestation API + 0xB3 00 linux/mmc/ioctl.h -0xB4 00-0F linux/gpio.h -0xB5 00-0F uapi/linux/rpmsg.h +0xB4 00-0F linux/gpio.h +0xB5 00-0F uapi/linux/rpmsg.h 0xB6 all linux/fpga-dfl.h -0xB7 all uapi/linux/remoteproc_cdev.h -0xB7 all uapi/linux/nsfs.h > -0xB8 01-02 uapi/misc/mrvl_cn10k_dpi.h Marvell CN10K DPI driver -0xB8 all uapi/linux/mshv.h Microsoft Hyper-V /dev/mshv driver - +0xB7 all uapi/linux/remoteproc_cdev.h +0xB7 all uapi/linux/nsfs.h > +0xB8 01-02 uapi/misc/mrvl_cn10k_dpi.h Marvell CN10K DPI driver +0xB8 all uapi/linux/mshv.h Microsoft Hyper-V /dev/mshv driver + 0xC0 00-0F linux/usb/iowarrior.h -0xCA 00-0F uapi/misc/cxl.h Dead since 6.15 +0xCA 00-0F uapi/misc/cxl.h Dead since 6.15 0xCA 10-2F uapi/misc/ocxl.h -0xCA 80-BF uapi/scsi/cxlflash_ioctl.h Dead since 6.15 -0xCB 00-1F CBM serial IEC bus in development: - -0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver -0xCD 01 linux/reiserfs_fs.h Dead since 6.13 -0xCE 01-02 uapi/linux/cxl_mem.h Compute Express Link Memory Devices +0xCA 80-BF uapi/scsi/cxlflash_ioctl.h Dead since 6.15 +0xCB 00-1F CBM serial IEC bus in development: + +0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver +0xCD 01 linux/reiserfs_fs.h Dead since 6.13 +0xCE 01-02 uapi/linux/cxl_mem.h Compute Express Link Memory Devices 0xCF 02 fs/smb/client/cifs_ioctl.h 0xDB 00-0F drivers/char/mwave/mwavepub.h -0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ - +0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ + 0xE5 00-3F linux/fuse.h -0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver -0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry -0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) - -0xF6 all LTTng Linux Trace Toolkit Next Generation - -0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver - -0xF9 00-0F uapi/misc/amd-apml.h AMD side band system management interface driver - +0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver +0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry +0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) + +0xF6 all LTTng Linux Trace Toolkit Next Generation + +0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver + +0xF9 00-0F uapi/misc/amd-apml.h AMD side band system management interface driver + 0xFD all linux/dm-ioctl.h 0xFE all linux/isst_if.h -==== ===== ======================================================= ================================================================ +==== ===== ========================================================= ================================================================ diff --git a/Documentation/userspace-api/iommufd.rst b/Documentation/userspace-api/iommufd.rst index b0df15865dec..f1c4d21e5c5e 100644 --- a/Documentation/userspace-api/iommufd.rst +++ b/Documentation/userspace-api/iommufd.rst @@ -43,7 +43,7 @@ Following IOMMUFD objects are exposed to userspace: - IOMMUFD_OBJ_HWPT_PAGING, representing an actual hardware I/O page table (i.e. a single struct iommu_domain) managed by the iommu driver. "PAGING" - primarly indicates this type of HWPT should be linked to an IOAS. It also + primarily indicates this type of HWPT should be linked to an IOAS. It also indicates that it is backed by an iommu_domain with __IOMMU_DOMAIN_PAGING feature flag. This can be either an UNMANAGED stage-1 domain for a device running in the user space, or a nesting parent stage-2 domain for mappings @@ -76,7 +76,7 @@ Following IOMMUFD objects are exposed to userspace: * Security namespace for guest owned ID, e.g. guest-controlled cache tags * Non-device-affiliated event reporting, e.g. invalidation queue errors - * Access to a sharable nesting parent pagetable across physical IOMMUs + * Access to a shareable nesting parent pagetable across physical IOMMUs * Virtualization of various platforms IDs, e.g. RIDs and others * Delivery of paravirtualized invalidation * Direct assigned invalidation queues @@ -124,6 +124,17 @@ Following IOMMUFD objects are exposed to userspace: used to allocate a vEVENTQ. Each vIOMMU can support multiple types of vEVENTS, but is confined to one vEVENTQ per vEVENTQ type. +- IOMMUFD_OBJ_HW_QUEUE, representing a hardware accelerated queue, as a subset + of IOMMU's virtualization features, for the IOMMU HW to directly read or write + the virtual queue memory owned by a guest OS. This HW-acceleration feature can + allow VM to work with the IOMMU HW directly without a VM Exit, so as to reduce + overhead from the hypercalls. Along with the HW QUEUE object, iommufd provides + user space an mmap interface for VMM to mmap a physical MMIO region from the + host physical address space to the guest physical address space, allowing the + guest OS to directly control the allocated HW QUEUE. Thus, when allocating a + HW QUEUE, the VMM must request a pair of mmap info (offset/length) and pass in + exactly to an mmap syscall via its offset and length arguments. + All user-visible objects are destroyed via the IOMMU_DESTROY uAPI. The diagrams below show relationships between user-visible objects and kernel @@ -270,6 +281,7 @@ User visible objects are backed by following datastructures: - iommufd_viommu for IOMMUFD_OBJ_VIOMMU. - iommufd_vdevice for IOMMUFD_OBJ_VDEVICE. - iommufd_veventq for IOMMUFD_OBJ_VEVENTQ. +- iommufd_hw_queue for IOMMUFD_OBJ_HW_QUEUE. Several terminologies when looking at these datastructures: diff --git a/Documentation/userspace-api/media/cec/cec-pin-error-inj.rst b/Documentation/userspace-api/media/cec/cec-pin-error-inj.rst index 411d42a742f3..c02790319f3f 100644 --- a/Documentation/userspace-api/media/cec/cec-pin-error-inj.rst +++ b/Documentation/userspace-api/media/cec/cec-pin-error-inj.rst @@ -41,6 +41,9 @@ error injection status:: # rx-clear clear all rx error injections for # tx-clear clear all tx error injections for # + # RX error injection settings: + # rx-no-low-drive do not generate low-drive pulses + # # RX error injection: # [,] rx-nack NACK the message instead of sending an ACK # [,] rx-low-drive force a low-drive condition at this bit position @@ -53,6 +56,10 @@ error injection status:: # tx-custom-low-usecs define the 'low' time for the custom pulse # tx-custom-high-usecs define the 'high' time for the custom pulse # tx-custom-pulse transmit the custom pulse once the bus is idle + # tx-glitch-low-usecs define the 'low' time for the glitch pulse + # tx-glitch-high-usecs define the 'high' time for the glitch pulse + # tx-glitch-falling-edge send the glitch pulse after every falling edge + # tx-glitch-rising-edge send the glitch pulse after every rising edge # # TX error injection: # [,] tx-no-eom don't set the EOM bit @@ -193,6 +200,14 @@ Receive Messages This does not work if the remote CEC transmitter has logical address 0 ('TV') since that will always win. +``rx-no-low-drive`` + The receiver will ignore situations that would normally generate a + Low Drive pulse (3.6 ms). This is typically done if a spurious pulse is + detected when receiving a message, and it indicates to the transmitter that + the message has to be retransmitted since the receiver got confused. + Disabling this is useful to test how other CEC devices handle glitches + by ensuring we will not be the one that generates a Low Drive. + Transmit Messages ----------------- @@ -327,3 +342,30 @@ Custom Pulses ``tx-custom-pulse`` Transmit a single custom pulse as soon as the CEC bus is idle. + +Glitch Pulses +------------- + +This emulates what happens if the signal on the CEC line is seeing spurious +pulses. Typically this happens after the falling or rising edge where there +is a short voltage fluctuation that, if the CEC hardware doesn't do +deglitching, can be seen as a spurious pulse and can cause a Low Drive +condition or corrupt data. + +``tx-glitch-low-usecs `` + This defines the duration in microseconds that the glitch pulse pulls + the CEC line low. The default is 1 microsecond. The range is 0-100 + microseconds. If 0, then no glitch pulse will be generated. + +``tx-glitch-high-usecs `` + This defines the duration in microseconds that the glitch pulse keeps the + CEC line high (unless another CEC adapter pulls it low in that time). + The default is 1 microseconds. The range is 0-100 microseconds. If 0, then + no glitch pulse will be generated.The total period of the glitch pulse is + ``tx-custom-low-usecs + tx-custom-high-usecs``. + +``tx-glitch-falling-edge`` + Send the glitch pulse right after the falling edge. + +``tx-glitch-rising-edge`` + Send the glitch pulse right after the rising edge. diff --git a/Documentation/userspace-api/media/rc/rc-protos.rst b/Documentation/userspace-api/media/rc/rc-protos.rst index 2a888ff5829f..ec706290c921 100644 --- a/Documentation/userspace-api/media/rc/rc-protos.rst +++ b/Documentation/userspace-api/media/rc/rc-protos.rst @@ -449,6 +449,6 @@ the 32 bits. xbox-dvd (RC_PROTO_XBOX_DVD) ---------------------------- -This protocol is used by XBox DVD Remote, which was made for the original -XBox. There is no in-kernel decoder or encoder for this protocol. The usb +This protocol is used by Xbox DVD Remote, which was made for the original +Xbox. There is no in-kernel decoder or encoder for this protocol. The usb device decodes the protocol. There is a BPF decoder available in v4l-utils. diff --git a/Documentation/userspace-api/media/v4l/biblio.rst b/Documentation/userspace-api/media/v4l/biblio.rst index 35674eeae20d..856acf6a890c 100644 --- a/Documentation/userspace-api/media/v4l/biblio.rst +++ b/Documentation/userspace-api/media/v4l/biblio.rst @@ -150,7 +150,7 @@ ITU-T.81 ======== -:title: ITU-T Recommendation T.81 "Information Technology --- Digital Compression and Coding of Continous-Tone Still Images --- Requirements and Guidelines" +:title: ITU-T Recommendation T.81 "Information Technology --- Digital Compression and Coding of Continuous-Tone Still Images --- Requirements and Guidelines" :author: International Telecommunication Union (http://www.itu.int) diff --git a/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst b/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst index 42cdb0a9f786..96e0e85a822c 100644 --- a/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst +++ b/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst @@ -48,7 +48,7 @@ capabilities, and they may support :ref:`control` ioctls. The :ref:`video standard ` ioctls provide information vital to program a sliced VBI device, therefore must be supported. -.. _sliced-vbi-format-negotitation: +.. _sliced-vbi-format-negotiation: Sliced VBI Format Negotiation ============================= @@ -377,7 +377,7 @@ Sliced VBI Data in MPEG Streams If a device can produce an MPEG output stream, it may be capable of providing -:ref:`negotiated sliced VBI services ` +:ref:`negotiated sliced VBI services ` as data embedded in the MPEG stream. Users or applications control this sliced VBI data insertion with the :ref:`V4L2_CID_MPEG_STREAM_VBI_FMT ` diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst index b6cfc0e823d2..ccd439e9e0e3 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst @@ -64,17 +64,12 @@ FM_RX Control IDs broadcasts speech. If the transmitter doesn't make this distinction, then it will be set. -``V4L2_CID_TUNE_DEEMPHASIS`` - (enum) - -enum v4l2_deemphasis - +``V4L2_CID_TUNE_DEEMPHASIS (enum)`` Configures the de-emphasis value for reception. A de-emphasis filter is applied to the broadcast to accentuate the high audio frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_deemphasis defines possible - values for de-emphasis. Here they are: - - + or 75 microseconds is used. The enum v4l2_deemphasis defines possible + values for de-emphasis. They are: .. flat-table:: :header-rows: 0 diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst index 04c997c9a4c3..cb40cf4cc3ec 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst @@ -104,7 +104,7 @@ FM_TX Control IDs ``V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (integer)`` Sets the audio deviation limiter feature release time. Unit is in - useconds. Step and range are driver-specific. + microseconds. Step and range are driver-specific. ``V4L2_CID_AUDIO_LIMITER_DEVIATION (integer)`` Configures audio frequency deviation level in Hz. The range and step @@ -121,16 +121,16 @@ FM_TX Control IDs range and step are driver-specific. ``V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (integer)`` - Sets the threshold level for audio compression freature. It is a dB + Sets the threshold level for audio compression feature. It is a dB value. The range and step are driver-specific. ``V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (integer)`` - Sets the attack time for audio compression feature. It is a useconds + Sets the attack time for audio compression feature. It is a microseconds value. The range and step are driver-specific. ``V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (integer)`` Sets the release time for audio compression feature. It is a - useconds value. The range and step are driver-specific. + microseconds value. The range and step are driver-specific. ``V4L2_CID_PILOT_TONE_ENABLED (boolean)`` Enables or disables the pilot tone generation feature. @@ -143,17 +143,12 @@ FM_TX Control IDs Configures pilot tone frequency value. Unit is in Hz. The range and step are driver-specific. -``V4L2_CID_TUNE_PREEMPHASIS`` - (enum) - -enum v4l2_preemphasis - +``V4L2_CID_TUNE_PREEMPHASIS (enum)`` Configures the pre-emphasis value for broadcasting. A pre-emphasis filter is applied to the broadcast to accentuate the high audio frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_preemphasis defines possible - values for pre-emphasis. Here they are: - - + or 75 microseconds is used. The enum v4l2_preemphasis defines possible + values for pre-emphasis. They are: .. flat-table:: :header-rows: 0 @@ -166,8 +161,6 @@ enum v4l2_preemphasis - * - ``V4L2_PREEMPHASIS_75_uS`` - A pre-emphasis of 75 uS is used. - - ``V4L2_CID_TUNE_POWER_LEVEL (integer)`` Sets the output power level for signal transmission. Unit is in dBuV. Range and step are driver-specific. diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index bb6876cfc271..0de80328c36b 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -20,6 +20,7 @@ These formats are used for the :ref:`metadata` interface only. metafmt-pisp-fe metafmt-rkisp1 metafmt-uvc + metafmt-uvc-msxu-1-5 metafmt-vivid metafmt-vsp1-hgo metafmt-vsp1-hgt diff --git a/Documentation/userspace-api/media/v4l/metafmt-uvc-msxu-1-5.rst b/Documentation/userspace-api/media/v4l/metafmt-uvc-msxu-1-5.rst new file mode 100644 index 000000000000..dd1c3076df24 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-uvc-msxu-1-5.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _v4l2-meta-fmt-uvc-msxu-1-5: + +*********************************** +V4L2_META_FMT_UVC_MSXU_1_5 ('UVCM') +*********************************** + +Microsoft(R)'s UVC Payload Metadata. + + +Description +=========== + +V4L2_META_FMT_UVC_MSXU_1_5 buffers follow the metadata buffer layout of +V4L2_META_FMT_UVC with the only difference that it includes all the UVC +metadata in the `buffer[]` field, not just the first 2-12 bytes. + +The metadata format follows the specification from Microsoft(R) [1]. + +.. _1: + +[1] https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5 diff --git a/Documentation/userspace-api/media/v4l/metafmt-uvc.rst b/Documentation/userspace-api/media/v4l/metafmt-uvc.rst index 784346d14bbd..4c05e9e54683 100644 --- a/Documentation/userspace-api/media/v4l/metafmt-uvc.rst +++ b/Documentation/userspace-api/media/v4l/metafmt-uvc.rst @@ -44,7 +44,9 @@ Each individual block contains the following fields: them * - :cspan:`1` *The rest is an exact copy of the UVC payload header:* * - __u8 length; - - length of the rest of the block, including this field + - length of the rest of the block, including this field. Please note that + regardless of this value, for V4L2_META_FMT_UVC the kernel will never + copy more than 2-12 bytes. * - __u8 flags; - Flags, indicating presence of other standard UVC fields * - __u8 buf[]; diff --git a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst index ed3eb432967d..b5ca501842b0 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst @@ -19,6 +19,7 @@ orders. See also `the Wikipedia article on Bayer filter .. toctree:: :maxdepth: 1 + pixfmt-rawnn-cru pixfmt-srggb8 pixfmt-srggb8-pisp-comp pixfmt-srggb10 diff --git a/Documentation/userspace-api/media/v4l/pixfmt-rawnn-cru.rst b/Documentation/userspace-api/media/v4l/pixfmt-rawnn-cru.rst new file mode 100644 index 000000000000..db81f1cfe0f5 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/pixfmt-rawnn-cru.rst @@ -0,0 +1,143 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _v4l2-pix-fmt-raw-cru10: +.. _v4l2-pix-fmt-raw-cru12: +.. _v4l2-pix-fmt-raw-cru14: +.. _v4l2-pix-fmt-raw-cru20: + +********************************************************************************************************************************** +V4L2_PIX_FMT_RAW_CRU10 ('CR10'), V4L2_PIX_FMT_RAW_CRU12 ('CR12'), V4L2_PIX_FMT_RAW_CRU14 ('CR14'), V4L2_PIX_FMT_RAW_CRU20 ('CR20') +********************************************************************************************************************************** + +=============================================================== +Renesas RZ/V2H Camera Receiver Unit 64-bit packed pixel formats +=============================================================== + +| V4L2_PIX_FMT_RAW_CRU10 (CR10) +| V4L2_PIX_FMT_RAW_CRU12 (CR12) +| V4L2_PIX_FMT_RAW_CRU14 (CR14) +| V4L2_PIX_FMT_RAW_CRU20 (CR20) + +Description +=========== + +These pixel formats are some of the RAW outputs for the Camera Receiver Unit in +the Renesas RZ/V2H SoC. They are raw formats which pack pixels contiguously into +64-bit units, with the 4 or 8 most significant bits padded. + +**Byte Order** + +.. flat-table:: RAW formats + :header-rows: 2 + :stub-columns: 0 + :widths: 36 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + :fill-cells: + + * - :rspan:`1` Pixel Format Code + - :cspan:`63` Data organization + * - 63 + - 62 + - 61 + - 60 + - 59 + - 58 + - 57 + - 56 + - 55 + - 54 + - 53 + - 52 + - 51 + - 50 + - 49 + - 48 + - 47 + - 46 + - 45 + - 44 + - 43 + - 42 + - 41 + - 40 + - 39 + - 38 + - 37 + - 36 + - 35 + - 34 + - 33 + - 32 + - 31 + - 30 + - 29 + - 28 + - 27 + - 26 + - 25 + - 24 + - 23 + - 22 + - 21 + - 20 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - V4L2_PIX_FMT_RAW_CRU10 + - 0 + - 0 + - 0 + - 0 + - :cspan:`9` P5 + - :cspan:`9` P4 + - :cspan:`9` P3 + - :cspan:`9` P2 + - :cspan:`9` P1 + - :cspan:`9` P0 + * - V4L2_PIX_FMT_RAW_CRU12 + - 0 + - 0 + - 0 + - 0 + - :cspan:`11` P4 + - :cspan:`11` P3 + - :cspan:`11` P2 + - :cspan:`11` P1 + - :cspan:`11` P0 + * - V4L2_PIX_FMT_RAW_CRU14 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - :cspan:`13` P3 + - :cspan:`13` P2 + - :cspan:`13` P1 + - :cspan:`13` P0 + * - V4L2_PIX_FMT_RAW_CRU20 + - 0 + - 0 + - 0 + - 0 + - :cspan:`19` P2 + - :cspan:`19` P1 + - :cspan:`19` P0 diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst index 7c3810ff783c..8c03aedcc00e 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst @@ -6,7 +6,7 @@ .. _v4l2-pix-fmt-sgrbg12p: ******************************************************************************************************************************* -V4L2_PIX_FMT_SRGGB12P ('pRCC'), V4L2_PIX_FMT_SGRBG12P ('pgCC'), V4L2_PIX_FMT_SGBRG12P ('pGCC'), V4L2_PIX_FMT_SBGGR12P ('pBCC'), +V4L2_PIX_FMT_SRGGB12P ('pRCC'), V4L2_PIX_FMT_SGRBG12P ('pgCC'), V4L2_PIX_FMT_SGBRG12P ('pGCC'), V4L2_PIX_FMT_SBGGR12P ('pBCC') ******************************************************************************************************************************* @@ -20,7 +20,7 @@ Description These four pixel formats are packed raw sRGB / Bayer formats with 12 bits per colour. Every two consecutive samples are packed into three bytes. Each of the first two bytes contain the 8 high order bits of -the pixels, and the third byte contains the four least significants +the pixels, and the third byte contains the four least significant bits of each pixel, in the same order. Each n-pixel row contains n/2 green samples and n/2 blue or red diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst index 3572e42adb22..f4f53d7dbdeb 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst @@ -24,7 +24,7 @@ These four pixel formats are packed raw sRGB / Bayer formats with 14 bits per colour. Every four consecutive samples are packed into seven bytes. Each of the first four bytes contain the eight high order bits of the pixels, and the three following bytes contains the six least -significants bits of each pixel, in the same order. +significant bits of each pixel, in the same order. Each n-pixel row contains n/2 green samples and n/2 blue or red samples, with alternating green-red and green-blue rows. They are conventionally diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst index 7f013356118a..6613e188242a 100644 --- a/Documentation/userspace-api/sysfs-platform_profile.rst +++ b/Documentation/userspace-api/sysfs-platform_profile.rst @@ -18,9 +18,9 @@ API for selecting the platform profile of these automatic mechanisms. Note that this API is only for selecting the platform profile, it is NOT a goal of this API to allow monitoring the resulting performance characteristics. Monitoring performance is best done with device/vendor -specific tools such as e.g. turbostat. +specific tools, e.g. turbostat. -Specifically when selecting a high performance profile the actual achieved +Specifically, when selecting a high performance profile the actual achieved performance may be limited by various factors such as: the heat generated by other components, room temperature, free air flow at the bottom of a laptop, etc. It is explicitly NOT a goal of this API to let userspace know @@ -44,7 +44,7 @@ added. Drivers which wish to introduce new profile names must: "Custom" profile support ======================== The platform_profile class also supports profiles advertising a "custom" -profile. This is intended to be set by drivers when the setttings in the +profile. This is intended to be set by drivers when the settings in the driver have been modified in a way that a standard profile doesn't represent the current state. diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 1bd2d42e6424..6aa40ee05a4a 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -2006,7 +2006,14 @@ frequency is KHz. If the KVM_CAP_VM_TSC_CONTROL capability is advertised, this can also be used as a vm ioctl to set the initial tsc frequency of subsequently -created vCPUs. +created vCPUs. Note, the vm ioctl is only allowed prior to creating vCPUs. + +For TSC protected Confidential Computing (CoCo) VMs where TSC frequency +is configured once at VM scope and remains unchanged during VM's +lifetime, the vm ioctl should be used to configure the TSC frequency +and the vcpu ioctl is not supported. + +Example of such CoCo VMs: TDX guests. 4.56 KVM_GET_TSC_KHZ -------------------- @@ -6645,7 +6652,8 @@ to the byte array. .. note:: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, KVM_EXIT_XEN, - KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding + KVM_EXIT_EPR, KVM_EXIT_HYPERCALL, KVM_EXIT_TDX, + KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding operations are complete (and guest state is consistent) only after userspace has re-entered the kernel with KVM_RUN. The kernel side will first finish incomplete operations and then check for pending signals. @@ -7174,6 +7182,69 @@ The valid value for 'flags' is: - KVM_NOTIFY_CONTEXT_INVALID -- the VM context is corrupted and not valid in VMCS. It would run into unknown result if resume the target VM. +:: + + /* KVM_EXIT_TDX */ + struct { + __u64 flags; + __u64 nr; + union { + struct { + u64 ret; + u64 data[5]; + } unknown; + struct { + u64 ret; + u64 gpa; + u64 size; + } get_quote; + struct { + u64 ret; + u64 leaf; + u64 r11, r12, r13, r14; + } get_tdvmcall_info; + struct { + u64 ret; + u64 vector; + } setup_event_notify; + }; + } tdx; + +Process a TDVMCALL from the guest. KVM forwards select TDVMCALL based +on the Guest-Hypervisor Communication Interface (GHCI) specification; +KVM bridges these requests to the userspace VMM with minimal changes, +placing the inputs in the union and copying them back to the guest +on re-entry. + +Flags are currently always zero, whereas ``nr`` contains the TDVMCALL +number from register R11. The remaining field of the union provide the +inputs and outputs of the TDVMCALL. Currently the following values of +``nr`` are defined: + + * ``TDVMCALL_GET_QUOTE``: the guest has requested to generate a TD-Quote + signed by a service hosting TD-Quoting Enclave operating on the host. + Parameters and return value are in the ``get_quote`` field of the union. + The ``gpa`` field and ``size`` specify the guest physical address + (without the shared bit set) and the size of a shared-memory buffer, in + which the TDX guest passes a TD Report. The ``ret`` field represents + the return value of the GetQuote request. When the request has been + queued successfully, the TDX guest can poll the status field in the + shared-memory area to check whether the Quote generation is completed or + not. When completed, the generated Quote is returned via the same buffer. + + * ``TDVMCALL_GET_TD_VM_CALL_INFO``: the guest has requested the support + status of TDVMCALLs. The output values for the given leaf should be + placed in fields from ``r11`` to ``r14`` of the ``get_tdvmcall_info`` + field of the union. + + * ``TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT``: the guest has requested to + set up a notification interrupt for vector ``vector``. + +KVM may add support for more values in the future that may cause a userspace +exit, even without calls to ``KVM_ENABLE_CAP`` or similar. In this case, +it will enter with output fields already valid; in the common case, the +``unknown.ret`` field of the union will be ``TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED``. +Userspace need not do anything if it does not wish to support a TDVMCALL. :: /* Fix the size of the union. */ @@ -7780,6 +7851,7 @@ Valid bits in args[0] are:: #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) + #define KVM_X86_DISABLE_EXITS_APERFMPERF (1 << 4) Enabling this capability on a VM provides userspace with a way to no longer intercept some instructions for improved latency in some @@ -7790,6 +7862,28 @@ all such vmexits. Do not enable KVM_FEATURE_PV_UNHALT if you disable HLT exits. +Virtualizing the ``IA32_APERF`` and ``IA32_MPERF`` MSRs requires more +than just disabling APERF/MPERF exits. While both Intel and AMD +document strict usage conditions for these MSRs--emphasizing that only +the ratio of their deltas over a time interval (T0 to T1) is +architecturally defined--simply passing through the MSRs can still +produce an incorrect ratio. + +This erroneous ratio can occur if, between T0 and T1: + +1. The vCPU thread migrates between logical processors. +2. Live migration or suspend/resume operations take place. +3. Another task shares the vCPU's logical processor. +4. C-states lower than C0 are emulated (e.g., via HLT interception). +5. The guest TSC frequency doesn't match the host TSC frequency. + +Due to these complexities, KVM does not automatically associate this +passthrough capability with the guest CPUID bit, +``CPUID.6:ECX.APERFMPERF[bit 0]``. Userspace VMMs that deem this +mechanism adequate for virtualizing the ``IA32_APERF`` and +``IA32_MPERF`` MSRs must set the guest CPUID bit explicitly. + + 7.14 KVM_CAP_S390_HPAGE_1M -------------------------- @@ -8316,7 +8410,7 @@ core crystal clock frequency, if a non-zero CPUID 0x15 is exposed to the guest. 7.36 KVM_CAP_DIRTY_LOG_RING/KVM_CAP_DIRTY_LOG_RING_ACQ_REL ---------------------------------------------------------- -:Architectures: x86, arm64 +:Architectures: x86, arm64, riscv :Type: vm :Parameters: args[0] - size of the dirty log ring @@ -8528,7 +8622,7 @@ ENOSYS for the others. When enabled, KVM will exit to userspace with KVM_EXIT_SYSTEM_EVENT of type KVM_SYSTEM_EVENT_SUSPEND to process the guest suspend request. -7.37 KVM_CAP_ARM_WRITABLE_IMP_ID_REGS +7.42 KVM_CAP_ARM_WRITABLE_IMP_ID_REGS ------------------------------------- :Architectures: arm64 @@ -8557,6 +8651,17 @@ given VM. When this capability is enabled, KVM resets the VCPU when setting MP_STATE_INIT_RECEIVED through IOCTL. The original MP_STATE is preserved. +7.43 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED +------------------------------------------- + +:Architectures: arm64 +:Target: VM +:Parameters: None + +This capability indicate to the userspace whether a PFNMAP memory region +can be safely mapped as cacheable. This relies on the presence of +force write back (FWB) feature support on the hardware. + 8. Other capabilities. ====================== diff --git a/Documentation/virt/kvm/devices/arm-vgic-v3.rst b/Documentation/virt/kvm/devices/arm-vgic-v3.rst index e860498b1e35..ff02102f7141 100644 --- a/Documentation/virt/kvm/devices/arm-vgic-v3.rst +++ b/Documentation/virt/kvm/devices/arm-vgic-v3.rst @@ -78,6 +78,8 @@ Groups: -ENXIO The group or attribute is unknown/unsupported for this device or hardware support is missing. -EFAULT Invalid user pointer for attr->addr. + -EBUSY Attempt to write a register that is read-only after + initialization ======= ============================================================= @@ -120,6 +122,12 @@ Groups: Note that distributor fields are not banked, but return the same value regardless of the mpidr used to access the register. + Userspace is allowed to write the following register fields prior to + initialization of the VGIC: + + * GICD_IIDR.Revision + * GICD_TYPER2.nASSGIcap + GICD_IIDR.Revision is updated when the KVM implementation is changed in a way directly observable by the guest or userspace. Userspace should read GICD_IIDR from KVM and write back the read value to confirm its expected @@ -128,6 +136,12 @@ Groups: behavior. + GICD_TYPER2.nASSGIcap allows userspace to control the support of SGIs + without an active state. At VGIC creation the field resets to the + maximum capability of the system. Userspace is expected to read the field + to determine the supported value(s) before writing to the field. + + The GICD_STATUSR and GICR_STATUSR registers are architecturally defined such that a write of a clear bit has no effect, whereas a write with a set bit clears that value. To allow userspace to freely set the values of these two @@ -202,16 +216,69 @@ Groups: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS accesses the CPU interface registers for the CPU specified by the mpidr field. - CPU interface registers access is not implemented for AArch32 mode. - Error -ENXIO is returned when accessed in AArch32 mode. + The available registers are: + + =============== ==================================================== + ICC_PMR_EL1 + ICC_BPR0_EL1 + ICC_AP0R0_EL1 + ICC_AP0R1_EL1 when the host implements at least 6 bits of priority + ICC_AP0R2_EL1 when the host implements 7 bits of priority + ICC_AP0R3_EL1 when the host implements 7 bits of priority + ICC_AP1R0_EL1 + ICC_AP1R1_EL1 when the host implements at least 6 bits of priority + ICC_AP1R2_EL1 when the host implements 7 bits of priority + ICC_AP1R3_EL1 when the host implements 7 bits of priority + ICC_BPR1_EL1 + ICC_CTLR_EL1 + ICC_SRE_EL1 + ICC_IGRPEN0_EL1 + ICC_IGRPEN1_EL1 + =============== ==================================================== + + When EL2 is available for the guest, these registers are also available: + + ============= ==================================================== + ICH_AP0R0_EL2 + ICH_AP0R1_EL2 when the host implements at least 6 bits of priority + ICH_AP0R2_EL2 when the host implements 7 bits of priority + ICH_AP0R3_EL2 when the host implements 7 bits of priority + ICH_AP1R0_EL2 + ICH_AP1R1_EL2 when the host implements at least 6 bits of priority + ICH_AP1R2_EL2 when the host implements 7 bits of priority + ICH_AP1R3_EL2 when the host implements 7 bits of priority + ICH_HCR_EL2 + ICC_SRE_EL2 + ICH_VTR_EL2 + ICH_VMCR_EL2 + ICH_LR0_EL2 + ICH_LR1_EL2 + ICH_LR2_EL2 + ICH_LR3_EL2 + ICH_LR4_EL2 + ICH_LR5_EL2 + ICH_LR6_EL2 + ICH_LR7_EL2 + ICH_LR8_EL2 + ICH_LR9_EL2 + ICH_LR10_EL2 + ICH_LR11_EL2 + ICH_LR12_EL2 + ICH_LR13_EL2 + ICH_LR14_EL2 + ICH_LR15_EL2 + ============= ==================================================== + + CPU interface registers are only described using the AArch64 + encoding. Errors: - ======= ===================================================== - -ENXIO Getting or setting this register is not yet supported + ======= ================================================= + -ENXIO Getting or setting this register is not supported -EBUSY VCPU is running -EINVAL Invalid mpidr or register value supplied - ======= ===================================================== + ======= ================================================= KVM_DEV_ARM_VGIC_GRP_NR_IRQS diff --git a/Documentation/virt/kvm/review-checklist.rst b/Documentation/virt/kvm/review-checklist.rst index dc01aea4057b..debac54e14e7 100644 --- a/Documentation/virt/kvm/review-checklist.rst +++ b/Documentation/virt/kvm/review-checklist.rst @@ -7,7 +7,7 @@ Review checklist for kvm patches 1. The patch must follow Documentation/process/coding-style.rst and Documentation/process/submitting-patches.rst. -2. Patches should be against kvm.git master branch. +2. Patches should be against kvm.git master or next branches. 3. If the patch introduces or modifies a new userspace API: - the API must be documented in Documentation/virt/kvm/api.rst @@ -18,10 +18,10 @@ Review checklist for kvm patches 5. New features must default to off (userspace should explicitly request them). Performance improvements can and should default to on. -6. New cpu features should be exposed via KVM_GET_SUPPORTED_CPUID2 +6. New cpu features should be exposed via KVM_GET_SUPPORTED_CPUID2, + or its equivalent for non-x86 architectures -7. Emulator changes should be accompanied by unit tests for qemu-kvm.git - kvm/test directory. +7. The feature should be testable (see below). 8. Changes should be vendor neutral when possible. Changes to common code are better than duplicating changes to vendor code. @@ -36,6 +36,87 @@ Review checklist for kvm patches 11. New guest visible features must either be documented in a hardware manual or be accompanied by documentation. -12. Features must be robust against reset and kexec - for example, shared - host/guest memory must be unshared to prevent the host from writing to - guest memory that the guest has not reserved for this purpose. +Testing of KVM code +------------------- + +All features contributed to KVM, and in many cases bugfixes too, should be +accompanied by some kind of tests and/or enablement in open source guests +and VMMs. KVM is covered by multiple test suites: + +*Selftests* + These are low level tests that allow granular testing of kernel APIs. + This includes API failure scenarios, invoking APIs after specific + guest instructions, and testing multiple calls to ``KVM_CREATE_VM`` + within a single test. They are included in the kernel tree at + ``tools/testing/selftests/kvm``. + +``kvm-unit-tests`` + A collection of small guests that test CPU and emulated device features + from a guest's perspective. They run under QEMU or ``kvmtool``, and + are generally not KVM-specific: they can be run with any accelerator + that QEMU support or even on bare metal, making it possible to compare + behavior across hypervisors and processor families. + +Functional test suites + Various sets of functional tests exist, such as QEMU's ``tests/functional`` + suite and `avocado-vt `__. + These typically involve running a full operating system in a virtual + machine. + +The best testing approach depends on the feature's complexity and +operation. Here are some examples and guidelines: + +New instructions (no new registers or APIs) + The corresponding CPU features (if applicable) should be made available + in QEMU. If the instructions require emulation support or other code in + KVM, it is worth adding coverage to ``kvm-unit-tests`` or selftests; + the latter can be a better choice if the instructions relate to an API + that already has good selftest coverage. + +New hardware features (new registers, no new APIs) + These should be tested via ``kvm-unit-tests``; this more or less implies + supporting them in QEMU and/or ``kvmtool``. In some cases selftests + can be used instead, similar to the previous case, or specifically to + test corner cases in guest state save/restore. + +Bug fixes and performance improvements + These usually do not introduce new APIs, but it's worth sharing + any benchmarks and tests that will validate your contribution, + ideally in the form of regression tests. Tests and benchmarks + can be included in either ``kvm-unit-tests`` or selftests, depending + on the specifics of your change. Selftests are especially useful for + regression tests because they are included directly in Linux's tree. + +Large scale internal changes + While it's difficult to provide a single policy, you should ensure that + the changed code is covered by either ``kvm-unit-tests`` or selftests. + In some cases the affected code is run for any guests and functional + tests suffice. Explain your testing process in the cover letter, + as that can help identify gaps in existing test suites. + +New APIs + It is important to demonstrate your use case. This can be as simple as + explaining that the feature is already in use on bare metal, or it can be + a proof-of-concept implementation in userspace. The latter need not be + open source, though that is of course preferrable for easier testing. + Selftests should test corner cases of the APIs, and should also cover + basic host and guest operation if no open source VMM uses the feature. + +Bigger features, usually spanning host and guest + These should be supported by Linux guests, with limited exceptions for + Hyper-V features that are testable on Windows guests. It is strongly + suggested that the feature be usable with an open source host VMM, such + as at least one of QEMU or crosvm, and guest firmware. Selftests should + test at least API error cases. Guest operation can be covered by + either selftests of ``kvm-unit-tests`` (this is especially important for + paravirtualized and Windows-only features). Strong selftest coverage + can also be a replacement for implementation in an open source VMM, + but this is generally not recommended. + +Following the above suggestions for testing in selftests and +``kvm-unit-tests`` will make it easier for the maintainers to review +and accept your code. In fact, even before you contribute your changes +upstream it will make it easier for you to develop for KVM. + +Of course, the KVM maintainers reserve the right to require more tests, +though they may also waive the requirement from time to time. diff --git a/Documentation/virt/kvm/x86/intel-tdx.rst b/Documentation/virt/kvm/x86/intel-tdx.rst index 76bdd95334d6..5efac62c92c7 100644 --- a/Documentation/virt/kvm/x86/intel-tdx.rst +++ b/Documentation/virt/kvm/x86/intel-tdx.rst @@ -79,7 +79,20 @@ to be configured to the TDX guest. struct kvm_tdx_capabilities { __u64 supported_attrs; __u64 supported_xfam; - __u64 reserved[254]; + + /* TDG.VP.VMCALL hypercalls executed in kernel and forwarded to + * userspace, respectively + */ + __u64 kernel_tdvmcallinfo_1_r11; + __u64 user_tdvmcallinfo_1_r11; + + /* TDG.VP.VMCALL instruction executions subfunctions executed in kernel + * and forwarded to userspace, respectively + */ + __u64 kernel_tdvmcallinfo_1_r12; + __u64 user_tdvmcallinfo_1_r12; + + __u64 reserved[250]; /* Configurable CPUID bits for userspace */ struct kvm_cpuid2 cpuid; diff --git a/Documentation/wmi/acpi-interface.rst b/Documentation/wmi/acpi-interface.rst index f1b28835d23c..1ef003b033bf 100644 --- a/Documentation/wmi/acpi-interface.rst +++ b/Documentation/wmi/acpi-interface.rst @@ -36,7 +36,7 @@ Offset Size (in bytes) Content The WMI object flags control whether the method or notification ID is used: -- 0x1: Data block usage is expensive and must be explicitly enabled/disabled. +- 0x1: Data block is expensive to collect. - 0x2: Data block contains WMI methods. - 0x4: Data block contains ASCIZ string. - 0x8: Data block describes a WMI event, use notification ID instead @@ -83,14 +83,18 @@ event as hexadecimal value. Their first parameter is an integer with a value of 0 if the WMI event should be disabled, other values will enable the WMI event. +Those ACPI methods are always called even for WMI events not registered as +being expensive to collect to match the behavior of the Windows driver. + WCxx ACPI methods ----------------- -Similar to the ``WExx`` ACPI methods, except that it controls data collection -instead of events and thus the last two characters of the ACPI method name are -the method ID of the data block to enable/disable. +Similar to the ``WExx`` ACPI methods, except that instead of WMI events it controls +data collection of data blocks registered as being expensive to collect. Thus the +last two characters of the ACPI method name are the method ID of the data block +to enable/disable. Those ACPI methods are also called before setting data blocks to match the -behaviour of the Windows driver. +behavior of the Windows driver. _WED ACPI method ---------------- diff --git a/Documentation/wmi/devices/lenovo-wmi-gamezone.rst b/Documentation/wmi/devices/lenovo-wmi-gamezone.rst new file mode 100644 index 000000000000..997263e51a7d --- /dev/null +++ b/Documentation/wmi/devices/lenovo-wmi-gamezone.rst @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +========================================================== +Lenovo WMI Interface Gamezone Driver (lenovo-wmi-gamezone) +========================================================== + +Introduction +============ +The Lenovo WMI gamezone interface is broken up into multiple GUIDs, +The primary "Gamezone" GUID provides advanced features such as fan +profiles and overclocking. It is paired with multiple event GUIDs +and data block GUIDs that provide context for the various methods. + +Gamezone Data +------------- + +WMI GUID ``887B54E3-DDDC-4B2C-8B88-68A26A8835D0`` + +The Gamezone Data WMI interface provides platform-profile and fan curve +settings for devices that fall under the "Gaming Series" of Lenovo devices. +It uses a notifier chain to inform other Lenovo WMI interface drivers of the +current platform profile when it changes. + +The following platform profiles are supported: + - low-power + - balanced + - balanced-performance + - performance + - custom + +Balanced-Performance +~~~~~~~~~~~~~~~~~~~~ +Some newer Lenovo "Gaming Series" laptops have an "Extreme Mode" profile +enabled in their BIOS. For these devices, the performance platform profile +corresponds to the BIOS Extreme Mode, while the balanced-performance +platform profile corresponds to the BIOS Performance mode. For legacy +devices, the performance platform profile will correspond with the BIOS +Performance mode. + +For some newer devices the "Extreme Mode" profile is incomplete in the BIOS +and setting it will cause undefined behavior. A BIOS bug quirk table is +provided to ensure these devices cannot set "Extreme Mode" from the driver. + +Custom Profile +~~~~~~~~~~~~~~ +The custom profile represents a hardware mode on Lenovo devices that enables +user modifications to Package Power Tracking (PPT) and fan curve settings. +When an attribute exposed by the Other Mode WMI interface is to be modified, +the Gamezone driver must first be switched to the "custom" profile manually, +or the setting will have no effect. If another profile is set from the list +of supported profiles, the BIOS will override any user PPT settings when +switching to that profile. + +Gamezone Thermal Mode Event +--------------------------- + +WMI GUID ``D320289E-8FEA-41E0-86F9-911D83151B5F`` + +The Gamezone Thermal Mode Event interface notifies the system when the platform +profile has changed, either through the hardware event (Fn+Q for laptops or +Legion + Y for Go Series), or through the Gamezone WMI interface. This event is +implemented in the Lenovo WMI Events driver (lenovo-wmi-events). + + +WMI interface description +========================= + +The WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_GAMEZONE_DATA class"), guid("{887B54E3-DDDC-4B2C-8B88-68A26A8835D0}")] + class LENOVO_GAMEZONE_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(4), Implemented, Description("Is SupportGpu OverClock")] void IsSupportGpuOC([out, Description("Is SupportGpu OverClock")] uint32 Data); + [WmiMethodId(11), Implemented, Description("Get AslCode Version")] void GetVersion ([out, Description("AslCode version")] UINT32 Data); + [WmiMethodId(12), Implemented, Description("Fan cooling capability")] void IsSupportFanCooling([out, Description("Fan cooling capability")] UINT32 Data); + [WmiMethodId(13), Implemented, Description("Set Fan cooling on/off")] void SetFanCooling ([in, Description("Set Fan cooling on/off")] UINT32 Data); + [WmiMethodId(14), Implemented, Description("cpu oc capability")] void IsSupportCpuOC ([out, Description("cpu oc capability")] UINT32 Data); + [WmiMethodId(15), Implemented, Description("bios has overclock capability")] void IsBIOSSupportOC ([out, Description("bios has overclock capability")] UINT32 Data); + [WmiMethodId(16), Implemented, Description("enable or disable overclock in bios")] void SetBIOSOC ([in, Description("enable or disable overclock in bios")] UINT32 Data); + [WmiMethodId(18), Implemented, Description("Get CPU temperature")] void GetCPUTemp ([out, Description("Get CPU temperature")] UINT32 Data); + [WmiMethodId(19), Implemented, Description("Get GPU temperature")] void GetGPUTemp ([out, Description("Get GPU temperature")] UINT32 Data); + [WmiMethodId(20), Implemented, Description("Get Fan cooling on/off status")] void GetFanCoolingStatus ([out, Description("Get Fan cooling on/off status")] UINT32 Data); + [WmiMethodId(21), Implemented, Description("EC support disable windows key capability")] void IsSupportDisableWinKey ([out, Description("EC support disable windows key capability")] UINT32 Data); + [WmiMethodId(22), Implemented, Description("Set windows key disable/enable")] void SetWinKeyStatus ([in, Description("Set windows key disable/enable")] UINT32 Data); + [WmiMethodId(23), Implemented, Description("Get windows key disable/enable status")] void GetWinKeyStatus ([out, Description("Get windows key disable/enable status")] UINT32 Data); + [WmiMethodId(24), Implemented, Description("EC support disable touchpad capability")] void IsSupportDisableTP ([out, Description("EC support disable touchpad capability")] UINT32 Data); + [WmiMethodId(25), Implemented, Description("Set touchpad disable/enable")] void SetTPStatus ([in, Description("Set touchpad disable/enable")] UINT32 Data); + [WmiMethodId(26), Implemented, Description("Get touchpad disable/enable status")] void GetTPStatus ([out, Description("Get touchpad disable/enable status")] UINT32 Data); + [WmiMethodId(30), Implemented, Description("Get Keyboard feature list")] void GetKeyboardfeaturelist ([out, Description("Get Keyboard feature list")] UINT32 Data); + [WmiMethodId(31), Implemented, Description("Get Memory OC Information")] void GetMemoryOCInfo ([out, Description("Get Memory OC Information")] UINT32 Data); + [WmiMethodId(32), Implemented, Description("Water Cooling feature capability")] void IsSupportWaterCooling ([out, Description("Water Cooling feature capability")] UINT32 Data); + [WmiMethodId(33), Implemented, Description("Set Water Cooling status")] void SetWaterCoolingStatus ([in, Description("Set Water Cooling status")] UINT32 Data); + [WmiMethodId(34), Implemented, Description("Get Water Cooling status")] void GetWaterCoolingStatus ([out, Description("Get Water Cooling status")] UINT32 Data); + [WmiMethodId(35), Implemented, Description("Lighting feature capability")] void IsSupportLightingFeature ([out, Description("Lighting feature capability")] UINT32 Data); + [WmiMethodId(36), Implemented, Description("Set keyboard light off or on to max")] void SetKeyboardLight ([in, Description("keyboard light off or on switch")] UINT32 Data); + [WmiMethodId(37), Implemented, Description("Get keyboard light on/off status")] void GetKeyboardLight ([out, Description("Get keyboard light on/off status")] UINT32 Data); + [WmiMethodId(38), Implemented, Description("Get Macrokey scan code")] void GetMacrokeyScancode ([in, Description("Macrokey index")] UINT32 idx, [out, Description("Scan code")] UINT32 scancode); + [WmiMethodId(39), Implemented, Description("Get Macrokey count")] void GetMacrokeyCount ([out, Description("Macrokey count")] UINT32 Data); + [WmiMethodId(40), Implemented, Description("Support G-Sync feature")] void IsSupportGSync ([out, Description("Support G-Sync feature")] UINT32 Data); + [WmiMethodId(41), Implemented, Description("Get G-Sync Status")] void GetGSyncStatus ([out, Description("Get G-Sync Status")] UINT32 Data); + [WmiMethodId(42), Implemented, Description("Set G-Sync Status")] void SetGSyncStatus ([in, Description("Set G-Sync Status")] UINT32 Data); + [WmiMethodId(43), Implemented, Description("Support Smart Fan feature")] void IsSupportSmartFan ([out, Description("Support Smart Fan feature")] UINT32 Data); + [WmiMethodId(44), Implemented, Description("Set Smart Fan Mode")] void SetSmartFanMode ([in, Description("Set Smart Fan Mode")] UINT32 Data); + [WmiMethodId(45), Implemented, Description("Get Smart Fan Mode")] void GetSmartFanMode ([out, Description("Get Smart Fan Mode")] UINT32 Data); + [WmiMethodId(46), Implemented, Description("Get Smart Fan Setting Mode")] void GetSmartFanSetting ([out, Description("Get Smart Setting Mode")] UINT32 Data); + [WmiMethodId(47), Implemented, Description("Get Power Charge Mode")] void GetPowerChargeMode ([out, Description("Get Power Charge Mode")] UINT32 Data); + [WmiMethodId(48), Implemented, Description("Get Gaming Product Info")] void GetProductInfo ([out, Description("Get Gaming Product Info")] UINT32 Data); + [WmiMethodId(49), Implemented, Description("Over Drive feature capability")] void IsSupportOD ([out, Description("Over Drive feature capability")] UINT32 Data); + [WmiMethodId(50), Implemented, Description("Get Over Drive status")] void GetODStatus ([out, Description("Get Over Drive status")] UINT32 Data); + [WmiMethodId(51), Implemented, Description("Set Over Drive status")] void SetODStatus ([in, Description("Set Over Drive status")] UINT32 Data); + [WmiMethodId(52), Implemented, Description("Set Light Control Owner")] void SetLightControlOwner ([in, Description("Set Light Control Owner")] UINT32 Data); + [WmiMethodId(53), Implemented, Description("Set DDS Control Owner")] void SetDDSControlOwner ([in, Description("Set DDS Control Owner")] UINT32 Data); + [WmiMethodId(54), Implemented, Description("Get the flag of restore OC value")] void IsRestoreOCValue ([in, Description("Clean this flag")] UINT32 idx, [out, Description("Restore oc value flag")] UINT32 Data); + [WmiMethodId(55), Implemented, Description("Get Real Thremal Mode")] void GetThermalMode ([out, Description("Real Thremal Mode")] UINT32 Data); + [WmiMethodId(56), Implemented, Description("Get the OC switch status in BIOS")] void GetBIOSOCMode ([out, Description("OC Mode")] UINT32 Data); + [WmiMethodId(59), Implemented, Description("Get hardware info support version")] void GetHardwareInfoSupportVersion ([out, Description("version")] UINT32 Data); + [WmiMethodId(60), Implemented, Description("Get Cpu core 0 max frequency")] void GetCpuFrequency ([out, Description("frequency")] UINT32 Data); + [WmiMethodId(62), Implemented, Description("Check the Adapter type fit for OC")] void IsACFitForOC ([out, Description("AC check result")] UINT32 Data); + [WmiMethodId(63), Implemented, Description("Is support IGPU mode")] void IsSupportIGPUMode ([out, Description("IGPU modes")] UINT32 Data); + [WmiMethodId(64), Implemented, Description("Get IGPU Mode Status")] void GetIGPUModeStatus([out, Description("IGPU Mode Status")] UINT32 Data); + [WmiMethodId(65), Implemented, Description("Set IGPU Mode")] void SetIGPUModeStatus([in, Description("IGPU Mode")] UINT32 mode, [out, Description("return code")] UINT32 Data); + [WmiMethodId(66), Implemented, Description("Notify DGPU Status")] void NotifyDGPUStatus([in, Description("DGPU status")] UINT32 status, [out, Description("return code")] UINT32 Data); + [WmiMethodId(67), Implemented, Description("Is changed Y log")] void IsChangedYLog([out, Description("Is changed Y Log")] UINT32 Data); + [WmiMethodId(68), Implemented, Description("Get DGPU Hardwawre ID")] void GetDGPUHWId([out, Description("Get DGPU Hardware ID")] string Data); + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of CPU OC parameter list"), guid("{B7F3CA0A-ACDC-42D2-9217-77C6C628FBD2}")] + class LENOVO_GAMEZONE_CPU_OC_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("OC tune id.")] uint32 Tuneid; + [WmiDataId(2), read, Description("Default value.")] uint32 DefaultValue; + [WmiDataId(3), read, Description("OC Value.")] uint32 OCValue; + [WmiDataId(4), read, Description("Min Value.")] uint32 MinValue; + [WmiDataId(5), read, Description("Max Value.")] uint32 MaxValue; + [WmiDataId(6), read, Description("Scale Value.")] uint32 ScaleValue; + [WmiDataId(7), read, Description("OC Order id.")] uint32 OCOrderid; + [WmiDataId(8), read, Description("NON-OC Order id.")] uint32 NOCOrderid; + [WmiDataId(9), read, Description("Delay time in ms.")] uint32 Interval; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of GPU OC parameter list"), guid("{887B54E2-DDDC-4B2C-8B88-68A26A8835D0}")] + class LENOVO_GAMEZONE_GPU_OC_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("P-State ID.")] uint32 PStateID; + [WmiDataId(2), read, Description("CLOCK ID.")] uint32 ClockID; + [WmiDataId(3), read, Description("Default value.")] uint32 defaultvalue; + [WmiDataId(4), read, Description("OC Offset freqency.")] uint32 OCOffsetFreq; + [WmiDataId(5), read, Description("OC Min offset value.")] uint32 OCMinOffset; + [WmiDataId(6), read, Description("OC Max offset value.")] uint32 OCMaxOffset; + [WmiDataId(7), read, Description("OC Offset Scale.")] uint32 OCOffsetScale; + [WmiDataId(8), read, Description("OC Order id.")] uint32 OCOrderid; + [WmiDataId(9), read, Description("NON-OC Order id.")] uint32 NOCOrderid; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Fancooling finish event"), guid("{BC72A435-E8C1-4275-B3E2-D8B8074ABA59}")] + class LENOVO_GAMEZONE_FAN_COOLING_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Fancooling clean finish event")] uint32 EventId; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan mode change event"), guid("{D320289E-8FEA-41E0-86F9-611D83151B5F}")] + class LENOVO_GAMEZONE_SMART_FAN_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Smart Fan Mode change event")] uint32 mode; + [WmiDataId(2), read, Description("version of FN+Q")] uint32 version; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan setting mode change event"), guid("{D320289E-8FEA-41E1-86F9-611D83151B5F}")] + class LENOVO_GAMEZONE_SMART_FAN_SETTING_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Smart Fan Setting mode change event")] uint32 mode; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("POWER CHARGE MODE Change EVENT"), guid("{D320289E-8FEA-41E0-86F9-711D83151B5F}")] + class LENOVO_GAMEZONE_POWER_CHARGE_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("POWER CHARGE MODE Change EVENT")] uint32 mode; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Thermal Mode Real Mode change event"), guid("{D320289E-8FEA-41E0-86F9-911D83151B5F}")] + class LENOVO_GAMEZONE_THERMAL_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Thermal Mode Real Mode")] uint32 mode; + }; diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst new file mode 100644 index 000000000000..d7928b8dfb4b --- /dev/null +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -0,0 +1,108 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +=========================================================== +Lenovo WMI Interface Other Mode Driver (lenovo-wmi-other) +=========================================================== + +Introduction +============ +Lenovo WMI Other Mode interface is broken up into multiple GUIDs, +The primary Other Mode interface provides advanced power tuning features +such as Package Power Tracking (PPT). It is paired with multiple data block +GUIDs that provide context for the various methods. + + +Other Mode +---------- + +WMI GUID ``DC2A8805-3A8C-41BA-A6F7-092E0089CD3B`` + +The Other Mode WMI interface uses the firmware_attributes class to expose +various WMI attributes provided by the interface in the sysfs. This enables +CPU and GPU power limit tuning as well as various other attributes for +devices that fall under the "Gaming Series" of Lenovo devices. Each +attribute exposed by the Other Mode interface has corresponding +capability data blocks which allow the driver to probe details about the +attribute. Each attribute has multiple pages, one for each of the platform +profiles managed by the Gamezone interface. Attributes are exposed in sysfs +under the following path: + +:: + + /sys/class/firmware-attributes/lenovo-wmi-other/attributes// + +LENOVO_CAPABILITY_DATA_01 +------------------------- + +WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154`` + +The LENOVO_CAPABILITY_DATA_01 interface provides information on various +power limits of integrated CPU and GPU components. + +Each attribute has the following properties: + - current_value + - default_value + - display_name + - max_value + - min_value + - scalar_increment + - type + +The following attributes are implemented: + - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit + - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking + - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking + + +WMI interface description +========================= + +The WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_OTHER_METHOD class"), guid("{dc2a8805-3a8c-41ba-a6f7-092e0089cd3b}")] + class LENOVO_OTHER_METHOD { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(17), Implemented, Description("Get Feature Value ")] void GetFeatureValue([in] uint32 IDs, [out] uint32 value); + [WmiMethodId(18), Implemented, Description("Set Feature Value ")] void SetFeatureValue([in] uint32 IDs, [in] uint32 value); + [WmiMethodId(19), Implemented, Description("Get Data By Command ")] void GetDataByCommand([in] uint32 IDs, [in] uint32 Command, [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint32 Data[]); + [WmiMethodId(99), Implemented, Description("Get Data By Package for TAC")] void GetDataByPackage([in, Max(40)] uint8 Input[], [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint8 Data[]); + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")] + class LENOVO_CAPABILITY_DATA_00 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 01"), guid("{7a8f5407-cb67-4d6e-b547-39b3be018154}")] + class LENOVO_CAPABILITY_DATA_01 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Default Value.")] uint32 DefaultValue; + [WmiDataId(4), read, Description("Step.")] uint32 Step; + [WmiDataId(5), read, Description("Minimum Value.")] uint32 MinValue; + [WmiDataId(6), read, Description("Maximum Value.")] uint32 MaxValue; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 02"), guid("{bbf1f790-6c2f-422b-bc8c-4e7369c7f6ab}")] + class LENOVO_CAPABILITY_DATA_02 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize; + [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[]; + }; diff --git a/LICENSES/deprecated/GFDL-1.1 b/LICENSES/deprecated/GFDL-1.1 index ae62699ab62c..d7a524f602a4 100644 --- a/LICENSES/deprecated/GFDL-1.1 +++ b/LICENSES/deprecated/GFDL-1.1 @@ -24,7 +24,7 @@ License-Text: Version 1.1, March 2000 Copyright (C) 2000 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/LICENSES/deprecated/GFDL-1.2 b/LICENSES/deprecated/GFDL-1.2 index b97e99a11d37..5e8fff04af05 100644 --- a/LICENSES/deprecated/GFDL-1.2 +++ b/LICENSES/deprecated/GFDL-1.2 @@ -23,7 +23,7 @@ License-Text: Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/LICENSES/deprecated/GPL-1.0 b/LICENSES/deprecated/GPL-1.0 index 3a4fa969e4c2..8d0a75431f06 100644 --- a/LICENSES/deprecated/GPL-1.0 +++ b/LICENSES/deprecated/GPL-1.0 @@ -14,7 +14,8 @@ License-Text: Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA + + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -227,8 +228,7 @@ the exclusion of warranty; and each file should have at least 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. + along with this program; if not, see . Also add information on how to contact you by electronic and paper mail. diff --git a/LICENSES/preferred/GPL-2.0 b/LICENSES/preferred/GPL-2.0 index ff0812fd89cc..ea8e93dc44bc 100644 --- a/LICENSES/preferred/GPL-2.0 +++ b/LICENSES/preferred/GPL-2.0 @@ -20,8 +20,8 @@ License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -322,10 +322,8 @@ the "copyright" line and a pointer to where the full notice is found. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, see . Also add information on how to contact you by electronic and paper mail. diff --git a/LICENSES/preferred/LGPL-2.0 b/LICENSES/preferred/LGPL-2.0 index 957d798fe037..dc629746eb72 100644 --- a/LICENSES/preferred/LGPL-2.0 +++ b/LICENSES/preferred/LGPL-2.0 @@ -16,7 +16,7 @@ GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. -51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -468,8 +468,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License -along with this library; if not, write to the Free Software Foundation, -Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +along with this library; if not, see . Also add information on how to contact you by electronic and paper mail. diff --git a/LICENSES/preferred/LGPL-2.1 b/LICENSES/preferred/LGPL-2.1 index b73f9b6230f5..105b9f3c5ba1 100644 --- a/LICENSES/preferred/LGPL-2.1 +++ b/LICENSES/preferred/LGPL-2.1 @@ -18,7 +18,7 @@ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -488,9 +488,9 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add -information on how to contact you by electronic and paper mail. +along with this library; if not, see . + +Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if diff --git a/MAINTAINERS b/MAINTAINERS index e6cd3db64c12..5257d52679d6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -158,7 +158,7 @@ S: Maintained W: http://github.com/v9fs Q: http://patchwork.kernel.org/project/v9fs-devel/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git -T: git git://github.com/martinetd/linux.git +T: git https://github.com/martinetd/linux.git F: Documentation/filesystems/9p.rst F: fs/9p/ F: include/net/9p/ @@ -207,7 +207,7 @@ X: arch/*/include/uapi/ X: include/uapi/ ABIT UGURU 1,2 HARDWARE MONITOR DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/abituguru.c @@ -302,20 +302,22 @@ F: include/linux/acpi.h F: include/linux/fwnode.h F: include/linux/fw_table.h F: lib/fw_table.c +F: rust/kernel/acpi.rs F: tools/power/acpi/ ACPI APEI M: "Rafael J. Wysocki" -R: Len Brown -R: James Morse R: Tony Luck R: Borislav Petkov +R: Hanjun Guo +R: Mauro Carvalho Chehab +R: Shuai Xue L: linux-acpi@vger.kernel.org F: drivers/acpi/apei/ ACPI COMPONENT ARCHITECTURE (ACPICA) +M: "Rafael J. Wysocki" M: Robert Moore -M: "Rafael J. Wysocki" L: linux-acpi@vger.kernel.org L: acpica-devel@lists.linux.dev S: Supported @@ -371,7 +373,7 @@ S: Maintained F: drivers/platform/x86/quickstart.c ACPI SERIAL MULTI INSTANTIATE DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/serial-multi-instantiate.c @@ -550,6 +552,7 @@ L: linux-pwm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml F: drivers/gpio/gpio-adp5585.c +F: drivers/input/keyboard/adp5585-keys.c F: drivers/mfd/adp5585.c F: drivers/pwm/pwm-adp5585.c F: include/linux/mfd/adp5585.h @@ -821,7 +824,7 @@ F: drivers/media/platform/allegro-dvt/ ALLIED VISION ALVIUM CAMERA DRIVER M: Tommaso Merciai -M: Martin Hecht +M: Martin Hecht L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml @@ -928,13 +931,13 @@ F: Documentation/devicetree/bindings/dma/altr,msgdma.yaml F: drivers/dma/altera-msgdma.c ALTERA PIO DRIVER -M: Mun Yew Tham +M: Adrian Ng L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-altera.c ALTERA TRIPLE SPEED ETHERNET DRIVER -M: Joyce Ooi +M: Boon Khai Ng L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/altera/ @@ -1112,6 +1115,15 @@ F: arch/x86/include/asm/amd/hsmp.h F: arch/x86/include/uapi/asm/amd_hsmp.h F: drivers/platform/x86/amd/hsmp/ +AMD HETERO CORE HARDWARE FEEDBACK DRIVER +M: Mario Limonciello +R: Perry Yuan +L: platform-driver-x86@vger.kernel.org +S: Supported +B: https://gitlab.freedesktop.org/drm/amd/-/issues +F: Documentation/arch/x86/amd-hfi.rst +F: drivers/platform/x86/amd/hfi/ + AMD IOMMU (AMD-VI) M: Joerg Roedel R: Suravee Suthikulpanit @@ -1157,7 +1169,6 @@ F: arch/x86/include/asm/amd/node.h F: arch/x86/kernel/amd_node.c AMD PDS CORE DRIVER -M: Shannon Nelson M: Brett Creeley L: netdev@vger.kernel.org S: Maintained @@ -1232,7 +1243,7 @@ F: drivers/spi/spi-amd.c F: drivers/spi/spi-amd.h AMD XDNA DRIVER -M: Min Ma +M: Min Ma M: Lizhi Hou L: dri-devel@lists.freedesktop.org S: Supported @@ -1307,6 +1318,15 @@ S: Maintained F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml F: drivers/rtc/rtc-amlogic-a4.c +AMLOGIC SPISG DRIVER +M: Sunny Luo +M: Xianwei Zhao +L: linux-amlogic@lists.infradead.org +L: linux-spi@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml +F: drivers/spi/spi-amlogic-spisg.c + AMPHENOL CHIPCAP 2 DRIVER M: Javier Carrasco L: linux-hwmon@vger.kernel.org @@ -1375,6 +1395,14 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml F: Documentation/iio/ad4030.rst F: drivers/iio/adc/ad4030.c +ANALOG DEVICES INC AD4080 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/adc/adi,ad4080.yaml +F: drivers/iio/adc/ad4080.c + ANALOG DEVICES INC AD4130 DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org @@ -1384,6 +1412,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c +ANALOG DEVICES INC AD4170-4 DRIVER +M: Marcelo Schmitt +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml +F: drivers/iio/adc/ad4170-4.c + ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich M: Nuno Sá @@ -1482,6 +1518,7 @@ S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml F: drivers/iio/adc/ad7768-1.c +F: include/dt-bindings/iio/adc/adi,ad7768-1.h ANALOG DEVICES INC AD7780 DRIVER M: Michael Hennerich @@ -1965,6 +2002,16 @@ F: drivers/irqchip/irq-gic*.[ch] F: include/linux/irqchip/arm-gic*.h F: include/linux/irqchip/arm-vgic-info.h +ARM GENERIC INTERRUPT CONTROLLER V5 DRIVERS +M: Lorenzo Pieralisi +M: Marc Zyngier +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5*.yaml +F: drivers/irqchip/irq-gic-its-msi-parent.[ch] +F: drivers/irqchip/irq-gic-v5*.[ch] +F: include/linux/irqchip/arm-gic-v5.h + ARM HDLCD DRM DRIVER M: Liviu Dudau S: Supported @@ -2331,6 +2378,8 @@ F: Documentation/devicetree/bindings/arm/apple/* F: Documentation/devicetree/bindings/clock/apple,nco.yaml F: Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml F: Documentation/devicetree/bindings/dma/apple,admac.yaml +F: Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml +F: Documentation/devicetree/bindings/gpu/apple,agx.yaml F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml F: Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml F: Documentation/devicetree/bindings/interrupt-controller/apple,* @@ -2338,6 +2387,7 @@ F: Documentation/devicetree/bindings/iommu/apple,dart.yaml F: Documentation/devicetree/bindings/iommu/apple,sart.yaml F: Documentation/devicetree/bindings/leds/backlight/apple,dwi-bl.yaml F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +F: Documentation/devicetree/bindings/mfd/apple,smc.yaml F: Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml F: Documentation/devicetree/bindings/nvmem/apple,efuses.yaml @@ -2345,6 +2395,7 @@ F: Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* +F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml @@ -2354,6 +2405,7 @@ F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/cpufreq/apple-soc-cpufreq.c F: drivers/dma/apple-admac.c +F: drivers/gpio/gpio-macsmc.c F: drivers/pmdomain/apple/ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c @@ -2361,10 +2413,12 @@ F: drivers/input/touchscreen/apple_z2.c F: drivers/iommu/apple-dart.c F: drivers/iommu/io-pgtable-dart.c F: drivers/irqchip/irq-apple-aic.c +F: drivers/mfd/macsmc.c F: drivers/nvme/host/apple.c F: drivers/nvmem/apple-efuses.c F: drivers/nvmem/apple-spmi-nvmem.c F: drivers/pinctrl/pinctrl-apple-gpio.c +F: drivers/power/reset/macsmc-reboot.c F: drivers/pwm/pwm-apple.c F: drivers/soc/apple/* F: drivers/spi/spi-apple.c @@ -2373,6 +2427,7 @@ F: drivers/video/backlight/apple_dwi_bl.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/pinctrl/apple.h +F: include/linux/mfd/macsmc.h F: include/linux/soc/apple/* F: include/uapi/drm/asahi_drm.h @@ -2408,12 +2463,20 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) S: Supported Q: https://patchwork.ozlabs.org/project/linux-aspeed/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/bmc/linux.git F: Documentation/devicetree/bindings/arm/aspeed/ F: arch/arm/boot/dts/aspeed/ F: arch/arm/mach-aspeed/ N: aspeed +ARM/AXIADO ARCHITECTURE +M: Harshit Shah +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/arm/axiado.yaml +F: arch/arm64/boot/dts/axiado/ +N: axiado + ARM/AXM LSI SOC M: Krzysztof Kozlowski L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -2473,6 +2536,19 @@ F: arch/arm/boot/compressed/misc-ep93xx.h F: arch/arm/mach-ep93xx/ F: drivers/iio/adc/ep93xx_adc.c +ARM/CIX SOC SUPPORT +M: Peter Chen +M: Fugang Duan +R: CIX Linux Kernel Upstream Group +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git +F: Documentation/devicetree/bindings/arm/cix.yaml +F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml +F: arch/arm64/boot/dts/cix/ +F: drivers/mailbox/cix-mailbox.c +K: \bcix\b + ARM/CLKDEV SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -2522,7 +2598,7 @@ M: Hans Ulli Kroll M: Linus Walleij L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/ulli-kroll/linux.git +T: git https://github.com/ulli-kroll/linux.git F: Documentation/devicetree/bindings/arm/gemini.yaml F: Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml F: Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt @@ -2545,7 +2621,6 @@ F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm F: Documentation/devicetree/bindings/bus/moxtet.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml -F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt F: Documentation/devicetree/bindings/interrupt-controller/marvell,mpic.yaml F: Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml F: Documentation/devicetree/bindings/watchdog/armada-37xx-wdt.txt @@ -2586,6 +2661,8 @@ L: imx@lists.linux.dev L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/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/arm64/boot/dts/freescale/ @@ -2728,7 +2805,7 @@ M: Vladimir Zapolskiy M: Piotr Wojtaszczyk L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/vzapolskiy/linux-lpc32xx.git +T: git https://github.com/vzapolskiy/linux-lpc32xx.git F: Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml F: arch/arm/boot/dts/nxp/lpc/lpc32* F: arch/arm/mach-lpc32xx/ @@ -2788,6 +2865,14 @@ F: drivers/irqchip/irq-mvebu-* F: drivers/pinctrl/mvebu/ F: drivers/rtc/rtc-armada38x.c +ARM/Marvell PXA1908 SOC support +M: Duje Mihanović +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: arch/arm64/boot/dts/marvell/mmp/ +F: drivers/clk/mmp/clk-pxa1908*.c +F: include/dt-bindings/clock/marvell,pxa1908.h + ARM/Mediatek RTC DRIVER M: Eddie Huang M: Sean Wang @@ -2884,7 +2969,7 @@ M: Krzysztof Kozlowski L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Odd Fixes F: Documentation/devicetree/bindings/arm/moxart.yaml -F: Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt +F: Documentation/devicetree/bindings/clock/moxa,moxart-clock.yaml F: arch/arm/boot/dts/moxa/ F: drivers/clk/clk-moxart.c @@ -2894,7 +2979,7 @@ M: Romain Perier L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained W: http://linux-chenxing.org/ -T: git git://github.com/linux-chenxing/linux.git +T: git https://github.com/linux-chenxing/linux.git F: Documentation/devicetree/bindings/arm/mstar/* F: Documentation/devicetree/bindings/clock/mstar,msc313-mpll.yaml F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml @@ -3001,8 +3086,10 @@ R: Ghennadi Procopciuc R: NXP S32 Linux Team L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml F: arch/arm64/boot/dts/freescale/s32g*.dts* F: drivers/pinctrl/nxp/ +F: drivers/rtc/rtc-s32g.c ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER M: Jan Petrous @@ -3193,6 +3280,7 @@ F: arch/arm/mach-exynos*/ F: arch/arm/mach-s3c/ F: arch/arm/mach-s5p*/ F: arch/arm64/boot/dts/exynos/ +F: arch/arm64/boot/dts/tesla/ F: drivers/*/*/*s3c24* F: drivers/*/*s3c24* F: drivers/*/*s3c64xx* @@ -3262,10 +3350,15 @@ M: Dinh Nguyen S: Maintained F: drivers/clk/socfpga/ +ARM/SOCFPGA DWMAC GLUE LAYER BINDINGS +M: Matthew Gerlach +S: Maintained +F: Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml +F: Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml + ARM/SOCFPGA DWMAC GLUE LAYER M: Maxime Chevallier S: Maintained -F: Documentation/devicetree/bindings/net/socfpga-dwmac.txt F: drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c ARM/SOCFPGA EDAC BINDINGS @@ -3375,6 +3468,7 @@ M: linux-fsd@tesla.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-samsung-soc@vger.kernel.org S: Maintained +P: Documentation/process/maintainer-soc-clean-dts.rst F: arch/arm64/boot/dts/tesla/ ARM/TEXAS INSTRUMENT AEMIF/EMIF DRIVERS @@ -3432,7 +3526,7 @@ F: Documentation/devicetree/bindings/arm/ti/nspire.yaml F: arch/arm/boot/dts/nspire/ ARM/TOSHIBA VISCONTI ARCHITECTURE -M: Nobuhiro Iwamatsu +M: Nobuhiro Iwamatsu L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwamatsu/linux-visconti.git @@ -3508,6 +3602,7 @@ F: Documentation/devicetree/bindings/hwinfo/via,vt8500-scc-id.yaml F: Documentation/devicetree/bindings/i2c/wm,wm8505-i2c.yaml F: Documentation/devicetree/bindings/interrupt-controller/via,vt8500-intc.yaml F: Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml +F: Documentation/devicetree/bindings/timer/via,vt8500-timer.yaml F: arch/arm/boot/dts/vt8500/ F: arch/arm/mach-vt8500/ F: drivers/clocksource/timer-vt8500.c @@ -3551,7 +3646,7 @@ F: arch/arm64/boot/Makefile F: scripts/make_fit.py ARM64 PLATFORM DRIVERS -M: Hans de Goede +M: Hans de Goede M: Ilpo Järvinen R: Bryan O'Donoghue L: platform-driver-x86@vger.kernel.org @@ -3572,6 +3667,7 @@ F: drivers/virt/coco/arm-cca-guest/ F: drivers/virt/coco/pkvm-guest/ F: tools/testing/selftests/arm64/ X: arch/arm64/boot/dts/ +X: arch/arm64/configs/defconfig ARROW SPEEDCHIPS XRS7000 SERIES ETHERNET SWITCH DRIVER M: George McCollister @@ -3712,7 +3808,7 @@ F: drivers/platform/x86/asus*.c F: drivers/platform/x86/eeepc*.c ASUS TF103C DOCK DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git @@ -3814,16 +3910,16 @@ ATHEROS 71XX/9XXX GPIO DRIVER M: Alban Bedel S: Maintained W: https://github.com/AlbanBedel/linux -T: git git://github.com/AlbanBedel/linux -F: Documentation/devicetree/bindings/gpio/gpio-ath79.txt +T: git https://github.com/AlbanBedel/linux.git +F: Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml F: drivers/gpio/gpio-ath79.c ATHEROS 71XX/9XXX USB PHY DRIVER M: Alban Bedel S: Maintained W: https://github.com/AlbanBedel/linux -T: git git://github.com/AlbanBedel/linux -F: Documentation/devicetree/bindings/phy/phy-ath79-usb.txt +T: git https://github.com/AlbanBedel/linux.git +F: Documentation/devicetree/bindings/phy/qca,ar7100-usb-phy.yaml F: drivers/phy/qualcomm/phy-ath79-usb.c ATHEROS ATH GENERIC UTILITIES @@ -3887,7 +3983,7 @@ F: drivers/net/ethernet/cadence/ ATMEL MAXTOUCH DRIVER M: Nick Dyer S: Maintained -T: git git://github.com/ndyer/linux.git +T: git https://github.com/ndyer/linux.git F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml F: drivers/input/touchscreen/atmel_mxt_ts.c @@ -4110,7 +4206,7 @@ W: http://www.baycom.org/~tom/ham/ham.html F: drivers/net/hamradio/baycom* BCACHE (BLOCK LAYER CACHE) -M: Coly Li +M: Coly Li M: Kent Overstreet L: linux-bcache@vger.kernel.org S: Maintained @@ -4121,7 +4217,7 @@ F: drivers/md/bcache/ BCACHEFS M: Kent Overstreet L: linux-bcachefs@vger.kernel.org -S: Supported +S: Externally maintained C: irc://irc.oftc.net/bcache P: Documentation/filesystems/bcachefs/SubmittingPatches.rst T: git https://evilpiepirate.org/git/bcachefs.git @@ -4182,6 +4278,7 @@ F: include/linux/cpumask_types.h F: include/linux/find.h F: include/linux/nodemask.h F: include/linux/nodemask_types.h +F: include/uapi/linux/bits.h F: include/vdso/bits.h F: lib/bitmap-str.c F: lib/bitmap.c @@ -4194,6 +4291,7 @@ F: tools/include/linux/bitfield.h F: tools/include/linux/bitmap.h F: tools/include/linux/bits.h F: tools/include/linux/find.h +F: tools/include/uapi/linux/bits.h F: tools/include/vdso/bits.h F: tools/lib/bitmap.c F: tools/lib/find_bit.c @@ -4585,7 +4683,6 @@ F: security/bpf/ BPF [SELFTESTS] (Test Runners & Infrastructure) M: Andrii Nakryiko M: Eduard Zingerman -R: Mykola Lysenko L: bpf@vger.kernel.org S: Maintained F: tools/testing/selftests/bpf/ @@ -4854,6 +4951,12 @@ F: drivers/firmware/broadcom/tee_bnxt_fw.c F: drivers/net/ethernet/broadcom/bnxt/ F: include/linux/firmware/broadcom/tee_bnxt_fw.h +BROADCOM BNG_EN 800 GIGABIT ETHERNET DRIVER +M: Vikas Gupta +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/broadcom/bnge/ + BROADCOM BRCM80211 IEEE802.11 WIRELESS DRIVERS M: Arend van Spriel L: linux-wireless@vger.kernel.org @@ -5086,7 +5189,6 @@ F: include/linux/platform_data/brcmnand.h BROADCOM STB PCIE DRIVER M: Jim Quinlan -M: Nicolas Saenz Julienne M: Florian Fainelli R: Broadcom internal kernel review list L: linux-pci@vger.kernel.org @@ -5156,7 +5258,6 @@ F: drivers/gpio/gpio-bt8xx.c BTRFS FILE SYSTEM M: Chris Mason -M: Josef Bacik M: David Sterba L: linux-btrfs@vger.kernel.org S: Maintained @@ -5569,6 +5670,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git F: drivers/char/ F: drivers/misc/ F: include/linux/miscdevice.h +F: rust/kernel/miscdevice.rs F: samples/rust/rust_misc_device.rs X: drivers/char/agp/ X: drivers/char/hw_random/ @@ -5614,14 +5716,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git F: drivers/usb/chipidea/ CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/input/touchscreen/chipone,icn8318.yaml F: drivers/input/touchscreen/chipone_icn8318.c CHIPONE ICN8505 I2C TOUCHSCREEN DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/chipone_icn8505.c @@ -5742,9 +5844,9 @@ F: drivers/spi/spi-cs42l43* F: include/dt-bindings/sound/cs* F: include/linux/mfd/cs42l43* F: include/sound/cs* -F: sound/pci/hda/cirrus* -F: sound/pci/hda/cs* -F: sound/pci/hda/hda_component* +F: sound/hda/codecs/cirrus* +F: sound/hda/codecs/side-codecs/cs* +F: sound/hda/codecs/side-codecs/hda_component* F: sound/soc/codecs/cs* CIRRUS LOGIC HAPTIC DRIVERS @@ -5980,6 +6082,7 @@ F: include/dt-bindings/clock/ F: include/linux/clk-pr* F: include/linux/clk/ F: include/linux/of_clk.h +F: scripts/gdb/linux/clk.py F: rust/helpers/clk.c F: rust/kernel/clk.rs X: drivers/clk/clkdev.c @@ -6164,9 +6267,11 @@ L: cgroups@vger.kernel.org L: linux-mm@kvack.org S: Maintained F: include/linux/memcontrol.h +F: include/linux/page_counter.h F: mm/memcontrol.c F: mm/memcontrol-v1.c F: mm/memcontrol-v1.h +F: mm/page_counter.c F: mm/swap_cgroup.c F: samples/cgroup/* F: tools/testing/selftests/cgroup/memcg_protection.m @@ -6255,7 +6360,7 @@ F: include/linux/cpuhotplug.h F: include/linux/smpboot.h F: kernel/cpu.c F: kernel/smpboot.* -F: rust/helper/cpu.c +F: rust/helpers/cpu.c F: rust/kernel/cpu.rs CPU IDLE TIME MANAGEMENT FRAMEWORK @@ -6360,10 +6465,8 @@ L: linux-crypto@vger.kernel.org S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git crc-next F: Documentation/staging/crc* -F: arch/*/lib/crc* F: include/linux/crc* -F: lib/crc* -F: lib/tests/crc_kunit.c +F: lib/crc/ F: scripts/gen-crc-consts.py CREATIVE SB0540 @@ -6412,7 +6515,6 @@ L: linux-crypto@vger.kernel.org S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git libcrypto-next T: git https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git libcrypto-fixes -F: arch/*/lib/crypto/ F: lib/crypto/ CRYPTO SPEED TEST COMPARE @@ -6610,7 +6712,7 @@ S: Supported F: drivers/input/keyboard/dlink-dir685-touchkeys.c DALLAS/MAXIM DS1685-FAMILY REAL TIME CLOCK -M: Joshua Kinard +M: Joshua Kinard S: Maintained F: drivers/rtc/rtc-ds1685.c F: include/linux/rtc/ds1685.h @@ -6919,7 +7021,7 @@ F: include/dt-bindings/pmu/exynos_ppmu.h F: include/linux/devfreq-event.h DEVICE RESOURCE MANAGEMENT HELPERS -M: Hans de Goede +M: Hans de Goede R: Matti Vaittinen S: Maintained F: include/linux/devm-helpers.h @@ -7136,15 +7238,15 @@ F: include/linux/swiotlb.h F: kernel/dma/ DMA MAPPING HELPERS DEVICE DRIVER API [RUST] -M: Abdiel Janulgue M: Danilo Krummrich +R: Abdiel Janulgue R: Daniel Almeida R: Robin Murphy R: Andreas Hindborg L: rust-for-linux@vger.kernel.org S: Supported W: https://rust-for-linux.com -T: git https://github.com/Rust-for-Linux/linux.git alloc-next +T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git F: rust/helpers/dma.c F: rust/kernel/dma.rs F: samples/rust/rust_dma.rs @@ -7325,6 +7427,8 @@ M: Arkadiusz Kubalewski M: Jiri Pirko L: netdev@vger.kernel.org S: Supported +F: Documentation/devicetree/bindings/dpll/dpll-device.yaml +F: Documentation/devicetree/bindings/dpll/dpll-pin.yaml F: Documentation/driver-api/dpll.rst F: drivers/dpll/* F: include/linux/dpll.h @@ -7368,6 +7472,7 @@ F: include/linux/property.h F: include/linux/sysfs.h F: lib/kobj* F: rust/kernel/device.rs +F: rust/kernel/device/ F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs @@ -7384,14 +7489,24 @@ F: drivers/soc/ti/smartreflex.c F: include/linux/power/smartreflex.h DRM ACCEL DRIVERS FOR INTEL VPU -M: Jacek Lawrynowicz M: Maciej Falkowski +M: Karol Wachowski L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/accel/ivpu/ F: include/uapi/drm/ivpu_accel.h +DRM ACCEL DRIVER FOR ROCKCHIP NPU +M: Tomeu Vizoso +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/accel/rocket/ +F: Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml +F: drivers/accel/rocket/ +F: include/uapi/drm/rocket_accel.h + DRM COMPUTE ACCELERATORS DRIVERS AND FRAMEWORK M: Oded Gabbay L: dri-devel@lists.freedesktop.org @@ -7520,7 +7635,7 @@ F: drivers/gpu/drm/gud/ F: include/drm/gud.h DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS -M: Hans de Goede +M: Hans de Goede S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/tiny/gm12u320.c @@ -7536,7 +7651,7 @@ F: drivers/gpu/drm/panel/panel-himax-hx8394.c DRM DRIVER FOR HX8357D PANELS S: Orphan T: git https://gitlab.freedesktop.org/drm/misc/kernel.git -F: Documentation/devicetree/bindings/display/himax,hx8357d.txt +F: Documentation/devicetree/bindings/display/himax,hx8357.yaml F: drivers/gpu/drm/tiny/hx8357d.c DRM DRIVER FOR HYPERV SYNTHETIC VIDEO DEVICE @@ -7714,7 +7829,7 @@ Q: https://patchwork.freedesktop.org/project/nouveau/ Q: https://gitlab.freedesktop.org/drm/nouveau/-/merge_requests B: https://gitlab.freedesktop.org/drm/nouveau/-/issues C: irc://irc.oftc.net/nouveau -T: git https://gitlab.freedesktop.org/drm/nouveau.git +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/nouveau/ F: include/uapi/drm/nouveau_drm.h @@ -7760,6 +7875,13 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/repaper.txt F: drivers/gpu/drm/tiny/repaper.c +DRM DRIVER FOR PIXPAPER E-INK PANEL +M: LiangCheng Wang +L: dri-devel@lists.freedesktop.org +S: Maintained +F: Documentation/devicetree/bindings/display/mayqueen,pixpaper.yaml +F: drivers/gpu/drm/tiny/pixpaper.c + DRM DRIVER FOR QEMU'S CIRRUS DEVICE M: Dave Airlie M: Gerd Hoffmann @@ -7830,7 +7952,7 @@ DRM DRIVER FOR SITRONIX ST7586 PANELS M: David Lechner S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git -F: Documentation/devicetree/bindings/display/sitronix,st7586.txt +F: Documentation/devicetree/bindings/display/sitronix,st7586.yaml F: drivers/gpu/drm/sitronix/st7586.c DRM DRIVER FOR SITRONIX ST7571 PANELS @@ -7882,6 +8004,14 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/synaptics,r63353.yaml F: drivers/gpu/drm/panel/panel-synaptics-r63353.c +DRM DRIVER FOR SYNOPSYS DESIGNWARE DISPLAYPORT BRIDGE +M: Andy Yan +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml +F: drivers/gpu/drm/bridge/synopsys/dw-dp.c +F: include/drm/bridge/dw_dp.h + DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE M: Jagan Teki S: Maintained @@ -7922,7 +8052,7 @@ F: drivers/gpu/drm/ci/xfails/vkms* F: drivers/gpu/drm/vkms/ DRM DRIVER FOR VIRTUALBOX VIRTUAL GPU -M: Hans de Goede +M: Hans de Goede L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git @@ -7973,7 +8103,6 @@ F: Documentation/devicetree/bindings/gpu/ F: Documentation/gpu/ F: drivers/gpu/drm/ F: drivers/gpu/vga/ -F: rust/kernel/drm/ F: include/drm/drm F: include/linux/vga* F: include/uapi/drm/ @@ -7985,11 +8114,21 @@ X: drivers/gpu/drm/i915/ X: drivers/gpu/drm/kmb/ X: drivers/gpu/drm/mediatek/ X: drivers/gpu/drm/msm/ -X: drivers/gpu/drm/nouveau/ +X: drivers/gpu/drm/nova/ X: drivers/gpu/drm/radeon/ X: drivers/gpu/drm/tegra/ X: drivers/gpu/drm/xe/ +DRM DRIVERS AND COMMON INFRASTRUCTURE [RUST] +M: Danilo Krummrich +M: Alice Ryhl +S: Supported +W: https://drm.pages.freedesktop.org/maintainer-tools/drm-rust.html +T: git https://gitlab.freedesktop.org/drm/rust/kernel.git +F: drivers/gpu/drm/nova/ +F: drivers/gpu/nova-core/ +F: rust/kernel/drm/ + DRM DRIVERS FOR ALLWINNER A10 M: Maxime Ripard M: Chen-Yu Tsai @@ -8054,7 +8193,7 @@ M: Alison Wang L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git -F: Documentation/devicetree/bindings/display/fsl,dcu.txt +F: Documentation/devicetree/bindings/display/fsl,ls1021a-dcu.yaml F: Documentation/devicetree/bindings/display/fsl,vf610-tcon.yaml F: drivers/gpu/drm/fsl-dcu/ @@ -8320,6 +8459,17 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/scheduler/ F: include/drm/gpu_scheduler.h +DRM GPUVM +M: Danilo Krummrich +R: Matthew Brost +R: Thomas Hellström +R: Alice Ryhl +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_gpuvm.c +F: include/drm/drm_gpuvm.h + DRM LOG M: Jocelyn Falempe M: Javier Martinez Canillas @@ -8360,7 +8510,7 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/drm_panic_qr.rs DRM PRIVACY-SCREEN CLASS -M: Hans de Goede +M: Hans de Goede L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git @@ -9285,6 +9435,10 @@ F: include/linux/fs.h F: include/linux/fs_types.h F: include/uapi/linux/fs.h F: include/uapi/linux/openat2.h +F: rust/kernel/fs.rs +F: rust/kernel/fs/ +F: rust/kernel/seq_file.rs +F: rust/kernel/sync/poll.rs F: Documentation/driver-api/early-userspace/buffer-format.rst F: init/do_mounts* F: init/*initramfs* @@ -9464,7 +9618,7 @@ K: \bunsafe_memcpy\b K: \b__NO_FORTIFY\b FPGA DFL DRIVERS -M: Wu Hao +M: Xu Yilun R: Tom Rix L: linux-fpga@vger.kernel.org S: Maintained @@ -9477,7 +9631,6 @@ F: include/uapi/linux/fpga-dfl.h FPGA MANAGER FRAMEWORK M: Moritz Fischer -M: Wu Hao M: Xu Yilun R: Tom Rix L: linux-fpga@vger.kernel.org @@ -9827,7 +9980,7 @@ F: fs/freevxfs/ FREEZER M: "Rafael J. Wysocki" -M: Pavel Machek +R: Pavel Machek L: linux-pm@vger.kernel.org S: Supported F: Documentation/power/freezing-of-tasks.rst @@ -9983,7 +10136,6 @@ F: drivers/fwctl/mlx5/ FWCTL PDS DRIVER M: Brett Creeley -R: Shannon Nelson L: linux-kernel@vger.kernel.org S: Maintained F: drivers/fwctl/pds/ @@ -10033,8 +10185,6 @@ L: linux-hardening@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening F: Documentation/kbuild/gcc-plugins.rst -F: include/linux/stackleak.h -F: kernel/stackleak.c F: scripts/Makefile.gcc-plugins F: scripts/gcc-plugins/ @@ -10095,6 +10245,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/entry F: include/linux/entry-common.h F: include/linux/entry-kvm.h +F: include/linux/irq-entry-common.h F: kernel/entry/ GENERIC GPIO I2C DRIVER @@ -10264,13 +10415,13 @@ S: Maintained F: Documentation/devicetree/bindings/connector/gocontroll,moduline-module-slot.yaml GOODIX TOUCHSCREEN -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/goodix* GOOGLE ETHERNET DRIVERS -M: Jeroen de Borst +M: Joshua Washington M: Harshitha Ramamurthy L: netdev@vger.kernel.org S: Maintained @@ -10293,6 +10444,7 @@ R: Tudor Ambarus L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-samsung-soc@vger.kernel.org S: Maintained +P: Documentation/process/maintainer-soc-clean-dts.rst C: irc://irc.oftc.net/pixel6-kernel-dev F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml @@ -10303,7 +10455,7 @@ F: include/dt-bindings/clock/google,gs101.h K: [gG]oogle.?[tT]ensor GPD POCKET FAN DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/gpd-pocket-fan.c @@ -10547,7 +10699,8 @@ S: Maintained F: block/partitions/efi.* HABANALABS PCI DRIVER -M: Ofir Bitton +M: Koby Elbaz +M: Konstantin Sinyuk L: dri-devel@lists.freedesktop.org S: Supported C: irc://irc.oftc.net/dri-devel @@ -10704,7 +10857,7 @@ F: drivers/video/fbdev/hgafb.c HIBERNATION (aka Software Suspend, aka swsusp) M: "Rafael J. Wysocki" -M: Pavel Machek +R: Pavel Machek L: linux-pm@vger.kernel.org S: Supported B: https://bugzilla.kernel.org @@ -10882,7 +11035,7 @@ S: Maintained F: drivers/dma/hisi_dma.c HISILICON GPIO DRIVER -M: Jay Fang +M: Yang Shen L: linux-gpio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml @@ -10905,7 +11058,7 @@ F: Documentation/admin-guide/perf/hns3-pmu.rst F: drivers/perf/hisilicon/hns3_pmu.c HISILICON I2C CONTROLLER DRIVER -M: Yicong Yang +M: Devyn Liu L: linux-i2c@vger.kernel.org S: Maintained W: https://www.hisilicon.com @@ -10992,7 +11145,7 @@ F: Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt F: drivers/infiniband/hw/hns/ HISILICON SAS Controller -M: Yihang Li +M: Yihang Li S: Supported W: http://www.hisilicon.com F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt @@ -11049,7 +11202,8 @@ F: Documentation/ABI/testing/debugfs-hisi-zip F: drivers/crypto/hisilicon/zip/ HMM - Heterogeneous Memory Management -M: Jérôme Glisse +M: Jason Gunthorpe +M: Leon Romanovsky L: linux-mm@kvack.org S: Maintained F: Documentation/mm/hmm.rst @@ -11198,7 +11352,8 @@ F: include/linux/platform_data/huawei-gaokun-ec.h HUGETLB SUBSYSTEM M: Muchun Song -R: Oscar Salvador +M: Oscar Salvador +R: David Hildenbrand L: linux-mm@kvack.org S: Maintained F: Documentation/ABI/testing/sysfs-kernel-mm-hugepages @@ -11209,6 +11364,7 @@ F: fs/hugetlbfs/ F: include/linux/hugetlb.h F: include/trace/events/hugetlbfs.h F: mm/hugetlb.c +F: mm/hugetlb_cgroup.c F: mm/hugetlb_cma.c F: mm/hugetlb_cma.h F: mm/hugetlb_vmemmap.c @@ -11327,6 +11483,7 @@ F: drivers/tty/hvc/ HUNG TASK DETECTOR M: Andrew Morton R: Lance Yang +R: Masami Hiramatsu L: linux-kernel@vger.kernel.org S: Maintained F: include/linux/hung_task.h @@ -11464,7 +11621,7 @@ F: drivers/i2c/busses/i2c-via.c F: drivers/i2c/busses/i2c-viapro.c I2C/SMBUS INTEL CHT WHISKEY COVE PMIC DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-cht-wc.c @@ -11501,6 +11658,13 @@ S: Maintained F: Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml F: drivers/i3c/master/i3c-master-cdns.c +I3C DRIVER FOR RENESAS +M: Wolfram Sang +M: Tommaso Merciai +S: Supported +F: Documentation/devicetree/bindings/i3c/renesas,i3c.yaml +F: drivers/i3c/master/renesas-i3c.c + I3C DRIVER FOR SYNOPSYS DESIGNWARE S: Orphan F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml @@ -11511,6 +11675,7 @@ M: Alexandre Belloni R: Frank Li L: linux-i3c@lists.infradead.org (moderated for non-subscribers) S: Maintained +Q: https://patchwork.kernel.org/project/linux-i3c/list/ C: irc://chat.freenode.net/linux-i3c T: git git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux.git F: Documentation/ABI/testing/sysfs-bus-i3c @@ -11655,7 +11820,7 @@ M: Ike Panhc L: platform-driver-x86@vger.kernel.org S: Maintained W: http://launchpad.net/ideapad-laptop -F: drivers/platform/x86/ideapad-laptop.c +F: drivers/platform/x86/lenovo/ideapad-laptop.c IDEAPAD LAPTOP SLIDEBAR DRIVER M: Andrey Moiseev @@ -12054,13 +12219,13 @@ S: Supported F: sound/soc/intel/ INTEL ATOMISP2 DUMMY / POWER-MANAGEMENT DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/intel/atomisp2/pm.c INTEL ATOMISP2 LED DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/intel/atomisp2/led.c @@ -12161,7 +12326,6 @@ F: include/linux/avf/virtchnl.h F: include/linux/net/intel/*/ INTEL ETHERNET PROTOCOL DRIVER FOR RDMA -M: Mustafa Ismail M: Tatyana Nikolova L: linux-rdma@vger.kernel.org S: Supported @@ -12233,9 +12397,8 @@ F: drivers/dma/idxd/* F: include/uapi/linux/idxd.h INTEL IN FIELD SCAN (IFS) DEVICE -M: Jithu Joseph +M: Tony Luck R: Ashok Raj -R: Tony Luck S: Maintained F: drivers/platform/x86/intel/ifs F: include/trace/events/intel_ifs.h @@ -12289,6 +12452,15 @@ T: git git://linuxtv.org/media.git F: Documentation/admin-guide/media/ipu6-isys.rst F: drivers/media/pci/intel/ipu6/ +INTEL IPU7 INPUT SYSTEM DRIVER +M: Sakari Ailus +R: Bingbu Cao +R: Stanislaw Gruszka +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/staging/media/ipu7/ + INTEL ISHTP ECLITE DRIVER M: Srinivas Pandruvada L: platform-driver-x86@vger.kernel.org @@ -12436,6 +12608,8 @@ F: include/linux/mfd/intel_soc_pmic* INTEL PMT DRIVERS M: David E. Box S: Supported +F: Documentation/ABI/testing/sysfs-class-intel_pmt +F: Documentation/ABI/testing/sysfs-class-intel_pmt-features F: drivers/platform/x86/intel/pmt/ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT @@ -12454,10 +12628,9 @@ S: Supported F: drivers/cpufreq/intel_pstate.c INTEL PTP DFL ToD DRIVER -M: Tianfei Zhang L: linux-fpga@vger.kernel.org L: netdev@vger.kernel.org -S: Maintained +S: Orphan F: drivers/ptp/ptp_dfl_tod.c INTEL QUADRATURE ENCODER PERIPHERAL DRIVER @@ -12513,7 +12686,7 @@ L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git F: Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu -F: Documentation/devicetree/bindings/firmware/intel,stratix10-svc.txt +F: Documentation/devicetree/bindings/firmware/intel,stratix10-svc.yaml F: drivers/firmware/stratix10-rsu.c F: drivers/firmware/stratix10-svc.c F: include/linux/firmware/intel/stratix10-smc.h @@ -12571,12 +12744,21 @@ M: Miri Korenblit L: linux-wireless@vger.kernel.org S: Supported W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi -T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git +T: git https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next.git/ F: drivers/net/wireless/intel/iwlwifi/ -INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER -M: Jithu Joseph +INTEL VISION SENSING CONTROLLER DRIVER +M: Sakari Ailus +R: Bingbu Cao +R: Lixu Zhang +R: Stanislaw Gruszka +L: linux-media@vger.kernel.org S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/pci/intel/ivsc/ + +INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER +S: Orphan W: https://slimbootloader.github.io/security/firmware-update.html F: drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -12586,9 +12768,8 @@ S: Maintained F: drivers/platform/x86/intel/wmi/thunderbolt.c INTEL WWAN IOSM DRIVER -M: M Chetan Kumar L: netdev@vger.kernel.org -S: Maintained +S: Orphan F: drivers/net/wwan/iosm/ INTEL(R) FLEXIBLE RETURN AND EVENT DELIVERY @@ -12729,6 +12910,7 @@ F: include/linux/io_uring.h F: include/linux/io_uring_types.h F: include/trace/events/io_uring.h F: include/uapi/linux/io_uring.h +F: include/uapi/linux/io_uring/ F: io_uring/ IPMI SUBSYSTEM @@ -13035,11 +13217,9 @@ F: mm/kasan/ F: scripts/Makefile.kasan KCONFIG -M: Masahiro Yamada L: linux-kbuild@vger.kernel.org -S: Maintained +S: Orphan Q: https://patchwork.kernel.org/project/linux-kbuild/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git kbuild F: Documentation/kbuild/kconfig* F: scripts/Kconfig.include F: scripts/kconfig/ @@ -13104,13 +13284,12 @@ S: Maintained F: fs/autofs/ KERNEL BUILD + files below scripts/ (unless maintained elsewhere) -M: Masahiro Yamada -R: Nathan Chancellor -R: Nicolas Schier +M: Nathan Chancellor +M: Nicolas Schier L: linux-kbuild@vger.kernel.org -S: Maintained +S: Odd Fixes Q: https://patchwork.kernel.org/project/linux-kbuild/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git F: Documentation/kbuild/ F: Makefile F: scripts/*vmlinux* @@ -13135,13 +13314,17 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/har F: Documentation/ABI/testing/sysfs-kernel-oops_count F: Documentation/ABI/testing/sysfs-kernel-warn_count F: arch/*/configs/hardening.config +F: include/linux/kstack_erase.h F: include/linux/overflow.h F: include/linux/randomize_kstack.h F: include/linux/ucopysize.h F: kernel/configs/hardening.config +F: kernel/kstack_erase.c F: lib/tests/randstruct_kunit.c F: lib/tests/usercopy_kunit.c F: mm/usercopy.c +F: scripts/Makefile.kstack_erase +F: scripts/Makefile.randstruct F: security/Kconfig.hardening K: \b(add|choose)_random_kstack_offset\b K: \b__check_(object_size|heap_object)\b @@ -13395,11 +13578,13 @@ M: Alexander Graf M: Mike Rapoport M: Changyuan Lyu L: kexec@lists.infradead.org +L: linux-mm@kvack.org S: Maintained F: Documentation/admin-guide/mm/kho.rst F: Documentation/core-api/kho/* F: include/linux/kexec_handover.h F: kernel/kexec_handover.c +F: tools/testing/selftests/kho/ KEYS-ENCRYPTED M: Mimi Zohar @@ -13544,7 +13729,6 @@ F: scripts/Makefile.kmsan KPROBES M: Naveen N Rao -M: Anil S Keshavamurthy M: "David S. Miller" M: Masami Hiramatsu L: linux-kernel@vger.kernel.org @@ -13721,14 +13905,23 @@ S: Maintained W: http://legousb.sourceforge.net/ F: drivers/usb/misc/legousbtower.c +LENOVO drivers +M: Mark Pearson +M: Derek J. Clark +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: Documentation/wmi/devices/lenovo-wmi-gamezone.rst +F: Documentation/wmi/devices/lenovo-wmi-other.rst +F: drivers/platform/x86/lenovo/* + LENOVO WMI HOTKEY UTILITIES DRIVER M: Jackie Dong L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/platform/x86/lenovo-wmi-hotkey-utilities.c +F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c LETSKETCH HID TABLET DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git @@ -13778,7 +13971,7 @@ F: drivers/ata/sata_gemini.c F: drivers/ata/sata_gemini.h LIBATA SATA AHCI PLATFORM devices support -M: Hans de Goede +M: Hans de Goede L: linux-ide@vger.kernel.org S: Maintained F: drivers/ata/ahci_platform.c @@ -13848,7 +14041,7 @@ M: Oliver O'Halloran L: nvdimm@lists.linux.dev S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ -F: Documentation/devicetree/bindings/pmem/pmem-region.txt +F: Documentation/devicetree/bindings/pmem/pmem-region.yaml F: drivers/nvdimm/of_pmem.c LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM @@ -14108,8 +14301,10 @@ F: Documentation/admin-guide/LSM/LoadPin.rst F: security/loadpin/ LOCKDOWN SECURITY MODULE +M: Nicolas Bouchinet +M: Xiu Jianfeng L: linux-security-module@vger.kernel.org -S: Odd Fixes +S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm.git F: security/lockdown/ @@ -14148,7 +14343,7 @@ F: Documentation/admin-guide/ldm.rst F: block/partitions/ldm.* LOGITECH HID GAMING KEYBOARDS -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git @@ -14225,6 +14420,13 @@ S: Maintained F: Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml F: drivers/soc/loongson/loongson2_guts.c +LOONGSON-2 SOC SERIES MMC/SD/SDIO CONTROLLER DRIVER +M: Binbin Zhou +L: linux-mmc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml +F: drivers/mmc/host/loongson2-mmc.c + LOONGSON-2 SOC SERIES PM DRIVER M: Yinbo Zhu L: linux-pm@vger.kernel.org @@ -14545,7 +14747,7 @@ MARVELL ARMADA 3700 PHY DRIVERS M: Miquel Raynal S: Maintained F: Documentation/devicetree/bindings/phy/marvell,armada-3700-utmi-phy.yaml -F: Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt +F: Documentation/devicetree/bindings/phy/marvell,comphy-cp110.yaml F: drivers/phy/marvell/phy-mvebu-a3700-comphy.c F: drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -14830,7 +15032,7 @@ F: Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml F: drivers/power/supply/max17040_battery.c MAXIM MAX17042 FAMILY FUEL GAUGE DRIVERS -R: Hans de Goede +R: Hans de Goede R: Krzysztof Kozlowski R: Marek Szyprowski R: Sebastian Krzyszkowiak @@ -15514,7 +15716,6 @@ MEDIATEK T7XX 5G WWAN MODEM DRIVER M: Chandrashekar Devegowda R: Chiranjeevi Rapolu R: Liu Haijun -R: M Chetan Kumar R: Ricardo Martinez L: netdev@vger.kernel.org S: Supported @@ -15572,13 +15773,6 @@ S: Supported W: http://www.melexis.com F: drivers/iio/temperature/mlx90635.c -MELFAS MIP4 TOUCHSCREEN DRIVER -M: Sangwon Jee -S: Supported -W: http://www.melfas.com -F: Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt -F: drivers/input/touchscreen/melfas_mip4.c - MELLANOX BLUEFIELD I2C DRIVER M: Khalil Blaiech M: Asmaa Mnebhi @@ -15597,6 +15791,7 @@ F: drivers/net/ethernet/mellanox/mlx4/en_* MELLANOX ETHERNET DRIVER (mlx5e) M: Saeed Mahameed M: Tariq Toukan +M: Mark Bloch L: netdev@vger.kernel.org S: Maintained W: https://www.nvidia.com/networking/ @@ -15632,7 +15827,7 @@ Q: https://patchwork.kernel.org/project/netdevbpf/list/ F: drivers/net/ethernet/mellanox/mlxfw/ MELLANOX HARDWARE PLATFORM SUPPORT -M: Hans de Goede +M: Hans de Goede M: Ilpo Järvinen M: Vadim Pasternak L: platform-driver-x86@vger.kernel.org @@ -15666,6 +15861,7 @@ MELLANOX MLX5 core VPI driver M: Saeed Mahameed M: Leon Romanovsky M: Tariq Toukan +M: Mark Bloch L: netdev@vger.kernel.org L: linux-rdma@vger.kernel.org S: Maintained @@ -15723,11 +15919,16 @@ MEMBLOCK AND MEMORY MANAGEMENT INITIALIZATION M: Mike Rapoport L: linux-mm@kvack.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git for-next +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git fixes F: Documentation/core-api/boot-time-mm.rst F: Documentation/core-api/kho/bindings/memblock/* F: include/linux/memblock.h +F: mm/bootmem_info.c F: mm/memblock.c +F: mm/memtest.c F: mm/mm_init.c +F: mm/rodata_test.c F: tools/testing/memblock/ MEMORY ALLOCATION PROFILING @@ -15768,6 +15969,8 @@ F: Documentation/admin-guide/mm/memory-hotplug.rst F: Documentation/core-api/memory-hotplug.rst F: drivers/base/memory.c F: include/linux/memory_hotplug.h +F: include/linux/memremap.h +F: mm/memremap.c F: mm/memory_hotplug.c F: tools/testing/selftests/memory-hotplug/ @@ -15778,24 +15981,8 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm T: quilt git://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new -F: Documentation/admin-guide/mm/ -F: Documentation/mm/ -F: include/linux/gfp.h -F: include/linux/gfp_types.h -F: include/linux/memfd.h -F: include/linux/memory_hotplug.h -F: include/linux/memory-tiers.h -F: include/linux/mempolicy.h -F: include/linux/mempool.h -F: include/linux/memremap.h -F: include/linux/mmzone.h -F: include/linux/mmu_notifier.h -F: include/linux/pagewalk.h -F: include/trace/events/ksm.h F: mm/ F: tools/mm/ -F: tools/testing/selftests/mm/ -N: include/linux/page[-_]* MEMORY MANAGEMENT - CORE M: Andrew Morton @@ -15810,18 +15997,40 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: include/linux/gfp.h +F: include/linux/gfp_types.h +F: include/linux/highmem.h F: include/linux/memory.h F: include/linux/mm.h F: include/linux/mm_*.h +F: include/linux/mmzone.h F: include/linux/mmdebug.h +F: include/linux/mmu_notifier.h F: include/linux/pagewalk.h +F: include/linux/pgtable.h +F: include/linux/ptdump.h +F: include/linux/vmpressure.h +F: include/linux/vmstat.h F: kernel/fork.c F: mm/Kconfig F: mm/debug.c +F: mm/folio-compat.c +F: mm/highmem.c F: mm/init-mm.c +F: mm/internal.h +F: mm/maccess.c F: mm/memory.c +F: mm/mmu_notifier.c +F: mm/mmzone.c F: mm/pagewalk.c +F: mm/pgtable-generic.c +F: mm/ptdump.c +F: mm/sparse-vmemmap.c +F: mm/sparse.c F: mm/util.c +F: mm/vmpressure.c +F: mm/vmstat.c +N: include/linux/page[-_]* MEMORY MANAGEMENT - EXECMEM M: Andrew Morton @@ -15842,6 +16051,10 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: mm/gup.c +F: mm/gup_test.c +F: mm/gup_test.h +F: tools/testing/selftests/mm/gup_longterm.c +F: tools/testing/selftests/mm/gup_test.c MEMORY MANAGEMENT - KSM (Kernel Samepage Merging) M: Andrew Morton @@ -15857,6 +16070,7 @@ F: Documentation/mm/ksm.rst F: include/linux/ksm.h F: include/trace/events/ksm.h F: mm/ksm.c +F: mm/mm_slot.h MEMORY MANAGEMENT - MEMORY POLICY AND MIGRATION M: Andrew Morton @@ -15874,21 +16088,88 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: include/linux/mempolicy.h +F: include/uapi/linux/mempolicy.h F: include/linux/migrate.h +F: include/linux/migrate_mode.h F: mm/mempolicy.c F: mm/migrate.c F: mm/migrate_device.c +MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU) +M: Andrew Morton +M: Axel Rasmussen +M: Yuanchu Xie +R: Wei Xu +L: linux-mm@kvack.org +S: Maintained +W: http://www.linux-mm.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/admin-guide/mm/multigen_lru.rst +F: Documentation/mm/multigen_lru.rst +F: include/linux/mm_inline.h +F: include/linux/mmzone.h +F: mm/swap.c +F: mm/vmscan.c +F: mm/workingset.c + +MEMORY MANAGEMENT - MISC +M: Andrew Morton +M: David Hildenbrand +R: Lorenzo Stoakes +R: Liam R. Howlett +R: Vlastimil Babka +R: Mike Rapoport +R: Suren Baghdasaryan +R: Michal Hocko +L: linux-mm@kvack.org +S: Maintained +W: http://www.linux-mm.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/admin-guide/mm/ +F: Documentation/mm/ +F: include/linux/cma.h +F: include/linux/dmapool.h +F: include/linux/ioremap.h +F: include/linux/memory-tiers.h +F: include/linux/page_idle.h +F: mm/backing-dev.c +F: mm/cma.c +F: mm/cma_debug.c +F: mm/cma_sysfs.c +F: mm/dmapool.c +F: mm/dmapool_test.c +F: mm/early_ioremap.c +F: mm/fadvise.c +F: mm/ioremap.c +F: mm/mapping_dirty_helpers.c +F: mm/memory-tiers.c +F: mm/page_idle.c +F: mm/pgalloc-track.h +F: mm/process_vm_access.c +F: tools/testing/selftests/mm/ + MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION M: Andrew Morton M: Mike Rapoport L: linux-mm@kvack.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git F: include/linux/numa_memblks.h F: mm/numa.c F: mm/numa_emulation.c F: mm/numa_memblks.c +MEMORY MANAGEMENT - OOM KILLER +M: Michal Hocko +R: David Rientjes +R: Shakeel Butt +L: linux-mm@kvack.org +S: Maintained +F: include/linux/oom.h +F: include/trace/events/oom.h +F: include/uapi/linux/oom.h +F: mm/oom_kill.c + MEMORY MANAGEMENT - PAGE ALLOCATOR M: Andrew Morton M: Vlastimil Babka @@ -15903,8 +16184,20 @@ F: include/linux/compaction.h F: include/linux/gfp.h F: include/linux/page-isolation.h F: mm/compaction.c +F: mm/debug_page_alloc.c +F: mm/debug_page_ref.c +F: mm/fail_page_alloc.c F: mm/page_alloc.c +F: mm/page_ext.c +F: mm/page_frag_cache.c F: mm/page_isolation.c +F: mm/page_owner.c +F: mm/page_poison.c +F: mm/page_reporting.c +F: mm/page_reporting.h +F: mm/show_mem.c +F: mm/shuffle.c +F: mm/shuffle.h MEMORY MANAGEMENT - RECLAIM M: Andrew Morton @@ -15918,6 +16211,7 @@ L: linux-mm@kvack.org S: Maintained F: mm/pt_reclaim.c F: mm/vmscan.c +F: mm/workingset.c MEMORY MANAGEMENT - RMAP (REVERSE MAPPING) M: Andrew Morton @@ -15930,6 +16224,7 @@ R: Harry Yoo L: linux-mm@kvack.org S: Maintained F: include/linux/rmap.h +F: mm/page_vma_mapped.c F: mm/rmap.c MEMORY MANAGEMENT - SECRETMEM @@ -15962,9 +16257,9 @@ F: mm/swapfile.c MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) M: Andrew Morton M: David Hildenbrand +M: Lorenzo Stoakes R: Zi Yan R: Baolin Wang -R: Lorenzo Stoakes R: Liam R. Howlett R: Nico Pache R: Ryan Roberts @@ -15980,6 +16275,7 @@ F: include/linux/khugepaged.h F: include/trace/events/huge_memory.h F: mm/huge_memory.c F: mm/khugepaged.c +F: mm/mm_slot.h F: tools/testing/selftests/mm/khugepaged.c F: tools/testing/selftests/mm/split_huge_page_test.c F: tools/testing/selftests/mm/transhuge-stress.c @@ -16007,8 +16303,10 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: rust/helpers/mm.c +F: rust/helpers/page.c F: rust/kernel/mm.rs F: rust/kernel/mm/ +F: rust/kernel/page.rs MEMORY MAPPING M: Andrew Morton @@ -16022,11 +16320,15 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: include/trace/events/mmap.h +F: mm/interval_tree.c +F: mm/mincore.c F: mm/mlock.c F: mm/mmap.c F: mm/mprotect.c F: mm/mremap.c F: mm/mseal.c +F: mm/msync.c +F: mm/nommu.c F: mm/vma.c F: mm/vma.h F: mm/vma_exec.c @@ -16513,6 +16815,14 @@ L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/microchip/ +MICROCHIP ZL3073X DRIVER +M: Ivan Vecera +M: Prathosh Satish +L: netdev@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml +F: drivers/dpll/zl3073x/ + MICROSEMI MIPS SOCS M: Alexandre Belloni M: UNGLinuxDriver@microchip.com @@ -16589,7 +16899,7 @@ S: Maintained F: drivers/platform/surface/surface_gpe.c MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT -M: Hans de Goede +M: Hans de Goede M: Ilpo Järvinen M: Maximilian Luz L: platform-driver-x86@vger.kernel.org @@ -16834,8 +17144,8 @@ F: include/dt-bindings/clock/mobileye,eyeq5-clk.h MODULE SUPPORT M: Luis Chamberlain M: Petr Pavlu +M: Daniel Gomez R: Sami Tolvanen -R: Daniel Gomez L: linux-modules@vger.kernel.org L: linux-kernel@vger.kernel.org S: Maintained @@ -17195,6 +17505,8 @@ F: drivers/net/ethernet/neterion/ NETFILTER M: Pablo Neira Ayuso M: Jozsef Kadlecsik +M: Florian Westphal +R: Phil Sutter L: netfilter-devel@vger.kernel.org L: coreteam@netfilter.org S: Maintained @@ -17234,10 +17546,10 @@ F: drivers/rtc/rtc-ntxec.c F: include/linux/mfd/ntxec.h NETRONOME ETHERNET DRIVERS -M: Louis Peens R: Jakub Kicinski +R: Simon Horman L: oss-drivers@corigine.com -S: Maintained +S: Odd Fixes F: drivers/net/ethernet/netronome/ NETWORK BLOCK DEVICE (NBD) @@ -17394,6 +17706,7 @@ F: include/linux/ethtool.h F: include/linux/framer/framer-provider.h F: include/linux/framer/framer.h F: include/linux/in.h +F: include/linux/in6.h F: include/linux/indirect_call_wrapper.h F: include/linux/inet.h F: include/linux/inet_diag.h @@ -17563,9 +17876,9 @@ F: net/ipv6/syncookies.c F: net/ipv6/tcp*.c NETWORKING [TLS] -M: Boris Pismenny M: John Fastabend M: Jakub Kicinski +M: Sabrina Dubroca L: netdev@vger.kernel.org S: Maintained F: include/net/tls.h @@ -17757,7 +18070,7 @@ F: tools/include/nolibc/ F: tools/testing/selftests/nolibc/ NOVATEK NVT-TS I2C TOUCHSCREEN DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/input/touchscreen/novatek,nvt-ts.yaml @@ -17957,6 +18270,7 @@ NXP i.MX 8M ISI DRIVER M: Laurent Pinchart L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/fsl,imx8*-isi.yaml F: Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml F: drivers/media/platform/nxp/imx8-isi/ @@ -18002,9 +18316,9 @@ L: linux-clk@vger.kernel.org L: imx@lists.linux.dev S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux.git clk/imx -F: Documentation/devicetree/bindings/clock/imx* +F: Documentation/devicetree/bindings/clock/*imx* F: drivers/clk/imx/ -F: include/dt-bindings/clock/imx* +F: include/dt-bindings/clock/*imx* NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER M: Jagan Teki @@ -18629,6 +18943,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git F: Documentation/ABI/testing/sysfs-firmware-ofw F: drivers/of/ F: include/linux/of*.h +F: rust/helpers/of.c F: rust/kernel/of.rs F: scripts/dtc/ F: tools/testing/selftests/dt/ @@ -18830,6 +19145,11 @@ F: Documentation/mm/page_table_check.rst F: include/linux/page_table_check.h F: mm/page_table_check.c +PAGE STATE DEBUG SCRIPT +M: Ye Liu +S: Maintained +F: tools/mm/show_page_info.py + PANASONIC LAPTOP ACPI EXTRAS DRIVER M: Kenneth Chan L: platform-driver-x86@vger.kernel.org @@ -18949,7 +19269,7 @@ M: Pali Rohár L: linux-pci@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/pci/aardvark-pci.txt +F: Documentation/devicetree/bindings/pci/marvell,armada-3700-pcie.yaml F: drivers/pci/controller/pci-aardvark.c PCI DRIVER FOR ALTERA PCIE IP @@ -18964,7 +19284,7 @@ M: Toan Le L: linux-pci@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/pci/xgene-pci.txt +F: Documentation/devicetree/bindings/pci/apm,xgene-pcie.yaml F: drivers/pci/controller/pci-xgene.c PCI DRIVER FOR ARM VERSATILE PLATFORM @@ -19197,7 +19517,7 @@ M: Toan Le L: linux-pci@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt +F: Documentation/devicetree/bindings/interrupt-controller/apm,xgene1-msi.yaml F: drivers/pci/controller/pci-xgene-msi.c PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS @@ -19283,7 +19603,7 @@ PCIE DRIVER FOR AMAZON ANNAPURNA LABS M: Jonathan Chocron L: linux-pci@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/pci/pcie-al.txt +F: Documentation/devicetree/bindings/pci/amazon,al-alpine-v3-pcie.yaml F: drivers/pci/controller/dwc/pcie-al.c PCIE DRIVER FOR AMLOGIC MESON @@ -19427,7 +19747,7 @@ F: crypto/pcrypt.c F: include/crypto/pcrypt.h PDS DSC VIRTIO DATA PATH ACCELERATOR -R: Shannon Nelson +R: Brett Creeley F: drivers/vdpa/pds/ PECI HARDWARE MONITORING DRIVERS @@ -19449,7 +19769,6 @@ F: include/linux/peci-cpu.h F: include/linux/peci.h PENSANDO ETHERNET DRIVERS -M: Shannon Nelson M: Brett Creeley L: netdev@vger.kernel.org S: Maintained @@ -19467,6 +19786,7 @@ F: arch/*/include/asm/percpu.h F: include/linux/percpu*.h F: lib/percpu*.c F: mm/percpu*.c +F: mm/percpu-internal.h PER-TASK DELAY ACCOUNTING M: Balbir Singh @@ -19475,6 +19795,16 @@ S: Maintained F: include/linux/delayacct.h F: kernel/delayacct.c +TASK DELAY MONITORING TOOLS +M: Andrew Morton +M: Wang Yaxin +M: Fan Yu +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/accounting/delay-accounting.rst +F: tools/accounting/delaytop.c +F: tools/accounting/getdelays.c + PERFORMANCE EVENTS SUBSYSTEM M: Peter Zijlstra M: Ingo Molnar @@ -19614,8 +19944,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/pinctrl/intel.git F: drivers/pinctrl/intel/ PIN CONTROLLER - KEEMBAY -M: Lakshmi Sowjanya D -S: Supported +S: Orphan F: drivers/pinctrl/pinctrl-keembay* PIN CONTROLLER - MEDIATEK @@ -19712,13 +20041,6 @@ S: Supported F: Documentation/devicetree/bindings/input/pine64,pinephone-keyboard.yaml F: drivers/input/keyboard/pinephone-keyboard.c -PKTCDVD DRIVER -M: linux-block@vger.kernel.org -S: Orphan -F: drivers/block/pktcdvd.c -F: include/linux/pktcdvd.h -F: include/uapi/linux/pktcdvd.h - PLANTOWER PMS7003 AIR POLLUTION SENSOR DRIVER M: Tomasz Duszynski S: Maintained @@ -19750,7 +20072,7 @@ L: linux-pm@vger.kernel.org S: Supported W: https://01.org/pm-graph B: https://bugzilla.kernel.org/buglist.cgi?component=pm-graph&product=Tools -T: git git://github.com/intel/pm-graph +T: git https://github.com/intel/pm-graph.git F: tools/power/pm-graph PM6764TR DRIVER @@ -19780,7 +20102,7 @@ F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.yaml F: drivers/iio/magnetometer/rm3100* PNP SUPPORT -M: "Rafael J. Wysocki" +M: "Rafael J. Wysocki" L: linux-acpi@vger.kernel.org S: Maintained F: drivers/pnp/ @@ -20141,8 +20463,8 @@ M: Haojian Zhuang M: Robert Jarzmik L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/hzhuang1/linux.git -T: git git://github.com/rjarzmik/linux.git +T: git https://github.com/hzhuang1/linux.git +T: git https://github.com/rjarzmik/linux.git F: arch/arm/boot/dts/intel/pxa/ F: arch/arm/mach-pxa/ F: drivers/dma/pxa* @@ -20168,21 +20490,15 @@ S: Supported F: Documentation/devicetree/bindings/soc/qcom/qcom,apr* F: Documentation/devicetree/bindings/sound/qcom,* F: drivers/soc/qcom/apr.c -F: include/dt-bindings/sound/qcom,wcd9335.h -F: include/dt-bindings/sound/qcom,wcd934x.h -F: sound/soc/codecs/lpass-rx-macro.* -F: sound/soc/codecs/lpass-tx-macro.* -F: sound/soc/codecs/lpass-va-macro.c -F: sound/soc/codecs/lpass-wsa-macro.* +F: drivers/soundwire/qcom.c +F: include/dt-bindings/sound/qcom,wcd93* +F: sound/soc/codecs/lpass-*.* F: sound/soc/codecs/msm8916-wcd-analog.c F: sound/soc/codecs/msm8916-wcd-digital.c F: sound/soc/codecs/wcd-clsh-v2.* F: sound/soc/codecs/wcd-mbhc-v2.* -F: sound/soc/codecs/wcd9335.* -F: sound/soc/codecs/wcd934x.c -F: sound/soc/codecs/wsa881x.c -F: sound/soc/codecs/wsa883x.c -F: sound/soc/codecs/wsa884x.c +F: sound/soc/codecs/wcd93*.* +F: sound/soc/codecs/wsa88*.* F: sound/soc/qcom/ QCOM EMBEDDED USB DEBUGGER (EUD) @@ -20207,12 +20523,6 @@ S: Maintained F: drivers/firmware/qemu_fw_cfg.c F: include/uapi/linux/qemu_fw_cfg.h -QIB DRIVER -M: Dennis Dalessandro -L: linux-rdma@vger.kernel.org -S: Supported -F: drivers/infiniband/hw/qib/ - QLOGIC QL41xxx FCOE DRIVER M: Saurav Kashyap M: Javed Hasan @@ -20376,7 +20686,7 @@ QUALCOMM ATHEROS QCA7K ETHERNET DRIVER M: Stefan Wahren L: netdev@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/qca,qca7000.txt +F: Documentation/devicetree/bindings/net/qca,qca7000.yaml F: drivers/net/ethernet/qualcomm/qca* QUALCOMM BAM-DMUX WWAN NETWORK DRIVER @@ -20391,6 +20701,7 @@ QUALCOMM CAMERA SUBSYSTEM DRIVER M: Robert Foss M: Todor Tomov M: Bryan O'Donoghue +R: Vladimir Zapolskiy L: linux-media@vger.kernel.org S: Maintained F: Documentation/admin-guide/media/qcom_camss.rst @@ -20408,7 +20719,7 @@ F: include/dt-bindings/clock/qcom,* QUALCOMM CLOUD AI (QAIC) DRIVER M: Jeff Hugo -R: Carl Vanderlip +R: Carl Vanderlip L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Supported @@ -20575,6 +20886,13 @@ S: Maintained F: Documentation/devicetree/bindings/mtd/qcom,nandc.yaml F: drivers/mtd/nand/raw/qcom_nandc.c +QUALCOMM SMB CHARGER DRIVER +M: Casey Connolly +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml +F: drivers/power/supply/qcom_smbx.c + QUALCOMM QSEECOM DRIVER M: Maximilian Luz L: linux-arm-msm@vger.kernel.org @@ -20588,8 +20906,8 @@ S: Maintained F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c QUALCOMM RMNET DRIVER -M: Subash Abhinov Kasiviswanathan -M: Sean Tranchetti +M: Subash Abhinov Kasiviswanathan +M: Sean Tranchetti L: netdev@vger.kernel.org S: Maintained F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst @@ -20789,6 +21107,14 @@ S: Maintained F: Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml F: drivers/media/platform/raspberrypi/rp1-cfe/ +RASPBERRY PI RP1 PCI DRIVER +M: Andrea della Porta +S: Maintained +F: arch/arm64/boot/dts/broadcom/rp1*.dts* +F: drivers/clk/clk-rp1.c +F: drivers/misc/rp1/ +F: drivers/pinctrl/pinctrl-rp1.c + RC-CORE / LIRC FRAMEWORK M: Sean Young L: linux-media@vger.kernel.org @@ -21214,9 +21540,17 @@ M: Lad Prabhakar L: netdev@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/renesas,r9a09g057-gbeth.yaml +F: Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c +RENESAS RZ/V2H(P) RSPI DRIVER +M: Fabrizio Castro +L: linux-spi@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml +F: drivers/spi/spi-rzv2h-rspi.c + RENESAS RZ/V2H(P) USB2PHY PORT RESET DRIVER M: Fabrizio Castro M: Lad Prabhakar @@ -21337,6 +21671,15 @@ F: drivers/irqchip/irq-riscv-intc.c F: include/linux/irqchip/riscv-aplic.h F: include/linux/irqchip/riscv-imsic.h +RISC-V ANDES SoC Support +M: Ben Zong-You Xie +S: Maintained +T: git: https://github.com/ben717-linux/linux +F: Documentation/devicetree/bindings/interrupt-controller/andestech,plicsw.yaml +F: Documentation/devicetree/bindings/riscv/andes.yaml +F: Documentation/devicetree/bindings/timer/andestech,plmt0.yaml +F: arch/riscv/boot/dts/andes/ + RISC-V ARCHITECTURE M: Paul Walmsley M: Palmer Dabbelt @@ -21426,7 +21769,7 @@ N: spacemit K: spacemit RISC-V THEAD SoC SUPPORT -M: Drew Fustini +M: Drew Fustini M: Guo Ren M: Fu Wei L: linux-riscv@lists.infradead.org @@ -21445,6 +21788,7 @@ F: drivers/mailbox/mailbox-th1520.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c F: drivers/pinctrl/pinctrl-th1520.c F: drivers/pmdomain/thead/ +F: drivers/power/sequencing/pwrseq-thead-gpu.c F: drivers/reset/reset-th1520.c F: include/dt-bindings/clock/thead,th1520-clk-ap.h F: include/dt-bindings/power/thead,th1520-power.h @@ -21518,6 +21862,14 @@ S: Maintained F: Documentation/devicetree/bindings/media/rockchip-rga.yaml F: drivers/media/platform/rockchip/rga/ +ROCKCHIP RKVDEC VIDEO DECODER DRIVER +M: Detlev Casanova +L: linux-media@vger.kernel.org +L: linux-rockchip@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/media/rockchip,vdec.yaml +F: drivers/media/platform/rockchip/rkvdec/ + ROCKCHIP RK3308 INTERNAL AUDIO CODEC M: Luca Ceresoli S: Maintained @@ -21760,6 +22112,10 @@ K: \b(?i:rust)\b RUST [ALLOC] M: Danilo Krummrich +R: Lorenzo Stoakes +R: Vlastimil Babka +R: Liam R. Howlett +R: Uladzislau Rezki L: rust-for-linux@vger.kernel.org S: Maintained T: git https://github.com/Rust-for-Linux/linux.git alloc-next @@ -21876,7 +22232,7 @@ F: arch/s390/mm S390 NETWORK DRIVERS M: Alexandra Winter -M: Thorsten Winkler +R: Aswin Karuvally L: linux-s390@vger.kernel.org L: netdev@vger.kernel.org S: Supported @@ -21993,6 +22349,16 @@ B: mailto:linux-samsung-soc@vger.kernel.org F: Documentation/devicetree/bindings/sound/samsung* F: sound/soc/samsung/ +SAMSUNG EXYNOS2200 SoC SUPPORT +M: Ivaylo Ivanov +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: linux-samsung-soc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/clock/samsung,exynos2200-cmu.yaml +F: arch/arm64/boot/dts/exynos/exynos2200* +F: drivers/clk/samsung/clk-exynos2200.c +F: include/dt-bindings/clock/samsung,exynos2200-cmu.h + SAMSUNG EXYNOS850 SoC SUPPORT M: Sam Protsenko L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -22217,13 +22583,14 @@ F: include/linux/wait.h F: include/uapi/linux/sched.h F: kernel/fork.c F: kernel/sched/ +F: tools/sched/ SCHEDULER - SCHED_EXT R: Tejun Heo R: David Vernet R: Andrea Righi R: Changwoo Min -L: linux-kernel@vger.kernel.org +L: sched-ext@lists.linux.dev S: Maintained W: https://github.com/sched-ext/scx T: git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git @@ -22350,6 +22717,17 @@ M: Jim Cromie S: Maintained F: drivers/clocksource/scx200_hrt.c +SDCA LIBRARY AND CLASS DRIVER +M: Charles Keepax +M: Maciej Strozek +R: Bard Liao +R: Pierre-Louis Bossart +L: linux-sound@vger.kernel.org +L: patches@opensource.cirrus.com +S: Maintained +F: include/sound/sdca* +F: sound/soc/sdca/* + SDRICOH_CS MMC/SD HOST CONTROLLER INTERFACE DRIVER M: Sascha Sommer L: sdricohcs-devel@lists.sourceforge.net (subscribers-only) @@ -22602,9 +22980,11 @@ S: Maintained F: drivers/misc/sgi-xp/ SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS +M: D. Wythe +M: Dust Li +M: Sidraya Jayagond M: Wenjia Zhang -M: Jan Karcher -R: D. Wythe +R: Mahanta Jambigi R: Tony Lu R: Wen Gu L: linux-rdma@vger.kernel.org @@ -22637,7 +23017,9 @@ R: Muchun Song L: linux-mm@kvack.org S: Maintained F: Documentation/admin-guide/mm/shrinker_debugfs.rst +F: include/linux/list_lru.h F: include/linux/shrinker.h +F: mm/list_lru.c F: mm/shrinker.c F: mm/shrinker_debug.c @@ -22760,7 +23142,7 @@ K: fu[57]40 K: [^@]sifive SILEAD TOUCHSCREEN DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org L: platform-driver-x86@vger.kernel.org S: Maintained @@ -22793,7 +23175,7 @@ F: Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml F: drivers/i3c/master/svc-i3c-master.c SIMPLEFB FB DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-fbdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/display/simple-framebuffer.yaml @@ -22869,17 +23251,24 @@ F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml F: drivers/nvmem/layouts/sl28vpd.c SLAB ALLOCATOR -M: Christoph Lameter -M: David Rientjes -M: Andrew Morton M: Vlastimil Babka +M: Andrew Morton +R: Christoph Lameter +R: David Rientjes R: Roman Gushchin R: Harry Yoo L: linux-mm@kvack.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git -F: include/linux/sl?b*.h -F: mm/sl?b* +F: Documentation/admin-guide/mm/slab.rst +F: Documentation/mm/slab.rst +F: include/linux/mempool.h +F: include/linux/slab.h +F: mm/failslab.c +F: mm/mempool.c +F: mm/slab.h +F: mm/slab_common.c +F: mm/slub.c SLCAN CAN NETWORK DRIVER M: Dario Binacchi @@ -22905,7 +23294,7 @@ M: Casey Schaufler L: linux-security-module@vger.kernel.org S: Maintained W: http://schaufler-ca.com -T: git git://github.com/cschaufler/smack-next +T: git https://github.com/cschaufler/smack-next.git F: Documentation/admin-guide/LSM/Smack.rst F: security/smack/ @@ -22922,7 +23311,7 @@ F: Documentation/hwmon/emc2103.rst F: drivers/hwmon/emc2103.c SMSC SCH5627 HARDWARE MONITOR DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/hwmon/sch5627.rst @@ -23010,7 +23399,7 @@ S: Maintained F: drivers/leds/leds-net48xx.c SOFT-IWARP DRIVER (siw) -M: Bernard Metzler +M: Bernard Metzler L: linux-rdma@vger.kernel.org S: Supported F: drivers/infiniband/sw/siw/ @@ -23065,6 +23454,7 @@ F: drivers/md/md* F: drivers/md/raid* F: include/linux/raid/ F: include/uapi/linux/raid/ +F: lib/raid6/ SOLIDRUN CLEARFOG SUPPORT M: Russell King @@ -23331,7 +23721,6 @@ SOUNDWIRE SUBSYSTEM M: Vinod Koul M: Bard Liao R: Pierre-Louis Bossart -R: Sanyog Kale L: linux-sound@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire.git @@ -23429,7 +23818,7 @@ F: include/linux/mtd/spi-nor.h SPI OFFLOAD R: David Lechner -F: drivers/spi/spi-offload-trigger-pwm.c +F: drivers/spi/spi-offload-trigger-*.c F: drivers/spi/spi-offload.c F: include/linux/spi/offload/ K: spi_offload @@ -23510,6 +23899,12 @@ F: drivers/bus/stm32_etzpc.c F: drivers/bus/stm32_firewall.c F: drivers/bus/stm32_rifsc.c +ST STM32 HDP PINCTRL DRIVER +M: Clément Le Goffic +S: Maintained +F: Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml +F: drivers/pinctrl/stm32/pinctrl-stm32-hdp.c + ST STM32 I2C/SMBUS DRIVER M: Pierre-Yves MORDRET M: Alain Volmat @@ -23523,6 +23918,14 @@ S: Maintained F: Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml F: drivers/memory/stm32_omm.c +ST STM32 PINCTRL DRIVER +M: Antonio Borneo +S: Maintained +F: Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml +F: drivers/pinctrl/stm32/ +F: include/dt-bindings/pinctrl/stm32-pinfunc.h +X: drivers/pinctrl/stm32/pinctrl-stm32-hdp.c + ST STM32 SPI DRIVER M: Alain Volmat L: linux-spi@vger.kernel.org @@ -23577,7 +23980,7 @@ S: Supported F: Documentation/process/stable-kernel-rules.rst STAGING - ATOMISP DRIVER -M: Hans de Goede +M: Hans de Goede M: Mauro Carvalho Chehab R: Sakari Ailus L: linux-media@vger.kernel.org @@ -23588,7 +23991,6 @@ STAGING - INDUSTRIAL IO M: Jonathan Cameron L: linux-iio@vger.kernel.org S: Odd Fixes -F: Documentation/devicetree/bindings/staging/iio/ F: drivers/staging/iio/ STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec) @@ -23873,12 +24275,18 @@ F: arch/m68k/sun3*/ F: drivers/net/ethernet/i825xx/sun3* SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml F: drivers/input/keyboard/sun4i-lradc-keys.c +SUNDANCE NETWORK DRIVER +M: Denis Kirjanov +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/dlink/sundance.c + SUNPLUS ETHERNET DRIVER M: Wells Lu L: netdev@vger.kernel.org @@ -23955,8 +24363,8 @@ F: drivers/sh/ SUSPEND TO RAM M: "Rafael J. Wysocki" -M: Len Brown -M: Pavel Machek +R: Len Brown +R: Pavel Machek L: linux-pm@vger.kernel.org S: Supported B: https://bugzilla.kernel.org @@ -24044,7 +24452,6 @@ F: drivers/reset/reset-axs10x.c SYNOPSYS CREG GPIO DRIVER M: Eugeniy Paltsev S: Maintained -F: Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt F: drivers/gpio/gpio-creg-snps.c SYNOPSYS DESIGNWARE 8250 UART DRIVER @@ -24115,6 +24522,7 @@ M: Bin Du L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-designware-amdisp.c +F: include/linux/soc/amd/isp4_misc.h SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER M: Jaehoon Chung @@ -24554,7 +24962,7 @@ F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml F: Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml F: include/sound/tas2*.h F: include/sound/tlv320*.h -F: sound/pci/hda/tas2781_hda_i2c.c +F: sound/hda/codecs/side-codecs/tas2781_hda_i2c.c F: sound/soc/codecs/pcm1681.c F: sound/soc/codecs/pcm1789*.* F: sound/soc/codecs/pcm179x*.* @@ -24602,7 +25010,7 @@ TEXAS INSTRUMENTS TPS6131X FLASH LED DRIVER M: Matthias Fend L: linux-leds@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/leds/ti,tps6131x.yaml +F: Documentation/devicetree/bindings/leds/ti,tps61310.yaml F: drivers/leds/flash/leds-tps6131x.c TEXAS INSTRUMENTS' DAC7612 DAC DRIVER @@ -24717,14 +25125,14 @@ S: Maintained W: http://ibm-acpi.sourceforge.net W: http://thinkwiki.org/wiki/Ibm-acpi T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git -F: drivers/platform/x86/thinkpad_acpi.c +F: drivers/platform/x86/lenovo/thinkpad_acpi.c THINKPAD LMI DRIVER -M: Mark Pearson +M: Mark Pearson L: platform-driver-x86@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-class-firmware-attributes -F: drivers/platform/x86/think-lmi.? +F: drivers/platform/x86/lenovo/think-lmi.? THP7312 ISP DRIVER M: Laurent Pinchart @@ -25079,8 +25487,11 @@ M: Hugh Dickins R: Baolin Wang L: linux-mm@kvack.org S: Maintained +F: include/linux/memfd.h F: include/linux/shmem_fs.h +F: mm/memfd.c F: mm/shmem.c +F: mm/shmem_quota.c TOMOYO SECURITY MODULE M: Kentaro Takeda @@ -25158,6 +25569,13 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/toshiba-wmi.c +TOUCH OVERLAY +M: Javier Carrasco +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/touch-overlay.c +F: include/linux/input/touch-overlay.h + TPM DEVICE DRIVER M: Peter Huewe M: Jarkko Sakkinen @@ -25244,12 +25662,18 @@ TRADITIONAL CHINESE DOCUMENTATION M: Hu Haowen <2023002089@link.tyut.edu.cn> S: Maintained W: https://github.com/srcres258/linux-doc -T: git git://github.com/srcres258/linux-doc.git doc-zh-tw +T: git https://github.com/srcres258/linux-doc.git doc-zh-tw F: Documentation/translations/zh_TW/ -TRIGGER SOURCE - PWM +TRIGGER SOURCE - ADI UTIL SIGMA DELTA SPI M: David Lechner S: Maintained +F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml + +TRIGGER SOURCE +M: David Lechner +S: Maintained +F: Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml F: Documentation/devicetree/bindings/trigger-source/pwm-trigger.yaml TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE @@ -25641,7 +26065,7 @@ F: Documentation/hid/hiddev.rst F: drivers/hid/usbhid/ USB INTEL XHCI ROLE MUX DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -25832,7 +26256,7 @@ F: Documentation/firmware-guide/acpi/intel-pmc-mux.rst F: drivers/usb/typec/mux/intel_pmc_mux.c USB TYPEC PI3USB30532 MUX DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/typec/mux/pi3usb30532.c @@ -25861,12 +26285,13 @@ F: drivers/usb/host/uhci* USB VIDEO CLASS M: Laurent Pinchart -M: Hans de Goede +M: Hans de Goede L: linux-media@vger.kernel.org S: Maintained W: http://www.ideasonboard.org/uvc/ T: git git://linuxtv.org/media.git F: Documentation/userspace-api/media/drivers/uvcvideo.rst +F: Documentation/userspace-api/media/v4l/metafmt-uvc-msxu-1-5.rst F: Documentation/userspace-api/media/v4l/metafmt-uvc.rst F: drivers/media/common/uvc.c F: drivers/media/usb/uvc/ @@ -25915,6 +26340,8 @@ F: fs/hostfs/ USERSPACE COPYIN/COPYOUT (UIOVEC) M: Alexander Viro +L: linux-block@vger.kernel.org +L: linux-fsdevel@vger.kernel.org S: Maintained F: include/linux/uio.h F: lib/iov_iter.c @@ -25936,6 +26363,13 @@ F: Documentation/driver-api/uio-howto.rst F: drivers/uio/ F: include/linux/uio_driver.h +USERSPACE STACK UNWINDING +M: Josh Poimboeuf +M: Steven Rostedt +S: Maintained +F: include/linux/unwind*.h +F: kernel/unwind/ + UTIL-LINUX PACKAGE M: Karel Zak L: util-linux@vger.kernel.org @@ -26098,7 +26532,6 @@ S: Maintained F: drivers/vfio/platform/ VFIO QAT PCI DRIVER -M: Xin Zeng M: Giovanni Cabiddu L: kvm@vger.kernel.org L: qat-linux@intel.com @@ -26392,7 +26825,7 @@ F: include/uapi/linux/virtio_snd.h F: sound/virtio/* VIRTUAL BOX GUEST DEVICE DRIVER -M: Hans de Goede +M: Hans de Goede M: Arnd Bergmann M: Greg Kroah-Hartman S: Maintained @@ -26401,7 +26834,7 @@ F: include/linux/vbox_utils.h F: include/uapi/linux/vbox*.h VIRTUAL BOX SHARED FOLDER VFS DRIVER -M: Hans de Goede +M: Hans de Goede L: linux-fsdevel@vger.kernel.org S: Maintained F: fs/vboxsf/* @@ -26463,6 +26896,7 @@ F: include/uapi/linux/vm_sockets.h F: include/uapi/linux/vm_sockets_diag.h F: include/uapi/linux/vsockmon.h F: net/vmw_vsock/ +F: tools/testing/selftests/vsock/ F: tools/testing/vsock/ VMALLOC @@ -26574,6 +27008,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git F: Documentation/devicetree/bindings/regulator/ F: Documentation/power/regulator/ F: drivers/regulator/ +F: rust/kernel/regulator.rs F: include/dt-bindings/regulator/ F: include/linux/regulator/ K: regulator_get_optional @@ -26655,7 +27090,7 @@ F: drivers/mmc/host/wbsd.* WACOM PROTOCOL 4 SERIAL TABLETS M: Julian Squires -M: Hans de Goede +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained F: drivers/input/tablet/wacom_serial4.c @@ -26822,7 +27257,7 @@ F: include/linux/wwan.h F: include/uapi/linux/wwan.h X-POWERS AXP288 PMIC DRIVERS -M: Hans de Goede +M: Hans de Goede S: Maintained F: drivers/acpi/pmic/intel_pmic_xpower.c N: axp288 @@ -26914,14 +27349,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/mm F: arch/x86/mm/ X86 PLATFORM ANDROID TABLETS DSDT FIXUP DRIVER -M: Hans de Goede +M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git F: drivers/platform/x86/x86-android-tablets/ X86 PLATFORM DRIVERS -M: Hans de Goede +M: Hans de Goede M: Ilpo Järvinen L: platform-driver-x86@vger.kernel.org S: Maintained @@ -26952,7 +27387,7 @@ F: arch/x86/kernel/stacktrace.c F: arch/x86/kernel/unwind_*.c X86 TRUST DOMAIN EXTENSIONS (TDX) -M: Kirill A. Shutemov +M: Kirill A. Shutemov R: Dave Hansen L: x86@kernel.org L: linux-coco@lists.linux.dev @@ -27321,13 +27756,6 @@ S: Supported W: http://www.marvell.com F: drivers/i2c/busses/i2c-xlp9xx.c -XRA1403 GPIO EXPANDER -M: Nandor Han -L: linux-gpio@vger.kernel.org -S: Maintained -F: Documentation/devicetree/bindings/gpio/gpio-xra1403.txt -F: drivers/gpio/gpio-xra1403.c - XTENSA XTFPGA PLATFORM SUPPORT M: Max Filippov S: Maintained @@ -27458,6 +27886,7 @@ L: linux-mm@kvack.org S: Maintained F: Documentation/mm/zsmalloc.rst F: include/linux/zsmalloc.h +F: mm/zpdesc.h F: mm/zsmalloc.c ZSTD @@ -27491,7 +27920,7 @@ SENARYTECH AUDIO CODEC DRIVER M: bo liu S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git -F: sound/pci/hda/patch_senarytech.c +F: sound/hda/codecs/senarytech.c THE REST M: Linus Torvalds diff --git a/Makefile b/Makefile index ba0827a1fccd..9771619ac596 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 -PATCHLEVEL = 16 +PATCHLEVEL = 17 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc6 NAME = Baby Opossum Posse # *DOCUMENTATION* @@ -479,11 +479,17 @@ export rust_common_flags := --edition=2021 \ -Wrust_2018_idioms \ -Wunreachable_pub \ -Wclippy::all \ + -Wclippy::as_ptr_cast_mut \ + -Wclippy::as_underscore \ + -Wclippy::cast_lossless \ -Wclippy::ignored_unit_patterns \ -Wclippy::mut_mut \ -Wclippy::needless_bitwise_bool \ -Aclippy::needless_lifetimes \ -Wclippy::no_mangle_with_rust_abi \ + -Wclippy::ptr_as_ptr \ + -Wclippy::ptr_cast_constness \ + -Wclippy::ref_as_ptr \ -Wclippy::undocumented_unsafe_blocks \ -Wclippy::unnecessary_safety_comment \ -Wclippy::unnecessary_safety_doc \ @@ -543,6 +549,7 @@ LZMA = lzma LZ4 = lz4 XZ = xz ZSTD = zstd +TAR = tar CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF) @@ -622,7 +629,7 @@ export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN export HOSTRUSTC KBUILD_HOSTRUSTFLAGS export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX -export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD +export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD TAR export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS KBUILD_PROCMACROLDFLAGS LDFLAGS_MODULE export KBUILD_USERCFLAGS KBUILD_USERLDFLAGS @@ -1086,6 +1093,7 @@ include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan include-$(CONFIG_KCOV) += scripts/Makefile.kcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct +include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase include-$(CONFIG_AUTOFDO_CLANG) += scripts/Makefile.autofdo include-$(CONFIG_PROPELLER_CLANG) += scripts/Makefile.propeller include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins @@ -1134,7 +1142,7 @@ KBUILD_USERCFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CPPFLAGS) $(KBUILD KBUILD_USERLDFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) # userspace programs are linked via the compiler, use the correct linker -ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_LD_IS_LLD),yy) +ifdef CONFIG_CC_IS_CLANG KBUILD_USERLDFLAGS += --ld-path=$(LD) endif diff --git a/arch/Kconfig b/arch/Kconfig index a3308a220f86..d1b4ffd6e085 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -64,8 +64,17 @@ config HOTPLUG_PARALLEL bool select HOTPLUG_SPLIT_STARTUP +config GENERIC_IRQ_ENTRY + bool + +config GENERIC_SYSCALL + bool + depends on GENERIC_IRQ_ENTRY + config GENERIC_ENTRY bool + select GENERIC_IRQ_ENTRY + select GENERIC_SYSCALL config KPROBES bool "Kprobes" @@ -435,6 +444,13 @@ config HAVE_HARDLOCKUP_DETECTOR_ARCH It uses the same command line parameters, and sysctl interface, as the generic hardlockup detectors. +config UNWIND_USER + bool + +config HAVE_UNWIND_USER_FP + bool + select UNWIND_USER + config HAVE_PERF_REGS bool help @@ -630,11 +646,11 @@ config SECCOMP_CACHE_DEBUG If unsure, say N. -config HAVE_ARCH_STACKLEAK +config HAVE_ARCH_KSTACK_ERASE bool help An architecture should select this if it has the code which - fills the used part of the kernel stack with the STACKLEAK_POISON + fills the used part of the kernel stack with the KSTACK_ERASE_POISON value before returning from system calls. config HAVE_STACKPROTECTOR @@ -1763,4 +1779,7 @@ config ARCH_WANTS_PRE_LINK_VMLINUX An architecture can select this if it provides arch//tools/Makefile with .arch.vmlinux.o target to be linked into vmlinux. +config ARCH_HAS_CPU_ATTACK_VECTORS + bool + endmenu diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 109a4cddcd13..80367f2cf821 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -7,6 +7,7 @@ config ALPHA select ARCH_HAS_DMA_OPS if PCI select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO + select ARCH_MODULE_NEEDS_WEAK_PER_CPU if SMP select ARCH_NO_PREEMPT select ARCH_NO_SG_CHAIN select ARCH_USE_CMPXCHG_LOCKREF diff --git a/arch/alpha/include/asm/param.h b/arch/alpha/include/asm/param.h deleted file mode 100644 index cfe947ce9461..000000000000 --- a/arch/alpha/include/asm/param.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_ALPHA_PARAM_H -#define _ASM_ALPHA_PARAM_H - -#include - -# undef HZ -# define HZ CONFIG_HZ -# define USER_HZ 1024 -# define CLOCKS_PER_SEC USER_HZ /* frequency at which times() counts */ - -#endif /* _ASM_ALPHA_PARAM_H */ diff --git a/arch/alpha/include/asm/percpu.h b/arch/alpha/include/asm/percpu.h index 6923249f2d49..4383d66341dc 100644 --- a/arch/alpha/include/asm/percpu.h +++ b/arch/alpha/include/asm/percpu.h @@ -9,10 +9,9 @@ * way above 4G. * * Always use weak definitions for percpu variables in modules. + * Therefore, we have enabled CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU + * in the Kconfig. */ -#if defined(MODULE) && defined(CONFIG_SMP) -#define ARCH_NEEDS_WEAK_PER_CPU -#endif #include diff --git a/arch/alpha/include/uapi/asm/param.h b/arch/alpha/include/uapi/asm/param.h index 49c7119934e2..e4e410f9bf85 100644 --- a/arch/alpha/include/uapi/asm/param.h +++ b/arch/alpha/include/uapi/asm/param.h @@ -2,14 +2,9 @@ #ifndef _UAPI_ASM_ALPHA_PARAM_H #define _UAPI_ASM_ALPHA_PARAM_H -#define HZ 1024 - +#define __USER_HZ 1024 #define EXEC_PAGESIZE 8192 -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ +#include #endif /* _UAPI_ASM_ALPHA_PARAM_H */ diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 8f1f18adcdb5..5ef57f88df6b 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -152,6 +152,9 @@ #define SO_PASSRIGHTS 83 +#define SO_INQ 84 +#define SCM_INQ SO_INQ + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c index b1bfbd11980d..d38f4d6759e4 100644 --- a/arch/alpha/kernel/core_marvel.c +++ b/arch/alpha/kernel/core_marvel.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -79,10 +80,12 @@ mk_resource_name(int pe, int port, char *str) { char tmp[80]; char *name; - - sprintf(tmp, "PCI %s PE %d PORT %d", str, pe, port); - name = memblock_alloc_or_panic(strlen(tmp) + 1, SMP_CACHE_BYTES); - strcpy(name, tmp); + size_t sz; + + sz = scnprintf(tmp, sizeof(tmp), "PCI %s PE %d PORT %d", str, pe, port); + sz += 1; /* NUL terminator */ + name = memblock_alloc_or_panic(sz, SMP_CACHE_BYTES); + strscpy(name, tmp, sz); return name; } diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl index 2dd6340de6b4..16dca28ebf17 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl @@ -507,3 +507,5 @@ 575 common listxattrat sys_listxattrat 576 common removexattrat sys_removexattrat 577 common open_tree_attr sys_open_tree_attr +578 common file_getattr sys_file_getattr +579 common file_setattr sys_file_setattr diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index e0c233c178b1..cad5367b7c37 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -284,7 +284,7 @@ enum arc_getset { static const struct user_regset arc_regsets[] = { [REGSET_CMN] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), @@ -293,7 +293,7 @@ static const struct user_regset arc_regsets[] = { }, #ifdef CONFIG_ISA_ARCV2 [REGSET_ARCV2] = { - .core_note_type = NT_ARC_V2, + USER_REGSET_NOTE_TYPE(ARC_V2), .n = ELF_ARCV2REG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3072731fe09c..b1f3df39ed40 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -8,8 +8,6 @@ config ARM select ARCH_HAS_CACHE_LINE_SIZE if OF select ARCH_HAS_CPU_CACHE_ALIASING select ARCH_HAS_CPU_FINALIZE_INIT if MMU - select ARCH_HAS_CRC32 if KERNEL_MODE_NEON - select ARCH_HAS_CRC_T10DIF if KERNEL_MODE_NEON select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DMA_ALLOC if MMU @@ -87,11 +85,11 @@ config ARM select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_PFN_VALID select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER if AEABI && !OABI_COMPAT - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE @@ -102,12 +100,12 @@ config ARM select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_DEBUG_KMEMLEAK if !XIP_KERNEL select HAVE_DMA_CONTIGUOUS if MMU + select HAVE_EXTRA_IPI_TRACEPOINTS select HAVE_DYNAMIC_FTRACE if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU select HAVE_EXIT_THREAD select HAVE_GUP_FAST if ARM_LPAE - select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER if !XIP_KERNEL @@ -121,7 +119,7 @@ config ARM select HAVE_KERNEL_XZ select HAVE_KPROBES if !XIP_KERNEL && !CPU_ENDIAN_BE32 && !CPU_V7M select HAVE_KRETPROBES if HAVE_KPROBES - select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if (LD_VERSION >= 23600 || LD_CAN_USE_KEEP_IN_OVERLAY) + select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if (LD_VERSION >= 23600 || LD_IS_LLD) && LD_CAN_USE_KEEP_IN_OVERLAY select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI select HAVE_OPTPROBES if !THUMB2_KERNEL diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 4808d3ed98e4..e31e95ffd33f 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -149,7 +149,7 @@ endif # Need -Uarm for gcc < 3.x KBUILD_CPPFLAGS +=$(cpp-y) KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm -KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) -Wa,$(arch-y) $(tune-y) -include asm/unified.h -msoft-float +KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) -Wa,$(arch-y) $(tune-y) -include $(srctree)/arch/arm/include/asm/unified.h -msoft-float KBUILD_RUSTFLAGS += --target=arm-unknown-linux-gnueabi CHECKFLAGS += -D__arm__ diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index d61369b1eabe..a159120d1e42 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -9,7 +9,6 @@ OBJS = HEAD = head.o OBJS += misc.o decompress.o -CFLAGS_decompress.o += $(DISABLE_STACKLEAK_PLUGIN) ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y) OBJS += debug.o AFLAGS_head.o += -DDEBUG @@ -96,6 +95,7 @@ KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING ccflags-y := -fpic $(call cc-option,-mno-single-pic-base,) -fno-builtin \ -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ + $(DISABLE_KSTACK_ERASE) \ -I$(obj) ccflags-remove-$(CONFIG_FUNCTION_TRACER) += -pg asflags-y := -DZIMAGE diff --git a/arch/arm/boot/dts/allwinner/sun8i-v3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-v3.dtsi index 186c30cbe6ee..95bd0b616349 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-v3.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-v3.dtsi @@ -56,6 +56,15 @@ i2s0_pins: i2s0-pins { function = "i2s"; }; + /omit-if-no-ref/ + lcd_rgb666_pd_pins: lcd-rgb666-pd-pins { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", + "PD6", "PD7", "PD8", "PD9", "PD10", "PD11", + "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", + "PD18", "PD19", "PD20", "PD21"; + function = "lcd"; + }; + uart1_pg_pins: uart1-pg-pins { pins = "PG6", "PG7"; function = "uart1"; diff --git a/arch/arm/boot/dts/allwinner/sun8i-v3s.dtsi b/arch/arm/boot/dts/allwinner/sun8i-v3s.dtsi index f909b1d4dbca..fa54510319ac 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-v3s.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-v3s.dtsi @@ -411,6 +411,15 @@ i2c1_pe_pins: i2c1-pe-pins { function = "i2c1"; }; + /omit-if-no-ref/ + lcd_rgb666_pe_pins: lcd-rgb666-pe-pins { + pins = "PE0", "PE1", "PE2", "PE3", "PE4", "PE5", + "PE6", "PE7", "PE8", "PE9", "PE10", "PE11", + "PE12", "PE13", "PE14", "PE15", "PE16", "PE17", + "PE18", "PE19", "PE23", "PE24"; + function = "lcd"; + }; + uart0_pb_pins: uart0-pb-pins { pins = "PB8", "PB9"; function = "uart0"; @@ -652,7 +661,7 @@ csi1: camera@1cb4000 { reg = <0x01cb4000 0x3000>; interrupts = ; clocks = <&ccu CLK_BUS_CSI>, - <&ccu CLK_CSI1_SCLK>, + <&ccu CLK_CSI_SCLK>, <&ccu CLK_DRAM_CSI>; clock-names = "bus", "mod", "ram"; resets = <&ccu RST_BUS_CSI>; diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile index 2e5f4833a073..aba7451ab749 100644 --- a/arch/arm/boot/dts/aspeed/Makefile +++ b/arch/arm/boot/dts/aspeed/Makefile @@ -27,6 +27,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-facebook-harma.dtb \ aspeed-bmc-facebook-minerva.dtb \ aspeed-bmc-facebook-minipack.dtb \ + aspeed-bmc-facebook-santabarbara.dtb \ aspeed-bmc-facebook-tiogapass.dtb \ aspeed-bmc-facebook-wedge40.dtb \ aspeed-bmc-facebook-wedge100.dtb \ @@ -50,12 +51,12 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-lenovo-hr630.dtb \ aspeed-bmc-lenovo-hr855xg2.dtb \ aspeed-bmc-microsoft-olympus.dtb \ + aspeed-bmc-nvidia-gb200nvl-bmc.dtb \ aspeed-bmc-opp-lanyang.dtb \ aspeed-bmc-opp-mowgli.dtb \ aspeed-bmc-opp-nicole.dtb \ aspeed-bmc-opp-palmetto.dtb \ aspeed-bmc-opp-romulus.dtb \ - aspeed-bmc-opp-swift.dtb \ aspeed-bmc-opp-tacoma.dtb \ aspeed-bmc-opp-vesnin.dtb \ aspeed-bmc-opp-witherspoon.dtb \ diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ampere-mtjade.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ampere-mtjade.dts index 31c5d319aa0a..263702599767 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ampere-mtjade.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ampere-mtjade.dts @@ -825,7 +825,7 @@ ocp-aux-pwren-hog { line-name = "ocp-aux-pwren"; }; - bmc-ready { + bmc-ready-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-arm-stardragon4800-rep2.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-arm-stardragon4800-rep2.dts index 29c68c37e7f5..9605ccade155 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-arm-stardragon4800-rep2.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-arm-stardragon4800-rep2.dts @@ -201,13 +201,13 @@ &gfx { }; &gpio { - pin_gpio_c7 { + pin-gpio-c7-hog { gpio-hog; gpios = ; output-low; line-name = "BIOS_SPI_MUX_S"; }; - pin_gpio_d1 { + pin-gpio-d1-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-e3c246d4i.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-e3c246d4i.dts index bb2e6ef609af..93190f4e696c 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-e3c246d4i.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-e3c246d4i.dts @@ -182,7 +182,7 @@ &gpio { "CK_33M_BMC", "LFRAME", "SERIRQ", "S_PLTRST"; /* Assert BMC_READY so BIOS doesn't sit around waiting for it */ - bmc-ready { + bmc-ready-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-bytedance-g220a.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-bytedance-g220a.dts index 3f03a198a1a8..54a5509b04f1 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-bytedance-g220a.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-bytedance-g220a.dts @@ -915,14 +915,14 @@ fan@5 { }; &gpio { - pin_gpio_i3 { + pin-gpio-i3-hog { gpio-hog; gpios = ; output-low; line-name = "NCSI_BMC_R_SEL"; }; - pin_gpio_b6 { + pin-gpio-b6-hog { gpio-hog; gpios = ; output-low; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-delta-ahe50dc.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-delta-ahe50dc.dts index b6bfdaea08e6..cce8d0416dc8 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-delta-ahe50dc.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-delta-ahe50dc.dts @@ -395,7 +395,7 @@ &gpio { * back to one causes a power output glitch, so install a hog to keep * it at one as a failsafe to ensure nothing accidentally touches it. */ - doom-guardrail { + doom-guardrail-hog { gpio-hog; gpios = ; output-low; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-bletchley.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-bletchley.dts index 5be0e8fd2633..24969c82d05e 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-bletchley.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-bletchley.dts @@ -52,10 +52,6 @@ tpm@0 { }; }; - switchphy: ethernet-phy@0 { - // Fixed link - }; - front_gpio_leds { compatible = "gpio-leds"; sys_log_id { @@ -285,7 +281,6 @@ vbus_sled6: vbus_sled6 { &mac2 { status = "okay"; phy-mode = "rgmii"; - phy-handle = <&switchphy>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_rgmii3_default>; @@ -398,10 +393,13 @@ sled1_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; @@ -484,10 +482,13 @@ sled2_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; @@ -570,10 +571,13 @@ sled3_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; @@ -656,10 +660,13 @@ sled4_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; @@ -742,10 +749,13 @@ sled5_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; @@ -828,10 +838,13 @@ sled6_fusb302: typec-portc@22 { connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; - data-role = "host"; - pd-disable; - typec-power-opmode = "default"; + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x20>; + power-role = "dual"; + try-power-role = "sink"; + data-role = "dual"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; }; }; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-catalina.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-catalina.dts index c151984289bc..8d786510167f 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-catalina.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-catalina.dts @@ -186,18 +186,29 @@ flash@1 { &i2c0 { status = "okay"; + multi-master; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; + }; i2c-mux@71 { compatible = "nxp,pca9546"; reg = <0x71>; #address-cells = <1>; #size-cells = <0>; - i2c-mux-idle-disconnect; i2c0mux0ch0: i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0>; + mctp-controller; + + // IOB0 NIC0 TEMP + temperature-sensor@1f { + compatible = "ti,tmp421"; + reg = <0x1f>; + }; }; i2c0mux0ch1: i2c@1 { #address-cells = <1>; @@ -208,6 +219,13 @@ i2c0mux0ch2: i2c@2 { #address-cells = <1>; #size-cells = <0>; reg = <2>; + mctp-controller; + + // IOB0 NIC1 TEMP + temperature-sensor@1f { + compatible = "ti,tmp421"; + reg = <0x1f>; + }; }; i2c0mux0ch3: i2c@3 { #address-cells = <1>; @@ -293,12 +311,18 @@ i2c-mux@75 { reg = <0x75>; #address-cells = <1>; #size-cells = <0>; - i2c-mux-idle-disconnect; i2c0mux3ch0: i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0>; + mctp-controller; + + // IOB1 NIC0 TEMP + temperature-sensor@1f { + compatible = "ti,tmp421"; + reg = <0x1f>; + }; }; i2c0mux3ch1: i2c@1 { #address-cells = <1>; @@ -309,6 +333,13 @@ i2c0mux3ch2: i2c@2 { #address-cells = <1>; #size-cells = <0>; reg = <2>; + mctp-controller; + + // IOB1 NIC1 TEMP + temperature-sensor@1f { + compatible = "ti,tmp421"; + reg = <0x1f>; + }; }; i2c0mux3ch3: i2c@3 { #address-cells = <1>; @@ -404,40 +435,105 @@ i2c1mux0ch0: i2c@0 { #size-cells = <0>; reg = <0x0>; - power-sensor@41 { - compatible = "ti,ina238"; - reg = <0x41>; - shunt-resistor = <500>; - }; - power-sensor@42 { - compatible = "ti,ina238"; - reg = <0x42>; - shunt-resistor = <500>; - }; - power-sensor@44 { - compatible = "ti,ina238"; - reg = <0x44>; - shunt-resistor = <500>; + power-sensor@22 { + compatible = "mps,mp5990"; + reg = <0x22>; }; }; i2c1mux0ch1: i2c@1 { #address-cells = <1>; #size-cells = <0>; reg = <0x1>; - - power-sensor@41 { - compatible = "ti,ina238"; - reg = <0x41>; - }; - power-sensor@43 { - compatible = "ti,ina238"; - reg = <0x43>; - }; }; i2c1mux0ch2: i2c@2 { #address-cells = <1>; #size-cells = <0>; reg = <0x2>; + + fanctl2: fan-controller@1 { + compatible = "nuvoton,nct7363"; + reg = <0x01>; + #pwm-cells = <2>; + + fan-9 { + pwms = <&fanctl2 0 40000>; + tach-ch = /bits/ 8 <0x09>; + }; + fan-11 { + pwms = <&fanctl2 0 40000>; + tach-ch = /bits/ 8 <0x0b>; + }; + fan-10 { + pwms = <&fanctl2 4 40000>; + tach-ch = /bits/ 8 <0x0a>; + }; + fan-13 { + pwms = <&fanctl2 4 40000>; + tach-ch = /bits/ 8 <0x0d>; + }; + fan-15 { + pwms = <&fanctl2 6 40000>; + tach-ch = /bits/ 8 <0x0f>; + }; + fan-1 { + pwms = <&fanctl2 6 40000>; + tach-ch = /bits/ 8 <0x01>; + }; + fan-0 { + pwms = <&fanctl2 10 40000>; + tach-ch = /bits/ 8 <0x00>; + }; + fan-3 { + pwms = <&fanctl2 10 40000>; + tach-ch = /bits/ 8 <0x03>; + }; + }; + fanctl3: fan-controller@2 { + compatible = "nuvoton,nct7363"; + reg = <0x02>; + #pwm-cells = <2>; + + fan-9 { + pwms = <&fanctl3 0 40000>; + tach-ch = /bits/ 8 <0x09>; + }; + fan-11 { + pwms = <&fanctl3 0 40000>; + tach-ch = /bits/ 8 <0x0b>; + }; + fan-10 { + pwms = <&fanctl3 4 40000>; + tach-ch = /bits/ 8 <0x0a>; + }; + fan-13 { + pwms = <&fanctl3 4 40000>; + tach-ch = /bits/ 8 <0x0d>; + }; + fan-15 { + pwms = <&fanctl3 6 40000>; + tach-ch = /bits/ 8 <0x0f>; + }; + fan-1 { + pwms = <&fanctl3 6 40000>; + tach-ch = /bits/ 8 <0x01>; + }; + fan-0 { + pwms = <&fanctl3 10 40000>; + tach-ch = /bits/ 8 <0x00>; + }; + fan-3 { + pwms = <&fanctl3 10 40000>; + tach-ch = /bits/ 8 <0x03>; + }; + }; + fanctl0: fan-controller@21{ + compatible = "maxim,max31790"; + reg = <0x21>; + }; + fanctl1: fan-controller@27{ + compatible = "maxim,max31790"; + reg = <0x27>; + }; }; i2c1mux0ch3: i2c@3 { #address-cells = <1>; @@ -449,6 +545,14 @@ i2c1mux0ch4: i2c@4 { #size-cells = <0>; reg = <0x4>; + power-monitor@13 { + compatible = "infineon,xdp710"; + reg = <0x13>; + }; + power-monitor@1c { + compatible = "infineon,xdp710"; + reg = <0x1c>; + }; power-monitor@42 { compatible = "lltc,ltc4287"; reg = <0x42>; @@ -520,6 +624,12 @@ temperature-sensor@4b { compatible = "ti,tmp75"; reg = <0x4b>; }; + + // FIO REMOTE TEMP SENSOR + temperature-sensor@4f { + compatible = "ti,tmp75"; + reg = <0x4f>; + }; }; }; }; @@ -626,27 +736,6 @@ i2c5mux0ch7: i2c@7 { #address-cells = <1>; #size-cells = <0>; reg = <7>; - - power-sensor@40 { - compatible = "ti,ina230"; - reg = <0x40>; - shunt-resistor = <2000>; - }; - power-sensor@41 { - compatible = "ti,ina230"; - reg = <0x41>; - shunt-resistor = <2000>; - }; - power-sensor@44 { - compatible = "ti,ina230"; - reg = <0x44>; - shunt-resistor = <2000>; - }; - power-sensor@45 { - compatible = "ti,ina230"; - reg = <0x45>; - shunt-resistor = <2000>; - }; }; }; }; @@ -708,6 +797,12 @@ eeprom@56 { &i2c10 { status = "okay"; + multi-master; + mctp-controller; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; + }; // OCP NIC0 TEMP temperature-sensor@1f { @@ -733,16 +828,24 @@ ssif-bmc@10 { &i2c12 { status = "okay"; + multi-master; // Module 1 FRU EEPROM eeprom@50 { compatible = "atmel,24c64"; reg = <0x50>; }; + + // Secondary CBC FRU EEPROM + eeprom@54 { + compatible = "atmel,24c02"; + reg = <0x54>; + }; }; &i2c13 { status = "okay"; + multi-master; // Module 0 FRU EEPROM eeprom@50 { @@ -750,18 +853,12 @@ eeprom@50 { reg = <0x50>; }; - // Left CBC FRU EEPROM + // Primary CBC FRU EEPROM eeprom@54 { compatible = "atmel,24c02"; reg = <0x54>; }; - // Right CBC FRU EEPROM - eeprom@55 { - compatible = "atmel,24c02"; - reg = <0x55>; - }; - // HMC FRU EEPROM eeprom@57 { compatible = "atmel,24c02"; @@ -835,6 +932,12 @@ io_expander14: gpio@15 { &i2c15 { status = "okay"; + multi-master; + mctp-controller; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; + }; // OCP NIC1 TEMP temperature-sensor@1f { diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-harma.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-harma.dts index 9cb511a846e3..b9a93f23bd0a 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-harma.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-harma.dts @@ -218,6 +218,25 @@ temperature-sensor@4b { compatible = "ti,tmp75"; reg = <0x4b>; }; + + gpio@12 { + compatible = "nxp,pca9555"; + reg = <0x12>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <116 IRQ_TYPE_LEVEL_LOW>; + + gpio-line-names = + "","", + "","", + "","", + "","", + "","", + "","", + "","fcb1-activate", + "",""; + }; }; &i2c1 { @@ -273,6 +292,25 @@ temperature-sensor@4b { compatible = "ti,tmp75"; reg = <0x4b>; }; + + gpio@12 { + compatible = "nxp,pca9555"; + reg = <0x12>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <114 IRQ_TYPE_LEVEL_LOW>; + + gpio-line-names = + "","", + "","", + "","", + "","", + "","", + "","", + "","fcb0-activate", + "",""; + }; }; &i2c3 { @@ -354,11 +392,22 @@ imux22: i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0>; + + power-monitor@45 { + compatible = "ti,ina230"; + reg = <0x45>; + }; + }; imux23: i2c@1 { #address-cells = <1>; #size-cells = <0>; reg = <1>; + + power-monitor@45 { + compatible = "ti,ina230"; + reg = <0x45>; + }; }; }; }; @@ -405,6 +454,25 @@ eeprom@52 { &i2c11 { status = "okay"; + gpio@13 { + compatible = "nxp,pca9555"; + reg = <0x13>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <222 IRQ_TYPE_LEVEL_LOW>; + + gpio-line-names = + "","", + "","", + "","", + "","health-mmc", + "","", + "","", + "","", + "",""; + }; + gpio@30 { compatible = "nxp,pca9555"; reg = <0x30>; @@ -480,6 +548,19 @@ eeprom@54 { compatible = "atmel,24c64"; reg = <0x54>; }; + + adc@1d { + compatible = "ti,adc128d818"; + reg = <0x1d>; + ti,mode = /bits/ 8 <1>; + }; + + adc@1f { + compatible = "ti,adc128d818"; + reg = <0x1f>; + ti,mode = /bits/ 8 <1>; + }; + }; imux30: i2c@2 { #address-cells = <1>; @@ -581,7 +662,7 @@ &gpio0 { /*T0-T7*/ "","","","","","","","", /*U0-U7*/ "","","","","","","led-identify-gate","", /*V0-V7*/ "","","","", - "rtc-battery-voltage-read-enable","", + "","", "","", /*W0-W7*/ "","","","","","","","", /*X0-X7*/ "","","","","","","","", @@ -666,7 +747,7 @@ &sgpiom0 { "presence-cmm","ac-control-n", /*G0-G3 line 96-103*/ "FM_CPU_CORETYPE2","", - "FM_CPU_CORETYPE1","", + "FM_CPU_CORETYPE1","rtc-battery-voltage-read-enable", "FM_CPU_CORETYPE0","", "FM_BOARD_REV_ID5","", /*G4-G7 line 104-111*/ diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-santabarbara.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-santabarbara.dts new file mode 100644 index 000000000000..ee93a971c500 --- /dev/null +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-santabarbara.dts @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2025 Facebook Inc. + +/dts-v1/; +#include "aspeed-g6.dtsi" +#include +#include + +/ { + model = "Facebook Santabarbara BMC"; + compatible = "facebook,santabarbara-bmc", "aspeed,ast2600"; + + aliases { + serial0 = &uart1; + serial2 = &uart3; + serial3 = &uart4; + serial4 = &uart5; + i2c16 = &i2c4mux0ch0; + i2c17 = &i2c4mux0ch1; + i2c18 = &i2c4mux0ch2; + i2c19 = &i2c4mux0ch3; + i2c20 = &i2c4mux0ch4; + i2c21 = &i2c4mux0ch5; + i2c22 = &i2c4mux0ch6; + i2c23 = &i2c4mux0ch7; + i2c24 = &i2c5mux0ch0; + i2c25 = &i2c5mux0ch1; + i2c26 = &i2c5mux0ch2; + i2c27 = &i2c5mux0ch3; + i2c28 = &i2c5mux1ch0; + i2c29 = &i2c5mux1ch1; + i2c30 = &i2c5mux1ch2; + i2c31 = &i2c5mux1ch3; + i2c32 = &i2c12mux0ch0; + i2c33 = &i2c12mux0ch1; + i2c34 = &i2c12mux0ch2; + i2c35 = &i2c12mux0ch3; + i2c36 = &i2c12mux0ch4; + i2c37 = &i2c12mux0ch5; + i2c38 = &i2c12mux0ch6; + i2c39 = &i2c12mux0ch7; + }; + + chosen { + stdout-path = "serial4:57600n8"; + }; + + iio-hwmon { + compatible = "iio-hwmon"; + io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, + <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>, + <&adc1 2>; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + label = "bmc_heartbeat_amber"; + gpios = <&gpio0 ASPEED_GPIO(P, 7) GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + }; + + led-1 { + label = "fp_id_amber"; + default-state = "off"; + gpios = <&gpio0 ASPEED_GPIO(B, 5) GPIO_ACTIVE_HIGH>; + }; + + led-2 { + label = "power_blue"; + default-state = "off"; + gpios = <&gpio0 ASPEED_GPIO(P, 4) GPIO_ACTIVE_HIGH>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + p3v3_bmc_aux: regulator-p3v3-bmc-aux { + compatible = "regulator-fixed"; + regulator-name = "p3v3_bmc_aux"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + spi_gpio: spi { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = <&gpio0 ASPEED_GPIO(Z, 3) GPIO_ACTIVE_HIGH>; + mosi-gpios = <&gpio0 ASPEED_GPIO(Z, 4) GPIO_ACTIVE_HIGH>; + miso-gpios = <&gpio0 ASPEED_GPIO(Z, 5) GPIO_ACTIVE_HIGH>; + num-chipselects = <1>; + cs-gpios = <&gpio0 ASPEED_GPIO(Z, 0) GPIO_ACTIVE_LOW>; + status = "okay"; + + tpm@0 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + spi-max-frequency = <33000000>; + reg = <0>; + }; + }; +}; + +&adc0 { + aspeed,int-vref-microvolt = <2500000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_adc0_default &pinctrl_adc1_default + &pinctrl_adc2_default &pinctrl_adc3_default + &pinctrl_adc4_default &pinctrl_adc5_default + &pinctrl_adc6_default &pinctrl_adc7_default>; + status = "okay"; +}; + +&adc1 { + aspeed,int-vref-microvolt = <2500000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_adc10_default>; + status = "okay"; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +&fmc { + status = "okay"; + + flash@0 { + status = "okay"; + m25p,fast-read; + label = "bmc"; + spi-max-frequency = <50000000>; +#include "openbmc-flash-layout-128.dtsi" + }; + + flash@1 { + status = "okay"; + m25p,fast-read; + label = "alt-bmc"; + spi-max-frequency = <50000000>; + }; +}; + +&gpio0 { + gpio-line-names = + /*A0-A7*/ "","","","","","","","", + /*B0-B7*/ "rtc-battery-voltage-read-enable","","","BMC_READY", + "","led-identify","","", + /*C0-C7*/ "","","","","","","","", + /*D0-D7*/ "","","","","","","","", + /*E0-E7*/ "","","","","","","","", + /*F0-F7*/ "","","","","","","","", + /*G0-G7*/ "FM_MUX1_SEL_R","","","","","","","", + /*H0-H7*/ "","","","","","","","", + /*I0-I7*/ "","","","","","","","", + /*J0-J7*/ "","","","","","","","", + /*K0-K7*/ "","","","","","","","", + /*L0-L7*/ "","","","","","","","", + /*M0-M7*/ "","","","","","","","", + /*N0-N7*/ "led-postcode-0","led-postcode-1", + "led-postcode-2","led-postcode-3", + "led-postcode-4","led-postcode-5", + "led-postcode-6","led-postcode-7", + /*O0-O7*/ "","","","","","","","", + /*P0-P7*/ "power-button","","reset-button","", + "led-power","","","", + /*Q0-Q7*/ "","","","","","","","", + /*R0-R7*/ "","","","","","","","", + /*S0-S7*/ "","","power-host-control","","","","","", + /*T0-T7*/ "","","","","","","","", + /*U0-U7*/ "","","","","","","","", + /*V0-V7*/ "","","","","","","","", + /*W0-W7*/ "","","","","","","","", + /*X0-X7*/ "","","","","","","","", + /*Y0-Y7*/ "","","","","","","","", + /*Z0-Z7*/ "","","","","","","",""; +}; + +&gpio1 { + gpio-line-names = + /*18A0-18A7*/ "","","","","","","","", + /*18B0-18B7*/ "","","","", + "FM_BOARD_BMC_REV_ID0","FM_BOARD_BMC_REV_ID1", + "FM_BOARD_BMC_REV_ID2","", + /*18C0-18C7*/ "SPI_BMC_BIOS_ROM_IRQ0_R_N","","","","","","","", + /*18D0-18D7*/ "","","","","","","","", + /*18E0-18E3*/ "FM_BMC_PROT_LS_EN","AC_PWR_BMC_BTN_R_N","",""; +}; + +&i2c0 { + status = "okay"; + + // MB FRU + eeprom@53 { + compatible = "atmel,24c128"; + reg = <0x53>; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; +}; + +&i2c1 { + status = "okay"; + + gpio@20 { + compatible = "nxp,pca9555"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <112 IRQ_TYPE_LEVEL_LOW>; + gpio-line-names = + "FM_NIC_PPS_IN_OE_N","FM_NIC_PPS_OUT_OE_N", + "FM_CPU0_TRIGGERTSC_OE_N","FM_NIC_PPS_IN_MUX_OE_N", + "FM_CPU0_CORETYPE0","FM_CPU0_CORETYPE1", + "FM_CPU0_CORETYPE2","FM_NIC_PPS_OUT_MUX_OE", + "CLKMUX_INPUT_LOSS_U45_R_N","FM_CPU0_SP7R1", + "FM_CPU0_SP7R2","FM_CPU0_SP7R3", + "FM_CPU0_SP7R4","", + "FM_NIC_PPS_IN_S0_R","FM_NIC_PPS_IN_S1_R"; + }; + + fan-controller@21{ + compatible = "maxim,max31790"; + reg = <0x21>; + }; + + gpio@22 { + compatible = "nxp,pca9555"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <116 IRQ_TYPE_LEVEL_LOW>; + gpio-line-names = + "FM_CBL_PRSNT_0A_N","FM_CBL_PRSNT_0B_N", + "FM_CBL_PRSNT_1A_N","FM_CBL_PRSNT_1B_N", + "FM_MODULE_PWRGD_0A","FM_MODULE_PWRGD_0B", + "CLKMUX_INPUT_LOSS_U88_R_N","FM_MODULE_PWRGD_1B", + "","", + "CLKMUX_INPUT_LOSS_U83_R_N","CLKMUX_INPUT_LOSS_U84_R_N", + "FM_P3V3_E1S_0_FAULT_R_N","FM_P3V3_E1S_1_FAULT_R_N", + "E1S_0_P12V_ADC_R_ALERT","E1S_1_P12V_ADC_R_ALERT"; + }; + + gpio@24 { + compatible = "nxp,pca9555"; + reg = <0x24>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <114 IRQ_TYPE_LEVEL_LOW>; + gpio-line-names = + "FM_CBL_PRSNT_2A_N","FM_CBL_PRSNT_2B_N", + "FM_CBL_PRSNT_3A_N","FM_CBL_PRSNT_3B_N", + "FM_CBL_PRSNT_4A_N","FM_CBL_PRSNT_4B_N", + "FM_P3V3_NIC_400G_FAULT_R_N","FM_MODULE_PWRGD_2B", + "OCP_SFF_P12V_ADC_R_ALERT","FM_MODULE_PWRGD_3B", + "FM_THERMAL_ALERT_R_N","FM_MODULE_PWRGD_4B", + "FM_CBL_PRSNT_OSFP_A_N","FM_CBL_PRSNT_OSFP_B_N", + "FM_JTAG_MCIO_MUX_S0","FM_JTAG_MCIO_MUX_S1"; + }; + + gpio@26 { + compatible = "nxp,pca9555"; + reg = <0x26>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&sgpiom0>; + interrupts = <118 IRQ_TYPE_LEVEL_LOW>; + gpio-line-names = + "FAN_0_PRSNT_R1_N","FAN_1_PRSNT_R1_N", + "FAN_2_PRSNT_R1_N","FAN_3_PRSNT_R1_N", + "P12V_FAN_0_ADC_ALERT","P12V_FAN_1_ADC_ALERT", + "P12V_FAN_2_ADC_ALERT","P12V_FAN_3_ADC_ALERT", + "P12V_FAN0_PWRGD_R","P12V_FAN1_PWRGD_R", + "P12V_FAN2_PWRGD_R","P12V_FAN3_PWRGD_R", + "","","",""; + }; +}; + +&i2c4 { + status = "okay"; + + i2c-mux@70 { + compatible = "nxp,pca9548"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c4mux0ch0: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + // HPM Board ID EEPROM + eeprom@51 { + compatible = "atmel,24c128"; + reg = <0x51>; + }; + + // SCM Board ID EEPROM + eeprom@53 { + compatible = "atmel,24c128"; + reg = <0x53>; + }; + }; + i2c4mux0ch1: i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c4mux0ch2: i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c4mux0ch3: i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@40 { + compatible = "ti,ina230"; + reg = <0x40>; + shunt-resistor = <2000>; + }; + + power-monitor@42 { + compatible = "ti,ina230"; + reg = <0x42>; + shunt-resistor = <2000>; + }; + + power-monitor@44 { + compatible = "ti,ina230"; + reg = <0x44>; + shunt-resistor = <2000>; + }; + + power-monitor@46 { + compatible = "ti,ina230"; + reg = <0x46>; + shunt-resistor = <2000>; + }; + + voltage-sensor@48 { + compatible = "ti,ads7830"; + reg = <0x48>; + vref-supply = <&p3v3_bmc_aux>; + }; + + voltage-sensor@4a { + compatible = "ti,ads7830"; + reg = <0x4a>; + vref-supply = <&p3v3_bmc_aux>; + }; + + temperature-sensor@4c { + compatible = "ti,tmp75"; + reg = <0x4c>; + }; + + temperature-sensor@4e { + compatible = "ti,tmp75"; + reg = <0x4e>; + }; + }; + i2c4mux0ch4: i2c@4 { + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c4mux0ch5: i2c@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c4mux0ch6: i2c@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@40 { + compatible = "ti,ina230"; + reg = <0x40>; + shunt-resistor = <2000>; + }; + + power-monitor@42 { + compatible = "ti,ina230"; + reg = <0x42>; + shunt-resistor = <2000>; + }; + + power-monitor@44 { + compatible = "ti,ina230"; + reg = <0x44>; + shunt-resistor = <2000>; + }; + + power-monitor@46 { + compatible = "ti,ina230"; + reg = <0x46>; + shunt-resistor = <2000>; + }; + + voltage-sensor@48 { + compatible = "ti,ads7830"; + reg = <0x48>; + }; + }; + i2c4mux0ch7: i2c@7 { + reg = <7>; + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@4b { + compatible = "ti,tmp75"; + reg = <0x4b>; + }; + + temperature-sensor@4f { + compatible = "ti,tmp75"; + reg = <0x4f>; + }; + + // FIO FRU + eeprom@53 { + compatible = "atmel,24c512"; + reg = <0x53>; + }; + }; + }; +}; + +&i2c5 { + status = "okay"; + + // E1S BP FRU + eeprom@52 { + compatible = "atmel,24c64"; + reg = <0x52>; + }; + + i2c-mux@71 { + compatible = "nxp,pca9546"; + reg = <0x71>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c5mux0ch0: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c5mux0ch1: i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c5mux0ch2: i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c5mux0ch3: i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + i2c-mux@72 { + compatible = "nxp,pca9546"; + reg = <0x72>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c5mux1ch0: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + voltage-sensor@48 { + compatible = "ti,ads7830"; + reg = <0x48>; + }; + }; + i2c5mux1ch1: i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@48 { + compatible = "ti,tmp75"; + reg = <0x48>; + }; + }; + i2c5mux1ch2: i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@40 { + compatible = "ti,ina230"; + reg = <0x40>; + shunt-resistor = <2000>; + }; + + power-monitor@41 { + compatible = "ti,ina230"; + reg = <0x41>; + shunt-resistor = <2000>; + }; + + power-monitor@44 { + compatible = "ti,ina230"; + reg = <0x44>; + shunt-resistor = <2000>; + }; + + power-monitor@45 { + compatible = "ti,ina230"; + reg = <0x45>; + shunt-resistor = <2000>; + }; + }; + i2c5mux1ch3: i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + gpio@74 { + compatible = "nxp,pca9539"; + reg = <0x74>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = + "P12V_E1S_ADC_ALERT","BUFF0_100M_LOSB_PLD", + "E1S_BP_SKU_ID0","E1S_BP_SKU_ID1", + "E1S_BP_SKU_ID2","E1S_BP_REV_ID0", + "E1S_BP_REV_ID1","E1S_BP_REV_ID2", + "P3V3_E1S_1_FAULT_R_N","P3V3_E1S_2_FAULT_R_N", + "P3V3_E1S_3_FAULT_R_N","P3V3_E1S_4_FAULT_R_N", + "P12V_E1S_1_FAULT_R_N","P12V_E1S_2_FAULT_R_N", + "P12V_E1S_3_FAULT_R_N","P12V_E1S_4_FAULT_R_N"; + }; + }; + }; +}; + +&i2c6 { + status = "okay"; + + // Rainbow0 FRU + eeprom@52 { + compatible = "atmel,24c256"; + reg = <0x52>; + }; +}; + +&i2c7 { + status = "okay"; +}; + +&i2c8 { + status = "okay"; + + // Rainbow2 FRU + eeprom@52 { + compatible = "atmel,24c256"; + reg = <0x52>; + }; +}; + +&i2c9 { + status = "okay"; + + temperature-sensor@4b { + compatible = "ti,tmp75"; + reg = <0x4b>; + }; + + // SCM FRU + eeprom@50 { + compatible = "atmel,24c128"; + reg = <0x50>; + }; + + // BSM FRU + eeprom@56 { + compatible = "atmel,24c64"; + reg = <0x56>; + }; +}; + +&i2c10 { + status = "okay"; + + // Rainbow3 FRU + eeprom@52 { + compatible = "atmel,24c256"; + reg = <0x52>; + }; +}; + +&i2c11 { + status = "okay"; + + // OCP NIC TEMP + temperature-sensor@1f { + compatible = "ti,tmp421"; + reg = <0x1f>; + }; + + // OCP NIC FRU + eeprom@50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; +}; + +&i2c12 { + status = "okay"; + + // SWB FRU + eeprom@52 { + compatible = "atmel,24c64"; + reg = <0x52>; + }; + + i2c-mux@72 { + compatible = "nxp,pca9548"; + reg = <0x72>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c12mux0ch0: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@48 { + compatible = "ti,tmp75"; + reg = <0x48>; + }; + }; + i2c12mux0ch1: i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@42 { + compatible = "mps,mp2971"; + reg = <0x42>; + }; + + power-monitor@43 { + compatible = "mps,mp2971"; + reg = <0x43>; + }; + }; + i2c12mux0ch2: i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@40 { + compatible = "ti,ina230"; + reg = <0x40>; + shunt-resistor = <2000>; + }; + + power-monitor@41 { + compatible = "ti,ina230"; + reg = <0x41>; + shunt-resistor = <2000>; + }; + }; + i2c12mux0ch3: i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@44 { + compatible = "ti,ina230"; + reg = <0x44>; + shunt-resistor = <2000>; + }; + + power-monitor@45 { + compatible = "ti,ina230"; + reg = <0x45>; + shunt-resistor = <2000>; + }; + }; + i2c12mux0ch4: i2c@4 { + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + + voltage-sensor@49 { + compatible = "ti,ads7830"; + reg = <0x49>; + }; + }; + i2c12mux0ch5: i2c@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c12mux0ch6: i2c@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + }; + i2c12mux0ch7: i2c@7 { + reg = <7>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; +}; + +&i2c13 { + status = "okay"; + + // Rainbow1 FRU + eeprom@52 { + compatible = "atmel,24c256"; + reg = <0x52>; + }; +}; + +&i2c14 { + status = "okay"; +}; + +&i2c15 { + status = "okay"; +}; + +&kcs2 { + aspeed,lpc-io-reg = <0xca8>; + status = "okay"; +}; + +&kcs3 { + aspeed,lpc-io-reg = <0xca2>; + status = "okay"; +}; + +&mac2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rmii3_default>; + use-ncsi; + status = "okay"; +}; + +&mac3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rmii4_default>; + use-ncsi; + status = "okay"; +}; + +&sgpiom0 { + ngpios = <128>; + bus-frequency = <2000000>; + gpio-line-names = + /*in - out - in - out */ + /*A0-A3 line 0-7*/ + "PDB1_HSC_PWR_OK","power-chassis-control", + "PDB2_HSC_PWR_OK","FM_MODULE_PWRGD_0A_OUT", + "PWRGD_P12V_MEM","FM_MODULE_PWRGD_0B_OUT", + "PWRGD_P12V_SCM","FM_MODULE_PWRGD_1B_OUT", + /*A4-A7 line 8-15*/ + "PWRGD_P12V_FAN","FM_MODULE_PWRGD_2B_OUT", + "PWRGD_P5V_AUX","FM_MODULE_PWRGD_3B_OUT", + "power-chassis-good","FM_MODULE_PWRGD_4B_OUT", + "PWRGD_P1V8_LDO","FM_CBL_PRSNT_0A_N_OUT", + /*B0-B3 line 16-23*/ + "PWRGD_P1V_LDO","FM_CBL_PRSNT_0B_N_OUT", + "PWRGD_PVDD33_S5","FM_CBL_PRSNT_1A_N_OUT", + "PWRGD_PVDD18_S5_P0","FM_CBL_PRSNT_1B_N_OUT", + "CPU0_SLP_S5_N","FM_CBL_PRSNT_2A_N_OUT", + /*B4-B7 line 24-31*/ + "PWRGD_PVDDIO_MEM_S3_P0","FM_CBL_PRSNT_2B_N_OUT", + "CPU0_SLP_S3_N","FM_CBL_PRSNT_3A_N_OUT", + "FM_MODULE_PWRGD_1B","FM_CBL_PRSNT_3B_N_OUT", + "FM_MODULE_PWRGD_2B","FM_CBL_PRSNT_4A_N_OUT", + /*C0-C3 line 32-39*/ + "FM_MODULE_PWRGD_3B","FM_CBL_PRSNT_4B_N_OUT", + "FM_MODULE_PWRGD_4B","P12V_FAN0_PWRGD_OUT", + "FM_MODULE_PWRGD_0B","P12V_FAN1_PWRGD_OUT", + "PWRGD_PVDDIO_P0","P12V_FAN2_PWRGD_OUT", + /*C4-C7 line 40-47*/ + "PWRGD_PVDDCR_SOC_P0","P12V_FAN3_PWRGD_OUT", + "PWRGD_PVDDCR_CPU0_P0","P12V_FAN4_PWRGD_OUT", + "PWRGD_PVDDCR_CPU1_P0","P12V_FAN5_PWRGD_OUT", + "FM_CPU0_PWR_GOOD","P12V_FAN6_PWRGD_OUT", + /*D0-D3 line 48-55*/ + "host0-ready","P12V_FAN7_PWRGD_OUT", + "FM_PWRGD_CPU0_PWROK","FAN_0_PRSNT_R1_N_OUT", + "FM_RST_CPU0_RESETL_N","FAN_1_PRSNT_R1_N_OUT", + "RST_CPU0_PERST0_R_N","FAN_2_PRSNT_R1_N_OUT", + /*D4-D7 line 56-63*/ + "RST_CPU0_PERST1_R_N","FAN_3_PRSNT_R1_N_OUT", + "BIOS_POST_CMPLT","FAN_4_PRSNT_R1_N_OUT", + "","FAN_5_PRSNT_R1_N_OUT", + "","FAN_6_PRSNT_R1_N_OUT", + /*E0-E3 line 64-71*/ + "FM_PWRGD_CHAD_CPU0","FAN_7_PRSNT_R1_N_OUT", + "FM_PWRGD_CHEH_CPU0","TRAY_SLOT_ID0_OUT", + "FM_PWRGD_CHIL_CPU0","TRAY_SLOT_ID1_OUT", + "FM_PWRGD_CHMP_CPU0","TRAY_SLOT_ID2_OUT", + /*E4-E7 line 72-79*/ + "P12V_E1S_0_PWRGD","TRAY_SLOT_ID3_OUT", + "P12V_E1S_1_PWRGD","TRAY_SLOT_ID4_OUT", + "P3V3_E1S_0_PWRGD","SCM_JTAG_MUX_S0_R", + "P3V3_E1S_1_PWRGD","SCM_JTAG_MUX_S1_R", + /*F0-F3 line 80-87*/ + "FM_MODULE_PWRGD_0A","BMC_SGPIO_READY", + "OCP_V3_1_P3V3_PLD_R_PWRGD","CPU0_SYS_RESET_N", + "P12V_OCP_V3_1_PLD_PWRGD","RST_CPU0_KBRST_N", + "PWRGD_OCP_SFF_PWR_GOOD","BIOS_DEBUG_MODE", + /*F4-F7 line 88-95*/ + "","CLR_CMOS", + "","I3C_SPD_MUX_FORCE_SEL", + "","FM_JTAG_HOST_SEL", + "","TRAY_PRESENT_N", + /*G0-G3 line 96-103*/ + "MB_REV_ID_0","UART_BMC_SEL0", + "MB_REV_ID_1","UART_BMC_SEL1", + "MB_REV_ID_2","SCM_USB_SEL", + "MB_SKU_ID_0","FORCE_ALL_PWRON", + /*G4-G7 line 104-111*/ + "MB_SKU_ID_1","PASSWORD_CLEAR", + "MB_SKU_ID_2","", + "MB_SKU_ID_3","", + "","BIOS_DEBUG_MODE", + /*H0-H3 line 112-119*/ + "FM_IOEXP_U538_INT_N","", + "FM_IOEXP_U539_INT_N","", + "FM_IOEXP_U540_INT_N","", + "FM_IOEXP_U541_INT_N","", + /*H4-H7 line 120-127*/ + "FM_IOEXP_PDB2_U1003_INT_N","", + "","","","","","", + /*I0-I3 line 128-135*/ + "","","","", + "PDB_IRQ_PMBUS_ALERT_ISO_R_N","", + "PDB_UV_ALERT_ISO_R_N","", + /*I4-I7 line 136-143*/ + "P12V_SCM_ADC_ALERT","", + "CPU0_REGS_I2C_ALERT_N","", + "FM_RTC_ALERT_N","", + "APML_CPU0_ALERT_R_N","", + /*J0-J3 line 144-151*/ + "SMB_RJ45_FIO_TMP_ALERT","", + "FM_SMB_ALERT_MCIO_0A_N","", + "I3C_MCIO_0B_ALERT_ISO_R_N","", + "FM_SMB_ALERT_MCIO_1A_N","", + /*J4-J7 line 152-159*/ + "I3C_MCIO_1B_ALERT_ISO_R_N","", + "FM_SMB_ALERT_MCIO_2A_N","", + "I3C_MCIO_2B_ALERT_ISO_R_N","", + "FM_SMB_ALERT_MCIO_3A_N","", + /*K0-K3 line 160-167*/ + "I3C_MCIO_3B_ALERT_ISO_R_N","", + "FM_SMB_ALERT_MCIO_4A_N","", + "I3C_MCIO_4B_ALERT_ISO_R_N","", + "","", + /*K4-K7 line 168-175*/ + "","","","","","","","", + /*L0-L3 line 176-183*/ + "FM_CPU0_THERMTRIP_N","", + "FM_CPU0_PROCHOT_N","", + "FM_CPU0_SMERR_N","", + "FM_PVDDCR_CPU0_P0_OCP_N","", + /*L4-L7 line 184-191*/ + "FM_PVDDCR_CPU1_P0_OCP_N","", + "FM_PVDDCR_SOC_P0_OCP_N","", + "FM_OCP_PWRBRK_R_N","", + "PMIC_ERROR_N","", + /*M0-M3 line 192-199*/ + "","","","","","","","", + /*M4-M7 line 200-207*/ + "","","","","","","","", + /*N0-N3 line 208-215*/ + "FM_PRSNT_CPU0_N","", + "OCP_SFF_PRSNT_N","", + "E1S_0_PRSNT_R_N","", + "E1S_BP_0_PRSNT_R_N","", + /*N4-N7 line 216-223*/ + "E1S_BP_1_PRSNT_R_N","", + "E1S_BP_2_PRSNT_R_N","", + "E1S_BP_3_PRSNT_R_N","", + "PDB_PRSNT_J311_N","", + /*O0-O3 line 224-231*/ + "PDB_PRSNT_J312_N","", + "PDB_PRSNT_J313_N","", + "PDB_PRSNT_J314_N","", + "PRSNT_RJ45_FIO_N_R","", + /*O4-O7 line 232-239*/ + "PRSNT_LEAK_CABLE_1_R_N","", + "PRSNT_LEAK_CABLE_2_R_N","", + "PRSNT_HDT_N","", + "","", + /*P0-P3 line 240-247*/ + "","","","","","","","", + /*P4-P7 line 248-255*/ + "","","","","","","",""; + status = "okay"; +}; + +// BIOS Flash +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi2_default>; + status = "okay"; + + flash@0 { + m25p,fast-read; + label = "pnor"; + spi-max-frequency = <12000000>; + spi-tx-bus-width = <2>; + spi-rx-bus-width = <2>; + status = "okay"; + }; +}; + +// HOST BIOS Debug +&uart1 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +// BMC Debug Console +&uart5 { + status = "okay"; +}; + +&uart_routing { + status = "okay"; +}; + +&wdt1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wdtrst1_default>; + aspeed,reset-type = "soc"; + aspeed,external-signal; + aspeed,ext-push-pull; + aspeed,ext-active-high; + aspeed,ext-pulse-duration = <256>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-yosemite4.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-yosemite4.dts index 29f224bccd63..aae789854c52 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-yosemite4.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-yosemite4.dts @@ -189,6 +189,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT1_UART_SEL0","SLOT1_UART_SEL1", + "SLOT1_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -235,6 +240,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT2_UART_SEL0","SLOT2_UART_SEL1", + "SLOT2_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -281,6 +291,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT3_UART_SEL0","SLOT3_UART_SEL1", + "SLOT3_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -327,6 +342,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT4_UART_SEL0","SLOT4_UART_SEL1", + "SLOT4_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -373,6 +393,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT5_UART_SEL0","SLOT5_UART_SEL1", + "SLOT5_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -419,6 +444,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT6_UART_SEL0","SLOT6_UART_SEL1", + "SLOT6_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -465,6 +495,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT7_UART_SEL0","SLOT7_UART_SEL1", + "SLOT7_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { @@ -511,6 +546,11 @@ gpio@22 { reg = <0x22>; gpio-controller; #gpio-cells = <2>; + gpio-line-names = "SLOT8_UART_SEL0","SLOT8_UART_SEL1", + "SLOT8_UART_SEL2","","","","","", + "","","","","","","","", + "","","","","","","","", + "","","","","","","",""; }; gpio@23 { diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-bonnell.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-bonnell.dts index 7364adc6b80d..2f5d4075a64a 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-bonnell.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-bonnell.dts @@ -155,7 +155,7 @@ &gpio0 { /*Y0-Y7*/ "","","","","","","","", /*Z0-Z7*/ "","","","","","","",""; - usb_power { + usb-power-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts index 9961508ee872..4d9e2cd11f44 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts @@ -312,7 +312,7 @@ &gpio0 { /*Y0-Y7*/ "","","","","","","","", /*Z0-Z7*/ "","","","","","","",""; - usb_power { + usb-power-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-rainier.dts index 638a2c1c7892..757421bc3605 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-rainier.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-rainier.dts @@ -224,14 +224,14 @@ &gpio0 { /*Y0-Y7*/ "","","","","","","","", /*Z0-Z7*/ "","","","","","","",""; - i2c3_mux_oe_n { + i2c3-mux-oe-n-hog { gpio-hog; gpios = ; output-high; line-name = "I2C3_MUX_OE_N"; }; - usb_power { + usb-power-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-system1.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-system1.dts index 360b9ce3c850..c8267c97a44e 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-system1.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-system1.dts @@ -116,63 +116,63 @@ vga_memory: region@bf000000 { leds { compatible = "gpio-leds"; - led-0 { + led-bmc-ready { gpios = <&gpio0 ASPEED_GPIO(L, 7) GPIO_ACTIVE_HIGH>; }; - led-1 { + led-bmc-hb { gpios = <&gpio0 ASPEED_GPIO(P, 7) GPIO_ACTIVE_HIGH>; }; - led-2 { + led-rear-enc-fault0 { gpios = <&gpio0 ASPEED_GPIO(S, 6) GPIO_ACTIVE_HIGH>; }; - led-3 { + led-rear-enc-id0 { gpios = <&gpio0 ASPEED_GPIO(S, 7) GPIO_ACTIVE_HIGH>; }; - led-4 { + led-fan0-fault { gpios = <&pca3 5 GPIO_ACTIVE_LOW>; }; - led-5 { + led-fan1-fault { gpios = <&pca3 6 GPIO_ACTIVE_LOW>; }; - led-6 { + led-fan2-fault { gpios = <&pca3 7 GPIO_ACTIVE_LOW>; }; - led-7 { + led-fan3-fault { gpios = <&pca3 8 GPIO_ACTIVE_LOW>; }; - led-8 { + led-fan4-fault { gpios = <&pca3 9 GPIO_ACTIVE_LOW>; }; - led-9 { + led-fan5-fault { gpios = <&pca3 10 GPIO_ACTIVE_LOW>; }; - led-a { + led-fan6-fault { gpios = <&pca3 11 GPIO_ACTIVE_LOW>; }; - led-b { + led-nvmed0-fault { gpios = <&pca4 4 GPIO_ACTIVE_HIGH>; }; - led-c { + led-nvmed1-fault { gpios = <&pca4 5 GPIO_ACTIVE_HIGH>; }; - led-d { + led-nvmed2-fault { gpios = <&pca4 6 GPIO_ACTIVE_HIGH>; }; - led-e { + led-nvmed3-fault { gpios = <&pca4 7 GPIO_ACTIVE_HIGH>; }; }; @@ -355,7 +355,35 @@ &uhci { status = "okay"; }; +&pinctrl { + pinctrl_gpiol4_unbiased: gpiol4 { + pins = "C15"; + bias-disable; + }; + + pinctrl_gpiol5_unbiased: gpiol5 { + pins = "F15"; + bias-disable; + }; + + pinctrl_gpiol6_unbiased: gpiol6 { + pins = "B14"; + bias-disable; + }; + + pinctrl_gpiol7_unbiased: gpiol7 { + pins = "C14"; + bias-disable; + }; +}; + &gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpiol4_unbiased + &pinctrl_gpiol5_unbiased + &pinctrl_gpiol6_unbiased + &pinctrl_gpiol7_unbiased>; + gpio-line-names = /*A0-A7*/ "","","","","","","","", /*B0-B7*/ "","","","","bmc-tpm-reset","","","", @@ -368,14 +396,14 @@ &gpio0 { /*I0-I7*/ "","","","","","","","", /*J0-J7*/ "","","","","","","","", /*K0-K7*/ "","","","","","","","", - /*L0-L7*/ "","","","","","","","bmc-ready", + /*L0-L7*/ "","","","","","","","led-bmc-ready", /*M0-M7*/ "","","","","","","","", - /*N0-N7*/ "fpga-debug-enable","","","","","","","", + /*N0-N7*/ "pch-reset","","","","","flash-write-override","","", /*O0-O7*/ "","","","","","","","", - /*P0-P7*/ "","","","","","","","bmc-hb", + /*P0-P7*/ "","","","","","","","led-bmc-hb", /*Q0-Q7*/ "","","","","","","pch-ready","", /*R0-R7*/ "","","","","","","","", - /*S0-S7*/ "","","","","","","rear-enc-fault0","rear-enc-id0", + /*S0-S7*/ "","","","","","","led-rear-enc-fault0","led-rear-enc-id0", /*T0-T7*/ "","","","","","","","", /*U0-U7*/ "","","","","","","","", /*V0-V7*/ "","rtc-battery-voltage-read-enable","","power-chassis-control","","","","", @@ -383,6 +411,34 @@ &gpio0 { /*X0-X7*/ "fpga-pgood","power-chassis-good","pch-pgood","","","","","", /*Y0-Y7*/ "","","","","","","","", /*Z0-Z7*/ "","","","","","","",""; + + pin-gpio-hog-0 { + gpio-hog; + gpios = ; + input; + line-name = "RST_RTCRST_N"; + }; + + pin-gpio-hog-1 { + gpio-hog; + gpios = ; + input; + line-name = "RST_SRTCRST_N"; + }; + + pin-gpio-hog-2 { + gpio-hog; + gpios = ; + output-high; + line-name = "BMC_FAN_E3_SVC_PEX_INT_N"; + }; + + pin-gpio-hog-3 { + gpio-hog; + gpios = ; + output-low; + line-name = "isolate_errs_cpu1"; + }; }; &emmc_controller { @@ -401,7 +457,7 @@ &emmc { &sgpiom0 { status = "okay"; ngpios = <128>; - bus-frequency = <1000000>; + bus-frequency = <500000>; }; &ibt { @@ -486,23 +542,6 @@ eeprom@50 { compatible = "atmel,24c64"; reg = <0x50>; }; - - regulator@60 { - compatible = "maxim,max8952"; - reg = <0x60>; - - max8952,default-mode = <0>; - max8952,dvs-mode-microvolt = <1250000>, <1200000>, - <1050000>, <950000>; - max8952,sync-freq = <0>; - max8952,ramp-speed = <0>; - - regulator-name = "VR_v77_1v4"; - regulator-min-microvolt = <770000>; - regulator-max-microvolt = <1400000>; - regulator-always-on; - regulator-boot-on; - }; }; &i2c1 { @@ -763,6 +802,15 @@ i2c3mux0chn7: i2c@7 { &i2c4 { status = "okay"; + multi-master; + bus-frequency = <1000000>; + + ipmb@10 { + compatible = "ipmb-dev"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; + + i2c-protocol; + }; }; &i2c5 { @@ -1189,23 +1237,6 @@ eeprom@50 { compatible = "atmel,24c64"; reg = <0x50>; }; - - regulator@60 { - compatible = "maxim,max8952"; - reg = <0x60>; - - max8952,default-mode = <0>; - max8952,dvs-mode-microvolt = <1250000>, <1200000>, - <1050000>, <950000>; - max8952,sync-freq = <0>; - max8952,ramp-speed = <0>; - - regulator-name = "VR_v77_1v4"; - regulator-min-microvolt = <770000>; - regulator-max-microvolt = <1400000>; - regulator-always-on; - regulator-boot-on; - }; }; &i2c11 { diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr630.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr630.dts index ddbcbc64e235..4ad0f44af1ab 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr630.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr630.dts @@ -405,161 +405,161 @@ fan@13 { &gpio { - pin_gpio_b5 { + pin-gpio-b5-hog { gpio-hog; gpios = ; output-high; line-name = "IRQ_BMC_PCH_SMI_LPC_N"; }; - pin_gpio_f0 { + pin-gpio-f0-hog { gpio-hog; gpios = ; output-low; line-name = "IRQ_BMC_PCH_NMI_R"; }; - pin_gpio_f3 { + pin-gpio-f3-hog { gpio-hog; gpios = ; output-high; line-name = "I2C_BUS0_RST_OUT_N"; }; - pin_gpio_f4 { + pin-gpio-f4-hog { gpio-hog; gpios = ; output-low; line-name = "FM_SKT0_FAULT_LED"; }; - pin_gpio_f5 { + pin-gpio-f5-hog { gpio-hog; gpios = ; output-low; line-name = "FM_SKT1_FAULT_LED"; }; - pin_gpio_g4 { + pin-gpio-g4-hog { gpio-hog; gpios = ; output-high; line-name = "FAN_PWR_CTL_N"; }; - pin_gpio_g7 { + pin-gpio-g7-hog { gpio-hog; gpios = ; output-high; line-name = "RST_BMC_PCIE_I2CMUX_N"; }; - pin_gpio_h2 { + pin-gpio-h2-hog { gpio-hog; gpios = ; output-high; line-name = "PSU1_FFS_N_R"; }; - pin_gpio_h3 { + pin-gpio-h3-hog { gpio-hog; gpios = ; output-high; line-name = "PSU2_FFS_N_R"; }; - pin_gpio_i3 { + pin-gpio-i3-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_INTRUDED_COVER"; }; - pin_gpio_j2 { + pin-gpio-j2-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_BIOS_UPDATE_N"; }; - pin_gpio_j3 { + pin-gpio-j3-hog { gpio-hog; gpios = ; output-high; line-name = "RST_BMC_HDD_I2CMUX_N"; }; - pin_gpio_s2 { + pin-gpio-s2-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_VGA_SW"; }; - pin_gpio_s4 { + pin-gpio-s4-hog { gpio-hog; gpios = ; output; line-name = "VBAT_EN_N"; }; - pin_gpio_s6 { + pin-gpio-s6-hog { gpio-hog; gpios = ; output-high; line-name = "PU_BMC_GPIOS6"; }; - pin_gpio_y0 { + pin-gpio-y0-hog { gpio-hog; gpios = ; output-low; line-name = "BMC_NCSI_MUX_CTL_S0"; }; - pin_gpio_y1 { + pin-gpio-y1-hog { gpio-hog; gpios = ; output-low; line-name = "BMC_NCSI_MUX_CTL_S1"; }; - pin_gpio_z0 { + pin-gpio-z0-hog { gpio-hog; gpios = ; output-high; line-name = "I2C_RISER2_INT_N"; }; - pin_gpio_z2 { + pin-gpio-z2-hog { gpio-hog; gpios = ; output-high; line-name = "I2C_RISER2_RESET_N"; }; - pin_gpio_z3 { + pin-gpio-z3-hog { gpio-hog; gpios = ; output-high; line-name = "FM_BMC_PCH_SCI_LPC_N"; }; - pin_gpio_z7 { + pin-gpio-z7-hog { gpio-hog; gpios = ; output-low; line-name = "BMC_POST_CMPLT_N"; }; - pin_gpio_aa0 { + pin-gpio-aa0-hog { gpio-hog; gpios = ; output-low; line-name = "HOST_BMC_USB_SEL"; }; - pin_gpio_aa5 { + pin-gpio-aa5-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr855xg2.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr855xg2.dts index 6045b60b80da..de61eac54585 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr855xg2.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-lenovo-hr855xg2.dts @@ -425,238 +425,238 @@ fan@16 { &gpio { - pin_gpio_a1 { + pin-gpio-a1-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_EMMC_RST_N"; }; - pin_gpio_a3 { + pin-gpio-a3-hog { gpio-hog; gpios = ; output-high; line-name = "PCH_PWROK_BMC_FPGA"; }; - pin_gpio_b5 { + pin-gpio-b5-hog { gpio-hog; gpios = ; output-high; line-name = "IRQ_BMC_PCH_SMI_LPC_N"; }; - pin_gpio_b7 { + pin-gpio-b7-hog { gpio-hog; gpios = ; output-low; line-name = "CPU_SM_WP"; }; - pin_gpio_e0 { + pin-gpio-e0-hog { gpio-hog; gpios = ; input; line-name = "PDB_PSU_SEL"; }; - pin_gpio_e2 { + pin-gpio-e2-hog { gpio-hog; gpios = ; output-high; line-name = "LOCATOR_LED_N"; }; - pin_gpio_e5 { + pin-gpio-e5-hog { gpio-hog; gpios = ; output-high; line-name = "FM_BMC_DBP_PRESENT_R1_N"; }; - pin_gpio_e6 { + pin-gpio-e6-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_ME_SECURITY_OVERRIDE_N"; }; - pin_gpio_f0 { + pin-gpio-f0-hog { gpio-hog; gpios = ; output-high; line-name = "IRQ_BMC_PCH_NMI_R"; }; - pin_gpio_f1 { + pin-gpio-f1-hog { gpio-hog; gpios = ; input; line-name = "CPU2_PROCDIS_BMC_N"; }; - pin_gpio_f2 { + pin-gpio-f2-hog { gpio-hog; gpios = ; output-high; line-name = "RM_THROTTLE_EN_N"; }; - pin_gpio_f3 { + pin-gpio-f3-hog { gpio-hog; gpios = ; output-low; line-name = "FM_PMBUS_ALERT_B_EN"; }; - pin_gpio_f4 { + pin-gpio-f4-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_FORCE_NM_THROTTLE_N"; }; - pin_gpio_f6 { + pin-gpio-f6-hog { gpio-hog; gpios = ; output-high; line-name = "FM_BMC_CPU_PWR_DEBUG_N"; }; - pin_gpio_g7 { + pin-gpio-g7-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_PCIE_I2C_MUX_RST_N"; }; - pin_gpio_h6 { + pin-gpio-h6-hog { gpio-hog; gpios = ; output-high; line-name = "FM_BMC_DBP_PRESENT_R2_N"; }; - pin_gpio_i3 { + pin-gpio-i3-hog { gpio-hog; gpios = ; output-high; line-name = "SPI_BMC_BIOS_WP_N"; }; - pin_gpio_j1 { + pin-gpio-j1-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_USB_SEL"; }; - pin_gpio_j2 { + pin-gpio-j2-hog { gpio-hog; gpios = ; output-high; line-name = "PDB_SMB_RST_N"; }; - pin_gpio_j3 { + pin-gpio-j3-hog { gpio-hog; gpios = ; output-high; line-name = "SPI_BMC_BIOS_HOLD_N"; }; - pin_gpio_l0 { + pin-gpio-l0-hog { gpio-hog; gpios = ; output-high; line-name = "PDB_FAN_TACH_SEL"; }; - pin_gpio_l1 { + pin-gpio-l1-hog { gpio-hog; gpios = ; output-high; line-name = "SYS_RESET_BMC_FPGA_N"; }; - pin_gpio_l4 { + pin-gpio-l4-hog { gpio-hog; gpios = ; output-high; line-name = "FM_EFUSE_FAN_G1_EN"; }; - pin_gpio_l5 { + pin-gpio-l5-hog { gpio-hog; gpios = ; output-high; line-name = "FM_EFUSE_FAN_G2_EN"; }; - pin_gpio_r6 { + pin-gpio-r6-hog { gpio-hog; gpios = ; input; line-name = "CPU3_PROCDIS_BMC_N"; }; - pin_gpio_r7 { + pin-gpio-r7-hog { gpio-hog; gpios = ; input; line-name = "CPU4_PROCDIS_BMC_N"; }; - pin_gpio_s1 { + pin-gpio-s1-hog { gpio-hog; gpios = ; output-low; line-name = "DBP_SYSPWROK_BMC"; }; - pin_gpio_s2 { + pin-gpio-s2-hog { gpio-hog; gpios = ; output-high; line-name = "PCH_RST_RSMRST_N"; }; - pin_gpio_s6 { + pin-gpio-s6-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_HW_STRAP_5"; }; - pin_gpio_z3 { + pin-gpio-z3-hog { gpio-hog; gpios = ; output-high; line-name = "FM_BMC_PCH_SCI_LPC_N"; }; - pin_gpio_aa0 { + pin-gpio-aa0-hog { gpio-hog; gpios = ; output-low; line-name = "FW_PSU_ALERT_EN_N"; }; - pin_gpio_aa4 { + pin-gpio-aa4-hog { gpio-hog; gpios = ; output-high; line-name = "DBP_CPU_PREQ_N"; }; - pin_gpio_ab3 { + pin-gpio-ab3-hog { gpio-hog; gpios = ; output-low; line-name = "BMC_WDTRST"; }; - pin_gpio_ac6 { + pin-gpio-ac6-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-gb200nvl-bmc.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-gb200nvl-bmc.dts new file mode 100644 index 000000000000..41e3e9dd85f5 --- /dev/null +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-gb200nvl-bmc.dts @@ -0,0 +1,1128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +#include "aspeed-g6.dtsi" +#include +#include +#include + +/ { + model = "AST2600 GB200NVL BMC"; + compatible = "nvidia,gb200nvl-bmc", "aspeed,ast2600"; + + aliases { + serial2 = &uart3; + serial4 = &uart5; + i2c16 = &imux16; + i2c17 = &imux17; + i2c18 = &imux18; + i2c19 = &imux19; + i2c20 = &imux20; + i2c21 = &imux21; + i2c22 = &imux22; + i2c23 = &imux23; + i2c24 = &imux24; + i2c25 = &imux25; + i2c26 = &imux26; + i2c27 = &imux27; + i2c28 = &imux28; + i2c29 = &imux29; + i2c30 = &imux30; + i2c31 = &imux31; + i2c32 = &imux32; + i2c33 = &imux33; + i2c34 = &imux34; + i2c35 = &imux35; + i2c36 = &imux36; + i2c37 = &imux37; + i2c38 = &imux38; + i2c39 = &imux39; + i2c40 = &e1si2c0; + i2c41 = &e1si2c1; + i2c42 = &e1si2c2; + i2c43 = &e1si2c3; + i2c44 = &e1si2c4; + i2c45 = &e1si2c5; + i2c46 = &e1si2c6; + i2c47 = &e1si2c7; + i2c48 = &i2c17mux0; + i2c49 = &i2c17mux1; + i2c50 = &i2c17mux2; + i2c51 = &i2c17mux3; + i2c52 = &i2c25mux0; + i2c53 = &i2c25mux1; + i2c54 = &i2c25mux2; + i2c55 = &i2c25mux3; + i2c56 = &i2c29mux0; + i2c57 = &i2c29mux1; + i2c58 = &i2c29mux2; + i2c59 = &i2c29mux3; + }; + + chosen { + stdout-path = &uart5; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vga_memory: framebuffer@9f000000 { + no-map; + reg = <0x9f000000 0x01000000>; /* 16M */ + }; + + ramoops@a0000000 { + compatible = "ramoops"; + reg = <0xa0000000 0x100000>; /* 1MB */ + record-size = <0x10000>; /* 64KB */ + max-reason = <2>; /* KMSG_DUMP_OOPS */ + }; + + gfx_memory: framebuffer { + size = <0x01000000>; + alignment = <0x01000000>; + compatible = "shared-dma-pool"; + reusable; + }; + + video_engine_memory: jpegbuffer { + size = <0x02000000>; /* 32M */ + alignment = <0x01000000>; + compatible = "shared-dma-pool"; + reusable; + }; + }; + + leds { + compatible = "gpio-leds"; + led-0 { + label = "uid_led"; + gpios = <&sgpiom0 27 GPIO_ACTIVE_LOW>; + }; + led-1 { + label = "fault_led"; + gpios = <&sgpiom0 29 GPIO_ACTIVE_LOW>; + }; + led-2 { + label = "power_led"; + gpios = <&sgpiom0 31 GPIO_ACTIVE_LOW>; + }; + }; + + buttons { + button-power { + label = "power-btn"; + gpio = <&sgpiom0 156 GPIO_ACTIVE_LOW>; + }; + button-uid { + label = "uid-btn"; + gpio = <&sgpiom0 154 GPIO_ACTIVE_LOW>; + }; + }; +}; + +// Enable Primary flash on FMC for bring up activity +&fmc { + status = "okay"; + flash@0 { + status = "okay"; + compatible = "jedec,spi-nor"; + label = "bmc"; + spi-max-frequency = <50000000>; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + u-boot@0 { + // 896KB + reg = <0x0 0xe0000>; + label = "u-boot"; + }; + + kernel@100000 { + // 9MB + reg = <0x100000 0x900000>; + label = "kernel"; + }; + + rofs@a00000 { + // 55292KB (extends to end of 64MB SPI - 4KB) + reg = <0xa00000 0x35FF000>; + label = "rofs"; + }; + }; + }; +}; + +&spi2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi2_default>; + + // Data SPI is 64MB in size + flash@0 { + status = "okay"; + label = "config"; + spi-max-frequency = <50000000>; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + u-boot-env@0 { + // 256KB + reg = <0x0 0x40000>; + label = "u-boot-env"; + }; + + rwfs@40000 { + // 16MB + reg = <0x40000 0x1000000>; + label = "rwfs"; + }; + + log@1040000 { + // 40MB + reg = <0x1040000 0x2800000>; + label = "log"; + }; + }; + }; +}; + +&uart1 { + status = "okay"; +}; + +&uart3 { + // Enabling SOL + status = "okay"; +}; + +&uart5 { + // BMC Debug Console + status = "okay"; +}; + +&uart_routing { + status = "okay"; +}; + +&mac2 { + status = "okay"; + phy-mode = "rmii"; + use-ncsi; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rmii3_default>; +}; + +/* + * Enable USB port A as device (via the virtual hub) to host + */ +&vhub { + status = "okay"; +}; + +&video { + status = "okay"; + memory-region = <&video_engine_memory>; +}; + +// USB 2.0 to HMC, on USB Port B +&ehci1 { + status = "okay"; +}; + +// USB 1.0 +&uhci { + status = "okay"; +}; + +&sgpiom0 { + status="okay"; + ngpios = <128>; + gpio-line-names = + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "RUN_POWER_FAULT_L-I","SYS_RST_IN_L-O", + "RUN_POWER_PG-I","PWR_BRAKE_L-O", + "SYS_RST_OUT_L-I","RUN_POWER_EN-O", + "L0L1_RST_REQ_OUT_L-I","SHDN_FORCE_L-O", + "L2_RST_REQ_OUT_L-I","SHDN_REQ_L-O", + "SHDN_OK_L-I","UID_LED_N-O", + "BMC_I2C1_FPGA_ALERT_L-I","SYS_FAULT_LED_N-O", + "BMC_I2C0_FPGA_ALERT_L-I","PWR_LED_N-O", + "FPGA_RSVD_FFU3-I","", + "FPGA_RSVD_FFU2-I","", + "FPGA_RSVD_FFU1-I","", + "FPGA_RSVD_FFU0-I","BMC_I2C_SSIF_ALERT_L-O", + "CPU_BOOT_DONE-I","JTAG_MUX_SELECT-O", + "SPI_BMC_FPGA_INT_L-I","RTC_CLR_L-O", + "THERM_BB_WARN_L-I","UART_MUX_SEL-O", + "THERM_BB_OVERT_L-I","", + "CPU0_UPHY3_PRSNT1_L-I","IOBRD0_RUN_POWER_EN-O", + "CPU0_UPHY3_PRSNT0_L-I","IOBRD1_RUN_POWER_EN-O", + "CPU0_UPHY2_PRSNT1_L-I","FPGA_RSVD_FFU4-O", + "CPU0_UPHY2_PRSNT0_L-I","FPGA_RSVD_FFU5-O", + "CPU0_UPHY1_PRSNT1_L-I","FPGA_RSVD_FFU6-O", + "CPU0_UPHY1_PRSNT0_L-I","FPGA_RSVD_FFU7-O", + "CPU0_UPHY0_PRSNT1_L-I","RSVD_NV_PLT_DETECT-O", + "CPU0_UPHY0_PRSNT0_L-I","SPI1_INT_L-O", + "CPU1_UPHY3_PRSNT1_L-I","", + "CPU1_UPHY3_PRSNT0_L-I","HMC_EROT_MUX_STATUS", + "CPU1_UPHY2_PRSNT1_L-I","", + "CPU1_UPHY2_PRSNT0_L-I","", + "CPU1_UPHY1_PRSNT1_L-I","", + "CPU1_UPHY1_PRSNT0_L-I","", + "CPU1_UPHY0_PRSNT1_L-I","", + "CPU1_UPHY0_PRSNT0_L-I","", + "FAN1_PRESENT_L-I","", + "FAN0_PRESENT_L-I","", + "","", + "IPEX_CABLE_PRSNT_L-I","", + "M2_1_PRSNT_L-I","", + "M2_0_PRSNT_L-I","", + "CPU1_UPHY4_PRSNT1_L-I","", + "CPU0_UPHY4_PRSNT0_L-I","", + "","", + "I2C_RTC_ALERT_L-I","", + "FAN7_PRESENT_L-I","", + "FAN6_PRESENT_L-I","", + "FAN5_PRESENT_L-I","", + "FAN4_PRESENT_L-I","", + "FAN3_PRESENT_L-I","", + "FAN2_PRESENT_L-I","", + "IOBRD0_IOX_INT_L-I","", + "IOBRD1_PRSNT_L-I","", + "IOBRD0_PRSNT_L-I","", + "IOBRD1_PWR_GOOD-I","", + "IOBRD0_PWR_GOOD-I","", + "","", + "","", + "FAN_FAIL_IN_L-I","", + "","", + "","", + "","", + "PDB_CABLE_PRESENT_L-I","", + "","", + "CHASSIS_PWR_BRK_L-I","", + "","", + "IOBRD1_IOX_INT_L-I","", + "10GBE_SMBALRT_L-I","", + "PCIE_WAKE_L-I","", + "I2C_M21_ALERT_L-I","", + "I2C_M20_ALERT_L-I","", + "TRAY_FAST_SHDN_L-I","", + "UID_BTN_N-I","", + "PWR_BTN_L-I","", + "PSU_SMB_ALERT_L-I","", + "","", + "","", + "NODE_LOC_ID[0]-I","", + "NODE_LOC_ID[1]-I","", + "NODE_LOC_ID[2]-I","", + "NODE_LOC_ID[3]-I","", + "NODE_LOC_ID[4]-I","", + "NODE_LOC_ID[5]-I","", + "FAN10_PRESENT_L-I","", + "FAN9_PRESENT_L-I","", + "FAN8_PRESENT_L-I","", + "FPGA1_READY_HMC-I","", + "DP_HPD-I","", + "HMC_I2C3_FPGA_ALERT_L-I","", + "HMC_I2C2_FPGA_ALERT_L-I","", + "FPGA0_READY_HMC-I","", + "","", + "","", + "","", + "","", + "LEAK_DETECT_ALERT_L-I","", + "MOD1_B2B_CABLE_PRESENT_L-I","", + "MOD1_CLINK_CABLE_PRESENT_L-I","", + "FAN11_PRESENT_L-I","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "","", + "RSVD_SGPIO_IN_CRC[0]","RSVD_SGPIO_O_CRC[7]", + "RSVD_SGPIO_IN_CRC[1]","RSVD_SGPIO_O_CRC[6]", + "RSVD_SGPIO_IN_CRC[2]","RSVD_SGPIO_O_CRC[5]", + "RSVD_SGPIO_IN_CRC[3]","RSVD_SGPIO_O_CRC[4]", + "RSVD_SGPIO_IN_CRC[4]","RSVD_SGPIO_O_CRC[3]", + "RSVD_SGPIO_IN_CRC[5]","RSVD_SGPIO_O_CRC[2]", + "RSVD_SGPIO_IN_CRC[6]","RSVD_SGPIO_O_CRC[1]", + "RSVD_SGPIO_IN_CRC[7]","RSVD_SGPIO_O_CRC[0]"; +}; + +// I2C1, SSIF IPMI interface +&i2c0 { + status = "okay"; + clock-frequency = <400000>; + + ssif-bmc@10 { + compatible = "ssif-bmc"; + reg = <0x10>; + }; +}; + +// I2C2 +// BMC_I2C1_FPGA - Secondary FPGA +// HMC EROT +&i2c1 { + status = "okay"; + clock-frequency = <400000>; + multi-master; +}; + +// I2C3 +// BMC_I2C0_FPGA - Primary FPGA +// HMC FRU EEPROM +&i2c2 { + status = "okay"; + clock-frequency = <400000>; + multi-master; +}; + +// I2C4 +&i2c3 { + status = "disabled"; +}; + +// I2C5 +// RTC Driver +// IO Expander +&i2c4 { + status = "okay"; + clock-frequency = <400000>; + + // Module 0, Expander @0x21 + exp4: gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = ; + gpio-line-names = + "RTC_MUX_SEL-O", + "PCI_MUX_SEL-O", + "TPM_MUX_SEL-O", + "FAN_MUX-SEL-O", + "SGMII_MUX_SEL-O", + "DP_MUX_SEL-O", + "UPHY3_USB_SEL-O", + "NCSI_MUX_SEL-O", + "BMC_PHY_RST-O", + "RTC_CLR_L-O", + "BMC_12V_CTRL-O", + "PS_RUN_IO0_PG-I", + "", + "", + "", + ""; + }; +}; + +// I2C6 +// Module 0/1 I2C MUX x3 +&i2c5 { + status = "okay"; + clock-frequency = <400000>; + multi-master; + + i2c-mux@71 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x71>; + i2c-mux-idle-disconnect; + + imux16: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux17: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + i2c-mux@74 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x74>; + i2c-mux-idle-disconnect; + + i2c17mux0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c17mux1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + i2c17mux2: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + i2c17mux3: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + }; + + imux18: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux19: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + + i2c-mux@72 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x72>; + i2c-mux-idle-disconnect; + + imux20: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux21: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = + "RST_CX_0_L-O", + "RST_CX_1_L-O", + "CX0_SSD0_PRSNT_L-I", + "CX1_SSD1_PRSNT_L-I", + "CX_BOOT_CMPLT_CX0-I", + "CX_BOOT_CMPLT_CX1-I", + "CX_TWARN_CX0_L-I", + "CX_TWARN_CX1_L-I", + "CX_OVT_SHDN_CX0-I", + "CX_OVT_SHDN_CX1-I", + "FNP_L_CX0-O", + "FNP_L_CX1-O", + "", + "MCU_GPIO-I", + "MCU_RST_N-O", + "MCU_RECOVERY_N-O"; + }; + }; + + imux22: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux23: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + + i2c-mux@73 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x73>; + i2c-mux-idle-disconnect; + + imux24: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux25: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + i2c-mux@70 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + i2c25mux0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c25mux1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + i2c25mux2: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + i2c25mux3: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + }; + + imux26: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux27: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + + i2c-mux@75 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x75>; + i2c-mux-idle-disconnect; + + imux28: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux29: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + i2c-mux@74 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x74>; + i2c-mux-idle-disconnect; + + i2c29mux0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c29mux1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + i2c29mux2: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + i2c29mux3: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + }; + + imux30: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux31: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + + i2c-mux@76 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux32: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux33: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = + "SEC_RST_CX_0_L-O", + "SEC_RST_CX_1_L-O", + "SEC_CX0_SSD0_PRSNT_L-I", + "SEC_CX1_SSD1_PRSNT_L-I", + "SEC_CX_BOOT_CMPLT_CX0-I", + "SEC_CX_BOOT_CMPLT_CX1-I", + "SEC_CX_TWARN_CX0_L-I", + "SEC_CX_TWARN_CX1_L-I", + "SEC_CX_OVT_SHDN_CX0-I", + "SEC_CX_OVT_SHDN_CX1-I", + "SEC_FNP_L_CX0-O", + "SEC_FNP_L_CX1-O", + "", + "SEC_MCU_GPIO-I", + "SEC_MCU_RST_N-O", + "SEC_MCU_RECOVERY_N-O"; + }; + }; + + imux34: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux35: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; + + i2c-mux@77 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x77>; + i2c-mux-idle-disconnect; + + imux36: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux37: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux38: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux39: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; +}; + +// I2C7 +// Module 0/1 Leak Sensors +// Module 0/1 Fan Controllers +&i2c6 { + status = "okay"; + clock-frequency = <400000>; + + pmic@12 { + compatible = "ti,lm5066i"; + reg = <0x12>; + shunt-resistor-micro-ohms = <190>; + status = "okay"; + }; + + pmic@14 { + compatible = "ti,lm5066i"; + reg = <0x14>; + shunt-resistor-micro-ohms = <190>; + status = "okay"; + }; + + pwm@20 { + compatible = "maxim,max31790"; + reg = <0x20>; + }; + + pwm@23 { + compatible = "maxim,max31790"; + reg = <0x23>; + }; + + pwm@2c { + compatible = "maxim,max31790"; + reg = <0x2c>; + }; + + pwm@2f { + compatible = "maxim,max31790"; + reg = <0x2f>; + }; +}; + +// I2C9 +// M.2 +&i2c8 { + status = "okay"; + clock-frequency = <400000>; + multi-master; +}; + +// I2C10 +// HMC IO Expander +// Module 0/1 IO Expanders +&i2c9 { + status = "okay"; + clock-frequency = <400000>; + + // Module 0, Expander @0x20 + exp0: gpio@20 { + compatible = "nxp,pca9555"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = ; + gpio-line-names = + "FPGA_THERM_OVERT_L-I", + "FPGA_READY_BMC-I", + "HMC_BMC_DETECT-O", + "HMC_PGOOD-O", + "", + "BMC_STBY_CYCLE-O", + "FPGA_EROT_FATAL_ERROR_L-I", + "WP_HW_EXT_CTRL_L-O", + "EROT_FPGA_RST_L-O", + "FPGA_EROT_RECOVERY_L-O", + "BMC_EROT_FPGA_SPI_MUX_SEL-O", + "USB_HUB_RESET_L-O", + "NCSI_CS1_SEL-O", + "SGPIO_EN_L-O", + "B2B_IOEXP_INT_L-I", + "I2C_BUS_MUX_RESET_L-O"; + }; + + // Module 1, Expander @0x21 + exp1: gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = ; + gpio-line-names = + "SEC_FPGA_THERM_OVERT_L-I", + "SEC_FPGA_READY_BMC-I", + "", + "", + "", + "", + "SEC_FPGA_EROT_FATAL_ERROR_L-I", + "SEC_WP_HW_EXT_CTRL_L-O", + "SEC_EROT_FPGA_RST_L-O", + "SEC_FPGA_EROT_RECOVERY_L-O", + "SEC_BMC_EROT_FPGA_SPI_MUX_SEL-O", + "SEC_USB2_HUB_RST_L-O", + "", + "", + "", + "SEC_I2C_BUS_MUX_RESET_L-O"; + }; + + // HMC Expander @0x27 + exp2: gpio@27 { + compatible = "nxp,pca9555"; + reg = <0x27>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = ; + gpio-line-names = + "HMC_PRSNT_L-I", + "HMC_READY-I", + "HMC_EROT_FATAL_ERROR_L-I", + "I2C_MUX_SEL-O", + "HMC_EROT_SPI_MUX_SEL-O", + "HMC_EROT_RECOVERY_L-O", + "HMC_EROT_RST_L-O", + "GLOBAL_WP_HMC-O", + "FPGA_RST_L-O", + "USB2_HUB_RST-O", + "CPU_UART_MUX_SEL-O", + "", + "", + "", + "", + ""; + }; + + // HMC Expander @0x74 + exp3: gpio@74 { + compatible = "nxp,pca9555"; + reg = <0x74>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = ; + gpio-line-names = + "IOB_PRSNT_L", + "IOB_DP_HPD", + "IOX_BMC_RESET", + "IOB_IOEXP_INT_L", + "IOB_UID_LED_L", + "IOB_UID_BTN_L", + "IOB_SYS_RST_BTN_L", + "IOB_PWR_LED_L", + "IOB_PWR_BTN_L", + "IOB_PHY_RST", + "CPLD_JTAG_MUX_SEL", + "", + "", + "", + "", + ""; + }; +}; + +// I2C11 +// BMC FRU EEPROM +// BMC Temp Sensor +&i2c10 { + status = "okay"; + clock-frequency = <400000>; + + // BMC FRU EEPROM - 256 bytes + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + pagesize = <8>; + }; +}; + +// I2C12 +&i2c11 { + status = "disabled"; +}; + +// I2C13 +&i2c12 { + status = "disabled"; +}; + +// I2C14 +// Module 0 UPHY3 SMBus +&i2c13 { + status = "disabled"; +}; + +// I2C15 +// Module 1 UPHY3 SMBus +&i2c14 { + status = "okay"; + clock-frequency = <100000>; + multi-master; + + //E1.S drive slot 0-3 + i2c-mux@77 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x77>; + i2c-mux-idle-disconnect; + + e1si2c0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + e1si2c1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + e1si2c2: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + e1si2c3: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; +}; + +// I2C16 +&i2c15 { + status = "okay"; + clock-frequency = <100000>; + multi-master; + + //E1.S drive slot 4-7 + i2c-mux@77 { + compatible = "nxp,pca9546"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x77>; + i2c-mux-idle-disconnect; + + e1si2c4: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + e1si2c5: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + e1si2c6: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + e1si2c7: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; +}; + +&rng { + status = "okay"; +}; + +&gpio0 { + gpio-line-names = + /*A0-A7*/ "", "", "", "", "", "", "", "", + /*B0-B7*/ "", "", "", "", "", "", "", "", + /*C0-C7*/ "SGPIO_I2C_MUX_SEL-O", "", "", "", "", "", "", "", + /*D0-D7*/ "", "", "", "UART1_MUX_SEL-O", "", "FPGA_PEX_RST_L-O", "", "", + /*E0-E7*/ "RTL8221_PHY_RST_L-O", "RTL8211_PHY_INT_L-I", "", "UART3_MUX_SEL-O", + "", "", "", "SGPIO_BMC_EN-O", + /*F0-F7*/ "", "", "", "", "", "", "", "", + /*G0-G7*/ "", "", "", "", "", "", "", "", + /*H0-H7*/ "", "", "", "", "", "", "", "", + /*I0-I7*/ "", "", "", "", "", "QSPI2_RST_L-O", "GLOBAL_WP_BMC-O", "BMC_DDR4_TEN-O", + /*J0-J7*/ "", "", "", "", "", "", "", "", + /*K0-K7*/ "", "", "", "", "", "", "", "", + /*L0-L7*/ "", "", "", "", "", "", "", "", + /*M0-M7*/ "PCIE_EP_RST_EN-O", "BMC_FRU_WP-O", "HMC_RESET_L-O", "STBY_POWER_EN-O", + "STBY_POWER_PG-I", "PCIE_EP_RST_L-O", "", "", + /*N0-N7*/ "", "", "", "", "", "", "", "", + /*O0-O7*/ "", "", "", "", "", "", "", "", + /*P0-P7*/ "", "", "", "", "", "", "", "", + /*Q0-Q7*/ "", "", "", "", "", "", "", "", + /*R0-R7*/ "", "", "", "", "", "", "", "", + /*S0-S7*/ "", "", "", "", "", "", "", "", + /*T0-T7*/ "", "", "", "", "", "", "", "", + /*U0-U7*/ "", "", "", "", "", "", "", "", + /*V0-V7*/ "AP_EROT_REQ-O", "EROT_AP_GNT-I", "", "","PCB_TEMP_ALERT-I", "","", "", + /*W0-W7*/ "", "", "", "", "", "", "", "", + /*X0-X7*/ "", "", "TPM_MUX_SEL-O", "", "", "", "", "", + /*Y0-Y7*/ "", "", "", "EMMC_RST-O", "","", "", "", + /*Z0-Z7*/ "BMC_READY-O","", "", "", "", "", "", ""; +}; + +&gpio1 { + /* 36 1.8V GPIOs */ + gpio-line-names = + /*A0-A7*/ "", "", "", "", "", "", "", "", + /*B0-B7*/ "", "", "", "", "", "", "IO_EXPANDER_INT_L-I","", + /*C0-C7*/ "", "", "", "", "", "", "", "", + /*D0-D7*/ "", "", "", "", "", "", "SPI_HOST_TPM_RST_L-O", "SPI_BMC_FPGA_INT_L-I", + /*E0-E7*/ "", "", "", "", "", "", "", ""; +}; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-lanyang.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-lanyang.dts index 370738572a55..65b2208f5a90 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-lanyang.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-lanyang.dts @@ -52,12 +52,12 @@ hdd_fault { gpios = <&gpio ASPEED_GPIO(B, 3) GPIO_ACTIVE_HIGH>; }; bmc_err { - lable = "BMC_fault"; + label = "BMC_fault"; gpios = <&gpio ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>; }; sys_err { - lable = "Sys_fault"; + label = "Sys_fault"; gpios = <&gpio ASPEED_GPIO(H, 7) GPIO_ACTIVE_HIGH>; }; }; @@ -264,49 +264,49 @@ &gfx { }; &gpio { - pin_gpio_b0 { + pin-gpio-b0-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_HDD1_PWR_EN"; }; - pin_gpio_b5 { + pin-gpio-b5-hog { gpio-hog; gpios = ; input; line-name = "BMC_USB1_OCI2"; }; - pin_gpio_h5 { + pin-gpio-h5-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_CP0_PERST_ENABLE_R"; }; - pin_gpio_z2 { + pin-gpio-z2-hog { gpio-hog; gpios = ; output-high; line-name = "RST_PCA9546_U177_N"; }; - pin_gpio_aa6 { + pin-gpio-aa6-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_CP0_RESET_N"; }; - pin_gpio_aa7 { + pin-gpio-aa7-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_TPM_RESET_N"; }; - pin_gpio_ab0 { + pin-gpio-ab0-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-nicole.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-nicole.dts index b1d0ff85d397..1a7c61750d0d 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-nicole.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-nicole.dts @@ -248,27 +248,27 @@ &gpio { /*AB0-AB7*/ "","","","","","","","", /*AC0-AC7*/ "","","","","","","",""; - func_mode0 { + func-mode0-hog { gpio-hog; gpios = ; output-low; }; - func_mode1 { + func-mode1-hog { gpio-hog; gpios = ; output-low; }; - func_mode2 { + func-mode2-hog { gpio-hog; gpios = ; output-low; }; - seq_cont { + seq-cont-hog { gpio-hog; gpios = ; output-low; }; - ncsi_cfg { + ncsi-cfg-hog { gpio-hog; input; gpios = ; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-palmetto.dts index 45631b47a7b3..123da82c04d5 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-palmetto.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-palmetto.dts @@ -209,140 +209,140 @@ &lpc_ctrl { }; &gpio { - pin_func_mode0 { + pin-func-mode0-hog { gpio-hog; gpios = ; output-low; line-name = "func_mode0"; }; - pin_func_mode1 { + pin-func-mode1-hog { gpio-hog; gpios = ; output-low; line-name = "func_mode1"; }; - pin_func_mode2 { + pin-func-mode2-hog { gpio-hog; gpios = ; output-low; line-name = "func_mode2"; }; - pin_gpio_a0 { + pin-gpio-a0-hog { gpio-hog; gpios = ; input; line-name = "BMC_FAN_RESERVED_N"; }; - pin_gpio_a1 { + pin-gpio-a1-hog { gpio-hog; gpios = ; output-high; line-name = "APSS_WDT_N"; }; - pin_gpio_b1 { + pin-gpio-b1-hog { gpio-hog; gpios = ; output-high; line-name = "APSS_BOOT_MODE"; }; - pin_gpio_b2 { + pin-gpio-b2-hog { gpio-hog; gpios = ; output-high; line-name = "APSS_RESET_N"; }; - pin_gpio_b7 { + pin-gpio-b7-hog { gpio-hog; gpios = ; output-high; line-name = "SPIVID_STBY_RESET_N"; }; - pin_gpio_d1 { + pin-gpio-d1-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_POWER_UP"; }; - pin_gpio_f1 { + pin-gpio-f1-hog { gpio-hog; gpios = ; input; line-name = "BMC_BATTERY_TEST"; }; - pin_gpio_f4 { + pin-gpio-f4-hog { gpio-hog; gpios = ; input; line-name = "AST_HW_FAULT_N"; }; - pin_gpio_f5 { + pin-gpio-f5-hog { gpio-hog; gpios = ; input; line-name = "AST_SYS_FAULT_N"; }; - pin_gpio_f7 { + pin-gpio-f7-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_FULL_SPEED_N"; }; - pin_gpio_g3 { + pin-gpio-g3-hog { gpio-hog; gpios = ; output-high; line-name = "BMC_FAN_ERROR_N"; }; - pin_gpio_g4 { + pin-gpio-g4-hog { gpio-hog; gpios = ; input; line-name = "BMC_WDT_RST1_P"; }; - pin_gpio_g5 { + pin-gpio-g5-hog { gpio-hog; gpios = ; input; line-name = "BMC_WDT_RST2_P"; }; - pin_gpio_h0 { + pin-gpio-h0-hog { gpio-hog; gpios = ; input; line-name = "PE_SLOT_TEST_EN_N"; }; - pin_gpio_h1 { + pin-gpio-h1-hog { gpio-hog; gpios = ; input; line-name = "BMC_RTCRST_N"; }; - pin_gpio_h2 { + pin-gpio-h2-hog { gpio-hog; gpios = ; output-high; line-name = "SYS_PWROK_BMC"; }; - pin_gpio_h7 { + pin-gpio-h7-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-romulus.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-romulus.dts index 24df24ad9c80..e6b383f6e977 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-romulus.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-romulus.dts @@ -263,17 +263,17 @@ &gpio { /*AB0-AB7*/ "","","","","","","","", /*AC0-AC7*/ "","","","","","","",""; - nic_func_mode0 { + nic-func-mode0-hog { gpio-hog; gpios = ; output-low; }; - nic_func_mode1 { + nic-func-mode1-hog { gpio-hog; gpios = ; output-low; }; - seq_cont { + seq-cont-hog { gpio-hog; gpios = ; output-low; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-swift.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-swift.dts deleted file mode 100644 index a0e8c97e944a..000000000000 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-swift.dts +++ /dev/null @@ -1,974 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/dts-v1/; -#include "aspeed-g5.dtsi" -#include -#include - -/ { - model = "Swift BMC"; - compatible = "ibm,swift-bmc", "aspeed,ast2500"; - - chosen { - stdout-path = &uart5; - bootargs = "console=ttyS4,115200 earlycon"; - }; - - memory@80000000 { - reg = <0x80000000 0x20000000>; - }; - - reserved-memory { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - flash_memory: region@98000000 { - no-map; - reg = <0x98000000 0x04000000>; /* 64M */ - }; - - gfx_memory: framebuffer { - size = <0x01000000>; - alignment = <0x01000000>; - compatible = "shared-dma-pool"; - reusable; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - event-air-water { - label = "air-water"; - gpios = <&gpio ASPEED_GPIO(B, 5) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - event-checkstop { - label = "checkstop"; - gpios = <&gpio ASPEED_GPIO(J, 2) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - event-ps0-presence { - label = "ps0-presence"; - gpios = <&gpio ASPEED_GPIO(R, 7) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - event-ps1-presence { - label = "ps1-presence"; - gpios = <&gpio ASPEED_GPIO(N, 0) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - event-oppanel-presence { - label = "oppanel-presence"; - gpios = <&gpio ASPEED_GPIO(A, 7) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - event-opencapi-riser-presence { - label = "opencapi-riser-presence"; - gpios = <&gpio ASPEED_GPIO(I, 0) GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - iio-hwmon-battery { - compatible = "iio-hwmon"; - io-channels = <&adc 12>; - }; - - gpio-keys-polled { - compatible = "gpio-keys-polled"; - poll-interval = <1000>; - - event-scm0-presence { - label = "scm0-presence"; - gpios = <&pca9552 6 GPIO_ACTIVE_LOW>; - linux,code = <6>; - }; - - event-scm1-presence { - label = "scm1-presence"; - gpios = <&pca9552 7 GPIO_ACTIVE_LOW>; - linux,code = <7>; - }; - - event-cpu0vrm-presence { - label = "cpu0vrm-presence"; - gpios = <&pca9552 12 GPIO_ACTIVE_LOW>; - linux,code = <12>; - }; - - event-cpu1vrm-presence { - label = "cpu1vrm-presence"; - gpios = <&pca9552 13 GPIO_ACTIVE_LOW>; - linux,code = <13>; - }; - - event-fan0-presence { - label = "fan0-presence"; - gpios = <&pca0 5 GPIO_ACTIVE_LOW>; - linux,code = <5>; - }; - - event-fan1-presence { - label = "fan1-presence"; - gpios = <&pca0 6 GPIO_ACTIVE_LOW>; - linux,code = <6>; - }; - - event-fan2-presence { - label = "fan2-presence"; - gpios = <&pca0 7 GPIO_ACTIVE_LOW>; - linux,code = <7>; - }; - - event-fan3-presence { - label = "fan3-presence"; - gpios = <&pca0 8 GPIO_ACTIVE_LOW>; - linux,code = <8>; - }; - - event-fanboost-presence { - label = "fanboost-presence"; - gpios = <&pca0 9 GPIO_ACTIVE_LOW>; - linux,code = <9>; - }; - }; - - leds { - compatible = "gpio-leds"; - - fan0 { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca0 0 GPIO_ACTIVE_LOW>; - }; - - fan1 { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca0 1 GPIO_ACTIVE_LOW>; - }; - - fan2 { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca0 2 GPIO_ACTIVE_LOW>; - }; - - fan3 { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca0 3 GPIO_ACTIVE_LOW>; - }; - - fanboost { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca0 4 GPIO_ACTIVE_LOW>; - }; - - front-fault { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca1 2 GPIO_ACTIVE_LOW>; - }; - - front-power { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca1 3 GPIO_ACTIVE_LOW>; - }; - - front-id { - retain-state-shutdown; - default-state = "keep"; - gpios = <&pca1 0 GPIO_ACTIVE_LOW>; - }; - - rear-fault { - gpios = <&gpio ASPEED_GPIO(N, 2) GPIO_ACTIVE_LOW>; - }; - - rear-id { - gpios = <&gpio ASPEED_GPIO(N, 4) GPIO_ACTIVE_LOW>; - }; - }; - - fsi: gpio-fsi { - compatible = "fsi-master-gpio", "fsi-master"; - #address-cells = <2>; - #size-cells = <0>; - no-gpio-delays; - - clock-gpios = <&gpio ASPEED_GPIO(P, 1) GPIO_ACTIVE_HIGH>; - data-gpios = <&gpio ASPEED_GPIO(P, 2) GPIO_ACTIVE_HIGH>; - mux-gpios = <&gpio ASPEED_GPIO(P, 4) GPIO_ACTIVE_HIGH>; - enable-gpios = <&gpio ASPEED_GPIO(P, 0) GPIO_ACTIVE_HIGH>; - trans-gpios = <&gpio ASPEED_GPIO(P, 3) GPIO_ACTIVE_HIGH>; - }; - - iio-hwmon-dps310 { - compatible = "iio-hwmon"; - io-channels = <&dps 0>; - }; - -}; - -&fmc { - status = "okay"; - - flash@0 { - status = "okay"; - label = "bmc"; - m25p,fast-read; - spi-max-frequency = <100000000>; - partitions { - #address-cells = < 1 >; - #size-cells = < 1 >; - compatible = "fixed-partitions"; - u-boot@0 { - reg = < 0 0x60000 >; - label = "u-boot"; - }; - u-boot-env@60000 { - reg = < 0x60000 0x20000 >; - label = "u-boot-env"; - }; - obmc-ubi@80000 { - reg = < 0x80000 0x7F80000>; - label = "obmc-ubi"; - }; - }; - }; - - flash@1 { - status = "okay"; - label = "alt-bmc"; - m25p,fast-read; - spi-max-frequency = <100000000>; - partitions { - #address-cells = < 1 >; - #size-cells = < 1 >; - compatible = "fixed-partitions"; - u-boot@0 { - reg = < 0 0x60000 >; - label = "alt-u-boot"; - }; - u-boot-env@60000 { - reg = < 0x60000 0x20000 >; - label = "alt-u-boot-env"; - }; - obmc-ubi@80000 { - reg = < 0x80000 0x7F80000>; - label = "alt-obmc-ubi"; - }; - }; - }; -}; - -&spi1 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_spi1_default>; - - flash@0 { - status = "okay"; - label = "pnor"; - m25p,fast-read; - spi-max-frequency = <100000000>; - }; -}; - -&uart1 { - /* Rear RS-232 connector */ - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_txd1_default - &pinctrl_rxd1_default - &pinctrl_nrts1_default - &pinctrl_ndtr1_default - &pinctrl_ndsr1_default - &pinctrl_ncts1_default - &pinctrl_ndcd1_default - &pinctrl_nri1_default>; -}; - -&uart2 { - /* APSS */ - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_txd2_default &pinctrl_rxd2_default>; -}; - -&uart5 { - status = "okay"; -}; - -&lpc_ctrl { - status = "okay"; - memory-region = <&flash_memory>; - flash = <&spi1>; -}; - -&mac0 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_rmii1_default>; - use-ncsi; - clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>, - <&syscon ASPEED_CLK_MAC1RCLK>; - clock-names = "MACCLK", "RCLK"; -}; - -&i2c2 { - status = "okay"; - - /* MUX -> - * Samtec 1 - * Samtec 2 - */ -}; - -&i2c3 { - status = "okay"; - - max31785@52 { - compatible = "maxim,max31785a"; - reg = <0x52>; - #address-cells = <1>; - #size-cells = <0>; - - fan@0 { - compatible = "pmbus-fan"; - reg = <0>; - tach-pulses = <2>; - maxim,fan-rotor-input = "tach"; - maxim,fan-pwm-freq = <25000>; - maxim,fan-no-watchdog; - maxim,fan-no-fault-ramp; - maxim,fan-ramp = <2>; - maxim,fan-fault-pin-mon; - }; - - fan@1 { - compatible = "pmbus-fan"; - reg = <1>; - tach-pulses = <2>; - maxim,fan-rotor-input = "tach"; - maxim,fan-pwm-freq = <25000>; - maxim,fan-no-watchdog; - maxim,fan-no-fault-ramp; - maxim,fan-ramp = <2>; - maxim,fan-fault-pin-mon; - }; - - fan@2 { - compatible = "pmbus-fan"; - reg = <2>; - tach-pulses = <2>; - maxim,fan-rotor-input = "tach"; - maxim,fan-pwm-freq = <25000>; - maxim,fan-no-watchdog; - maxim,fan-no-fault-ramp; - maxim,fan-ramp = <2>; - maxim,fan-fault-pin-mon; - }; - - fan@3 { - compatible = "pmbus-fan"; - reg = <3>; - tach-pulses = <2>; - maxim,fan-rotor-input = "tach"; - maxim,fan-pwm-freq = <25000>; - maxim,fan-no-watchdog; - maxim,fan-no-fault-ramp; - maxim,fan-ramp = <2>; - maxim,fan-fault-pin-mon; - }; - - fan@4 { - compatible = "pmbus-fan"; - reg = <4>; - tach-pulses = <2>; - maxim,fan-rotor-input = "tach"; - maxim,fan-pwm-freq = <25000>; - maxim,fan-no-watchdog; - maxim,fan-no-fault-ramp; - maxim,fan-ramp = <2>; - maxim,fan-fault-pin-mon; - }; - }; - - pca0: pca9552@60 { - compatible = "nxp,pca9552"; - reg = <0x60>; - #address-cells = <1>; - #size-cells = <0>; - - gpio-controller; - #gpio-cells = <2>; - - gpio@0 { - reg = <0>; - type = ; - }; - - gpio@1 { - reg = <1>; - type = ; - }; - - gpio@2 { - reg = <2>; - type = ; - }; - - gpio@3 { - reg = <3>; - type = ; - }; - - gpio@4 { - reg = <4>; - type = ; - }; - - gpio@5 { - reg = <5>; - type = ; - }; - - gpio@6 { - reg = <6>; - type = ; - }; - - gpio@7 { - reg = <7>; - type = ; - }; - - gpio@8 { - reg = <8>; - type = ; - }; - - gpio@9 { - reg = <9>; - type = ; - }; - - gpio@10 { - reg = <10>; - type = ; - }; - - gpio@11 { - reg = <11>; - type = ; - }; - - gpio@12 { - reg = <12>; - type = ; - }; - - gpio@13 { - reg = <13>; - type = ; - }; - - gpio@14 { - reg = <14>; - type = ; - }; - - gpio@15 { - reg = <15>; - type = ; - }; - }; - - power-supply@68 { - compatible = "ibm,cffps2"; - reg = <0x68>; - }; - - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - - power-supply@69 { - compatible = "ibm,cffps2"; - reg = <0x69>; - }; - - eeprom@51 { - compatible = "atmel,24c64"; - reg = <0x51>; - }; -}; - -&i2c7 { - status = "okay"; - - dps: dps310@76 { - compatible = "infineon,dps310"; - reg = <0x76>; - #io-channel-cells = <0>; - }; - - tmp275@48 { - compatible = "ti,tmp275"; - reg = <0x48>; - }; - - si7021a20@20 { - compatible = "si,si7021a20"; - reg = <0x20>; - }; - - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - - pca1: pca9551@60 { - compatible = "nxp,pca9551"; - reg = <0x60>; - #address-cells = <1>; - #size-cells = <0>; - - gpio-controller; - #gpio-cells = <2>; - - gpio@0 { - reg = <0>; - type = ; - }; - - gpio@1 { - reg = <1>; - type = ; - }; - - gpio@2 { - reg = <2>; - type = ; - }; - - gpio@3 { - reg = <3>; - type = ; - }; - - gpio@4 { - reg = <4>; - type = ; - }; - - gpio@5 { - reg = <5>; - type = ; - }; - - gpio@6 { - reg = <6>; - type = ; - }; - - gpio@7 { - reg = <7>; - type = ; - }; - }; -}; - -&i2c8 { - status = "okay"; - - pca9552: pca9552@60 { - compatible = "nxp,pca9552"; - reg = <0x60>; - #address-cells = <1>; - #size-cells = <0>; - gpio-controller; - #gpio-cells = <2>; - - gpio-line-names = "PS_SMBUS_RESET_N", "APSS_RESET_N", - "GPU0_TH_OVERT_N_BUFF", "GPU1_TH_OVERT_N_BUFF", - "GPU2_TH_OVERT_N_BUFF", "GPU3_TH_OVERT_N_BUFF", - "P9_SCM0_PRES", "P9_SCM1_PRES", - "GPU0_PWR_GOOD_BUFF", "GPU1_PWR_GOOD_BUFF", - "GPU2_PWR_GOOD_BUFF", "GPU3_PWR_GOOD_BUFF", - "PRESENT_VRM_CP0_N", "PRESENT_VRM_CP1_N", - "12V_BREAKER_FLT_N", "THROTTLE_UNLATCHED_N"; - - gpio@0 { - reg = <0>; - type = ; - }; - - gpio@1 { - reg = <1>; - type = ; - }; - - gpio@2 { - reg = <2>; - type = ; - }; - - gpio@3 { - reg = <3>; - type = ; - }; - - gpio@4 { - reg = <4>; - type = ; - }; - - gpio@5 { - reg = <5>; - type = ; - }; - - gpio@6 { - reg = <6>; - type = ; - }; - - gpio@7 { - reg = <7>; - type = ; - }; - - gpio@8 { - reg = <8>; - type = ; - }; - - gpio@9 { - reg = <9>; - type = ; - }; - - gpio@10 { - reg = <10>; - type = ; - }; - - gpio@11 { - reg = <11>; - type = ; - }; - - gpio@12 { - reg = <12>; - type = ; - }; - - gpio@13 { - reg = <13>; - type = ; - }; - - gpio@14 { - reg = <14>; - type = ; - }; - - gpio@15 { - reg = <15>; - type = ; - }; - }; - - rtc@32 { - compatible = "epson,rx8900"; - reg = <0x32>; - }; - - eeprom@51 { - compatible = "atmel,24c64"; - reg = <0x51>; - }; - - ucd90160@64 { - compatible = "ti,ucd90160"; - reg = <0x64>; - }; -}; - -&i2c9 { - status = "okay"; - - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - - tmp423a@4c { - compatible = "ti,tmp423"; - reg = <0x4c>; - }; - - ir35221@71 { - compatible = "infineon,ir35221"; - reg = <0x71>; - }; - - ir35221@72 { - compatible = "infineon,ir35221"; - reg = <0x72>; - }; - - pca2: pca9539@74 { - compatible = "nxp,pca9539"; - reg = <0x74>; - #address-cells = <1>; - #size-cells = <0>; - gpio-controller; - #gpio-cells = <2>; - - gpio@0 { - reg = <0>; - }; - - gpio@1 { - reg = <1>; - }; - - gpio@2 { - reg = <2>; - }; - - gpio@3 { - reg = <3>; - }; - - gpio@4 { - reg = <4>; - }; - - gpio@5 { - reg = <5>; - }; - - gpio@6 { - reg = <6>; - }; - - gpio@7 { - reg = <7>; - }; - - gpio@8 { - reg = <8>; - }; - - gpio@9 { - reg = <9>; - }; - - gpio@10 { - reg = <10>; - }; - - gpio@11 { - reg = <11>; - }; - - gpio@12 { - reg = <12>; - }; - - gpio@13 { - reg = <13>; - }; - - gpio@14 { - reg = <14>; - }; - - gpio@15 { - reg = <15>; - }; - }; -}; - -&i2c10 { - status = "okay"; - - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - - tmp423a@4c { - compatible = "ti,tmp423"; - reg = <0x4c>; - }; - - ir35221@71 { - compatible = "infineon,ir35221"; - reg = <0x71>; - }; - - ir35221@72 { - compatible = "infineon,ir35221"; - reg = <0x72>; - }; - - pca3: pca9539@74 { - compatible = "nxp,pca9539"; - reg = <0x74>; - #address-cells = <1>; - #size-cells = <0>; - gpio-controller; - #gpio-cells = <2>; - - gpio@0 { - reg = <0>; - }; - - gpio@1 { - reg = <1>; - }; - - gpio@2 { - reg = <2>; - }; - - gpio@3 { - reg = <3>; - }; - - gpio@4 { - reg = <4>; - }; - - gpio@5 { - reg = <5>; - }; - - gpio@6 { - reg = <6>; - }; - - gpio@7 { - reg = <7>; - }; - - gpio@8 { - reg = <8>; - }; - - gpio@9 { - reg = <9>; - }; - - gpio@10 { - reg = <10>; - }; - - gpio@11 { - reg = <11>; - }; - - gpio@12 { - reg = <12>; - }; - - gpio@13 { - reg = <13>; - }; - - gpio@14 { - reg = <14>; - }; - - gpio@15 { - reg = <15>; - }; - }; -}; - -&i2c11 { - /* MUX - * -> PCIe Slot 0 - * -> PCIe Slot 1 - * -> PCIe Slot 2 - * -> PCIe Slot 3 - */ - status = "okay"; -}; - -&i2c12 { - status = "okay"; - - tmp275@48 { - compatible = "ti,tmp275"; - reg = <0x48>; - }; - - tmp275@4a { - compatible = "ti,tmp275"; - reg = <0x4a>; - }; -}; - -&i2c13 { - status = "okay"; -}; - -&vuart { - status = "okay"; -}; - -&gfx { - status = "okay"; - memory-region = <&gfx_memory>; -}; - -&wdt1 { - aspeed,reset-type = "none"; - aspeed,external-signal; - aspeed,ext-push-pull; - aspeed,ext-active-high; - - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_wdtrst1_default>; -}; - -&wdt2 { - aspeed,alt-boot; -}; - -&ibt { - status = "okay"; -}; - -&adc { - status = "okay"; -}; - -&sdmmc { - status = "okay"; -}; - -&sdhci1 { - status = "okay"; - - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sd2_default>; -}; - -#include "ibm-power9-dual.dtsi" diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-zaius.dts index 9904f0a58cfa..6ac7b0aa6e54 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-zaius.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-zaius.dts @@ -509,25 +509,25 @@ &gpio { /*AB0-AB7*/ "","","","","","","","", /*AC0-AC7*/ "","","","","","","",""; - line_iso_u146_en { + line-iso-u146-en-hog { gpio-hog; gpios = ; output-high; }; - ncsi_mux_en_n { + ncsi-mux-en-n-hog { gpio-hog; gpios = ; output-low; }; - line_bmc_i2c2_sw_rst_n { + line-bmc-i2c2-sw-rst-n-hog { gpio-hog; gpios = ; output-high; }; - line_bmc_i2c5_sw_rst_n { + line-bmc-i2c5-sw-rst-n-hog { gpio-hog; gpios = ; output-high; diff --git a/arch/arm/boot/dts/broadcom/bcm63138.dtsi b/arch/arm/boot/dts/broadcom/bcm63138.dtsi index e74ba6bf370d..4ec568586b14 100644 --- a/arch/arm/boot/dts/broadcom/bcm63138.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm63138.dtsi @@ -184,13 +184,69 @@ ubus@fffe8000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; - ranges = <0 0xfffe8000 0x8100>; + ranges = <0 0xfffe8000 0x10000>; timer: timer@80 { compatible = "brcm,bcm6328-timer", "syscon"; reg = <0x80 0x3c>; }; + /* GPIOs 0 .. 31 */ + gpio0: gpio@100 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x100 0x04>, <0x114 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@104 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x104 0x04>, <0x118 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@108 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x108 0x04>, <0x11c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@10c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x10c 0x04>, <0x120 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@110 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x110 0x04>, <0x124 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + rng@300 { + compatible = "brcm,iproc-rng200"; + reg = <0x300 0x28>; + interrupts = ; + }; + serial0: serial@600 { compatible = "brcm,bcm6345-uart"; reg = <0x600 0x1b>; @@ -209,6 +265,14 @@ serial1: serial@620 { status = "disabled"; }; + leds: led-controller@700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x700 0xdc>; + status = "disabled"; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -248,6 +312,19 @@ bootlut: bootlut@8000 { reg = <0x8000 0x50>; }; + pl081_dma: dma-controller@d000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0xd000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; + reboot { compatible = "syscon-reboot"; regmap = <&timer>; diff --git a/arch/arm/boot/dts/broadcom/bcm63148.dtsi b/arch/arm/boot/dts/broadcom/bcm63148.dtsi index 53703827ee3f..e071cddb28fc 100644 --- a/arch/arm/boot/dts/broadcom/bcm63148.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm63148.dtsi @@ -99,6 +99,62 @@ bus@ff800000 { #size-cells = <1>; ranges = <0 0xfffe8000 0x8000>; + /* GPIOs 0 .. 31 */ + gpio0: gpio@100 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x100 0x04>, <0x114 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@104 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x104 0x04>, <0x118 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@108 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x108 0x04>, <0x11c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@10c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x10c 0x04>, <0x120 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@110 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x110 0x04>, <0x124 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + rng@300 { + compatible = "brcm,iproc-rng200"; + reg = <0x300 0x28>; + interrupts = ; + }; + uart0: serial@600 { compatible = "brcm,bcm6345-uart"; reg = <0x600 0x20>; @@ -108,6 +164,14 @@ uart0: serial@600 { status = "disabled"; }; + leds: led-controller@700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x700 0xdc>; + status = "disabled"; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/boot/dts/broadcom/bcm63178.dtsi b/arch/arm/boot/dts/broadcom/bcm63178.dtsi index 6d8d33498983..430750b3030f 100644 --- a/arch/arm/boot/dts/broadcom/bcm63178.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm63178.dtsi @@ -117,6 +117,97 @@ bus@ff800000 { #size-cells = <1>; ranges = <0 0xff800000 0x800000>; + watchdog@480 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x480 0x10>; + }; + + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -143,6 +234,27 @@ nandcs: nand@0 { }; }; + leds: led-controller@3000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x3000 0xdc>; + 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 = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; + uart0: serial@12000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x12000 0x1000>; diff --git a/arch/arm/boot/dts/broadcom/bcm6846.dtsi b/arch/arm/boot/dts/broadcom/bcm6846.dtsi index e0e06af3fe89..f5591a45d2e4 100644 --- a/arch/arm/boot/dts/broadcom/bcm6846.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6846.dtsi @@ -196,6 +196,7 @@ uart0: serial@640 { rng@b80 { compatible = "brcm,iproc-rng200"; reg = <0xb80 0x28>; + interrupts = ; }; leds: led-controller@800 { diff --git a/arch/arm/boot/dts/broadcom/bcm6855.dtsi b/arch/arm/boot/dts/broadcom/bcm6855.dtsi index 52915ec6f339..a88c3f0fbcb0 100644 --- a/arch/arm/boot/dts/broadcom/bcm6855.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6855.dtsi @@ -116,6 +116,103 @@ bus@ff800000 { #size-cells = <1>; ranges = <0 0xff800000 0x800000>; + watchdog@480 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x480 0x10>; + }; + + watchdog@4c0 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x4c0 0x10>; + status = "disabled"; + }; + + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -143,6 +240,27 @@ nandcs: nand@0 { }; }; + leds: led-controller@3000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x3000 0xdc>; + 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 = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; + uart0: serial@12000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x12000 0x1000>; @@ -151,5 +269,14 @@ uart0: serial@12000 { clock-names = "uartclk", "apb_pclk"; status = "disabled"; }; + + uart1: serial@13000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x13000 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&uart_clk>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm6878.dtsi b/arch/arm/boot/dts/broadcom/bcm6878.dtsi index 70cf23a65fdb..dd837bf69390 100644 --- a/arch/arm/boot/dts/broadcom/bcm6878.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6878.dtsi @@ -108,6 +108,111 @@ bus@ff800000 { #size-cells = <1>; ranges = <0 0xff800000 0x800000>; + watchdog@480 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x480 0x10>; + }; + + watchdog@4c0 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x4c0 0x10>; + status = "disabled"; + }; + + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + + leds: led-controller@700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x700 0xdc>; + status = "disabled"; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -134,10 +239,23 @@ nandcs: nand@0 { }; }; + pl081_dma: dma-controller@11000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0x11000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; + uart0: serial@12000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x12000 0x1000>; - interrupts = ; + interrupts = ; clocks = <&uart_clk>, <&uart_clk>; clock-names = "uartclk", "apb_pclk"; status = "disabled"; diff --git a/arch/arm/boot/dts/broadcom/bcm7445.dtsi b/arch/arm/boot/dts/broadcom/bcm7445.dtsi index 5ac2042515b8..c6307c7437e3 100644 --- a/arch/arm/boot/dts/broadcom/bcm7445.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm7445.dtsi @@ -237,7 +237,8 @@ memc@0 { ranges = <0x0 0x0 0x80000>; memc-ddr@2000 { - compatible = "brcm,brcmstb-memc-ddr"; + compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x", + "brcm,brcmstb-memc-ddr"; reg = <0x2000 0x800>; }; @@ -259,7 +260,8 @@ memc@80000 { ranges = <0x0 0x80000 0x80000>; memc-ddr@2000 { - compatible = "brcm,brcmstb-memc-ddr"; + compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x", + "brcm,brcmstb-memc-ddr"; reg = <0x2000 0x800>; }; @@ -281,7 +283,8 @@ memc@100000 { ranges = <0x0 0x100000 0x80000>; memc-ddr@2000 { - compatible = "brcm,brcmstb-memc-ddr"; + compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x", + "brcm,brcmstb-memc-ddr"; reg = <0x2000 0x800>; }; diff --git a/arch/arm/boot/dts/broadcom/bcm958625-meraki-mx6x-common.dtsi b/arch/arm/boot/dts/broadcom/bcm958625-meraki-mx6x-common.dtsi index 71a8b77b46f4..7e71aecb7251 100644 --- a/arch/arm/boot/dts/broadcom/bcm958625-meraki-mx6x-common.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm958625-meraki-mx6x-common.dtsi @@ -17,21 +17,21 @@ pwm-leds { led-1 { function = LED_FUNCTION_INDICATOR; color = ; - pwms = <&pwm 1 50000>; + pwms = <&pwm 1 50000 0>; max-brightness = <255>; }; led-2 { function = LED_FUNCTION_POWER; color = ; - pwms = <&pwm 2 50000>; + pwms = <&pwm 2 50000 0>; max-brightness = <255>; }; led-3 { function = LED_FUNCTION_INDICATOR; color = ; - pwms = <&pwm 3 50000>; + pwms = <&pwm 3 50000 0>; max-brightness = <255>; }; }; @@ -132,7 +132,6 @@ pwm_leds: pwm_leds { &pwm { status = "okay"; - #pwm-cells = <2>; }; &uart0 { diff --git a/arch/arm/boot/dts/intel/ixp/intel-ixp42x-linksys-wrv54g.dts b/arch/arm/boot/dts/intel/ixp/intel-ixp42x-linksys-wrv54g.dts index 98275a363c57..cb1842c83ac8 100644 --- a/arch/arm/boot/dts/intel/ixp/intel-ixp42x-linksys-wrv54g.dts +++ b/arch/arm/boot/dts/intel/ixp/intel-ixp42x-linksys-wrv54g.dts @@ -72,10 +72,55 @@ spi { cs-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; num-chipselects = <1>; - switch@0 { + ethernet-switch@0 { compatible = "micrel,ks8995"; reg = <0>; spi-max-frequency = <50000000>; + + /* + * The PHYs are accessed over the external MDIO + * bus and not internally through the switch control + * registers. + */ + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-port@0 { + reg = <0>; + label = "1"; + phy-mode = "mii"; + phy-handle = <&phy1>; + }; + ethernet-port@1 { + reg = <1>; + label = "2"; + phy-mode = "mii"; + phy-handle = <&phy2>; + }; + ethernet-port@2 { + reg = <2>; + label = "3"; + phy-mode = "mii"; + phy-handle = <&phy3>; + }; + ethernet-port@3 { + reg = <3>; + label = "4"; + phy-mode = "mii"; + phy-handle = <&phy4>; + }; + ethernet-port@4 { + reg = <4>; + ethernet = <ðb>; + phy-mode = "mii"; + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + }; }; }; @@ -135,40 +180,59 @@ pci@c0000000 { }; /* - * EthB - connected to the KS8995 switch ports 1-4 - * FIXME: the boardfile defines .phy_mask = 0x1e for this port to enable output to - * all four switch ports, also using an out of tree multiphy patch. - * Do we need a new binding and property for this? + * EthB connects to the KS8995 CPU port and faces ports 1-4 + * through the switch fabric. + * + * To complicate things, the MDIO channel is also only + * accessible through EthB, but used independently for PHY + * control. */ - ethernet@c8009000 { + ethb: ethernet@c8009000 { status = "okay"; queue-rx = <&qmgr 3>; queue-txready = <&qmgr 20>; - phy-mode = "rgmii"; - phy-handle = <&phy4>; + phy-mode = "mii"; + fixed-link { + speed = <100>; + full-duplex; + }; mdio { #address-cells = <1>; #size-cells = <0>; - /* Should be ports 1-4 on the KS8995 switch */ + /* + * LAN ports 1-4 on the KS8995 switch + * and PHY5 for WAN need to be accessed + * through this external MDIO channel. + */ + phy1: ethernet-phy@1 { + reg = <1>; + }; + phy2: ethernet-phy@2 { + reg = <2>; + }; + phy3: ethernet-phy@3 { + reg = <3>; + }; phy4: ethernet-phy@4 { reg = <4>; }; - - /* Should be port 5 on the KS8995 switch */ phy5: ethernet-phy@5 { reg = <5>; }; }; }; - /* EthC - connected to KS8995 switch port 5 */ - ethernet@c800a000 { + /* + * EthC connects to MII-P5 on the KS8995 bypassing + * all of the switch logic and facing PHY5 + */ + ethc: ethernet@c800a000 { status = "okay"; queue-rx = <&qmgr 4>; queue-txready = <&qmgr 21>; - phy-mode = "rgmii"; + phy-mode = "mii"; phy-handle = <&phy5>; }; }; diff --git a/arch/arm/boot/dts/marvell/kirkwood-km_common.dtsi b/arch/arm/boot/dts/marvell/kirkwood-km_common.dtsi index 52baffe45f12..259cb3d5f16d 100644 --- a/arch/arm/boot/dts/marvell/kirkwood-km_common.dtsi +++ b/arch/arm/boot/dts/marvell/kirkwood-km_common.dtsi @@ -27,8 +27,8 @@ serial@12000 { i2c { compatible = "i2c-gpio"; - gpios = < &gpio0 8 GPIO_ACTIVE_HIGH /* sda */ - &gpio0 9 GPIO_ACTIVE_HIGH>; /* scl */ + sda-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; + scl-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; i2c-gpio,delay-us = <2>; /* ~100 kHz */ }; }; diff --git a/arch/arm/boot/dts/mediatek/Makefile b/arch/arm/boot/dts/mediatek/Makefile index 1957947cb41c..e48de3efeb3b 100644 --- a/arch/arm/boot/dts/mediatek/Makefile +++ b/arch/arm/boot/dts/mediatek/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_MEDIATEK) += \ mt2701-evb.dtb \ + mt6572-jty-d101.dtb \ + mt6572-lenovo-a369i.dtb \ mt6580-evbp1.dtb \ mt6582-prestigio-pmt5008-3g.dtb \ mt6589-aquaris5.dtb \ diff --git a/arch/arm/boot/dts/mediatek/mt6572-jty-d101.dts b/arch/arm/boot/dts/mediatek/mt6572-jty-d101.dts new file mode 100644 index 000000000000..18c3cab6b7a3 --- /dev/null +++ b/arch/arm/boot/dts/mediatek/mt6572-jty-d101.dts @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Max Shevchenko + */ + +/dts-v1/; +#include "mt6572.dtsi" + +/ { + model = "JTY D101"; + compatible = "jty,d101", "mediatek,mt6572"; + + aliases { + serial0 = &uart0; + }; + + chosen { + #address-cells = <1>; + #size-cells = <1>; + stdout-path = "serial0:921600n8"; + + framebuffer: framebuffer@bf400000 { + compatible = "simple-framebuffer"; + memory-region = <&framebuffer_reserved>; + width = <1024>; + height = <600>; + stride = <(1024 * 2)>; + format = "r5g6b5"; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x40000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + connsys@80000000 { + reg = <0x80000000 0x100000>; + no-map; + }; + + modem@be000000 { + reg = <0xbe000000 0x1400000>; + no-map; + }; + + framebuffer_reserved: framebuffer@bf400000 { + reg = <0xbf400000 0xc00000>; + no-map; + }; + }; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/mediatek/mt6572-lenovo-a369i.dts b/arch/arm/boot/dts/mediatek/mt6572-lenovo-a369i.dts new file mode 100644 index 000000000000..c2f0c60ea777 --- /dev/null +++ b/arch/arm/boot/dts/mediatek/mt6572-lenovo-a369i.dts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Max Shevchenko + */ + +/dts-v1/; +#include "mt6572.dtsi" + +/ { + model = "Lenovo A369i"; + compatible = "lenovo,a369i", "mediatek,mt6572"; + + aliases { + serial0 = &uart0; + }; + + chosen { + #address-cells = <1>; + #size-cells = <1>; + stdout-path = "serial0:921600n8"; + + framebuffer: framebuffer@9fa00000 { + compatible = "simple-framebuffer"; + memory-region = <&framebuffer_reserved>; + width = <480>; + height = <800>; + stride = <(480 * 2)>; + format = "r5g6b5"; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + connsys@80000000 { + reg = <0x80000000 0x100000>; + no-map; + }; + + framebuffer_reserved: framebuffer@9fa00000 { + reg = <0x9fa00000 0x600000>; + no-map; + }; + }; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/mediatek/mt6572.dtsi b/arch/arm/boot/dts/mediatek/mt6572.dtsi new file mode 100644 index 000000000000..ac70f266d698 --- /dev/null +++ b/arch/arm/boot/dts/mediatek/mt6572.dtsi @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Max Shevchenko + */ + +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&sysirq>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "mediatek,mt6589-smp"; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x0>; + }; + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x1>; + }; + }; + + uart_clk: dummy26m { + compatible = "fixed-clock"; + clock-frequency = <26000000>; + #clock-cells = <0>; + }; + + system_clk: dummy13m { + compatible = "fixed-clock"; + clock-frequency = <13000000>; + #clock-cells = <0>; + }; + + rtc_clk: dummy32k { + compatible = "fixed-clock"; + clock-frequency = <32000>; + #clock-cells = <0>; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges; + + watchdog: watchdog@10007000 { + compatible = "mediatek,mt6572-wdt", "mediatek,mt6589-wdt"; + reg = <0x10007000 0x100>; + interrupts = ; + timeout-sec = <15>; + #reset-cells = <1>; + }; + + timer: timer@10008000 { + compatible = "mediatek,mt6572-timer", "mediatek,mt6577-timer"; + reg = <0x10008000 0x80>; + interrupts = ; + clocks = <&system_clk>, <&rtc_clk>; + clock-names = "system-clk", "rtc-clk"; + }; + + sysirq: interrupt-controller@10200100 { + compatible = "mediatek,mt6572-sysirq", "mediatek,mt6577-sysirq"; + reg = <0x10200100 0x1c>; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + }; + + gic: interrupt-controller@10211000 { + compatible = "arm,cortex-a7-gic"; + reg = <0x10211000 0x1000>, + <0x10212000 0x2000>, + <0x10214000 0x2000>, + <0x10216000 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + }; + + uart0: serial@11005000 { + compatible = "mediatek,mt6572-uart", "mediatek,mt6577-uart"; + reg = <0x11005000 0x400>; + interrupts = ; + clocks = <&uart_clk>; + clock-names = "baud"; + status = "disabled"; + }; + + uart1: serial@11006000 { + compatible = "mediatek,mt6572-uart", "mediatek,mt6577-uart"; + reg = <0x11006000 0x400>; + interrupts = ; + clocks = <&uart_clk>; + clock-names = "baud"; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts b/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts index cdc56b53299d..c1ff3248bd8f 100644 --- a/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts +++ b/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts @@ -609,7 +609,7 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff --git a/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi b/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi index 8ac85dac5a96..13c28e92b17e 100644 --- a/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi +++ b/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi @@ -44,7 +44,7 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff --git a/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi b/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi index ef11606a82b3..0417f53b3e96 100644 --- a/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi +++ b/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi @@ -234,7 +234,7 @@ qspi1_flash: flash@0 { compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; m25p,fast-read; @@ -385,7 +385,7 @@ &sdmmc1 { wilc: wifi@0 { reg = <0>; - compatible = "microchip,wilc1000"; + compatible = "microchip,wilc3000", "microchip,wilc1000"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wilc_default>; clocks = <&pmc PMC_TYPE_SYSTEM 9>; diff --git a/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts b/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts index 9fa6f1395aa6..fbae6a9af6c3 100644 --- a/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts +++ b/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts @@ -714,7 +714,7 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff --git a/arch/arm/boot/dts/microchip/at91-sama7d65_curiosity.dts b/arch/arm/boot/dts/microchip/at91-sama7d65_curiosity.dts index 53a657cf4efb..d086437f5e6f 100644 --- a/arch/arm/boot/dts/microchip/at91-sama7d65_curiosity.dts +++ b/arch/arm/boot/dts/microchip/at91-sama7d65_curiosity.dts @@ -38,7 +38,24 @@ reg_5v: regulator-5v { regulator-max-microvolt = <5000000>; regulator-always-on; }; +}; +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can1_default>; + status = "okay"; +}; + +&can2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can2_default>; + status = "okay"; +}; + +&can3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can3_default>; + status = "okay"; }; &dma0 { @@ -278,6 +295,24 @@ &main_xtal { }; &pioa { + pinctrl_can1_default: can1-default { + pinmux = , + ; + bias-disable; + }; + + pinctrl_can2_default: can2-default { + pinmux = , + ; + bias-disable; + }; + + pinctrl_can3_default: can3-default { + pinmux = , + ; + bias-disable; + }; + pinctrl_gmac0_default: gmac0-default { pinmux = , , @@ -352,6 +387,8 @@ &rtt { &sdmmc1 { bus-width = <4>; + no-1-8-v; + sdhci-caps-mask = <0x0 0x00200000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sdmmc1_default>; status = "okay"; diff --git a/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts b/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts index 2543599013b1..3924f62ff0fb 100644 --- a/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts +++ b/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts @@ -35,16 +35,6 @@ aliases { i2c2 = &i2c9; }; - clocks { - slow_xtal { - clock-frequency = <32768>; - }; - - main_xtal { - clock-frequency = <24000000>; - }; - }; - gpio-keys { compatible = "gpio-keys"; @@ -556,6 +546,10 @@ &i2s0 { pinctrl-0 = <&pinctrl_i2s0_default>; }; +&main_xtal { + clock-frequency = <24000000>; +}; + &pdmc0 { #sound-dai-cells = <0>; microchip,mic-pos = , /* MIC 1 */ @@ -885,6 +879,10 @@ input@0 { }; }; +&slow_xtal { + clock-frequency = <32768>; +}; + &spdifrx { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spdifrx_default>; diff --git a/arch/arm/boot/dts/microchip/at91rm9200.dtsi b/arch/arm/boot/dts/microchip/at91rm9200.dtsi index 2a4c83d88733..e105ad855ce8 100644 --- a/arch/arm/boot/dts/microchip/at91rm9200.dtsi +++ b/arch/arm/boot/dts/microchip/at91rm9200.dtsi @@ -714,9 +714,8 @@ usb0: usb@300000 { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 25 GPIO_ACTIVE_HIGH /* sda */ - &pioA 26 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioA 25 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 26 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9260.dtsi b/arch/arm/boot/dts/microchip/at91sam9260.dtsi index ec973f07a961..fc0b6a73204f 100644 --- a/arch/arm/boot/dts/microchip/at91sam9260.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9260.dtsi @@ -781,9 +781,8 @@ nand_controller: nand-controller { i2c_gpio0: i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 23 GPIO_ACTIVE_HIGH /* sda */ - &pioA 24 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioA 23 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 24 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9261.dtsi b/arch/arm/boot/dts/microchip/at91sam9261.dtsi index 0b556c234557..d1d678b77e84 100644 --- a/arch/arm/boot/dts/microchip/at91sam9261.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9261.dtsi @@ -655,8 +655,8 @@ i2c-gpio-0 { compatible = "i2c-gpio"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c_bitbang>; - gpios = <&pioA 7 GPIO_ACTIVE_HIGH>, /* sda */ - <&pioA 8 GPIO_ACTIVE_HIGH>; /* scl */ + sda-gpios = <&pioA 7 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 8 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9263.dtsi b/arch/arm/boot/dts/microchip/at91sam9263.dtsi index 3e9e5ce7c6c8..a4b5d1f228f9 100644 --- a/arch/arm/boot/dts/microchip/at91sam9263.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9263.dtsi @@ -826,9 +826,8 @@ nand_controller1: nand-controller { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioB 4 GPIO_ACTIVE_HIGH /* sda */ - &pioB 5 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioB 4 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioB 5 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9g25-gardena-smart-gateway.dts b/arch/arm/boot/dts/microchip/at91sam9g25-gardena-smart-gateway.dts index e0c1e8df81b1..947c011c1b00 100644 --- a/arch/arm/boot/dts/microchip/at91sam9g25-gardena-smart-gateway.dts +++ b/arch/arm/boot/dts/microchip/at91sam9g25-gardena-smart-gateway.dts @@ -46,7 +46,7 @@ led-power-blue { led-power-green { label = "smartgw:power:green"; gpios = <&pioC 20 GPIO_ACTIVE_HIGH>; - default-state = "on"; + linux,default-trigger = "timer"; }; led-power-red { diff --git a/arch/arm/boot/dts/microchip/at91sam9g45.dtsi b/arch/arm/boot/dts/microchip/at91sam9g45.dtsi index 535e26e05e99..4e00ed2d3ecd 100644 --- a/arch/arm/boot/dts/microchip/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9g45.dtsi @@ -1010,9 +1010,8 @@ nand_controller: nand-controller { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 20 GPIO_ACTIVE_HIGH /* sda */ - &pioA 21 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioA 20 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 21 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <5>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9n12.dtsi b/arch/arm/boot/dts/microchip/at91sam9n12.dtsi index 2f930c39ce4d..af41c3dbb4bf 100644 --- a/arch/arm/boot/dts/microchip/at91sam9n12.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9n12.dtsi @@ -786,9 +786,8 @@ nand_controller: nand-controller { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 30 GPIO_ACTIVE_HIGH /* sda */ - &pioA 31 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioA 30 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 31 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9rl.dtsi b/arch/arm/boot/dts/microchip/at91sam9rl.dtsi index 1fec9fcc7cd1..de74cf2980a0 100644 --- a/arch/arm/boot/dts/microchip/at91sam9rl.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9rl.dtsi @@ -833,8 +833,8 @@ rtc@fffffe00 { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 23 GPIO_ACTIVE_HIGH>, /* sda */ - <&pioA 24 GPIO_ACTIVE_HIGH>; /* scl */ + sda-gpios = <&pioA 23 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 24 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ @@ -847,8 +847,8 @@ i2c-gpio-0 { i2c-gpio-1 { compatible = "i2c-gpio"; - gpios = <&pioD 10 GPIO_ACTIVE_HIGH>, /* sda */ - <&pioD 11 GPIO_ACTIVE_HIGH>; /* scl */ + sda-gpios = <&pioD 10 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioD 11 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/at91sam9x5.dtsi b/arch/arm/boot/dts/microchip/at91sam9x5.dtsi index 17bdf1e4db01..9070fd06995a 100644 --- a/arch/arm/boot/dts/microchip/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/microchip/at91sam9x5.dtsi @@ -933,9 +933,8 @@ nand_controller: nand-controller { i2c-gpio-0 { compatible = "i2c-gpio"; - gpios = <&pioA 30 GPIO_ACTIVE_HIGH /* sda */ - &pioA 31 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioA 30 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA 31 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ @@ -948,9 +947,8 @@ &pioA 31 GPIO_ACTIVE_HIGH /* scl */ i2c-gpio-1 { compatible = "i2c-gpio"; - gpios = <&pioC 0 GPIO_ACTIVE_HIGH /* sda */ - &pioC 1 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioC 0 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioC 1 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ @@ -963,9 +961,8 @@ &pioC 1 GPIO_ACTIVE_HIGH /* scl */ i2c-gpio-2 { compatible = "i2c-gpio"; - gpios = <&pioB 4 GPIO_ACTIVE_HIGH /* sda */ - &pioB 5 GPIO_ACTIVE_HIGH /* scl */ - >; + sda-gpios = <&pioB 4 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioB 5 GPIO_ACTIVE_HIGH>; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ diff --git a/arch/arm/boot/dts/microchip/sam9x7.dtsi b/arch/arm/boot/dts/microchip/sam9x7.dtsi index b217a908f525..66c07e642c3e 100644 --- a/arch/arm/boot/dts/microchip/sam9x7.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x7.dtsi @@ -45,11 +45,13 @@ cpu@0 { clocks { slow_xtal: clock-slowxtal { compatible = "fixed-clock"; + clock-output-names = "slow_xtal"; #clock-cells = <0>; }; main_xtal: clock-mainxtal { compatible = "fixed-clock"; + clock-output-names = "main_xtal"; #clock-cells = <0>; }; }; @@ -983,6 +985,32 @@ pwm0: pwm@f8034000 { status = "disabled"; }; + hlcdc: hlcdc@f8038000 { + compatible = "microchip,sam9x75-xlcdc"; + reg = <0xf8038000 0x4000>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 25>, <&pmc PMC_TYPE_GCK 25>, <&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>; + }; + }; + flx9: flexcom@f8040000 { compatible = "microchip,sam9x7-flexcom", "atmel,sama5d2-flexcom"; reg = <0xf8040000 0x200>; @@ -1087,6 +1115,15 @@ AT91_XDMAC_DT_PER_IF(1) | }; }; + lvds_controller: lvds-controller@f8060000 { + compatible = "microchip,sam9x75-lvds"; + reg = <0xf8060000 0x100>; + interrupts = <56 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 56>; + clock-names = "pclk"; + status = "disabled"; + }; + matrix: matrix@ffffde00 { compatible = "microchip,sam9x7-matrix", "atmel,at91sam9x5-matrix", "syscon"; reg = <0xffffde00 0x200>; diff --git a/arch/arm/boot/dts/microchip/sama5d2.dtsi b/arch/arm/boot/dts/microchip/sama5d2.dtsi index dc22fb679333..17430d7f2055 100644 --- a/arch/arm/boot/dts/microchip/sama5d2.dtsi +++ b/arch/arm/boot/dts/microchip/sama5d2.dtsi @@ -32,6 +32,8 @@ cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a5"; reg = <0>; + d-cache-size = <0x8000>; // L1, 32 KB + i-cache-size = <0x8000>; // L1, 32 KB next-level-cache = <&L2>; }; }; @@ -160,6 +162,7 @@ L2: cache-controller@a00000 { interrupts = <63 IRQ_TYPE_LEVEL_HIGH 4>; cache-unified; cache-level = <2>; + cache-size = <0x20000>; // L2, 128 KB }; ebi: ebi@10000000 { diff --git a/arch/arm/boot/dts/microchip/sama5d3.dtsi b/arch/arm/boot/dts/microchip/sama5d3.dtsi index e95799c17fdb..00ba59ac1968 100644 --- a/arch/arm/boot/dts/microchip/sama5d3.dtsi +++ b/arch/arm/boot/dts/microchip/sama5d3.dtsi @@ -48,6 +48,8 @@ cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a5"; reg = <0x0>; + d-cache-size = <0x8000>; // L1, 32 KB + i-cache-size = <0x8000>; // L1, 32 KB }; }; diff --git a/arch/arm/boot/dts/microchip/sama5d4.dtsi b/arch/arm/boot/dts/microchip/sama5d4.dtsi index 59a7d557c7cb..ec1d68c640de 100644 --- a/arch/arm/boot/dts/microchip/sama5d4.dtsi +++ b/arch/arm/boot/dts/microchip/sama5d4.dtsi @@ -50,6 +50,8 @@ cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a5"; reg = <0>; + d-cache-size = <0x8000>; // L1, 32 KB + i-cache-size = <0x8000>; // L1, 32 KB next-level-cache = <&L2>; }; }; @@ -143,6 +145,7 @@ L2: cache-controller@a00000 { interrupts = <67 IRQ_TYPE_LEVEL_HIGH 4>; cache-unified; cache-level = <2>; + cache-size = <0x20000>; // L2, 128 KB }; ebi: ebi@10000000 { diff --git a/arch/arm/boot/dts/microchip/sama7d65.dtsi b/arch/arm/boot/dts/microchip/sama7d65.dtsi index d08d773b1cc5..c191acc2c89f 100644 --- a/arch/arm/boot/dts/microchip/sama7d65.dtsi +++ b/arch/arm/boot/dts/microchip/sama7d65.dtsi @@ -32,17 +32,29 @@ cpu0: cpu@0 { device_type = "cpu"; clocks = <&pmc PMC_TYPE_CORE PMC_CPUPLL>; clock-names = "cpu"; + d-cache-size = <0x8000>; // L1, 32 KB + i-cache-size = <0x8000>; // L1, 32 KB + next-level-cache = <&L2>; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-size = <0x40000>; // L2, 256 KB + cache-unified; + }; }; }; clocks { main_xtal: clock-mainxtal { compatible = "fixed-clock"; + clock-output-names = "main_xtal"; #clock-cells = <0>; }; slow_xtal: clock-slowxtal { compatible = "fixed-clock"; + clock-output-names = "slow_xtal"; #clock-cells = <0>; }; }; @@ -163,6 +175,86 @@ chipid@e0020000 { reg = <0xe0020000 0x8>; }; + can0: can@e0828000 { + compatible = "bosch,m_can"; + reg = <0xe0828000 0x200>, <0x100000 0x7800>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 58>, <&pmc PMC_TYPE_GCK 58>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 58>; + assigned-clock-rates = <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x3400 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + can1: can@e082c000 { + compatible = "bosch,m_can"; + reg = <0xe082c000 0x200>, <0x100000 0xbc00>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 59>, <&pmc PMC_TYPE_GCK 59>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 59>; + assigned-clock-rates = <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x7800 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + can2: can@e0830000 { + compatible = "bosch,m_can"; + reg = <0xe0830000 0x200>, <0x100000 0x10000>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 60>, <&pmc PMC_TYPE_GCK 60>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 60>; + assigned-clock-rates = <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0xbc00 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + can3: can@e0834000 { + compatible = "bosch,m_can"; + reg = <0xe0834000 0x200>, <0x110000 0x4400>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 61>, <&pmc PMC_TYPE_GCK 61>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 61>; + assigned-clock-rates = <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x0 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + can4: can@e0838000 { + compatible = "bosch,m_can"; + reg = <0xe0838000 0x200>, <0x110000 0x8800>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 62>, <&pmc PMC_TYPE_GCK 62>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 62>; + assigned-clock-rates = <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x4400 0 0 64 0 0 32 32>; + status = "disabled"; + }; + dma2: dma-controller@e1200000 { compatible = "microchip,sama7d65-dma", "microchip,sama7g5-dma"; reg = <0xe1200000 0x1000>; @@ -186,6 +278,45 @@ sdmmc1: mmc@e1208000 { status = "disabled"; }; + aes: crypto@e1600000 { + compatible = "microchip,sama7d65-aes", "atmel,at91sam9g46-aes"; + reg = <0xe1600000 0x100>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 26>; + clock-names = "aes_clk"; + dmas = <&dma0 AT91_XDMAC_DT_PERID(1)>, + <&dma0 AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + }; + + sha: crypto@e1604000 { + compatible = "microchip,sama7d65-sha", "atmel,at91sam9g46-sha"; + reg = <0xe1604000 0x100>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 78>; + clock-names = "sha_clk"; + dmas = <&dma0 AT91_XDMAC_DT_PERID(48)>; + dma-names = "tx"; + }; + + tdes: crypto@e1608000 { + compatible = "microchip,sama7d65-tdes", "atmel,at91sam9g46-tdes"; + reg = <0xe1608000 0x100>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 91>; + clock-names = "tdes_clk"; + dmas = <&dma0 AT91_XDMAC_DT_PERID(54)>, + <&dma0 AT91_XDMAC_DT_PERID(53)>; + dma-names = "tx", "rx"; + }; + + trng: rng@e160c000 { + compatible = "microchip,sama7d65-trng", "microchip,sam9x60-trng"; + reg = <0xe160c000 0x100>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 92>; + }; + dma0: dma-controller@e1610000 { compatible = "microchip,sama7d65-dma", "microchip,sama7g5-dma"; reg = <0xe1610000 0x1000>; @@ -254,6 +385,15 @@ pit64b1: timer@e1804000 { clock-names = "pclk", "gclk"; }; + pwm: pwm@e1818000 { + compatible = "microchip,sama7d65-pwm", "atmel,sama5d2-pwm"; + reg = <0xe1818000 0x500>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 72>; + #pwm-cells = <3>; + status = "disabled"; + }; + flx0: flexcom@e1820000 { compatible = "microchip,sama7d65-flexcom", "atmel,sama5d2-flexcom"; reg = <0xe1820000 0x200>; diff --git a/arch/arm/boot/dts/microchip/sama7g5.dtsi b/arch/arm/boot/dts/microchip/sama7g5.dtsi index 17bcdcf0cf4a..381cbcfcb34a 100644 --- a/arch/arm/boot/dts/microchip/sama7g5.dtsi +++ b/arch/arm/boot/dts/microchip/sama7g5.dtsi @@ -38,6 +38,16 @@ cpu0: cpu@0 { clock-names = "cpu"; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; /* min followed by max */ + d-cache-size = <0x8000>; // L1, 32 KB + i-cache-size = <0x8000>; // L1, 32 KB + next-level-cache = <&L2>; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-size = <0x40000>; // L2, 256 KB + cache-unified; + }; }; }; @@ -117,19 +127,22 @@ map1 { }; clocks { - slow_xtal: slow_xtal { + slow_xtal: clock-slowxtal { compatible = "fixed-clock"; + clock-output-names = "slow_xtal"; #clock-cells = <0>; }; - main_xtal: main_xtal { + main_xtal: clock-mainxtal { compatible = "fixed-clock"; + clock-output-names = "main_xtal"; #clock-cells = <0>; }; - usb_clk: usb_clk { + usb_clk: clock-usbclk { compatible = "fixed-clock"; #clock-cells = <0>; + clock-output-names = "usb_clk"; clock-frequency = <48000000>; }; }; diff --git a/arch/arm/boot/dts/nvidia/Makefile b/arch/arm/boot/dts/nvidia/Makefile index ff2c5bfd8efa..7c1d3cb5dcf0 100644 --- a/arch/arm/boot/dts/nvidia/Makefile +++ b/arch/arm/boot/dts/nvidia/Makefile @@ -31,10 +31,12 @@ dtb-$(CONFIG_ARCH_TEGRA_3x_SOC) += \ tegra30-asus-nexus7-grouper-PM269.dtb \ tegra30-asus-nexus7-grouper-E1565.dtb \ tegra30-asus-nexus7-tilapia-E1565.dtb \ + tegra30-asus-p1801-t.dtb \ tegra30-asus-tf201.dtb \ tegra30-asus-tf300t.dtb \ tegra30-asus-tf300tg.dtb \ tegra30-asus-tf300tl.dtb \ + tegra30-asus-tf600t.dtb \ tegra30-asus-tf700t.dtb \ tegra30-beaver.dtb \ tegra30-cardhu-a02.dtb \ diff --git a/arch/arm/boot/dts/nvidia/tegra30-asus-p1801-t.dts b/arch/arm/boot/dts/nvidia/tegra30-asus-p1801-t.dts new file mode 100644 index 000000000000..9241cc269a89 --- /dev/null +++ b/arch/arm/boot/dts/nvidia/tegra30-asus-p1801-t.dts @@ -0,0 +1,2087 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include +#include +#include + +#include "tegra30.dtsi" +#include "tegra30-cpu-opp.dtsi" +#include "tegra30-cpu-opp-microvolt.dtsi" + +/ { + model = "Asus Portable AiO P1801-T"; + compatible = "asus,p1801-t", "nvidia,tegra30"; + chassis-type = "convertible"; + + aliases { + mmc0 = &sdmmc4; /* eMMC */ + mmc1 = &sdmmc1; /* uSD slot */ + mmc2 = &sdmmc3; /* WiFi */ + + rtc0 = &pmic; + rtc1 = "/rtc@7000e000"; + + display0 = &hdmi; + + serial1 = &uartc; /* Bluetooth */ + serial2 = &uartb; /* GPS */ + }; + + /* + * The decompressor and also some bootloaders rely on a + * pre-existing /chosen node to be available to insert the + * command line and merge other ATAGS info. + */ + chosen {}; + + firmware { + trusted-foundations { + compatible = "tlm,trusted-foundations"; + tlm,version-major = <2>; + tlm,version-minor = <8>; + }; + }; + + memory@80000000 { + reg = <0x80000000 0x80000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + linux,cma@80000000 { + compatible = "shared-dma-pool"; + alloc-ranges = <0x80000000 0x30000000>; + size = <0x10000000>; /* 256MiB */ + linux,cma-default; + reusable; + }; + + framebuffer@abe01000 { + reg = <0xabe01000 (1920 * 1080 * 4)>; + no-map; + }; + + trustzone@bfe00000 { + reg = <0xbfe00000 0x200000>; /* 2MB */ + no-map; + }; + + ramoops@fea00000 { + compatible = "ramoops"; + reg = <0xfea00000 0x10000>; /* 64kB */ + console-size = <0x8000>; /* 32kB */ + record-size = <0x400>; /* 1kB */ + ecc-size = <16>; + }; + }; + + host1x@50000000 { + hdmi: hdmi@54280000 { + status = "okay"; + + hdmi-supply = <&hdmi_5v0_sys>; + pll-supply = <&vdd_1v8_vio>; + vdd-supply = <&vdd_3v3_sys>; + + port { + hdmi_out: endpoint { + remote-endpoint = <&bridge_in>; + }; + }; + }; + }; + + gpio@6000d000 { + init-lpm-in-hog { + gpio-hog; + gpios = ; + input; + }; + + init-lpm-out-hog { + gpio-hog; + gpios = , + ; + output-low; + }; + + tp-vendor-hog { + gpio-hog; + gpios = ; + input; + }; + }; + + vde@6001a000 { + assigned-clocks = <&tegra_car TEGRA30_CLK_VDE>; + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_P>; + assigned-clock-rates = <408000000>; + }; + + pinmux@70000868 { + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinmux { + /* SDMMC1 pinmux */ + sdmmc1-clk { + nvidia,pins = "sdmmc1_clk_pz0"; + nvidia,function = "sdmmc1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-cmd { + nvidia,pins = "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "sdmmc1_cmd_pz1"; + nvidia,function = "sdmmc1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-cd { + nvidia,pins = "gmi_iordy_pi5"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-wp { + nvidia,pins = "vi_d11_pt3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC2 pinmux */ + vi-d1-pd5 { + nvidia,pins = "vi_d1_pd5", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d5_pl3", + "vi_d7_pl5"; + nvidia,function = "sdmmc2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-d8-pl6 { + nvidia,pins = "vi_d8_pl6", + "vi_d9_pl7"; + nvidia,function = "sdmmc2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + + /* SDMMC3 pinmux */ + sdmmc3-clk { + nvidia,pins = "sdmmc3_clk_pa6"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc3-cmd { + nvidia,pins = "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "sdmmc3_dat4_pd1", + "sdmmc3_dat5_pd0", + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC4 pinmux */ + sdmmc4-clk { + nvidia,pins = "sdmmc4_clk_pcc4"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc4-cmd { + nvidia,pins = "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc4-rst-n { + nvidia,pins = "sdmmc4_rst_n_pcc3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + cam-mclk { + nvidia,pins = "cam_mclk_pcc0"; + nvidia,function = "vi_alt3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + drive-sdmmc4 { + nvidia,pins = "drive_gma", + "drive_gmb", + "drive_gmc", + "drive_gmd"; + nvidia,pull-down-strength = <9>; + nvidia,pull-up-strength = <9>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + + /* I2C pinmux */ + gen1-i2c { + nvidia,pins = "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5"; + nvidia,function = "i2c1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = <0>; + }; + gen2-i2c { + nvidia,pins = "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6"; + nvidia,function = "i2c2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = <0>; + }; + cam-i2c { + nvidia,pins = "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2"; + nvidia,function = "i2c3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = <0>; + }; + ddc-i2c { + nvidia,pins = "ddc_scl_pv4", + "ddc_sda_pv5"; + nvidia,function = "i2c4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + }; + pwr-i2c { + nvidia,pins = "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7"; + nvidia,function = "i2cpwr"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = <0>; + }; + hotplug-i2c { + nvidia,pins = "pu4"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* HDMI pinmux */ + hdmi-cec { + nvidia,pins = "hdmi_cec_pee3"; + nvidia,function = "cec"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = <0>; + }; + hdmi-hpd { + nvidia,pins = "hdmi_int_pn7"; + nvidia,function = "hdmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-A */ + ulpi-data0-po1 { + nvidia,pins = "ulpi_data0_po1"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data1-po2 { + nvidia,pins = "ulpi_data1_po2"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data5-po6 { + nvidia,pins = "ulpi_data5_po6"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data7-po0 { + nvidia,pins = "ulpi_data7_po0", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data6_po7"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-B */ + uartb-txd-rts { + nvidia,pins = "uart2_txd_pc2", + "uart2_rts_n_pj6"; + nvidia,function = "uartb"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + uartb-rxd-cts { + nvidia,pins = "uart2_rxd_pc3", + "uart2_cts_n_pj5"; + nvidia,function = "uartb"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-C */ + uartc-rxd-cts { + nvidia,pins = "uart3_cts_n_pa1", + "uart3_rxd_pw7"; + nvidia,function = "uartc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + uartc-txd-rts { + nvidia,pins = "uart3_rts_n_pc0", + "uart3_txd_pw6"; + nvidia,function = "uartc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-D */ + ulpi-nxt-py2 { + nvidia,pins = "ulpi_nxt_py2"; + nvidia,function = "uartd"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-clk-py0 { + nvidia,pins = "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_stp_py3"; + nvidia,function = "uartd"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* I2S pinmux */ + dap-i2s0 { + nvidia,pins = "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3"; + nvidia,function = "i2s0"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap-i2s1 { + nvidia,pins = "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5"; + nvidia,function = "i2s1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap3-fs { + nvidia,pins = "dap3_fs_pp0", + "dap3_din_pp1"; + nvidia,function = "i2s2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap3-dout { + nvidia,pins = "dap3_dout_pp2", + "dap3_sclk_pp3"; + nvidia,function = "i2s2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap-i2s3 { + nvidia,pins = "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7"; + nvidia,function = "i2s3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* sensors pinmux */ + nct-irq { + nvidia,pins = "pcc2"; + nvidia,function = "i2s4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Asus EC pinmux */ + ec-irqs { + nvidia,pins = "kb_row10_ps2", + "kb_row15_ps7"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ec-reqs { + nvidia,pins = "kb_col1_pq1"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* memory type bootstrap */ + mem-boostraps { + nvidia,pins = "gmi_ad4_pg4", + "gmi_ad5_pg5"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* PCI-e pinmux */ + pex-l2-rst-n { + nvidia,pins = "pex_l2_rst_n_pcc6", + "pex_l0_rst_n_pdd1", + "pex_l1_rst_n_pdd5"; + nvidia,function = "pcie"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pex-l2-clkreq-n { + nvidia,pins = "pex_l2_clkreq_n_pcc7", + "pex_l0_prsnt_n_pdd0", + "pex_l0_clkreq_n_pdd2", + "pex_wake_n_pdd3", + "pex_l1_prsnt_n_pdd4", + "pex_l1_clkreq_n_pdd6", + "pex_l2_prsnt_n_pdd7"; + nvidia,function = "pcie"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SPI pinmux */ + spi1-mosi-px4 { + nvidia,pins = "spi1_mosi_px4", + "spi1_sck_px5", + "spi1_cs0_n_px6", + "spi1_miso_px7"; + nvidia,function = "spi1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + spi2-cs1-n-pw2 { + nvidia,pins = "spi2_cs1_n_pw2"; + nvidia,function = "spi2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + spi2-sck-px2 { + nvidia,pins = "spi2_sck_px2"; + nvidia,function = "spi2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-a17-pb0 { + nvidia,pins = "gmi_a17_pb0", + "gmi_a16_pj7"; + nvidia,function = "spi4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-a18-pb1 { + nvidia,pins = "gmi_a18_pb1"; + nvidia,function = "spi4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-a19-pk7 { + nvidia,pins = "gmi_a19_pk7"; + nvidia,function = "spi4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Display A pinmux */ + lcd-pwr0-pb2 { + nvidia,pins = "lcd_pwr0_pb2", + "lcd_pclk_pb3", + "lcd_pwr1_pc1", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d2_pe2", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_vsync_pj4", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_dc0_pn6", + "lcd_sdin_pz2"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-cs0-n-pn4 { + nvidia,pins = "lcd_cs0_n_pn4", + "lcd_sdout_pn5", + "lcd_wr_n_pz3"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + blink { + nvidia,pins = "clk_32k_out_pa0"; + nvidia,function = "blink"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* KBC keys */ + kb-col0-pq0 { + nvidia,pins = "kb_col0_pq0"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + kb-col1-pq1 { + nvidia,pins = "kb_row1_pr1", + "kb_row3_pr3", + "kb_row14_ps6"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + kb-col4-pq4 { + nvidia,pins = "kb_col4_pq4", + "kb_col5_pq5", + "kb_col7_pq7", + "kb_row2_pr2", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row12_ps4", + "kb_row13_ps5"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + gmi-wp-n-pc7 { + nvidia,pins = "gmi_wp_n_pc7", + "gmi_wait_pi7", + "gmi_cs3_n_pk4"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-cs0-n-pj0 { + nvidia,pins = "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-pclk-pt0 { + nvidia,pins = "vi_pclk_pt0"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + + /* GPIO keys pinmux */ + power-key { + nvidia,pins = "pv0"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vol-keys { + nvidia,pins = "kb_col2_pq2", + "kb_col3_pq3"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Bluetooth */ + bt-shutdown { + nvidia,pins = "pu0"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + bt-dev-wake { + nvidia,pins = "pu1"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + bt-host-wake { + nvidia,pins = "pu6"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + pu2 { + nvidia,pins = "pu2"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pu3 { + nvidia,pins = "pu3"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pcc1 { + nvidia,pins = "pcc1"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pv2 { + nvidia,pins = "pv2"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pv3 { + nvidia,pins = "pv3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-vsync-pd6 { + nvidia,pins = "vi_vsync_pd6", + "vi_hsync_pd7"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + vi-d10-pt2 { + nvidia,pins = "vi_d10_pt2", + "vi_d0_pt4", + "pbb0"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + kb-row0-pr0 { + nvidia,pins = "kb_row0_pr0"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad0-pg0 { + nvidia,pins = "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_dqs_pi2", + "gmi_adv_n_pk0", + "gmi_clk_pk1"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad13-ph5 { + nvidia,pins = "gmi_ad13_ph5"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad10-ph2 { + nvidia,pins = "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad14_ph6"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad12-ph4 { + nvidia,pins = "gmi_ad12_ph4", + "gmi_rst_n_pi4"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* USB2 VBUS control */ + usb2-vbus-control { + nvidia,pins = "gmi_ad15_ph7"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* PWM pinmux */ + pwm-0 { + nvidia,pins = "gmi_ad8_ph0"; + nvidia,function = "pwm0"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pwm-2 { + nvidia,pins = "pu5"; + nvidia,function = "pwm2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* S/PDIF pinmux */ + spdif-out { + nvidia,pins = "spdif_out_pk5"; + nvidia,function = "spdif"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + spdif-in { + nvidia,pins = "spdif_in_pk6"; + nvidia,function = "spdif"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-d4-pl2 { + nvidia,pins = "vi_d4_pl2"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-d6-pl4 { + nvidia,pins = "vi_d6_pl4"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + vi-mclk-pt1 { + nvidia,pins = "vi_mclk_pt1"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + jtag-rtck { + nvidia,pins = "jtag_rtck_pu7"; + nvidia,function = "rtck"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + crt-hsync-pv6 { + nvidia,pins = "crt_hsync_pv6", + "crt_vsync_pv7"; + nvidia,function = "crt"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + clk1-out { + nvidia,pins = "clk1_out_pw4"; + nvidia,function = "extperiph1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk2-out { + nvidia,pins = "clk2_out_pw5"; + nvidia,function = "extperiph2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk3-out { + nvidia,pins = "clk3_out_pee0"; + nvidia,function = "extperiph3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sys-clk-req { + nvidia,pins = "sys_clk_req_pz5"; + nvidia,function = "sysclk"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb4 { + nvidia,pins = "pbb4"; + nvidia,function = "vgp4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb5 { + nvidia,pins = "pbb5"; + nvidia,function = "vgp5"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb6 { + nvidia,pins = "pbb6"; + nvidia,function = "vgp6"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk2-req-pcc5 { + nvidia,pins = "clk2_req_pcc5", + "clk1_req_pee2"; + nvidia,function = "dap"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk3-req-pee1 { + nvidia,pins = "clk3_req_pee1"; + nvidia,function = "dev3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + owr { + nvidia,pins = "owr"; + nvidia,function = "owr"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* P1801-T specific pinmux */ + lcd-pwr2 { + nvidia,pins = "lcd_pwr2_pc6", + "lcd_dc1_pd2"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-m1 { + nvidia,pins = "lcd_m1_pw1"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + key-mode { + nvidia,pins = "gmi_cs4_n_pk2"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + splashtop { + nvidia,pins = "gmi_cs6_n_pi3"; + nvidia,function = "nand_alt"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + w8-detect { + nvidia,pins = "gmi_cs7_n_pi6"; + nvidia,function = "nand_alt"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb3 { + nvidia,pins = "pbb3"; + nvidia,function = "vgp3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb7 { + nvidia,pins = "pbb7"; + nvidia,function = "i2s4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + spi2-mosi-px0 { + nvidia,pins = "spi2_mosi_px0"; + nvidia,function = "spi6"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + tp-vendor { + nvidia,pins = "kb_row6_pr6", + "kb_row7_pr7"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + tp-power { + nvidia,pins = "kb_row8_ps0"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* GPIO power/drive control */ + drive-dap1 { + nvidia,pins = "drive_dap1", + "drive_dap2", + "drive_dbg", + "drive_at5", + "drive_gme", + "drive_ddc", + "drive_ao1", + "drive_uart3"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,low-power-mode = ; + nvidia,pull-down-strength = <31>; + nvidia,pull-up-strength = <31>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + drive-sdio1 { + nvidia,pins = "drive_sdio1", + "drive_sdio3"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,pull-down-strength = <46>; + nvidia,pull-up-strength = <42>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + }; + }; + + uartb: serial@70006040 { + compatible = "nvidia,tegra30-hsuart"; + reset-names = "serial"; + /delete-property/ reg-shift; + status = "okay"; + + /* Broadcom GPS BCM47511 */ + }; + + uartc: serial@70006200 { + compatible = "nvidia,tegra30-hsuart"; + reset-names = "serial"; + /delete-property/ reg-shift; + status = "okay"; + + /* Azurewave AW-AH691 BCM43241B0 */ + }; + + pwm: pwm@7000a000 { + status = "okay"; + }; + + i2c@7000c000 { + status = "okay"; + clock-frequency = <280000>; + }; + + i2c@7000c400 { + status = "okay"; + clock-frequency = <400000>; + + /* Nuvoton NPCE791LA0DX embedded controller */ + }; + + i2c@7000c500 { + status = "okay"; + clock-frequency = <100000>; + + accelerometer@f { + compatible = "kionix,kxtf9"; + reg = <0x0f>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vdd-supply = <&vdd_1v8_vio>; + vddio-supply = <&vdd_1v8_vio>; + + mount-matrix = "0", "1", "0", + "1", "0", "0", + "0", "0", "1"; + }; + }; + + hdmi_ddc: i2c@7000c700 { + status = "okay"; + clock-frequency = <33000>; + }; + + i2c@7000d000 { + status = "okay"; + clock-frequency = <400000>; + + rt5640: audio-codec@1c { + compatible = "realtek,rt5640"; + reg = <0x1c>; + + realtek,dmic1-data-pin = <1>; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + clock-names = "mclk"; + + realtek,ldo1-en-gpios = <&gpio TEGRA_GPIO(BB, 6) GPIO_ACTIVE_HIGH>; + }; + + /* Texas Instruments TPS659110 PMIC */ + pmic: pmic@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + interrupts = ; + #interrupt-cells = <2>; + interrupt-controller; + wakeup-source; + + ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + ti,system-power-controller; + ti,sleep-keep-ck32k; + ti,sleep-enable; + + #gpio-cells = <2>; + gpio-controller; + + vcc1-supply = <&vdd_5v0_bat>; + vcc2-supply = <&vdd_5v0_bat>; + vcc3-supply = <&vdd_1v8_vio>; + vcc4-supply = <&vdd_5v0_bat>; + vcc5-supply = <&vdd_5v0_bat>; + vcc6-supply = <&vddio_ddr>; + vcc7-supply = <&vdd_5v0_bat>; + vccio-supply = <&vdd_5v0_bat>; + + pmic-sleep-hog { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + }; + + regulators { + /* vdd1 is not used by Portable AiO */ + + vddio_ddr: vdd2 { + regulator-name = "vddio_ddr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_cpu: vddctrl { + regulator-name = "vdd_cpu,vdd_sys"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-coupled-with = <&vdd_core>; + regulator-coupled-max-spread = <300000>; + regulator-max-step-microvolt = <100000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <1>; + + nvidia,tegra-cpu-regulator; + }; + + vdd_1v8_vio: vio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + /* eMMC VDD */ + vcore_emmc: ldo1 { + regulator-name = "vdd_emmc_core"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + /* uSD slot VDD */ + vdd_usd: ldo2 { + regulator-name = "vdd_usd"; + regulator-min-microvolt = <3100000>; + regulator-max-microvolt = <3100000>; + regulator-always-on; + }; + + /* uSD slot VDDIO */ + vddio_usd: ldo3 { + regulator-name = "vddio_usd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3100000>; + }; + + ldo4 { + regulator-name = "vdd_rtc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + /* ldo5 is not used by Portable AiO */ + + ldo6 { + regulator-name = "avdd_dsi_csi,pwrdet_mipi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo7 { + regulator-name = "vdd_pllm,x,u,a_p_c_s"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <8>; + }; + + ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + ti,regulator-ext-sleep-control = <8>; + }; + }; + }; + + nct72: temperature-sensor@4c { + compatible = "onnn,nct1008"; + reg = <0x4c>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vcc-supply = <&vdd_3v3_sys>; + #thermal-sensor-cells = <1>; + }; + + vdd_core: core-regulator@60 { + compatible = "ti,tps62361"; + reg = <0x60>; + + regulator-name = "tps62361-vout"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1770000>; + regulator-coupled-with = <&vdd_cpu>; + regulator-coupled-max-spread = <300000>; + regulator-max-step-microvolt = <100000>; + regulator-boot-on; + regulator-always-on; + ti,enable-vout-discharge; + ti,vsel0-state-high; + ti,vsel1-state-high; + + nvidia,tegra-core-regulator; + }; + }; + + vdd_5v0_bat: regulator-bat { + compatible = "regulator-fixed"; + regulator-name = "vdd_ac_bat"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_5v0_cp: regulator-sby { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_sby"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_5v0_sys: regulator-5v { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_1v5_ddr: regulator-ddr { + compatible = "regulator-fixed"; + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_3v3_sys: regulator-3v { + compatible = "regulator-fixed"; + regulator-name = "vdd_3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_3v3_com: regulator-com { + compatible = "regulator-fixed"; + regulator-name = "vdd_3v3_com"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + gpio = <&gpio TEGRA_GPIO(D, 0) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_sys>; + }; + + usb2_vbus: regulator-usb2 { + compatible = "regulator-fixed"; + regulator-name = "usb2_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(H, 7) GPIO_ACTIVE_HIGH>; + gpio-open-drain; + vin-supply = <&vdd_5v0_sys>; + }; + + hdmi_5v0_sys: regulator-hdmi { + compatible = "regulator-fixed"; + regulator-name = "hdmi_5v0_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + gpio = <&gpio TEGRA_GPIO(P, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_sys>; + }; + + pmc@7000e400 { + status = "okay"; + nvidia,invert-interrupt; + nvidia,suspend-mode = <2>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <200>; + nvidia,core-pwr-good-time = <3845 3845>; + nvidia,core-pwr-off-time = <0>; + nvidia,core-power-req-active-high; + nvidia,sys-clock-req-active-high; + core-supply = <&vdd_core>; + + i2c-thermtrip { + nvidia,i2c-controller-id = <4>; + nvidia,bus-addr = <0x2d>; + nvidia,reg-addr = <0x3f>; + nvidia,reg-data = <0x81>; + }; + }; + + memory-controller@7000f000 { + emc-timings-3 { + /* Micron 2GB 800MHz */ + nvidia,ram-code = <3>; + + timing-25500000 { + clock-frequency = <25500000>; + + nvidia,emem-configuration = < 0x00030003 0xc0000020 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x75830303 0x001f0000 >; + }; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emem-configuration = < 0x00010003 0xc0000020 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x74630303 0x001f0000 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emem-configuration = < 0x00000003 0xc0000030 + 0x00000001 0x00000001 0x00000003 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0503 0x73c30504 0x001f0000 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emem-configuration = < 0x00000006 0xc0000025 + 0x00000001 0x00000001 0x00000005 0x00000002 + 0x00000003 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0505 0x73840a06 0x001f0000 >; + }; + + timing-400000000 { + clock-frequency = <400000000>; + + nvidia,emem-configuration = < 0x0000000c 0xc0000048 + 0x00000001 0x00000002 0x00000009 0x00000005 + 0x00000005 0x00000001 0x00000002 0x00000008 + 0x00000002 0x00000002 0x00000003 0x00000006 + 0x06030202 0x000d0709 0x7086120a 0x001f0000 >; + }; + + timing-800000000 { + clock-frequency = <800000000>; + + nvidia,emem-configuration = < 0x00000018 0xc0000090 + 0x00000004 0x00000005 0x00000013 0x0000000c + 0x0000000b 0x00000002 0x00000003 0x0000000c + 0x00000002 0x00000002 0x00000004 0x00000008 + 0x08040202 0x00160d13 0x712c2414 0x001f0000 >; + }; + }; + }; + + memory-controller@7000f400 { + emc-timings-3 { + /* Micron 2GB 800MHz */ + nvidia,ram-code = <3>; + + timing-25500000 { + clock-frequency = <25500000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200008>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-dyn-self-ref; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000001 + 0x00000006 0x00000000 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x00000009 + 0x0000000b 0x000000c0 0x00000000 0x00000030 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000007 0x00000007 + 0x00000004 0x00000001 0x00000000 0x00000004 + 0x00000005 0x000000c7 0x00000006 0x00000006 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000287 0xe8000000 0xff00ff00 >; + }; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200008>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-dyn-self-ref; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000002 + 0x0000000d 0x00000001 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x00000009 + 0x0000000b 0x00000181 0x00000000 0x00000060 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x0000000e 0x0000000e + 0x00000004 0x00000002 0x00000000 0x00000004 + 0x00000005 0x0000018e 0x00000006 0x00000006 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x8000040b 0xe8000000 0xff00ff00 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200008>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-dyn-self-ref; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000004 + 0x0000001a 0x00000003 0x00000001 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000001 + 0x00000001 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x00000009 + 0x0000000b 0x00000303 0x00000000 0x000000c0 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x0000001c 0x0000001c + 0x00000004 0x00000004 0x00000000 0x00000004 + 0x00000005 0x0000031c 0x00000006 0x00000006 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000713 0xe8000000 0xff00ff00 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200008>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-dyn-self-ref; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000009 + 0x00000035 0x00000007 0x00000002 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000002 + 0x00000002 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000006 0x00000004 0x00000009 + 0x0000000b 0x00000607 0x00000000 0x00000181 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000038 0x00000038 + 0x00000004 0x00000007 0x00000000 0x00000004 + 0x00000005 0x00000638 0x00000007 0x00000004 + 0x00000000 0x00000000 0x00004288 0x004400a4 + 0x00008000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00020000 + 0x00000100 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000d22 0xe8000000 0xff00ff00 >; + }; + + timing-400000000 { + clock-frequency = <400000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200000>; + nvidia,emc-mode-reset = <0x80000521>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + + nvidia,emc-configuration = < 0x00000012 + 0x00000066 0x0000000c 0x00000004 0x00000003 + 0x00000008 0x00000002 0x0000000a 0x00000004 + 0x00000004 0x00000002 0x00000001 0x00000000 + 0x00000004 0x00000006 0x00000004 0x0000000a + 0x0000000c 0x00000bf0 0x00000000 0x000002fc + 0x00000001 0x00000008 0x00000001 0x00000000 + 0x00000008 0x0000000f 0x0000006c 0x00000200 + 0x00000004 0x0000000c 0x00000000 0x00000004 + 0x00000005 0x00000c30 0x00000000 0x00000004 + 0x00000000 0x00000000 0x00007088 0x001d0084 + 0x00008000 0x00044000 0x00044000 0x00044000 + 0x00044000 0x00044000 0x00044000 0x00044000 + 0x00044000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00048000 0x00048000 0x00048000 + 0x00048000 0x000002a0 0x0800013d 0x00000000 + 0x77fff884 0x01f1f508 0x05057404 0x54000007 + 0x080001e8 0x08000021 0x00000802 0x00020000 + 0x00000100 0x0158000c 0xa0f10000 0x00000000 + 0x00000000 0x800018c8 0xe8000000 0xff00ff89 >; + }; + + timing-800000000 { + clock-frequency = <800000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200018>; + nvidia,emc-mode-reset = <0x80000d71>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000025 + 0x000000ce 0x0000001a 0x00000009 0x00000005 + 0x0000000d 0x00000004 0x00000013 0x00000009 + 0x00000009 0x00000003 0x00000001 0x00000000 + 0x00000007 0x0000000b 0x00000009 0x0000000b + 0x00000012 0x00001820 0x00000000 0x00000608 + 0x00000003 0x00000012 0x00000001 0x00000000 + 0x0000000f 0x00000018 0x000000d8 0x00000200 + 0x00000005 0x00000018 0x00000000 0x00000007 + 0x00000008 0x00001860 0x0000000c 0x00000004 + 0x00000000 0x00000000 0x00005088 0xf0070191 + 0x00008000 0x0000c00a 0x0000000a 0x0000000a + 0x0000000a 0x0000000a 0x0000000a 0x0000000a + 0x0000000a 0x00018000 0x00018000 0x00018000 + 0x00018000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x0000000a 0x0000000a 0x0000000a + 0x0000000a 0x000002a0 0x0800013d 0x22220000 + 0x77fff884 0x01f1f501 0x07077404 0x54000000 + 0x080001e8 0x08000021 0x00000802 0x00020000 + 0x00000100 0x00f0000c 0xa0f10202 0x00000000 + 0x00000000 0x8000308c 0xe8000000 0xff00ff49 >; + }; + }; + }; + + hda@70030000 { + status = "okay"; + }; + + ahub@70080000 { + i2s@70080400 { /* i2s1 */ + status = "okay"; + }; + + /* BT SCO */ + i2s@70080600 { /* i2s3 */ + status = "okay"; + }; + }; + + sdmmc1: mmc@78000000 { + status = "okay"; + + /* SDR104 mode unsupported yet */ + max-frequency = <104000000>; + + cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>; + bus-width = <4>; + + vmmc-supply = <&vdd_usd>; /* ldo2 */ + vqmmc-supply = <&vddio_usd>; /* ldo3 */ + }; + + sdmmc3: mmc@78000400 { + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + keep-power-in-suspend; + bus-width = <4>; + non-removable; + + mmc-pwrseq = <&brcm_wifi_pwrseq>; + vmmc-supply = <&vdd_3v3_com>; + vqmmc-supply = <&vdd_1v8_vio>; + + /* Azurewave AW-AH691 BCM43241B0 */ + wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + + interrupt-parent = <&gpio>; + interrupts = ; + interrupt-names = "host-wake"; + }; + }; + + sdmmc4: mmc@78000600 { + status = "okay"; + bus-width = <8>; + + non-removable; + mmc-ddr-3_3v; + + vmmc-supply = <&vcore_emmc>; + vqmmc-supply = <&vdd_1v8_vio>; + }; + + /* USB via ASUS connector */ + usb@7d000000 { + compatible = "nvidia,tegra30-udc"; + status = "okay"; + dr_mode = "peripheral"; + }; + + usb-phy@7d000000 { + status = "okay"; + dr_mode = "peripheral"; + nvidia,hssync-start-delay = <0>; + nvidia,xcvr-lsfslew = <2>; + nvidia,xcvr-lsrslew = <2>; + vbus-supply = <&vdd_5v0_sys>; + }; + + /* mini-USB port */ + usb@7d004000 { + status = "okay"; + }; + + usb-phy@7d004000 { + status = "okay"; + vbus-supply = <&usb2_vbus>; + }; + + /* Full size USB */ + usb@7d008000 { + status = "okay"; + }; + + usb-phy@7d008000 { + status = "okay"; + vbus-supply = <&vdd_5v0_bat>; + }; + + pad_battery: battery-cell { + compatible = "simple-battery"; + device-chemistry = "lithium-ion-polymer"; + charge-full-design-microamp-hours = <5136000>; + energy-full-design-microwatt-hours = <38000000>; + operating-range-celsius = <0 45>; + }; + + /* Connected to a 18.4" LVDS panel */ + bridge { + compatible = "mstar,tsumu88adt3-lf-1"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_in: endpoint { + remote-endpoint = <&hdmi_out>; + }; + }; + + port@1 { + reg = <1>; + + bridge_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; + + /* PMIC has a built-in 32KHz oscillator which is used by PMC */ + clk32k_in: clock-32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "pmic-oscillator"; + }; + + connector { + compatible = "hdmi-connector"; + label = "HDMI"; + type = "a"; + + /* low: tablet, high: dock */ + hpd-gpios = <&gpio TEGRA_GPIO(H, 4) GPIO_ACTIVE_LOW>; + ddc-i2c-bus = <&hdmi_ddc>; + ddc-en-gpios = <&gpio TEGRA_GPIO(H, 3) GPIO_ACTIVE_HIGH>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&bridge_out>; + }; + }; + }; + + cpus { + cpu0: cpu@0 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu1: cpu@1 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu2: cpu@2 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu3: cpu@3 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + key-power { + label = "Power"; + gpios = <&gpio TEGRA_GPIO(V, 0) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + + key-volume-up { + label = "Volume Up"; + gpios = <&gpio TEGRA_GPIO(Q, 2) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + + key-volume-down { + label = "Volume Down"; + gpios = <&gpio TEGRA_GPIO(Q, 3) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + + switch-docking-station-mode { + label = "Mode"; + gpios = <&gpio TEGRA_GPIO(K, 2) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + }; + + opp-table-actmon { + opp-800000000 { + opp-supported-hw = <0x0006>; + }; + + /delete-node/ opp-900000000; + }; + + opp-table-emc { + opp-800000000-1300 { + opp-supported-hw = <0x0006>; + }; + + /delete-node/ opp-900000000-1350; + }; + + brcm_wifi_pwrseq: pwrseq-wifi { + compatible = "mmc-pwrseq-simple"; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_BLINK>; + clock-names = "ext_clock"; + + reset-gpios = <&gpio TEGRA_GPIO(D, 4) GPIO_ACTIVE_LOW>; + post-power-on-delay-ms = <300>; + power-off-delay-us = <300>; + }; + + sound { + compatible = "asus,tegra-audio-rt5640-p1801-t", + "nvidia,tegra-audio-rt5640"; + nvidia,model = "Asus Portable AiO P1801-T RT5642"; + + nvidia,audio-routing = + "Headphones", "HPOR", + "Headphones", "HPOL", + "Speakers", "SPORP", + "Speakers", "SPORN", + "Speakers", "SPOLP", + "Speakers", "SPOLN", + "DMIC1", "Mic Jack"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5640>; + + nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(W, 2) GPIO_ACTIVE_LOW>; + + clocks = <&tegra_car TEGRA30_CLK_PLL_A>, + <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + + assigned-clocks = <&tegra_car TEGRA30_CLK_EXTERN1>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA30_CLK_EXTERN1>; + }; + + thermal-zones { + /* + * NCT72 has two sensors: + * + * 0: internal that monitors ambient/skin temperature + * 1: external that is connected to the CPU's diode + * + * Ideally we should use userspace thermal governor, + * but it's a much more complex solution. The "skin" + * zone exists as a simpler solution which prevents + * the Portable AiO from getting too hot from a user's + * tactile perspective. The CPU zone is intended to + * protect silicon from damage. + */ + + skin-thermal { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + thermal-sensors = <&nct72 0>; + + trips { + trip0: skin-alert { + /* throttle at 57C until temperature drops to 56.8C */ + temperature = <57000>; + hysteresis = <200>; + type = "passive"; + }; + + trip1: skin-crit { + /* shut down at 65C */ + temperature = <65000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&trip0>; + 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>, + <&actmon THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-thermal { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + thermal-sensors = <&nct72 1>; + + trips { + trip2: cpu-alert { + /* throttle at 75C until temperature drops to 74.8C */ + temperature = <75000>; + hysteresis = <200>; + type = "passive"; + }; + + trip3: cpu-crit { + /* shut down at 90C */ + temperature = <90000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map1 { + trip = <&trip2>; + 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>, + <&actmon THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts b/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts new file mode 100644 index 000000000000..5d9e23a43820 --- /dev/null +++ b/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts @@ -0,0 +1,2500 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include +#include +#include +#include + +#include "tegra30.dtsi" +#include "tegra30-cpu-opp.dtsi" +#include "tegra30-cpu-opp-microvolt.dtsi" + +/ { + model = "Asus VivoTab RT TF600T"; + compatible = "asus,tf600t", "nvidia,tegra30"; + chassis-type = "convertible"; + + aliases { + mmc0 = &sdmmc4; /* eMMC */ + mmc1 = &sdmmc1; /* uSD slot */ + mmc2 = &sdmmc3; /* WiFi */ + + rtc0 = &pmic; + rtc1 = "/rtc@7000e000"; + + display1 = &hdmi; + + serial1 = &uartc; /* Bluetooth */ + serial2 = &uartb; /* GPS */ + }; + + /* + * The decompressor and also some bootloaders rely on a + * pre-existing /chosen node to be available to insert the + * command line and merge other ATAGS info. + */ + chosen {}; + + memory@80000000 { + reg = <0x80000000 0x80000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + linux,cma@80000000 { + compatible = "shared-dma-pool"; + alloc-ranges = <0x80000000 0x30000000>; + size = <0x10000000>; /* 256MiB */ + linux,cma-default; + reusable; + }; + }; + + host1x@50000000 { + hdmi: hdmi@54280000 { + status = "okay"; + + hdmi-supply = <&hdmi_5v0_sys>; + 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>; + }; + }; + + vde@6001a000 { + assigned-clocks = <&tegra_car TEGRA30_CLK_VDE>; + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_P>; + assigned-clock-rates = <408000000>; + }; + + pinmux@70000868 { + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinmux { + /* SDMMC1 pinmux */ + sdmmc1-clk { + nvidia,pins = "sdmmc1_clk_pz0"; + nvidia,function = "sdmmc1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-cmd { + nvidia,pins = "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "sdmmc1_cmd_pz1"; + nvidia,function = "sdmmc1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-cd { + nvidia,pins = "gmi_iordy_pi5"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc1-wp { + nvidia,pins = "vi_d11_pt3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC2 pinmux */ + vi-d1-pd5 { + nvidia,pins = "vi_d1_pd5", + "vi_d2_pl0", + "vi_d3_pl1", + "vi_d5_pl3", + "vi_d7_pl5"; + nvidia,function = "sdmmc2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-d8-pl6 { + nvidia,pins = "vi_d8_pl6", + "vi_d9_pl7"; + nvidia,function = "sdmmc2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + + /* SDMMC3 pinmux */ + sdmmc3-clk { + nvidia,pins = "sdmmc3_clk_pa6"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc3-cmd { + nvidia,pins = "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "sdmmc3_dat4_pd1", + "sdmmc3_dat5_pd0", + "sdmmc3_dat6_pd3", + "sdmmc3_dat7_pd4"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC4 pinmux */ + sdmmc4-clk { + nvidia,pins = "sdmmc4_clk_pcc4"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc4-cmd { + nvidia,pins = "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc4-rst-n { + nvidia,pins = "sdmmc4_rst_n_pcc3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + cam-mclk { + nvidia,pins = "cam_mclk_pcc0"; + nvidia,function = "vi_alt3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* I2C pinmux */ + gen1-i2c { + nvidia,pins = "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5"; + nvidia,function = "i2c1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = ; + }; + gen2-i2c { + nvidia,pins = "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6"; + nvidia,function = "i2c2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = ; + }; + cam-i2c { + nvidia,pins = "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2"; + nvidia,function = "i2c3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = ; + }; + ddc-i2c { + nvidia,pins = "ddc_scl_pv4", + "ddc_sda_pv5"; + nvidia,function = "i2c4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = ; + }; + pwr-i2c { + nvidia,pins = "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7"; + nvidia,function = "i2cpwr"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = ; + }; + hotplug-i2c { + nvidia,pins = "pu4"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* HDMI pinmux */ + hdmi-cec { + nvidia,pins = "hdmi_cec_pee3"; + nvidia,function = "cec"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,lock = ; + }; + hdmi-hpd { + nvidia,pins = "hdmi_int_pn7"; + nvidia,function = "hdmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-A */ + ulpi-data0-po1 { + nvidia,pins = "ulpi_data0_po1"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data1-po2 { + nvidia,pins = "ulpi_data1_po2"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data5-po6 { + nvidia,pins = "ulpi_data5_po6"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-data7-po0 { + nvidia,pins = "ulpi_data7_po0", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data6_po7"; + nvidia,function = "uarta"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-B */ + uartb-txd-rts { + nvidia,pins = "uart2_txd_pc2", + "uart2_rts_n_pj6"; + nvidia,function = "uartb"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + uartb-rxd-cts { + nvidia,pins = "uart2_rxd_pc3", + "uart2_cts_n_pj5"; + nvidia,function = "uartb"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-C */ + uartc-rxd-cts { + nvidia,pins = "uart3_cts_n_pa1", + "uart3_rxd_pw7"; + nvidia,function = "uartc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + uartc-txd-rts { + nvidia,pins = "uart3_rts_n_pc0", + "uart3_txd_pw6"; + nvidia,function = "uartc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* UART-D */ + ulpi-nxt-py2 { + nvidia,pins = "ulpi_nxt_py2"; + nvidia,function = "uartd"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ulpi-clk-py0 { + nvidia,pins = "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_stp_py3"; + nvidia,function = "uartd"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* I2S pinmux */ + dap-i2s0 { + nvidia,pins = "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3"; + nvidia,function = "i2s0"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap-i2s1 { + nvidia,pins = "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5"; + nvidia,function = "i2s1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap3-fs { + nvidia,pins = "dap3_fs_pp0"; + nvidia,function = "i2s2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap3-din { + nvidia,pins = "dap3_din_pp1"; + nvidia,function = "i2s2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap3-dout { + nvidia,pins = "dap3_dout_pp2", + "dap3_sclk_pp3"; + nvidia,function = "i2s2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + dap-i2s3 { + nvidia,pins = "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7"; + nvidia,function = "i2s3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + i2s4 { + nvidia,pins = "pbb7"; + nvidia,function = "i2s4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Sensors pinmux */ + nct-irq { + nvidia,pins = "pcc2"; + nvidia,function = "i2s4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + hall { + nvidia,pins = "pbb6"; + nvidia,function = "vgp6"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Asus EC pinmux */ + ec-irqs { + nvidia,pins = "kb_row10_ps2", + "kb_row15_ps7"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + ec-reqs { + nvidia,pins = "kb_col1_pq1"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Memory type bootstrap */ + mem-boostraps { + nvidia,pins = "gmi_ad4_pg4", + "gmi_ad5_pg5"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* PCI-e pinmux */ + pex-l2-rst-n { + nvidia,pins = "pex_l2_rst_n_pcc6", + "pex_l0_rst_n_pdd1", + "pex_l1_rst_n_pdd5"; + nvidia,function = "pcie"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pex-l2-clkreq-n { + nvidia,pins = "pex_l2_clkreq_n_pcc7", + "pex_l0_prsnt_n_pdd0", + "pex_l0_clkreq_n_pdd2", + "pex_wake_n_pdd3", + "pex_l1_prsnt_n_pdd4", + "pex_l1_clkreq_n_pdd6", + "pex_l2_prsnt_n_pdd7"; + nvidia,function = "pcie"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Display A pinmux */ + lcd-pwr0-pb2 { + nvidia,pins = "lcd_pwr0_pb2", + "lcd_pclk_pb3", + "lcd_pwr1_pc1", + "lcd_d0_pe0", + "lcd_d1_pe1", + "lcd_d2_pe2", + "lcd_d3_pe3", + "lcd_d4_pe4", + "lcd_d5_pe5", + "lcd_d6_pe6", + "lcd_d7_pe7", + "lcd_d8_pf0", + "lcd_d9_pf1", + "lcd_d10_pf2", + "lcd_d11_pf3", + "lcd_d12_pf4", + "lcd_d13_pf5", + "lcd_d14_pf6", + "lcd_d15_pf7", + "lcd_de_pj1", + "lcd_hsync_pj3", + "lcd_vsync_pj4", + "lcd_d16_pm0", + "lcd_d17_pm1", + "lcd_d18_pm2", + "lcd_d19_pm3", + "lcd_d20_pm4", + "lcd_d21_pm5", + "lcd_d22_pm6", + "lcd_d23_pm7", + "lcd_dc0_pn6", + "lcd_sdin_pz2"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-cs0-n-pn4 { + nvidia,pins = "lcd_sdout_pn5", + "lcd_wr_n_pz3", + "lcd_pwr2_pc6", + "lcd_dc1_pd2"; + nvidia,function = "displaya"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + blink { + nvidia,pins = "clk_32k_out_pa0"; + nvidia,function = "blink"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* KBC keys */ + kb-col0 { + nvidia,pins = "kb_col0_pq0", + "kb_row1_pr1", + "kb_row3_pr3", + "kb_row7_pr7", + "kb_row8_ps0"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + kb-col5 { + nvidia,pins = "kb_col5_pq5", + "kb_col7_pq7", + "kb_row2_pr2", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row13_ps5"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + gmi-cs0-n-pj0 { + nvidia,pins = "gmi_wp_n_pc7", + "gmi_wait_pi7", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + vi-pclk-pt0 { + nvidia,pins = "vi_pclk_pt0"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + + /* GPIO keys pinmux */ + power-key { + nvidia,pins = "pv0"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vol-keys { + nvidia,pins = "kb_col3_pq3", + "kb_col4_pq4"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Bluetooth */ + bt-shutdown { + nvidia,pins = "pu0"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + bt-dev-wake { + nvidia,pins = "pu1"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + bt-host-wake { + nvidia,pins = "pu6"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + pu2 { + nvidia,pins = "pu2"; + nvidia,function = "rsvd1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pu3 { + nvidia,pins = "pu3"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pcc1 { + nvidia,pins = "pcc1"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pv2 { + nvidia,pins = "pv2"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pv3 { + nvidia,pins = "pv3"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + vi-vsync-pd6 { + nvidia,pins = "vi_vsync_pd6", + "vi_hsync_pd7"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + vi-d10-pt2 { + nvidia,pins = "vi_d10_pt2", + "vi_d0_pt4", + "pbb0"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + kb-row0-pr0 { + nvidia,pins = "kb_row0_pr0"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad0-pg0 { + nvidia,pins = "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_dqs_pi2", + "gmi_adv_n_pk0", + "gmi_clk_pk1"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad13-ph5 { + nvidia,pins = "gmi_ad13_ph5"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad10-ph2 { + nvidia,pins = "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad14_ph6"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + gmi-ad12-ph4 { + nvidia,pins = "gmi_ad12_ph4", + "gmi_rst_n_pi4", + "gmi_cs7_n_pi6"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Vibrator control */ + vibrator { + nvidia,pins = "gmi_ad11_ph3"; + nvidia,function = "nand"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* PWM pinmux */ + pwm-0 { + nvidia,pins = "gmi_ad8_ph0"; + nvidia,function = "pwm0"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pwm-2 { + nvidia,pins = "pu5"; + nvidia,function = "pwm2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + gmi-cs-n { + nvidia,pins = "gmi_cs4_n_pk2", + "gmi_cs6_n_pi3"; + nvidia,function = "gmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Spdif pinmux */ + spdif-out { + nvidia,pins = "spdif_out_pk5"; + nvidia,function = "spdif"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + spdif-in { + nvidia,pins = "spdif_in_pk6"; + nvidia,function = "spdif"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + vi-d4-pl2 { + nvidia,pins = "vi_d4_pl2"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + vi-d6-pl4 { + nvidia,pins = "vi_d6_pl4"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <0>; + nvidia,io-reset = <0>; + }; + vi-mclk-pt1 { + nvidia,pins = "vi_mclk_pt1"; + nvidia,function = "vi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + jtag { + nvidia,pins = "jtag_rtck_pu7"; + nvidia,function = "rtck"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + crt-sync { + nvidia,pins = "crt_hsync_pv6", + "crt_vsync_pv7"; + nvidia,function = "crt"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + clk1-out { + nvidia,pins = "clk1_out_pw4"; + nvidia,function = "extperiph1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk2-out { + nvidia,pins = "clk2_out_pw5"; + nvidia,function = "extperiph2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk3-out { + nvidia,pins = "clk3_out_pee0"; + nvidia,function = "extperiph3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sys-clk-req { + nvidia,pins = "sys_clk_req_pz5"; + nvidia,function = "sysclk"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + pbb3 { + nvidia,pins = "pbb3"; + nvidia,function = "vgp3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb4 { + nvidia,pins = "pbb4"; + nvidia,function = "vgp4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pbb5 { + nvidia,pins = "pbb5"; + nvidia,function = "vgp5"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + clk2-req-pcc5 { + nvidia,pins = "clk2_req_pcc5", + "clk1_req_pee2"; + nvidia,function = "dap"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + clk3-req-pee1 { + nvidia,pins = "clk3_req_pee1"; + nvidia,function = "dev3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + owr { + nvidia,pins = "owr"; + nvidia,function = "owr"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* GPIO power/drive control */ + drive-dap1 { + nvidia,pins = "drive_dap1", + "drive_dap2", + "drive_dbg", + "drive_at5", + "drive_gme", + "drive_ddc", + "drive_ao1", + "drive_uart3"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,low-power-mode = ; + nvidia,pull-down-strength = <31>; + nvidia,pull-up-strength = <31>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + drive-sdio1 { + nvidia,pins = "drive_sdio1", + "drive_sdio3"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,pull-down-strength = <46>; + nvidia,pull-up-strength = <42>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + drive-sdmmc4 { + nvidia,pins = "drive_gma", + "drive_gmb", + "drive_gmc", + "drive_gmd"; + nvidia,pull-down-strength = <9>; + nvidia,pull-up-strength = <9>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + }; + }; + + uartb: serial@70006040 { + compatible = "nvidia,tegra30-hsuart"; + reset-names = "serial"; + /delete-property/ reg-shift; + status = "okay"; + + /* Broadcom GPS BCM47511 */ + }; + + uartc: serial@70006200 { + compatible = "nvidia,tegra30-hsuart"; + reset-names = "serial"; + /delete-property/ reg-shift; + status = "okay"; + + nvidia,adjust-baud-rates = <0 9600 100>, + <9600 115200 200>, + <1000000 4000000 136>; + + /* Azurewave AW-NH665 BCM4330B1 */ + bluetooth { + compatible = "brcm,bcm4330-bt"; + max-speed = <4000000>; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_BLINK>; + clock-names = "txco"; + + interrupt-parent = <&gpio>; + interrupts = ; + interrupt-names = "host-wakeup"; + + device-wakeup-gpios = <&gpio TEGRA_GPIO(U, 1) GPIO_ACTIVE_HIGH>; + shutdown-gpios = <&gpio TEGRA_GPIO(U, 0) GPIO_ACTIVE_HIGH>; + + vbat-supply = <&vdd_3v3_com>; + vddio-supply = <&vdd_1v8_vio>; + }; + }; + + pwm@7000a000 { + status = "okay"; + }; + + gen1_i2c: i2c@7000c000 { + status = "okay"; + clock-frequency = <100000>; + + /* Nuvoton NPCE698LA0BX embedded controller */ + }; + + i2c@7000c400 { + status = "okay"; + clock-frequency = <400000>; + + /* Atmel Maxtouch MXT1664 HID over I2C */ + touchscreen@4b { + compatible = "hid-over-i2c"; + reg = <0x4b>; + + hid-descr-addr = <0x0000>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vdd-supply = <&vdd_3v3_sys>; + vddl-supply = <&vdd_1v8_vio>; + }; + }; + + i2c@7000c500 { + status = "okay"; + clock-frequency = <100000>; + + /* TI TPS61050/61052 Boost Converter */ + flash-led@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + + led { + color = ; + }; + }; + + imu@69 { + compatible = "invensense,mpu6050"; + reg = <0x69>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vdd-supply = <&vdd_3v3_sys>; + vddio-supply = <&vdd_1v8_vio>; + + mount-matrix = "0", "-1", "0", + "-1", "0", "0", + "0", "0", "-1"; + + /* External I2C interface */ + i2c-gate { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@d { + compatible = "asahi-kasei,ak8975"; + reg = <0x0d>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vdd-supply = <&vdd_3v3_sys>; + vid-supply = <&vdd_1v8_vio>; + + mount-matrix = "0", "-1", "0", + "-1", "0", "0", + "0", "0", "-1"; + }; + }; + }; + }; + + hdmi_ddc: i2c@7000c700 { + status = "okay"; + clock-frequency = <93750>; + }; + + i2c@7000d000 { + status = "okay"; + clock-frequency = <400000>; + + rt5640: audio-codec@1c { + compatible = "realtek,rt5640"; + reg = <0x1c>; + + interrupt-parent = <&gpio>; + interrupts = ; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + clock-names = "mclk"; + }; + + /* Texas Instruments TPS659110 PMIC */ + pmic: pmic@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + interrupts = ; + #interrupt-cells = <2>; + interrupt-controller; + + ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + ti,system-power-controller; + ti,sleep-keep-ck32k; + ti,sleep-enable; + + #gpio-cells = <2>; + gpio-controller; + + vcc1-supply = <&vdd_5v0_bat>; + vcc2-supply = <&vdd_5v0_bat>; + vcc3-supply = <&vdd_1v8_vio>; + vcc4-supply = <&vdd_5v0_sys>; + vcc5-supply = <&vdd_5v0_bat>; + vcc6-supply = <&vdd_3v3_sys>; + vcc7-supply = <&vdd_5v0_bat>; + vccio-supply = <&vdd_5v0_bat>; + + pmic-sleep-hog { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + }; + + regulators { + vdd_lcd: vdd1 { + regulator-name = "vddio_ddr_1v2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <8>; + }; + + vddio_ddr: vdd2 { + regulator-name = "vddio_ddr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_cpu: vddctrl { + regulator-name = "vdd_cpu,vdd_sys"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-coupled-with = <&vdd_core>; + regulator-coupled-max-spread = <300000>; + regulator-max-step-microvolt = <100000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <1>; + + nvidia,tegra-cpu-regulator; + }; + + vdd_1v8_vio: vio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + /* eMMC VDD */ + vcore_emmc: ldo1 { + regulator-name = "vdd_emmc_core"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + /* ldo2 and ldo3 are not used by TF600T */ + + ldo4 { + regulator-name = "vdd_rtc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + /* uSD slot VDDIO */ + vddio_usd: ldo5 { + regulator-name = "vddio_sdmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + avdd_dsi_csi: ldo6 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo7 { + regulator-name = "vdd_pllm,x,u,a_p_c_s"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + ti,regulator-ext-sleep-control = <8>; + }; + + ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + ti,regulator-ext-sleep-control = <8>; + }; + }; + }; + + /* Capella CM3218 ambient light sensor */ + light-sensor@48 { + compatible = "capella,cm32181"; + reg = <0x48>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vdd-supply = <&vdd_3v3_als>; + }; + + nct72: temperature-sensor@4c { + compatible = "onnn,nct1008"; + reg = <0x4c>; + + interrupt-parent = <&gpio>; + interrupts = ; + + vcc-supply = <&vdd_3v3_sys>; + #thermal-sensor-cells = <1>; + }; + + vdd_core: core-regulator@60 { + compatible = "ti,tps62361"; + reg = <0x60>; + + regulator-name = "tps62361-vout"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1770000>; + regulator-coupled-with = <&vdd_cpu>; + regulator-coupled-max-spread = <300000>; + regulator-max-step-microvolt = <100000>; + regulator-boot-on; + regulator-always-on; + ti,enable-vout-discharge; + ti,vsel0-state-high; + ti,vsel1-state-high; + + nvidia,tegra-core-regulator; + }; + }; + + pmc@7000e400 { + status = "okay"; + nvidia,invert-interrupt; + nvidia,suspend-mode = <2>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <200>; + nvidia,core-pwr-good-time = <3845 3845>; + nvidia,core-pwr-off-time = <0>; + nvidia,core-power-req-active-high; + nvidia,sys-clock-req-active-high; + core-supply = <&vdd_core>; + + i2c-thermtrip { + nvidia,i2c-controller-id = <4>; + nvidia,bus-addr = <0x2d>; + nvidia,reg-addr = <0x3f>; + nvidia,reg-data = <0x81>; + }; + }; + + spi@7000da00 { + status = "okay"; + spi-max-frequency = <25000000>; + + flash@1 { + compatible = "winbond,w25q32", "jedec,spi-nor"; + reg = <1>; + + spi-max-frequency = <20000000>; + vcc-supply = <&vdd_3v3_sys>; + }; + }; + + memory-controller@7000f000 { + emc-timings-0 { + /* Elpida 2GB 750 MHZ */ + nvidia,ram-code = <0>; + + timing-25500000 { + clock-frequency = <25500000>; + + nvidia,emem-configuration = < 0x00020001 0xc0000010 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x75e30303 0x001f0000 >; + }; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emem-configuration = < 0x00010001 0xc0000010 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x74e30303 0x001f0000 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emem-configuration = < 0x00000001 0xc0000018 + 0x00000001 0x00000001 0x00000003 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0503 0x74430504 0x001f0000 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emem-configuration = < 0x00000003 0xc0000025 + 0x00000001 0x00000001 0x00000005 0x00000002 + 0x00000003 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0505 0x74040a06 0x001f0000 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emem-configuration = < 0x00000005 0xc0000044 + 0x00000001 0x00000002 0x00000009 0x00000005 + 0x00000005 0x00000001 0x00000002 0x00000008 + 0x00000002 0x00000002 0x00000003 0x00000006 + 0x06030202 0x000d0709 0x7086110a 0x001f0000 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emem-configuration = < 0x0000000b 0xc0000087 + 0x00000004 0x00000005 0x00000012 0x0000000c + 0x0000000b 0x00000002 0x00000003 0x0000000c + 0x00000002 0x00000002 0x00000004 0x00000008 + 0x08040202 0x00160d12 0x710c2213 0x001f0000 >; + }; + }; + + emc-timings-1 { + /* Hynix 2GB 750 MHZ */ + nvidia,ram-code = <1>; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emem-configuration = < 0x00010003 0xc0000010 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x74630303 0x001f0000 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emem-configuration = < 0x00000003 0xc0000018 + 0x00000001 0x00000001 0x00000003 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0503 0x73c30504 0x001f0000 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emem-configuration = < 0x00000006 0xc0000025 + 0x00000001 0x00000001 0x00000005 0x00000002 + 0x00000003 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0505 0x73840a06 0x001f0000 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emem-configuration = < 0x0000000b 0xc0000044 + 0x00000001 0x00000002 0x00000009 0x00000005 + 0x00000005 0x00000001 0x00000002 0x00000008 + 0x00000002 0x00000002 0x00000003 0x00000006 + 0x06030202 0x000c0609 0x7086110a 0x001f0000 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emem-configuration = < 0x00000016 0xc0000087 + 0x00000003 0x00000004 0x00000012 0x0000000c + 0x0000000b 0x00000002 0x00000003 0x0000000c + 0x00000002 0x00000002 0x00000004 0x00000008 + 0x08040202 0x00150c12 0x710c2213 0x001f0000 >; + }; + }; + + emc-timings-2 { + /* Micron 2GB 750 MHZ */ + nvidia,ram-code = <2>; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emem-configuration = < 0x00010003 0xc0000010 + 0x00000001 0x00000001 0x00000002 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0502 0x73430303 0x001f0000 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emem-configuration = < 0x00000003 0xc0000018 + 0x00000001 0x00000001 0x00000003 0x00000000 + 0x00000001 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0503 0x74430504 0x001f0000 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emem-configuration = < 0x00000006 0xc0000025 + 0x00000001 0x00000001 0x00000005 0x00000002 + 0x00000003 0x00000001 0x00000003 0x00000008 + 0x00000002 0x00000001 0x00000002 0x00000006 + 0x06020102 0x000a0505 0x74040a06 0x001f0000 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emem-configuration = < 0x0000000b 0xc0000044 + 0x00000001 0x00000002 0x00000009 0x00000005 + 0x00000005 0x00000001 0x00000002 0x00000008 + 0x00000002 0x00000002 0x00000003 0x00000006 + 0x06030202 0x000d0709 0x7086110a 0x001f0000 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emem-configuration = < 0x00000016 0xc0000087 + 0x00000004 0x00000005 0x00000012 0x0000000c + 0x0000000b 0x00000003 0x00000003 0x0000000c + 0x00000002 0x00000002 0x00000004 0x00000008 + 0x08040202 0x00160d12 0x710c2213 0x001f0000 >; + }; + }; + }; + + memory-controller@7000f400 { + emc-timings-0 { + /* Elpida 2GB 750 MHZ */ + nvidia,ram-code = <0>; + + timing-25500000 { + clock-frequency = <25500000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000001 + 0x00000007 0x00000000 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x000000c0 0x00000000 0x00000030 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000008 0x00000008 + 0x00000004 0x00000001 0x00000000 0x00000004 + 0x00000005 0x000000c7 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000287 0xe8000000 0xff00ff00 >; + }; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000002 + 0x0000000f 0x00000001 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000181 0x00000000 0x00000060 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000010 0x00000010 + 0x00000004 0x00000002 0x00000000 0x00000004 + 0x00000005 0x0000018e 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x8000040b 0xe8000000 0xff00ff00 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000004 + 0x0000001e 0x00000003 0x00000001 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000001 + 0x00000001 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000303 0x00000000 0x000000c0 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000020 0x00000020 + 0x00000004 0x00000004 0x00000000 0x00000004 + 0x00000005 0x0000031c 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000713 0xe8000000 0xff00ff00 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000009 + 0x0000003d 0x00000007 0x00000002 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000002 + 0x00000002 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000006 0x00000004 0x0000000a + 0x0000000b 0x00000607 0x00000000 0x00000181 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000040 0x00000040 + 0x00000004 0x00000007 0x00000000 0x00000004 + 0x00000005 0x00000638 0x00000007 0x00000004 + 0x00000000 0x00000000 0x00004288 0x004400a4 + 0x00008000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00020000 + 0x00000100 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000d22 0xe8000000 0xff00ff00 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200040>; + nvidia,emc-mode-reset = <0x80000521>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + + nvidia,emc-configuration = < 0x00000011 + 0x0000006f 0x0000000c 0x00000004 0x00000003 + 0x00000008 0x00000002 0x0000000a 0x00000004 + 0x00000004 0x00000002 0x00000001 0x00000000 + 0x00000004 0x00000006 0x00000004 0x0000000a + 0x0000000c 0x00000b2d 0x00000000 0x000002cb + 0x00000001 0x00000008 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000075 0x00000200 + 0x00000004 0x0000000c 0x00000000 0x00000004 + 0x00000005 0x00000b6d 0x00000000 0x00000004 + 0x00000000 0x00000000 0x00007088 0x00200084 + 0x00008000 0x00034000 0x00034000 0x00034000 + 0x00034000 0x00014000 0x00014000 0x00014000 + 0x00014000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00048000 0x00048000 0x00048000 + 0x00048000 0x000002a0 0x0600013d 0x00000000 + 0x77fff884 0x01f1f508 0x05057404 0x54000007 + 0x080001e8 0x06000021 0x00000802 0x00020000 + 0x00000100 0x0150000c 0xa0f10000 0x00000000 + 0x00000000 0x8000174b 0xe8000000 0xff00ff89 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200058>; + nvidia,emc-mode-reset = <0x80000d71>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000023 + 0x000000df 0x00000019 0x00000009 0x00000005 + 0x0000000d 0x00000004 0x00000013 0x00000009 + 0x00000009 0x00000003 0x00000001 0x00000000 + 0x00000007 0x0000000b 0x00000009 0x0000000b + 0x00000011 0x0000169a 0x00000000 0x000005a6 + 0x00000003 0x00000010 0x00000001 0x00000000 + 0x0000000e 0x00000018 0x000000e9 0x00000200 + 0x00000005 0x00000017 0x00000000 0x00000007 + 0x00000008 0x000016da 0x0000000c 0x00000004 + 0x00000000 0x00000000 0x00005088 0xf0080191 + 0x00008000 0x0000000a 0x0000000a 0x0000000a + 0x0000000a 0x00000008 0x00000008 0x00000008 + 0x00000008 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x0000000a 0x0000000a 0x0000000a + 0x0000000a 0x000002a0 0x0600013d 0x22220000 + 0x77fff884 0x01f1f501 0x07077404 0x54000000 + 0x080001e8 0x06000021 0x00000802 0x00020000 + 0x00000100 0x00df000c 0xa0f10000 0x00000000 + 0x00000000 0x80002d93 0xf8000000 0xff00ff49 >; + }; + }; + + emc-timings-1 { + /* Hynix 2GB 750 MHZ */ + nvidia,ram-code = <1>; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000002 + 0x0000000d 0x00000001 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000181 0x00000000 0x00000060 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x0000000e 0x0000000e + 0x00000004 0x00000002 0x00000000 0x00000004 + 0x00000005 0x0000018e 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x8000040b 0xe8000000 0xff00ff00 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000004 + 0x0000001a 0x00000003 0x00000001 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000001 + 0x00000001 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000303 0x00000000 0x000000c0 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x0000001c 0x0000001c + 0x00000004 0x00000004 0x00000000 0x00000004 + 0x00000005 0x0000031c 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000713 0xe8000000 0xff00ff00 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000009 + 0x00000035 0x00000007 0x00000002 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000002 + 0x00000002 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000006 0x00000004 0x0000000a + 0x0000000b 0x00000607 0x00000000 0x00000181 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000038 0x00000038 + 0x00000004 0x00000007 0x00000000 0x00000004 + 0x00000005 0x00000638 0x00000007 0x00000004 + 0x00000000 0x00000000 0x00004288 0x004400a4 + 0x00008000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00020000 + 0x00000100 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000d22 0xe8000000 0xff00ff00 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200040>; + nvidia,emc-mode-reset = <0x80000521>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + + nvidia,emc-configuration = < 0x00000011 + 0x00000060 0x0000000c 0x00000003 0x00000004 + 0x00000008 0x00000002 0x0000000a 0x00000003 + 0x00000003 0x00000002 0x00000001 0x00000000 + 0x00000004 0x00000006 0x00000004 0x0000000a + 0x0000000c 0x00000b2d 0x00000000 0x000002cb + 0x00000001 0x00000008 0x00000001 0x00000000 + 0x00000007 0x00000010 0x00000066 0x00000200 + 0x00000004 0x0000000c 0x00000000 0x00000004 + 0x00000005 0x00000b6d 0x00000000 0x00000004 + 0x00000000 0x00000000 0x00007288 0x00200084 + 0x00008000 0x00044000 0x00044000 0x00044000 + 0x00044000 0x00014000 0x00014000 0x00014000 + 0x00014000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00048000 0x00048000 0x00048000 + 0x00048000 0x000002a0 0x0600013d 0x00000000 + 0x77fff884 0x01f1f508 0x05057404 0x54000007 + 0x08000168 0x06000021 0x00000802 0x00020000 + 0x00000100 0x015f000c 0xa0f10000 0x00000000 + 0x00000000 0x8000174b 0xe8000000 0xff00ff89 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200058>; + nvidia,emc-mode-reset = <0x80000d71>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000023 + 0x000000c1 0x00000019 0x00000008 0x00000005 + 0x0000000d 0x00000004 0x00000013 0x00000008 + 0x00000008 0x00000003 0x00000001 0x00000000 + 0x00000007 0x0000000b 0x00000009 0x0000000b + 0x00000011 0x0000169a 0x00000000 0x000005a6 + 0x00000003 0x00000010 0x00000001 0x00000000 + 0x0000000e 0x00000018 0x000000cb 0x00000200 + 0x00000005 0x00000017 0x00000000 0x00000007 + 0x00000008 0x000016da 0x0000000c 0x00000004 + 0x00000000 0x00000000 0x00005088 0xf0080191 + 0x00008000 0x00008008 0x00000008 0x00000008 + 0x00000008 0x00000008 0x00000008 0x00000008 + 0x00000008 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x0000000a 0x0000000a 0x0000000a + 0x0000000a 0x000002a0 0x0800013d 0x22220000 + 0x77fff884 0x01f1f501 0x07077404 0x54000000 + 0x080001e8 0x08000021 0x00000802 0x00020000 + 0x00000100 0x00fd000c 0xa0f10000 0x00000000 + 0x00000000 0x80002d93 0xe8000000 0xff00ff49 >; + }; + }; + + emc-timings-2 { + /* Micron 2GB 750 MHZ */ + nvidia,ram-code = <2>; + + timing-51000000 { + clock-frequency = <51000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200008>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000002 + 0x00000008 0x00000001 0x00000000 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000000 + 0x00000000 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000181 0x00000000 0x00000060 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000009 0x00000009 + 0x00000004 0x00000002 0x00000000 0x00000004 + 0x00000005 0x0000018e 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x8000040b 0xe8000000 0xff00ff00 >; + }; + + timing-102000000 { + clock-frequency = <102000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000004 + 0x0000001e 0x00000003 0x00000001 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000001 + 0x00000001 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000005 0x00000004 0x0000000a + 0x0000000b 0x00000303 0x00000000 0x000000c0 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000020 0x00000020 + 0x00000004 0x00000004 0x00000000 0x00000004 + 0x00000005 0x0000031c 0x00000006 0x00000004 + 0x00000000 0x00000000 0x00004288 0x007800a4 + 0x00008000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x000fc000 0x000fc000 0x000fc000 + 0x000fc000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00000000 + 0x00000040 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000713 0xe8000000 0xff00ff00 >; + }; + + timing-204000000 { + clock-frequency = <204000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100003>; + nvidia,emc-mode-2 = <0x80200048>; + nvidia,emc-mode-reset = <0x80001221>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + nvidia,emc-cfg-dyn-self-ref; + + nvidia,emc-configuration = < 0x00000009 + 0x0000003d 0x00000007 0x00000002 0x00000002 + 0x0000000a 0x00000005 0x0000000b 0x00000002 + 0x00000002 0x00000003 0x00000001 0x00000000 + 0x00000005 0x00000006 0x00000004 0x0000000a + 0x0000000b 0x00000607 0x00000000 0x00000181 + 0x00000002 0x00000002 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000040 0x00000040 + 0x00000004 0x00000007 0x00000000 0x00000004 + 0x00000005 0x00000638 0x00000007 0x00000004 + 0x00000000 0x00000000 0x00004288 0x004400a4 + 0x00008000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00080000 0x00080000 0x00080000 + 0x00080000 0x000002a0 0x0800211c 0x00000000 + 0x77fff884 0x01f1f108 0x05057404 0x54000007 + 0x08000168 0x08000000 0x00000802 0x00020000 + 0x00000100 0x000c000c 0xa0f10000 0x00000000 + 0x00000000 0x80000d22 0xe8000000 0xff00ff00 >; + }; + + timing-375000000 { + clock-frequency = <375000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200040>; + nvidia,emc-mode-reset = <0x80000521>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + + nvidia,emc-configuration = < 0x00000011 + 0x0000006f 0x0000000c 0x00000004 0x00000003 + 0x00000008 0x00000002 0x0000000a 0x00000004 + 0x00000004 0x00000002 0x00000001 0x00000000 + 0x00000004 0x00000006 0x00000004 0x0000000a + 0x0000000c 0x00000b2d 0x00000000 0x000002cb + 0x00000001 0x00000008 0x00000001 0x00000000 + 0x00000007 0x0000000f 0x00000075 0x00000200 + 0x00000004 0x0000000c 0x00000000 0x00000004 + 0x00000005 0x00000b6d 0x00000000 0x00000004 + 0x00000000 0x00000000 0x00007088 0x00200084 + 0x00008000 0x00044000 0x00044000 0x00044000 + 0x00044000 0x00014000 0x00014000 0x00014000 + 0x00014000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00048000 0x00048000 0x00048000 + 0x00048000 0x000002a0 0x0800013d 0x00000000 + 0x77fff884 0x01f1f508 0x05057404 0x54000007 + 0x080001e8 0x08000021 0x00000802 0x00020000 + 0x00000100 0x0150000c 0xa0f10000 0x00000000 + 0x00000000 0x8000174b 0xe8000000 0xff00ff89 >; + }; + + timing-750000000 { + clock-frequency = <750000000>; + + nvidia,emc-auto-cal-interval = <0x001fffff>; + nvidia,emc-mode-1 = <0x80100002>; + nvidia,emc-mode-2 = <0x80200058>; + nvidia,emc-mode-reset = <0x80000d71>; + nvidia,emc-zcal-cnt-long = <0x00000040>; + nvidia,emc-cfg-periodic-qrst; + + nvidia,emc-configuration = < 0x00000023 + 0x000000df 0x00000019 0x00000009 0x00000005 + 0x0000000d 0x00000004 0x00000013 0x00000009 + 0x00000009 0x00000006 0x00000001 0x00000000 + 0x00000007 0x0000000b 0x00000009 0x0000000b + 0x00000011 0x0000169a 0x00000000 0x000005a6 + 0x00000003 0x00000010 0x00000001 0x00000000 + 0x0000000e 0x00000018 0x000000e9 0x00000200 + 0x00000005 0x00000017 0x00000000 0x00000007 + 0x00000008 0x000016da 0x0000000c 0x00000004 + 0x00000000 0x00000000 0x00005088 0xf0080191 + 0x00008000 0x0000800a 0x0000000a 0x0000000a + 0x0000000a 0x00000008 0x00000008 0x00000008 + 0x00000008 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x00000000 0x00000000 0x00000000 + 0x00000000 0x007fc00a 0x0000000a 0x0000000a + 0x0000000a 0x000002a0 0x0800013d 0x22220000 + 0x77fff884 0x01f1f501 0x07077404 0x54000000 + 0x080001e8 0x08000021 0x00000802 0x00020000 + 0x00000100 0x00df000c 0xa0f10000 0x00000000 + 0x00000000 0x80002d93 0xf8000000 0xff00ff49 >; + }; + }; + }; + + hda@70030000 { + status = "okay"; + }; + + ahub@70080000 { + i2s@70080400 { /* i2s1 */ + status = "okay"; + }; + + /* BT SCO */ + i2s@70080600 { /* i2s3 */ + status = "okay"; + }; + }; + + sdmmc1: mmc@78000000 { + status = "okay"; + bus-width = <4>; + + cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>; + power-gpios = <&gpio TEGRA_GPIO(D, 7) GPIO_ACTIVE_HIGH>; + + vmmc-supply = <&vdd_3v3_sys>; + vqmmc-supply = <&vddio_usd>; + }; + + sdmmc3: mmc@78000400 { + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + assigned-clocks = <&tegra_car TEGRA30_CLK_SDMMC3>; + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_C>; + assigned-clock-rates = <50000000>; + + max-frequency = <50000000>; + keep-power-in-suspend; + bus-width = <4>; + non-removable; + + mmc-pwrseq = <&brcm_wifi_pwrseq>; + vmmc-supply = <&vdd_3v3_com>; + vqmmc-supply = <&vdd_1v8_vio>; + + /* Azurewave AW-NH665 BCM4330B1 */ + wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + + interrupt-parent = <&gpio>; + interrupts = ; + interrupt-names = "host-wake"; + }; + }; + + sdmmc4: mmc@78000600 { + status = "okay"; + bus-width = <8>; + + non-removable; + mmc-ddr-1_8v; + + vmmc-supply = <&vcore_emmc>; + vqmmc-supply = <&vdd_1v8_vio>; + }; + + /* USB via ASUS connector */ + usb@7d000000 { + compatible = "nvidia,tegra30-udc"; + status = "okay"; + dr_mode = "peripheral"; + }; + + usb-phy@7d000000 { + status = "okay"; + dr_mode = "peripheral"; + nvidia,hssync-start-delay = <0>; + nvidia,xcvr-lsfslew = <2>; + nvidia,xcvr-lsrslew = <2>; + vbus-supply = <&vdd_5v0_sys>; + }; + + /* Dock's USB port */ + usb@7d008000 { + status = "okay"; + }; + + usb-phy@7d008000 { + status = "okay"; + vbus-supply = <&vdd_5v0_bat>; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + + enable-gpios = <&gpio TEGRA_GPIO(H, 2) GPIO_ACTIVE_HIGH>; + power-supply = <&vdd_5v0_bl>; + pwms = <&pwm 0 71428>; + + brightness-levels = <1 255>; + num-interpolated-steps = <254>; + default-brightness-level = <15>; + }; + + pad_battery: battery-pad { + compatible = "simple-battery"; + device-chemistry = "lithium-ion-polymer"; + charge-full-design-microamp-hours = <6760000>; + energy-full-design-microwatt-hours = <25000000>; + operating-range-celsius = <0 45>; + }; + + dock_battery: battery-dock { + compatible = "simple-battery"; + device-chemistry = "lithium-ion-polymer"; + charge-full-design-microamp-hours = <2980000>; + energy-full-design-microwatt-hours = <22000000>; + operating-range-celsius = <0 45>; + }; + + /* PMIC has a built-in 32KHz oscillator which is used by PMC */ + clk32k_in: clock-32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "pmic-oscillator"; + }; + + cpus { + cpu0: cpu@0 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu1: cpu@1 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu2: cpu@2 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + cpu3: cpu@3 { + cpu-supply = <&vdd_cpu>; + operating-points-v2 = <&cpu0_opp_table>; + #cooling-cells = <2>; + }; + }; + + extcon-keys { + compatible = "gpio-keys"; + + switch-dock-hall-sensor { + label = "Lid sensor"; + gpios = <&gpio TEGRA_GPIO(BB, 6) GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + debounce-interval = <500>; + wakeup-event-action = ; + wakeup-source; + }; + + switch-lineout-detect { + label = "Audio dock line-out detect"; + gpios = <&gpio TEGRA_GPIO(X, 3) GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + key-power { + label = "Power"; + gpios = <&gpio TEGRA_GPIO(V, 0) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + + key-volume-down { + label = "Volume Down"; + gpios = <&gpio TEGRA_GPIO(Q, 4) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + + key-volume-up { + label = "Volume Up"; + gpios = <&gpio TEGRA_GPIO(Q, 3) GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <10>; + wakeup-event-action = ; + wakeup-source; + }; + }; + + haptic-feedback { + compatible = "gpio-vibrator"; + enable-gpios = <&gpio TEGRA_GPIO(H, 3) GPIO_ACTIVE_HIGH>; + vcc-supply = <&vdd_3v3_sys>; + }; + + opp-table-actmon { + /delete-node/ opp-800000000; + /delete-node/ opp-900000000; + }; + + opp-table-emc { + /delete-node/ opp-800000000-1300; + /delete-node/ opp-900000000-1350; + }; + + brcm_wifi_pwrseq: pwrseq-wifi { + compatible = "mmc-pwrseq-simple"; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_BLINK>; + clock-names = "ext_clock"; + + reset-gpios = <&gpio TEGRA_GPIO(P, 1) GPIO_ACTIVE_LOW>; + post-power-on-delay-ms = <300>; + power-off-delay-us = <300>; + }; + + vdd_5v0_bat: regulator-bat { + compatible = "regulator-fixed"; + regulator-name = "vdd_ac_bat"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_5v0_cp: regulator-sby { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_sby"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_5v0_sys: regulator-5v { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_1v5_ddr: regulator-ddr { + compatible = "regulator-fixed"; + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_3v3_sys: regulator-3v { + compatible = "regulator-fixed"; + regulator-name = "vdd_3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + gpio = <&pmic 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + vdd_3v3_com: regulator-com { + compatible = "regulator-fixed"; + regulator-name = "vdd_3v3_com"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + gpio = <&gpio TEGRA_GPIO(D, 0) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_sys>; + }; + + vdd_3v3_als: regulator-als { + compatible = "regulator-fixed"; + regulator-name = "vdd_3v3_als"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + gpio = <&gpio TEGRA_GPIO(L, 5) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_sys>; + }; + + vdd_5v0_bl: regulator-bl { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_bl"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + gpio = <&gpio TEGRA_GPIO(H, 0) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_bat>; + }; + + hdmi_5v0_sys: regulator-hdmi { + compatible = "regulator-fixed"; + regulator-name = "hdmi_5v0_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(P, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_sys>; + }; + + sound { + compatible = "asus,tegra-audio-rt5640-tf600t", + "nvidia,tegra-audio-rt5640"; + nvidia,model = "Asus VivoTab RT TF600T RT5640"; + + nvidia,audio-routing = + "Headphones", "HPOR", + "Headphones", "HPOL", + "Speakers", "SPORP", + "Speakers", "SPORN", + "Speakers", "SPOLP", + "Speakers", "SPOLN", + "DMIC1", "Mic Jack"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5640>; + + nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(W, 2) GPIO_ACTIVE_LOW>; + nvidia,mic-det-gpios = <&gpio TEGRA_GPIO(X, 2) GPIO_ACTIVE_LOW>; + nvidia,coupled-mic-hp-det; + + clocks = <&tegra_car TEGRA30_CLK_PLL_A>, + <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + + assigned-clocks = <&tegra_car TEGRA30_CLK_EXTERN1>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_1>; + + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA30_CLK_EXTERN1>; + }; + + thermal-zones { + /* + * NCT72 has two sensors: + * + * 0: internal that monitors ambient/skin temperature + * 1: external that is connected to the CPU's diode + * + * Ideally we should use userspace thermal governor, + * but it's a much more complex solution. The "skin" + * zone exists as a simpler solution which prevents + * Transformers from getting too hot from a user's + * tactile perspective. The CPU zone is intended to + * protect silicon from damage. + */ + + skin-thermal { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + thermal-sensors = <&nct72 0>; + + trips { + trip0: skin-alert { + /* throttle at 57C until temperature drops to 56.8C */ + temperature = <57000>; + hysteresis = <200>; + type = "passive"; + }; + + trip1: skin-crit { + /* shut down at 65C */ + temperature = <65000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&trip0>; + 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>, + <&actmon THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-thermal { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + thermal-sensors = <&nct72 1>; + + trips { + trip2: cpu-alert { + /* throttle at 75C until temperature drops to 74.8C */ + temperature = <75000>; + hysteresis = <200>; + type = "passive"; + }; + + trip3: cpu-crit { + /* shut down at 90C */ + temperature = <90000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map1 { + trip = <&trip2>; + 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>, + <&actmon THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts b/arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts index 4012f9c799a8..b7d0ebb766a6 100644 --- a/arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts +++ b/arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts @@ -1155,6 +1155,14 @@ lcd_ddc: i2c@7000c000 { status = "okay"; clock-frequency = <400000>; + embedded-controller@10 { + compatible = "pegatron,chagall-ec"; + reg = <0x10>; + + monitored-battery = <&battery>; + power-supplies = <&mains>; + }; + /* Wolfson Microelectronics WM8903 audio codec */ wm8903: audio-codec@1a { compatible = "wlf,wm8903"; @@ -2596,6 +2604,14 @@ backlight: backlight { default-brightness-level = <15>; }; + battery: battery-cell { + compatible = "simple-battery"; + device-chemistry = "lithium-ion-polymer"; + charge-full-design-microamp-hours = <3050000>; + energy-full-design-microwatt-hours = <23000000>; + operating-range-celsius = <0 45>; + }; + /* PMIC has a built-in 32KHz oscillator which is used by PMC */ clk32k_in: clock-32k { compatible = "fixed-clock"; diff --git a/arch/arm/boot/dts/nxp/imx/Makefile b/arch/arm/boot/dts/nxp/imx/Makefile index 8b3abe817e12..de4142e8f3ce 100644 --- a/arch/arm/boot/dts/nxp/imx/Makefile +++ b/arch/arm/boot/dts/nxp/imx/Makefile @@ -356,6 +356,9 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ull-dhcom-pdk2.dtb \ imx6ull-dhcom-picoitx.dtb \ imx6ull-dhcor-maveo-box.dtb \ + imx6ull-engicam-microgea-bmm.dtb \ + imx6ull-engicam-microgea-gtw.dtb \ + imx6ull-engicam-microgea-rmm.dtb \ imx6ull-jozacp.dtb \ imx6ull-kontron-bl.dtb \ imx6ull-myir-mys-6ulx-eval.dtb \ diff --git a/arch/arm/boot/dts/nxp/imx/imx53-tx53-x03x.dts b/arch/arm/boot/dts/nxp/imx/imx53-tx53-x03x.dts index 5f62c99909c5..872cf7e16f20 100644 --- a/arch/arm/boot/dts/nxp/imx/imx53-tx53-x03x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx53-tx53-x03x.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2013-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx53-tx53-x13x.dts b/arch/arm/boot/dts/nxp/imx/imx53-tx53-x13x.dts index 9c9122da3737..96c37f4296e5 100644 --- a/arch/arm/boot/dts/nxp/imx/imx53-tx53-x13x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx53-tx53-x13x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2013-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx53-tx53.dtsi b/arch/arm/boot/dts/nxp/imx/imx53-tx53.dtsi index 29e3f5f37c25..88855d3b2031 100644 --- a/arch/arm/boot/dts/nxp/imx/imx53-tx53.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx53-tx53.dtsi @@ -1,45 +1,9 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2012-2017 * based on imx53-qsb.dts * Copyright 2011 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include "imx53.dtsi" diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-gw551x.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-gw551x.dts index 82d5f85722ea..50dd3df9dd04 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-gw551x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-gw551x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-gw553x.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-gw553x.dts index 59b8afc36e66..8ca5b6b8da07 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-gw553x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-gw553x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2016 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-gw560x.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-gw560x.dts index 21bdfaf8df53..b94455406a57 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-gw560x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-gw560x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-gw5903.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-gw5903.dts index 103261ea9334..dd978105b42f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-gw5903.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-gw5903.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-gw5904.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-gw5904.dts index 9c6d3cd3d6a7..172dad423639 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-gw5904.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-gw5904.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6dl-comtft.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6dl-comtft.dts index 7436626673fc..136ae7841878 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6dl-comtft.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6dl-comtft.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034-mb7.dts index fc23b4d291a1..e1b525ed292a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034.dts index 9eb2ef17339c..9a6a5cda9a3b 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8034.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035-mb7.dts index 4101c6597721..0e8f4c3f3760 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035.dts index a5532ecc18c5..9958e8701c98 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6s-8035.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-801x.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-801x.dts index 67ed0452f5de..d9bfd340efb2 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-801x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-801x.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033-mb7.dts index d34189fc52d9..8243f0d6d387 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033.dts index 7030b2654bbd..2d031403ab19 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-8033.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-80xx-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-80xx-mb7.dts index aef5fcc42904..684a2583db75 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-80xx-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-80xx-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-811x.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-811x.dts index 5342f2f5a8a8..7fdc794615f2 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-811x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-811x.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-81xx-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-81xx-mb7.dts index c4588fb0bf6f..209aaebe148a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-81xx-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-tx6u-81xx-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2016-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-gw551x.dts b/arch/arm/boot/dts/nxp/imx/imx6q-gw551x.dts index 2c7feeef1b0e..44d1871ac666 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-gw551x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-gw551x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-gw553x.dts b/arch/arm/boot/dts/nxp/imx/imx6q-gw553x.dts index e9c224cea752..22842f2ef685 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-gw553x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-gw553x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2016 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-gw560x.dts b/arch/arm/boot/dts/nxp/imx/imx6q-gw560x.dts index 735f2bbf1439..c69fdd064e2f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-gw560x.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-gw560x.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-gw5903.dts b/arch/arm/boot/dts/nxp/imx/imx6q-gw5903.dts index a182e4cb0e6e..a9a33eeb9712 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-gw5903.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-gw5903.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-gw5904.dts b/arch/arm/boot/dts/nxp/imx/imx6q-gw5904.dts index ca1e2ae3341e..25a93cd4e5f5 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-gw5904.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-gw5904.dts @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010-comtft.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010-comtft.dts index 393bfec58e2f..d630c572c704 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010-comtft.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010-comtft.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010.dts index 4ee860b626ff..01ac3493fa32 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1010.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020-comtft.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020-comtft.dts index 1ab175ffa238..1013025cb2d5 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020-comtft.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020-comtft.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020.dts index 0a4daec8d3ad..5dd8f1642db3 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1020.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036-mb7.dts index 9ffbb0fe7df8..ffa79c0eb05a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036.dts index cb2fcb4896c6..1346fd663d68 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1036.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-10x0-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-10x0-mb7.dts index d43a5d8f1749..eac07d5944cc 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-10x0-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-10x0-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1110.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1110.dts index f7b0acb65352..c485da35d333 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1110.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-1110.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-11x0-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-11x0-mb7.dts index 387edf2b3f96..53304fc3a90b 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-11x0-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-tx6q-11x0-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2016-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw551x.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw551x.dtsi index 29960d1cf6a0..009a9d56757c 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw551x.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw551x.dtsi @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw553x.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw553x.dtsi index c6e231de674a..e3b677384a22 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw553x.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw553x.dtsi @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2016 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw560x.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw560x.dtsi index d0f648938cae..ce1d49a9e0cd 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw560x.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw560x.dtsi @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5903.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5903.dtsi index 71911df881cc..50b484998c49 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5903.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5903.dtsi @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5904.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5904.dtsi index 716c324a7458..3125cd04d4ea 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5904.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-gw5904.dtsi @@ -1,48 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Gateworks Corporation - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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 file 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 file; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lcd.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lcd.dtsi index 77594546ef37..cdeaca36867e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lcd.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lcd.dtsi @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ / { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lvds.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lvds.dtsi index 4eb53d5677a6..63d09c01a3c6 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lvds.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-lvds.dtsi @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ / { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-mb7.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-mb7.dtsi index bae7313d729d..dd4e5bce4a55 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-mb7.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6-mb7.dtsi @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ / { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi index 2fa37d1b16cc..2bb5b762c984 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2014-2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037-mb7.dts index 92b38e6699aa..3183abdd25aa 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037.dts b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037.dts index ffc0f2ee11d2..174824a8138e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8037.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137-mb7.dts b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137-mb7.dts index 07ad70718aec..31854bc52e76 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137-mb7.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137-mb7.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137.dts b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137.dts index dd494d587014..dfe1535128fe 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6qp-tx6qp-8137.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2017 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-bl-common.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-bl-common.dtsi index 29d2f86d5e34..f4c45e964daf 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-bl-common.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-bl-common.dtsi @@ -168,7 +168,6 @@ &uart2 { pinctrl-0 = <&pinctrl_uart2>; linux,rs485-enabled-at-boot-time; rs485-rx-during-tx; - rs485-rts-active-low; uart-has-rtscts; status = "okay"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-sl-common.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-sl-common.dtsi index dcf88f610346..4c0ac4d4df68 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-sl-common.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-kontron-sl-common.dtsi @@ -26,8 +26,29 @@ &ecspi2 { flash@0 { compatible = "mxicy,mx25v8035f", "jedec,spi-nor"; - spi-max-frequency = <50000000>; reg = <0>; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + reg = <0x0 0xf0000>; + label = "u-boot"; + }; + + partition@f0000 { + reg = <0xf0000 0x8000>; + label = "env"; + }; + + partition@f8000 { + reg = <0xf8000 0x8000>; + label = "env_redundant"; + }; + }; }; }; @@ -61,7 +82,7 @@ &qspi { pinctrl-0 = <&pinctrl_qspi>; status = "okay"; - spi-flash@0 { + flash@0 { #address-cells = <1>; #size-cells = <1>; compatible = "spi-nand"; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0010.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0010.dts index 8c2f3df79b47..188f3a2a312f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0010.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0010.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0011.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0011.dts index d82698e7d50f..247a0aab7791 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0011.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-0011.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-mainboard.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-mainboard.dts index 20c810a81403..84b45542814e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-mainboard.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul-mainboard.dts @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi index 278120404d31..f053358bc931 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi @@ -1,42 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright 2015 Lothar Waßmann - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file 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. - * - * This file 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include 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 new file mode 100644 index 000000000000..279d46c22cd7 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-bmm.dts @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Amarula Solutions, Dario Binacchi + * Copyright (C) 2025 Engicam srl + */ + +/dts-v1/; + +#include "imx6ull-engicam-microgea.dtsi" + +/ { + compatible = "engicam,microgea-imx6ull-bmm", + "engicam,microgea-imx6ull", "fsl,imx6ull"; + model = "Engicam MicroGEA i.MX6ULL BMM Board"; + + backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 100>; + num-interpolated-steps = <100>; + default-brightness-level = <85>; + pwms = <&pwm8 0 100000 0>; + }; + + buzzer { + compatible = "pwm-beeper"; + pwms = <&pwm4 0 1000000 0>; + }; + + 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>; + }; + + reg_usb1_vbus: regulator-usb1-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usb1>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb2_vbus: regulator-usb2-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usb2>; + regulator-name = "usbotg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_ext_pwr: regulator-ext-pwr { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_ext_pwr>; + regulator-name = "ext-pwr"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "imx6ull-microgea-bmm-sgtl5000"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,widgets = + "Microphone", "Mic Jack", + "Headphone", "Headphone Jack"; + simple-audio-card,routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + + cpu_dai: simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + + codec_dai: simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + clock-frequency = <100000>; + status = "okay"; + + codec: audio-codec@a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mclk>; + #sound-dai-cells = <0>; + clocks = <&clks IMX6UL_CLK_CKO>; + assigned-clocks = <&clks IMX6UL_CLK_CKO2_SEL>, + <&clks IMX6UL_CLK_CKO2_PODF>, + <&clks IMX6UL_CLK_CKO2>, + <&clks IMX6UL_CLK_CKO>; + assigned-clock-parents = <&clks IMX6UL_CLK_OSC>, + <&clks IMX6UL_CLK_CKO2_SEL>, + <&clks IMX6UL_CLK_CKO2_PODF>, + <&clks IMX6UL_CLK_CKO2>; + VDDA-supply = <®_3v3>; + VDDIO-supply = <®_3v3>; + VDDD-supply = <®_1v8>; + }; +}; + +&pwm4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm4>; + status = "okay"; +}; + +&pwm8 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm8>; + status = "okay"; +}; + +&sai2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2>; + status = "okay"; +}; + +&tsc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tsc>; + measure-delay-time = <0x9ffff>; + pre-charge-time = <0xfff>; + xnur-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "host"; + vbus-supply = <®_usb1_vbus>; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + vbus-supply = <®_usb2_vbus>; + status = "okay"; +}; + +/* MicroSD */ +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + vmmc-supply = <®_3v3>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + +&iomuxc { + pinctrl_can: can-grp { + fsl,pins = < + MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020 + MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO00__I2C2_SCL 0x4001b8b0 + MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 + >; + }; + + pinctrl_mclk: mclkgrp { + fsl,pins = < + MX6UL_PAD_JTAG_TMS__CCM_CLKO1 0x13009 + >; + }; + + pinctrl_pwm4: pwm4grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO05__PWM4_OUT 0x110b0 + >; + }; + + pinctrl_pwm8: pwm8grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_ER__PWM8_OUT 0x11008 + >; + }; + + pinctrl_sai2: sai2grp { + fsl,pins = < + MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x130b0 + MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088 + MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 + MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x120b0 + >; + }; + + pinctrl_tsc: tscgrp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x000b0 + MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x000b0 + MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x000b0 + MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000b0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9 + >; + }; +}; + +&iomuxc_snvs { + pinctrl_reg_usb1: regusb1grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x17059 + >; + }; + + pinctrl_reg_usb2: regusb2grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x17059 + >; + }; + + pinctrl_reg_ext_pwr: reg-ext-pwrgrp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x17059 + >; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-gtw.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-gtw.dts new file mode 100644 index 000000000000..d500f8839102 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-gtw.dts @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Amarula Solutions, Dario Binacchi + * Copyright (C) 2025 Engicam srl + */ + +/dts-v1/; + +#include "imx6ull-engicam-microgea.dtsi" + +/ { + compatible = "engicam,microgea-imx6ull-gtw", + "engicam,microgea-imx6ull", "fsl,imx6ull"; + model = "Engicam MicroGEA i.MX6ULL GTW Board"; + + 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>; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_keys>; + + user-button { + label = "User button"; + gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; + linux,code = ; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_leds>, <&pinctrl_pwrled>; + + led-0 { + gpios = <&gpio5 7 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + led-1 { + gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + }; + + led-2 { + gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + }; + + led-3 { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + }; + }; + + usb_hub: usb-hub { + compatible = "smsc,usb3503a"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_hub>; + reset-gpios = <&gpio5 6 GPIO_ACTIVE_LOW>; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "otg"; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +/* MicroSD */ +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + vmmc-supply = <®_3v3>; + bus-width = <4>; + non-removable; + status = "okay"; +}; + +&iomuxc { + pinctrl_gpio_keys: gpio_keysgrp { + fsl,pins = < + MX6UL_PAD_JTAG_TDI__GPIO1_IO13 0x0b0b0 + >; + }; + + pinctrl_leds: ledsgrp { + fsl,pins = < + MX6UL_PAD_JTAG_TCK__GPIO1_IO14 0x130b0 + MX6UL_PAD_JTAG_TRST_B__GPIO1_IO15 0x130b0 + MX6UL_PAD_JTAG_TDO__GPIO1_IO12 0x130b0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 + MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 + MX6UL_PAD_UART2_RTS_B__UART2_DCE_RTS 0x1b0b1 + MX6UL_PAD_UART2_CTS_B__UART2_DCE_CTS 0x1b0b1 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + >; + }; +}; + +&iomuxc_snvs { + pinctrl_pwrled: ledsgrp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x130b0 + >; + }; + + pinctrl_usb_hub: usb_hubgrp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x17059 + >; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts new file mode 100644 index 000000000000..5d1cc8a1f555 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Amarula Solutions, Dario Binacchi + * Copyright (C) 2025 Engicam srl + */ + +/dts-v1/; + +#include "imx6ull-engicam-microgea.dtsi" + +/ { + compatible = "engicam,microgea-imx6ull-rmm", + "engicam,microgea-imx6ull", "fsl,imx6ull"; + model = "Engicam MicroGEA i.MX6ULL BMM Board"; + + backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 100>; + num-interpolated-steps = <100>; + default-brightness-level = <85>; + pwms = <&pwm8 0 100000 0>; + }; + + buzzer { + compatible = "pwm-beeper"; + pwms = <&pwm4 0 1000000 0>; + }; + + 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>; + }; + + reg_usb1_vbus: regulator-usb1-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usb1>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb2_vbus: regulator-usb2-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usb2>; + regulator-name = "usbotg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_ext_pwr: regulator-ext-pwr { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_ext_pwr>; + regulator-name = "ext-pwr"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio5 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "imx6ull-microgea-rmm-sgtl5000"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,widgets = + "Microphone", "Mic Jack", + "Headphone", "Headphone Jack"; + simple-audio-card,routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + + cpu_dai: simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + + codec_dai: simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_leds>; + + led-0 { + gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; + default-state = "off"; + status = "okay"; + }; + + led-1 { + gpios = <&gpio2 11 GPIO_ACTIVE_HIGH>; + default-state = "off"; + status = "okay"; + }; + }; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can>; + status = "okay"; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + clock-frequency = <100000>; + status = "okay"; + + touchscreen: touchscreen@38 { + compatible ="edt,edt-ft5306"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_touchscreen>; + interrupt-parent = <&gpio2>; + interrupts = <8 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>; + report-rate-hz = <6>; + /* settings valid only for Hycon touchscreen */ + touchscreen-size-x = <1280>; + touchscreen-size-y = <800>; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + clock-frequency = <100000>; + status = "okay"; + + codec: audio-codec@a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mclk>; + #sound-dai-cells = <0>; + clocks = <&clks IMX6UL_CLK_CKO>; + assigned-clocks = <&clks IMX6UL_CLK_CKO2_SEL>, + <&clks IMX6UL_CLK_CKO2_PODF>, + <&clks IMX6UL_CLK_CKO2>, + <&clks IMX6UL_CLK_CKO>; + assigned-clock-parents = <&clks IMX6UL_CLK_OSC>, + <&clks IMX6UL_CLK_CKO2_SEL>, + <&clks IMX6UL_CLK_CKO2_PODF>, + <&clks IMX6UL_CLK_CKO2>; + VDDA-supply = <®_3v3>; + VDDIO-supply = <®_3v3>; + VDDD-supply = <®_1v8>; + }; +}; + +&pwm4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm4>; + status = "okay"; +}; + +&pwm8 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm8>; + status = "okay"; +}; + +&sai2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "host"; + vbus-supply = <®_usb1_vbus>; + disable-over-current; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + vbus-supply = <®_usb2_vbus>; + disable-over-current; + status = "okay"; +}; + +/* MicroSD */ +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + vmmc-supply = <®_3v3>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + +&iomuxc { + pinctrl_can: can-grp { + fsl,pins = < + MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020 + MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6UL_PAD_CSI_PIXCLK__I2C1_SCL 0x4001b8b0 + MX6UL_PAD_CSI_MCLK__I2C1_SDA 0x4001b8b0 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO00__I2C2_SCL 0x4001b8b0 + MX6UL_PAD_GPIO1_IO01__I2C2_SDA 0x4001b8b0 + >; + }; + + pinctrl_leds: ledsgrp { + fsl,pins = < + MX6UL_PAD_ENET2_RX_EN__GPIO2_IO10 0x130b0 + MX6UL_PAD_ENET2_TX_DATA0__GPIO2_IO11 0x130b0 + >; + }; + + pinctrl_mclk: mclkgrp { + fsl,pins = < + MX6UL_PAD_JTAG_TMS__CCM_CLKO1 0x13009 + >; + }; + + pinctrl_pwm4: pwm4grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO05__PWM4_OUT 0x110b0 + >; + }; + + pinctrl_pwm8: pwm8grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_ER__PWM8_OUT 0x110b0 + >; + }; + + pinctrl_sai2: sai2grp { + fsl,pins = < + MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x130b0 + MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088 + MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 + MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x120b0 + >; + }; + + pinctrl_touchscreen: touchgrp { + fsl,pins = < + MX6UL_PAD_ENET2_TX_CLK__GPIO2_IO14 0x17059 + MX6UL_PAD_ENET2_RX_DATA0__GPIO2_IO08 0x17059 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6UL_PAD_UART4_TX_DATA__UART4_DCE_TX 0x0b0b0 + MX6UL_PAD_UART4_RX_DATA__UART4_DCE_RX 0x0b0b0 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9 + >; + }; +}; + +&iomuxc_snvs { + pinctrl_reg_usb1: regusb1grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x17059 + >; + }; + + pinctrl_reg_usb2: regusb2grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x17059 + >; + }; + + pinctrl_reg_ext_pwr: reg-ext-pwrgrp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x17059 + >; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi new file mode 100644 index 000000000000..43518bf07602 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Amarula Solutions, Dario Binacchi + * Copyright (C) 2025 Engicam srl + */ + +/dts-v1/; + + #include "imx6ull.dtsi" + +/ { + compatible = "engicam,microgea-imx6ull", "fsl,imx6ull"; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; + }; +}; + +&fec1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet1>, <&pinctrl_phy_reset>; + phy-mode = "rmii"; + phy-handle = <ðphy0>; + local-mac-address = [00 00 00 00 00 00]; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + reset-assert-us = <4000>; + reset-deassert-us = <4000>; + }; + }; +}; + +/* NAND */ +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <0>; + nand-ecc-step-size = <0>; + nand-on-flash-bbt; + status = "okay"; +}; + +&iomuxc { + pinctrl_enet1: enet1grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009 + MX6UL_PAD_GPIO1_IO07__ENET1_MDC 0x1b0b0 + MX6UL_PAD_GPIO1_IO06__ENET1_MDIO 0x1b0b0 + >; + }; + + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0xb0b1 + MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0xb0b1 + MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0xb0b1 + MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000 + MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0xb0b1 + MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0xb0b1 + MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0xb0b1 + MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0xb0b1 + MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0xb0b1 + MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0xb0b1 + MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0xb0b1 + MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0xb0b1 + MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0xb0b1 + MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0xb0b1 + MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0xb0b1 + >; + }; +}; + +&iomuxc_snvs { + pinctrl_phy_reset: phy-resetgrp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x1b0b0 + >; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts b/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts index af4acc311572..f2cd95e992e7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts +++ b/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts @@ -31,30 +31,6 @@ back { }; }; - reg_brcm: regulator-brcm { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio5 10 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_brcm_reg>; - regulator-name = "brcm_reg"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - startup-delay-us = <200000>; - }; - - reg_bt: regulator-bt { - compatible = "regulator-fixed"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_bt_reg>; - enable-active-high; - gpio = <&gpio5 17 GPIO_ACTIVE_HIGH>; - regulator-name = "bt_reg"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - reg_peri_3p15v: regulator-peri-3p15v { compatible = "regulator-fixed"; regulator-name = "peri_3p15v_reg"; @@ -63,6 +39,14 @@ reg_peri_3p15v: regulator-peri-3p15v { regulator-always-on; }; + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_brcm_reg>; + post-power-on-delay-ms = <200>; + reset-gpios = <&gpio5 10 GPIO_ACTIVE_LOW>; + }; + sound { compatible = "simple-audio-card"; simple-audio-card,name = "imx7-sgtl5000"; @@ -288,6 +272,14 @@ &uart3 { assigned-clock-parents = <&clks IMX7D_PLL_SYS_MAIN_240M_CLK>; uart-has-rtscts; status = "okay"; + + bluetooth { + compatible = "brcm,bcm4345c5"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_bt_reg>; + shutdown-gpios = <&gpio5 17 GPIO_ACTIVE_HIGH>; + max-speed = <3000000>; + }; }; &uart6 { @@ -305,14 +297,21 @@ &usbotg1 { }; &usdhc1 { + #address-cells = <1>; + #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc1>; bus-width = <4>; keep-power-in-suspend; no-1-8-v; non-removable; - vmmc-supply = <®_brcm>; + mmc-pwrseq = <&sdio_pwrseq>; status = "okay"; + + wifi@0 { + compatible = "brcm,bcm43455-fmac", "brcm,bcm4329-fmac"; + reg = <0>; + }; }; &usdhc3 { diff --git a/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi b/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi index 3c6ef7bfba60..880b9a4f32b0 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi @@ -399,6 +399,7 @@ gpio_ptc: gpio@40ae0000 { <&pcc3 IMX7ULP_CLK_PCTLC>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 0 20>; + ngpios = <20>; }; gpio_ptd: gpio@40af0000 { @@ -413,6 +414,7 @@ gpio_ptd: gpio@40af0000 { <&pcc3 IMX7ULP_CLK_PCTLD>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 32 12>; + ngpios = <12>; }; gpio_pte: gpio@40b00000 { @@ -427,6 +429,7 @@ gpio_pte: gpio@40b00000 { <&pcc3 IMX7ULP_CLK_PCTLE>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 64 16>; + ngpios = <16>; }; gpio_ptf: gpio@40b10000 { @@ -441,6 +444,7 @@ gpio_ptf: gpio@40b10000 { <&pcc3 IMX7ULP_CLK_PCTLF>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 96 20>; + ngpios = <20>; }; }; diff --git a/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi b/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi index 41f41a786f9d..6cf405e9b082 100644 --- a/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi +++ b/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi @@ -481,6 +481,7 @@ pwm1: pwm@4005c000 { compatible = "nxp,lpc3220-pwm"; reg = <0x4005c000 0x4>; clocks = <&clk LPC32XX_CLK_PWM1>; + #pwm-cells = <3>; assigned-clocks = <&clk LPC32XX_CLK_PWM1>; assigned-clock-parents = <&clk LPC32XX_CLK_PERIPH>; status = "disabled"; @@ -490,6 +491,7 @@ pwm2: pwm@4005c004 { compatible = "nxp,lpc3220-pwm"; reg = <0x4005c004 0x4>; clocks = <&clk LPC32XX_CLK_PWM2>; + #pwm-cells = <3>; assigned-clocks = <&clk LPC32XX_CLK_PWM2>; assigned-clock-parents = <&clk LPC32XX_CLK_PERIPH>; status = "disabled"; diff --git a/arch/arm/boot/dts/nxp/mxs/Makefile b/arch/arm/boot/dts/nxp/mxs/Makefile index 96dd31ea19ba..d72ba702b6fa 100644 --- a/arch/arm/boot/dts/nxp/mxs/Makefile +++ b/arch/arm/boot/dts/nxp/mxs/Makefile @@ -5,6 +5,7 @@ dtb-$(CONFIG_ARCH_MXS) += \ imx23-sansa.dtb \ imx23-stmp378x_devb.dtb \ imx23-xfi3.dtb \ + imx28-amarula-rmm.dtb \ imx28-apf28.dtb \ imx28-apf28dev.dtb \ imx28-apx4devkit.dtb \ diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-amarula-rmm.dts b/arch/arm/boot/dts/nxp/mxs/imx28-amarula-rmm.dts new file mode 100644 index 000000000000..af59211842fb --- /dev/null +++ b/arch/arm/boot/dts/nxp/mxs/imx28-amarula-rmm.dts @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Amarula Solutions, Dario Binacchi + */ + +/dts-v1/; + +#include "imx28.dtsi" +#include +#include + +/ { + compatible = "amarula,imx28-rmm", "fsl,imx28"; + model = "Amarula i.MX28 rmm"; + + memory@40000000 { + reg = <0x40000000 0x08000000>; + device_type = "memory"; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 4 5000000 0>; + brightness-levels = <0 255>; + num-interpolated-steps = <255>; + default-brightness-level = <255>; + power-supply = <®_5v>; + }; + + beeper { + compatible = "pwm-beeper"; + pwms = <&pwm 7 100000 0>; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&leds_pins>; + + led-0 { + gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-1 { + gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-2 { + gpios = <&gpio3 17 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + 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>; + regulator-always-on; + }; + + reg_5v: regulator-5v { + compatible = "regulator-fixed"; + regulator-name = "5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + reg_fec_3v3: regulator-fec-3v3 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&fec_3v3_enable_pin>; + regulator-name = "fec-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpios = <&gpio3 27 GPIO_ACTIVE_HIGH>; + enable-active-high; + startup-delay-us = <300000>; + vin-supply = <®_5v>; + }; + + reg_usb0_vbus: regulator-usb0-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&usb0_vbus_enable_pin>; + regulator-name = "usb0_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio2 5 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + reg_usb1_vbus: regulator-usb1-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&usb1_vbus_enable_pin>; + regulator-name = "usb1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio2 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; +}; + +&auart0 { + pinctrl-names = "default"; + pinctrl-0 = <&auart0_2pins_a>; + status = "okay"; +}; + +&auart1 { + pinctrl-names = "default"; + pinctrl-0 = <&auart1_pins_a>; + status = "okay"; +}; + +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_pins_a>; + status = "okay"; +}; + +&duart { + pinctrl-names = "default"; + pinctrl-0 = <&duart_pins_b>; + status = "okay"; +}; + +&duart_pins_b { + fsl,voltage = ; +}; + +&gpmi { + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gpmi_pins_a &gpmi_status_cfg>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; + + touchscreen: touchscreen@38 { + compatible = "edt,edt-ft5306"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins &edt_ft5x06_wake_pin>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + wake-gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>; + }; +}; + +&lradc { + status = "okay"; +}; + +&mac0 { + pinctrl-names = "default"; + pinctrl-0 = <&mac0_pins_a>; + phy-mode = "rmii"; + phy-supply = <®_fec_3v3>; + phy-handle = <ðphy>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + max-speed = <100>; + reset-gpios = <&gpio3 28 GPIO_ACTIVE_LOW>; + reset-assert-us = <4000>; + reset-deassert-us = <4000>; + }; + }; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&hog_pins_a>; + + edt_ft5x06_pins: edt-ft5x06@0 { + reg = <0>; + fsl,pinmux-ids = < + MX28_PAD_GPMI_RDY1__GPIO_0_21 /* Reset */ + MX28_PAD_GPMI_CE3N__GPIO_0_19 /* Interrupt */ + >; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + edt_ft5x06_wake_pin: edt-ft5x06-wake@0 { + reg = <0>; + fsl,pinmux-ids = ; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + fec_3v3_enable_pin: fec-3v3-enable@0 { + reg = <0>; + fsl,pinmux-ids = ; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + hog_pins_a: hog@0 { + reg = <0>; + fsl,pinmux-ids = < + MX28_PAD_SSP2_SS1__GPIO_2_20 /* External power */ + >; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + leds_pins: leds@0 { + reg = <0>; + fsl,pinmux-ids = < + MX28_PAD_SSP0_DATA7__GPIO_2_7 + MX28_PAD_PWM0__GPIO_3_16 + MX28_PAD_PWM1__GPIO_3_17 + >; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + usb0_vbus_enable_pin: usb0-vbus-enable@0 { + reg = <0>; + fsl,pinmux-ids = ; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; + + usb1_vbus_enable_pin: usb1-vbus-enable@0 { + reg = <0>; + fsl,pinmux-ids = ; + fsl,drive-strength = ; + fsl,pull-up = ; + fsl,voltage = ; + }; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm4_pins_a &pwm7_pins_a>; + status = "okay"; +}; + +/* microSD */ +&ssp0 { + compatible = "fsl,imx28-mmc"; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_4bit_pins_a &mmc0_sck_cfg>; + broken-cd; + bus-width = <4>; + status = "okay"; +}; + +&usb0 { + dr_mode = "host"; + vbus-supply = <®_usb0_vbus>; + status = "okay"; +}; + +&usb1 { + dr_mode = "host"; + vbus-supply = <®_usb1_vbus>; + status = "okay"; +}; + +&usbphy0 { + status = "okay"; +}; + +&usbphy1 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28.dtsi b/arch/arm/boot/dts/nxp/mxs/imx28.dtsi index bbea8b77386f..ece46d0e7c7f 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28.dtsi +++ b/arch/arm/boot/dts/nxp/mxs/imx28.dtsi @@ -755,6 +755,16 @@ MX28_PAD_PWM4__PWM_4 fsl,pull-up = ; }; + pwm7_pins_a: pwm7@0 { + reg = <0>; + fsl,pinmux-ids = < + MX28_PAD_SAIF1_SDATA0__PWM_7 + >; + fsl,drive-strength = ; + fsl,voltage = ; + fsl,pull-up = ; + }; + lcdif_24bit_pins_a: lcdif-24bit@0 { reg = <0>; fsl,pinmux-ids = < diff --git a/arch/arm/boot/dts/nxp/vf/vf-colibri-eval-v3.dtsi b/arch/arm/boot/dts/nxp/vf/vf-colibri-eval-v3.dtsi index 5a19da9313ae..86c360868e4b 100644 --- a/arch/arm/boot/dts/nxp/vf/vf-colibri-eval-v3.dtsi +++ b/arch/arm/boot/dts/nxp/vf/vf-colibri-eval-v3.dtsi @@ -17,6 +17,7 @@ clk16m: clk16m { panel: panel { compatible = "edt,et057090dhu"; backlight = <&bl>; + power-supply = <®_3v3>; port { panel_in: endpoint { @@ -142,11 +143,9 @@ &usbh1 { }; &iomuxc { - vf610-colibri { - pinctrl_can_int: can_int { - fsl,pins = < - VF610_PAD_PTB21__GPIO_43 0x22ed - >; - }; + pinctrl_can_int: can_intgrp { + fsl,pins = < + VF610_PAD_PTB21__GPIO_43 0x22ed + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf-colibri.dtsi b/arch/arm/boot/dts/nxp/vf/vf-colibri.dtsi index cc1e069c44e6..98f9ee1b0030 100644 --- a/arch/arm/boot/dts/nxp/vf/vf-colibri.dtsi +++ b/arch/arm/boot/dts/nxp/vf/vf-colibri.dtsi @@ -171,180 +171,178 @@ &usbphy1 { }; &iomuxc { - vf610-colibri { - pinctrl_flexcan0: can0grp { - fsl,pins = < - VF610_PAD_PTB14__CAN0_RX 0x31F1 - VF610_PAD_PTB15__CAN0_TX 0x31F2 - >; - }; + pinctrl_flexcan0: can0grp { + fsl,pins = < + VF610_PAD_PTB14__CAN0_RX 0x31F1 + VF610_PAD_PTB15__CAN0_TX 0x31F2 + >; + }; - pinctrl_flexcan1: can1grp { - fsl,pins = < - VF610_PAD_PTB16__CAN1_RX 0x31F1 - VF610_PAD_PTB17__CAN1_TX 0x31F2 - >; - }; + pinctrl_flexcan1: can1grp { + fsl,pins = < + VF610_PAD_PTB16__CAN1_RX 0x31F1 + VF610_PAD_PTB17__CAN1_TX 0x31F2 + >; + }; - pinctrl_gpio_ext: gpio_ext { - fsl,pins = < - VF610_PAD_PTD10__GPIO_89 0x22ed /* EXT_IO_0 */ - VF610_PAD_PTD9__GPIO_88 0x22ed /* EXT_IO_1 */ - VF610_PAD_PTD26__GPIO_68 0x22ed /* EXT_IO_2 */ - >; - }; + pinctrl_gpio_ext: gpio_extgrp { + fsl,pins = < + VF610_PAD_PTD10__GPIO_89 0x22ed /* EXT_IO_0 */ + VF610_PAD_PTD9__GPIO_88 0x22ed /* EXT_IO_1 */ + VF610_PAD_PTD26__GPIO_68 0x22ed /* EXT_IO_2 */ + >; + }; - pinctrl_dcu0_1: dcu0grp_1 { - fsl,pins = < - VF610_PAD_PTE0__DCU0_HSYNC 0x1902 - VF610_PAD_PTE1__DCU0_VSYNC 0x1902 - VF610_PAD_PTE2__DCU0_PCLK 0x1902 - VF610_PAD_PTE4__DCU0_DE 0x1902 - VF610_PAD_PTE5__DCU0_R0 0x1902 - VF610_PAD_PTE6__DCU0_R1 0x1902 - VF610_PAD_PTE7__DCU0_R2 0x1902 - VF610_PAD_PTE8__DCU0_R3 0x1902 - VF610_PAD_PTE9__DCU0_R4 0x1902 - VF610_PAD_PTE10__DCU0_R5 0x1902 - VF610_PAD_PTE11__DCU0_R6 0x1902 - VF610_PAD_PTE12__DCU0_R7 0x1902 - VF610_PAD_PTE13__DCU0_G0 0x1902 - VF610_PAD_PTE14__DCU0_G1 0x1902 - VF610_PAD_PTE15__DCU0_G2 0x1902 - VF610_PAD_PTE16__DCU0_G3 0x1902 - VF610_PAD_PTE17__DCU0_G4 0x1902 - VF610_PAD_PTE18__DCU0_G5 0x1902 - VF610_PAD_PTE19__DCU0_G6 0x1902 - VF610_PAD_PTE20__DCU0_G7 0x1902 - VF610_PAD_PTE21__DCU0_B0 0x1902 - VF610_PAD_PTE22__DCU0_B1 0x1902 - VF610_PAD_PTE23__DCU0_B2 0x1902 - VF610_PAD_PTE24__DCU0_B3 0x1902 - VF610_PAD_PTE25__DCU0_B4 0x1902 - VF610_PAD_PTE26__DCU0_B5 0x1902 - VF610_PAD_PTE27__DCU0_B6 0x1902 - VF610_PAD_PTE28__DCU0_B7 0x1902 - >; - }; + pinctrl_dcu0_1: dcu01grp { + fsl,pins = < + VF610_PAD_PTE0__DCU0_HSYNC 0x1902 + VF610_PAD_PTE1__DCU0_VSYNC 0x1902 + VF610_PAD_PTE2__DCU0_PCLK 0x1902 + VF610_PAD_PTE4__DCU0_DE 0x1902 + VF610_PAD_PTE5__DCU0_R0 0x1902 + VF610_PAD_PTE6__DCU0_R1 0x1902 + VF610_PAD_PTE7__DCU0_R2 0x1902 + VF610_PAD_PTE8__DCU0_R3 0x1902 + VF610_PAD_PTE9__DCU0_R4 0x1902 + VF610_PAD_PTE10__DCU0_R5 0x1902 + VF610_PAD_PTE11__DCU0_R6 0x1902 + VF610_PAD_PTE12__DCU0_R7 0x1902 + VF610_PAD_PTE13__DCU0_G0 0x1902 + VF610_PAD_PTE14__DCU0_G1 0x1902 + VF610_PAD_PTE15__DCU0_G2 0x1902 + VF610_PAD_PTE16__DCU0_G3 0x1902 + VF610_PAD_PTE17__DCU0_G4 0x1902 + VF610_PAD_PTE18__DCU0_G5 0x1902 + VF610_PAD_PTE19__DCU0_G6 0x1902 + VF610_PAD_PTE20__DCU0_G7 0x1902 + VF610_PAD_PTE21__DCU0_B0 0x1902 + VF610_PAD_PTE22__DCU0_B1 0x1902 + VF610_PAD_PTE23__DCU0_B2 0x1902 + VF610_PAD_PTE24__DCU0_B3 0x1902 + VF610_PAD_PTE25__DCU0_B4 0x1902 + VF610_PAD_PTE26__DCU0_B5 0x1902 + VF610_PAD_PTE27__DCU0_B6 0x1902 + VF610_PAD_PTE28__DCU0_B7 0x1902 + >; + }; - pinctrl_dspi1: dspi1grp { - fsl,pins = < - VF610_PAD_PTD5__DSPI1_CS0 0x33e2 - VF610_PAD_PTD6__DSPI1_SIN 0x33e1 - VF610_PAD_PTD7__DSPI1_SOUT 0x33e2 - VF610_PAD_PTD8__DSPI1_SCK 0x33e2 - >; - }; + pinctrl_dspi1: dspi1grp { + fsl,pins = < + VF610_PAD_PTD5__DSPI1_CS0 0x33e2 + VF610_PAD_PTD6__DSPI1_SIN 0x33e1 + VF610_PAD_PTD7__DSPI1_SOUT 0x33e2 + VF610_PAD_PTD8__DSPI1_SCK 0x33e2 + >; + }; - pinctrl_esdhc1: esdhc1grp { - fsl,pins = < - VF610_PAD_PTA24__ESDHC1_CLK 0x31ef - VF610_PAD_PTA25__ESDHC1_CMD 0x31ef - VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef - VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef - VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef - VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef - VF610_PAD_PTB20__GPIO_42 0x219d - >; - }; + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + VF610_PAD_PTA24__ESDHC1_CLK 0x31ef + VF610_PAD_PTA25__ESDHC1_CMD 0x31ef + VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef + VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef + VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef + VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef + VF610_PAD_PTB20__GPIO_42 0x219d + >; + }; - pinctrl_fec1: fec1grp { - fsl,pins = < - VF610_PAD_PTA6__RMII_CLKOUT 0x30d2 - VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 - VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 - VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 - VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 - VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 - VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 - VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 - VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 - VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + VF610_PAD_PTA6__RMII_CLKOUT 0x30d2 + VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 + VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 + VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 + VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 + VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 + VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 + VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 + VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 + VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 + >; + }; - pinctrl_gpio_bl_on: gpio_bl_on { - fsl,pins = < - VF610_PAD_PTC0__GPIO_45 0x22ef - >; - }; + pinctrl_gpio_bl_on: gpio_bl_ongrp { + fsl,pins = < + VF610_PAD_PTC0__GPIO_45 0x22ef + >; + }; - pinctrl_i2c0: i2c0grp { - fsl,pins = < - VF610_PAD_PTB14__I2C0_SCL 0x37ff - VF610_PAD_PTB15__I2C0_SDA 0x37ff - >; - }; + pinctrl_i2c0: i2c0grp { + fsl,pins = < + VF610_PAD_PTB14__I2C0_SCL 0x37ff + VF610_PAD_PTB15__I2C0_SDA 0x37ff + >; + }; - pinctrl_i2c0_gpio: i2c0gpiogrp { - fsl,pins = < - VF610_PAD_PTB14__GPIO_36 0x37ff - VF610_PAD_PTB15__GPIO_37 0x37ff - >; - }; + pinctrl_i2c0_gpio: i2c0gpiogrp { + fsl,pins = < + VF610_PAD_PTB14__GPIO_36 0x37ff + VF610_PAD_PTB15__GPIO_37 0x37ff + >; + }; - pinctrl_nfc: nfcgrp { - fsl,pins = < - VF610_PAD_PTD23__NF_IO7 0x28df - VF610_PAD_PTD22__NF_IO6 0x28df - VF610_PAD_PTD21__NF_IO5 0x28df - VF610_PAD_PTD20__NF_IO4 0x28df - VF610_PAD_PTD19__NF_IO3 0x28df - VF610_PAD_PTD18__NF_IO2 0x28df - VF610_PAD_PTD17__NF_IO1 0x28df - VF610_PAD_PTD16__NF_IO0 0x28df - VF610_PAD_PTB24__NF_WE_B 0x28c2 - VF610_PAD_PTB25__NF_CE0_B 0x28c2 - VF610_PAD_PTB27__NF_RE_B 0x28c2 - VF610_PAD_PTC26__NF_RB_B 0x283d - VF610_PAD_PTC27__NF_ALE 0x28c2 - VF610_PAD_PTC28__NF_CLE 0x28c2 - >; - }; + pinctrl_nfc: nfcgrp { + fsl,pins = < + VF610_PAD_PTD23__NF_IO7 0x28df + VF610_PAD_PTD22__NF_IO6 0x28df + VF610_PAD_PTD21__NF_IO5 0x28df + VF610_PAD_PTD20__NF_IO4 0x28df + VF610_PAD_PTD19__NF_IO3 0x28df + VF610_PAD_PTD18__NF_IO2 0x28df + VF610_PAD_PTD17__NF_IO1 0x28df + VF610_PAD_PTD16__NF_IO0 0x28df + VF610_PAD_PTB24__NF_WE_B 0x28c2 + VF610_PAD_PTB25__NF_CE0_B 0x28c2 + VF610_PAD_PTB27__NF_RE_B 0x28c2 + VF610_PAD_PTC26__NF_RB_B 0x283d + VF610_PAD_PTC27__NF_ALE 0x28c2 + VF610_PAD_PTC28__NF_CLE 0x28c2 + >; + }; - pinctrl_pwm0: pwm0grp { - fsl,pins = < - VF610_PAD_PTB0__FTM0_CH0 0x1182 - VF610_PAD_PTB1__FTM0_CH1 0x1182 - >; - }; + pinctrl_pwm0: pwm0grp { + fsl,pins = < + VF610_PAD_PTB0__FTM0_CH0 0x1182 + VF610_PAD_PTB1__FTM0_CH1 0x1182 + >; + }; - pinctrl_pwm1: pwm1grp { - fsl,pins = < - VF610_PAD_PTB8__FTM1_CH0 0x1182 - VF610_PAD_PTB9__FTM1_CH1 0x1182 - >; - }; + pinctrl_pwm1: pwm1grp { + fsl,pins = < + VF610_PAD_PTB8__FTM1_CH0 0x1182 + VF610_PAD_PTB9__FTM1_CH1 0x1182 + >; + }; - pinctrl_uart0: uart0grp { - fsl,pins = < - VF610_PAD_PTB10__UART0_TX 0x21a2 - VF610_PAD_PTB11__UART0_RX 0x21a1 - VF610_PAD_PTB12__UART0_RTS 0x21a2 - VF610_PAD_PTB13__UART0_CTS 0x21a1 - >; - }; + pinctrl_uart0: uart0grp { + fsl,pins = < + VF610_PAD_PTB10__UART0_TX 0x21a2 + VF610_PAD_PTB11__UART0_RX 0x21a1 + VF610_PAD_PTB12__UART0_RTS 0x21a2 + VF610_PAD_PTB13__UART0_CTS 0x21a1 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - VF610_PAD_PTB4__UART1_TX 0x21a2 - VF610_PAD_PTB5__UART1_RX 0x21a1 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + VF610_PAD_PTB4__UART1_TX 0x21a2 + VF610_PAD_PTB5__UART1_RX 0x21a1 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - VF610_PAD_PTD0__UART2_TX 0x21a2 - VF610_PAD_PTD1__UART2_RX 0x21a1 - VF610_PAD_PTD2__UART2_RTS 0x21a2 - VF610_PAD_PTD3__UART2_CTS 0x21a1 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + VF610_PAD_PTD0__UART2_TX 0x21a2 + VF610_PAD_PTD1__UART2_RX 0x21a1 + VF610_PAD_PTD2__UART2_RTS 0x21a2 + VF610_PAD_PTD3__UART2_CTS 0x21a1 + >; + }; - pinctrl_usbh1_reg: gpio_usb_vbus { - fsl,pins = < - VF610_PAD_PTD4__GPIO_83 0x22ed - >; - }; + pinctrl_usbh1_reg: gpio_usb_vbusgrp { + fsl,pins = < + VF610_PAD_PTD4__GPIO_83 0x22ed + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf500-colibri.dtsi b/arch/arm/boot/dts/nxp/vf/vf500-colibri.dtsi index 8af7ed56e653..ae3403c766d6 100644 --- a/arch/arm/boot/dts/nxp/vf/vf500-colibri.dtsi +++ b/arch/arm/boot/dts/nxp/vf/vf500-colibri.dtsi @@ -40,30 +40,28 @@ &nfc { }; &iomuxc { - vf610-colibri { - pinctrl_touchctrl_idle: touchctrl_idle { - fsl,pins = < - VF610_PAD_PTA18__GPIO_8 0x006d - VF610_PAD_PTA19__GPIO_9 0x006c - >; - }; + pinctrl_touchctrl_idle: touchctrl_idlegrp { + fsl,pins = < + VF610_PAD_PTA18__GPIO_8 0x006d + VF610_PAD_PTA19__GPIO_9 0x006c + >; + }; - pinctrl_touchctrl_default: touchctrl_default { - fsl,pins = < - VF610_PAD_PTA18__ADC0_SE0 0x0040 - VF610_PAD_PTA19__ADC0_SE1 0x0040 - VF610_PAD_PTA16__ADC1_SE0 0x0040 - VF610_PAD_PTB2__ADC1_SE2 0x0040 - >; - }; + pinctrl_touchctrl_default: touchctrl_defaultgrp { + fsl,pins = < + VF610_PAD_PTA18__ADC0_SE0 0x0040 + VF610_PAD_PTA19__ADC0_SE1 0x0040 + VF610_PAD_PTA16__ADC1_SE0 0x0040 + VF610_PAD_PTB2__ADC1_SE2 0x0040 + >; + }; - pinctrl_touchctrl_gpios: touchctrl_gpios { - fsl,pins = < - VF610_PAD_PTA23__GPIO_13 0x22e9 - VF610_PAD_PTB23__GPIO_93 0x22e9 - VF610_PAD_PTA22__GPIO_12 0x22e9 - VF610_PAD_PTA11__GPIO_4 0x22e9 - >; - }; + pinctrl_touchctrl_gpios: touchctrl_gpiosgrp { + fsl,pins = < + VF610_PAD_PTA23__GPIO_13 0x22e9 + VF610_PAD_PTB23__GPIO_93 0x22e9 + VF610_PAD_PTA22__GPIO_12 0x22e9 + VF610_PAD_PTA11__GPIO_4 0x22e9 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf500.dtsi b/arch/arm/boot/dts/nxp/vf/vf500.dtsi index 0c0dd442300a..71ccdaa6f269 100644 --- a/arch/arm/boot/dts/nxp/vf/vf500.dtsi +++ b/arch/arm/boot/dts/nxp/vf/vf500.dtsi @@ -43,15 +43,13 @@ global_timer: timer@40002200 { }; }; - bus@40080000 { - pmu@40089000 { - compatible = "arm,cortex-a5-pmu"; - interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; - interrupt-affinity = <&a5_cpu>; - reg = <0x40089000 0x1000>; - }; - }; + }; + pmu { + compatible = "arm,cortex-a5-pmu"; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; + interrupt-affinity = <&a5_cpu>; + interrupt-parent = <&mscm_ir>; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-bk4.dts b/arch/arm/boot/dts/nxp/vf/vf610-bk4.dts index 2492fb99956c..e36c854dc297 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-bk4.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-bk4.dts @@ -458,7 +458,7 @@ VF610_PAD_PTE16__GPIO_121 0x1183 >; }; - pinctrl_gpio_spi: pinctrl-gpio-spi { + pinctrl_gpio_spi: pinctrl-gpio-spigrp { fsl,pins = < VF610_PAD_PTB18__GPIO_40 0x1183 VF610_PAD_PTD10__GPIO_89 0x1183 diff --git a/arch/arm/boot/dts/nxp/vf/vf610-cosmic.dts b/arch/arm/boot/dts/nxp/vf/vf610-cosmic.dts index 703f375d7e24..f1e6344b0c69 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-cosmic.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-cosmic.dts @@ -47,39 +47,37 @@ &fec1 { }; &iomuxc { - vf610-cosmic { - pinctrl_esdhc1: esdhc1grp { - fsl,pins = < - VF610_PAD_PTA24__ESDHC1_CLK 0x31ef - VF610_PAD_PTA25__ESDHC1_CMD 0x31ef - VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef - VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef - VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef - VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef - VF610_PAD_PTB28__GPIO_98 0x219d - >; - }; + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + VF610_PAD_PTA24__ESDHC1_CLK 0x31ef + VF610_PAD_PTA25__ESDHC1_CMD 0x31ef + VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef + VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef + VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef + VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef + VF610_PAD_PTB28__GPIO_98 0x219d + >; + }; - pinctrl_fec1: fec1grp { - fsl,pins = < - VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 - VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 - VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 - VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 - VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 - VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 - VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 - VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 - VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 + VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 + VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 + VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 + VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 + VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 + VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 + VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 + VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - VF610_PAD_PTB4__UART1_TX 0x21a2 - VF610_PAD_PTB5__UART1_RX 0x21a1 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + VF610_PAD_PTB4__UART1_TX 0x21a2 + VF610_PAD_PTB5__UART1_RX 0x21a1 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-twr.dts b/arch/arm/boot/dts/nxp/vf/vf610-twr.dts index 876c14ecceb6..e7c2f6d46ab2 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-twr.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-twr.dts @@ -166,131 +166,129 @@ codec: sgtl5000@a { }; &iomuxc { - vf610-twr { - pinctrl_adc0_ad5: adc0ad5grp { - fsl,pins = < - VF610_PAD_PTC30__ADC0_SE5 0xa1 - >; - }; + pinctrl_adc0_ad5: adc0ad5grp { + fsl,pins = < + VF610_PAD_PTC30__ADC0_SE5 0xa1 + >; + }; - pinctrl_dspi0: dspi0grp { - fsl,pins = < - VF610_PAD_PTB19__DSPI0_CS0 0x1182 - VF610_PAD_PTB20__DSPI0_SIN 0x1181 - VF610_PAD_PTB21__DSPI0_SOUT 0x1182 - VF610_PAD_PTB22__DSPI0_SCK 0x1182 - >; - }; + pinctrl_dspi0: dspi0grp { + fsl,pins = < + VF610_PAD_PTB19__DSPI0_CS0 0x1182 + VF610_PAD_PTB20__DSPI0_SIN 0x1181 + VF610_PAD_PTB21__DSPI0_SOUT 0x1182 + VF610_PAD_PTB22__DSPI0_SCK 0x1182 + >; + }; - pinctrl_esdhc1: esdhc1grp { - fsl,pins = < - VF610_PAD_PTA24__ESDHC1_CLK 0x31ef - VF610_PAD_PTA25__ESDHC1_CMD 0x31ef - VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef - VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef - VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef - VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef - VF610_PAD_PTA7__GPIO_134 0x219d - >; - }; + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + VF610_PAD_PTA24__ESDHC1_CLK 0x31ef + VF610_PAD_PTA25__ESDHC1_CMD 0x31ef + VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef + VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef + VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef + VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef + VF610_PAD_PTA7__GPIO_134 0x219d + >; + }; - pinctrl_fec0: fec0grp { - fsl,pins = < - VF610_PAD_PTA6__RMII_CLKIN 0x30d1 - VF610_PAD_PTC0__ENET_RMII0_MDC 0x30d3 - VF610_PAD_PTC1__ENET_RMII0_MDIO 0x30d1 - VF610_PAD_PTC2__ENET_RMII0_CRS 0x30d1 - VF610_PAD_PTC3__ENET_RMII0_RXD1 0x30d1 - VF610_PAD_PTC4__ENET_RMII0_RXD0 0x30d1 - VF610_PAD_PTC5__ENET_RMII0_RXER 0x30d1 - VF610_PAD_PTC6__ENET_RMII0_TXD1 0x30d2 - VF610_PAD_PTC7__ENET_RMII0_TXD0 0x30d2 - VF610_PAD_PTC8__ENET_RMII0_TXEN 0x30d2 - >; - }; + pinctrl_fec0: fec0grp { + fsl,pins = < + VF610_PAD_PTA6__RMII_CLKIN 0x30d1 + VF610_PAD_PTC0__ENET_RMII0_MDC 0x30d3 + VF610_PAD_PTC1__ENET_RMII0_MDIO 0x30d1 + VF610_PAD_PTC2__ENET_RMII0_CRS 0x30d1 + VF610_PAD_PTC3__ENET_RMII0_RXD1 0x30d1 + VF610_PAD_PTC4__ENET_RMII0_RXD0 0x30d1 + VF610_PAD_PTC5__ENET_RMII0_RXER 0x30d1 + VF610_PAD_PTC6__ENET_RMII0_TXD1 0x30d2 + VF610_PAD_PTC7__ENET_RMII0_TXD0 0x30d2 + VF610_PAD_PTC8__ENET_RMII0_TXEN 0x30d2 + >; + }; - pinctrl_fec1: fec1grp { - fsl,pins = < - VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 - VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 - VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 - VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 - VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 - VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 - VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 - VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 - VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 + VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 + VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 + VF610_PAD_PTC12__ENET_RMII1_RXD1 0x30d1 + VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 + VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 + VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 + VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 + VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 + >; + }; - pinctrl_i2c0: i2c0grp { - fsl,pins = < - VF610_PAD_PTB14__I2C0_SCL 0x30d3 - VF610_PAD_PTB15__I2C0_SDA 0x30d3 - >; - }; + pinctrl_i2c0: i2c0grp { + fsl,pins = < + VF610_PAD_PTB14__I2C0_SCL 0x30d3 + VF610_PAD_PTB15__I2C0_SDA 0x30d3 + >; + }; - pinctrl_nfc: nfcgrp { - fsl,pins = < - VF610_PAD_PTD31__NF_IO15 0x28df - VF610_PAD_PTD30__NF_IO14 0x28df - VF610_PAD_PTD29__NF_IO13 0x28df - VF610_PAD_PTD28__NF_IO12 0x28df - VF610_PAD_PTD27__NF_IO11 0x28df - VF610_PAD_PTD26__NF_IO10 0x28df - VF610_PAD_PTD25__NF_IO9 0x28df - VF610_PAD_PTD24__NF_IO8 0x28df - VF610_PAD_PTD23__NF_IO7 0x28df - VF610_PAD_PTD22__NF_IO6 0x28df - VF610_PAD_PTD21__NF_IO5 0x28df - VF610_PAD_PTD20__NF_IO4 0x28df - VF610_PAD_PTD19__NF_IO3 0x28df - VF610_PAD_PTD18__NF_IO2 0x28df - VF610_PAD_PTD17__NF_IO1 0x28df - VF610_PAD_PTD16__NF_IO0 0x28df - VF610_PAD_PTB24__NF_WE_B 0x28c2 - VF610_PAD_PTB25__NF_CE0_B 0x28c2 - VF610_PAD_PTB27__NF_RE_B 0x28c2 - VF610_PAD_PTC26__NF_RB_B 0x283d - VF610_PAD_PTC27__NF_ALE 0x28c2 - VF610_PAD_PTC28__NF_CLE 0x28c2 - >; - }; + pinctrl_nfc: nfcgrp { + fsl,pins = < + VF610_PAD_PTD31__NF_IO15 0x28df + VF610_PAD_PTD30__NF_IO14 0x28df + VF610_PAD_PTD29__NF_IO13 0x28df + VF610_PAD_PTD28__NF_IO12 0x28df + VF610_PAD_PTD27__NF_IO11 0x28df + VF610_PAD_PTD26__NF_IO10 0x28df + VF610_PAD_PTD25__NF_IO9 0x28df + VF610_PAD_PTD24__NF_IO8 0x28df + VF610_PAD_PTD23__NF_IO7 0x28df + VF610_PAD_PTD22__NF_IO6 0x28df + VF610_PAD_PTD21__NF_IO5 0x28df + VF610_PAD_PTD20__NF_IO4 0x28df + VF610_PAD_PTD19__NF_IO3 0x28df + VF610_PAD_PTD18__NF_IO2 0x28df + VF610_PAD_PTD17__NF_IO1 0x28df + VF610_PAD_PTD16__NF_IO0 0x28df + VF610_PAD_PTB24__NF_WE_B 0x28c2 + VF610_PAD_PTB25__NF_CE0_B 0x28c2 + VF610_PAD_PTB27__NF_RE_B 0x28c2 + VF610_PAD_PTC26__NF_RB_B 0x283d + VF610_PAD_PTC27__NF_ALE 0x28c2 + VF610_PAD_PTC28__NF_CLE 0x28c2 + >; + }; - pinctrl_pwm0: pwm0grp { - fsl,pins = < - VF610_PAD_PTB0__FTM0_CH0 0x1582 - VF610_PAD_PTB1__FTM0_CH1 0x1582 - VF610_PAD_PTB2__FTM0_CH2 0x1582 - VF610_PAD_PTB3__FTM0_CH3 0x1582 - >; - }; + pinctrl_pwm0: pwm0grp { + fsl,pins = < + VF610_PAD_PTB0__FTM0_CH0 0x1582 + VF610_PAD_PTB1__FTM0_CH1 0x1582 + VF610_PAD_PTB2__FTM0_CH2 0x1582 + VF610_PAD_PTB3__FTM0_CH3 0x1582 + >; + }; - pinctrl_sai2: sai2grp { - fsl,pins = < - VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed - VF610_PAD_PTA18__SAI2_TX_DATA 0x02ee - VF610_PAD_PTA19__SAI2_TX_SYNC 0x02ed - VF610_PAD_PTA21__SAI2_RX_BCLK 0x02ed - VF610_PAD_PTA22__SAI2_RX_DATA 0x02ed - VF610_PAD_PTA23__SAI2_RX_SYNC 0x02ed - VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x02ed - >; - }; + pinctrl_sai2: sai2grp { + fsl,pins = < + VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed + VF610_PAD_PTA18__SAI2_TX_DATA 0x02ee + VF610_PAD_PTA19__SAI2_TX_SYNC 0x02ed + VF610_PAD_PTA21__SAI2_RX_BCLK 0x02ed + VF610_PAD_PTA22__SAI2_RX_DATA 0x02ed + VF610_PAD_PTA23__SAI2_RX_SYNC 0x02ed + VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x02ed + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - VF610_PAD_PTB4__UART1_TX 0x21a2 - VF610_PAD_PTB5__UART1_RX 0x21a1 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + VF610_PAD_PTB4__UART1_TX 0x21a2 + VF610_PAD_PTB5__UART1_RX 0x21a1 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - VF610_PAD_PTB6__UART2_TX 0x21a2 - VF610_PAD_PTB7__UART2_RX 0x21a1 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + VF610_PAD_PTB6__UART2_TX 0x21a2 + VF610_PAD_PTB7__UART2_RX 0x21a1 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-cfu1.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-cfu1.dts index 7e72f860c3c5..929426c1299c 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-cfu1.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-cfu1.dts @@ -68,8 +68,8 @@ sff: sfp { pinctrl-0 = <&pinctrl_optical>; pinctrl-names = "default"; i2c-bus = <&i2c0>; - los-gpio = <&gpio4 4 GPIO_ACTIVE_HIGH>; - tx-disable-gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>; + los-gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&gpio3 22 GPIO_ACTIVE_HIGH>; }; supply-voltage-monitor { @@ -333,7 +333,7 @@ VF610_PAD_PTB17__I2C1_SDA 0x37ff >; }; - pinctrl_leds_debug: pinctrl-leds-debug { + pinctrl_leds_debug: pinctrl-leds-debuggrp { fsl,pins = < VF610_PAD_PTD3__GPIO_82 0x31c2 VF610_PAD_PTE3__GPIO_108 0x31c2 diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts index 4f99044837f8..79ea7cf57a4d 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts @@ -311,7 +311,7 @@ gpio5: io-expander@18 { * I/O14 - OPT1_TX_DIS * I/O15 - OPT2_TX_DIS */ - gpio6: sx1503@20 { + gpio6: pinctrl@20 { compatible = "semtech,sx1503q"; pinctrl-names = "default"; @@ -429,7 +429,7 @@ ethernet-phy@0 { }; &iomuxc { - pinctr_atzb_rf_233: pinctrl-atzb-rf-233 { + pinctr_atzb_rf_233: pinctrl-atzb-rf-233grp { fsl,pins = < VF610_PAD_PTB2__GPIO_24 0x31c2 VF610_PAD_PTE27__GPIO_132 0x33e2 @@ -437,7 +437,7 @@ VF610_PAD_PTE27__GPIO_132 0x33e2 }; - pinctrl_sx1503_20: pinctrl-sx1503-20 { + pinctrl_sx1503_20: pinctrl-sx1503-20grp { fsl,pins = < VF610_PAD_PTB1__GPIO_23 0x219d >; @@ -450,7 +450,7 @@ VF610_PAD_PTA21__UART3_RX 0x21a1 >; }; - pinctrl_mdio_mux: pinctrl-mdio-mux { + pinctrl_mdio_mux: pinctrl-mdio-muxgrp { fsl,pins = < VF610_PAD_PTA18__GPIO_8 0x31c2 VF610_PAD_PTA19__GPIO_9 0x31c2 @@ -458,7 +458,7 @@ VF610_PAD_PTB3__GPIO_25 0x31c2 >; }; - pinctrl_fec0_phy_int: pinctrl-fec0-phy-int { + pinctrl_fec0_phy_int: pinctrl-fec0-phy-intgrp { fsl,pins = < VF610_PAD_PTB28__GPIO_98 0x219d >; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-scu4-aib.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-scu4-aib.dts index 77492eeea450..8020a644dd9d 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-scu4-aib.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-scu4-aib.dts @@ -583,7 +583,7 @@ &i2c2 { pinctrl-0 = <&pinctrl_i2c2>; status = "okay"; - gpio9: io-expander@20 { + gpio9: pinctrl@20 { compatible = "semtech,sx1503q"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sx1503_20>; @@ -623,7 +623,6 @@ eeprom@54 { i2c-mux@70 { compatible = "nxp,pca9548"; - pinctrl-names = "default"; #address-cells = <1>; #size-cells = <0>; reg = <0x70>; @@ -662,7 +661,6 @@ sff4_i2c: i2c@5 { i2c-mux@71 { compatible = "nxp,pca9548"; - pinctrl-names = "default"; reg = <0x71>; #address-cells = <1>; #size-cells = <0>; @@ -747,7 +745,7 @@ VF610_PAD_PTC8__DSPI1_SCK 0x1182 >; }; - pinctrl_dspi2: dspi2gpio { + pinctrl_dspi2: dspi2gpiogrp { fsl,pins = < VF610_PAD_PTD30__GPIO_64 0x33e2 VF610_PAD_PTD29__GPIO_65 0x33e1 @@ -819,13 +817,13 @@ VF610_PAD_PTA23__I2C2_SDA 0x37ff >; }; - pinctrl_leds_debug: pinctrl-leds-debug { + pinctrl_leds_debug: pinctrl-leds-debuggrp { fsl,pins = < VF610_PAD_PTB26__GPIO_96 0x31c2 >; }; - pinctrl_mdio_mux: pinctrl-mdio-mux { + pinctrl_mdio_mux: pinctrl-mdio-muxgrp { fsl,pins = < VF610_PAD_PTE27__GPIO_132 0x31c2 VF610_PAD_PTE28__GPIO_133 0x31c2 @@ -845,7 +843,7 @@ VF610_PAD_PTD12__QSPI0_B_DATA0 0x31c3 >; }; - pinctrl_sx1503_20: pinctrl-sx1503-20 { + pinctrl_sx1503_20: pinctrl-sx1503-20grp { fsl,pins = < VF610_PAD_PTD31__GPIO_63 0x219d >; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-spb4.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-spb4.dts index 2a490464660c..423d185c971f 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-spb4.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-spb4.dts @@ -323,7 +323,7 @@ VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 >; }; - pinctrl_gpio_switch0: pinctrl-gpio-switch0 { + pinctrl_gpio_switch0: pinctrl-gpio-switch0grp { fsl,pins = < VF610_PAD_PTB28__GPIO_98 0x219d >; @@ -343,7 +343,7 @@ VF610_PAD_PTB17__I2C1_SDA 0x37ff >; }; - pinctrl_leds_debug: pinctrl-leds-debug { + pinctrl_leds_debug: pinctrl-leds-debuggrp { fsl,pins = < VF610_PAD_PTD3__GPIO_82 0x31c2 >; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-dtu.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-dtu.dts index 078d8699e16d..d5c7f710c314 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-dtu.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-dtu.dts @@ -284,13 +284,13 @@ VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 >; }; - pinctrl_gpio_phy9: pinctrl-gpio-phy9 { + pinctrl_gpio_phy9: pinctrl-gpio-phy9grp { fsl,pins = < VF610_PAD_PTB24__GPIO_94 0x219d >; }; - pinctrl_gpio_switch0: pinctrl-gpio-switch0 { + pinctrl_gpio_switch0: pinctrl-gpio-switch0grp { fsl,pins = < VF610_PAD_PTB28__GPIO_98 0x219d >; @@ -310,7 +310,7 @@ VF610_PAD_PTB17__I2C1_SDA 0x37ff >; }; - pinctrl_leds_debug: pinctrl-leds-debug { + pinctrl_leds_debug: pinctrl-leds-debuggrp { fsl,pins = < VF610_PAD_PTD3__GPIO_82 0x31c2 >; diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-spu3.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-spu3.dts index 22c8f44390a9..344cc2b4d0ad 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-spu3.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-ssmb-spu3.dts @@ -330,7 +330,7 @@ VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 >; }; - pinctrl_gpio_switch0: pinctrl-gpio-switch0 { + pinctrl_gpio_switch0: pinctrl-gpio-switch0grp { fsl,pins = < VF610_PAD_PTB28__GPIO_98 0x219d >; @@ -350,7 +350,7 @@ VF610_PAD_PTB17__I2C1_SDA 0x37ff >; }; - pinctrl_leds_debug: pinctrl-leds-debug { + pinctrl_leds_debug: pinctrl-leds-debuggrp { fsl,pins = < VF610_PAD_PTD3__GPIO_82 0x31c2 >; diff --git a/arch/arm/boot/dts/nxp/vf/vf610m4-colibri.dts b/arch/arm/boot/dts/nxp/vf/vf610m4-colibri.dts index 2c2db47af441..86d32f54c250 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610m4-colibri.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610m4-colibri.dts @@ -50,14 +50,12 @@ &uart2 { }; &iomuxc { - vf610-colibri { - pinctrl_uart2: uart2grp { - fsl,pins = < - VF610_PAD_PTD0__UART2_TX 0x21a2 - VF610_PAD_PTD1__UART2_RX 0x21a1 - VF610_PAD_PTD2__UART2_RTS 0x21a2 - VF610_PAD_PTD3__UART2_CTS 0x21a1 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + VF610_PAD_PTD0__UART2_TX 0x21a2 + VF610_PAD_PTD1__UART2_RX 0x21a1 + VF610_PAD_PTD2__UART2_RTS 0x21a2 + VF610_PAD_PTD3__UART2_CTS 0x21a1 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vf610m4-cosmic.dts b/arch/arm/boot/dts/nxp/vf/vf610m4-cosmic.dts index f7474c11aabd..454b484368cb 100644 --- a/arch/arm/boot/dts/nxp/vf/vf610m4-cosmic.dts +++ b/arch/arm/boot/dts/nxp/vf/vf610m4-cosmic.dts @@ -79,12 +79,10 @@ &uart3 { }; &iomuxc { - vf610-cosmic { - pinctrl_uart3: uart3grp { - fsl,pins = < - VF610_PAD_PTA20__UART3_TX 0x21a2 - VF610_PAD_PTA21__UART3_RX 0x21a1 - >; - }; + pinctrl_uart3: uart3grp { + fsl,pins = < + VF610_PAD_PTA20__UART3_TX 0x21a2 + VF610_PAD_PTA21__UART3_RX 0x21a1 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/vf/vfxxx.dtsi b/arch/arm/boot/dts/nxp/vf/vfxxx.dtsi index 597f20be82f1..124003c0be26 100644 --- a/arch/arm/boot/dts/nxp/vf/vfxxx.dtsi +++ b/arch/arm/boot/dts/nxp/vf/vfxxx.dtsi @@ -318,6 +318,7 @@ gpio0: gpio@40049000 { interrupt-controller; #interrupt-cells = <2>; gpio-ranges = <&iomuxc 0 0 32>; + ngpios = <32>; }; gpio1: gpio@4004a000 { @@ -329,6 +330,7 @@ gpio1: gpio@4004a000 { interrupt-controller; #interrupt-cells = <2>; gpio-ranges = <&iomuxc 0 32 32>; + ngpios = <32>; }; gpio2: gpio@4004b000 { @@ -340,6 +342,7 @@ gpio2: gpio@4004b000 { interrupt-controller; #interrupt-cells = <2>; gpio-ranges = <&iomuxc 0 64 32>; + ngpios = <32>; }; gpio3: gpio@4004c000 { @@ -351,6 +354,7 @@ gpio3: gpio@4004c000 { interrupt-controller; #interrupt-cells = <2>; gpio-ranges = <&iomuxc 0 96 32>; + ngpios = <32>; }; gpio4: gpio@4004d000 { @@ -362,6 +366,7 @@ gpio4: gpio@4004d000 { interrupt-controller; #interrupt-cells = <2>; gpio-ranges = <&iomuxc 0 128 7>; + ngpios = <7>; }; anatop: anatop@40050000 { @@ -603,7 +608,7 @@ usbmisc1: usb@400b4800 { ftm: ftm@400b8000 { compatible = "fsl,ftm-timer"; - reg = <0x400b8000 0x1000 0x400b9000 0x1000>; + reg = <0x400b8000 0x1000>, <0x400b9000 0x1000>; interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; clock-names = "ftm-evt", "ftm-src", "ftm-evt-counter-en", "ftm-src-counter-en"; diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 0c1d116f6e84..e875b5d25e84 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -47,6 +47,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ qcom-msm8974-samsung-hlte.dtb \ qcom-msm8974-sony-xperia-rhine-amami.dtb \ qcom-msm8974-sony-xperia-rhine-honami.dtb \ + qcom-msm8974-sony-xperia-rhine-togari.dtb \ qcom-msm8974pro-fairphone-fp2.dtb \ qcom-msm8974pro-htc-m8.dtb \ qcom-msm8974pro-oneplus-bacon.dtb \ diff --git a/arch/arm/boot/dts/qcom/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom/qcom-msm8960.dtsi index 4babd0bbe5d6..203f0b69b353 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8960.dtsi +++ b/arch/arm/boot/dts/qcom/qcom-msm8960.dtsi @@ -18,7 +18,7 @@ / { cpus { #address-cells = <1>; #size-cells = <0>; - interrupts = ; + interrupts = ; cpu@0 { compatible = "qcom,krait"; @@ -96,7 +96,7 @@ cpu_crit1: trip1 { cpu-pmu { compatible = "qcom,krait-pmu"; - interrupts = ; + interrupts = ; qcom,no-pc-write; }; @@ -149,9 +149,9 @@ intc: interrupt-controller@2000000 { timer@200a000 { compatible = "qcom,kpss-wdt-msm8960", "qcom,kpss-timer", "qcom,msm-timer"; - interrupts = , - , - ; + interrupts = , + , + ; reg = <0x0200a000 0x100>; clock-frequency = <27000000>; clocks = <&sleep_clk>; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974-lge-nexus5-hammerhead.dts b/arch/arm/boot/dts/qcom/qcom-msm8974-lge-nexus5-hammerhead.dts index 261044fdfee8..b3127f0383cf 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974-lge-nexus5-hammerhead.dts +++ b/arch/arm/boot/dts/qcom/qcom-msm8974-lge-nexus5-hammerhead.dts @@ -12,6 +12,7 @@ / { chassis-type = "handset"; aliases { + mmc0 = &sdhc_1; serial0 = &blsp1_uart1; serial1 = &blsp2_uart4; }; @@ -598,7 +599,7 @@ &sdhc_2 { pinctrl-0 = <&sdc2_on>; pinctrl-1 = <&sdc2_off>; - bcrmf@1 { + wifi@1 { compatible = "brcm,bcm4339-fmac", "brcm,bcm4329-fmac"; reg = <1>; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-amami.dts b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-amami.dts index 9f2ab5c122d0..472a45408add 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-amami.dts +++ b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-amami.dts @@ -5,6 +5,22 @@ / { model = "Sony Xperia Z1 Compact"; compatible = "sony,xperia-amami", "qcom,msm8974"; chassis-type = "handset"; + + gpio-keys { + key-camera-snapshot { + label = "camera_snapshot"; + gpios = <&pm8941_gpios 3 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + }; + + key-camera-focus { + label = "camera_focus"; + gpios = <&pm8941_gpios 4 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + }; + }; }; &smbb { diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-honami.dts b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-honami.dts index 9028f17e5c4a..c3d69641fc1d 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-honami.dts +++ b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-honami.dts @@ -5,4 +5,20 @@ / { model = "Sony Xperia Z1"; compatible = "sony,xperia-honami", "qcom,msm8974"; chassis-type = "handset"; + + gpio-keys { + key-camera-snapshot { + label = "camera_snapshot"; + gpios = <&pm8941_gpios 3 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + }; + + key-camera-focus { + label = "camera_focus"; + gpios = <&pm8941_gpios 4 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-togari.dts b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-togari.dts new file mode 100644 index 000000000000..f60f7304d35e --- /dev/null +++ b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine-togari.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "qcom-msm8974-sony-xperia-rhine.dtsi" + +/* Togari uses a different touchscreen compared to other rhine devices */ +/delete-node/ &touchscreen; + +/ { + model = "Sony Xperia Z Ultra"; + compatible = "sony,xperia-togari", "qcom,msm8974"; + chassis-type = "handset"; +}; + +&pm8941_l23 { + regulator-min-microvolt = <2600000>; + regulator-max-microvolt = <2600000>; +}; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine.dtsi b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine.dtsi index d34659ebac22..d7322fc6a095 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine.dtsi +++ b/arch/arm/boot/dts/qcom/qcom-msm8974-sony-xperia-rhine.dtsi @@ -8,6 +8,8 @@ / { aliases { + mmc0 = &sdhc_1; + mmc1 = &sdhc_2; serial0 = &blsp1_uart2; }; @@ -28,20 +30,6 @@ key-volume-down { linux,code = ; }; - key-camera-snapshot { - label = "camera_snapshot"; - gpios = <&pm8941_gpios 3 GPIO_ACTIVE_LOW>; - linux,input-type = <1>; - linux,code = ; - }; - - key-camera-focus { - label = "camera_focus"; - gpios = <&pm8941_gpios 4 GPIO_ACTIVE_LOW>; - linux,input-type = <1>; - linux,code = ; - }; - key-volume-up { label = "volume_up"; gpios = <&pm8941_gpios 5 GPIO_ACTIVE_LOW>; @@ -98,7 +86,7 @@ &blsp1_i2c2 { status = "okay"; clock-frequency = <355000>; - synaptics@2c { + touchscreen: synaptics@2c { compatible = "syna,rmi4-i2c"; reg = <0x2c>; @@ -446,6 +434,8 @@ &sdhc_2 { }; &smbb { + usb-charge-current-limit = <1800000>; + qcom,fast-charge-safe-current = <1500000>; qcom,fast-charge-current-limit = <1500000>; qcom,dc-current-limit = <1800000>; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974pro-oneplus-bacon.dts b/arch/arm/boot/dts/qcom/qcom-msm8974pro-oneplus-bacon.dts index 4c8edadea0ac..88ff6535477b 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974pro-oneplus-bacon.dts +++ b/arch/arm/boot/dts/qcom/qcom-msm8974pro-oneplus-bacon.dts @@ -13,6 +13,7 @@ / { qcom,board-id = <8 0>; aliases { + mmc0 = &sdhc_1; serial0 = &blsp1_uart2; }; diff --git a/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts b/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts index 2de047393652..3258b2e27434 100644 --- a/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts +++ b/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -86,7 +87,66 @@ switch-8 { debounce-interval = <20>; gpios = <&pca9698 15 GPIO_ACTIVE_LOW>; }; + }; + leds { + compatible = "gpio-leds"; + + led-dbg0 { + gpios = <&pca9698 0 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <0>; + }; + + led-dbg1 { + gpios = <&pca9698 1 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + }; + + led-dbg2 { + gpios = <&pca9698 2 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + }; + + led-dbg3 { + gpios = <&pca9698 3 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <3>; + }; + + led-dbg4 { + gpios = <&pca9698 4 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <4>; + }; + + led-dbg5 { + gpios = <&pca9698 5 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <5>; + }; + + led-dbg6 { + gpios = <&pca9698 6 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <6>; + }; + + led-dbg7 { + gpios = <&pca9698 7 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <7>; + }; }; }; @@ -111,6 +171,10 @@ ð_miic { renesas,miic-switch-portin = ; }; +&ext_rtc_clk { + clock-frequency = <32768>; +}; + &gmac2 { status = "okay"; phy-mode = "gmii"; diff --git a/arch/arm/boot/dts/renesas/r9a06g032.dtsi b/arch/arm/boot/dts/renesas/r9a06g032.dtsi index 80ad1fdc77a0..13a60656b044 100644 --- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi +++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi @@ -73,8 +73,8 @@ rtc0: rtc@40006000 { , ; interrupt-names = "alarm", "timer", "pps"; - clocks = <&sysctrl R9A06G032_HCLK_RTC>; - clock-names = "hclk"; + clocks = <&sysctrl R9A06G032_HCLK_RTC>, <&ext_rtc_clk>; + clock-names = "hclk", "xtal"; power-domains = <&sysctrl>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts b/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts index 21f824b09191..decbf2726ec4 100644 --- a/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts +++ b/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts @@ -272,7 +272,7 @@ &gmac { phy-mode = "rmii"; phy-handle = <&phy0>; assigned-clocks = <&cru SCLK_MAC_SRC>; - assigned-clock-rates= <50000000>; + assigned-clock-rates = <50000000>; pinctrl-names = "default"; pinctrl-0 = <&rmii_pins>; status = "okay"; diff --git a/arch/arm/boot/dts/rockchip/rv1109-relfor-saib.dts b/arch/arm/boot/dts/rockchip/rv1109-relfor-saib.dts index c13829d32c32..8a92700349b4 100644 --- a/arch/arm/boot/dts/rockchip/rv1109-relfor-saib.dts +++ b/arch/arm/boot/dts/rockchip/rv1109-relfor-saib.dts @@ -250,9 +250,9 @@ rtc0: rtc@52 { &i2s0 { /delete-property/ pinctrl-0; rockchip,trcm-sync-rx-only; - pinctrl-0 = <&i2s0m0_sclk_rx>, - <&i2s0m0_lrck_rx>, - <&i2s0m0_sdi0>; + pinctrl-0 = <&i2s0m0_sclk_rx>, + <&i2s0m0_lrck_rx>, + <&i2s0m0_sdi0>; pinctrl-names = "default"; status = "okay"; }; diff --git a/arch/arm/boot/dts/samsung/exynos3250-monk.dts b/arch/arm/boot/dts/samsung/exynos3250-monk.dts index 2de877d4ccc5..68236c7297d7 100644 --- a/arch/arm/boot/dts/samsung/exynos3250-monk.dts +++ b/arch/arm/boot/dts/samsung/exynos3250-monk.dts @@ -56,7 +56,7 @@ vemmc_reg: voltage-regulator-0 { enable-active-high; }; - i2c_max77836: i2c-gpio-0 { + i2c_max77836: i2c-8 { compatible = "i2c-gpio"; sda-gpios = <&gpd0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpd0 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/exynos3250-rinato.dts b/arch/arm/boot/dts/samsung/exynos3250-rinato.dts index 88fb3e68ff02..36d2171c1ce8 100644 --- a/arch/arm/boot/dts/samsung/exynos3250-rinato.dts +++ b/arch/arm/boot/dts/samsung/exynos3250-rinato.dts @@ -58,7 +58,7 @@ wlan_pwrseq: mshc1-pwrseq { reset-gpios = <&gpe0 4 GPIO_ACTIVE_LOW>; }; - i2c_max77836: i2c-gpio-0 { + i2c_max77836: i2c-8 { compatible = "i2c-gpio"; sda-gpios = <&gpd0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpd0 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/exynos4210-i9100.dts b/arch/arm/boot/dts/samsung/exynos4210-i9100.dts index 0d8495792a70..df229fb8a16b 100644 --- a/arch/arm/boot/dts/samsung/exynos4210-i9100.dts +++ b/arch/arm/boot/dts/samsung/exynos4210-i9100.dts @@ -130,7 +130,7 @@ wlan_pwrseq: sdhci3-pwrseq { reset-gpios = <&gpl1 2 GPIO_ACTIVE_LOW>; }; - i2c_max17042_fuel: i2c-gpio-0 { + i2c_max17042_fuel: i2c-9 { compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; @@ -154,7 +154,7 @@ battery@36 { }; }; - i2c_s5k5baf: i2c-gpio-1 { + i2c_s5k5baf: i2c-10 { compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; @@ -184,7 +184,7 @@ s5k5bafx_ep: endpoint { }; }; - i2c-gpio-2 { + i2c-11 { compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi index 70e3091062f9..12b7f252b24d 100644 --- a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi +++ b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi @@ -123,7 +123,7 @@ led-touchkeys { color = ; }; - i2c_max77693: i2c-gpio-1 { + i2c_max77693: i2c-9 { compatible = "i2c-gpio"; sda-gpios = <&gpm2 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpm2 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -169,7 +169,7 @@ charger { }; }; - i2c_max77693_fuel: i2c-gpio-2 { + i2c_max77693_fuel: i2c-10 { compatible = "i2c-gpio"; sda-gpios = <&gpy0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpy0 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -190,7 +190,7 @@ fuel-gauge@36 { }; }; - i2c_magnetometer: i2c-gpio-3 { + i2c_magnetometer: i2c-11 { compatible = "i2c-gpio"; sda-gpios = <&gpy2 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpy2 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -208,7 +208,7 @@ magnetometer@2e { }; }; - i2c_lightsensor: i2c-gpio-4 { + i2c_lightsensor: i2c-12 { compatible = "i2c-gpio"; sda-gpios = <&gpl0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpl0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -220,7 +220,7 @@ i2c_lightsensor: i2c-gpio-4 { /* WiFi model uses CM3323, 3G/LTE use CM36653 */ }; - i2c_bl: i2c-gpio-5 { + i2c_bl: i2c-13 { compatible = "i2c-gpio"; sda-gpios = <&gpm4 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpm4 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/exynos4412-galaxy-s3.dtsi b/arch/arm/boot/dts/samsung/exynos4412-galaxy-s3.dtsi index 54e1a57ae886..3248be990059 100644 --- a/arch/arm/boot/dts/samsung/exynos4412-galaxy-s3.dtsi +++ b/arch/arm/boot/dts/samsung/exynos4412-galaxy-s3.dtsi @@ -53,7 +53,7 @@ ps_als_reg: voltage-regulator-11 { enable-active-high; }; - i2c_ak8975: i2c-gpio-0 { + i2c_ak8975: i2c-13 { compatible = "i2c-gpio"; sda-gpios = <&gpy2 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpy2 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -68,7 +68,7 @@ magnetometer@c { }; }; - i2c_cm36651: i2c-gpio-2 { + i2c_cm36651: i2c-14 { compatible = "i2c-gpio"; sda-gpios = <&gpf0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpf0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/exynos4412-midas.dtsi b/arch/arm/boot/dts/samsung/exynos4412-midas.dtsi index 3d5aace668dc..05ddddb565ee 100644 --- a/arch/arm/boot/dts/samsung/exynos4412-midas.dtsi +++ b/arch/arm/boot/dts/samsung/exynos4412-midas.dtsi @@ -166,7 +166,7 @@ key-ok { }; }; - i2c_max77693: i2c-gpio-1 { + i2c_max77693: i2c-9 { compatible = "i2c-gpio"; sda-gpios = <&gpm2 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpm2 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -246,7 +246,7 @@ charger { }; }; - i2c_max77693_fuel: i2c-gpio-3 { + i2c_max77693_fuel: i2c-10 { compatible = "i2c-gpio"; sda-gpios = <&gpf1 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpf1 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -267,7 +267,7 @@ fuel-gauge@36 { }; }; - i2c-gpio-4 { + i2c-11 { compatible = "i2c-gpio"; sda-gpios = <&gpl0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpl0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -286,7 +286,7 @@ touchkey@20 { }; }; - i2c-mhl { + i2c-12 { compatible = "i2c-gpio"; sda-gpios = <&gpf0 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpf0 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/exynos4412-p4note.dtsi b/arch/arm/boot/dts/samsung/exynos4412-p4note.dtsi index 28a605802733..8d52aa13b862 100644 --- a/arch/arm/boot/dts/samsung/exynos4412-p4note.dtsi +++ b/arch/arm/boot/dts/samsung/exynos4412-p4note.dtsi @@ -140,7 +140,7 @@ battery_cell: battery-cell { constant-charge-voltage-max-microvolt = <4200000>; }; - i2c-gpio-1 { + i2c-9 { compatible = "i2c-gpio"; sda-gpios = <&gpy2 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpy2 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -158,7 +158,7 @@ magnetometer@c { }; }; - i2c-gpio-2 { + i2c-10 { compatible = "i2c-gpio"; sda-gpios = <&gpy0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpy0 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -179,7 +179,7 @@ fuel-gauge@36 { }; }; - i2c-gpio-3 { + i2c-11 { compatible = "i2c-gpio"; sda-gpios = <&gpm4 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpm4 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -207,7 +207,7 @@ adc { }; }; - i2c-gpio-4 { + i2c-12 { compatible = "i2c-gpio"; sda-gpios = <&gpm2 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpm2 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/s5pv210-aquila.dts b/arch/arm/boot/dts/samsung/s5pv210-aquila.dts index 0f5c6cd0f3a1..e9ec2cc718e0 100644 --- a/arch/arm/boot/dts/samsung/s5pv210-aquila.dts +++ b/arch/arm/boot/dts/samsung/s5pv210-aquila.dts @@ -62,7 +62,7 @@ bat_reg: regulator-2 { regulator-max-microvolt = <3700000>; }; - i2c_pmic: i2c-pmic { + i2c_pmic: i2c-3 { compatible = "i2c-gpio"; sda-gpios = <&gpj4 0 GPIO_ACTIVE_HIGH>; scl-gpios = <&gpj4 3 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm/boot/dts/samsung/s5pv210-aries.dtsi b/arch/arm/boot/dts/samsung/s5pv210-aries.dtsi index 153514e80c9a..0a1a35f4f7cc 100644 --- a/arch/arm/boot/dts/samsung/s5pv210-aries.dtsi +++ b/arch/arm/boot/dts/samsung/s5pv210-aries.dtsi @@ -102,7 +102,7 @@ wifi_pwrseq: wifi-pwrseq { power-off-delay-us = <500>; }; - i2c_sound: i2c-gpio-0 { + i2c_sound: i2c-3 { compatible = "i2c-gpio"; sda-gpios = <&mp05 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&mp05 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -150,7 +150,7 @@ wm8994: audio-codec@1a { }; }; - i2c_accel: i2c-gpio-1 { + i2c_accel: i2c-4 { compatible = "i2c-gpio"; sda-gpios = <&gpj3 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpj3 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -170,7 +170,7 @@ accelerometer@38 { }; }; - i2c_pmic: i2c-gpio-2 { + i2c_pmic: i2c-5 { compatible = "i2c-gpio"; sda-gpios = <&gpj4 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpj4 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -420,7 +420,7 @@ safe2_sreg: ESAFEOUT2 { }; }; - i2c_musb: i2c-gpio-3 { + i2c_musb: i2c-6 { compatible = "i2c-gpio"; sda-gpios = <&gpj3 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpj3 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -442,7 +442,7 @@ fsa9480: musb@25 { }; }; - i2c_fuel: i2c-gpio-4 { + i2c_fuel: i2c-7 { compatible = "i2c-gpio"; sda-gpios = <&mp05 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&mp05 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -459,7 +459,7 @@ fg: fuelgauge@36 { }; }; - i2c_touchkey: i2c-gpio-5 { + i2c_touchkey: i2c-8 { compatible = "i2c-gpio"; sda-gpios = <&gpj3 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpj3 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -485,7 +485,7 @@ touchkey@20 { }; }; - i2c_prox: i2c-gpio-6 { + i2c_prox: i2c-9 { compatible = "i2c-gpio"; sda-gpios = <&gpg2 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpg0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -513,7 +513,7 @@ light-sensor@44 { }; }; - i2c_magnetometer: i2c-gpio-7 { + i2c_magnetometer: i2c-10 { compatible = "i2c-gpio"; sda-gpios = <&gpj0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpj0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/s5pv210-galaxys.dts b/arch/arm/boot/dts/samsung/s5pv210-galaxys.dts index 879294412381..5863a1300cc1 100644 --- a/arch/arm/boot/dts/samsung/s5pv210-galaxys.dts +++ b/arch/arm/boot/dts/samsung/s5pv210-galaxys.dts @@ -51,7 +51,7 @@ key-home { }; }; - i2c_fmradio: i2c-gpio-8 { + i2c_fmradio: i2c-11 { compatible = "i2c-gpio"; sda-gpios = <&gpd1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpd1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; diff --git a/arch/arm/boot/dts/samsung/s5pv210-goni.dts b/arch/arm/boot/dts/samsung/s5pv210-goni.dts index d32f42dd1bf5..079581f4dfec 100644 --- a/arch/arm/boot/dts/samsung/s5pv210-goni.dts +++ b/arch/arm/boot/dts/samsung/s5pv210-goni.dts @@ -74,7 +74,7 @@ tsp_reg: regulator-3 { enable-active-high; }; - i2c_pmic: i2c-pmic { + i2c_pmic: i2c-3 { compatible = "i2c-gpio"; sda-gpios = <&gpj4 0 GPIO_ACTIVE_HIGH>; scl-gpios = <&gpj4 3 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm/boot/dts/st/Makefile b/arch/arm/boot/dts/st/Makefile index cc9948b9870f..66d4f96da5dd 100644 --- a/arch/arm/boot/dts/st/Makefile +++ b/arch/arm/boot/dts/st/Makefile @@ -72,7 +72,8 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32mp157c-odyssey.dtb \ stm32mp157c-osd32mp1-red.dtb \ stm32mp157c-phycore-stm32mp1-3.dtb \ - stm32mp157c-ultra-fly-sbc.dtb + stm32mp157c-ultra-fly-sbc.dtb \ + stm32mp157f-dk2.dtb dtb-$(CONFIG_ARCH_U8500) += \ ste-snowball.dtb \ ste-hrefprev60-stuib.dtb \ diff --git a/arch/arm/boot/dts/st/spear1310-evb.dts b/arch/arm/boot/dts/st/spear1310-evb.dts index 089bd7db55c7..417a064db11e 100644 --- a/arch/arm/boot/dts/st/spear1310-evb.dts +++ b/arch/arm/boot/dts/st/spear1310-evb.dts @@ -159,7 +159,7 @@ button@1 { }; }; - gmac0: eth@e2000000 { + gmac0: ethernet@e2000000 { phy-mode = "gmii"; status = "okay"; }; diff --git a/arch/arm/boot/dts/st/spear1310.dtsi b/arch/arm/boot/dts/st/spear1310.dtsi index ba827d60bf07..1498996be14e 100644 --- a/arch/arm/boot/dts/st/spear1310.dtsi +++ b/arch/arm/boot/dts/st/spear1310.dtsi @@ -128,7 +128,7 @@ pcie2: pcie@b4000000 { status = "disabled"; }; - gmac1: eth@5c400000 { + gmac1: ethernet@5c400000 { compatible = "st,spear600-gmac"; reg = <0x5c400000 0x8000>; interrupts = <0 95 0x4>; @@ -137,7 +137,7 @@ gmac1: eth@5c400000 { status = "disabled"; }; - gmac2: eth@5c500000 { + gmac2: ethernet@5c500000 { compatible = "st,spear600-gmac"; reg = <0x5c500000 0x8000>; interrupts = <0 96 0x4>; @@ -146,7 +146,7 @@ gmac2: eth@5c500000 { status = "disabled"; }; - gmac3: eth@5c600000 { + gmac3: ethernet@5c600000 { compatible = "st,spear600-gmac"; reg = <0x5c600000 0x8000>; interrupts = <0 97 0x4>; @@ -155,7 +155,7 @@ gmac3: eth@5c600000 { status = "disabled"; }; - gmac4: eth@5c700000 { + gmac4: ethernet@5c700000 { compatible = "st,spear600-gmac"; reg = <0x5c700000 0x8000>; interrupts = <0 98 0x4>; diff --git a/arch/arm/boot/dts/st/spear1340-evb.dts b/arch/arm/boot/dts/st/spear1340-evb.dts index d24146c3c9e8..9e7c356b1d9e 100644 --- a/arch/arm/boot/dts/st/spear1340-evb.dts +++ b/arch/arm/boot/dts/st/spear1340-evb.dts @@ -157,7 +157,7 @@ partition@1200000 { }; }; - gmac0: eth@e2000000 { + gmac0: ethernet@e2000000 { phy-mode = "rgmii"; status = "okay"; }; diff --git a/arch/arm/boot/dts/st/spear13xx.dtsi b/arch/arm/boot/dts/st/spear13xx.dtsi index 76749992394d..159e941708ca 100644 --- a/arch/arm/boot/dts/st/spear13xx.dtsi +++ b/arch/arm/boot/dts/st/spear13xx.dtsi @@ -149,7 +149,7 @@ fsmc: flash@b0000000 { status = "disabled"; }; - gmac0: eth@e2000000 { + gmac0: ethernet@e2000000 { compatible = "st,spear600-gmac"; reg = <0xe2000000 0x8000>; interrupts = <0 33 0x4>, diff --git a/arch/arm/boot/dts/st/spear300-evb.dts b/arch/arm/boot/dts/st/spear300-evb.dts index 7d4e6412d558..80fae76d4610 100644 --- a/arch/arm/boot/dts/st/spear300-evb.dts +++ b/arch/arm/boot/dts/st/spear300-evb.dts @@ -69,7 +69,7 @@ fsmc: flash@94000000 { status = "okay"; }; - gmac: eth@e0800000 { + gmac: ethernet@e0800000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/st/spear310-evb.dts b/arch/arm/boot/dts/st/spear310-evb.dts index 459182210825..a3449eb7e59b 100644 --- a/arch/arm/boot/dts/st/spear310-evb.dts +++ b/arch/arm/boot/dts/st/spear310-evb.dts @@ -88,7 +88,7 @@ fsmc: flash@44000000 { status = "okay"; }; - gmac: eth@e0800000 { + gmac: ethernet@e0800000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/st/spear320-evb.dts b/arch/arm/boot/dts/st/spear320-evb.dts index 6ac53d993cf3..984075e60634 100644 --- a/arch/arm/boot/dts/st/spear320-evb.dts +++ b/arch/arm/boot/dts/st/spear320-evb.dts @@ -84,7 +84,7 @@ fsmc: flash@4c000000 { status = "okay"; }; - gmac: eth@e0800000 { + gmac: ethernet@e0800000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/st/spear3xx.dtsi b/arch/arm/boot/dts/st/spear3xx.dtsi index f54bb80ba28a..54e87ac98164 100644 --- a/arch/arm/boot/dts/st/spear3xx.dtsi +++ b/arch/arm/boot/dts/st/spear3xx.dtsi @@ -46,7 +46,7 @@ dma@fc400000 { status = "disabled"; }; - gmac: eth@e0800000 { + gmac: ethernet@e0800000 { compatible = "snps,dwmac-3.40a"; reg = <0xe0800000 0x8000>; interrupts = <23 22>; diff --git a/arch/arm/boot/dts/st/stm32mp131.dtsi b/arch/arm/boot/dts/st/stm32mp131.dtsi index 492bcf586361..ace9495b9b06 100644 --- a/arch/arm/boot/dts/st/stm32mp131.dtsi +++ b/arch/arm/boot/dts/st/stm32mp131.dtsi @@ -1614,6 +1614,8 @@ ethernet1: ethernet@5800a000 { snps,axi-config = <&stmmac_axi_config_1>; snps,tso; access-controllers = <&etzpc 48>; + nvmem-cells = <ðernet_mac1_address>; + nvmem-cell-names = "mac-address"; status = "disabled"; stmmac_axi_config_1: stmmac-axi-config { diff --git a/arch/arm/boot/dts/st/stm32mp133.dtsi b/arch/arm/boot/dts/st/stm32mp133.dtsi index e48838374f0d..49583137b597 100644 --- a/arch/arm/boot/dts/st/stm32mp133.dtsi +++ b/arch/arm/boot/dts/st/stm32mp133.dtsi @@ -93,6 +93,8 @@ ethernet2: ethernet@5800e000 { snps,axi-config = <&stmmac_axi_config_2>; snps,tso; access-controllers = <&etzpc 49>; + nvmem-cells = <ðernet_mac2_address>; + nvmem-cell-names = "mac-address"; status = "disabled"; stmmac_axi_config_2: stmmac-axi-config { diff --git a/arch/arm/boot/dts/st/stm32mp15-scmi.dtsi b/arch/arm/boot/dts/st/stm32mp15-scmi.dtsi index dc3b09f2f2af..98552fe45d4e 100644 --- a/arch/arm/boot/dts/st/stm32mp15-scmi.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15-scmi.dtsi @@ -4,11 +4,15 @@ * Author: Alexandre Torgue for STMicroelectronics. */ +#include + / { firmware { optee: optee { compatible = "linaro,optee-tz"; method = "smc"; + interrupt-parent = <&intc>; + interrupts = ; }; scmi: scmi { @@ -35,21 +39,21 @@ scmi_reguls: regulators { #size-cells = <0>; scmi_reg11: regulator@0 { - reg = <0>; + reg = ; regulator-name = "reg11"; regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1100000>; }; scmi_reg18: regulator@1 { - reg = <1>; + reg = ; regulator-name = "reg18"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; scmi_usb33: regulator@2 { - reg = <2>; + reg = ; regulator-name = "usb33"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; diff --git a/arch/arm/boot/dts/st/stm32mp157f-dk2-scmi.dtsi b/arch/arm/boot/dts/st/stm32mp157f-dk2-scmi.dtsi new file mode 100644 index 000000000000..89de85a2eff3 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp157f-dk2-scmi.dtsi @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2025 - All Rights Reserved + * Author: Amelie Delaunay for STMicroelectronics. + */ + +#include "stm32mp15-scmi.dtsi" + +/ { + reserved-memory { + optee@de000000 { + reg = <0xde000000 0x2000000>; + no-map; + }; + }; + + arm_wdt: watchdog { + compatible = "arm,smc-wdt"; + arm,smc-id = <0xbc000000>; + status = "disabled"; + }; + +}; + +&adc { + vdd-supply = <&scmi_vdd>; + vdda-supply = <&scmi_vdd>; +}; + +&cpu0 { + clocks = <&scmi_clk CK_SCMI_MPU>; +}; + +&cpu1 { + clocks = <&scmi_clk CK_SCMI_MPU>; +}; + +&cryp1 { + clocks = <&scmi_clk CK_SCMI_CRYP1>; + resets = <&scmi_reset RST_SCMI_CRYP1>; +}; + +&cs42l51 { + VL-supply = <&scmi_v3v3>; + VD-supply = <&scmi_v1v8_audio>; + VA-supply = <&scmi_v1v8_audio>; + VAHP-supply = <&scmi_v1v8_audio>; +}; + +&dsi { + phy-dsi-supply = <&scmi_reg18>; + clocks = <&rcc DSI>, <&scmi_clk CK_SCMI_HSE>, <&rcc DSI_PX>; +}; + +&gpioz { + clocks = <&scmi_clk CK_SCMI_GPIOZ>; +}; + +&hash1 { + clocks = <&scmi_clk CK_SCMI_HASH1>; + resets = <&scmi_reset RST_SCMI_HASH1>; +}; + +&i2c1 { + hdmi-transmitter@39 { + iovcc-supply = <&scmi_v3v3_hdmi>; + cvcc12-supply = <&scmi_v1v2_hdmi>; + }; +}; + +&iwdg2 { + clocks = <&rcc IWDG2>, <&scmi_clk CK_SCMI_LSI>; + status = "disabled"; +}; + +&m4_rproc { + /delete-property/ st,syscfg-holdboot; + resets = <&scmi_reset RST_SCMI_MCU>, + <&scmi_reset RST_SCMI_MCU_HOLD_BOOT>; + reset-names = "mcu_rst", "hold_boot"; +}; + +&mdma1 { + resets = <&scmi_reset RST_SCMI_MDMA>; +}; + +&optee { + interrupt-parent = <&intc>; + interrupts = ; +}; + +&pwr_regulators { + vdd-supply = <&scmi_vdd>; + vdd_3v3_usbfs-supply = <&scmi_vdd_usb>; + status = "disabled"; +}; + +&rcc { + compatible = "st,stm32mp1-rcc-secure", "syscon"; + clock-names = "hse", "hsi", "csi", "lse", "lsi"; + clocks = <&scmi_clk CK_SCMI_HSE>, + <&scmi_clk CK_SCMI_HSI>, + <&scmi_clk CK_SCMI_CSI>, + <&scmi_clk CK_SCMI_LSE>, + <&scmi_clk CK_SCMI_LSI>; +}; + +&rng1 { + clocks = <&scmi_clk CK_SCMI_RNG1>; + resets = <&scmi_reset RST_SCMI_RNG1>; +}; + +&rtc { + clocks = <&scmi_clk CK_SCMI_RTCAPB>, <&scmi_clk CK_SCMI_RTC>; +}; + +&scmi_reguls { + scmi_vddcore: regulator@3 { + reg = ; + regulator-name = "vddcore"; + }; + + scmi_vdd: regulator@5 { + reg = ; + regulator-name = "vdd"; + }; + + scmi_v3v3: regulator@6 { + reg = ; + regulator-name = "v3v3"; + }; + + scmi_v1v8_audio: regulator@7 { + reg = ; + regulator-name = "v1v8_audio"; + }; + + scmi_v3v3_hdmi: regulator@8 { + reg = ; + regulator-name = "v3v3_hdmi"; + }; + + scmi_vdd_usb: regulator@a { + reg = ; + regulator-name = "vdd_usb"; + }; + + scmi_vdda: regulator@b { + reg = ; + regulator-name = "vdda"; + }; + + scmi_v1v2_hdmi: regulator@c { + reg = ; + regulator-name = "v1v2_hdmi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + scmi_vbus_otg: regulator@f { + reg = ; + regulator-name = "vbus_otg"; + }; + + scmi_vbus_sw: regulator@10 { + reg = ; + regulator-name = "vbus_sw"; + }; +}; + +&sdmmc1 { + vmmc-supply = <&scmi_v3v3>; +}; + +&sdmmc3 { + vmmc-supply = <&scmi_v3v3>; +}; + +&usbh_ehci { + hub@1 { + vdd-supply = <&scmi_v3v3>; + }; +}; + +&usbphyc_port0 { + phy-supply = <&scmi_vdd_usb>; +}; + +&usbphyc_port1 { + phy-supply = <&scmi_vdd_usb>; +}; + +&vrefbuf { + vdda-supply = <&scmi_vdd>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp157f-dk2.dts b/arch/arm/boot/dts/st/stm32mp157f-dk2.dts new file mode 100644 index 000000000000..43375c4d62a3 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp157f-dk2.dts @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2025 - All Rights Reserved + * Author: Amelie Delaunay for STMicroelectronics. + */ + +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xf.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" +#include "stm32mp15xx-dkx.dtsi" +#include "stm32mp157f-dk2-scmi.dtsi" + +/ { + model = "STMicroelectronics STM32MP157F-DK2 Discovery Board"; + compatible = "st,stm32mp157f-dk2", "st,stm32mp157"; + + aliases { + ethernet0 = ðernet0; + serial3 = &usart2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpioh 4 GPIO_ACTIVE_LOW>; + }; +}; + +&arm_wdt { + timeout-sec = <32>; + status = "okay"; +}; + +&cryp1 { + status = "okay"; +}; + +&dsi { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + panel@0 { + compatible = "orisetech,otm8009a"; + reg = <0>; + reset-gpios = <&gpioe 4 GPIO_ACTIVE_LOW>; + power-supply = <&scmi_v3v3>; + status = "okay"; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; +}; + +&dsi_in { + remote-endpoint = <<dc_ep1_out>; +}; + +&dsi_out { + remote-endpoint = <&panel_in>; +}; + +&i2c1 { + touchscreen@38 { + compatible = "focaltech,ft6236"; + reg = <0x38>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpiof>; + touchscreen-size-x = <480>; + touchscreen-size-y = <800>; + status = "okay"; + }; +}; + +/* I2C4 is managed by OP-TEE */ +&i2c4 { + status = "disabled"; + + /* i2c4 subnodes, which won't be managed by Linux */ + typec@28 { + status = "disabled"; + connector { + status = "disabled"; + }; + }; + + stpmic@33 { + status = "disabled"; + }; +}; + +<dc { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + ltdc_ep1_out: endpoint@1 { + reg = <1>; + remote-endpoint = <&dsi_in>; + }; + }; +}; + +&rtc { + pinctrl-names = "default"; + pinctrl-0 = <&rtc_rsvd_pins_a>; + + rtc_lsco_pins_a: rtc-lsco-0 { + pins = "out2_rmp"; + function = "lsco"; + }; +}; + +/* Wifi */ +&sdmmc2 { + pinctrl-names = "default", "opendrain", "sleep"; + pinctrl-0 = <&sdmmc2_b4_pins_a>; + pinctrl-1 = <&sdmmc2_b4_od_pins_a>; + pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>; + non-removable; + cap-sdio-irq; + st,neg-edge; + bus-width = <4>; + vmmc-supply = <&scmi_v3v3>; + mmc-pwrseq = <&wifi_pwrseq>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + pinctrl-names = "default"; + pinctrl-0 = <&rtc_lsco_pins_a>; + }; +}; + +/* Bluetooth */ +&usart2 { + pinctrl-names = "default", "sleep", "idle"; + pinctrl-0 = <&usart2_pins_c>; + pinctrl-1 = <&usart2_sleep_pins_c>; + pinctrl-2 = <&usart2_idle_pins_c>; + uart-has-rtscts; + status = "okay"; + + bluetooth { + shutdown-gpios = <&gpioz 6 GPIO_ACTIVE_HIGH>; + compatible = "brcm,bcm43438-bt"; + max-speed = <3000000>; + vbat-supply = <&scmi_v3v3>; + vddio-supply = <&scmi_v3v3>; + }; +}; + +/* Since I2C4 is disabled, STUSB1600 is also disabled so there is no Type-C support */ +&usbotg_hs { + dr_mode = "peripheral"; + role-switch-default-mode = "peripheral"; + /* + * Forcing dr_mode = "peripheral"/"role-switch-default-mode = "peripheral"; + * will cause the pull-up on D+/D- to be raised as soon as the OTG is configured at runtime, + * regardless of the presence of VBUS. Notice that on self-powered devices like + * stm32mp157f-dk2, this isn't compliant with the USB standard. That's why usbotg_hs is kept + * disabled here. + */ + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xf.dtsi b/arch/arm/boot/dts/st/stm32mp15xf.dtsi new file mode 100644 index 000000000000..ffa55d64bea3 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xf.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2025 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +&etzpc { + cryp1: cryp@54001000 { + compatible = "st,stm32mp1-cryp"; + reg = <0x54001000 0x400>; + interrupts = ; + clocks = <&rcc CRYP1>; + resets = <&rcc CRYP1_R>; + access-controllers = <&etzpc 9>; + status = "disabled"; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi index a5511b1f0ce3..46692d8f566a 100644 --- a/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi @@ -254,7 +254,7 @@ &i2c4 { /delete-property/dmas; /delete-property/dma-names; - stusb1600@28 { + stusb1600: typec@28 { compatible = "st,stusb1600"; reg = <0x28>; interrupts = <11 IRQ_TYPE_LEVEL_LOW>; @@ -515,6 +515,7 @@ sai2a_endpoint: endpoint { remote-endpoint = <&cs42l51_tx_endpoint>; dai-format = "i2s"; mclk-fs = <256>; + system-clock-direction-out; dai-tdm-slot-num = <2>; dai-tdm-slot-width = <32>; }; diff --git a/arch/arm/boot/dts/ti/omap/Makefile b/arch/arm/boot/dts/ti/omap/Makefile index 95c68135dd0c..1aef60eef671 100644 --- a/arch/arm/boot/dts/ti/omap/Makefile +++ b/arch/arm/boot/dts/ti/omap/Makefile @@ -93,6 +93,7 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-boneblue.dtb \ am335x-bonegreen.dtb \ am335x-bonegreen-wireless.dtb \ + am335x-bonegreen-eco.dtb \ am335x-chiliboard.dtb \ am335x-cm-t335.dtb \ am335x-evm.dtb \ diff --git a/arch/arm/boot/dts/ti/omap/am335x-bone-common.dtsi b/arch/arm/boot/dts/ti/omap/am335x-bone-common.dtsi index c400b7b70d0d..ad1e60a9b6fd 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-bone-common.dtsi +++ b/arch/arm/boot/dts/ti/omap/am335x-bone-common.dtsi @@ -212,7 +212,7 @@ &i2c0 { status = "okay"; clock-frequency = <400000>; - tps: tps@24 { + tps: pmic@24 { reg = <0x24>; }; diff --git a/arch/arm/boot/dts/ti/omap/am335x-boneblack.dts b/arch/arm/boot/dts/ti/omap/am335x-boneblack.dts index 16b567e3cb47..b4fdcf9c02b5 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-boneblack.dts +++ b/arch/arm/boot/dts/ti/omap/am335x-boneblack.dts @@ -35,7 +35,7 @@ &gpio0 { "P9_18 [spi0_d1]", "P9_17 [spi0_cs0]", "[mmc0_cd]", - "P8_42A [ecappwm0]", + "P9_42A [ecappwm0]", "P8_35 [lcd d12]", "P8_33 [lcd d13]", "P8_31 [lcd d14]", diff --git a/arch/arm/boot/dts/ti/omap/am335x-bonegreen-eco.dts b/arch/arm/boot/dts/ti/omap/am335x-bonegreen-eco.dts new file mode 100644 index 000000000000..d21118cdb6c2 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/am335x-bonegreen-eco.dts @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Bootlin + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-common.dtsi" +#include + +/ { + model = "Seeed Studio BeagleBone Green Eco"; + compatible = "seeed,am335x-bone-green-eco", "ti,am33xx"; + + cpus { + cpu@0 { + cpu0-supply = <&buck1>; + }; + }; + + sys_5v: regulator-sys-5v { + compatible = "regulator-fixed"; + regulator-name = "sys_5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + v3v3: regulator-v3v3 { + compatible = "regulator-fixed"; + regulator-name = "v3v3"; + regulator-always-on; + }; +}; + +&usb0 { + interrupts-extended = <&intc 18>; + interrupt-names = "mc"; +}; + +&baseboard_eeprom { + vcc-supply = <&v3v3>; +}; + +&i2c0 { + /delete-node/ pmic@24; + + tps65214: pmic@30 { + compatible = "ti,tps65214"; + reg = <0x30>; + buck1-supply = <&sys_5v>; + buck2-supply = <&sys_5v>; + buck3-supply = <&sys_5v>; + ldo1-supply = <&sys_5v>; + ldo2-supply = <&sys_5v>; + + interrupt-parent = <&intc>; + interrupts = <7>; + pinctrl-0 = <&pmic_irq_pins_default>; + + regulators { + buck1: buck1 { + regulator-name = "vdd_mpu"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1298500>; + regulator-boot-on; + regulator-always-on; + }; + + buck2: buck2 { + regulator-name = "vdd_core"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + buck3: buck3 { + regulator-name = "vdds_ddr"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + }; + + ldo1_reg: ldo1 { + regulator-name = "vdd_1v8_1"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: ldo2 { + regulator-name = "vdd_1v8_2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-id"; + phy-handle = <&dp83867_0>; + ti,dual-emac-pvid = <1>; +}; + +&mac_sw { + pinctrl-0 = <&cpsw_b_default>; + pinctrl-1 = <&cpsw_b_sleep>; +}; + +&davinci_mdio_sw { + /delete-node/ ethernet-phy@0; + + dp83867_0: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,min-output-impedance; + ti,dp83867-rxctrl-strap-quirk; + }; +}; + +&am33xx_pinmux { + cpsw_b_default: cpsw-b-default-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE2) + >; + }; + + cpsw_b_sleep: cpsw-b-sleep-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + >; + }; + + pmic_irq_pins_default: pmic-irq-default-pins { + pinctrl-single,pins = < + AM33XX_IOPAD(AM335X_PIN_NNMI, PIN_INPUT_PULLUP | MUX_MODE0) + >; + }; +}; diff --git a/arch/arm/boot/dts/ti/omap/am335x-nano.dts b/arch/arm/boot/dts/ti/omap/am335x-nano.dts index 56929059f5af..d51cdd6e1ab4 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-nano.dts +++ b/arch/arm/boot/dts/ti/omap/am335x-nano.dts @@ -167,7 +167,7 @@ &uart1 { pinctrl-names = "default"; pinctrl-0 = <&uart1_pins>; status = "okay"; - rts-gpio = <&gpio0 13 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; rs485-rts-active-high; rs485-rx-during-tx; rs485-rts-delay = <1 1>; @@ -178,7 +178,7 @@ &uart2 { pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; status = "okay"; - rts-gpio = <&gpio2 15 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>; rs485-rts-active-high; rs485-rts-delay = <1 1>; linux,rs485-enabled-at-boot-time; @@ -187,7 +187,7 @@ &uart2 { &uart3 { pinctrl-names = "default"; pinctrl-0 = <&uart3_pins>; - rts-gpio = <&gpio2 17 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; rs485-rts-active-high; rs485-rx-during-tx; rs485-rts-delay = <1 1>; @@ -198,7 +198,7 @@ &uart3 { &uart4 { pinctrl-names = "default"; pinctrl-0 = <&uart4_pins>; - rts-gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; rs485-rts-active-high; rs485-rx-during-tx; rs485-rts-delay = <1 1>; diff --git a/arch/arm/boot/dts/ti/omap/am335x-pdu001.dts b/arch/arm/boot/dts/ti/omap/am335x-pdu001.dts index ded19e24e666..c9ccb9de21ad 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-pdu001.dts +++ b/arch/arm/boot/dts/ti/omap/am335x-pdu001.dts @@ -256,8 +256,9 @@ &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; - rts-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; rs485-rts-active-high; + rs485-rx-during-tx; rs485-rts-delay = <0 0>; linux,rs485-enabled-at-boot-time; diff --git a/arch/arm/boot/dts/ti/omap/dra7.dtsi b/arch/arm/boot/dts/ti/omap/dra7.dtsi index b709703f6c0d..711ce4c31bb1 100644 --- a/arch/arm/boot/dts/ti/omap/dra7.dtsi +++ b/arch/arm/boot/dts/ti/omap/dra7.dtsi @@ -195,24 +195,22 @@ axi0: target-module@51000000 { clock-names = "fck", "phy-clk", "phy-clk-div"; #size-cells = <1>; #address-cells = <1>; - ranges = <0x51000000 0x51000000 0x3000>, - <0x20000000 0x20000000 0x10000000>; + ranges = <0x51000000 0x51000000 0x3000 + 0x0 0x20000000 0x10000000>; dma-ranges; /** * To enable PCI endpoint mode, disable the pcie1_rc * node and enable pcie1_ep mode. */ pcie1_rc: pcie@51000000 { - reg = <0x51000000 0x2000>, - <0x51002000 0x14c>, - <0x20001000 0x2000>; + reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>; reg-names = "rc_dbics", "ti_conf", "config"; interrupts = <0 232 0x4>, <0 233 0x4>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; - ranges = <0x81000000 0 0x00000000 0x20003000 0 0x00010000>, - <0x82000000 0 0x20013000 0x20013000 0 0x0ffed000>; + ranges = <0x81000000 0 0 0x03000 0 0x00010000 + 0x82000000 0 0x20013000 0x13000 0 0xffed000>; bus-range = <0x00 0xff>; #interrupt-cells = <1>; num-lanes = <1>; @@ -235,10 +233,7 @@ pcie1_intc: interrupt-controller { }; pcie1_ep: pcie_ep@51000000 { - reg = <0x51000000 0x28>, - <0x51002000 0x14c>, - <0x51001000 0x28>, - <0x20001000 0x10000000>; + reg = <0x51000000 0x28>, <0x51002000 0x14c>, <0x51001000 0x28>, <0x1000 0x10000000>; reg-names = "ep_dbics", "ti_conf", "ep_dbics2", "addr_space"; interrupts = <0 232 0x4>; num-lanes = <1>; @@ -269,21 +264,19 @@ axi1: target-module@51800000 { reset-names = "rstctrl"; #size-cells = <1>; #address-cells = <1>; - ranges = <0x51800000 0x51800000 0x3000>, - <0x30000000 0x30000000 0x10000000>; + ranges = <0x51800000 0x51800000 0x3000 + 0x0 0x30000000 0x10000000>; dma-ranges; status = "disabled"; pcie2_rc: pcie@51800000 { - reg = <0x51800000 0x2000>, - <0x51802000 0x14c>, - <0x30001000 0x2000>; + reg = <0x51800000 0x2000>, <0x51802000 0x14c>, <0x1000 0x2000>; reg-names = "rc_dbics", "ti_conf", "config"; interrupts = <0 355 0x4>, <0 356 0x4>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; - ranges = <0x81000000 0 0x00000000 0x30003000 0 0x00010000>, - <0x82000000 0 0x30013000 0x30013000 0 0x0ffed000>; + ranges = <0x81000000 0 0 0x03000 0 0x00010000 + 0x82000000 0 0x30013000 0x13000 0 0xffed000>; bus-range = <0x00 0xff>; #interrupt-cells = <1>; num-lanes = <1>; diff --git a/arch/arm/boot/dts/vt8500/vt8500-bv07.dts b/arch/arm/boot/dts/vt8500/vt8500-bv07.dts index 38a2da5e2c5d..c8c07c2b4acf 100644 --- a/arch/arm/boot/dts/vt8500/vt8500-bv07.dts +++ b/arch/arm/boot/dts/vt8500/vt8500-bv07.dts @@ -10,6 +10,11 @@ / { model = "Benign BV07 Netbook"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; }; &fb { diff --git a/arch/arm/boot/dts/vt8500/vt8500.dtsi b/arch/arm/boot/dts/vt8500/vt8500.dtsi index d1dd37220d41..9b87b1289792 100644 --- a/arch/arm/boot/dts/vt8500/vt8500.dtsi +++ b/arch/arm/boot/dts/vt8500/vt8500.dtsi @@ -11,20 +11,16 @@ / { compatible = "via,vt8500"; cpus { - #address-cells = <0>; + #address-cells = <1>; #size-cells = <0>; - cpu { + cpu@0 { device_type = "cpu"; compatible = "arm,arm926ej-s"; + reg = <0x0>; }; }; - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - aliases { serial0 = &uart0; serial1 = &uart1; @@ -126,7 +122,7 @@ usb@d8007b00 { interrupts = <43>; }; - fb: fb@d8050800 { + fb: lcd-controller@d800e400 { compatible = "via,vt8500-fb"; reg = <0xd800e400 0x400>; interrupts = <12>; diff --git a/arch/arm/boot/dts/vt8500/wm8505-ref.dts b/arch/arm/boot/dts/vt8500/wm8505-ref.dts index 8ce9e2ef0a81..d4ff99c70012 100644 --- a/arch/arm/boot/dts/vt8500/wm8505-ref.dts +++ b/arch/arm/boot/dts/vt8500/wm8505-ref.dts @@ -10,6 +10,11 @@ / { model = "Wondermedia WM8505 Netbook"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; }; &fb { diff --git a/arch/arm/boot/dts/vt8500/wm8505.dtsi b/arch/arm/boot/dts/vt8500/wm8505.dtsi index 2b1819f0c541..915adbf6e1e0 100644 --- a/arch/arm/boot/dts/vt8500/wm8505.dtsi +++ b/arch/arm/boot/dts/vt8500/wm8505.dtsi @@ -11,20 +11,16 @@ / { compatible = "wm,wm8505"; cpus { - #address-cells = <0>; + #address-cells = <1>; #size-cells = <0>; - cpu { + cpu@0 { device_type = "cpu"; compatible = "arm,arm926ej-s"; + reg = <0x0>; }; }; - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - aliases { serial0 = &uart0; serial1 = &uart1; @@ -288,7 +284,7 @@ rtc@d8100000 { interrupts = <48>; }; - sdhc@d800a000 { + mmc@d800a000 { compatible = "wm,wm8505-sdhc"; reg = <0xd800a000 0x400>; interrupts = <20>, <21>; diff --git a/arch/arm/boot/dts/vt8500/wm8650-mid.dts b/arch/arm/boot/dts/vt8500/wm8650-mid.dts index 7977b6c1e8eb..bfc570e80073 100644 --- a/arch/arm/boot/dts/vt8500/wm8650-mid.dts +++ b/arch/arm/boot/dts/vt8500/wm8650-mid.dts @@ -10,6 +10,11 @@ / { model = "Wondermedia WM8650-MID Tablet"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x10000000>; + }; }; &fb { diff --git a/arch/arm/boot/dts/vt8500/wm8650.dtsi b/arch/arm/boot/dts/vt8500/wm8650.dtsi index 042eec78c085..82eef7504364 100644 --- a/arch/arm/boot/dts/vt8500/wm8650.dtsi +++ b/arch/arm/boot/dts/vt8500/wm8650.dtsi @@ -11,20 +11,16 @@ / { compatible = "wm,wm8650"; cpus { - #address-cells = <0>; + #address-cells = <1>; #size-cells = <0>; - cpu { + cpu@0 { device_type = "cpu"; compatible = "arm,arm926ej-s"; + reg = <0x0>; }; }; - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - aliases { serial0 = &uart0; serial1 = &uart1; @@ -196,7 +192,7 @@ usb@d8007b00 { interrupts = <43>; }; - sdhc@d800a000 { + mmc@d800a000 { compatible = "wm,wm8505-sdhc"; reg = <0xd800a000 0x400>; interrupts = <20>, <21>; diff --git a/arch/arm/boot/dts/vt8500/wm8750-apc8750.dts b/arch/arm/boot/dts/vt8500/wm8750-apc8750.dts index 136e812bc1e4..72d633bedff0 100644 --- a/arch/arm/boot/dts/vt8500/wm8750-apc8750.dts +++ b/arch/arm/boot/dts/vt8500/wm8750-apc8750.dts @@ -11,6 +11,11 @@ / { model = "VIA APC8750"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x20000000>; + }; }; &pinctrl { diff --git a/arch/arm/boot/dts/vt8500/wm8750.dtsi b/arch/arm/boot/dts/vt8500/wm8750.dtsi index 56342aa1d993..5342b7fe4ef8 100644 --- a/arch/arm/boot/dts/vt8500/wm8750.dtsi +++ b/arch/arm/boot/dts/vt8500/wm8750.dtsi @@ -11,20 +11,16 @@ / { compatible = "wm,wm8750"; cpus { - #address-cells = <0>; + #address-cells = <1>; #size-cells = <0>; - cpu { + cpu@0 { device_type = "cpu"; compatible = "arm,arm1176jzf"; + reg = <0x0>; }; }; - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - aliases { serial0 = &uart0; serial1 = &uart1; @@ -328,7 +324,7 @@ rtc@d8100000 { interrupts = <48>; }; - sdhc@d800a000 { + mmc@d800a000 { compatible = "wm,wm8505-sdhc"; reg = <0xd800a000 0x1000>; interrupts = <20 21>; diff --git a/arch/arm/boot/dts/vt8500/wm8850-w70v2.dts b/arch/arm/boot/dts/vt8500/wm8850-w70v2.dts index 5d409323b10c..eb16991a2ccc 100644 --- a/arch/arm/boot/dts/vt8500/wm8850-w70v2.dts +++ b/arch/arm/boot/dts/vt8500/wm8850-w70v2.dts @@ -22,6 +22,11 @@ backlight { brightness-levels = <0 40 60 80 100 130 190 255>; default-brightness-level = <5>; }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x20000000>; + }; }; &fb { diff --git a/arch/arm/boot/dts/vt8500/wm8850.dtsi b/arch/arm/boot/dts/vt8500/wm8850.dtsi index 03e72f28d31b..58109aa05f74 100644 --- a/arch/arm/boot/dts/vt8500/wm8850.dtsi +++ b/arch/arm/boot/dts/vt8500/wm8850.dtsi @@ -18,14 +18,10 @@ cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a9"; reg = <0x0>; + next-level-cache = <&l2_cache>; }; }; - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - aliases { serial0 = &uart0; serial1 = &uart1; @@ -299,7 +295,7 @@ rtc@d8100000 { interrupts = <48>; }; - sdhc@d800a000 { + mmc@d800a000 { compatible = "wm,wm8505-sdhc"; reg = <0xd800a000 0x1000>; interrupts = <20 21>; @@ -313,5 +309,18 @@ ethernet@d8004000 { reg = <0xd8004000 0x100>; interrupts = <10>; }; + + l2_cache: cache-controller@d9000000 { + compatible = "arm,pl310-cache"; + reg = <0xd9000000 0x1000>; + arm,double-linefill = <1>; + arm,dynamic-clock-gating = <1>; + arm,shared-override; + arm,standby-mode = <1>; + cache-level = <2>; + cache-unified; + prefetch-data = <1>; + prefetch-instr = <1>; + }; }; }; diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index 86b271cc29e1..3389a70e4d49 100644 --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -578,8 +578,8 @@ static int sa1111_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) return 0; } -static void sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct sa1111 *sachip = gc_to_sa1111(gc); unsigned long flags; @@ -597,6 +597,8 @@ static void sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, sa1111_gpio_modify(reg + SA1111_GPIO_PCDWR, (msk >> 12) & 255, val >> 12); sa1111_gpio_modify(reg + SA1111_GPIO_PCSSR, (msk >> 12) & 255, val >> 12); spin_unlock_irqrestore(&sachip->lock, flags); + + return 0; } static int sa1111_gpio_to_irq(struct gpio_chip *gc, unsigned offset) @@ -615,7 +617,7 @@ static int sa1111_setup_gpios(struct sa1111 *sachip) sachip->gc.direction_input = sa1111_gpio_direction_input; sachip->gc.direction_output = sa1111_gpio_direction_output; sachip->gc.get = sa1111_gpio_get; - sachip->gc.set_rv = sa1111_gpio_set; + sachip->gc.set = sa1111_gpio_set; sachip->gc.set_multiple = sa1111_gpio_set_multiple; sachip->gc.to_irq = sa1111_gpio_to_irq; sachip->gc.base = -1; diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c index 2d3ee76c8e17..dddb73c96826 100644 --- a/arch/arm/common/scoop.c +++ b/arch/arm/common/scoop.c @@ -218,7 +218,7 @@ static int scoop_probe(struct platform_device *pdev) devptr->gpio.label = dev_name(&pdev->dev); devptr->gpio.base = inf->gpio_base; devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */ - devptr->gpio.set_rv = scoop_gpio_set; + devptr->gpio.set = scoop_gpio_set; devptr->gpio.get = scoop_gpio_get; devptr->gpio.direction_input = scoop_gpio_direction_input; devptr->gpio.direction_output = scoop_gpio_direction_output; diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index f71af368674c..6915c766923a 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -363,8 +363,6 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m -CONFIG_CRYPTO_SHA1_ARM_NEON=m -CONFIG_CRYPTO_SHA512_ARM=m CONFIG_CRYPTO_AES_ARM_BS=m CONFIG_CRYPTO_CHACHA20_NEON=m CONFIG_CRYPTO_DEV_EXYNOS_RNG=y diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 062c1eb8dd60..9a57763a8d38 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -12,6 +12,7 @@ CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y CONFIG_ARCH_MULTI_V6=y CONFIG_ARCH_MXC=y CONFIG_SOC_IMX31=y @@ -32,7 +33,6 @@ CONFIG_ARM_PSCI=y CONFIG_HIGHMEM=y CONFIG_ARCH_FORCE_MAX_ORDER=13 CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" -CONFIG_KEXEC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y @@ -68,6 +68,7 @@ CONFIG_BT=y CONFIG_BT_BNEP=m CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_BCM=y CONFIG_BT_NXPUART=m CONFIG_CFG80211=y CONFIG_CFG80211_WEXT=y @@ -129,7 +130,6 @@ CONFIG_CS89x0_PLATFORM=y CONFIG_QCA7000_SPI=m # CONFIG_NET_VENDOR_SEEQ is not set CONFIG_SMC91X=y -CONFIG_SMC911X=y CONFIG_SMSC911X=y # CONFIG_NET_VENDOR_STMICRO is not set CONFIG_MICREL_PHY=y @@ -153,9 +153,7 @@ CONFIG_MWIFIEX_PCIE=m CONFIG_WL12XX=m CONFIG_WL18XX=m CONFIG_WLCORE_SDIO=m -# CONFIG_WILINK_PLATFORM_DATA is not set CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_EVBUG=m CONFIG_KEYBOARD_GPIO=y CONFIG_KEYBOARD_SNVS_PWRKEY=y CONFIG_KEYBOARD_IMX=y @@ -183,6 +181,7 @@ CONFIG_TOUCHSCREEN_COLIBRI_VF50=y CONFIG_INPUT_MISC=y CONFIG_INPUT_MMA8450=y CONFIG_INPUT_GPIO_BEEPER=m +CONFIG_INPUT_PWM_BEEPER=y CONFIG_SERIO_SERPORT=m # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_IMX=y @@ -190,9 +189,7 @@ CONFIG_SERIAL_IMX_CONSOLE=y CONFIG_SERIAL_FSL_LPUART=y CONFIG_SERIAL_FSL_LPUART_CONSOLE=y CONFIG_SERIAL_DEV_BUS=y -# CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y CONFIG_I2C_MUX_GPIO=y # CONFIG_I2C_HELPER_AUTO is not set CONFIG_I2C_ALGOPCF=m @@ -204,14 +201,9 @@ CONFIG_SPI_FSL_QUADSPI=y CONFIG_SPI_GPIO=y CONFIG_SPI_IMX=y CONFIG_SPI_FSL_DSPI=y -CONFIG_PINCTRL_IMX8MM=y -CONFIG_PINCTRL_IMX8MN=y -CONFIG_PINCTRL_IMX8MP=y -CONFIG_PINCTRL_IMX8MQ=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_MXC=y CONFIG_GPIO_SIOX=m -CONFIG_GPIO_VF610=y CONFIG_GPIO_MAX732X=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y @@ -225,7 +217,6 @@ CONFIG_W1_SLAVE_THERM=m CONFIG_POWER_RESET=y CONFIG_POWER_RESET_SYSCON=y CONFIG_POWER_RESET_SYSCON_POWEROFF=y -CONFIG_POWER_SUPPLY=y CONFIG_RN5T618_POWER=m CONFIG_SENSORS_MC13783_ADC=y CONFIG_SENSORS_GPIO_FAN=y @@ -283,13 +274,13 @@ CONFIG_VIDEO_OV5645=m CONFIG_VIDEO_ADV7180=m CONFIG_IMX_IPUV3_CORE=y CONFIG_DRM=y -CONFIG_DRM_I2C_NXP_TDA998X=y CONFIG_DRM_MSM=y CONFIG_DRM_PANEL_LVDS=y -CONFIG_DRM_PANEL_SIMPLE=y -CONFIG_DRM_PANEL_EDP=y CONFIG_DRM_PANEL_SEIKO_43WVF1G=y +CONFIG_DRM_PANEL_EDP=y +CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_I2C_NXP_TDA998X=y CONFIG_DRM_LVDS_CODEC=m CONFIG_DRM_SII902X=y CONFIG_DRM_TI_TFP410=y @@ -310,7 +301,6 @@ CONFIG_LCD_PLATFORM=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_PWM=y CONFIG_BACKLIGHT_GPIO=y -CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y @@ -346,6 +336,7 @@ CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_TEST=m CONFIG_USB_EHSET_TEST_FIXTURE=m +CONFIG_USB_HSIC_USB3503=y CONFIG_USB_ONBOARD_DEV=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_MXS_PHY=y @@ -380,11 +371,8 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_ESDHC_IMX=y -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y CONFIG_LEDS_GPIO=y CONFIG_LEDS_PWM=y -CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y @@ -453,7 +441,6 @@ CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=y CONFIG_ISO9660_FS=m @@ -490,5 +477,4 @@ CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y # CONFIG_SLUB_DEBUG is not set -# CONFIG_SCHED_DEBUG is not set # CONFIG_FTRACE is not set diff --git a/arch/arm/configs/milbeaut_m10v_defconfig b/arch/arm/configs/milbeaut_m10v_defconfig index 242e7d5a3f68..a3be0b2ede09 100644 --- a/arch/arm/configs/milbeaut_m10v_defconfig +++ b/arch/arm/configs/milbeaut_m10v_defconfig @@ -98,9 +98,6 @@ CONFIG_CRYPTO_SELFTESTS=y CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_SEQIV=m CONFIG_CRYPTO_GHASH_ARM_CE=m -CONFIG_CRYPTO_SHA1_ARM_NEON=m -CONFIG_CRYPTO_SHA1_ARM_CE=m -CONFIG_CRYPTO_SHA512_ARM=m CONFIG_CRYPTO_AES_ARM=m CONFIG_CRYPTO_AES_ARM_BS=m CONFIG_CRYPTO_AES_ARM_CE=m diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 50c170b4619f..f2822eeefb95 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -344,6 +344,7 @@ CONFIG_INPUT_MAX77693_HAPTIC=m CONFIG_INPUT_MAX8997_HAPTIC=m CONFIG_INPUT_GPIO_DECODER=m CONFIG_INPUT_CPCAP_PWRBUTTON=m +CONFIG_INPUT_TPS65219_PWRBUTTON=m CONFIG_INPUT_AXP20X_PEK=m CONFIG_INPUT_DA9063_ONKEY=m CONFIG_INPUT_ADXL34X=m @@ -618,6 +619,7 @@ CONFIG_MFD_PALMAS=y CONFIG_MFD_TPS65090=y CONFIG_MFD_TPS65217=y CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS65219=y CONFIG_MFD_TPS6586X=y CONFIG_MFD_TPS65910=y CONFIG_MFD_STM32_LPTIMER=m @@ -667,6 +669,7 @@ CONFIG_REGULATOR_TPS62360=y CONFIG_REGULATOR_TPS65090=y CONFIG_REGULATOR_TPS65217=y CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS65219=y CONFIG_REGULATOR_TPS6586X=y CONFIG_REGULATOR_TPS65910=y CONFIG_REGULATOR_TWL4030=y @@ -792,7 +795,12 @@ CONFIG_SND_HDA_TEGRA=m CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_REALTEK_LIB=m +CONFIG_SND_HDA_CODEC_ALC269=m CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_HDMI_GENERIC=m +CONFIG_SND_HDA_CODEC_HDMI_NVIDIA=m +CONFIG_SND_HDA_CODEC_HDMI_TEGRA=m CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=m CONFIG_SND_ATMEL_SOC=m @@ -1280,9 +1288,6 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_CRYPTO_GHASH_ARM_CE=m -CONFIG_CRYPTO_SHA1_ARM_NEON=m -CONFIG_CRYPTO_SHA1_ARM_CE=m -CONFIG_CRYPTO_SHA512_ARM=m CONFIG_CRYPTO_AES_ARM=m CONFIG_CRYPTO_AES_ARM_BS=m CONFIG_CRYPTO_AES_ARM_CE=m @@ -1298,7 +1303,6 @@ CONFIG_CRYPTO_DEV_MARVELL_CESA=m CONFIG_CRYPTO_DEV_QCE=m CONFIG_CRYPTO_DEV_QCOM_RNG=m CONFIG_CRYPTO_DEV_ROCKCHIP=m -CONFIG_CRYPTO_DEV_STM32_CRC=m CONFIG_CRYPTO_DEV_STM32_HASH=m CONFIG_CRYPTO_DEV_STM32_CRYP=m CONFIG_CMA_SIZE_MBYTES=64 diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig index c76d66135abb..3b08c63b6de4 100644 --- a/arch/arm/configs/mxs_defconfig +++ b/arch/arm/configs/mxs_defconfig @@ -32,9 +32,6 @@ CONFIG_INET=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_SYN_COOKIES=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_DIAG is not set # CONFIG_IPV6 is not set CONFIG_CAN=m @@ -45,7 +42,6 @@ CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_DATAFLASH=y -CONFIG_MTD_M25P80=y CONFIG_MTD_SST25L=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_GPMI_NAND=y @@ -60,7 +56,6 @@ CONFIG_ENC28J60=y CONFIG_ICPLUS_PHY=y CONFIG_MICREL_PHY=y CONFIG_REALTEK_PHY=y -CONFIG_SMSC_PHY=y CONFIG_CAN_FLEXCAN=m CONFIG_USB_USBNET=y CONFIG_USB_NET_SMSC95XX=y @@ -69,21 +64,22 @@ CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=y CONFIG_TOUCHSCREEN_MXS_LRADC=y CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PWM_BEEPER=y # CONFIG_SERIO is not set # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_MXS_AUART=y # CONFIG_HW_RANDOM is not set -# CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y CONFIG_I2C_MXS=y CONFIG_SPI=y CONFIG_SPI_GPIO=m CONFIG_SPI_MXS=y -CONFIG_GPIO_SYSFS=y # CONFIG_HWMON is not set CONFIG_WATCHDOG=y CONFIG_STMP3XXX_RTC_WATCHDOG=y @@ -138,10 +134,6 @@ CONFIG_PWM_MXS=y CONFIG_NVMEM_MXS_OCOTP=y CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set -CONFIG_NETFS_SUPPORT=m -CONFIG_FSCACHE=y -CONFIG_FSCACHE_STATS=y -CONFIG_CACHEFILES=m CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 9f9780c8e62a..939913ed9a73 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -142,7 +142,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m CONFIG_NETFILTER_XT_MATCH_CPU=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m @@ -385,6 +384,7 @@ CONFIG_TOUCHSCREEN_TSC2007=m CONFIG_INPUT_MISC=y CONFIG_INPUT_CPCAP_PWRBUTTON=m CONFIG_INPUT_TPS65218_PWRBUTTON=m +CONFIG_INPUT_TPS65219_PWRBUTTON=m CONFIG_INPUT_TWL4030_PWRBUTTON=m CONFIG_INPUT_UINPUT=m CONFIG_INPUT_PALMAS_PWRBUTTON=m @@ -454,6 +454,7 @@ CONFIG_MFD_TPS65217=y CONFIG_MFD_TI_LP873X=y CONFIG_MFD_TI_LP87565=y CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS65219=y CONFIG_MFD_TPS65910=y CONFIG_TWL6040_CORE=y CONFIG_REGULATOR_CPCAP=y @@ -470,6 +471,7 @@ CONFIG_REGULATOR_TPS65023=y CONFIG_REGULATOR_TPS6507X=y CONFIG_REGULATOR_TPS65217=y CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS65219=y CONFIG_REGULATOR_TPS65910=y CONFIG_REGULATOR_TWL4030=y CONFIG_RC_CORE=m @@ -704,8 +706,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_SECURITY=y CONFIG_CRYPTO_MICHAEL_MIC=y CONFIG_CRYPTO_GHASH_ARM_CE=m -CONFIG_CRYPTO_SHA1_ARM_NEON=m -CONFIG_CRYPTO_SHA512_ARM=m CONFIG_CRYPTO_AES_ARM=m CONFIG_CRYPTO_AES_ARM_BS=m CONFIG_CRYPTO_CHACHA20_NEON=m diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index ff29c5b0e9c9..1a80602c1284 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -658,8 +658,6 @@ CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_SHA1_ARM=m -CONFIG_CRYPTO_SHA512_ARM=m CONFIG_CRYPTO_AES_ARM=m CONFIG_FONTS=y CONFIG_FONT_8x8=y diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig index e447329398d5..2cad045e1d8d 100644 --- a/arch/arm/configs/sama5_defconfig +++ b/arch/arm/configs/sama5_defconfig @@ -95,6 +95,7 @@ CONFIG_LIBERTAS_THINFIRM_USB=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_MWIFIEX_USB=m +CONFIG_WILC1000_SDIO=m CONFIG_RT2X00=m CONFIG_RT2500USB=m CONFIG_RT73USB=m diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index 7c3d6a8f0038..e4cb33b2bcee 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -58,6 +58,7 @@ CONFIG_SH_ETH=y CONFIG_RAVB=y CONFIG_SMSC911X=y CONFIG_STMMAC_ETH=y +# CONFIG_DWMAC_RENESAS_GBETH is not set CONFIG_MICREL_PHY=y CONFIG_SMSC_PHY=y CONFIG_CAN_RCAR=y diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index 3a9bda2bf422..ba863b445417 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -225,7 +225,12 @@ CONFIG_SND_HDA_TEGRA=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_REALTEK_LIB=y +CONFIG_SND_HDA_CODEC_ALC269=y CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_HDMI_GENERIC=y +CONFIG_SND_HDA_CODEC_HDMI_NVIDIA=y +CONFIG_SND_HDA_CODEC_HDMI_TEGRA=y # CONFIG_SND_ARM is not set # CONFIG_SND_SPI is not set # CONFIG_SND_USB is not set diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig index 7efb9a8596e4..1e5f3cdf691c 100644 --- a/arch/arm/crypto/Kconfig +++ b/arch/arm/crypto/Kconfig @@ -62,47 +62,6 @@ config CRYPTO_BLAKE2B_NEON much faster than the SHA-2 family and slightly faster than SHA-1. -config CRYPTO_SHA1_ARM - tristate "Hash functions: SHA-1" - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: arm - -config CRYPTO_SHA1_ARM_NEON - tristate "Hash functions: SHA-1 (NEON)" - depends on KERNEL_MODE_NEON - select CRYPTO_SHA1_ARM - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: arm using - - NEON (Advanced SIMD) extensions - -config CRYPTO_SHA1_ARM_CE - tristate "Hash functions: SHA-1 (ARMv8 Crypto Extensions)" - depends on KERNEL_MODE_NEON - select CRYPTO_SHA1_ARM - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: arm using ARMv8 Crypto Extensions - -config CRYPTO_SHA512_ARM - tristate "Hash functions: SHA-384 and SHA-512 (NEON)" - select CRYPTO_HASH - depends on !CPU_V7M - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: arm using - - NEON (Advanced SIMD) extensions - config CRYPTO_AES_ARM tristate "Ciphers: AES" select CRYPTO_ALGAPI diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index 8479137c6e80..4f23999ae17d 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -5,38 +5,17 @@ obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o -obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o -obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o -obj-$(CONFIG_CRYPTO_SHA512_ARM) += sha512-arm.o obj-$(CONFIG_CRYPTO_BLAKE2B_NEON) += blake2b-neon.o obj-$(CONFIG_CRYPTO_NHPOLY1305_NEON) += nhpoly1305-neon.o obj-$(CONFIG_CRYPTO_CURVE25519_NEON) += curve25519-neon.o obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o -obj-$(CONFIG_CRYPTO_SHA1_ARM_CE) += sha1-arm-ce.o obj-$(CONFIG_CRYPTO_GHASH_ARM_CE) += ghash-arm-ce.o aes-arm-y := aes-cipher-core.o aes-cipher-glue.o aes-arm-bs-y := aes-neonbs-core.o aes-neonbs-glue.o -sha1-arm-y := sha1-armv4-large.o sha1_glue.o -sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o -sha512-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha512-neon-glue.o -sha512-arm-y := sha512-core.o sha512-glue.o $(sha512-arm-neon-y) blake2b-neon-y := blake2b-neon-core.o blake2b-neon-glue.o -sha1-arm-ce-y := sha1-ce-core.o sha1-ce-glue.o aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o ghash-arm-ce-y := ghash-ce-core.o ghash-ce-glue.o nhpoly1305-neon-y := nh-neon-core.o nhpoly1305-neon-glue.o curve25519-neon-y := curve25519-core.o curve25519-glue.o - -quiet_cmd_perl = PERL $@ - cmd_perl = $(PERL) $(<) > $(@) - -$(obj)/%-core.S: $(src)/%-armv4.pl - $(call cmd,perl) - -clean-files += sha512-core.S - -aflags-thumb2-$(CONFIG_THUMB2_KERNEL) := -U__thumb2__ -D__thumb2__=1 - -AFLAGS_sha512-core.o += $(aflags-thumb2-y) diff --git a/arch/arm/crypto/aes-neonbs-glue.c b/arch/arm/crypto/aes-neonbs-glue.c index c60104dc1585..df5afe601e4a 100644 --- a/arch/arm/crypto/aes-neonbs-glue.c +++ b/arch/arm/crypto/aes-neonbs-glue.c @@ -206,7 +206,7 @@ static int ctr_encrypt(struct skcipher_request *req) while (walk.nbytes > 0) { const u8 *src = walk.src.virt.addr; u8 *dst = walk.dst.virt.addr; - int bytes = walk.nbytes; + unsigned int bytes = walk.nbytes; if (unlikely(bytes < AES_BLOCK_SIZE)) src = dst = memcpy(buf + sizeof(buf) - bytes, diff --git a/arch/arm/crypto/sha1-ce-glue.c b/arch/arm/crypto/sha1-ce-glue.c deleted file mode 100644 index fac07a4799de..000000000000 --- a/arch/arm/crypto/sha1-ce-glue.c +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sha1-ce-glue.c - SHA-1 secure hash using ARMv8 Crypto Extensions - * - * Copyright (C) 2015 Linaro Ltd - */ - -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); - -asmlinkage void sha1_ce_transform(struct sha1_state *sst, u8 const *src, - int blocks); - -static int sha1_ce_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - int remain; - - kernel_neon_begin(); - remain = sha1_base_do_update_blocks(desc, data, len, sha1_ce_transform); - kernel_neon_end(); - - return remain; -} - -static int sha1_ce_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - kernel_neon_begin(); - sha1_base_do_finup(desc, data, len, sha1_ce_transform); - kernel_neon_end(); - - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .init = sha1_base_init, - .update = sha1_ce_update, - .finup = sha1_ce_finup, - .descsize = SHA1_STATE_SIZE, - .digestsize = SHA1_DIGEST_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-ce", - .cra_priority = 200, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_ce_mod_init(void) -{ - return crypto_register_shash(&alg); -} - -static void __exit sha1_ce_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_cpu_feature_match(SHA1, sha1_ce_mod_init); -module_exit(sha1_ce_mod_fini); diff --git a/arch/arm/crypto/sha1_glue.c b/arch/arm/crypto/sha1_glue.c deleted file mode 100644 index 255da00c7d98..000000000000 --- a/arch/arm/crypto/sha1_glue.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * Glue code for the SHA1 Secure Hash Algorithm assembler implementation - * - * This file is based on sha1_generic.c and sha1_ssse3_glue.c - * - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - * Copyright (c) Mathias Krause - */ - -#include -#include -#include -#include -#include - -asmlinkage void sha1_block_data_order(struct sha1_state *digest, - const u8 *data, int rounds); - -static int sha1_update_arm(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - /* make sure signature matches sha1_block_fn() */ - BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0); - - return sha1_base_do_update_blocks(desc, data, len, - sha1_block_data_order); -} - -static int sha1_finup_arm(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha1_base_do_finup(desc, data, len, sha1_block_data_order); - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_update_arm, - .finup = sha1_finup_arm, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-asm", - .cra_priority = 150, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - - -static int __init sha1_mod_init(void) -{ - return crypto_register_shash(&alg); -} - - -static void __exit sha1_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - - -module_init(sha1_mod_init); -module_exit(sha1_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm (ARM)"); -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_AUTHOR("David McCullough "); diff --git a/arch/arm/crypto/sha1_neon_glue.c b/arch/arm/crypto/sha1_neon_glue.c deleted file mode 100644 index d321850f22a6..000000000000 --- a/arch/arm/crypto/sha1_neon_glue.c +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Glue code for the SHA1 Secure Hash Algorithm assembler implementation using - * ARM NEON instructions. - * - * Copyright © 2014 Jussi Kivilinna - * - * This file is based on sha1_generic.c and sha1_ssse3_glue.c: - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - * Copyright (c) Mathias Krause - * Copyright (c) Chandramouli Narayanan - */ - -#include -#include -#include -#include -#include -#include - -asmlinkage void sha1_transform_neon(struct sha1_state *state_h, - const u8 *data, int rounds); - -static int sha1_neon_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - int remain; - - kernel_neon_begin(); - remain = sha1_base_do_update_blocks(desc, data, len, - sha1_transform_neon); - kernel_neon_end(); - - return remain; -} - -static int sha1_neon_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - kernel_neon_begin(); - sha1_base_do_finup(desc, data, len, sha1_transform_neon); - kernel_neon_end(); - - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_neon_update, - .finup = sha1_neon_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-neon", - .cra_priority = 250, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_neon_mod_init(void) -{ - if (!cpu_has_neon()) - return -ENODEV; - - return crypto_register_shash(&alg); -} - -static void __exit sha1_neon_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_init(sha1_neon_mod_init); -module_exit(sha1_neon_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, NEON accelerated"); -MODULE_ALIAS_CRYPTO("sha1"); diff --git a/arch/arm/crypto/sha512-glue.c b/arch/arm/crypto/sha512-glue.c deleted file mode 100644 index f8a6480889b1..000000000000 --- a/arch/arm/crypto/sha512-glue.c +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sha512-glue.c - accelerated SHA-384/512 for ARM - * - * Copyright (C) 2015 Linaro Ltd - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "sha512.h" - -MODULE_DESCRIPTION("Accelerated SHA-384/SHA-512 secure hash for ARM"); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); - -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha512"); -MODULE_ALIAS_CRYPTO("sha384-arm"); -MODULE_ALIAS_CRYPTO("sha512-arm"); - -asmlinkage void sha512_block_data_order(struct sha512_state *state, - u8 const *src, int blocks); - -static int sha512_arm_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, - sha512_block_data_order); -} - -static int sha512_arm_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha512_base_do_finup(desc, data, len, sha512_block_data_order); - return sha512_base_finish(desc, out); -} - -static struct shash_alg sha512_arm_algs[] = { { - .init = sha384_base_init, - .update = sha512_arm_update, - .finup = sha512_arm_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA384_DIGEST_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-arm", - .cra_priority = 250, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .init = sha512_base_init, - .update = sha512_arm_update, - .finup = sha512_arm_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA512_DIGEST_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-arm", - .cra_priority = 250, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static int __init sha512_arm_mod_init(void) -{ - int err; - - err = crypto_register_shashes(sha512_arm_algs, - ARRAY_SIZE(sha512_arm_algs)); - if (err) - return err; - - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) { - err = crypto_register_shashes(sha512_neon_algs, - ARRAY_SIZE(sha512_neon_algs)); - if (err) - goto err_unregister; - } - return 0; - -err_unregister: - crypto_unregister_shashes(sha512_arm_algs, - ARRAY_SIZE(sha512_arm_algs)); - - return err; -} - -static void __exit sha512_arm_mod_fini(void) -{ - crypto_unregister_shashes(sha512_arm_algs, - ARRAY_SIZE(sha512_arm_algs)); - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) - crypto_unregister_shashes(sha512_neon_algs, - ARRAY_SIZE(sha512_neon_algs)); -} - -module_init(sha512_arm_mod_init); -module_exit(sha512_arm_mod_fini); diff --git a/arch/arm/crypto/sha512-neon-glue.c b/arch/arm/crypto/sha512-neon-glue.c deleted file mode 100644 index bd528077fefb..000000000000 --- a/arch/arm/crypto/sha512-neon-glue.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sha512-neon-glue.c - accelerated SHA-384/512 for ARM NEON - * - * Copyright (C) 2015 Linaro Ltd - */ - -#include -#include -#include -#include -#include -#include - -#include "sha512.h" - -MODULE_ALIAS_CRYPTO("sha384-neon"); -MODULE_ALIAS_CRYPTO("sha512-neon"); - -asmlinkage void sha512_block_data_order_neon(struct sha512_state *state, - const u8 *src, int blocks); - -static int sha512_neon_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - int remain; - - kernel_neon_begin(); - remain = sha512_base_do_update_blocks(desc, data, len, - sha512_block_data_order_neon); - kernel_neon_end(); - return remain; -} - -static int sha512_neon_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - kernel_neon_begin(); - sha512_base_do_finup(desc, data, len, sha512_block_data_order_neon); - kernel_neon_end(); - return sha512_base_finish(desc, out); -} - -struct shash_alg sha512_neon_algs[] = { { - .init = sha384_base_init, - .update = sha512_neon_update, - .finup = sha512_neon_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA384_DIGEST_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-neon", - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - - } -}, { - .init = sha512_base_init, - .update = sha512_neon_update, - .finup = sha512_neon_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA512_DIGEST_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-neon", - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; diff --git a/arch/arm/crypto/sha512.h b/arch/arm/crypto/sha512.h deleted file mode 100644 index eeaee52cda69..000000000000 --- a/arch/arm/crypto/sha512.h +++ /dev/null @@ -1,3 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -extern struct shash_alg sha512_neon_algs[2]; diff --git a/arch/arm/include/asm/cti.h b/arch/arm/include/asm/cti.h deleted file mode 100644 index f8500e5d6ea8..000000000000 --- a/arch/arm/include/asm/cti.h +++ /dev/null @@ -1,160 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASMARM_CTI_H -#define __ASMARM_CTI_H - -#include -#include - -/* The registers' definition is from section 3.2 of - * Embedded Cross Trigger Revision: r0p0 - */ -#define CTICONTROL 0x000 -#define CTISTATUS 0x004 -#define CTILOCK 0x008 -#define CTIPROTECTION 0x00C -#define CTIINTACK 0x010 -#define CTIAPPSET 0x014 -#define CTIAPPCLEAR 0x018 -#define CTIAPPPULSE 0x01c -#define CTIINEN 0x020 -#define CTIOUTEN 0x0A0 -#define CTITRIGINSTATUS 0x130 -#define CTITRIGOUTSTATUS 0x134 -#define CTICHINSTATUS 0x138 -#define CTICHOUTSTATUS 0x13c -#define CTIPERIPHID0 0xFE0 -#define CTIPERIPHID1 0xFE4 -#define CTIPERIPHID2 0xFE8 -#define CTIPERIPHID3 0xFEC -#define CTIPCELLID0 0xFF0 -#define CTIPCELLID1 0xFF4 -#define CTIPCELLID2 0xFF8 -#define CTIPCELLID3 0xFFC - -/* The below are from section 3.6.4 of - * CoreSight v1.0 Architecture Specification - */ -#define LOCKACCESS 0xFB0 -#define LOCKSTATUS 0xFB4 - -/** - * struct cti - cross trigger interface struct - * @base: mapped virtual address for the cti base - * @irq: irq number for the cti - * @trig_out_for_irq: triger out number which will cause - * the @irq happen - * - * cti struct used to operate cti registers. - */ -struct cti { - void __iomem *base; - int irq; - int trig_out_for_irq; -}; - -/** - * cti_init - initialize the cti instance - * @cti: cti instance - * @base: mapped virtual address for the cti base - * @irq: irq number for the cti - * @trig_out: triger out number which will cause - * the @irq happen - * - * called by machine code to pass the board dependent - * @base, @irq and @trig_out to cti. - */ -static inline void cti_init(struct cti *cti, - void __iomem *base, int irq, int trig_out) -{ - cti->base = base; - cti->irq = irq; - cti->trig_out_for_irq = trig_out; -} - -/** - * cti_map_trigger - use the @chan to map @trig_in to @trig_out - * @cti: cti instance - * @trig_in: trigger in number - * @trig_out: trigger out number - * @channel: channel number - * - * This function maps one trigger in of @trig_in to one trigger - * out of @trig_out using the channel @chan. - */ -static inline void cti_map_trigger(struct cti *cti, - int trig_in, int trig_out, int chan) -{ - void __iomem *base = cti->base; - unsigned long val; - - val = __raw_readl(base + CTIINEN + trig_in * 4); - val |= BIT(chan); - __raw_writel(val, base + CTIINEN + trig_in * 4); - - val = __raw_readl(base + CTIOUTEN + trig_out * 4); - val |= BIT(chan); - __raw_writel(val, base + CTIOUTEN + trig_out * 4); -} - -/** - * cti_enable - enable the cti module - * @cti: cti instance - * - * enable the cti module - */ -static inline void cti_enable(struct cti *cti) -{ - __raw_writel(0x1, cti->base + CTICONTROL); -} - -/** - * cti_disable - disable the cti module - * @cti: cti instance - * - * enable the cti module - */ -static inline void cti_disable(struct cti *cti) -{ - __raw_writel(0, cti->base + CTICONTROL); -} - -/** - * cti_irq_ack - clear the cti irq - * @cti: cti instance - * - * clear the cti irq - */ -static inline void cti_irq_ack(struct cti *cti) -{ - void __iomem *base = cti->base; - unsigned long val; - - val = __raw_readl(base + CTIINTACK); - val |= BIT(cti->trig_out_for_irq); - __raw_writel(val, base + CTIINTACK); -} - -/** - * cti_unlock - unlock cti module - * @cti: cti instance - * - * unlock the cti module, or else any writes to the cti - * module is not allowed. - */ -static inline void cti_unlock(struct cti *cti) -{ - __raw_writel(CS_LAR_KEY, cti->base + LOCKACCESS); -} - -/** - * cti_lock - lock cti module - * @cti: cti instance - * - * lock the cti module, so any writes to the cti - * module will be not allowed. - */ -static inline void cti_lock(struct cti *cti) -{ - __raw_writel(~CS_LAR_KEY, cti->base + LOCKACCESS); -} -#endif diff --git a/arch/arm/include/asm/stacktrace.h b/arch/arm/include/asm/stacktrace.h index f80a85b091d6..ba2f771cca23 100644 --- a/arch/arm/include/asm/stacktrace.h +++ b/arch/arm/include/asm/stacktrace.h @@ -2,8 +2,9 @@ #ifndef __ASM_STACKTRACE_H #define __ASM_STACKTRACE_H -#include #include +#include +#include struct stackframe { /* diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index f379c852dcb7..88336a1292bb 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -119,7 +119,7 @@ no_work_pending: ct_user_enter save = 0 -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE bl stackleak_erase_on_task_stack #endif restore_user_regs fast = 0, offset = 0 diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index c421a899fc84..7951b2c06fec 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -677,7 +677,7 @@ enum arm_regset { static const struct user_regset arm_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(u32), .align = sizeof(u32), @@ -689,7 +689,7 @@ static const struct user_regset arm_regsets[] = { * For the FPA regs in fpstate, the real fields are a mixture * of sizes, so pretend that the registers are word-sized: */ - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_fp) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -702,7 +702,7 @@ static const struct user_regset arm_regsets[] = { * Pretend that the VFP regs are word-sized, since the FPSCR is * a single word dangling at the end of struct user_vfp: */ - .core_note_type = NT_ARM_VFP, + USER_REGSET_NOTE_TYPE(ARM_VFP), .n = ARM_VFPREGS_SIZE / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index a41c93988d2c..0bfd66c7ada0 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -1004,7 +1004,7 @@ static void __init reserve_crashkernel(void) total_mem = get_total_mem(); ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base, - NULL, NULL); + NULL, NULL, NULL); /* invalid value specified or crashkernel=0 */ if (ret || !crash_size) return; diff --git a/arch/arm/lib/.gitignore b/arch/arm/lib/.gitignore new file mode 100644 index 000000000000..647d7a922e68 --- /dev/null +++ b/arch/arm/lib/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# This now-removed directory used to contain generated files. +/crypto/ diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 91ea0e29107a..0ca5aae1bcc3 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -5,8 +5,6 @@ # Copyright (C) 1995-2000 Russell King # -obj-y += crypto/ - lib-y := changebit.o csumipv6.o csumpartial.o \ csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ delay.o delay-loop.o findbit.o memchr.o memcpy.o \ @@ -47,9 +45,3 @@ ifeq ($(CONFIG_KERNEL_MODE_NEON),y) endif obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o - -obj-$(CONFIG_CRC32_ARCH) += crc32-arm.o -crc32-arm-y := crc32.o crc32-core.o - -obj-$(CONFIG_CRC_T10DIF_ARCH) += crc-t10dif-arm.o -crc-t10dif-arm-y := crc-t10dif.o crc-t10dif-core.o diff --git a/arch/arm/lib/crypto/sha256.c b/arch/arm/lib/crypto/sha256.c deleted file mode 100644 index 109192e54b0f..000000000000 --- a/arch/arm/lib/crypto/sha256.c +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-256 optimized for ARM - * - * Copyright 2025 Google LLC - */ -#include -#include -#include -#include - -asmlinkage void sha256_blocks_arch(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -EXPORT_SYMBOL_GPL(sha256_blocks_arch); -asmlinkage void sha256_block_data_order_neon(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -asmlinkage void sha256_ce_transform(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_neon); -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_ce); - -void sha256_blocks_simd(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && - static_branch_likely(&have_neon)) { - kernel_neon_begin(); - if (static_branch_likely(&have_ce)) - sha256_ce_transform(state, data, nblocks); - else - sha256_block_data_order_neon(state, data, nblocks); - kernel_neon_end(); - } else { - sha256_blocks_arch(state, data, nblocks); - } -} -EXPORT_SYMBOL_GPL(sha256_blocks_simd); - -bool sha256_is_arch_optimized(void) -{ - /* We always can use at least the ARM scalar implementation. */ - return true; -} -EXPORT_SYMBOL_GPL(sha256_is_arch_optimized); - -static int __init sha256_arm_mod_init(void) -{ - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && (elf_hwcap & HWCAP_NEON)) { - static_branch_enable(&have_neon); - if (elf_hwcap2 & HWCAP2_SHA2) - static_branch_enable(&have_ce); - } - return 0; -} -subsys_initcall(sha256_arm_mod_init); - -static void __exit sha256_arm_mod_exit(void) -{ -} -module_exit(sha256_arm_mod_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-256 optimized for ARM"); diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 04bd91c72521..c5ef27e3cd8f 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +config ARCH_MICROCHIP + bool + menuconfig ARCH_AT91 bool "AT91/Microchip SoCs" depends on (CPU_LITTLE_ENDIAN && (ARCH_MULTI_V4T || ARCH_MULTI_V5)) || \ @@ -8,6 +11,7 @@ menuconfig ARCH_AT91 select GPIOLIB select PINCTRL select SOC_BUS + select ARCH_MICROCHIP if ARCH_AT91 config SOC_SAMV7 diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 5d4f977ac7d2..47335c7dadf8 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -143,7 +143,7 @@ static void __init highbank_init(void) sregs_base = of_iomap(np, 0); WARN_ON(!sregs_base); - pm_power_off = highbank_power_off; + register_platform_power_off(highbank_power_off); highbank_pm_init(); bus_register_notifier(&platform_bus_type, &highbank_platform_nb); diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig index 35a3430c7942..638eabad2dd3 100644 --- a/arch/arm/mach-mediatek/Kconfig +++ b/arch/arm/mach-mediatek/Kconfig @@ -15,6 +15,10 @@ config MACH_MT2701 bool "MediaTek MT2701 SoCs support" default ARCH_MEDIATEK +config MACH_MT6572 + bool "MediaTek MT6572 SoCs support" + default ARCH_MEDIATEK + config MACH_MT6589 bool "MediaTek MT6589 SoCs support" default ARCH_MEDIATEK diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c index e6e9f93a1f01..5c28124bd007 100644 --- a/arch/arm/mach-mediatek/mediatek.c +++ b/arch/arm/mach-mediatek/mediatek.c @@ -38,6 +38,7 @@ static void __init mediatek_timer_init(void) static const char * const mediatek_board_dt_compat[] = { "mediatek,mt2701", + "mediatek,mt6572", "mediatek,mt6589", "mediatek,mt6592", "mediatek,mt7623", diff --git a/arch/arm/mach-mediatek/platsmp.c b/arch/arm/mach-mediatek/platsmp.c index 16a4ee6c9590..bbd26d423bde 100644 --- a/arch/arm/mach-mediatek/platsmp.c +++ b/arch/arm/mach-mediatek/platsmp.c @@ -29,6 +29,12 @@ static const struct mtk_smp_boot_info mtk_mt8135_tz_boot = { { 0x3f8, 0x3f8, 0x3f8 }, }; +static const struct mtk_smp_boot_info mtk_mt6572_boot = { + 0x10001400, 0x08, + { 0x534c4131 }, + { 0x0c }, +}; + static const struct mtk_smp_boot_info mtk_mt6589_boot = { 0x10002000, 0x34, { 0x534c4131, 0x4c415332, 0x41534c33 }, @@ -49,6 +55,7 @@ static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = { }; static const struct of_device_id mtk_smp_boot_infos[] __initconst = { + { .compatible = "mediatek,mt6572", .data = &mtk_mt6572_boot }, { .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot }, { .compatible = "mediatek,mt7623", .data = &mtk_mt7623_boot }, { .compatible = "mediatek,mt7629", .data = &mtk_mt7623_boot }, diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 0daf6c5b5c1c..16392720296c 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -175,20 +176,18 @@ static struct resource latch1_resources[] = { #define LATCH1_LABEL "latch1" -static struct bgpio_pdata latch1_pdata = { - .label = LATCH1_LABEL, - .base = -1, - .ngpio = LATCH1_NGPIO, +static const struct property_entry latch1_gpio_props[] = { + PROPERTY_ENTRY_STRING("label", LATCH1_LABEL), + PROPERTY_ENTRY_U32("ngpios", LATCH1_NGPIO), + { } }; -static struct platform_device latch1_gpio_device = { +static const struct platform_device_info latch1_gpio_devinfo = { .name = "basic-mmio-gpio", .id = 0, - .resource = latch1_resources, - .num_resources = ARRAY_SIZE(latch1_resources), - .dev = { - .platform_data = &latch1_pdata, - }, + .res = latch1_resources, + .num_res = ARRAY_SIZE(latch1_resources), + .properties = latch1_gpio_props, }; #define LATCH1_PIN_LED_CAMERA 0 @@ -213,20 +212,18 @@ static struct resource latch2_resources[] = { #define LATCH2_LABEL "latch2" -static struct bgpio_pdata latch2_pdata = { - .label = LATCH2_LABEL, - .base = -1, - .ngpio = LATCH2_NGPIO, +static const struct property_entry latch2_gpio_props[] = { + PROPERTY_ENTRY_STRING("label", LATCH2_LABEL), + PROPERTY_ENTRY_U32("ngpios", LATCH2_NGPIO), + { } }; -static struct platform_device latch2_gpio_device = { +static struct platform_device_info latch2_gpio_devinfo = { .name = "basic-mmio-gpio", .id = 1, - .resource = latch2_resources, - .num_resources = ARRAY_SIZE(latch2_resources), - .dev = { - .platform_data = &latch2_pdata, - }, + .res = latch2_resources, + .num_res = ARRAY_SIZE(latch2_resources), + .properties = latch2_gpio_props, }; #define LATCH2_PIN_LCD_VBLEN 0 @@ -542,8 +539,6 @@ static struct gpiod_lookup_table keybrd_pwr_gpio_table = { }; static struct platform_device *ams_delta_devices[] __initdata = { - &latch1_gpio_device, - &latch2_gpio_device, &ams_delta_kp_device, &ams_delta_audio_device, &ams_delta_serio_device, @@ -697,6 +692,9 @@ static void __init ams_delta_init(void) omap1_usb_init(&ams_delta_usb_config); platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices)); + platform_device_register_full(&latch1_gpio_devinfo); + platform_device_register_full(&latch2_gpio_devinfo); + /* * As soon as regulator consumers have been registered, assign their * dev_names to consumer supply entries of respective regulators. diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 33533e35720f..c0b1f7e6be87 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -1096,7 +1096,7 @@ static void __init spitz_init(void) software_node_register(&spitz_scoop_2_gpiochip_node); init_gpio_reset(SPITZ_GPIO_ON_RESET, 1, 0); - pm_power_off = spitz_poweroff; + register_platform_power_off(spitz_poweroff); PMCR = 0x00; diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c index 36915a073c23..f432d22bfed8 100644 --- a/arch/arm/mach-rockchip/platsmp.c +++ b/arch/arm/mach-rockchip/platsmp.c @@ -279,11 +279,6 @@ static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus) } if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { - if (rockchip_smp_prepare_sram(node)) { - of_node_put(node); - return; - } - /* enable the SCU power domain */ pmu_set_power_domain(PMU_PWRDN_SCU, true); @@ -316,11 +311,19 @@ static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus) asm ("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); ncores = ((l2ctlr >> 24) & 0x3) + 1; } - of_node_put(node); /* Make sure that all cores except the first are really off */ for (i = 1; i < ncores; i++) pmu_set_power_domain(0 + i, false); + + if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { + if (rockchip_smp_prepare_sram(node)) { + of_node_put(node); + return; + } + } + + of_node_put(node); } static void __init rk3036_smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/arm/mach-s3c/gpio-samsung.c b/arch/arm/mach-s3c/gpio-samsung.c index 206a492fbaf5..81e198e5a6d3 100644 --- a/arch/arm/mach-s3c/gpio-samsung.c +++ b/arch/arm/mach-s3c/gpio-samsung.c @@ -517,7 +517,7 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) if (!gc->direction_output) gc->direction_output = samsung_gpiolib_2bit_output; if (!gc->set) - gc->set_rv = samsung_gpiolib_set; + gc->set = samsung_gpiolib_set; if (!gc->get) gc->get = samsung_gpiolib_get; diff --git a/arch/arm/mach-s3c/mach-crag6410.c b/arch/arm/mach-s3c/mach-crag6410.c index e5df2cb51ab2..028169c7debf 100644 --- a/arch/arm/mach-s3c/mach-crag6410.c +++ b/arch/arm/mach-s3c/mach-crag6410.c @@ -252,14 +252,17 @@ static struct resource crag6410_mmgpio_resource[] = { [0] = DEFINE_RES_MEM_NAMED(S3C64XX_PA_XM0CSN4, 1, "dat"), }; -static struct platform_device crag6410_mmgpio = { +static const struct property_entry crag6410_mmgpio_props[] = { + PROPERTY_ENTRY_U32("gpio-mmio,base", MMGPIO_GPIO_BASE), + { } +}; + +static struct platform_device_info crag6410_mmgpio_devinfo = { .name = "basic-mmio-gpio", .id = -1, - .resource = crag6410_mmgpio_resource, - .num_resources = ARRAY_SIZE(crag6410_mmgpio_resource), - .dev.platform_data = &(struct bgpio_pdata) { - .base = MMGPIO_GPIO_BASE, - }, + .res = crag6410_mmgpio_resource, + .num_res = ARRAY_SIZE(crag6410_mmgpio_resource), + .properties = crag6410_mmgpio_props, }; static struct platform_device speyside_device = { @@ -373,7 +376,6 @@ static struct platform_device *crag6410_devices[] __initdata = { &crag6410_gpio_keydev, &crag6410_dm9k_device, &s3c64xx_device_spi0, - &crag6410_mmgpio, &crag6410_lcd_powerdev, &crag6410_backlight_device, &speyside_device, @@ -871,6 +873,7 @@ static void __init crag6410_machine_init(void) pwm_add_table(crag6410_pwm_lookup, ARRAY_SIZE(crag6410_pwm_lookup)); platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices)); + platform_device_register_full(&crag6410_mmgpio_devinfo); gpio_led_register_device(-1, &gpio_leds_pdata); diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 0c586047d130..5383a26f5116 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -298,7 +298,7 @@ static struct platform_device *sa11x0_devices[] __initdata = { static int __init sa1100_init(void) { struct resource wdt_res = DEFINE_RES_MEM(0x90000000, 0x20); - pm_power_off = sa1100_power_off; + register_platform_power_off(sa1100_power_off); regulator_has_full_constraints(); diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index d5c805adf7a8..ea706fac6358 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -63,7 +63,7 @@ static void __init tegra_cpu_reset_handler_enable(void) BUG_ON(is_enabled); BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE); - memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start, + memcpy_toio(iram_base, (void *)__tegra_cpu_reset_handler_start, tegra_cpu_reset_handler_size); err = call_firmware_op(set_cpu_boot_addr, 0, reset_address); diff --git a/arch/arm/mach-vt8500/vt8500.c b/arch/arm/mach-vt8500/vt8500.c index 0ab40087ae1c..1d294255d708 100644 --- a/arch/arm/mach-vt8500/vt8500.c +++ b/arch/arm/mach-vt8500/vt8500.c @@ -141,7 +141,7 @@ static void __init vt8500_init(void) pr_err("%s:ioremap(power_off) failed\n", __func__); } if (pmc_base) - pm_power_off = &vt8500_power_off; + register_platform_power_off(vt8500_power_off); else pr_err("%s: PMC Hibernation register could not be remapped, not enabling power off!\n", __func__); } diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c index 25dbd84a1aaf..2bfefb252ffd 100644 --- a/arch/arm/mm/cache-feroceon-l2.c +++ b/arch/arm/mm/cache-feroceon-l2.c @@ -295,7 +295,7 @@ static inline u32 read_extra_features(void) return u; } -static inline void write_extra_features(u32 u) +static inline void __init write_extra_features(u32 u) { __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u)); } diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c index b1e1aba602f7..bfe166ccace0 100644 --- a/arch/arm/mm/cache-tauros2.c +++ b/arch/arm/mm/cache-tauros2.c @@ -177,7 +177,7 @@ static inline void __init write_actlr(u32 actlr) __asm__("mcr p15, 0, %0, c1, c0, 1\n" : : "r" (actlr)); } -static void enable_extra_feature(unsigned int features) +static void __init enable_extra_feature(unsigned int features) { u32 u; diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index ab01b51de559..46169fe42c61 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -268,7 +268,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) int sig, code; vm_fault_t fault; unsigned int flags = FAULT_FLAG_DEFAULT; - unsigned long vm_flags = VM_ACCESS_FLAGS; + vm_flags_t vm_flags = VM_ACCESS_FLAGS; if (kprobe_page_fault(regs, fsr)) return 0; diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index ca1bd764cfa5..49e29b7894a3 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -468,14 +468,14 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (is_out) { seq_printf(s, " out %s %s\n", - out & msk ? "hi" : "lo", + str_hi_lo(out & msk), blink & msk ? "(blink )" : ""); continue; } seq_printf(s, " in %s (act %s) - IRQ", - (data_in ^ in_pol) & msk ? "hi" : "lo", - in_pol & msk ? "lo" : "hi"); + str_hi_lo((data_in ^ in_pol) & msk), + str_lo_hi(in_pol & msk)); if (!((edg_msk | lvl_msk) & msk)) { seq_puts(s, " disabled\n"); continue; @@ -540,7 +540,7 @@ void __init orion_gpio_init(int gpio_base, int ngpio, ochip->chip.direction_input = orion_gpio_direction_input; ochip->chip.get = orion_gpio_get; ochip->chip.direction_output = orion_gpio_direction_output; - ochip->chip.set_rv = orion_gpio_set; + ochip->chip.set = orion_gpio_set; ochip->chip.to_irq = orion_gpio_to_irq; ochip->chip.base = gpio_base; ochip->chip.ngpio = ngpio; diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index 27c1d5ebcd91..b07e699aaa3c 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -482,3 +482,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile index cb044bfd145d..cf8cd39ab804 100644 --- a/arch/arm/vdso/Makefile +++ b/arch/arm/vdso/Makefile @@ -26,7 +26,7 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH) CFLAGS_REMOVE_vdso.o = -pg # Force -O2 to avoid libgcc dependencies -CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) +CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) ifeq ($(c-gettimeofday-y),) CFLAGS_vgettimeofday.o = -O2 else diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index a395b6c0aae2..8655bc3d3634 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -541,7 +541,7 @@ static int __init xen_late_init(void) if (!xen_domain()) return -ENODEV; - pm_power_off = xen_power_off; + register_platform_power_off(xen_power_off); register_restart_handler(&xen_restart_nb); if (!xen_initial_domain()) { struct timespec64 ts; diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 55fc331af337..e9bbfacc35a6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -21,8 +21,6 @@ config ARM64 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_CC_PLATFORM - select ARCH_HAS_CRC32 - select ARCH_HAS_CRC_T10DIF if KERNEL_MODE_NEON select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL select ARCH_HAS_DEBUG_VM_PGTABLE @@ -44,7 +42,6 @@ config ARM64 select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT select ARCH_HAS_PREEMPT_LAZY select ARCH_HAS_PTDUMP - select ARCH_HAS_PTE_DEVMAP select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_HW_PTE_YOUNG select ARCH_HAS_SETUP_DMA_OPS @@ -129,6 +126,7 @@ config ARM64 select ARM_GIC_V2M if PCI select ARM_GIC_V3 select ARM_GIC_V3_ITS if PCI + select ARM_GIC_V5 select ARM_PSCI_FW select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS @@ -136,6 +134,7 @@ config ARM64 select CPU_PM if (SUSPEND || CPU_IDLE) select CPUMASK_OFFSTACK if NR_CPUS > 256 select DCACHE_WORD_ACCESS + select HAVE_EXTRA_IPI_TRACEPOINTS select DYNAMIC_FTRACE if FUNCTION_TRACER select DMA_BOUNCE_UNALIGNED_KMALLOC select DMA_DIRECT_REMAP @@ -187,12 +186,12 @@ config ARM64 select HAVE_ARCH_KCSAN if EXPERT select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT select HAVE_ARCH_PREL32_RELOCATIONS select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE @@ -223,7 +222,6 @@ config ARM64 select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS @@ -234,6 +232,7 @@ config ARM64 select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_IOREMAP_PROT select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_LIVEPATCH select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI select HAVE_PERF_EVENTS @@ -242,6 +241,7 @@ config ARM64 select HAVE_PERF_USER_STACK_DUMP select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RELIABLE_STACKTRACE select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_FUNCTION_ARG_ACCESS_API select MMU_GATHER_RCU_TABLE_FREE @@ -256,6 +256,7 @@ config ARM64 select HOTPLUG_SMT if HOTPLUG_CPU select IRQ_DOMAIN select IRQ_FORCED_THREADING + select JUMP_LABEL select KASAN_VMALLOC if KASAN select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA @@ -279,6 +280,7 @@ config ARM64 select HAVE_SOFTIRQ_ON_OWN_STACK select USER_STACKTRACE_SUPPORT select VDSO_GETRANDOM + select VMAP_STACK help ARM 64-bit (AArch64) Linux support. @@ -2499,3 +2501,4 @@ source "drivers/acpi/Kconfig" source "arch/arm64/kvm/Kconfig" +source "kernel/livepatch/Kconfig" diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index a541bb029aa4..a88f5ad9328c 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -40,6 +40,12 @@ config ARCH_APPLE This enables support for Apple's in-house ARM SoC family, such as the Apple M1. +config ARCH_AXIADO + bool "Axiado SoC Family" + select GPIOLIB + help + This enables support for Axiado SoC family like AX3000 + menuconfig ARCH_BCM bool "Broadcom SoC Support" @@ -106,6 +112,12 @@ config ARCH_BLAIZE help This enables support for the Blaize SoC family +config ARCH_CIX + bool "Cixtech SoC family" + help + This enables support for the Cixtech SoC family, + like P1(sky1). + config ARCH_EXYNOS bool "Samsung Exynos SoC family" select COMMON_CLK_SAMSUNG @@ -135,11 +147,7 @@ config ARCH_SPARX5 config ARCH_K3 bool "Texas Instruments Inc. K3 multicore SoC architecture" - select PM_GENERIC_DOMAINS if PM - select MAILBOX select SOC_TI - select TI_MESSAGE_MANAGER - select TI_SCI_PROTOCOL select TI_K3_SOCINFO help This enables support for Texas Instruments' K3 multicore SoC @@ -178,6 +186,14 @@ config ARCH_MESON This enables support for the arm64 based Amlogic SoCs such as the s905, S905X/D, S912, A113X/D or S905X/D2 +config ARCH_MMP + bool "Marvell MMP SoC Family" + select PINCTRL + select PINCTRL_SINGLE + help + This enables support for Marvell MMP SoC family, currently + supporting PXA1908 aka IAP140. + config ARCH_MVEBU bool "Marvell EBU SoC Family" select ARMADA_AP806_SYSCON @@ -307,11 +323,16 @@ config ARCH_INTEL_SOCFPGA Stratix 10 (ex. Altera), Stratix10 Software Virtual Platform, Agilex and eASIC N5X. +config ARCH_SOPHGO + bool "Sophgo SoCs" + select ARCH_HAS_RESET_CONTROLLER + help + This enables support for Sophgo SoC platform hardware. + config ARCH_STM32 bool "STMicroelectronics STM32 SoC Family" select GPIOLIB select PINCTRL - select PINCTRL_STM32MP257 select ARM_SMC_MBOX select ARM_SCMI_PROTOCOL select REGULATOR diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 79b73a21ddc2..b0844404eda1 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -9,10 +9,12 @@ subdir-y += amlogic subdir-y += apm subdir-y += apple subdir-y += arm +subdir-y += axiado subdir-y += bitmain subdir-y += blaize subdir-y += broadcom subdir-y += cavium +subdir-y += cix subdir-y += exynos subdir-y += freescale subdir-y += hisilicon @@ -28,6 +30,7 @@ subdir-y += realtek subdir-y += renesas subdir-y += rockchip subdir-y += socionext +subdir-y += sophgo subdir-y += sprd subdir-y += st subdir-y += synaptics diff --git a/arch/arm64/boot/dts/airoha/en7581-evb.dts b/arch/arm64/boot/dts/airoha/en7581-evb.dts index 99d2c4f1fc5a..dae9968a4ff6 100644 --- a/arch/arm64/boot/dts/airoha/en7581-evb.dts +++ b/arch/arm64/boot/dts/airoha/en7581-evb.dts @@ -98,3 +98,11 @@ &pcie1 { &i2c0 { status = "okay"; }; + +ð { + status = "okay"; +}; + +&gdm1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/airoha/en7581.dtsi b/arch/arm64/boot/dts/airoha/en7581.dtsi index 536ece69b935..ff6908a76e8e 100644 --- a/arch/arm64/boot/dts/airoha/en7581.dtsi +++ b/arch/arm64/boot/dts/airoha/en7581.dtsi @@ -346,5 +346,54 @@ i2c1: i2c@1fbf8100 { status = "disabled"; }; + + eth: ethernet@1fb50000 { + compatible = "airoha,en7581-eth"; + reg = <0 0x1fb50000 0 0x2600>, + <0 0x1fb54000 0 0x2000>, + <0 0x1fb56000 0 0x2000>; + reg-names = "fe", "qdma0", "qdma1"; + + resets = <&scuclk EN7581_FE_RST>, + <&scuclk EN7581_FE_PDMA_RST>, + <&scuclk EN7581_FE_QDMA_RST>, + <&scuclk EN7581_XSI_MAC_RST>, + <&scuclk EN7581_DUAL_HSI0_MAC_RST>, + <&scuclk EN7581_DUAL_HSI1_MAC_RST>, + <&scuclk EN7581_HSI_MAC_RST>, + <&scuclk EN7581_XFP_MAC_RST>; + reset-names = "fe", "pdma", "qdma", + "xsi-mac", "hsi0-mac", "hsi1-mac", + "hsi-mac", "xfp-mac"; + + interrupts = , + , + , + , + , + , + , + , + , + ; + + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + + gdm1: ethernet@1 { + compatible = "airoha,eth-mac"; + reg = <1>; + phy-mode = "internal"; + status = "disabled"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile index 773cc02a13d0..780aeba0f3a4 100644 --- a/arch/arm64/boot/dts/allwinner/Makefile +++ b/arch/arm64/boot/dts/allwinner/Makefile @@ -57,3 +57,4 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h700-anbernic-rg35xx-sp.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun55i-a527-cubie-a5e.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun55i-h728-x96qpro+.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun55i-t527-avaota-a1.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun55i-t527-orangepi-4a.dtb diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a100.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a100.dtsi index bd366389b238..bb5f9e4f3d42 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a100.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a100.dtsi @@ -236,6 +236,21 @@ mmc2_pins: mmc2-pins { bias-pull-up; }; + rgmii0_pins: rgmii0-pins { + pins = "PH0", "PH1", "PH2", "PH3", "PH4", + "PH5", "PH6", "PH7", "PH9", "PH10", + "PH14", "PH15", "PH16", "PH17", "PH18"; + function = "emac0"; + drive-strength = <40>; + }; + + rmii0_pins: rmii0-pins { + pins = "PH0", "PH1", "PH2", "PH3", "PH4", + "PH5", "PH6", "PH7", "PH9", "PH10"; + function = "emac0"; + drive-strength = <40>; + }; + uart0_pb_pins: uart0-pb-pins { pins = "PB9", "PB10"; function = "uart0"; @@ -405,6 +420,26 @@ i2c3: i2c@5002c00 { #size-cells = <0>; }; + emac0: ethernet@5020000 { + compatible = "allwinner,sun50i-a100-emac", + "allwinner,sun50i-a64-emac"; + reg = <0x5020000 0x10000>; + interrupts = ; + interrupt-names = "macirq"; + clocks = <&ccu CLK_BUS_EMAC>; + clock-names = "stmmaceth"; + resets = <&ccu RST_BUS_EMAC>; + reset-names = "stmmaceth"; + syscon = <&syscon>; + status = "disabled"; + + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + ths: thermal-sensor@5070400 { compatible = "allwinner,sun50i-a100-ths"; reg = <0x05070400 0x100>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-liontron-h-a133l.dts b/arch/arm64/boot/dts/allwinner/sun50i-a133-liontron-h-a133l.dts index fe77178d3e33..90a50910f07b 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a133-liontron-h-a133l.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-liontron-h-a133l.dts @@ -65,6 +65,25 @@ &ehci1 { status = "okay"; }; +&emac0 { + pinctrl-names = "default"; + pinctrl-0 = <&rmii0_pins>; + phy-handle = <&rmii_phy>; + phy-mode = "rmii"; + status = "okay"; +}; + +&mdio0 { + reset-gpios = <&pio 7 12 GPIO_ACTIVE_LOW>; /* PH12 */ + reset-delay-us = <2000>; + reset-post-delay-us = <2000>; + + rmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; +}; + &mmc0 { vmmc-supply = <®_dcdc1>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index 8b7cbc2e78f5..6b6f2296bdff 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -7,6 +7,8 @@ #include #include #include +#include +#include / { interrupt-parent = <&gic>; @@ -106,6 +108,21 @@ soc { #size-cells = <1>; ranges = <0x0 0x0 0x0 0x40000000>; + gpu: gpu@1800000 { + compatible = "allwinner,sun55i-a523-mali", + "arm,mali-valhall-jm"; + reg = <0x1800000 0x10000>; + interrupts = , + , + ; + interrupt-names = "job", "mmu", "gpu"; + clocks = <&ccu CLK_GPU>, <&ccu CLK_BUS_GPU>; + clock-names = "core", "bus"; + power-domains = <&pck600 PD_GPU>; + resets = <&ccu RST_BUS_GPU>; + status = "disabled"; + }; + pio: pinctrl@2000000 { compatible = "allwinner,sun55i-a523-pinctrl"; reg = <0x2000000 0x800>; @@ -126,16 +143,6 @@ pio: pinctrl@2000000 { interrupt-controller; #interrupt-cells = <3>; - rgmii0_pins: rgmii0-pins { - pins = "PH0", "PH1", "PH2", "PH3", "PH4", - "PH5", "PH6", "PH7", "PH9", "PH10", - "PH14", "PH15", "PH16", "PH17", "PH18"; - allwinner,pinmux = <5>; - function = "emac0"; - drive-strength = <40>; - bias-disable; - }; - mmc0_pins: mmc0-pins { pins = "PF0" ,"PF1", "PF2", "PF3", "PF4", "PF5"; allwinner,pinmux = <2>; @@ -163,11 +170,35 @@ mmc2_pins: mmc2-pins { bias-pull-up; }; + rgmii0_pins: rgmii0-pins { + pins = "PH0", "PH1", "PH2", "PH3", "PH4", + "PH5", "PH6", "PH7", "PH9", "PH10", + "PH14", "PH15", "PH16", "PH17", "PH18"; + allwinner,pinmux = <5>; + function = "gmac0"; + drive-strength = <40>; + bias-disable; + }; + uart0_pb_pins: uart0-pb-pins { pins = "PB9", "PB10"; allwinner,pinmux = <2>; function = "uart0"; }; + + /omit-if-no-ref/ + uart1_pins: uart1-pins { + pins = "PG6", "PG7"; + function = "uart1"; + allwinner,pinmux = <2>; + }; + + /omit-if-no-ref/ + uart1_rts_cts_pins: uart1-rts-cts-pins { + pins = "PG8", "PG9"; + function = "uart1"; + allwinner,pinmux = <2>; + }; }; ccu: clock-controller@2001000 { @@ -181,69 +212,6 @@ ccu: clock-controller@2001000 { #reset-cells = <1>; }; - mmc0: mmc@4020000 { - compatible = "allwinner,sun55i-a523-mmc", - "allwinner,sun20i-d1-mmc"; - reg = <0x04020000 0x1000>; - clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>; - clock-names = "ahb", "mmc"; - resets = <&ccu RST_BUS_MMC0>; - reset-names = "ahb"; - interrupts = ; - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins>; - status = "disabled"; - - max-frequency = <150000000>; - cap-sd-highspeed; - cap-mmc-highspeed; - cap-sdio-irq; - #address-cells = <1>; - #size-cells = <0>; - }; - - mmc1: mmc@4021000 { - compatible = "allwinner,sun55i-a523-mmc", - "allwinner,sun20i-d1-mmc"; - reg = <0x04021000 0x1000>; - clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>; - clock-names = "ahb", "mmc"; - resets = <&ccu RST_BUS_MMC1>; - reset-names = "ahb"; - interrupts = ; - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins>; - status = "disabled"; - - max-frequency = <150000000>; - cap-sd-highspeed; - cap-mmc-highspeed; - cap-sdio-irq; - #address-cells = <1>; - #size-cells = <0>; - }; - - mmc2: mmc@4022000 { - compatible = "allwinner,sun55i-a523-mmc", - "allwinner,sun20i-d1-mmc"; - reg = <0x04022000 0x1000>; - clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>; - clock-names = "ahb", "mmc"; - resets = <&ccu RST_BUS_MMC2>; - reset-names = "ahb"; - interrupts = ; - pinctrl-names = "default"; - pinctrl-0 = <&mmc2_pins>; - status = "disabled"; - - max-frequency = <150000000>; - cap-sd-highspeed; - cap-mmc-highspeed; - cap-sdio-irq; - #address-cells = <1>; - #size-cells = <0>; - }; - wdt: watchdog@2050000 { compatible = "allwinner,sun55i-a523-wdt"; reg = <0x2050000 0x20>; @@ -428,6 +396,14 @@ syscon: syscon@3000000 { ranges; }; + sid: efuse@3006000 { + compatible = "allwinner,sun55i-a523-sid", + "allwinner,sun50i-a64-sid"; + reg = <0x03006000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + }; + gic: interrupt-controller@3400000 { compatible = "arm,gic-v3"; #address-cells = <1>; @@ -449,6 +425,69 @@ its: msi-controller@3440000 { }; }; + mmc0: mmc@4020000 { + compatible = "allwinner,sun55i-a523-mmc", + "allwinner,sun20i-d1-mmc"; + reg = <0x04020000 0x1000>; + clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC0>; + reset-names = "ahb"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; + status = "disabled"; + + max-frequency = <150000000>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + #address-cells = <1>; + #size-cells = <0>; + }; + + mmc1: mmc@4021000 { + compatible = "allwinner,sun55i-a523-mmc", + "allwinner,sun20i-d1-mmc"; + reg = <0x04021000 0x1000>; + clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC1>; + reset-names = "ahb"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + status = "disabled"; + + max-frequency = <150000000>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + #address-cells = <1>; + #size-cells = <0>; + }; + + mmc2: mmc@4022000 { + compatible = "allwinner,sun55i-a523-mmc", + "allwinner,sun20i-d1-mmc"; + reg = <0x04022000 0x1000>; + clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC2>; + reset-names = "ahb"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_pins>; + status = "disabled"; + + max-frequency = <150000000>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + #address-cells = <1>; + #size-cells = <0>; + }; + usb_otg: usb@4100000 { compatible = "allwinner,sun55i-a523-musb", "allwinner,sun8i-a33-musb"; @@ -540,8 +579,8 @@ ohci1: usb@4200400 { status = "disabled"; }; - emac0: ethernet@4500000 { - compatible = "allwinner,sun55i-a523-emac0", + gmac0: ethernet@4500000 { + compatible = "allwinner,sun55i-a523-gmac0", "allwinner,sun50i-a64-emac"; reg = <0x04500000 0x10000>; clocks = <&ccu CLK_BUS_EMAC0>; @@ -562,6 +601,14 @@ mdio0: mdio { }; }; + ppu: power-controller@7001400 { + compatible = "allwinner,sun55i-a523-ppu"; + reg = <0x07001400 0x400>; + clocks = <&r_ccu CLK_BUS_R_PPU1>; + resets = <&r_ccu RST_BUS_R_PPU1>; + #power-domain-cells = <1>; + }; + r_ccu: clock-controller@7010000 { compatible = "allwinner,sun55i-a523-r-ccu"; reg = <0x7010000 0x250>; @@ -608,6 +655,14 @@ r_i2c_pins: r-i2c-pins { }; }; + pck600: power-controller@7060000 { + compatible = "allwinner,sun55i-a523-pck-600"; + reg = <0x07060000 0x8000>; + clocks = <&r_ccu CLK_BUS_R_PPU0>; + resets = <&r_ccu RST_BUS_R_PPU0>; + #power-domain-cells = <1>; + }; + r_i2c0: i2c@7081400 { compatible = "allwinner,sun55i-a523-i2c", "allwinner,sun8i-v536-i2c", diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a527-cubie-a5e.dts b/arch/arm64/boot/dts/allwinner/sun55i-a527-cubie-a5e.dts index 0f58d92a6adc..553ad774ed13 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a527-cubie-a5e.dts +++ b/arch/arm64/boot/dts/allwinner/sun55i-a527-cubie-a5e.dts @@ -12,7 +12,7 @@ / { compatible = "radxa,cubie-a5e", "allwinner,sun55i-a527"; aliases { - ethernet0 = &emac0; + ethernet0 = &gmac0; serial0 = &uart0; }; @@ -55,7 +55,7 @@ &ehci1 { status = "okay"; }; -&emac0 { +&gmac0 { phy-mode = "rgmii-id"; phy-handle = <&ext_rgmii_phy>; phy-supply = <®_cldo3>; @@ -66,6 +66,11 @@ &emac0 { status = "okay"; }; +&gpu { + mali-supply = <®_dcdc2>; + status = "okay"; +}; + &mdio0 { ext_rgmii_phy: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; diff --git a/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts b/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts index 59db103546f6..a96927fbdadd 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts +++ b/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts @@ -54,6 +54,11 @@ &ehci1 { status = "okay"; }; +&gpu { + mali-supply = <®_dcdc2>; + status = "okay"; +}; + &mmc0 { vmmc-supply = <®_vcc3v3>; cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */ 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 08127f0cdd35..b9eeb6753e9e 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts +++ b/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts @@ -12,7 +12,7 @@ / { compatible = "yuzukihd,avaota-a1", "allwinner,sun55i-t527"; aliases { - ethernet0 = &emac0; + ethernet0 = &gmac0; serial0 = &uart0; }; @@ -65,7 +65,7 @@ &ehci1 { status = "okay"; }; -&emac0 { +&gmac0 { phy-mode = "rgmii-id"; phy-handle = <&ext_rgmii_phy>; phy-supply = <®_dcdc4>; @@ -76,6 +76,11 @@ &emac0 { status = "okay"; }; +&gpu { + mali-supply = <®_dcdc2>; + status = "okay"; +}; + &mdio0 { ext_rgmii_phy: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; diff --git a/arch/arm64/boot/dts/allwinner/sun55i-t527-orangepi-4a.dts b/arch/arm64/boot/dts/allwinner/sun55i-t527-orangepi-4a.dts new file mode 100644 index 000000000000..d07bb9193b43 --- /dev/null +++ b/arch/arm64/boot/dts/allwinner/sun55i-t527-orangepi-4a.dts @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Chen-Yu Tsai + */ + +/dts-v1/; + +#include "sun55i-a523.dtsi" + +#include +#include + +/ { + model = "OrangePi 4A"; + compatible = "xunlong,orangepi-4a", "allwinner,sun55i-t527"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + ext_osc32k: ext-osc32k-clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "ext_osc32k"; + }; + + leds { + compatible = "gpio-leds"; + + /* PWM capable pin, but PWM isn't supported yet. */ + led { + function = LED_FUNCTION_STATUS; + color = ; + gpios = <&pio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ + }; + }; + + wifi_pwrseq: pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&r_pio 1 1 GPIO_ACTIVE_LOW>; /* PM1 */ + clocks = <&rtc CLK_OSC32K_FANOUT>; + clock-names = "ext_clock"; + }; + + reg_otg_vbus: regulator-otg-vbus { + compatible = "regulator-fixed"; + regulator-name = "otg-vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <®_vcc5v>; + gpio = <&r_pio 0 4 GPIO_ACTIVE_HIGH>; /* PL4 */ + enable-active-high; + }; + + reg_pcie_vcc3v3: regulator-pcie-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc-pcie-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_vcc5v>; + gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */ + enable-active-high; + }; + + reg_usb_vbus: regulator-usb-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb-vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <®_vcc5v>; + gpio = <&r_pio 0 12 GPIO_ACTIVE_HIGH>; /* PL12 */ + enable-active-high; + }; + + reg_vcc5v: regulator-vcc5v { + /* board wide 5V supply from USB type-C port */ + compatible = "regulator-fixed"; + regulator-name = "vcc-5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +&gpu { + mali-supply = <®_dcdc2>; + status = "okay"; +}; + +&mmc0 { + vmmc-supply = <®_cldo3>; + cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */ + bus-width = <4>; + status = "okay"; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_dldo1_323>; + vqmmc-supply = <®_bldo1>; + status = "okay"; + + brcmf: wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&r_pio>; + interrupts = <1 0 IRQ_TYPE_LEVEL_LOW>; /* PM0 */ + interrupt-names = "host-wake"; + }; +}; + +&mmc2 { + bus-width = <8>; + cap-mmc-hw-reset; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + vmmc-supply = <®_cldo3>; + vqmmc-supply = <®_cldo1>; + status = "okay"; +}; + +&ohci0 { + status = "okay"; +}; + +&ohci1 { + status = "okay"; +}; + +&pio { + vcc-pb-supply = <®_cldo3>; /* via VCC-IO */ + vcc-pc-supply = <®_cldo1>; + vcc-pd-supply = <®_cldo3>; + vcc-pe-supply = <®_aldo2>; + vcc-pf-supply = <®_cldo3>; /* VCC-IO for 3.3v; VCC-MCSI for 1.8v */ + vcc-pg-supply = <®_bldo1>; + vcc-ph-supply = <®_cldo3>; /* via VCC-IO */ + vcc-pi-supply = <®_cldo3>; + vcc-pj-supply = <®_cldo1>; + vcc-pk-supply = <®_cldo1>; +}; + +&r_i2c0 { + status = "okay"; + + axp717: pmic@35 { + compatible = "x-powers,axp717"; + reg = <0x35>; + interrupt-controller; + #interrupt-cells = <1>; + interrupts-extended = <&nmi_intc 0 IRQ_TYPE_LEVEL_LOW>; + + vin1-supply = <®_vcc5v>; + vin2-supply = <®_vcc5v>; + vin3-supply = <®_vcc5v>; + vin4-supply = <®_vcc5v>; + aldoin-supply = <®_vcc5v>; + bldoin-supply = <®_vcc5v>; + cldoin-supply = <®_vcc5v>; + + regulators { + /* Supplies the "little" cluster (1.4 GHz cores) */ + reg_dcdc1: dcdc1 { + regulator-always-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1160000>; + regulator-name = "vdd-cpul"; + }; + + reg_dcdc2: dcdc2 { + regulator-always-on; + regulator-min-microvolt = <920000>; + regulator-max-microvolt = <920000>; + regulator-name = "vdd-gpu-sys"; + }; + + reg_dcdc3: dcdc3 { + regulator-always-on; + regulator-min-microvolt = <1160000>; + regulator-max-microvolt = <1160000>; + regulator-name = "vcc-dram"; + }; + + reg_dcdc4: dcdc4 { + /* feeds 3.3V pin on GPIO header */ + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vdd-io"; + }; + + aldo1 { + /* not actually connected */ + regulator-name = "avdd-csi"; + }; + + reg_aldo2: aldo2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pe"; + }; + + reg_aldo3: aldo3 { + /* supplies the I2C pins for this PMIC */ + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-pl-usb"; + }; + + reg_aldo4: aldo4 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pll-dxco-avcc"; + }; + + reg_bldo1: bldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pg-wifi"; + }; + + reg_bldo2: bldo2 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pm-lpddr"; + }; + + bldo3 { + /* not actually connected */ + regulator-name = "afvcc-csi"; + }; + + bldo4 { + /* not actually connected */ + regulator-name = "dvdd-csi"; + }; + + reg_cldo1: cldo1 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-cvp-pc-lvds-mcsi-pk-efuse-pcie-edp-1v8"; + }; + + reg_cldo2: cldo2 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc3v3-csi"; + }; + + reg_cldo3: cldo3 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-io-mmc-nand-pd-pi-usb"; + }; + + reg_cldo4: cldo4 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-3v3-phy1-lcd"; + }; + + reg_cpusldo: cpusldo { + /* supplies the management core */ + regulator-always-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-name = "vdd-cpus-usb-0v9"; + }; + }; + }; + + axp323: pmic@36 { + compatible = "x-powers,axp323"; + reg = <0x36>; + #interrupt-cells = <1>; + interrupt-controller; + status = "okay"; + + vin1-supply = <®_vcc5v>; + vin2-supply = <®_vcc5v>; + vin3-supply = <®_vcc5v>; + + regulators { + reg_aldo1_323: aldo1 { + /* less capable and shares load with dldo1 */ + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi"; + }; + + reg_dldo1_323: dldo1 { + /* more capable and shares load with aldo1 */ + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi2"; + }; + + /* Supplies the "big" cluster (1.8 GHz cores) */ + reg_dcdc1_323: dcdc1 { + regulator-always-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1150000>; + regulator-name = "vdd-cpub"; + }; + + /* DCDC2 is polyphased with DCDC1 */ + + /* Some RISC-V management core related voltage */ + reg_dcdc3_323: dcdc3 { + regulator-always-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-name = "vdd-dnr"; + }; + }; + }; +}; + +&r_pio { +/* + * Specifying the supply would create a circular dependency. + * + * vcc-pl-supply = <®_aldo3>; + */ + vcc-pm-supply = <®_bldo2>; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4345c5"; + clocks = <&rtc CLK_OSC32K_FANOUT>; + clock-names = "lpo"; + vbat-supply = <®_aldo1_323>; + vddio-supply = <®_bldo1>; + device-wakeup-gpios = <&r_pio 1 3 GPIO_ACTIVE_HIGH>; /* PM3 */ + host-wakeup-gpios = <&r_pio 1 4 GPIO_ACTIVE_HIGH>; /* PM4 */ + shutdown-gpios = <&r_pio 1 2 GPIO_ACTIVE_HIGH>; /* PM2 */ + }; +}; + +&usb_otg { + /* + * The OTG controller is connected to one of the type-A ports. + * There is a regulator, controlled by a GPIO, to provide VBUS power + * to the port, and a VBUSDET GPIO, to detect externally provided + * power. But without ID or CC pins there is no real way to do a + * runtime role detection. + */ + dr_mode = "host"; + status = "okay"; +}; + +&usbphy { + usb0_vbus-supply = <®_otg_vbus>; + usb0_vbus_det-gpios = <&r_pio 0 7 GPIO_ACTIVE_HIGH>; /* PL7 */ + usb1_vbus-supply = <®_usb_vbus>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi index 0def0b0daaf7..effd242f6bf7 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi @@ -130,16 +130,19 @@ clocks { cb_intosc_hs_div2_clk: cb-intosc-hs-div2-clk { #clock-cells = <0>; compatible = "fixed-clock"; + clock-frequency = <150000000>; }; cb_intosc_ls_clk: cb-intosc-ls-clk { #clock-cells = <0>; compatible = "fixed-clock"; + clock-frequency = <300000000>; }; f2s_free_clk: f2s-free-clk { #clock-cells = <0>; compatible = "fixed-clock"; + status = "disabled"; }; osc1: osc1 { @@ -395,7 +398,7 @@ pinctrl1: pinctrl@ffd13100 { rst: rstmgr@ffd11000 { #reset-cells = <1>; - compatible = "altr,stratix10-rst-mgr"; + compatible = "altr,stratix10-rst-mgr", "altr,rst-mgr"; reg = <0xffd11000 0x1000>; }; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_swvp.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_swvp.dts index 34ccf8138f7b..ad52e8a0b9ba 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10_swvp.dts +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_swvp.dts @@ -68,7 +68,6 @@ &gmac0 { &gmac1 { status = "okay"; phy-mode = "rgmii"; - phy-addr = <0xffffffff>; }; &gmac2 { @@ -103,12 +102,7 @@ &usb1 { status = "okay"; }; -&rst { - altr,modrst-offset = <0x20>; -}; - &sysmgr { reg = <0xffd12000 0x1000>; interrupts = <0x0 0x10 0x4>; - cpu1-start-addr = <0xffd06230>; }; diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile index 15e7901c1268..619dce79b020 100644 --- a/arch/arm64/boot/dts/amlogic/Makefile +++ b/arch/arm64/boot/dts/amlogic/Makefile @@ -80,6 +80,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-q200.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-q201.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-rbox-pro.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-s912-libretech-pc.dtb +dtb-$(CONFIG_ARCH_MESON) += meson-gxm-ugoos-am3.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-vega-s96.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-wetek-core2.dtb dtb-$(CONFIG_ARCH_MESON) += meson-s4-s805x2-aq222.dtb diff --git a/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi index a8c90245c42a..5f602f1170c0 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi +++ b/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include / { cpus { #address-cells = <2>; @@ -92,6 +93,102 @@ uart_b: serial@7a000 { clock-names = "xtal", "pclk", "baud"; status = "disabled"; }; + + periphs_pinctrl: pinctrl@4000 { + compatible = "amlogic,pinctrl-s6"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x4000 0x0 0x340>; + + gpioz: gpio@c0 { + reg = <0 0xc0 0 0x20>, <0 0x18 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_Z<<8) 16>; + }; + + gpiox: gpio@100 { + reg = <0 0x100 0 0x30>, <0 0xc 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_X<<8) 20>; + }; + + gpioh: gpio@140 { + reg = <0 0x140 0 0x20>, <0 0x2c 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_H<<8) 9>; + }; + + gpiod: gpio@180 { + reg = <0 0x180 0 0x20>, <0 0x8 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_D<<8) 7>; + }; + + gpiof: gpio@1a0 { + reg = <0 0x1a0 0 0x20>, <0 0x20 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_F<<8) 5>; + }; + + gpioe: gpio@1c0 { + reg = <0 0x1c0 0 0x20>, <0 0x48 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_E<<8) 3>; + }; + + gpioc: gpio@200 { + reg = <0 0x200 0 0x20>, <0 0x24 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_C<<8) 8>; + }; + + gpiob: gpio@240 { + reg = <0 0x240 0 0x20>, <0 0x0 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_B<<8) 14>; + }; + + gpioa: gpio@280 { + reg = <0 0x280 0 0x20>, <0 0x40 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_A<<8) 16>; + }; + + test_n: gpio@2c0 { + reg = <0 0x2c0 0 0x20>; + reg-names = "gpio"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = + <&periphs_pinctrl 0 (AMLOGIC_GPIO_TEST_N<<8) 1>; + }; + + gpiocc: gpio@300 { + reg = <0 0x300 0 0x20>, <0 0x14 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_CC<<8) 2>; + }; + }; }; }; }; diff --git a/arch/arm64/boot/dts/amlogic/amlogic-s7.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-s7.dtsi index f0c172681bd1..260918b37b9a 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-s7.dtsi +++ b/arch/arm64/boot/dts/amlogic/amlogic-s7.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include / { cpus { @@ -94,6 +95,86 @@ uart_b: serial@7a000 { clock-names = "xtal", "pclk", "baud"; status = "disabled"; }; + + periphs_pinctrl: pinctrl@4000 { + compatible = "amlogic,pinctrl-s7"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x4000 0x0 0x340>; + + gpioz: gpio@c0 { + reg = <0 0xc0 0 0x20>, <0 0x18 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_Z<<8) 13>; + }; + + gpiox: gpio@100 { + reg = <0 0x100 0 0x30>, <0 0xc 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_X<<8) 20>; + }; + + gpioh: gpio@140 { + reg = <0 0x140 0 0x20>, <0 0x2c 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_H<<8) 12>; + }; + + gpiod: gpio@180 { + reg = <0 0x180 0 0x20>, <0 0x40 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_D<<8) 12>; + }; + + gpioe: gpio@1c0 { + reg = <0 0x1c0 0 0x20>, <0 0x48 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_E<<8) 2>; + }; + + gpioc: gpio@200 { + reg = <0 0x200 0 0x20>, <0 0x24 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_C<<8) 8>; + }; + + gpiob: gpio@240 { + reg = <0 0x240 0 0x20>, <0 0x0 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_B<<8) 14>; + }; + + test_n: gpio@2c0 { + reg = <0 0x2c0 0 0x20>; + reg-names = "gpio"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = + <&periphs_pinctrl 0 (AMLOGIC_GPIO_TEST_N<<8) 1>; + }; + + gpiocc: gpio@300 { + reg = <0 0x300 0 0x20>, <0 0x14 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_CC<<8) 2>; + }; + }; }; }; }; diff --git a/arch/arm64/boot/dts/amlogic/amlogic-s7d.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-s7d.dtsi index e1099bc1535d..c4d260d5bb58 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-s7d.dtsi +++ b/arch/arm64/boot/dts/amlogic/amlogic-s7d.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include / { cpus { @@ -94,6 +95,95 @@ uart_b: serial@7a000 { clock-names = "xtal", "pclk", "baud"; status = "disabled"; }; + + periphs_pinctrl: pinctrl@4000 { + compatible = "amlogic,pinctrl-s7d", + "amlogic,pinctrl-s7"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x4000 0x0 0x340>; + + gpioz: gpio@c0 { + reg = <0 0xc0 0 0x20>, <0 0x18 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_Z<<8) 13>; + }; + + gpiox: gpio@100 { + reg = <0 0x100 0 0x30>, <0 0xc 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_X<<8) 20>; + }; + + gpioh: gpio@140 { + reg = <0 0x140 0 0x20>, <0 0x2c 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_H<<8) 12>; + }; + + gpiod: gpio@180 { + reg = <0 0x180 0 0x20>, <0 0x40 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_D<<8) 5>; + }; + + gpioe: gpio@1c0 { + reg = <0 0x1c0 0 0x20>, <0 0x48 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_E<<8) 2>; + }; + + gpioc: gpio@200 { + reg = <0 0x200 0 0x20>, <0 0x24 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_C<<8) 8>; + }; + + gpiob: gpio@240 { + reg = <0 0x240 0 0x20>, <0 0x0 0 0x8>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_B<<8) 14>; + }; + + gpiodv: gpio@280 { + reg = <0 0x280 0 0x20>, <0 0x8 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_DV<<8) 7>; + }; + + test_n: gpio@2c0 { + reg = <0 0x2c0 0 0x20>; + reg-names = "gpio"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = + <&periphs_pinctrl 0 (AMLOGIC_GPIO_TEST_N<<8) 1>; + }; + + gpiocc: gpio@300 { + reg = <0 0x300 0 0x20>, <0 0x14 0 0x4>; + reg-names = "gpio", "mux"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&periphs_pinctrl 0 (AMLOGIC_GPIO_CC<<8) 2>; + }; + }; }; }; }; diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-khadas-vim3.dts b/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-khadas-vim3.dts index 124a80901084..9fd68195be3f 100644 --- a/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-khadas-vim3.dts +++ b/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-khadas-vim3.dts @@ -39,3 +39,7 @@ &usb { phy-names = "usb2-phy0", "usb2-phy1"; }; */ + +&npu { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-libretech-cc.dts index 415248931ab1..82546b738977 100644 --- a/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-libretech-cc.dts +++ b/arch/arm64/boot/dts/amlogic/meson-g12b-a311d-libretech-cc.dts @@ -111,3 +111,7 @@ &cpu103 { &pwm_ab { pinctrl-0 = <&pwm_a_e_pins>, <&pwm_b_x7_pins>; }; + +&npu { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi index 6da1316d97c6..b4f88ed6273b 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi @@ -97,7 +97,7 @@ sdio_pwrseq: sdio-pwrseq { clock-names = "ext_clock"; }; - cvbs-connector { + cvbs_connector: cvbs-connector { compatible = "composite-video-connector"; port { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts index ecaf678b23dd..9d5a481b309f 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts @@ -217,7 +217,7 @@ &sd_emmc_a { vmmc-supply = <&vddao_3v3>; vqmmc-supply = <&vddio_boot>; - brcmf: brcmf@1 { + brcmf: wifi@1 { reg = <1>; compatible = "brcm,bcm4329-fmac"; }; diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-ugoos-am3.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-ugoos-am3.dts new file mode 100644 index 000000000000..ba871f3f53bb --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-ugoos-am3.dts @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 J. Neuschäfer + * + * Debug UART (3.3V, 115200 baud) at the corner of the board: + * (4) (3) (2) [1] + * Vcc RXD TXD GND + */ + +/dts-v1/; + +#include +#include + +#include "meson-gxm.dtsi" +#include "meson-gx-p23x-q20x.dtsi" + +/ { + compatible = "ugoos,am3", "amlogic,s912", "amlogic,meson-gxm"; + model = "Ugoos AM3"; + + adc-keys { + compatible = "adc-keys"; + io-channels = <&saradc 0>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <1710000>; + + button-function { + label = "Update"; + linux,code = ; + press-threshold-microvolt = <10000>; + }; + }; +}; + +&cvbs_connector { + /* Not used on this board */ + status = "disabled"; +}; + +ðmac { + pinctrl-0 = <ð_pins>; + pinctrl-names = "default"; + + /* Select external PHY by default */ + phy-handle = <&external_phy>; + + amlogic,tx-delay-ns = <2>; + + /* External PHY is in RGMII */ + phy-mode = "rgmii"; + + status = "okay"; +}; + +&external_mdio { + external_phy: ethernet-phy@0 { + /* Realtek RTL8211F (0x001cc916) */ + reg = <0>; + + reset-assert-us = <10000>; + reset-deassert-us = <80000>; + reset-gpios = <&gpio GPIOZ_14 GPIO_ACTIVE_LOW>; + + interrupt-parent = <&gpio_intc>; + /* MAC_INTR on GPIOZ_15 */ + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; + }; +}; + +&i2c_B { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c_b_pins>; + + rtc: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; + #clock-cells = <0>; + }; +}; + +/* WLAN: Atheros 10k (QCA9377) */ +&sd_emmc_a { + max-frequency = <200000000>; +}; + +/* eMMC */ +&sd_emmc_c { + max-frequency = <100000000>; +}; diff --git a/arch/arm64/boot/dts/apple/spi1-nvram.dtsi b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi index 3df2fd3993b5..9740fbf200f0 100644 --- a/arch/arm64/boot/dts/apple/spi1-nvram.dtsi +++ b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi @@ -20,8 +20,6 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0x0>; spi-max-frequency = <25000000>; - #address-cells = <1>; - #size-cells = <1>; partitions { compatible = "fixed-partitions"; diff --git a/arch/arm64/boot/dts/apple/t6000.dtsi b/arch/arm64/boot/dts/apple/t6000.dtsi index 89c3b211b116..0ad77c98073f 100644 --- a/arch/arm64/boot/dts/apple/t6000.dtsi +++ b/arch/arm64/boot/dts/apple/t6000.dtsi @@ -16,3 +16,7 @@ / { }; /delete-node/ &pmgr_south; + +&gpu { + compatible = "apple,agx-g13s"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi index d2cf81926f28..ffbe823b71bc 100644 --- a/arch/arm64/boot/dts/apple/t6001.dtsi +++ b/arch/arm64/boot/dts/apple/t6001.dtsi @@ -62,3 +62,7 @@ p-core-pmu-affinity { }; }; }; + +&gpu { + compatible = "apple,agx-g13c", "apple,agx-g13s"; +}; diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index e36f422d257d..8fb648836b53 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -300,3 +300,7 @@ &ps_gfx { // On t6002, the die0 GPU power domain needs both AFR power domains power-domains = <&ps_afr>, <&ps_afr_die1>; }; + +&gpu { + compatible = "apple,agx-g13d", "apple,agx-g13s"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 87dfc13d7417..e20234ef2135 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -11,6 +11,10 @@ / { #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -378,4 +382,34 @@ nco_clkref: clock-ref-nco { #clock-cells = <0>; clock-output-names = "nco_ref"; }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gpu_globals: globals { + status = "disabled"; + }; + + gpu_hw_cal_a: hw-cal-a { + status = "disabled"; + }; + + gpu_hw_cal_b: hw-cal-b { + status = "disabled"; + }; + + uat_handoff: uat-handoff { + status = "disabled"; + }; + + uat_pagetables: uat-pagetables { + status = "disabled"; + }; + + uat_ttbs: uat-ttbs { + status = "disabled"; + }; + }; }; diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 110bc6719512..1563b3ce1ff6 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -72,12 +72,12 @@ boot_stage: boot-stage@6001 { reg = <0x6001 0x1>; }; - boot_error_count: boot-error-count@6002 { + boot_error_count: boot-error-count@6002,0 { reg = <0x6002 0x1>; bits = <0 4>; }; - panic_count: panic-count@6002 { + panic_count: panic-count@6002,4 { reg = <0x6002 0x1>; bits = <4 4>; }; @@ -86,7 +86,7 @@ boot_error_stage: boot-error-stage@6003 { reg = <0x6003 0x1>; }; - shutdown_flag: shutdown-flag@600f { + shutdown_flag: shutdown-flag@600f,3 { reg = <0x600f 0x1>; bits = <3 1>; }; @@ -302,6 +302,34 @@ mca: mca@39b600000 { #sound-dai-cells = <1>; }; + gpu: gpu@406400000 { + compatible = "apple,agx-g13s"; + reg = <0x4 0x6400000 0 0x40000>, + <0x4 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, + <&gpu_hw_cal_a>, <&gpu_hw_cal_b>, <&gpu_globals>; + memory-region-names = "ttbs", "pagetables", "handoff", + "hw-cal-a", "hw-cal-b", "globals"; + + apple,firmware-abi = <0 0 0>; + }; + + agx_mbox: mbox@406408000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x4 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + pcie0_dart_0: iommu@581008000 { compatible = "apple,t6000-dart"; reg = <0x5 0x81008000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t8012-j132.dts b/arch/arm64/boot/dts/apple/t8012-j132.dts index 778a69be18dd..7dcac51703ff 100644 --- a/arch/arm64/boot/dts/apple/t8012-j132.dts +++ b/arch/arm64/boot/dts/apple/t8012-j132.dts @@ -7,6 +7,7 @@ /dts-v1/; #include "t8012-jxxx.dtsi" +#include "t8012-touchbar.dtsi" / { model = "Apple T2 MacBookPro15,2 (j132)"; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index e2d9439397f7..5b3c42e9f0e6 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -100,6 +100,8 @@ dfr_mipi_out_panel: endpoint@0 { &displaydfr_mipi { status = "okay"; + #address-cells = <1>; + #size-cells = <0>; dfr_panel: panel@0 { compatible = "apple,j293-summit", "apple,summit"; diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 8e82231acab5..0c8206156bfe 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -71,7 +71,7 @@ hpm1: usb-pd@3f { */ &port00 { bus-range = <1 1>; - wifi0: network@0,0 { + wifi0: wifi@0,0 { compatible = "pci14e4,4425"; reg = <0x10000 0x0 0x0 0x0 0x0>; /* To be filled by the loader */ diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 20faf0c0d809..589ddc039799 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -19,6 +19,10 @@ / { #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -351,6 +355,36 @@ nco_clkref: clock-ref-nco { clock-output-names = "nco_ref"; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gpu_globals: globals { + status = "disabled"; + }; + + gpu_hw_cal_a: hw-cal-a { + status = "disabled"; + }; + + gpu_hw_cal_b: hw-cal-b { + status = "disabled"; + }; + + uat_handoff: uat-handoff { + status = "disabled"; + }; + + uat_pagetables: uat-pagetables { + status = "disabled"; + }; + + uat_ttbs: uat-ttbs { + status = "disabled"; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -359,6 +393,34 @@ soc { ranges; nonposted-mmio; + gpu: gpu@206400000 { + compatible = "apple,agx-g13g"; + reg = <0x2 0x6400000 0 0x40000>, + <0x2 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, + <&gpu_hw_cal_a>, <&gpu_hw_cal_b>, <&gpu_globals>; + memory-region-names = "ttbs", "pagetables", "handoff", + "hw-cal-a", "hw-cal-b", "globals"; + + apple,firmware-abi = <0 0 0>; + }; + + agx_mbox: mbox@206408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + cpufreq_e: performance-controller@210e20000 { compatible = "apple,t8103-cluster-cpufreq", "apple,cluster-cpufreq"; reg = <0x2 0x10e20000 0 0x1000>; @@ -405,8 +467,6 @@ displaydfr_mipi: dsi@228600000 { compatible = "apple,t8103-display-pipe-mipi", "apple,h7-display-pipe-mipi"; reg = <0x2 0x28600000 0x0 0x100000>; power-domains = <&ps_mipi_dsi>; - #address-cells = <1>; - #size-cells = <0>; status = "disabled"; ports { @@ -761,12 +821,12 @@ boot_stage: boot-stage@9f01 { reg = <0x9f01 0x1>; }; - boot_error_count: boot-error-count@9f02 { + boot_error_count: boot-error-count@9f02,0 { reg = <0x9f02 0x1>; bits = <0 4>; }; - panic_count: panic-count@9f02 { + panic_count: panic-count@9f02,4 { reg = <0x9f02 0x1>; bits = <4 4>; }; @@ -775,7 +835,7 @@ boot_error_stage: boot-error-stage@9f03 { reg = <0x9f03 0x1>; }; - shutdown_flag: shutdown-flag@9f0f { + shutdown_flag: shutdown-flag@9f0f,3 { reg = <0x9f0f 0x1>; bits = <3 1>; }; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index be86d34c6696..fb8ad7d4c65a 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -63,6 +63,8 @@ dfr_mipi_out_panel: endpoint@0 { &displaydfr_mipi { status = "okay"; + #address-cells = <1>; + #size-cells = <0>; dfr_panel: panel@0 { compatible = "apple,j493-summit", "apple,summit"; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index e95711d8337f..b36b345861b6 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -19,6 +19,10 @@ / { #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -366,6 +370,36 @@ nco_clkref: clock-ref-nco { clock-output-names = "nco_ref"; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gpu_globals: globals { + status = "disabled"; + }; + + gpu_hw_cal_a: hw-cal-a { + status = "disabled"; + }; + + gpu_hw_cal_b: hw-cal-b { + status = "disabled"; + }; + + uat_handoff: uat-handoff { + status = "disabled"; + }; + + uat_pagetables: uat-pagetables { + status = "disabled"; + }; + + uat_ttbs: uat-ttbs { + status = "disabled"; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -374,6 +408,34 @@ soc { ranges; nonposted-mmio; + gpu: gpu@206400000 { + compatible = "apple,agx-g14g"; + reg = <0x2 0x6400000 0 0x40000>, + <0x2 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, + <&gpu_hw_cal_a>, <&gpu_hw_cal_b>, <&gpu_globals>; + memory-region-names = "ttbs", "pagetables", "handoff", + "hw-cal-a", "hw-cal-b", "globals"; + + apple,firmware-abi = <0 0 0>; + }; + + agx_mbox: mbox@206408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + cpufreq_e: cpufreq@210e20000 { compatible = "apple,t8112-cluster-cpufreq", "apple,cluster-cpufreq"; reg = <0x2 0x10e20000 0 0x1000>; @@ -420,8 +482,6 @@ displaydfr_mipi: dsi@228600000 { compatible = "apple,t8112-display-pipe-mipi", "apple,h7-display-pipe-mipi"; reg = <0x2 0x28600000 0x0 0x100000>; power-domains = <&ps_mipi_dsi>; - #address-cells = <1>; - #size-cells = <0>; status = "disabled"; ports { @@ -809,12 +869,12 @@ boot_stage: boot-stage@f701 { reg = <0xf701 0x1>; }; - boot_error_count: boot-error-count@f702 { + boot_error_count: boot-error-count@f702,0 { reg = <0xf702 0x1>; bits = <0 4>; }; - panic_count: panic-count@f702 { + panic_count: panic-count@f702,4 { reg = <0xf702 0x1>; bits = <4 4>; }; @@ -823,7 +883,7 @@ boot_error_stage: boot-error-stage@f703 { reg = <0xf703 0x1>; }; - shutdown_flag: shutdown-flag@f70f { + shutdown_flag: shutdown-flag@f70f,3 { reg = <0xf70f 0x1>; bits = <3 1>; }; diff --git a/arch/arm64/boot/dts/axiado/Makefile b/arch/arm64/boot/dts/axiado/Makefile new file mode 100644 index 000000000000..6676ad07db61 --- /dev/null +++ b/arch/arm64/boot/dts/axiado/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_AXIADO) += ax3000-evk.dtb diff --git a/arch/arm64/boot/dts/axiado/ax3000-evk.dts b/arch/arm64/boot/dts/axiado/ax3000-evk.dts new file mode 100644 index 000000000000..b86e96962557 --- /dev/null +++ b/arch/arm64/boot/dts/axiado/ax3000-evk.dts @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021-25 Axiado Corporation (or its affiliates). All rights reserved. + */ + +/dts-v1/; + +#include "ax3000.dtsi" + +/ { + model = "Axiado AX3000 EVK"; + compatible = "axiado,ax3000-evk", "axiado,ax3000"; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + }; + + chosen { + stdout-path = "serial3:115200"; + }; + + memory@0 { + device_type = "memory"; + /* Cortex-A53 will use following memory map */ + reg = <0x00000000 0x3d000000 0x00000000 0x23000000>, + <0x00000004 0x00000000 0x00000000 0x80000000>; + }; +}; + +/* GPIO bank 0 - 7 */ +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&gpio6 { + status = "okay"; +}; + +&gpio7 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/axiado/ax3000.dtsi b/arch/arm64/boot/dts/axiado/ax3000.dtsi new file mode 100644 index 000000000000..792f52e0c7dd --- /dev/null +++ b/arch/arm64/boot/dts/axiado/ax3000.dtsi @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021-25 Axiado Corporation (or its affiliates). All rights reserved. + */ + +/dts-v1/; + +#include +#include + +/memreserve/ 0x3c0013a0 0x00000008; /* cpu-release-addr */ +/ { + model = "Axiado AX3000"; + interrupt-parent = <&gic500>; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x3c0013a0>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + next-level-cache = <&l2>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x3c0013a0>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + next-level-cache = <&l2>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x2>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x3c0013a0>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + next-level-cache = <&l2>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x3>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x3c0013a0>; + d-cache-size = <0x8000>; + d-cache-line-size = <64>; + d-cache-sets = <128>; + i-cache-size = <0x8000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + next-level-cache = <&l2>; + }; + + l2: l2-cache0 { + compatible = "cache"; + cache-size = <0x100000>; + cache-unified; + cache-line-size = <64>; + cache-sets = <1024>; + cache-level = <2>; + }; + }; + + clocks { + clk_xin: clock-200000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_xin"; + }; + + refclk: clock-125000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + }; + }; + + soc { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic500>; + + gic500: interrupt-controller@80300000 { + compatible = "arm,gic-v3"; + reg = <0x00 0x80300000 0x00 0x10000>, + <0x00 0x80380000 0x00 0x80000>; + ranges; + #interrupt-cells = <3>; + #address-cells = <2>; + #size-cells = <2>; + interrupt-controller; + #redistributor-regions = <1>; + interrupts = ; + }; + + /* GPIO Controller banks 0 - 7 */ + gpio0: gpio-controller@80500000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80500000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio1: gpio-controller@80580000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80580000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio2: gpio-controller@80600000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80600000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio3: gpio-controller@80680000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80680000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio4: gpio-controller@80700000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80700000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio5: gpio-controller@80780000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80780000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio6: gpio-controller@80800000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80800000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + gpio7: gpio-controller@80880000 { + compatible = "axiado,ax3000-gpio", "cdns,gpio-r1p02"; + reg = <0x00 0x80880000 0x00 0x400>; + clocks = <&refclk>; + interrupt-parent = <&gic500>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + /* I3C Controller 0 - 16 */ + i3c0: i3c@80500400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80500400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c1: i3c@80500800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80500800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c2: i3c@80580400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80580400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c3: i3c@80580800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80580800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c4: i3c@80600400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80600400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c5: i3c@80600800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80600800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c6: i3c@80680400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80680400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c7: i3c@80680800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80680800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c8: i3c@80700400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80700400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c9: i3c@80700800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80700800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c10: i3c@80780400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80780400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c11: i3c@80780800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80780800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c12: i3c@80800400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80800400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c13: i3c@80800800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80800800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c14: i3c@80880400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80880400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c15: i3c@80880800 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80880800 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + i3c16: i3c@80620400 { + compatible = "axiado,ax3000-i3c", "cdns,i3c-master"; + reg = <0x00 0x80620400 0x00 0x400>; + clocks = <&refclk &clk_xin>; + clock-names = "pclk", "sysclk"; + interrupt-parent = <&gic500>; + interrupts = ; + i2c-scl-hz = <100000>; + i3c-scl-hz = <400000>; + #address-cells = <3>; + #size-cells = <0>; + status = "disabled"; + }; + + uart0: serial@80520000 { + compatible = "axiado,ax3000-uart", "cdns,uart-r1p12"; + reg = <0x00 0x80520000 0x00 0x100>; + interrupt-parent = <&gic500>; + interrupts = ; + clock-names = "uart_clk", "pclk"; + clocks = <&refclk &refclk>; + status = "disabled"; + }; + + uart1: serial@805a0000 { + compatible = "axiado,ax3000-uart", "cdns,uart-r1p12"; + reg = <0x00 0x805A0000 0x00 0x100>; + interrupt-parent = <&gic500>; + interrupts = ; + clock-names = "uart_clk", "pclk"; + clocks = <&refclk &refclk>; + status = "disabled"; + }; + + uart2: serial@80620000 { + compatible = "axiado,ax3000-uart", "cdns,uart-r1p12"; + reg = <0x00 0x80620000 0x00 0x100>; + interrupt-parent = <&gic500>; + interrupts = ; + clock-names = "uart_clk", "pclk"; + clocks = <&refclk &refclk>; + status = "disabled"; + }; + + uart3: serial@80520800 { + compatible = "axiado,ax3000-uart", "cdns,uart-r1p12"; + reg = <0x00 0x80520800 0x00 0x100>; + interrupt-parent = <&gic500>; + interrupts = ; + clock-names = "uart_clk", "pclk"; + clocks = <&refclk &refclk>; + status = "disabled"; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic500>; + interrupts = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/Makefile b/arch/arm64/boot/dts/broadcom/Makefile index 01ecfa304184..83d45afc6588 100644 --- a/arch/arm64/boot/dts/broadcom/Makefile +++ b/arch/arm64/boot/dts/broadcom/Makefile @@ -7,13 +7,15 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-400.dtb \ bcm2711-rpi-4-b.dtb \ bcm2711-rpi-cm4-io.dtb \ bcm2712-rpi-5-b.dtb \ + bcm2712-rpi-5-b-ovl-rp1.dtb \ bcm2712-d-rpi-5-b.dtb \ bcm2837-rpi-2-b.dtb \ bcm2837-rpi-3-a-plus.dtb \ bcm2837-rpi-3-b.dtb \ bcm2837-rpi-3-b-plus.dtb \ bcm2837-rpi-cm3-io3.dtb \ - bcm2837-rpi-zero-2-w.dtb + bcm2837-rpi-zero-2-w.dtb \ + rp1.dtbo subdir-y += bcmbca subdir-y += northstar2 diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-ovl-rp1.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-ovl-rp1.dts new file mode 100644 index 000000000000..6ea3c102e0d6 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-ovl-rp1.dts @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/dts-v1/; + +#include +#include "bcm2712.dtsi" + +/ { + compatible = "raspberrypi,5-model-b", "brcm,bcm2712"; + model = "Raspberry Pi 5"; + + aliases { + serial10 = &uart10; + }; + + chosen: chosen { + stdout-path = "serial10:115200n8"; + }; + + clk_rp1_xosc: clock-50000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "rp1-xosc"; + clock-frequency = <50000000>; + }; + + /* Will be filled by the bootloader */ + memory@0 { + device_type = "memory"; + reg = <0 0 0 0x28000000>; + }; + + sd_io_1v8_reg: sd-io-1v8-reg { + compatible = "regulator-gpio"; + regulator-name = "vdd-sd-io"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + regulator-settling-time-us = <5000>; + gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>; + states = <1800000 1>, + <3300000 0>; + }; + + sd_vcc_reg: sd-vcc-reg { + compatible = "regulator-fixed"; + regulator-name = "vcc-sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; + }; +}; + +/* The Debug UART, on Rpi5 it's on JST-SH 1.0mm 3-pin connector + * labeled "UART", i.e. the interface with the system console. + */ +&uart10 { + status = "okay"; +}; + +/* SDIO1 is used to drive the SD card */ +&sdio1 { + vqmmc-supply = <&sd_io_1v8_reg>; + vmmc-supply = <&sd_vcc_reg>; + bus-width = <4>; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr104; +}; + +&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>; + }; +}; + +&hvs { + clocks = <&firmware_clocks 4>, <&firmware_clocks 16>; + clock-names = "core", "disp"; +}; + +&hdmi0 { + clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>; + clock-names = "hdmi", "bvb", "audio", "cec"; +}; + +&hdmi1 { + clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>; + clock-names = "hdmi", "bvb", "audio", "cec"; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "okay"; +}; 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 34470e3d7171..a70a9b158df3 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -1,108 +1,19 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * bcm2712-rpi-5-b-ovl-rp1.dts is the overlay-ready DT which will make + * the RP1 driver to load the RP1 dtb overlay at runtime, while + * bcm2712-rpi-5-b.dts (this file) is the fully defined one (i.e. it + * already contains RP1 node, so no overlay is loaded nor needed). + * This file is not intended to be modified, nodes should be added + * to the included bcm2712-rpi-5-b-ovl-rp1.dts. + */ + /dts-v1/; -#include -#include "bcm2712.dtsi" +#include "bcm2712-rpi-5-b-ovl-rp1.dts" -/ { - compatible = "raspberrypi,5-model-b", "brcm,bcm2712"; - model = "Raspberry Pi 5"; - - aliases { - serial10 = &uart10; - }; - - chosen: chosen { - stdout-path = "serial10:115200n8"; - }; - - /* Will be filled by the bootloader */ - memory@0 { - device_type = "memory"; - reg = <0 0 0 0x28000000>; - }; - - sd_io_1v8_reg: sd-io-1v8-reg { - compatible = "regulator-gpio"; - regulator-name = "vdd-sd-io"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - regulator-settling-time-us = <5000>; - gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>; - states = <1800000 1>, - <3300000 0>; - }; - - sd_vcc_reg: sd-vcc-reg { - compatible = "regulator-fixed"; - regulator-name = "vcc-sd"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - enable-active-high; - gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; - }; -}; - -/* The Debug UART, on Rpi5 it's on JST-SH 1.0mm 3-pin connector - * labeled "UART", i.e. the interface with the system console. - */ -&uart10 { - status = "okay"; -}; - -/* SDIO1 is used to drive the SD card */ -&sdio1 { - vqmmc-supply = <&sd_io_1v8_reg>; - vmmc-supply = <&sd_vcc_reg>; - bus-width = <4>; - sd-uhs-sdr50; - sd-uhs-ddr50; - sd-uhs-sdr104; -}; - -&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>; - }; -}; - -&hvs { - clocks = <&firmware_clocks 4>, <&firmware_clocks 16>; - clock-names = "core", "disp"; -}; - -&hdmi0 { - clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>; - clock-names = "hdmi", "bvb", "audio", "cec"; -}; - -&hdmi1 { - clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>; - clock-names = "hdmi", "bvb", "audio", "cec"; +&pcie2 { + #include "rp1-nexus.dtsi" }; &pcie1 { diff --git a/arch/arm64/boot/dts/broadcom/bcmbca/bcm4908.dtsi b/arch/arm64/boot/dts/broadcom/bcmbca/bcm4908.dtsi index 613ba7ee43d6..3b7595fd4e81 100644 --- a/arch/arm64/boot/dts/broadcom/bcmbca/bcm4908.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcmbca/bcm4908.dtsi @@ -323,11 +323,12 @@ pmb: power-controller@2800c0 { }; }; + /* PERF Peripherals */ bus@ff800000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; - ranges = <0x00 0x00 0xff800000 0x3000>; + ranges = <0x00 0x00 0xff800000 0x400000>; twd: timer-mfd@400 { compatible = "brcm,bcm4908-twd", "simple-mfd", "syscon"; @@ -348,13 +349,103 @@ watchdog@28 { }; }; - gpio0: gpio-controller@500 { + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x528 0x04>; reg-names = "dirout", "dat"; - reg = <0x500 0x28>, <0x528 0x28>; - - #gpio-cells = <2>; gpio-controller; + #gpio-cells = <2>; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x540 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x544 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 256 .. 287 */ + gpio8: gpio@520 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x520 0x04>, <0x548 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 288 .. 319 */ + gpio9: gpio@524 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x524 0x04>, <0x54c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; }; pinctrl@560 { @@ -584,6 +675,12 @@ leds: leds@800 { #size-cells = <0>; }; + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -636,6 +733,19 @@ reset-controller@2644 { #reset-cells = <1>; }; }; + + pl081_dma: dma-controller@59000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0x59000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; }; reboot { diff --git a/arch/arm64/boot/dts/broadcom/bcmbca/bcm63158.dtsi b/arch/arm64/boot/dts/broadcom/bcmbca/bcm63158.dtsi index 48d618e75866..a441388c0cd2 100644 --- a/arch/arm64/boot/dts/broadcom/bcmbca/bcm63158.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcmbca/bcm63158.dtsi @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright 2022 Broadcom Ltd. + * This DTSI is for the B0 and later revision of the SoC */ #include @@ -125,6 +126,101 @@ bus@ff800000 { #size-cells = <1>; ranges = <0x0 0x0 0xff800000 0x800000>; + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + + leds: led-controller@800 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x800 0xdc>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -151,6 +247,21 @@ nandcs: nand@0 { }; }; + /* B0 AHB Peripherals */ + pl081_dma: dma-controller@11000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0x11000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; + + /* B0 ARM UART Peripheral block */ uart0: serial@12000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x12000 0x1000>; @@ -159,5 +270,23 @@ uart0: serial@12000 { clock-names = "uartclk", "apb_pclk"; status = "disabled"; }; + + uart1: serial@13000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x13000 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&uart_clk>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart2: serial@14000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x14000 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&uart_clk>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; }; }; diff --git a/arch/arm64/boot/dts/broadcom/bcmbca/bcm6856.dtsi b/arch/arm64/boot/dts/broadcom/bcmbca/bcm6856.dtsi index 00c62c1e5df0..dcbd0fdd33d2 100644 --- a/arch/arm64/boot/dts/broadcom/bcmbca/bcm6856.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcmbca/bcm6856.dtsi @@ -93,11 +93,103 @@ gic: interrupt-controller@1000 { }; }; + /* PERF Peripherals */ bus@ff800000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; - ranges = <0x0 0x0 0xff800000 0x800000>; + ranges = <0x0 0x0 0xff800000 0x400000>; + + watchdog@480 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x480 0x10>; + }; + + watchdog@4c0 { + compatible = "brcm,bcm6345-wdt"; + reg = <0x4c0 0x10>; + status = "disabled"; + }; + + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; uart0: serial@640 { compatible = "brcm,bcm6345-uart"; @@ -108,6 +200,29 @@ uart0: serial@640 { status = "disabled"; }; + uart1: serial@660 { + compatible = "brcm,bcm6345-uart"; + reg = <0x660 0x18>; + interrupts = ; + clocks = <&periph_clk>; + clock-names = "refclk"; + status = "disabled"; + }; + + leds: led-controller@800 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x800 0xdc>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -133,5 +248,18 @@ nandcs: nand@0 { reg = <0>; }; }; + + pl081_dma: dma-controller@59000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0x59000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; }; }; diff --git a/arch/arm64/boot/dts/broadcom/bcmbca/bcm6858.dtsi b/arch/arm64/boot/dts/broadcom/bcmbca/bcm6858.dtsi index caeaf428dc15..c105a734a648 100644 --- a/arch/arm64/boot/dts/broadcom/bcmbca/bcm6858.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcmbca/bcm6858.dtsi @@ -111,11 +111,12 @@ gic: interrupt-controller@1000 { }; }; + /* PERF Peripherals */ bus@ff800000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; - ranges = <0x0 0x0 0xff800000 0x62000>; + ranges = <0x0 0x0 0xff800000 0x400000>; twd: timer-mfd@400 { compatible = "brcm,bcm4908-twd", "simple-mfd", "syscon"; @@ -136,6 +137,86 @@ watchdog@28 { }; }; + /* GPIOs 0 .. 31 */ + gpio0: gpio@500 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x500 0x04>, <0x520 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 32 .. 63 */ + gpio1: gpio@504 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x504 0x04>, <0x524 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 64 .. 95 */ + gpio2: gpio@508 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x508 0x04>, <0x528 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 96 .. 127 */ + gpio3: gpio@50c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x50c 0x04>, <0x52c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 128 .. 159 */ + gpio4: gpio@510 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x510 0x04>, <0x530 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 160 .. 191 */ + gpio5: gpio@514 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x514 0x04>, <0x534 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 192 .. 223 */ + gpio6: gpio@518 { + compatible = "brcm,bcm6345-gpio"; + reg = <0x518 0x04>, <0x538 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + /* GPIOs 224 .. 255 */ + gpio7: gpio@51c { + compatible = "brcm,bcm6345-gpio"; + reg = <0x51c 0x04>, <0x53c 0x04>; + reg-names = "dirout", "dat"; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + uart0: serial@640 { compatible = "brcm,bcm6345-uart"; reg = <0x640 0x18>; @@ -145,6 +226,29 @@ uart0: serial@640 { status = "disabled"; }; + uart1: serial@660 { + compatible = "brcm,bcm6345-uart"; + reg = <0x660 0x18>; + interrupts = ; + clocks = <&periph_clk>; + clock-names = "refclk"; + status = "disabled"; + }; + + leds: led-controller@800 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm63138-leds"; + reg = <0x800 0xdc>; + status = "disabled"; + }; + + rng@b80 { + compatible = "brcm,iproc-rng200"; + reg = <0xb80 0x28>; + interrupts = ; + }; + hsspi: spi@1000 { #address-cells = <1>; #size-cells = <0>; @@ -170,5 +274,18 @@ nandcs: nand@0 { reg = <0>; }; }; + + pl081_dma: dma-controller@59000 { + compatible = "arm,pl081", "arm,primecell"; + // The magic B105F00D info is missing + arm,primecell-periphid = <0x00041081>; + reg = <0x59000 0x1000>; + interrupts = ; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + clocks = <&periph_clk>; + clock-names = "apb_pclk"; + #dma-cells = <2>; + }; }; }; diff --git a/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi b/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi index 5a4b81faff20..9888a1fabd5c 100644 --- a/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi +++ b/arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi @@ -367,7 +367,6 @@ gic: interrupt-controller@65210000 { v2m0: v2m@0 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x00000 0x1000>; arm,msi-base-spi = <72>; @@ -376,7 +375,6 @@ v2m0: v2m@0 { v2m1: v2m@10000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x10000 0x1000>; arm,msi-base-spi = <88>; @@ -385,7 +383,6 @@ v2m1: v2m@10000 { v2m2: v2m@20000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x20000 0x1000>; arm,msi-base-spi = <104>; @@ -394,7 +391,6 @@ v2m2: v2m@20000 { v2m3: v2m@30000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x30000 0x1000>; arm,msi-base-spi = <120>; @@ -403,7 +399,6 @@ v2m3: v2m@30000 { v2m4: v2m@40000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x40000 0x1000>; arm,msi-base-spi = <136>; @@ -412,7 +407,6 @@ v2m4: v2m@40000 { v2m5: v2m@50000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x50000 0x1000>; arm,msi-base-spi = <152>; @@ -421,7 +415,6 @@ v2m5: v2m@50000 { v2m6: v2m@60000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x60000 0x1000>; arm,msi-base-spi = <168>; @@ -430,7 +423,6 @@ v2m6: v2m@60000 { v2m7: v2m@70000 { compatible = "arm,gic-v2m-frame"; - interrupt-parent = <&gic>; msi-controller; reg = <0x70000 0x1000>; arm,msi-base-spi = <184>; diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi new file mode 100644 index 000000000000..5002a375eb0b --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +#include +#include +#include + +pci_ep_bus: pci-ep-bus@1 { + compatible = "simple-bus"; + ranges = <0x00 0x40000000 0x01 0x00 0x00000000 0x00 0x00400000>; + dma-ranges = <0x10 0x00000000 0x43000000 0x10 0x00000000 0x10 0x00000000>; + #address-cells = <2>; + #size-cells = <2>; + + rp1_clocks: clocks@40018000 { + compatible = "raspberrypi,rp1-clocks"; + reg = <0x00 0x40018000 0x0 0x10038>; + #clock-cells = <1>; + clocks = <&clk_rp1_xosc>; + assigned-clocks = <&rp1_clocks RP1_PLL_SYS_CORE>, + <&rp1_clocks RP1_PLL_SYS>, + <&rp1_clocks RP1_PLL_SYS_SEC>, + <&rp1_clocks RP1_CLK_SYS>; + assigned-clock-rates = <1000000000>, // RP1_PLL_SYS_CORE + <200000000>, // RP1_PLL_SYS + <125000000>, // RP1_PLL_SYS_SEC + <200000000>; // RP1_CLK_SYS + }; + + rp1_gpio: pinctrl@400d0000 { + compatible = "raspberrypi,rp1-gpio"; + reg = <0x00 0x400d0000 0x0 0xc000>, + <0x00 0x400e0000 0x0 0xc000>, + <0x00 0x400f0000 0x0 0xc000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>, + <1 IRQ_TYPE_LEVEL_HIGH>, + <2 IRQ_TYPE_LEVEL_HIGH>; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/rp1-nexus.dtsi b/arch/arm64/boot/dts/broadcom/rp1-nexus.dtsi new file mode 100644 index 000000000000..0ef30d7f1c35 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/rp1-nexus.dtsi @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +rp1_nexus { + compatible = "pci1de4,1"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01 0x00 0x00000000 + 0x02000000 0x00 0x00000000 + 0x0 0x400000>; + interrupt-controller; + #interrupt-cells = <2>; + + #include "rp1-common.dtsi" +}; diff --git a/arch/arm64/boot/dts/broadcom/rp1.dtso b/arch/arm64/boot/dts/broadcom/rp1.dtso new file mode 100644 index 000000000000..ab4f146d22c0 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/rp1.dtso @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; +/plugin/; + +&pcie2 { + #address-cells = <3>; + #size-cells = <2>; + + #include "rp1-nexus.dtsi" +}; diff --git a/arch/arm64/boot/dts/cavium/thunder2-99xx.dtsi b/arch/arm64/boot/dts/cavium/thunder2-99xx.dtsi index 6dfe78a7d4ab..966fb57280f3 100644 --- a/arch/arm64/boot/dts/cavium/thunder2-99xx.dtsi +++ b/arch/arm64/boot/dts/cavium/thunder2-99xx.dtsi @@ -136,8 +136,8 @@ uart0: serial@402020000 { reg = <0x04 0x02020000 0x0 0x1000>; interrupt-parent = <&gic>; interrupts = ; - clocks = <&clk125mhz>; - clock-names = "apb_pclk"; + clocks = <&clk125mhz>, <&clk125mhz>; + clock-names = "uartclk", "apb_pclk"; }; }; diff --git a/arch/arm64/boot/dts/cix/Makefile b/arch/arm64/boot/dts/cix/Makefile new file mode 100644 index 000000000000..ed3713982012 --- /dev/null +++ b/arch/arm64/boot/dts/cix/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_CIX) += sky1-orion-o6.dtb diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts new file mode 100644 index 000000000000..d74964d53c3b --- /dev/null +++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2025 Cix Technology Group Co., Ltd. + * + */ + +/dts-v1/; + +#include "sky1.dtsi" +/ { + model = "Radxa Orion O6"; + compatible = "radxa,orion-o6", "cix,sky1"; + + aliases { + serial2 = &uart2; + }; + + chosen { + stdout-path = &uart2; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x0 0x28000000>; + linux,cma-default; + }; + }; + +}; + +&uart2 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi new file mode 100644 index 000000000000..7dfe7677e649 --- /dev/null +++ b/arch/arm64/boot/dts/cix/sky1.dtsi @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2025 Cix Technology Group Co., Ltd. + * + */ + +#include +#include + +/ { + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a520"; + enable-method = "psci"; + reg = <0x0 0x0>; + device_type = "cpu"; + capacity-dmips-mhz = <403>; + }; + + cpu1: cpu@100 { + compatible = "arm,cortex-a520"; + enable-method = "psci"; + reg = <0x0 0x100>; + device_type = "cpu"; + capacity-dmips-mhz = <403>; + }; + + cpu2: cpu@200 { + compatible = "arm,cortex-a520"; + enable-method = "psci"; + reg = <0x0 0x200>; + device_type = "cpu"; + capacity-dmips-mhz = <403>; + }; + + cpu3: cpu@300 { + compatible = "arm,cortex-a520"; + enable-method = "psci"; + reg = <0x0 0x300>; + device_type = "cpu"; + capacity-dmips-mhz = <403>; + }; + + cpu4: cpu@400 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x400>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu5: cpu@500 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x500>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu6: cpu@600 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x600>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu7: cpu@700 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x700>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu8: cpu@800 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x800>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu9: cpu@900 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0x900>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu10: cpu@a00 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0xa00>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + cpu11: cpu@b00 { + compatible = "arm,cortex-a720"; + enable-method = "psci"; + reg = <0x0 0xb00>; + device_type = "cpu"; + capacity-dmips-mhz = <1024>; + }; + + 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>; + }; + core8 { + cpu = <&cpu8>; + }; + core9 { + cpu = <&cpu9>; + }; + core10 { + cpu = <&cpu10>; + }; + core11 { + cpu = <&cpu11>; + }; + }; + }; + }; + + firmware { + ap_to_pm_scmi: scmi { + compatible = "arm,scmi"; + mbox-names = "tx", "rx"; + mboxes = <&mbox_ap2pm 8>, <&mbox_pm2ap 8>; + shmem = <&ap2pm_scmi_mem>, <&pm2ap_scmi_mem>; + #address-cells = <1>; + #size-cells = <0>; + + scmi_clk: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; + }; + }; + + pmu-a520 { + compatible = "arm,cortex-a520-pmu"; + interrupts = ; + }; + + pmu-a720 { + compatible = "arm,cortex-a720-pmu"; + interrupts = ; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + soc@0 { + compatible = "simple-bus"; + ranges = <0 0 0 0 0x20 0>; + dma-ranges; + #address-cells = <2>; + #size-cells = <2>; + + uart0: serial@40b0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x040b0000 0x0 0x1000>; + interrupts = ; + clocks = <&scmi_clk CLK_TREE_FCH_UART0_FUNC>, <&scmi_clk CLK_TREE_FCH_UART0_APB>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart1: serial@40c0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x040c0000 0x0 0x1000>; + interrupts = ; + clocks = <&scmi_clk CLK_TREE_FCH_UART1_FUNC>, <&scmi_clk CLK_TREE_FCH_UART1_APB>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart2: serial@40d0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x040d0000 0x0 0x1000>; + interrupts = ; + clocks = <&scmi_clk CLK_TREE_FCH_UART2_FUNC>, <&scmi_clk CLK_TREE_FCH_UART2_APB>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart3: serial@40e0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x040e0000 0x0 0x1000>; + interrupts = ; + clocks = <&scmi_clk CLK_TREE_FCH_UART3_FUNC>, <&scmi_clk CLK_TREE_FCH_UART3_APB>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + mbox_ap2se: mailbox@5060000 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x05060000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "tx"; + }; + + mbox_se2ap: mailbox@5070000 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x05070000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "rx"; + }; + + ap2pm_scmi_mem: shmem@6590000 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x06590000 0x0 0x80>; + reg-io-width = <4>; + }; + + mbox_ap2pm: mailbox@6590080 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x06590080 0x0 0xff80>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "tx"; + }; + + pm2ap_scmi_mem: shmem@65a0000 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x065a0000 0x0 0x80>; + reg-io-width = <4>; + }; + + mbox_pm2ap: mailbox@65a0080 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x065a0080 0x0 0xff80>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "rx"; + }; + + mbox_sfh2ap: mailbox@8090000 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x08090000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "rx"; + }; + + mbox_ap2sfh: mailbox@80a0000 { + compatible = "cix,sky1-mbox"; + reg = <0x0 0x080a0000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <1>; + cix,mbox-dir = "tx"; + }; + + gic: interrupt-controller@e010000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x0e010000 0 0x10000>, /* GICD */ + <0x0 0x0e090000 0 0x300000>; /* GICR * 12 */ + interrupts = ; + #interrupt-cells = <4>; + interrupt-controller; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic_its: msi-controller@e050000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x0e050000 0x0 0x30000>; + msi-controller; + #msi-cells = <1>; + }; + + ppi-partitions { + ppi_partition0: interrupt-partition-0 { + affinity = <&cpu0 &cpu1 &cpu2 &cpu3>; + }; + + ppi_partition1: interrupt-partition-1 { + affinity = <&cpu4 &cpu5 &cpu6 &cpu7 &cpu8 &cpu9 &cpu10 &cpu11>; + }; + }; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-names = "sec-phys", "phys", "virt", "hyp-phys", "hyp-virt"; + interrupts = , + , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/Makefile b/arch/arm64/boot/dts/exynos/Makefile index 89c90564c3d8..bdb9e9813e50 100644 --- a/arch/arm64/boot/dts/exynos/Makefile +++ b/arch/arm64/boot/dts/exynos/Makefile @@ -2,6 +2,7 @@ subdir-y += google dtb-$(CONFIG_ARCH_EXYNOS) += \ + exynos2200-g0s.dtb \ exynos5433-tm2.dtb \ exynos5433-tm2e.dtb \ exynos7-espresso.dtb \ diff --git a/arch/arm64/boot/dts/exynos/exynos2200-g0s.dts b/arch/arm64/boot/dts/exynos/exynos2200-g0s.dts new file mode 100644 index 000000000000..0e348c5cf7df --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynos2200-g0s.dts @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Samsung Galaxy S22+ (g0s/SM-S906B) device tree source + * + * Copyright (c) 2025, Ivaylo Ivanov + */ + +/dts-v1/; +#include "exynos2200.dtsi" +#include +#include +#include + +/ { + model = "Samsung Galaxy S22+ (SM-S906B)"; + compatible = "samsung,g0s", "samsung,exynos2200"; + chassis-type = "handset"; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + framebuffer: framebuffer { + compatible = "simple-framebuffer"; + memory-region = <&cont_splash_mem>; + width = <1080>; + height = <2340>; + stride = <(1080 * 4)>; + format = "a8r8g8b8"; + }; + }; + + /* + * RTC clock (XrtcXTI); external, must be 32.768 kHz. + * + * TODO: Remove this once RTC clock is implemented properly as part of + * PMIC driver. + */ + rtcclk: clock-rtcclk { + compatible = "fixed-clock"; + clock-output-names = "rtcclk"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&key_volup>; + pinctrl-names = "default"; + + volup-key { + label = "Volume Up"; + linux,code = ; + gpios = <&gpa3 0 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>, + <0x8 0x80000000 0x1 0x7e000000>; + }; + + /* TODO: Remove this once PMIC is implemented */ + reg_dummy: regulator-0 { + compatible = "regulator-fixed"; + regulator-name = "dummy_reg"; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cont_splash_mem: framebuffer@f6200000 { + reg = <0x0 0xf6200000 0x0 (1080 * 2340 * 4)>; + no-map; + }; + + debug_kinfo_reserved: debug-kinfo-reserved@fcfff000 { + reg = <0x0 0xfcfff000 0x0 0x1000>; + no-map; + }; + + log_itmon: log-itmon@fffe0000 { + reg = <0x0 0xfffe0000 0x0 0x20000>; + no-map; + }; + }; +}; + +&cmu_hsi0 { + clocks = <&xtcxo>, + <&rtcclk>, + <&cmu_top CLK_DOUT_CMU_HSI0_NOC>, + <&cmu_top CLK_DOUT_CMU_HSI0_DPGTC>, + <&cmu_top CLK_DOUT_CMU_HSI0_DPOSC>, + <&cmu_top CLK_DOUT_CMU_HSI0_USB32DRD>; + clock-names = "oscclk", "rtcclk", "noc", "dpgtc", "dposc", "usb"; +}; + +/* + * cpu2 and cpu3 fail to come up consistently, which leads to a hang later + * in the boot process. Disable them until the issue is figured out. + */ +&cpu2 { + status = "fail"; +}; + +&cpu3 { + status = "fail"; +}; + +&ext_26m { + clock-frequency = <26000000>; +}; + +&ext_200m { + clock-frequency = <200000000>; +}; + +&mct_peris { + status = "okay"; +}; + +&pinctrl_alive { + key_volup: key-volup-pins { + samsung,pins = "gpa3-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&ppi_cluster0 { + affinity = <&cpu0 &cpu1>; +}; + +&usb { + /* TODO: Replace these once PMIC is implemented */ + vdd10-supply = <®_dummy>; + vdd33-supply = <®_dummy>; + status = "okay"; +}; + +&usb32drd { + status = "okay"; +}; + +&usb_dwc3 { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + maximum-speed = "high-speed"; +}; + +&usb_hsphy { + /* TODO: Replace these once PMIC is implemented */ + vdda12-supply = <®_dummy>; + vdd-supply = <®_dummy>; + status = "okay"; +}; + +&xtcxo { + clock-frequency = <76800000>; +}; diff --git a/arch/arm64/boot/dts/exynos/exynos2200-pinctrl.dtsi b/arch/arm64/boot/dts/exynos/exynos2200-pinctrl.dtsi new file mode 100644 index 000000000000..f618ff290604 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynos2200-pinctrl.dtsi @@ -0,0 +1,1765 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Samsung's Exynos 2200 SoC pin-mux and pin-config device tree source + * + * Copyright (c) 2025, Ivaylo Ivanov + */ + +#include +#include "exynos-pinctrl.h" + +&pinctrl_alive { + gpa0: gpa0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + }; + + gpa1: gpa1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + }; + + gpa2: gpa2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + }; + + gpa3: gpa3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + }; + + gpa4: gpa4-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpq0: gpq0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + }; + + gpq1: gpq1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + }; + + gpq2: gpq2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + }; + + bt_hostwake: bt-hostwake-pins { + samsung,pins = "gpa0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart1_bus: uart1-bus-pins { + samsung,pins = "gpq0-3", "gpq0-2", "gpq0-1", "gpq0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + wlan_host_wake: wlan-host-wake-pins { + samsung,pins = "gpa0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; +}; + +&pinctrl_cmgp { + gpm0: gpm0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm1: gpm1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm2: gpm2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm3: gpm3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm4: gpm4-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm5: gpm5-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm6: gpm6-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm7: gpm7-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm8: gpm8-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm9: gpm9-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm10: gpm10-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm11: gpm11-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm12: gpm12-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupts = , + ; + }; + + gpm13: gpm13-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + gpm14: gpm14-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm15: gpm15-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm16: gpm16-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm17: gpm17-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm20: gpm20-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm21: gpm21-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm22: gpm22-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm23: gpm23-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + gpm24: gpm24-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + hsi2c24_bus: hsi2c24-bus-pins { + samsung,pins = "gpm0-0", "gpm0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c25_bus: hsi2c25-bus-pins { + samsung,pins = "gpm1-0", "gpm1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c26_bus: hsi2c26-bus-pins { + samsung,pins = "gpm2-0", "gpm2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c27_bus: hsi2c27-bus-pins { + samsung,pins = "gpm3-0", "gpm3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c28_bus: hsi2c28-bus-pins { + samsung,pins = "gpm4-0", "gpm4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c29_bus: hsi2c29-bus-pins { + samsung,pins = "gpm5-0", "gpm5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c30_bus: hsi2c30-bus-pins { + samsung,pins = "gpm6-0", "gpm6-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c31_bus: hsi2c31-bus-pins { + samsung,pins = "gpm7-0", "gpm7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c32_bus: hsi2c32-bus-pins { + samsung,pins = "gpm8-0", "gpm8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c33_bus: hsi2c33-bus-pins { + samsung,pins = "gpm9-0", "gpm9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c34_bus: hsi2c34-bus-pins { + samsung,pins = "gpm10-0", "gpm10-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c35_bus: hsi2c35-bus-pins { + samsung,pins = "gpm11-0", "gpm11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c36_bus: hsi2c36-bus-pins { + samsung,pins = "gpm12-0", "gpm12-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c37_bus: hsi2c37-bus-pins { + samsung,pins = "gpm13-0", "gpm13-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c38_bus: hsi2c38-bus-pins { + samsung,pins = "gpm23-0", "gpm24-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi12_bus: spi12-bus-pins { + samsung,pins = "gpm0-0", "gpm0-1", "gpm1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi12_cs: spi12-cs-pins { + samsung,pins = "gpm1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi12_cs_func: spi12-cs-func-pins { + samsung,pins = "gpm1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi13_bus: spi13-bus-pins { + samsung,pins = "gpm2-0", "gpm2-1", "gpm3-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi13_cs: spi13-cs-pins { + samsung,pins = "gpm3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi13_cs_func: spi13-cs-func-pins { + samsung,pins = "gpm3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi14_bus: spi14-bus-pins { + samsung,pins = "gpm4-0", "gpm4-1", "gpm5-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi14_cs: spi14-cs-pins { + samsung,pins = "gpm5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi14_cs_func: spi14-cs-func-pins { + samsung,pins = "gpm5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi15_bus: spi15-bus-pins { + samsung,pins = "gpm6-0", "gpm6-1", "gpm7-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi15_cs: spi15-cs-pins { + samsung,pins = "gpm7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi15_cs_func: spi15-cs-func-pins { + samsung,pins = "gpm7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi16_bus: spi16-bus-pins { + samsung,pins = "gpm8-0", "gpm8-1", "gpm9-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi16_cs: spi16-cs-pins { + samsung,pins = "gpm9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi16_cs_func: spi16-cs-func-pins { + samsung,pins = "gpm9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi17_bus: spi17-bus-pins { + samsung,pins = "gpm10-0", "gpm10-1", "gpm11-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi17_cs: spi17-cs-pins { + samsung,pins = "gpm11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi17_cs_func: spi17-cs-func-pins { + samsung,pins = "gpm11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi18_bus: spi18-bus-pins { + samsung,pins = "gpm12-0", "gpm12-1", "gpm13-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi18_cs: spi18-cs-pins { + samsung,pins = "gpm13-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi18_cs_func: spi18-cs-func-pins { + samsung,pins = "gpm13-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart14_bus_single: uart14-bus-single-pins { + samsung,pins = "gpm0-0", "gpm0-1", "gpm2-0", "gpm2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart14_bus_dual: uart14-bus-dual-pins { + samsung,pins = "gpm0-0", "gpm0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart15_bus_single: uart15-bus-single-pins { + samsung,pins = "gpm3-0", "gpm3-1", "gpm4-0", "gpm4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart15_bus_dual: uart15-bus-dual-pins { + samsung,pins = "gpm3-0", "gpm3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart16_bus_single: uart16-bus-single-pins { + samsung,pins = "gpm5-0", "gpm5-1", "gpm6-0", "gpm6-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart16_bus_dual: uart16-bus-dual-pins { + samsung,pins = "gpm5-0", "gpm5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart17_bus_single: uart17-bus-single-pins { + samsung,pins = "gpm7-0", "gpm7-1", "gpm8-0", "gpm8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart17_bus_dual: uart17-bus-dual-pins { + samsung,pins = "gpm7-0", "gpm7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart18_bus_single: uart18-bus-single-pins { + samsung,pins = "gpm8-0", "gpm8-1", "gpm9-0", "gpm9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart18_bus_dual: uart18-bus-dual-pins { + samsung,pins = "gpm8-0", "gpm8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart19_bus_single: uart19-bus-single-pins { + samsung,pins = "gpm10-0", "gpm10-1", "gpm11-0", "gpm11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart19_bus_dual: uart19-bus-dual-pins { + samsung,pins = "gpm12-0", "gpm12-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart20_bus_single: uart20-bus-single-pins { + samsung,pins = "gpm13-0", "gpm13-1", "gpm14-0", "gpm14-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart20_bus_dual: uart20-bus-dual-pins { + samsung,pins = "gpm13-0", "gpm13-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + +}; + +&pinctrl_hsi1 { + gpf0: gpf0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pcie0_clkreq: pcie0-clkreq-pins { + samsung,pins = "gpf0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + pcie0_perst: pcie0-perst-pins { + samsung,pins = "gpf0-1"; + samsung,pin-function = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + }; + + pcie1_clkreq: pcie1-clkreq-pins { + samsung,pins = "gpf0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + pcie1_perst: pcie1-perst-pins { + samsung,pins = "gpf0-3"; + samsung,pin-function = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + }; +}; + +&pinctrl_hsi1ufs { + gpf2: gpf2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + ufs_rst_n: ufs-rst-n-pins { + samsung,pins = "gpf2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + ufs_refclk_out: ufs-refclk-out-pins { + samsung,pins = "gpf2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + samsung,pin-drv = ; + samsung,pin-pud-pdn = ; + }; +}; + +&pinctrl_peric0 { + gpb0: gpb0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpb1: gpb1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpb2: gpb2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpb3: gpb3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc0: gpc0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc1: gpc1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc2: gpc2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpg1: gpg1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpg2: gpg2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp4: gpp4-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + aud_i2s0_bus: aud-i2s0-bus-pins { + samsung,pins = "gpb0-0", "gpb0-1", "gpb0-2", "gpb0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + aud_i2s1_bus: aud-i2s1-bus-pins { + samsung,pins = "gpb1-0", "gpb1-1", "gpb1-2", "gpb1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + aud_i2s2_bus: aud-i2s2-bus-pins { + samsung,pins = "gpb2-0", "gpb2-1", "gpb2-2", "gpb2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + aud_i2s3_bus: aud-i2s3-bus-pins { + samsung,pins = "gpb3-0", "gpb3-1", "gpb3-2", "gpb3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + aud_i2s3_pci: aud-i2s3-pci-pins { + samsung,pins = "gpb3-0", "gpb3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + aud_dsd_bus: aud-dsd-bus-pins { + samsung,pins = "gpb1-0", "gpb1-1", "gpb1-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + decon_0_te: decon-0-te-pins { + samsung,pins = "gpg2-0"; + samsung,pin-function = ; + }; + + hsi2c8_bus: hsi2c8-bus-pins { + samsung,pins = "gpp4-0", "gpp4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c9_bus: hsi2c9-bus-pins { + samsung,pins = "gpp4-2", "gpp4-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c0_bus: i3c0-bus-pins { + samsung,pins = "gpc0-0", "gpc0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c1_bus: i3c1-bus-pins { + samsung,pins = "gpc1-0", "gpc1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c2_bus: i3c2-bus-pins { + samsung,pins = "gpc2-0", "gpc2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + spi4_bus: spi4-bus-pins { + samsung,pins = "gpp4-2", "gpp4-1", "gpp4-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi4_cs: spi4-cs-pins { + samsung,pins = "gpp4-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi4_cs_func: spi4-cs-func-pins { + samsung,pins = "gpp4-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart6_bus_single: uart6-bus-single-pins { + samsung,pins = "gpp4-0", "gpp4-1", "gpp4-2", "gpp4-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart6_bus_dual: uart6-bus-dual-pins { + samsung,pins = "gpp4-0", "gpp4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; +}; + +&pinctrl_peric1 { + gpp7: gpp7-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp8: gpp8-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp9: gpp9-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp10: gpp10-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + hsi2c14_bus: hsi2c14-bus-pins { + samsung,pins = "gpp7-0", "gpp7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c15_bus: hsi2c15-bus-pins { + samsung,pins = "gpp7-2", "gpp7-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c16_bus: hsi2c16-bus-pins { + samsung,pins = "gpp8-0", "gpp8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c17_bus: hsi2c17-bus-pins { + samsung,pins = "gpp8-2", "gpp8-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c18_bus: hsi2c18-bus-pins { + samsung,pins = "gpp9-0", "gpp9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c19_bus: hsi2c19-bus-pins { + samsung,pins = "gpp9-2", "gpp9-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c20_bus: hsi2c20-bus-pins { + samsung,pins = "gpp10-0", "gpp10-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c21_bus: hsi2c21-bus-pins { + samsung,pins = "gpp10-2", "gpp10-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi7_bus: spi7-bus-pins { + samsung,pins = "gpp7-2", "gpp7-1", "gpp7-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi7_cs: spi7-cs-pins { + samsung,pins = "gpp7-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi7_cs_func: spi7-cs-func-pins { + samsung,pins = "gpp7-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi8_bus: spi8-bus-pins { + samsung,pins = "gpp8-2", "gpp8-1", "gpp8-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi8_cs: spi8-cs-pins { + samsung,pins = "gpp8-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi8_cs_func: spi8-cs-func-pins { + samsung,pins = "gpp8-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi9_bus: spi9-bus-pins { + samsung,pins = "gpp9-2", "gpp9-1", "gpp9-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi9_cs: spi9-cs-pins { + samsung,pins = "gpp9-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi9_cs_func: spi9-cs-func-pins { + samsung,pins = "gpp9-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi10_bus: spi10-bus-pins { + samsung,pins = "gpp10-2", "gpp10-1", "gpp10-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi10_cs: spi10-cs-pins { + samsung,pins = "gpp10-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi10_cs_func: spi10-cs-func-pins { + samsung,pins = "gpp10-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart9_bus_single: uart9-bus-single-pins { + samsung,pins = "gpp7-3", "gpp7-2", "gpp7-1", "gpp7-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart9_bus_dual: uart9-bus-dual-pins { + samsung,pins = "gpp7-0", "gpp7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart10_bus_single: uart10-bus-single-pins { + samsung,pins = "gpp8-3", "gpp8-2", "gpp8-1", "gpp8-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart10_bus_dual: uart10-bus-dual-pins { + samsung,pins = "gpp8-0", "gpp8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart11_bus_single: uart11-bus-single-pins { + samsung,pins = "gpp9-3", "gpp9-2", "gpp9-1", "gpp9-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart11_bus_dual: uart11-bus-dual-pins { + samsung,pins = "gpp9-0", "gpp9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart12_bus_single: uart12-bus-single-pins { + samsung,pins = "gpp10-3", "gpp10-2", "gpp10-1", "gpp10-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart12_bus_dual: uart12-bus-dual-pins { + samsung,pins = "gpp10-0", "gpp10-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + +}; + +&pinctrl_peric2 { + gpc3: gpc3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc4: gpc4-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc5: gpc5-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc6: gpc6-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc7: gpc7-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc8: gpc8-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc9: gpc9-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpg0: gpg0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp0: gpp0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp1: gpp1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp2: gpp2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp3: gpp3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp5: gpp5-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp6: gpp6-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpp11: gpp11-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + hsi2c0_bus: hsi2c0-bus-pins { + samsung,pins = "gpp0-0", "gpp0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c1_bus: hsi2c1-bus-pins { + samsung,pins = "gpp0-2", "gpp0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c2_bus: hsi2c2-bus-pins { + samsung,pins = "gpp1-0", "gpp1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c3_bus: hsi2c3-bus-pins { + samsung,pins = "gpp1-2", "gpp1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c4_bus: hsi2c4-bus-pins { + samsung,pins = "gpp2-0", "gpp2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c5_bus: hsi2c5-bus-pins { + samsung,pins = "gpp2-2", "gpp2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c6_bus: hsi2c6-bus-pins { + samsung,pins = "gpp3-0", "gpp3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c7_bus: hsi2c7-bus-pins { + samsung,pins = "gpp3-2", "gpp3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c10_bus: hsi2c10-bus-pins { + samsung,pins = "gpp5-0", "gpp5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c11_bus: hsi2c11-bus-pins { + samsung,pins = "gpp5-2", "gpp5-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c12_bus: hsi2c12-bus-pins { + samsung,pins = "gpp6-0", "gpp6-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c13_bus: hsi2c13-bus-pins { + samsung,pins = "gpp6-2", "gpp6-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi2c22_bus: hsi2c22-bus-pins { + samsung,pins = "gpp11-0", "gpp11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c3_bus: i3c3-bus-pins { + samsung,pins = "gpc3-0", "gpc3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c4_bus: i3c4-bus-pins { + samsung,pins = "gpc4-0", "gpc4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c5_bus: i3c5-bus-pins { + samsung,pins = "gpc5-0", "gpc5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + i3c6_bus: i3c6-bus-pins { + samsung,pins = "gpc6-0", "gpc6-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c7_bus: i3c7-bus-pins { + samsung,pins = "gpc7-0", "gpc7-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c8_bus: i3c8-bus-pins { + samsung,pins = "gpc8-0", "gpc8-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c9_bus: i3c9-bus-pins { + samsung,pins = "gpc9-0", "gpc9-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; + + i3c10_bus: i3c10-bus-pins { + samsung,pins = "gpp2-2", "gpp2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i3c11_bus: i3c11-bus-pins { + samsung,pins = "gpp3-2", "gpp3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hsi223_bus: hsi2c23-bus-pins { + samsung,pins = "gpp11-2", "gpp11-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi0_bus: spi0-bus-pins { + samsung,pins = "gpp0-2", "gpp0-1", "gpp0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi0_cs: spi0-cs-pins { + samsung,pins = "gpp0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi0_cs_func: spi0-cs-func-pins { + samsung,pins = "gpp0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi1_bus: spi1-bus-pins { + samsung,pins = "gpp1-2", "gpp1-1", "gpp1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi1_cs: spi1-cs-pins { + samsung,pins = "gpp1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi1_cs_func: spi1-cs-func-pins { + samsung,pins = "gpp1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi2_bus: spi2-bus-pins { + samsung,pins = "gpp2-2", "gpp2-1", "gpp2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi2_cs: spi2-cs-pins { + samsung,pins = "gpp2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi2_cs_func: spi2-cs-func-pins { + samsung,pins = "gpp2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi3_bus: spi3-bus-pins { + samsung,pins = "gpp3-2", "gpp3-1", "gpp3-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi3_cs: spi3-cs-pins { + samsung,pins = "gpp3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi3_cs_func: spi3-cs-func-pins { + samsung,pins = "gpp3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi5_bus: spi5-bus-pins { + samsung,pins = "gpp5-2", "gpp5-1", "gpp5-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi5_cs: spi5-cs-pins { + samsung,pins = "gpp5-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi5_cs_func: spi5-cs-func-pins { + samsung,pins = "gpp5-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi6_bus: spi6-bus-pins { + samsung,pins = "gpp6-2", "gpp6-1", "gpp6-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi6_cs: spi6-cs-pins { + samsung,pins = "gpp6-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi6_cs_func: spi6-cs-func-pins { + samsung,pins = "gpp6-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi11_bus: spi11-bus-pins { + samsung,pins = "gpp11-2", "gpp11-1", "gpp11-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi11_cs: spi11-cs-pins { + samsung,pins = "gpp11-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi11_cs_func: spi11-cs-func-pins { + samsung,pins = "gpp11-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart0_bus_single: uart0-bus-single-pins { + samsung,pins = "gpg0-2", "gpg0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart2_bus_single: uart2-bus-single-pins { + samsung,pins = "gpp0-0", "gpp0-1", "gpp0-2", "gpp0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart2_bus_dual: uart2-bus-dual-pins { + samsung,pins = "gpp0-0", "gpp0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart3_bus_single: uart3-bus-single-pins { + samsung,pins = "gpp1-0", "gpp1-1", "gpp1-2", "gpp1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart3_bus_dual: uart3-bus-dual-pins { + samsung,pins = "gpp1-0", "gpp1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart4_bus_single: uart4-bus-single-pins { + samsung,pins = "gpp2-0", "gpp2-1", "gpp2-2", "gpp2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart4_bus_dual: uart4-bus-dual-pins { + samsung,pins = "gpp2-0", "gpp2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart5_bus_single: uart5-bus-single-pins { + samsung,pins = "gpp3-0", "gpp3-1", "gpp3-2", "gpp3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart5_bus_dual: uart5-bus-dual-pins { + samsung,pins = "gpp3-0", "gpp3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart7_bus_single: uart7-bus-single-pins { + samsung,pins = "gpp5-0", "gpp5-1", "gpp5-2", "gpp5-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart7_bus_dual: uart7-bus-dual-pins { + samsung,pins = "gpp5-0", "gpp5-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart8_bus_single: uart8-bus-single-pins { + samsung,pins = "gpp6-3", "gpp6-2", "gpp6-1", "gpp6-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart8_bus_dual: uart8-bus-dual-pins { + samsung,pins = "gpp6-0", "gpp6-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart13_bus_single: uart13-bus-single-pins { + samsung,pins = "gpp11-3", "gpp11-2", "gpp11-1", "gpp11-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + uart13_bus_dual: uart13-bus-dual-pins { + samsung,pins = "gpp11-0", "gpp11-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; +}; + +&pinctrl_ufs { + gpf1: gpf1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; +}; + +&pinctrl_vts { + gpv0: gpv0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + amic_pdm0_bus: amic-pdm0-bus-pins { + samsung,pins = "gpv0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + amic_pdm1_bus: amic-pdm1-bus-pins { + samsung,pins = "gpv0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + amic_pdm2_bus: amic-pdm2-bus-pins { + samsung,pins = "gpv0-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_bus_clk0: dmic-bus-clk0-pins { + samsung,pins = "gpv0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_bus_clk1: dmic-bus-clk1-pins { + samsung,pins = "gpv0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_bus_clk2: dmic-bus-clk2-pins { + samsung,pins = "gpv0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_pdm0_bus: dmic-pdm0-bus-pins { + samsung,pins = "gpv0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_pdm1_bus: dmic-pdm1-bus-pins { + samsung,pins = "gpv0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; + + dmic_pdm2_bus: dmic-pdm2-bus-pins { + samsung,pins = "gpv0-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/exynos2200.dtsi b/arch/arm64/boot/dts/exynos/exynos2200.dtsi new file mode 100644 index 000000000000..6b5ac02d010f --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynos2200.dtsi @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Samsung's Exynos 2200 SoC device tree source + * + * Copyright (c) 2025, Ivaylo Ivanov + */ + +#include +#include + +/ { + compatible = "samsung,exynos2200"; + #address-cells = <2>; + #size-cells = <2>; + + interrupt-parent = <&gic>; + + aliases { + pinctrl0 = &pinctrl_alive; + pinctrl1 = &pinctrl_cmgp; + pinctrl2 = &pinctrl_hsi1; + pinctrl3 = &pinctrl_ufs; + pinctrl4 = &pinctrl_hsi1ufs; + pinctrl5 = &pinctrl_peric0; + pinctrl6 = &pinctrl_peric1; + pinctrl7 = &pinctrl_peric2; + pinctrl8 = &pinctrl_vts; + }; + + xtcxo: clock-1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "oscclk"; + }; + + ext_26m: clock-2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "ext-26m"; + }; + + ext_200m: clock-3 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "ext-200m"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + 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>; + }; + }; + + cluster2 { + core0 { + cpu = <&cpu7>; + }; + }; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a510"; + reg = <0>; + capacity-dmips-mhz = <260>; + dynamic-power-coefficient = <189>; + enable-method = "psci"; + cpu-idle-states = <&little_cpu_sleep>; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a510"; + reg = <0x100>; + capacity-dmips-mhz = <260>; + dynamic-power-coefficient = <189>; + enable-method = "psci"; + cpu-idle-states = <&little_cpu_sleep>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a510"; + reg = <0x200>; + capacity-dmips-mhz = <260>; + dynamic-power-coefficient = <189>; + enable-method = "psci"; + cpu-idle-states = <&little_cpu_sleep>; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a510"; + reg = <0x300>; + capacity-dmips-mhz = <260>; + dynamic-power-coefficient = <189>; + enable-method = "psci"; + cpu-idle-states = <&little_cpu_sleep>; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "arm,cortex-a710"; + reg = <0x400>; + capacity-dmips-mhz = <380>; + dynamic-power-coefficient = <560>; + enable-method = "psci"; + cpu-idle-states = <&big_cpu_sleep>; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "arm,cortex-a710"; + reg = <0x500>; + capacity-dmips-mhz = <380>; + dynamic-power-coefficient = <560>; + enable-method = "psci"; + cpu-idle-states = <&big_cpu_sleep>; + }; + + cpu6: cpu@600 { + device_type = "cpu"; + compatible = "arm,cortex-a710"; + reg = <0x600>; + capacity-dmips-mhz = <380>; + dynamic-power-coefficient = <560>; + enable-method = "psci"; + cpu-idle-states = <&big_cpu_sleep>; + }; + + cpu7: cpu@700 { + device_type = "cpu"; + compatible = "arm,cortex-x2"; + reg = <0x700>; + capacity-dmips-mhz = <488>; + dynamic-power-coefficient = <765>; + enable-method = "psci"; + cpu-idle-states = <&prime_cpu_sleep>; + }; + + idle-states { + entry-method = "psci"; + + little_cpu_sleep: cpu-sleep-0 { + compatible = "arm,idle-state"; + idle-state-name = "c2"; + entry-latency-us = <70>; + exit-latency-us = <170>; + min-residency-us = <2000>; + arm,psci-suspend-param = <0x10000>; + }; + + big_cpu_sleep: cpu-sleep-1 { + compatible = "arm,idle-state"; + idle-state-name = "c2"; + entry-latency-us = <235>; + exit-latency-us = <220>; + min-residency-us = <3500>; + arm,psci-suspend-param = <0x10000>; + }; + + prime_cpu_sleep: cpu-sleep-2 { + compatible = "arm,idle-state"; + idle-state-name = "c2"; + entry-latency-us = <150>; + exit-latency-us = <190>; + min-residency-us = <2500>; + arm,psci-suspend-param = <0x10000>; + }; + }; + }; + + pmu-a510 { + compatible = "arm,cortex-a510-pmu"; + interrupts = ; + }; + + pmu-a710 { + compatible = "arm,cortex-a710-pmu"; + interrupts = ; + }; + + pmu-x2 { + compatible = "arm,cortex-x2-pmu"; + interrupts = ; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + soc { + compatible = "simple-bus"; + ranges; + + #address-cells = <2>; + #size-cells = <2>; + + chipid@10000000 { + compatible = "samsung,exynos2200-chipid", + "samsung,exynos850-chipid"; + reg = <0x0 0x10000000 0x0 0x24>; + }; + + cmu_peris: clock-controller@10020000 { + compatible = "samsung,exynos2200-cmu-peris"; + reg = <0x0 0x10020000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&cmu_top CLK_DOUT_TCXO_DIV3>, + <&cmu_top CLK_DOUT_CMU_PERIS_NOC>, + <&cmu_top CLK_DOUT_CMU_PERIS_GIC>; + clock-names = "tcxo_div3", + "noc", + "gic"; + }; + + mct_peris: timer@10040000 { + compatible = "samsung,exynos2200-mct-peris", + "samsung,exynos4210-mct"; + reg = <0x0 0x10040000 0x0 0x800>; + clocks = <&cmu_top CLK_DOUT_TCXO_DIV3>, <&cmu_peris CLK_MOUT_PERIS_GIC>; + clock-names = "fin_pll", "mct"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + status = "disabled"; + }; + + gic: interrupt-controller@10200000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x10200000 0x0 0x10000>, /* GICD */ + <0x0 0x10240000 0x0 0x200000>; /* GICR * 8 */ + + #interrupt-cells = <4>; + interrupt-controller; + interrupts = ; + + ppi-partitions { + ppi_cluster0: interrupt-partition-0 { + affinity = <&cpu0 &cpu1 &cpu2 &cpu3>; + }; + + ppi_cluster1: interrupt-partition-1 { + affinity = <&cpu4 &cpu5 &cpu6>; + }; + + ppi_cluster2: interrupt-partition-2 { + affinity = <&cpu7>; + }; + }; + }; + + cmu_peric0: clock-controller@10400000 { + compatible = "samsung,exynos2200-cmu-peric0"; + reg = <0x0 0x10400000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_PERIC0_NOC>, + <&cmu_top CLK_DOUT_CMU_PERIC0_IP0>, + <&cmu_top CLK_DOUT_CMU_PERIC0_IP1>; + clock-names = "oscclk", "noc", "ip0", "ip1"; + }; + + syscon_peric0: syscon@10420000 { + compatible = "samsung,exynos2200-peric0-sysreg", "syscon"; + reg = <0x0 0x10420000 0x0 0x2000>; + }; + + pinctrl_peric0: pinctrl@10430000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x10430000 0x0 0x1000>; + }; + + cmu_peric1: clock-controller@10700000 { + compatible = "samsung,exynos2200-cmu-peric1"; + reg = <0x0 0x10700000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_PERIC1_NOC>, + <&cmu_top CLK_DOUT_CMU_PERIC1_IP0>, + <&cmu_top CLK_DOUT_CMU_PERIC1_IP1>; + clock-names = "oscclk", "noc", "ip0", "ip1"; + }; + + syscon_peric1: syscon@10720000 { + compatible = "samsung,exynos2200-peric1-sysreg", "syscon"; + reg = <0x0 0x10720000 0x0 0x2000>; + }; + + pinctrl_peric1: pinctrl@10730000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x10730000 0x0 0x1000>; + }; + + cmu_hsi0: clock-controller@10a00000 { + compatible = "samsung,exynos2200-cmu-hsi0"; + reg = <0x0 0x10a00000 0x0 0x8000>; + #clock-cells = <1>; + }; + + usb32drd: phy@10aa0000 { + compatible = "samsung,exynos2200-usb32drd-phy"; + reg = <0x0 0x10aa0000 0x0 0x10000>; + + clocks = <&cmu_hsi0 CLK_MOUT_HSI0_NOC>; + clock-names = "phy"; + + #phy-cells = <1>; + phys = <&usb_hsphy>; + phy-names = "hs"; + + samsung,pmu-syscon = <&pmu_system_controller>; + + status = "disabled"; + }; + + usb_hsphy: phy@10ab0000 { + compatible = "samsung,exynos2200-eusb2-phy"; + reg = <0x0 0x10ab0000 0x0 0x10000>; + + clocks = <&cmu_hsi0 CLK_MOUT_HSI0_USB32DRD>, + <&cmu_hsi0 CLK_MOUT_HSI0_NOC>, + <&cmu_hsi0 CLK_DOUT_DIV_CLK_HSI0_EUSB>; + clock-names = "ref", "bus", "ctrl"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + usb: usb@10b00000 { + compatible = "samsung,exynos2200-dwusb3"; + ranges = <0x0 0x0 0x10b00000 0x10000>; + + clocks = <&cmu_hsi0 CLK_MOUT_HSI0_NOC>; + clock-names = "link_aclk"; + + #address-cells = <1>; + #size-cells = <1>; + + status = "disabled"; + + usb_dwc3: usb@0 { + compatible = "snps,dwc3"; + reg = <0x0 0x10000>; + + clocks = <&cmu_hsi0 CLK_MOUT_HSI0_USB32DRD>; + clock-names = "ref"; + + interrupts = ; + + phys = <&usb32drd 0>; + phy-names = "usb2-phy"; + + snps,dis-u2-freeclk-exists-quirk; + snps,gfladj-refclk-lpm-sel-quirk; + snps,has-lpm-erratum; + snps,quirk-frame-length-adjustment = <0x20>; + snps,usb3_lpm_capable; + }; + }; + + cmu_ufs: clock-controller@11000000 { + compatible = "samsung,exynos2200-cmu-ufs"; + reg = <0x0 0x11000000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_UFS_NOC>, + <&cmu_top CLK_MOUT_CMU_UFS_MMC_CARD>, + <&cmu_top CLK_DOUT_CMU_UFS_UFS_EMBD>; + clock-names = "oscclk", "noc", "mmc", "ufs"; + }; + + syscon_ufs: syscon@11020000 { + compatible = "samsung,exynos2200-ufs-sysreg", "syscon"; + reg = <0x0 0x11020000 0x0 0x2000>; + }; + + pinctrl_ufs: pinctrl@11040000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x11040000 0x0 0x1000>; + }; + + pinctrl_hsi1ufs: pinctrl@11060000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x11060000 0x0 0x1000>; + }; + + pinctrl_hsi1: pinctrl@11240000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x11240000 0x0 0x1000>; + }; + + cmu_peric2: clock-controller@11c00000 { + compatible = "samsung,exynos2200-cmu-peric2"; + reg = <0x0 0x11c00000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_PERIC2_NOC>, + <&cmu_top CLK_DOUT_CMU_PERIC2_IP0>, + <&cmu_top CLK_DOUT_CMU_PERIC2_IP1>; + clock-names = "oscclk", "noc", "ip0", "ip1"; + }; + + syscon_peric2: syscon@11c20000 { + compatible = "samsung,exynos2200-peric2-sysreg", "syscon"; + reg = <0x0 0x11c20000 0x0 0x4000>; + }; + + pinctrl_peric2: pinctrl@11c30000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x11c30000 0x0 0x1000>; + }; + + cmu_cmgp: clock-controller@14e00000 { + compatible = "samsung,exynos2200-cmu-cmgp"; + reg = <0x0 0x14e00000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_alive CLK_DOUT_ALIVE_CMGP_NOC>, + <&cmu_alive CLK_DOUT_ALIVE_CMGP_PERI>; + clock-names = "oscclk", "noc", "peri"; + }; + + syscon_cmgp: syscon@14e20000 { + compatible = "samsung,exynos2200-cmgp-sysreg", "syscon"; + reg = <0x0 0x14e20000 0x0 0x2000>; + }; + + pinctrl_cmgp: pinctrl@14e30000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x14e30000 0x0 0x1000>; + + wakeup-interrupt-controller { + compatible = "samsung,exynos2200-wakeup-eint", + "samsung,exynos850-wakeup-eint", + "samsung,exynos7-wakeup-eint"; + }; + }; + + cmu_vts: clock-controller@15300000 { + compatible = "samsung,exynos2200-cmu-vts"; + reg = <0x0 0x15300000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_VTS_DMIC>; + clock-names = "oscclk", "dmic"; + }; + + pinctrl_vts: pinctrl@15320000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x15320000 0x0 0x1000>; + }; + + cmu_alive: clock-controller@15800000 { + compatible = "samsung,exynos2200-cmu-alive"; + reg = <0x0 0x15800000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top CLK_DOUT_CMU_ALIVE_NOC>; + clock-names = "oscclk", "noc"; + }; + + pinctrl_alive: pinctrl@15850000 { + compatible = "samsung,exynos2200-pinctrl"; + reg = <0x0 0x15850000 0x0 0x1000>; + + wakeup-interrupt-controller { + compatible = "samsung,exynos2200-wakeup-eint", + "samsung,exynos850-wakeup-eint", + "samsung,exynos7-wakeup-eint"; + }; + }; + + pmu_system_controller: system-controller@15860000 { + compatible = "samsung,exynos2200-pmu", + "samsung,exynos7-pmu", "syscon"; + reg = <0x0 0x15860000 0x0 0x10000>; + + reboot: syscon-reboot { + compatible = "syscon-reboot"; + offset = <0x3c00>; /* SYSTEM_CONFIGURATION */ + mask = <0x2>; /* SWRESET_SYSTEM */ + value = <0x2>; /* reset value */ + }; + }; + + cmu_top: clock-controller@1a320000 { + compatible = "samsung,exynos2200-cmu-top"; + reg = <0x0 0x1a320000 0x0 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>; + clock-names = "oscclk"; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + /* + * Non-updatable, broken stock Samsung bootloader does not + * configure CNTFRQ_EL0 + */ + clock-frequency = <25600000>; + }; +}; + +#include "exynos2200-pinctrl.dtsi" diff --git a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi index 8f02de8480b6..a1fb354dea9f 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi @@ -85,7 +85,7 @@ homepage-key { }; }; - i2c_max98504: i2c-gpio-0 { + i2c_max98504: i2c-13 { compatible = "i2c-gpio"; sda-gpios = <&gpd0 1 GPIO_ACTIVE_HIGH>; scl-gpios = <&gpd0 0 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts index 61eec1aff32e..b8ce433b93b1 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts +++ b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts @@ -89,7 +89,7 @@ key-volup { memory@40000000 { device_type = "memory"; reg = <0x0 0x40000000 0x3d800000>, - <0x0 0x80000000 0x7d800000>; + <0x0 0x80000000 0x40000000>; }; pwrseq_mmc1: pwrseq-mmc1 { diff --git a/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts b/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts index eb97dcc41542..b1d9eff5a827 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts +++ b/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts @@ -78,7 +78,7 @@ key-volup { memory@40000000 { device_type = "memory"; reg = <0x0 0x40000000 0x3e400000>, - <0x0 0x80000000 0xbe400000>; + <0x0 0x80000000 0x80000000>; }; pwrseq_mmc1: pwrseq-mmc1 { diff --git a/arch/arm64/boot/dts/exynos/exynos7870.dtsi b/arch/arm64/boot/dts/exynos/exynos7870.dtsi index 5cba8c9bb403..d5d347623b90 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos7870.dtsi @@ -327,6 +327,7 @@ usb@0 { phys = <&usbdrd_phy 0>; usb-role-switch; + snps,usb2-gadget-lpm-disable; }; }; diff --git a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi b/arch/arm64/boot/dts/exynos/exynosautov920.dtsi index 2cb8041c8a9f..0fdf2062930a 100644 --- a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi +++ b/arch/arm64/boot/dts/exynos/exynosautov920.dtsi @@ -455,6 +455,26 @@ serial_0: serial@10880000 { samsung,uart-fifosize = <256>; status = "disabled"; }; + + spi_0: spi@10880000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10880000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_bus &spi0_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI00_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 1>, <&pdma0 0>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <256>; + status = "disabled"; + }; }; usi_1: usi@108a00c0 { @@ -484,6 +504,26 @@ serial_1: serial@108a0000 { samsung,uart-fifosize = <256>; status = "disabled"; }; + + spi_1: spi@108a0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x108a0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_bus &spi1_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI01_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 3>, <&pdma0 2>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <256>; + status = "disabled"; + }; }; usi_2: usi@108c00c0 { @@ -513,6 +553,26 @@ serial_2: serial@108c0000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_2: spi@108c0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x108c0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi2_bus &spi2_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI02_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 5>, <&pdma0 4>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_3: usi@108e00c0 { @@ -542,6 +602,26 @@ serial_3: serial@108e0000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_3: spi@108e0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x108e0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi3_bus &spi3_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI03_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 7>, <&pdma0 6>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_4: usi@109000c0 { @@ -571,6 +651,26 @@ serial_4: serial@10900000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_4: spi@10900000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10900000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi4_bus &spi4_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI04_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 9>, <&pdma0 8>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_5: usi@109200c0 { @@ -600,6 +700,26 @@ serial_5: serial@10920000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_5: spi@10920000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10920000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi5_bus &spi5_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI05_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 11>, <&pdma0 10>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_6: usi@109400c0 { @@ -629,6 +749,26 @@ serial_6: serial@10940000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_6: spi@10940000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10940000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi6_bus &spi6_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI06_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 13>, <&pdma0 12>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_7: usi@109600c0 { @@ -658,6 +798,26 @@ serial_7: serial@10960000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_7: spi@10960000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10960000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi7_bus &spi7_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI07_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 15>, <&pdma0 14>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_8: usi@109800c0 { @@ -687,6 +847,27 @@ serial_8: serial@10980000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_8: spi@10980000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10980000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi8_bus &spi8_cs_func>; + clocks = <&cmu_peric0 CLK_MOUT_PERIC0_NOC_USER>, + <&cmu_peric0 CLK_DOUT_PERIC0_USI08_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma0 17>, <&pdma0 16>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; + }; pwm: pwm@109b0000 { @@ -752,6 +933,26 @@ serial_9: serial@10c8000 { samsung,uart-fifosize = <256>; status = "disabled"; }; + + spi_9: spi@10c80000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10c80000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi9_bus &spi9_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI09_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 1>, <&pdma1 0>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <256>; + status = "disabled"; + }; }; usi_10: usi@10ca00c0 { @@ -781,6 +982,26 @@ serial_10: serial@10ca0000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_10: spi@10ca0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10ca0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi10_bus &spi10_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI10_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 3>, <&pdma1 2>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_11: usi@10cc00c0 { @@ -810,6 +1031,26 @@ serial_11: serial@10cc0000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_11: spi@10cc0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10cc0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi11_bus &spi11_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI11_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 5>, <&pdma1 4>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_12: usi@10ce00c0 { @@ -839,6 +1080,26 @@ serial_12: serial@10ce0000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_12: spi@10ce0000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10ce0000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi12_bus &spi12_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI12_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 7>, <&pdma1 6>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_13: usi@10d000c0 { @@ -868,6 +1129,26 @@ serial_13: serial@10d00000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_13: spi@10d00000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10d00000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi13_bus &spi13_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI13_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 9>, <&pdma1 8>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_14: usi@10d200c0 { @@ -897,6 +1178,26 @@ serial_14: serial@10d20000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_14: spi@10d20000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10d20000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi14_bus &spi14_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI14_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 11>, <&pdma1 10>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_15: usi@10d400c0 { @@ -926,6 +1227,26 @@ serial_15: serial@10d40000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_15: spi@10d40000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10d40000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi15_bus &spi15_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI15_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 13>, <&pdma1 12>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_16: usi@10d600c0 { @@ -955,6 +1276,26 @@ serial_16: serial@10d60000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_16: spi@10d60000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10d60000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi16_bus &spi16_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI16_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 15>, <&pdma1 14>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; usi_17: usi@10d800c0 { @@ -984,6 +1325,26 @@ serial_17: serial@10d80000 { samsung,uart-fifosize = <64>; status = "disabled"; }; + + spi_17: spi@10d80000 { + compatible = "samsung,exynosautov920-spi", + "samsung,exynos850-spi"; + reg = <0x10d80000 0x30>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&spi17_bus &spi17_cs_func>; + clocks = <&cmu_peric1 CLK_MOUT_PERIC1_NOC_USER>, + <&cmu_peric1 CLK_DOUT_PERIC1_USI17_USI>; + clock-names = "spi", "spi_busclk0"; + samsung,spi-src-clk = <0>; + dmas = <&pdma1 17>, <&pdma1 16>; + dma-names = "tx", "rx"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <64>; + status = "disabled"; + }; }; cmu_top: clock-controller@11000000 { @@ -1048,6 +1409,23 @@ pinctrl_hsi1: pinctrl@16450000 { interrupts = ; }; + cmu_hsi2: clock-controller@16b00000 { + compatible = "samsung,exynosautov920-cmu-hsi2"; + reg = <0x16b00000 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top DOUT_CLKCMU_HSI2_NOC>, + <&cmu_top DOUT_CLKCMU_HSI2_NOC_UFS>, + <&cmu_top DOUT_CLKCMU_HSI2_UFS_EMBD>, + <&cmu_top DOUT_CLKCMU_HSI2_ETHERNET>; + clock-names = "oscclk", + "noc", + "ufs", + "embd", + "ethernet"; + }; + pinctrl_hsi2: pinctrl@16c10000 { compatible = "samsung,exynosautov920-pinctrl"; reg = <0x16c10000 0x10000>; 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 d6ddcc13f7b2..84ff3e047d3b 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi +++ b/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi @@ -60,6 +60,21 @@ button-power { }; }; + reboot-mode { + compatible = "nvmem-reboot-mode"; + nvmem-cells = <&nvmem_reboot_mode>; + nvmem-cell-names = "reboot-mode"; + mode-bootloader = <0x800000fc>; + mode-charge = <0x8000000a>; + mode-dm-verity-device-corrupted = <0x80000050>; + mode-fastboot = <0x800000fa>; + mode-reboot-ab-update = <0x80000052>; + mode-recovery = <0x800000ff>; + mode-rescue = <0x800000f9>; + mode-shutdown-thermal = <0x80000051>; + mode-shutdown-thermal-battery = <0x80000051>; + }; + /* TODO: Remove this once PMIC is implemented */ reg_placeholder: regulator-0 { compatible = "regulator-fixed"; @@ -85,6 +100,20 @@ cont_splash_mem: splash@fac00000 { }; }; +&acpm_ipc { + pmic { + compatible = "samsung,s2mpg10-pmic"; + interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int>; + system-power-controller; + wakeup-source; + + regulators { + }; + }; +}; + &ext_24_5m { clock-frequency = <24576000>; }; @@ -188,6 +217,60 @@ usbc0_role_sw: endpoint { }; }; }; + + pmic@66 { + compatible = "maxim,max77759"; + reg = <0x66>; + + pinctrl-0 = <&if_pmic_int>; + pinctrl-names = "default"; + interrupts-extended = <&gpa8 3 IRQ_TYPE_LEVEL_LOW>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio { + compatible = "maxim,max77759-gpio"; + + gpio-controller; + #gpio-cells = <2>; + /* + * "Human-readable name [SIGNAL_LABEL]" where the + * latter comes from the schematic + */ + gpio-line-names = "OTG boost [OTG_BOOST_EN]", + "max20339 IRQ [MW_OVP_INT_L]"; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + nvmem-0 { + compatible = "maxim,max77759-nvmem"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + nvmem_reboot_mode: reboot-mode@0 { + reg = <0x0 0x4>; + }; + + boot-reason@4 { + reg = <0x4 0x4>; + }; + + shutdown-user-flag@8 { + reg = <0x8 0x1>; + }; + + rsoc@a { + reg = <0xa 0x2>; + }; + }; + }; + }; }; &pinctrl_far_alive { @@ -211,9 +294,22 @@ typec_int: typec-int-pins { samsung,pin-pud = ; samsung,pin-drv = ; }; + + if_pmic_int: if-pmic-int-pins { + samsung,pins = "gpa8-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; }; &pinctrl_gpio_alive { + pmic_int: pmic-int-pins { + samsung,pins = "gpa0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + key_power: key-power-pins { samsung,pins = "gpa10-1"; samsung,pin-function = ; diff --git a/arch/arm64/boot/dts/exynos/google/gs101.dtsi b/arch/arm64/boot/dts/exynos/google/gs101.dtsi index 48c691fd0a3a..c0f8c25861a9 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101.dtsi +++ b/arch/arm64/boot/dts/exynos/google/gs101.dtsi @@ -155,6 +155,7 @@ ananke_cpu_sleep: cpu-ananke-sleep { idle-state-name = "c2"; compatible = "arm,idle-state"; arm,psci-suspend-param = <0x0010000>; + local-timer-stop; entry-latency-us = <70>; exit-latency-us = <160>; min-residency-us = <2000>; @@ -164,6 +165,7 @@ enyo_cpu_sleep: cpu-enyo-sleep { idle-state-name = "c2"; compatible = "arm,idle-state"; arm,psci-suspend-param = <0x0010000>; + local-timer-stop; entry-latency-us = <150>; exit-latency-us = <190>; min-residency-us = <2500>; @@ -173,6 +175,7 @@ hera_cpu_sleep: cpu-hera-sleep { idle-state-name = "c2"; compatible = "arm,idle-state"; arm,psci-suspend-param = <0x0010000>; + local-timer-stop; entry-latency-us = <235>; exit-latency-us = <220>; min-residency-us = <3500>; @@ -1368,6 +1371,7 @@ ufs_0: ufs@14700000 { <&cmu_hsi2 CLK_GOUT_HSI2_SYSREG_HSI2_PCLK>; clock-names = "core_clk", "sclk_unipro_main", "fmp", "aclk", "pclk", "sysreg"; + dma-coherent; freq-table-hz = <0 0>, <0 0>, <0 0>, <0 0>, <0 0>, <0 0>; pinctrl-0 = <&ufs_rst_n &ufs_refclk_out>; pinctrl-names = "default"; @@ -1415,10 +1419,7 @@ poweroff: syscon-poweroff { }; reboot: syscon-reboot { - compatible = "syscon-reboot"; - offset = <0x3a00>; /* SYSTEM_CONFIGURATION */ - mask = <0x2>; /* SWRESET_SYSTEM */ - value = <0x2>; /* reset value */ + compatible = "google,gs101-reboot"; }; reboot-mode { @@ -1426,6 +1427,7 @@ reboot-mode { offset = <0x0810>; /* EXYNOS_PMU_SYSIP_DAT0 */ mode-bootloader = <0xfc>; mode-charge = <0x0a>; + mode-dm-verity-device-corrupted = <0x50>; mode-fastboot = <0xfa>; mode-reboot-ab-update = <0x52>; mode-recovery = <0xff>; diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile index 0b473a23d120..23535ed47631 100644 --- a/arch/arm64/boot/dts/freescale/Makefile +++ b/arch/arm64/boot/dts/freescale/Makefile @@ -229,6 +229,14 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-skov-revc-tian-g07017.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-toradex-smarc-dev.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314.dtb + +imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10-dtbs += imx8mp-tx8p-ml81-moduline-display-106.dtb \ + imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtbo +imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17-dtbs += imx8mp-tx8p-ml81-moduline-display-106.dtb \ + imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-var-som-symphony.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-venice-gw71xx-2x.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-venice-gw72xx-2x.dtb @@ -260,16 +268,16 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-lvds1-imx-lvds-hdmi.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-mx8-dlvds-lcd1.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-pcie-ep.dtb -imx8mp-tqma8mpql-mba8mpxl-lvds-dtbs += imx8mp-tqma8mpql-mba8mpxl.dtb imx8mp-tqma8mpql-mba8mpxl-lvds.dtbo +imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33-dtbs += imx8mp-tqma8mpql-mba8mpxl.dtb imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01-dtbs += imx8mp-tqma8mpql-mba8mpxl.dtb imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtbo imx8mp-tqma8mpql-mba8mp-ras314-imx219-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtbo -imx8mp-tqma8mpql-mba8mp-ras314-lvds-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mpxl-lvds.dtbo -imx8mp-tqma8mpql-mba8mp-ras314-lvds-imx219-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mpxl-lvds.dtbo imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtbo -dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl-lvds.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-tm070jvhg33.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtb -dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds.dtb -dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds-imx219.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33-imx219.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mq-evk.dtb imx8mq-evk-pcie1-ep-dtbs += imx8mq-evk.dtb imx-pcie1-ep.dtbo @@ -301,6 +309,14 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qm-apalis-v1.1-eval-v1.2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qm-apalis-v1.1-ixora-v1.1.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qm-apalis-v1.1-ixora-v1.2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qm-mek.dtb + +imx8qm-mek-ov5640-csi0-dtbs := imx8qm-mek.dtb imx8qm-mek-ov5640-csi0.dtbo +dtb-${CONFIG_ARCH_MXC} += imx8qm-mek-ov5640-csi0.dtb +imx8qm-mek-ov5640-csi1-dtbs := imx8qm-mek.dtb imx8qm-mek-ov5640-csi1.dtbo +dtb-${CONFIG_ARCH_MXC} += imx8qm-mek-ov5640-csi1.dtb +imx8qm-mek-ov5640-dual-dtbs := imx8qm-mek.dtb imx8qm-mek-ov5640-csi0.dtbo imx8qm-mek-ov5640-csi1.dtbo +dtb-${CONFIG_ARCH_MXC} += imx8qm-mek-ov5640-dual.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8qxp-ai_ml.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qxp-colibri-aster.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qxp-colibri-eval-v3.dtb @@ -311,6 +327,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek.dtb imx8qxp-mek-pcie-ep-dtbs += imx8qxp-mek.dtb imx-pcie0-ep.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek-pcie-ep.dtb +imx8qxp-mek-ov5640-csi-dtbs := imx8qxp-mek.dtb imx8qxp-mek-ov5640-csi.dtbo +dtb-${CONFIG_ARCH_MXC} += imx8qxp-mek-ov5640-csi.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8qxp-tqma8xqp-mba8xx.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qxp-tqma8xqps-mb-smarc-2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8ulp-evk.dtb @@ -324,6 +343,16 @@ 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 dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin.dtb + +imx93-phyboard-nash-peb-wlbt-07-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-peb-wlbt-07.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-peb-wlbt-07.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 + dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxca.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla.dtb @@ -339,6 +368,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-pcie0-ep.dtb imx95-19x19-evk-pcie0-ep-dtbs += imx95-19x19-evk.dtb imx-pcie0-ep.dtbo imx95-19x19-evk-pcie1-ep-dtbs += imx95-19x19-evk.dtb imx-pcie1-ep.dtbo dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk-pcie0-ep.dtb imx95-19x19-evk-pcie1-ep.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-libra-rdk-fpsc.dtb imx8mm-kontron-dl-dtbs := imx8mm-kontron-bl.dtb imx8mm-kontron-dl.dtbo diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a-mbls10xxa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a-mbls10xxa.dts index 03748a7f657b..e04483fdb908 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a-mbls10xxa.dts +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a-mbls10xxa.dts @@ -41,9 +41,21 @@ &esdhc { wp-gpios = <&gpio3 3 GPIO_ACTIVE_HIGH>; }; +&sfp1 { + status = "okay"; +}; + +&sfp1_i2c { + status = "okay"; +}; + &usb2 { status = "okay"; }; #include "fsl-ls1043-post.dtsi" #include "tqmls104xa-mbls10xxa-fman.dtsi" + +&enet6 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a.dtsi index 12d5f3938e5d..257d90bb9c20 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-tqmls1043a.dtsi @@ -17,11 +17,10 @@ &qspi { qflash0: flash@0 { compatible = "jedec,spi-nor"; reg = <0>; - #address-cells = <1>; - #size-cells = <1>; spi-max-frequency = <62500000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; + vcc-supply = <®_vcc1v8>; partitions { compatible = "fixed-partitions"; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi index c0e3e8fa1e79..26bea88cb967 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi @@ -550,6 +550,9 @@ i2c1: i2c@2190000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; scl-gpios = <&gpio4 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 36>, + <&edma0 1 37>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -563,6 +566,9 @@ i2c2: i2c@21a0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; scl-gpios = <&gpio4 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 34>, + <&edma0 1 35>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -576,6 +582,9 @@ i2c3: i2c@21b0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; scl-gpios = <&gpio4 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 40>, + <&edma0 1 41>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -717,6 +726,9 @@ lpuart0: serial@2950000 { interrupts = ; clocks = <&clockgen QORIQ_CLK_SYSCLK 0>; clock-names = "ipg"; + dmas = <&edma0 1 32>, + <&edma0 1 33>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -727,6 +739,9 @@ lpuart1: serial@2960000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 30>, + <&edma0 1 31>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -737,6 +752,9 @@ lpuart2: serial@2970000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 28>, + <&edma0 1 29>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -747,6 +765,9 @@ lpuart3: serial@2980000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 26>, + <&edma0 1 27>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -757,6 +778,9 @@ lpuart4: serial@2990000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 24>, + <&edma0 1 25>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -767,6 +791,9 @@ lpuart5: serial@29a0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 22>, + <&edma0 1 23>; + dma-names = "rx", "tx"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a-mbls10xxa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a-mbls10xxa.dts index 37834ae3deac..43261cda3fcf 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a-mbls10xxa.dts +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a-mbls10xxa.dts @@ -44,6 +44,22 @@ &esdhc { wp-gpios = <&gpio3 3 GPIO_ACTIVE_HIGH>; }; +&sfp1 { + status = "okay"; +}; + +&sfp2 { + status = "okay"; +}; + +&sfp1_i2c { + status = "okay"; +}; + +&sfp2_i2c { + status = "okay"; +}; + &usb2 { status = "okay"; }; @@ -51,6 +67,10 @@ &usb2 { #include "fsl-ls1046-post.dtsi" #include "tqmls104xa-mbls10xxa-fman.dtsi" -&enet7 { - status = "disabled"; +&enet6 { + status = "okay"; +}; + +&enet7 { + status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a.dtsi index 4a8f8bc688f5..fa543db99def 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-tqmls1046a.dtsi @@ -17,11 +17,10 @@ &qspi { qflash0: flash@0 { compatible = "jedec,spi-nor"; reg = <0>; - #address-cells = <1>; - #size-cells = <1>; spi-max-frequency = <62500000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; + vcc-supply = <®_vcc1v8>; partitions { compatible = "fixed-partitions"; @@ -38,5 +37,6 @@ qflash1: flash@1 { spi-max-frequency = <62500000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; + vcc-supply = <®_vcc1v8>; }; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi index 0baf256b4400..4a22fde38bea 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi @@ -523,6 +523,9 @@ i2c1: i2c@2190000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; scl-gpios = <&gpio3 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 36>, + <&edma0 1 37>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -535,6 +538,9 @@ i2c2: i2c@21a0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; scl-gpios = <&gpio3 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 34>, + <&edma0 1 35>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -547,6 +553,9 @@ i2c3: i2c@21b0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; scl-gpios = <&gpio3 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + dmas = <&edma0 1 40>, + <&edma0 1 41>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -633,6 +642,9 @@ lpuart0: serial@2950000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(1)>; clock-names = "ipg"; + dmas = <&edma0 1 32>, + <&edma0 1 33>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -643,6 +655,9 @@ lpuart1: serial@2960000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; clock-names = "ipg"; + dmas = <&edma0 1 30>, + <&edma0 1 31>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -653,6 +668,9 @@ lpuart2: serial@2970000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; clock-names = "ipg"; + dmas = <&edma0 1 28>, + <&edma0 1 29>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -663,6 +681,9 @@ lpuart3: serial@2980000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; clock-names = "ipg"; + dmas = <&edma0 1 26>, + <&edma0 1 27>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -673,6 +694,9 @@ lpuart4: serial@2990000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; clock-names = "ipg"; + dmas = <&edma0 1 24>, + <&edma0 1 25>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -683,15 +707,19 @@ lpuart5: serial@29a0000 { clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; clock-names = "ipg"; + dmas = <&edma0 1 22>, + <&edma0 1 23>; + dma-names = "rx", "tx"; status = "disabled"; }; wdog0: watchdog@2ad0000 { - compatible = "fsl,imx21-wdt"; + compatible = "fsl,ls1046a-wdt", "fsl,imx21-wdt"; reg = <0x0 0x2ad0000 0x0 0x10000>; interrupts = ; clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>; + big-endian; }; edma0: dma-controller@2c00000 { diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a-mbls10xxa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a-mbls10xxa.dts index e567918f6afc..181eeab55aa0 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a-mbls10xxa.dts +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a-mbls10xxa.dts @@ -53,6 +53,14 @@ &esdhc { wp-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>; }; +&sfp1 { + status = "okay"; +}; + +&sfp2 { + status = "okay"; +}; + &sfp1_i2c { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a.dtsi index 9a0f21484be9..b8a213df238a 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-tqmls1088a.dtsi @@ -17,11 +17,10 @@ &qspi { qflash0: flash@0 { compatible = "jedec,spi-nor"; reg = <0>; - #address-cells = <1>; - #size-cells = <1>; spi-max-frequency = <62500000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; + vcc-supply = <®_vcc1v8>; partitions { compatible = "fixed-partitions"; @@ -38,5 +37,6 @@ qflash1: flash@1 { spi-max-frequency = <62500000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; + vcc-supply = <®_vcc1v8>; }; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-qds.dts b/arch/arm64/boot/dts/freescale/fsl-lx2160a-qds.dts index 4d721197d837..2d01e20b47e7 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-qds.dts +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-qds.dts @@ -43,12 +43,22 @@ mdio@0 { /* On-board PHY #1 RGMI1*/ reg = <0x00>; #address-cells = <1>; #size-cells = <0>; + + rgmii_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id001c.c916"; + reg = <0x1>; + }; }; mdio@8 { /* On-board PHY #2 RGMI2*/ reg = <0x8>; #address-cells = <1>; #size-cells = <0>; + + rgmii_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id001c.c916"; + reg = <0x2>; + }; }; mdio@18 { /* Slot #1 */ @@ -169,6 +179,16 @@ &crypto { status = "okay"; }; +&dpmac17 { + phy-handle = <&rgmii_phy1>; + phy-connection-type = "rgmii-id"; +}; + +&dpmac18 { + phy-handle = <&rgmii_phy2>; + phy-connection-type = "rgmii-id"; +}; + &dspi0 { status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi index d39242c1b9f7..2cf0f7208350 100644 --- a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi @@ -10,12 +10,264 @@ img_ipg_clk: clock-img-ipg { clock-output-names = "img_ipg_clk"; }; +img_pxl_clk: clock-img-pxl { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <600000000>; + clock-output-names = "img_pxl_clk"; +}; + img_subsys: bus@58000000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <0x58000000 0x0 0x58000000 0x1000000>; + isi: isi@58100000 { + reg = <0x58100000 0x80000>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&pdma0_lpcg IMX_LPCG_CLK_0>, + <&pdma1_lpcg IMX_LPCG_CLK_0>, + <&pdma2_lpcg IMX_LPCG_CLK_0>, + <&pdma3_lpcg IMX_LPCG_CLK_0>, + <&pdma4_lpcg IMX_LPCG_CLK_0>, + <&pdma5_lpcg IMX_LPCG_CLK_0>, + <&pdma6_lpcg IMX_LPCG_CLK_0>, + <&pdma7_lpcg IMX_LPCG_CLK_0>; + clock-names = "per0", "per1", "per2", "per3", + "per4", "per5", "per6", "per7"; + interrupt-parent = <&gic>; + power-domains = <&pd IMX_SC_R_ISI_CH0>, + <&pd IMX_SC_R_ISI_CH1>, + <&pd IMX_SC_R_ISI_CH2>, + <&pd IMX_SC_R_ISI_CH3>, + <&pd IMX_SC_R_ISI_CH4>, + <&pd IMX_SC_R_ISI_CH5>, + <&pd IMX_SC_R_ISI_CH6>, + <&pd IMX_SC_R_ISI_CH7>; + status = "disabled"; + }; + + irqsteer_csi0: irqsteer@58220000 { + compatible = "fsl,imx8qm-irqsteer", "fsl,imx-irqsteer"; + reg = <0x58220000 0x1000>; + #interrupt-cells = <1>; + interrupt-controller; + interrupts = ; + clocks = <&img_ipg_clk>; + clock-names = "ipg"; + interrupt-parent = <&gic>; + power-domains = <&pd IMX_SC_R_CSI_0>; + fsl,channel = <0>; + fsl,num-irqs = <32>; + status = "disabled"; + }; + + gpio0_mipi_csi0: gpio@58222000 { + compatible = "fsl,imx8qm-gpio", "fsl,imx35-gpio"; + reg = <0x58222000 0x1000>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0>; + #gpio-cells = <2>; + gpio-controller; + interrupt-parent = <&irqsteer_csi0>; + power-domains = <&pd IMX_SC_R_CSI_0>; + }; + + csi0_core_lpcg: clock-controller@58223018 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58223018 0x4>; + clocks = <&clk IMX_SC_R_CSI_0 IMX_SC_PM_CLK_PER>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi0_lpcg_core_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + csi0_esc_lpcg: clock-controller@5822301c { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x5822301c 0x4>; + clocks = <&clk IMX_SC_R_CSI_0 IMX_SC_PM_CLK_MISC>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi0_lpcg_esc_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + i2c_mipi_csi0: i2c@58226000 { + compatible = "fsl,imx8qxp-lpi2c", "fsl,imx7ulp-lpi2c"; + reg = <0x58226000 0x1000>; + interrupts = <8>; + clocks = <&clk IMX_SC_R_CSI_0_I2C_0 IMX_SC_PM_CLK_PER>, + <&img_ipg_clk>; + clock-names = "per", "ipg"; + assigned-clocks = <&clk IMX_SC_R_CSI_0_I2C_0 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <24000000>; + interrupt-parent = <&irqsteer_csi0>; + power-domains = <&pd IMX_SC_R_CSI_0_I2C_0>; + status = "disabled"; + }; + + mipi_csi_0: csi@58227000 { + compatible = "fsl,imx8qxp-mipi-csi2"; + reg = <0x58227000 0x1000>, + <0x58221000 0x1000>; + clocks = <&csi0_core_lpcg IMX_LPCG_CLK_4>, + <&csi0_esc_lpcg IMX_LPCG_CLK_4>, + <&csi0_pxl_lpcg IMX_LPCG_CLK_0>; + clock-names = "core", "esc", "ui"; + assigned-clocks = <&csi0_core_lpcg IMX_LPCG_CLK_4>, + <&csi0_esc_lpcg IMX_LPCG_CLK_4>; + assigned-clock-rates = <360000000>, <72000000>; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + resets = <&scu_reset IMX_SC_R_CSI_0>; + status = "disabled"; + }; + + irqsteer_csi1: irqsteer@58240000 { + compatible = "fsl,imx8qm-irqsteer", "fsl,imx-irqsteer"; + reg = <0x58240000 0x1000>; + #interrupt-cells = <1>; + interrupt-controller; + interrupts = ; + clocks = <&img_ipg_clk>; + clock-names = "ipg"; + interrupt-parent = <&gic>; + power-domains = <&pd IMX_SC_R_CSI_1>; + fsl,channel = <0>; + fsl,num-irqs = <32>; + status = "disabled"; + }; + + gpio0_mipi_csi1: gpio@58242000 { + compatible = "fsl,imx8qm-gpio", "fsl,imx35-gpio"; + reg = <0x58242000 0x1000>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0>; + #gpio-cells = <2>; + gpio-controller; + interrupt-parent = <&irqsteer_csi1>; + power-domains = <&pd IMX_SC_R_CSI_1>; + }; + + csi1_core_lpcg: clock-controller@58243018 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58243018 0x4>; + clocks = <&clk IMX_SC_R_CSI_1 IMX_SC_PM_CLK_PER>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi1_lpcg_core_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + csi1_esc_lpcg: clock-controller@5824301c { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x5824301c 0x4>; + clocks = <&clk IMX_SC_R_CSI_1 IMX_SC_PM_CLK_MISC>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi1_lpcg_esc_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + i2c_mipi_csi1: i2c@58246000 { + compatible = "fsl,imx8qxp-lpi2c", "fsl,imx7ulp-lpi2c"; + reg = <0x58246000 0x1000>; + interrupts = <8>; + clocks = <&clk IMX_SC_R_CSI_1_I2C_0 IMX_SC_PM_CLK_PER>, + <&img_ipg_clk>; + clock-names = "per", "ipg"; + assigned-clocks = <&clk IMX_SC_R_CSI_1_I2C_0 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <24000000>; + interrupt-parent = <&irqsteer_csi1>; + power-domains = <&pd IMX_SC_R_CSI_1_I2C_0>; + status = "disabled"; + }; + + mipi_csi_1: csi@58247000 { + compatible = "fsl,imx8qxp-mipi-csi2"; + reg = <0x58247000 0x1000>, + <0x58241000 0x1000>; + clocks = <&csi1_core_lpcg IMX_LPCG_CLK_4>, + <&csi1_esc_lpcg IMX_LPCG_CLK_4>, + <&csi1_pxl_lpcg IMX_LPCG_CLK_0>; + clock-names = "core", "esc", "ui"; + assigned-clocks = <&csi1_core_lpcg IMX_LPCG_CLK_4>, + <&csi1_esc_lpcg IMX_LPCG_CLK_4>; + assigned-clock-rates = <360000000>, <72000000>; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + resets = <&scu_reset IMX_SC_R_CSI_1>; + status = "disabled"; + }; + + irqsteer_parallel: irqsteer@58260000 { + compatible = "fsl,imx8qm-irqsteer", "fsl,imx-irqsteer"; + reg = <0x58260000 0x1000>; + #interrupt-cells = <1>; + interrupt-controller; + interrupts = ; + clocks = <&clk_dummy>; + clock-names = "ipg"; + interrupt-parent = <&gic>; + power-domains = <&pd IMX_SC_R_PI_0>; + fsl,channel = <0>; + fsl,num-irqs = <32>; + status = "disabled"; + }; + + pi0_ipg_lpcg: clock-controller@58263004 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58263004 0x4>; + clocks = <&clk IMX_SC_R_PI_0 IMX_SC_PM_CLK_PER>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pi0_lpcg_ipg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + pi0_pxl_lpcg: clock-controller@58263018 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58263018 0x4>; + clocks = <&clk IMX_SC_R_PI_0 IMX_SC_PM_CLK_PER>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pi0_lpcg_pxl_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + pi0_misc_lpcg: clock-controller@5826301c { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x5826301c 0x4>; + clocks = <&clk IMX_SC_R_PI_0 IMX_SC_PM_CLK_MISC0>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pi0_lpcg_misc_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + i2c0_parallel: i2c@58266000 { + compatible = "fsl,imx8qxp-lpi2c", "fsl,imx7ulp-lpi2c"; + reg = <0x58266000 0x1000>; + interrupts = <8>; + clocks = <&clk IMX_SC_R_PI_0_I2C_0 IMX_SC_PM_CLK_PER>, + <&img_ipg_clk>; + clock-names = "per", "ipg"; + assigned-clocks = <&clk IMX_SC_R_PI_0_I2C_0 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <24000000>; + interrupt-parent = <&irqsteer_parallel>; + power-domains = <&pd IMX_SC_R_PI_0_I2C_0>; + status = "disabled"; + }; + jpegdec: jpegdec@58400000 { reg = <0x58400000 0x00050000>; interrupts = ; @@ -40,6 +292,116 @@ jpegenc: jpegenc@58450000 { <&pd IMX_SC_R_MJPEG_ENC_S0>; }; + pdma0_lpcg: clock-controller@58500000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58500000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma0_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH0>; + }; + + pdma1_lpcg: clock-controller@58510000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58510000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma1_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH1>; + }; + + pdma2_lpcg: clock-controller@58520000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58520000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma2_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH2>; + }; + + pdma3_lpcg: clock-controller@58530000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58530000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma3_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH3>; + }; + + pdma4_lpcg: clock-controller@58540000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58540000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma4_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH4>; + }; + + pdma5_lpcg: clock-controller@58550000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58550000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma5_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH5>; + }; + + pdma6_lpcg: clock-controller@58560000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58560000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma6_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH6>; + }; + + pdma7_lpcg: clock-controller@58570000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58570000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "pdma7_lpcg_clk"; + power-domains = <&pd IMX_SC_R_ISI_CH7>; + }; + + csi0_pxl_lpcg: clock-controller@58580000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58580000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi0_lpcg_pxl_clk"; + power-domains = <&pd IMX_SC_R_CSI_0>; + }; + + csi1_pxl_lpcg: clock-controller@58590000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x58590000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "csi1_lpcg_pxl_clk"; + power-domains = <&pd IMX_SC_R_CSI_1>; + }; + + hdmi_rx_pxl_link_lpcg: clock-controller@585a0000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x585a0000 0x10000>; + clocks = <&img_pxl_clk>; + #clock-cells = <1>; + clock-indices = ; + clock-output-names = "hdmi_rx_lpcg_pxl_link_clk"; + power-domains = <&pd IMX_SC_R_HDMI_RX>; + }; + img_jpeg_dec_lpcg: clock-controller@585d0000 { compatible = "fsl,imx8qxp-lpcg"; reg = <0x585d0000 0x10000>; diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-security.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-security.dtsi new file mode 100644 index 000000000000..3e04142aca5c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8-ss-security.dtsi @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include + +security_subsys: bus@31400000 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x31400000 0x0 0x31400000 0x90000>; + + crypto: crypto@31400000 { + compatible = "fsl,imx8qm-caam", "fsl,sec-v4.0"; + reg = <0x31400000 0x90000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x31400000 0x90000>; + power-domains = <&pd IMX_SC_R_CAAM_JR2>; + fsl,sec-era = <9>; + + sec_jr2: jr@30000 { + compatible = "fsl,imx8qm-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x30000 0x10000>; + interrupts = ; + power-domains = <&pd IMX_SC_R_CAAM_JR2>; + }; + + sec_jr3: jr@40000 { + compatible = "fsl,imx8qm-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x40000 0x10000>; + interrupts = ; + power-domains = <&pd IMX_SC_R_CAAM_JR3>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-beacon-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-beacon-som.dtsi index 21bcd82fd092..8287a7f66ed3 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-beacon-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-beacon-som.dtsi @@ -294,6 +294,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw700x.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw700x.dtsi index 5a3b1142ddf4..37db4f0dd505 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw700x.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw700x.dtsi @@ -418,6 +418,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts index d8b67e12f7d7..272c2b223d16 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts @@ -833,6 +833,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts index 46d1ee0a4ee8..c09b40fc6dec 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts @@ -743,6 +743,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts index c0aadff4e25b..636daa3d6ca2 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts @@ -621,6 +621,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts index 86a610de84fe..99572961d9e1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts @@ -682,6 +682,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MM_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi index d29710772569..1594ce9182a5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi @@ -464,6 +464,7 @@ reg_vdd_phy: LDO4 { }; reg_nvcc_sd: LDO5 { + regulator-always-on; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <1800000>; regulator-name = "On-module +V3.3_1.8_SD (LDO5)"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index cfebaa01217e..ded89b046970 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -915,6 +915,8 @@ uart2: serial@30890000 { clocks = <&clk IMX8MM_CLK_UART2_ROOT>, <&clk IMX8MM_CLK_UART2_ROOT>; clock-names = "ipg", "per"; + dmas = <&sdma1 24 4 0>, <&sdma1 25 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-beacon-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-beacon-som.dtsi index 67a99383a632..917b7d0007a7 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-beacon-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-beacon-som.dtsi @@ -305,6 +305,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MN_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; 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 dc94d73f7106..d7f7f9aafb7d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts @@ -79,6 +79,10 @@ &sai3 { <&clk IMX8MN_AUDIO_PLL2_OUT>; }; +&sound { + audio-asrc = <&easrc>; +}; + &tlv320aic3x04 { clock-names = "mclk"; clocks = <&clk IMX8MN_CLK_SAI3_ROOT>; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi index 640c41b51af9..1d23814e11cd 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi @@ -52,6 +52,10 @@ &A53_0 { cpu-supply = <&buck2_reg>; }; +&easrc { + status = "okay"; +}; + &flexspi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_flexspi>; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts b/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts index 30c286b34aa5..a5f52f60169e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts @@ -693,6 +693,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MN_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi index 848ba5e46ee6..b98b3d0ddf25 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -860,6 +860,8 @@ uart2: serial@30890000 { clocks = <&clk IMX8MN_CLK_UART2_ROOT>, <&clk IMX8MN_CLK_UART2_ROOT>; clock-names = "ipg", "per"; + dmas = <&sdma1 24 4 0>, <&sdma1 25 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; }; 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 d0fc5977258f..16078ff60ef0 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 @@ -555,6 +555,7 @@ &usdhc2 { pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; vmmc-supply = <®_usdhc2_vmmc>; + vqmmc-supply = <&ldo5>; bus-width = <4>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi index 7f754e0a5d69..68c2e0156a5c 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi @@ -609,6 +609,7 @@ &usdhc2 { pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; vmmc-supply = <®_usdhc2_vmmc>; + vqmmc-supply = <&ldo5>; bus-width = <4>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts index 1ba3018c621e..c0cc5611048e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts @@ -168,37 +168,6 @@ audio_codec_bt_sco: audio-codec-bt-sco { #sound-dai-cells = <1>; }; - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "wm8960-audio"; - simple-audio-card,format = "i2s"; - simple-audio-card,frame-master = <&cpudai>; - simple-audio-card,bitclock-master = <&cpudai>; - simple-audio-card,widgets = - "Headphone", "Headphone Jack", - "Speaker", "External Speaker", - "Microphone", "Mic Jack"; - simple-audio-card,routing = - "Headphone Jack", "HP_L", - "Headphone Jack", "HP_R", - "External Speaker", "SPK_LP", - "External Speaker", "SPK_LN", - "External Speaker", "SPK_RP", - "External Speaker", "SPK_RN", - "LINPUT1", "Mic Jack", - "LINPUT3", "Mic Jack", - "Mic Jack", "MICB"; - - cpudai: simple-audio-card,cpu { - sound-dai = <&sai3>; - }; - - simple-audio-card,codec { - sound-dai = <&wm8960>; - }; - - }; - sound-bt-sco { compatible = "simple-audio-card"; simple-audio-card,name = "bt-sco-audio"; @@ -239,6 +208,26 @@ cpu { }; }; + sound-wm8960 { + compatible = "fsl,imx-audio-wm8960"; + audio-asrc = <&easrc>; + audio-codec = <&wm8960>; + audio-cpu = <&sai3>; + 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", + "LINPUT3", "Mic Jack", + "Mic Jack", "MICB"; + hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_HIGH>; + model = "wm8960-audio"; + pinctrl-0 = <&pinctrl_hpdet>; + pinctrl-names = "default"; + }; + sound-xcvr { compatible = "fsl,imx-audio-card"; model = "imx-audio-xcvr"; @@ -319,6 +308,11 @@ &dsp { status = "okay"; }; +&easrc { + fsl,asrc-rate = <48000>; + status = "okay"; +}; + &eqos { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_eqos>; @@ -952,6 +946,12 @@ MX8MP_IOMUXC_HDMI_HPD__HDMIMIX_HDMI_HPD 0x40000010 >; }; + pinctrl_hpdet: hpdetgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI3_RXFS__GPIO4_IO28 0xd6 + >; + }; + pinctrl_i2c1: i2c1grp { fsl,pins = < MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL 0x400001c2 diff --git a/arch/arm64/boot/dts/freescale/imx8mp-nominal.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-nominal.dtsi index 2ce1860b244d..f269f7a004fc 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-nominal.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-nominal.dtsi @@ -89,4 +89,22 @@ &media_blk_ctrl { <1039500000>; }; +&vpu_g1 { + assigned-clocks = <&clk IMX8MP_CLK_VPU_G1>; + assigned-clock-parents = <&clk IMX8MP_VPU_PLL_OUT>; + assigned-clock-rates = <600000000>; +}; + +&vpu_g2 { + assigned-clocks = <&clk IMX8MP_CLK_VPU_G2>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_1000M>; + assigned-clock-rates = <500000000>; +}; + +&vpumix_blk_ctrl { + assigned-clocks = <&clk IMX8MP_VPU_PLL>, <&clk IMX8MP_CLK_VPU_BUS>; + assigned-clock-parents = <0>, <&clk IMX8MP_VPU_PLL_OUT>; + assigned-clock-rates = <600000000>, <600000000>; +}; + /delete-node/ &{noc_opp_table/opp-1000000000}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h index 0fef066471ba..16f5899de415 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h +++ b/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h @@ -6,6 +6,39 @@ #ifndef __DTS_IMX8MP_PINFUNC_H #define __DTS_IMX8MP_PINFUNC_H +/* Drive Strength */ +#define MX8MP_DSE_X1 0x0 +#define MX8MP_DSE_X2 0x4 +#define MX8MP_DSE_X4 0x2 +#define MX8MP_DSE_X6 0x6 + +/* Slew Rate */ +#define MX8MP_FSEL_FAST 0x10 +#define MX8MP_FSEL_SLOW 0x0 + +/* Open Drain */ +#define MX8MP_ODE_ENABLE 0x20 +#define MX8MP_ODE_DISABLE 0x0 + +#define MX8MP_PULL_DOWN 0x0 +#define MX8MP_PULL_UP 0x40 + +/* Hysteresis */ +#define MX8MP_HYS_CMOS 0x0 +#define MX8MP_HYS_SCHMITT 0x80 + +#define MX8MP_PULL_ENABLE 0x100 +#define MX8MP_PULL_DISABLE 0x0 + +/* SION force input mode */ +#define MX8MP_SION 0x40000000 + +/* 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 | \ + MX8MP_PULL_ENABLE | MX8MP_SION) + /* * The pin function ID is a tuple of * diff --git a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc-dev.dts b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc-dev.dts index 55b8c5c14fb4..6f9dcd3a75c8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc-dev.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc-dev.dts @@ -102,11 +102,6 @@ &gpio1 { <&pinctrl_gpio13>; }; -&gpio3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_lvds_dsi_sel>; -}; - &gpio4 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio4>, <&pinctrl_gpio6>; @@ -213,6 +208,7 @@ fan_controller: fan@18 { #pwm-cells = <2>; fan { + cooling-levels = <255>; pwms = <&fan_controller 40000 PWM_POLARITY_INVERTED>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi index 22f6daabdb90..bebe19eb360f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi @@ -320,6 +320,8 @@ &gpio2 { }; &gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lvds_dsi_sel>; gpio-line-names = "ETH_0_INT#", /* 0 */ "SLEEP#", "", @@ -349,14 +351,6 @@ &gpio3 { "", "", "SMARC_I2C_PM_CK"; - - lvds_dsi_mux_hog: lvds-dsi-mux-hog { - gpio-hog; - gpios = <7 GPIO_ACTIVE_HIGH>; - line-name = "LVDS_DSI_SEL"; - /* LVDS_DSI_SEL as DSI */ - output-low; - }; }; &gpio4 { 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 d7fd9d36f824..f7346b3d35fe 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts @@ -467,6 +467,10 @@ &pwm4 { status = "okay"; }; +®_usdhc2_vqmmc { + status = "okay"; +}; + &sai5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai5>; @@ -876,8 +880,7 @@ pinctrl_usdhc2: usdhc2grp { , , , - , - ; + ; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { @@ -886,8 +889,7 @@ pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { , , , - , - ; + ; }; pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { @@ -896,8 +898,7 @@ pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { , , , - , - ; + ; }; pinctrl_usdhc2_gpio: usdhc2-gpiogrp { diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso similarity index 100% rename from arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds.dtso rename to arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts index 23c612e80dd3..4eedd00d83b9 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts @@ -235,6 +235,7 @@ linux,cma { sound { compatible = "fsl,imx-audio-tlv320aic32x4"; model = "tqm-tlv320aic32"; + audio-asrc = <&easrc>; audio-cpu = <&sai3>; audio-codec = <&tlv320aic3x04>; }; @@ -603,6 +604,10 @@ &pwm3 { status = "okay"; }; +®_usdhc2_vqmmc { + status = "okay"; +}; + &sai3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai3>; @@ -982,8 +987,7 @@ pinctrl_usdhc2: usdhc2grp { , , , - , - ; + ; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { @@ -992,8 +996,7 @@ pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { , , , - , - ; + ; }; pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { @@ -1002,8 +1005,7 @@ pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { , , , - , - ; + ; }; pinctrl_usdhc2_gpio: usdhc2-gpiogrp { diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql.dtsi index 6067ca3be814..9716f24f7c6e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql.dtsi @@ -16,13 +16,18 @@ memory@40000000 { reg = <0x0 0x40000000 0 0x80000000>; }; - /* identical to buck4_reg, but should never change */ - reg_vcc3v3: regulator-vcc3v3 { - compatible = "regulator-fixed"; - regulator-name = "VCC3V3"; - regulator-min-microvolt = <3300000>; + reg_usdhc2_vqmmc: regulator-usdhc2-vqmmc { + compatible = "regulator-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vqmmc>; + regulator-name = "V_SD2"; + regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - regulator-always-on; + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + states = <1800000 0x1>, + <3300000 0x0>; + vin-supply = <&ldo5_reg>; + status = "disabled"; }; }; @@ -30,6 +35,10 @@ &A53_0 { cpu-supply = <&buck2_reg>; }; +&easrc { + status = "okay"; +}; + &flexspi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_flexspi0>; @@ -169,17 +178,21 @@ at24c02: eeprom@53 { read-only; reg = <0x53>; pagesize = <16>; - vcc-supply = <®_vcc3v3>; + vcc-supply = <&buck4_reg>; }; m24c64: eeprom@57 { compatible = "atmel,24c64"; reg = <0x57>; pagesize = <32>; - vcc-supply = <®_vcc3v3>; + vcc-supply = <&buck4_reg>; }; }; +&usdhc2 { + vqmmc-supply = <®_usdhc2_vqmmc>; +}; + &usdhc3 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc3>; @@ -189,7 +202,7 @@ &usdhc3 { non-removable; no-sd; no-sdio; - vmmc-supply = <®_vcc3v3>; + vmmc-supply = <&buck4_reg>; vqmmc-supply = <&buck5_reg>; status = "okay"; }; @@ -229,6 +242,10 @@ pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { fsl,pins = ; }; + pinctrl_reg_usdhc2_vqmmc: regusdhc2vqmmcgrp { + fsl,pins = ; + }; + pinctrl_usdhc3: usdhc3grp { fsl,pins = , , 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 new file mode 100644 index 000000000000..e3965caca6be --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 GOcontroll B.V. + * Author: Maud Spierings + */ + +#include +#include + +#include "imx8mp-pinfunc.h" + +/dts-v1/; +/plugin/; + +&{/} { + model = "GOcontroll Moduline Display with BOE av101hdt-a10 display"; + + panel { + compatible = "boe,av101hdt-a10"; + enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_panel>; + pinctrl-names = "default"; + power-supply = <®_3v3_per>; + reset-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + + port { + panel_lvds_in: endpoint { + remote-endpoint = <&ldb_lvds_ch0>; + }; + }; + }; + + reg_vbus: regulator-vbus { + compatible = "regulator-fixed"; + power-supply = <®_6v4>; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "usb-c-vbus"; + }; +}; + +&iomuxc { + pinctrl_panel: panelgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO07__GPIO1_IO07 + MX8MP_DSE_X1 + MX8MP_IOMUXC_GPIO1_IO09__GPIO1_IO09 + MX8MP_DSE_X1 + >; + }; +}; + +&lcdif2 { + status = "okay"; +}; + +&lvds_bridge { + assigned-clocks = <&clk IMX8MP_CLK_MEDIA_LDB>, <&clk IMX8MP_VIDEO_PLL1>; + /* IMX8MP_VIDEO_PLL1 = IMX8MP_CLK_MEDIA_DISP2_PIX * 2 * 7 */ + assigned-clock-rates = <0>, <1054620000>; + status = "okay"; + + ports { + port@1 { + ldb_lvds_ch0: endpoint { + remote-endpoint = <&panel_lvds_in>; + }; + }; + }; +}; + +&usb_dwc3_1 { + dr_mode = "host"; + + connector { + compatible = "usb-c-connector"; + data-role = "host"; + pd-disable; + vbus-supply = <®_vbus>; + + port { + high_speed_ep: endpoint { + remote-endpoint = <&usb1_hs_ep>; + }; + }; + }; + + port { + usb1_hs_ep: endpoint { + remote-endpoint = <&high_speed_ep>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso new file mode 100644 index 000000000000..3eb665ce9d5d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av123z7m-n17.dtso @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 GOcontroll B.V. + * Author: Maud Spierings + */ + +#include + +#include "imx8mp-pinfunc.h" + +/dts-v1/; +/plugin/; + +&{/} { + model = "GOcontroll Moduline Display with BOE av123z7m-n17 display"; + + panel { + compatible = "boe,av123z7m-n17"; + enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_panel>; + pinctrl-names = "default"; + power-supply = <®_3v3_per>; + reset-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dual-lvds-odd-pixels; + + panel_in0: endpoint { + remote-endpoint = <&lvds1_out>; + }; + }; + + port@1 { + reg = <1>; + dual-lvds-even-pixels; + + panel_in1: endpoint { + remote-endpoint = <&lvds0_out>; + }; + }; + }; + }; +}; + +&i2c4 { + #address-cells = <1>; + #size-cells = <0>; + + /* sn65dsi85 */ + bridge@2d { + compatible = "ti,sn65dsi84"; + reg = <0x2d>; + enable-gpios = <&gpio4 14 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_lvds_bridge>; + pinctrl-names = "default"; + vcc-supply = <®_1v8_per>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi_lvds_bridge_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&mipi_dsi_out>; + }; + }; + + port@2 { + reg = <2>; + + lvds0_out: endpoint { + remote-endpoint = <&panel_in1>; + }; + }; + + port@3 { + reg = <3>; + + lvds1_out: endpoint { + remote-endpoint = <&panel_in0>; + }; + }; + }; + }; + + /* max25014 @ 0x6f */ +}; + +&iomuxc { + pinctrl_lvds_bridge: lvdsbridgegrp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_TXD2__GPIO4_IO14 + MX8MP_DSE_X1 + >; + }; + + pinctrl_panel: panelgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO07__GPIO1_IO07 + MX8MP_DSE_X1 + MX8MP_IOMUXC_GPIO1_IO09__GPIO1_IO09 + MX8MP_DSE_X1 + >; + }; +}; + +&lcdif1 { + status = "okay"; +}; + +&mipi_dsi { + /* + * burst has to be at least 2x dsi clock that the sn65dsi85 expects + * display pixelclock * bpp / lanes / 2 = dsi clock + * 88.000.000 * 24 / 4 / 2 = 264.000.000 + * range gets rounded up to 265.000.000 - 270.000.000 + * 267.500.000 * 2 = 535.000.000 + */ + samsung,burst-clock-frequency = <535000000>; + samsung,esc-clock-frequency = <12000000>; + status = "okay"; + + ports { + port@1 { + mipi_dsi_out: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = < &dsi_lvds_bridge_in>; + }; + }; + }; +}; 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 new file mode 100644 index 000000000000..afd886dd590f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106.dts @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 GOcontroll B.V. + * Author: Maud Spierings + */ + +/dts-v1/; + +#include "imx8mp-tx8p-ml81.dtsi" + +/ { + compatible = "gocontroll,moduline-display", "fsl,imx8mp"; + chassis-type = "embedded"; + hardware = "Moduline Display V1.06"; + model = "GOcontroll Moduline Display baseboard"; + + aliases { + can0 = &flexcan1; + can1 = &flexcan2; + ethernet0 = &eqos; + ethernet1 = &fec; + mmc0 = &usdhc3; + mmc1 = &usdhc2; + rtc0 = &rtc_pcf; /* i2c rtc is the main rtc */ + rtc1 = &snvs_rtc; + spi0 = &ecspi2; /* spidev number compatibility */ + spi1 = &ecspi1; /* spidev number compatibility */ + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + external-sensor-supply { + compatible = "regulator-output"; + vout-supply = <®_5v0_sensor>; + }; + + reg_1v8_per: regulator-1v8-per { + compatible = "regulator-fixed"; + pinctrl-0 = <&pinctrl_reg_1v8>; + pinctrl-names = "default"; + power-supply = <®_3v3_per>; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "1v8-per"; + gpio = <&gpio3 25 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_3v3_per: regulator-3v3-per { + compatible = "regulator-fixed"; + power-supply = <®_6v4>; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3-per"; + }; + + 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_5v0_sensor: regulator-5v0-sensor { + compatible = "regulator-fixed"; + pinctrl-0 = <&pinctrl_reg_5v0_sensor>; + pinctrl-names = "default"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "5v0-supply-external-sensor"; + gpio = <&gpio4 9 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + 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"; + pinctrl-0 = <&pinctrl_flexcan1_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can1-stby"; + gpio = <&gpio4 3 GPIO_ACTIVE_LOW>; + }; + + reg_can2_stby: regulator-can2-stby { + compatible = "regulator-fixed"; + pinctrl-0 = <&pinctrl_flexcan2_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can2-stby"; + gpio = <&gpio5 9 GPIO_ACTIVE_LOW>; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&cpudai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&cpudai>; + simple-audio-card,name = "tas2505-audio"; + simple-audio-card,routing = "Speaker", "DAC"; + simple-audio-card,widgets = "Speaker", "Speaker External"; + + simple-audio-card,codec { + sound-dai = <&tas2505>; + }; + + cpudai: simple-audio-card,cpu { + sound-dai = <&sai6>; + }; + }; + + wifi_powerseq: wifi-powerseq { + 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 = <&gpio2 19 GPIO_ACTIVE_LOW>; + }; +}; + +&ecspi1 { + cs-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>, + <&gpio1 11 GPIO_ACTIVE_LOW>, + <&gpio1 10 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi1>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + interrupt-parent = <&gpio4>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + i2c-bus = <&i2c2>; + reset-gpios = <&gpio5 10 GPIO_ACTIVE_LOW>; + slot-number = <1>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio4 16 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_3v3_per>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + interrupt-parent = <&gpio1>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + i2c-bus = <&i2c2>; + reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>; + slot-number = <2>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio4 16 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_3v3_per>; + }; + + adc@2 { + compatible = "microchip,mcp3004"; + reg = <2>; + spi-max-frequency = <2300000>; + vref-supply = <®_vdd_3v3>; + }; +}; + +&flexcan1 { + pinctrl-0 = <&pinctrl_flexcan1>; + pinctrl-names = "default"; + xceiver-supply = <®_can1_stby>; + status = "okay"; +}; + +&flexcan2 { + pinctrl-0 = <&pinctrl_flexcan2>; + pinctrl-names = "default"; + xceiver-supply = <®_can2_stby>; + status = "okay"; +}; + +&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"; +}; + +&i2c4 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c4>; + pinctrl-1 = <&pinctrl_i2c4_gpio>; + pinctrl-names = "default", "gpio"; + scl-gpios = <&gpio5 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + tas2505: audio-codec@18 { + compatible = "ti,tas2505"; + reg = <0x18>; + clocks = <&clk IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1>; + clock-names = "mclk"; + #sound-dai-cells = <0>; + aic32x4-gpio-func = <0xff 0xff 0xff 0xff 0xff>; + av-supply = <®_1v8_per>; + dv-supply = <®_1v8_per>; + iov-supply = <®_vdd_3v3>; + pinctrl-0 = <&pinctrl_tas_reset>; + pinctrl-names = "default"; + reset-gpios = <&gpio3 24 GPIO_ACTIVE_LOW>; + }; + + rtc_pcf: rtc@51 { + compatible = "nxp,pcf85063a"; + reg = <0x51>; + quartz-load-femtofarads = <7000>; + + clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; +}; + +&iomuxc { + pinctrl_bt: btgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO14__GPIO1_IO14 + MX8MP_DSE_X1 + MX8MP_IOMUXC_GPIO1_IO12__GPIO1_IO12 + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_GPIO1_IO15__GPIO1_IO15 + MX8MP_DSE_X1 + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI1_MOSI__ECSPI1_MOSI + MX8MP_DSE_X4 + MX8MP_IOMUXC_ECSPI1_MISO__ECSPI1_MISO + (MX8MP_DSE_X4 | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_ECSPI1_SCLK__ECSPI1_SCLK + MX8MP_DSE_X4 + MX8MP_IOMUXC_SD2_CD_B__GPIO2_IO12 + MX8MP_DSE_X1 + MX8MP_IOMUXC_GPIO1_IO11__GPIO1_IO11 + MX8MP_DSE_X1 + MX8MP_IOMUXC_GPIO1_IO10__GPIO1_IO10 + MX8MP_DSE_X1 + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + MX8MP_IOMUXC_SPDIF_RX__CAN1_RX + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_SPDIF_TX__CAN1_TX + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_flexcan1_reg: flexcan1reggrp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_RXD1__GPIO4_IO03 + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_flexcan2: flexcan2grp { + fsl,pins = < + MX8MP_IOMUXC_UART3_TXD__CAN2_RX + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_UART3_RXD__CAN2_TX + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_flexcan2_reg: flexcan2reggrp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI1_SS0__GPIO5_IO09 + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_i2c2_gpio: i2c2-gpiogrp { + fsl,pins = < + MX8MP_IOMUXC_I2C2_SCL__GPIO5_IO16 + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_I2C2_SDA__GPIO5_IO17 + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_i2c4: i2c4grp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_MISO__I2C4_SCL + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_ECSPI2_SS0__I2C4_SDA + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_i2c4_gpio: i2c4-gpiogrp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_MISO__GPIO5_IO12 + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_ECSPI2_SS0__GPIO5_IO13 + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_usdhc2: pinctrlusdhc2grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + >; + }; + + pinctrl_reg_1v8: reg-1v8-grp { + fsl,pins = < + MX8MP_IOMUXC_SAI5_MCLK__GPIO3_IO25 + MX8MP_DSE_X1 + >; + }; + + pinctrl_reg_5v0_sensor: reg-5v0-sensorgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_RXD7__GPIO4_IO09 + MX8MP_DSE_X1 + >; + }; + + pinctrl_sai6: sai6grp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_TXD6__AUDIOMIX_SAI6_TX_SYNC + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_SAI1_RXD4__AUDIOMIX_SAI6_TX_BCLK + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_SAI1_TXD5__AUDIOMIX_SAI6_TX_DATA00 + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_SAI1_TXD7__AUDIOMIX_SAI6_MCLK + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + MX8MP_IOMUXC_SAI1_RXD5__AUDIOMIX_SAI6_RX_DATA00 + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + >; + }; + + pinctrl_tas_reset: tasresetgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI5_RXD3__GPIO3_IO24 + MX8MP_DSE_X1 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_UART1_TXD__UART1_DCE_TX + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + 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) + MX8MP_IOMUXC_SAI3_RXD__UART2_DCE_RTS + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_SAI3_RXC__UART2_DCE_CTS + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO02__WDOG1_WDOG_B + (MX8MP_DSE_X6 | MX8MP_HYS_SCHMITT) + >; + }; + + pinctrl_wl_int: wlintgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO13__GPIO1_IO13 + (MX8MP_PULL_UP | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_wl_reg: wlreggrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_RESET_B__GPIO2_IO19 + MX8MP_DSE_X1 + >; + }; +}; + +&sai6 { + assigned-clocks = <&clk IMX8MP_CLK_SAI6>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <12288000>; + pinctrl-0 = <&pinctrl_sai6>; + pinctrl-names = "default"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&uart1 { + pinctrl-0 = <&pinctrl_uart1>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart2 { + pinctrl-0 = <&pinctrl_uart2>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "infineon,cyw43439-bt", "brcm,bcm4329-bt"; + interrupt-parent = <&gpio1>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "host-wakeup"; + device-wakeup-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + max-speed = <921600>; + pinctrl-0 = <&pinctrl_bt>; + pinctrl-names = "default"; + shutdown-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + vbat-supply = <®_3v3_per>; + vddio-supply = <®_3v3_per>; + }; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb3_phy0 { + status = "okay"; +}; + +&usb3_phy1 { + status = "okay"; +}; + +&usb_dwc3_0 { + dr_mode = "peripheral"; +}; + +&usdhc2 { + #address-cells = <1>; + #size-cells = <0>; + assigned-clocks = <&clk IMX8MP_CLK_USDHC2>; + assigned-clock-rates = <50000000>; + cap-power-off-card; + keep-power-in-suspend; + max-frequency = <50000000>; + mmc-pwrseq = <&wifi_powerseq>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc2>; + pinctrl-names = "default"; + sd-uhs-sdr25; + vmmc-supply = <®_3v3_per>; + status = "okay"; + + wifi@1 { + compatible = "infineon,cyw43439-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&gpio1>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "host-wake"; + pinctrl-0 = <&pinctrl_wl_int>; + pinctrl-names = "default"; + brcm,board-type = "GOcontroll,moduline"; + }; +}; + +&wdog1 { + pinctrl-0 = <&pinctrl_wdog>; + pinctrl-names = "default"; + fsl,ext-reset-output; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi new file mode 100644 index 000000000000..fe8ba16eb40e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2020 Lothar Waßmann + * 2025 Maud Spierings + */ + +#include "imx8mp.dtsi" + +/ { + /* PHY regulator */ + regulator-3v3-etn { + compatible = "regulator-fixed"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + enable-active-high; + pinctrl-0 = <&pinctrl_reg_3v3_etn>; + pinctrl-names = "default"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3-etn"; + vin-supply = <®_vdd_3v3>; + }; +}; + +&A53_0 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_1 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_2 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_3 { + cpu-supply = <®_vdd_arm>; +}; + +&eqos { + assigned-clocks = <&clk IMX8MP_CLK_ENET_AXI>, + <&clk IMX8MP_CLK_ENET_QOS_TIMER>, + <&clk IMX8MP_CLK_ENET_QOS>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_266M>, + <&clk IMX8MP_SYS_PLL2_100M>, + <&clk IMX8MP_SYS_PLL2_50M>; + assigned-clock-rates = <266000000>, <100000000>, <50000000>; + phy-handle = <ðphy0>; + phy-mode = "rmii"; + 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>; + pinctrl-0 = <&pinctrl_ethphy_rst_b>; + pinctrl-names = "default"; + reset-delay-us = <25000>; + reset-gpios = <&gpio4 22 GPIO_ACTIVE_LOW>; + + ethphy0: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&gpio4>; + interrupts = <21 IRQ_TYPE_EDGE_FALLING>; + clocks = <&clk IMX8MP_CLK_ENET_QOS>; + pinctrl-0 = <&pinctrl_ethphy_int_b>; + pinctrl-names = "default"; + smsc,disable-energy-detect; + }; + }; +}; + +&gpio1 { + gpio-line-names = "SODIMM_152", + "SODIMM_42", + "PMIC_WDOG_B 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_MDC", + "ENET_MDIO", + "", + "ENET_XTAL1/CLKIN", + "ENET_TXD1", + "ENET_TXD0", + "ENET_TXEN", + "ENET_POWER", + "ENET_COL/CRS_DV", + "ENET_RXER", + "ENET_RXD0", + "ENET_RXD1", + "", + "", + "", + ""; +}; + +&gpio2 { + gpio-line-names = "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SODIMM_51", + "SODIMM_57", + "SODIMM_56", + "SODIMM_52", + "SODIMM_53", + "SODIMM_54", + "SODIMM_55", + "SODIMM_15", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""; +}; + +&gpio3 { + gpio-line-names = "", + "", + "EMMC_DS", + "EMMC_DAT5", + "EMMC_DAT6", + "EMMC_DAT7", + "", + "", + "", + "", + "EMMC_DAT0", + "EMMC_DAT1", + "EMMC_DAT2", + "EMMC_DAT3", + "", + "EMMC_DAT4", + "", + "EMMC_CLK", + "EMMC_CMD", + "SODIMM_75", + "SODIMM_145", + "SODIMM_163", + "SODIMM_164", + "SODIMM_165", + "SODIMM_143", + "SODIMM_144", + "SODIMM_72", + "SODIMM_73", + "SODIMM_74", + "SODIMM_93", + "", + ""; +}; + +&gpio4 { + gpio-line-names = "SODIMM_98", + "SODIMM_99", + "SODIMM_100", + "SODIMM_101", + "SODIMM_45", + "SODIMM_43", + "SODIMM_105", + "SODIMM_106", + "SODIMM_107", + "SODIMM_108", + "SODIMM_104", + "SODIMM_103", + "SODIMM_115", + "SODIMM_114", + "SODIMM_113", + "SODIMM_112", + "SODIMM_109", + "SODIMM_110", + "SODIMM_95", + "SODIMM_96", + "SODIMM_97", + "ENET_nINT", + "ENET_nRST", + "SODIMM_84", + "SODIMM_87", + "SODIMM_86", + "SODIMM_85", + "SODIMM_83", + "", + "SODIMM_66", + "SODIMM_65", + ""; +}; + +&gpio5 { + gpio-line-names = "", + "", + "", + "SODIMM_76", + "SODIMM_81", + "SODIMM_146", + "SODIMM_48", + "SODIMM_46", + "SODIMM_47", + "SODIMM_44", + "SODIMM_49", + "", + "SODIMM_70", + "SODIMM_69", + "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_68", + "SODIMM_67", + "", + ""; +}; + +&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@25 { + compatible = "nxp,pca9450c"; + reg = <0x25>; + interrupt-parent = <&gpio1>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&pinctrl_pmic>; + pinctrl-names = "default"; + + regulators { + reg_vdd_soc: BUCK1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <805000>; + regulator-name = "vdd-soc"; + regulator-ramp-delay = <3125>; + }; + + reg_vdd_arm: BUCK2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <950000>; + regulator-min-microvolt = <805000>; + regulator-name = "vdd-core"; + regulator-ramp-delay = <3125>; + nxp,dvs-run-voltage = <950000>; + nxp,dvs-standby-voltage = <850000>; + }; + + reg_vdd_3v3: BUCK4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3"; + }; + + reg_nvcc_nand: BUCK5 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "nvcc-nand"; + }; + + reg_nvcc_dram: BUCK6 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1100000>; + regulator-min-microvolt = <1100000>; + regulator-name = "nvcc-dram"; + }; + + reg_snvs_1v8: LDO1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "snvs-1v8"; + }; + + ldo2_reg: LDO2 { + regulator-always-on; + regulator-max-microvolt = <1150000>; + regulator-min-microvolt = <800000>; + regulator-name = "LDO2"; + }; + + reg_vdda_1v8: LDO3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "vdda-1v8"; + }; + + ldo4_reg: LDO4 { + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <800000>; + regulator-name = "LDO4"; + }; + + ldo5_reg: LDO5 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + regulator-name = "LDO5"; + }; + }; + }; +}; + +&iomuxc { + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX8MP_IOMUXC_ENET_TD2__CCM_ENET_QOS_CLOCK_GENERATE_REF_CLK + (MX8MP_DSE_X4 | MX8MP_PULL_UP | MX8MP_PULL_ENABLE | MX8MP_SION) + MX8MP_IOMUXC_ENET_MDC__ENET_QOS_MDC + (MX8MP_DSE_X4 | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_MDIO__ENET_QOS_MDIO + (MX8MP_DSE_X4 | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_TD0__ENET_QOS_RGMII_TD0 + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST) + MX8MP_IOMUXC_ENET_TD1__ENET_QOS_RGMII_TD1 + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST) + MX8MP_IOMUXC_ENET_RD0__ENET_QOS_RGMII_RD0 + (MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RD1__ENET_QOS_RGMII_RD1 + (MX8MP_FSEL_FAST | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RXC__ENET_QOS_RX_ER + (MX8MP_FSEL_FAST | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RX_CTL__ENET_QOS_RGMII_RX_CTL + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_TX_CTL__ENET_QOS_RGMII_TX_CTL + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST) + >; + }; + + pinctrl_eqos_sleep: eqos-sleep-grp { + fsl,pins = < + MX8MP_IOMUXC_ENET_TD2__GPIO1_IO19 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_MDC__GPIO1_IO16 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_MDIO__GPIO1_IO17 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_TD0__GPIO1_IO21 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_TD1__GPIO1_IO20 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RD0__GPIO1_IO26 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RD1__GPIO1_IO27 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RXC__GPIO1_IO25 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_RX_CTL__GPIO1_IO24 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ENET_TX_CTL__GPIO1_IO22 + (MX8MP_ODE_ENABLE | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_ethphy_int_b: ethphy-int-bgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI2_RXFS__GPIO4_IO21 + (MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT) + >; + }; + + pinctrl_ethphy_rst_b: ethphy-rst-bgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI2_RXC__GPIO4_IO22 + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_I2C1_SDA__I2C1_SDA + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_i2c1_gpio: i2c1-gpiogrp { + fsl,pins = < + MX8MP_IOMUXC_I2C1_SCL__GPIO5_IO14 + MX8MP_I2C_DEFAULT + MX8MP_IOMUXC_I2C1_SDA__GPIO5_IO15 + MX8MP_I2C_DEFAULT + >; + }; + + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 + (MX8MP_PULL_UP | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_reg_3v3_etn: reg-3v3-etngrp { + fsl,pins = < + MX8MP_IOMUXC_ENET_TXC__GPIO1_IO23 + (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK + (MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD + MX8MP_USDHC_DATA_DEFAULT + 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 + (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 + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + 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) + >; + }; +}; + +&usdhc3 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC3>; + assigned-clock-rates = <200000000>; + bus-width = <8>; + max-frequency = <200000000>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <®_vdd_3v3>; + voltage-ranges = <3300 3300>; + vqmmc-supply = <®_nvcc_nand>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-var-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-var-som.dtsi index b59da91fdd04..29f080904482 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-var-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-var-som.dtsi @@ -55,6 +55,24 @@ reg_usdhc2_vqmmc: regulator-usdhc2-vqmmc { states = <3300000 0x0 1800000 0x1>; vin-supply = <&ldo5>; }; + + reg_phy_supply: regulator-phy-supply { + compatible = "regulator-fixed"; + regulator-name = "phy-supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <20000>; + gpio = <&gpio2 20 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + reg_phy_vddio: regulator-phy-vddio { + compatible = "regulator-fixed"; + regulator-name = "vddio-1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; }; &A53_0 { @@ -73,6 +91,53 @@ &A53_3 { cpu-supply = <&buck2>; }; +&eqos { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos>; + /* + * 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>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <100000>; + vddio-supply = <®_phy_vddio>; + + 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"; + }; + }; + }; + }; +}; + &i2c1 { clock-frequency = <400000>; pinctrl-names = "default"; @@ -239,6 +304,27 @@ &wdog1 { &iomuxc { + 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_SD2_WP__GPIO2_IO20 0x10 + MX8MP_IOMUXC_GPIO1_IO10__GPIO1_IO10 0x150 + >; + }; + pinctrl_i2c1: i2c1grp { fsl,pins = < MX8MP_IOMUXC_SD1_DATA4__I2C1_SCL 0x400001c2 diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw702x.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw702x.dtsi index 10713c34ff39..cbf0c9a740fa 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw702x.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw702x.dtsi @@ -434,6 +434,8 @@ &usdhc3 { pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + assigned-clocks = <&clk IMX8MP_CLK_USDHC3>; + assigned-clock-rates = <400000000>; bus-width = <8>; non-removable; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw71xx.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw71xx.dtsi index 2f740d74707b..4bf818873fe3 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw71xx.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw71xx.dtsi @@ -70,7 +70,7 @@ &ecspi2 { tpm@1 { compatible = "atmel,attpm20p", "tcg,tpm_tis-spi"; reg = <0x1>; - spi-max-frequency = <36000000>; + spi-max-frequency = <25000000>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw72xx.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw72xx.dtsi index 5ab3ffe9931d..cf747ec6fa16 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw72xx.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw72xx.dtsi @@ -110,7 +110,7 @@ &ecspi2 { tpm@1 { compatible = "atmel,attpm20p", "tcg,tpm_tis-spi"; reg = <0x1>; - spi-max-frequency = <36000000>; + spi-max-frequency = <25000000>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw73xx.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw73xx.dtsi index e2b5e7ac3e46..5eb114d2360a 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw73xx.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw73xx.dtsi @@ -122,7 +122,7 @@ &ecspi2 { tpm@1 { compatible = "atmel,attpm20p", "tcg,tpm_tis-spi"; reg = <0x1>; - spi-max-frequency = <36000000>; + spi-max-frequency = <25000000>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts index 6daa2313f879..12de7cf1e853 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts @@ -201,7 +201,7 @@ &ecspi1 { tpm@0 { compatible = "atmel,attpm20p", "tcg,tpm_tis-spi"; reg = <0x0>; - spi-max-frequency = <36000000>; + spi-max-frequency = <25000000>; }; }; @@ -301,7 +301,7 @@ &gpio2 { &gpio3 { gpio-line-names = "", "", "", "", "", "", "m2_rst", "", - "", "", "", "", "", "", "m2_gpio10", "", + "", "", "", "", "", "", "m2_wdis2#", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""; }; @@ -310,7 +310,7 @@ &gpio4 { gpio-line-names = "", "", "m2_off#", "", "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "m2_wdis#", "", "", "", "", "", + "", "", "m2_wdis1#", "", "", "", "", "", "", "", "", "", "", "", "", "rs485_en"; }; @@ -811,14 +811,14 @@ pinctrl_hog: hoggrp { MX8MP_IOMUXC_GPIO1_IO09__GPIO1_IO09 0x40000040 /* DIO0 */ MX8MP_IOMUXC_GPIO1_IO11__GPIO1_IO11 0x40000040 /* DIO1 */ MX8MP_IOMUXC_SAI1_RXD0__GPIO4_IO02 0x40000040 /* M2SKT_OFF# */ - MX8MP_IOMUXC_SAI1_TXD6__GPIO4_IO18 0x40000150 /* M2SKT_WDIS# */ + MX8MP_IOMUXC_SAI1_TXD6__GPIO4_IO18 0x40000150 /* M2SKT_WDIS1# */ MX8MP_IOMUXC_SD1_DATA4__GPIO2_IO06 0x40000040 /* M2SKT_PIN20 */ MX8MP_IOMUXC_SD1_STROBE__GPIO2_IO11 0x40000040 /* M2SKT_PIN22 */ MX8MP_IOMUXC_SD2_CLK__GPIO2_IO13 0x40000150 /* PCIE1_WDIS# */ MX8MP_IOMUXC_SD2_CMD__GPIO2_IO14 0x40000150 /* PCIE3_WDIS# */ MX8MP_IOMUXC_SD2_DATA3__GPIO2_IO18 0x40000150 /* PCIE2_WDIS# */ MX8MP_IOMUXC_NAND_DATA00__GPIO3_IO06 0x40000040 /* M2SKT_RST# */ - MX8MP_IOMUXC_NAND_DQS__GPIO3_IO14 0x40000040 /* M2SKT_GPIO10 */ + MX8MP_IOMUXC_NAND_DQS__GPIO3_IO14 0x40000150 /* M2KST_WDIS2# */ MX8MP_IOMUXC_SAI3_TXD__GPIO5_IO01 0x40000104 /* UART_TERM */ MX8MP_IOMUXC_SAI3_TXFS__GPIO4_IO31 0x40000104 /* UART_RS485 */ MX8MP_IOMUXC_SAI3_TXC__GPIO5_IO00 0x40000104 /* UART_HALF */ diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index 948b88cf5e9d..bb24dba7338e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -320,7 +320,10 @@ map0 { <&A53_0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, <&A53_1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, <&A53_2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&A53_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + <&A53_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&gpu3d THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&gpu2d THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&npu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; }; @@ -350,7 +353,10 @@ map0 { <&A53_0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, <&A53_1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, <&A53_2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&A53_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + <&A53_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&gpu3d THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&gpu2d THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&npu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; }; @@ -876,24 +882,17 @@ pgc_mediamix: power-domain@10 { pgc_vpu_g1: power-domain@11 { #power-domain-cells = <0>; - power-domains = <&pgc_vpumix>; reg = ; - clocks = <&clk IMX8MP_CLK_VPU_G1_ROOT>; }; pgc_vpu_g2: power-domain@12 { #power-domain-cells = <0>; - power-domains = <&pgc_vpumix>; reg = ; - clocks = <&clk IMX8MP_CLK_VPU_G2_ROOT>; - }; pgc_vpu_vc8000e: power-domain@13 { #power-domain-cells = <0>; - power-domains = <&pgc_vpumix>; reg = ; - clocks = <&clk IMX8MP_CLK_VPU_VC8KE_ROOT>; }; pgc_hdmimix: power-domain@14 { @@ -2235,6 +2234,7 @@ gpu3d: gpu@38000000 { <&clk IMX8MP_CLK_GPU_ROOT>, <&clk IMX8MP_CLK_GPU_AHB>; clock-names = "core", "shader", "bus", "reg"; + #cooling-cells = <2>; assigned-clocks = <&clk IMX8MP_CLK_GPU3D_CORE>, <&clk IMX8MP_CLK_GPU3D_SHADER_CORE>; assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_1000M>, @@ -2251,6 +2251,7 @@ gpu2d: gpu@38008000 { <&clk IMX8MP_CLK_GPU_ROOT>, <&clk IMX8MP_CLK_GPU_AHB>; clock-names = "core", "bus", "reg"; + #cooling-cells = <2>; assigned-clocks = <&clk IMX8MP_CLK_GPU2D_CORE>; assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_1000M>; assigned-clock-rates = <1000000000>; @@ -2263,8 +2264,8 @@ vpu_g1: video-codec@38300000 { interrupts = ; clocks = <&clk IMX8MP_CLK_VPU_G1_ROOT>; assigned-clocks = <&clk IMX8MP_CLK_VPU_G1>; - assigned-clock-parents = <&clk IMX8MP_VPU_PLL_OUT>; - assigned-clock-rates = <600000000>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_800M>; + assigned-clock-rates = <800000000>; power-domains = <&vpumix_blk_ctrl IMX8MP_VPUBLK_PD_G1>; }; @@ -2273,9 +2274,9 @@ vpu_g2: video-codec@38310000 { reg = <0x38310000 0x10000>; interrupts = ; clocks = <&clk IMX8MP_CLK_VPU_G2_ROOT>; - assigned-clocks = <&clk IMX8MP_CLK_VPU_G2>; - assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_1000M>; - assigned-clock-rates = <500000000>; + assigned-clocks = <&clk IMX8MP_CLK_VPU_G2>, <&clk IMX8MP_VPU_PLL_OUT>; + assigned-clock-parents = <&clk IMX8MP_VPU_PLL_OUT>; + assigned-clock-rates = <700000000>, <700000000>; power-domains = <&vpumix_blk_ctrl IMX8MP_VPUBLK_PD_G2>; }; @@ -2290,9 +2291,9 @@ vpumix_blk_ctrl: blk-ctrl@38330000 { <&clk IMX8MP_CLK_VPU_G2_ROOT>, <&clk IMX8MP_CLK_VPU_VC8KE_ROOT>; clock-names = "g1", "g2", "vc8000e"; - assigned-clocks = <&clk IMX8MP_CLK_VPU_BUS>, <&clk IMX8MP_VPU_PLL>; - assigned-clock-parents = <&clk IMX8MP_VPU_PLL_OUT>; - assigned-clock-rates = <600000000>, <600000000>; + assigned-clocks = <&clk IMX8MP_CLK_VPU_BUS>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_800M>; + assigned-clock-rates = <800000000>; interconnects = <&noc IMX8MP_ICM_VPU_G1 &noc IMX8MP_ICN_VIDEO>, <&noc IMX8MP_ICM_VPU_G2 &noc IMX8MP_ICN_VIDEO>, <&noc IMX8MP_ICM_VPU_H1 &noc IMX8MP_ICN_VIDEO>; @@ -2308,6 +2309,7 @@ npu: npu@38500000 { <&clk IMX8MP_CLK_ML_AXI>, <&clk IMX8MP_CLK_ML_AHB>; clock-names = "core", "shader", "bus", "reg"; + #cooling-cells = <2>; power-domains = <&pgc_mlmix>; }; diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi0.dtso b/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi0.dtso new file mode 100644 index 000000000000..ceb63c28b21a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi0.dtso @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&i2c_mipi_csi0 { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + pinctrl-0 = <&pinctrl_i2c_mipi_csi0>; + pinctrl-names = "default"; + status = "okay"; + + ov5640_mipi_0: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + clocks = <&xtal24m>; + clock-names = "xclk"; + pinctrl-0 = <&pinctrl_mipi_csi0>; + pinctrl-names = "default"; + powerdown-gpios = <&lsio_gpio1 28 GPIO_ACTIVE_HIGH>; + reset-gpios = <&lsio_gpio1 27 GPIO_ACTIVE_LOW>; + AVDD-supply = <®_2v8>; + DVDD-supply = <®_1v5>; + DOVDD-supply = <®_1v8>; + + port { + ov5640_mipi_0_ep: endpoint { + bus-type = ; + data-lanes = <1 2>; + remote-endpoint = <&mipi_csi0_in>; + }; + }; + }; +}; + +&irqsteer_csi0 { + status = "okay"; +}; + +&isi { + status = "okay"; +}; + +&mipi_csi_0 { + status = "okay"; + + ports { + port@0 { + mipi_csi0_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&ov5640_mipi_0_ep>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi1.dtso b/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi1.dtso new file mode 100644 index 000000000000..9e6d33c0315e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek-ov5640-csi1.dtso @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&i2c_mipi_csi1 { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + pinctrl-0 = <&pinctrl_i2c_mipi_csi1>; + pinctrl-names = "default"; + status = "okay"; + + ov5640_mipi_1: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + clocks = <&xtal24m>; + clock-names = "xclk"; + pinctrl-0 = <&pinctrl_mipi_csi1>; + pinctrl-names = "default"; + powerdown-gpios = <&lsio_gpio1 31 GPIO_ACTIVE_HIGH>; + reset-gpios = <&lsio_gpio1 30 GPIO_ACTIVE_LOW>; + AVDD-supply = <®_2v8>; + DVDD-supply = <®_1v5>; + DOVDD-supply = <®_1v8>; + + port { + ov5640_mipi_1_ep: endpoint { + bus-type = ; + data-lanes = <1 2>; + remote-endpoint = <&mipi_csi1_in>; + }; + }; + }; +}; + +&irqsteer_csi1 { + status = "okay"; +}; + +&isi { + status = "okay"; +}; + +&mipi_csi_1 { + status = "okay"; + + ports { + port@0 { + mipi_csi1_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&ov5640_mipi_1_ep>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts index 353f825a8ac5..95523c538135 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts @@ -32,6 +32,13 @@ memory@80000000 { reg = <0x00000000 0x80000000 0 0x40000000>; }; + xtal24m: clock-xtal24m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "xtal_24MHz"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -113,6 +120,15 @@ dsp_vdev0buffer: memory@94300000 { reg = <0 0x94300000 0 0x100000>; no-map; }; + + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0xc0000000 0 0x3c000000>; + size = <0 0x3c000000>; + linux,cma-default; + reusable; + }; }; lvds_backlight0: backlight-lvds0 { @@ -131,6 +147,72 @@ lvds_backlight1: backlight-lvds1 { default-brightness-level = <80>; }; + i2c-mux { + compatible = "i2c-mux-gpio"; + mux-gpios = <&lsio_gpio5 3 GPIO_ACTIVE_HIGH>; /* needs to be an unused GPIO */ + i2c-parent = <&i2c1>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + wm8960: audio-codec@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; + clock-names = "mclk"; + assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, + <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clock-rates = <786432000>, + <49152000>, + <12288000>, + <12288000>; + wlf,shared-lrclk; + wlf,hp-cfg = <2 2 3>; + wlf,gpio-cfg = <1 3>; + AVDD-supply = <®_audio_3v3>; + DBVDD-supply = <®_audio_1v8>; + DCVDD-supply = <®_audio_1v8>; + SPKVDD1-supply = <®_audio_5v>; + SPKVDD2-supply = <®_audio_5v>; + }; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + wm8962: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, + <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clock-rates = <786432000>, + <49152000>, + <12288000>, + <12288000>; + DCVDD-supply = <®_audio_1v8>; + DBVDD-supply = <®_audio_1v8>; + AVDD-supply = <®_audio_1v8>; + CPVDD-supply = <®_audio_1v8>; + MICVDD-supply = <®_audio_3v3>; + PLLVDD-supply = <®_audio_1v8>; + SPKVDD1-supply = <®_audio_5v>; + SPKVDD2-supply = <®_audio_5v>; + }; + }; + + }; + mux-controller { compatible = "nxp,cbdtu02043", "gpio-sbu-mux"; pinctrl-names = "default"; @@ -146,6 +228,27 @@ usb3_data_ss: endpoint { }; }; + reg_1v5: regulator-1v5 { + compatible = "regulator-fixed"; + regulator-name = "1v5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + }; + + reg_1v8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_2v8: regulator-2v8 { + compatible = "regulator-fixed"; + regulator-name = "2v8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + reg_usdhc2_vmmc: usdhc2-vmmc { compatible = "regulator-fixed"; regulator-name = "SD1_SPWR"; @@ -314,6 +417,21 @@ sound-wm8960 { "Mic Jack", "MICB"; }; + sound-wm8962 { + compatible = "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + audio-cpu = <&sai1>; + audio-codec = <&wm8962>; + hp-det-gpios = <&lsio_gpio0 31 GPIO_ACTIVE_HIGH>; + audio-routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN1R", "AMIC", + "IN3R", "AMIC"; + }; + imx8qm-cm4-0 { compatible = "fsl,imx8qm-cm4"; clocks = <&clk_dummy>; @@ -511,26 +629,6 @@ &i2c1 { scl-gpios = <&lsio_gpio0 14 GPIO_ACTIVE_HIGH>; sda-gpios = <&lsio_gpio0 15 GPIO_ACTIVE_HIGH>; status = "okay"; - - wm8960: audio-codec@1a { - compatible = "wlf,wm8960"; - reg = <0x1a>; - clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; - clock-names = "mclk"; - assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, - <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, - <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, - <&mclkout0_lpcg IMX_LPCG_CLK_0>; - assigned-clock-rates = <786432000>, <49152000>, <12288000>, <12288000>; - wlf,shared-lrclk; - wlf,hp-cfg = <2 2 3>; - wlf,gpio-cfg = <1 3>; - AVDD-supply = <®_audio_3v3>; - DBVDD-supply = <®_audio_1v8>; - DCVDD-supply = <®_audio_1v8>; - SPKVDD1-supply = <®_audio_5v>; - SPKVDD2-supply = <®_audio_5v>; - }; }; &i2c1_lvds0 { @@ -815,6 +913,20 @@ IMX8QM_QSPI1A_DATA1_LSIO_GPIO4_IO25 0x0600004c >; }; + pinctrl_i2c_mipi_csi0: i2c-mipi-csi0grp { + fsl,pins = < + IMX8QM_MIPI_CSI0_I2C0_SCL_MIPI_CSI0_I2C0_SCL 0xc2000020 + IMX8QM_MIPI_CSI0_I2C0_SDA_MIPI_CSI0_I2C0_SDA 0xc2000020 + >; + }; + + pinctrl_i2c_mipi_csi1: i2c-mipi-csi1grp { + fsl,pins = < + IMX8QM_MIPI_CSI1_I2C0_SCL_MIPI_CSI1_I2C0_SCL 0xc2000020 + IMX8QM_MIPI_CSI1_I2C0_SDA_MIPI_CSI1_I2C0_SDA 0xc2000020 + >; + }; + pinctrl_i2c0: i2c0grp { fsl,pins = < IMX8QM_HDMI_TX0_TS_SCL_DMA_I2C0_SCL 0x06000021 @@ -1008,6 +1120,22 @@ IMX8QM_LVDS1_I2C1_SDA_LVDS1_I2C1_SDA 0xc600004c >; }; + pinctrl_mipi_csi0: mipi-csi0grp { + fsl,pins = < + IMX8QM_MIPI_CSI0_GPIO0_00_LSIO_GPIO1_IO27 0xC0000041 + IMX8QM_MIPI_CSI0_GPIO0_01_LSIO_GPIO1_IO28 0xC0000041 + IMX8QM_MIPI_CSI0_MCLK_OUT_MIPI_CSI0_ACM_MCLK_OUT 0xC0000041 + >; + }; + + pinctrl_mipi_csi1: mipi-csi1grp { + fsl,pins = < + IMX8QM_MIPI_CSI1_GPIO0_00_LSIO_GPIO1_IO30 0xC0000041 + IMX8QM_MIPI_CSI1_GPIO0_01_LSIO_GPIO1_IO31 0xC0000041 + IMX8QM_MIPI_CSI1_MCLK_OUT_MIPI_CSI1_ACM_MCLK_OUT 0xC0000041 + >; + }; + pinctrl_pciea: pcieagrp { fsl,pins = < IMX8QM_PCIE_CTRL0_WAKE_B_LSIO_GPIO4_IO28 0x04000021 diff --git a/arch/arm64/boot/dts/freescale/imx8qm-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8qm-ss-img.dtsi index 2bbdacb1313f..4b7e685daa02 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm-ss-img.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8qm-ss-img.dtsi @@ -3,6 +3,31 @@ * Copyright 2021 NXP */ +&isi { + compatible = "fsl,imx8qm-isi"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + + isi_in_2: endpoint { + remote-endpoint = <&mipi_csi0_out>; + }; + }; + + port@3 { + reg = <3>; + + isi_in_3: endpoint { + remote-endpoint = <&mipi_csi1_out>; + }; + }; + }; +}; + &jpegdec { compatible = "nxp,imx8qm-jpgdec", "nxp,imx8qxp-jpgdec"; }; @@ -10,3 +35,57 @@ &jpegdec { &jpegenc { compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc"; }; + +&mipi_csi_0 { + compatible = "fsl,imx8qm-mipi-csi2", "fsl,imx8qxp-mipi-csi2"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + + mipi_csi0_out: endpoint { + remote-endpoint = <&isi_in_2>; + }; + }; + }; +}; + +&mipi_csi_1 { + compatible = "fsl,imx8qm-mipi-csi2", "fsl,imx8qxp-mipi-csi2"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + + mipi_csi1_out: endpoint { + remote-endpoint = <&isi_in_3>; + }; + }; + }; +}; + +&pi0_ipg_lpcg { + status = "disabled"; +}; + +&pi0_misc_lpcg { + status = "disabled"; +}; + +&pi0_pxl_lpcg { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qm.dtsi b/arch/arm64/boot/dts/freescale/imx8qm.dtsi index 6fa31bc9ece8..827e1365b5da 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8qm.dtsi @@ -333,6 +333,11 @@ iomuxc: pinctrl { compatible = "fsl,imx8qm-iomuxc"; }; + scu_reset: reset-controller { + compatible = "fsl,imx-scu-reset"; + #reset-cells = <1>; + }; + rtc: rtc { compatible = "fsl,imx8qxp-sc-rtc"; }; @@ -356,6 +361,11 @@ tsens: thermal-sensor { compatible = "fsl,imx8qxp-sc-thermal", "fsl,imx-sc-thermal"; #thermal-sensor-cells = <1>; }; + + watchdog { + compatible = "fsl,imx8qm-sc-wdt", "fsl,imx-sc-wdt"; + timeout-sec = <60>; + }; }; thermal-zones { @@ -612,6 +622,7 @@ vpu_dsp: dsp@556e8000 { }; /* sorted in register address */ + #include "imx8-ss-security.dtsi" #include "imx8-ss-cm41.dtsi" #include "imx8-ss-audio.dtsi" #include "imx8-ss-vpu.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-csi.dtso b/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-csi.dtso new file mode 100644 index 000000000000..dd65ed8bb37c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-csi.dtso @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ +/dts-v1/; +/plugin/; + +#include +#include + +&i2c_mipi_csi0 { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + pinctrl-0 = <&pinctrl_i2c_mipi_csi0>; + pinctrl-names = "default"; + status = "okay"; + + ov5640_mipi: camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + clocks = <&xtal24m>; + clock-names = "xclk"; + pinctrl-0 = <&pinctrl_mipi_csi0>; + pinctrl-names = "default"; + powerdown-gpios = <&lsio_gpio3 7 GPIO_ACTIVE_HIGH>; + reset-gpios = <&lsio_gpio3 8 GPIO_ACTIVE_LOW>; + AVDD-supply = <®_2v8>; + DVDD-supply = <®_1v5>; + DOVDD-supply = <®_1v8>; + + port { + ov5640_mipi_ep: endpoint { + bus-type = ; + data-lanes = <1 2>; + remote-endpoint = <&mipi_csi0_in>; + }; + }; + }; +}; + +&irqsteer_csi0 { + status = "okay"; +}; + +&isi { + status = "okay"; +}; + +&mipi_csi_0 { + status = "okay"; + + ports { + port@0 { + mipi_csi0_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&ov5640_mipi_ep>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts index c93d123670bd..e54be7f649ff 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts @@ -64,6 +64,92 @@ usb3_data_ss: endpoint { }; }; + i2c-mux { + compatible = "i2c-mux-gpio"; + mux-gpios = <&lsio_gpio5 0 GPIO_ACTIVE_HIGH>; /* needs to be an unused GPIO */ + i2c-parent = <&cm40_i2c>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + wm8960: audio-codec@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; + clock-names = "mclk"; + assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, + <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clock-rates = <786432000>, + <49152000>, + <12288000>, + <12288000>; + wlf,shared-lrclk; + wlf,hp-cfg = <2 2 3>; + wlf,gpio-cfg = <1 3>; + AVDD-supply = <®_audio_3v3>; + DBVDD-supply = <®_audio_1v8>; + DCVDD-supply = <®_audio_1v8>; + SPKVDD1-supply = <®_audio_5v>; + SPKVDD2-supply = <®_audio_5v>; + }; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + wm8962: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, + <&mclkout0_lpcg IMX_LPCG_CLK_0>; + assigned-clock-rates = <786432000>, + <49152000>, + <12288000>, + <12288000>; + DCVDD-supply = <®_audio_1v8>; + DBVDD-supply = <®_audio_1v8>; + AVDD-supply = <®_audio_1v8>; + CPVDD-supply = <®_audio_1v8>; + MICVDD-supply = <®_audio_3v3>; + PLLVDD-supply = <®_audio_1v8>; + SPKVDD1-supply = <®_audio_5v>; + SPKVDD2-supply = <®_audio_5v>; + }; + }; + }; + + reg_1v5: regulator-1v5 { + compatible = "regulator-fixed"; + regulator-name = "1v5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + }; + + reg_1v8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_2v8: regulator-2v8 { + compatible = "regulator-fixed"; + regulator-name = "2v8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + reg_pcieb: regulator-pcie { compatible = "regulator-fixed"; regulator-max-microvolt = <3300000>; @@ -187,6 +273,15 @@ dsp_vdev0buffer: memory@94300000 { no-map; }; + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0xc0000000 0 0x3c000000>; + size = <0 0x3c000000>; + linux,cma-default; + reusable; + }; + gpu_reserved: memory@880000000 { no-map; reg = <0x8 0x80000000 0 0x10000000>; @@ -248,6 +343,21 @@ sound-wm8960 { "LINPUT1", "Mic Jack", "Mic Jack", "MICB"; }; + + sound-wm8962 { + compatible = "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + audio-cpu = <&sai1>; + audio-codec = <&wm8962>; + hp-det-gpios = <&lsio_gpio1 0 GPIO_ACTIVE_HIGH>; + audio-routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC", + "IN1R", "AMIC"; + }; }; &amix { @@ -427,29 +537,6 @@ &cm40_i2c { sda-gpios = <&lsio_gpio1 9 GPIO_ACTIVE_HIGH>; status = "okay"; - wm8960: audio-codec@1a { - compatible = "wlf,wm8960"; - reg = <0x1a>; - clocks = <&mclkout0_lpcg IMX_LPCG_CLK_0>; - clock-names = "mclk"; - assigned-clocks = <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, - <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_SLV_BUS>, - <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>, - <&mclkout0_lpcg IMX_LPCG_CLK_0>; - assigned-clock-rates = <786432000>, - <49152000>, - <12288000>, - <12288000>; - wlf,shared-lrclk; - wlf,hp-cfg = <2 2 3>; - wlf,gpio-cfg = <1 3>; - AVDD-supply = <®_audio_3v3>; - DBVDD-supply = <®_audio_1v8>; - DCVDD-supply = <®_audio_1v8>; - SPKVDD1-supply = <®_audio_5v>; - SPKVDD2-supply = <®_audio_5v>; - }; - pca6416: gpio@20 { compatible = "ti,tca6416"; reg = <0x20>; @@ -780,6 +867,13 @@ IMX8QXP_FLEXCAN1_RX_ADMA_FLEXCAN1_RX 0x21 >; }; + pinctrl_i2c_mipi_csi0: i2c-mipi-csi0grp { + fsl,pins = < + IMX8QXP_MIPI_CSI0_I2C0_SCL_MIPI_CSI0_I2C0_SCL 0xc2000020 + IMX8QXP_MIPI_CSI0_I2C0_SDA_MIPI_CSI0_I2C0_SDA 0xc2000020 + >; + }; + pinctrl_ioexp_rst: ioexprstgrp { fsl,pins = < IMX8QXP_SPI2_SDO_LSIO_GPIO1_IO01 0x06000021 @@ -820,6 +914,14 @@ IMX8QXP_FLEXCAN2_RX_ADMA_UART3_RX 0x06000020 >; }; + pinctrl_mipi_csi0: mipi-csi0grp { + fsl,pins = < + IMX8QXP_MIPI_CSI0_GPIO0_01_LSIO_GPIO3_IO07 0xC0000041 + IMX8QXP_MIPI_CSI0_GPIO0_00_LSIO_GPIO3_IO08 0xC0000041 + IMX8QXP_MIPI_CSI0_MCLK_OUT_MIPI_CSI0_ACM_MCLK_OUT 0xC0000041 + >; + }; + pinctrl_pcieb: pcieagrp { fsl,pins = < IMX8QXP_PCIE_CTRL0_PERST_B_LSIO_GPIO4_IO00 0x06000021 diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi index 3a087317591d..232cf25dadfc 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi @@ -4,6 +4,86 @@ * Dong Aisheng */ +&csi1_pxl_lpcg { + status = "disabled"; +}; + +&csi1_core_lpcg { + status = "disabled"; +}; + +&csi1_esc_lpcg { + status = "disabled"; +}; + +&gpio0_mipi_csi1 { + status = "disabled"; +}; + +&i2c_mipi_csi1 { + status = "disabled"; +}; + +&irqsteer_csi1 { + status = "disabled"; +}; + +&isi { + compatible = "fsl,imx8qxp-isi"; + reg = <0x58100000 0x60000>; + interrupts = , + , + , + , + , + ; + clocks = <&pdma0_lpcg IMX_LPCG_CLK_0>, + <&pdma1_lpcg IMX_LPCG_CLK_0>, + <&pdma2_lpcg IMX_LPCG_CLK_0>, + <&pdma3_lpcg IMX_LPCG_CLK_0>, + <&pdma4_lpcg IMX_LPCG_CLK_0>, + <&pdma5_lpcg IMX_LPCG_CLK_0>; + clock-names = "per0", "per1", "per2", "per3", "per4", "per5"; + power-domains = <&pd IMX_SC_R_ISI_CH0>, + <&pd IMX_SC_R_ISI_CH1>, + <&pd IMX_SC_R_ISI_CH2>, + <&pd IMX_SC_R_ISI_CH3>, + <&pd IMX_SC_R_ISI_CH4>, + <&pd IMX_SC_R_ISI_CH5>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + + isi_in_2: endpoint { + remote-endpoint = <&mipi_csi0_out>; + }; + }; + }; +}; + +&mipi_csi_0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + + mipi_csi0_out: endpoint { + remote-endpoint = <&isi_in_2>; + }; + }; + }; +}; + &jpegdec { compatible = "nxp,imx8qxp-jpgdec"; }; @@ -11,3 +91,7 @@ &jpegdec { &jpegenc { compatible = "nxp,imx8qxp-jpgenc"; }; + +&mipi_csi_1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-ss-security.dtsi b/arch/arm64/boot/dts/freescale/imx8qxp-ss-security.dtsi new file mode 100644 index 000000000000..15f1239dab24 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8qxp-ss-security.dtsi @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Actia Nordic AB + */ + +&crypto { + compatible = "fsl,imx8qxp-caam", "fsl,sec-v4.0"; +}; + +&sec_jr2 { + compatible = "fsl,imx8qxp-job-ring", "fsl,sec-v4.0-job-ring"; +}; + +&sec_jr3 { + compatible = "fsl,imx8qxp-job-ring", "fsl,sec-v4.0-job-ring"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8qxp.dtsi b/arch/arm64/boot/dts/freescale/imx8qxp.dtsi index 05138326f0a5..9e46e16a8dc0 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8qxp.dtsi @@ -241,6 +241,11 @@ scu_key: keys { status = "disabled"; }; + scu_reset: reset-controller { + compatible = "fsl,imx-scu-reset"; + #reset-cells = <1>; + }; + rtc: rtc { compatible = "fsl,imx8qxp-sc-rtc"; }; @@ -321,6 +326,7 @@ map0 { /* sorted in register address */ #include "imx8-ss-img.dtsi" #include "imx8-ss-vpu.dtsi" + #include "imx8-ss-security.dtsi" #include "imx8-ss-cm40.dtsi" #include "imx8-ss-gpu0.dtsi" #include "imx8-ss-adma.dtsi" @@ -332,6 +338,7 @@ map0 { #include "imx8qxp-ss-img.dtsi" #include "imx8qxp-ss-vpu.dtsi" +#include "imx8qxp-ss-security.dtsi" #include "imx8qxp-ss-adma.dtsi" #include "imx8qxp-ss-conn.dtsi" #include "imx8qxp-ss-lsio.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi index 2562a35286c2..13b01f3aa2a4 100644 --- a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi @@ -686,6 +686,7 @@ gpioe: gpio@2d000000 { <&pcc4 IMX8ULP_CLK_PCTLE>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 32 24>; + ngpios = <24>; }; gpiof: gpio@2d010000 { @@ -701,6 +702,7 @@ gpiof: gpio@2d010000 { <&pcc4 IMX8ULP_CLK_PCTLF>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 64 32>; + ngpios = <32>; }; per_bridge5: bus@2d800000 { @@ -855,6 +857,7 @@ gpiod: gpio@2e200000 { <&pcc5 IMX8ULP_CLK_RGPIOD>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc1 0 0 24>; + ngpios = <24>; }; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts index 8491eb53120e..e24e12f04526 100644 --- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts @@ -95,6 +95,15 @@ reg_can2_standby: regulator-can2-standby { gpio = <&adp5585 6 GPIO_ACTIVE_LOW>; }; + 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"; pinctrl-names = "default"; @@ -107,6 +116,28 @@ reg_usdhc2_vmmc: regulator-usdhc2 { 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; + }; + + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6524 12 GPIO_ACTIVE_LOW>; + }; + backlight_lvds: backlight-lvds { compatible = "pwm-backlight"; pwms = <&adp5585 0 100000 0>; @@ -217,10 +248,10 @@ mdio { ethphy1: ethernet-phy@1 { reg = <1>; - eee-broken-1000t; reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; reset-deassert-us = <80000>; + realtek,clkout-disable; }; }; }; @@ -245,6 +276,7 @@ ethphy2: ethernet-phy@2 { reset-gpios = <&pcal6524 16 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; reset-deassert-us = <80000>; + realtek,clkout-disable; }; }; }; @@ -493,6 +525,10 @@ &lpuart5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart5>; status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; }; &micfil { @@ -594,6 +630,21 @@ &usdhc2 { no-mmc; }; +&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 = <&usdhc3_pwrseq>; + vmmc-supply = <®_usdhc3_vmmc>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + &wdog3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdog>; @@ -622,13 +673,13 @@ 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 0x5fe + 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 0x5fe + MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x58e MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e >; }; @@ -660,24 +711,17 @@ 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 0x5fe + 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 0x5fe + MX93_PAD_ENET2_TXC__ENET1_RGMII_TXC 0x58e MX93_PAD_ENET2_TX_CTL__ENET1_RGMII_TX_CTL 0x57e >; }; - pinctrl_lpi2c3: lpi2c3grp { - fsl,pins = < - MX93_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e - MX93_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e - >; - }; - pinctrl_fec_sleep: fecsleepgrp { fsl,pins = < MX93_PAD_ENET2_MDC__GPIO4_IO14 0x51e @@ -935,6 +979,59 @@ 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 = < + 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_wlan: usdhc3wlangrp { + fsl,pins = < + 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-14x14-evk.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts index f556b6569a68..c5d86b54ad33 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts @@ -99,6 +99,15 @@ reg_can2_en: regulator-can2-en { 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"; pinctrl-names = "default"; @@ -111,6 +120,23 @@ reg_usdhc2_vmmc: regulator-usdhc2 { off-on-delay-us = <12000>; }; + 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; + }; + reg_vdd_12v: regulator-vdd-12v { compatible = "regulator-fixed"; regulator-name = "reg_vdd_12v"; @@ -126,6 +152,11 @@ reg_vref_1v8: regulator-adc-vref { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; + + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6524 12 GPIO_ACTIVE_LOW>; + }; }; &adc1 { @@ -366,6 +397,21 @@ &usdhc2 { status = "okay"; }; +&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 = <&usdhc3_pwrseq>; + vmmc-supply = <®_usdhc3_vmmc>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + &wdog3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdog>; @@ -552,6 +598,59 @@ MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e >; }; + /* 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_wlan: usdhc3wlangrp { + fsl,pins = < + 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-9x9-qsb.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts index 75e67115d52f..f6f8d105b737 100644 --- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts @@ -82,6 +82,15 @@ reg_audio_pwr: regulator-audio-pwr { 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_rpi_3v3: regulator-rpi { compatible = "regulator-fixed"; regulator-name = "VDD_RPI_3V3"; @@ -103,6 +112,23 @@ reg_usdhc2_vmmc: regulator-usdhc2 { off-on-delay-us = <12000>; }; + 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; + }; + sound-bt-sco { compatible = "simple-audio-card"; simple-audio-card,name = "bt-sco-audio"; @@ -151,6 +177,11 @@ sound-wm8962 { "IN3R", "AMIC", "IN1R", "AMIC"; }; + + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6524 12 GPIO_ACTIVE_LOW>; + }; }; &adc1 { @@ -184,7 +215,6 @@ mdio { ethphy1: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <1>; - eee-broken-1000t; reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; reset-deassert-us = <80000>; @@ -265,6 +295,11 @@ rtc@53 { interrupt-parent = <&pcal6524>; interrupts = <1 IRQ_TYPE_EDGE_FALLING>; }; + + inertial-meter@6a { + compatible = "st,lsm6dso"; + reg = <0x6a>; + }; }; &lpi2c2 { @@ -380,6 +415,17 @@ &lpuart1 { /* console */ status = "okay"; }; +&lpuart5 { + /* BT */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + &micfil { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pdm>; @@ -458,6 +504,20 @@ &usdhc2 { status = "okay"; }; +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + mmc-pwrseq = <&usdhc3_pwrseq>; + vmmc-supply = <®_usdhc3_vmmc>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + &wdog3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdog>; @@ -650,6 +710,42 @@ MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e >; }; + /* 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_wdog: wdoggrp { fsl,pins = < MX93_PAD_WDOG_ANY__WDOG1_WDOG_ANY 0x31e diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash-peb-wlbt-07.dtso b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash-peb-wlbt-07.dtso new file mode 100644 index 000000000000..7381b87444e8 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash-peb-wlbt-07.dtso @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2025 PHYTEC Messtechnik GmbH + * Author: Primoz Fiser + */ + +/dts-v1/; +/plugin/; + +#include +#include "imx93-pinfunc.h" + +&{/} { + usdhc3_pwrseq: usdhc3-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>; + }; +}; + +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +/* + * NOTE: When uSDHC3 port is multiplexed on GPIO_IO[27:22] pads, it only + * supports 50 MHz mode, due to introduction of potential variations in + * trace impedance, drive strength, and timing skew. Refer to i.MX 93 + * Application Processors Data Sheet, Rev. 3, page 60 for more details. + */ +&usdhc3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_wlbt>; + pinctrl-1 = <&pinctrl_usdhc3_sleep>, <&pinctrl_wlbt>; + mmc-pwrseq = <&usdhc3_pwrseq>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + +&iomuxc { + pinctrl_uart5: uart5grp { + fsl,pins = < + MX93_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + MX93_PAD_DAP_TDI__LPUART5_RX 0x31e + MX93_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + MX93_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX93_PAD_GPIO_IO22__USDHC3_CLK 0x179e + MX93_PAD_SD3_CMD__USDHC3_CMD 0x4000178e + 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 + >; + }; + + pinctrl_usdhc3_sleep: usdhc3sleepgrp { + fsl,pins = < + MX93_PAD_GPIO_IO22__USDHC3_CLK 0x31e + MX93_PAD_SD3_CMD__USDHC3_CMD 0x31e + MX93_PAD_SD3_DATA0__USDHC3_DATA0 0x31e + MX93_PAD_SD3_DATA1__USDHC3_DATA1 0x31e + MX93_PAD_SD3_DATA2__USDHC3_DATA2 0x31e + MX93_PAD_SD3_DATA3__USDHC3_DATA3 0x31e + >; + }; + + pinctrl_wlbt: wlbtgrp { + fsl,pins = < + MX93_PAD_CCM_CLKO2__GPIO3_IO27 0x31e /* WAKE_DEV */ + MX93_PAD_CCM_CLKO3__GPIO4_IO28 0x31e /* WAKE_HOST */ + MX93_PAD_CCM_CLKO4__GPIO4_IO29 0x31e /* PDn */ + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts index 7e9d031a2f0e..475913cf0cb9 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts @@ -18,7 +18,6 @@ / { "fsl,imx93"; aliases { - ethernet0 = &fec; ethernet1 = &eqos; rtc0 = &i2c_rtc; rtc1 = &bbnsm_rtc; @@ -54,18 +53,10 @@ reg_vcc_1v8: regulator-vcc-1v8 { regulator-max-microvolt = <1800000>; regulator-min-microvolt = <1800000>; }; - - reg_vref_1v8: regulator-adc-vref { - compatible = "regulator-fixed"; - regulator-name = "VREF_1V8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; }; /* ADC */ &adc1 { - vref-supply = <®_vref_1v8>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-eval-01.dtso b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-eval-01.dtso new file mode 100644 index 000000000000..a20898734741 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-eval-01.dtso @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2025 PHYTEC Messtechnik GmbH + * Author: Andrej Picej + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include "imx93-pinfunc.h" + +&{/} { + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_keys>; + + button-s2 { + label = "sleep"; + linux,code = ; + gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + user-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_user_leds>; + + user-led2 { + gpios = <&gpio4 13 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + }; +}; + +&iomuxc { + pinctrl_gpio_keys: gpiokeysgrp { + fsl,pins = < + MX93_PAD_PDM_BIT_STREAM1__GPIO1_IO10 0x31e + >; + }; + + pinctrl_user_leds: userledsgrp { + fsl,pins = < + MX93_PAD_ENET1_RD3__GPIO4_IO13 0x31e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-wlbt-05.dtso b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-wlbt-05.dtso new file mode 100644 index 000000000000..a7285f009566 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-wlbt-05.dtso @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2025 PHYTEC Messtechnik GmbH + * Author: Andrej Picej + */ + +/dts-v1/; +/plugin/; + +#include +#include "imx93-pinfunc.h" + +&{/} { + usdhc3_pwrseq: usdhc3-pwrseq { + compatible = "mmc-pwrseq-simple"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <60>; + reset-gpios = <&gpio4 7 GPIO_ACTIVE_LOW>; + }; +}; + +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + shutdown-gpios = <&gpio4 13 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>; + max-speed = <2000000>; + }; +}; + +&usdhc3 { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_wlbt>; + pinctrl-1 = <&pinctrl_usdhc3_sleep>, <&pinctrl_wlbt>; + mmc-pwrseq = <&usdhc3_pwrseq>; + bus-width = <4>; + non-removable; + no-1-8-v; + status = "okay"; + + brmcf: wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + }; +}; + +&iomuxc { + pinctrl_uart5: uart5grp { + fsl,pins = < + MX93_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + MX93_PAD_DAP_TDI__LPUART5_RX 0x31e + MX93_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + MX93_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX93_PAD_GPIO_IO22__USDHC3_CLK 0x179e + MX93_PAD_GPIO_IO23__USDHC3_CMD 0x4000139e + MX93_PAD_GPIO_IO24__USDHC3_DATA0 0x4000139e + MX93_PAD_GPIO_IO25__USDHC3_DATA1 0x4000139e + MX93_PAD_GPIO_IO26__USDHC3_DATA2 0x4000139e + MX93_PAD_GPIO_IO27__USDHC3_DATA3 0x4000139e + >; + }; + + pinctrl_usdhc3_sleep: usdhc3sleepgrp { + fsl,pins = < + MX93_PAD_GPIO_IO22__USDHC3_CLK 0x31e + MX93_PAD_GPIO_IO23__USDHC3_CMD 0x31e + MX93_PAD_GPIO_IO24__USDHC3_DATA0 0x31e + MX93_PAD_GPIO_IO25__USDHC3_DATA1 0x31e + MX93_PAD_GPIO_IO26__USDHC3_DATA2 0x31e + MX93_PAD_GPIO_IO27__USDHC3_DATA3 0x31e + >; + }; + + pinctrl_wlbt: wlbtgrp { + fsl,pins = < + MX93_PAD_ENET1_RD3__GPIO4_IO13 0x31e /* BT ENABLE */ + MX93_PAD_ENET1_TXC__GPIO4_IO07 0x31e /* WLAN ENABLE */ + MX93_PAD_I2C1_SCL__GPIO1_IO00 0x31e /* HOST WAKEUP */ + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts index 0c55b749c834..6f1374f5757f 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts @@ -18,6 +18,7 @@ /{ "fsl,imx93"; aliases { + ethernet1 = &eqos; rtc0 = &i2c_rtc; rtc1 = &bbnsm_rtc; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-phycore-rpmsg.dtso b/arch/arm64/boot/dts/freescale/imx93-phycore-rpmsg.dtso new file mode 100644 index 000000000000..23bede7833f8 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-phycore-rpmsg.dtso @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2025 PHYTEC Messtechnik GmbH + * Author: Primoz Fiser + */ + +/dts-v1/; +/plugin/; + +&{/} { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + + rsc_table: rsc-table@2021e000 { + reg = <0 0x2021e000 0 0x1000>; + no-map; + }; + + 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; + }; + + vdevbuffer: vdevbuffer@a4020000 { + compatible = "shared-dma-pool"; + reg = <0 0xa4020000 0 0x100000>; + no-map; + }; + }; +}; + +&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"; +}; + +&mu1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi b/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi index 22dbcc89e311..c6f5aa38ebf9 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi @@ -16,6 +16,10 @@ /{ model = "PHYTEC phyCORE-i.MX93"; compatible = "phytec,imx93-phycore-som", "fsl,imx93"; + aliases { + ethernet0 = &fec; + }; + reserved-memory { ranges; #address-cells = <2>; @@ -42,6 +46,19 @@ led-0 { linux,default-trigger = "heartbeat"; }; }; + + reg_vdda_1v8: regulator-vdda-1v8 { + compatible = "regulator-fixed"; + regulator-name = "VDDA_1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&buck5>; + }; +}; + +/* ADC */ +&adc1 { + vref-supply = <®_vdda_1v8>; }; /* Ethernet */ @@ -178,6 +195,9 @@ &usdhc1 { /* Watchdog */ &wdog3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wdog>; + fsl,ext-reset-output; status = "okay"; }; @@ -266,4 +286,10 @@ MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x400013be MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x179e >; }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX93_PAD_WDOG_ANY__WDOG1_WDOG_ANY 0x31e + >; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi index 2cabdae24227..82914ca148d3 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2022 TQ-Systems GmbH , + * Copyright (c) 2022-2025 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Markus Niebel */ @@ -11,6 +11,12 @@ /{ model = "TQ-Systems i.MX93 TQMa93xxLA/TQMa93xxCA SOM"; compatible = "tq,imx93-tqma9352", "fsl,imx93"; + memory@80000000 { + device_type = "memory"; + /* our minimum RAM config will be 1024 MiB */ + reg = <0x00000000 0x80000000 0 0x40000000>; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -70,15 +76,6 @@ partitions { }; }; -&gpio1 { - pmic-irq-hog { - gpio-hog; - gpios = <3 GPIO_ACTIVE_LOW>; - input; - line-name = "PMIC_IRQ#"; - }; -}; - &lpi2c1 { clock-frequency = <400000>; pinctrl-names = "default", "sleep"; @@ -110,11 +107,11 @@ buck1: BUCK1 { regulator-ramp-delay = <3125>; }; - /* V_DDRQ - 1.1 LPDDR4 or 0.6 LPDDR4X */ + /* V_DDRQ - 0.6 V for LPDDR4X */ buck2: BUCK2 { regulator-name = "BUCK2"; regulator-min-microvolt = <600000>; - regulator-max-microvolt = <1100000>; + regulator-max-microvolt = <600000>; regulator-boot-on; regulator-always-on; regulator-ramp-delay = <3125>; diff --git a/arch/arm64/boot/dts/freescale/imx93-var-som.dtsi b/arch/arm64/boot/dts/freescale/imx93-var-som.dtsi index 783938245e4f..a5f09487d803 100644 --- a/arch/arm64/boot/dts/freescale/imx93-var-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-var-som.dtsi @@ -19,26 +19,19 @@ mmc_pwrseq: mmc-pwrseq { reset-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>, /* WIFI_RESET */ <&gpio3 7 GPIO_ACTIVE_LOW>; /* WIFI_PWR_EN */ }; - - reg_eqos_phy: regulator-eqos-phy { - compatible = "regulator-fixed"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_reg_eqos_phy>; - regulator-name = "eth_phy_pwr"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>; - enable-active-high; - startup-delay-us = <100000>; - regulator-always-on; - }; }; &eqos { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_eqos>; + /* + * 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 { @@ -51,6 +44,28 @@ ethphy0: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0>; eee-broken-1000t; + reset-gpios = <&gpio1 7 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"; + }; + }; }; }; }; @@ -75,14 +90,15 @@ 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 0x5fe + 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 0x5fe + MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x58e MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e + MX93_PAD_UART2_TXD__GPIO1_IO07 0x51e >; }; diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi index 64cd0776b43d..8a7f1cd76c76 100644 --- a/arch/arm64/boot/dts/freescale/imx93.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93.dtsi @@ -297,7 +297,8 @@ edma1: dma-controller@44000000 { , // 27: TMP2 CH1/CH3 , // 28: TMP2 Overflow , // 29: PDM - ; // 30: ADC1 + , // 30: ADC1 + ; // err clocks = <&clk IMX93_CLK_EDMA1_GATE>; clock-names = "dma"; }; @@ -667,7 +668,8 @@ edma2: dma-controller@42000000 { , , , - ; + , + ; clocks = <&clk IMX93_CLK_EDMA2_GATE>; clock-names = "dma"; }; @@ -1197,6 +1199,7 @@ gpio2: gpio@43810000 { <&clk IMX93_CLK_GPIO2_GATE>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc 0 4 30>; + ngpios = <30>; }; gpio3: gpio@43820000 { @@ -1213,6 +1216,7 @@ gpio3: gpio@43820000 { clock-names = "gpio", "port"; gpio-ranges = <&iomuxc 0 84 8>, <&iomuxc 8 66 18>, <&iomuxc 26 34 2>, <&iomuxc 28 0 4>; + ngpios = <32>; }; gpio4: gpio@43830000 { @@ -1228,6 +1232,7 @@ gpio4: gpio@43830000 { <&clk IMX93_CLK_GPIO4_GATE>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc 0 38 28>, <&iomuxc 28 36 2>; + ngpios = <30>; }; gpio1: gpio@47400000 { @@ -1243,6 +1248,7 @@ gpio1: gpio@47400000 { <&clk IMX93_CLK_GPIO1_GATE>; clock-names = "gpio", "port"; gpio-ranges = <&iomuxc 0 92 16>; + ngpios = <16>; }; ocotp: efuse@47510000 { diff --git a/arch/arm64/boot/dts/freescale/imx94.dtsi b/arch/arm64/boot/dts/freescale/imx94.dtsi index 3661ea48d7d2..44dee2cbd42d 100644 --- a/arch/arm64/boot/dts/freescale/imx94.dtsi +++ b/arch/arm64/boot/dts/freescale/imx94.dtsi @@ -108,6 +108,16 @@ scmi_misc: protocol@84 { }; }; + mqs1: mqs1 { + compatible = "fsl,imx943-aonmix-mqs"; + status = "disabled"; + }; + + mqs2: mqs2 { + compatible = "fsl,imx943-wakeupmix-mqs"; + status = "disabled"; + }; + pmu { compatible = "arm,cortex-a55-pmu"; interrupts = ; @@ -785,6 +795,7 @@ gpio2: gpio@43810000 { #gpio-cells = <2>; gpio-controller; gpio-ranges = <&scmi_iomuxc 0 4 32>; + ngpios = <32>; }; gpio3: gpio@43820000 { @@ -797,6 +808,7 @@ gpio3: gpio@43820000 { #gpio-cells = <2>; gpio-controller; gpio-ranges = <&scmi_iomuxc 0 36 26>; + ngpios = <26>; }; gpio4: gpio@43840000 { @@ -810,6 +822,7 @@ gpio4: gpio@43840000 { gpio-controller; gpio-ranges = <&scmi_iomuxc 0 62 4>, <&scmi_iomuxc 4 0 4>, <&scmi_iomuxc 8 140 12>, <&scmi_iomuxc 20 164 12>; + ngpios = <32>; }; gpio5: gpio@43850000 { @@ -822,6 +835,7 @@ gpio5: gpio@43850000 { #gpio-cells = <2>; gpio-controller; gpio-ranges = <&scmi_iomuxc 0 108 32>; + ngpios = <32>; }; gpio6: gpio@43860000 { @@ -834,6 +848,7 @@ gpio6: gpio@43860000 { #gpio-cells = <2>; gpio-controller; gpio-ranges = <&scmi_iomuxc 0 66 32>; + ngpios = <32>; }; gpio7: gpio@43870000 { @@ -846,6 +861,8 @@ gpio7: gpio@43870000 { #gpio-cells = <2>; gpio-controller; gpio-ranges = <&scmi_iomuxc 0 98 10>, <&scmi_iomuxc 16 152 12>; + gpio-reserved-ranges = <10 6>; + ngpios = <28>; }; aips1: bus@44000000 { @@ -1028,6 +1045,13 @@ flexcan1: can@443a0000 { compatible = "fsl,imx94-flexcan", "fsl,imx95-flexcan"; reg = <0x443a0000 0x10000>; interrupts = ; + clocks = <&scmi_clk IMX94_CLK_BUSAON>, + <&scmi_clk IMX94_CLK_CAN1>; + clock-names = "ipg", "per"; + assigned-clocks = <&scmi_clk IMX94_CLK_CAN1>; + assigned-clock-parents = <&scmi_clk IMX94_CLK_SYSPLL1_PFD1_DIV2>; + assigned-clock-rates = <80000000>; + fsl,clk-source = /bits/ 8 <0>; status = "disabled"; }; @@ -1045,6 +1069,26 @@ sai1: sai@443b0000 { status = "disabled"; }; + micfil: micfil@44520000 { + compatible = "fsl,imx943-micfil"; + reg = <0x44520000 0x10000>; + interrupts = , + , + , + ; + clocks = <&scmi_clk IMX94_CLK_BUSAON>, + <&scmi_clk IMX94_CLK_PDM>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>, + <&scmi_clk IMX94_CLK_AUDIOPLL2>, + <&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,imx94-adc", "nxp,imx93-adc"; reg = <0x44530000 0x10000>; diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts index cc8f3e6a1789..c8c3eff9df1a 100644 --- a/arch/arm64/boot/dts/freescale/imx943-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts @@ -12,15 +12,28 @@ / { model = "NXP i.MX943 EVK board"; aliases { + i2c2 = &lpi2c3; + i2c3 = &lpi2c4; + i2c5 = &lpi2c6; mmc0 = &usdhc1; mmc1 = &usdhc2; serial0 = &lpuart1; }; + bt_sco_codec: bt-sco-codec { + compatible = "linux,bt-sco"; + #sound-dai-cells = <1>; + }; + chosen { stdout-path = &lpuart1; }; + dmic: dmic { + compatible = "dmic-codec"; + #sound-dai-cells = <0>; + }; + reg_usdhc2_vmmc: regulator-usdhc2 { compatible = "regulator-fixed"; off-on-delay-us = <12000>; @@ -33,6 +46,15 @@ reg_usdhc2_vmmc: regulator-usdhc2 { enable-active-high; }; + reg_audio_pwr: regulator-wm8962-pwr { + compatible = "regulator-fixed"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "audio-pwr"; + gpio = <&pcal6416_i2c3_u171 12 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + reserved-memory { ranges; #address-cells = <2>; @@ -47,19 +69,429 @@ linux,cma { }; }; + 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 = <&sai3>; + }; + }; + + 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-codec = <&wm8962>; + audio-cpu = <&sai1>; + audio-routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC", + "IN1R", "AMIC"; + hp-det-gpio = <&pcal6416_i2c3_u48 14 GPIO_ACTIVE_HIGH>; + model = "wm8962-audio"; + }; + memory@80000000 { reg = <0x0 0x80000000 0x0 0x80000000>; device_type = "memory"; }; }; +&lpi2c3 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-names = "default"; + status = "okay"; + + pca9670_i2c3: gpio@23 { + compatible = "nxp,pca9670"; + reg = <0x23>; + #gpio-cells = <2>; + gpio-controller; + }; + + pca9548_i2c3: i2c-mux@77 { + compatible = "nxp,pca9548"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@4 { + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + + wm8962: codec@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&scmi_clk IMX94_CLK_SAI1>; + 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>; + }; + }; + + i2c@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6416_i2c3_u46: gpio@20 { + compatible = "nxp,pcal6416"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + + sd-card-on-hog { + gpios = <13 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + }; + }; + + pcal6416_i2c3_u171: gpio@21 { + compatible = "nxp,pcal6416"; + reg = <0x21>; + #gpio-cells = <2>; + gpio-controller; + + audio-pwren-hog { + gpios = <12 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + }; + + mqs-mic-sel-hog { + gpios = <11 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-low; + }; + }; + }; + + i2c@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6416_i2c3_u48: gpio@20 { + compatible = "nxp,pcal6416"; + reg = <0x20>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio3>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + pinctrl-0 = <&pinctrl_ioexpander_int>; + pinctrl-names = "default"; + }; + }; + + i2c@7 { + reg = <7>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6408_i2c3_u172: gpio@20 { + compatible = "nxp,pcal6408"; + reg = <0x20>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio3>; + /* shared int pin with u48 */ + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + }; +}; + +&lpi2c4 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c4>; + pinctrl-names = "default"; + status = "okay"; +}; + +&lpi2c6 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c6>; + pinctrl-names = "default"; + status = "okay"; + + pca9544_i2c6: i2c-mux@77 { + compatible = "nxp,pca9544"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6416_i2c6_u50: gpio@21 { + compatible = "nxp,pcal6416"; + reg = <0x21>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6408_i2c6_u170: gpio@20 { + compatible = "nxp,pcal6408"; + reg = <0x20>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio4>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + pinctrl-0 = <&pinctrl_ioexpander_int2>; + pinctrl-names = "default"; + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pcal6416_i2c6_u44: gpio@20 { + compatible = "nxp,pcal6416"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + + /* pdm selection */ + can-pdm-sel-hog { + gpios = <12 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-low; + }; + + sai3-sel-hog { + gpios = <11 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + }; + + /* eMMC IOMUX selection */ + sd1-sel-hog { + gpios = <0 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + }; + + /* SD card IOMUX selection */ + sd2-sel-hog { + gpios = <1 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + }; + }; + }; + }; +}; + &lpuart1 { pinctrl-0 = <&pinctrl_uart1>; pinctrl-names = "default"; status = "okay"; }; +&micfil { + assigned-clocks = <&scmi_clk IMX94_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>, + <&scmi_clk IMX94_CLK_AUDIOPLL2>, + <&scmi_clk IMX94_CLK_PDM>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, + <3612672000>, <393216000>, + <361267200>, <49152000>; + pinctrl-0 = <&pinctrl_pdm>; + pinctrl-names = "default"; + status = "okay"; +}; + +&sai1 { + assigned-clocks = <&scmi_clk IMX94_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>, + <&scmi_clk IMX94_CLK_AUDIOPLL2>, + <&scmi_clk IMX94_CLK_SAI1>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, + <3612672000>, <393216000>, + <361267200>, <12288000>; + pinctrl-0 = <&pinctrl_sai1>; + pinctrl-names = "default"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&sai3 { + assigned-clocks = <&scmi_clk IMX94_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>, + <&scmi_clk IMX94_CLK_AUDIOPLL2>, + <&scmi_clk IMX94_CLK_SAI3>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX94_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, + <3612672000>, <393216000>, + <361267200>, <12288000>; + pinctrl-0 = <&pinctrl_sai3>; + pinctrl-names = "default"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + &scmi_iomuxc { + + pinctrl_ioexpander_int2: ioexpanderint2grp { + fsl,pins = < + IMX94_PAD_CCM_CLKO4__GPIO4_IO3 0x31e + >; + }; + + pinctrl_ioexpander_int: ioexpanderintgrp { + fsl,pins = < + IMX94_PAD_GPIO_IO45__GPIO3_IO13 0x31e + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + IMX94_PAD_GPIO_IO16__LPI2C3_SDA 0x40000b9e + IMX94_PAD_GPIO_IO17__LPI2C3_SCL 0x40000b9e + >; + }; + + pinctrl_lpi2c4: lpi2c4grp { + fsl,pins = < + IMX94_PAD_GPIO_IO18__LPI2C4_SDA 0x40000b9e + IMX94_PAD_GPIO_IO19__LPI2C4_SCL 0x40000b9e + >; + }; + + pinctrl_lpi2c6: lpi2c6grp { + fsl,pins = < + IMX94_PAD_GPIO_IO29__LPI2C6_SDA 0x40000b9e + IMX94_PAD_GPIO_IO28__LPI2C6_SCL 0x40000b9e + >; + }; + + pinctrl_pdm: pdmgrp { + fsl,pins = < + IMX94_PAD_PDM_CLK__PDM_CLK 0x31e + IMX94_PAD_PDM_BIT_STREAM0__PDM_BIT_STREAM0 0x31e + IMX94_PAD_PDM_BIT_STREAM1__PDM_BIT_STREAM1 0x31e + >; + }; + + pinctrl_sai1: sai1grp { + fsl,pins = < + IMX94_PAD_SAI1_TXFS__SAI1_TX_SYNC 0x31e + IMX94_PAD_SAI1_TXC__SAI1_TX_BCLK 0x31e + IMX94_PAD_SAI1_TXD0__SAI1_TX_DATA0 0x31e + IMX94_PAD_SAI1_RXD0__SAI1_RX_DATA0 0x31e + IMX94_PAD_I2C2_SDA__SAI1_MCLK 0x31e + >; + }; + + pinctrl_sai3: sai3grp { + fsl,pins = < + IMX94_PAD_GPIO_IO42__SAI3_TX_BCLK 0x31e + IMX94_PAD_GPIO_IO56__SAI3_TX_SYNC 0x31e + IMX94_PAD_GPIO_IO46__SAI3_RX_DATA0 0x31e + IMX94_PAD_GPIO_IO47__SAI3_TX_DATA0 0x31e + >; + }; + pinctrl_uart1: uart1grp { fsl,pins = < IMX94_PAD_UART1_TXD__LPUART1_TX 0x31e diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts index 6c47f4b47356..46f6e0fbf2b0 100644 --- a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts @@ -494,6 +494,14 @@ &netc_bus0 { <0x60 &its 0x66 0x1>, //ENETC1 VF1 <0x80 &its 0x64 0x1>, //ENETC2 PF <0xc0 &its 0x67 0x1>; + 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>; }; &netc_emdio { @@ -574,17 +582,17 @@ &sai3 { &scmi_iomuxc { pinctrl_emdio: emdiogrp { fsl,pins = < - IMX95_PAD_ENET2_MDC__NETCMIX_TOP_NETC_MDC 0x57e - IMX95_PAD_ENET2_MDIO__NETCMIX_TOP_NETC_MDIO 0x97e + IMX95_PAD_ENET2_MDC__NETCMIX_TOP_NETC_MDC 0x50e + IMX95_PAD_ENET2_MDIO__NETCMIX_TOP_NETC_MDIO 0x90e >; }; 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_TD3__NETCMIX_TOP_ETH0_RGMII_TD3 0x50e + IMX95_PAD_ENET1_TD2__NETCMIX_TOP_ETH0_RGMII_TD2 0x50e + IMX95_PAD_ENET1_TD1__NETCMIX_TOP_ETH0_RGMII_TD1 0x50e + IMX95_PAD_ENET1_TD0__NETCMIX_TOP_ETH0_RGMII_TD0 0x50e 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 @@ -598,10 +606,10 @@ IMX95_PAD_ENET1_RD3__NETCMIX_TOP_ETH0_RGMII_RD3 0x57e pinctrl_enetc1: enetc1grp { fsl,pins = < - 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_TD3__NETCMIX_TOP_ETH1_RGMII_TD3 0x50e + IMX95_PAD_ENET2_TD2__NETCMIX_TOP_ETH1_RGMII_TD2 0x50e + IMX95_PAD_ENET2_TD1__NETCMIX_TOP_ETH1_RGMII_TD1 0x50e + IMX95_PAD_ENET2_TD0__NETCMIX_TOP_ETH1_RGMII_TD0 0x50e IMX95_PAD_ENET2_TX_CTL__NETCMIX_TOP_ETH1_RGMII_TX_CTL 0x57e IMX95_PAD_ENET2_TXC__NETCMIX_TOP_ETH1_RGMII_TX_CLK 0x58e IMX95_PAD_ENET2_RX_CTL__NETCMIX_TOP_ETH1_RGMII_RX_CTL 0x57e @@ -1070,7 +1078,10 @@ usb3_data_hs: endpoint { &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>; status = "okay"; port { diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts index 6886ea766655..9d034275c847 100644 --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts @@ -77,6 +77,29 @@ linux_cma: linux,cma { }; }; + flexcan1_phy: can-phy0 { + compatible = "nxp,tjr1443"; + #phy-cells = <0>; + max-bitrate = <8000000>; + enable-gpios = <&i2c6_pcal6416 6 GPIO_ACTIVE_HIGH>; + standby-gpios = <&i2c6_pcal6416 5 GPIO_ACTIVE_LOW>; + }; + + flexcan2_phy: can-phy1 { + compatible = "nxp,tjr1443"; + #phy-cells = <0>; + max-bitrate = <8000000>; + enable-gpios = <&i2c4_gpio_expander_21 4 GPIO_ACTIVE_HIGH>; + standby-gpios = <&i2c4_gpio_expander_21 3 GPIO_ACTIVE_LOW>; + }; + + reg_vref_1v8: 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>; @@ -204,6 +227,11 @@ sound-wm8962 { }; }; +&adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +}; + &enetc_port0 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enetc0>; @@ -212,6 +240,20 @@ &enetc_port0 { status = "okay"; }; +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; + phys = <&flexcan1_phy>; + status = "disabled"; +}; + +&flexcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan2>; + phys = <&flexcan2_phy>; + status = "okay"; +}; + &flexspi1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_flexspi1>; @@ -231,6 +273,37 @@ flash@0 { }; }; +&lpi2c2 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpi2c2>; + status = "okay"; + + adp5585: io-expander@34 { + compatible = "adi,adp5585-00", "adi,adp5585"; + reg = <0x34>; + 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"; + + i2c3_gpio_expander_20: gpio@20 { + compatible = "nxp,pcal6408"; + #gpio-cells = <2>; + gpio-controller; + reg = <0x20>; + vcc-supply = <®_3p3v>; + }; +}; + &lpi2c4 { clock-frequency = <400000>; pinctrl-names = "default"; @@ -378,6 +451,24 @@ &lpuart1 { status = "okay"; }; +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "disabled"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +&lpspi7 { + num-cs = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi7>; + cs-gpios = <&gpio2 4 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + &micfil { #sound-dai-cells = <0>; pinctrl-names = "default"; @@ -414,10 +505,17 @@ &netc_emdio { ethphy0: ethernet-phy@1 { reg = <1>; + reset-gpios = <&i2c5_pcal6408 2 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <80000>; realtek,clkout-disable; }; }; +&netc_timer { + status = "okay"; +}; + &pcie0 { pinctrl-0 = <&pinctrl_pcie0>; pinctrl-names = "default"; @@ -484,6 +582,12 @@ &sai3 { status = "okay"; }; +&tpm3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tpm3>; + status = "okay"; +}; + &usb2 { dr_mode = "host"; disable-over-current; @@ -514,7 +618,10 @@ usb3_data_hs: endpoint { }; &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>; orientation-switch; status = "okay"; @@ -566,17 +673,17 @@ &wdog3 { &scmi_iomuxc { pinctrl_emdio: emdiogrp{ fsl,pins = < - IMX95_PAD_ENET1_MDC__NETCMIX_TOP_NETC_MDC 0x57e - IMX95_PAD_ENET1_MDIO__NETCMIX_TOP_NETC_MDIO 0x97e + IMX95_PAD_ENET1_MDC__NETCMIX_TOP_NETC_MDC 0x50e + IMX95_PAD_ENET1_MDIO__NETCMIX_TOP_NETC_MDIO 0x90e >; }; 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_TD3__NETCMIX_TOP_ETH0_RGMII_TD3 0x50e + IMX95_PAD_ENET1_TD2__NETCMIX_TOP_ETH0_RGMII_TD2 0x50e + IMX95_PAD_ENET1_TD1__NETCMIX_TOP_ETH0_RGMII_TD1 0x50e + IMX95_PAD_ENET1_TD0__NETCMIX_TOP_ETH0_RGMII_TD0 0x50e 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 @@ -588,6 +695,20 @@ IMX95_PAD_ENET1_RD3__NETCMIX_TOP_ETH0_RGMII_RD3 0x57e >; }; + 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_flexcan2: flexcan2grp { + fsl,pins = < + IMX95_PAD_GPIO_IO25__CAN2_TX 0x39e + IMX95_PAD_GPIO_IO27__CAN2_RX 0x39e + >; + }; + pinctrl_flexspi1: flexspi1grp { fsl,pins = < IMX95_PAD_XSPI1_SS0_B__FLEXSPI1_A_SS0_B 0x3fe @@ -628,6 +749,27 @@ IMX95_PAD_GPIO_IO36__GPIO5_IO_BIT16 0x31e >; }; + pinctrl_lpi2c1: lpi2c1grp { + fsl,pins = < + IMX95_PAD_I2C1_SCL__AONMIX_TOP_LPI2C1_SCL 0x40000b9e + IMX95_PAD_I2C1_SDA__AONMIX_TOP_LPI2C1_SDA 0x40000b9e + >; + }; + + pinctrl_lpi2c2: lpi2c2grp { + fsl,pins = < + IMX95_PAD_I2C2_SCL__AONMIX_TOP_LPI2C2_SCL 0x40000b9e + IMX95_PAD_I2C2_SDA__AONMIX_TOP_LPI2C2_SDA 0x40000b9e + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO00__LPI2C3_SDA 0x40000b9e + IMX95_PAD_GPIO_IO01__LPI2C3_SCL 0x40000b9e + >; + }; + pinctrl_lpi2c4: lpi2c4grp { fsl,pins = < IMX95_PAD_GPIO_IO30__LPI2C4_SDA 0x40000b9e @@ -656,6 +798,15 @@ IMX95_PAD_GPIO_IO09__LPI2C7_SCL 0x40000b9e >; }; + pinctrl_lpspi7: lpspi7grp { + fsl,pins = < + IMX95_PAD_GPIO_IO04__GPIO2_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_pcie0: pcie0grp { fsl,pins = < IMX95_PAD_GPIO_IO32__HSIOMIX_TOP_PCIE1_CLKREQ_B 0x4000031e @@ -716,6 +867,12 @@ IMX95_PAD_GPIO_IO21__SAI3_TX_DATA_BIT0 0x31e >; }; + pinctrl_tpm3: tpm3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO12__TPM3_CH2 0x51e + >; + }; + pinctrl_tpm6: tpm6grp { fsl,pins = < IMX95_PAD_GPIO_IO19__TPM6_CH2 0x51e @@ -729,6 +886,15 @@ IMX95_PAD_UART1_TXD__AONMIX_TOP_LPUART1_TX 0x31e >; }; + pinctrl_uart5: uart5grp { + fsl,pins = < + IMX95_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + IMX95_PAD_DAP_TDI__LPUART5_RX 0x31e + IMX95_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + IMX95_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + >; + }; + pinctrl_usdhc1: usdhc1grp { fsl,pins = < IMX95_PAD_SD1_CLK__USDHC1_CLK 0x158e @@ -821,12 +987,12 @@ 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_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 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx95-libra-rdk-fpsc.dts b/arch/arm64/boot/dts/freescale/imx95-libra-rdk-fpsc.dts new file mode 100644 index 000000000000..26c2df9b1b60 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-libra-rdk-fpsc.dts @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 PHYTEC Messtechnik GmbH + */ + +/dts-v1/; + +#include +#include + +#include "imx95-phycore-fpsc.dtsi" + +/ { + compatible = "phytec,imx95-libra-rdk-fpsc", + "phytec,imx95-phycore-fpsc", "fsl,imx95"; + model = "PHYTEC Libra i.MX95 RDK FPSC"; + + aliases { + can1 = &flexcan2; + can2 = &flexcan1; + ethernet0 = &enetc_port0; + serial0 = &lpuart7; + serial1 = &lpuart8; + }; + + chosen { + stdout-path = &lpuart7; + }; + + backlight_lvds0: backlight0 { + compatible = "pwm-backlight"; + pinctrl-0 = <&pinctrl_lvds0>; + power-supply = <®_vdd_12v0>; + status = "disabled"; + }; + + transceiver1: can-phy { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <8000000>; + enable-gpios = <&gpio_expander 10 GPIO_ACTIVE_LOW>; + }; + + transceiver2: can-phy { + compatible = "ti,tcan1043"; + #phy-cells = <0>; + max-bitrate = <8000000>; + enable-gpios = <&gpio_expander 9 GPIO_ACTIVE_LOW>; + }; + + panel0_lvds: panel-lvds0 { + backlight = <&backlight_lvds0>; + power-supply = <®_vdd_3v3>; + status = "disabled"; + }; + + reg_vdd_12v0: regulator-vdd-12v0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <12000000>; + regulator-min-microvolt = <12000000>; + regulator-name = "VDD_12V0"; + }; + + reg_vdd_1v8: regulator-vdd-1v8 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "VDD_1V8"; + }; + + reg_vdd_3v3: regulator-vdd-3v3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "VDD_3V3"; + }; + + reg_vdd_5v0: regulator-vdd-5v0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "VDD_5V0"; + }; +}; + +&enetc_port0 { + phy-handle = <ðphy0>; + status = "okay"; +}; + +&enetc_port2 { + managed = "in-band-status"; + phy-handle = <ðphy2>; + phy-mode = "10gbase-r"; +}; + +/* CAN FD */ +&flexcan1 { + phys = <&transceiver1>; + status = "okay"; +}; + +&flexcan2 { + phys = <&transceiver2>; + status = "okay"; +}; + +/* SPI-NOR */ +&flexspi1 { + pinctrl-0 = <&pinctrl_flexspi>; + pinctrl-names = "default"; + status = "okay"; + + spi_nor: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <166000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + vcc-supply = <®_vdd_1v8>; + }; +}; + +&gpio2 { + gpio-line-names = "", "", "", "", "", + "", "", "", "", "", + "", "", "", "", "", + "", "RGMII2_nINT", "GPIO4", "RTC_INT", "", + "LVDS1_BL_EN"; +}; + +&lpi2c1 { + temperature-sensor@4f { + compatible = "nxp,p3t1755"; + reg = <0x4f>; + vs-supply = <®_vdd_1v8>; + }; +}; + +&lpi2c3 { + status = "okay"; + + leds@62 { + compatible = "nxp,pca9533"; + reg = <0x62>; + + led-1 { + type = ; + }; + + led-2 { + type = ; + }; + + led-3 { + type = ; + }; + }; +}; + +&lpi2c4 { + status = "okay"; + + gpio_expander: gpio@20 { + compatible = "ti,tca6416"; + reg = <0x20>; + interrupt-parent = <&gpio2>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "CSI1_CTRL1", "CSI1_CTRL2", "CSI1_CTRL3", + "CSI1_CTRL4", "CSI2_CTRL1", "CSI2_CTRL2", + "CSI2_CTRL3", "CSI2_CTRL4", "CLK_EN_AV", + "nCAN2_EN", "nCAN1_EN", "PCIE1_nWAKE", + "PCIE2_nWAKE", "PCIE2_nALERT_3V3", + "UART1_BT_RS_SEL", "UART1_RS232_485_SEL"; + vcc-supply = <®_vdd_1v8>; + + uart1_bt_rs_sel: bt-rs-hog { + gpios = <14 GPIO_ACTIVE_HIGH>; + gpio-hog; + line-name = "UART1_BT_RS_SEL"; + output-low; + }; + }; +}; + +&lpi2c5 { + status = "okay"; + + eeprom@51 { + compatible = "atmel,24c02"; + reg = <0x51>; + pagesize = <16>; + vcc-supply = <®_vdd_1v8>; + }; +}; + +/* Used for M33 debug */ +&lpuart2 { + pinctrl-0 = <&pinctrl_lpuart2>; + pinctrl-names = "default"; +}; + +/* A-55 debug UART */ +&lpuart7 { + status = "okay"; +}; + +/* RS232/RS485/BT */ +&lpuart8 { + uart-has-rtscts; + status = "okay"; +}; + +&netc_emdio { /* RGMII2 */ + ethphy0: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + interrupt-parent = <&gpio2>; + interrupts = <16 IRQ_TYPE_LEVEL_LOW>; + enet-phy-lane-no-swap; + ti,clk-output-sel = ; + ti,fifo-depth = ; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + }; + + ethphy2: ethernet-phy@8 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x8>; + max-speed = <10000>; /* 10Gbit/s */ + status = "disabled"; + }; +}; + +&pcie0 { + reset-gpio = <&gpio1 10 GPIO_ACTIVE_LOW>; + vpcie-supply = <®_vdd_3v3>; + status = "okay"; +}; + +&pcie1 { + reset-gpio = <&gpio1 14 GPIO_ACTIVE_LOW>; + vpcie-supply = <®_vdd_3v3>; + status = "okay"; +}; + +&rv3028 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rtc>; + interrupt-parent = <&gpio2>; + interrupts = <18 IRQ_TYPE_LEVEL_LOW>; + aux-voltage-chargeable = <1>; + wakeup-source; + trickle-resistor-ohms = <3000>; +}; + +&scmi_iomuxc { + pinctrl_lpuart2: lpuart2grp { /* FPSC proprietary */ + fsl,pins = < + IMX95_PAD_UART2_TXD__AONMIX_TOP_LPUART2_TX 0x31e + IMX95_PAD_UART2_RXD__AONMIX_TOP_LPUART2_RX 0x31e + >; + }; + + pinctrl_lvds0: lvds0grp { + fsl,pins = < + IMX95_PAD_GPIO_IO20__GPIO2_IO_BIT20 0x31e + >; + }; + + pinctrl_rtc: rtcgrp { + fsl,pins = < + IMX95_PAD_GPIO_IO18__GPIO2_IO_BIT18 0x31e + >; + }; + + pinctrl_tpm4: tpm4grp { + fsl,pins = < + IMX95_PAD_GPIO_IO21__TPM4_CH1 0x51e + >; + }; +}; + +&tpm4 { + pinctrl-0 = <&pinctrl_tpm4>; + pinctrl-names = "default"; +}; + +&usb3 { + fsl,over-current-active-low; + fsl,power-active-low; + status = "okay"; +}; + +&usb3_dwc3 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usb3_phy { + vbus-supply = <®_vdd_5v0>; + status = "okay"; +}; + +/* uSD Card */ +&usdhc2 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-phycore-fpsc.dtsi b/arch/arm64/boot/dts/freescale/imx95-phycore-fpsc.dtsi new file mode 100644 index 000000000000..7519d5bd06ba --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-phycore-fpsc.dtsi @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 PHYTEC Messtechnik GmbH + */ + +#include +#include "imx95.dtsi" + +/ { + model = "PHYTEC phyCORE-i.MX95 FPSC"; + compatible = "phytec,imx95-phycore-fpsc", "fsl,imx95"; + + aliases { + ethernet1 = &enetc_port1; + i2c1 = &lpi2c2; + i2c2 = &lpi2c5; + i2c3 = &lpi2c3; + i2c4 = &lpi2c4; + i2c5 = &lpi2c1; + rtc0 = &rv3028; + rtc1 = &scmi_bbm; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x00000000 0x80000000 0x00000001 0x00000000>; + }; + + reg_nvcc_aon: regulator-nvcc-aon { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "VDD_IO"; + }; + + 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 = "VDDSW_SD2"; + gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reserved-memory { + ranges; + #address-cells = <2>; + #size-cells = <2>; + + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x80000000 0 0x7f000000>; + reusable; + size = <0 0x3c000000>; + linux,cma-default; + }; + }; +}; + +&enetc_port0 { /* FPSC RGMII2 */ + phy-mode = "rgmii-id"; + pinctrl-0 = <&pinctrl_enetc0>; + pinctrl-names = "default"; +}; + +&enetc_port1 { + phy-handle = <ðphy1>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&pinctrl_enetc1>; + pinctrl-names = "default"; + status = "okay"; +}; + +&flexcan1 { /* FPSC CAN1 */ + pinctrl-0 = <&pinctrl_flexcan1>; + pinctrl-names = "default"; +}; + +&flexcan2 { /* FPSC CAN2 */ + pinctrl-0 = <&pinctrl_flexcan2>; + pinctrl-names = "default"; +}; + +&flexspi1 { /* FPSC QSPI */ + pinctrl-0 = <&pinctrl_flexspi>; + pinctrl-names = "default"; +}; + +&gpio1 { /* FPSC GPIO */ + gpio-line-names = "", "", "", "", "GPIO2", + "GPIO1", "", "", "", "", + "PCIE1_nPERST", "USB1_PWR_EN", "GPIO3", "USB2_PWR_EN", "PCIE2_nPERST"; + pinctrl-0 = <&pinctrl_gpio1>; + pinctrl-names = "default"; + status = "okay"; +}; + +&gpio2 { /* FPSC GPIO */ + gpio-line-names = "", "", "", "", "", + "", "", "", "", "", + "", "", "", "", "", + "", "RGMII2_nINT", "GPIO4"; + pinctrl-0 = <&pinctrl_gpio2>; + pinctrl-names = "default"; +}; + +&gpio3 { + gpio-line-names = "", "", "", "", "", + "", "", "SD2_RESET_B"; +}; + +&gpio4 { + gpio-line-names = "ENET2_nINT"; +}; + +&gpio5 { + gpio-line-names = "", "", "", "", "", + "", "", "", "", "", + "", "", "", "USB1_OC", "USB2_OC"; +}; + +&lpi2c1 { /* FPSC I2C5 */ + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c1>; + pinctrl-names = "default"; + status = "okay"; + + dram_sense: temperature-sensor@48 { + compatible = "ti,tmp102"; + reg = <0x48>; + #thermal-sensor-cells = <1>; + }; + + emmc_sense: temperature-sensor@49 { + compatible = "ti,tmp102"; + reg = <0x49>; + #thermal-sensor-cells = <1>; + }; + + ethphy_sense: temperature-sensor@4a { + compatible = "ti,tmp102"; + reg = <0x4a>; + #thermal-sensor-cells = <1>; + }; + + pmic_sense: temperature-sensor@4b { + compatible = "ti,tmp102"; + reg = <0x4b>; + #thermal-sensor-cells = <1>; + }; + + /* User EEPROM */ + eeprom@50 { + compatible = "st,24c32", "atmel,24c32"; + reg = <0x50>; + pagesize = <32>; + vcc-supply = <®_nvcc_aon>; + }; + + /* Factory EEPROM */ + eeprom@51 { + compatible = "st,24c32", "atmel,24c32"; + reg = <0x51>; + pagesize = <32>; + vcc-supply = <®_nvcc_aon>; + }; + + rv3028: rtc@52 { + compatible = "microcrystal,rv3028"; + reg = <0x52>; + }; + + /* User EEPROM ID page */ + eeprom@58 { + compatible = "st,24c32", "atmel,24c32"; + reg = <0x58>; + pagesize = <32>; + vcc-supply = <®_nvcc_aon>; + }; +}; + +&lpi2c2 { /* FPSC I2C1 */ + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c2>; + pinctrl-names = "default"; +}; + +&lpi2c3 { /* FPSC I2C3 */ + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-names = "default"; +}; + +&lpi2c4 { /* FPSC I2C4 */ + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c4>; + pinctrl-names = "default"; +}; + +&lpi2c5 { /* FPSC I2C2 */ + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c5>; + pinctrl-names = "default"; +}; + +&lpspi3 { /* FPSC SPI2 */ + pinctrl-0 = <&pinctrl_lpspi3>; + pinctrl-names = "default"; +}; + +&lpspi4 { /* FPSC SPI3 */ + pinctrl-0 = <&pinctrl_lpspi4>; + pinctrl-names = "default"; +}; + +&lpspi7 { /* FPSC SPI1 */ + pinctrl-0 = <&pinctrl_lpspi7>; + pinctrl-names = "default"; +}; + +&lpuart5 { /* FPSC UART2 */ + pinctrl-0 = <&pinctrl_lpuart5>; + pinctrl-names = "default"; +}; + +&lpuart7 { /* FPSC UART3 */ + pinctrl-0 = <&pinctrl_lpuart7>; + pinctrl-names = "default"; +}; + +&lpuart8 { /* FPSC UART1 */ + pinctrl-0 = <&pinctrl_lpuart8>; + pinctrl-names = "default"; +}; + +&netc_blk_ctrl { + status = "okay"; +}; + +&netc_emdio { /* FPSC RGMII2 */ + pinctrl-0 = <&pinctrl_emdio>; + pinctrl-names = "default"; + status = "okay"; + + ethphy1: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x0>; + interrupt-parent = <&gpio4>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + enet-phy-lane-no-swap; + ti,clk-output-sel = ; + ti,fifo-depth = ; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + }; +}; + +&netcmix_blk_ctrl { + status = "okay"; +}; + +&pcie0 { /* FPSC PCIE1 */ + pinctrl-0 = <&pinctrl_pcie0>; + pinctrl-names = "default"; +}; + +&pcie1 { /* FPSC PCIE2 */ + pinctrl-0 = <&pinctrl_pcie1>; + pinctrl-names = "default"; +}; + +&sai5 { /* FPSC SAI1 */ + pinctrl-0 = <&pinctrl_sai5>; + pintrc-names = "default"; +}; + +&scmi_iomuxc { + pinctrl_emdio: emdiogrp { + fsl,pins = < + IMX95_PAD_ENET2_MDIO__NETCMIX_TOP_NETC_MDIO 0x97e /* RGMII2_MDIO */ + IMX95_PAD_ENET2_MDC__NETCMIX_TOP_NETC_MDC 0x502 /* RGMII2_MDC */ + >; + }; + + pinctrl_enetc0: enetc0grp { + fsl,pins = < + IMX95_PAD_GPIO_IO16__GPIO2_IO_BIT16 0x31e /* RGMII2_nINT */ + IMX95_PAD_CCM_CLKO3__NETCMIX_TOP_NETC_TMR_1588_TRIG2 0x31e /* RGMII2_EVENT_IN */ + IMX95_PAD_CCM_CLKO4__NETCMIX_TOP_NETC_TMR_1588_PP2 0x31e /* RGMII2_EVENT_OUT */ + + IMX95_PAD_ENET1_TD3__NETCMIX_TOP_ETH0_RGMII_TD3 0x57e /* RGMII2_TX_3 */ + IMX95_PAD_ENET1_TD2__NETCMIX_TOP_ETH0_RGMII_TD2 0x57e /* RGMII2_TX_2 */ + IMX95_PAD_ENET1_TD1__NETCMIX_TOP_ETH0_RGMII_TD1 0x57e /* RGMII2_TX_1 */ + IMX95_PAD_ENET1_TD0__NETCMIX_TOP_ETH0_RGMII_TD0 0x57e /* RGMII2_TX_0 */ + IMX95_PAD_ENET1_TX_CTL__NETCMIX_TOP_ETH0_RGMII_TX_CTL 0x57e /* RGMII2_TX_CTL */ + IMX95_PAD_ENET1_TXC__NETCMIX_TOP_ETH0_RGMII_TX_CLK 0x58e /* RGMII2_TXC */ + IMX95_PAD_ENET1_RD3__NETCMIX_TOP_ETH0_RGMII_RD3 0x57e /* RGMII2_RX_3 */ + IMX95_PAD_ENET1_RD2__NETCMIX_TOP_ETH0_RGMII_RD2 0x57e /* RGMII2_RX_2 */ + IMX95_PAD_ENET1_RD1__NETCMIX_TOP_ETH0_RGMII_RD1 0x57e /* RGMII2_RX_1 */ + IMX95_PAD_ENET1_RD0__NETCMIX_TOP_ETH0_RGMII_RD0 0x57e /* RGMII2_RX_0 */ + IMX95_PAD_ENET1_RX_CTL__NETCMIX_TOP_ETH0_RGMII_RX_CTL 0x57e /* RGMII2_RX_CTL */ + IMX95_PAD_ENET1_RXC__NETCMIX_TOP_ETH0_RGMII_RX_CLK 0x58e /* RGMII2_RXC */ + >; + }; + + pinctrl_enetc1: enetc1grp { + fsl,pins = < + IMX95_PAD_ENET1_MDC__GPIO4_IO_BIT0 0x31e + IMX95_PAD_ENET2_TD0__NETCMIX_TOP_ETH1_RGMII_TD0 0x57e + IMX95_PAD_ENET2_TD1__NETCMIX_TOP_ETH1_RGMII_TD1 0x57e + IMX95_PAD_ENET2_TD2__NETCMIX_TOP_ETH1_RGMII_TD2 0x57e + IMX95_PAD_ENET2_TD3__NETCMIX_TOP_ETH1_RGMII_TD3 0x57e + IMX95_PAD_ENET2_TX_CTL__NETCMIX_TOP_ETH1_RGMII_TX_CTL 0x57e + IMX95_PAD_ENET2_TXC__NETCMIX_TOP_ETH1_RGMII_TX_CLK 0x58e + 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 0x57e + IMX95_PAD_ENET2_RX_CTL__NETCMIX_TOP_ETH1_RGMII_RX_CTL 0x57e + IMX95_PAD_ENET2_RXC__NETCMIX_TOP_ETH1_RGMII_RX_CLK 0x58e + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + IMX95_PAD_PDM_CLK__AONMIX_TOP_CAN1_TX 0x51e /* CAN1_TX */ + IMX95_PAD_PDM_BIT_STREAM0__AONMIX_TOP_CAN1_RX 0x51e /* CAN1_RX */ + >; + }; + + pinctrl_flexcan2: flexcan2grp { + fsl,pins = < + IMX95_PAD_GPIO_IO25__CAN2_TX 0x51e /* CAN2_TX */ + IMX95_PAD_GPIO_IO27__CAN2_RX 0x51e /* CAN2_RX */ + >; + }; + + pinctrl_flexspi: flexspigrp { + fsl,pins = < + IMX95_PAD_XSPI1_SS0_B__FLEXSPI1_A_SS0_B 0x3fe /* QSPI_CE */ + IMX95_PAD_XSPI1_SCLK__FLEXSPI1_A_SCLK 0x3fe /* QSPI_CLK */ + IMX95_PAD_XSPI1_DATA0__FLEXSPI1_A_DATA_BIT0 0x3fe /* QSPI_DATA_0 */ + IMX95_PAD_XSPI1_DATA1__FLEXSPI1_A_DATA_BIT1 0x3fe /* QSPI_DATA_1 */ + IMX95_PAD_XSPI1_DATA2__FLEXSPI1_A_DATA_BIT2 0x3fe /* QSPI_DATA_2 */ + IMX95_PAD_XSPI1_DATA3__FLEXSPI1_A_DATA_BIT3 0x3fe /* QSPI_DATA_3 */ + IMX95_PAD_XSPI1_DQS__FLEXSPI1_A_DQS 0x3fe /* QSPI_DQS */ + >; + }; + + pinctrl_gpio1: gpio1grp { + fsl,pins = < + IMX95_PAD_UART1_TXD__AONMIX_TOP_GPIO1_IO_BIT5 0x31e /* GPIO1 */ + IMX95_PAD_UART1_RXD__AONMIX_TOP_GPIO1_IO_BIT4 0x31e /* GPIO2 */ + IMX95_PAD_SAI1_TXC__AONMIX_TOP_GPIO1_IO_BIT12 0x31e /* GPIO3 */ + >; + }; + + pinctrl_gpio2: gpio2grp { + fsl,pins = < + IMX95_PAD_GPIO_IO17__GPIO2_IO_BIT17 0x31e /* GPIO4 */ + >; + }; + + pinctrl_lpi2c1: lpi2c1grp { + fsl,pins = < + IMX95_PAD_I2C1_SCL__AONMIX_TOP_LPI2C1_SCL 0x40000b9e /* I2C5_SCL */ + IMX95_PAD_I2C1_SDA__AONMIX_TOP_LPI2C1_SDA 0x40000b9e /* I2C5_SDA */ + >; + }; + + pinctrl_lpi2c2: lpi2c2grp { + fsl,pins = < + IMX95_PAD_I2C2_SDA__AONMIX_TOP_LPI2C2_SDA 0x40000b9e /* I2C1_SDA_DNU */ + IMX95_PAD_I2C2_SCL__AONMIX_TOP_LPI2C2_SCL 0x40000b9e /* I2C1_SCL_DNU */ + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e /* I2C3_SDA */ + IMX95_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e /* I2C3_SCL */ + >; + }; + + pinctrl_lpi2c4: lpi2c4grp { + fsl,pins = < + IMX95_PAD_GPIO_IO30__LPI2C4_SDA 0x40000b9e /* I2C4_SDA */ + IMX95_PAD_GPIO_IO31__LPI2C4_SCL 0x40000b9e /* I2C4_SDL */ + >; + }; + + pinctrl_lpi2c5: lpi2c5grp { + fsl,pins = < + IMX95_PAD_GPIO_IO22__LPI2C5_SDA 0x40000b9e /* I2C2_SDA */ + IMX95_PAD_GPIO_IO23__LPI2C5_SCL 0x40000b9e /* I2C2_SCL */ + >; + }; + + pinctrl_lpspi3: lpspi3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO11__LPSPI3_SCK 0x51e /* SPI2_SCLK */ + IMX95_PAD_GPIO_IO10__LPSPI3_SOUT 0x51e /* SPI2_MOSI */ + IMX95_PAD_GPIO_IO09__LPSPI3_SIN 0x51e /* SPI2_MISO */ + IMX95_PAD_GPIO_IO08__LPSPI3_PCS0 0x51e /* SPI2_CS */ + >; + }; + + pinctrl_lpspi4: lpspi4grp { + fsl,pins = < + IMX95_PAD_GPIO_IO21__LPSPI4_SCK 0x51e /* SPI3_SCLK */ + IMX95_PAD_GPIO_IO20__LPSPI4_SOUT 0x51e /* SPI3_MOSI */ + IMX95_PAD_GPIO_IO19__LPSPI4_SIN 0x51e /* SPI3_MISO */ + IMX95_PAD_GPIO_IO18__LPSPI4_PCS0 0x51e /* SPI3_CS */ + >; + }; + + pinctrl_lpspi7: lpspi7grp { + fsl,pins = < + IMX95_PAD_GPIO_IO07__LPSPI7_SCK 0x51e /* SPI1_SCLK */ + IMX95_PAD_GPIO_IO06__LPSPI7_SOUT 0x51e /* SPI1_MOSI */ + IMX95_PAD_GPIO_IO05__LPSPI7_SIN 0x51e /* SPI1_MISO */ + IMX95_PAD_GPIO_IO04__LPSPI7_PCS0 0x51e /* SPI1_CS */ + >; + }; + + pinctrl_lpuart5: lpuart5grp { + fsl,pins = < + IMX95_PAD_GPIO_IO01__LPUART5_RX 0x51e /* UART2_RXD */ + IMX95_PAD_GPIO_IO00__LPUART5_TX 0x51e /* UART2_TXD */ + IMX95_PAD_GPIO_IO03__LPUART5_RTS_B 0x51e /* UART2_RTS */ + IMX95_PAD_GPIO_IO02__LPUART5_CTS_B 0x51e /* UART2_CTS */ + >; + }; + + pinctrl_lpuart7: lpuart7grp { + fsl,pins = < + IMX95_PAD_GPIO_IO37__LPUART7_RX 0x31e /* UART3_RXD */ + IMX95_PAD_GPIO_IO36__LPUART7_TX 0x31e /* UART3_TXD */ + >; + }; + + pinctrl_lpuart8: lpuart8grp { + fsl,pins = < + IMX95_PAD_GPIO_IO13__LPUART8_RX 0x51e /* UART1_RXD */ + IMX95_PAD_GPIO_IO12__LPUART8_TX 0x51e /* UART1_TXD */ + IMX95_PAD_GPIO_IO15__LPUART8_RTS_B 0x51e /* UART1_RTS */ + IMX95_PAD_GPIO_IO14__LPUART8_CTS_B 0x51e /* UART1_CTS */ + >; + }; + + pinctrl_pcie0: pcie0grp { + fsl,pins = < + IMX95_PAD_GPIO_IO32__HSIOMIX_TOP_PCIE1_CLKREQ_B 0x31e /* PCIE1_nCLKREQ */ + IMX95_PAD_PDM_BIT_STREAM1__AONMIX_TOP_GPIO1_IO_BIT10 0x31e /* PCIE1_nPERST */ + >; + }; + + pinctrl_pcie1: pcie1grp { + fsl,pins = < + IMX95_PAD_GPIO_IO35__HSIOMIX_TOP_PCIE2_CLKREQ_B 0x31e /* PCIE2_nCLKREQ */ + IMX95_PAD_SAI1_RXD0__AONMIX_TOP_GPIO1_IO_BIT14 0x31e /* PCIE2_nPERST */ + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + IMX95_PAD_SD2_RESET_B__GPIO3_IO_BIT7 0x31e + >; + }; + + pinctrl_sai5: sai5grp { + fsl,pins = < + IMX95_PAD_XSPI1_DQS__SAI5_RX_SYNC 0x51e /* SAI1_RX_SYNC */ + IMX95_PAD_XSPI1_SS1_B__SAI5_RX_BCLK 0x51e /* SAI1_RX_BCLK */ + IMX95_PAD_XSPI1_DATA7__SAI5_RX_DATA_BIT0 0x51e /* SAI1_RX_DATA */ + IMX95_PAD_XSPI1_DATA5__SAI5_TX_SYNC 0x51e /* SAI1_TX_SYNC */ + IMX95_PAD_XSPI1_DATA6__SAI5_TX_BCLK 0x51e /* SAI1_TX_BCLK */ + IMX95_PAD_XSPI1_DATA4__SAI5_TX_DATA_BIT0 0x51e /* SAI1_TX_DATA */ + >; + }; + + pinctrl_tpm3: tpm3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO24__TPM3_CH3 0x51e /* PWM1 */ + >; + }; + + pinctrl_tpm5: tpm5grp { + fsl,pins = < + IMX95_PAD_GPIO_IO26__TPM5_CH3 0x51e /* PWM2 */ + >; + }; + + pinctrl_usbc: usbcgrp { + fsl,pins = < + IMX95_PAD_SAI1_TXFS__AONMIX_TOP_GPIO1_IO_BIT11 0x51e /* USB1_PWR_EN */ + IMX95_PAD_GPIO_IO33__GPIO5_IO_BIT13 0x51e /* USB1_OC */ + >; + }; + + pinctrl_usb2: usb2grp { + fsl,pins = < + IMX95_PAD_SAI1_TXD0__AONMIX_TOP_GPIO1_IO_BIT13 0x51e /* USB2_PWR_EN */ + IMX95_PAD_GPIO_IO34__GPIO5_IO_BIT14 0x51e /* USB2_OC */ + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + 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_CMD__USDHC1_CMD 0x138e + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x158e + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + 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_CMD__USDHC1_CMD 0x138e + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x158e + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + 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_CMD__USDHC1_CMD 0x13fe + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x15fe + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + IMX95_PAD_SD2_CD_B__USDHC2_CD_B 0x31e /* CD */ + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x158e /* CLK */ + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x138e /* CMD */ + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x138e /* DATA0 */ + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x138e /* DATA1 */ + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x138e /* DATA2 */ + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x138e /* DATA3 */ + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + IMX95_PAD_SD2_CD_B__USDHC2_CD_B 0x31e /* CD */ + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x158e /* CLK */ + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x138e /* CMD */ + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x138e /* DATA0 */ + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x138e /* DATA1 */ + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x138e /* DATA2 */ + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x138e /* DATA3 */ + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + IMX95_PAD_SD2_CD_B__USDHC2_CD_B 0x31e /* CD */ + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x15fe /* CLK */ + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x13fe /* CMD */ + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x13fe /* DATA0 */ + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x13fe /* DATA1 */ + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x13fe /* DATA2 */ + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x13fe /* DATA3 */ + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + IMX95_PAD_SD3_CLK__USDHC3_CLK 0x158e /* SDIO_CLK */ + IMX95_PAD_SD3_CMD__USDHC3_CMD 0x138e /* SDIO_CMD */ + IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x138e /* SDIO_DATA0 */ + IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x138e /* SDIO_DATA1 */ + IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x138e /* SDIO_DATA2 */ + IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x138e /* SDIO_DATA3 */ + >; + }; +}; + +&tpm3 { /* FPSC PWM1 */ + pinctrl-0 = <&pinctrl_tpm3>; + pinctrl-names = "default"; +}; + +&tpm5 { /* FPSC PWM2 */ + pinctrl-0 = <&pinctrl_tpm5>; + pinctrl-names = "default"; +}; + +&usb3 { /* FPSC USB1 */ + pinctrl-0 = <&pinctrl_usbc>; + pinctrl-names = "default"; +}; + +&usdhc1 { + bus-width = <8>; + non-removable; + no-sd; + no-sdio; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + pinctrl-3 = <&pinctrl_usdhc1>; + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + status = "okay"; +}; + +&usdhc2 { /* FPSC SDCARD */ + bus-width = <4>; + disable-wp; + pinctrl-0 = <&pinctrl_usdhc2>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>; + pinctrl-3 = <&pinctrl_usdhc2>; + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + sd-uhs-sdr104; + vmmc-supply = <®_usdhc2_vmmc>; +}; + +&usdhc3 { /* FPSC SDIO */ + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-names = "default"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi index 632631a29112..8296888bce59 100644 --- a/arch/arm64/boot/dts/freescale/imx95.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi @@ -3,6 +3,7 @@ * Copyright 2024 NXP */ +#include #include #include #include @@ -688,15 +689,14 @@ tpm6: pwm@42510000 { }; i3c2: i3c@42520000 { - compatible = "silvaco,i3c-master-v1"; + compatible = "nxp,imx95-i3c", "silvaco,i3c-master-v1"; reg = <0x42520000 0x10000>; interrupts = ; #address-cells = <3>; #size-cells = <0>; clocks = <&scmi_clk IMX95_CLK_BUSAON>, - <&scmi_clk IMX95_CLK_I3C2>, <&scmi_clk IMX95_CLK_I3C2SLOW>; - clock-names = "pclk", "fast_clk", "slow_clk"; + clock-names = "pclk", "fast_clk"; status = "disabled"; }; @@ -1152,6 +1152,7 @@ gpio2: gpio@43810000 { <&scmi_clk IMX95_CLK_BUSWAKEUP>; clock-names = "gpio", "port"; gpio-ranges = <&scmi_iomuxc 0 4 32>; + ngpios = <32>; }; gpio3: gpio@43820000 { @@ -1168,6 +1169,7 @@ gpio3: gpio@43820000 { clock-names = "gpio", "port"; gpio-ranges = <&scmi_iomuxc 0 104 8>, <&scmi_iomuxc 8 74 18>, <&scmi_iomuxc 26 42 2>, <&scmi_iomuxc 28 0 4>; + ngpios = <32>; }; gpio4: gpio@43840000 { @@ -1183,6 +1185,7 @@ gpio4: gpio@43840000 { <&scmi_clk IMX95_CLK_BUSWAKEUP>; clock-names = "gpio", "port"; gpio-ranges = <&scmi_iomuxc 0 46 28>, <&scmi_iomuxc 28 44 2>; + ngpios = <30>; }; gpio5: gpio@43850000 { @@ -1198,6 +1201,7 @@ gpio5: gpio@43850000 { <&scmi_clk IMX95_CLK_BUSWAKEUP>; clock-names = "gpio", "port"; gpio-ranges = <&scmi_iomuxc 0 92 12>, <&scmi_iomuxc 12 36 6>; + ngpios = <18>; }; aips1: bus@44000000 { @@ -1273,15 +1277,14 @@ tpm2: pwm@44320000 { }; i3c1: i3c@44330000 { - compatible = "silvaco,i3c-master-v1"; + compatible = "nxp,imx95-i3c", "silvaco,i3c-master-v1"; reg = <0x44330000 0x10000>; interrupts = ; #address-cells = <3>; #size-cells = <0>; clocks = <&scmi_clk IMX95_CLK_BUSAON>, - <&scmi_clk IMX95_CLK_I3C1>, <&scmi_clk IMX95_CLK_I3C1SLOW>; - clock-names = "pclk", "fast_clk", "slow_clk"; + clock-names = "pclk", "fast_clk"; status = "disabled"; }; @@ -1508,6 +1511,7 @@ gpio1: gpio@47400000 { <&scmi_clk IMX95_CLK_M33>; clock-names = "gpio", "port"; gpio-ranges = <&scmi_iomuxc 0 112 16>; + ngpios = <16>; status = "disabled"; }; @@ -1708,7 +1712,7 @@ pcie0_ep: pcie-ep@4c300000 { <0x9 0 1 0>; reg-names = "dbi","atu", "dbi2", "app", "dma", "addr_space"; num-lanes = <1>; - interrupts = ; + interrupts = ; interrupt-names = "dma"; clocks = <&scmi_clk IMX95_CLK_HSIO>, <&scmi_clk IMX95_CLK_HSIOPLL>, @@ -1801,6 +1805,49 @@ pcie1_ep: pcie-ep@4c380000 { status = "disabled"; }; + vpu_blk_ctrl: clock-controller@4c410000 { + compatible = "nxp,imx95-vpu-csr", "syscon"; + reg = <0x0 0x4c410000 0x0 0x10000>; + #clock-cells = <1>; + clocks = <&scmi_clk IMX95_CLK_VPUAPB>; + power-domains = <&scmi_devpd IMX95_PD_VPU>; + assigned-clocks = <&scmi_clk IMX95_CLK_VPUAPB>, + <&scmi_clk IMX95_CLK_VPU>, + <&scmi_clk IMX95_CLK_VPUJPEG>; + assigned-clock-parents = <&scmi_clk IMX95_CLK_SYSPLL1_PFD1_DIV2>, + <&scmi_clk IMX95_CLK_SYSPLL1_PFD2>, + <&scmi_clk IMX95_CLK_SYSPLL1_PFD0>; + assigned-clock-rates = <133333333>, <667000000>, <500000000>; + }; + + jpegdec: jpegdec@4c500000 { + compatible = "nxp,imx95-jpgdec", "nxp,imx8qxp-jpgdec"; + reg = <0x0 0x4C500000 0x0 0x00050000>; + interrupts = , + , + , + ; + clocks = <&scmi_clk IMX95_CLK_VPU>, + <&vpu_blk_ctrl IMX95_CLK_VPUBLK_JPEG_DEC>; + assigned-clocks = <&vpu_blk_ctrl IMX95_CLK_VPUBLK_JPEG_DEC>; + assigned-clock-parents = <&scmi_clk IMX95_CLK_VPUJPEG>; + power-domains = <&scmi_devpd IMX95_PD_VPU>; + }; + + jpegenc: jpegenc@4c550000 { + compatible = "nxp,imx95-jpgenc", "nxp,imx8qxp-jpgenc"; + reg = <0x0 0x4C550000 0x0 0x00050000>; + interrupts = , + , + , + ; + clocks = <&scmi_clk IMX95_CLK_VPU>, + <&vpu_blk_ctrl IMX95_CLK_VPUBLK_JPEG_ENC>; + assigned-clocks = <&vpu_blk_ctrl IMX95_CLK_VPUBLK_JPEG_ENC>; + assigned-clock-parents = <&scmi_clk IMX95_CLK_VPUJPEG>; + power-domains = <&scmi_devpd IMX95_PD_VPU>; + }; + netcmix_blk_ctrl: syscon@4c810000 { compatible = "nxp,imx95-netcmix-blk-ctrl", "syscon"; reg = <0x0 0x4c810000 0x0 0x8>; @@ -1861,6 +1908,14 @@ netc_bus0: pcie@4ca00000 { <0x90 &its 0x65 0x1>, //ENETC2 VF0 <0xa0 &its 0x66 0x1>, //ENETC2 VF1 <0xc0 &its 0x67 0x1>; //NETC Timer + iommu-map = <0x0 &smmu 0x20 0x1>, + <0x10 &smmu 0x21 0x1>, + <0x20 &smmu 0x22 0x1>, + <0x40 &smmu 0x23 0x1>, + <0x80 &smmu 0x24 0x1>, + <0x90 &smmu 0x25 0x1>, + <0xa0 &smmu 0x26 0x1>, + <0xc0 &smmu 0x27 0x1>; /* ENETC0~2 and Timer BAR0 - non-prefetchable memory */ ranges = <0x82000000 0x0 0x4cc00000 0x0 0x4cc00000 0x0 0xe0000 /* Timer BAR2 - prefetchable memory */ diff --git a/arch/arm64/boot/dts/freescale/mba8mx.dtsi b/arch/arm64/boot/dts/freescale/mba8mx.dtsi index 7ee1228a50f4..79daba930ad6 100644 --- a/arch/arm64/boot/dts/freescale/mba8mx.dtsi +++ b/arch/arm64/boot/dts/freescale/mba8mx.dtsi @@ -136,7 +136,7 @@ reg_vcc_3v3: regulator-3v3 { regulator-max-microvolt = <3300000>; }; - sound { + sound: sound { compatible = "fsl,imx-audio-tlv320aic32x4"; model = "tqm-tlv320aic32"; ssi-controller = <&sai3>; diff --git a/arch/arm64/boot/dts/freescale/s32g2.dtsi b/arch/arm64/boot/dts/freescale/s32g2.dtsi index ea1456d361a3..09d2fbbe1d8c 100644 --- a/arch/arm64/boot/dts/freescale/s32g2.dtsi +++ b/arch/arm64/boot/dts/freescale/s32g2.dtsi @@ -114,6 +114,14 @@ soc@0 { #size-cells = <1>; ranges = <0 0 0 0x80000000>; + rtc0: rtc@40060000 { + compatible = "nxp,s32g2-rtc"; + reg = <0x40060000 0x1000>; + interrupts = ; + clocks = <&clks 54>, <&clks 55>; + clock-names = "ipg", "source0"; + }; + pinctrl: pinctrl@4009c240 { compatible = "nxp,s32g2-siul2-pinctrl"; /* MSCR0-MSCR101 registers on siul2_0 */ @@ -376,6 +384,68 @@ uart1: serial@401cc000 { status = "disabled"; }; + usbmisc: usbmisc@44064200 { + #index-cells = <1>; + compatible = "nxp,s32g2-usbmisc"; + reg = <0x44064200 0x200>; + }; + + usbotg: usb@44064000 { + compatible = "nxp,s32g2-usb"; + reg = <0x44064000 0x200>; + interrupt-parent = <&gic>; + interrupts = , /* OTG Core */ + ; /* OTG Wakeup */ + clocks = <&clks 94>, <&clks 95>; + fsl,usbmisc = <&usbmisc 0>; + ahb-burst-config = <0x3>; + tx-burst-size-dword = <0x10>; + rx-burst-size-dword = <0x10>; + phy_type = "ulpi"; + dr_mode = "host"; + maximum-speed = "high-speed"; + status = "disabled"; + }; + + spi0: spi@401d4000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x401d4000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <8>; + bus-num = <0>; + dmas = <&edma0 0 7>, <&edma0 0 8>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi1: spi@401d8000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x401d8000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <1>; + dmas = <&edma0 0 10>, <&edma0 0 11>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi2: spi@401dc000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x401dc000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <2>; + dmas = <&edma0 0 13>, <&edma0 0 14>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c0: i2c@401e4000 { compatible = "nxp,s32g2-i2c"; reg = <0x401e4000 0x1000>; @@ -460,6 +530,45 @@ uart2: serial@402bc000 { status = "disabled"; }; + spi3: spi@402c8000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x402c8000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <3>; + dmas = <&edma0 1 7>, <&edma0 1 8>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi4: spi@402cc000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x402cc000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <4>; + dmas = <&edma0 1 10>, <&edma0 1 11>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi5: spi@402d0000 { + compatible = "nxp,s32g2-dspi"; + reg = <0x402d0000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <5>; + dmas = <&edma0 1 13>, <&edma0 1 14>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c3: i2c@402d8000 { compatible = "nxp,s32g2-i2c"; reg = <0x402d8000 0x1000>; diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi index 991dbfbfa203..39effbe8217c 100644 --- a/arch/arm64/boot/dts/freescale/s32g3.dtsi +++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi @@ -171,6 +171,15 @@ soc@0 { #size-cells = <1>; ranges = <0 0 0 0x80000000>; + rtc0: rtc@40060000 { + compatible = "nxp,s32g3-rtc", + "nxp,s32g2-rtc"; + reg = <0x40060000 0x1000>; + interrupts = ; + clocks = <&clks 54>, <&clks 55>; + clock-names = "ipg", "source0"; + }; + pinctrl: pinctrl@4009c240 { compatible = "nxp,s32g2-siul2-pinctrl"; /* MSCR0-MSCR101 registers on siul2_0 */ @@ -435,6 +444,68 @@ uart1: serial@401cc000 { status = "disabled"; }; + usbmisc: usbmisc@44064200 { + #index-cells = <1>; + compatible = "nxp,s32g3-usbmisc"; + reg = <0x44064200 0x200>; + }; + + usbotg: usb@44064000 { + compatible = "nxp,s32g3-usb", "nxp,s32g2-usb"; + reg = <0x44064000 0x200>; + interrupt-parent = <&gic>; + interrupts = , /* OTG Core */ + ; /* OTG Wakeup */ + clocks = <&clks 94>, <&clks 95>; + fsl,usbmisc = <&usbmisc 0>; + ahb-burst-config = <0x3>; + tx-burst-size-dword = <0x10>; + rx-burst-size-dword = <0x10>; + phy_type = "ulpi"; + dr_mode = "host"; + maximum-speed = "high-speed"; + status = "disabled"; + }; + + spi0: spi@401d4000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401d4000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <8>; + bus-num = <0>; + dmas = <&edma0 0 7>, <&edma0 0 8>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi1: spi@401d8000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401d8000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <1>; + dmas = <&edma0 0 10>, <&edma0 0 11>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi2: spi@401dc000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401dc000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <2>; + dmas = <&edma0 0 13>, <&edma0 0 14>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c0: i2c@401e4000 { compatible = "nxp,s32g3-i2c", "nxp,s32g2-i2c"; @@ -524,6 +595,45 @@ uart2: serial@402bc000 { status = "disabled"; }; + spi3: spi@402c8000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x402c8000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <3>; + dmas = <&edma0 1 7>, <&edma0 1 8>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi4: spi@402cc000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x402cc000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <4>; + dmas = <&edma0 1 10>, <&edma0 1 11>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi5: spi@402d0000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x402d0000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <5>; + bus-num = <5>; + dmas = <&edma0 1 13>, <&edma0 1 14>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c3: i2c@402d8000 { compatible = "nxp,s32g3-i2c", "nxp,s32g2-i2c"; diff --git a/arch/arm64/boot/dts/freescale/s32gxxxa-evb.dtsi b/arch/arm64/boot/dts/freescale/s32gxxxa-evb.dtsi index d26af0fb8be7..f1969cdcef19 100644 --- a/arch/arm64/boot/dts/freescale/s32gxxxa-evb.dtsi +++ b/arch/arm64/boot/dts/freescale/s32gxxxa-evb.dtsi @@ -173,6 +173,78 @@ i2c4-gpio-grp1 { pinmux = <0x2d40>, <0x2d30>; }; }; + + dspi1_pins: dspi1-pins { + dspi1-grp0 { + pinmux = <0x72>; + output-enable; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi1-grp1 { + pinmux = <0x62>; + output-enable; + slew-rate = <150>; + }; + + dspi1-grp2 { + pinmux = <0x83>; + output-enable; + input-enable; + slew-rate = <150>; + }; + + dspi1-grp3 { + pinmux = <0x5F0>; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi1-grp4 { + pinmux = <0x3D92>, + <0x3DA2>, + <0x3DB2>; + }; + }; + + dspi5_pins: dspi5-pins { + dspi5-grp0 { + pinmux = <0x93>; + output-enable; + input-enable; + slew-rate = <150>; + }; + + dspi5-grp1 { + pinmux = <0xA0>; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi5-grp2 { + pinmux = <0x3ED2>, + <0x3EE2>, + <0x3EF2>; + }; + + dspi5-grp3 { + pinmux = <0xB3>; + output-enable; + slew-rate = <150>; + }; + + dspi5-grp4 { + pinmux = <0xC3>; + output-enable; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + }; }; &can0 { @@ -220,3 +292,15 @@ &i2c4 { pinctrl-1 = <&i2c4_gpio_pins>; status = "okay"; }; + +&spi1 { + pinctrl-0 = <&dspi1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&spi5 { + pinctrl-0 = <&dspi5_pins>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/s32gxxxa-rdb.dtsi b/arch/arm64/boot/dts/freescale/s32gxxxa-rdb.dtsi index 4587e1cb8835..3bc3335c9248 100644 --- a/arch/arm64/boot/dts/freescale/s32gxxxa-rdb.dtsi +++ b/arch/arm64/boot/dts/freescale/s32gxxxa-rdb.dtsi @@ -127,6 +127,78 @@ i2c4-gpio-grp1 { pinmux = <0x2d40>, <0x2d30>; }; }; + + dspi1_pins: dspi1-pins { + dspi1-grp0 { + pinmux = <0x72>; + output-enable; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi1-grp1 { + pinmux = <0x62>; + output-enable; + slew-rate = <150>; + }; + + dspi1-grp2 { + pinmux = <0x83>; + output-enable; + input-enable; + slew-rate = <150>; + }; + + dspi1-grp3 { + pinmux = <0x5F0>; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi1-grp4 { + pinmux = <0x3D92>, + <0x3DA2>, + <0x3DB2>; + }; + }; + + dspi5_pins: dspi5-pins { + dspi5-grp0 { + pinmux = <0x93>; + output-enable; + input-enable; + slew-rate = <150>; + }; + + dspi5-grp1 { + pinmux = <0xA0>; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + + dspi5-grp2 { + pinmux = <0x3ED2>, + <0x3EE2>, + <0x3EF2>; + }; + + dspi5-grp3 { + pinmux = <0xB3>; + output-enable; + slew-rate = <150>; + }; + + dspi5-grp4 { + pinmux = <0xC3>; + output-enable; + input-enable; + slew-rate = <150>; + bias-pull-up; + }; + }; }; &can0 { @@ -160,6 +232,18 @@ pca85073a: rtc@51 { }; }; +&spi1 { + pinctrl-0 = <&dspi1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&spi5 { + pinctrl-0 = <&dspi5_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + &i2c2 { pinctrl-names = "default", "gpio"; pinctrl-0 = <&i2c2_pins>; diff --git a/arch/arm64/boot/dts/freescale/tqmls1088a-mbls10xxa-mc.dtsi b/arch/arm64/boot/dts/freescale/tqmls1088a-mbls10xxa-mc.dtsi index 2471bb109e8e..9d44f488c083 100644 --- a/arch/arm64/boot/dts/freescale/tqmls1088a-mbls10xxa-mc.dtsi +++ b/arch/arm64/boot/dts/freescale/tqmls1088a-mbls10xxa-mc.dtsi @@ -10,23 +10,7 @@ #include / { - sfp1: sfp1 { - compatible = "sff,sfp"; - i2c-bus = <&sfp1_i2c>; - mod-def0-gpios = <&gpioexp2 2 GPIO_ACTIVE_LOW>; - los-gpios = <&gpioexp2 3 GPIO_ACTIVE_HIGH>; - tx-fault-gpios = <&gpioexp2 0 GPIO_ACTIVE_HIGH>; - tx-disable-gpios = <&gpioexp2 1 GPIO_ACTIVE_HIGH>; - }; - sfp2: sfp2 { - compatible = "sff,sfp"; - i2c-bus = <&sfp2_i2c>; - mod-def0-gpios = <&gpioexp2 10 GPIO_ACTIVE_LOW>; - los-gpios = <&gpioexp2 11 GPIO_ACTIVE_HIGH>; - tx-fault-gpios = <&gpioexp2 8 GPIO_ACTIVE_HIGH>; - tx-disable-gpios = <&gpioexp2 9 GPIO_ACTIVE_HIGH>; - }; }; &dpmac1 { diff --git a/arch/arm64/boot/dts/freescale/tqmls10xxa-mbls10xxa.dtsi b/arch/arm64/boot/dts/freescale/tqmls10xxa-mbls10xxa.dtsi index 65b4ed28a3d4..444bbf511596 100644 --- a/arch/arm64/boot/dts/freescale/tqmls10xxa-mbls10xxa.dtsi +++ b/arch/arm64/boot/dts/freescale/tqmls10xxa-mbls10xxa.dtsi @@ -47,6 +47,26 @@ reg_3v3: regulator-3v3 { regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; + + sfp1: sfp1 { + compatible = "sff,sfp"; + i2c-bus = <&sfp1_i2c>; + mod-def0-gpios = <&gpioexp2 2 GPIO_ACTIVE_LOW>; + los-gpios = <&gpioexp2 3 GPIO_ACTIVE_HIGH>; + tx-fault-gpios = <&gpioexp2 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&gpioexp2 1 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; + + sfp2: sfp2 { + compatible = "sff,sfp"; + i2c-bus = <&sfp2_i2c>; + mod-def0-gpios = <&gpioexp2 10 GPIO_ACTIVE_LOW>; + los-gpios = <&gpioexp2 11 GPIO_ACTIVE_HIGH>; + tx-fault-gpios = <&gpioexp2 8 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&gpioexp2 9 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; }; &duart0 { @@ -69,6 +89,7 @@ i2c-mux@70 { reg = <0x70>; #address-cells = <1>; #size-cells = <0>; + vdd-supply = <®_3v3>; i2c@0 { reg = <0x0>; diff --git a/arch/arm64/boot/dts/freescale/tqmls10xxa.dtsi b/arch/arm64/boot/dts/freescale/tqmls10xxa.dtsi index 138f8778afde..7da1bfd83cca 100644 --- a/arch/arm64/boot/dts/freescale/tqmls10xxa.dtsi +++ b/arch/arm64/boot/dts/freescale/tqmls10xxa.dtsi @@ -8,6 +8,14 @@ */ / { + reg_vcc1v8: regulator-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "VCC1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + reg_vcc3v3: regulator-vcc3v3 { compatible = "regulator-fixed"; regulator-name = "VCC3V3"; diff --git a/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi b/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi index a77a504effea..c1e66db0f4c5 100644 --- a/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi +++ b/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi @@ -126,6 +126,7 @@ cb_intosc_ls_clk: cb-intosc-ls-clk { f2s_free_clk: f2s-free-clk { #clock-cells = <0>; compatible = "fixed-clock"; + clock-frequency = <100000000>; }; osc1: osc1 { diff --git a/arch/arm64/boot/dts/lg/lg1312.dtsi b/arch/arm64/boot/dts/lg/lg1312.dtsi index bb0bcc6875dc..e83fdc92621e 100644 --- a/arch/arm64/boot/dts/lg/lg1312.dtsi +++ b/arch/arm64/boot/dts/lg/lg1312.dtsi @@ -5,103 +5,12 @@ * Copyright (C) 2016, LG Electronics */ -#include #include +#include "lg131x.dtsi" + / { - #address-cells = <2>; - #size-cells = <2>; - compatible = "lge,lg1312"; - interrupt-parent = <&gic>; - - cpus { - #address-cells = <2>; - #size-cells = <0>; - - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x0>; - next-level-cache = <&L2_0>; - }; - cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x1>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x2>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x3>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - L2_0: l2-cache0 { - compatible = "cache"; - cache-level = <2>; - cache-unified; - }; - }; - - psci { - compatible = "arm,psci-0.2", "arm,psci"; - method = "smc"; - cpu_suspend = <0x84000001>; - cpu_off = <0x84000002>; - cpu_on = <0x84000003>; - }; - - gic: interrupt-controller@c0001000 { - #interrupt-cells = <3>; - compatible = "arm,gic-400"; - interrupt-controller; - reg = <0x0 0xc0001000 0x1000>, - <0x0 0xc0002000 0x2000>, - <0x0 0xc0004000 0x2000>, - <0x0 0xc0006000 0x2000>; - }; - - pmu { - compatible = "arm,cortex-a53-pmu"; - interrupts = , - , - , - ; - interrupt-affinity = <&cpu0>, - <&cpu1>, - <&cpu2>, - <&cpu3>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; - }; - - clk_bus: clk_bus { - #clock-cells = <0>; - - compatible = "fixed-clock"; - clock-frequency = <198000000>; - clock-output-names = "BUSCLK"; - }; soc { #address-cells = <2>; @@ -122,233 +31,4 @@ eth0: ethernet@c1b00000 { mac-address = [ 00 00 00 00 00 00 ]; }; }; - - amba { - #address-cells = <2>; - #size-cells = <1>; - - compatible = "simple-bus"; - interrupt-parent = <&gic>; - ranges; - - timers: timer@fd100000 { - compatible = "arm,sp804", "arm,primecell"; - reg = <0x0 0xfd100000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>, <&clk_bus>; - clock-names = "timer0clk", "timer1clk", "apb_pclk"; - }; - wdog: watchdog@fd200000 { - compatible = "arm,sp805", "arm,primecell"; - reg = <0x0 0xfd200000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "wdog_clk", "apb_pclk"; - }; - uart0: serial@fe000000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe000000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - uart1: serial@fe100000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe100000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - uart2: serial@fe200000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe200000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - spi0: spi@fe800000 { - compatible = "arm,pl022", "arm,primecell"; - reg = <0x0 0xfe800000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "sspclk", "apb_pclk"; - }; - spi1: spi@fe900000 { - compatible = "arm,pl022", "arm,primecell"; - reg = <0x0 0xfe900000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "sspclk", "apb_pclk"; - }; - dmac0: dma-controller@c1128000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0x0 0xc1128000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - #dma-cells = <1>; - }; - gpio0: gpio@fd400000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd400000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio1: gpio@fd410000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd410000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio2: gpio@fd420000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd420000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio3: gpio@fd430000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd430000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - gpio4: gpio@fd440000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd440000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio5: gpio@fd450000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd450000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio6: gpio@fd460000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd460000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio7: gpio@fd470000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd470000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio8: gpio@fd480000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd480000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio9: gpio@fd490000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd490000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio10: gpio@fd4a0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4a0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio11: gpio@fd4b0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4b0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - gpio12: gpio@fd4c0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4c0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio13: gpio@fd4d0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4d0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio14: gpio@fd4e0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4e0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio15: gpio@fd4f0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4f0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio16: gpio@fd500000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd500000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio17: gpio@fd510000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd510000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - }; }; diff --git a/arch/arm64/boot/dts/lg/lg1313.dtsi b/arch/arm64/boot/dts/lg/lg1313.dtsi index c07d670bc465..92fa5694cad1 100644 --- a/arch/arm64/boot/dts/lg/lg1313.dtsi +++ b/arch/arm64/boot/dts/lg/lg1313.dtsi @@ -5,103 +5,12 @@ * Copyright (C) 2016, LG Electronics */ -#include #include +#include "lg131x.dtsi" + / { - #address-cells = <2>; - #size-cells = <2>; - compatible = "lge,lg1313"; - interrupt-parent = <&gic>; - - cpus { - #address-cells = <2>; - #size-cells = <0>; - - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x0>; - next-level-cache = <&L2_0>; - }; - cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x1>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x2>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0 0x3>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - L2_0: l2-cache0 { - compatible = "cache"; - cache-level = <2>; - cache-unified; - }; - }; - - psci { - compatible = "arm,psci-0.2", "arm,psci"; - method = "smc"; - cpu_suspend = <0x84000001>; - cpu_off = <0x84000002>; - cpu_on = <0x84000003>; - }; - - gic: interrupt-controller@c0001000 { - #interrupt-cells = <3>; - compatible = "arm,gic-400"; - interrupt-controller; - reg = <0x0 0xc0001000 0x1000>, - <0x0 0xc0002000 0x2000>, - <0x0 0xc0004000 0x2000>, - <0x0 0xc0006000 0x2000>; - }; - - pmu { - compatible = "arm,cortex-a53-pmu"; - interrupts = , - , - , - ; - interrupt-affinity = <&cpu0>, - <&cpu1>, - <&cpu2>, - <&cpu3>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; - }; - - clk_bus: clk_bus { - #clock-cells = <0>; - - compatible = "fixed-clock"; - clock-frequency = <198000000>; - clock-output-names = "BUSCLK"; - }; soc { #address-cells = <2>; @@ -122,233 +31,4 @@ eth0: ethernet@c3700000 { mac-address = [ 00 00 00 00 00 00 ]; }; }; - - amba { - #address-cells = <2>; - #size-cells = <1>; - - compatible = "simple-bus"; - interrupt-parent = <&gic>; - ranges; - - timers: timer@fd100000 { - compatible = "arm,sp804", "arm,primecell"; - reg = <0x0 0xfd100000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>, <&clk_bus>; - clock-names = "timer0clk", "timer1clk", "apb_pclk"; - }; - wdog: watchdog@fd200000 { - compatible = "arm,sp805", "arm,primecell"; - reg = <0x0 0xfd200000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "wdog_clk", "apb_pclk"; - }; - uart0: serial@fe000000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe000000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - uart1: serial@fe100000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe100000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - uart2: serial@fe200000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0xfe200000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - spi0: spi@fe800000 { - compatible = "arm,pl022", "arm,primecell"; - reg = <0x0 0xfe800000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "sspclk", "apb_pclk"; - }; - spi1: spi@fe900000 { - compatible = "arm,pl022", "arm,primecell"; - reg = <0x0 0xfe900000 0x1000>; - interrupts = ; - clocks = <&clk_bus>, <&clk_bus>; - clock-names = "sspclk", "apb_pclk"; - }; - dmac0: dma-controller@c1128000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0x0 0xc1128000 0x1000>; - interrupts = ; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - #dma-cells = <1>; - }; - gpio0: gpio@fd400000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd400000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio1: gpio@fd410000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd410000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio2: gpio@fd420000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd420000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio3: gpio@fd430000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd430000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - gpio4: gpio@fd440000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd440000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio5: gpio@fd450000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd450000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio6: gpio@fd460000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd460000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio7: gpio@fd470000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd470000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio8: gpio@fd480000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd480000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio9: gpio@fd490000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd490000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio10: gpio@fd4a0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4a0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio11: gpio@fd4b0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4b0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - gpio12: gpio@fd4c0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4c0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio13: gpio@fd4d0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4d0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio14: gpio@fd4e0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4e0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio15: gpio@fd4f0000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd4f0000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio16: gpio@fd500000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd500000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - gpio17: gpio@fd510000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0x0 0xfd510000 0x1000>; - clocks = <&clk_bus>; - clock-names = "apb_pclk"; - }; - }; }; diff --git a/arch/arm64/boot/dts/lg/lg131x.dtsi b/arch/arm64/boot/dts/lg/lg131x.dtsi new file mode 100644 index 000000000000..4cb1e4510897 --- /dev/null +++ b/arch/arm64/boot/dts/lg/lg131x.dtsi @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dts file for lg131x SoCs + * + * Copyright (C) 2016, LG Electronics + */ + +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + interrupt-parent = <&gic>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x0>; + next-level-cache = <&L2_0>; + }; + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x1>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x2>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0 0x3>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + L2_0: l2-cache0 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + psci { + compatible = "arm,psci-0.2", "arm,psci"; + method = "smc"; + cpu_suspend = <0x84000001>; + cpu_off = <0x84000002>; + cpu_on = <0x84000003>; + }; + + gic: interrupt-controller@c0001000 { + #interrupt-cells = <3>; + compatible = "arm,gic-400"; + interrupt-controller; + reg = <0x0 0xc0001000 0x1000>, + <0x0 0xc0002000 0x2000>, + <0x0 0xc0004000 0x2000>, + <0x0 0xc0006000 0x2000>; + }; + + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = , + , + , + ; + interrupt-affinity = <&cpu0>, + <&cpu1>, + <&cpu2>, + <&cpu3>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; + + clk_bus: clk_bus { + #clock-cells = <0>; + + compatible = "fixed-clock"; + clock-frequency = <198000000>; + clock-output-names = "BUSCLK"; + }; + + amba { + #address-cells = <2>; + #size-cells = <1>; + + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges; + + timers: timer@fd100000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0x0 0xfd100000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>, <&clk_bus>; + clock-names = "timer0clk", "timer1clk", "apb_pclk"; + }; + wdog: watchdog@fd200000 { + compatible = "arm,sp805", "arm,primecell"; + reg = <0x0 0xfd200000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "wdog_clk", "apb_pclk"; + }; + uart0: serial@fe000000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0xfe000000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + uart1: serial@fe100000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0xfe100000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + uart2: serial@fe200000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0xfe200000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + spi0: spi@fe800000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0xfe800000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "sspclk", "apb_pclk"; + }; + spi1: spi@fe900000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0xfe900000 0x1000>; + interrupts = ; + clocks = <&clk_bus>, <&clk_bus>; + clock-names = "sspclk", "apb_pclk"; + }; + dmac0: dma-controller@c1128000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0x0 0xc1128000 0x1000>; + interrupts = ; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + #dma-cells = <1>; + }; + gpio0: gpio@fd400000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd400000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio1: gpio@fd410000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd410000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio2: gpio@fd420000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd420000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio3: gpio@fd430000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd430000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + }; + gpio4: gpio@fd440000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd440000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio5: gpio@fd450000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd450000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio6: gpio@fd460000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd460000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio7: gpio@fd470000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd470000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio8: gpio@fd480000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd480000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio9: gpio@fd490000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd490000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio10: gpio@fd4a0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4a0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio11: gpio@fd4b0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4b0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + }; + gpio12: gpio@fd4c0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4c0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio13: gpio@fd4d0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4d0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio14: gpio@fd4e0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4e0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio15: gpio@fd4f0000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd4f0000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio16: gpio@fd500000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd500000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + gpio17: gpio@fd510000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0x0 0xfd510000 0x1000>; + clocks = <&clk_bus>; + clock-names = "apb_pclk"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile index ce751b5028e2..40e5ac6cd468 100644 --- a/arch/arm64/boot/dts/marvell/Makefile +++ b/arch/arm64/boot/dts/marvell/Makefile @@ -32,3 +32,5 @@ dtb-$(CONFIG_ARCH_MVEBU) += cn9130-cf-base.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9130-cf-pro.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9131-cf-solidwan.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9132-clearfog.dtb + +subdir-y += mmp diff --git a/arch/arm64/boot/dts/marvell/mmp/Makefile b/arch/arm64/boot/dts/marvell/mmp/Makefile new file mode 100644 index 000000000000..103175ed63b0 --- /dev/null +++ b/arch/arm64/boot/dts/marvell/mmp/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_MMP) += pxa1908-samsung-coreprimevelte.dtb diff --git a/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts b/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts new file mode 100644 index 000000000000..47a4f01a7077 --- /dev/null +++ b/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "pxa1908.dtsi" +#include +#include + +/ { + model = "Samsung Galaxy Core Prime VE LTE"; + compatible = "samsung,coreprimevelte", "marvell,pxa1908"; + + aliases { + mmc0 = &sdh2; /* eMMC */ + mmc1 = &sdh0; /* SD card */ + serial0 = &uart0; + }; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + stdout-path = "serial0:115200n8"; + + fb0: framebuffer@17177000 { + compatible = "simple-framebuffer"; + reg = <0 0x17177000 0 (480 * 800 * 4)>; + width = <480>; + height = <800>; + stride = <(480 * 4)>; + format = "a8r8g8b8"; + }; + }; + + /* Bootloader fills this in */ + memory@0 { + device_type = "memory"; + reg = <0 0 0 0>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + framebuffer@17000000 { + reg = <0 0x17000000 0 0x1800000>; + no-map; + }; + + gpu@9000000 { + reg = <0 0x9000000 0 0x1000000>; + }; + + /* Communications processor, aka modem */ + cp@5000000 { + reg = <0 0x5000000 0 0x3000000>; + }; + + cm3@a000000 { + reg = <0 0xa000000 0 0x80000>; + }; + + seclog@8000000 { + reg = <0 0x8000000 0 0x100000>; + }; + + ramoops@8100000 { + compatible = "ramoops"; + reg = <0 0x8100000 0 0x40000>; + record-size = <0x8000>; + console-size = <0x20000>; + max-reason = <5>; + }; + }; + + i2c-muic { + compatible = "i2c-gpio"; + sda-gpios = <&gpio 30 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio 29 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <3>; + i2c-gpio,timeout-ms = <100>; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c_muic_pins>; + + muic: extcon@14 { + compatible = "siliconmitus,sm5504-muic"; + reg = <0x14>; + interrupt-parent = <&gpio>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&gpio_keys_pins>; + autorepeat; + + key-home { + label = "Home"; + linux,code = ; + gpios = <&gpio 50 GPIO_ACTIVE_LOW>; + }; + + key-volup { + label = "Volume Up"; + linux,code = ; + gpios = <&gpio 16 GPIO_ACTIVE_LOW>; + }; + + key-voldown { + label = "Volume Down"; + linux,code = ; + gpios = <&gpio 17 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&smmu { + status = "okay"; +}; + +&pmx { + pinctrl-single,gpio-range = <&range 55 55 0>, + <&range 110 32 0>, + <&range 52 1 0>; + + pinctrl-names = "default"; + pinctrl-0 = <&board_pins_0 &board_pins_1 &board_pins_2>; + + board_pins_0: board-pins-0 { + pinctrl-single,pins = < + 0x160 0 + 0x164 0 + 0x168 0 + 0x16c 0 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0x8000 0x8000 0 0xc000>; + pinctrl-single,bias-pulldown = <0x8000 0x8000 0 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0x288 0x388>; + }; + + board_pins_1: board-pins-1 { + pinctrl-single,pins = < + 0x44 1 + 0x48 1 + 0x20 1 + 0x18 1 + 0x14 1 + 0x10 1 + 0xc 1 + 0x8 1 + 0x68 1 + 0x58 0 + 0x54 0 + 0x7c 0 + 0x6c 0 + 0x70 0 + 0x4c 1 + 0x50 1 + 0xac 0 + 0x90 0 + 0x8c 0 + 0x88 0 + 0x84 0 + 0xc8 0 + 0x128 0 + 0x190 0 + 0x194 0 + 0x1a0 0 + 0x114 0 + 0x118 0 + 0x1d8 0 + 0x1e4 0 + 0xe8 0 + 0x100 0 + 0x204 0 + 0x210 0 + 0x218 0 + >; + pinctrl-single,bias-pullup = <0xc000 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0x8000 0xa000 0x8000 0xc000>; + pinctrl-single,low-power-mode = <0x288 0x388>; + }; + + board_pins_2: board-pins-2 { + pinctrl-single,pins = < + 0x260 0 + 0x264 0 + 0x268 0 + 0x26c 0 + 0x270 0 + 0x274 0 + 0x78 0 + 0x74 0 + 0xb0 1 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0 0xa000 0 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0 0x388>; + }; + + uart0_pins: uart0-pins { + pinctrl-single,pins = < + 0x198 6 + 0x19c 6 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0 0xa000 0 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0 0x388>; + }; + + gpio_keys_pins: gpio-keys-pins { + pinctrl-single,pins = < + 0x11c 0 + 0x120 0 + 0x1a4 0 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0xc000 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0x8000 0xa0000 0x8000 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0 0x388>; + }; + + i2c_muic_pins: i2c-muic-pins { + pinctrl-single,pins = < + 0x154 0 + 0x150 0 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0 0xa000 0 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0x288 0x388>; + }; + + sdh0_pins_0: sdh0-pins-0 { + pinctrl-single,pins = < + 0x108 0 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0xc000 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0x8000 0xa000 0x8000 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0 0x388>; + }; + + sdh0_pins_1: sdh0-pins-1 { + pinctrl-single,pins = < + 0x94 0 + 0x98 0 + 0x9c 0 + 0xa0 0 + 0xa4 0 + >; + pinctrl-single,drive-strength = <0x800 0x1800>; + pinctrl-single,bias-pullup = <0xc000 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0x8000 0xa000 0x8000 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0 0x388>; + }; + + sdh0_pins_2: sdh0-pins-2 { + pinctrl-single,pins = < + 0xa8 0 + >; + pinctrl-single,drive-strength = <0x1000 0x1800>; + pinctrl-single,bias-pullup = <0 0xc000 0 0xc000>; + pinctrl-single,bias-pulldown = <0 0xa000 0 0xa000>; + pinctrl-single,input-schmitt = <0 0x30>; + pinctrl-single,input-schmitt-enable = <0x40 0 0x40 0x40>; + pinctrl-single,low-power-mode = <0x208 0x388>; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +}; + +&twsi0 { + status = "okay"; +}; + +&twsi1 { + status = "okay"; +}; + +&twsi2 { + status = "okay"; +}; + +&twsi3 { + status = "okay"; +}; + +&usb { + extcon = <&muic>, <&muic>; +}; + +&sdh2 { + /* Disabled for now because initialization fails with -ETIMEDOUT. */ + status = "disabled"; + bus-width = <8>; + non-removable; + mmc-ddr-1_8v; +}; + +&sdh0 { + pinctrl-names = "default"; + pinctrl-0 = <&sdh0_pins_0 &sdh0_pins_1 &sdh0_pins_2>; + cd-gpios = <&gpio 11 0>; + cd-inverted; + bus-width = <4>; + wp-inverted; +}; diff --git a/arch/arm64/boot/dts/marvell/mmp/pxa1908.dtsi b/arch/arm64/boot/dts/marvell/mmp/pxa1908.dtsi new file mode 100644 index 000000000000..cf2b9109688c --- /dev/null +++ b/arch/arm64/boot/dts/marvell/mmp/pxa1908.dtsi @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-only +/dts-v1/; + +#include +#include + +/ { + model = "Marvell Armada PXA1908"; + compatible = "marvell,pxa1908"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0 0>; + enable-method = "psci"; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0 1>; + enable-method = "psci"; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0 2>; + enable-method = "psci"; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0 3>; + enable-method = "psci"; + }; + }; + + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = , + , + , + ; + interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + smmu: iommu@c0010000 { + compatible = "arm,mmu-400"; + reg = <0 0xc0010000 0 0x10000>; + #global-interrupts = <1>; + #iommu-cells = <1>; + interrupts = , + ; + status = "disabled"; + }; + + gic: interrupt-controller@d1df9000 { + compatible = "arm,gic-400"; + reg = <0 0xd1df9000 0 0x1000>, + <0 0xd1dfa000 0 0x2000>, + /* The subsequent registers are guesses. */ + <0 0xd1dfc000 0 0x2000>, + <0 0xd1dfe000 0 0x2000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + }; + + apb@d4000000 { + compatible = "simple-bus"; + reg = <0 0xd4000000 0 0x200000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xd4000000 0x200000>; + + pdma: dma-controller@0 { + compatible = "marvell,pdma-1.0"; + reg = <0 0x10000>; + interrupts = ; + dma-channels = <30>; + #dma-cells = <2>; + }; + + twsi1: i2c@10800 { + compatible = "mrvl,mmp-twsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x10800 0x64>; + interrupts = ; + clocks = <&apbc PXA1908_CLK_TWSI1>; + mrvl,i2c-fast-mode; + status = "disabled"; + }; + + twsi0: i2c@11000 { + compatible = "mrvl,mmp-twsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x11000 0x64>; + interrupts = ; + clocks = <&apbc PXA1908_CLK_TWSI0>; + mrvl,i2c-fast-mode; + status = "disabled"; + }; + + twsi3: i2c@13800 { + compatible = "mrvl,mmp-twsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x13800 0x64>; + interrupts = ; + clocks = <&apbc PXA1908_CLK_TWSI3>; + mrvl,i2c-fast-mode; + status = "disabled"; + }; + + apbc: clock-controller@15000 { + compatible = "marvell,pxa1908-apbc"; + reg = <0x15000 0x1000>; + #clock-cells = <1>; + }; + + uart0: serial@17000 { + compatible = "mrvl,mmp-uart", "intel,xscale-uart"; + reg = <0x17000 0x1000>; + interrupts = ; + clocks = <&apbc PXA1908_CLK_UART0>; + reg-shift = <2>; + }; + + uart1: serial@18000 { + compatible = "mrvl,mmp-uart", "intel,xscale-uart"; + reg = <0x18000 0x1000>; + interrupts = ; + clocks = <&apbc PXA1908_CLK_UART1>; + reg-shift = <2>; + }; + + gpio: gpio@19000 { + compatible = "marvell,mmp-gpio"; + reg = <0x19000 0x800>; + #address-cells = <1>; + #size-cells = <1>; + gpio-controller; + #gpio-cells = <2>; + clocks = <&apbc PXA1908_CLK_GPIO>; + interrupts = ; + interrupt-names = "gpio_mux"; + interrupt-controller; + #interrupt-cells = <2>; + ranges = <0 0x19000 0x800>; + + gpio@0 { + reg = <0x0 0x4>; + }; + + gpio@4 { + reg = <0x4 0x4>; + }; + + gpio@8 { + reg = <0x8 0x4>; + }; + + gpio@100 { + reg = <0x100 0x4>; + }; + }; + + pmx: pinmux@1e000 { + compatible = "marvell,pxa1908-padconf", "pinconf-single"; + reg = <0x1e000 0x330>; + + #pinctrl-cells = <1>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <7>; + + range: gpio-range { + #pinctrl-single,gpio-range-cells = <3>; + }; + }; + + uart2: serial@36000 { + compatible = "mrvl,mmp-uart", "intel,xscale-uart"; + reg = <0x36000 0x1000>; + interrupts = ; + clocks = <&apbcp PXA1908_CLK_UART2>; + reg-shift = <2>; + }; + + twsi2: i2c@37000 { + compatible = "mrvl,mmp-twsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x37000 0x64>; + interrupts = ; + clocks = <&apbcp PXA1908_CLK_TWSI2>; + mrvl,i2c-fast-mode; + status = "disabled"; + }; + + apbcp: clock-controller@3b000 { + compatible = "marvell,pxa1908-apbcp"; + reg = <0x3b000 0x1000>; + #clock-cells = <1>; + }; + + mpmu: clock-controller@50000 { + compatible = "marvell,pxa1908-mpmu"; + reg = <0x50000 0x1000>; + #clock-cells = <1>; + }; + }; + + axi@d4200000 { + compatible = "simple-bus"; + reg = <0 0xd4200000 0 0x200000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xd4200000 0x200000>; + + usbphy: phy@7000 { + compatible = "marvell,pxa1928-usb-phy"; + reg = <0x7000 0x200>; + clocks = <&apmu PXA1908_CLK_USB>; + #phy-cells = <0>; + }; + + usb: usb@8000 { + compatible = "chipidea,usb2"; + reg = <0x8000 0x200>; + interrupts = ; + clocks = <&apmu PXA1908_CLK_USB>; + phys = <&usbphy>; + phy-names = "usb-phy"; + }; + + sdh0: mmc@80000 { + compatible = "mrvl,pxav3-mmc"; + reg = <0x80000 0x120>; + interrupts = ; + clocks = <&apmu PXA1908_CLK_SDH0>; + clock-names = "io"; + mrvl,clk-delay-cycles = <31>; + }; + + sdh1: mmc@80800 { + compatible = "mrvl,pxav3-mmc"; + reg = <0x80800 0x120>; + interrupts = ; + clocks = <&apmu PXA1908_CLK_SDH1>; + clock-names = "io"; + mrvl,clk-delay-cycles = <31>; + }; + + sdh2: mmc@81000 { + compatible = "mrvl,pxav3-mmc"; + reg = <0x81000 0x120>; + interrupts = ; + clocks = <&apmu PXA1908_CLK_SDH2>; + clock-names = "io"; + mrvl,clk-delay-cycles = <31>; + }; + + apmu: clock-controller@82800 { + compatible = "marvell,pxa1908-apmu"; + reg = <0x82800 0x400>; + #clock-cells = <1>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile index f68865d06edd..a4df4c21399e 100644 --- a/arch/arm64/boot/dts/mediatek/Makefile +++ b/arch/arm64/boot/dts/mediatek/Makefile @@ -68,6 +68,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-magneton-sku393218.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-ponyta-sku0.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-ponyta-sku1.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-rusty-sku196608.dtb +dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-squirtle.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-starmie-sku0.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-starmie-sku1.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-steelix-sku131072.dtb @@ -76,8 +77,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-tentacool-sku327681.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-tentacool-sku327683.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-tentacruel-sku262144.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-tentacruel-sku262148.dtb -dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-voltorb-sku589824.dtb -dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-voltorb-sku589825.dtb +dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-corsola-voltorb.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8186-evb.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8188-evb.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8188-geralt-ciri-sku0.dtb diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi index 81ba045e0e0e..5fd222df440d 100644 --- a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi @@ -3,6 +3,7 @@ /dts-v1/; #include +#include #include #include "mt7988a.dtsi" @@ -21,6 +22,25 @@ fan: pwm-fan { status = "okay"; }; + gpio-leds { + compatible = "gpio-leds"; + + led_green: led-green { + function = LED_FUNCTION_STATUS; + color = ; + gpios = <&pio 79 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + led_blue: led-blue { + function = LED_FUNCTION_WPS; + color = ; + gpios = <&pio 63 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + reg_1p8v: regulator-1p8v { compatible = "regulator-fixed"; regulator-name = "fixed-1.8V"; @@ -40,6 +60,10 @@ reg_3p3v: regulator-3p3v { }; }; +&cci { + proc-supply = <&rt5190_buck3>; +}; + &cpu0 { proc-supply = <&rt5190_buck3>; }; @@ -219,18 +243,6 @@ &pcie3 { }; &pio { - mdio0_pins: mdio0-pins { - mux { - function = "eth"; - groups = "mdc_mdio0"; - }; - - conf { - pins = "SMI_0_MDC", "SMI_0_MDIO"; - drive-strength = <8>; - }; - }; - i2c0_pins: i2c0-g0-pins { mux { function = "i2c"; @@ -245,20 +257,6 @@ mux { }; }; - i2c1_sfp_pins: i2c1-sfp-g0-pins { - mux { - function = "i2c"; - groups = "i2c1_sfp"; - }; - }; - - i2c2_0_pins: i2c2-g0-pins { - mux { - function = "i2c"; - groups = "i2c2_0"; - }; - }; - i2c2_1_pins: i2c2-g1-pins { mux { function = "i2c"; @@ -294,34 +292,6 @@ mux { }; }; - gbe0_led1_pins: gbe0-led1-pins { - mux { - function = "led"; - groups = "gbe0_led1"; - }; - }; - - gbe1_led1_pins: gbe1-led1-pins { - mux { - function = "led"; - groups = "gbe1_led1"; - }; - }; - - gbe2_led1_pins: gbe2-led1-pins { - mux { - function = "led"; - groups = "gbe2_led1"; - }; - }; - - gbe3_led1_pins: gbe3-led1-pins { - mux { - function = "led"; - groups = "gbe3_led1"; - }; - }; - i2p5gbe_led0_pins: 2p5gbe-led0-pins { mux { function = "led"; @@ -329,13 +299,6 @@ mux { }; }; - i2p5gbe_led1_pins: 2p5gbe-led1-pins { - mux { - function = "led"; - groups = "2p5gbe_led1"; - }; - }; - mmc0_pins_emmc_45: mmc0-emmc-45-pins { mux { function = "flash"; @@ -357,40 +320,12 @@ mux { }; }; - snfi_pins: snfi-pins { - mux { - function = "flash"; - groups = "snfi"; - }; - }; - - spi0_pins: spi0-pins { - mux { - function = "spi"; - groups = "spi0"; - }; - }; - spi0_flash_pins: spi0-flash-pins { mux { function = "spi"; groups = "spi0", "spi0_wp_hold"; }; }; - - spi2_pins: spi2-pins { - mux { - function = "spi"; - groups = "spi2"; - }; - }; - - spi2_flash_pins: spi2-flash-pins { - mux { - function = "spi"; - groups = "spi2", "spi2_wp_hold"; - }; - }; }; &pwm { diff --git a/arch/arm64/boot/dts/mediatek/mt7988a.dtsi b/arch/arm64/boot/dts/mediatek/mt7988a.dtsi index c46b31f8d653..560ec86dbec0 100644 --- a/arch/arm64/boot/dts/mediatek/mt7988a.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7988a.dtsi @@ -12,6 +12,35 @@ / { #address-cells = <2>; #size-cells = <2>; + cci: cci { + compatible = "mediatek,mt7988-cci", "mediatek,mt8183-cci"; + clocks = <&mcusys CLK_MCU_BUS_DIV_SEL>, + <&topckgen CLK_TOP_XTAL>; + clock-names = "cci", "intermediate"; + operating-points-v2 = <&cci_opp>; + }; + + cci_opp: opp-table-cci { + compatible = "operating-points-v2"; + opp-shared; + opp-480000000 { + opp-hz = /bits/ 64 <480000000>; + opp-microvolt = <850000>; + }; + opp-660000000 { + opp-hz = /bits/ 64 <660000000>; + opp-microvolt = <850000>; + }; + opp-900000000 { + opp-hz = /bits/ 64 <900000000>; + opp-microvolt = <850000>; + }; + opp-1080000000 { + opp-hz = /bits/ 64 <1080000000>; + opp-microvolt = <900000>; + }; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -25,6 +54,7 @@ cpu0: cpu@0 { <&topckgen CLK_TOP_XTAL>; clock-names = "cpu", "intermediate"; operating-points-v2 = <&cluster0_opp>; + mediatek,cci = <&cci>; }; cpu1: cpu@1 { @@ -36,6 +66,7 @@ cpu1: cpu@1 { <&topckgen CLK_TOP_XTAL>; clock-names = "cpu", "intermediate"; operating-points-v2 = <&cluster0_opp>; + mediatek,cci = <&cci>; }; cpu2: cpu@2 { @@ -47,6 +78,7 @@ cpu2: cpu@2 { <&topckgen CLK_TOP_XTAL>; clock-names = "cpu", "intermediate"; operating-points-v2 = <&cluster0_opp>; + mediatek,cci = <&cci>; }; cpu3: cpu@3 { @@ -58,6 +90,7 @@ cpu3: cpu@3 { <&topckgen CLK_TOP_XTAL>; clock-names = "cpu", "intermediate"; operating-points-v2 = <&cluster0_opp>; + mediatek,cci = <&cci>; }; cluster0_opp: opp-table-0 { diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index 6d1d8877b43f..122a57c3780b 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -318,6 +318,14 @@ reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; + + afe_dma_mem: audio-dma-pool { + compatible = "shared-dma-pool"; + size = <0 0x100000>; + alignment = <0 0x10>; + no-map; + }; + vpu_dma_reserved: vpu-dma-mem@b7000000 { compatible = "shared-dma-pool"; reg = <0 0xb7000000 0 0x500000>; @@ -887,6 +895,7 @@ afe: audio-controller@11220000 { <&topckgen CLK_TOP_AUD_2_SEL>; assigned-clock-parents = <&topckgen CLK_TOP_APLL1>, <&topckgen CLK_TOP_APLL2>; + memory-region = <&afe_dma_mem>; }; mmc0: mmc@11230000 { diff --git a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi index ecc6c4d6f1cd..400c61d11035 100644 --- a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi @@ -85,6 +85,13 @@ reserved_memory: reserved-memory { #size-cells = <2>; ranges; + afe_dma_mem: audio-dma-pool { + compatible = "shared-dma-pool"; + size = <0 0x100000>; + alignment = <0 0x10>; + no-map; + }; + scp_mem_reserved: memory@50000000 { compatible = "shared-dma-pool"; reg = <0 0x50000000 0 0x2900000>; @@ -199,6 +206,10 @@ tboard_thermistor2: thermal-sensor2 { }; }; +&afe { + memory-region = <&afe_dma_mem>; +}; + &auxadc { status = "okay"; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-squirtle.dts b/arch/arm64/boot/dts/mediatek/mt8186-corsola-squirtle.dts new file mode 100644 index 000000000000..f721ad4e5c97 --- /dev/null +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola-squirtle.dts @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 Google LLC + */ + +/dts-v1/; +#include "mt8186-corsola-voltorb.dtsi" + +/ { + model = "Google squirtle board"; + compatible = "google,squirtle", "mediatek,mt8186"; + chassis-type = "convertible"; +}; + +&i2c1 { + touchscreen@10 { + compatible = "elan,ekth6915"; + reg = <0x10>; + interrupts-extended = <&pio 12 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&touchscreen_pins>; + reset-gpios = <&pio 60 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_s3>; + status = "fail-needs-probe"; + }; + + touchscreen@16 { + compatible = "elan,ekth8d18", "elan,ekth6a12nay"; + reg = <0x16>; + interrupts-extended = <&pio 12 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&touchscreen_pins>; + reset-gpios = <&pio 60 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_s3>; + status = "fail-needs-probe"; + }; +}; + +&i2c2 { + trackpad@68 { + compatible = "hid-over-i2c"; + reg = <0x68>; + hid-descr-addr = <0x20>; + interrupts-extended = <&pio 11 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pin>; + vdd-supply = <&pp3300_s3>; + wakeup-source; + status = "fail-needs-probe"; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + /delete-node/ codec@1a; + + rt5650: codec@1a { + compatible = "realtek,rt5650"; + reg = <0x1a>; + interrupts-extended = <&pio 17 IRQ_TYPE_EDGE_BOTH>; + avdd-supply = <&mt6366_vio18_reg>; + cpvdd-supply = <&mt6366_vio18_reg>; + pinctrl-names = "default"; + pinctrl-0 = <&speaker_codec_pins_default>; + cbj-sleeve-gpios = <&pio 150 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + realtek,dmic1-data-pin = <2>; + realtek,jd-mode = <2>; + }; +}; + +&sound { + compatible = "mediatek,mt8186-mt6366-rt5650-sound"; + model = "mt8186_rt5650"; + + audio-routing = + "Headphone", "HPOL", + "Headphone", "HPOR", + "HDMI1", "TX"; + + hs-playback-dai-link { + codec { + sound-dai = <&rt5650>; + }; + }; + + hs-capture-dai-link { + codec { + sound-dai = <&rt5650>; + }; + }; + + spk-hdmi-playback-dai-link { + codec { + sound-dai = <&it6505dptx>; + }; + }; +}; + +&speaker_codec { + status = "disabled"; +}; + +&trackpad_steelix { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-steelix.dtsi b/arch/arm64/boot/dts/mediatek/mt8186-corsola-steelix.dtsi index e74e886a00cb..8a196dc9a96b 100644 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola-steelix.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola-steelix.dtsi @@ -118,13 +118,16 @@ &i2c2 { i2c-scl-internal-delay-ns = <22000>; /* second source component */ - trackpad@2c { + trackpad_steelix: trackpad@2c { compatible = "hid-over-i2c"; reg = <0x2c>; hid-descr-addr = <0x20>; interrupts-extended = <&pio 11 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pin>; vdd-supply = <&pp3300_s3>; wakeup-source; + status = "fail-needs-probe"; }; }; @@ -197,3 +200,7 @@ pins-vreg-en { }; }; }; + +&trackpad { + status = "fail-needs-probe"; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacool-sku327683.dts b/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacool-sku327683.dts index c3ae6f9616c8..4dbf2cb73a81 100644 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacool-sku327683.dts +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacool-sku327683.dts @@ -17,6 +17,8 @@ trackpad@15 { compatible = "hid-over-i2c"; reg = <0x15>; interrupts-extended = <&pio 11 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pin>; hid-descr-addr = <0x0001>; vdd-supply = <&pp3300_s3>; wakeup-source; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacruel-sku262148.dts b/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacruel-sku262148.dts index 447b57b12b41..ee5bc2cd9e9f 100644 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacruel-sku262148.dts +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola-tentacruel-sku262148.dts @@ -19,6 +19,8 @@ trackpad@15 { compatible = "hid-over-i2c"; reg = <0x15>; interrupts-extended = <&pio 11 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pin>; hid-descr-addr = <0x0001>; vdd-supply = <&pp3300_s3>; wakeup-source; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589824.dts b/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589824.dts deleted file mode 100644 index d16834eec87a..000000000000 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589824.dts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Copyright 2022 Google LLC - */ - -/dts-v1/; -#include "mt8186-corsola-voltorb.dtsi" - -/ { - model = "Google Voltorb sku589824 board"; - compatible = "google,voltorb-sku589824", "google,voltorb", - "mediatek,mt8186"; -}; diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589825.dts b/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb.dts similarity index 76% rename from arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589825.dts rename to arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb.dts index 45e57f7706cc..cc805408a8b7 100644 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb-sku589825.dts +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb.dts @@ -7,9 +7,8 @@ #include "mt8186-corsola-voltorb.dtsi" / { - model = "Google Voltorb sku589825 board"; - compatible = "google,voltorb-sku589825", "google,voltorb", - "mediatek,mt8186"; + model = "Google Voltorb board"; + compatible = "google,voltorb", "mediatek,mt8186"; }; &i2c1 { diff --git a/arch/arm64/boot/dts/mediatek/mt8186-corsola.dtsi b/arch/arm64/boot/dts/mediatek/mt8186-corsola.dtsi index fc78a79d96e9..ff20376a44d7 100644 --- a/arch/arm64/boot/dts/mediatek/mt8186-corsola.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8186-corsola.dtsi @@ -161,6 +161,13 @@ reserved_memory: reserved-memory { #size-cells = <2>; ranges; + afe_dma_mem: audio-dma-pool { + compatible = "shared-dma-pool"; + size = <0 0x100000>; + alignment = <0 0x10>; + no-map; + }; + adsp_dma_mem: memory@61000000 { compatible = "shared-dma-pool"; reg = <0 0x61000000 0 0x100000>; @@ -310,6 +317,7 @@ &adsp { }; &afe { + memory-region = <&afe_dma_mem>; status = "okay"; }; @@ -390,19 +398,17 @@ &i2c1 { &i2c2 { pinctrl-names = "default"; - /* - * Trackpad pin put here to work around second source components - * sharing the pinmux in steelix designs. - */ - pinctrl-0 = <&i2c2_pins>, <&trackpad_pin>; + pinctrl-0 = <&i2c2_pins>; clock-frequency = <400000>; i2c-scl-internal-delay-ns = <10000>; status = "okay"; - trackpad@15 { + trackpad: trackpad@15 { compatible = "elan,ekth3000"; reg = <0x15>; interrupts-extended = <&pio 11 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pin>; vcc-supply = <&pp3300_s3>; wakeup-source; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts b/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts index 8c485c3ced2c..163960f58db5 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts +++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts @@ -85,8 +85,15 @@ &i2c2 { trackpad@2c { compatible = "hid-over-i2c"; reg = <0x2c>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pins>; hid-descr-addr = <0x20>; interrupts-extended = <&pio 15 IRQ_TYPE_LEVEL_LOW>; wakeup-source; + status = "fail-needs-probe"; }; }; + +&trackpad { + status = "fail-needs-probe"; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi index dd0d07fbe61a..0b4664f044a1 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi @@ -199,6 +199,13 @@ reserved_memory: reserved-memory { #size-cells = <2>; ranges; + afe_dma_mem: audio-dma-pool { + compatible = "shared-dma-pool"; + size = <0 0x100000>; + alignment = <0 0x10>; + no-map; + }; + scp_mem_reserved: scp@50000000 { compatible = "shared-dma-pool"; reg = <0 0x50000000 0 0x2900000>; @@ -276,6 +283,10 @@ sound: sound { }; }; +&afe { + memory-region = <&afe_dma_mem>; +}; + &dsi0 { status = "okay"; }; @@ -335,11 +346,13 @@ &i2c2 { clock-frequency = <400000>; clock-stretch-ns = <12600>; pinctrl-names = "default"; - pinctrl-0 = <&i2c2_pins>, <&trackpad_pins>; + pinctrl-0 = <&i2c2_pins>; - trackpad@15 { + trackpad: trackpad@15 { compatible = "elan,ekth3000"; reg = <0x15>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_pins>; interrupts-extended = <&pio 15 IRQ_TYPE_LEVEL_LOW>; vcc-supply = <&pp3300_u>; wakeup-source; diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi index dd065b1bf94a..8877953ce292 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi @@ -1430,6 +1430,31 @@ mmc2: mmc@11250000 { status = "disabled"; }; + ufshci: ufshci@11270000 { + compatible = "mediatek,mt8195-ufshci"; + reg = <0 0x11270000 0 0x2300>; + interrupts = ; + phys = <&ufsphy>; + clocks = <&infracfg_ao CLK_INFRA_AO_AES_UFSFDE>, + <&infracfg_ao CLK_INFRA_AO_AES>, + <&infracfg_ao CLK_INFRA_AO_UFS_TICK>, + <&infracfg_ao CLK_INFRA_AO_UNIPRO_SYS>, + <&infracfg_ao CLK_INFRA_AO_UNIPRO_TICK>, + <&infracfg_ao CLK_INFRA_AO_UFS_MP_SAP_B>, + <&infracfg_ao CLK_INFRA_AO_UFS_TX_SYMBOL>, + <&infracfg_ao CLK_INFRA_AO_PERI_UFS_MEM_SUB>; + clock-names = "ufs", "ufs_aes", "ufs_tick", + "unipro_sysclk", "unipro_tick", + "unipro_mp_bclk", "ufs_tx_symbol", + "ufs_mem_sub"; + freq-table-hz = <0 0>, <0 0>, <0 0>, + <0 0>, <0 0>, <0 0>, + <0 0>, <0 0>; + + mediatek,ufs-disable-mcq; + status = "disabled"; + }; + lvts_mcu: thermal-sensor@11278000 { compatible = "mediatek,mt8195-lvts-mcu"; reg = <0 0x11278000 0 0x1000>; diff --git a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi index eaf45d42cd34..a2cdecd2b903 100644 --- a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi @@ -1161,6 +1161,10 @@ power-key { linux,keycodes = ; wakeup-source; }; + + home { + linux,keycodes = ; + }; }; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8395-genio-1200-evk.dts b/arch/arm64/boot/dts/mediatek/mt8395-genio-1200-evk.dts index be5e5f339e81..cf8cd37f5708 100644 --- a/arch/arm64/boot/dts/mediatek/mt8395-genio-1200-evk.dts +++ b/arch/arm64/boot/dts/mediatek/mt8395-genio-1200-evk.dts @@ -79,9 +79,21 @@ bl31_secmon_mem: memory@54600000 { reg = <0 0x54600000 0x0 0x200000>; }; - snd_dma_mem: memory@60000000 { + adsp_mem: memory@60000000 { compatible = "shared-dma-pool"; - reg = <0 0x60000000 0 0x1100000>; + reg = <0 0x60000000 0 0xf00000>; + no-map; + }; + + afe_dma_mem: memory@60f00000 { + compatible = "shared-dma-pool"; + reg = <0 0x60f00000 0 0x100000>; + no-map; + }; + + adsp_dma_mem: memory@61000000 { + compatible = "shared-dma-pool"; + reg = <0 0x61000000 0 0x100000>; no-map; }; @@ -179,6 +191,16 @@ wifi_fixed_3v3: regulator-2 { }; }; +&adsp { + memory-region = <&adsp_dma_mem>, <&adsp_mem>; + status = "okay"; +}; + +&afe { + memory-region = <&afe_dma_mem>; + status = "okay"; +}; + &disp_pwm0 { pinctrl-names = "default"; pinctrl-0 = <&disp_pwm0_pins>; @@ -968,6 +990,21 @@ pins { &pmic { interrupts-extended = <&pio 222 IRQ_TYPE_LEVEL_HIGH>; + + mt6359keys: keys { + compatible = "mediatek,mt6359-keys"; + mediatek,long-press-mode = <1>; + power-off-time-sec = <0>; + + power-key { + linux,keycodes = ; + wakeup-source; + }; + + home { + linux,keycodes = ; + }; + }; }; &scp { @@ -976,6 +1013,26 @@ &scp { status = "okay"; }; +&sound { + compatible = "mediatek,mt8195_mt6359"; + model = "mt8395-evk"; + pinctrl-names = "default"; + pinctrl-0 = <&audio_default_pins>; + audio-routing = + "Headphone", "Headphone L", + "Headphone", "Headphone R"; + mediatek,adsp = <&adsp>; + status = "okay"; + + headphone-dai-link { + link-name = "DL_SRC_BE"; + + codec { + sound-dai = <&pmic 0>; + }; + }; +}; + &spi1 { pinctrl-0 = <&spi1_pins>; pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi b/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi index fead4dde590d..acd3137d2464 100644 --- a/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi +++ b/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi @@ -32,11 +32,6 @@ gic: interrupt-controller@dfff9000 { #interrupt-cells = <3>; interrupt-controller; #address-cells = <0>; - ppi-partitions { - ppi_cluster0: interrupt-partition-0 { - affinity = <&cpu0 &cpu1 &cpu2 &cpu3>; - }; - }; }; }; diff --git a/arch/arm64/boot/dts/nvidia/Makefile b/arch/arm64/boot/dts/nvidia/Makefile index 0fbb8a494dba..171e08c94d5a 100644 --- a/arch/arm64/boot/dts/nvidia/Makefile +++ b/arch/arm64/boot/dts/nvidia/Makefile @@ -12,6 +12,7 @@ DTC_FLAGS_tegra234-p3737-0000+p3701-0000 := -@ 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 := -@ dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb @@ -31,3 +32,4 @@ dtb-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra234-p3737-0000+p3701-0008.dtb 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 diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3834-0008.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3834-0008.dtsi new file mode 100644 index 000000000000..94ace6784749 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3834-0008.dtsi @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +#include "tegra264-p3834.dtsi" + +/ { + compatible = "nvidia,p3834-0008", "nvidia,tegra264"; +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi new file mode 100644 index 000000000000..06795c82427a --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +#include "tegra264.dtsi" + +/ { + compatible = "nvidia,p3834", "nvidia,tegra264"; + + aliases { + }; + + bus@0 { + serial@c4e0000 { + status = "okay"; + }; + + serial@c5a0000 { + status = "okay"; + }; + }; + + bus@8100000000 { + iommu@5000000 { + status = "okay"; + }; + + iommu@6000000 { + status = "okay"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+p3834-0008.dts b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+p3834-0008.dts new file mode 100644 index 000000000000..3a6f4b7e6b75 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+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-p3971-0089+p3834.dtsi" + +/ { + model = "NVIDIA P3971-0089+P3834-0008 Engineering Reference Platform"; + compatible = "nvidia,p3971-0089+p3834-0008", "nvidia,p3834-0008", "nvidia,tegra264"; +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+p3834.dtsi new file mode 100644 index 000000000000..46cfa8f1da1c --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089+p3834.dtsi @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +#include "tegra264-p3971-0089.dtsi" + +/ { + 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-p3971-0089.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089.dtsi new file mode 100644 index 000000000000..e8576cf2a0b6 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3971-0089.dtsi @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +#include "tegra264-p3971.dtsi" diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3971.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3971.dtsi new file mode 100644 index 000000000000..6b6259b7310f --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3971.dtsi @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +/ { +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi new file mode 100644 index 000000000000..e02659efa233 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +#include +#include +#include +#include +#include + +/ { + compatible = "nvidia,tegra264"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + shmem_bpmp: shmem@86070000 { + compatible = "nvidia,tegra264-bpmp-shmem"; + reg = <0x0 0x86070000 0x0 0x2000>; + no-map; + }; + }; + + /* SYSTEM MMIO */ + bus@0 { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <2>; + + ranges = <0x00 0x00000000 0x00 0x00000000 0x01 0x00000000>; + + misc@100000 { + compatible = "nvidia,tegra234-misc"; + reg = <0x0 0x00100000 0x0 0x0f000>, + <0x0 0x0c140000 0x0 0x10000>; + }; + + timer@8000000 { + compatible = "nvidia,tegra234-timer"; + reg = <0x0 0x08000000 0x0 0x140000>; + interrupts = , + , + , + ; + status = "disabled"; + }; + + gpcdma: dma-controller@8400000 { + compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma"; + reg = <0x0 0x08400000 0x0 0x210000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + #dma-cells = <1>; + iommus = <&smmu1 0x00000800>; + dma-coherent; + dma-channel-mask = <0xfffffffe>; + status = "disabled"; + }; + + hsp_top: hsp@8800000 { + compatible = "nvidia,tegra264-hsp"; + reg = <0x0 0x08800000 0x0 0xd0000>; + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "doorbell", "shared0", "shared1", "shared2", + "shared3", "shared4", "shared5", "shared6", + "shared7"; + #mbox-cells = <2>; + }; + + rtc: rtc@c2c0000 { + compatible = "nvidia,tegra264-rtc", "nvidia,tegra20-rtc"; + reg = <0x0 0x0c2c0000 0x0 0x10000>; + interrupt-parent = <&pmc>; + interrupts = <65 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&bpmp TEGRA264_CLK_CLK_S>; + clock-names = "rtc"; + status = "disabled"; + }; + + serial@c4e0000 { + compatible = "nvidia,tegra264-utc"; + reg = <0x0 0x0c4e0000 0x0 0x8000>, + <0x0 0x0c4e8000 0x0 0x8000>; + reg-names = "tx", "rx"; + interrupts = ; + rx-threshold = <4>; + tx-threshold = <4>; + status = "disabled"; + }; + + serial@c5a0000 { + compatible = "nvidia,tegra264-utc"; + reg = <0x0 0x0c5a0000 0x0 0x8000>, + <0x0 0x0c5a8000 0x0 0x8000>; + reg-names = "tx", "rx"; + interrupts = ; + rx-threshold = <4>; + tx-threshold = <4>; + status = "disabled"; + }; + + uart0: serial@c5f0000 { + compatible = "arm,sbsa-uart"; + reg = <0x0 0x0c5f0000 0x0 0x10000>; + interrupts = ; + status = "disabled"; + }; + + pmc: pmc@c800000 { + compatible = "nvidia,tegra264-pmc"; + reg = <0x0 0x0c800000 0x0 0x100000>, + <0x0 0x0c990000 0x0 0x10000>, + <0x0 0x0ca00000 0x0 0x10000>, + <0x0 0x0c980000 0x0 0x10000>, + <0x0 0x0c9c0000 0x0 0x40000>; + reg-names = "pmc", "wake", "aotag", "scratch", "misc"; + #interrupt-cells = <2>; + interrupt-controller; + }; + }; + + /* TOP_MMIO */ + bus@8100000000 { + compatible = "simple-bus"; + + #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 */ + + smmu1: iommu@5000000 { + compatible = "arm,smmu-v3"; + reg = <0x00 0x5000000 0x0 0x200000>; + interrupts = , + ; + interrupt-names = "eventq", "gerror"; + status = "disabled"; + + #iommu-cells = <1>; + dma-coherent; + }; + + smmu2: iommu@6000000 { + compatible = "arm,smmu-v3"; + reg = <0x00 0x6000000 0x0 0x200000>; + interrupts = , + ; + interrupt-names = "eventq", "gerror"; + status = "disabled"; + + #iommu-cells = <1>; + dma-coherent; + }; + + 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-names = "broadcast", "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", + "ch10", "ch11", "ch12", "ch13", "ch14", + "ch15"; + interrupts = , + , + , + , + , + , + , + ; + #interconnect-cells = <1>; + + #address-cells = <2>; + #size-cells = <2>; + + /* limit the DMA range for memory clients to [39:0] */ + dma-ranges = <0x0 0x0 0x0 0x0 0x100 0x0>; + + emc: external-memory-controller@8800000 { + compatible = "nvidia,tegra264-emc"; + reg = <0x00 0x8800000 0x0 0x20000>, + <0x00 0x8890000 0x0 0x20000>; + interrupts = ; + clocks = <&bpmp TEGRA264_CLK_EMC>; + clock-names = "emc"; + + #interconnect-cells = <0>; + nvidia,bpmp = <&bpmp>; + }; + }; + + smmu0: iommu@a000000 { + compatible = "arm,smmu-v3"; + reg = <0x00 0xa000000 0x0 0x200000>; + interrupts = , + ; + interrupt-names = "eventq", "gerror"; + status = "disabled"; + + #iommu-cells = <1>; + dma-coherent; + }; + + smmu4: iommu@b000000 { + compatible = "arm,smmu-v3"; + reg = <0x00 0xb000000 0x0 0x200000>; + interrupts = , + ; + interrupt-names = "eventq", "gerror"; + status = "disabled"; + + #iommu-cells = <1>; + dma-coherent; + }; + + gic: interrupt-controller@46000000 { + compatible = "arm,gic-v3"; + reg = <0x00 0x46000000 0x0 0x010000>, /* GICD */ + <0x00 0x46080000 0x0 0x400000>; /* GICR */ + interrupt-parent = <&gic>; + interrupts = ; + + redistributor-stride = <0x0 0x40000>; + #redistributor-regions = <1>; + #interrupt-cells = <3>; + interrupt-controller; + + #address-cells = <2>; + #size-cells = <2>; + + ranges = <0x0 0x0 0x00 0x46000000 0x0 0x1000000>; + + its: msi-controller@40000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x40000 0x0 0x40000>; + #msi-cells = <1>; + msi-controller; + }; + }; + }; + + /* DISP_USB MMIO */ + bus@8800000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + + ranges = <0x00 0x00000000 0x88 0x00000000 0x01 0x00000000>; + + smmu3: iommu@6000000 { + compatible = "arm,smmu-v3"; + reg = <0x00 0x6000000 0x0 0x200000>; + interrupts = , + ; + interrupt-names = "eventq", "gerror"; + status = "disabled"; + + #iommu-cells = <1>; + dma-coherent; + }; + }; + + /* UPHY MMIO */ + bus@a800000000 { + compatible = "simple-bus"; + #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) */ + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,armv8"; + device_type = "cpu"; + reg = <0x00000>; + status = "okay"; + + enable-method = "psci"; + + i-cache-size = <65536>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <65536>; + d-cache-line-size = <64>; + d-cache-sets = <256>; + }; + + cpu1: cpu@1 { + compatible = "arm,armv8"; + device_type = "cpu"; + reg = <0x10000>; + status = "okay"; + + enable-method = "psci"; + + i-cache-size = <65536>; + i-cache-line-size = <64>; + i-cache-sets = <256>; + d-cache-size = <65536>; + d-cache-line-size = <64>; + d-cache-sets = <256>; + }; + }; + + bpmp: bpmp { + compatible = "nvidia,tegra264-bpmp", "nvidia,tegra186-bpmp"; + mboxes = <&hsp_top TEGRA_HSP_MBOX_TYPE_DB + TEGRA_HSP_DB_MASTER_BPMP>; + memory-region = <&shmem_bpmp>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + + i2c { + compatible = "nvidia,tegra186-bpmp-i2c"; + nvidia,bpmp-bus-id = <5>; + #address-cells = <1>; + #size-cells = <0>; + }; + + thermal { + compatible = "nvidia,tegra186-bpmp-thermal"; + #thermal-sensor-cells = <1>; + }; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = ; + status = "okay"; + }; + + psci { + compatible = "arm,psci-1.0"; + status = "okay"; + method = "smc"; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + , + ; + status = "okay"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 669b888b27a1..4bfa926b6a08 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -1,12 +1,13 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc.dtb -apq8016-sbc-usb-host-dtbs := apq8016-sbc.dtb apq8016-sbc-usb-host.dtbo +apq8016-sbc-d3-camera-mezzanine-dtbs := apq8016-sbc.dtb apq8016-sbc-d3-camera-mezzanine.dtbo +apq8016-sbc-usb-host-dtbs := apq8016-sbc.dtb apq8016-sbc-usb-host.dtbo dtb-$(CONFIG_ARCH_QCOM) += sar2130p-qar2130p.dtb -dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc-usb-host.dtb dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc-d3-camera-mezzanine.dtb +dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc-usb-host.dtb 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 @@ -77,6 +78,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8953-xiaomi-tissot.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8953-xiaomi-vince.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8956-sony-xperia-loire-kugo.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8956-sony-xperia-loire-suzu.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8976-longcheer-l9360.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8992-lg-bullhead-rev-10.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8992-lg-bullhead-rev-101.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8992-lg-h815.dtb @@ -309,6 +311,8 @@ x1e78100-lenovo-thinkpad-t14s-oled-el2-dtbs := x1e78100-lenovo-thinkpad-t14s-ole dtb-$(CONFIG_ARCH_QCOM) += x1e78100-lenovo-thinkpad-t14s-oled.dtb x1e78100-lenovo-thinkpad-t14s-oled-el2.dtb x1e80100-asus-vivobook-s15-el2-dtbs := x1e80100-asus-vivobook-s15.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-asus-vivobook-s15.dtb x1e80100-asus-vivobook-s15-el2.dtb +x1e80100-asus-zenbook-a14-el2-dtbs := x1e80100-asus-zenbook-a14.dtb x1-el2.dtbo +dtb-$(CONFIG_ARCH_QCOM) += x1e80100-asus-zenbook-a14.dtb x1e80100-asus-zenbook-a14-el2.dtb x1e80100-crd-el2-dtbs := x1e80100-crd.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-crd.dtb x1e80100-crd-el2.dtb x1e80100-dell-xps13-9345-el2-dtbs := x1e80100-dell-xps13-9345.dtb x1-el2.dtbo @@ -325,5 +329,7 @@ x1e80100-microsoft-romulus15-el2-dtbs := x1e80100-microsoft-romulus15.dtb x1-el2 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-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-crd-el2-dtbs := x1p42100-crd.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1p42100-crd.dtb x1p42100-crd-el2.dtb diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dts b/arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dtso similarity index 89% rename from arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dts rename to arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dtso index f9cbf8c1d689..d739ece6b44f 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dts +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc-d3-camera-mezzanine.dtso @@ -5,10 +5,12 @@ */ /dts-v1/; +/plugin/; -#include "apq8016-sbc.dts" +#include +#include -/ { +&{/} { camera_vdddo_1v8: regulator-camera-vdddo { compatible = "regulator-fixed"; regulator-name = "camera_vdddo"; @@ -38,6 +40,9 @@ &camss { status = "okay"; ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; csiphy0_ep: endpoint { @@ -53,6 +58,9 @@ &cci { }; &cci_i2c0 { + #address-cells = <1>; + #size-cells = <0>; + camera@3b { compatible = "ovti,ov5640"; reg = <0x3b>; diff --git a/arch/arm64/boot/dts/qcom/ipq6018.dtsi b/arch/arm64/boot/dts/qcom/ipq6018.dtsi index 7f0faf26b707..bfe59b020841 100644 --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi @@ -885,8 +885,24 @@ pcie0: pcie@20000000 { ranges = <0x81000000 0x0 0x00000000 0x0 0x20200000 0x0 0x10000>, <0x82000000 0x0 0x20220000 0x0 0x20220000 0x0 0xfde0000>; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi index 78e1992b7495..fffb47ec2448 100644 --- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi @@ -847,8 +847,24 @@ pcie1: pcie@10000000 { ranges = <0x81000000 0x0 0x00000000 0x10200000 0x0 0x10000>, /* I/O */ <0x82000000 0x0 0x10220000 0x10220000 0x0 0xfde0000>; /* MEM */ - interrupts = ; - interrupt-names = "msi"; + 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 142 @@ -919,8 +935,24 @@ pcie0: pcie@20000000 { ranges = <0x81000000 0x0 0x00000000 0x20200000 0x0 0x10000>, /* I/O */ <0x82000000 0x0 0x20220000 0x20220000 0x0 0xfde0000>; /* MEM */ - interrupts = ; - interrupt-names = "msi"; + 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 75 diff --git a/arch/arm64/boot/dts/qcom/msm8976-longcheer-l9360.dts b/arch/arm64/boot/dts/qcom/msm8976-longcheer-l9360.dts new file mode 100644 index 000000000000..e524d58cf0a4 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8976-longcheer-l9360.dts @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, André Apitzsch + */ + +/dts-v1/; + +#include + +#include "msm8976.dtsi" +#include "pm8004.dtsi" +#include "pm8950.dtsi" + +/ { + model = "BQ Aquaris X5 Plus (Longcheer L9360)"; + compatible = "longcheer,l9360", "qcom,msm8976"; + chassis-type = "handset"; + + aliases { + mmc0 = &sdhc_1; /* SDC1 eMMC slot */ + mmc1 = &sdhc_2; /* SDC2 SD card slot */ + }; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + framebuffer0: framebuffer@83200000 { + compatible = "simple-framebuffer"; + reg = <0x0 0x83200000 0x0 (1080 * 1920 * 3)>; + width = <1080>; + height = <1920>; + stride = <(1080 * 3)>; + format = "r8g8b8"; + + power-domains = <&gcc MDSS_GDSC>; + + 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>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&hall_sensor_default>, <&volume_up_default>; + pinctrl-names = "default"; + + event-hall-sensor { + label = "Hall Effect Sensor"; + gpios = <&tlmm 107 GPIO_ACTIVE_HIGH>; + linux,input-type = ; + linux,code = ; + linux,can-disable; + wakeup-source; + }; + + key-volume-up { + label = "Volume Up"; + gpios = <&tlmm 113 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + gpios = <&tlmm 101 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "off"; + function = LED_FUNCTION_KBD_BACKLIGHT; + + pinctrl-0 = <&button_backlight_default>; + pinctrl-names = "default"; + }; + }; + + reg_ts_vdd: regulator-vdd-ts { + compatible = "regulator-fixed"; + regulator-name = "regulator-vdd-ts"; + + gpio = <&tlmm 33 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reserved-memory { + framebuffer@83000000 { + reg = <0x0 0x83000000 0x0 0x2800000>; + no-map; + }; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + regulator-name = "vph-pwr"; + regulator-always-on; + regulator-boot-on; + }; +}; + +&blsp1_i2c2 { + status = "okay"; + + led-controller@30 { + compatible = "kinetic,ktd2026"; + reg = <0x30>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + }; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; + }; +}; + +&blsp1_i2c4 { + status = "okay"; + + nfc@28 { + compatible = "nxp,pn547", "nxp,nxp-nci-i2c"; + reg = <0x28>; + + interrupts-extended = <&tlmm 140 IRQ_TYPE_EDGE_RISING>; + + enable-gpios = <&tlmm 122 GPIO_ACTIVE_HIGH>; + firmware-gpios = <&tlmm 109 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&nfc_default>; + pinctrl-1 = <&nfc_sleep>; + pinctrl-names = "default", "sleep"; + }; +}; + +&blsp2_i2c2 { + status = "okay"; + + touchscreen@20 { + reg = <0x20>; + compatible = "syna,rmi4-i2c"; + + interrupts-extended = <&tlmm 65 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-0 = <&ts_int_default>, <&ts_reset_default>; + pinctrl-1 = <&ts_int_sleep>, <&ts_reset_sleep>; + pinctrl-names = "default", "sleep"; + + vdd-supply = <&pm8950_l6>; + vio-supply = <®_ts_vdd>; + + reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; + + syna,reset-delay-ms = <200>; + syna,startup-delay-ms = <200>; + + #address-cells = <1>; + #size-cells = <0>; + + rmi4-f01@1 { + reg = <0x1>; + syna,nosleep-mode = <1>; + }; + + rmi4-f12@12 { + reg = <0x12>; + syna,sensor-type = <1>; + }; + }; +}; + +&blsp2_uart2 { + status = "okay"; +}; + +&gcc { + vdd_gfx-supply = <&pm8004_s5>; +}; + +&pm8004_spmi_regulators { + vdd_s2-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + + /* Cluster 1 supply */ + pm8004_s2: s2 { + /* regulator-min-microvolt = <500000>; */ + /* Set .95V to prevent unstabilities until CPR for this SoC is done */ + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1165000>; + regulator-name = "vdd_apc1"; + /* Set always on until the CPU PLL is done */ + regulator-always-on; + regulator-boot-on; + }; + + pm8004_s5: s5 { + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1165000>; + regulator-enable-ramp-delay = <500>; + regulator-name = "vdd_gfx"; + /* Hack this on until the gpu driver is ready for it */ + regulator-always-on; + }; +}; + +&pm8950_resin { + linux,code = ; + status = "okay"; +}; + +&pm8950_spmi_regulators { + vdd_s5-supply = <&vph_pwr>; + + /* Cluster 0 supply */ + pm8950_spmi_s5: s5 { + /* Set .95V to prevent unstabilities until CPR for this SoC is done */ + /* regulator-min-microvolt = <500000>; */ + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1165000>; + regulator-name = "vdd_apc0"; + /* Set always on until the CPU PLL is done */ + regulator-always-on; + regulator-boot-on; + }; +}; + +&rpm_requests { + pm8950_regulators: regulators { + compatible = "qcom,rpm-pm8950-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + vdd_l1_l19-supply = <&pm8950_s3>; + vdd_l2_l23-supply = <&pm8950_s3>; + vdd_l3-supply = <&pm8950_s3>; + vdd_l5_l6_l7_l16-supply = <&pm8950_s4>; + vdd_l8_l11_l12_l17_l22-supply = <&vph_pwr>; + + pm8950_s1: s1 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1162500>; + }; + + pm8950_s3: s3 { + regulator-min-microvolt = <1325000>; + regulator-max-microvolt = <1325000>; + }; + + pm8950_s4: s4 { + regulator-min-microvolt = <2050000>; + regulator-max-microvolt = <2050000>; + }; + + pm8950_l1: l1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + pm8950_l2: l2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + pm8950_l3: l3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1100000>; + }; + + pm8950_l5: l5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8950_l6: l6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8950_l7: l7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8950_l8: l8 { + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + }; + + pm8950_l9: l9 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + }; + + pm8950_l10: l10 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + pm8950_l11: l11 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + }; + + pm8950_l12: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + }; + + pm8950_l13: l13 { + regulator-min-microvolt = <3075000>; + regulator-max-microvolt = <3075000>; + }; + + pm8950_l14: l14 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + pm8950_l15: l15 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + pm8950_l16: l16 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8950_l17: l17 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + }; + + pm8950_l19: l19 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1350000>; + }; + + pm8950_l22: l22 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + pm8950_l23: l23 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + }; +}; + +&sdhc_1 { + bus-width = <8>; + non-removable; + vmmc-supply = <&pm8950_l8>; + vqmmc-supply = <&pm8950_l5>; + status = "okay"; +}; + +&sdhc_2 { + bus-width = <4>; + cd-gpios = <&tlmm 100 GPIO_ACTIVE_LOW>; + vmmc-supply = <&pm8950_l11>; + vqmmc-supply = <&pm8950_l12>; + + pinctrl-0 = <&sdc2_default>, <&sdc2_cd_default>; + pinctrl-1 = <&sdc2_sleep>, <&sdc2_cd_sleep>; + pinctrl-names = "default", "sleep"; + + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <0 4>; + + button_backlight_default: button-backlight-default-state { + pins = "gpio101"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + hall_sensor_default: hall-sensor-default-state { + pins = "gpio107"; + function = "gpio"; + drive-strength = <6>; + bias-pull-up; + }; + + nfc_default: nfc-default-state { + pins = "gpio122", "gpio140"; + function = "gpio"; + drive-strength = <6>; + bias-pull-up; + }; + + nfc_sleep: nfc-sleep-state { + int-pins { + pins = "gpio140"; + function = "gpio"; + drive-strength = <6>; + bias-pull-up; + }; + ven-pins { + pins = "gpio122"; + function = "gpio"; + drive-strength = <6>; + bias-disable; + }; + }; + + sdc2_cd_default: sdc2-cd-default-state { + pins = "gpio100"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + sdc2_cd_sleep: sdc2-cd-sleep-state { + pins = "gpio100"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + ts_int_default: ts-int-state { + pins = "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + ts_int_sleep: ts-int-state { + pins = "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + ts_reset_default: ts-reset-state { + pins = "gpio64"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + ts_reset_sleep: ts-sleep-state { + pins = "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + volume_up_default: volume-up-default-state { + pins = "gpio113"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; +}; + +&xo_board { + clock-frequency = <19200000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8976.dtsi b/arch/arm64/boot/dts/qcom/msm8976.dtsi index e2ac2fd6882f..f9962512f243 100644 --- a/arch/arm64/boot/dts/qcom/msm8976.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8976.dtsi @@ -782,6 +782,42 @@ blsp2_i2c4_sleep: blsp2-i2c4-sleep-state { bias-disable; }; + 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>; + }; + }; + + 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>; + }; + }; + wcss_wlan_default: wcss-wlan-default-state { wcss-wlan2-pins { pins = "gpio40"; @@ -1331,6 +1367,7 @@ blsp1_dma: dma-controller@7884000 { clock-names = "bam_clk"; #dma-cells = <1>; qcom,ee = <0>; + qcom,controlled-remotely; }; blsp1_uart1: serial@78af000 { @@ -1451,6 +1488,7 @@ blsp2_dma: dma-controller@7ac4000 { clock-names = "bam_clk"; #dma-cells = <1>; qcom,ee = <0>; + qcom,controlled-remotely; }; blsp2_uart2: serial@7af0000 { diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index ede851fbf628..f91605de4909 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -1910,8 +1910,22 @@ pcie0: pcie@600000 { device_type = "pci"; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1973,8 +1987,22 @@ pcie1: pcie@608000 { device_type = "pci"; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 272 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -2034,8 +2062,22 @@ pcie2: pcie@610000 { device_type = "pci"; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 58cee37cb8ee..0b0a9379cb05 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -936,8 +936,24 @@ pcie0: pcie@1c00000 { <0x02000000 0x0 0x1b300000 0x1b300000 0x0 0xd00000>; #interrupt-cells = <1>; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 0 135 IRQ_TYPE_LEVEL_HIGH>, <0 0 0 2 &intc 0 0 136 IRQ_TYPE_LEVEL_HIGH>, diff --git a/arch/arm64/boot/dts/qcom/qcm2290.dtsi b/arch/arm64/boot/dts/qcom/qcm2290.dtsi index f49ac1c1f8a3..fa24b77a31a7 100644 --- a/arch/arm64/boot/dts/qcom/qcm2290.dtsi +++ b/arch/arm64/boot/dts/qcom/qcm2290.dtsi @@ -1628,6 +1628,109 @@ adreno_smmu: iommu@59a0000 { #iommu-cells = <2>; }; + camss: camss@5c6e000 { + compatible = "qcom,qcm2290-camss"; + + reg = <0x0 0x5c6e000 0x0 0x1000>, + <0x0 0x5c75000 0x0 0x1000>, + <0x0 0x5c52000 0x0 0x1000>, + <0x0 0x5c53000 0x0 0x1000>, + <0x0 0x5c66000 0x0 0x400>, + <0x0 0x5c68000 0x0 0x400>, + <0x0 0x5c11000 0x0 0x1000>, + <0x0 0x5c6f000 0x0 0x4000>, + <0x0 0x5c76000 0x0 0x4000>; + reg-names = "csid0", + "csid1", + "csiphy0", + "csiphy1", + "csitpg0", + "csitpg1", + "top", + "vfe0", + "vfe1"; + + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&gcc GCC_CAMSS_AXI_CLK>, + <&gcc GCC_CAMSS_NRT_AXI_CLK>, + <&gcc GCC_CAMSS_RT_AXI_CLK>, + <&gcc GCC_CAMSS_TFE_0_CSID_CLK>, + <&gcc GCC_CAMSS_TFE_1_CSID_CLK>, + <&gcc GCC_CAMSS_CPHY_0_CLK>, + <&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>, + <&gcc GCC_CAMSS_CPHY_1_CLK>, + <&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>, + <&gcc GCC_CAMSS_TOP_AHB_CLK>, + <&gcc GCC_CAMSS_TFE_0_CLK>, + <&gcc GCC_CAMSS_TFE_0_CPHY_RX_CLK>, + <&gcc GCC_CAMSS_TFE_1_CLK>, + <&gcc GCC_CAMSS_TFE_1_CPHY_RX_CLK> ; + clock-names = "ahb", + "axi", + "camnoc_nrt_axi", + "camnoc_rt_axi", + "csi0", + "csi1", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "top_ahb", + "vfe0", + "vfe0_cphy_rx", + "vfe1", + "vfe1_cphy_rx"; + + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csiphy0", + "csiphy1", + "csitpg0", + "csitpg1", + "vfe0", + "vfe1"; + + interconnects = <&bimc MASTER_APPSS_PROC RPM_ACTIVE_TAG + &config_noc SLAVE_CAMERA_CFG RPM_ACTIVE_TAG>, + <&mmrt_virt MASTER_CAMNOC_HF RPM_ALWAYS_TAG + &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>, + <&mmnrt_virt MASTER_CAMNOC_SF RPM_ALWAYS_TAG + &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>; + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc"; + + iommus = <&apps_smmu 0x400 0x0>, + <&apps_smmu 0x800 0x0>, + <&apps_smmu 0x820 0x0>, + <&apps_smmu 0x840 0x0>; + + power-domains = <&gcc GCC_CAMSS_TOP_GDSC>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + }; + }; + mdss: display-subsystem@5e00000 { compatible = "qcom,qcm2290-mdss"; reg = <0x0 0x05e00000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/qcom/qcs615-ride.dts b/arch/arm64/boot/dts/qcom/qcs615-ride.dts index 2b5aa3c66867..a6652e4817d1 100644 --- a/arch/arm64/boot/dts/qcom/qcs615-ride.dts +++ b/arch/arm64/boot/dts/qcom/qcs615-ride.dts @@ -240,6 +240,18 @@ &qupv3_id_0 { status = "okay"; }; +&remoteproc_adsp { + firmware-name = "qcom/qcs615/adsp.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/qcs615/cdsp.mbn"; + + status = "okay"; +}; + &rpmhcc { clocks = <&xo_board_clk>; }; diff --git a/arch/arm64/boot/dts/qcom/qcs615.dtsi b/arch/arm64/boot/dts/qcom/qcs615.dtsi index bb8b6c3ebd03..bfbb21035492 100644 --- a/arch/arm64/boot/dts/qcom/qcs615.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs615.dtsi @@ -332,6 +332,50 @@ mc_virt: interconnect-2 { qcom,bcm-voters = <&apps_bcm_voter>; }; + smp2p-adsp { + compatible = "qcom,smp2p"; + qcom,smem = <443>, <429>; + interrupts = ; + /* On this platform, bit 26 (normally SLPI) is repurposed for ADSP */ + mboxes = <&apss_shared 26>; + + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + adsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + adsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + smp2p-cdsp { + compatible = "qcom,smp2p"; + qcom,smem = <94>, <432>; + interrupts = ; + mboxes = <&apss_shared 6>; + + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + cdsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + cdsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + }; + qup_opp_table: opp-table-qup { compatible = "operating-points-v2"; opp-shared; @@ -429,6 +473,16 @@ smem_region: smem@86000000 { no-map; hwlocks = <&tcsr_mutex 3>; }; + + rproc_cdsp_mem: rproc-cdsp@93b00000 { + reg = <0x0 0x93b00000 0x0 0x1e00000>; + no-map; + }; + + rproc_adsp_mem: rproc-adsp@95900000 { + reg = <0x0 0x95900000 0x0 0x1e00000>; + no-map; + }; }; soc: soc@0 { @@ -1902,6 +1956,7 @@ replicator@604a000 { clocks = <&aoss_qmp>; clock-names = "apb_pclk"; + status = "disabled"; in-ports { port { @@ -2461,6 +2516,9 @@ cti@6c13000 { clocks = <&aoss_qmp>; clock-names = "apb_pclk"; + + /* Not all required clocks can be enabled from the OS */ + status = "fail"; }; cti@6c20000 { @@ -3073,6 +3131,44 @@ cti@7900000 { clock-names = "apb_pclk"; }; + remoteproc_cdsp: remoteproc@8300000 { + compatible = "qcom,qcs615-cdsp-pas", "qcom,sm8150-cdsp-pas"; + reg = <0x0 0x08300000 0x0 0x4040>; + + interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack"; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + power-domains = <&rpmhpd RPMHPD_CX>; + power-domain-names = "cx"; + + memory-region = <&rproc_cdsp_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&cdsp_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + status = "disabled"; + + glink-edge { + interrupts = ; + mboxes = <&apss_shared 4>; + label = "cdsp"; + qcom,remote-pid = <5>; + }; + }; + pmu@90b6300 { compatible = "qcom,qcs615-cpu-bwmon", "qcom,sdm845-bwmon"; reg = <0x0 0x090b6300 0x0 0x600>; @@ -3245,6 +3341,20 @@ sram@c3f0000 { reg = <0x0 0x0c3f0000 0x0 0x400>; }; + sram@14680000 { + compatible = "qcom,qcs615-imem", "syscon", "simple-mfd"; + reg = <0x0 0x14680000 0x0 0x2c000>; + ranges = <0 0 0x14680000 0x2c000>; + + #address-cells = <1>; + #size-cells = <1>; + + pil-reloc@2a94c { + compatible = "qcom,pil-reloc-info"; + reg = <0x2a94c 0xc8>; + }; + }; + apps_smmu: iommu@15000000 { compatible = "qcom,qcs615-smmu-500", "qcom,smmu-500", "arm,mmu-500"; reg = <0x0 0x15000000 0x0 0x80000>; @@ -3692,6 +3802,44 @@ usb_2_dwc3: usb@a800000 { maximum-speed = "high-speed"; }; }; + + remoteproc_adsp: remoteproc@62400000 { + compatible = "qcom,qcs615-adsp-pas", "qcom,sm8150-adsp-pas"; + reg = <0x0 0x62400000 0x0 0x4040>; + + interrupts-extended = <&intc GIC_SPI 162 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack"; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + power-domains = <&rpmhpd RPMHPD_CX>; + power-domain-names = "cx"; + + memory-region = <&rproc_adsp_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&adsp_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + status = "disabled"; + + glink_edge: glink-edge { + interrupts = ; + mboxes = <&apss_shared 24>; + label = "lpass"; + qcom,remote-pid = <2>; + }; + }; }; arch_timer: timer { diff --git a/arch/arm64/boot/dts/qcom/qcs8300-ride.dts b/arch/arm64/boot/dts/qcom/qcs8300-ride.dts index 3ff8f398cad3..8c166ead912c 100644 --- a/arch/arm64/boot/dts/qcom/qcs8300-ride.dts +++ b/arch/arm64/boot/dts/qcom/qcs8300-ride.dts @@ -304,6 +304,10 @@ usb2_en: usb2-en-state { }; }; +&iris { + status = "okay"; +}; + &qupv3_id_0 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/qcs8300.dtsi b/arch/arm64/boot/dts/qcom/qcs8300.dtsi index 009f9658a4fa..7ada029c32c1 100644 --- a/arch/arm64/boot/dts/qcom/qcs8300.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs8300.dtsi @@ -4211,6 +4211,77 @@ usb_2_dwc3: usb@a400000 { }; }; + iris: video-codec@aa00000 { + compatible = "qcom,qcs8300-iris"; + + reg = <0x0 0x0aa00000 0x0 0xf0000>; + interrupts = ; + + power-domains = <&videocc VIDEO_CC_MVS0C_GDSC>, + <&videocc VIDEO_CC_MVS0_GDSC>, + <&rpmhpd RPMHPD_MX>, + <&rpmhpd RPMHPD_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mxc", + "mmcx"; + + operating-points-v2 = <&iris_opp_table>; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem"; + + memory-region = <&video_mem>; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>; + reset-names = "bus"; + + iommus = <&apps_smmu 0x0880 0x0400>, + <&apps_smmu 0x0887 0x0400>; + dma-coherent; + + status = "disabled"; + + iris_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-366000000 { + opp-hz = /bits/ 64 <366000000>; + required-opps = <&rpmhpd_opp_svs_l1>, + <&rpmhpd_opp_svs_l1>; + }; + + opp-444000000 { + opp-hz = /bits/ 64 <444000000>; + required-opps = <&rpmhpd_opp_nom>, + <&rpmhpd_opp_nom>; + }; + + opp-533000000 { + opp-hz = /bits/ 64 <533000000>; + required-opps = <&rpmhpd_opp_turbo>, + <&rpmhpd_opp_turbo>; + }; + + opp-560000000 { + opp-hz = /bits/ 64 <560000000>; + required-opps = <&rpmhpd_opp_turbo_l1>, + <&rpmhpd_opp_turbo_l1>; + }; + }; + }; + videocc: clock-controller@abf0000 { compatible = "qcom,qcs8300-videocc"; reg = <0x0 0x0abf0000 0x0 0x10000>; diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5-vision-mezzanine.dtso b/arch/arm64/boot/dts/qcom/qrb5165-rb5-vision-mezzanine.dtso index 5fe331923dd3..771baf7e09e6 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5-vision-mezzanine.dtso +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5-vision-mezzanine.dtso @@ -9,10 +9,6 @@ #include #include -&camcc { - status = "okay"; -}; - &camss { vdda-phy-supply = <&vreg_l5a_0p88>; vdda-pll-supply = <&vreg_l9a_1p2>; diff --git a/arch/arm64/boot/dts/qcom/sa8775p-ride.dtsi b/arch/arm64/boot/dts/qcom/sa8775p-ride.dtsi index 3ae416ab66e8..63b3031cfcc1 100644 --- a/arch/arm64/boot/dts/qcom/sa8775p-ride.dtsi +++ b/arch/arm64/boot/dts/qcom/sa8775p-ride.dtsi @@ -28,6 +28,64 @@ chosen { 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"; + + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + vin-supply = <&vreg_5p0>; + }; + + vreg_1p0: vreg-1p0-regulator { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P0"; + + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + + vin-supply = <&vreg_1p8>; + }; + + vreg_3p0: vreg-3p0-regulator { + compatible = "regulator-fixed"; + regulator-name = "VREG_3P0"; + + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + + vin-supply = <&vreg_12p0>; + }; + vreg_conn_1p8: vreg_conn_1p8 { compatible = "regulator-fixed"; regulator-name = "vreg_conn_1p8"; @@ -128,6 +186,30 @@ dp1_connector_in: endpoint { }; }; }; + + dp-dsi0-connector { + compatible = "dp-connector"; + label = "DSI0"; + type = "full-size"; + + port { + dp_dsi0_connector_in: endpoint { + remote-endpoint = <&dsi2dp_bridge0_out>; + }; + }; + }; + + dp-dsi1-connector { + compatible = "dp-connector"; + label = "DSI1"; + type = "full-size"; + + port { + dp_dsi1_connector_in: endpoint { + remote-endpoint = <&dsi2dp_bridge1_out>; + }; + }; + }; }; &apps_rsc { @@ -513,6 +595,113 @@ &i2c11 { &i2c18 { clock-frequency = <400000>; + + status = "okay"; + + io_expander: gpio@74 { + compatible = "ti,tca9539"; + reg = <0x74>; + interrupts-extended = <&tlmm 98 IRQ_TYPE_EDGE_BOTH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&tlmm 97 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&io_expander_intr_active>, + <&io_expander_reset_active>; + pinctrl-names = "default"; + }; + + i2c-mux@70 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + + #size-cells = <0>; + reg = <0x70>; + + 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_bridge0_in: endpoint { + remote-endpoint = <&mdss0_dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + dsi2dp_bridge0_out: endpoint { + remote-endpoint = <&dp_dsi0_connector_in>; + }; + }; + }; + }; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + bridge@58 { + compatible = "analogix,anx7625"; + reg = <0x58>; + interrupts-extended = <&io_expander 10 IRQ_TYPE_EDGE_FALLING>; + enable-gpios = <&io_expander 9 GPIO_ACTIVE_HIGH>; + reset-gpios = <&io_expander 8 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_bridge1_in: endpoint { + remote-endpoint = <&mdss0_dsi1_out>; + }; + }; + + port@1 { + reg = <1>; + + dsi2dp_bridge1_out: endpoint { + remote-endpoint = <&dp_dsi1_connector_in>; + }; + }; + }; + }; + }; + }; + +}; + +&iris { + firmware-name = "qcom/vpu/vpu30_p4_s6.mbn"; + status = "okay"; }; @@ -560,6 +749,40 @@ &mdss0_dp1_phy { status = "okay"; }; +&mdss0_dsi0 { + vdda-supply = <&vreg_l1c>; + + status = "okay"; +}; + +&mdss0_dsi0_out { + data-lanes = <0 1 2 3>; + remote-endpoint = <&dsi2dp_bridge0_in>; +}; + +&mdss0_dsi0_phy { + vdds-supply = <&vreg_l4a>; + + status = "okay"; +}; + +&mdss0_dsi1 { + vdda-supply = <&vreg_l1c>; + + status = "okay"; +}; + +&mdss0_dsi1_out { + data-lanes = <0 1 2 3>; + remote-endpoint = <&dsi2dp_bridge1_in>; +}; + +&mdss0_dsi1_phy { + vdds-supply = <&vreg_l4a>; + + status = "okay"; +}; + &pmm8654au_0_gpios { gpio-line-names = "DS_EN", "POFF_COMPLETE", @@ -753,6 +976,21 @@ ethernet0_mdio: ethernet0-mdio-pins { }; }; + io_expander_intr_active: io-expander-intr-active-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + io_expander_reset_active: io-expander-reset-active-state { + pins = "gpio97"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-high; + }; + pcie0_default_state: pcie0-default-state { perst-pins { pins = "gpio2"; diff --git a/arch/arm64/boot/dts/qcom/sa8775p.dtsi b/arch/arm64/boot/dts/qcom/sa8775p.dtsi index 45f536633f64..fed34717460f 100644 --- a/arch/arm64/boot/dts/qcom/sa8775p.dtsi +++ b/arch/arm64/boot/dts/qcom/sa8775p.dtsi @@ -6,11 +6,14 @@ #include #include +#include #include #include #include #include +#include #include +#include #include #include #include @@ -51,6 +54,11 @@ cpu0: cpu@0 { next-level-cache = <&l2_0>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu0_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl0 MASTER_EPSS_L3_APPS + &epss_l3_cl0 SLAVE_EPSS_L3_SHARED>; l2_0: l2-cache { compatible = "cache"; cache-level = <2>; @@ -75,6 +83,11 @@ cpu1: cpu@100 { next-level-cache = <&l2_1>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu0_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl0 MASTER_EPSS_L3_APPS + &epss_l3_cl0 SLAVE_EPSS_L3_SHARED>; l2_1: l2-cache { compatible = "cache"; cache-level = <2>; @@ -94,6 +107,11 @@ cpu2: cpu@200 { next-level-cache = <&l2_2>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu0_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl0 MASTER_EPSS_L3_APPS + &epss_l3_cl0 SLAVE_EPSS_L3_SHARED>; l2_2: l2-cache { compatible = "cache"; cache-level = <2>; @@ -113,6 +131,11 @@ cpu3: cpu@300 { next-level-cache = <&l2_3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu0_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl0 MASTER_EPSS_L3_APPS + &epss_l3_cl0 SLAVE_EPSS_L3_SHARED>; l2_3: l2-cache { compatible = "cache"; cache-level = <2>; @@ -132,6 +155,11 @@ cpu4: cpu@10000 { next-level-cache = <&l2_4>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu4_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl1 MASTER_EPSS_L3_APPS + &epss_l3_cl1 SLAVE_EPSS_L3_SHARED>; l2_4: l2-cache { compatible = "cache"; cache-level = <2>; @@ -157,6 +185,11 @@ cpu5: cpu@10100 { next-level-cache = <&l2_5>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu4_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl1 MASTER_EPSS_L3_APPS + &epss_l3_cl1 SLAVE_EPSS_L3_SHARED>; l2_5: l2-cache { compatible = "cache"; cache-level = <2>; @@ -176,6 +209,11 @@ cpu6: cpu@10200 { next-level-cache = <&l2_6>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu4_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl1 MASTER_EPSS_L3_APPS + &epss_l3_cl1 SLAVE_EPSS_L3_SHARED>; l2_6: l2-cache { compatible = "cache"; cache-level = <2>; @@ -195,6 +233,11 @@ cpu7: cpu@10300 { next-level-cache = <&l2_7>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu4_opp_table>; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&epss_l3_cl1 MASTER_EPSS_L3_APPS + &epss_l3_cl1 SLAVE_EPSS_L3_SHARED>; l2_7: l2-cache { compatible = "cache"; cache-level = <2>; @@ -284,6 +327,176 @@ cluster_sleep_apss_rsc_pc: cluster-sleep-1 { }; }; + cpu0_opp_table: opp-table-cpu0 { + compatible = "operating-points-v2"; + opp-shared; + + opp-1267200000 { + opp-hz = /bits/ 64 <1267200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1363200000 { + opp-hz = /bits/ 64 <1363200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1459200000 { + opp-hz = /bits/ 64 <1459200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1536000000 { + opp-hz = /bits/ 64 <1536000000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1632000000 { + opp-hz = /bits/ 64 <1632000000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1708800000 { + opp-hz = /bits/ 64 <1708800000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1785600000 { + opp-hz = /bits/ 64 <1785600000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1862400000 { + opp-hz = /bits/ 64 <1862400000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1939200000 { + opp-hz = /bits/ 64 <1939200000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-2016000000 { + opp-hz = /bits/ 64 <2016000000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-2112000000 { + opp-hz = /bits/ 64 <2112000000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2188800000 { + opp-hz = /bits/ 64 <2188800000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2265600000 { + opp-hz = /bits/ 64 <2265600000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2361600000 { + opp-hz = /bits/ 64 <2361600000>; + opp-peak-kBps = <(3196800 * 4) (1612800 * 32)>; + }; + + opp-2457600000 { + opp-hz = /bits/ 64 <2457600000>; + opp-peak-kBps = <(3196800 * 4) (1612800 * 32)>; + }; + + opp-2553600000 { + opp-hz = /bits/ 64 <2553600000>; + opp-peak-kBps = <(3196800 * 4) (1708800 * 32)>; + }; + }; + + cpu4_opp_table: opp-table-cpu4 { + compatible = "operating-points-v2"; + opp-shared; + + opp-1267200000 { + opp-hz = /bits/ 64 <1267200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1363200000 { + opp-hz = /bits/ 64 <1363200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1459200000 { + opp-hz = /bits/ 64 <1459200000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1536000000 { + opp-hz = /bits/ 64 <1536000000>; + opp-peak-kBps = <(1555200 * 4) (921600 * 32)>; + }; + + opp-1632000000 { + opp-hz = /bits/ 64 <1632000000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1708800000 { + opp-hz = /bits/ 64 <1708800000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1785600000 { + opp-hz = /bits/ 64 <1785600000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1862400000 { + opp-hz = /bits/ 64 <1862400000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-1939200000 { + opp-hz = /bits/ 64 <1939200000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-2016000000 { + opp-hz = /bits/ 64 <2016000000>; + opp-peak-kBps = <(1708800 * 4) (1228800 * 32)>; + }; + + opp-2112000000 { + opp-hz = /bits/ 64 <2112000000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2188800000 { + opp-hz = /bits/ 64 <2188800000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2265600000 { + opp-hz = /bits/ 64 <2265600000>; + opp-peak-kBps = <(2092800 * 4) (1555200 * 32)>; + }; + + opp-2361600000 { + opp-hz = /bits/ 64 <2361600000>; + opp-peak-kBps = <(3196800 * 4) (1612800 * 32)>; + }; + + opp-2457600000 { + opp-hz = /bits/ 64 <2457600000>; + opp-peak-kBps = <(3196800 * 4) (1612800 * 32)>; + }; + + opp-2553600000 { + opp-hz = /bits/ 64 <2553600000>; + opp-peak-kBps = <(3196800 * 4) (1708800 * 32)>; + }; + }; + dummy-sink { compatible = "arm,coresight-dummy-sink"; @@ -4049,6 +4262,76 @@ llcc: system-cache-controller@9200000 { interrupts = ; }; + iris: video-codec@aa00000 { + compatible = "qcom,sa8775p-iris", "qcom,sm8550-iris"; + + reg = <0x0 0x0aa00000 0x0 0xf0000>; + interrupts = ; + + power-domains = <&videocc VIDEO_CC_MVS0C_GDSC>, + <&videocc VIDEO_CC_MVS0_GDSC>, + <&rpmhpd SA8775P_MX>, + <&rpmhpd SA8775P_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mxc", + "mmcx"; + operating-points-v2 = <&iris_opp_table>; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem"; + + memory-region = <&pil_video_mem>; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>; + reset-names = "bus"; + + iommus = <&apps_smmu 0x0880 0x0400>, + <&apps_smmu 0x0887 0x0400>; + dma-coherent; + + status = "disabled"; + + iris_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-366000000 { + opp-hz = /bits/ 64 <366000000>; + required-opps = <&rpmhpd_opp_svs_l1>, + <&rpmhpd_opp_svs_l1>; + }; + + opp-444000000 { + opp-hz = /bits/ 64 <444000000>; + required-opps = <&rpmhpd_opp_nom>, + <&rpmhpd_opp_nom>; + }; + + opp-533000000 { + opp-hz = /bits/ 64 <533000000>; + required-opps = <&rpmhpd_opp_turbo>, + <&rpmhpd_opp_turbo>; + }; + + opp-560000000 { + opp-hz = /bits/ 64 <560000000>; + required-opps = <&rpmhpd_opp_turbo_l1>, + <&rpmhpd_opp_turbo_l1>; + }; + }; + }; + videocc: clock-controller@abf0000 { compatible = "qcom,sa8775p-videocc"; reg = <0x0 0x0abf0000 0x0 0x10000>; @@ -4156,6 +4439,22 @@ dpu_intf4_out: endpoint { remote-endpoint = <&mdss0_dp1_in>; }; }; + + port@2 { + reg = <2>; + + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss0_dsi0_in>; + }; + }; + + port@3 { + reg = <3>; + + dpu_intf2_out: endpoint { + remote-endpoint = <&mdss0_dsi1_in>; + }; + }; }; mdss0_mdp_opp_table: opp-table { @@ -4183,6 +4482,161 @@ opp-650000000 { }; }; + mdss0_dsi0: dsi@ae94000 { + compatible = "qcom,sa8775p-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x0ae94000 0x0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss0>; + interrupts = <4>; + + clocks = <&dispcc0 MDSS_DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_ESC0_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc0 MDSS_DISP_CC_MDSS_PCLK0_CLK_SRC>; + assigned-clock-parents = <&mdss0_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi0_phy DSI_PIXEL_PLL_CLK>; + phys = <&mdss0_dsi0_phy>; + + operating-points-v2 = <&mdss_dsi_opp_table>; + power-domains = <&rpmhpd SA8775P_MMCX>; + + #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 = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss0_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>; + }; + }; + }; + + mdss0_dsi0_phy: phy@ae94400 { + compatible = "qcom,sa8775p-dsi-phy-5nm"; + reg = <0x0 0x0ae94400 0x0 0x200>, + <0x0 0x0ae94600 0x0 0x280>, + <0x0 0x0ae94900 0x0 0x27c>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + + mdss0_dsi1: dsi@ae96000 { + compatible = "qcom,sa8775p-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x0ae96000 0x0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss0>; + interrupts = <5>; + + clocks = <&dispcc0 MDSS_DISP_CC_MDSS_BYTE1_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_PCLK1_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_ESC1_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&dispcc0 MDSS_DISP_CC_MDSS_PCLK1_CLK_SRC>; + assigned-clock-parents = <&mdss0_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi1_phy DSI_PIXEL_PLL_CLK>; + phys = <&mdss0_dsi1_phy>; + + operating-points-v2 = <&mdss_dsi_opp_table>; + power-domains = <&rpmhpd SA8775P_MMCX>; + + #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 = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss0_dsi1_out: endpoint { }; + }; + }; + }; + + mdss0_dsi1_phy: phy@ae96400 { + compatible = "qcom,sa8775p-dsi-phy-5nm"; + reg = <0x0 0x0ae96400 0x0 0x200>, + <0x0 0x0ae96600 0x0 0x280>, + <0x0 0x0ae96900 0x0 0x27c>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + mdss0_dp0_phy: phy@aec2a00 { compatible = "qcom,sa8775p-edp-phy"; @@ -4389,7 +4843,10 @@ dispcc0: clock-controller@af00000 { <&sleep_clk>, <&mdss0_dp0_phy 0>, <&mdss0_dp0_phy 1>, <&mdss0_dp1_phy 0>, <&mdss0_dp1_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 SA8775P_MMCX>; #clock-cells = <1>; #reset-cells = <1>; @@ -5548,6 +6005,15 @@ rpmhpd_opp_turbo_l1: opp-9 { }; }; + epss_l3_cl0: interconnect@18590000 { + compatible = "qcom,sa8775p-epss-l3", + "qcom,epss-l3"; + reg = <0x0 0x18590000 0x0 0x1000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_GPLL0>; + clock-names = "xo", "alternate"; + #interconnect-cells = <1>; + }; + cpufreq_hw: cpufreq@18591000 { compatible = "qcom,sa8775p-cpufreq-epss", "qcom,cpufreq-epss"; @@ -5565,14 +6031,23 @@ cpufreq_hw: cpufreq@18591000 { #freq-domain-cells = <1>; }; + epss_l3_cl1: interconnect@18592000 { + compatible = "qcom,sa8775p-epss-l3", + "qcom,epss-l3"; + reg = <0x0 0x18592000 0x0 0x1000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_GPLL0>; + clock-names = "xo", "alternate"; + #interconnect-cells = <1>; + }; + remoteproc_gpdsp0: remoteproc@20c00000 { compatible = "qcom,sa8775p-gpdsp0-pas"; reg = <0x0 0x20c00000 0x0 0x10000>; interrupts-extended = <&intc GIC_SPI 768 IRQ_TYPE_EDGE_RISING>, <&smp2p_gpdsp0_in 0 0>, - <&smp2p_gpdsp0_in 2 0>, <&smp2p_gpdsp0_in 1 0>, + <&smp2p_gpdsp0_in 2 0>, <&smp2p_gpdsp0_in 3 0>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; @@ -5614,8 +6089,8 @@ remoteproc_gpdsp1: remoteproc@21c00000 { interrupts-extended = <&intc GIC_SPI 624 IRQ_TYPE_EDGE_RISING>, <&smp2p_gpdsp1_in 0 0>, - <&smp2p_gpdsp1_in 2 0>, <&smp2p_gpdsp1_in 1 0>, + <&smp2p_gpdsp1_in 2 0>, <&smp2p_gpdsp1_in 3 0>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; @@ -5755,8 +6230,8 @@ remoteproc_cdsp0: remoteproc@26300000 { interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp0_in 0 IRQ_TYPE_EDGE_RISING>, - <&smp2p_cdsp0_in 2 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp0_in 1 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp0_in 2 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp0_in 3 IRQ_TYPE_EDGE_RISING>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; @@ -5887,8 +6362,8 @@ remoteproc_cdsp1: remoteproc@2a300000 { interrupts-extended = <&intc GIC_SPI 798 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp1_in 0 IRQ_TYPE_EDGE_RISING>, - <&smp2p_cdsp1_in 2 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp1_in 1 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp1_in 2 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp1_in 3 IRQ_TYPE_EDGE_RISING>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; @@ -6043,8 +6518,8 @@ remoteproc_adsp: remoteproc@30000000 { interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>, <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>, - <&smp2p_adsp_in 2 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>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; @@ -7120,9 +7595,17 @@ pcie0: pcie@1c00000 { , , , - ; - interrupt-names = "msi0", "msi1", "msi2", "msi3", - "msi4", "msi5", "msi6", "msi7"; + , + ; + 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 GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH>, @@ -7278,9 +7761,17 @@ pcie1: pcie@1c10000 { , , , - ; - interrupt-names = "msi0", "msi1", "msi2", "msi3", - "msi4", "msi5", "msi6", "msi7"; + , + ; + 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 GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, diff --git a/arch/arm64/boot/dts/qcom/sar2130p.dtsi b/arch/arm64/boot/dts/qcom/sar2130p.dtsi index b0e342810ae7..e400ea4cdee8 100644 --- a/arch/arm64/boot/dts/qcom/sar2130p.dtsi +++ b/arch/arm64/boot/dts/qcom/sar2130p.dtsi @@ -1289,7 +1289,8 @@ pcie0: pcie@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1297,7 +1298,8 @@ pcie0: pcie@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 0 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1406,7 +1408,8 @@ pcie1: pcie@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1414,7 +1417,8 @@ pcie1: pcie@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 0 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index 01e727b021ec..3afb69921be3 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -3526,18 +3526,18 @@ spmi_bus: spmi@c440000 { #interrupt-cells = <4>; }; - sram@146aa000 { + sram@14680000 { compatible = "qcom,sc7180-imem", "syscon", "simple-mfd"; - reg = <0 0x146aa000 0 0x2000>; + reg = <0 0x14680000 0 0x2e000>; #address-cells = <1>; #size-cells = <1>; - ranges = <0 0 0x146aa000 0x2000>; + ranges = <0 0 0x14680000 0x2e000>; - pil-reloc@94c { + pil-reloc@2a94c { compatible = "qcom,pil-reloc-info"; - reg = <0x94c 0xc8>; + reg = <0x2a94c 0xc8>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index b1cc3bc1aec8..64a2abd30100 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -2227,9 +2227,17 @@ pcie1: pcie@1c08000 { , , , - ; - interrupt-names = "msi0", "msi1", "msi2", "msi3", - "msi4", "msi5", "msi6", "msi7"; + , + ; + 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 434 IRQ_TYPE_LEVEL_HIGH>, diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi index b84e47a461a0..f4f1d6a11960 100644 --- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1726,7 +1727,8 @@ pcie0: pcie@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1734,7 +1736,8 @@ pcie0: pcie@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1747,17 +1750,13 @@ pcie0: pcie@1c00000 { <&gcc GCC_PCIE_0_CFG_AHB_CLK>, <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, - <&gcc GCC_PCIE_0_CLKREF_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "ref", - "tbu"; + "slave_q2a"; assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>; assigned-clock-rates = <19200000>; @@ -1847,7 +1846,8 @@ pcie3: pcie@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1855,7 +1855,8 @@ pcie3: pcie@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1868,17 +1869,13 @@ pcie3: pcie@1c08000 { <&gcc GCC_PCIE_3_CFG_AHB_CLK>, <&gcc GCC_PCIE_3_MSTR_AXI_CLK>, <&gcc GCC_PCIE_3_SLV_AXI_CLK>, - <&gcc GCC_PCIE_3_SLV_Q2A_AXI_CLK>, - <&gcc GCC_PCIE_3_CLKREF_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + <&gcc GCC_PCIE_3_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "ref", - "tbu"; + "slave_q2a"; assigned-clocks = <&gcc GCC_PCIE_3_AUX_CLK>; assigned-clock-rates = <19200000>; @@ -1969,7 +1966,8 @@ pcie1: pcie@1c10000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1977,7 +1975,8 @@ pcie1: pcie@1c10000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 747 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1990,17 +1989,13 @@ pcie1: pcie@1c10000 { <&gcc GCC_PCIE_1_CFG_AHB_CLK>, <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, <&gcc GCC_PCIE_1_SLV_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>, - <&gcc GCC_PCIE_1_CLKREF_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "ref", - "tbu"; + "slave_q2a"; assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>; assigned-clock-rates = <19200000>; @@ -2091,7 +2086,8 @@ pcie2: pcie@1c18000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -2099,7 +2095,8 @@ pcie2: pcie@1c18000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 663 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -2112,17 +2109,13 @@ pcie2: pcie@1c18000 { <&gcc GCC_PCIE_2_CFG_AHB_CLK>, <&gcc GCC_PCIE_2_MSTR_AXI_CLK>, <&gcc GCC_PCIE_2_SLV_AXI_CLK>, - <&gcc GCC_PCIE_2_SLV_Q2A_AXI_CLK>, - <&gcc GCC_PCIE_2_CLKREF_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + <&gcc GCC_PCIE_2_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "ref", - "tbu"; + "slave_q2a"; assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>; assigned-clock-rates = <19200000>; @@ -2934,6 +2927,19 @@ usb_sec_dwc3_ss: endpoint { }; }; + camcc: clock-controller@ad00000 { + compatible = "qcom,sc8180x-camcc"; + reg = <0 0x0ad00000 0 0x20000>; + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + power-domains = <&rpmhpd SC8180X_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + mdss: mdss@ae00000 { compatible = "qcom,sc8180x-mdss"; reg = <0 0x0ae00000 0 0x1000>; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts index ae7a275fd223..cefecb7a23cf 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts @@ -1090,6 +1090,8 @@ &pmk8280_pon_resin { }; &pmk8280_rtc { + qcom,uefi-rtc-info; + status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index 3bc8471c658b..c0f466d96630 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -2327,8 +2327,24 @@ pcie0: pcie@1c00000 { ranges = <0x01000000 0x0 0x00000000 0x0 0x60200000 0x0 0x100000>, <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0xd00000>; - interrupts = ; - interrupt-names = "msi"; + 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 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -2436,8 +2452,24 @@ pcie1: pcie@1c08000 { ranges = <0x01000000 0x0 0x00000000 0x0 0x40200000 0x0 0x100000>, <0x02000000 0x0 0x40300000 0x0 0x40300000 0x0 0x1fd00000>; - interrupts = ; - interrupt-names = "msi"; + 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 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -5081,18 +5113,18 @@ spmi_bus: spmi@c440000 { #interrupt-cells = <4>; }; - sram@146bf000 { + sram@14680000 { compatible = "qcom,sdm845-imem", "syscon", "simple-mfd"; - reg = <0 0x146bf000 0 0x1000>; + reg = <0 0x14680000 0 0x40000>; #address-cells = <1>; #size-cells = <1>; - ranges = <0 0 0x146bf000 0x1000>; + ranges = <0 0 0x14680000 0x40000>; - pil-reloc@94c { + pil-reloc@3f94c { compatible = "qcom,pil-reloc-info"; - reg = <0x94c 0xc8>; + reg = <0x3f94c 0xc8>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts index 3b28c543fd96..8ef6db3be6e3 100644 --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts @@ -624,6 +624,12 @@ dai@2 { }; }; +&slpi_pas { + firmware-name = "qcom/sdm850/LENOVO/81JL/qcslpi850.mbn"; + + status = "okay"; +}; + &sound { compatible = "lenovo,yoga-c630-sndcard", "qcom,sdm845-sndcard"; model = "Lenovo-YOGA-C630-13Q50"; diff --git a/arch/arm64/boot/dts/qcom/sm6115.dtsi b/arch/arm64/boot/dts/qcom/sm6115.dtsi index c8865779173e..91fc36b59abf 100644 --- a/arch/arm64/boot/dts/qcom/sm6115.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6115.dtsi @@ -721,6 +721,13 @@ qup_spi5_default: qup-spi5-default-state { bias-pull-up; }; + qup_uart4_default: qup-uart4-default-state { + pins = "gpio12", "gpio13"; + function = "qup4"; + drive-strength = <2>; + bias-disable; + }; + sdc1_state_on: sdc1-on-state { clk-pins { pins = "sdc1_clk"; @@ -1565,6 +1572,8 @@ uart4: serial@4a90000 { reg = <0x0 0x04a90000 0x0 0x4000>; clock-names = "se"; clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart4_default>; interrupts = ; interconnects = <&clk_virt MASTER_QUP_CORE_0 RPM_ALWAYS_TAG &clk_virt SLAVE_QUP_CORE_0 RPM_ALWAYS_TAG>, diff --git a/arch/arm64/boot/dts/qcom/sm6350.dtsi b/arch/arm64/boot/dts/qcom/sm6350.dtsi index f80b21d28a92..ff1eb2c53e7b 100644 --- a/arch/arm64/boot/dts/qcom/sm6350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6350.dtsi @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include / { @@ -1320,6 +1322,63 @@ IPCC_MPROC_SIGNAL_GLINK_QMP label = "lpass"; qcom,remote-pid = <2>; + apr { + compatible = "qcom,apr-v2"; + qcom,glink-channels = "apr_audio_svc"; + qcom,domain = ; + #address-cells = <1>; + #size-cells = <0>; + + service@3 { + reg = ; + compatible = "qcom,q6core"; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + }; + + q6afe: service@4 { + compatible = "qcom,q6afe"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + + q6afedai: dais { + compatible = "qcom,q6afe-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + }; + + q6afecc: clock-controller { + compatible = "qcom,q6afe-clocks"; + #clock-cells = <2>; + }; + }; + + q6asm: service@7 { + compatible = "qcom,q6asm"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + + q6asmdai: dais { + compatible = "qcom,q6asm-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + iommus = <&apps_smmu 0x1001 0x0>; + }; + }; + + q6adm: service@8 { + compatible = "qcom,q6adm"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + + q6routing: routing { + compatible = "qcom,q6adm-routing"; + #sound-dai-cells = <0>; + }; + }; + }; + fastrpc { compatible = "qcom,fastrpc"; qcom,glink-channels = "fastrpcglink-apps-dsp"; @@ -1953,6 +2012,20 @@ usb_1_dwc3_ss_out: endpoint { }; }; + videocc: clock-controller@aaf0000 { + compatible = "qcom,sm6350-videocc"; + reg = <0x0 0x0aaf0000 0x0 0x10000>; + clocks = <&gcc GCC_VIDEO_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + clock-names = "iface", + "bi_tcxo", + "sleep_clk"; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + cci0: cci@ac4a000 { compatible = "qcom,sm6350-cci", "qcom,msm8996-cci"; reg = <0x0 0x0ac4a000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index cdb47359c4c8..abf12e10d33f 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -1853,7 +1853,8 @@ pcie0: pcie@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1861,7 +1862,8 @@ pcie0: pcie@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1874,17 +1876,13 @@ pcie0: pcie@1c00000 { <&gcc GCC_PCIE_0_CFG_AHB_CLK>, <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "tbu", - "ref"; + "slave_q2a"; iommu-map = <0x0 &apps_smmu 0x1d80 0x1>, <0x100 &apps_smmu 0x1d81 0x1>; @@ -1970,7 +1968,8 @@ pcie1: pcie@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1978,7 +1977,8 @@ pcie1: pcie@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1991,17 +1991,13 @@ pcie1: pcie@1c08000 { <&gcc GCC_PCIE_1_CFG_AHB_CLK>, <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, <&gcc GCC_PCIE_1_SLV_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>, - <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; clock-names = "pipe", "aux", "cfg", "bus_master", "bus_slave", - "slave_q2a", - "tbu", - "ref"; + "slave_q2a"; assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>; assigned-clock-rates = <19200000>; diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index f0d18fd37aaf..b30aea8b0540 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -2150,7 +2150,8 @@ pcie0: pcie@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -2158,7 +2159,8 @@ pcie0: pcie@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -2270,7 +2272,8 @@ pcie1: pcie@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -2278,7 +2281,8 @@ pcie1: pcie@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -2395,7 +2399,8 @@ pcie2: pcie@1c10000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -2403,7 +2408,8 @@ pcie2: pcie@1c10000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 290 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -4653,7 +4659,6 @@ camcc: clock-controller@ad00000 { clock-names = "iface", "bi_tcxo", "bi_tcxo_ao", "sleep_clk"; power-domains = <&rpmhpd RPMHPD_MMCX>; required-opps = <&rpmhpd_opp_low_svs>; - status = "disabled"; #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index 971c828a7555..9a4207ead615 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -1538,7 +1538,8 @@ pcie0: pcie@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1546,7 +1547,8 @@ pcie0: pcie@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ @@ -1647,7 +1649,8 @@ pcie1: pcie@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -1655,7 +1658,8 @@ pcie1: pcie@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 54c6d0fdb2af..33574ad706b9 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -3739,6 +3739,7 @@ aoss_qmp: power-management@c300000 { sram@c3f0000 { compatible = "qcom,rpmh-stats"; reg = <0 0x0c3f0000 0 0x400>; + qcom,qmp = <&aoss_qmp>; }; spmi_bus: spmi@c400000 { diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi index 71a7e3b57ece..45713d46f3c5 100644 --- a/arch/arm64/boot/dts/qcom/sm8550.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi @@ -3406,6 +3406,216 @@ cci2_i2c1: i2c-bus@1 { }; }; + camss: isp@acb7000 { + compatible = "qcom,sm8550-camss"; + + reg = <0x0 0x0acb7000 0x0 0x0d00>, + <0x0 0x0acb9000 0x0 0x0d00>, + <0x0 0x0acbb000 0x0 0x0d00>, + <0x0 0x0acca000 0x0 0x0a00>, + <0x0 0x0acce000 0x0 0x0a00>, + <0x0 0x0acb6000 0x0 0x1000>, + <0x0 0x0ace4000 0x0 0x2000>, + <0x0 0x0ace6000 0x0 0x2000>, + <0x0 0x0ace8000 0x0 0x2000>, + <0x0 0x0acea000 0x0 0x2000>, + <0x0 0x0acec000 0x0 0x2000>, + <0x0 0x0acee000 0x0 0x2000>, + <0x0 0x0acf0000 0x0 0x2000>, + <0x0 0x0acf2000 0x0 0x2000>, + <0x0 0x0ac62000 0x0 0xf000>, + <0x0 0x0ac71000 0x0 0xf000>, + <0x0 0x0ac80000 0x0 0xf000>, + <0x0 0x0accb000 0x0 0x1800>, + <0x0 0x0accf000 0x0 0x1800>; + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_wrapper", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csiphy5", + "csiphy6", + "csiphy7", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1"; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CPAS_FAST_AHB_CLK>, + <&camcc CAM_CC_CPAS_IFE_LITE_CLK>, + <&camcc CAM_CC_CPAS_IFE_0_CLK>, + <&camcc CAM_CC_CPAS_IFE_1_CLK>, + <&camcc CAM_CC_CPAS_IFE_2_CLK>, + <&camcc CAM_CC_CSID_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_CSIPHY3_CLK>, + <&camcc CAM_CC_CSI3PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY4_CLK>, + <&camcc CAM_CC_CSI4PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY5_CLK>, + <&camcc CAM_CC_CSI5PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY6_CLK>, + <&camcc CAM_CC_CSI6PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY7_CLK>, + <&camcc CAM_CC_CSI7PHYTIMER_CLK>, + <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>, + <&gcc GCC_CAMERA_HF_AXI_CLK>, + <&camcc CAM_CC_IFE_0_CLK>, + <&camcc CAM_CC_IFE_0_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_CLK>, + <&camcc CAM_CC_IFE_1_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_CLK>, + <&camcc CAM_CC_IFE_2_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_LITE_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_LITE_CSID_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "cpas_fast_ahb_clk", + "cpas_ife_lite", + "cpas_vfe0", + "cpas_vfe1", + "cpas_vfe2", + "csid", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "csiphy4", + "csiphy4_timer", + "csiphy5", + "csiphy5_timer", + "csiphy6", + "csiphy6_timer", + "csiphy7", + "csiphy7_timer", + "csiphy_rx", + "gcc_axi_hf", + "vfe0", + "vfe0_fast_ahb", + "vfe1", + "vfe1_fast_ahb", + "vfe2", + "vfe2_fast_ahb", + "vfe_lite", + "vfe_lite_ahb", + "vfe_lite_cphy_rx", + "vfe_lite_csid"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csiphy5", + "csiphy6", + "csiphy7", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1"; + + 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_HF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_0_mnoc"; + + iommus = <&apps_smmu 0x800 0x20>; + + power-domains = <&camcc CAM_CC_IFE_0_GDSC>, + <&camcc CAM_CC_IFE_1_GDSC>, + <&camcc CAM_CC_IFE_2_GDSC>, + <&camcc CAM_CC_TITAN_TOP_GDSC>; + power-domain-names = "ife0", + "ife1", + "ife2", + "top"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + + port@2 { + reg = <2>; + }; + + port@3 { + reg = <3>; + }; + + port@4 { + reg = <4>; + }; + + port@5 { + reg = <5>; + }; + + port@6 { + reg = <6>; + }; + + port@7 { + reg = <7>; + }; + }; + }; + camcc: clock-controller@ade0000 { compatible = "qcom,sm8550-camcc"; reg = <0 0x0ade0000 0 0x20000>; @@ -4024,6 +4234,7 @@ aoss_qmp: power-management@c300000 { sram@c3f0000 { compatible = "qcom,rpmh-stats"; reg = <0 0x0c3f0000 0 0x400>; + qcom,qmp = <&aoss_qmp>; }; spmi_bus: spmi@c400000 { diff --git a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts b/arch/arm64/boot/dts/qcom/sm8650-hdk.dts index d0912735b54e..259649d7dcd7 100644 --- a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts +++ b/arch/arm64/boot/dts/qcom/sm8650-hdk.dts @@ -894,6 +894,10 @@ &ipa { status = "okay"; }; +&iris { + status = "okay"; +}; + &gpu { status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts index 76ef43c10f77..8a957adbfb38 100644 --- a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts @@ -585,6 +585,10 @@ vreg_l7n_3p3: ldo7 { }; }; +&iris { + status = "okay"; +}; + &lpass_tlmm { spkr_1_sd_n_active: spkr-1-sd-n-active-state { pins = "gpio21"; diff --git a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts index 71033fba21b5..7552d5d3fb40 100644 --- a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts +++ b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts @@ -824,6 +824,10 @@ &ipa { status = "okay"; }; +&iris { + status = "okay"; +}; + &gpu { status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 495ea9bfd008..e14d3d778b71 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -4962,6 +4962,99 @@ opp-202000000 { }; }; + iris: video-codec@aa00000 { + compatible = "qcom,sm8650-iris"; + reg = <0 0x0aa00000 0 0xf0000>; + + interrupts = ; + + power-domains = <&videocc VIDEO_CC_MVS0C_GDSC>, + <&videocc VIDEO_CC_MVS0_GDSC>, + <&rpmhpd RPMHPD_MXC>, + <&rpmhpd RPMHPD_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mxc", + "mmcx"; + + operating-points-v2 = <&iris_opp_table>; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem"; + + memory-region = <&video_mem>; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>, + <&videocc VIDEO_CC_XO_CLK_ARES>, + <&videocc VIDEO_CC_MVS0C_CLK_ARES>; + reset-names = "bus", + "xo", + "core"; + + iommus = <&apps_smmu 0x1940 0>, + <&apps_smmu 0x1947 0>; + + dma-coherent; + + /* + * IRIS firmware is signed by vendors, only + * enable in boards where the proper signed firmware + * is available. + */ + status = "disabled"; + + iris_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-196000000 { + opp-hz = /bits/ 64 <196000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>, + <&rpmhpd_opp_low_svs_d1>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + }; + + opp-380000000 { + opp-hz = /bits/ 64 <380000000>; + required-opps = <&rpmhpd_opp_svs>, + <&rpmhpd_opp_svs>; + }; + + opp-435000000 { + opp-hz = /bits/ 64 <435000000>; + required-opps = <&rpmhpd_opp_svs_l1>, + <&rpmhpd_opp_svs_l1>; + }; + + opp-480000000 { + opp-hz = /bits/ 64 <480000000>; + required-opps = <&rpmhpd_opp_nom>, + <&rpmhpd_opp_nom>; + }; + + opp-533333334 { + opp-hz = /bits/ 64 <533333334>; + required-opps = <&rpmhpd_opp_turbo>, + <&rpmhpd_opp_turbo>; + }; + }; + }; + videocc: clock-controller@aaf0000 { compatible = "qcom,sm8650-videocc"; reg = <0 0x0aaf0000 0 0x10000>; @@ -5732,6 +5825,7 @@ aoss_qmp: power-management@c300000 { sram@c3f0000 { compatible = "qcom,rpmh-stats"; reg = <0 0x0c3f0000 0 0x400>; + qcom,qmp = <&aoss_qmp>; }; spmi_bus: spmi@c400000 { @@ -6868,8 +6962,7 @@ apps_rsc: rsc@17a00000 { compatible = "qcom,rpmh-rsc"; reg = <0 0x17a00000 0 0x10000>, <0 0x17a10000 0 0x10000>, - <0 0x17a20000 0 0x10000>, - <0 0x17a30000 0 0x10000>; + <0 0x17a20000 0 0x10000>; reg-names = "drv-0", "drv-1", "drv-2"; diff --git a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts index 72f081a890df..75cfbb510be5 100644 --- a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts @@ -29,6 +29,33 @@ aliases { serial0 = &uart7; }; + 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 101 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_l2i_1p2>; + + #sound-dai-cells = <1>; + }; + chosen { stdout-path = "serial0:115200n8"; }; @@ -81,6 +108,89 @@ key-volume-up { }; }; + sound { + compatible = "qcom,sm8750-sndcard", "qcom,sm8450-sndcard"; + model = "SM8750-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 BIAS3", /* MIC4 on schematics */ + "VA DMIC1", "MIC BIAS3", /* MIC1 on schematics */ + "VA DMIC2", "MIC BIAS1", + "VA DMIC3", "MIC BIAS1", + "VA DMIC0", "VA MIC BIAS3", + "VA DMIC1", "VA MIC BIAS3", + "VA DMIC2", "VA MIC BIAS1", + "VA DMIC3", "VA MIC BIAS1", + "TX SWR_INPUT1", "ADC2_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 = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&lpass_wsamacro 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>; + }; + }; + }; + vph_pwr: vph-pwr-regulator { compatible = "regulator-fixed"; @@ -702,6 +812,14 @@ vreg_l7n_3p3: ldo7 { }; }; +&lpass_vamacro { + pinctrl-0 = <&dmic01_default>, <&dmic23_default>; + pinctrl-names = "default"; + + vdd-micb-supply = <&vreg_l1b_1p8>; + qcom,dmic-sample-rate = <4800000>; +}; + &pm8550_flash { status = "okay"; @@ -806,6 +924,74 @@ &remoteproc_mpss { status = "fail"; }; +&swr0 { + status = "okay"; + + /* WSA883x, left/front speaker */ + left_spkr: speaker@0,1 { + compatible = "sdw10217020200"; + reg = <0 1>; + pinctrl-0 = <&spkr_0_sd_n_active>; + pinctrl-names = "default"; + powerdown-gpios = <&lpass_tlmm 17 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrLeft"; + #thermal-sensor-cells = <0>; + vdd-supply = <&vreg_l15b_1p8>; + }; + + /* WSA883x, right/back speaker */ + right_spkr: speaker@0,2 { + compatible = "sdw10217020200"; + reg = <0 2>; + pinctrl-0 = <&spkr_1_sd_n_active>; + pinctrl-names = "default"; + powerdown-gpios = <&lpass_tlmm 18 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrRight"; + #thermal-sensor-cells = <0>; + vdd-supply = <&vreg_l15b_1p8>; + }; +}; + +&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 { /* reserved for secure world */ gpio-reserved-ranges = <36 4>, <74 1>; @@ -814,3 +1000,50 @@ &tlmm { &uart7 { status = "okay"; }; + +/* Pinctrl */ +&lpass_tlmm { + spkr_0_sd_n_active: spkr-0-sd-n-active-state { + pins = "gpio17"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + spkr_1_sd_n_active: spkr-1-sd-n-active-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; +}; + +&tlmm { + wcd_default: wcd-reset-n-active-state { + pins = "gpio101"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l1j_0p91>; + vdda-pll-supply = <&vreg_l3g_1p2>; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 215 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l17b_2p5>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l1d_1p2>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8750-qrd.dts b/arch/arm64/boot/dts/qcom/sm8750-qrd.dts index 840a6d8f8a24..13c7b9664c89 100644 --- a/arch/arm64/boot/dts/qcom/sm8750-qrd.dts +++ b/arch/arm64/boot/dts/qcom/sm8750-qrd.dts @@ -28,6 +28,37 @@ aliases { serial0 = &uart7; }; + 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 101 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>; + /* + * Mismatch with schematics - downstream DTS has L15B at 1.8 V, + * schematics L2I at 1.2 V + */ + vdd-px-supply = <&vreg_l15b_1p8>; + + #sound-dai-cells = <1>; + }; + chosen { stdout-path = "serial0:115200n8"; }; @@ -80,6 +111,88 @@ key-volume-up { }; }; + sound { + compatible = "qcom,sm8750-sndcard", "qcom,sm8450-sndcard"; + model = "SM8750-QRD"; + 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_INPUT2", "ADC3_OUTPUT", + "TX SWR_INPUT3", "ADC4_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 = <&north_spkr>, <&south_spkr>, <&swr0 0>, <&lpass_wsamacro 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>; + }; + }; + }; + vph_pwr: vph-pwr-regulator { compatible = "regulator-fixed"; @@ -803,11 +916,141 @@ &remoteproc_mpss { 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 { /* reserved for secure world */ gpio-reserved-ranges = <36 4>, <74 1>; + + spkr_0_sd_n_active: spkr-0-sd-n-active-state { + pins = "gpio76"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + spkr_1_sd_n_active: spkr-1-sd-n-active-state { + pins = "gpio77"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio101"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; }; &uart7 { status = "okay"; }; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l1j_0p91>; + vdda-pll-supply = <&vreg_l3g_1p2>; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 215 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l17b_2p5>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l1d_1p2>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi index 980ba1ca23c4..4643705021c6 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -177,7 +178,6 @@ cluster1_c4: cpu-sleep-1 { exit-latency-us = <130>; min-residency-us = <686>; }; - }; domain-idle-states { @@ -1986,7 +1986,6 @@ &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, interconnect-names = "qup-core", "qup-config"; - pinctrl-0 = <&qup_uart7_default>; pinctrl-names = "default"; @@ -2027,7 +2026,6 @@ pcie_noc: interconnect@16c0000 { #interconnect-cells = <2>; clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>; - }; aggre1_noc: interconnect@16e0000 { @@ -2037,7 +2035,6 @@ aggre1_noc: interconnect@16e0000 { #interconnect-cells = <2>; clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>; - }; aggre2_noc: interconnect@1700000 { @@ -2257,6 +2254,36 @@ lpass_wsa2macro: codec@6aa0000 { #sound-dai-cells = <1>; }; + swr3: soundwire@6ab0000 { + compatible = "qcom,soundwire-v2.1.0", "qcom,soundwire-v2.0.0"; + reg = <0x0 0x06ab0000 0x0 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 0x18f 0x18f 0x0f 0x0f 0xff 0x31f>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x03 0x05 0x02 0x04 0x15 0x00 0x00 0x00 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 0x0e 0x0e 0xff 0xff 0xff 0x0f>; + qcom,ports-hstop = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0x0e 0x0e 0xff 0xff 0xff 0x0f>; + qcom,ports-word-length = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0x0f 0x0f 0x00 0xff 0xff 0x18>; + qcom,ports-block-pack-mode = /bits/ 8 <0x00 0x01 0x01 0x00 0x01 0x01 0x00 0x01 0x01 0x01 0x01 0x00 0x00>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + lpass_rxmacro: codec@6ac0000 { compatible = "qcom,sm8750-lpass-rx-macro", "qcom,sm8550-lpass-rx-macro"; reg = <0x0 0x06ac0000 0x0 0x1000>; @@ -2274,6 +2301,36 @@ lpass_rxmacro: codec@6ac0000 { #sound-dai-cells = <1>; }; + swr1: soundwire@6ad0000 { + compatible = "qcom,soundwire-v2.1.0", "qcom,soundwire-v2.0.0"; + reg = <0x0 0x06ad0000 0x0 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 0x31 0xff 0xff 0xff>; + qcom,ports-offset1 = /bits/ 8 <0x00 0x00 0x0b 0x01 0x00 0x00 0xff 0xff 0x00 0xff 0xff 0xff>; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x0b 0x00 0x00 0x00 0xff 0xff 0x00 0xff 0xff 0xff>; + qcom,ports-hstart = /bits/ 8 <0xff 0x03 0xff 0xff 0xff 0x08 0xff 0xff 0x00 0xff 0xff 0xff>; + qcom,ports-hstop = /bits/ 8 <0xff 0x06 0xff 0xff 0xff 0x08 0xff 0xff 0x0f 0xff 0xff 0xff>; + qcom,ports-word-length = /bits/ 8 <0x01 0x07 0x04 0xff 0xff 0x0f 0xff 0xff 0x18 0xff 0xff 0xff>; + qcom,ports-block-pack-mode = /bits/ 8 <0xff 0x00 0x01 0xff 0xff 0x00 0xff 0xff 0x01 0xff 0xff 0xff>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0x00 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0x01 0x00 0x00 0x00 0x00 0x00 0xff 0xff 0x01 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + lpass_txmacro: codec@6ae0000 { compatible = "qcom,sm8750-lpass-tx-macro", "qcom,sm8550-lpass-tx-macro"; reg = <0x0 0x06ae0000 0x0 0x1000>; @@ -2308,6 +2365,36 @@ lpass_wsamacro: codec@6b00000 { #sound-dai-cells = <1>; }; + swr0: soundwire@6b10000 { + compatible = "qcom,soundwire-v2.1.0", "qcom,soundwire-v2.0.0"; + reg = <0x0 0x06b10000 0x0 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 0x18f 0x18f 0x0f 0x0f 0xff 0x31f>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x03 0x05 0x02 0x04 0x15 0x00 0x00 0x00 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 0x0e 0x0e 0xff 0xff 0xff 0x0f>; + qcom,ports-hstop = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0x0e 0x0e 0xff 0xff 0xff 0x0f>; + qcom,ports-word-length = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0x0f 0x0f 0x00 0xff 0xff 0x18>; + qcom,ports-block-pack-mode = /bits/ 8 <0x00 0x01 0x01 0x00 0x01 0x01 0x00 0x01 0x01 0x01 0x01 0x00 0x00>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + lpass_ag_noc: interconnect@7e40000 { compatible = "qcom,sm8750-lpass-ag-noc"; reg = <0x0 0x07e40000 0x0 0xe080>; @@ -2329,6 +2416,38 @@ lpass_lpicx_noc: interconnect@7420000 { #interconnect-cells = <2>; }; + swr2: soundwire@7630000 { + compatible = "qcom,soundwire-v2.1.0", "qcom,soundwire-v2.0.0"; + reg = <0x0 0x07630000 0x0 0x10000>; + interrupts = , + ; + 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,sm8750-lpass-va-macro", "qcom,sm8550-lpass-va-macro"; reg = <0x0 0x07660000 0x0 0x2000>; @@ -2490,6 +2609,7 @@ aoss_qmp: power-management@c300000 { sram@c3f0000 { compatible = "qcom,rpmh-stats"; reg = <0x0 0x0c3f0000 0x0 0x400>; + qcom,qmp = <&aoss_qmp>; }; spmi_bus: spmi@c400000 { @@ -3184,6 +3304,108 @@ gic_its: msi-controller@16040000 { }; }; + ufs_mem_phy: phy@1d80000 { + compatible = "qcom,sm8750-qmp-ufs-phy"; + reg = <0x0 0x01d80000 0x0 0x2000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_UFS_PHY_PHY_AUX_CLK>, + <&tcsrcc 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: ufs@1d84000 { + compatible = "qcom,sm8750-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>, + <&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 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-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-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>; + }; + }; + }; + apps_rsc: rsc@16500000 { compatible = "qcom,rpmh-rsc"; reg = <0x0 0x16500000 0x0 0x10000>, diff --git a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi new file mode 100644 index 000000000000..c771fd1d8029 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi @@ -0,0 +1,1496 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Aleksandrs Vinarskis + */ + +#include +#include +#include +#include +#include +#include + +#include "x1e80100-pmics.dtsi" + +/ { + model = "ASUS Zenbook A14"; + chassis-type = "laptop"; + + aliases { + serial0 = &uart21; + serial1 = &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>; + + 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>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&hall_int_n_default>; + pinctrl-names = "default"; + + switch-lid { + label = "lid"; + gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + wakeup-source; + wakeup-event-action = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + pinctrl-0 = <&cam_indicator_en>; + pinctrl-names = "default"; + + led-camera-indicator { + label = "white:camera-indicator"; + function = LED_FUNCTION_INDICATOR; + color = ; + gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "none"; + default-state = "off"; + /* Reuse as a panic indicator until we get a "camera on" trigger */ + panic-indicator; + }; + }; + + 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 display-adjacent port */ + 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 user-adjacent port */ + 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; + }; + }; + + sound { + compatible = "qcom,x1e80100-sndcard"; + model = "X1E80100-ASUS-Zenbook-A14"; + audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT", + "SpkrRight IN", "WSA WSA_SPK2 OUT", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC2", "MIC BIAS2", + "VA DMIC0", "MIC BIAS1", + "VA DMIC1", "MIC BIAS1", + "VA DMIC0", "VA MIC BIAS1", + "VA DMIC1", "VA MIC BIAS1", + "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 = <&wcd938x 1>, <&swr2 1>, + <&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 = <&wcd938x 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 = <&left_spkr>, <&right_spkr>, + <&swr0 0>, <&lpass_wsamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + 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 = <&pm8550ve_8_gpios 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&misc_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + regulator-always-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; + }; + + vreg_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; + }; + + 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; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-bob1-supply = <&vreg_vph_pwr>; + vdd-bob2-supply = <&vreg_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 = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l4b_1p8: ldo4 { + regulator-name = "vreg_l4b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l6b_1p8: ldo6 { + regulator-name = "vreg_l6b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + 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 = <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 = ; + regulator-always-on; + }; + + vreg_l17b_2p5: ldo17 { + regulator-name = "vreg_l17b_2p5"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2504000>; + 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 = <&vreg_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 = <880000>; + regulator-initial-mode = ; + }; + + vreg_l3c_0p9: ldo3 { + regulator-name = "vreg_l3c_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + 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 = <&vreg_vph_pwr>; + + vreg_l1d_0p8: ldo1 { + regulator-name = "vreg_l1d_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l2d_0p9: ldo2 { + regulator-name = "vreg_l2d_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + 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 = <880000>; + 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 = <&vreg_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 = <&vreg_vph_pwr>; + vdd-s2-supply = <&vreg_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 = <880000>; + 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 = <&vreg_vph_pwr>; + + vreg_s5j_1p2: smps5 { + regulator-name = "vreg_s5j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l1j_0p9: ldo1 { + regulator-name = "vreg_l1j_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + 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 = <880000>; + regulator-initial-mode = ; + }; + }; +}; + +&i2c0 { + clock-frequency = <400000>; + + status = "okay"; + + /* ELAN, 04F3:3315 */ + 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; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + + status = "okay"; + + /* Left-side display-adjacent port */ + typec-mux@8 { + compatible = "parade,ps8833", "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"; + + 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>; + }; + }; + }; + }; +}; + +&i2c4 { + clock-frequency = <400000>; + + status = "okay"; + + /* ASUSTeK, 0B05:4543 */ + hdtl@17 { + compatible = "hid-over-i2c"; + reg = <0x17>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 95 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&hdtl_default>; + pinctrl-names = "default"; + + wakeup-source; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + 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 @0x5b */ +}; + +&i2c7 { + clock-frequency = <400000>; + + status = "okay"; + + /* Left-side user-adjacent port */ + typec-mux@8 { + compatible = "parade,ps8833", "parade,ps8830"; + reg = <0x08>; + + 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>; + }; + }; + }; + }; +}; + +&i2c8 { + clock-frequency = <400000>; + + status = "okay"; + + /* ASUSTeK, 0B05:0220 */ + keyboard@15 { + compatible = "hid-over-i2c"; + reg = <0x15>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&kybd_default>; + pinctrl-names = "default"; + + wakeup-source; + }; +}; + +&lpass_tlmm { + spkr_01_sd_n_active: spkr-01-sd-n-active-state { + pins = "gpio12"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; +}; + +&lpass_vamacro { + pinctrl-0 = <&dmic01_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 { + data-lanes = <0 1>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + status = "okay"; +}; + +&mdss_dp1_out { + data-lanes = <0 1>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp3 { + /delete-property/ #sound-dai-cells; + + status = "okay"; + + aux-bus { + panel { + compatible = "edp-panel"; + 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>; + }; + }; + }; + }; + + ports { + port@1 { + reg = <1>; + + mdss_dp3_out: endpoint { + 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 { + perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + 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"; +}; + +&pcie6a { + perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; + + 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"; +}; + +&pm8550_gpios { + rtmr0_default: rtmr0-reset-n-active-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_8_gpios { + misc_3p3_reg_en: misc-3p3-reg-en-state { + pins = "gpio6"; + function = "normal"; + power-source = <1>; + bias-disable; + input-disable; + output-enable; + drive-push-pull; + qcom,drive-strength = ; + }; +}; + +&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; + }; +}; + +&pmc8380_3_gpios { + edp_bl_en: edp-bl-en-state { + pins = "gpio4"; + function = "normal"; + power-source = <1>; + 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>; + 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>; +}; + +&spi10 { + status = "disabled"; + + /* Unknown device */ +}; + +&swr0 { + status = "okay"; + + pinctrl-0 = <&wsa_swr_active>, <&spkr_01_sd_n_active>; + pinctrl-names = "default"; + + /* WSA8845, Left Speaker */ + left_spkr: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Right Speaker */ + right_spkr: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrRight"; + 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>; + }; +}; + +&tlmm { + gpio-reserved-ranges = <44 4>, /* SPI11, TZ Protected */ + <90 1>; /* Unknown, TZ Protected */ + + cam_indicator_en: cam-indicator-en-state { + pins = "gpio110"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + edp_reg_en: edp-reg-en-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + hall_int_n_default: hall-int-n-state { + pins = "gpio92"; + function = "gpio"; + bias-disable; + }; + + hdtl_default: hdtl-default-state { + pins = "gpio95"; + function = "gpio"; + }; + + kybd_default: kybd-default-state { + pins = "gpio67"; + function = "gpio"; + bias-pull-up; + }; + + 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-disable; + }; + + 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-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; + }; + + 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 = <16>; + bias-disable; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_en: wcn-wlan-en-state { + pins = "gpio117"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; +}; + +&uart21 { + compatible = "qcom,geni-debug-uart"; + + status = "okay"; +}; + +&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_0p9>; + + 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_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>; + + phys = <&eusb6_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p9>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p9>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-asus-zenbook-a14.dts b/arch/arm64/boot/dts/qcom/x1e80100-asus-zenbook-a14.dts new file mode 100644 index 000000000000..0d0bcc50207d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1e80100-asus-zenbook-a14.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Aleksandrs Vinarskis + */ + +/dts-v1/; + +#include "x1e80100.dtsi" +#include "x1-asus-zenbook-a14.dtsi" + +/ { + model = "ASUS Zenbook A14 (UX3407RA)"; + compatible = "asus,zenbook-a14-ux3407ra", "qcom,x1e80100"; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1e80100/ASUSTeK/zenbook-a14/qcdxkmsuc8380.mbn"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1e80100/ASUSTeK/zenbook-a14/qcadsp8380.mbn", + "qcom/x1e80100/ASUSTeK/zenbook-a14/adsp_dtbs.elf"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1e80100/ASUSTeK/zenbook-a14/qccdsp8380.mbn", + "qcom/x1e80100/ASUSTeK/zenbook-a14/cdsp_dtbs.elf"; + + 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 967f6dba0878..fd00d1bf12e1 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts @@ -22,6 +22,7 @@ / { aliases { serial0 = &uart21; + serial1 = &uart14; }; gpio-keys { @@ -288,6 +289,101 @@ vreg_vph_pwr: regulator-vph-pwr { regulator-always-on; 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_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_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 { @@ -744,8 +840,21 @@ touchscreen@10 { &i2c9 { clock-frequency = <400000>; - status = "disabled"; - /* USB3 retimer device @0x4f */ + status = "okay"; + + 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"; + }; }; &i2c17 { @@ -848,6 +957,23 @@ &pcie4_phy { status = "okay"; }; +&pcie4_port0 { + 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 { perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; @@ -967,6 +1093,14 @@ edp_reg_en: edp-reg-en-state { bias-disable; }; + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + hall_int_n_default: hall-int-n-state { pins = "gpio92"; function = "gpio"; @@ -1102,6 +1236,37 @@ reset-n-pins { drive-strength = <2>; }; }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + 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>; + }; }; &uart21 { @@ -1172,3 +1337,37 @@ &usb_1_ss1_dwc3_hs { &usb_1_ss1_qmpphy_out { remote-endpoint = <&retimer_ss1_ss_in>; }; + +&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>; + + phys = <&eusb6_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p9>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p9>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-hp-omnibook-x14.dts b/arch/arm64/boot/dts/qcom/x1e80100-hp-omnibook-x14.dts index 10b3af5e79fb..8d2a9b7f4730 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-hp-omnibook-x14.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-hp-omnibook-x14.dts @@ -153,6 +153,14 @@ pmic_glink_ss1_ss_in: endpoint { remote-endpoint = <&usb_1_ss1_qmpphy_out>; }; }; + + port@2 { + reg = <2>; + + pmic_glink_ss1_sbu: endpoint { + remote-endpoint = <&usb_1_ss1_sbu_mux>; + }; + }; }; }; }; @@ -477,6 +485,25 @@ vreg_pmu_rfa_1p7: ldo9 { }; }; }; + + usb-1-ss1-sbu-mux { + compatible = "onnn,fsusb42", "gpio-sbu-mux"; + + enable-gpios = <&tlmm 179 GPIO_ACTIVE_LOW>; + select-gpios = <&tlmm 178 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb_1_ss1_sbu_default>; + pinctrl-names = "default"; + + mode-switch; + orientation-switch; + + port { + usb_1_ss1_sbu_mux: endpoint { + remote-endpoint = <&pmic_glink_ss1_sbu>; + }; + }; + }; }; &apps_rsc { @@ -877,15 +904,6 @@ touchpad@15 { }; }; -&i2c1 { - clock-frequency = <400000>; - - status = "okay"; - - /* type-c PS8830 Retimer #2 0x8 */ - /* is active on Windows */ -}; - &i2c3 { clock-frequency = <400000>; @@ -943,14 +961,6 @@ retimer_ss0_con_sbu_out: endpoint { }; }; -&i2c4 { - clock-frequency = <400000>; - - status = "okay"; - - /* is active on Windows */ -}; - &i2c5 { clock-frequency = <400000>; status = "okay"; @@ -991,14 +1001,6 @@ touchscreen@10 { }; }; -&i2c9 { - clock-frequency = <400000>; - - status = "okay"; - - /* is active on Windows */ -}; - &lpass_tlmm { spkr_01_sd_n_active: spkr-01-sd-n-active-state { pins = "gpio12"; @@ -1193,17 +1195,6 @@ edp_bl_reg_en: edp-bl-reg-en-state { }; -&pmk8550_gpios { - edp_bl_pwm: edp-bl-pwm-state { - pins = "gpio5"; - function = "func3"; - }; -}; - -&pmk8550_pwm { - status = "okay"; -}; - &pmc8380_5_gpios { usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { pins = "gpio8"; @@ -1215,6 +1206,17 @@ usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { }; }; +&pmk8550_gpios { + edp_bl_pwm: edp-bl-pwm-state { + pins = "gpio5"; + function = "func3"; + }; +}; + +&pmk8550_pwm { + status = "okay"; +}; + &qupv3_0 { status = "okay"; }; @@ -1419,6 +1421,30 @@ reset-n-pins { }; }; + usb_1_ss1_sbu_default: usb-1-ss1-sbu-state { + mode-pins { + pins = "gpio177"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-high; + }; + + oe-n-pins { + pins = "gpio179"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + sel-pins { + pins = "gpio178"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + }; + wcd_default: wcd-reset-n-active-state { pins = "gpio191"; function = "gpio"; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi b/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi index c02fd4d15c96..e3888bc143a0 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi @@ -224,6 +224,7 @@ pmk8550_rtc: rtc@6100 { reg-names = "rtc", "alarm"; interrupts = <0x0 0x62 0x1 IRQ_TYPE_EDGE_RISING>; qcom,no-alarm; /* alarm owned by ADSP */ + qcom,uefi-rtc-info; }; pmk8550_sdam_2: nvram@7100 { diff --git a/arch/arm64/boot/dts/qcom/x1e80100.dtsi b/arch/arm64/boot/dts/qcom/x1e80100.dtsi index a8eb4c5fe99f..a9a7bb676c6f 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100.dtsi @@ -3378,7 +3378,8 @@ pcie6a: pci@1bf8000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -3386,7 +3387,8 @@ pcie6a: pci@1bf8000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; @@ -3508,7 +3510,8 @@ pcie5: pci@1c00000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -3516,7 +3519,8 @@ pcie5: pci@1c00000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; @@ -3636,7 +3640,8 @@ pcie4: pci@1c08000 { , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", @@ -3644,7 +3649,8 @@ pcie4: pci@1c08000 { "msi4", "msi5", "msi6", - "msi7"; + "msi7", + "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; @@ -8548,7 +8554,7 @@ timer { ; }; - thermal-zones { + thermal_zones: thermal-zones { aoss0-thermal { thermal-sensors = <&tsens0 0>; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-asus-zenbook-a14.dts b/arch/arm64/boot/dts/qcom/x1p42100-asus-zenbook-a14.dts new file mode 100644 index 000000000000..bd75ff898601 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1p42100-asus-zenbook-a14.dts @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Aleksandrs Vinarskis + */ + +/dts-v1/; + +#include "x1p42100.dtsi" +#include "x1-asus-zenbook-a14.dtsi" + +/delete-node/ &pmc8380_6; +/delete-node/ &pmc8380_6_thermal; + +/ { + model = "ASUS Zenbook A14 (UX3407QA)"; + compatible = "asus,zenbook-a14-ux3407qa", "qcom,x1p42100"; + + wcn6855-pmu { + compatible = "qcom,wcn6855-pmu"; + + vddaon-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_wcn_1p9>; + vddpcie1p3-supply = <&vreg_wcn_1p9>; + vddpcie1p9-supply = <&vreg_wcn_1p9>; + vddpmu-supply = <&vreg_wcn_0p95>; + vddpmucx-supply = <&vreg_wcn_0p95>; + vddpmumx-supply = <&vreg_wcn_0p95>; + vddrfa0p95-supply = <&vreg_wcn_0p95>; + vddrfa1p3-supply = <&vreg_wcn_1p9>; + vddrfa1p9-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_0p8: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn_0p8"; + }; + + vreg_pmu_aon_0p8: ldo1 { + regulator-name = "vreg_pmu_aon_0p8"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p8: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p8"; + }; + + vreg_pmu_btcmx_0p8: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p8"; + }; + + vreg_pmu_pcie_1p8: ldo5 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo6 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_rfa_0p8: ldo7 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo8 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p7: ldo9 { + regulator-name = "vreg_pmu_rfa_1p7"; + }; + }; + }; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1p42100/ASUSTeK/zenbook-a14/qcdxkmsucpurwa.mbn"; +}; + +&pcie4_port0 { + wifi@0 { + compatible = "pci17cb,1103"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; + + qcom,calibration-variant = "UX3407Q"; + }; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1p42100/ASUSTeK/zenbook-a14/qcadsp8380.mbn", + "qcom/x1p42100/ASUSTeK/zenbook-a14/adsp_dtbs.elf"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1p42100/ASUSTeK/zenbook-a14/qccdsp8380.mbn", + "qcom/x1p42100/ASUSTeK/zenbook-a14/cdsp_dtbs.elf"; + + status = "okay"; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn6855-bt"; + + vddaon-supply = <&vreg_pmu_aon_0p8>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p8>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; + + max-speed = <3000000>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/x1p42100.dtsi b/arch/arm64/boot/dts/qcom/x1p42100.dtsi index 27f479010bc3..9af9e707f982 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100.dtsi +++ b/arch/arm64/boot/dts/qcom/x1p42100.dtsi @@ -18,6 +18,7 @@ /delete-node/ &cpu_pd10; /delete-node/ &cpu_pd11; /delete-node/ &pcie3_phy; +/delete-node/ &thermal_zones; &gcc { compatible = "qcom,x1p42100-gcc", "qcom,x1e80100-gcc"; @@ -79,3 +80,558 @@ pcie3_phy: phy@1bd4000 { status = "disabled"; }; }; + +/* 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/renesas/Makefile b/arch/arm64/boot/dts/renesas/Makefile index aa7f996c0546..6093d5f6e548 100644 --- a/arch/arm64/boot/dts/renesas/Makefile +++ b/arch/arm64/boot/dts/renesas/Makefile @@ -96,6 +96,7 @@ dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g2-white-hawk-single-ard-audio-da7212.dtb DTC_FLAGS_r8a779g3-sparrow-hawk += -Wno-spi_bus_bridge dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk.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 @@ -105,6 +106,8 @@ dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-white-hawk-single-ard-audio-da7212.dtb dtb-$(CONFIG_ARCH_R8A779H0) += r8a779h0-gray-hawk-single.dtb +dtb-$(CONFIG_ARCH_R8A779H0) += r8a779h2-gray-hawk-single.dtb + dtb-$(CONFIG_ARCH_R8A77951) += r8a779m1-salvator-xs.dtb r8a779m1-salvator-xs-panel-aa104xd12-dtbs := r8a779m1-salvator-xs.dtb salvator-panel-aa104xd12.dtbo dtb-$(CONFIG_ARCH_R8A77951) += r8a779m1-salvator-xs-panel-aa104xd12.dtb @@ -156,10 +159,25 @@ dtb-$(CONFIG_ARCH_R9A08G045) += r9a08g045s33-smarc-pmod1-type-3a.dtb dtb-$(CONFIG_ARCH_R9A09G011) += r9a09g011-v2mevk2.dtb dtb-$(CONFIG_ARCH_R9A09G047) += r9a09g047e57-smarc.dtb +dtb-$(CONFIG_ARCH_R9A09G047) += r9a09g047e57-smarc-cru-csi-ov5645.dtbo +r9a09g047e57-smarc-cru-csi-ov5645-dtbs := r9a09g047e57-smarc.dtb r9a09g047e57-smarc-cru-csi-ov5645.dtbo +dtb-$(CONFIG_ARCH_R9A09G047) += r9a09g047e57-smarc-cru-csi-ov5645.dtb dtb-$(CONFIG_ARCH_R9A09G056) += r9a09g056n48-rzv2n-evk.dtb +dtb-$(CONFIG_ARCH_R9A09G056) += rzv2-evk-cn15-emmc.dtbo +r9a09g056n48-rzv2n-evk-cn15-emmc-dtbs := r9a09g056n48-rzv2n-evk.dtb rzv2-evk-cn15-emmc.dtbo +dtb-$(CONFIG_ARCH_R9A09G056) += r9a09g056n48-rzv2n-evk-cn15-emmc.dtb +dtb-$(CONFIG_ARCH_R9A09G056) += rzv2-evk-cn15-sd.dtbo +r9a09g056n48-rzv2n-evk-cn15-sd-dtbs := r9a09g056n48-rzv2n-evk.dtb rzv2-evk-cn15-sd.dtbo +dtb-$(CONFIG_ARCH_R9A09G056) += r9a09g056n48-rzv2n-evk-cn15-sd.dtb dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h44-rzv2h-evk.dtb +dtb-$(CONFIG_ARCH_R9A09G057) += rzv2-evk-cn15-emmc.dtbo +r9a09g057h44-rzv2h-evk-cn15-emmc-dtbs := r9a09g057h44-rzv2h-evk.dtb rzv2-evk-cn15-emmc.dtbo +dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h44-rzv2h-evk-cn15-emmc.dtb +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_RCAR_GEN3) += draak-ebisu-panel-aa104xd12.dtbo diff --git a/arch/arm64/boot/dts/renesas/condor-common.dtsi b/arch/arm64/boot/dts/renesas/condor-common.dtsi index a10584150571..9fe9c722187d 100644 --- a/arch/arm64/boot/dts/renesas/condor-common.dtsi +++ b/arch/arm64/boot/dts/renesas/condor-common.dtsi @@ -174,6 +174,7 @@ phy0: ethernet-phy@0 { &i2c0 { pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; + bootph-all; status = "okay"; clock-frequency = <400000>; @@ -230,6 +231,7 @@ eeprom@50 { compatible = "rohm,br24t01", "atmel,24c01"; reg = <0x50>; pagesize = <8>; + bootph-all; }; }; diff --git a/arch/arm64/boot/dts/renesas/draak.dtsi b/arch/arm64/boot/dts/renesas/draak.dtsi index 380b857fd273..71d9f277c966 100644 --- a/arch/arm64/boot/dts/renesas/draak.dtsi +++ b/arch/arm64/boot/dts/renesas/draak.dtsi @@ -308,6 +308,7 @@ &hsusb { &i2c0 { pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; + bootph-all; status = "okay"; ak4613: codec@10 { @@ -449,6 +450,7 @@ eeprom@50 { compatible = "rohm,br24t01", "atmel,24c01"; reg = <0x50>; pagesize = <8>; + bootph-all; }; }; diff --git a/arch/arm64/boot/dts/renesas/ebisu.dtsi b/arch/arm64/boot/dts/renesas/ebisu.dtsi index 4f38b01ae18d..c4c86344fb90 100644 --- a/arch/arm64/boot/dts/renesas/ebisu.dtsi +++ b/arch/arm64/boot/dts/renesas/ebisu.dtsi @@ -327,9 +327,18 @@ phy0: ethernet-phy@0 { }; }; +&can0 { + pinctrl-0 = <&can0_pins>; + pinctrl-names = "default"; + + /* Please only enable canfd or can0 */ + /* status = "okay"; */ +}; + &canfd { pinctrl-0 = <&canfd0_pins>; pinctrl-names = "default"; + /* Please only enable canfd or can0 */ status = "okay"; channel0 { @@ -503,6 +512,7 @@ cs2000: clk-multiplier@4f { }; &i2c_dvfs { + bootph-all; status = "okay"; clock-frequency = <400000>; @@ -526,6 +536,7 @@ eeprom@50 { compatible = "rohm,br24t01", "atmel,24c01"; reg = <0x50>; pagesize = <8>; + bootph-all; }; }; @@ -579,6 +590,11 @@ avb_pins: avb { function = "avb"; }; + can0_pins: can0 { + groups = "can0_data"; + function = "can0"; + }; + canfd0_pins: canfd0 { groups = "canfd0_data"; function = "canfd0"; diff --git a/arch/arm64/boot/dts/renesas/gray-hawk-single.dtsi b/arch/arm64/boot/dts/renesas/gray-hawk-single.dtsi new file mode 100644 index 000000000000..2edb5cb3407b --- /dev/null +++ b/arch/arm64/boot/dts/renesas/gray-hawk-single.dtsi @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the Gray Hawk Single board + * + * Copyright (C) 2023 Renesas Electronics Corp. + * Copyright (C) 2024-2025 Glider bv + */ +/* + * [How to use Sound] + * + * Because R-Car V4M has only 1 SSI, it cannot handle both Playback/Capture + * at the same time. You need to switch the direction which is controlled + * by the GP0_01 pin via amixer. + * + * Playback (CN9500) + * > amixer set "MUX" "Playback" // for GP0_01 + * > amixer set "DAC 1" 85% + * > aplay xxx.wav + * + * Capture (CN9501) + * > amixer set "MUX" "Capture" // for GP0_01 + * > amixer set "Mic 1" 80% + * > amixer set "ADC 1" on + * > amixer set 'ADC 1' 80% + * > arecord xxx hoge.wav + */ + +#include +#include +#include +#include + +/ { + model = "Renesas Gray Hawk Single board"; + compatible = "renesas,gray-hawk-single"; + + aliases { + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + serial0 = &hscif0; + serial1 = &hscif2; + ethernet0 = &avb0; + ethernet1 = &avb1; + ethernet2 = &avb2; + }; + + can_transceiver0: can-phy0 { + compatible = "nxp,tjr1443"; + #phy-cells = <0>; + enable-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; + max-bitrate = <5000000>; + }; + + chosen { + bootargs = "ignore_loglevel rw root=/dev/nfs ip=on"; + stdout-path = "serial0:921600n8"; + }; + + sn65dsi86_refclk: clk-x6 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <38400000>; + }; + + keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&keys_pins>; + pinctrl-names = "default"; + + key-1 { + gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + linux,code = ; + label = "SW47"; + wakeup-source; + debounce-interval = <20>; + }; + + key-2 { + gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; + linux,code = ; + label = "SW48"; + wakeup-source; + debounce-interval = <20>; + }; + + key-3 { + gpios = <&gpio5 2 GPIO_ACTIVE_LOW>; + linux,code = ; + label = "SW49"; + wakeup-source; + debounce-interval = <20>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led-1 { + gpios = <&gpio7 0 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <1>; + }; + + led-2 { + gpios = <&gpio7 1 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <2>; + }; + + led-3 { + gpios = <&gpio7 2 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <3>; + }; + }; + + memory@48000000 { + device_type = "memory"; + /* first 128MB is reserved for secure area. */ + reg = <0x0 0x48000000 0x0 0x78000000>; + }; + + memory@480000000 { + device_type = "memory"; + reg = <0x4 0x80000000 0x1 0x80000000>; + }; + + pcie_clk: clk-9fgv0841-pci { + compatible = "fixed-clock"; + clock-frequency = <100000000>; + #clock-cells = <0>; + }; + + mini-dp-con { + compatible = "dp-connector"; + label = "CN5"; + type = "mini"; + + port { + mini_dp_con_in: endpoint { + remote-endpoint = <&sn65dsi86_out0>; + }; + }; + }; + + reg_1p2v: regulator-1p2v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sound_mux: sound-mux { + compatible = "simple-audio-mux"; + mux-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + state-labels = "Playback", "Capture"; + }; + + sound_card: sound { + compatible = "audio-graph-card2"; + label = "rcar-sound"; + aux-devs = <&sound_mux>; // for GP0_01 + + links = <&rsnd_port>; // AK4619 Audio Codec + }; +}; + +&audio_clkin { + clock-frequency = <24576000>; +}; + +&avb0 { + pinctrl-0 = <&avb0_pins>; + pinctrl-names = "default"; + phy-handle = <&avb0_phy>; + tx-internal-delay-ps = <2000>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + avb0_phy: ethernet-phy@0 { + compatible = "ethernet-phy-id0022.1622", + "ethernet-phy-ieee802.3-c22"; + rxc-skew-ps = <1500>; + reg = <0>; + interrupts-extended = <&gpio7 5 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&gpio7 10 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&avb1 { + pinctrl-0 = <&avb1_pins>; + pinctrl-names = "default"; + phy-handle = <&avb1_phy>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio6 1 GPIO_ACTIVE_LOW>; + reset-post-delay-us = <4000>; + + avb1_phy: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + interrupts-extended = <&gpio6 3 IRQ_TYPE_LEVEL_LOW>; + }; + }; +}; + +&avb2 { + pinctrl-0 = <&avb2_pins>; + pinctrl-names = "default"; + phy-handle = <&avb2_phy>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio5 5 GPIO_ACTIVE_LOW>; + reset-post-delay-us = <4000>; + + avb2_phy: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + interrupts-extended = <&gpio5 4 IRQ_TYPE_LEVEL_LOW>; + }; + }; +}; + +&can_clk { + clock-frequency = <40000000>; +}; + +&canfd { + pinctrl-0 = <&canfd0_pins>, <&canfd1_pins>, <&can_clk_pins>; + pinctrl-names = "default"; + status = "okay"; + + channel0 { + status = "okay"; + phys = <&can_transceiver0>; + }; + + channel1 { + status = "okay"; + }; +}; + +&csi40 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi40_in: endpoint { + bus-type = ; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&max96724_out0>; + }; + }; + }; +}; + +&csi41 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi41_in: endpoint { + bus-type = ; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&max96724_out1>; + }; + }; + }; +}; + +&dsi0 { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + dsi0_out: endpoint { + remote-endpoint = <&sn65dsi86_in0>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&du { + status = "okay"; +}; + +&extal_clk { + clock-frequency = <16666666>; +}; + +&extalr_clk { + clock-frequency = <32768>; +}; + +&gpio1 { + audio-power-hog { + gpio-hog; + gpios = <8 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "Audio-Power"; + }; +}; + +&hscif0 { + pinctrl-0 = <&hscif0_pins>; + pinctrl-names = "default"; + bootph-all; + + uart-has-rtscts; + status = "okay"; +}; + +&hscif2 { + pinctrl-0 = <&hscif2_pins>; + pinctrl-names = "default"; + + uart-has-rtscts; + status = "okay"; +}; + +&i2c0 { + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + + status = "okay"; + clock-frequency = <400000>; + + io_expander_a: gpio@20 { + compatible = "onnn,pca9654"; + reg = <0x20>; + interrupts-extended = <&gpio0 0 IRQ_TYPE_LEVEL_LOW>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + io_expander_b: gpio@21 { + compatible = "onnn,pca9654"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + }; + + io_expander_c: gpio@22 { + compatible = "onnn,pca9654"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + }; + + eeprom@50 { + compatible = "rohm,br24g01", "atmel,24c01"; + label = "cpu-board"; + reg = <0x50>; + pagesize = <8>; + }; + + eeprom@51 { + compatible = "rohm,br24g01", "atmel,24c01"; + label = "breakout-board"; + reg = <0x51>; + pagesize = <8>; + }; + + eeprom@52 { + compatible = "rohm,br24g01", "atmel,24c01"; + label = "csi-dsi-sub-board-id"; + reg = <0x52>; + pagesize = <8>; + }; + + eeprom@53 { + compatible = "rohm,br24g01", "atmel,24c01"; + label = "ethernet-sub-board-id"; + reg = <0x53>; + pagesize = <8>; + }; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + + status = "okay"; + clock-frequency = <400000>; + + bridge@2c { + pinctrl-0 = <&irq0_pins>; + pinctrl-names = "default"; + + compatible = "ti,sn65dsi86"; + reg = <0x2c>; + + clocks = <&sn65dsi86_refclk>; + clock-names = "refclk"; + + interrupts-extended = <&intc_ex 0 IRQ_TYPE_LEVEL_HIGH>; + + enable-gpios = <&gpio1 26 GPIO_ACTIVE_HIGH>; + + vccio-supply = <®_1p8v>; + vpll-supply = <®_1p8v>; + vcca-supply = <®_1p2v>; + vcc-supply = <®_1p2v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sn65dsi86_in0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + sn65dsi86_out0: endpoint { + remote-endpoint = <&mini_dp_con_in>; + }; + }; + }; + }; + + gmsl0: gmsl-deserializer@4e { + compatible = "maxim,max96724"; + reg = <0x4e>; + enable-gpios = <&io_expander_b 0 GPIO_ACTIVE_HIGH>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@4 { + reg = <4>; + max96724_out0: endpoint { + bus-type = ; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi40_in>; + }; + }; + }; + }; + + gmsl1: gmsl-deserializer@4f { + compatible = "maxim,max96724"; + reg = <0x4f>; + enable-gpios = <&io_expander_c 0 GPIO_ACTIVE_HIGH>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@4 { + reg = <4>; + max96724_out1: endpoint { + bus-type = ; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi41_in>; + }; + }; + }; + }; +}; + +&i2c3 { + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + + status = "okay"; + clock-frequency = <400000>; + + codec@10 { + compatible = "asahi-kasei,ak4619"; + reg = <0x10>; + + clocks = <&rcar_sound>; + clock-names = "mclk"; + + #sound-dai-cells = <0>; + port { + ak4619_endpoint: endpoint { + remote-endpoint = <&rsnd_endpoint>; + }; + }; + }; +}; + +&isp0 { + status = "okay"; +}; + +&isp1 { + status = "okay"; +}; + +&mmc0 { + pinctrl-0 = <&mmc_pins>; + pinctrl-1 = <&mmc_pins>; + pinctrl-names = "default", "state_uhs"; + + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + bus-width = <8>; + no-sd; + no-sdio; + non-removable; + full-pwr-cycle-in-suspend; + status = "okay"; +}; + +&pcie0_clkref { + compatible = "gpio-gate-clock"; + clocks = <&pcie_clk>; + enable-gpios = <&gpio4 21 GPIO_ACTIVE_LOW>; + /delete-property/ clock-frequency; +}; + +&pciec0 { + reset-gpios = <&io_expander_a 0 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pfc { + pinctrl-0 = <&scif_clk_pins>, <&scif_clk2_pins>; + pinctrl-names = "default"; + + avb0_pins: avb0 { + mux { + groups = "avb0_link", "avb0_mdio", "avb0_rgmii", + "avb0_txcrefclk"; + function = "avb0"; + }; + + pins_mdio { + groups = "avb0_mdio"; + drive-strength = <21>; + }; + + pins_mii { + groups = "avb0_rgmii"; + drive-strength = <21>; + }; + }; + + avb1_pins: avb1 { + mux { + groups = "avb1_link", "avb1_mdio", "avb1_rgmii", + "avb1_txcrefclk"; + function = "avb1"; + }; + + link { + groups = "avb1_link"; + bias-disable; + }; + + mdio { + groups = "avb1_mdio"; + drive-strength = <24>; + bias-disable; + }; + + rgmii { + groups = "avb1_rgmii"; + drive-strength = <24>; + bias-disable; + }; + }; + + avb2_pins: avb2 { + mux { + groups = "avb2_link", "avb2_mdio", "avb2_rgmii", + "avb2_txcrefclk"; + function = "avb2"; + }; + + link { + groups = "avb2_link"; + bias-disable; + }; + + mdio { + groups = "avb2_mdio"; + drive-strength = <24>; + bias-disable; + }; + + rgmii { + groups = "avb2_rgmii"; + drive-strength = <24>; + bias-disable; + }; + }; + + can_clk_pins: can-clk { + groups = "can_clk"; + function = "can_clk"; + }; + + canfd0_pins: canfd0 { + groups = "canfd0_data"; + function = "canfd0"; + }; + + canfd1_pins: canfd1 { + groups = "canfd1_data"; + function = "canfd1"; + }; + + hscif0_pins: hscif0 { + groups = "hscif0_data", "hscif0_ctrl"; + function = "hscif0"; + }; + + hscif2_pins: hscif2 { + groups = "hscif2_data", "hscif2_ctrl"; + function = "hscif2"; + }; + + i2c0_pins: i2c0 { + groups = "i2c0"; + function = "i2c0"; + }; + + i2c1_pins: i2c1 { + groups = "i2c1"; + function = "i2c1"; + }; + + i2c3_pins: i2c3 { + groups = "i2c3"; + function = "i2c3"; + }; + + irq0_pins: irq0_pins { + groups = "intc_ex_irq0_a"; + function = "intc_ex"; + }; + + keys_pins: keys { + pins = "GP_5_0", "GP_5_1", "GP_5_2"; + bias-pull-up; + }; + + mmc_pins: mmc { + groups = "mmc_data8", "mmc_ctrl", "mmc_ds"; + function = "mmc"; + power-source = <1800>; + }; + + qspi0_pins: qspi0 { + groups = "qspi0_ctrl", "qspi0_data4"; + function = "qspi0"; + }; + + scif_clk_pins: scif-clk { + groups = "scif_clk"; + function = "scif_clk"; + }; + + scif_clk2_pins: scif-clk2 { + groups = "scif_clk2"; + function = "scif_clk2"; + }; + + sound_clk_pins: sound_clk { + groups = "audio_clkin", "audio_clkout"; + function = "audio_clk"; + }; + + sound_pins: sound { + groups = "ssi_ctrl", "ssi_data"; + function = "ssi"; + }; +}; + +&rcar_sound { + pinctrl-0 = <&sound_clk_pins>, <&sound_pins>; + pinctrl-names = "default"; + + status = "okay"; + + /* audio_clkout */ + clock-frequency = <12288000>; + + ports { + rsnd_port: port { + rsnd_endpoint: endpoint { + remote-endpoint = <&ak4619_endpoint>; + bitclock-master; + frame-master; + + /* see above [How to use Sound] */ + playback = <&ssi0>; + capture = <&ssi0>; + }; + }; + }; +}; + +&rpc { + pinctrl-0 = <&qspi0_pins>; + pinctrl-names = "default"; + + status = "okay"; + + flash@0 { + compatible = "spansion,s25fs512s", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <40000000>; + spi-rx-bus-width = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot@0 { + reg = <0x0 0x1200000>; + read-only; + }; + user@1200000 { + reg = <0x1200000 0x2e00000>; + }; + }; + }; +}; + +&rwdt { + timeout-sec = <60>; + status = "okay"; +}; + +&scif_clk { + clock-frequency = <24000000>; +}; + +&scif_clk2 { + clock-frequency = <24000000>; +}; + +&vin00 { + status = "okay"; +}; + +&vin01 { + status = "okay"; +}; + +&vin02 { + status = "okay"; +}; + +&vin03 { + status = "okay"; +}; + +&vin04 { + status = "okay"; +}; + +&vin05 { + status = "okay"; +}; + +&vin06 { + status = "okay"; +}; + +&vin07 { + status = "okay"; +}; + +&vin08 { + status = "okay"; +}; + +&vin09 { + status = "okay"; +}; + +&vin10 { + status = "okay"; +}; + +&vin11 { + status = "okay"; +}; + +&vin12 { + status = "okay"; +}; + +&vin13 { + status = "okay"; +}; + +&vin14 { + status = "okay"; +}; + +&vin15 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi index 6dbf05a55935..8d9ca30c299c 100644 --- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi @@ -798,6 +798,16 @@ pciec0: pcie@e65d0000 { <0 0 0 4 &gic GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>; snps,enable-cdm-check; 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@e65d8000 { @@ -835,6 +845,16 @@ pciec1: pcie@e65d8000 { <0 0 0 4 &gic GIC_SPI 456 IRQ_TYPE_LEVEL_HIGH>; snps,enable-cdm-check; 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@e65d0000 { diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts index 6955eafd8d6a..9ba23129e65e 100644 --- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts +++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts @@ -130,6 +130,13 @@ mini_dp_con_in: endpoint { }; }; + /* Page 26 / PCIe.0/1 CLK */ + pcie_refclk: clk-x8 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + reg_1p2v: regulator-1p2v { compatible = "regulator-fixed"; regulator-name = "fixed-1.2V"; @@ -404,6 +411,14 @@ i2c0_mux2: i2c@2 { reg = <2>; #address-cells = <1>; #size-cells = <0>; + + /* Page 26 / PCIe.0/1 CLK */ + pcie_clk: clk@68 { + compatible = "renesas,9fgv0441"; + reg = <0x68>; + clocks = <&pcie_refclk>; + #clock-cells = <1>; + }; }; i2c0_mux3: i2c@3 { @@ -487,26 +502,38 @@ msiof1_snd_endpoint: endpoint { /* Page 26 / 2230 Key M M.2 */ &pcie0_clkref { - clock-frequency = <100000000>; + status = "disabled"; }; &pciec0 { + clocks = <&cpg CPG_MOD 624>, <&pcie_clk 0>; reset-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; status = "okay"; }; +&pciec0_rp { + clocks = <&pcie_clk 1>; + vpcie3v3-supply = <®_3p3v>; +}; + /* Page 25 / PCIe to USB */ &pcie1_clkref { - clock-frequency = <100000000>; + status = "disabled"; }; &pciec1 { + clocks = <&cpg CPG_MOD 625>, <&pcie_clk 2>; /* uPD720201 is PCIe Gen2 x1 device */ num-lanes = <1>; reset-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; status = "okay"; }; +&pciec1_rp { + clocks = <&pcie_clk 3>; + vpcie3v3-supply = <®_3p3v>; +}; + &pfc { pinctrl-0 = <&scif_clk_pins>; pinctrl-names = "default"; @@ -679,19 +706,6 @@ sound_clk_pins: sound-clk { }; }; -/* Page 30 / Audio_Codec */ -&rcar_sound { - pinctrl-0 = <&sound_clk_pins>; - pinctrl-names = "default"; - - /* It is used for ADG output as DA7212_MCLK */ - - /* audio_clkout */ - clock-frequency = <12288000>; /* 48 kHz groups */ - - status = "okay"; -}; - /* Page 31 / FAN */ &pwm0 { pinctrl-0 = <&pwm0_pins>; @@ -720,6 +734,19 @@ &pwm7 { status = "okay"; }; +/* Page 30 / Audio_Codec */ +&rcar_sound { + pinctrl-0 = <&sound_clk_pins>; + pinctrl-names = "default"; + + /* It is used for ADG output as DA7212_MCLK */ + + /* audio_clkout */ + clock-frequency = <12288000>; /* 48 kHz groups */ + + status = "okay"; +}; + /* Page 16 / QSPI_FLASH */ &rpc { pinctrl-0 = <&qspi0_pins>; diff --git a/arch/arm64/boot/dts/renesas/r8a779h0-gray-hawk-single.dts b/arch/arm64/boot/dts/renesas/r8a779h0-gray-hawk-single.dts index 4d890e0617af..1be7836c41f4 100644 --- a/arch/arm64/boot/dts/renesas/r8a779h0-gray-hawk-single.dts +++ b/arch/arm64/boot/dts/renesas/r8a779h0-gray-hawk-single.dts @@ -5,866 +5,13 @@ * Copyright (C) 2023 Renesas Electronics Corp. * Copyright (C) 2024 Glider bv */ -/* - * [How to use Sound] - * - * Because R-Car V4M has only 1 SSI, it cannot handle both Playback/Capture - * at the same time. You need to switch the direction which is controlled - * by the GP0_01 pin via amixer. - * - * Playback (CN9500) - * > amixer set "MUX" "Playback" // for GP0_01 - * > amixer set "DAC 1" 85% - * > aplay xxx.wav - * - * Capture (CN9501) - * > amixer set "MUX" "Capture" // for GP0_01 - * > amixer set "Mic 1" 80% - * > amixer set "ADC 1" on - * > amixer set 'ADC 1' 80% - * > arecord xxx hoge.wav - */ /dts-v1/; -#include -#include -#include -#include - #include "r8a779h0.dtsi" +#include "gray-hawk-single.dtsi" / { model = "Renesas Gray Hawk Single board based on r8a779h0"; compatible = "renesas,gray-hawk-single", "renesas,r8a779h0"; - - aliases { - i2c0 = &i2c0; - i2c1 = &i2c1; - i2c2 = &i2c2; - i2c3 = &i2c3; - serial0 = &hscif0; - serial1 = &hscif2; - ethernet0 = &avb0; - ethernet1 = &avb1; - ethernet2 = &avb2; - }; - - can_transceiver0: can-phy0 { - compatible = "nxp,tjr1443"; - #phy-cells = <0>; - enable-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; - max-bitrate = <5000000>; - }; - - chosen { - bootargs = "ignore_loglevel rw root=/dev/nfs ip=on"; - stdout-path = "serial0:921600n8"; - }; - - sn65dsi86_refclk: clk-x6 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <38400000>; - }; - - keys { - compatible = "gpio-keys"; - - pinctrl-0 = <&keys_pins>; - pinctrl-names = "default"; - - key-1 { - gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; - linux,code = ; - label = "SW47"; - wakeup-source; - debounce-interval = <20>; - }; - - key-2 { - gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; - linux,code = ; - label = "SW48"; - wakeup-source; - debounce-interval = <20>; - }; - - key-3 { - gpios = <&gpio5 2 GPIO_ACTIVE_LOW>; - linux,code = ; - label = "SW49"; - wakeup-source; - debounce-interval = <20>; - }; - }; - - leds { - compatible = "gpio-leds"; - - led-1 { - gpios = <&gpio7 0 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_INDICATOR; - function-enumerator = <1>; - }; - - led-2 { - gpios = <&gpio7 1 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_INDICATOR; - function-enumerator = <2>; - }; - - led-3 { - gpios = <&gpio7 2 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_INDICATOR; - function-enumerator = <3>; - }; - }; - - memory@48000000 { - device_type = "memory"; - /* first 128MB is reserved for secure area. */ - reg = <0x0 0x48000000 0x0 0x78000000>; - }; - - memory@480000000 { - device_type = "memory"; - reg = <0x4 0x80000000 0x1 0x80000000>; - }; - - pcie_clk: clk-9fgv0841-pci { - compatible = "fixed-clock"; - clock-frequency = <100000000>; - #clock-cells = <0>; - }; - - mini-dp-con { - compatible = "dp-connector"; - label = "CN5"; - type = "mini"; - - port { - mini_dp_con_in: endpoint { - remote-endpoint = <&sn65dsi86_out0>; - }; - }; - }; - - reg_1p2v: regulator-1p2v { - compatible = "regulator-fixed"; - regulator-name = "fixed-1.2V"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_1p8v: regulator-1p8v { - compatible = "regulator-fixed"; - regulator-name = "fixed-1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_3p3v: regulator-3p3v { - compatible = "regulator-fixed"; - regulator-name = "fixed-3.3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - sound_mux: sound-mux { - compatible = "simple-audio-mux"; - mux-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; - state-labels = "Playback", "Capture"; - }; - - sound_card: sound { - compatible = "audio-graph-card2"; - label = "rcar-sound"; - aux-devs = <&sound_mux>; // for GP0_01 - - links = <&rsnd_port>; // AK4619 Audio Codec - }; -}; - -&audio_clkin { - clock-frequency = <24576000>; -}; - -&avb0 { - pinctrl-0 = <&avb0_pins>; - pinctrl-names = "default"; - phy-handle = <&avb0_phy>; - tx-internal-delay-ps = <2000>; - status = "okay"; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - - avb0_phy: ethernet-phy@0 { - compatible = "ethernet-phy-id0022.1622", - "ethernet-phy-ieee802.3-c22"; - rxc-skew-ps = <1500>; - reg = <0>; - interrupts-extended = <&gpio7 5 IRQ_TYPE_LEVEL_LOW>; - reset-gpios = <&gpio7 10 GPIO_ACTIVE_LOW>; - }; - }; -}; - -&avb1 { - pinctrl-0 = <&avb1_pins>; - pinctrl-names = "default"; - phy-handle = <&avb1_phy>; - status = "okay"; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - - reset-gpios = <&gpio6 1 GPIO_ACTIVE_LOW>; - reset-post-delay-us = <4000>; - - avb1_phy: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c45"; - reg = <0>; - interrupts-extended = <&gpio6 3 IRQ_TYPE_LEVEL_LOW>; - }; - }; -}; - -&avb2 { - pinctrl-0 = <&avb2_pins>; - pinctrl-names = "default"; - phy-handle = <&avb2_phy>; - status = "okay"; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - - reset-gpios = <&gpio5 5 GPIO_ACTIVE_LOW>; - reset-post-delay-us = <4000>; - - avb2_phy: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c45"; - reg = <0>; - interrupts-extended = <&gpio5 4 IRQ_TYPE_LEVEL_LOW>; - }; - }; -}; - -&can_clk { - clock-frequency = <40000000>; -}; - -&canfd { - pinctrl-0 = <&canfd0_pins>, <&canfd1_pins>, <&can_clk_pins>; - pinctrl-names = "default"; - status = "okay"; - - channel0 { - status = "okay"; - phys = <&can_transceiver0>; - }; - - channel1 { - status = "okay"; - }; -}; - -&csi40 { - status = "okay"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi40_in: endpoint { - bus-type = ; - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&max96724_out0>; - }; - }; - }; -}; - -&csi41 { - status = "okay"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi41_in: endpoint { - bus-type = ; - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&max96724_out1>; - }; - }; - }; -}; - -&dsi0 { - status = "okay"; - - ports { - port@1 { - reg = <1>; - - dsi0_out: endpoint { - remote-endpoint = <&sn65dsi86_in0>; - data-lanes = <1 2 3 4>; - }; - }; - }; -}; - -&du { - status = "okay"; -}; - -&extal_clk { - clock-frequency = <16666666>; -}; - -&extalr_clk { - clock-frequency = <32768>; -}; - -&gpio1 { - audio-power-hog { - gpio-hog; - gpios = <8 GPIO_ACTIVE_HIGH>; - output-high; - line-name = "Audio-Power"; - }; -}; - -&hscif0 { - pinctrl-0 = <&hscif0_pins>; - pinctrl-names = "default"; - bootph-all; - - uart-has-rtscts; - status = "okay"; -}; - -&hscif2 { - pinctrl-0 = <&hscif2_pins>; - pinctrl-names = "default"; - - uart-has-rtscts; - status = "okay"; -}; - -&i2c0 { - pinctrl-0 = <&i2c0_pins>; - pinctrl-names = "default"; - - status = "okay"; - clock-frequency = <400000>; - - io_expander_a: gpio@20 { - compatible = "onnn,pca9654"; - reg = <0x20>; - interrupts-extended = <&gpio0 0 IRQ_TYPE_LEVEL_LOW>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; - - io_expander_b: gpio@21 { - compatible = "onnn,pca9654"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <2>; - }; - - io_expander_c: gpio@22 { - compatible = "onnn,pca9654"; - reg = <0x22>; - gpio-controller; - #gpio-cells = <2>; - }; - - eeprom@50 { - compatible = "rohm,br24g01", "atmel,24c01"; - label = "cpu-board"; - reg = <0x50>; - pagesize = <8>; - }; - - eeprom@51 { - compatible = "rohm,br24g01", "atmel,24c01"; - label = "breakout-board"; - reg = <0x51>; - pagesize = <8>; - }; - - eeprom@52 { - compatible = "rohm,br24g01", "atmel,24c01"; - label = "csi-dsi-sub-board-id"; - reg = <0x52>; - pagesize = <8>; - }; - - eeprom@53 { - compatible = "rohm,br24g01", "atmel,24c01"; - label = "ethernet-sub-board-id"; - reg = <0x53>; - pagesize = <8>; - }; -}; - -&i2c1 { - pinctrl-0 = <&i2c1_pins>; - pinctrl-names = "default"; - - status = "okay"; - clock-frequency = <400000>; - - bridge@2c { - pinctrl-0 = <&irq0_pins>; - pinctrl-names = "default"; - - compatible = "ti,sn65dsi86"; - reg = <0x2c>; - - clocks = <&sn65dsi86_refclk>; - clock-names = "refclk"; - - interrupts-extended = <&intc_ex 0 IRQ_TYPE_LEVEL_HIGH>; - - enable-gpios = <&gpio1 26 GPIO_ACTIVE_HIGH>; - - vccio-supply = <®_1p8v>; - vpll-supply = <®_1p8v>; - vcca-supply = <®_1p2v>; - vcc-supply = <®_1p2v>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - sn65dsi86_in0: endpoint { - remote-endpoint = <&dsi0_out>; - }; - }; - - port@1 { - reg = <1>; - - sn65dsi86_out0: endpoint { - remote-endpoint = <&mini_dp_con_in>; - }; - }; - }; - }; - - gmsl0: gmsl-deserializer@4e { - compatible = "maxim,max96724"; - reg = <0x4e>; - enable-gpios = <&io_expander_b 0 GPIO_ACTIVE_HIGH>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@4 { - reg = <4>; - max96724_out0: endpoint { - bus-type = ; - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&csi40_in>; - }; - }; - }; - }; - - gmsl1: gmsl-deserializer@4f { - compatible = "maxim,max96724"; - reg = <0x4f>; - enable-gpios = <&io_expander_c 0 GPIO_ACTIVE_HIGH>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@4 { - reg = <4>; - max96724_out1: endpoint { - bus-type = ; - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&csi41_in>; - }; - }; - }; - }; -}; - -&i2c3 { - pinctrl-0 = <&i2c3_pins>; - pinctrl-names = "default"; - - status = "okay"; - clock-frequency = <400000>; - - codec@10 { - compatible = "asahi-kasei,ak4619"; - reg = <0x10>; - - clocks = <&rcar_sound>; - clock-names = "mclk"; - - #sound-dai-cells = <0>; - port { - ak4619_endpoint: endpoint { - remote-endpoint = <&rsnd_endpoint>; - }; - }; - }; -}; - -&isp0 { - status = "okay"; -}; - -&isp1 { - status = "okay"; -}; - -&mmc0 { - pinctrl-0 = <&mmc_pins>; - pinctrl-1 = <&mmc_pins>; - pinctrl-names = "default", "state_uhs"; - - vmmc-supply = <®_3p3v>; - vqmmc-supply = <®_1p8v>; - mmc-hs200-1_8v; - mmc-hs400-1_8v; - bus-width = <8>; - no-sd; - no-sdio; - non-removable; - full-pwr-cycle-in-suspend; - status = "okay"; -}; - -&pcie0_clkref { - compatible = "gpio-gate-clock"; - clocks = <&pcie_clk>; - enable-gpios = <&gpio4 21 GPIO_ACTIVE_LOW>; - /delete-property/ clock-frequency; -}; - -&pciec0 { - reset-gpios = <&io_expander_a 0 GPIO_ACTIVE_LOW>; - status = "okay"; -}; - -&pfc { - pinctrl-0 = <&scif_clk_pins>, <&scif_clk2_pins>; - pinctrl-names = "default"; - - avb0_pins: avb0 { - mux { - groups = "avb0_link", "avb0_mdio", "avb0_rgmii", - "avb0_txcrefclk"; - function = "avb0"; - }; - - pins_mdio { - groups = "avb0_mdio"; - drive-strength = <21>; - }; - - pins_mii { - groups = "avb0_rgmii"; - drive-strength = <21>; - }; - }; - - avb1_pins: avb1 { - mux { - groups = "avb1_link", "avb1_mdio", "avb1_rgmii", - "avb1_txcrefclk"; - function = "avb1"; - }; - - link { - groups = "avb1_link"; - bias-disable; - }; - - mdio { - groups = "avb1_mdio"; - drive-strength = <24>; - bias-disable; - }; - - rgmii { - groups = "avb1_rgmii"; - drive-strength = <24>; - bias-disable; - }; - }; - - avb2_pins: avb2 { - mux { - groups = "avb2_link", "avb2_mdio", "avb2_rgmii", - "avb2_txcrefclk"; - function = "avb2"; - }; - - link { - groups = "avb2_link"; - bias-disable; - }; - - mdio { - groups = "avb2_mdio"; - drive-strength = <24>; - bias-disable; - }; - - rgmii { - groups = "avb2_rgmii"; - drive-strength = <24>; - bias-disable; - }; - }; - - can_clk_pins: can-clk { - groups = "can_clk"; - function = "can_clk"; - }; - - canfd0_pins: canfd0 { - groups = "canfd0_data"; - function = "canfd0"; - }; - - canfd1_pins: canfd1 { - groups = "canfd1_data"; - function = "canfd1"; - }; - - hscif0_pins: hscif0 { - groups = "hscif0_data", "hscif0_ctrl"; - function = "hscif0"; - }; - - hscif2_pins: hscif2 { - groups = "hscif2_data", "hscif2_ctrl"; - function = "hscif2"; - }; - - i2c0_pins: i2c0 { - groups = "i2c0"; - function = "i2c0"; - }; - - i2c1_pins: i2c1 { - groups = "i2c1"; - function = "i2c1"; - }; - - i2c3_pins: i2c3 { - groups = "i2c3"; - function = "i2c3"; - }; - - irq0_pins: irq0_pins { - groups = "intc_ex_irq0_a"; - function = "intc_ex"; - }; - - keys_pins: keys { - pins = "GP_5_0", "GP_5_1", "GP_5_2"; - bias-pull-up; - }; - - mmc_pins: mmc { - groups = "mmc_data8", "mmc_ctrl", "mmc_ds"; - function = "mmc"; - power-source = <1800>; - }; - - qspi0_pins: qspi0 { - groups = "qspi0_ctrl", "qspi0_data4"; - function = "qspi0"; - }; - - scif_clk_pins: scif-clk { - groups = "scif_clk"; - function = "scif_clk"; - }; - - scif_clk2_pins: scif-clk2 { - groups = "scif_clk2"; - function = "scif_clk2"; - }; - - sound_clk_pins: sound_clk { - groups = "audio_clkin", "audio_clkout"; - function = "audio_clk"; - }; - - sound_pins: sound { - groups = "ssi_ctrl", "ssi_data"; - function = "ssi"; - }; -}; - -&rcar_sound { - pinctrl-0 = <&sound_clk_pins>, <&sound_pins>; - pinctrl-names = "default"; - - status = "okay"; - - /* audio_clkout */ - clock-frequency = <12288000>; - - ports { - rsnd_port: port { - rsnd_endpoint: endpoint { - remote-endpoint = <&ak4619_endpoint>; - bitclock-master; - frame-master; - - /* see above [How to use Sound] */ - playback = <&ssi0>; - capture = <&ssi0>; - }; - }; - }; -}; - -&rpc { - pinctrl-0 = <&qspi0_pins>; - pinctrl-names = "default"; - - status = "okay"; - - flash@0 { - compatible = "spansion,s25fs512s", "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <40000000>; - spi-rx-bus-width = <4>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - boot@0 { - reg = <0x0 0x1200000>; - read-only; - }; - user@1200000 { - reg = <0x1200000 0x2e00000>; - }; - }; - }; -}; - -&rwdt { - timeout-sec = <60>; - status = "okay"; -}; - -&scif_clk { - clock-frequency = <24000000>; -}; - -&scif_clk2 { - clock-frequency = <24000000>; -}; - -&vin00 { - status = "okay"; -}; - -&vin01 { - status = "okay"; -}; - -&vin02 { - status = "okay"; -}; - -&vin03 { - status = "okay"; -}; - -&vin04 { - status = "okay"; -}; - -&vin05 { - status = "okay"; -}; - -&vin06 { - status = "okay"; -}; - -&vin07 { - status = "okay"; -}; - -&vin08 { - status = "okay"; -}; - -&vin09 { - status = "okay"; -}; - -&vin10 { - status = "okay"; -}; - -&vin11 { - status = "okay"; -}; - -&vin12 { - status = "okay"; -}; - -&vin13 { - status = "okay"; -}; - -&vin14 { - status = "okay"; -}; - -&vin15 { - status = "okay"; }; diff --git a/arch/arm64/boot/dts/renesas/r8a779h2-gray-hawk-single.dts b/arch/arm64/boot/dts/renesas/r8a779h2-gray-hawk-single.dts new file mode 100644 index 000000000000..aeb32c77099e --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a779h2-gray-hawk-single.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the R-Car V4M-7 Gray Hawk Single board + * + * Copyright (C) 2025 Glider bv + */ + +/dts-v1/; + +#include "r8a779h2.dtsi" +#include "gray-hawk-single.dtsi" + +/ { + model = "Renesas Gray Hawk Single board based on r8a779h2"; + compatible = "renesas,gray-hawk-single", "renesas,r8a779h2", + "renesas,r8a779h0"; +}; diff --git a/arch/arm64/boot/dts/renesas/r8a779h2.dtsi b/arch/arm64/boot/dts/renesas/r8a779h2.dtsi new file mode 100644 index 000000000000..2707d2d36766 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a779h2.dtsi @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the R-Car V4M-7 (R8A779H2) SoC + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#include "r8a779h0.dtsi" + +/ { + compatible = "renesas,r8a779h2", "renesas,r8a779h0"; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a09g047.dtsi b/arch/arm64/boot/dts/renesas/r9a09g047.dtsi index 876f70fed433..e4fac7e0d764 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g047.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g047.dtsi @@ -280,6 +280,27 @@ sys: system-controller@10430000 { resets = <&cpg 0x30>; }; + xspi: spi@11030000 { + compatible = "renesas,r9a09g047-xspi"; + reg = <0 0x11030000 0 0x10000>, + <0 0x20000000 0 0x10000000>; + reg-names = "regs", "dirmap"; + interrupts = , + ; + interrupt-names = "pulse", "err_pulse"; + clocks = <&cpg CPG_MOD 0x9f>, + <&cpg CPG_MOD 0xa0>, + <&cpg CPG_CORE R9A09G047_SPI_CLK_SPI>, + <&cpg CPG_MOD 0xa1>; + clock-names = "ahb", "axi", "spi", "spix2"; + resets = <&cpg 0xa3>, <&cpg 0xa4>; + reset-names = "hresetn", "aresetn"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + scif0: serial@11c01400 { compatible = "renesas,scif-r9a09g047", "renesas,scif-r9a09g057"; reg = <0 0x11c01400 0 0x400>; @@ -669,6 +690,284 @@ sdhi2_vqmmc: vqmmc-regulator { status = "disabled"; }; }; + + eth0: ethernet@15c30000 { + compatible = "renesas,r9a09g047-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c30000 0 0x10000>; + clocks = <&cpg CPG_MOD 0xbd>, <&cpg CPG_MOD 0xbc>, + <&cpg CPG_CORE R9A09G047_GBETH_0_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xb8>, <&cpg CPG_MOD 0xb9>, + <&cpg CPG_MOD 0xba>, <&cpg CPG_MOD 0xbb>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + resets = <&cpg 0xb0>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup0>; + snps,mtl-tx-config = <&mtl_tx_setup0>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup0: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup0: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + + eth1: ethernet@15c40000 { + compatible = "renesas,r9a09g047-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c40000 0 0x10000>; + clocks = <&cpg CPG_MOD 0xc3>, <&cpg CPG_MOD 0xc2>, + <&cpg CPG_CORE R9A09G047_GBETH_1_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xbe>, <&cpg CPG_MOD 0xbf>, + <&cpg CPG_MOD 0xc0>, <&cpg CPG_MOD 0xc1>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + resets = <&cpg 0xb1>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup1>; + snps,mtl-tx-config = <&mtl_tx_setup1>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio1: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup1: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup1: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + + cru: video@16000000 { + compatible = "renesas,r9a09g047-cru"; + reg = <0 0x16000000 0 0x400>; + clocks = <&cpg CPG_MOD 0xd3>, + <&cpg CPG_MOD 0xd4>, + <&cpg CPG_MOD 0xd2>; + clock-names = "video", "apb", "axi"; + interrupts = , + , + , + , + ; + interrupt-names = "image_conv", "axi_mst_err", + "vd_addr_wend", "sd_addr_wend", + "vsd_addr_wend"; + resets = <&cpg 0xc5>, <&cpg 0xc6>; + reset-names = "presetn", "aresetn"; + power-domains = <&cpg>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <1>; + crucsi2: endpoint@0 { + reg = <0>; + remote-endpoint = <&csi2cru>; + }; + }; + }; + }; + + csi2: csi2@16000400 { + compatible = "renesas,r9a09g047-csi2", "renesas,r9a09g057-csi2"; + reg = <0 0x16000400 0 0xc00>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xd3>, <&cpg CPG_MOD 0xd4>; + clock-names = "video", "apb"; + resets = <&cpg 0xc5>, <&cpg 0xc7>; + reset-names = "presetn", "cmn-rstb"; + power-domains = <&cpg>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + csi2cru: endpoint@0 { + reg = <0>; + remote-endpoint = <&crucsi2>; + }; + }; + }; + }; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + snps,blen = <16 8 4 0 0 0 0>; }; timer { diff --git a/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc-cru-csi-ov5645.dtso b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc-cru-csi-ov5645.dtso new file mode 100644 index 000000000000..0f18f68f8120 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc-cru-csi-ov5645.dtso @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree overlay for the RZ/G3E SMARC EVK with OV5645 camera + * connected to CSI and CRU enabled. + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +#define OV5645_PARENT_I2C i2c0 +#include "rz-smarc-cru-csi-ov5645.dtsi" + +&ov5645 { + enable-gpios = <&pinctrl RZG3E_GPIO(D, 6) GPIO_ACTIVE_HIGH>; + reset-gpios = <&pinctrl RZG3E_GPIO(D, 7) GPIO_ACTIVE_LOW>; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts index 1f5e61a73c35..1e67f0a2a945 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts @@ -8,6 +8,7 @@ /dts-v1/; /* Switch selection settings */ +#define SW_LCD_EN 0 #define SW_GPIO8_CAN0_STB 0 #define SW_GPIO9_CAN1_STB 0 #define SW_LCD_EN 0 @@ -15,7 +16,16 @@ #define SW_SD0_DEV_SEL 0 #define SW_SDIO_M2E 0 +#define PMOD_GPIO4 0 +#define PMOD_GPIO6 0 +#define PMOD_GPIO7 0 + +#define KEY_1_GPIO RZG3E_GPIO(3, 1) +#define KEY_2_GPIO RZG3E_GPIO(8, 4) +#define KEY_3_GPIO RZG3E_GPIO(8, 5) + #include +#include #include #include "r9a09g047e57.dtsi" #include "rzg3e-smarc-som.dtsi" @@ -74,6 +84,34 @@ &can_transceiver1 { }; #endif +&i2c0 { + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; +}; + +&keys { + key-sleep { + pinctrl-0 = <&nmi_pins>; + pinctrl-names = "default"; + + interrupts-extended = <&icu 0 IRQ_TYPE_EDGE_FALLING>; + linux,code = ; + label = "SLEEP"; + debounce-interval = <20>; + }; +#if PMOD_GPIO4 + /delete-node/ key-1; +#endif + +#if SW_LCD_EN || PMOD_GPIO6 + /delete-node/ key-2; +#endif + +#if SW_LCD_EN || PMOD_GPIO7 + /delete-node/ key-3; +#endif +}; + &pinctrl { canfd_pins: canfd { can1_pins: can1 { @@ -87,6 +125,15 @@ can4_pins: can4 { }; }; + i2c0_pins: i2c0 { + pinmux = , /* SCL0 */ + ; /* SDA0 */ + }; + + nmi_pins: nmi { + pinmux = ; /* NMI */ + }; + scif_pins: scif { pins = "SCIF_TXD", "SCIF_RXD"; renesas,output-impedance = <1>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g056.dtsi b/arch/arm64/boot/dts/renesas/r9a09g056.dtsi index 90964bd864cc..10d3b9727ea5 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g056.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g056.dtsi @@ -123,6 +123,35 @@ L3_CA55: cache-controller-0 { }; }; + gpu_opp_table: opp-table-1 { + compatible = "operating-points-v2"; + + opp-630000000 { + opp-hz = /bits/ 64 <630000000>; + opp-microvolt = <800000>; + }; + + opp-315000000 { + opp-hz = /bits/ 64 <315000000>; + opp-microvolt = <800000>; + }; + + opp-157500000 { + opp-hz = /bits/ 64 <157500000>; + opp-microvolt = <800000>; + }; + + opp-78750000 { + opp-hz = /bits/ 64 <78750000>; + opp-microvolt = <800000>; + }; + + opp-19687500 { + opp-hz = /bits/ 64 <19687500>; + opp-microvolt = <800000>; + }; + }; + psci { compatible = "arm,psci-1.0", "arm,psci-0.2"; method = "smc"; @@ -177,6 +206,147 @@ sys: system-controller@10430000 { resets = <&cpg 0x30>; }; + xspi: spi@11030000 { + compatible = "renesas,r9a09g056-xspi", "renesas,r9a09g047-xspi"; + reg = <0 0x11030000 0 0x10000>, + <0 0x20000000 0 0x10000000>; + reg-names = "regs", "dirmap"; + interrupts = , + ; + interrupt-names = "pulse", "err_pulse"; + clocks = <&cpg CPG_MOD 0x9f>, + <&cpg CPG_MOD 0xa0>, + <&cpg CPG_CORE R9A09G056_SPI_CLK_SPI>, + <&cpg CPG_MOD 0xa1>; + clock-names = "ahb", "axi", "spi", "spix2"; + resets = <&cpg 0xa3>, <&cpg 0xa4>; + reset-names = "hresetn", "aresetn"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + ostm0: timer@11800000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x11800000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x43>; + resets = <&cpg 0x6d>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm1: timer@11801000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x11801000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x44>; + resets = <&cpg 0x6e>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm2: timer@14000000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x14000000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x45>; + resets = <&cpg 0x6f>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm3: timer@14001000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x14001000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x46>; + resets = <&cpg 0x70>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm4: timer@12c00000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x12c00000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x47>; + resets = <&cpg 0x71>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm5: timer@12c01000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x12c01000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x48>; + resets = <&cpg 0x72>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm6: timer@12c02000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x12c02000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x49>; + resets = <&cpg 0x73>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ostm7: timer@12c03000 { + compatible = "renesas,r9a09g056-ostm", "renesas,ostm"; + reg = <0x0 0x12c03000 0x0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 0x4a>; + resets = <&cpg 0x74>; + power-domains = <&cpg>; + 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>; + clocks = <&cpg CPG_MOD 0x4d>, <&cpg CPG_MOD 0x4e>; + clock-names = "pclk", "oscclk"; + resets = <&cpg 0x76>; + power-domains = <&cpg>; + 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>; + power-domains = <&cpg>; + status = "disabled"; + }; + scif: serial@11c01400 { compatible = "renesas,scif-r9a09g056", "renesas,scif-r9a09g057"; @@ -199,6 +369,217 @@ scif: serial@11c01400 { status = "disabled"; }; + i2c0: i2c@14400400 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14400400 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x94>; + resets = <&cpg 0x98>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@14400800 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14400800 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x95>; + resets = <&cpg 0x99>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: i2c@14400c00 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14400c00 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x96>; + resets = <&cpg 0x9a>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c3: i2c@14401000 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14401000 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x97>; + resets = <&cpg 0x9b>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@14401400 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14401400 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x98>; + resets = <&cpg 0x9c>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c5: i2c@14401800 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14401800 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x99>; + resets = <&cpg 0x9d>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c6: i2c@14401c00 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14401c00 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x9a>; + resets = <&cpg 0x9e>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c7: i2c@14402000 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x14402000 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x9b>; + resets = <&cpg 0x9f>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c8: i2c@11c01000 { + compatible = "renesas,riic-r9a09g056", "renesas,riic-r9a09g057"; + reg = <0 0x11c01000 0 0x400>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-names = "tei", "ri", "ti", "spi", "sti", + "naki", "ali", "tmoi"; + clocks = <&cpg CPG_MOD 0x93>; + resets = <&cpg 0xa0>; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gpu: gpu@14850000 { + compatible = "renesas,r9a09g056-mali", + "arm,mali-bifrost"; + reg = <0x0 0x14850000 0x0 0x10000>; + interrupts = , + , + , + ; + interrupt-names = "job", "mmu", "gpu", "event"; + clocks = <&cpg CPG_MOD 0xf0>, + <&cpg CPG_MOD 0xf1>, + <&cpg CPG_MOD 0xf2>; + clock-names = "gpu", "bus", "bus_ace"; + resets = <&cpg 0xdd>, + <&cpg 0xde>, + <&cpg 0xdf>; + reset-names = "rst", "axi_rst", "ace_rst"; + power-domains = <&cpg>; + operating-points-v2 = <&gpu_opp_table>; + status = "disabled"; + }; + gic: interrupt-controller@14900000 { compatible = "arm,gic-v3"; reg = <0x0 0x14900000 0 0x20000>, @@ -209,6 +590,72 @@ gic: interrupt-controller@14900000 { interrupts = ; }; + ohci0: usb@15800000 { + compatible = "generic-ohci"; + reg = <0 0x15800000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb6>; + resets = <&usb20phyrst>, <&cpg 0xac>; + phys = <&usb2_phy0 1>; + phy-names = "usb"; + power-domains = <&cpg>; + status = "disabled"; + }; + + ehci0: usb@15800100 { + compatible = "generic-ehci"; + reg = <0 0x15800100 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb6>; + resets = <&usb20phyrst>, <&cpg 0xac>; + phys = <&usb2_phy0 2>; + phy-names = "usb"; + companion = <&ohci0>; + power-domains = <&cpg>; + status = "disabled"; + }; + + usb2_phy0: usb-phy@15800200 { + compatible = "renesas,usb2-phy-r9a09g056", "renesas,usb2-phy-r9a09g057"; + reg = <0 0x15800200 0 0x700>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, + <&cpg CPG_CORE R9A09G056_USB2_0_CLK_CORE0>; + clock-names = "fck", "usb_x1"; + resets = <&usb20phyrst>; + #phy-cells = <1>; + power-domains = <&cpg>; + status = "disabled"; + }; + + hsusb: usb@15820000 { + compatible = "renesas,usbhs-r9a09g056", + "renesas,rzg2l-usbhs"; + reg = <0 0x15820000 0 0x10000>; + interrupts = , + , + , + ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb5>; + resets = <&usb20phyrst>, + <&cpg 0xae>; + phys = <&usb2_phy0 3>; + phy-names = "usb"; + power-domains = <&cpg>; + status = "disabled"; + }; + + usb20phyrst: usb20phy-reset@15830000 { + compatible = "renesas,r9a09g056-usb2phy-reset", + "renesas,r9a09g057-usb2phy-reset"; + reg = <0 0x15830000 0 0x10000>; + clocks = <&cpg CPG_MOD 0xb6>; + resets = <&cpg 0xaf>; + power-domains = <&cpg>; + #reset-cells = <0>; + status = "disabled"; + }; + sdhi0: mmc@15c00000 { compatible = "renesas,sdhi-r9a09g056", "renesas,sdhi-r9a09g057"; reg = <0x0 0x15c00000 0 0x10000>; @@ -268,6 +715,215 @@ sdhi2_vqmmc: vqmmc-regulator { status = "disabled"; }; }; + + eth0: ethernet@15c30000 { + compatible = "renesas,r9a09g056-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c30000 0 0x10000>; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + clocks = <&cpg CPG_MOD 0xbd>, <&cpg CPG_MOD 0xbc>, + <&cpg CPG_CORE R9A09G056_GBETH_0_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xb8>, <&cpg CPG_MOD 0xb9>, + <&cpg CPG_MOD 0xba>, <&cpg CPG_MOD 0xbb>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + resets = <&cpg 0xb0>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup0>; + snps,mtl-tx-config = <&mtl_tx_setup0>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup0: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup0: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + + eth1: ethernet@15c40000 { + compatible = "renesas,r9a09g056-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c40000 0 0x10000>; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + clocks = <&cpg CPG_MOD 0xc3>, <&cpg CPG_MOD 0xc2>, + <&cpg CPG_CORE R9A09G056_GBETH_1_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xbe>, <&cpg CPG_MOD 0xbf>, + <&cpg CPG_MOD 0xc0>, <&cpg CPG_MOD 0xc1>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + resets = <&cpg 0xb1>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup1>; + snps,mtl-tx-config = <&mtl_tx_setup1>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio1: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup1: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup1: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + snps,blen = <16 8 4 0 0 0 0>; }; timer { diff --git a/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts index 24343fce7f53..03aeea781186 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts @@ -15,6 +15,15 @@ / { compatible = "renesas,rzv2n-evk", "renesas,r9a09g056n48", "renesas,r9a09g056"; aliases { + ethernet0 = ð0; + ethernet1 = ð1; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c6 = &i2c6; + i2c7 = &i2c7; + i2c8 = &i2c8; mmc1 = &sdhi1; serial0 = &scif; }; @@ -30,6 +39,24 @@ memory@48000000 { reg = <0x0 0x48000000 0x1 0xf8000000>; }; + reg_0p8v: regulator-0p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-0.8V"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + reg_3p3v: regulator-3p3v { compatible = "regulator-fixed"; regulator-name = "fixed-3.3V"; @@ -48,13 +75,232 @@ vqmmc_sdhi1: regulator-vqmmc-sdhi1 { gpios-states = <0>; states = <3300000 0>, <1800000 1>; }; + + /* 32.768kHz crystal */ + x6: x6-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; }; &audio_extal_clk { clock-frequency = <22579200>; }; +&ehci0 { + dr_mode = "otg"; + status = "okay"; +}; + +ð0 { + pinctrl-0 = <ð0_pins>; + pinctrl-names = "default"; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + +ð1 { + pinctrl-0 = <ð1_pins>; + pinctrl-names = "default"; + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <®_0p8v>; +}; + +&hsusb { + dr_mode = "otg"; + status = "okay"; +}; + +&i2c0 { + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c2 { + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c3 { + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c6 { + pinctrl-0 = <&i2c6_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c7 { + pinctrl-0 = <&i2c7_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; +}; + +&i2c8 { + pinctrl-0 = <&i2c8_pins>; + pinctrl-names = "default"; + clock-frequency = <400000>; + status = "okay"; + + raa215300: pmic@12 { + compatible = "renesas,raa215300"; + reg = <0x12>, <0x6f>; + reg-names = "main", "rtc"; + clocks = <&x6>; + clock-names = "xin"; + }; +}; + +&mdio0 { + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + reg = <0>; + rxc-skew-psec = <0>; + txc-skew-psec = <0>; + rxdv-skew-psec = <0>; + txdv-skew-psec = <0>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + +&mdio1 { + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + reg = <0>; + rxc-skew-psec = <0>; + txc-skew-psec = <0>; + rxdv-skew-psec = <0>; + txdv-skew-psec = <0>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + +&ohci0 { + dr_mode = "otg"; + status = "okay"; +}; + +&ostm0 { + status = "okay"; +}; + +&ostm1 { + status = "okay"; +}; + +&ostm2 { + status = "okay"; +}; + +&ostm3 { + status = "okay"; +}; + +&ostm4 { + status = "okay"; +}; + +&ostm5 { + status = "okay"; +}; + +&ostm6 { + status = "okay"; +}; + +&ostm7 { + status = "okay"; +}; + &pinctrl { + eth0_pins: eth0 { + pins = "ET0_TXC_TXCLK"; + output-enable; + }; + + eth1_pins: eth1 { + pins = "ET1_TXC_TXCLK"; + output-enable; + }; + + i2c0_pins: i2c0 { + pinmux = , /* I2C0_SDA */ + ; /* I2C0_SCL */ + }; + + i2c1_pins: i2c1 { + pinmux = , /* I2C1_SDA */ + ; /* I2C1_SCL */ + }; + + i2c2_pins: i2c2 { + pinmux = , /* I2C2_SDA */ + ; /* I2C2_SCL */ + }; + + i2c3_pins: i2c3 { + pinmux = , /* I2C3_SDA */ + ; /* I2C3_SCL */ + }; + + i2c6_pins: i2c6 { + pinmux = , /* I2C6_SDA */ + ; /* I2C6_SCL */ + /* There are no pull-up resistors on the EVK, so enable the internal pull-up */ + bias-pull-up; + }; + + i2c7_pins: i2c7 { + pinmux = , /* I2C7_SDA */ + ; /* I2C7_SCL */ + /* There are no pull-up resistors on the EVK, so enable the internal pull-up */ + bias-pull-up; + }; + + i2c8_pins: i2c8 { + pinmux = , /* I2C8_SDA */ + ; /* I2C8_SCL */ + }; + scif_pins: scif { pins = "SCIF_TXD", "SCIF_RXD"; renesas,output-impedance = <1>; @@ -85,6 +331,28 @@ sd1-dat-cmd { slew-rate = <0>; }; }; + + usb20_pins: usb20 { + ovc { + pinmux = ; /* OVC */ + }; + + vbus { + pinmux = ; /* VBUS */ + }; + }; + + xspi_pins: xspi0 { + ctrl { + pins = "XSPI0_RESET0N", "XSPI0_CS0N", "XSPI0_CKP"; + output-enable; + }; + + io { + pins = "XSPI0_IO0", "XSPI0_IO1", "XSPI0_IO2", "XSPI0_IO3"; + renesas,output-impedance = <3>; + }; + }; }; &qextal_clk { @@ -112,3 +380,61 @@ &sdhi1 { sd-uhs-sdr104; status = "okay"; }; + +&usb20phyrst { + status = "okay"; +}; + +&usb2_phy0 { + pinctrl-0 = <&usb20_pins>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&wdt1 { + status = "okay"; +}; + +&xspi { + pinctrl-0 = <&xspi_pins>; + pinctrl-names = "default"; + /* + * MT25QU512ABB8E12 flash chip is capable of running at 166MHz + * clock frequency. Set the clock frequency to the maximum 133MHz + * supported by the RZ/V2N SoC. + */ + assigned-clocks = <&cpg CPG_CORE R9A09G056_SPI_CLK_SPI>; + assigned-clock-rates = <133333334>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + vcc-supply = <®_1p8v>; + m25p,fast-read; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bl2"; + reg = <0x00000000 0x00060000>; + }; + + partition@60000 { + label = "fip"; + reg = <0x00060000 0x1fa0000>; + }; + + partition@2000000 { + label = "user"; + reg = <0x2000000 0x2000000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi index 0f3501951409..044f2a22f161 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi @@ -280,6 +280,27 @@ sys: system-controller@10430000 { resets = <&cpg 0x30>; }; + xspi: spi@11030000 { + compatible = "renesas,r9a09g057-xspi", "renesas,r9a09g047-xspi"; + reg = <0 0x11030000 0 0x10000>, + <0 0x20000000 0 0x10000000>; + reg-names = "regs", "dirmap"; + interrupts = , + ; + interrupt-names = "pulse", "err_pulse"; + clocks = <&cpg CPG_MOD 0x9f>, + <&cpg CPG_MOD 0xa0>, + <&cpg CPG_CORE R9A09G057_SPI_CLK_SPI>, + <&cpg CPG_MOD 0xa1>; + clock-names = "ahb", "axi", "spi", "spix2"; + resets = <&cpg 0xa3>, <&cpg 0xa4>; + reset-names = "hresetn", "aresetn"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + dmac0: dma-controller@11400000 { compatible = "renesas,r9a09g057-dmac"; reg = <0 0x11400000 0 0x10000>; @@ -807,6 +828,119 @@ gic: interrupt-controller@14900000 { interrupts = ; }; + ohci0: usb@15800000 { + compatible = "generic-ohci"; + reg = <0 0x15800000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb6>; + resets = <&usb20phyrst>, <&cpg 0xac>; + phys = <&usb2_phy0 1>; + phy-names = "usb"; + power-domains = <&cpg>; + status = "disabled"; + }; + + ohci1: usb@15810000 { + compatible = "generic-ohci"; + reg = <0 0x15810000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb4>, <&cpg CPG_MOD 0xb7>; + resets = <&usb21phyrst>, <&cpg 0xad>; + phys = <&usb2_phy1 1>; + phy-names = "usb"; + power-domains = <&cpg>; + status = "disabled"; + }; + + ehci0: usb@15800100 { + compatible = "generic-ehci"; + reg = <0 0x15800100 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb6>; + resets = <&usb20phyrst>, <&cpg 0xac>; + phys = <&usb2_phy0 2>; + phy-names = "usb"; + companion = <&ohci0>; + power-domains = <&cpg>; + status = "disabled"; + }; + + ehci1: usb@15810100 { + compatible = "generic-ehci"; + reg = <0 0x15810100 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb4>, <&cpg CPG_MOD 0xb7>; + resets = <&usb21phyrst>, <&cpg 0xad>; + phys = <&usb2_phy1 2>; + phy-names = "usb"; + companion = <&ohci1>; + power-domains = <&cpg>; + status = "disabled"; + }; + + usb2_phy0: usb-phy@15800200 { + compatible = "renesas,usb2-phy-r9a09g057"; + reg = <0 0x15800200 0 0x700>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb3>, + <&cpg CPG_CORE R9A09G057_USB2_0_CLK_CORE0>; + clock-names = "fck", "usb_x1"; + resets = <&usb20phyrst>; + #phy-cells = <1>; + power-domains = <&cpg>; + status = "disabled"; + }; + + usb2_phy1: usb-phy@15810200 { + compatible = "renesas,usb2-phy-r9a09g057"; + reg = <0 0x15810200 0 0x700>; + interrupts = ; + clocks = <&cpg CPG_MOD 0xb4>, + <&cpg CPG_CORE R9A09G057_USB2_0_CLK_CORE1>; + clock-names = "fck", "usb_x1"; + resets = <&usb21phyrst>; + #phy-cells = <1>; + power-domains = <&cpg>; + status = "disabled"; + }; + + hsusb: usb@15820000 { + compatible = "renesas,usbhs-r9a09g057", + "renesas,rzg2l-usbhs"; + reg = <0 0x15820000 0 0x10000>; + interrupts = , + , + , + ; + clocks = <&cpg CPG_MOD 0xb3>, <&cpg CPG_MOD 0xb5>; + resets = <&usb20phyrst>, + <&cpg 0xae>; + phys = <&usb2_phy0 3>; + phy-names = "usb"; + power-domains = <&cpg>; + status = "disabled"; + }; + + usb20phyrst: usb20phy-reset@15830000 { + compatible = "renesas,r9a09g057-usb2phy-reset"; + reg = <0 0x15830000 0 0x10000>; + clocks = <&cpg CPG_MOD 0xb6>; + resets = <&cpg 0xaf>; + power-domains = <&cpg>; + #reset-cells = <0>; + status = "disabled"; + }; + + usb21phyrst: usb21phy-reset@15840000 { + compatible = "renesas,r9a09g057-usb2phy-reset"; + reg = <0 0x15840000 0 0x10000>; + clocks = <&cpg CPG_MOD 0xb7>; + resets = <&cpg 0xaf>; + power-domains = <&cpg>; + #reset-cells = <0>; + status = "disabled"; + }; + sdhi0: mmc@15c00000 { compatible = "renesas,sdhi-r9a09g057"; reg = <0x0 0x15c00000 0 0x10000>; @@ -866,6 +1000,215 @@ sdhi2_vqmmc: vqmmc-regulator { status = "disabled"; }; }; + + eth0: ethernet@15c30000 { + compatible = "renesas,r9a09g057-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c30000 0 0x10000>; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + clocks = <&cpg CPG_MOD 0xbd>, <&cpg CPG_MOD 0xbc>, + <&cpg CPG_CORE R9A09G057_GBETH_0_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xb8>, <&cpg CPG_MOD 0xb9>, + <&cpg CPG_MOD 0xba>, <&cpg CPG_MOD 0xbb>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + resets = <&cpg 0xb0>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup0>; + snps,mtl-tx-config = <&mtl_tx_setup0>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup0: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup0: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + + eth1: ethernet@15c40000 { + compatible = "renesas,r9a09g057-gbeth", "renesas,rzv2h-gbeth", + "snps,dwmac-5.20"; + reg = <0 0x15c40000 0 0x10000>; + interrupts = , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi", + "rx-queue-0", "rx-queue-1", "rx-queue-2", + "rx-queue-3", "tx-queue-0", "tx-queue-1", + "tx-queue-2", "tx-queue-3"; + clocks = <&cpg CPG_MOD 0xc3>, <&cpg CPG_MOD 0xc2>, + <&cpg CPG_CORE R9A09G057_GBETH_1_CLK_PTP_REF_I>, + <&cpg CPG_MOD 0xbe>, <&cpg CPG_MOD 0xbf>, + <&cpg CPG_MOD 0xc0>, <&cpg CPG_MOD 0xc1>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "rx", "tx-180", "rx-180"; + resets = <&cpg 0xb1>; + power-domains = <&cpg>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,mtl-rx-config = <&mtl_rx_setup1>; + snps,mtl-tx-config = <&mtl_tx_setup1>; + snps,txpbl = <32>; + snps,rxpbl = <32>; + status = "disabled"; + + mdio1: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mtl_rx_setup1: rx-queues-config { + snps,rx-queues-to-use = <4>; + 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>; + }; + }; + + mtl_tx_setup1: tx-queues-config { + snps,tx-queues-to-use = <4>; + + 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>; + }; + }; + }; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + snps,blen = <16 8 4 0 0 0 0>; }; timer { diff --git a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts index 063eca0ba3e2..5c3f4e471e3d 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts @@ -16,6 +16,8 @@ / { compatible = "renesas,rzv2h-evk", "renesas,r9a09g057h44", "renesas,r9a09g057"; aliases { + ethernet0 = ð0; + ethernet1 = ð1; i2c0 = &i2c0; i2c1 = &i2c1; i2c2 = &i2c2; @@ -43,7 +45,7 @@ memory@240000000 { reg = <0x2 0x40000000 0x2 0x00000000>; }; - reg_0p8v: regulator0 { + reg_0p8v: regulator-0p8v { compatible = "regulator-fixed"; regulator-name = "fixed-0.8V"; @@ -53,7 +55,16 @@ reg_0p8v: regulator0 { regulator-always-on; }; - reg_3p3v: regulator1 { + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { compatible = "regulator-fixed"; regulator-name = "fixed-3.3V"; @@ -72,17 +83,54 @@ vqmmc_sdhi1: regulator-vccq-sdhi1 { gpios-states = <0>; states = <3300000 0>, <1800000 1>; }; + + /* 32.768kHz crystal */ + x6: x6-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; }; &audio_extal_clk { clock-frequency = <22579200>; }; +&ehci0 { + dr_mode = "otg"; + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +ð0 { + pinctrl-0 = <ð0_pins>; + pinctrl-names = "default"; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + +ð1 { + pinctrl-0 = <ð1_pins>; + pinctrl-names = "default"; + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + &gpu { status = "okay"; mali-supply = <®_0p8v>; }; +&hsusb { + dr_mode = "otg"; + status = "okay"; +}; + &i2c0 { pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; @@ -137,6 +185,61 @@ &i2c8 { clock-frequency = <400000>; status = "okay"; + + raa215300: pmic@12 { + compatible = "renesas,raa215300"; + reg = <0x12>, <0x6f>; + reg-names = "main", "rtc"; + clocks = <&x6>; + clock-names = "xin"; + }; +}; + +&mdio0 { + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + reg = <0>; + rxc-skew-psec = <0>; + txc-skew-psec = <0>; + rxdv-skew-psec = <0>; + txdv-skew-psec = <0>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + +&mdio1 { + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + reg = <0>; + rxc-skew-psec = <0>; + txc-skew-psec = <0>; + rxdv-skew-psec = <0>; + txdv-skew-psec = <0>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + +&ohci0 { + dr_mode = "otg"; + status = "okay"; +}; + +&ohci1 { + status = "okay"; }; &ostm0 { @@ -172,6 +275,16 @@ &ostm7 { }; &pinctrl { + eth0_pins: eth0 { + pins = "ET0_TXC_TXCLK"; + output-enable; + }; + + eth1_pins: eth1 { + pins = "ET1_TXC_TXCLK"; + output-enable; + }; + i2c0_pins: i2c0 { pinmux = , /* I2C0_SDA */ ; /* I2C0_SCL */ @@ -237,6 +350,38 @@ sd1_cd { pinmux = ; /* SD1_CD */ }; }; + + usb20_pins: usb20 { + ovc { + pinmux = ; /* OVC */ + }; + + vbus { + pinmux = ; /* VBUS */ + }; + }; + + usb21_pins: usb21 { + ovc { + pinmux = ; /* OVC */ + }; + + vbus { + pinmux = ; /* VBUS */ + }; + }; + + xspi_pins: xspi0 { + ctrl { + pins = "XSPI0_RESET0N", "XSPI0_CS0N", "XSPI0_CKP"; + output-enable; + }; + + io { + pins = "XSPI0_IO0", "XSPI0_IO1", "XSPI0_IO2", "XSPI0_IO3"; + renesas,output-impedance = <3>; + }; + }; }; &qextal_clk { @@ -266,6 +411,71 @@ &sdhi1 { status = "okay"; }; +&usb20phyrst { + status = "okay"; +}; + +&usb21phyrst { + status = "okay"; +}; + +&usb2_phy0 { + pinctrl-0 = <&usb20_pins>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&usb2_phy1 { + pinctrl-0 = <&usb21_pins>; + pinctrl-names = "default"; + + status = "okay"; +}; + &wdt1 { status = "okay"; }; + +&xspi { + pinctrl-0 = <&xspi_pins>; + pinctrl-names = "default"; + /* + * MT25QU512ABB8E12 flash chip is capable of running at 166MHz + * clock frequency. Set the clock frequency to the maximum 133MHz + * supported by the RZ/V2H SoC. + */ + assigned-clocks = <&cpg CPG_CORE R9A09G057_SPI_CLK_SPI>; + assigned-clock-rates = <133333334>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + vcc-supply = <®_1p8v>; + m25p,fast-read; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bl2"; + reg = <0x00000000 0x00060000>; + }; + + partition@60000 { + label = "fip"; + reg = <0x00060000 0x1fa0000>; + }; + + partition@2000000 { + label = "user"; + reg = <0x2000000 0x2000000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi b/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi index afdc1940e24a..58561da3007a 100644 --- a/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi +++ b/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi @@ -23,6 +23,9 @@ * SW_GPIO9_CAN1_STB: * 0 - Connect to GPIO9 PMOD (default) * 1 - Connect to CAN1 transceiver STB pin + * + * GPIO keys are enabled by default. Use PMOD_GPIO macros to disable them + * if needed. */ / { @@ -35,6 +38,7 @@ chosen { }; aliases { + i2c0 = &i2c0; serial3 = &scif0; mmc1 = &sdhi1; }; @@ -52,12 +56,45 @@ can_transceiver1: can-phy1 { max-bitrate = <8000000>; status = "disabled"; }; + + keys: keys { + compatible = "gpio-keys"; + + key-1 { + interrupts-extended = <&pinctrl KEY_1_GPIO IRQ_TYPE_EDGE_FALLING>; + linux,code = ; + label = "USER_SW1"; + wakeup-source; + debounce-interval = <20>; + }; + + key-2 { + interrupts-extended = <&pinctrl KEY_2_GPIO IRQ_TYPE_EDGE_FALLING>; + linux,code = ; + label = "USER_SW2"; + wakeup-source; + debounce-interval = <20>; + }; + + key-3 { + interrupts-extended = <&pinctrl KEY_3_GPIO IRQ_TYPE_EDGE_FALLING>; + linux,code = ; + label = "USER_SW3"; + wakeup-source; + debounce-interval = <20>; + }; + }; }; &canfd { status = "okay"; }; +&i2c0 { + status = "okay"; + clock-frequency = <400000>; +}; + &scif0 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi index ecea29a76b14..7faa44510d98 100644 --- a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi +++ b/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi @@ -26,6 +26,8 @@ / { compatible = "renesas,rzg3e-smarcm", "renesas,r9a09g047e57", "renesas,r9a09g047"; aliases { + ethernet0 = ð0; + ethernet1 = ð1; i2c2 = &i2c2; mmc0 = &sdhi0; mmc2 = &sdhi2; @@ -77,6 +79,24 @@ &audio_extal_clk { clock-frequency = <48000000>; }; +ð0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + + pinctrl-0 = <ð0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +ð1 { + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + + pinctrl-0 = <ð1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + &gpu { status = "okay"; mali-supply = <®_vdd0p8v_others>; @@ -102,7 +122,98 @@ raa215300: pmic@12 { }; }; +&mdio0 { + phy0: ethernet-phy@7 { + compatible = "ethernet-phy-id0022.1640", + "ethernet-phy-ieee802.3-c22"; + 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>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + +&mdio1 { + phy1: ethernet-phy@7 { + compatible = "ethernet-phy-id0022.1640", + "ethernet-phy-ieee802.3-c22"; + 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>; + rxd0-skew-psec = <0>; + rxd1-skew-psec = <0>; + rxd2-skew-psec = <0>; + rxd3-skew-psec = <0>; + txd0-skew-psec = <0>; + txd1-skew-psec = <0>; + txd2-skew-psec = <0>; + txd3-skew-psec = <0>; + }; +}; + &pinctrl { + eth0_pins: eth0 { + clk { + pinmux = ; /* TXC */ + output-enable; + }; + + ctrl { + pinmux = , /* MDC */ + , /* MDIO */ + , /* PHY_INTR (IRQ2) */ + , /* RXD3 */ + , /* RXD2 */ + , /* RXD1 */ + , /* RXD0 */ + , /* RXC */ + , /* RX_CTL */ + , /* TXD3 */ + , /* TXD2 */ + , /* TXD1 */ + , /* TXD0 */ + ; /* TX_CTL */ + }; + }; + + eth1_pins: eth1 { + clk { + pinmux = ; /* TXC */ + output-enable; + }; + + ctrl { + + pinmux = , /* MDC */ + , /* MDIO */ + , /* PHY_INTR (IRQ15) */ + , /* RXD3 */ + , /* RXD2 */ + , /* RXD1 */ + , /* RXD0 */ + , /* RXC */ + , /* RX_CTL */ + , /* TXD3 */ + , /* TXD2 */ + , /* TXD1 */ + , /* TXD0 */ + ; /* TX_CTL */ + }; + }; + i2c2_pins: i2c { pinmux = , /* SCL2 */ ; /* SDA2 */ @@ -182,6 +293,15 @@ sd2-pwen { pinmux = ; /* SD2PWEN */ }; }; + + xspi_pins: xspi0 { + pinmux = , /* XSPI0_IO0 */ + , /* XSPI0_IO1 */ + , /* XSPI0_IO2 */ + , /* XSPI0_IO3 */ + , /* XSPI0_CKP */ + ; /* XSPI0_CS0 */ + }; }; &qextal_clk { @@ -245,3 +365,40 @@ &sdhi2_vqmmc { &wdt1 { status = "okay"; }; + +&xspi { + pinctrl-0 = <&xspi_pins>; + pinctrl-names = "default"; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + vcc-supply = <®_1p8v>; + m25p,fast-read; + spi-max-frequency = <50000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bl2"; + reg = <0x00000000 0x00060000>; + }; + + partition@60000 { + label = "fip"; + reg = <0x00060000 0x007a0000>; + }; + + partition@800000 { + label = "user"; + reg = <0x800000 0x800000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-emmc.dtso b/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-emmc.dtso new file mode 100644 index 000000000000..eda2b31f6d79 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-emmc.dtso @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Shared DT overlay for the eMMC Sub Board (RTK0EF0186B02000BJ), which + * is connected to the CN15 connector on the RZ/V2H and RZ/V2N EVKs. + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + aliases { + mmc0 = "/soc/mmc@15c00000"; + }; +}; + +&pinctrl { + sdhi0_emmc_pins: emmc-pins { + sd0-clk { + pins = "SD0CLK"; + renesas,output-impedance = <3>; + slew-rate = <0>; + }; + + sd0-dat-cmd { + pins = "SD0DAT0", "SD0DAT1", "SD0DAT2", "SD0DAT3", "SD0DAT4", + "SD0DAT5", "SD0DAT6", "SD0DAT7", "SD0CMD"; + input-enable; + renesas,output-impedance = <3>; + slew-rate = <0>; + }; + }; +}; + +&sdhi0 { + pinctrl-0 = <&sdhi0_emmc_pins>; + pinctrl-1 = <&sdhi0_emmc_pins>; + pinctrl-names = "default", "state_uhs"; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + bus-width = <8>; + mmc-hs200-1_8v; + non-removable; + fixed-emmc-driver-type = <1>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-sd.dtso b/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-sd.dtso new file mode 100644 index 000000000000..0af1e0a6c7f4 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/rzv2-evk-cn15-sd.dtso @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Shared DT overlay for the microSD Sub Board (RTK0EF0186B01000BJ), which + * is connected to the CN15 connector on the RZ/V2H and RZ/V2N EVKs. + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + aliases { + mmc0 = "/soc/mmc@15c00000"; + }; + + vqmmc_sdhi0: regulator-vqmmc-sdhi0 { + compatible = "regulator-gpio"; + regulator-name = "SDHI0 VqmmC"; + gpios = <&pinctrl RZG2L_GPIO(10, 0) GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + gpios-states = <0>; + states = <3300000 0>, <1800000 1>; + }; +}; + +&pinctrl { + sdhi0-pwr-en-hog { + gpio-hog; + gpios = ; + output-high; + line-name = "sd0_pwr_en"; + }; + + sdhi0_pins: sd0 { + sd0-cd { + pinmux = ; /* SD0_CD */ + }; + + sd0-clk { + pins = "SD0CLK"; + renesas,output-impedance = <3>; + slew-rate = <0>; + }; + + sd0-dat-cmd { + pins = "SD0DAT0", "SD0DAT1", "SD0DAT2", "SD0DAT3", "SD0CMD"; + input-enable; + renesas,output-impedance = <3>; + slew-rate = <0>; + }; + }; +}; + +&sdhi0 { + pinctrl-0 = <&sdhi0_pins>; + pinctrl-1 = <&sdhi0_pins>; + pinctrl-names = "default", "state_uhs"; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <&vqmmc_sdhi0>; + bus-width = <4>; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi index 68971c870d17..bbb3583372d0 100644 --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi @@ -592,6 +592,7 @@ csa_dvfs: adc@7f { }; &i2c_dvfs { + bootph-all; status = "okay"; clock-frequency = <400000>; @@ -625,6 +626,7 @@ eeprom@50 { compatible = "rohm,br24t01", "atmel,24c01"; reg = <0x50>; pagesize = <8>; + bootph-all; }; }; diff --git a/arch/arm64/boot/dts/renesas/ulcb.dtsi b/arch/arm64/boot/dts/renesas/ulcb.dtsi index fcab957b54f7..8a30908992ab 100644 --- a/arch/arm64/boot/dts/renesas/ulcb.dtsi +++ b/arch/arm64/boot/dts/renesas/ulcb.dtsi @@ -244,6 +244,7 @@ versaclock5: clock-generator@6a { }; &i2c_dvfs { + bootph-all; status = "okay"; clock-frequency = <400000>; @@ -277,6 +278,7 @@ eeprom@50 { compatible = "rohm,br24t01", "atmel,24c01"; reg = <0x50>; pagesize = <8>; + bootph-all; }; }; diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index 4bf84622db47..099520962ffb 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -18,6 +18,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3308-evb.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3308-roc-cc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3308-rock-pi-s.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3308-rock-s0.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3308-sakurapi-rk3308b.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3318-a95x-z2.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3326-anbernic-rg351m.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3326-anbernic-rg351v.dtb @@ -84,6 +85,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4c.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock960.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64-v2.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64-screen.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb @@ -143,7 +145,10 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-display-vz.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-io-expander.dtbo 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-luckfox-omni3576.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-nanopi-m5.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-roc-pc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-rock-4d.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3582-radxa-e52c.dtb @@ -160,6 +165,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-firefly-itx-3588j.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-friendlyelec-cm3588-nas.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-h96-max-v58.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-jaguar.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-jaguar-ethernet-switch.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-jaguar-pre-ict-tester.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-mnt-reform2.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-nanopc-t6.dtb @@ -174,6 +180,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-rock-5b.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-rock-5b-pcie-ep.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-rock-5b-pcie-srns.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-rock-5b-plus.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-rock-5t.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-tiger-haikou.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-tiger-haikou-video-demo.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-toybrick-x0.dtb @@ -188,6 +195,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6c.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-odroid-m2.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-5.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-5b.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-roc-pc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-rock-5a.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-rock-5c.dtb @@ -220,11 +228,23 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-puma-haikou-haikou-video-demo.dtb rk3399-puma-haikou-haikou-video-demo-dtbs := rk3399-puma-haikou.dtb \ rk3399-puma-haikou-video-demo.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64-screen.dtb +rk3399-rockpro64-screen-dtbs := rk3399-rockpro64.dtb \ + rk3399-rockpro64-screen.dtbo + +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) += rk3568-wolfvision-pf5-vz-2-uhd.dtb rk3568-wolfvision-pf5-vz-2-uhd-dtbs := rk3568-wolfvision-pf5.dtb \ rk3568-wolfvision-pf5-display-vz.dtbo \ rk3568-wolfvision-pf5-io-expander.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5-v1.2-wifibt.dtb +rk3576-armsom-sige5-v1.2-wifibt-dtbs := rk3576-armsom-sige5.dtb \ + rk3576-armsom-sige5-v1.2-wifibt.dtbo + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6a-wifi.dtb rk3588-edgeble-neu6a-wifi-dtbs := rk3588-edgeble-neu6a-io.dtb \ rk3588-edgeble-neu6a-wifi.dtbo @@ -233,6 +253,10 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6b-wifi.dtb rk3588-edgeble-neu6b-wifi-dtbs := rk3588-edgeble-neu6b-io.dtb \ rk3588-edgeble-neu6a-wifi.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-jaguar-ethernet-switch.dtb +rk3588-jaguar-ethernet-switch-dtbs := rk3588-jaguar.dtb \ + rk3588-jaguar-ethernet-switch.dtbo + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-jaguar-pre-ict-tester.dtb rk3588-jaguar-pre-ict-tester-dtbs := rk3588-jaguar.dtb \ rk3588-jaguar-pre-ict-tester.dtbo diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w-a2.dts b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w-a2.dts index 1d26164be7b8..a31c61c8f148 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w-a2.dts +++ b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w-a2.dts @@ -12,6 +12,8 @@ / { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w.dts b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w.dts index 82c6acdb4fae..a3c6edfdb37c 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w.dts +++ b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3146w.dts @@ -12,6 +12,8 @@ / { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3148w.dts b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3148w.dts index 94449132df38..9b5eff392dfa 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3148w.dts +++ b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk050h3148w.dts @@ -12,6 +12,8 @@ / { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk500hd1829.dts b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk500hd1829.dts index d7b639e7ccab..36b7cae49e31 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra-ltk500hd1829.dts +++ b/arch/arm64/boot/dts/rockchip/px30-cobra-ltk500hd1829.dts @@ -16,6 +16,8 @@ aliases { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-evb.dts b/arch/arm64/boot/dts/rockchip/px30-evb.dts index d93aaac7a42f..85d1642eb9be 100644 --- a/arch/arm64/boot/dts/rockchip/px30-evb.dts +++ b/arch/arm64/boot/dts/rockchip/px30-evb.dts @@ -124,6 +124,8 @@ &display_subsystem { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; ports { @@ -483,8 +485,7 @@ &isp { ports { port@0 { - mipi_in_ucam: endpoint@0 { - reg = <0>; + mipi_in_ucam: endpoint { data-lanes = <1 2>; remote-endpoint = <&ucam_out>; }; diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3146w-a2.dts b/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3146w-a2.dts index b71929bcb33e..932721ffd470 100644 --- a/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3146w-a2.dts +++ b/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3146w-a2.dts @@ -12,6 +12,8 @@ / { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3148w.dts b/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3148w.dts index a9bd5936c701..70adf091371c 100644 --- a/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3148w.dts +++ b/arch/arm64/boot/dts/rockchip/px30-pp1516-ltk050h3148w.dts @@ -12,6 +12,8 @@ / { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi index 3f9a133d7373..192791993f05 100644 --- a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi @@ -72,7 +72,7 @@ vcc5v0_sys: regulator-vccsys { }; vcc_cam_avdd: regulator-vcc-cam-avdd { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; regulator-name = "vcc_cam_avdd"; gpio = <&gpio3 RK_PC0 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; @@ -83,7 +83,7 @@ vcc_cam_avdd: regulator-vcc-cam-avdd { }; vcc_cam_dovdd: regulator-vcc-cam-dovdd { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; regulator-name = "vcc_cam_dovdd"; gpio = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; @@ -94,7 +94,7 @@ vcc_cam_dovdd: regulator-vcc-cam-dovdd { }; vcc_cam_dvdd: regulator-vcc-cam-dvdd { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; regulator-name = "vcc_cam_dvdd"; gpio = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; enable-active-high; @@ -106,7 +106,7 @@ vcc_cam_dvdd: regulator-vcc-cam-dvdd { }; vcc_lens_afvdd: regulator-vcc-lens-afvdd { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; regulator-name = "vcc_lens_afvdd"; gpio = <&gpio3 RK_PB2 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; @@ -444,8 +444,7 @@ &isp { ports { port@0 { - mipi_in_ucam: endpoint@0 { - reg = <0>; + mipi_in_ucam: endpoint { data-lanes = <1 2>; remote-endpoint = <&ucam_out>; }; diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso b/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso index 7d9ea5aa5984..760d5139f95d 100644 --- a/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso +++ b/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso @@ -26,7 +26,7 @@ backlight: backlight { }; cam_afvdd_2v8: regulator-cam-afvdd-2v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 2 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <2800000>; regulator-min-microvolt = <2800000>; @@ -35,7 +35,7 @@ cam_afvdd_2v8: regulator-cam-afvdd-2v8 { }; cam_avdd_2v8: regulator-cam-avdd-2v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 4 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <2800000>; regulator-min-microvolt = <2800000>; @@ -44,7 +44,7 @@ cam_avdd_2v8: regulator-cam-avdd-2v8 { }; cam_dovdd_1v8: regulator-cam-dovdd-1v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 3 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <1800000>; regulator-min-microvolt = <1800000>; @@ -94,6 +94,15 @@ video-adapter-led { }; }; +&cif_clkout_m0 { + rockchip,pins = + <2 RK_PB3 1 &pcfg_pull_none_12ma>; +}; + +&csi_dphy { + status = "okay"; +}; + &display_subsystem { status = "okay"; }; @@ -135,6 +144,12 @@ &i2c1 { /* OV5675, GT911, DW9714 are limited to 400KHz */ clock-frequency = <400000>; + focus: focus@c { + compatible = "dongwoon,dw9714"; + reg = <0xc>; + vcc-supply = <&cam_afvdd_2v8>; + }; + touchscreen@14 { compatible = "goodix,gt911"; reg = <0x14>; @@ -157,6 +172,44 @@ pca9670: gpio@27 { pinctrl-names = "default"; reset-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>; }; + + camera@36 { + compatible = "ovti,ov5675"; + reg = <0x36>; + clocks = <&cru SCLK_CIF_OUT>; + assigned-clocks = <&cru SCLK_CIF_OUT>; + /* Only parent to get exactly 19.2MHz */ + assigned-clock-parents = <&cru USB480M>; + assigned-clock-rates = <19200000>; + avdd-supply = <&cam_avdd_2v8>; + dvdd-supply = <&cam_dvdd_1v2>; + dovdd-supply = <&cam_dovdd_1v8>; + lens-focus = <&focus>; + orientation = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&cif_clkout_m0>; + reset-gpios = <&pca9670 6 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + cam_out: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <450000000>; + remote-endpoint = <&mipi_in_cam>; + }; + }; + }; +}; + +&isp { + status = "okay"; +}; + +&isp_in { + mipi_in_cam: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&cam_out>; + }; }; &pinctrl { diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi index ab232e5c7ad6..4203b335a263 100644 --- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi @@ -379,6 +379,18 @@ pmic_int: pmic-int { <0 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>; }; }; + + spi1 { + spi1_csn0_gpio_pin: spi1-csn0-gpio-pin { + rockchip,pins = + <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up_4ma>; + }; + + spi1_csn1_gpio_pin: spi1-csn1-gpio-pin { + rockchip,pins = + <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up_4ma>; + }; + }; }; &pmu_io_domains { @@ -396,6 +408,17 @@ &sdmmc { vqmmc-supply = <&vccio_sd>; }; +&spi1 { + /* + * Hardware CS has a very slow rise time of about 6us, + * causing transmission errors. + * With cs-gpios we have a rise time of about 20ns. + */ + cs-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>, <&gpio3 RK_PB2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_clk &spi1_csn0_gpio_pin &spi1_csn1_gpio_pin &spi1_miso &spi1_mosi>; +}; + &tsadc { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/px30.dtsi b/arch/arm64/boot/dts/rockchip/px30.dtsi index feabdadfa440..46f64cd33b9b 100644 --- a/arch/arm64/boot/dts/rockchip/px30.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30.dtsi @@ -351,8 +351,6 @@ power-domain@PX30_PD_GPU { pmugrf: syscon@ff010000 { compatible = "rockchip,px30-pmugrf", "syscon", "simple-mfd"; reg = <0x0 0xff010000 0x0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; pmu_io_domains: io-domains { compatible = "rockchip,px30-pmu-io-voltage-domain"; @@ -453,8 +451,6 @@ gic: interrupt-controller@ff131000 { grf: syscon@ff140000 { compatible = "rockchip,px30-grf", "syscon", "simple-mfd"; reg = <0x0 0xff140000 0x0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; io_domains: io-domains { compatible = "rockchip,px30-io-voltage-domain"; @@ -1137,8 +1133,6 @@ dsi: dsi@ff450000 { resets = <&cru SRST_MIPIDSI_HOST_P>; reset-names = "apb"; rockchip,grf = <&grf>; - #address-cells = <1>; - #size-cells = <0>; status = "disabled"; ports { @@ -1269,10 +1263,8 @@ ports { #address-cells = <1>; #size-cells = <0>; - port@0 { + isp_in: port@0 { reg = <0>; - #address-cells = <1>; - #size-cells = <0>; }; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts b/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts new file mode 100644 index 000000000000..e5e6b800c2d1 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Akash Gajjar + * Copyright (c) 2019 Jagan Teki + * Copyright (C) 2024 TheSnowfield + * Copyright (C) 2025 Hsun Lai + */ + +/dts-v1/; +#include "rk3308.dtsi" +#include + +/ { + model = "Sakura Pi RK3308B"; + compatible = "sakurapi,rk3308-sakurapi-rk3308b", "rockchip,rk3308"; + + aliases { + mmc0 = &emmc; + mmc1 = &sdmmc; + mmc2 = &sdio; + }; + + chosen { + stdout-path = "serial2:1500000n8"; + }; + + 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>; + }; + + vdd_core: regulator-vdd-core { + compatible = "pwm-regulator"; + pwms = <&pwm0 0 5000 1>; + regulator-name = "vdd_core"; + regulator-min-microvolt = <827000>; + regulator-max-microvolt = <1340000>; + regulator-settling-time-up-us = <250>; + regulator-always-on; + regulator-boot-on; + pwm-supply = <&vcc5v0_sys>; + }; + + vdd_log: regulator-vdd-log { + compatible = "regulator-fixed"; + regulator-name = "vdd_log"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc_ddr: regulator-vcc-ddr { + compatible = "regulator-fixed"; + regulator-name = "vcc_ddr"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + vin-supply = <&vcc5v0_sys>; + }; + + 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 = <&vcc_io>; + }; + + vcc_io: regulator-vcc-io { + compatible = "regulator-fixed"; + regulator-name = "vcc_io"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc_phy: regulator-vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc5v0_otg: regulator-vcc5v0-otg { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&otg_vbus_drv>; + regulator-name = "vcc5v0_otg"; + regulator-always-on; + vin-supply = <&vcc5v0_sys>; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-0 = <&wifi_enable_h>; + pinctrl-names = "default"; + /* + * 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) + */ + reset-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>; + }; +}; + +&cpu0 { + cpu-supply = <&vdd_core>; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + non-removable; + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&rtc_32k>; + + bluetooth { + bt_reg_on: bt-reg-on { + rockchip,pins = <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + bt_wake_host: bt-wake-host { + rockchip,pins = <4 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + host_wake_bt: host-wake-bt { + rockchip,pins = <4 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb { + otg_vbus_drv: otg-vbus-drv { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wifi { + wifi_host_wake: wifi-host-wake { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_pin_pull_down>; +}; + +&pwm3 { + status = "okay"; +}; + +&saradc { + vref-supply = <&vcc_1v8>; + status = "okay"; +}; + +&sdio { + #address-cells = <1>; + #size-cells = <0>; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + no-mmc; + no-sd; + status = "okay"; + + brcmf: wifi@1 { + compatible = "brcm,bcm43455-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&gpio0>; + interrupts = ; + interrupt-names = "host-wake"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_host_wake>; + }; +}; + +&sdmmc { + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_det &sdmmc_bus4>; + card-detect-delay = <800>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4345c5"; + clocks = <&cru SCLK_RTC32K>; + clock-names = "lpo"; + pinctrl-names = "default"; + pinctrl-0 = <&host_wake_bt &bt_wake_host &bt_reg_on>; + device-wakeup-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio4 RK_PB2 GPIO_ACTIVE_HIGH>; + shutdown-gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_HIGH>; + max-speed = <1500000>; + }; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_otg { + status = "okay"; +}; + +&u2phy_host { + status = "okay"; +}; + +&usb20_otg { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usb_host_ehci { + status = "okay"; +}; + +&usb_host_ohci { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3326-anbernic-rg351m.dtsi b/arch/arm64/boot/dts/rockchip/rk3326-anbernic-rg351m.dtsi index 150fadcb0b3c..54395a40b087 100644 --- a/arch/arm64/boot/dts/rockchip/rk3326-anbernic-rg351m.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3326-anbernic-rg351m.dtsi @@ -118,6 +118,8 @@ &display_subsystem { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; ports { diff --git a/arch/arm64/boot/dts/rockchip/rk3326-gameforce-chi.dts b/arch/arm64/boot/dts/rockchip/rk3326-gameforce-chi.dts index 10e6ab724ac4..4d306085646c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3326-gameforce-chi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3326-gameforce-chi.dts @@ -322,6 +322,8 @@ &display_subsystem { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; internal_display: panel@0 { diff --git a/arch/arm64/boot/dts/rockchip/rk3326-odroid-go.dtsi b/arch/arm64/boot/dts/rockchip/rk3326-odroid-go.dtsi index 446a1a6c12e7..bf4554eff47d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3326-odroid-go.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3326-odroid-go.dtsi @@ -220,6 +220,8 @@ &display_subsystem { }; &dsi { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; ports { diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi index 7d992c3c01ce..6438c969f9d7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi @@ -731,11 +731,7 @@ vop: vop@ff370000 { status = "disabled"; vop_out: port { - #address-cells = <1>; - #size-cells = <0>; - - vop_out_hdmi: endpoint@0 { - reg = <0>; + vop_out_hdmi: endpoint { remote-endpoint = <&hdmi_in_vop>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3368-lba3368.dts b/arch/arm64/boot/dts/rockchip/rk3368-lba3368.dts index b99bb0a5f900..b9801a691b48 100644 --- a/arch/arm64/boot/dts/rockchip/rk3368-lba3368.dts +++ b/arch/arm64/boot/dts/rockchip/rk3368-lba3368.dts @@ -609,7 +609,7 @@ &uart0 { bluetooth { compatible = "brcm,bcm4345c5"; - interrupts-extended = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&gpio3 RK_PA7 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "host-wakeup"; clocks = <&rk808 RK808_CLKOUT1>; clock-names = "lpo"; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi index 9d5f5b083e3c..4dcceb9136b7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi @@ -2071,8 +2071,6 @@ mipi_dsi: dsi@ff960000 { resets = <&cru SRST_P_MIPI_DSI0>; reset-names = "apb"; rockchip,grf = <&grf>; - #address-cells = <1>; - #size-cells = <0>; status = "disabled"; ports { @@ -2112,8 +2110,6 @@ mipi_dsi1: dsi@ff968000 { resets = <&cru SRST_P_MIPI_DSI1>; reset-names = "apb"; rockchip,grf = <&grf>; - #address-cells = <1>; - #size-cells = <0>; #phy-cells = <0>; status = "disabled"; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi index a9ea4b0daa04..9d07353df52c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi @@ -250,18 +250,11 @@ &edp { */ assigned-clocks = <&cru PCLK_EDP>; assigned-clock-rates = <24000000>; +}; - ports { - edp_out: port@1 { - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - - edp_out_panel: endpoint@0 { - reg = <0>; - remote-endpoint = <&panel_in_edp>; - }; - }; +&edp_out { + edp_out_panel: endpoint { + remote-endpoint = <&panel_in_edp>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi index 5e068377a0a2..6aaaf0f7f73f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi @@ -627,8 +627,10 @@ &mipi_dphy_rx0 { }; &mipi_dsi { - status = "okay"; clock-master; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; ports { mipi_out: port@1 { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-hugsun-x99.dts b/arch/arm64/boot/dts/rockchip/rk3399-hugsun-x99.dts index 81c4fcb30f39..352c8efb37e0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-hugsun-x99.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-hugsun-x99.dts @@ -211,7 +211,6 @@ &i2c0 { vdd_cpu_b: syr827@40 { compatible = "silergy,syr827"; reg = <0x40>; - regulator-compatible = "fan53555-reg"; pinctrl-0 = <&vsel1_pin>; regulator-name = "vdd_cpu_b"; regulator-min-microvolt = <712500>; @@ -229,7 +228,6 @@ regulator-state-mem { vdd_gpu: syr828@41 { compatible = "silergy,syr828"; reg = <0x41>; - regulator-compatible = "fan53555-reg"; pinctrl-0 = <&vsel2_pin>; regulator-name = "vdd_gpu"; regulator-min-microvolt = <712500>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts index 5473070823cb..b33a1509a8e9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts @@ -404,18 +404,11 @@ &edp { pinctrl-names = "default"; pinctrl-0 = <&edp_hpd>; status = "okay"; +}; - ports { - edp_out: port@1 { - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - - edp_out_panel: endpoint@0 { - reg = <0>; - remote-endpoint = <&panel_in_edp>; - }; - }; +&edp_out { + edp_out_panel: endpoint { + remote-endpoint = <&panel_in_edp>; }; }; @@ -966,6 +959,7 @@ spiflash: flash@0 { reg = <0>; m25p,fast-read; spi-max-frequency = <10000000>; + vcc-supply = <&vcc_3v0>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts index 04ba4c4565d0..6f97e57f36f5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts @@ -104,6 +104,16 @@ vcc_sys: regulator-vcc-sys { regulator-boot-on; }; + avdd2v8_dvp: regulator-avdd2v8-dvp { + compatible = "regulator-fixed"; + regulator-name = "avdd2v8_dvp"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <&vcc3v3_sys>; + }; + vcc3v3_sys: regulator-vcc3v3-sys { compatible = "regulator-fixed"; regulator-name = "vcc3v3_sys"; @@ -136,6 +146,16 @@ vcc1v8_codec: regulator-vcc1v8-codec { vin-supply = <&vcc3v3_sys>; }; + vcc1v2_dvp: regulator-vcc1v2-dvp { + compatible = "regulator-fixed"; + regulator-name = "vcc1v2_dvp"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vcca1v8_s3>; + }; + wifi_pwrseq: sdio-wifi-pwrseq { compatible = "mmc-pwrseq-simple"; clocks = <&rk818 1>; @@ -312,6 +332,8 @@ vcc3v0_touch: LDO_REG2 { vcca1v8_codec: LDO_REG3 { regulator-name = "vcca1v8_codec"; + regulator-always-on; + regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; @@ -420,6 +442,67 @@ regulator-state-mem { }; }; +&i2c1 { + assigned-clocks = <&cru SCLK_CIF_OUT>; + assigned-clock-rates = <24000000>; + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_xfer &cif_clkouta>; + status = "okay"; + + wcam: camera@1a { + compatible = "sony,imx258"; + reg = <0x1a>; + clocks = <&cru SCLK_CIF_OUT>; /* MIPI_MCLK0, derived from CIF_CLKO */ + lens-focus = <&wcam_lens>; + orientation = <1>; /* V4L2_CAMERA_ORIENTATION_BACK */ + pinctrl-names = "default"; + pinctrl-0 = <&camera_rst_l>; + reset-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_LOW>; + rotation = <270>; + /* Note: both cameras also depend on vcca1v8_codec to power the I2C bus. */ + vif-supply = <&vcc1v8_dvp>; + vana-supply = <&avdd2v8_dvp>; + vdig-supply = <&vcc1v2_dvp>; /* DVDD_DVP is the same as VCC1V2_DVP */ + + port { + wcam_out: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <636000000>; + remote-endpoint = <&mipi_in_wcam>; + }; + }; + }; + + wcam_lens: camera-lens@c { + compatible = "dongwoon,dw9714"; + reg = <0x0c>; + /* Same I2c bus as both cameras, depends on vcca1v8_codec for power. */ + vcc-supply = <&vcc1v8_dvp>; + }; + + ucam: camera@36 { + compatible = "ovti,ov8858"; + reg = <0x36>; + clocks = <&cru SCLK_CIF_OUT>; /* MIPI_MCLK1, derived from CIF_CLK0 */ + clock-names = "xvclk"; + dovdd-supply = <&vcc1v8_dvp>; + orientation = <0>; /* V4L2_CAMERA_ORIENTATION_FRONT */ + pinctrl-names = "default"; + pinctrl-0 = <&camera2_rst_l &dvp_pdn0_h>; + powerdown-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + rotation = <90>; + + port { + ucam_out: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&mipi_in_ucam>; + }; + }; + }; +}; + &i2c3 { i2c-scl-rising-time-ns = <450>; i2c-scl-falling-time-ns = <15>; @@ -462,30 +545,61 @@ &io_domains { status = "okay"; }; -&mipi_dsi { +&isp0 { status = "okay"; - clock-master; ports { - mipi_out: port@1 { - #address-cells = <0>; - #size-cells = <0>; - reg = <1>; - - mipi_out_panel: endpoint { - remote-endpoint = <&mipi_in_panel>; + port@0 { + mipi_in_ucam: endpoint@0 { + reg = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&ucam_out>; }; }; }; +}; + +&isp0_mmu { + status = "okay"; +}; + +&isp1 { + status = "okay"; + + ports { + port@0 { + mipi_in_wcam: endpoint@0 { + reg = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&wcam_out>; + }; + }; + }; +}; + +&mipi_dphy_rx0 { + status = "okay"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&mipi_dsi { + clock-master; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; panel@0 { - compatible = "hannstar,hsd060bhw4"; + compatible = "hannstar,hsd060bhw4", "himax,hx8394"; reg = <0>; backlight = <&backlight>; - reset-gpios = <&gpio4 RK_PD1 GPIO_ACTIVE_LOW>; - vcc-supply = <&vcc2v8_lcd>; iovcc-supply = <&vcc1v8_lcd>; pinctrl-names = "default"; + pinctrl-0 = <&lcd1_rst_pin>; + reset-gpios = <&gpio4 RK_PD1 GPIO_ACTIVE_LOW>; + vcc-supply = <&vcc2v8_lcd>; port { mipi_in_panel: endpoint { @@ -495,6 +609,16 @@ mipi_in_panel: endpoint { }; }; +&mipi_out { + mipi_out_panel: endpoint { + remote-endpoint = <&mipi_in_panel>; + }; +}; + +&mipi_dsi1 { + status = "okay"; +}; + &pmu_io_domains { pmu1830-supply = <&vcc_1v8>; status = "okay"; @@ -507,6 +631,24 @@ pwrbtn_pin: pwrbtn-pin { }; }; + lcd { + lcd1_rst_pin: lcd1-rst-pin { + rockchip,pins = <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + cameras { + camera_rst_l: camera-rst-l { + rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + camera2_rst_l: camera2-rst-l { + rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + dvp_pdn0_h: dvp-pdn0-h { + rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + leds { red_led_pin: red-led-pin { rockchip,pins = <4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; @@ -612,6 +754,7 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <10000000>; + vcc-supply = <&vcc_1v8>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso index 0377ec860d35..5e8f729c2cf2 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso @@ -26,7 +26,7 @@ backlight: backlight { }; cam_afvdd_2v8: regulator-cam-afvdd-2v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 2 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <2800000>; regulator-min-microvolt = <2800000>; @@ -35,7 +35,7 @@ cam_afvdd_2v8: regulator-cam-afvdd-2v8 { }; cam_avdd_2v8: regulator-cam-avdd-2v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 4 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <2800000>; regulator-min-microvolt = <2800000>; @@ -44,7 +44,7 @@ cam_avdd_2v8: regulator-cam-avdd-2v8 { }; cam_dovdd_1v8: regulator-cam-dovdd-1v8 { - compatible = "regulator-fixed"; + compatible = "regulator-fixed"; gpio = <&pca9670 3 GPIO_ACTIVE_LOW>; regulator-max-microvolt = <1800000>; regulator-min-microvolt = <1800000>; @@ -124,12 +124,6 @@ pca9670: gpio@27 { }; }; -&mipi_out { - mipi_out_panel: endpoint { - remote-endpoint = <&mipi_in_panel>; - }; -}; - &mipi_dsi { #address-cells = <1>; #size-cells = <0>; @@ -151,6 +145,12 @@ mipi_in_panel: endpoint { }; }; +&mipi_out { + mipi_out_panel: endpoint { + remote-endpoint = <&mipi_in_panel>; + }; +}; + &pinctrl { pca9670 { pca9670_resetn: pca9670-resetn { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts index 15da5c80d25d..962b8b231c96 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts @@ -421,7 +421,6 @@ vdd_cpu_b: regulator@40 { compatible = "silergy,syr827"; reg = <0x40>; fcs,suspend-voltage-selector = <1>; - regulator-compatible = "fan53555-reg"; pinctrl-0 = <&vsel1_gpio>; vsel-gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>; regulator-name = "vdd_cpu_b"; @@ -440,7 +439,6 @@ vdd_gpu: regulator@41 { compatible = "silergy,syr828"; reg = <0x41>; fcs,suspend-voltage-selector = <1>; - regulator-compatible = "fan53555-reg"; pinctrl-0 = <&vsel2_gpio>; vsel-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; regulator-name = "vdd_gpu"; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64-screen.dtso b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64-screen.dtso new file mode 100644 index 000000000000..dabe535f2111 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64-screen.dtso @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * Copyright (c) 2025 Peter Robinson + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +&{/} { + avdd: regulator-avdd { + compatible = "regulator-fixed"; + regulator-name = "avdd"; + regulator-min-microvolt = <11000000>; + regulator-max-microvolt = <11000000>; + vin-supply = <&vcc3v3_s0>; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <5>; + pwms = <&pwm0 0 1000000 0>; + status = "okay"; + }; +}; + +&i2c4 { + #address-cells = <1>; + #size-cells = <0>; + + touch: touchscreen@5d { + compatible = "goodix,gt911"; + reg = <0x5d>; + interrupt-parent = <&gpio4>; + interrupts = ; + AVDD28-supply = <&vcc3v0_touch>; + VDDIO-supply = <&vcc3v0_touch>; + irq-gpios = <&gpio4 RK_PD5 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; + +&mipi_dsi { + clock-master; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + mipi_panel: panel@0 { + compatible = "feiyang,fy07024di26a30d"; + reg = <0>; + avdd-supply = <&avdd>; + backlight = <&backlight>; + dvdd-supply = <&vcc3v3_s0>; + + port { + mipi_in_panel: endpoint { + remote-endpoint = <&mipi_out_panel>; + }; + }; + }; +}; + +&mipi_out { + mipi_out_panel: endpoint { + remote-endpoint = <&mipi_in_panel>; + }; +}; + +&pwm0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi index a7e4adf87e7a..8b72ae6449c9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi @@ -20,15 +20,6 @@ chosen { stdout-path = "serial2:1500000n8"; }; - /* enable for panel backlight support */ - backlight: backlight { - compatible = "pwm-backlight"; - brightness-levels = <0 4 8 16 32 64 128 255>; - default-brightness-level = <5>; - pwms = <&pwm0 0 1000000 0>; - status = "disabled"; - }; - clkin_gmac: external-gmac-clock { compatible = "fixed-clock"; clock-frequency = <125000000>; @@ -116,14 +107,6 @@ dit_p0_0: endpoint { }; }; - avdd: regulator-avdd { - compatible = "regulator-fixed"; - regulator-name = "avdd"; - regulator-min-microvolt = <11000000>; - regulator-max-microvolt = <11000000>; - vin-supply = <&vcc3v3_s0>; - }; - vcc12v_dcin: regulator-vcc12v-dcin { compatible = "regulator-fixed"; regulator-name = "vcc12v_dcin"; @@ -590,19 +573,6 @@ fusb0: typec-portc@22 { vbus-supply = <&vcc5v0_typec>; status = "okay"; }; - - /* enable for pine64 touch screen support */ - touch: touchscreen@5d { - compatible = "goodix,gt911"; - reg = <0x5d>; - interrupt-parent = <&gpio4>; - interrupts = ; - AVDD28-supply = <&vcc3v0_touch>; - VDDIO-supply = <&vcc3v0_touch>; - irq-gpios = <&gpio4 RK_PD5 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; }; &i2s0 { @@ -638,36 +608,6 @@ &io_domains { gpio1830-supply = <&vcc_3v0>; }; -/* enable for pine64 panel display support */ -&mipi_dsi { - clock-master; - status = "disabled"; - - ports { - mipi_out: port@1 { - reg = <1>; - - mipi_out_panel: endpoint { - remote-endpoint = <&mipi_in_panel>; - }; - }; - }; - - mipi_panel: panel@0 { - compatible = "feiyang,fy07024di26a30d"; - reg = <0>; - avdd-supply = <&avdd>; - backlight = <&backlight>; - dvdd-supply = <&vcc3v3_s0>; - - port { - mipi_in_panel: endpoint { - remote-endpoint = <&mipi_out_panel>; - }; - }; - }; -}; - &pcie0 { ep-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_HIGH>; num-lanes = <4>; @@ -782,10 +722,6 @@ vcc5v0_host_en: vcc5v0-host-en { }; }; -&pwm0 { - status = "okay"; -}; - &pwm1 { 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 fdaa8472b7a7..a4ceafe6dd7a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts @@ -142,21 +142,13 @@ sdio_pwrseq: sdio-pwrseq { &edp { status = "okay"; - - ports { - edp_out: port@1 { - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - - edp_out_panel: endpoint@0 { - reg = <0>; - remote-endpoint = <&panel_in_edp>; - }; - }; - }; }; +&edp_out { + edp_out_panel: endpoint { + remote-endpoint = <&panel_in_edp>; + }; +}; &i2c1 { i2c-scl-rising-time-ns = <300>; i2c-scl-falling-time-ns = <15>; diff --git a/arch/arm64/boot/dts/rockchip/rk3528-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3528-pinctrl.dtsi index ea051362fb26..59b75c91bbb7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3528-pinctrl.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3528-pinctrl.dtsi @@ -98,42 +98,42 @@ eth_pins: eth-pins { fephy { /omit-if-no-ref/ - fephym0_led_dpx: fephym0-led_dpx { + fephym0_led_dpx: fephym0-led-dpx { rockchip,pins = /* fephy_led_dpx_m0 */ <4 RK_PB5 2 &pcfg_pull_none>; }; /omit-if-no-ref/ - fephym0_led_link: fephym0-led_link { + fephym0_led_link: fephym0-led-link { rockchip,pins = /* fephy_led_link_m0 */ <4 RK_PC0 2 &pcfg_pull_none>; }; /omit-if-no-ref/ - fephym0_led_spd: fephym0-led_spd { + fephym0_led_spd: fephym0-led-spd { rockchip,pins = /* fephy_led_spd_m0 */ <4 RK_PB7 2 &pcfg_pull_none>; }; /omit-if-no-ref/ - fephym1_led_dpx: fephym1-led_dpx { + fephym1_led_dpx: fephym1-led-dpx { rockchip,pins = /* fephy_led_dpx_m1 */ <2 RK_PA4 5 &pcfg_pull_none>; }; /omit-if-no-ref/ - fephym1_led_link: fephym1-led_link { + fephym1_led_link: fephym1-led-link { rockchip,pins = /* fephy_led_link_m1 */ <2 RK_PA6 5 &pcfg_pull_none>; }; /omit-if-no-ref/ - fephym1_led_spd: fephym1-led_spd { + fephym1_led_spd: fephym1-led-spd { rockchip,pins = /* fephy_led_spd_m1 */ <2 RK_PA5 5 &pcfg_pull_none>; @@ -779,7 +779,7 @@ rgmii_miim: rgmii-miim { }; /omit-if-no-ref/ - rgmii_rx_bus2: rgmii-rx_bus2 { + rgmii_rx_bus2: rgmii-rx-bus2 { rockchip,pins = /* rgmii_rxd0 */ <3 RK_PA3 2 &pcfg_pull_none>, @@ -790,7 +790,7 @@ rgmii_rx_bus2: rgmii-rx_bus2 { }; /omit-if-no-ref/ - rgmii_tx_bus2: rgmii-tx_bus2 { + rgmii_tx_bus2: rgmii-tx-bus2 { rockchip,pins = /* rgmii_txd0 */ <3 RK_PA1 2 &pcfg_pull_none_drv_level_2>, @@ -801,7 +801,7 @@ rgmii_tx_bus2: rgmii-tx_bus2 { }; /omit-if-no-ref/ - rgmii_rgmii_clk: rgmii-rgmii_clk { + rgmii_rgmii_clk: rgmii-rgmii-clk { rockchip,pins = /* rgmii_rxclk */ <3 RK_PA5 2 &pcfg_pull_none>, @@ -810,7 +810,7 @@ rgmii_rgmii_clk: rgmii-rgmii_clk { }; /omit-if-no-ref/ - rgmii_rgmii_bus: rgmii-rgmii_bus { + rgmii_rgmii_bus: rgmii-rgmii-bus { rockchip,pins = /* rgmii_rxd2 */ <3 RK_PA7 2 &pcfg_pull_none>, diff --git a/arch/arm64/boot/dts/rockchip/rk3528-radxa-e20c.dts b/arch/arm64/boot/dts/rockchip/rk3528-radxa-e20c.dts index 9f6ccd9dd1f7..12eec2c1db22 100644 --- a/arch/arm64/boot/dts/rockchip/rk3528-radxa-e20c.dts +++ b/arch/arm64/boot/dts/rockchip/rk3528-radxa-e20c.dts @@ -198,6 +198,11 @@ &gmac1 { status = "okay"; }; +&gpu { + mali-supply = <&vdd_logic>; + status = "okay"; +}; + &i2c1 { pinctrl-names = "default"; pinctrl-0 = <&i2c1m0_xfer>; @@ -278,6 +283,7 @@ &saradc { &sdhci { bus-width = <8>; cap-mmc-highspeed; + mmc-hs200-1_8v; no-sd; no-sdio; non-removable; diff --git a/arch/arm64/boot/dts/rockchip/rk3528.dtsi b/arch/arm64/boot/dts/rockchip/rk3528.dtsi index d1c72b52aa4e..001a555c83b7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3528.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3528.dtsi @@ -95,6 +95,36 @@ scmi_clk: protocol@14 { }; }; + gpu_opp_table: opp-table-gpu { + compatible = "operating-points-v2"; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <875000 875000 1000000>; + opp-suspend; + }; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = <875000 875000 1000000>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <875000 875000 1000000>; + }; + + opp-700000000 { + opp-hz = /bits/ 64 <700000000>; + opp-microvolt = <900000 900000 1000000>; + }; + + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <950000 950000 1000000>; + }; + }; + pinctrl: pinctrl { compatible = "rockchip,rk3528-pinctrl"; rockchip,grf = <&ioc_grf>; @@ -439,13 +469,133 @@ ioc_grf: syscon@ff540000 { reg = <0x0 0xff540000 0x0 0x40000>; }; + pmu: power-management@ff600000 { + compatible = "rockchip,rk3528-pmu", "syscon", "simple-mfd"; + reg = <0x0 0xff600000 0x0 0x2000>; + + power: power-controller { + compatible = "rockchip,rk3528-power-controller"; + #power-domain-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + /* These power domains are grouped by VD_GPU */ + power-domain@4 { + reg = <4>; + clocks = <&cru ACLK_GPU_MALI>, + <&cru PCLK_GPU_ROOT>; + pm_qos = <&qos_gpu_m0>, + <&qos_gpu_m1>; + #power-domain-cells = <0>; + }; + + /* These power domains are grouped by VD_LOGIC */ + power-domain@5 { + reg = <5>; + pm_qos = <&qos_rkvdec>; + #power-domain-cells = <0>; + status = "disabled"; + }; + power-domain@6 { + reg = <6>; + pm_qos = <&qos_rkvenc>; + #power-domain-cells = <0>; + status = "disabled"; + }; + power-domain@7 { + reg = <7>; + pm_qos = <&qos_gmac0>, + <&qos_hdcp>, + <&qos_jpegdec>, + <&qos_rga2_m0ro>, + <&qos_rga2_m0wo>, + <&qos_sdmmc0>, + <&qos_usb2host>, + <&qos_vdpp>, + <&qos_vop>; + #power-domain-cells = <0>; + status = "disabled"; + }; + power-domain@8 { + reg = <8>; + pm_qos = <&qos_emmc>, + <&qos_fspi>, + <&qos_gmac1>, + <&qos_pcie>, + <&qos_sdio0>, + <&qos_sdio1>, + <&qos_tsp>, + <&qos_usb3otg>, + <&qos_vpu>; + #power-domain-cells = <0>; + status = "disabled"; + }; + }; + }; + + gpu: gpu@ff700000 { + compatible = "rockchip,rk3528-mali", "arm,mali-450"; + reg = <0x0 0xff700000 0x0 0x40000>; + assigned-clocks = <&cru ACLK_GPU_MALI>, + <&scmi_clk SCMI_CLK_GPU>; + assigned-clock-rates = <297000000>, <300000000>; + clocks = <&cru ACLK_GPU_MALI>, <&scmi_clk SCMI_CLK_GPU>; + clock-names = "bus", "core"; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "gp", + "gpmmu", + "pp", + "pp0", + "ppmmu0", + "pp1", + "ppmmu1"; + operating-points-v2 = <&gpu_opp_table>; + power-domains = <&power 4>; + resets = <&cru SRST_A_GPU>; + status = "disabled"; + }; + + spi0: spi@ff9c0000 { + compatible = "rockchip,rk3528-spi", + "rockchip,rk3066-spi"; + reg = <0x0 0xff9c0000 0x0 0x1000>; + clocks = <&cru CLK_SPI0>, <&cru PCLK_SPI0>; + clock-names = "spiclk", "apb_pclk"; + interrupts = ; + dmas = <&dmac 25>, <&dmac 24>; + dma-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi1: spi@ff9d0000 { + compatible = "rockchip,rk3528-spi", + "rockchip,rk3066-spi"; + reg = <0x0 0xff9d0000 0x0 0x1000>; + clocks = <&cru CLK_SPI1>, <&cru PCLK_SPI1>; + clock-names = "spiclk", "apb_pclk"; + interrupts = ; + dmas = <&dmac 31>, <&dmac 30>; + dma-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + uart0: serial@ff9f0000 { compatible = "rockchip,rk3528-uart", "snps,dw-apb-uart"; reg = <0x0 0xff9f0000 0x0 0x100>; clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 8>, <&dmac 9>; + dmas = <&dmac 9>, <&dmac 8>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -457,7 +607,7 @@ uart1: serial@ff9f8000 { clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 10>, <&dmac 11>; + dmas = <&dmac 11>, <&dmac 10>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -469,7 +619,7 @@ uart2: serial@ffa00000 { clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 12>, <&dmac 13>; + dmas = <&dmac 13>, <&dmac 12>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -481,7 +631,7 @@ uart3: serial@ffa08000 { clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 14>, <&dmac 15>; + dmas = <&dmac 15>, <&dmac 14>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -493,7 +643,7 @@ uart4: serial@ffa10000 { clocks = <&cru SCLK_UART4>, <&cru PCLK_UART4>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 16>, <&dmac 17>; + dmas = <&dmac 17>, <&dmac 16>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -505,7 +655,7 @@ uart5: serial@ffa18000 { clocks = <&cru SCLK_UART5>, <&cru PCLK_UART5>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 18>, <&dmac 19>; + dmas = <&dmac 19>, <&dmac 18>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -517,7 +667,7 @@ uart6: serial@ffa20000 { clocks = <&cru SCLK_UART6>, <&cru PCLK_UART6>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 20>, <&dmac 21>; + dmas = <&dmac 21>, <&dmac 20>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; @@ -529,7 +679,7 @@ uart7: serial@ffa28000 { clocks = <&cru SCLK_UART7>, <&cru PCLK_UART7>; clock-names = "baudclk", "apb_pclk"; interrupts = ; - dmas = <&dmac 22>, <&dmac 23>; + dmas = <&dmac 23>, <&dmac 22>; reg-io-width = <4>; reg-shift = <2>; status = "disabled"; diff --git a/arch/arm64/boot/dts/rockchip/rk3562.dtsi b/arch/arm64/boot/dts/rockchip/rk3562.dtsi index def504ffa326..f84676b47b27 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3562.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -351,7 +352,7 @@ pcie2x1: pcie@fe000000 { num-lanes = <1>; phys = <&combphy PHY_TYPE_PCIE>; phy-names = "pcie-phy"; - power-domains = <&power 15>; + power-domains = <&power RK3562_PD_PHP>; ranges = <0x01000000 0x0 0xfc100000 0x0 0xfc100000 0x0 0x100000 0x02000000 0x0 0xfc200000 0x0 0xfc200000 0x0 0x1e00000 0x03000000 0x3 0x00000000 0x3 0x00000000 0x0 0x40000000>; @@ -667,48 +668,48 @@ power: power-controller { #address-cells = <1>; #size-cells = <0>; - power-domain@8 { - reg = <8>; + power-domain@RK3562_PD_GPU { + reg = ; pm_qos = <&qos_gpu>; #power-domain-cells = <0>; }; - power-domain@7 { - reg = <7>; + power-domain@RK3562_PD_NPU { + reg = ; pm_qos = <&qos_npu>; #power-domain-cells = <0>; }; - power-domain@11 { - reg = <11>; + power-domain@RK3562_PD_VDPU { + reg = ; pm_qos = <&qos_rkvdec>; #power-domain-cells = <0>; }; - power-domain@12 { - reg = <12>; + power-domain@RK3562_PD_VI { + reg = ; pm_qos = <&qos_isp>, <&qos_vicap>; #power-domain-cells = <1>; #address-cells = <1>; #size-cells = <0>; - power-domain@10 { - reg = <10>; + power-domain@RK3562_PD_VEPU { + reg = ; pm_qos = <&qos_vepu>; #power-domain-cells = <0>; }; }; - power-domain@13 { - reg = <13>; + power-domain@RK3562_PD_VO { + reg = ; pm_qos = <&qos_vop>; #power-domain-cells = <1>; #address-cells = <1>; #size-cells = <0>; - power-domain@14 { - reg = <14>; + power-domain@RK3562_PD_RGA { + reg = ; pm_qos = <&qos_rga_rd>, <&qos_rga_wr>, <&qos_jpeg>; @@ -716,8 +717,8 @@ power-domain@14 { }; }; - power-domain@15 { - reg = <15>; + power-domain@RK3562_PD_PHP { + reg = ; pm_qos = <&qos_pcie>, <&qos_usb3>; #power-domain-cells = <0>; @@ -737,7 +738,7 @@ gpu: gpu@ff320000 { ; interrupt-names = "job", "mmu", "gpu"; operating-points-v2 = <&gpu_opp_table>; - power-domains = <&power 8>; + power-domains = <&power RK3562_PD_GPU>; #cooling-cells = <2>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi index 233eade30f21..645db9d3d297 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi @@ -469,7 +469,7 @@ regulator-state-mem { }; &i2c1 { - /* Unknown/unused device at 0x3c */ + /* Unused iSmartWare SW2001 encryption device at 0x3c */ status = "disabled"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi index e7ba477e75f9..7f578c50b4ad 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi @@ -53,7 +53,7 @@ led-0 { gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>; linux,default-trigger = "default-on"; pinctrl-names = "default"; - pinctrl-0 =<&blue_led>; + pinctrl-0 = <&blue_led>; }; led-1 { @@ -62,7 +62,7 @@ led-1 { gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>; linux,default-trigger = "heartbeat"; pinctrl-names = "default"; - pinctrl-0 =<&heartbeat_led>; + pinctrl-0 = <&heartbeat_led>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi index 3613661417b2..5c6f8cc401c9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi @@ -55,7 +55,7 @@ switch-cover { label = "cover"; gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_LOW>; linux,input-type = ; - linux,code = ; + linux,code = ; linux,can-disable; wakeup-event-action = ; wakeup-source; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi index 3473b1eef5cd..d0e38412d56a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi @@ -282,11 +282,11 @@ lcd: panel@0 { reg = <0>; backlight = <&backlight>; enable-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>; - rotation = <90>; power-supply = <&vcc_3v3>; + rotation = <90>; - port@0 { - panel_in_dsi: endpoint@0 { + port { + panel_in_dsi: endpoint { remote-endpoint = <&dsi0_out_con>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts index 3c127c5c2607..a9021c524afb 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts @@ -30,6 +30,7 @@ gmac1_clkin: external-gmac1-clock { fan: gpio_fan { compatible = "gpio-fan"; + fan-supply = <&vcc12v_dcin>; gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>; gpio-fan,speed-map = < 0 0>, diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts index b073a4d03e4f..b01f952b640e 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts @@ -22,6 +22,15 @@ aliases { mmc1 = &sdhci; }; + backlight: backlight { + compatible = "pwm-backlight"; + brightness-levels = <20 220>; + default-brightness-level = <100>; + num-interpolated-steps = <200>; + power-supply = <&vcc3v3_sys>; + pwms = <&pwm4 0 25000 0>; + }; + chosen: chosen { stdout-path = "serial2:1500000n8"; }; @@ -184,6 +193,47 @@ &cpu3 { cpu-supply = <&vdd_cpu>; }; +&dsi0 { + clock-master; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + panel@0 { + compatible = "wanchanglong,w552793baa", "raydium,rm67200"; + reg = <0>; + backlight = <&backlight>; + iovcc-supply = <&vcc3v3_lcd0_n>; + reset-gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>; + vdd-supply = <&vcc3v3_lcd0_n>; + vsn-supply = <&vcc5v0_sys>; + vsp-supply = <&vcc5v0_sys>; + + port { + panel_in_dsi: endpoint { + remote-endpoint = <&dsi0_out_panel>; + }; + }; + }; + +}; + +&dsi0_in { + dsi0_in_vp1: endpoint { + remote-endpoint = <&vp1_out_dsi0>; + }; +}; + +&dsi0_out { + dsi0_out_panel: endpoint { + remote-endpoint = <&panel_in_dsi>; + }; +}; + +&dsi_dphy0 { + status = "okay"; +}; + &gmac0 { assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>; assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>; @@ -581,6 +631,10 @@ &pmu_io_domains { status = "okay"; }; +&pwm4 { + status = "okay"; +}; + &saradc { vref-supply = <&vcca_1v8>; status = "okay"; @@ -672,8 +726,9 @@ &usb2phy1_otg { }; &vop { - assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; - assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; + assigned-clocks = <&cru DCLK_VOP0>, <&cru PLL_VPLL>, <&cru DCLK_VOP1>; + assigned-clock-parents = <&pmucru PLL_HPLL>, <&xin24m>, <&cru PLL_VPLL>; + assigned-clock-rates = <0>, <132000000>, <132000000>; status = "okay"; }; @@ -687,3 +742,10 @@ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { remote-endpoint = <&hdmi_in_vp0>; }; }; + +&vp1 { + vp1_out_dsi0: endpoint@ROCKCHIP_VOP2_EP_MIPI0 { + reg = ; + remote-endpoint = <&dsi0_in_vp1>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts index 3b31f0dd8f3b..718d1a2da8e5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts @@ -17,6 +17,19 @@ aliases { ethernet0 = &gmac0; }; + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&gpio4_a0_k1_pin>; + pinctrl-names = "default"; + + button-reset { + debounce-interval = <50>; + gpios = <&gpio4 RK_PA0 GPIO_ACTIVE_LOW>; + label = "RESET"; + linux,code = ; + }; + }; + gpio-leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -29,7 +42,6 @@ led-lan1 { function-enumerator = <1>; gpios = <&gpio3 RK_PD6 GPIO_ACTIVE_HIGH>; label = "LAN-1"; - linux,default-trigger = "netdev"; }; led-lan2 { @@ -39,7 +51,6 @@ led-lan2 { function-enumerator = <2>; gpios = <&gpio3 RK_PD7 GPIO_ACTIVE_HIGH>; label = "LAN-2"; - linux,default-trigger = "netdev"; }; power_led: led-sys { @@ -56,7 +67,6 @@ led-wan { function = LED_FUNCTION_WAN; gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>; label = "WAN"; - linux,default-trigger = "netdev"; }; }; }; @@ -127,6 +137,12 @@ eth_phy0_reset_pin: eth-phy0-reset-pin { }; }; + gpio-keys { + gpio4_a0_k1_pin: gpio4-a0-k1-pin { + rockchip,pins = <4 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + gpio-leds { lan1_led_pin: lan1-led-pin { rockchip,pins = <3 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dtsi index a28b4af10d13..e3f44ea4eabe 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dtsi @@ -18,12 +18,27 @@ / { aliases { mmc0 = &sdmmc0; mmc1 = &sdhci; + rtc0 = &hym8563; }; chosen: chosen { stdout-path = "serial2:1500000n8"; }; + adc-keys { + compatible = "adc-keys"; + io-channels = <&saradc 0>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <1800000>; + poll-interval = <100>; + + button-maskrom { + label = "MASKROM"; + linux,code = ; + press-threshold-microvolt = <0>; + }; + }; + hdmi-con { compatible = "hdmi-connector"; type = "a"; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5-v1.2-wifibt.dtso b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5-v1.2-wifibt.dtso new file mode 100644 index 000000000000..242ccfaf711b --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5-v1.2-wifibt.dtso @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * DT-overlay to enable the onboard WiFi and Bluetooth module present in v1.2 + * boards. Note that v1.1 boards use a different module, so this probably won't + * work there. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +&sdio { + #address-cells = <1>; + #size-cells = <0>; + + wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + clock-names = "lpo"; + clocks = <&hym8563>; + interrupt-names = "host-wake"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-0 = <&wifi_wake_host>; + pinctrl-names = "default"; + }; +}; + +&uart4 { + status = "okay"; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + clock-names = "lpo"; + clocks = <&hym8563>; + device-wakeup-gpios = <&gpio1 RK_PD7 GPIO_ACTIVE_HIGH>; + interrupt-names = "host-wakeup"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-0 = <&bt_reg_on>, <&bt_wake_host>, <&host_wake_bt>; + pinctrl-names = "default"; + shutdown-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>; + vbat-supply = <&vcc_3v3_s3>; + vddio-supply = <&vcc_1v8_s3>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts index b09e789c75c4..3386084f6318 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts @@ -196,6 +196,30 @@ vcc_5v0_device: regulator-vcc-5v0-device { vin-supply = <&vcc_12v0_dcin>; }; + vcc_5v0_typec0: regulator-vcc-5v0-typec0 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_otg0_pwren>; + regulator-name = "vcc_5v0_typec0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_5v0_device>; + }; + + vcc_5v0_usbhost: regulator-vcc-5v0-usbhost { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio4 RK_PA4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_host_pwren>; + regulator-name = "vcc_5v0_usbhost"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_5v0_device>; + }; + vcc_3v3_ufs_s0: regulator-vcc-ufs-s0 { compatible = "regulator-fixed"; regulator-name = "vcc_3v3_ufs_s0"; @@ -205,52 +229,80 @@ vcc_3v3_ufs_s0: regulator-vcc-ufs-s0 { regulator-max-microvolt = <3300000>; vin-supply = <&vcc_5v0_sys>; }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&hym8563>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_reg_on>; + reset-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; + }; +}; + +&combphy1_psu { + status = "okay"; }; &combphy0_ps { status = "okay"; }; +&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>; +}; + &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>; +}; + &gmac0 { phy-mode = "rgmii-id"; clock_in_out = "output"; - - snps,reset-gpio = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; - snps,reset-active-low; - snps,reset-delays-us = <0 20000 100000>; - + phy-handle = <&rgmii_phy0>; pinctrl-names = "default"; pinctrl-0 = <ð0m0_miim ð0m0_tx_bus2 ð0m0_rx_bus2 ð0m0_rgmii_clk ð0m0_rgmii_bus>; - - phy-handle = <&rgmii_phy0>; status = "okay"; }; &gmac1 { phy-mode = "rgmii-id"; clock_in_out = "output"; - - snps,reset-gpio = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>; - snps,reset-active-low; - snps,reset-delays-us = <0 20000 100000>; - + phy-handle = <&rgmii_phy1>; pinctrl-names = "default"; pinctrl-0 = <ð1m0_miim ð1m0_tx_bus2 ð1m0_rx_bus2 ð1m0_rgmii_clk - ð1m0_rgmii_bus - ðm0_clk1_25m_out>; - - phy-handle = <&rgmii_phy1>; + ð1m0_rgmii_bus>; status = "okay"; }; @@ -643,6 +695,58 @@ regulator-state-mem { &i2c2 { status = "okay"; + usbc0: typec-portc@22 { + compatible = "fcs,fusb302"; + reg = <0x22>; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&usbc0_interrupt>; + vbus-supply = <&vcc_5v0_typec0>; + + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + data-role = "dual"; + /* fusb302 supports PD Rev 2.0 Ver 1.2 */ + pd-revision = /bits/ 8 <0x2 0x0 0x1 0x2>; + power-role = "source"; + source-pdos = ; + + altmodes { + displayport { + svid = /bits/ 16 <0xff01>; + vdo = <0xffffffff>; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + usbc0_hs_ep: endpoint { + remote-endpoint = <&usb_drd0_hs_ep>; + }; + }; + port@1 { + reg = <1>; + usbc0_ss_ep: endpoint { + remote-endpoint = <&usb_drd0_ss_ep>; + }; + }; + port@2 { + reg = <2>; + usbc0_dp_ep: endpoint { + remote-endpoint = <&usbdp_phy_ep>; + }; + }; + }; + }; + }; + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; @@ -679,7 +783,11 @@ &mdio0 { rgmii_phy0: phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0x1>; - clocks = <&cru REFCLKO25M_GMAC0_OUT>; + pinctrl-names = "default"; + pinctrl-0 = <&gmac0_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; }; }; @@ -687,7 +795,11 @@ &mdio1 { rgmii_phy1: phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0x1>; - clocks = <&cru REFCLKO25M_GMAC1_OUT>; + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>; }; }; @@ -700,6 +812,15 @@ &pcie0 { }; &pinctrl { + gmac { + gmac0_rst: gmac0-rst { + rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + gmac1_rst: gmac1-rst { + rockchip,pins = <3 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + headphone { hp_det: hp-det { rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>; @@ -729,6 +850,48 @@ pcie_reset: pcie-reset { rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>; }; }; + + usb { + usb_host_pwren: usb-host-pwren { + rockchip,pins = <4 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + usb_otg0_pwren: usb-otg0-pwren { + rockchip,pins = <4 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + usbc0_interrupt: usbc0-interrupt { + rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + usbc0_sbu1: usbc0-sbu1 { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_down>; + }; + usbc0_sbu2: usbc0-sbu2 { + rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + wireless-bluetooth { + bt_reg_on: bt-reg-on { + rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + host_wake_bt: host-wake-bt { + rockchip,pins = <1 RK_PD4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + bt_wake_host: bt-wake-host { + rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + wireless-wlan { + wifi_wake_host: wifi-wake-host { + rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + wifi_reg_on: wifi-reg-on { + rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; }; &sai1 { @@ -756,6 +919,23 @@ &sdhci { status = "okay"; }; +&sdio { + bus-width = <4>; + cap-sdio-irq; + disable-wp; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + no-sd; + no-mmc; + non-removable; + sd-uhs-sdr50; + sd-uhs-sdr104; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vcc_1v8_s3>; + wakeup-source; + status = "okay"; +}; + &sdmmc { bus-width = <4>; cap-mmc-highspeed; @@ -770,11 +950,81 @@ &sdmmc { status = "okay"; }; +&u2phy0 { + status = "okay"; +}; + +&u2phy0_otg { + status = "okay"; +}; + +&u2phy1 { + status = "okay"; +}; + +&u2phy1_otg { + phy-supply = <&vcc_5v0_usbhost>; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0m0_xfer>; status = "okay"; }; +/* Used by Bluetooth modules, enabled in a version specific overlay */ +&uart4 { + pinctrl-0 = <&uart4m1_xfer &uart4m1_ctsn &uart4m1_rtsn>; + pinctrl-names = "default"; + uart-has-rtscts; +}; + +&usb_drd0_dwc3 { + usb-role-switch; + dr_mode = "otg"; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + usb_drd0_hs_ep: endpoint { + remote-endpoint = <&usbc0_hs_ep>; + }; + }; + + port@1 { + reg = <1>; + usb_drd0_ss_ep: endpoint { + remote-endpoint = <&usbc0_ss_ep>; + }; + }; + }; +}; + +&usb_drd1_dwc3 { + dr_mode = "host"; + status = "okay"; +}; + +&usbdp_phy { + mode-switch; + orientation-switch; + pinctrl-names = "default"; + pinctrl-0 = <&usbc0_sbu1 &usbc0_sbu2>; + sbu1-dc-gpios = <&gpio2 RK_PA6 GPIO_ACTIVE_HIGH>; + sbu2-dc-gpios = <&gpio2 RK_PA7 GPIO_ACTIVE_HIGH>; + status = "okay"; + + port { + usbdp_phy_ep: endpoint { + remote-endpoint = <&usbc0_dp_ep>; + }; + }; +}; + &vop { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts index 0902d694cef4..56527c56830e 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts @@ -282,6 +282,11 @@ ð1m0_rgmii_bus status = "okay"; }; +&gpu { + mali-supply = <&vdd_gpu_s0>; + status = "okay"; +}; + &hdmi { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-luckfox-core3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-luckfox-core3576.dtsi new file mode 100644 index 000000000000..9187012d6fa4 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-luckfox-core3576.dtsi @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2024 Rockchip Electronics Co., Ltd. + * Copyright (c) 2025 John Clark + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "rk3576.dtsi" + +/ { + model = "Luckfox Core3576 Module"; + compatible = "luckfox,core3576","rockchip,rk3576"; + + aliases { + mmc0 = &sdhci; + }; + + chosen { + stdout-path = "serial0:1500000n8"; + }; + + hdmi-con { + compatible = "hdmi-connector"; + hdmi-pwr-supply = <&vcc_5v0_hdmi>; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + vbus_5v0_typec: regulator-vbus-5v0-typec { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_otg0_pwr_en>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vbus5v0_typec"; + vin-supply = <&vcc_5v0_device>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-name = "vcc_1v1_nldo_s3"; + vin-supply = <&vcc_5v0_sys>; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-name = "vcc_2v0_pldo_s3"; + vin-supply = <&vcc_5v0_sys>; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_3v3_pcie: regulator-vcc-3v3-pcie { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio4 RK_PA0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pwr_en>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3_pcie"; + startup-delay-us = <1000>; + vin-supply = <&vcc_5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_3v3_rtc_s5: regulator-vcc-3v3-rtc-s5 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3_rtc_s5"; + vin-supply = <&vcc_5v0_sys>; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_5v0_dcin: regulator-vcc-5v0-dcin { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_dcin"; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_5v0_device: regulator-vcc-5v0-device { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_device"; + vin-supply = <&vcc_5v0_dcin>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_5v0_hdmi: regulator-vcc-5v0-hdmi { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_con_en>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_hdmi"; + vin-supply = <&vcc_5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_5v0_host: regulator-vcc-5v0-host { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_host_pwr_en>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_host"; + vin-supply = <&vcc_5v0_device>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_5v0_sys: regulator-vcc-5v0-sys { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_sys"; + vin-supply = <&vcc_5v0_dcin>; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; +}; + +&combphy0_ps { + status = "okay"; +}; + +&combphy1_psu { + status = "okay"; +}; + +&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>; +}; + +&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>; +}; + +&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>; + }; +}; + +&hdptxphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + pmic@23 { + compatible = "rockchip,rk806"; + reg = <0x23>; + #gpio-cells = <2>; + gpio-controller; + interrupt-parent = <&gpio0>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, + <&rk806_dvs2_null>, <&rk806_dvs3_null>; + system-power-controller; + + vcc1-supply = <&vcc_5v0_sys>; + vcc2-supply = <&vcc_5v0_sys>; + vcc3-supply = <&vcc_5v0_sys>; + vcc4-supply = <&vcc_5v0_sys>; + vcc5-supply = <&vcc_5v0_sys>; + vcc6-supply = <&vcc_5v0_sys>; + vcc7-supply = <&vcc_5v0_sys>; + vcc8-supply = <&vcc_5v0_sys>; + vcc9-supply = <&vcc_5v0_sys>; + vcc10-supply = <&vcc_5v0_sys>; + vcc11-supply = <&vcc_2v0_pldo_s3>; + vcc12-supply = <&vcc_5v0_sys>; + vcc13-supply = <&vcc_1v1_nldo_s3>; + vcc14-supply = <&vcc_1v1_nldo_s3>; + vcca-supply = <&vcc_5v0_sys>; + + rk806_dvs1_null: dvs1-null-pins { + pins = "gpio_pwrctrl2"; + 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 = <750000>; + 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-always-on; + 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-cells = <0>; + clock-output-names = "hym8563"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&hym8563_int>; + wakeup-source; + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_reset>; + reset-gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc_3v3_pcie>; + status = "okay"; +}; + +&pinctrl { + hdmi { + hdmi_con_en: hdmi-con-en { + rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hym8563 { + hym8563_int: hym8563-int { + rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + pcie { + pcie_pwr_en: pcie-pwr-en { + rockchip,pins = <4 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + pcie_reset: pcie-reset { + rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb { + usb_host_pwr_en: usb-host-pwr-en { + rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + usb_otg0_pwr_en: usb-otg0-pwr-en { + rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + usbc0_int: usbc0-int { + rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&rng { + status = "okay"; +}; + +&saradc { + vref-supply = <&vcca_1v8_s0>; + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + full-pwr-cycle-in-suspend; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + no-sd; + no-sdio; + non-removable; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vccio_sd_s0>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4m1_xfer &uart4m1_ctsn>; + status = "okay"; +}; + +&u2phy1 { + status = "okay"; +}; + +&u2phy1_otg { + phy-supply = <&vcc_5v0_host>; + status = "okay"; +}; + +&usb_drd1_dwc3 { + dr_mode = "host"; + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp0 { + vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { + reg = ; + remote-endpoint = <&hdmi_in_vp0>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-luckfox-omni3576.dts b/arch/arm64/boot/dts/rockchip/rk3576-luckfox-omni3576.dts new file mode 100644 index 000000000000..6c75959adfe1 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-luckfox-omni3576.dts @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2024 Rockchip Electronics Co., Ltd. + * Copyright (c) 2025 John Clark + */ + +/dts-v1/; + +#include "rk3576-luckfox-core3576.dtsi" + +/ { + model = "Luckfox Omni3576 Carrier Board"; + compatible = "luckfox,omni3576", "luckfox,core3576", "rockchip,rk3576"; + + aliases { + mmc1 = &sdmmc; + }; + + leds: leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_green_pin>; + + green_led: green-led { + color = ; + function = LED_FUNCTION_HEARTBEAT; + gpios = <&gpio1 RK_PD5 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; +}; + +&pinctrl { + leds { + led_green_pin: led-green-pin { + rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + no-sdio; + sd-uhs-sdr104; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vccio_sd_s0>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts b/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts new file mode 100644 index 000000000000..cce34c541f7c --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts @@ -0,0 +1,941 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 FriendlyElec Computer Tech. Co., Ltd. + * Copyright (c) 2025 John Clark + */ + +/dts-v1/; + +#include +#include +#include +#include +#include +#include +#include +#include "rk3576.dtsi" + +/ { + model = "FriendlyElec NanoPi M5"; + compatible = "friendlyarm,nanopi-m5", "rockchip,rk3576"; + + aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; + mmc0 = &sdmmc; + }; + + chosen { + stdout-path = "serial0:1500000n8"; + }; + + hdmi-con { + compatible = "hdmi-connector"; + hdmi-pwr-supply = <&vcc5v_hdmi_tx>; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + keys { + compatible = "gpio-keys"; + + usr_button: key-1 { + debounce-interval = <50>; + gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_LOW>; + label = "user"; + linux,code = ; + pinctrl-names = "default"; + pinctrl-0 = <&usr_button_l>; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_sys: led-0 { + color = ; + function = LED_FUNCTION_HEARTBEAT; + gpios = <&gpio2 RK_PB3 GPIO_ACTIVE_HIGH>; + label = "sys"; + linux,default-trigger = "heartbeat"; + pinctrl-names = "default"; + pinctrl-0 = <&led_sys_h>; + }; + + led1: led-1 { + color = ; + function = LED_FUNCTION_LAN; + gpios = <&gpio4 RK_PC5 GPIO_ACTIVE_HIGH>; + label = "led1"; + linux,default-trigger = "netdev"; + pinctrl-names = "default"; + pinctrl-0 = <&led1_h>; + }; + + led2: led-2 { + color = ; + function = LED_FUNCTION_LAN; + gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>; + label = "led2"; + linux,default-trigger = "netdev"; + pinctrl-names = "default"; + pinctrl-0 = <&led2_h>; + }; + }; + + usb3_port2_5v: regulator-usb3-port2-5v { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb3_host_pwren_h>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "usb3_port2_5v"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc12v_dcin: regulator-vcc12v-dcin { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-name = "vcc12v_dcin"; + }; + + vcc3v3_m2_keym: regulator-vcc3v3-m2-keym { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pwren_h>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc3v3_m2_keym"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc3v3_sd_s0: regulator-vcc3v3-sd-s0 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_pwren_h>; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc3v3_sd_s0"; + vin-supply = <&vcc_3v3_s3>; + }; + + vcc5v0_sys_s5: regulator-vcc5v0-sys-s5 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc5v0_sys_s5"; + vin-supply = <&vcc12v_dcin>; + }; + + vcc5v0_usb_otg0: regulator-vcc5v0-usb-otg0 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PD1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_otg0_pwren_h>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc5v0_usb_otg0"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc5v_hdmi_tx: regulator-vcc5v-hdmi-tx { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc5v_hdmi_tx"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-name = "vcc_1v1_nldo_s3"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-name = "vcc_2v0_pldo_s3"; + vin-supply = <&vcc5v0_sys_s5>; + }; + + vcc_3v3_s0: regulator-vcc-3v3-s0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3_s0"; + vin-supply = <&vcc_3v3_s3>; + }; + + sound { + compatible = "simple-audio-card"; + pinctrl-names = "default"; + pinctrl-0 = <&hp_det_l>; + + simple-audio-card,format = "i2s"; + simple-audio-card,hp-det-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_LOW>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "realtek,rt5616-codec"; + + simple-audio-card,routing = + "Headphones", "HPOL", + "Headphones", "HPOR", + "IN1P", "Microphone Jack"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Microphone", "Microphone Jack"; + + simple-audio-card,codec { + sound-dai = <&rt5616>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + }; +}; + +&combphy0_ps { + status = "okay"; +}; + +&combphy1_psu { + status = "okay"; +}; + +&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>; +}; + +&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>; +}; + +&fspi1m1_pins { + /* gpio1_d5, gpio1_c4-c7 (clk, d0-d4) are for spi nor flash */ + /* gpio1_d0-d4 muxed to sai2 audio functions */ + rockchip,pins = + <1 RK_PD5 3 &pcfg_pull_none>, + <1 RK_PC4 3 &pcfg_pull_none>, + <1 RK_PC5 3 &pcfg_pull_none>, + <1 RK_PC6 3 &pcfg_pull_none>, + <1 RK_PC7 3 &pcfg_pull_none>; +}; + +&gmac0 { + clock_in_out = "output"; + phy-handle = <&rgmii_phy0>; + phy-mode = "rgmii-id"; + phy-supply = <&vcc_3v3_s3>; + pinctrl-names = "default"; + pinctrl-0 = <ð0m0_miim>, + <ð0m0_tx_bus2>, + <ð0m0_rx_bus2>, + <ð0m0_rgmii_clk>, + <ð0m0_rgmii_bus>; + status = "okay"; +}; + +&gmac1 { + clock_in_out = "output"; + phy-handle = <&rgmii_phy1>; + phy-mode = "rgmii-id"; + phy-supply = <&vcc_3v3_s3>; + 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>; + }; +}; + +&hdptxphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + pmic@23 { + compatible = "rockchip,rk806"; + reg = <0x23>; + #gpio-cells = <2>; + gpio-controller; + interrupt-parent = <&gpio0>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, + <&rk806_dvs2_null>, <&rk806_dvs3_null>; + system-power-controller; + + vcc1-supply = <&vcc5v0_sys_s5>; + vcc2-supply = <&vcc5v0_sys_s5>; + vcc3-supply = <&vcc5v0_sys_s5>; + vcc4-supply = <&vcc5v0_sys_s5>; + vcc5-supply = <&vcc5v0_sys_s5>; + vcc6-supply = <&vcc5v0_sys_s5>; + vcc7-supply = <&vcc5v0_sys_s5>; + vcc8-supply = <&vcc5v0_sys_s5>; + vcc9-supply = <&vcc5v0_sys_s5>; + vcc10-supply = <&vcc5v0_sys_s5>; + vcc11-supply = <&vcc_2v0_pldo_s3>; + vcc12-supply = <&vcc5v0_sys_s5>; + vcc13-supply = <&vcc_1v1_nldo_s3>; + vcc14-supply = <&vcc_1v1_nldo_s3>; + vcca-supply = <&vcc5v0_sys_s5>; + + rk806_dvs1_null: dvs1-null-pins { + pins = "gpio_pwrctrl1"; + 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_null: dvs2-null-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun0"; + }; + + 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_null: dvs3-null-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun0"; + }; + + 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-enable-ramp-delay = <400>; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-name = "vdd_cpu_big_s0"; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_npu_s0: dcdc-reg2 { + regulator-boot-on; + regulator-enable-ramp-delay = <400>; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-name = "vdd_npu_s0"; + regulator-ramp-delay = <12500>; + + 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-name = "vdd_cpu_lit_s0"; + regulator-ramp-delay = <12500>; + + 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-enable-ramp-delay = <400>; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <900000>; + regulator-name = "vdd_gpu_s0"; + regulator-ramp-delay = <12500>; + + 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 = <750000>; + 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-always-on; + 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-cells = <0>; + clock-output-names = "hym8563"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&hym8563_int>; + wakeup-source; + }; +}; + +&i2c5 { + clock-frequency = <200000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5m3_xfer>; + status = "okay"; + + rt5616: audio-codec@1b { + compatible = "realtek,rt5616"; + reg = <0x1b>; + assigned-clocks = <&cru CLK_SAI2_MCLKOUT>; + assigned-clock-rates = <12288000>; + clocks = <&cru CLK_SAI2_MCLKOUT>; + clock-names = "mclk"; + #sound-dai-cells = <0>; + }; +}; + +&mdio0 { + rgmii_phy0: phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + clocks = <&cru REFCLKO25M_GMAC0_OUT>; + interrupt-parent = <&gpio2>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&gmac0_int>, <&gmac0_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; + }; +}; + +&mdio1 { + rgmii_phy1: phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + clocks = <&cru REFCLKO25M_GMAC1_OUT>; + interrupt-parent = <&gpio3>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_int>, <&gmac1_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>; + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_perstn>; + reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_m2_keym>; + status = "okay"; +}; + +&pinctrl { + gmac { + gmac0_int: gmac0-int { + rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + gmac0_rst: gmac0-rst { + rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + gmac1_int: gmac1-int { + rockchip,pins = <3 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + gmac1_rst: gmac1-rst { + rockchip,pins = <3 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hym8563 { + hym8563_int: hym8563-int { + rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + keys { + usr_button_l: usr-button-l { + rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + leds { + led_sys_h: led-sys-h { + rockchip,pins = <2 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + led1_h: led1-h { + rockchip,pins = <4 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + led2_h: led2-h { + rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie { + pcie0_pwren_h: pcie0-pwren-h { + rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + pcie0_perstn: pcie0-perstn { + rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sdmmc { + sdmmc0_pwren_h: sdmmc0-pwren-h { + rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sound { + hp_det_l: hp-det-l { + rockchip,pins = <2 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb { + usb3_host_pwren_h: usb3-host-pwren-h { + rockchip,pins = <0 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + usb_otg0_pwren_h: usb-otg0-pwren-h { + rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&sai2 { + status = "okay"; +}; + +&saradc { + vref-supply = <&vcca_1v8_s0>; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + no-mmc; + no-sdio; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk>, <&sdmmc0_cmd>, <&sdmmc0_det>, <&sdmmc0_bus4>; + sd-uhs-sdr104; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vcc3v3_sd_s0>; + status = "okay"; +}; + +&sfc1 { + pinctrl-names = "default"; + pinctrl-0 = <&fspi1m1_csn0>, <&fspi1m1_pins>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + m25p,fast-read; + spi-max-frequency = <50000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <1>; + vcc-supply = <&vcc_1v8_s3>; + }; +}; + +&u2phy0 { + status = "okay"; +}; + +&u2phy0_otg { + phy-supply = <&vcc5v0_usb_otg0>; + status = "okay"; +}; + +&u2phy1 { + status = "okay"; +}; + +&u2phy1_otg { + phy-supply = <&usb3_port2_5v>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&usbdp_phy { + status = "okay"; +}; + +&usb_drd0_dwc3 { + dr_mode = "otg"; + extcon = <&u2phy0>; + status = "okay"; +}; + +&usb_drd1_dwc3 { + dr_mode = "host"; + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp0 { + vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { + reg = ; + remote-endpoint = <&hdmi_in_vp0>; + }; +}; + +&wdt { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts index 6756403111e7..9bc33422ced5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts @@ -37,6 +37,14 @@ hdmi_con_in: endpoint { }; }; + rfkill { + compatible = "rfkill-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_en_h>; + radio-type = "wlan"; + shutdown-gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>; + }; + leds: leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -57,13 +65,13 @@ user-led { }; }; - vcc_12v0_dcin: regulator-vcc-12v0-dcin { + vcc_5v0_dcin: regulator-vcc-5v0-dcin { compatible = "regulator-fixed"; regulator-always-on; regulator-boot-on; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - regulator-name = "vcc_12v0_dcin"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc_5v0_dcin"; }; vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { @@ -159,6 +167,19 @@ vcc_3v3_ufs_s0: regulator-vcc-ufs-s0 { vin-supply = <&vcc_5v0_sys>; }; + vcc_3v3_wifi: regulator-vcc-3v3-wifi { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio2 RK_PC7 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_wifi_pwr>; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3_wifi"; + vin-supply = <&vcc_3v3_s3>; + }; + vcc_5v0_device: regulator-vcc-5v0-device { compatible = "regulator-fixed"; regulator-always-on; @@ -166,7 +187,7 @@ vcc_5v0_device: regulator-vcc-5v0-device { regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-name = "vcc_5v0_device"; - vin-supply = <&vcc_12v0_dcin>; + vin-supply = <&vcc_5v0_sys>; }; vcc_5v0_host: regulator-vcc-5v0-host { @@ -180,7 +201,21 @@ vcc_5v0_host: regulator-vcc-5v0-host { regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-name = "vcc5v0_host"; - vin-supply = <&vcc_5v0_device>; + vin-supply = <&vcc_5v0_sys>; + }; + + vcc_5v0_otg: regulator-vcc-5v0-otg { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_otg_pwren>; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "vcc5v0_otg"; + vin-supply = <&vcc_5v0_sys>; }; vcc_5v0_sys: regulator-vcc-5v0-sys { @@ -190,10 +225,14 @@ vcc_5v0_sys: regulator-vcc-5v0-sys { regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-name = "vcc_5v0_sys"; - vin-supply = <&vcc_12v0_dcin>; + vin-supply = <&vcc_5v0_dcin>; }; }; +&combphy0_ps { + status = "okay"; +}; + &combphy1_psu { status = "okay"; }; @@ -265,6 +304,10 @@ hdmi_out_con: endpoint { }; }; +&hdmi_sound { + status = "okay"; +}; + &hdptxphy { status = "okay"; }; @@ -641,17 +684,27 @@ hym8563: rtc@51 { &mdio0 { rgmii_phy0: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c916"; reg = <0x1>; clocks = <&cru REFCLKO25M_GMAC0_OUT>; + assigned-clocks = <&cru REFCLKO25M_GMAC0_OUT>; + assigned-clock-rates = <25000000>; pinctrl-names = "default"; pinctrl-0 = <&rtl8211f_rst>; reset-assert-us = <20000>; reset-deassert-us = <100000>; - reset-gpio = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; }; }; +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_reset>; + reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc_3v3_pcie>; + status = "okay"; +}; + &pinctrl { hym8563 { hym8563_int: hym8563-int { @@ -678,13 +731,33 @@ pcie { pcie_pwren: pcie-pwren { rockchip,pins = <2 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; }; + pcie_reset: pcie-reset { + rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; }; usb { usb_host_pwren: usb-host-pwren { - rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; + rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; + }; + usb_otg_pwren: usb-otg-pwren { + rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_down>; + }; }; + + wifi { + usb_wifi_pwr: usb-wifi-pwr { + rockchip,pins = <2 RK_PC7 RK_FUNC_GPIO &pcfg_pull_down>; + }; + wifi_en_h: wifi-en-h { + rockchip,pins = <2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; +}; + +&sai6 { + status = "okay"; }; &sdmmc { @@ -721,15 +794,38 @@ &u2phy0 { status = "okay"; }; +&u2phy0_otg { + phy-supply = <&vcc_5v0_otg>; + status = "okay"; +}; + &u2phy1 { status = "okay"; }; +&u2phy1_otg { + phy-supply = <&vcc_5v0_host>; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0m0_xfer>; status = "okay"; }; +&ufshc { + status = "okay"; +}; + +&usbdp_phy { + status = "okay"; +}; + +&usb_drd0_dwc3 { + dr_mode = "host"; + status = "okay"; +}; + &usb_drd1_dwc3 { dr_mode = "host"; status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 1086482f0479..c3cdae8a5494 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -615,7 +615,7 @@ pcie1: pcie@22400000 { <0 0 0 2 &pcie1_intc 1>, <0 0 0 3 &pcie1_intc 2>, <0 0 0 4 &pcie1_intc 3>; - linux,pci-domain = <0>; + linux,pci-domain = <1>; max-link-speed = <2>; num-ib-windows = <8>; num-viewport = <8>; @@ -1155,12 +1155,14 @@ vop: vop@27d00000 { <&cru HCLK_VOP>, <&cru DCLK_VP0>, <&cru DCLK_VP1>, - <&cru DCLK_VP2>; + <&cru DCLK_VP2>, + <&hdptxphy>; clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", - "dclk_vp2"; + "dclk_vp2", + "pll_hdmiphy0"; iommus = <&vop_mmu>; power-domains = <&power RK3576_PD_VOP>; rockchip,grf = <&sys_grf>; @@ -1695,6 +1697,22 @@ sdmmc: mmc@2a310000 { status = "disabled"; }; + sdio: mmc@2a320000 { + compatible = "rockchip,rk3576-dw-mshc"; + reg = <0x0 0x2a320000 0x0 0x4000>; + clocks = <&cru HCLK_SDIO>, <&cru CCLK_SRC_SDIO>; + clock-names = "biu", "ciu"; + fifo-depth = <0x100>; + interrupts = ; + max-frequency = <200000000>; + pinctrl-0 = <&sdmmc1m0_clk &sdmmc1m0_cmd &sdmmc1m0_bus4>; + pinctrl-names = "default"; + power-domains = <&power RK3576_PD_SDGMAC>; + resets = <&cru SRST_H_SDIO>; + reset-names = "reset"; + status = "disabled"; + }; + sdhci: mmc@2a330000 { compatible = "rockchip,rk3576-dwcmshc", "rockchip,rk3588-dwcmshc"; reg = <0x0 0x2a330000 0x0 0x10000>; @@ -2391,6 +2409,7 @@ hdptxphy: hdmiphy@2b000000 { reg = <0x0 0x2b000000 0x0 0x2000>; clocks = <&cru CLK_PHY_REF_SRC>, <&cru PCLK_HDPTX_APB>; clock-names = "ref", "apb"; + #clock-cells = <0>; resets = <&cru SRST_P_HDPTX_APB>, <&cru SRST_HDPTX_INIT>, <&cru SRST_HDPTX_CMN>, <&cru SRST_HDPTX_LANE>; reset-names = "apb", "init", "cmn", "lane"; diff --git a/arch/arm64/boot/dts/rockchip/rk3582-radxa-e52c.dts b/arch/arm64/boot/dts/rockchip/rk3582-radxa-e52c.dts index e04f21d8c831..431ff77d4518 100644 --- a/arch/arm64/boot/dts/rockchip/rk3582-radxa-e52c.dts +++ b/arch/arm64/boot/dts/rockchip/rk3582-radxa-e52c.dts @@ -250,6 +250,7 @@ eeprom@50 { compatible = "belling,bl24c16a", "atmel,24c16"; reg = <0x50>; pagesize = <16>; + read-only; vcc-supply = <&vcc_3v3_pmu>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dts b/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dts index ae9274365bed..39197ee19837 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dts @@ -373,6 +373,20 @@ vcc5v0_host_en: vcc5v0-host-en { rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; }; }; + + wireless-bluetooth { + bt_reset_pin: bt-reset-pin { + rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + bt_wake_pin: bt-wake-pin { + rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + bt_wake_host_irq: bt-wake-host-irq { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; }; &pwm1 { @@ -767,6 +781,28 @@ &uart2 { status = "okay"; }; +&uart6 { + pinctrl-names = "default"; + pinctrl-0 = <&uart6m1_xfer &uart6m1_ctsn &uart6m1_rtsn>; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + clocks = <&hym8563>; + clock-names = "lpo"; + interrupt-parent = <&gpio0>; + interrupts = ; + interrupt-names = "host-wakeup"; + device-wakeup-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; + shutdown-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; + max-speed = <1500000>; + pinctrl-names = "default"; + pinctrl-0 = <&bt_wake_host_irq &bt_wake_pin &bt_reset_pin>; + vbat-supply = <&vcc_3v3_s3>; + vddio-supply = <&vcc_1v8_s3>; + }; +}; + &usbdp_phy1 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi index 7f874c77410c..6584d73660f6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi @@ -578,14 +578,14 @@ hdmim0_tx0_hpd: hdmim0-tx0-hpd { hdmim0_tx0_scl: hdmim0-tx0-scl { rockchip,pins = /* hdmim0_tx0_scl */ - <4 RK_PB7 5 &pcfg_pull_none>; + <4 RK_PB7 5 &pcfg_pull_none_drv_level_5_smt>; }; /omit-if-no-ref/ hdmim0_tx0_sda: hdmim0-tx0-sda { rockchip,pins = /* hdmim0_tx0_sda */ - <4 RK_PC0 5 &pcfg_pull_none>; + <4 RK_PC0 5 &pcfg_pull_none_drv_level_1_smt>; }; /omit-if-no-ref/ @@ -640,14 +640,14 @@ hdmim1_tx0_hpd: hdmim1-tx0-hpd { hdmim1_tx0_scl: hdmim1-tx0-scl { rockchip,pins = /* hdmim1_tx0_scl */ - <0 RK_PD5 11 &pcfg_pull_none>; + <0 RK_PD5 11 &pcfg_pull_none_drv_level_5_smt>; }; /omit-if-no-ref/ hdmim1_tx0_sda: hdmim1-tx0-sda { rockchip,pins = /* hdmim1_tx0_sda */ - <0 RK_PD4 11 &pcfg_pull_none>; + <0 RK_PD4 11 &pcfg_pull_none_drv_level_1_smt>; }; /omit-if-no-ref/ @@ -668,14 +668,14 @@ hdmim1_tx1_hpd: hdmim1-tx1-hpd { hdmim1_tx1_scl: hdmim1-tx1-scl { rockchip,pins = /* hdmim1_tx1_scl */ - <3 RK_PC6 5 &pcfg_pull_none>; + <3 RK_PC6 5 &pcfg_pull_none_drv_level_5_smt>; }; /omit-if-no-ref/ hdmim1_tx1_sda: hdmim1-tx1-sda { rockchip,pins = /* hdmim1_tx1_sda */ - <3 RK_PC5 5 &pcfg_pull_none>; + <3 RK_PC5 5 &pcfg_pull_none_drv_level_1_smt>; }; /omit-if-no-ref/ hdmim2_rx_cec: hdmim2-rx-cec { @@ -709,14 +709,14 @@ hdmim2_rx_sda: hdmim2-rx-sda { hdmim2_tx0_scl: hdmim2-tx0-scl { rockchip,pins = /* hdmim2_tx0_scl */ - <3 RK_PC7 5 &pcfg_pull_none>; + <3 RK_PC7 5 &pcfg_pull_none_drv_level_5_smt>; }; /omit-if-no-ref/ hdmim2_tx0_sda: hdmim2-tx0-sda { rockchip,pins = /* hdmim2_tx0_sda */ - <3 RK_PD0 5 &pcfg_pull_none>; + <3 RK_PD0 5 &pcfg_pull_none_drv_level_1_smt>; }; /omit-if-no-ref/ @@ -730,14 +730,14 @@ hdmim2_tx1_cec: hdmim2-tx1-cec { hdmim2_tx1_scl: hdmim2-tx1-scl { rockchip,pins = /* hdmim2_tx1_scl */ - <1 RK_PA4 5 &pcfg_pull_none>; + <1 RK_PA4 5 &pcfg_pull_none_drv_level_5_smt>; }; /omit-if-no-ref/ hdmim2_tx1_sda: hdmim2-tx1-sda { rockchip,pins = /* hdmim2_tx1_sda */ - <1 RK_PA3 5 &pcfg_pull_none>; + <1 RK_PA3 5 &pcfg_pull_none_drv_level_1_smt>; }; /omit-if-no-ref/ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi index cc37f082adea..b07543315f87 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi @@ -321,6 +321,7 @@ &sdmmc { bus-width = <4>; cap-mmc-highspeed; cap-sd-highspeed; + cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; disable-wp; max-frequency = <150000000>; no-sdio; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts index 8e912da299a2..ff1ba5ed56ef 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts @@ -391,6 +391,17 @@ hdmi1_out_con: endpoint { }; }; +&hdmi_receiver_cma { + status = "okay"; +}; + +&hdmi_receiver { + hpd-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_hpd>; + pinctrl-names = "default"; + status = "okay"; +}; + &hdptxphy0 { status = "okay"; }; @@ -582,6 +593,12 @@ rtl8211f_rst: rtl8211f-rst { }; + hdmirx { + hdmirx_hpd: hdmirx-5v-detection { + rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + hym8563 { hym8563_int: hym8563-int { rockchip,pins = <0 RK_PD4 RK_FUNC_GPIO &pcfg_pull_up>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi index 244c66faa161..fb48ddc04bcb 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi @@ -160,14 +160,15 @@ hdmim0_tx1_cec: hdmim0-tx1-cec { hdmim0_tx1_scl: hdmim0-tx1-scl { rockchip,pins = /* hdmim0_tx1_scl */ - <2 RK_PB5 4 &pcfg_pull_none>; + <2 RK_PB5 4 &pcfg_pull_none_drv_level_3_smt>; }; /omit-if-no-ref/ hdmim0_tx1_sda: hdmim0-tx1-sda { rockchip,pins = /* hdmim0_tx1_sda */ - <2 RK_PB4 4 &pcfg_pull_none>; + <2 RK_PB4 4 &pcfg_pull_none_drv_level_1_smt>; + }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-friendlyelec-cm3588-nas.dts b/arch/arm64/boot/dts/rockchip/rk3588-friendlyelec-cm3588-nas.dts index 8171fbfd819a..5fbbeb6f5a93 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-friendlyelec-cm3588-nas.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-friendlyelec-cm3588-nas.dts @@ -335,6 +335,17 @@ hdmi0_out_con: endpoint { }; }; +&hdmi_receiver_cma { + status = "okay"; +}; + +&hdmi_receiver { + hpd-gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_hpd>; + pinctrl-names = "default"; + status = "okay"; +}; + &hdptxphy0 { status = "okay"; }; @@ -478,6 +489,12 @@ key1_pin: key1-pin { }; }; + hdmirx { + hdmirx_hpd: hdmirx-5v-detection { + rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + pcie { pcie2_0_rst: pcie2-0-rst { rockchip,pins = <4 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-jaguar-ethernet-switch.dtso b/arch/arm64/boot/dts/rockchip/rk3588-jaguar-ethernet-switch.dtso new file mode 100644 index 000000000000..7d9b1f080b3f --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-jaguar-ethernet-switch.dtso @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Cherry Embedded Solutions GmbH + * + * Device Tree Overlay for the Ethernet Switch adapter for the Mezzanine + * connector on RK3588 Jaguar + * (manual: https://embedded.cherry.de/jaguar-ethernet-switch-user-manual/) + * + * This adapter has a KSZ9896 Ethernet Switch with 4 1GbE Ethernet connectors, + * two user controllable LEDs, and an M12 12-pin connector which exposes the + * following signals: + * - RS232/RS485 (max 250Kbps/500Kbps, RX pin1, TX pin2) + * - two digital inputs (pin4 routed to GPIO3_C5 on SoC, pin5 to GPIO4_B4) + * - two digital outputs (pin7 routed to GPIO3_D3 on SoC, pin8 to GPIO3_D1) + * - two analog inputs (pin10 to channel1 of ADS1015, pin11 to channel2) + * + * RK3588 Jaguar can be powered entirely through the adapter via the M8 3-pin + * connector (12-24V). + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +&{/} { + aliases { + ethernet1 = "/ethernet@fe1c0000"; + }; + + mezzanine-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_usr1_pin &led_usr2_pin>; + + led-1 { + gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>; + label = "USR1"; + }; + + led-2 { + gpios = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>; + label = "USR2"; + }; + }; +}; + +&gmac1 { + clock_in_out = "output"; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_rx_bus2 + &gmac1_tx_bus2 + &gmac1_rgmii_clk + &gmac1_rgmii_bus + ð1_pins>; + rx_delay = <0x0>; + tx_delay = <0x0>; + status = "okay"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&i2c1 { + #address-cells = <1>; + /* + * ADS1015 can handle high-speed (HS) mode (up to 3.4MHz) on I2C bus, + * but SoC can handle only up to 400kHz. + */ + clock-frequency = <400000>; + #size-cells = <0>; + status = "okay"; + + adc@48 { + compatible = "ti,ads1015"; + reg = <0x48>; + #address-cells = <1>; + interrupt-parent = <&gpio3>; + interrupts = ; + pinctrl-0 = <&adc_alert>; + pinctrl-names = "default"; + #io-channel-cells = <1>; + #size-cells = <0>; + + channel@1 { + reg = <5>; /* Single-ended between AIN1 and GND */ + ti,datarate = <0>; + ti,gain = <5>; + }; + + channel@2 { + reg = <6>; /* Single-ended between AIN2 and GND */ + ti,datarate = <0>; + ti,gain = <5>; + }; + }; + + switch@5f { + compatible = "microchip,ksz9896"; + reg = <0x5f>; + interrupt-parent = <&gpio3>; + interrupts = ; /* ETH_INTRP_N */ + pinctrl-0 = <ð_reset_n ð_intrp_n>; + pinctrl-names = "default"; + reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>; /* ETH_RESET */ + microchip,synclko-disable; /* CLKO_25_125 only routed to TP1 */ + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + lan1: port@0 { + reg = <0>; + label = "ETH1"; + }; + + lan2: port@1 { + reg = <1>; + label = "ETH2"; + }; + + lan3: port@2 { + reg = <2>; + label = "ETH3"; + }; + + lan4: port@3 { + reg = <3>; + label = "ETH4"; + }; + + port@5 { + reg = <5>; + ethernet = <&gmac1>; + label = "CPU"; + phy-mode = "rgmii-id"; + rx-internal-delay-ps = <2000>; + tx-internal-delay-ps = <2000>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pinctrl { + adc { + adc_alert: adc-alert-irq { + rockchip,pins = + <3 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + ethernet { + eth_intrp_n: eth-intrp-n { + rockchip,pins = + <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + eth_reset_n: eth-reset-n { + rockchip,pins = + <3 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + leds { + led_usr1_pin: led-usr1-pin { + rockchip,pins = + <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + led_usr2_pin: led-usr2-pin { + rockchip,pins = + <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; +}; + +&uart9 { + /* GPIO3_D0/EN_RS485_MODE for switching between RS232 and RS485 */ + pinctrl-0 = <&uart9m2_xfer &uart9m2_rtsn>; + pinctrl-names = "default"; + linux,rs485-enabled-at-boot-time; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts b/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts index ebe77cdd24e8..176925d0a1a8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts @@ -10,6 +10,7 @@ #include #include #include +#include "rk8xx.h" #include "rk3588.dtsi" / { @@ -693,6 +694,7 @@ pmic@0 { vcc13-supply = <&vcc_1v1_nldo_s3>; vcc14-supply = <&vcc_1v1_nldo_s3>; vcca-supply = <&vcc5v0_sys>; + rockchip,reset-mode = ; rk806_dvs1_null: dvs1-null-pins { pins = "gpio_pwrctrl1"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts index 121e4d1c3fa5..8222f1fae8fa 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts @@ -77,7 +77,7 @@ &analog_sound { pinctrl-names = "default"; pinctrl-0 = <&hp_detect>; simple-audio-card,aux-devs = <&speaker_amp>, <&headphone_amp>; - simple-audio-card,hp-det-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_LOW>; + simple-audio-card,hp-det-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>; simple-audio-card,widgets = "Microphone", "Onboard Microphone", "Microphone", "Microphone Jack", diff --git a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5.dtsi index 91d56c34a1e4..8a8f3b26754d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5.dtsi @@ -365,6 +365,8 @@ &sdhci { max-frequency = <200000000>; mmc-hs400-1_8v; mmc-hs400-enhanced-strobe; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vcc_1v8_s3>; 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 new file mode 100644 index 000000000000..973d39a7e0e0 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi @@ -0,0 +1,878 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; + +#include +#include +#include +#include "rk3588.dtsi" + +/ { + aliases { + mmc0 = &sdhci; + mmc1 = &sdmmc; + mmc2 = &sdio; + }; + + chosen { + stdout-path = "serial2:1500000n8"; + }; + + hdmi0-con { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi0_con_in: endpoint { + remote-endpoint = <&hdmi0_out_con>; + }; + }; + }; + + hdmi1-con { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi1_con_in: endpoint { + remote-endpoint = <&hdmi1_out_con>; + }; + }; + }; + + fan: pwm-fan { + compatible = "pwm-fan"; + cooling-levels = <0 120 150 180 210 240 255>; + fan-supply = <&vcc5v0_sys>; + pwms = <&pwm1 0 50000 0>; + #cooling-cells = <2>; + }; + + rfkill-bt { + compatible = "rfkill-gpio"; + label = "rfkill-m2-bt"; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; + }; + + vcc3v3_pcie2x1l0: regulator-vcc3v3-pcie2x1l0 { + compatible = "regulator-fixed"; + enable-active-high; + regulator-name = "vcc3v3_pcie2x1l0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <50000>; + vin-supply = <&vcc5v0_sys>; + status = "disabled"; + }; + + vcc3v3_pcie2x1l2: regulator-vcc3v3-pcie2x1l2 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_pcie2x1l2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <5000>; + vin-supply = <&vcc_3v3_s3>; + }; + + vcc3v3_pcie30: regulator-vcc3v3-pcie30 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_vcc3v3_en>; + regulator-name = "vcc3v3_pcie30"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <5000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc5v0_host: regulator-vcc5v0-host { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_host"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_sys>; + }; + + 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>; + }; + + vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_1v1_nldo_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <&vcc5v0_sys>; + }; +}; + +&combphy0_ps { + status = "okay"; +}; + +&combphy1_ps { + status = "okay"; +}; + +&combphy2_psu { + status = "okay"; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; +}; + +&cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; +}; + +&cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; +}; + +&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>; +}; + +&gpu { + mali-supply = <&vdd_gpu_s0>; + status = "okay"; +}; + +&hdmi0 { + status = "okay"; +}; + +&hdmi0_in { + hdmi0_in_vp0: endpoint { + remote-endpoint = <&vp0_out_hdmi0>; + }; +}; + +&hdmi0_out { + hdmi0_out_con: endpoint { + remote-endpoint = <&hdmi0_con_in>; + }; +}; + +&hdmi0_sound { + status = "okay"; +}; + +&hdmi1 { + pinctrl-0 = <&hdmim0_tx1_cec &hdmim0_tx1_hpd + &hdmim1_tx1_scl &hdmim1_tx1_sda>; + status = "okay"; +}; + +&hdmi1_in { + hdmi1_in_vp1: endpoint { + remote-endpoint = <&vp1_out_hdmi1>; + }; +}; + +&hdmi1_out { + hdmi1_out_con: endpoint { + remote-endpoint = <&hdmi1_con_in>; + }; +}; + +&hdmi1_sound { + status = "okay"; +}; + +&hdmi_receiver_cma { + status = "okay"; +}; + +&hdmi_receiver { + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_hpd>; + pinctrl-names = "default"; +}; + +&hdptxphy0 { + status = "okay"; +}; + +&hdptxphy1 { + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0m2_xfer>; + status = "okay"; + + vdd_cpu_big0_s0: regulator@42 { + compatible = "rockchip,rk8602"; + reg = <0x42>; + fcs,suspend-voltage-selector = <1>; + regulator-name = "vdd_cpu_big0_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <2300>; + vin-supply = <&vcc5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_big1_s0: regulator@43 { + compatible = "rockchip,rk8603", "rockchip,rk8602"; + reg = <0x43>; + fcs,suspend-voltage-selector = <1>; + regulator-name = "vdd_cpu_big1_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <2300>; + vin-supply = <&vcc5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; +}; + +&i2c6 { + status = "okay"; + + hym8563: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; + #clock-cells = <0>; + clock-output-names = "hym8563"; + pinctrl-names = "default"; + pinctrl-0 = <&hym8563_int>; + interrupt-parent = <&gpio0>; + interrupts = ; + wakeup-source; + }; +}; + +&i2c7 { + status = "okay"; + + es8316: audio-codec@11 { + compatible = "everest,es8316"; + reg = <0x11>; + clocks = <&cru I2S0_8CH_MCLKOUT>; + clock-names = "mclk"; + assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; + assigned-clock-rates = <12288000>; + #sound-dai-cells = <0>; + + port { + es8316_p0_0: endpoint { + remote-endpoint = <&i2s0_8ch_p0_0>; + }; + }; + }; +}; + +&i2s0_8ch { + pinctrl-names = "default"; + pinctrl-0 = <&i2s0_lrck + &i2s0_mclk + &i2s0_sclk + &i2s0_sdi0 + &i2s0_sdo0>; + status = "okay"; + + i2s0_8ch_p0: port { + i2s0_8ch_p0_0: endpoint { + dai-format = "i2s"; + mclk-fs = <256>; + remote-endpoint = <&es8316_p0_0>; + }; + }; +}; + +&i2s5_8ch { + status = "okay"; +}; + +&i2s6_8ch { + status = "okay"; +}; + +&package_thermal { + polling-delay = <1000>; + + trips { + package_fan0: package-fan0 { + temperature = <55000>; + hysteresis = <2000>; + type = "active"; + }; + + package_fan1: package-fan1 { + temperature = <65000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map0 { + trip = <&package_fan0>; + cooling-device = <&fan THERMAL_NO_LIMIT 1>; + }; + + map1 { + trip = <&package_fan1>; + cooling-device = <&fan 2 THERMAL_NO_LIMIT>; + }; + }; +}; + +&pcie2x1l0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_0_rst>; + reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie2x1l0>; + status = "okay"; +}; + +&pcie2x1l2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_2_rst>; + reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie2x1l2>; + status = "okay"; +}; + +&pcie30phy { + status = "okay"; +}; + +&pcie3x4 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_rst>; + reset-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie30>; + status = "okay"; +}; + +&pd_gpu { + domain-supply = <&vdd_gpu_s0>; +}; + +&pinctrl { + hym8563 { + hym8563_int: hym8563-int { + rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie2 { + pcie2_0_rst: pcie2-0-rst { + rockchip,pins = <4 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + pcie2_2_rst: pcie2-2-rst { + rockchip,pins = <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie3 { + pcie3_rst: pcie3-rst { + rockchip,pins = <4 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + pcie3_vcc3v3_en: pcie3-vcc3v3-en { + rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pwm1 { + status = "okay"; +}; + +&saradc { + vref-supply = <&avcc_1v8_s0>; + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + no-sdio; + no-sd; + non-removable; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + status = "okay"; +}; + +&sdmmc { + max-frequency = <200000000>; + no-sdio; + no-mmc; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; + disable-wp; + sd-uhs-sdr104; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vccio_sd_s0>; + status = "okay"; +}; + +&sfc { + pinctrl-names = "default"; + pinctrl-0 = <&fspim2_pins>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <104000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <1>; + vcc-supply = <&vcc_3v3_s3>; + }; +}; + +&spi2 { + status = "okay"; + assigned-clocks = <&cru CLK_SPI2>; + assigned-clock-rates = <200000000>; + pinctrl-names = "default"; + pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>; + num-cs = <1>; + + pmic@0 { + compatible = "rockchip,rk806"; + spi-max-frequency = <1000000>; + reg = <0x0>; + + interrupt-parent = <&gpio0>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, + <&rk806_dvs2_null>, <&rk806_dvs3_null>; + + system-power-controller; + + vcc1-supply = <&vcc5v0_sys>; + vcc2-supply = <&vcc5v0_sys>; + vcc3-supply = <&vcc5v0_sys>; + vcc4-supply = <&vcc5v0_sys>; + vcc5-supply = <&vcc5v0_sys>; + vcc6-supply = <&vcc5v0_sys>; + vcc7-supply = <&vcc5v0_sys>; + vcc8-supply = <&vcc5v0_sys>; + vcc9-supply = <&vcc5v0_sys>; + vcc10-supply = <&vcc5v0_sys>; + vcc11-supply = <&vcc_2v0_pldo_s3>; + vcc12-supply = <&vcc5v0_sys>; + vcc13-supply = <&vcc_1v1_nldo_s3>; + vcc14-supply = <&vcc_1v1_nldo_s3>; + vcca-supply = <&vcc5v0_sys>; + + gpio-controller; + #gpio-cells = <2>; + + 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"; + }; + + regulators { + vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 { + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_gpu_s0"; + regulator-enable-ramp-delay = <400>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { + 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; + }; + }; + + vdd_log_s0: dcdc-reg3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <675000>; + regulator-max-microvolt = <750000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_log_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vdd_vdenc_s0: vdd_vdenc_mem_s0: dcdc-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_vdenc_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_ddr_s0: dcdc-reg5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <675000>; + regulator-max-microvolt = <900000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_ddr_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <850000>; + }; + }; + + vdd2_ddr_s3: dcdc-reg6 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vdd2_ddr_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_2v0_pldo_s3: dcdc-reg7 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_2v0_pldo_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <2000000>; + }; + }; + + vcc_3v3_s3: dcdc-reg8 { + 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>; + }; + }; + + vddq_ddr_s0: dcdc-reg9 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vddq_ddr_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v8_s3: dcdc-reg10 { + 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>; + }; + }; + + avcc_1v8_s0: pldo-reg1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "avcc_1v8_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v8_s0: pldo-reg2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc_1v8_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + avdd_1v2_s0: pldo-reg3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-name = "avdd_1v2_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_3v3_s0: pldo-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <12500>; + regulator-name = "vcc_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-ramp-delay = <12500>; + regulator-name = "vccio_sd_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + pldo6_s3: pldo-reg6 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "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 = <750000>; + regulator-max-microvolt = <750000>; + regulator-name = "vdd_0v75_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vdd_ddr_pll_s0: nldo-reg2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-name = "vdd_ddr_pll_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <850000>; + }; + }; + + avdd_0v75_s0: nldo-reg3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + regulator-name = "avdd_0v75_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_0v85_s0: nldo-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-name = "vdd_0v85_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_0v75_s0: nldo-reg5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + regulator-name = "vdd_0v75_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; +}; + +&tsadc { + status = "okay"; +}; + +&uart2 { + pinctrl-0 = <&uart2m0_xfer>; + status = "okay"; +}; + +&u2phy1 { + status = "okay"; +}; + +&u2phy1_otg { + status = "okay"; +}; + +&u2phy2 { + status = "okay"; +}; + +&u2phy2_host { + /* connected to USB hub, which is powered by vcc5v0_sys */ + phy-supply = <&vcc5v0_sys>; + status = "okay"; +}; + +&u2phy3 { + status = "okay"; +}; + +&u2phy3_host { + phy-supply = <&vcc5v0_host>; + status = "okay"; +}; + +&usbdp_phy1 { + 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 { + dr_mode = "host"; + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp0 { + vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { + reg = ; + remote-endpoint = <&hdmi0_in_vp0>; + }; +}; + +&vp1 { + vp1_out_hdmi1: endpoint@ROCKCHIP_VOP2_EP_HDMI1 { + reg = ; + remote-endpoint = <&hdmi1_in_vp1>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dtsi index 6052787d2560..e5c474e4d02a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dtsi @@ -2,22 +2,9 @@ /dts-v1/; -#include -#include -#include -#include "rk3588.dtsi" +#include "rk3588-rock-5b-5bp-5t.dtsi" / { - aliases { - mmc0 = &sdhci; - mmc1 = &sdmmc; - mmc2 = &sdio; - }; - - chosen { - stdout-path = "serial2:1500000n8"; - }; - analog-sound { compatible = "audio-graph-card"; label = "rk3588-es8316"; @@ -35,28 +22,6 @@ analog-sound { pinctrl-0 = <&hp_detect>; }; - hdmi0-con { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi0_con_in: endpoint { - remote-endpoint = <&hdmi0_out_con>; - }; - }; - }; - - hdmi1-con { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi1_con_in: endpoint { - remote-endpoint = <&hdmi1_out_con>; - }; - }; - }; - leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -70,369 +35,19 @@ led_rgb_b { }; }; - fan: pwm-fan { - compatible = "pwm-fan"; - cooling-levels = <0 120 150 180 210 240 255>; - fan-supply = <&vcc5v0_sys>; - pwms = <&pwm1 0 50000 0>; - #cooling-cells = <2>; - }; - rfkill { compatible = "rfkill-gpio"; label = "rfkill-m2-wlan"; radio-type = "wlan"; shutdown-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; }; - - rfkill-bt { - compatible = "rfkill-gpio"; - label = "rfkill-m2-bt"; - radio-type = "bluetooth"; - shutdown-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; - }; - - vcc3v3_pcie2x1l0: regulator-vcc3v3-pcie2x1l0 { - compatible = "regulator-fixed"; - enable-active-high; - gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&pcie2_0_vcc3v3_en>; - regulator-name = "vcc3v3_pcie2x1l0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - startup-delay-us = <50000>; - vin-supply = <&vcc5v0_sys>; - }; - - vcc3v3_pcie2x1l2: regulator-vcc3v3-pcie2x1l2 { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie2x1l2"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - startup-delay-us = <5000>; - vin-supply = <&vcc_3v3_s3>; - }; - - vcc3v3_pcie30: regulator-vcc3v3-pcie30 { - compatible = "regulator-fixed"; - enable-active-high; - gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&pcie3_vcc3v3_en>; - regulator-name = "vcc3v3_pcie30"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - startup-delay-us = <5000>; - vin-supply = <&vcc5v0_sys>; - }; - - vcc5v0_host: regulator-vcc5v0-host { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc5v0_sys>; - }; - - 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>; - }; - - vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { - compatible = "regulator-fixed"; - regulator-name = "vcc_1v1_nldo_s3"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <&vcc5v0_sys>; - }; -}; - -&combphy0_ps { - status = "okay"; -}; - -&combphy1_ps { - status = "okay"; -}; - -&combphy2_psu { - status = "okay"; -}; - -&cpu_b0 { - cpu-supply = <&vdd_cpu_big0_s0>; -}; - -&cpu_b1 { - cpu-supply = <&vdd_cpu_big0_s0>; -}; - -&cpu_b2 { - cpu-supply = <&vdd_cpu_big1_s0>; -}; - -&cpu_b3 { - cpu-supply = <&vdd_cpu_big1_s0>; -}; - -&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>; -}; - -&gpu { - mali-supply = <&vdd_gpu_s0>; - status = "okay"; -}; - -&hdmi0 { - status = "okay"; -}; - -&hdmi0_in { - hdmi0_in_vp0: endpoint { - remote-endpoint = <&vp0_out_hdmi0>; - }; -}; - -&hdmi0_out { - hdmi0_out_con: endpoint { - remote-endpoint = <&hdmi0_con_in>; - }; -}; - -&hdmi0_sound { - status = "okay"; -}; - -&hdmi1 { - pinctrl-0 = <&hdmim0_tx1_cec &hdmim0_tx1_hpd - &hdmim1_tx1_scl &hdmim1_tx1_sda>; - status = "okay"; -}; - -&hdmi1_in { - hdmi1_in_vp1: endpoint { - remote-endpoint = <&vp1_out_hdmi1>; - }; -}; - -&hdmi1_out { - hdmi1_out_con: endpoint { - remote-endpoint = <&hdmi1_con_in>; - }; -}; - -&hdmi1_sound { - status = "okay"; -}; - -&hdmi_receiver_cma { - status = "okay"; }; &hdmi_receiver { hpd-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_hpd>; - pinctrl-names = "default"; status = "okay"; }; -&hdptxphy0 { - status = "okay"; -}; - -&hdptxphy1 { - status = "okay"; -}; - -&i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c0m2_xfer>; - status = "okay"; - - vdd_cpu_big0_s0: regulator@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big0_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_big1_s0: regulator@43 { - compatible = "rockchip,rk8603", "rockchip,rk8602"; - reg = <0x43>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big1_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; -}; - -&i2c6 { - status = "okay"; - - hym8563: rtc@51 { - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0>; - clock-output-names = "hym8563"; - pinctrl-names = "default"; - pinctrl-0 = <&hym8563_int>; - interrupt-parent = <&gpio0>; - interrupts = ; - wakeup-source; - }; -}; - -&i2c7 { - status = "okay"; - - es8316: audio-codec@11 { - compatible = "everest,es8316"; - reg = <0x11>; - clocks = <&cru I2S0_8CH_MCLKOUT>; - clock-names = "mclk"; - assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; - assigned-clock-rates = <12288000>; - #sound-dai-cells = <0>; - - port { - es8316_p0_0: endpoint { - remote-endpoint = <&i2s0_8ch_p0_0>; - }; - }; - }; -}; - -&i2s0_8ch { - pinctrl-names = "default"; - pinctrl-0 = <&i2s0_lrck - &i2s0_mclk - &i2s0_sclk - &i2s0_sdi0 - &i2s0_sdo0>; - status = "okay"; - - i2s0_8ch_p0: port { - i2s0_8ch_p0_0: endpoint { - dai-format = "i2s"; - mclk-fs = <256>; - remote-endpoint = <&es8316_p0_0>; - }; - }; -}; - -&i2s5_8ch { - status = "okay"; -}; - -&i2s6_8ch { - status = "okay"; -}; - -&package_thermal { - polling-delay = <1000>; - - trips { - package_fan0: package-fan0 { - temperature = <55000>; - hysteresis = <2000>; - type = "active"; - }; - - package_fan1: package-fan1 { - temperature = <65000>; - hysteresis = <2000>; - type = "active"; - }; - }; - - cooling-maps { - map0 { - trip = <&package_fan0>; - cooling-device = <&fan THERMAL_NO_LIMIT 1>; - }; - - map1 { - trip = <&package_fan1>; - cooling-device = <&fan 2 THERMAL_NO_LIMIT>; - }; - }; -}; - -&pcie2x1l0 { - pinctrl-names = "default"; - pinctrl-0 = <&pcie2_0_rst>; - reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie2x1l0>; - status = "okay"; -}; - -&pcie2x1l2 { - pinctrl-names = "default"; - pinctrl-0 = <&pcie2_2_rst>; - reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie2x1l2>; - status = "okay"; -}; - -&pcie30phy { - status = "okay"; -}; - -&pcie3x4 { - pinctrl-names = "default"; - pinctrl-0 = <&pcie3_rst>; - reset-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie30>; - status = "okay"; -}; - -&pd_gpu { - domain-supply = <&vdd_gpu_s0>; -}; - &pinctrl { hdmirx { hdmirx_hpd: hdmirx-5v-detection { @@ -440,506 +55,32 @@ hdmirx_hpd: hdmirx-5v-detection { }; }; - hym8563 { - hym8563_int: hym8563-int { - rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - leds { led_rgb_b: led-rgb-b { rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; }; }; + pcie2 { + pcie2_0_vcc3v3_en: pcie2-0-vcc-en { + rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + sound { hp_detect: hp-detect { rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; }; }; - - pcie2 { - pcie2_0_rst: pcie2-0-rst { - rockchip,pins = <4 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - pcie2_0_vcc3v3_en: pcie2-0-vcc-en { - rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - pcie2_2_rst: pcie2-2-rst { - rockchip,pins = <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - pcie3 { - pcie3_rst: pcie3-rst { - rockchip,pins = <4 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - pcie3_vcc3v3_en: pcie3-vcc3v3-en { - rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; -}; - -&pwm1 { - status = "okay"; -}; - -&saradc { - vref-supply = <&avcc_1v8_s0>; - status = "okay"; -}; - -&sdhci { - bus-width = <8>; - no-sdio; - no-sd; - non-removable; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - status = "okay"; -}; - -&sdmmc { - max-frequency = <200000000>; - no-sdio; - no-mmc; - bus-width = <4>; - cap-mmc-highspeed; - cap-sd-highspeed; - cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <&vcc_3v3_s3>; - vqmmc-supply = <&vccio_sd_s0>; - status = "okay"; -}; - -&sfc { - pinctrl-names = "default"; - pinctrl-0 = <&fspim2_pins>; - status = "okay"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <104000000>; - spi-rx-bus-width = <4>; - spi-tx-bus-width = <1>; - vcc-supply = <&vcc_3v3_s3>; - }; -}; - -&spi2 { - status = "okay"; - assigned-clocks = <&cru CLK_SPI2>; - assigned-clock-rates = <200000000>; - pinctrl-names = "default"; - pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>; - num-cs = <1>; - - pmic@0 { - compatible = "rockchip,rk806"; - spi-max-frequency = <1000000>; - reg = <0x0>; - - interrupt-parent = <&gpio0>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-names = "default"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, - <&rk806_dvs2_null>, <&rk806_dvs3_null>; - - system-power-controller; - - vcc1-supply = <&vcc5v0_sys>; - vcc2-supply = <&vcc5v0_sys>; - vcc3-supply = <&vcc5v0_sys>; - vcc4-supply = <&vcc5v0_sys>; - vcc5-supply = <&vcc5v0_sys>; - vcc6-supply = <&vcc5v0_sys>; - vcc7-supply = <&vcc5v0_sys>; - vcc8-supply = <&vcc5v0_sys>; - vcc9-supply = <&vcc5v0_sys>; - vcc10-supply = <&vcc5v0_sys>; - vcc11-supply = <&vcc_2v0_pldo_s3>; - vcc12-supply = <&vcc5v0_sys>; - vcc13-supply = <&vcc_1v1_nldo_s3>; - vcc14-supply = <&vcc_1v1_nldo_s3>; - vcca-supply = <&vcc5v0_sys>; - - gpio-controller; - #gpio-cells = <2>; - - 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"; - }; - - regulators { - vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 { - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_gpu_s0"; - regulator-enable-ramp-delay = <400>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { - 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; - }; - }; - - vdd_log_s0: dcdc-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <750000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_log_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_vdenc_s0: vdd_vdenc_mem_s0: dcdc-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_vdenc_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_ddr_s0: dcdc-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <900000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - vdd2_ddr_s3: dcdc-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vdd2_ddr_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vcc_2v0_pldo_s3: dcdc-reg7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <2000000>; - regulator-max-microvolt = <2000000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_2v0_pldo_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <2000000>; - }; - }; - - vcc_3v3_s3: dcdc-reg8 { - 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>; - }; - }; - - vddq_ddr_s0: dcdc-reg9 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vddq_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s3: dcdc-reg10 { - 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>; - }; - }; - - avcc_1v8_s0: pldo-reg1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "avcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s0: pldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "vcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - avdd_1v2_s0: pldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-name = "avdd_1v2_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_3v3_s0: pldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-ramp-delay = <12500>; - regulator-name = "vcc_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-ramp-delay = <12500>; - regulator-name = "vccio_sd_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pldo6_s3: pldo-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "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 = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_ddr_pll_s0: nldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_ddr_pll_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - avdd_0v75_s0: nldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "avdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v85_s0: nldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_0v85_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v75_s0: nldo-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - }; -}; - -&tsadc { - status = "okay"; -}; - -&uart2 { - pinctrl-0 = <&uart2m0_xfer>; - status = "okay"; -}; - -&u2phy1 { - status = "okay"; -}; - -&u2phy1_otg { - status = "okay"; -}; - -&u2phy2 { - status = "okay"; -}; - -&u2phy2_host { - /* connected to USB hub, which is powered by vcc5v0_sys */ - phy-supply = <&vcc5v0_sys>; - status = "okay"; -}; - -&u2phy3 { - status = "okay"; -}; - -&u2phy3_host { - phy-supply = <&vcc5v0_host>; - status = "okay"; -}; - -&usbdp_phy1 { - 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 { - dr_mode = "host"; - status = "okay"; }; &usb_host2_xhci { status = "okay"; }; -&vop { +&vcc3v3_pcie2x1l0 { + gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_0_vcc3v3_en>; status = "okay"; }; - -&vop_mmu { - status = "okay"; -}; - -&vp0 { - vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { - reg = ; - remote-endpoint = <&hdmi0_in_vp0>; - }; -}; - -&vp1 { - vp1_out_hdmi1: endpoint@ROCKCHIP_VOP2_EP_HDMI1 { - reg = ; - remote-endpoint = <&hdmi1_in_vp1>; - }; -}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts new file mode 100644 index 000000000000..f16ff0064309 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; + +#include "rk3588-rock-5b-5bp-5t.dtsi" + +/ { + model = "Radxa ROCK 5T"; + compatible = "radxa,rock-5t", "rockchip,rk3588"; + + analog-sound { + compatible = "audio-graph-card"; + label = "rk3588-es8316"; + + widgets = "Microphone", "Mic Jack", + "Headphone", "Headphones"; + + routing = "MIC2", "Mic Jack", + "Headphones", "HPOL", + "Headphones", "HPOR"; + + dais = <&i2s0_8ch_p0>; + hp-det-gpios = <&gpio4 RK_PC3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&hp_detect>; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_rgb_b>; + + led_rgb_b { + function = LED_FUNCTION_STATUS; + color = ; + gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + rfkill { + compatible = "rfkill-gpio"; + label = "rfkill-m2-wlan"; + radio-type = "wlan"; + shutdown-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>; + }; + + vcc3v3_pcie2x1l1: regulator-vcc3v3-pcie2x1l2 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_pcie2x1l1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <5000>; + vin-supply = <&vcc_3v3_s3>; + }; +}; + +&hdmi_receiver { + hpd-gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pcie2x1l1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_1_rst>; + reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie2x1l1>; + status = "okay"; +}; + +&pcie30phy { + data-lanes = <1 1 2 2>; +}; + +&pcie3x2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3x2_rst>; + reset-gpios = <&gpio4 RK_PB0 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie30>; + status = "okay"; +}; + +&pcie3x4 { + num-lanes = <2>; +}; + +&pinctrl { + hdmirx { + hdmirx_hpd: hdmirx-5v-detection { + rockchip,pins = <2 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + leds { + led_rgb_b: led-rgb-b { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie2 { + pcie2_1_rst: pcie2-1-rst { + rockchip,pins = <4 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + pcie2_0_vcc3v3_en: pcie2-0-vcc-en { + rockchip,pins = <2 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie3 { + pcie3x2_rst: pcie3x2-rst { + rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sound { + hp_detect: hp-detect { + rockchip,pins = <4 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb { + vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <1 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&vcc3v3_pcie2x1l0 { + gpios = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_0_vcc3v3_en>; + status = "okay"; +}; + +&vcc5v0_host { + enable-active-high; + gpio = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc5v0_host_en>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi index c4933a08dd1e..b44e89e1bb15 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include "rk8xx.h" #include "rk3588.dtsi" / { @@ -440,6 +441,7 @@ pmic@0 { vcc13-supply = <&vcc_1v1_nldo_s3>; vcc14-supply = <&vcc_1v1_nldo_s3>; vcca-supply = <&vcc5v0_sys>; + rockchip,reset-mode = ; rk806_dvs1_null: dvs1-null-pins { pins = "gpio_pwrctrl1"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-turing-rk1.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-turing-rk1.dtsi index 60ad272982ad..6daea8961fdd 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-turing-rk1.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-turing-rk1.dtsi @@ -398,17 +398,6 @@ rk806_dvs3_null: dvs3-null-pins { regulators { vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 { - /* - * RK3588's GPU power domain cannot be enabled - * without this regulator active, but it - * doesn't have to be on when the GPU PD is - * disabled. Because the PD binding does not - * currently allow us to express this - * relationship, we have no choice but to do - * this instead: - */ - regulator-always-on; - regulator-boot-on; regulator-min-microvolt = <550000>; regulator-max-microvolt = <950000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588j.dtsi b/arch/arm64/boot/dts/rockchip/rk3588j.dtsi index 3045cb3bd68c..9884a5df47df 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588j.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588j.dtsi @@ -28,7 +28,7 @@ cluster1_opp_table: opp-table-cluster1 { compatible = "operating-points-v2"; opp-shared; - opp-1200000000{ + opp-1200000000 { opp-hz = /bits/ 64 <1200000000>; opp-microvolt = <750000 750000 950000>; clock-latency-ns = <40000>; @@ -49,7 +49,7 @@ cluster2_opp_table: opp-table-cluster2 { compatible = "operating-points-v2"; opp-shared; - opp-1200000000{ + opp-1200000000 { opp-hz = /bits/ 64 <1200000000>; opp-microvolt = <750000 750000 950000>; clock-latency-ns = <40000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-coolpi-4b.dts b/arch/arm64/boot/dts/rockchip/rk3588s-coolpi-4b.dts index 8b717c4017a4..b2947b36fada 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-coolpi-4b.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-coolpi-4b.dts @@ -474,6 +474,7 @@ &sdmmc { bus-width = <4>; cap-mmc-highspeed; cap-sd-highspeed; + cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; disable-wp; max-frequency = <150000000>; no-sdio; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts b/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts index 873a2bd6a6de..55fc7cbef58d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "rk3588s.dtsi" @@ -456,6 +457,42 @@ &cpu_b3 { cpu-supply = <&vdd_cpu_big1_s0>; }; +&dsi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + panel@0 { + compatible = "huiling,hl055fhav028c", "himax,hx8399c"; + reg = <0>; + backlight = <&backlight>; + iovcc-supply = <&vcc3v3_lcd0_n>; + pinctrl-0 = <&lcd_rst>; + pinctrl-names = "default"; + reset-gpios = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>; + rotation = <90>; + vcc-supply = <&vcc3v3_lcd0_n>; + + port { + mipi_panel_in: endpoint { + remote-endpoint = <&dsi0_out_panel>; + }; + }; + }; +}; + +&dsi0_in { + dsi0_in_vp3: endpoint { + remote-endpoint = <&vp3_out_dsi0>; + }; +}; + +&dsi0_out { + dsi0_out_panel: endpoint { + remote-endpoint = <&mipi_panel_in>; + }; +}; + &gpu { mali-supply = <&vdd_gpu_s0>; status = "okay"; @@ -633,6 +670,10 @@ &i2s0_sdi0 status = "okay"; }; +&mipidcphy0 { + status = "okay"; +}; + &package_thermal { polling-delay = <1000>; @@ -762,11 +803,16 @@ led_pins: led-pins { }; }; - lcd_bl_en { + lcd { lcd_bl_en: lcd-bl-en { rockchip,pins = <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; }; + + lcd_rst: lcd-rst { + rockchip,pins = + <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; + }; }; pcie-pins { @@ -1239,3 +1285,21 @@ bluetooth { shutdown-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; }; }; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp3 { + #address-cells = <1>; + #size-cells = <0>; + + vp3_out_dsi0: endpoint@ROCKCHIP_VOP2_EP_MIPI0 { + reg = ; + remote-endpoint = <&dsi0_in_vp3>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts new file mode 100644 index 000000000000..7e179862da6e --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts @@ -0,0 +1,840 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; + +#include +#include +#include +#include +#include +#include "rk3588s.dtsi" + +/ { + model = "Firefly Station M3"; + compatible = "firefly,rk3588s-roc-pc", "rockchip,rk3588s"; + + aliases { + ethernet0 = &gmac1; + mmc0 = &sdhci; + mmc1 = &sdmmc; + }; + + analog-sound { + compatible = "simple-audio-card"; + pinctrl-names = "default"; + pinctrl-0 = <&hp_detect>; + simple-audio-card,name = "rockchip,es8388"; + simple-audio-card,bitclock-master = <&masterdai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&masterdai>; + simple-audio-card,hp-det-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,pin-switches = "Headphones"; + simple-audio-card,routing = + "Headphones", "LOUT1", + "Headphones", "ROUT1", + "LINPUT1", "Microphone Jack", + "RINPUT1", "Microphone Jack", + "LINPUT2", "Onboard Microphone", + "RINPUT2", "Onboard Microphone"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Microphone", "Onboard Microphone", + "Headphone", "Headphones"; + + masterdai: simple-audio-card,codec { + sound-dai = <&es8388>; + system-clock-frequency = <12288000>; + }; + + simple-audio-card,cpu { + sound-dai = <&i2s0_8ch>; + }; + }; + + chosen { + stdout-path = "serial2:1500000n8"; + }; + + hdmi-con { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi0_out_con>; + }; + }; + }; + + fan: fan { + compatible = "pwm-fan"; + cooling-levels = <60 100 140 160 185 220 255>; + fan-supply = <&vcc12v_dcin>; + pwms = <&pwm11 0 50000 1>; + #cooling-cells = <2>; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_pins>; + + led-0 { + color = ; + default-state = "on"; + function = LED_FUNCTION_POWER; + gpios = <&gpio1 RK_PD5 GPIO_ACTIVE_HIGH>; + }; + + led-1 { + color = ; + default-state = "off"; + gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>; + }; + + led-2 { + color = ; + default-state = "off"; + gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; + }; + }; + + 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>; + }; + + vbus5v0_typec: regulator-vbus5v0-typec { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&typec5v_pwren>; + regulator-name = "vbus5v0_typec"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc3v3_pcie20: regulator-vcc3v3-pcie20 { + compatible = "regulator-fixed"; + gpio = <&gpio1 RK_PD7 GPIO_ACTIVE_HIGH>; + regulator-name = "vcc3v3_pcie20"; + enable-active-high; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <5000>; + vin-supply = <&vcc12v_dcin>; + }; + + vcc5v0_host: regulator-vcc5v0-host { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc5v0_host_en>; + regulator-name = "vcc5v0_host"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_sys>; + }; + + 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 = <&vcc12v_dcin>; + }; + + vcc5v0_usb: regulator-vcc5v0-usb { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_usb"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc12v_dcin>; + }; +}; + +&combphy0_ps { + status = "okay"; +}; + +&combphy2_psu { + status = "okay"; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; +}; + +&cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; +}; + +&cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; +}; + +&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>; +}; + +&gmac1 { + clock_in_out = "output"; + phy-handle = <&rgmii_phy1>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_miim + &gmac1_tx_bus2 + &gmac1_rx_bus2 + &gmac1_rgmii_clk + &gmac1_rgmii_bus>; + status = "okay"; +}; + +&gpu { + mali-supply = <&vdd_gpu_s0>; + status = "okay"; +}; + +&hdmi0 { + status = "okay"; +}; + +&hdmi0_in { + hdmi0_in_vp0: endpoint { + remote-endpoint = <&vp0_out_hdmi0>; + }; +}; + +&hdmi0_out { + hdmi0_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&hdptxphy0 { + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0m2_xfer>; + status = "okay"; + + vdd_cpu_big0_s0: regulator@42 { + compatible = "rockchip,rk8602"; + reg = <0x42>; + fcs,suspend-voltage-selector = <1>; + regulator-name = "vdd_cpu_big0_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <2300>; + vin-supply = <&vcc5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_big1_s0: regulator@43 { + compatible = "rockchip,rk8603", "rockchip,rk8602"; + reg = <0x43>; + fcs,suspend-voltage-selector = <1>; + regulator-name = "vdd_cpu_big1_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <2300>; + vin-supply = <&vcc5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2m0_xfer>; + status = "okay"; + + vdd_npu_s0: regulator@42 { + compatible = "rockchip,rk8602"; + reg = <0x42>; + fcs,suspend-voltage-selector = <1>; + regulator-name = "vdd_npu_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <2300>; + vin-supply = <&vcc5v0_sys>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + hym8563: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; + #clock-cells = <0>; + clock-output-names = "hym8563"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&hym8563_int>; + }; +}; + +&i2c3 { + status = "okay"; + + es8388: audio-codec@11 { + compatible = "everest,es8388", "everest,es8328"; + reg = <0x11>; + clocks = <&cru I2S1_8CH_MCLKOUT>; + AVDD-supply = <&vcc_3v3_s0>; + DVDD-supply = <&vcc_1v8_s0>; + HPVDD-supply = <&vcc_3v3_s0>; + PVDD-supply = <&vcc_3v3_s0>; + assigned-clocks = <&cru I2S1_8CH_MCLKOUT>; + assigned-clock-rates = <12288000>; + #sound-dai-cells = <0>; + }; +}; + +&i2s0_8ch { + pinctrl-names = "default"; + pinctrl-0 = <&i2s0_lrck + &i2s0_mclk + &i2s0_sclk + &i2s0_sdi0 + &i2s0_sdo0>; + status = "okay"; +}; + +&mdio1 { + rgmii_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + pinctrl-names = "default"; + pinctrl-0 = <&rtl8211f_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_LOW>; + }; +}; + +&pcie2x1l1 { + reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>; + vpcie3v3-supply = <&vcc3v3_pcie20>; + status = "okay"; +}; + +&pd_gpu { + domain-supply = <&vdd_gpu_s0>; +}; + +&pinctrl { + hym8563 { + hym8563_int: hym8563-int { + rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + headphone { + hp_detect: hp-detect { + rockchip,pins = <1 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + leds { + led_pins: led-pins { + rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + rtl8211 { + rtl8211f_rst: rtl8211f-rst { + rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb { + typec5v_pwren: typec5v-pwren { + rockchip,pins = <1 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pwm11 { + pinctrl-names = "default"; + pinctrl-0 = <&pwm11m3_pins>; + status = "okay"; +}; + +&saradc { + vref-supply = <&vcc_1v8_s0>; + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-sd-highspeed; + disable-wp; + max-frequency = <150000000>; + no-sdio; + no-mmc; + sd-uhs-sdr104; + vmmc-supply = <&vcc_3v3_s3>; + vqmmc-supply = <&vccio_sd_s0>; + status = "okay"; +}; + +&spi2 { + assigned-clocks = <&cru CLK_SPI2>; + assigned-clock-rates = <200000000>; + num-cs = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>; + status = "okay"; + + pmic@0 { + compatible = "rockchip,rk806"; + reg = <0x0>; + interrupt-parent = <&gpio0>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, + <&rk806_dvs2_null>, <&rk806_dvs3_null>; + spi-max-frequency = <1000000>; + system-power-controller; + + vcc1-supply = <&vcc5v0_sys>; + vcc2-supply = <&vcc5v0_sys>; + vcc3-supply = <&vcc5v0_sys>; + vcc4-supply = <&vcc5v0_sys>; + vcc5-supply = <&vcc5v0_sys>; + vcc6-supply = <&vcc5v0_sys>; + vcc7-supply = <&vcc5v0_sys>; + vcc8-supply = <&vcc5v0_sys>; + vcc9-supply = <&vcc5v0_sys>; + vcc10-supply = <&vcc5v0_sys>; + vcc11-supply = <&vcc_2v0_pldo_s3>; + vcc12-supply = <&vcc5v0_sys>; + vcc13-supply = <&vcc_1v1_nldo_s3>; + vcc14-supply = <&vcc_1v1_nldo_s3>; + vcca-supply = <&vcc5v0_sys>; + + gpio-controller; + #gpio-cells = <2>; + + 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"; + }; + + regulators { + vdd_gpu_s0: dcdc-reg1 { + regulator-name = "vdd_gpu_s0"; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <400>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_lit_s0: dcdc-reg2 { + regulator-name = "vdd_cpu_lit_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_log_s0: dcdc-reg3 { + regulator-name = "vdd_log_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <675000>; + regulator-max-microvolt = <750000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vdd_vdenc_s0: dcdc-reg4 { + regulator-name = "vdd_vdenc_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_ddr_s0: dcdc-reg5 { + regulator-name = "vdd_ddr_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <675000>; + regulator-max-microvolt = <900000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <850000>; + }; + }; + + vcc_1v1_nldo_s3: vdd2_ddr_s3: dcdc-reg6 { + regulator-name = "vdd2_ddr_s3"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1100000>; + regulator-min-microvolt = <1100000>; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_2v0_pldo_s3: dcdc-reg7 { + regulator-name = "vdd_2v0_pldo_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <2000000>; + }; + }; + + vcc_3v3_s3: dcdc-reg8 { + regulator-name = "vcc_3v3_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vddq_ddr_s0: dcdc-reg9 { + regulator-name = "vddq_ddr_s0"; + regulator-always-on; + regulator-boot-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v8_s3: dcdc-reg10 { + regulator-name = "vcc_1v8_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + avcc_1v8_s0: pldo-reg1 { + regulator-name = "avcc_1v8_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v8_s0: pldo-reg2 { + regulator-name = "vcc_1v8_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + avdd_1v2_s0: pldo-reg3 { + regulator-name = "avdd_1v2_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_3v3_s0: pldo-reg4 { + regulator-name = "vcc_3v3_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vccio_sd_s0: pldo-reg5 { + regulator-name = "vccio_sd_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <12500>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + pldo6_s3: pldo-reg6 { + regulator-name = "pldo6_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_0v75_s3: nldo-reg1 { + regulator-name = "vdd_0v75_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vdd_ddr_pll_s0: nldo-reg2 { + regulator-name = "vdd_ddr_pll_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <850000>; + }; + }; + + avdd_0v75_s0: nldo-reg3 { + regulator-name = "avdd_0v75_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_0v85_s0: nldo-reg4 { + regulator-name = "vdd_0v85_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_0v75_s0: nldo-reg5 { + regulator-name = "vdd_0v75_s0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; +}; + +&tsadc { + status = "okay"; +}; + +&u2phy0 { + status = "okay"; +}; + +&u2phy0_otg { + status = "okay"; +}; + +&u2phy2 { + status = "okay"; +}; + +&u2phy3 { + status = "okay"; +}; + +&u2phy2_host { + phy-supply = <&vcc5v0_host>; + status = "okay"; +}; + +&u2phy3_host { + phy-supply = <&vcc5v0_host>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; + status = "okay"; +}; + +&uart7 { + pinctrl-0 = <&uart7m2_xfer>; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usb_host0_xhci { + extcon = <&u2phy0>; + status = "okay"; +}; + +&usb_host1_ehci { + status = "okay"; +}; + +&usb_host1_ohci { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp0 { + vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { + reg = ; + remote-endpoint = <&hdmi0_in_vp0>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk8xx.h b/arch/arm64/boot/dts/rockchip/rk8xx.h new file mode 100644 index 000000000000..a6fbef71c064 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk8xx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) */ +/* + * Device Tree defines for Rockchip RK8xx PMICs + * + * Copyright 2025 Cherry Embedded Solutions GmbH + * + * Author: Quentin Schulz + */ + +#ifndef _DT_MFD_ROCKCHIP_RK8XX_H +#define _DT_MFD_ROCKCHIP_RK8XX_H + +/* For use with rockchip,reset-mode property */ +#define RK806_RESTART 0 +#define RK806_RESET 1 +#define RK806_RESET_NOTIFY 2 + +#endif diff --git a/arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi b/arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi index 5c645437b507..b0475b7c655a 100644 --- a/arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi +++ b/arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi @@ -332,6 +332,41 @@ pcfg_pull_none_drv_level_0_smt: pcfg-pull-none-drv-level-0-smt { input-schmitt-enable; }; + /omit-if-no-ref/ + pcfg_pull_none_drv_level_1_smt: pcfg-pull-none-drv-level-1-smt { + bias-disable; + drive-strength = <1>; + input-schmitt-enable; + }; + + /omit-if-no-ref/ + pcfg_pull_none_drv_level_2_smt: pcfg-pull-none-drv-level-2-smt { + bias-disable; + drive-strength = <2>; + input-schmitt-enable; + }; + + /omit-if-no-ref/ + pcfg_pull_none_drv_level_3_smt: pcfg-pull-none-drv-level-3-smt { + bias-disable; + drive-strength = <3>; + input-schmitt-enable; + }; + + /omit-if-no-ref/ + pcfg_pull_none_drv_level_4_smt: pcfg-pull-none-drv-level-4-smt { + bias-disable; + drive-strength = <4>; + input-schmitt-enable; + }; + + /omit-if-no-ref/ + pcfg_pull_none_drv_level_5_smt: pcfg-pull-none-drv-level-5-smt { + bias-disable; + drive-strength = <5>; + input-schmitt-enable; + }; + /omit-if-no-ref/ pcfg_output_high: pcfg-output-high { output-high; diff --git a/arch/arm64/boot/dts/sophgo/Makefile b/arch/arm64/boot/dts/sophgo/Makefile new file mode 100644 index 000000000000..94f52cd7d994 --- /dev/null +++ b/arch/arm64/boot/dts/sophgo/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SOPHGO) += sg2000-milkv-duo-module-01-evb.dtb diff --git a/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01-evb.dts b/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01-evb.dts new file mode 100644 index 000000000000..a281fee0d76e --- /dev/null +++ b/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01-evb.dts @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; + +#include "sg2000-milkv-duo-module-01.dtsi" + +/ { + model = "Milk-V Duo Module 01 Evaluation Board"; + compatible = "milkv,duo-module-01-evb", "milkv,duo-module-01", "sophgo,sg2000"; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&pinctrl { + sdhci0_cfg: sdhci0-cfg { + sdhci0-cd-pins { + pinmux = ; + bias-pull-up; + drive-strength-microamp = <10800>; + power-source = <3300>; + }; + + sdhci0-clk-pins { + pinmux = ; + bias-pull-up; + drive-strength-microamp = <16100>; + power-source = <3300>; + }; + + sdhci0-cmd-pins { + pinmux = ; + bias-pull-up; + drive-strength-microamp = <10800>; + power-source = <3300>; + }; + + sdhci0-data-pins { + pinmux = , + , + , + ; + bias-pull-up; + drive-strength-microamp = <10800>; + power-source = <3300>; + }; + }; + + uart0_cfg: uart0-cfg { + uart0-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <10800>; + power-source = <3300>; + }; + }; +}; + +&uart0 { + pinctrl-0 = <&uart0_cfg>; + pinctrl-names = "default"; + status = "okay"; +}; + +&sdhci0 { + bus-width = <4>; + no-1-8-v; + no-mmc; + no-sdio; + disable-wp; + pinctrl-0 = <&sdhci0_cfg>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01.dtsi b/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01.dtsi new file mode 100644 index 000000000000..32c988f3c58f --- /dev/null +++ b/arch/arm64/boot/dts/sophgo/sg2000-milkv-duo-module-01.dtsi @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +#include +#include "sg2000.dtsi" + +/ { + model = "Milk-V Duo Module 01"; + compatible = "milkv,duo-module-01", "sophgo,sg2000"; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + serial4 = &uart4; + }; +}; + +&osc { + clock-frequency = <25000000>; +}; + +&emmc { + bus-width = <4>; + no-1-8-v; + cap-mmc-hw-reset; + no-sd; + no-sdio; + non-removable; + status = "okay"; +}; + +/* Wi-Fi */ +&sdhci1 { + bus-width = <4>; + cap-sdio-irq; + no-mmc; + no-sd; + non-removable; +}; diff --git a/arch/arm64/boot/dts/sophgo/sg2000.dtsi b/arch/arm64/boot/dts/sophgo/sg2000.dtsi new file mode 100644 index 000000000000..51177dfe9ed2 --- /dev/null +++ b/arch/arm64/boot/dts/sophgo/sg2000.dtsi @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +#define SOC_PERIPHERAL_IRQ(nr) GIC_SPI (nr) + +#include +#include +#include + +/ { + compatible = "sophgo,sg2000"; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <0>; + enable-method = "psci"; + i-cache-size = <32768>; + d-cache-size = <32768>; + next-level-cache = <&l2>; + }; + + l2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x20000>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; /* 512MiB */ + }; + + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = , + ; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + cpu_on = <0xc4000003>; + cpu_off = <0x84000002>; + }; + + soc { + gic: interrupt-controller@1f01000 { + compatible = "arm,cortex-a15-gic"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0x01f01000 0x1000>, + <0x01f02000 0x2000>; + }; + + pinctrl: pinctrl@3001000 { + compatible = "sophgo,sg2000-pinctrl"; + reg = <0x03001000 0x1000>, + <0x05027000 0x1000>; + reg-names = "sys", "rtc"; + }; + + clk: clock-controller@3002000 { + compatible = "sophgo,sg2000-clk"; + reg = <0x03002000 0x1000>; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + always-on; + clock-frequency = <25000000>; + }; +}; diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi index aba90d555f4e..5ac9e72478dd 100644 --- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi @@ -133,6 +133,53 @@ pins { }; }; + pwm3_pins_a: pwm3-0 { + pins { + pinmux = ; /* TIM3_CH2 */ + bias-pull-down; + drive-push-pull; + slew-rate = <0>; + }; + }; + + pwm3_sleep_pins_a: pwm3-sleep-0 { + pins { + pinmux = ; /* TIM3_CH2 */ + }; + }; + + pwm8_pins_a: pwm8-0 { + pins { + pinmux = , /* TIM8_CH1 */ + ; /* TIM8_CH4 */ + bias-pull-down; + drive-push-pull; + slew-rate = <0>; + }; + }; + + pwm8_sleep_pins_a: pwm8-sleep-0 { + pins { + pinmux = , /* TIM8_CH1 */ + ; /* TIM8_CH4 */ + }; + }; + + pwm12_pins_a: pwm12-0 { + pins { + pinmux = ; /* TIM12_CH2 */ + bias-pull-down; + drive-push-pull; + slew-rate = <0>; + }; + }; + + pwm12_sleep_pins_a: pwm12-sleep-0 { + pins { + pinmux = ; /* TIM12_CH2 */ + }; + }; + sdmmc1_b4_pins_a: sdmmc1-b4-0 { pins1 { pinmux = , /* SDMMC1_D0 */ @@ -209,6 +256,20 @@ pins1 { }; }; + tim10_counter_pins_a: tim10-counter-0 { + pins { + pinmux = ; /* TIM10_CH1 */ + bias-disable; + }; + }; + + tim10_counter_sleep_pins_a: tim10-counter-sleep-0 { + pins { + pinmux = ; /* TIM10_CH1 */ + bias-disable; + }; + }; + usart2_pins_a: usart2-0 { pins1 { pinmux = ; /* USART2_TX */ diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index 8d87865850a7..303abf915b8e 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -150,7 +150,7 @@ timer { , , ; - always-on; + arm,no-tick-in-suspend; }; soc@0 { @@ -291,6 +291,273 @@ rifsc: bus@42080000 { #access-controller-cells = <1>; ranges; + timers2: timer@40000000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40000000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM2>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 1>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@1 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <1>; + status = "disabled"; + }; + }; + + timers3: timer@40010000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40010000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM3>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 2>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@2 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <2>; + status = "disabled"; + }; + }; + + timers4: timer@40020000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40020000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM4>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 3>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@3 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <3>; + status = "disabled"; + }; + }; + + timers5: timer@40030000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40030000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM5>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 4>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@4 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <4>; + status = "disabled"; + }; + }; + + timers6: timer@40040000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40040000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM6>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 5>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + timer@5 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <5>; + status = "disabled"; + }; + }; + + timers7: timer@40050000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40050000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM7>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 6>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + timer@6 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <6>; + status = "disabled"; + }; + }; + + timers12: timer@40060000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40060000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM12>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 10>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@11 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <11>; + status = "disabled"; + }; + }; + + timers13: timer@40070000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40070000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM13>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 11>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@12 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <12>; + status = "disabled"; + }; + }; + + timers14: timer@40080000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40080000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM14>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 12>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@13 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <13>; + status = "disabled"; + }; + }; + lptimer1: timer@40090000 { compatible = "st,stm32mp25-lptimer", "st,stm32-lptimer"; reg = <0x40090000 0x400>; @@ -597,6 +864,136 @@ i2c7: i2c@40180000 { status = "disabled"; }; + timers10: timer@401c0000 { + compatible = "st,stm32mp25-timers"; + reg = <0x401c0000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM10>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 8>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@9 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <9>; + status = "disabled"; + }; + }; + + timers11: timer@401d0000 { + compatible = "st,stm32mp25-timers"; + reg = <0x401d0000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM11>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 9>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@10 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <10>; + status = "disabled"; + }; + }; + + timers1: timer@40200000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40200000 0x400>; + interrupts = , + , + , + ; + interrupt-names = "brk", "up", "trg-com", "cc"; + clocks = <&rcc CK_KER_TIM1>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 0>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@0 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <0>; + status = "disabled"; + }; + }; + + timers8: timer@40210000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40210000 0x400>; + interrupts = , + , + , + ; + interrupt-names = "brk", "up", "trg-com", "cc"; + clocks = <&rcc CK_KER_TIM8>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 7>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@7 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <7>; + status = "disabled"; + }; + }; + usart6: serial@40220000 { compatible = "st,stm32h7-uart"; reg = <0x40220000 0x400>; @@ -654,6 +1051,99 @@ spi4: spi@40240000 { status = "disabled"; }; + timers15: timer@40250000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40250000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM15>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 13>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@14 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <14>; + status = "disabled"; + }; + }; + + timers16: timer@40260000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40260000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM16>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 14>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@15 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <15>; + status = "disabled"; + }; + }; + + timers17: timer@40270000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40270000 0x400>; + interrupts = ; + interrupt-names = "global"; + clocks = <&rcc CK_KER_TIM17>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 15>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@16 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <16>; + status = "disabled"; + }; + }; + spi5: spi@40280000 { #address-cells = <1>; #size-cells = <0>; @@ -783,6 +1273,40 @@ uart9: serial@402c0000 { status = "disabled"; }; + timers20: timer@40320000 { + compatible = "st,stm32mp25-timers"; + reg = <0x40320000 0x400>; + interrupts = , + , + , + ; + interrupt-names = "brk", "up", "trg-com", "cc"; + clocks = <&rcc CK_KER_TIM20>; + clock-names = "int"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 16>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + counter { + compatible = "st,stm32mp25-timer-counter"; + status = "disabled"; + }; + + pwm { + compatible = "st,stm32mp25-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + + timer@19 { + compatible = "st,stm32mp25-timer-trigger"; + reg = <19>; + status = "disabled"; + }; + }; + usart1: serial@40330000 { compatible = "st,stm32h7-uart"; reg = <0x40330000 0x400>; @@ -1495,7 +2019,6 @@ gpioz: gpio@46200000 { st,bank-ioport = <11>; status = "disabled"; }; - }; exti2: interrupt-controller@46230000 { diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts index 2f561ad40665..836b1958ce65 100644 --- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts +++ b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts @@ -293,6 +293,64 @@ &spi8 { status = "disabled"; }; +&timers3 { + status = "disabled"; + counter { + status = "okay"; + }; + pwm { + pinctrl-0 = <&pwm3_pins_a>; + pinctrl-1 = <&pwm3_sleep_pins_a>; + pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@2 { + status = "okay"; + }; +}; + +&timers8 { + status = "disabled"; + counter { + status = "okay"; + }; + pwm { + pinctrl-0 = <&pwm8_pins_a>; + pinctrl-1 = <&pwm8_sleep_pins_a>; + pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@7 { + status = "okay"; + }; +}; + +&timers10 { + status = "disabled"; + counter { + pinctrl-0 = <&tim10_counter_pins_a>; + pinctrl-1 = <&tim10_counter_sleep_pins_a>; + pinctrl-names = "default", "sleep"; + status = "okay"; + }; +}; + +&timers12 { + status = "disabled"; + counter { + status = "okay"; + }; + pwm { + pinctrl-0 = <&pwm12_pins_a>; + pinctrl-1 = <&pwm12_sleep_pins_a>; + pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@11 { + status = "okay"; + }; +}; + &usart2 { pinctrl-names = "default", "idle", "sleep"; pinctrl-0 = <&usart2_pins_a>; diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile index c6171de9fe88..aad9177930e6 100644 --- a/arch/arm64/boot/dts/ti/Makefile +++ b/arch/arm64/boot/dts/ti/Makefile @@ -33,6 +33,9 @@ dtb-$(CONFIG_ARCH_K3) += k3-am62-pocketbeagle2.dtb dtb-$(CONFIG_ARCH_K3) += k3-am62a7-sk.dtb dtb-$(CONFIG_ARCH_K3) += k3-am62a7-phyboard-lyra-rdk.dtb +# Boards with AM62Dx SoC +dtb-$(CONFIG_ARCH_K3) += k3-am62d2-evm.dtb + # Boards with AM62Px SoC dtb-$(CONFIG_ARCH_K3) += k3-am62p5-sk.dtb dtb-$(CONFIG_ARCH_K3) += k3-am62p5-verdin-nonwifi-dahlia.dtb @@ -278,24 +281,4 @@ dtb- += k3-am625-beagleplay-csi2-ov5640.dtb \ k3-j784s4-evm-usxgmii-exp1-exp2.dtb # Enable support for device-tree overlays -DTC_FLAGS_k3-am625-beagleplay += -@ -DTC_FLAGS_k3-am625-phyboard-lyra-rdk += -@ -DTC_FLAGS_k3-am62a7-phyboard-lyra-rdk += -@ -DTC_FLAGS_k3-am625-sk += -@ -DTC_FLAGS_k3-am62-lp-sk += -@ -DTC_FLAGS_k3-am62a7-sk += -@ -DTC_FLAGS_k3-am62p5-sk += -@ -DTC_FLAGS_k3-am642-evm += -@ -DTC_FLAGS_k3-am642-phyboard-electra-rdk += -@ -DTC_FLAGS_k3-am642-tqma64xxl-mbax4xxl += -@ -DTC_FLAGS_k3-am6548-iot2050-advanced-m2 += -@ -DTC_FLAGS_k3-am68-sk-base-board += -@ -DTC_FLAGS_k3-am69-sk += -@ -DTC_FLAGS_k3-j7200-common-proc-board += -@ -DTC_FLAGS_k3-j721e-common-proc-board += -@ -DTC_FLAGS_k3-j721e-evm-pcie0-ep += -@ -DTC_FLAGS_k3-j721e-sk += -@ -DTC_FLAGS_k3-j721s2-common-proc-board += -@ -DTC_FLAGS_k3-j722s-evm += -@ -DTC_FLAGS_k3-j784s4-evm += -@ -DTC_FLAGS_k3-j742s2-evm += -@ +DTC_FLAGS := -@ 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 aafdb90c0eb7..4609f366006e 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts @@ -74,6 +74,22 @@ vddshv_sdio: regulator-4 { }; &main_pmx0 { + main_mmc0_pins_default: main-mmc0-default-pins { + bootph-all; + pinctrl-single,pins = < + 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 */ + >; + }; + vddshv_sdio_pins_default: vddshv-sdio-default-pins { pinctrl-single,pins = < AM62X_IOPAD(0x07c, PIN_OUTPUT, 7) /* (M19) GPMC0_CLK.GPIO0_31 */ @@ -144,6 +160,14 @@ exp2: gpio@23 { }; }; +&sdhci0 { + bootph-all; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&main_mmc0_pins_default>; + status = "okay"; +}; + &sdhci1 { vmmc-supply = <&vdd_mmc1>; vqmmc-supply = <&vddshv_sdio>; diff --git a/arch/arm64/boot/dts/ti/k3-am62-main.dtsi b/arch/arm64/boot/dts/ti/k3-am62-main.dtsi index 9e0b6eee9ac7..120ba8f9dd0e 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62-main.dtsi @@ -553,7 +553,6 @@ sdhci0: mmc@fa10000 { clocks = <&k3_clks 57 5>, <&k3_clks 57 6>; clock-names = "clk_ahb", "clk_xin"; bus-width = <8>; - mmc-ddr-1_8v; mmc-hs200-1_8v; ti,clkbuf-sel = <0x7>; ti,otap-del-sel-legacy = <0x0>; diff --git a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi index 1ea8f64b1b3b..bc2289d74774 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi @@ -507,16 +507,16 @@ AM62X_IOPAD(0x01ec, PIN_INPUT_PULLUP, 0) /* (A17) I2C1_SDA */ /* SODIMM 12 */ /* Verdin I2C_2_DSI */ pinctrl_i2c2: main-i2c2-default-pins { pinctrl-single,pins = < - AM62X_IOPAD(0x00b0, PIN_INPUT, 1) /* (K22) GPMC0_CSn2.I2C2_SCL */ /* SODIMM 55 */ - AM62X_IOPAD(0x00b4, PIN_INPUT, 1) /* (K24) GPMC0_CSn3.I2C2_SDA */ /* SODIMM 53 */ + AM62X_IOPAD(0x00b0, PIN_INPUT_PULLUP, 1) /* (K22) GPMC0_CSn2.I2C2_SCL */ /* SODIMM 55 */ + AM62X_IOPAD(0x00b4, PIN_INPUT_PULLUP, 1) /* (K24) GPMC0_CSn3.I2C2_SDA */ /* SODIMM 53 */ >; }; /* Verdin I2C_4_CSI */ pinctrl_i2c3: main-i2c3-default-pins { pinctrl-single,pins = < - AM62X_IOPAD(0x01d0, PIN_INPUT, 2) /* (A15) UART0_CTSn.I2C3_SCL */ /* SODIMM 95 */ - AM62X_IOPAD(0x01d4, PIN_INPUT, 2) /* (B15) UART0_RTSn.I2C3_SDA */ /* SODIMM 93 */ + AM62X_IOPAD(0x01d0, PIN_INPUT_PULLUP, 2) /* (A15) UART0_CTSn.I2C3_SCL */ /* SODIMM 95 */ + AM62X_IOPAD(0x01d4, PIN_INPUT_PULLUP, 2) /* (B15) UART0_RTSn.I2C3_SDA */ /* SODIMM 93 */ >; }; @@ -786,8 +786,8 @@ AM62X_MCU_IOPAD(0x0010, PIN_INPUT, 7) /* (C9) MCU_SPI0_D1.MCU_GPIO0_4 */ /* SODI /* Verdin I2C_3_HDMI */ pinctrl_mcu_i2c0: mcu-i2c0-default-pins { pinctrl-single,pins = < - AM62X_MCU_IOPAD(0x0044, PIN_INPUT, 0) /* (A8) MCU_I2C0_SCL */ /* SODIMM 59 */ - AM62X_MCU_IOPAD(0x0048, PIN_INPUT, 0) /* (D10) MCU_I2C0_SDA */ /* SODIMM 57 */ + AM62X_MCU_IOPAD(0x0044, PIN_INPUT_PULLUP, 0) /* (A8) MCU_I2C0_SCL */ /* SODIMM 59 */ + AM62X_MCU_IOPAD(0x0048, PIN_INPUT_PULLUP, 0) /* (D10) MCU_I2C0_SDA */ /* SODIMM 57 */ >; }; diff --git a/arch/arm64/boot/dts/ti/k3-am625-sk.dts b/arch/arm64/boot/dts/ti/k3-am625-sk.dts index 2fbfa3719345..d240165bda9c 100644 --- a/arch/arm64/boot/dts/ti/k3-am625-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am625-sk.dts @@ -106,6 +106,22 @@ vcc_1v8: regulator-5 { }; &main_pmx0 { + main_mmc0_pins_default: main-mmc0-default-pins { + bootph-all; + 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_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 */ + >; + }; + main_rgmii2_pins_default: main-rgmii2-default-pins { bootph-all; pinctrl-single,pins = < @@ -195,6 +211,14 @@ exp1: gpio@22 { }; }; +&sdhci0 { + bootph-all; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&main_mmc0_pins_default>; + status = "okay"; +}; + &sdhci1 { vmmc-supply = <&vdd_mmc1>; vqmmc-supply = <&vdd_sd_dv>; diff --git a/arch/arm64/boot/dts/ti/k3-am62a-main.dtsi b/arch/arm64/boot/dts/ti/k3-am62a-main.dtsi index 63e097ddf988..44e7e459f176 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62a-main.dtsi @@ -51,6 +51,7 @@ phy_gmii_sel: phy@4044 { compatible = "ti,am654-phy-gmii-sel"; reg = <0x4044 0x8>; #phy-cells = <1>; + bootph-all; }; epwm_tbclk: clock-controller@4130 { @@ -96,6 +97,7 @@ secure_proxy_main: mailbox@4d000000 { #mbox-cells = <1>; interrupt-names = "rx_012"; interrupts = ; + bootph-all; }; inta_main_dmss: interrupt-controller@48000000 { @@ -131,6 +133,7 @@ main_bcdma: dma-controller@485c0100 { ti,sci-rm-range-bchan = <0x20>; /* BLOCK_COPY_CHAN */ ti,sci-rm-range-rchan = <0x21>; /* SPLIT_TR_RX_CHAN */ ti,sci-rm-range-tchan = <0x22>; /* SPLIT_TR_TX_CHAN */ + bootph-all; }; main_pktdma: dma-controller@485c0000 { @@ -147,6 +150,8 @@ main_pktdma: dma-controller@485c0000 { "ring", "tchan", "rchan", "rflow"; msi-parent = <&inta_main_dmss>; #dma-cells = <2>; + bootph-all; + ti,sci = <&dmsc>; ti,sci-dev-id = <30>; ti,sci-rm-range-tchan = <0x23>, /* UNMAPPED_TX_CHAN */ @@ -220,16 +225,19 @@ dmsc: system-controller@44043000 { k3_pds: power-controller { compatible = "ti,sci-pm-domain"; #power-domain-cells = <2>; + bootph-all; }; k3_clks: clock-controller { compatible = "ti,k2g-sci-clk"; #clock-cells = <2>; + bootph-all; }; k3_reset: reset-controller { compatible = "ti,sci-reset"; #reset-cells = <2>; + bootph-all; }; }; @@ -254,6 +262,7 @@ secure_proxy_sa3: mailbox@43600000 { * firmware on non-MPU processors */ status = "disabled"; + bootph-all; }; main_pmx0: pinctrl@f4000 { @@ -282,6 +291,7 @@ main_timer0: timer@2400000 { assigned-clock-parents = <&k3_clks 36 3>; power-domains = <&k3_pds 36 TI_SCI_PD_EXCLUSIVE>; ti,timer-pwm; + bootph-all; }; main_timer1: timer@2410000 { @@ -651,6 +661,7 @@ usb0: usb@31000000 { interrupt-names = "host", "peripheral"; maximum-speed = "high-speed"; dr_mode = "otg"; + bootph-all; snps,usb2-gadget-lpm-disable; snps,usb2-lpm-disable; }; @@ -745,6 +756,7 @@ cpsw_port1: port@1 { phys = <&phy_gmii_sel 1>; mac-address = [00 00 00 00 00 00]; ti,syscon-efuse = <&cpsw_mac_syscon 0x0>; + bootph-all; }; cpsw_port2: port@2 { @@ -764,6 +776,7 @@ cpsw3g_mdio: mdio@f00 { clocks = <&k3_clks 13 0>; clock-names = "fck"; bus_freq = <1000000>; + bootph-all; }; cpts@3d000 { diff --git a/arch/arm64/boot/dts/ti/k3-am62a-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-am62a-wakeup.dtsi index 259ae6ebbfb5..9ef1c829a9df 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a-wakeup.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62a-wakeup.dtsi @@ -17,6 +17,7 @@ wkup_conf: bus@43000000 { chipid: chipid@14 { compatible = "ti,am654-chipid"; reg = <0x14 0x4>; + bootph-all; }; opp_efuse_table: syscon@18 { @@ -67,6 +68,7 @@ wkup_uart0: serial@0 { reg = <0 0x100>; interrupts = ; status = "disabled"; + bootph-pre-ram; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts index b27759026014..bceead5e288e 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts @@ -36,6 +36,7 @@ memory@80000000 { /* 4G RAM */ reg = <0x00000000 0x80000000 0x00000000 0x80000000>, <0x00000008 0x80000000 0x00000000 0x80000000>; + bootph-all; }; reserved-memory { @@ -151,6 +152,7 @@ vdd_mmc1: regulator-3 { regulator-boot-on; enable-active-high; gpio = <&exp1 3 GPIO_ACTIVE_HIGH>; + bootph-all; }; vcc_3v3_sys: regulator-4 { @@ -297,12 +299,13 @@ main_uart0_pins_default: main-uart0-default-pins { AM62AX_IOPAD(0x1c8, PIN_INPUT, 0) /* (E14) UART0_RXD */ AM62AX_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (D15) UART0_TXD */ >; + bootph-all; }; main_uart1_pins_default: main-uart1-default-pins { pinctrl-single,pins = < - AM62AX_IOPAD(0x01e8, PIN_INPUT, 1) /* (C17) I2C1_SCL.UART1_RXD */ - AM62AX_IOPAD(0x01ec, PIN_OUTPUT, 1) /* (E17) I2C1_SDA.UART1_TXD */ + AM62AX_IOPAD(0x01ac, PIN_INPUT, 2) /* (B21) MCASP0_AFSR.UART1_RXD */ + AM62AX_IOPAD(0x01b0, PIN_OUTPUT, 2) /* (A21) MCASP0_ACLKR.UART1_TXD */ AM62AX_IOPAD(0x0194, PIN_INPUT, 2) /* (C19) MCASP0_AXR3.UART1_CTSn */ AM62AX_IOPAD(0x0198, PIN_OUTPUT, 2) /* (B19) MCASP0_AXR2.UART1_RTSn */ >; @@ -320,6 +323,7 @@ main_i2c1_pins_default: main-i2c1-default-pins { AM62AX_IOPAD(0x1e8, PIN_INPUT_PULLUP, 0) /* (B17) I2C1_SCL */ AM62AX_IOPAD(0x1ec, PIN_INPUT_PULLUP, 0) /* (A17) I2C1_SDA */ >; + bootph-all; }; main_i2c2_pins_default: main-i2c2-default-pins { @@ -356,6 +360,7 @@ AM62AX_IOPAD(0x228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */ AM62AX_IOPAD(0x224, PIN_INPUT, 0) /* (D22) MMC1_DAT3 */ AM62AX_IOPAD(0x240, PIN_INPUT, 0) /* (D17) MMC1_SDCD */ >; + bootph-all; }; usr_led_pins_default: usr-led-default-pins { @@ -375,6 +380,7 @@ main_mdio1_pins_default: main-mdio1-default-pins { AM62AX_IOPAD(0x160, PIN_OUTPUT, 0) /* (V12) MDIO0_MDC */ AM62AX_IOPAD(0x15c, PIN_INPUT, 0) /* (V13) MDIO0_MDIO */ >; + bootph-all; }; main_rgmii1_pins_default: main-rgmii1-default-pins { @@ -392,6 +398,7 @@ AM62AX_IOPAD(0x140, PIN_INPUT, 0) /* (AA17) RGMII1_TD3 */ AM62AX_IOPAD(0x130, PIN_INPUT, 0) /* (AB17) RGMII1_TXC */ AM62AX_IOPAD(0x12c, PIN_INPUT, 0) /* (W16) RGMII1_TX_CTL */ >; + bootph-all; }; main_mcasp1_pins_default: main-mcasp1-default-pins { @@ -572,6 +579,7 @@ exp1: gpio@22 { #interrupt-cells = <2>; pinctrl-names = "default"; pinctrl-0 = <&main_gpio1_ioexp_intr_pins_default>; + bootph-all; gpio-line-names = "GPIO_CPSW2_RST", "GPIO_CPSW1_RST", "BT_EN_SOC", "MMC1_SD_EN", @@ -675,10 +683,12 @@ &sdhci1 { pinctrl-names = "default"; pinctrl-0 = <&main_mmc1_pins_default>; disable-wp; + bootph-all; }; &main_gpio0 { status = "okay"; + bootph-all; }; &main_gpio1 { @@ -693,6 +703,7 @@ &main_uart0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&main_uart0_pins_default>; + bootph-all; }; /* Main UART1 is used for TIFS firmware logs */ @@ -739,10 +750,15 @@ &cpsw3g { pinctrl-0 = <&main_rgmii1_pins_default>; }; +&phy_gmii_sel { + bootph-all; +}; + &cpsw_port1 { status = "okay"; phy-mode = "rgmii-rxid"; phy-handle = <&cpsw3g_phy0>; + bootph-all; }; &cpsw_port2 { @@ -759,6 +775,7 @@ cpsw3g_phy0: ethernet-phy@0 { ti,rx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; + bootph-all; }; }; @@ -876,3 +893,45 @@ &c7x_0 { &main_rti4 { status = "reserved"; }; + +&fss { + status = "okay"; +}; + +&ospi0 { + pinctrl-names = "default"; + pinctrl-0 = <&ospi0_pins_default>; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <25000000>; + cdns,tshsl-ns = <60>; + cdns,tsd2d-ns = <60>; + cdns,tchsh-ns = <60>; + cdns,tslch-ns = <60>; + cdns,read-delay = <2>; + bootph-all; + }; +}; + +&main_pmx0 { + ospi0_pins_default: ospi0-default-pins { + bootph-all; + pinctrl-single,pins = < + AM62AX_IOPAD(0x000, PIN_OUTPUT, 0) /* (H24) OSPI0_CLK */ + AM62AX_IOPAD(0x02c, PIN_OUTPUT, 0) /* (F23) OSPI0_CSn0 */ + AM62AX_IOPAD(0x00c, PIN_INPUT, 0) /* (E25) OSPI0_D0 */ + AM62AX_IOPAD(0x010, PIN_INPUT, 0) /* (G24) OSPI0_D1 */ + AM62AX_IOPAD(0x014, PIN_INPUT, 0) /* (F25) OSPI0_D2 */ + AM62AX_IOPAD(0x018, PIN_INPUT, 0) /* (F24) OSPI0_D3 */ + AM62AX_IOPAD(0x01c, PIN_INPUT, 0) /* (J23) OSPI0_D4 */ + AM62AX_IOPAD(0x020, PIN_INPUT, 0) /* (J25) OSPI0_D5 */ + AM62AX_IOPAD(0x024, PIN_INPUT, 0) /* (H25) OSPI0_D6 */ + AM62AX_IOPAD(0x028, PIN_INPUT, 0) /* (J22) OSPI0_D7 */ + AM62AX_IOPAD(0x008, PIN_INPUT, 0) /* (J24) OSPI0_DQS */ + >; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts b/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts new file mode 100644 index 000000000000..daea18b0bc61 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * AM62D2 EVM: https://www.ti.com/lit/zip/sprcal5 + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include +#include +#include +#include "k3-am62d2.dtsi" + +/ { + compatible = "ti,am62d2-evm", "ti,am62d2"; + model = "Texas Instruments AM62D2 EVM"; + + aliases { + serial0 = &wkup_uart0; + serial1 = &mcu_uart0; + serial2 = &main_uart0; + mmc0 = &sdhci0; + mmc1 = &sdhci1; + rtc0 = &wkup_rtc0; + ethernet0 = &cpsw_port1; + ethernet1 = &cpsw_port2; + }; + + chosen { + stdout-path = &main_uart0; + }; + + memory@80000000 { + device_type = "memory"; + /* 4G RAM */ + reg = <0x00000000 0x80000000 0x00000000 0x80000000>, + <0x00000008 0x80000000 0x00000000 0x80000000>; + bootph-all; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* global cma region */ + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x00 0x2000000>; + alloc-ranges = <0x00 0xc0000000 0x00 0x2000000>; + linux,cma-default; + }; + + secure_tfa_ddr: tfa@80000000 { + reg = <0x00 0x80000000 0x00 0x80000>; + no-map; + }; + + c7x_0_dma_memory_region: c7x-dma-memory@99800000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x99800000 0x00 0x100000>; + no-map; + }; + + c7x_0_memory_region: c7x-memory@99900000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x99900000 0x00 0xf00000>; + no-map; + }; + + mcu_r5fss0_core0_dma_memory_region: r5f-dma-memory@9b800000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9b800000 0x00 0x100000>; + no-map; + }; + + mcu_r5fss0_core0_memory_region: r5f-dma-memory@9b900000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9b900000 0x00 0xf00000>; + no-map; + }; + + wkup_r5fss0_core0_dma_memory_region: r5f-dma-memory@9c800000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9c800000 0x00 0x100000>; + no-map; + }; + + wkup_r5fss0_core0_memory_region: r5f-dma-memory@9c900000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9c900000 0x00 0xf00000>; + no-map; + bootph-pre-ram; + }; + + secure_ddr: optee@9e800000 { + reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */ + no-map; + }; + + rtos_ipc_memory_region: ipc-memories@a0000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa0000000 0x00 0x01000000>; + no-map; + }; + }; + + opp-table { + /* Requires VDD_CORE at 0v85 */ + opp-1400000000 { + opp-hz = /bits/ 64 <1400000000>; + opp-supported-hw = <0x01 0x0004>; + clock-latency-ns = <6000000>; + }; + }; + + vout_pd: regulator-0 { + /* TPS65988 PD CONTROLLER OUTPUT */ + compatible = "regulator-fixed"; + regulator-name = "vout_pd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + bootph-all; + }; + + vmain_pd: regulator-1 { + /* Output of TPS22811 */ + compatible = "regulator-fixed"; + regulator-name = "vmain_pd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vout_pd>; + regulator-always-on; + regulator-boot-on; + bootph-all; + }; + + vcc_5v0: regulator-2 { + /* Output of TPS630702RNMR */ + compatible = "regulator-fixed"; + regulator-name = "vcc_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vmain_pd>; + regulator-always-on; + regulator-boot-on; + bootph-all; + }; + + vcc_3v3_main: regulator-3 { + /* output of LM5141-Q1 */ + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3_main"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vmain_pd>; + regulator-always-on; + regulator-boot-on; + bootph-all; + }; + + vdd_mmc1: regulator-4 { + /* TPS22918DBVR */ + compatible = "regulator-fixed"; + regulator-name = "vdd_mmc1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + gpio = <&exp1 3 GPIO_ACTIVE_HIGH>; + bootph-all; + }; + + vcc_3v3_sys: regulator-5 { + /* output of TPS222965DSGT */ + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_3v3_main>; + regulator-always-on; + regulator-boot-on; + bootph-all; + }; + + vddshv_sdio: regulator-6 { + compatible = "regulator-gpio"; + regulator-name = "vddshv_sdio"; + pinctrl-names = "default"; + pinctrl-0 = <&vddshv_sdio_pins_default>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + gpios = <&main_gpio1 31 GPIO_ACTIVE_HIGH>; + states = <1800000 0x0>, + <3300000 0x1>; + bootph-all; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&usr_led_pins_default>; + + led-0 { + label = "am62d-evm:green:heartbeat"; + gpios = <&main_gpio1 49 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + function = LED_FUNCTION_HEARTBEAT; + default-state = "off"; + }; + }; +}; + +&mcu_pmx0 { + status = "okay"; + + pmic_irq_pins_default: pmic-irq-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x000, PIN_INPUT, 7) /* (E11) MCU_GPIO0_0 */ + >; + }; + + wkup_uart0_pins_default: wkup-uart0-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x0024, PIN_INPUT, 0) /* (C9) WKUP_UART0_RXD */ + AM62DX_MCU_IOPAD(0x0028, PIN_OUTPUT, 0) /* (E9) WKUP_UART0_TXD */ + AM62DX_MCU_IOPAD(0x002c, PIN_INPUT, 0) /* (C10) WKUP_UART0_CTSn */ + AM62DX_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (C8) WKUP_UART0_RTSn */ + >; + bootph-all; + }; +}; + +/* WKUP UART0 is used for DM firmware logs */ +&wkup_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&wkup_uart0_pins_default>; + bootph-all; + status = "reserved"; +}; + +&main_pmx0 { + main_uart0_pins_default: main-uart0-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x01c8, PIN_INPUT, 0) /* (E14) UART0_RXD */ + AM62DX_IOPAD(0x01cc, PIN_OUTPUT, 0) /* (D15) UART0_TXD */ + >; + bootph-all; + }; + + main_i2c0_pins_default: main-i2c0-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x01e0, PIN_INPUT_PULLUP, 0) /* (D17) I2C0_SCL */ + AM62DX_IOPAD(0x01e4, PIN_INPUT_PULLUP, 0) /* (E16) I2C0_SDA */ + >; + bootph-all; + }; + + main_i2c1_pins_default: main-i2c1-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x01e8, PIN_INPUT_PULLUP, 0) /* (C17) I2C1_SCL */ + AM62DX_IOPAD(0x01ec, PIN_INPUT_PULLUP, 0) /* (E17) I2C1_SDA */ + >; + bootph-all; + }; + + main_i2c2_pins_default: main-i2c2-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x00b0, PIN_INPUT_PULLUP, 1) /* (M22) GPMC0_CSn2.I2C2_SCL */ + AM62DX_IOPAD(0x00b4, PIN_INPUT_PULLUP, 1) /* (M20) GPMC0_CSn3.I2C2_SDA */ + >; + }; + + main_mmc0_pins_default: main-mmc0-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x0220, PIN_INPUT_PULLUP, 0) /* (Y6) MMC0_CMD */ + AM62DX_IOPAD(0x0218, PIN_OUTPUT, 0) /* (AB7) MMC0_CLK */ + AM62DX_IOPAD(0x0214, PIN_INPUT_PULLUP, 0) /* (AA6) MMC0_DAT0 */ + AM62DX_IOPAD(0x0210, PIN_INPUT_PULLUP, 0) /* (AB6) MMC0_DAT1 */ + AM62DX_IOPAD(0x020c, PIN_INPUT_PULLUP, 0) /* (Y7) MMC0_DAT2 */ + AM62DX_IOPAD(0x0208, PIN_INPUT_PULLUP, 0) /* (AA7) MMC0_DAT3 */ + AM62DX_IOPAD(0x0204, PIN_INPUT_PULLUP, 0) /* (Y8) MMC0_DAT4 */ + AM62DX_IOPAD(0x0200, PIN_INPUT_PULLUP, 0) /* (W7) MMC0_DAT5 */ + AM62DX_IOPAD(0x01fc, PIN_INPUT_PULLUP, 0) /* (W9) MMC0_DAT6 */ + AM62DX_IOPAD(0x01f8, PIN_INPUT_PULLUP, 0) /* (AB8) MMC0_DAT7 */ + >; + bootph-all; + }; + + main_mmc1_pins_default: main-mmc1-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x023c, PIN_INPUT, 0) /* (C21) MMC1_CMD */ + AM62DX_IOPAD(0x0234, PIN_OUTPUT, 0) /* (E22) MMC1_CLK */ + AM62DX_IOPAD(0x0230, PIN_INPUT, 0) /* (B22) MMC1_DAT0 */ + AM62DX_IOPAD(0x022c, PIN_INPUT, 0) /* (D21) MMC1_DAT1 */ + AM62DX_IOPAD(0x0228, PIN_INPUT, 0) /* (C22) MMC1_DAT2 */ + AM62DX_IOPAD(0x0224, PIN_INPUT, 0) /* (D22) MMC1_DAT3 */ + AM62DX_IOPAD(0x0240, PIN_INPUT, 0) /* (E18) MMC1_SDCD */ + >; + bootph-all; + }; + + main_mdio0_pins_default: main-mdio0-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x160, PIN_OUTPUT, 0) /* (V12) MDIO0_MDC */ + AM62DX_IOPAD(0x15c, PIN_INPUT, 0) /* (V13) MDIO0_MDIO */ + >; + bootph-all; + }; + + main_rgmii1_pins_default: main-rgmii1-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x14c, PIN_INPUT, 0) /* (AB16) RGMII1_RD0 */ + AM62DX_IOPAD(0x150, PIN_INPUT, 0) /* (V15) RGMII1_RD1 */ + AM62DX_IOPAD(0x154, PIN_INPUT, 0) /* (W15) RGMII1_RD2 */ + AM62DX_IOPAD(0x158, PIN_INPUT, 0) /* (V14) RGMII1_RD3 */ + AM62DX_IOPAD(0x148, PIN_INPUT, 0) /* (AA16) RGMII1_RXC */ + AM62DX_IOPAD(0x144, PIN_INPUT, 0) /* (AA15) RGMII1_RX_CTL */ + AM62DX_IOPAD(0x134, PIN_INPUT, 0) /* (Y17) RGMII1_TD0 */ + AM62DX_IOPAD(0x138, PIN_INPUT, 0) /* (V16) RGMII1_TD1 */ + AM62DX_IOPAD(0x13c, PIN_INPUT, 0) /* (Y16) RGMII1_TD2 */ + AM62DX_IOPAD(0x140, PIN_INPUT, 0) /* (AA17) RGMII1_TD3 */ + AM62DX_IOPAD(0x0130, PIN_OUTPUT, 0) /* (AB17) RGMII1_TXC */ + AM62DX_IOPAD(0x012c, PIN_OUTPUT, 0) /* (W16) RGMII1_TX_CTL */ + >; + bootph-all; + }; + + main_rgmii2_pins_default: main-rgmii2-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x0184, PIN_INPUT, 0) /* (AA21) RGMII2_RD0 */ + AM62DX_IOPAD(0x0188, PIN_INPUT, 0) /* (Y20) RGMII2_RD1 */ + AM62DX_IOPAD(0x018c, PIN_INPUT, 0) /* (AB21) RGMII2_RD2 */ + AM62DX_IOPAD(0x0190, PIN_INPUT, 0) /* (AB20) RGMII2_RD3 */ + AM62DX_IOPAD(0x0180, PIN_INPUT, 0) /* (AA20) RGMII2_RXC */ + AM62DX_IOPAD(0x017c, PIN_INPUT, 0) /* (W18) RGMII2_RX_CTL */ + AM62DX_IOPAD(0x016c, PIN_INPUT, 0) /* (AA19) RGMII2_TD0 */ + AM62DX_IOPAD(0x0170, PIN_INPUT, 0) /* (Y18) RGMII2_TD1 */ + AM62DX_IOPAD(0x0174, PIN_INPUT, 0) /* (AA18) RGMII2_TD2 */ + AM62DX_IOPAD(0x0178, PIN_INPUT, 0) /* (W17) RGMII2_TD3 */ + AM62DX_IOPAD(0x0168, PIN_OUTPUT, 0) /* (AB19) RGMII2_TXC */ + AM62DX_IOPAD(0x0164, PIN_OUTPUT, 0) /* (Y19) RGMII2_TX_CTL */ + >; + bootph-all; + }; + + main_gpio1_ioexp_intr_pins_default: main-gpio1-ioexp-intr-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x01d4, PIN_INPUT, 7) /* (C15) UART0_RTSn.GPIO1_23 */ + >; + }; + + vddshv_sdio_pins_default: vddshv-sdio-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x1f4, PIN_OUTPUT, 7) /* (M19) GPMC0_CLK.GPIO1_31 */ + >; + bootph-all; + }; + + usr_led_pins_default: usr-led-default-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x0244, PIN_INPUT, 7) /* (D18) MMC1_SDWP.GPIO1_49 */ + >; + }; +}; + +&mcu_gpio0 { + status = "okay"; +}; + +&main_i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c0_pins_default>; + clock-frequency = <400000>; + bootph-all; + status = "okay"; + + typec_pd0: usb-power-controller@3f { + compatible = "ti,tps6598x"; + reg = <0x3f>; + + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + self-powered; + data-role = "dual"; + power-role = "sink"; + port { + usb_con_hs: endpoint { + remote-endpoint = <&usb0_hs_ep>; + }; + }; + }; + }; + + exp1: gpio@22 { + compatible = "ti,tca6424"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&main_gpio1>; + interrupts = <23 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #interrupt-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&main_gpio1_ioexp_intr_pins_default>; + bootph-all; + + gpio-line-names = "GPIO_CPSW2_RST", "GPIO_CPSW1_RST", + "","MMC1_SD_EN", + "VPP_EN", "GPIO_DIX_RST", + "IO_EXP_OPT_EN", "DIX_INT", + "GPIO_eMMC_RSTn", "CPLD2_DONE", + "CPLD2_INTN", "CPLD1_DONE", + "CPLD1_INTN", "USB_TYPEA_OC_INDICATION", + "PCM1_INT", "PCM2_INT", + "GPIO_PCM1_RST", "TEST_GPIO2", + "GPIO_PCM2_RST", "", + "IO_MCAN0_STB", "IO_MCAN1_STB", + "PD_I2C_IRQ", "IO_EXP_TEST_LED"; + }; + + exp2: gpio@20 { + compatible = "ti,tca6416"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + + gpio-line-names = "PCM6240_BUF_IO_EN", "", + "CPLD1_JTAGENB", "CPLD1_PROGRAMN", + "CPLD2_JTAGENB", "CPLD2_PROGRAMN", + "", "", + "", "CPLD1_TCK", + "CPLD1_TMS", "CPLD1_TDI", + "CPLD1_TDO", "CPLD2_TCK", + "CPLD2_TMS", "CPLD2_TDI", + "CPLD2_TDO", "ADDR1_IO_EXP", + "SoC_I2C0_SCL", "SoC_I2C0_SDA"; + }; +}; + +&main_i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c1_pins_default>; + clock-frequency = <100000>; + status = "okay"; +}; + +&main_i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&main_i2c2_pins_default>; + clock-frequency = <400000>; + status = "okay"; +}; + +&sdhci0 { + /* eMMC */ + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&main_mmc0_pins_default>; + bootph-all; + status = "okay"; +}; + +&sdhci1 { + /* SD/MMC */ + vmmc-supply = <&vdd_mmc1>; + vqmmc-supply = <&vddshv_sdio>; + pinctrl-names = "default"; + pinctrl-0 = <&main_mmc1_pins_default>; + disable-wp; + bootph-all; + status = "okay"; +}; + +&main_gpio0 { + bootph-all; + status = "okay"; +}; + +&main_gpio1 { + bootph-all; + status = "okay"; +}; + +&main_gpio_intr { + status = "okay"; +}; + +&main_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_uart0_pins_default>; + bootph-all; + status = "okay"; +}; + +&usb0 { + usb-role-switch; + + port { + usb0_hs_ep: endpoint { + remote-endpoint = <&usb_con_hs>; + }; + }; +}; + +&cpsw3g { + pinctrl-names = "default"; + pinctrl-0 = <&main_rgmii1_pins_default>, + <&main_rgmii2_pins_default>; + status = "okay"; + + cpts@3d000 { + /* MAP HW3_TS_PUSH to GENF1 */ + ti,pps = <2 1>; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-id"; + phy-handle = <&cpsw3g_phy0>; + status = "okay"; +}; + +&cpsw_port2 { + phy-mode = "rgmii-id"; + phy-handle = <&cpsw3g_phy1>; + status = "okay"; +}; + +&cpsw3g_mdio { + pinctrl-names = "default"; + pinctrl-0 = <&main_mdio0_pins_default>; + status = "okay"; + + cpsw3g_phy0: ethernet-phy@0 { + reg = <0>; + ti,fifo-depth = ; + ti,min-output-impedance; + }; + + cpsw3g_phy1: ethernet-phy@3 { + reg = <3>; + ti,fifo-depth = ; + ti,min-output-impedance; + }; +}; + +&mailbox0_cluster0 { + status = "okay"; + + mbox_r5_0: mbox-r5-0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; +}; + +&mailbox0_cluster1 { + status = "okay"; + + mbox_c7x_0: mbox-c7x-0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; +}; + +&mailbox0_cluster2 { + status = "okay"; + + mbox_mcu_r5_0: mbox-mcu-r5-0 { + ti,mbox-rx = <0 0 0>; + ti,mbox-tx = <1 0 0>; + }; +}; + +&wkup_r5fss0 { + status = "okay"; +}; + +&wkup_r5fss0_core0 { + mboxes = <&mailbox0_cluster0 &mbox_r5_0>; + memory-region = <&wkup_r5fss0_core0_dma_memory_region>, + <&wkup_r5fss0_core0_memory_region>; + bootph-pre-ram; +}; + +&mcu_r5fss0 { + status = "okay"; +}; + +&mcu_r5fss0_core0 { + mboxes = <&mailbox0_cluster2 &mbox_mcu_r5_0>; + memory-region = <&mcu_r5fss0_core0_dma_memory_region>, + <&mcu_r5fss0_core0_memory_region>; + firmware-name = "am62d-mcu-r5f0_0-fw"; + status = "okay"; +}; + +&c7x_0 { + mboxes = <&mailbox0_cluster1 &mbox_c7x_0>; + memory-region = <&c7x_0_dma_memory_region>, + <&c7x_0_memory_region>; + firmware-name = "am62d-c71_0-fw"; + status = "okay"; +}; + +/* main_rti4 is used by C7x DSP */ +&main_rti4 { + status = "reserved"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62d2.dtsi b/arch/arm64/boot/dts/ti/k3-am62d2.dtsi new file mode 100644 index 000000000000..c7d8ab43c72f --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62d2.dtsi @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Device Tree Source for AM62D2 SoC family in Quad core configuration + * + * TRM: https://www.ti.com/lit/pdf/sprujd4 + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "k3-am62a7.dtsi" + +/ { + model = "Texas Instruments K3 AM62D SoC"; + compatible = "ti,am62d2"; +}; + +/delete-node/ &vpu; /* Video Codec is disabled in AM62D2 SoC */ +/delete-node/ &e5010; /* JPEG Encoder is disabled in AM62D2 SoC */ 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 fa55c43ca28d..2e5e25a8ca86 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 @@ -259,7 +259,7 @@ secure_proxy_sa3: mailbox@43600000 { main_pmx0: pinctrl@f4000 { compatible = "pinctrl-single"; - reg = <0x00 0xf4000 0x00 0x2ac>; + reg = <0x00 0xf4000 0x00 0x2b0>; #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0xffffffff>; diff --git a/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-thermal.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-thermal.dtsi index c7486fb2a5b4..138b9c395be4 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-thermal.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-thermal.dtsi @@ -12,12 +12,29 @@ main0_thermal: main0-thermal { thermal-sensors = <&wkup_vtm0 0>; trips { + main0_alert: main0-alert { + temperature = <115000>; + hysteresis = <2000>; + type = "passive"; + }; + main0_crit: main0-crit { temperature = <125000>; /* milliCelsius */ hysteresis = <2000>; /* milliCelsius */ type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&main0_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>; + }; + }; }; main1_thermal: main1-thermal { @@ -26,12 +43,29 @@ main1_thermal: main1-thermal { thermal-sensors = <&wkup_vtm0 1>; trips { + main1_alert: main1-alert { + temperature = <115000>; + hysteresis = <2000>; + type = "passive"; + }; + main1_crit: main1-crit { temperature = <125000>; /* milliCelsius */ hysteresis = <2000>; /* milliCelsius */ type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&main1_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>; + }; + }; }; main2_thermal: main2-thermal { @@ -40,11 +74,28 @@ main2_thermal: main2-thermal { thermal-sensors = <&wkup_vtm0 2>; trips { + main2_alert: main2-alert { + temperature = <115000>; + hysteresis = <2000>; + type = "passive"; + }; + main2_crit: main2-crit { temperature = <125000>; /* milliCelsius */ hysteresis = <2000>; /* milliCelsius */ type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&main2_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/ti/k3-am62p-verdin.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi index 226398c37fa9..a2fdc6741da2 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi @@ -426,14 +426,14 @@ AM62PX_IOPAD(0x00f4, PIN_INPUT, 7) /* (Y20) VOUT0_DATA15.GPIO0_60 */ /* WIFI_SPI /* Verdin PWM_3_DSI as GPIO */ pinctrl_pwm3_dsi_gpio: main-gpio1-16-default-pins { pinctrl-single,pins = < - AM62PX_IOPAD(0x01b8, PIN_OUTPUT, 7) /* (E20) SPI0_CS1.GPIO1_16 */ /* SODIMM 19 */ + AM62PX_IOPAD(0x01b8, PIN_INPUT, 7) /* (E20) SPI0_CS1.GPIO1_16 */ /* SODIMM 19 */ >; }; /* Verdin SD_1_CD# */ pinctrl_sd1_cd: main-gpio1-48-default-pins { pinctrl-single,pins = < - AM62PX_IOPAD(0x0240, PIN_INPUT, 7) /* (D23) MMC1_SDCD.GPIO1_48 */ /* SODIMM 84 */ + AM62PX_IOPAD(0x0240, PIN_INPUT_PULLUP, 7) /* (D23) MMC1_SDCD.GPIO1_48 */ /* SODIMM 84 */ >; }; @@ -717,8 +717,8 @@ AM62PX_MCU_IOPAD(0x0010, PIN_INPUT, 7) /* (D10) MCU_SPI0_D1.MCU_GPIO0_4 */ /* SO /* Verdin I2C_3_HDMI */ pinctrl_mcu_i2c0: mcu-i2c0-default-pins { pinctrl-single,pins = < - AM62PX_MCU_IOPAD(0x0044, PIN_INPUT, 0) /* (E11) MCU_I2C0_SCL */ /* SODIMM 59 */ - AM62PX_MCU_IOPAD(0x0048, PIN_INPUT, 0) /* (D11) MCU_I2C0_SDA */ /* SODIMM 57 */ + AM62PX_MCU_IOPAD(0x0044, PIN_INPUT_PULLUP, 0) /* (E11) MCU_I2C0_SCL */ /* SODIMM 59 */ + AM62PX_MCU_IOPAD(0x0048, PIN_INPUT_PULLUP, 0) /* (D11) MCU_I2C0_SDA */ /* SODIMM 57 */ >; }; @@ -848,6 +848,30 @@ mbox_mcu_r5_0: mbox-mcu-r5-0 { }; }; +&main0_alert { + temperature = <95000>; +}; + +&main0_crit { + temperature = <105000>; +}; + +&main1_alert { + temperature = <95000>; +}; + +&main1_crit { + temperature = <105000>; +}; + +&main2_alert { + temperature = <95000>; +}; + +&main2_crit { + temperature = <105000>; +}; + &main_gpio0 { gpio-line-names = "SODIMM_52", diff --git a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts index 83c37de7d338..899da7896563 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts @@ -214,6 +214,14 @@ sound_master: simple-audio-card,codec { }; }; +&cpsw_mac_syscon { + bootph-all; +}; + +&phy_gmii_sel { + bootph-all; +}; + &main_gpio0 { bootph-all; }; @@ -267,6 +275,7 @@ main_mdio1_pins_default: main-mdio1-default-pins { AM62PX_IOPAD(0x0160, PIN_OUTPUT, 0) /* (F17) MDIO0_MDC */ AM62PX_IOPAD(0x015c, PIN_INPUT, 0) /* (F16) MDIO0_MDIO */ >; + bootph-all; }; main_mmc1_pins_default: main-mmc1-default-pins { @@ -547,6 +556,7 @@ &cpsw_port1 { phy-mode = "rgmii-rxid"; phy-handle = <&cpsw3g_phy0>; status = "okay"; + bootph-all; }; &cpsw_port2 { @@ -562,6 +572,7 @@ &cpsw3g_mdio { cpsw3g_phy0: ethernet-phy@0 { reg = <0>; + bootph-all; ti,rx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; diff --git a/arch/arm64/boot/dts/ti/k3-am62p5.dtsi b/arch/arm64/boot/dts/ti/k3-am62p5.dtsi index 140587d02e88..202378d9d5cf 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p5.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p5.dtsi @@ -49,6 +49,7 @@ cpu0: cpu@0 { next-level-cache = <&l2_0>; operating-points-v2 = <&a53_opp_table>; clocks = <&k3_clks 135 0>; + #cooling-cells = <2>; }; cpu1: cpu@1 { @@ -65,6 +66,7 @@ cpu1: cpu@1 { next-level-cache = <&l2_0>; operating-points-v2 = <&a53_opp_table>; clocks = <&k3_clks 136 0>; + #cooling-cells = <2>; }; cpu2: cpu@2 { @@ -81,6 +83,7 @@ cpu2: cpu@2 { next-level-cache = <&l2_0>; operating-points-v2 = <&a53_opp_table>; clocks = <&k3_clks 137 0>; + #cooling-cells = <2>; }; cpu3: cpu@3 { @@ -97,6 +100,7 @@ cpu3: cpu@3 { next-level-cache = <&l2_0>; operating-points-v2 = <&a53_opp_table>; clocks = <&k3_clks 138 0>; + #cooling-cells = <2>; }; }; 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 ee8337bfbbfd..13e1d36123d5 100644 --- a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi @@ -203,22 +203,6 @@ AM62X_IOPAD(0x0b4, PIN_INPUT_PULLUP, 1) /* (K24/H19) GPMC0_CSn3.I2C2_SDA */ >; }; - main_mmc0_pins_default: main-mmc0-default-pins { - bootph-all; - pinctrl-single,pins = < - AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3/V3) MMC0_CMD */ - AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (AB1/Y1) MMC0_CLK */ - AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2/V2) MMC0_DAT0 */ - AM62X_IOPAD(0x210, PIN_INPUT, 0) /* (AA1/V1) MMC0_DAT1 */ - AM62X_IOPAD(0x20c, PIN_INPUT, 0) /* (AA3/W2) MMC0_DAT2 */ - AM62X_IOPAD(0x208, PIN_INPUT, 0) /* (Y4/W1) MMC0_DAT3 */ - AM62X_IOPAD(0x204, PIN_INPUT, 0) /* (AB2/Y2) MMC0_DAT4 */ - AM62X_IOPAD(0x200, PIN_INPUT, 0) /* (AC1/W3) MMC0_DAT5 */ - AM62X_IOPAD(0x1fc, PIN_INPUT, 0) /* (AD2/W4) MMC0_DAT6 */ - AM62X_IOPAD(0x1f8, PIN_INPUT, 0) /* (AC2/V4) MMC0_DAT7 */ - >; - }; - main_mmc1_pins_default: main-mmc1-default-pins { bootph-all; pinctrl-single,pins = < @@ -457,14 +441,6 @@ &main_i2c2 { clock-frequency = <400000>; }; -&sdhci0 { - bootph-all; - status = "okay"; - non-removable; - pinctrl-names = "default"; - pinctrl-0 = <&main_mmc0_pins_default>; -}; - &sdhci1 { /* SD/MMC */ bootph-all; diff --git a/arch/arm64/boot/dts/ti/k3-am642-evm-pcie0-ep.dtso b/arch/arm64/boot/dts/ti/k3-am642-evm-pcie0-ep.dtso index 432751774853..a7e8d4ea98ac 100644 --- a/arch/arm64/boot/dts/ti/k3-am642-evm-pcie0-ep.dtso +++ b/arch/arm64/boot/dts/ti/k3-am642-evm-pcie0-ep.dtso @@ -46,6 +46,7 @@ pcie0_ep: pcie-ep@f102000 { max-functions = /bits/ 8 <1>; phys = <&serdes0_pcie_link>; phy-names = "pcie-phy"; + bootph-all; ti,syscon-pcie-ctrl = <&pcie0_ctrl 0x0>; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am642-phyboard-electra-rdk.dts b/arch/arm64/boot/dts/ti/k3-am642-phyboard-electra-rdk.dts index f63c101b7d61..129524eb5b91 100644 --- a/arch/arm64/boot/dts/ti/k3-am642-phyboard-electra-rdk.dts +++ b/arch/arm64/boot/dts/ti/k3-am642-phyboard-electra-rdk.dts @@ -322,6 +322,8 @@ AM64X_IOPAD(0x0040, PIN_OUTPUT, 7) /* (U21) GPMC0_AD1.GPIO0_16 */ &icssg0_mdio { pinctrl-names = "default"; pinctrl-0 = <&icssg0_mdio_pins_default &clkout0_pins_default>; + assigned-clocks = <&k3_clks 157 123>; + assigned-clock-parents = <&k3_clks 157 125>; status = "okay"; icssg0_phy1: ethernet-phy@1 { diff --git a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi index b085e7361116..61c11dc92d9c 100644 --- a/arch/arm64/boot/dts/ti/k3-am65-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am65-main.dtsi @@ -655,6 +655,7 @@ secure_proxy_main: mailbox@32c00000 { <0x00 0x32800000 0x00 0x100000>; interrupt-names = "rx_011"; interrupts = ; + bootph-all; }; hwspinlock: spinlock@30e00000 { diff --git a/arch/arm64/boot/dts/ti/k3-am65-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-am65-wakeup.dtsi index eee072e44a42..d62a0be767c8 100644 --- a/arch/arm64/boot/dts/ti/k3-am65-wakeup.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am65-wakeup.dtsi @@ -21,16 +21,19 @@ dmsc: system-controller@44083000 { k3_pds: power-controller { compatible = "ti,sci-pm-domain"; #power-domain-cells = <2>; + bootph-all; }; k3_clks: clock-controller { compatible = "ti,k2g-sci-clk"; #clock-cells = <2>; + bootph-all; }; k3_reset: reset-controller { compatible = "ti,sci-reset"; #reset-cells = <2>; + bootph-all; }; }; @@ -43,6 +46,7 @@ wkup_conf: bus@43000000 { chipid: chipid@14 { compatible = "ti,am654-chipid"; reg = <0x14 0x4>; + bootph-all; }; }; @@ -107,5 +111,6 @@ wkup_vtm0: temperature-sensor@42050000 { reg = <0x42050000 0x25c>; power-domains = <&k3_pds 80 TI_SCI_PD_EXCLUSIVE>; #thermal-sensor-cells = <1>; + bootph-all; }; }; 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 c30425960398..e589690c7c82 100644 --- a/arch/arm64/boot/dts/ti/k3-am654-base-board.dts +++ b/arch/arm64/boot/dts/ti/k3-am654-base-board.dts @@ -144,6 +144,7 @@ vtt_supply: regulator-3 { regulator-boot-on; vin-supply = <&vcc3v3_io>; gpio = <&wkup_gpio0 28 GPIO_ACTIVE_HIGH>; + bootph-all; }; }; @@ -155,12 +156,14 @@ AM65X_WKUP_IOPAD(0x00a4, PIN_OUTPUT, 0) /* (AB5) WKUP_UART0_TXD */ AM65X_WKUP_IOPAD(0x00c8, PIN_INPUT, 1) /* (AC2) WKUP_GPIO0_6.WKUP_UART0_CTSn */ AM65X_WKUP_IOPAD(0x00cc, PIN_OUTPUT, 1) /* (AC1) WKUP_GPIO0_7.WKUP_UART0_RTSn */ >; + bootph-all; }; ddr_vtt_pins_default: ddr-vtt-default-pins { pinctrl-single,pins = < AM65X_WKUP_IOPAD(0x0040, PIN_OUTPUT_PULLUP, 7) /* WKUP_GPIO0_28 */ >; + bootph-all; }; wkup_i2c0_pins_default: wkup-i2c0-default-pins { @@ -168,6 +171,7 @@ wkup_i2c0_pins_default: wkup-i2c0-default-pins { AM65X_WKUP_IOPAD(0x00e0, PIN_INPUT, 0) /* (AC7) WKUP_I2C0_SCL */ AM65X_WKUP_IOPAD(0x00e4, PIN_INPUT, 0) /* (AD6) WKUP_I2C0_SDA */ >; + bootph-all; }; push_button_pins_default: push-button-default-pins { @@ -191,6 +195,7 @@ AM65X_WKUP_IOPAD(0x0024, PIN_INPUT, 0) /* (R2) MCU_OSPI0_D6 */ AM65X_WKUP_IOPAD(0x0028, PIN_INPUT, 0) /* (R3) MCU_OSPI0_D7 */ AM65X_WKUP_IOPAD(0x002c, PIN_OUTPUT, 0) /* (R4) MCU_OSPI0_CSn0 */ >; + bootph-all; }; wkup_pca554_default: wkup-pca554-default-pins { @@ -206,6 +211,7 @@ AM65X_WKUP_IOPAD(0x0048, PIN_OUTPUT, 4) /* (P5) MCU_OSPI1_D2.MCU_UART0_TXD */ AM65X_WKUP_IOPAD(0x004C, PIN_INPUT, 4) /* (P1) MCU_OSPI1_D3.MCU_UART0_CTSn */ AM65X_WKUP_IOPAD(0x0054, PIN_OUTPUT, 4) /* (N3) MCU_OSPI1_CSn1.MCU_UART0_RTSn */ >; + bootph-all; }; mcu_cpsw_pins_default: mcu-cpsw-default-pins { @@ -248,6 +254,7 @@ AM65X_IOPAD(0x01e8, PIN_OUTPUT, 0) /* (AE11) UART0_TXD */ AM65X_IOPAD(0x01ec, PIN_INPUT, 0) /* (AG11) UART0_CTSn */ AM65X_IOPAD(0x01f0, PIN_OUTPUT, 0) /* (AD11) UART0_RTSn */ >; + bootph-all; }; main_i2c2_pins_default: main-i2c2-default-pins { @@ -281,6 +288,7 @@ AM65X_IOPAD(0x0188, PIN_INPUT_PULLUP, 0) /* (D25) MMC0_DAT7 */ AM65X_IOPAD(0x01b4, PIN_INPUT_PULLUP, 0) /* (A23) MMC0_SDCD */ AM65X_IOPAD(0x01b0, PIN_INPUT, 0) /* (C25) MMC0_DS */ >; + bootph-all; }; main_mmc1_pins_default: main-mmc1-default-pins { @@ -294,6 +302,7 @@ AM65X_IOPAD(0x02c4, PIN_INPUT_PULLUP, 0) /* (D27) MMC1_DAT3 */ AM65X_IOPAD(0x02dc, PIN_INPUT_PULLUP, 0) /* (B24) MMC1_SDCD */ AM65X_IOPAD(0x02e0, PIN_INPUT, 0) /* (C24) MMC1_SDWP */ >; + bootph-all; }; usb1_pins_default: usb1-default-pins { @@ -343,6 +352,7 @@ &main_uart0 { pinctrl-names = "default"; pinctrl-0 = <&main_uart0_pins_default>; power-domains = <&k3_pds 146 TI_SCI_PD_SHARED>; + bootph-all; }; &wkup_i2c0 { @@ -368,6 +378,7 @@ vdd_mpu: regulator@60 { ti,vsel0-state-high; ti,vsel1-state-high; ti,enable-vout-discharge; + bootph-all; }; gpio@38 { @@ -456,6 +467,7 @@ &sdhci0 { bus-width = <8>; non-removable; ti,driver-strength-ohm = <50>; + bootph-all; }; /* @@ -470,6 +482,7 @@ &sdhci1 { pinctrl-0 = <&main_mmc1_pins_default>; ti,driver-strength-ohm = <50>; disable-wp; + bootph-all; }; &usb1 { @@ -630,3 +643,7 @@ &cpsw_port1 { &dss { status = "disabled"; }; + +&wkup_gpio0 { + bootph-all; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dtso b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dtso index c3cb752f8cd7..d04dd7a44008 100644 --- a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dtso +++ b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb2.dtso @@ -46,6 +46,7 @@ AM65X_IOPAD(0x02bc, PIN_OUTPUT, 0) /* (AD9) USB0_DRVVBUS */ &dwc3_0 { status = "okay"; + bootph-all; }; &usb0_phy { diff --git a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dtso b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dtso index 333e423e8bb6..04393f21d712 100644 --- a/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dtso +++ b/arch/arm64/boot/dts/ti/k3-am654-pcie-usb3.dtso @@ -45,6 +45,7 @@ &dwc3_0 { <&k3_clks 151 8>; /* set PIPE3_TXB_CLK to WIZ8B2M4VSB */ phys = <&serdes0 PHY_TYPE_USB3 0>; phy-names = "usb3-phy"; + bootph-all; }; &usb0 { diff --git a/arch/arm64/boot/dts/ti/k3-am68-sk-base-board.dts b/arch/arm64/boot/dts/ti/k3-am68-sk-base-board.dts index 5fa70a874d7b..e84c504c87d2 100644 --- a/arch/arm64/boot/dts/ti/k3-am68-sk-base-board.dts +++ b/arch/arm64/boot/dts/ti/k3-am68-sk-base-board.dts @@ -344,6 +344,7 @@ J721S2_WKUP_IOPAD(0x008, PIN_OUTPUT, 0) /* (E22) MCU_RGMII1_TD3 */ J721S2_WKUP_IOPAD(0x018, PIN_OUTPUT, 0) /* (F21) MCU_RGMII1_TXC */ J721S2_WKUP_IOPAD(0x000, PIN_OUTPUT, 0) /* (F22) MCU_RGMII1_TX_CTL */ >; + bootph-all; }; mcu_mdio_pins_default: mcu-mdio-default-pins { @@ -351,6 +352,7 @@ mcu_mdio_pins_default: mcu-mdio-default-pins { J721S2_WKUP_IOPAD(0x034, PIN_OUTPUT, 0) /* (A21) MCU_MDIO0_MDC */ J721S2_WKUP_IOPAD(0x030, PIN_INPUT, 0) /* (A22) MCU_MDIO0_MDIO */ >; + bootph-all; }; mcu_mcan0_pins_default: mcu-mcan0-default-pins { @@ -412,6 +414,14 @@ J721S2_WKUP_IOPAD(0x000, PIN_INPUT, 7) /* (K26) WKUP_GPIO0_49 */ }; }; +&cpsw_mac_syscon { + bootph-all; +}; + +&phy_gmii_sel { + bootph-all; +}; + &main_gpio0 { status = "okay"; pinctrl-names = "default"; @@ -626,6 +636,7 @@ &mcu_cpsw { &davinci_mdio { phy0: ethernet-phy@0 { reg = <0>; + bootph-all; ti,rx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; @@ -635,6 +646,7 @@ phy0: ethernet-phy@0 { &cpsw_port1 { phy-mode = "rgmii-rxid"; phy-handle = <&phy0>; + bootph-all; }; &mcu_mcan0 { diff --git a/arch/arm64/boot/dts/ti/k3-am69-sk.dts b/arch/arm64/boot/dts/ti/k3-am69-sk.dts index f28375629739..612ac27643d2 100644 --- a/arch/arm64/boot/dts/ti/k3-am69-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am69-sk.dts @@ -568,6 +568,7 @@ J784S4_WKUP_IOPAD(0x008, PIN_OUTPUT, 0) /* (E38) MCU_RGMII1_TD3 */ J784S4_WKUP_IOPAD(0x018, PIN_OUTPUT, 0) /* (E36) MCU_RGMII1_TXC */ J784S4_WKUP_IOPAD(0x000, PIN_OUTPUT, 0) /* (C38) MCU_RGMII1_TX_CTL */ >; + bootph-all; }; mcu_mdio_pins_default: mcu-mdio-default-pins { @@ -575,6 +576,7 @@ mcu_mdio_pins_default: mcu-mdio-default-pins { J784S4_WKUP_IOPAD(0x034, PIN_OUTPUT, 0) /* (A36) MCU_MDIO0_MDC */ J784S4_WKUP_IOPAD(0x030, PIN_INPUT, 0) /* (B35) MCU_MDIO0_MDIO */ >; + bootph-all; }; mcu_rpi_hdr1_gpio0_pins_default: mcu-rpi-hdr1-gpio0-default-pins { @@ -630,6 +632,14 @@ J784S4_WKUP_IOPAD(0x0, PIN_INPUT, 7) /* (M33) WKUP_GPIO0_49 */ }; }; +&cpsw_mac_syscon { + bootph-all; +}; + +&phy_gmii_sel { + bootph-all; +}; + &mailbox0_cluster0 { status = "okay"; interrupts = <436>; @@ -968,6 +978,7 @@ &mcu_cpsw { &davinci_mdio { mcu_phy0: ethernet-phy@0 { reg = <0>; + bootph-all; ti,rx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; @@ -978,6 +989,7 @@ &mcu_cpsw_port1 { status = "okay"; phy-mode = "rgmii-rxid"; phy-handle = <&mcu_phy0>; + bootph-all; }; &mcu_r5fss0_core0 { @@ -1294,8 +1306,12 @@ partition@3fc0000 { &serdes_ln_ctrl { idle-states = , , , , - , , - , ; + , , + , , + , , + , , + , , + , ; }; &serdes_wiz0 { diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi index 83cf0adb2cb7..62f45377a2c9 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi @@ -2067,4 +2067,94 @@ gpu: gpu@4e20000000 { power-domain-names = "a", "b"; dma-coherent; }; + + mcasp0: mcasp@2b00000 { + compatible = "ti,am33xx-mcasp-audio"; + reg = <0x00 0x02b00000 0x00 0x2000>, + <0x00 0x02b08000 0x00 0x1000>; + reg-names = "mpu","dat"; + interrupts = , + ; + interrupt-names = "tx", "rx"; + dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>; + dma-names = "tx", "rx"; + clocks = <&k3_clks 209 0>; + clock-names = "fck"; + assigned-clocks = <&k3_clks 209 0>; + assigned-clock-parents = <&k3_clks 209 1>; + power-domains = <&k3_pds 209 TI_SCI_PD_EXCLUSIVE>; + status = "disabled"; + }; + + mcasp1: mcasp@2b10000 { + compatible = "ti,am33xx-mcasp-audio"; + reg = <0x00 0x02b10000 0x00 0x2000>, + <0x00 0x02b18000 0x00 0x1000>; + reg-names = "mpu","dat"; + interrupts = , + ; + interrupt-names = "tx", "rx"; + dmas = <&main_udmap 0xc401>, <&main_udmap 0x4401>; + dma-names = "tx", "rx"; + clocks = <&k3_clks 210 0>; + clock-names = "fck"; + assigned-clocks = <&k3_clks 210 0>; + assigned-clock-parents = <&k3_clks 210 1>; + power-domains = <&k3_pds 210 TI_SCI_PD_EXCLUSIVE>; + status = "disabled"; + }; + + mcasp2: mcasp@2b20000 { + compatible = "ti,am33xx-mcasp-audio"; + reg = <0x00 0x02b20000 0x00 0x2000>, + <0x00 0x02b28000 0x00 0x1000>; + reg-names = "mpu","dat"; + interrupts = , + ; + interrupt-names = "tx", "rx"; + dmas = <&main_udmap 0xc402>, <&main_udmap 0x4402>; + dma-names = "tx", "rx"; + clocks = <&k3_clks 211 0>; + clock-names = "fck"; + assigned-clocks = <&k3_clks 211 0>; + assigned-clock-parents = <&k3_clks 211 1>; + power-domains = <&k3_pds 211 TI_SCI_PD_EXCLUSIVE>; + status = "disabled"; + }; + + mcasp3: mcasp@2b30000 { + compatible = "ti,am33xx-mcasp-audio"; + reg = <0x00 0x02b30000 0x00 0x2000>, + <0x00 0x02b38000 0x00 0x1000>; + reg-names = "mpu","dat"; + interrupts = , + ; + interrupt-names = "tx", "rx"; + dmas = <&main_udmap 0xc403>, <&main_udmap 0x4403>; + dma-names = "tx", "rx"; + clocks = <&k3_clks 212 0>; + clock-names = "fck"; + assigned-clocks = <&k3_clks 212 0>; + assigned-clock-parents = <&k3_clks 212 1>; + power-domains = <&k3_pds 212 TI_SCI_PD_EXCLUSIVE>; + status = "disabled"; + }; + + mcasp4: mcasp@2b40000 { + compatible = "ti,am33xx-mcasp-audio"; + reg = <0x00 0x02b40000 0x00 0x2000>, + <0x00 0x02b48000 0x00 0x1000>; + reg-names = "mpu","dat"; + interrupts = , + ; + interrupt-names = "tx", "rx"; + dmas = <&main_udmap 0xc404>, <&main_udmap 0x4404>; + dma-names = "tx", "rx"; + clocks = <&k3_clks 213 0>; + clock-names = "fck"; + assigned-clocks = <&k3_clks 213 0>; + assigned-clock-parents = <&k3_clks 213 1>; + power-domains = <&k3_pds 213 TI_SCI_PD_EXCLUSIVE>; + status = "disabled"; + }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j722s-evm.dts b/arch/arm64/boot/dts/ti/k3-j722s-evm.dts index a47852fdca70..9d8abfa9afd2 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s-evm.dts +++ b/arch/arm64/boot/dts/ti/k3-j722s-evm.dts @@ -282,6 +282,14 @@ csi23_mux: mux-controller-1 { }; }; +&cpsw_mac_syscon { + bootph-all; +}; + +&phy_gmii_sel { + bootph-all; +}; + &main_pmx0 { main_mcan0_pins_default: main-mcan0-default-pins { @@ -346,6 +354,7 @@ mdio_pins_default: mdio-default-pins { J722S_IOPAD(0x0160, PIN_OUTPUT, 0) /* (AC24) MDIO0_MDC */ J722S_IOPAD(0x015c, PIN_INPUT, 0) /* (AD25) MDIO0_MDIO */ >; + bootph-all; }; ospi0_pins_default: ospi0-default-pins { @@ -380,6 +389,7 @@ J722S_IOPAD(0x0140, PIN_OUTPUT, 0) /* (AF24) RGMII1_TD3 */ J722S_IOPAD(0x0130, PIN_OUTPUT, 0) /* (AG26) RGMII1_TXC */ J722S_IOPAD(0x012c, PIN_OUTPUT, 0) /* (AF25) RGMII1_TX_CTL */ >; + bootph-all; }; main_usb1_pins_default: main-usb1-default-pins { @@ -424,6 +434,7 @@ &cpsw3g_mdio { cpsw3g_phy0: ethernet-phy@0 { reg = <0>; + bootph-all; ti,rx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; @@ -434,6 +445,7 @@ &cpsw_port1 { phy-mode = "rgmii-rxid"; phy-handle = <&cpsw3g_phy0>; status = "okay"; + bootph-all; }; &main_gpio1 { @@ -634,7 +646,7 @@ p05-hog { /* P05 - USB2.0_MUX_SEL */ gpio-hog; gpios = <5 GPIO_ACTIVE_LOW>; - output-high; + output-low; }; p01_hog: p01-hog { diff --git a/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi b/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi index 78d7e800b311..5cfa7bf36641 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi @@ -418,6 +418,15 @@ serdes_ln_ctrl: mux-controller@4080 { <0x10 0x3>; /* SERDES1 lane0 select */ }; + audio_refclk0: clock@82e0 { + compatible = "ti,am62-audio-refclk"; + reg = <0x82e0 0x4>; + clocks = <&k3_clks 157 0>; + assigned-clocks = <&k3_clks 157 0>; + assigned-clock-parents = <&k3_clks 157 15>; + #clock-cells = <0>; + }; + audio_refclk1: clock@82e4 { compatible = "ti,am62-audio-refclk"; reg = <0x82e4 0x4>; diff --git a/arch/arm64/boot/dts/ti/k3-j722s.dtsi b/arch/arm64/boot/dts/ti/k3-j722s.dtsi index 14c6c6a332ef..cdc8570e54b2 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j722s.dtsi @@ -56,6 +56,7 @@ cpu0: cpu@0 { d-cache-sets = <128>; next-level-cache = <&l2_0>; clocks = <&k3_clks 135 0>; + #cooling-cells = <2>; }; cpu1: cpu@1 { @@ -71,6 +72,7 @@ cpu1: cpu@1 { d-cache-sets = <128>; next-level-cache = <&l2_0>; clocks = <&k3_clks 136 0>; + #cooling-cells = <2>; }; cpu2: cpu@2 { @@ -86,6 +88,7 @@ cpu2: cpu@2 { d-cache-sets = <128>; next-level-cache = <&l2_0>; clocks = <&k3_clks 137 0>; + #cooling-cells = <2>; }; cpu3: cpu@3 { @@ -101,6 +104,7 @@ cpu3: cpu@3 { d-cache-sets = <128>; next-level-cache = <&l2_0>; clocks = <&k3_clks 138 0>; + #cooling-cells = <2>; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-main-common.dtsi b/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-main-common.dtsi index 363d68fec387..7c5b0c69897d 100644 --- a/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-main-common.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-main-common.dtsi @@ -131,6 +131,11 @@ acspcie0_proxy_ctrl: clock-controller@1a090 { compatible = "ti,j784s4-acspcie-proxy-ctrl", "syscon"; reg = <0x1a090 0x4>; }; + + acspcie1_proxy_ctrl: clock-controller@1a094 { + compatible = "ti,j784s4-acspcie-proxy-ctrl", "syscon"; + reg = <0x1a094 0x4>; + }; }; main_ehrpwm0: pwm@3000000 { @@ -2675,4 +2680,15 @@ mcasp4: mcasp@2b40000 { power-domains = <&k3_pds 269 TI_SCI_PD_EXCLUSIVE>; status = "disabled"; }; + + bist_main14: bist@33c0000 { + compatible = "ti,j784s4-bist"; + reg = <0x00 0x033c0000 0x00 0x400>, + <0x00 0x0010c1a0 0x00 0x01c>; + reg-names = "cfg", "ctrl_mmr"; + clocks = <&k3_clks 237 7>; + power-domains = <&k3_pds 237 TI_SCI_PD_EXCLUSIVE>; + bootph-pre-ram; + ti,sci-dev-id = <234>; + }; }; diff --git a/arch/arm64/boot/dts/ti/k3-pinctrl.h b/arch/arm64/boot/dts/ti/k3-pinctrl.h index cac7cccc1112..c0f09be8d3f9 100644 --- a/arch/arm64/boot/dts/ti/k3-pinctrl.h +++ b/arch/arm64/boot/dts/ti/k3-pinctrl.h @@ -8,6 +8,7 @@ #ifndef DTS_ARM64_TI_K3_PINCTRL_H #define DTS_ARM64_TI_K3_PINCTRL_H +#define ST_EN_SHIFT (14) #define PULLUDEN_SHIFT (16) #define PULLTYPESEL_SHIFT (17) #define RXACTIVE_SHIFT (18) @@ -19,6 +20,10 @@ #define DS_PULLUD_EN_SHIFT (27) #define DS_PULLTYPE_SEL_SHIFT (28) +/* Schmitt trigger configuration */ +#define ST_DISABLE (0 << ST_EN_SHIFT) +#define ST_ENABLE (1 << ST_EN_SHIFT) + #define PULL_DISABLE (1 << PULLUDEN_SHIFT) #define PULL_ENABLE (0 << PULLUDEN_SHIFT) @@ -32,9 +37,13 @@ #define PIN_OUTPUT (INPUT_DISABLE | PULL_DISABLE) #define PIN_OUTPUT_PULLUP (INPUT_DISABLE | PULL_UP) #define PIN_OUTPUT_PULLDOWN (INPUT_DISABLE | PULL_DOWN) -#define PIN_INPUT (INPUT_EN | PULL_DISABLE) -#define PIN_INPUT_PULLUP (INPUT_EN | PULL_UP) -#define PIN_INPUT_PULLDOWN (INPUT_EN | PULL_DOWN) +#define PIN_INPUT (INPUT_EN | ST_ENABLE | PULL_DISABLE) +#define PIN_INPUT_PULLUP (INPUT_EN | ST_ENABLE | PULL_UP) +#define PIN_INPUT_PULLDOWN (INPUT_EN | ST_ENABLE | PULL_DOWN) +/* Input configurations with Schmitt Trigger disabled */ +#define PIN_INPUT_NOST (INPUT_EN | PULL_DISABLE) +#define PIN_INPUT_PULLUP_NOST (INPUT_EN | PULL_UP) +#define PIN_INPUT_PULLDOWN_NOST (INPUT_EN | PULL_DOWN) #define PIN_DEBOUNCE_DISABLE (0 << DEBOUNCE_SHIFT) #define PIN_DEBOUNCE_CONF1 (1 << DEBOUNCE_SHIFT) @@ -63,6 +72,9 @@ #define AM62AX_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) #define AM62AX_MCU_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) +#define AM62DX_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) +#define AM62DX_MCU_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) + #define AM62PX_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) #define AM62PX_MCU_IOPAD(pa, val, muxmode) (((pa) & 0x1fff)) ((val) | (muxmode)) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 897fc686e6a9..58f87d09366c 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -38,6 +38,7 @@ CONFIG_ARCH_AIROHA=y CONFIG_ARCH_SUNXI=y CONFIG_ARCH_ALPINE=y CONFIG_ARCH_APPLE=y +CONFIG_ARCH_AXIADO=y CONFIG_ARCH_BCM=y CONFIG_ARCH_BCM2835=y CONFIG_ARCH_BCM_IPROC=y @@ -45,6 +46,7 @@ CONFIG_ARCH_BCMBCA=y CONFIG_ARCH_BRCMSTB=y CONFIG_ARCH_BERLIN=y CONFIG_ARCH_BLAIZE=y +CONFIG_ARCH_CIX=y CONFIG_ARCH_EXYNOS=y CONFIG_ARCH_SPARX5=y CONFIG_ARCH_K3=y @@ -66,6 +68,7 @@ CONFIG_ARCH_RENESAS=y CONFIG_ARCH_ROCKCHIP=y CONFIG_ARCH_SEATTLE=y CONFIG_ARCH_INTEL_SOCFPGA=y +CONFIG_ARCH_SOPHGO=y CONFIG_ARCH_STM32=y CONFIG_ARCH_SYNQUACER=y CONFIG_ARCH_TEGRA=y @@ -266,6 +269,7 @@ CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE=y CONFIG_QCOM_QSEECOM=y CONFIG_QCOM_QSEECOM_UEFISECAPP=y CONFIG_EXYNOS_ACPM_PROTOCOL=m +CONFIG_TEGRA_BPMP=y CONFIG_GNSS=m CONFIG_GNSS_MTK_SERIAL=m CONFIG_MTD=y @@ -581,6 +585,7 @@ CONFIG_SPI_QUP=y CONFIG_SPI_QCOM_GENI=m CONFIG_SPI_S3C64XX=y CONFIG_SPI_SH_MSIOF=m +CONFIG_SPI_STM32_OSPI=m CONFIG_SPI_SUN6I=y CONFIG_SPI_TEGRA210_QUAD=m CONFIG_SPI_TEGRA114=m @@ -622,6 +627,7 @@ CONFIG_PINCTRL_QCS615=y CONFIG_PINCTRL_QCS8300=y CONFIG_PINCTRL_QDF2XXX=y CONFIG_PINCTRL_QDU1000=y +CONFIG_PINCTRL_RP1=m CONFIG_PINCTRL_SA8775P=y CONFIG_PINCTRL_SC7180=y CONFIG_PINCTRL_SC7280=y @@ -654,6 +660,7 @@ CONFIG_PINCTRL_SM8450_LPASS_LPI=m CONFIG_PINCTRL_SC8280XP_LPASS_LPI=m CONFIG_PINCTRL_SM8550_LPASS_LPI=m CONFIG_PINCTRL_SM8650_LPASS_LPI=m +CONFIG_PINCTRL_SOPHGO_SG2000=y CONFIG_GPIO_ALTERA=m CONFIG_GPIO_DAVINCI=y CONFIG_GPIO_DWAPB=y @@ -707,6 +714,7 @@ CONFIG_SENSORS_SL28CPLD=m CONFIG_SENSORS_INA2XX=m CONFIG_SENSORS_INA3221=m CONFIG_SENSORS_TMP102=m +CONFIG_MISC_RP1=m CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y CONFIG_CPU_THERMAL=y CONFIG_DEVFREQ_THERMAL=y @@ -769,11 +777,13 @@ CONFIG_MFD_EXYNOS_LPASS=m CONFIG_MFD_HI6421_PMIC=y CONFIG_MFD_HI655X_PMIC=y CONFIG_MFD_MAX77620=y +CONFIG_MFD_MAX77759=m CONFIG_MFD_MT6360=y CONFIG_MFD_MT6397=y CONFIG_MFD_SPMI_PMIC=y CONFIG_MFD_RK8XX_I2C=y CONFIG_MFD_RK8XX_SPI=y +CONFIG_MFD_SEC_ACPM=m CONFIG_MFD_SEC_I2C=y CONFIG_MFD_SL28CPLD=y CONFIG_RZ_MTU3=y @@ -783,6 +793,7 @@ CONFIG_MFD_TPS65219=y CONFIG_MFD_TPS6594_I2C=m CONFIG_MFD_ROHM_BD718XX=y CONFIG_MFD_STM32_LPTIMER=m +CONFIG_MFD_STM32_TIMERS=m CONFIG_MFD_WCD934X=m CONFIG_MFD_KHADAS_MCU=m CONFIG_REGULATOR_FIXED_VOLTAGE=y @@ -865,6 +876,7 @@ CONFIG_VIDEO_RENESAS_FCP=m CONFIG_VIDEO_RENESAS_FDP1=m CONFIG_VIDEO_RENESAS_VSP1=m CONFIG_VIDEO_RCAR_DRIF=m +CONFIG_VIDEO_ROCKCHIP_RGA=m CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m CONFIG_VIDEO_SAMSUNG_S5P_MFC=m @@ -977,12 +989,12 @@ CONFIG_BACKLIGHT_LP855X=m CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set -CONFIG_SOUND=y -CONFIG_SND=y +CONFIG_SOUND=m +CONFIG_SND=m CONFIG_SND_ALOOP=m CONFIG_SND_HDA_TEGRA=m CONFIG_SND_HDA_CODEC_HDMI=m -CONFIG_SND_SOC=y +CONFIG_SND_SOC=m CONFIG_SND_BCM2835_SOC_I2S=m CONFIG_SND_SOC_FSL_ASRC=m CONFIG_SND_SOC_FSL_MICFIL=m @@ -1019,12 +1031,12 @@ CONFIG_SND_SOC_ROCKCHIP_SAI=m CONFIG_SND_SOC_ROCKCHIP_SPDIF=m CONFIG_SND_SOC_ROCKCHIP_RT5645=m CONFIG_SND_SOC_RK3399_GRU_SOUND=m -CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SOC_RCAR=m CONFIG_SND_SOC_MSIOF=m CONFIG_SND_SOC_RZ=m +CONFIG_SND_SOC_SAMSUNG=m CONFIG_SND_SOC_SOF_TOPLEVEL=y -CONFIG_SND_SOC_SOF_OF=y +CONFIG_SND_SOC_SOF_OF=m CONFIG_SND_SOC_SOF_MTK_TOPLEVEL=y CONFIG_SND_SOC_SOF_MT8186=m CONFIG_SND_SOC_SOF_MT8195=m @@ -1055,7 +1067,6 @@ CONFIG_SND_SOC_DA7213=m CONFIG_SND_SOC_ES7134=m CONFIG_SND_SOC_ES7241=m CONFIG_SND_SOC_ES8316=m -CONFIG_SND_SOC_ES8328=m CONFIG_SND_SOC_ES8328_I2C=m CONFIG_SND_SOC_GTM601=m CONFIG_SND_SOC_MSM8916_WCD_ANALOG=m @@ -1073,7 +1084,6 @@ 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_WCD939X=m CONFIG_SND_SOC_WCD939X_SDW=m CONFIG_SND_SOC_WM8524=m CONFIG_SND_SOC_WM8904=m @@ -1245,6 +1255,7 @@ CONFIG_RTC_DRV_BQ32K=m CONFIG_RTC_DRV_RX8581=m CONFIG_RTC_DRV_RV3028=m CONFIG_RTC_DRV_RV8803=m +CONFIG_RTC_DRV_S32G=m CONFIG_RTC_DRV_S5M=y CONFIG_RTC_DRV_DS3232=y CONFIG_RTC_DRV_PCF2127=m @@ -1317,6 +1328,7 @@ CONFIG_COMMON_CLK_CS2000_CP=y CONFIG_COMMON_CLK_FSL_SAI=y CONFIG_COMMON_CLK_S2MPS11=y CONFIG_COMMON_CLK_PWM=y +CONFIG_COMMON_CLK_RP1=m CONFIG_COMMON_CLK_RS9_PCIE=y CONFIG_COMMON_CLK_VC3=y CONFIG_COMMON_CLK_VC5=y @@ -1401,6 +1413,8 @@ CONFIG_SDM_DISPCC_845=y CONFIG_SDM_LPASSCC_845=m CONFIG_SDX_GCC_75=y CONFIG_SM_CAMCC_8250=m +CONFIG_SM_CAMCC_8550=m +CONFIG_SM_CAMCC_8650=m CONFIG_SM_DISPCC_6115=m CONFIG_SM_DISPCC_8250=y CONFIG_SM_DISPCC_8450=m @@ -1428,8 +1442,10 @@ CONFIG_SM_VIDEOCC_8250=y CONFIG_SM_VIDEOCC_8550=m CONFIG_QCOM_HFPLL=y CONFIG_CLK_GFM_LPASS_SM8250=m +CONFIG_SM_VIDEOCC_8450=m CONFIG_CLK_RCAR_USB2_CLOCK_SEL=y CONFIG_CLK_RENESAS_VBATTB=m +CONFIG_CLK_SOPHGO_CV1800=y CONFIG_HWSPINLOCK=y CONFIG_HWSPINLOCK_OMAP=m CONFIG_HWSPINLOCK_QCOM=y @@ -1444,7 +1460,10 @@ CONFIG_PLATFORM_MHU=y CONFIG_BCM2835_MBOX=y CONFIG_QCOM_APCS_IPC=y CONFIG_MTK_ADSP_MBOX=m +CONFIG_QCOM_CPUCP_MBOX=m +CONFIG_TEGRA_HSP_MBOX=y CONFIG_QCOM_IPCC=y +CONFIG_CIX_MBOX=y CONFIG_ROCKCHIP_IOMMU=y CONFIG_TEGRA_IOMMU_SMMU=y CONFIG_ARM_SMMU=y @@ -1502,6 +1521,8 @@ 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 @@ -1514,11 +1535,14 @@ CONFIG_TI_SCI_PM_DOMAINS=y CONFIG_ARM_IMX_BUS_DEVFREQ=y CONFIG_ARM_IMX8M_DDRC_DEVFREQ=m CONFIG_ARM_MEDIATEK_CCI_DEVFREQ=m +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI=m CONFIG_EXTCON_PTN5150=m CONFIG_EXTCON_USB_GPIO=y CONFIG_EXTCON_USBC_CROS_EC=y CONFIG_FSL_IFC=y CONFIG_RENESAS_RPCIF=m +CONFIG_STM32_OMM=m CONFIG_IIO=y CONFIG_EXYNOS_ADC=y CONFIG_IMX8QXP_ADC=m @@ -1530,6 +1554,7 @@ CONFIG_QCOM_SPMI_VADC=m CONFIG_QCOM_SPMI_ADC5=m CONFIG_ROCKCHIP_SARADC=m CONFIG_RZG2L_ADC=m +CONFIG_SOPHGO_CV1800B_ADC=m CONFIG_TI_ADS1015=m CONFIG_TI_AM335X_ADC=m CONFIG_IIO_CROS_EC_SENSORS_CORE=m @@ -1539,6 +1564,7 @@ CONFIG_IIO_CROS_EC_LIGHT_PROX=m CONFIG_SENSORS_ISL29018=m CONFIG_VCNL4000=m CONFIG_IIO_ST_MAGN_3AXIS=m +CONFIG_IIO_STM32_TIMER_TRIGGER=m CONFIG_IIO_CROS_EC_BARO=m CONFIG_MPL3115=m CONFIG_PWM=y @@ -1557,6 +1583,7 @@ CONFIG_PWM_RENESAS_TPU=m CONFIG_PWM_ROCKCHIP=y CONFIG_PWM_SAMSUNG=y CONFIG_PWM_SL28CPLD=m +CONFIG_PWM_STM32=m CONFIG_PWM_SUN4I=m CONFIG_PWM_TEGRA=m CONFIG_PWM_TIECAP=m @@ -1572,7 +1599,9 @@ CONFIG_RESET_IMX7=y CONFIG_RESET_QCOM_AOSS=y CONFIG_RESET_QCOM_PDC=m CONFIG_RESET_RZG2L_USBPHY_CTRL=y +CONFIG_RESET_RZV2H_USB2PHY=m CONFIG_RESET_TI_SCI=y +CONFIG_PHY_SNPS_EUSB2=m CONFIG_PHY_XGENE=y CONFIG_PHY_CAN_TRANSCEIVER=m CONFIG_PHY_NXP_PTN3222=m @@ -1597,7 +1626,6 @@ CONFIG_PHY_QCOM_EDP=m CONFIG_PHY_QCOM_PCIE2=m CONFIG_PHY_QCOM_QMP=m CONFIG_PHY_QCOM_QUSB2=m -CONFIG_PHY_QCOM_SNPS_EUSB2=m CONFIG_PHY_QCOM_EUSB2_REPEATER=m CONFIG_PHY_QCOM_M31_USB=m CONFIG_PHY_QCOM_USB_HS=m @@ -1662,6 +1690,7 @@ CONFIG_FPGA_BRIDGE=m CONFIG_ALTERA_FREEZE_BRIDGE=m CONFIG_FPGA_REGION=m CONFIG_OF_FPGA_REGION=m +CONFIG_OF_OVERLAY=y CONFIG_TEE=y CONFIG_OPTEE=y CONFIG_MUX_GPIO=m @@ -1703,6 +1732,7 @@ CONFIG_INTERCONNECT_QCOM_X1E80100=y CONFIG_COUNTER=m CONFIG_TI_EQEP=m CONFIG_RZ_MTU3_CNT=m +CONFIG_STM32_TIMER_CNT=m CONFIG_HTE=y CONFIG_HTE_TEGRA194=y CONFIG_HTE_TEGRA194_TEST=m @@ -1743,8 +1773,6 @@ CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_ANSI_CPRNG=y CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_GHASH_ARM64_CE=y -CONFIG_CRYPTO_SHA1_ARM64_CE=y -CONFIG_CRYPTO_SHA512_ARM64_CE=m CONFIG_CRYPTO_SHA3_ARM64=m CONFIG_CRYPTO_SM3_ARM64_CE=m CONFIG_CRYPTO_AES_ARM64_CE_BLK=y diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig index c44b0f202a1f..3bb5b513d5ae 100644 --- a/arch/arm64/crypto/Kconfig +++ b/arch/arm64/crypto/Kconfig @@ -25,36 +25,6 @@ config CRYPTO_NHPOLY1305_NEON Architecture: arm64 using: - NEON (Advanced SIMD) extensions -config CRYPTO_SHA1_ARM64_CE - tristate "Hash functions: SHA-1 (ARMv8 Crypto Extensions)" - depends on KERNEL_MODE_NEON - select CRYPTO_HASH - select CRYPTO_SHA1 - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: arm64 using: - - ARMv8 Crypto Extensions - -config CRYPTO_SHA512_ARM64 - tristate "Hash functions: SHA-384 and SHA-512" - select CRYPTO_HASH - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: arm64 - -config CRYPTO_SHA512_ARM64_CE - tristate "Hash functions: SHA-384 and SHA-512 (ARMv8 Crypto Extensions)" - depends on KERNEL_MODE_NEON - select CRYPTO_HASH - select CRYPTO_SHA512_ARM64 - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: arm64 using: - - ARMv8 Crypto Extensions - config CRYPTO_SHA3_ARM64 tristate "Hash functions: SHA-3 (ARMv8.2 Crypto Extensions)" depends on KERNEL_MODE_NEON diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile index c231c980c514..a8b2cdbe202c 100644 --- a/arch/arm64/crypto/Makefile +++ b/arch/arm64/crypto/Makefile @@ -5,12 +5,6 @@ # Copyright (C) 2014 Linaro Ltd # -obj-$(CONFIG_CRYPTO_SHA1_ARM64_CE) += sha1-ce.o -sha1-ce-y := sha1-ce-glue.o sha1-ce-core.o - -obj-$(CONFIG_CRYPTO_SHA512_ARM64_CE) += sha512-ce.o -sha512-ce-y := sha512-ce-glue.o sha512-ce-core.o - obj-$(CONFIG_CRYPTO_SHA3_ARM64) += sha3-ce.o sha3-ce-y := sha3-ce-glue.o sha3-ce-core.o @@ -53,9 +47,6 @@ aes-ce-blk-y := aes-glue-ce.o aes-ce.o obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o aes-neon-blk-y := aes-glue-neon.o aes-neon.o -obj-$(CONFIG_CRYPTO_SHA512_ARM64) += sha512-arm64.o -sha512-arm64-y := sha512-glue.o sha512-core.o - obj-$(CONFIG_CRYPTO_NHPOLY1305_NEON) += nhpoly1305-neon.o nhpoly1305-neon-y := nh-neon-core.o nhpoly1305-neon-glue.o @@ -64,11 +55,3 @@ aes-arm64-y := aes-cipher-core.o aes-cipher-glue.o obj-$(CONFIG_CRYPTO_AES_ARM64_BS) += aes-neon-bs.o aes-neon-bs-y := aes-neonbs-core.o aes-neonbs-glue.o - -quiet_cmd_perlasm = PERLASM $@ - cmd_perlasm = $(PERL) $(<) void $(@) - -$(obj)/sha512-core.S: $(src)/../lib/crypto/sha2-armv8.pl - $(call cmd,perlasm) - -clean-files += sha512-core.S diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c deleted file mode 100644 index 65b6980817e5..000000000000 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sha1-ce-glue.c - SHA-1 secure hash using ARMv8 Crypto Extensions - * - * Copyright (C) 2014 - 2017 Linaro Ltd - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_CRYPTO("sha1"); - -struct sha1_ce_state { - struct sha1_state sst; - u32 finalize; -}; - -extern const u32 sha1_ce_offsetof_count; -extern const u32 sha1_ce_offsetof_finalize; - -asmlinkage int __sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, - int blocks); - -static void sha1_ce_transform(struct sha1_state *sst, u8 const *src, - int blocks) -{ - while (blocks) { - int rem; - - kernel_neon_begin(); - rem = __sha1_ce_transform(container_of(sst, - struct sha1_ce_state, - sst), src, blocks); - kernel_neon_end(); - src += (blocks - rem) * SHA1_BLOCK_SIZE; - blocks = rem; - } -} - -const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count); -const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize); - -static int sha1_ce_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - struct sha1_ce_state *sctx = shash_desc_ctx(desc); - - sctx->finalize = 0; - return sha1_base_do_update_blocks(desc, data, len, sha1_ce_transform); -} - -static int sha1_ce_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - struct sha1_ce_state *sctx = shash_desc_ctx(desc); - bool finalized = false; - - /* - * Allow the asm code to perform the finalization if there is no - * partial data and the input is a round multiple of the block size. - */ - if (len >= SHA1_BLOCK_SIZE) { - unsigned int remain = len - round_down(len, SHA1_BLOCK_SIZE); - - finalized = !remain; - sctx->finalize = finalized; - sha1_base_do_update_blocks(desc, data, len, sha1_ce_transform); - data += len - remain; - len = remain; - } - if (!finalized) { - sctx->finalize = 0; - sha1_base_do_finup(desc, data, len, sha1_ce_transform); - } - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .init = sha1_base_init, - .update = sha1_ce_update, - .finup = sha1_ce_finup, - .descsize = sizeof(struct sha1_ce_state), - .statesize = SHA1_STATE_SIZE, - .digestsize = SHA1_DIGEST_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-ce", - .cra_priority = 200, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_ce_mod_init(void) -{ - return crypto_register_shash(&alg); -} - -static void __exit sha1_ce_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_cpu_feature_match(SHA1, sha1_ce_mod_init); -module_exit(sha1_ce_mod_fini); diff --git a/arch/arm64/crypto/sha512-ce-glue.c b/arch/arm64/crypto/sha512-ce-glue.c deleted file mode 100644 index 6fb3001fa2c9..000000000000 --- a/arch/arm64/crypto/sha512-ce-glue.c +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sha512-ce-glue.c - SHA-384/SHA-512 using ARMv8 Crypto Extensions - * - * Copyright (C) 2018 Linaro Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("SHA-384/SHA-512 secure hash using ARMv8 Crypto Extensions"); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha512"); - -asmlinkage int __sha512_ce_transform(struct sha512_state *sst, u8 const *src, - int blocks); - -static void sha512_ce_transform(struct sha512_state *sst, u8 const *src, - int blocks) -{ - do { - int rem; - - kernel_neon_begin(); - rem = __sha512_ce_transform(sst, src, blocks); - kernel_neon_end(); - src += (blocks - rem) * SHA512_BLOCK_SIZE; - blocks = rem; - } while (blocks); -} - -static int sha512_ce_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, - sha512_ce_transform); -} - -static int sha512_ce_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha512_base_do_finup(desc, data, len, sha512_ce_transform); - return sha512_base_finish(desc, out); -} - -static struct shash_alg algs[] = { { - .init = sha384_base_init, - .update = sha512_ce_update, - .finup = sha512_ce_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA384_DIGEST_SIZE, - .base.cra_name = "sha384", - .base.cra_driver_name = "sha384-ce", - .base.cra_priority = 200, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA512_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, -}, { - .init = sha512_base_init, - .update = sha512_ce_update, - .finup = sha512_ce_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA512_DIGEST_SIZE, - .base.cra_name = "sha512", - .base.cra_driver_name = "sha512-ce", - .base.cra_priority = 200, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA512_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, -} }; - -static int __init sha512_ce_mod_init(void) -{ - return crypto_register_shashes(algs, ARRAY_SIZE(algs)); -} - -static void __exit sha512_ce_mod_fini(void) -{ - crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); -} - -module_cpu_feature_match(SHA512, sha512_ce_mod_init); -module_exit(sha512_ce_mod_fini); diff --git a/arch/arm64/crypto/sha512-glue.c b/arch/arm64/crypto/sha512-glue.c deleted file mode 100644 index 15aa9d8b7b2c..000000000000 --- a/arch/arm64/crypto/sha512-glue.c +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Linux/arm64 port of the OpenSSL SHA512 implementation for AArch64 - * - * Copyright (c) 2016 Linaro Ltd. - */ - -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("SHA-384/SHA-512 secure hash for arm64"); -MODULE_AUTHOR("Andy Polyakov "); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha512"); - -asmlinkage void sha512_blocks_arch(u64 *digest, const void *data, - unsigned int num_blks); - -static void sha512_arm64_transform(struct sha512_state *sst, u8 const *src, - int blocks) -{ - sha512_blocks_arch(sst->state, src, blocks); -} - -static int sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, - sha512_arm64_transform); -} - -static int sha512_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha512_base_do_finup(desc, data, len, sha512_arm64_transform); - return sha512_base_finish(desc, out); -} - -static struct shash_alg algs[] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = sha512_update, - .finup = sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base.cra_name = "sha512", - .base.cra_driver_name = "sha512-arm64", - .base.cra_priority = 150, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA512_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = sha512_update, - .finup = sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base.cra_name = "sha384", - .base.cra_driver_name = "sha384-arm64", - .base.cra_priority = 150, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA384_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, -} }; - -static int __init sha512_mod_init(void) -{ - return crypto_register_shashes(algs, ARRAY_SIZE(algs)); -} - -static void __exit sha512_mod_fini(void) -{ - crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); -} - -module_init(sha512_mod_init); -module_exit(sha512_mod_fini); diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index a407f9cd549e..c07a58b96329 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -150,7 +150,7 @@ acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor) {} #endif -static inline const char *acpi_get_enable_method(int cpu) +static __always_inline const char *acpi_get_enable_method(int cpu) { if (acpi_psci_present()) return "psci"; diff --git a/arch/arm64/include/asm/asm-bug.h b/arch/arm64/include/asm/asm-bug.h index 6e73809f6492..a5f13801b784 100644 --- a/arch/arm64/include/asm/asm-bug.h +++ b/arch/arm64/include/asm/asm-bug.h @@ -21,16 +21,21 @@ #endif #ifdef CONFIG_GENERIC_BUG - -#define __BUG_ENTRY(flags) \ +#define __BUG_ENTRY_START \ .pushsection __bug_table,"aw"; \ .align 2; \ 14470: .long 14471f - .; \ -_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ - .short flags; \ + +#define __BUG_ENTRY_END \ .align 2; \ .popsection; \ 14471: + +#define __BUG_ENTRY(flags) \ + __BUG_ENTRY_START \ +_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ + .short flags; \ + __BUG_ENTRY_END #else #define __BUG_ENTRY(flags) #endif @@ -41,4 +46,24 @@ _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ #define ASM_BUG() ASM_BUG_FLAGS(0) +#ifdef CONFIG_DEBUG_BUGVERBOSE +#define __BUG_LOCATION_STRING(file, line) \ + ".long " file "- .;" \ + ".short " line ";" +#else +#define __BUG_LOCATION_STRING(file, line) +#endif + +#define __BUG_ENTRY_STRING(file, line, flags) \ + __stringify(__BUG_ENTRY_START) \ + __BUG_LOCATION_STRING(file, line) \ + ".short " flags ";" \ + __stringify(__BUG_ENTRY_END) + +#define ARCH_WARN_ASM(file, line, flags, size) \ + __BUG_ENTRY_STRING(file, line, flags) \ + __stringify(brk BUG_BRK_IMM) + +#define ARCH_WARN_REACHABLE + #endif /* __ASM_ASM_BUG_H */ diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index ad63457a05c5..23be85d93348 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -41,6 +41,11 @@ /* * Save/restore interrupts. */ + .macro save_and_disable_daif, flags + mrs \flags, daif + msr daifset, #0xf + .endm + .macro save_and_disable_irq, flags mrs \flags, daif msr daifset, #3 @@ -53,7 +58,7 @@ .macro disable_step_tsk, flgs, tmp tbz \flgs, #TIF_SINGLESTEP, 9990f mrs \tmp, mdscr_el1 - bic \tmp, \tmp, #DBG_MDSCR_SS + bic \tmp, \tmp, #MDSCR_EL1_SS msr mdscr_el1, \tmp isb // Take effect before a subsequent clear of DAIF.D 9990: @@ -63,7 +68,7 @@ .macro enable_step_tsk, flgs, tmp tbz \flgs, #TIF_SINGLESTEP, 9990f mrs \tmp, mdscr_el1 - orr \tmp, \tmp, #DBG_MDSCR_SS + orr \tmp, \tmp, #MDSCR_EL1_SS msr mdscr_el1, \tmp 9990: .endm diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h index 1ca947d5c939..f5801b0ba9e9 100644 --- a/arch/arm64/include/asm/barrier.h +++ b/arch/arm64/include/asm/barrier.h @@ -44,6 +44,9 @@ SB_BARRIER_INSN"nop\n", \ ARM64_HAS_SB)) +#define gsb_ack() asm volatile(GSB_ACK_BARRIER_INSN : : : "memory") +#define gsb_sys() asm volatile(GSB_SYS_BARRIER_INSN : : : "memory") + #ifdef CONFIG_ARM64_PSEUDO_NMI #define pmr_sync() \ do { \ diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h index 99cd6546e72e..09963004ceea 100644 --- a/arch/arm64/include/asm/cache.h +++ b/arch/arm64/include/asm/cache.h @@ -87,6 +87,23 @@ int cache_line_size(void); #define dma_get_cache_alignment cache_line_size +/* Compress a u64 MPIDR value into 32 bits. */ +static inline u64 arch_compact_of_hwid(u64 id) +{ + u64 aff3 = MPIDR_AFFINITY_LEVEL(id, 3); + + /* + * These bits are expected to be RES0. If not, return a value with + * the upper 32 bits set to force the caller to give up on 32 bit + * cache ids. + */ + if (FIELD_GET(GENMASK_ULL(63, 40), id)) + return id; + + return (aff3 << 24) | FIELD_GET(GENMASK_ULL(23, 0), id); +} +#define arch_compact_of_hwid arch_compact_of_hwid + /* * Read the effective value of CTR_EL0. * diff --git a/arch/arm64/include/asm/cfi.h b/arch/arm64/include/asm/cfi.h new file mode 100644 index 000000000000..ab90f0351b7a --- /dev/null +++ b/arch/arm64/include/asm/cfi.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ARM64_CFI_H +#define _ASM_ARM64_CFI_H + +#define __bpfcall + +#endif /* _ASM_ARM64_CFI_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index c4326f1cb917..bf13d676aae2 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -275,6 +275,14 @@ extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0; #define ARM64_CPUCAP_OPTIONAL_FOR_LATE_CPU ((u16)BIT(5)) /* Panic when a conflict is detected */ #define ARM64_CPUCAP_PANIC_ON_CONFLICT ((u16)BIT(6)) +/* + * When paired with SCOPE_LOCAL_CPU, all early CPUs must satisfy the + * condition. This is different from SCOPE_SYSTEM where the check is performed + * only once at the end of the SMP boot on the sanitised ID registers. + * SCOPE_SYSTEM is not suitable for cases where the capability depends on + * properties local to a CPU like MIDR_EL1. + */ +#define ARM64_CPUCAP_MATCH_ALL_EARLY_CPUS ((u16)BIT(7)) /* * CPU errata workarounds that need to be enabled at boot time if one or @@ -304,6 +312,16 @@ extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0; (ARM64_CPUCAP_SCOPE_LOCAL_CPU | \ ARM64_CPUCAP_OPTIONAL_FOR_LATE_CPU | \ ARM64_CPUCAP_PERMITTED_FOR_LATE_CPU) +/* + * CPU feature detected at boot time and present on all early CPUs. Late CPUs + * are permitted to have the feature even if it hasn't been enabled, although + * the feature will not be used by Linux in this case. If all early CPUs have + * the feature, then every late CPU must have it. + */ +#define ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE \ + (ARM64_CPUCAP_SCOPE_LOCAL_CPU | \ + ARM64_CPUCAP_PERMITTED_FOR_LATE_CPU | \ + ARM64_CPUCAP_MATCH_ALL_EARLY_CPUS) /* * CPU feature detected at boot time, on one or more CPUs. A late CPU @@ -391,6 +409,11 @@ static inline int cpucap_default_scope(const struct arm64_cpu_capabilities *cap) return cap->type & ARM64_CPUCAP_SCOPE_MASK; } +static inline bool cpucap_match_all_early_cpus(const struct arm64_cpu_capabilities *cap) +{ + return cap->type & ARM64_CPUCAP_MATCH_ALL_EARLY_CPUS; +} + /* * Generic helper for handling capabilities with multiple (match,enable) pairs * of call backs, sharing the same capability bit. @@ -848,6 +871,11 @@ static inline bool system_supports_pmuv3(void) return cpus_have_final_cap(ARM64_HAS_PMUV3); } +static inline bool system_supports_bbml2_noabort(void) +{ + return alternative_has_cap_unlikely(ARM64_HAS_BBML2_NOABORT); +} + int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); bool try_emulate_mrs(struct pt_regs *regs, u32 isn); diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h index 8f6ba31b8658..f5e3ed2420ce 100644 --- a/arch/arm64/include/asm/debug-monitors.h +++ b/arch/arm64/include/asm/debug-monitors.h @@ -13,14 +13,8 @@ #include /* Low-level stepping controls. */ -#define DBG_MDSCR_SS (1 << 0) #define DBG_SPSR_SS (1 << 21) -/* MDSCR_EL1 enabling bits */ -#define DBG_MDSCR_KDE (1 << 13) -#define DBG_MDSCR_MDE (1 << 15) -#define DBG_MDSCR_MASK ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE) - #define DBG_ESR_EVT(x) (((x) >> 27) & 0x7) /* AArch64 */ @@ -62,30 +56,6 @@ struct task_struct; #define DBG_HOOK_HANDLED 0 #define DBG_HOOK_ERROR 1 -struct step_hook { - struct list_head node; - int (*fn)(struct pt_regs *regs, unsigned long esr); -}; - -void register_user_step_hook(struct step_hook *hook); -void unregister_user_step_hook(struct step_hook *hook); - -void register_kernel_step_hook(struct step_hook *hook); -void unregister_kernel_step_hook(struct step_hook *hook); - -struct break_hook { - struct list_head node; - int (*fn)(struct pt_regs *regs, unsigned long esr); - u16 imm; - u16 mask; /* These bits are ignored when comparing with imm */ -}; - -void register_user_break_hook(struct break_hook *hook); -void unregister_user_break_hook(struct break_hook *hook); - -void register_kernel_break_hook(struct break_hook *hook); -void unregister_kernel_break_hook(struct break_hook *hook); - u8 debug_monitors_arch(void); enum dbg_active_el { @@ -108,17 +78,15 @@ void kernel_rewind_single_step(struct pt_regs *regs); void kernel_fastforward_single_step(struct pt_regs *regs); #ifdef CONFIG_HAVE_HW_BREAKPOINT -int reinstall_suspended_bps(struct pt_regs *regs); +bool try_step_suspended_breakpoints(struct pt_regs *regs); #else -static inline int reinstall_suspended_bps(struct pt_regs *regs) +static inline bool try_step_suspended_breakpoints(struct pt_regs *regs) { - return -ENODEV; + return false; } #endif -int aarch32_break_handler(struct pt_regs *regs); - -void debug_traps_init(void); +bool try_handle_aarch32_break(struct pt_regs *regs); #endif /* __ASSEMBLY */ #endif /* __ASM_DEBUG_MONITORS_H */ diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index ba5df0df02a4..46033027510c 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -165,6 +165,50 @@ .Lskip_gicv3_\@: .endm +/* GICv5 system register access */ +.macro __init_el2_gicv5 + mrs_s x0, SYS_ID_AA64PFR2_EL1 + ubfx x0, x0, #ID_AA64PFR2_EL1_GCIE_SHIFT, #4 + cbz x0, .Lskip_gicv5_\@ + + mov x0, #(ICH_HFGITR_EL2_GICRCDNMIA | \ + ICH_HFGITR_EL2_GICRCDIA | \ + ICH_HFGITR_EL2_GICCDDI | \ + ICH_HFGITR_EL2_GICCDEOI | \ + ICH_HFGITR_EL2_GICCDHM | \ + ICH_HFGITR_EL2_GICCDRCFG | \ + ICH_HFGITR_EL2_GICCDPEND | \ + ICH_HFGITR_EL2_GICCDAFF | \ + ICH_HFGITR_EL2_GICCDPRI | \ + ICH_HFGITR_EL2_GICCDDIS | \ + ICH_HFGITR_EL2_GICCDEN) + msr_s SYS_ICH_HFGITR_EL2, x0 // Disable instruction traps + mov_q x0, (ICH_HFGRTR_EL2_ICC_PPI_ACTIVERn_EL1 | \ + ICH_HFGRTR_EL2_ICC_PPI_PRIORITYRn_EL1 | \ + ICH_HFGRTR_EL2_ICC_PPI_PENDRn_EL1 | \ + ICH_HFGRTR_EL2_ICC_PPI_ENABLERn_EL1 | \ + ICH_HFGRTR_EL2_ICC_PPI_HMRn_EL1 | \ + ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1 | \ + ICH_HFGRTR_EL2_ICC_ICSR_EL1 | \ + ICH_HFGRTR_EL2_ICC_PCR_EL1 | \ + ICH_HFGRTR_EL2_ICC_HPPIR_EL1 | \ + ICH_HFGRTR_EL2_ICC_HAPR_EL1 | \ + ICH_HFGRTR_EL2_ICC_CR0_EL1 | \ + ICH_HFGRTR_EL2_ICC_IDRn_EL1 | \ + ICH_HFGRTR_EL2_ICC_APR_EL1) + msr_s SYS_ICH_HFGRTR_EL2, x0 // Disable reg read traps + mov_q x0, (ICH_HFGWTR_EL2_ICC_PPI_ACTIVERn_EL1 | \ + ICH_HFGWTR_EL2_ICC_PPI_PRIORITYRn_EL1 | \ + ICH_HFGWTR_EL2_ICC_PPI_PENDRn_EL1 | \ + ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1 | \ + ICH_HFGWTR_EL2_ICC_ICSR_EL1 | \ + ICH_HFGWTR_EL2_ICC_PCR_EL1 | \ + ICH_HFGWTR_EL2_ICC_CR0_EL1 | \ + ICH_HFGWTR_EL2_ICC_APR_EL1) + msr_s SYS_ICH_HFGWTR_EL2, x0 // Disable reg write traps +.Lskip_gicv5_\@: +.endm + .macro __init_el2_hstr msr hstr_el2, xzr // Disable CP15 traps to EL2 .endm @@ -189,6 +233,28 @@ .Lskip_set_cptr_\@: .endm +/* + * Configure BRBE to permit recording cycle counts and branch mispredicts. + * + * At any EL, to record cycle counts BRBE requires that both BRBCR_EL2.CC=1 and + * BRBCR_EL1.CC=1. + * + * At any EL, to record branch mispredicts BRBE requires that both + * BRBCR_EL2.MPRED=1 and BRBCR_EL1.MPRED=1. + * + * Set {CC,MPRED} in BRBCR_EL2 in case nVHE mode is used and we are + * executing in EL1. + */ +.macro __init_el2_brbe + mrs x1, id_aa64dfr0_el1 + ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4 + cbz x1, .Lskip_brbe_\@ + + mov_q x0, BRBCR_ELx_CC | BRBCR_ELx_MPRED + msr_s SYS_BRBCR_EL2, x0 +.Lskip_brbe_\@: +.endm + /* Disable any fine grained traps */ .macro __init_el2_fgt mrs x1, id_aa64mmfr0_el1 @@ -196,20 +262,62 @@ cbz x1, .Lskip_fgt_\@ mov x0, xzr + mov x2, xzr mrs x1, id_aa64dfr0_el1 ubfx x1, x1, #ID_AA64DFR0_EL1_PMSVer_SHIFT, #4 cmp x1, #3 b.lt .Lskip_spe_fgt_\@ /* Disable PMSNEVFR_EL1 read and write traps */ - orr x0, x0, #(1 << 62) + orr x0, x0, #HDFGRTR_EL2_nPMSNEVFR_EL1_MASK + orr x2, x2, #HDFGWTR_EL2_nPMSNEVFR_EL1_MASK .Lskip_spe_fgt_\@: + mrs x1, id_aa64dfr0_el1 + ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4 + cbz x1, .Lskip_brbe_fgt_\@ + + /* + * Disable read traps for the following registers + * + * [BRBSRC|BRBTGT|RBINF]_EL1 + * [BRBSRCINJ|BRBTGTINJ|BRBINFINJ|BRBTS]_EL1 + */ + orr x0, x0, #HDFGRTR_EL2_nBRBDATA_MASK + + /* + * Disable write traps for the following registers + * + * [BRBSRCINJ|BRBTGTINJ|BRBINFINJ|BRBTS]_EL1 + */ + orr x2, x2, #HDFGWTR_EL2_nBRBDATA_MASK + + /* Disable read and write traps for [BRBCR|BRBFCR]_EL1 */ + orr x0, x0, #HDFGRTR_EL2_nBRBCTL_MASK + orr x2, x2, #HDFGWTR_EL2_nBRBCTL_MASK + + /* Disable read traps for BRBIDR_EL1 */ + orr x0, x0, #HDFGRTR_EL2_nBRBIDR_MASK + +.Lskip_brbe_fgt_\@: .Lset_debug_fgt_\@: msr_s SYS_HDFGRTR_EL2, x0 - msr_s SYS_HDFGWTR_EL2, x0 + msr_s SYS_HDFGWTR_EL2, x2 mov x0, xzr + mov x2, xzr + + mrs x1, id_aa64dfr0_el1 + ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4 + cbz x1, .Lskip_brbe_insn_fgt_\@ + + /* Disable traps for BRBIALL instruction */ + orr x2, x2, #HFGITR_EL2_nBRBIALL_MASK + + /* Disable traps for BRBINJ instruction */ + orr x2, x2, #HFGITR_EL2_nBRBINJ_MASK + +.Lskip_brbe_insn_fgt_\@: mrs x1, id_aa64pfr1_el1 ubfx x1, x1, #ID_AA64PFR1_EL1_SME_SHIFT, #4 cbz x1, .Lskip_sme_fgt_\@ @@ -250,7 +358,7 @@ .Lset_fgt_\@: msr_s SYS_HFGRTR_EL2, x0 msr_s SYS_HFGWTR_EL2, x0 - msr_s SYS_HFGITR_EL2, xzr + msr_s SYS_HFGITR_EL2, x2 mrs x1, id_aa64pfr0_el1 // AMU traps UNDEF without AMU ubfx x1, x1, #ID_AA64PFR0_EL1_AMU_SHIFT, #4 @@ -287,17 +395,6 @@ .Lskip_fgt2_\@: .endm -.macro __init_el2_gcs - mrs_s x1, SYS_ID_AA64PFR1_EL1 - ubfx x1, x1, #ID_AA64PFR1_EL1_GCS_SHIFT, #4 - cbz x1, .Lskip_gcs_\@ - - /* Ensure GCS is not enabled when we start trying to do BLs */ - msr_s SYS_GCSCR_EL1, xzr - msr_s SYS_GCSCRE0_EL1, xzr -.Lskip_gcs_\@: -.endm - /** * Initialize EL2 registers to sane values. This should be called early on all * cores that were booted in EL2. Note that everything gets initialised as @@ -311,15 +408,16 @@ __init_el2_hcrx __init_el2_timers __init_el2_debug + __init_el2_brbe __init_el2_lor __init_el2_stage2 __init_el2_gicv3 + __init_el2_gicv5 __init_el2_hstr __init_el2_nvhe_idregs __init_el2_cptr __init_el2_fgt __init_el2_fgt2 - __init_el2_gcs .endm #ifndef __KVM_NVHE_HYPERVISOR__ @@ -371,6 +469,13 @@ msr_s SYS_MPAMHCR_EL2, xzr // clear TRAP_MPAMIDR_EL1 -> EL2 .Lskip_mpam_\@: + check_override id_aa64pfr1, ID_AA64PFR1_EL1_GCS_SHIFT, .Linit_gcs_\@, .Lskip_gcs_\@, x1, x2 + +.Linit_gcs_\@: + msr_s SYS_GCSCR_EL1, xzr + msr_s SYS_GCSCRE0_EL1, xzr + +.Lskip_gcs_\@: check_override id_aa64pfr0, ID_AA64PFR0_EL1_SVE_SHIFT, .Linit_sve_\@, .Lskip_sve_\@, x1, x2 .Linit_sve_\@: /* SVE register access */ diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h index d48fc16584cd..e3874c4fc399 100644 --- a/arch/arm64/include/asm/exception.h +++ b/arch/arm64/include/asm/exception.h @@ -59,8 +59,20 @@ void do_el0_bti(struct pt_regs *regs); void do_el1_bti(struct pt_regs *regs, unsigned long esr); void do_el0_gcs(struct pt_regs *regs, unsigned long esr); void do_el1_gcs(struct pt_regs *regs, unsigned long esr); -void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr, +#ifdef CONFIG_HAVE_HW_BREAKPOINT +void do_breakpoint(unsigned long esr, struct pt_regs *regs); +void do_watchpoint(unsigned long addr, unsigned long esr, struct pt_regs *regs); +#else +static inline void do_breakpoint(unsigned long esr, struct pt_regs *regs) {} +static inline void do_watchpoint(unsigned long addr, unsigned long esr, + struct pt_regs *regs) {} +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ +void do_el0_softstep(unsigned long esr, struct pt_regs *regs); +void do_el1_softstep(unsigned long esr, struct pt_regs *regs); +void do_el0_brk64(unsigned long esr, struct pt_regs *regs); +void do_el1_brk64(unsigned long esr, struct pt_regs *regs); +void do_bkpt32(unsigned long esr, struct pt_regs *regs); void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs); void do_sve_acc(unsigned long esr, struct pt_regs *regs); void do_sme_acc(unsigned long esr, struct pt_regs *regs); diff --git a/arch/arm64/include/asm/gcs.h b/arch/arm64/include/asm/gcs.h index f50660603ecf..5bc432234d3a 100644 --- a/arch/arm64/include/asm/gcs.h +++ b/arch/arm64/include/asm/gcs.h @@ -58,7 +58,7 @@ static inline u64 gcsss2(void) static inline bool task_gcs_el0_enabled(struct task_struct *task) { - return current->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE; + return task->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE; } void gcs_set_el0_mode(struct task_struct *task); diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index 1c3f9617d54f..13f94c8ddfc0 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -176,6 +176,8 @@ #define KERNEL_HWCAP_POE __khwcap2_feature(POE) #define __khwcap3_feature(x) (const_ilog2(HWCAP3_ ## x) + 128) +#define KERNEL_HWCAP_MTE_FAR __khwcap3_feature(MTE_FAR) +#define KERNEL_HWCAP_MTE_STORE_ONLY __khwcap3_feature(MTE_STORE_ONLY) /* * This yields a mask that user programs can use to figure out what diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h index 21fc85e9d2be..3184f5d1e3ae 100644 --- a/arch/arm64/include/asm/kgdb.h +++ b/arch/arm64/include/asm/kgdb.h @@ -24,6 +24,18 @@ static inline void arch_kgdb_breakpoint(void) extern void kgdb_handle_bus_error(void); extern int kgdb_fault_expected; +int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr); +int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr); +#ifdef CONFIG_KGDB +int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr); +#else +static inline int kgdb_single_step_handler(struct pt_regs *regs, + unsigned long esr) +{ + return DBG_HOOK_ERROR; +} +#endif + #endif /* !__ASSEMBLY__ */ /* diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h index be7a3680dadf..f2782560647b 100644 --- a/arch/arm64/include/asm/kprobes.h +++ b/arch/arm64/include/asm/kprobes.h @@ -41,4 +41,12 @@ void __kretprobe_trampoline(void); void __kprobes *trampoline_probe_handler(struct pt_regs *regs); #endif /* CONFIG_KPROBES */ + +int __kprobes kprobe_brk_handler(struct pt_regs *regs, + unsigned long esr); +int __kprobes kprobe_ss_brk_handler(struct pt_regs *regs, + unsigned long esr); +int __kprobes kretprobe_brk_handler(struct pt_regs *regs, + unsigned long esr); + #endif /* _ARM_KPROBES_H */ diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index bd020fc28aa9..fa8a08a1ccd5 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -45,16 +45,39 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu); void kvm_skip_instr32(struct kvm_vcpu *vcpu); void kvm_inject_undefined(struct kvm_vcpu *vcpu); -void kvm_inject_vabt(struct kvm_vcpu *vcpu); -void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); -void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); +int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr); +int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr); void kvm_inject_size_fault(struct kvm_vcpu *vcpu); +static inline int kvm_inject_sea_dabt(struct kvm_vcpu *vcpu, u64 addr) +{ + return kvm_inject_sea(vcpu, false, addr); +} + +static inline int kvm_inject_sea_iabt(struct kvm_vcpu *vcpu, u64 addr) +{ + return kvm_inject_sea(vcpu, true, addr); +} + +static inline int kvm_inject_serror(struct kvm_vcpu *vcpu) +{ + /* + * ESR_ELx.ISV (later renamed to IDS) indicates whether or not + * ESR_ELx.ISS contains IMPLEMENTATION DEFINED syndrome information. + * + * Set the bit when injecting an SError w/o an ESR to indicate ISS + * does not follow the architected format. + */ + return kvm_inject_serror_esr(vcpu, ESR_ELx_ISV); +} + void kvm_vcpu_wfi(struct kvm_vcpu *vcpu); void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu); int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2); int kvm_inject_nested_irq(struct kvm_vcpu *vcpu); +int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr); +int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr); static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu) { @@ -195,6 +218,11 @@ static inline bool vcpu_el2_tge_is_set(const struct kvm_vcpu *vcpu) return ctxt_sys_reg(&vcpu->arch.ctxt, HCR_EL2) & HCR_TGE; } +static inline bool vcpu_el2_amo_is_set(const struct kvm_vcpu *vcpu) +{ + return ctxt_sys_reg(&vcpu->arch.ctxt, HCR_EL2) & HCR_AMO; +} + static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu) { bool e2h, tge; @@ -224,6 +252,20 @@ static inline bool vcpu_is_host_el0(const struct kvm_vcpu *vcpu) return is_hyp_ctxt(vcpu) && !vcpu_is_el2(vcpu); } +static inline bool is_nested_ctxt(struct kvm_vcpu *vcpu) +{ + return vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu); +} + +static inline bool vserror_state_is_nested(struct kvm_vcpu *vcpu) +{ + if (!is_nested_ctxt(vcpu)) + return false; + + return vcpu_el2_amo_is_set(vcpu) || + (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TMEA); +} + /* * The layout of SPSR for an AArch32 state is different when observed from an * AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32 @@ -561,68 +603,6 @@ static __always_inline void kvm_incr_pc(struct kvm_vcpu *vcpu) vcpu_set_flag((v), e); \ } while (0) -#define __build_check_all_or_none(r, bits) \ - BUILD_BUG_ON(((r) & (bits)) && ((r) & (bits)) != (bits)) - -#define __cpacr_to_cptr_clr(clr, set) \ - ({ \ - u64 cptr = 0; \ - \ - if ((set) & CPACR_EL1_FPEN) \ - cptr |= CPTR_EL2_TFP; \ - if ((set) & CPACR_EL1_ZEN) \ - cptr |= CPTR_EL2_TZ; \ - if ((set) & CPACR_EL1_SMEN) \ - cptr |= CPTR_EL2_TSM; \ - if ((clr) & CPACR_EL1_TTA) \ - cptr |= CPTR_EL2_TTA; \ - if ((clr) & CPTR_EL2_TAM) \ - cptr |= CPTR_EL2_TAM; \ - if ((clr) & CPTR_EL2_TCPAC) \ - cptr |= CPTR_EL2_TCPAC; \ - \ - cptr; \ - }) - -#define __cpacr_to_cptr_set(clr, set) \ - ({ \ - u64 cptr = 0; \ - \ - if ((clr) & CPACR_EL1_FPEN) \ - cptr |= CPTR_EL2_TFP; \ - if ((clr) & CPACR_EL1_ZEN) \ - cptr |= CPTR_EL2_TZ; \ - if ((clr) & CPACR_EL1_SMEN) \ - cptr |= CPTR_EL2_TSM; \ - if ((set) & CPACR_EL1_TTA) \ - cptr |= CPTR_EL2_TTA; \ - if ((set) & CPTR_EL2_TAM) \ - cptr |= CPTR_EL2_TAM; \ - if ((set) & CPTR_EL2_TCPAC) \ - cptr |= CPTR_EL2_TCPAC; \ - \ - cptr; \ - }) - -#define cpacr_clear_set(clr, set) \ - do { \ - BUILD_BUG_ON((set) & CPTR_VHE_EL2_RES0); \ - BUILD_BUG_ON((clr) & CPACR_EL1_E0POE); \ - __build_check_all_or_none((clr), CPACR_EL1_FPEN); \ - __build_check_all_or_none((set), CPACR_EL1_FPEN); \ - __build_check_all_or_none((clr), CPACR_EL1_ZEN); \ - __build_check_all_or_none((set), CPACR_EL1_ZEN); \ - __build_check_all_or_none((clr), CPACR_EL1_SMEN); \ - __build_check_all_or_none((set), CPACR_EL1_SMEN); \ - \ - if (has_vhe() || has_hvhe()) \ - sysreg_clear_set(cpacr_el1, clr, set); \ - else \ - sysreg_clear_set(cptr_el2, \ - __cpacr_to_cptr_clr(clr, set), \ - __cpacr_to_cptr_set(clr, set));\ - } while (0) - /* * Returns a 'sanitised' view of CPTR_EL2, translating from nVHE to the VHE * format if E2H isn't set. @@ -689,6 +669,9 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu) if (kvm_has_fpmr(kvm)) vcpu->arch.hcrx_el2 |= HCRX_EL2_EnFPM; + + if (kvm_has_sctlr2(kvm)) + vcpu->arch.hcrx_el2 |= HCRX_EL2_SCTLR2En; } } #endif /* __ARM64_KVM_EMULATE_H__ */ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 5ccca509dff1..2b07f0a27a7d 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -523,6 +523,7 @@ enum vcpu_sysreg { /* Anything from this can be RES0/RES1 sanitised */ MARKER(__SANITISED_REG_START__), TCR2_EL2, /* Extended Translation Control Register (EL2) */ + SCTLR2_EL2, /* System Control Register 2 (EL2) */ MDCR_EL2, /* Monitor Debug Configuration Register (EL2) */ CNTHCTL_EL2, /* Counter-timer Hypervisor Control register */ @@ -537,6 +538,7 @@ enum vcpu_sysreg { VNCR(TTBR1_EL1),/* Translation Table Base Register 1 */ VNCR(TCR_EL1), /* Translation Control Register */ VNCR(TCR2_EL1), /* Extended Translation Control Register */ + VNCR(SCTLR2_EL1), /* System Control Register 2 */ VNCR(ESR_EL1), /* Exception Syndrome Register */ VNCR(AFSR0_EL1),/* Auxiliary Fault Status Register 0 */ VNCR(AFSR1_EL1),/* Auxiliary Fault Status Register 1 */ @@ -565,6 +567,10 @@ enum vcpu_sysreg { VNCR(POR_EL1), /* Permission Overlay Register 1 (EL1) */ + /* FEAT_RAS registers */ + VNCR(VDISR_EL2), + VNCR(VSESR_EL2), + VNCR(HFGRTR_EL2), VNCR(HFGWTR_EL2), VNCR(HFGITR_EL2), @@ -704,6 +710,7 @@ struct kvm_host_data { #define KVM_HOST_DATA_FLAG_EL1_TRACING_CONFIGURED 5 #define KVM_HOST_DATA_FLAG_VCPU_IN_HYP_CONTEXT 6 #define KVM_HOST_DATA_FLAG_L1_VNCR_MAPPED 7 +#define KVM_HOST_DATA_FLAG_HAS_BRBE 8 unsigned long flags; struct kvm_cpu_context host_ctxt; @@ -737,6 +744,7 @@ struct kvm_host_data { u64 trfcr_el1; /* Values of trap registers for the host before guest entry. */ u64 mdcr_el2; + u64 brbcr_el1; } host_debug_state; /* Guest trace filter value */ @@ -817,7 +825,7 @@ struct kvm_vcpu_arch { u8 iflags; /* State flags for kernel bookkeeping, unused by the hypervisor code */ - u8 sflags; + u16 sflags; /* * Don't run the guest (internal implementation need). @@ -953,9 +961,21 @@ struct kvm_vcpu_arch { __vcpu_flags_preempt_enable(); \ } while (0) +#define __vcpu_test_and_clear_flag(v, flagset, f, m) \ + ({ \ + typeof(v->arch.flagset) set; \ + \ + set = __vcpu_get_flag(v, flagset, f, m); \ + __vcpu_clear_flag(v, flagset, f, m); \ + \ + set; \ + }) + #define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__) #define vcpu_set_flag(v, ...) __vcpu_set_flag((v), __VA_ARGS__) #define vcpu_clear_flag(v, ...) __vcpu_clear_flag((v), __VA_ARGS__) +#define vcpu_test_and_clear_flag(v, ...) \ + __vcpu_test_and_clear_flag((v), __VA_ARGS__) /* KVM_ARM_VCPU_INIT completed */ #define VCPU_INITIALIZED __vcpu_single_flag(cflags, BIT(0)) @@ -1015,6 +1035,8 @@ struct kvm_vcpu_arch { #define IN_WFI __vcpu_single_flag(sflags, BIT(6)) /* KVM is currently emulating a nested ERET */ #define IN_NESTED_ERET __vcpu_single_flag(sflags, BIT(7)) +/* SError pending for nested guest */ +#define NESTED_SERROR_PENDING __vcpu_single_flag(sflags, BIT(8)) /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */ @@ -1138,109 +1160,8 @@ u64 kvm_vcpu_apply_reg_masks(const struct kvm_vcpu *, enum vcpu_sysreg, u64); __v; \ }) -u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg); -void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg); - -static inline bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val) -{ - /* - * *** VHE ONLY *** - * - * System registers listed in the switch are not saved on every - * exit from the guest but are only saved on vcpu_put. - * - * Note that MPIDR_EL1 for the guest is set by KVM via VMPIDR_EL2 but - * should never be listed below, because the guest cannot modify its - * own MPIDR_EL1 and MPIDR_EL1 is accessed for VCPU A from VCPU B's - * thread when emulating cross-VCPU communication. - */ - if (!has_vhe()) - return false; - - switch (reg) { - case SCTLR_EL1: *val = read_sysreg_s(SYS_SCTLR_EL12); break; - case CPACR_EL1: *val = read_sysreg_s(SYS_CPACR_EL12); break; - case TTBR0_EL1: *val = read_sysreg_s(SYS_TTBR0_EL12); break; - case TTBR1_EL1: *val = read_sysreg_s(SYS_TTBR1_EL12); break; - case TCR_EL1: *val = read_sysreg_s(SYS_TCR_EL12); break; - case TCR2_EL1: *val = read_sysreg_s(SYS_TCR2_EL12); break; - case PIR_EL1: *val = read_sysreg_s(SYS_PIR_EL12); break; - case PIRE0_EL1: *val = read_sysreg_s(SYS_PIRE0_EL12); break; - case POR_EL1: *val = read_sysreg_s(SYS_POR_EL12); break; - case ESR_EL1: *val = read_sysreg_s(SYS_ESR_EL12); break; - case AFSR0_EL1: *val = read_sysreg_s(SYS_AFSR0_EL12); break; - case AFSR1_EL1: *val = read_sysreg_s(SYS_AFSR1_EL12); break; - case FAR_EL1: *val = read_sysreg_s(SYS_FAR_EL12); break; - case MAIR_EL1: *val = read_sysreg_s(SYS_MAIR_EL12); break; - case VBAR_EL1: *val = read_sysreg_s(SYS_VBAR_EL12); break; - case CONTEXTIDR_EL1: *val = read_sysreg_s(SYS_CONTEXTIDR_EL12);break; - case TPIDR_EL0: *val = read_sysreg_s(SYS_TPIDR_EL0); break; - case TPIDRRO_EL0: *val = read_sysreg_s(SYS_TPIDRRO_EL0); break; - case TPIDR_EL1: *val = read_sysreg_s(SYS_TPIDR_EL1); break; - case AMAIR_EL1: *val = read_sysreg_s(SYS_AMAIR_EL12); break; - case CNTKCTL_EL1: *val = read_sysreg_s(SYS_CNTKCTL_EL12); break; - case ELR_EL1: *val = read_sysreg_s(SYS_ELR_EL12); break; - case SPSR_EL1: *val = read_sysreg_s(SYS_SPSR_EL12); break; - case PAR_EL1: *val = read_sysreg_par(); break; - case DACR32_EL2: *val = read_sysreg_s(SYS_DACR32_EL2); break; - case IFSR32_EL2: *val = read_sysreg_s(SYS_IFSR32_EL2); break; - case DBGVCR32_EL2: *val = read_sysreg_s(SYS_DBGVCR32_EL2); break; - case ZCR_EL1: *val = read_sysreg_s(SYS_ZCR_EL12); break; - default: return false; - } - - return true; -} - -static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg) -{ - /* - * *** VHE ONLY *** - * - * System registers listed in the switch are not restored on every - * entry to the guest but are only restored on vcpu_load. - * - * Note that MPIDR_EL1 for the guest is set by KVM via VMPIDR_EL2 but - * should never be listed below, because the MPIDR should only be set - * once, before running the VCPU, and never changed later. - */ - if (!has_vhe()) - return false; - - switch (reg) { - case SCTLR_EL1: write_sysreg_s(val, SYS_SCTLR_EL12); break; - case CPACR_EL1: write_sysreg_s(val, SYS_CPACR_EL12); break; - case TTBR0_EL1: write_sysreg_s(val, SYS_TTBR0_EL12); break; - case TTBR1_EL1: write_sysreg_s(val, SYS_TTBR1_EL12); break; - case TCR_EL1: write_sysreg_s(val, SYS_TCR_EL12); break; - case TCR2_EL1: write_sysreg_s(val, SYS_TCR2_EL12); break; - case PIR_EL1: write_sysreg_s(val, SYS_PIR_EL12); break; - case PIRE0_EL1: write_sysreg_s(val, SYS_PIRE0_EL12); break; - case POR_EL1: write_sysreg_s(val, SYS_POR_EL12); break; - case ESR_EL1: write_sysreg_s(val, SYS_ESR_EL12); break; - case AFSR0_EL1: write_sysreg_s(val, SYS_AFSR0_EL12); break; - case AFSR1_EL1: write_sysreg_s(val, SYS_AFSR1_EL12); break; - case FAR_EL1: write_sysreg_s(val, SYS_FAR_EL12); break; - case MAIR_EL1: write_sysreg_s(val, SYS_MAIR_EL12); break; - case VBAR_EL1: write_sysreg_s(val, SYS_VBAR_EL12); break; - case CONTEXTIDR_EL1: write_sysreg_s(val, SYS_CONTEXTIDR_EL12);break; - case TPIDR_EL0: write_sysreg_s(val, SYS_TPIDR_EL0); break; - case TPIDRRO_EL0: write_sysreg_s(val, SYS_TPIDRRO_EL0); break; - case TPIDR_EL1: write_sysreg_s(val, SYS_TPIDR_EL1); break; - case AMAIR_EL1: write_sysreg_s(val, SYS_AMAIR_EL12); break; - case CNTKCTL_EL1: write_sysreg_s(val, SYS_CNTKCTL_EL12); break; - case ELR_EL1: write_sysreg_s(val, SYS_ELR_EL12); break; - case SPSR_EL1: write_sysreg_s(val, SYS_SPSR_EL12); break; - case PAR_EL1: write_sysreg_s(val, SYS_PAR_EL1); break; - case DACR32_EL2: write_sysreg_s(val, SYS_DACR32_EL2); break; - case IFSR32_EL2: write_sysreg_s(val, SYS_IFSR32_EL2); break; - case DBGVCR32_EL2: write_sysreg_s(val, SYS_DBGVCR32_EL2); break; - case ZCR_EL1: write_sysreg_s(val, SYS_ZCR_EL12); break; - default: return false; - } - - return true; -} +u64 vcpu_read_sys_reg(const struct kvm_vcpu *, enum vcpu_sysreg); +void vcpu_write_sys_reg(struct kvm_vcpu *, u64, enum vcpu_sysreg); struct kvm_vm_stat { struct kvm_vm_stat_generic generic; @@ -1289,9 +1210,8 @@ void kvm_arm_resume_guest(struct kvm *kvm); }) /* - * The couple of isb() below are there to guarantee the same behaviour - * on VHE as on !VHE, where the eret to EL1 acts as a context - * synchronization event. + * The isb() below is there to guarantee the same behaviour on VHE as on !VHE, + * where the eret to EL1 acts as a context synchronization event. */ #define kvm_call_hyp(f, ...) \ do { \ @@ -1309,7 +1229,6 @@ void kvm_arm_resume_guest(struct kvm *kvm); \ if (has_vhe()) { \ ret = f(__VA_ARGS__); \ - isb(); \ } else { \ ret = kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \ } \ @@ -1389,8 +1308,6 @@ static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch) return (vcpu_arch->steal.base != INVALID_GPA); } -void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome); - struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr); DECLARE_KVM_HYP_PER_CPU(struct kvm_host_data, kvm_host_data); @@ -1482,7 +1399,6 @@ int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *range); /* Guest/host FPSIMD coordination helpers */ -int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu); @@ -1668,6 +1584,12 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val); #define kvm_has_s1poe(k) \ (kvm_has_feat((k), ID_AA64MMFR3_EL1, S1POE, IMP)) +#define kvm_has_ras(k) \ + (kvm_has_feat((k), ID_AA64PFR0_EL1, RAS, IMP)) + +#define kvm_has_sctlr2(k) \ + (kvm_has_feat((k), ID_AA64MMFR3_EL1, SCTLRX, IMP)) + static inline bool kvm_arch_has_irq_bypass(void) { return true; diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index b98ac6aa631f..e4069f2ce642 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -180,6 +180,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu); int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, phys_addr_t pa, unsigned long size, bool writable); +int kvm_handle_guest_sea(struct kvm_vcpu *vcpu); int kvm_handle_guest_abort(struct kvm_vcpu *vcpu); phys_addr_t kvm_mmu_get_httbr(void); @@ -371,6 +372,24 @@ static inline void kvm_fault_unlock(struct kvm *kvm) read_unlock(&kvm->mmu_lock); } +/* + * ARM64 KVM relies on a simple conversion from physaddr to a kernel + * virtual address (KVA) when it does cache maintenance as the CMO + * instructions work on virtual addresses. This is incompatible with + * VM_PFNMAP VMAs which may not have a kernel direct mapping to a + * virtual address. + * + * With S2FWB and CACHE DIC features, KVM need not do cache flushing + * and CMOs are NOP'd. This has the effect of no longer requiring a + * KVA for addresses mapped into the S2. The presence of these features + * are thus necessary to support cacheable S2 mapping of VM_PFNMAP. + */ +static inline bool kvm_supports_cacheable_pfnmap(void) +{ + return cpus_have_final_cap(ARM64_HAS_STAGE2_FWB) && + cpus_have_final_cap(ARM64_HAS_CACHE_DIC); +} + #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS void kvm_s2_ptdump_create_debugfs(struct kvm *kvm); #else diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index 0bd07ea068a1..7fd76f41c296 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -80,6 +80,8 @@ extern void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu); extern void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu); extern void check_nested_vcpu_requests(struct kvm_vcpu *vcpu); +extern void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu); +extern void kvm_nested_sync_hwstate(struct kvm_vcpu *vcpu); struct kvm_s2_trans { phys_addr_t output; diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index 2888b5d03757..1246216616b5 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -355,6 +355,11 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke return pteref; } +static inline kvm_pte_t *kvm_dereference_pteref_raw(kvm_pteref_t pteref) +{ + return pteref; +} + static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker) { /* @@ -384,6 +389,11 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke return rcu_dereference_check(pteref, !(walker->flags & KVM_PGTABLE_WALK_SHARED)); } +static inline kvm_pte_t *kvm_dereference_pteref_raw(kvm_pteref_t pteref) +{ + return rcu_dereference_raw(pteref); +} + static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker) { if (walker->flags & KVM_PGTABLE_WALK_SHARED) @@ -551,6 +561,26 @@ static inline int kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2 */ void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt); +/** + * kvm_pgtable_stage2_destroy_range() - Destroy the unlinked range of addresses. + * @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*(). + * @addr: Intermediate physical address at which to place the mapping. + * @size: Size of the mapping. + * + * The page-table is assumed to be unreachable by any hardware walkers prior + * to freeing and therefore no TLB invalidation is performed. + */ +void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt, + u64 addr, u64 size); + +/** + * kvm_pgtable_stage2_destroy_pgd() - Destroy the PGD of guest stage-2 page-table. + * @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*(). + * + * It is assumed that the rest of the page-table is freed before this operation. + */ +void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt); + /** * kvm_pgtable_stage2_free_unlinked() - Free an unlinked stage-2 paging structure. * @mm_ops: Memory management callbacks. diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index ea58282f59bb..35f9d9478004 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -179,7 +179,9 @@ struct pkvm_mapping { int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu, struct kvm_pgtable_mm_ops *mm_ops); -void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt); +void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt, + u64 addr, u64 size); +void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt); int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys, enum kvm_pgtable_prot prot, void *mc, enum kvm_pgtable_walk_flags flags); diff --git a/arch/arm64/include/asm/kvm_ras.h b/arch/arm64/include/asm/kvm_ras.h deleted file mode 100644 index 9398ade632aa..000000000000 --- a/arch/arm64/include/asm/kvm_ras.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2018 - Arm Ltd */ - -#ifndef __ARM64_KVM_RAS_H__ -#define __ARM64_KVM_RAS_H__ - -#include -#include -#include - -#include - -/* - * Was this synchronous external abort a RAS notification? - * Returns '0' for errors handled by some RAS subsystem, or -ENOENT. - */ -static inline int kvm_handle_guest_sea(void) -{ - /* apei_claim_sea(NULL) expects to mask interrupts itself */ - lockdep_assert_irqs_enabled(); - - return apei_claim_sea(NULL); -} - -#endif /* __ARM64_KVM_RAS_H__ */ diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 717829df294e..5213248e081b 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -118,7 +118,7 @@ * VMAP'd stacks are allocated at page granularity, so we must ensure that such * stacks are a multiple of page size. */ -#if defined(CONFIG_VMAP_STACK) && (MIN_THREAD_SHIFT < PAGE_SHIFT) +#if (MIN_THREAD_SHIFT < PAGE_SHIFT) #define THREAD_SHIFT PAGE_SHIFT #else #define THREAD_SHIFT MIN_THREAD_SHIFT @@ -135,11 +135,7 @@ * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry * assembly. */ -#ifdef CONFIG_VMAP_STACK #define THREAD_ALIGN (2 * THREAD_SIZE) -#else -#define THREAD_ALIGN THREAD_SIZE -#endif #define IRQ_STACK_SIZE THREAD_SIZE diff --git a/arch/arm64/include/asm/mman.h b/arch/arm64/include/asm/mman.h index 21df8bbd2668..8770c7ee759f 100644 --- a/arch/arm64/include/asm/mman.h +++ b/arch/arm64/include/asm/mman.h @@ -11,10 +11,10 @@ #include #include -static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot, +static inline vm_flags_t arch_calc_vm_prot_bits(unsigned long prot, unsigned long pkey) { - unsigned long ret = 0; + vm_flags_t ret = 0; if (system_supports_bti() && (prot & PROT_BTI)) ret |= VM_ARM64_BTI; @@ -34,8 +34,8 @@ static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot, } #define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey) -static inline unsigned long arch_calc_vm_flag_bits(struct file *file, - unsigned long flags) +static inline vm_flags_t arch_calc_vm_flag_bits(struct file *file, + unsigned long flags) { /* * Only allow MTE on anonymous mappings as these are guaranteed to be @@ -68,7 +68,7 @@ static inline bool arch_validate_prot(unsigned long prot, } #define arch_validate_prot(prot, addr) arch_validate_prot(prot, addr) -static inline bool arch_validate_flags(unsigned long vm_flags) +static inline bool arch_validate_flags(vm_flags_t vm_flags) { if (system_supports_mte()) { /* diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 6e8aa8e72601..49f1a810df16 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -17,6 +17,13 @@ #include #include +enum pgtable_type { + TABLE_PTE, + TABLE_PMD, + TABLE_PUD, + TABLE_P4D, +}; + typedef struct { atomic64_t id; #ifdef CONFIG_COMPAT diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h index 79550b22ba19..fb9b88eebeb1 100644 --- a/arch/arm64/include/asm/module.h +++ b/arch/arm64/include/asm/module.h @@ -19,6 +19,7 @@ struct mod_arch_specific { /* for CONFIG_DYNAMIC_FTRACE */ struct plt_entry *ftrace_trampolines; + struct plt_entry *init_ftrace_trampolines; }; u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs, diff --git a/arch/arm64/include/asm/module.lds.h b/arch/arm64/include/asm/module.lds.h index b9ae8349e35d..fb944b46846d 100644 --- a/arch/arm64/include/asm/module.lds.h +++ b/arch/arm64/include/asm/module.lds.h @@ -2,6 +2,7 @@ SECTIONS { .plt 0 : { BYTE(0) } .init.plt 0 : { BYTE(0) } .text.ftrace_trampoline 0 : { BYTE(0) } + .init.text.ftrace_trampoline 0 : { BYTE(0) } #ifdef CONFIG_KASAN_SW_TAGS /* diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h index 7830d031742e..85dceb1c66f4 100644 --- a/arch/arm64/include/asm/pgtable-prot.h +++ b/arch/arm64/include/asm/pgtable-prot.h @@ -17,7 +17,6 @@ #define PTE_SWP_EXCLUSIVE (_AT(pteval_t, 1) << 2) /* only for swp ptes */ #define PTE_DIRTY (_AT(pteval_t, 1) << 55) #define PTE_SPECIAL (_AT(pteval_t, 1) << 56) -#define PTE_DEVMAP (_AT(pteval_t, 1) << 57) /* * PTE_PRESENT_INVALID=1 & PTE_VALID=0 indicates that the pte's fields should be diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 192d86e1cc76..abd2dee416b3 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -190,7 +190,6 @@ static inline pteval_t __phys_to_pte_val(phys_addr_t phys) #define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) #define pte_user_exec(pte) (!(pte_val(pte) & PTE_UXN)) #define pte_cont(pte) (!!(pte_val(pte) & PTE_CONT)) -#define pte_devmap(pte) (!!(pte_val(pte) & PTE_DEVMAP)) #define pte_tagged(pte) ((pte_val(pte) & PTE_ATTRINDX_MASK) == \ PTE_ATTRINDX(MT_NORMAL_TAGGED)) @@ -372,11 +371,6 @@ static inline pmd_t pmd_mkcont(pmd_t pmd) return __pmd(pmd_val(pmd) | PMD_SECT_CONT); } -static inline pte_t pte_mkdevmap(pte_t pte) -{ - return set_pte_bit(pte, __pgprot(PTE_DEVMAP | PTE_SPECIAL)); -} - #ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP static inline int pte_uffd_wp(pte_t pte) { @@ -653,14 +647,6 @@ static inline pmd_t pmd_mkhuge(pmd_t pmd) return __pmd((pmd_val(pmd) & ~mask) | val); } -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -#define pmd_devmap(pmd) pte_devmap(pmd_pte(pmd)) -#endif -static inline pmd_t pmd_mkdevmap(pmd_t pmd) -{ - return pte_pmd(set_pte_bit(pmd_pte(pmd), __pgprot(PTE_DEVMAP))); -} - #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP #define pmd_special(pte) (!!((pmd_val(pte) & PTE_SPECIAL))) static inline pmd_t pmd_mkspecial(pmd_t pmd) @@ -1302,16 +1288,6 @@ static inline int pmdp_set_access_flags(struct vm_area_struct *vma, return __ptep_set_access_flags(vma, address, (pte_t *)pmdp, pmd_pte(entry), dirty); } - -static inline int pud_devmap(pud_t pud) -{ - return 0; -} - -static inline int pgd_devmap(pgd_t pgd) -{ - return 0; -} #endif #ifdef CONFIG_PAGE_TABLE_CHECK @@ -1643,6 +1619,14 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf, */ #define arch_wants_old_prefaulted_pte cpu_has_hw_af +/* + * Request exec memory is read into pagecache in at least 64K folios. This size + * can be contpte-mapped when 4K base pages are in use (16 pages into 1 iTLB + * entry), and HPA can coalesce it (4 pages into 1 TLB entry) when 16K base + * pages are in use. + */ +#define exec_folio_order() ilog2(SZ_64K >> PAGE_SHIFT) + static inline bool pud_sect_supported(void) { return PAGE_SIZE == SZ_4K; @@ -1659,6 +1643,16 @@ extern void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t old_pte, pte_t new_pte); +#define modify_prot_start_ptes modify_prot_start_ptes +extern pte_t modify_prot_start_ptes(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + unsigned int nr); + +#define modify_prot_commit_ptes modify_prot_commit_ptes +extern void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep, pte_t old_pte, pte_t pte, + unsigned int nr); + #ifdef CONFIG_ARM64_CONTPTE /* diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 1bf1a3b16e88..61d62bfd5a7b 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -23,6 +23,8 @@ #define MTE_CTRL_TCF_ASYNC (1UL << 17) #define MTE_CTRL_TCF_ASYMM (1UL << 18) +#define MTE_CTRL_STORE_ONLY (1UL << 19) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 2510eec026f7..d48ef6d5abcc 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -50,10 +50,32 @@ struct seq_file; */ extern void smp_init_cpus(void); +enum ipi_msg_type { + IPI_RESCHEDULE, + IPI_CALL_FUNC, + IPI_CPU_STOP, + IPI_CPU_STOP_NMI, + IPI_TIMER, + IPI_IRQ_WORK, + NR_IPI, + /* + * Any enum >= NR_IPI and < MAX_IPI is special and not tracable + * with trace_ipi_* + */ + IPI_CPU_BACKTRACE = NR_IPI, + IPI_KGDB_ROUNDUP, + MAX_IPI +}; + /* * Register IPI interrupts with the arch SMP code */ -extern void set_smp_ipi_range(int ipi_base, int nr_ipi); +extern void set_smp_ipi_range_percpu(int ipi_base, int nr_ipi, int ncpus); + +static inline void set_smp_ipi_range(int ipi_base, int n) +{ + set_smp_ipi_range_percpu(ipi_base, n, 0); +} /* * Called from the secondary holding pen, this is the secondary CPU entry point. diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h index 66ec8caa6ac0..6d3280932bf5 100644 --- a/arch/arm64/include/asm/stacktrace.h +++ b/arch/arm64/include/asm/stacktrace.h @@ -59,7 +59,6 @@ static inline bool on_task_stack(const struct task_struct *tsk, #define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1)) -#ifdef CONFIG_VMAP_STACK DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack); static inline struct stack_info stackinfo_get_overflow(void) @@ -72,11 +71,8 @@ static inline struct stack_info stackinfo_get_overflow(void) .high = high, }; } -#else -#define stackinfo_get_overflow() stackinfo_get_unknown() -#endif -#if defined(CONFIG_ARM_SDE_INTERFACE) && defined(CONFIG_VMAP_STACK) +#if defined(CONFIG_ARM_SDE_INTERFACE) DECLARE_PER_CPU(unsigned long *, sdei_stack_normal_ptr); DECLARE_PER_CPU(unsigned long *, sdei_stack_critical_ptr); diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index f1bb0d10c39a..6604fd6f33f4 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -113,10 +113,14 @@ /* Register-based PAN access, for save/restore purposes */ #define SYS_PSTATE_PAN sys_reg(3, 0, 4, 2, 3) -#define __SYS_BARRIER_INSN(CRm, op2, Rt) \ - __emit_inst(0xd5000000 | sys_insn(0, 3, 3, (CRm), (op2)) | ((Rt) & 0x1f)) +#define __SYS_BARRIER_INSN(op0, op1, CRn, CRm, op2, Rt) \ + __emit_inst(0xd5000000 | \ + sys_insn((op0), (op1), (CRn), (CRm), (op2)) | \ + ((Rt) & 0x1f)) -#define SB_BARRIER_INSN __SYS_BARRIER_INSN(0, 7, 31) +#define SB_BARRIER_INSN __SYS_BARRIER_INSN(0, 3, 3, 0, 7, 31) +#define GSB_SYS_BARRIER_INSN __SYS_BARRIER_INSN(1, 0, 12, 0, 0, 31) +#define GSB_ACK_BARRIER_INSN __SYS_BARRIER_INSN(1, 0, 12, 0, 1, 31) /* Data cache zero operations */ #define SYS_DC_ISW sys_insn(1, 0, 7, 6, 2) @@ -202,16 +206,8 @@ #define SYS_DBGVCR32_EL2 sys_reg(2, 4, 0, 7, 0) #define SYS_BRBINF_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 0)) -#define SYS_BRBINFINJ_EL1 sys_reg(2, 1, 9, 1, 0) #define SYS_BRBSRC_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 1)) -#define SYS_BRBSRCINJ_EL1 sys_reg(2, 1, 9, 1, 1) #define SYS_BRBTGT_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 2)) -#define SYS_BRBTGTINJ_EL1 sys_reg(2, 1, 9, 1, 2) -#define SYS_BRBTS_EL1 sys_reg(2, 1, 9, 0, 2) - -#define SYS_BRBCR_EL1 sys_reg(2, 1, 9, 0, 0) -#define SYS_BRBFCR_EL1 sys_reg(2, 1, 9, 0, 1) -#define SYS_BRBIDR0_EL1 sys_reg(2, 1, 9, 2, 0) #define SYS_TRCITECR_EL1 sys_reg(3, 0, 1, 2, 3) #define SYS_TRCACATR(m) sys_reg(2, 1, 2, ((m & 7) << 1), (2 | (m >> 3))) @@ -277,8 +273,6 @@ /* ETM */ #define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4) -#define SYS_BRBCR_EL2 sys_reg(2, 4, 9, 0, 0) - #define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0) #define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5) #define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6) @@ -821,6 +815,12 @@ #define OP_COSP_RCTX sys_insn(1, 3, 7, 3, 6) #define OP_CPP_RCTX sys_insn(1, 3, 7, 3, 7) +/* + * BRBE Instructions + */ +#define BRB_IALL_INSN __emit_inst(0xd5000000 | OP_BRB_IALL | (0x1f)) +#define BRB_INJ_INSN __emit_inst(0xd5000000 | OP_BRB_INJ | (0x1f)) + /* Common SCTLR_ELx flags. */ #define SCTLR_ELx_ENTP2 (BIT(60)) #define SCTLR_ELx_DSSBS (BIT(44)) @@ -1078,12 +1078,70 @@ #define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \ GCS_CAP_VALID_TOKEN) +/* + * Definitions for GICv5 instructions + */ +#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) + +/* 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_GIC_CDIA_VALID_MASK BIT_ULL(32) +#define GICV5_GICR_CDIA_VALID(r) FIELD_GET(GICV5_GIC_CDIA_VALID_MASK, r) +#define GICV5_GIC_CDIA_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDIA_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 ARM64_FEATURE_FIELD_BITS 4 -/* Defined for compatibility only, do not add new users. */ -#define ARM64_FEATURE_MASK(x) (x##_MASK) - #ifdef __ASSEMBLY__ .macro mrs_s, rt, sreg diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h index c34344256762..344b1c1a4bbb 100644 --- a/arch/arm64/include/asm/system_misc.h +++ b/arch/arm64/include/asm/system_misc.h @@ -25,10 +25,6 @@ void arm64_notify_die(const char *str, struct pt_regs *regs, int signo, int sicode, unsigned long far, unsigned long err); -void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned long, - struct pt_regs *), - int sig, int code, const char *name); - struct mm_struct; extern void __show_regs(struct pt_regs *); diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 1269c2487574..f241b8601ebd 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -70,6 +70,7 @@ void arch_setup_new_exec(void); #define TIF_SYSCALL_TRACEPOINT 10 /* syscall tracepoint for ftrace */ #define TIF_SECCOMP 11 /* syscall secure computing */ #define TIF_SYSCALL_EMU 12 /* syscall emulation active */ +#define TIF_PATCH_PENDING 13 /* pending live patching update */ #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ #define TIF_FREEZE 19 #define TIF_RESTORE_SIGMASK 20 @@ -96,6 +97,7 @@ void arch_setup_new_exec(void); #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) +#define _TIF_PATCH_PENDING (1 << TIF_PATCH_PENDING) #define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) #define _TIF_32BIT (1 << TIF_32BIT) @@ -107,7 +109,8 @@ void arch_setup_new_exec(void); #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY | \ _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ _TIF_UPROBE | _TIF_MTE_ASYNC_FAULT | \ - _TIF_NOTIFY_SIGNAL | _TIF_SIGPENDING) + _TIF_NOTIFY_SIGNAL | _TIF_SIGPENDING | \ + _TIF_PATCH_PENDING) #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \ diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index aa9efee17277..18a5dc0c9a54 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -322,17 +322,6 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm) return true; } -/* - * If mprotect/munmap/etc occurs during TLB batched flushing, we need to ensure - * all the previously issued TLBIs targeting mm have completed. But since we - * can be executing on a remote CPU, a DSB cannot guarantee this like it can - * for arch_tlbbatch_flush(). Our only option is to flush the entire mm. - */ -static inline void arch_flush_tlb_batched_pending(struct mm_struct *mm) -{ - flush_tlb_mm(mm); -} - /* * To support TLB batched flush for multiple pages unmapping, we only send * the TLBI for each page in arch_tlbbatch_add_pending() and wait for the diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index 82cf1f879c61..e3e8944a71c3 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -29,6 +29,12 @@ void arm64_force_sig_fault_pkey(unsigned long far, const char *str, int pkey); void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str); void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str); +int bug_brk_handler(struct pt_regs *regs, unsigned long esr); +int cfi_brk_handler(struct pt_regs *regs, unsigned long esr); +int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr); +int kasan_brk_handler(struct pt_regs *regs, unsigned long esr); +int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr); + int early_brk64(unsigned long addr, unsigned long esr, struct pt_regs *regs); /* diff --git a/arch/arm64/include/asm/uprobes.h b/arch/arm64/include/asm/uprobes.h index 014b02897f8e..89bfb0213a50 100644 --- a/arch/arm64/include/asm/uprobes.h +++ b/arch/arm64/include/asm/uprobes.h @@ -28,4 +28,15 @@ struct arch_uprobe { bool simulate; }; +int uprobe_brk_handler(struct pt_regs *regs, unsigned long esr); +#ifdef CONFIG_UPROBES +int uprobe_single_step_handler(struct pt_regs *regs, unsigned long esr); +#else +static inline int uprobe_single_step_handler(struct pt_regs *regs, + unsigned long esr) +{ + return DBG_HOOK_ERROR; +} +#endif + #endif diff --git a/arch/arm64/include/asm/vdso/vsyscall.h b/arch/arm64/include/asm/vdso/vsyscall.h index de58951b8df6..417aae5763a8 100644 --- a/arch/arm64/include/asm/vdso/vsyscall.h +++ b/arch/arm64/include/asm/vdso/vsyscall.h @@ -13,12 +13,11 @@ * Update the vDSO data page to keep in sync with kernel timekeeping. */ static __always_inline -void __arm64_update_vsyscall(struct vdso_time_data *vdata) +void __arch_update_vdso_clock(struct vdso_clock *vc) { - vdata->clock_data[CS_HRES_COARSE].mask = VDSO_PRECISION_MASK; - vdata->clock_data[CS_RAW].mask = VDSO_PRECISION_MASK; + vc->mask = VDSO_PRECISION_MASK; } -#define __arch_update_vsyscall __arm64_update_vsyscall +#define __arch_update_vdso_clock __arch_update_vdso_clock /* The asm-generic header needs to be included after the definitions above */ #include diff --git a/arch/arm64/include/asm/vncr_mapping.h b/arch/arm64/include/asm/vncr_mapping.h index 6f556e993644..f6ec500ad3fa 100644 --- a/arch/arm64/include/asm/vncr_mapping.h +++ b/arch/arm64/include/asm/vncr_mapping.h @@ -51,6 +51,7 @@ #define VNCR_SP_EL1 0x240 #define VNCR_VBAR_EL1 0x250 #define VNCR_TCR2_EL1 0x270 +#define VNCR_SCTLR2_EL1 0x278 #define VNCR_PIRE0_EL1 0x290 #define VNCR_PIR_EL1 0x2A0 #define VNCR_POR_EL1 0x2A8 @@ -84,6 +85,7 @@ #define VNCR_ICH_HCR_EL2 0x4C0 #define VNCR_ICH_VMCR_EL2 0x4C8 #define VNCR_VDISR_EL2 0x500 +#define VNCR_VSESR_EL2 0x508 #define VNCR_PMBLIMITR_EL1 0x800 #define VNCR_PMBPTR_EL1 0x810 #define VNCR_PMBSR_EL1 0x820 diff --git a/arch/arm64/include/uapi/asm/bitsperlong.h b/arch/arm64/include/uapi/asm/bitsperlong.h index 485d60bee26c..d59730975f30 100644 --- a/arch/arm64/include/uapi/asm/bitsperlong.h +++ b/arch/arm64/include/uapi/asm/bitsperlong.h @@ -17,7 +17,12 @@ #ifndef __ASM_BITSPERLONG_H #define __ASM_BITSPERLONG_H +#if defined(__KERNEL__) && !defined(__aarch64__) +/* Used by the compat vDSO */ +#define __BITS_PER_LONG 32 +#else #define __BITS_PER_LONG 64 +#endif #include diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index 705a7afa8e58..72c78468b806 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -143,5 +143,7 @@ /* * HWCAP3 flags - for AT_HWCAP3 */ +#define HWCAP3_MTE_FAR (1UL << 0) +#define HWCAP3_MTE_STORE_ONLY (1UL << 1) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2920b0a51403..76f32e424065 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -34,7 +34,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ cpufeature.o alternative.o cacheinfo.o \ smp.o smp_spin_table.o topology.o smccc-call.o \ syscall.o proton-pack.o idle.o patching.o pi/ \ - rsi.o + rsi.o jump_label.o obj-$(CONFIG_COMPAT) += sys32.o signal32.o \ sys_compat.o @@ -47,7 +47,6 @@ obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF) += watchdog_hld.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_CPU_PM) += sleep.o suspend.o -obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_EFI) += efi.o efi-rt-wrapper.o obj-$(CONFIG_PCI) += pci.o @@ -81,7 +80,7 @@ obj-y += head.o always-$(KBUILD_BUILTIN) += vmlinux.lds ifeq ($(CONFIG_DEBUG_EFI),y) -AFLAGS_head.o += -DVMLINUX_PATH="\"$(realpath $(objtree)/vmlinux)\"" +AFLAGS_head.o += -DVMLINUX_PATH="\"$(abspath vmlinux)\"" endif # for cleaning diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index b9a66fc146c9..4d529ff7ba51 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -197,6 +197,8 @@ static int __init acpi_fadt_sanity_check(void) */ void __init acpi_boot_table_init(void) { + int ret; + /* * Enable ACPI instead of device tree unless * - ACPI has been disabled explicitly (acpi=off), or @@ -250,10 +252,12 @@ void __init acpi_boot_table_init(void) * behaviour, use acpi=nospcr to disable console in ACPI SPCR * table as default serial console. */ - acpi_parse_spcr(earlycon_acpi_spcr_enable, + ret = acpi_parse_spcr(earlycon_acpi_spcr_enable, !param_acpi_nospcr); - pr_info("Use ACPI SPCR as default console: %s\n", - param_acpi_nospcr ? "No" : "Yes"); + if (!ret || param_acpi_nospcr || !IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) + pr_info("Use ACPI SPCR as default console: No\n"); + else + pr_info("Use ACPI SPCR as default console: Yes\n"); if (IS_ENABLED(CONFIG_ACPI_BGRT)) acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index b34044e20128..ef269a5a37e1 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -303,6 +304,7 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = { }; static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_EL1_DF2_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_GCS), FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_EL1_GCS_SHIFT, 4, 0), S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_EL1_MTE_frac_SHIFT, 4, 0), @@ -320,6 +322,8 @@ 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_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, }; @@ -500,6 +504,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr3[] = { ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_POE), FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR3_EL1_S1POE_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR3_EL1_S1PIE_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR3_EL1_SCTLRX_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR3_EL1_TCRX_SHIFT, 4, 0), ARM64_FTR_END, }; @@ -1941,11 +1946,11 @@ static bool has_pmuv3(const struct arm64_cpu_capabilities *entry, int scope) extern void create_kpti_ng_temp_pgd(pgd_t *pgdir, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), int flags); + phys_addr_t (*pgtable_alloc)(enum pgtable_type), int flags); static phys_addr_t __initdata kpti_ng_temp_alloc; -static phys_addr_t __init kpti_ng_pgd_alloc(int shift) +static phys_addr_t __init kpti_ng_pgd_alloc(enum pgtable_type type) { kpti_ng_temp_alloc -= PAGE_SIZE; return kpti_ng_temp_alloc; @@ -2213,6 +2218,38 @@ static bool hvhe_possible(const struct arm64_cpu_capabilities *entry, return arm64_test_sw_feature_override(ARM64_SW_FEATURE_OVERRIDE_HVHE); } +static bool has_bbml2_noabort(const struct arm64_cpu_capabilities *caps, int scope) +{ + /* + * We want to allow usage of BBML2 in as wide a range of kernel contexts + * as possible. This list is therefore an allow-list of known-good + * implementations that both support BBML2 and additionally, fulfill the + * extra constraint of never generating TLB conflict aborts when using + * the relaxed BBML2 semantics (such aborts make use of BBML2 in certain + * kernel contexts difficult to prove safe against recursive aborts). + * + * Note that implementations can only be considered "known-good" if their + * implementors attest to the fact that the implementation never raises + * TLB conflict aborts for BBML2 mapping granularity changes. + */ + static const struct midr_range supports_bbml2_noabort_list[] = { + MIDR_REV_RANGE(MIDR_CORTEX_X4, 0, 3, 0xf), + MIDR_REV_RANGE(MIDR_NEOVERSE_V3, 0, 2, 0xf), + {} + }; + + /* Does our cpu guarantee to never raise TLB conflict aborts? */ + if (!is_midr_in_range_list(supports_bbml2_noabort_list)) + return false; + + /* + * We currently ignore the ID_AA64MMFR2_EL1 register, and only care + * about whether the MIDR check passes. + */ + + return true; +} + #ifdef CONFIG_ARM64_PAN static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused) { @@ -2233,6 +2270,24 @@ static void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused) /* Firmware may have left a deferred SError in this register. */ write_sysreg_s(0, SYS_DISR_EL1); } +static bool has_rasv1p1(const struct arm64_cpu_capabilities *__unused, int scope) +{ + const struct arm64_cpu_capabilities rasv1p1_caps[] = { + { + ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, RAS, V1P1) + }, + { + ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, RAS, IMP) + }, + { + ARM64_CPUID_FIELDS(ID_AA64PFR1_EL1, RAS_frac, RASv1p1) + }, + }; + + return (has_cpuid_feature(&rasv1p1_caps[0], scope) || + (has_cpuid_feature(&rasv1p1_caps[1], scope) && + has_cpuid_feature(&rasv1p1_caps[2], scope))); +} #endif /* CONFIG_ARM64_RAS_EXTN */ #ifdef CONFIG_ARM64_PTR_AUTH @@ -2296,11 +2351,11 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry, int scope) { /* - * ARM64_HAS_GIC_CPUIF_SYSREGS has a lower index, and is a boot CPU + * ARM64_HAS_GICV3_CPUIF has a lower index, and is a boot CPU * feature, so will be detected earlier. */ - BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_MASKING <= ARM64_HAS_GIC_CPUIF_SYSREGS); - if (!cpus_have_cap(ARM64_HAS_GIC_CPUIF_SYSREGS)) + BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_MASKING <= ARM64_HAS_GICV3_CPUIF); + if (!cpus_have_cap(ARM64_HAS_GICV3_CPUIF)) return false; return enable_pseudo_nmi; @@ -2496,8 +2551,8 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_always, }, { - .desc = "GIC system register CPU interface", - .capability = ARM64_HAS_GIC_CPUIF_SYSREGS, + .desc = "GICv3 CPU interface", + .capability = ARM64_HAS_GICV3_CPUIF, .type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE, .matches = has_useable_gicv3_cpuif, ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, GIC, IMP) @@ -2651,6 +2706,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .cpu_enable = cpu_clear_disr, ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, RAS, IMP) }, + { + .desc = "RASv1p1 Extension Support", + .capability = ARM64_HAS_RASV1P1_EXTN, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_rasv1p1, + }, #endif /* CONFIG_ARM64_RAS_EXTN */ #ifdef CONFIG_ARM64_AMU_EXTN { @@ -2874,6 +2935,20 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64PFR1_EL1, MTE, MTE3) }, + { + .desc = "FAR on MTE Tag Check Fault", + .capability = ARM64_MTE_FAR, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTEFAR, IMP) + }, + { + .desc = "Store Only MTE Tag Check", + .capability = ARM64_MTE_STORE_ONLY, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTESTOREONLY, IMP) + }, #endif /* CONFIG_ARM64_MTE */ { .desc = "RCpc load-acquire (LDAPR)", @@ -2980,6 +3055,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64MMFR2_EL1, EVT, IMP) }, + { + .desc = "BBM Level 2 without TLB conflict abort", + .capability = ARM64_HAS_BBML2_NOABORT, + .type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE, + .matches = has_bbml2_noabort, + }, { .desc = "52-bit Virtual Addressing for KVM (LPA2)", .capability = ARM64_HAS_LPA2, @@ -3061,6 +3142,20 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_pmuv3, }, #endif + { + .desc = "SCTLR2", + .capability = ARM64_HAS_SCTLR2, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64MMFR3_EL1, SCTLRX, IMP) + }, + { + .desc = "GICv5 CPU interface", + .type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE, + .capability = ARM64_HAS_GICV5_CPUIF, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, GCIE, IMP) + }, {}, }; @@ -3135,6 +3230,13 @@ static bool has_sve_feature(const struct arm64_cpu_capabilities *cap, int scope) } #endif +#ifdef CONFIG_ARM64_SME +static bool has_sme_feature(const struct arm64_cpu_capabilities *cap, int scope) +{ + return system_supports_sme() && has_user_cpuid_feature(cap, scope); +} +#endif + static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(ID_AA64ISAR0_EL1, AES, PMULL, CAP_HWCAP, KERNEL_HWCAP_PMULL), HWCAP_CAP(ID_AA64ISAR0_EL1, AES, AES, CAP_HWCAP, KERNEL_HWCAP_AES), @@ -3211,6 +3313,8 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { #ifdef CONFIG_ARM64_MTE HWCAP_CAP(ID_AA64PFR1_EL1, MTE, MTE2, CAP_HWCAP, KERNEL_HWCAP_MTE), HWCAP_CAP(ID_AA64PFR1_EL1, MTE, MTE3, CAP_HWCAP, KERNEL_HWCAP_MTE3), + HWCAP_CAP(ID_AA64PFR2_EL1, MTEFAR, IMP, CAP_HWCAP, KERNEL_HWCAP_MTE_FAR), + HWCAP_CAP(ID_AA64PFR2_EL1, MTESTOREONLY, IMP, CAP_HWCAP , KERNEL_HWCAP_MTE_STORE_ONLY), #endif /* CONFIG_ARM64_MTE */ HWCAP_CAP(ID_AA64MMFR0_EL1, ECV, IMP, CAP_HWCAP, KERNEL_HWCAP_ECV), HWCAP_CAP(ID_AA64MMFR1_EL1, AFP, IMP, CAP_HWCAP, KERNEL_HWCAP_AFP), @@ -3223,31 +3327,31 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(ID_AA64ISAR2_EL1, BC, IMP, CAP_HWCAP, KERNEL_HWCAP_HBC), #ifdef CONFIG_ARM64_SME HWCAP_CAP(ID_AA64PFR1_EL1, SME, IMP, CAP_HWCAP, KERNEL_HWCAP_SME), - HWCAP_CAP(ID_AA64SMFR0_EL1, FA64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_FA64), - HWCAP_CAP(ID_AA64SMFR0_EL1, LUTv2, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_LUTV2), - HWCAP_CAP(ID_AA64SMFR0_EL1, SMEver, SME2p2, CAP_HWCAP, KERNEL_HWCAP_SME2P2), - HWCAP_CAP(ID_AA64SMFR0_EL1, SMEver, SME2p1, CAP_HWCAP, KERNEL_HWCAP_SME2P1), - HWCAP_CAP(ID_AA64SMFR0_EL1, SMEver, SME2, CAP_HWCAP, KERNEL_HWCAP_SME2), - HWCAP_CAP(ID_AA64SMFR0_EL1, I16I64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I64), - HWCAP_CAP(ID_AA64SMFR0_EL1, F64F64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F64F64), - HWCAP_CAP(ID_AA64SMFR0_EL1, I16I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I32), - HWCAP_CAP(ID_AA64SMFR0_EL1, B16B16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16B16), - HWCAP_CAP(ID_AA64SMFR0_EL1, F16F16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F16), - HWCAP_CAP(ID_AA64SMFR0_EL1, F8F16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F8F16), - HWCAP_CAP(ID_AA64SMFR0_EL1, F8F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F8F32), - HWCAP_CAP(ID_AA64SMFR0_EL1, I8I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I8I32), - HWCAP_CAP(ID_AA64SMFR0_EL1, F16F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F32), - HWCAP_CAP(ID_AA64SMFR0_EL1, B16F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16F32), - HWCAP_CAP(ID_AA64SMFR0_EL1, BI32I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_BI32I32), - HWCAP_CAP(ID_AA64SMFR0_EL1, F32F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F32F32), - HWCAP_CAP(ID_AA64SMFR0_EL1, SF8FMA, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8FMA), - HWCAP_CAP(ID_AA64SMFR0_EL1, SF8DP4, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8DP4), - HWCAP_CAP(ID_AA64SMFR0_EL1, SF8DP2, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8DP2), - HWCAP_CAP(ID_AA64SMFR0_EL1, SBitPerm, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SBITPERM), - HWCAP_CAP(ID_AA64SMFR0_EL1, AES, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_AES), - HWCAP_CAP(ID_AA64SMFR0_EL1, SFEXPA, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SFEXPA), - HWCAP_CAP(ID_AA64SMFR0_EL1, STMOP, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_STMOP), - HWCAP_CAP(ID_AA64SMFR0_EL1, SMOP4, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SMOP4), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, FA64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_FA64), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, LUTv2, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_LUTV2), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2p2, CAP_HWCAP, KERNEL_HWCAP_SME2P2), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2p1, CAP_HWCAP, KERNEL_HWCAP_SME2P1), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2, CAP_HWCAP, KERNEL_HWCAP_SME2), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, I16I64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I64), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F64F64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F64F64), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, I16I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, B16B16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16B16), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F16F16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F16), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F8F16, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F8F16), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F8F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F8F32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, I8I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I8I32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F16F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, B16F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16F32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, BI32I32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_BI32I32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, F32F32, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F32F32), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SF8FMA, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8FMA), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SF8DP4, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8DP4), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SF8DP2, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SF8DP2), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SBitPerm, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SBITPERM), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, AES, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_AES), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SFEXPA, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SFEXPA), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, STMOP, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_STMOP), + HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMOP4, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_SMOP4), #endif /* CONFIG_ARM64_SME */ HWCAP_CAP(ID_AA64FPFR0_EL1, F8CVT, IMP, CAP_HWCAP, KERNEL_HWCAP_F8CVT), HWCAP_CAP(ID_AA64FPFR0_EL1, F8FMA, IMP, CAP_HWCAP, KERNEL_HWCAP_F8FMA), @@ -3370,18 +3474,49 @@ static void update_cpu_capabilities(u16 scope_mask) scope_mask &= ARM64_CPUCAP_SCOPE_MASK; for (i = 0; i < ARM64_NCAPS; i++) { + bool match_all = false; + bool caps_set = false; + bool boot_cpu = false; + caps = cpucap_ptrs[i]; - if (!caps || !(caps->type & scope_mask) || - cpus_have_cap(caps->capability) || - !caps->matches(caps, cpucap_default_scope(caps))) + if (!caps || !(caps->type & scope_mask)) continue; - if (caps->desc && !caps->cpus) + match_all = cpucap_match_all_early_cpus(caps); + caps_set = cpus_have_cap(caps->capability); + boot_cpu = scope_mask & SCOPE_BOOT_CPU; + + /* + * Unless it's a match-all CPUs feature, avoid probing if + * already detected. + */ + if (!match_all && caps_set) + continue; + + /* + * A match-all CPUs capability is only set when probing the + * boot CPU. It may be cleared subsequently if not detected on + * secondary ones. + */ + if (match_all && !caps_set && !boot_cpu) + continue; + + if (!caps->matches(caps, cpucap_default_scope(caps))) { + if (match_all) + __clear_bit(caps->capability, system_cpucaps); + continue; + } + + /* + * Match-all CPUs capabilities are logged later when the + * system capabilities are finalised. + */ + if (!match_all && caps->desc && !caps->cpus) pr_info("detected: %s\n", caps->desc); __set_bit(caps->capability, system_cpucaps); - if ((scope_mask & SCOPE_BOOT_CPU) && (caps->type & SCOPE_BOOT_CPU)) + if (boot_cpu && (caps->type & SCOPE_BOOT_CPU)) set_bit(caps->capability, boot_cpucaps); } } @@ -3782,17 +3917,24 @@ static void __init setup_system_capabilities(void) enable_cpu_capabilities(SCOPE_ALL & ~SCOPE_BOOT_CPU); apply_alternatives_all(); - /* - * Log any cpucaps with a cpumask as these aren't logged by - * update_cpu_capabilities(). - */ for (int i = 0; i < ARM64_NCAPS; i++) { const struct arm64_cpu_capabilities *caps = cpucap_ptrs[i]; - if (caps && caps->cpus && caps->desc && - cpumask_any(caps->cpus) < nr_cpu_ids) + if (!caps || !caps->desc) + continue; + + /* + * Log any cpucaps with a cpumask as these aren't logged by + * update_cpu_capabilities(). + */ + if (caps->cpus && cpumask_any(caps->cpus) < nr_cpu_ids) pr_info("detected: %s on CPU%*pbl\n", caps->desc, cpumask_pr_args(caps->cpus)); + + /* Log match-all CPUs capabilities */ + if (cpucap_match_all_early_cpus(caps) && + cpus_have_cap(caps->capability)) + pr_info("detected: %s\n", caps->desc); } /* diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index c1f2b6b04b41..ba834909a28b 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -160,6 +160,8 @@ static const char *const hwcap_str[] = { [KERNEL_HWCAP_SME_SFEXPA] = "smesfexpa", [KERNEL_HWCAP_SME_STMOP] = "smestmop", [KERNEL_HWCAP_SME_SMOP4] = "smesmop4", + [KERNEL_HWCAP_MTE_FAR] = "mtefar", + [KERNEL_HWCAP_MTE_STORE_ONLY] = "mtestoreonly", }; #ifdef CONFIG_COMPAT diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index 58f047de3e1c..110d9ff54174 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -21,8 +21,12 @@ #include #include #include +#include +#include +#include #include #include +#include /* Determine debug architecture. */ u8 debug_monitors_arch(void) @@ -34,7 +38,7 @@ u8 debug_monitors_arch(void) /* * MDSCR access routines. */ -static void mdscr_write(u32 mdscr) +static void mdscr_write(u64 mdscr) { unsigned long flags; flags = local_daif_save(); @@ -43,7 +47,7 @@ static void mdscr_write(u32 mdscr) } NOKPROBE_SYMBOL(mdscr_write); -static u32 mdscr_read(void) +static u64 mdscr_read(void) { return read_sysreg(mdscr_el1); } @@ -79,16 +83,16 @@ static DEFINE_PER_CPU(int, kde_ref_count); void enable_debug_monitors(enum dbg_active_el el) { - u32 mdscr, enable = 0; + u64 mdscr, enable = 0; WARN_ON(preemptible()); if (this_cpu_inc_return(mde_ref_count) == 1) - enable = DBG_MDSCR_MDE; + enable = MDSCR_EL1_MDE; if (el == DBG_ACTIVE_EL1 && this_cpu_inc_return(kde_ref_count) == 1) - enable |= DBG_MDSCR_KDE; + enable |= MDSCR_EL1_KDE; if (enable && debug_enabled) { mdscr = mdscr_read(); @@ -100,16 +104,16 @@ NOKPROBE_SYMBOL(enable_debug_monitors); void disable_debug_monitors(enum dbg_active_el el) { - u32 mdscr, disable = 0; + u64 mdscr, disable = 0; WARN_ON(preemptible()); if (this_cpu_dec_return(mde_ref_count) == 0) - disable = ~DBG_MDSCR_MDE; + disable = ~MDSCR_EL1_MDE; if (el == DBG_ACTIVE_EL1 && this_cpu_dec_return(kde_ref_count) == 0) - disable &= ~DBG_MDSCR_KDE; + disable &= ~MDSCR_EL1_KDE; if (disable) { mdscr = mdscr_read(); @@ -156,74 +160,6 @@ NOKPROBE_SYMBOL(clear_user_regs_spsr_ss); #define set_regs_spsr_ss(r) set_user_regs_spsr_ss(&(r)->user_regs) #define clear_regs_spsr_ss(r) clear_user_regs_spsr_ss(&(r)->user_regs) -static DEFINE_SPINLOCK(debug_hook_lock); -static LIST_HEAD(user_step_hook); -static LIST_HEAD(kernel_step_hook); - -static void register_debug_hook(struct list_head *node, struct list_head *list) -{ - spin_lock(&debug_hook_lock); - list_add_rcu(node, list); - spin_unlock(&debug_hook_lock); - -} - -static void unregister_debug_hook(struct list_head *node) -{ - spin_lock(&debug_hook_lock); - list_del_rcu(node); - spin_unlock(&debug_hook_lock); - synchronize_rcu(); -} - -void register_user_step_hook(struct step_hook *hook) -{ - register_debug_hook(&hook->node, &user_step_hook); -} - -void unregister_user_step_hook(struct step_hook *hook) -{ - unregister_debug_hook(&hook->node); -} - -void register_kernel_step_hook(struct step_hook *hook) -{ - register_debug_hook(&hook->node, &kernel_step_hook); -} - -void unregister_kernel_step_hook(struct step_hook *hook) -{ - unregister_debug_hook(&hook->node); -} - -/* - * Call registered single step handlers - * There is no Syndrome info to check for determining the handler. - * So we call all the registered handlers, until the right handler is - * found which returns zero. - */ -static int call_step_hook(struct pt_regs *regs, unsigned long esr) -{ - struct step_hook *hook; - struct list_head *list; - int retval = DBG_HOOK_ERROR; - - list = user_mode(regs) ? &user_step_hook : &kernel_step_hook; - - /* - * Since single-step exception disables interrupt, this function is - * entirely not preemptible, and we can use rcu list safely here. - */ - list_for_each_entry_rcu(hook, list, node) { - retval = hook->fn(regs, esr); - if (retval == DBG_HOOK_HANDLED) - break; - } - - return retval; -} -NOKPROBE_SYMBOL(call_step_hook); - static void send_user_sigtrap(int si_code) { struct pt_regs *regs = current_pt_regs(); @@ -238,105 +174,110 @@ static void send_user_sigtrap(int si_code) "User debug trap"); } -static int single_step_handler(unsigned long unused, unsigned long esr, - struct pt_regs *regs) +/* + * We have already unmasked interrupts and enabled preemption + * when calling do_el0_softstep() from entry-common.c. + */ +void do_el0_softstep(unsigned long esr, struct pt_regs *regs) { - bool handler_found = false; + if (uprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED) + return; + send_user_sigtrap(TRAP_TRACE); /* - * If we are stepping a pending breakpoint, call the hw_breakpoint - * handler first. + * ptrace will disable single step unless explicitly + * asked to re-enable it. For other clients, it makes + * sense to leave it enabled (i.e. rewind the controls + * to the active-not-pending state). */ - if (!reinstall_suspended_bps(regs)) - return 0; + user_rewind_single_step(current); +} - if (!handler_found && call_step_hook(regs, esr) == DBG_HOOK_HANDLED) - handler_found = true; +void do_el1_softstep(unsigned long esr, struct pt_regs *regs) +{ + if (kgdb_single_step_handler(regs, esr) == DBG_HOOK_HANDLED) + return; - if (!handler_found && user_mode(regs)) { - send_user_sigtrap(TRAP_TRACE); + pr_warn("Unexpected kernel single-step exception at EL1\n"); + /* + * Re-enable stepping since we know that we will be + * returning to regs. + */ + set_regs_spsr_ss(regs); +} +NOKPROBE_SYMBOL(do_el1_softstep); - /* - * ptrace will disable single step unless explicitly - * asked to re-enable it. For other clients, it makes - * sense to leave it enabled (i.e. rewind the controls - * to the active-not-pending state). - */ - user_rewind_single_step(current); - } else if (!handler_found) { - pr_warn("Unexpected kernel single-step exception at EL1\n"); - /* - * Re-enable stepping since we know that we will be - * returning to regs. - */ - set_regs_spsr_ss(regs); +static int call_el1_break_hook(struct pt_regs *regs, unsigned long esr) +{ + if (esr_brk_comment(esr) == BUG_BRK_IMM) + return bug_brk_handler(regs, esr); + + if (IS_ENABLED(CONFIG_CFI_CLANG) && esr_is_cfi_brk(esr)) + return cfi_brk_handler(regs, esr); + + if (esr_brk_comment(esr) == FAULT_BRK_IMM) + return reserved_fault_brk_handler(regs, esr); + + if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) && + (esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) + return kasan_brk_handler(regs, esr); + + if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr)) + return ubsan_brk_handler(regs, esr); + + if (IS_ENABLED(CONFIG_KGDB)) { + if (esr_brk_comment(esr) == KGDB_DYN_DBG_BRK_IMM) + return kgdb_brk_handler(regs, esr); + if (esr_brk_comment(esr) == KGDB_COMPILED_DBG_BRK_IMM) + return kgdb_compiled_brk_handler(regs, esr); } - return 0; -} -NOKPROBE_SYMBOL(single_step_handler); - -static LIST_HEAD(user_break_hook); -static LIST_HEAD(kernel_break_hook); - -void register_user_break_hook(struct break_hook *hook) -{ - register_debug_hook(&hook->node, &user_break_hook); -} - -void unregister_user_break_hook(struct break_hook *hook) -{ - unregister_debug_hook(&hook->node); -} - -void register_kernel_break_hook(struct break_hook *hook) -{ - register_debug_hook(&hook->node, &kernel_break_hook); -} - -void unregister_kernel_break_hook(struct break_hook *hook) -{ - unregister_debug_hook(&hook->node); -} - -static int call_break_hook(struct pt_regs *regs, unsigned long esr) -{ - struct break_hook *hook; - struct list_head *list; - - list = user_mode(regs) ? &user_break_hook : &kernel_break_hook; - - /* - * Since brk exception disables interrupt, this function is - * entirely not preemptible, and we can use rcu list safely here. - */ - list_for_each_entry_rcu(hook, list, node) { - if ((esr_brk_comment(esr) & ~hook->mask) == hook->imm) - return hook->fn(regs, esr); + if (IS_ENABLED(CONFIG_KPROBES)) { + if (esr_brk_comment(esr) == KPROBES_BRK_IMM) + return kprobe_brk_handler(regs, esr); + if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM) + return kprobe_ss_brk_handler(regs, esr); } + if (IS_ENABLED(CONFIG_KRETPROBES) && + esr_brk_comment(esr) == KRETPROBES_BRK_IMM) + return kretprobe_brk_handler(regs, esr); + return DBG_HOOK_ERROR; } -NOKPROBE_SYMBOL(call_break_hook); +NOKPROBE_SYMBOL(call_el1_break_hook); -static int brk_handler(unsigned long unused, unsigned long esr, - struct pt_regs *regs) +/* + * We have already unmasked interrupts and enabled preemption + * when calling do_el0_brk64() from entry-common.c. + */ +void do_el0_brk64(unsigned long esr, struct pt_regs *regs) { - if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED) - return 0; + if (IS_ENABLED(CONFIG_UPROBES) && + esr_brk_comment(esr) == UPROBES_BRK_IMM && + uprobe_brk_handler(regs, esr) == DBG_HOOK_HANDLED) + return; - if (user_mode(regs)) { - send_user_sigtrap(TRAP_BRKPT); - } else { - pr_warn("Unexpected kernel BRK exception at EL1\n"); - return -EFAULT; - } - - return 0; + send_user_sigtrap(TRAP_BRKPT); } -NOKPROBE_SYMBOL(brk_handler); -int aarch32_break_handler(struct pt_regs *regs) +void do_el1_brk64(unsigned long esr, struct pt_regs *regs) +{ + if (call_el1_break_hook(regs, esr) == DBG_HOOK_HANDLED) + return; + + die("Oops - BRK", regs, esr); +} +NOKPROBE_SYMBOL(do_el1_brk64); + +#ifdef CONFIG_COMPAT +void do_bkpt32(unsigned long esr, struct pt_regs *regs) +{ + arm64_notify_die("aarch32 BKPT", regs, SIGTRAP, TRAP_BRKPT, regs->pc, esr); +} +#endif /* CONFIG_COMPAT */ + +bool try_handle_aarch32_break(struct pt_regs *regs) { u32 arm_instr; u16 thumb_instr; @@ -344,7 +285,7 @@ int aarch32_break_handler(struct pt_regs *regs) void __user *pc = (void __user *)instruction_pointer(regs); if (!compat_user_mode(regs)) - return -EFAULT; + return false; if (compat_thumb_mode(regs)) { /* get 16-bit Thumb instruction */ @@ -368,20 +309,12 @@ int aarch32_break_handler(struct pt_regs *regs) } if (!bp) - return -EFAULT; + return false; send_user_sigtrap(TRAP_BRKPT); - return 0; -} -NOKPROBE_SYMBOL(aarch32_break_handler); - -void __init debug_traps_init(void) -{ - hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP, - TRAP_TRACE, "single-step handler"); - hook_debug_fault_code(DBG_ESR_EVT_BRK, brk_handler, SIGTRAP, - TRAP_BRKPT, "BRK handler"); + return true; } +NOKPROBE_SYMBOL(try_handle_aarch32_break); /* Re-enable single step for syscall restarting. */ void user_rewind_single_step(struct task_struct *task) @@ -415,7 +348,7 @@ void kernel_enable_single_step(struct pt_regs *regs) { WARN_ON(!irqs_disabled()); set_regs_spsr_ss(regs); - mdscr_write(mdscr_read() | DBG_MDSCR_SS); + mdscr_write(mdscr_read() | MDSCR_EL1_SS); enable_debug_monitors(DBG_ACTIVE_EL1); } NOKPROBE_SYMBOL(kernel_enable_single_step); @@ -423,7 +356,7 @@ NOKPROBE_SYMBOL(kernel_enable_single_step); void kernel_disable_single_step(void) { WARN_ON(!irqs_disabled()); - mdscr_write(mdscr_read() & ~DBG_MDSCR_SS); + mdscr_write(mdscr_read() & ~MDSCR_EL1_SS); disable_debug_monitors(DBG_ACTIVE_EL1); } NOKPROBE_SYMBOL(kernel_disable_single_step); @@ -431,7 +364,7 @@ NOKPROBE_SYMBOL(kernel_disable_single_step); int kernel_active_single_step(void) { WARN_ON(!irqs_disabled()); - return mdscr_read() & DBG_MDSCR_SS; + return mdscr_read() & MDSCR_EL1_SS; } NOKPROBE_SYMBOL(kernel_active_single_step); diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 3857fd7ee8d4..6c371b158b99 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -15,6 +15,7 @@ #include #include +#include static bool region_is_misaligned(const efi_memory_desc_t *md) { @@ -214,9 +215,8 @@ static int __init arm64_efi_rt_init(void) if (!efi_enabled(EFI_RUNTIME_SERVICES)) return 0; - p = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, GFP_KERNEL, - NUMA_NO_NODE, &&l); -l: if (!p) { + p = arch_alloc_vmap_stack(THREAD_SIZE, NUMA_NO_NODE); + if (!p) { pr_warn("Failed to allocate EFI runtime stack\n"); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return -ENOMEM; diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c index 7c1970b341b8..2b0c5925502e 100644 --- a/arch/arm64/kernel/entry-common.c +++ b/arch/arm64/kernel/entry-common.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,9 @@ static void do_notify_resume(struct pt_regs *regs, unsigned long thread_flags) (void __user *)NULL, current); } + if (thread_flags & _TIF_PATCH_PENDING) + klp_update_patch_state(current); + if (thread_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) do_signal(regs); @@ -344,7 +348,7 @@ static DEFINE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa); static void cortex_a76_erratum_1463225_svc_handler(void) { - u32 reg, val; + u64 reg, val; if (!unlikely(test_thread_flag(TIF_SINGLESTEP))) return; @@ -354,7 +358,7 @@ static void cortex_a76_erratum_1463225_svc_handler(void) __this_cpu_write(__in_cortex_a76_erratum_1463225_wa, 1); reg = read_sysreg(mdscr_el1); - val = reg | DBG_MDSCR_SS | DBG_MDSCR_KDE; + val = reg | MDSCR_EL1_SS | MDSCR_EL1_KDE; write_sysreg(val, mdscr_el1); asm volatile("msr daifclr, #8"); isb(); @@ -441,6 +445,28 @@ static __always_inline void fpsimd_syscall_exit(void) __this_cpu_write(fpsimd_last_state.to_save, FP_STATE_CURRENT); } +/* + * In debug exception context, we explicitly disable preemption despite + * having interrupts disabled. + * This serves two purposes: it makes it much less likely that we would + * accidentally schedule in exception context and it will force a warning + * if we somehow manage to schedule by accident. + */ +static void debug_exception_enter(struct pt_regs *regs) +{ + preempt_disable(); + + /* This code is a bit fragile. Test it. */ + RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work"); +} +NOKPROBE_SYMBOL(debug_exception_enter); + +static void debug_exception_exit(struct pt_regs *regs) +{ + preempt_enable_no_resched(); +} +NOKPROBE_SYMBOL(debug_exception_exit); + UNHANDLED(el1t, 64, sync) UNHANDLED(el1t, 64, irq) UNHANDLED(el1t, 64, fiq) @@ -504,13 +530,51 @@ static void noinstr el1_mops(struct pt_regs *regs, unsigned long esr) exit_to_kernel_mode(regs); } -static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr) +static void noinstr el1_breakpt(struct pt_regs *regs, unsigned long esr) { + arm64_enter_el1_dbg(regs); + debug_exception_enter(regs); + do_breakpoint(esr, regs); + debug_exception_exit(regs); + arm64_exit_el1_dbg(regs); +} + +static void noinstr el1_softstp(struct pt_regs *regs, unsigned long esr) +{ + arm64_enter_el1_dbg(regs); + if (!cortex_a76_erratum_1463225_debug_handler(regs)) { + debug_exception_enter(regs); + /* + * After handling a breakpoint, we suspend the breakpoint + * and use single-step to move to the next instruction. + * If we are stepping a suspended breakpoint there's nothing more to do: + * the single-step is complete. + */ + if (!try_step_suspended_breakpoints(regs)) + do_el1_softstep(esr, regs); + debug_exception_exit(regs); + } + arm64_exit_el1_dbg(regs); +} + +static void noinstr el1_watchpt(struct pt_regs *regs, unsigned long esr) +{ + /* Watchpoints are the only debug exception to write FAR_EL1 */ unsigned long far = read_sysreg(far_el1); arm64_enter_el1_dbg(regs); - if (!cortex_a76_erratum_1463225_debug_handler(regs)) - do_debug_exception(far, esr, regs); + debug_exception_enter(regs); + do_watchpoint(far, esr, regs); + debug_exception_exit(regs); + arm64_exit_el1_dbg(regs); +} + +static void noinstr el1_brk64(struct pt_regs *regs, unsigned long esr) +{ + arm64_enter_el1_dbg(regs); + debug_exception_enter(regs); + do_el1_brk64(esr, regs); + debug_exception_exit(regs); arm64_exit_el1_dbg(regs); } @@ -553,10 +617,16 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs) el1_mops(regs, esr); break; case ESR_ELx_EC_BREAKPT_CUR: + el1_breakpt(regs, esr); + break; case ESR_ELx_EC_SOFTSTP_CUR: + el1_softstp(regs, esr); + break; case ESR_ELx_EC_WATCHPT_CUR: + el1_watchpt(regs, esr); + break; case ESR_ELx_EC_BRK64: - el1_dbg(regs, esr); + el1_brk64(regs, esr); break; case ESR_ELx_EC_FPAC: el1_fpac(regs, esr); @@ -747,17 +817,59 @@ static void noinstr el0_inv(struct pt_regs *regs, unsigned long esr) exit_to_user_mode(regs); } -static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr) +static void noinstr el0_breakpt(struct pt_regs *regs, unsigned long esr) { - /* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */ + if (!is_ttbr0_addr(regs->pc)) + arm64_apply_bp_hardening(); + + enter_from_user_mode(regs); + debug_exception_enter(regs); + do_breakpoint(esr, regs); + debug_exception_exit(regs); + local_daif_restore(DAIF_PROCCTX); + exit_to_user_mode(regs); +} + +static void noinstr el0_softstp(struct pt_regs *regs, unsigned long esr) +{ + if (!is_ttbr0_addr(regs->pc)) + arm64_apply_bp_hardening(); + + enter_from_user_mode(regs); + /* + * After handling a breakpoint, we suspend the breakpoint + * and use single-step to move to the next instruction. + * If we are stepping a suspended breakpoint there's nothing more to do: + * the single-step is complete. + */ + if (!try_step_suspended_breakpoints(regs)) { + local_daif_restore(DAIF_PROCCTX); + do_el0_softstep(esr, regs); + } + exit_to_user_mode(regs); +} + +static void noinstr el0_watchpt(struct pt_regs *regs, unsigned long esr) +{ + /* Watchpoints are the only debug exception to write FAR_EL1 */ unsigned long far = read_sysreg(far_el1); enter_from_user_mode(regs); - do_debug_exception(far, esr, regs); + debug_exception_enter(regs); + do_watchpoint(far, esr, regs); + debug_exception_exit(regs); local_daif_restore(DAIF_PROCCTX); exit_to_user_mode(regs); } +static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr) +{ + enter_from_user_mode(regs); + local_daif_restore(DAIF_PROCCTX); + do_el0_brk64(esr, regs); + exit_to_user_mode(regs); +} + static void noinstr el0_svc(struct pt_regs *regs) { enter_from_user_mode(regs); @@ -826,10 +938,16 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs) el0_gcs(regs, esr); break; case ESR_ELx_EC_BREAKPT_LOW: + el0_breakpt(regs, esr); + break; case ESR_ELx_EC_SOFTSTP_LOW: + el0_softstp(regs, esr); + break; case ESR_ELx_EC_WATCHPT_LOW: + el0_watchpt(regs, esr); + break; case ESR_ELx_EC_BRK64: - el0_dbg(regs, esr); + el0_brk64(regs, esr); break; case ESR_ELx_EC_FPAC: el0_fpac(regs, esr); @@ -912,6 +1030,14 @@ static void noinstr el0_svc_compat(struct pt_regs *regs) exit_to_user_mode(regs); } +static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr) +{ + enter_from_user_mode(regs); + local_daif_restore(DAIF_PROCCTX); + do_bkpt32(esr, regs); + exit_to_user_mode(regs); +} + asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs) { unsigned long esr = read_sysreg(esr_el1); @@ -946,10 +1072,16 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs) el0_cp15(regs, esr); break; case ESR_ELx_EC_BREAKPT_LOW: + el0_breakpt(regs, esr); + break; case ESR_ELx_EC_SOFTSTP_LOW: + el0_softstp(regs, esr); + break; case ESR_ELx_EC_WATCHPT_LOW: + el0_watchpt(regs, esr); + break; case ESR_ELx_EC_BKPT32: - el0_dbg(regs, esr); + el0_bkpt32(regs, esr); break; default: el0_inv(regs, esr); @@ -977,7 +1109,6 @@ UNHANDLED(el0t, 32, fiq) UNHANDLED(el0t, 32, error) #endif /* CONFIG_COMPAT */ -#ifdef CONFIG_VMAP_STACK asmlinkage void noinstr __noreturn handle_bad_stack(struct pt_regs *regs) { unsigned long esr = read_sysreg(esr_el1); @@ -986,7 +1117,6 @@ asmlinkage void noinstr __noreturn handle_bad_stack(struct pt_regs *regs) arm64_enter_nmi(regs); panic_bad_stack(regs, esr, far); } -#endif /* CONFIG_VMAP_STACK */ #ifdef CONFIG_ARM_SDE_INTERFACE asmlinkage noinstr unsigned long diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 5ae2a34b50bd..f8018b5c1f9a 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -55,7 +55,6 @@ .endif sub sp, sp, #PT_REGS_SIZE -#ifdef CONFIG_VMAP_STACK /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) @@ -97,7 +96,6 @@ /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 -#endif b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm @@ -540,7 +538,6 @@ SYM_CODE_START(vectors) kernel_ventry 0, t, 32, error // Error 32-bit EL0 SYM_CODE_END(vectors) -#ifdef CONFIG_VMAP_STACK SYM_CODE_START_LOCAL(__bad_stack) /* * We detected an overflow in kernel_ventry, which switched to the @@ -568,7 +565,6 @@ SYM_CODE_START_LOCAL(__bad_stack) bl handle_bad_stack ASM_BUG() SYM_CODE_END(__bad_stack) -#endif /* CONFIG_VMAP_STACK */ .macro entry_handler el:req, ht:req, regsize:req, label:req @@ -614,7 +610,7 @@ SYM_CODE_END(ret_to_kernel) SYM_CODE_START_LOCAL(ret_to_user) ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step enable_step_tsk x19, x2 -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE bl stackleak_erase_on_task_stack #endif kernel_exit 0 @@ -825,6 +821,7 @@ SYM_CODE_END(__bp_harden_el1_vectors) * */ SYM_FUNC_START(cpu_switch_to) + save_and_disable_daif x11 mov x10, #THREAD_CPU_CONTEXT add x8, x0, x10 mov x9, sp @@ -848,6 +845,7 @@ SYM_FUNC_START(cpu_switch_to) ptrauth_keys_install_kernel x1, x8, x9, x10 scs_save x0 scs_load_current + restore_irq x11 ret SYM_FUNC_END(cpu_switch_to) NOKPROBE(cpu_switch_to) @@ -874,6 +872,7 @@ NOKPROBE(ret_from_fork) * Calls func(regs) using this CPU's irq stack and shadow irq stack. */ SYM_FUNC_START(call_on_irq_stack) + save_and_disable_daif x9 #ifdef CONFIG_SHADOW_CALL_STACK get_current_task x16 scs_save x16 @@ -888,8 +887,10 @@ SYM_FUNC_START(call_on_irq_stack) /* Move to the new stack and call the function there */ add sp, x16, #IRQ_STACK_SIZE + restore_irq x9 blr x1 + save_and_disable_daif x9 /* * Restore the SP from the FP, and restore the FP and LR from the frame * record. @@ -897,6 +898,7 @@ SYM_FUNC_START(call_on_irq_stack) mov sp, x29 ldp x29, x30, [sp], #16 scs_load_current + restore_irq x9 ret SYM_FUNC_END(call_on_irq_stack) NOKPROBE(call_on_irq_stack) @@ -1003,7 +1005,6 @@ SYM_CODE_START(__sdei_asm_handler) 1: adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6 2: str x19, [x5] -#ifdef CONFIG_VMAP_STACK /* * entry.S may have been using sp as a scratch register, find whether * this is a normal or critical event and switch to the appropriate @@ -1016,7 +1017,6 @@ SYM_CODE_START(__sdei_asm_handler) 2: mov x6, #SDEI_STACK_SIZE add x5, x5, x6 mov sp, x5 -#endif #ifdef CONFIG_SHADOW_CALL_STACK /* Use a separate shadow call stack for normal and critical events */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index 5a890714ee2e..5adad37ab4fa 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -258,10 +258,17 @@ int ftrace_update_ftrace_func(ftrace_func_t func) return ftrace_modify_code(pc, 0, new, false); } -static struct plt_entry *get_ftrace_plt(struct module *mod) +static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr) { #ifdef CONFIG_MODULES - struct plt_entry *plt = mod->arch.ftrace_trampolines; + struct plt_entry *plt = NULL; + + if (within_module_mem_type(addr, mod, MOD_INIT_TEXT)) + plt = mod->arch.init_ftrace_trampolines; + else if (within_module_mem_type(addr, mod, MOD_TEXT)) + plt = mod->arch.ftrace_trampolines; + else + return NULL; return &plt[FTRACE_PLT_IDX]; #else @@ -332,7 +339,7 @@ static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, if (WARN_ON(!mod)) return false; - plt = get_ftrace_plt(mod); + plt = get_ftrace_plt(mod, pc); if (!plt) { pr_err("ftrace: no module PLT for %ps\n", (void *)*addr); return false; diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 722ac45f9f7b..ab76b36dce82 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -618,8 +619,7 @@ NOKPROBE_SYMBOL(toggle_bp_registers); /* * Debug exception handlers. */ -static int breakpoint_handler(unsigned long unused, unsigned long esr, - struct pt_regs *regs) +void do_breakpoint(unsigned long esr, struct pt_regs *regs) { int i, step = 0, *kernel_step; u32 ctrl_reg; @@ -662,7 +662,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr, } if (!step) - return 0; + return; if (user_mode(regs)) { debug_info->bps_disabled = 1; @@ -670,7 +670,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr, /* If we're already stepping a watchpoint, just return. */ if (debug_info->wps_disabled) - return 0; + return; if (test_thread_flag(TIF_SINGLESTEP)) debug_info->suspended_step = 1; @@ -681,7 +681,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr, kernel_step = this_cpu_ptr(&stepping_kernel_bp); if (*kernel_step != ARM_KERNEL_STEP_NONE) - return 0; + return; if (kernel_active_single_step()) { *kernel_step = ARM_KERNEL_STEP_SUSPEND; @@ -690,10 +690,8 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr, kernel_enable_single_step(regs); } } - - return 0; } -NOKPROBE_SYMBOL(breakpoint_handler); +NOKPROBE_SYMBOL(do_breakpoint); /* * Arm64 hardware does not always report a watchpoint hit address that matches @@ -752,8 +750,7 @@ static int watchpoint_report(struct perf_event *wp, unsigned long addr, return step; } -static int watchpoint_handler(unsigned long addr, unsigned long esr, - struct pt_regs *regs) +void do_watchpoint(unsigned long addr, unsigned long esr, struct pt_regs *regs) { int i, step = 0, *kernel_step, access, closest_match = 0; u64 min_dist = -1, dist; @@ -808,7 +805,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr, rcu_read_unlock(); if (!step) - return 0; + return; /* * We always disable EL0 watchpoints because the kernel can @@ -821,7 +818,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr, /* If we're already stepping a breakpoint, just return. */ if (debug_info->bps_disabled) - return 0; + return; if (test_thread_flag(TIF_SINGLESTEP)) debug_info->suspended_step = 1; @@ -832,7 +829,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr, kernel_step = this_cpu_ptr(&stepping_kernel_bp); if (*kernel_step != ARM_KERNEL_STEP_NONE) - return 0; + return; if (kernel_active_single_step()) { *kernel_step = ARM_KERNEL_STEP_SUSPEND; @@ -841,44 +838,41 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr, kernel_enable_single_step(regs); } } - - return 0; } -NOKPROBE_SYMBOL(watchpoint_handler); +NOKPROBE_SYMBOL(do_watchpoint); /* * Handle single-step exception. */ -int reinstall_suspended_bps(struct pt_regs *regs) +bool try_step_suspended_breakpoints(struct pt_regs *regs) { struct debug_info *debug_info = ¤t->thread.debug; - int handled_exception = 0, *kernel_step; - - kernel_step = this_cpu_ptr(&stepping_kernel_bp); + int *kernel_step = this_cpu_ptr(&stepping_kernel_bp); + bool handled_exception = false; /* - * Called from single-step exception handler. - * Return 0 if execution can resume, 1 if a SIGTRAP should be - * reported. + * Called from single-step exception entry. + * Return true if we stepped a breakpoint and can resume execution, + * false if we need to handle a single-step. */ if (user_mode(regs)) { if (debug_info->bps_disabled) { debug_info->bps_disabled = 0; toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL0, 1); - handled_exception = 1; + handled_exception = true; } if (debug_info->wps_disabled) { debug_info->wps_disabled = 0; toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 1); - handled_exception = 1; + handled_exception = true; } if (handled_exception) { if (debug_info->suspended_step) { debug_info->suspended_step = 0; /* Allow exception handling to fall-through. */ - handled_exception = 0; + handled_exception = false; } else { user_disable_single_step(current); } @@ -892,17 +886,17 @@ int reinstall_suspended_bps(struct pt_regs *regs) if (*kernel_step != ARM_KERNEL_STEP_SUSPEND) { kernel_disable_single_step(); - handled_exception = 1; + handled_exception = true; } else { - handled_exception = 0; + handled_exception = false; } *kernel_step = ARM_KERNEL_STEP_NONE; } - return !handled_exception; + return handled_exception; } -NOKPROBE_SYMBOL(reinstall_suspended_bps); +NOKPROBE_SYMBOL(try_step_suspended_breakpoints); /* * Context-switcher for restoring suspended breakpoints. @@ -987,12 +981,6 @@ static int __init arch_hw_breakpoint_init(void) pr_info("found %d breakpoint and %d watchpoint registers.\n", core_num_brps, core_num_wrps); - /* Register debug fault handlers. */ - hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, - TRAP_HWBKPT, "hw-breakpoint handler"); - hook_debug_fault_code(DBG_ESR_EVT_HWWP, watchpoint_handler, SIGTRAP, - TRAP_HWBKPT, "hw-watchpoint handler"); - /* * Reset the breakpoint resources. We assume that a halting * debugger will leave the world in a nice state for us. diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 85087e2df564..c0065a1d77cf 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -51,7 +51,6 @@ static void init_irq_scs(void) scs_alloc(early_cpu_to_node(cpu)); } -#ifdef CONFIG_VMAP_STACK static void __init init_irq_stacks(void) { int cpu; @@ -62,18 +61,6 @@ static void __init init_irq_stacks(void) per_cpu(irq_stack_ptr, cpu) = p; } } -#else -/* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */ -DEFINE_PER_CPU_ALIGNED(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack); - -static void init_irq_stacks(void) -{ - int cpu; - - for_each_possible_cpu(cpu) - per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu); -} -#endif #ifndef CONFIG_PREEMPT_RT static void ____do_softirq(struct pt_regs *regs) diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c index f3c4d3a8a20f..968324a79a89 100644 --- a/arch/arm64/kernel/kgdb.c +++ b/arch/arm64/kernel/kgdb.c @@ -234,23 +234,23 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, return err; } -static int kgdb_brk_fn(struct pt_regs *regs, unsigned long esr) +int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr) { kgdb_handle_exception(1, SIGTRAP, 0, regs); return DBG_HOOK_HANDLED; } -NOKPROBE_SYMBOL(kgdb_brk_fn) +NOKPROBE_SYMBOL(kgdb_brk_handler) -static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned long esr) +int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr) { compiled_break = 1; kgdb_handle_exception(1, SIGTRAP, 0, regs); return DBG_HOOK_HANDLED; } -NOKPROBE_SYMBOL(kgdb_compiled_brk_fn); +NOKPROBE_SYMBOL(kgdb_compiled_brk_handler); -static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr) +int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr) { if (!kgdb_single_step) return DBG_HOOK_ERROR; @@ -258,21 +258,7 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr) kgdb_handle_exception(0, SIGTRAP, 0, regs); return DBG_HOOK_HANDLED; } -NOKPROBE_SYMBOL(kgdb_step_brk_fn); - -static struct break_hook kgdb_brkpt_hook = { - .fn = kgdb_brk_fn, - .imm = KGDB_DYN_DBG_BRK_IMM, -}; - -static struct break_hook kgdb_compiled_brkpt_hook = { - .fn = kgdb_compiled_brk_fn, - .imm = KGDB_COMPILED_DBG_BRK_IMM, -}; - -static struct step_hook kgdb_step_hook = { - .fn = kgdb_step_brk_fn -}; +NOKPROBE_SYMBOL(kgdb_single_step_handler); static int __kgdb_notify(struct die_args *args, unsigned long cmd) { @@ -311,15 +297,7 @@ static struct notifier_block kgdb_notifier = { */ int kgdb_arch_init(void) { - int ret = register_die_notifier(&kgdb_notifier); - - if (ret != 0) - return ret; - - register_kernel_break_hook(&kgdb_brkpt_hook); - register_kernel_break_hook(&kgdb_compiled_brkpt_hook); - register_kernel_step_hook(&kgdb_step_hook); - return 0; + return register_die_notifier(&kgdb_notifier); } /* @@ -329,9 +307,6 @@ int kgdb_arch_init(void) */ void kgdb_arch_exit(void) { - unregister_kernel_break_hook(&kgdb_brkpt_hook); - unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook); - unregister_kernel_step_hook(&kgdb_step_hook); unregister_die_notifier(&kgdb_notifier); } diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index af1ca875c52c..410060ebd86d 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -94,7 +94,7 @@ int load_other_segments(struct kimage *image, char *initrd, unsigned long initrd_len, char *cmdline) { - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; void *dtb = NULL; unsigned long initrd_load_addr = 0, dtb_len, orig_segments = image->nr_segments; diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c index bde32979c06a..7afd370da9f4 100644 --- a/arch/arm64/kernel/module-plts.c +++ b/arch/arm64/kernel/module-plts.c @@ -283,7 +283,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, unsigned long core_plts = 0; unsigned long init_plts = 0; Elf64_Sym *syms = NULL; - Elf_Shdr *pltsec, *tramp = NULL; + Elf_Shdr *pltsec, *tramp = NULL, *init_tramp = NULL; int i; /* @@ -298,6 +298,9 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, else if (!strcmp(secstrings + sechdrs[i].sh_name, ".text.ftrace_trampoline")) tramp = sechdrs + i; + else if (!strcmp(secstrings + sechdrs[i].sh_name, + ".init.text.ftrace_trampoline")) + init_tramp = sechdrs + i; else if (sechdrs[i].sh_type == SHT_SYMTAB) syms = (Elf64_Sym *)sechdrs[i].sh_addr; } @@ -363,5 +366,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry); } + if (init_tramp) { + init_tramp->sh_type = SHT_NOBITS; + init_tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC; + init_tramp->sh_addralign = __alignof__(struct plt_entry); + init_tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry); + } + return 0; } diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index 06bb680bfe97..d6d443c4a01a 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -23,6 +23,7 @@ #include #include #include +#include enum aarch64_reloc_op { RELOC_OP_NONE, @@ -48,7 +49,17 @@ static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val) return 0; } -static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len) +#define WRITE_PLACE(place, val, mod) do { \ + __typeof__(val) __val = (val); \ + \ + if (mod->state == MODULE_STATE_UNFORMED) \ + *(place) = __val; \ + else \ + aarch64_insn_copy(place, &(__val), sizeof(*place)); \ +} while (0) + +static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len, + struct module *me) { s64 sval = do_reloc(op, place, val); @@ -66,7 +77,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len) switch (len) { case 16: - *(s16 *)place = sval; + WRITE_PLACE((s16 *)place, sval, me); switch (op) { case RELOC_OP_ABS: if (sval < 0 || sval > U16_MAX) @@ -82,7 +93,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len) } break; case 32: - *(s32 *)place = sval; + WRITE_PLACE((s32 *)place, sval, me); switch (op) { case RELOC_OP_ABS: if (sval < 0 || sval > U32_MAX) @@ -98,7 +109,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len) } break; case 64: - *(s64 *)place = sval; + WRITE_PLACE((s64 *)place, sval, me); break; default: pr_err("Invalid length (%d) for data relocation\n", len); @@ -113,7 +124,8 @@ enum aarch64_insn_movw_imm_type { }; static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val, - int lsb, enum aarch64_insn_movw_imm_type imm_type) + int lsb, enum aarch64_insn_movw_imm_type imm_type, + struct module *me) { u64 imm; s64 sval; @@ -145,7 +157,7 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val, /* Update the instruction with the new encoding. */ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm); - *place = cpu_to_le32(insn); + WRITE_PLACE(place, cpu_to_le32(insn), me); if (imm > U16_MAX) return -ERANGE; @@ -154,7 +166,8 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val, } static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val, - int lsb, int len, enum aarch64_insn_imm_type imm_type) + int lsb, int len, enum aarch64_insn_imm_type imm_type, + struct module *me) { u64 imm, imm_mask; s64 sval; @@ -170,7 +183,7 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val, /* Update the instruction's immediate field. */ insn = aarch64_insn_encode_immediate(imm_type, insn, imm); - *place = cpu_to_le32(insn); + WRITE_PLACE(place, cpu_to_le32(insn), me); /* * Extract the upper value bits (including the sign bit) and @@ -189,17 +202,17 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val, } static int reloc_insn_adrp(struct module *mod, Elf64_Shdr *sechdrs, - __le32 *place, u64 val) + __le32 *place, u64 val, struct module *me) { u32 insn; if (!is_forbidden_offset_for_adrp(place)) return reloc_insn_imm(RELOC_OP_PAGE, place, val, 12, 21, - AARCH64_INSN_IMM_ADR); + AARCH64_INSN_IMM_ADR, me); /* patch ADRP to ADR if it is in range */ if (!reloc_insn_imm(RELOC_OP_PREL, place, val & ~0xfff, 0, 21, - AARCH64_INSN_IMM_ADR)) { + AARCH64_INSN_IMM_ADR, me)) { insn = le32_to_cpu(*place); insn &= ~BIT(31); } else { @@ -211,7 +224,7 @@ static int reloc_insn_adrp(struct module *mod, Elf64_Shdr *sechdrs, AARCH64_INSN_BRANCH_NOLINK); } - *place = cpu_to_le32(insn); + WRITE_PLACE(place, cpu_to_le32(insn), me); return 0; } @@ -255,23 +268,23 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, /* Data relocations. */ case R_AARCH64_ABS64: overflow_check = false; - ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); + ovf = reloc_data(RELOC_OP_ABS, loc, val, 64, me); break; case R_AARCH64_ABS32: - ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); + ovf = reloc_data(RELOC_OP_ABS, loc, val, 32, me); break; case R_AARCH64_ABS16: - ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); + ovf = reloc_data(RELOC_OP_ABS, loc, val, 16, me); break; case R_AARCH64_PREL64: overflow_check = false; - ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); + ovf = reloc_data(RELOC_OP_PREL, loc, val, 64, me); break; case R_AARCH64_PREL32: - ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); + ovf = reloc_data(RELOC_OP_PREL, loc, val, 32, me); break; case R_AARCH64_PREL16: - ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); + ovf = reloc_data(RELOC_OP_PREL, loc, val, 16, me); break; /* MOVW instruction relocations. */ @@ -280,88 +293,88 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, fallthrough; case R_AARCH64_MOVW_UABS_G0: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_UABS_G1_NC: overflow_check = false; fallthrough; case R_AARCH64_MOVW_UABS_G1: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_UABS_G2_NC: overflow_check = false; fallthrough; case R_AARCH64_MOVW_UABS_G2: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_UABS_G3: /* We're using the top bits so we can't overflow. */ overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_SABS_G0: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_SABS_G1: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_SABS_G2: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_PREL_G0_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_PREL_G0: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_PREL_G1_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_PREL_G1: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_PREL_G2_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, - AARCH64_INSN_IMM_MOVKZ); + AARCH64_INSN_IMM_MOVKZ, me); break; case R_AARCH64_MOVW_PREL_G2: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; case R_AARCH64_MOVW_PREL_G3: /* We're using the top bits so we can't overflow. */ overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, - AARCH64_INSN_IMM_MOVNZ); + AARCH64_INSN_IMM_MOVNZ, me); break; /* Immediate instruction relocations. */ case R_AARCH64_LD_PREL_LO19: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, - AARCH64_INSN_IMM_19); + AARCH64_INSN_IMM_19, me); break; case R_AARCH64_ADR_PREL_LO21: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, - AARCH64_INSN_IMM_ADR); + AARCH64_INSN_IMM_ADR, me); break; case R_AARCH64_ADR_PREL_PG_HI21_NC: overflow_check = false; fallthrough; case R_AARCH64_ADR_PREL_PG_HI21: - ovf = reloc_insn_adrp(me, sechdrs, loc, val); + ovf = reloc_insn_adrp(me, sechdrs, loc, val, me); if (ovf && ovf != -ERANGE) return ovf; break; @@ -369,46 +382,46 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, case R_AARCH64_LDST8_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, - AARCH64_INSN_IMM_12); + AARCH64_INSN_IMM_12, me); break; case R_AARCH64_LDST16_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, - AARCH64_INSN_IMM_12); + AARCH64_INSN_IMM_12, me); break; case R_AARCH64_LDST32_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, - AARCH64_INSN_IMM_12); + AARCH64_INSN_IMM_12, me); break; case R_AARCH64_LDST64_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, - AARCH64_INSN_IMM_12); + AARCH64_INSN_IMM_12, me); break; case R_AARCH64_LDST128_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, - AARCH64_INSN_IMM_12); + AARCH64_INSN_IMM_12, me); break; case R_AARCH64_TSTBR14: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, - AARCH64_INSN_IMM_14); + AARCH64_INSN_IMM_14, me); break; case R_AARCH64_CONDBR19: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, - AARCH64_INSN_IMM_19); + AARCH64_INSN_IMM_19, me); break; case R_AARCH64_JUMP26: case R_AARCH64_CALL26: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, - AARCH64_INSN_IMM_26); + AARCH64_INSN_IMM_26, me); if (ovf == -ERANGE) { val = module_emit_plt_entry(me, sechdrs, loc, &rel[i], sym); if (!val) return -ENOEXEC; ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, - 26, AARCH64_INSN_IMM_26); + 26, AARCH64_INSN_IMM_26, me); } break; @@ -453,6 +466,17 @@ static int module_init_ftrace_plt(const Elf_Ehdr *hdr, __init_plt(&plts[FTRACE_PLT_IDX], FTRACE_ADDR); mod->arch.ftrace_trampolines = plts; + + s = find_section(hdr, sechdrs, ".init.text.ftrace_trampoline"); + if (!s) + return -ENOEXEC; + + plts = (void *)s->sh_addr; + + __init_plt(&plts[FTRACE_PLT_IDX], FTRACE_ADDR); + + mod->arch.init_ftrace_trampolines = plts; + #endif return 0; } diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c index 2fbfd27ff5f2..e5e773844889 100644 --- a/arch/arm64/kernel/mte.c +++ b/arch/arm64/kernel/mte.c @@ -200,7 +200,7 @@ static void mte_update_sctlr_user(struct task_struct *task) * program requested values go with what was requested. */ resolved_mte_tcf = (mte_ctrl & pref) ? pref : mte_ctrl; - sctlr &= ~SCTLR_EL1_TCF0_MASK; + sctlr &= ~(SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK); /* * Pick an actual setting. The order in which we check for * set bits and map into register values determines our @@ -212,6 +212,10 @@ static void mte_update_sctlr_user(struct task_struct *task) sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYNC); else if (resolved_mte_tcf & MTE_CTRL_TCF_SYNC) sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, SYNC); + + if (mte_ctrl & MTE_CTRL_STORE_ONLY) + sctlr |= SYS_FIELD_PREP(SCTLR_EL1, TCSO0, 1); + task->thread.sctlr_user = sctlr; } @@ -371,6 +375,9 @@ long set_mte_ctrl(struct task_struct *task, unsigned long arg) (arg & PR_MTE_TCF_SYNC)) mte_ctrl |= MTE_CTRL_TCF_ASYMM; + if (arg & PR_MTE_STORE_ONLY) + mte_ctrl |= MTE_CTRL_STORE_ONLY; + task->thread.mte_ctrl = mte_ctrl; if (task == current) { preempt_disable(); @@ -398,6 +405,8 @@ long get_mte_ctrl(struct task_struct *task) ret |= PR_MTE_TCF_ASYNC; if (mte_ctrl & MTE_CTRL_TCF_SYNC) ret |= PR_MTE_TCF_SYNC; + if (mte_ctrl & MTE_CTRL_STORE_ONLY) + ret |= PR_MTE_STORE_ONLY; return ret; } diff --git a/arch/arm64/kernel/pi/Makefile b/arch/arm64/kernel/pi/Makefile index 4d11a8c29181..be92d73c25b2 100644 --- a/arch/arm64/kernel/pi/Makefile +++ b/arch/arm64/kernel/pi/Makefile @@ -2,7 +2,7 @@ # Copyright 2022 Google LLC KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ - -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \ + -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_KSTACK_ERASE) \ $(DISABLE_LATENT_ENTROPY_PLUGIN) \ $(call cc-option,-mbranch-protection=none) \ -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ @@ -41,4 +41,4 @@ obj-y := idreg-override.pi.o \ obj-$(CONFIG_RELOCATABLE) += relocate.pi.o obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.pi.o -extra-y := $(patsubst %.pi.o,%.o,$(obj-y)) +targets := $(patsubst %.pi.o,%.o,$(obj-y)) diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c index d9e462eafb95..0c5d408afd95 100644 --- a/arch/arm64/kernel/probes/kprobes.c +++ b/arch/arm64/kernel/probes/kprobes.c @@ -292,8 +292,8 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) return 0; } -static int __kprobes -kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) +int __kprobes +kprobe_brk_handler(struct pt_regs *regs, unsigned long esr) { struct kprobe *p, *cur_kprobe; struct kprobe_ctlblk *kcb; @@ -336,13 +336,8 @@ kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) return DBG_HOOK_HANDLED; } -static struct break_hook kprobes_break_hook = { - .imm = KPROBES_BRK_IMM, - .fn = kprobe_breakpoint_handler, -}; - -static int __kprobes -kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr) +int __kprobes +kprobe_ss_brk_handler(struct pt_regs *regs, unsigned long esr) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); unsigned long addr = instruction_pointer(regs); @@ -360,13 +355,8 @@ kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr) return DBG_HOOK_ERROR; } -static struct break_hook kprobes_break_ss_hook = { - .imm = KPROBES_BRK_SS_IMM, - .fn = kprobe_breakpoint_ss_handler, -}; - -static int __kprobes -kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) +int __kprobes +kretprobe_brk_handler(struct pt_regs *regs, unsigned long esr) { if (regs->pc != (unsigned long)__kretprobe_trampoline) return DBG_HOOK_ERROR; @@ -375,11 +365,6 @@ kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) return DBG_HOOK_HANDLED; } -static struct break_hook kretprobes_break_hook = { - .imm = KRETPROBES_BRK_IMM, - .fn = kretprobe_breakpoint_handler, -}; - /* * Provide a blacklist of symbols identifying ranges which cannot be kprobed. * This blacklist is exposed to userspace via debugfs (kprobes/blacklist). @@ -422,9 +407,5 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p) int __init arch_init_kprobes(void) { - register_kernel_break_hook(&kprobes_break_hook); - register_kernel_break_hook(&kprobes_break_ss_hook); - register_kernel_break_hook(&kretprobes_break_hook); - return 0; } diff --git a/arch/arm64/kernel/probes/kprobes_trampoline.S b/arch/arm64/kernel/probes/kprobes_trampoline.S index a362f3dbb3d1..b60739d3983f 100644 --- a/arch/arm64/kernel/probes/kprobes_trampoline.S +++ b/arch/arm64/kernel/probes/kprobes_trampoline.S @@ -12,7 +12,7 @@ SYM_CODE_START(__kretprobe_trampoline) /* * Trigger a breakpoint exception. The PC will be adjusted by - * kretprobe_breakpoint_handler(), and no subsequent instructions will + * kretprobe_brk_handler(), and no subsequent instructions will * be executed from the trampoline. */ brk #KRETPROBES_BRK_IMM diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c index cb3d05af36e3..1f91fd2a8187 100644 --- a/arch/arm64/kernel/probes/uprobes.c +++ b/arch/arm64/kernel/probes/uprobes.c @@ -173,7 +173,7 @@ int arch_uprobe_exception_notify(struct notifier_block *self, return NOTIFY_DONE; } -static int uprobe_breakpoint_handler(struct pt_regs *regs, +int uprobe_brk_handler(struct pt_regs *regs, unsigned long esr) { if (uprobe_pre_sstep_notifier(regs)) @@ -182,7 +182,7 @@ static int uprobe_breakpoint_handler(struct pt_regs *regs, return DBG_HOOK_ERROR; } -static int uprobe_single_step_handler(struct pt_regs *regs, +int uprobe_single_step_handler(struct pt_regs *regs, unsigned long esr) { struct uprobe_task *utask = current->utask; @@ -194,23 +194,3 @@ static int uprobe_single_step_handler(struct pt_regs *regs, return DBG_HOOK_ERROR; } -/* uprobe breakpoint handler hook */ -static struct break_hook uprobes_break_hook = { - .imm = UPROBES_BRK_IMM, - .fn = uprobe_breakpoint_handler, -}; - -/* uprobe single step handler hook */ -static struct step_hook uprobes_step_hook = { - .fn = uprobe_single_step_handler, -}; - -static int __init arch_init_uprobes(void) -{ - register_user_break_hook(&uprobes_break_hook); - register_user_step_hook(&uprobes_step_hook); - - return 0; -} - -device_initcall(arch_init_uprobes); diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index a5ca15daeb8a..96482a1412c6 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -288,7 +288,9 @@ static void flush_gcs(void) if (!system_supports_gcs()) return; - gcs_free(current); + current->thread.gcspr_el0 = 0; + current->thread.gcs_base = 0; + current->thread.gcs_size = 0; current->thread.gcs_el0_mode = 0; write_sysreg_s(GCSCRE0_EL1_nTR, SYS_GCSCRE0_EL1); write_sysreg_s(0, SYS_GCSPR_EL0); @@ -305,13 +307,13 @@ static int copy_thread_gcs(struct task_struct *p, p->thread.gcs_base = 0; p->thread.gcs_size = 0; + p->thread.gcs_el0_mode = current->thread.gcs_el0_mode; + p->thread.gcs_el0_locked = current->thread.gcs_el0_locked; + gcs = gcs_alloc_thread_stack(p, args); if (IS_ERR_VALUE(gcs)) return PTR_ERR((void *)gcs); - p->thread.gcs_el0_mode = current->thread.gcs_el0_mode; - p->thread.gcs_el0_locked = current->thread.gcs_el0_locked; - return 0; } @@ -339,7 +341,6 @@ void flush_thread(void) void arch_release_task_struct(struct task_struct *tsk) { fpsimd_release_task(tsk); - gcs_free(tsk); } int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) @@ -671,6 +672,11 @@ static void permission_overlay_switch(struct task_struct *next) current->thread.por_el0 = read_sysreg_s(SYS_POR_EL0); if (current->thread.por_el0 != next->thread.por_el0) { write_sysreg_s(next->thread.por_el0, SYS_POR_EL0); + /* + * No ISB required as we can tolerate spurious Overlay faults - + * the fault handler will check again based on the new value + * of POR_EL0. + */ } } @@ -849,10 +855,14 @@ long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) if (is_compat_thread(ti)) return -EINVAL; - if (system_supports_mte()) + if (system_supports_mte()) { valid_mask |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC \ | PR_MTE_TAG_MASK; + if (cpus_have_cap(ARM64_MTE_STORE_ONLY)) + valid_mask |= PR_MTE_STORE_ONLY; + } + if (arg & ~valid_mask) return -EINVAL; diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index a360e52db02f..4b001121c72d 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -141,7 +141,7 @@ unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) addr += n; if (regs_within_kernel_stack(regs, (unsigned long)addr)) - return *addr; + return READ_ONCE_NOCHECK(*addr); else return 0; } @@ -1586,7 +1586,7 @@ enum aarch64_regset { static const struct user_regset aarch64_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct user_pt_regs) / sizeof(u64), .size = sizeof(u64), .align = sizeof(u64), @@ -1594,7 +1594,7 @@ static const struct user_regset aarch64_regsets[] = { .set = gpr_set }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_fpsimd_state) / sizeof(u32), /* * We pretend we have 32-bit registers because the fpsr and @@ -1607,7 +1607,7 @@ static const struct user_regset aarch64_regsets[] = { .set = fpr_set }, [REGSET_TLS] = { - .core_note_type = NT_ARM_TLS, + USER_REGSET_NOTE_TYPE(ARM_TLS), .n = 2, .size = sizeof(void *), .align = sizeof(void *), @@ -1616,7 +1616,7 @@ static const struct user_regset aarch64_regsets[] = { }, #ifdef CONFIG_HAVE_HW_BREAKPOINT [REGSET_HW_BREAK] = { - .core_note_type = NT_ARM_HW_BREAK, + USER_REGSET_NOTE_TYPE(ARM_HW_BREAK), .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -1624,7 +1624,7 @@ static const struct user_regset aarch64_regsets[] = { .set = hw_break_set, }, [REGSET_HW_WATCH] = { - .core_note_type = NT_ARM_HW_WATCH, + USER_REGSET_NOTE_TYPE(ARM_HW_WATCH), .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -1633,7 +1633,7 @@ static const struct user_regset aarch64_regsets[] = { }, #endif [REGSET_SYSTEM_CALL] = { - .core_note_type = NT_ARM_SYSTEM_CALL, + USER_REGSET_NOTE_TYPE(ARM_SYSTEM_CALL), .n = 1, .size = sizeof(int), .align = sizeof(int), @@ -1641,7 +1641,7 @@ static const struct user_regset aarch64_regsets[] = { .set = system_call_set, }, [REGSET_FPMR] = { - .core_note_type = NT_ARM_FPMR, + USER_REGSET_NOTE_TYPE(ARM_FPMR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), @@ -1650,7 +1650,7 @@ static const struct user_regset aarch64_regsets[] = { }, #ifdef CONFIG_ARM64_SVE [REGSET_SVE] = { /* Scalable Vector Extension */ - .core_note_type = NT_ARM_SVE, + USER_REGSET_NOTE_TYPE(ARM_SVE), .n = DIV_ROUND_UP(SVE_PT_SIZE(ARCH_SVE_VQ_MAX, SVE_PT_REGS_SVE), SVE_VQ_BYTES), @@ -1662,7 +1662,7 @@ static const struct user_regset aarch64_regsets[] = { #endif #ifdef CONFIG_ARM64_SME [REGSET_SSVE] = { /* Streaming mode SVE */ - .core_note_type = NT_ARM_SSVE, + USER_REGSET_NOTE_TYPE(ARM_SSVE), .n = DIV_ROUND_UP(SVE_PT_SIZE(SME_VQ_MAX, SVE_PT_REGS_SVE), SVE_VQ_BYTES), .size = SVE_VQ_BYTES, @@ -1671,7 +1671,7 @@ static const struct user_regset aarch64_regsets[] = { .set = ssve_set, }, [REGSET_ZA] = { /* SME ZA */ - .core_note_type = NT_ARM_ZA, + USER_REGSET_NOTE_TYPE(ARM_ZA), /* * ZA is a single register but it's variably sized and * the ptrace core requires that the size of any data @@ -1687,7 +1687,7 @@ static const struct user_regset aarch64_regsets[] = { .set = za_set, }, [REGSET_ZT] = { /* SME ZT */ - .core_note_type = NT_ARM_ZT, + USER_REGSET_NOTE_TYPE(ARM_ZT), .n = 1, .size = ZT_SIG_REG_BYTES, .align = sizeof(u64), @@ -1697,7 +1697,7 @@ static const struct user_regset aarch64_regsets[] = { #endif #ifdef CONFIG_ARM64_PTR_AUTH [REGSET_PAC_MASK] = { - .core_note_type = NT_ARM_PAC_MASK, + USER_REGSET_NOTE_TYPE(ARM_PAC_MASK), .n = sizeof(struct user_pac_mask) / sizeof(u64), .size = sizeof(u64), .align = sizeof(u64), @@ -1705,7 +1705,7 @@ static const struct user_regset aarch64_regsets[] = { /* this cannot be set dynamically */ }, [REGSET_PAC_ENABLED_KEYS] = { - .core_note_type = NT_ARM_PAC_ENABLED_KEYS, + USER_REGSET_NOTE_TYPE(ARM_PAC_ENABLED_KEYS), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -1714,7 +1714,7 @@ static const struct user_regset aarch64_regsets[] = { }, #ifdef CONFIG_CHECKPOINT_RESTORE [REGSET_PACA_KEYS] = { - .core_note_type = NT_ARM_PACA_KEYS, + USER_REGSET_NOTE_TYPE(ARM_PACA_KEYS), .n = sizeof(struct user_pac_address_keys) / sizeof(__uint128_t), .size = sizeof(__uint128_t), .align = sizeof(__uint128_t), @@ -1722,7 +1722,7 @@ static const struct user_regset aarch64_regsets[] = { .set = pac_address_keys_set, }, [REGSET_PACG_KEYS] = { - .core_note_type = NT_ARM_PACG_KEYS, + USER_REGSET_NOTE_TYPE(ARM_PACG_KEYS), .n = sizeof(struct user_pac_generic_keys) / sizeof(__uint128_t), .size = sizeof(__uint128_t), .align = sizeof(__uint128_t), @@ -1733,7 +1733,7 @@ static const struct user_regset aarch64_regsets[] = { #endif #ifdef CONFIG_ARM64_TAGGED_ADDR_ABI [REGSET_TAGGED_ADDR_CTRL] = { - .core_note_type = NT_ARM_TAGGED_ADDR_CTRL, + USER_REGSET_NOTE_TYPE(ARM_TAGGED_ADDR_CTRL), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -1743,7 +1743,7 @@ static const struct user_regset aarch64_regsets[] = { #endif #ifdef CONFIG_ARM64_POE [REGSET_POE] = { - .core_note_type = NT_ARM_POE, + USER_REGSET_NOTE_TYPE(ARM_POE), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -1753,7 +1753,7 @@ static const struct user_regset aarch64_regsets[] = { #endif #ifdef CONFIG_ARM64_GCS [REGSET_GCS] = { - .core_note_type = NT_ARM_GCS, + USER_REGSET_NOTE_TYPE(ARM_GCS), .n = sizeof(struct user_gcs) / sizeof(u64), .size = sizeof(u64), .align = sizeof(u64), @@ -1943,7 +1943,7 @@ static int compat_tls_set(struct task_struct *target, static const struct user_regset aarch32_regsets[] = { [REGSET_COMPAT_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = COMPAT_ELF_NGREG, .size = sizeof(compat_elf_greg_t), .align = sizeof(compat_elf_greg_t), @@ -1951,7 +1951,7 @@ static const struct user_regset aarch32_regsets[] = { .set = compat_gpr_set }, [REGSET_COMPAT_VFP] = { - .core_note_type = NT_ARM_VFP, + USER_REGSET_NOTE_TYPE(ARM_VFP), .n = VFP_STATE_SIZE / sizeof(compat_ulong_t), .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), @@ -1968,7 +1968,7 @@ static const struct user_regset_view user_aarch32_view = { static const struct user_regset aarch32_ptrace_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = COMPAT_ELF_NGREG, .size = sizeof(compat_elf_greg_t), .align = sizeof(compat_elf_greg_t), @@ -1976,7 +1976,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .set = compat_gpr_set }, [REGSET_FPR] = { - .core_note_type = NT_ARM_VFP, + USER_REGSET_NOTE_TYPE(ARM_VFP), .n = VFP_STATE_SIZE / sizeof(compat_ulong_t), .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), @@ -1984,7 +1984,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .set = compat_vfp_set }, [REGSET_TLS] = { - .core_note_type = NT_ARM_TLS, + USER_REGSET_NOTE_TYPE(ARM_TLS), .n = 1, .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), @@ -1993,7 +1993,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { }, #ifdef CONFIG_HAVE_HW_BREAKPOINT [REGSET_HW_BREAK] = { - .core_note_type = NT_ARM_HW_BREAK, + USER_REGSET_NOTE_TYPE(ARM_HW_BREAK), .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -2001,7 +2001,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .set = hw_break_set, }, [REGSET_HW_WATCH] = { - .core_note_type = NT_ARM_HW_WATCH, + USER_REGSET_NOTE_TYPE(ARM_HW_WATCH), .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -2010,7 +2010,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { }, #endif [REGSET_SYSTEM_CALL] = { - .core_note_type = NT_ARM_SYSTEM_CALL, + USER_REGSET_NOTE_TYPE(ARM_SYSTEM_CALL), .n = 1, .size = sizeof(int), .align = sizeof(int), diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c index 255d12f881c2..6f24a0251e18 100644 --- a/arch/arm64/kernel/sdei.c +++ b/arch/arm64/kernel/sdei.c @@ -34,10 +34,8 @@ unsigned long sdei_exit_mode; DECLARE_PER_CPU(unsigned long *, sdei_stack_normal_ptr); DECLARE_PER_CPU(unsigned long *, sdei_stack_critical_ptr); -#ifdef CONFIG_VMAP_STACK DEFINE_PER_CPU(unsigned long *, sdei_stack_normal_ptr); DEFINE_PER_CPU(unsigned long *, sdei_stack_critical_ptr); -#endif DECLARE_PER_CPU(unsigned long *, sdei_shadow_call_stack_normal_ptr); DECLARE_PER_CPU(unsigned long *, sdei_shadow_call_stack_critical_ptr); @@ -65,8 +63,7 @@ static void free_sdei_stacks(void) { int cpu; - if (!IS_ENABLED(CONFIG_VMAP_STACK)) - return; + BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK)); for_each_possible_cpu(cpu) { _free_sdei_stack(&sdei_stack_normal_ptr, cpu); @@ -91,8 +88,7 @@ static int init_sdei_stacks(void) int cpu; int err = 0; - if (!IS_ENABLED(CONFIG_VMAP_STACK)) - return 0; + BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK)); for_each_possible_cpu(cpu) { err = _init_sdei_stack(&sdei_stack_normal_ptr, cpu); diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 417140cd399b..db3f972f8cd9 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -95,8 +95,11 @@ static void save_reset_user_access_state(struct user_access_state *ua_state) ua_state->por_el0 = read_sysreg_s(SYS_POR_EL0); write_sysreg_s(por_enable_all, SYS_POR_EL0); - /* Ensure that any subsequent uaccess observes the updated value */ - isb(); + /* + * No ISB required as we can tolerate spurious Overlay faults - + * the fault handler will check again based on the new value + * of POR_EL0. + */ } } diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 3b3f6b56e733..68cea3a4a35c 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -64,26 +64,18 @@ struct secondary_data secondary_data; /* Number of CPUs which aren't online, but looping in kernel text. */ static int cpus_stuck_in_kernel; -enum ipi_msg_type { - IPI_RESCHEDULE, - IPI_CALL_FUNC, - IPI_CPU_STOP, - IPI_CPU_STOP_NMI, - IPI_TIMER, - IPI_IRQ_WORK, - NR_IPI, - /* - * Any enum >= NR_IPI and < MAX_IPI is special and not tracable - * with trace_ipi_* - */ - IPI_CPU_BACKTRACE = NR_IPI, - IPI_KGDB_ROUNDUP, - MAX_IPI -}; - static int ipi_irq_base __ro_after_init; static int nr_ipi __ro_after_init = NR_IPI; -static struct irq_desc *ipi_desc[MAX_IPI] __ro_after_init; + +struct ipi_descs { + struct irq_desc *descs[MAX_IPI]; +}; + +static DEFINE_PER_CPU_READ_MOSTLY(struct ipi_descs, pcpu_ipi_desc); + +#define get_ipi_desc(__cpu, __ipi) (per_cpu_ptr(&pcpu_ipi_desc, __cpu)->descs[__ipi]) + +static bool percpu_ipi_descs __ro_after_init; static bool crash_stop; @@ -844,7 +836,7 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); for_each_online_cpu(cpu) - seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu)); + seq_printf(p, "%10u ", irq_desc_kstat_cpu(get_ipi_desc(cpu, i), cpu)); seq_printf(p, " %s\n", ipi_types[i]); } @@ -917,9 +909,20 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs #endif } +static void arm64_send_ipi(const cpumask_t *mask, unsigned int nr) +{ + unsigned int cpu; + + if (!percpu_ipi_descs) + __ipi_send_mask(get_ipi_desc(0, nr), mask); + else + for_each_cpu(cpu, mask) + __ipi_send_single(get_ipi_desc(cpu, nr), cpu); +} + static void arm64_backtrace_ipi(cpumask_t *mask) { - __ipi_send_mask(ipi_desc[IPI_CPU_BACKTRACE], mask); + arm64_send_ipi(mask, IPI_CPU_BACKTRACE); } void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) @@ -944,7 +947,7 @@ void kgdb_roundup_cpus(void) if (cpu == this_cpu) continue; - __ipi_send_single(ipi_desc[IPI_KGDB_ROUNDUP], cpu); + __ipi_send_single(get_ipi_desc(cpu, IPI_KGDB_ROUNDUP), cpu); } } #endif @@ -1013,14 +1016,16 @@ static void do_handle_IPI(int ipinr) static irqreturn_t ipi_handler(int irq, void *data) { - do_handle_IPI(irq - ipi_irq_base); + unsigned int ipi = (irq - ipi_irq_base) % nr_ipi; + + do_handle_IPI(ipi); return IRQ_HANDLED; } static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) { trace_ipi_raise(target, ipi_types[ipinr]); - __ipi_send_mask(ipi_desc[ipinr], target); + arm64_send_ipi(target, ipinr); } static bool ipi_should_be_nmi(enum ipi_msg_type ipi) @@ -1046,11 +1051,15 @@ static void ipi_setup(int cpu) return; for (i = 0; i < nr_ipi; i++) { - if (ipi_should_be_nmi(i)) { - prepare_percpu_nmi(ipi_irq_base + i); - enable_percpu_nmi(ipi_irq_base + i, 0); + if (!percpu_ipi_descs) { + if (ipi_should_be_nmi(i)) { + prepare_percpu_nmi(ipi_irq_base + i); + enable_percpu_nmi(ipi_irq_base + i, 0); + } else { + enable_percpu_irq(ipi_irq_base + i, 0); + } } else { - enable_percpu_irq(ipi_irq_base + i, 0); + enable_irq(irq_desc_get_irq(get_ipi_desc(cpu, i))); } } } @@ -1064,44 +1073,77 @@ static void ipi_teardown(int cpu) return; for (i = 0; i < nr_ipi; i++) { - if (ipi_should_be_nmi(i)) { - disable_percpu_nmi(ipi_irq_base + i); - teardown_percpu_nmi(ipi_irq_base + i); + if (!percpu_ipi_descs) { + if (ipi_should_be_nmi(i)) { + disable_percpu_nmi(ipi_irq_base + i); + teardown_percpu_nmi(ipi_irq_base + i); + } else { + disable_percpu_irq(ipi_irq_base + i); + } } else { - disable_percpu_irq(ipi_irq_base + i); + disable_irq(irq_desc_get_irq(get_ipi_desc(cpu, i))); } } } #endif -void __init set_smp_ipi_range(int ipi_base, int n) +static void ipi_setup_sgi(int ipi) +{ + int err, irq, cpu; + + irq = ipi_irq_base + ipi; + + if (ipi_should_be_nmi(ipi)) { + err = request_percpu_nmi(irq, ipi_handler, "IPI", &irq_stat); + WARN(err, "Could not request IRQ %d as NMI, err=%d\n", irq, err); + } else { + err = request_percpu_irq(irq, ipi_handler, "IPI", &irq_stat); + WARN(err, "Could not request IRQ %d as IRQ, err=%d\n", irq, err); + } + + for_each_possible_cpu(cpu) + get_ipi_desc(cpu, ipi) = irq_to_desc(irq); + + irq_set_status_flags(irq, IRQ_HIDDEN); +} + +static void ipi_setup_lpi(int ipi, int ncpus) +{ + for (int cpu = 0; cpu < ncpus; cpu++) { + int err, irq; + + irq = ipi_irq_base + (cpu * nr_ipi) + ipi; + + err = irq_force_affinity(irq, cpumask_of(cpu)); + WARN(err, "Could not force affinity IRQ %d, err=%d\n", irq, err); + + err = request_irq(irq, ipi_handler, IRQF_NO_AUTOEN, "IPI", + NULL); + WARN(err, "Could not request IRQ %d, err=%d\n", irq, err); + + irq_set_status_flags(irq, (IRQ_HIDDEN | IRQ_NO_BALANCING_MASK)); + + get_ipi_desc(cpu, ipi) = irq_to_desc(irq); + } +} + +void __init set_smp_ipi_range_percpu(int ipi_base, int n, int ncpus) { int i; WARN_ON(n < MAX_IPI); nr_ipi = min(n, MAX_IPI); - for (i = 0; i < nr_ipi; i++) { - int err; - - if (ipi_should_be_nmi(i)) { - err = request_percpu_nmi(ipi_base + i, ipi_handler, - "IPI", &irq_stat); - WARN(err, "Could not request IPI %d as NMI, err=%d\n", - i, err); - } else { - err = request_percpu_irq(ipi_base + i, ipi_handler, - "IPI", &irq_stat); - WARN(err, "Could not request IPI %d as IRQ, err=%d\n", - i, err); - } - - ipi_desc[i] = irq_to_desc(ipi_base + i); - irq_set_status_flags(ipi_base + i, IRQ_HIDDEN); - } - + percpu_ipi_descs = !!ncpus; ipi_irq_base = ipi_base; + for (i = 0; i < nr_ipi; i++) { + if (!percpu_ipi_descs) + ipi_setup_sgi(i); + else + ipi_setup_lpi(i, ncpus); + } + /* Setup the boot CPU immediately */ ipi_setup(smp_processor_id()); } @@ -1143,7 +1185,7 @@ static inline unsigned int num_other_online_cpus(void) void smp_send_stop(void) { static unsigned long stop_in_progress; - cpumask_t mask; + static cpumask_t mask; unsigned long timeout; /* diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 1d9d51d7627f..3ebcf8c53fb0 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -152,6 +152,8 @@ kunwind_recover_return_address(struct kunwind_state *state) orig_pc = kretprobe_find_ret_addr(state->task, (void *)state->common.fp, &state->kr_cur); + if (!orig_pc) + return -EINVAL; state->common.pc = orig_pc; state->flags.kretprobe = 1; } @@ -277,21 +279,24 @@ kunwind_next(struct kunwind_state *state) typedef bool (*kunwind_consume_fn)(const struct kunwind_state *state, void *cookie); -static __always_inline void +static __always_inline int do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state, void *cookie) { - if (kunwind_recover_return_address(state)) - return; + int ret; + + ret = kunwind_recover_return_address(state); + if (ret) + return ret; while (1) { - int ret; - if (!consume_state(state, cookie)) - break; + return -EINVAL; ret = kunwind_next(state); + if (ret == -ENOENT) + return 0; if (ret < 0) - break; + return ret; } } @@ -324,7 +329,7 @@ do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state, : stackinfo_get_unknown(); \ }) -static __always_inline void +static __always_inline int kunwind_stack_walk(kunwind_consume_fn consume_state, void *cookie, struct task_struct *task, struct pt_regs *regs) @@ -332,10 +337,8 @@ kunwind_stack_walk(kunwind_consume_fn consume_state, struct stack_info stacks[] = { stackinfo_get_task(task), STACKINFO_CPU(irq), -#if defined(CONFIG_VMAP_STACK) STACKINFO_CPU(overflow), -#endif -#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_ARM_SDE_INTERFACE) +#if defined(CONFIG_ARM_SDE_INTERFACE) STACKINFO_SDEI(normal), STACKINFO_SDEI(critical), #endif @@ -352,7 +355,7 @@ kunwind_stack_walk(kunwind_consume_fn consume_state, if (regs) { if (task != current) - return; + return -EINVAL; kunwind_init_from_regs(&state, regs); } else if (task == current) { kunwind_init_from_caller(&state); @@ -360,7 +363,7 @@ kunwind_stack_walk(kunwind_consume_fn consume_state, kunwind_init_from_task(&state, task); } - do_kunwind(&state, consume_state, cookie); + return do_kunwind(&state, consume_state, cookie); } struct kunwind_consume_entry_data { @@ -387,6 +390,36 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, kunwind_stack_walk(arch_kunwind_consume_entry, &data, task, regs); } +static __always_inline bool +arch_reliable_kunwind_consume_entry(const struct kunwind_state *state, void *cookie) +{ + /* + * At an exception boundary we can reliably consume the saved PC. We do + * not know whether the LR was live when the exception was taken, and + * so we cannot perform the next unwind step reliably. + * + * All that matters is whether the *entire* unwind is reliable, so give + * up as soon as we hit an exception boundary. + */ + if (state->source == KUNWIND_SOURCE_REGS_PC) + return false; + + return arch_kunwind_consume_entry(state, cookie); +} + +noinline noinstr int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, + void *cookie, + struct task_struct *task) +{ + struct kunwind_consume_entry_data data = { + .consume_entry = consume_entry, + .cookie = cookie, + }; + + return kunwind_stack_walk(arch_reliable_kunwind_consume_entry, &data, + task, NULL); +} + struct bpf_unwind_consume_entry_data { bool (*consume_entry)(void *cookie, u64 ip, u64 sp, u64 fp); void *cookie; diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 9bfa5c944379..f528b6041f6a 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -454,7 +454,7 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr) u32 insn; /* check for AArch32 breakpoint instructions */ - if (!aarch32_break_handler(regs)) + if (try_handle_aarch32_break(regs)) return; if (user_insn_read(regs, &insn)) @@ -894,8 +894,6 @@ void bad_el0_sync(struct pt_regs *regs, int reason, unsigned long esr) "Bad EL0 synchronous exception"); } -#ifdef CONFIG_VMAP_STACK - DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack) __aligned(16); @@ -927,10 +925,10 @@ void __noreturn panic_bad_stack(struct pt_regs *regs, unsigned long esr, unsigne nmi_panic(NULL, "kernel stack overflow"); cpu_park_loop(); } -#endif void __noreturn arm64_serror_panic(struct pt_regs *regs, unsigned long esr) { + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); console_verbose(); pr_crit("SError Interrupt on CPU%d, code 0x%016lx -- %s\n", @@ -987,7 +985,7 @@ void do_serror(struct pt_regs *regs, unsigned long esr) int is_valid_bugaddr(unsigned long addr) { /* - * bug_handler() only called for BRK #BUG_BRK_IMM. + * bug_brk_handler() only called for BRK #BUG_BRK_IMM. * So the answer is trivial -- any spurious instances with no * bug table entry will be rejected by report_bug() and passed * back to the debug-monitors code and handled as a fatal @@ -997,7 +995,7 @@ int is_valid_bugaddr(unsigned long addr) } #endif -static int bug_handler(struct pt_regs *regs, unsigned long esr) +int bug_brk_handler(struct pt_regs *regs, unsigned long esr) { switch (report_bug(regs->pc, regs)) { case BUG_TRAP_TYPE_BUG: @@ -1017,13 +1015,8 @@ static int bug_handler(struct pt_regs *regs, unsigned long esr) return DBG_HOOK_HANDLED; } -static struct break_hook bug_break_hook = { - .fn = bug_handler, - .imm = BUG_BRK_IMM, -}; - #ifdef CONFIG_CFI_CLANG -static int cfi_handler(struct pt_regs *regs, unsigned long esr) +int cfi_brk_handler(struct pt_regs *regs, unsigned long esr) { unsigned long target; u32 type; @@ -1046,15 +1039,9 @@ static int cfi_handler(struct pt_regs *regs, unsigned long esr) arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); return DBG_HOOK_HANDLED; } - -static struct break_hook cfi_break_hook = { - .fn = cfi_handler, - .imm = CFI_BRK_IMM_BASE, - .mask = CFI_BRK_IMM_MASK, -}; #endif /* CONFIG_CFI_CLANG */ -static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr) +int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr) { pr_err("%s generated an invalid instruction at %pS!\n", "Kernel text patching", @@ -1064,11 +1051,6 @@ static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr) return DBG_HOOK_ERROR; } -static struct break_hook fault_break_hook = { - .fn = reserved_fault_handler, - .imm = FAULT_BRK_IMM, -}; - #ifdef CONFIG_KASAN_SW_TAGS #define KASAN_ESR_RECOVER 0x20 @@ -1076,7 +1058,7 @@ static struct break_hook fault_break_hook = { #define KASAN_ESR_SIZE_MASK 0x0f #define KASAN_ESR_SIZE(esr) (1 << ((esr) & KASAN_ESR_SIZE_MASK)) -static int kasan_handler(struct pt_regs *regs, unsigned long esr) +int kasan_brk_handler(struct pt_regs *regs, unsigned long esr) { bool recover = esr & KASAN_ESR_RECOVER; bool write = esr & KASAN_ESR_WRITE; @@ -1107,62 +1089,12 @@ static int kasan_handler(struct pt_regs *regs, unsigned long esr) arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); return DBG_HOOK_HANDLED; } - -static struct break_hook kasan_break_hook = { - .fn = kasan_handler, - .imm = KASAN_BRK_IMM, - .mask = KASAN_BRK_MASK, -}; #endif #ifdef CONFIG_UBSAN_TRAP -static int ubsan_handler(struct pt_regs *regs, unsigned long esr) +int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr) { die(report_ubsan_failure(esr & UBSAN_BRK_MASK), regs, esr); return DBG_HOOK_HANDLED; } - -static struct break_hook ubsan_break_hook = { - .fn = ubsan_handler, - .imm = UBSAN_BRK_IMM, - .mask = UBSAN_BRK_MASK, -}; #endif - -/* - * Initial handler for AArch64 BRK exceptions - * This handler only used until debug_traps_init(). - */ -int __init early_brk64(unsigned long addr, unsigned long esr, - struct pt_regs *regs) -{ -#ifdef CONFIG_CFI_CLANG - if (esr_is_cfi_brk(esr)) - return cfi_handler(regs, esr) != DBG_HOOK_HANDLED; -#endif -#ifdef CONFIG_KASAN_SW_TAGS - if ((esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) - return kasan_handler(regs, esr) != DBG_HOOK_HANDLED; -#endif -#ifdef CONFIG_UBSAN_TRAP - if (esr_is_ubsan_brk(esr)) - return ubsan_handler(regs, esr) != DBG_HOOK_HANDLED; -#endif - return bug_handler(regs, esr) != DBG_HOOK_HANDLED; -} - -void __init trap_init(void) -{ - register_kernel_break_hook(&bug_break_hook); -#ifdef CONFIG_CFI_CLANG - register_kernel_break_hook(&cfi_break_hook); -#endif - register_kernel_break_hook(&fault_break_hook); -#ifdef CONFIG_KASAN_SW_TAGS - register_kernel_break_hook(&kasan_break_hook); -#endif -#ifdef CONFIG_UBSAN_TRAP - register_kernel_break_hook(&ubsan_break_hook); -#endif - debug_traps_init(); -} diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index 5e27e46aa496..7dec05dd33b7 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -36,7 +36,8 @@ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO # -Wmissing-prototypes and -Wmissing-declarations are removed from # the CFLAGS to make possible to build the kernel with CONFIG_WERROR enabled. CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ - $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \ + $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) \ + $(GCC_PLUGINS_CFLAGS) \ $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ -Wmissing-prototypes -Wmissing-declarations diff --git a/arch/arm64/kernel/watchdog_hld.c b/arch/arm64/kernel/watchdog_hld.c index dcd25322127c..3093037dcb7b 100644 --- a/arch/arm64/kernel/watchdog_hld.c +++ b/arch/arm64/kernel/watchdog_hld.c @@ -34,3 +34,61 @@ bool __init arch_perf_nmi_is_available(void) */ return arm_pmu_irq_is_nmi(); } + +static int watchdog_perf_update_period(void *data) +{ + int cpu = smp_processor_id(); + u64 max_cpu_freq, new_period; + + max_cpu_freq = cpufreq_get_hw_max_freq(cpu) * 1000UL; + if (!max_cpu_freq) + return 0; + + new_period = watchdog_thresh * max_cpu_freq; + hardlockup_detector_perf_adjust_period(new_period); + + return 0; +} + +static int watchdog_freq_notifier_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_policy *policy = data; + int cpu; + + if (val != CPUFREQ_CREATE_POLICY) + return NOTIFY_DONE; + + /* + * Let each online CPU related to the policy update the period by their + * own. This will serialize with the framework on start/stop the lockup + * detector (softlockup_{start,stop}_all) and avoid potential race + * condition. Otherwise we may have below theoretical race condition: + * (core 0/1 share the same policy) + * [core 0] [core 1] + * hardlockup_detector_event_create() + * hw_nmi_get_sample_period() + * (cpufreq registered, notifier callback invoked) + * watchdog_freq_notifier_callback() + * watchdog_perf_update_period() + * (since core 1's event's not yet created, + * the period is not set) + * perf_event_create_kernel_counter() + * (event's period is SAFE_MAX_CPU_FREQ) + */ + for_each_cpu(cpu, policy->cpus) + smp_call_on_cpu(cpu, watchdog_perf_update_period, NULL, false); + + return NOTIFY_DONE; +} + +static struct notifier_block watchdog_freq_notifier = { + .notifier_call = watchdog_freq_notifier_callback, +}; + +static int __init init_watchdog_freq_notifier(void) +{ + return cpufreq_register_notifier(&watchdog_freq_notifier, + CPUFREQ_POLICY_NOTIFIER); +} +core_initcall(init_watchdog_freq_notifier); diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 7c329e01c557..3ebc0570345c 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -23,7 +23,8 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ vgic/vgic-v3.o vgic/vgic-v4.o \ vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \ vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \ - vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o + vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \ + vgic/vgic-v5.o kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 701ea10a63f1..dbd74e4885e2 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -830,7 +830,7 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map) * by the guest (either FEAT_VHE or FEAT_E2H0 is implemented, but * not both). This simplifies the handling of the EL1NV* bits. */ - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + if (is_nested_ctxt(vcpu)) { u64 val = __vcpu_sys_reg(vcpu, CNTHCTL_EL2); /* Use the VHE format for mental sanity */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index de2b4e9c9f9f..5bf101c869c9 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -408,6 +408,13 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES: r = BIT(0); break; + case KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED: + if (!kvm) + r = -EINVAL; + else + r = kvm_supports_cacheable_pfnmap(); + break; + default: r = 0; } @@ -521,7 +528,7 @@ static void vcpu_set_pauth_traps(struct kvm_vcpu *vcpu) * Either we're running an L2 guest, and the API/APK bits come * from L1's HCR_EL2, or API/APK are both set. */ - if (unlikely(vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu))) { + if (unlikely(is_nested_ctxt(vcpu))) { u64 val; val = __vcpu_sys_reg(vcpu, HCR_EL2); @@ -740,7 +747,8 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, */ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) { - bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF); + bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE); + return ((irq_lines || kvm_vgic_vcpu_pending_irq(v)) && !kvm_arm_vcpu_stopped(v) && !v->arch.pause); } @@ -825,10 +833,6 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) if (!kvm_arm_vcpu_is_finalized(vcpu)) return -EPERM; - ret = kvm_arch_vcpu_run_map_fp(vcpu); - if (ret) - return ret; - if (likely(vcpu_has_run_once(vcpu))) return 0; @@ -1187,6 +1191,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) */ preempt_disable(); + kvm_nested_flush_hwstate(vcpu); + if (kvm_vcpu_has_pmu(vcpu)) kvm_pmu_flush_hwstate(vcpu); @@ -1286,6 +1292,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) /* Exit types that need handling before we can be preempted */ handle_exit_early(vcpu, ret); + kvm_nested_sync_hwstate(vcpu); + preempt_enable(); /* @@ -2129,7 +2137,7 @@ static void cpu_hyp_init(void *discard) static void cpu_hyp_uninit(void *discard) { - if (__this_cpu_read(kvm_hyp_initialized)) { + if (!is_protected_kvm_enabled() && __this_cpu_read(kvm_hyp_initialized)) { cpu_hyp_reset(); __this_cpu_write(kvm_hyp_initialized, 0); } @@ -2345,8 +2353,13 @@ static void __init teardown_hyp_mode(void) free_hyp_pgds(); for_each_possible_cpu(cpu) { + if (per_cpu(kvm_hyp_initialized, cpu)) + continue; + free_pages(per_cpu(kvm_arm_hyp_stack_base, cpu), NVHE_STACK_SHIFT - PAGE_SHIFT); - free_pages(kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu], nvhe_percpu_order()); + + if (!kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu]) + continue; if (free_sve) { struct cpu_sve_state *sve_state; @@ -2354,6 +2367,9 @@ static void __init teardown_hyp_mode(void) sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; free_pages((unsigned long) sve_state, pkvm_host_sve_state_order()); } + + free_pages(kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu], nvhe_percpu_order()); + } } @@ -2392,12 +2408,12 @@ static u64 get_hyp_id_aa64pfr0_el1(void) */ u64 val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); - val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) | - ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3)); + val &= ~(ID_AA64PFR0_EL1_CSV2 | + ID_AA64PFR0_EL1_CSV3); - val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), + val |= FIELD_PREP(ID_AA64PFR0_EL1_CSV2, arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED); - val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), + val |= FIELD_PREP(ID_AA64PFR0_EL1_CSV3, arm64_get_meltdown_state() == SPECTRE_UNAFFECTED); return val; @@ -2761,18 +2777,15 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons, kvm_vgic_v4_unset_forwarding(irqfd->kvm, prod->irq); } -bool kvm_arch_irqfd_route_changed(struct kvm_kernel_irq_routing_entry *old, - struct kvm_kernel_irq_routing_entry *new) +void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd, + struct kvm_kernel_irq_routing_entry *old, + struct kvm_kernel_irq_routing_entry *new) { - if (new->type != KVM_IRQ_ROUTING_MSI) - return true; + if (old->type == KVM_IRQ_ROUTING_MSI && + new->type == KVM_IRQ_ROUTING_MSI && + !memcmp(&old->msi, &new->msi, sizeof(new->msi))) + return; - return memcmp(&old->msi, &new->msi, sizeof(new->msi)); -} - -int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set) -{ /* * Remapping the vLPI requires taking the its_lock mutex to resolve * the new translation. We're in spinlock land at this point, so no @@ -2780,7 +2793,7 @@ int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq, * * Unmap the vLPI and fall back to software LPI injection. */ - return kvm_vgic_v4_unset_forwarding(kvm, host_irq); + return kvm_vgic_v4_unset_forwarding(irqfd->kvm, irqfd->producer->irq); } void kvm_arch_irq_bypass_stop(struct irq_bypass_consumer *cons) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index a25be111cd8f..d71ca4ddc9d1 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1047,34 +1047,51 @@ static void compute_s1_overlay_permissions(struct kvm_vcpu *vcpu, idx = FIELD_GET(PTE_PO_IDX_MASK, wr->desc); - switch (wi->regime) { - case TR_EL10: - pov_perms = perm_idx(vcpu, POR_EL1, idx); - uov_perms = perm_idx(vcpu, POR_EL0, idx); - break; - case TR_EL20: - pov_perms = perm_idx(vcpu, POR_EL2, idx); - uov_perms = perm_idx(vcpu, POR_EL0, idx); - break; - case TR_EL2: - pov_perms = perm_idx(vcpu, POR_EL2, idx); - uov_perms = 0; - break; - } + if (wr->pov) { + switch (wi->regime) { + case TR_EL10: + pov_perms = perm_idx(vcpu, POR_EL1, idx); + break; + case TR_EL20: + pov_perms = perm_idx(vcpu, POR_EL2, idx); + break; + case TR_EL2: + pov_perms = perm_idx(vcpu, POR_EL2, idx); + break; + } - if (pov_perms & ~POE_RWX) - pov_perms = POE_NONE; + if (pov_perms & ~POE_RWX) + pov_perms = POE_NONE; + + /* R_QXXPC, S1PrivOverflow enabled */ + if (wr->pwxn && (pov_perms & POE_X)) + pov_perms &= ~POE_W; - if (wi->poe && wr->pov) { wr->pr &= pov_perms & POE_R; wr->pw &= pov_perms & POE_W; wr->px &= pov_perms & POE_X; } - if (uov_perms & ~POE_RWX) - uov_perms = POE_NONE; + if (wr->uov) { + switch (wi->regime) { + case TR_EL10: + uov_perms = perm_idx(vcpu, POR_EL0, idx); + break; + case TR_EL20: + uov_perms = perm_idx(vcpu, POR_EL0, idx); + break; + case TR_EL2: + uov_perms = 0; + break; + } + + if (uov_perms & ~POE_RWX) + uov_perms = POE_NONE; + + /* R_NPBXC, S1UnprivOverlay enabled */ + if (wr->uwxn && (uov_perms & POE_X)) + uov_perms &= ~POE_W; - if (wi->e0poe && wr->uov) { wr->ur &= uov_perms & POE_R; wr->uw &= uov_perms & POE_W; wr->ux &= uov_perms & POE_X; @@ -1095,24 +1112,15 @@ static void compute_s1_permissions(struct kvm_vcpu *vcpu, if (!wi->hpd) compute_s1_hierarchical_permissions(vcpu, wi, wr); - if (wi->poe || wi->e0poe) - compute_s1_overlay_permissions(vcpu, wi, wr); + compute_s1_overlay_permissions(vcpu, wi, wr); - /* R_QXXPC */ - if (wr->pwxn) { - if (!wr->pov && wr->pw) - wr->px = false; - if (wr->pov && wr->px) - wr->pw = false; - } + /* R_QXXPC, S1PrivOverlay disabled */ + if (!wr->pov) + wr->px &= !(wr->pwxn && wr->pw); - /* R_NPBXC */ - if (wr->uwxn) { - if (!wr->uov && wr->uw) - wr->ux = false; - if (wr->uov && wr->ux) - wr->uw = false; - } + /* R_NPBXC, S1UnprivOverlay disabled */ + if (!wr->uov) + wr->ux &= !(wr->uwxn && wr->uw); pan = wi->pan && (wr->ur || wr->uw || (pan3_enabled(vcpu, wi->regime) && wr->ux)); @@ -1412,10 +1420,10 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) return; /* - * If we only have a single stage of translation (E2H=0 or - * TGE=1), exit early. Same thing if {VM,DC}=={0,0}. + * If we only have a single stage of translation (EL2&0), exit + * early. Same thing if {VM,DC}=={0,0}. */ - if (!vcpu_el2_e2h_is_set(vcpu) || vcpu_el2_tge_is_set(vcpu) || + if (compute_translation_regime(vcpu, op) == TR_EL20 || !(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) return; diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index 54911a93b001..da66c4a14775 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -66,7 +66,6 @@ struct reg_bits_to_feat_map { #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 -#define FEAT_PMUv3p9 ID_AA64DFR0_EL1, PMUVer, V3P9 #define FEAT_TRBE ID_AA64DFR0_EL1, TraceBuffer, IMP #define FEAT_TRBEv1p1 ID_AA64DFR0_EL1, TraceBuffer, TRBE_V1P1 #define FEAT_DoubleLock ID_AA64DFR0_EL1, DoubleLock, IMP @@ -89,6 +88,7 @@ struct reg_bits_to_feat_map { #define FEAT_RASv2 ID_AA64PFR0_EL1, RAS, V2 #define FEAT_GICv3 ID_AA64PFR0_EL1, GIC, IMP #define FEAT_LOR ID_AA64MMFR1_EL1, LO, IMP +#define FEAT_SPEv1p2 ID_AA64DFR0_EL1, PMSVer, V1P2 #define FEAT_SPEv1p4 ID_AA64DFR0_EL1, PMSVer, V1P4 #define FEAT_SPEv1p5 ID_AA64DFR0_EL1, PMSVer, V1P5 #define FEAT_ATS1A ID_AA64ISAR2_EL1, ATS1A, IMP @@ -131,6 +131,27 @@ struct reg_bits_to_feat_map { #define FEAT_SPMU ID_AA64DFR1_EL1, SPMU, IMP #define FEAT_SPE_nVM ID_AA64DFR2_EL1, SPE_nVM, IMP #define FEAT_STEP2 ID_AA64DFR2_EL1, STEP, IMP +#define FEAT_SYSREG128 ID_AA64ISAR2_EL1, SYSREG_128, IMP +#define FEAT_CPA2 ID_AA64ISAR3_EL1, CPA, CPA2 +#define FEAT_ASID2 ID_AA64MMFR4_EL1, ASID2, IMP +#define FEAT_MEC ID_AA64MMFR3_EL1, MEC, IMP +#define FEAT_HAFT ID_AA64MMFR1_EL1, HAFDBS, HAFT +#define FEAT_BTI ID_AA64PFR1_EL1, BT, IMP +#define FEAT_ExS ID_AA64MMFR0_EL1, EXS, IMP +#define FEAT_IESB ID_AA64MMFR2_EL1, IESB, IMP +#define FEAT_LSE2 ID_AA64MMFR2_EL1, AT, IMP +#define FEAT_LSMAOC ID_AA64MMFR2_EL1, LSM, IMP +#define FEAT_MixedEnd ID_AA64MMFR0_EL1, BIGEND, IMP +#define FEAT_MixedEndEL0 ID_AA64MMFR0_EL1, BIGENDEL0, IMP +#define FEAT_MTE2 ID_AA64PFR1_EL1, MTE, MTE2 +#define FEAT_MTE_ASYNC ID_AA64PFR1_EL1, MTE_frac, ASYNC +#define FEAT_MTE_STORE_ONLY ID_AA64PFR2_EL1, MTESTOREONLY, IMP +#define FEAT_PAN ID_AA64MMFR1_EL1, PAN, IMP +#define FEAT_PAN3 ID_AA64MMFR1_EL1, PAN, PAN3 +#define FEAT_SSBS ID_AA64PFR1_EL1, SSBS, IMP +#define FEAT_TIDCP1 ID_AA64MMFR1_EL1, TIDCP1, IMP +#define FEAT_FGT ID_AA64MMFR0_EL1, FGT, IMP +#define FEAT_MTPMU ID_AA64DFR0_EL1, MTPMU, IMP static bool not_feat_aa64el3(struct kvm *kvm) { @@ -218,11 +239,62 @@ static bool feat_trbe_mpam(struct kvm *kvm) (read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_MPAM)); } +static bool feat_asid2_e2h1(struct kvm *kvm) +{ + return kvm_has_feat(kvm, FEAT_ASID2) && !kvm_has_feat(kvm, FEAT_E2H0); +} + +static bool feat_d128_e2h1(struct kvm *kvm) +{ + return kvm_has_feat(kvm, FEAT_D128) && !kvm_has_feat(kvm, FEAT_E2H0); +} + +static bool feat_mec_e2h1(struct kvm *kvm) +{ + return kvm_has_feat(kvm, FEAT_MEC) && !kvm_has_feat(kvm, FEAT_E2H0); +} + static bool feat_ebep_pmuv3_ss(struct kvm *kvm) { return kvm_has_feat(kvm, FEAT_EBEP) || kvm_has_feat(kvm, FEAT_PMUv3_SS); } +static bool feat_mixedendel0(struct kvm *kvm) +{ + return kvm_has_feat(kvm, FEAT_MixedEnd) || kvm_has_feat(kvm, FEAT_MixedEndEL0); +} + +static bool feat_mte_async(struct kvm *kvm) +{ + return kvm_has_feat(kvm, FEAT_MTE2) && kvm_has_feat_enum(kvm, FEAT_MTE_ASYNC); +} + +#define check_pmu_revision(k, r) \ + ({ \ + (kvm_has_feat((k), ID_AA64DFR0_EL1, PMUVer, r) && \ + !kvm_has_feat((k), ID_AA64DFR0_EL1, PMUVer, IMP_DEF)); \ + }) + +static bool feat_pmuv3p1(struct kvm *kvm) +{ + return check_pmu_revision(kvm, V3P1); +} + +static bool feat_pmuv3p5(struct kvm *kvm) +{ + return check_pmu_revision(kvm, V3P5); +} + +static bool feat_pmuv3p7(struct kvm *kvm) +{ + return check_pmu_revision(kvm, V3P7); +} + +static bool feat_pmuv3p9(struct kvm *kvm) +{ + return check_pmu_revision(kvm, V3P9); +} + static bool compute_hcr_rw(struct kvm *kvm, u64 *bits) { /* This is purely academic: AArch32 and NV are mutually exclusive */ @@ -681,7 +753,7 @@ static const struct reg_bits_to_feat_map hdfgrtr2_feat_map[] = { NEEDS_FEAT(HDFGRTR2_EL2_nPMICFILTR_EL0 | HDFGRTR2_EL2_nPMICNTR_EL0, FEAT_PMUv3_ICNTR), - NEEDS_FEAT(HDFGRTR2_EL2_nPMUACR_EL1, FEAT_PMUv3p9), + NEEDS_FEAT(HDFGRTR2_EL2_nPMUACR_EL1, feat_pmuv3p9), NEEDS_FEAT(HDFGRTR2_EL2_nPMSSCR_EL1 | HDFGRTR2_EL2_nPMSSDATA, FEAT_PMUv3_SS), @@ -713,7 +785,7 @@ static const struct reg_bits_to_feat_map hdfgwtr2_feat_map[] = { FEAT_PMUv3_ICNTR), NEEDS_FEAT(HDFGWTR2_EL2_nPMUACR_EL1 | HDFGWTR2_EL2_nPMZR_EL0, - FEAT_PMUv3p9), + feat_pmuv3p9), NEEDS_FEAT(HDFGWTR2_EL2_nPMSSCR_EL1, FEAT_PMUv3_SS), NEEDS_FEAT(HDFGWTR2_EL2_nPMIAR_EL1, FEAT_SEBEP), NEEDS_FEAT(HDFGWTR2_EL2_nPMSDSFR_EL1, feat_spe_fds), @@ -832,6 +904,150 @@ static const struct reg_bits_to_feat_map hcr_feat_map[] = { NEEDS_FEAT_FIXED(HCR_EL2_E2H, compute_hcr_e2h), }; +static const struct reg_bits_to_feat_map sctlr2_feat_map[] = { + NEEDS_FEAT(SCTLR2_EL1_NMEA | + SCTLR2_EL1_EASE, + FEAT_DoubleFault2), + NEEDS_FEAT(SCTLR2_EL1_EnADERR, feat_aderr), + NEEDS_FEAT(SCTLR2_EL1_EnANERR, feat_anerr), + NEEDS_FEAT(SCTLR2_EL1_EnIDCP128, FEAT_SYSREG128), + NEEDS_FEAT(SCTLR2_EL1_EnPACM | + SCTLR2_EL1_EnPACM0, + feat_pauth_lr), + NEEDS_FEAT(SCTLR2_EL1_CPTA | + SCTLR2_EL1_CPTA0 | + SCTLR2_EL1_CPTM | + SCTLR2_EL1_CPTM0, + FEAT_CPA2), +}; + +static const struct reg_bits_to_feat_map tcr2_el2_feat_map[] = { + NEEDS_FEAT(TCR2_EL2_FNG1 | + TCR2_EL2_FNG0 | + TCR2_EL2_A2, + feat_asid2_e2h1), + NEEDS_FEAT(TCR2_EL2_DisCH1 | + TCR2_EL2_DisCH0 | + TCR2_EL2_D128, + feat_d128_e2h1), + NEEDS_FEAT(TCR2_EL2_AMEC1, feat_mec_e2h1), + NEEDS_FEAT(TCR2_EL2_AMEC0, FEAT_MEC), + NEEDS_FEAT(TCR2_EL2_HAFT, FEAT_HAFT), + NEEDS_FEAT(TCR2_EL2_PTTWI | + TCR2_EL2_PnCH, + FEAT_THE), + NEEDS_FEAT(TCR2_EL2_AIE, FEAT_AIE), + NEEDS_FEAT(TCR2_EL2_POE | + TCR2_EL2_E0POE, + FEAT_S1POE), + NEEDS_FEAT(TCR2_EL2_PIE, FEAT_S1PIE), +}; + +static const struct reg_bits_to_feat_map sctlr_el1_feat_map[] = { + NEEDS_FEAT(SCTLR_EL1_CP15BEN | + SCTLR_EL1_ITD | + SCTLR_EL1_SED, + FEAT_AA32EL0), + NEEDS_FEAT(SCTLR_EL1_BT0 | + SCTLR_EL1_BT1, + FEAT_BTI), + NEEDS_FEAT(SCTLR_EL1_CMOW, FEAT_CMOW), + NEEDS_FEAT(SCTLR_EL1_TSCXT, feat_csv2_2_csv2_1p2), + NEEDS_FEAT(SCTLR_EL1_EIS | + SCTLR_EL1_EOS, + FEAT_ExS), + NEEDS_FEAT(SCTLR_EL1_EnFPM, FEAT_FPMR), + NEEDS_FEAT(SCTLR_EL1_IESB, FEAT_IESB), + NEEDS_FEAT(SCTLR_EL1_EnALS, FEAT_LS64), + NEEDS_FEAT(SCTLR_EL1_EnAS0, FEAT_LS64_ACCDATA), + NEEDS_FEAT(SCTLR_EL1_EnASR, FEAT_LS64_V), + NEEDS_FEAT(SCTLR_EL1_nAA, FEAT_LSE2), + NEEDS_FEAT(SCTLR_EL1_LSMAOE | + SCTLR_EL1_nTLSMD, + FEAT_LSMAOC), + NEEDS_FEAT(SCTLR_EL1_EE, FEAT_MixedEnd), + NEEDS_FEAT(SCTLR_EL1_E0E, feat_mixedendel0), + NEEDS_FEAT(SCTLR_EL1_MSCEn, FEAT_MOPS), + NEEDS_FEAT(SCTLR_EL1_ATA0 | + SCTLR_EL1_ATA | + SCTLR_EL1_TCF0 | + SCTLR_EL1_TCF, + FEAT_MTE2), + NEEDS_FEAT(SCTLR_EL1_ITFSB, feat_mte_async), + NEEDS_FEAT(SCTLR_EL1_TCSO0 | + SCTLR_EL1_TCSO, + FEAT_MTE_STORE_ONLY), + NEEDS_FEAT(SCTLR_EL1_NMI | + SCTLR_EL1_SPINTMASK, + FEAT_NMI), + NEEDS_FEAT(SCTLR_EL1_SPAN, FEAT_PAN), + NEEDS_FEAT(SCTLR_EL1_EPAN, FEAT_PAN3), + NEEDS_FEAT(SCTLR_EL1_EnDA | + SCTLR_EL1_EnDB | + SCTLR_EL1_EnIA | + SCTLR_EL1_EnIB, + feat_pauth), + NEEDS_FEAT(SCTLR_EL1_EnTP2, FEAT_SME), + NEEDS_FEAT(SCTLR_EL1_EnRCTX, FEAT_SPECRES), + NEEDS_FEAT(SCTLR_EL1_DSSBS, FEAT_SSBS), + NEEDS_FEAT(SCTLR_EL1_TIDCP, FEAT_TIDCP1), + NEEDS_FEAT(SCTLR_EL1_TME0 | + SCTLR_EL1_TME | + SCTLR_EL1_TMT0 | + SCTLR_EL1_TMT, + FEAT_TME), + NEEDS_FEAT(SCTLR_EL1_TWEDEL | + SCTLR_EL1_TWEDEn, + FEAT_TWED), + NEEDS_FEAT(SCTLR_EL1_UCI | + SCTLR_EL1_EE | + SCTLR_EL1_E0E | + SCTLR_EL1_WXN | + SCTLR_EL1_nTWE | + SCTLR_EL1_nTWI | + SCTLR_EL1_UCT | + SCTLR_EL1_DZE | + SCTLR_EL1_I | + SCTLR_EL1_UMA | + SCTLR_EL1_SA0 | + SCTLR_EL1_SA | + SCTLR_EL1_C | + SCTLR_EL1_A | + SCTLR_EL1_M, + FEAT_AA64EL1), +}; + +static const struct reg_bits_to_feat_map mdcr_el2_feat_map[] = { + NEEDS_FEAT(MDCR_EL2_EBWE, FEAT_Debugv8p9), + NEEDS_FEAT(MDCR_EL2_TDOSA, FEAT_DoubleLock), + NEEDS_FEAT(MDCR_EL2_PMEE, FEAT_EBEP), + NEEDS_FEAT(MDCR_EL2_TDCC, FEAT_FGT), + NEEDS_FEAT(MDCR_EL2_MTPME, FEAT_MTPMU), + NEEDS_FEAT(MDCR_EL2_HPME | + MDCR_EL2_HPMN | + MDCR_EL2_TPMCR | + MDCR_EL2_TPM, + FEAT_PMUv3), + NEEDS_FEAT(MDCR_EL2_HPMD, feat_pmuv3p1), + NEEDS_FEAT(MDCR_EL2_HCCD | + MDCR_EL2_HLP, + feat_pmuv3p5), + NEEDS_FEAT(MDCR_EL2_HPMFZO, feat_pmuv3p7), + NEEDS_FEAT(MDCR_EL2_PMSSE, FEAT_PMUv3_SS), + NEEDS_FEAT(MDCR_EL2_E2PB | + MDCR_EL2_TPMS, + FEAT_SPE), + NEEDS_FEAT(MDCR_EL2_HPMFZS, FEAT_SPEv1p2), + NEEDS_FEAT(MDCR_EL2_EnSPM, FEAT_SPMU), + NEEDS_FEAT(MDCR_EL2_EnSTEPOP, FEAT_STEP2), + NEEDS_FEAT(MDCR_EL2_E2TB, FEAT_TRBE), + NEEDS_FEAT(MDCR_EL2_TTRF, FEAT_TRF), + NEEDS_FEAT(MDCR_EL2_TDA | + MDCR_EL2_TDE | + MDCR_EL2_TDRA, + FEAT_AA64EL1), +}; + static void __init check_feat_map(const struct reg_bits_to_feat_map *map, int map_size, u64 res0, const char *str) { @@ -863,6 +1079,14 @@ void __init check_feature_map(void) __HCRX_EL2_RES0, "HCRX_EL2"); check_feat_map(hcr_feat_map, ARRAY_SIZE(hcr_feat_map), HCR_EL2_RES0, "HCR_EL2"); + check_feat_map(sctlr2_feat_map, ARRAY_SIZE(sctlr2_feat_map), + SCTLR2_EL1_RES0, "SCTLR2_EL1"); + check_feat_map(tcr2_el2_feat_map, ARRAY_SIZE(tcr2_el2_feat_map), + TCR2_EL2_RES0, "TCR2_EL2"); + check_feat_map(sctlr_el1_feat_map, ARRAY_SIZE(sctlr_el1_feat_map), + SCTLR_EL1_RES0, "SCTLR_EL1"); + check_feat_map(mdcr_el2_feat_map, ARRAY_SIZE(mdcr_el2_feat_map), + MDCR_EL2_RES0, "MDCR_EL2"); } static bool idreg_feat_match(struct kvm *kvm, const struct reg_bits_to_feat_map *map) @@ -1077,6 +1301,31 @@ void get_reg_fixed_bits(struct kvm *kvm, enum vcpu_sysreg reg, u64 *res0, u64 *r *res0 |= HCR_EL2_RES0 | (mask & ~fixed); *res1 = HCR_EL2_RES1 | (mask & fixed); break; + case SCTLR2_EL1: + case SCTLR2_EL2: + *res0 = compute_res0_bits(kvm, sctlr2_feat_map, + ARRAY_SIZE(sctlr2_feat_map), 0, 0); + *res0 |= SCTLR2_EL1_RES0; + *res1 = SCTLR2_EL1_RES1; + break; + case TCR2_EL2: + *res0 = compute_res0_bits(kvm, tcr2_el2_feat_map, + ARRAY_SIZE(tcr2_el2_feat_map), 0, 0); + *res0 |= TCR2_EL2_RES0; + *res1 = TCR2_EL2_RES1; + break; + case SCTLR_EL1: + *res0 = compute_res0_bits(kvm, sctlr_el1_feat_map, + ARRAY_SIZE(sctlr_el1_feat_map), 0, 0); + *res0 |= SCTLR_EL1_RES0; + *res1 = SCTLR_EL1_RES1; + break; + case MDCR_EL2: + *res0 = compute_res0_bits(kvm, mdcr_el2_feat_map, + ARRAY_SIZE(mdcr_el2_feat_map), 0, 0); + *res0 |= MDCR_EL2_RES0; + *res1 = MDCR_EL2_RES1; + break; default: WARN_ON_ONCE(1); *res0 = *res1 = 0; diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index 1a7dab333f55..381382c19fe4 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -81,6 +81,10 @@ void kvm_init_host_debug_data(void) !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P)) host_data_set_flag(HAS_SPE); + /* Check if we have BRBE implemented and available at the host */ + if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT)) + host_data_set_flag(HAS_BRBE); + if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceFilt_SHIFT)) { /* Force disable trace in protected mode in case of no TRBE */ if (is_protected_kvm_enabled()) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 3a384e9660b8..af69c897c2c3 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -88,6 +88,7 @@ enum cgt_group_id { CGT_HCRX_EnFPM, CGT_HCRX_TCR2En, + CGT_HCRX_SCTLR2En, CGT_CNTHCTL_EL1TVT, CGT_CNTHCTL_EL1TVCT, @@ -108,6 +109,7 @@ enum cgt_group_id { CGT_HCR_TTLB_TTLBOS, CGT_HCR_TVM_TRVM, CGT_HCR_TVM_TRVM_HCRX_TCR2En, + CGT_HCR_TVM_TRVM_HCRX_SCTLR2En, CGT_HCR_TPU_TICAB, CGT_HCR_TPU_TOCU, CGT_HCR_NV1_nNV2_ENSCXT, @@ -398,6 +400,12 @@ static const struct trap_bits coarse_trap_bits[] = { .mask = HCRX_EL2_TCR2En, .behaviour = BEHAVE_FORWARD_RW, }, + [CGT_HCRX_SCTLR2En] = { + .index = HCRX_EL2, + .value = 0, + .mask = HCRX_EL2_SCTLR2En, + .behaviour = BEHAVE_FORWARD_RW, + }, [CGT_CNTHCTL_EL1TVT] = { .index = CNTHCTL_EL2, .value = CNTHCTL_EL1TVT, @@ -449,6 +457,8 @@ static const enum cgt_group_id *coarse_control_combo[] = { MCB(CGT_HCR_TVM_TRVM, CGT_HCR_TVM, CGT_HCR_TRVM), MCB(CGT_HCR_TVM_TRVM_HCRX_TCR2En, CGT_HCR_TVM, CGT_HCR_TRVM, CGT_HCRX_TCR2En), + MCB(CGT_HCR_TVM_TRVM_HCRX_SCTLR2En, + CGT_HCR_TVM, CGT_HCR_TRVM, CGT_HCRX_SCTLR2En), MCB(CGT_HCR_TPU_TICAB, CGT_HCR_TPU, CGT_HCR_TICAB), MCB(CGT_HCR_TPU_TOCU, CGT_HCR_TPU, CGT_HCR_TOCU), MCB(CGT_HCR_NV1_nNV2_ENSCXT, CGT_HCR_NV1_nNV2, CGT_HCR_ENSCXT), @@ -782,6 +792,7 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = { SR_TRAP(OP_TLBI_RVALE1OSNXS, CGT_HCR_TTLB_TTLBOS), SR_TRAP(OP_TLBI_RVAALE1OSNXS, CGT_HCR_TTLB_TTLBOS), SR_TRAP(SYS_SCTLR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_SCTLR2_EL1, CGT_HCR_TVM_TRVM_HCRX_SCTLR2En), SR_TRAP(SYS_TTBR0_EL1, CGT_HCR_TVM_TRVM), SR_TRAP(SYS_TTBR1_EL1, CGT_HCR_TVM_TRVM), SR_TRAP(SYS_TCR_EL1, CGT_HCR_TVM_TRVM), @@ -1354,6 +1365,7 @@ static const struct encoding_to_trap_config encoding_to_fgt[] __initconst = { SR_FGT(SYS_SCXTNUM_EL0, HFGRTR, SCXTNUM_EL0, 1), SR_FGT(SYS_SCXTNUM_EL1, HFGRTR, SCXTNUM_EL1, 1), SR_FGT(SYS_SCTLR_EL1, HFGRTR, SCTLR_EL1, 1), + SR_FGT(SYS_SCTLR2_EL1, HFGRTR, SCTLR_EL1, 1), SR_FGT(SYS_REVIDR_EL1, HFGRTR, REVIDR_EL1, 1), SR_FGT(SYS_PAR_EL1, HFGRTR, PAR_EL1, 1), SR_FGT(SYS_MPIDR_EL1, HFGRTR, MPIDR_EL1, 1), @@ -2592,13 +2604,8 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index) static bool __forward_traps(struct kvm_vcpu *vcpu, unsigned int reg, u64 control_bit) { - bool control_bit_set; - - if (!vcpu_has_nv(vcpu)) - return false; - - control_bit_set = __vcpu_sys_reg(vcpu, reg) & control_bit; - if (!is_hyp_ctxt(vcpu) && control_bit_set) { + if (is_nested_ctxt(vcpu) && + (__vcpu_sys_reg(vcpu, reg) & control_bit)) { kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu)); return true; } @@ -2719,6 +2726,9 @@ static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2, case except_type_irq: kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_IRQ); break; + case except_type_serror: + kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR); + break; default: WARN_ONCE(1, "Unsupported EL2 exception injection %d\n", type); } @@ -2816,3 +2826,28 @@ int kvm_inject_nested_irq(struct kvm_vcpu *vcpu) /* esr_el2 value doesn't matter for exits due to irqs. */ return kvm_inject_nested(vcpu, 0, except_type_irq); } + +int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr) +{ + u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, + iabt ? ESR_ELx_EC_IABT_LOW : ESR_ELx_EC_DABT_LOW); + esr |= ESR_ELx_FSC_EXTABT | ESR_ELx_IL; + + vcpu_write_sys_reg(vcpu, addr, FAR_EL2); + + if (__vcpu_sys_reg(vcpu, SCTLR2_EL2) & SCTLR2_EL1_EASE) + return kvm_inject_nested(vcpu, esr, except_type_serror); + + return kvm_inject_nested_sync(vcpu, esr); +} + +int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr) +{ + /* + * Hardware sets up the EC field when propagating ESR as a result of + * vSError injection. Manually populate EC for an emulated SError + * exception. + */ + esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR); + return kvm_inject_nested(vcpu, esr, except_type_serror); +} diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index 8f6c8f57c6b9..15e17aca1dec 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -14,32 +14,6 @@ #include #include -/* - * Called on entry to KVM_RUN unless this vcpu previously ran at least - * once and the most recent prior KVM_RUN for this vcpu was called from - * the same task as current (highly likely). - * - * This is guaranteed to execute before kvm_arch_vcpu_load_fp(vcpu), - * such that on entering hyp the relevant parts of current are already - * mapped. - */ -int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu) -{ - struct user_fpsimd_state *fpsimd = ¤t->thread.uw.fpsimd_state; - int ret; - - /* pKVM has its own tracking of the host fpsimd state. */ - if (is_protected_kvm_enabled()) - return 0; - - /* Make sure the host task fpsimd state is visible to hyp: */ - ret = kvm_share_hyp(fpsimd, fpsimd + 1); - if (ret) - return ret; - - return 0; -} - /* * Prepare vcpu for saving the host's FPSIMD state and loading the guest's. * The actual loading is done by the FPSIMD access trap taken to hyp. diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 2196979a24a3..16ba5e9ac86c 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -818,8 +818,9 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu, struct kvm_vcpu_events *events) { - events->exception.serror_pending = !!(vcpu->arch.hcr_el2 & HCR_VSE); events->exception.serror_has_esr = cpus_have_final_cap(ARM64_HAS_RAS_EXTN); + events->exception.serror_pending = (vcpu->arch.hcr_el2 & HCR_VSE) || + vcpu_get_flag(vcpu, NESTED_SERROR_PENDING); if (events->exception.serror_pending && events->exception.serror_has_esr) events->exception.serror_esr = vcpu_get_vsesr(vcpu); @@ -833,29 +834,62 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu, return 0; } +static void commit_pending_events(struct kvm_vcpu *vcpu) +{ + if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION)) + return; + + /* + * Reset the MMIO emulation state to avoid stepping PC after emulating + * the exception entry. + */ + vcpu->mmio_needed = false; + kvm_call_hyp(__kvm_adjust_pc, vcpu); +} + int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, struct kvm_vcpu_events *events) { bool serror_pending = events->exception.serror_pending; bool has_esr = events->exception.serror_has_esr; bool ext_dabt_pending = events->exception.ext_dabt_pending; + u64 esr = events->exception.serror_esr; + int ret = 0; - if (serror_pending && has_esr) { - if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) - return -EINVAL; - - if (!((events->exception.serror_esr) & ~ESR_ELx_ISS_MASK)) - kvm_set_sei_esr(vcpu, events->exception.serror_esr); - else - return -EINVAL; - } else if (serror_pending) { - kvm_inject_vabt(vcpu); + /* + * Immediately commit the pending SEA to the vCPU's architectural + * state which is necessary since we do not return a pending SEA + * to userspace via KVM_GET_VCPU_EVENTS. + */ + if (ext_dabt_pending) { + ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); + commit_pending_events(vcpu); } - if (ext_dabt_pending) - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); + if (ret < 0) + return ret; - return 0; + if (!serror_pending) + return 0; + + if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && has_esr) + return -EINVAL; + + if (has_esr && (esr & ~ESR_ELx_ISS_MASK)) + return -EINVAL; + + if (has_esr) + ret = kvm_inject_serror_esr(vcpu, esr); + else + ret = kvm_inject_serror(vcpu); + + /* + * We could've decided that the SError is due for immediate software + * injection; commit the exception in case userspace decides it wants + * to inject more exceptions for some strange reason. + */ + commit_pending_events(vcpu); + return (ret < 0) ? ret : 0; } u32 __attribute_const__ kvm_target_cpu(void) diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 453266c96481..a598072f36d2 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -32,7 +32,7 @@ typedef int (*exit_handle_fn)(struct kvm_vcpu *); static void kvm_handle_guest_serror(struct kvm_vcpu *vcpu, u64 esr) { if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(NULL, esr)) - kvm_inject_vabt(vcpu); + kvm_inject_serror(vcpu); } static int handle_hvc(struct kvm_vcpu *vcpu) @@ -252,7 +252,7 @@ static int kvm_handle_ptrauth(struct kvm_vcpu *vcpu) return 1; } - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + if (is_nested_ctxt(vcpu)) { kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu)); return 1; } @@ -311,12 +311,11 @@ static int kvm_handle_gcs(struct kvm_vcpu *vcpu) static int handle_other(struct kvm_vcpu *vcpu) { - bool is_l2 = vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu); + bool allowed, fwd = is_nested_ctxt(vcpu); u64 hcrx = __vcpu_sys_reg(vcpu, HCRX_EL2); u64 esr = kvm_vcpu_get_esr(vcpu); u64 iss = ESR_ELx_ISS(esr); struct kvm *kvm = vcpu->kvm; - bool allowed, fwd = false; /* * We only trap for two reasons: @@ -335,28 +334,23 @@ static int handle_other(struct kvm_vcpu *vcpu) switch (iss) { case ESR_ELx_ISS_OTHER_ST64BV: allowed = kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64_V); - if (is_l2) - fwd = !(hcrx & HCRX_EL2_EnASR); + fwd &= !(hcrx & HCRX_EL2_EnASR); break; case ESR_ELx_ISS_OTHER_ST64BV0: allowed = kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64_ACCDATA); - if (is_l2) - fwd = !(hcrx & HCRX_EL2_EnAS0); + fwd &= !(hcrx & HCRX_EL2_EnAS0); break; case ESR_ELx_ISS_OTHER_LDST64B: allowed = kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64); - if (is_l2) - fwd = !(hcrx & HCRX_EL2_EnALS); + fwd &= !(hcrx & HCRX_EL2_EnALS); break; case ESR_ELx_ISS_OTHER_TSBCSYNC: allowed = kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, TRBE_V1P1); - if (is_l2) - fwd = (__vcpu_sys_reg(vcpu, HFGITR2_EL2) & HFGITR2_EL2_TSBCSYNC); + fwd &= (__vcpu_sys_reg(vcpu, HFGITR2_EL2) & HFGITR2_EL2_TSBCSYNC); break; case ESR_ELx_ISS_OTHER_PSBCSYNC: allowed = kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, V1P5); - if (is_l2) - fwd = (__vcpu_sys_reg(vcpu, HFGITR_EL2) & HFGITR_EL2_PSBCSYNC); + fwd &= (__vcpu_sys_reg(vcpu, HFGITR_EL2) & HFGITR_EL2_PSBCSYNC); break; default: /* Clearly, we're missing something. */ @@ -496,7 +490,7 @@ void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index) kvm_handle_guest_serror(vcpu, disr_to_esr(disr)); } else { - kvm_inject_vabt(vcpu); + kvm_inject_serror(vcpu); } return; diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c index 6a2a899a344e..bef40ddb16db 100644 --- a/arch/arm64/kvm/hyp/exception.c +++ b/arch/arm64/kvm/hyp/exception.c @@ -22,34 +22,28 @@ static inline u64 __vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) { - u64 val; - - if (unlikely(vcpu_has_nv(vcpu))) + if (has_vhe()) return vcpu_read_sys_reg(vcpu, reg); - else if (__vcpu_read_sys_reg_from_cpu(reg, &val)) - return val; return __vcpu_sys_reg(vcpu, reg); } static inline void __vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg) { - if (unlikely(vcpu_has_nv(vcpu))) + if (has_vhe()) vcpu_write_sys_reg(vcpu, val, reg); - else if (!__vcpu_write_sys_reg_to_cpu(val, reg)) + else __vcpu_assign_sys_reg(vcpu, reg, val); } static void __vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long target_mode, u64 val) { - if (unlikely(vcpu_has_nv(vcpu))) { + if (has_vhe()) { if (target_mode == PSR_MODE_EL1h) vcpu_write_sys_reg(vcpu, val, SPSR_EL1); else vcpu_write_sys_reg(vcpu, val, SPSR_EL2); - } else if (has_vhe()) { - write_sysreg_el1(val, SYS_SPSR); } else { __vcpu_assign_sys_reg(vcpu, SPSR_EL1, val); } @@ -57,7 +51,7 @@ static void __vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long target_mode, static void __vcpu_write_spsr_abt(struct kvm_vcpu *vcpu, u64 val) { - if (has_vhe()) + if (has_vhe() && vcpu_get_flag(vcpu, SYSREGS_ON_CPU)) write_sysreg(val, spsr_abt); else vcpu->arch.ctxt.spsr_abt = val; @@ -65,7 +59,7 @@ static void __vcpu_write_spsr_abt(struct kvm_vcpu *vcpu, u64 val) static void __vcpu_write_spsr_und(struct kvm_vcpu *vcpu, u64 val) { - if (has_vhe()) + if (has_vhe() && vcpu_get_flag(vcpu, SYSREGS_ON_CPU)) write_sysreg(val, spsr_und); else vcpu->arch.ctxt.spsr_und = val; @@ -339,6 +333,10 @@ static void kvm_inject_exception(struct kvm_vcpu *vcpu) enter_exception64(vcpu, PSR_MODE_EL1h, except_type_sync); break; + case unpack_vcpu_flag(EXCEPT_AA64_EL1_SERR): + enter_exception64(vcpu, PSR_MODE_EL1h, except_type_serror); + break; + case unpack_vcpu_flag(EXCEPT_AA64_EL2_SYNC): enter_exception64(vcpu, PSR_MODE_EL2h, except_type_sync); break; @@ -347,9 +345,13 @@ static void kvm_inject_exception(struct kvm_vcpu *vcpu) enter_exception64(vcpu, PSR_MODE_EL2h, except_type_irq); break; + case unpack_vcpu_flag(EXCEPT_AA64_EL2_SERR): + enter_exception64(vcpu, PSR_MODE_EL2h, except_type_serror); + break; + default: /* - * Only EL1_SYNC and EL2_{SYNC,IRQ} makes + * Only EL1_{SYNC,SERR} and EL2_{SYNC,IRQ,SERR} makes * sense so far. Everything else gets silently * ignored. */ diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 76dfda116e56..84ec4e100fbb 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -65,6 +65,136 @@ static inline void __activate_traps_fpsimd32(struct kvm_vcpu *vcpu) } } +static inline void __activate_cptr_traps_nvhe(struct kvm_vcpu *vcpu) +{ + u64 val = CPTR_NVHE_EL2_RES1 | CPTR_EL2_TAM | CPTR_EL2_TTA; + + /* + * Always trap SME since it's not supported in KVM. + * TSM is RES1 if SME isn't implemented. + */ + val |= CPTR_EL2_TSM; + + if (!vcpu_has_sve(vcpu) || !guest_owns_fp_regs()) + val |= CPTR_EL2_TZ; + + if (!guest_owns_fp_regs()) + val |= CPTR_EL2_TFP; + + write_sysreg(val, cptr_el2); +} + +static inline void __activate_cptr_traps_vhe(struct kvm_vcpu *vcpu) +{ + /* + * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to + * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2, + * except for some missing controls, such as TAM. + * In this case, CPTR_EL2.TAM has the same position with or without + * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM + * shift value for trapping the AMU accesses. + */ + u64 val = CPTR_EL2_TAM | CPACR_EL1_TTA; + u64 cptr; + + if (guest_owns_fp_regs()) { + val |= CPACR_EL1_FPEN; + if (vcpu_has_sve(vcpu)) + val |= CPACR_EL1_ZEN; + } + + if (!vcpu_has_nv(vcpu)) + goto write; + + /* + * The architecture is a bit crap (what a surprise): an EL2 guest + * writing to CPTR_EL2 via CPACR_EL1 can't set any of TCPAC or TTA, + * as they are RES0 in the guest's view. To work around it, trap the + * sucker using the very same bit it can't set... + */ + if (vcpu_el2_e2h_is_set(vcpu) && is_hyp_ctxt(vcpu)) + val |= CPTR_EL2_TCPAC; + + /* + * Layer the guest hypervisor's trap configuration on top of our own if + * we're in a nested context. + */ + if (is_hyp_ctxt(vcpu)) + goto write; + + cptr = vcpu_sanitised_cptr_el2(vcpu); + + /* + * Pay attention, there's some interesting detail here. + * + * The CPTR_EL2.xEN fields are 2 bits wide, although there are only two + * meaningful trap states when HCR_EL2.TGE = 0 (running a nested guest): + * + * - CPTR_EL2.xEN = x0, traps are enabled + * - CPTR_EL2.xEN = x1, traps are disabled + * + * In other words, bit[0] determines if guest accesses trap or not. In + * the interest of simplicity, clear the entire field if the guest + * hypervisor has traps enabled to dispel any illusion of something more + * complicated taking place. + */ + if (!(SYS_FIELD_GET(CPACR_EL1, FPEN, cptr) & BIT(0))) + val &= ~CPACR_EL1_FPEN; + if (!(SYS_FIELD_GET(CPACR_EL1, ZEN, cptr) & BIT(0))) + val &= ~CPACR_EL1_ZEN; + + if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S2POE, IMP)) + val |= cptr & CPACR_EL1_E0POE; + + val |= cptr & CPTR_EL2_TCPAC; + +write: + write_sysreg(val, cpacr_el1); +} + +static inline void __activate_cptr_traps(struct kvm_vcpu *vcpu) +{ + if (!guest_owns_fp_regs()) + __activate_traps_fpsimd32(vcpu); + + if (has_vhe() || has_hvhe()) + __activate_cptr_traps_vhe(vcpu); + else + __activate_cptr_traps_nvhe(vcpu); +} + +static inline void __deactivate_cptr_traps_nvhe(struct kvm_vcpu *vcpu) +{ + u64 val = CPTR_NVHE_EL2_RES1; + + if (!cpus_have_final_cap(ARM64_SVE)) + val |= CPTR_EL2_TZ; + if (!cpus_have_final_cap(ARM64_SME)) + val |= CPTR_EL2_TSM; + + write_sysreg(val, cptr_el2); +} + +static inline void __deactivate_cptr_traps_vhe(struct kvm_vcpu *vcpu) +{ + u64 val = CPACR_EL1_FPEN; + + if (cpus_have_final_cap(ARM64_SVE)) + val |= CPACR_EL1_ZEN; + if (cpus_have_final_cap(ARM64_SME)) + val |= CPACR_EL1_SMEN; + + write_sysreg(val, cpacr_el1); +} + +static inline void __deactivate_cptr_traps(struct kvm_vcpu *vcpu) +{ + if (has_vhe() || has_hvhe()) + __deactivate_cptr_traps_vhe(vcpu); + else + __deactivate_cptr_traps_nvhe(vcpu); +} + #define reg_to_fgt_masks(reg) \ ({ \ struct fgt_masks *m; \ @@ -168,7 +298,7 @@ static inline void __activate_traps_fpsimd32(struct kvm_vcpu *vcpu) u64 val; \ \ ctxt_sys_reg(hctxt, reg) = read_sysreg_s(SYS_ ## reg); \ - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) \ + if (is_nested_ctxt(vcpu)) \ compute_clr_set(vcpu, reg, c, s); \ \ compute_undef_clr_set(vcpu, kvm, reg, c, s); \ @@ -306,7 +436,7 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) if (cpus_have_final_cap(ARM64_HAS_HCX)) { u64 hcrx = vcpu->arch.hcrx_el2; - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + if (is_nested_ctxt(vcpu)) { u64 val = __vcpu_sys_reg(vcpu, HCRX_EL2); hcrx |= val & __HCRX_EL2_MASK; hcrx &= ~(~val & __HCRX_EL2_nMASK); @@ -346,21 +476,56 @@ static inline void ___activate_traps(struct kvm_vcpu *vcpu, u64 hcr) write_sysreg_hcr(hcr); - if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE)) - write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2); + if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE)) { + u64 vsesr; + + /* + * When HCR_EL2.AMO is set, physical SErrors are taken to EL2 + * and vSError injection is enabled for EL1. Conveniently, for + * NV this means that it is never the case where a 'physical' + * SError (injected by KVM or userspace) and vSError are + * deliverable to the same context. + * + * As such, we can trivially select between the host or guest's + * VSESR_EL2. Except for the case that FEAT_RAS hasn't been + * exposed to the guest, where ESR propagation in hardware + * occurs unconditionally. + * + * Paper over the architectural wart and use an IMPLEMENTATION + * DEFINED ESR value in case FEAT_RAS is hidden from the guest. + */ + if (!vserror_state_is_nested(vcpu)) + vsesr = vcpu->arch.vsesr_el2; + else if (kvm_has_ras(kern_hyp_va(vcpu->kvm))) + vsesr = __vcpu_sys_reg(vcpu, VSESR_EL2); + else + vsesr = ESR_ELx_ISV; + + write_sysreg_s(vsesr, SYS_VSESR_EL2); + } } static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) { + u64 *hcr; + + if (vserror_state_is_nested(vcpu)) + hcr = __ctxt_sys_reg(&vcpu->arch.ctxt, HCR_EL2); + else + hcr = &vcpu->arch.hcr_el2; + /* * If we pended a virtual abort, preserve it until it gets * cleared. See D1.14.3 (Virtual Interrupts) for details, but * the crucial bit is "On taking a vSError interrupt, * HCR_EL2.VSE is cleared to 0." + * + * Additionally, when in a nested context we need to propagate the + * updated state to the guest hypervisor's HCR_EL2. */ - if (vcpu->arch.hcr_el2 & HCR_VSE) { - vcpu->arch.hcr_el2 &= ~HCR_VSE; - vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE; + if (*hcr & HCR_VSE) { + *hcr &= ~HCR_VSE; + *hcr |= read_sysreg(hcr_el2) & HCR_VSE; } } @@ -401,7 +566,7 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) * nested guest, as the guest hypervisor could select a smaller VL. Slap * that into hardware before wrapping up. */ - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) + if (is_nested_ctxt(vcpu)) sve_cond_update_zcr_vq(__vcpu_sys_reg(vcpu, ZCR_EL2), SYS_ZCR_EL2); write_sysreg_el1(__vcpu_sys_reg(vcpu, vcpu_sve_zcr_elx(vcpu)), SYS_ZCR); @@ -427,7 +592,7 @@ static inline void fpsimd_lazy_switch_to_guest(struct kvm_vcpu *vcpu) if (vcpu_has_sve(vcpu)) { /* A guest hypervisor may restrict the effective max VL. */ - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) + if (is_nested_ctxt(vcpu)) zcr_el2 = __vcpu_sys_reg(vcpu, ZCR_EL2); else zcr_el2 = vcpu_sve_max_vq(vcpu) - 1; @@ -486,11 +651,6 @@ static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) */ if (system_supports_sve()) { __hyp_sve_save_host(); - - /* Re-enable SVE traps if not supported for the guest vcpu. */ - if (!vcpu_has_sve(vcpu)) - cpacr_clear_set(CPACR_EL1_ZEN, 0); - } else { __fpsimd_save_state(host_data_ptr(host_ctxt.fp_regs)); } @@ -541,10 +701,7 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) /* Valid trap. Switch the context: */ /* First disable enough traps to allow us to update the registers */ - if (sve_guest || (is_protected_kvm_enabled() && system_supports_sve())) - cpacr_clear_set(0, CPACR_EL1_FPEN | CPACR_EL1_ZEN); - else - cpacr_clear_set(0, CPACR_EL1_FPEN); + __deactivate_cptr_traps(vcpu); isb(); /* Write out the host state if it's in the registers */ @@ -566,6 +723,13 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) *host_data_ptr(fp_owner) = FP_STATE_GUEST_OWNED; + /* + * Re-enable traps necessary for the current state of the guest, e.g. + * those enabled by a guest hypervisor. The ERET to the guest will + * provide the necessary context synchronization. + */ + __activate_cptr_traps(vcpu); + return true; } diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h index 4d0dbea4c56f..a17cbe7582de 100644 --- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h +++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h @@ -109,6 +109,28 @@ static inline bool ctxt_has_s1poe(struct kvm_cpu_context *ctxt) return kvm_has_s1poe(kern_hyp_va(vcpu->kvm)); } +static inline bool ctxt_has_ras(struct kvm_cpu_context *ctxt) +{ + struct kvm_vcpu *vcpu; + + if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) + return false; + + vcpu = ctxt_to_vcpu(ctxt); + return kvm_has_ras(kern_hyp_va(vcpu->kvm)); +} + +static inline bool ctxt_has_sctlr2(struct kvm_cpu_context *ctxt) +{ + struct kvm_vcpu *vcpu; + + if (!cpus_have_final_cap(ARM64_HAS_SCTLR2)) + return false; + + vcpu = ctxt_to_vcpu(ctxt); + return kvm_has_sctlr2(kern_hyp_va(vcpu->kvm)); +} + static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) { ctxt_sys_reg(ctxt, SCTLR_EL1) = read_sysreg_el1(SYS_SCTLR); @@ -147,6 +169,9 @@ static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) ctxt_sys_reg(ctxt, SP_EL1) = read_sysreg(sp_el1); ctxt_sys_reg(ctxt, ELR_EL1) = read_sysreg_el1(SYS_ELR); ctxt_sys_reg(ctxt, SPSR_EL1) = read_sysreg_el1(SYS_SPSR); + + if (ctxt_has_sctlr2(ctxt)) + ctxt_sys_reg(ctxt, SCTLR2_EL1) = read_sysreg_el1(SYS_SCTLR2); } static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) @@ -159,8 +184,13 @@ static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) if (!has_vhe() && ctxt->__hyp_running_vcpu) ctxt->regs.pstate = read_sysreg_el2(SYS_SPSR); - if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) + if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) + return; + + if (!vserror_state_is_nested(ctxt_to_vcpu(ctxt))) ctxt_sys_reg(ctxt, DISR_EL1) = read_sysreg_s(SYS_VDISR_EL2); + else if (ctxt_has_ras(ctxt)) + ctxt_sys_reg(ctxt, VDISR_EL2) = read_sysreg_s(SYS_VDISR_EL2); } static inline void __sysreg_restore_common_state(struct kvm_cpu_context *ctxt) @@ -252,6 +282,9 @@ static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt, write_sysreg(ctxt_sys_reg(ctxt, SP_EL1), sp_el1); write_sysreg_el1(ctxt_sys_reg(ctxt, ELR_EL1), SYS_ELR); write_sysreg_el1(ctxt_sys_reg(ctxt, SPSR_EL1), SYS_SPSR); + + if (ctxt_has_sctlr2(ctxt)) + write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR2_EL1), SYS_SCTLR2); } /* Read the VCPU state's PSTATE, but translate (v)EL2 to EL1. */ @@ -275,6 +308,7 @@ static inline void __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctx { u64 pstate = to_hw_pstate(ctxt); u64 mode = pstate & PSR_AA32_MODE_MASK; + u64 vdisr; /* * Safety check to ensure we're setting the CPU up to enter the guest @@ -293,8 +327,17 @@ static inline void __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctx write_sysreg_el2(ctxt->regs.pc, SYS_ELR); write_sysreg_el2(pstate, SYS_SPSR); - if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) - write_sysreg_s(ctxt_sys_reg(ctxt, DISR_EL1), SYS_VDISR_EL2); + if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) + return; + + if (!vserror_state_is_nested(ctxt_to_vcpu(ctxt))) + vdisr = ctxt_sys_reg(ctxt, DISR_EL1); + else if (ctxt_has_ras(ctxt)) + vdisr = ctxt_sys_reg(ctxt, VDISR_EL2); + else + vdisr = 0; + + write_sysreg_s(vdisr, SYS_VDISR_EL2); } static inline void __sysreg32_save_state(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index a76522d63c3e..0b0a68b663d4 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -12,7 +12,7 @@ asflags-y := -D__KVM_NVHE_HYPERVISOR__ -D__DISABLE_EXPORTS ccflags-y := -D__KVM_NVHE_HYPERVISOR__ -D__DISABLE_EXPORTS -D__DISABLE_TRACE_MMIO__ ccflags-y += -fno-stack-protector \ -DDISABLE_BRANCH_PROFILING \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) hostprogs := gen-hyprel HOST_EXTRACFLAGS += -I$(objtree)/include diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c index 2f4a4f5036bb..2a1c0f49792b 100644 --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c @@ -92,12 +92,42 @@ static void __trace_switch_to_host(void) *host_data_ptr(host_debug_state.trfcr_el1)); } +static void __debug_save_brbe(u64 *brbcr_el1) +{ + *brbcr_el1 = 0; + + /* Check if the BRBE is enabled */ + if (!(read_sysreg_el1(SYS_BRBCR) & (BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE))) + return; + + /* + * Prohibit branch record generation while we are in guest. + * Since access to BRBCR_EL1 is trapped, the guest can't + * modify the filtering set by the host. + */ + *brbcr_el1 = read_sysreg_el1(SYS_BRBCR); + write_sysreg_el1(0, SYS_BRBCR); +} + +static void __debug_restore_brbe(u64 brbcr_el1) +{ + if (!brbcr_el1) + return; + + /* Restore BRBE controls */ + write_sysreg_el1(brbcr_el1, SYS_BRBCR); +} + 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)); + /* Disable BRBE branch records */ + if (host_data_test_flag(HAS_BRBE)) + __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1)); + if (__trace_needs_switch()) __trace_switch_to_guest(); } @@ -111,6 +141,8 @@ 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)); + if (host_data_test_flag(HAS_BRBE)) + __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1)); if (__trace_needs_switch()) __trace_switch_to_host(); } diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index e9198e56e784..3206b2c07f82 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -69,7 +69,10 @@ static void fpsimd_sve_sync(struct kvm_vcpu *vcpu) if (!guest_owns_fp_regs()) return; - cpacr_clear_set(0, CPACR_EL1_FPEN | CPACR_EL1_ZEN); + /* + * Traps have been disabled by __deactivate_cptr_traps(), but there + * hasn't necessarily been a context synchronization event yet. + */ isb(); if (vcpu_has_sve(vcpu)) diff --git a/arch/arm64/kvm/hyp/nvhe/list_debug.c b/arch/arm64/kvm/hyp/nvhe/list_debug.c index 46a2d4f2b3c6..baa6260f88dc 100644 --- a/arch/arm64/kvm/hyp/nvhe/list_debug.c +++ b/arch/arm64/kvm/hyp/nvhe/list_debug.c @@ -17,7 +17,7 @@ static inline __must_check bool nvhe_check_data_corruption(bool v) bool corruption = unlikely(condition); \ if (corruption) { \ if (IS_ENABLED(CONFIG_BUG_ON_DATA_CORRUPTION)) { \ - BUG_ON(1); \ + BUG(); \ } else \ WARN_ON(1); \ } \ diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 95d7534c9679..8957734d6183 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -479,6 +479,7 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range) { struct kvm_mem_range cur; kvm_pte_t pte; + u64 granule; s8 level; int ret; @@ -496,18 +497,21 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range) return -EPERM; } - do { - u64 granule = kvm_granule_size(level); + for (; level <= KVM_PGTABLE_LAST_LEVEL; level++) { + if (!kvm_level_supports_block_mapping(level)) + continue; + granule = kvm_granule_size(level); cur.start = ALIGN_DOWN(addr, granule); cur.end = cur.start + granule; - level++; - } while ((level <= KVM_PGTABLE_LAST_LEVEL) && - !(kvm_level_supports_block_mapping(level) && - range_included(&cur, range))); + if (!range_included(&cur, range)) + continue; + *range = cur; + return 0; + } - *range = cur; + WARN_ON(1); - return 0; + return -EINVAL; } int host_stage2_idmap_locked(phys_addr_t addr, u64 size, diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 73affe1333a4..ccd575d5f6de 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -47,65 +47,6 @@ struct fgt_masks hdfgwtr2_masks; extern void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc); -static void __activate_cptr_traps(struct kvm_vcpu *vcpu) -{ - u64 val = CPTR_EL2_TAM; /* Same bit irrespective of E2H */ - - if (!guest_owns_fp_regs()) - __activate_traps_fpsimd32(vcpu); - - if (has_hvhe()) { - val |= CPACR_EL1_TTA; - - if (guest_owns_fp_regs()) { - val |= CPACR_EL1_FPEN; - if (vcpu_has_sve(vcpu)) - val |= CPACR_EL1_ZEN; - } - - write_sysreg(val, cpacr_el1); - } else { - val |= CPTR_EL2_TTA | CPTR_NVHE_EL2_RES1; - - /* - * Always trap SME since it's not supported in KVM. - * TSM is RES1 if SME isn't implemented. - */ - val |= CPTR_EL2_TSM; - - if (!vcpu_has_sve(vcpu) || !guest_owns_fp_regs()) - val |= CPTR_EL2_TZ; - - if (!guest_owns_fp_regs()) - val |= CPTR_EL2_TFP; - - write_sysreg(val, cptr_el2); - } -} - -static void __deactivate_cptr_traps(struct kvm_vcpu *vcpu) -{ - if (has_hvhe()) { - u64 val = CPACR_EL1_FPEN; - - if (cpus_have_final_cap(ARM64_SVE)) - val |= CPACR_EL1_ZEN; - if (cpus_have_final_cap(ARM64_SME)) - val |= CPACR_EL1_SMEN; - - write_sysreg(val, cpacr_el1); - } else { - u64 val = CPTR_NVHE_EL2_RES1; - - if (!cpus_have_final_cap(ARM64_SVE)) - val |= CPTR_EL2_TZ; - if (!cpus_have_final_cap(ARM64_SME)) - val |= CPTR_EL2_TSM; - - write_sysreg(val, cptr_el2); - } -} - static void __activate_traps(struct kvm_vcpu *vcpu) { ___activate_traps(vcpu, vcpu->arch.hcr_el2); @@ -331,7 +272,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 SPE and TRBE. + * above disabling of BRBE, SPE and TRBE. * * See DDI0487I.a D8.1.5 "Out-of-context translation regimes", * rule R_LFHQG and subsequent information statements. diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index 1ddd9ed3cbb3..71d2fc97f004 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -253,6 +253,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) *vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR); *vcpu_cpsr(vcpu) = read_sysreg_el2(SYS_SPSR); + __vcpu_assign_sys_reg(vcpu, read_sysreg_el1(SYS_VBAR), VBAR_EL1); kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); @@ -372,6 +373,9 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = { /* Debug and Trace Registers are restricted. */ + /* Group 1 ID registers */ + HOST_HANDLED(SYS_REVIDR_EL1), + /* AArch64 mappings of the AArch32 ID registers */ /* CRm=1 */ AARCH32(SYS_ID_PFR0_EL1), @@ -460,6 +464,7 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = { HOST_HANDLED(SYS_CCSIDR_EL1), HOST_HANDLED(SYS_CLIDR_EL1), + HOST_HANDLED(SYS_AIDR_EL1), HOST_HANDLED(SYS_CSSELR_EL1), HOST_HANDLED(SYS_CTR_EL0), diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index c351b4abd5db..c36f282a175d 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1551,21 +1551,38 @@ static int stage2_free_walker(const struct kvm_pgtable_visit_ctx *ctx, return 0; } -void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt) +void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt, + u64 addr, u64 size) { - size_t pgd_sz; struct kvm_pgtable_walker walker = { .cb = stage2_free_walker, .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, }; - WARN_ON(kvm_pgtable_walk(pgt, 0, BIT(pgt->ia_bits), &walker)); + WARN_ON(kvm_pgtable_walk(pgt, addr, size, &walker)); +} + +void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt) +{ + size_t pgd_sz; + pgd_sz = kvm_pgd_pages(pgt->ia_bits, pgt->start_level) * PAGE_SIZE; - pgt->mm_ops->free_pages_exact(kvm_dereference_pteref(&walker, pgt->pgd), pgd_sz); + + /* + * Since the pgtable is unlinked at this point, and not shared with + * other walkers, safely deference pgd with kvm_dereference_pteref_raw() + */ + pgt->mm_ops->free_pages_exact(kvm_dereference_pteref_raw(pgt->pgd), pgd_sz); pgt->pgd = NULL; } +void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt) +{ + kvm_pgtable_stage2_destroy_range(pgt, 0, BIT(pgt->ia_bits)); + kvm_pgtable_stage2_destroy_pgd(pgt); +} + void kvm_pgtable_stage2_free_unlinked(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, s8 level) { kvm_pteref_t ptep = (kvm_pteref_t)pgtable; diff --git a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c index 87a54375bd6e..78579b31a420 100644 --- a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c +++ b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c @@ -20,7 +20,7 @@ static bool __is_be(struct kvm_vcpu *vcpu) if (vcpu_mode_is_32bit(vcpu)) return !!(read_sysreg_el2(SYS_SPSR) & PSR_AA32_E_BIT); - return !!(read_sysreg(SCTLR_EL1) & SCTLR_ELx_EE); + return !!(read_sysreg_el1(SYS_SCTLR) & SCTLR_ELx_EE); } /* diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c index f162b0df5cae..d81275790e69 100644 --- a/arch/arm64/kvm/hyp/vgic-v3-sr.c +++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c @@ -296,12 +296,19 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if) } /* - * Prevent the guest from touching the ICC_SRE_EL1 system - * register. Note that this may not have any effect, as - * ICC_SRE_EL2.Enable being RAO/WI is a valid implementation. + * GICv5 BET0 FEAT_GCIE_LEGACY doesn't include ICC_SRE_EL2. This is due + * to be relaxed in a future spec release, at which point this in + * condition can be dropped. */ - write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE, - ICC_SRE_EL2); + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) { + /* + * Prevent the guest from touching the ICC_SRE_EL1 system + * register. Note that this may not have any effect, as + * ICC_SRE_EL2.Enable being RAO/WI is a valid implementation. + */ + write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE, + ICC_SRE_EL2); + } /* * If we need to trap system registers, we must write @@ -322,8 +329,14 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if) cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2); } - val = read_gicreg(ICC_SRE_EL2); - write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2); + /* + * Can be dropped in the future when GICv5 spec is relaxed. See comment + * above. + */ + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) { + val = read_gicreg(ICC_SRE_EL2); + write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2); + } if (!cpu_if->vgic_sre) { /* Make sure ENABLE is set at EL2 before setting SRE at EL1 */ @@ -423,9 +436,19 @@ void __vgic_v3_init_lrs(void) */ u64 __vgic_v3_get_gic_config(void) { - u64 val, sre = read_gicreg(ICC_SRE_EL1); + u64 val, sre; unsigned long flags = 0; + /* + * In compat mode, we cannot access ICC_SRE_EL1 at any EL + * other than EL1 itself; just return the + * ICH_VTR_EL2. ICC_IDR0_EL1 is only implemented on a GICv5 + * system, so we first check if we have GICv5 support. + */ + if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) + return read_gicreg(ICH_VTR_EL2); + + sre = read_gicreg(ICC_SRE_EL1); /* * To check whether we have a MMIO-based (GICv2 compatible) * CPU interface, we need to disable the system register @@ -471,6 +494,16 @@ u64 __vgic_v3_get_gic_config(void) return val; } +static void __vgic_v3_compat_mode_enable(void) +{ + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) + return; + + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, 0, ICH_VCTLR_EL2_V3); + /* Wait for V3 to become enabled */ + isb(); +} + static u64 __vgic_v3_read_vmcr(void) { return read_gicreg(ICH_VMCR_EL2); @@ -490,6 +523,8 @@ void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if) void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if) { + __vgic_v3_compat_mode_enable(); + /* * If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen * is dependent on ICC_SRE_EL1.SRE, and we have to perform the @@ -1050,7 +1085,7 @@ static bool __vgic_v3_check_trap_forwarding(struct kvm_vcpu *vcpu, { u64 ich_hcr; - if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu)) + if (!is_nested_ctxt(vcpu)) return false; ich_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2); diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 09df2b42bc1b..0998ad4a2552 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -43,13 +43,15 @@ DEFINE_PER_CPU(unsigned long, kvm_hyp_vector); * * - API/APK: they are already accounted for by vcpu_load(), and can * only take effect across a load/put cycle (such as ERET) + * + * - FIEN: no way we let a guest have access to the RAS "Common Fault + * Injection" thing, whatever that does */ -#define NV_HCR_GUEST_EXCLUDE (HCR_TGE | HCR_API | HCR_APK) +#define NV_HCR_GUEST_EXCLUDE (HCR_TGE | HCR_API | HCR_APK | HCR_FIEN) static u64 __compute_hcr(struct kvm_vcpu *vcpu) { - u64 guest_hcr = __vcpu_sys_reg(vcpu, HCR_EL2); - u64 hcr = vcpu->arch.hcr_el2; + u64 guest_hcr, hcr = vcpu->arch.hcr_el2; if (!vcpu_has_nv(vcpu)) return hcr; @@ -68,10 +70,21 @@ static u64 __compute_hcr(struct kvm_vcpu *vcpu) if (!vcpu_el2_e2h_is_set(vcpu)) hcr |= HCR_NV1; + /* + * Nothing in HCR_EL2 should impact running in hypervisor + * context, apart from bits we have defined as RESx (E2H, + * HCD and co), or that cannot be set directly (the EXCLUDE + * bits). Given that we OR the guest's view with the host's, + * we can use the 0 value as the starting point, and only + * use the config-driven RES1 bits. + */ + guest_hcr = kvm_vcpu_apply_reg_masks(vcpu, HCR_EL2, 0); + write_sysreg_s(vcpu->arch.ctxt.vncr_array, SYS_VNCR_EL2); } else { host_data_clear_flag(VCPU_IN_HYP_CONTEXT); + guest_hcr = __vcpu_sys_reg(vcpu, HCR_EL2); if (guest_hcr & HCR_NV) { u64 va = __fix_to_virt(vncr_fixmap(smp_processor_id())); @@ -90,87 +103,6 @@ static u64 __compute_hcr(struct kvm_vcpu *vcpu) return hcr | (guest_hcr & ~NV_HCR_GUEST_EXCLUDE); } -static void __activate_cptr_traps(struct kvm_vcpu *vcpu) -{ - u64 cptr; - - /* - * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to - * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2, - * except for some missing controls, such as TAM. - * In this case, CPTR_EL2.TAM has the same position with or without - * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM - * shift value for trapping the AMU accesses. - */ - u64 val = CPACR_EL1_TTA | CPTR_EL2_TAM; - - if (guest_owns_fp_regs()) { - val |= CPACR_EL1_FPEN; - if (vcpu_has_sve(vcpu)) - val |= CPACR_EL1_ZEN; - } else { - __activate_traps_fpsimd32(vcpu); - } - - if (!vcpu_has_nv(vcpu)) - goto write; - - /* - * The architecture is a bit crap (what a surprise): an EL2 guest - * writing to CPTR_EL2 via CPACR_EL1 can't set any of TCPAC or TTA, - * as they are RES0 in the guest's view. To work around it, trap the - * sucker using the very same bit it can't set... - */ - if (vcpu_el2_e2h_is_set(vcpu) && is_hyp_ctxt(vcpu)) - val |= CPTR_EL2_TCPAC; - - /* - * Layer the guest hypervisor's trap configuration on top of our own if - * we're in a nested context. - */ - if (is_hyp_ctxt(vcpu)) - goto write; - - cptr = vcpu_sanitised_cptr_el2(vcpu); - - /* - * Pay attention, there's some interesting detail here. - * - * The CPTR_EL2.xEN fields are 2 bits wide, although there are only two - * meaningful trap states when HCR_EL2.TGE = 0 (running a nested guest): - * - * - CPTR_EL2.xEN = x0, traps are enabled - * - CPTR_EL2.xEN = x1, traps are disabled - * - * In other words, bit[0] determines if guest accesses trap or not. In - * the interest of simplicity, clear the entire field if the guest - * hypervisor has traps enabled to dispel any illusion of something more - * complicated taking place. - */ - if (!(SYS_FIELD_GET(CPACR_EL1, FPEN, cptr) & BIT(0))) - val &= ~CPACR_EL1_FPEN; - if (!(SYS_FIELD_GET(CPACR_EL1, ZEN, cptr) & BIT(0))) - val &= ~CPACR_EL1_ZEN; - - if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S2POE, IMP)) - val |= cptr & CPACR_EL1_E0POE; - - val |= cptr & CPTR_EL2_TCPAC; - -write: - write_sysreg(val, cpacr_el1); -} - -static void __deactivate_cptr_traps(struct kvm_vcpu *vcpu) -{ - u64 val = CPACR_EL1_FPEN | CPACR_EL1_ZEN_EL1EN; - - if (cpus_have_final_cap(ARM64_SME)) - val |= CPACR_EL1_SMEN_EL1EN; - - write_sysreg(val, cpacr_el1); -} - static void __activate_traps(struct kvm_vcpu *vcpu) { u64 val; @@ -639,10 +571,10 @@ static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu) host_ctxt = host_data_ptr(host_ctxt); guest_ctxt = &vcpu->arch.ctxt; - sysreg_save_host_state_vhe(host_ctxt); - fpsimd_lazy_switch_to_guest(vcpu); + sysreg_save_host_state_vhe(host_ctxt); + /* * Note that ARM erratum 1165522 requires us to configure both stage 1 * and stage 2 translation for the guest context before we clear @@ -667,15 +599,23 @@ static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu) __deactivate_traps(vcpu); - fpsimd_lazy_switch_to_host(vcpu); - sysreg_restore_host_state_vhe(host_ctxt); + __debug_switch_to_host(vcpu); + + /* + * Ensure that all system register writes above have taken effect + * before returning to the host. In VHE mode, CPTR traps for + * FPSIMD/SVE/SME also apply to EL2, so FPSIMD/SVE/SME state must be + * manipulated after the ISB. + */ + isb(); + + fpsimd_lazy_switch_to_host(vcpu); + if (guest_owns_fp_regs()) __fpsimd_save_fpexc32(vcpu); - __debug_switch_to_host(vcpu); - return exit_code; } NOKPROBE_SYMBOL(__kvm_vcpu_run_vhe); @@ -705,12 +645,6 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) */ local_daif_restore(DAIF_PROCCTX_NOIRQ); - /* - * When we exit from the guest we change a number of CPU configuration - * parameters, such as traps. We rely on the isb() in kvm_call_hyp*() - * to make sure these changes take effect before running the host or - * additional guests. - */ return ret; } diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c index 73e4bc7fde9e..f28c6cf4fe1b 100644 --- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c +++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c @@ -77,6 +77,9 @@ static void __sysreg_save_vel2_state(struct kvm_vcpu *vcpu) __vcpu_assign_sys_reg(vcpu, SP_EL2, read_sysreg(sp_el1)); __vcpu_assign_sys_reg(vcpu, ELR_EL2, read_sysreg_el1(SYS_ELR)); __vcpu_assign_sys_reg(vcpu, SPSR_EL2, read_sysreg_el1(SYS_SPSR)); + + if (ctxt_has_sctlr2(&vcpu->arch.ctxt)) + __vcpu_assign_sys_reg(vcpu, SCTLR2_EL2, read_sysreg_el1(SYS_SCTLR2)); } static void __sysreg_restore_vel2_state(struct kvm_vcpu *vcpu) @@ -139,6 +142,9 @@ static void __sysreg_restore_vel2_state(struct kvm_vcpu *vcpu) write_sysreg(__vcpu_sys_reg(vcpu, SP_EL2), sp_el1); write_sysreg_el1(__vcpu_sys_reg(vcpu, ELR_EL2), SYS_ELR); write_sysreg_el1(__vcpu_sys_reg(vcpu, SPSR_EL2), SYS_SPSR); + + if (ctxt_has_sctlr2(&vcpu->arch.ctxt)) + write_sysreg_el1(__vcpu_sys_reg(vcpu, SCTLR2_EL2), SYS_SCTLR2); } /* diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index a640e839848e..6745f38b64f9 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -15,13 +15,11 @@ #include #include -static void pend_sync_exception(struct kvm_vcpu *vcpu) +static unsigned int exception_target_el(struct kvm_vcpu *vcpu) { /* If not nesting, EL1 is the only possible exception target */ - if (likely(!vcpu_has_nv(vcpu))) { - kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); - return; - } + if (likely(!vcpu_has_nv(vcpu))) + return PSR_MODE_EL1h; /* * With NV, we need to pick between EL1 and EL2. Note that we @@ -32,26 +30,76 @@ static void pend_sync_exception(struct kvm_vcpu *vcpu) switch(*vcpu_cpsr(vcpu) & PSR_MODE_MASK) { case PSR_MODE_EL2h: case PSR_MODE_EL2t: - kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); - break; + return PSR_MODE_EL2h; case PSR_MODE_EL1h: case PSR_MODE_EL1t: - kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); - break; + return PSR_MODE_EL1h; case PSR_MODE_EL0t: - if (vcpu_el2_tge_is_set(vcpu)) - kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); - else - kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); - break; + return vcpu_el2_tge_is_set(vcpu) ? PSR_MODE_EL2h : PSR_MODE_EL1h; default: BUG(); } } -static bool match_target_el(struct kvm_vcpu *vcpu, unsigned long target) +static enum vcpu_sysreg exception_esr_elx(struct kvm_vcpu *vcpu) { - return (vcpu_get_flag(vcpu, EXCEPT_MASK) == target); + if (exception_target_el(vcpu) == PSR_MODE_EL2h) + return ESR_EL2; + + return ESR_EL1; +} + +static enum vcpu_sysreg exception_far_elx(struct kvm_vcpu *vcpu) +{ + if (exception_target_el(vcpu) == PSR_MODE_EL2h) + return FAR_EL2; + + return FAR_EL1; +} + +static void pend_sync_exception(struct kvm_vcpu *vcpu) +{ + if (exception_target_el(vcpu) == PSR_MODE_EL1h) + kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); + else + kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); +} + +static void pend_serror_exception(struct kvm_vcpu *vcpu) +{ + if (exception_target_el(vcpu) == PSR_MODE_EL1h) + kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SERR); + else + kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR); +} + +static bool __effective_sctlr2_bit(struct kvm_vcpu *vcpu, unsigned int idx) +{ + u64 sctlr2; + + if (!kvm_has_sctlr2(vcpu->kvm)) + return false; + + if (is_nested_ctxt(vcpu) && + !(__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_SCTLR2En)) + return false; + + if (exception_target_el(vcpu) == PSR_MODE_EL1h) + sctlr2 = vcpu_read_sys_reg(vcpu, SCTLR2_EL1); + else + sctlr2 = vcpu_read_sys_reg(vcpu, SCTLR2_EL2); + + return sctlr2 & BIT(idx); +} + +static bool effective_sctlr2_ease(struct kvm_vcpu *vcpu) +{ + return __effective_sctlr2_bit(vcpu, SCTLR2_EL1_EASE_SHIFT); +} + +static bool effective_sctlr2_nmea(struct kvm_vcpu *vcpu) +{ + return __effective_sctlr2_bit(vcpu, SCTLR2_EL1_NMEA_SHIFT); } static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) @@ -60,7 +108,11 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr bool is_aarch32 = vcpu_mode_is_32bit(vcpu); u64 esr = 0; - pend_sync_exception(vcpu); + /* This delight is brought to you by FEAT_DoubleFault2. */ + if (effective_sctlr2_ease(vcpu)) + pend_serror_exception(vcpu); + else + pend_sync_exception(vcpu); /* * Build an {i,d}abort, depending on the level and the @@ -83,13 +135,8 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr esr |= ESR_ELx_FSC_EXTABT; - if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) { - vcpu_write_sys_reg(vcpu, addr, FAR_EL1); - vcpu_write_sys_reg(vcpu, esr, ESR_EL1); - } else { - vcpu_write_sys_reg(vcpu, addr, FAR_EL2); - vcpu_write_sys_reg(vcpu, esr, ESR_EL2); - } + vcpu_write_sys_reg(vcpu, addr, exception_far_elx(vcpu)); + vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); } static void inject_undef64(struct kvm_vcpu *vcpu) @@ -105,10 +152,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) if (kvm_vcpu_trap_il_is32bit(vcpu)) esr |= ESR_ELx_IL; - if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) - vcpu_write_sys_reg(vcpu, esr, ESR_EL1); - else - vcpu_write_sys_reg(vcpu, esr, ESR_EL2); + vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); } #define DFSR_FSC_EXTABT_LPAE 0x10 @@ -155,36 +199,35 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr) vcpu_write_sys_reg(vcpu, far, FAR_EL1); } -/** - * kvm_inject_dabt - inject a data abort into the guest - * @vcpu: The VCPU to receive the data abort - * @addr: The address to report in the DFAR - * - * It is assumed that this code is called from the VCPU thread and that the - * VCPU therefore is not currently executing guest code. - */ -void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) +static void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr) { if (vcpu_el1_is_32bit(vcpu)) - inject_abt32(vcpu, false, addr); + inject_abt32(vcpu, iabt, addr); else - inject_abt64(vcpu, false, addr); + inject_abt64(vcpu, iabt, addr); } -/** - * kvm_inject_pabt - inject a prefetch abort into the guest - * @vcpu: The VCPU to receive the prefetch abort - * @addr: The address to report in the DFAR - * - * It is assumed that this code is called from the VCPU thread and that the - * VCPU therefore is not currently executing guest code. - */ -void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) +static bool kvm_sea_target_is_el2(struct kvm_vcpu *vcpu) { - if (vcpu_el1_is_32bit(vcpu)) - inject_abt32(vcpu, true, addr); - else - inject_abt64(vcpu, true, addr); + if (__vcpu_sys_reg(vcpu, HCR_EL2) & (HCR_TGE | HCR_TEA)) + return true; + + if (!vcpu_mode_priv(vcpu)) + return false; + + return (*vcpu_cpsr(vcpu) & PSR_A_BIT) && + (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TMEA); +} + +int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr) +{ + lockdep_assert_held(&vcpu->mutex); + + if (is_nested_ctxt(vcpu) && kvm_sea_target_is_el2(vcpu)) + return kvm_inject_nested_sea(vcpu, iabt, addr); + + __kvm_inject_sea(vcpu, iabt, addr); + return 1; } void kvm_inject_size_fault(struct kvm_vcpu *vcpu) @@ -194,10 +237,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu) addr = kvm_vcpu_get_fault_ipa(vcpu); addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0); - if (kvm_vcpu_trap_is_iabt(vcpu)) - kvm_inject_pabt(vcpu, addr); - else - kvm_inject_dabt(vcpu, addr); + __kvm_inject_sea(vcpu, kvm_vcpu_trap_is_iabt(vcpu), addr); /* * If AArch64 or LPAE, set FSC to 0 to indicate an Address @@ -210,9 +250,9 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu) !(vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE)) return; - esr = vcpu_read_sys_reg(vcpu, ESR_EL1); + esr = vcpu_read_sys_reg(vcpu, exception_esr_elx(vcpu)); esr &= ~GENMASK_ULL(5, 0); - vcpu_write_sys_reg(vcpu, esr, ESR_EL1); + vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); } /** @@ -230,25 +270,70 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu) inject_undef64(vcpu); } -void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 esr) +static bool serror_is_masked(struct kvm_vcpu *vcpu) { - vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK); - *vcpu_hcr(vcpu) |= HCR_VSE; + return (*vcpu_cpsr(vcpu) & PSR_A_BIT) && !effective_sctlr2_nmea(vcpu); } -/** - * kvm_inject_vabt - inject an async abort / SError into the guest - * @vcpu: The VCPU to receive the exception - * - * It is assumed that this code is called from the VCPU thread and that the - * VCPU therefore is not currently executing guest code. - * - * Systems with the RAS Extensions specify an imp-def ESR (ISV/IDS = 1) with - * the remaining ISS all-zeros so that this error is not interpreted as an - * uncategorized RAS error. Without the RAS Extensions we can't specify an ESR - * value, so the CPU generates an imp-def value. - */ -void kvm_inject_vabt(struct kvm_vcpu *vcpu) +static bool kvm_serror_target_is_el2(struct kvm_vcpu *vcpu) { - kvm_set_sei_esr(vcpu, ESR_ELx_ISV); + if (is_hyp_ctxt(vcpu) || vcpu_el2_amo_is_set(vcpu)) + return true; + + if (!(__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TMEA)) + return false; + + /* + * In another example where FEAT_DoubleFault2 is entirely backwards, + * "masked" as it relates to the routing effects of HCRX_EL2.TMEA + * doesn't consider SCTLR2_EL1.NMEA. That is to say, even if EL1 asked + * for non-maskable SErrors, the EL2 bit takes priority if A is set. + */ + if (vcpu_mode_priv(vcpu)) + return *vcpu_cpsr(vcpu) & PSR_A_BIT; + + /* + * Otherwise SErrors are considered unmasked when taken from EL0 and + * NMEA is set. + */ + return serror_is_masked(vcpu); +} + +static bool kvm_serror_undeliverable_at_el2(struct kvm_vcpu *vcpu) +{ + return !(vcpu_el2_tge_is_set(vcpu) || vcpu_el2_amo_is_set(vcpu)); +} + +int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr) +{ + lockdep_assert_held(&vcpu->mutex); + + if (is_nested_ctxt(vcpu) && kvm_serror_target_is_el2(vcpu)) + return kvm_inject_nested_serror(vcpu, esr); + + if (vcpu_is_el2(vcpu) && kvm_serror_undeliverable_at_el2(vcpu)) { + vcpu_set_vsesr(vcpu, esr); + vcpu_set_flag(vcpu, NESTED_SERROR_PENDING); + return 1; + } + + /* + * Emulate the exception entry if SErrors are unmasked. This is useful if + * the vCPU is in a nested context w/ vSErrors enabled then we've already + * delegated he hardware vSError context (i.e. HCR_EL2.VSE, VSESR_EL2, + * VDISR_EL2) to the guest hypervisor. + * + * As we're emulating the SError injection we need to explicitly populate + * ESR_ELx.EC because hardware will not do it on our behalf. + */ + if (!serror_is_masked(vcpu)) { + pend_serror_exception(vcpu); + esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR); + vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); + return 1; + } + + vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK); + *vcpu_hcr(vcpu) |= HCR_VSE; + return 1; } diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c index ab365e839874..54f9358c9e0e 100644 --- a/arch/arm64/kvm/mmio.c +++ b/arch/arm64/kvm/mmio.c @@ -72,7 +72,7 @@ unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len) return data; } -static bool kvm_pending_sync_exception(struct kvm_vcpu *vcpu) +static bool kvm_pending_external_abort(struct kvm_vcpu *vcpu) { if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION)) return false; @@ -90,6 +90,8 @@ static bool kvm_pending_sync_exception(struct kvm_vcpu *vcpu) switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) { case unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC): case unpack_vcpu_flag(EXCEPT_AA64_EL2_SYNC): + case unpack_vcpu_flag(EXCEPT_AA64_EL1_SERR): + case unpack_vcpu_flag(EXCEPT_AA64_EL2_SERR): return true; default: return false; @@ -113,7 +115,7 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu) * Detect if the MMIO return was already handled or if userspace aborted * the MMIO access. */ - if (unlikely(!vcpu->mmio_needed || kvm_pending_sync_exception(vcpu))) + if (unlikely(!vcpu->mmio_needed || kvm_pending_external_abort(vcpu))) return 1; vcpu->mmio_needed = 0; @@ -169,10 +171,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu), kvm_vcpu_get_hfar(vcpu), fault_ipa); - if (vcpu_is_protected(vcpu)) { - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); - return 1; - } + if (vcpu_is_protected(vcpu)) + return kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, &vcpu->kvm->arch.flags)) { diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 2942ec92c5a4..86f3d80daf37 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -4,19 +4,20 @@ * Author: Christoffer Dall */ +#include #include #include #include #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -193,11 +194,6 @@ int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, return 0; } -static bool kvm_is_device_pfn(unsigned long pfn) -{ - return !pfn_is_map_memory(pfn); -} - static void *stage2_memcache_zalloc_page(void *arg) { struct kvm_mmu_memory_cache *mc = arg; @@ -908,6 +904,38 @@ static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type) return 0; } +/* + * Assume that @pgt is valid and unlinked from the KVM MMU to free the + * page-table without taking the kvm_mmu_lock and without performing any + * TLB invalidations. + * + * Also, the range of addresses can be large enough to cause need_resched + * warnings, for instance on CONFIG_PREEMPT_NONE kernels. Hence, invoke + * cond_resched() periodically to prevent hogging the CPU for a long time + * and schedule something else, if required. + */ +static void stage2_destroy_range(struct kvm_pgtable *pgt, phys_addr_t addr, + phys_addr_t end) +{ + u64 next; + + do { + next = stage2_range_addr_end(addr, end); + KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, addr, + next - addr); + if (next != end) + cond_resched(); + } while (addr = next, addr != end); +} + +static void kvm_stage2_destroy(struct kvm_pgtable *pgt) +{ + unsigned int ia_bits = VTCR_EL2_IPA(pgt->mmu->vtcr); + + stage2_destroy_range(pgt, 0, BIT(ia_bits)); + KVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt); +} + /** * kvm_init_stage2_mmu - Initialise a S2 MMU structure * @kvm: The pointer to the KVM structure @@ -984,7 +1012,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t return 0; out_destroy_pgtable: - KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt); + kvm_stage2_destroy(pgt); out_free_pgtable: kfree(pgt); return err; @@ -1081,7 +1109,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) write_unlock(&kvm->mmu_lock); if (pgt) { - KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt); + kvm_stage2_destroy(pgt); kfree(pgt); } } @@ -1470,6 +1498,18 @@ static bool kvm_vma_mte_allowed(struct vm_area_struct *vma) return vma->vm_flags & VM_MTE_ALLOWED; } +static bool kvm_vma_is_cacheable(struct vm_area_struct *vma) +{ + switch (FIELD_GET(PTE_ATTRINDX_MASK, pgprot_val(vma->vm_page_prot))) { + case MT_NORMAL_NC: + case MT_DEVICE_nGnRnE: + case MT_DEVICE_nGnRE: + return false; + default: + return true; + } +} + 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, @@ -1477,8 +1517,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, { int ret = 0; bool write_fault, writable, force_pte = false; - bool exec_fault, mte_allowed; - bool device = false, vfio_allow_any_uc = false; + 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; struct kvm *kvm = vcpu->kvm; @@ -1492,6 +1532,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R; struct kvm_pgtable *pgt; struct page *page; + vm_flags_t vm_flags; enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED; if (fault_is_perm) @@ -1619,6 +1660,10 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED; + 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; @@ -1642,18 +1687,39 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, if (is_error_noslot_pfn(pfn)) return -EFAULT; - if (kvm_is_device_pfn(pfn)) { - /* - * If the page was identified as device early by looking at - * the VMA flags, vma_pagesize is already representing the - * largest quantity we can map. If instead it was mapped - * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE - * and must not be upgraded. - * - * In both cases, we don't let transparent_hugepage_adjust() - * change things at the last minute. - */ - device = true; + /* + * Check if this is non-struct page memory PFN, and cannot support + * CMOs. It could potentially be unsafe to access as cachable. + */ + if (vm_flags & (VM_PFNMAP | VM_MIXEDMAP) && !pfn_is_map_memory(pfn)) { + if (is_vma_cacheable) { + /* + * Whilst the VMA owner expects cacheable mapping to this + * PFN, hardware also has to support the FWB and CACHE DIC + * features. + * + * ARM64 KVM relies on kernel VA mapping to the PFN to + * perform cache maintenance as the CMO instructions work on + * virtual addresses. VM_PFNMAP region are not necessarily + * mapped to a KVA and hence the presence of hardware features + * S2FWB and CACHE DIC are mandatory to avoid the need for + * cache maintenance. + */ + if (!kvm_supports_cacheable_pfnmap()) + return -EFAULT; + } else { + /* + * If the page was identified as device early by looking at + * the VMA flags, vma_pagesize is already representing the + * largest quantity we can map. If instead it was mapped + * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE + * and must not be upgraded. + * + * In both cases, we don't let transparent_hugepage_adjust() + * change things at the last minute. + */ + s2_force_noncacheable = true; + } } else if (logging_active && !write_fault) { /* * Only actually map the page as writable if this was a write @@ -1662,7 +1728,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, writable = false; } - if (exec_fault && device) + if (exec_fault && s2_force_noncacheable) return -ENOEXEC; /* @@ -1695,7 +1761,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * 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 || device)) { + if (vma_pagesize == PAGE_SIZE && !(force_pte || s2_force_noncacheable)) { if (fault_is_perm && fault_granule > PAGE_SIZE) vma_pagesize = fault_granule; else @@ -1709,7 +1775,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, } } - if (!fault_is_perm && !device && kvm_has_mte(kvm)) { + 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); @@ -1725,7 +1791,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, if (exec_fault) prot |= KVM_PGTABLE_PROT_X; - if (device) { + if (s2_force_noncacheable) { if (vfio_allow_any_uc) prot |= KVM_PGTABLE_PROT_NORMAL_NC; else @@ -1778,6 +1844,19 @@ static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) read_unlock(&vcpu->kvm->mmu_lock); } +int kvm_handle_guest_sea(struct kvm_vcpu *vcpu) +{ + /* + * Give APEI the opportunity to claim the abort before handling it + * within KVM. apei_claim_sea() expects to be called with IRQs enabled. + */ + lockdep_assert_irqs_enabled(); + if (apei_claim_sea(NULL) == 0) + return 1; + + return kvm_inject_serror(vcpu); +} + /** * kvm_handle_guest_abort - handles all 2nd stage aborts * @vcpu: the VCPU pointer @@ -1801,17 +1880,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) gfn_t gfn; int ret, idx; - /* Synchronous External Abort? */ - if (kvm_vcpu_abt_issea(vcpu)) { - /* - * For RAS the host kernel may handle this abort. - * There is no need to pass the error into the guest. - */ - if (kvm_handle_guest_sea()) - kvm_inject_vabt(vcpu); - - return 1; - } + if (kvm_vcpu_abt_issea(vcpu)) + return kvm_handle_guest_sea(vcpu); esr = kvm_vcpu_get_esr(vcpu); @@ -1836,11 +1906,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) if (fault_ipa >= BIT_ULL(VTCR_EL2_IPA(vcpu->arch.hw_mmu->vtcr))) { fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0); - if (is_iabt) - kvm_inject_pabt(vcpu, fault_ipa); - else - kvm_inject_dabt(vcpu, fault_ipa); - return 1; + return kvm_inject_sea(vcpu, is_iabt, fault_ipa); } } @@ -1912,8 +1978,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) } if (kvm_vcpu_abt_iss1tw(vcpu)) { - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); - ret = 1; + ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); goto out_unlock; } @@ -1958,10 +2023,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) if (ret == 0) ret = 1; out: - if (ret == -ENOEXEC) { - kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); - ret = 1; - } + if (ret == -ENOEXEC) + ret = kvm_inject_sea_iabt(vcpu, kvm_vcpu_get_hfar(vcpu)); out_unlock: srcu_read_unlock(&vcpu->kvm->srcu, idx); return ret; @@ -2221,6 +2284,15 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, ret = -EINVAL; break; } + + /* + * Cacheable PFNMAP is allowed only if the hardware + * supports it. + */ + if (kvm_vma_is_cacheable(vma) && !kvm_supports_cacheable_pfnmap()) { + ret = -EINVAL; + break; + } } hva = min(reg_end, vma->vm_end); } while (hva < reg_end); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 5b191f4dc566..77db81bae86f 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1287,7 +1287,10 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); - BUG_ON(!(esr & ESR_ELx_VNCR_SHIFT)); + WARN_ON_ONCE(!(esr & ESR_ELx_VNCR)); + + if (kvm_vcpu_abt_issea(vcpu)) + return kvm_handle_guest_sea(vcpu); if (esr_fsc_is_permission_fault(esr)) { inject_vncr_perm(vcpu); @@ -1402,6 +1405,21 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) } } +#define has_tgran_2(__r, __sz) \ + ({ \ + u64 _s1, _s2, _mmfr0 = __r; \ + \ + _s2 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz##_2, _mmfr0); \ + \ + _s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz, _mmfr0); \ + \ + ((_s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI && \ + _s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \ + (_s2 == ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \ + _s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI)); \ + }) /* * Our emulated CPU doesn't support all the possible features. For the * sake of simplicity (and probably mental sanity), wipe out a number @@ -1411,6 +1429,8 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) */ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) { + u64 orig_val = val; + switch (reg) { case SYS_ID_AA64ISAR0_EL1: /* Support everything but TME */ @@ -1424,12 +1444,11 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) break; case SYS_ID_AA64PFR0_EL1: - /* No RME, AMU, MPAM, S-EL2, or RAS */ + /* No RME, AMU, MPAM, or S-EL2 */ val &= ~(ID_AA64PFR0_EL1_RME | ID_AA64PFR0_EL1_AMU | ID_AA64PFR0_EL1_MPAM | ID_AA64PFR0_EL1_SEL2 | - ID_AA64PFR0_EL1_RAS | ID_AA64PFR0_EL1_EL3 | ID_AA64PFR0_EL1_EL2 | ID_AA64PFR0_EL1_EL1 | @@ -1480,13 +1499,16 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) */ switch (PAGE_SIZE) { case SZ_4K: - val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN4_2, IMP); + if (has_tgran_2(orig_val, 4)) + val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN4_2, IMP); fallthrough; case SZ_16K: - val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN16_2, IMP); + if (has_tgran_2(orig_val, 16)) + val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN16_2, IMP); fallthrough; case SZ_64K: - val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN64_2, IMP); + if (has_tgran_2(orig_val, 64)) + val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN64_2, IMP); break; } @@ -1663,69 +1685,21 @@ int kvm_init_nv_sysregs(struct kvm_vcpu *vcpu) set_sysreg_masks(kvm, HFGITR2_EL2, res0, res1); /* TCR2_EL2 */ - res0 = TCR2_EL2_RES0; - res1 = TCR2_EL2_RES1; - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, D128, IMP)) - res0 |= (TCR2_EL2_DisCH0 | TCR2_EL2_DisCH1 | TCR2_EL2_D128); - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, MEC, IMP)) - res0 |= TCR2_EL2_AMEC1 | TCR2_EL2_AMEC0; - if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, HAFDBS, HAFT)) - res0 |= TCR2_EL2_HAFT; - if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, THE, IMP)) - res0 |= TCR2_EL2_PTTWI | TCR2_EL2_PnCH; - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, AIE, IMP)) - res0 |= TCR2_EL2_AIE; - if (!kvm_has_s1poe(kvm)) - res0 |= TCR2_EL2_POE | TCR2_EL2_E0POE; - if (!kvm_has_s1pie(kvm)) - res0 |= TCR2_EL2_PIE; - if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, VH, IMP)) - res0 |= (TCR2_EL2_E0POE | TCR2_EL2_D128 | - TCR2_EL2_AMEC1 | TCR2_EL2_DisCH0 | TCR2_EL2_DisCH1); + get_reg_fixed_bits(kvm, TCR2_EL2, &res0, &res1); set_sysreg_masks(kvm, TCR2_EL2, res0, res1); /* SCTLR_EL1 */ - res0 = SCTLR_EL1_RES0; - res1 = SCTLR_EL1_RES1; - if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, PAN, PAN3)) - res0 |= SCTLR_EL1_EPAN; + get_reg_fixed_bits(kvm, SCTLR_EL1, &res0, &res1); set_sysreg_masks(kvm, SCTLR_EL1, res0, res1); + /* SCTLR2_ELx */ + get_reg_fixed_bits(kvm, SCTLR2_EL1, &res0, &res1); + set_sysreg_masks(kvm, SCTLR2_EL1, res0, res1); + get_reg_fixed_bits(kvm, SCTLR2_EL2, &res0, &res1); + set_sysreg_masks(kvm, SCTLR2_EL2, res0, res1); + /* MDCR_EL2 */ - res0 = MDCR_EL2_RES0; - res1 = MDCR_EL2_RES1; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) - res0 |= (MDCR_EL2_HPMN | MDCR_EL2_TPMCR | - MDCR_EL2_TPM | MDCR_EL2_HPME); - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, IMP)) - res0 |= MDCR_EL2_E2PB | MDCR_EL2_TPMS; - if (!kvm_has_feat(kvm, ID_AA64DFR1_EL1, SPMU, IMP)) - res0 |= MDCR_EL2_EnSPM; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P1)) - res0 |= MDCR_EL2_HPMD; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceFilt, IMP)) - res0 |= MDCR_EL2_TTRF; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P5)) - res0 |= MDCR_EL2_HCCD | MDCR_EL2_HLP; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, IMP)) - res0 |= MDCR_EL2_E2TB; - if (!kvm_has_feat(kvm, ID_AA64MMFR0_EL1, FGT, IMP)) - res0 |= MDCR_EL2_TDCC; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, MTPMU, IMP) || - kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP)) - res0 |= MDCR_EL2_MTPME; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P7)) - res0 |= MDCR_EL2_HPMFZO; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSS, IMP)) - res0 |= MDCR_EL2_PMSSE; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, V1P2)) - res0 |= MDCR_EL2_HPMFZS; - if (!kvm_has_feat(kvm, ID_AA64DFR1_EL1, EBEP, IMP)) - res0 |= MDCR_EL2_PMEE; - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, DebugVer, V8P9)) - res0 |= MDCR_EL2_EBWE; - if (!kvm_has_feat(kvm, ID_AA64DFR2_EL1, STEP, IMP)) - res0 |= MDCR_EL2_EnSTEPOP; + get_reg_fixed_bits(kvm, MDCR_EL2, &res0, &res1); set_sysreg_masks(kvm, MDCR_EL2, res0, res1); /* CNTHCTL_EL2 */ @@ -1782,3 +1756,43 @@ void check_nested_vcpu_requests(struct kvm_vcpu *vcpu) if (kvm_check_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu)) kvm_inject_nested_irq(vcpu); } + +/* + * One of the many architectural bugs in FEAT_NV2 is that the guest hypervisor + * can write to HCR_EL2 behind our back, potentially changing the exception + * routing / masking for even the host context. + * + * What follows is some slop to (1) react to exception routing / masking and (2) + * preserve the pending SError state across translation regimes. + */ +void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu) +{ + if (!vcpu_has_nv(vcpu)) + return; + + if (unlikely(vcpu_test_and_clear_flag(vcpu, NESTED_SERROR_PENDING))) + kvm_inject_serror_esr(vcpu, vcpu_get_vsesr(vcpu)); +} + +void kvm_nested_sync_hwstate(struct kvm_vcpu *vcpu) +{ + unsigned long *hcr = vcpu_hcr(vcpu); + + if (!vcpu_has_nv(vcpu)) + return; + + /* + * We previously decided that an SError was deliverable to the guest. + * Reap the pending state from HCR_EL2 and... + */ + if (unlikely(__test_and_clear_bit(__ffs(HCR_VSE), hcr))) + vcpu_set_flag(vcpu, NESTED_SERROR_PENDING); + + /* + * Re-attempt SError injection in case the deliverability has changed, + * which is necessary to faithfully emulate WFI the case of a pending + * SError being a wakeup condition. + */ + if (unlikely(vcpu_test_and_clear_flag(vcpu, NESTED_SERROR_PENDING))) + kvm_inject_serror_esr(vcpu, vcpu_get_vsesr(vcpu)); +} diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index fcd70bfe44fb..61827cf6fea4 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -316,9 +316,16 @@ static int __pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 start, u64 e return 0; } -void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt) +void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt, + u64 addr, u64 size) { - __pkvm_pgtable_stage2_unmap(pgt, 0, ~(0ULL)); + __pkvm_pgtable_stage2_unmap(pgt, addr, addr + size); +} + +void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt) +{ + /* Expected to be called after all pKVM mappings have been released. */ + WARN_ON_ONCE(!RB_EMPTY_ROOT(&pgt->pkvm_mappings.rb_root)); } int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 76c2f0da821f..b29f72478a50 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -82,44 +82,105 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu, "sys_reg write to read-only register"); } -#define PURE_EL2_SYSREG(el2) \ - case el2: { \ - *el1r = el2; \ - return true; \ - } +enum sr_loc_attr { + SR_LOC_MEMORY = 0, /* Register definitely in memory */ + SR_LOC_LOADED = BIT(0), /* Register on CPU, unless it cannot */ + SR_LOC_MAPPED = BIT(1), /* Register in a different CPU register */ + SR_LOC_XLATED = BIT(2), /* Register translated to fit another reg */ + SR_LOC_SPECIAL = BIT(3), /* Demanding register, implies loaded */ +}; -#define MAPPED_EL2_SYSREG(el2, el1, fn) \ - case el2: { \ - *xlate = fn; \ - *el1r = el1; \ - return true; \ - } +struct sr_loc { + enum sr_loc_attr loc; + enum vcpu_sysreg map_reg; + u64 (*xlate)(u64); +}; -static bool get_el2_to_el1_mapping(unsigned int reg, - unsigned int *el1r, u64 (**xlate)(u64)) +static enum sr_loc_attr locate_direct_register(const struct kvm_vcpu *vcpu, + enum vcpu_sysreg reg) { switch (reg) { - PURE_EL2_SYSREG( VPIDR_EL2 ); - PURE_EL2_SYSREG( VMPIDR_EL2 ); - PURE_EL2_SYSREG( ACTLR_EL2 ); - PURE_EL2_SYSREG( HCR_EL2 ); - PURE_EL2_SYSREG( MDCR_EL2 ); - PURE_EL2_SYSREG( HSTR_EL2 ); - PURE_EL2_SYSREG( HACR_EL2 ); - PURE_EL2_SYSREG( VTTBR_EL2 ); - PURE_EL2_SYSREG( VTCR_EL2 ); - PURE_EL2_SYSREG( RVBAR_EL2 ); - PURE_EL2_SYSREG( TPIDR_EL2 ); - PURE_EL2_SYSREG( HPFAR_EL2 ); - PURE_EL2_SYSREG( HCRX_EL2 ); - PURE_EL2_SYSREG( HFGRTR_EL2 ); - PURE_EL2_SYSREG( HFGWTR_EL2 ); - PURE_EL2_SYSREG( HFGITR_EL2 ); - PURE_EL2_SYSREG( HDFGRTR_EL2 ); - PURE_EL2_SYSREG( HDFGWTR_EL2 ); - PURE_EL2_SYSREG( HAFGRTR_EL2 ); - PURE_EL2_SYSREG( CNTVOFF_EL2 ); - PURE_EL2_SYSREG( CNTHCTL_EL2 ); + case SCTLR_EL1: + case CPACR_EL1: + case TTBR0_EL1: + case TTBR1_EL1: + case TCR_EL1: + case TCR2_EL1: + case PIR_EL1: + case PIRE0_EL1: + case POR_EL1: + case ESR_EL1: + case AFSR0_EL1: + case AFSR1_EL1: + case FAR_EL1: + case MAIR_EL1: + case VBAR_EL1: + case CONTEXTIDR_EL1: + case AMAIR_EL1: + case CNTKCTL_EL1: + case ELR_EL1: + case SPSR_EL1: + case ZCR_EL1: + case SCTLR2_EL1: + /* + * EL1 registers which have an ELx2 mapping are loaded if + * we're not in hypervisor context. + */ + return is_hyp_ctxt(vcpu) ? SR_LOC_MEMORY : SR_LOC_LOADED; + + case TPIDR_EL0: + case TPIDRRO_EL0: + case TPIDR_EL1: + case PAR_EL1: + case DACR32_EL2: + case IFSR32_EL2: + case DBGVCR32_EL2: + /* These registers are always loaded, no matter what */ + return SR_LOC_LOADED; + + default: + /* Non-mapped EL2 registers are by definition in memory. */ + return SR_LOC_MEMORY; + } +} + +static void locate_mapped_el2_register(const struct kvm_vcpu *vcpu, + enum vcpu_sysreg reg, + enum vcpu_sysreg map_reg, + u64 (*xlate)(u64), + struct sr_loc *loc) +{ + if (!is_hyp_ctxt(vcpu)) { + loc->loc = SR_LOC_MEMORY; + return; + } + + loc->loc = SR_LOC_LOADED | SR_LOC_MAPPED; + loc->map_reg = map_reg; + + WARN_ON(locate_direct_register(vcpu, map_reg) != SR_LOC_MEMORY); + + if (xlate != NULL && !vcpu_el2_e2h_is_set(vcpu)) { + loc->loc |= SR_LOC_XLATED; + loc->xlate = xlate; + } +} + +#define MAPPED_EL2_SYSREG(r, m, t) \ + case r: { \ + locate_mapped_el2_register(vcpu, r, m, t, loc); \ + break; \ + } + +static void locate_register(const struct kvm_vcpu *vcpu, enum vcpu_sysreg reg, + struct sr_loc *loc) +{ + if (!vcpu_get_flag(vcpu, SYSREGS_ON_CPU)) { + loc->loc = SR_LOC_MEMORY; + return; + } + + switch (reg) { MAPPED_EL2_SYSREG(SCTLR_EL2, SCTLR_EL1, translate_sctlr_el2_to_sctlr_el1 ); MAPPED_EL2_SYSREG(CPTR_EL2, CPACR_EL1, @@ -144,125 +205,190 @@ static bool get_el2_to_el1_mapping(unsigned int reg, MAPPED_EL2_SYSREG(SPSR_EL2, SPSR_EL1, NULL ); MAPPED_EL2_SYSREG(ZCR_EL2, ZCR_EL1, NULL ); MAPPED_EL2_SYSREG(CONTEXTIDR_EL2, CONTEXTIDR_EL1, NULL ); + MAPPED_EL2_SYSREG(SCTLR2_EL2, SCTLR2_EL1, NULL ); + case CNTHCTL_EL2: + /* CNTHCTL_EL2 is super special, until we support NV2.1 */ + loc->loc = ((is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu)) ? + SR_LOC_SPECIAL : SR_LOC_MEMORY); + break; default: - return false; + loc->loc = locate_direct_register(vcpu, reg); } } -u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) +static u64 read_sr_from_cpu(enum vcpu_sysreg reg) { u64 val = 0x8badf00d8badf00d; - u64 (*xlate)(u64) = NULL; - unsigned int el1r; - if (!vcpu_get_flag(vcpu, SYSREGS_ON_CPU)) - goto memory_read; + switch (reg) { + case SCTLR_EL1: val = read_sysreg_s(SYS_SCTLR_EL12); break; + case CPACR_EL1: val = read_sysreg_s(SYS_CPACR_EL12); break; + case TTBR0_EL1: val = read_sysreg_s(SYS_TTBR0_EL12); break; + case TTBR1_EL1: val = read_sysreg_s(SYS_TTBR1_EL12); break; + case TCR_EL1: val = read_sysreg_s(SYS_TCR_EL12); break; + case TCR2_EL1: val = read_sysreg_s(SYS_TCR2_EL12); break; + case PIR_EL1: val = read_sysreg_s(SYS_PIR_EL12); break; + case PIRE0_EL1: val = read_sysreg_s(SYS_PIRE0_EL12); break; + case POR_EL1: val = read_sysreg_s(SYS_POR_EL12); break; + case ESR_EL1: val = read_sysreg_s(SYS_ESR_EL12); break; + case AFSR0_EL1: val = read_sysreg_s(SYS_AFSR0_EL12); break; + case AFSR1_EL1: val = read_sysreg_s(SYS_AFSR1_EL12); break; + case FAR_EL1: val = read_sysreg_s(SYS_FAR_EL12); break; + case MAIR_EL1: val = read_sysreg_s(SYS_MAIR_EL12); break; + case VBAR_EL1: val = read_sysreg_s(SYS_VBAR_EL12); break; + case CONTEXTIDR_EL1: val = read_sysreg_s(SYS_CONTEXTIDR_EL12);break; + case AMAIR_EL1: val = read_sysreg_s(SYS_AMAIR_EL12); break; + case CNTKCTL_EL1: val = read_sysreg_s(SYS_CNTKCTL_EL12); break; + case ELR_EL1: val = read_sysreg_s(SYS_ELR_EL12); break; + case SPSR_EL1: val = read_sysreg_s(SYS_SPSR_EL12); break; + case ZCR_EL1: val = read_sysreg_s(SYS_ZCR_EL12); break; + case SCTLR2_EL1: val = read_sysreg_s(SYS_SCTLR2_EL12); break; + case TPIDR_EL0: val = read_sysreg_s(SYS_TPIDR_EL0); break; + case TPIDRRO_EL0: val = read_sysreg_s(SYS_TPIDRRO_EL0); break; + case TPIDR_EL1: val = read_sysreg_s(SYS_TPIDR_EL1); break; + case PAR_EL1: val = read_sysreg_par(); break; + case DACR32_EL2: val = read_sysreg_s(SYS_DACR32_EL2); break; + case IFSR32_EL2: val = read_sysreg_s(SYS_IFSR32_EL2); break; + case DBGVCR32_EL2: val = read_sysreg_s(SYS_DBGVCR32_EL2); break; + default: WARN_ON_ONCE(1); + } - if (unlikely(get_el2_to_el1_mapping(reg, &el1r, &xlate))) { - if (!is_hyp_ctxt(vcpu)) - goto memory_read; + return val; +} + +static void write_sr_to_cpu(enum vcpu_sysreg reg, u64 val) +{ + switch (reg) { + case SCTLR_EL1: write_sysreg_s(val, SYS_SCTLR_EL12); break; + case CPACR_EL1: write_sysreg_s(val, SYS_CPACR_EL12); break; + case TTBR0_EL1: write_sysreg_s(val, SYS_TTBR0_EL12); break; + case TTBR1_EL1: write_sysreg_s(val, SYS_TTBR1_EL12); break; + case TCR_EL1: write_sysreg_s(val, SYS_TCR_EL12); break; + case TCR2_EL1: write_sysreg_s(val, SYS_TCR2_EL12); break; + case PIR_EL1: write_sysreg_s(val, SYS_PIR_EL12); break; + case PIRE0_EL1: write_sysreg_s(val, SYS_PIRE0_EL12); break; + case POR_EL1: write_sysreg_s(val, SYS_POR_EL12); break; + case ESR_EL1: write_sysreg_s(val, SYS_ESR_EL12); break; + case AFSR0_EL1: write_sysreg_s(val, SYS_AFSR0_EL12); break; + case AFSR1_EL1: write_sysreg_s(val, SYS_AFSR1_EL12); break; + case FAR_EL1: write_sysreg_s(val, SYS_FAR_EL12); break; + case MAIR_EL1: write_sysreg_s(val, SYS_MAIR_EL12); break; + case VBAR_EL1: write_sysreg_s(val, SYS_VBAR_EL12); break; + case CONTEXTIDR_EL1: write_sysreg_s(val, SYS_CONTEXTIDR_EL12);break; + case AMAIR_EL1: write_sysreg_s(val, SYS_AMAIR_EL12); break; + case CNTKCTL_EL1: write_sysreg_s(val, SYS_CNTKCTL_EL12); break; + case ELR_EL1: write_sysreg_s(val, SYS_ELR_EL12); break; + case SPSR_EL1: write_sysreg_s(val, SYS_SPSR_EL12); break; + case ZCR_EL1: write_sysreg_s(val, SYS_ZCR_EL12); break; + case SCTLR2_EL1: write_sysreg_s(val, SYS_SCTLR2_EL12); break; + case TPIDR_EL0: write_sysreg_s(val, SYS_TPIDR_EL0); break; + case TPIDRRO_EL0: write_sysreg_s(val, SYS_TPIDRRO_EL0); break; + case TPIDR_EL1: write_sysreg_s(val, SYS_TPIDR_EL1); break; + case PAR_EL1: write_sysreg_s(val, SYS_PAR_EL1); break; + case DACR32_EL2: write_sysreg_s(val, SYS_DACR32_EL2); break; + case IFSR32_EL2: write_sysreg_s(val, SYS_IFSR32_EL2); break; + case DBGVCR32_EL2: write_sysreg_s(val, SYS_DBGVCR32_EL2); break; + default: WARN_ON_ONCE(1); + } +} + +u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, enum vcpu_sysreg reg) +{ + struct sr_loc loc = {}; + + locate_register(vcpu, reg, &loc); + + WARN_ON_ONCE(!has_vhe() && loc.loc != SR_LOC_MEMORY); + + if (loc.loc & SR_LOC_SPECIAL) { + u64 val; + + WARN_ON_ONCE(loc.loc & ~SR_LOC_SPECIAL); /* - * CNTHCTL_EL2 requires some special treatment to - * account for the bits that can be set via CNTKCTL_EL1. + * CNTHCTL_EL2 requires some special treatment to account + * for the bits that can be set via CNTKCTL_EL1 when E2H==1. */ switch (reg) { case CNTHCTL_EL2: - if (vcpu_el2_e2h_is_set(vcpu)) { - val = read_sysreg_el1(SYS_CNTKCTL); - val &= CNTKCTL_VALID_BITS; - val |= __vcpu_sys_reg(vcpu, reg) & ~CNTKCTL_VALID_BITS; - return val; - } - break; + val = read_sysreg_el1(SYS_CNTKCTL); + val &= CNTKCTL_VALID_BITS; + val |= __vcpu_sys_reg(vcpu, reg) & ~CNTKCTL_VALID_BITS; + return val; + default: + WARN_ON_ONCE(1); } - - /* - * If this register does not have an EL1 counterpart, - * then read the stored EL2 version. - */ - if (reg == el1r) - goto memory_read; - - /* - * If we have a non-VHE guest and that the sysreg - * requires translation to be used at EL1, use the - * in-memory copy instead. - */ - if (!vcpu_el2_e2h_is_set(vcpu) && xlate) - goto memory_read; - - /* Get the current version of the EL1 counterpart. */ - WARN_ON(!__vcpu_read_sys_reg_from_cpu(el1r, &val)); - if (reg >= __SANITISED_REG_START__) - val = kvm_vcpu_apply_reg_masks(vcpu, reg, val); - - return val; } - /* EL1 register can't be on the CPU if the guest is in vEL2. */ - if (unlikely(is_hyp_ctxt(vcpu))) - goto memory_read; + if (loc.loc & SR_LOC_LOADED) { + enum vcpu_sysreg map_reg = reg; - if (__vcpu_read_sys_reg_from_cpu(reg, &val)) - return val; + if (loc.loc & SR_LOC_MAPPED) + map_reg = loc.map_reg; + + if (!(loc.loc & SR_LOC_XLATED)) { + u64 val = read_sr_from_cpu(map_reg); + + if (reg >= __SANITISED_REG_START__) + val = kvm_vcpu_apply_reg_masks(vcpu, reg, val); + + return val; + } + } -memory_read: return __vcpu_sys_reg(vcpu, reg); } -void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg) +void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, enum vcpu_sysreg reg) { - u64 (*xlate)(u64) = NULL; - unsigned int el1r; + struct sr_loc loc = {}; - if (!vcpu_get_flag(vcpu, SYSREGS_ON_CPU)) - goto memory_write; + locate_register(vcpu, reg, &loc); - if (unlikely(get_el2_to_el1_mapping(reg, &el1r, &xlate))) { - if (!is_hyp_ctxt(vcpu)) - goto memory_write; + WARN_ON_ONCE(!has_vhe() && loc.loc != SR_LOC_MEMORY); - /* - * Always store a copy of the write to memory to avoid having - * to reverse-translate virtual EL2 system registers for a - * non-VHE guest hypervisor. - */ - __vcpu_assign_sys_reg(vcpu, reg, val); + if (loc.loc & SR_LOC_SPECIAL) { + + WARN_ON_ONCE(loc.loc & ~SR_LOC_SPECIAL); switch (reg) { case CNTHCTL_EL2: /* - * If E2H=0, CNHTCTL_EL2 is a pure shadow register. - * Otherwise, some of the bits are backed by + * If E2H=1, some of the bits are backed by * CNTKCTL_EL1, while the rest is kept in memory. * Yes, this is fun stuff. */ - if (vcpu_el2_e2h_is_set(vcpu)) - write_sysreg_el1(val, SYS_CNTKCTL); - return; + write_sysreg_el1(val, SYS_CNTKCTL); + break; + default: + WARN_ON_ONCE(1); } - - /* No EL1 counterpart? We're done here.? */ - if (reg == el1r) - return; - - if (!vcpu_el2_e2h_is_set(vcpu) && xlate) - val = xlate(val); - - /* Redirect this to the EL1 version of the register. */ - WARN_ON(!__vcpu_write_sys_reg_to_cpu(val, el1r)); - return; } - /* EL1 register can't be on the CPU if the guest is in vEL2. */ - if (unlikely(is_hyp_ctxt(vcpu))) - goto memory_write; + if (loc.loc & SR_LOC_LOADED) { + enum vcpu_sysreg map_reg = reg; + u64 xlated_val; - if (__vcpu_write_sys_reg_to_cpu(val, reg)) - return; + if (reg >= __SANITISED_REG_START__) + val = kvm_vcpu_apply_reg_masks(vcpu, reg, val); + + if (loc.loc & SR_LOC_MAPPED) + map_reg = loc.map_reg; + + if (loc.loc & SR_LOC_XLATED) + xlated_val = loc.xlate(val); + else + xlated_val = val; + + write_sr_to_cpu(map_reg, xlated_val); + + /* + * Fall through to write the backing store anyway, which + * allows translated registers to be directly read without a + * reverse translation. + */ + } -memory_write: __vcpu_assign_sys_reg(vcpu, reg, val); } @@ -533,8 +659,7 @@ static bool access_gic_sre(struct kvm_vcpu *vcpu, return ignore_write(vcpu, p); if (p->Op1 == 4) { /* ICC_SRE_EL2 */ - p->regval = (ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE | - ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB); + p->regval = KVM_ICC_SRE_EL2; } else { /* ICC_SRE_EL1 */ p->regval = vcpu->arch.vgic_cpu.vgic_v3.vgic_sre; } @@ -773,6 +898,12 @@ static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) return mpidr; } +static unsigned int hidden_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *r) +{ + return REG_HIDDEN; +} + static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) { @@ -1579,6 +1710,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_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val); /* Read a sanitised cpufeature ID register by sys_reg_desc */ @@ -1601,39 +1733,28 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val = sanitise_id_aa64pfr0_el1(vcpu, val); break; case SYS_ID_AA64PFR1_EL1: - if (!kvm_has_mte(vcpu->kvm)) { - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE_frac); - } - - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_RNDR_trap); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_NMI); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_GCS); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_THE); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTEX); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_DF2); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_PFAR); - val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MPAM_frac); + val = sanitise_id_aa64pfr1_el1(vcpu, val); break; case SYS_ID_AA64PFR2_EL1: - /* We only expose FPMR */ - val &= ID_AA64PFR2_EL1_FPMR; + val &= ID_AA64PFR2_EL1_FPMR | + (kvm_has_mte(vcpu->kvm) ? + ID_AA64PFR2_EL1_MTEFAR | ID_AA64PFR2_EL1_MTESTOREONLY : + 0); break; case SYS_ID_AA64ISAR1_EL1: if (!vcpu_has_ptrauth(vcpu)) - val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_APA) | - ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_API) | - ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPA) | - ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPI)); + val &= ~(ID_AA64ISAR1_EL1_APA | + ID_AA64ISAR1_EL1_API | + ID_AA64ISAR1_EL1_GPA | + ID_AA64ISAR1_EL1_GPI); break; case SYS_ID_AA64ISAR2_EL1: if (!vcpu_has_ptrauth(vcpu)) - val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) | - ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3)); + val &= ~(ID_AA64ISAR2_EL1_APA3 | + ID_AA64ISAR2_EL1_GPA3); if (!cpus_have_final_cap(ARM64_HAS_WFXT) || has_broken_cntvoff()) - val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT); + val &= ~ID_AA64ISAR2_EL1_WFxT; break; case SYS_ID_AA64ISAR3_EL1: val &= ID_AA64ISAR3_EL1_FPRCVT | ID_AA64ISAR3_EL1_FAMINMAX; @@ -1643,11 +1764,13 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val &= ~ID_AA64MMFR2_EL1_NV; break; case SYS_ID_AA64MMFR3_EL1: - val &= ID_AA64MMFR3_EL1_TCRX | ID_AA64MMFR3_EL1_S1POE | - ID_AA64MMFR3_EL1_S1PIE; + val &= ID_AA64MMFR3_EL1_TCRX | + ID_AA64MMFR3_EL1_SCTLRX | + ID_AA64MMFR3_EL1_S1POE | + ID_AA64MMFR3_EL1_S1PIE; break; case SYS_ID_MMFR4_EL1: - val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX); + val &= ~ID_MMFR4_EL1_CCIDX; break; } @@ -1811,7 +1934,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 (kvm_vgic_global_state.type == VGIC_V3) { + if (vgic_is_v3(vcpu->kvm)) { val &= ~ID_AA64PFR0_EL1_GIC_MASK; val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); } @@ -1828,6 +1951,31 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val) return val; } +static u64 sanitise_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu, u64 val) +{ + u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); + + if (!kvm_has_mte(vcpu->kvm)) { + val &= ~ID_AA64PFR1_EL1_MTE; + val &= ~ID_AA64PFR1_EL1_MTE_frac; + } + + if (!(cpus_have_final_cap(ARM64_HAS_RASV1P1_EXTN) && + SYS_FIELD_GET(ID_AA64PFR0_EL1, RAS, pfr0) == ID_AA64PFR0_EL1_RAS_IMP)) + val &= ~ID_AA64PFR1_EL1_RAS_frac; + + val &= ~ID_AA64PFR1_EL1_SME; + val &= ~ID_AA64PFR1_EL1_RNDR_trap; + val &= ~ID_AA64PFR1_EL1_NMI; + val &= ~ID_AA64PFR1_EL1_GCS; + val &= ~ID_AA64PFR1_EL1_THE; + val &= ~ID_AA64PFR1_EL1_MTEX; + val &= ~ID_AA64PFR1_EL1_PFAR; + val &= ~ID_AA64PFR1_EL1_MPAM_frac; + + 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); @@ -1953,6 +2101,14 @@ 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); } @@ -2325,6 +2481,10 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu, EL2_REG_FILTERED(name, acc, rst, v, el2_visibility) #define EL2_REG_VNCR(name, rst, v) EL2_REG(name, bad_vncr_trap, rst, v) +#define EL2_REG_VNCR_FILT(name, vis) \ + EL2_REG_FILTERED(name, bad_vncr_trap, reset_val, 0, vis) +#define EL2_REG_VNCR_GICv3(name) \ + EL2_REG_VNCR_FILT(name, hidden_visibility) #define EL2_REG_REDIR(name, rst, v) EL2_REG(name, bad_redir_trap, rst, v) /* @@ -2483,6 +2643,21 @@ static unsigned int vncr_el2_visibility(const struct kvm_vcpu *vcpu, return REG_HIDDEN; } +static unsigned int sctlr2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + if (kvm_has_sctlr2(vcpu->kvm)) + return 0; + + return REG_HIDDEN; +} + +static unsigned int sctlr2_el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + return __el2_visibility(vcpu, rd, sctlr2_visibility); +} + static bool access_zcr_el2(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) @@ -2513,11 +2688,7 @@ static bool access_gic_vtr(struct kvm_vcpu *vcpu, if (p->is_write) return write_to_read_only(vcpu, p, r); - p->regval = kvm_vgic_global_state.ich_vtr_el2; - p->regval &= ~(ICH_VTR_EL2_DVIM | - ICH_VTR_EL2_A3V | - ICH_VTR_EL2_IDbits); - p->regval |= ICH_VTR_EL2_nV4; + p->regval = kvm_get_guest_vtr_el2(); return true; } @@ -2588,6 +2759,26 @@ static unsigned int tcr2_el2_visibility(const struct kvm_vcpu *vcpu, return __el2_visibility(vcpu, rd, tcr2_visibility); } +static unsigned int fgt2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + if (el2_visibility(vcpu, rd) == 0 && + kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, FGT, FGT2)) + return 0; + + return REG_HIDDEN; +} + +static unsigned int fgt_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + if (el2_visibility(vcpu, rd) == 0 && + kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, FGT, IMP)) + return 0; + + return REG_HIDDEN; +} + static unsigned int s1pie_visibility(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd) { @@ -2624,7 +2815,7 @@ static bool access_mdcr(struct kvm_vcpu *vcpu, */ if (hpmn > vcpu->kvm->arch.nr_pmu_counters) { hpmn = vcpu->kvm->arch.nr_pmu_counters; - u64_replace_bits(val, hpmn, MDCR_EL2_HPMN); + u64p_replace_bits(&val, hpmn, MDCR_EL2_HPMN); } __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val); @@ -2639,6 +2830,35 @@ static bool access_mdcr(struct kvm_vcpu *vcpu, return true; } +static bool access_ras(struct kvm_vcpu *vcpu, + struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + struct kvm *kvm = vcpu->kvm; + + switch(reg_to_encoding(r)) { + case SYS_ERXPFGCDN_EL1: + case SYS_ERXPFGCTL_EL1: + case SYS_ERXPFGF_EL1: + case SYS_ERXMISC2_EL1: + case SYS_ERXMISC3_EL1: + if (!(kvm_has_feat(kvm, ID_AA64PFR0_EL1, RAS, V1P1) || + (kvm_has_feat_enum(kvm, ID_AA64PFR0_EL1, RAS, IMP) && + kvm_has_feat(kvm, ID_AA64PFR1_EL1, RAS_frac, RASv1p1)))) { + kvm_inject_undefined(vcpu); + return false; + } + break; + default: + if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, RAS, IMP)) { + kvm_inject_undefined(vcpu); + return false; + } + } + + return trap_raz_wi(vcpu, p, r); +} + /* * For historical (ahem ABI) reasons, KVM treated MIDR_EL1, REVIDR_EL1, and * AIDR_EL1 as "invariant" registers, meaning userspace cannot change them. @@ -2861,12 +3081,10 @@ static const struct sys_reg_desc sys_reg_descs[] = { ~(ID_AA64PFR0_EL1_AMU | ID_AA64PFR0_EL1_MPAM | ID_AA64PFR0_EL1_SVE | - ID_AA64PFR0_EL1_RAS | ID_AA64PFR0_EL1_AdvSIMD | ID_AA64PFR0_EL1_FP)), ID_FILTERED(ID_AA64PFR1_EL1, id_aa64pfr1_el1, ~(ID_AA64PFR1_EL1_PFAR | - ID_AA64PFR1_EL1_DF2 | ID_AA64PFR1_EL1_MTEX | ID_AA64PFR1_EL1_THE | ID_AA64PFR1_EL1_GCS | @@ -2876,9 +3094,11 @@ static const struct sys_reg_desc sys_reg_descs[] = { ID_AA64PFR1_EL1_SME | ID_AA64PFR1_EL1_RES0 | ID_AA64PFR1_EL1_MPAM_frac | - ID_AA64PFR1_EL1_RAS_frac | ID_AA64PFR1_EL1_MTE)), - ID_WRITABLE(ID_AA64PFR2_EL1, ID_AA64PFR2_EL1_FPMR), + ID_WRITABLE(ID_AA64PFR2_EL1, + ID_AA64PFR2_EL1_FPMR | + ID_AA64PFR2_EL1_MTEFAR | + ID_AA64PFR2_EL1_MTESTOREONLY), ID_UNALLOCATED(4,3), ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0), ID_HIDDEN(ID_AA64SMFR0_EL1), @@ -2945,6 +3165,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { ID_AA64MMFR2_EL1_NV | ID_AA64MMFR2_EL1_CCIDX)), ID_WRITABLE(ID_AA64MMFR3_EL1, (ID_AA64MMFR3_EL1_TCRX | + ID_AA64MMFR3_EL1_SCTLRX | ID_AA64MMFR3_EL1_S1PIE | ID_AA64MMFR3_EL1_S1POE)), ID_WRITABLE(ID_AA64MMFR4_EL1, ID_AA64MMFR4_EL1_NV_frac), @@ -2955,6 +3176,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_SCTLR_EL1), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 }, { SYS_DESC(SYS_ACTLR_EL1), access_actlr, reset_actlr, ACTLR_EL1 }, { SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 }, + { SYS_DESC(SYS_SCTLR2_EL1), access_vm_reg, reset_val, SCTLR2_EL1, 0, + .visibility = sctlr2_visibility }, MTE_REG(RGSR_EL1), MTE_REG(GCR_EL1), @@ -2984,14 +3207,19 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 }, { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 }, - { SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi }, - { SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi }, + { SYS_DESC(SYS_ERRIDR_EL1), access_ras }, + { SYS_DESC(SYS_ERRSELR_EL1), access_ras }, + { SYS_DESC(SYS_ERXFR_EL1), access_ras }, + { SYS_DESC(SYS_ERXCTLR_EL1), access_ras }, + { SYS_DESC(SYS_ERXSTATUS_EL1), access_ras }, + { SYS_DESC(SYS_ERXADDR_EL1), access_ras }, + { SYS_DESC(SYS_ERXPFGF_EL1), access_ras }, + { SYS_DESC(SYS_ERXPFGCTL_EL1), access_ras }, + { SYS_DESC(SYS_ERXPFGCDN_EL1), access_ras }, + { SYS_DESC(SYS_ERXMISC0_EL1), access_ras }, + { SYS_DESC(SYS_ERXMISC1_EL1), access_ras }, + { SYS_DESC(SYS_ERXMISC2_EL1), access_ras }, + { SYS_DESC(SYS_ERXMISC3_EL1), access_ras }, MTE_REG(TFSR_EL1), MTE_REG(TFSRE0_EL1), @@ -3302,12 +3530,14 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG_VNCR(VMPIDR_EL2, reset_unknown, 0), EL2_REG(SCTLR_EL2, access_rw, reset_val, SCTLR_EL2_RES1), EL2_REG(ACTLR_EL2, access_rw, reset_val, 0), + EL2_REG_FILTERED(SCTLR2_EL2, access_vm_reg, reset_val, 0, + sctlr2_el2_visibility), EL2_REG_VNCR(HCR_EL2, reset_hcr, 0), EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0), EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1), EL2_REG_VNCR(HSTR_EL2, reset_val, 0), - EL2_REG_VNCR(HFGRTR_EL2, reset_val, 0), - EL2_REG_VNCR(HFGWTR_EL2, reset_val, 0), + EL2_REG_VNCR_FILT(HFGRTR_EL2, fgt_visibility), + EL2_REG_VNCR_FILT(HFGWTR_EL2, fgt_visibility), EL2_REG_VNCR(HFGITR_EL2, reset_val, 0), EL2_REG_VNCR(HACR_EL2, reset_val, 0), @@ -3327,9 +3557,14 @@ static const struct sys_reg_desc sys_reg_descs[] = { vncr_el2_visibility), { SYS_DESC(SYS_DACR32_EL2), undef_access, reset_unknown, DACR32_EL2 }, - EL2_REG_VNCR(HDFGRTR_EL2, reset_val, 0), - EL2_REG_VNCR(HDFGWTR_EL2, reset_val, 0), - EL2_REG_VNCR(HAFGRTR_EL2, reset_val, 0), + EL2_REG_VNCR_FILT(HDFGRTR2_EL2, fgt2_visibility), + EL2_REG_VNCR_FILT(HDFGWTR2_EL2, fgt2_visibility), + EL2_REG_VNCR_FILT(HFGRTR2_EL2, fgt2_visibility), + EL2_REG_VNCR_FILT(HFGWTR2_EL2, fgt2_visibility), + EL2_REG_VNCR_FILT(HDFGRTR_EL2, fgt_visibility), + EL2_REG_VNCR_FILT(HDFGWTR_EL2, fgt_visibility), + EL2_REG_VNCR_FILT(HAFGRTR_EL2, fgt_visibility), + EL2_REG_VNCR_FILT(HFGITR2_EL2, fgt2_visibility), EL2_REG_REDIR(SPSR_EL2, reset_val, 0), EL2_REG_REDIR(ELR_EL2, reset_val, 0), { SYS_DESC(SYS_SP_EL1), access_sp_el1}, @@ -3344,6 +3579,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG(AFSR0_EL2, access_rw, reset_val, 0), EL2_REG(AFSR1_EL2, access_rw, reset_val, 0), EL2_REG_REDIR(ESR_EL2, reset_val, 0), + EL2_REG_VNCR(VSESR_EL2, reset_unknown, 0), { SYS_DESC(SYS_FPEXC32_EL2), undef_access, reset_val, FPEXC32_EL2, 0x700 }, EL2_REG_REDIR(FAR_EL2, reset_val, 0), @@ -3370,43 +3606,44 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_MPAMVPM7_EL2), undef_access }, EL2_REG(VBAR_EL2, access_rw, reset_val, 0), - EL2_REG(RVBAR_EL2, access_rw, reset_val, 0), + { SYS_DESC(SYS_RVBAR_EL2), undef_access }, { SYS_DESC(SYS_RMR_EL2), undef_access }, + EL2_REG_VNCR(VDISR_EL2, reset_unknown, 0), - EL2_REG_VNCR(ICH_AP0R0_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP0R1_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP0R2_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP0R3_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP1R0_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP1R1_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP1R2_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_AP1R3_EL2, reset_val, 0), + EL2_REG_VNCR_GICv3(ICH_AP0R0_EL2), + EL2_REG_VNCR_GICv3(ICH_AP0R1_EL2), + EL2_REG_VNCR_GICv3(ICH_AP0R2_EL2), + EL2_REG_VNCR_GICv3(ICH_AP0R3_EL2), + EL2_REG_VNCR_GICv3(ICH_AP1R0_EL2), + EL2_REG_VNCR_GICv3(ICH_AP1R1_EL2), + EL2_REG_VNCR_GICv3(ICH_AP1R2_EL2), + EL2_REG_VNCR_GICv3(ICH_AP1R3_EL2), { SYS_DESC(SYS_ICC_SRE_EL2), access_gic_sre }, - EL2_REG_VNCR(ICH_HCR_EL2, reset_val, 0), + EL2_REG_VNCR_GICv3(ICH_HCR_EL2), { SYS_DESC(SYS_ICH_VTR_EL2), access_gic_vtr }, { SYS_DESC(SYS_ICH_MISR_EL2), access_gic_misr }, { SYS_DESC(SYS_ICH_EISR_EL2), access_gic_eisr }, { SYS_DESC(SYS_ICH_ELRSR_EL2), access_gic_elrsr }, - EL2_REG_VNCR(ICH_VMCR_EL2, reset_val, 0), + EL2_REG_VNCR_GICv3(ICH_VMCR_EL2), - EL2_REG_VNCR(ICH_LR0_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR1_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR2_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR3_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR4_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR5_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR6_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR7_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR8_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR9_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR10_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR11_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR12_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR13_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR14_EL2, reset_val, 0), - EL2_REG_VNCR(ICH_LR15_EL2, reset_val, 0), + EL2_REG_VNCR_GICv3(ICH_LR0_EL2), + EL2_REG_VNCR_GICv3(ICH_LR1_EL2), + EL2_REG_VNCR_GICv3(ICH_LR2_EL2), + EL2_REG_VNCR_GICv3(ICH_LR3_EL2), + EL2_REG_VNCR_GICv3(ICH_LR4_EL2), + EL2_REG_VNCR_GICv3(ICH_LR5_EL2), + EL2_REG_VNCR_GICv3(ICH_LR6_EL2), + EL2_REG_VNCR_GICv3(ICH_LR7_EL2), + EL2_REG_VNCR_GICv3(ICH_LR8_EL2), + EL2_REG_VNCR_GICv3(ICH_LR9_EL2), + EL2_REG_VNCR_GICv3(ICH_LR10_EL2), + EL2_REG_VNCR_GICv3(ICH_LR11_EL2), + EL2_REG_VNCR_GICv3(ICH_LR12_EL2), + EL2_REG_VNCR_GICv3(ICH_LR13_EL2), + EL2_REG_VNCR_GICv3(ICH_LR14_EL2), + EL2_REG_VNCR_GICv3(ICH_LR15_EL2), EL2_REG(CONTEXTIDR_EL2, access_rw, reset_val, 0), EL2_REG(TPIDR_EL2, access_rw, reset_val, 0), @@ -4275,12 +4512,12 @@ static const struct sys_reg_desc cp15_64_regs[] = { }; static bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n, - bool is_32) + bool reset_check) { unsigned int i; for (i = 0; i < n; i++) { - if (!is_32 && table[i].reg && !table[i].reset) { + if (reset_check && table[i].reg && !table[i].reset) { kvm_err("sys_reg table %pS entry %d (%s) lacks reset\n", &table[i], i, table[i].name); return false; @@ -4475,7 +4712,7 @@ static bool kvm_esr_cp10_id_to_sys64(u64 esr, struct sys_reg_params *params) return true; kvm_pr_unimpl("Unhandled cp10 register %s: %u\n", - params->is_write ? "write" : "read", reg_id); + str_write_read(params->is_write), reg_id); return false; } @@ -5269,18 +5506,22 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) int __init kvm_sys_reg_table_init(void) { + const struct sys_reg_desc *gicv3_regs; bool valid = true; - unsigned int i; + unsigned int i, sz; int ret = 0; /* Make sure tables are unique and in order. */ - valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false); - valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true); - valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true); - valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true); - valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true); + valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), true); + valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), false); + valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), false); + valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), false); + valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), false); valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false); + gicv3_regs = vgic_v3_get_sysreg_table(&sz); + valid &= check_sysreg_table(gicv3_regs, sz, false); + if (!valid) return -EINVAL; diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index ef97d9fc67cc..317abc490368 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -108,7 +108,7 @@ inline void print_sys_reg_msg(const struct sys_reg_params *p, /* Look, we even formatted it for you to paste into the table! */ kvm_pr_unimpl("%pV { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", &(struct va_format){ fmt, &va }, - p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, p->is_write ? "write" : "read"); + p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, str_write_read(p->is_write)); va_end(va); } diff --git a/arch/arm64/kvm/trace_handle_exit.h b/arch/arm64/kvm/trace_handle_exit.h index f85415db7713..a7ab9a3bbed0 100644 --- a/arch/arm64/kvm/trace_handle_exit.h +++ b/arch/arm64/kvm/trace_handle_exit.h @@ -113,7 +113,7 @@ TRACE_EVENT(kvm_sys_access, __entry->vcpu_pc, __entry->name ?: "UNKN", __entry->Op0, __entry->Op1, __entry->CRn, __entry->CRm, __entry->Op2, - __entry->is_write ? "write" : "read") + str_write_read(__entry->is_write)) ); TRACE_EVENT(kvm_set_guest_debug, diff --git a/arch/arm64/kvm/vgic-sys-reg-v3.c b/arch/arm64/kvm/vgic-sys-reg-v3.c index 5eacb4b3250a..bdc2d57370b2 100644 --- a/arch/arm64/kvm/vgic-sys-reg-v3.c +++ b/arch/arm64/kvm/vgic-sys-reg-v3.c @@ -297,6 +297,91 @@ static int get_gic_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, return 0; } +static int set_gic_ich_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + __vcpu_assign_sys_reg(vcpu, r->reg, val); + return 0; +} + +static int get_gic_ich_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + *val = __vcpu_sys_reg(vcpu, r->reg); + return 0; +} + +static int set_gic_ich_apr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + u8 idx = r->Op2 & 3; + + if (idx > vgic_v3_max_apr_idx(vcpu)) + return -EINVAL; + + return set_gic_ich_reg(vcpu, r, val); +} + +static int get_gic_ich_apr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + u8 idx = r->Op2 & 3; + + if (idx > vgic_v3_max_apr_idx(vcpu)) + return -EINVAL; + + return get_gic_ich_reg(vcpu, r, val); +} + +static int set_gic_icc_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + if (val != KVM_ICC_SRE_EL2) + return -EINVAL; + return 0; +} + +static int get_gic_icc_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + *val = KVM_ICC_SRE_EL2; + return 0; +} + +static int set_gic_ich_vtr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + if (val != kvm_get_guest_vtr_el2()) + return -EINVAL; + return 0; +} + +static int get_gic_ich_vtr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + *val = kvm_get_guest_vtr_el2(); + return 0; +} + +static unsigned int el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + return vcpu_has_nv(vcpu) ? 0 : REG_HIDDEN; +} + +#define __EL2_REG(r, acc, i) \ + { \ + SYS_DESC(SYS_ ## r), \ + .get_user = get_gic_ ## acc, \ + .set_user = set_gic_ ## acc, \ + .reg = i, \ + .visibility = el2_visibility, \ + } + +#define EL2_REG(r, acc) __EL2_REG(r, acc, r) + +#define EL2_REG_RO(r, acc) __EL2_REG(r, acc, 0) + static const struct sys_reg_desc gic_v3_icc_reg_descs[] = { { SYS_DESC(SYS_ICC_PMR_EL1), .set_user = set_gic_pmr, .get_user = get_gic_pmr, }, @@ -328,8 +413,42 @@ static const struct sys_reg_desc gic_v3_icc_reg_descs[] = { .set_user = set_gic_grpen0, .get_user = get_gic_grpen0, }, { SYS_DESC(SYS_ICC_IGRPEN1_EL1), .set_user = set_gic_grpen1, .get_user = get_gic_grpen1, }, + EL2_REG(ICH_AP0R0_EL2, ich_apr), + EL2_REG(ICH_AP0R1_EL2, ich_apr), + EL2_REG(ICH_AP0R2_EL2, ich_apr), + EL2_REG(ICH_AP0R3_EL2, ich_apr), + EL2_REG(ICH_AP1R0_EL2, ich_apr), + EL2_REG(ICH_AP1R1_EL2, ich_apr), + EL2_REG(ICH_AP1R2_EL2, ich_apr), + EL2_REG(ICH_AP1R3_EL2, ich_apr), + EL2_REG_RO(ICC_SRE_EL2, icc_sre), + EL2_REG(ICH_HCR_EL2, ich_reg), + EL2_REG_RO(ICH_VTR_EL2, ich_vtr), + EL2_REG(ICH_VMCR_EL2, ich_reg), + EL2_REG(ICH_LR0_EL2, ich_reg), + EL2_REG(ICH_LR1_EL2, ich_reg), + EL2_REG(ICH_LR2_EL2, ich_reg), + EL2_REG(ICH_LR3_EL2, ich_reg), + EL2_REG(ICH_LR4_EL2, ich_reg), + EL2_REG(ICH_LR5_EL2, ich_reg), + EL2_REG(ICH_LR6_EL2, ich_reg), + EL2_REG(ICH_LR7_EL2, ich_reg), + EL2_REG(ICH_LR8_EL2, ich_reg), + EL2_REG(ICH_LR9_EL2, ich_reg), + EL2_REG(ICH_LR10_EL2, ich_reg), + EL2_REG(ICH_LR11_EL2, ich_reg), + EL2_REG(ICH_LR12_EL2, ich_reg), + EL2_REG(ICH_LR13_EL2, ich_reg), + EL2_REG(ICH_LR14_EL2, ich_reg), + EL2_REG(ICH_LR15_EL2, ich_reg), }; +const struct sys_reg_desc *vgic_v3_get_sysreg_table(unsigned int *sz) +{ + *sz = ARRAY_SIZE(gic_v3_icc_reg_descs); + return gic_v3_icc_reg_descs; +} + static u64 attr_to_id(u64 attr) { return ARM64_SYS_REG(FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_OP0_MASK, attr), @@ -341,8 +460,12 @@ static u64 attr_to_id(u64 attr) int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { - if (get_reg_by_id(attr_to_id(attr->attr), gic_v3_icc_reg_descs, - ARRAY_SIZE(gic_v3_icc_reg_descs))) + const struct sys_reg_desc *r; + + r = get_reg_by_id(attr_to_id(attr->attr), gic_v3_icc_reg_descs, + ARRAY_SIZE(gic_v3_icc_reg_descs)); + + if (r && !sysreg_hidden(vcpu, r)) return 0; return -ENXIO; diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index eb1205654ac8..1e680ad6e863 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -157,6 +157,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) kvm->arch.vgic.in_kernel = true; kvm->arch.vgic.vgic_model = type; + kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; @@ -165,6 +166,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) else INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); + if (type == KVM_DEV_TYPE_ARM_VGIC_V3) + kvm->arch.vgic.nassgicap = system_supports_direct_sgis(); + out_unlock: mutex_unlock(&kvm->arch.config_lock); kvm_unlock_all_vcpus(kvm); @@ -391,11 +395,10 @@ int vgic_init(struct kvm *kvm) goto out; /* - * If we have GICv4.1 enabled, unconditionally request enable the - * v4 support so that we get HW-accelerated vSGIs. Otherwise, only - * enable it if we present a virtual ITS to the guest. + * Ensure vPEs are allocated if direct IRQ injection (e.g. vSGIs, + * vLPIs) is supported. */ - if (vgic_supports_direct_msis(kvm)) { + if (vgic_supports_direct_irqs(kvm)) { ret = vgic_v4_init(kvm); if (ret) goto out; @@ -409,15 +412,7 @@ int vgic_init(struct kvm *kvm) goto out; vgic_debug_init(kvm); - - /* - * If userspace didn't set the GIC implementation revision, - * default to the latest and greatest. You know want it. - */ - if (!dist->implementation_rev) - dist->implementation_rev = KVM_VGIC_IMP_REV_LATEST; dist->initialized = true; - out: return ret; } @@ -443,7 +438,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) dist->vgic_cpu_base = VGIC_ADDR_UNDEF; } - if (vgic_supports_direct_msis(kvm)) + if (vgic_supports_direct_irqs(kvm)) vgic_v4_teardown(kvm); xa_destroy(&dist->lpi_xa); @@ -674,10 +669,12 @@ void kvm_vgic_init_cpu_hardware(void) * We want to make sure the list registers start out clear so that we * only have the program the used registers. */ - if (kvm_vgic_global_state.type == VGIC_V2) + if (kvm_vgic_global_state.type == VGIC_V2) { vgic_v2_init_lrs(); - else + } else if (kvm_vgic_global_state.type == VGIC_V3 || + kvm_vgic_global_state.has_gcie_v3_compat) { kvm_call_hyp(__vgic_v3_init_lrs); + } } /** @@ -722,6 +719,9 @@ int kvm_vgic_hyp_init(void) kvm_info("GIC system register CPU interface enabled\n"); } break; + case GIC_V5: + ret = vgic_v5_probe(gic_kvm_info); + break; default: ret = -ENODEV; } diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 534049c7c94b..7368c13f16b7 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -758,7 +758,7 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite) if (irq) { scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { if (irq->hw) - WARN_ON(its_unmap_vlpi(ite->irq->host_irq)); + its_unmap_vlpi(ite->irq->host_irq); irq->hw = false; } @@ -2694,6 +2694,9 @@ static int vgic_its_ctrl(struct kvm *kvm, struct vgic_its *its, u64 attr) case KVM_DEV_ARM_ITS_RESTORE_TABLES: ret = abi->restore_tables(its); break; + default: + ret = -ENXIO; + break; } mutex_unlock(&its->its_lock); diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index f9ae790163fb..3d1a776b716d 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -5,6 +5,7 @@ * Copyright (C) 2015 ARM Ltd. * Author: Marc Zyngier */ +#include #include #include #include @@ -303,12 +304,6 @@ static int vgic_get_common_attr(struct kvm_device *dev, VGIC_NR_PRIVATE_IRQS, uaddr); break; } - case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - - r = put_user(dev->kvm->arch.vgic.mi_intid, uaddr); - break; - } } return r; @@ -509,6 +504,24 @@ int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, return 0; } +/* + * Allow access to certain ID-like registers prior to VGIC initialization, + * thereby allowing the VMM to provision the features / sizing of the VGIC. + */ +static bool reg_allowed_pre_init(struct kvm_device_attr *attr) +{ + if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS) + return false; + + switch (attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK) { + case GICD_IIDR: + case GICD_TYPER2: + return true; + default: + return false; + } +} + /* * vgic_v3_attr_regs_access - allows user space to access VGIC v3 state * @@ -523,7 +536,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, struct vgic_reg_attr reg_attr; gpa_t addr; struct kvm_vcpu *vcpu; - bool uaccess, post_init = true; + bool uaccess; u32 val; int ret; @@ -539,9 +552,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, /* Sysregs uaccess is performed by the sysreg handling code */ uaccess = false; break; - case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: - post_init = false; - fallthrough; default: uaccess = true; } @@ -561,7 +571,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, mutex_lock(&dev->kvm->arch.config_lock); - if (post_init != vgic_initialized(dev->kvm)) { + if (!(vgic_initialized(dev->kvm) || reg_allowed_pre_init(attr))) { ret = -EBUSY; goto out; } @@ -591,19 +601,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, } break; } - case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: - if (!is_write) { - val = dev->kvm->arch.vgic.mi_intid; - ret = 0; - break; - } - - ret = -EINVAL; - if ((val < VGIC_NR_PRIVATE_IRQS) && (val >= VGIC_NR_SGIS)) { - dev->kvm->arch.vgic.mi_intid = val; - ret = 0; - } - break; default: ret = -EINVAL; break; @@ -630,8 +627,24 @@ static int vgic_v3_set_attr(struct kvm_device *dev, case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: - case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: return vgic_v3_attr_regs_access(dev, attr, true); + case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: { + u32 __user *uaddr = (u32 __user *)attr->addr; + u32 val; + + if (get_user(val, uaddr)) + return -EFAULT; + + guard(mutex)(&dev->kvm->arch.config_lock); + if (vgic_initialized(dev->kvm)) + return -EBUSY; + + if (!irq_is_ppi(val)) + return -EINVAL; + + dev->kvm->arch.vgic.mi_intid = val; + return 0; + } default: return vgic_set_common_attr(dev, attr); } @@ -645,8 +658,13 @@ static int vgic_v3_get_attr(struct kvm_device *dev, case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: - case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: return vgic_v3_attr_regs_access(dev, attr, false); + case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: { + u32 __user *uaddr = (u32 __user *)(long)attr->addr; + + guard(mutex)(&dev->kvm->arch.config_lock); + return put_user(dev->kvm->arch.vgic.mi_intid, uaddr); + } default: return vgic_get_common_attr(dev, attr); } diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index ae4c0593d114..70d50c77e5dc 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -50,8 +50,25 @@ bool vgic_has_its(struct kvm *kvm) bool vgic_supports_direct_msis(struct kvm *kvm) { - return (kvm_vgic_global_state.has_gicv4_1 || - (kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm))); + /* + * Deliberately conflate vLPI and vSGI support on GICv4.1 hardware, + * indirectly allowing userspace to control whether or not vPEs are + * allocated for the VM. + */ + if (system_supports_direct_sgis() && !vgic_supports_direct_sgis(kvm)) + return false; + + return kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm); +} + +bool system_supports_direct_sgis(void) +{ + return kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi(); +} + +bool vgic_supports_direct_sgis(struct kvm *kvm) +{ + return kvm->arch.vgic.nassgicap; } /* @@ -86,7 +103,7 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu, } break; case GICD_TYPER2: - if (kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi()) + if (vgic_supports_direct_sgis(vcpu->kvm)) value = GICD_TYPER2_nASSGIcap; break; case GICD_IIDR: @@ -119,7 +136,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu, dist->enabled = val & GICD_CTLR_ENABLE_SS_G1; /* Not a GICv4.1? No HW SGIs */ - if (!kvm_vgic_global_state.has_gicv4_1 || !gic_cpuif_has_vsgi()) + if (!vgic_supports_direct_sgis(vcpu->kvm)) val &= ~GICD_CTLR_nASSGIreq; /* Dist stays enabled? nASSGIreq is RO */ @@ -133,7 +150,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu, if (is_hwsgi != dist->nassgireq) vgic_v4_configure_vsgis(vcpu->kvm); - if (kvm_vgic_global_state.has_gicv4_1 && + if (vgic_supports_direct_sgis(vcpu->kvm) && was_enabled != dist->enabled) kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_RELOAD_GICv4); else if (!was_enabled && dist->enabled) @@ -159,8 +176,18 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, switch (addr & 0x0c) { case GICD_TYPER2: - if (val != vgic_mmio_read_v3_misc(vcpu, addr, len)) + reg = vgic_mmio_read_v3_misc(vcpu, addr, len); + + if (reg == val) + return 0; + if (vgic_initialized(vcpu->kvm)) + return -EBUSY; + if ((reg ^ val) & ~GICD_TYPER2_nASSGIcap) return -EINVAL; + if (!system_supports_direct_sgis() && val) + return -EINVAL; + + dist->nassgicap = val & GICD_TYPER2_nASSGIcap; return 0; case GICD_IIDR: reg = vgic_mmio_read_v3_misc(vcpu, addr, len); @@ -178,7 +205,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, } case GICD_CTLR: /* Not a GICv4.1? No HW SGIs */ - if (!kvm_vgic_global_state.has_gicv4_1) + if (!vgic_supports_direct_sgis(vcpu->kvm)) val &= ~GICD_CTLR_nASSGIreq; dist->enabled = val & GICD_CTLR_ENABLE_SS_G1; diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index e416e433baff..a573b1f0c6cb 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -1091,7 +1091,7 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address, len = vgic_v3_init_dist_iodev(io_device); break; default: - BUG_ON(1); + BUG(); } io_device->base_addr = dist_base_address; diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c index d22a8ad7bcc5..7f1259b49c50 100644 --- a/arch/arm64/kvm/vgic/vgic-v3-nested.c +++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c @@ -36,6 +36,11 @@ struct shadow_if { static DEFINE_PER_CPU(struct shadow_if, shadow_if); +static int lr_map_idx_to_shadow_idx(struct shadow_if *shadow_if, int idx) +{ + return hweight16(shadow_if->lr_map & (BIT(idx) - 1)); +} + /* * Nesting GICv3 support * @@ -111,7 +116,7 @@ bool vgic_state_is_nested(struct kvm_vcpu *vcpu) { u64 xmo; - if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + if (is_nested_ctxt(vcpu)) { xmo = __vcpu_sys_reg(vcpu, HCR_EL2) & (HCR_IMO | HCR_FMO); WARN_ONCE(xmo && xmo != (HCR_IMO | HCR_FMO), "Separate virtual IRQ/FIQ settings not supported\n"); @@ -209,6 +214,29 @@ u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu) return reg; } +static u64 translate_lr_pintid(struct kvm_vcpu *vcpu, u64 lr) +{ + struct vgic_irq *irq; + + if (!(lr & ICH_LR_HW)) + return lr; + + /* We have the HW bit set, check for validity of pINTID */ + irq = vgic_get_vcpu_irq(vcpu, FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); + /* If there was no real mapping, nuke the HW bit */ + if (!irq || !irq->hw || irq->intid > VGIC_MAX_SPI) + lr &= ~ICH_LR_HW; + + /* Translate the virtual mapping to the real one, even if invalid */ + if (irq) { + lr &= ~ICH_LR_PHYS_ID_MASK; + lr |= FIELD_PREP(ICH_LR_PHYS_ID_MASK, (u64)irq->hwintid); + vgic_put_irq(vcpu->kvm, irq); + } + + return lr; +} + /* * For LRs which have HW bit set such as timer interrupts, we modify them to * have the host hardware interrupt number instead of the virtual one programmed @@ -217,58 +245,37 @@ u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu) static void vgic_v3_create_shadow_lr(struct kvm_vcpu *vcpu, struct vgic_v3_cpu_if *s_cpu_if) { - unsigned long lr_map = 0; - int index = 0; + struct shadow_if *shadow_if; + + shadow_if = container_of(s_cpu_if, struct shadow_if, cpuif); + shadow_if->lr_map = 0; for (int i = 0; i < kvm_vgic_global_state.nr_lr; i++) { u64 lr = __vcpu_sys_reg(vcpu, ICH_LRN(i)); - struct vgic_irq *irq; if (!(lr & ICH_LR_STATE)) - lr = 0; + continue; - if (!(lr & ICH_LR_HW)) - goto next; + lr = translate_lr_pintid(vcpu, lr); - /* We have the HW bit set, check for validity of pINTID */ - irq = vgic_get_vcpu_irq(vcpu, FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); - if (!irq || !irq->hw || irq->intid > VGIC_MAX_SPI ) { - /* There was no real mapping, so nuke the HW bit */ - lr &= ~ICH_LR_HW; - if (irq) - vgic_put_irq(vcpu->kvm, irq); - goto next; - } - - /* Translate the virtual mapping to the real one */ - lr &= ~ICH_LR_PHYS_ID_MASK; - lr |= FIELD_PREP(ICH_LR_PHYS_ID_MASK, (u64)irq->hwintid); - - vgic_put_irq(vcpu->kvm, irq); - -next: - s_cpu_if->vgic_lr[index] = lr; - if (lr) { - lr_map |= BIT(i); - index++; - } + s_cpu_if->vgic_lr[hweight16(shadow_if->lr_map)] = lr; + shadow_if->lr_map |= BIT(i); } - container_of(s_cpu_if, struct shadow_if, cpuif)->lr_map = lr_map; - s_cpu_if->used_lrs = index; + s_cpu_if->used_lrs = hweight16(shadow_if->lr_map); } void vgic_v3_sync_nested(struct kvm_vcpu *vcpu) { struct shadow_if *shadow_if = get_shadow_if(); - int i, index = 0; + int i; for_each_set_bit(i, &shadow_if->lr_map, kvm_vgic_global_state.nr_lr) { u64 lr = __vcpu_sys_reg(vcpu, ICH_LRN(i)); struct vgic_irq *irq; if (!(lr & ICH_LR_HW) || !(lr & ICH_LR_STATE)) - goto next; + continue; /* * If we had a HW lr programmed by the guest hypervisor, we @@ -277,15 +284,13 @@ void vgic_v3_sync_nested(struct kvm_vcpu *vcpu) */ irq = vgic_get_vcpu_irq(vcpu, FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); if (WARN_ON(!irq)) /* Shouldn't happen as we check on load */ - goto next; + continue; - lr = __gic_v3_get_lr(index); + lr = __gic_v3_get_lr(lr_map_idx_to_shadow_idx(shadow_if, i)); if (!(lr & ICH_LR_STATE)) irq->active = false; vgic_put_irq(vcpu->kvm, irq); - next: - index++; } } @@ -368,13 +373,11 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu) val = __vcpu_sys_reg(vcpu, ICH_LRN(i)); val &= ~ICH_LR_STATE; - val |= s_cpu_if->vgic_lr[i] & ICH_LR_STATE; + val |= s_cpu_if->vgic_lr[lr_map_idx_to_shadow_idx(shadow_if, i)] & ICH_LR_STATE; __vcpu_assign_sys_reg(vcpu, ICH_LRN(i), val); - s_cpu_if->vgic_lr[i] = 0; } - shadow_if->lr_map = 0; vcpu->arch.vgic_cpu.vgic_v3.used_lrs = 0; } @@ -398,9 +401,7 @@ void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu) { bool level; - level = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) & ICH_HCR_EL2_En; - if (level) - level &= vgic_v3_get_misr(vcpu); + level = (__vcpu_sys_reg(vcpu, ICH_HCR_EL2) & ICH_HCR_EL2_En) && vgic_v3_get_misr(vcpu); kvm_vgic_inject_irq(vcpu->kvm, vcpu, vcpu->kvm->arch.vgic.mi_intid, level, vcpu); } diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c index 193946108192..4d9343d2b0b1 100644 --- a/arch/arm64/kvm/vgic/vgic-v4.c +++ b/arch/arm64/kvm/vgic/vgic-v4.c @@ -356,7 +356,7 @@ int vgic_v4_put(struct kvm_vcpu *vcpu) { struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe; - if (!vgic_supports_direct_msis(vcpu->kvm) || !vpe->resident) + if (!vgic_supports_direct_irqs(vcpu->kvm) || !vpe->resident) return 0; return its_make_vpe_non_resident(vpe, vgic_v4_want_doorbell(vcpu)); @@ -367,7 +367,7 @@ int vgic_v4_load(struct kvm_vcpu *vcpu) struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe; int err; - if (!vgic_supports_direct_msis(vcpu->kvm) || vpe->resident) + if (!vgic_supports_direct_irqs(vcpu->kvm) || vpe->resident) return 0; if (vcpu_get_flag(vcpu, IN_WFI)) @@ -527,28 +527,26 @@ static struct vgic_irq *__vgic_host_irq_get_vlpi(struct kvm *kvm, int host_irq) return NULL; } -int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int host_irq) +void kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int host_irq) { struct vgic_irq *irq; unsigned long flags; - int ret = 0; if (!vgic_supports_direct_msis(kvm)) - return 0; + return; irq = __vgic_host_irq_get_vlpi(kvm, host_irq); if (!irq) - return 0; + return; raw_spin_lock_irqsave(&irq->irq_lock, flags); WARN_ON(irq->hw && irq->host_irq != host_irq); if (irq->hw) { atomic_dec(&irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count); irq->hw = false; - ret = its_unmap_vlpi(host_irq); + its_unmap_vlpi(host_irq); } raw_spin_unlock_irqrestore(&irq->irq_lock, flags); vgic_put_irq(kvm, irq); - return ret; } diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c new file mode 100644 index 000000000000..6bdbb221bcde --- /dev/null +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include "vgic.h" + +/* + * 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) +{ + u64 ich_vtr_el2; + int ret; + + if (!info->has_gcie_v3_compat) + 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; + kvm_vgic_global_state.can_emulate_gicv2 = false; + kvm_vgic_global_state.has_gicv4 = false; + kvm_vgic_global_state.has_gicv4_1 = false; + + ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config); + kvm_vgic_global_state.ich_vtr_el2 = (u32)ich_vtr_el2; + + /* + * The ListRegs field is 5 bits, but there is an architectural + * maximum of 16 list registers. Just ignore bit 4... + */ + kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1; + + ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3); + if (ret) { + kvm_err("Cannot register GICv3-legacy KVM device.\n"); + return ret; + } + + static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif); + kvm_info("GCIE legacy system register CPU interface\n"); + + return 0; +} diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 8f8096d48925..f5148b38120a 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -951,7 +951,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) * can be directly injected (GICv4). */ if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head) && - !vgic_supports_direct_msis(vcpu->kvm)) + !vgic_supports_direct_irqs(vcpu->kvm)) return; DEBUG_SPINLOCK_BUG_ON(!irqs_disabled()); @@ -965,7 +965,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) if (can_access_vgic_from_kernel()) vgic_restore_state(vcpu); - if (vgic_supports_direct_msis(vcpu->kvm)) + if (vgic_supports_direct_irqs(vcpu->kvm)) vgic_v4_commit(vcpu); } diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 4349084cb9a6..de1c1d3261c3 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -64,6 +64,24 @@ KVM_REG_ARM_VGIC_SYSREG_CRM_MASK | \ KVM_REG_ARM_VGIC_SYSREG_OP2_MASK) +#define KVM_ICC_SRE_EL2 (ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE | \ + ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB) +#define KVM_ICH_VTR_EL2_RES0 (ICH_VTR_EL2_DVIM | \ + ICH_VTR_EL2_A3V | \ + ICH_VTR_EL2_IDbits) +#define KVM_ICH_VTR_EL2_RES1 ICH_VTR_EL2_nV4 + +static inline u64 kvm_get_guest_vtr_el2(void) +{ + u64 vtr; + + vtr = kvm_vgic_global_state.ich_vtr_el2; + vtr &= ~KVM_ICH_VTR_EL2_RES0; + vtr |= KVM_ICH_VTR_EL2_RES1; + + return vtr; +} + /* * As per Documentation/virt/kvm/devices/arm-vgic-its.rst, * below macros are defined for ITS table entry encoding. @@ -297,6 +315,7 @@ int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write, int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr, bool is_write); int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); +const struct sys_reg_desc *vgic_v3_get_sysreg_table(unsigned int *sz); int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write, u32 intid, u32 *val); int kvm_register_vgic_device(unsigned long type); @@ -308,6 +327,8 @@ int vgic_init(struct kvm *kvm); void vgic_debug_init(struct kvm *kvm); void vgic_debug_destroy(struct kvm *kvm); +int vgic_v5_probe(const struct gic_kvm_info *info); + static inline int vgic_v3_max_apr_idx(struct kvm_vcpu *vcpu) { struct vgic_cpu *cpu_if = &vcpu->arch.vgic_cpu; @@ -369,7 +390,15 @@ 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); @@ -389,6 +418,17 @@ 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) +{ + return cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF) && + kvm_vgic_global_state.has_gcie_v3_compat; +} + +static inline bool vgic_is_v3(struct kvm *kvm) +{ + return kvm_vgic_global_state.type == VGIC_V3 || vgic_is_v3_compat(kvm); +} + int vgic_its_debug_init(struct kvm_device *dev); void vgic_its_debug_destroy(struct kvm_device *dev); diff --git a/arch/arm64/lib/.gitignore b/arch/arm64/lib/.gitignore new file mode 100644 index 000000000000..647d7a922e68 --- /dev/null +++ b/arch/arm64/lib/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# This now-removed directory used to contain generated files. +/crypto/ diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 027bfa9689c6..633e5223d944 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -1,7 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 - -obj-y += crypto/ - lib-y := clear_user.o delay.o copy_from_user.o \ copy_to_user.o copy_page.o \ clear_page.o csum.o insn.o memchr.o memcpy.o \ @@ -16,12 +13,6 @@ endif lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o -obj-$(CONFIG_CRC32_ARCH) += crc32-arm64.o -crc32-arm64-y := crc32.o crc32-core.o - -obj-$(CONFIG_CRC_T10DIF_ARCH) += crc-t10dif-arm64.o -crc-t10dif-arm64-y := crc-t10dif.o crc-t10dif-core.o - obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o obj-$(CONFIG_ARM64_MTE) += mte.o diff --git a/arch/arm64/lib/crypto/sha256.c b/arch/arm64/lib/crypto/sha256.c deleted file mode 100644 index bcf7a3adc0c4..000000000000 --- a/arch/arm64/lib/crypto/sha256.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-256 optimized for ARM64 - * - * Copyright 2025 Google LLC - */ -#include -#include -#include -#include - -asmlinkage void sha256_blocks_arch(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -EXPORT_SYMBOL_GPL(sha256_blocks_arch); -asmlinkage void sha256_block_neon(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -asmlinkage size_t __sha256_ce_transform(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_neon); -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_ce); - -void sha256_blocks_simd(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && - static_branch_likely(&have_neon)) { - if (static_branch_likely(&have_ce)) { - do { - size_t rem; - - kernel_neon_begin(); - rem = __sha256_ce_transform(state, - data, nblocks); - kernel_neon_end(); - data += (nblocks - rem) * SHA256_BLOCK_SIZE; - nblocks = rem; - } while (nblocks); - } else { - kernel_neon_begin(); - sha256_block_neon(state, data, nblocks); - kernel_neon_end(); - } - } else { - sha256_blocks_arch(state, data, nblocks); - } -} -EXPORT_SYMBOL_GPL(sha256_blocks_simd); - -bool sha256_is_arch_optimized(void) -{ - /* We always can use at least the ARM64 scalar implementation. */ - return true; -} -EXPORT_SYMBOL_GPL(sha256_is_arch_optimized); - -static int __init sha256_arm64_mod_init(void) -{ - if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && - cpu_have_named_feature(ASIMD)) { - static_branch_enable(&have_neon); - if (cpu_have_named_feature(SHA2)) - static_branch_enable(&have_ce); - } - return 0; -} -subsys_initcall(sha256_arm64_mod_init); - -static void __exit sha256_arm64_mod_exit(void) -{ -} -module_exit(sha256_arm64_mod_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-256 optimized for ARM64"); diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c index bcac4f55f9c1..c0557945939c 100644 --- a/arch/arm64/mm/contpte.c +++ b/arch/arm64/mm/contpte.c @@ -68,7 +68,144 @@ static void contpte_convert(struct mm_struct *mm, unsigned long addr, pte = pte_mkyoung(pte); } - __flush_tlb_range(&vma, start_addr, addr, PAGE_SIZE, true, 3); + /* + * On eliding the __tlb_flush_range() under BBML2+noabort: + * + * NOTE: Instead of using N=16 as the contiguous block length, we use + * N=4 for clarity. + * + * NOTE: 'n' and 'c' are used to denote the "contiguous bit" being + * unset and set, respectively. + * + * We worry about two cases where contiguous bit is used: + * - When folding N smaller non-contiguous ptes as 1 contiguous block. + * - When unfolding a contiguous block into N smaller non-contiguous ptes. + * + * Currently, the BBML0 folding case looks as follows: + * + * 0) Initial page-table layout: + * + * +----+----+----+----+ + * |RO,n|RO,n|RO,n|RW,n| <--- last page being set as RO + * +----+----+----+----+ + * + * 1) Aggregate AF + dirty flags using __ptep_get_and_clear(): + * + * +----+----+----+----+ + * | 0 | 0 | 0 | 0 | + * +----+----+----+----+ + * + * 2) __flush_tlb_range(): + * + * |____ tlbi + dsb ____| + * + * 3) __set_ptes() to repaint contiguous block: + * + * +----+----+----+----+ + * |RO,c|RO,c|RO,c|RO,c| + * +----+----+----+----+ + * + * 4) The kernel will eventually __flush_tlb() for changed page: + * + * |____| <--- tlbi + dsb + * + * As expected, the intermediate tlbi+dsb ensures that other PEs + * only ever see an invalid (0) entry, or the new contiguous TLB entry. + * The final tlbi+dsb will always throw away the newly installed + * contiguous TLB entry, which is a micro-optimisation opportunity, + * but does not affect correctness. + * + * In the BBML2 case, the change is avoiding the intermediate tlbi+dsb. + * This means a few things, but notably other PEs will still "see" any + * stale cached TLB entries. This could lead to a "contiguous bit + * misprogramming" issue until the final tlbi+dsb of the changed page, + * which would clear out both the stale (RW,n) entry and the new (RO,c) + * contiguous entry installed in its place. + * + * What this is saying, is the following: + * + * +----+----+----+----+ + * |RO,n|RO,n|RO,n|RW,n| <--- old page tables, all non-contiguous + * +----+----+----+----+ + * + * +----+----+----+----+ + * |RO,c|RO,c|RO,c|RO,c| <--- new page tables, all contiguous + * +----+----+----+----+ + * /\ + * || + * + * If both the old single (RW,n) and new contiguous (RO,c) TLB entries + * are present, and a write is made to this address, do we fault or + * is the write permitted (via amalgamation)? + * + * The relevant Arm ARM DDI 0487L.a requirements are RNGLXZ and RJQQTC, + * and together state that when BBML1 or BBML2 are implemented, either + * a TLB conflict abort is raised (which we expressly forbid), or will + * "produce an OA, access permissions, and memory attributes that are + * consistent with any of the programmed translation table values". + * + * That is to say, will either raise a TLB conflict, or produce one of + * the cached TLB entries, but never amalgamate. + * + * Thus, as the page tables are only considered "consistent" after + * the final tlbi+dsb (which evicts both the single stale (RW,n) TLB + * entry as well as the new contiguous (RO,c) TLB entry), omitting the + * initial tlbi+dsb is correct. + * + * It is also important to note that at the end of the BBML2 folding + * case, we are still left with potentially all N TLB entries still + * cached (the N-1 non-contiguous ptes, and the single contiguous + * block). However, over time, natural TLB pressure will cause the + * non-contiguous pte TLB entries to be flushed, leaving only the + * contiguous block TLB entry. This means that omitting the tlbi+dsb is + * not only correct, but also keeps our eventual performance benefits. + * + * For the unfolding case, BBML0 looks as follows: + * + * 0) Initial page-table layout: + * + * +----+----+----+----+ + * |RW,c|RW,c|RW,c|RW,c| <--- last page being set as RO + * +----+----+----+----+ + * + * 1) Aggregate AF + dirty flags using __ptep_get_and_clear(): + * + * +----+----+----+----+ + * | 0 | 0 | 0 | 0 | + * +----+----+----+----+ + * + * 2) __flush_tlb_range(): + * + * |____ tlbi + dsb ____| + * + * 3) __set_ptes() to repaint as non-contiguous: + * + * +----+----+----+----+ + * |RW,n|RW,n|RW,n|RW,n| + * +----+----+----+----+ + * + * 4) Update changed page permissions: + * + * +----+----+----+----+ + * |RW,n|RW,n|RW,n|RO,n| <--- last page permissions set + * +----+----+----+----+ + * + * 5) The kernel will eventually __flush_tlb() for changed page: + * + * |____| <--- tlbi + dsb + * + * For BBML2, we again remove the intermediate tlbi+dsb. Here, there + * are no issues, as the final tlbi+dsb covering the changed page is + * guaranteed to remove the original large contiguous (RW,c) TLB entry, + * as well as the intermediate (RW,n) TLB entry; the next access will + * install the new (RO,n) TLB entry and the page tables are only + * considered "consistent" after the final tlbi+dsb, so software must + * be prepared for this inconsistency prior to finishing the mm dance + * regardless. + */ + + if (!system_supports_bbml2_noabort()) + __flush_tlb_range(&vma, start_addr, addr, PAGE_SIZE, true, 3); __set_ptes(mm, start_addr, start_ptep, pte, CONT_PTES); } @@ -169,17 +306,46 @@ pte_t contpte_ptep_get(pte_t *ptep, pte_t orig_pte) for (i = 0; i < CONT_PTES; i++, ptep++) { pte = __ptep_get(ptep); - if (pte_dirty(pte)) + if (pte_dirty(pte)) { orig_pte = pte_mkdirty(orig_pte); + for (; i < CONT_PTES; i++, ptep++) { + pte = __ptep_get(ptep); + if (pte_young(pte)) { + orig_pte = pte_mkyoung(orig_pte); + break; + } + } + break; + } - if (pte_young(pte)) + if (pte_young(pte)) { orig_pte = pte_mkyoung(orig_pte); + i++; + ptep++; + for (; i < CONT_PTES; i++, ptep++) { + pte = __ptep_get(ptep); + if (pte_dirty(pte)) { + orig_pte = pte_mkdirty(orig_pte); + break; + } + } + break; + } } return orig_pte; } EXPORT_SYMBOL_GPL(contpte_ptep_get); +static inline bool contpte_is_consistent(pte_t pte, unsigned long pfn, + pgprot_t orig_prot) +{ + pgprot_t prot = pte_pgprot(pte_mkold(pte_mkclean(pte))); + + return pte_valid_cont(pte) && pte_pfn(pte) == pfn && + pgprot_val(prot) == pgprot_val(orig_prot); +} + pte_t contpte_ptep_get_lockless(pte_t *orig_ptep) { /* @@ -202,7 +368,6 @@ pte_t contpte_ptep_get_lockless(pte_t *orig_ptep) pgprot_t orig_prot; unsigned long pfn; pte_t orig_pte; - pgprot_t prot; pte_t *ptep; pte_t pte; int i; @@ -219,18 +384,44 @@ pte_t contpte_ptep_get_lockless(pte_t *orig_ptep) for (i = 0; i < CONT_PTES; i++, ptep++, pfn++) { pte = __ptep_get(ptep); - prot = pte_pgprot(pte_mkold(pte_mkclean(pte))); - if (!pte_valid_cont(pte) || - pte_pfn(pte) != pfn || - pgprot_val(prot) != pgprot_val(orig_prot)) + if (!contpte_is_consistent(pte, pfn, orig_prot)) goto retry; - if (pte_dirty(pte)) + if (pte_dirty(pte)) { orig_pte = pte_mkdirty(orig_pte); + for (; i < CONT_PTES; i++, ptep++, pfn++) { + pte = __ptep_get(ptep); - if (pte_young(pte)) + if (!contpte_is_consistent(pte, pfn, orig_prot)) + goto retry; + + if (pte_young(pte)) { + orig_pte = pte_mkyoung(orig_pte); + break; + } + } + break; + } + + if (pte_young(pte)) { orig_pte = pte_mkyoung(orig_pte); + i++; + ptep++; + pfn++; + for (; i < CONT_PTES; i++, ptep++, pfn++) { + pte = __ptep_get(ptep); + + if (!contpte_is_consistent(pte, pfn, orig_prot)) + goto retry; + + if (pte_dirty(pte)) { + orig_pte = pte_mkdirty(orig_pte); + break; + } + } + break; + } } return orig_pte; diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index ec0a337891dd..d816ff44faff 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -53,18 +53,12 @@ struct fault_info { }; static const struct fault_info fault_info[]; -static struct fault_info debug_fault_info[]; static inline const struct fault_info *esr_to_fault_info(unsigned long esr) { return fault_info + (esr & ESR_ELx_FSC); } -static inline const struct fault_info *esr_to_debug_fault_info(unsigned long esr) -{ - return debug_fault_info + DBG_ESR_EVT(esr); -} - static void data_abort_decode(unsigned long esr) { unsigned long iss2 = ESR_ELx_ISS2(esr); @@ -487,17 +481,29 @@ static void do_bad_area(unsigned long far, unsigned long esr, } } -static bool fault_from_pkey(unsigned long esr, struct vm_area_struct *vma, - unsigned int mm_flags) +static bool fault_from_pkey(struct vm_area_struct *vma, unsigned int mm_flags) { - unsigned long iss2 = ESR_ELx_ISS2(esr); - if (!system_supports_poe()) return false; - if (esr_fsc_is_permission_fault(esr) && (iss2 & ESR_ELx_Overlay)) - return true; - + /* + * We do not check whether an Overlay fault has occurred because we + * cannot make a decision based solely on its value: + * + * - If Overlay is set, a fault did occur due to POE, but it may be + * spurious in those cases where we update POR_EL0 without ISB (e.g. + * on context-switch). We would then need to manually check POR_EL0 + * against vma_pkey(vma), which is exactly what + * arch_vma_access_permitted() does. + * + * - If Overlay is not set, we may still need to report a pkey fault. + * This is the case if an access was made within a mapping but with no + * page mapped, and POR_EL0 forbids the access (according to + * vma_pkey()). Such access will result in a SIGSEGV regardless + * because core code checks arch_vma_access_permitted(), but in order + * to report the correct error code - SEGV_PKUERR - we must handle + * that case here. + */ return !arch_vma_access_permitted(vma, mm_flags & FAULT_FLAG_WRITE, mm_flags & FAULT_FLAG_INSTRUCTION, @@ -549,7 +555,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, const struct fault_info *inf; struct mm_struct *mm = current->mm; vm_fault_t fault; - unsigned long vm_flags; + vm_flags_t vm_flags; unsigned int mm_flags = FAULT_FLAG_DEFAULT; unsigned long addr = untagged_addr(far); struct vm_area_struct *vma; @@ -635,7 +641,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, goto bad_area; } - if (fault_from_pkey(esr, vma, mm_flags)) { + if (fault_from_pkey(vma, mm_flags)) { pkey = vma_pkey(vma); vma_end_read(vma); fault = 0; @@ -679,7 +685,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, goto bad_area; } - if (fault_from_pkey(esr, vma, mm_flags)) { + if (fault_from_pkey(vma, mm_flags)) { pkey = vma_pkey(vma); mmap_read_unlock(mm); fault = 0; @@ -826,6 +832,7 @@ static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs) */ siaddr = untagged_addr(far); } + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr); return 0; @@ -837,9 +844,12 @@ static int do_tag_check_fault(unsigned long far, unsigned long esr, /* * The architecture specifies that bits 63:60 of FAR_EL1 are UNKNOWN * for tag check faults. Set them to corresponding bits in the untagged - * address. + * address if ARM64_MTE_FAR isn't supported. + * Otherwise, bits 63:60 of FAR_EL1 are not UNKNOWN. */ - far = (__untagged_addr(far) & ~MTE_TAG_MASK) | (far & MTE_TAG_MASK); + if (!cpus_have_cap(ARM64_MTE_FAR)) + far = (__untagged_addr(far) & ~MTE_TAG_MASK) | (far & MTE_TAG_MASK); + do_bad_area(far, esr, regs); return 0; } @@ -938,75 +948,6 @@ void do_sp_pc_abort(unsigned long addr, unsigned long esr, struct pt_regs *regs) } NOKPROBE_SYMBOL(do_sp_pc_abort); -/* - * __refdata because early_brk64 is __init, but the reference to it is - * clobbered at arch_initcall time. - * See traps.c and debug-monitors.c:debug_traps_init(). - */ -static struct fault_info __refdata debug_fault_info[] = { - { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" }, - { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" }, - { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 3" }, - { do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" }, - { do_bad, SIGKILL, SI_KERNEL, "aarch32 vector catch" }, - { early_brk64, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 7" }, -}; - -void __init hook_debug_fault_code(int nr, - int (*fn)(unsigned long, unsigned long, struct pt_regs *), - int sig, int code, const char *name) -{ - BUG_ON(nr < 0 || nr >= ARRAY_SIZE(debug_fault_info)); - - debug_fault_info[nr].fn = fn; - debug_fault_info[nr].sig = sig; - debug_fault_info[nr].code = code; - debug_fault_info[nr].name = name; -} - -/* - * In debug exception context, we explicitly disable preemption despite - * having interrupts disabled. - * This serves two purposes: it makes it much less likely that we would - * accidentally schedule in exception context and it will force a warning - * if we somehow manage to schedule by accident. - */ -static void debug_exception_enter(struct pt_regs *regs) -{ - preempt_disable(); - - /* This code is a bit fragile. Test it. */ - RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work"); -} -NOKPROBE_SYMBOL(debug_exception_enter); - -static void debug_exception_exit(struct pt_regs *regs) -{ - preempt_enable_no_resched(); -} -NOKPROBE_SYMBOL(debug_exception_exit); - -void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr, - struct pt_regs *regs) -{ - const struct fault_info *inf = esr_to_debug_fault_info(esr); - unsigned long pc = instruction_pointer(regs); - - debug_exception_enter(regs); - - if (user_mode(regs) && !is_ttbr0_addr(pc)) - arm64_apply_bp_hardening(); - - if (inf->fn(addr_if_watchpoint, esr, regs)) { - arm64_notify_die(inf->name, regs, inf->sig, inf->code, pc, esr); - } - - debug_exception_exit(regs); -} -NOKPROBE_SYMBOL(do_debug_exception); - /* * Used during anonymous page fault handling. */ diff --git a/arch/arm64/mm/gcs.c b/arch/arm64/mm/gcs.c index 5c46ec527b1c..6e93f78de79b 100644 --- a/arch/arm64/mm/gcs.c +++ b/arch/arm64/mm/gcs.c @@ -157,12 +157,6 @@ void gcs_free(struct task_struct *task) if (!system_supports_gcs()) return; - /* - * When fork() with CLONE_VM fails, the child (tsk) already - * has a GCS allocated, and exit_thread() calls this function - * to free it. In this case the parent (current) and the - * child share the same mm struct. - */ if (!task->mm || task->mm != current->mm) return; diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 0c8737f4f2ce..1d90a7e75333 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -225,7 +225,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, ncontig = num_contig_ptes(sz, &pgsize); if (!pte_present(pte)) { - for (i = 0; i < ncontig; i++, ptep++, addr += pgsize) + for (i = 0; i < ncontig; i++, ptep++) __set_ptes_anysz(mm, ptep, pte, 1, pgsize); return; } diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 0c8c35dd645e..ea84a61ed508 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -106,7 +106,7 @@ static void __init arch_reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), &crash_size, &crash_base, - &low_size, &high); + &low_size, NULL, &high); if (ret) return; diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c index c86c348857c4..08ee177432c2 100644 --- a/arch/arm64/mm/mmap.c +++ b/arch/arm64/mm/mmap.c @@ -81,7 +81,7 @@ static int __init adjust_protection_map(void) } arch_initcall(adjust_protection_map); -pgprot_t vm_get_page_prot(unsigned long vm_flags) +pgprot_t vm_get_page_prot(vm_flags_t vm_flags) { ptdesc_t prot; diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 8fcf59ba39db..183801520740 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -46,13 +47,6 @@ #define NO_CONT_MAPPINGS BIT(1) #define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */ -enum pgtable_type { - TABLE_PTE, - TABLE_PMD, - TABLE_PUD, - TABLE_P4D, -}; - u64 kimage_voffset __ro_after_init; EXPORT_SYMBOL(kimage_voffset); @@ -1305,7 +1299,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr) next = addr; end = addr + PUD_SIZE; do { - pmd_free_pte_page(pmdp, next); + if (pmd_present(pmdp_get(pmdp))) + pmd_free_pte_page(pmdp, next); } while (pmdp++, next += PMD_SIZE, next != end); pud_clear(pudp); @@ -1523,24 +1518,41 @@ static int __init prevent_bootmem_remove_init(void) early_initcall(prevent_bootmem_remove_init); #endif -pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) +pte_t modify_prot_start_ptes(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep, unsigned int nr) { + pte_t pte = get_and_clear_ptes(vma->vm_mm, addr, ptep, nr); + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_2645198)) { /* * Break-before-make (BBM) is required for all user space mappings * when the permission changes from executable to non-executable * in cases where cpu is affected with errata #2645198. */ - if (pte_user_exec(ptep_get(ptep))) - return ptep_clear_flush(vma, addr, ptep); + if (pte_accessible(vma->vm_mm, pte) && pte_user_exec(pte)) + __flush_tlb_range(vma, addr, nr * PAGE_SIZE, + PAGE_SIZE, true, 3); } - return ptep_get_and_clear(vma->vm_mm, addr, ptep); + + return pte; +} + +pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) +{ + return modify_prot_start_ptes(vma, addr, ptep, 1); +} + +void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep, pte_t old_pte, pte_t pte, + unsigned int nr) +{ + set_ptes(vma->vm_mm, addr, ptep, pte, nr); } void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t old_pte, pte_t pte) { - set_pte_at(vma->vm_mm, addr, ptep, pte); + modify_prot_commit_ptes(vma, addr, ptep, old_pte, pte, 1); } /* diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 80d470aa469d..8c75965afc9e 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -454,7 +454,7 @@ SYM_FUNC_START(__cpu_setup) dsb nsh msr cpacr_el1, xzr // Reset cpacr_el1 - mov x1, #1 << 12 // Reset mdscr_el1 and disable + mov x1, MDSCR_EL1_TDCC // Reset mdscr_el1 and disable msr mdscr_el1, x1 // access to the DCC from EL0 reset_pmuserenr_el0 x1 // Disable PMU access from EL0 reset_amuserenr_el0 x1 // Disable AMU access from EL0 @@ -518,7 +518,6 @@ alternative_else_nop_endif msr REG_PIR_EL1, x0 orr tcr2, tcr2, TCR2_EL1_PIE - msr REG_TCR2_EL1, x0 .Lskip_indirection: diff --git a/arch/arm64/mm/ptdump_debugfs.c b/arch/arm64/mm/ptdump_debugfs.c index 68bf1a125502..1e308328c079 100644 --- a/arch/arm64/mm/ptdump_debugfs.c +++ b/arch/arm64/mm/ptdump_debugfs.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include #include #include @@ -9,9 +8,7 @@ static int ptdump_show(struct seq_file *m, void *v) { struct ptdump_info *info = m->private; - get_online_mems(); ptdump_walk(m, info); - put_online_mems(); return 0; } DEFINE_SHOW_ATTRIBUTE(ptdump); diff --git a/arch/arm64/net/bpf_jit.h b/arch/arm64/net/bpf_jit.h index a3b0e693a125..bbea4f36f9f2 100644 --- a/arch/arm64/net/bpf_jit.h +++ b/arch/arm64/net/bpf_jit.h @@ -325,4 +325,9 @@ #define A64_MRS_SP_EL0(Rt) \ aarch64_insn_gen_mrs(Rt, AARCH64_INSN_SYSREG_SP_EL0) +/* Barriers */ +#define A64_SB aarch64_insn_get_sb_value() +#define A64_DSB_NSH (aarch64_insn_get_dsb_base_value() | 0x7 << 8) +#define A64_ISB aarch64_insn_get_isb_value() + #endif /* _BPF_JIT_H */ diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index da8b89dd2910..52ffe115a8c4 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) #define TCCNT_PTR (MAX_BPF_JIT_REG + 2) #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) +#define PRIVATE_SP (MAX_BPF_JIT_REG + 4) #define ARENA_VM_START (MAX_BPF_JIT_REG + 5) #define check_imm(bits, imm) do { \ @@ -68,6 +70,8 @@ static const int bpf2a64[] = { [TCCNT_PTR] = A64_R(26), /* temporary register for blinding constants */ [BPF_REG_AX] = A64_R(9), + /* callee saved register for private stack pointer */ + [PRIVATE_SP] = A64_R(27), /* callee saved register for kern_vm_start address */ [ARENA_VM_START] = A64_R(28), }; @@ -86,6 +90,7 @@ struct jit_ctx { u64 user_vm_start; u64 arena_vm_start; bool fp_used; + bool priv_sp_used; bool write; }; @@ -98,6 +103,10 @@ struct bpf_plt { #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) +/* Memory size/value to protect private stack overflow/underflow */ +#define PRIV_STACK_GUARD_SZ 16 +#define PRIV_STACK_GUARD_VAL 0xEB9F12345678eb9fULL + static inline void emit(const u32 insn, struct jit_ctx *ctx) { if (ctx->image != NULL && ctx->write) @@ -106,6 +115,14 @@ static inline void emit(const u32 insn, struct jit_ctx *ctx) ctx->idx++; } +static inline void emit_u32_data(const u32 data, struct jit_ctx *ctx) +{ + if (ctx->image != NULL && ctx->write) + ctx->image[ctx->idx] = data; + + ctx->idx++; +} + static inline void emit_a64_mov_i(const int is64, const int reg, const s32 val, struct jit_ctx *ctx) { @@ -166,6 +183,12 @@ static inline void emit_bti(u32 insn, struct jit_ctx *ctx) emit(insn, ctx); } +static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx) +{ + if (IS_ENABLED(CONFIG_CFI_CLANG)) + emit_u32_data(hash, ctx); +} + /* * Kernel addresses in the vmalloc space use at most 48 bits, and the * remaining bits are guaranteed to be 0x1. So we can compose the address @@ -387,8 +410,11 @@ static void find_used_callee_regs(struct jit_ctx *ctx) if (reg_used & 8) ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_9]; - if (reg_used & 16) + if (reg_used & 16) { ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_FP]; + if (ctx->priv_sp_used) + ctx->used_callee_reg[i++] = bpf2a64[PRIVATE_SP]; + } if (ctx->arena_vm_start) ctx->used_callee_reg[i++] = bpf2a64[ARENA_VM_START]; @@ -412,6 +438,7 @@ static void push_callee_regs(struct jit_ctx *ctx) emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); emit(A64_PUSH(A64_R(25), A64_R(26), A64_SP), ctx); emit(A64_PUSH(A64_R(27), A64_R(28), A64_SP), ctx); + ctx->fp_used = true; } else { find_used_callee_regs(ctx); for (i = 0; i + 1 < ctx->nr_used_callee_reg; i += 2) { @@ -461,6 +488,19 @@ static void pop_callee_regs(struct jit_ctx *ctx) } } +static void emit_percpu_ptr(const u8 dst_reg, void __percpu *ptr, + struct jit_ctx *ctx) +{ + const u8 tmp = bpf2a64[TMP_REG_1]; + + emit_a64_mov_i64(dst_reg, (__force const u64)ptr, ctx); + if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN)) + emit(A64_MRS_TPIDR_EL2(tmp), ctx); + else + emit(A64_MRS_TPIDR_EL1(tmp), ctx); + emit(A64_ADD(1, dst_reg, dst_reg, tmp), ctx); +} + #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) @@ -476,7 +516,8 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) const bool is_main_prog = !bpf_is_subprog(prog); const u8 fp = bpf2a64[BPF_REG_FP]; const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; - const int idx0 = ctx->idx; + const u8 priv_sp = bpf2a64[PRIVATE_SP]; + void __percpu *priv_stack_ptr; int cur_offset; /* @@ -502,6 +543,9 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) * */ + emit_kcfi(is_main_prog ? cfi_bpf_hash : cfi_bpf_subprog_hash, ctx); + const int idx0 = ctx->idx; + /* bpf function may be invoked by 3 instruction types: * 1. bl, attached via freplace to bpf prog via short jump * 2. br, attached via freplace to bpf prog via long jump @@ -551,15 +595,23 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) emit(A64_SUB_I(1, A64_SP, A64_FP, 96), ctx); } - if (ctx->fp_used) - /* Set up BPF prog stack base register */ - emit(A64_MOV(1, fp, A64_SP), ctx); - /* Stack must be multiples of 16B */ ctx->stack_size = round_up(prog->aux->stack_depth, 16); + if (ctx->fp_used) { + if (ctx->priv_sp_used) { + /* Set up private stack pointer */ + priv_stack_ptr = prog->aux->priv_stack_ptr + PRIV_STACK_GUARD_SZ; + emit_percpu_ptr(priv_sp, priv_stack_ptr, ctx); + emit(A64_ADD_I(1, fp, priv_sp, ctx->stack_size), ctx); + } else { + /* Set up BPF prog stack base register */ + emit(A64_MOV(1, fp, A64_SP), ctx); + } + } + /* Set up function call stack */ - if (ctx->stack_size) + if (ctx->stack_size && !ctx->priv_sp_used) emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); if (ctx->arena_vm_start) @@ -623,7 +675,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) emit(A64_STR64I(tcc, ptr, 0), ctx); /* restore SP */ - if (ctx->stack_size) + if (ctx->stack_size && !ctx->priv_sp_used) emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); pop_callee_regs(ctx); @@ -991,7 +1043,7 @@ static void build_epilogue(struct jit_ctx *ctx, bool was_classic) const u8 ptr = bpf2a64[TCCNT_PTR]; /* We're done with BPF stack */ - if (ctx->stack_size) + if (ctx->stack_size && !ctx->priv_sp_used) emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); pop_callee_regs(ctx); @@ -1120,6 +1172,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, const u8 tmp2 = bpf2a64[TMP_REG_2]; const u8 fp = bpf2a64[BPF_REG_FP]; const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; + const u8 priv_sp = bpf2a64[PRIVATE_SP]; const s16 off = insn->off; const s32 imm = insn->imm; const int i = insn - ctx->prog->insnsi; @@ -1564,7 +1617,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, src = tmp2; } if (src == fp) { - src_adj = A64_SP; + src_adj = ctx->priv_sp_used ? priv_sp : A64_SP; off_adj = off + ctx->stack_size; } else { src_adj = src; @@ -1630,17 +1683,14 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, return ret; break; - /* speculation barrier */ + /* speculation barrier against v1 and v4 */ case BPF_ST | BPF_NOSPEC: - /* - * Nothing required here. - * - * In case of arm64, we rely on the firmware mitigation of - * Speculative Store Bypass as controlled via the ssbd kernel - * parameter. Whenever the mitigation is enabled, it works - * for all of the kernel code with no need to provide any - * additional instructions. - */ + if (alternative_has_cap_likely(ARM64_HAS_SB)) { + emit(A64_SB, ctx); + } else { + emit(A64_DSB_NSH, ctx); + emit(A64_ISB, ctx); + } break; /* ST: *(size *)(dst + off) = imm */ @@ -1657,7 +1707,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, dst = tmp2; } if (dst == fp) { - dst_adj = A64_SP; + dst_adj = ctx->priv_sp_used ? priv_sp : A64_SP; off_adj = off + ctx->stack_size; } else { dst_adj = dst; @@ -1719,7 +1769,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, dst = tmp2; } if (dst == fp) { - dst_adj = A64_SP; + dst_adj = ctx->priv_sp_used ? priv_sp : A64_SP; off_adj = off + ctx->stack_size; } else { dst_adj = dst; @@ -1862,6 +1912,39 @@ 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; + u64 *stack_ptr; + + for_each_possible_cpu(cpu) { + stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu); + stack_ptr[0] = PRIV_STACK_GUARD_VAL; + stack_ptr[1] = PRIV_STACK_GUARD_VAL; + stack_ptr[underflow_idx] = PRIV_STACK_GUARD_VAL; + stack_ptr[underflow_idx + 1] = PRIV_STACK_GUARD_VAL; + } +} + +static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size, + struct bpf_prog *prog) +{ + int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3; + u64 *stack_ptr; + + for_each_possible_cpu(cpu) { + stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu); + if (stack_ptr[0] != PRIV_STACK_GUARD_VAL || + stack_ptr[1] != PRIV_STACK_GUARD_VAL || + stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL || + stack_ptr[underflow_idx + 1] != PRIV_STACK_GUARD_VAL) { + pr_err("BPF private stack overflow/underflow detected for prog %sx\n", + bpf_jit_get_prog_name(prog)); + break; + } + } +} + struct arm64_jit_data { struct bpf_binary_header *header; u8 *ro_image; @@ -1874,9 +1957,11 @@ struct bpf_prog *bpf_int_jit_compile(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; + 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; @@ -1908,6 +1993,23 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) } prog->aux->jit_data = jit_data; } + priv_stack_ptr = prog->aux->priv_stack_ptr; + if (!priv_stack_ptr && prog->aux->jits_use_priv_stack) { + /* Allocate actual private stack size with verifier-calculated + * stack size plus two memory guards to protect overflow and + * underflow. + */ + 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; + goto out_priv_stack; + } + + priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz); + prog->aux->priv_stack_ptr = priv_stack_ptr; + } if (jit_data->ctx.offset) { ctx = jit_data->ctx; ro_image_ptr = jit_data->ro_image; @@ -1931,6 +2033,9 @@ 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.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); + if (priv_stack_ptr) + ctx.priv_sp_used = true; + /* Pass 1: Estimate the maximum image size. * * BPF line info needs ctx->offset[i] to be the offset of @@ -2058,9 +2163,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) jit_data->ro_header = ro_header; } - prog->bpf_func = (void *)ctx.ro_image; + prog->bpf_func = (void *)ctx.ro_image + cfi_get_offset(); prog->jited = 1; - prog->jited_len = prog_size; + prog->jited_len = prog_size - cfi_get_offset(); if (!prog->is_func || extra_pass) { int i; @@ -2070,7 +2175,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.offset[i] *= AARCH64_INSN_SIZE; bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); out_off: + if (!ro_header && priv_stack_ptr) { + free_percpu(priv_stack_ptr); + prog->aux->priv_stack_ptr = NULL; + } kvfree(ctx.offset); +out_priv_stack: kfree(jit_data); prog->aux->jit_data = NULL; } @@ -2089,6 +2199,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) goto out_off; } +bool bpf_jit_supports_private_stack(void) +{ + return true; +} + bool bpf_jit_supports_kfunc_call(void) { return true; @@ -2243,11 +2358,6 @@ static int calc_arg_aux(const struct btf_func_model *m, /* the rest arguments are passed through stack */ for (; i < m->nr_args; i++) { - /* We can not know for sure about exact alignment needs for - * struct passed on stack, so deny those - */ - if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) - return -ENOTSUPP; stack_slots = (m->arg_size[i] + 7) / 8; a->bstack_for_args += stack_slots * 8; a->ostack_for_args = a->ostack_for_args + stack_slots * 8; @@ -2434,6 +2544,12 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, /* return address locates above FP */ retaddr_off = stack_size + 8; + if (flags & BPF_TRAMP_F_INDIRECT) { + /* + * Indirect call for bpf_struct_ops + */ + emit_kcfi(cfi_get_func_hash(func_addr), ctx); + } /* bpf trampoline may be invoked by 3 instruction types: * 1. bl, attached to bpf prog or kernel function via short jump * 2. br, attached to bpf prog or kernel function via long jump @@ -2911,6 +3027,17 @@ bool bpf_jit_supports_percpu_insn(void) return true; } +bool bpf_jit_bypass_spec_v4(void) +{ + /* In case of arm64, we rely on the firmware mitigation of Speculative + * Store Bypass as controlled via the ssbd kernel parameter. Whenever + * the mitigation is enabled, it works for all of the kernel code with + * no need to provide any additional instructions. Therefore, skip + * inserting nospec insns against Spectre v4. + */ + return true; +} + bool bpf_jit_inlines_helper_call(s32 imm) { switch (imm) { @@ -2928,6 +3055,8 @@ void bpf_jit_free(struct bpf_prog *prog) if (prog->jited) { struct arm64_jit_data *jit_data = prog->aux->jit_data; struct bpf_binary_header *hdr; + void __percpu *priv_stack_ptr; + int priv_stack_alloc_sz; /* * If we fail the final pass of JIT (from jit_subprogs), @@ -2939,8 +3068,16 @@ void bpf_jit_free(struct bpf_prog *prog) sizeof(jit_data->header->size)); kfree(jit_data); } + prog->bpf_func -= cfi_get_offset(); hdr = bpf_jit_binary_pack_hdr(prog); bpf_jit_binary_pack_free(hdr, NULL); + priv_stack_ptr = prog->aux->priv_stack_ptr; + if (priv_stack_ptr) { + priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) + + 2 * PRIV_STACK_GUARD_SZ; + priv_stack_check_guard(priv_stack_ptr, priv_stack_alloc_sz, prog); + free_percpu(prog->aux->priv_stack_ptr); + } WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); } diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 10effd4cff6b..9ff5cdbd2759 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -35,7 +35,8 @@ HAS_GENERIC_AUTH HAS_GENERIC_AUTH_ARCH_QARMA3 HAS_GENERIC_AUTH_ARCH_QARMA5 HAS_GENERIC_AUTH_IMP_DEF -HAS_GIC_CPUIF_SYSREGS +HAS_GICV3_CPUIF +HAS_GICV5_CPUIF HAS_GIC_PRIO_MASKING HAS_GIC_PRIO_RELAXED_SYNC HAS_HCR_NV1 @@ -45,11 +46,14 @@ HAS_LPA2 HAS_LSE_ATOMICS HAS_MOPS HAS_NESTED_VIRT +HAS_BBML2_NOABORT HAS_PAN HAS_PMUV3 HAS_S1PIE HAS_S1POE +HAS_SCTLR2 HAS_RAS_EXTN +HAS_RASV1P1_EXTN HAS_RNG HAS_SB HAS_STAGE2_FWB @@ -68,6 +72,8 @@ MPAM MPAM_HCR MTE MTE_ASYMM +MTE_FAR +MTE_STORE_ONLY SME SME_FA64 SME2 diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl index 0765b3a8d6d6..8d9088bc577d 100644 --- a/arch/arm64/tools/syscall_32.tbl +++ b/arch/arm64/tools/syscall_32.tbl @@ -479,3 +479,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 8a8cf6874298..696ab1f32a67 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1314,7 +1314,10 @@ UnsignedEnum 19:16 UINJ 0b0000 NI 0b0001 IMP EndEnum -Res0 15:12 +UnsignedEnum 15:12 GCIE + 0b0000 NI + 0b0001 IMP +EndEnum UnsignedEnum 11:8 MTEFAR 0b0000 NI 0b0001 IMP @@ -1329,6 +1332,138 @@ UnsignedEnum 3:0 MTEPERM EndEnum EndSysreg + +SysregFields BRBINFx_EL1 +Res0 63:47 +Field 46 CCU +Field 45:40 CC_EXP +Field 39:32 CC_MANT +Res0 31:18 +Field 17 LASTFAILED +Field 16 T +Res0 15:14 +Enum 13:8 TYPE + 0b000000 DIRECT_UNCOND + 0b000001 INDIRECT + 0b000010 DIRECT_LINK + 0b000011 INDIRECT_LINK + 0b000101 RET + 0b000111 ERET + 0b001000 DIRECT_COND + 0b100001 DEBUG_HALT + 0b100010 CALL + 0b100011 TRAP + 0b100100 SERROR + 0b100110 INSN_DEBUG + 0b100111 DATA_DEBUG + 0b101010 ALIGN_FAULT + 0b101011 INSN_FAULT + 0b101100 DATA_FAULT + 0b101110 IRQ + 0b101111 FIQ + 0b110000 IMPDEF_TRAP_EL3 + 0b111001 DEBUG_EXIT +EndEnum +Enum 7:6 EL + 0b00 EL0 + 0b01 EL1 + 0b10 EL2 + 0b11 EL3 +EndEnum +Field 5 MPRED +Res0 4:2 +Enum 1:0 VALID + 0b00 NONE + 0b01 TARGET + 0b10 SOURCE + 0b11 FULL +EndEnum +EndSysregFields + +SysregFields BRBCR_ELx +Res0 63:24 +Field 23 EXCEPTION +Field 22 ERTN +Res0 21:10 +Field 9 FZPSS +Field 8 FZP +Res0 7 +Enum 6:5 TS + 0b01 VIRTUAL + 0b10 GUEST_PHYSICAL + 0b11 PHYSICAL +EndEnum +Field 4 MPRED +Field 3 CC +Res0 2 +Field 1 ExBRE +Field 0 E0BRE +EndSysregFields + +Sysreg BRBCR_EL1 2 1 9 0 0 +Fields BRBCR_ELx +EndSysreg + +Sysreg BRBFCR_EL1 2 1 9 0 1 +Res0 63:30 +Enum 29:28 BANK + 0b00 BANK_0 + 0b01 BANK_1 +EndEnum +Res0 27:23 +Field 22 CONDDIR +Field 21 DIRCALL +Field 20 INDCALL +Field 19 RTN +Field 18 INDIRECT +Field 17 DIRECT +Field 16 EnI +Res0 15:8 +Field 7 PAUSED +Field 6 LASTFAILED +Res0 5:0 +EndSysreg + +Sysreg BRBTS_EL1 2 1 9 0 2 +Field 63:0 TS +EndSysreg + +Sysreg BRBINFINJ_EL1 2 1 9 1 0 +Fields BRBINFx_EL1 +EndSysreg + +Sysreg BRBSRCINJ_EL1 2 1 9 1 1 +Field 63:0 ADDRESS +EndSysreg + +Sysreg BRBTGTINJ_EL1 2 1 9 1 2 +Field 63:0 ADDRESS +EndSysreg + +Sysreg BRBIDR0_EL1 2 1 9 2 0 +Res0 63:16 +Enum 15:12 CC + 0b0101 20_BIT +EndEnum +Enum 11:8 FORMAT + 0b0000 FORMAT_0 +EndEnum +Enum 7:0 NUMREC + 0b00001000 8 + 0b00010000 16 + 0b00100000 32 + 0b01000000 64 +EndEnum +EndSysreg + +Sysreg BRBCR_EL2 2 4 9 0 0 +Fields BRBCR_ELx +EndSysreg + +Sysreg BRBCR_EL12 2 5 9 0 0 +Fields BRBCR_ELx +EndSysreg + Sysreg ID_AA64ZFR0_EL1 3 0 0 4 4 Res0 63:60 UnsignedEnum 59:56 F64MM @@ -3021,6 +3156,435 @@ Sysreg PMIAR_EL1 3 0 9 14 7 Field 63:0 ADDRESS EndSysreg +SysregFields ICC_PPI_HMRx_EL1 +Field 63 HM63 +Field 62 HM62 +Field 61 HM61 +Field 60 HM60 +Field 59 HM59 +Field 58 HM58 +Field 57 HM57 +Field 56 HM56 +Field 55 HM55 +Field 54 HM54 +Field 53 HM53 +Field 52 HM52 +Field 51 HM51 +Field 50 HM50 +Field 49 HM49 +Field 48 HM48 +Field 47 HM47 +Field 46 HM46 +Field 45 HM45 +Field 44 HM44 +Field 43 HM43 +Field 42 HM42 +Field 41 HM41 +Field 40 HM40 +Field 39 HM39 +Field 38 HM38 +Field 37 HM37 +Field 36 HM36 +Field 35 HM35 +Field 34 HM34 +Field 33 HM33 +Field 32 HM32 +Field 31 HM31 +Field 30 HM30 +Field 29 HM29 +Field 28 HM28 +Field 27 HM27 +Field 26 HM26 +Field 25 HM25 +Field 24 HM24 +Field 23 HM23 +Field 22 HM22 +Field 21 HM21 +Field 20 HM20 +Field 19 HM19 +Field 18 HM18 +Field 17 HM17 +Field 16 HM16 +Field 15 HM15 +Field 14 HM14 +Field 13 HM13 +Field 12 HM12 +Field 11 HM11 +Field 10 HM10 +Field 9 HM9 +Field 8 HM8 +Field 7 HM7 +Field 6 HM6 +Field 5 HM5 +Field 4 HM4 +Field 3 HM3 +Field 2 HM2 +Field 1 HM1 +Field 0 HM0 +EndSysregFields + +Sysreg ICC_PPI_HMR0_EL1 3 0 12 10 0 +Fields ICC_PPI_HMRx_EL1 +EndSysreg + +Sysreg ICC_PPI_HMR1_EL1 3 0 12 10 1 +Fields ICC_PPI_HMRx_EL1 +EndSysreg + +Sysreg ICC_IDR0_EL1 3 0 12 10 2 +Res0 63:12 +UnsignedEnum 11:8 GCIE_LEGACY + 0b0000 NI + 0b0001 IMP +EndEnum +UnsignedEnum 7:4 PRI_BITS + 0b0011 4BITS + 0b0100 5BITS +EndEnum +UnsignedEnum 3:0 ID_BITS + 0b0000 16BITS + 0b0001 24BITS +EndEnum +EndSysreg + +Sysreg ICC_ICSR_EL1 3 0 12 10 4 +Res0 63:48 +Field 47:32 IAFFID +Res0 31:16 +Field 15:11 Priority +Res0 10:6 +Field 5 HM +Field 4 Active +Field 3 IRM +Field 2 Pending +Field 1 Enabled +Field 0 F +EndSysreg + +SysregFields ICC_PPI_ENABLERx_EL1 +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 ICC_PPI_ENABLER0_EL1 3 0 12 10 6 +Fields ICC_PPI_ENABLERx_EL1 +EndSysreg + +Sysreg ICC_PPI_ENABLER1_EL1 3 0 12 10 7 +Fields ICC_PPI_ENABLERx_EL1 +EndSysreg + +SysregFields ICC_PPI_ACTIVERx_EL1 +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 ICC_PPI_CACTIVER0_EL1 3 0 12 13 0 +Fields ICC_PPI_ACTIVERx_EL1 +EndSysreg + +Sysreg ICC_PPI_CACTIVER1_EL1 3 0 12 13 1 +Fields ICC_PPI_ACTIVERx_EL1 +EndSysreg + +Sysreg ICC_PPI_SACTIVER0_EL1 3 0 12 13 2 +Fields ICC_PPI_ACTIVERx_EL1 +EndSysreg + +Sysreg ICC_PPI_SACTIVER1_EL1 3 0 12 13 3 +Fields ICC_PPI_ACTIVERx_EL1 +EndSysreg + +SysregFields ICC_PPI_PENDRx_EL1 +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 ICC_PPI_CPENDR0_EL1 3 0 12 13 4 +Fields ICC_PPI_PENDRx_EL1 +EndSysreg + +Sysreg ICC_PPI_CPENDR1_EL1 3 0 12 13 5 +Fields ICC_PPI_PENDRx_EL1 +EndSysreg + +Sysreg ICC_PPI_SPENDR0_EL1 3 0 12 13 6 +Fields ICC_PPI_PENDRx_EL1 +EndSysreg + +Sysreg ICC_PPI_SPENDR1_EL1 3 0 12 13 7 +Fields ICC_PPI_PENDRx_EL1 +EndSysreg + +SysregFields ICC_PPI_PRIORITYRx_EL1 +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 ICC_PPI_PRIORITYR0_EL1 3 0 12 14 0 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR1_EL1 3 0 12 14 1 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR2_EL1 3 0 12 14 2 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR3_EL1 3 0 12 14 3 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR4_EL1 3 0 12 14 4 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR5_EL1 3 0 12 14 5 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR6_EL1 3 0 12 14 6 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR7_EL1 3 0 12 14 7 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR8_EL1 3 0 12 15 0 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR9_EL1 3 0 12 15 1 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR10_EL1 3 0 12 15 2 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR11_EL1 3 0 12 15 3 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR12_EL1 3 0 12 15 4 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR13_EL1 3 0 12 15 5 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR14_EL1 3 0 12 15 6 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + +Sysreg ICC_PPI_PRIORITYR15_EL1 3 0 12 15 7 +Fields ICC_PPI_PRIORITYRx_EL1 +EndSysreg + Sysreg PMSELR_EL0 3 3 9 12 5 Res0 63:5 Field 4:0 SEL @@ -3103,6 +3667,19 @@ Res0 14:12 Field 11:0 AFFINITY EndSysreg +Sysreg ICC_CR0_EL1 3 1 12 0 1 +Res0 63:39 +Field 38 PID +Field 37:32 IPPT +Res0 31:1 +Field 0 EN +EndSysreg + +Sysreg ICC_PCR_EL1 3 1 12 0 2 +Res0 63:5 +Field 4:0 PRIORITY +EndSysreg + Sysreg CSSELR_EL1 3 2 0 0 0 Res0 63:5 Field 4 TnD @@ -3989,6 +4566,54 @@ Field 31:16 PhyPARTID29 Field 15:0 PhyPARTID28 EndSysreg +Sysreg ICH_HFGRTR_EL2 3 4 12 9 4 +Res0 63:21 +Field 20 ICC_PPI_ACTIVERn_EL1 +Field 19 ICC_PPI_PRIORITYRn_EL1 +Field 18 ICC_PPI_PENDRn_EL1 +Field 17 ICC_PPI_ENABLERn_EL1 +Field 16 ICC_PPI_HMRn_EL1 +Res0 15:8 +Field 7 ICC_IAFFIDR_EL1 +Field 6 ICC_ICSR_EL1 +Field 5 ICC_PCR_EL1 +Field 4 ICC_HPPIR_EL1 +Field 3 ICC_HAPR_EL1 +Field 2 ICC_CR0_EL1 +Field 1 ICC_IDRn_EL1 +Field 0 ICC_APR_EL1 +EndSysreg + +Sysreg ICH_HFGWTR_EL2 3 4 12 9 6 +Res0 63:21 +Field 20 ICC_PPI_ACTIVERn_EL1 +Field 19 ICC_PPI_PRIORITYRn_EL1 +Field 18 ICC_PPI_PENDRn_EL1 +Field 17 ICC_PPI_ENABLERn_EL1 +Res0 16:7 +Field 6 ICC_ICSR_EL1 +Field 5 ICC_PCR_EL1 +Res0 4:3 +Field 2 ICC_CR0_EL1 +Res0 1 +Field 0 ICC_APR_EL1 +EndSysreg + +Sysreg ICH_HFGITR_EL2 3 4 12 9 7 +Res0 63:11 +Field 10 GICRCDNMIA +Field 9 GICRCDIA +Field 8 GICCDDI +Field 7 GICCDEOI +Field 6 GICCDHM +Field 5 GICCDRCFG +Field 4 GICCDPEND +Field 3 GICCDAFF +Field 2 GICCDPRI +Field 1 GICCDDIS +Field 0 GICCDEN +EndSysreg + Sysreg ICH_HCR_EL2 3 4 12 11 0 Res0 63:32 Field 31:27 EOIcount @@ -4037,6 +4662,12 @@ Field 1 U Field 0 EOI EndSysreg +Sysreg ICH_VCTLR_EL2 3 4 12 11 4 +Res0 63:2 +Field 1 V3 +Field 0 En +EndSysreg + Sysreg CONTEXTIDR_EL2 3 4 13 0 1 Fields CONTEXTIDR_ELx EndSysreg @@ -4150,7 +4781,13 @@ Mapping TCR_EL1 EndSysreg Sysreg TCR2_EL1 3 0 2 0 3 -Res0 63:16 +Res0 63:22 +Field 21 FNGNA1 +Field 20 FNGNA0 +Res0 19 +Field 18 FNG1 +Field 17 FNG0 +Field 16 A2 Field 15 DisCH1 Field 14 DisCH0 Res0 13:12 @@ -4174,7 +4811,10 @@ Mapping TCR2_EL1 EndSysreg Sysreg TCR2_EL2 3 4 2 0 3 -Res0 63:16 +Res0 63:19 +Field 18 FNG1 +Field 17 FNG0 +Field 16 A2 Field 15 DisCH1 Field 14 DisCH0 Field 13 AMEC1 diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig index acc431c331b0..4331313a42ff 100644 --- a/arch/csky/Kconfig +++ b/arch/csky/Kconfig @@ -80,7 +80,6 @@ config CSKY select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZO select HAVE_KERNEL_LZMA diff --git a/arch/csky/kernel/ptrace.c b/arch/csky/kernel/ptrace.c index 0f7e7b653c72..6bb685a2646b 100644 --- a/arch/csky/kernel/ptrace.c +++ b/arch/csky/kernel/ptrace.c @@ -166,7 +166,7 @@ static int fpr_set(struct task_struct *target, static const struct user_regset csky_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct pt_regs) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -174,7 +174,7 @@ static const struct user_regset csky_regsets[] = { .set = gpr_set, }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_fp) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/hexagon/kernel/ptrace.c b/arch/hexagon/kernel/ptrace.c index 905b06790ab7..2093eee143e1 100644 --- a/arch/hexagon/kernel/ptrace.c +++ b/arch/hexagon/kernel/ptrace.c @@ -137,7 +137,7 @@ enum hexagon_regset { static const struct user_regset hexagon_regsets[] = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 4b19f93379a1..f0abc38c40ac 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -15,7 +15,6 @@ config LOONGARCH select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_CPU_FINALIZE_INIT - select ARCH_HAS_CRC32 select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_FAST_MULTIPLIER @@ -25,7 +24,6 @@ config LOONGARCH select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PREEMPT_LAZY - select ARCH_HAS_PTE_DEVMAP select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_MEMORY select ARCH_HAS_SET_DIRECT_MAP @@ -120,11 +118,11 @@ config LOONGARCH select HAVE_ARCH_KASAN select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB if PERF_EVENTS + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD @@ -144,7 +142,6 @@ config LOONGARCH select HAVE_EXIT_THREAD select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index b0703a4e02a2..a3a9759414f4 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -102,7 +102,13 @@ KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma) ifdef CONFIG_OBJTOOL ifdef CONFIG_CC_HAS_ANNOTATE_TABLEJUMP +# The annotate-tablejump option can not be passed to LLVM backend when LTO is enabled. +# Ensure it is aware of linker with LTO, '--loongarch-annotate-tablejump' also needs to +# be passed via '-mllvm' to ld.lld. KBUILD_CFLAGS += -mannotate-tablejump +ifdef CONFIG_LTO_CLANG +KBUILD_LDFLAGS += -mllvm --loongarch-annotate-tablejump +endif else KBUILD_CFLAGS += -fno-jump-tables # keep compatibility with older compilers endif diff --git a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts index a34734a6c3ce..018ed904352a 100644 --- a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts @@ -41,6 +41,15 @@ linux,cma { }; }; +&apbdma3 { + status = "okay"; +}; + +&mmc0 { + status = "okay"; + bus-width = <4>; +}; + &gmac0 { status = "okay"; diff --git a/arch/loongarch/boot/dts/loongson-2k0500.dtsi b/arch/loongarch/boot/dts/loongson-2k0500.dtsi index 760c60eebb89..588ebc3bded4 100644 --- a/arch/loongarch/boot/dts/loongson-2k0500.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k0500.dtsi @@ -104,7 +104,7 @@ dma-controller@1fe10c10 { status = "disabled"; }; - dma-controller@1fe10c20 { + apbdma2: dma-controller@1fe10c20 { compatible = "loongson,ls2k0500-apbdma", "loongson,ls2k1000-apbdma"; reg = <0 0x1fe10c20 0 0x8>; interrupt-parent = <&eiointc>; @@ -114,7 +114,7 @@ dma-controller@1fe10c20 { status = "disabled"; }; - dma-controller@1fe10c30 { + apbdma3: dma-controller@1fe10c30 { compatible = "loongson,ls2k0500-apbdma", "loongson,ls2k1000-apbdma"; reg = <0 0x1fe10c30 0 0x8>; interrupt-parent = <&eiointc>; @@ -437,6 +437,30 @@ i2c@1ff4a800 { status = "disabled"; }; + mmc0: mmc@1ff64000 { + compatible = "loongson,ls2k0500-mmc"; + reg = <0 0x1ff64000 0 0x2000>, + <0 0x1fe10100 0 0x4>; + interrupt-parent = <&eiointc>; + interrupts = <57>; + dmas = <&apbdma3 0>; + dma-names = "rx-tx"; + clocks = <&clk LOONGSON2_APB_CLK>; + status = "disabled"; + }; + + mmc@1ff66000 { + compatible = "loongson,ls2k0500-mmc"; + reg = <0 0x1ff66000 0 0x2000>, + <0 0x1fe10100 0 0x4>; + interrupt-parent = <&eiointc>; + interrupts = <58>; + dmas = <&apbdma2 0>; + dma-names = "rx-tx"; + clocks = <&clk LOONGSON2_APB_CLK>; + status = "disabled"; + }; + pmc: power-management@1ff6c000 { compatible = "loongson,ls2k0500-pmc", "syscon"; reg = <0x0 0x1ff6c000 0x0 0x58>; diff --git a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts index 78ea995abf1c..d9a452ada5d7 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts @@ -48,6 +48,19 @@ fan0: pwm-fan { }; }; +&apbdma1 { + status = "okay"; +}; + +&mmc { + status = "okay"; + + pinctrl-0 = <&sdio_pins_default>; + pinctrl-names = "default"; + bus-width = <4>; + cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; +}; + &gmac0 { status = "okay"; diff --git a/arch/loongarch/boot/dts/loongson-2k1000.dtsi b/arch/loongarch/boot/dts/loongson-2k1000.dtsi index 1da3beb00f0e..d8e01e2534dd 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k1000.dtsi @@ -187,14 +187,14 @@ gpio0: gpio@1fe00500 { <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, - <>, - <26 IRQ_TYPE_LEVEL_HIGH>, + <0 IRQ_TYPE_NONE>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, + <26 IRQ_TYPE_NONE>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, @@ -209,13 +209,13 @@ gpio0: gpio@1fe00500 { <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, - <>, + <0 IRQ_TYPE_NONE>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, - <>, - <>, + <0 IRQ_TYPE_NONE>, + <0 IRQ_TYPE_NONE>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>, @@ -256,7 +256,7 @@ dma-controller@1fe00c00 { status = "disabled"; }; - dma-controller@1fe00c10 { + apbdma1: dma-controller@1fe00c10 { compatible = "loongson,ls2k1000-apbdma"; reg = <0x0 0x1fe00c10 0x0 0x8>; interrupt-parent = <&liointc1>; @@ -405,6 +405,18 @@ i2s: i2s@1fe2d000 { status = "disabled"; }; + mmc: mmc@1fe2c000 { + compatible = "loongson,ls2k1000-mmc"; + reg = <0 0x1fe2c000 0 0x68>, + <0 0x1fe00438 0 0x8>; + interrupt-parent = <&liointc0>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + dmas = <&apbdma1 0>; + dma-names = "rx-tx"; + status = "disabled"; + }; + spi0: spi@1fff0220 { compatible = "loongson,ls2k1000-spi"; reg = <0x0 0x1fff0220 0x0 0x10>; diff --git a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts index ea9e6985d0e9..3c6b12220386 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts @@ -39,6 +39,16 @@ linux,cma { }; }; +&emmc { + status = "okay"; + + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sd; + no-sdio; +}; + &sata { status = "okay"; }; diff --git a/arch/loongarch/boot/dts/loongson-2k2000.dtsi b/arch/loongarch/boot/dts/loongson-2k2000.dtsi index 9e0411f2754c..00cc485b753b 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k2000.dtsi @@ -259,6 +259,24 @@ uart0: serial@1fe001e0 { status = "disabled"; }; + emmc: mmc@79990000 { + compatible = "loongson,ls2k2000-mmc"; + reg = <0x0 0x79990000 0x0 0x1000>; + interrupt-parent = <&pic>; + interrupts = <51 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_EMMC_CLK>; + status = "disabled"; + }; + + mmc@79991000 { + compatible = "loongson,ls2k2000-mmc"; + reg = <0x0 0x79991000 0x0 0x1000>; + interrupt-parent = <&pic>; + interrupts = <50 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_EMMC_CLK>; + status = "disabled"; + }; + pcie@1a000000 { compatible = "loongson,ls2k-pci"; reg = <0x0 0x1a000000 0x0 0x02000000>, diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 0d59af6007b7..34eaee0384c9 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -225,7 +225,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m CONFIG_NETFILTER_XT_MATCH_CPU=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m @@ -784,8 +783,23 @@ CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_REALTEK_LIB=y +CONFIG_SND_HDA_CODEC_ALC260=y +CONFIG_SND_HDA_CODEC_ALC262=y +CONFIG_SND_HDA_CODEC_ALC268=y +CONFIG_SND_HDA_CODEC_ALC269=y +CONFIG_SND_HDA_CODEC_ALC662=y +CONFIG_SND_HDA_CODEC_ALC680=y +CONFIG_SND_HDA_CODEC_ALC861=y +CONFIG_SND_HDA_CODEC_ALC861VD=y +CONFIG_SND_HDA_CODEC_ALC880=y +CONFIG_SND_HDA_CODEC_ALC882=y CONFIG_SND_HDA_CODEC_SIGMATEL=y CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_HDMI_GENERIC=y +CONFIG_SND_HDA_CODEC_HDMI_INTEL=y +CONFIG_SND_HDA_CODEC_HDMI_ATI=y +CONFIG_SND_HDA_CODEC_HDMI_NVIDIA=y CONFIG_SND_HDA_CODEC_CONEXANT=y CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=m diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild index 80ddb5edb845..b04d2cef935f 100644 --- a/arch/loongarch/include/asm/Kbuild +++ b/arch/loongarch/include/asm/Kbuild @@ -10,5 +10,4 @@ generic-y += user.h generic-y += ioctl.h generic-y += mmzone.h generic-y += statfs.h -generic-y += param.h generic-y += text-patching.h diff --git a/arch/loongarch/include/asm/addrspace.h b/arch/loongarch/include/asm/addrspace.h index fe198b473f84..e739dbc6329d 100644 --- a/arch/loongarch/include/asm/addrspace.h +++ b/arch/loongarch/include/asm/addrspace.h @@ -18,12 +18,12 @@ /* * This gives the physical RAM offset. */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifndef PHYS_OFFSET #define PHYS_OFFSET _UL(0) #endif extern unsigned long vm_map_base; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #ifndef IO_BASE #define IO_BASE CSR_DMW0_BASE @@ -66,7 +66,7 @@ extern unsigned long vm_map_base; #define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #endif -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define _ATYPE_ #define _ATYPE32_ #define _ATYPE64_ @@ -85,7 +85,7 @@ extern unsigned long vm_map_base; /* * 32/64-bit LoongArch address spaces */ -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define _ACAST32_ #define _ACAST64_ #else diff --git a/arch/loongarch/include/asm/alternative-asm.h b/arch/loongarch/include/asm/alternative-asm.h index ff3d10ac393f..7dc29bd9b2f0 100644 --- a/arch/loongarch/include/asm/alternative-asm.h +++ b/arch/loongarch/include/asm/alternative-asm.h @@ -2,7 +2,7 @@ #ifndef _ASM_ALTERNATIVE_ASM_H #define _ASM_ALTERNATIVE_ASM_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #include @@ -77,6 +77,6 @@ .previous .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_ALTERNATIVE_ASM_H */ diff --git a/arch/loongarch/include/asm/alternative.h b/arch/loongarch/include/asm/alternative.h index cee7b29785ab..b5bae21fb3c8 100644 --- a/arch/loongarch/include/asm/alternative.h +++ b/arch/loongarch/include/asm/alternative.h @@ -2,7 +2,7 @@ #ifndef _ASM_ALTERNATIVE_H #define _ASM_ALTERNATIVE_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -106,6 +106,6 @@ extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); #define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ (asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory")) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_ALTERNATIVE_H */ diff --git a/arch/loongarch/include/asm/asm-extable.h b/arch/loongarch/include/asm/asm-extable.h index df05005f2b80..d60bdf2e6377 100644 --- a/arch/loongarch/include/asm/asm-extable.h +++ b/arch/loongarch/include/asm/asm-extable.h @@ -7,7 +7,7 @@ #define EX_TYPE_UACCESS_ERR_ZERO 2 #define EX_TYPE_BPF 3 -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define __ASM_EXTABLE_RAW(insn, fixup, type, data) \ .pushsection __ex_table, "a"; \ @@ -22,7 +22,7 @@ __ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_FIXUP, 0) .endm -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ #include #include @@ -60,6 +60,6 @@ #define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err) \ _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_ASM_EXTABLE_H */ diff --git a/arch/loongarch/include/asm/asm.h b/arch/loongarch/include/asm/asm.h index f591b3245def..f018d26fc995 100644 --- a/arch/loongarch/include/asm/asm.h +++ b/arch/loongarch/include/asm/asm.h @@ -110,7 +110,7 @@ #define LONG_SRA srai.w #define LONG_SRAV sra.w -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define LONG .word #endif #define LONGSIZE 4 @@ -131,7 +131,7 @@ #define LONG_SRA srai.d #define LONG_SRAV sra.d -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define LONG .dword #endif #define LONGSIZE 8 @@ -158,7 +158,7 @@ #define PTR_SCALESHIFT 2 -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define PTR .word #endif #define PTRSIZE 4 @@ -181,7 +181,7 @@ #define PTR_SCALESHIFT 3 -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define PTR .dword #endif #define PTRSIZE 8 diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h index 98cf4d7b4b0a..dfb982fe8701 100644 --- a/arch/loongarch/include/asm/cpu.h +++ b/arch/loongarch/include/asm/cpu.h @@ -46,7 +46,7 @@ #define PRID_PRODUCT_MASK 0x0fff -#if !defined(__ASSEMBLY__) +#if !defined(__ASSEMBLER__) enum cpu_type_enum { CPU_UNKNOWN, @@ -55,7 +55,7 @@ enum cpu_type_enum { CPU_LAST }; -#endif /* !__ASSEMBLY */ +#endif /* !__ASSEMBLER__ */ /* * ISA Level encodings diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h index 6e0a99763a9a..f4caaf764f9e 100644 --- a/arch/loongarch/include/asm/ftrace.h +++ b/arch/loongarch/include/asm/ftrace.h @@ -14,7 +14,7 @@ #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifndef CONFIG_DYNAMIC_FTRACE @@ -84,7 +84,7 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr) #endif -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* CONFIG_FUNCTION_TRACER */ diff --git a/arch/loongarch/include/asm/gpr-num.h b/arch/loongarch/include/asm/gpr-num.h index 996038da806d..af95b941f48b 100644 --- a/arch/loongarch/include/asm/gpr-num.h +++ b/arch/loongarch/include/asm/gpr-num.h @@ -2,7 +2,7 @@ #ifndef __ASM_GPR_NUM_H #define __ASM_GPR_NUM_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ .equ .L__gpr_num_zero, 0 .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 @@ -25,7 +25,7 @@ .equ .L__gpr_num_$s\num, 23 + \num .endr -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ #define __DEFINE_ASM_GPR_NUMS \ " .equ .L__gpr_num_zero, 0\n" \ @@ -47,6 +47,6 @@ " .equ .L__gpr_num_$s\\num, 23 + \\num\n" \ " .endr\n" \ -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_GPR_NUM_H */ diff --git a/arch/loongarch/include/asm/hugetlb.h b/arch/loongarch/include/asm/hugetlb.h index 4dc4b3e04225..ab68b594f889 100644 --- a/arch/loongarch/include/asm/hugetlb.h +++ b/arch/loongarch/include/asm/hugetlb.h @@ -10,20 +10,6 @@ uint64_t pmd_to_entrylo(unsigned long pmd_val); -#define __HAVE_ARCH_PREPARE_HUGEPAGE_RANGE -static inline int prepare_hugepage_range(struct file *file, - unsigned long addr, - unsigned long len) -{ - unsigned long task_size = STACK_TOP; - - if (len > task_size) - return -ENOMEM; - if (task_size - len < addr) - return -EINVAL; - return 0; -} - #define __HAVE_ARCH_HUGE_PTE_CLEAR static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep, unsigned long sz) diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index 3089785ca97e..277d2140676b 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); int larch_insn_read(void *addr, u32 *insnp); int larch_insn_write(void *addr, u32 insn); int larch_insn_patch_text(void *addr, u32 insn); +int larch_insn_text_copy(void *dst, void *src, size_t len); u32 larch_insn_gen_nop(void); u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); @@ -510,6 +511,8 @@ u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj); u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm); u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); +u32 larch_insn_gen_beq(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); +u32 larch_insn_gen_bne(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); static inline bool signed_imm_check(long val, unsigned int bit) diff --git a/arch/loongarch/include/asm/irqflags.h b/arch/loongarch/include/asm/irqflags.h index 003172b8406b..620163628a7f 100644 --- a/arch/loongarch/include/asm/irqflags.h +++ b/arch/loongarch/include/asm/irqflags.h @@ -5,7 +5,7 @@ #ifndef _ASM_IRQFLAGS_H #define _ASM_IRQFLAGS_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -80,6 +80,6 @@ static inline int arch_irqs_disabled(void) return arch_irqs_disabled_flags(arch_local_save_flags()); } -#endif /* #ifndef __ASSEMBLY__ */ +#endif /* #ifndef __ASSEMBLER__ */ #endif /* _ASM_IRQFLAGS_H */ diff --git a/arch/loongarch/include/asm/jump_label.h b/arch/loongarch/include/asm/jump_label.h index 8a924bd69d19..4000c7603d8e 100644 --- a/arch/loongarch/include/asm/jump_label.h +++ b/arch/loongarch/include/asm/jump_label.h @@ -7,7 +7,7 @@ #ifndef __ASM_JUMP_LABEL_H #define __ASM_JUMP_LABEL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -50,5 +50,5 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke return true; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_JUMP_LABEL_H */ diff --git a/arch/loongarch/include/asm/kasan.h b/arch/loongarch/include/asm/kasan.h index 7f52bd31b9d4..62f139a9c87d 100644 --- a/arch/loongarch/include/asm/kasan.h +++ b/arch/loongarch/include/asm/kasan.h @@ -2,7 +2,7 @@ #ifndef __ASM_KASAN_H #define __ASM_KASAN_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index a3c4cc46c892..0cecbd038bb3 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -50,12 +50,6 @@ struct kvm_vm_stat { struct kvm_vm_stat_generic generic; u64 pages; u64 hugepages; - u64 ipi_read_exits; - u64 ipi_write_exits; - u64 eiointc_read_exits; - u64 eiointc_write_exits; - u64 pch_pic_read_exits; - u64 pch_pic_write_exits; }; struct kvm_vcpu_stat { @@ -65,6 +59,12 @@ struct kvm_vcpu_stat { u64 cpucfg_exits; u64 signal_exits; u64 hypercall_exits; + u64 ipi_read_exits; + u64 ipi_write_exits; + u64 eiointc_read_exits; + u64 eiointc_write_exits; + u64 pch_pic_read_exits; + u64 pch_pic_write_exits; }; #define KVM_MEM_HUGEPAGE_CAPABLE (1UL << 0) diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index d84dac88a584..09dfd7eb406e 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -9,15 +9,15 @@ #include #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include /* CPUCFG */ #define read_cpucfg(reg) __cpucfg(reg) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ /* LoongArch Registers */ #define REG_ZERO 0x0 @@ -53,7 +53,7 @@ #define REG_S7 0x1e #define REG_S8 0x1f -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* Bit fields for CPUCFG registers */ #define LOONGARCH_CPUCFG0 0x0 @@ -171,7 +171,7 @@ * SW emulation for KVM hypervirsor, see arch/loongarch/include/uapi/asm/kvm_para.h */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* CSR */ #define csr_read32(reg) __csrrd_w(reg) @@ -187,7 +187,7 @@ #define iocsr_write32(val, reg) __iocsrwr_w(val, reg) #define iocsr_write64(val, reg) __iocsrwr_d(val, reg) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ /* CSR register number */ @@ -451,6 +451,13 @@ #define LOONGARCH_CSR_KS6 0x36 #define LOONGARCH_CSR_KS7 0x37 #define LOONGARCH_CSR_KS8 0x38 +#define LOONGARCH_CSR_KS9 0x39 +#define LOONGARCH_CSR_KS10 0x3a +#define LOONGARCH_CSR_KS11 0x3b +#define LOONGARCH_CSR_KS12 0x3c +#define LOONGARCH_CSR_KS13 0x3d +#define LOONGARCH_CSR_KS14 0x3e +#define LOONGARCH_CSR_KS15 0x3f /* Exception allocated KS0, KS1 and KS2 statically */ #define EXCEPTION_KS0 LOONGARCH_CSR_KS0 @@ -1195,7 +1202,7 @@ #define LOONGARCH_IOCSR_EXTIOI_ROUTE_BASE 0x1c00 #define IOCSR_EXTIOI_VECTOR_NUM 256 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ static __always_inline u64 drdtime(void) { @@ -1357,7 +1364,7 @@ __BUILD_CSR_OP(tlbidx) #define clear_csr_estat(val) \ csr_xchg32(~(val), val, LOONGARCH_CSR_ESTAT) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* Generic EntryLo bit definitions */ #define ENTRYLO_V (_ULCAST_(1) << 0) diff --git a/arch/loongarch/include/asm/orc_types.h b/arch/loongarch/include/asm/orc_types.h index caf1f71a1057..d5fa98d1d177 100644 --- a/arch/loongarch/include/asm/orc_types.h +++ b/arch/loongarch/include/asm/orc_types.h @@ -34,7 +34,7 @@ #define ORC_TYPE_REGS 3 #define ORC_TYPE_REGS_PARTIAL 4 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * This struct is more or less a vastly simplified version of the DWARF Call * Frame Information standard. It contains only the necessary parts of DWARF @@ -53,6 +53,6 @@ struct orc_entry { unsigned int type:3; unsigned int signal:1; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ORC_TYPES_H */ diff --git a/arch/loongarch/include/asm/page.h b/arch/loongarch/include/asm/page.h index 7368f12b7cb1..a3aaf34fba16 100644 --- a/arch/loongarch/include/asm/page.h +++ b/arch/loongarch/include/asm/page.h @@ -15,7 +15,7 @@ #define HPAGE_MASK (~(HPAGE_SIZE - 1)) #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -110,6 +110,6 @@ extern int __virt_addr_valid(volatile void *kaddr); #include #include -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _ASM_PAGE_H */ diff --git a/arch/loongarch/include/asm/pgtable-bits.h b/arch/loongarch/include/asm/pgtable-bits.h index 45bfc65a0c9f..2fc3789220ac 100644 --- a/arch/loongarch/include/asm/pgtable-bits.h +++ b/arch/loongarch/include/asm/pgtable-bits.h @@ -22,7 +22,6 @@ #define _PAGE_PFN_SHIFT 12 #define _PAGE_SWP_EXCLUSIVE_SHIFT 23 #define _PAGE_PFN_END_SHIFT 48 -#define _PAGE_DEVMAP_SHIFT 59 #define _PAGE_PRESENT_INVALID_SHIFT 60 #define _PAGE_NO_READ_SHIFT 61 #define _PAGE_NO_EXEC_SHIFT 62 @@ -36,7 +35,6 @@ #define _PAGE_MODIFIED (_ULCAST_(1) << _PAGE_MODIFIED_SHIFT) #define _PAGE_PROTNONE (_ULCAST_(1) << _PAGE_PROTNONE_SHIFT) #define _PAGE_SPECIAL (_ULCAST_(1) << _PAGE_SPECIAL_SHIFT) -#define _PAGE_DEVMAP (_ULCAST_(1) << _PAGE_DEVMAP_SHIFT) /* We borrow bit 23 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE (_ULCAST_(1) << _PAGE_SWP_EXCLUSIVE_SHIFT) @@ -76,8 +74,8 @@ #define __READABLE (_PAGE_VALID) #define __WRITEABLE (_PAGE_DIRTY | _PAGE_WRITE) -#define _PAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PAGE_DEVMAP | _PFN_MASK | _CACHE_MASK | _PAGE_PLV) -#define _HPAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PAGE_DEVMAP | _PFN_MASK | _CACHE_MASK | _PAGE_PLV | _PAGE_HUGE) +#define _PAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV) +#define _HPAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV | _PAGE_HUGE) #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_NO_READ | \ _PAGE_USER | _CACHE_CC) @@ -92,7 +90,7 @@ #define PAGE_KERNEL_WUC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ _PAGE_GLOBAL | _PAGE_KERN | _CACHE_WUC) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define _PAGE_IOREMAP pgprot_val(PAGE_KERNEL_SUC) @@ -127,6 +125,6 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) return __pgprot(prot); } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _ASM_PGTABLE_BITS_H */ diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index b30185302c07..bd128696e96d 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -55,7 +55,7 @@ #define USER_PTRS_PER_PGD ((TASK_SIZE64 / PGDIR_SIZE)?(TASK_SIZE64 / PGDIR_SIZE):1) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -409,9 +409,6 @@ static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; } #endif /* CONFIG_ARCH_HAS_PTE_SPECIAL */ -static inline int pte_devmap(pte_t pte) { return !!(pte_val(pte) & _PAGE_DEVMAP); } -static inline pte_t pte_mkdevmap(pte_t pte) { pte_val(pte) |= _PAGE_DEVMAP; return pte; } - #define pte_accessible pte_accessible static inline unsigned long pte_accessible(struct mm_struct *mm, pte_t a) { @@ -540,17 +537,6 @@ static inline pmd_t pmd_mkyoung(pmd_t pmd) return pmd; } -static inline int pmd_devmap(pmd_t pmd) -{ - return !!(pmd_val(pmd) & _PAGE_DEVMAP); -} - -static inline pmd_t pmd_mkdevmap(pmd_t pmd) -{ - pmd_val(pmd) |= _PAGE_DEVMAP; - return pmd; -} - static inline struct page *pmd_page(pmd_t pmd) { if (pmd_trans_huge(pmd)) @@ -606,11 +592,6 @@ static inline long pmd_protnone(pmd_t pmd) #define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0) #define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0) -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -#define pud_devmap(pud) (0) -#define pgd_devmap(pgd) (0) -#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ - /* * We provide our own get_unmapped area to cope with the virtual aliasing * constraints placed on us by the cache architecture. @@ -618,6 +599,6 @@ static inline long pmd_protnone(pmd_t pmd) #define HAVE_ARCH_UNMAPPED_AREA #define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _ASM_PGTABLE_H */ diff --git a/arch/loongarch/include/asm/prefetch.h b/arch/loongarch/include/asm/prefetch.h index 1672262a5e2e..0b168cdaae9a 100644 --- a/arch/loongarch/include/asm/prefetch.h +++ b/arch/loongarch/include/asm/prefetch.h @@ -8,7 +8,7 @@ #define Pref_Load 0 #define Pref_Store 8 -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ .macro __pref hint addr #ifdef CONFIG_CPU_HAS_PREFETCH diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index ad0bd234a0f1..3a47f52959a8 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -39,7 +39,7 @@ int loongson_cpu_disable(void); void loongson_cpu_die(unsigned int cpu); #endif -static inline void plat_smp_setup(void) +static inline void __init plat_smp_setup(void) { loongson_smp_setup(); } diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 3eda298702b1..5cb568a60cf8 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -58,7 +58,7 @@ .endm .macro STACKLEAK_ERASE -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE bl stackleak_erase_on_task_stack #endif .endm diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 4f5a9441754e..9dfa2ef00816 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -10,7 +10,7 @@ #ifdef __KERNEL__ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -53,7 +53,7 @@ static inline struct thread_info *current_thread_info(void) register unsigned long current_stack_pointer __asm__("$sp"); -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ /* thread information allocation */ #define THREAD_SIZE SZ_16K diff --git a/arch/loongarch/include/asm/types.h b/arch/loongarch/include/asm/types.h index baf15a0dcf8b..0edd731f3d6a 100644 --- a/arch/loongarch/include/asm/types.h +++ b/arch/loongarch/include/asm/types.h @@ -8,7 +8,7 @@ #include #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define _ULCAST_ #define _U64CAST_ #else diff --git a/arch/loongarch/include/asm/unwind_hints.h b/arch/loongarch/include/asm/unwind_hints.h index 2c68bc72736c..16c7f7e465a0 100644 --- a/arch/loongarch/include/asm/unwind_hints.h +++ b/arch/loongarch/include/asm/unwind_hints.h @@ -5,7 +5,7 @@ #include #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ .macro UNWIND_HINT_UNDEFINED UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED @@ -23,7 +23,7 @@ UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL .endm -#else /* !__ASSEMBLY__ */ +#else /* !__ASSEMBLER__ */ #define UNWIND_HINT_SAVE \ UNWIND_HINT(UNWIND_HINT_TYPE_SAVE, 0, 0, 0) @@ -31,6 +31,6 @@ #define UNWIND_HINT_RESTORE \ UNWIND_HINT(UNWIND_HINT_TYPE_RESTORE, 0, 0, 0) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */ diff --git a/arch/loongarch/include/asm/vdso/arch_data.h b/arch/loongarch/include/asm/vdso/arch_data.h index 322d0a5f1c84..395ec223bcbe 100644 --- a/arch/loongarch/include/asm/vdso/arch_data.h +++ b/arch/loongarch/include/asm/vdso/arch_data.h @@ -7,7 +7,7 @@ #ifndef _VDSO_ARCH_DATA_H #define _VDSO_ARCH_DATA_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -20,6 +20,6 @@ struct vdso_arch_data { struct vdso_pcpu_data pdata[NR_CPUS]; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/loongarch/include/asm/vdso/getrandom.h b/arch/loongarch/include/asm/vdso/getrandom.h index a81724b69f29..2ff05003c6e7 100644 --- a/arch/loongarch/include/asm/vdso/getrandom.h +++ b/arch/loongarch/include/asm/vdso/getrandom.h @@ -5,7 +5,7 @@ #ifndef __ASM_VDSO_GETRANDOM_H #define __ASM_VDSO_GETRANDOM_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -28,6 +28,6 @@ static __always_inline ssize_t getrandom_syscall(void *_buffer, size_t _len, uns return ret; } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_VDSO_GETRANDOM_H */ diff --git a/arch/loongarch/include/asm/vdso/gettimeofday.h b/arch/loongarch/include/asm/vdso/gettimeofday.h index f15503e3336c..dcafabca9bb6 100644 --- a/arch/loongarch/include/asm/vdso/gettimeofday.h +++ b/arch/loongarch/include/asm/vdso/gettimeofday.h @@ -7,7 +7,7 @@ #ifndef __ASM_VDSO_GETTIMEOFDAY_H #define __ASM_VDSO_GETTIMEOFDAY_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -89,6 +89,6 @@ static inline bool loongarch_vdso_hres_capable(void) } #define __arch_vdso_hres_capable loongarch_vdso_hres_capable -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_VDSO_GETTIMEOFDAY_H */ diff --git a/arch/loongarch/include/asm/vdso/processor.h b/arch/loongarch/include/asm/vdso/processor.h index ef5770b343a0..1e255373b0b8 100644 --- a/arch/loongarch/include/asm/vdso/processor.h +++ b/arch/loongarch/include/asm/vdso/processor.h @@ -5,10 +5,10 @@ #ifndef __ASM_VDSO_PROCESSOR_H #define __ASM_VDSO_PROCESSOR_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define cpu_relax() barrier() -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_VDSO_PROCESSOR_H */ diff --git a/arch/loongarch/include/asm/vdso/vdso.h b/arch/loongarch/include/asm/vdso/vdso.h index 50c65fb29daf..04bd2d452876 100644 --- a/arch/loongarch/include/asm/vdso/vdso.h +++ b/arch/loongarch/include/asm/vdso/vdso.h @@ -7,7 +7,7 @@ #ifndef _ASM_VDSO_VDSO_H #define _ASM_VDSO_VDSO_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -16,6 +16,6 @@ #define VVAR_SIZE (VDSO_NR_PAGES << PAGE_SHIFT) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/loongarch/include/asm/vdso/vsyscall.h b/arch/loongarch/include/asm/vdso/vsyscall.h index 1140b54b4bc8..558eb9dfda52 100644 --- a/arch/loongarch/include/asm/vdso/vsyscall.h +++ b/arch/loongarch/include/asm/vdso/vsyscall.h @@ -2,13 +2,13 @@ #ifndef __ASM_VDSO_VSYSCALL_H #define __ASM_VDSO_VSYSCALL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include /* The asm-generic header needs to be included after the definitions above */ #include -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_VDSO_VSYSCALL_H */ diff --git a/arch/loongarch/include/uapi/asm/setup.h b/arch/loongarch/include/uapi/asm/setup.h new file mode 100644 index 000000000000..d46363ce3e02 --- /dev/null +++ b/arch/loongarch/include/uapi/asm/setup.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef _UAPI_ASM_LOONGARCH_SETUP_H +#define _UAPI_ASM_LOONGARCH_SETUP_H + +#define COMMAND_LINE_SIZE 4096 + +#endif /* _UAPI_ASM_LOONGARCH_SETUP_H */ diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index a54cd6fd3796..1367ca759468 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/loongarch/kernel/alternative.c b/arch/loongarch/kernel/alternative.c index 4ad13847e962..0e0c766df1e3 100644 --- a/arch/loongarch/kernel/alternative.c +++ b/arch/loongarch/kernel/alternative.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index de21e72759ee..860a3bc030e0 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -144,6 +144,18 @@ void __init efi_init(void) if (efi_memmap_init_early(&data) < 0) panic("Unable to map EFI memory map.\n"); + /* + * Reserve the physical memory region occupied by the EFI + * memory map table (header + descriptors). This is crucial + * for kdump, as the kdump kernel relies on this original + * memmap passed by the bootloader. Without reservation, + * this region could be overwritten by the primary kernel. + * Also, set the EFI_PRESERVE_BS_REGIONS flag to indicate that + * critical boot services code/data regions like this are preserved. + */ + memblock_reserve((phys_addr_t)boot_memmap, sizeof(*tbl) + data.size); + set_bit(EFI_PRESERVE_BS_REGIONS, &efi.flags); + early_memunmap(tbl, sizeof(*tbl)); } diff --git a/arch/loongarch/kernel/elf.c b/arch/loongarch/kernel/elf.c index 0fa81ced28dc..3d98c6aa00db 100644 --- a/arch/loongarch/kernel/elf.c +++ b/arch/loongarch/kernel/elf.c @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c index 27144de5c5fe..c0a5dc9aeae2 100644 --- a/arch/loongarch/kernel/env.c +++ b/arch/loongarch/kernel/env.c @@ -39,16 +39,19 @@ void __init init_environ(void) static int __init init_cpu_fullname(void) { - struct device_node *root; int cpu, ret; - char *model; + char *cpuname; + const char *model; + struct device_node *root; /* Parsing cpuname from DTS model property */ root = of_find_node_by_path("/"); - ret = of_property_read_string(root, "model", (const char **)&model); + ret = of_property_read_string(root, "model", &model); + if (ret == 0) { + cpuname = kstrdup(model, GFP_KERNEL); + loongson_sysconf.cpuname = strsep(&cpuname, " "); + } of_node_put(root); - if (ret == 0) - loongson_sysconf.cpuname = strsep(&model, " "); if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) { for (cpu = 0; cpu < NR_CPUS; cpu++) diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c index 14d7d700bcb9..72ecfed29d55 100644 --- a/arch/loongarch/kernel/inst.c +++ b/arch/loongarch/kernel/inst.c @@ -4,6 +4,8 @@ */ #include #include +#include +#include #include #include @@ -218,6 +220,50 @@ int larch_insn_patch_text(void *addr, u32 insn) return ret; } +struct insn_copy { + void *dst; + void *src; + size_t len; + unsigned int cpu; +}; + +static int text_copy_cb(void *data) +{ + int ret = 0; + struct insn_copy *copy = data; + + if (smp_processor_id() == copy->cpu) { + ret = copy_to_kernel_nofault(copy->dst, copy->src, copy->len); + if (ret) + pr_err("%s: operation failed\n", __func__); + } + + flush_icache_range((unsigned long)copy->dst, (unsigned long)copy->dst + copy->len); + + return ret; +} + +int larch_insn_text_copy(void *dst, void *src, size_t len) +{ + int ret = 0; + size_t start, end; + struct insn_copy copy = { + .dst = dst, + .src = src, + .len = len, + .cpu = smp_processor_id(), + }; + + start = round_down((size_t)dst, PAGE_SIZE); + end = round_up((size_t)dst + len, PAGE_SIZE); + + set_memory_rw(start, (end - start) / PAGE_SIZE); + ret = stop_machine(text_copy_cb, ©, cpu_online_mask); + set_memory_rox(start, (end - start) / PAGE_SIZE); + + return ret; +} + u32 larch_insn_gen_nop(void) { return INSN_NOP; @@ -323,6 +369,34 @@ u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) return insn.word; } +u32 larch_insn_gen_beq(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) +{ + union loongarch_instruction insn; + + if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { + pr_warn("The generated beq instruction is out of range.\n"); + return INSN_BREAK; + } + + emit_beq(&insn, rj, rd, imm >> 2); + + return insn.word; +} + +u32 larch_insn_gen_bne(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) +{ + union loongarch_instruction insn; + + if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { + pr_warn("The generated bne instruction is out of range.\n"); + return INSN_BREAK; + } + + emit_bne(&insn, rj, rd, imm >> 2); + + return insn.word; +} + u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) { union loongarch_instruction insn; diff --git a/arch/loongarch/kernel/kfpu.c b/arch/loongarch/kernel/kfpu.c index 4c476904227f..141b49bd989c 100644 --- a/arch/loongarch/kernel/kfpu.c +++ b/arch/loongarch/kernel/kfpu.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c index e2f30ff9afde..a43ba7f9f987 100644 --- a/arch/loongarch/kernel/module-sections.c +++ b/arch/loongarch/kernel/module-sections.c @@ -8,6 +8,7 @@ #include #include #include +#include Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val) { @@ -61,39 +62,38 @@ Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr v return (Elf_Addr)&plt[nr]; } -static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y) +#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b)) + +static int compare_rela(const void *x, const void *y) { - return x->r_info == y->r_info && x->r_addend == y->r_addend; -} + int ret; + const Elf_Rela *rela_x = x, *rela_y = y; -static bool duplicate_rela(const Elf_Rela *rela, int idx) -{ - int i; + ret = cmp_3way(rela_x->r_info, rela_y->r_info); + if (ret == 0) + ret = cmp_3way(rela_x->r_addend, rela_y->r_addend); - for (i = 0; i < idx; i++) { - if (is_rela_equal(&rela[i], &rela[idx])) - return true; - } - - return false; + return ret; } static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts, unsigned int *gots) { - unsigned int i, type; + unsigned int i; + + sort(relas, num, sizeof(Elf_Rela), compare_rela, NULL); for (i = 0; i < num; i++) { - type = ELF_R_TYPE(relas[i].r_info); - switch (type) { + if (i && !compare_rela(&relas[i-1], &relas[i])) + continue; + + switch (ELF_R_TYPE(relas[i].r_info)) { case R_LARCH_SOP_PUSH_PLT_PCREL: case R_LARCH_B26: - if (!duplicate_rela(relas, i)) - (*plts)++; + (*plts)++; break; case R_LARCH_GOT_PC_HI20: - if (!duplicate_rela(relas, i)) - (*gots)++; + (*gots)++; break; default: break; /* Do nothing. */ diff --git a/arch/loongarch/kernel/paravirt.c b/arch/loongarch/kernel/paravirt.c index e5a39bbad078..b1b51f920b23 100644 --- a/arch/loongarch/kernel/paravirt.c +++ b/arch/loongarch/kernel/paravirt.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include #include #include diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index 5e2402cfcab0..8edd0954e55a 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -864,7 +864,7 @@ enum loongarch_regset { static const struct user_regset loongarch64_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), @@ -872,7 +872,7 @@ static const struct user_regset loongarch64_regsets[] = { .set = gpr_set, }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), @@ -880,7 +880,7 @@ static const struct user_regset loongarch64_regsets[] = { .set = fpr_set, }, [REGSET_CPUCFG] = { - .core_note_type = NT_LOONGARCH_CPUCFG, + USER_REGSET_NOTE_TYPE(LOONGARCH_CPUCFG), .n = 64, .size = sizeof(u32), .align = sizeof(u32), @@ -889,7 +889,7 @@ static const struct user_regset loongarch64_regsets[] = { }, #ifdef CONFIG_CPU_HAS_LSX [REGSET_LSX] = { - .core_note_type = NT_LOONGARCH_LSX, + USER_REGSET_NOTE_TYPE(LOONGARCH_LSX), .n = NUM_FPU_REGS, .size = 16, .align = 16, @@ -899,7 +899,7 @@ static const struct user_regset loongarch64_regsets[] = { #endif #ifdef CONFIG_CPU_HAS_LASX [REGSET_LASX] = { - .core_note_type = NT_LOONGARCH_LASX, + USER_REGSET_NOTE_TYPE(LOONGARCH_LASX), .n = NUM_FPU_REGS, .size = 32, .align = 32, @@ -909,7 +909,7 @@ static const struct user_regset loongarch64_regsets[] = { #endif #ifdef CONFIG_CPU_HAS_LBT [REGSET_LBT] = { - .core_note_type = NT_LOONGARCH_LBT, + USER_REGSET_NOTE_TYPE(LOONGARCH_LBT), .n = 5, .size = sizeof(u64), .align = sizeof(u64), @@ -919,7 +919,7 @@ static const struct user_regset loongarch64_regsets[] = { #endif #ifdef CONFIG_HAVE_HW_BREAKPOINT [REGSET_HW_BREAK] = { - .core_note_type = NT_LOONGARCH_HW_BREAK, + USER_REGSET_NOTE_TYPE(LOONGARCH_HW_BREAK), .n = sizeof(struct user_watch_state_v2) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -927,7 +927,7 @@ static const struct user_regset loongarch64_regsets[] = { .set = hw_break_set, }, [REGSET_HW_WATCH] = { - .core_note_type = NT_LOONGARCH_HW_WATCH, + USER_REGSET_NOTE_TYPE(LOONGARCH_HW_WATCH), .n = sizeof(struct user_watch_state_v2) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/loongarch/kernel/relocate_kernel.S b/arch/loongarch/kernel/relocate_kernel.S index 84e6de2fd973..8b5140ac9ea1 100644 --- a/arch/loongarch/kernel/relocate_kernel.S +++ b/arch/loongarch/kernel/relocate_kernel.S @@ -109,4 +109,4 @@ SYM_CODE_END(kexec_smp_wait) relocate_new_kernel_end: .section ".data" -SYM_DATA(relocate_new_kernel_size, .long relocate_new_kernel_end - relocate_new_kernel) +SYM_DATA(relocate_new_kernel_size, .quad relocate_new_kernel_end - relocate_new_kernel) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index b99fbb388fe0..075b79b2c1d3 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -191,6 +191,16 @@ static int __init early_parse_mem(char *p) return -EINVAL; } + start = 0; + size = memparse(p, &p); + if (*p == '@') /* Every mem=... should contain '@' */ + start = memparse(p + 1, &p); + else { /* Only one mem=... is allowed if no '@' */ + usermem = 1; + memblock_enforce_memory_limit(size); + return 0; + } + /* * If a user specifies memory size, we * blow away any automatically generated @@ -201,14 +211,6 @@ static int __init early_parse_mem(char *p) memblock_remove(memblock_start_of_DRAM(), memblock_end_of_DRAM() - memblock_start_of_DRAM()); } - start = 0; - size = memparse(p, &p); - if (*p == '@') - start = memparse(p + 1, &p); - else { - pr_err("Invalid format!\n"); - return -EINVAL; - } if (!IS_ENABLED(CONFIG_NUMA)) memblock_add(start, size); @@ -265,7 +267,7 @@ static void __init arch_reserve_crashkernel(void) return; ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), - &crash_size, &crash_base, &low_size, &high); + &crash_size, &crash_base, &low_size, NULL, &high); if (ret) return; diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c index 4740cb5b2388..c9f7ca778364 100644 --- a/arch/loongarch/kernel/signal.c +++ b/arch/loongarch/kernel/signal.c @@ -677,6 +677,11 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, for (i = 1; i < 32; i++) err |= __put_user(regs->regs[i], &sc->sc_regs[i]); +#ifdef CONFIG_CPU_HAS_LBT + if (extctx->lbt.addr) + err |= protected_save_lbt_context(extctx); +#endif + if (extctx->lasx.addr) err |= protected_save_lasx_context(extctx); else if (extctx->lsx.addr) @@ -684,11 +689,6 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, else if (extctx->fpu.addr) err |= protected_save_fpu_context(extctx); -#ifdef CONFIG_CPU_HAS_LBT - if (extctx->lbt.addr) - err |= protected_save_lbt_context(extctx); -#endif - /* Set the "end" magic */ info = (struct sctx_info *)extctx->end.addr; err |= __put_user(0, &info->magic); diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index bc75a3a69fc8..f3092f2de8b5 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -5,6 +5,7 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include +#include #include #include #include @@ -102,7 +103,24 @@ static int constant_timer_next_event(unsigned long delta, struct clock_event_dev return 0; } -static unsigned long __init get_loops_per_jiffy(void) +static int arch_timer_starting(unsigned int cpu) +{ + set_csr_ecfg(ECFGF_TIMER); + + return 0; +} + +static int arch_timer_dying(unsigned int cpu) +{ + constant_set_state_shutdown(this_cpu_ptr(&constant_clockevent_device)); + + /* Clear Timer Interrupt */ + write_csr_tintclear(CSR_TINTCLR_TI); + + return 0; +} + +static unsigned long get_loops_per_jiffy(void) { unsigned long lpj = (unsigned long)const_clock_freq; @@ -172,6 +190,10 @@ int constant_clockevent_init(void) lpj_fine = get_loops_per_jiffy(); pr_info("Constant clock event device register\n"); + cpuhp_setup_state(CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING, + "clockevents/loongarch/timer:starting", + arch_timer_starting, arch_timer_dying); + return 0; } diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 47fc2de6d150..3d9be6ca7ec5 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/loongarch/kernel/unwind_guess.c b/arch/loongarch/kernel/unwind_guess.c index 98379b7d4147..08d7951b2f60 100644 --- a/arch/loongarch/kernel/unwind_guess.c +++ b/arch/loongarch/kernel/unwind_guess.c @@ -3,6 +3,7 @@ * Copyright (C) 2022 Loongson Technology Corporation Limited */ #include +#include unsigned long unwind_get_return_address(struct unwind_state *state) { diff --git a/arch/loongarch/kernel/unwind_orc.c b/arch/loongarch/kernel/unwind_orc.c index d623935a7547..0d5fa64a2225 100644 --- a/arch/loongarch/kernel/unwind_orc.c +++ b/arch/loongarch/kernel/unwind_orc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include +#include #include +#include #include #include #include @@ -507,7 +508,7 @@ bool unwind_next_frame(struct unwind_state *state) state->pc = bt_address(pc); if (!state->pc) { - pr_err("cannot find unwind pc at %pK\n", (void *)pc); + pr_err("cannot find unwind pc at %p\n", (void *)pc); goto err; } diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c index 929ae240280a..729e775bd40d 100644 --- a/arch/loongarch/kernel/unwind_prologue.c +++ b/arch/loongarch/kernel/unwind_prologue.c @@ -3,6 +3,7 @@ * Copyright (C) 2022 Loongson Technology Corporation Limited */ #include +#include #include #include diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index fa52251b3bf1..2ce41f93b2a4 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -289,9 +289,11 @@ static int kvm_trap_handle_gspr(struct kvm_vcpu *vcpu) er = EMULATE_FAIL; switch (((inst.word >> 24) & 0xff)) { case 0x0: /* CPUCFG GSPR */ + trace_kvm_exit_cpucfg(vcpu, KVM_TRACE_EXIT_CPUCFG); er = kvm_emu_cpucfg(vcpu, inst); break; case 0x4: /* CSR{RD,WR,XCHG} GSPR */ + trace_kvm_exit_csr(vcpu, KVM_TRACE_EXIT_CSR); er = kvm_handle_csr(vcpu, inst); break; case 0x6: /* Cache, Idle and IOCSR GSPR */ @@ -821,32 +823,25 @@ static int kvm_handle_lbt_disabled(struct kvm_vcpu *vcpu, int ecode) return RESUME_GUEST; } -static int kvm_send_pv_ipi(struct kvm_vcpu *vcpu) +static void kvm_send_pv_ipi(struct kvm_vcpu *vcpu) { - unsigned int min, cpu, i; - unsigned long ipi_bitmap; + unsigned int min, cpu; struct kvm_vcpu *dest; + DECLARE_BITMAP(ipi_bitmap, BITS_PER_LONG * 2) = { + kvm_read_reg(vcpu, LOONGARCH_GPR_A1), + kvm_read_reg(vcpu, LOONGARCH_GPR_A2) + }; min = kvm_read_reg(vcpu, LOONGARCH_GPR_A3); - for (i = 0; i < 2; i++, min += BITS_PER_LONG) { - ipi_bitmap = kvm_read_reg(vcpu, LOONGARCH_GPR_A1 + i); - if (!ipi_bitmap) + for_each_set_bit(cpu, ipi_bitmap, BITS_PER_LONG * 2) { + dest = kvm_get_vcpu_by_cpuid(vcpu->kvm, cpu + min); + if (!dest) continue; - cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG); - while (cpu < BITS_PER_LONG) { - dest = kvm_get_vcpu_by_cpuid(vcpu->kvm, cpu + min); - cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG, cpu + 1); - if (!dest) - continue; - - /* Send SWI0 to dest vcpu to emulate IPI interrupt */ - kvm_queue_irq(dest, INT_SWI0); - kvm_vcpu_kick(dest); - } + /* Send SWI0 to dest vcpu to emulate IPI interrupt */ + kvm_queue_irq(dest, INT_SWI0); + kvm_vcpu_kick(dest); } - - return 0; } /* diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index f39929d7bf8a..026b139dcff2 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -9,7 +9,8 @@ static void eiointc_set_sw_coreisr(struct loongarch_eiointc *s) { - int ipnum, cpu, irq_index, irq_mask, irq; + int ipnum, cpu, cpuid, irq; + struct kvm_vcpu *vcpu; for (irq = 0; irq < EIOINTC_IRQS; irq++) { ipnum = s->ipmap.reg_u8[irq / 32]; @@ -17,20 +18,23 @@ static void eiointc_set_sw_coreisr(struct loongarch_eiointc *s) ipnum = count_trailing_zeros(ipnum); ipnum = (ipnum >= 0 && ipnum < 4) ? ipnum : 0; } - irq_index = irq / 32; - irq_mask = BIT(irq & 0x1f); - cpu = s->coremap.reg_u8[irq]; - if (!!(s->coreisr.reg_u32[cpu][irq_index] & irq_mask)) - set_bit(irq, s->sw_coreisr[cpu][ipnum]); + cpuid = s->coremap.reg_u8[irq]; + vcpu = kvm_get_vcpu_by_cpuid(s->kvm, cpuid); + if (!vcpu) + continue; + + cpu = vcpu->vcpu_id; + if (test_bit(irq, (unsigned long *)s->coreisr.reg_u32[cpu])) + __set_bit(irq, s->sw_coreisr[cpu][ipnum]); else - clear_bit(irq, s->sw_coreisr[cpu][ipnum]); + __clear_bit(irq, s->sw_coreisr[cpu][ipnum]); } } static void eiointc_update_irq(struct loongarch_eiointc *s, int irq, int level) { - int ipnum, cpu, found, irq_index, irq_mask; + int ipnum, cpu, found; struct kvm_vcpu *vcpu; struct kvm_interrupt vcpu_irq; @@ -41,20 +45,22 @@ static void eiointc_update_irq(struct loongarch_eiointc *s, int irq, int level) } cpu = s->sw_coremap[irq]; - vcpu = kvm_get_vcpu(s->kvm, cpu); - irq_index = irq / 32; - irq_mask = BIT(irq & 0x1f); + vcpu = kvm_get_vcpu_by_id(s->kvm, cpu); + if (unlikely(vcpu == NULL)) { + kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); + return; + } if (level) { /* if not enable return false */ - if (((s->enable.reg_u32[irq_index]) & irq_mask) == 0) + if (!test_bit(irq, (unsigned long *)s->enable.reg_u32)) return; - s->coreisr.reg_u32[cpu][irq_index] |= irq_mask; + __set_bit(irq, (unsigned long *)s->coreisr.reg_u32[cpu]); found = find_first_bit(s->sw_coreisr[cpu][ipnum], EIOINTC_IRQS); - set_bit(irq, s->sw_coreisr[cpu][ipnum]); + __set_bit(irq, s->sw_coreisr[cpu][ipnum]); } else { - s->coreisr.reg_u32[cpu][irq_index] &= ~irq_mask; - clear_bit(irq, s->sw_coreisr[cpu][ipnum]); + __clear_bit(irq, (unsigned long *)s->coreisr.reg_u32[cpu]); + __clear_bit(irq, s->sw_coreisr[cpu][ipnum]); found = find_first_bit(s->sw_coreisr[cpu][ipnum], EIOINTC_IRQS); } @@ -66,20 +72,25 @@ static void eiointc_update_irq(struct loongarch_eiointc *s, int irq, int level) } static inline void eiointc_update_sw_coremap(struct loongarch_eiointc *s, - int irq, void *pvalue, u32 len, bool notify) + int irq, u64 val, u32 len, bool notify) { - int i, cpu; - u64 val = *(u64 *)pvalue; + int i, cpu, cpuid; + struct kvm_vcpu *vcpu; for (i = 0; i < len; i++) { - cpu = val & 0xff; + cpuid = val & 0xff; val = val >> 8; if (!(s->status & BIT(EIOINTC_ENABLE_CPU_ENCODE))) { - cpu = ffs(cpu) - 1; - cpu = (cpu >= 4) ? 0 : cpu; + cpuid = ffs(cpuid) - 1; + cpuid = (cpuid >= 4) ? 0 : cpuid; } + vcpu = kvm_get_vcpu_by_cpuid(s->kvm, cpuid); + if (!vcpu) + continue; + + cpu = vcpu->vcpu_id; if (s->sw_coremap[irq + i] == cpu) continue; @@ -99,159 +110,14 @@ void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level) unsigned long flags; unsigned long *isr = (unsigned long *)s->isr.reg_u8; - level ? set_bit(irq, isr) : clear_bit(irq, isr); spin_lock_irqsave(&s->lock, flags); + level ? __set_bit(irq, isr) : __clear_bit(irq, isr); eiointc_update_irq(s, irq, level); spin_unlock_irqrestore(&s->lock, flags); } -static inline void eiointc_enable_irq(struct kvm_vcpu *vcpu, - struct loongarch_eiointc *s, int index, u8 mask, int level) -{ - u8 val; - int irq; - - val = mask & s->isr.reg_u8[index]; - irq = ffs(val); - while (irq != 0) { - /* - * enable bit change from 0 to 1, - * need to update irq by pending bits - */ - eiointc_update_irq(s, irq - 1 + index * 8, level); - val &= ~BIT(irq - 1); - irq = ffs(val); - } -} - -static int loongarch_eiointc_readb(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, - gpa_t addr, int len, void *val) -{ - int index, ret = 0; - u8 data = 0; - gpa_t offset; - - offset = addr - EIOINTC_BASE; - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = offset - EIOINTC_NODETYPE_START; - data = s->nodetype.reg_u8[index]; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - index = offset - EIOINTC_IPMAP_START; - data = s->ipmap.reg_u8[index]; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = offset - EIOINTC_ENABLE_START; - data = s->enable.reg_u8[index]; - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - index = offset - EIOINTC_BOUNCE_START; - data = s->bounce.reg_u8[index]; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = offset - EIOINTC_COREISR_START; - data = s->coreisr.reg_u8[vcpu->vcpu_id][index]; - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - index = offset - EIOINTC_COREMAP_START; - data = s->coremap.reg_u8[index]; - break; - default: - ret = -EINVAL; - break; - } - *(u8 *)val = data; - - return ret; -} - -static int loongarch_eiointc_readw(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, - gpa_t addr, int len, void *val) -{ - int index, ret = 0; - u16 data = 0; - gpa_t offset; - - offset = addr - EIOINTC_BASE; - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = (offset - EIOINTC_NODETYPE_START) >> 1; - data = s->nodetype.reg_u16[index]; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - index = (offset - EIOINTC_IPMAP_START) >> 1; - data = s->ipmap.reg_u16[index]; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = (offset - EIOINTC_ENABLE_START) >> 1; - data = s->enable.reg_u16[index]; - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - index = (offset - EIOINTC_BOUNCE_START) >> 1; - data = s->bounce.reg_u16[index]; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = (offset - EIOINTC_COREISR_START) >> 1; - data = s->coreisr.reg_u16[vcpu->vcpu_id][index]; - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - index = (offset - EIOINTC_COREMAP_START) >> 1; - data = s->coremap.reg_u16[index]; - break; - default: - ret = -EINVAL; - break; - } - *(u16 *)val = data; - - return ret; -} - -static int loongarch_eiointc_readl(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, - gpa_t addr, int len, void *val) -{ - int index, ret = 0; - u32 data = 0; - gpa_t offset; - - offset = addr - EIOINTC_BASE; - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = (offset - EIOINTC_NODETYPE_START) >> 2; - data = s->nodetype.reg_u32[index]; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - index = (offset - EIOINTC_IPMAP_START) >> 2; - data = s->ipmap.reg_u32[index]; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = (offset - EIOINTC_ENABLE_START) >> 2; - data = s->enable.reg_u32[index]; - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - index = (offset - EIOINTC_BOUNCE_START) >> 2; - data = s->bounce.reg_u32[index]; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = (offset - EIOINTC_COREISR_START) >> 2; - data = s->coreisr.reg_u32[vcpu->vcpu_id][index]; - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - index = (offset - EIOINTC_COREMAP_START) >> 2; - data = s->coremap.reg_u32[index]; - break; - default: - ret = -EINVAL; - break; - } - *(u32 *)val = data; - - return ret; -} - -static int loongarch_eiointc_readq(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, - gpa_t addr, int len, void *val) +static int loongarch_eiointc_read(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, + gpa_t addr, unsigned long *val) { int index, ret = 0; u64 data = 0; @@ -287,7 +153,7 @@ static int loongarch_eiointc_readq(struct kvm_vcpu *vcpu, struct loongarch_eioin ret = -EINVAL; break; } - *(u64 *)val = data; + *val = data; return ret; } @@ -297,7 +163,7 @@ static int kvm_eiointc_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *val) { int ret = -EINVAL; - unsigned long flags; + unsigned long flags, data, offset; struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; if (!eiointc) { @@ -305,358 +171,120 @@ static int kvm_eiointc_read(struct kvm_vcpu *vcpu, return -EINVAL; } - vcpu->kvm->stat.eiointc_read_exits++; + if (addr & (len - 1)) { + kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len); + return -EINVAL; + } + + offset = addr & 0x7; + addr -= offset; + vcpu->stat.eiointc_read_exits++; spin_lock_irqsave(&eiointc->lock, flags); + ret = loongarch_eiointc_read(vcpu, eiointc, addr, &data); + spin_unlock_irqrestore(&eiointc->lock, flags); + if (ret) + return ret; + + data = data >> (offset * 8); switch (len) { case 1: - ret = loongarch_eiointc_readb(vcpu, eiointc, addr, len, val); + *(long *)val = (s8)data; break; case 2: - ret = loongarch_eiointc_readw(vcpu, eiointc, addr, len, val); + *(long *)val = (s16)data; break; case 4: - ret = loongarch_eiointc_readl(vcpu, eiointc, addr, len, val); - break; - case 8: - ret = loongarch_eiointc_readq(vcpu, eiointc, addr, len, val); + *(long *)val = (s32)data; break; default: - WARN_ONCE(1, "%s: Abnormal address access: addr 0x%llx, size %d\n", - __func__, addr, len); - } - spin_unlock_irqrestore(&eiointc->lock, flags); - - return ret; -} - -static int loongarch_eiointc_writeb(struct kvm_vcpu *vcpu, - struct loongarch_eiointc *s, - gpa_t addr, int len, const void *val) -{ - int index, irq, bits, ret = 0; - u8 cpu; - u8 data, old_data; - u8 coreisr, old_coreisr; - gpa_t offset; - - data = *(u8 *)val; - offset = addr - EIOINTC_BASE; - - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = (offset - EIOINTC_NODETYPE_START); - s->nodetype.reg_u8[index] = data; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - /* - * ipmap cannot be set at runtime, can be set only at the beginning - * of irqchip driver, need not update upper irq level - */ - index = (offset - EIOINTC_IPMAP_START); - s->ipmap.reg_u8[index] = data; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = (offset - EIOINTC_ENABLE_START); - old_data = s->enable.reg_u8[index]; - s->enable.reg_u8[index] = data; - /* - * 1: enable irq. - * update irq when isr is set. - */ - data = s->enable.reg_u8[index] & ~old_data & s->isr.reg_u8[index]; - eiointc_enable_irq(vcpu, s, index, data, 1); - /* - * 0: disable irq. - * update irq when isr is set. - */ - data = ~s->enable.reg_u8[index] & old_data & s->isr.reg_u8[index]; - eiointc_enable_irq(vcpu, s, index, data, 0); - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - /* do not emulate hw bounced irq routing */ - index = offset - EIOINTC_BOUNCE_START; - s->bounce.reg_u8[index] = data; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = (offset - EIOINTC_COREISR_START); - /* use attrs to get current cpu index */ - cpu = vcpu->vcpu_id; - coreisr = data; - old_coreisr = s->coreisr.reg_u8[cpu][index]; - /* write 1 to clear interrupt */ - s->coreisr.reg_u8[cpu][index] = old_coreisr & ~coreisr; - coreisr &= old_coreisr; - bits = sizeof(data) * 8; - irq = find_first_bit((void *)&coreisr, bits); - while (irq < bits) { - eiointc_update_irq(s, irq + index * bits, 0); - bitmap_clear((void *)&coreisr, irq, 1); - irq = find_first_bit((void *)&coreisr, bits); - } - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - irq = offset - EIOINTC_COREMAP_START; - index = irq; - s->coremap.reg_u8[index] = data; - eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); - break; - default: - ret = -EINVAL; + *(long *)val = (long)data; break; } - return ret; + return 0; } -static int loongarch_eiointc_writew(struct kvm_vcpu *vcpu, +static int loongarch_eiointc_write(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, - gpa_t addr, int len, const void *val) + gpa_t addr, u64 value, u64 field_mask) { - int i, index, irq, bits, ret = 0; + int index, irq, ret = 0; u8 cpu; - u16 data, old_data; - u16 coreisr, old_coreisr; + u64 data, old, mask; gpa_t offset; - data = *(u16 *)val; - offset = addr - EIOINTC_BASE; + offset = addr & 7; + mask = field_mask << (offset * 8); + data = (value & field_mask) << (offset * 8); - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = (offset - EIOINTC_NODETYPE_START) >> 1; - s->nodetype.reg_u16[index] = data; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - /* - * ipmap cannot be set at runtime, can be set only at the beginning - * of irqchip driver, need not update upper irq level - */ - index = (offset - EIOINTC_IPMAP_START) >> 1; - s->ipmap.reg_u16[index] = data; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = (offset - EIOINTC_ENABLE_START) >> 1; - old_data = s->enable.reg_u32[index]; - s->enable.reg_u16[index] = data; - /* - * 1: enable irq. - * update irq when isr is set. - */ - data = s->enable.reg_u16[index] & ~old_data & s->isr.reg_u16[index]; - index = index << 1; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index + i, mask, 1); - } - /* - * 0: disable irq. - * update irq when isr is set. - */ - data = ~s->enable.reg_u16[index] & old_data & s->isr.reg_u16[index]; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index, mask, 0); - } - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - /* do not emulate hw bounced irq routing */ - index = (offset - EIOINTC_BOUNCE_START) >> 1; - s->bounce.reg_u16[index] = data; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = (offset - EIOINTC_COREISR_START) >> 1; - /* use attrs to get current cpu index */ - cpu = vcpu->vcpu_id; - coreisr = data; - old_coreisr = s->coreisr.reg_u16[cpu][index]; - /* write 1 to clear interrupt */ - s->coreisr.reg_u16[cpu][index] = old_coreisr & ~coreisr; - coreisr &= old_coreisr; - bits = sizeof(data) * 8; - irq = find_first_bit((void *)&coreisr, bits); - while (irq < bits) { - eiointc_update_irq(s, irq + index * bits, 0); - bitmap_clear((void *)&coreisr, irq, 1); - irq = find_first_bit((void *)&coreisr, bits); - } - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - irq = offset - EIOINTC_COREMAP_START; - index = irq >> 1; - s->coremap.reg_u16[index] = data; - eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int loongarch_eiointc_writel(struct kvm_vcpu *vcpu, - struct loongarch_eiointc *s, - gpa_t addr, int len, const void *val) -{ - int i, index, irq, bits, ret = 0; - u8 cpu; - u32 data, old_data; - u32 coreisr, old_coreisr; - gpa_t offset; - - data = *(u32 *)val; - offset = addr - EIOINTC_BASE; - - switch (offset) { - case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: - index = (offset - EIOINTC_NODETYPE_START) >> 2; - s->nodetype.reg_u32[index] = data; - break; - case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: - /* - * ipmap cannot be set at runtime, can be set only at the beginning - * of irqchip driver, need not update upper irq level - */ - index = (offset - EIOINTC_IPMAP_START) >> 2; - s->ipmap.reg_u32[index] = data; - break; - case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: - index = (offset - EIOINTC_ENABLE_START) >> 2; - old_data = s->enable.reg_u32[index]; - s->enable.reg_u32[index] = data; - /* - * 1: enable irq. - * update irq when isr is set. - */ - data = s->enable.reg_u32[index] & ~old_data & s->isr.reg_u32[index]; - index = index << 2; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index + i, mask, 1); - } - /* - * 0: disable irq. - * update irq when isr is set. - */ - data = ~s->enable.reg_u32[index] & old_data & s->isr.reg_u32[index]; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index, mask, 0); - } - break; - case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: - /* do not emulate hw bounced irq routing */ - index = (offset - EIOINTC_BOUNCE_START) >> 2; - s->bounce.reg_u32[index] = data; - break; - case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: - index = (offset - EIOINTC_COREISR_START) >> 2; - /* use attrs to get current cpu index */ - cpu = vcpu->vcpu_id; - coreisr = data; - old_coreisr = s->coreisr.reg_u32[cpu][index]; - /* write 1 to clear interrupt */ - s->coreisr.reg_u32[cpu][index] = old_coreisr & ~coreisr; - coreisr &= old_coreisr; - bits = sizeof(data) * 8; - irq = find_first_bit((void *)&coreisr, bits); - while (irq < bits) { - eiointc_update_irq(s, irq + index * bits, 0); - bitmap_clear((void *)&coreisr, irq, 1); - irq = find_first_bit((void *)&coreisr, bits); - } - break; - case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - irq = offset - EIOINTC_COREMAP_START; - index = irq >> 2; - s->coremap.reg_u32[index] = data; - eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int loongarch_eiointc_writeq(struct kvm_vcpu *vcpu, - struct loongarch_eiointc *s, - gpa_t addr, int len, const void *val) -{ - int i, index, irq, bits, ret = 0; - u8 cpu; - u64 data, old_data; - u64 coreisr, old_coreisr; - gpa_t offset; - - data = *(u64 *)val; + addr -= offset; offset = addr - EIOINTC_BASE; switch (offset) { case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: index = (offset - EIOINTC_NODETYPE_START) >> 3; - s->nodetype.reg_u64[index] = data; + old = s->nodetype.reg_u64[index]; + s->nodetype.reg_u64[index] = (old & ~mask) | data; break; case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: /* * ipmap cannot be set at runtime, can be set only at the beginning * of irqchip driver, need not update upper irq level */ - index = (offset - EIOINTC_IPMAP_START) >> 3; - s->ipmap.reg_u64 = data; + old = s->ipmap.reg_u64; + s->ipmap.reg_u64 = (old & ~mask) | data; break; case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: index = (offset - EIOINTC_ENABLE_START) >> 3; - old_data = s->enable.reg_u64[index]; - s->enable.reg_u64[index] = data; + old = s->enable.reg_u64[index]; + s->enable.reg_u64[index] = (old & ~mask) | data; /* * 1: enable irq. * update irq when isr is set. */ - data = s->enable.reg_u64[index] & ~old_data & s->isr.reg_u64[index]; - index = index << 3; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index + i, mask, 1); + data = s->enable.reg_u64[index] & ~old & s->isr.reg_u64[index]; + while (data) { + irq = __ffs(data); + eiointc_update_irq(s, irq + index * 64, 1); + data &= ~BIT_ULL(irq); } /* * 0: disable irq. * update irq when isr is set. */ - data = ~s->enable.reg_u64[index] & old_data & s->isr.reg_u64[index]; - for (i = 0; i < sizeof(data); i++) { - u8 mask = (data >> (i * 8)) & 0xff; - eiointc_enable_irq(vcpu, s, index, mask, 0); + data = ~s->enable.reg_u64[index] & old & s->isr.reg_u64[index]; + while (data) { + irq = __ffs(data); + eiointc_update_irq(s, irq + index * 64, 0); + data &= ~BIT_ULL(irq); } break; case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: /* do not emulate hw bounced irq routing */ index = (offset - EIOINTC_BOUNCE_START) >> 3; - s->bounce.reg_u64[index] = data; + old = s->bounce.reg_u64[index]; + s->bounce.reg_u64[index] = (old & ~mask) | data; break; case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: index = (offset - EIOINTC_COREISR_START) >> 3; /* use attrs to get current cpu index */ cpu = vcpu->vcpu_id; - coreisr = data; - old_coreisr = s->coreisr.reg_u64[cpu][index]; + old = s->coreisr.reg_u64[cpu][index]; /* write 1 to clear interrupt */ - s->coreisr.reg_u64[cpu][index] = old_coreisr & ~coreisr; - coreisr &= old_coreisr; - bits = sizeof(data) * 8; - irq = find_first_bit((void *)&coreisr, bits); - while (irq < bits) { - eiointc_update_irq(s, irq + index * bits, 0); - bitmap_clear((void *)&coreisr, irq, 1); - irq = find_first_bit((void *)&coreisr, bits); + s->coreisr.reg_u64[cpu][index] = old & ~data; + data &= old; + while (data) { + irq = __ffs(data); + eiointc_update_irq(s, irq + index * 64, 0); + data &= ~BIT_ULL(irq); } break; case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: - irq = offset - EIOINTC_COREMAP_START; - index = irq >> 3; - s->coremap.reg_u64[index] = data; - eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); + index = (offset - EIOINTC_COREMAP_START) >> 3; + old = s->coremap.reg_u64[index]; + s->coremap.reg_u64[index] = (old & ~mask) | data; + data = s->coremap.reg_u64[index]; + eiointc_update_sw_coremap(s, index * 8, data, sizeof(data), true); break; default: ret = -EINVAL; @@ -671,7 +299,7 @@ static int kvm_eiointc_write(struct kvm_vcpu *vcpu, gpa_t addr, int len, const void *val) { int ret = -EINVAL; - unsigned long flags; + unsigned long flags, value; struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; if (!eiointc) { @@ -679,24 +307,30 @@ static int kvm_eiointc_write(struct kvm_vcpu *vcpu, return -EINVAL; } - vcpu->kvm->stat.eiointc_write_exits++; + if (addr & (len - 1)) { + kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len); + return -EINVAL; + } + + vcpu->stat.eiointc_write_exits++; spin_lock_irqsave(&eiointc->lock, flags); switch (len) { case 1: - ret = loongarch_eiointc_writeb(vcpu, eiointc, addr, len, val); + value = *(unsigned char *)val; + ret = loongarch_eiointc_write(vcpu, eiointc, addr, value, 0xFF); break; case 2: - ret = loongarch_eiointc_writew(vcpu, eiointc, addr, len, val); + value = *(unsigned short *)val; + ret = loongarch_eiointc_write(vcpu, eiointc, addr, value, USHRT_MAX); break; case 4: - ret = loongarch_eiointc_writel(vcpu, eiointc, addr, len, val); - break; - case 8: - ret = loongarch_eiointc_writeq(vcpu, eiointc, addr, len, val); + value = *(unsigned int *)val; + ret = loongarch_eiointc_write(vcpu, eiointc, addr, value, UINT_MAX); break; default: - WARN_ONCE(1, "%s: Abnormal address access: addr 0x%llx, size %d\n", - __func__, addr, len); + value = *(unsigned long *)val; + ret = loongarch_eiointc_write(vcpu, eiointc, addr, value, ULONG_MAX); + break; } spin_unlock_irqrestore(&eiointc->lock, flags); @@ -787,7 +421,7 @@ static int kvm_eiointc_ctrl_access(struct kvm_device *dev, int ret = 0; unsigned long flags; unsigned long type = (unsigned long)attr->attr; - u32 i, start_irq; + u32 i, start_irq, val; void __user *data; struct loongarch_eiointc *s = dev->kvm->arch.eiointc; @@ -795,8 +429,14 @@ static int kvm_eiointc_ctrl_access(struct kvm_device *dev, spin_lock_irqsave(&s->lock, flags); switch (type) { case KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU: - if (copy_from_user(&s->num_cpu, data, 4)) + if (copy_from_user(&val, data, 4)) ret = -EFAULT; + else { + if (val >= EIOINTC_ROUTE_MAX_VCPUS) + ret = -EINVAL; + else + s->num_cpu = val; + } break; case KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE: if (copy_from_user(&s->features, data, 4)) @@ -809,7 +449,7 @@ static int kvm_eiointc_ctrl_access(struct kvm_device *dev, for (i = 0; i < (EIOINTC_IRQS / 4); i++) { start_irq = i * 4; eiointc_update_sw_coremap(s, start_irq, - (void *)&s->coremap.reg_u32[i], sizeof(u32), false); + s->coremap.reg_u32[i], sizeof(u32), false); } break; default: @@ -824,7 +464,7 @@ static int kvm_eiointc_regs_access(struct kvm_device *dev, struct kvm_device_attr *attr, bool is_write) { - int addr, cpuid, offset, ret = 0; + int addr, cpu, offset, ret = 0; unsigned long flags; void *p = NULL; void __user *data; @@ -832,7 +472,7 @@ static int kvm_eiointc_regs_access(struct kvm_device *dev, s = dev->kvm->arch.eiointc; addr = attr->attr; - cpuid = addr >> 16; + cpu = addr >> 16; addr &= 0xffff; data = (void __user *)attr->addr; switch (addr) { @@ -857,8 +497,11 @@ static int kvm_eiointc_regs_access(struct kvm_device *dev, p = &s->isr.reg_u32[offset]; break; case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + if (cpu >= s->num_cpu) + return -EINVAL; + offset = (addr - EIOINTC_COREISR_START) / 4; - p = &s->coreisr.reg_u32[cpuid][offset]; + p = &s->coreisr.reg_u32[cpu][offset]; break; case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: offset = (addr - EIOINTC_COREMAP_START) / 4; @@ -899,9 +542,15 @@ static int kvm_eiointc_sw_status_access(struct kvm_device *dev, data = (void __user *)attr->addr; switch (addr) { case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU: + if (is_write) + return ret; + p = &s->num_cpu; break; case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE: + if (is_write) + return ret; + p = &s->features; break; case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE: @@ -956,7 +605,7 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) { int ret; struct loongarch_eiointc *s; - struct kvm_io_device *device, *device1; + struct kvm_io_device *device; struct kvm *kvm = dev->kvm; /* eiointc has been created */ @@ -984,10 +633,10 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) return ret; } - device1 = &s->device_vext; - kvm_iodevice_init(device1, &kvm_eiointc_virt_ops); + device = &s->device_vext; + kvm_iodevice_init(device, &kvm_eiointc_virt_ops); ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, - EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device1); + EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); if (ret < 0) { kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); kfree(s); diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index fe734dc062ed..5a8481dda052 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -99,7 +99,7 @@ static void write_mailbox(struct kvm_vcpu *vcpu, int offset, uint64_t data, int static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) { int i, idx, ret; - uint32_t val = 0, mask = 0; + uint64_t val = 0, mask = 0; /* * Bit 27-30 is mask for byte writing. @@ -108,7 +108,7 @@ static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) if ((data >> 27) & 0xf) { /* Read the old val */ idx = srcu_read_lock(&vcpu->kvm->srcu); - ret = kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); + ret = kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, 4, &val); srcu_read_unlock(&vcpu->kvm->srcu, idx); if (unlikely(ret)) { kvm_err("%s: : read data from addr %llx failed\n", __func__, addr); @@ -124,7 +124,7 @@ static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) } val |= ((uint32_t)(data >> 32) & ~mask); idx = srcu_read_lock(&vcpu->kvm->srcu); - ret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); + ret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, 4, &val); srcu_read_unlock(&vcpu->kvm->srcu, idx); if (unlikely(ret)) kvm_err("%s: : write data to addr %llx failed\n", __func__, addr); @@ -268,36 +268,16 @@ static int kvm_ipi_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, gpa_t addr, int len, void *val) { - int ret; - struct loongarch_ipi *ipi; - - ipi = vcpu->kvm->arch.ipi; - if (!ipi) { - kvm_err("%s: ipi irqchip not valid!\n", __func__); - return -EINVAL; - } - ipi->kvm->stat.ipi_read_exits++; - ret = loongarch_ipi_readl(vcpu, addr, len, val); - - return ret; + vcpu->stat.ipi_read_exits++; + return loongarch_ipi_readl(vcpu, addr, len, val); } static int kvm_ipi_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, gpa_t addr, int len, const void *val) { - int ret; - struct loongarch_ipi *ipi; - - ipi = vcpu->kvm->arch.ipi; - if (!ipi) { - kvm_err("%s: ipi irqchip not valid!\n", __func__); - return -EINVAL; - } - ipi->kvm->stat.ipi_write_exits++; - ret = loongarch_ipi_writel(vcpu, addr, len, val); - - return ret; + vcpu->stat.ipi_write_exits++; + return loongarch_ipi_writel(vcpu, addr, len, val); } static const struct kvm_io_device_ops kvm_ipi_ops = { @@ -318,7 +298,7 @@ static int kvm_ipi_regs_access(struct kvm_device *dev, cpu = (attr->attr >> 16) & 0x3ff; addr = attr->attr & 0xff; - vcpu = kvm_get_vcpu(dev->kvm, cpu); + vcpu = kvm_get_vcpu_by_id(dev->kvm, cpu); if (unlikely(vcpu == NULL)) { kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); return -EINVAL; diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index 08fce845f668..119290bcea79 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -195,8 +195,13 @@ static int kvm_pch_pic_read(struct kvm_vcpu *vcpu, return -EINVAL; } + if (addr & (len - 1)) { + kvm_err("%s: pch pic not aligned addr %llx len %d\n", __func__, addr, len); + return -EINVAL; + } + /* statistics of pch pic reading */ - vcpu->kvm->stat.pch_pic_read_exits++; + vcpu->stat.pch_pic_read_exits++; ret = loongarch_pch_pic_read(s, addr, len, val); return ret; @@ -302,8 +307,13 @@ static int kvm_pch_pic_write(struct kvm_vcpu *vcpu, return -EINVAL; } + if (addr & (len - 1)) { + kvm_err("%s: pch pic not aligned addr %llx len %d\n", __func__, addr, len); + return -EINVAL; + } + /* statistics of pch pic writing */ - vcpu->kvm->stat.pch_pic_write_exits++; + vcpu->stat.pch_pic_write_exits++; ret = loongarch_pch_pic_write(s, addr, len, val); return ret; diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index 4c3f22de4b40..8462083f0301 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -83,28 +83,11 @@ void kvm_deliver_intr(struct kvm_vcpu *vcpu) unsigned long *pending = &vcpu->arch.irq_pending; unsigned long *pending_clr = &vcpu->arch.irq_clear; - if (!(*pending) && !(*pending_clr)) - return; + for_each_set_bit(priority, pending_clr, INT_IPI + 1) + kvm_irq_clear(vcpu, priority); - if (*pending_clr) { - priority = __ffs(*pending_clr); - while (priority <= INT_IPI) { - kvm_irq_clear(vcpu, priority); - priority = find_next_bit(pending_clr, - BITS_PER_BYTE * sizeof(*pending_clr), - priority + 1); - } - } - - if (*pending) { - priority = __ffs(*pending); - while (priority <= INT_IPI) { - kvm_irq_deliver(vcpu, priority); - priority = find_next_bit(pending, - BITS_PER_BYTE * sizeof(*pending), - priority + 1); - } - } + for_each_set_bit(priority, pending, INT_IPI + 1) + kvm_irq_deliver(vcpu, priority); } int kvm_pending_timer(struct kvm_vcpu *vcpu) diff --git a/arch/loongarch/kvm/trace.h b/arch/loongarch/kvm/trace.h index 1783397b1bc8..145514dab6d5 100644 --- a/arch/loongarch/kvm/trace.h +++ b/arch/loongarch/kvm/trace.h @@ -46,11 +46,15 @@ DEFINE_EVENT(kvm_transition, kvm_out, /* Further exit reasons */ #define KVM_TRACE_EXIT_IDLE 64 #define KVM_TRACE_EXIT_CACHE 65 +#define KVM_TRACE_EXIT_CPUCFG 66 +#define KVM_TRACE_EXIT_CSR 67 /* Tracepoints for VM exits */ #define kvm_trace_symbol_exit_types \ { KVM_TRACE_EXIT_IDLE, "IDLE" }, \ - { KVM_TRACE_EXIT_CACHE, "CACHE" } + { KVM_TRACE_EXIT_CACHE, "CACHE" }, \ + { KVM_TRACE_EXIT_CPUCFG, "CPUCFG" }, \ + { KVM_TRACE_EXIT_CSR, "CSR" } DECLARE_EVENT_CLASS(kvm_exit, TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), @@ -82,6 +86,14 @@ DEFINE_EVENT(kvm_exit, kvm_exit_cache, TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), TP_ARGS(vcpu, reason)); +DEFINE_EVENT(kvm_exit, kvm_exit_cpucfg, + TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), + TP_ARGS(vcpu, reason)); + +DEFINE_EVENT(kvm_exit, kvm_exit_csr, + TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), + TP_ARGS(vcpu, reason)); + DEFINE_EVENT(kvm_exit, kvm_exit, TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), TP_ARGS(vcpu, reason)); diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 5af32ec62cb1..ce478151466c 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -20,7 +20,13 @@ const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { STATS_DESC_COUNTER(VCPU, idle_exits), STATS_DESC_COUNTER(VCPU, cpucfg_exits), STATS_DESC_COUNTER(VCPU, signal_exits), - STATS_DESC_COUNTER(VCPU, hypercall_exits) + STATS_DESC_COUNTER(VCPU, hypercall_exits), + STATS_DESC_COUNTER(VCPU, ipi_read_exits), + STATS_DESC_COUNTER(VCPU, ipi_write_exits), + STATS_DESC_COUNTER(VCPU, eiointc_read_exits), + STATS_DESC_COUNTER(VCPU, eiointc_write_exits), + STATS_DESC_COUNTER(VCPU, pch_pic_read_exits), + STATS_DESC_COUNTER(VCPU, pch_pic_write_exits) }; const struct kvm_stats_header kvm_vcpu_stats_header = { @@ -1277,9 +1283,11 @@ int kvm_own_lbt(struct kvm_vcpu *vcpu) return -EINVAL; preempt_disable(); - set_csr_euen(CSR_EUEN_LBTEN); - _restore_lbt(&vcpu->arch.lbt); - vcpu->arch.aux_inuse |= KVM_LARCH_LBT; + if (!(vcpu->arch.aux_inuse & KVM_LARCH_LBT)) { + set_csr_euen(CSR_EUEN_LBTEN); + _restore_lbt(&vcpu->arch.lbt); + vcpu->arch.aux_inuse |= KVM_LARCH_LBT; + } preempt_enable(); return 0; diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile index fae77809048b..ccea3bbd4353 100644 --- a/arch/loongarch/lib/Makefile +++ b/arch/loongarch/lib/Makefile @@ -11,5 +11,3 @@ 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 - -obj-$(CONFIG_CRC32_ARCH) += crc32-loongarch.o diff --git a/arch/loongarch/lib/csum.c b/arch/loongarch/lib/csum.c index df309ae4045d..bcc9d01d8c41 100644 --- a/arch/loongarch/lib/csum.c +++ b/arch/loongarch/lib/csum.c @@ -2,6 +2,7 @@ // Copyright (C) 2019-2020 Arm Ltd. #include +#include #include #include diff --git a/arch/loongarch/mm/ioremap.c b/arch/loongarch/mm/ioremap.c index 70ca73019811..df949a3d0f34 100644 --- a/arch/loongarch/mm/ioremap.c +++ b/arch/loongarch/mm/ioremap.c @@ -16,12 +16,12 @@ void __init early_iounmap(void __iomem *addr, unsigned long size) } -void *early_memremap_ro(resource_size_t phys_addr, unsigned long size) +void * __init early_memremap_ro(resource_size_t phys_addr, unsigned long size) { return early_memremap(phys_addr, size); } -void *early_memremap_prot(resource_size_t phys_addr, unsigned long size, +void * __init early_memremap_prot(resource_size_t phys_addr, unsigned long size, unsigned long prot_val) { return early_memremap(phys_addr, size); diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c index 99165903908a..f5e910b68229 100644 --- a/arch/loongarch/mm/pageattr.c +++ b/arch/loongarch/mm/pageattr.c @@ -118,7 +118,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, pgp return 0; mmap_write_lock(&init_mm); - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, &masks); + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); mmap_write_unlock(&init_mm); flush_tlb_kernel_range(start, end); diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index fa1500d4aa3e..abfdb6bb5c38 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -4,13 +4,20 @@ * * Copyright (C) 2022 Loongson Technology Corporation Limited */ +#include #include "bpf_jit.h" -#define REG_TCC LOONGARCH_GPR_A6 -#define TCC_SAVED LOONGARCH_GPR_S5 +#define LOONGARCH_MAX_REG_ARGS 8 -#define SAVE_RA BIT(0) -#define SAVE_TCC BIT(1) +#define LOONGARCH_LONG_JUMP_NINSNS 5 +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) + +#define LOONGARCH_FENTRY_NINSNS 2 +#define LOONGARCH_FENTRY_NBYTES (LOONGARCH_FENTRY_NINSNS * 4) +#define LOONGARCH_BPF_FENTRY_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) + +#define REG_TCC LOONGARCH_GPR_A6 +#define BPF_TAIL_CALL_CNT_PTR_STACK_OFF(stack) (round_up(stack, 16) - 80) static const int regmap[] = { /* return value from in-kernel function, and exit value for eBPF program */ @@ -32,32 +39,57 @@ static const int regmap[] = { [BPF_REG_AX] = LOONGARCH_GPR_T0, }; -static void mark_call(struct jit_ctx *ctx) +static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset) { - ctx->flags |= SAVE_RA; -} + const struct bpf_prog *prog = ctx->prog; + const bool is_main_prog = !bpf_is_subprog(prog); -static void mark_tail_call(struct jit_ctx *ctx) -{ - ctx->flags |= SAVE_TCC; -} + if (is_main_prog) { + /* + * LOONGARCH_GPR_T3 = MAX_TAIL_CALL_CNT + * if (REG_TCC > T3 ) + * std REG_TCC -> LOONGARCH_GPR_SP + store_offset + * else + * std REG_TCC -> LOONGARCH_GPR_SP + store_offset + * REG_TCC = LOONGARCH_GPR_SP + store_offset + * + * std REG_TCC -> LOONGARCH_GPR_SP + store_offset + * + * The purpose of this code is to first push the TCC into stack, + * and then push the address of TCC into stack. + * In cases where bpf2bpf and tailcall are used in combination, + * the value in REG_TCC may be a count or an address, + * these two cases need to be judged and handled separately. + */ + emit_insn(ctx, addid, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); + *store_offset -= sizeof(long); -static bool seen_call(struct jit_ctx *ctx) -{ - return (ctx->flags & SAVE_RA); -} + emit_cond_jmp(ctx, BPF_JGT, REG_TCC, LOONGARCH_GPR_T3, 4); -static bool seen_tail_call(struct jit_ctx *ctx) -{ - return (ctx->flags & SAVE_TCC); -} + /* + * If REG_TCC < MAX_TAIL_CALL_CNT, the value in REG_TCC is a count, + * push tcc into stack + */ + emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset); -static u8 tail_call_reg(struct jit_ctx *ctx) -{ - if (seen_call(ctx)) - return TCC_SAVED; + /* Push the address of TCC into the REG_TCC */ + emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_SP, *store_offset); - return REG_TCC; + emit_uncond_jmp(ctx, 2); + + /* + * If REG_TCC > MAX_TAIL_CALL_CNT, the value in REG_TCC is an address, + * push tcc_ptr into stack + */ + emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset); + } else { + *store_offset -= sizeof(long); + emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset); + } + + /* Push tcc_ptr into stack */ + *store_offset -= sizeof(long); + emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset); } /* @@ -80,6 +112,10 @@ static u8 tail_call_reg(struct jit_ctx *ctx) * | $s4 | * +-------------------------+ * | $s5 | + * +-------------------------+ + * | tcc | + * +-------------------------+ + * | tcc_ptr | * +-------------------------+ <--BPF_REG_FP * | prog->aux->stack_depth | * | (optional) | @@ -88,22 +124,32 @@ static u8 tail_call_reg(struct jit_ctx *ctx) */ static void build_prologue(struct jit_ctx *ctx) { - int stack_adjust = 0, store_offset, bpf_stack_adjust; + int i, stack_adjust = 0, store_offset, bpf_stack_adjust; + const struct bpf_prog *prog = ctx->prog; + const bool is_main_prog = !bpf_is_subprog(prog); bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); - /* To store ra, fp, s0, s1, s2, s3, s4 and s5. */ + /* To store ra, fp, s0, s1, s2, s3, s4, s5 */ stack_adjust += sizeof(long) * 8; + /* To store tcc and tcc_ptr */ + stack_adjust += sizeof(long) * 2; + stack_adjust = round_up(stack_adjust, 16); stack_adjust += bpf_stack_adjust; + /* Reserve space for the move_imm + jirl instruction */ + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) + emit_insn(ctx, nop); + /* - * First instruction initializes the tail call count (TCC). - * On tail call we skip this instruction, and the TCC is - * passed in REG_TCC from the caller. + * First instruction initializes the tail call count (TCC) + * register to zero. On tail call we skip this instruction, + * and the TCC is passed in REG_TCC from the caller. */ - emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); + if (is_main_prog) + emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_ZERO, 0); emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -stack_adjust); @@ -131,20 +177,13 @@ static void build_prologue(struct jit_ctx *ctx) store_offset -= sizeof(long); emit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset); + prepare_bpf_tail_call_cnt(ctx, &store_offset); + emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust); if (bpf_stack_adjust) emit_insn(ctx, addid, regmap[BPF_REG_FP], LOONGARCH_GPR_SP, bpf_stack_adjust); - /* - * Program contains calls and tail calls, so REG_TCC need - * to be saved across calls. - */ - if (seen_tail_call(ctx) && seen_call(ctx)) - move_reg(ctx, TCC_SAVED, REG_TCC); - else - emit_insn(ctx, nop); - ctx->stack_size = stack_adjust; } @@ -177,6 +216,16 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) load_offset -= sizeof(long); emit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset); + /* + * When push into the stack, follow the order of tcc then tcc_ptr. + * When pop from the stack, first pop tcc_ptr then followed by tcc. + */ + load_offset -= 2 * sizeof(long); + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset); + + load_offset += sizeof(long); + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset); + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust); if (!is_tail_call) { @@ -189,7 +238,7 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) * Call the next bpf prog and skip the first instruction * of TCC initialization. */ - emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 1); + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 6); } } @@ -208,12 +257,10 @@ bool bpf_jit_supports_far_kfunc_call(void) return true; } -/* initialized on the first pass of build_body() */ -static int out_offset = -1; -static int emit_bpf_tail_call(struct jit_ctx *ctx) +static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) { - int off; - u8 tcc = tail_call_reg(ctx); + int off, tc_ninsn = 0; + int tcc_ptr_off = BPF_TAIL_CALL_CNT_PTR_STACK_OFF(ctx->stack_size); u8 a1 = LOONGARCH_GPR_A1; u8 a2 = LOONGARCH_GPR_A2; u8 t1 = LOONGARCH_GPR_T1; @@ -222,7 +269,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) const int idx0 = ctx->idx; #define cur_offset (ctx->idx - idx0) -#define jmp_offset (out_offset - (cur_offset)) +#define jmp_offset (tc_ninsn - (cur_offset)) /* * a0: &ctx @@ -232,6 +279,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) * if (index >= array->map.max_entries) * goto out; */ + tc_ninsn = insn ? ctx->offset[insn+1] - ctx->offset[insn] : ctx->offset[0]; off = offsetof(struct bpf_array, map.max_entries); emit_insn(ctx, ldwu, t1, a1, off); /* bgeu $a2, $t1, jmp_offset */ @@ -239,11 +287,15 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) goto toofar; /* - * if (--TCC < 0) - * goto out; + * if ((*tcc_ptr)++ >= MAX_TAIL_CALL_CNT) + * goto out; */ - emit_insn(ctx, addid, REG_TCC, tcc, -1); - if (emit_tailcall_jmp(ctx, BPF_JSLT, REG_TCC, LOONGARCH_GPR_ZERO, jmp_offset) < 0) + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); + emit_insn(ctx, ldd, t3, REG_TCC, 0); + emit_insn(ctx, addid, t3, t3, 1); + emit_insn(ctx, std, t3, REG_TCC, 0); + emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); + if (emit_tailcall_jmp(ctx, BPF_JSGT, t3, t2, jmp_offset) < 0) goto toofar; /* @@ -263,15 +315,6 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) emit_insn(ctx, ldd, t3, t2, off); __build_epilogue(ctx, true); - /* out: */ - if (out_offset == -1) - out_offset = cur_offset; - if (cur_offset != out_offset) { - pr_err_once("tail_call out_offset = %d, expected %d!\n", - cur_offset, out_offset); - return -1; - } - return 0; toofar: @@ -463,7 +506,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext u64 func_addr; bool func_addr_fixed, sign_extend; int i = insn - ctx->prog->insnsi; - int ret, jmp_offset; + int ret, jmp_offset, tcc_ptr_off; const u8 code = insn->code; const u8 cond = BPF_OP(code); const u8 t1 = LOONGARCH_GPR_T1; @@ -899,12 +942,16 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext /* function call */ case BPF_JMP | BPF_CALL: - mark_call(ctx); ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &func_addr, &func_addr_fixed); if (ret < 0) return ret; + if (insn->src_reg == BPF_PSEUDO_CALL) { + tcc_ptr_off = BPF_TAIL_CALL_CNT_PTR_STACK_OFF(ctx->stack_size); + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); + } + move_addr(ctx, t1, func_addr); emit_insn(ctx, jirl, LOONGARCH_GPR_RA, t1, 0); @@ -915,8 +962,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext /* tail call */ case BPF_JMP | BPF_TAIL_CALL: - mark_tail_call(ctx); - if (emit_bpf_tail_call(ctx) < 0) + if (emit_bpf_tail_call(ctx, i) < 0) return -EINVAL; break; @@ -1180,12 +1226,528 @@ static int validate_code(struct jit_ctx *ctx) return -1; } + return 0; +} + +static int validate_ctx(struct jit_ctx *ctx) +{ + if (validate_code(ctx)) + return -1; + if (WARN_ON_ONCE(ctx->num_exentries != ctx->prog->aux->num_exentries)) return -1; return 0; } +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) +{ + if (!target) { + pr_err("bpf_jit: jump target address is error\n"); + return -EFAULT; + } + + move_imm(ctx, LOONGARCH_GPR_T1, target, false); + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); + + return 0; +} + +static int emit_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) +{ + int i; + struct jit_ctx ctx; + + ctx.idx = 0; + ctx.image = (union loongarch_instruction *)insns; + + if (!target) { + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) + emit_insn((&ctx), nop); + return 0; + } + + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, (u64)target); +} + +static int emit_call(struct jit_ctx *ctx, u64 addr) +{ + return emit_jump_and_link(ctx, LOONGARCH_GPR_RA, addr); +} + +void *bpf_arch_text_copy(void *dst, void *src, size_t len) +{ + int ret; + + mutex_lock(&text_mutex); + ret = larch_insn_text_copy(dst, src, len); + mutex_unlock(&text_mutex); + + return ret ? ERR_PTR(-EINVAL) : dst; +} + +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, + void *old_addr, void *new_addr) +{ + int ret; + bool is_call = (poke_type == BPF_MOD_CALL); + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; + + if (!is_kernel_text((unsigned long)ip) && + !is_bpf_text_address((unsigned long)ip)) + return -ENOTSUPP; + + ret = emit_jump_or_nops(old_addr, ip, old_insns, is_call); + if (ret) + return ret; + + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) + return -EFAULT; + + ret = emit_jump_or_nops(new_addr, ip, new_insns, is_call); + if (ret) + return ret; + + mutex_lock(&text_mutex); + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); + mutex_unlock(&text_mutex); + + return ret; +} + +int bpf_arch_text_invalidate(void *dst, size_t len) +{ + int i; + int ret = 0; + u32 *inst; + + inst = kvmalloc(len, GFP_KERNEL); + if (!inst) + return -ENOMEM; + + for (i = 0; i < (len / sizeof(u32)); i++) + inst[i] = INSN_BREAK; + + mutex_lock(&text_mutex); + if (larch_insn_text_copy(dst, inst, len)) + ret = -EINVAL; + mutex_unlock(&text_mutex); + + kvfree(inst); + + return ret; +} + +static void store_args(struct jit_ctx *ctx, int nargs, int args_off) +{ + int i; + + for (i = 0; i < nargs; i++) { + emit_insn(ctx, std, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off); + args_off -= 8; + } +} + +static void restore_args(struct jit_ctx *ctx, int nargs, int args_off) +{ + int i; + + for (i = 0; i < nargs; i++) { + emit_insn(ctx, ldd, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off); + 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) +{ + int ret; + u32 *branch; + 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 { + 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); + /* arg2: &run_ctx */ + emit_insn(ctx, addid, LOONGARCH_GPR_A1, LOONGARCH_GPR_FP, -run_ctx_off); + ret = emit_call(ctx, (const u64)bpf_trampoline_enter(p)); + if (ret) + return ret; + + /* store prog start time */ + move_reg(ctx, LOONGARCH_GPR_S1, LOONGARCH_GPR_A0); + + /* + * if (__bpf_prog_enter(prog) == 0) + * goto skip_exec_of_prog; + */ + branch = (u32 *)ctx->image + ctx->idx; + /* nop reserved for conditional jump */ + emit_insn(ctx, nop); + + /* arg1: &args_off */ + emit_insn(ctx, addid, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -args_off); + if (!p->jited) + move_imm(ctx, LOONGARCH_GPR_A1, (const s64)p->insnsi, false); + ret = emit_call(ctx, (const u64)p->bpf_func); + if (ret) + return ret; + + if (save_ret) { + emit_insn(ctx, std, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -retval_off); + emit_insn(ctx, std, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8)); + } + + /* update branch with beqz */ + if (ctx->image) { + int offset = (void *)(&ctx->image[ctx->idx]) - (void *)branch; + *branch = larch_insn_gen_beq(LOONGARCH_GPR_A0, LOONGARCH_GPR_ZERO, offset); + } + + /* arg1: prog */ + move_imm(ctx, LOONGARCH_GPR_A0, (const s64)p, false); + /* arg2: prog start time */ + move_reg(ctx, LOONGARCH_GPR_A1, LOONGARCH_GPR_S1); + /* arg3: &run_ctx */ + emit_insn(ctx, addid, LOONGARCH_GPR_A2, LOONGARCH_GPR_FP, -run_ctx_off); + ret = emit_call(ctx, (const u64)bpf_trampoline_exit(p)); + + 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) +{ + int i; + + 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); + } +} + +void *arch_alloc_bpf_trampoline(unsigned int size) +{ + return bpf_prog_pack_alloc(size, jit_fill_hole); +} + +void arch_free_bpf_trampoline(void *image, unsigned int size) +{ + bpf_prog_pack_free(image, size); +} + +static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, + const struct btf_func_model *m, struct bpf_tramp_links *tlinks, + void *func_addr, u32 flags) +{ + int i, ret, save_ret; + int stack_size = 0, nargs = 0; + int retval_off, args_off, nargs_off, ip_off, run_ctx_off, sreg_off, tcc_ptr_off; + bool is_struct_ops = flags & BPF_TRAMP_F_INDIRECT; + void *orig_call = func_addr; + struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; + struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; + struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; + u32 **branches = NULL; + + if (flags & (BPF_TRAMP_F_ORIG_STACK | BPF_TRAMP_F_SHARE_IPMODIFY)) + return -ENOTSUPP; + + /* + * FP + 8 [ RA to parent func ] return address to parent + * function + * FP + 0 [ FP of parent func ] frame pointer of parent + * function + * FP - 8 [ T0 to traced func ] return address of traced + * function + * 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 - nargs_off [ regs count ] + * + * FP - ip_off [ traced func ] BPF_TRAMP_F_IP_ARG + * + * FP - run_ctx_off [ bpf_tramp_run_ctx ] + * + * FP - sreg_off [ callee saved reg ] + * + * FP - tcc_ptr_off [ tail_call_cnt_ptr ] + */ + + if (m->nr_args > LOONGARCH_MAX_REG_ARGS) + return -ENOTSUPP; + + if (flags & (BPF_TRAMP_F_ORIG_STACK | BPF_TRAMP_F_SHARE_IPMODIFY)) + return -ENOTSUPP; + + stack_size = 0; + + /* Room of trampoline frame to store return address and frame pointer */ + stack_size += 16; + + save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); + if (save_ret) { + /* Save BPF R0 and A0 */ + stack_size += 16; + retval_off = stack_size; + } + + /* Room of trampoline frame to store args */ + nargs = m->nr_args; + stack_size += nargs * 8; + args_off = stack_size; + + /* Room of trampoline frame to store args number */ + stack_size += 8; + nargs_off = stack_size; + + /* Room of trampoline frame to store ip address */ + if (flags & BPF_TRAMP_F_IP_ARG) { + stack_size += 8; + ip_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; + + stack_size += 8; + sreg_off = stack_size; + + /* Room of trampoline frame to store tail_call_cnt_ptr */ + if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) { + stack_size += 8; + tcc_ptr_off = stack_size; + } + + stack_size = round_up(stack_size, 16); + + if (is_struct_ops) { + /* + * For the trampoline called directly, just handle + * the frame of trampoline. + */ + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -stack_size); + emit_insn(ctx, std, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, stack_size - 8); + emit_insn(ctx, std, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size - 16); + emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size); + } else { + /* + * For the trampoline called from function entry, + * the frame of traced function and the frame of + * trampoline need to be considered. + */ + /* RA and FP for parent function */ + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -16); + emit_insn(ctx, std, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, 8); + emit_insn(ctx, std, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, 0); + emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, 16); + + /* RA and FP for traced function */ + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -stack_size); + emit_insn(ctx, std, LOONGARCH_GPR_T0, LOONGARCH_GPR_SP, stack_size - 8); + emit_insn(ctx, std, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size - 16); + emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size); + } + + if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) + emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_FP, -tcc_ptr_off); + + /* callee saved register S1 to pass start time */ + 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); + } + + /* 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)) + orig_call += LOONGARCH_FENTRY_NBYTES; + /* Direct jump skips 5 NOP instructions */ + else if (is_bpf_text_address((unsigned long)orig_call)) + orig_call += LOONGARCH_BPF_FENTRY_NBYTES; + + if (flags & BPF_TRAMP_F_CALL_ORIG) { + move_imm(ctx, LOONGARCH_GPR_A0, (const s64)im, false); + ret = emit_call(ctx, (const u64)__bpf_tramp_enter); + if (ret) + 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 (ret) + return ret; + } + if (fmod_ret->nr_links) { + branches = kcalloc(fmod_ret->nr_links, sizeof(u32 *), GFP_KERNEL); + if (!branches) + return -ENOMEM; + + invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, run_ctx_off, branches); + } + + if (flags & BPF_TRAMP_F_CALL_ORIG) { + restore_args(ctx, m->nr_args, args_off); + + if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_FP, -tcc_ptr_off); + + ret = emit_call(ctx, (const u64)orig_call); + if (ret) + goto out; + emit_insn(ctx, std, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -retval_off); + emit_insn(ctx, std, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8)); + im->ip_after_call = ctx->ro_image + ctx->idx; + /* Reserve space for the move_imm + jirl instruction */ + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) + emit_insn(ctx, nop); + } + + for (i = 0; ctx->image && i < fmod_ret->nr_links; i++) { + int offset = (void *)(&ctx->image[ctx->idx]) - (void *)branches[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); + if (ret) + goto out; + } + + if (flags & BPF_TRAMP_F_CALL_ORIG) { + im->ip_epilogue = ctx->ro_image + ctx->idx; + move_imm(ctx, LOONGARCH_GPR_A0, (const s64)im, false); + ret = emit_call(ctx, (const u64)__bpf_tramp_exit); + if (ret) + goto out; + } + + if (flags & BPF_TRAMP_F_RESTORE_REGS) + restore_args(ctx, m->nr_args, args_off); + + if (save_ret) { + emit_insn(ctx, ldd, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -retval_off); + emit_insn(ctx, ldd, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8)); + } + + emit_insn(ctx, ldd, LOONGARCH_GPR_S1, LOONGARCH_GPR_FP, -sreg_off); + + if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_FP, -tcc_ptr_off); + + if (is_struct_ops) { + /* trampoline called directly */ + emit_insn(ctx, ldd, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, stack_size - 8); + emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size - 16); + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_size); + + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0); + } else { + /* trampoline called from function entry */ + emit_insn(ctx, ldd, LOONGARCH_GPR_T0, LOONGARCH_GPR_SP, stack_size - 8); + emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_size - 16); + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_size); + + emit_insn(ctx, ldd, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, 8); + emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, 0); + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, 16); + + if (flags & BPF_TRAMP_F_SKIP_FRAME) + /* return to parent function */ + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0); + else + /* return to traced function */ + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T0, 0); + } + + ret = ctx->idx; +out: + kfree(branches); + + return ret; +} + +int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, + void *ro_image_end, const struct btf_func_model *m, + u32 flags, struct bpf_tramp_links *tlinks, void *func_addr) +{ + int ret, size; + void *image, *tmp; + struct jit_ctx ctx; + + size = ro_image_end - ro_image; + image = kvmalloc(size, GFP_KERNEL); + if (!image) + return -ENOMEM; + + ctx.image = (union loongarch_instruction *)image; + ctx.ro_image = (union loongarch_instruction *)ro_image; + ctx.idx = 0; + + jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); + ret = __arch_prepare_bpf_trampoline(&ctx, im, m, tlinks, func_addr, flags); + if (ret > 0 && validate_code(&ctx) < 0) { + ret = -EINVAL; + goto out; + } + + tmp = bpf_arch_text_copy(ro_image, image, size); + if (IS_ERR(tmp)) { + ret = PTR_ERR(tmp); + goto out; + } + + bpf_flush_icache(ro_image, ro_image_end); +out: + kvfree(image); + return ret < 0 ? ret : size; +} + +int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, + struct bpf_tramp_links *tlinks, void *func_addr) +{ + int ret; + struct jit_ctx ctx; + struct bpf_tramp_image im; + + ctx.image = NULL; + ctx.idx = 0; + + ret = __arch_prepare_bpf_trampoline(&ctx, &im, m, tlinks, func_addr, flags); + + /* Page align */ + return ret < 0 ? ret : round_up(ret * LOONGARCH_INSN_SIZE, PAGE_SIZE); +} + struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) { bool tmp_blinded = false, extra_pass = false; @@ -1288,7 +1850,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) build_epilogue(&ctx); /* 3. Extra pass to validate JITed code */ - if (validate_code(&ctx)) { + if (validate_ctx(&ctx)) { bpf_jit_binary_free(header); prog = orig_prog; goto out_offset; @@ -1342,7 +1904,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) if (tmp_blinded) bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); - out_offset = -1; return prog; @@ -1354,6 +1915,16 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) goto out_offset; } +bool bpf_jit_bypass_spec_v1(void) +{ + return true; +} + +bool bpf_jit_bypass_spec_v4(void) +{ + return true; +} + /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ bool bpf_jit_supports_subprog_tailcalls(void) { diff --git a/arch/loongarch/net/bpf_jit.h b/arch/loongarch/net/bpf_jit.h index f9c569f53949..5697158fd164 100644 --- a/arch/loongarch/net/bpf_jit.h +++ b/arch/loongarch/net/bpf_jit.h @@ -18,6 +18,7 @@ struct jit_ctx { u32 *offset; int num_exentries; union loongarch_instruction *image; + union loongarch_instruction *ro_image; u32 stack_size; }; @@ -308,3 +309,8 @@ static inline int emit_tailcall_jmp(struct jit_ctx *ctx, u8 cond, enum loongarch return -EINVAL; } + +static inline void bpf_flush_icache(void *start, void *end) +{ + flush_icache_range((unsigned long)start, (unsigned long)end); +} diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c index 2726639150bc..5bc9627a6cf9 100644 --- a/arch/loongarch/pci/pci.c +++ b/arch/loongarch/pci/pci.c @@ -3,7 +3,6 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include -#include #include #include #include diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index ccd2c5e135c6..d8316f993482 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -36,7 +36,7 @@ endif # VDSO linker flags. ldflags-y := -Bsymbolic --no-undefined -soname=linux-vdso.so.1 \ - $(filter -E%,$(KBUILD_CFLAGS)) -nostdlib -shared --build-id -T + $(filter -E%,$(KBUILD_CFLAGS)) -shared --build-id -T # # Shared build commands. diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index eb5bb6d36899..11835eb59d94 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -32,6 +32,7 @@ config M68K select HAVE_ASM_MODVERSIONS select HAVE_DEBUG_BUGVERBOSE select HAVE_EFFICIENT_UNALIGNED_ACCESS if !CPU_HAS_NO_UNALIGNED + select HAVE_LD_DEAD_CODE_DATA_ELIMINATION select HAVE_MOD_ARCH_SPECIFIC select HAVE_UID16 select MMU_GATHER_NO_RANGE if MMU diff --git a/arch/m68k/Kconfig.debug b/arch/m68k/Kconfig.debug index 30638a6e8edc..d036f903864c 100644 --- a/arch/m68k/Kconfig.debug +++ b/arch/m68k/Kconfig.debug @@ -10,7 +10,7 @@ config BOOTPARAM_STRING config EARLY_PRINTK bool "Early printk" - depends on !(SUN3 || M68000 || COLDFIRE) + depends on MMU_MOTOROLA help Write kernel log output directly to a serial port. Where implemented, output goes to the framebuffer as well. diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 0147130dc34e..242d18e750b0 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -16,12 +16,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include diff --git a/arch/m68k/apollo/config.c b/arch/m68k/apollo/config.c index e161ecd76035..e324c5f671de 100644 --- a/arch/m68k/apollo/config.c +++ b/arch/m68k/apollo/config.c @@ -3,9 +3,7 @@ #include #include #include -#include #include -#include #include #include diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index b48a0606a000..ee2d061efb2a 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/arch/m68k/coldfire/gpio.c b/arch/m68k/coldfire/gpio.c index 30e5a4ed799d..e2f7af1facb2 100644 --- a/arch/m68k/coldfire/gpio.c +++ b/arch/m68k/coldfire/gpio.c @@ -160,7 +160,7 @@ static struct gpio_chip mcfgpio_chip = { .direction_input = mcfgpio_direction_input, .direction_output = mcfgpio_direction_output, .get = mcfgpio_get_value, - .set_rv = mcfgpio_set_value, + .set = mcfgpio_set_value, .to_irq = mcfgpio_to_irq, .base = 0, .ngpio = MCFGPIO_PIN_MAX, diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig index d05690289e33..5171bb183967 100644 --- a/arch/m68k/configs/amiga_defconfig +++ b/arch/m68k/configs/amiga_defconfig @@ -85,7 +85,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -267,6 +266,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -356,6 +356,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -375,6 +376,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_A2065=y CONFIG_ARIADNE=y @@ -448,8 +450,10 @@ CONFIG_RTC_DRV_RP5C01=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -548,6 +552,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -580,7 +585,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -600,6 +604,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -631,6 +636,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig index a1747fbe23fb..16f343ae48c6 100644 --- a/arch/m68k/configs/apollo_defconfig +++ b/arch/m68k/configs/apollo_defconfig @@ -81,7 +81,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -263,6 +262,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -336,6 +336,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -355,6 +356,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_PPP=m CONFIG_PPP_BSDCOMP=m @@ -405,8 +407,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -505,6 +509,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -537,7 +542,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -557,6 +561,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -588,6 +593,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig index 74293551f66b..c08788728ea9 100644 --- a/arch/m68k/configs/atari_defconfig +++ b/arch/m68k/configs/atari_defconfig @@ -88,7 +88,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -270,6 +269,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -351,6 +351,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -370,6 +371,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_ATARILANCE=y CONFIG_NE2000=y @@ -425,8 +427,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -525,6 +529,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -557,7 +562,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -577,6 +581,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -608,6 +613,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig index 419b13ae950a..962497e7c53f 100644 --- a/arch/m68k/configs/bvme6000_defconfig +++ b/arch/m68k/configs/bvme6000_defconfig @@ -78,7 +78,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -260,6 +259,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -334,6 +334,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -353,6 +354,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_BVME6000_NET=y CONFIG_PPP=m @@ -397,8 +399,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -497,6 +501,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -529,7 +534,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -549,6 +553,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -580,6 +585,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig index 4c81d756587c..ec28650189e4 100644 --- a/arch/m68k/configs/hp300_defconfig +++ b/arch/m68k/configs/hp300_defconfig @@ -80,7 +80,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -262,6 +261,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -335,6 +335,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -354,6 +355,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_HPLANCE=y CONFIG_PPP=m @@ -407,8 +409,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -507,6 +511,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -539,7 +544,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -559,6 +563,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -590,6 +595,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig index daa01d7fb462..0afb3ad180de 100644 --- a/arch/m68k/configs/mac_defconfig +++ b/arch/m68k/configs/mac_defconfig @@ -79,7 +79,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -261,6 +260,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -347,6 +347,7 @@ CONFIG_MAC_EMUMOUSEBTN=y CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -366,6 +367,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_MACMACE=y CONFIG_MAC89x0=y @@ -424,8 +426,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -524,6 +528,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -556,7 +561,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -576,6 +580,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -607,6 +612,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig index 641ca22eb3b2..b311e953995d 100644 --- a/arch/m68k/configs/multi_defconfig +++ b/arch/m68k/configs/multi_defconfig @@ -99,7 +99,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -281,6 +280,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -390,6 +390,7 @@ CONFIG_MAC_EMUMOUSEBTN=y CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -409,6 +410,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_A2065=y CONFIG_ARIADNE=y @@ -511,8 +513,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -611,6 +615,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -643,7 +648,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -663,6 +667,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -694,6 +699,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig index f98ffa7a1640..f4e6224f137f 100644 --- a/arch/m68k/configs/mvme147_defconfig +++ b/arch/m68k/configs/mvme147_defconfig @@ -77,7 +77,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -259,6 +258,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -333,6 +333,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -352,6 +353,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_MVME147_NET=y CONFIG_PPP=m @@ -397,8 +399,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -497,6 +501,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -529,7 +534,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -549,6 +553,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -580,6 +585,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig index 2bfc3f4b48f9..498e167222f1 100644 --- a/arch/m68k/configs/mvme16x_defconfig +++ b/arch/m68k/configs/mvme16x_defconfig @@ -78,7 +78,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -260,6 +259,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -334,6 +334,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -353,6 +354,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_MVME16x_NET=y CONFIG_PPP=m @@ -398,8 +400,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -498,6 +502,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -530,7 +535,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -550,6 +554,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -581,6 +586,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig index 2bd46cbcca2a..8c6b1eef8534 100644 --- a/arch/m68k/configs/q40_defconfig +++ b/arch/m68k/configs/q40_defconfig @@ -79,7 +79,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -261,6 +260,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -340,6 +340,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -359,6 +360,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_NE2000=y CONFIG_PLIP=m @@ -414,8 +416,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -514,6 +518,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -546,7 +551,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -566,6 +570,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -597,6 +602,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig index dc7fc94fc669..c34648f299ef 100644 --- a/arch/m68k/configs/sun3_defconfig +++ b/arch/m68k/configs/sun3_defconfig @@ -74,7 +74,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -256,6 +255,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -330,6 +330,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -349,6 +350,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_SUN3LANCE=y CONFIG_SUN3_82586=y @@ -395,8 +397,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -495,6 +499,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -527,7 +532,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -547,6 +551,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -577,6 +582,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig index b026a54867f5..73810d14660f 100644 --- a/arch/m68k/configs/sun3x_defconfig +++ b/arch/m68k/configs/sun3x_defconfig @@ -75,7 +75,6 @@ CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_HOOK=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_ZONES=y -# CONFIG_NF_CT_PROTO_DCCP is not set CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m @@ -257,6 +256,7 @@ CONFIG_BRIDGE_EBT_REDIRECT=m CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_SCTP=m CONFIG_SCTP_COOKIE_HMAC_SHA1=y CONFIG_RDS=m CONFIG_RDS_TCP=m @@ -331,6 +331,7 @@ CONFIG_TCM_PSCSI=m CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_WIREGUARD=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_NET_TEAM=m CONFIG_NET_TEAM_MODE_BROADCAST=m @@ -350,6 +351,7 @@ CONFIG_PFCP=m CONFIG_MACSEC=m CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m CONFIG_VETH=m CONFIG_SUN3LANCE=y CONFIG_PPP=m @@ -395,8 +397,10 @@ CONFIG_RTC_DRV_GENERIC=m CONFIG_DAX=m CONFIG_EXT4_FS=y CONFIG_JFS_FS=m +CONFIG_XFS_FS=m CONFIG_OCFS2_FS=m # CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m CONFIG_BCACHEFS_FS=m CONFIG_FANOTIFY=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -495,6 +499,7 @@ CONFIG_DLM=m CONFIG_ENCRYPTED_KEYS=m CONFIG_HARDENED_USERCOPY=y CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_RSA=m @@ -527,7 +532,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m @@ -547,6 +551,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m # CONFIG_CRYPTO_HW is not set +CONFIG_PRIME_NUMBERS=m CONFIG_XZ_DEC_TEST=m CONFIG_GLOB_SELFTEST=m # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set @@ -578,6 +583,7 @@ CONFIG_FIND_BIT_BENCHMARK=m CONFIG_TEST_FIRMWARE=m CONFIG_TEST_SYSCTL=m CONFIG_LINEAR_RANGES_TEST=m +CONFIG_CRC_BENCHMARK=y CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m CONFIG_TEST_KMOD=m diff --git a/arch/m68k/include/asm/adb_iop.h b/arch/m68k/include/asm/adb_iop.h index 6aecd020e2fc..ca10b1ec0c78 100644 --- a/arch/m68k/include/asm/adb_iop.h +++ b/arch/m68k/include/asm/adb_iop.h @@ -33,7 +33,7 @@ #define ADB_IOP_SRQ 0x04 /* SRQ detected */ #define ADB_IOP_TIMEOUT 0x02 /* nonzero if timeout */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct adb_iopmsg { __u8 flags; /* ADB flags */ @@ -43,4 +43,4 @@ struct adb_iopmsg { __u8 spare[21]; /* spare */ }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ diff --git a/arch/m68k/include/asm/bootinfo.h b/arch/m68k/include/asm/bootinfo.h index 81c91af8ec6c..267272b436e2 100644 --- a/arch/m68k/include/asm/bootinfo.h +++ b/arch/m68k/include/asm/bootinfo.h @@ -14,7 +14,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifdef CONFIG_BOOTINFO_PROC extern void save_bootinfo(const struct bi_record *bi); @@ -28,7 +28,7 @@ void process_uboot_commandline(char *commandp, int size); static inline void process_uboot_commandline(char *commandp, int size) {} #endif -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _M68K_BOOTINFO_H */ diff --git a/arch/m68k/include/asm/entry.h b/arch/m68k/include/asm/entry.h index 9b52b060c76a..86cba7c19e67 100644 --- a/arch/m68k/include/asm/entry.h +++ b/arch/m68k/include/asm/entry.h @@ -4,7 +4,7 @@ #include #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #include #endif @@ -41,7 +41,7 @@ #define ALLOWINT (~0x700) #endif /* machine compilation types */ -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ /* * This defines the normal kernel pt-regs layout. * diff --git a/arch/m68k/include/asm/kexec.h b/arch/m68k/include/asm/kexec.h index 3b0b64f0a353..f79427bd6487 100644 --- a/arch/m68k/include/asm/kexec.h +++ b/arch/m68k/include/asm/kexec.h @@ -15,7 +15,7 @@ #define KEXEC_ARCH KEXEC_ARCH_68K -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ static inline void crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs) @@ -23,7 +23,7 @@ static inline void crash_setup_regs(struct pt_regs *newregs, /* Dummy implementation for now */ } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* CONFIG_KEXEC_CORE */ diff --git a/arch/m68k/include/asm/mac_baboon.h b/arch/m68k/include/asm/mac_baboon.h index 08d9b8829a1a..ed5b5b48bdf8 100644 --- a/arch/m68k/include/asm/mac_baboon.h +++ b/arch/m68k/include/asm/mac_baboon.h @@ -5,7 +5,7 @@ #define BABOON_BASE (0x50F1A000) /* same as IDE controller base */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct baboon { char pad1[208]; /* generic IDE registers, not used here */ @@ -36,4 +36,4 @@ extern void baboon_register_interrupts(void); extern void baboon_irq_enable(int); extern void baboon_irq_disable(int); -#endif /* __ASSEMBLY **/ +#endif /* __ASSEMBLER__ */ diff --git a/arch/m68k/include/asm/mac_iop.h b/arch/m68k/include/asm/mac_iop.h index 32f1c79c818f..a6753eb16ba4 100644 --- a/arch/m68k/include/asm/mac_iop.h +++ b/arch/m68k/include/asm/mac_iop.h @@ -66,7 +66,7 @@ #define IOP_ADDR_ALIVE 0x031F #define IOP_ADDR_RECV_MSG 0x0320 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * IOP Control registers, staggered because in usual Apple style they were @@ -163,4 +163,4 @@ extern void iop_ism_irq_poll(uint); extern void iop_register_interrupts(void); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ diff --git a/arch/m68k/include/asm/mac_oss.h b/arch/m68k/include/asm/mac_oss.h index 56ef986c0a9b..a6e86e443155 100644 --- a/arch/m68k/include/asm/mac_oss.h +++ b/arch/m68k/include/asm/mac_oss.h @@ -59,7 +59,7 @@ #define OSS_POWEROFF 0x80 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct mac_oss { __u8 irq_level[0x10]; /* [0x000-0x00f] Interrupt levels */ @@ -77,4 +77,4 @@ extern void oss_register_interrupts(void); extern void oss_irq_enable(int); extern void oss_irq_disable(int); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ diff --git a/arch/m68k/include/asm/mac_psc.h b/arch/m68k/include/asm/mac_psc.h index 86a5a5eab89e..6587dbd54476 100644 --- a/arch/m68k/include/asm/mac_psc.h +++ b/arch/m68k/include/asm/mac_psc.h @@ -207,7 +207,7 @@ * Unknown, always 0x0000. */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern volatile __u8 *psc; @@ -249,4 +249,4 @@ static inline u32 psc_read_long(int offset) return *((volatile __u32 *)(psc + offset)); } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ diff --git a/arch/m68k/include/asm/mac_via.h b/arch/m68k/include/asm/mac_via.h index a9ef1e9ba6c4..b065cd8e5071 100644 --- a/arch/m68k/include/asm/mac_via.h +++ b/arch/m68k/include/asm/mac_via.h @@ -250,7 +250,7 @@ #define IER_SET_BIT(b) (0x80 | (1<<(b)) ) #define IER_CLR_BIT(b) (0x7F & (1<<(b)) ) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern volatile __u8 *via1,*via2; extern int rbv_present,via_alt_mapping; @@ -267,6 +267,6 @@ extern void via1_irq(struct irq_desc *desc); extern void via1_set_head(int); extern int via2_scsi_drq_pending(void); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_MAC_VIA_H_ */ diff --git a/arch/m68k/include/asm/math-emu.h b/arch/m68k/include/asm/math-emu.h index eefaa3a2b596..91074ade14ad 100644 --- a/arch/m68k/include/asm/math-emu.h +++ b/arch/m68k/include/asm/math-emu.h @@ -67,7 +67,7 @@ #define PMUNIMPL (1< #include @@ -127,7 +127,7 @@ extern unsigned int fp_debugprint; #define FPDATA ((struct fp_data *)current->thread.fp) -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ #define FPDATA %a2 @@ -311,6 +311,6 @@ old_gas=old_gas+1 .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_M68K_SETUP_H */ diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h index d79fef609194..189bb7b1e663 100644 --- a/arch/m68k/include/asm/mcf_pgtable.h +++ b/arch/m68k/include/asm/mcf_pgtable.h @@ -92,7 +92,7 @@ #define PTE_MASK PAGE_MASK #define CF_PAGE_CHG_MASK (PTE_MASK | CF_PAGE_ACCESSED | CF_PAGE_DIRTY) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define pmd_pgtable(pmd) pfn_to_virt(pmd_val(pmd) >> PAGE_SHIFT) @@ -292,5 +292,5 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) #define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) #define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _MCF_PGTABLE_H */ diff --git a/arch/m68k/include/asm/mcfmmu.h b/arch/m68k/include/asm/mcfmmu.h index 283352ab0d5d..db16ea1057f7 100644 --- a/arch/m68k/include/asm/mcfmmu.h +++ b/arch/m68k/include/asm/mcfmmu.h @@ -88,7 +88,7 @@ #define MMUDR_PAN 10 /* Physical address */ #define MMUDR_PAMASK 0xfffffc00 /* PA mask */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * Simple access functions for the MMU registers. Nothing fancy diff --git a/arch/m68k/include/asm/motorola_pgtable.h b/arch/m68k/include/asm/motorola_pgtable.h index 14fee64d3e60..dcf6829b3eab 100644 --- a/arch/m68k/include/asm/motorola_pgtable.h +++ b/arch/m68k/include/asm/motorola_pgtable.h @@ -44,7 +44,7 @@ /* We borrow bit 11 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE 0x800 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* This is the cache mode to be used for pages containing page descriptors for * processors >= '040. It is in pte_mknocache(), and the variable is defined @@ -202,5 +202,5 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) return pte; } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _MOTOROLA_PGTABLE_H */ diff --git a/arch/m68k/include/asm/nettel.h b/arch/m68k/include/asm/nettel.h index 3bd4b7a4613f..9bf55cef119e 100644 --- a/arch/m68k/include/asm/nettel.h +++ b/arch/m68k/include/asm/nettel.h @@ -38,7 +38,7 @@ #define NETtel_LEDADDR 0x30400000 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern volatile unsigned short ppdata; @@ -80,7 +80,7 @@ static __inline__ void mcf_setppdata(unsigned int mask, unsigned int bits) #define MCFPP_DTR0 0x0040 #define MCFPP_DTR1 0x0000 /* Port 1 no DTR support */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * These functions defined to give quasi generic access to the * PPIO bits used for DTR/DCD. diff --git a/arch/m68k/include/asm/openprom.h b/arch/m68k/include/asm/openprom.h index dd22e649f5c5..6456ba40a946 100644 --- a/arch/m68k/include/asm/openprom.h +++ b/arch/m68k/include/asm/openprom.h @@ -21,7 +21,7 @@ #define LINUX_OPPROM_MAGIC 0x10010407 #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* V0 prom device operations. */ struct linux_dev_v0_funcs { int (*v0_devopen)(char *device_str); @@ -308,6 +308,6 @@ struct linux_prom_ranges { unsigned int or_size; }; -#endif /* !(__ASSEMBLY__) */ +#endif /* !(__ASSEMBLER__) */ #endif /* !(__SPARC_OPENPROM_H) */ diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h index b173ba27d36f..d30f8b2f1592 100644 --- a/arch/m68k/include/asm/page.h +++ b/arch/m68k/include/asm/page.h @@ -10,7 +10,7 @@ #define PAGE_OFFSET (PAGE_OFFSET_RAW) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * These are used to make use of C type-checking.. @@ -48,7 +48,7 @@ extern unsigned long _rambase; extern unsigned long _ramstart; extern unsigned long _ramend; -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #ifdef CONFIG_MMU #include diff --git a/arch/m68k/include/asm/page_mm.h b/arch/m68k/include/asm/page_mm.h index e0ae4d5fc985..ed782609ca41 100644 --- a/arch/m68k/include/asm/page_mm.h +++ b/arch/m68k/include/asm/page_mm.h @@ -2,7 +2,7 @@ #ifndef _M68K_PAGE_MM_H #define _M68K_PAGE_MM_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -144,6 +144,6 @@ extern int m68k_virt_to_node_shift; #define virt_addr_valid(kaddr) ((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory) #define pfn_valid(pfn) virt_addr_valid(pfn_to_virt(pfn)) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _M68K_PAGE_MM_H */ diff --git a/arch/m68k/include/asm/page_no.h b/arch/m68k/include/asm/page_no.h index 63c0e706084b..39db2026a4b4 100644 --- a/arch/m68k/include/asm/page_no.h +++ b/arch/m68k/include/asm/page_no.h @@ -2,7 +2,7 @@ #ifndef _M68K_PAGE_NO_H #define _M68K_PAGE_NO_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long memory_start; extern unsigned long memory_end; @@ -37,6 +37,6 @@ static inline void *pfn_to_virt(unsigned long pfn) #define ARCH_PFN_OFFSET PHYS_PFN(PAGE_OFFSET_RAW) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _M68K_PAGE_NO_H */ diff --git a/arch/m68k/include/asm/pgtable.h b/arch/m68k/include/asm/pgtable.h index 49fcfd734860..02f1a4601379 100644 --- a/arch/m68k/include/asm/pgtable.h +++ b/arch/m68k/include/asm/pgtable.h @@ -10,7 +10,7 @@ #include #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern void paging_init(void); #endif diff --git a/arch/m68k/include/asm/pgtable_mm.h b/arch/m68k/include/asm/pgtable_mm.h index dbdf1c2b2f66..62f2ff4e6799 100644 --- a/arch/m68k/include/asm/pgtable_mm.h +++ b/arch/m68k/include/asm/pgtable_mm.h @@ -11,7 +11,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include #include @@ -145,7 +145,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf, #define update_mmu_cache(vma, addr, ptep) \ update_mmu_cache_range(NULL, vma, addr, ptep, 1) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ /* MMU-specific headers */ @@ -157,7 +157,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf, #include #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * Macro to mark a page protection value as "uncacheable". */ @@ -182,6 +182,6 @@ pgprot_t pgprot_dmacoherent(pgprot_t prot); #define pgprot_dmacoherent(prot) pgprot_dmacoherent(prot) #endif /* CONFIG_COLDFIRE */ -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _M68K_PGTABLE_H */ diff --git a/arch/m68k/include/asm/ptrace.h b/arch/m68k/include/asm/ptrace.h index ea5a80ca1ab3..bc86ce012025 100644 --- a/arch/m68k/include/asm/ptrace.h +++ b/arch/m68k/include/asm/ptrace.h @@ -4,7 +4,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifndef PS_S #define PS_S (0x2000) @@ -24,5 +24,5 @@ #define arch_has_block_step() (1) #endif -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _M68K_PTRACE_H */ diff --git a/arch/m68k/include/asm/setup.h b/arch/m68k/include/asm/setup.h index 2c99477aaf89..e4ec169f5c7d 100644 --- a/arch/m68k/include/asm/setup.h +++ b/arch/m68k/include/asm/setup.h @@ -28,9 +28,9 @@ #define CL_SIZE COMMAND_LINE_SIZE -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long m68k_machtype; -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #if !defined(CONFIG_AMIGA) # define MACH_IS_AMIGA (0) @@ -199,7 +199,7 @@ extern unsigned long m68k_machtype; #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long m68k_cputype; extern unsigned long m68k_fputype; extern unsigned long m68k_mmutype; @@ -213,7 +213,7 @@ extern unsigned long vme_brdtype; */ extern int m68k_is040or060; -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #if !defined(CONFIG_M68020) # define CPU_IS_020 (0) @@ -321,7 +321,7 @@ extern int m68k_is040or060; #define NUM_MEMINFO 4 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct m68k_mem_info { unsigned long addr; /* physical address of memory chunk */ unsigned long size; /* length of memory chunk (in bytes) */ diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h index 858cbe936f5b..80ca185a18a1 100644 --- a/arch/m68k/include/asm/sun3_pgtable.h +++ b/arch/m68k/include/asm/sun3_pgtable.h @@ -4,7 +4,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -19,7 +19,7 @@ #define PTOV(addr) __va(addr) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ /* These need to be defined for compatibility although the sun3 doesn't use them */ #define _PAGE_NOCACHE030 0x040 @@ -74,7 +74,7 @@ /* We borrow bit 6 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE 0x040 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { @@ -186,5 +186,5 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) return pte; } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* !_SUN3_PGTABLE_H */ diff --git a/arch/m68k/include/asm/sun3mmu.h b/arch/m68k/include/asm/sun3mmu.h index 21a75daa278f..fee05cd2ce5b 100644 --- a/arch/m68k/include/asm/sun3mmu.h +++ b/arch/m68k/include/asm/sun3mmu.h @@ -67,7 +67,7 @@ #define SUN3_BUSERR_PROTERR (0x40) #define SUN3_BUSERR_INVALID (0x80) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* Read bus error status register (implicitly clearing it). */ static inline unsigned char sun3_get_buserr(void) @@ -167,6 +167,6 @@ extern void __iomem *sun3_ioremap(unsigned long phys, unsigned long size, extern int sun3_map_test(unsigned long addr, char *val); -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* !__SUN3_MMU_H__ */ diff --git a/arch/m68k/include/asm/thread_info.h b/arch/m68k/include/asm/thread_info.h index 3e31adbddc75..5cb3ace55622 100644 --- a/arch/m68k/include/asm/thread_info.h +++ b/arch/m68k/include/asm/thread_info.h @@ -22,7 +22,7 @@ #define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct thread_info { struct task_struct *task; /* main task structure */ @@ -31,7 +31,7 @@ struct thread_info { __u32 cpu; /* should always be 0 on m68k */ unsigned long tp_value; /* thread pointer */ }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #define INIT_THREAD_INFO(tsk) \ { \ @@ -39,7 +39,7 @@ struct thread_info { .preempt_count = INIT_PREEMPT_COUNT, \ } -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { diff --git a/arch/m68k/include/asm/traps.h b/arch/m68k/include/asm/traps.h index a9d5c1c870d3..c7b3989bd4b2 100644 --- a/arch/m68k/include/asm/traps.h +++ b/arch/m68k/include/asm/traps.h @@ -11,7 +11,7 @@ #ifndef _M68K_TRAPS_H #define _M68K_TRAPS_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -94,7 +94,7 @@ asmlinkage void bad_inthandler(void); #define VECOFF(vec) ((vec)<<2) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* Status register bits */ #define PS_T (0x8000) @@ -271,6 +271,6 @@ struct frame { asmlinkage void berr_040cleanup(struct frame *fp); #endif -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _M68K_TRAPS_H */ diff --git a/arch/m68k/include/uapi/asm/bootinfo-vme.h b/arch/m68k/include/uapi/asm/bootinfo-vme.h index f36a09ab5e79..b8139eb39352 100644 --- a/arch/m68k/include/uapi/asm/bootinfo-vme.h +++ b/arch/m68k/include/uapi/asm/bootinfo-vme.h @@ -33,7 +33,7 @@ #define VME_TYPE_BVME6000 0x6000 /* BVM Ltd. BVME6000 */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * Board ID data structure - pointer to this retrieved from Bug by head.S @@ -56,7 +56,7 @@ typedef struct { __be32 option2; } t_bdid, *p_bdid; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* diff --git a/arch/m68k/include/uapi/asm/bootinfo.h b/arch/m68k/include/uapi/asm/bootinfo.h index 024e87d7095f..28d2d44c08d0 100644 --- a/arch/m68k/include/uapi/asm/bootinfo.h +++ b/arch/m68k/include/uapi/asm/bootinfo.h @@ -16,7 +16,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * Bootinfo definitions @@ -43,7 +43,7 @@ struct mem_info { __be32 size; /* length of memory chunk (in bytes) */ }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* @@ -167,7 +167,7 @@ struct mem_info { #define BI_VERSION_MAJOR(v) (((v) >> 16) & 0xffff) #define BI_VERSION_MINOR(v) ((v) & 0xffff) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct bootversion { __be16 branch; @@ -178,7 +178,7 @@ struct bootversion { } machversions[]; } __packed; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPI_ASM_M68K_BOOTINFO_H */ diff --git a/arch/m68k/include/uapi/asm/ptrace.h b/arch/m68k/include/uapi/asm/ptrace.h index ebd9fccb3d11..d70f771399b4 100644 --- a/arch/m68k/include/uapi/asm/ptrace.h +++ b/arch/m68k/include/uapi/asm/ptrace.h @@ -22,7 +22,7 @@ #define PT_SR 17 #define PT_PC 18 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* this struct defines the way the registers are stored on the stack during a system call. */ @@ -81,5 +81,5 @@ struct switch_stack { #define PTRACE_GETFDPIC_EXEC 0 #define PTRACE_GETFDPIC_INTERP 1 -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPI_M68K_PTRACE_H */ diff --git a/arch/m68k/kernel/early_printk.c b/arch/m68k/kernel/early_printk.c index f11ef9f1f56f..521cbb8a150c 100644 --- a/arch/m68k/kernel/early_printk.c +++ b/arch/m68k/kernel/early_printk.c @@ -16,25 +16,10 @@ #include "../mvme147/mvme147.h" #include "../mvme16x/mvme16x.h" -asmlinkage void __init debug_cons_nputs(const char *s, unsigned n); - -static void __ref debug_cons_write(struct console *c, - const char *s, unsigned n) -{ -#if !(defined(CONFIG_SUN3) || defined(CONFIG_M68000) || \ - defined(CONFIG_COLDFIRE)) - if (MACH_IS_MVME147) - mvme147_scc_write(c, s, n); - else if (MACH_IS_MVME16x) - mvme16x_cons_write(c, s, n); - else - debug_cons_nputs(s, n); -#endif -} +asmlinkage void __init debug_cons_nputs(struct console *c, const char *s, unsigned int n); static struct console early_console_instance = { .name = "debug", - .write = debug_cons_write, .flags = CON_PRINTBUFFER | CON_BOOT, .index = -1 }; @@ -44,6 +29,12 @@ static int __init setup_early_printk(char *buf) if (early_console || buf) return 0; + if (MACH_IS_MVME147) + early_console_instance.write = mvme147_scc_write; + else if (MACH_IS_MVME16x) + early_console_instance.write = mvme16x_cons_write; + else + early_console_instance.write = debug_cons_nputs; early_console = &early_console_instance; register_console(early_console); @@ -51,20 +42,15 @@ static int __init setup_early_printk(char *buf) } early_param("earlyprintk", setup_early_printk); -/* - * debug_cons_nputs() defined in arch/m68k/kernel/head.S cannot be called - * after init sections are discarded (for platforms that use it). - */ -#if !(defined(CONFIG_SUN3) || defined(CONFIG_M68000) || \ - defined(CONFIG_COLDFIRE)) - static int __init unregister_early_console(void) { - if (!early_console || MACH_IS_MVME16x) - return 0; + /* + * debug_cons_nputs() defined in arch/m68k/kernel/head.S cannot be + * called after init sections are discarded (for platforms that use it). + */ + if (early_console && early_console->write == debug_cons_nputs) + return unregister_console(early_console); - return unregister_console(early_console); + return 0; } late_initcall(unregister_early_console); - -#endif diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 852255cf60de..2e4ef0358887 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -3263,8 +3263,8 @@ func_return putn * turns around and calls the internal routines. This routine * is used by the boot console. * - * The calling parameters are: - * void debug_cons_nputs(const char *str, unsigned length) + * The function signature is - + * void debug_cons_nputs(struct console *c, const char *s, unsigned int n) * * This routine does NOT understand variable arguments only * simple strings! @@ -3273,8 +3273,8 @@ ENTRY(debug_cons_nputs) moveml %d0/%d1/%a0,%sp@- movew %sr,%sp@- ori #0x0700,%sr - movel %sp@(18),%a0 /* fetch parameter */ - movel %sp@(22),%d1 /* fetch parameter */ + movel %sp@(22),%a0 /* char *s */ + movel %sp@(26),%d1 /* unsigned int n */ jra 2f 1: #ifdef CONSOLE_DEBUG @@ -3400,6 +3400,7 @@ L(console_clear_loop): movel %d4,%d1 /* screen height in pixels */ divul %a0@(FONT_DESC_HEIGHT),%d1 /* d1 = max num rows */ + subql #1,%d1 /* row range is 0 to num - 1 */ movel %d0,%a2@(Lconsole_struct_num_columns) movel %d1,%a2@(Lconsole_struct_num_rows) @@ -3532,61 +3533,44 @@ func_start console_putc,%a0/%a1/%d0-%d7 tstl %pc@(L(console_font)) jeq L(console_exit) + lea %pc@(L(console_globals)),%a0 + /* Output character in d7 on console. */ movel ARG1,%d7 cmpib #'\n',%d7 - jbne 1f - - /* A little safe recursion is good for the soul */ - console_putc #'\r' -1: - lea %pc@(L(console_globals)),%a0 - - cmpib #10,%d7 jne L(console_not_lf) + + clrl %a0@(Lconsole_struct_cur_column) /* implicit \r */ + movel %a0@(Lconsole_struct_cur_row),%d0 - addil #1,%d0 - movel %d0,%a0@(Lconsole_struct_cur_row) movel %a0@(Lconsole_struct_num_rows),%d1 cmpl %d1,%d0 jcs 1f - subil #1,%d0 - movel %d0,%a0@(Lconsole_struct_cur_row) console_scroll + jra L(console_exit) 1: + addql #1,%d0 + movel %d0,%a0@(Lconsole_struct_cur_row) jra L(console_exit) L(console_not_lf): - cmpib #13,%d7 - jne L(console_not_cr) + cmpib #'\r',%d7 + jne L(console_not_lf_not_cr) clrl %a0@(Lconsole_struct_cur_column) jra L(console_exit) -L(console_not_cr): - cmpib #1,%d7 - jne L(console_not_home) - clrl %a0@(Lconsole_struct_cur_row) - clrl %a0@(Lconsole_struct_cur_column) - jra L(console_exit) - -/* - * At this point we know that the %d7 character is going to be - * rendered on the screen. Register usage is - - * a0 = pointer to console globals - * a1 = font data - * d0 = cursor column - * d1 = cursor row to draw the character - * d7 = character number - */ -L(console_not_home): + /* + * At this point we know that the %d7 character is going to be + * rendered on the screen. Register usage is - + * a0 = pointer to console globals + * a1 = font data + * d0 = cursor column + * d1 = cursor row to draw the character + * d7 = character number + */ +L(console_not_lf_not_cr): movel %a0@(Lconsole_struct_cur_column),%d0 - addql #1,%a0@(Lconsole_struct_cur_column) - movel %a0@(Lconsole_struct_num_columns),%d1 - cmpl %d1,%d0 - jcs 1f - console_putc #'\n' /* recursion is OK! */ -1: movel %a0@(Lconsole_struct_cur_row),%d1 /* @@ -3633,6 +3617,23 @@ L(console_do_font_scanline): addq #1,%d1 dbra %d7,L(console_read_char_scanline) + /* + * Register usage in the code below: + * a0 = pointer to console globals + * d0 = cursor column + * d1 = cursor column limit + */ + + lea %pc@(L(console_globals)),%a0 + + movel %a0@(Lconsole_struct_cur_column),%d0 + addql #1,%d0 + movel %d0,%a0@(Lconsole_struct_cur_column) /* Update cursor pos */ + movel %a0@(Lconsole_struct_num_columns),%d1 + cmpl %d1,%d0 + jcs L(console_exit) + console_putc #'\n' /* Line wrap using tail recursion */ + L(console_exit): func_return console_putc diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index c20d590e4297..cfa2df24eced 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -319,7 +319,7 @@ enum m68k_regset { static const struct user_regset m68k_user_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(u32), .align = sizeof(u16), @@ -327,7 +327,7 @@ static const struct user_regset m68k_user_regsets[] = { }, #ifdef CONFIG_FPU [REGSET_FPU] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_m68kfp_struct) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl index 9fe47112c586..f41d38dfbf13 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl @@ -467,3 +467,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index d26c7f4f8c36..c0033f885ed4 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include /* keyb */ @@ -23,7 +22,6 @@ #include /* keyb */ #include -#include #include #include #include diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c index 01e6b0e37f8d..9cb813eda4fd 100644 --- a/arch/m68k/mac/via.c +++ b/arch/m68k/mac/via.c @@ -621,6 +621,22 @@ static u64 mac_read_clk(struct clocksource *cs) * These problems are avoided by ignoring the low byte. Clock accuracy * is 256 times worse (error can reach 0.327 ms) but CPU overhead is * reduced by avoiding slow VIA register accesses. + * + * The VIA timer counter observably decrements to 0xFFFF before the + * counter reload interrupt gets raised. That complicates things a bit. + * + * State | vT1CH | VIA_TIMER_1_INT | inference drawn + * ------+------------+-----------------+----------------------------- + * i | FE thru 00 | false | counter is decrementing + * ii | FF | false | counter wrapped + * iii | FF | true | wrapped, interrupt raised + * iv | FF | false | wrapped, interrupt handled + * v | FE thru 00 | true | wrapped, interrupt unhandled + * + * State iv is never observed because handling the interrupt involves + * a 6522 register access and every access consumes a "phi 2" clock + * cycle. So 0xFF implies either state ii or state iii, depending on + * the value of the VIA_TIMER_1_INT bit. */ local_irq_save(flags); diff --git a/arch/m68k/math-emu/fp_emu.h b/arch/m68k/math-emu/fp_emu.h index c1ecfef7886a..6ac811c31ca4 100644 --- a/arch/m68k/math-emu/fp_emu.h +++ b/arch/m68k/math-emu/fp_emu.h @@ -38,12 +38,12 @@ #ifndef _FP_EMU_H #define _FP_EMU_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #include #endif #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define IS_INF(a) ((a)->exp == 0x7fff) #define IS_ZERO(a) ((a)->mant.m64 == 0) @@ -124,7 +124,7 @@ extern const struct fp_ext fp_Inf; : "a1", "d1", "d2", "memory"); \ }) -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ /* * set, reset or clear a bit in the fp status register @@ -141,6 +141,6 @@ extern const struct fp_ext fp_Inf; btst #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _FP_EMU_H */ diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 745bd575dcfa..62283bc2ed79 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -92,7 +92,7 @@ void mmu_page_dtor(void *page) } /* ++andreas: {get,free}_pointer_table rewritten to use unused fields from - struct page instead of separately kmalloced struct. Stolen from + struct ptdesc instead of separately kmalloced struct. Stolen from arch/sparc/mm/srmmu.c ... */ typedef struct list_head ptable_desc; @@ -103,8 +103,7 @@ static struct list_head ptable_list[3] = { LIST_HEAD_INIT(ptable_list[2]), }; -#define PD_PTABLE(page) ((ptable_desc *)&(virt_to_page((void *)(page))->lru)) -#define PD_PAGE(ptable) (list_entry(ptable, struct page, lru)) +#define PD_PTABLE(ptdesc) ((ptable_desc *)&(virt_to_ptdesc((void *)(ptdesc))->pt_list)) #define PD_PTDESC(ptable) (list_entry(ptable, struct ptdesc, pt_list)) #define PD_MARKBITS(dp) (*(unsigned int *)&PD_PTDESC(dp)->pt_index) @@ -121,10 +120,10 @@ void __init init_pointer_table(void *table, int type) { ptable_desc *dp; unsigned long ptable = (unsigned long)table; - unsigned long page = ptable & PAGE_MASK; - unsigned int mask = 1U << ((ptable - page)/ptable_size(type)); + unsigned long pt_addr = ptable & PAGE_MASK; + unsigned int mask = 1U << ((ptable - pt_addr)/ptable_size(type)); - dp = PD_PTABLE(page); + dp = PD_PTABLE(pt_addr); if (!(PD_MARKBITS(dp) & mask)) { PD_MARKBITS(dp) = ptable_mask(type); list_add(dp, &ptable_list[type]); @@ -133,9 +132,9 @@ void __init init_pointer_table(void *table, int type) PD_MARKBITS(dp) &= ~mask; pr_debug("init_pointer_table: %lx, %x\n", ptable, PD_MARKBITS(dp)); - /* unreserve the page so it's possible to free that page */ - __ClearPageReserved(PD_PAGE(dp)); - init_page_count(PD_PAGE(dp)); + /* unreserve the ptdesc so it's possible to free that ptdesc */ + __ClearPageReserved(ptdesc_page(PD_PTDESC(dp))); + init_page_count(ptdesc_page(PD_PTDESC(dp))); return; } @@ -148,40 +147,44 @@ void *get_pointer_table(struct mm_struct *mm, int type) /* * For a pointer table for a user process address space, a - * table is taken from a page allocated for the purpose. Each - * page can hold 8 pointer tables. The page is remapped in + * table is taken from a ptdesc allocated for the purpose. Each + * ptdesc can hold 8 pointer tables. The ptdesc is remapped in * virtual address space to be noncacheable. */ if (mask == 0) { - void *page; + struct ptdesc *ptdesc; ptable_desc *new; + void *pt_addr; - if (!(page = (void *)get_zeroed_page(GFP_KERNEL))) + ptdesc = pagetable_alloc(GFP_KERNEL | __GFP_ZERO, 0); + if (!ptdesc) return NULL; + pt_addr = ptdesc_address(ptdesc); + switch (type) { case TABLE_PTE: /* * m68k doesn't have SPLIT_PTE_PTLOCKS for not having * SMP. */ - pagetable_pte_ctor(mm, virt_to_ptdesc(page)); + pagetable_pte_ctor(mm, ptdesc); break; case TABLE_PMD: - pagetable_pmd_ctor(mm, virt_to_ptdesc(page)); + pagetable_pmd_ctor(mm, ptdesc); break; case TABLE_PGD: - pagetable_pgd_ctor(virt_to_ptdesc(page)); + pagetable_pgd_ctor(ptdesc); break; } - mmu_page_ctor(page); + mmu_page_ctor(pt_addr); - new = PD_PTABLE(page); + new = PD_PTABLE(pt_addr); PD_MARKBITS(new) = ptable_mask(type) - 1; list_add_tail(new, dp); - return (pmd_t *)page; + return (pmd_t *)pt_addr; } for (tmp = 1, off = 0; (mask & tmp) == 0; tmp <<= 1, off += ptable_size(type)) @@ -191,28 +194,27 @@ void *get_pointer_table(struct mm_struct *mm, int type) /* move to end of list */ list_move_tail(dp, &ptable_list[type]); } - return page_address(PD_PAGE(dp)) + off; + return ptdesc_address(PD_PTDESC(dp)) + off; } int free_pointer_table(void *table, int type) { ptable_desc *dp; unsigned long ptable = (unsigned long)table; - unsigned long page = ptable & PAGE_MASK; - unsigned int mask = 1U << ((ptable - page)/ptable_size(type)); + unsigned long pt_addr = ptable & PAGE_MASK; + unsigned int mask = 1U << ((ptable - pt_addr)/ptable_size(type)); - dp = PD_PTABLE(page); + dp = PD_PTABLE(pt_addr); if (PD_MARKBITS (dp) & mask) panic ("table already free!"); PD_MARKBITS (dp) |= mask; if (PD_MARKBITS(dp) == ptable_mask(type)) { - /* all tables in page are free, free page */ + /* all tables in ptdesc are free, free ptdesc */ list_del(dp); - mmu_page_dtor((void *)page); - pagetable_dtor(virt_to_ptdesc((void *)page)); - free_page (page); + mmu_page_dtor((void *)pt_addr); + pagetable_dtor_free(virt_to_ptdesc((void *)pt_addr)); return 1; } else if (ptable_list[type].next != dp) { /* diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index de7870ad2a30..5a4258697622 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -13,14 +13,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index f18ec02ddeb2..484ebb3baedf 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -28,7 +28,6 @@ config MICROBLAZE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_PAGE_SIZE_4KB diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl index 7b6e97828e55..580af574fe73 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl @@ -473,3 +473,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index ccb4b4b59bca..a2ab67b747a1 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -193,7 +193,7 @@ static struct timecounter xilinx_tc = { .cc = NULL, }; -static u64 xilinx_cc_read(const struct cyclecounter *cc) +static u64 xilinx_cc_read(struct cyclecounter *cc) { return xilinx_read(NULL); } diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 1e48184ecf1e..caf508f6e9ec 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -73,7 +73,6 @@ config MIPS select HAVE_EBPF_JIT if !CPU_MICROMIPS select HAVE_EXIT_THREAD select HAVE_GUP_FAST - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS @@ -563,6 +562,7 @@ config MIPS_MALTA select MIPS_L1_CACHE_SHIFT_6 select MIPS_MSC select PCI_GT64XXX_PCI0 + select RTC_MC146818_LIB select SMP_UP if SMP select SWAP_IO_SPACE select SYS_HAS_CPU_MIPS32_R1 @@ -1837,6 +1837,7 @@ config CPU_LOONGSON2EF select CPU_SUPPORTS_64BIT_KERNEL select CPU_SUPPORTS_HIGHMEM select CPU_SUPPORTS_HUGEPAGES + select RTC_MC146818_LIB config CPU_LOONGSON32 bool @@ -2024,7 +2025,6 @@ config CPU_MIPSR5 config CPU_MIPSR6 bool default y if CPU_MIPS32_R6 || CPU_MIPS64_R6 - select ARCH_HAS_CRC32 select CPU_HAS_RIXI select CPU_HAS_DIEI if !CPU_DIEI_BROKEN select HAVE_ARCH_BITREVERSE diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c index 411f70ceb762..e79e26ffac99 100644 --- a/arch/mips/alchemy/common/gpiolib.c +++ b/arch/mips/alchemy/common/gpiolib.c @@ -40,9 +40,11 @@ static int gpio2_get(struct gpio_chip *chip, unsigned offset) return !!alchemy_gpio2_get_value(offset + ALCHEMY_GPIO2_BASE); } -static void gpio2_set(struct gpio_chip *chip, unsigned offset, int value) +static int gpio2_set(struct gpio_chip *chip, unsigned offset, int value) { alchemy_gpio2_set_value(offset + ALCHEMY_GPIO2_BASE, value); + + return 0; } static int gpio2_direction_input(struct gpio_chip *chip, unsigned offset) @@ -68,10 +70,12 @@ static int gpio1_get(struct gpio_chip *chip, unsigned offset) return !!alchemy_gpio1_get_value(offset + ALCHEMY_GPIO1_BASE); } -static void gpio1_set(struct gpio_chip *chip, +static int gpio1_set(struct gpio_chip *chip, unsigned offset, int value) { alchemy_gpio1_set_value(offset + ALCHEMY_GPIO1_BASE, value); + + return 0; } static int gpio1_direction_input(struct gpio_chip *chip, unsigned offset) @@ -147,7 +151,7 @@ static struct gpio_chip au1300_gpiochip = { .direction_input = alchemy_gpic_dir_input, .direction_output = alchemy_gpic_dir_output, .get = alchemy_gpic_get, - .set_rv = alchemy_gpic_set, + .set = alchemy_gpic_set, .to_irq = alchemy_gpic_gpio_to_irq, .base = AU1300_GPIO_BASE, .ngpio = AU1300_GPIO_NUM, diff --git a/arch/mips/bcm63xx/gpio.c b/arch/mips/bcm63xx/gpio.c index e7a53cd0dec5..ff45a6989c3a 100644 --- a/arch/mips/bcm63xx/gpio.c +++ b/arch/mips/bcm63xx/gpio.c @@ -131,7 +131,7 @@ static struct gpio_chip bcm63xx_gpio_chip = { .direction_input = bcm63xx_gpio_direction_input, .direction_output = bcm63xx_gpio_direction_output, .get = bcm63xx_gpio_get, - .set_rv = bcm63xx_gpio_set, + .set = bcm63xx_gpio_set, .base = 0, }; diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile index 196c44fa72d9..8473c4671702 100644 --- a/arch/mips/boot/Makefile +++ b/arch/mips/boot/Makefile @@ -54,10 +54,10 @@ UIMAGE_ENTRYADDR = $(VMLINUX_ENTRY_ADDRESS) # Compressed vmlinux images # -extra-y += vmlinux.bin.bz2 -extra-y += vmlinux.bin.gz -extra-y += vmlinux.bin.lzma -extra-y += vmlinux.bin.lzo +targets += vmlinux.bin.bz2 +targets += vmlinux.bin.gz +targets += vmlinux.bin.lzma +targets += vmlinux.bin.lzo $(obj)/vmlinux.bin.bz2: $(obj)/vmlinux.bin FORCE $(call if_changed,bzip2) diff --git a/arch/mips/boot/dts/lantiq/danube_easy50712.dts b/arch/mips/boot/dts/lantiq/danube_easy50712.dts index 1ce20b7d05cb..c4d7aa5753b0 100644 --- a/arch/mips/boot/dts/lantiq/danube_easy50712.dts +++ b/arch/mips/boot/dts/lantiq/danube_easy50712.dts @@ -82,13 +82,16 @@ conf_out { }; }; - etop@e180000 { + ethernet@e180000 { compatible = "lantiq,etop-xway"; reg = <0xe180000 0x40000>; interrupt-parent = <&icu0>; interrupts = <73 78>; + interrupt-names = "tx", "rx"; phy-mode = "rmii"; mac-address = [ 00 11 22 33 44 55 ]; + lantiq,rx-burst-length = <4>; + lantiq,tx-burst-length = <4>; }; stp0: stp@e100bb0 { diff --git a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts index 6898b2d8267d..9fc1a1b0a81b 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts +++ b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts @@ -21,3 +21,11 @@ memory@0 { <0x8 0x02000000 0x0 0x7E000000>; }; }; + +&i2c2 { + temperature-sensor@48 { + compatible = "ti,tmp112"; + reg = <0x48>; + label = "U60"; + }; +}; diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi index a84e6e720619..36a73e8a63a1 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi @@ -110,6 +110,81 @@ soc: soc { ranges; compatible = "simple-bus"; + i2c0: i2c@300000 { + compatible = "mobileye,eyeq5-i2c", "arm,primecell"; + reg = <0 0x300000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; /* Fast mode */ + #address-cells = <1>; + #size-cells = <0>; + clocks = <&olb 35>, <&olb EQ5C_PER_I2C>; + clock-names = "i2cclk", "apb_pclk"; + resets = <&olb 0 13>; + i2c-transfer-timeout-us = <10000>; + mobileye,olb = <&olb 0>; + }; + + i2c1: i2c@400000 { + compatible = "mobileye,eyeq5-i2c", "arm,primecell"; + reg = <0 0x400000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; /* Fast mode */ + #address-cells = <1>; + #size-cells = <0>; + clocks = <&olb 35>, <&olb EQ5C_PER_I2C>; + clock-names = "i2cclk", "apb_pclk"; + resets = <&olb 0 14>; + i2c-transfer-timeout-us = <10000>; + mobileye,olb = <&olb 1>; + }; + + i2c2: i2c@500000 { + compatible = "mobileye,eyeq5-i2c", "arm,primecell"; + reg = <0 0x500000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; /* Fast mode */ + #address-cells = <1>; + #size-cells = <0>; + clocks = <&olb 35>, <&olb EQ5C_PER_I2C>; + clock-names = "i2cclk", "apb_pclk"; + resets = <&olb 0 15>; + i2c-transfer-timeout-us = <10000>; + mobileye,olb = <&olb 2>; + }; + + i2c3: i2c@600000 { + compatible = "mobileye,eyeq5-i2c", "arm,primecell"; + reg = <0 0x600000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; /* Fast mode */ + #address-cells = <1>; + #size-cells = <0>; + clocks = <&olb 35>, <&olb EQ5C_PER_I2C>; + clock-names = "i2cclk", "apb_pclk"; + resets = <&olb 0 16>; + i2c-transfer-timeout-us = <10000>; + mobileye,olb = <&olb 3>; + }; + + i2c4: i2c@700000 { + compatible = "mobileye,eyeq5-i2c", "arm,primecell"; + reg = <0 0x700000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; /* Fast mode */ + #address-cells = <1>; + #size-cells = <0>; + clocks = <&olb 35>, <&olb EQ5C_PER_I2C>; + clock-names = "i2cclk", "apb_pclk"; + resets = <&olb 0 17>; + i2c-transfer-timeout-us = <10000>; + mobileye,olb = <&olb 4>; + }; + uart0: serial@800000 { compatible = "arm,pl011", "arm,primecell"; reg = <0 0x800000 0x0 0x1000>; @@ -178,6 +253,58 @@ timer { clocks = <&olb EQ5C_CPU_CORE0>; }; }; + + emmc: mmc@2200000 { + compatible = "mobileye,eyeq-sd4hc", "cdns,sd4hc"; + reg = <0 0x2200000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clocks = <&olb EQ5C_PER_EMMC>; + bus-width = <8>; + max-frequency = <200000000>; + mmc-ddr-1_8v; + sd-uhs-ddr50; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + + cdns,phy-input-delay-legacy = <4>; + cdns,phy-input-delay-mmc-highspeed = <2>; + cdns,phy-input-delay-mmc-ddr = <3>; + cdns,phy-dll-delay-sdclk = <32>; + cdns,phy-dll-delay-sdclk-hsmmc = <32>; + cdns,phy-dll-delay-strobe = <32>; + }; + + gpio0: gpio@1400000 { + compatible = "mobileye,eyeq5-gpio"; + reg = <0x0 0x1400000 0x0 0x1000>; + gpio-bank = <0>; + ngpios = <29>; + interrupt-parent = <&gic>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&olb 0 0 29>; + interrupt-controller; + #interrupt-cells = <2>; + resets = <&olb 0 26>; + }; + + gpio1: gpio@1500000 { + compatible = "mobileye,eyeq5-gpio"; + reg = <0x0 0x1500000 0x0 0x1000>; + gpio-bank = <1>; + ngpios = <23>; + interrupt-parent = <&gic>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&olb 0 29 23>; + interrupt-controller; + #interrupt-cells = <2>; + resets = <&olb 0 26>; + }; }; }; diff --git a/arch/mips/boot/dts/mobileye/eyeq6h.dtsi b/arch/mips/boot/dts/mobileye/eyeq6h.dtsi index dabd5ed778b7..5ae939d25ea8 100644 --- a/arch/mips/boot/dts/mobileye/eyeq6h.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq6h.dtsi @@ -109,6 +109,28 @@ olb_east: system-controller@d3358000 { clock-names = "ref"; }; + emmc: mmc@d8010000 { + compatible = "mobileye,eyeq-sd4hc", "cdns,sd4hc"; + reg = <0 0xd8010000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clocks = <&olb_south EQ6HC_SOUTH_DIV_EMMC>; + bus-width = <8>; + max-frequency = <200000000>; + mmc-ddr-1_8v; + sd-uhs-ddr50; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + + cdns,phy-input-delay-legacy = <4>; + cdns,phy-input-delay-mmc-highspeed = <2>; + cdns,phy-input-delay-mmc-ddr = <3>; + cdns,phy-dll-delay-sdclk = <32>; + cdns,phy-dll-delay-sdclk-hsmmc = <32>; + cdns,phy-dll-delay-strobe = <32>; + }; + olb_south: system-controller@d8013000 { compatible = "mobileye,eyeq6h-south-olb", "syscon"; reg = <0x0 0xd8013000 0x0 0x1000>; diff --git a/arch/mips/boot/dts/qca/ar9132.dtsi b/arch/mips/boot/dts/qca/ar9132.dtsi index 61dcfa5b6ca7..c1ca03a27b6c 100644 --- a/arch/mips/boot/dts/qca/ar9132.dtsi +++ b/arch/mips/boot/dts/qca/ar9132.dtsi @@ -156,6 +156,15 @@ spi: spi@1f000000 { #address-cells = <1>; #size-cells = <0>; }; + + wifi: wifi@180c0000 { + compatible = "qca,ar9130-wifi"; + reg = <0x180c0000 0x230000>; + + interrupts = <2>; + + status = "disabled"; + }; }; usb_phy: usb-phy { diff --git a/arch/mips/boot/dts/qca/ar9132_tl_wr1043nd_v1.dts b/arch/mips/boot/dts/qca/ar9132_tl_wr1043nd_v1.dts index f894fe17816b..a7901bb040ce 100644 --- a/arch/mips/boot/dts/qca/ar9132_tl_wr1043nd_v1.dts +++ b/arch/mips/boot/dts/qca/ar9132_tl_wr1043nd_v1.dts @@ -108,3 +108,7 @@ partition@2 { }; }; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/qca/ar9331.dtsi b/arch/mips/boot/dts/qca/ar9331.dtsi index 768ac0f869b1..6eb84a26a20f 100644 --- a/arch/mips/boot/dts/qca/ar9331.dtsi +++ b/arch/mips/boot/dts/qca/ar9331.dtsi @@ -285,6 +285,15 @@ spi: spi@1f000000 { status = "disabled"; }; + + wifi: wifi@18100000 { + compatible = "qca,ar9330-wifi"; + reg = <0x18100000 0x20000>; + + interrupts = <2>; + + status = "disabled"; + }; }; usb_phy: usb-phy { diff --git a/arch/mips/boot/dts/qca/ar9331_dpt_module.dts b/arch/mips/boot/dts/qca/ar9331_dpt_module.dts index c857cd22f7db..08e728b8ced8 100644 --- a/arch/mips/boot/dts/qca/ar9331_dpt_module.dts +++ b/arch/mips/boot/dts/qca/ar9331_dpt_module.dts @@ -97,3 +97,7 @@ &phy_port0 { &phy_port4 { status = "okay"; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/qca/ar9331_dragino_ms14.dts b/arch/mips/boot/dts/qca/ar9331_dragino_ms14.dts index 7affa58d4fa6..37a74aabe4b4 100644 --- a/arch/mips/boot/dts/qca/ar9331_dragino_ms14.dts +++ b/arch/mips/boot/dts/qca/ar9331_dragino_ms14.dts @@ -98,3 +98,7 @@ spiflash: w25q128@0 { reg = <0>; }; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/qca/ar9331_omega.dts b/arch/mips/boot/dts/qca/ar9331_omega.dts index 8904aa917a6e..1450419024cb 100644 --- a/arch/mips/boot/dts/qca/ar9331_omega.dts +++ b/arch/mips/boot/dts/qca/ar9331_omega.dts @@ -74,3 +74,7 @@ spiflash: w25q128@0 { reg = <0>; }; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/qca/ar9331_openembed_som9331_board.dts b/arch/mips/boot/dts/qca/ar9331_openembed_som9331_board.dts index dc65ebd60bbc..5786a827c000 100644 --- a/arch/mips/boot/dts/qca/ar9331_openembed_som9331_board.dts +++ b/arch/mips/boot/dts/qca/ar9331_openembed_som9331_board.dts @@ -106,3 +106,7 @@ &phy_port2 { &phy_port4 { status = "okay"; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/qca/ar9331_tl_mr3020.dts b/arch/mips/boot/dts/qca/ar9331_tl_mr3020.dts index 10b9759228b7..a7108c803eb3 100644 --- a/arch/mips/boot/dts/qca/ar9331_tl_mr3020.dts +++ b/arch/mips/boot/dts/qca/ar9331_tl_mr3020.dts @@ -114,3 +114,7 @@ spiflash: s25sl032p@0 { reg = <0>; }; }; + +&wifi { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts b/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts index 7743d014631a..0bfb1dde9764 100644 --- a/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts +++ b/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts @@ -56,7 +56,7 @@ led-power-blue { led-power-green { label = "smartgw:power:green"; gpios = <&gpio 19 GPIO_ACTIVE_HIGH>; - default-state = "off"; + linux,default-trigger = "timer"; }; led-power-red { diff --git a/arch/mips/boot/dts/ralink/mt7620a.dtsi b/arch/mips/boot/dts/ralink/mt7620a.dtsi index d66045948a83..460164bdd430 100644 --- a/arch/mips/boot/dts/ralink/mt7620a.dtsi +++ b/arch/mips/boot/dts/ralink/mt7620a.dtsi @@ -62,4 +62,14 @@ uartlite@c00 { reg-shift = <2>; }; }; + + wmac: wifi@10180000 { + compatible = "ralink,rt2880-wifi"; + reg = <0x10180000 0x40000>; + + clocks = <&sysc 16>; + + interrupt-parent = <&cpuintc>; + interrupts = <6>; + }; }; diff --git a/arch/mips/boot/dts/ralink/mt7628a.dtsi b/arch/mips/boot/dts/ralink/mt7628a.dtsi index 0212700c4fb4..5d7a6cfa9e2b 100644 --- a/arch/mips/boot/dts/ralink/mt7628a.dtsi +++ b/arch/mips/boot/dts/ralink/mt7628a.dtsi @@ -33,7 +33,7 @@ palmbus@10000000 { #size-cells = <1>; sysc: syscon@0 { - compatible = "ralink,mt7628-sysc", "syscon"; + compatible = "ralink,mt7628-sysc", "ralink,mt7688-sysc", "syscon"; reg = <0x0 0x60>; #clock-cells = <1>; #reset-cells = <1>; @@ -134,13 +134,8 @@ pinmux_p4led_an_gpio: p4led-an-gpio-pins { watchdog: watchdog@100 { compatible = "mediatek,mt7621-wdt"; - reg = <0x100 0x30>; - - resets = <&sysc 8>; - reset-names = "wdt"; - - interrupt-parent = <&intc>; - interrupts = <24>; + reg = <0x100 0x100>; + mediatek,sysctl = <&sysc>; status = "disabled"; }; diff --git a/arch/mips/boot/dts/realtek/cameo-rtl9302c-2x-rtl8224-2xge.dts b/arch/mips/boot/dts/realtek/cameo-rtl9302c-2x-rtl8224-2xge.dts index 6789bf374044..6f6a05d4088e 100644 --- a/arch/mips/boot/dts/realtek/cameo-rtl9302c-2x-rtl8224-2xge.dts +++ b/arch/mips/boot/dts/realtek/cameo-rtl9302c-2x-rtl8224-2xge.dts @@ -71,3 +71,99 @@ partition@1180000 { }; }; }; + +&mdio0 { + /* External RTL8224 */ + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy1: ethernet-phy@1 { + reg = <1>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy2: ethernet-phy@2 { + reg = <2>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy3: ethernet-phy@3 { + reg = <3>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; +}; + +&mdio1 { + /* External RTL8224 */ + phy4: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy5: ethernet-phy@1 { + reg = <1>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy6: ethernet-phy@2 { + reg = <2>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + phy7: ethernet-phy@3 { + reg = <3>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; +}; + +&switch0 { + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + phy-handle = <&phy0>; + phy-mode = "usxgmii"; + }; + port@1 { + reg = <1>; + phy-handle = <&phy1>; + phy-mode = "usxgmii"; + }; + port@2 { + reg = <2>; + phy-handle = <&phy2>; + phy-mode = "usxgmii"; + }; + port@3 { + reg = <3>; + phy-handle = <&phy3>; + phy-mode = "usxgmii"; + }; + port@16 { + reg = <16>; + phy-handle = <&phy4>; + phy-mode = "usxgmii"; + }; + port@17 { + reg = <17>; + phy-handle = <&phy5>; + phy-mode = "usxgmii"; + }; + port@18 { + reg = <18>; + phy-handle = <&phy6>; + phy-mode = "usxgmii"; + }; + port@19 { + reg = <19>; + phy-handle = <&phy7>; + phy-mode = "usxgmii"; + }; + port@24{ + reg = <24>; + phy-mode = "10gbase-r"; + }; + port@25{ + reg = <25>; + phy-mode = "10gbase-r"; + }; + }; +}; diff --git a/arch/mips/boot/dts/realtek/rtl930x.dtsi b/arch/mips/boot/dts/realtek/rtl930x.dtsi index 101bab72a95f..24e262e2dc2a 100644 --- a/arch/mips/boot/dts/realtek/rtl930x.dtsi +++ b/arch/mips/boot/dts/realtek/rtl930x.dtsi @@ -48,6 +48,10 @@ switch0: switch@1b000000 { #address-cells = <1>; #size-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <23>, <24>; + interrupt-names = "switch", "nic"; + reboot@c { compatible = "syscon-reboot"; reg = <0x0c 0x4>; @@ -138,6 +142,33 @@ timer0: timer@3200 { clocks = <&lx_clk>; }; + watchdog0: watchdog@3260 { + compatible = "realtek,rtl9300-wdt"; + reg = <0x3260 0xc>; + + realtek,reset-mode = "soc"; + + clocks = <&lx_clk>; + timeout-sec = <30>; + + interrupt-parent = <&intc>; + interrupt-names = "phase1", "phase2"; + interrupts = <5>, <6>; + }; + + gpio0: gpio@3300 { + compatible = "realtek,rtl9300-gpio", "realtek,otto-gpio"; + reg = <0x3300 0x1c>, <0x3338 0x8>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <13>; + }; + snand: spi@1a400 { compatible = "realtek,rtl9301-snand"; reg = <0x1a400 0x44>; diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig index 11f4aa6e80e9..450e979ef5d9 100644 --- a/arch/mips/cavium-octeon/Kconfig +++ b/arch/mips/cavium-octeon/Kconfig @@ -23,12 +23,6 @@ config CAVIUM_OCTEON_CVMSEG_SIZE legally range is from zero to 54 cache blocks (i.e. CVMSEG LM is between zero and 6192 bytes). -config CRYPTO_SHA256_OCTEON - tristate - default CRYPTO_LIB_SHA256 - select CRYPTO_ARCH_HAVE_LIB_SHA256 - select CRYPTO_LIB_SHA256_GENERIC - endif # CPU_CAVIUM_OCTEON if CAVIUM_OCTEON_SOC diff --git a/arch/mips/cavium-octeon/crypto/Makefile b/arch/mips/cavium-octeon/crypto/Makefile index db26c73fa0ed..83f2f5dd93cc 100644 --- a/arch/mips/cavium-octeon/crypto/Makefile +++ b/arch/mips/cavium-octeon/crypto/Makefile @@ -6,6 +6,3 @@ obj-y += octeon-crypto.o obj-$(CONFIG_CRYPTO_MD5_OCTEON) += octeon-md5.o -obj-$(CONFIG_CRYPTO_SHA1_OCTEON) += octeon-sha1.o -obj-$(CONFIG_CRYPTO_SHA256_OCTEON) += octeon-sha256.o -obj-$(CONFIG_CRYPTO_SHA512_OCTEON) += octeon-sha512.o diff --git a/arch/mips/cavium-octeon/crypto/octeon-crypto.c b/arch/mips/cavium-octeon/crypto/octeon-crypto.c index cfb4a146cf17..0ff8559391f5 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-crypto.c +++ b/arch/mips/cavium-octeon/crypto/octeon-crypto.c @@ -7,12 +7,11 @@ */ #include +#include #include #include #include -#include "octeon-crypto.h" - /** * Enable access to Octeon's COP2 crypto hardware for kernel use. Wrap any * crypto operations in calls to octeon_crypto_enable/disable in order to make diff --git a/arch/mips/cavium-octeon/crypto/octeon-md5.c b/arch/mips/cavium-octeon/crypto/octeon-md5.c index fbc84eb7fedf..a8ce831e2ceb 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-md5.c +++ b/arch/mips/cavium-octeon/crypto/octeon-md5.c @@ -19,6 +19,7 @@ * any later version. */ +#include #include #include #include @@ -27,8 +28,6 @@ #include #include -#include "octeon-crypto.h" - struct octeon_md5_state { __le32 hash[MD5_HASH_WORDS]; u64 byte_count; diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha1.c b/arch/mips/cavium-octeon/crypto/octeon-sha1.c deleted file mode 100644 index e70f21a473da..000000000000 --- a/arch/mips/cavium-octeon/crypto/octeon-sha1.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * - * SHA1 Secure Hash Algorithm. - * - * Adapted for OCTEON by Aaro Koskinen . - * - * Based on crypto/sha1_generic.c, which is: - * - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "octeon-crypto.h" - -/* - * We pass everything as 64-bit. OCTEON can handle misaligned data. - */ - -static void octeon_sha1_store_hash(struct sha1_state *sctx) -{ - u64 *hash = (u64 *)sctx->state; - union { - u32 word[2]; - u64 dword; - } hash_tail = { { sctx->state[4], } }; - - write_octeon_64bit_hash_dword(hash[0], 0); - write_octeon_64bit_hash_dword(hash[1], 1); - write_octeon_64bit_hash_dword(hash_tail.dword, 2); - memzero_explicit(&hash_tail.word[0], sizeof(hash_tail.word[0])); -} - -static void octeon_sha1_read_hash(struct sha1_state *sctx) -{ - u64 *hash = (u64 *)sctx->state; - union { - u32 word[2]; - u64 dword; - } hash_tail; - - hash[0] = read_octeon_64bit_hash_dword(0); - hash[1] = read_octeon_64bit_hash_dword(1); - hash_tail.dword = read_octeon_64bit_hash_dword(2); - sctx->state[4] = hash_tail.word[0]; - memzero_explicit(&hash_tail.dword, sizeof(hash_tail.dword)); -} - -static void octeon_sha1_transform(struct sha1_state *sctx, const u8 *src, - int blocks) -{ - do { - const u64 *block = (const u64 *)src; - - write_octeon_64bit_block_dword(block[0], 0); - write_octeon_64bit_block_dword(block[1], 1); - write_octeon_64bit_block_dword(block[2], 2); - write_octeon_64bit_block_dword(block[3], 3); - write_octeon_64bit_block_dword(block[4], 4); - write_octeon_64bit_block_dword(block[5], 5); - write_octeon_64bit_block_dword(block[6], 6); - octeon_sha1_start(block[7]); - - src += SHA1_BLOCK_SIZE; - } while (--blocks); -} - -static int octeon_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - struct octeon_cop2_state state; - unsigned long flags; - int remain; - - flags = octeon_crypto_enable(&state); - octeon_sha1_store_hash(sctx); - - remain = sha1_base_do_update_blocks(desc, data, len, - octeon_sha1_transform); - - octeon_sha1_read_hash(sctx); - octeon_crypto_disable(&state, flags); - return remain; -} - -static int octeon_sha1_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *out) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - struct octeon_cop2_state state; - unsigned long flags; - - flags = octeon_crypto_enable(&state); - octeon_sha1_store_hash(sctx); - - sha1_base_do_finup(desc, src, len, octeon_sha1_transform); - - octeon_sha1_read_hash(sctx); - octeon_crypto_disable(&state, flags); - return sha1_base_finish(desc, out); -} - -static struct shash_alg octeon_sha1_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = octeon_sha1_update, - .finup = octeon_sha1_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "octeon-sha1", - .cra_priority = OCTEON_CR_OPCODE_PRIORITY, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init octeon_sha1_mod_init(void) -{ - if (!octeon_has_crypto()) - return -ENOTSUPP; - return crypto_register_shash(&octeon_sha1_alg); -} - -static void __exit octeon_sha1_mod_fini(void) -{ - crypto_unregister_shash(&octeon_sha1_alg); -} - -module_init(octeon_sha1_mod_init); -module_exit(octeon_sha1_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm (OCTEON)"); -MODULE_AUTHOR("Aaro Koskinen "); diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha512.c b/arch/mips/cavium-octeon/crypto/octeon-sha512.c deleted file mode 100644 index 215311053db3..000000000000 --- a/arch/mips/cavium-octeon/crypto/octeon-sha512.c +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * - * SHA-512 and SHA-384 Secure Hash Algorithm. - * - * Adapted for OCTEON by Aaro Koskinen . - * - * Based on crypto/sha512_generic.c, which is: - * - * Copyright (c) Jean-Luc Cooke - * Copyright (c) Andrew McDonald - * Copyright (c) 2003 Kyle McMartin - */ - -#include -#include -#include -#include -#include -#include - -#include "octeon-crypto.h" - -/* - * We pass everything as 64-bit. OCTEON can handle misaligned data. - */ - -static void octeon_sha512_store_hash(struct sha512_state *sctx) -{ - write_octeon_64bit_hash_sha512(sctx->state[0], 0); - write_octeon_64bit_hash_sha512(sctx->state[1], 1); - write_octeon_64bit_hash_sha512(sctx->state[2], 2); - write_octeon_64bit_hash_sha512(sctx->state[3], 3); - write_octeon_64bit_hash_sha512(sctx->state[4], 4); - write_octeon_64bit_hash_sha512(sctx->state[5], 5); - write_octeon_64bit_hash_sha512(sctx->state[6], 6); - write_octeon_64bit_hash_sha512(sctx->state[7], 7); -} - -static void octeon_sha512_read_hash(struct sha512_state *sctx) -{ - sctx->state[0] = read_octeon_64bit_hash_sha512(0); - sctx->state[1] = read_octeon_64bit_hash_sha512(1); - sctx->state[2] = read_octeon_64bit_hash_sha512(2); - sctx->state[3] = read_octeon_64bit_hash_sha512(3); - sctx->state[4] = read_octeon_64bit_hash_sha512(4); - sctx->state[5] = read_octeon_64bit_hash_sha512(5); - sctx->state[6] = read_octeon_64bit_hash_sha512(6); - sctx->state[7] = read_octeon_64bit_hash_sha512(7); -} - -static void octeon_sha512_transform(struct sha512_state *sctx, - const u8 *src, int blocks) -{ - do { - const u64 *block = (const u64 *)src; - - write_octeon_64bit_block_sha512(block[0], 0); - write_octeon_64bit_block_sha512(block[1], 1); - write_octeon_64bit_block_sha512(block[2], 2); - write_octeon_64bit_block_sha512(block[3], 3); - write_octeon_64bit_block_sha512(block[4], 4); - write_octeon_64bit_block_sha512(block[5], 5); - write_octeon_64bit_block_sha512(block[6], 6); - write_octeon_64bit_block_sha512(block[7], 7); - write_octeon_64bit_block_sha512(block[8], 8); - write_octeon_64bit_block_sha512(block[9], 9); - write_octeon_64bit_block_sha512(block[10], 10); - write_octeon_64bit_block_sha512(block[11], 11); - write_octeon_64bit_block_sha512(block[12], 12); - write_octeon_64bit_block_sha512(block[13], 13); - write_octeon_64bit_block_sha512(block[14], 14); - octeon_sha512_start(block[15]); - - src += SHA512_BLOCK_SIZE; - } while (--blocks); -} - -static int octeon_sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - struct octeon_cop2_state state; - unsigned long flags; - int remain; - - flags = octeon_crypto_enable(&state); - octeon_sha512_store_hash(sctx); - - remain = sha512_base_do_update_blocks(desc, data, len, - octeon_sha512_transform); - - octeon_sha512_read_hash(sctx); - octeon_crypto_disable(&state, flags); - return remain; -} - -static int octeon_sha512_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *hash) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - struct octeon_cop2_state state; - unsigned long flags; - - flags = octeon_crypto_enable(&state); - octeon_sha512_store_hash(sctx); - - sha512_base_do_finup(desc, src, len, octeon_sha512_transform); - - octeon_sha512_read_hash(sctx); - octeon_crypto_disable(&state, flags); - return sha512_base_finish(desc, hash); -} - -static struct shash_alg octeon_sha512_algs[2] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = octeon_sha512_update, - .finup = octeon_sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name= "octeon-sha512", - .cra_priority = OCTEON_CR_OPCODE_PRIORITY, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = octeon_sha512_update, - .finup = octeon_sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name= "octeon-sha384", - .cra_priority = OCTEON_CR_OPCODE_PRIORITY, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static int __init octeon_sha512_mod_init(void) -{ - if (!octeon_has_crypto()) - return -ENOTSUPP; - return crypto_register_shashes(octeon_sha512_algs, - ARRAY_SIZE(octeon_sha512_algs)); -} - -static void __exit octeon_sha512_mod_fini(void) -{ - crypto_unregister_shashes(octeon_sha512_algs, - ARRAY_SIZE(octeon_sha512_algs)); -} - -module_init(octeon_sha512_mod_init); -module_exit(octeon_sha512_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-512 and SHA-384 Secure Hash Algorithms (OCTEON)"); -MODULE_AUTHOR("Aaro Koskinen "); diff --git a/arch/mips/configs/cavium_octeon_defconfig b/arch/mips/configs/cavium_octeon_defconfig index 88ae0aa85364..3f50e1d78894 100644 --- a/arch/mips/configs/cavium_octeon_defconfig +++ b/arch/mips/configs/cavium_octeon_defconfig @@ -156,8 +156,6 @@ CONFIG_SECURITY_NETWORK=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD5_OCTEON=y -CONFIG_CRYPTO_SHA1_OCTEON=m -CONFIG_CRYPTO_SHA512_OCTEON=m CONFIG_CRYPTO_DES=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_DEBUG_FS=y diff --git a/arch/mips/configs/eyeq5_defconfig b/arch/mips/configs/eyeq5_defconfig index ff7af5dc6d9d..6688f56aba1c 100644 --- a/arch/mips/configs/eyeq5_defconfig +++ b/arch/mips/configs/eyeq5_defconfig @@ -19,20 +19,18 @@ CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y CONFIG_EYEQ=y -CONFIG_MACH_EYEQ5=y CONFIG_FIT_IMAGE_FDT_EPM5=y -CONFIG_PAGE_SIZE_16KB=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_SPARSEMEM_MANUAL=y CONFIG_USERFAULTFD=y CONFIG_NET=y CONFIG_PACKET=y @@ -64,8 +62,14 @@ CONFIG_CAN_M_CAN=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_NOMADIK=y # CONFIG_PTP_1588_CLOCK is not set CONFIG_PINCTRL=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_NOMADIK=y +CONFIG_SENSORS_LM75=y CONFIG_MFD_SYSCON=y CONFIG_HID_A4TECH=y CONFIG_HID_BELKIN=y @@ -79,6 +83,8 @@ 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 diff --git a/arch/mips/configs/eyeq6_defconfig b/arch/mips/configs/eyeq6_defconfig index 0afbb45a78e8..0a00a201937b 100644 --- a/arch/mips/configs/eyeq6_defconfig +++ b/arch/mips/configs/eyeq6_defconfig @@ -82,6 +82,8 @@ 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 diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig index 114fcd67898d..cdedbb8a8f53 100644 --- a/arch/mips/configs/fuloong2e_defconfig +++ b/arch/mips/configs/fuloong2e_defconfig @@ -44,7 +44,6 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_TRACE=m CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_IPRANGE=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m diff --git a/arch/mips/configs/generic/board-marduk.config b/arch/mips/configs/generic/board-marduk.config index 05ca34cd5a73..65433c5c4fde 100644 --- a/arch/mips/configs/generic/board-marduk.config +++ b/arch/mips/configs/generic/board-marduk.config @@ -50,4 +50,3 @@ CONFIG_CRYPTO_DEV_IMGTEC_HASH=y CONFIG_IMGPDC_WDT=y CONFIG_IR_IMG=y CONFIG_CC10001_ADC=y -CONFIG_SND_SOC_IMG=y diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index f1a8ccf2c459..2decf8b98d31 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -79,7 +79,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m diff --git a/arch/mips/configs/loongson2k_defconfig b/arch/mips/configs/loongson2k_defconfig index 4b7f914d01d0..0cc665d3ea34 100644 --- a/arch/mips/configs/loongson2k_defconfig +++ b/arch/mips/configs/loongson2k_defconfig @@ -52,7 +52,6 @@ CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_MARK=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m CONFIG_NETFILTER_XT_MATCH_LIMIT=m @@ -257,6 +256,17 @@ CONFIG_SND_HDA_INTEL=y CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_PATCH_LOADER=y CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_REALTEK_LIB=y +CONFIG_SND_HDA_CODEC_ALC260=y +CONFIG_SND_HDA_CODEC_ALC262=y +CONFIG_SND_HDA_CODEC_ALC268=y +CONFIG_SND_HDA_CODEC_ALC269=y +CONFIG_SND_HDA_CODEC_ALC662=y +CONFIG_SND_HDA_CODEC_ALC680=y +CONFIG_SND_HDA_CODEC_ALC861=y +CONFIG_SND_HDA_CODEC_ALC861VD=y +CONFIG_SND_HDA_CODEC_ALC880=y +CONFIG_SND_HDA_CODEC_ALC882=y CONFIG_SND_HDA_CODEC_ANALOG=y CONFIG_SND_HDA_CODEC_SIGMATEL=y CONFIG_SND_HDA_CODEC_VIA=y diff --git a/arch/mips/configs/loongson3_defconfig b/arch/mips/configs/loongson3_defconfig index 98844b457b7f..240efff37d98 100644 --- a/arch/mips/configs/loongson3_defconfig +++ b/arch/mips/configs/loongson3_defconfig @@ -72,7 +72,6 @@ CONFIG_NETFILTER_XT_TARGET_MARK=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m CONFIG_NETFILTER_XT_MATCH_LIMIT=m @@ -293,8 +292,23 @@ CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_HDA_INTEL=m CONFIG_SND_HDA_PATCH_LOADER=y CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_REALTEK_LIB=m +CONFIG_SND_HDA_CODEC_ALC260=m +CONFIG_SND_HDA_CODEC_ALC262=m +CONFIG_SND_HDA_CODEC_ALC268=m +CONFIG_SND_HDA_CODEC_ALC269=m +CONFIG_SND_HDA_CODEC_ALC662=m +CONFIG_SND_HDA_CODEC_ALC680=m +CONFIG_SND_HDA_CODEC_ALC861=m +CONFIG_SND_HDA_CODEC_ALC861VD=m +CONFIG_SND_HDA_CODEC_ALC880=m +CONFIG_SND_HDA_CODEC_ALC882=m CONFIG_SND_HDA_CODEC_SIGMATEL=m CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_HDMI_GENERIC=m +CONFIG_SND_HDA_CODEC_HDMI_INTEL=m +CONFIG_SND_HDA_CODEC_HDMI_ATI=m +CONFIG_SND_HDA_CODEC_HDMI_NVIDIA=m CONFIG_SND_HDA_CODEC_CONEXANT=m # CONFIG_SND_USB is not set CONFIG_HIDRAW=y diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 869a14b3184f..9fcbac829920 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -80,7 +80,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m CONFIG_NETFILTER_XT_MATCH_HELPER=m diff --git a/arch/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig index 41e1fea303ea..19102386a81c 100644 --- a/arch/mips/configs/malta_kvm_defconfig +++ b/arch/mips/configs/malta_kvm_defconfig @@ -84,7 +84,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m CONFIG_NETFILTER_XT_MATCH_HELPER=m diff --git a/arch/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig index 13ff1877e26e..1dd07c9d1812 100644 --- a/arch/mips/configs/maltaup_xpa_defconfig +++ b/arch/mips/configs/maltaup_xpa_defconfig @@ -82,7 +82,6 @@ CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m CONFIG_NETFILTER_XT_MATCH_HELPER=m diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index e4bcdb64df6c..2707ab134639 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -273,6 +273,7 @@ CONFIG_DM9102=m CONFIG_ULI526X=m CONFIG_PCMCIA_XIRCOM=m CONFIG_DL2K=m +CONFIG_SUNDANCE=m CONFIG_PCMCIA_FMVJ18X=m CONFIG_E100=m CONFIG_E1000=m diff --git a/arch/mips/configs/rb532_defconfig b/arch/mips/configs/rb532_defconfig index 9fb114ef5e2d..30d18b084cda 100644 --- a/arch/mips/configs/rb532_defconfig +++ b/arch/mips/configs/rb532_defconfig @@ -56,7 +56,6 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_TRACE=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index 7b5a5591ccc9..39a2419e1f3e 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -64,7 +64,6 @@ CONFIG_NETFILTER_XT_MATCH_COMMENT=m CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m diff --git a/arch/mips/crypto/Kconfig b/arch/mips/crypto/Kconfig index 6bf073ae7613..7b91f4ec65bf 100644 --- a/arch/mips/crypto/Kconfig +++ b/arch/mips/crypto/Kconfig @@ -12,24 +12,4 @@ config CRYPTO_MD5_OCTEON Architecture: mips OCTEON using crypto instructions, when available -config CRYPTO_SHA1_OCTEON - tristate "Hash functions: SHA-1 (OCTEON)" - depends on CPU_CAVIUM_OCTEON - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: mips OCTEON - -config CRYPTO_SHA512_OCTEON - tristate "Hash functions: SHA-384 and SHA-512 (OCTEON)" - depends on CPU_CAVIUM_OCTEON - select CRYPTO_SHA512 - select CRYPTO_HASH - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: mips OCTEON using crypto instructions, when available - endmenu diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index a600670d00e9..fd60837ce50b 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -123,6 +123,7 @@ extern struct cpuinfo_mips cpu_data[]; extern void cpu_probe(void); extern void cpu_report(void); +extern void cpu_disable_mmid(void); extern const char *__cpu_name[]; #define cpu_name_string() __cpu_name[raw_smp_processor_id()] diff --git a/arch/mips/include/asm/hugetlb.h b/arch/mips/include/asm/hugetlb.h index fbc71ddcf0f6..8c460ce01ffe 100644 --- a/arch/mips/include/asm/hugetlb.h +++ b/arch/mips/include/asm/hugetlb.h @@ -11,20 +11,6 @@ #include -#define __HAVE_ARCH_PREPARE_HUGEPAGE_RANGE -static inline int prepare_hugepage_range(struct file *file, - unsigned long addr, - unsigned long len) -{ - unsigned long task_size = STACK_TOP; - - if (len > task_size) - return -ENOMEM; - if (task_size - len < addr) - return -EINVAL; - return 0; -} - #define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep, diff --git a/arch/mips/include/asm/mach-generic/mc146818rtc.h b/arch/mips/include/asm/mach-generic/mc146818rtc.h index 9c72e540ff56..249279b0494d 100644 --- a/arch/mips/include/asm/mach-generic/mc146818rtc.h +++ b/arch/mips/include/asm/mach-generic/mc146818rtc.h @@ -29,8 +29,4 @@ static inline void CMOS_WRITE(unsigned char data, unsigned long addr) #define RTC_ALWAYS_BCD 0 -#ifndef mc146818_decode_year -#define mc146818_decode_year(year) ((year) < 70 ? (year) + 2000 : (year) + 1900) -#endif - #endif /* __ASM_MACH_GENERIC_MC146818RTC_H */ diff --git a/arch/mips/include/asm/mach-ip30/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ip30/cpu-feature-overrides.h index ce4e4c6e09e2..50d487a4c95e 100644 --- a/arch/mips/include/asm/mach-ip30/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-ip30/cpu-feature-overrides.h @@ -5,7 +5,7 @@ * Copyright (C) 2003 Ralf Baechle * 2004-2007 Stanislaw Skowronek * 2009 Johannes Dickgreber - * 2015 Joshua Kinard + * 2015 Joshua Kinard * */ #ifndef __ASM_MACH_IP30_CPU_FEATURE_OVERRIDES_H diff --git a/arch/mips/include/asm/mach-ip30/spaces.h b/arch/mips/include/asm/mach-ip30/spaces.h index c8a302dfbe05..d381b93d6ad3 100644 --- a/arch/mips/include/asm/mach-ip30/spaces.h +++ b/arch/mips/include/asm/mach-ip30/spaces.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2016 Joshua Kinard + * Copyright (C) 2016 Joshua Kinard * */ #ifndef _ASM_MACH_IP30_SPACES_H diff --git a/arch/mips/include/asm/mach-jazz/mc146818rtc.h b/arch/mips/include/asm/mach-jazz/mc146818rtc.h index 987f727afe25..639bff8ebca3 100644 --- a/arch/mips/include/asm/mach-jazz/mc146818rtc.h +++ b/arch/mips/include/asm/mach-jazz/mc146818rtc.h @@ -33,6 +33,4 @@ static inline void CMOS_WRITE(unsigned char data, unsigned long addr) #define RTC_ALWAYS_BCD 0 -#define mc146818_decode_year(year) ((year) + 1980) - #endif /* __ASM_MACH_JAZZ_MC146818RTC_H */ diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h index c2e0acb755cd..dd9f621d0204 100644 --- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h @@ -99,5 +99,8 @@ extern __iomem void *ltq_cgu_membase; extern void ltq_pmu_enable(unsigned int module); extern void ltq_pmu_disable(unsigned int module); +/* VMMC */ +extern unsigned int *ltq_get_cp1_base(void); + #endif /* CONFIG_SOC_TYPE_XWAY */ #endif /* _LTQ_XWAY_H__ */ diff --git a/arch/mips/include/asm/mach-malta/mc146818rtc.h b/arch/mips/include/asm/mach-malta/mc146818rtc.h index e8cc7fdf7415..7da2c0ea55da 100644 --- a/arch/mips/include/asm/mach-malta/mc146818rtc.h +++ b/arch/mips/include/asm/mach-malta/mc146818rtc.h @@ -31,6 +31,4 @@ static inline void CMOS_WRITE(unsigned char data, unsigned long addr) #define RTC_ALWAYS_BCD 0 -#define mc146818_decode_year(year) ((year) < 70 ? (year) + 2000 : (year) + 1900) - #endif /* __ASM_MACH_MALTA_MC146818RTC_H */ diff --git a/arch/mips/include/asm/mach-rm/mc146818rtc.h b/arch/mips/include/asm/mach-rm/mc146818rtc.h deleted file mode 100644 index a074f4f84f75..000000000000 --- a/arch/mips/include/asm/mach-rm/mc146818rtc.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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) 2004 by Ralf Baechle - * - * RTC routines for PC style attached Dallas chip with ARC epoch. - */ -#ifndef __ASM_MACH_RM_MC146818RTC_H -#define __ASM_MACH_RM_MC146818RTC_H - -#ifdef CONFIG_CPU_BIG_ENDIAN -#define mc146818_decode_year(year) ((year) < 70 ? (year) + 2000 : (year) + 1900) -#else -#define mc146818_decode_year(year) ((year) + 1980) -#endif - -#include - -#endif /* __ASM_MACH_RM_MC146818RTC_H */ diff --git a/arch/mips/include/asm/mc146818-time.h b/arch/mips/include/asm/mc146818-time.h index cbf5cec345f1..ac52a30b4161 100644 --- a/arch/mips/include/asm/mc146818-time.h +++ b/arch/mips/include/asm/mc146818-time.h @@ -8,112 +8,21 @@ #ifndef __ASM_MC146818_TIME_H #define __ASM_MC146818_TIME_H -#include #include #include -/* - * For check timing call set_rtc_mmss() 500ms; used in timer interrupt. - */ -#define USEC_AFTER 500000 -#define USEC_BEFORE 500000 - -/* - * In order to set the CMOS clock precisely, set_rtc_mmss has to be - * called 500 ms after the second nowtime has started, because when - * nowtime is written into the registers of the CMOS clock, it will - * jump to the next second precisely 500 ms later. Check the Motorola - * MC146818A or Dallas DS12887 data sheet for details. - * - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you'll only notice that after reboot! - */ -static inline int mc146818_set_rtc_mmss(unsigned long nowtime) -{ - int real_seconds, real_minutes, cmos_minutes; - unsigned char save_control, save_freq_select; - int retval = 0; - unsigned long flags; - - spin_lock_irqsave(&rtc_lock, flags); - save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - cmos_minutes = CMOS_READ(RTC_MINUTES); - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - cmos_minutes = bcd2bin(cmos_minutes); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - cmos_minutes) < 30) { - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - real_seconds = bin2bcd(real_seconds); - real_minutes = bin2bcd(real_minutes); - } - CMOS_WRITE(real_seconds, RTC_SECONDS); - CMOS_WRITE(real_minutes, RTC_MINUTES); - } else { - printk_once(KERN_NOTICE - "set_rtc_mmss: can't update from %d to %d\n", - cmos_minutes, real_minutes); - retval = -1; - } - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - spin_unlock_irqrestore(&rtc_lock, flags); - - return retval; -} - +#ifdef CONFIG_RTC_MC146818_LIB static inline time64_t mc146818_get_cmos_time(void) { - unsigned int year, mon, day, hour, min, sec; - unsigned long flags; + struct rtc_time tm; - spin_lock_irqsave(&rtc_lock, flags); - - do { - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - sec = bcd2bin(sec); - min = bcd2bin(min); - hour = bcd2bin(hour); - day = bcd2bin(day); - mon = bcd2bin(mon); - year = bcd2bin(year); + if (mc146818_get_time(&tm, 1000)) { + pr_err("Unable to read current time from RTC\n"); + return 0; } - spin_unlock_irqrestore(&rtc_lock, flags); - year = mc146818_decode_year(year); - return mktime64(year, mon, day, hour, min, sec); + return rtc_tm_to_time64(&tm); } +#endif /* CONFIG_RTC_MC146818_LIB */ #endif /* __ASM_MC146818_TIME_H */ diff --git a/arch/mips/include/asm/mips-cps.h b/arch/mips/include/asm/mips-cps.h index 917009b80e69..1fffd47a4564 100644 --- a/arch/mips/include/asm/mips-cps.h +++ b/arch/mips/include/asm/mips-cps.h @@ -258,6 +258,8 @@ static inline bool mips_cps_multicluster_cpus(void) /** * mips_cps_first_online_in_cluster() - Detect if CPU is first online in cluster + * @first_cpu: The first other online CPU in cluster, or nr_cpu_ids if + * the function returns true. * * Determine whether the local CPU is the first to be brought online in its * cluster - that is, whether there are any other online CPUs in the local @@ -265,6 +267,6 @@ static inline bool mips_cps_multicluster_cpus(void) * * Returns true if this CPU is first online, else false. */ -extern unsigned int mips_cps_first_online_in_cluster(void); +extern unsigned int mips_cps_first_online_in_cluster(int *first_cpu); #endif /* __MIPS_ASM_MIPS_CPS_H__ */ diff --git a/arch/mips/cavium-octeon/crypto/octeon-crypto.h b/arch/mips/include/asm/octeon/crypto.h similarity index 100% rename from arch/mips/cavium-octeon/crypto/octeon-crypto.h rename to arch/mips/include/asm/octeon/crypto.h diff --git a/arch/mips/include/asm/sgi/heart.h b/arch/mips/include/asm/sgi/heart.h index 0d03751955c4..c224c2e3575a 100644 --- a/arch/mips/include/asm/sgi/heart.h +++ b/arch/mips/include/asm/sgi/heart.h @@ -4,7 +4,7 @@ * * Copyright (C) 2004-2007 Stanislaw Skowronek * 2009 Johannes Dickgreber - * 2007-2015 Joshua Kinard + * 2007-2015 Joshua Kinard */ #ifndef __ASM_SGI_HEART_H #define __ASM_SGI_HEART_H diff --git a/arch/mips/include/asm/smp-cps.h b/arch/mips/include/asm/smp-cps.h index 10d3ebd890cb..88cfae5d22c8 100644 --- a/arch/mips/include/asm/smp-cps.h +++ b/arch/mips/include/asm/smp-cps.h @@ -24,6 +24,7 @@ struct core_boot_config { struct cluster_boot_config { unsigned long *core_power; + struct cpumask cpumask; struct core_boot_config *core_config; }; diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index e855a3611d92..5e7193b759f3 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -55,7 +55,7 @@ static inline int mips_clockevent_init(void) */ extern int init_r4k_clocksource(void); -static inline int init_mips_clocksource(void) +static inline __init int init_mips_clocksource(void) { #ifdef CONFIG_CSRC_R4K return init_r4k_clocksource(); diff --git a/arch/mips/include/asm/vpe.h b/arch/mips/include/asm/vpe.h index 61fd4d0aeda4..c0769dc4b853 100644 --- a/arch/mips/include/asm/vpe.h +++ b/arch/mips/include/asm/vpe.h @@ -119,4 +119,12 @@ void cleanup_tc(struct tc *tc); int __init vpe_module_init(void); void __exit vpe_module_exit(void); + +#ifdef CONFIG_MIPS_VPE_LOADER_MT +void *vpe_alloc(void); +int vpe_start(void *vpe, unsigned long start); +int vpe_stop(void *vpe); +int vpe_free(void *vpe); +#endif /* CONFIG_MIPS_VPE_LOADER_MT */ + #endif /* _ASM_VPE_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 31ac655b7837..72fb1b006da9 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -163,6 +163,9 @@ #define SO_PASSRIGHTS 83 +#define SO_INQ 84 +#define SCM_INQ SO_INQ + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index af7412549e6e..04dc9ab55524 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include @@ -37,6 +38,8 @@ unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); +static bool mmid_disabled_quirk; + static inline unsigned long cpu_get_msa_id(void) { unsigned long status, msa_id; @@ -645,7 +648,7 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c) config5 &= ~(MIPS_CONF5_UFR | MIPS_CONF5_UFE); if (cpu_has_mips_r6) { - if (!__builtin_constant_p(cpu_has_mmid) || cpu_has_mmid) + if (!mmid_disabled_quirk && (!__builtin_constant_p(cpu_has_mmid) || cpu_has_mmid)) config5 |= MIPS_CONF5_MI; else config5 &= ~MIPS_CONF5_MI; @@ -708,7 +711,6 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c) max_mmid_width); asid_mask = GENMASK(max_mmid_width - 1, 0); } - set_cpu_asid_mask(c, asid_mask); } } @@ -2046,3 +2048,39 @@ void cpu_set_vpe_id(struct cpuinfo_mips *cpuinfo, unsigned int vpe) cpuinfo->globalnumber &= ~MIPS_GLOBALNUMBER_VP; cpuinfo->globalnumber |= vpe << MIPS_GLOBALNUMBER_VP_SHF; } + +void cpu_disable_mmid(void) +{ + int i; + unsigned long asid_mask; + unsigned int cpu = smp_processor_id(); + struct cpuinfo_mips *c = ¤t_cpu_data; + unsigned int config4 = read_c0_config4(); + unsigned int config5 = read_c0_config5(); + + /* Setup the initial ASID mask based on config4 */ + asid_mask = MIPS_ENTRYHI_ASID; + if (config4 & MIPS_CONF4_AE) + asid_mask |= MIPS_ENTRYHI_ASIDX; + set_cpu_asid_mask(c, asid_mask); + + /* Disable MMID in the C0 and update cpuinfo_mips accordingly */ + config5 &= ~(MIPS_CONF5_UFR | MIPS_CONF5_UFE); + config5 &= ~MIPS_CONF5_MI; + write_c0_config5(config5); + /* Ensure the write to config5 above takes effect */ + back_to_back_c0_hazard(); + c->options &= ~MIPS_CPU_MMID; + + /* Setup asid cache value cleared in per_cpu_trap_init() */ + cpu_data[cpu].asid_cache = asid_first_version(cpu); + + /* Reinit context for each CPU */ + for_each_possible_cpu(i) + set_cpu_context(i, &init_mm, 0); + + /* Ensure that now MMID will be seen as disable */ + mmid_disabled_quirk = true; + + pr_info("MMID support disabled due to hardware support issue\n"); +} diff --git a/arch/mips/kernel/gpio_txx9.c b/arch/mips/kernel/gpio_txx9.c index 027fb57d0d79..96ac40d20c23 100644 --- a/arch/mips/kernel/gpio_txx9.c +++ b/arch/mips/kernel/gpio_txx9.c @@ -70,7 +70,7 @@ static int txx9_gpio_dir_out(struct gpio_chip *chip, unsigned int offset, static struct gpio_chip txx9_gpio_chip = { .get = txx9_gpio_get, - .set_rv = txx9_gpio_set, + .set = txx9_gpio_set, .direction_input = txx9_gpio_dir_in, .direction_output = txx9_gpio_dir_out, .label = "TXx9", diff --git a/arch/mips/kernel/mips-cm.c b/arch/mips/kernel/mips-cm.c index 43cb1e20baed..7c9c5dc38823 100644 --- a/arch/mips/kernel/mips-cm.c +++ b/arch/mips/kernel/mips-cm.c @@ -10,6 +10,7 @@ #include #include +#include #include void __iomem *mips_gcr_base; @@ -248,6 +249,11 @@ void mips_cm_update_property(void) return; pr_info("HCI (Hardware Cache Init for the L2 cache) in GCR_L2_RAM_CONFIG from the CM3 is broken"); mips_cm_is_l2_hci_broken = true; + + /* Disable MMID only if it was configured */ + if (cpu_has_mmid) + cpu_disable_mmid(); + of_node_put(cm_node); } @@ -529,39 +535,23 @@ void mips_cm_error_report(void) write_gcr_error_cause(cm_error); } -unsigned int mips_cps_first_online_in_cluster(void) +unsigned int mips_cps_first_online_in_cluster(int *first_cpu) { - unsigned int local_cl; - int i; - - local_cl = cpu_cluster(¤t_cpu_data); + unsigned int local_cl = cpu_cluster(¤t_cpu_data); + struct cpumask *local_cl_mask; /* - * We rely upon knowledge that CPUs are numbered sequentially by - * cluster - ie. CPUs 0..X will be in cluster 0, CPUs X+1..Y in cluster - * 1, CPUs Y+1..Z in cluster 2 etc. This means that CPUs in the same - * cluster will immediately precede or follow one another. - * - * First we scan backwards, until we find an online CPU in the cluster - * or we move on to another cluster. + * mips_cps_cluster_bootcfg is allocated in cps_prepare_cpus. If it is + * not yet done, then we are so early that only one CPU is running, so + * it is the first online CPU in the cluster. */ - for (i = smp_processor_id() - 1; i >= 0; i--) { - if (cpu_cluster(&cpu_data[i]) != local_cl) - break; - if (!cpu_online(i)) - continue; - return false; - } + if (IS_ENABLED(CONFIG_MIPS_CPS) && mips_cps_cluster_bootcfg) + local_cl_mask = &mips_cps_cluster_bootcfg[local_cl].cpumask; + else + return true; - /* Then do the same for higher numbered CPUs */ - for (i = smp_processor_id() + 1; i < nr_cpu_ids; i++) { - if (cpu_cluster(&cpu_data[i]) != local_cl) - break; - if (!cpu_online(i)) - continue; - return false; - } - - /* We found no online CPUs in the local cluster */ - return true; + *first_cpu = cpumask_any_and_but(local_cl_mask, + cpu_online_mask, + smp_processor_id()); + return (*first_cpu >= nr_cpu_ids); } diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b630604c577f..02aa6a04a21d 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -690,18 +690,20 @@ unsigned long mips_stack_top(void) } /* Space for the VDSO, data page & GIC user page */ - top -= PAGE_ALIGN(current->thread.abi->vdso->size); - top -= PAGE_SIZE; - top -= mips_gic_present() ? PAGE_SIZE : 0; + if (current->thread.abi) { + top -= PAGE_ALIGN(current->thread.abi->vdso->size); + top -= PAGE_SIZE; + top -= mips_gic_present() ? PAGE_SIZE : 0; + + /* Space to randomize the VDSO base */ + if (current->flags & PF_RANDOMIZE) + top -= VDSO_RANDOMIZE_SIZE; + } /* Space for cache colour alignment */ if (cpu_has_dc_aliases) top -= shm_align_mask + 1; - /* Space to randomize the VDSO base */ - if (current->flags & PF_RANDOMIZE) - top -= VDSO_RANDOMIZE_SIZE; - return top; } diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index b890d64d352c..3f4c94c88124 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -935,7 +935,7 @@ int regs_query_register_offset(const char *name) static const struct user_regset mips_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(unsigned int), .align = sizeof(unsigned int), @@ -943,7 +943,7 @@ static const struct user_regset mips_regsets[] = { .set = gpr32_set, }, [REGSET_DSP] = { - .core_note_type = NT_MIPS_DSP, + USER_REGSET_NOTE_TYPE(MIPS_DSP), .n = NUM_DSP_REGS + 1, .size = sizeof(u32), .align = sizeof(u32), @@ -953,7 +953,7 @@ static const struct user_regset mips_regsets[] = { }, #ifdef CONFIG_MIPS_FP_SUPPORT [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), @@ -961,7 +961,7 @@ static const struct user_regset mips_regsets[] = { .set = fpr_set, }, [REGSET_FP_MODE] = { - .core_note_type = NT_MIPS_FP_MODE, + USER_REGSET_NOTE_TYPE(MIPS_FP_MODE), .n = 1, .size = sizeof(int), .align = sizeof(int), @@ -971,7 +971,7 @@ static const struct user_regset mips_regsets[] = { #endif #ifdef CONFIG_CPU_HAS_MSA [REGSET_MSA] = { - .core_note_type = NT_MIPS_MSA, + USER_REGSET_NOTE_TYPE(MIPS_MSA), .n = NUM_FPU_REGS + 1, .size = 16, .align = 16, @@ -995,7 +995,7 @@ static const struct user_regset_view user_mips_view = { static const struct user_regset mips64_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), @@ -1003,7 +1003,7 @@ static const struct user_regset mips64_regsets[] = { .set = gpr64_set, }, [REGSET_DSP] = { - .core_note_type = NT_MIPS_DSP, + USER_REGSET_NOTE_TYPE(MIPS_DSP), .n = NUM_DSP_REGS + 1, .size = sizeof(u64), .align = sizeof(u64), @@ -1013,7 +1013,7 @@ static const struct user_regset mips64_regsets[] = { }, #ifdef CONFIG_MIPS_FP_SUPPORT [REGSET_FP_MODE] = { - .core_note_type = NT_MIPS_FP_MODE, + USER_REGSET_NOTE_TYPE(MIPS_FP_MODE), .n = 1, .size = sizeof(int), .align = sizeof(int), @@ -1021,7 +1021,7 @@ static const struct user_regset mips64_regsets[] = { .set = fp_mode_set, }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), @@ -1031,7 +1031,7 @@ static const struct user_regset mips64_regsets[] = { #endif #ifdef CONFIG_CPU_HAS_MSA [REGSET_MSA] = { - .core_note_type = NT_MIPS_MSA, + USER_REGSET_NOTE_TYPE(MIPS_MSA), .n = NUM_FPU_REGS + 1, .size = 16, .align = 16, diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c index cda7983e7c18..7f1c136ad850 100644 --- a/arch/mips/kernel/relocate.c +++ b/arch/mips/kernel/relocate.c @@ -138,7 +138,7 @@ static int __init reloc_handler(u32 type, u32 *loc_orig, u32 *loc_new, apply_r_mips_hi16_rel(loc_orig, loc_new, offset); break; default: - pr_err("Unhandled relocation type %d at 0x%pK\n", type, + pr_err("Unhandled relocation type %d at 0x%p\n", type, loc_orig); return -ENOEXEC; } @@ -439,10 +439,10 @@ static void show_kernel_relocation(const char *level) { if (__kaslr_offset > 0) { printk(level); - pr_cont("Kernel relocated by 0x%pK\n", (void *)__kaslr_offset); - pr_cont(" .text @ 0x%pK\n", _text); - pr_cont(" .data @ 0x%pK\n", _sdata); - pr_cont(" .bss @ 0x%pK\n", __bss_start); + pr_cont("Kernel relocated by 0x%p\n", (void *)__kaslr_offset); + pr_cont(" .text @ 0x%p\n", _text); + pr_cont(" .data @ 0x%p\n", _sdata); + pr_cont(" .bss @ 0x%p\n", __bss_start); } } diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index fbfe0771317e..11b9b6b63e19 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -458,7 +458,7 @@ static void __init mips_parse_crashkernel(void) total_mem = memblock_phys_mem_size(); ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base, - NULL, NULL); + NULL, NULL, NULL); if (ret != 0 || crash_size <= 0) return; diff --git a/arch/mips/kernel/smp-cps.c b/arch/mips/kernel/smp-cps.c index 7b0e69af4097..22d4f9ff3ae2 100644 --- a/arch/mips/kernel/smp-cps.c +++ b/arch/mips/kernel/smp-cps.c @@ -281,9 +281,20 @@ static void __init cps_smp_setup(void) #endif /* CONFIG_MIPS_MT_FPAFF */ } +unsigned long calibrate_delay_is_known(void) +{ + int first_cpu_cluster = 0; + + /* The calibration has to be done on the primary CPU of the cluster */ + if (mips_cps_first_online_in_cluster(&first_cpu_cluster)) + return 0; + + return cpu_data[first_cpu_cluster].udelay_val; +} + static void __init cps_prepare_cpus(unsigned int max_cpus) { - unsigned int nclusters, ncores, core_vpes, c, cl, cca; + unsigned int nclusters, ncores, core_vpes, nvpe = 0, c, cl, cca; bool cca_unsuitable, cores_limited; struct cluster_boot_config *cluster_bootcfg; struct core_boot_config *core_bootcfg; @@ -356,10 +367,13 @@ static void __init cps_prepare_cpus(unsigned int max_cpus) /* Allocate VPE boot configuration structs */ for (c = 0; c < ncores; c++) { + int v; core_vpes = core_vpe_count(cl, c); core_bootcfg[c].vpe_config = kcalloc(core_vpes, sizeof(*core_bootcfg[c].vpe_config), GFP_KERNEL); + for (v = 0; v < core_vpes; v++) + cpumask_set_cpu(nvpe++, &mips_cps_cluster_bootcfg[cl].cpumask); if (!core_bootcfg[c].vpe_config) goto err_out; } diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index aa70e371bb54..d824ffe9a014 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -406,3 +406,5 @@ 465 n32 listxattrat sys_listxattrat 466 n32 removexattrat sys_removexattrat 467 n32 open_tree_attr sys_open_tree_attr +468 n32 file_getattr sys_file_getattr +469 n32 file_setattr sys_file_setattr diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index 1e8c44c7b614..7a7049c2c307 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -382,3 +382,5 @@ 465 n64 listxattrat sys_listxattrat 466 n64 removexattrat sys_removexattrat 467 n64 open_tree_attr sys_open_tree_attr +468 n64 file_getattr sys_file_getattr +469 n64 file_setattr sys_file_setattr diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index 114a5a1a6230..d330274f0601 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -455,3 +455,5 @@ 465 o32 listxattrat sys_listxattrat 466 o32 removexattrat sys_removexattrat 467 o32 open_tree_attr sys_open_tree_attr +468 o32 file_getattr sys_file_getattr +469 o32 file_setattr sys_file_setattr diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index cef3c423a41a..a75587018f44 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -315,7 +315,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) * we allocate is out of range, just give up now. */ if (!cpu_has_ebase_wg && virt_to_phys(gebase) >= 0x20000000) { - kvm_err("CP0_EBase.WG required for guest exception base %pK\n", + kvm_err("CP0_EBase.WG required for guest exception base %p\n", gebase); err = -ENOMEM; goto out_free_gebase; diff --git a/arch/mips/lantiq/falcon/prom.c b/arch/mips/lantiq/falcon/prom.c index 7b98def106e4..2a38c4267685 100644 --- a/arch/mips/lantiq/falcon/prom.c +++ b/arch/mips/lantiq/falcon/prom.c @@ -36,14 +36,14 @@ #define BOOT_NVEC (BOOT_REG_BASE | 0x04) #define BOOT_EVEC (BOOT_REG_BASE | 0x08) -void __init ltq_soc_nmi_setup(void) +static void __init ltq_soc_nmi_setup(void) { extern void (*nmi_handler)(void); ltq_w32((unsigned long)&nmi_handler, (void *)BOOT_NVEC); } -void __init ltq_soc_ejtag_setup(void) +static void __init ltq_soc_ejtag_setup(void) { extern void (*ejtag_debug_handler)(void); diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c index 1187729d8cbb..577e6e6309a6 100644 --- a/arch/mips/lantiq/falcon/sysctrl.c +++ b/arch/mips/lantiq/falcon/sysctrl.c @@ -14,6 +14,7 @@ #include #include "../clk.h" +#include "../prom.h" /* infrastructure control register */ #define SYS1_INFRAC 0x00bc @@ -72,11 +73,6 @@ static void __iomem *sysctl_membase[3], *status_membase; void __iomem *ltq_sys1_membase, *ltq_ebu_membase; -void falcon_trigger_hrst(int level) -{ - sysctl_w32(SYSCTL_SYS1, level & 1, SYS1_HRSTOUTC); -} - static inline void sysctl_wait(struct clk *clk, unsigned int test, unsigned int reg) { @@ -214,19 +210,16 @@ void __init ltq_soc_init(void) of_node_put(np_syseth); of_node_put(np_sysgpe); - if ((request_mem_region(res_status.start, resource_size(&res_status), - res_status.name) < 0) || - (request_mem_region(res_ebu.start, resource_size(&res_ebu), - res_ebu.name) < 0) || - (request_mem_region(res_sys[0].start, - resource_size(&res_sys[0]), - res_sys[0].name) < 0) || - (request_mem_region(res_sys[1].start, - resource_size(&res_sys[1]), - res_sys[1].name) < 0) || - (request_mem_region(res_sys[2].start, - resource_size(&res_sys[2]), - res_sys[2].name) < 0)) + if ((!request_mem_region(res_status.start, resource_size(&res_status), + res_status.name)) || + (!request_mem_region(res_ebu.start, resource_size(&res_ebu), + res_ebu.name)) || + (!request_mem_region(res_sys[0].start, resource_size(&res_sys[0]), + res_sys[0].name)) || + (!request_mem_region(res_sys[1].start, resource_size(&res_sys[1]), + res_sys[1].name)) || + (!request_mem_region(res_sys[2].start, resource_size(&res_sys[2]), + res_sys[2].name))) pr_err("Failed to request core resources"); status_membase = ioremap(res_status.start, diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c index a112573b6e37..961c55933a6d 100644 --- a/arch/mips/lantiq/irq.c +++ b/arch/mips/lantiq/irq.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -335,7 +336,8 @@ static const struct irq_domain_ops irq_domain_ops = { .map = icu_map, }; -int __init icu_of_init(struct device_node *node, struct device_node *parent) +static int __init +icu_of_init(struct device_node *node, struct device_node *parent) { struct device_node *eiu_node; struct resource res; diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c index 47ad21430fe2..39fb3ecdd6b7 100644 --- a/arch/mips/lantiq/xway/clk.c +++ b/arch/mips/lantiq/xway/clk.c @@ -74,7 +74,7 @@ unsigned long ltq_danube_pp32_hz(void) return clk; } -unsigned long ltq_ar9_sys_hz(void) +static unsigned long ltq_ar9_sys_hz(void) { if (((ltq_cgu_r32(CGU_SYS) >> 3) & 0x3) == 0x2) return CLOCK_393M; diff --git a/arch/mips/lantiq/xway/dcdc.c b/arch/mips/lantiq/xway/dcdc.c index 4a808f8c5beb..b79c462fd48a 100644 --- a/arch/mips/lantiq/xway/dcdc.c +++ b/arch/mips/lantiq/xway/dcdc.c @@ -46,7 +46,7 @@ static struct platform_driver dcdc_driver = { }, }; -int __init dcdc_init(void) +static int __init dcdc_init(void) { int ret = platform_driver_register(&dcdc_driver); diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c index 934ac72937e5..4693eba6c296 100644 --- a/arch/mips/lantiq/xway/dma.c +++ b/arch/mips/lantiq/xway/dma.c @@ -289,7 +289,7 @@ static struct platform_driver dma_driver = { }, }; -int __init +static int __init dma_init(void) { return platform_driver_register(&dma_driver); diff --git a/arch/mips/lantiq/xway/gptu.c b/arch/mips/lantiq/xway/gptu.c index 8d52001301de..484c9e3000c1 100644 --- a/arch/mips/lantiq/xway/gptu.c +++ b/arch/mips/lantiq/xway/gptu.c @@ -194,7 +194,7 @@ static struct platform_driver dma_driver = { }, }; -int __init gptu_init(void) +static int __init gptu_init(void) { int ret = platform_driver_register(&dma_driver); diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 5a75283d17f1..6031a0272d87 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -497,7 +497,7 @@ void __init ltq_soc_init(void) ifccr = CGU_IFCCR_VR9; pcicr = CGU_PCICR_VR9; } else { - clkdev_add_pmu("1e180000.etop", NULL, 1, 0, PMU_PPE); + clkdev_add_pmu("1e180000.ethernet", NULL, 1, 0, PMU_PPE); } if (!of_machine_is_compatible("lantiq,ase")) @@ -531,9 +531,9 @@ void __init ltq_soc_init(void) CLOCK_133M, CLOCK_133M); clkdev_add_pmu("1e101000.usb", "otg", 1, 0, PMU_USB0); clkdev_add_pmu("1f203018.usb2-phy", "phy", 1, 0, PMU_USB0_P); - clkdev_add_pmu("1e180000.etop", "ppe", 1, 0, PMU_PPE); - clkdev_add_cgu("1e180000.etop", "ephycgu", CGU_EPHY); - clkdev_add_pmu("1e180000.etop", "ephy", 1, 0, PMU_EPHY); + clkdev_add_pmu("1e180000.ethernet", "ppe", 1, 0, PMU_PPE); + clkdev_add_cgu("1e180000.ethernet", "ephycgu", CGU_EPHY); + clkdev_add_pmu("1e180000.ethernet", "ephy", 1, 0, PMU_EPHY); clkdev_add_pmu("1e103000.sdio", NULL, 1, 0, PMU_ASE_SDIO); clkdev_add_pmu("1e116000.mei", "dfe", 1, 0, PMU_DFE); } else if (of_machine_is_compatible("lantiq,grx390")) { @@ -592,7 +592,7 @@ void __init ltq_soc_init(void) clkdev_add_pmu("1e101000.usb", "otg", 1, 0, PMU_USB0 | PMU_AHBM); clkdev_add_pmu("1f203034.usb2-phy", "phy", 1, 0, PMU_USB1_P); clkdev_add_pmu("1e106000.usb", "otg", 1, 0, PMU_USB1 | PMU_AHBM); - clkdev_add_pmu("1e180000.etop", "switch", 1, 0, PMU_SWITCH); + clkdev_add_pmu("1e180000.ethernet", "switch", 1, 0, PMU_SWITCH); clkdev_add_pmu("1e103000.sdio", NULL, 1, 0, PMU_SDIO); clkdev_add_pmu("1e103100.deu", NULL, 1, 0, PMU_DEU); clkdev_add_pmu("1e116000.mei", "dfe", 1, 0, PMU_DFE); diff --git a/arch/mips/lib/.gitignore b/arch/mips/lib/.gitignore new file mode 100644 index 000000000000..647d7a922e68 --- /dev/null +++ b/arch/mips/lib/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# This now-removed directory used to contain generated files. +/crypto/ diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 9d75845ef78e..5d5b993cbc2b 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -3,8 +3,6 @@ # Makefile for MIPS-specific library files.. # -obj-y += crypto/ - lib-y += bitops.o csum_partial.o delay.o memcpy.o memset.o \ mips-atomic.o strncpy_user.o \ strnlen_user.o uncached.o @@ -16,7 +14,5 @@ lib-$(CONFIG_GENERIC_CSUM) := $(filter-out csum_partial.o, $(lib-y)) obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o -obj-$(CONFIG_CRC32_ARCH) += crc32-mips.o - # libgcc-style stuff needed in the kernel obj-y += bswapsi.o bswapdi.o multi3.o diff --git a/arch/mips/loongson64/setup.c b/arch/mips/loongson64/setup.c index 257038e18779..b3e590eae952 100644 --- a/arch/mips/loongson64/setup.c +++ b/arch/mips/loongson64/setup.c @@ -3,7 +3,6 @@ * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology * Author: Fuxin Zhang, zhangfx@lemote.com */ -#include #include #include diff --git a/arch/mips/mm/physaddr.c b/arch/mips/mm/physaddr.c index f9b8c85e9843..a6b1bf82057a 100644 --- a/arch/mips/mm/physaddr.c +++ b/arch/mips/mm/physaddr.c @@ -30,7 +30,7 @@ static inline bool __debug_virt_addr_valid(unsigned long x) phys_addr_t __virt_to_phys(volatile const void *x) { WARN(!__debug_virt_addr_valid((unsigned long)x), - "virt_to_phys used for non-linear address: %pK (%pS)\n", + "virt_to_phys used for non-linear address: %p (%pS)\n", x, x); return __virt_to_phys_nodebug(x); diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 76f3b9c0a9f0..347126dc010d 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -508,6 +508,60 @@ static int __init set_ntlb(char *str) __setup("ntlb=", set_ntlb); +/* Initialise all TLB entries with unique values */ +static void r4k_tlb_uniquify(void) +{ + int entry = num_wired_entries(); + + htw_stop(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + + while (entry < current_cpu_data.tlbsize) { + unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); + unsigned long asid = 0; + int idx; + + /* Skip wired MMID to make ginvt_mmid work */ + if (cpu_has_mmid) + asid = MMID_KERNEL_WIRED + 1; + + /* Check for match before using UNIQUE_ENTRYHI */ + do { + if (cpu_has_mmid) { + write_c0_memorymapid(asid); + write_c0_entryhi(UNIQUE_ENTRYHI(entry)); + } else { + write_c0_entryhi(UNIQUE_ENTRYHI(entry) | asid); + } + mtc0_tlbw_hazard(); + tlb_probe(); + tlb_probe_hazard(); + idx = read_c0_index(); + /* No match or match is on current entry */ + if (idx < 0 || idx == entry) + break; + /* + * If we hit a match, we need to try again with + * a different ASID. + */ + asid++; + } while (asid < asid_mask); + + if (idx >= 0 && idx != entry) + panic("Unable to uniquify TLB entry %d", idx); + + write_c0_index(entry); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + entry++; + } + + tlbw_use_hazard(); + htw_start(); + flush_micro_tlb(); +} + /* * Configure TLB (for init or after a CPU has been powered off). */ @@ -547,7 +601,7 @@ static void r4k_tlb_configure(void) temp_tlb_entry = current_cpu_data.tlbsize - 1; /* From this point on the ARC firmware is dead. */ - local_flush_tlb_all(); + r4k_tlb_uniquify(); /* Did I tell you that ARC SUCKS? */ } diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index 68a8cefed420..0e85839b8225 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -234,7 +234,7 @@ static struct platform_driver ltq_pci_driver = { }, }; -int __init pcibios_init(void) +static int __init pcibios_init(void) { int ret = platform_driver_register(<q_pci_driver); if (ret) diff --git a/arch/mips/pci/pci-rt2880.c b/arch/mips/pci/pci-rt2880.c index 1cada09fa5db..006e2bbab87e 100644 --- a/arch/mips/pci/pci-rt2880.c +++ b/arch/mips/pci/pci-rt2880.c @@ -264,7 +264,7 @@ static struct platform_driver rt288x_pci_driver = { }, }; -int __init pcibios_init(void) +static int __init pcibios_init(void) { int ret = platform_driver_register(&rt288x_pci_driver); diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c index af5bbbea949b..955b36e89358 100644 --- a/arch/mips/ralink/irq.c +++ b/arch/mips/ralink/irq.c @@ -15,6 +15,7 @@ #include #include +#include #include "common.h" diff --git a/arch/mips/rb532/gpio.c b/arch/mips/rb532/gpio.c index 0e47cd59b6cb..9aa5ef374465 100644 --- a/arch/mips/rb532/gpio.c +++ b/arch/mips/rb532/gpio.c @@ -164,7 +164,7 @@ static struct rb532_gpio_chip rb532_gpio_chip[] = { .direction_input = rb532_gpio_direction_input, .direction_output = rb532_gpio_direction_output, .get = rb532_gpio_get, - .set_rv = rb532_gpio_set, + .set = rb532_gpio_set, .to_irq = rb532_gpio_to_irq, .base = 0, .ngpio = 32, diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c index 288d4d17eddd..20ef663af16e 100644 --- a/arch/mips/sgi-ip27/ip27-irq.c +++ b/arch/mips/sgi-ip27/ip27-irq.c @@ -165,7 +165,7 @@ static void hub_domain_free(struct irq_domain *domain, return; irqd = irq_domain_get_irq_data(domain, virq); - if (irqd && irqd->chip_data) + if (irqd) kfree(irqd->chip_data); } diff --git a/arch/mips/sgi-ip30/ip30-power.c b/arch/mips/sgi-ip30/ip30-power.c index 120b3f3d5108..66851e17c5a7 100644 --- a/arch/mips/sgi-ip30/ip30-power.c +++ b/arch/mips/sgi-ip30/ip30-power.c @@ -3,7 +3,7 @@ * ip30-power.c: Software powerdown and reset handling for IP30 architecture. * * Copyright (C) 2004-2007 Stanislaw Skowronek - * 2014 Joshua Kinard + * 2014 Joshua Kinard * 2009 Johannes Dickgreber */ diff --git a/arch/mips/sgi-ip30/ip30-setup.c b/arch/mips/sgi-ip30/ip30-setup.c index e8547636a748..3fcb3ec9f802 100644 --- a/arch/mips/sgi-ip30/ip30-setup.c +++ b/arch/mips/sgi-ip30/ip30-setup.c @@ -3,7 +3,7 @@ * SGI IP30 miscellaneous setup bits. * * Copyright (C) 2004-2007 Stanislaw Skowronek - * 2007 Joshua Kinard + * 2007 Joshua Kinard * 2009 Johannes Dickgreber */ diff --git a/arch/mips/sgi-ip30/ip30-smp.c b/arch/mips/sgi-ip30/ip30-smp.c index 4bfe654602b1..1e8210f2a9f8 100644 --- a/arch/mips/sgi-ip30/ip30-smp.c +++ b/arch/mips/sgi-ip30/ip30-smp.c @@ -5,7 +5,7 @@ * and smp-bmips.c. * * Copyright (C) 2005-2007 Stanislaw Skowronek - * 2006-2007, 2014-2015 Joshua Kinard + * 2006-2007, 2014-2015 Joshua Kinard * 2009 Johannes Dickgreber */ diff --git a/arch/mips/sgi-ip30/ip30-timer.c b/arch/mips/sgi-ip30/ip30-timer.c index d13e105478ae..7652f72f0daf 100644 --- a/arch/mips/sgi-ip30/ip30-timer.c +++ b/arch/mips/sgi-ip30/ip30-timer.c @@ -5,7 +5,7 @@ * * Copyright (C) 2004-2007 Stanislaw Skowronek * Copyright (C) 2009 Johannes Dickgreber - * Copyright (C) 2011 Joshua Kinard + * Copyright (C) 2011 Joshua Kinard */ #include diff --git a/arch/mips/sgi-ip30/ip30-xtalk.c b/arch/mips/sgi-ip30/ip30-xtalk.c index 7ceb2b23ea1c..d798ee8c998c 100644 --- a/arch/mips/sgi-ip30/ip30-xtalk.c +++ b/arch/mips/sgi-ip30/ip30-xtalk.c @@ -3,7 +3,7 @@ * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support. * Copyright (C) 2004-2007 Stanislaw Skowronek * Copyright (C) 2009 Johannes Dickgreber - * Copyright (C) 2007, 2014-2016 Joshua Kinard + * Copyright (C) 2007, 2014-2016 Joshua Kinard */ #include diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index 0586ca7668b4..5dc867ea2c69 100644 --- a/arch/mips/txx9/generic/setup.c +++ b/arch/mips/txx9/generic/setup.c @@ -655,7 +655,7 @@ void __init txx9_iocled_init(unsigned long baseaddr, if (!iocled->mmioaddr) goto out_free; iocled->chip.get = txx9_iocled_get; - iocled->chip.set_rv = txx9_iocled_set; + iocled->chip.set = txx9_iocled_set; iocled->chip.direction_input = txx9_iocled_dir_in; iocled->chip.direction_output = txx9_iocled_dir_out; iocled->chip.label = "iocled"; @@ -776,7 +776,7 @@ struct txx9_sramc_dev { }; static ssize_t txx9_sram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { struct txx9_sramc_dev *dev = bin_attr->private; @@ -791,7 +791,7 @@ static ssize_t txx9_sram_read(struct file *filp, struct kobject *kobj, } static ssize_t txx9_sram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { struct txx9_sramc_dev *dev = bin_attr->private; diff --git a/arch/nios2/kernel/ptrace.c b/arch/nios2/kernel/ptrace.c index 9221c15972e6..c88f5cabc0c1 100644 --- a/arch/nios2/kernel/ptrace.c +++ b/arch/nios2/kernel/ptrace.c @@ -95,7 +95,7 @@ enum nios2_regset { static const struct user_regset nios2_regsets[] = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = NUM_PTRACE_REG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), diff --git a/arch/openrisc/include/asm/mmu.h b/arch/openrisc/include/asm/mmu.h index eb720110f3a2..e7826a681bc4 100644 --- a/arch/openrisc/include/asm/mmu.h +++ b/arch/openrisc/include/asm/mmu.h @@ -15,7 +15,7 @@ #ifndef __ASM_OPENRISC_MMU_H #define __ASM_OPENRISC_MMU_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ typedef unsigned long mm_context_t; #endif diff --git a/arch/openrisc/include/asm/page.h b/arch/openrisc/include/asm/page.h index c589e96035e1..85797f94d1d7 100644 --- a/arch/openrisc/include/asm/page.h +++ b/arch/openrisc/include/asm/page.h @@ -25,7 +25,7 @@ */ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define clear_page(page) memset((page), 0, PAGE_SIZE) #define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) @@ -55,10 +55,10 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) }) #define __pgprot(x) ((pgprot_t) { (x) }) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET)) #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) @@ -73,7 +73,7 @@ static inline unsigned long virt_to_pfn(const void *kaddr) #define virt_addr_valid(kaddr) (pfn_valid(virt_to_pfn(kaddr))) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #include #include diff --git a/arch/openrisc/include/asm/pgtable.h b/arch/openrisc/include/asm/pgtable.h index 5bd6463bd514..d33702831505 100644 --- a/arch/openrisc/include/asm/pgtable.h +++ b/arch/openrisc/include/asm/pgtable.h @@ -23,7 +23,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -430,5 +430,5 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) typedef pte_t *pte_addr_t; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_OPENRISC_PGTABLE_H */ diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index e05d1b59e24e..3ff893a67c13 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -39,7 +39,7 @@ */ #define TASK_UNMAPPED_BASE (TASK_SIZE / 8 * 3) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct task_struct; @@ -78,5 +78,5 @@ void show_registers(struct pt_regs *regs); #define cpu_relax() barrier() -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_OPENRISC_PROCESSOR_H */ diff --git a/arch/openrisc/include/asm/ptrace.h b/arch/openrisc/include/asm/ptrace.h index e5a282b67075..28facf2f3e00 100644 --- a/arch/openrisc/include/asm/ptrace.h +++ b/arch/openrisc/include/asm/ptrace.h @@ -27,7 +27,7 @@ * they share a cacheline (not done yet, though... future optimization). */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * This struct describes how the registers are laid out on the kernel stack * during a syscall or other kernel entry. @@ -147,7 +147,7 @@ static inline unsigned long regs_get_register(struct pt_regs *regs, return *(unsigned long *)((unsigned long)regs + offset); } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* * Offsets used by 'ptrace' system call interface. diff --git a/arch/openrisc/include/asm/setup.h b/arch/openrisc/include/asm/setup.h index 9acbc5deda69..dce9f4d3b378 100644 --- a/arch/openrisc/include/asm/setup.h +++ b/arch/openrisc/include/asm/setup.h @@ -8,7 +8,7 @@ #include #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ void __init or1k_early_setup(void *fdt); #endif diff --git a/arch/openrisc/include/asm/thread_info.h b/arch/openrisc/include/asm/thread_info.h index 4af3049c34c2..e338fff7efb0 100644 --- a/arch/openrisc/include/asm/thread_info.h +++ b/arch/openrisc/include/asm/thread_info.h @@ -17,7 +17,7 @@ #ifdef __KERNEL__ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include #endif @@ -38,7 +38,7 @@ * - if the contents of this structure are changed, the assembly constants * must also be changed */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct thread_info { struct task_struct *task; /* main task structure */ @@ -58,7 +58,7 @@ struct thread_info { * * preempt_count needs to be 1 initially, until the scheduler is functional. */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ @@ -75,7 +75,7 @@ register struct thread_info *current_thread_info_reg asm("r10"); #define get_thread_info(ti) get_task_struct((ti)->task) #define put_thread_info(ti) put_task_struct((ti)->task) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ /* * thread information flags diff --git a/arch/openrisc/include/uapi/asm/ptrace.h b/arch/openrisc/include/uapi/asm/ptrace.h index a77cc9915ca8..1f12a60d5a06 100644 --- a/arch/openrisc/include/uapi/asm/ptrace.h +++ b/arch/openrisc/include/uapi/asm/ptrace.h @@ -20,7 +20,7 @@ #ifndef _UAPI__ASM_OPENRISC_PTRACE_H #define _UAPI__ASM_OPENRISC_PTRACE_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * This is the layout of the regset returned by the GETREGSET ptrace call */ diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c index 3a7b5baaa450..af932a4ad306 100644 --- a/arch/openrisc/kernel/dma.c +++ b/arch/openrisc/kernel/dma.c @@ -72,7 +72,7 @@ void *arch_dma_set_uncached(void *cpu_addr, size_t size) * them and setting the cache-inhibit bit. */ mmap_write_lock(&init_mm); - error = walk_page_range_novma(&init_mm, va, va + size, + error = walk_kernel_page_table_range(va, va + size, &set_nocache_walk_ops, NULL, NULL); mmap_write_unlock(&init_mm); @@ -87,7 +87,7 @@ void arch_dma_clear_uncached(void *cpu_addr, size_t size) mmap_write_lock(&init_mm); /* walk_page_range shouldn't be able to fail here */ - WARN_ON(walk_page_range_novma(&init_mm, va, va + size, + WARN_ON(walk_kernel_page_table_range(va, va + size, &clear_nocache_walk_ops, NULL, NULL)); mmap_write_unlock(&init_mm); } diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c index 8430570d0620..552489b24855 100644 --- a/arch/openrisc/kernel/ptrace.c +++ b/arch/openrisc/kernel/ptrace.c @@ -124,7 +124,7 @@ enum or1k_regset { static const struct user_regset or1k_regsets[] = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), @@ -133,7 +133,7 @@ static const struct user_regset or1k_regsets[] = { }, #ifdef CONFIG_FPU [REGSET_FPU] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct __or1k_fpu_state) / sizeof(long), .size = sizeof(long), .align = sizeof(long), diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index fcc5973f7519..2efa4b08b7b8 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -81,7 +81,6 @@ config PARISC select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1) - select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY if DYNAMIC_FTRACE select HAVE_KPROBES_ON_FTRACE select HAVE_DYNAMIC_FTRACE_WITH_REGS diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 21b8166a6883..48ae3c79557a 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile @@ -39,7 +39,9 @@ endif export LD_BFD -# Set default 32 bits cross compilers for vdso +# Set default 32 bits cross compilers for vdso. +# This means that for 64BIT, both the 64-bit tools and the 32-bit tools +# need to be in the path. CC_ARCHES_32 = hppa hppa2.0 hppa1.1 CC_SUFFIXES = linux linux-gnu unknown-linux-gnu suse-linux CROSS32_COMPILE := $(call cc-cross-prefix, \ @@ -139,7 +141,7 @@ palo lifimage: vmlinuz fi @if test ! -f "$(PALOCONF)"; then \ cp $(srctree)/arch/parisc/defpalo.conf $(objtree)/palo.conf; \ - echo 'A generic palo config file ($(objree)/palo.conf) has been created for you.'; \ + echo 'A generic palo config file ($(objtree)/palo.conf) has been created for you.'; \ echo 'You should check it and re-run "make palo".'; \ echo 'WARNING: the "lifimage" file is now placed in this directory by default!'; \ false; \ diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h index 1a86a4370b29..2c139a4dbf4b 100644 --- a/arch/parisc/include/asm/pgtable.h +++ b/arch/parisc/include/asm/pgtable.h @@ -276,7 +276,7 @@ extern unsigned long *empty_zero_page; #define pte_none(x) (pte_val(x) == 0) #define pte_present(x) (pte_val(x) & _PAGE_PRESENT) #define pte_user(x) (pte_val(x) & _PAGE_USER) -#define pte_clear(mm, addr, xp) set_pte(xp, __pte(0)) +#define pte_clear(mm, addr, xp) set_pte_at((mm), (addr), (xp), __pte(0)) #define pmd_flag(x) (pmd_val(x) & PxD_FLAG_MASK) #define pmd_address(x) ((unsigned long)(pmd_val(x) &~ PxD_FLAG_MASK) << PxD_VALUE_SHIFT) @@ -392,6 +392,7 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr, } } #define set_ptes set_ptes +#define set_pte_at(mm, addr, ptep, pte) set_ptes(mm, addr, ptep, pte, 1) /* Used for deferring calls to flush_dcache_page() */ @@ -456,7 +457,7 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned if (!pte_young(pte)) { return 0; } - set_pte(ptep, pte_mkold(pte)); + set_pte_at(vma->vm_mm, addr, ptep, pte_mkold(pte)); return 1; } @@ -466,7 +467,7 @@ pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, pte_t *pt struct mm_struct; static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - set_pte(ptep, pte_wrprotect(*ptep)); + set_pte_at(mm, addr, ptep, pte_wrprotect(*ptep)); } #define pte_same(A,B) (pte_val(A) == pte_val(B)) diff --git a/arch/parisc/include/asm/special_insns.h b/arch/parisc/include/asm/special_insns.h index 51f40eaf7780..1013eeba31e5 100644 --- a/arch/parisc/include/asm/special_insns.h +++ b/arch/parisc/include/asm/special_insns.h @@ -32,6 +32,34 @@ pa; \ }) +/** + * prober_user() - Probe user read access + * @sr: Space regster. + * @va: Virtual address. + * + * Return: Non-zero if address is accessible. + * + * Due to the way _PAGE_READ is handled in TLB entries, we need + * a special check to determine whether a user address is accessible. + * The ldb instruction does the initial access check. If it is + * successful, the probe instruction checks user access rights. + */ +#define prober_user(sr, va) ({ \ + unsigned long read_allowed; \ + __asm__ __volatile__( \ + "copy %%r0,%0\n" \ + "8:\tldb 0(%%sr%1,%2),%%r0\n" \ + "\tproberi (%%sr%1,%2),%3,%0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \ + "or %%r0,%%r0,%%r0") \ + : "=&r" (read_allowed) \ + : "i" (sr), "r" (va), "i" (PRIV_USER) \ + : "memory" \ + ); \ + read_allowed; \ +}) + #define CR_EIEM 15 /* External Interrupt Enable Mask */ #define CR_CR16 16 /* CR16 Interval Timer */ #define CR_EIRR 23 /* External Interrupt Request Register */ diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 88d0ae5769dd..6c531d2c847e 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -42,9 +42,24 @@ __gu_err; \ }) -#define __get_user(val, ptr) \ -({ \ - __get_user_internal(SR_USER, val, ptr); \ +#define __probe_user_internal(sr, error, ptr) \ +({ \ + __asm__("\tproberi (%%sr%1,%2),%3,%0\n" \ + "\tcmpiclr,= 1,%0,%0\n" \ + "\tldi %4,%0\n" \ + : "=r"(error) \ + : "i"(sr), "r"(ptr), "i"(PRIV_USER), \ + "i"(-EFAULT)); \ +}) + +#define __get_user(val, ptr) \ +({ \ + register long __gu_err; \ + \ + __gu_err = __get_user_internal(SR_USER, val, ptr); \ + if (likely(!__gu_err)) \ + __probe_user_internal(SR_USER, __gu_err, ptr); \ + __gu_err; \ }) #define __get_user_asm(sr, val, ldx, ptr) \ diff --git a/arch/parisc/include/asm/video.h b/arch/parisc/include/asm/video.h index c5dff3223194..a9d50ebd6e76 100644 --- a/arch/parisc/include/asm/video.h +++ b/arch/parisc/include/asm/video.h @@ -6,7 +6,7 @@ struct device; -#if defined(CONFIG_STI_CORE) +#if defined(CONFIG_STI_CORE) && defined(CONFIG_VIDEO) bool video_is_primary_device(struct device *dev); #define video_is_primary_device video_is_primary_device #endif diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 1f2d5b7a7f5d..c16ec36dfee6 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -144,6 +144,9 @@ #define SO_PASSRIGHTS 0x4051 +#define SO_INQ 0x4052 +#define SCM_INQ SO_INQ + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index db531e58d70e..37ca484cc495 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -429,7 +429,7 @@ static inline pte_t *get_ptep(struct mm_struct *mm, unsigned long addr) return ptep; } -static inline bool pte_needs_flush(pte_t pte) +static inline bool pte_needs_cache_flush(pte_t pte) { return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NO_CACHE)) == (_PAGE_PRESENT | _PAGE_ACCESSED); @@ -630,7 +630,7 @@ static void flush_cache_page_if_present(struct vm_area_struct *vma, ptep = get_ptep(vma->vm_mm, vmaddr); if (ptep) { pte = ptep_get(ptep); - needs_flush = pte_needs_flush(pte); + needs_flush = pte_needs_cache_flush(pte); pte_unmap(ptep); } if (needs_flush) @@ -841,7 +841,7 @@ void flush_cache_vmap(unsigned long start, unsigned long end) } vm = find_vm_area((void *)start); - if (WARN_ON_ONCE(!vm)) { + if (!vm) { flush_cache_all(); return; } diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index ea57bcc21dc5..f4bf61a34701 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S @@ -499,6 +499,12 @@ * this happens is quite subtle, read below */ .macro make_insert_tlb spc,pte,prot,tmp space_to_prot \spc \prot /* create prot id from space */ + +#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT + /* need to drop DMB bit, as it's used as SPECIAL flag */ + depi 0,_PAGE_SPECIAL_BIT,1,\pte +#endif + /* The following is the real subtlety. This is depositing * T <-> _PAGE_REFTRAP * D <-> _PAGE_DIRTY @@ -511,17 +517,18 @@ * Finally, _PAGE_READ goes in the top bit of PL1 (so we * trigger an access rights trap in user space if the user * tries to read an unreadable page */ -#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT - /* need to drop DMB bit, as it's used as SPECIAL flag */ - depi 0,_PAGE_SPECIAL_BIT,1,\pte -#endif depd \pte,8,7,\prot /* PAGE_USER indicates the page can be read with user privileges, * so deposit X1|11 to PL1|PL2 (remember the upper bit of PL1 - * contains _PAGE_READ) */ + * contains _PAGE_READ). While the kernel can't directly write + * user pages which have _PAGE_WRITE zero, it can read pages + * which have _PAGE_READ zero (PL <= PL1). Thus, the kernel + * exception fault handler doesn't trigger when reading pages + * that aren't user read accessible */ extrd,u,*= \pte,_PAGE_USER_BIT+32,1,%r0 depdi 7,11,3,\prot + /* If we're a gateway page, drop PL2 back to zero for promotion * to kernel privilege (so we can execute the page as kernel). * Any privilege promotion page always denys read and write */ diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index ceb45f51d52e..8a17ab7e6e0b 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -562,12 +562,12 @@ static int gpr_set(struct task_struct *target, static const struct user_regset native_regsets[] = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), .regset_get = gpr_get, .set = gpr_set }, [REGSET_FP] = { - .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(__u64), .align = sizeof(__u64), .regset_get = fpr_get, .set = fpr_set } @@ -629,12 +629,12 @@ static int gpr32_set(struct task_struct *target, */ static const struct user_regset compat_regsets[] = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), .regset_get = gpr32_get, .set = gpr32_set }, [REGSET_FP] = { - .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(__u64), .align = sizeof(__u64), .regset_get = fpr_get, .set = fpr_set } diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 0fa81bf1466b..f58c4bccfbce 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -613,6 +613,9 @@ lws_compare_and_swap32: lws_compare_and_swap: /* Trigger memory reference interruptions without writing to memory */ 1: ldw 0(%r26), %r28 + proberi (%r26), PRIV_USER, %r28 + comb,=,n %r28, %r0, lws_fault /* backwards, likely not taken */ + nop 2: stbys,e %r0, 0(%r26) /* Calculate 8-bit hash index from virtual address */ @@ -767,6 +770,9 @@ cas2_lock_start: copy %r26, %r28 depi_safe 0, 31, 2, %r28 10: ldw 0(%r28), %r1 + proberi (%r28), PRIV_USER, %r1 + comb,=,n %r1, %r0, lws_fault /* backwards, likely not taken */ + nop 11: stbys,e %r0, 0(%r28) /* Calculate 8-bit hash index from virtual address */ @@ -951,41 +957,47 @@ atomic_xchg_begin: /* 8-bit exchange */ 1: ldb 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop copy %r23, %r20 depi_safe 0, 31, 2, %r20 b atomic_xchg_start 2: stbys,e %r0, 0(%r20) - nop - nop - nop /* 16-bit exchange */ 3: ldh 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop copy %r23, %r20 depi_safe 0, 31, 2, %r20 b atomic_xchg_start 4: stbys,e %r0, 0(%r20) - nop - nop - nop /* 32-bit exchange */ 5: ldw 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop b atomic_xchg_start 6: stbys,e %r0, 0(%r23) nop nop - nop - nop - nop /* 64-bit exchange */ #ifdef CONFIG_64BIT 7: ldd 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop 8: stdby,e %r0, 0(%r23) #else 7: ldw 0(%r24), %r20 8: ldw 4(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop copy %r23, %r20 depi_safe 0, 31, 2, %r20 9: stbys,e %r0, 0(%r20) diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index 94df3cb957e9..88a788a7b18d 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -466,3 +466,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/parisc/lib/memcpy.c b/arch/parisc/lib/memcpy.c index 5fc0c852c84c..69d65ffab312 100644 --- a/arch/parisc/lib/memcpy.c +++ b/arch/parisc/lib/memcpy.c @@ -12,6 +12,7 @@ #include #include #include +#include #define get_user_space() mfsp(SR_USER) #define get_kernel_space() SR_KERNEL @@ -32,9 +33,25 @@ EXPORT_SYMBOL(raw_copy_to_user); unsigned long raw_copy_from_user(void *dst, const void __user *src, unsigned long len) { + unsigned long start = (unsigned long) src; + unsigned long end = start + len; + unsigned long newlen = len; + mtsp(get_user_space(), SR_TEMP1); mtsp(get_kernel_space(), SR_TEMP2); - return pa_memcpy(dst, (void __force *)src, len); + + /* Check region is user accessible */ + if (start) + while (start < end) { + if (!prober_user(SR_TEMP1, start)) { + newlen = (start - (unsigned long) src); + break; + } + start += PAGE_SIZE; + /* align to page boundry which may have different permission */ + start = PAGE_ALIGN_DOWN(start); + } + return len - newlen + pa_memcpy(dst, (void __force *)src, newlen); } EXPORT_SYMBOL(raw_copy_from_user); diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index c39de84e98b0..f1785640b049 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -363,6 +363,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, mmap_read_unlock(mm); bad_area_nosemaphore: + if (!user_mode(regs) && fixup_exception(regs)) { + return; + } + if (user_mode(regs)) { int signo, si_code; diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c3e0cc83f120..93402a1d9c9f 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -127,8 +127,6 @@ config PPC select ARCH_ENABLE_MEMORY_HOTPLUG select ARCH_ENABLE_MEMORY_HOTREMOVE select ARCH_HAS_COPY_MC if PPC64 - select ARCH_HAS_CRC32 if PPC64 && ALTIVEC - select ARCH_HAS_CRC_T10DIF if PPC64 && ALTIVEC select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL select ARCH_HAS_DEBUG_VM_PGTABLE @@ -149,7 +147,6 @@ config PPC select ARCH_HAS_PMEM_API select ARCH_HAS_PREEMPT_LAZY select ARCH_HAS_PTDUMP - select ARCH_HAS_PTE_DEVMAP if PPC_BOOK3S_64 select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SCALED_CPUTIME if VIRT_CPU_ACCOUNTING_NATIVE && PPC_BOOK3S_64 select ARCH_HAS_SET_MEMORY @@ -246,7 +243,6 @@ config PPC select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_DESCRIPTORS if PPC64_ELF_ABI_V1 select HAVE_FUNCTION_ERROR_INJECTION diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index f3804103c56c..9753fb87217c 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -101,7 +101,7 @@ KBUILD_LDFLAGS += -m elf$(BITS)$(LDEMULATION) endif LDFLAGS_vmlinux-y := -Bstatic -LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie +LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie --no-dynamic-linker LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) += -z notext LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index a7ab087d412c..c47b78c1d3e7 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -243,13 +243,13 @@ $(obj)/wrapper.a: $(obj-wlib) FORCE hostprogs := addnote hack-coff mktree targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) zImage.lds -extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ +always-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds dtstree := $(src)/dts wrapper := $(src)/wrapper -wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ +wrapperbits := $(always-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ $(wrapper) FORCE ############# @@ -456,7 +456,7 @@ WRAPPER_DTSDIR := /usr/lib/kernel-wrapper/dts WRAPPER_BINDIR := /usr/sbin INSTALL := install -extra-installed := $(patsubst $(obj)/%, $(DESTDIR)$(WRAPPER_OBJDIR)/%, $(extra-y)) +extra-installed := $(patsubst $(obj)/%, $(DESTDIR)$(WRAPPER_OBJDIR)/%, $(always-y)) hostprogs-installed := $(patsubst %, $(DESTDIR)$(WRAPPER_BINDIR)/%, $(hostprogs)) wrapper-installed := $(DESTDIR)$(WRAPPER_BINDIR)/wrapper dts-installed := $(patsubst $(dtstree)/%, $(DESTDIR)$(WRAPPER_DTSDIR)/%, $(wildcard $(dtstree)/*.dts)) diff --git a/arch/powerpc/boot/dts/microwatt.dts b/arch/powerpc/boot/dts/microwatt.dts index c4e4d2a9b460..292b909ca9ce 100644 --- a/arch/powerpc/boot/dts/microwatt.dts +++ b/arch/powerpc/boot/dts/microwatt.dts @@ -4,7 +4,7 @@ / { #size-cells = <0x02>; #address-cells = <0x02>; - model-name = "microwatt"; + model = "microwatt"; compatible = "microwatt-soc"; aliases { @@ -37,7 +37,7 @@ cpus { ibm,powerpc-cpu-features { display-name = "Microwatt"; - isa = <3010>; + isa = <3100>; device_type = "cpu-features"; compatible = "ibm,powerpc-cpu-features"; diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index e09b37d7489d..a89cb3139ca8 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -6,6 +6,7 @@ */ /dts-v1/; +#include / { compatible = "fsl,mpc8315erdb"; @@ -358,6 +359,15 @@ pmc: power@b00 { interrupt-parent = <&ipic>; fsl,mpc8313-wakeup-timer = <>m1>; }; + + gpio: gpio-controller@c00 { + compatible = "fsl,mpc8314-gpio"; + reg = <0xc00 0x100>; + interrupts = <74 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&ipic>; + gpio-controller; + #gpio-cells = <2>; + }; }; pci0: pci@e0008500 { diff --git a/arch/powerpc/boot/install.sh b/arch/powerpc/boot/install.sh index 101fcb397a0f..c3df6c27ce75 100755 --- a/arch/powerpc/boot/install.sh +++ b/arch/powerpc/boot/install.sh @@ -19,19 +19,19 @@ set -e # this should work for both the pSeries zImage and the iSeries vmlinux.sm -image_name=`basename $2` +image_name=$(basename "$2") echo "Warning: '${INSTALLKERNEL}' command not available... Copying" \ "directly to $4/$image_name-$1" >&2 -if [ -f $4/$image_name-$1 ]; then - mv $4/$image_name-$1 $4/$image_name-$1.old +if [ -f "$4"/"$image_name"-"$1" ]; then + mv "$4"/"$image_name"-"$1" "$4"/"$image_name"-"$1".old fi -if [ -f $4/System.map-$1 ]; then - mv $4/System.map-$1 $4/System-$1.old +if [ -f "$4"/System.map-"$1" ]; then + mv "$4"/System.map-"$1" "$4"/System-"$1".old fi -cat $2 > $4/$image_name-$1 -cp $3 $4/System.map-$1 +cat "$2" > "$4"/"$image_name"-"$1" +cp "$3" "$4"/System.map-"$1" diff --git a/arch/powerpc/configs/44x/akebono_defconfig b/arch/powerpc/configs/44x/akebono_defconfig index fde4824f235e..1882eb2da354 100644 --- a/arch/powerpc/configs/44x/akebono_defconfig +++ b/arch/powerpc/configs/44x/akebono_defconfig @@ -128,6 +128,5 @@ CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0x00010000 CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH=0x33f CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_SHA1_PPC=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig index 3347192b77b8..7a31b52e92e1 100644 --- a/arch/powerpc/configs/cell_defconfig +++ b/arch/powerpc/configs/cell_defconfig @@ -62,7 +62,6 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_TCPMSS=m CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index 379229c982a4..d06388b0f66e 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -46,7 +46,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_IDLE=y -CONFIG_HZ_100=y +CONFIG_HZ_1000=y CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y CONFIG_PPC_UV=y @@ -322,7 +322,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD5_PPC=m CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA1_PPC=m CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m @@ -341,3 +340,4 @@ CONFIG_KVM_BOOK3S_64_HV=m CONFIG_VHOST_NET=m CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CALLER=y +CONFIG_KALLSYMS_ALL=y diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 3423c405cad4..ce34597e9f3e 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -57,7 +57,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_PMAC64=y -CONFIG_HZ_100=y +CONFIG_HZ_1000=y CONFIG_PPC_TRANSACTIONAL_MEM=y CONFIG_KEXEC=y CONFIG_KEXEC_FILE=y @@ -388,7 +388,6 @@ CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_LZO=m CONFIG_CRYPTO_MD5_PPC=m -CONFIG_CRYPTO_SHA1_PPC=m CONFIG_CRYPTO_AES_GCM_P10=m CONFIG_CRYPTO_DEV_NX=y CONFIG_CRYPTO_DEV_NX_ENCRYPT=m @@ -466,3 +465,4 @@ CONFIG_TEST_MEMCAT_P=m CONFIG_TEST_MEMINIT=m CONFIG_TEST_FREE_PAGES=m CONFIG_MEMTEST=y +CONFIG_KALLSYMS_ALL=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index f96f8ed9856c..b082c1fae13c 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -252,7 +252,6 @@ CONFIG_NET_SCH_DSMARK=m CONFIG_NET_SCH_NETEM=m CONFIG_NET_SCH_INGRESS=m CONFIG_NET_CLS_BASIC=m -CONFIG_NET_CLS_TCINDEX=m CONFIG_NET_CLS_ROUTE4=m CONFIG_NET_CLS_FW=m CONFIG_NET_CLS_U32=m @@ -434,6 +433,7 @@ CONFIG_DM9102=m CONFIG_ULI526X=m CONFIG_PCMCIA_XIRCOM=m CONFIG_DL2K=m +CONFIG_SUNDANCE=m CONFIG_S2IO=m CONFIG_FEC_MPC52xx=m CONFIG_GIANFAR=m diff --git a/arch/powerpc/crypto/Kconfig b/arch/powerpc/crypto/Kconfig index caaa359f4742..cfe39fc221cf 100644 --- a/arch/powerpc/crypto/Kconfig +++ b/arch/powerpc/crypto/Kconfig @@ -23,22 +23,6 @@ config CRYPTO_MD5_PPC Architecture: powerpc -config CRYPTO_SHA1_PPC - tristate "Hash functions: SHA-1" - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: powerpc - -config CRYPTO_SHA1_PPC_SPE - tristate "Hash functions: SHA-1 (SPE)" - depends on SPE - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: powerpc using - - SPE (Signal Processing Engine) extensions - config CRYPTO_AES_PPC_SPE tristate "Ciphers: AES, modes: ECB/CBC/CTR/XTS (SPE)" depends on SPE diff --git a/arch/powerpc/crypto/Makefile b/arch/powerpc/crypto/Makefile index 8c2936ae466f..bc8fd27344b8 100644 --- a/arch/powerpc/crypto/Makefile +++ b/arch/powerpc/crypto/Makefile @@ -7,16 +7,12 @@ obj-$(CONFIG_CRYPTO_AES_PPC_SPE) += aes-ppc-spe.o obj-$(CONFIG_CRYPTO_MD5_PPC) += md5-ppc.o -obj-$(CONFIG_CRYPTO_SHA1_PPC) += sha1-powerpc.o -obj-$(CONFIG_CRYPTO_SHA1_PPC_SPE) += sha1-ppc-spe.o obj-$(CONFIG_CRYPTO_AES_GCM_P10) += aes-gcm-p10-crypto.o obj-$(CONFIG_CRYPTO_DEV_VMX_ENCRYPT) += vmx-crypto.o obj-$(CONFIG_CRYPTO_CURVE25519_PPC64) += curve25519-ppc64le.o aes-ppc-spe-y := aes-spe-core.o aes-spe-keys.o aes-tab-4k.o aes-spe-modes.o aes-spe-glue.o md5-ppc-y := md5-asm.o md5-glue.o -sha1-powerpc-y := sha1-powerpc-asm.o sha1.o -sha1-ppc-spe-y := sha1-spe-asm.o sha1-spe-glue.o aes-gcm-p10-crypto-y := aes-gcm-p10-glue.o aes-gcm-p10.o ghashp10-ppc.o aesp10-ppc.o vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o aes_xts.o ghash.o curve25519-ppc64le-y := curve25519-ppc64le-core.o curve25519-ppc64le_asm.o diff --git a/arch/powerpc/crypto/sha1-spe-glue.c b/arch/powerpc/crypto/sha1-spe-glue.c deleted file mode 100644 index 04c88e173ce1..000000000000 --- a/arch/powerpc/crypto/sha1-spe-glue.c +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Glue code for SHA-1 implementation for SPE instructions (PPC) - * - * Based on generic implementation. - * - * Copyright (c) 2015 Markus Stockhausen - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * MAX_BYTES defines the number of bytes that are allowed to be processed - * between preempt_disable() and preempt_enable(). SHA1 takes ~1000 - * operations per 64 bytes. e500 cores can issue two arithmetic instructions - * per clock cycle using one 32/64 bit unit (SU1) and one 32 bit unit (SU2). - * Thus 2KB of input data will need an estimated maximum of 18,000 cycles. - * Headroom for cache misses included. Even with the low end model clocked - * at 667 MHz this equals to a critical time window of less than 27us. - * - */ -#define MAX_BYTES 2048 - -asmlinkage void ppc_spe_sha1_transform(u32 *state, const u8 *src, u32 blocks); - -static void spe_begin(void) -{ - /* We just start SPE operations and will save SPE registers later. */ - preempt_disable(); - enable_kernel_spe(); -} - -static void spe_end(void) -{ - disable_kernel_spe(); - /* reenable preemption */ - preempt_enable(); -} - -static void ppc_spe_sha1_block(struct sha1_state *sctx, const u8 *src, - int blocks) -{ - do { - int unit = min(blocks, MAX_BYTES / SHA1_BLOCK_SIZE); - - spe_begin(); - ppc_spe_sha1_transform(sctx->state, src, unit); - spe_end(); - - src += unit * SHA1_BLOCK_SIZE; - blocks -= unit; - } while (blocks); -} - -static int ppc_spe_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_base_do_update_blocks(desc, data, len, ppc_spe_sha1_block); -} - -static int ppc_spe_sha1_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *out) -{ - sha1_base_do_finup(desc, src, len, ppc_spe_sha1_block); - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = ppc_spe_sha1_update, - .finup = ppc_spe_sha1_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-ppc-spe", - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init ppc_spe_sha1_mod_init(void) -{ - return crypto_register_shash(&alg); -} - -static void __exit ppc_spe_sha1_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_init(ppc_spe_sha1_mod_init); -module_exit(ppc_spe_sha1_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, SPE optimized"); - -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_ALIAS_CRYPTO("sha1-ppc-spe"); diff --git a/arch/powerpc/crypto/sha1.c b/arch/powerpc/crypto/sha1.c deleted file mode 100644 index 4593946aa9b3..000000000000 --- a/arch/powerpc/crypto/sha1.c +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * - * powerpc implementation of the SHA1 Secure Hash Algorithm. - * - * Derived from cryptoapi implementation, adapted for in-place - * scatterlist interface. - * - * Derived from "crypto/sha1.c" - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - */ -#include -#include -#include -#include -#include - -asmlinkage void powerpc_sha_transform(u32 *state, const u8 *src); - -static void powerpc_sha_block(struct sha1_state *sctx, const u8 *data, - int blocks) -{ - do { - powerpc_sha_transform(sctx->state, data); - data += 64; - } while (--blocks); -} - -static int powerpc_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_base_do_update_blocks(desc, data, len, powerpc_sha_block); -} - -/* Add padding and return the message digest. */ -static int powerpc_sha1_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *out) -{ - sha1_base_do_finup(desc, src, len, powerpc_sha_block); - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = powerpc_sha1_update, - .finup = powerpc_sha1_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-powerpc", - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_powerpc_mod_init(void) -{ - return crypto_register_shash(&alg); -} - -static void __exit sha1_powerpc_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_init(sha1_powerpc_mod_init); -module_exit(sha1_powerpc_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm"); - -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_ALIAS_CRYPTO("sha1-powerpc"); diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h index aa90a048f319..7132392fa7cd 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h @@ -168,12 +168,6 @@ extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm, extern int hash__has_transparent_hugepage(void); #endif -static inline pmd_t hash__pmd_mkdevmap(pmd_t pmd) -{ - BUG(); - return pmd; -} - #endif /* !__ASSEMBLY__ */ #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */ diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h index 0bf6fd0bf42a..0fb5b7da9478 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h @@ -259,7 +259,7 @@ static inline void mark_hpte_slot_valid(unsigned char *hpte_slot_array, */ static inline int hash__pmd_trans_huge(pmd_t pmd) { - return !!((pmd_val(pmd) & (_PAGE_PTE | H_PAGE_THP_HUGE | _PAGE_DEVMAP)) == + return !!((pmd_val(pmd) & (_PAGE_PTE | H_PAGE_THP_HUGE)) == (_PAGE_PTE | H_PAGE_THP_HUGE)); } @@ -281,11 +281,6 @@ extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm, extern int hash__has_transparent_hugepage(void); #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ -static inline pmd_t hash__pmd_mkdevmap(pmd_t pmd) -{ - return __pmd(pmd_val(pmd) | (_PAGE_PTE | H_PAGE_THP_HUGE | _PAGE_DEVMAP)); -} - #endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_BOOK3S_64_HASH_64K_H */ diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h index a2ddcbb3fcb9..c19800365315 100644 --- a/arch/powerpc/include/asm/book3s/64/pgtable.h +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h @@ -88,7 +88,6 @@ #define _PAGE_SOFT_DIRTY _RPAGE_SW3 /* software: software dirty tracking */ #define _PAGE_SPECIAL _RPAGE_SW2 /* software: special page */ -#define _PAGE_DEVMAP _RPAGE_SW1 /* software: ZONE_DEVICE page */ /* * Drivers request for cache inhibited pte mapping using _PAGE_NO_CACHE @@ -109,7 +108,7 @@ */ #define _HPAGE_CHG_MASK (PTE_RPN_MASK | _PAGE_HPTEFLAGS | _PAGE_DIRTY | \ _PAGE_ACCESSED | H_PAGE_THP_HUGE | _PAGE_PTE | \ - _PAGE_SOFT_DIRTY | _PAGE_DEVMAP) + _PAGE_SOFT_DIRTY) /* * user access blocked by key */ @@ -123,7 +122,7 @@ */ #define _PAGE_CHG_MASK (PTE_RPN_MASK | _PAGE_HPTEFLAGS | _PAGE_DIRTY | \ _PAGE_ACCESSED | _PAGE_SPECIAL | _PAGE_PTE | \ - _PAGE_SOFT_DIRTY | _PAGE_DEVMAP) + _PAGE_SOFT_DIRTY) /* * We define 2 sets of base prot bits, one for basic pages (ie, @@ -609,24 +608,6 @@ static inline pte_t pte_mkhuge(pte_t pte) return pte; } -static inline pte_t pte_mkdevmap(pte_t pte) -{ - return __pte_raw(pte_raw(pte) | cpu_to_be64(_PAGE_SPECIAL | _PAGE_DEVMAP)); -} - -/* - * This is potentially called with a pmd as the argument, in which case it's not - * safe to check _PAGE_DEVMAP unless we also confirm that _PAGE_PTE is set. - * That's because the bit we use for _PAGE_DEVMAP is not reserved for software - * use in page directory entries (ie. non-ptes). - */ -static inline int pte_devmap(pte_t pte) -{ - __be64 mask = cpu_to_be64(_PAGE_DEVMAP | _PAGE_PTE); - - return (pte_raw(pte) & mask) == mask; -} - static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { /* FIXME!! check whether this need to be a conditional */ @@ -1379,36 +1360,6 @@ static inline bool arch_needs_pgtable_deposit(void) } extern void serialize_against_pte_lookup(struct mm_struct *mm); - -static inline pmd_t pmd_mkdevmap(pmd_t pmd) -{ - if (radix_enabled()) - return radix__pmd_mkdevmap(pmd); - return hash__pmd_mkdevmap(pmd); -} - -static inline pud_t pud_mkdevmap(pud_t pud) -{ - if (radix_enabled()) - return radix__pud_mkdevmap(pud); - BUG(); - return pud; -} - -static inline int pmd_devmap(pmd_t pmd) -{ - return pte_devmap(pmd_pte(pmd)); -} - -static inline int pud_devmap(pud_t pud) -{ - return pte_devmap(pud_pte(pud)); -} - -static inline int pgd_devmap(pgd_t pgd) -{ - return 0; -} #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION diff --git a/arch/powerpc/include/asm/book3s/64/pkeys.h b/arch/powerpc/include/asm/book3s/64/pkeys.h index 5b178139f3c0..ff911b4251d9 100644 --- a/arch/powerpc/include/asm/book3s/64/pkeys.h +++ b/arch/powerpc/include/asm/book3s/64/pkeys.h @@ -5,7 +5,7 @@ #include -static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) +static inline u64 vmflag_to_pte_pkey_bits(vm_flags_t vm_flags) { if (!mmu_has_feature(MMU_FTR_PKEY)) return 0x0UL; diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h index 8f55ff74bb68..df23a8267e4d 100644 --- a/arch/powerpc/include/asm/book3s/64/radix.h +++ b/arch/powerpc/include/asm/book3s/64/radix.h @@ -264,7 +264,7 @@ static inline int radix__p4d_bad(p4d_t p4d) static inline int radix__pmd_trans_huge(pmd_t pmd) { - return (pmd_val(pmd) & (_PAGE_PTE | _PAGE_DEVMAP)) == _PAGE_PTE; + return (pmd_val(pmd) & _PAGE_PTE) == _PAGE_PTE; } static inline pmd_t radix__pmd_mkhuge(pmd_t pmd) @@ -274,7 +274,7 @@ static inline pmd_t radix__pmd_mkhuge(pmd_t pmd) static inline int radix__pud_trans_huge(pud_t pud) { - return (pud_val(pud) & (_PAGE_PTE | _PAGE_DEVMAP)) == _PAGE_PTE; + return (pud_val(pud) & _PAGE_PTE) == _PAGE_PTE; } static inline pud_t radix__pud_mkhuge(pud_t pud) @@ -315,16 +315,6 @@ static inline int radix__has_transparent_pud_hugepage(void) } #endif -static inline pmd_t radix__pmd_mkdevmap(pmd_t pmd) -{ - return __pmd(pmd_val(pmd) | (_PAGE_PTE | _PAGE_DEVMAP)); -} - -static inline pud_t radix__pud_mkdevmap(pud_t pud) -{ - return __pud(pud_val(pud) | (_PAGE_PTE | _PAGE_DEVMAP)); -} - struct vmem_altmap; struct dev_pagemap; extern int __meminit radix__vmemmap_create_mapping(unsigned long start, diff --git a/arch/powerpc/include/asm/floppy.h b/arch/powerpc/include/asm/floppy.h index f8ce178b43b7..34abf8bea2cc 100644 --- a/arch/powerpc/include/asm/floppy.h +++ b/arch/powerpc/include/asm/floppy.h @@ -144,9 +144,12 @@ static int hard_dma_setup(char *addr, unsigned long size, int mode, int io) bus_addr = 0; } - if (!bus_addr) /* need to map it */ + if (!bus_addr) { /* need to map it */ bus_addr = dma_map_single(&isa_bridge_pcidev->dev, addr, size, dir); + if (dma_mapping_error(&isa_bridge_pcidev->dev, bus_addr)) + return -ENOMEM; + } /* remember this one as prev */ prev_addr = addr; diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 6df6dbbe1e7c..ea6c8dc400d2 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -270,6 +270,7 @@ #define H_QUERY_INT_STATE 0x1E4 #define H_POLL_PENDING 0x1D8 #define H_ILLAN_ATTRIBUTES 0x244 +#define H_ADD_LOGICAL_LAN_BUFFERS 0x248 #define H_MODIFY_HEA_QP 0x250 #define H_QUERY_HEA_QP 0x254 #define H_QUERY_HEA 0x258 diff --git a/arch/powerpc/include/asm/mman.h b/arch/powerpc/include/asm/mman.h index 42a51a993d94..912f78a956a1 100644 --- a/arch/powerpc/include/asm/mman.h +++ b/arch/powerpc/include/asm/mman.h @@ -14,7 +14,7 @@ #include #include -static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot, +static inline vm_flags_t arch_calc_vm_prot_bits(unsigned long prot, unsigned long pkey) { #ifdef CONFIG_PPC_MEM_KEYS diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index 59a2c7dbc78f..28e752138996 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -30,9 +30,9 @@ extern u32 reserved_allocation_mask; /* bits set for reserved keys */ #endif -static inline u64 pkey_to_vmflag_bits(u16 pkey) +static inline vm_flags_t pkey_to_vmflag_bits(u16 pkey) { - return (((u64)pkey << VM_PKEY_SHIFT) & ARCH_VM_PKEY_FLAGS); + return (((vm_flags_t)pkey << VM_PKEY_SHIFT) & ARCH_VM_PKEY_FLAGS); } static inline int vma_pkey(struct vm_area_struct *vma) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 4312bcb913a4..8053b24afc39 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -425,6 +425,7 @@ #define PPC_RAW_SC() (0x44000002) #define PPC_RAW_SYNC() (0x7c0004ac) #define PPC_RAW_ISYNC() (0x4c00012c) +#define PPC_RAW_LWSYNC() (0x7c2004ac) /* * Define what the VSX XX1 form instructions will look like, then add diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 02897f4b0dbf..b891910fce8a 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -183,7 +183,7 @@ /* * Used to name C functions called from asm */ -#ifdef CONFIG_PPC_KERNEL_PCREL +#if defined(__powerpc64__) && defined(CONFIG_PPC_KERNEL_PCREL) #define CFUNC(name) name@notoc #else #define CFUNC(name) name diff --git a/arch/powerpc/include/uapi/asm/eeh.h b/arch/powerpc/include/uapi/asm/eeh.h index 28186071fafc..3b5c47ff3fc4 100644 --- a/arch/powerpc/include/uapi/asm/eeh.h +++ b/arch/powerpc/include/uapi/asm/eeh.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * 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. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2015 * * Authors: Gavin Shan diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h index 2c145da3b774..b5211e413829 100644 --- a/arch/powerpc/include/uapi/asm/ioctls.h +++ b/arch/powerpc/include/uapi/asm/ioctls.h @@ -23,10 +23,10 @@ #define TCSETSW _IOW('t', 21, struct termios) #define TCSETSF _IOW('t', 22, struct termios) -#define TCGETA _IOR('t', 23, struct termio) -#define TCSETA _IOW('t', 24, struct termio) -#define TCSETAW _IOW('t', 25, struct termio) -#define TCSETAF _IOW('t', 28, struct termio) +#define TCGETA 0x40147417 /* _IOR('t', 23, struct termio) */ +#define TCSETA 0x80147418 /* _IOW('t', 24, struct termio) */ +#define TCSETAW 0x80147419 /* _IOW('t', 25, struct termio) */ +#define TCSETAF 0x8014741c /* _IOW('t', 28, struct termio) */ #define TCSBRK _IO('t', 29) #define TCXONC _IO('t', 30) diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index eaeda001784e..077c5437f521 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * 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. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2007 * * Authors: Hollis Blanchard diff --git a/arch/powerpc/include/uapi/asm/kvm_para.h b/arch/powerpc/include/uapi/asm/kvm_para.h index a809b1b44ddf..ac596064d4c7 100644 --- a/arch/powerpc/include/uapi/asm/kvm_para.h +++ b/arch/powerpc/include/uapi/asm/kvm_para.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * 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. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2008 * * Authors: Hollis Blanchard diff --git a/arch/powerpc/include/uapi/asm/ps3fb.h b/arch/powerpc/include/uapi/asm/ps3fb.h index fd7e3a0d35d5..b1c6b0cd9e80 100644 --- a/arch/powerpc/include/uapi/asm/ps3fb.h +++ b/arch/powerpc/include/uapi/asm/ps3fb.h @@ -2,19 +2,6 @@ /* * Copyright (C) 2006 Sony Computer Entertainment Inc. * Copyright 2006, 2007 Sony Corporation - * - * 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; version 2 of the License. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _ASM_POWERPC_PS3FB_H_ diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index fb2b95267022..2f0a2e69c607 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -199,7 +199,9 @@ obj-$(CONFIG_ALTIVEC) += vector.o obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o obj64-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_entry_64.o -extra-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init_check +ifdef KBUILD_BUILTIN +always-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init_check +endif obj-$(CONFIG_PPC64) += $(obj64-y) obj-$(CONFIG_PPC32) += $(obj32-y) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 83fe99861eb1..bb836f02101c 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1139,6 +1139,7 @@ int eeh_unfreeze_pe(struct eeh_pe *pe) return ret; } +EXPORT_SYMBOL_GPL(eeh_unfreeze_pe); static struct pci_device_id eeh_reset_ids[] = { @@ -1208,16 +1209,16 @@ int eeh_dev_open(struct pci_dev *pdev) struct eeh_dev *edev; int ret = -ENODEV; - mutex_lock(&eeh_dev_mutex); + guard(mutex)(&eeh_dev_mutex); /* No PCI device ? */ if (!pdev) - goto out; + return ret; /* No EEH device or PE ? */ edev = pci_dev_to_eeh_dev(pdev); if (!edev || !edev->pe) - goto out; + return ret; /* * The PE might have been put into frozen state, but we @@ -1227,16 +1228,12 @@ int eeh_dev_open(struct pci_dev *pdev) */ ret = eeh_pe_change_owner(edev->pe); if (ret) - goto out; + return ret; /* Increase PE's pass through count */ atomic_inc(&edev->pe->pass_dev_cnt); - mutex_unlock(&eeh_dev_mutex); return 0; -out: - mutex_unlock(&eeh_dev_mutex); - return ret; } EXPORT_SYMBOL_GPL(eeh_dev_open); @@ -1252,22 +1249,20 @@ void eeh_dev_release(struct pci_dev *pdev) { struct eeh_dev *edev; - mutex_lock(&eeh_dev_mutex); + guard(mutex)(&eeh_dev_mutex); /* No PCI device ? */ if (!pdev) - goto out; + return; /* No EEH device ? */ edev = pci_dev_to_eeh_dev(pdev); if (!edev || !edev->pe || !eeh_pe_passed(edev->pe)) - goto out; + return; /* Decrease PE's pass through count */ WARN_ON(atomic_dec_if_positive(&edev->pe->pass_dev_cnt) < 0); eeh_pe_change_owner(edev->pe); -out: - mutex_unlock(&eeh_dev_mutex); } EXPORT_SYMBOL(eeh_dev_release); @@ -1509,6 +1504,8 @@ int eeh_pe_configure(struct eeh_pe *pe) /* Invalid PE ? */ if (!pe) return -ENODEV; + else + ret = eeh_ops->configure_bridge(pe); return ret; } diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 7efe04c68f0f..48ad0116f359 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -257,13 +257,12 @@ static void eeh_pe_report_edev(struct eeh_dev *edev, eeh_report_fn fn, struct pci_driver *driver; enum pci_ers_result new_result; - pci_lock_rescan_remove(); pdev = edev->pdev; if (pdev) get_device(&pdev->dev); - pci_unlock_rescan_remove(); if (!pdev) { eeh_edev_info(edev, "no device"); + *result = PCI_ERS_RESULT_DISCONNECT; return; } device_lock(&pdev->dev); @@ -304,8 +303,9 @@ static void eeh_pe_report(const char *name, struct eeh_pe *root, struct eeh_dev *edev, *tmp; pr_info("EEH: Beginning: '%s'\n", name); - eeh_for_each_pe(root, pe) eeh_pe_for_each_dev(pe, edev, tmp) - eeh_pe_report_edev(edev, fn, result); + eeh_for_each_pe(root, pe) + eeh_pe_for_each_dev(pe, edev, tmp) + eeh_pe_report_edev(edev, fn, result); if (result) pr_info("EEH: Finished:'%s' with aggregate recovery state:'%s'\n", name, pci_ers_result_name(*result)); @@ -383,6 +383,8 @@ static void eeh_dev_restore_state(struct eeh_dev *edev, void *userdata) if (!edev) return; + pci_lock_rescan_remove(); + /* * The content in the config space isn't saved because * the blocked config space on some adapters. We have @@ -393,14 +395,19 @@ static void eeh_dev_restore_state(struct eeh_dev *edev, void *userdata) if (list_is_last(&edev->entry, &edev->pe->edevs)) eeh_pe_restore_bars(edev->pe); + pci_unlock_rescan_remove(); return; } pdev = eeh_dev_to_pci_dev(edev); - if (!pdev) + if (!pdev) { + pci_unlock_rescan_remove(); return; + } pci_restore_state(pdev); + + pci_unlock_rescan_remove(); } /** @@ -647,9 +654,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus, if (any_passed || driver_eeh_aware || (pe->type & EEH_PE_VF)) { eeh_pe_dev_traverse(pe, eeh_rmv_device, rmv_data); } else { - pci_lock_rescan_remove(); pci_hp_remove_devices(bus); - pci_unlock_rescan_remove(); } /* @@ -665,8 +670,6 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus, if (rc) return rc; - pci_lock_rescan_remove(); - /* Restore PE */ eeh_ops->configure_bridge(pe); eeh_pe_restore_bars(pe); @@ -674,7 +677,6 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus, /* Clear frozen state */ rc = eeh_clear_pe_frozen_state(pe, false); if (rc) { - pci_unlock_rescan_remove(); return rc; } @@ -709,7 +711,6 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus, pe->tstamp = tstamp; pe->freeze_count = cnt; - pci_unlock_rescan_remove(); return 0; } @@ -843,10 +844,13 @@ void eeh_handle_normal_event(struct eeh_pe *pe) {LIST_HEAD_INIT(rmv_data.removed_vf_list), 0}; int devices = 0; + pci_lock_rescan_remove(); + bus = eeh_pe_bus_get(pe); if (!bus) { pr_err("%s: Cannot find PCI bus for PHB#%x-PE#%x\n", __func__, pe->phb->global_number, pe->addr); + pci_unlock_rescan_remove(); return; } @@ -907,7 +911,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe) /* FIXME: Use the same format as dump_stack() */ pr_err("EEH: Call Trace:\n"); for (i = 0; i < pe->trace_entries; i++) - pr_err("EEH: [%pK] %pS\n", ptrs[i], ptrs[i]); + pr_err("EEH: [%p] %pS\n", ptrs[i], ptrs[i]); pe->trace_entries = 0; } @@ -1094,10 +1098,15 @@ void eeh_handle_normal_event(struct eeh_pe *pe) eeh_pe_state_clear(pe, EEH_PE_PRI_BUS, true); eeh_pe_dev_mode_mark(pe, EEH_DEV_REMOVED); - pci_lock_rescan_remove(); - pci_hp_remove_devices(bus); - pci_unlock_rescan_remove(); + bus = eeh_pe_bus_get(pe); + if (bus) + pci_hp_remove_devices(bus); + else + pr_err("%s: PCI bus for PHB#%x-PE#%x disappeared\n", + __func__, pe->phb->global_number, pe->addr); + /* The passed PE should no longer be used */ + pci_unlock_rescan_remove(); return; } @@ -1114,6 +1123,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe) eeh_clear_slot_attention(edev->pdev); eeh_pe_state_clear(pe, EEH_PE_RECOVERING, true); + + pci_unlock_rescan_remove(); } /** @@ -1132,6 +1143,7 @@ void eeh_handle_special_event(void) unsigned long flags; int rc; + pci_lock_rescan_remove(); do { rc = eeh_ops->next_error(&pe); @@ -1171,10 +1183,12 @@ void eeh_handle_special_event(void) break; case EEH_NEXT_ERR_NONE: + pci_unlock_rescan_remove(); return; default: pr_warn("%s: Invalid value %d from next_error()\n", __func__, rc); + pci_unlock_rescan_remove(); return; } @@ -1186,7 +1200,9 @@ void eeh_handle_special_event(void) if (rc == EEH_NEXT_ERR_FROZEN_PE || rc == EEH_NEXT_ERR_FENCED_PHB) { eeh_pe_state_mark(pe, EEH_PE_RECOVERING); + pci_unlock_rescan_remove(); eeh_handle_normal_event(pe); + pci_lock_rescan_remove(); } else { eeh_for_each_pe(pe, tmp_pe) eeh_pe_for_each_dev(tmp_pe, edev, tmp_edev) @@ -1199,7 +1215,6 @@ void eeh_handle_special_event(void) eeh_report_failure, NULL); eeh_set_channel_state(pe, pci_channel_io_perm_failure); - pci_lock_rescan_remove(); list_for_each_entry(hose, &hose_list, list_node) { phb_pe = eeh_phb_pe_get(hose); if (!phb_pe || @@ -1218,7 +1233,6 @@ void eeh_handle_special_event(void) } pci_hp_remove_devices(bus); } - pci_unlock_rescan_remove(); } /* @@ -1228,4 +1242,6 @@ void eeh_handle_special_event(void) if (rc == EEH_NEXT_ERR_DEAD_IOC) break; } while (rc != EEH_NEXT_ERR_NONE); + + pci_unlock_rescan_remove(); } diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index d283d281d28e..e740101fadf3 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -671,10 +671,12 @@ static void eeh_bridge_check_link(struct eeh_dev *edev) eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val); /* Check link */ - if (!edev->pdev->link_active_reporting) { - eeh_edev_dbg(edev, "No link reporting capability\n"); - msleep(1000); - return; + if (edev->pdev) { + if (!edev->pdev->link_active_reporting) { + eeh_edev_dbg(edev, "No link reporting capability\n"); + msleep(1000); + return; + } } /* Wait the link is up until timeout (5s) */ diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 8ca49e40c473..5782e743fd27 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -333,7 +333,7 @@ static __init u64 fadump_calculate_reserve_size(void) * memory at a predefined offset. */ ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), - &size, &base, NULL, NULL); + &size, &base, NULL, NULL, NULL); if (ret == 0 && size > 0) { unsigned long max_size; @@ -1373,15 +1373,12 @@ static void fadump_free_elfcorehdr_buf(void) static void fadump_invalidate_release_mem(void) { - mutex_lock(&fadump_mutex); - if (!fw_dump.dump_active) { - mutex_unlock(&fadump_mutex); - return; + scoped_guard(mutex, &fadump_mutex) { + if (!fw_dump.dump_active) + return; + fadump_cleanup(); } - fadump_cleanup(); - mutex_unlock(&fadump_mutex); - fadump_free_elfcorehdr_buf(); fadump_release_memory(fw_dump.boot_mem_top, memblock_end_of_DRAM()); fadump_free_cpu_notes_buf(); diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c index 5b3c093611ba..7209d00a9c25 100644 --- a/arch/powerpc/kernel/kvm.c +++ b/arch/powerpc/kernel/kvm.c @@ -632,19 +632,19 @@ static void __init kvm_check_ins(u32 *inst, u32 features) #endif } - switch (inst_no_rt & ~KVM_MASK_RB) { #ifdef CONFIG_PPC_BOOK3S_32 + switch (inst_no_rt & ~KVM_MASK_RB) { case KVM_INST_MTSRIN: if (features & KVM_MAGIC_FEAT_SR) { u32 inst_rb = _inst & KVM_MASK_RB; kvm_patch_ins_mtsrin(inst, inst_rt, inst_rb); } break; -#endif } +#endif - switch (_inst) { #ifdef CONFIG_BOOKE + switch (_inst) { case KVM_INST_WRTEEI_0: kvm_patch_ins_wrteei_0(inst); break; @@ -652,8 +652,8 @@ static void __init kvm_check_ins(u32 *inst, u32 features) case KVM_INST_WRTEEI_1: kvm_patch_ins_wrtee(inst, 0, 1); break; -#endif } +#endif } extern u32 kvm_template_start[]; diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 1da2f6e7d2a1..ae1906bfe8a5 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -54,9 +54,10 @@ static int legacy_serial_console = -1; static const upf_t legacy_port_flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_PORT; -static unsigned int tsi_serial_in(struct uart_port *p, int offset) +static u32 tsi_serial_in(struct uart_port *p, unsigned int offset) { - unsigned int tmp; + u32 tmp; + offset = offset << p->regshift; if (offset == UART_IIR) { tmp = readl(p->membase + (UART_IIR & ~3)); @@ -65,7 +66,7 @@ static unsigned int tsi_serial_in(struct uart_port *p, int offset) return readb(p->membase + offset); } -static void tsi_serial_out(struct uart_port *p, int offset, int value) +static void tsi_serial_out(struct uart_port *p, unsigned int offset, u32 value) { offset = offset << p->regshift; if (!((offset == UART_IER) && (value & UART_IER_UUE))) @@ -77,6 +78,8 @@ static int __init add_legacy_port(struct device_node *np, int want_index, phys_addr_t taddr, unsigned long irq, upf_t flags, int irq_check_parent) { + struct plat_serial8250_port *legacy_port; + struct legacy_serial_info *legacy_info; const __be32 *clk, *spd, *rs; u32 clock = BASE_BAUD * 16; u32 shift = 0; @@ -110,16 +113,17 @@ static int __init add_legacy_port(struct device_node *np, int want_index, if (index >= legacy_serial_count) legacy_serial_count = index + 1; + legacy_port = &legacy_serial_ports[index]; + legacy_info = &legacy_serial_infos[index]; + /* Check if there is a port who already claimed our slot */ - if (legacy_serial_infos[index].np != NULL) { + if (legacy_info->np != NULL) { /* if we still have some room, move it, else override */ if (legacy_serial_count < MAX_LEGACY_SERIAL_PORTS) { printk(KERN_DEBUG "Moved legacy port %d -> %d\n", index, legacy_serial_count); - legacy_serial_ports[legacy_serial_count] = - legacy_serial_ports[index]; - legacy_serial_infos[legacy_serial_count] = - legacy_serial_infos[index]; + legacy_serial_ports[legacy_serial_count] = *legacy_port; + legacy_serial_infos[legacy_serial_count] = *legacy_info; legacy_serial_count++; } else { printk(KERN_DEBUG "Replacing legacy port %d\n", index); @@ -127,36 +131,32 @@ static int __init add_legacy_port(struct device_node *np, int want_index, } /* Now fill the entry */ - memset(&legacy_serial_ports[index], 0, - sizeof(struct plat_serial8250_port)); + memset(legacy_port, 0, sizeof(*legacy_port)); if (iotype == UPIO_PORT) - legacy_serial_ports[index].iobase = base; + legacy_port->iobase = base; else - legacy_serial_ports[index].mapbase = base; + legacy_port->mapbase = base; - legacy_serial_ports[index].iotype = iotype; - legacy_serial_ports[index].uartclk = clock; - legacy_serial_ports[index].irq = irq; - legacy_serial_ports[index].flags = flags; - legacy_serial_ports[index].regshift = shift; - legacy_serial_infos[index].taddr = taddr; - legacy_serial_infos[index].np = of_node_get(np); - legacy_serial_infos[index].clock = clock; - legacy_serial_infos[index].speed = spd ? be32_to_cpup(spd) : 0; - legacy_serial_infos[index].irq_check_parent = irq_check_parent; + legacy_port->iotype = iotype; + legacy_port->uartclk = clock; + legacy_port->irq = irq; + legacy_port->flags = flags; + legacy_port->regshift = shift; + legacy_info->taddr = taddr; + legacy_info->np = of_node_get(np); + legacy_info->clock = clock; + legacy_info->speed = spd ? be32_to_cpup(spd) : 0; + legacy_info->irq_check_parent = irq_check_parent; if (iotype == UPIO_TSI) { - legacy_serial_ports[index].serial_in = tsi_serial_in; - legacy_serial_ports[index].serial_out = tsi_serial_out; + legacy_port->serial_in = tsi_serial_in; + legacy_port->serial_out = tsi_serial_out; } - printk(KERN_DEBUG "Found legacy serial port %d for %pOF\n", - index, np); - printk(KERN_DEBUG " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n", + printk(KERN_DEBUG "Found legacy serial port %d for %pOF\n", index, np); + printk(KERN_DEBUG " %s=%pa, taddr=%pa, irq=%lx, clk=%d, speed=%d\n", (iotype == UPIO_PORT) ? "port" : "mem", - (unsigned long long)base, (unsigned long long)taddr, irq, - legacy_serial_ports[index].uartclk, - legacy_serial_infos[index].speed); + &base, &taddr, irq, legacy_port->uartclk, legacy_info->speed); return index; } diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 9ea74973d78d..6f444d0822d8 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -141,6 +141,9 @@ void pci_hp_add_devices(struct pci_bus *bus) struct pci_controller *phb; struct device_node *dn = pci_bus_to_OF_node(bus); + if (!dn) + return; + phb = pci_bus_to_host(bus); mode = PCI_PROBE_NORMAL; diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 69623b9045d5..3090b97258ae 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -15,8 +15,8 @@ has_renamed_memintrinsics() { - grep -q "^CONFIG_KASAN=y$" ${KCONFIG_CONFIG} && \ - ! grep -q "^CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y" ${KCONFIG_CONFIG} + grep -q "^CONFIG_KASAN=y$" "${KCONFIG_CONFIG}" && \ + ! grep -q "^CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y" "${KCONFIG_CONFIG}" } if has_renamed_memintrinsics @@ -42,15 +42,15 @@ check_section() { file=$1 section=$2 - size=$(objdump -h -j $section $file 2>/dev/null | awk "\$2 == \"$section\" {print \$3}") + size=$(objdump -h -j "$section" "$file" 2>/dev/null | awk "\$2 == \"$section\" {print \$3}") size=${size:-0} - if [ $size -ne 0 ]; then + if [ "$size" -ne 0 ]; then ERROR=1 echo "Error: Section $section not empty in prom_init.c" >&2 fi } -for UNDEF in $($NM -u $OBJ | awk '{print $2}') +for UNDEF in $($NM -u "$OBJ" | awk '{print $2}') do # On 64-bit nm gives us the function descriptors, which have # a leading . on the name, so strip it off here. @@ -87,8 +87,8 @@ do fi done -check_section $OBJ .data -check_section $OBJ .bss -check_section $OBJ .init.data +check_section "$OBJ" .data +check_section "$OBJ" .bss +check_section "$OBJ" .init.data exit $ERROR diff --git a/arch/powerpc/kernel/ptrace/ptrace-view.c b/arch/powerpc/kernel/ptrace/ptrace-view.c index c1819e0a6684..0310f9097e39 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-view.c +++ b/arch/powerpc/kernel/ptrace/ptrace-view.c @@ -568,114 +568,114 @@ static int pkey_set(struct task_struct *target, const struct user_regset *regset static const struct user_regset native_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), .regset_get = gpr_get, .set = gpr_set }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), .regset_get = fpr_get, .set = fpr_set }, #ifdef CONFIG_ALTIVEC [REGSET_VMX] = { - .core_note_type = NT_PPC_VMX, .n = 34, + USER_REGSET_NOTE_TYPE(PPC_VMX), .n = 34, .size = sizeof(vector128), .align = sizeof(vector128), .active = vr_active, .regset_get = vr_get, .set = vr_set }, #endif #ifdef CONFIG_VSX [REGSET_VSX] = { - .core_note_type = NT_PPC_VSX, .n = 32, + USER_REGSET_NOTE_TYPE(PPC_VSX), .n = 32, .size = sizeof(double), .align = sizeof(double), .active = vsr_active, .regset_get = vsr_get, .set = vsr_set }, #endif #ifdef CONFIG_SPE [REGSET_SPE] = { - .core_note_type = NT_PPC_SPE, .n = 35, + USER_REGSET_NOTE_TYPE(PPC_SPE), .n = 35, .size = sizeof(u32), .align = sizeof(u32), .active = evr_active, .regset_get = evr_get, .set = evr_set }, #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM [REGSET_TM_CGPR] = { - .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PPC_TM_CGPR), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), .active = tm_cgpr_active, .regset_get = tm_cgpr_get, .set = tm_cgpr_set }, [REGSET_TM_CFPR] = { - .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PPC_TM_CFPR), .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set }, [REGSET_TM_CVMX] = { - .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, + USER_REGSET_NOTE_TYPE(PPC_TM_CVMX), .n = ELF_NVMX, .size = sizeof(vector128), .align = sizeof(vector128), .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set }, [REGSET_TM_CVSX] = { - .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, + USER_REGSET_NOTE_TYPE(PPC_TM_CVSX), .n = ELF_NVSX, .size = sizeof(double), .align = sizeof(double), .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set }, [REGSET_TM_SPR] = { - .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, + USER_REGSET_NOTE_TYPE(PPC_TM_SPR), .n = ELF_NTMSPRREG, .size = sizeof(u64), .align = sizeof(u64), .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set }, [REGSET_TM_CTAR] = { - .core_note_type = NT_PPC_TM_CTAR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CTAR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set }, [REGSET_TM_CPPR] = { - .core_note_type = NT_PPC_TM_CPPR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CPPR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set }, [REGSET_TM_CDSCR] = { - .core_note_type = NT_PPC_TM_CDSCR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CDSCR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set }, #endif #ifdef CONFIG_PPC64 [REGSET_PPR] = { - .core_note_type = NT_PPC_PPR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_PPR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = ppr_get, .set = ppr_set }, [REGSET_DSCR] = { - .core_note_type = NT_PPC_DSCR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_DSCR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = dscr_get, .set = dscr_set }, #endif #ifdef CONFIG_PPC_BOOK3S_64 [REGSET_TAR] = { - .core_note_type = NT_PPC_TAR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TAR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = tar_get, .set = tar_set }, [REGSET_EBB] = { - .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, + USER_REGSET_NOTE_TYPE(PPC_EBB), .n = ELF_NEBB, .size = sizeof(u64), .align = sizeof(u64), .active = ebb_active, .regset_get = ebb_get, .set = ebb_set }, [REGSET_PMR] = { - .core_note_type = NT_PPC_PMU, .n = ELF_NPMU, + USER_REGSET_NOTE_TYPE(PPC_PMU), .n = ELF_NPMU, .size = sizeof(u64), .align = sizeof(u64), .active = pmu_active, .regset_get = pmu_get, .set = pmu_set }, [REGSET_DEXCR] = { - .core_note_type = NT_PPC_DEXCR, .n = ELF_NDEXCR, + USER_REGSET_NOTE_TYPE(PPC_DEXCR), .n = ELF_NDEXCR, .size = sizeof(u64), .align = sizeof(u64), .active = dexcr_active, .regset_get = dexcr_get }, #ifdef CONFIG_CHECKPOINT_RESTORE [REGSET_HASHKEYR] = { - .core_note_type = NT_PPC_HASHKEYR, .n = ELF_NHASHKEYR, + USER_REGSET_NOTE_TYPE(PPC_HASHKEYR), .n = ELF_NHASHKEYR, .size = sizeof(u64), .align = sizeof(u64), .active = hashkeyr_active, .regset_get = hashkeyr_get, .set = hashkeyr_set }, @@ -683,7 +683,7 @@ static const struct user_regset native_regsets[] = { #endif #ifdef CONFIG_PPC_MEM_KEYS [REGSET_PKEY] = { - .core_note_type = NT_PPC_PKEY, .n = ELF_NPKEY, + USER_REGSET_NOTE_TYPE(PPC_PKEY), .n = ELF_NPKEY, .size = sizeof(u64), .align = sizeof(u64), .active = pkey_active, .regset_get = pkey_get, .set = pkey_set }, @@ -843,92 +843,92 @@ static int gpr32_set(struct task_struct *target, */ static const struct user_regset compat_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), .regset_get = gpr32_get, .set = gpr32_set }, [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), .regset_get = fpr_get, .set = fpr_set }, #ifdef CONFIG_ALTIVEC [REGSET_VMX] = { - .core_note_type = NT_PPC_VMX, .n = 34, + USER_REGSET_NOTE_TYPE(PPC_VMX), .n = 34, .size = sizeof(vector128), .align = sizeof(vector128), .active = vr_active, .regset_get = vr_get, .set = vr_set }, #endif #ifdef CONFIG_SPE [REGSET_SPE] = { - .core_note_type = NT_PPC_SPE, .n = 35, + USER_REGSET_NOTE_TYPE(PPC_SPE), .n = 35, .size = sizeof(u32), .align = sizeof(u32), .active = evr_active, .regset_get = evr_get, .set = evr_set }, #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM [REGSET_TM_CGPR] = { - .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, + USER_REGSET_NOTE_TYPE(PPC_TM_CGPR), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), .active = tm_cgpr_active, .regset_get = tm_cgpr32_get, .set = tm_cgpr32_set }, [REGSET_TM_CFPR] = { - .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, + USER_REGSET_NOTE_TYPE(PPC_TM_CFPR), .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set }, [REGSET_TM_CVMX] = { - .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, + USER_REGSET_NOTE_TYPE(PPC_TM_CVMX), .n = ELF_NVMX, .size = sizeof(vector128), .align = sizeof(vector128), .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set }, [REGSET_TM_CVSX] = { - .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, + USER_REGSET_NOTE_TYPE(PPC_TM_CVSX), .n = ELF_NVSX, .size = sizeof(double), .align = sizeof(double), .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set }, [REGSET_TM_SPR] = { - .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, + USER_REGSET_NOTE_TYPE(PPC_TM_SPR), .n = ELF_NTMSPRREG, .size = sizeof(u64), .align = sizeof(u64), .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set }, [REGSET_TM_CTAR] = { - .core_note_type = NT_PPC_TM_CTAR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CTAR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set }, [REGSET_TM_CPPR] = { - .core_note_type = NT_PPC_TM_CPPR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CPPR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set }, [REGSET_TM_CDSCR] = { - .core_note_type = NT_PPC_TM_CDSCR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TM_CDSCR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set }, #endif #ifdef CONFIG_PPC64 [REGSET_PPR] = { - .core_note_type = NT_PPC_PPR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_PPR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = ppr_get, .set = ppr_set }, [REGSET_DSCR] = { - .core_note_type = NT_PPC_DSCR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_DSCR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = dscr_get, .set = dscr_set }, #endif #ifdef CONFIG_PPC_BOOK3S_64 [REGSET_TAR] = { - .core_note_type = NT_PPC_TAR, .n = 1, + USER_REGSET_NOTE_TYPE(PPC_TAR), .n = 1, .size = sizeof(u64), .align = sizeof(u64), .regset_get = tar_get, .set = tar_set }, [REGSET_EBB] = { - .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, + USER_REGSET_NOTE_TYPE(PPC_EBB), .n = ELF_NEBB, .size = sizeof(u64), .align = sizeof(u64), .active = ebb_active, .regset_get = ebb_get, .set = ebb_set }, diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 5407024881e5..583dc16e9d3c 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -312,13 +312,13 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, { struct rtas_update_flash_t *const uf = &rtas_update_flash_data; char *p; - int next_free, rc; + int next_free; struct flash_block_list *fl; - mutex_lock(&rtas_update_flash_mutex); + guard(mutex)(&rtas_update_flash_mutex); if (uf->status == FLASH_AUTH || count == 0) - goto out; /* discard data */ + return count; /* discard data */ /* In the case that the image is not ready for flashing, the memory * allocated for the block list will be freed upon the release of the @@ -327,7 +327,7 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, if (uf->flist == NULL) { uf->flist = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); if (!uf->flist) - goto nomem; + return -ENOMEM; } fl = uf->flist; @@ -338,7 +338,7 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, /* Need to allocate another block_list */ fl->next = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); if (!fl->next) - goto nomem; + return -ENOMEM; fl = fl->next; next_free = 0; } @@ -347,25 +347,17 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, count = RTAS_BLK_SIZE; p = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); if (!p) - goto nomem; + return -ENOMEM; if(copy_from_user(p, buffer, count)) { kmem_cache_free(flash_block_cache, p); - rc = -EFAULT; - goto error; + return -EFAULT; } fl->blocks[next_free].data = p; fl->blocks[next_free].length = count; fl->num_blocks++; -out: - mutex_unlock(&rtas_update_flash_mutex); - return count; -nomem: - rc = -ENOMEM; -error: - mutex_unlock(&rtas_update_flash_mutex); - return rc; + return count; } /* @@ -405,19 +397,18 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf, static const char reject_str[] = "0"; static const char commit_str[] = "1"; char stkbuf[10]; - int op, rc; + int op; - mutex_lock(&rtas_manage_flash_mutex); + guard(mutex)(&rtas_manage_flash_mutex); if ((args_buf->status == MANAGE_AUTH) || (count == 0)) - goto out; + return count; op = -1; if (buf) { if (count > 9) count = 9; - rc = -EFAULT; if (copy_from_user (stkbuf, buf, count)) - goto error; + return -EFAULT; if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) op = RTAS_REJECT_TMP_IMG; else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) @@ -425,18 +416,11 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf, } if (op == -1) { /* buf is empty, or contains invalid string */ - rc = -EINVAL; - goto error; + return -EINVAL; } manage_flash(args_buf, op); -out: - mutex_unlock(&rtas_manage_flash_mutex); return count; - -error: - mutex_unlock(&rtas_manage_flash_mutex); - return rc; } /* @@ -499,16 +483,14 @@ static ssize_t validate_flash_write(struct file *file, const char __user *buf, { struct rtas_validate_flash_t *const args_buf = &rtas_validate_flash_data; - int rc; - mutex_lock(&rtas_validate_flash_mutex); + guard(mutex)(&rtas_validate_flash_mutex); /* We are only interested in the first 4K of the * candidate image */ if ((*off >= VALIDATE_BUF_SIZE) || (args_buf->status == VALIDATE_AUTH)) { *off += count; - mutex_unlock(&rtas_validate_flash_mutex); return count; } @@ -519,20 +501,14 @@ static ssize_t validate_flash_write(struct file *file, const char __user *buf, args_buf->status = VALIDATE_INCOMPLETE; } - if (!access_ok(buf, count)) { - rc = -EFAULT; - goto done; - } - if (copy_from_user(args_buf->buf + *off, buf, count)) { - rc = -EFAULT; - goto done; - } + if (!access_ok(buf, count)) + return -EFAULT; + + if (copy_from_user(args_buf->buf + *off, buf, count)) + return -EFAULT; *off += count; - rc = count; -done: - mutex_unlock(&rtas_validate_flash_mutex); - return rc; + return count; } static int validate_flash_release(struct inode *inode, struct file *file) diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c index afb690a172b4..ec900bce0257 100644 --- a/arch/powerpc/kernel/secvar-sysfs.c +++ b/arch/powerpc/kernel/secvar-sysfs.c @@ -121,7 +121,7 @@ static struct attribute *secvar_attrs[] = { static const struct attribute_group secvar_attr_group = { .attrs = secvar_attrs, - .bin_attrs_new = secvar_bin_attrs, + .bin_attrs = secvar_bin_attrs, }; __ATTRIBUTE_GROUPS(secvar_attr); diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 7284c8021eeb..8fd7cbf3bd04 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -141,10 +141,7 @@ void __init check_smt_enabled(void) smt_enabled_at_boot = 0; else { int smt; - int rc; - - rc = kstrtoint(smt_enabled_cmdline, 10, &smt); - if (!rc) + if (!kstrtoint(smt_enabled_cmdline, 10, &smt)) smt_enabled_at_boot = min(threads_per_core, smt); } diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 5ac7084eebc0..f59e4b9cc207 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -1700,28 +1700,23 @@ static void __init build_sched_topology(void) #ifdef CONFIG_SCHED_SMT if (has_big_cores) { pr_info("Big cores detected but using small core scheduling\n"); - powerpc_topology[i++] = (struct sched_domain_topology_level){ - smallcore_smt_mask, powerpc_smt_flags, SD_INIT_NAME(SMT) - }; + powerpc_topology[i++] = + SDTL_INIT(smallcore_smt_mask, powerpc_smt_flags, SMT); } else { - powerpc_topology[i++] = (struct sched_domain_topology_level){ - cpu_smt_mask, powerpc_smt_flags, SD_INIT_NAME(SMT) - }; + powerpc_topology[i++] = SDTL_INIT(cpu_smt_mask, powerpc_smt_flags, SMT); } #endif if (shared_caches) { - powerpc_topology[i++] = (struct sched_domain_topology_level){ - shared_cache_mask, powerpc_shared_cache_flags, SD_INIT_NAME(CACHE) - }; + powerpc_topology[i++] = + SDTL_INIT(shared_cache_mask, powerpc_shared_cache_flags, CACHE); } + if (has_coregroup_support()) { - powerpc_topology[i++] = (struct sched_domain_topology_level){ - cpu_mc_mask, powerpc_shared_proc_flags, SD_INIT_NAME(MC) - }; + powerpc_topology[i++] = + SDTL_INIT(cpu_mc_mask, powerpc_shared_proc_flags, MC); } - powerpc_topology[i++] = (struct sched_domain_topology_level){ - cpu_cpu_mask, powerpc_shared_proc_flags, SD_INIT_NAME(PKG) - }; + + powerpc_topology[i++] = SDTL_INIT(cpu_cpu_mask, powerpc_shared_proc_flags, PKG); /* There must be one trailing NULL entry left. */ BUG_ON(i >= ARRAY_SIZE(powerpc_topology) - 1); diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl index 9a084bdb8926..b453e80dfc00 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl @@ -558,3 +558,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/powerpc/kernel/vdso/Makefile b/arch/powerpc/kernel/vdso/Makefile index e8824f933326..8834dfe9d727 100644 --- a/arch/powerpc/kernel/vdso/Makefile +++ b/arch/powerpc/kernel/vdso/Makefile @@ -53,7 +53,7 @@ ldflags-$(CONFIG_LD_ORPHAN_WARN) += -Wl,--orphan-handling=$(CONFIG_LD_ORPHAN_WAR ldflags-y += $(filter-out $(CC_AUTO_VAR_INIT_ZERO_ENABLER) $(CC_FLAGS_FTRACE) -Wa$(comma)%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) CC32FLAGS := -m32 -CC32FLAGSREMOVE := -mcmodel=medium -mabi=elfv1 -mabi=elfv2 -mcall-aixdesc +CC32FLAGSREMOVE := -mcmodel=medium -mabi=elfv1 -mabi=elfv2 -mcall-aixdesc -mpcrel ifdef CONFIG_CC_IS_CLANG # This flag is supported by clang for 64-bit but not 32-bit so it will cause # an unused command line flag warning for this file. diff --git a/arch/powerpc/kexec/core.c b/arch/powerpc/kexec/core.c index 00e9c267b912..d1a2d755381c 100644 --- a/arch/powerpc/kexec/core.c +++ b/arch/powerpc/kexec/core.c @@ -110,7 +110,7 @@ void __init arch_reserve_crashkernel(void) /* use common parsing */ ret = parse_crashkernel(boot_command_line, total_mem_sz, &crash_size, - &crash_base, NULL, NULL); + &crash_base, NULL, NULL, NULL); if (ret) return; diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c index 3a6592a31a10..03f8c34fa0a2 100644 --- a/arch/powerpc/kvm/book3s_hv_uvmem.c +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c @@ -393,7 +393,7 @@ static int kvmppc_memslot_page_merge(struct kvm *kvm, { unsigned long gfn = memslot->base_gfn; unsigned long end, start = gfn_to_hva(kvm, gfn); - unsigned long vm_flags; + vm_flags_t vm_flags; int ret = 0; struct vm_area_struct *vma; int merge_flag = (merge) ? MADV_MERGEABLE : MADV_UNMERGEABLE; diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 153587741864..2ba057171ebe 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -69,7 +69,7 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) /* * Common checks before entering the guest world. Call with interrupts - * disabled. + * enabled. * * returns: * diff --git a/arch/powerpc/kvm/trace_book3s.h b/arch/powerpc/kvm/trace_book3s.h index 372a82fa2de3..9260ddbd557f 100644 --- a/arch/powerpc/kvm/trace_book3s.h +++ b/arch/powerpc/kvm/trace_book3s.h @@ -25,6 +25,7 @@ {0xe00, "H_DATA_STORAGE"}, \ {0xe20, "H_INST_STORAGE"}, \ {0xe40, "H_EMUL_ASSIST"}, \ + {0xea0, "H_VIRT"}, \ {0xf00, "PERFMON"}, \ {0xf20, "ALTIVEC"}, \ {0xf40, "VSX"} diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 481f968e42c7..f14ecab674a3 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -3,8 +3,6 @@ # Makefile for ppc-specific library files.. # -obj-y += crypto/ - CFLAGS_code-patching.o += -fno-stack-protector CFLAGS_feature-fixups.o += -fno-stack-protector @@ -80,10 +78,4 @@ 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_CRC32_ARCH) += crc32-powerpc.o -crc32-powerpc-y := crc32.o crc32c-vpmsum_asm.o - -obj-$(CONFIG_CRC_T10DIF_ARCH) += crc-t10dif-powerpc.o -crc-t10dif-powerpc-y := crc-t10dif.o crct10dif-vpmsum_asm.o - obj-$(CONFIG_PPC64) += $(obj64-y) diff --git a/arch/powerpc/mm/book3s64/hash_hugepage.c b/arch/powerpc/mm/book3s64/hash_hugepage.c index 15d6f3ea7178..cdfd4fe75edb 100644 --- a/arch/powerpc/mm/book3s64/hash_hugepage.c +++ b/arch/powerpc/mm/book3s64/hash_hugepage.c @@ -54,7 +54,7 @@ int __hash_page_thp(unsigned long ea, unsigned long access, unsigned long vsid, /* * Make sure this is thp or devmap entry */ - if (!(old_pmd & (H_PAGE_THP_HUGE | _PAGE_DEVMAP))) + if (!(old_pmd & H_PAGE_THP_HUGE)) return 0; rflags = htab_convert_pte_flags(new_pmd, flags); diff --git a/arch/powerpc/mm/book3s64/hash_pgtable.c b/arch/powerpc/mm/book3s64/hash_pgtable.c index 988948d69bc1..82d31177630b 100644 --- a/arch/powerpc/mm/book3s64/hash_pgtable.c +++ b/arch/powerpc/mm/book3s64/hash_pgtable.c @@ -195,7 +195,7 @@ unsigned long hash__pmd_hugepage_update(struct mm_struct *mm, unsigned long addr unsigned long old; #ifdef CONFIG_DEBUG_VM - WARN_ON(!hash__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp)); + WARN_ON(!hash__pmd_trans_huge(*pmdp)); assert_spin_locked(pmd_lockptr(mm, pmdp)); #endif @@ -227,7 +227,6 @@ pmd_t hash__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long addres VM_BUG_ON(address & ~HPAGE_PMD_MASK); VM_BUG_ON(pmd_trans_huge(*pmdp)); - VM_BUG_ON(pmd_devmap(*pmdp)); pmd = *pmdp; pmd_clear(pmdp); diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 5158aefe4873..4693c464fc5a 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -343,7 +343,7 @@ static inline bool hash_supports_debug_pagealloc(void) static u8 *linear_map_hash_slots; static unsigned long linear_map_hash_count; static DEFINE_RAW_SPINLOCK(linear_map_hash_lock); -static void hash_debug_pagealloc_alloc_slots(void) +static __init void hash_debug_pagealloc_alloc_slots(void) { if (!hash_supports_debug_pagealloc()) return; @@ -409,7 +409,7 @@ static DEFINE_RAW_SPINLOCK(linear_map_kf_hash_lock); static phys_addr_t kfence_pool; -static inline void hash_kfence_alloc_pool(void) +static __init void hash_kfence_alloc_pool(void) { if (!kfence_early_init_enabled()) goto err; @@ -445,7 +445,7 @@ static inline void hash_kfence_alloc_pool(void) disable_kfence(); } -static inline void hash_kfence_map_pool(void) +static __init void hash_kfence_map_pool(void) { unsigned long kfence_pool_start, kfence_pool_end; unsigned long prot = pgprot_val(PAGE_KERNEL); diff --git a/arch/powerpc/mm/book3s64/hugetlbpage.c b/arch/powerpc/mm/book3s64/hugetlbpage.c index 83c3361b358b..2bcbbf9d85ac 100644 --- a/arch/powerpc/mm/book3s64/hugetlbpage.c +++ b/arch/powerpc/mm/book3s64/hugetlbpage.c @@ -74,7 +74,7 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid, } while(!pte_xchg(ptep, __pte(old_pte), __pte(new_pte))); /* Make sure this is a hugetlb entry */ - if (old_pte & (H_PAGE_THP_HUGE | _PAGE_DEVMAP)) + if (old_pte & H_PAGE_THP_HUGE) return 0; rflags = htab_convert_pte_flags(new_pte, flags); diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c index 0db01e10a3f8..c9431ae7f78a 100644 --- a/arch/powerpc/mm/book3s64/pgtable.c +++ b/arch/powerpc/mm/book3s64/pgtable.c @@ -62,7 +62,7 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, unsigned long address, { int changed; #ifdef CONFIG_DEBUG_VM - WARN_ON(!pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp)); + WARN_ON(!pmd_trans_huge(*pmdp)); assert_spin_locked(pmd_lockptr(vma->vm_mm, pmdp)); #endif changed = !pmd_same(*(pmdp), entry); @@ -82,7 +82,6 @@ int pudp_set_access_flags(struct vm_area_struct *vma, unsigned long address, { int changed; #ifdef CONFIG_DEBUG_VM - WARN_ON(!pud_devmap(*pudp)); assert_spin_locked(pud_lockptr(vma->vm_mm, pudp)); #endif changed = !pud_same(*(pudp), entry); @@ -204,8 +203,8 @@ pmd_t pmdp_huge_get_and_clear_full(struct vm_area_struct *vma, { pmd_t pmd; VM_BUG_ON(addr & ~HPAGE_PMD_MASK); - VM_BUG_ON((pmd_present(*pmdp) && !pmd_trans_huge(*pmdp) && - !pmd_devmap(*pmdp)) || !pmd_present(*pmdp)); + VM_BUG_ON((pmd_present(*pmdp) && !pmd_trans_huge(*pmdp)) || + !pmd_present(*pmdp)); pmd = pmdp_huge_get_and_clear(vma->vm_mm, addr, pmdp); /* * if it not a fullmm flush, then we can possibly end up converting @@ -223,8 +222,7 @@ pud_t pudp_huge_get_and_clear_full(struct vm_area_struct *vma, pud_t pud; VM_BUG_ON(addr & ~HPAGE_PMD_MASK); - VM_BUG_ON((pud_present(*pudp) && !pud_devmap(*pudp)) || - !pud_present(*pudp)); + VM_BUG_ON(!pud_present(*pudp)); pud = pudp_huge_get_and_clear(vma->vm_mm, addr, pudp); /* * if it not a fullmm flush, then we can possibly end up converting @@ -644,7 +642,7 @@ unsigned long memremap_compat_align(void) EXPORT_SYMBOL_GPL(memremap_compat_align); #endif -pgprot_t vm_get_page_prot(unsigned long vm_flags) +pgprot_t vm_get_page_prot(vm_flags_t vm_flags) { unsigned long prot; diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 9f764bc42b8c..be523e5fe9c5 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -363,7 +363,7 @@ static int __meminit create_physical_mapping(unsigned long start, } #ifdef CONFIG_KFENCE -static inline phys_addr_t alloc_kfence_pool(void) +static __init phys_addr_t alloc_kfence_pool(void) { phys_addr_t kfence_pool; @@ -393,7 +393,7 @@ static inline phys_addr_t alloc_kfence_pool(void) return 0; } -static inline void map_kfence_pool(phys_addr_t kfence_pool) +static __init void map_kfence_pool(phys_addr_t kfence_pool) { if (!kfence_pool) return; @@ -1122,18 +1122,25 @@ int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, in pte_t *pte; /* - * Make sure we align the start vmemmap addr so that we calculate - * the correct start_pfn in altmap boundary check to decided whether - * we should use altmap or RAM based backing memory allocation. Also - * the address need to be aligned for set_pte operation. - - * If the start addr is already PMD_SIZE aligned we will try to use - * a pmd mapping. We don't want to be too aggressive here beacause - * that will cause more allocations in RAM. So only if the namespace - * vmemmap start addr is PMD_SIZE aligned we will use PMD mapping. + * If altmap is present, Make sure we align the start vmemmap addr + * to PAGE_SIZE so that we calculate the correct start_pfn in + * altmap boundary check to decide whether we should use altmap or + * RAM based backing memory allocation. Also the address need to be + * aligned for set_pte operation. If the start addr is already + * PMD_SIZE aligned and with in the altmap boundary then we will + * try to use a pmd size altmap mapping else we go for page size + * mapping. + * + * If altmap is not present, align the vmemmap addr to PMD_SIZE and + * always allocate a PMD size page for vmemmap backing. + * */ - start = ALIGN_DOWN(start, PAGE_SIZE); + if (altmap) + start = ALIGN_DOWN(start, PAGE_SIZE); + else + start = ALIGN_DOWN(start, PMD_SIZE); + for (addr = start; addr < end; addr = next) { next = pmd_addr_end(addr, end); @@ -1159,7 +1166,7 @@ int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, in * in altmap block allocation failures, in which case * we fallback to RAM for vmemmap allocation. */ - if (!IS_ALIGNED(addr, PMD_SIZE) || (altmap && + if (altmap && (!IS_ALIGNED(addr, PMD_SIZE) || altmap_cross_boundary(altmap, addr, PMD_SIZE))) { /* * make sure we don't create altmap mappings @@ -1173,7 +1180,7 @@ int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, in vmemmap_set_pmd(pmd, p, node, addr, next); pr_debug("PMD_SIZE vmemmap mapping\n"); continue; - } else if (altmap) { + } else { /* * A vmemmap block allocation can fail due to * alignment requirements and we trying to align @@ -1426,7 +1433,7 @@ unsigned long radix__pmd_hugepage_update(struct mm_struct *mm, unsigned long add unsigned long old; #ifdef CONFIG_DEBUG_VM - WARN_ON(!radix__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp)); + WARN_ON(!radix__pmd_trans_huge(*pmdp)); assert_spin_locked(pmd_lockptr(mm, pmdp)); #endif @@ -1443,7 +1450,7 @@ unsigned long radix__pud_hugepage_update(struct mm_struct *mm, unsigned long add unsigned long old; #ifdef CONFIG_DEBUG_VM - WARN_ON(!pud_devmap(*pudp)); + WARN_ON(!pud_trans_huge(*pudp)); assert_spin_locked(pud_lockptr(mm, pudp)); #endif @@ -1461,7 +1468,6 @@ pmd_t radix__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long addre VM_BUG_ON(address & ~HPAGE_PMD_MASK); VM_BUG_ON(radix__pmd_trans_huge(*pmdp)); - VM_BUG_ON(pmd_devmap(*pmdp)); /* * khugepaged calls this for normal pmd */ diff --git a/arch/powerpc/mm/nohash/kaslr_booke.c b/arch/powerpc/mm/nohash/kaslr_booke.c index 5c8d1bb98b3e..5e4897daaaea 100644 --- a/arch/powerpc/mm/nohash/kaslr_booke.c +++ b/arch/powerpc/mm/nohash/kaslr_booke.c @@ -178,7 +178,7 @@ static void __init get_crash_kernel(void *fdt, unsigned long size) int ret; ret = parse_crashkernel(boot_command_line, size, &crash_size, - &crash_base, NULL, NULL); + &crash_base, NULL, NULL, NULL); if (ret != 0 || crash_size == 0) return; if (crash_base == 0) diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index 61df5aed7989..dfaa9fd86f7e 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -509,7 +509,7 @@ pte_t *__find_linux_pte(pgd_t *pgdir, unsigned long ea, return NULL; #endif - if (pmd_trans_huge(pmd) || pmd_devmap(pmd)) { + if (pmd_trans_huge(pmd)) { if (is_thp) *is_thp = true; ret_pte = (pte_t *)pmdp; diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index 5daa77aee7f7..025524378443 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -370,6 +370,23 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o return 0; } +bool bpf_jit_bypass_spec_v1(void) +{ +#if defined(CONFIG_PPC_E500) || defined(CONFIG_PPC_BOOK3S_64) + return !(security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && + security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)); +#else + return true; +#endif +} + +bool bpf_jit_bypass_spec_v4(void) +{ + return !(security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && + security_ftr_enabled(SEC_FTR_STF_BARRIER) && + stf_barrier_type_get() != STF_BARRIER_NONE); +} + /* * We spill into the redzone always, even if the bpf program has its own stackframe. * Offsets hardcoded based on BPF_PPC_STACK_SAVE -- see bpf_jit_stack_local() @@ -392,11 +409,77 @@ asm ( " blr ;" ); +static int emit_atomic_ld_st(const struct bpf_insn insn, struct codegen_context *ctx, u32 *image) +{ + u32 code = insn.code; + u32 dst_reg = bpf_to_ppc(insn.dst_reg); + u32 src_reg = bpf_to_ppc(insn.src_reg); + u32 size = BPF_SIZE(code); + u32 tmp1_reg = bpf_to_ppc(TMP_REG_1); + u32 tmp2_reg = bpf_to_ppc(TMP_REG_2); + s16 off = insn.off; + s32 imm = insn.imm; + + switch (imm) { + case BPF_LOAD_ACQ: + switch (size) { + case BPF_B: + EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off)); + break; + case BPF_H: + EMIT(PPC_RAW_LHZ(dst_reg, src_reg, off)); + break; + case BPF_W: + EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off)); + break; + case BPF_DW: + if (off % 4) { + EMIT(PPC_RAW_LI(tmp1_reg, off)); + EMIT(PPC_RAW_LDX(dst_reg, src_reg, tmp1_reg)); + } else { + EMIT(PPC_RAW_LD(dst_reg, src_reg, off)); + } + break; + } + EMIT(PPC_RAW_LWSYNC()); + break; + case BPF_STORE_REL: + EMIT(PPC_RAW_LWSYNC()); + switch (size) { + case BPF_B: + EMIT(PPC_RAW_STB(src_reg, dst_reg, off)); + break; + case BPF_H: + EMIT(PPC_RAW_STH(src_reg, dst_reg, off)); + break; + case BPF_W: + EMIT(PPC_RAW_STW(src_reg, dst_reg, off)); + break; + case BPF_DW: + if (off % 4) { + EMIT(PPC_RAW_LI(tmp2_reg, off)); + EMIT(PPC_RAW_STDX(src_reg, dst_reg, tmp2_reg)); + } else { + EMIT(PPC_RAW_STD(src_reg, dst_reg, off)); + } + break; + } + break; + default: + pr_err_ratelimited("unexpected atomic load/store op code %02x\n", + imm); + return -EINVAL; + } + + return 0; +} + /* Assemble the body code between the prologue & epilogue */ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct codegen_context *ctx, u32 *addrs, int pass, bool extra_pass) { enum stf_barrier_type stf_barrier = stf_barrier_type_get(); + bool sync_emitted, ori31_emitted; const struct bpf_insn *insn = fp->insnsi; int flen = fp->len; int i, ret; @@ -789,30 +872,51 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code /* * BPF_ST NOSPEC (speculation barrier) + * + * The following must act as a barrier against both Spectre v1 + * and v4 if we requested both mitigations. Therefore, also emit + * 'isync; sync' on E500 or 'ori31' on BOOK3S_64 in addition to + * the insns needed for a Spectre v4 barrier. + * + * If we requested only !bypass_spec_v1 OR only !bypass_spec_v4, + * we can skip the respective other barrier type as an + * optimization. */ case BPF_ST | BPF_NOSPEC: - if (!security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) || - !security_ftr_enabled(SEC_FTR_STF_BARRIER)) - break; - - switch (stf_barrier) { - case STF_BARRIER_EIEIO: - EMIT(PPC_RAW_EIEIO() | 0x02000000); - break; - case STF_BARRIER_SYNC_ORI: + sync_emitted = false; + ori31_emitted = false; + if (IS_ENABLED(CONFIG_PPC_E500) && + !bpf_jit_bypass_spec_v1()) { + EMIT(PPC_RAW_ISYNC()); EMIT(PPC_RAW_SYNC()); - EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0)); - EMIT(PPC_RAW_ORI(_R31, _R31, 0)); - break; - case STF_BARRIER_FALLBACK: - ctx->seen |= SEEN_FUNC; - PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier)); - EMIT(PPC_RAW_MTCTR(_R12)); - EMIT(PPC_RAW_BCTRL()); - break; - case STF_BARRIER_NONE: - break; + sync_emitted = true; } + if (!bpf_jit_bypass_spec_v4()) { + switch (stf_barrier) { + case STF_BARRIER_EIEIO: + EMIT(PPC_RAW_EIEIO() | 0x02000000); + break; + case STF_BARRIER_SYNC_ORI: + if (!sync_emitted) + EMIT(PPC_RAW_SYNC()); + EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0)); + EMIT(PPC_RAW_ORI(_R31, _R31, 0)); + ori31_emitted = true; + break; + case STF_BARRIER_FALLBACK: + ctx->seen |= SEEN_FUNC; + PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier)); + EMIT(PPC_RAW_MTCTR(_R12)); + EMIT(PPC_RAW_BCTRL()); + break; + case STF_BARRIER_NONE: + break; + } + } + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && + !bpf_jit_bypass_spec_v1() && + !ori31_emitted) + EMIT(PPC_RAW_ORI(_R31, _R31, 0)); break; /* @@ -859,8 +963,25 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code /* * BPF_STX ATOMIC (atomic ops) */ + 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: + if (bpf_atomic_is_load_store(&insn[i])) { + ret = emit_atomic_ld_st(insn[i], ctx, image); + if (ret) + return ret; + + if (size != BPF_DW && insn_is_zext(&insn[i + 1])) + addrs[++i] = ctx->idx * 4; + break; + } else if (size == BPF_B || size == BPF_H) { + pr_err_ratelimited( + "eBPF filter atomic op code %02x (@%d) unsupported\n", + code, i); + return -EOPNOTSUPP; + } + save_reg = tmp2_reg; ret_reg = src_reg; diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index b0768f3d2893..e42677cc254a 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -713,12 +713,12 @@ static ssize_t catalog_event_len_validate(struct hv_24x7_event_data *event, ev_len = be16_to_cpu(event->length); if (ev_len % 16) - pr_info("event %zu has length %zu not divisible by 16: event=%pK\n", + pr_info("event %zu has length %zu not divisible by 16: event=%p\n", event_idx, ev_len, event); ev_end = (__u8 *)event + ev_len; if (ev_end > end) { - pr_warn("event %zu has .length=%zu, ends after buffer end: ev_end=%pK > end=%pK, offset=%zu\n", + pr_warn("event %zu has .length=%zu, ends after buffer end: ev_end=%p > end=%p, offset=%zu\n", event_idx, ev_len, ev_end, end, offset); return -1; @@ -726,14 +726,14 @@ static ssize_t catalog_event_len_validate(struct hv_24x7_event_data *event, calc_ev_end = event_end(event, end); if (!calc_ev_end) { - pr_warn("event %zu has a calculated length which exceeds buffer length %zu: event=%pK end=%pK, offset=%zu\n", + pr_warn("event %zu has a calculated length which exceeds buffer length %zu: event=%p end=%p, offset=%zu\n", event_idx, event_data_bytes, event, end, offset); return -1; } if (calc_ev_end > ev_end) { - pr_warn("event %zu exceeds its own length: event=%pK, end=%pK, offset=%zu, calc_ev_end=%pK\n", + pr_warn("event %zu exceeds its own length: event=%p, end=%p, offset=%zu, calc_ev_end=%p\n", event_idx, event, ev_end, offset, calc_ev_end); return -1; } @@ -1141,7 +1141,7 @@ static struct attribute *if_attrs[] = { static const struct attribute_group if_group = { .name = "interface", - .bin_attrs_new = if_bin_attrs, + .bin_attrs = if_bin_attrs, .attrs = if_attrs, }; diff --git a/arch/powerpc/platforms/44x/gpio.c b/arch/powerpc/platforms/44x/gpio.c index d540e261d85a..08ab76582568 100644 --- a/arch/powerpc/platforms/44x/gpio.c +++ b/arch/powerpc/platforms/44x/gpio.c @@ -180,7 +180,7 @@ static int __init ppc4xx_add_gpiochips(void) gc->direction_input = ppc4xx_gpio_dir_in; gc->direction_output = ppc4xx_gpio_dir_out; gc->get = ppc4xx_gpio_get; - gc->set_rv = ppc4xx_gpio_set; + gc->set = ppc4xx_gpio_set; ret = of_mm_gpiochip_add_data(np, mm_gc, ppc4xx_gc); if (ret) diff --git a/arch/powerpc/platforms/512x/mpc512x_lpbfifo.c b/arch/powerpc/platforms/512x/mpc512x_lpbfifo.c index 9668b052cd4b..f251e0f68262 100644 --- a/arch/powerpc/platforms/512x/mpc512x_lpbfifo.c +++ b/arch/powerpc/platforms/512x/mpc512x_lpbfifo.c @@ -240,10 +240,8 @@ static int mpc512x_lpbfifo_kick(void) dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; /* Make DMA channel work with LPB FIFO data register */ - if (dma_dev->device_config(lpbfifo.chan, &dma_conf)) { - ret = -EINVAL; - goto err_dma_prep; - } + if (dma_dev->device_config(lpbfifo.chan, &dma_conf)) + return -EINVAL; sg_init_table(&sg, 1); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c index bda707d848a6..7748b6641a3c 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c @@ -336,7 +336,7 @@ static void mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *gpt) gpt->gc.direction_input = mpc52xx_gpt_gpio_dir_in; gpt->gc.direction_output = mpc52xx_gpt_gpio_dir_out; gpt->gc.get = mpc52xx_gpt_gpio_get; - gpt->gc.set_rv = mpc52xx_gpt_gpio_set; + gpt->gc.set = mpc52xx_gpt_gpio_set; gpt->gc.base = -1; gpt->gc.parent = gpt->dev; diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 6e37dfc6c5c9..cb7b9498f291 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -126,7 +126,7 @@ static int mcu_gpiochip_add(struct mcu *mcu) gc->can_sleep = 1; gc->ngpio = MCU_NUM_GPIO; gc->base = -1; - gc->set_rv = mcu_gpio_set; + gc->set = mcu_gpio_set; gc->direction_output = mcu_gpio_dir_out; gc->parent = dev; diff --git a/arch/powerpc/platforms/8xx/cpm1-ic.c b/arch/powerpc/platforms/8xx/cpm1-ic.c index a49d4a9ab3bc..3292071e4da3 100644 --- a/arch/powerpc/platforms/8xx/cpm1-ic.c +++ b/arch/powerpc/platforms/8xx/cpm1-ic.c @@ -110,8 +110,7 @@ static int cpm_pic_probe(struct platform_device *pdev) out_be32(&data->reg->cpic_cimr, 0); - data->host = irq_domain_create_linear(of_fwnode_handle(dev->of_node), - 64, &cpm_pic_host_ops, data); + data->host = irq_domain_create_linear(dev_fwnode(dev), 64, &cpm_pic_host_ops, data); if (!data->host) return -ENODEV; diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index 7462c221115c..7433be7d66ee 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -499,7 +499,7 @@ int cpm1_gpiochip_add16(struct device *dev) gc->direction_input = cpm1_gpio16_dir_in; gc->direction_output = cpm1_gpio16_dir_out; gc->get = cpm1_gpio16_get; - gc->set_rv = cpm1_gpio16_set; + gc->set = cpm1_gpio16_set; gc->to_irq = cpm1_gpio16_to_irq; gc->parent = dev; gc->owner = THIS_MODULE; @@ -622,7 +622,7 @@ int cpm1_gpiochip_add32(struct device *dev) gc->direction_input = cpm1_gpio32_dir_in; gc->direction_output = cpm1_gpio32_dir_out; gc->get = cpm1_gpio32_get; - gc->set_rv = cpm1_gpio32_set; + gc->set = cpm1_gpio32_set; gc->parent = dev; gc->owner = THIS_MODULE; diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 613b383ed8b3..7b527d18aa5e 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -122,16 +122,11 @@ choice If unsure, select Generic. config POWERPC64_CPU - bool "Generic (POWER5 and PowerPC 970 and above)" - depends on PPC_BOOK3S_64 && !CPU_LITTLE_ENDIAN + bool "Generic 64 bits powerpc" + depends on PPC_BOOK3S_64 + select ARCH_HAS_FAST_MULTIPLIER if CPU_LITTLE_ENDIAN select PPC_64S_HASH_MMU - -config POWERPC64_CPU - bool "Generic (POWER8 and above)" - depends on PPC_BOOK3S_64 && CPU_LITTLE_ENDIAN - select ARCH_HAS_FAST_MULTIPLIER - select PPC_64S_HASH_MMU - select PPC_HAS_LBARX_LHARX + select PPC_HAS_LBARX_LHARX if CPU_LITTLE_ENDIAN config POWERPC_CPU bool "Generic 32 bits powerpc" diff --git a/arch/powerpc/platforms/book3s/vas-api.c b/arch/powerpc/platforms/book3s/vas-api.c index dc6f75d3ac6e..49b15e7a8265 100644 --- a/arch/powerpc/platforms/book3s/vas-api.c +++ b/arch/powerpc/platforms/book3s/vas-api.c @@ -425,23 +425,22 @@ static vm_fault_t vas_mmap_fault(struct vm_fault *vmf) return VM_FAULT_SIGBUS; } - mutex_lock(&txwin->task_ref.mmap_mutex); /* * The window may be inactive due to lost credit (Ex: core * removal with DLPAR). If the window is active again when * the credit is available, map the new paste address at the * window virtual address. */ - if (txwin->status == VAS_WIN_ACTIVE) { - paste_addr = cp_inst->coproc->vops->paste_addr(txwin); - if (paste_addr) { - fault = vmf_insert_pfn(vma, vma->vm_start, - (paste_addr >> PAGE_SHIFT)); - mutex_unlock(&txwin->task_ref.mmap_mutex); - return fault; + scoped_guard(mutex, &txwin->task_ref.mmap_mutex) { + if (txwin->status == VAS_WIN_ACTIVE) { + paste_addr = cp_inst->coproc->vops->paste_addr(txwin); + if (paste_addr) { + fault = vmf_insert_pfn(vma, vma->vm_start, + (paste_addr >> PAGE_SHIFT)); + return fault; + } } } - mutex_unlock(&txwin->task_ref.mmap_mutex); /* * Received this fault due to closing the actual window. @@ -494,9 +493,8 @@ static void vas_mmap_close(struct vm_area_struct *vma) return; } - mutex_lock(&txwin->task_ref.mmap_mutex); - txwin->task_ref.vma = NULL; - mutex_unlock(&txwin->task_ref.mmap_mutex); + scoped_guard(mutex, &txwin->task_ref.mmap_mutex) + txwin->task_ref.vma = NULL; } static const struct vm_operations_struct vas_vm_ops = { @@ -552,18 +550,16 @@ static int coproc_mmap(struct file *fp, struct vm_area_struct *vma) * close/open event and allows mmap() only when the window is * active. */ - mutex_lock(&txwin->task_ref.mmap_mutex); + guard(mutex)(&txwin->task_ref.mmap_mutex); if (txwin->status != VAS_WIN_ACTIVE) { pr_err("Window is not active\n"); - rc = -EACCES; - goto out; + return -EACCES; } paste_addr = cp_inst->coproc->vops->paste_addr(txwin); if (!paste_addr) { pr_err("Window paste address failed\n"); - rc = -EINVAL; - goto out; + return -EINVAL; } pfn = paste_addr >> PAGE_SHIFT; @@ -583,8 +579,6 @@ static int coproc_mmap(struct file *fp, struct vm_area_struct *vma) txwin->task_ref.vma = vma; vma->vm_ops = &vas_vm_ops; -out: - mutex_unlock(&txwin->task_ref.mmap_mutex); return rc; } diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 9f9e4b871627..7ec60290abe6 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -143,42 +143,13 @@ spufs_evict_inode(struct inode *inode) put_spu_gang(ei->i_gang); } -static void spufs_prune_dir(struct dentry *dir) -{ - struct dentry *dentry; - struct hlist_node *n; - - inode_lock(d_inode(dir)); - hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) { - spin_lock(&dentry->d_lock); - if (simple_positive(dentry)) { - dget_dlock(dentry); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - simple_unlink(d_inode(dir), dentry); - /* XXX: what was dcache_lock protecting here? Other - * filesystems (IB, configfs) release dcache_lock - * before unlink */ - dput(dentry); - } else { - spin_unlock(&dentry->d_lock); - } - } - shrink_dcache_parent(dir); - inode_unlock(d_inode(dir)); -} - /* Caller must hold parent->i_mutex */ -static int spufs_rmdir(struct inode *parent, struct dentry *dir) +static void spufs_rmdir(struct inode *parent, struct dentry *dir) { - /* remove all entries */ - int res; - spufs_prune_dir(dir); - d_drop(dir); - res = simple_rmdir(parent, dir); - /* We have to give up the mm_struct */ - spu_forget(SPUFS_I(d_inode(dir))->i_ctx); - return res; + struct spu_context *ctx = SPUFS_I(d_inode(dir))->i_ctx; + + locked_recursive_removal(dir, NULL); + spu_forget(ctx); } static int spufs_fill_dir(struct dentry *dir, @@ -222,15 +193,13 @@ static int spufs_dir_close(struct inode *inode, struct file *file) { struct inode *parent; struct dentry *dir; - int ret; dir = file->f_path.dentry; parent = d_inode(dir->d_parent); inode_lock_nested(parent, I_MUTEX_PARENT); - ret = spufs_rmdir(parent, dir); + spufs_rmdir(parent, dir); inode_unlock(parent); - WARN_ON(ret); unuse_gang(dir->d_parent); return dcache_dir_close(inode, file); @@ -288,11 +257,11 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, ret = spufs_fill_dir(dentry, spufs_dir_debug_contents, mode, ctx); + inode_unlock(inode); + if (ret) spufs_rmdir(dir, dentry); - inode_unlock(inode); - return ret; } @@ -475,7 +444,7 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, ret = spufs_context_open(&path); if (ret < 0) - WARN_ON(spufs_rmdir(inode, dentry)); + spufs_rmdir(inode, dentry); out_aff_unlock: if (affinity) diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index e119ced05d10..eb092f293113 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -28,13 +28,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c index 64a9c7125c29..f8139948348e 100644 --- a/arch/powerpc/platforms/powernv/ocxl.c +++ b/arch/powerpc/platforms/powernv/ocxl.c @@ -172,12 +172,11 @@ static void pnv_ocxl_fixup_actag(struct pci_dev *dev) if (phb->type != PNV_PHB_NPU_OCAPI) return; - mutex_lock(&links_list_lock); + guard(mutex)(&links_list_lock); link = find_link(dev); if (!link) { dev_warn(&dev->dev, "couldn't update actag information\n"); - mutex_unlock(&links_list_lock); return; } @@ -206,7 +205,6 @@ static void pnv_ocxl_fixup_actag(struct pci_dev *dev) dev_dbg(&dev->dev, "total actags for function: %d\n", link->fn_desired_actags[PCI_FUNC(dev->devfn)]); - mutex_unlock(&links_list_lock); } DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pnv_ocxl_fixup_actag); @@ -253,12 +251,11 @@ int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, { struct npu_link *link; - mutex_lock(&links_list_lock); + guard(mutex)(&links_list_lock); link = find_link(dev); if (!link) { dev_err(&dev->dev, "actag information not found\n"); - mutex_unlock(&links_list_lock); return -ENODEV; } /* @@ -274,7 +271,6 @@ int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, *enabled = link->fn_actags[PCI_FUNC(dev->devfn)].count; *supported = link->fn_desired_actags[PCI_FUNC(dev->devfn)]; - mutex_unlock(&links_list_lock); return 0; } EXPORT_SYMBOL_GPL(pnv_ocxl_get_actag); @@ -293,12 +289,11 @@ int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count) * * We only support one AFU-carrying function for now. */ - mutex_lock(&links_list_lock); + guard(mutex)(&links_list_lock); link = find_link(dev); if (!link) { dev_err(&dev->dev, "actag information not found\n"); - mutex_unlock(&links_list_lock); return -ENODEV; } @@ -309,7 +304,6 @@ int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count) break; } - mutex_unlock(&links_list_lock); dev_dbg(&dev->dev, "%d PASIDs available for function\n", rc ? 0 : *count); return rc; diff --git a/arch/powerpc/platforms/powernv/opal-core.c b/arch/powerpc/platforms/powernv/opal-core.c index e652da8f986f..784602a48afb 100644 --- a/arch/powerpc/platforms/powernv/opal-core.c +++ b/arch/powerpc/platforms/powernv/opal-core.c @@ -208,7 +208,7 @@ static ssize_t read_opalcore(struct file *file, struct kobject *kobj, static struct bin_attribute opal_core_attr __ro_after_init = { .attr = {.name = "core", .mode = 0400}, - .read_new = read_opalcore + .read = read_opalcore }; /* @@ -607,7 +607,7 @@ static const struct bin_attribute *const mpipl_bin_attr[] = { static const struct attribute_group mpipl_group = { .attrs = mpipl_attr, - .bin_attrs_new = mpipl_bin_attr, + .bin_attrs = mpipl_bin_attr, }; static int __init opalcore_init(void) diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c index 27e25693cf39..cc3cc9ddf9d1 100644 --- a/arch/powerpc/platforms/powernv/opal-dump.c +++ b/arch/powerpc/platforms/powernv/opal-dump.c @@ -342,7 +342,7 @@ static void create_dump_obj(uint32_t id, size_t size, uint32_t type) dump->dump_attr.attr.name = "dump"; dump->dump_attr.attr.mode = 0400; dump->dump_attr.size = size; - dump->dump_attr.read_new = dump_attr_read; + dump->dump_attr.read = dump_attr_read; dump->id = id; dump->size = size; diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c index de33f354e9fd..c3fc5d258146 100644 --- a/arch/powerpc/platforms/powernv/opal-elog.c +++ b/arch/powerpc/platforms/powernv/opal-elog.c @@ -203,7 +203,7 @@ static void create_elog_obj(uint64_t id, size_t size, uint64_t type) elog->raw_attr.attr.name = "raw"; elog->raw_attr.attr.mode = 0400; elog->raw_attr.size = size; - elog->raw_attr.read_new = raw_attr_read; + elog->raw_attr.read = raw_attr_read; elog->id = id; elog->size = size; diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c index fd8c8621e973..a3f7a2928767 100644 --- a/arch/powerpc/platforms/powernv/opal-flash.c +++ b/arch/powerpc/platforms/powernv/opal-flash.c @@ -493,7 +493,7 @@ static ssize_t image_data_write(struct file *filp, struct kobject *kobj, static const struct bin_attribute image_data_attr = { .attr = {.name = "image", .mode = 0200}, .size = MAX_IMAGE_SIZE, /* Limit image size */ - .write_new = image_data_write, + .write = image_data_write, }; static struct kobj_attribute validate_attribute = diff --git a/arch/powerpc/platforms/powernv/opal-msglog.c b/arch/powerpc/platforms/powernv/opal-msglog.c index f1988d0ab45c..992a6b379a66 100644 --- a/arch/powerpc/platforms/powernv/opal-msglog.c +++ b/arch/powerpc/platforms/powernv/opal-msglog.c @@ -102,7 +102,7 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, static struct bin_attribute opal_msglog_attr __ro_after_init = { .attr = {.name = "msglog", .mode = 0400}, - .read_new = opal_msglog_read + .read = opal_msglog_read }; struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 9ec265fcaff4..09bd93464b4f 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -815,7 +815,7 @@ static int opal_add_one_export(struct kobject *parent, const char *export_name, sysfs_bin_attr_init(attr); attr->attr.name = name; attr->attr.mode = 0400; - attr->read_new = sysfs_bin_attr_simple_read; + attr->read = sysfs_bin_attr_simple_read; attr->private = __va(vals[0]); attr->size = vals[1]; diff --git a/arch/powerpc/platforms/powernv/ultravisor.c b/arch/powerpc/platforms/powernv/ultravisor.c index 157d9a8134e4..c526871a1229 100644 --- a/arch/powerpc/platforms/powernv/ultravisor.c +++ b/arch/powerpc/platforms/powernv/ultravisor.c @@ -40,7 +40,7 @@ static ssize_t uv_msglog_read(struct file *file, struct kobject *kobj, static struct bin_attribute uv_msglog_attr __ro_after_init = { .attr = {.name = "msglog", .mode = 0400}, - .read_new = uv_msglog_read + .read = uv_msglog_read }; static int __init uv_init(void) diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 5f4037c1d7fe..5e0a718d1be7 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -532,7 +532,6 @@ static int cmm_migratepage(struct balloon_dev_info *b_dev_info, spin_lock_irqsave(&b_dev_info->pages_lock, flags); balloon_page_insert(b_dev_info, newpage); - balloon_page_delete(page); b_dev_info->isolated_pages--; spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); @@ -542,6 +541,7 @@ static int cmm_migratepage(struct balloon_dev_info *b_dev_info, */ plpar_page_set_active(page); + balloon_page_finalize(page); /* balloon page list reference */ put_page(page); diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index 213aa26dc8b3..979487da6522 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -404,6 +404,45 @@ get_device_node_with_drc_info(u32 index) return NULL; } +static struct device_node * +get_device_node_with_drc_indexes(u32 drc_index) +{ + struct device_node *np = NULL; + u32 nr_indexes, index; + int i, rc; + + for_each_node_with_property(np, "ibm,drc-indexes") { + /* + * First element in the array is the total number of + * DRC indexes returned. + */ + rc = of_property_read_u32_index(np, "ibm,drc-indexes", + 0, &nr_indexes); + if (rc) + goto out_put_np; + + /* + * Retrieve DRC index from the list and return the + * device node if matched with the specified index. + */ + for (i = 0; i < nr_indexes; i++) { + rc = of_property_read_u32_index(np, "ibm,drc-indexes", + i+1, &index); + if (rc) + goto out_put_np; + + if (drc_index == index) + return np; + } + } + + return NULL; + +out_put_np: + of_node_put(np); + return NULL; +} + static int dlpar_hp_dt_add(u32 index) { struct device_node *np, *nodes; @@ -423,10 +462,19 @@ static int dlpar_hp_dt_add(u32 index) goto out; } + /* + * Recent FW provides ibm,drc-info property. So search + * for the user specified DRC index from ibm,drc-info + * property. If this property is not available, search + * in the indexes array from ibm,drc-indexes property. + */ np = get_device_node_with_drc_info(index); - if (!np) - return -EIO; + if (!np) { + np = get_device_node_with_drc_indexes(index); + if (!np) + return -EIO; + } /* Next, configure the connector. */ nodes = dlpar_configure_connector(cpu_to_be32(index), np); diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 52e2623a741d..aeb8633a3d00 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -29,7 +29,7 @@ struct pci_controller *init_phb_dynamic(struct device_node *dn) nid = of_node_to_nid(dn); if (likely((nid) >= 0)) { if (!node_online(nid)) { - if (__register_one_node(nid)) { + if (register_one_node(nid)) { pr_err("PCI: Failed to register node %d\n", nid); } else { update_numa_distance(dn); diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c index 257fd1f8bc19..f9e9cc40c9d0 100644 --- a/arch/powerpc/platforms/pseries/plpks-secvar.c +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c @@ -59,7 +59,14 @@ static u32 get_policy(const char *name) return PLPKS_SIGNEDUPDATE; } -static const char * const plpks_var_names[] = { +static const char * const plpks_var_names_static[] = { + "PK", + "moduledb", + "trustedcadb", + NULL, +}; + +static const char * const plpks_var_names_dynamic[] = { "PK", "KEK", "db", @@ -152,39 +159,55 @@ static int plpks_set_variable(const char *key, u64 key_len, u8 *data, return rc; } -// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does. -// Instead, report the format using the SB_VERSION variable in the keystore. -// The string is made up by us, and takes the form "ibm,plpks-sb-v" (or "ibm,plpks-sb-unknown" -// if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a -// "1 byte unsigned integer value". +/* + * Return the key management mode. + * + * SB_VERSION is defined as a "1 byte unsigned integer value", taking values + * starting from 1. It is owned by the Partition Firmware and its presence + * indicates that the key management mode is dynamic. Any failure in + * reading SB_VERSION defaults the key management mode to static. The error + * codes -ENOENT or -EPERM are expected in static key management mode. An + * unexpected error code will have to be investigated. Only signed variables + * have null bytes in their names, SB_VERSION does not. + * + * Return 0 to indicate that the key management mode is static. Otherwise + * return the SB_VERSION value to indicate that the key management mode is + * dynamic. + */ +static u8 plpks_get_sb_keymgmt_mode(void) +{ + u8 mode; + ssize_t rc; + struct plpks_var var = { + .component = NULL, + .name = "SB_VERSION", + .namelen = 10, + .datalen = 1, + .data = &mode, + }; + + rc = plpks_read_fw_var(&var); + if (rc) { + if (rc != -ENOENT && rc != -EPERM) + pr_info("Error %ld reading SB_VERSION from firmware\n", rc); + mode = 0; + } + return mode; +} + +/* + * PLPKS dynamic secure boot doesn't give us a format string in the same way + * OPAL does. Instead, report the format using the SB_VERSION variable in the + * keystore. The string, made up by us, takes the form of either + * "ibm,plpks-sb-v" or "ibm,plpks-sb-v0", based on the key management mode, + * and return the length of the secvar format property. + */ static ssize_t plpks_secvar_format(char *buf, size_t bufsize) { - struct plpks_var var = {0}; - ssize_t ret; - u8 version; + u8 mode; - var.component = NULL; - // Only the signed variables have null bytes in their names, this one doesn't - var.name = "SB_VERSION"; - var.namelen = strlen(var.name); - var.datalen = 1; - var.data = &version; - - // Unlike the other vars, SB_VERSION is owned by firmware instead of the OS - ret = plpks_read_fw_var(&var); - if (ret) { - if (ret == -ENOENT) { - ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown"); - } else { - pr_err("Error %ld reading SB_VERSION from firmware\n", ret); - ret = -EIO; - } - goto err; - } - - ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version); -err: - return ret; + mode = plpks_get_sb_keymgmt_mode(); + return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode); } static int plpks_max_size(u64 *max_size) @@ -197,21 +220,34 @@ static int plpks_max_size(u64 *max_size) return 0; } - -static const struct secvar_operations plpks_secvar_ops = { +static const struct secvar_operations plpks_secvar_ops_static = { .get = plpks_get_variable, .set = plpks_set_variable, .format = plpks_secvar_format, .max_size = plpks_max_size, .config_attrs = config_attrs, - .var_names = plpks_var_names, + .var_names = plpks_var_names_static, +}; + +static const struct secvar_operations plpks_secvar_ops_dynamic = { + .get = plpks_get_variable, + .set = plpks_set_variable, + .format = plpks_secvar_format, + .max_size = plpks_max_size, + .config_attrs = config_attrs, + .var_names = plpks_var_names_dynamic, }; static int plpks_secvar_init(void) { + u8 mode; + if (!plpks_is_available()) return -ENODEV; - return set_secvar_ops(&plpks_secvar_ops); + mode = plpks_get_sb_keymgmt_mode(); + if (mode) + return set_secvar_ops(&plpks_secvar_ops_dynamic); + return set_secvar_ops(&plpks_secvar_ops_static); } machine_device_initcall(pseries, plpks_secvar_init); diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index e22fc638dbc7..f469f6a9f6e0 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -210,7 +210,7 @@ int cpm2_gpiochip_add32(struct device *dev) gc->direction_input = cpm2_gpio32_dir_in; gc->direction_output = cpm2_gpio32_dir_out; gc->get = cpm2_gpio32_get; - gc->set_rv = cpm2_gpio32_set; + gc->set = cpm2_gpio32_set; gc->parent = dev; gc->owner = THIS_MODULE; diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c index ce6c739c51e5..06d9101a5d49 100644 --- a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c +++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c @@ -75,7 +75,7 @@ static ssize_t fsl_timer_wakeup_store(struct device *dev, if (kstrtoll(buf, 0, &interval)) return -EINVAL; - mutex_lock(&sysfs_lock); + guard(mutex)(&sysfs_lock); if (fsl_wakeup->timer) { disable_irq_wake(fsl_wakeup->timer->irq); @@ -83,31 +83,23 @@ static ssize_t fsl_timer_wakeup_store(struct device *dev, fsl_wakeup->timer = NULL; } - if (!interval) { - mutex_unlock(&sysfs_lock); + if (!interval) return count; - } fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq, fsl_wakeup, interval); - if (!fsl_wakeup->timer) { - mutex_unlock(&sysfs_lock); + if (!fsl_wakeup->timer) return -EINVAL; - } ret = enable_irq_wake(fsl_wakeup->timer->irq); if (ret) { mpic_free_timer(fsl_wakeup->timer); fsl_wakeup->timer = NULL; - mutex_unlock(&sysfs_lock); - return ret; } mpic_start_timer(fsl_wakeup->timer); - mutex_unlock(&sysfs_lock); - return count; } diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 4fe8a7b1b288..2a007bfb038d 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -412,9 +412,8 @@ static int fsl_of_msi_probe(struct platform_device *dev) } platform_set_drvdata(dev, msi); - msi->irqhost = irq_domain_create_linear(of_fwnode_handle(dev->dev.of_node), - NR_MSI_IRQS_MAX, &fsl_msi_host_ops, msi); - + msi->irqhost = irq_domain_create_linear(dev_fwnode(&dev->dev), NR_MSI_IRQS_MAX, + &fsl_msi_host_ops, msi); if (msi->irqhost == NULL) { dev_err(&dev->dev, "No memory for MSI irqhost\n"); err = -ENOMEM; diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 36061f4732b7..51dcd8eaa243 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -24,9 +24,6 @@ config RISCV select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_BINFMT_FLAT - select ARCH_HAS_CRC32 if RISCV_ISA_ZBC - select ARCH_HAS_CRC64 if 64BIT && RISCV_ISA_ZBC - select ARCH_HAS_CRC_T10DIF if RISCV_ISA_ZBC select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_VM_PGTABLE @@ -46,7 +43,6 @@ config RISCV select ARCH_HAS_PREEMPT_LAZY select ARCH_HAS_PREPARE_SYNC_CORE_CMD select ARCH_HAS_PTDUMP if MMU - select ARCH_HAS_PTE_DEVMAP if 64BIT && MMU select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU @@ -63,12 +59,13 @@ config RISCV select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW - select ARCH_SUPPORTS_CFI_CLANG + # clang >= 17: https://github.com/llvm/llvm-project/commit/62fa708ceb027713b386c7e0efda994f8bdc27e2 + select ARCH_SUPPORTS_CFI_CLANG if CLANG_VERSION >= 170000 select ARCH_SUPPORTS_DEBUG_PAGEALLOC if MMU select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE select ARCH_SUPPORTS_HUGETLBFS if MMU # LLD >= 14: https://github.com/llvm/llvm-project/issues/50505 - select ARCH_SUPPORTS_LTO_CLANG if LLD_VERSION >= 140000 + select ARCH_SUPPORTS_LTO_CLANG if LLD_VERSION >= 140000 && CMODEL_MEDANY select ARCH_SUPPORTS_LTO_CLANG_THIN if LLD_VERSION >= 140000 select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS if 64BIT && MMU select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU @@ -96,7 +93,9 @@ config RISCV select CLINT_TIMER if RISCV_M_MODE select CLONE_BACKWARDS select COMMON_CLK + select CPU_NO_EFFICIENT_FFS if !RISCV_ISA_ZBB select CPU_PM if CPU_IDLE || HIBERNATION || SUSPEND + select DYNAMIC_FTRACE if FUNCTION_TRACER select EDAC_SUPPORT select FRAME_POINTER if PERF_EVENTS || (FUNCTION_TRACER && !DYNAMIC_FTRACE) select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY if DYNAMIC_FTRACE @@ -135,13 +134,13 @@ config RISCV 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_QXFER_PKT select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT && MMU @@ -158,10 +157,9 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI_CLANG) select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER if HAVE_DYNAMIC_FTRACE_WITH_ARGS select HAVE_FUNCTION_GRAPH_FREGS - select HAVE_FUNCTION_TRACER if !XIP_KERNEL + select HAVE_FUNCTION_TRACER if !XIP_KERNEL && HAVE_DYNAMIC_FTRACE select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMU select HAVE_FUNCTION_ARG_ACCESS_API diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index a9c3d2f6debc..61ceae0aa27a 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -1,5 +1,12 @@ menu "SoC selection" +config ARCH_ANDES + bool "Andes SoCs" + depends on MMU && !XIP_KERNEL + select ERRATA_ANDES + help + This enables support for Andes SoC platform hardware. + config ARCH_MICROCHIP_POLARFIRE def_bool ARCH_MICROCHIP diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile index 64a898da9aee..3b99e91efa25 100644 --- a/arch/riscv/boot/dts/Makefile +++ b/arch/riscv/boot/dts/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 subdir-y += allwinner +subdir-y += andes subdir-y += canaan subdir-y += microchip subdir-y += renesas diff --git a/arch/riscv/boot/dts/andes/Makefile b/arch/riscv/boot/dts/andes/Makefile new file mode 100644 index 000000000000..c545c668ef70 --- /dev/null +++ b/arch/riscv/boot/dts/andes/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_ANDES) += qilai-voyager.dtb diff --git a/arch/riscv/boot/dts/andes/qilai-voyager.dts b/arch/riscv/boot/dts/andes/qilai-voyager.dts new file mode 100644 index 000000000000..fa7d2b32a9b4 --- /dev/null +++ b/arch/riscv/boot/dts/andes/qilai-voyager.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2025 Andes Technology Corporation. All rights reserved. + */ + +#include "qilai.dtsi" + +/ { + model = "Voyager"; + compatible = "andestech,voyager", "andestech,qilai"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory@400000000 { + device_type = "memory"; + reg = <0x4 0x00000000 0x4 0x00000000>; + }; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/andes/qilai.dtsi b/arch/riscv/boot/dts/andes/qilai.dtsi new file mode 100644 index 000000000000..de3de32f8c39 --- /dev/null +++ b/arch/riscv/boot/dts/andes/qilai.dtsi @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2025 Andes Technology Corporation. All rights reserved. + */ + +/dts-v1/; + +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <62500000>; + + cpu0: cpu@0 { + compatible = "andestech,ax45mp", "riscv"; + device_type = "cpu"; + reg = <0>; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", + "zicntr", "zicsr", "zifencei", + "zihpm", "xandespmu"; + mmu-type = "riscv,sv39"; + clock-frequency = <100000000>; + i-cache-size = <0x8000>; + i-cache-sets = <256>; + i-cache-line-size = <64>; + d-cache-size = <0x8000>; + d-cache-sets = <128>; + d-cache-line-size = <64>; + next-level-cache = <&l2_cache>; + + cpu0_intc: interrupt-controller { + compatible = "andestech,cpu-intc", "riscv,cpu-intc"; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + cpu1: cpu@1 { + compatible = "andestech,ax45mp", "riscv"; + device_type = "cpu"; + reg = <1>; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", + "zicntr", "zicsr", "zifencei", + "zihpm", "xandespmu"; + mmu-type = "riscv,sv39"; + clock-frequency = <100000000>; + i-cache-size = <0x8000>; + i-cache-sets = <256>; + i-cache-line-size = <64>; + d-cache-size = <0x8000>; + d-cache-sets = <128>; + d-cache-line-size = <64>; + next-level-cache = <&l2_cache>; + + cpu1_intc: interrupt-controller { + compatible = "andestech,cpu-intc", + "riscv,cpu-intc"; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + cpu2: cpu@2 { + compatible = "andestech,ax45mp", "riscv"; + device_type = "cpu"; + reg = <2>; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", + "zicntr", "zicsr", "zifencei", + "zihpm", "xandespmu"; + mmu-type = "riscv,sv39"; + clock-frequency = <100000000>; + i-cache-size = <0x8000>; + i-cache-sets = <256>; + i-cache-line-size = <64>; + d-cache-size = <0x8000>; + d-cache-sets = <128>; + d-cache-line-size = <64>; + next-level-cache = <&l2_cache>; + + cpu2_intc: interrupt-controller { + compatible = "andestech,cpu-intc", + "riscv,cpu-intc"; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + cpu3: cpu@3 { + compatible = "andestech,ax45mp", "riscv"; + device_type = "cpu"; + reg = <3>; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", + "zicntr", "zicsr", "zifencei", + "zihpm", "xandespmu"; + mmu-type = "riscv,sv39"; + clock-frequency = <100000000>; + i-cache-size = <0x8000>; + i-cache-sets = <256>; + i-cache-line-size = <64>; + d-cache-size = <0x8000>; + d-cache-sets = <128>; + d-cache-line-size = <64>; + next-level-cache = <&l2_cache>; + + cpu3_intc: interrupt-controller { + compatible = "andestech,cpu-intc", + "riscv,cpu-intc"; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + }; + + soc { + compatible = "simple-bus"; + ranges; + interrupt-parent = <&plic>; + #address-cells = <2>; + #size-cells = <2>; + + plmt: timer@100000 { + compatible = "andestech,qilai-plmt", "andestech,plmt0"; + reg = <0x0 0x00100000 0x0 0x100000>; + interrupts-extended = <&cpu0_intc 7>, + <&cpu1_intc 7>, + <&cpu2_intc 7>, + <&cpu3_intc 7>; + }; + + l2_cache: cache-controller@200000 { + compatible = "andestech,qilai-ax45mp-cache", + "andestech,ax45mp-cache", "cache"; + reg = <0x0 0x00200000 0x0 0x100000>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; + cache-line-size = <64>; + cache-level = <2>; + cache-sets = <2048>; + cache-size = <0x200000>; + cache-unified; + }; + + plic_sw: interrupt-controller@400000 { + compatible = "andestech,qilai-plicsw", + "andestech,plicsw"; + reg = <0x0 0x00400000 0x0 0x400000>; + interrupts-extended = <&cpu0_intc 3>, + <&cpu1_intc 3>, + <&cpu2_intc 3>, + <&cpu3_intc 3>; + }; + + plic: interrupt-controller@2000000 { + compatible = "andestech,qilai-plic", + "andestech,nceplic100"; + reg = <0x0 0x02000000 0x0 0x2000000>; + #address-cells = <0>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>, + <&cpu1_intc 11>, <&cpu1_intc 9>, + <&cpu2_intc 11>, <&cpu2_intc 9>, + <&cpu3_intc 11>, <&cpu3_intc 9>; + riscv,ndev = <71>; + }; + + uart0: serial@30300000 { + compatible = "andestech,uart16550", "ns16550a"; + reg = <0x0 0x30300000 0x0 0x100000>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <50000000>; + reg-offset = <32>; + reg-shift = <2>; + reg-io-width = <4>; + no-loopback-test; + }; + }; +}; diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts index 900a50526d77..06731b8c7bc3 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts @@ -49,32 +49,28 @@ led-controller { compatible = "pwm-leds"; led-d1 { - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 0 7812500 0>; color = ; max-brightness = <255>; label = "d1"; }; led-d2 { - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 1 7812500 0>; color = ; max-brightness = <255>; label = "d2"; }; led-d3 { - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 2 7812500 0>; color = ; max-brightness = <255>; label = "d3"; }; led-d4 { - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 3 7812500 0>; color = ; max-brightness = <255>; label = "d4"; diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts index 72b87b08ab44..03ce2cee4e97 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts @@ -51,8 +51,7 @@ led-controller-1 { compatible = "pwm-leds"; led-d12 { - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 0 7812500 0>; color = ; max-brightness = <255>; label = "d12"; @@ -68,20 +67,17 @@ multi-led { label = "d2"; led-red { - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 2 7812500 0>; color = ; }; led-green { - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 1 7812500 0>; color = ; }; led-blue { - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 3 7812500 0>; color = ; }; }; diff --git a/arch/riscv/boot/dts/sophgo/Makefile b/arch/riscv/boot/dts/sophgo/Makefile index 85966306801e..6f65526d4193 100644 --- a/arch/riscv/boot/dts/sophgo/Makefile +++ b/arch/riscv/boot/dts/sophgo/Makefile @@ -3,4 +3,6 @@ dtb-$(CONFIG_ARCH_SOPHGO) += cv1800b-milkv-duo.dtb dtb-$(CONFIG_ARCH_SOPHGO) += cv1812h-huashan-pi.dtb dtb-$(CONFIG_ARCH_SOPHGO) += sg2002-licheerv-nano-b.dtb dtb-$(CONFIG_ARCH_SOPHGO) += sg2042-milkv-pioneer.dtb +dtb-$(CONFIG_ARCH_SOPHGO) += sg2042-evb-v1.dtb +dtb-$(CONFIG_ARCH_SOPHGO) += sg2042-evb-v2.dtb dtb-$(CONFIG_ARCH_SOPHGO) += sg2044-sophgo-srd3-10.dtb diff --git a/arch/riscv/boot/dts/sophgo/cv180x.dtsi b/arch/riscv/boot/dts/sophgo/cv180x.dtsi index ed06c3609fb2..ccdb45498653 100644 --- a/arch/riscv/boot/dts/sophgo/cv180x.dtsi +++ b/arch/riscv/boot/dts/sophgo/cv180x.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include "cv18xx-reset.h" / { #address-cells = <1>; @@ -24,11 +25,45 @@ soc { #size-cells = <1>; ranges; + rst: reset-controller@3003000 { + compatible = "sophgo,cv1800b-reset"; + reg = <0x3003000 0x1000>; + #reset-cells = <1>; + }; + + mdio: mdio-mux@3009800 { + compatible = "mdio-mux-mmioreg", "mdio-mux"; + reg = <0x3009800 0x4>; + #address-cells = <1>; + #size-cells = <0>; + mdio-parent-bus = <&gmac0_mdio>; + mux-mask = <0x80>; + status = "disabled"; + + internal_mdio: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + internal_ephy: phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + }; + + external_mdio: mdio@80 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x80>; + }; + }; + gpio0: gpio@3020000 { compatible = "snps,dw-apb-gpio"; reg = <0x3020000 0x1000>; #address-cells = <1>; #size-cells = <0>; + resets = <&rst RST_GPIO0>; porta: gpio-controller@0 { compatible = "snps,dw-apb-gpio-port"; @@ -47,6 +82,7 @@ gpio1: gpio@3021000 { reg = <0x3021000 0x1000>; #address-cells = <1>; #size-cells = <0>; + resets = <&rst RST_GPIO1>; portb: gpio-controller@0 { compatible = "snps,dw-apb-gpio-port"; @@ -65,6 +101,7 @@ gpio2: gpio@3022000 { reg = <0x3022000 0x1000>; #address-cells = <1>; #size-cells = <0>; + resets = <&rst RST_GPIO2>; portc: gpio-controller@0 { compatible = "snps,dw-apb-gpio-port"; @@ -83,6 +120,7 @@ gpio3: gpio@3023000 { reg = <0x3023000 0x1000>; #address-cells = <1>; #size-cells = <0>; + resets = <&rst RST_GPIO3>; portd: gpio-controller@0 { compatible = "snps,dw-apb-gpio-port"; @@ -126,6 +164,7 @@ i2c0: i2c@4000000 { clocks = <&clk CLK_I2C>, <&clk CLK_APB_I2C0>; clock-names = "ref", "pclk"; interrupts = ; + resets = <&rst RST_I2C0>; status = "disabled"; }; @@ -137,6 +176,7 @@ i2c1: i2c@4010000 { clocks = <&clk CLK_I2C>, <&clk CLK_APB_I2C1>; clock-names = "ref", "pclk"; interrupts = ; + resets = <&rst RST_I2C1>; status = "disabled"; }; @@ -148,6 +188,7 @@ i2c2: i2c@4020000 { clocks = <&clk CLK_I2C>, <&clk CLK_APB_I2C2>; clock-names = "ref", "pclk"; interrupts = ; + resets = <&rst RST_I2C2>; status = "disabled"; }; @@ -159,6 +200,7 @@ i2c3: i2c@4030000 { clocks = <&clk CLK_I2C>, <&clk CLK_APB_I2C3>; clock-names = "ref", "pclk"; interrupts = ; + resets = <&rst RST_I2C3>; status = "disabled"; }; @@ -170,9 +212,56 @@ i2c4: i2c@4040000 { clocks = <&clk CLK_I2C>, <&clk CLK_APB_I2C4>; clock-names = "ref", "pclk"; interrupts = ; + resets = <&rst RST_I2C4>; status = "disabled"; }; + gmac0: ethernet@4070000 { + compatible = "sophgo,cv1800b-dwmac", "snps,dwmac-3.70a"; + reg = <0x04070000 0x10000>; + clocks = <&clk CLK_AXI4_ETH0>, <&clk CLK_ETH0_500M>; + clock-names = "stmmaceth", "ptp_ref"; + interrupts = ; + interrupt-names = "macirq"; + phy-handle = <&internal_ephy>; + phy-mode = "internal"; + resets = <&rst RST_ETH0>; + reset-names = "stmmaceth"; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,multicast-filter-bins = <0>; + snps,perfect-filter-entries = <1>; + snps,aal; + snps,txpbl = <8>; + snps,rxpbl = <8>; + snps,mtl-rx-config = <&gmac0_mtl_rx_setup>; + snps,mtl-tx-config = <&gmac0_mtl_tx_setup>; + snps,axi-config = <&gmac0_stmmac_axi_setup>; + status = "disabled"; + + gmac0_mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac0_mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <1>; + queue0 {}; + }; + + gmac0_mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <1>; + queue0 {}; + }; + + gmac0_stmmac_axi_setup: stmmac-axi-config { + snps,blen = <16 8 4 0 0 0 0>; + snps,rd_osr_lmt = <2>; + snps,wr_osr_lmt = <1>; + }; + }; + uart0: serial@4140000 { compatible = "snps,dw-apb-uart"; reg = <0x04140000 0x100>; @@ -181,6 +270,7 @@ uart0: serial@4140000 { clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; + resets = <&rst RST_UART0>; status = "disabled"; }; @@ -192,6 +282,7 @@ uart1: serial@4150000 { clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; + resets = <&rst RST_UART1>; status = "disabled"; }; @@ -203,6 +294,7 @@ uart2: serial@4160000 { clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; + resets = <&rst RST_UART2>; status = "disabled"; }; @@ -214,6 +306,7 @@ uart3: serial@4170000 { clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; + resets = <&rst RST_UART3>; status = "disabled"; }; @@ -225,6 +318,7 @@ spi0: spi@4180000 { clocks = <&clk CLK_SPI>, <&clk CLK_APB_SPI0>; clock-names = "ssi_clk", "pclk"; interrupts = ; + resets = <&rst RST_SPI0>; status = "disabled"; }; @@ -236,6 +330,7 @@ spi1: spi@4190000 { clocks = <&clk CLK_SPI>, <&clk CLK_APB_SPI1>; clock-names = "ssi_clk", "pclk"; interrupts = ; + resets = <&rst RST_SPI1>; status = "disabled"; }; @@ -247,6 +342,7 @@ spi2: spi@41a0000 { clocks = <&clk CLK_SPI>, <&clk CLK_APB_SPI2>; clock-names = "ssi_clk", "pclk"; interrupts = ; + resets = <&rst RST_SPI2>; status = "disabled"; }; @@ -258,6 +354,7 @@ spi3: spi@41b0000 { clocks = <&clk CLK_SPI>, <&clk CLK_APB_SPI3>; clock-names = "ssi_clk", "pclk"; interrupts = ; + resets = <&rst RST_SPI3>; status = "disabled"; }; @@ -269,6 +366,7 @@ uart4: serial@41c0000 { clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; + resets = <&rst RST_UART4>; status = "disabled"; }; @@ -307,5 +405,17 @@ dmac: dma-controller@4330000 { snps,data-width = <2>; status = "disabled"; }; + + rtc@5025000 { + compatible = "sophgo,cv1800b-rtc", "syscon"; + reg = <0x5025000 0x2000>; + interrupts = , + , + ; + interrupt-names = "alarm", "longpress", "vbat"; + clocks = <&clk CLK_RTC_25M>, + <&clk CLK_SRC_RTC_SYS_0>; + clock-names = "rtc", "mcu"; + }; }; }; diff --git a/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts b/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts index 26b57e15adc1..4a5835fa9e96 100644 --- a/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts +++ b/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts @@ -55,6 +55,14 @@ &emmc { non-removable; }; +&gmac0 { + status = "okay"; +}; + +&mdio { + status = "okay"; +}; + &sdhci0 { status = "okay"; bus-width = <4>; diff --git a/arch/riscv/boot/dts/sophgo/cv18xx-reset.h b/arch/riscv/boot/dts/sophgo/cv18xx-reset.h new file mode 100644 index 000000000000..7e7c5ca2dbbd --- /dev/null +++ b/arch/riscv/boot/dts/sophgo/cv18xx-reset.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (C) 2025 Inochi Amaoto + */ + +#ifndef _SOPHGO_CV18XX_RESET +#define _SOPHGO_CV18XX_RESET + +#define RST_DDR 2 +#define RST_H264C 3 +#define RST_JPEG 4 +#define RST_H265C 5 +#define RST_VIPSYS 6 +#define RST_TDMA 7 +#define RST_TPU 8 +#define RST_TPUSYS 9 +#define RST_USB 11 +#define RST_ETH0 12 +#define RST_ETH1 13 +#define RST_NAND 14 +#define RST_EMMC 15 +#define RST_SD0 16 +#define RST_SDMA 18 +#define RST_I2S0 19 +#define RST_I2S1 20 +#define RST_I2S2 21 +#define RST_I2S3 22 +#define RST_UART0 23 +#define RST_UART1 24 +#define RST_UART2 25 +#define RST_UART3 26 +#define RST_I2C0 27 +#define RST_I2C1 28 +#define RST_I2C2 29 +#define RST_I2C3 30 +#define RST_I2C4 31 +#define RST_PWM0 32 +#define RST_PWM1 33 +#define RST_PWM2 34 +#define RST_PWM3 35 +#define RST_SPI0 40 +#define RST_SPI1 41 +#define RST_SPI2 42 +#define RST_SPI3 43 +#define RST_GPIO0 44 +#define RST_GPIO1 45 +#define RST_GPIO2 46 +#define RST_EFUSE 47 +#define RST_WDT 48 +#define RST_AHB_ROM 49 +#define RST_SPIC 50 +#define RST_TEMPSEN 51 +#define RST_SARADC 52 +#define RST_COMBO_PHY0 58 +#define RST_SPI_NAND 61 +#define RST_SE 62 +#define RST_UART4 74 +#define RST_GPIO3 75 +#define RST_SYSTEM 76 +#define RST_TIMER 77 +#define RST_TIMER0 78 +#define RST_TIMER1 79 +#define RST_TIMER2 80 +#define RST_TIMER3 81 +#define RST_TIMER4 82 +#define RST_TIMER5 83 +#define RST_TIMER6 84 +#define RST_TIMER7 85 +#define RST_WGN0 86 +#define RST_WGN1 87 +#define RST_WGN2 88 +#define RST_KEYSCAN 89 +#define RST_AUDDAC 91 +#define RST_AUDDAC_APB 92 +#define RST_AUDADC 93 +#define RST_VCSYS 95 +#define RST_ETHPHY 96 +#define RST_ETHPHY_APB 97 +#define RST_AUDSRC 98 +#define RST_VIP_CAM0 99 +#define RST_WDT1 100 +#define RST_WDT2 101 +#define RST_AUTOCLEAR_CPUCORE0 256 +#define RST_AUTOCLEAR_CPUCORE1 257 +#define RST_AUTOCLEAR_CPUCORE2 258 +#define RST_AUTOCLEAR_CPUCORE3 259 +#define RST_AUTOCLEAR_CPUSYS0 260 +#define RST_AUTOCLEAR_CPUSYS1 261 +#define RST_AUTOCLEAR_CPUSYS2 262 +#define RST_CPUCORE0 288 +#define RST_CPUCORE1 289 +#define RST_CPUCORE2 290 +#define RST_CPUCORE3 291 +#define RST_CPUSYS0 292 +#define RST_CPUSYS1 293 +#define RST_CPUSYS2 294 + +#endif /* _SOPHGO_CV18XX_RESET */ diff --git a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi index b136b6c4128c..77ded5304272 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi @@ -259,8 +259,10 @@ cpu0: cpu@0 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <0>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -284,8 +286,10 @@ cpu1: cpu@1 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <1>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -309,8 +313,10 @@ cpu2: cpu@2 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <2>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -334,8 +340,10 @@ cpu3: cpu@3 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <3>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -359,8 +367,10 @@ cpu4: cpu@4 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <4>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -384,8 +394,10 @@ cpu5: cpu@5 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <5>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -409,8 +421,10 @@ cpu6: cpu@6 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <6>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -434,8 +448,10 @@ cpu7: cpu@7 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <7>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -459,8 +475,10 @@ cpu8: cpu@8 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <8>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -484,8 +502,10 @@ cpu9: cpu@9 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <9>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -509,8 +529,10 @@ cpu10: cpu@10 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <10>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -534,8 +556,10 @@ cpu11: cpu@11 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <11>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -559,8 +583,10 @@ cpu12: cpu@12 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <12>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -584,8 +610,10 @@ cpu13: cpu@13 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <13>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -609,8 +637,10 @@ cpu14: cpu@14 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <14>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -634,8 +664,10 @@ cpu15: cpu@15 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <15>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -659,8 +691,10 @@ cpu16: cpu@16 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <16>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -684,8 +718,10 @@ cpu17: cpu@17 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <17>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -709,8 +745,10 @@ cpu18: cpu@18 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <18>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -734,8 +772,10 @@ cpu19: cpu@19 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <19>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -759,8 +799,10 @@ cpu20: cpu@20 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <20>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -784,8 +826,10 @@ cpu21: cpu@21 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <21>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -809,8 +853,10 @@ cpu22: cpu@22 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <22>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -834,8 +880,10 @@ cpu23: cpu@23 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <23>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -859,8 +907,10 @@ cpu24: cpu@24 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <24>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -884,8 +934,10 @@ cpu25: cpu@25 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <25>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -909,8 +961,10 @@ cpu26: cpu@26 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <26>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -934,8 +988,10 @@ cpu27: cpu@27 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <27>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -959,8 +1015,10 @@ cpu28: cpu@28 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <28>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -984,8 +1042,10 @@ cpu29: cpu@29 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <29>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1009,8 +1069,10 @@ cpu30: cpu@30 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <30>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1034,8 +1096,10 @@ cpu31: cpu@31 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <31>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1059,8 +1123,10 @@ cpu32: cpu@32 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <32>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1084,8 +1150,10 @@ cpu33: cpu@33 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <33>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1109,8 +1177,10 @@ cpu34: cpu@34 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <34>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1134,8 +1204,10 @@ cpu35: cpu@35 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <35>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1159,8 +1231,10 @@ cpu36: cpu@36 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <36>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1184,8 +1258,10 @@ cpu37: cpu@37 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <37>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1209,8 +1285,10 @@ cpu38: cpu@38 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <38>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1234,8 +1312,10 @@ cpu39: cpu@39 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <39>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1259,8 +1339,10 @@ cpu40: cpu@40 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <40>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1284,8 +1366,10 @@ cpu41: cpu@41 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <41>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1309,8 +1393,10 @@ cpu42: cpu@42 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <42>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1334,8 +1420,10 @@ cpu43: cpu@43 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <43>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1359,8 +1447,10 @@ cpu44: cpu@44 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <44>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1384,8 +1474,10 @@ cpu45: cpu@45 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <45>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1409,8 +1501,10 @@ cpu46: cpu@46 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <46>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1434,8 +1528,10 @@ cpu47: cpu@47 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <47>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1459,8 +1555,10 @@ cpu48: cpu@48 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <48>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1484,8 +1582,10 @@ cpu49: cpu@49 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <49>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1509,8 +1609,10 @@ cpu50: cpu@50 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <50>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1534,8 +1636,10 @@ cpu51: cpu@51 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <51>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1559,8 +1663,10 @@ cpu52: cpu@52 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <52>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1584,8 +1690,10 @@ cpu53: cpu@53 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <53>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1609,8 +1717,10 @@ cpu54: cpu@54 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <54>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1634,8 +1744,10 @@ cpu55: cpu@55 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <55>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1659,8 +1771,10 @@ cpu56: cpu@56 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <56>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1684,8 +1798,10 @@ cpu57: cpu@57 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <57>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1709,8 +1825,10 @@ cpu58: cpu@58 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <58>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1734,8 +1852,10 @@ cpu59: cpu@59 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <59>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1759,8 +1879,10 @@ cpu60: cpu@60 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <60>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1784,8 +1906,10 @@ cpu61: cpu@61 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <61>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1809,8 +1933,10 @@ cpu62: cpu@62 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <62>; i-cache-block-size = <64>; i-cache-size = <65536>; @@ -1834,8 +1960,10 @@ cpu63: cpu@63 { riscv,isa = "rv64imafdc"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", - "zicntr", "zicsr", "zifencei", - "zihpm"; + "ziccrse", "zicntr", "zicsr", + "zifencei", "zihpm", "zfh", + "xtheadvector"; + thead,vlenb = <16>; reg = <63>; i-cache-block-size = <64>; i-cache-size = <65536>; diff --git a/arch/riscv/boot/dts/sophgo/sg2042-evb-v1.dts b/arch/riscv/boot/dts/sophgo/sg2042-evb-v1.dts new file mode 100644 index 000000000000..3320bc1dd2c6 --- /dev/null +++ b/arch/riscv/boot/dts/sophgo/sg2042-evb-v1.dts @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2025 Sophgo Technology Inc. All rights reserved. + */ + +#include "sg2042.dtsi" + +#include +#include + +/ { + model = "Sophgo SG2042 EVB V1.X"; + compatible = "sophgo,sg2042-evb-v1", "sophgo,sg2042"; + + chosen { + stdout-path = "serial0"; + }; + + gpio-power { + compatible = "gpio-keys"; + + key-power { + label = "Power Key"; + linux,code = ; + gpios = <&port0a 22 GPIO_ACTIVE_HIGH>; + linux,input-type = ; + debounce-interval = <100>; + }; + }; + + pwmfan: pwm-fan { + compatible = "pwm-fan"; + cooling-levels = <103 128 179 230 255>; + pwms = <&pwm 0 40000 0>; + #cooling-cells = <2>; + }; + + thermal-zones { + soc-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&mcu 0>; + + trips { + soc_active1: soc-active1 { + temperature = <30000>; + hysteresis = <8000>; + type = "active"; + }; + + soc_active2: soc-active2 { + temperature = <58000>; + hysteresis = <12000>; + type = "active"; + }; + + soc_active3: soc-active3 { + temperature = <70000>; + hysteresis = <10000>; + type = "active"; + }; + + soc_hot: soc-hot { + temperature = <80000>; + hysteresis = <5000>; + type = "hot"; + }; + }; + + cooling-maps { + map0 { + trip = <&soc_active1>; + cooling-device = <&pwmfan 0 1>; + }; + + map1 { + trip = <&soc_active2>; + cooling-device = <&pwmfan 1 2>; + }; + + map2 { + trip = <&soc_active3>; + cooling-device = <&pwmfan 2 3>; + }; + + map3 { + trip = <&soc_hot>; + cooling-device = <&pwmfan 3 4>; + }; + }; + }; + + board-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&mcu 1>; + + trips { + board_active: board-active { + temperature = <75000>; + hysteresis = <8000>; + type = "active"; + }; + }; + + cooling-maps { + map4 { + trip = <&board_active>; + cooling-device = <&pwmfan 3 4>; + }; + }; + }; + }; +}; + +&cgi_main { + clock-frequency = <25000000>; +}; + +&cgi_dpll0 { + clock-frequency = <25000000>; +}; + +&cgi_dpll1 { + clock-frequency = <25000000>; +}; + +&emmc { + pinctrl-0 = <&emmc_cfg>; + pinctrl-names = "default"; + bus-width = <4>; + no-sdio; + no-sd; + non-removable; + wp-inverted; + status = "okay"; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_cfg>; + pinctrl-names = "default"; + status = "okay"; + + mcu: syscon@17 { + compatible = "sophgo,sg2042-hwmon-mcu"; + reg = <0x17>; + #thermal-sensor-cells = <1>; + }; +}; + +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + phy0: phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&port0a 27 GPIO_ACTIVE_LOW>; + reset-assert-us = <100000>; + reset-deassert-us = <100000>; + }; + }; +}; + +&pinctrl { + emmc_cfg: sdhci-emmc-cfg { + sdhci-emmc-wp-pins { + pinmux = ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + + sdhci-emmc-cd-pins { + pinmux = ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + + sdhci-emmc-rst-pwr-pins { + pinmux = , + ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + }; + + i2c1_cfg: i2c1-cfg { + i2c1-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + }; + + sd_cfg: sdhci-sd-cfg { + sdhci-sd-cd-wp-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + + sdhci-sd-rst-pwr-pins { + pinmux = , + ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + }; + + uart0_cfg: uart0-cfg { + uart0-rx-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + }; +}; + +&sd { + pinctrl-0 = <&sd_cfg>; + pinctrl-names = "default"; + bus-width = <4>; + no-sdio; + no-mmc; + wp-inverted; + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_cfg>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/sophgo/sg2042-evb-v2.dts b/arch/riscv/boot/dts/sophgo/sg2042-evb-v2.dts new file mode 100644 index 000000000000..46980e41b886 --- /dev/null +++ b/arch/riscv/boot/dts/sophgo/sg2042-evb-v2.dts @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2025 Sophgo Technology Inc. All rights reserved. + */ + +#include "sg2042.dtsi" + +#include +#include + +/ { + model = "Sophgo SG2042 EVB V2.0"; + compatible = "sophgo,sg2042-evb-v2", "sophgo,sg2042"; + + chosen { + stdout-path = "serial0"; + }; + + pwmfan: pwm-fan { + compatible = "pwm-fan"; + cooling-levels = <103 128 179 230 255>; + pwms = <&pwm 0 40000 0>; + #cooling-cells = <2>; + }; + + thermal-zones { + soc-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&mcu 0>; + + trips { + soc_active1: soc-active1 { + temperature = <30000>; + hysteresis = <8000>; + type = "active"; + }; + + soc_active2: soc-active2 { + temperature = <58000>; + hysteresis = <12000>; + type = "active"; + }; + + soc_active3: soc-active3 { + temperature = <70000>; + hysteresis = <10000>; + type = "active"; + }; + + soc_hot: soc-hot { + temperature = <80000>; + hysteresis = <5000>; + type = "hot"; + }; + }; + + cooling-maps { + map0 { + trip = <&soc_active1>; + cooling-device = <&pwmfan 0 1>; + }; + + map1 { + trip = <&soc_active2>; + cooling-device = <&pwmfan 1 2>; + }; + + map2 { + trip = <&soc_active3>; + cooling-device = <&pwmfan 2 3>; + }; + + map3 { + trip = <&soc_hot>; + cooling-device = <&pwmfan 3 4>; + }; + }; + }; + + board-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&mcu 1>; + + trips { + board_active: board-active { + temperature = <75000>; + hysteresis = <8000>; + type = "active"; + }; + }; + + cooling-maps { + map4 { + trip = <&board_active>; + cooling-device = <&pwmfan 3 4>; + }; + }; + }; + }; +}; + +&cgi_main { + clock-frequency = <25000000>; +}; + +&cgi_dpll0 { + clock-frequency = <25000000>; +}; + +&cgi_dpll1 { + clock-frequency = <25000000>; +}; + +&emmc { + pinctrl-0 = <&emmc_cfg>; + pinctrl-names = "default"; + bus-width = <4>; + no-sdio; + no-sd; + non-removable; + wp-inverted; + status = "okay"; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_cfg>; + pinctrl-names = "default"; + status = "okay"; + + mcu: syscon@17 { + compatible = "sophgo,sg2042-hwmon-mcu"; + reg = <0x17>; + #thermal-sensor-cells = <1>; + }; +}; + +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + phy0: phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&port0a 27 GPIO_ACTIVE_LOW>; + reset-assert-us = <100000>; + reset-deassert-us = <100000>; + }; + }; +}; + +&pinctrl { + emmc_cfg: sdhci-emmc-cfg { + sdhci-emmc-wp-pins { + pinmux = ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + + sdhci-emmc-cd-pins { + pinmux = ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + + sdhci-emmc-rst-pwr-pins { + pinmux = , + ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + }; + + i2c1_cfg: i2c1-cfg { + i2c1-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + }; + + sd_cfg: sdhci-sd-cfg { + sdhci-sd-cd-wp-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + + sdhci-sd-rst-pwr-pins { + pinmux = , + ; + bias-disable; + drive-strength-microamp = <26800>; + input-schmitt-disable; + }; + }; + + uart0_cfg: uart0-cfg { + uart0-rx-pins { + pinmux = , + ; + bias-pull-up; + drive-strength-microamp = <26800>; + input-schmitt-enable; + }; + }; +}; + +&sd { + pinctrl-0 = <&sd_cfg>; + pinctrl-names = "default"; + bus-width = <4>; + no-sdio; + no-mmc; + wp-inverted; + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_cfg>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/sophgo/sg2042.dtsi b/arch/riscv/boot/dts/sophgo/sg2042.dtsi index 85636d1798f1..b3e4d3c18fdc 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042.dtsi @@ -569,6 +569,67 @@ spi1: spi@7040005000 { status = "disabled"; }; + gmac0: ethernet@7040026000 { + compatible = "sophgo,sg2042-dwmac", "snps,dwmac-5.00a"; + reg = <0x70 0x40026000 0x0 0x4000>; + clocks = <&clkgen GATE_CLK_AXI_ETH0>, + <&clkgen GATE_CLK_PTP_REF_I_ETH0>, + <&clkgen GATE_CLK_TX_ETH0>; + clock-names = "stmmaceth", "ptp_ref", "tx"; + dma-noncoherent; + interrupt-parent = <&intc>; + interrupts = <132 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + resets = <&rstgen RST_ETH0>; + reset-names = "stmmaceth"; + snps,multicast-filter-bins = <0>; + snps,perfect-filter-entries = <1>; + snps,aal; + snps,tso; + snps,txpbl = <32>; + snps,rxpbl = <32>; + snps,mtl-rx-config = <&gmac0_mtl_rx_setup>; + snps,mtl-tx-config = <&gmac0_mtl_tx_setup>; + snps,axi-config = <&gmac0_stmmac_axi_setup>; + status = "disabled"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac0_mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <8>; + queue0 {}; + queue1 {}; + queue2 {}; + queue3 {}; + queue4 {}; + queue5 {}; + queue6 {}; + queue7 {}; + }; + + gmac0_mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <8>; + queue0 {}; + queue1 {}; + queue2 {}; + queue3 {}; + queue4 {}; + queue5 {}; + queue6 {}; + queue7 {}; + }; + + gmac0_stmmac_axi_setup: stmmac-axi-config { + snps,blen = <16 8 4 0 0 0 0>; + snps,wr_osr_lmt = <1>; + snps,rd_osr_lmt = <2>; + }; + }; + emmc: mmc@704002a000 { compatible = "sophgo,sg2042-dwcmshc"; reg = <0x70 0x4002a000 0x0 0x1000>; diff --git a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi index 2a4267078ce6..523799a1a8b8 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi @@ -32,12 +32,13 @@ cpu0: cpu@0 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu0_intc: interrupt-controller { @@ -67,12 +68,13 @@ cpu1: cpu@1 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu1_intc: interrupt-controller { @@ -102,12 +104,13 @@ cpu2: cpu@2 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu2_intc: interrupt-controller { @@ -137,12 +140,13 @@ cpu3: cpu@3 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu3_intc: interrupt-controller { @@ -172,12 +176,13 @@ cpu4: cpu@4 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu4_intc: interrupt-controller { @@ -207,12 +212,13 @@ cpu5: cpu@5 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu5_intc: interrupt-controller { @@ -242,12 +248,13 @@ cpu6: cpu@6 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu6_intc: interrupt-controller { @@ -277,12 +284,13 @@ cpu7: cpu@7 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu7_intc: interrupt-controller { @@ -312,12 +320,13 @@ cpu8: cpu@8 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu8_intc: interrupt-controller { @@ -347,12 +356,13 @@ cpu9: cpu@9 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu9_intc: interrupt-controller { @@ -382,12 +392,13 @@ cpu10: cpu@10 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu10_intc: interrupt-controller { @@ -417,12 +428,13 @@ cpu11: cpu@11 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu11_intc: interrupt-controller { @@ -452,12 +464,13 @@ cpu12: cpu@12 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu12_intc: interrupt-controller { @@ -487,12 +500,13 @@ cpu13: cpu@13 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu13_intc: interrupt-controller { @@ -522,12 +536,13 @@ cpu14: cpu@14 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu14_intc: interrupt-controller { @@ -557,12 +572,13 @@ cpu15: cpu@15 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu15_intc: interrupt-controller { @@ -592,12 +608,13 @@ cpu16: cpu@16 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu16_intc: interrupt-controller { @@ -627,12 +644,13 @@ cpu17: cpu@17 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu17_intc: interrupt-controller { @@ -662,12 +680,13 @@ cpu18: cpu@18 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu18_intc: interrupt-controller { @@ -697,12 +716,13 @@ cpu19: cpu@19 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu19_intc: interrupt-controller { @@ -732,12 +752,13 @@ cpu20: cpu@20 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu20_intc: interrupt-controller { @@ -767,12 +788,13 @@ cpu21: cpu@21 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu21_intc: interrupt-controller { @@ -802,12 +824,13 @@ cpu22: cpu@22 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu22_intc: interrupt-controller { @@ -837,12 +860,13 @@ cpu23: cpu@23 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu23_intc: interrupt-controller { @@ -872,12 +896,13 @@ cpu24: cpu@24 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu24_intc: interrupt-controller { @@ -907,12 +932,13 @@ cpu25: cpu@25 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu25_intc: interrupt-controller { @@ -942,12 +968,13 @@ cpu26: cpu@26 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu26_intc: interrupt-controller { @@ -977,12 +1004,13 @@ cpu27: cpu@27 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu27_intc: interrupt-controller { @@ -1012,12 +1040,13 @@ cpu28: cpu@28 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu28_intc: interrupt-controller { @@ -1047,12 +1076,13 @@ cpu29: cpu@29 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu29_intc: interrupt-controller { @@ -1082,12 +1112,13 @@ cpu30: cpu@30 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu30_intc: interrupt-controller { @@ -1117,12 +1148,13 @@ cpu31: cpu@31 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu31_intc: interrupt-controller { @@ -1152,12 +1184,13 @@ cpu32: cpu@32 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu32_intc: interrupt-controller { @@ -1187,12 +1220,13 @@ cpu33: cpu@33 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu33_intc: interrupt-controller { @@ -1222,12 +1256,13 @@ cpu34: cpu@34 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu34_intc: interrupt-controller { @@ -1257,12 +1292,13 @@ cpu35: cpu@35 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu35_intc: interrupt-controller { @@ -1292,12 +1328,13 @@ cpu36: cpu@36 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu36_intc: interrupt-controller { @@ -1327,12 +1364,13 @@ cpu37: cpu@37 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu37_intc: interrupt-controller { @@ -1362,12 +1400,13 @@ cpu38: cpu@38 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu38_intc: interrupt-controller { @@ -1397,12 +1436,13 @@ cpu39: cpu@39 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu39_intc: interrupt-controller { @@ -1432,12 +1472,13 @@ cpu40: cpu@40 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu40_intc: interrupt-controller { @@ -1467,12 +1508,13 @@ cpu41: cpu@41 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu41_intc: interrupt-controller { @@ -1502,12 +1544,13 @@ cpu42: cpu@42 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu42_intc: interrupt-controller { @@ -1537,12 +1580,13 @@ cpu43: cpu@43 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu43_intc: interrupt-controller { @@ -1572,12 +1616,13 @@ cpu44: cpu@44 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu44_intc: interrupt-controller { @@ -1607,12 +1652,13 @@ cpu45: cpu@45 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu45_intc: interrupt-controller { @@ -1642,12 +1688,13 @@ cpu46: cpu@46 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu46_intc: interrupt-controller { @@ -1677,12 +1724,13 @@ cpu47: cpu@47 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu47_intc: interrupt-controller { @@ -1712,12 +1760,13 @@ cpu48: cpu@48 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu48_intc: interrupt-controller { @@ -1747,12 +1796,13 @@ cpu49: cpu@49 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu49_intc: interrupt-controller { @@ -1782,12 +1832,13 @@ cpu50: cpu@50 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu50_intc: interrupt-controller { @@ -1817,12 +1868,13 @@ cpu51: cpu@51 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu51_intc: interrupt-controller { @@ -1852,12 +1904,13 @@ cpu52: cpu@52 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu52_intc: interrupt-controller { @@ -1887,12 +1940,13 @@ cpu53: cpu@53 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu53_intc: interrupt-controller { @@ -1922,12 +1976,13 @@ cpu54: cpu@54 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu54_intc: interrupt-controller { @@ -1957,12 +2012,13 @@ cpu55: cpu@55 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu55_intc: interrupt-controller { @@ -1992,12 +2048,13 @@ cpu56: cpu@56 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu56_intc: interrupt-controller { @@ -2027,12 +2084,13 @@ cpu57: cpu@57 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu57_intc: interrupt-controller { @@ -2062,12 +2120,13 @@ cpu58: cpu@58 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu58_intc: interrupt-controller { @@ -2097,12 +2156,13 @@ cpu59: cpu@59 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu59_intc: interrupt-controller { @@ -2132,12 +2192,13 @@ cpu60: cpu@60 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu60_intc: interrupt-controller { @@ -2167,12 +2228,13 @@ cpu61: cpu@61 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu61_intc: interrupt-controller { @@ -2202,12 +2264,13 @@ cpu62: cpu@62 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu62_intc: interrupt-controller { @@ -2237,12 +2300,13 @@ cpu63: cpu@63 { "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zfa", "zfbfmin", "zfh", "zfhmin", - "zicbom", "zicbop", "zicboz", + "zicbom", "zicbop", "zicboz", "ziccrse", "zicntr", "zicond","zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zvfbfmin", "zvfbfwma", "zvfh", "zvfhmin"; riscv,cbom-block-size = <64>; + riscv,cbop-block-size = <64>; riscv,cboz-block-size = <64>; cpu63_intc: interrupt-controller { @@ -2714,6 +2778,97 @@ l3_cache: cache-controller-16 { }; }; + pmu { + compatible = "riscv,pmu"; + riscv,event-to-mhpmevent = + <0x00003 0x00000000 0x00000010>, + <0x00004 0x00000000 0x00000011>, + <0x00005 0x00000000 0x00000007>, + <0x00006 0x00000000 0x00000006>, + <0x00008 0x00000000 0x00000027>, + <0x00009 0x00000000 0x00000028>, + <0x10000 0x00000000 0x0000000c>, + <0x10001 0x00000000 0x0000000d>, + <0x10002 0x00000000 0x0000000e>, + <0x10003 0x00000000 0x0000000f>, + <0x10008 0x00000000 0x00000001>, + <0x10009 0x00000000 0x00000002>, + <0x10010 0x00000000 0x00000010>, + <0x10011 0x00000000 0x00000011>, + <0x10012 0x00000000 0x00000012>, + <0x10013 0x00000000 0x00000013>, + <0x10019 0x00000000 0x00000004>, + <0x10021 0x00000000 0x00000003>, + <0x10030 0x00000000 0x0000001c>, + <0x10031 0x00000000 0x0000001b>; + riscv,event-to-mhpmcounters = + <0x00003 0x00003 0xfffffff8>, + <0x00004 0x00004 0xfffffff8>, + <0x00005 0x00005 0xfffffff8>, + <0x00006 0x00006 0xfffffff8>, + <0x00007 0x00007 0xfffffff8>, + <0x00008 0x00008 0xfffffff8>, + <0x00009 0x00009 0xfffffff8>, + <0x0000a 0x0000a 0xfffffff8>, + <0x10000 0x10000 0xfffffff8>, + <0x10001 0x10001 0xfffffff8>, + <0x10002 0x10002 0xfffffff8>, + <0x10003 0x10003 0xfffffff8>, + <0x10008 0x10008 0xfffffff8>, + <0x10009 0x10009 0xfffffff8>, + <0x10010 0x10010 0xfffffff8>, + <0x10011 0x10011 0xfffffff8>, + <0x10012 0x10012 0xfffffff8>, + <0x10013 0x10013 0xfffffff8>, + <0x10019 0x10019 0xfffffff8>, + <0x10021 0x10021 0xfffffff8>, + <0x10030 0x10030 0xfffffff8>, + <0x10031 0x10031 0xfffffff8>; + riscv,raw-event-to-mhpmcounters = + <0x00000000 0x00000001 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000002 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000003 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000004 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000005 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000006 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000007 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000008 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000009 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000a 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000b 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000c 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000d 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000e 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000000f 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000010 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000011 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000012 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000013 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000014 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000015 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000016 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000017 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000018 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000019 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001a 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001b 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001c 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001d 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001e 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000001f 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000020 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000021 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000022 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000023 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000024 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000025 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000026 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000027 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000028 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x00000029 0xffffffff 0xffffffff 0xfffffff8>, + <0x00000000 0x0000002a 0xffffffff 0xffffffff 0xfffffff8>; + }; + soc { intc: interrupt-controller@6d40000000 { compatible = "sophgo,sg2044-plic", "thead,c900-plic"; diff --git a/arch/riscv/boot/dts/sophgo/sg2044-sophgo-srd3-10.dts b/arch/riscv/boot/dts/sophgo/sg2044-sophgo-srd3-10.dts index 54cdf4239d5f..fed3d9a384a0 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044-sophgo-srd3-10.dts +++ b/arch/riscv/boot/dts/sophgo/sg2044-sophgo-srd3-10.dts @@ -27,6 +27,93 @@ &osc { clock-frequency = <25000000>; }; +&emmc { + bus-width = <4>; + no-sdio; + no-sd; + non-removable; + wp-inverted; + status = "okay"; +}; + +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + phy0: phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&porta 28 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <10000>; + rx-internal-delay-ps = <2050>; + }; + }; +}; + +&i2c1 { + status = "okay"; + + mcu: syscon@17 { + compatible = "sophgo,sg2044-hwmon-mcu", "sophgo,sg2042-hwmon-mcu"; + reg = <0x17>; + #thermal-sensor-cells = <1>; + }; +}; + +&msi { + status = "okay"; +}; + +&pcie0 { + bus-range = <0x00 0xff>; + linux,pci-domain = <1>; + status = "okay"; +}; + +&pcie1 { + bus-range = <0x00 0xff>; + linux,pci-domain = <0>; + status = "okay"; +}; + +&pcie2 { + bus-range = <0x00 0xff>; + linux,pci-domain = <3>; + status = "okay"; +}; + +&pcie3 { + bus-range = <0x00 0xff>; + linux,pci-domain = <2>; + status = "okay"; +}; + +&pcie4 { + bus-range = <0x00 0xff>; + linux,pci-domain = <4>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&sd { + bus-width = <4>; + no-sdio; + no-mmc; + wp-inverted; + status = "okay"; +}; + +&uart0 { + /* for firmware */ + status = "reserved"; +}; + &uart1 { status = "okay"; }; diff --git a/arch/riscv/boot/dts/sophgo/sg2044.dtsi b/arch/riscv/boot/dts/sophgo/sg2044.dtsi index d67e45f77d6e..6ec955744b0c 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2044.dtsi @@ -3,7 +3,11 @@ * Copyright (C) 2025 Inochi Amaoto */ +#include +#include #include +#include +#include #include "sg2044-cpus.dtsi" #include "sg2044-reset.h" @@ -28,10 +32,243 @@ soc { #size-cells = <2>; ranges; + pcie0: pcie@6c00000000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x00000000 0x0 0x00001000>, + <0x6c 0x00300000 0x0 0x00004000>, + <0x48 0x00000000 0x0 0x00001000>, + <0x6c 0x000c0000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + clocks = <&clk CLK_GATE_PCIE_1G>; + clock-names = "core"; + device_type = "pci"; + 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>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x48 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x10000000 0x0 0x10000000 0x0 0x04000000>, + <0x02000000 0x0 0x14000000 0x0 0x14000000 0x0 0x04000000>, + <0x43000000 0x4a 0x00000000 0x4a 0x00000000 0x2 0x00000000>, + <0x03000000 0x49 0x00000000 0x49 0x00000000 0x1 0x00000000>; + status = "disabled"; + + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <65 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + }; + }; + + pcie1: pcie@6c00400000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x00400000 0x0 0x00001000>, + <0x6c 0x00700000 0x0 0x00004000>, + <0x40 0x00000000 0x0 0x00001000>, + <0x6c 0x00780000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + clocks = <&clk CLK_GATE_PCIE_1G>; + clock-names = "core"; + device_type = "pci"; + 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>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x40 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x00000000 0x0 0x00000000 0x0 0x04000000>, + <0x02000000 0x0 0x04000000 0x0 0x04000000 0x0 0x04000000>, + <0x43000000 0x42 0x00000000 0x42 0x00000000 0x2 0x00000000>, + <0x03000000 0x41 0x00000000 0x41 0x00000000 0x1 0x00000000>; + status = "disabled"; + + pcie_intc1: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + }; + }; + + pcie2: pcie@6c04000000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x04000000 0x0 0x00001000>, + <0x6c 0x04300000 0x0 0x00004000>, + <0x58 0x00000000 0x0 0x00001000>, + <0x6c 0x040c0000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + clocks = <&clk CLK_GATE_PCIE_1G>; + clock-names = "core"; + device_type = "pci"; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc2 0>, + <0 0 0 2 &pcie_intc2 1>, + <0 0 0 3 &pcie_intc2 2>, + <0 0 0 4 &pcie_intc2 3>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x58 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x30000000 0x0 0x30000000 0x0 0x04000000>, + <0x02000000 0x0 0x34000000 0x0 0x34000000 0x0 0x04000000>, + <0x43000000 0x5a 0x00000000 0x5a 0x00000000 0x2 0x00000000>, + <0x03000000 0x59 0x00000000 0x59 0x00000000 0x1 0x00000000>; + status = "disabled"; + + pcie_intc2: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <74 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + }; + }; + + pcie3: pcie@6c04400000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x04400000 0x0 0x00001000>, + <0x6c 0x04700000 0x0 0x00004000>, + <0x50 0x00000000 0x0 0x00001000>, + <0x6c 0x04780000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + clocks = <&clk CLK_GATE_PCIE_1G>; + clock-names = "core"; + device_type = "pci"; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc3 0>, + <0 0 0 2 &pcie_intc3 1>, + <0 0 0 3 &pcie_intc3 2>, + <0 0 0 4 &pcie_intc3 3>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x50 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x20000000 0x0 0x20000000 0x0 0x04000000>, + <0x02000000 0x0 0x24000000 0x0 0x24000000 0x0 0x04000000>, + <0x43000000 0x52 0x00000000 0x52 0x00000000 0x2 0x00000000>, + <0x03000000 0x51 0x00000000 0x51 0x00000000 0x1 0x00000000>; + status = "disabled"; + + pcie_intc3: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <73 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + }; + }; + + pcie4: pcie@6c08400000 { + compatible = "sophgo,sg2044-pcie"; + reg = <0x6c 0x08400000 0x0 0x00001000>, + <0x6c 0x08700000 0x0 0x00004000>, + <0x60 0x00000000 0x0 0x00001000>, + <0x6c 0x08780000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "app"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + clocks = <&clk CLK_GATE_PCIE_1G>; + clock-names = "core"; + device_type = "pci"; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc4 0>, + <0 0 0 2 &pcie_intc4 1>, + <0 0 0 3 &pcie_intc4 2>, + <0 0 0 4 &pcie_intc4 3>; + msi-parent = <&msi>; + ranges = <0x01000000 0x0 0x00000000 0x60 0x10000000 0x0 0x00200000>, + <0x42000000 0x0 0x40000000 0x0 0x40000000 0x0 0x04000000>, + <0x02000000 0x0 0x44000000 0x0 0x44000000 0x0 0x04000000>, + <0x43000000 0x62 0x00000000 0x62 0x00000000 0x2 0x00000000>, + <0x03000000 0x61 0x00000000 0x61 0x00000000 0x1 0x00000000>; + status = "disabled"; + + pcie_intc4: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <125 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + }; + }; + + msi: msi-controller@6d50000000 { + compatible = "sophgo,sg2044-msi"; + reg = <0x6d 0x50000000 0x0 0x800>, + <0x0 0x7ee00000 0x0 0x40>; + reg-names = "clr", "doorbell"; + #msi-cells = <0>; + msi-controller; + msi-ranges = <&intc 352 IRQ_TYPE_LEVEL_HIGH 512>; + status = "disabled"; + }; + + spifmc0: spi@7001000000 { + compatible = "sophgo,sg2044-spifmc-nor"; + reg = <0x70 0x01000000 0x0 0x4000000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk CLK_GATE_AHB_SPIFMC>; + interrupt-parent = <&intc>; + interrupts = <37 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_SPIFMC0>; + status = "disabled"; + }; + + spifmc1: spi@7005000000 { + compatible = "sophgo,sg2044-spifmc-nor"; + reg = <0x70 0x05000000 0x0 0x4000000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk CLK_GATE_AHB_SPIFMC>; + interrupt-parent = <&intc>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_SPIFMC1>; + status = "disabled"; + }; + + dmac0: dma-controller@7020000000 { + compatible = "snps,axi-dma-1.01a"; + reg = <0x70 0x20000000 0x0 0x10000>; + #dma-cells = <1>; + clock-names = "core-clk", "cfgr-clk"; + clocks = <&clk CLK_GATE_SYSDMA_AXI>, + <&clk CLK_GATE_SYSDMA_AXI>; + dma-noncoherent; + interrupt-parent = <&intc>; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + dma-channels = <8>; + snps,priority = <0 1 2 3 4 5 6 7>; + snps,block-size = <4096 4096 4096 4096 + 4096 4096 4096 4096>; + snps,dma-masters = <2>; + snps,data-width = <2>; + snps,axi-max-burst-len = <4>; + status = "disabled"; + }; + uart0: serial@7030000000 { compatible = "sophgo,sg2044-uart", "snps,dw-apb-uart"; reg = <0x70 0x30000000 0x0 0x1000>; clock-frequency = <500000000>; + clocks = <&clk CLK_GATE_UART_500M>, + <&clk CLK_GATE_APB_UART>; + clock-names = "baudclk", "apb_pclk"; interrupt-parent = <&intc>; interrupts = <41 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; @@ -44,6 +281,9 @@ uart1: serial@7030001000 { compatible = "sophgo,sg2044-uart", "snps,dw-apb-uart"; reg = <0x70 0x30001000 0x0 0x1000>; clock-frequency = <500000000>; + clocks = <&clk CLK_GATE_UART_500M>, + <&clk CLK_GATE_APB_UART>; + clock-names = "baudclk", "apb_pclk"; interrupt-parent = <&intc>; interrupts = <42 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; @@ -56,6 +296,9 @@ uart2: serial@7030002000 { compatible = "sophgo,sg2044-uart", "snps,dw-apb-uart"; reg = <0x70 0x30002000 0x0 0x1000>; clock-frequency = <500000000>; + clocks = <&clk CLK_GATE_UART_500M>, + <&clk CLK_GATE_APB_UART>; + clock-names = "baudclk", "apb_pclk"; interrupt-parent = <&intc>; interrupts = <43 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; @@ -68,6 +311,9 @@ uart3: serial@7030003000 { compatible = "sophgo,sg2044-uart", "snps,dw-apb-uart"; reg = <0x70 0x30003000 0x0 0x1000>; clock-frequency = <500000000>; + clocks = <&clk CLK_GATE_UART_500M>, + <&clk CLK_GATE_APB_UART>; + clock-names = "baudclk", "apb_pclk"; interrupt-parent = <&intc>; interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; @@ -76,6 +322,259 @@ uart3: serial@7030003000 { status = "disabled"; }; + gmac0: ethernet@7030006000 { + compatible = "sophgo,sg2044-dwmac", "snps,dwmac-5.30a"; + reg = <0x70 0x30006000 0x0 0x4000>; + clocks = <&clk CLK_GATE_AXI_ETH0>, + <&clk CLK_GATE_PTP_REF_I_ETH0>, + <&clk CLK_GATE_TX_ETH0>; + clock-names = "stmmaceth", "ptp_ref", "tx"; + dma-noncoherent; + interrupt-parent = <&intc>; + interrupts = <296 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + resets = <&rst RST_ETH0>; + reset-names = "stmmaceth"; + snps,multicast-filter-bins = <0>; + snps,perfect-filter-entries = <1>; + snps,aal; + snps,tso; + snps,txpbl = <32>; + snps,rxpbl = <32>; + snps,mtl-rx-config = <&gmac0_mtl_rx_setup>; + snps,mtl-tx-config = <&gmac0_mtl_tx_setup>; + snps,axi-config = <&gmac0_stmmac_axi_setup>; + status = "disabled"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac0_mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <8>; + snps,rx-sched-wsp; + queue0 {}; + queue1 {}; + queue2 {}; + queue3 {}; + queue4 {}; + queue5 {}; + queue6 {}; + queue7 {}; + }; + + gmac0_mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <8>; + queue0 {}; + queue1 {}; + queue2 {}; + queue3 {}; + queue4 {}; + queue5 {}; + queue6 {}; + queue7 {}; + }; + + gmac0_stmmac_axi_setup: stmmac-axi-config { + snps,blen = <16 8 4 0 0 0 0>; + snps,wr_osr_lmt = <1>; + snps,rd_osr_lmt = <2>; + }; + }; + + emmc: mmc@703000a000 { + compatible = "sophgo,sg2044-dwcmshc", "sophgo,sg2042-dwcmshc"; + reg = <0x70 0x3000a000 0x0 0x1000>; + clocks = <&clk CLK_GATE_EMMC>, + <&clk CLK_GATE_AXI_EMMC>, + <&clk CLK_GATE_EMMC_100K>; + clock-names = "core", "bus", "timer"; + interrupt-parent = <&intc>; + interrupts = <298 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + sd: mmc@703000b000 { + compatible = "sophgo,sg2044-dwcmshc", "sophgo,sg2042-dwcmshc"; + reg = <0x70 0x3000b000 0x0 0x1000>; + clocks = <&clk CLK_GATE_SD>, + <&clk CLK_GATE_AXI_SD>, + <&clk CLK_GATE_SD_100K>; + clock-names = "core", "bus", "timer"; + interrupt-parent = <&intc>; + interrupts = <300 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c0: i2c@7040005000 { + compatible = "sophgo,sg2044-i2c", "snps,designware-i2c"; + reg = <0x70 0x40005000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + clocks = <&clk CLK_GATE_APB_I2C>; + clock-names = "ref"; + interrupt-parent = <&intc>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_I2C0>; + status = "disabled"; + }; + + i2c1: i2c@7040006000 { + compatible = "sophgo,sg2044-i2c", "snps,designware-i2c"; + reg = <0x70 0x40006000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + clocks = <&clk CLK_GATE_APB_I2C>; + clock-names = "ref"; + interrupt-parent = <&intc>; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_I2C1>; + status = "disabled"; + }; + + i2c2: i2c@7040007000 { + compatible = "sophgo,sg2044-i2c", "snps,designware-i2c"; + reg = <0x70 0x40007000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + clocks = <&clk CLK_GATE_APB_I2C>; + clock-names = "ref"; + interrupt-parent = <&intc>; + interrupts = <33 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_I2C2>; + status = "disabled"; + }; + + i2c3: i2c@7040008000 { + compatible = "sophgo,sg2044-i2c", "snps,designware-i2c"; + reg = <0x70 0x40008000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + clocks = <&clk CLK_GATE_APB_I2C>; + clock-names = "ref"; + interrupt-parent = <&intc>; + interrupts = <34 IRQ_TYPE_LEVEL_HIGH>; + resets = <&rst RST_I2C3>; + status = "disabled"; + }; + + gpio0: gpio@7040009000 { + compatible = "snps,dw-apb-gpio"; + reg = <0x70 0x40009000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk CLK_GATE_APB_GPIO>, + <&clk CLK_GATE_GPIO_DB>; + clock-names = "bus", "db"; + resets = <&rst RST_GPIO0>; + + porta: gpio-controller@0 { + compatible = "snps,dw-apb-gpio-port"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + gpio1: gpio@704000a000 { + compatible = "snps,dw-apb-gpio"; + reg = <0x70 0x4000a000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk CLK_GATE_APB_GPIO>, + <&clk CLK_GATE_GPIO_DB>; + clock-names = "bus", "db"; + resets = <&rst RST_GPIO1>; + + portb: gpio-controller@0 { + compatible = "snps,dw-apb-gpio-port"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + gpio2: gpio@704000b000 { + compatible = "snps,dw-apb-gpio"; + reg = <0x70 0x4000b000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk CLK_GATE_APB_GPIO>, + <&clk CLK_GATE_GPIO_DB>; + clock-names = "bus", "db"; + resets = <&rst RST_GPIO2>; + + portc: gpio-controller@0 { + compatible = "snps,dw-apb-gpio-port"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <28 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + pwm: pwm@704000c000 { + compatible = "sophgo,sg2044-pwm"; + reg = <0x70 0x4000c000 0x0 0x1000>; + #pwm-cells = <3>; + clocks = <&clk CLK_GATE_APB_PWM>; + clock-names = "apb"; + resets = <&rst RST_PWM>; + status = "disabled"; + }; + + syscon: syscon@7050000000 { + compatible = "sophgo,sg2044-top-syscon", "syscon"; + reg = <0x70 0x50000000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&osc>; + }; + + pinctrl: pinctrl@7050001000 { + compatible = "sophgo,sg2044-pinctrl"; + reg = <0x70 0x50001000 0x0 0x1000>; + }; + + clk: clock-controller@7050002000 { + compatible = "sophgo,sg2044-clk"; + reg = <0x70 0x50002000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&syscon CLK_FPLL0>, <&syscon CLK_FPLL1>, + <&syscon CLK_FPLL2>, <&syscon CLK_DPLL0>, + <&syscon CLK_DPLL1>, <&syscon CLK_DPLL2>, + <&syscon CLK_DPLL3>, <&syscon CLK_DPLL4>, + <&syscon CLK_DPLL5>, <&syscon CLK_DPLL6>, + <&syscon CLK_DPLL7>, <&syscon CLK_MPLL0>, + <&syscon CLK_MPLL1>, <&syscon CLK_MPLL2>, + <&syscon CLK_MPLL3>, <&syscon CLK_MPLL4>, + <&syscon CLK_MPLL5>; + clock-names = "fpll0", "fpll1", "fpll2", "dpll0", + "dpll1", "dpll2", "dpll3", "dpll4", + "dpll5", "dpll6", "dpll7", "mpll0", + "mpll1", "mpll2", "mpll3", "mpll4", + "mpll5"; + }; + rst: reset-controller@7050003000 { compatible = "sophgo,sg2044-reset", "sophgo,sg2042-reset"; diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 816ef1bc358e..fe22c747c501 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -30,6 +30,16 @@ led1 { }; }; +&emmc { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi index 283663647a86..381055737422 100644 --- a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi @@ -20,4 +20,12 @@ uart0-2-pins { drive-strength = <32>; }; }; + + pwm14_1_cfg: pwm14-1-cfg { + pwm14-1-pins { + pinmux = ; + bias-pull-up = <0>; + drive-strength = <32>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index c0f8c5fca975..abde8bb07c95 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -346,6 +346,18 @@ soc { dma-noncoherent; ranges; + syscon_rcpu: system-controller@c0880000 { + compatible = "spacemit,k1-syscon-rcpu"; + reg = <0x0 0xc0880000 0x0 0x2048>; + #reset-cells = <1>; + }; + + syscon_rcpu2: system-controller@c0888000 { + compatible = "spacemit,k1-syscon-rcpu2"; + reg = <0x0 0xc0888000 0x0 0x28>; + #reset-cells = <1>; + }; + syscon_apbc: system-controller@d4015000 { compatible = "spacemit,k1-syscon-apbc"; reg = <0x0 0xd4015000 0x0 0x1000>; @@ -357,114 +369,6 @@ syscon_apbc: system-controller@d4015000 { #reset-cells = <1>; }; - uart0: serial@d4017000 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017000 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART0>, - <&syscon_apbc CLK_UART0_BUS>; - clock-names = "core", "bus"; - interrupts = <42>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart2: serial@d4017100 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017100 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART2>, - <&syscon_apbc CLK_UART2_BUS>; - clock-names = "core", "bus"; - interrupts = <44>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart3: serial@d4017200 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017200 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART3>, - <&syscon_apbc CLK_UART3_BUS>; - clock-names = "core", "bus"; - interrupts = <45>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart4: serial@d4017300 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017300 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART4>, - <&syscon_apbc CLK_UART4_BUS>; - clock-names = "core", "bus"; - interrupts = <46>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart5: serial@d4017400 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017400 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART5>, - <&syscon_apbc CLK_UART5_BUS>; - clock-names = "core", "bus"; - interrupts = <47>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart6: serial@d4017500 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017500 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART6>, - <&syscon_apbc CLK_UART6_BUS>; - clock-names = "core", "bus"; - interrupts = <48>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart7: serial@d4017600 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017600 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART7>, - <&syscon_apbc CLK_UART7_BUS>; - clock-names = "core", "bus"; - interrupts = <49>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart8: serial@d4017700 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017700 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART8>, - <&syscon_apbc CLK_UART8_BUS>; - clock-names = "core", "bus"; - interrupts = <50>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - - uart9: serial@d4017800 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xd4017800 0x0 0x100>; - clocks = <&syscon_apbc CLK_UART9>, - <&syscon_apbc CLK_UART9_BUS>; - clock-names = "core", "bus"; - interrupts = <51>; - reg-shift = <2>; - reg-io-width = <4>; - status = "disabled"; - }; - gpio: gpio@d4019000 { compatible = "spacemit,k1-gpio"; reg = <0x0 0xd4019000 0x0 0x100>; @@ -483,6 +387,78 @@ gpio: gpio@d4019000 { <&pinctrl 3 0 96 32>; }; + pwm0: pwm@d401a000 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a000 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM0>; + resets = <&syscon_apbc RESET_PWM0>; + status = "disabled"; + }; + + pwm1: pwm@d401a400 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a400 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM1>; + resets = <&syscon_apbc RESET_PWM1>; + status = "disabled"; + }; + + pwm2: pwm@d401a800 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a800 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM2>; + resets = <&syscon_apbc RESET_PWM2>; + status = "disabled"; + }; + + pwm3: pwm@d401ac00 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401ac00 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM3>; + resets = <&syscon_apbc RESET_PWM3>; + status = "disabled"; + }; + + pwm4: pwm@d401b000 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b000 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM4>; + resets = <&syscon_apbc RESET_PWM4>; + status = "disabled"; + }; + + pwm5: pwm@d401b400 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b400 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM5>; + resets = <&syscon_apbc RESET_PWM5>; + status = "disabled"; + }; + + pwm6: pwm@d401b800 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b800 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM6>; + resets = <&syscon_apbc RESET_PWM6>; + status = "disabled"; + }; + + pwm7: pwm@d401bc00 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401bc00 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM7>; + resets = <&syscon_apbc RESET_PWM7>; + status = "disabled"; + }; + pinctrl: pinctrl@d401e000 { compatible = "spacemit,k1-pinctrl"; reg = <0x0 0xd401e000 0x0 0x400>; @@ -491,6 +467,114 @@ pinctrl: pinctrl@d401e000 { clock-names = "func", "bus"; }; + pwm8: pwm@d4020000 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020000 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM8>; + resets = <&syscon_apbc RESET_PWM8>; + status = "disabled"; + }; + + pwm9: pwm@d4020400 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020400 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM9>; + resets = <&syscon_apbc RESET_PWM9>; + status = "disabled"; + }; + + pwm10: pwm@d4020800 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020800 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM10>; + resets = <&syscon_apbc RESET_PWM10>; + status = "disabled"; + }; + + pwm11: pwm@d4020c00 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020c00 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM11>; + resets = <&syscon_apbc RESET_PWM11>; + status = "disabled"; + }; + + pwm12: pwm@d4021000 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021000 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM12>; + resets = <&syscon_apbc RESET_PWM12>; + status = "disabled"; + }; + + pwm13: pwm@d4021400 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021400 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM13>; + resets = <&syscon_apbc RESET_PWM13>; + status = "disabled"; + }; + + pwm14: pwm@d4021800 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021800 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM14>; + resets = <&syscon_apbc RESET_PWM14>; + status = "disabled"; + }; + + pwm15: pwm@d4021c00 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021c00 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM15>; + resets = <&syscon_apbc RESET_PWM15>; + status = "disabled"; + }; + + pwm16: pwm@d4022000 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022000 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM16>; + resets = <&syscon_apbc RESET_PWM16>; + status = "disabled"; + }; + + pwm17: pwm@d4022400 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022400 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM17>; + resets = <&syscon_apbc RESET_PWM17>; + status = "disabled"; + }; + + pwm18: pwm@d4022800 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022800 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM18>; + resets = <&syscon_apbc RESET_PWM18>; + status = "disabled"; + }; + + pwm19: pwm@d4022c00 { + compatible = "spacemit,k1-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022c00 0x0 0x10>; + #pwm-cells = <3>; + clocks = <&syscon_apbc CLK_PWM19>; + resets = <&syscon_apbc RESET_PWM19>; + status = "disabled"; + }; + syscon_mpmu: system-controller@d4050000 { compatible = "spacemit,k1-syscon-mpmu"; reg = <0x0 0xd4050000 0x0 0x209c>; @@ -553,14 +637,201 @@ clint: timer@e4000000 { <&cpu7_intc 3>, <&cpu7_intc 7>; }; - sec_uart1: serial@f0612000 { - compatible = "spacemit,k1-uart", "intel,xscale-uart"; - reg = <0x0 0xf0612000 0x0 0x100>; - interrupts = <43>; - clock-frequency = <14857000>; - reg-shift = <2>; - reg-io-width = <4>; - status = "reserved"; /* for TEE usage */ + syscon_apbc2: system-controller@f0610000 { + compatible = "spacemit,k1-syscon-apbc2"; + reg = <0x0 0xf0610000 0x0 0x20>; + #reset-cells = <1>; + }; + + camera-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, + <0x0 0x80000000 0x1 0x00000000 0x1 0x80000000>; + }; + + dma-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, + <0x1 0x00000000 0x1 0x80000000 0x3 0x00000000>; + + uart0: serial@d4017000 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017000 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART0>, + <&syscon_apbc CLK_UART0_BUS>; + clock-names = "core", "bus"; + interrupts = <42>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart2: serial@d4017100 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017100 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART2>, + <&syscon_apbc CLK_UART2_BUS>; + clock-names = "core", "bus"; + interrupts = <44>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart3: serial@d4017200 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017200 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART3>, + <&syscon_apbc CLK_UART3_BUS>; + clock-names = "core", "bus"; + interrupts = <45>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart4: serial@d4017300 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017300 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART4>, + <&syscon_apbc CLK_UART4_BUS>; + clock-names = "core", "bus"; + interrupts = <46>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart5: serial@d4017400 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017400 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART5>, + <&syscon_apbc CLK_UART5_BUS>; + clock-names = "core", "bus"; + interrupts = <47>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart6: serial@d4017500 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017500 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART6>, + <&syscon_apbc CLK_UART6_BUS>; + clock-names = "core", "bus"; + interrupts = <48>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart7: serial@d4017600 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017600 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART7>, + <&syscon_apbc CLK_UART7_BUS>; + clock-names = "core", "bus"; + interrupts = <49>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart8: serial@d4017700 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017700 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART8>, + <&syscon_apbc CLK_UART8_BUS>; + clock-names = "core", "bus"; + interrupts = <50>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart9: serial@d4017800 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xd4017800 0x0 0x100>; + clocks = <&syscon_apbc CLK_UART9>, + <&syscon_apbc CLK_UART9_BUS>; + clock-names = "core", "bus"; + interrupts = <51>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + sec_uart1: serial@f0612000 { + compatible = "spacemit,k1-uart", + "intel,xscale-uart"; + reg = <0x0 0xf0612000 0x0 0x100>; + interrupts = <43>; + clock-frequency = <14857000>; + reg-shift = <2>; + reg-io-width = <4>; + status = "reserved"; /* for TEE usage */ + }; + }; + + multimedia-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, + <0x0 0x80000000 0x1 0x00000000 0x3 0x80000000>; + }; + + network-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, + <0x0 0x80000000 0x1 0x00000000 0x0 0x80000000>; + }; + + pcie-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, + <0x0 0xb8000000 0x1 0x38000000 0x3 0x48000000>; + }; + + storage-bus { + compatible = "simple-bus"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>; + + emmc: mmc@d4281000 { + compatible = "spacemit,k1-sdhci"; + reg = <0x0 0xd4281000 0x0 0x200>; + clocks = <&syscon_apmu CLK_SDH_AXI>, + <&syscon_apmu CLK_SDH2>; + clock-names = "core", "io"; + interrupts = <101>; + status = "disabled"; + }; }; }; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi index 4baeb981d4df..2eaf01775ef5 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi @@ -8,6 +8,7 @@ #include "jh7110.dtsi" #include "jh7110-pinfunc.h" #include +#include #include / { @@ -38,6 +39,14 @@ gpio-restart { priority = <224>; }; + leds { + compatible = "gpio-leds"; + + led_status_power: led-0 { + gpios = <&aongpio 3 GPIO_ACTIVE_HIGH>; + }; + }; + pwmdac_codec: audio-codec { compatible = "linux,spdif-dit"; #sound-dai-cells = <0>; diff --git a/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts index 3bd62ab78523..fdaf6b4557da 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts @@ -12,9 +12,9 @@ / { }; &gmac0 { - starfive,tx-use-rgmii-clk; assigned-clocks = <&aoncrg JH7110_AONCLK_GMAC0_TX>; assigned-clock-parents = <&aoncrg JH7110_AONCLK_GMAC0_RMII_RTX>; + starfive,tx-use-rgmii-clk; status = "okay"; }; @@ -31,14 +31,14 @@ &pcie1 { }; &phy0 { - motorcomm,tx-clk-adj-enabled; + rx-internal-delay-ps = <1500>; + tx-internal-delay-ps = <1500>; + motorcomm,rx-clk-drv-microamp = <3970>; + motorcomm,rx-data-drv-microamp = <2910>; motorcomm,tx-clk-10-inverted; motorcomm,tx-clk-100-inverted; motorcomm,tx-clk-1000-inverted; - motorcomm,rx-clk-drv-microamp = <3970>; - motorcomm,rx-data-drv-microamp = <2910>; - rx-internal-delay-ps = <1500>; - tx-internal-delay-ps = <1500>; + motorcomm,tx-clk-adj-enabled; }; &pwm { diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 1db0054c4e09..03f1d7319049 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include / { compatible = "thead,th1520"; @@ -234,6 +235,8 @@ aon: aon { compatible = "thead,th1520-aon"; mboxes = <&mbox_910t 1>; mbox-names = "aon"; + resets = <&rst TH1520_RESET_ID_GPU_CLKGEN>; + reset-names = "gpu-clkgen"; #power-domain-cells = <1>; }; @@ -294,8 +297,9 @@ gmac1: ethernet@ffe7060000 { reg-names = "dwmac", "apb"; interrupts = <67 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "macirq"; - clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC1>; - clock-names = "stmmaceth", "pclk"; + clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC1>, + <&clk CLK_PERISYS_APB4_HCLK>; + clock-names = "stmmaceth", "pclk", "apb"; snps,pbl = <32>; snps,fixed-burst; snps,multicast-filter-bins = <64>; @@ -316,8 +320,9 @@ gmac0: ethernet@ffe7070000 { reg-names = "dwmac", "apb"; interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "macirq"; - clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC0>; - clock-names = "stmmaceth", "pclk"; + clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC0>, + <&clk CLK_PERISYS_APB4_HCLK>; + clock-names = "stmmaceth", "pclk", "apb"; snps,pbl = <32>; snps,fixed-burst; snps,multicast-filter-bins = <64>; @@ -662,6 +667,17 @@ padctrl_aosys: pinctrl@fffff4a000 { thead,pad-group = <1>; }; + pvt: pvt@fffff4e000 { + compatible = "moortec,mr75203"; + reg = <0xff 0xfff4e000 0x0 0x80>, + <0xff 0xfff4e080 0x0 0x100>, + <0xff 0xfff4e180 0x0 0x680>, + <0xff 0xfff4e800 0x0 0x600>; + reg-names = "common", "ts", "pd", "vm"; + clocks = <&aonsys_clk>; + #thermal-sensor-cells = <1>; + }; + gpio@fffff52000 { compatible = "snps,dw-apb-gpio"; reg = <0xff 0xfff52000 0x0 0x1000>; diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index fe8bd8afb418..7b5eed17611a 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -22,6 +22,7 @@ CONFIG_USER_NS=y CONFIG_CHECKPOINT_RESTORE=y CONFIG_BLK_DEV_INITRD=y CONFIG_PROFILING=y +CONFIG_ARCH_ANDES=y CONFIG_ARCH_MICROCHIP=y CONFIG_ARCH_SIFIVE=y CONFIG_ARCH_SOPHGO=y @@ -191,7 +192,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_SOC=y CONFIG_SND_DESIGNWARE_I2S=m -CONFIG_SND_SOC_STARFIVE=m CONFIG_SND_SOC_JH7110_PWMDAC=m CONFIG_SND_SOC_JH7110_TDM=m CONFIG_SND_SOC_WM8978=m @@ -230,6 +230,7 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_OF_DWCMSHC=y +CONFIG_MMC_SDHCI_OF_K1=y CONFIG_MMC_SDHCI_CADENCE=y CONFIG_MMC_SPI=y CONFIG_MMC_DW=y @@ -258,6 +259,8 @@ CONFIG_RPMSG_CTRL=y CONFIG_RPMSG_VIRTIO=y CONFIG_PM_DEVFREQ=y CONFIG_IIO=y +CONFIG_PWM=y +CONFIG_PWM_PXA=m CONFIG_THEAD_C900_ACLINT_SSWI=y CONFIG_PHY_SUN4I_USB=m CONFIG_PHY_STARFIVE_JH7110_DPHY_RX=m diff --git a/arch/riscv/crypto/Kconfig b/arch/riscv/crypto/Kconfig index cd9b776602f8..a75d6325607b 100644 --- a/arch/riscv/crypto/Kconfig +++ b/arch/riscv/crypto/Kconfig @@ -28,17 +28,6 @@ config CRYPTO_GHASH_RISCV64 Architecture: riscv64 using: - Zvkg vector crypto extension -config CRYPTO_SHA512_RISCV64 - tristate "Hash functions: SHA-384 and SHA-512" - depends on 64BIT && RISCV_ISA_V && TOOLCHAIN_HAS_VECTOR_CRYPTO - select CRYPTO_SHA512 - help - SHA-384 and SHA-512 secure hash algorithm (FIPS 180) - - Architecture: riscv64 using: - - Zvknhb vector crypto extension - - Zvkb vector crypto extension - config CRYPTO_SM3_RISCV64 tristate "Hash functions: SM3 (ShangMi 3)" depends on 64BIT && RISCV_ISA_V && TOOLCHAIN_HAS_VECTOR_CRYPTO diff --git a/arch/riscv/crypto/Makefile b/arch/riscv/crypto/Makefile index e10e8257734e..183495a95cc0 100644 --- a/arch/riscv/crypto/Makefile +++ b/arch/riscv/crypto/Makefile @@ -7,9 +7,6 @@ aes-riscv64-y := aes-riscv64-glue.o aes-riscv64-zvkned.o \ obj-$(CONFIG_CRYPTO_GHASH_RISCV64) += ghash-riscv64.o ghash-riscv64-y := ghash-riscv64-glue.o ghash-riscv64-zvkg.o -obj-$(CONFIG_CRYPTO_SHA512_RISCV64) += sha512-riscv64.o -sha512-riscv64-y := sha512-riscv64-glue.o sha512-riscv64-zvknhb-zvkb.o - obj-$(CONFIG_CRYPTO_SM3_RISCV64) += sm3-riscv64.o sm3-riscv64-y := sm3-riscv64-glue.o sm3-riscv64-zvksh-zvkb.o diff --git a/arch/riscv/crypto/sha512-riscv64-glue.c b/arch/riscv/crypto/sha512-riscv64-glue.c deleted file mode 100644 index 4634fca78ae2..000000000000 --- a/arch/riscv/crypto/sha512-riscv64-glue.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-512 and SHA-384 using the RISC-V vector crypto extensions - * - * Copyright (C) 2023 VRULL GmbH - * Author: Heiko Stuebner - * - * Copyright (C) 2023 SiFive, Inc. - * Author: Jerry Shih - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Note: the asm function only uses the 'state' field of struct sha512_state. - * It is assumed to be the first field. - */ -asmlinkage void sha512_transform_zvknhb_zvkb( - struct sha512_state *state, const u8 *data, int num_blocks); - -static void sha512_block(struct sha512_state *state, const u8 *data, - int num_blocks) -{ - /* - * Ensure struct sha512_state begins directly with the SHA-512 - * 512-bit internal state, as this is what the asm function expects. - */ - BUILD_BUG_ON(offsetof(struct sha512_state, state) != 0); - - if (crypto_simd_usable()) { - kernel_vector_begin(); - sha512_transform_zvknhb_zvkb(state, data, num_blocks); - kernel_vector_end(); - } else { - sha512_generic_block_fn(state, data, num_blocks); - } -} - -static int riscv64_sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, sha512_block); -} - -static int riscv64_sha512_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha512_base_do_finup(desc, data, len, sha512_block); - return sha512_base_finish(desc, out); -} - -static int riscv64_sha512_digest(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha512_base_init(desc) ?: - riscv64_sha512_finup(desc, data, len, out); -} - -static struct shash_alg riscv64_sha512_algs[] = { - { - .init = sha512_base_init, - .update = riscv64_sha512_update, - .finup = riscv64_sha512_finup, - .digest = riscv64_sha512_digest, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA512_DIGEST_SIZE, - .base = { - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_name = "sha512", - .cra_driver_name = "sha512-riscv64-zvknhb-zvkb", - .cra_module = THIS_MODULE, - }, - }, { - .init = sha384_base_init, - .update = riscv64_sha512_update, - .finup = riscv64_sha512_finup, - .descsize = SHA512_STATE_SIZE, - .digestsize = SHA384_DIGEST_SIZE, - .base = { - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_name = "sha384", - .cra_driver_name = "sha384-riscv64-zvknhb-zvkb", - .cra_module = THIS_MODULE, - }, - }, -}; - -static int __init riscv64_sha512_mod_init(void) -{ - if (riscv_isa_extension_available(NULL, ZVKNHB) && - riscv_isa_extension_available(NULL, ZVKB) && - riscv_vector_vlen() >= 128) - return crypto_register_shashes(riscv64_sha512_algs, - ARRAY_SIZE(riscv64_sha512_algs)); - - return -ENODEV; -} - -static void __exit riscv64_sha512_mod_exit(void) -{ - crypto_unregister_shashes(riscv64_sha512_algs, - ARRAY_SIZE(riscv64_sha512_algs)); -} - -module_init(riscv64_sha512_mod_init); -module_exit(riscv64_sha512_mod_exit); - -MODULE_DESCRIPTION("SHA-512 (RISC-V accelerated)"); -MODULE_AUTHOR("Heiko Stuebner "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CRYPTO("sha512"); -MODULE_ALIAS_CRYPTO("sha384"); diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h index a8a2af6dfe9d..2a16e88e13de 100644 --- a/arch/riscv/include/asm/asm.h +++ b/arch/riscv/include/asm/asm.h @@ -91,7 +91,7 @@ #endif .macro asm_per_cpu dst sym tmp - REG_L \tmp, TASK_TI_CPU_NUM(tp) + lw \tmp, TASK_TI_CPU_NUM(tp) slli \tmp, \tmp, PER_CPU_OFFSET_SHIFT la \dst, __per_cpu_offset add \dst, \dst, \tmp diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h index 1aaea81fb141..4c03e20ad11f 100644 --- a/arch/riscv/include/asm/bug.h +++ b/arch/riscv/include/asm/bug.h @@ -31,40 +31,45 @@ typedef u32 bug_insn_t; #ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS #define __BUG_ENTRY_ADDR RISCV_INT " 1b - ." -#define __BUG_ENTRY_FILE RISCV_INT " %0 - ." +#define __BUG_ENTRY_FILE(file) RISCV_INT " " file " - ." #else #define __BUG_ENTRY_ADDR RISCV_PTR " 1b" -#define __BUG_ENTRY_FILE RISCV_PTR " %0" +#define __BUG_ENTRY_FILE(file) RISCV_PTR " " file #endif #ifdef CONFIG_DEBUG_BUGVERBOSE -#define __BUG_ENTRY \ +#define __BUG_ENTRY(file, line, flags) \ __BUG_ENTRY_ADDR "\n\t" \ - __BUG_ENTRY_FILE "\n\t" \ - RISCV_SHORT " %1\n\t" \ - RISCV_SHORT " %2" + __BUG_ENTRY_FILE(file) "\n\t" \ + RISCV_SHORT " " line "\n\t" \ + RISCV_SHORT " " flags #else -#define __BUG_ENTRY \ - __BUG_ENTRY_ADDR "\n\t" \ - RISCV_SHORT " %2" +#define __BUG_ENTRY(file, line, flags) \ + __BUG_ENTRY_ADDR "\n\t" \ + RISCV_SHORT " " flags #endif #ifdef CONFIG_GENERIC_BUG -#define __BUG_FLAGS(flags) \ -do { \ - __asm__ __volatile__ ( \ + +#define ARCH_WARN_ASM(file, line, flags, size) \ "1:\n\t" \ "ebreak\n" \ ".pushsection __bug_table,\"aw\"\n\t" \ "2:\n\t" \ - __BUG_ENTRY "\n\t" \ - ".org 2b + %3\n\t" \ + __BUG_ENTRY(file, line, flags) "\n\t" \ + ".org 2b + " size "\n\t" \ ".popsection" \ + +#define __BUG_FLAGS(flags) \ +do { \ + __asm__ __volatile__ ( \ + ARCH_WARN_ASM("%0", "%1", "%2", "%3") \ : \ : "i" (__FILE__), "i" (__LINE__), \ "i" (flags), \ "i" (sizeof(struct bug_entry))); \ } while (0) + #else /* CONFIG_GENERIC_BUG */ #define __BUG_FLAGS(flags) do { \ __asm__ __volatile__ ("ebreak\n"); \ @@ -78,6 +83,8 @@ do { \ #define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) +#define ARCH_WARN_REACHABLE + #define HAVE_ARCH_BUG #include diff --git a/arch/riscv/include/asm/cfi.h b/arch/riscv/include/asm/cfi.h index fb9696d7a3f2..4508aaa7a2fd 100644 --- a/arch/riscv/include/asm/cfi.h +++ b/arch/riscv/include/asm/cfi.h @@ -14,27 +14,11 @@ struct pt_regs; #ifdef CONFIG_CFI_CLANG enum bug_trap_type handle_cfi_failure(struct pt_regs *regs); #define __bpfcall -static inline int cfi_get_offset(void) -{ - return 4; -} - -#define cfi_get_offset cfi_get_offset -extern u32 cfi_bpf_hash; -extern u32 cfi_bpf_subprog_hash; -extern u32 cfi_get_func_hash(void *func); #else static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) { return BUG_TRAP_TYPE_NONE; } - -#define cfi_bpf_hash 0U -#define cfi_bpf_subprog_hash 0U -static inline u32 cfi_get_func_hash(void *func) -{ - return 0; -} #endif /* CONFIG_CFI_CLANG */ #endif /* _ASM_RISCV_CFI_H */ diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h index 7b038f3b7cb0..59c975f750c9 100644 --- a/arch/riscv/include/asm/irq.h +++ b/arch/riscv/include/asm/irq.h @@ -22,6 +22,8 @@ void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu); void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void)); struct fwnode_handle *riscv_get_intc_hwnode(void); +int riscv_get_hart_index(struct fwnode_handle *fwnode, u32 logical_index, + u32 *hart_index); #ifdef CONFIG_ACPI diff --git a/arch/riscv/include/asm/kvm_aia.h b/arch/riscv/include/asm/kvm_aia.h index 3b643b9efc07..b04ecdd1a860 100644 --- a/arch/riscv/include/asm/kvm_aia.h +++ b/arch/riscv/include/asm/kvm_aia.h @@ -87,6 +87,9 @@ DECLARE_STATIC_KEY_FALSE(kvm_riscv_aia_available); extern struct kvm_device_ops kvm_riscv_aia_device_ops; +bool kvm_riscv_vcpu_aia_imsic_has_interrupt(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_aia_imsic_load(struct kvm_vcpu *vcpu, int cpu); +void kvm_riscv_vcpu_aia_imsic_put(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu); int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu); @@ -147,7 +150,7 @@ int kvm_riscv_vcpu_aia_rmw_ireg(struct kvm_vcpu *vcpu, unsigned int csr_num, int kvm_riscv_vcpu_aia_update(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_aia_reset(struct kvm_vcpu *vcpu); -int kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu); int kvm_riscv_aia_inject_msi_by_id(struct kvm *kvm, u32 hart_index, @@ -161,7 +164,6 @@ void kvm_riscv_aia_destroy_vm(struct kvm *kvm); int kvm_riscv_aia_alloc_hgei(int cpu, struct kvm_vcpu *owner, void __iomem **hgei_va, phys_addr_t *hgei_pa); void kvm_riscv_aia_free_hgei(int cpu, int hgei); -void kvm_riscv_aia_wakeon_hgei(struct kvm_vcpu *owner, bool enable); void kvm_riscv_aia_enable(void); void kvm_riscv_aia_disable(void); diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h new file mode 100644 index 000000000000..595e2183173e --- /dev/null +++ b/arch/riscv/include/asm/kvm_gstage.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#ifndef __RISCV_KVM_GSTAGE_H_ +#define __RISCV_KVM_GSTAGE_H_ + +#include + +struct kvm_gstage { + struct kvm *kvm; + unsigned long flags; +#define KVM_GSTAGE_FLAGS_LOCAL BIT(0) + unsigned long vmid; + pgd_t *pgd; +}; + +struct kvm_gstage_mapping { + gpa_t addr; + pte_t pte; + u32 level; +}; + +#ifdef CONFIG_64BIT +#define kvm_riscv_gstage_index_bits 9 +#else +#define kvm_riscv_gstage_index_bits 10 +#endif + +extern unsigned long kvm_riscv_gstage_mode; +extern unsigned long kvm_riscv_gstage_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)) + +bool kvm_riscv_gstage_get_leaf(struct kvm_gstage *gstage, gpa_t addr, + pte_t **ptepp, u32 *ptep_level); + +int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, + struct kvm_mmu_memory_cache *pcache, + const struct kvm_gstage_mapping *map); + +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); + +enum kvm_riscv_gstage_op { + GSTAGE_OP_NOP = 0, /* Nothing */ + GSTAGE_OP_CLEAR, /* Clear/Unmap */ + GSTAGE_OP_WP, /* Write-protect */ +}; + +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); + +void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, + gpa_t start, gpa_t size, bool may_block); + +void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end); + +void kvm_riscv_gstage_mode_detect(void); + +#endif diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 85cfebc32e4c..d71d3299a335 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,14 +38,16 @@ #define KVM_REQ_UPDATE_HGATP KVM_ARCH_REQ(2) #define KVM_REQ_FENCE_I \ KVM_ARCH_REQ_FLAGS(3, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) -#define KVM_REQ_HFENCE_GVMA_VMID_ALL KVM_REQ_TLB_FLUSH #define KVM_REQ_HFENCE_VVMA_ALL \ KVM_ARCH_REQ_FLAGS(4, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_HFENCE \ KVM_ARCH_REQ_FLAGS(5, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(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) | \ @@ -54,24 +58,6 @@ BIT(IRQ_VS_TIMER) | \ BIT(IRQ_VS_EXT)) -enum kvm_riscv_hfence_type { - KVM_RISCV_HFENCE_UNKNOWN = 0, - KVM_RISCV_HFENCE_GVMA_VMID_GPA, - KVM_RISCV_HFENCE_VVMA_ASID_GVA, - KVM_RISCV_HFENCE_VVMA_ASID_ALL, - KVM_RISCV_HFENCE_VVMA_GVA, -}; - -struct kvm_riscv_hfence { - enum kvm_riscv_hfence_type type; - unsigned long asid; - unsigned long order; - gpa_t addr; - gpa_t size; -}; - -#define KVM_RISCV_VCPU_MAX_HFENCE 64 - struct kvm_vm_stat { struct kvm_vm_stat_generic generic; }; @@ -97,15 +83,6 @@ struct kvm_vcpu_stat { struct kvm_arch_memory_slot { }; -struct kvm_vmid { - /* - * Writes to vmid_version and vmid happen with vmid_lock held - * whereas reads happen without any lock held. - */ - unsigned long vmid_version; - unsigned long vmid; -}; - struct kvm_arch { /* G-stage vmid */ struct kvm_vmid vmid; @@ -306,76 +283,8 @@ static inline bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu) return IS_ENABLED(CONFIG_GUEST_PERF_EVENTS) && !!vcpu; } -#define KVM_RISCV_GSTAGE_TLB_MIN_ORDER 12 - -void kvm_riscv_local_hfence_gvma_vmid_gpa(unsigned long vmid, - gpa_t gpa, gpa_t gpsz, - unsigned long order); -void kvm_riscv_local_hfence_gvma_vmid_all(unsigned long vmid); -void kvm_riscv_local_hfence_gvma_gpa(gpa_t gpa, gpa_t gpsz, - unsigned long order); -void kvm_riscv_local_hfence_gvma_all(void); -void kvm_riscv_local_hfence_vvma_asid_gva(unsigned long vmid, - unsigned long asid, - unsigned long gva, - unsigned long gvsz, - unsigned long order); -void kvm_riscv_local_hfence_vvma_asid_all(unsigned long vmid, - unsigned long asid); -void kvm_riscv_local_hfence_vvma_gva(unsigned long vmid, - unsigned long gva, unsigned long gvsz, - unsigned long order); -void kvm_riscv_local_hfence_vvma_all(unsigned long vmid); - -void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu); - -void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu); -void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu); -void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu); -void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu); - -void kvm_riscv_fence_i(struct kvm *kvm, - unsigned long hbase, unsigned long hmask); -void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm, - unsigned long hbase, unsigned long hmask, - gpa_t gpa, gpa_t gpsz, - unsigned long order); -void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm, - unsigned long hbase, unsigned long hmask); -void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm, - unsigned long hbase, unsigned long hmask, - unsigned long gva, unsigned long gvsz, - unsigned long order, unsigned long asid); -void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm, - unsigned long hbase, unsigned long hmask, - unsigned long asid); -void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, - unsigned long hbase, unsigned long hmask, - unsigned long gva, unsigned long gvsz, - unsigned long order); -void kvm_riscv_hfence_vvma_all(struct kvm *kvm, - unsigned long hbase, unsigned long hmask); - -int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, - phys_addr_t hpa, unsigned long size, - bool writable, bool in_atomic); -void kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, - unsigned long size); -int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, - struct kvm_memory_slot *memslot, - gpa_t gpa, unsigned long hva, bool is_write); -int kvm_riscv_gstage_alloc_pgd(struct kvm *kvm); -void kvm_riscv_gstage_free_pgd(struct kvm *kvm); -void kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu); -void __init kvm_riscv_gstage_mode_detect(void); -unsigned long __init kvm_riscv_gstage_mode(void); -int kvm_riscv_gstage_gpa_bits(void); - -void __init kvm_riscv_gstage_vmid_detect(void); -unsigned long kvm_riscv_gstage_vmid_bits(void); -int kvm_riscv_gstage_vmid_init(struct kvm *kvm); -bool kvm_riscv_gstage_vmid_ver_changed(struct kvm_vmid *vmid); -void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu); +static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} +static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {} int kvm_riscv_setup_default_irq_routing(struct kvm *kvm, u32 lines); @@ -412,7 +321,6 @@ void __kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu); bool kvm_riscv_vcpu_stopped(struct kvm_vcpu *vcpu); -void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu *vcpu); #endif /* __RISCV_KVM_HOST_H__ */ diff --git a/arch/riscv/include/asm/kvm_mmu.h b/arch/riscv/include/asm/kvm_mmu.h new file mode 100644 index 000000000000..5439e76f0a96 --- /dev/null +++ b/arch/riscv/include/asm/kvm_mmu.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#ifndef __RISCV_KVM_MMU_H_ +#define __RISCV_KVM_MMU_H_ + +#include + +int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, + unsigned long size, bool writable, bool in_atomic); +void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size); +int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, + gpa_t gpa, unsigned long hva, bool is_write, + struct kvm_gstage_mapping *out_map); +int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm); +void kvm_riscv_mmu_free_pgd(struct kvm *kvm); +void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu); + +#endif diff --git a/arch/riscv/include/asm/kvm_tlb.h b/arch/riscv/include/asm/kvm_tlb.h new file mode 100644 index 000000000000..38a2f933ad3a --- /dev/null +++ b/arch/riscv/include/asm/kvm_tlb.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#ifndef __RISCV_KVM_TLB_H_ +#define __RISCV_KVM_TLB_H_ + +#include + +enum kvm_riscv_hfence_type { + KVM_RISCV_HFENCE_UNKNOWN = 0, + KVM_RISCV_HFENCE_GVMA_VMID_GPA, + KVM_RISCV_HFENCE_GVMA_VMID_ALL, + KVM_RISCV_HFENCE_VVMA_ASID_GVA, + KVM_RISCV_HFENCE_VVMA_ASID_ALL, + KVM_RISCV_HFENCE_VVMA_GVA, + KVM_RISCV_HFENCE_VVMA_ALL +}; + +struct kvm_riscv_hfence { + enum kvm_riscv_hfence_type type; + unsigned long asid; + unsigned long vmid; + unsigned long order; + gpa_t addr; + gpa_t size; +}; + +#define KVM_RISCV_VCPU_MAX_HFENCE 64 + +#define KVM_RISCV_GSTAGE_TLB_MIN_ORDER 12 + +void kvm_riscv_local_hfence_gvma_vmid_gpa(unsigned long vmid, + gpa_t gpa, gpa_t gpsz, + unsigned long order); +void kvm_riscv_local_hfence_gvma_vmid_all(unsigned long vmid); +void kvm_riscv_local_hfence_gvma_gpa(gpa_t gpa, gpa_t gpsz, + unsigned long order); +void kvm_riscv_local_hfence_gvma_all(void); +void kvm_riscv_local_hfence_vvma_asid_gva(unsigned long vmid, + unsigned long asid, + unsigned long gva, + unsigned long gvsz, + unsigned long order); +void kvm_riscv_local_hfence_vvma_asid_all(unsigned long vmid, + unsigned long asid); +void kvm_riscv_local_hfence_vvma_gva(unsigned long vmid, + unsigned long gva, unsigned long gvsz, + unsigned long order); +void kvm_riscv_local_hfence_vvma_all(unsigned long vmid); + +void kvm_riscv_tlb_flush_process(struct kvm_vcpu *vcpu); + +void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu); +void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu); +void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu); + +void kvm_riscv_fence_i(struct kvm *kvm, + unsigned long hbase, unsigned long hmask); +void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + gpa_t gpa, gpa_t gpsz, + unsigned long order, unsigned long vmid); +void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + unsigned long vmid); +void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + unsigned long gva, unsigned long gvsz, + unsigned long order, unsigned long asid, + unsigned long vmid); +void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + unsigned long asid, unsigned long vmid); +void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + unsigned long gva, unsigned long gvsz, + unsigned long order, unsigned long vmid); +void kvm_riscv_hfence_vvma_all(struct kvm *kvm, + unsigned long hbase, unsigned long hmask, + unsigned long vmid); + +#endif diff --git a/arch/riscv/include/asm/kvm_vcpu_sbi.h b/arch/riscv/include/asm/kvm_vcpu_sbi.h index 439ab2b3534f..d678fd7e5973 100644 --- a/arch/riscv/include/asm/kvm_vcpu_sbi.h +++ b/arch/riscv/include/asm/kvm_vcpu_sbi.h @@ -49,6 +49,16 @@ struct kvm_vcpu_sbi_extension { /* Extension specific probe function */ unsigned long (*probe)(struct kvm_vcpu *vcpu); + + /* + * Init/deinit function called once during VCPU init/destroy. These + * might be use if the SBI extensions need to allocate or do specific + * init time only configuration. + */ + int (*init)(struct kvm_vcpu *vcpu); + void (*deinit)(struct kvm_vcpu *vcpu); + + void (*reset)(struct kvm_vcpu *vcpu); }; void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run); @@ -72,6 +82,8 @@ const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext( bool riscv_vcpu_supports_sbi_ext(struct kvm_vcpu *vcpu, int idx); int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run); void kvm_riscv_vcpu_sbi_init(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_sbi_deinit(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_sbi_reset(struct kvm_vcpu *vcpu); int kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu *vcpu, unsigned long reg_num, unsigned long *reg_val); diff --git a/arch/riscv/include/asm/kvm_vmid.h b/arch/riscv/include/asm/kvm_vmid.h new file mode 100644 index 000000000000..ab98e1434fb7 --- /dev/null +++ b/arch/riscv/include/asm/kvm_vmid.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#ifndef __RISCV_KVM_VMID_H_ +#define __RISCV_KVM_VMID_H_ + +#include + +struct kvm_vmid { + /* + * Writes to vmid_version and vmid happen with vmid_lock held + * whereas reads happen without any lock held. + */ + unsigned long vmid_version; + unsigned long vmid; +}; + +void __init kvm_riscv_gstage_vmid_detect(void); +unsigned long kvm_riscv_gstage_vmid_bits(void); +int kvm_riscv_gstage_vmid_init(struct kvm *kvm); +bool kvm_riscv_gstage_vmid_ver_changed(struct kvm_vmid *vmid); +void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu); +void kvm_riscv_gstage_vmid_sanitize(struct kvm_vcpu *vcpu); + +#endif diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h index 7de05db7d3bd..1018d2216901 100644 --- a/arch/riscv/include/asm/pgtable-64.h +++ b/arch/riscv/include/asm/pgtable-64.h @@ -397,24 +397,8 @@ static inline struct page *pgd_page(pgd_t pgd) p4d_t *p4d_offset(pgd_t *pgd, unsigned long address); #ifdef CONFIG_TRANSPARENT_HUGEPAGE -static inline int pte_devmap(pte_t pte); static inline pte_t pmd_pte(pmd_t pmd); static inline pte_t pud_pte(pud_t pud); - -static inline int pmd_devmap(pmd_t pmd) -{ - return pte_devmap(pmd_pte(pmd)); -} - -static inline int pud_devmap(pud_t pud) -{ - return pte_devmap(pud_pte(pud)); -} - -static inline int pgd_devmap(pgd_t pgd) -{ - return 0; -} #endif #endif /* _ASM_RISCV_PGTABLE_64_H */ diff --git a/arch/riscv/include/asm/pgtable-bits.h b/arch/riscv/include/asm/pgtable-bits.h index a8f5205cea54..179bd4afece4 100644 --- a/arch/riscv/include/asm/pgtable-bits.h +++ b/arch/riscv/include/asm/pgtable-bits.h @@ -19,7 +19,6 @@ #define _PAGE_SOFT (3 << 8) /* Reserved for software */ #define _PAGE_SPECIAL (1 << 8) /* RSW: 0x1 */ -#define _PAGE_DEVMAP (1 << 9) /* RSW, devmap */ #define _PAGE_TABLE _PAGE_PRESENT /* diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 438ce7df24c3..91697fbf1f90 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -409,13 +409,6 @@ static inline int pte_special(pte_t pte) return pte_val(pte) & _PAGE_SPECIAL; } -#ifdef CONFIG_ARCH_HAS_PTE_DEVMAP -static inline int pte_devmap(pte_t pte) -{ - return pte_val(pte) & _PAGE_DEVMAP; -} -#endif - /* static inline pte_t pte_rdprotect(pte_t pte) */ static inline pte_t pte_wrprotect(pte_t pte) @@ -457,11 +450,6 @@ static inline pte_t pte_mkspecial(pte_t pte) return __pte(pte_val(pte) | _PAGE_SPECIAL); } -static inline pte_t pte_mkdevmap(pte_t pte) -{ - return __pte(pte_val(pte) | _PAGE_DEVMAP); -} - static inline pte_t pte_mkhuge(pte_t pte) { return pte; @@ -790,11 +778,6 @@ static inline pmd_t pmd_mkdirty(pmd_t pmd) return pte_pmd(pte_mkdirty(pmd_pte(pmd))); } -static inline pmd_t pmd_mkdevmap(pmd_t pmd) -{ - return pte_pmd(pte_mkdevmap(pmd_pte(pmd))); -} - #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP static inline bool pmd_special(pmd_t pmd) { @@ -946,11 +929,6 @@ static inline pud_t pud_mkhuge(pud_t pud) return pud; } -static inline pud_t pud_mkdevmap(pud_t pud) -{ - return pte_pud(pte_mkdevmap(pud_pte(pud))); -} - static inline int pudp_set_access_flags(struct vm_area_struct *vma, unsigned long address, pud_t *pudp, pud_t entry, int dirty) @@ -1075,7 +1053,6 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) */ #ifdef CONFIG_64BIT #define TASK_SIZE_64 (PGDIR_SIZE * PTRS_PER_PGD / 2) -#define TASK_SIZE_MAX LONG_MAX #ifdef CONFIG_COMPAT #define TASK_SIZE_32 (_AC(0x80000000, UL) - PAGE_SIZE) diff --git a/arch/riscv/include/asm/runtime-const.h b/arch/riscv/include/asm/runtime-const.h index 451fd76b8811..d766e2b9e6df 100644 --- a/arch/riscv/include/asm/runtime-const.h +++ b/arch/riscv/include/asm/runtime-const.h @@ -206,7 +206,7 @@ static inline void __runtime_fixup_32(__le16 *lui_parcel, __le16 *addi_parcel, u addi_insn_mask &= 0x07fff; } - if (lower_immediate & 0x00000fff) { + if (lower_immediate & 0x00000fff || lui_insn == RISCV_INSN_NOP4) { /* replace upper 12 bits of addi with lower 12 bits of val */ addi_insn &= addi_insn_mask; addi_insn |= (lower_immediate & 0x00000fff) << 20; diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h index 1a20dd746a49..eed0abc40514 100644 --- a/arch/riscv/include/asm/tlbflush.h +++ b/arch/riscv/include/asm/tlbflush.h @@ -63,7 +63,6 @@ void flush_pud_tlb_range(struct vm_area_struct *vma, unsigned long start, bool arch_tlbbatch_should_defer(struct mm_struct *mm); void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch, struct mm_struct *mm, unsigned long start, unsigned long end); -void arch_flush_tlb_batched_pending(struct mm_struct *mm); void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch); extern unsigned long tlb_flush_all_threshold; diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index d472da4450e6..f5f4f7f85543 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -127,6 +127,7 @@ do { \ #ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT #define __get_user_8(x, ptr, label) \ +do { \ u32 __user *__ptr = (u32 __user *)(ptr); \ u32 __lo, __hi; \ asm_goto_output( \ @@ -141,7 +142,7 @@ do { \ : : label); \ (x) = (__typeof__(x))((__typeof__((x) - (x)))( \ (((u64)__hi << 32) | __lo))); \ - +} while (0) #else /* !CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ #define __get_user_8(x, ptr, label) \ do { \ @@ -208,7 +209,7 @@ do { \ err = 0; \ break; \ __gu_failed: \ - x = 0; \ + x = (__typeof__(x))0; \ err = -EFAULT; \ } while (0) @@ -310,8 +311,8 @@ do { \ do { \ if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && \ !IS_ALIGNED((uintptr_t)__gu_ptr, sizeof(*__gu_ptr))) { \ - __inttype(x) val = (__inttype(x))x; \ - if (__asm_copy_to_user_sum_enabled(__gu_ptr, &(val), sizeof(*__gu_ptr))) \ + __typeof__(*(__gu_ptr)) ___val = (x); \ + if (__asm_copy_to_user_sum_enabled(__gu_ptr, &(___val), sizeof(*__gu_ptr))) \ goto label; \ break; \ } \ @@ -437,10 +438,10 @@ unsigned long __must_check clear_user(void __user *to, unsigned long n) } #define __get_kernel_nofault(dst, src, type, err_label) \ - __get_user_nocheck(*((type *)(dst)), (type *)(src), err_label) + __get_user_nocheck(*((type *)(dst)), (__force __user type *)(src), err_label) #define __put_kernel_nofault(dst, src, type, err_label) \ - __put_user_nocheck(*((type *)(src)), (type *)(dst), err_label) + __put_user_nocheck(*((type *)(src)), (__force __user type *)(dst), err_label) static __must_check __always_inline bool user_access_begin(const void __user *ptr, size_t len) { diff --git a/arch/riscv/include/asm/vdso/getrandom.h b/arch/riscv/include/asm/vdso/getrandom.h index 8dc92441702a..c6d66895c1f5 100644 --- a/arch/riscv/include/asm/vdso/getrandom.h +++ b/arch/riscv/include/asm/vdso/getrandom.h @@ -18,7 +18,7 @@ static __always_inline ssize_t getrandom_syscall(void *_buffer, size_t _len, uns register unsigned int flags asm("a2") = _flags; asm volatile ("ecall\n" - : "+r" (ret) + : "=r" (ret) : "r" (nr), "r" (buffer), "r" (len), "r" (flags) : "memory"); diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index 45c9b426fcc5..b61786d43c20 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -205,11 +205,11 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to, THEAD_VSETVLI_T4X0E8M8D1 THEAD_VSB_V_V0T0 "add t0, t0, t4\n\t" - THEAD_VSB_V_V0T0 + THEAD_VSB_V_V8T0 "add t0, t0, t4\n\t" - THEAD_VSB_V_V0T0 + THEAD_VSB_V_V16T0 "add t0, t0, t4\n\t" - THEAD_VSB_V_V0T0 + THEAD_VSB_V_V24T0 : : "r" (datap) : "memory", "t0", "t4"); } else { asm volatile ( @@ -241,11 +241,11 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_ THEAD_VSETVLI_T4X0E8M8D1 THEAD_VLB_V_V0T0 "add t0, t0, t4\n\t" - THEAD_VLB_V_V0T0 + THEAD_VLB_V_V8T0 "add t0, t0, t4\n\t" - THEAD_VLB_V_V0T0 + THEAD_VLB_V_V16T0 "add t0, t0, t4\n\t" - THEAD_VLB_V_V0T0 + THEAD_VLB_V_V24T0 : : "r" (datap) : "memory", "t0", "t4"); } else { asm volatile ( diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 5f59fd226cc5..ef27d4289da1 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -18,6 +18,7 @@ #define __KVM_HAVE_IRQ_LINE #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 #define KVM_INTERRUPT_SET -1U #define KVM_INTERRUPT_UNSET -2U diff --git a/arch/riscv/kernel/cfi.c b/arch/riscv/kernel/cfi.c index 64bdd3e1ab8c..6ec9dbd7292e 100644 --- a/arch/riscv/kernel/cfi.c +++ b/arch/riscv/kernel/cfi.c @@ -75,56 +75,3 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) return report_cfi_failure(regs, regs->epc, &target, type); } - -#ifdef CONFIG_CFI_CLANG -struct bpf_insn; - -/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */ -extern unsigned int __bpf_prog_runX(const void *ctx, - const struct bpf_insn *insn); - -/* - * Force a reference to the external symbol so the compiler generates - * __kcfi_typid. - */ -__ADDRESSABLE(__bpf_prog_runX); - -/* u32 __ro_after_init cfi_bpf_hash = __kcfi_typeid___bpf_prog_runX; */ -asm ( -" .pushsection .data..ro_after_init,\"aw\",@progbits \n" -" .type cfi_bpf_hash,@object \n" -" .globl cfi_bpf_hash \n" -" .p2align 2, 0x0 \n" -"cfi_bpf_hash: \n" -" .word __kcfi_typeid___bpf_prog_runX \n" -" .size cfi_bpf_hash, 4 \n" -" .popsection \n" -); - -/* Must match bpf_callback_t */ -extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64); - -__ADDRESSABLE(__bpf_callback_fn); - -/* u32 __ro_after_init cfi_bpf_subprog_hash = __kcfi_typeid___bpf_callback_fn; */ -asm ( -" .pushsection .data..ro_after_init,\"aw\",@progbits \n" -" .type cfi_bpf_subprog_hash,@object \n" -" .globl cfi_bpf_subprog_hash \n" -" .p2align 2, 0x0 \n" -"cfi_bpf_subprog_hash: \n" -" .word __kcfi_typeid___bpf_callback_fn \n" -" .size cfi_bpf_subprog_hash, 4 \n" -" .popsection \n" -); - -u32 cfi_get_func_hash(void *func) -{ - u32 hash; - - if (get_kernel_nofault(hash, func - cfi_get_offset())) - return 0; - - return hash; -} -#endif diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index e6fbaaf54956..87d655944803 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -18,10 +18,10 @@ const struct cpu_operations cpu_ops_sbi; /* * Ordered booting via HSM brings one cpu at a time. However, cpu hotplug can - * be invoked from multiple threads in parallel. Define a per cpu data + * be invoked from multiple threads in parallel. Define an array of boot data * to handle that. */ -static DEFINE_PER_CPU(struct sbi_hart_boot_data, boot_data); +static struct sbi_hart_boot_data boot_data[NR_CPUS]; static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, unsigned long priv) @@ -67,7 +67,7 @@ static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle) unsigned long boot_addr = __pa_symbol(secondary_start_sbi); unsigned long hartid = cpuid_to_hartid_map(cpuid); unsigned long hsm_data; - struct sbi_hart_boot_data *bdata = &per_cpu(boot_data, cpuid); + struct sbi_hart_boot_data *bdata = &boot_data[cpuid]; /* Make sure tidle is updated */ smp_mb(); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 75656afa2d6b..d0ded2438533 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -46,7 +46,7 @@ * a0 = &new_vmalloc[BIT_WORD(cpu)] * a1 = BIT_MASK(cpu) */ - REG_L a2, TASK_TI_CPU(tp) + lw a2, TASK_TI_CPU(tp) /* * Compute the new_vmalloc element position: * (cpu / 64) * 8 = (cpu >> 6) << 3 @@ -220,7 +220,7 @@ SYM_CODE_START_NOALIGN(ret_from_exception) #endif bnez s0, 1f -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE call stackleak_erase_on_task_stack #endif diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index 4c6c24380cfd..8d18d6727f0f 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -14,6 +14,18 @@ #include #ifdef CONFIG_DYNAMIC_FTRACE +void ftrace_arch_code_modify_prepare(void) + __acquires(&text_mutex) +{ + mutex_lock(&text_mutex); +} + +void ftrace_arch_code_modify_post_process(void) + __releases(&text_mutex) +{ + mutex_unlock(&text_mutex); +} + unsigned long ftrace_call_adjust(unsigned long addr) { if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS)) @@ -29,10 +41,8 @@ unsigned long arch_ftrace_get_symaddr(unsigned long fentry_ip) void arch_ftrace_update_code(int command) { - mutex_lock(&text_mutex); command |= FTRACE_MAY_SLEEP; ftrace_modify_all_code(command); - mutex_unlock(&text_mutex); flush_icache_all(); } @@ -149,6 +159,8 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) unsigned int nops[2], offset; int ret; + guard(mutex)(&text_mutex); + ret = ftrace_rec_set_nop_ops(rec); if (ret) return ret; @@ -157,9 +169,7 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) nops[0] = to_auipc_t0(offset); nops[1] = RISCV_INSN_NOP4; - mutex_lock(&text_mutex); ret = patch_insn_write((void *)pc, nops, 2 * MCOUNT_INSN_SIZE); - mutex_unlock(&text_mutex); return ret; } diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 9ceda02507ca..b6af20bc300f 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -32,6 +32,40 @@ struct fwnode_handle *riscv_get_intc_hwnode(void) } EXPORT_SYMBOL_GPL(riscv_get_intc_hwnode); +/** + * riscv_get_hart_index() - get hart index for interrupt delivery + * @fwnode: interrupt controller node + * @logical_index: index within the "interrupts-extended" property + * @hart_index: filled with the hart index to use + * + * RISC-V uses term "hart index" for its interrupt controllers, for the + * purpose of the interrupt routing to destination harts. + * It may be arbitrary numbers assigned to each destination hart in context + * of the particular interrupt domain. + * + * These numbers encoded in the optional property "riscv,hart-indexes" + * that should contain hart index for each interrupt destination in the same + * order as in the "interrupts-extended" property. If this property + * not exist, it assumed equal to the logical index, i.e. index within the + * "interrupts-extended" property. + * + * Return: error code + */ +int riscv_get_hart_index(struct fwnode_handle *fwnode, u32 logical_index, + u32 *hart_index) +{ + static const char *prop_hart_index = "riscv,hart-indexes"; + struct device_node *np = to_of_node(fwnode); + + if (!np || !of_property_present(np, prop_hart_index)) { + *hart_index = logical_index; + return 0; + } + + return of_property_read_u32_index(np, prop_hart_index, + logical_index, hart_index); +} + #ifdef CONFIG_IRQ_STACKS #include diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c index f4755d49b89e..531d348db84d 100644 --- a/arch/riscv/kernel/kexec_elf.c +++ b/arch/riscv/kernel/kexec_elf.c @@ -28,7 +28,7 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, int i; int ret = 0; size_t size; - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; const struct elf_phdr *phdr; kbuf.image = image; @@ -66,7 +66,7 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len, { int i; int ret; - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; const struct elf_phdr *phdr; unsigned long lowest_paddr = ULONG_MAX; unsigned long lowest_vaddr = ULONG_MAX; @@ -95,6 +95,7 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len, kbuf.buf_align = PMD_SIZE; kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE); + kbuf.cma = NULL; kbuf.top_down = false; ret = arch_kexec_locate_mem_hole(&kbuf); if (!ret) { diff --git a/arch/riscv/kernel/kexec_image.c b/arch/riscv/kernel/kexec_image.c index 26a81774a78a..8f2eb900910b 100644 --- a/arch/riscv/kernel/kexec_image.c +++ b/arch/riscv/kernel/kexec_image.c @@ -41,7 +41,7 @@ static void *image_load(struct kimage *image, struct riscv_image_header *h; u64 flags; bool be_image, be_kernel; - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; int ret; /* Check Image header */ diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index e36104af2e24..b9eb41b0a975 100644 --- a/arch/riscv/kernel/machine_kexec_file.c +++ b/arch/riscv/kernel/machine_kexec_file.c @@ -261,7 +261,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start, int ret; void *fdt; unsigned long initrd_pbase = 0UL; - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; char *modified_cmdline = NULL; kbuf.image = image; diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile index 81d69d45c06c..7dd15be69c90 100644 --- a/arch/riscv/kernel/pi/Makefile +++ b/arch/riscv/kernel/pi/Makefile @@ -2,7 +2,7 @@ # This file was copied from arm64/kernel/pi/Makefile. KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ - -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \ + -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_KSTACK_ERASE) \ $(call cc-option,-mbranch-protection=none) \ -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ -include $(srctree)/include/linux/hidden.h \ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index ea67e9fb7a58..8e86305831ea 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -186,7 +186,7 @@ static int tagged_addr_ctrl_set(struct task_struct *target, static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), @@ -195,7 +195,7 @@ static const struct user_regset riscv_user_regset[] = { }, #ifdef CONFIG_FPU [REGSET_F] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), @@ -205,7 +205,7 @@ static const struct user_regset riscv_user_regset[] = { #endif #ifdef CONFIG_RISCV_ISA_V [REGSET_V] = { - .core_note_type = NT_RISCV_VECTOR, + USER_REGSET_NOTE_TYPE(RISCV_VECTOR), .align = 16, .n = ((32 * RISCV_MAX_VLENB) + sizeof(struct __riscv_v_regset_state)) / sizeof(__u32), @@ -216,7 +216,7 @@ static const struct user_regset riscv_user_regset[] = { #endif #ifdef CONFIG_RISCV_ISA_SUPM [REGSET_TAGGED_ADDR_CTRL] = { - .core_note_type = NT_RISCV_TAGGED_ADDR_CTRL, + USER_REGSET_NOTE_TYPE(RISCV_TAGGED_ADDR_CTRL), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -380,7 +380,7 @@ static int compat_riscv_gpr_set(struct task_struct *target, static const struct user_regset compat_riscv_user_regset[] = { [REGSET_X] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(compat_elf_greg_t), .align = sizeof(compat_elf_greg_t), @@ -389,7 +389,7 @@ static const struct user_regset compat_riscv_user_regset[] = { }, #ifdef CONFIG_FPU [REGSET_F] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index f7c9a1caa83e..f90cce7a3ace 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -50,6 +52,7 @@ atomic_t hart_lottery __section(".sdata") #endif ; unsigned long boot_cpu_hartid; +EXPORT_SYMBOL_GPL(boot_cpu_hartid); /* * Place kernel memory regions on the resource tree so that @@ -361,6 +364,9 @@ void __init setup_arch(char **cmdline_p) riscv_user_isa_enable(); riscv_spinlock_init(); + + if (!IS_ENABLED(CONFIG_RISCV_ISA_ZBB) || !riscv_isa_extension_available(NULL, ZBB)) + static_branch_disable(&efficient_ffs_key); } bool arch_cpu_is_hotpluggable(int cpu) diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 9c83848797a7..80230de167de 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -151,7 +152,9 @@ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ { \ if (user_mode(regs)) { \ irqentry_enter_from_user_mode(regs); \ + local_irq_enable(); \ do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \ + local_irq_disable(); \ irqentry_exit_to_user_mode(regs); \ } else { \ irqentry_state_t state = irqentry_nmi_enter(regs); \ @@ -173,17 +176,14 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); - local_irq_enable(); handled = riscv_v_first_use_handler(regs); - - local_irq_disable(); - if (!handled) do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, "Oops - illegal instruction"); + local_irq_disable(); irqentry_exit_to_user_mode(regs); } else { irqentry_state_t state = irqentry_nmi_enter(regs); @@ -308,9 +308,11 @@ asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs) { if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); + local_irq_enable(); handle_break(regs); + local_irq_disable(); irqentry_exit_to_user_mode(regs); } else { irqentry_state_t state = irqentry_nmi_enter(regs); diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index dd8e4af6583f..f760e4fcc052 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -454,14 +454,14 @@ static int handle_scalar_misaligned_load(struct pt_regs *regs) val.data_u64 = 0; if (user_mode(regs)) { - if (copy_from_user_nofault(&val, (u8 __user *)addr, len)) + if (copy_from_user(&val, (u8 __user *)addr, len)) return -1; } else { memcpy(&val, (u8 *)addr, len); } if (!fp) - SET_RD(insn, regs, val.data_ulong << shift >> shift); + SET_RD(insn, regs, (long)(val.data_ulong << shift) >> shift); else if (len == 8) set_f64_rd(insn, regs, val.data_u64); else @@ -555,7 +555,7 @@ static int handle_scalar_misaligned_store(struct pt_regs *regs) return -EOPNOTSUPP; if (user_mode(regs)) { - if (copy_to_user_nofault((u8 __user *)addr, &val, len)) + if (copy_to_user((u8 __user *)addr, &val, len)) return -1; } else { memcpy((u8 *)addr, &val, len); diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S index 7c15b0f4ee3b..c29ef12a63bb 100644 --- a/arch/riscv/kernel/vdso/vdso.lds.S +++ b/arch/riscv/kernel/vdso/vdso.lds.S @@ -30,7 +30,7 @@ SECTIONS *(.data .data.* .gnu.linkonce.d.*) *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) - } + } :text .note : { *(.note.*) } :text :note diff --git a/arch/riscv/kernel/vendor_extensions/sifive.c b/arch/riscv/kernel/vendor_extensions/sifive.c index 1411337dc1e6..8fcf67e8c07f 100644 --- a/arch/riscv/kernel/vendor_extensions/sifive.c +++ b/arch/riscv/kernel/vendor_extensions/sifive.c @@ -8,7 +8,7 @@ #include /* All SiFive vendor extensions supported in Linux */ -const struct riscv_isa_ext_data riscv_isa_vendor_ext_sifive[] = { +static const struct riscv_isa_ext_data riscv_isa_vendor_ext_sifive[] = { __RISCV_ISA_EXT_DATA(xsfvfnrclipxfqf, RISCV_ISA_VENDOR_EXT_XSFVFNRCLIPXFQF), __RISCV_ISA_EXT_DATA(xsfvfwmaccqqq, RISCV_ISA_VENDOR_EXT_XSFVFWMACCQQQ), __RISCV_ISA_EXT_DATA(xsfvqmaccdod, RISCV_ISA_VENDOR_EXT_XSFVQMACCDOD), diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig index 704c2899197e..5a62091b0809 100644 --- a/arch/riscv/kvm/Kconfig +++ b/arch/riscv/kvm/Kconfig @@ -25,6 +25,7 @@ config KVM select HAVE_KVM_MSI select HAVE_KVM_VCPU_ASYNC_IOCTL select HAVE_KVM_READONLY_MEM + select HAVE_KVM_DIRTY_RING_ACQ_REL select KVM_COMMON select KVM_GENERIC_DIRTYLOG_READ_PROTECT select KVM_GENERIC_HARDWARE_ENABLING diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index 4e0bba91d284..4b199dc3e58b 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -14,6 +14,7 @@ kvm-y += aia.o kvm-y += aia_aplic.o kvm-y += aia_device.o kvm-y += aia_imsic.o +kvm-y += gstage.o kvm-y += main.o kvm-y += mmu.o kvm-y += nacl.o diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index 19afd1f23537..dad318185660 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -30,28 +30,6 @@ unsigned int kvm_riscv_aia_nr_hgei; unsigned int kvm_riscv_aia_max_ids; DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available); -static int aia_find_hgei(struct kvm_vcpu *owner) -{ - int i, hgei; - unsigned long flags; - struct aia_hgei_control *hgctrl = get_cpu_ptr(&aia_hgei); - - raw_spin_lock_irqsave(&hgctrl->lock, flags); - - hgei = -1; - for (i = 1; i <= kvm_riscv_aia_nr_hgei; i++) { - if (hgctrl->owners[i] == owner) { - hgei = i; - break; - } - } - - raw_spin_unlock_irqrestore(&hgctrl->lock, flags); - - put_cpu_ptr(&aia_hgei); - return hgei; -} - static inline unsigned long aia_hvictl_value(bool ext_irq_pending) { unsigned long hvictl; @@ -95,7 +73,6 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { - int hgei; unsigned long seip; if (!kvm_riscv_aia_available()) @@ -114,11 +91,7 @@ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) if (!kvm_riscv_aia_initialized(vcpu->kvm) || !seip) return false; - hgei = aia_find_hgei(vcpu); - if (hgei > 0) - return !!(ncsr_read(CSR_HGEIP) & BIT(hgei)); - - return false; + return kvm_riscv_vcpu_aia_imsic_has_interrupt(vcpu); } void kvm_riscv_vcpu_aia_update_hvip(struct kvm_vcpu *vcpu) @@ -164,6 +137,9 @@ void kvm_riscv_vcpu_aia_load(struct kvm_vcpu *vcpu, int cpu) csr_write(CSR_HVIPRIO2H, csr->hviprio2h); #endif } + + if (kvm_riscv_aia_initialized(vcpu->kvm)) + kvm_riscv_vcpu_aia_imsic_load(vcpu, cpu); } void kvm_riscv_vcpu_aia_put(struct kvm_vcpu *vcpu) @@ -174,6 +150,9 @@ void kvm_riscv_vcpu_aia_put(struct kvm_vcpu *vcpu) if (!kvm_riscv_aia_available()) return; + if (kvm_riscv_aia_initialized(vcpu->kvm)) + kvm_riscv_vcpu_aia_imsic_put(vcpu); + if (kvm_riscv_nacl_available()) { nsh = nacl_shmem(); csr->vsiselect = nacl_csr_read(nsh, CSR_VSISELECT); @@ -472,22 +451,6 @@ void kvm_riscv_aia_free_hgei(int cpu, int hgei) raw_spin_unlock_irqrestore(&hgctrl->lock, flags); } -void kvm_riscv_aia_wakeon_hgei(struct kvm_vcpu *owner, bool enable) -{ - int hgei; - - if (!kvm_riscv_aia_available()) - return; - - hgei = aia_find_hgei(owner); - if (hgei > 0) { - if (enable) - csr_set(CSR_HGEIE, BIT(hgei)); - else - csr_clear(CSR_HGEIE, BIT(hgei)); - } -} - static irqreturn_t hgei_interrupt(int irq, void *dev_id) { int i; diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c index 806c41931cde..b195a93add1c 100644 --- a/arch/riscv/kvm/aia_device.c +++ b/arch/riscv/kvm/aia_device.c @@ -509,12 +509,12 @@ void kvm_riscv_vcpu_aia_reset(struct kvm_vcpu *vcpu) kvm_riscv_vcpu_aia_imsic_reset(vcpu); } -int kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu) +void kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context; if (!kvm_riscv_aia_available()) - return 0; + return; /* * We don't do any memory allocations over here because these @@ -526,8 +526,6 @@ int kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu) /* Initialize default values in AIA vcpu context */ vaia->imsic_addr = KVM_RISCV_AIA_UNDEF_ADDR; vaia->hart_index = vcpu->vcpu_idx; - - return 0; } void kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu) diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c index 29ef9c2133a9..fda0346f0ea1 100644 --- a/arch/riscv/kvm/aia_imsic.c +++ b/arch/riscv/kvm/aia_imsic.c @@ -16,6 +16,7 @@ #include #include #include +#include #define IMSIC_MAX_EIX (IMSIC_MAX_ID / BITS_PER_TYPE(u64)) @@ -676,6 +677,48 @@ static void imsic_swfile_update(struct kvm_vcpu *vcpu, imsic_swfile_extirq_update(vcpu); } +bool kvm_riscv_vcpu_aia_imsic_has_interrupt(struct kvm_vcpu *vcpu) +{ + struct imsic *imsic = vcpu->arch.aia_context.imsic_state; + unsigned long flags; + bool ret = false; + + /* + * The IMSIC SW-file directly injects interrupt via hvip so + * only check for interrupt when IMSIC VS-file is being used. + */ + + read_lock_irqsave(&imsic->vsfile_lock, flags); + if (imsic->vsfile_cpu > -1) + ret = !!(csr_read(CSR_HGEIP) & BIT(imsic->vsfile_hgei)); + read_unlock_irqrestore(&imsic->vsfile_lock, flags); + + return ret; +} + +void kvm_riscv_vcpu_aia_imsic_load(struct kvm_vcpu *vcpu, int cpu) +{ + /* + * No need to explicitly clear HGEIE CSR bits because the + * hgei interrupt handler (aka hgei_interrupt()) will always + * clear it for us. + */ +} + +void kvm_riscv_vcpu_aia_imsic_put(struct kvm_vcpu *vcpu) +{ + struct imsic *imsic = vcpu->arch.aia_context.imsic_state; + unsigned long flags; + + if (!kvm_vcpu_is_blocking(vcpu)) + return; + + read_lock_irqsave(&imsic->vsfile_lock, flags); + if (imsic->vsfile_cpu > -1) + csr_set(CSR_HGEIE, BIT(imsic->vsfile_hgei)); + read_unlock_irqrestore(&imsic->vsfile_lock, flags); +} + void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu) { unsigned long flags; @@ -703,9 +746,8 @@ void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu) */ /* Purge the G-stage mapping */ - kvm_riscv_gstage_iounmap(vcpu->kvm, - vcpu->arch.aia_context.imsic_addr, - IMSIC_MMIO_PAGE_SZ); + kvm_riscv_mmu_iounmap(vcpu->kvm, vcpu->arch.aia_context.imsic_addr, + IMSIC_MMIO_PAGE_SZ); /* TODO: Purge the IOMMU mapping ??? */ @@ -781,13 +823,16 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu) * producers to the new IMSIC VS-file. */ + /* Ensure HGEIE CSR bit is zero before using the new IMSIC VS-file */ + csr_clear(CSR_HGEIE, BIT(new_vsfile_hgei)); + /* Zero-out new IMSIC VS-file */ imsic_vsfile_local_clear(new_vsfile_hgei, imsic->nr_hw_eix); /* Update G-stage mapping for the new IMSIC VS-file */ - ret = kvm_riscv_gstage_ioremap(kvm, vcpu->arch.aia_context.imsic_addr, - new_vsfile_pa, IMSIC_MMIO_PAGE_SZ, - true, true); + ret = kvm_riscv_mmu_ioremap(kvm, vcpu->arch.aia_context.imsic_addr, + new_vsfile_pa, IMSIC_MMIO_PAGE_SZ, + true, true); if (ret) goto fail_free_vsfile_hgei; diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c new file mode 100644 index 000000000000..24c270d6d0e2 --- /dev/null +++ b/arch/riscv/kvm/gstage.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#include +#include +#include +#include +#include +#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; +#else +unsigned long kvm_riscv_gstage_mode __ro_after_init = HGATP_MODE_SV32X4; +unsigned long kvm_riscv_gstage_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) +{ + unsigned long mask; + unsigned long shift = HGATP_PAGE_SHIFT + (kvm_riscv_gstage_index_bits * level); + + if (level == (kvm_riscv_gstage_pgd_levels - 1)) + mask = (PTRS_PER_PTE * (1UL << kvm_riscv_gstage_pgd_xbits)) - 1; + else + mask = PTRS_PER_PTE - 1; + + return (addr >> shift) & mask; +} + +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) +{ + u32 i; + unsigned long psz = 1UL << 12; + + for (i = 0; i < kvm_riscv_gstage_pgd_levels; i++) { + if (page_size == (psz << (i * kvm_riscv_gstage_index_bits))) { + *out_level = i; + return 0; + } + } + + return -EINVAL; +} + +static int gstage_level_to_page_order(u32 level, unsigned long *out_pgorder) +{ + if (kvm_riscv_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) +{ + int rc; + unsigned long page_order = PAGE_SHIFT; + + rc = gstage_level_to_page_order(level, &page_order); + if (rc) + return rc; + + *out_pgsize = BIT(page_order); + return 0; +} + +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; + + *ptep_level = current_level; + ptep = (pte_t *)gstage->pgd; + ptep = &ptep[gstage_pte_index(addr, current_level)]; + while (ptep && pte_val(ptep_get(ptep))) { + if (gstage_pte_leaf(ptep)) { + *ptep_level = current_level; + *ptepp = ptep; + return true; + } + + if (current_level) { + current_level--; + *ptep_level = current_level; + ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); + ptep = &ptep[gstage_pte_index(addr, current_level)]; + } else { + ptep = NULL; + } + } + + return false; +} + +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)) + return; + addr &= ~(BIT(order) - 1); + + if (gstage->flags & KVM_GSTAGE_FLAGS_LOCAL) + kvm_riscv_local_hfence_gvma_vmid_gpa(gstage->vmid, addr, BIT(order), order); + else + kvm_riscv_hfence_gvma_vmid_gpa(gstage->kvm, -1UL, 0, addr, BIT(order), order, + gstage->vmid); +} + +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; + pte_t *next_ptep = (pte_t *)gstage->pgd; + pte_t *ptep = &next_ptep[gstage_pte_index(map->addr, current_level)]; + + if (current_level < map->level) + return -EINVAL; + + while (current_level != map->level) { + if (gstage_pte_leaf(ptep)) + return -EEXIST; + + if (!pte_val(ptep_get(ptep))) { + if (!pcache) + return -ENOMEM; + next_ptep = kvm_mmu_memory_cache_alloc(pcache); + if (!next_ptep) + return -ENOMEM; + set_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)), + __pgprot(_PAGE_TABLE))); + } else { + if (gstage_pte_leaf(ptep)) + return -EEXIST; + next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); + } + + current_level--; + ptep = &next_ptep[gstage_pte_index(map->addr, current_level)]; + } + + if (pte_val(*ptep) != pte_val(map->pte)) { + set_pte(ptep, map->pte); + if (gstage_pte_leaf(ptep)) + gstage_tlb_flush(gstage, current_level, map->addr); + } + + return 0; +} + +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) +{ + pgprot_t prot; + int ret; + + out_map->addr = gpa; + out_map->level = 0; + + ret = gstage_page_size_to_level(page_size, &out_map->level); + if (ret) + return ret; + + /* + * A RISC-V implementation can choose to either: + * 1) Update 'A' and 'D' PTE bits in hardware + * 2) Generate page fault when 'A' and/or 'D' bits are not set + * PTE so that software can update these bits. + * + * We support both options mentioned above. To achieve this, we + * always set 'A' and 'D' PTE bits at time of creating G-stage + * mapping. To support KVM dirty page logging with both options + * mentioned above, we will write-protect G-stage PTEs to track + * dirty pages. + */ + + if (page_exec) { + if (page_rdonly) + prot = PAGE_READ_EXEC; + else + prot = PAGE_WRITE_EXEC; + } else { + if (page_rdonly) + prot = PAGE_READ; + else + prot = PAGE_WRITE; + } + 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); +} + +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) +{ + int i, ret; + pte_t old_pte, *next_ptep; + u32 next_ptep_level; + unsigned long next_page_size, page_size; + + ret = gstage_level_to_page_size(ptep_level, &page_size); + if (ret) + return; + + WARN_ON(addr & (page_size - 1)); + + if (!pte_val(ptep_get(ptep))) + return; + + 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); + if (ret) + return; + + if (op == GSTAGE_OP_CLEAR) + set_pte(ptep, __pte(0)); + for (i = 0; i < PTRS_PER_PTE; i++) + kvm_riscv_gstage_op_pte(gstage, addr + i * next_page_size, + &next_ptep[i], next_ptep_level, op); + if (op == GSTAGE_OP_CLEAR) + put_page(virt_to_page(next_ptep)); + } else { + old_pte = *ptep; + if (op == GSTAGE_OP_CLEAR) + set_pte(ptep, __pte(0)); + else if (op == GSTAGE_OP_WP) + set_pte(ptep, __pte(pte_val(ptep_get(ptep)) & ~_PAGE_WRITE)); + if (pte_val(*ptep) != pte_val(old_pte)) + gstage_tlb_flush(gstage, ptep_level, addr); + } +} + +void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, + gpa_t start, gpa_t size, bool may_block) +{ + int ret; + pte_t *ptep; + u32 ptep_level; + bool found_leaf; + unsigned long page_size; + gpa_t addr = start, end = start + size; + + 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); + 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_CLEAR); + +next: + addr += page_size; + + /* + * If the range is too large, release the kvm->mmu_lock + * to prevent starvation and lockup detector warnings. + */ + if (!(gstage->flags & KVM_GSTAGE_FLAGS_LOCAL) && may_block && addr < end) + cond_resched_lock(&gstage->kvm->mmu_lock); + } +} + +void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end) +{ + int ret; + pte_t *ptep; + u32 ptep_level; + bool found_leaf; + gpa_t addr = start; + unsigned long page_size; + + 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); + 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); + +next: + addr += page_size; + } +} + +void __init kvm_riscv_gstage_mode_detect(void) +{ +#ifdef CONFIG_64BIT + /* 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; + goto skip_sv48x4_test; + } + + /* 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; + } +skip_sv48x4_test: + + csr_write(CSR_HGATP, 0); + kvm_riscv_local_hfence_gvma_all(); +#endif +} diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c index 4b24705dc63a..67c876de74ef 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -134,7 +135,7 @@ static int __init riscv_kvm_init(void) (rc) ? slist : "no features"); } - switch (kvm_riscv_gstage_mode()) { + switch (kvm_riscv_gstage_mode) { case HGATP_MODE_SV32X4: str = "Sv32x4"; break; diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 1087ea74567b..525fb5a330c0 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -6,371 +6,73 @@ * Anup Patel */ -#include #include -#include #include #include #include #include #include #include +#include #include -#include -#include -#ifdef CONFIG_64BIT -static unsigned long gstage_mode __ro_after_init = (HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT); -static unsigned long gstage_pgd_levels __ro_after_init = 3; -#define gstage_index_bits 9 -#else -static unsigned long gstage_mode __ro_after_init = (HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT); -static unsigned long gstage_pgd_levels __ro_after_init = 2; -#define gstage_index_bits 10 -#endif - -#define gstage_pgd_xbits 2 -#define gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + gstage_pgd_xbits)) -#define gstage_gpa_bits (HGATP_PAGE_SHIFT + \ - (gstage_pgd_levels * gstage_index_bits) + \ - gstage_pgd_xbits) -#define gstage_gpa_size ((gpa_t)(1ULL << gstage_gpa_bits)) - -#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) -{ - unsigned long mask; - unsigned long shift = HGATP_PAGE_SHIFT + (gstage_index_bits * level); - - if (level == (gstage_pgd_levels - 1)) - mask = (PTRS_PER_PTE * (1UL << gstage_pgd_xbits)) - 1; - else - mask = PTRS_PER_PTE - 1; - - return (addr >> shift) & mask; -} - -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) -{ - u32 i; - unsigned long psz = 1UL << 12; - - for (i = 0; i < gstage_pgd_levels; i++) { - if (page_size == (psz << (i * gstage_index_bits))) { - *out_level = i; - return 0; - } - } - - return -EINVAL; -} - -static int gstage_level_to_page_order(u32 level, unsigned long *out_pgorder) -{ - if (gstage_pgd_levels < level) - return -EINVAL; - - *out_pgorder = 12 + (level * gstage_index_bits); - return 0; -} - -static int gstage_level_to_page_size(u32 level, unsigned long *out_pgsize) -{ - int rc; - unsigned long page_order = PAGE_SHIFT; - - rc = gstage_level_to_page_order(level, &page_order); - if (rc) - return rc; - - *out_pgsize = BIT(page_order); - return 0; -} - -static bool gstage_get_leaf_entry(struct kvm *kvm, gpa_t addr, - pte_t **ptepp, u32 *ptep_level) -{ - pte_t *ptep; - u32 current_level = gstage_pgd_levels - 1; - - *ptep_level = current_level; - ptep = (pte_t *)kvm->arch.pgd; - ptep = &ptep[gstage_pte_index(addr, current_level)]; - while (ptep && pte_val(ptep_get(ptep))) { - if (gstage_pte_leaf(ptep)) { - *ptep_level = current_level; - *ptepp = ptep; - return true; - } - - if (current_level) { - current_level--; - *ptep_level = current_level; - ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); - ptep = &ptep[gstage_pte_index(addr, current_level)]; - } else { - ptep = NULL; - } - } - - return false; -} - -static void gstage_remote_tlb_flush(struct kvm *kvm, u32 level, gpa_t addr) -{ - unsigned long order = PAGE_SHIFT; - - if (gstage_level_to_page_order(level, &order)) - return; - addr &= ~(BIT(order) - 1); - - kvm_riscv_hfence_gvma_vmid_gpa(kvm, -1UL, 0, addr, BIT(order), order); -} - -static int gstage_set_pte(struct kvm *kvm, u32 level, - struct kvm_mmu_memory_cache *pcache, - gpa_t addr, const pte_t *new_pte) -{ - u32 current_level = gstage_pgd_levels - 1; - pte_t *next_ptep = (pte_t *)kvm->arch.pgd; - pte_t *ptep = &next_ptep[gstage_pte_index(addr, current_level)]; - - if (current_level < level) - return -EINVAL; - - while (current_level != level) { - if (gstage_pte_leaf(ptep)) - return -EEXIST; - - if (!pte_val(ptep_get(ptep))) { - if (!pcache) - return -ENOMEM; - next_ptep = kvm_mmu_memory_cache_alloc(pcache); - if (!next_ptep) - return -ENOMEM; - set_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)), - __pgprot(_PAGE_TABLE))); - } else { - if (gstage_pte_leaf(ptep)) - return -EEXIST; - next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); - } - - current_level--; - ptep = &next_ptep[gstage_pte_index(addr, current_level)]; - } - - set_pte(ptep, *new_pte); - if (gstage_pte_leaf(ptep)) - gstage_remote_tlb_flush(kvm, current_level, addr); - - return 0; -} - -static int gstage_map_page(struct kvm *kvm, - struct kvm_mmu_memory_cache *pcache, - gpa_t gpa, phys_addr_t hpa, - unsigned long page_size, - bool page_rdonly, bool page_exec) -{ - int ret; - u32 level = 0; - pte_t new_pte; - pgprot_t prot; - - ret = gstage_page_size_to_level(page_size, &level); - if (ret) - return ret; - - /* - * A RISC-V implementation can choose to either: - * 1) Update 'A' and 'D' PTE bits in hardware - * 2) Generate page fault when 'A' and/or 'D' bits are not set - * PTE so that software can update these bits. - * - * We support both options mentioned above. To achieve this, we - * always set 'A' and 'D' PTE bits at time of creating G-stage - * mapping. To support KVM dirty page logging with both options - * mentioned above, we will write-protect G-stage PTEs to track - * dirty pages. - */ - - if (page_exec) { - if (page_rdonly) - prot = PAGE_READ_EXEC; - else - prot = PAGE_WRITE_EXEC; - } else { - if (page_rdonly) - prot = PAGE_READ; - else - prot = PAGE_WRITE; - } - new_pte = pfn_pte(PFN_DOWN(hpa), prot); - new_pte = pte_mkdirty(new_pte); - - return gstage_set_pte(kvm, level, pcache, gpa, &new_pte); -} - -enum gstage_op { - GSTAGE_OP_NOP = 0, /* Nothing */ - GSTAGE_OP_CLEAR, /* Clear/Unmap */ - GSTAGE_OP_WP, /* Write-protect */ -}; - -static void gstage_op_pte(struct kvm *kvm, gpa_t addr, - pte_t *ptep, u32 ptep_level, enum gstage_op op) -{ - int i, ret; - pte_t *next_ptep; - u32 next_ptep_level; - unsigned long next_page_size, page_size; - - ret = gstage_level_to_page_size(ptep_level, &page_size); - if (ret) - return; - - BUG_ON(addr & (page_size - 1)); - - if (!pte_val(ptep_get(ptep))) - return; - - 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); - if (ret) - return; - - if (op == GSTAGE_OP_CLEAR) - set_pte(ptep, __pte(0)); - for (i = 0; i < PTRS_PER_PTE; i++) - gstage_op_pte(kvm, addr + i * next_page_size, - &next_ptep[i], next_ptep_level, op); - if (op == GSTAGE_OP_CLEAR) - put_page(virt_to_page(next_ptep)); - } else { - if (op == GSTAGE_OP_CLEAR) - set_pte(ptep, __pte(0)); - else if (op == GSTAGE_OP_WP) - set_pte(ptep, __pte(pte_val(ptep_get(ptep)) & ~_PAGE_WRITE)); - gstage_remote_tlb_flush(kvm, ptep_level, addr); - } -} - -static void gstage_unmap_range(struct kvm *kvm, gpa_t start, - gpa_t size, bool may_block) -{ - int ret; - pte_t *ptep; - u32 ptep_level; - bool found_leaf; - unsigned long page_size; - gpa_t addr = start, end = start + size; - - while (addr < end) { - found_leaf = gstage_get_leaf_entry(kvm, addr, - &ptep, &ptep_level); - ret = gstage_level_to_page_size(ptep_level, &page_size); - if (ret) - break; - - if (!found_leaf) - goto next; - - if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) - gstage_op_pte(kvm, addr, ptep, - ptep_level, GSTAGE_OP_CLEAR); - -next: - addr += page_size; - - /* - * If the range is too large, release the kvm->mmu_lock - * to prevent starvation and lockup detector warnings. - */ - if (may_block && addr < end) - cond_resched_lock(&kvm->mmu_lock); - } -} - -static void gstage_wp_range(struct kvm *kvm, gpa_t start, gpa_t end) -{ - int ret; - pte_t *ptep; - u32 ptep_level; - bool found_leaf; - gpa_t addr = start; - unsigned long page_size; - - while (addr < end) { - found_leaf = gstage_get_leaf_entry(kvm, addr, - &ptep, &ptep_level); - ret = gstage_level_to_page_size(ptep_level, &page_size); - if (ret) - break; - - if (!found_leaf) - goto next; - - if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) - gstage_op_pte(kvm, addr, ptep, - ptep_level, GSTAGE_OP_WP); - -next: - addr += page_size; - } -} - -static void gstage_wp_memory_region(struct kvm *kvm, int slot) +static void mmu_wp_memory_region(struct kvm *kvm, int slot) { struct kvm_memslots *slots = kvm_memslots(kvm); struct kvm_memory_slot *memslot = id_to_memslot(slots, slot); phys_addr_t start = memslot->base_gfn << PAGE_SHIFT; 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; spin_lock(&kvm->mmu_lock); - gstage_wp_range(kvm, start, end); + kvm_riscv_gstage_wp_range(&gstage, start, end); spin_unlock(&kvm->mmu_lock); - kvm_flush_remote_tlbs(kvm); + kvm_flush_remote_tlbs_memslot(kvm, memslot); } -int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, - phys_addr_t hpa, unsigned long size, - bool writable, bool in_atomic) +int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, + unsigned long size, bool writable, bool in_atomic) { - pte_t pte; int ret = 0; + pgprot_t prot; unsigned long pfn; phys_addr_t addr, end; struct kvm_mmu_memory_cache pcache = { .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0, .gfp_zero = __GFP_ZERO, }; + 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; end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK; pfn = __phys_to_pfn(hpa); + prot = pgprot_noncached(PAGE_WRITE); for (addr = gpa; addr < end; addr += PAGE_SIZE) { - pte = pfn_pte(pfn, PAGE_KERNEL_IO); + map.addr = addr; + map.pte = pfn_pte(pfn, prot); + map.pte = pte_mkdirty(map.pte); + map.level = 0; if (!writable) - pte = pte_wrprotect(pte); + map.pte = pte_wrprotect(map.pte); - ret = kvm_mmu_topup_memory_cache(&pcache, gstage_pgd_levels); + ret = kvm_mmu_topup_memory_cache(&pcache, kvm_riscv_gstage_pgd_levels); if (ret) goto out; spin_lock(&kvm->mmu_lock); - ret = gstage_set_pte(kvm, 0, &pcache, addr, &pte); + ret = kvm_riscv_gstage_set_pte(&gstage, &pcache, &map); spin_unlock(&kvm->mmu_lock); if (ret) goto out; @@ -383,10 +85,17 @@ int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, return ret; } -void kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) +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; + spin_lock(&kvm->mmu_lock); - gstage_unmap_range(kvm, gpa, size, false); + kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); spin_unlock(&kvm->mmu_lock); } @@ -398,8 +107,14 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, phys_addr_t base_gfn = slot->base_gfn + gfn_offset; phys_addr_t start = (base_gfn + __ffs(mask)) << PAGE_SHIFT; phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT; + struct kvm_gstage gstage; - gstage_wp_range(kvm, start, end); + gstage.kvm = kvm; + gstage.flags = 0; + gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); + gstage.pgd = kvm->arch.pgd; + + kvm_riscv_gstage_wp_range(&gstage, start, end); } void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) @@ -416,7 +131,7 @@ void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) void kvm_arch_flush_shadow_all(struct kvm *kvm) { - kvm_riscv_gstage_free_pgd(kvm); + kvm_riscv_mmu_free_pgd(kvm); } void kvm_arch_flush_shadow_memslot(struct kvm *kvm, @@ -424,9 +139,15 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, { gpa_t gpa = slot->base_gfn << PAGE_SHIFT; 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; spin_lock(&kvm->mmu_lock); - gstage_unmap_range(kvm, gpa, size, false); + kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); spin_unlock(&kvm->mmu_lock); } @@ -441,7 +162,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, * the memory slot is write protected. */ if (change != KVM_MR_DELETE && new->flags & KVM_MEM_LOG_DIRTY_PAGES) - gstage_wp_memory_region(kvm, new->id); + mmu_wp_memory_region(kvm, new->id); } int kvm_arch_prepare_memory_region(struct kvm *kvm, @@ -463,7 +184,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, * space addressable by the KVM guest GPA space. */ if ((new->base_gfn + new->npages) >= - (gstage_gpa_size >> PAGE_SHIFT)) + (kvm_riscv_gstage_gpa_size >> PAGE_SHIFT)) return -EFAULT; hva = new->userspace_addr; @@ -487,10 +208,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, * +--------------------------------------------+ */ do { - struct vm_area_struct *vma = find_vma(current->mm, hva); + struct vm_area_struct *vma; hva_t vm_start, vm_end; - if (!vma || vma->vm_start >= reg_end) + vma = find_vma_intersection(current->mm, hva, reg_end); + if (!vma) break; /* @@ -519,9 +241,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, goto out; } - ret = kvm_riscv_gstage_ioremap(kvm, gpa, pa, - vm_end - vm_start, - writable, false); + ret = kvm_riscv_mmu_ioremap(kvm, gpa, pa, vm_end - vm_start, + writable, false); if (ret) break; } @@ -532,7 +253,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, goto out; if (ret) - kvm_riscv_gstage_iounmap(kvm, base_gpa, size); + kvm_riscv_mmu_iounmap(kvm, base_gpa, size); out: mmap_read_unlock(current->mm); @@ -541,12 +262,18 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) { + struct kvm_gstage gstage; + if (!kvm->arch.pgd) return false; - gstage_unmap_range(kvm, range->start << PAGE_SHIFT, - (range->end - range->start) << PAGE_SHIFT, - range->may_block); + 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, range->start << PAGE_SHIFT, + (range->end - range->start) << PAGE_SHIFT, + range->may_block); return false; } @@ -555,14 +282,19 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) pte_t *ptep; u32 ptep_level = 0; u64 size = (range->end - range->start) << PAGE_SHIFT; + struct kvm_gstage gstage; if (!kvm->arch.pgd) return false; WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); - if (!gstage_get_leaf_entry(kvm, range->start << PAGE_SHIFT, - &ptep, &ptep_level)) + gstage.kvm = kvm; + gstage.flags = 0; + gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); + gstage.pgd = kvm->arch.pgd; + if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT, + &ptep, &ptep_level)) return false; return ptep_test_and_clear_young(NULL, 0, ptep); @@ -573,22 +305,27 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) pte_t *ptep; u32 ptep_level = 0; u64 size = (range->end - range->start) << PAGE_SHIFT; + struct kvm_gstage gstage; if (!kvm->arch.pgd) return false; WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); - if (!gstage_get_leaf_entry(kvm, range->start << PAGE_SHIFT, - &ptep, &ptep_level)) + gstage.kvm = kvm; + gstage.flags = 0; + gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); + gstage.pgd = kvm->arch.pgd; + if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT, + &ptep, &ptep_level)) return false; return pte_young(ptep_get(ptep)); } -int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, - struct kvm_memory_slot *memslot, - gpa_t gpa, unsigned long hva, bool is_write) +int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, + gpa_t gpa, unsigned long hva, bool is_write, + struct kvm_gstage_mapping *out_map) { int ret; kvm_pfn_t hfn; @@ -601,10 +338,19 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, bool logging = (memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY)) ? true : false; unsigned long vma_pagesize, mmu_seq; + 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; + + /* 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, gstage_pgd_levels); + ret = kvm_mmu_topup_memory_cache(pcache, kvm_riscv_gstage_pgd_levels); if (ret) { kvm_err("Failed to topup G-stage cache\n"); return ret; @@ -648,7 +394,8 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, return -EFAULT; } - hfn = kvm_faultin_pfn(vcpu, gfn, is_write, &writable, &page); + hfn = __kvm_faultin_pfn(memslot, gfn, is_write ? FOLL_WRITE : 0, + &writable, &page); if (hfn == KVM_PFN_ERR_HWPOISON) { send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva, vma_pageshift, current); @@ -670,12 +417,12 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, goto out_unlock; if (writable) { - mark_page_dirty(kvm, gfn); - ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, - vma_pagesize, false, true); + mark_page_dirty_in_slot(kvm, memslot, gfn); + ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT, + vma_pagesize, false, true, out_map); } else { - ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, - vma_pagesize, true, true); + ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT, + vma_pagesize, true, true, out_map); } if (ret) @@ -687,7 +434,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, return ret; } -int kvm_riscv_gstage_alloc_pgd(struct kvm *kvm) +int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm) { struct page *pgd_page; @@ -697,7 +444,7 @@ int kvm_riscv_gstage_alloc_pgd(struct kvm *kvm) } pgd_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, - get_order(gstage_pgd_size)); + get_order(kvm_riscv_gstage_pgd_size)); if (!pgd_page) return -ENOMEM; kvm->arch.pgd = page_to_virt(pgd_page); @@ -706,13 +453,18 @@ int kvm_riscv_gstage_alloc_pgd(struct kvm *kvm) return 0; } -void kvm_riscv_gstage_free_pgd(struct kvm *kvm) +void kvm_riscv_mmu_free_pgd(struct kvm *kvm) { + struct kvm_gstage gstage; void *pgd = NULL; spin_lock(&kvm->mmu_lock); if (kvm->arch.pgd) { - gstage_unmap_range(kvm, 0UL, gstage_gpa_size, false); + 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); pgd = READ_ONCE(kvm->arch.pgd); kvm->arch.pgd = NULL; kvm->arch.pgd_phys = 0; @@ -720,12 +472,12 @@ void kvm_riscv_gstage_free_pgd(struct kvm *kvm) spin_unlock(&kvm->mmu_lock); if (pgd) - free_pages((unsigned long)pgd, get_order(gstage_pgd_size)); + free_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size)); } -void kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu) +void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu) { - unsigned long hgatp = gstage_mode; + unsigned long hgatp = kvm_riscv_gstage_mode << HGATP_MODE_SHIFT; struct kvm_arch *k = &vcpu->kvm->arch; hgatp |= (READ_ONCE(k->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID; @@ -736,37 +488,3 @@ void kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu) if (!kvm_riscv_gstage_vmid_bits()) kvm_riscv_local_hfence_gvma_all(); } - -void __init kvm_riscv_gstage_mode_detect(void) -{ -#ifdef CONFIG_64BIT - /* 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) { - gstage_mode = (HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT); - gstage_pgd_levels = 5; - goto skip_sv48x4_test; - } - - /* 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) { - gstage_mode = (HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT); - gstage_pgd_levels = 4; - } -skip_sv48x4_test: - - csr_write(CSR_HGATP, 0); - kvm_riscv_local_hfence_gvma_all(); -#endif -} - -unsigned long __init kvm_riscv_gstage_mode(void) -{ - return gstage_mode >> HGATP_MODE_SHIFT; -} - -int kvm_riscv_gstage_gpa_bits(void) -{ - return gstage_gpa_bits; -} diff --git a/arch/riscv/kvm/tlb.c b/arch/riscv/kvm/tlb.c index 2f91ea5f8493..3c5a70a2b927 100644 --- a/arch/riscv/kvm/tlb.c +++ b/arch/riscv/kvm/tlb.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #define has_svinval() riscv_has_extension_unlikely(RISCV_ISA_EXT_SVINVAL) @@ -156,36 +158,13 @@ void kvm_riscv_local_hfence_vvma_all(unsigned long vmid) csr_write(CSR_HGATP, hgatp); } -void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu) -{ - unsigned long vmid; - - if (!kvm_riscv_gstage_vmid_bits() || - vcpu->arch.last_exit_cpu == vcpu->cpu) - return; - - /* - * On RISC-V platforms with hardware VMID support, we share same - * VMID for all VCPUs of a particular Guest/VM. This means we might - * have stale G-stage TLB entries on the current Host CPU due to - * some other VCPU of the same Guest which ran previously on the - * current Host CPU. - * - * To cleanup stale TLB entries, we simply flush all G-stage TLB - * entries by VMID whenever underlying Host CPU changes for a VCPU. - */ - - vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); - kvm_riscv_local_hfence_gvma_vmid_all(vmid); -} - void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu) { kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_FENCE_I_RCVD); local_flush_icache_all(); } -void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu) +void kvm_riscv_tlb_flush_process(struct kvm_vcpu *vcpu) { struct kvm_vmid *v = &vcpu->kvm->arch.vmid; unsigned long vmid = READ_ONCE(v->vmid); @@ -258,51 +237,58 @@ static bool vcpu_hfence_enqueue(struct kvm_vcpu *vcpu, void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu) { - unsigned long vmid; struct kvm_riscv_hfence d = { 0 }; - struct kvm_vmid *v = &vcpu->kvm->arch.vmid; while (vcpu_hfence_dequeue(vcpu, &d)) { switch (d.type) { case KVM_RISCV_HFENCE_UNKNOWN: break; case KVM_RISCV_HFENCE_GVMA_VMID_GPA: - vmid = READ_ONCE(v->vmid); if (kvm_riscv_nacl_available()) - nacl_hfence_gvma_vmid(nacl_shmem(), vmid, + nacl_hfence_gvma_vmid(nacl_shmem(), d.vmid, d.addr, d.size, d.order); else - kvm_riscv_local_hfence_gvma_vmid_gpa(vmid, d.addr, + kvm_riscv_local_hfence_gvma_vmid_gpa(d.vmid, d.addr, d.size, d.order); break; + case KVM_RISCV_HFENCE_GVMA_VMID_ALL: + if (kvm_riscv_nacl_available()) + nacl_hfence_gvma_vmid_all(nacl_shmem(), d.vmid); + else + kvm_riscv_local_hfence_gvma_vmid_all(d.vmid); + break; case KVM_RISCV_HFENCE_VVMA_ASID_GVA: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD); - vmid = READ_ONCE(v->vmid); if (kvm_riscv_nacl_available()) - nacl_hfence_vvma_asid(nacl_shmem(), vmid, d.asid, + nacl_hfence_vvma_asid(nacl_shmem(), d.vmid, d.asid, d.addr, d.size, d.order); else - kvm_riscv_local_hfence_vvma_asid_gva(vmid, d.asid, d.addr, + kvm_riscv_local_hfence_vvma_asid_gva(d.vmid, d.asid, d.addr, d.size, d.order); break; case KVM_RISCV_HFENCE_VVMA_ASID_ALL: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD); - vmid = READ_ONCE(v->vmid); if (kvm_riscv_nacl_available()) - nacl_hfence_vvma_asid_all(nacl_shmem(), vmid, d.asid); + nacl_hfence_vvma_asid_all(nacl_shmem(), d.vmid, d.asid); else - kvm_riscv_local_hfence_vvma_asid_all(vmid, d.asid); + kvm_riscv_local_hfence_vvma_asid_all(d.vmid, d.asid); break; case KVM_RISCV_HFENCE_VVMA_GVA: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_RCVD); - vmid = READ_ONCE(v->vmid); if (kvm_riscv_nacl_available()) - nacl_hfence_vvma(nacl_shmem(), vmid, + nacl_hfence_vvma(nacl_shmem(), d.vmid, d.addr, d.size, d.order); else - kvm_riscv_local_hfence_vvma_gva(vmid, d.addr, + kvm_riscv_local_hfence_vvma_gva(d.vmid, d.addr, d.size, d.order); break; + case KVM_RISCV_HFENCE_VVMA_ALL: + kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_RCVD); + if (kvm_riscv_nacl_available()) + nacl_hfence_vvma_all(nacl_shmem(), d.vmid); + else + kvm_riscv_local_hfence_vvma_all(d.vmid); + break; default: break; } @@ -355,35 +341,43 @@ void kvm_riscv_fence_i(struct kvm *kvm, void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm, unsigned long hbase, unsigned long hmask, gpa_t gpa, gpa_t gpsz, - unsigned long order) + unsigned long order, unsigned long vmid) { struct kvm_riscv_hfence data; data.type = KVM_RISCV_HFENCE_GVMA_VMID_GPA; data.asid = 0; + data.vmid = vmid; data.addr = gpa; data.size = gpsz; data.order = order; make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE, - KVM_REQ_HFENCE_GVMA_VMID_ALL, &data); + KVM_REQ_TLB_FLUSH, &data); } void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm, - unsigned long hbase, unsigned long hmask) + unsigned long hbase, unsigned long hmask, + unsigned long vmid) { - make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE_GVMA_VMID_ALL, - KVM_REQ_HFENCE_GVMA_VMID_ALL, NULL); + struct kvm_riscv_hfence data = {0}; + + data.type = KVM_RISCV_HFENCE_GVMA_VMID_ALL; + data.vmid = vmid; + make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE, + KVM_REQ_TLB_FLUSH, &data); } void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm, unsigned long hbase, unsigned long hmask, unsigned long gva, unsigned long gvsz, - unsigned long order, unsigned long asid) + unsigned long order, unsigned long asid, + unsigned long vmid) { struct kvm_riscv_hfence data; data.type = KVM_RISCV_HFENCE_VVMA_ASID_GVA; data.asid = asid; + data.vmid = vmid; data.addr = gva; data.size = gvsz; data.order = order; @@ -393,13 +387,13 @@ void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm, void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm, unsigned long hbase, unsigned long hmask, - unsigned long asid) + unsigned long asid, unsigned long vmid) { - struct kvm_riscv_hfence data; + struct kvm_riscv_hfence data = {0}; data.type = KVM_RISCV_HFENCE_VVMA_ASID_ALL; data.asid = asid; - data.addr = data.size = data.order = 0; + data.vmid = vmid; make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE, KVM_REQ_HFENCE_VVMA_ALL, &data); } @@ -407,12 +401,13 @@ void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm, void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, unsigned long hbase, unsigned long hmask, unsigned long gva, unsigned long gvsz, - unsigned long order) + unsigned long order, unsigned long vmid) { struct kvm_riscv_hfence data; data.type = KVM_RISCV_HFENCE_VVMA_GVA; data.asid = 0; + data.vmid = vmid; data.addr = gva; data.size = gvsz; data.order = order; @@ -421,8 +416,21 @@ void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, } void kvm_riscv_hfence_vvma_all(struct kvm *kvm, - unsigned long hbase, unsigned long hmask) + unsigned long hbase, unsigned long hmask, + unsigned long vmid) { - make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE_VVMA_ALL, - KVM_REQ_HFENCE_VVMA_ALL, NULL); + struct kvm_riscv_hfence data = {0}; + + data.type = KVM_RISCV_HFENCE_VVMA_ALL; + data.vmid = vmid; + make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE, + KVM_REQ_HFENCE_VVMA_ALL, &data); +} + +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, gfn_t gfn, u64 nr_pages) +{ + kvm_riscv_hfence_gvma_vmid_gpa(kvm, -1UL, 0, + gfn << PAGE_SHIFT, nr_pages << PAGE_SHIFT, + PAGE_SHIFT, READ_ONCE(kvm->arch.vmid.vmid)); + return 0; } diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index e0a01af426ff..3ebcfffaa978 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -111,7 +112,7 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) vcpu->arch.hfence_tail = 0; memset(vcpu->arch.hfence_queue, 0, sizeof(vcpu->arch.hfence_queue)); - kvm_riscv_vcpu_sbi_sta_reset(vcpu); + kvm_riscv_vcpu_sbi_reset(vcpu); /* Reset the guest CSRs for hotplug usecase */ if (loaded) @@ -148,8 +149,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) spin_lock_init(&vcpu->arch.reset_state.lock); - if (kvm_riscv_vcpu_alloc_vector_context(vcpu)) - return -ENOMEM; + rc = kvm_riscv_vcpu_alloc_vector_context(vcpu); + if (rc) + return rc; /* Setup VCPU timer */ kvm_riscv_vcpu_timer_init(vcpu); @@ -158,9 +160,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) kvm_riscv_vcpu_pmu_init(vcpu); /* Setup VCPU AIA */ - rc = kvm_riscv_vcpu_aia_init(vcpu); - if (rc) - return rc; + kvm_riscv_vcpu_aia_init(vcpu); /* * Setup SBI extensions @@ -187,6 +187,8 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { + kvm_riscv_vcpu_sbi_deinit(vcpu); + /* Cleanup VCPU AIA context */ kvm_riscv_vcpu_aia_deinit(vcpu); @@ -207,16 +209,6 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) return kvm_riscv_vcpu_timer_pending(vcpu); } -void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) -{ - kvm_riscv_aia_wakeon_hgei(vcpu, true); -} - -void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) -{ - kvm_riscv_aia_wakeon_hgei(vcpu, false); -} - int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) { return (kvm_riscv_vcpu_has_interrupts(vcpu, -1UL) && @@ -630,7 +622,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) } } - kvm_riscv_gstage_update_hgatp(vcpu); + kvm_riscv_mmu_update_hgatp(vcpu); kvm_riscv_vcpu_timer_restore(vcpu); @@ -690,7 +682,14 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) } } -static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) +/** + * kvm_riscv_check_vcpu_requests - check and handle pending vCPU requests + * @vcpu: the VCPU pointer + * + * Return: 1 if we should enter the guest + * 0 if we should exit to userspace + */ +static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) { struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu); @@ -715,17 +714,13 @@ static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) kvm_riscv_reset_vcpu(vcpu, true); if (kvm_check_request(KVM_REQ_UPDATE_HGATP, vcpu)) - kvm_riscv_gstage_update_hgatp(vcpu); + kvm_riscv_mmu_update_hgatp(vcpu); if (kvm_check_request(KVM_REQ_FENCE_I, vcpu)) kvm_riscv_fence_i_process(vcpu); - /* - * The generic KVM_REQ_TLB_FLUSH is same as - * KVM_REQ_HFENCE_GVMA_VMID_ALL - */ - if (kvm_check_request(KVM_REQ_HFENCE_GVMA_VMID_ALL, vcpu)) - kvm_riscv_hfence_gvma_vmid_all_process(vcpu); + if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) + kvm_riscv_tlb_flush_process(vcpu); if (kvm_check_request(KVM_REQ_HFENCE_VVMA_ALL, vcpu)) kvm_riscv_hfence_vvma_all_process(vcpu); @@ -735,7 +730,12 @@ static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu)) kvm_riscv_vcpu_record_steal_time(vcpu); + + if (kvm_dirty_ring_check_request(vcpu)) + return 0; } + + return 1; } static void kvm_riscv_update_hvip(struct kvm_vcpu *vcpu) @@ -917,7 +917,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_riscv_gstage_vmid_update(vcpu); - kvm_riscv_check_vcpu_requests(vcpu); + ret = kvm_riscv_check_vcpu_requests(vcpu); + if (ret <= 0) + continue; preempt_disable(); @@ -961,12 +963,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) } /* - * Cleanup stale TLB enteries + * Sanitize VMID mappings cached (TLB) on current CPU * * Note: This should be done after G-stage VMID has been * updated using kvm_riscv_gstage_vmid_ver_changed() */ - kvm_riscv_local_tlb_sanitize(vcpu); + kvm_riscv_gstage_vmid_sanitize(vcpu); trace_kvm_entry(vcpu); diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c index 6e0c18412795..0bb0c51e3c89 100644 --- a/arch/riscv/kvm/vcpu_exit.c +++ b/arch/riscv/kvm/vcpu_exit.c @@ -9,10 +9,13 @@ #include #include #include +#include +#include static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_cpu_trap *trap) { + struct kvm_gstage_mapping host_map; struct kvm_memory_slot *memslot; unsigned long hva, fault_addr; bool writable; @@ -40,8 +43,9 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, }; } - ret = kvm_riscv_gstage_map(vcpu, memslot, fault_addr, hva, - (trap->scause == EXC_STORE_GUEST_PAGE_FAULT) ? true : false); + ret = kvm_riscv_mmu_map(vcpu, memslot, fault_addr, hva, + (trap->scause == EXC_STORE_GUEST_PAGE_FAULT) ? true : false, + &host_map); if (ret < 0) return ret; @@ -135,7 +139,7 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu, void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu, struct kvm_cpu_trap *trap) { - unsigned long vsstatus = csr_read(CSR_VSSTATUS); + unsigned long vsstatus = ncsr_read(CSR_VSSTATUS); /* Change Guest SSTATUS.SPP bit */ vsstatus &= ~SR_SPP; @@ -151,15 +155,15 @@ void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu, vsstatus &= ~SR_SIE; /* Update Guest SSTATUS */ - csr_write(CSR_VSSTATUS, vsstatus); + ncsr_write(CSR_VSSTATUS, vsstatus); /* Update Guest SCAUSE, STVAL, and SEPC */ - csr_write(CSR_VSCAUSE, trap->scause); - csr_write(CSR_VSTVAL, trap->stval); - csr_write(CSR_VSEPC, trap->sepc); + ncsr_write(CSR_VSCAUSE, trap->scause); + ncsr_write(CSR_VSTVAL, trap->stval); + ncsr_write(CSR_VSEPC, trap->sepc); /* Set Guest PC to Guest exception vector */ - vcpu->arch.guest_context.sepc = csr_read(CSR_VSTVEC); + vcpu->arch.guest_context.sepc = ncsr_read(CSR_VSTVEC); /* Set Guest privilege mode to supervisor */ vcpu->arch.guest_context.sstatus |= SR_SPP; diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index 2e1b646f0d61..cce6a38ea54f 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -23,7 +23,7 @@ #define KVM_ISA_EXT_ARR(ext) \ [KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext -/* Mapping between KVM ISA Extension ID & Host ISA extension ID */ +/* 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, @@ -35,7 +35,7 @@ static const unsigned long kvm_isa_ext_arr[] = { [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, /* Multi letter extensions (alphabetically sorted) */ - [KVM_RISCV_ISA_EXT_SMNPM] = RISCV_ISA_EXT_SSNPM, + KVM_ISA_EXT_ARR(SMNPM), KVM_ISA_EXT_ARR(SMSTATEEN), KVM_ISA_EXT_ARR(SSAIA), KVM_ISA_EXT_ARR(SSCOFPMF), @@ -112,6 +112,36 @@ static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) 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; + + *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) { @@ -219,13 +249,13 @@ static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) void kvm_riscv_vcpu_setup_isa(struct kvm_vcpu *vcpu) { - unsigned long host_isa, i; + unsigned long guest_ext, i; for (i = 0; i < ARRAY_SIZE(kvm_isa_ext_arr); i++) { - host_isa = kvm_isa_ext_arr[i]; - if (__riscv_isa_extension_available(NULL, host_isa) && - kvm_riscv_vcpu_isa_enable_allowed(i)) - set_bit(host_isa, vcpu->arch.isa); + if (kvm_riscv_vcpu_isa_check_host(i, &guest_ext)) + continue; + if (kvm_riscv_vcpu_isa_enable_allowed(i)) + set_bit(guest_ext, vcpu->arch.isa); } } @@ -607,18 +637,15 @@ static int riscv_vcpu_get_isa_ext_single(struct kvm_vcpu *vcpu, unsigned long reg_num, unsigned long *reg_val) { - unsigned long host_isa_ext; + unsigned long guest_ext; + int ret; - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || - reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) - return -ENOENT; - - host_isa_ext = kvm_isa_ext_arr[reg_num]; - if (!__riscv_isa_extension_available(NULL, host_isa_ext)) - return -ENOENT; + ret = kvm_riscv_vcpu_isa_check_host(reg_num, &guest_ext); + if (ret) + return ret; *reg_val = 0; - if (__riscv_isa_extension_available(vcpu->arch.isa, host_isa_ext)) + if (__riscv_isa_extension_available(vcpu->arch.isa, guest_ext)) *reg_val = 1; /* Mark the given extension as available */ return 0; @@ -628,17 +655,14 @@ static int riscv_vcpu_set_isa_ext_single(struct kvm_vcpu *vcpu, unsigned long reg_num, unsigned long reg_val) { - unsigned long host_isa_ext; + unsigned long guest_ext; + int ret; - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || - reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) - return -ENOENT; + ret = kvm_riscv_vcpu_isa_check_host(reg_num, &guest_ext); + if (ret) + return ret; - host_isa_ext = kvm_isa_ext_arr[reg_num]; - if (!__riscv_isa_extension_available(NULL, host_isa_ext)) - return -ENOENT; - - if (reg_val == test_bit(host_isa_ext, vcpu->arch.isa)) + if (reg_val == test_bit(guest_ext, vcpu->arch.isa)) return 0; if (!vcpu->arch.ran_atleast_once) { @@ -648,10 +672,10 @@ static int riscv_vcpu_set_isa_ext_single(struct kvm_vcpu *vcpu, */ if (reg_val == 1 && kvm_riscv_vcpu_isa_enable_allowed(reg_num)) - set_bit(host_isa_ext, vcpu->arch.isa); + set_bit(guest_ext, vcpu->arch.isa); else if (!reg_val && kvm_riscv_vcpu_isa_disable_allowed(reg_num)) - clear_bit(host_isa_ext, vcpu->arch.isa); + clear_bit(guest_ext, vcpu->arch.isa); else return -EINVAL; kvm_riscv_vcpu_fp_reset(vcpu); @@ -1009,16 +1033,15 @@ static int copy_fp_d_reg_indices(const struct kvm_vcpu *vcpu, static int copy_isa_ext_reg_indices(const struct kvm_vcpu *vcpu, u64 __user *uindices) { + unsigned long guest_ext; unsigned int n = 0; - unsigned long isa_ext; for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { u64 size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_ISA_EXT | i; - isa_ext = kvm_isa_ext_arr[i]; - if (!__riscv_isa_extension_available(NULL, isa_ext)) + if (kvm_riscv_vcpu_isa_check_host(i, &guest_ext)) continue; if (uindices) { diff --git a/arch/riscv/kvm/vcpu_sbi.c b/arch/riscv/kvm/vcpu_sbi.c index 6e09b518a5d1..a56c4959f9ad 100644 --- a/arch/riscv/kvm/vcpu_sbi.c +++ b/arch/riscv/kvm/vcpu_sbi.c @@ -536,5 +536,54 @@ void kvm_riscv_vcpu_sbi_init(struct kvm_vcpu *vcpu) scontext->ext_status[idx] = ext->default_disabled ? KVM_RISCV_SBI_EXT_STATUS_DISABLED : KVM_RISCV_SBI_EXT_STATUS_ENABLED; + + if (ext->init && ext->init(vcpu) != 0) + scontext->ext_status[idx] = KVM_RISCV_SBI_EXT_STATUS_UNAVAILABLE; + } +} + +void kvm_riscv_vcpu_sbi_deinit(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; + const struct kvm_riscv_sbi_extension_entry *entry; + const struct kvm_vcpu_sbi_extension *ext; + int idx, i; + + for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { + entry = &sbi_ext[i]; + ext = entry->ext_ptr; + idx = entry->ext_idx; + + if (idx < 0 || idx >= ARRAY_SIZE(scontext->ext_status)) + continue; + + if (scontext->ext_status[idx] == KVM_RISCV_SBI_EXT_STATUS_UNAVAILABLE || + !ext->deinit) + continue; + + ext->deinit(vcpu); + } +} + +void kvm_riscv_vcpu_sbi_reset(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; + const struct kvm_riscv_sbi_extension_entry *entry; + const struct kvm_vcpu_sbi_extension *ext; + int idx, i; + + for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { + entry = &sbi_ext[i]; + ext = entry->ext_ptr; + idx = entry->ext_idx; + + if (idx < 0 || idx >= ARRAY_SIZE(scontext->ext_status)) + continue; + + if (scontext->ext_status[idx] != KVM_RISCV_SBI_EXT_STATUS_ENABLED || + !ext->reset) + continue; + + ext->reset(vcpu); } } diff --git a/arch/riscv/kvm/vcpu_sbi_replace.c b/arch/riscv/kvm/vcpu_sbi_replace.c index 5fbf3f94f1e8..b490ed1428a6 100644 --- a/arch/riscv/kvm/vcpu_sbi_replace.c +++ b/arch/riscv/kvm/vcpu_sbi_replace.c @@ -96,6 +96,7 @@ static int kvm_sbi_ext_rfence_handler(struct kvm_vcpu *vcpu, struct kvm_run *run unsigned long hmask = cp->a0; unsigned long hbase = cp->a1; unsigned long funcid = cp->a6; + unsigned long vmid; switch (funcid) { case SBI_EXT_RFENCE_REMOTE_FENCE_I: @@ -103,22 +104,22 @@ static int kvm_sbi_ext_rfence_handler(struct kvm_vcpu *vcpu, struct kvm_run *run kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_FENCE_I_SENT); break; case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: - if (cp->a2 == 0 && cp->a3 == 0) - kvm_riscv_hfence_vvma_all(vcpu->kvm, hbase, hmask); + vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); + if ((cp->a2 == 0 && cp->a3 == 0) || cp->a3 == -1UL) + kvm_riscv_hfence_vvma_all(vcpu->kvm, hbase, hmask, vmid); else kvm_riscv_hfence_vvma_gva(vcpu->kvm, hbase, hmask, - cp->a2, cp->a3, PAGE_SHIFT); + cp->a2, cp->a3, PAGE_SHIFT, vmid); kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_SENT); break; case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID: - if (cp->a2 == 0 && cp->a3 == 0) - kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, - hbase, hmask, cp->a4); + vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); + if ((cp->a2 == 0 && cp->a3 == 0) || cp->a3 == -1UL) + kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, hbase, hmask, + cp->a4, vmid); else - kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, - hbase, hmask, - cp->a2, cp->a3, - PAGE_SHIFT, cp->a4); + kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, hbase, hmask, cp->a2, + cp->a3, PAGE_SHIFT, cp->a4, vmid); kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_SENT); break; case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA: @@ -127,9 +128,9 @@ static int kvm_sbi_ext_rfence_handler(struct kvm_vcpu *vcpu, struct kvm_run *run case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID: /* * Until nested virtualization is implemented, the - * SBI HFENCE calls should be treated as NOPs + * SBI HFENCE calls should return not supported + * hence fallthrough. */ - break; default: retdata->err_val = SBI_ERR_NOT_SUPPORTED; } diff --git a/arch/riscv/kvm/vcpu_sbi_sta.c b/arch/riscv/kvm/vcpu_sbi_sta.c index 5f35427114c1..cc6cb7c8f0e4 100644 --- a/arch/riscv/kvm/vcpu_sbi_sta.c +++ b/arch/riscv/kvm/vcpu_sbi_sta.c @@ -16,7 +16,7 @@ #include #include -void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu) +static void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu) { vcpu->arch.sta.shmem = INVALID_GPA; vcpu->arch.sta.last_steal = 0; @@ -156,6 +156,7 @@ const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_sta = { .extid_end = SBI_EXT_STA, .handler = kvm_sbi_ext_sta_handler, .probe = kvm_sbi_ext_sta_probe, + .reset = kvm_riscv_vcpu_sbi_sta_reset, }; int kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu *vcpu, diff --git a/arch/riscv/kvm/vcpu_sbi_v01.c b/arch/riscv/kvm/vcpu_sbi_v01.c index 8f4c4fa16227..368dfddd23d9 100644 --- a/arch/riscv/kvm/vcpu_sbi_v01.c +++ b/arch/riscv/kvm/vcpu_sbi_v01.c @@ -23,6 +23,7 @@ static int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm *kvm = vcpu->kvm; struct kvm_cpu_context *cp = &vcpu->arch.guest_context; struct kvm_cpu_trap *utrap = retdata->utrap; + unsigned long vmid; switch (cp->a7) { case SBI_EXT_0_1_CONSOLE_GETCHAR: @@ -78,25 +79,21 @@ static int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I) kvm_riscv_fence_i(vcpu->kvm, 0, hmask); else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA) { + vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); if (cp->a1 == 0 && cp->a2 == 0) - kvm_riscv_hfence_vvma_all(vcpu->kvm, - 0, hmask); + kvm_riscv_hfence_vvma_all(vcpu->kvm, 0, hmask, vmid); else - kvm_riscv_hfence_vvma_gva(vcpu->kvm, - 0, hmask, - cp->a1, cp->a2, - PAGE_SHIFT); + kvm_riscv_hfence_vvma_gva(vcpu->kvm, 0, hmask, cp->a1, + cp->a2, PAGE_SHIFT, vmid); } else { + vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); if (cp->a1 == 0 && cp->a2 == 0) - kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, - 0, hmask, - cp->a3); + kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, 0, hmask, + cp->a3, vmid); else - kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, - 0, hmask, - cp->a1, cp->a2, - PAGE_SHIFT, - cp->a3); + kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, 0, hmask, + cp->a1, cp->a2, PAGE_SHIFT, + cp->a3, vmid); } break; default: diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index ff672fa71fcc..85a7262115e1 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -345,8 +345,24 @@ void kvm_riscv_vcpu_timer_save(struct kvm_vcpu *vcpu) /* * The vstimecmp CSRs are saved by kvm_riscv_vcpu_timer_sync() * upon every VM exit so no need to save here. + * + * If VS-timer expires when no VCPU running on a host CPU then + * WFI executed by such host CPU will be effective NOP resulting + * in no power savings. This is because as-per RISC-V Privileged + * specificaiton: "WFI is also required to resume execution for + * locally enabled interrupts pending at any privilege level, + * regardless of the global interrupt enable at each privilege + * level." + * + * To address the above issue, vstimecmp CSR must be set to -1UL + * over here when VCPU is scheduled-out or exits to user space. */ + csr_write(CSR_VSTIMECMP, -1UL); +#if defined(CONFIG_32BIT) + csr_write(CSR_VSTIMECMPH, -1UL); +#endif + /* timer should be enabled for the remaining operations */ if (unlikely(!t->init_done)) return; diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c index a5f88cb717f3..05f3cc2d8e31 100644 --- a/arch/riscv/kvm/vcpu_vector.c +++ b/arch/riscv/kvm/vcpu_vector.c @@ -182,6 +182,8 @@ int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu, struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; unsigned long reg_val; + if (reg_size != sizeof(reg_val)) + return -EINVAL; if (copy_from_user(®_val, uaddr, reg_size)) return -EFAULT; if (reg_val != cntx->vector.vlenb) diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c index b27ec8f96697..66d91ae6e9b2 100644 --- a/arch/riscv/kvm/vm.c +++ b/arch/riscv/kvm/vm.c @@ -11,6 +11,7 @@ #include #include #include +#include const struct _kvm_stats_desc kvm_vm_stats_desc[] = { KVM_GENERIC_VM_STATS() @@ -31,13 +32,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { int r; - r = kvm_riscv_gstage_alloc_pgd(kvm); + r = kvm_riscv_mmu_alloc_pgd(kvm); if (r) return r; r = kvm_riscv_gstage_vmid_init(kvm); if (r) { - kvm_riscv_gstage_free_pgd(kvm); + kvm_riscv_mmu_free_pgd(kvm); return r; } @@ -199,7 +200,7 @@ 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(); + r = kvm_riscv_gstage_gpa_bits; break; default: r = 0; diff --git a/arch/riscv/kvm/vmid.c b/arch/riscv/kvm/vmid.c index ddc98714ce8e..3b426c800480 100644 --- a/arch/riscv/kvm/vmid.c +++ b/arch/riscv/kvm/vmid.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include static unsigned long vmid_version = 1; static unsigned long vmid_next; @@ -122,3 +124,26 @@ void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu) kvm_for_each_vcpu(i, v, vcpu->kvm) kvm_make_request(KVM_REQ_UPDATE_HGATP, v); } + +void kvm_riscv_gstage_vmid_sanitize(struct kvm_vcpu *vcpu) +{ + unsigned long vmid; + + if (!kvm_riscv_gstage_vmid_bits() || + vcpu->arch.last_exit_cpu == vcpu->cpu) + return; + + /* + * On RISC-V platforms with hardware VMID support, we share same + * VMID for all VCPUs of a particular Guest/VM. This means we might + * have stale G-stage TLB entries on the current Host CPU due to + * some other VCPU of the same Guest which ran previously on the + * current Host CPU. + * + * To cleanup stale TLB entries, we simply flush all G-stage TLB + * entries by VMID whenever underlying Host CPU changes for a VCPU. + */ + + vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); + kvm_riscv_local_hfence_gvma_vmid_all(vmid); +} diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 0baec92d2f55..bbc031124974 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += crypto/ lib-y += delay.o lib-y += memcpy.o lib-y += memset.o @@ -16,12 +15,6 @@ endif lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o -obj-$(CONFIG_CRC32_ARCH) += crc32-riscv.o -crc32-riscv-y := crc32.o crc32_msb.o crc32_lsb.o -obj-$(CONFIG_CRC64_ARCH) += crc64-riscv.o -crc64-riscv-y := crc64.o crc64_msb.o crc64_lsb.o -obj-$(CONFIG_CRC_T10DIF_ARCH) += crc-t10dif-riscv.o -crc-t10dif-riscv-y := crc-t10dif.o crc16_msb.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/crypto/Kconfig b/arch/riscv/lib/crypto/Kconfig deleted file mode 100644 index 47c99ea97ce2..000000000000 --- a/arch/riscv/lib/crypto/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config CRYPTO_CHACHA_RISCV64 - tristate - depends on 64BIT && RISCV_ISA_V && TOOLCHAIN_HAS_VECTOR_CRYPTO - default CRYPTO_LIB_CHACHA - select CRYPTO_ARCH_HAVE_LIB_CHACHA - select CRYPTO_LIB_CHACHA_GENERIC - -config CRYPTO_SHA256_RISCV64 - tristate - depends on 64BIT && RISCV_ISA_V && TOOLCHAIN_HAS_VECTOR_CRYPTO - default CRYPTO_LIB_SHA256 - select CRYPTO_ARCH_HAVE_LIB_SHA256 - select CRYPTO_ARCH_HAVE_LIB_SHA256_SIMD - select CRYPTO_LIB_SHA256_GENERIC diff --git a/arch/riscv/lib/crypto/sha256.c b/arch/riscv/lib/crypto/sha256.c deleted file mode 100644 index 71808397dff4..000000000000 --- a/arch/riscv/lib/crypto/sha256.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-256 (RISC-V accelerated) - * - * Copyright (C) 2022 VRULL GmbH - * Author: Heiko Stuebner - * - * Copyright (C) 2023 SiFive, Inc. - * Author: Jerry Shih - */ - -#include -#include -#include -#include - -asmlinkage void sha256_transform_zvknha_or_zvknhb_zvkb( - u32 state[SHA256_STATE_WORDS], const u8 *data, size_t nblocks); - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_extensions); - -void sha256_blocks_simd(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - if (static_branch_likely(&have_extensions)) { - kernel_vector_begin(); - sha256_transform_zvknha_or_zvknhb_zvkb(state, data, nblocks); - kernel_vector_end(); - } else { - sha256_blocks_generic(state, data, nblocks); - } -} -EXPORT_SYMBOL_GPL(sha256_blocks_simd); - -void sha256_blocks_arch(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - sha256_blocks_generic(state, data, nblocks); -} -EXPORT_SYMBOL_GPL(sha256_blocks_arch); - -bool sha256_is_arch_optimized(void) -{ - return static_key_enabled(&have_extensions); -} -EXPORT_SYMBOL_GPL(sha256_is_arch_optimized); - -static int __init riscv64_sha256_mod_init(void) -{ - /* Both zvknha and zvknhb provide the SHA-256 instructions. */ - if ((riscv_isa_extension_available(NULL, ZVKNHA) || - riscv_isa_extension_available(NULL, ZVKNHB)) && - riscv_isa_extension_available(NULL, ZVKB) && - riscv_vector_vlen() >= 128) - static_branch_enable(&have_extensions); - return 0; -} -subsys_initcall(riscv64_sha256_mod_init); - -static void __exit riscv64_sha256_mod_exit(void) -{ -} -module_exit(riscv64_sha256_mod_exit); - -MODULE_DESCRIPTION("SHA-256 (RISC-V accelerated)"); -MODULE_AUTHOR("Heiko Stuebner "); -MODULE_LICENSE("GPL"); diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index 0194324a0c50..04ed6f8acae4 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -20,6 +20,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include "../kernel/head.h" static void show_pte(unsigned long addr) @@ -291,6 +294,11 @@ void handle_page_fault(struct pt_regs *regs) if (kprobe_page_fault(regs, cause)) return; + if (user_mode(regs)) + trace_page_fault_user(addr, regs, cause); + else + trace_page_fault_kernel(addr, regs, cause); + /* * Fault-in kernel-space virtual memory on-demand. * The 'reference' page table is init_mm.pgd. diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 8d0374d7ce8e..15683ae13fa5 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1408,7 +1408,7 @@ static void __init arch_reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), &crash_size, &crash_base, - &low_size, &high); + &low_size, NULL, &high); if (ret) return; diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index d815448758a1..3f76db3d2769 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -299,7 +299,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_kernel_page_table_range(lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; @@ -317,13 +317,13 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_kernel_page_table_range(lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; } - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); unlock: @@ -335,7 +335,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, */ flush_tlb_all(); #else - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); mmap_write_unlock(&init_mm); diff --git a/arch/riscv/mm/ptdump.c b/arch/riscv/mm/ptdump.c index 32922550a50a..3b51690cc876 100644 --- a/arch/riscv/mm/ptdump.c +++ b/arch/riscv/mm/ptdump.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -413,9 +412,7 @@ bool ptdump_check_wx(void) static int ptdump_show(struct seq_file *m, void *v) { - get_online_mems(); ptdump_walk(m, m->private); - put_online_mems(); return 0; } diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c index e737ba7949b1..8404530ec00f 100644 --- a/arch/riscv/mm/tlbflush.c +++ b/arch/riscv/mm/tlbflush.c @@ -234,11 +234,6 @@ void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch, mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end); } -void arch_flush_tlb_batched_pending(struct mm_struct *mm) -{ - flush_tlb_mm(mm); -} - void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) { __flush_tlb_range(NULL, &batch->cpumask, diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 10e01ff06312..9883a55d61b5 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -1356,7 +1356,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, emit_mv(rd, rs, ctx); #ifdef CONFIG_SMP /* Load current CPU number in T1 */ - emit_ld(RV_REG_T1, offsetof(struct thread_info, cpu), + emit_lw(RV_REG_T1, offsetof(struct thread_info, cpu), RV_REG_TP, ctx); /* Load address of __per_cpu_offset array in T2 */ emit_addr(RV_REG_T2, (u64)&__per_cpu_offset, extra_pass, ctx); @@ -1763,7 +1763,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, */ if (insn->src_reg == 0 && insn->imm == BPF_FUNC_get_smp_processor_id) { /* Load current CPU number in R0 */ - emit_ld(bpf_to_rv_reg(BPF_REG_0, ctx), offsetof(struct thread_info, cpu), + emit_lw(bpf_to_rv_reg(BPF_REG_0, ctx), offsetof(struct thread_info, cpu), RV_REG_TP, ctx); break; } diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile index fb9c917c9b45..240592e3f5c2 100644 --- a/arch/riscv/purgatory/Makefile +++ b/arch/riscv/purgatory/Makefile @@ -53,7 +53,7 @@ targets += purgatory.ro purgatory.chk PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel PURGATORY_CFLAGS := -mcmodel=medany -ffreestanding -fno-zero-initialized-in-bss -PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING +PURGATORY_CFLAGS += $(DISABLE_KSTACK_ERASE) -DDISABLE_BRANCH_PROFILING PURGATORY_CFLAGS += -fno-stack-protector -g0 # Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That diff --git a/arch/riscv/purgatory/purgatory.c b/arch/riscv/purgatory/purgatory.c index 80596ab5fb62..bbd5cfa4d741 100644 --- a/arch/riscv/purgatory/purgatory.c +++ b/arch/riscv/purgatory/purgatory.c @@ -20,14 +20,14 @@ struct kexec_sha_region purgatory_sha_regions[KEXEC_SEGMENT_MAX] __section(".kex static int verify_sha256_digest(void) { struct kexec_sha_region *ptr, *end; - struct sha256_state ss; + struct sha256_ctx sctx; u8 digest[SHA256_DIGEST_SIZE]; - sha256_init(&ss); + sha256_init(&sctx); end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions); for (ptr = purgatory_sha_regions; ptr < end; ptr++) - sha256_update(&ss, (uint8_t *)(ptr->start), ptr->len); - sha256_final(&ss, digest); + sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len); + sha256_final(&sctx, digest); if (memcmp(digest, purgatory_sha256_digest, sizeof(digest)) != 0) return 1; return 0; diff --git a/arch/riscv/tools/relocs_check.sh b/arch/riscv/tools/relocs_check.sh index baeb2e7b2290..742993e6a8cb 100755 --- a/arch/riscv/tools/relocs_check.sh +++ b/arch/riscv/tools/relocs_check.sh @@ -14,7 +14,9 @@ bad_relocs=$( ${srctree}/scripts/relocs_check.sh "$@" | # These relocations are okay # R_RISCV_RELATIVE - grep -F -w -v 'R_RISCV_RELATIVE' + # R_RISCV_NONE + grep -F -w -v 'R_RISCV_RELATIVE +R_RISCV_NONE' ) if [ -z "$bad_relocs" ]; then diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0c16dc443e2f..bf680c26a33c 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -74,8 +74,8 @@ config S390 select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM select ARCH_ENABLE_MEMORY_HOTREMOVE select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 + select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_CPU_FINALIZE_INIT - select ARCH_HAS_CRC32 select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL select ARCH_HAS_DEBUG_VM_PGTABLE @@ -103,6 +103,7 @@ config S390 select ARCH_HAS_UBSAN select ARCH_HAS_VDSO_TIME_DATA select ARCH_HAVE_NMI_SAFE_CMPXCHG + select ARCH_HAVE_TRACE_MMIO_ACCESS select ARCH_INLINE_READ_LOCK select ARCH_INLINE_READ_LOCK_BH select ARCH_INLINE_READ_LOCK_IRQ @@ -132,6 +133,7 @@ config S390 select ARCH_INLINE_WRITE_UNLOCK_IRQ select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE + select ARCH_MODULE_NEEDS_WEAK_PER_CPU select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC @@ -150,6 +152,7 @@ config S390 select ARCH_WANT_KERNEL_PMD_MKWRITE select ARCH_WANT_LD_ORPHAN_WARN select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP + select ARCH_WANTS_THP_SWAP select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 select DCACHE_WORD_ACCESS if !KMSAN @@ -176,10 +179,10 @@ config S390 select HAVE_ARCH_KCSAN select HAVE_ARCH_KMSAN select HAVE_ARCH_KFENCE + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SOFT_DIRTY - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_VMAP_STACK @@ -199,7 +202,6 @@ config S390 select HAVE_GUP_FAST select HAVE_FENTRY select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index dd7ba7587dd5..ad2b0baa527c 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -12,6 +12,7 @@ #define KMSG_COMPONENT "appldata" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index bee49626be4b..02f2cf082748 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -19,15 +19,15 @@ CC_FLAGS_MARCH_MINIMUM := -march=z10 KBUILD_AFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_AFLAGS_DECOMPRESSOR)) KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_CFLAGS_DECOMPRESSOR)) -KBUILD_AFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -KBUILD_CFLAGS += $(CC_FLAGS_MARCH_MINIMUM) +KBUILD_AFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS +KBUILD_CFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char obj-y := head.o als.o startup.o physmem_info.o ipl_parm.o ipl_report.o vmem.o obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o obj-y += version.o pgm_check.o ctype.o ipl_data.o relocs.o alternative.o -obj-y += uv.o printk.o +obj-y += uv.o printk.o trampoline.o obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o obj-y += $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) info.o obj-$(CONFIG_KERNEL_ZSTD) += clz_ctz.o diff --git a/arch/s390/boot/als.c b/arch/s390/boot/als.c index 79afb5fa7f1f..25a20986b96e 100644 --- a/arch/s390/boot/als.c +++ b/arch/s390/boot/als.c @@ -65,7 +65,7 @@ static void facility_mismatch(void) boot_emerg("The Linux kernel requires more recent processor hardware\n"); boot_emerg("Detected machine-type number: %4x\n", id.machine); print_missing_facilities(); - boot_emerg("See Principles of Operations for facility bits\n"); + boot_emerg("See z/Architecture Principles of Operation - Facility Indications\n"); disabled_wait(); } diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index e045cae6e80a..c0152db285f0 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -6,7 +6,7 @@ #define IPL_START 0x200 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -74,6 +74,7 @@ void print_stacktrace(unsigned long sp); void error(char *m); int get_random(unsigned long limit, unsigned long *value); void boot_rb_dump(void); +void __noreturn jump_to_kernel(psw_t *psw); #ifndef boot_fmt #define boot_fmt(fmt) fmt @@ -121,5 +122,5 @@ static inline bool intersects(unsigned long addr0, unsigned long size0, { return addr0 + size0 > addr1 && addr1 + size1 > addr0; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* BOOT_BOOT_H */ diff --git a/arch/s390/boot/ipl_data.c b/arch/s390/boot/ipl_data.c index 0846e2b249c6..c4130a80b058 100644 --- a/arch/s390/boot/ipl_data.c +++ b/arch/s390/boot/ipl_data.c @@ -16,7 +16,9 @@ struct ipl_lowcore { struct ccw0 ccwpgm[2]; /* 0x0008 */ u8 fill[56]; /* 0x0018 */ struct ccw0 ccwpgmcc[20]; /* 0x0050 */ - u8 pad_0xf0[0x01a0-0x00f0]; /* 0x00f0 */ + u8 pad_0xf0[0x0140-0x00f0]; /* 0x00f0 */ + psw_t svc_old_psw; /* 0x0140 */ + u8 pad_0x150[0x01a0-0x0150]; /* 0x0150 */ psw_t restart_psw; /* 0x01a0 */ psw_t external_new_psw; /* 0x01b0 */ psw_t svc_new_psw; /* 0x01c0 */ @@ -75,6 +77,11 @@ static struct ipl_lowcore ipl_lowcore __used __section(".ipldata") = { [18] = CCW0(CCW_CMD_READ_IPL, 0x690, 0x50, CCW_FLAG_SLI | CCW_FLAG_CC), [19] = CCW0(CCW_CMD_READ_IPL, 0x6e0, 0x50, CCW_FLAG_SLI), }, + /* + * Let the GDB's lx-symbols command find the jump_to_kernel symbol + * without having to load decompressor symbols. + */ + .svc_old_psw = { .mask = 0, .addr = (unsigned long)jump_to_kernel }, .restart_psw = { .mask = 0, .addr = IPL_START, }, .external_new_psw = { .mask = PSW_MASK_DISABLED, .addr = __LC_EXT_NEW_PSW, }, .svc_new_psw = { .mask = PSW_MASK_DISABLED, .addr = __LC_SVC_NEW_PSW, }, diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index da8337e63a3e..93684a775716 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -384,7 +384,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size) kernel_start = round_down(kernel_end - kernel_size, THREAD_SIZE); boot_debug("Randomization range: 0x%016lx-0x%016lx\n", vmax - kaslr_len, vmax); boot_debug("kernel image: 0x%016lx-0x%016lx (kaslr)\n", kernel_start, - kernel_size + kernel_size); + kernel_start + kernel_size); } else if (vmax < __NO_KASLR_END_KERNEL || vsize > __NO_KASLR_END_KERNEL) { kernel_start = round_down(vmax - kernel_size, THREAD_SIZE); boot_debug("kernel image: 0x%016lx-0x%016lx (constrained)\n", kernel_start, @@ -642,5 +642,5 @@ void startup_kernel(void) psw.addr = __kaslr_offset + vmlinux.entry; psw.mask = PSW_KERNEL_BITS; boot_debug("Starting kernel at: 0x%016lx\n", psw.addr); - __load_psw(psw); + jump_to_kernel(&psw); } diff --git a/arch/s390/boot/trampoline.S b/arch/s390/boot/trampoline.S new file mode 100644 index 000000000000..1cb5adf005ea --- /dev/null +++ b/arch/s390/boot/trampoline.S @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include + +# This function is identical to __load_psw(), but the lx-symbols GDB command +# puts a breakpoint on it, so it needs to be kept separate. +SYM_CODE_START(jump_to_kernel) + lpswe 0(%r2) +SYM_CODE_END(jump_to_kernel) diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 1d073acd05a7..cea3de4dce8c 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -530,6 +530,9 @@ void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned l lowcore_address + sizeof(struct lowcore), POPULATE_LOWCORE); for_each_physmem_usable_range(i, &start, &end) { + /* Do not map lowcore with identity mapping */ + if (!start) + start = sizeof(struct lowcore); pgtable_populate((unsigned long)__identity_va(start), (unsigned long)__identity_va(end), POPULATE_IDENTITY); diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 8ecad727497e..5e616bc988ac 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -5,6 +5,7 @@ CONFIG_WATCH_QUEUE=y CONFIG_AUDIT=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_POSIX_AUX_CLOCKS=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y CONFIG_BPF_JIT_ALWAYS_ON=y @@ -19,6 +20,7 @@ CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y +CONFIG_SCHED_PROXY_EXEC=y CONFIG_NUMA_BALANCING=y CONFIG_MEMCG=y CONFIG_BLK_CGROUP=y @@ -42,6 +44,7 @@ CONFIG_PROFILING=y CONFIG_KEXEC=y CONFIG_KEXEC_FILE=y CONFIG_KEXEC_SIG=y +CONFIG_CRASH_DM_CRYPT=y CONFIG_LIVEPATCH=y CONFIG_MARCH_Z13=y CONFIG_NR_CPUS=512 @@ -105,6 +108,7 @@ CONFIG_CMA_AREAS=7 CONFIG_MEM_SOFT_DIRTY=y CONFIG_DEFERRED_STRUCT_PAGE_INIT=y CONFIG_IDLE_PAGE_TRACKING=y +CONFIG_ZONE_DEVICE=y CONFIG_PERCPU_STATS=y CONFIG_GUP_TEST=y CONFIG_ANON_VMA_NAME=y @@ -223,17 +227,19 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m CONFIG_NETFILTER_XT_TARGET_CT=m CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m CONFIG_NETFILTER_XT_TARGET_HMARK=m CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_NAT=m CONFIG_NETFILTER_XT_TARGET_NETMAP=m CONFIG_NETFILTER_XT_TARGET_NFLOG=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m CONFIG_NETFILTER_XT_TARGET_TEE=m CONFIG_NETFILTER_XT_TARGET_TPROXY=m -CONFIG_NETFILTER_XT_TARGET_TRACE=m CONFIG_NETFILTER_XT_TARGET_SECMARK=m CONFIG_NETFILTER_XT_TARGET_TCPMSS=m CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m @@ -319,16 +325,8 @@ CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_RPFILTER=m CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_TTL=m -CONFIG_IP_NF_RAW=m -CONFIG_IP_NF_SECURITY=m -CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_NFT_FIB_IPV6=m CONFIG_IP6_NF_IPTABLES=m @@ -341,15 +339,9 @@ CONFIG_IP6_NF_MATCH_IPV6HEADER=m CONFIG_IP6_NF_MATCH_MH=m CONFIG_IP6_NF_MATCH_RPFILTER=m CONFIG_IP6_NF_MATCH_RT=m -CONFIG_IP6_NF_TARGET_HL=m -CONFIG_IP6_NF_FILTER=m CONFIG_IP6_NF_TARGET_REJECT=m -CONFIG_IP6_NF_MANGLE=m -CONFIG_IP6_NF_RAW=m -CONFIG_IP6_NF_SECURITY=m -CONFIG_IP6_NF_NAT=m -CONFIG_IP6_NF_TARGET_MASQUERADE=m CONFIG_NF_TABLES_BRIDGE=m +CONFIG_IP_SCTP=m CONFIG_RDS=m CONFIG_RDS_RDMA=m CONFIG_RDS_TCP=m @@ -384,6 +376,7 @@ CONFIG_NET_SCH_FQ_CODEL=m CONFIG_NET_SCH_INGRESS=m CONFIG_NET_SCH_PLUG=m CONFIG_NET_SCH_ETS=m +CONFIG_NET_SCH_DUALPI2=m CONFIG_NET_CLS_BASIC=m CONFIG_NET_CLS_ROUTE4=m CONFIG_NET_CLS_FW=m @@ -505,6 +498,7 @@ CONFIG_DM_VDO=m CONFIG_NETDEVICES=y CONFIG_BONDING=m CONFIG_DUMMY=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_IFB=m CONFIG_MACVLAN=m @@ -642,6 +636,7 @@ CONFIG_VP_VDPA=m CONFIG_VHOST_NET=m CONFIG_VHOST_VSOCK=m CONFIG_VHOST_VDPA=m +CONFIG_DEV_DAX=m CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y @@ -666,6 +661,7 @@ CONFIG_NILFS2_FS=m CONFIG_BCACHEFS_FS=y CONFIG_BCACHEFS_QUOTA=y CONFIG_BCACHEFS_POSIX_ACL=y +CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y CONFIG_FS_ENCRYPTION=y CONFIG_FS_VERITY=y @@ -756,6 +752,8 @@ CONFIG_HARDENED_USERCOPY=y CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_CRYPTO_USER=m CONFIG_CRYPTO_SELFTESTS=y +CONFIG_CRYPTO_SELFTESTS_FULL=y +CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m @@ -784,7 +782,6 @@ CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_MD4=m @@ -804,8 +801,6 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m -CONFIG_CRYPTO_SHA512_S390=m -CONFIG_CRYPTO_SHA1_S390=m CONFIG_CRYPTO_SHA3_256_S390=m CONFIG_CRYPTO_SHA3_512_S390=m CONFIG_CRYPTO_GHASH_S390=m @@ -819,11 +814,13 @@ CONFIG_PKEY_EP11=m CONFIG_PKEY_PCKMO=m CONFIG_PKEY_UV=m CONFIG_CRYPTO_PAES_S390=m +CONFIG_CRYPTO_PHMAC_S390=m CONFIG_CRYPTO_DEV_VIRTIO=m CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_CRYPTO_KRB5=m CONFIG_CRYPTO_KRB5_SELFTESTS=y CONFIG_CORDIC=m +CONFIG_TRACE_MMIO_ACCESS=y CONFIG_RANDOM32_SELFTEST=y CONFIG_XZ_DEC_MICROLZMA=y CONFIG_DMA_CMA=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index c13a77765162..094599cdaf4d 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -4,6 +4,7 @@ CONFIG_WATCH_QUEUE=y CONFIG_AUDIT=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_POSIX_AUX_CLOCKS=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y CONFIG_BPF_JIT_ALWAYS_ON=y @@ -17,6 +18,7 @@ CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y +CONFIG_SCHED_PROXY_EXEC=y CONFIG_NUMA_BALANCING=y CONFIG_MEMCG=y CONFIG_BLK_CGROUP=y @@ -40,11 +42,12 @@ CONFIG_PROFILING=y CONFIG_KEXEC=y CONFIG_KEXEC_FILE=y CONFIG_KEXEC_SIG=y +CONFIG_CRASH_DM_CRYPT=y CONFIG_LIVEPATCH=y CONFIG_MARCH_Z13=y CONFIG_NR_CPUS=512 CONFIG_NUMA=y -CONFIG_HZ_100=y +CONFIG_HZ_1000=y CONFIG_CERT_STORE=y CONFIG_EXPOLINE=y CONFIG_EXPOLINE_AUTO=y @@ -97,6 +100,7 @@ CONFIG_CMA_AREAS=7 CONFIG_MEM_SOFT_DIRTY=y CONFIG_DEFERRED_STRUCT_PAGE_INIT=y CONFIG_IDLE_PAGE_TRACKING=y +CONFIG_ZONE_DEVICE=y CONFIG_PERCPU_STATS=y CONFIG_ANON_VMA_NAME=y CONFIG_USERFAULTFD=y @@ -214,17 +218,19 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m CONFIG_NETFILTER_XT_TARGET_CT=m CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m CONFIG_NETFILTER_XT_TARGET_HMARK=m CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_NAT=m CONFIG_NETFILTER_XT_TARGET_NETMAP=m CONFIG_NETFILTER_XT_TARGET_NFLOG=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m CONFIG_NETFILTER_XT_TARGET_TEE=m CONFIG_NETFILTER_XT_TARGET_TPROXY=m -CONFIG_NETFILTER_XT_TARGET_TRACE=m CONFIG_NETFILTER_XT_TARGET_SECMARK=m CONFIG_NETFILTER_XT_TARGET_TCPMSS=m CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m @@ -310,16 +316,8 @@ CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_RPFILTER=m CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_TTL=m -CONFIG_IP_NF_RAW=m -CONFIG_IP_NF_SECURITY=m -CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_NFT_FIB_IPV6=m CONFIG_IP6_NF_IPTABLES=m @@ -332,15 +330,9 @@ CONFIG_IP6_NF_MATCH_IPV6HEADER=m CONFIG_IP6_NF_MATCH_MH=m CONFIG_IP6_NF_MATCH_RPFILTER=m CONFIG_IP6_NF_MATCH_RT=m -CONFIG_IP6_NF_TARGET_HL=m -CONFIG_IP6_NF_FILTER=m CONFIG_IP6_NF_TARGET_REJECT=m -CONFIG_IP6_NF_MANGLE=m -CONFIG_IP6_NF_RAW=m -CONFIG_IP6_NF_SECURITY=m -CONFIG_IP6_NF_NAT=m -CONFIG_IP6_NF_TARGET_MASQUERADE=m CONFIG_NF_TABLES_BRIDGE=m +CONFIG_IP_SCTP=m CONFIG_RDS=m CONFIG_RDS_RDMA=m CONFIG_RDS_TCP=m @@ -374,6 +366,7 @@ CONFIG_NET_SCH_FQ_CODEL=m CONFIG_NET_SCH_INGRESS=m CONFIG_NET_SCH_PLUG=m CONFIG_NET_SCH_ETS=m +CONFIG_NET_SCH_DUALPI2=m CONFIG_NET_CLS_BASIC=m CONFIG_NET_CLS_ROUTE4=m CONFIG_NET_CLS_FW=m @@ -495,6 +488,7 @@ CONFIG_DM_VDO=m CONFIG_NETDEVICES=y CONFIG_BONDING=m CONFIG_DUMMY=m +CONFIG_OVPN=m CONFIG_EQUALIZER=m CONFIG_IFB=m CONFIG_MACVLAN=m @@ -632,6 +626,7 @@ CONFIG_VP_VDPA=m CONFIG_VHOST_NET=m CONFIG_VHOST_VSOCK=m CONFIG_VHOST_VDPA=m +CONFIG_DEV_DAX=m CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y @@ -653,6 +648,7 @@ CONFIG_NILFS2_FS=m CONFIG_BCACHEFS_FS=m CONFIG_BCACHEFS_QUOTA=y CONFIG_BCACHEFS_POSIX_ACL=y +CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y CONFIG_FS_ENCRYPTION=y CONFIG_FS_VERITY=y @@ -684,7 +680,6 @@ CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_INODE64=y CONFIG_TMPFS_QUOTA=y CONFIG_HUGETLBFS=y -CONFIG_CONFIGFS_FS=m CONFIG_ECRYPT_FS=m CONFIG_CRAMFS=m CONFIG_SQUASHFS=m @@ -742,6 +737,7 @@ CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_USER=m CONFIG_CRYPTO_SELFTESTS=y +CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_BENCHMARK=m @@ -770,7 +766,6 @@ CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_MD4=m @@ -791,8 +786,6 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m -CONFIG_CRYPTO_SHA512_S390=m -CONFIG_CRYPTO_SHA1_S390=m CONFIG_CRYPTO_SHA3_256_S390=m CONFIG_CRYPTO_SHA3_512_S390=m CONFIG_CRYPTO_GHASH_S390=m @@ -806,6 +799,7 @@ CONFIG_PKEY_EP11=m CONFIG_PKEY_PCKMO=m CONFIG_PKEY_UV=m CONFIG_CRYPTO_PAES_S390=m +CONFIG_CRYPTO_PHMAC_S390=m CONFIG_CRYPTO_DEV_VIRTIO=m CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_CRYPTO_KRB5=m diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig index 8163c1702720..ed0b137353ad 100644 --- a/arch/s390/configs/zfcpdump_defconfig +++ b/arch/s390/configs/zfcpdump_defconfig @@ -1,5 +1,6 @@ CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_POSIX_AUX_CLOCKS=y CONFIG_BPF_SYSCALL=y # CONFIG_CPU_ISOLATION is not set # CONFIG_UTS_NS is not set @@ -11,7 +12,7 @@ CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_KEXEC=y CONFIG_MARCH_Z13=y CONFIG_NR_CPUS=2 -CONFIG_HZ_100=y +CONFIG_HZ_1000=y # CONFIG_CHSC_SCH is not set # CONFIG_SCM_BUS is not set # CONFIG_AP is not set diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig index e2c27588b21a..03f73fbd38b6 100644 --- a/arch/s390/crypto/Kconfig +++ b/arch/s390/crypto/Kconfig @@ -2,26 +2,6 @@ menu "Accelerated Cryptographic Algorithms for CPU (s390)" -config CRYPTO_SHA512_S390 - tristate "Hash functions: SHA-384 and SHA-512" - select CRYPTO_HASH - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: s390 - - It is available as of z10. - -config CRYPTO_SHA1_S390 - tristate "Hash functions: SHA-1" - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: s390 - - It is available as of z990. - config CRYPTO_SHA3_256_S390 tristate "Hash functions: SHA3-224 and SHA3-256" select CRYPTO_HASH diff --git a/arch/s390/crypto/Makefile b/arch/s390/crypto/Makefile index 21757d86cd49..998f4b656b18 100644 --- a/arch/s390/crypto/Makefile +++ b/arch/s390/crypto/Makefile @@ -3,8 +3,6 @@ # Cryptographic API # -obj-$(CONFIG_CRYPTO_SHA1_S390) += sha1_s390.o sha_common.o -obj-$(CONFIG_CRYPTO_SHA512_S390) += sha512_s390.o sha_common.o obj-$(CONFIG_CRYPTO_SHA3_256_S390) += sha3_256_s390.o sha_common.o obj-$(CONFIG_CRYPTO_SHA3_512_S390) += sha3_512_s390.o sha_common.o obj-$(CONFIG_CRYPTO_DES_S390) += des_s390.o @@ -13,4 +11,5 @@ obj-$(CONFIG_CRYPTO_PAES_S390) += paes_s390.o obj-$(CONFIG_S390_PRNG) += prng.o obj-$(CONFIG_CRYPTO_GHASH_S390) += ghash_s390.o obj-$(CONFIG_CRYPTO_HMAC_S390) += hmac_s390.o +obj-$(CONFIG_CRYPTO_PHMAC_S390) += phmac_s390.o obj-y += arch_random.o diff --git a/arch/s390/crypto/arch_random.c b/arch/s390/crypto/arch_random.c index a8a2407381af..083e8d5eada2 100644 --- a/arch/s390/crypto/arch_random.c +++ b/arch/s390/crypto/arch_random.c @@ -6,6 +6,7 @@ * Author(s): Harald Freudenberger */ +#include #include #include #include diff --git a/arch/s390/crypto/hmac_s390.c b/arch/s390/crypto/hmac_s390.c index 93a1098d9f8d..58444da9b004 100644 --- a/arch/s390/crypto/hmac_s390.c +++ b/arch/s390/crypto/hmac_s390.c @@ -290,6 +290,7 @@ static int s390_hmac_export(struct shash_desc *desc, void *out) struct s390_kmac_sha2_ctx *ctx = shash_desc_ctx(desc); unsigned int bs = crypto_shash_blocksize(desc->tfm); unsigned int ds = bs / 2; + u64 lo = ctx->buflen[0]; union { u8 *u8; u64 *u64; @@ -301,9 +302,10 @@ static int s390_hmac_export(struct shash_desc *desc, void *out) else memcpy(p.u8, ctx->param, ds); p.u8 += ds; - put_unaligned(ctx->buflen[0], p.u64++); + lo += bs; + put_unaligned(lo, p.u64++); if (ds == SHA512_DIGEST_SIZE) - put_unaligned(ctx->buflen[1], p.u64); + put_unaligned(ctx->buflen[1] + (lo < bs), p.u64); return err; } @@ -316,14 +318,16 @@ static int s390_hmac_import(struct shash_desc *desc, const void *in) const u8 *u8; const u64 *u64; } p = { .u8 = in }; + u64 lo; int err; err = s390_hmac_sha2_init(desc); memcpy(ctx->param, p.u8, ds); p.u8 += ds; - ctx->buflen[0] = get_unaligned(p.u64++); + lo = get_unaligned(p.u64++); + ctx->buflen[0] = lo - bs; if (ds == SHA512_DIGEST_SIZE) - ctx->buflen[1] = get_unaligned(p.u64); + ctx->buflen[1] = get_unaligned(p.u64) - (lo < bs); if (ctx->buflen[0] | ctx->buflen[1]) ctx->gr0.ikp = 1; return err; diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c index 8a340c16acb4..a624a43a2b54 100644 --- a/arch/s390/crypto/paes_s390.c +++ b/arch/s390/crypto/paes_s390.c @@ -1633,7 +1633,7 @@ static int __init paes_s390_init(void) /* with this pseudo devie alloc and start a crypto engine */ paes_crypto_engine = crypto_engine_alloc_init_and_set(paes_dev.this_device, - true, NULL, false, MAX_QLEN); + true, false, MAX_QLEN); if (!paes_crypto_engine) { rc = -ENOMEM; goto out_err; diff --git a/arch/s390/crypto/phmac_s390.c b/arch/s390/crypto/phmac_s390.c new file mode 100644 index 000000000000..7ecfdc4fba2d --- /dev/null +++ b/arch/s390/crypto/phmac_s390.c @@ -0,0 +1,1048 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright IBM Corp. 2025 + * + * s390 specific HMAC support for protected keys. + */ + +#define KMSG_COMPONENT "phmac_s390" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct crypto_engine *phmac_crypto_engine; +#define MAX_QLEN 10 + +/* + * A simple hash walk helper + */ + +struct hash_walk_helper { + struct crypto_hash_walk walk; + const u8 *walkaddr; + int walkbytes; +}; + +/* + * Prepare hash walk helper. + * Set up the base hash walk, fill walkaddr and walkbytes. + * Returns 0 on success or negative value on error. + */ +static inline int hwh_prepare(struct ahash_request *req, + struct hash_walk_helper *hwh) +{ + hwh->walkbytes = crypto_hash_walk_first(req, &hwh->walk); + if (hwh->walkbytes < 0) + return hwh->walkbytes; + hwh->walkaddr = hwh->walk.data; + return 0; +} + +/* + * Advance hash walk helper by n bytes. + * Progress the walkbytes and walkaddr fields by n bytes. + * If walkbytes is then 0, pull next hunk from hash walk + * and update walkbytes and walkaddr. + * If n is negative, unmap hash walk and return error. + * Returns 0 on success or negative value on error. + */ +static inline int hwh_advance(struct hash_walk_helper *hwh, int n) +{ + if (n < 0) + return crypto_hash_walk_done(&hwh->walk, n); + + hwh->walkbytes -= n; + hwh->walkaddr += n; + if (hwh->walkbytes > 0) + return 0; + + hwh->walkbytes = crypto_hash_walk_done(&hwh->walk, 0); + if (hwh->walkbytes < 0) + return hwh->walkbytes; + + hwh->walkaddr = hwh->walk.data; + return 0; +} + +/* + * KMAC param block layout for sha2 function codes: + * The layout of the param block for the KMAC instruction depends on the + * blocksize of the used hashing sha2-algorithm function codes. The param block + * contains the hash chaining value (cv), the input message bit-length (imbl) + * and the hmac-secret (key). To prevent code duplication, the sizes of all + * these are calculated based on the blocksize. + * + * param-block: + * +-------+ + * | cv | + * +-------+ + * | imbl | + * +-------+ + * | key | + * +-------+ + * + * sizes: + * part | sh2-alg | calculation | size | type + * -----+---------+-------------+------+-------- + * cv | 224/256 | blocksize/2 | 32 | u64[8] + * | 384/512 | | 64 | u128[8] + * imbl | 224/256 | blocksize/8 | 8 | u64 + * | 384/512 | | 16 | u128 + * key | 224/256 | blocksize | 96 | u8[96] + * | 384/512 | | 160 | u8[160] + */ + +#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE +#define MAX_IMBL_SIZE sizeof(u128) +#define MAX_BLOCK_SIZE SHA512_BLOCK_SIZE + +#define SHA2_CV_SIZE(bs) ((bs) >> 1) +#define SHA2_IMBL_SIZE(bs) ((bs) >> 3) + +#define SHA2_IMBL_OFFSET(bs) (SHA2_CV_SIZE(bs)) +#define SHA2_KEY_OFFSET(bs) (SHA2_CV_SIZE(bs) + SHA2_IMBL_SIZE(bs)) + +#define PHMAC_MAX_KEYSIZE 256 +#define PHMAC_SHA256_PK_SIZE (SHA256_BLOCK_SIZE + 32) +#define PHMAC_SHA512_PK_SIZE (SHA512_BLOCK_SIZE + 32) +#define PHMAC_MAX_PK_SIZE PHMAC_SHA512_PK_SIZE + +/* phmac protected key struct */ +struct phmac_protkey { + u32 type; + u32 len; + u8 protkey[PHMAC_MAX_PK_SIZE]; +}; + +#define PK_STATE_NO_KEY 0 +#define PK_STATE_CONVERT_IN_PROGRESS 1 +#define PK_STATE_VALID 2 + +/* phmac tfm context */ +struct phmac_tfm_ctx { + /* source key material used to derive a protected key from */ + u8 keybuf[PHMAC_MAX_KEYSIZE]; + unsigned int keylen; + + /* cpacf function code to use with this protected key type */ + long fc; + + /* nr of requests enqueued via crypto engine which use this tfm ctx */ + atomic_t via_engine_ctr; + + /* spinlock to atomic read/update all the following fields */ + spinlock_t pk_lock; + + /* see PK_STATE* defines above, < 0 holds convert failure rc */ + int pk_state; + /* if state is valid, pk holds the protected key */ + struct phmac_protkey pk; +}; + +union kmac_gr0 { + unsigned long reg; + struct { + unsigned long : 48; + unsigned long ikp : 1; + unsigned long iimp : 1; + unsigned long ccup : 1; + unsigned long : 6; + unsigned long fc : 7; + }; +}; + +struct kmac_sha2_ctx { + u8 param[MAX_DIGEST_SIZE + MAX_IMBL_SIZE + PHMAC_MAX_PK_SIZE]; + union kmac_gr0 gr0; + u8 buf[MAX_BLOCK_SIZE]; + u64 buflen[2]; +}; + +/* phmac request context */ +struct phmac_req_ctx { + struct hash_walk_helper hwh; + struct kmac_sha2_ctx kmac_ctx; + bool final; +}; + +/* + * Pkey 'token' struct used to derive a protected key value from a clear key. + */ +struct hmac_clrkey_token { + u8 type; + u8 res0[3]; + u8 version; + u8 res1[3]; + u32 keytype; + u32 len; + u8 key[]; +} __packed; + +static int hash_key(const u8 *in, unsigned int inlen, + u8 *digest, unsigned int digestsize) +{ + unsigned long func; + union { + struct sha256_paramblock { + u32 h[8]; + u64 mbl; + } sha256; + struct sha512_paramblock { + u64 h[8]; + u128 mbl; + } sha512; + } __packed param; + +#define PARAM_INIT(x, y, z) \ + param.sha##x.h[0] = SHA##y ## _H0; \ + param.sha##x.h[1] = SHA##y ## _H1; \ + param.sha##x.h[2] = SHA##y ## _H2; \ + param.sha##x.h[3] = SHA##y ## _H3; \ + param.sha##x.h[4] = SHA##y ## _H4; \ + param.sha##x.h[5] = SHA##y ## _H5; \ + param.sha##x.h[6] = SHA##y ## _H6; \ + param.sha##x.h[7] = SHA##y ## _H7; \ + param.sha##x.mbl = (z) + + switch (digestsize) { + case SHA224_DIGEST_SIZE: + func = CPACF_KLMD_SHA_256; + PARAM_INIT(256, 224, inlen * 8); + break; + case SHA256_DIGEST_SIZE: + func = CPACF_KLMD_SHA_256; + PARAM_INIT(256, 256, inlen * 8); + break; + case SHA384_DIGEST_SIZE: + func = CPACF_KLMD_SHA_512; + PARAM_INIT(512, 384, inlen * 8); + break; + case SHA512_DIGEST_SIZE: + func = CPACF_KLMD_SHA_512; + PARAM_INIT(512, 512, inlen * 8); + break; + default: + return -EINVAL; + } + +#undef PARAM_INIT + + cpacf_klmd(func, ¶m, in, inlen); + + memcpy(digest, ¶m, digestsize); + + return 0; +} + +/* + * make_clrkey_token() - wrap the clear key into a pkey clearkey token. + */ +static inline int make_clrkey_token(const u8 *clrkey, size_t clrkeylen, + unsigned int digestsize, u8 *dest) +{ + struct hmac_clrkey_token *token = (struct hmac_clrkey_token *)dest; + unsigned int blocksize; + int rc; + + token->type = 0x00; + token->version = 0x02; + switch (digestsize) { + case SHA224_DIGEST_SIZE: + case SHA256_DIGEST_SIZE: + token->keytype = PKEY_KEYTYPE_HMAC_512; + blocksize = 64; + break; + case SHA384_DIGEST_SIZE: + case SHA512_DIGEST_SIZE: + token->keytype = PKEY_KEYTYPE_HMAC_1024; + blocksize = 128; + break; + default: + return -EINVAL; + } + token->len = blocksize; + + if (clrkeylen > blocksize) { + rc = hash_key(clrkey, clrkeylen, token->key, digestsize); + if (rc) + return rc; + } else { + memcpy(token->key, clrkey, clrkeylen); + } + + return 0; +} + +/* + * phmac_tfm_ctx_setkey() - Set key value into tfm context, maybe construct + * a clear key token digestible by pkey from a clear key value. + */ +static inline int phmac_tfm_ctx_setkey(struct phmac_tfm_ctx *tfm_ctx, + const u8 *key, unsigned int keylen) +{ + if (keylen > sizeof(tfm_ctx->keybuf)) + return -EINVAL; + + memcpy(tfm_ctx->keybuf, key, keylen); + tfm_ctx->keylen = keylen; + + return 0; +} + +/* + * Convert the raw key material into a protected key via PKEY api. + * This function may sleep - don't call in non-sleeping context. + */ +static inline int convert_key(const u8 *key, unsigned int keylen, + struct phmac_protkey *pk) +{ + int rc, i; + + pk->len = sizeof(pk->protkey); + + /* + * In case of a busy card retry with increasing delay + * of 200, 400, 800 and 1600 ms - in total 3 s. + */ + for (rc = -EIO, i = 0; rc && i < 5; i++) { + if (rc == -EBUSY && msleep_interruptible((1 << i) * 100)) { + rc = -EINTR; + goto out; + } + rc = pkey_key2protkey(key, keylen, + pk->protkey, &pk->len, &pk->type, + PKEY_XFLAG_NOMEMALLOC); + } + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +/* + * (Re-)Convert the raw key material from the tfm ctx into a protected + * key via convert_key() function. Update the pk_state, pk_type, pk_len + * and the protected key in the tfm context. + * Please note this function may be invoked concurrently with the very + * same tfm context. The pk_lock spinlock in the context ensures an + * atomic update of the pk and the pk state but does not guarantee any + * order of update. So a fresh converted valid protected key may get + * updated with an 'old' expired key value. As the cpacf instructions + * detect this, refuse to operate with an invalid key and the calling + * code triggers a (re-)conversion this does no harm. This may lead to + * unnecessary additional conversion but never to invalid data on the + * hash operation. + */ +static int phmac_convert_key(struct phmac_tfm_ctx *tfm_ctx) +{ + struct phmac_protkey pk; + int rc; + + spin_lock_bh(&tfm_ctx->pk_lock); + tfm_ctx->pk_state = PK_STATE_CONVERT_IN_PROGRESS; + spin_unlock_bh(&tfm_ctx->pk_lock); + + rc = convert_key(tfm_ctx->keybuf, tfm_ctx->keylen, &pk); + + /* update context */ + spin_lock_bh(&tfm_ctx->pk_lock); + if (rc) { + tfm_ctx->pk_state = rc; + } else { + tfm_ctx->pk_state = PK_STATE_VALID; + tfm_ctx->pk = pk; + } + spin_unlock_bh(&tfm_ctx->pk_lock); + + memzero_explicit(&pk, sizeof(pk)); + pr_debug("rc=%d\n", rc); + return rc; +} + +/* + * kmac_sha2_set_imbl - sets the input message bit-length based on the blocksize + */ +static inline void kmac_sha2_set_imbl(u8 *param, u64 buflen_lo, + u64 buflen_hi, unsigned int blocksize) +{ + u8 *imbl = param + SHA2_IMBL_OFFSET(blocksize); + + switch (blocksize) { + case SHA256_BLOCK_SIZE: + *(u64 *)imbl = buflen_lo * BITS_PER_BYTE; + break; + case SHA512_BLOCK_SIZE: + *(u128 *)imbl = (((u128)buflen_hi << 64) + buflen_lo) << 3; + break; + default: + break; + } +} + +static int phmac_kmac_update(struct ahash_request *req, bool maysleep) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *ctx = &req_ctx->kmac_ctx; + struct hash_walk_helper *hwh = &req_ctx->hwh; + unsigned int bs = crypto_ahash_blocksize(tfm); + unsigned int offset, k, n; + int rc = 0; + + /* + * The walk is always mapped when this function is called. + * Note that in case of partial processing or failure the walk + * is NOT unmapped here. So a follow up task may reuse the walk + * or in case of unrecoverable failure needs to unmap it. + */ + + while (hwh->walkbytes > 0) { + /* check sha2 context buffer */ + offset = ctx->buflen[0] % bs; + if (offset + hwh->walkbytes < bs) + goto store; + + if (offset) { + /* fill ctx buffer up to blocksize and process this block */ + n = bs - offset; + memcpy(ctx->buf + offset, hwh->walkaddr, n); + ctx->gr0.iimp = 1; + for (;;) { + k = _cpacf_kmac(&ctx->gr0.reg, ctx->param, ctx->buf, bs); + if (likely(k == bs)) + break; + if (unlikely(k > 0)) { + /* + * Can't deal with hunks smaller than blocksize. + * And kmac should always return the nr of + * processed bytes as 0 or a multiple of the + * blocksize. + */ + rc = -EIO; + goto out; + } + /* protected key is invalid and needs re-conversion */ + if (!maysleep) { + rc = -EKEYEXPIRED; + goto out; + } + rc = phmac_convert_key(tfm_ctx); + if (rc) + goto out; + spin_lock_bh(&tfm_ctx->pk_lock); + memcpy(ctx->param + SHA2_KEY_OFFSET(bs), + tfm_ctx->pk.protkey, tfm_ctx->pk.len); + spin_unlock_bh(&tfm_ctx->pk_lock); + } + ctx->buflen[0] += n; + if (ctx->buflen[0] < n) + ctx->buflen[1]++; + rc = hwh_advance(hwh, n); + if (unlikely(rc)) + goto out; + offset = 0; + } + + /* process as many blocks as possible from the walk */ + while (hwh->walkbytes >= bs) { + n = (hwh->walkbytes / bs) * bs; + ctx->gr0.iimp = 1; + k = _cpacf_kmac(&ctx->gr0.reg, ctx->param, hwh->walkaddr, n); + if (likely(k > 0)) { + ctx->buflen[0] += k; + if (ctx->buflen[0] < k) + ctx->buflen[1]++; + rc = hwh_advance(hwh, k); + if (unlikely(rc)) + goto out; + } + if (unlikely(k < n)) { + /* protected key is invalid and needs re-conversion */ + if (!maysleep) { + rc = -EKEYEXPIRED; + goto out; + } + rc = phmac_convert_key(tfm_ctx); + if (rc) + goto out; + spin_lock_bh(&tfm_ctx->pk_lock); + memcpy(ctx->param + SHA2_KEY_OFFSET(bs), + tfm_ctx->pk.protkey, tfm_ctx->pk.len); + spin_unlock_bh(&tfm_ctx->pk_lock); + } + } + +store: + /* store incomplete block in context buffer */ + if (hwh->walkbytes) { + memcpy(ctx->buf + offset, hwh->walkaddr, hwh->walkbytes); + ctx->buflen[0] += hwh->walkbytes; + if (ctx->buflen[0] < hwh->walkbytes) + ctx->buflen[1]++; + rc = hwh_advance(hwh, hwh->walkbytes); + if (unlikely(rc)) + goto out; + } + + } /* end of while (hwh->walkbytes > 0) */ + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_kmac_final(struct ahash_request *req, bool maysleep) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *ctx = &req_ctx->kmac_ctx; + unsigned int ds = crypto_ahash_digestsize(tfm); + unsigned int bs = crypto_ahash_blocksize(tfm); + unsigned int k, n; + int rc = 0; + + n = ctx->buflen[0] % bs; + ctx->gr0.iimp = 0; + kmac_sha2_set_imbl(ctx->param, ctx->buflen[0], ctx->buflen[1], bs); + for (;;) { + k = _cpacf_kmac(&ctx->gr0.reg, ctx->param, ctx->buf, n); + if (likely(k == n)) + break; + if (unlikely(k > 0)) { + /* Can't deal with hunks smaller than blocksize. */ + rc = -EIO; + goto out; + } + /* protected key is invalid and needs re-conversion */ + if (!maysleep) { + rc = -EKEYEXPIRED; + goto out; + } + rc = phmac_convert_key(tfm_ctx); + if (rc) + goto out; + spin_lock_bh(&tfm_ctx->pk_lock); + memcpy(ctx->param + SHA2_KEY_OFFSET(bs), + tfm_ctx->pk.protkey, tfm_ctx->pk.len); + spin_unlock_bh(&tfm_ctx->pk_lock); + } + + memcpy(req->result, ctx->param, ds); + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + unsigned int bs = crypto_ahash_blocksize(tfm); + int rc = 0; + + /* zero request context (includes the kmac sha2 context) */ + memset(req_ctx, 0, sizeof(*req_ctx)); + + /* + * setkey() should have set a valid fc into the tfm context. + * Copy this function code into the gr0 field of the kmac context. + */ + if (!tfm_ctx->fc) { + rc = -ENOKEY; + goto out; + } + kmac_ctx->gr0.fc = tfm_ctx->fc; + + /* + * Copy the pk from tfm ctx into kmac ctx. The protected key + * may be outdated but update() and final() will handle this. + */ + spin_lock_bh(&tfm_ctx->pk_lock); + memcpy(kmac_ctx->param + SHA2_KEY_OFFSET(bs), + tfm_ctx->pk.protkey, tfm_ctx->pk.len); + spin_unlock_bh(&tfm_ctx->pk_lock); + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_update(struct ahash_request *req) +{ + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + struct hash_walk_helper *hwh = &req_ctx->hwh; + int rc; + + /* prep the walk in the request context */ + rc = hwh_prepare(req, hwh); + if (rc) + goto out; + + /* Try synchronous operation if no active engine usage */ + if (!atomic_read(&tfm_ctx->via_engine_ctr)) { + rc = phmac_kmac_update(req, false); + if (rc == 0) + goto out; + } + + /* + * If sync operation failed or key expired or there are already + * requests enqueued via engine, fallback to async. Mark tfm as + * using engine to serialize requests. + */ + if (rc == 0 || rc == -EKEYEXPIRED) { + atomic_inc(&tfm_ctx->via_engine_ctr); + rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); + if (rc != -EINPROGRESS) + atomic_dec(&tfm_ctx->via_engine_ctr); + } + + if (rc != -EINPROGRESS) { + hwh_advance(hwh, rc); + memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); + } + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_final(struct ahash_request *req) +{ + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + int rc = 0; + + /* Try synchronous operation if no active engine usage */ + if (!atomic_read(&tfm_ctx->via_engine_ctr)) { + rc = phmac_kmac_final(req, false); + if (rc == 0) + goto out; + } + + /* + * If sync operation failed or key expired or there are already + * requests enqueued via engine, fallback to async. Mark tfm as + * using engine to serialize requests. + */ + if (rc == 0 || rc == -EKEYEXPIRED) { + req->nbytes = 0; + req_ctx->final = true; + atomic_inc(&tfm_ctx->via_engine_ctr); + rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); + if (rc != -EINPROGRESS) + atomic_dec(&tfm_ctx->via_engine_ctr); + } + +out: + if (rc != -EINPROGRESS) + memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_finup(struct ahash_request *req) +{ + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + struct hash_walk_helper *hwh = &req_ctx->hwh; + int rc; + + /* prep the walk in the request context */ + rc = hwh_prepare(req, hwh); + if (rc) + goto out; + + /* Try synchronous operations if no active engine usage */ + if (!atomic_read(&tfm_ctx->via_engine_ctr)) { + rc = phmac_kmac_update(req, false); + if (rc == 0) + req->nbytes = 0; + } + if (!rc && !req->nbytes && !atomic_read(&tfm_ctx->via_engine_ctr)) { + rc = phmac_kmac_final(req, false); + if (rc == 0) + goto out; + } + + /* + * If sync operation failed or key expired or there are already + * requests enqueued via engine, fallback to async. Mark tfm as + * using engine to serialize requests. + */ + if (rc == 0 || rc == -EKEYEXPIRED) { + req_ctx->final = true; + atomic_inc(&tfm_ctx->via_engine_ctr); + rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); + if (rc != -EINPROGRESS) + atomic_dec(&tfm_ctx->via_engine_ctr); + } + + if (rc != -EINPROGRESS) + hwh_advance(hwh, rc); + +out: + if (rc != -EINPROGRESS) + memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_digest(struct ahash_request *req) +{ + int rc; + + rc = phmac_init(req); + if (rc) + goto out; + + rc = phmac_finup(req); + +out: + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + unsigned int ds = crypto_ahash_digestsize(tfm); + unsigned int bs = crypto_ahash_blocksize(tfm); + unsigned int tmpkeylen; + u8 *tmpkey = NULL; + int rc = 0; + + if (!crypto_ahash_tested(tfm)) { + /* + * selftest running: key is a raw hmac clear key and needs + * to get embedded into a 'clear key token' in order to have + * it correctly processed by the pkey module. + */ + tmpkeylen = sizeof(struct hmac_clrkey_token) + bs; + tmpkey = kzalloc(tmpkeylen, GFP_KERNEL); + if (!tmpkey) { + rc = -ENOMEM; + goto out; + } + rc = make_clrkey_token(key, keylen, ds, tmpkey); + if (rc) + goto out; + keylen = tmpkeylen; + key = tmpkey; + } + + /* copy raw key into tfm context */ + rc = phmac_tfm_ctx_setkey(tfm_ctx, key, keylen); + if (rc) + goto out; + + /* convert raw key into protected key */ + rc = phmac_convert_key(tfm_ctx); + if (rc) + goto out; + + /* set function code in tfm context, check for valid pk type */ + switch (ds) { + case SHA224_DIGEST_SIZE: + if (tfm_ctx->pk.type != PKEY_KEYTYPE_HMAC_512) + rc = -EINVAL; + else + tfm_ctx->fc = CPACF_KMAC_PHMAC_SHA_224; + break; + case SHA256_DIGEST_SIZE: + if (tfm_ctx->pk.type != PKEY_KEYTYPE_HMAC_512) + rc = -EINVAL; + else + tfm_ctx->fc = CPACF_KMAC_PHMAC_SHA_256; + break; + case SHA384_DIGEST_SIZE: + if (tfm_ctx->pk.type != PKEY_KEYTYPE_HMAC_1024) + rc = -EINVAL; + else + tfm_ctx->fc = CPACF_KMAC_PHMAC_SHA_384; + break; + case SHA512_DIGEST_SIZE: + if (tfm_ctx->pk.type != PKEY_KEYTYPE_HMAC_1024) + rc = -EINVAL; + else + tfm_ctx->fc = CPACF_KMAC_PHMAC_SHA_512; + break; + default: + tfm_ctx->fc = 0; + rc = -EINVAL; + } + +out: + kfree(tmpkey); + pr_debug("rc=%d\n", rc); + return rc; +} + +static int phmac_export(struct ahash_request *req, void *out) +{ + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *ctx = &req_ctx->kmac_ctx; + + memcpy(out, ctx, sizeof(*ctx)); + + return 0; +} + +static int phmac_import(struct ahash_request *req, const void *in) +{ + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *ctx = &req_ctx->kmac_ctx; + + memset(req_ctx, 0, sizeof(*req_ctx)); + memcpy(ctx, in, sizeof(*ctx)); + + return 0; +} + +static int phmac_init_tfm(struct crypto_ahash *tfm) +{ + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + + memset(tfm_ctx, 0, sizeof(*tfm_ctx)); + spin_lock_init(&tfm_ctx->pk_lock); + + crypto_ahash_set_reqsize(tfm, sizeof(struct phmac_req_ctx)); + + return 0; +} + +static void phmac_exit_tfm(struct crypto_ahash *tfm) +{ + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + + memzero_explicit(tfm_ctx->keybuf, sizeof(tfm_ctx->keybuf)); + memzero_explicit(&tfm_ctx->pk, sizeof(tfm_ctx->pk)); +} + +static int phmac_do_one_request(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = ahash_request_cast(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); + struct phmac_req_ctx *req_ctx = ahash_request_ctx(req); + struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + struct hash_walk_helper *hwh = &req_ctx->hwh; + int rc = -EINVAL; + + /* + * Three kinds of requests come in here: + * update when req->nbytes > 0 and req_ctx->final is false + * final when req->nbytes = 0 and req_ctx->final is true + * finup when req->nbytes > 0 and req_ctx->final is true + * For update and finup the hwh walk needs to be prepared and + * up to date but the actual nr of bytes in req->nbytes may be + * any non zero number. For final there is no hwh walk needed. + */ + + if (req->nbytes) { + rc = phmac_kmac_update(req, true); + if (rc == -EKEYEXPIRED) { + /* + * Protected key expired, conversion is in process. + * Trigger a re-schedule of this request by returning + * -ENOSPC ("hardware queue full") to the crypto engine. + * To avoid immediately re-invocation of this callback, + * tell scheduler to voluntarily give up the CPU here. + */ + pr_debug("rescheduling request\n"); + cond_resched(); + return -ENOSPC; + } else if (rc) { + hwh_advance(hwh, rc); + goto out; + } + req->nbytes = 0; + } + + if (req_ctx->final) { + rc = phmac_kmac_final(req, true); + if (rc == -EKEYEXPIRED) { + /* + * Protected key expired, conversion is in process. + * Trigger a re-schedule of this request by returning + * -ENOSPC ("hardware queue full") to the crypto engine. + * To avoid immediately re-invocation of this callback, + * tell scheduler to voluntarily give up the CPU here. + */ + pr_debug("rescheduling request\n"); + cond_resched(); + return -ENOSPC; + } + } + +out: + if (rc || req_ctx->final) + memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); + pr_debug("request complete with rc=%d\n", rc); + local_bh_disable(); + atomic_dec(&tfm_ctx->via_engine_ctr); + crypto_finalize_hash_request(engine, req, rc); + local_bh_enable(); + return rc; +} + +#define S390_ASYNC_PHMAC_ALG(x) \ +{ \ + .base = { \ + .init = phmac_init, \ + .update = phmac_update, \ + .final = phmac_final, \ + .finup = phmac_finup, \ + .digest = phmac_digest, \ + .setkey = phmac_setkey, \ + .import = phmac_import, \ + .export = phmac_export, \ + .init_tfm = phmac_init_tfm, \ + .exit_tfm = phmac_exit_tfm, \ + .halg = { \ + .digestsize = SHA##x##_DIGEST_SIZE, \ + .statesize = sizeof(struct kmac_sha2_ctx), \ + .base = { \ + .cra_name = "phmac(sha" #x ")", \ + .cra_driver_name = "phmac_s390_sha" #x, \ + .cra_blocksize = SHA##x##_BLOCK_SIZE, \ + .cra_priority = 400, \ + .cra_flags = CRYPTO_ALG_ASYNC | \ + CRYPTO_ALG_NO_FALLBACK, \ + .cra_ctxsize = sizeof(struct phmac_tfm_ctx), \ + .cra_module = THIS_MODULE, \ + }, \ + }, \ + }, \ + .op = { \ + .do_one_request = phmac_do_one_request, \ + }, \ +} + +static struct phmac_alg { + unsigned int fc; + struct ahash_engine_alg alg; + bool registered; +} phmac_algs[] = { + { + .fc = CPACF_KMAC_PHMAC_SHA_224, + .alg = S390_ASYNC_PHMAC_ALG(224), + }, { + .fc = CPACF_KMAC_PHMAC_SHA_256, + .alg = S390_ASYNC_PHMAC_ALG(256), + }, { + .fc = CPACF_KMAC_PHMAC_SHA_384, + .alg = S390_ASYNC_PHMAC_ALG(384), + }, { + .fc = CPACF_KMAC_PHMAC_SHA_512, + .alg = S390_ASYNC_PHMAC_ALG(512), + } +}; + +static struct miscdevice phmac_dev = { + .name = "phmac", + .minor = MISC_DYNAMIC_MINOR, +}; + +static void s390_phmac_exit(void) +{ + struct phmac_alg *phmac; + int i; + + if (phmac_crypto_engine) { + crypto_engine_stop(phmac_crypto_engine); + crypto_engine_exit(phmac_crypto_engine); + } + + for (i = ARRAY_SIZE(phmac_algs) - 1; i >= 0; i--) { + phmac = &phmac_algs[i]; + if (phmac->registered) + crypto_engine_unregister_ahash(&phmac->alg); + } + + misc_deregister(&phmac_dev); +} + +static int __init s390_phmac_init(void) +{ + struct phmac_alg *phmac; + int i, rc; + + /* for selftest cpacf klmd subfunction is needed */ + if (!cpacf_query_func(CPACF_KLMD, CPACF_KLMD_SHA_256)) + return -ENODEV; + if (!cpacf_query_func(CPACF_KLMD, CPACF_KLMD_SHA_512)) + return -ENODEV; + + /* register a simple phmac pseudo misc device */ + rc = misc_register(&phmac_dev); + if (rc) + return rc; + + /* with this pseudo device alloc and start a crypto engine */ + phmac_crypto_engine = + crypto_engine_alloc_init_and_set(phmac_dev.this_device, + true, false, MAX_QLEN); + if (!phmac_crypto_engine) { + rc = -ENOMEM; + goto out_err; + } + rc = crypto_engine_start(phmac_crypto_engine); + if (rc) { + crypto_engine_exit(phmac_crypto_engine); + phmac_crypto_engine = NULL; + goto out_err; + } + + for (i = 0; i < ARRAY_SIZE(phmac_algs); i++) { + phmac = &phmac_algs[i]; + if (!cpacf_query_func(CPACF_KMAC, phmac->fc)) + continue; + rc = crypto_engine_register_ahash(&phmac->alg); + if (rc) + goto out_err; + phmac->registered = true; + pr_debug("%s registered\n", phmac->alg.base.halg.base.cra_name); + } + + return 0; + +out_err: + s390_phmac_exit(); + return rc; +} + +module_init(s390_phmac_init); +module_exit(s390_phmac_exit); + +MODULE_ALIAS_CRYPTO("phmac(sha224)"); +MODULE_ALIAS_CRYPTO("phmac(sha256)"); +MODULE_ALIAS_CRYPTO("phmac(sha384)"); +MODULE_ALIAS_CRYPTO("phmac(sha512)"); + +MODULE_DESCRIPTION("S390 HMAC driver for protected keys"); +MODULE_LICENSE("GPL"); diff --git a/arch/s390/crypto/sha.h b/arch/s390/crypto/sha.h index d757ccbce2b4..cadb4b13622a 100644 --- a/arch/s390/crypto/sha.h +++ b/arch/s390/crypto/sha.h @@ -27,6 +27,9 @@ struct s390_sha_ctx { u64 state[SHA512_DIGEST_SIZE / sizeof(u64)]; u64 count_hi; } sha512; + struct { + __le64 state[SHA3_STATE_SIZE / sizeof(u64)]; + } sha3; }; int func; /* KIMD function to use */ bool first_message_part; diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c deleted file mode 100644 index d229cbd2ba22..000000000000 --- a/arch/s390/crypto/sha1_s390.c +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Cryptographic API. - * - * s390 implementation of the SHA1 Secure Hash Algorithm. - * - * Derived from cryptoapi implementation, adapted for in-place - * scatterlist interface. Originally based on the public domain - * implementation written by Steve Reid. - * - * s390 Version: - * Copyright IBM Corp. 2003, 2007 - * Author(s): Thomas Spatzier - * Jan Glauber (jan.glauber@de.ibm.com) - * - * Derived from "crypto/sha1_generic.c" - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - */ -#include -#include -#include -#include -#include -#include - -#include "sha.h" - -static int s390_sha1_init(struct shash_desc *desc) -{ - struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - - sctx->state[0] = SHA1_H0; - sctx->state[1] = SHA1_H1; - sctx->state[2] = SHA1_H2; - sctx->state[3] = SHA1_H3; - sctx->state[4] = SHA1_H4; - sctx->count = 0; - sctx->func = CPACF_KIMD_SHA_1; - - return 0; -} - -static int s390_sha1_export(struct shash_desc *desc, void *out) -{ - struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - struct sha1_state *octx = out; - - octx->count = sctx->count; - memcpy(octx->state, sctx->state, sizeof(octx->state)); - return 0; -} - -static int s390_sha1_import(struct shash_desc *desc, const void *in) -{ - struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - const struct sha1_state *ictx = in; - - sctx->count = ictx->count; - memcpy(sctx->state, ictx->state, sizeof(ictx->state)); - sctx->func = CPACF_KIMD_SHA_1; - return 0; -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = s390_sha1_init, - .update = s390_sha_update_blocks, - .finup = s390_sha_finup, - .export = s390_sha1_export, - .import = s390_sha1_import, - .descsize = S390_SHA_CTX_SIZE, - .statesize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-s390", - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_s390_init(void) -{ - if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_1)) - return -ENODEV; - return crypto_register_shash(&alg); -} - -static void __exit sha1_s390_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_cpu_feature_match(S390_CPU_FEATURE_MSA, sha1_s390_init); -module_exit(sha1_s390_fini); - -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm"); diff --git a/arch/s390/crypto/sha3_256_s390.c b/arch/s390/crypto/sha3_256_s390.c index 4a7731ac6bcd..03bb4f4bab70 100644 --- a/arch/s390/crypto/sha3_256_s390.c +++ b/arch/s390/crypto/sha3_256_s390.c @@ -35,23 +35,33 @@ static int sha3_256_init(struct shash_desc *desc) static int sha3_256_export(struct shash_desc *desc, void *out) { struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - struct sha3_state *octx = out; + union { + u8 *u8; + u64 *u64; + } p = { .u8 = out }; + int i; if (sctx->first_message_part) { - memset(sctx->state, 0, sizeof(sctx->state)); - sctx->first_message_part = 0; + memset(out, 0, SHA3_STATE_SIZE); + return 0; } - memcpy(octx->st, sctx->state, sizeof(octx->st)); + for (i = 0; i < SHA3_STATE_SIZE / 8; i++) + put_unaligned(le64_to_cpu(sctx->sha3.state[i]), p.u64++); return 0; } static int sha3_256_import(struct shash_desc *desc, const void *in) { struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - const struct sha3_state *ictx = in; + union { + const u8 *u8; + const u64 *u64; + } p = { .u8 = in }; + int i; + for (i = 0; i < SHA3_STATE_SIZE / 8; i++) + sctx->sha3.state[i] = cpu_to_le64(get_unaligned(p.u64++)); sctx->count = 0; - memcpy(sctx->state, ictx->st, sizeof(ictx->st)); sctx->first_message_part = 0; sctx->func = CPACF_KIMD_SHA3_256; diff --git a/arch/s390/crypto/sha3_512_s390.c b/arch/s390/crypto/sha3_512_s390.c index 018f02fff444..a5c9690eecb1 100644 --- a/arch/s390/crypto/sha3_512_s390.c +++ b/arch/s390/crypto/sha3_512_s390.c @@ -34,24 +34,33 @@ static int sha3_512_init(struct shash_desc *desc) static int sha3_512_export(struct shash_desc *desc, void *out) { struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - struct sha3_state *octx = out; - + union { + u8 *u8; + u64 *u64; + } p = { .u8 = out }; + int i; if (sctx->first_message_part) { - memset(sctx->state, 0, sizeof(sctx->state)); - sctx->first_message_part = 0; + memset(out, 0, SHA3_STATE_SIZE); + return 0; } - memcpy(octx->st, sctx->state, sizeof(octx->st)); + for (i = 0; i < SHA3_STATE_SIZE / 8; i++) + put_unaligned(le64_to_cpu(sctx->sha3.state[i]), p.u64++); return 0; } static int sha3_512_import(struct shash_desc *desc, const void *in) { struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - const struct sha3_state *ictx = in; + union { + const u8 *u8; + const u64 *u64; + } p = { .u8 = in }; + int i; + for (i = 0; i < SHA3_STATE_SIZE / 8; i++) + sctx->sha3.state[i] = cpu_to_le64(get_unaligned(p.u64++)); sctx->count = 0; - memcpy(sctx->state, ictx->st, sizeof(ictx->st)); sctx->first_message_part = 0; sctx->func = CPACF_KIMD_SHA3_512; diff --git a/arch/s390/crypto/sha512_s390.c b/arch/s390/crypto/sha512_s390.c deleted file mode 100644 index 33711a29618c..000000000000 --- a/arch/s390/crypto/sha512_s390.c +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Cryptographic API. - * - * s390 implementation of the SHA512 and SHA38 Secure Hash Algorithm. - * - * Copyright IBM Corp. 2007 - * Author(s): Jan Glauber (jang@de.ibm.com) - */ -#include -#include -#include -#include -#include -#include -#include - -#include "sha.h" - -static int sha512_init(struct shash_desc *desc) -{ - struct s390_sha_ctx *ctx = shash_desc_ctx(desc); - - ctx->sha512.state[0] = SHA512_H0; - ctx->sha512.state[1] = SHA512_H1; - ctx->sha512.state[2] = SHA512_H2; - ctx->sha512.state[3] = SHA512_H3; - ctx->sha512.state[4] = SHA512_H4; - ctx->sha512.state[5] = SHA512_H5; - ctx->sha512.state[6] = SHA512_H6; - ctx->sha512.state[7] = SHA512_H7; - ctx->count = 0; - ctx->sha512.count_hi = 0; - ctx->func = CPACF_KIMD_SHA_512; - - return 0; -} - -static int sha512_export(struct shash_desc *desc, void *out) -{ - struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - struct sha512_state *octx = out; - - octx->count[0] = sctx->count; - octx->count[1] = sctx->sha512.count_hi; - memcpy(octx->state, sctx->state, sizeof(octx->state)); - return 0; -} - -static int sha512_import(struct shash_desc *desc, const void *in) -{ - struct s390_sha_ctx *sctx = shash_desc_ctx(desc); - const struct sha512_state *ictx = in; - - sctx->count = ictx->count[0]; - sctx->sha512.count_hi = ictx->count[1]; - - memcpy(sctx->state, ictx->state, sizeof(ictx->state)); - sctx->func = CPACF_KIMD_SHA_512; - return 0; -} - -static struct shash_alg sha512_alg = { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_init, - .update = s390_sha_update_blocks, - .finup = s390_sha_finup, - .export = sha512_export, - .import = sha512_import, - .descsize = sizeof(struct s390_sha_ctx), - .statesize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name= "sha512-s390", - .cra_priority = 300, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -MODULE_ALIAS_CRYPTO("sha512"); - -static int sha384_init(struct shash_desc *desc) -{ - struct s390_sha_ctx *ctx = shash_desc_ctx(desc); - - ctx->sha512.state[0] = SHA384_H0; - ctx->sha512.state[1] = SHA384_H1; - ctx->sha512.state[2] = SHA384_H2; - ctx->sha512.state[3] = SHA384_H3; - ctx->sha512.state[4] = SHA384_H4; - ctx->sha512.state[5] = SHA384_H5; - ctx->sha512.state[6] = SHA384_H6; - ctx->sha512.state[7] = SHA384_H7; - ctx->count = 0; - ctx->sha512.count_hi = 0; - ctx->func = CPACF_KIMD_SHA_512; - - return 0; -} - -static struct shash_alg sha384_alg = { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_init, - .update = s390_sha_update_blocks, - .finup = s390_sha_finup, - .export = sha512_export, - .import = sha512_import, - .descsize = sizeof(struct s390_sha_ctx), - .statesize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name= "sha384-s390", - .cra_priority = 300, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_ctxsize = sizeof(struct s390_sha_ctx), - .cra_module = THIS_MODULE, - } -}; - -MODULE_ALIAS_CRYPTO("sha384"); - -static int __init init(void) -{ - int ret; - - if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_512)) - return -ENODEV; - if ((ret = crypto_register_shash(&sha512_alg)) < 0) - goto out; - if ((ret = crypto_register_shash(&sha384_alg)) < 0) - crypto_unregister_shash(&sha512_alg); -out: - return ret; -} - -static void __exit fini(void) -{ - crypto_unregister_shash(&sha512_alg); - crypto_unregister_shash(&sha384_alg); -} - -module_cpu_feature_match(S390_CPU_FEATURE_MSA, init); -module_exit(fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA512 and SHA-384 Secure Hash Algorithm"); diff --git a/arch/s390/crypto/sha_common.c b/arch/s390/crypto/sha_common.c index b5e2c365ea05..d6f839618794 100644 --- a/arch/s390/crypto/sha_common.c +++ b/arch/s390/crypto/sha_common.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include "sha.h" diff --git a/arch/s390/hypfs/hypfs.h b/arch/s390/hypfs/hypfs.h index 83ebf54cca6b..4dc2e068e0ff 100644 --- a/arch/s390/hypfs/hypfs.h +++ b/arch/s390/hypfs/hypfs.h @@ -48,7 +48,7 @@ void hypfs_sprp_exit(void); int __hypfs_fs_init(void); -static inline int hypfs_fs_init(void) +static __always_inline int hypfs_fs_init(void) { if (IS_ENABLED(CONFIG_S390_HYPFS_FS)) return __hypfs_fs_init(); diff --git a/arch/s390/hypfs/hypfs_dbfs.c b/arch/s390/hypfs/hypfs_dbfs.c index 5d9effb0867c..41a0d2066fa0 100644 --- a/arch/s390/hypfs/hypfs_dbfs.c +++ b/arch/s390/hypfs/hypfs_dbfs.c @@ -6,6 +6,7 @@ * Author(s): Michael Holzheu */ +#include #include #include "hypfs.h" @@ -66,23 +67,27 @@ static long dbfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long rc; mutex_lock(&df->lock); - if (df->unlocked_ioctl) - rc = df->unlocked_ioctl(file, cmd, arg); - else - rc = -ENOTTY; + rc = df->unlocked_ioctl(file, cmd, arg); mutex_unlock(&df->lock); return rc; } -static const struct file_operations dbfs_ops = { +static const struct file_operations dbfs_ops_ioctl = { .read = dbfs_read, .unlocked_ioctl = dbfs_ioctl, }; +static const struct file_operations dbfs_ops = { + .read = dbfs_read, +}; + void hypfs_dbfs_create_file(struct hypfs_dbfs_file *df) { - df->dentry = debugfs_create_file(df->name, 0400, dbfs_dir, df, - &dbfs_ops); + const struct file_operations *fops = &dbfs_ops; + + if (df->unlocked_ioctl && !security_locked_down(LOCKDOWN_DEBUGFS)) + fops = &dbfs_ops_ioctl; + df->dentry = debugfs_create_file(df->name, 0400, dbfs_dir, df, fops); mutex_init(&df->lock); } diff --git a/arch/s390/hypfs/hypfs_diag.h b/arch/s390/hypfs/hypfs_diag.h index 7090eff27fef..b5218135b8fe 100644 --- a/arch/s390/hypfs/hypfs_diag.h +++ b/arch/s390/hypfs/hypfs_diag.h @@ -19,7 +19,7 @@ int diag204_store(void *buf, int pages); int __hypfs_diag_fs_init(void); void __hypfs_diag_fs_exit(void); -static inline int hypfs_diag_fs_init(void) +static __always_inline int hypfs_diag_fs_init(void) { if (IS_ENABLED(CONFIG_S390_HYPFS_FS)) return __hypfs_diag_fs_init(); diff --git a/arch/s390/include/asm/alternative.h b/arch/s390/include/asm/alternative.h index c7bf60a541e9..1c56480def9e 100644 --- a/arch/s390/include/asm/alternative.h +++ b/arch/s390/include/asm/alternative.h @@ -51,7 +51,7 @@ ALT_TYPE_SPEC << ALT_TYPE_SHIFT | \ (facility) << ALT_DATA_SHIFT) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -183,7 +183,7 @@ static inline void apply_alternatives(struct alt_instr *start, struct alt_instr /* Use this macro if clobbers are needed without inputs. */ #define ASM_NO_INPUT_CLOBBER(clobber...) : clobber -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ /* * Issue one struct alt_instr descriptor entry (need to put it into @@ -233,6 +233,6 @@ static inline void apply_alternatives(struct alt_instr *start, struct alt_instr .popsection .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_ALTERNATIVE_H */ diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index 395b02d6a133..352108727d7e 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -103,7 +103,7 @@ struct ap_tapq_hwinfo { unsigned int accel : 1; /* A */ unsigned int ep11 : 1; /* X */ unsigned int apxa : 1; /* APXA */ - unsigned int : 1; + unsigned int slcf : 1; /* Cmd filtering avail. */ unsigned int class : 8; unsigned int bs : 2; /* SE bind/assoc */ unsigned int : 14; diff --git a/arch/s390/include/asm/asm-const.h b/arch/s390/include/asm/asm-const.h index 11f615eb0066..1cfffad9eea0 100644 --- a/arch/s390/include/asm/asm-const.h +++ b/arch/s390/include/asm/asm-const.h @@ -2,7 +2,7 @@ #ifndef _ASM_S390_ASM_CONST_H #define _ASM_S390_ASM_CONST_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ # define stringify_in_c(...) __VA_ARGS__ #else /* This version of stringify will deal with commas... */ diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h index 54cb97603ec0..4bc5317fbb12 100644 --- a/arch/s390/include/asm/cpacf.h +++ b/arch/s390/include/asm/cpacf.h @@ -129,6 +129,10 @@ #define CPACF_KMAC_HMAC_SHA_256 0x71 #define CPACF_KMAC_HMAC_SHA_384 0x72 #define CPACF_KMAC_HMAC_SHA_512 0x73 +#define CPACF_KMAC_PHMAC_SHA_224 0x78 +#define CPACF_KMAC_PHMAC_SHA_256 0x79 +#define CPACF_KMAC_PHMAC_SHA_384 0x7a +#define CPACF_KMAC_PHMAC_SHA_512 0x7b /* * Function codes for the PCKMO (PERFORM CRYPTOGRAPHIC KEY MANAGEMENT) diff --git a/arch/s390/include/asm/cpu.h b/arch/s390/include/asm/cpu.h index 26c710cd3485..5672e3fab52b 100644 --- a/arch/s390/include/asm/cpu.h +++ b/arch/s390/include/asm/cpu.h @@ -9,7 +9,7 @@ #ifndef _ASM_S390_CPU_H #define _ASM_S390_CPU_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -24,5 +24,5 @@ struct cpuid DECLARE_STATIC_KEY_FALSE(cpu_has_bear); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_CPU_H */ diff --git a/arch/s390/include/asm/cpu_mf-insn.h b/arch/s390/include/asm/cpu_mf-insn.h index a68b362e0964..941663939cc7 100644 --- a/arch/s390/include/asm/cpu_mf-insn.h +++ b/arch/s390/include/asm/cpu_mf-insn.h @@ -8,7 +8,7 @@ #ifndef _ASM_S390_CPU_MF_INSN_H #define _ASM_S390_CPU_MF_INSN_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ /* Macro to generate the STCCTM instruction with a customized * M3 field designating the counter set. @@ -17,6 +17,6 @@ .insn rsy,0xeb0000000017,\r1,\m3 & 0xf,\db2 .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/s390/include/asm/ctlreg.h b/arch/s390/include/asm/ctlreg.h index e6527f51ad0b..e93cc240a1ed 100644 --- a/arch/s390/include/asm/ctlreg.h +++ b/arch/s390/include/asm/ctlreg.h @@ -80,7 +80,7 @@ #define CR14_EXTERNAL_DAMAGE_SUBMASK BIT(CR14_EXTERNAL_DAMAGE_SUBMASK_BIT) #define CR14_WARNING_SUBMASK BIT(CR14_WARNING_SUBMASK_BIT) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -252,5 +252,5 @@ union ctlreg15 { }; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_S390_CTLREG_H */ diff --git a/arch/s390/include/asm/dwarf.h b/arch/s390/include/asm/dwarf.h index 390906b8e386..e3ad6798d0cd 100644 --- a/arch/s390/include/asm/dwarf.h +++ b/arch/s390/include/asm/dwarf.h @@ -2,7 +2,7 @@ #ifndef _ASM_S390_DWARF_H #define _ASM_S390_DWARF_H -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define CFI_STARTPROC .cfi_startproc #define CFI_ENDPROC .cfi_endproc @@ -33,6 +33,6 @@ .cfi_sections .eh_frame, .debug_frame #endif -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_DWARF_H */ diff --git a/arch/s390/include/asm/entry-common.h b/arch/s390/include/asm/entry-common.h index 35555c944630..979af986a8fe 100644 --- a/arch/s390/include/asm/entry-common.h +++ b/arch/s390/include/asm/entry-common.h @@ -59,4 +59,14 @@ static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, #define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare +static __always_inline bool arch_in_rcu_eqs(void) +{ + if (IS_ENABLED(CONFIG_KVM)) + return current->flags & PF_VCPU; + + return false; +} + +#define arch_in_rcu_eqs arch_in_rcu_eqs + #endif diff --git a/arch/s390/include/asm/extmem.h b/arch/s390/include/asm/extmem.h index e0a06060afdd..225ee89c3f5e 100644 --- a/arch/s390/include/asm/extmem.h +++ b/arch/s390/include/asm/extmem.h @@ -6,7 +6,7 @@ #ifndef _ASM_S390X_DCSS_H #define _ASM_S390X_DCSS_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * DCSS segment is defined as a contiguous range of pages using DEFSEG command. diff --git a/arch/s390/include/asm/fpu-insn-asm.h b/arch/s390/include/asm/fpu-insn-asm.h index d296322be4bc..cc0468fdf2d0 100644 --- a/arch/s390/include/asm/fpu-insn-asm.h +++ b/arch/s390/include/asm/fpu-insn-asm.h @@ -16,7 +16,7 @@ #error only can be included directly #endif -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ /* Macros to generate vector instruction byte code */ @@ -750,5 +750,5 @@ MRXBOPC 0, 0x77, v1, v2, v3 .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_S390_FPU_INSN_ASM_H */ diff --git a/arch/s390/include/asm/fpu-insn.h b/arch/s390/include/asm/fpu-insn.h index f668bffd6dd3..135bb89c0a89 100644 --- a/arch/s390/include/asm/fpu-insn.h +++ b/arch/s390/include/asm/fpu-insn.h @@ -9,7 +9,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -475,5 +475,5 @@ static __always_inline void fpu_vzero(u8 v) : "memory"); } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_S390_FPU_INSN_H */ diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h index 185331e91f83..bee2d16c2951 100644 --- a/arch/s390/include/asm/ftrace.h +++ b/arch/s390/include/asm/ftrace.h @@ -5,7 +5,7 @@ #define ARCH_SUPPORTS_FTRACE_OPS 1 #define MCOUNT_INSN_SIZE 6 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include static __always_inline unsigned long return_address(unsigned int n) @@ -134,7 +134,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct ftrace_regs *fregs); #define ftrace_graph_func ftrace_graph_func -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #ifdef CONFIG_FUNCTION_TRACER diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index bde6a496df5f..697497e7d13e 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -25,7 +25,7 @@ #define EXT_IRQ_CP_SERVICE 0x2603 #define EXT_IRQ_IUCV 0x4000 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -120,6 +120,6 @@ void irq_subclass_unregister(enum irq_subclass subclass); #define irq_canonicalize(irq) (irq) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_IRQ_H */ diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index bf78cf381dfc..d9cbc18f6b2e 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -4,7 +4,7 @@ #define HAVE_JUMP_LABEL_BATCH -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -51,5 +51,5 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool return true; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index cb89e54ada25..f870d09515cc 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -716,6 +716,9 @@ extern char sie_exit; bool kvm_s390_pv_is_protected(struct kvm *kvm); bool kvm_s390_pv_cpu_is_protected(struct kvm_vcpu *vcpu); +extern int kvm_s390_enter_exit_sie(struct kvm_s390_sie_block *scb, + u64 *gprs, unsigned long gasce); + extern int kvm_s390_gisc_register(struct kvm *kvm, u32 gisc); extern int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc); diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index e99e9c87b1ce..d9c853db9a40 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -22,7 +22,7 @@ #define LOWCORE_ALT_ADDRESS _AC(0x70000, UL) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct pgm_tdb { u64 data[32]; @@ -237,7 +237,7 @@ static inline void set_prefix(__u32 address) asm volatile("spx %0" : : "Q" (address) : "memory"); } -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ .macro GET_LC reg ALTERNATIVE "lghi \reg,0", \ @@ -251,5 +251,5 @@ static inline void set_prefix(__u32 address) ALT_FEATURE(MFEATURE_LOWCORE) .endm -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_LOWCORE_H */ diff --git a/arch/s390/include/asm/machine.h b/arch/s390/include/asm/machine.h index 8abe5afdbfc4..9bd4a9dc7778 100644 --- a/arch/s390/include/asm/machine.h +++ b/arch/s390/include/asm/machine.h @@ -20,7 +20,7 @@ #define MFEATURE_LPAR 9 #define MFEATURE_DIAG288 10 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -100,5 +100,5 @@ DEFINE_MACHINE_HAS_FEATURE(lpar, MFEATURE_LPAR) #define machine_is_kvm machine_has_kvm #define machine_is_lpar machine_has_lpar -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_S390_MACHINE_H */ diff --git a/arch/s390/include/asm/mem_encrypt.h b/arch/s390/include/asm/mem_encrypt.h index b85e13505a0f..28c83ec1f243 100644 --- a/arch/s390/include/asm/mem_encrypt.h +++ b/arch/s390/include/asm/mem_encrypt.h @@ -2,11 +2,11 @@ #ifndef S390_MEM_ENCRYPT_H__ #define S390_MEM_ENCRYPT_H__ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ int set_memory_encrypted(unsigned long vaddr, int numpages); int set_memory_decrypted(unsigned long vaddr, int numpages); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* S390_MEM_ENCRYPT_H__ */ diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index 227466ce9e41..6454c1531854 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h @@ -33,7 +33,7 @@ #define MCCK_CODE_FC_VALID BIT(63 - 43) #define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ union mci { unsigned long val; @@ -104,5 +104,5 @@ void nmi_free_mcesa(u64 *mcesad); void s390_handle_mcck(void); void s390_do_machine_check(struct pt_regs *regs); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_NMI_H */ diff --git a/arch/s390/include/asm/nospec-branch.h b/arch/s390/include/asm/nospec-branch.h index c7c96282f011..81c4813cff18 100644 --- a/arch/s390/include/asm/nospec-branch.h +++ b/arch/s390/include/asm/nospec-branch.h @@ -2,7 +2,7 @@ #ifndef _ASM_S390_EXPOLINE_H #define _ASM_S390_EXPOLINE_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -42,6 +42,6 @@ void __s390_indirect_jump_r13(void); void __s390_indirect_jump_r14(void); void __s390_indirect_jump_r15(void); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_EXPOLINE_H */ diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h index cb15dd25bf21..6ce6b56e282b 100644 --- a/arch/s390/include/asm/nospec-insn.h +++ b/arch/s390/include/asm/nospec-insn.h @@ -3,9 +3,10 @@ #define _ASM_S390_NOSPEC_ASM_H #include +#include #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #ifdef CC_USING_EXPOLINE @@ -128,6 +129,6 @@ .endm #endif /* CC_USING_EXPOLINE */ -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_NOSPEC_ASM_H */ diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index 4e5dbabdf202..9240a363c893 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h @@ -33,7 +33,7 @@ #define HAVE_ARCH_HUGETLB_UNMAPPED_AREA #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ void __storage_key_init_range(unsigned long start, unsigned long end); @@ -130,11 +130,19 @@ typedef pte_t *pgtable_t; static inline void page_set_storage_key(unsigned long addr, unsigned char skey, int mapped) { - if (!mapped) - asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0" - : : "d" (skey), "a" (addr)); - else - asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); + if (!mapped) { + asm volatile( + " .insn rrf,0xb22b0000,%[skey],%[addr],8,0" + : + : [skey] "d" (skey), [addr] "a" (addr) + : "memory"); + } else { + asm volatile( + " sske %[skey],%[addr]" + : + : [skey] "d" (skey), [addr] "a" (addr) + : "memory"); + } } static inline unsigned char page_get_storage_key(unsigned long addr) @@ -274,7 +282,7 @@ static inline unsigned long virt_to_pfn(const void *kaddr) #define VM_DATA_DEFAULT_FLAGS VM_DATA_FLAGS_NON_EXEC -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #include #include diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h index 84f6b8357b45..96af7d964014 100644 --- a/arch/s390/include/asm/percpu.h +++ b/arch/s390/include/asm/percpu.h @@ -16,10 +16,9 @@ * For 64 bit module code, the module may be more than 4G above the * per cpu area, use weak definitions to force the compiler to * generate external references. + * Therefore, we have enabled CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU + * in the Kconfig. */ -#if defined(MODULE) -#define ARCH_NEEDS_WEAK_PER_CPU -#endif /* * We use a compare-and-swap loop since that uses less cpu cycles than diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 6d8bc27a366e..c1a7a92f0575 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -963,6 +963,12 @@ static inline pmd_t pmd_clear_soft_dirty(pmd_t pmd) return clear_pmd_bit(pmd, __pgprot(_SEGMENT_ENTRY_SOFT_DIRTY)); } +#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION +#define pmd_swp_soft_dirty(pmd) pmd_soft_dirty(pmd) +#define pmd_swp_mksoft_dirty(pmd) pmd_mksoft_dirty(pmd) +#define pmd_swp_clear_soft_dirty(pmd) pmd_clear_soft_dirty(pmd) +#endif + /* * query functions pte_write/pte_dirty/pte_young only work if * pte_present() is true. Undefined behaviour if not.. @@ -1979,6 +1985,45 @@ static inline unsigned long __swp_offset_rste(swp_entry_t entry) #define __rste_to_swp_entry(rste) ((swp_entry_t) { rste }) +/* + * s390 has different layout for PTE and region / segment table entries (RSTE). + * This is also true for swap entries, and their swap type and offset encoding. + * For hugetlbfs PTE_MARKER support, s390 has internal __swp_type_rste() and + * __swp_offset_rste() helpers to correctly handle RSTE swap entries. + * + * But common swap code does not know about this difference, and only uses + * __swp_type(), __swp_offset() and __swp_entry() helpers for conversion between + * arch-dependent and arch-independent representation of swp_entry_t for all + * pagetable levels. On s390, those helpers only work for PTE swap entries. + * + * Therefore, implement __pmd_to_swp_entry() to build a fake PTE swap entry + * and return the arch-dependent representation of that. Correspondingly, + * implement __swp_entry_to_pmd() to convert that into a proper PMD swap + * entry again. With this, the arch-dependent swp_entry_t representation will + * always look like a PTE swap entry in common code. + * + * This is somewhat similar to fake PTEs in hugetlbfs code for s390, but only + * requires conversion of the swap type and offset, and not all the possible + * PTE bits. + */ +static inline swp_entry_t __pmd_to_swp_entry(pmd_t pmd) +{ + swp_entry_t arch_entry; + pte_t pte; + + arch_entry = __rste_to_swp_entry(pmd_val(pmd)); + pte = mk_swap_pte(__swp_type_rste(arch_entry), __swp_offset_rste(arch_entry)); + return __pte_to_swp_entry(pte); +} + +static inline pmd_t __swp_entry_to_pmd(swp_entry_t arch_entry) +{ + pmd_t pmd; + + pmd = __pmd(mk_swap_rste(__swp_type(arch_entry), __swp_offset(arch_entry))); + return pmd; +} + extern int vmem_add_mapping(unsigned long start, unsigned long size); extern void vmem_remove_mapping(unsigned long start, unsigned long size); extern int __vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot, bool alloc); diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 6c8063cb8fe7..6a9c08b80eda 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -26,7 +26,7 @@ #define RESTART_FLAG_CTLREGS _AC(1 << 0, U) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -418,6 +418,6 @@ static __always_inline void bpon(void) ); } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_S390_PROCESSOR_H */ diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 62c0ab4a4b9d..dfa770b15fad 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -54,7 +54,7 @@ PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_MCHECK | \ PSW_MASK_PSTATE | PSW_ASC_PRIMARY) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct psw_bits { unsigned long : 1; @@ -265,7 +265,7 @@ static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *r addr = kernel_stack_pointer(regs) + n * sizeof(long); if (!regs_within_kernel_stack(regs, addr)) return 0; - return READ_ONCE_NOCHECK(addr); + return READ_ONCE_NOCHECK(*(unsigned long *)addr); } /** @@ -292,5 +292,5 @@ static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc) regs->gprs[2] = rc; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _S390_PTRACE_H */ diff --git a/arch/s390/include/asm/purgatory.h b/arch/s390/include/asm/purgatory.h index e297bcfc476f..4c7a43bc43a1 100644 --- a/arch/s390/include/asm/purgatory.h +++ b/arch/s390/include/asm/purgatory.h @@ -7,11 +7,11 @@ #ifndef _S390_PURGATORY_H_ #define _S390_PURGATORY_H_ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include int verify_sha256_digest(void); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _S390_PURGATORY_H_ */ diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 1e62919bacf4..0f184dbdbe5e 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -21,7 +21,7 @@ #define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 #define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include #include @@ -199,5 +199,5 @@ static inline int sclp_get_core_info(struct sclp_core_info *info, int early) return _sclp_get_core_info(info); } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_SCLP_H */ diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 031e881b4d88..7c57ac968bf6 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -24,7 +24,7 @@ #define LEGACY_COMMAND_LINE_SIZE 896 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -41,6 +41,8 @@ struct parmarea { char command_line[COMMAND_LINE_SIZE]; /* 0x10480 */ }; +extern char arch_hw_string[128]; + extern struct parmarea parmarea; extern unsigned int zlib_dfltcc_support; @@ -100,5 +102,5 @@ static __always_inline u32 gen_lpswe(unsigned long addr) BUILD_BUG_ON(addr > 0xfff); return 0xb2b20000 | addr; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_SETUP_H */ diff --git a/arch/s390/include/asm/sigp.h b/arch/s390/include/asm/sigp.h index 472943b77066..97d77868f83c 100644 --- a/arch/s390/include/asm/sigp.h +++ b/arch/s390/include/asm/sigp.h @@ -36,7 +36,7 @@ #define SIGP_STATUS_INCORRECT_STATE 0x00000200UL #define SIGP_STATUS_NOT_RUNNING 0x00000400UL -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -68,6 +68,6 @@ static inline int __pcpu_sigp(u16 addr, u8 order, unsigned long parm, return cc; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __S390_ASM_SIGP_H */ diff --git a/arch/s390/include/asm/skey.h b/arch/s390/include/asm/skey.h new file mode 100644 index 000000000000..84e7cf28b712 --- /dev/null +++ b/arch/s390/include/asm/skey.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_SKEY_H +#define __ASM_SKEY_H + +#include + +struct skey_region { + unsigned long start; + unsigned long end; +}; + +#define SKEY_REGION(_start, _end) \ + stringify_in_c(.section .skey_region,"a";) \ + stringify_in_c(.balign 8;) \ + stringify_in_c(.quad (_start);) \ + stringify_in_c(.quad (_end);) \ + stringify_in_c(.previous) + +extern int skey_regions_initialized; +extern struct skey_region __skey_region_start[]; +extern struct skey_region __skey_region_end[]; + +void __skey_regions_initialize(void); + +static inline void skey_regions_initialize(void) +{ + if (READ_ONCE(skey_regions_initialized)) + return; + __skey_regions_initialize(); +} + +#endif /* __ASM_SKEY_H */ diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index 391eb04d26d8..f6ed2c8192c8 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -24,7 +24,7 @@ #define STACK_INIT_OFFSET (THREAD_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * low level task data that entry.S needs immediate access to diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index bed8d0b5a282..59dfb8780f62 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -196,13 +196,6 @@ static inline unsigned long get_tod_clock_fast(void) asm volatile("stckf %0" : "=Q" (clk) : : "cc"); return clk; } - -static inline cycles_t get_cycles(void) -{ - return (cycles_t) get_tod_clock() >> 2; -} -#define get_cycles get_cycles - int get_phys_clock(unsigned long *clock); void init_cpu_timer(void); @@ -230,6 +223,12 @@ static inline unsigned long get_tod_clock_monotonic(void) return tod; } +static inline cycles_t get_cycles(void) +{ + return (cycles_t)get_tod_clock_monotonic() >> 2; +} +#define get_cycles get_cycles + /** * tod_to_ns - convert a TOD format value to nanoseconds * @todval: to be converted TOD format value diff --git a/arch/s390/include/asm/tpi.h b/arch/s390/include/asm/tpi.h index f76e5fdff23a..71c8b6f76cdd 100644 --- a/arch/s390/include/asm/tpi.h +++ b/arch/s390/include/asm/tpi.h @@ -5,7 +5,7 @@ #include #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* I/O-Interruption Code as stored by TEST PENDING INTERRUPTION (TPI). */ struct tpi_info { @@ -32,6 +32,6 @@ struct tpi_adapter_info { u32 :27; } __packed __aligned(4); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_TPI_H */ diff --git a/arch/s390/include/asm/types.h b/arch/s390/include/asm/types.h index 0b5d550a0478..53695b2196f7 100644 --- a/arch/s390/include/asm/types.h +++ b/arch/s390/include/asm/types.h @@ -5,7 +5,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ union register_pair { unsigned __int128 pair; @@ -15,5 +15,5 @@ union register_pair { }; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ASM_S390_TYPES_H */ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index a43fc88c0050..3e5b8b677057 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -473,188 +473,30 @@ do { \ void __cmpxchg_user_key_called_with_bad_pointer(void); -#define CMPXCHG_USER_KEY_MAX_LOOPS 128 +int __cmpxchg_user_key1(unsigned long address, unsigned char *uval, + unsigned char old, unsigned char new, unsigned long key); +int __cmpxchg_user_key2(unsigned long address, unsigned short *uval, + unsigned short old, unsigned short new, unsigned long key); +int __cmpxchg_user_key4(unsigned long address, unsigned int *uval, + unsigned int old, unsigned int new, unsigned long key); +int __cmpxchg_user_key8(unsigned long address, unsigned long *uval, + unsigned long old, unsigned long new, unsigned long key); +int __cmpxchg_user_key16(unsigned long address, __uint128_t *uval, + __uint128_t old, __uint128_t new, unsigned long key); -static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, - __uint128_t old, __uint128_t new, - unsigned long key, int size) +static __always_inline int _cmpxchg_user_key(unsigned long address, void *uval, + __uint128_t old, __uint128_t new, + unsigned long key, int size) { - bool sacf_flag; - int rc = 0; - switch (size) { - case 1: { - unsigned int prev, shift, mask, _old, _new; - unsigned long count; - - shift = (3 ^ (address & 3)) << 3; - address ^= address & 3; - _old = ((unsigned int)old & 0xff) << shift; - _new = ((unsigned int)new & 0xff) << shift; - mask = ~(0xff << shift); - sacf_flag = enable_sacf_uaccess(); - asm_inline volatile( - " spka 0(%[key])\n" - " sacf 256\n" - " llill %[count],%[max_loops]\n" - "0: l %[prev],%[address]\n" - "1: nr %[prev],%[mask]\n" - " xilf %[mask],0xffffffff\n" - " or %[new],%[prev]\n" - " or %[prev],%[tmp]\n" - "2: lr %[tmp],%[prev]\n" - "3: cs %[prev],%[new],%[address]\n" - "4: jnl 5f\n" - " xr %[tmp],%[prev]\n" - " xr %[new],%[tmp]\n" - " nr %[tmp],%[mask]\n" - " jnz 5f\n" - " brct %[count],2b\n" - "5: sacf 768\n" - " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev]) - : [rc] "+&d" (rc), - [prev] "=&d" (prev), - [address] "+Q" (*(int *)address), - [tmp] "+&d" (_old), - [new] "+&d" (_new), - [mask] "+&d" (mask), - [count] "=a" (count) - : [key] "%[count]" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY), - [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS) - : "memory", "cc"); - disable_sacf_uaccess(sacf_flag); - *(unsigned char *)uval = prev >> shift; - if (!count) - rc = -EAGAIN; - return rc; + case 1: return __cmpxchg_user_key1(address, uval, old, new, key); + case 2: return __cmpxchg_user_key2(address, uval, old, new, key); + case 4: return __cmpxchg_user_key4(address, uval, old, new, key); + case 8: return __cmpxchg_user_key8(address, uval, old, new, key); + case 16: return __cmpxchg_user_key16(address, uval, old, new, key); + default: __cmpxchg_user_key_called_with_bad_pointer(); } - case 2: { - unsigned int prev, shift, mask, _old, _new; - unsigned long count; - - shift = (2 ^ (address & 2)) << 3; - address ^= address & 2; - _old = ((unsigned int)old & 0xffff) << shift; - _new = ((unsigned int)new & 0xffff) << shift; - mask = ~(0xffff << shift); - sacf_flag = enable_sacf_uaccess(); - asm_inline volatile( - " spka 0(%[key])\n" - " sacf 256\n" - " llill %[count],%[max_loops]\n" - "0: l %[prev],%[address]\n" - "1: nr %[prev],%[mask]\n" - " xilf %[mask],0xffffffff\n" - " or %[new],%[prev]\n" - " or %[prev],%[tmp]\n" - "2: lr %[tmp],%[prev]\n" - "3: cs %[prev],%[new],%[address]\n" - "4: jnl 5f\n" - " xr %[tmp],%[prev]\n" - " xr %[new],%[tmp]\n" - " nr %[tmp],%[mask]\n" - " jnz 5f\n" - " brct %[count],2b\n" - "5: sacf 768\n" - " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev]) - : [rc] "+&d" (rc), - [prev] "=&d" (prev), - [address] "+Q" (*(int *)address), - [tmp] "+&d" (_old), - [new] "+&d" (_new), - [mask] "+&d" (mask), - [count] "=a" (count) - : [key] "%[count]" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY), - [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS) - : "memory", "cc"); - disable_sacf_uaccess(sacf_flag); - *(unsigned short *)uval = prev >> shift; - if (!count) - rc = -EAGAIN; - return rc; - } - case 4: { - unsigned int prev = old; - - sacf_flag = enable_sacf_uaccess(); - asm_inline volatile( - " spka 0(%[key])\n" - " sacf 256\n" - "0: cs %[prev],%[new],%[address]\n" - "1: sacf 768\n" - " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) - : [rc] "+&d" (rc), - [prev] "+&d" (prev), - [address] "+Q" (*(int *)address) - : [new] "d" ((unsigned int)new), - [key] "a" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY) - : "memory", "cc"); - disable_sacf_uaccess(sacf_flag); - *(unsigned int *)uval = prev; - return rc; - } - case 8: { - unsigned long prev = old; - - sacf_flag = enable_sacf_uaccess(); - asm_inline volatile( - " spka 0(%[key])\n" - " sacf 256\n" - "0: csg %[prev],%[new],%[address]\n" - "1: sacf 768\n" - " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) - : [rc] "+&d" (rc), - [prev] "+&d" (prev), - [address] "+QS" (*(long *)address) - : [new] "d" ((unsigned long)new), - [key] "a" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY) - : "memory", "cc"); - disable_sacf_uaccess(sacf_flag); - *(unsigned long *)uval = prev; - return rc; - } - case 16: { - __uint128_t prev = old; - - sacf_flag = enable_sacf_uaccess(); - asm_inline volatile( - " spka 0(%[key])\n" - " sacf 256\n" - "0: cdsg %[prev],%[new],%[address]\n" - "1: sacf 768\n" - " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REGPAIR(1b, 1b, %[rc], %[prev]) - : [rc] "+&d" (rc), - [prev] "+&d" (prev), - [address] "+QS" (*(__int128_t *)address) - : [new] "d" (new), - [key] "a" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY) - : "memory", "cc"); - disable_sacf_uaccess(sacf_flag); - *(__uint128_t *)uval = prev; - return rc; - } - } - __cmpxchg_user_key_called_with_bad_pointer(); - return rc; + return 0; } /** @@ -686,8 +528,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, BUILD_BUG_ON(sizeof(*(__ptr)) != sizeof(*(__uval))); \ might_fault(); \ __chk_user_ptr(__ptr); \ - __cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \ - (old), (new), (key), sizeof(*(__ptr))); \ + _cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \ + (old), (new), (key), sizeof(*(__ptr))); \ }) #endif /* __S390_UACCESS_H */ diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h index 420a073fdde5..8e2fffa0ca68 100644 --- a/arch/s390/include/asm/vdso.h +++ b/arch/s390/include/asm/vdso.h @@ -4,11 +4,11 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ int vdso_getcpu_init(void); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #define __VDSO_PAGES 4 diff --git a/arch/s390/include/asm/vdso/getrandom.h b/arch/s390/include/asm/vdso/getrandom.h index f8713ce39bb2..6741a27199f8 100644 --- a/arch/s390/include/asm/vdso/getrandom.h +++ b/arch/s390/include/asm/vdso/getrandom.h @@ -3,7 +3,7 @@ #ifndef __ASM_VDSO_GETRANDOM_H #define __ASM_VDSO_GETRANDOM_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -23,6 +23,6 @@ static __always_inline ssize_t getrandom_syscall(void *buffer, size_t len, unsig return syscall3(__NR_getrandom, (long)buffer, (long)len, (long)flags); } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_VDSO_GETRANDOM_H */ diff --git a/arch/s390/include/asm/vdso/gettimeofday.h b/arch/s390/include/asm/vdso/gettimeofday.h index fb4564308e9d..c31ac5f61c83 100644 --- a/arch/s390/include/asm/vdso/gettimeofday.h +++ b/arch/s390/include/asm/vdso/gettimeofday.h @@ -16,13 +16,7 @@ static inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_time_data *vd) { - u64 adj, now; - - now = get_tod_clock(); - adj = vd->arch_data.tod_steering_end - now; - if (unlikely((s64) adj > 0)) - now += (vd->arch_data.tod_steering_delta < 0) ? (adj >> 15) : -(adj >> 15); - return now; + return get_tod_clock() - vd->arch_data.tod_delta; } static __always_inline diff --git a/arch/s390/include/asm/vdso/time_data.h b/arch/s390/include/asm/vdso/time_data.h index 8a08752422e6..25c4e0d9f596 100644 --- a/arch/s390/include/asm/vdso/time_data.h +++ b/arch/s390/include/asm/vdso/time_data.h @@ -5,8 +5,7 @@ #include struct arch_vdso_time_data { - __s64 tod_steering_delta; - __u64 tod_steering_end; + __s64 tod_delta; }; #endif /* __S390_ASM_VDSO_TIME_DATA_H */ diff --git a/arch/s390/include/asm/vdso/vsyscall.h b/arch/s390/include/asm/vdso/vsyscall.h index d346ebe51301..b00acec8ddbc 100644 --- a/arch/s390/include/asm/vdso/vsyscall.h +++ b/arch/s390/include/asm/vdso/vsyscall.h @@ -2,7 +2,7 @@ #ifndef __ASM_VDSO_VSYSCALL_H #define __ASM_VDSO_VSYSCALL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -11,6 +11,6 @@ /* The asm-generic header needs to be included after the definitions above */ #include -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_VDSO_VSYSCALL_H */ diff --git a/arch/s390/include/uapi/asm/ptrace.h b/arch/s390/include/uapi/asm/ptrace.h index bb0826024bb9..ea202072f1ad 100644 --- a/arch/s390/include/uapi/asm/ptrace.h +++ b/arch/s390/include/uapi/asm/ptrace.h @@ -242,7 +242,8 @@ #define PTRACE_OLDSETOPTIONS 21 #define PTRACE_SYSEMU 31 #define PTRACE_SYSEMU_SINGLESTEP 32 -#ifndef __ASSEMBLY__ + +#ifndef __ASSEMBLER__ #include #include @@ -450,6 +451,6 @@ struct user_regs_struct { unsigned long ieee_instruction_pointer; /* obsolete, always 0 */ }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPI_S390_PTRACE_H */ diff --git a/arch/s390/include/uapi/asm/schid.h b/arch/s390/include/uapi/asm/schid.h index a3e1cf168553..d804d1a5b1b3 100644 --- a/arch/s390/include/uapi/asm/schid.h +++ b/arch/s390/include/uapi/asm/schid.h @@ -4,7 +4,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct subchannel_id { __u32 cssid : 8; @@ -15,6 +15,6 @@ struct subchannel_id { __u32 sch_no : 16; } __attribute__ ((packed, aligned(4))); -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPIASM_SCHID_H */ diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 84457dbb26b4..4ab468c5032e 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h @@ -10,7 +10,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ typedef unsigned long addr_t; typedef __signed__ long saddr_t; @@ -25,6 +25,6 @@ typedef struct { }; } __attribute__((packed, aligned(4))) __vector128; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPI_S390_TYPES_H */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index ea5ed6654050..eb06ff888314 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -41,7 +41,7 @@ obj-y += processor.o syscall.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += debug.o irq.o ipl.o dis.o vdso.o cpufeature.o obj-y += sysinfo.o lgr.o os_info.o ctlreg.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o -obj-y += entry.o reipl.o kdebugfs.o alternative.o +obj-y += entry.o reipl.o kdebugfs.o alternative.o skey.o obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o obj-y += smp.o text_amode31.o stacktrace.o abs_lowcore.o facility.o uv.o wti.o obj-y += diag/ diff --git a/arch/s390/kernel/cpacf.c b/arch/s390/kernel/cpacf.c index 4b9b34f95d72..3bebc47beeab 100644 --- a/arch/s390/kernel/cpacf.c +++ b/arch/s390/kernel/cpacf.c @@ -101,7 +101,7 @@ static const struct bin_attribute *const cpacf_attrs[] = { static const struct attribute_group cpacf_attr_grp = { .name = "cpacf", - .bin_attrs_new = cpacf_attrs, + .bin_attrs = cpacf_attrs, }; static int __init cpacf_init(void) diff --git a/arch/s390/kernel/cpufeature.c b/arch/s390/kernel/cpufeature.c index 76210f001028..c9eef9ed876b 100644 --- a/arch/s390/kernel/cpufeature.c +++ b/arch/s390/kernel/cpufeature.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index adb164223f8c..d4839de8ce9d 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/arch/s390/kernel/ctlreg.c b/arch/s390/kernel/ctlreg.c index 8cc26cf2c64a..a0501f4c7e7a 100644 --- a/arch/s390/kernel/ctlreg.c +++ b/arch/s390/kernel/ctlreg.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 2a41be2f7925..c62100dc62c8 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -1677,7 +1677,7 @@ EXPORT_SYMBOL(debug_dflt_header_fn); /* * prints debug data sprintf-formatted: - * debug_sprinf_event/exception calls must be used together with this view + * debug_sprintf_event/exception calls must be used together with this view */ #define DEBUG_SPRINTF_MAX_ARGS 10 diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index 94eb8168ea44..63a1d4226ff8 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 54cf0923050f..9adfbdd377dc 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -105,6 +105,8 @@ static inline void strim_all(char *str) } } +char arch_hw_string[128]; + static noinline __init void setup_arch_string(void) { struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page; @@ -131,6 +133,7 @@ static noinline __init void setup_arch_string(void) machine_is_vm() ? "z/VM" : machine_is_kvm() ? "KVM" : "unknown"); } + sprintf(arch_hw_string, "HW: %s (%s)", mstr, hvstr); dump_stack_set_arch_desc("%s (%s)", mstr, hvstr); } @@ -154,6 +157,7 @@ void __init __do_early_pgm_check(struct pt_regs *regs) regs->int_code = lc->pgm_int_code; regs->int_parm_long = lc->trans_exc_code; + regs->last_break = lc->pgm_last_break; ip = __rewind_psw(regs->psw, regs->int_code >> 16); /* Monitor Event? Might be a warning */ diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 0f00f4b06d51..75b0fbb236d0 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -124,7 +124,7 @@ _LPP_OFFSET = __LC_LPP #endif .macro STACKLEAK_ERASE -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE brasl %r14,stackleak_erase_on_task_stack #endif .endm diff --git a/arch/s390/kernel/facility.c b/arch/s390/kernel/facility.c index f02127219a27..d028b0be5c1d 100644 --- a/arch/s390/kernel/facility.c +++ b/arch/s390/kernel/facility.c @@ -3,6 +3,7 @@ * Copyright IBM Corp. 2023 */ +#include #include unsigned int stfle_size(void) diff --git a/arch/s390/kernel/fpu.c b/arch/s390/kernel/fpu.c index 6f2e87920288..03a8973aec3c 100644 --- a/arch/s390/kernel/fpu.c +++ b/arch/s390/kernel/fpu.c @@ -5,6 +5,8 @@ * Copyright IBM Corp. 2015 * Author(s): Hendrik Brueckner */ + +#include #include #include #include diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index ff15f91affde..961a3d60a4dd 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -596,7 +596,7 @@ static struct attribute *ipl_fcp_attrs[] = { static const struct attribute_group ipl_fcp_attr_group = { .attrs = ipl_fcp_attrs, - .bin_attrs_new = ipl_fcp_bin_attrs, + .bin_attrs = ipl_fcp_bin_attrs, }; static struct attribute *ipl_nvme_attrs[] = { @@ -610,7 +610,7 @@ static struct attribute *ipl_nvme_attrs[] = { static const struct attribute_group ipl_nvme_attr_group = { .attrs = ipl_nvme_attrs, - .bin_attrs_new = ipl_nvme_bin_attrs, + .bin_attrs = ipl_nvme_bin_attrs, }; static struct attribute *ipl_eckd_attrs[] = { @@ -623,7 +623,7 @@ static struct attribute *ipl_eckd_attrs[] = { static const struct attribute_group ipl_eckd_attr_group = { .attrs = ipl_eckd_attrs, - .bin_attrs_new = ipl_eckd_bin_attrs, + .bin_attrs = ipl_eckd_bin_attrs, }; /* CCW ipl device attributes */ @@ -920,7 +920,7 @@ static struct attribute *reipl_fcp_attrs[] = { static const struct attribute_group reipl_fcp_attr_group = { .attrs = reipl_fcp_attrs, - .bin_attrs_new = reipl_fcp_bin_attrs, + .bin_attrs = reipl_fcp_bin_attrs, }; static struct kobj_attribute sys_reipl_fcp_clear_attr = @@ -958,7 +958,7 @@ static struct attribute *reipl_nvme_attrs[] = { static const struct attribute_group reipl_nvme_attr_group = { .attrs = reipl_nvme_attrs, - .bin_attrs_new = reipl_nvme_bin_attrs + .bin_attrs = reipl_nvme_bin_attrs }; static ssize_t reipl_nvme_clear_show(struct kobject *kobj, @@ -1051,7 +1051,7 @@ static struct attribute *reipl_eckd_attrs[] = { static const struct attribute_group reipl_eckd_attr_group = { .attrs = reipl_eckd_attrs, - .bin_attrs_new = reipl_eckd_bin_attrs + .bin_attrs = reipl_eckd_bin_attrs }; static ssize_t reipl_eckd_clear_show(struct kobject *kobj, @@ -1596,7 +1596,7 @@ static const struct bin_attribute *const dump_fcp_bin_attrs[] = { static const struct attribute_group dump_fcp_attr_group = { .name = IPL_FCP_STR, .attrs = dump_fcp_attrs, - .bin_attrs_new = dump_fcp_bin_attrs, + .bin_attrs = dump_fcp_bin_attrs, }; /* NVME dump device attributes */ @@ -1630,7 +1630,7 @@ static const struct bin_attribute *const dump_nvme_bin_attrs[] = { static const struct attribute_group dump_nvme_attr_group = { .name = IPL_NVME_STR, .attrs = dump_nvme_attrs, - .bin_attrs_new = dump_nvme_bin_attrs, + .bin_attrs = dump_nvme_bin_attrs, }; /* ECKD dump device attributes */ @@ -1664,7 +1664,7 @@ static const struct bin_attribute *const dump_eckd_bin_attrs[] = { static const struct attribute_group dump_eckd_attr_group = { .name = IPL_ECKD_STR, .attrs = dump_eckd_attrs, - .bin_attrs_new = dump_eckd_bin_attrs, + .bin_attrs = dump_eckd_bin_attrs, }; /* CCW dump device attributes */ diff --git a/arch/s390/kernel/kexec_elf.c b/arch/s390/kernel/kexec_elf.c index 4d364de43799..143e34a4eca5 100644 --- a/arch/s390/kernel/kexec_elf.c +++ b/arch/s390/kernel/kexec_elf.c @@ -16,7 +16,7 @@ static int kexec_file_add_kernel_elf(struct kimage *image, struct s390_load_data *data) { - struct kexec_buf buf; + struct kexec_buf buf = {}; const Elf_Ehdr *ehdr; const Elf_Phdr *phdr; Elf_Addr entry; diff --git a/arch/s390/kernel/kexec_image.c b/arch/s390/kernel/kexec_image.c index a32ce8bea745..9a439175723c 100644 --- a/arch/s390/kernel/kexec_image.c +++ b/arch/s390/kernel/kexec_image.c @@ -16,7 +16,7 @@ static int kexec_file_add_kernel_image(struct kimage *image, struct s390_load_data *data) { - struct kexec_buf buf; + struct kexec_buf buf = {}; buf.image = image; diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c index c2bac14dd668..a36d7311c668 100644 --- a/arch/s390/kernel/machine_kexec_file.c +++ b/arch/s390/kernel/machine_kexec_file.c @@ -129,7 +129,7 @@ static int kexec_file_update_purgatory(struct kimage *image, static int kexec_file_add_purgatory(struct kimage *image, struct s390_load_data *data) { - struct kexec_buf buf; + struct kexec_buf buf = {}; int ret; buf.image = image; @@ -152,7 +152,7 @@ static int kexec_file_add_purgatory(struct kimage *image, static int kexec_file_add_initrd(struct kimage *image, struct s390_load_data *data) { - struct kexec_buf buf; + struct kexec_buf buf = {}; int ret; buf.image = image; @@ -184,7 +184,7 @@ static int kexec_file_add_ipl_report(struct kimage *image, { __u32 *lc_ipl_parmblock_ptr; unsigned int len, ncerts; - struct kexec_buf buf; + struct kexec_buf buf = {}; unsigned long addr; void *ptr, *end; int ret; diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 3da371c144eb..11f33243a23f 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -116,18 +116,82 @@ static __always_inline char *u64_to_hex(char *dest, u64 val) return dest; } +static notrace void nmi_print_info(void) +{ + struct lowcore *lc = get_lowcore(); + char message[100]; + char *ptr; + int i; + + ptr = nmi_puts(message, "Unrecoverable machine check, code: "); + ptr = u64_to_hex(ptr, lc->mcck_interruption_code); + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + + ptr = nmi_puts(message, init_utsname()->release); + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + + ptr = nmi_puts(message, arch_hw_string); + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + + ptr = nmi_puts(message, "PSW: "); + ptr = u64_to_hex(ptr, lc->mcck_old_psw.mask); + ptr = nmi_puts(ptr, " "); + ptr = u64_to_hex(ptr, lc->mcck_old_psw.addr); + ptr = nmi_puts(ptr, " PFX: "); + ptr = u64_to_hex(ptr, (u64)get_lowcore()); + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + + ptr = nmi_puts(message, "LBA: "); + ptr = u64_to_hex(ptr, lc->last_break_save_area); + ptr = nmi_puts(ptr, " EDC: "); + ptr = u64_to_hex(ptr, lc->external_damage_code); + ptr = nmi_puts(ptr, " FSA: "); + ptr = u64_to_hex(ptr, lc->failing_storage_address); + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + + ptr = nmi_puts(message, "CRS:\n"); + sclp_emergency_printk(message); + ptr = message; + for (i = 0; i < 16; i++) { + ptr = u64_to_hex(ptr, lc->cregs_save_area[i].val); + ptr = nmi_puts(ptr, " "); + if ((i + 1) % 4 == 0) { + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + ptr = message; + } + } + + ptr = nmi_puts(message, "GPRS:\n"); + sclp_emergency_printk(message); + ptr = message; + for (i = 0; i < 16; i++) { + ptr = u64_to_hex(ptr, lc->gpregs_save_area[i]); + ptr = nmi_puts(ptr, " "); + if ((i + 1) % 4 == 0) { + ptr = nmi_puts(ptr, "\n"); + sclp_emergency_printk(message); + ptr = message; + } + } + + ptr = nmi_puts(message, "System stopped\n"); + sclp_emergency_printk(message); +} + static notrace void s390_handle_damage(void) { struct lowcore *lc = get_lowcore(); union ctlreg0 cr0, cr0_new; - char message[100]; psw_t psw_save; - char *ptr; smp_emergency_stop(); diag_amode31_ops.diag308_reset(); - ptr = nmi_puts(message, "System stopped due to unrecoverable machine check, code: 0x"); - u64_to_hex(ptr, lc->mcck_interruption_code); /* * Disable low address protection and make machine check new PSW a @@ -141,7 +205,7 @@ static notrace void s390_handle_damage(void) psw_bits(lc->mcck_new_psw).io = 0; psw_bits(lc->mcck_new_psw).ext = 0; psw_bits(lc->mcck_new_psw).wait = 1; - sclp_emergency_printk(message); + nmi_print_info(); /* * Restore machine check new PSW and control register 0 to original diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 6a262e198e35..04457d88e589 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -761,8 +760,6 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) break; case PERF_TYPE_HARDWARE: - if (is_sampling_event(event)) /* No sampling support */ - return -ENOENT; ev = attr->config; if (!attr->exclude_user && attr->exclude_kernel) { /* @@ -860,6 +857,8 @@ static int cpumf_pmu_event_init(struct perf_event *event) unsigned int type = event->attr.type; int err = -ENOENT; + if (is_sampling_event(event)) /* No sampling support */ + return err; if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_RAW) err = __hw_perf_event_init(event, type); else if (event->pmu->type == type) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 91469401f2c9..f432869f8921 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index 2b9611c4718e..91b8716c883a 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kernel/perf_pai_crypto.c b/arch/s390/kernel/perf_pai_crypto.c index 63875270941b..9455f213dc20 100644 --- a/arch/s390/kernel/perf_pai_crypto.c +++ b/arch/s390/kernel/perf_pai_crypto.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -286,10 +285,10 @@ static int paicrypt_event_init(struct perf_event *event) /* PAI crypto PMU registered as PERF_TYPE_RAW, check event type */ if (a->type != PERF_TYPE_RAW && event->pmu->type != a->type) return -ENOENT; - /* PAI crypto event must be in valid range */ + /* PAI crypto event must be in valid range, try others if not */ if (a->config < PAI_CRYPTO_BASE || a->config > PAI_CRYPTO_BASE + paicrypt_cnt) - return -EINVAL; + return -ENOENT; /* Allow only CRYPTO_ALL for sampling */ if (a->sample_period && a->config != PAI_CRYPTO_BASE) return -EINVAL; @@ -696,7 +695,7 @@ static const char * const paicrypt_ctrnames[] = { [111] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_256", [112] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_128", [113] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_192", - [114] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256A", + [114] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256", [115] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_128", [116] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_256", [117] = "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_128", diff --git a/arch/s390/kernel/perf_pai_ext.c b/arch/s390/kernel/perf_pai_ext.c index fd14d5ebccbc..7b32935273ce 100644 --- a/arch/s390/kernel/perf_pai_ext.c +++ b/arch/s390/kernel/perf_pai_ext.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -266,7 +265,7 @@ static int paiext_event_valid(struct perf_event *event) event->hw.config_base = offsetof(struct paiext_cb, acc); return 0; } - return -EINVAL; + return -ENOENT; } /* Might be called on different CPU than the one the event is intended for. */ diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 9637aee43c40..f55f09cda6f8 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index e1240f6b29fa..494216c4b4f3 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -1209,7 +1209,7 @@ static int s390_runtime_instr_set(struct task_struct *target, static const struct user_regset s390_regsets[] = { { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(s390_regs) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -1217,7 +1217,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_regs_set, }, { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(s390_fp_regs) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -1225,7 +1225,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_fpregs_set, }, { - .core_note_type = NT_S390_SYSTEM_CALL, + USER_REGSET_NOTE_TYPE(S390_SYSTEM_CALL), .n = 1, .size = sizeof(unsigned int), .align = sizeof(unsigned int), @@ -1233,7 +1233,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_system_call_set, }, { - .core_note_type = NT_S390_LAST_BREAK, + USER_REGSET_NOTE_TYPE(S390_LAST_BREAK), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -1241,7 +1241,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_last_break_set, }, { - .core_note_type = NT_S390_TDB, + USER_REGSET_NOTE_TYPE(S390_TDB), .n = 1, .size = 256, .align = 1, @@ -1249,7 +1249,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_tdb_set, }, { - .core_note_type = NT_S390_VXRS_LOW, + USER_REGSET_NOTE_TYPE(S390_VXRS_LOW), .n = __NUM_VXRS_LOW, .size = sizeof(__u64), .align = sizeof(__u64), @@ -1257,7 +1257,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_vxrs_low_set, }, { - .core_note_type = NT_S390_VXRS_HIGH, + USER_REGSET_NOTE_TYPE(S390_VXRS_HIGH), .n = __NUM_VXRS_HIGH, .size = sizeof(__vector128), .align = sizeof(__vector128), @@ -1265,7 +1265,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_vxrs_high_set, }, { - .core_note_type = NT_S390_GS_CB, + USER_REGSET_NOTE_TYPE(S390_GS_CB), .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), @@ -1273,7 +1273,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_gs_cb_set, }, { - .core_note_type = NT_S390_GS_BC, + USER_REGSET_NOTE_TYPE(S390_GS_BC), .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), @@ -1281,7 +1281,7 @@ static const struct user_regset s390_regsets[] = { .set = s390_gs_bc_set, }, { - .core_note_type = NT_S390_RI_CB, + USER_REGSET_NOTE_TYPE(S390_RI_CB), .n = sizeof(struct runtime_instr_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), @@ -1413,7 +1413,7 @@ static int s390_compat_last_break_set(struct task_struct *target, static const struct user_regset s390_compat_regsets[] = { { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), @@ -1421,7 +1421,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_compat_regs_set, }, { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), @@ -1429,7 +1429,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_fpregs_set, }, { - .core_note_type = NT_S390_SYSTEM_CALL, + USER_REGSET_NOTE_TYPE(S390_SYSTEM_CALL), .n = 1, .size = sizeof(compat_uint_t), .align = sizeof(compat_uint_t), @@ -1437,7 +1437,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_system_call_set, }, { - .core_note_type = NT_S390_LAST_BREAK, + USER_REGSET_NOTE_TYPE(S390_LAST_BREAK), .n = 1, .size = sizeof(long), .align = sizeof(long), @@ -1445,7 +1445,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_compat_last_break_set, }, { - .core_note_type = NT_S390_TDB, + USER_REGSET_NOTE_TYPE(S390_TDB), .n = 1, .size = 256, .align = 1, @@ -1453,7 +1453,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_tdb_set, }, { - .core_note_type = NT_S390_VXRS_LOW, + USER_REGSET_NOTE_TYPE(S390_VXRS_LOW), .n = __NUM_VXRS_LOW, .size = sizeof(__u64), .align = sizeof(__u64), @@ -1461,7 +1461,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_vxrs_low_set, }, { - .core_note_type = NT_S390_VXRS_HIGH, + USER_REGSET_NOTE_TYPE(S390_VXRS_HIGH), .n = __NUM_VXRS_HIGH, .size = sizeof(__vector128), .align = sizeof(__vector128), @@ -1469,7 +1469,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_vxrs_high_set, }, { - .core_note_type = NT_S390_HIGH_GPRS, + USER_REGSET_NOTE_TYPE(S390_HIGH_GPRS), .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), @@ -1477,7 +1477,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_compat_regs_high_set, }, { - .core_note_type = NT_S390_GS_CB, + USER_REGSET_NOTE_TYPE(S390_GS_CB), .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), @@ -1485,7 +1485,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_gs_cb_set, }, { - .core_note_type = NT_S390_GS_BC, + USER_REGSET_NOTE_TYPE(S390_GS_BC), .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), @@ -1493,7 +1493,7 @@ static const struct user_regset s390_compat_regsets[] = { .set = s390_gs_bc_set, }, { - .core_note_type = NT_S390_RI_CB, + USER_REGSET_NOTE_TYPE(S390_RI_CB), .n = sizeof(struct runtime_instr_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index f244c5560e7f..7b529868789f 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -605,7 +605,7 @@ static void __init reserve_crashkernel(void) int rc; rc = parse_crashkernel(boot_command_line, ident_map_size, - &crash_size, &crash_base, NULL, NULL); + &crash_size, &crash_base, NULL, NULL, NULL); crash_base = ALIGN(crash_base, KEXEC_CRASH_MEM_ALIGN); crash_size = ALIGN(crash_size, KEXEC_CRASH_MEM_ALIGN); @@ -719,6 +719,11 @@ static void __init memblock_add_physmem_info(void) memblock_set_node(0, ULONG_MAX, &memblock.memory, 0); } +static void __init setup_high_memory(void) +{ + high_memory = __va(ident_map_size); +} + /* * Reserve memory used for lowcore. */ @@ -951,6 +956,7 @@ void __init setup_arch(char **cmdline_p) free_physmem_info(); setup_memory_end(); + setup_high_memory(); memblock_dump_all(); setup_memory(); diff --git a/arch/s390/kernel/skey.c b/arch/s390/kernel/skey.c new file mode 100644 index 000000000000..ba049fd103c2 --- /dev/null +++ b/arch/s390/kernel/skey.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include + +int skey_regions_initialized; + +static inline unsigned long load_real_address(unsigned long address) +{ + unsigned long real; + + asm volatile( + " lra %[real],0(%[address])\n" + : [real] "=d" (real) + : [address] "a" (address) + : "cc"); + return real; +} + +/* + * Initialize storage keys of registered memory regions with the + * default key. This is useful for code which is executed with a + * non-default access key. + */ +void __skey_regions_initialize(void) +{ + unsigned long address, real; + struct skey_region *r, *end; + + r = __skey_region_start; + end = __skey_region_end; + while (r < end) { + address = r->start & PAGE_MASK; + do { + real = load_real_address(address); + page_set_storage_key(real, PAGE_DEFAULT_KEY, 1); + address += PAGE_SIZE; + } while (address < r->end); + r++; + } + /* + * Make sure storage keys are initialized before + * skey_regions_initialized is changed. + */ + barrier(); + WRITE_ONCE(skey_regions_initialized, 1); +} diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 81f12bb77f62..e88ebe5339fc 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -175,13 +175,10 @@ static struct pcpu *pcpu_find_address(const struct cpumask *mask, u16 address) static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit) { - int order; - if (test_and_set_bit(ec_bit, &pcpu->ec_mask)) return; - order = pcpu_running(pcpu) ? SIGP_EXTERNAL_CALL : SIGP_EMERGENCY_SIGNAL; pcpu->ec_clk = get_tod_clock_fast(); - pcpu_sigp_retry(pcpu, order, 0); + pcpu_sigp_retry(pcpu, SIGP_EXTERNAL_CALL, 0); } static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) @@ -433,16 +430,16 @@ void notrace smp_emergency_stop(void) cpumask_copy(&cpumask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &cpumask); - end = get_tod_clock() + (1000000UL << 12); + end = get_tod_clock_monotonic() + (1000000UL << 12); for_each_cpu(cpu, &cpumask) { struct pcpu *pcpu = per_cpu_ptr(&pcpu_devices, cpu); set_bit(ec_stop_cpu, &pcpu->ec_mask); while (__pcpu_sigp(pcpu->address, SIGP_EMERGENCY_SIGNAL, 0, NULL) == SIGP_CC_BUSY && - get_tod_clock() < end) + get_tod_clock_monotonic() < end) cpu_relax(); } - while (get_tod_clock() < end) { + while (get_tod_clock_monotonic() < end) { for_each_cpu(cpu, &cpumask) if (pcpu_stopped(per_cpu_ptr(&pcpu_devices, cpu))) cpumask_clear_cpu(cpu, &cpumask); diff --git a/arch/s390/kernel/sthyi.c b/arch/s390/kernel/sthyi.c index d40f0b983e74..f4ccdbed4b89 100644 --- a/arch/s390/kernel/sthyi.c +++ b/arch/s390/kernel/sthyi.c @@ -5,6 +5,8 @@ * Copyright IBM Corp. 2016 * Author(s): Janosch Frank */ + +#include #include #include #include diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl index a4569b96ef06..8a6744d658db 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl @@ -470,3 +470,5 @@ 465 common listxattrat sys_listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr sys_file_setattr diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index fed17d407a44..63517b85f4c9 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -69,8 +69,6 @@ unsigned char ptff_function_mask[16]; static unsigned long lpar_offset; static unsigned long initial_leap_seconds; -static unsigned long tod_steering_end; -static long tod_steering_delta; /* * Get time offsets with PTFF @@ -80,9 +78,7 @@ void __init time_early_init(void) struct ptff_qto qto; struct ptff_qui qui; - /* Initialize TOD steering parameters */ - tod_steering_end = tod_clock_base.tod; - vdso_k_time_data->arch_data.tod_steering_end = tod_steering_end; + vdso_k_time_data->arch_data.tod_delta = tod_clock_base.tod; if (!test_facility(28)) return; @@ -226,21 +222,7 @@ void __init read_persistent_wall_and_boot_offset(struct timespec64 *wall_time, static u64 read_tod_clock(struct clocksource *cs) { - unsigned long now, adj; - - preempt_disable(); /* protect from changes to steering parameters */ - now = get_tod_clock(); - adj = tod_steering_end - now; - if (unlikely((s64) adj > 0)) - /* - * manually steer by 1 cycle every 2^16 cycles. This - * corresponds to shifting the tod delta by 15. 1s is - * therefore steered in ~9h. The adjust will decrease - * over time, until it finally reaches 0. - */ - now += (tod_steering_delta < 0) ? (adj >> 15) : -(adj >> 15); - preempt_enable(); - return now; + return get_tod_clock_monotonic(); } static struct clocksource clocksource_tod = { @@ -369,26 +351,11 @@ static inline int check_sync_clock(void) */ static void clock_sync_global(long delta) { - unsigned long now, adj; struct ptff_qto qto; /* Fixup the monotonic sched clock. */ tod_clock_base.eitod += delta; - /* Adjust TOD steering parameters. */ - now = get_tod_clock(); - adj = tod_steering_end - now; - if (unlikely((s64) adj >= 0)) - /* Calculate how much of the old adjustment is left. */ - tod_steering_delta = (tod_steering_delta < 0) ? - -(adj >> 15) : (adj >> 15); - tod_steering_delta += delta; - if ((abs(tod_steering_delta) >> 48) != 0) - panic("TOD clock sync offset %li is too large to drift\n", - tod_steering_delta); - tod_steering_end = now + (abs(tod_steering_delta) << 15); - vdso_k_time_data->arch_data.tod_steering_end = tod_steering_end; - vdso_k_time_data->arch_data.tod_steering_delta = tod_steering_delta; - + vdso_k_time_data->arch_data.tod_delta = tod_clock_base.tod; /* Update LPAR offset. */ if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0) lpar_offset = qto.tod_epoch_difference; @@ -430,7 +397,7 @@ struct clock_sync_data { /* * Server Time Protocol (STP) code. */ -static bool stp_online; +static bool stp_online = true; static struct stp_sstpi stp_info; static void *stp_page; @@ -456,7 +423,6 @@ static void __init stp_reset(void) if (rc == 0) set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags); else if (stp_online) { - pr_warn("The real or virtual hardware system does not provide an STP interface\n"); free_page((unsigned long) stp_page); stp_page = NULL; stp_online = false; @@ -580,7 +546,7 @@ static int stp_sync_clock(void *data) atomic_dec(&sync->cpus); /* Wait for in_sync to be set. */ while (READ_ONCE(sync->in_sync) == 0) - __udelay(1); + ; } if (sync->in_sync != 1) /* Didn't work. Clear per-cpu in sync bit again. */ @@ -591,81 +557,6 @@ static int stp_sync_clock(void *data) return 0; } -static int stp_clear_leap(void) -{ - struct __kernel_timex txc; - int ret; - - memset(&txc, 0, sizeof(txc)); - - ret = do_adjtimex(&txc); - if (ret < 0) - return ret; - - txc.modes = ADJ_STATUS; - txc.status &= ~(STA_INS|STA_DEL); - return do_adjtimex(&txc); -} - -static void stp_check_leap(void) -{ - struct stp_stzi stzi; - struct stp_lsoib *lsoib = &stzi.lsoib; - struct __kernel_timex txc; - int64_t timediff; - int leapdiff, ret; - - if (!stp_info.lu || !check_sync_clock()) { - /* - * Either a scheduled leap second was removed by the operator, - * or STP is out of sync. In both cases, clear the leap second - * kernel flags. - */ - if (stp_clear_leap() < 0) - pr_err("failed to clear leap second flags\n"); - return; - } - - if (chsc_stzi(stp_page, &stzi, sizeof(stzi))) { - pr_err("stzi failed\n"); - return; - } - - timediff = tod_to_ns(lsoib->nlsout - get_tod_clock()) / NSEC_PER_SEC; - leapdiff = lsoib->nlso - lsoib->also; - - if (leapdiff != 1 && leapdiff != -1) { - pr_err("Cannot schedule %d leap seconds\n", leapdiff); - return; - } - - if (timediff < 0) { - if (stp_clear_leap() < 0) - pr_err("failed to clear leap second flags\n"); - } else if (timediff < 7200) { - memset(&txc, 0, sizeof(txc)); - ret = do_adjtimex(&txc); - if (ret < 0) - return; - - txc.modes = ADJ_STATUS; - if (leapdiff > 0) - txc.status |= STA_INS; - else - txc.status |= STA_DEL; - ret = do_adjtimex(&txc); - if (ret < 0) - pr_err("failed to set leap second flags\n"); - /* arm Timer to clear leap second flags */ - mod_timer(&stp_timer, jiffies + secs_to_jiffies(14400)); - } else { - /* The day the leap second is scheduled for hasn't been reached. Retry - * in one hour. - */ - mod_timer(&stp_timer, jiffies + secs_to_jiffies(3600)); - } -} - /* * STP work. Check for the STP state and take over the clock * synchronization if the STP clock source is usable. @@ -707,8 +598,6 @@ static void stp_work_fn(struct work_struct *work) * Retry after a second. */ mod_timer(&stp_timer, jiffies + msecs_to_jiffies(MSEC_PER_SEC)); - else if (stp_info.lu) - stp_check_leap(); out_unlock: mutex_unlock(&stp_mutex); diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 3df048e190b1..46569b8e47dd 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -531,11 +531,11 @@ static const struct cpumask *cpu_drawer_mask(int cpu) } static struct sched_domain_topology_level s390_topology[] = { - { cpu_thread_mask, cpu_smt_flags, SD_INIT_NAME(SMT) }, - { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) }, - { cpu_book_mask, SD_INIT_NAME(BOOK) }, - { cpu_drawer_mask, SD_INIT_NAME(DRAWER) }, - { cpu_cpu_mask, SD_INIT_NAME(PKG) }, + SDTL_INIT(cpu_thread_mask, cpu_smt_flags, SMT), + SDTL_INIT(cpu_coregroup_mask, cpu_core_flags, MC), + SDTL_INIT(cpu_book_mask, NULL, BOOK), + SDTL_INIT(cpu_drawer_mask, NULL, DRAWER), + SDTL_INIT(cpu_cpu_mask, NULL, PKG), { NULL, }, }; diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index cd44be2b6ce8..0f88caca4eaf 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +#include #include #include #include diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index b99478e84da4..47f574cd1728 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -7,6 +7,7 @@ #define KMSG_COMPONENT "prot_virt" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index ff1ddba96352..1c606dfa595d 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -71,6 +71,13 @@ SECTIONS . = ALIGN(PAGE_SIZE); __end_ro_after_init = .; + . = ALIGN(8); + .skey_region_table : { + __skey_region_start = .; + KEEP(*(.skey_region)) + __skey_region_end = .; + } + .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 60c360c18690..2a92a8b9e4c2 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index d5ad10791c25..bf6fa8b9ca73 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include +#include #include #include #include @@ -5062,6 +5063,30 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason) return vcpu_post_run_handle_fault(vcpu); } +int noinstr kvm_s390_enter_exit_sie(struct kvm_s390_sie_block *scb, + u64 *gprs, unsigned long gasce) +{ + int ret; + + guest_state_enter_irqoff(); + + /* + * The guest_state_{enter,exit}_irqoff() functions inform lockdep and + * tracing that entry to the guest will enable host IRQs, and exit from + * the guest will disable host IRQs. + * + * We must not use lockdep/tracing/RCU in this critical section, so we + * use the low-level arch_local_irq_*() helpers to enable/disable IRQs. + */ + arch_local_irq_enable(); + ret = sie64a(scb, gprs, gasce); + arch_local_irq_disable(); + + guest_state_exit_irqoff(); + + return ret; +} + #define PSW_INT_MASK (PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_MCHECK) static int __vcpu_run(struct kvm_vcpu *vcpu) { @@ -5082,20 +5107,27 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) kvm_vcpu_srcu_read_unlock(vcpu); /* * As PF_VCPU will be used in fault handler, between - * guest_enter and guest_exit should be no uaccess. + * guest_timing_enter_irqoff and guest_timing_exit_irqoff + * should be no uaccess. */ - local_irq_disable(); - guest_enter_irqoff(); - __disable_cpu_timer_accounting(vcpu); - local_irq_enable(); if (kvm_s390_pv_cpu_is_protected(vcpu)) { memcpy(sie_page->pv_grregs, vcpu->run->s.regs.gprs, sizeof(sie_page->pv_grregs)); } - exit_reason = sie64a(vcpu->arch.sie_block, - vcpu->run->s.regs.gprs, - vcpu->arch.gmap->asce); + + local_irq_disable(); + guest_timing_enter_irqoff(); + __disable_cpu_timer_accounting(vcpu); + + exit_reason = kvm_s390_enter_exit_sie(vcpu->arch.sie_block, + vcpu->run->s.regs.gprs, + vcpu->arch.gmap->asce); + + __enable_cpu_timer_accounting(vcpu); + guest_timing_exit_irqoff(); + local_irq_enable(); + if (kvm_s390_pv_cpu_is_protected(vcpu)) { memcpy(vcpu->run->s.regs.gprs, sie_page->pv_grregs, @@ -5111,10 +5143,6 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->gpsw.mask &= ~PSW_INT_MASK; } } - local_irq_disable(); - __enable_cpu_timer_accounting(vcpu); - guest_exit_irqoff(); - local_irq_enable(); kvm_vcpu_srcu_read_lock(vcpu); rc = vcpu_post_run(vcpu, exit_reason); diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 14c330ec8ceb..25ede8354514 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -5,6 +5,8 @@ * Copyright IBM Corp. 2019, 2020 * Author(s): Janosch Frank */ + +#include #include #include #include diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index 13a9661d2b28..347268f89f2f 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -1170,10 +1170,6 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) vcpu->arch.sie_block->fpf & FPF_BPBC) set_thread_flag(TIF_ISOLATE_BP_GUEST); - local_irq_disable(); - guest_enter_irqoff(); - local_irq_enable(); - /* * Simulate a SIE entry of the VCPU (see sie64a), so VCPU blocking * and VCPU requests also hinder the vSIE from running and lead @@ -1183,15 +1179,16 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) vcpu->arch.sie_block->prog0c |= PROG_IN_SIE; current->thread.gmap_int_code = 0; barrier(); - if (!kvm_s390_vcpu_sie_inhibited(vcpu)) - rc = sie64a(scb_s, vcpu->run->s.regs.gprs, vsie_page->gmap->asce); + if (!kvm_s390_vcpu_sie_inhibited(vcpu)) { + local_irq_disable(); + guest_timing_enter_irqoff(); + rc = kvm_s390_enter_exit_sie(scb_s, vcpu->run->s.regs.gprs, vsie_page->gmap->asce); + guest_timing_exit_irqoff(); + local_irq_enable(); + } barrier(); vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE; - local_irq_disable(); - guest_exit_irqoff(); - local_irq_enable(); - /* restore guest state for bp isolation override */ if (!guest_bp_isolation) clear_thread_flag(TIF_ISOLATE_BP_GUEST); diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index cd35cdbfa871..f43f897d3fc0 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -3,7 +3,6 @@ # Makefile for s390-specific library files.. # -obj-y += crypto/ 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 @@ -25,6 +24,3 @@ obj-$(CONFIG_S390_MODULES_SANITY_TEST_HELPERS) += test_modules_helpers.o lib-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o obj-$(CONFIG_EXPOLINE_EXTERN) += expoline.o - -obj-$(CONFIG_CRC32_ARCH) += crc32-s390.o -crc32-s390-y := crc32.o crc32le-vx.o crc32be-vx.o diff --git a/arch/s390/lib/crypto/sha256.c b/arch/s390/lib/crypto/sha256.c deleted file mode 100644 index 7dfe120fafab..000000000000 --- a/arch/s390/lib/crypto/sha256.c +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-256 optimized using the CP Assist for Cryptographic Functions (CPACF) - * - * Copyright 2025 Google LLC - */ -#include -#include -#include -#include -#include - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_cpacf_sha256); - -void sha256_blocks_arch(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - if (static_branch_likely(&have_cpacf_sha256)) - cpacf_kimd(CPACF_KIMD_SHA_256, state, data, - nblocks * SHA256_BLOCK_SIZE); - else - sha256_blocks_generic(state, data, nblocks); -} -EXPORT_SYMBOL_GPL(sha256_blocks_arch); - -bool sha256_is_arch_optimized(void) -{ - return static_key_enabled(&have_cpacf_sha256); -} -EXPORT_SYMBOL_GPL(sha256_is_arch_optimized); - -static int __init sha256_s390_mod_init(void) -{ - if (cpu_have_feature(S390_CPU_FEATURE_MSA) && - cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_256)) - static_branch_enable(&have_cpacf_sha256); - return 0; -} -subsys_initcall(sha256_s390_mod_init); - -static void __exit sha256_s390_mod_exit(void) -{ -} -module_exit(sha256_s390_mod_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-256 using the CP Assist for Cryptographic Functions (CPACF)"); diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index be14c58cb989..c1ea14e3c927 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/arch/s390/lib/uaccess.c b/arch/s390/lib/uaccess.c index fa7d98fa1320..1a6ba105e071 100644 --- a/arch/s390/lib/uaccess.c +++ b/arch/s390/lib/uaccess.c @@ -8,11 +8,13 @@ * Gerald Schaefer (gerald.schaefer@de.ibm.com) */ +#include #include #include #include #include #include +#include #ifdef CONFIG_DEBUG_ENTRY void debug_user_asce(int exit) @@ -145,3 +147,189 @@ unsigned long _copy_to_user_key(void __user *to, const void *from, return raw_copy_to_user_key(to, from, n, key); } EXPORT_SYMBOL(_copy_to_user_key); + +#define CMPXCHG_USER_KEY_MAX_LOOPS 128 + +static nokprobe_inline int __cmpxchg_user_key_small(unsigned long address, unsigned int *uval, + unsigned int old, unsigned int new, + unsigned int mask, unsigned long key) +{ + unsigned long count; + unsigned int prev; + bool sacf_flag; + int rc = 0; + + skey_regions_initialize(); + sacf_flag = enable_sacf_uaccess(); + asm_inline volatile( + "20: spka 0(%[key])\n" + " sacf 256\n" + " llill %[count],%[max_loops]\n" + "0: l %[prev],%[address]\n" + "1: nr %[prev],%[mask]\n" + " xilf %[mask],0xffffffff\n" + " or %[new],%[prev]\n" + " or %[prev],%[tmp]\n" + "2: lr %[tmp],%[prev]\n" + "3: cs %[prev],%[new],%[address]\n" + "4: jnl 5f\n" + " xr %[tmp],%[prev]\n" + " xr %[new],%[tmp]\n" + " nr %[tmp],%[mask]\n" + " jnz 5f\n" + " brct %[count],2b\n" + "5: sacf 768\n" + " spka %[default_key]\n" + "21:\n" + EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev]) + SKEY_REGION(20b, 21b) + : [rc] "+&d" (rc), + [prev] "=&d" (prev), + [address] "+Q" (*(int *)address), + [tmp] "+&d" (old), + [new] "+&d" (new), + [mask] "+&d" (mask), + [count] "=a" (count) + : [key] "%[count]" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY), + [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS) + : "memory", "cc"); + disable_sacf_uaccess(sacf_flag); + *uval = prev; + if (!count) + rc = -EAGAIN; + return rc; +} + +int __kprobes __cmpxchg_user_key1(unsigned long address, unsigned char *uval, + unsigned char old, unsigned char new, unsigned long key) +{ + unsigned int prev, shift, mask, _old, _new; + int rc; + + shift = (3 ^ (address & 3)) << 3; + address ^= address & 3; + _old = (unsigned int)old << shift; + _new = (unsigned int)new << shift; + mask = ~(0xff << shift); + rc = __cmpxchg_user_key_small(address, &prev, _old, _new, mask, key); + *uval = prev >> shift; + return rc; +} +EXPORT_SYMBOL(__cmpxchg_user_key1); + +int __kprobes __cmpxchg_user_key2(unsigned long address, unsigned short *uval, + unsigned short old, unsigned short new, unsigned long key) +{ + unsigned int prev, shift, mask, _old, _new; + int rc; + + shift = (2 ^ (address & 2)) << 3; + address ^= address & 2; + _old = (unsigned int)old << shift; + _new = (unsigned int)new << shift; + mask = ~(0xffff << shift); + rc = __cmpxchg_user_key_small(address, &prev, _old, _new, mask, key); + *uval = prev >> shift; + return rc; +} +EXPORT_SYMBOL(__cmpxchg_user_key2); + +int __kprobes __cmpxchg_user_key4(unsigned long address, unsigned int *uval, + unsigned int old, unsigned int new, unsigned long key) +{ + unsigned int prev = old; + bool sacf_flag; + int rc = 0; + + skey_regions_initialize(); + sacf_flag = enable_sacf_uaccess(); + asm_inline volatile( + "20: spka 0(%[key])\n" + " sacf 256\n" + "0: cs %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + "21:\n" + EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) + SKEY_REGION(20b, 21b) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+Q" (*(int *)address) + : [new] "d" (new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + disable_sacf_uaccess(sacf_flag); + *uval = prev; + return rc; +} +EXPORT_SYMBOL(__cmpxchg_user_key4); + +int __kprobes __cmpxchg_user_key8(unsigned long address, unsigned long *uval, + unsigned long old, unsigned long new, unsigned long key) +{ + unsigned long prev = old; + bool sacf_flag; + int rc = 0; + + skey_regions_initialize(); + sacf_flag = enable_sacf_uaccess(); + asm_inline volatile( + "20: spka 0(%[key])\n" + " sacf 256\n" + "0: csg %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + "21:\n" + EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) + SKEY_REGION(20b, 21b) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+QS" (*(long *)address) + : [new] "d" (new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + disable_sacf_uaccess(sacf_flag); + *uval = prev; + return rc; +} +EXPORT_SYMBOL(__cmpxchg_user_key8); + +int __kprobes __cmpxchg_user_key16(unsigned long address, __uint128_t *uval, + __uint128_t old, __uint128_t new, unsigned long key) +{ + __uint128_t prev = old; + bool sacf_flag; + int rc = 0; + + skey_regions_initialize(); + sacf_flag = enable_sacf_uaccess(); + asm_inline volatile( + "20: spka 0(%[key])\n" + " sacf 256\n" + "0: cdsg %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + "21:\n" + EX_TABLE_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REGPAIR(1b, 1b, %[rc], %[prev]) + SKEY_REGION(20b, 21b) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+QS" (*(__int128_t *)address) + : [new] "d" (new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + disable_sacf_uaccess(sacf_flag); + *uval = prev; + return rc; +} +EXPORT_SYMBOL(__cmpxchg_user_key16); diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index ac604b176660..9af2aae0a515 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c @@ -247,11 +247,9 @@ static int ptdump_show(struct seq_file *m, void *v) .marker = markers, }; - get_online_mems(); mutex_lock(&cpa_mutex); ptdump_walk_pgd(&st.ptdump, &init_mm, NULL); mutex_unlock(&cpa_mutex); - put_online_mems(); return 0; } DEFINE_SHOW_ATTRIBUTE(ptdump); diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c index 012a4366a2ad..c7defe4ed1f6 100644 --- a/arch/s390/mm/gmap.c +++ b/arch/s390/mm/gmap.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index a45d417ad951..b63f427e7289 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -4,6 +4,8 @@ * * Copyright IBM Corp. 2007, 2025 */ + +#include #include #include #include diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 074bf4fb4ce2..e4953453d254 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -142,7 +142,7 @@ bool force_dma_unencrypted(struct device *dev) } /* protected virtualization */ -static void pv_init(void) +static void __init pv_init(void) { if (!is_prot_virt_guest()) return; diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c index b449fd2605b0..d2f6f1f6d2fc 100644 --- a/arch/s390/mm/pgalloc.c +++ b/arch/s390/mm/pgalloc.c @@ -173,11 +173,6 @@ void pte_free_defer(struct mm_struct *mm, pgtable_t pgtable) struct ptdesc *ptdesc = virt_to_ptdesc(pgtable); call_rcu(&ptdesc->pt_rcu_head, pte_free_now); - /* - * THPs are not allowed for KVM guests. Warn if pgste ever reaches here. - * Turn to the generic pte_free_defer() version once gmap is removed. - */ - WARN_ON_ONCE(mm_has_pgste(mm)); } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 7df70cd8f739..50eb57c976bc 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -334,7 +335,6 @@ pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, int nodat; struct mm_struct *mm = vma->vm_mm; - preempt_disable(); pgste = ptep_xchg_start(mm, addr, ptep); nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); old = ptep_flush_lazy(mm, addr, ptep, nodat); @@ -359,7 +359,6 @@ void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, } else { set_pte(ptep, pte); } - preempt_enable(); } static inline void pmdp_idte_local(struct mm_struct *mm, diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 448dd6ed1069..f48ef361bc83 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -64,13 +64,12 @@ void *vmem_crst_alloc(unsigned long val) pte_t __ref *vmem_pte_alloc(void) { - unsigned long size = PTRS_PER_PTE * sizeof(pte_t); pte_t *pte; if (slab_is_available()) - pte = (pte_t *) page_table_alloc(&init_mm); + pte = (pte_t *)page_table_alloc(&init_mm); else - pte = (pte_t *) memblock_alloc(size, size); + pte = (pte_t *)memblock_alloc(PAGE_SIZE, PAGE_SIZE); if (!pte) return NULL; memset64((u64 *)pte, _PAGE_INVALID, PTRS_PER_PTE); diff --git a/arch/s390/net/bpf_jit.h b/arch/s390/net/bpf_jit.h deleted file mode 100644 index 7822ea92e54a..000000000000 --- a/arch/s390/net/bpf_jit.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * BPF Jit compiler defines - * - * Copyright IBM Corp. 2012,2015 - * - * Author(s): Martin Schwidefsky - * Michael Holzheu - */ - -#ifndef __ARCH_S390_NET_BPF_JIT_H -#define __ARCH_S390_NET_BPF_JIT_H - -#ifndef __ASSEMBLY__ - -#include -#include - -#endif /* __ASSEMBLY__ */ - -/* - * Stackframe layout (packed stack): - * - * ^ high - * +---------------+ | - * | old backchain | | - * +---------------+ | - * | r15 - r6 | | - * +---------------+ | - * | 4 byte align | | - * | tail_call_cnt | | - * BFP -> +===============+ | - * | | | - * | BPF stack | | - * | | | - * R15+160 -> +---------------+ | - * | new backchain | | - * R15+152 -> +---------------+ | - * | + 152 byte SA | | - * R15 -> +---------------+ + low - * - * We get 160 bytes stack space from calling function, but only use - * 12 * 8 byte for old backchain, r15..r6, and tail_call_cnt. - * - * The stack size used by the BPF program ("BPF stack" above) is passed - * via "aux->stack_depth". - */ -#define STK_SPACE_ADD (160) -#define STK_160_UNUSED (160 - 12 * 8) -#define STK_OFF (STK_SPACE_ADD - STK_160_UNUSED) - -#define STK_OFF_R6 (160 - 11 * 8) /* Offset of r6 on stack */ -#define STK_OFF_TCCNT (160 - 12 * 8) /* Offset of tail_call_cnt on stack */ - -#endif /* __ARCH_S390_NET_BPF_JIT_H */ diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index c7f8313ba449..bb17efe29d65 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -32,7 +32,6 @@ #include #include #include -#include "bpf_jit.h" struct bpf_jit { u32 seen; /* Flags to remember seen eBPF instructions */ @@ -54,6 +53,7 @@ struct bpf_jit { int prologue_plt; /* Start of prologue hotpatch PLT */ int kern_arena; /* Pool offset of kernel arena address */ u64 user_arena; /* User arena address */ + u32 frame_off; /* Offset of struct bpf_prog from %r15 */ }; #define SEEN_MEM BIT(0) /* use mem[] for temporary storage */ @@ -425,12 +425,26 @@ static void jit_fill_hole(void *area, unsigned int size) memset(area, 0, size); } +/* + * Caller-allocated part of the frame. + * Thanks to packed stack, its otherwise unused initial part can be used for + * the BPF stack and for the next frame. + */ +struct prog_frame { + u64 unused[8]; + /* BPF stack starts here and grows towards 0 */ + u32 tail_call_cnt; + u32 pad; + u64 r6[10]; /* r6 - r15 */ + u64 backchain; +} __packed; + /* * Save registers from "rs" (register start) to "re" (register end) on stack */ static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) { - u32 off = STK_OFF_R6 + (rs - 6) * 8; + u32 off = offsetof(struct prog_frame, r6) + (rs - 6) * 8; if (rs == re) /* stg %rs,off(%r15) */ @@ -443,12 +457,9 @@ static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) /* * Restore registers from "rs" (register start) to "re" (register end) on stack */ -static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re, u32 stack_depth) +static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re) { - u32 off = STK_OFF_R6 + (rs - 6) * 8; - - if (jit->seen & SEEN_STACK) - off += STK_OFF + stack_depth; + u32 off = jit->frame_off + offsetof(struct prog_frame, r6) + (rs - 6) * 8; if (rs == re) /* lg %rs,off(%r15) */ @@ -492,8 +503,7 @@ static int get_end(u16 seen_regs, int start) * Save and restore clobbered registers (6-15) on stack. * We save/restore registers in chunks with gap >= 2 registers. */ -static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth, - u16 extra_regs) +static void save_restore_regs(struct bpf_jit *jit, int op, u16 extra_regs) { u16 seen_regs = jit->seen_regs | extra_regs; const int last = 15, save_restore_size = 6; @@ -516,7 +526,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth, if (op == REGS_SAVE) save_regs(jit, rs, re); else - restore_regs(jit, rs, re, stack_depth); + restore_regs(jit, rs, re); re++; } while (re <= last); } @@ -566,18 +576,27 @@ static void bpf_jit_plt(struct bpf_plt *plt, void *ret, void *target) { memcpy(plt, &bpf_plt, sizeof(*plt)); plt->ret = ret; - plt->target = target; + /* + * (target == NULL) implies that the branch to this PLT entry was + * patched and became a no-op. However, some CPU could have jumped + * to this PLT entry before patching and may be still executing it. + * + * Since the intention in this case is to make the PLT entry a no-op, + * make the target point to the return label instead of NULL. + */ + plt->target = target ?: ret; } /* * Emit function prologue * * Save registers and create stack frame if necessary. - * See stack frame layout description in "bpf_jit.h"! + * Stack frame layout is described by struct prog_frame. */ -static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, - u32 stack_depth) +static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp) { + BUILD_BUG_ON(sizeof(struct prog_frame) != STACK_FRAME_OVERHEAD); + /* No-op for hotpatching */ /* brcl 0,prologue_plt */ EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt); @@ -585,8 +604,9 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, if (!bpf_is_subprog(fp)) { /* Initialize the tail call counter in the main program. */ - /* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */ - _EMIT6(0xd703f000 | STK_OFF_TCCNT, 0xf000 | STK_OFF_TCCNT); + /* xc tail_call_cnt(4,%r15),tail_call_cnt(%r15) */ + _EMIT6(0xd703f000 | offsetof(struct prog_frame, tail_call_cnt), + 0xf000 | offsetof(struct prog_frame, tail_call_cnt)); } else { /* * Skip the tail call counter initialization in subprograms. @@ -609,7 +629,7 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, jit->seen_regs |= NVREGS; } else { /* Save registers */ - save_restore_regs(jit, REGS_SAVE, stack_depth, + save_restore_regs(jit, REGS_SAVE, fp->aux->exception_boundary ? NVREGS : 0); } /* Setup literal pool */ @@ -629,13 +649,15 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) { /* lgr %w1,%r15 (backchain) */ EMIT4(0xb9040000, REG_W1, REG_15); - /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ - EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); - /* aghi %r15,-STK_OFF */ - EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); - /* stg %w1,152(%r15) (backchain) */ + /* la %bfp,unused_end(%r15) (BPF frame pointer) */ + EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, + offsetofend(struct prog_frame, unused)); + /* aghi %r15,-frame_off */ + EMIT4_IMM(0xa70b0000, REG_15, -jit->frame_off); + /* stg %w1,backchain(%r15) */ EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, - REG_15, 152); + REG_15, + offsetof(struct prog_frame, backchain)); } } @@ -669,13 +691,13 @@ static void call_r1(struct bpf_jit *jit) /* * Function epilogue */ -static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) +static void bpf_jit_epilogue(struct bpf_jit *jit) { jit->exit_ip = jit->prg; /* Load exit code: lgr %r2,%b0 */ EMIT4(0xb9040000, REG_2, BPF_REG_0); /* Restore registers */ - save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); + save_restore_regs(jit, REGS_RESTORE, 0); EMIT_JUMP_REG(14); jit->prg = ALIGN(jit->prg, 8); @@ -857,7 +879,7 @@ static int sign_extend(struct bpf_jit *jit, int r, u8 size, u8 flags) * stack space for the large switch statement. */ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, - int i, bool extra_pass, u32 stack_depth) + int i, bool extra_pass) { struct bpf_insn *insn = &fp->insnsi[i]; s32 branch_oc_off = insn->off; @@ -1778,9 +1800,10 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, * Note 2: We assume that the verifier does not let us call the * main program, which clears the tail call counter on entry. */ - /* mvc STK_OFF_TCCNT(4,%r15),N(%r15) */ - _EMIT6(0xd203f000 | STK_OFF_TCCNT, - 0xf000 | (STK_OFF_TCCNT + STK_OFF + stack_depth)); + /* mvc tail_call_cnt(4,%r15),frame_off+tail_call_cnt(%r15) */ + _EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt), + 0xf000 | (jit->frame_off + + offsetof(struct prog_frame, tail_call_cnt))); /* Sign-extend the kfunc arguments. */ if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { @@ -1831,10 +1854,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, * goto out; */ - if (jit->seen & SEEN_STACK) - off = STK_OFF_TCCNT + STK_OFF + stack_depth; - else - off = STK_OFF_TCCNT; + off = jit->frame_off + + offsetof(struct prog_frame, tail_call_cnt); /* lhi %w0,1 */ EMIT4_IMM(0xa7080000, REG_W0, 1); /* laal %w1,%w0,off(%r15) */ @@ -1864,7 +1885,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, /* * Restore registers before calling function */ - save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); + save_restore_regs(jit, REGS_RESTORE, 0); /* * goto *(prog->bpf_func + tail_call_start); @@ -2157,7 +2178,7 @@ static int bpf_set_addr(struct bpf_jit *jit, int i) * Compile eBPF program into s390x code */ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp, - bool extra_pass, u32 stack_depth) + bool extra_pass) { int i, insn_count, lit32_size, lit64_size; u64 kern_arena; @@ -2166,24 +2187,30 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp, jit->lit64 = jit->lit64_start; jit->prg = 0; jit->excnt = 0; + if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) + jit->frame_off = sizeof(struct prog_frame) - + offsetofend(struct prog_frame, unused) + + round_up(fp->aux->stack_depth, 8); + else + jit->frame_off = 0; kern_arena = bpf_arena_get_kern_vm_start(fp->aux->arena); if (kern_arena) jit->kern_arena = _EMIT_CONST_U64(kern_arena); jit->user_arena = bpf_arena_get_user_vm_start(fp->aux->arena); - bpf_jit_prologue(jit, fp, stack_depth); + bpf_jit_prologue(jit, fp); if (bpf_set_addr(jit, 0) < 0) return -1; for (i = 0; i < fp->len; i += insn_count) { - insn_count = bpf_jit_insn(jit, fp, i, extra_pass, stack_depth); + insn_count = bpf_jit_insn(jit, fp, i, extra_pass); if (insn_count < 0) return -1; /* Next instruction address */ if (bpf_set_addr(jit, i + insn_count) < 0) return -1; } - bpf_jit_epilogue(jit, stack_depth); + bpf_jit_epilogue(jit); lit32_size = jit->lit32 - jit->lit32_start; lit64_size = jit->lit64 - jit->lit64_start; @@ -2259,7 +2286,6 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit, */ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) { - u32 stack_depth = round_up(fp->aux->stack_depth, 8); struct bpf_prog *tmp, *orig_fp = fp; struct bpf_binary_header *header; struct s390_jit_data *jit_data; @@ -2312,7 +2338,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) * - 3: Calculate program size and addrs array */ for (pass = 1; pass <= 3; pass++) { - if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { + if (bpf_jit_prog(&jit, fp, extra_pass)) { fp = orig_fp; goto free_addrs; } @@ -2326,7 +2352,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) goto free_addrs; } skip_init_ctx: - if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { + if (bpf_jit_prog(&jit, fp, extra_pass)) { bpf_jit_binary_free(header); fp = orig_fp; goto free_addrs; @@ -2646,9 +2672,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, /* stg %r1,backchain_off(%r15) */ EMIT6_DISP_LH(0xe3000000, 0x0024, REG_1, REG_0, REG_15, tjit->backchain_off); - /* mvc tccnt_off(4,%r15),stack_size+STK_OFF_TCCNT(%r15) */ + /* mvc tccnt_off(4,%r15),stack_size+tail_call_cnt(%r15) */ _EMIT6(0xd203f000 | tjit->tccnt_off, - 0xf000 | (tjit->stack_size + STK_OFF_TCCNT)); + 0xf000 | (tjit->stack_size + + offsetof(struct prog_frame, tail_call_cnt))); /* stmg %r2,%rN,fwd_reg_args_off(%r15) */ if (nr_reg_args) EMIT6_DISP_LH(0xeb000000, 0x0024, REG_2, @@ -2785,8 +2812,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, (nr_stack_args * sizeof(u64) - 1) << 16 | tjit->stack_args_off, 0xf000 | tjit->orig_stack_args_off); - /* mvc STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ - _EMIT6(0xd203f000 | STK_OFF_TCCNT, 0xf000 | tjit->tccnt_off); + /* mvc tail_call_cnt(4,%r15),tccnt_off(%r15) */ + _EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt), + 0xf000 | tjit->tccnt_off); /* lgr %r1,%r8 */ EMIT4(0xb9040000, REG_1, REG_8); /* %r1() */ @@ -2843,8 +2871,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, if (flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET)) EMIT6_DISP_LH(0xe3000000, 0x0004, REG_2, REG_0, REG_15, tjit->retval_off); - /* mvc stack_size+STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ - _EMIT6(0xd203f000 | (tjit->stack_size + STK_OFF_TCCNT), + /* mvc stack_size+tail_call_cnt(4,%r15),tccnt_off(%r15) */ + _EMIT6(0xd203f000 | (tjit->stack_size + + offsetof(struct prog_frame, tail_call_cnt)), 0xf000 | tjit->tccnt_off); /* aghi %r15,stack_size */ EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); diff --git a/arch/s390/net/pnet.c b/arch/s390/net/pnet.c index 79211bec0fc8..03089ef479b2 100644 --- a/arch/s390/net/pnet.c +++ b/arch/s390/net/pnet.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index 81bdb54ad5e3..45a1c36c5a54 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 2fbee3887d13..d930416d4c90 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -54,6 +54,7 @@ static inline bool ers_result_indicates_abort(pci_ers_result_t ers_res) case PCI_ERS_RESULT_CAN_RECOVER: case PCI_ERS_RESULT_RECOVERED: case PCI_ERS_RESULT_NEED_RESET: + case PCI_ERS_RESULT_NONE: return false; default: return true; @@ -78,10 +79,6 @@ static bool is_driver_supported(struct pci_driver *driver) return false; if (!driver->err_handler->error_detected) return false; - if (!driver->err_handler->slot_reset) - return false; - if (!driver->err_handler->resume) - return false; return true; } @@ -106,6 +103,10 @@ static pci_ers_result_t zpci_event_do_error_state_clear(struct pci_dev *pdev, struct zpci_dev *zdev = to_zpci(pdev); int rc; + /* The underlying device may have been disabled by the event */ + if (!zdev_enabled(zdev)) + return PCI_ERS_RESULT_NEED_RESET; + pr_info("%s: Unblocking device access for examination\n", pci_name(pdev)); rc = zpci_reset_load_store_blocked(zdev); if (rc) { @@ -114,16 +115,18 @@ static pci_ers_result_t zpci_event_do_error_state_clear(struct pci_dev *pdev, return PCI_ERS_RESULT_NEED_RESET; } - if (driver->err_handler->mmio_enabled) { + if (driver->err_handler->mmio_enabled) ers_res = driver->err_handler->mmio_enabled(pdev); - if (ers_result_indicates_abort(ers_res)) { - pr_info("%s: Automatic recovery failed after MMIO re-enable\n", - pci_name(pdev)); - return ers_res; - } else if (ers_res == PCI_ERS_RESULT_NEED_RESET) { - pr_debug("%s: Driver needs reset to recover\n", pci_name(pdev)); - return ers_res; - } + else + ers_res = PCI_ERS_RESULT_NONE; + + if (ers_result_indicates_abort(ers_res)) { + pr_info("%s: Automatic recovery failed after MMIO re-enable\n", + pci_name(pdev)); + return ers_res; + } else if (ers_res == PCI_ERS_RESULT_NEED_RESET) { + pr_debug("%s: Driver needs reset to recover\n", pci_name(pdev)); + return ers_res; } pr_debug("%s: Unblocking DMA\n", pci_name(pdev)); @@ -150,7 +153,12 @@ static pci_ers_result_t zpci_event_do_reset(struct pci_dev *pdev, return ers_res; } pdev->error_state = pci_channel_io_normal; - ers_res = driver->err_handler->slot_reset(pdev); + + if (driver->err_handler->slot_reset) + ers_res = driver->err_handler->slot_reset(pdev); + else + ers_res = PCI_ERS_RESULT_NONE; + if (ers_result_indicates_abort(ers_res)) { pr_info("%s: Automatic recovery failed after slot reset\n", pci_name(pdev)); return ers_res; @@ -214,7 +222,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev) goto out_unlock; } - if (ers_res == PCI_ERS_RESULT_CAN_RECOVER) { + if (ers_res != PCI_ERS_RESULT_NEED_RESET) { ers_res = zpci_event_do_error_state_clear(pdev, driver); if (ers_result_indicates_abort(ers_res)) { status_str = "failed (abort on MMIO enable)"; @@ -225,6 +233,16 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev) if (ers_res == PCI_ERS_RESULT_NEED_RESET) ers_res = zpci_event_do_reset(pdev, driver); + /* + * ers_res can be PCI_ERS_RESULT_NONE either because the driver + * decided to return it, indicating that it abstains from voting + * on how to recover, or because it didn't implement the callback. + * Both cases assume, that if there is nothing else causing a + * disconnect, we recovered successfully. + */ + if (ers_res == PCI_ERS_RESULT_NONE) + ers_res = PCI_ERS_RESULT_RECOVERED; + if (ers_res != PCI_ERS_RESULT_RECOVERED) { pr_err("%s: Automatic recovery failed; operator intervention is required\n", pci_name(pdev)); @@ -273,6 +291,8 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf) struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); struct pci_dev *pdev = NULL; pci_ers_result_t ers_res; + u32 fh = 0; + int rc; zpci_dbg(3, "err fid:%x, fh:%x, pec:%x\n", ccdf->fid, ccdf->fh, ccdf->pec); @@ -281,6 +301,15 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf) if (zdev) { mutex_lock(&zdev->state_lock); + rc = clp_refresh_fh(zdev->fid, &fh); + if (rc) + goto no_pdev; + if (!fh || ccdf->fh != fh) { + /* Ignore events with stale handles */ + zpci_dbg(3, "err fid:%x, fh:%x (stale %x)\n", + ccdf->fid, fh, ccdf->fh); + goto no_pdev; + } zpci_update_fh(zdev, ccdf->fh); if (zdev->zbus->bus) pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); diff --git a/arch/s390/pci/pci_kvm_hook.c b/arch/s390/pci/pci_kvm_hook.c index ff34baf50a3e..df5b25dbe9ca 100644 --- a/arch/s390/pci/pci_kvm_hook.c +++ b/arch/s390/pci/pci_kvm_hook.c @@ -5,7 +5,9 @@ * Copyright (C) IBM Corp. 2022. All rights reserved. * Author(s): Pierre Morel */ + #include +#include struct zpci_kvm_hook zpci_kvm_hook; EXPORT_SYMBOL_GPL(zpci_kvm_hook); diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 0ecad08e1b1e..0ee0924cfab7 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -218,7 +218,7 @@ static struct attribute *zpci_dev_attrs[] = { const struct attribute_group zpci_attr_group = { .attrs = zpci_dev_attrs, - .bin_attrs_new = zpci_bin_attrs, + .bin_attrs = zpci_bin_attrs, }; static struct attribute *pfip_attrs[] = { diff --git a/arch/s390/purgatory/purgatory.c b/arch/s390/purgatory/purgatory.c index 030efda05dbe..ecb38102187c 100644 --- a/arch/s390/purgatory/purgatory.c +++ b/arch/s390/purgatory/purgatory.c @@ -16,7 +16,7 @@ int verify_sha256_digest(void) { struct kexec_sha_region *ptr, *end; u8 digest[SHA256_DIGEST_SIZE]; - struct sha256_state sctx; + struct sha256_ctx sctx; sha256_init(&sctx); end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions); diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 89185af7bcc9..d5795067befa 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -40,7 +40,6 @@ config SUPERH select HAVE_GUP_FAST if MMU select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_HW_BREAKPOINT select HAVE_IOREMAP_PROT if MMU && !X2TLB select HAVE_KERNEL_BZIP2 diff --git a/arch/sh/Makefile b/arch/sh/Makefile index cab2f9c011a8..7b420424b6d7 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -103,16 +103,16 @@ UTS_MACHINE := sh LDFLAGS_vmlinux += -e _stext ifdef CONFIG_CPU_LITTLE_ENDIAN -ld-bfd := elf32-sh-linux -LDFLAGS_vmlinux += --defsym jiffies=jiffies_64 --oformat $(ld-bfd) +ld_bfd := elf32-sh-linux +LDFLAGS_vmlinux += --defsym jiffies=jiffies_64 --oformat $(ld_bfd) KBUILD_LDFLAGS += -EL else -ld-bfd := elf32-shbig-linux -LDFLAGS_vmlinux += --defsym jiffies=jiffies_64+4 --oformat $(ld-bfd) +ld_bfd := elf32-shbig-linux +LDFLAGS_vmlinux += --defsym jiffies=jiffies_64+4 --oformat $(ld_bfd) KBUILD_LDFLAGS += -EB endif -export ld-bfd +export ld_bfd # Mach groups machdir-$(CONFIG_SOLUTION_ENGINE) += mach-se diff --git a/arch/sh/boot/compressed/Makefile b/arch/sh/boot/compressed/Makefile index 8bc319ff54bf..58df491778b2 100644 --- a/arch/sh/boot/compressed/Makefile +++ b/arch/sh/boot/compressed/Makefile @@ -27,7 +27,7 @@ endif ccflags-remove-$(CONFIG_MCOUNT) += -pg -LDFLAGS_vmlinux := --oformat $(ld-bfd) -Ttext $(IMAGE_OFFSET) -e startup \ +LDFLAGS_vmlinux := --oformat $(ld_bfd) -Ttext $(IMAGE_OFFSET) -e startup \ -T $(obj)/../../kernel/vmlinux.lds KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING @@ -51,7 +51,7 @@ $(obj)/vmlinux.bin.lzo: $(obj)/vmlinux.bin FORCE OBJCOPYFLAGS += -R .empty_zero_page -LDFLAGS_piggy.o := -r --format binary --oformat $(ld-bfd) -T +LDFLAGS_piggy.o := -r --format binary --oformat $(ld_bfd) -T $(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.$(suffix_y) FORCE $(call if_changed,ld) diff --git a/arch/sh/boot/romimage/Makefile b/arch/sh/boot/romimage/Makefile index c7c8be58400c..17b03df0a8de 100644 --- a/arch/sh/boot/romimage/Makefile +++ b/arch/sh/boot/romimage/Makefile @@ -13,7 +13,7 @@ mmcif-obj-$(CONFIG_CPU_SUBTYPE_SH7724) := $(obj)/mmcif-sh7724.o load-$(CONFIG_ROMIMAGE_MMCIF) := $(mmcif-load-y) obj-$(CONFIG_ROMIMAGE_MMCIF) := $(mmcif-obj-y) -LDFLAGS_vmlinux := --oformat $(ld-bfd) -Ttext $(load-y) -e romstart \ +LDFLAGS_vmlinux := --oformat $(ld_bfd) -Ttext $(load-y) -e romstart \ -T $(obj)/../../kernel/vmlinux.lds $(obj)/vmlinux: $(obj)/head.o $(obj-y) $(obj)/piggy.o FORCE @@ -24,7 +24,7 @@ OBJCOPYFLAGS += -j .empty_zero_page $(obj)/zeropage.bin: vmlinux FORCE $(call if_changed,objcopy) -LDFLAGS_piggy.o := -r --format binary --oformat $(ld-bfd) -T +LDFLAGS_piggy.o := -r --format binary --oformat $(ld_bfd) -T $(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/zeropage.bin arch/sh/boot/zImage FORCE $(call if_changed,ld) diff --git a/arch/sh/configs/titan_defconfig b/arch/sh/configs/titan_defconfig index f022ada363b5..8ef72b8dbcd3 100644 --- a/arch/sh/configs/titan_defconfig +++ b/arch/sh/configs/titan_defconfig @@ -61,7 +61,6 @@ CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_MARK=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m CONFIG_NETFILTER_XT_MATCH_LIMIT=m diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 8321b31d2e19..37073ca1e0ad 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -146,7 +146,7 @@ void __init reserve_crashkernel(void) return; ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), - &crash_size, &crash_base, NULL, NULL); + &crash_size, &crash_base, NULL, NULL, NULL); if (ret == 0 && crash_size > 0) { crashk_res.start = crash_base; crashk_res.end = crash_base + crash_size - 1; diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 36f50ad81e83..06f765d71a29 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -291,7 +291,7 @@ static const struct user_regset sh_regsets[] = { * PC, PR, SR, GBR, MACH, MACL, TRA */ [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), @@ -301,7 +301,7 @@ static const struct user_regset sh_regsets[] = { #ifdef CONFIG_SH_FPU [REGSET_FPU] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_fpu_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl index 52a7652fcff6..5e9c9eff5539 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl @@ -471,3 +471,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 0f88123925a4..7b595092cbfb 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -78,7 +78,6 @@ config SPARC64 select MMU_GATHER_NO_FLUSH_CACHE select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_DYNAMIC_FTRACE - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_PAGE_SIZE_8KB select HAVE_SYSCALL_TRACEPOINTS select HAVE_CONTEXT_TRACKING_USER @@ -97,6 +96,7 @@ config SPARC64 select HAVE_ARCH_AUDITSYSCALL select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC + select ARCH_SUPPORTS_HUGETLBFS select HAVE_NMI select HAVE_REGS_AND_STACK_ACCESS_API select ARCH_USE_QUEUED_RWLOCKS @@ -110,7 +110,6 @@ config SPARC64 select HAVE_SETUP_PER_CPU_AREA select NEED_PER_CPU_EMBED_FIRST_CHUNK select NEED_PER_CPU_PAGE_FIRST_CHUNK - select ARCH_HAS_CRC32 config ARCH_PROC_KCORE_TEXT def_bool y diff --git a/arch/sparc/crypto/Kconfig b/arch/sparc/crypto/Kconfig index a6ba319c42dc..f5b2e720fec3 100644 --- a/arch/sparc/crypto/Kconfig +++ b/arch/sparc/crypto/Kconfig @@ -26,26 +26,6 @@ config CRYPTO_MD5_SPARC64 Architecture: sparc64 using crypto instructions, when available -config CRYPTO_SHA1_SPARC64 - tristate "Hash functions: SHA-1" - depends on SPARC64 - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: sparc64 - -config CRYPTO_SHA512_SPARC64 - tristate "Hash functions: SHA-384 and SHA-512" - depends on SPARC64 - select CRYPTO_SHA512 - select CRYPTO_HASH - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: sparc64 using crypto instructions, when available - config CRYPTO_AES_SPARC64 tristate "Ciphers: AES, modes: ECB, CBC, CTR" depends on SPARC64 diff --git a/arch/sparc/crypto/Makefile b/arch/sparc/crypto/Makefile index 701c39edb0d7..0d05a17988c4 100644 --- a/arch/sparc/crypto/Makefile +++ b/arch/sparc/crypto/Makefile @@ -3,16 +3,12 @@ # Arch-specific CryptoAPI modules. # -obj-$(CONFIG_CRYPTO_SHA1_SPARC64) += sha1-sparc64.o -obj-$(CONFIG_CRYPTO_SHA512_SPARC64) += sha512-sparc64.o obj-$(CONFIG_CRYPTO_MD5_SPARC64) += md5-sparc64.o obj-$(CONFIG_CRYPTO_AES_SPARC64) += aes-sparc64.o obj-$(CONFIG_CRYPTO_DES_SPARC64) += des-sparc64.o obj-$(CONFIG_CRYPTO_CAMELLIA_SPARC64) += camellia-sparc64.o -sha1-sparc64-y := sha1_asm.o sha1_glue.o -sha512-sparc64-y := sha512_asm.o sha512_glue.o md5-sparc64-y := md5_asm.o md5_glue.o aes-sparc64-y := aes_asm.o aes_glue.o diff --git a/arch/sparc/crypto/sha1_glue.c b/arch/sparc/crypto/sha1_glue.c deleted file mode 100644 index ef19d5023b1b..000000000000 --- a/arch/sparc/crypto/sha1_glue.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Glue code for SHA1 hashing optimized for sparc64 crypto opcodes. - * - * This is based largely upon arch/x86/crypto/sha1_ssse3_glue.c - * - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - * Copyright (c) Mathias Krause - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -asmlinkage void sha1_sparc64_transform(struct sha1_state *digest, - const u8 *data, int rounds); - -static int sha1_sparc64_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_base_do_update_blocks(desc, data, len, - sha1_sparc64_transform); -} - -/* Add padding and return the message digest. */ -static int sha1_sparc64_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *out) -{ - sha1_base_do_finup(desc, src, len, sha1_sparc64_transform); - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_sparc64_update, - .finup = sha1_sparc64_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-sparc64", - .cra_priority = SPARC_CR_OPCODE_PRIORITY, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static bool __init sparc64_has_sha1_opcode(void) -{ - unsigned long cfr; - - if (!(sparc64_elf_hwcap & HWCAP_SPARC_CRYPTO)) - return false; - - __asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr)); - if (!(cfr & CFR_SHA1)) - return false; - - return true; -} - -static int __init sha1_sparc64_mod_init(void) -{ - if (sparc64_has_sha1_opcode()) { - pr_info("Using sparc64 sha1 opcode optimized SHA-1 implementation\n"); - return crypto_register_shash(&alg); - } - pr_info("sparc64 sha1 opcode not available.\n"); - return -ENODEV; -} - -static void __exit sha1_sparc64_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_init(sha1_sparc64_mod_init); -module_exit(sha1_sparc64_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, sparc64 sha1 opcode accelerated"); - -MODULE_ALIAS_CRYPTO("sha1"); - -#include "crop_devid.c" diff --git a/arch/sparc/crypto/sha512_glue.c b/arch/sparc/crypto/sha512_glue.c deleted file mode 100644 index 47b9277b6877..000000000000 --- a/arch/sparc/crypto/sha512_glue.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Glue code for SHA512 hashing optimized for sparc64 crypto opcodes. - * - * This is based largely upon crypto/sha512_generic.c - * - * Copyright (c) Jean-Luc Cooke - * Copyright (c) Andrew McDonald - * Copyright (c) 2003 Kyle McMartin - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -asmlinkage void sha512_sparc64_transform(u64 *digest, const char *data, - unsigned int rounds); - -static void sha512_block(struct sha512_state *sctx, const u8 *src, int blocks) -{ - sha512_sparc64_transform(sctx->state, src, blocks); -} - -static int sha512_sparc64_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, sha512_block); -} - -static int sha512_sparc64_finup(struct shash_desc *desc, const u8 *src, - unsigned int len, u8 *out) -{ - sha512_base_do_finup(desc, src, len, sha512_block); - return sha512_base_finish(desc, out); -} - -static struct shash_alg sha512 = { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = sha512_sparc64_update, - .finup = sha512_sparc64_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name= "sha512-sparc64", - .cra_priority = SPARC_CR_OPCODE_PRIORITY, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static struct shash_alg sha384 = { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = sha512_sparc64_update, - .finup = sha512_sparc64_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name= "sha384-sparc64", - .cra_priority = SPARC_CR_OPCODE_PRIORITY, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static bool __init sparc64_has_sha512_opcode(void) -{ - unsigned long cfr; - - if (!(sparc64_elf_hwcap & HWCAP_SPARC_CRYPTO)) - return false; - - __asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr)); - if (!(cfr & CFR_SHA512)) - return false; - - return true; -} - -static int __init sha512_sparc64_mod_init(void) -{ - if (sparc64_has_sha512_opcode()) { - int ret = crypto_register_shash(&sha384); - if (ret < 0) - return ret; - - ret = crypto_register_shash(&sha512); - if (ret < 0) { - crypto_unregister_shash(&sha384); - return ret; - } - - pr_info("Using sparc64 sha512 opcode optimized SHA-512/SHA-384 implementation\n"); - return 0; - } - pr_info("sparc64 sha512 opcode not available.\n"); - return -ENODEV; -} - -static void __exit sha512_sparc64_mod_fini(void) -{ - crypto_unregister_shash(&sha384); - crypto_unregister_shash(&sha512); -} - -module_init(sha512_sparc64_mod_init); -module_exit(sha512_sparc64_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-384 and SHA-512 Secure Hash Algorithm, sparc64 sha512 opcode accelerated"); - -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha512"); - -#include "crop_devid.c" diff --git a/arch/sparc/include/asm/hugetlb.h b/arch/sparc/include/asm/hugetlb.h index e7a9cdd498dc..d3bc16fbcbbd 100644 --- a/arch/sparc/include/asm/hugetlb.h +++ b/arch/sparc/include/asm/hugetlb.h @@ -50,11 +50,6 @@ static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, return changed; } -#define __HAVE_ARCH_HUGETLB_FREE_PGD_RANGE -void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, - unsigned long end, unsigned long floor, - unsigned long ceiling); - #include #endif /* _ASM_SPARC64_HUGETLB_H */ diff --git a/arch/sparc/include/asm/mman.h b/arch/sparc/include/asm/mman.h index af9c10c83dc5..3e4bac33be81 100644 --- a/arch/sparc/include/asm/mman.h +++ b/arch/sparc/include/asm/mman.h @@ -28,7 +28,7 @@ static inline void ipi_set_tstate_mcde(void *arg) } #define arch_calc_vm_prot_bits(prot, pkey) sparc_calc_vm_prot_bits(prot) -static inline unsigned long sparc_calc_vm_prot_bits(unsigned long prot) +static inline vm_flags_t sparc_calc_vm_prot_bits(unsigned long prot) { if (adi_capable() && (prot & PROT_ADI)) { struct pt_regs *regs; @@ -58,7 +58,7 @@ static inline int sparc_validate_prot(unsigned long prot, unsigned long addr) /* arch_validate_flags() - Ensure combination of flags is valid for a * VMA. */ -static inline bool arch_validate_flags(unsigned long vm_flags) +static inline bool arch_validate_flags(vm_flags_t vm_flags) { /* If ADI is being enabled on this VMA, check for ADI * capability on the platform and ensure VMA is suitable diff --git a/arch/sparc/include/asm/video.h b/arch/sparc/include/asm/video.h index a6f48f52db58..773717b6d491 100644 --- a/arch/sparc/include/asm/video.h +++ b/arch/sparc/include/asm/video.h @@ -19,8 +19,10 @@ static inline pgprot_t pgprot_framebuffer(pgprot_t prot, #define pgprot_framebuffer pgprot_framebuffer #endif +#ifdef CONFIG_VIDEO bool video_is_primary_device(struct device *dev); #define video_is_primary_device video_is_primary_device +#endif static inline void fb_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n) { diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index adcba7329386..71befa109e1c 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -145,6 +145,9 @@ #define SO_PASSRIGHTS 0x005c +#define SO_INQ 0x005d +#define SCM_INQ SO_INQ + #if !defined(__KERNEL__) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index c273ccebea46..c56333975fb1 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -218,7 +218,7 @@ static const struct user_regset sparc32_regsets[] = { * PSR, PC, nPC, Y, WIM, TBR */ [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = 38, .size = sizeof(u32), .align = sizeof(u32), .regset_get = genregs32_get, .set = genregs32_set @@ -234,7 +234,7 @@ static const struct user_regset sparc32_regsets[] = { * FPU QUEUE (64 32-bit ints) */ [REGSET_FP] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = 99, .size = sizeof(u32), .align = sizeof(u32), .regset_get = fpregs32_get, .set = fpregs32_set diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 4deba5b6eddb..9fc67fa9336f 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -420,7 +420,7 @@ static const struct user_regset sparc64_regsets[] = { * TSTATE, TPC, TNPC, Y */ [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = 36, .size = sizeof(u64), .align = sizeof(u64), .regset_get = genregs64_get, .set = genregs64_set @@ -432,7 +432,7 @@ static const struct user_regset sparc64_regsets[] = { * FPRS */ [REGSET_FP] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = 35, .size = sizeof(u64), .align = sizeof(u64), .regset_get = fpregs64_get, .set = fpregs64_set @@ -750,7 +750,7 @@ static const struct user_regset sparc32_regsets[] = { * PSR, PC, nPC, Y, WIM, TBR */ [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = 38, .size = sizeof(u32), .align = sizeof(u32), .regset_get = genregs32_get, .set = genregs32_set @@ -766,7 +766,7 @@ static const struct user_regset sparc32_regsets[] = { * FPU QUEUE (64 32-bit ints) */ [REGSET_FP] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = 99, .size = sizeof(u32), .align = sizeof(u32), .regset_get = fpregs32_get, .set = fpregs32_set diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl index 83e45eb6c095..ebb7d06d1044 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl @@ -513,3 +513,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 5cf9781d68b4..ee5091dd67ed 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -4,7 +4,6 @@ asflags-y := -ansi -DST_DIV0=0x02 -obj-y += crypto/ lib-$(CONFIG_SPARC32) += ashrdi3.o lib-$(CONFIG_SPARC32) += memcpy.o memset.o lib-y += strlen.o @@ -54,5 +53,3 @@ lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o obj-$(CONFIG_SPARC64) += iomap.o obj-$(CONFIG_SPARC32) += atomic32.o obj-$(CONFIG_SPARC64) += PeeCeeI.o -obj-$(CONFIG_CRC32_ARCH) += crc32-sparc.o -crc32-sparc-y := crc32.o crc32c_asm.o diff --git a/arch/sparc/lib/crypto/Kconfig b/arch/sparc/lib/crypto/Kconfig deleted file mode 100644 index e5c3e4d3dba6..000000000000 --- a/arch/sparc/lib/crypto/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config CRYPTO_SHA256_SPARC64 - tristate - depends on SPARC64 - default CRYPTO_LIB_SHA256 - select CRYPTO_ARCH_HAVE_LIB_SHA256 - select CRYPTO_LIB_SHA256_GENERIC diff --git a/arch/sparc/lib/crypto/Makefile b/arch/sparc/lib/crypto/Makefile deleted file mode 100644 index 75ee244ad6f7..000000000000 --- a/arch/sparc/lib/crypto/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -obj-$(CONFIG_CRYPTO_SHA256_SPARC64) += sha256-sparc64.o -sha256-sparc64-y := sha256.o sha256_asm.o diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c index 80504148d8a5..4b9431311e05 100644 --- a/arch/sparc/mm/hugetlbpage.c +++ b/arch/sparc/mm/hugetlbpage.c @@ -295,122 +295,3 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, return entry; } - -static void hugetlb_free_pte_range(struct mmu_gather *tlb, pmd_t *pmd, - unsigned long addr) -{ - pgtable_t token = pmd_pgtable(*pmd); - - pmd_clear(pmd); - pte_free_tlb(tlb, token, addr); - mm_dec_nr_ptes(tlb->mm); -} - -static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud, - unsigned long addr, unsigned long end, - unsigned long floor, unsigned long ceiling) -{ - pmd_t *pmd; - unsigned long next; - unsigned long start; - - start = addr; - pmd = pmd_offset(pud, addr); - do { - next = pmd_addr_end(addr, end); - if (pmd_none(*pmd)) - continue; - if (is_hugetlb_pmd(*pmd)) - pmd_clear(pmd); - else - hugetlb_free_pte_range(tlb, pmd, addr); - } while (pmd++, addr = next, addr != end); - - start &= PUD_MASK; - if (start < floor) - return; - if (ceiling) { - ceiling &= PUD_MASK; - if (!ceiling) - return; - } - if (end - 1 > ceiling - 1) - return; - - pmd = pmd_offset(pud, start); - pud_clear(pud); - pmd_free_tlb(tlb, pmd, start); - mm_dec_nr_pmds(tlb->mm); -} - -static void hugetlb_free_pud_range(struct mmu_gather *tlb, p4d_t *p4d, - unsigned long addr, unsigned long end, - unsigned long floor, unsigned long ceiling) -{ - pud_t *pud; - unsigned long next; - unsigned long start; - - start = addr; - pud = pud_offset(p4d, addr); - do { - next = pud_addr_end(addr, end); - if (pud_none_or_clear_bad(pud)) - continue; - if (is_hugetlb_pud(*pud)) - pud_clear(pud); - else - hugetlb_free_pmd_range(tlb, pud, addr, next, floor, - ceiling); - } while (pud++, addr = next, addr != end); - - start &= PGDIR_MASK; - if (start < floor) - return; - if (ceiling) { - ceiling &= PGDIR_MASK; - if (!ceiling) - return; - } - if (end - 1 > ceiling - 1) - return; - - pud = pud_offset(p4d, start); - p4d_clear(p4d); - pud_free_tlb(tlb, pud, start); - mm_dec_nr_puds(tlb->mm); -} - -void hugetlb_free_pgd_range(struct mmu_gather *tlb, - unsigned long addr, unsigned long end, - unsigned long floor, unsigned long ceiling) -{ - pgd_t *pgd; - p4d_t *p4d; - unsigned long next; - - addr &= PMD_MASK; - if (addr < floor) { - addr += PMD_SIZE; - if (!addr) - return; - } - if (ceiling) { - ceiling &= PMD_MASK; - if (!ceiling) - return; - } - if (end - 1 > ceiling - 1) - end -= PMD_SIZE; - if (addr > end - 1) - return; - - pgd = pgd_offset(tlb->mm, addr); - p4d = p4d_offset(pgd, addr); - do { - next = p4d_addr_end(addr, end); - if (p4d_none_or_clear_bad(p4d)) - continue; - hugetlb_free_pud_range(tlb, p4d, addr, next, floor, ceiling); - } while (p4d++, addr = next, addr != end); -} diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 25ae4c897aae..7ed58bf3aaca 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -3201,7 +3201,7 @@ void copy_highpage(struct page *to, struct page *from) } EXPORT_SYMBOL(copy_highpage); -pgprot_t vm_get_page_prot(unsigned long vm_flags) +pgprot_t vm_get_page_prot(vm_flags_t vm_flags) { unsigned long prot = pgprot_val(protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]); diff --git a/arch/sparc/vdso/Makefile b/arch/sparc/vdso/Makefile index fdc4a8f5a49c..683b2d408224 100644 --- a/arch/sparc/vdso/Makefile +++ b/arch/sparc/vdso/Makefile @@ -48,7 +48,7 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables -m64 SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 $(call cc-option,-fcall-used-g5) $(call cc-option,-fcall-used-g7) -$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) +$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) # # vDSO code runs in userspace and -pg doesn't help with profiling anyway. @@ -79,6 +79,7 @@ KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_32 := $(filter-out -mcmodel=medlow,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out $(KSTACK_ERASE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 += -m32 -msoft-float -fpic diff --git a/arch/um/Kconfig b/arch/um/Kconfig index f08e8a7fac93..9083bfdb7735 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -6,6 +6,7 @@ config UML bool default y select ARCH_WANTS_DYNAMIC_TASK_STRUCT + select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL @@ -35,6 +36,7 @@ config UML select HAVE_RUST select ARCH_HAS_UBSAN select HAVE_ARCH_TRACEHOOK + select HAVE_SYSCALL_TRACEPOINTS select THREAD_INFO_IN_TASK config MMU @@ -82,9 +84,6 @@ config NR_CPUS range 1 1 default 1 -config ARCH_HAS_CACHE_LINE_SIZE - def_bool y - source "arch/$(HEADER_ARCH)/um/Kconfig" config MAY_HAVE_RUNTIME_DEPS diff --git a/arch/um/drivers/Kconfig b/arch/um/drivers/Kconfig index 34085bfc6d41..6a0354ca032f 100644 --- a/arch/um/drivers/Kconfig +++ b/arch/um/drivers/Kconfig @@ -160,6 +160,7 @@ config UML_RTC config UML_PCI bool select FORCE_PCI + select IRQ_MSI_LIB select UML_IOMEM_EMULATION select UML_DMA_EMULATION select PCI_MSI diff --git a/arch/um/drivers/rtc_user.c b/arch/um/drivers/rtc_user.c index 51e79f3148cd..67912fcf7b28 100644 --- a/arch/um/drivers/rtc_user.c +++ b/arch/um/drivers/rtc_user.c @@ -28,7 +28,7 @@ int uml_rtc_start(bool timetravel) int err; if (timetravel) { - int err = os_pipe(uml_rtc_irq_fds, 1, 1); + err = os_pipe(uml_rtc_irq_fds, 1, 1); if (err) goto fail; } else { diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c index c5e6545f6fcf..8e8a8bf518b6 100644 --- a/arch/um/drivers/ubd_user.c +++ b/arch/um/drivers/ubd_user.c @@ -41,7 +41,7 @@ int start_io_thread(struct os_helper_thread **td_out, int *fd_out) *fd_out = fds[1]; err = os_set_fd_block(*fd_out, 0); - err = os_set_fd_block(kernel_fd, 0); + err |= os_set_fd_block(kernel_fd, 0); if (err) { printk("start_io_thread - failed to set nonblocking I/O.\n"); goto out_close; diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c index f292e0b4ff8b..9bbbddfe866b 100644 --- a/arch/um/drivers/vector_kern.c +++ b/arch/um/drivers/vector_kern.c @@ -1625,35 +1625,19 @@ static void vector_eth_configure( device->dev = dev; - *vp = ((struct vector_private) - { - .list = LIST_HEAD_INIT(vp->list), - .dev = dev, - .unit = n, - .options = get_transport_options(def), - .rx_irq = 0, - .tx_irq = 0, - .parsed = def, - .max_packet = get_mtu(def) + ETH_HEADER_OTHER, - /* TODO - we need to calculate headroom so that ip header - * is 16 byte aligned all the time - */ - .headroom = get_headroom(def), - .form_header = NULL, - .verify_header = NULL, - .header_rxbuffer = NULL, - .header_txbuffer = NULL, - .header_size = 0, - .rx_header_size = 0, - .rexmit_scheduled = false, - .opened = false, - .transport_data = NULL, - .in_write_poll = false, - .coalesce = 2, - .req_size = get_req_size(def), - .in_error = false, - .bpf = NULL - }); + INIT_LIST_HEAD(&vp->list); + vp->dev = dev; + vp->unit = n; + vp->options = get_transport_options(def); + vp->parsed = def; + vp->max_packet = get_mtu(def) + ETH_HEADER_OTHER; + /* + * TODO - we need to calculate headroom so that ip header + * is 16 byte aligned all the time + */ + vp->headroom = get_headroom(def); + vp->coalesce = 2; + vp->req_size = get_req_size(def); dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST); INIT_WORK(&vp->reset_tx, vector_reset_tx); diff --git a/arch/um/drivers/vfio_kern.c b/arch/um/drivers/vfio_kern.c index b51fc9888ae1..915812a79bfc 100644 --- a/arch/um/drivers/vfio_kern.c +++ b/arch/um/drivers/vfio_kern.c @@ -16,6 +16,7 @@ #include #include +#include "mconsole_kern.h" #include "virt-pci.h" #include "vfio_user.h" @@ -60,6 +61,7 @@ static LIST_HEAD(uml_vfio_groups); static DEFINE_MUTEX(uml_vfio_groups_mtx); static LIST_HEAD(uml_vfio_devices); +static DEFINE_MUTEX(uml_vfio_devices_mtx); static int uml_vfio_set_container(int group_fd) { @@ -570,29 +572,55 @@ static void uml_vfio_release_device(struct uml_vfio_device *dev) kfree(dev); } -static int uml_vfio_cmdline_set(const char *device, const struct kernel_param *kp) +static struct uml_vfio_device *uml_vfio_find_device(const char *device) +{ + struct uml_vfio_device *dev; + + list_for_each_entry(dev, ¨_vfio_devices, list) { + if (!strcmp(dev->name, device)) + return dev; + } + return NULL; +} + +static struct uml_vfio_device *uml_vfio_add_device(const char *device) { struct uml_vfio_device *dev; int fd; + guard(mutex)(¨_vfio_devices_mtx); + if (uml_vfio_container.fd < 0) { fd = uml_vfio_user_open_container(); if (fd < 0) - return fd; + return ERR_PTR(fd); uml_vfio_container.fd = fd; } + if (uml_vfio_find_device(device)) + return ERR_PTR(-EEXIST); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) - return -ENOMEM; + return ERR_PTR(-ENOMEM); dev->name = kstrdup(device, GFP_KERNEL); if (!dev->name) { kfree(dev); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } list_add_tail(&dev->list, ¨_vfio_devices); + return dev; +} + +static int uml_vfio_cmdline_set(const char *device, const struct kernel_param *kp) +{ + struct uml_vfio_device *dev; + + dev = uml_vfio_add_device(device); + if (IS_ERR(dev)) + return PTR_ERR(dev); return 0; } @@ -615,6 +643,42 @@ __uml_help(uml_vfio_cmdline_param_ops, " through multiple PCI devices to UML.\n\n" ); +static int uml_vfio_mc_config(char *str, char **error_out) +{ + struct uml_vfio_device *dev; + + if (*str != '=') { + *error_out = "Invalid config"; + return -EINVAL; + } + str += 1; + + dev = uml_vfio_add_device(str); + if (IS_ERR(dev)) + return PTR_ERR(dev); + uml_vfio_open_device(dev); + return 0; +} + +static int uml_vfio_mc_id(char **str, int *start_out, int *end_out) +{ + return -EOPNOTSUPP; +} + +static int uml_vfio_mc_remove(int n, char **error_out) +{ + return -EOPNOTSUPP; +} + +static struct mc_device uml_vfio_mc = { + .list = LIST_HEAD_INIT(uml_vfio_mc.list), + .name = "vfio_uml.device", + .config = uml_vfio_mc_config, + .get_config = NULL, + .id = uml_vfio_mc_id, + .remove = uml_vfio_mc_remove, +}; + static int __init uml_vfio_init(void) { struct uml_vfio_device *dev, *n; @@ -625,6 +689,8 @@ static int __init uml_vfio_init(void) list_for_each_entry_safe(dev, n, ¨_vfio_devices, list) uml_vfio_open_device(dev); + mconsole_register_dev(¨_vfio_mc); + return 0; } late_initcall(uml_vfio_init); diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c index 0fe207ca4b72..557d93aea00a 100644 --- a/arch/um/drivers/virt-pci.c +++ b/arch/um/drivers/virt-pci.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ static struct um_pci_device *um_pci_platform_device; static struct um_pci_device_reg um_pci_devices[MAX_DEVICES]; static struct fwnode_handle *um_pci_fwnode; static struct irq_domain *um_pci_inner_domain; -static struct irq_domain *um_pci_msi_domain; static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)]; static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset, @@ -400,21 +400,24 @@ static void um_pci_inner_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops um_pci_inner_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = um_pci_inner_domain_alloc, .free = um_pci_inner_domain_free, }; -static struct irq_chip um_pci_msi_irq_chip = { - .name = "UM virtual PCIe MSI", - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; +#define UM_PCI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_NO_AFFINITY) +#define UM_PCI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX) -static struct msi_domain_info um_pci_msi_domain_info = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | - MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX, - .chip = &um_pci_msi_irq_chip, +static const struct msi_parent_ops um_pci_msi_parent_ops = { + .required_flags = UM_PCI_MSI_FLAGS_REQUIRED, + .supported_flags = UM_PCI_MSI_FLAGS_SUPPORTED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PCI_MSI, + .prefix = "UM-virtual-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, }; static struct resource busn_resource = { @@ -559,17 +562,14 @@ static int __init um_pci_init(void) goto free; } - um_pci_inner_domain = irq_domain_create_linear(um_pci_fwnode, MAX_MSI_VECTORS, - &um_pci_inner_domain_ops, NULL); - if (!um_pci_inner_domain) { - err = -ENOMEM; - goto free; - } + struct irq_domain_info info = { + .fwnode = um_pci_fwnode, + .ops = &um_pci_inner_domain_ops, + .size = MAX_MSI_VECTORS, + }; - um_pci_msi_domain = pci_msi_create_irq_domain(um_pci_fwnode, - &um_pci_msi_domain_info, - um_pci_inner_domain); - if (!um_pci_msi_domain) { + um_pci_inner_domain = msi_create_parent_irq_domain(&info, &um_pci_msi_parent_ops); + if (!um_pci_inner_domain) { err = -ENOMEM; goto free; } @@ -611,7 +611,6 @@ device_initcall(um_pci_init); static void __exit um_pci_exit(void) { - irq_domain_remove(um_pci_msi_domain); irq_domain_remove(um_pci_inner_domain); pci_free_resource_list(&bridge->windows); pci_free_host_bridge(bridge); diff --git a/arch/um/drivers/virtio_pcidev.c b/arch/um/drivers/virtio_pcidev.c index 3c4c4c928fdd..e9e23cc3f357 100644 --- a/arch/um/drivers/virtio_pcidev.c +++ b/arch/um/drivers/virtio_pcidev.c @@ -42,7 +42,7 @@ struct virtio_pcidev_device { void *extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS + 1]; DECLARE_BITMAP(used_bufs, VIRTIO_PCIDEV_WRITE_BUFS); -#define UM_PCI_STAT_WAITING 0 +#define VIRTIO_PCIDEV_STAT_WAITING 0 unsigned long status; bool platform; @@ -172,7 +172,7 @@ static int virtio_pcidev_send_cmd(struct virtio_pcidev_device *dev, } /* kick and poll for getting a response on the queue */ - set_bit(UM_PCI_STAT_WAITING, &dev->status); + set_bit(VIRTIO_PCIDEV_STAT_WAITING, &dev->status); virtqueue_kick(dev->cmd_vq); ret = 0; @@ -193,7 +193,7 @@ static int virtio_pcidev_send_cmd(struct virtio_pcidev_device *dev, } udelay(1); } - clear_bit(UM_PCI_STAT_WAITING, &dev->status); + clear_bit(VIRTIO_PCIDEV_STAT_WAITING, &dev->status); if (bounce_out) memcpy(out, buf->data, out_size); @@ -439,7 +439,7 @@ static void virtio_pcidev_cmd_vq_cb(struct virtqueue *vq) void *cmd; int len; - if (test_bit(UM_PCI_STAT_WAITING, &dev->status)) + if (test_bit(VIRTIO_PCIDEV_STAT_WAITING, &dev->status)) return; while ((cmd = virtqueue_get_buf(vq, &len))) diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index 04ab3b653a48..b6810db24ca4 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -15,7 +15,6 @@ generic-y += mcs_spinlock.h generic-y += mmiowb.h generic-y += module.h generic-y += module.lds.h -generic-y += param.h generic-y += parport.h generic-y += percpu.h generic-y += preempt.h diff --git a/arch/um/include/asm/cpufeature.h b/arch/um/include/asm/cpufeature.h index 1eb8b834fbec..4354f6984271 100644 --- a/arch/um/include/asm/cpufeature.h +++ b/arch/um/include/asm/cpufeature.h @@ -4,7 +4,7 @@ #include -#if defined(__KERNEL__) && !defined(__ASSEMBLY__) +#if defined(__KERNEL__) && !defined(__ASSEMBLER__) #include #include @@ -137,5 +137,5 @@ static __always_inline bool _static_cpu_has(u16 bit) #define CPU_FEATURE_TYPEVAL boot_cpu_data.x86_vendor, boot_cpu_data.x86, \ boot_cpu_data.x86_model -#endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */ +#endif /* defined(__KERNEL__) && !defined(__ASSEMBLER__) */ #endif /* _ASM_UM_CPUFEATURE_H */ diff --git a/arch/um/include/asm/current.h b/arch/um/include/asm/current.h index de64e032d66c..8accc6d6f502 100644 --- a/arch/um/include/asm/current.h +++ b/arch/um/include/asm/current.h @@ -5,7 +5,7 @@ #include #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct task_struct; extern struct task_struct *cpu_tasks[NR_CPUS]; @@ -18,6 +18,6 @@ static __always_inline struct task_struct *get_current(void) #define current get_current() -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_CURRENT_H */ diff --git a/arch/um/include/asm/mmu_context.h b/arch/um/include/asm/mmu_context.h index 23dcc914d44e..0bbb24868557 100644 --- a/arch/um/include/asm/mmu_context.h +++ b/arch/um/include/asm/mmu_context.h @@ -16,11 +16,6 @@ #define activate_mm activate_mm static inline void activate_mm(struct mm_struct *old, struct mm_struct *new) { - /* - * This is called by fs/exec.c and sys_unshare() - * when the new ->mm is used for the first time. - */ - __switch_mm(&new->context.id); } static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, @@ -28,11 +23,9 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, { unsigned cpu = smp_processor_id(); - if(prev != next){ + if (prev != next) { cpumask_clear_cpu(cpu, mm_cpumask(prev)); cpumask_set_cpu(cpu, mm_cpumask(next)); - if(next != &init_mm) - __switch_mm(&next->context.id); } } diff --git a/arch/um/include/asm/page.h b/arch/um/include/asm/page.h index 3d516f3ca9c7..6f54254aaf44 100644 --- a/arch/um/include/asm/page.h +++ b/arch/um/include/asm/page.h @@ -11,7 +11,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct page; @@ -94,7 +94,7 @@ extern unsigned long uml_physmem; #include #include -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #ifdef CONFIG_X86_32 #define __HAVE_ARCH_GATE_AREA 1 diff --git a/arch/um/include/asm/ptrace-generic.h b/arch/um/include/asm/ptrace-generic.h index 4696f24d1492..86d74f9d33cf 100644 --- a/arch/um/include/asm/ptrace-generic.h +++ b/arch/um/include/asm/ptrace-generic.h @@ -6,7 +6,7 @@ #ifndef __UM_PTRACE_GENERIC_H #define __UM_PTRACE_GENERIC_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include diff --git a/arch/um/include/asm/thread_info.h b/arch/um/include/asm/thread_info.h index f9ad06fcc991..7a6f4dc99fa1 100644 --- a/arch/um/include/asm/thread_info.h +++ b/arch/um/include/asm/thread_info.h @@ -9,7 +9,7 @@ #define THREAD_SIZE_ORDER CONFIG_KERNEL_STACK_ORDER #define THREAD_SIZE ((1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -43,6 +43,8 @@ struct thread_info { #define TIF_NOTIFY_RESUME 8 #define TIF_SECCOMP 9 /* secure computing */ #define TIF_SINGLESTEP 10 /* single stepping userspace */ +#define TIF_SYSCALL_TRACEPOINT 11 /* syscall tracepoint instrumentation */ + #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) @@ -50,7 +52,11 @@ struct thread_info { #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) #define _TIF_MEMDIE (1 << TIF_MEMDIE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) +#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL | \ + _TIF_NOTIFY_RESUME) + #endif diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h index 4f44dcce8a7c..2f9bfd99460a 100644 --- a/arch/um/include/shared/as-layout.h +++ b/arch/um/include/shared/as-layout.h @@ -26,7 +26,7 @@ #define STUB_DATA_PAGES 2 /* must be a power of two */ #define STUB_END (STUB_DATA + STUB_DATA_PAGES * UM_KERN_PAGE_SIZE) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include diff --git a/arch/um/include/shared/skas/mm_id.h b/arch/um/include/shared/skas/mm_id.h index 89df9a55fbea..4f977ef5dda5 100644 --- a/arch/um/include/shared/skas/mm_id.h +++ b/arch/um/include/shared/skas/mm_id.h @@ -19,8 +19,6 @@ struct mm_id { int syscall_fd_map[STUB_MAX_FDS]; }; -void __switch_mm(struct mm_id *mm_idp); - void notify_mm_kill(int pid); #endif diff --git a/arch/um/include/shared/skas/skas.h b/arch/um/include/shared/skas/skas.h index 7d1de4cab551..807514e10538 100644 --- a/arch/um/include/shared/skas/skas.h +++ b/arch/um/include/shared/skas/skas.h @@ -9,7 +9,6 @@ #include extern int using_seccomp; -extern int userspace_pid[]; extern void new_thread_handler(void); extern void handle_syscall(struct uml_pt_regs *regs); diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index cb8b5cd9285c..13812fa97eee 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -26,8 +26,6 @@ void flush_thread(void) get_safe_registers(current_pt_regs()->regs.gp, current_pt_regs()->regs.fp); - - __switch_mm(¤t->mm->context.id); } void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 0cd6fad3d908..1be644de9e41 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -82,14 +82,18 @@ struct task_struct *__switch_to(struct task_struct *from, struct task_struct *to void interrupt_end(void) { struct pt_regs *regs = ¤t->thread.regs; + unsigned long thread_flags; - if (need_resched()) - schedule(); - if (test_thread_flag(TIF_SIGPENDING) || - test_thread_flag(TIF_NOTIFY_SIGNAL)) - do_signal(regs); - if (test_thread_flag(TIF_NOTIFY_RESUME)) - resume_user_mode_work(regs); + thread_flags = read_thread_flags(); + while (thread_flags & _TIF_WORK_MASK) { + if (thread_flags & _TIF_NEED_RESCHED) + schedule(); + if (thread_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) + do_signal(regs); + if (thread_flags & _TIF_NOTIFY_RESUME) + resume_user_mode_work(regs); + thread_flags = read_thread_flags(); + } } int get_current_pid(void) diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 2124624b7817..fdbb37b5c399 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -9,6 +9,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + void user_enable_single_step(struct task_struct *child) { set_tsk_thread_flag(child, TIF_SINGLESTEP); @@ -126,6 +129,9 @@ int syscall_trace_enter(struct pt_regs *regs) UPT_SYSCALL_ARG3(®s->regs), UPT_SYSCALL_ARG4(®s->regs)); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, UPT_SYSCALL_NR(®s->regs)); + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return 0; @@ -142,6 +148,9 @@ void syscall_trace_leave(struct pt_regs *regs) if (test_thread_flag(TIF_SINGLESTEP)) send_sigtrap(®s->regs, 0); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, PT_REGS_SYSCALL_RET(regs)); + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 849fafa4b54f..afe9a2f251ef 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -20,8 +20,8 @@ /* Ensure the stub_data struct covers the allocated area */ static_assert(sizeof(struct stub_data) == STUB_DATA_PAGES * UM_KERN_PAGE_SIZE); -spinlock_t mm_list_lock; -struct list_head mm_list; +static spinlock_t mm_list_lock; +static struct list_head mm_list; int init_new_context(struct task_struct *task, struct mm_struct *mm) { diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 05dcdc057af9..5881b17eb987 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -26,8 +26,6 @@ static int __init start_kernel_proc(void *unused) return 0; } -extern int userspace_pid[]; - static char cpu0_irqstack[THREAD_SIZE] __aligned(THREAD_SIZE); int __init start_uml(void) diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c index a5beaea2967e..ba7494f9bfe4 100644 --- a/arch/um/kernel/skas/syscall.c +++ b/arch/um/kernel/skas/syscall.c @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include @@ -43,7 +43,14 @@ void handle_syscall(struct uml_pt_regs *r) tt_extra_sched_jiffies += 1; if (syscall >= 0 && syscall < __NR_syscalls) { - unsigned long ret = EXECUTE_SYSCALL(syscall, regs); + unsigned long ret; + + ret = (*sys_call_table[syscall])(UPT_SYSCALL_ARG1(®s->regs), + UPT_SYSCALL_ARG2(®s->regs), + UPT_SYSCALL_ARG3(®s->regs), + UPT_SYSCALL_ARG4(®s->regs), + UPT_SYSCALL_ARG5(®s->regs), + UPT_SYSCALL_ARG6(®s->regs)); PT_REGS_SET_SYSCALL_RETURN(regs, ret); diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index e42ffac23e3c..78f48fa9db8b 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -267,7 +267,7 @@ static void get_skas_faultinfo(int pid, struct faultinfo *fi) memcpy(fi, (void *)current_stub_stack(), sizeof(*fi)); } -static void handle_trap(int pid, struct uml_pt_regs *regs) +static void handle_trap(struct uml_pt_regs *regs) { if ((UPT_IP(regs) >= STUB_START) && (UPT_IP(regs) < STUB_END)) fatal_sigsegv(); @@ -434,7 +434,6 @@ static int __init init_stub_exe_fd(void) __initcall(init_stub_exe_fd); int using_seccomp; -int userspace_pid[NR_CPUS]; /** * start_userspace() - prepare a new userspace process @@ -548,12 +547,12 @@ int start_userspace(struct mm_id *mm_id) return err; } -int unscheduled_userspace_iterations; +static int unscheduled_userspace_iterations; extern unsigned long tt_extra_sched_jiffies; void userspace(struct uml_pt_regs *regs) { - int err, status, op, pid = userspace_pid[0]; + int err, status, op; siginfo_t si_ptrace; siginfo_t *si; int sig; @@ -562,6 +561,8 @@ void userspace(struct uml_pt_regs *regs) interrupt_end(); while (1) { + struct mm_id *mm_id = current_mm_id(); + /* * When we are in time-travel mode, userspace can theoretically * do a *lot* of work without being scheduled. The problem with @@ -590,14 +591,12 @@ void userspace(struct uml_pt_regs *regs) current_mm_sync(); if (using_seccomp) { - struct mm_id *mm_id = current_mm_id(); struct stub_data *proc_data = (void *) mm_id->stack; - int ret; - ret = set_stub_state(regs, proc_data, singlestepping()); - if (ret) { + err = set_stub_state(regs, proc_data, singlestepping()); + if (err) { printk(UM_KERN_ERR "%s - failed to set regs: %d", - __func__, ret); + __func__, err); fatal_sigsegv(); } @@ -623,10 +622,10 @@ void userspace(struct uml_pt_regs *regs) mm_id->syscall_data_len = 0; mm_id->syscall_fd_num = 0; - ret = get_stub_state(regs, proc_data, NULL); - if (ret) { + err = get_stub_state(regs, proc_data, NULL); + if (err) { printk(UM_KERN_ERR "%s - failed to get regs: %d", - __func__, ret); + __func__, err); fatal_sigsegv(); } @@ -645,8 +644,10 @@ void userspace(struct uml_pt_regs *regs) GET_FAULTINFO_FROM_MC(regs->faultinfo, mcontext); } } else { + int pid = mm_id->pid; + /* Flush out any pending syscalls */ - err = syscall_stub_flush(current_mm_id()); + err = syscall_stub_flush(mm_id); if (err) { if (err == -ENOMEM) report_enomem(); @@ -756,7 +757,7 @@ void userspace(struct uml_pt_regs *regs) handle_syscall(regs); break; case SIGTRAP + 0x80: - handle_trap(pid, regs); + handle_trap(regs); break; case SIGTRAP: relay_signal(SIGTRAP, (struct siginfo *)si, regs, NULL); @@ -777,7 +778,6 @@ void userspace(struct uml_pt_regs *regs) __func__, sig); fatal_sigsegv(); } - pid = userspace_pid[0]; interrupt_end(); /* Avoid -ERESTARTSYS handling in host */ @@ -902,8 +902,3 @@ void reboot_skas(void) block_signals_trace(); UML_LONGJMP(&initial_jmpbuf, noreboot ? INIT_JMP_HALT : INIT_JMP_REBOOT); } - -void __switch_mm(struct mm_id *mm_idp) -{ - userspace_pid[0] = mm_idp->pid; -} diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 340e5468980e..52c8910ba2ef 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -75,13 +75,11 @@ config X86 select ARCH_ENABLE_SPLIT_PMD_PTLOCK if (PGTABLE_LEVELS > 2) && (X86_64 || X86_PAE) select ARCH_ENABLE_THP_MIGRATION if X86_64 && TRANSPARENT_HUGEPAGE select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI + select ARCH_HAS_CPU_ATTACK_VECTORS if CPU_MITIGATIONS select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_CPU_PASID if IOMMU_SVA - select ARCH_HAS_CRC32 - select ARCH_HAS_CRC64 if X86_64 - select ARCH_HAS_CRC_T10DIF select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL select ARCH_HAS_DEBUG_VM_PGTABLE if !X86_PAE @@ -89,7 +87,7 @@ config X86 select ARCH_HAS_DMA_OPS if GART_IOMMU || XEN select ARCH_HAS_EARLY_DEBUG if KGDB select ARCH_HAS_ELF_RANDOMIZE - select ARCH_HAS_EXECMEM_ROX if X86_64 + select ARCH_HAS_EXECMEM_ROX if X86_64 && STRICT_MODULE_RWX select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL @@ -101,7 +99,6 @@ config X86 select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PMEM_API if X86_64 select ARCH_HAS_PREEMPT_LAZY - select ARCH_HAS_PTE_DEVMAP if X86_64 select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_HW_PTE_YOUNG select ARCH_HAS_NONLEAF_PMD_YOUNG if PGTABLE_LEVELS > 2 @@ -126,6 +123,7 @@ config X86 select ARCH_SUPPORTS_ACPI select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC + select ARCH_SUPPORTS_HUGETLBFS select ARCH_SUPPORTS_PAGE_TABLE_CHECK if X86_64 select ARCH_SUPPORTS_NUMA_BALANCING if X86_64 select ARCH_SUPPORTS_KMAP_LOCAL_FORCE_MAP if NR_CPUS <= 4096 @@ -147,7 +145,7 @@ config X86 select ARCH_WANTS_DYNAMIC_TASK_STRUCT select ARCH_WANTS_NO_INSTR select ARCH_WANT_GENERAL_HUGETLB - select ARCH_WANT_HUGE_PMD_SHARE + select ARCH_WANT_HUGE_PMD_SHARE if X86_64 select ARCH_WANT_LD_ORPHAN_WARN select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP if X86_64 select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if X86_64 @@ -204,13 +202,13 @@ config X86 select HAVE_ARCH_KFENCE select HAVE_ARCH_KMSAN if X86_64 select HAVE_ARCH_KGDB + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT select HAVE_ARCH_PREL32_RELOCATIONS select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_THREAD_STRUCT_WHITELIST - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 @@ -244,7 +242,6 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_GRAPH_FUNC if HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER @@ -2695,6 +2692,24 @@ config MITIGATION_ITS disabled, mitigation cannot be enabled via cmdline. See +config MITIGATION_TSA + bool "Mitigate Transient Scheduler Attacks" + depends on CPU_SUP_AMD + default y + help + Enable mitigation for Transient Scheduler Attacks. TSA is a hardware + security vulnerability on AMD CPUs which can lead to forwarding of + invalid info to subsequent instructions and thus can affect their + timing and thereby cause a leakage. + +config MITIGATION_VMSCAPE + bool "Mitigate VMSCAPE" + depends on KVM + default y + help + Enable mitigation for VMSCAPE attacks. VMSCAPE is a hardware security + vulnerability on Intel and AMD CPUs that may allow a guest to do + Spectre v2 style attacks on userspace hypervisor. endif config ARCH_HAS_ADD_PAGES diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 640fcac3af74..3f9fb3698d66 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -71,7 +71,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) -sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|efi.._stub_entry\|efi\(32\)\?_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|_e\?data\|z_.*\)$$/\#define ZO_\2 0x\1/p' +sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|efi.._stub_entry\|efi\(32\)\?_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|_e\?data\|_e\?sbat\|z_.*\)$$/\#define ZO_\2 0x\1/p' quiet_cmd_zoffset = ZOFFSET $@ cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index f4f7b22d8113..3a38fdcdb9bd 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -106,6 +106,11 @@ vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o vmlinux-libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a vmlinux-libs-$(CONFIG_X86_64) += $(objtree)/arch/x86/boot/startup/lib.a +vmlinux-objs-$(CONFIG_EFI_SBAT) += $(obj)/sbat.o + +ifdef CONFIG_EFI_SBAT +$(obj)/sbat.o: $(CONFIG_EFI_SBAT_FILE) +endif $(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE $(call if_changed,ld) diff --git a/arch/x86/boot/compressed/sbat.S b/arch/x86/boot/compressed/sbat.S new file mode 100644 index 000000000000..838f70a997dd --- /dev/null +++ b/arch/x86/boot/compressed/sbat.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Embed SBAT data in the kernel. + */ + .pushsection ".sbat", "a", @progbits + .incbin CONFIG_EFI_SBAT_FILE + .popsection diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S index 3b2bc61c9408..587ce3e7c504 100644 --- a/arch/x86/boot/compressed/vmlinux.lds.S +++ b/arch/x86/boot/compressed/vmlinux.lds.S @@ -43,6 +43,14 @@ SECTIONS *(.rodata.*) _erodata = . ; } +#ifdef CONFIG_EFI_SBAT + .sbat : ALIGN(0x1000) { + _sbat = . ; + *(.sbat) + _esbat = ALIGN(0x1000); + . = _esbat; + } +#endif .data : ALIGN(0x1000) { _data = . ; *(.data) diff --git a/arch/x86/boot/cpuflags.c b/arch/x86/boot/cpuflags.c index 916bac09b464..63e037e94e4c 100644 --- a/arch/x86/boot/cpuflags.c +++ b/arch/x86/boot/cpuflags.c @@ -106,5 +106,18 @@ void get_cpuflags(void) cpuid(0x80000001, &ignored, &ignored, &cpu.flags[6], &cpu.flags[1]); } + + if (max_amd_level >= 0x8000001f) { + u32 ebx; + + /* + * The X86_FEATURE_COHERENCY_SFW_NO feature bit is in + * the virtualization flags entry (word 8) and set by + * scattered.c, so the bit needs to be explicitly set. + */ + cpuid(0x8000001f, &ignored, &ebx, &ignored, &ignored); + if (ebx & BIT(31)) + set_bit(X86_FEATURE_COHERENCY_SFW_NO, cpu.flags); + } } } diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index e1f4fd5bc8ee..9bea5a1e2c52 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -179,15 +179,11 @@ pecompat_fstart: #else .set pecompat_fstart, setup_size #endif - .ascii ".text" - .byte 0 - .byte 0 - .byte 0 - .long ZO__data - .long setup_size - .long ZO__data # Size of initialized data - # on disk - .long setup_size + .ascii ".text\0\0\0" + .long textsize # VirtualSize + .long setup_size # VirtualAddress + .long textsize # SizeOfRawData + .long setup_size # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations @@ -196,6 +192,23 @@ pecompat_fstart: IMAGE_SCN_MEM_READ | \ IMAGE_SCN_MEM_EXECUTE # Characteristics +#ifdef CONFIG_EFI_SBAT + .ascii ".sbat\0\0\0" + .long ZO__esbat - ZO__sbat # VirtualSize + .long setup_size + ZO__sbat # VirtualAddress + .long ZO__esbat - ZO__sbat # SizeOfRawData + .long setup_size + ZO__sbat # PointerToRawData + + .long 0, 0, 0 + .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_DISCARDABLE # Characteristics + + .set textsize, ZO__sbat +#else + .set textsize, ZO__data +#endif + .ascii ".data\0\0\0" .long ZO__end - ZO__data # VirtualSize .long setup_size + ZO__data # VirtualAddress diff --git a/arch/x86/boot/startup/sev-shared.c b/arch/x86/boot/startup/sev-shared.c index 7a706db87b93..a34cd19796f9 100644 --- a/arch/x86/boot/startup/sev-shared.c +++ b/arch/x86/boot/startup/sev-shared.c @@ -785,6 +785,7 @@ static void __head svsm_pval_4k_page(unsigned long paddr, bool validate) pc->entry[0].page_size = RMP_PG_SIZE_4K; pc->entry[0].action = validate; pc->entry[0].ignore_cf = 0; + pc->entry[0].rsvd = 0; pc->entry[0].pfn = paddr >> PAGE_SHIFT; /* Protocol 0, Call ID 1 */ @@ -810,6 +811,13 @@ static void __head pvalidate_4k_page(unsigned long vaddr, unsigned long paddr, if (ret) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE); } + + /* + * If validating memory (making it private) and affected by the + * cache-coherency vulnerability, perform the cache eviction mitigation. + */ + if (validate && !has_cpuflag(X86_FEATURE_COHERENCY_SFW_NO)) + sev_evict_cache((void *)vaddr, 1); } /* diff --git a/arch/x86/coco/sev/Makefile b/arch/x86/coco/sev/Makefile index db3255b979bd..342d79f0ab6a 100644 --- a/arch/x86/coco/sev/Makefile +++ b/arch/x86/coco/sev/Makefile @@ -5,5 +5,6 @@ obj-y += core.o sev-nmi.o vc-handle.o # Clang 14 and older may fail to respect __no_sanitize_undefined when inlining UBSAN_SANITIZE_sev-nmi.o := n -# GCC may fail to respect __no_sanitize_address when inlining +# GCC may fail to respect __no_sanitize_address or __no_kcsan when inlining KASAN_SANITIZE_sev-nmi.o := n +KCSAN_SANITIZE_sev-nmi.o := n diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index b6db4e0b936b..14ef5908fb27 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -88,7 +88,7 @@ static const char * const sev_status_feat_names[] = { */ static u64 snp_tsc_scale __ro_after_init; static u64 snp_tsc_offset __ro_after_init; -static u64 snp_tsc_freq_khz __ro_after_init; +static unsigned long snp_tsc_freq_khz __ro_after_init; DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data); DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa); @@ -227,6 +227,7 @@ static u64 svsm_build_ca_from_pfn_range(u64 pfn, u64 pfn_end, bool action, pe->page_size = RMP_PG_SIZE_4K; pe->action = action; pe->ignore_cf = 0; + pe->rsvd = 0; pe->pfn = pfn; pe++; @@ -257,6 +258,7 @@ static int svsm_build_ca_from_psc_desc(struct snp_psc_desc *desc, unsigned int d pe->page_size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K; pe->action = e->operation == SNP_PAGE_STATE_PRIVATE; pe->ignore_cf = 0; + pe->rsvd = 0; pe->pfn = e->gfn; pe++; @@ -358,10 +360,31 @@ static void svsm_pval_pages(struct snp_psc_desc *desc) static void pvalidate_pages(struct snp_psc_desc *desc) { + struct psc_entry *e; + unsigned int i; + if (snp_vmpl) svsm_pval_pages(desc); else pval_pages(desc); + + /* + * If not affected by the cache-coherency vulnerability there is no need + * to perform the cache eviction mitigation. + */ + if (cpu_feature_enabled(X86_FEATURE_COHERENCY_SFW_NO)) + return; + + for (i = 0; i <= desc->hdr.end_entry; i++) { + e = &desc->entries[i]; + + /* + * If validating memory (making it private) perform the cache + * eviction mitigation. + */ + if (e->operation == SNP_PAGE_STATE_PRIVATE) + sev_evict_cache(pfn_to_kaddr(e->gfn), e->pagesize ? 512 : 1); + } } static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc) @@ -1045,11 +1068,13 @@ int __init sev_es_setup_ap_jump_table(struct real_mode_header *rmh) * This is needed by the OVMF UEFI firmware which will use whatever it finds in * the GHCB MSR as its GHCB to talk to the hypervisor. So make sure the per-cpu * runtime GHCBs used by the kernel are also mapped in the EFI page-table. + * + * When running under SVSM the CA page is needed too, so map it as well. */ -int __init sev_es_efi_map_ghcbs(pgd_t *pgd) +int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd) { + unsigned long address, pflags, pflags_enc; struct sev_es_runtime_data *data; - unsigned long address, pflags; int cpu; u64 pfn; @@ -1057,6 +1082,7 @@ int __init sev_es_efi_map_ghcbs(pgd_t *pgd) return 0; pflags = _PAGE_NX | _PAGE_RW; + pflags_enc = cc_mkenc(pflags); for_each_possible_cpu(cpu) { data = per_cpu(runtime_data, cpu); @@ -1066,6 +1092,16 @@ int __init sev_es_efi_map_ghcbs(pgd_t *pgd) if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags)) return 1; + + if (snp_vmpl) { + address = per_cpu(svsm_caa_pa, cpu); + if (!address) + return 1; + + pfn = address >> PAGE_SHIFT; + if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags_enc)) + return 1; + } } return 0; @@ -1389,16 +1425,16 @@ int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, } EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req); -static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input, - struct snp_guest_request_ioctl *rio) +static int snp_issue_guest_request(struct snp_guest_req *req) { + struct snp_req_data *input = &req->input; struct ghcb_state state; struct es_em_ctxt ctxt; unsigned long flags; struct ghcb *ghcb; int ret; - rio->exitinfo2 = SEV_RET_NO_FW_CALL; + req->exitinfo2 = SEV_RET_NO_FW_CALL; /* * __sev_get_ghcb() needs to run with IRQs disabled because it is using @@ -1423,8 +1459,8 @@ static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_dat if (ret) goto e_put; - rio->exitinfo2 = ghcb->save.sw_exit_info_2; - switch (rio->exitinfo2) { + req->exitinfo2 = ghcb->save.sw_exit_info_2; + switch (req->exitinfo2) { case 0: break; @@ -1919,8 +1955,7 @@ static int enc_payload(struct snp_msg_desc *mdesc, u64 seqno, struct snp_guest_r return 0; } -static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, - struct snp_guest_request_ioctl *rio) +static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req) { unsigned long req_start = jiffies; unsigned int override_npages = 0; @@ -1934,7 +1969,7 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r * sequence number must be incremented or the VMPCK must be deleted to * prevent reuse of the IV. */ - rc = snp_issue_guest_request(req, &req->input, rio); + rc = snp_issue_guest_request(req); switch (rc) { case -ENOSPC: /* @@ -1987,7 +2022,7 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r snp_inc_msg_seqno(mdesc); if (override_err) { - rio->exitinfo2 = override_err; + req->exitinfo2 = override_err; /* * If an extended guest request was issued and the supplied certificate @@ -2005,12 +2040,20 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r return rc; } -int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, - struct snp_guest_request_ioctl *rio) +int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req) { u64 seqno; int rc; + /* + * enc_payload() calls aesgcm_encrypt(), which can potentially offload to HW. + * The offload's DMA SG list of data to encrypt has to be in linear mapping. + */ + if (!virt_addr_valid(req->req_buf) || !virt_addr_valid(req->resp_buf)) { + pr_warn("AES-GSM buffers must be in linear mapping"); + return -EINVAL; + } + guard(mutex)(&snp_cmd_mutex); /* Check if the VMPCK is not empty */ @@ -2043,14 +2086,14 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req req->input.resp_gpa = __pa(mdesc->response); req->input.data_gpa = req->certs_data ? __pa(req->certs_data) : 0; - rc = __handle_guest_request(mdesc, req, rio); + rc = __handle_guest_request(mdesc, req); if (rc) { if (rc == -EIO && - rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN)) + req->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN)) return rc; pr_alert("Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n", - rc, rio->exitinfo2); + rc, req->exitinfo2); snp_disable_vmpck(mdesc); return rc; @@ -2069,11 +2112,10 @@ EXPORT_SYMBOL_GPL(snp_send_guest_request); static int __init snp_get_tsc_info(void) { - struct snp_guest_request_ioctl *rio; struct snp_tsc_info_resp *tsc_resp; struct snp_tsc_info_req *tsc_req; struct snp_msg_desc *mdesc; - struct snp_guest_req *req; + struct snp_guest_req req = {}; int rc = -ENOMEM; tsc_req = kzalloc(sizeof(*tsc_req), GFP_KERNEL); @@ -2089,32 +2131,24 @@ static int __init snp_get_tsc_info(void) if (!tsc_resp) goto e_free_tsc_req; - req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) - goto e_free_tsc_resp; - - rio = kzalloc(sizeof(*rio), GFP_KERNEL); - if (!rio) - goto e_free_req; - mdesc = snp_msg_alloc(); if (IS_ERR_OR_NULL(mdesc)) - goto e_free_rio; + goto e_free_tsc_resp; rc = snp_msg_init(mdesc, snp_vmpl); if (rc) goto e_free_mdesc; - req->msg_version = MSG_HDR_VER; - req->msg_type = SNP_MSG_TSC_INFO_REQ; - req->vmpck_id = snp_vmpl; - req->req_buf = tsc_req; - req->req_sz = sizeof(*tsc_req); - req->resp_buf = (void *)tsc_resp; - req->resp_sz = sizeof(*tsc_resp) + AUTHTAG_LEN; - req->exit_code = SVM_VMGEXIT_GUEST_REQUEST; + req.msg_version = MSG_HDR_VER; + req.msg_type = SNP_MSG_TSC_INFO_REQ; + req.vmpck_id = snp_vmpl; + req.req_buf = tsc_req; + req.req_sz = sizeof(*tsc_req); + req.resp_buf = (void *)tsc_resp; + req.resp_sz = sizeof(*tsc_resp) + AUTHTAG_LEN; + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST; - rc = snp_send_guest_request(mdesc, req, rio); + rc = snp_send_guest_request(mdesc, &req); if (rc) goto e_request; @@ -2135,11 +2169,7 @@ static int __init snp_get_tsc_info(void) memzero_explicit(tsc_resp, sizeof(*tsc_resp) + AUTHTAG_LEN); e_free_mdesc: snp_msg_free(mdesc); -e_free_rio: - kfree(rio); -e_free_req: - kfree(req); - e_free_tsc_resp: +e_free_tsc_resp: kfree(tsc_resp); e_free_tsc_req: kfree(tsc_req); @@ -2167,15 +2197,31 @@ static unsigned long securetsc_get_tsc_khz(void) void __init snp_secure_tsc_init(void) { - unsigned long long tsc_freq_mhz; + struct snp_secrets_page *secrets; + unsigned long tsc_freq_mhz; + void *mem; if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) return; + mem = early_memremap_encrypted(sev_secrets_pa, PAGE_SIZE); + if (!mem) { + pr_err("Unable to get TSC_FACTOR: failed to map the SNP secrets page.\n"); + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECURE_TSC); + } + + secrets = (__force struct snp_secrets_page *)mem; + setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); rdmsrq(MSR_AMD64_GUEST_TSC_FREQ, tsc_freq_mhz); - snp_tsc_freq_khz = (unsigned long)(tsc_freq_mhz * 1000); + + /* Extract the GUEST TSC MHZ from BIT[17:0], rest is reserved space */ + tsc_freq_mhz &= GENMASK_ULL(17, 0); + + snp_tsc_freq_khz = SNP_SCALE_TSC_FREQ(tsc_freq_mhz * 1000, secrets->tsc_factor); x86_platform.calibrate_cpu = securetsc_get_tsc_khz; x86_platform.calibrate_tsc = securetsc_get_tsc_khz; + + early_memunmap(mem, PAGE_SIZE); } diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c index 0989d98da130..c3b4acbde0d8 100644 --- a/arch/x86/coco/sev/vc-handle.c +++ b/arch/x86/coco/sev/vc-handle.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -178,9 +179,15 @@ static enum es_result __vc_decode_kern_insn(struct es_em_ctxt *ctxt) return ES_OK; } +/* + * User instruction decoding is also required for the EFI runtime. Even though + * the EFI runtime is running in kernel mode, it uses special EFI virtual + * address mappings that require the use of efi_mm to properly address and + * decode. + */ static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt) { - if (user_mode(ctxt->regs)) + if (user_mode(ctxt->regs) || mm_is_efi(current->active_mm)) return __vc_decode_user_insn(ctxt); else return __vc_decode_kern_insn(ctxt); @@ -364,29 +371,30 @@ static enum es_result __vc_handle_msr_caa(struct pt_regs *regs, bool write) * executing with Secure TSC enabled, so special handling is required for * accesses of MSR_IA32_TSC and MSR_AMD64_GUEST_TSC_FREQ. */ -static enum es_result __vc_handle_secure_tsc_msrs(struct pt_regs *regs, bool write) +static enum es_result __vc_handle_secure_tsc_msrs(struct es_em_ctxt *ctxt, bool write) { + struct pt_regs *regs = ctxt->regs; u64 tsc; /* - * GUEST_TSC_FREQ should not be intercepted when Secure TSC is enabled. - * Terminate the SNP guest when the interception is enabled. + * Writing to MSR_IA32_TSC can cause subsequent reads of the TSC to + * return undefined values, and GUEST_TSC_FREQ is read-only. Generate + * a #GP on all writes. + */ + if (write) { + ctxt->fi.vector = X86_TRAP_GP; + ctxt->fi.error_code = 0; + return ES_EXCEPTION; + } + + /* + * GUEST_TSC_FREQ read should not be intercepted when Secure TSC is + * enabled. Terminate the guest if a read is attempted. */ if (regs->cx == MSR_AMD64_GUEST_TSC_FREQ) return ES_VMM_ERROR; - /* - * Writes: Writing to MSR_IA32_TSC can cause subsequent reads of the TSC - * to return undefined values, so ignore all writes. - * - * Reads: Reads of MSR_IA32_TSC should return the current TSC value, use - * the value returned by rdtsc_ordered(). - */ - if (write) { - WARN_ONCE(1, "TSC MSR writes are verboten!\n"); - return ES_OK; - } - + /* Reads of MSR_IA32_TSC should return the current TSC value. */ tsc = rdtsc_ordered(); regs->ax = lower_32_bits(tsc); regs->dx = upper_32_bits(tsc); @@ -409,7 +417,7 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt) case MSR_IA32_TSC: case MSR_AMD64_GUEST_TSC_FREQ: if (sev_status & MSR_AMD64_SNP_SECURE_TSC) - return __vc_handle_secure_tsc_msrs(regs, write); + return __vc_handle_secure_tsc_msrs(ctxt, write); break; default: break; diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index 7cd2f395f301..79fa38ca954d 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -27,10 +27,12 @@ CONFIG_CGROUP_DEBUG=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y +CONFIG_KEXEC=y +# Do not remove this as it results in non-bootable kernels +# CONFIG_64BIT is not set CONFIG_SMP=y CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y -CONFIG_NR_CPUS=8 CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y CONFIG_X86_MSR=y CONFIG_X86_CPUID=y @@ -39,9 +41,6 @@ CONFIG_X86_CHECK_BIOS_CORRUPTION=y CONFIG_EFI=y CONFIG_EFI_STUB=y CONFIG_HZ_1000=y -CONFIG_KEXEC=y -CONFIG_CRASH_DUMP=y -# CONFIG_MITIGATION_RETHUNK is not set CONFIG_HIBERNATION=y CONFIG_PM_DEBUG=y CONFIG_PM_TRACE_RTC=y @@ -52,7 +51,6 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_X86_ACPI_CPUFREQ=y CONFIG_KPROBES=y CONFIG_JUMP_LABEL=y -CONFIG_COMPAT_32BIT_TIME=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -63,9 +61,7 @@ CONFIG_BINFMT_MISC=y # CONFIG_COMPAT_BRK is not set CONFIG_NET=y CONFIG_PACKET=y -CONFIG_UNIX=y CONFIG_XFRM_USER=y -CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y @@ -134,7 +130,6 @@ CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_DEBUG_DEVRES=y CONFIG_CONNECTOR=y -CONFIG_EFI_CAPSULE_LOADER=y CONFIG_BLK_DEV_LOOP=y CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_SD=y @@ -210,7 +205,6 @@ CONFIG_SND_HDA_INTEL=y CONFIG_SND_HDA_HWDEP=y CONFIG_HIDRAW=y CONFIG_HID_GYRATION=y -CONFIG_LOGITECH_FF=y CONFIG_HID_NTRIG=y CONFIG_HID_PANTHERLORD=y CONFIG_PANTHERLORD_FF=y @@ -241,7 +235,6 @@ CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V2=y CONFIG_AUTOFS_FS=y CONFIG_ISO9660_FS=y @@ -266,19 +259,13 @@ CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_PRINTK_TIME=y CONFIG_DEBUG_KERNEL=y -CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_WX=y CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_PROVIDE_OHCI1394_DMA_INIT=y CONFIG_EARLY_PRINTK_DBGP=y CONFIG_DEBUG_BOOT_PARAMS=y -CONFIG_UNWINDER_FRAME_POINTER=y CONFIG_DEBUG_ENTRY=y -# CONFIG_64BIT is not set diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 61e25f6209ed..7d7310cdf8b0 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -27,6 +27,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y +CONFIG_KEXEC=y CONFIG_SMP=y CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y @@ -40,8 +41,6 @@ CONFIG_EFI=y CONFIG_EFI_STUB=y CONFIG_EFI_MIXED=y CONFIG_HZ_1000=y -CONFIG_KEXEC=y -CONFIG_CRASH_DUMP=y CONFIG_HIBERNATION=y CONFIG_PM_DEBUG=y CONFIG_PM_TRACE_RTC=y @@ -63,9 +62,7 @@ CONFIG_BINFMT_MISC=y # CONFIG_COMPAT_BRK is not set CONFIG_NET=y CONFIG_PACKET=y -CONFIG_UNIX=y CONFIG_XFRM_USER=y -CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y @@ -205,7 +202,6 @@ CONFIG_SND_HDA_INTEL=y CONFIG_SND_HDA_HWDEP=y CONFIG_HIDRAW=y CONFIG_HID_GYRATION=y -CONFIG_LOGITECH_FF=y CONFIG_HID_NTRIG=y CONFIG_HID_PANTHERLORD=y CONFIG_PANTHERLORD_FF=y @@ -239,7 +235,6 @@ CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V2=y CONFIG_AUTOFS_FS=y CONFIG_ISO9660_FS=y @@ -264,13 +259,11 @@ CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_PRINTK_TIME=y CONFIG_DEBUG_KERNEL=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_WX=y CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_PROVIDE_OHCI1394_DMA_INIT=y diff --git a/arch/x86/crypto/Kconfig b/arch/x86/crypto/Kconfig index 56cfdc79e2c6..94016c60561e 100644 --- a/arch/x86/crypto/Kconfig +++ b/arch/x86/crypto/Kconfig @@ -376,33 +376,6 @@ config CRYPTO_POLYVAL_CLMUL_NI Architecture: x86_64 using: - CLMUL-NI (carry-less multiplication new instructions) -config CRYPTO_SHA1_SSSE3 - tristate "Hash functions: SHA-1 (SSSE3/AVX/AVX2/SHA-NI)" - depends on 64BIT - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash algorithm (FIPS 180) - - Architecture: x86_64 using: - - SSSE3 (Supplemental SSE3) - - AVX (Advanced Vector Extensions) - - AVX2 (Advanced Vector Extensions 2) - - SHA-NI (SHA Extensions New Instructions) - -config CRYPTO_SHA512_SSSE3 - tristate "Hash functions: SHA-384 and SHA-512 (SSSE3/AVX/AVX2)" - depends on 64BIT - select CRYPTO_SHA512 - select CRYPTO_HASH - help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180) - - Architecture: x86_64 using: - - SSSE3 (Supplemental SSE3) - - AVX (Advanced Vector Extensions) - - AVX2 (Advanced Vector Extensions 2) - config CRYPTO_SM3_AVX_X86_64 tristate "Hash functions: SM3 (AVX)" depends on 64BIT diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile index aa289a9e0153..d402963d6b57 100644 --- a/arch/x86/crypto/Makefile +++ b/arch/x86/crypto/Makefile @@ -51,12 +51,6 @@ ifeq ($(CONFIG_AS_VAES)$(CONFIG_AS_VPCLMULQDQ),yy) aesni-intel-$(CONFIG_64BIT) += aes-gcm-avx10-x86_64.o endif -obj-$(CONFIG_CRYPTO_SHA1_SSSE3) += sha1-ssse3.o -sha1-ssse3-y := sha1_avx2_x86_64_asm.o sha1_ssse3_asm.o sha1_ni_asm.o sha1_ssse3_glue.o - -obj-$(CONFIG_CRYPTO_SHA512_SSSE3) += sha512-ssse3.o -sha512-ssse3-y := sha512-ssse3-asm.o sha512-avx-asm.o sha512-avx2-asm.o sha512_ssse3_glue.o - obj-$(CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL) += ghash-clmulni-intel.o ghash-clmulni-intel-y := ghash-clmulni-intel_asm.o ghash-clmulni-intel_glue.o diff --git a/arch/x86/crypto/aegis128-aesni-glue.c b/arch/x86/crypto/aegis128-aesni-glue.c index f1b6d40154e3..f1adfba1a76e 100644 --- a/arch/x86/crypto/aegis128-aesni-glue.c +++ b/arch/x86/crypto/aegis128-aesni-glue.c @@ -104,10 +104,12 @@ static void crypto_aegis128_aesni_process_ad( } } -static __always_inline void +static __always_inline int crypto_aegis128_aesni_process_crypt(struct aegis_state *state, struct skcipher_walk *walk, bool enc) { + int err = 0; + while (walk->nbytes >= AEGIS128_BLOCK_SIZE) { if (enc) aegis128_aesni_enc(state, walk->src.virt.addr, @@ -119,7 +121,10 @@ crypto_aegis128_aesni_process_crypt(struct aegis_state *state, walk->dst.virt.addr, round_down(walk->nbytes, AEGIS128_BLOCK_SIZE)); - skcipher_walk_done(walk, walk->nbytes % AEGIS128_BLOCK_SIZE); + kernel_fpu_end(); + err = skcipher_walk_done(walk, + walk->nbytes % AEGIS128_BLOCK_SIZE); + kernel_fpu_begin(); } if (walk->nbytes) { @@ -131,8 +136,11 @@ crypto_aegis128_aesni_process_crypt(struct aegis_state *state, aegis128_aesni_dec_tail(state, walk->src.virt.addr, walk->dst.virt.addr, walk->nbytes); - skcipher_walk_done(walk, 0); + kernel_fpu_end(); + err = skcipher_walk_done(walk, 0); + kernel_fpu_begin(); } + return err; } static struct aegis_ctx *crypto_aegis128_aesni_ctx(struct crypto_aead *aead) @@ -165,7 +173,7 @@ static int crypto_aegis128_aesni_setauthsize(struct crypto_aead *tfm, return 0; } -static __always_inline void +static __always_inline int crypto_aegis128_aesni_crypt(struct aead_request *req, struct aegis_block *tag_xor, unsigned int cryptlen, bool enc) @@ -174,20 +182,24 @@ crypto_aegis128_aesni_crypt(struct aead_request *req, struct aegis_ctx *ctx = crypto_aegis128_aesni_ctx(tfm); struct skcipher_walk walk; struct aegis_state state; + int err; if (enc) - skcipher_walk_aead_encrypt(&walk, req, true); + err = skcipher_walk_aead_encrypt(&walk, req, false); else - skcipher_walk_aead_decrypt(&walk, req, true); + err = skcipher_walk_aead_decrypt(&walk, req, false); + if (err) + return err; kernel_fpu_begin(); aegis128_aesni_init(&state, &ctx->key, req->iv); crypto_aegis128_aesni_process_ad(&state, req->src, req->assoclen); - crypto_aegis128_aesni_process_crypt(&state, &walk, enc); - aegis128_aesni_final(&state, tag_xor, req->assoclen, cryptlen); - + err = crypto_aegis128_aesni_process_crypt(&state, &walk, enc); + if (err == 0) + aegis128_aesni_final(&state, tag_xor, req->assoclen, cryptlen); kernel_fpu_end(); + return err; } static int crypto_aegis128_aesni_encrypt(struct aead_request *req) @@ -196,8 +208,11 @@ static int crypto_aegis128_aesni_encrypt(struct aead_request *req) struct aegis_block tag = {}; unsigned int authsize = crypto_aead_authsize(tfm); unsigned int cryptlen = req->cryptlen; + int err; - crypto_aegis128_aesni_crypt(req, &tag, cryptlen, true); + err = crypto_aegis128_aesni_crypt(req, &tag, cryptlen, true); + if (err) + return err; scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, authsize, 1); @@ -212,11 +227,14 @@ static int crypto_aegis128_aesni_decrypt(struct aead_request *req) struct aegis_block tag; unsigned int authsize = crypto_aead_authsize(tfm); unsigned int cryptlen = req->cryptlen - authsize; + int err; scatterwalk_map_and_copy(tag.bytes, req->src, req->assoclen + cryptlen, authsize, 0); - crypto_aegis128_aesni_crypt(req, &tag, cryptlen, false); + err = crypto_aegis128_aesni_crypt(req, &tag, cryptlen, false); + if (err) + return err; return crypto_memneq(tag.bytes, zeros.bytes, authsize) ? -EBADMSG : 0; } diff --git a/arch/x86/crypto/aria_aesni_avx2_glue.c b/arch/x86/crypto/aria_aesni_avx2_glue.c index b4bddcd58457..007b250f774c 100644 --- a/arch/x86/crypto/aria_aesni_avx2_glue.c +++ b/arch/x86/crypto/aria_aesni_avx2_glue.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/crypto/aria_aesni_avx_glue.c b/arch/x86/crypto/aria_aesni_avx_glue.c index ab9b38d05332..4c88ef4eba82 100644 --- a/arch/x86/crypto/aria_aesni_avx_glue.c +++ b/arch/x86/crypto/aria_aesni_avx_glue.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/crypto/camellia_aesni_avx_glue.c b/arch/x86/crypto/camellia_aesni_avx_glue.c index a7d162388142..5c321f255eb7 100644 --- a/arch/x86/crypto/camellia_aesni_avx_glue.c +++ b/arch/x86/crypto/camellia_aesni_avx_glue.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/crypto/camellia_glue.c b/arch/x86/crypto/camellia_glue.c index 3bd37d664121..cbede120e5f2 100644 --- a/arch/x86/crypto/camellia_glue.c +++ b/arch/x86/crypto/camellia_glue.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/crypto/curve25519-x86_64.c b/arch/x86/crypto/curve25519-x86_64.c index dcfc0de333de..d587f05c3c8c 100644 --- a/arch/x86/crypto/curve25519-x86_64.c +++ b/arch/x86/crypto/curve25519-x86_64.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/crypto/serpent_avx_glue.c b/arch/x86/crypto/serpent_avx_glue.c index e640abc1cb8a..9c8b3a335d5c 100644 --- a/arch/x86/crypto/serpent_avx_glue.c +++ b/arch/x86/crypto/serpent_avx_glue.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/crypto/sha1_ni_asm.S b/arch/x86/crypto/sha1_ni_asm.S deleted file mode 100644 index cade913d4882..000000000000 --- a/arch/x86/crypto/sha1_ni_asm.S +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Intel SHA Extensions optimized implementation of a SHA-1 update function - * - * 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 - * - * Copyright(c) 2015 Intel Corporation. - * - * 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. - * - * Contact Information: - * Sean Gulley - * Tim Chen - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Corporation. - * - * 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. - * - */ - -#include -#include - -#define DIGEST_PTR %rdi /* 1st arg */ -#define DATA_PTR %rsi /* 2nd arg */ -#define NUM_BLKS %rdx /* 3rd arg */ - -/* gcc conversion */ -#define FRAME_SIZE 32 /* space for 2x16 bytes */ - -#define ABCD %xmm0 -#define E0 %xmm1 /* Need two E's b/c they ping pong */ -#define E1 %xmm2 -#define MSG0 %xmm3 -#define MSG1 %xmm4 -#define MSG2 %xmm5 -#define MSG3 %xmm6 -#define SHUF_MASK %xmm7 - - -/* - * Intel SHA Extensions optimized implementation of a SHA-1 update function - * - * The function takes a pointer to the current hash values, a pointer to the - * input data, and a number of 64 byte blocks to process. Once all blocks have - * been processed, the digest pointer is updated with the resulting hash value. - * The function only processes complete blocks, there is no functionality to - * store partial blocks. All message padding and hash value initialization must - * be done outside the update function. - * - * The indented lines in the loop are instructions related to rounds processing. - * The non-indented lines are instructions related to the message schedule. - * - * void sha1_ni_transform(uint32_t *digest, const void *data, - uint32_t numBlocks) - * digest : pointer to digest - * data: pointer to input data - * numBlocks: Number of blocks to process - */ -.text -SYM_TYPED_FUNC_START(sha1_ni_transform) - push %rbp - mov %rsp, %rbp - sub $FRAME_SIZE, %rsp - and $~0xF, %rsp - - shl $6, NUM_BLKS /* convert to bytes */ - jz .Ldone_hash - add DATA_PTR, NUM_BLKS /* pointer to end of data */ - - /* load initial hash values */ - pinsrd $3, 1*16(DIGEST_PTR), E0 - movdqu 0*16(DIGEST_PTR), ABCD - pand UPPER_WORD_MASK(%rip), E0 - pshufd $0x1B, ABCD, ABCD - - movdqa PSHUFFLE_BYTE_FLIP_MASK(%rip), SHUF_MASK - -.Lloop0: - /* Save hash values for addition after rounds */ - movdqa E0, (0*16)(%rsp) - movdqa ABCD, (1*16)(%rsp) - - /* Rounds 0-3 */ - movdqu 0*16(DATA_PTR), MSG0 - pshufb SHUF_MASK, MSG0 - paddd MSG0, E0 - movdqa ABCD, E1 - sha1rnds4 $0, E0, ABCD - - /* Rounds 4-7 */ - movdqu 1*16(DATA_PTR), MSG1 - pshufb SHUF_MASK, MSG1 - sha1nexte MSG1, E1 - movdqa ABCD, E0 - sha1rnds4 $0, E1, ABCD - sha1msg1 MSG1, MSG0 - - /* Rounds 8-11 */ - movdqu 2*16(DATA_PTR), MSG2 - pshufb SHUF_MASK, MSG2 - sha1nexte MSG2, E0 - movdqa ABCD, E1 - sha1rnds4 $0, E0, ABCD - sha1msg1 MSG2, MSG1 - pxor MSG2, MSG0 - - /* Rounds 12-15 */ - movdqu 3*16(DATA_PTR), MSG3 - pshufb SHUF_MASK, MSG3 - sha1nexte MSG3, E1 - movdqa ABCD, E0 - sha1msg2 MSG3, MSG0 - sha1rnds4 $0, E1, ABCD - sha1msg1 MSG3, MSG2 - pxor MSG3, MSG1 - - /* Rounds 16-19 */ - sha1nexte MSG0, E0 - movdqa ABCD, E1 - sha1msg2 MSG0, MSG1 - sha1rnds4 $0, E0, ABCD - sha1msg1 MSG0, MSG3 - pxor MSG0, MSG2 - - /* Rounds 20-23 */ - sha1nexte MSG1, E1 - movdqa ABCD, E0 - sha1msg2 MSG1, MSG2 - sha1rnds4 $1, E1, ABCD - sha1msg1 MSG1, MSG0 - pxor MSG1, MSG3 - - /* Rounds 24-27 */ - sha1nexte MSG2, E0 - movdqa ABCD, E1 - sha1msg2 MSG2, MSG3 - sha1rnds4 $1, E0, ABCD - sha1msg1 MSG2, MSG1 - pxor MSG2, MSG0 - - /* Rounds 28-31 */ - sha1nexte MSG3, E1 - movdqa ABCD, E0 - sha1msg2 MSG3, MSG0 - sha1rnds4 $1, E1, ABCD - sha1msg1 MSG3, MSG2 - pxor MSG3, MSG1 - - /* Rounds 32-35 */ - sha1nexte MSG0, E0 - movdqa ABCD, E1 - sha1msg2 MSG0, MSG1 - sha1rnds4 $1, E0, ABCD - sha1msg1 MSG0, MSG3 - pxor MSG0, MSG2 - - /* Rounds 36-39 */ - sha1nexte MSG1, E1 - movdqa ABCD, E0 - sha1msg2 MSG1, MSG2 - sha1rnds4 $1, E1, ABCD - sha1msg1 MSG1, MSG0 - pxor MSG1, MSG3 - - /* Rounds 40-43 */ - sha1nexte MSG2, E0 - movdqa ABCD, E1 - sha1msg2 MSG2, MSG3 - sha1rnds4 $2, E0, ABCD - sha1msg1 MSG2, MSG1 - pxor MSG2, MSG0 - - /* Rounds 44-47 */ - sha1nexte MSG3, E1 - movdqa ABCD, E0 - sha1msg2 MSG3, MSG0 - sha1rnds4 $2, E1, ABCD - sha1msg1 MSG3, MSG2 - pxor MSG3, MSG1 - - /* Rounds 48-51 */ - sha1nexte MSG0, E0 - movdqa ABCD, E1 - sha1msg2 MSG0, MSG1 - sha1rnds4 $2, E0, ABCD - sha1msg1 MSG0, MSG3 - pxor MSG0, MSG2 - - /* Rounds 52-55 */ - sha1nexte MSG1, E1 - movdqa ABCD, E0 - sha1msg2 MSG1, MSG2 - sha1rnds4 $2, E1, ABCD - sha1msg1 MSG1, MSG0 - pxor MSG1, MSG3 - - /* Rounds 56-59 */ - sha1nexte MSG2, E0 - movdqa ABCD, E1 - sha1msg2 MSG2, MSG3 - sha1rnds4 $2, E0, ABCD - sha1msg1 MSG2, MSG1 - pxor MSG2, MSG0 - - /* Rounds 60-63 */ - sha1nexte MSG3, E1 - movdqa ABCD, E0 - sha1msg2 MSG3, MSG0 - sha1rnds4 $3, E1, ABCD - sha1msg1 MSG3, MSG2 - pxor MSG3, MSG1 - - /* Rounds 64-67 */ - sha1nexte MSG0, E0 - movdqa ABCD, E1 - sha1msg2 MSG0, MSG1 - sha1rnds4 $3, E0, ABCD - sha1msg1 MSG0, MSG3 - pxor MSG0, MSG2 - - /* Rounds 68-71 */ - sha1nexte MSG1, E1 - movdqa ABCD, E0 - sha1msg2 MSG1, MSG2 - sha1rnds4 $3, E1, ABCD - pxor MSG1, MSG3 - - /* Rounds 72-75 */ - sha1nexte MSG2, E0 - movdqa ABCD, E1 - sha1msg2 MSG2, MSG3 - sha1rnds4 $3, E0, ABCD - - /* Rounds 76-79 */ - sha1nexte MSG3, E1 - movdqa ABCD, E0 - sha1rnds4 $3, E1, ABCD - - /* Add current hash values with previously saved */ - sha1nexte (0*16)(%rsp), E0 - paddd (1*16)(%rsp), ABCD - - /* Increment data pointer and loop if more to process */ - add $64, DATA_PTR - cmp NUM_BLKS, DATA_PTR - jne .Lloop0 - - /* Write hash values back in the correct order */ - pshufd $0x1B, ABCD, ABCD - movdqu ABCD, 0*16(DIGEST_PTR) - pextrd $3, E0, 1*16(DIGEST_PTR) - -.Ldone_hash: - mov %rbp, %rsp - pop %rbp - - RET -SYM_FUNC_END(sha1_ni_transform) - -.section .rodata.cst16.PSHUFFLE_BYTE_FLIP_MASK, "aM", @progbits, 16 -.align 16 -PSHUFFLE_BYTE_FLIP_MASK: - .octa 0x000102030405060708090a0b0c0d0e0f - -.section .rodata.cst16.UPPER_WORD_MASK, "aM", @progbits, 16 -.align 16 -UPPER_WORD_MASK: - .octa 0xFFFFFFFF000000000000000000000000 diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c deleted file mode 100644 index 0a912bfc86c5..000000000000 --- a/arch/x86/crypto/sha1_ssse3_glue.c +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * - * Glue code for the SHA1 Secure Hash Algorithm assembler implementations - * using SSSE3, AVX, AVX2, and SHA-NI instructions. - * - * This file is based on sha1_generic.c - * - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - * Copyright (c) Mathias Krause - * Copyright (c) Chandramouli Narayanan - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -static const struct x86_cpu_id module_cpu_ids[] = { - X86_MATCH_FEATURE(X86_FEATURE_SHA_NI, NULL), - X86_MATCH_FEATURE(X86_FEATURE_AVX2, NULL), - X86_MATCH_FEATURE(X86_FEATURE_AVX, NULL), - X86_MATCH_FEATURE(X86_FEATURE_SSSE3, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, module_cpu_ids); - -static inline int sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len, sha1_block_fn *sha1_xform) -{ - int remain; - - /* - * Make sure struct sha1_state begins directly with the SHA1 - * 160-bit internal state, as this is what the asm functions expect. - */ - BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0); - - kernel_fpu_begin(); - remain = sha1_base_do_update_blocks(desc, data, len, sha1_xform); - kernel_fpu_end(); - - return remain; -} - -static inline int sha1_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out, - sha1_block_fn *sha1_xform) -{ - kernel_fpu_begin(); - sha1_base_do_finup(desc, data, len, sha1_xform); - kernel_fpu_end(); - - return sha1_base_finish(desc, out); -} - -asmlinkage void sha1_transform_ssse3(struct sha1_state *state, - const u8 *data, int blocks); - -static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_update(desc, data, len, sha1_transform_ssse3); -} - -static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha1_finup(desc, data, len, out, sha1_transform_ssse3); -} - -static struct shash_alg sha1_ssse3_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_ssse3_update, - .finup = sha1_ssse3_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-ssse3", - .cra_priority = 150, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int register_sha1_ssse3(void) -{ - if (boot_cpu_has(X86_FEATURE_SSSE3)) - return crypto_register_shash(&sha1_ssse3_alg); - return 0; -} - -static void unregister_sha1_ssse3(void) -{ - if (boot_cpu_has(X86_FEATURE_SSSE3)) - crypto_unregister_shash(&sha1_ssse3_alg); -} - -asmlinkage void sha1_transform_avx(struct sha1_state *state, - const u8 *data, int blocks); - -static int sha1_avx_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_update(desc, data, len, sha1_transform_avx); -} - -static int sha1_avx_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha1_finup(desc, data, len, out, sha1_transform_avx); -} - -static struct shash_alg sha1_avx_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_avx_update, - .finup = sha1_avx_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-avx", - .cra_priority = 160, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static bool avx_usable(void) -{ - if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) { - if (boot_cpu_has(X86_FEATURE_AVX)) - pr_info("AVX detected but unusable.\n"); - return false; - } - - return true; -} - -static int register_sha1_avx(void) -{ - if (avx_usable()) - return crypto_register_shash(&sha1_avx_alg); - return 0; -} - -static void unregister_sha1_avx(void) -{ - if (avx_usable()) - crypto_unregister_shash(&sha1_avx_alg); -} - -#define SHA1_AVX2_BLOCK_OPTSIZE 4 /* optimal 4*64 bytes of SHA1 blocks */ - -asmlinkage void sha1_transform_avx2(struct sha1_state *state, - const u8 *data, int blocks); - -static bool avx2_usable(void) -{ - if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) - && boot_cpu_has(X86_FEATURE_BMI1) - && boot_cpu_has(X86_FEATURE_BMI2)) - return true; - - return false; -} - -static inline void sha1_apply_transform_avx2(struct sha1_state *state, - const u8 *data, int blocks) -{ - /* Select the optimal transform based on data block size */ - if (blocks >= SHA1_AVX2_BLOCK_OPTSIZE) - sha1_transform_avx2(state, data, blocks); - else - sha1_transform_avx(state, data, blocks); -} - -static int sha1_avx2_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_update(desc, data, len, sha1_apply_transform_avx2); -} - -static int sha1_avx2_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha1_finup(desc, data, len, out, sha1_apply_transform_avx2); -} - -static struct shash_alg sha1_avx2_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_avx2_update, - .finup = sha1_avx2_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-avx2", - .cra_priority = 170, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int register_sha1_avx2(void) -{ - if (avx2_usable()) - return crypto_register_shash(&sha1_avx2_alg); - return 0; -} - -static void unregister_sha1_avx2(void) -{ - if (avx2_usable()) - crypto_unregister_shash(&sha1_avx2_alg); -} - -asmlinkage void sha1_ni_transform(struct sha1_state *digest, const u8 *data, - int rounds); - -static int sha1_ni_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_update(desc, data, len, sha1_ni_transform); -} - -static int sha1_ni_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha1_finup(desc, data, len, out, sha1_ni_transform); -} - -static struct shash_alg sha1_ni_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = sha1_ni_update, - .finup = sha1_ni_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-ni", - .cra_priority = 250, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int register_sha1_ni(void) -{ - if (boot_cpu_has(X86_FEATURE_SHA_NI)) - return crypto_register_shash(&sha1_ni_alg); - return 0; -} - -static void unregister_sha1_ni(void) -{ - if (boot_cpu_has(X86_FEATURE_SHA_NI)) - crypto_unregister_shash(&sha1_ni_alg); -} - -static int __init sha1_ssse3_mod_init(void) -{ - if (!x86_match_cpu(module_cpu_ids)) - return -ENODEV; - - if (register_sha1_ssse3()) - goto fail; - - if (register_sha1_avx()) { - unregister_sha1_ssse3(); - goto fail; - } - - if (register_sha1_avx2()) { - unregister_sha1_avx(); - unregister_sha1_ssse3(); - goto fail; - } - - if (register_sha1_ni()) { - unregister_sha1_avx2(); - unregister_sha1_avx(); - unregister_sha1_ssse3(); - goto fail; - } - - return 0; -fail: - return -ENODEV; -} - -static void __exit sha1_ssse3_mod_fini(void) -{ - unregister_sha1_ni(); - unregister_sha1_avx2(); - unregister_sha1_avx(); - unregister_sha1_ssse3(); -} - -module_init(sha1_ssse3_mod_init); -module_exit(sha1_ssse3_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, Supplemental SSE3 accelerated"); - -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_ALIAS_CRYPTO("sha1-ssse3"); -MODULE_ALIAS_CRYPTO("sha1-avx"); -MODULE_ALIAS_CRYPTO("sha1-avx2"); -MODULE_ALIAS_CRYPTO("sha1-ni"); diff --git a/arch/x86/crypto/sha512_ssse3_glue.c b/arch/x86/crypto/sha512_ssse3_glue.c deleted file mode 100644 index 067684c54395..000000000000 --- a/arch/x86/crypto/sha512_ssse3_glue.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Cryptographic API. - * - * Glue code for the SHA512 Secure Hash Algorithm assembler - * implementation using supplemental SSE3 / AVX / AVX2 instructions. - * - * This file is based on sha512_generic.c - * - * Copyright (C) 2013 Intel Corporation - * Author: Tim Chen - * - * 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. - * - * 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -asmlinkage void sha512_transform_ssse3(struct sha512_state *state, - const u8 *data, int blocks); - -static int sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len, sha512_block_fn *sha512_xform) -{ - int remain; - - /* - * Make sure struct sha512_state begins directly with the SHA512 - * 512-bit internal state, as this is what the asm functions expect. - */ - BUILD_BUG_ON(offsetof(struct sha512_state, state) != 0); - - kernel_fpu_begin(); - remain = sha512_base_do_update_blocks(desc, data, len, sha512_xform); - kernel_fpu_end(); - - return remain; -} - -static int sha512_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out, sha512_block_fn *sha512_xform) -{ - kernel_fpu_begin(); - sha512_base_do_finup(desc, data, len, sha512_xform); - kernel_fpu_end(); - - return sha512_base_finish(desc, out); -} - -static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_update(desc, data, len, sha512_transform_ssse3); -} - -static int sha512_ssse3_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha512_finup(desc, data, len, out, sha512_transform_ssse3); -} - -static struct shash_alg sha512_ssse3_algs[] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = sha512_ssse3_update, - .finup = sha512_ssse3_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-ssse3", - .cra_priority = 150, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = sha512_ssse3_update, - .finup = sha512_ssse3_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-ssse3", - .cra_priority = 150, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static int register_sha512_ssse3(void) -{ - if (boot_cpu_has(X86_FEATURE_SSSE3)) - return crypto_register_shashes(sha512_ssse3_algs, - ARRAY_SIZE(sha512_ssse3_algs)); - return 0; -} - -static void unregister_sha512_ssse3(void) -{ - if (boot_cpu_has(X86_FEATURE_SSSE3)) - crypto_unregister_shashes(sha512_ssse3_algs, - ARRAY_SIZE(sha512_ssse3_algs)); -} - -asmlinkage void sha512_transform_avx(struct sha512_state *state, - const u8 *data, int blocks); -static bool avx_usable(void) -{ - if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) { - if (boot_cpu_has(X86_FEATURE_AVX)) - pr_info("AVX detected but unusable.\n"); - return false; - } - - return true; -} - -static int sha512_avx_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_update(desc, data, len, sha512_transform_avx); -} - -static int sha512_avx_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha512_finup(desc, data, len, out, sha512_transform_avx); -} - -static struct shash_alg sha512_avx_algs[] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = sha512_avx_update, - .finup = sha512_avx_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-avx", - .cra_priority = 160, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = sha512_avx_update, - .finup = sha512_avx_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-avx", - .cra_priority = 160, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static int register_sha512_avx(void) -{ - if (avx_usable()) - return crypto_register_shashes(sha512_avx_algs, - ARRAY_SIZE(sha512_avx_algs)); - return 0; -} - -static void unregister_sha512_avx(void) -{ - if (avx_usable()) - crypto_unregister_shashes(sha512_avx_algs, - ARRAY_SIZE(sha512_avx_algs)); -} - -asmlinkage void sha512_transform_rorx(struct sha512_state *state, - const u8 *data, int blocks); - -static int sha512_avx2_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_update(desc, data, len, sha512_transform_rorx); -} - -static int sha512_avx2_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return sha512_finup(desc, data, len, out, sha512_transform_rorx); -} - -static struct shash_alg sha512_avx2_algs[] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = sha512_avx2_update, - .finup = sha512_avx2_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-avx2", - .cra_priority = 170, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = sha512_avx2_update, - .finup = sha512_avx2_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-avx2", - .cra_priority = 170, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static bool avx2_usable(void) -{ - if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_BMI2)) - return true; - - return false; -} - -static int register_sha512_avx2(void) -{ - if (avx2_usable()) - return crypto_register_shashes(sha512_avx2_algs, - ARRAY_SIZE(sha512_avx2_algs)); - return 0; -} -static const struct x86_cpu_id module_cpu_ids[] = { - X86_MATCH_FEATURE(X86_FEATURE_AVX2, NULL), - X86_MATCH_FEATURE(X86_FEATURE_AVX, NULL), - X86_MATCH_FEATURE(X86_FEATURE_SSSE3, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, module_cpu_ids); - -static void unregister_sha512_avx2(void) -{ - if (avx2_usable()) - crypto_unregister_shashes(sha512_avx2_algs, - ARRAY_SIZE(sha512_avx2_algs)); -} - -static int __init sha512_ssse3_mod_init(void) -{ - if (!x86_match_cpu(module_cpu_ids)) - return -ENODEV; - - if (register_sha512_ssse3()) - goto fail; - - if (register_sha512_avx()) { - unregister_sha512_ssse3(); - goto fail; - } - - if (register_sha512_avx2()) { - unregister_sha512_avx(); - unregister_sha512_ssse3(); - goto fail; - } - - return 0; -fail: - return -ENODEV; -} - -static void __exit sha512_ssse3_mod_fini(void) -{ - unregister_sha512_avx2(); - unregister_sha512_avx(); - unregister_sha512_ssse3(); -} - -module_init(sha512_ssse3_mod_init); -module_exit(sha512_ssse3_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA512 Secure Hash Algorithm, Supplemental SSE3 accelerated"); - -MODULE_ALIAS_CRYPTO("sha512"); -MODULE_ALIAS_CRYPTO("sha512-ssse3"); -MODULE_ALIAS_CRYPTO("sha512-avx"); -MODULE_ALIAS_CRYPTO("sha512-avx2"); -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha384-ssse3"); -MODULE_ALIAS_CRYPTO("sha384-avx"); -MODULE_ALIAS_CRYPTO("sha384-avx2"); diff --git a/arch/x86/crypto/sm4_aesni_avx_glue.c b/arch/x86/crypto/sm4_aesni_avx_glue.c index 72867fc49ce8..88caf418a06f 100644 --- a/arch/x86/crypto/sm4_aesni_avx_glue.c +++ b/arch/x86/crypto/sm4_aesni_avx_glue.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/crypto/twofish_glue.c b/arch/x86/crypto/twofish_glue.c index 4c67184dc573..8e9906d36902 100644 --- a/arch/x86/crypto/twofish_glue.c +++ b/arch/x86/crypto/twofish_glue.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/crypto/twofish_glue_3way.c b/arch/x86/crypto/twofish_glue_3way.c index 1a1ecfa7f72a..8ad77725bf60 100644 --- a/arch/x86/crypto/twofish_glue_3way.c +++ b/arch/x86/crypto/twofish_glue_3way.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index d83236b96f22..94519688b007 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h @@ -369,7 +369,7 @@ For 32-bit we have the following conventions - kernel is built with .endm .macro STACKLEAK_ERASE_NOCLOBBER -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE PUSH_AND_CLEAR_REGS call stackleak_erase POP_REGS @@ -388,7 +388,7 @@ For 32-bit we have the following conventions - kernel is built with #endif /* !CONFIG_X86_64 */ .macro STACKLEAK_ERASE -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE call stackleak_erase #endif .endm diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S index 175958b02f2b..8e9a0cc20a4a 100644 --- a/arch/x86/entry/entry.S +++ b/arch/x86/entry/entry.S @@ -36,20 +36,20 @@ EXPORT_SYMBOL_GPL(write_ibpb); /* * Define the VERW operand that is disguised as entry code so that - * it can be referenced with KPTI enabled. This ensure VERW can be + * it can be referenced with KPTI enabled. This ensures VERW can be * used late in exit-to-user path after page tables are switched. */ .pushsection .entry.text, "ax" .align L1_CACHE_BYTES, 0xcc -SYM_CODE_START_NOALIGN(mds_verw_sel) +SYM_CODE_START_NOALIGN(x86_verw_sel) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR .word __KERNEL_DS .align L1_CACHE_BYTES, 0xcc -SYM_CODE_END(mds_verw_sel); +SYM_CODE_END(x86_verw_sel); /* For KVM */ -EXPORT_SYMBOL_GPL(mds_verw_sel); +EXPORT_SYMBOL_GPL(x86_verw_sel); .popsection diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index ac007ea00979..4877e16da69a 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -473,3 +473,5 @@ 465 i386 listxattrat sys_listxattrat 466 i386 removexattrat sys_removexattrat 467 i386 open_tree_attr sys_open_tree_attr +468 i386 file_getattr sys_file_getattr +469 i386 file_setattr sys_file_setattr diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index cfb5ca41e30d..92cf0fe2291e 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -391,6 +391,8 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr # # Due to a historical design error, certain syscalls are numbered differently diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 54d3e9774d62..f247f5f5cb44 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -62,7 +62,7 @@ ifneq ($(RETPOLINE_VDSO_CFLAGS),) endif endif -$(vobjs): KBUILD_CFLAGS := $(filter-out $(PADDING_CFLAGS) $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) +$(vobjs): KBUILD_CFLAGS := $(filter-out $(PADDING_CFLAGS) $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) $(vobjs): KBUILD_AFLAGS += -DBUILD_VDSO # @@ -123,6 +123,7 @@ KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -mfentry,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out $(KSTACK_ERASE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS_32)) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 741b229f0718..c2fb729c270e 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -2826,7 +2826,7 @@ static void intel_pmu_read_event(struct perf_event *event) * If the PEBS counters snapshotting is enabled, * the topdown event is available in PEBS records. */ - if (is_topdown_event(event) && !is_pebs_counter_event_group(event)) + if (is_topdown_count(event) && !is_pebs_counter_event_group(event)) static_call(intel_pmu_update_topdown_event)(event, NULL); else intel_pmu_drain_pebs_buffer(); diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c index e0815a12db90..a762f7f5b161 100644 --- a/arch/x86/events/intel/uncore.c +++ b/arch/x86/events/intel/uncore.c @@ -1807,6 +1807,12 @@ static const struct intel_uncore_init_fun lnl_uncore_init __initconst = { .mmio_init = lnl_uncore_mmio_init, }; +static const struct intel_uncore_init_fun ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, + .use_discovery = true, +}; + static const struct intel_uncore_init_fun icx_uncore_init __initconst = { .cpu_init = icx_uncore_cpu_init, .pci_init = icx_uncore_pci_init, @@ -1888,6 +1894,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { X86_MATCH_VFM(INTEL_ARROWLAKE_U, &mtl_uncore_init), X86_MATCH_VFM(INTEL_ARROWLAKE_H, &mtl_uncore_init), X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_uncore_init), + X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_uncore_init), X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, &spr_uncore_init), X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &spr_uncore_init), X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, &gnr_uncore_init), diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h index 3dcb88c0ecfa..d8815fff7588 100644 --- a/arch/x86/events/intel/uncore.h +++ b/arch/x86/events/intel/uncore.h @@ -612,10 +612,12 @@ void tgl_uncore_cpu_init(void); void adl_uncore_cpu_init(void); void lnl_uncore_cpu_init(void); void mtl_uncore_cpu_init(void); +void ptl_uncore_cpu_init(void); void tgl_uncore_mmio_init(void); void tgl_l_uncore_mmio_init(void); void adl_uncore_mmio_init(void); void lnl_uncore_mmio_init(void); +void ptl_uncore_mmio_init(void); int snb_pci2phy_map_init(int devid); /* uncore_snbep.c */ diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c index 18a3022f26a0..7d57ce706feb 100644 --- a/arch/x86/events/intel/uncore_discovery.c +++ b/arch/x86/events/intel/uncore_discovery.c @@ -274,32 +274,15 @@ uncore_ignore_unit(struct uncore_unit_discovery *unit, int *ignore) return false; } -static int parse_discovery_table(struct pci_dev *dev, int die, - u32 bar_offset, bool *parsed, - int *ignore) +static int __parse_discovery_table(resource_size_t addr, int die, + bool *parsed, int *ignore) { struct uncore_global_discovery global; struct uncore_unit_discovery unit; void __iomem *io_addr; - resource_size_t addr; unsigned long size; - u32 val; int i; - pci_read_config_dword(dev, bar_offset, &val); - - if (val & ~PCI_BASE_ADDRESS_MEM_MASK & ~PCI_BASE_ADDRESS_MEM_TYPE_64) - return -EINVAL; - - addr = (resource_size_t)(val & PCI_BASE_ADDRESS_MEM_MASK); -#ifdef CONFIG_PHYS_ADDR_T_64BIT - if ((val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) { - u32 val2; - - pci_read_config_dword(dev, bar_offset + 4, &val2); - addr |= ((resource_size_t)val2) << 32; - } -#endif size = UNCORE_DISCOVERY_GLOBAL_MAP_SIZE; io_addr = ioremap(addr, size); if (!io_addr) @@ -342,7 +325,32 @@ static int parse_discovery_table(struct pci_dev *dev, int die, return 0; } -bool intel_uncore_has_discovery_tables(int *ignore) +static int parse_discovery_table(struct pci_dev *dev, int die, + u32 bar_offset, bool *parsed, + int *ignore) +{ + resource_size_t addr; + u32 val; + + pci_read_config_dword(dev, bar_offset, &val); + + if (val & ~PCI_BASE_ADDRESS_MEM_MASK & ~PCI_BASE_ADDRESS_MEM_TYPE_64) + return -EINVAL; + + addr = (resource_size_t)(val & PCI_BASE_ADDRESS_MEM_MASK); +#ifdef CONFIG_PHYS_ADDR_T_64BIT + if ((val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) { + u32 val2; + + pci_read_config_dword(dev, bar_offset + 4, &val2); + addr |= ((resource_size_t)val2) << 32; + } +#endif + + return __parse_discovery_table(addr, die, parsed, ignore); +} + +static bool intel_uncore_has_discovery_tables_pci(int *ignore) { u32 device, val, entry_id, bar_offset; int die, dvsec = 0, ret = true; @@ -391,6 +399,45 @@ bool intel_uncore_has_discovery_tables(int *ignore) return ret; } +static bool intel_uncore_has_discovery_tables_msr(int *ignore) +{ + unsigned long *die_mask; + bool parsed = false; + int cpu, die; + u64 base; + + die_mask = kcalloc(BITS_TO_LONGS(uncore_max_dies()), + sizeof(unsigned long), GFP_KERNEL); + if (!die_mask) + return false; + + cpus_read_lock(); + for_each_online_cpu(cpu) { + die = topology_logical_die_id(cpu); + if (__test_and_set_bit(die, die_mask)) + continue; + + if (rdmsrq_safe_on_cpu(cpu, UNCORE_DISCOVERY_MSR, &base)) + continue; + + if (!base) + continue; + + __parse_discovery_table(base, die, &parsed, ignore); + } + + cpus_read_unlock(); + + kfree(die_mask); + return parsed; +} + +bool intel_uncore_has_discovery_tables(int *ignore) +{ + return intel_uncore_has_discovery_tables_msr(ignore) || + intel_uncore_has_discovery_tables_pci(ignore); +} + void intel_uncore_clear_discovery_tables(void) { struct intel_uncore_discovery_type *type, *next; @@ -604,7 +651,7 @@ void intel_generic_uncore_mmio_init_box(struct intel_uncore_box *box) } addr = unit->addr; - box->io_addr = ioremap(addr, UNCORE_GENERIC_MMIO_SIZE); + box->io_addr = ioremap(addr, type->mmio_map_size); if (!box->io_addr) { pr_warn("Uncore type %d box %d: ioremap error for 0x%llx.\n", type->type_id, unit->id, (unsigned long long)addr); diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h index 0e94aa7db8e7..dff75c98e22f 100644 --- a/arch/x86/events/intel/uncore_discovery.h +++ b/arch/x86/events/intel/uncore_discovery.h @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Store the full address of the global discovery table */ +#define UNCORE_DISCOVERY_MSR 0x201e + /* Generic device ID of a discovery table device */ #define UNCORE_DISCOVERY_TABLE_DEVICE 0x09a7 /* Capability ID for a discovery table device */ @@ -168,3 +171,7 @@ bool intel_generic_uncore_assign_hw_event(struct perf_event *event, struct intel_uncore_box *box); void uncore_find_add_unit(struct intel_uncore_discovery_unit *node, struct rb_root *root, u16 *num_units); +struct intel_uncore_type ** +uncore_get_uncores(enum uncore_access_type type_id, int num_extra, + struct intel_uncore_type **extra, int max_num_types, + struct intel_uncore_type **uncores); diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c index a1a96833e30e..807e582b8f17 100644 --- a/arch/x86/events/intel/uncore_snb.c +++ b/arch/x86/events/intel/uncore_snb.c @@ -1855,3 +1855,82 @@ void lnl_uncore_mmio_init(void) } /* end of Lunar Lake MMIO uncore support */ + +/* Panther Lake uncore support */ + +#define UNCORE_PTL_MAX_NUM_UNCORE_TYPES 42 +#define UNCORE_PTL_TYPE_IMC 6 +#define UNCORE_PTL_TYPE_SNCU 34 +#define UNCORE_PTL_TYPE_HBO 41 + +#define PTL_UNCORE_GLOBAL_CTL_OFFSET 0x380 + +static struct intel_uncore_type ptl_uncore_imc = { + .name = "imc", + .mmio_map_size = 0xf00, +}; + +static void ptl_uncore_sncu_init_box(struct intel_uncore_box *box) +{ + intel_generic_uncore_mmio_init_box(box); + + /* Clear the global freeze bit */ + if (box->io_addr) + writel(0, box->io_addr + PTL_UNCORE_GLOBAL_CTL_OFFSET); +} + +static struct intel_uncore_ops ptl_uncore_sncu_ops = { + .init_box = ptl_uncore_sncu_init_box, + .exit_box = uncore_mmio_exit_box, + .disable_box = intel_generic_uncore_mmio_disable_box, + .enable_box = intel_generic_uncore_mmio_enable_box, + .disable_event = intel_generic_uncore_mmio_disable_event, + .enable_event = intel_generic_uncore_mmio_enable_event, + .read_counter = uncore_mmio_read_counter, +}; + +static struct intel_uncore_type ptl_uncore_sncu = { + .name = "sncu", + .ops = &ptl_uncore_sncu_ops, + .mmio_map_size = 0xf00, +}; + +static struct intel_uncore_type ptl_uncore_hbo = { + .name = "hbo", + .mmio_map_size = 0xf00, +}; + +static struct intel_uncore_type *ptl_uncores[UNCORE_PTL_MAX_NUM_UNCORE_TYPES] = { + [UNCORE_PTL_TYPE_IMC] = &ptl_uncore_imc, + [UNCORE_PTL_TYPE_SNCU] = &ptl_uncore_sncu, + [UNCORE_PTL_TYPE_HBO] = &ptl_uncore_hbo, +}; + +#define UNCORE_PTL_MMIO_EXTRA_UNCORES 1 + +static struct intel_uncore_type *ptl_mmio_extra_uncores[UNCORE_PTL_MMIO_EXTRA_UNCORES] = { + &adl_uncore_imc_free_running, +}; + +void ptl_uncore_mmio_init(void) +{ + uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, + UNCORE_PTL_MMIO_EXTRA_UNCORES, + ptl_mmio_extra_uncores, + UNCORE_PTL_MAX_NUM_UNCORE_TYPES, + ptl_uncores); +} + +static struct intel_uncore_type *ptl_msr_uncores[] = { + &mtl_uncore_cbox, + NULL +}; + +void ptl_uncore_cpu_init(void) +{ + mtl_uncore_cbox.num_boxes = 6; + mtl_uncore_cbox.ops = &lnl_uncore_msr_ops; + uncore_msr_uncores = ptl_msr_uncores; +} + +/* end of Panther Lake uncore support */ diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index 2824dc9950be..e1f370b8d065 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -6409,9 +6409,11 @@ static void uncore_type_customized_copy(struct intel_uncore_type *to_type, to_type->get_topology = from_type->get_topology; if (from_type->cleanup_mapping) to_type->cleanup_mapping = from_type->cleanup_mapping; + if (from_type->mmio_map_size) + to_type->mmio_map_size = from_type->mmio_map_size; } -static struct intel_uncore_type ** +struct intel_uncore_type ** uncore_get_uncores(enum uncore_access_type type_id, int num_extra, struct intel_uncore_type **extra, int max_num_types, struct intel_uncore_type **uncores) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 3d1d3547095a..afdbda2dd7b7 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -34,6 +34,7 @@ #include #include #include +#include void *hv_hypercall_pg; EXPORT_SYMBOL_GPL(hv_hypercall_pg); diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index 31f0d29cbc5e..090f5ac9f492 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -10,6 +10,7 @@ #include #include +#include #include static int hv_map_interrupt(union hv_device_id device_id, bool level, @@ -46,7 +47,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, if (nr_bank < 0) { local_irq_restore(flags); pr_err("%s: unable to generate VP set\n", __func__); - return EINVAL; + return -EINVAL; } intr_desc->target.flags = HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; @@ -66,7 +67,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, if (!hv_result_success(status)) hv_status_err(status, "\n"); - return hv_result(status); + return hv_result_to_errno(status); } static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) @@ -88,7 +89,10 @@ static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL); local_irq_restore(flags); - return hv_result(status); + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); } #ifdef CONFIG_PCI_MSI @@ -169,13 +173,34 @@ static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev) return dev_id; } -static int hv_map_msi_interrupt(struct pci_dev *dev, int cpu, int vector, - struct hv_interrupt_entry *entry) +/** + * hv_map_msi_interrupt() - "Map" the MSI IRQ in the hypervisor. + * @data: Describes the IRQ + * @out_entry: Hypervisor (MSI) interrupt entry (can be NULL) + * + * Map the IRQ in the hypervisor by issuing a MAP_DEVICE_INTERRUPT hypercall. + * + * Return: 0 on success, -errno on failure + */ +int hv_map_msi_interrupt(struct irq_data *data, + struct hv_interrupt_entry *out_entry) { - union hv_device_id device_id = hv_build_pci_dev_id(dev); + struct irq_cfg *cfg = irqd_cfg(data); + struct hv_interrupt_entry dummy; + union hv_device_id device_id; + struct msi_desc *msidesc; + struct pci_dev *dev; + int cpu; - return hv_map_interrupt(device_id, false, cpu, vector, entry); + msidesc = irq_data_get_msi_desc(data); + dev = msi_desc_to_pci_dev(msidesc); + device_id = hv_build_pci_dev_id(dev); + cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); + + return hv_map_interrupt(device_id, false, cpu, cfg->vector, + out_entry ? out_entry : &dummy); } +EXPORT_SYMBOL_GPL(hv_map_msi_interrupt); static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg) { @@ -188,13 +213,11 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry); static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { + struct hv_interrupt_entry *stored_entry; + struct irq_cfg *cfg = irqd_cfg(data); struct msi_desc *msidesc; struct pci_dev *dev; - struct hv_interrupt_entry out_entry, *stored_entry; - struct irq_cfg *cfg = irqd_cfg(data); - const cpumask_t *affinity; - int cpu; - u64 status; + int ret; msidesc = irq_data_get_msi_desc(data); dev = msi_desc_to_pci_dev(msidesc); @@ -204,9 +227,6 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - affinity = irq_data_get_effective_affinity_mask(data); - cpu = cpumask_first_and(affinity, cpu_online_mask); - if (data->chip_data) { /* * This interrupt is already mapped. Let's unmap first. @@ -219,14 +239,12 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) stored_entry = data->chip_data; data->chip_data = NULL; - status = hv_unmap_msi_interrupt(dev, stored_entry); + ret = hv_unmap_msi_interrupt(dev, stored_entry); kfree(stored_entry); - if (status != HV_STATUS_SUCCESS) { - hv_status_debug(status, "failed to unmap\n"); + if (ret) return; - } } stored_entry = kzalloc(sizeof(*stored_entry), GFP_ATOMIC); @@ -235,15 +253,14 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - status = hv_map_msi_interrupt(dev, cpu, cfg->vector, &out_entry); - if (status != HV_STATUS_SUCCESS) { + ret = hv_map_msi_interrupt(data, stored_entry); + if (ret) { kfree(stored_entry); return; } - *stored_entry = out_entry; data->chip_data = stored_entry; - entry_to_msi_msg(&out_entry, msg); + entry_to_msi_msg(data->chip_data, msg); return; } @@ -257,7 +274,6 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) { struct hv_interrupt_entry old_entry; struct msi_msg msg; - u64 status; if (!irqd->chip_data) { pr_debug("%s: no chip data\n!", __func__); @@ -270,10 +286,7 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) kfree(irqd->chip_data); irqd->chip_data = NULL; - status = hv_unmap_msi_interrupt(dev, &old_entry); - - if (status != HV_STATUS_SUCCESS) - hv_status_err(status, "\n"); + (void)hv_unmap_msi_interrupt(dev, &old_entry); } static void hv_msi_free_irq(struct irq_domain *domain, diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c index e93a2f488ff7..ade6c665c97e 100644 --- a/arch/x86/hyperv/ivm.c +++ b/arch/x86/hyperv/ivm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/hyperv/nested.c b/arch/x86/hyperv/nested.c index 1083dc8646f9..8ccbb7c4fc27 100644 --- a/arch/x86/hyperv/nested.c +++ b/arch/x86/hyperv/nested.c @@ -11,6 +11,7 @@ #include +#include #include #include #include diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 5ab1a4598d00..a03aa6f999d1 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -158,13 +158,13 @@ static inline bool acpi_has_cpu_in_madt(void) } #define ACPI_HAVE_ARCH_SET_ROOT_POINTER -static inline void acpi_arch_set_root_pointer(u64 addr) +static __always_inline void acpi_arch_set_root_pointer(u64 addr) { x86_init.acpi.set_root_pointer(addr); } #define ACPI_HAVE_ARCH_GET_ROOT_POINTER -static inline u64 acpi_arch_get_root_pointer(void) +static __always_inline u64 acpi_arch_get_root_pointer(void) { return x86_init.acpi.get_root_pointer(); } diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index 23d86c9750b9..07ba4935e873 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -488,11 +488,14 @@ static inline void apic_setup_apic_calls(void) { } extern void apic_ack_irq(struct irq_data *data); +#define APIC_VECTOR_TO_BIT_NUMBER(v) ((unsigned int)(v) % 32) +#define APIC_VECTOR_TO_REG_OFFSET(v) ((unsigned int)(v) / 32 * 0x10) + static inline bool lapic_vector_set_in_irr(unsigned int vector) { - u32 irr = apic_read(APIC_IRR + (vector / 32 * 0x10)); + u32 irr = apic_read(APIC_IRR + APIC_VECTOR_TO_REG_OFFSET(vector)); - return !!(irr & (1U << (vector % 32))); + return !!(irr & (1U << APIC_VECTOR_TO_BIT_NUMBER(vector))); } static inline bool is_vector_pending(unsigned int vector) @@ -500,6 +503,65 @@ static inline bool is_vector_pending(unsigned int vector) return lapic_vector_set_in_irr(vector) || pi_pending_this_cpu(vector); } +#define MAX_APIC_VECTOR 256 +#define APIC_VECTORS_PER_REG 32 + +/* + * Vector states are maintained by APIC in 32-bit registers that are + * 16 bytes aligned. The status of each vector is kept in a single + * bit. + */ +static inline int apic_find_highest_vector(void *bitmap) +{ + int vec; + u32 *reg; + + for (vec = MAX_APIC_VECTOR - APIC_VECTORS_PER_REG; vec >= 0; vec -= APIC_VECTORS_PER_REG) { + reg = bitmap + APIC_VECTOR_TO_REG_OFFSET(vec); + if (*reg) + return __fls(*reg) + vec; + } + + return -1; +} + +static inline u32 apic_get_reg(void *regs, int reg) +{ + return *((u32 *) (regs + reg)); +} + +static inline void apic_set_reg(void *regs, int reg, u32 val) +{ + *((u32 *) (regs + reg)) = val; +} + +static __always_inline u64 apic_get_reg64(void *regs, int reg) +{ + BUILD_BUG_ON(reg != APIC_ICR); + return *((u64 *) (regs + reg)); +} + +static __always_inline void apic_set_reg64(void *regs, int reg, u64 val) +{ + BUILD_BUG_ON(reg != APIC_ICR); + *((u64 *) (regs + reg)) = val; +} + +static inline void apic_clear_vector(int vec, void *bitmap) +{ + clear_bit(APIC_VECTOR_TO_BIT_NUMBER(vec), bitmap + APIC_VECTOR_TO_REG_OFFSET(vec)); +} + +static inline void apic_set_vector(int vec, void *bitmap) +{ + set_bit(APIC_VECTOR_TO_BIT_NUMBER(vec), bitmap + APIC_VECTOR_TO_REG_OFFSET(vec)); +} + +static inline int apic_test_vector(int vec, void *bitmap) +{ + return test_bit(APIC_VECTOR_TO_BIT_NUMBER(vec), bitmap + APIC_VECTOR_TO_REG_OFFSET(vec)); +} + /* * Warm reset vector position: */ diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h index f0e9acf72547..20fcb8507ad1 100644 --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h @@ -32,45 +32,42 @@ #ifdef CONFIG_GENERIC_BUG #ifdef CONFIG_X86_32 -# define __BUG_REL(val) ".long " __stringify(val) +# define __BUG_REL(val) ".long " val #else -# define __BUG_REL(val) ".long " __stringify(val) " - ." +# define __BUG_REL(val) ".long " val " - ." #endif #ifdef CONFIG_DEBUG_BUGVERBOSE +#define __BUG_ENTRY(file, line, flags) \ + "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \ + "\t" __BUG_REL(file) "\t# bug_entry::file\n" \ + "\t.word " line "\t# bug_entry::line\n" \ + "\t.word " flags "\t# bug_entry::flags\n" +#else +#define __BUG_ENTRY(file, line, flags) \ + "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \ + "\t.word " flags "\t# bug_entry::flags\n" +#endif + +#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \ + "1:\t" ins "\n" \ + ".pushsection __bug_table,\"aw\"\n" \ + __BUG_ENTRY(file, line, flags) \ + "\t.org 2b + " size "\n" \ + ".popsection\n" \ + extra #define _BUG_FLAGS(ins, flags, extra) \ do { \ - asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"aw\"\n" \ - "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ - "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \ - "\t.word %c1" "\t# bug_entry::line\n" \ - "\t.word %c2" "\t# bug_entry::flags\n" \ - "\t.org 2b+%c3\n" \ - ".popsection\n" \ - extra \ + asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \ + "%c1", "%c2", "%c3", extra) \ : : "i" (__FILE__), "i" (__LINE__), \ "i" (flags), \ "i" (sizeof(struct bug_entry))); \ } while (0) -#else /* !CONFIG_DEBUG_BUGVERBOSE */ - -#define _BUG_FLAGS(ins, flags, extra) \ -do { \ - asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"aw\"\n" \ - "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ - "\t.word %c0" "\t# bug_entry::flags\n" \ - "\t.org 2b+%c1\n" \ - ".popsection\n" \ - extra \ - : : "i" (flags), \ - "i" (sizeof(struct bug_entry))); \ -} while (0) - -#endif /* CONFIG_DEBUG_BUGVERBOSE */ +#define ARCH_WARN_ASM(file, line, flags, size) \ + _BUG_FLAGS_ASM(ASM_UD2, file, line, flags, size, "") #else @@ -92,11 +89,14 @@ do { \ * were to trigger, we'd rather wreck the machine in an attempt to get the * message out than not know about it. */ + +#define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b) + #define __WARN_FLAGS(flags) \ do { \ __auto_type __flags = BUGFLAG_WARNING|(flags); \ instrumentation_begin(); \ - _BUG_FLAGS(ASM_UD2, __flags, ANNOTATE_REACHABLE(1b)); \ + _BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \ instrumentation_end(); \ } while (0) diff --git a/arch/x86/include/asm/ce4100.h b/arch/x86/include/asm/ce4100.h index 2930f560d7f3..e1f965bb1e31 100644 --- a/arch/x86/include/asm/ce4100.h +++ b/arch/x86/include/asm/ce4100.h @@ -4,4 +4,10 @@ int ce4100_pci_init(void); +#ifdef CONFIG_SERIAL_8250 +void __init sdv_serial_fixup(void); +#else +static inline void sdv_serial_fixup(void) {}; +#endif + #endif diff --git a/arch/x86/include/asm/cfi.h b/arch/x86/include/asm/cfi.h index 3e51ba459154..1751f1eb95ef 100644 --- a/arch/x86/include/asm/cfi.h +++ b/arch/x86/include/asm/cfi.h @@ -116,8 +116,6 @@ struct pt_regs; #ifdef CONFIG_CFI_CLANG enum bug_trap_type handle_cfi_failure(struct pt_regs *regs); #define __bpfcall -extern u32 cfi_bpf_hash; -extern u32 cfi_bpf_subprog_hash; static inline int cfi_get_offset(void) { @@ -135,6 +133,8 @@ static inline int cfi_get_offset(void) #define cfi_get_offset cfi_get_offset extern u32 cfi_get_func_hash(void *func); +#define cfi_get_func_hash cfi_get_func_hash + extern int cfi_get_func_arity(void *func); #ifdef CONFIG_FINEIBT @@ -153,12 +153,6 @@ static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) { return BUG_TRAP_TYPE_NONE; } -#define cfi_bpf_hash 0U -#define cfi_bpf_subprog_hash 0U -static inline u32 cfi_get_func_hash(void *func) -{ - return 0; -} static inline int cfi_get_func_arity(void *func) { return 0; diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index ee176236c2be..751ca35386b0 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -218,6 +218,7 @@ #define X86_FEATURE_FLEXPRIORITY ( 8*32+ 1) /* "flexpriority" Intel FlexPriority */ #define X86_FEATURE_EPT ( 8*32+ 2) /* "ept" Intel Extended Page Table */ #define X86_FEATURE_VPID ( 8*32+ 3) /* "vpid" Intel Virtual Processor ID */ +#define X86_FEATURE_COHERENCY_SFW_NO ( 8*32+ 4) /* SNP cache coherency software work around not needed */ #define X86_FEATURE_VMMCALL ( 8*32+15) /* "vmmcall" Prefer VMMCALL to VMCALL */ #define X86_FEATURE_XENPV ( 8*32+16) /* Xen paravirtual guest */ @@ -456,10 +457,14 @@ #define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */ #define X86_FEATURE_WRMSR_XX_BASE_NS (20*32+ 1) /* WRMSR to {FS,GS,KERNEL_GS}_BASE is non-serializing */ #define X86_FEATURE_LFENCE_RDTSC (20*32+ 2) /* LFENCE always serializing / synchronizes RDTSC */ +#define X86_FEATURE_VERW_CLEAR (20*32+ 5) /* The memory form of VERW mitigates TSA */ #define X86_FEATURE_NULL_SEL_CLR_BASE (20*32+ 6) /* Null Selector Clears Base */ + #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* Automatic IBRS */ #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* SMM_CTL MSR is not present */ +#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_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 */ @@ -487,6 +492,10 @@ #define X86_FEATURE_PREFER_YMM (21*32+ 8) /* Avoid ZMM registers due to downclocking */ #define X86_FEATURE_APX (21*32+ 9) /* Advanced Performance Extensions */ #define X86_FEATURE_INDIRECT_THUNK_ITS (21*32+10) /* Use thunk for indirect branches in lower half of cacheline */ +#define X86_FEATURE_TSA_SQ_NO (21*32+11) /* AMD CPU not vulnerable to TSA-SQ */ +#define X86_FEATURE_TSA_L1_NO (21*32+12) /* AMD CPU not vulnerable to TSA-L1 */ +#define X86_FEATURE_CLEAR_CPU_BUF_VM (21*32+13) /* Clear CPU buffers using VERW before VMRUN */ +#define X86_FEATURE_IBPB_EXIT_TO_USER (21*32+14) /* Use IBPB on exit-to-userspace, see VMSCAPE bug */ /* * BUG word(s) @@ -542,5 +551,6 @@ #define X86_BUG_OLD_MICROCODE X86_BUG( 1*32+ 6) /* "old_microcode" CPU has old microcode, it is surely vulnerable to something */ #define X86_BUG_ITS X86_BUG( 1*32+ 7) /* "its" CPU is affected by Indirect Target Selection */ #define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */ - +#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */ +#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/arch/x86/include/asm/cpuid.h b/arch/x86/include/asm/cpuid.h deleted file mode 100644 index d5749b25fa10..000000000000 --- a/arch/x86/include/asm/cpuid.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef _ASM_X86_CPUID_H -#define _ASM_X86_CPUID_H - -#include - -#endif /* _ASM_X86_CPUID_H */ diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 363110e6b2e3..a2c1f2d24b64 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h @@ -9,6 +9,14 @@ #include #include +/* + * Define bits that are always set to 1 in DR7, only bit 10 is + * architecturally reserved to '1'. + * + * This is also the init/reset value for DR7. + */ +#define DR7_FIXED_1 0x00000400 + DECLARE_PER_CPU(unsigned long, cpu_dr7); #ifndef CONFIG_PARAVIRT_XXL @@ -100,8 +108,8 @@ static __always_inline void native_set_debugreg(int regno, unsigned long value) static inline void hw_breakpoint_disable(void) { - /* Zero the control register for HW Breakpoint */ - set_debugreg(0UL, 7); + /* Reset the control register for HW Breakpoint */ + set_debugreg(DR7_FIXED_1, 7); /* Zero-out the individual HW breakpoint address registers */ set_debugreg(0UL, 0); @@ -125,9 +133,12 @@ static __always_inline unsigned long local_db_save(void) return 0; get_debugreg(dr7, 7); - dr7 &= ~0x400; /* architecturally set bit */ + + /* Architecturally set bit */ + dr7 &= ~DR7_FIXED_1; if (dr7) - set_debugreg(0, 7); + set_debugreg(DR7_FIXED_1, 7); + /* * Ensure the compiler doesn't lower the above statements into * the critical section; disabling breakpoints late would not diff --git a/arch/x86/include/asm/entry-common.h b/arch/x86/include/asm/entry-common.h index d535a97c7284..ce3eb6d5fdf9 100644 --- a/arch/x86/include/asm/entry-common.h +++ b/arch/x86/include/asm/entry-common.h @@ -93,6 +93,13 @@ static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, * 8 (ia32) bits. */ choose_random_kstack_offset(rdtsc()); + + /* Avoid unnecessary reads of 'x86_ibpb_exit_to_user' */ + if (cpu_feature_enabled(X86_FEATURE_IBPB_EXIT_TO_USER) && + this_cpu_read(x86_ibpb_exit_to_user)) { + indirect_branch_prediction_barrier(); + this_cpu_write(x86_ibpb_exit_to_user, false); + } } #define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare diff --git a/arch/x86/include/asm/fpu/types.h b/arch/x86/include/asm/fpu/types.h index 1c94121acd3d..93e99d2583d6 100644 --- a/arch/x86/include/asm/fpu/types.h +++ b/arch/x86/include/asm/fpu/types.h @@ -118,7 +118,7 @@ enum xfeature { XFEATURE_PKRU, XFEATURE_PASID, XFEATURE_CET_USER, - XFEATURE_CET_KERNEL_UNUSED, + XFEATURE_CET_KERNEL, XFEATURE_RSRVD_COMP_13, XFEATURE_RSRVD_COMP_14, XFEATURE_LBR, @@ -142,7 +142,7 @@ enum xfeature { #define XFEATURE_MASK_PKRU (1 << XFEATURE_PKRU) #define XFEATURE_MASK_PASID (1 << XFEATURE_PASID) #define XFEATURE_MASK_CET_USER (1 << XFEATURE_CET_USER) -#define XFEATURE_MASK_CET_KERNEL (1 << XFEATURE_CET_KERNEL_UNUSED) +#define XFEATURE_MASK_CET_KERNEL (1 << XFEATURE_CET_KERNEL) #define XFEATURE_MASK_LBR (1 << XFEATURE_LBR) #define XFEATURE_MASK_XTILE_CFG (1 << XFEATURE_XTILE_CFG) #define XFEATURE_MASK_XTILE_DATA (1 << XFEATURE_XTILE_DATA) @@ -268,6 +268,16 @@ struct cet_user_state { u64 user_ssp; }; +/* + * State component 12 is Control-flow Enforcement supervisor states. + * This state includes SSP pointers for privilege levels 0 through 2. + */ +struct cet_supervisor_state { + u64 pl0_ssp; + u64 pl1_ssp; + u64 pl2_ssp; +} __packed; + /* * State component 15: Architectural LBR configuration state. * The size of Arch LBR state depends on the number of LBRs (lbr_depth). @@ -551,6 +561,31 @@ struct fpu_guest { struct fpstate *fpstate; }; +/* + * FPU state configuration data for fpu_guest. + * Initialized at boot time. Read only after init. + */ +struct vcpu_fpu_config { + /* + * @size: + * + * The default size of the register state buffer in guest FPUs. + * Includes all supported features except independent managed + * features and features which have to be requested by user space + * before usage. + */ + unsigned int size; + + /* + * @features: + * + * The default supported features bitmap in guest FPUs. Does not + * include independent managed features and features which have to + * be requested by user space before usage. + */ + u64 features; +}; + /* * FPU state configuration data. Initialized at boot time. Read only after init. */ @@ -567,8 +602,9 @@ struct fpu_state_config { * @default_size: * * The default size of the register state buffer. Includes all - * supported features except independent managed features and - * features which have to be requested by user space before usage. + * supported features except independent managed features, + * guest-only features and features which have to be requested by + * user space before usage. */ unsigned int default_size; @@ -584,8 +620,8 @@ struct fpu_state_config { * @default_features: * * The default supported features bitmap. Does not include - * independent managed features and features which have to - * be requested by user space before usage. + * independent managed features, guest-only features and features + * which have to be requested by user space before usage. */ u64 default_features; /* @@ -606,5 +642,6 @@ struct fpu_state_config { /* FPU state configuration information */ extern struct fpu_state_config fpu_kernel_cfg, fpu_user_cfg; +extern struct vcpu_fpu_config guest_default_cfg; #endif /* _ASM_X86_FPU_TYPES_H */ diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h index b308a76afbb7..7a7dc9d56027 100644 --- a/arch/x86/include/asm/fpu/xstate.h +++ b/arch/x86/include/asm/fpu/xstate.h @@ -46,9 +46,13 @@ /* Features which are dynamically enabled for a process on request */ #define XFEATURE_MASK_USER_DYNAMIC XFEATURE_MASK_XTILE_DATA +/* Supervisor features which are enabled only in guest FPUs */ +#define XFEATURE_MASK_GUEST_SUPERVISOR XFEATURE_MASK_CET_KERNEL + /* All currently supported supervisor features */ #define XFEATURE_MASK_SUPERVISOR_SUPPORTED (XFEATURE_MASK_PASID | \ - XFEATURE_MASK_CET_USER) + XFEATURE_MASK_CET_USER | \ + XFEATURE_MASK_GUEST_SUPERVISOR) /* * A supervisor state component may not always contain valuable information, @@ -75,8 +79,7 @@ * Unsupported supervisor features. When a supervisor feature in this mask is * supported in the future, move it to the supported supervisor feature mask. */ -#define XFEATURE_MASK_SUPERVISOR_UNSUPPORTED (XFEATURE_MASK_PT | \ - XFEATURE_MASK_CET_KERNEL) +#define XFEATURE_MASK_SUPERVISOR_UNSUPPORTED (XFEATURE_MASK_PT) /* All supervisor states including supported and unsupported states. */ #define XFEATURE_MASK_SUPERVISOR_ALL (XFEATURE_MASK_SUPERVISOR_SUPPORTED | \ diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index 162ebd73a698..cbe19e669080 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h @@ -92,8 +92,6 @@ struct irq_cfg { extern struct irq_cfg *irq_cfg(unsigned int irq); extern struct irq_cfg *irqd_cfg(struct irq_data *irq_data); -extern void lock_vector_lock(void); -extern void unlock_vector_lock(void); #ifdef CONFIG_SMP extern void vector_schedule_cleanup(struct irq_cfg *); extern void irq_complete_move(struct irq_cfg *cfg); @@ -101,12 +99,16 @@ extern void irq_complete_move(struct irq_cfg *cfg); static inline void vector_schedule_cleanup(struct irq_cfg *c) { } static inline void irq_complete_move(struct irq_cfg *c) { } #endif - extern void apic_ack_edge(struct irq_data *data); -#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ + +#ifdef CONFIG_X86_LOCAL_APIC +extern void lock_vector_lock(void); +extern void unlock_vector_lock(void); +#else static inline void lock_vector_lock(void) {} static inline void unlock_vector_lock(void) {} -#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ +#endif /* Statistics */ extern atomic_t irq_err_count; diff --git a/arch/x86/include/asm/init.h b/arch/x86/include/asm/init.h index 8b1b1abcef15..5a68e9db6518 100644 --- a/arch/x86/include/asm/init.h +++ b/arch/x86/include/asm/init.h @@ -5,7 +5,7 @@ #if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION < 170000 #define __head __section(".head.text") __no_sanitize_undefined __no_stack_protector #else -#define __head __section(".head.text") __no_sanitize_undefined +#define __head __section(".head.text") __no_sanitize_undefined __no_kstack_erase #endif struct x86_mapping_info { diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h index be10c188614f..e345dbdf933e 100644 --- a/arch/x86/include/asm/intel-family.h +++ b/arch/x86/include/asm/intel-family.h @@ -150,6 +150,11 @@ #define INTEL_PANTHERLAKE_L IFM(6, 0xCC) /* Cougar Cove / Crestmont */ +#define INTEL_WILDCATLAKE_L IFM(6, 0xD5) + +#define INTEL_NOVALAKE IFM(18, 0x01) +#define INTEL_NOVALAKE_L IFM(18, 0x03) + /* "Small Core" Processors (Atom/E-Core) */ #define INTEL_ATOM_BONNELL IFM(6, 0x1C) /* Diamondville, Pineview */ diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h index 43b7657febca..944637a4e6de 100644 --- a/arch/x86/include/asm/intel_telemetry.h +++ b/arch/x86/include/asm/intel_telemetry.h @@ -59,18 +59,6 @@ struct telemetry_plt_config { }; struct telemetry_core_ops { - int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period, - u8 *ioss_min_period, u8 *ioss_max_period); - - int (*get_eventconfig)(struct telemetry_evtconfig *pss_evtconfig, - struct telemetry_evtconfig *ioss_evtconfig, - int pss_len, int ioss_len); - - int (*update_events)(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig); - - int (*set_sampling_period)(u8 pss_period, u8 ioss_period); - int (*get_trace_verbosity)(enum telemetry_unit telem_unit, u32 *verbosity); @@ -84,11 +72,6 @@ struct telemetry_core_ops { int (*read_eventlog)(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len, int log_all_evts); - - int (*add_events)(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap); - - int (*reset_events)(void); }; int telemetry_set_pltdata(const struct telemetry_core_ops *ops, @@ -101,35 +84,15 @@ struct telemetry_plt_config *telemetry_get_pltdata(void); int telemetry_get_evtname(enum telemetry_unit telem_unit, const char **name, int len); -int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig); - -int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap); - -int telemetry_reset_events(void); - -int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_config, - struct telemetry_evtconfig *ioss_config, - int pss_len, int ioss_len); - int telemetry_read_events(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); -int telemetry_raw_read_events(enum telemetry_unit telem_unit, - struct telemetry_evtlog *evtlog, int len); - int telemetry_read_eventlog(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); -int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, - u8 *ioss_min_period, u8 *ioss_max_period); - -int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period); - int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit, u32 verbosity); diff --git a/arch/x86/include/asm/irq_remapping.h b/arch/x86/include/asm/irq_remapping.h index 5036f13ab69f..5a0d42464d44 100644 --- a/arch/x86/include/asm/irq_remapping.h +++ b/arch/x86/include/asm/irq_remapping.h @@ -26,7 +26,22 @@ enum { IRQ_REMAP_X2APIC_MODE, }; -struct vcpu_data { +/* + * This is mainly used to communicate information back-and-forth + * between SVM and IOMMU for setting up and tearing down posted + * interrupt + */ +struct amd_iommu_pi_data { + u64 vapic_addr; /* Physical address of the vCPU's vAPIC. */ + u32 ga_tag; + u32 vector; /* Guest vector of the interrupt */ + int cpu; + bool ga_log_intr; + bool is_guest_mode; + void *ir_data; +}; + +struct intel_iommu_pi_data { u64 pi_desc_addr; /* Physical address of PI Descriptor */ u32 vector; /* Guest vector of the interrupt */ }; diff --git a/arch/x86/include/asm/irqflags.h b/arch/x86/include/asm/irqflags.h index 9a9b21b78905..b30e5474c18e 100644 --- a/arch/x86/include/asm/irqflags.h +++ b/arch/x86/include/asm/irqflags.h @@ -44,13 +44,13 @@ static __always_inline void native_irq_enable(void) static __always_inline void native_safe_halt(void) { - mds_idle_clear_cpu_buffers(); + x86_idle_clear_cpu_buffers(); asm volatile("sti; hlt": : :"memory"); } static __always_inline void native_halt(void) { - mds_idle_clear_cpu_buffers(); + x86_idle_clear_cpu_buffers(); asm volatile("hlt": : :"memory"); } diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 8d50e3e0a19b..18a5c3119e1a 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -49,7 +49,6 @@ KVM_X86_OP(set_idt) KVM_X86_OP(get_gdt) KVM_X86_OP(set_gdt) KVM_X86_OP(sync_dirty_debug_regs) -KVM_X86_OP(set_dr6) KVM_X86_OP(set_dr7) KVM_X86_OP(cache_reg) KVM_X86_OP(get_rflags) @@ -112,7 +111,7 @@ KVM_X86_OP_OPTIONAL(update_cpu_dirty_logging) KVM_X86_OP_OPTIONAL(vcpu_blocking) KVM_X86_OP_OPTIONAL(vcpu_unblocking) KVM_X86_OP_OPTIONAL(pi_update_irte) -KVM_X86_OP_OPTIONAL(pi_start_assignment) +KVM_X86_OP_OPTIONAL(pi_start_bypass) KVM_X86_OP_OPTIONAL(apicv_pre_state_restore) KVM_X86_OP_OPTIONAL(apicv_post_state_restore) KVM_X86_OP_OPTIONAL_RET0(dy_apicv_has_pending_interrupt) @@ -139,7 +138,7 @@ KVM_X86_OP(check_emulate_instruction) KVM_X86_OP(apic_init_signal_blocked) KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush) KVM_X86_OP_OPTIONAL(migrate_timers) -KVM_X86_OP(msr_filter_changed) +KVM_X86_OP(recalc_msr_intercepts) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons); diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b4a391929cdb..f19a76d3ca0e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -249,7 +250,6 @@ enum x86_intercept_stage; #define DR7_BP_EN_MASK 0x000000ff #define DR7_GE (1 << 9) #define DR7_GD (1 << 13) -#define DR7_FIXED_1 0x00000400 #define DR7_VOLATILE 0xffff2bff #define KVM_GUESTDBG_VALID_MASK \ @@ -297,6 +297,7 @@ enum x86_intercept_stage; */ #define KVM_APIC_PV_EOI_PENDING 1 +struct kvm_kernel_irqfd; struct kvm_kernel_irq_routing_entry; /* @@ -700,8 +701,13 @@ struct kvm_vcpu_hv { struct kvm_vcpu_hv_tlb_flush_fifo tlb_flush_fifo[HV_NR_TLB_FLUSH_FIFOS]; - /* Preallocated buffer for handling hypercalls passing sparse vCPU set */ + /* + * Preallocated buffers for handling hypercalls that pass sparse vCPU + * sets (for high vCPU counts, they're too large to comfortably fit on + * the stack). + */ u64 sparse_banks[HV_MAX_SPARSE_VCPU_BANKS]; + DECLARE_BITMAP(vcpu_mask, KVM_MAX_VCPUS); struct hv_vp_assist_page vp_assist_page; @@ -764,6 +770,7 @@ enum kvm_only_cpuid_leafs { CPUID_8000_0022_EAX, CPUID_7_2_EDX, CPUID_24_0_EBX, + CPUID_8000_0021_ECX, NR_KVM_CPU_CAPS, NKVMCAPINTS = NR_KVM_CPU_CAPS - NCAPINTS, @@ -1314,6 +1321,12 @@ enum kvm_apicv_inhibit { */ APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED, + /* + * AVIC is disabled because the vCPU's APIC ID is beyond the max + * supported by AVIC/x2AVIC, i.e. the vCPU is unaddressable. + */ + APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG, + NR_APICV_INHIBIT_REASONS, }; @@ -1332,7 +1345,8 @@ enum kvm_apicv_inhibit { __APICV_INHIBIT_REASON(IRQWIN), \ __APICV_INHIBIT_REASON(PIT_REINJ), \ __APICV_INHIBIT_REASON(SEV), \ - __APICV_INHIBIT_REASON(LOGICAL_ID_ALIASED) + __APICV_INHIBIT_REASON(LOGICAL_ID_ALIASED), \ + __APICV_INHIBIT_REASON(PHYSICAL_ID_TOO_BIG) struct kvm_arch { unsigned long n_used_mmu_pages; @@ -1344,7 +1358,7 @@ struct kvm_arch { bool has_private_mem; bool has_protected_state; bool pre_fault_allowed; - struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES]; + struct hlist_head *mmu_page_hash; struct list_head active_mmu_pages; /* * A list of kvm_mmu_page structs that, if zapped, could possibly be @@ -1373,11 +1387,13 @@ struct kvm_arch { #define __KVM_HAVE_ARCH_NONCOHERENT_DMA atomic_t noncoherent_dma_count; -#define __KVM_HAVE_ARCH_ASSIGNED_DEVICE - atomic_t assigned_device_count; + unsigned long nr_possible_bypass_irqs; + +#ifdef CONFIG_KVM_IOAPIC struct kvm_pic *vpic; struct kvm_ioapic *vioapic; struct kvm_pit *vpit; +#endif atomic_t vapics_in_nmi_mode; struct mutex apic_map_lock; struct kvm_apic_map __rcu *apic_map; @@ -1392,12 +1408,8 @@ struct kvm_arch { gpa_t wall_clock; - bool mwait_in_guest; - bool hlt_in_guest; - bool pause_in_guest; - bool cstate_in_guest; + u64 disabled_exits; - unsigned long irq_sources_bitmap; s64 kvmclock_offset; /* @@ -1426,9 +1438,6 @@ struct kvm_arch { struct delayed_work kvmclock_update_work; struct delayed_work kvmclock_sync_work; - /* reads protected by irq_srcu, writes by irq_lock */ - struct hlist_head mask_notifier_list; - #ifdef CONFIG_KVM_HYPERV struct kvm_hv hyperv; #endif @@ -1451,6 +1460,7 @@ struct kvm_arch { bool x2apic_format; bool x2apic_broadcast_quirk_disabled; + bool has_mapped_host_mmio; bool guest_can_read_msr_platform_info; bool exception_payload_enabled; @@ -1674,6 +1684,12 @@ static inline u16 kvm_lapic_irq_dest_mode(bool dest_mode_logical) return dest_mode_logical ? APIC_DEST_LOGICAL : APIC_DEST_PHYSICAL; } +enum kvm_x86_run_flags { + KVM_RUN_FORCE_IMMEDIATE_EXIT = BIT(0), + KVM_RUN_LOAD_GUEST_DR6 = BIT(1), + KVM_RUN_LOAD_DEBUGCTL = BIT(2), +}; + struct kvm_x86_ops { const char *name; @@ -1702,6 +1718,12 @@ struct kvm_x86_ops { void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu); void (*vcpu_put)(struct kvm_vcpu *vcpu); + /* + * Mask of DEBUGCTL bits that are owned by the host, i.e. that need to + * match the host's value even while the guest is active. + */ + const u64 HOST_OWNED_DEBUGCTL; + void (*update_exception_bitmap)(struct kvm_vcpu *vcpu); int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); @@ -1724,7 +1746,6 @@ struct kvm_x86_ops { void (*get_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu); - void (*set_dr6)(struct kvm_vcpu *vcpu, unsigned long value); void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value); void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg); unsigned long (*get_rflags)(struct kvm_vcpu *vcpu); @@ -1755,7 +1776,7 @@ struct kvm_x86_ops { int (*vcpu_pre_run)(struct kvm_vcpu *vcpu); enum exit_fastpath_completion (*vcpu_run)(struct kvm_vcpu *vcpu, - bool force_immediate_exit); + u64 run_flags); int (*handle_exit)(struct kvm_vcpu *vcpu, enum exit_fastpath_completion exit_fastpath); int (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); @@ -1847,9 +1868,10 @@ struct kvm_x86_ops { void (*vcpu_blocking)(struct kvm_vcpu *vcpu); void (*vcpu_unblocking)(struct kvm_vcpu *vcpu); - int (*pi_update_irte)(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set); - void (*pi_start_assignment)(struct kvm *kvm); + int (*pi_update_irte)(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm, + unsigned int host_irq, uint32_t guest_irq, + struct kvm_vcpu *vcpu, u32 vector); + void (*pi_start_bypass)(struct kvm *kvm); void (*apicv_pre_state_restore)(struct kvm_vcpu *vcpu); void (*apicv_post_state_restore)(struct kvm_vcpu *vcpu); bool (*dy_apicv_has_pending_interrupt)(struct kvm_vcpu *vcpu); @@ -1886,7 +1908,7 @@ struct kvm_x86_ops { int (*enable_l2_tlb_flush)(struct kvm_vcpu *vcpu); void (*migrate_timers)(struct kvm_vcpu *vcpu); - void (*msr_filter_changed)(struct kvm_vcpu *vcpu); + void (*recalc_msr_intercepts)(struct kvm_vcpu *vcpu); int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); @@ -1944,6 +1966,7 @@ struct kvm_arch_async_pf { extern u32 __read_mostly kvm_nr_uret_msrs; extern bool __read_mostly allow_smaller_maxphyaddr; extern bool __read_mostly enable_apicv; +extern bool __read_mostly enable_ipiv; extern bool __read_mostly enable_device_posted_irqs; extern struct kvm_x86_ops kvm_x86_ops; @@ -1962,7 +1985,7 @@ void kvm_x86_vendor_exit(void); #define __KVM_HAVE_ARCH_VM_ALLOC static inline struct kvm *kvm_arch_alloc_vm(void) { - return __vmalloc(kvm_x86_ops.vm_size, GFP_KERNEL_ACCOUNT | __GFP_ZERO); + return kvzalloc(kvm_x86_ops.vm_size, GFP_KERNEL_ACCOUNT); } #define __KVM_HAVE_ARCH_VM_FREE @@ -2007,7 +2030,7 @@ void kvm_mmu_vendor_module_exit(void); void kvm_mmu_destroy(struct kvm_vcpu *vcpu); int kvm_mmu_create(struct kvm_vcpu *vcpu); -void kvm_mmu_init_vm(struct kvm *kvm); +int kvm_mmu_init_vm(struct kvm *kvm); void kvm_mmu_uninit_vm(struct kvm *kvm); void kvm_mmu_init_memslot_memory_attributes(struct kvm *kvm, @@ -2038,19 +2061,6 @@ 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); -struct kvm_irq_mask_notifier { - void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked); - int irq; - struct hlist_node link; -}; - -void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn); -void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn); -void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, - bool mask); - extern bool tdp_enabled; u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); @@ -2209,9 +2219,6 @@ static inline int __kvm_irq_line_state(unsigned long *irq_state, return !!(*irq_state); } -int kvm_pic_set_irq(struct kvm_pic *pic, int irq, int irq_source_id, int level); -void kvm_pic_clear_all(struct kvm_pic *pic, int irq_source_id); - void kvm_inject_nmi(struct kvm_vcpu *vcpu); int kvm_get_nr_pending_nmis(struct kvm_vcpu *vcpu); @@ -2388,9 +2395,6 @@ bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu); bool kvm_intr_is_single_vcpu(struct kvm *kvm, struct kvm_lapic_irq *irq, struct kvm_vcpu **dest_vcpu); -void kvm_set_msi_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, - struct kvm_lapic_irq *irq); - static inline bool kvm_irq_is_postable(struct kvm_lapic_irq *irq) { /* We can only post Fixed and LowPrio IRQs */ diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h index e988bac0a4a1..3c2de4ce3b10 100644 --- a/arch/x86/include/asm/module.h +++ b/arch/x86/include/asm/module.h @@ -5,12 +5,20 @@ #include #include +struct its_array { +#ifdef CONFIG_MITIGATION_ITS + void **pages; + int num; +#endif +}; + struct mod_arch_specific { #ifdef CONFIG_UNWINDER_ORC unsigned int num_orcs; int *orc_unwind_ip; struct orc_entry *orc_unwind; #endif + struct its_array its_pages; }; #endif /* _ASM_X86_MODULE_H */ diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index e1752ba47e67..abc4659f5809 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -112,12 +112,6 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output) return hv_status; } -/* Hypercall to the L0 hypervisor */ -static inline u64 hv_do_nested_hypercall(u64 control, void *input, void *output) -{ - return hv_do_hypercall(control | HV_HYPERCALL_NESTED, input, output); -} - /* Fast hypercall with 8 bytes of input and no output */ static inline u64 _hv_do_fast_hypercall8(u64 control, u64 input1) { @@ -165,13 +159,6 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) return _hv_do_fast_hypercall8(control, input1); } -static inline u64 hv_do_fast_nested_hypercall8(u16 code, u64 input1) -{ - u64 control = (u64)code | HV_HYPERCALL_FAST_BIT | HV_HYPERCALL_NESTED; - - return _hv_do_fast_hypercall8(control, input1); -} - /* Fast hypercall with 16 bytes of input */ static inline u64 _hv_do_fast_hypercall16(u64 control, u64 input1, u64 input2) { @@ -223,13 +210,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2) return _hv_do_fast_hypercall16(control, input1, input2); } -static inline u64 hv_do_fast_nested_hypercall16(u16 code, u64 input1, u64 input2) -{ - u64 control = (u64)code | HV_HYPERCALL_FAST_BIT | HV_HYPERCALL_NESTED; - - return _hv_do_fast_hypercall16(control, input1, input2); -} - extern struct hv_vp_assist_page **hv_vp_assist_page; static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) @@ -262,6 +242,8 @@ static inline void hv_apic_init(void) {} struct irq_domain *hv_create_pci_msi_domain(void); +int hv_map_msi_interrupt(struct irq_data *data, + struct hv_interrupt_entry *out_entry); int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, struct hv_interrupt_entry *entry); int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index b7dded3c8113..b65c3ba5fa14 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -419,6 +419,7 @@ #define DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI (1UL << 12) #define DEBUGCTLMSR_FREEZE_IN_SMM_BIT 14 #define DEBUGCTLMSR_FREEZE_IN_SMM (1UL << DEBUGCTLMSR_FREEZE_IN_SMM_BIT) +#define DEBUGCTLMSR_RTM_DEBUG BIT(15) #define MSR_PEBS_FRONTEND 0x000003f7 @@ -628,6 +629,7 @@ #define MSR_AMD64_OSVW_STATUS 0xc0010141 #define MSR_AMD_PPIN_CTL 0xc00102f0 #define MSR_AMD_PPIN 0xc00102f1 +#define MSR_AMD64_CPUID_FN_7 0xc0011002 #define MSR_AMD64_CPUID_FN_1 0xc0011004 #define MSR_AMD64_LS_CFG 0xc0011020 #define MSR_AMD64_DC_CFG 0xc0011022 @@ -732,6 +734,11 @@ #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301 #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR 0xc0000302 +/* AMD Hardware Feedback Support MSRs */ +#define MSR_AMD_WORKLOAD_CLASS_CONFIG 0xc0000500 +#define MSR_AMD_WORKLOAD_CLASS_ID 0xc0000501 +#define MSR_AMD_WORKLOAD_HRST 0xc0000502 + /* AMD Last Branch Record MSRs */ #define MSR_AMD64_LBR_SELECT 0xc000010e @@ -830,6 +837,7 @@ #define MSR_K7_HWCR_SMMLOCK BIT_ULL(MSR_K7_HWCR_SMMLOCK_BIT) #define MSR_K7_HWCR_IRPERF_EN_BIT 30 #define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) +#define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35 #define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_STATUS 0xc0010042 #define MSR_K7_HWCR_CPB_DIS_BIT 25 diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h index dd2b129b0418..6ca6516c7492 100644 --- a/arch/x86/include/asm/mwait.h +++ b/arch/x86/include/asm/mwait.h @@ -43,8 +43,6 @@ static __always_inline void __monitorx(const void *eax, u32 ecx, u32 edx) static __always_inline void __mwait(u32 eax, u32 ecx) { - mds_idle_clear_cpu_buffers(); - /* * Use the instruction mnemonic with implicit operands, as the LLVM * assembler fails to assemble the mnemonic with explicit operands: @@ -80,7 +78,7 @@ static __always_inline void __mwait(u32 eax, u32 ecx) */ static __always_inline void __mwaitx(u32 eax, u32 ebx, u32 ecx) { - /* No MDS buffer clear as this is AMD/HYGON only */ + /* No need for TSA buffer clearing on AMD */ /* "mwaitx %eax, %ebx, %ecx" */ asm volatile(".byte 0x0f, 0x01, 0xfb" @@ -98,7 +96,6 @@ static __always_inline void __mwaitx(u32 eax, u32 ebx, u32 ecx) */ static __always_inline void __sti_mwait(u32 eax, u32 ecx) { - mds_idle_clear_cpu_buffers(); asm volatile("sti; mwait" :: "a" (eax), "c" (ecx)); } @@ -115,21 +112,29 @@ static __always_inline void __sti_mwait(u32 eax, u32 ecx) */ static __always_inline void mwait_idle_with_hints(u32 eax, u32 ecx) { + if (need_resched()) + return; + + x86_idle_clear_cpu_buffers(); + if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) { const void *addr = ¤t_thread_info()->flags; alternative_input("", "clflush (%[addr])", X86_BUG_CLFLUSH_MONITOR, [addr] "a" (addr)); __monitor(addr, 0, 0); - if (!need_resched()) { - if (ecx & 1) { - __mwait(eax, ecx); - } else { - __sti_mwait(eax, ecx); - raw_local_irq_disable(); - } + if (need_resched()) + goto out; + + if (ecx & 1) { + __mwait(eax, ecx); + } else { + __sti_mwait(eax, ecx); + raw_local_irq_disable(); } } + +out: current_clr_polling(); } diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 20d754b98f3f..e29f82466f43 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -302,25 +302,31 @@ .endm /* - * Macro to execute VERW instruction that mitigate transient data sampling - * attacks such as MDS. On affected systems a microcode update overloaded VERW - * instruction to also clear the CPU buffers. VERW clobbers CFLAGS.ZF. - * + * Macro to execute VERW insns that mitigate transient data sampling + * attacks such as MDS or TSA. On affected systems a microcode update + * overloaded VERW insns to also clear the CPU buffers. VERW clobbers + * CFLAGS.ZF. * Note: Only the memory operand variant of VERW clears the CPU buffers. */ -.macro CLEAR_CPU_BUFFERS +.macro __CLEAR_CPU_BUFFERS feature #ifdef CONFIG_X86_64 - ALTERNATIVE "", "verw mds_verw_sel(%rip)", X86_FEATURE_CLEAR_CPU_BUF + ALTERNATIVE "", "verw x86_verw_sel(%rip)", \feature #else /* * In 32bit mode, the memory operand must be a %cs reference. The data * segments may not be usable (vm86 mode), and the stack segment may not * be flat (ESPFIX32). */ - ALTERNATIVE "", "verw %cs:mds_verw_sel", X86_FEATURE_CLEAR_CPU_BUF + ALTERNATIVE "", "verw %cs:x86_verw_sel", \feature #endif .endm +#define CLEAR_CPU_BUFFERS \ + __CLEAR_CPU_BUFFERS X86_FEATURE_CLEAR_CPU_BUF + +#define VM_CLEAR_CPU_BUFFERS \ + __CLEAR_CPU_BUFFERS X86_FEATURE_CLEAR_CPU_BUF_VM + #ifdef CONFIG_X86_64 .macro CLEAR_BRANCH_HISTORY ALTERNATIVE "", "call clear_bhb_loop", X86_FEATURE_CLEAR_BHB_LOOP @@ -524,6 +530,8 @@ void alternative_msr_write(unsigned int msr, u64 val, unsigned int feature) : "memory"); } +DECLARE_PER_CPU(bool, x86_ibpb_exit_to_user); + static inline void indirect_branch_prediction_barrier(void) { asm_inline volatile(ALTERNATIVE("", "call write_ibpb", X86_FEATURE_IBPB) @@ -567,24 +575,24 @@ DECLARE_STATIC_KEY_FALSE(switch_mm_always_ibpb); DECLARE_STATIC_KEY_FALSE(switch_vcpu_ibpb); -DECLARE_STATIC_KEY_FALSE(mds_idle_clear); +DECLARE_STATIC_KEY_FALSE(cpu_buf_idle_clear); DECLARE_STATIC_KEY_FALSE(switch_mm_cond_l1d_flush); DECLARE_STATIC_KEY_FALSE(cpu_buf_vm_clear); -extern u16 mds_verw_sel; +extern u16 x86_verw_sel; #include /** - * mds_clear_cpu_buffers - Mitigation for MDS and TAA vulnerability + * x86_clear_cpu_buffers - Buffer clearing support for different x86 CPU vulns * * This uses the otherwise unused and obsolete VERW instruction in * combination with microcode which triggers a CPU buffer flush when the * instruction is executed. */ -static __always_inline void mds_clear_cpu_buffers(void) +static __always_inline void x86_clear_cpu_buffers(void) { static const u16 ds = __KERNEL_DS; @@ -601,14 +609,15 @@ static __always_inline void mds_clear_cpu_buffers(void) } /** - * mds_idle_clear_cpu_buffers - Mitigation for MDS vulnerability + * x86_idle_clear_cpu_buffers - Buffer clearing support in idle for the MDS + * and TSA vulnerabilities. * * Clear CPU buffers if the corresponding static key is enabled */ -static __always_inline void mds_idle_clear_cpu_buffers(void) +static __always_inline void x86_idle_clear_cpu_buffers(void) { - if (static_branch_likely(&mds_idle_clear)) - mds_clear_cpu_buffers(); + if (static_branch_likely(&cpu_buf_idle_clear)) + x86_clear_cpu_buffers(); } #endif /* __ASSEMBLER__ */ diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 97954c936c54..e33df3da6980 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -301,16 +301,15 @@ static inline bool pmd_leaf(pmd_t pte) } #ifdef CONFIG_TRANSPARENT_HUGEPAGE -/* NOTE: when predicate huge page, consider also pmd_devmap, or use pmd_leaf */ static inline int pmd_trans_huge(pmd_t pmd) { - return (pmd_val(pmd) & (_PAGE_PSE|_PAGE_DEVMAP)) == _PAGE_PSE; + return (pmd_val(pmd) & _PAGE_PSE) == _PAGE_PSE; } #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD static inline int pud_trans_huge(pud_t pud) { - return (pud_val(pud) & (_PAGE_PSE|_PAGE_DEVMAP)) == _PAGE_PSE; + return (pud_val(pud) & _PAGE_PSE) == _PAGE_PSE; } #endif @@ -320,24 +319,6 @@ static inline int has_transparent_hugepage(void) return boot_cpu_has(X86_FEATURE_PSE); } -#ifdef CONFIG_ARCH_HAS_PTE_DEVMAP -static inline int pmd_devmap(pmd_t pmd) -{ - return !!(pmd_val(pmd) & _PAGE_DEVMAP); -} - -#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD -static inline int pud_devmap(pud_t pud) -{ - return !!(pud_val(pud) & _PAGE_DEVMAP); -} -#else -static inline int pud_devmap(pud_t pud) -{ - return 0; -} -#endif - #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP static inline bool pmd_special(pmd_t pmd) { @@ -361,12 +342,6 @@ static inline pud_t pud_mkspecial(pud_t pud) return pud_set_flags(pud, _PAGE_SPECIAL); } #endif /* CONFIG_ARCH_SUPPORTS_PUD_PFNMAP */ - -static inline int pgd_devmap(pgd_t pgd) -{ - return 0; -} -#endif #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ static inline pte_t pte_set_flags(pte_t pte, pteval_t set) @@ -527,11 +502,6 @@ static inline pte_t pte_mkspecial(pte_t pte) return pte_set_flags(pte, _PAGE_SPECIAL); } -static inline pte_t pte_mkdevmap(pte_t pte) -{ - return pte_set_flags(pte, _PAGE_SPECIAL|_PAGE_DEVMAP); -} - /* See comments above mksaveddirty_shift() */ static inline pmd_t pmd_mksaveddirty(pmd_t pmd) { @@ -603,11 +573,6 @@ static inline pmd_t pmd_mkwrite_shstk(pmd_t pmd) return pmd_set_flags(pmd, _PAGE_DIRTY); } -static inline pmd_t pmd_mkdevmap(pmd_t pmd) -{ - return pmd_set_flags(pmd, _PAGE_DEVMAP); -} - static inline pmd_t pmd_mkhuge(pmd_t pmd) { return pmd_set_flags(pmd, _PAGE_PSE); @@ -673,11 +638,6 @@ static inline pud_t pud_mkdirty(pud_t pud) return pud_mksaveddirty(pud); } -static inline pud_t pud_mkdevmap(pud_t pud) -{ - return pud_set_flags(pud, _PAGE_DEVMAP); -} - static inline pud_t pud_mkhuge(pud_t pud) { return pud_set_flags(pud, _PAGE_PSE); @@ -1008,13 +968,6 @@ static inline int pte_present(pte_t a) return pte_flags(a) & (_PAGE_PRESENT | _PAGE_PROTNONE); } -#ifdef CONFIG_ARCH_HAS_PTE_DEVMAP -static inline int pte_devmap(pte_t a) -{ - return (pte_flags(a) & _PAGE_DEVMAP) == _PAGE_DEVMAP; -} -#endif - #define pte_accessible pte_accessible static inline bool pte_accessible(struct mm_struct *mm, pte_t a) { diff --git a/arch/x86/include/asm/pgtable_64_types.h b/arch/x86/include/asm/pgtable_64_types.h index 4604f924d8b8..7eb61ef6a185 100644 --- a/arch/x86/include/asm/pgtable_64_types.h +++ b/arch/x86/include/asm/pgtable_64_types.h @@ -36,6 +36,9 @@ static inline bool pgtable_l5_enabled(void) #define pgtable_l5_enabled() cpu_feature_enabled(X86_FEATURE_LA57) #endif /* USE_EARLY_PGTABLE_L5 */ +#define ARCH_PAGE_TABLE_SYNC_MASK \ + (pgtable_l5_enabled() ? PGTBL_PGD_MODIFIED : PGTBL_P4D_MODIFIED) + extern unsigned int pgdir_shift; extern unsigned int ptrs_per_p4d; diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index b74ec5c3643b..2ec250ba467e 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h @@ -34,7 +34,6 @@ #define _PAGE_BIT_UFFD_WP _PAGE_BIT_SOFTW2 /* userfaultfd wrprotected */ #define _PAGE_BIT_SOFT_DIRTY _PAGE_BIT_SOFTW3 /* software dirty tracking */ #define _PAGE_BIT_KERNEL_4K _PAGE_BIT_SOFTW3 /* page must not be converted to large */ -#define _PAGE_BIT_DEVMAP _PAGE_BIT_SOFTW4 #ifdef CONFIG_X86_64 #define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW5 /* Saved Dirty bit (leaf) */ @@ -121,11 +120,9 @@ #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) -#define _PAGE_DEVMAP (_AT(u64, 1) << _PAGE_BIT_DEVMAP) #define _PAGE_SOFTW4 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW4) #else #define _PAGE_NX (_AT(pteval_t, 0)) -#define _PAGE_DEVMAP (_AT(pteval_t, 0)) #define _PAGE_SOFTW4 (_AT(pteval_t, 0)) #endif @@ -154,7 +151,7 @@ #define _COMMON_PAGE_CHG_MASK (PTE_PFN_MASK | _PAGE_PCD | _PAGE_PWT | \ _PAGE_SPECIAL | _PAGE_ACCESSED | \ _PAGE_DIRTY_BITS | _PAGE_SOFT_DIRTY | \ - _PAGE_DEVMAP | _PAGE_CC | _PAGE_UFFD_WP) + _PAGE_CC | _PAGE_UFFD_WP) #define _PAGE_CHG_MASK (_COMMON_PAGE_CHG_MASK | _PAGE_PAT) #define _HPAGE_CHG_MASK (_COMMON_PAGE_CHG_MASK | _PAGE_PSE | _PAGE_PAT_LARGE) @@ -214,9 +211,6 @@ enum page_cache_mode { #define PAGE_READONLY __pg(__PP| 0|_USR|___A|__NX| 0| 0| 0) #define PAGE_READONLY_EXEC __pg(__PP| 0|_USR|___A| 0| 0| 0| 0) -#define __PAGE_KERNEL (__PP|__RW| 0|___A|__NX|___D| 0|___G) -#define __PAGE_KERNEL_EXEC (__PP|__RW| 0|___A| 0|___D| 0|___G) - /* * Page tables needs to have Write=1 in order for any lower PTEs to be * writable. This includes shadow stack memory (Write=0, Dirty=1) diff --git a/arch/x86/include/asm/realmode.h b/arch/x86/include/asm/realmode.h index f607081a022a..e406a1e92c63 100644 --- a/arch/x86/include/asm/realmode.h +++ b/arch/x86/include/asm/realmode.h @@ -78,7 +78,7 @@ extern unsigned char secondary_startup_64[]; extern unsigned char secondary_startup_64_no_verify[]; #endif -static inline size_t real_mode_size_needed(void) +static __always_inline size_t real_mode_size_needed(void) { if (real_mode_header) return 0; /* already allocated. */ diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index 58e028d42e41..02236962fdb1 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -223,6 +223,18 @@ struct snp_tsc_info_resp { u8 rsvd2[100]; } __packed; +/* + * Obtain the mean TSC frequency by decreasing the nominal TSC frequency with + * TSC_FACTOR as documented in the SNP Firmware ABI specification: + * + * GUEST_TSC_FREQ * (1 - (TSC_FACTOR * 0.00001)) + * + * which is equivalent to: + * + * GUEST_TSC_FREQ -= (GUEST_TSC_FREQ * TSC_FACTOR) / 100000; + */ +#define SNP_SCALE_TSC_FREQ(freq, factor) ((freq) - (freq) * (factor) / 100000) + struct snp_guest_req { void *req_buf; size_t req_sz; @@ -231,6 +243,7 @@ struct snp_guest_req { size_t resp_sz; u64 exit_code; + u64 exitinfo2; unsigned int vmpck_id; u8 msg_version; u8 msg_type; @@ -282,8 +295,11 @@ struct snp_secrets_page { u8 svsm_guest_vmpl; u8 rsvd3[3]; + /* The percentage decrease from nominal to mean TSC frequency. */ + u32 tsc_factor; + /* Remainder of page */ - u8 rsvd4[3744]; + u8 rsvd4[3740]; } __packed; struct snp_msg_desc { @@ -445,7 +461,7 @@ static __always_inline void sev_es_nmi_complete(void) cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT)) __sev_es_nmi_complete(); } -extern int __init sev_es_efi_map_ghcbs(pgd_t *pgd); +extern int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd); extern void sev_enable(struct boot_params *bp); /* @@ -486,8 +502,6 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) return rc; } -struct snp_guest_request_ioctl; - void setup_ghcb(void); void early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned long npages); @@ -513,8 +527,7 @@ void snp_kexec_begin(void); int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id); struct snp_msg_desc *snp_msg_alloc(void); void snp_msg_free(struct snp_msg_desc *mdesc); -int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, - struct snp_guest_request_ioctl *rio); +int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req); int snp_svsm_vtpm_send_command(u8 *buffer); @@ -556,7 +569,7 @@ static inline void sev_es_ist_enter(struct pt_regs *regs) { } static inline void sev_es_ist_exit(void) { } static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; } static inline void sev_es_nmi_complete(void) { } -static inline int sev_es_efi_map_ghcbs(pgd_t *pgd) { return 0; } +static inline int sev_es_efi_map_ghcbs_cas(pgd_t *pgd) { return 0; } static inline void sev_enable(struct boot_params *bp) { } static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) { return 0; } static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs) { return 0; } @@ -587,8 +600,8 @@ static inline void snp_kexec_begin(void) { } static inline int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id) { return -1; } static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; } static inline void snp_msg_free(struct snp_msg_desc *mdesc) { } -static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, - struct snp_guest_request_ioctl *rio) { return -ENODEV; } +static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, + struct snp_guest_req *req) { return -ENODEV; } static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; } static inline void __init snp_secure_tsc_prepare(void) { } static inline void __init snp_secure_tsc_init(void) { } @@ -606,6 +619,24 @@ int rmp_make_shared(u64 pfn, enum pg_level level); void snp_leak_pages(u64 pfn, unsigned int npages); void kdump_sev_callback(void); void snp_fixup_e820_tables(void); + +static inline void sev_evict_cache(void *va, int npages) +{ + volatile u8 val __always_unused; + u8 *bytes = va; + int page_idx; + + /* + * For SEV guests, a read from the first/last cache-lines of a 4K page + * using the guest key is sufficient to cause a flush of all cache-lines + * associated with that 4K page without incurring all the overhead of a + * full CLFLUSH sequence. + */ + for (page_idx = 0; page_idx < npages; page_idx++) { + val = bytes[page_idx * PAGE_SIZE]; + val = bytes[page_idx * PAGE_SIZE + PAGE_SIZE - 1]; + } +} #else static inline bool snp_probe_rmptable_info(void) { return false; } static inline int snp_rmptable_init(void) { return -ENOSYS; } @@ -621,6 +652,7 @@ static inline int rmp_make_shared(u64 pfn, enum pg_level level) { return -ENODEV static inline void snp_leak_pages(u64 pfn, unsigned int npages) {} static inline void kdump_sev_callback(void) { } static inline void snp_fixup_e820_tables(void) {} +static inline void sev_evict_cache(void *va, int npages) {} #endif #endif diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/shared/tdx.h index 2f3820342598..8bc074c8d7c6 100644 --- a/arch/x86/include/asm/shared/tdx.h +++ b/arch/x86/include/asm/shared/tdx.h @@ -72,6 +72,7 @@ #define TDVMCALL_MAP_GPA 0x10001 #define TDVMCALL_GET_QUOTE 0x10002 #define TDVMCALL_REPORT_FATAL_ERROR 0x10003 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004ULL /* * TDG.VP.VMCALL Status Codes (returned in R10) @@ -80,6 +81,7 @@ #define TDVMCALL_STATUS_RETRY 0x0000000000000001ULL #define TDVMCALL_STATUS_INVALID_OPERAND 0x8000000000000000ULL #define TDVMCALL_STATUS_ALIGN_ERROR 0x8000000000000002ULL +#define TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED 0x8000000000000003ULL /* * Bitmasks of exposed registers (with VMM). diff --git a/arch/x86/include/asm/sighandling.h b/arch/x86/include/asm/sighandling.h index e770c4fc47f4..8727c7e21dd1 100644 --- a/arch/x86/include/asm/sighandling.h +++ b/arch/x86/include/asm/sighandling.h @@ -24,4 +24,26 @@ int ia32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs); int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs); int x32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs); +/* + * To prevent immediate repeat of single step trap on return from SIGTRAP + * handler if the trap flag (TF) is set without an external debugger attached, + * clear the software event flag in the augmented SS, ensuring no single-step + * trap is pending upon ERETU completion. + * + * Note, this function should be called in sigreturn() before the original + * state is restored to make sure the TF is read from the entry frame. + */ +static __always_inline void prevent_single_step_upon_eretu(struct pt_regs *regs) +{ + /* + * If the trap flag (TF) is set, i.e., the sigreturn() SYSCALL instruction + * is being single-stepped, do not clear the software event flag in the + * augmented SS, thus a debugger won't skip over the following instruction. + */ +#ifdef CONFIG_X86_FRED + if (!(regs->flags & X86_EFLAGS_TF)) + regs->fred_ss.swevent = 0; +#endif +} + #endif /* _ASM_X86_SIGHANDLING_H */ diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index 0c1c68039d6f..22bfebe6776d 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -112,7 +112,10 @@ void __noreturn hlt_play_dead(void); void native_play_dead(void); void play_dead_common(void); void wbinvd_on_cpu(int cpu); -int wbinvd_on_all_cpus(void); +void wbinvd_on_all_cpus(void); +void wbinvd_on_cpus_mask(struct cpumask *cpus); +void wbnoinvd_on_all_cpus(void); +void wbnoinvd_on_cpus_mask(struct cpumask *cpus); void smp_kick_mwait_play_dead(void); void __noreturn mwait_play_dead(unsigned int eax_hint); @@ -148,10 +151,24 @@ static inline struct cpumask *cpu_l2c_shared_mask(int cpu) #else /* !CONFIG_SMP */ #define wbinvd_on_cpu(cpu) wbinvd() -static inline int wbinvd_on_all_cpus(void) +static inline void wbinvd_on_all_cpus(void) { wbinvd(); - return 0; +} + +static inline void wbinvd_on_cpus_mask(struct cpumask *cpus) +{ + wbinvd(); +} + +static inline void wbnoinvd_on_all_cpus(void) +{ + wbnoinvd(); +} + +static inline void wbnoinvd_on_cpus_mask(struct cpumask *cpus) +{ + wbnoinvd(); } static inline struct cpumask *cpu_llc_shared_mask(int cpu) diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h index ecda17efa042..fde2bd7af19e 100644 --- a/arch/x86/include/asm/special_insns.h +++ b/arch/x86/include/asm/special_insns.h @@ -104,9 +104,36 @@ static inline void wrpkru(u32 pkru) } #endif +/* + * Write back all modified lines in all levels of cache associated with this + * logical processor to main memory, and then invalidate all caches. Depending + * on the micro-architecture, WBINVD (and WBNOINVD below) may or may not affect + * lower level caches associated with another logical processor that shares any + * level of this processor's cache hierarchy. + */ static __always_inline void wbinvd(void) { - asm volatile("wbinvd": : :"memory"); + asm volatile("wbinvd" : : : "memory"); +} + +/* Instruction encoding provided for binutils backwards compatibility. */ +#define ASM_WBNOINVD _ASM_BYTES(0xf3,0x0f,0x09) + +/* + * Write back all modified lines in all levels of cache associated with this + * logical processor to main memory, but do NOT explicitly invalidate caches, + * i.e. leave all/most cache lines in the hierarchy in non-modified state. + */ +static __always_inline void wbnoinvd(void) +{ + /* + * Explicitly encode WBINVD if X86_FEATURE_WBNOINVD is unavailable even + * though WBNOINVD is backwards compatible (it's simply WBINVD with an + * ignored REP prefix), to guarantee that WBNOINVD isn't used if it + * needs to be avoided for any reason. For all supported usage in the + * kernel, WBINVD is functionally a superset of WBNOINVD. + */ + alternative("wbinvd", ASM_WBNOINVD, X86_FEATURE_WBNOINVD); } static inline unsigned long __read_cr4(void) diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index ad954a1a6656..ffc27f676243 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -252,16 +252,21 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define AVIC_LOGICAL_ID_ENTRY_VALID_BIT 31 #define AVIC_LOGICAL_ID_ENTRY_VALID_MASK (1 << 31) +/* + * GA_LOG_INTR is a synthetic flag that's never propagated to hardware-visible + * tables. GA_LOG_INTR is set if the vCPU needs device posted IRQs to generate + * GA log interrupts to wake the vCPU (because it's blocking or about to block). + */ +#define AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR BIT_ULL(61) + #define AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK GENMASK_ULL(11, 0) -#define AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK (0xFFFFFFFFFFULL << 12) +#define AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK GENMASK_ULL(51, 12) #define AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK (1ULL << 62) #define AVIC_PHYSICAL_ID_ENTRY_VALID_MASK (1ULL << 63) #define AVIC_PHYSICAL_ID_TABLE_SIZE_MASK (0xFFULL) #define AVIC_DOORBELL_PHYSICAL_ID_MASK GENMASK_ULL(11, 0) -#define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL - #define AVIC_UNACCEL_ACCESS_WRITE_MASK 1 #define AVIC_UNACCEL_ACCESS_OFFSET_MASK 0xFF0 #define AVIC_UNACCEL_ACCESS_VECTOR_MASK 0xFFFFFFFF @@ -290,8 +295,6 @@ enum avic_ipi_failure_cause { static_assert((AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == AVIC_MAX_PHYSICAL_ID); static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_MAX_PHYSICAL_ID); -#define AVIC_HPA_MASK ~((0xFFFULL << 52) | 0xFFF) - #define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) #define SVM_SEV_FEAT_RESTRICTED_INJECTION BIT(3) #define SVM_SEV_FEAT_ALTERNATE_INJECTION BIT(4) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 8b19294600c4..7ddef3a69866 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -106,7 +106,7 @@ void tdx_init(void); typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args); -static inline u64 sc_retry(sc_func_t func, u64 fn, +static __always_inline u64 sc_retry(sc_func_t func, u64 fn, struct tdx_module_args *args) { int retry = RDRAND_RETRY_LOOPS; diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h index e9b81876ebe4..00daedfefc1b 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -356,11 +356,6 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); } -static inline void arch_flush_tlb_batched_pending(struct mm_struct *mm) -{ - flush_tlb_mm(mm); -} - extern void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch); static inline bool pte_flags_need_flush(unsigned long oldflags, diff --git a/arch/x86/include/asm/video.h b/arch/x86/include/asm/video.h index 0950c9535fae..08ec328203ef 100644 --- a/arch/x86/include/asm/video.h +++ b/arch/x86/include/asm/video.h @@ -13,8 +13,10 @@ pgprot_t pgprot_framebuffer(pgprot_t prot, unsigned long offset); #define pgprot_framebuffer pgprot_framebuffer +#ifdef CONFIG_VIDEO bool video_is_primary_device(struct device *dev); #define video_is_primary_device video_is_primary_device +#endif #include diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index 59a62c3780a2..a16d4631547c 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -94,12 +94,13 @@ DECLARE_STATIC_CALL(xen_hypercall, xen_hypercall_func); #ifdef MODULE #define __ADDRESSABLE_xen_hypercall #else -#define __ADDRESSABLE_xen_hypercall __ADDRESSABLE_ASM_STR(__SCK__xen_hypercall) +#define __ADDRESSABLE_xen_hypercall \ + __stringify(.global STATIC_CALL_KEY(xen_hypercall);) #endif #define __HYPERCALL \ __ADDRESSABLE_xen_hypercall \ - "call __SCT__xen_hypercall" + __stringify(call STATIC_CALL_TRAMP(xen_hypercall)) #define __HYPERCALL_ENTRY(x) "a" (x) diff --git a/arch/x86/include/uapi/asm/debugreg.h b/arch/x86/include/uapi/asm/debugreg.h index 0007ba077c0c..41da492dfb01 100644 --- a/arch/x86/include/uapi/asm/debugreg.h +++ b/arch/x86/include/uapi/asm/debugreg.h @@ -15,7 +15,26 @@ which debugging register was responsible for the trap. The other bits are either reserved or not of interest to us. */ -/* Define reserved bits in DR6 which are always set to 1 */ +/* + * Define bits in DR6 which are set to 1 by default. + * + * This is also the DR6 architectural value following Power-up, Reset or INIT. + * + * Note, with the introduction of Bus Lock Detection (BLD) and Restricted + * Transactional Memory (RTM), the DR6 register has been modified: + * + * 1) BLD flag (bit 11) is no longer reserved to 1 if the CPU supports + * Bus Lock Detection. The assertion of a bus lock could clear it. + * + * 2) RTM flag (bit 16) is no longer reserved to 1 if the CPU supports + * restricted transactional memory. #DB occurred inside an RTM region + * could clear it. + * + * Apparently, DR6.BLD and DR6.RTM are active low bits. + * + * As a result, DR6_RESERVED is an incorrect name now, but it is kept for + * compatibility. + */ #define DR6_RESERVED (0xFFFF0FF0) #define DR_TRAP0 (0x1) /* db0 */ diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 6f3499507c5e..0f15d683817d 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -965,7 +965,13 @@ struct kvm_tdx_cmd { struct kvm_tdx_capabilities { __u64 supported_attrs; __u64 supported_xfam; - __u64 reserved[254]; + + __u64 kernel_tdvmcallinfo_1_r11; + __u64 user_tdvmcallinfo_1_r11; + __u64 kernel_tdvmcallinfo_1_r12; + __u64 user_tdvmcallinfo_1_r12; + + __u64 reserved[250]; /* Configurable CPUID bits for userspace */ struct kvm_cpuid2 cpuid; diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index ecfe7b497cad..7bde68247b5f 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -116,6 +116,24 @@ static struct module *its_mod; #endif static void *its_page; static unsigned int its_offset; +struct its_array its_pages; + +static void *__its_alloc(struct its_array *pages) +{ + void *page __free(execmem) = execmem_alloc_rw(EXECMEM_MODULE_TEXT, PAGE_SIZE); + if (!page) + return NULL; + + void *tmp = krealloc(pages->pages, (pages->num+1) * sizeof(void *), + GFP_KERNEL); + if (!tmp) + return NULL; + + pages->pages = tmp; + pages->pages[pages->num++] = page; + + return no_free_ptr(page); +} /* Initialize a thunk with the "jmp *reg; int3" instructions. */ static void *its_init_thunk(void *thunk, int reg) @@ -151,6 +169,21 @@ static void *its_init_thunk(void *thunk, int reg) return thunk + offset; } +static void its_pages_protect(struct its_array *pages) +{ + for (int i = 0; i < pages->num; i++) { + void *page = pages->pages[i]; + execmem_restore_rox(page, PAGE_SIZE); + } +} + +static void its_fini_core(void) +{ + if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) + its_pages_protect(&its_pages); + kfree(its_pages.pages); +} + #ifdef CONFIG_MODULES void its_init_mod(struct module *mod) { @@ -173,10 +206,8 @@ void its_fini_mod(struct module *mod) its_page = NULL; mutex_unlock(&text_mutex); - for (int i = 0; i < mod->its_num_pages; i++) { - void *page = mod->its_page_array[i]; - execmem_restore_rox(page, PAGE_SIZE); - } + if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) + its_pages_protect(&mod->arch.its_pages); } void its_free_mod(struct module *mod) @@ -184,37 +215,32 @@ void its_free_mod(struct module *mod) if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) return; - for (int i = 0; i < mod->its_num_pages; i++) { - void *page = mod->its_page_array[i]; + for (int i = 0; i < mod->arch.its_pages.num; i++) { + void *page = mod->arch.its_pages.pages[i]; execmem_free(page); } - kfree(mod->its_page_array); + kfree(mod->arch.its_pages.pages); } #endif /* CONFIG_MODULES */ static void *its_alloc(void) { - void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); + struct its_array *pages = &its_pages; + void *page; +#ifdef CONFIG_MODULES + if (its_mod) + pages = &its_mod->arch.its_pages; +#endif + + page = __its_alloc(pages); if (!page) return NULL; -#ifdef CONFIG_MODULES - if (its_mod) { - void *tmp = krealloc(its_mod->its_page_array, - (its_mod->its_num_pages+1) * sizeof(void *), - GFP_KERNEL); - if (!tmp) - return NULL; + if (pages == &its_pages) + set_memory_x((unsigned long)page, 1); - its_mod->its_page_array = tmp; - its_mod->its_page_array[its_mod->its_num_pages++] = page; - - execmem_make_temp_rw(page, PAGE_SIZE); - } -#endif /* CONFIG_MODULES */ - - return no_free_ptr(page); + return page; } static void *its_allocate_thunk(int reg) @@ -268,7 +294,9 @@ u8 *its_static_thunk(int reg) return thunk; } -#endif +#else +static inline void its_fini_core(void) {} +#endif /* CONFIG_MITIGATION_ITS */ /* * Nomenclature for variable names to simplify and clarify this code and ease @@ -1155,43 +1183,6 @@ bool cfi_bhi __ro_after_init = false; #endif #ifdef CONFIG_CFI_CLANG -struct bpf_insn; - -/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */ -extern unsigned int __bpf_prog_runX(const void *ctx, - const struct bpf_insn *insn); - -KCFI_REFERENCE(__bpf_prog_runX); - -/* u32 __ro_after_init cfi_bpf_hash = __kcfi_typeid___bpf_prog_runX; */ -asm ( -" .pushsection .data..ro_after_init,\"aw\",@progbits \n" -" .type cfi_bpf_hash,@object \n" -" .globl cfi_bpf_hash \n" -" .p2align 2, 0x0 \n" -"cfi_bpf_hash: \n" -" .long __kcfi_typeid___bpf_prog_runX \n" -" .size cfi_bpf_hash, 4 \n" -" .popsection \n" -); - -/* Must match bpf_callback_t */ -extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64); - -KCFI_REFERENCE(__bpf_callback_fn); - -/* u32 __ro_after_init cfi_bpf_subprog_hash = __kcfi_typeid___bpf_callback_fn; */ -asm ( -" .pushsection .data..ro_after_init,\"aw\",@progbits \n" -" .type cfi_bpf_subprog_hash,@object \n" -" .globl cfi_bpf_subprog_hash \n" -" .p2align 2, 0x0 \n" -"cfi_bpf_subprog_hash: \n" -" .long __kcfi_typeid___bpf_callback_fn \n" -" .size cfi_bpf_subprog_hash, 4 \n" -" .popsection \n" -); - u32 cfi_get_func_hash(void *func) { u32 hash; @@ -2338,6 +2329,8 @@ void __init alternative_instructions(void) apply_retpolines(__retpoline_sites, __retpoline_sites_end); apply_returns(__return_sites, __return_sites_end); + its_fini_core(); + /* * Adjust all CALL instructions to point to func()-10, including * those in .altinstr_replacement. @@ -3107,6 +3100,6 @@ void __ref smp_text_poke_batch_add(void *addr, const void *opcode, size_t len, c */ void __ref smp_text_poke_single(void *addr, const void *opcode, size_t len, const void *emulate) { - __smp_text_poke_batch_add(addr, opcode, len, emulate); + smp_text_poke_batch_add(addr, opcode, len, emulate); smp_text_poke_batch_finish(); } diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index 93069b13d3af..a947b46a8b64 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -183,6 +183,7 @@ static void apic_update_vector(struct irq_data *irqd, unsigned int newvec, apicd->cpu = newcpu; BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec])); per_cpu(vector_irq, newcpu)[newvec] = desc; + apic_update_irq_cfg(irqd, newvec, newcpu); } static void vector_assign_managed_shutdown(struct irq_data *irqd) @@ -261,7 +262,6 @@ assign_vector_locked(struct irq_data *irqd, const struct cpumask *dest) if (vector < 0) return vector; apic_update_vector(irqd, vector, cpu); - apic_update_irq_cfg(irqd, vector, cpu); return 0; } @@ -338,7 +338,7 @@ assign_managed_vector(struct irq_data *irqd, const struct cpumask *dest) if (vector < 0) return vector; apic_update_vector(irqd, vector, cpu); - apic_update_irq_cfg(irqd, vector, cpu); + return 0; } diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 93da466dfe2c..a6f88ca1a6b4 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,7 +31,7 @@ #include "cpu.h" -u16 invlpgb_count_max __ro_after_init; +u16 invlpgb_count_max __ro_after_init = 1; static inline int rdmsrq_amd_safe(unsigned msr, u64 *p) { @@ -377,6 +377,47 @@ static void bsp_determine_snp(struct cpuinfo_x86 *c) #endif } +#define ZEN_MODEL_STEP_UCODE(fam, model, step, ucode) \ + X86_MATCH_VFM_STEPS(VFM_MAKE(X86_VENDOR_AMD, fam, model), \ + step, step, ucode) + +static const struct x86_cpu_id amd_tsa_microcode[] = { + ZEN_MODEL_STEP_UCODE(0x19, 0x01, 0x1, 0x0a0011d7), + ZEN_MODEL_STEP_UCODE(0x19, 0x01, 0x2, 0x0a00123b), + ZEN_MODEL_STEP_UCODE(0x19, 0x08, 0x2, 0x0a00820d), + ZEN_MODEL_STEP_UCODE(0x19, 0x11, 0x1, 0x0a10114c), + ZEN_MODEL_STEP_UCODE(0x19, 0x11, 0x2, 0x0a10124c), + ZEN_MODEL_STEP_UCODE(0x19, 0x18, 0x1, 0x0a108109), + ZEN_MODEL_STEP_UCODE(0x19, 0x21, 0x0, 0x0a20102e), + ZEN_MODEL_STEP_UCODE(0x19, 0x21, 0x2, 0x0a201211), + ZEN_MODEL_STEP_UCODE(0x19, 0x44, 0x1, 0x0a404108), + ZEN_MODEL_STEP_UCODE(0x19, 0x50, 0x0, 0x0a500012), + ZEN_MODEL_STEP_UCODE(0x19, 0x61, 0x2, 0x0a60120a), + ZEN_MODEL_STEP_UCODE(0x19, 0x74, 0x1, 0x0a704108), + ZEN_MODEL_STEP_UCODE(0x19, 0x75, 0x2, 0x0a705208), + ZEN_MODEL_STEP_UCODE(0x19, 0x78, 0x0, 0x0a708008), + ZEN_MODEL_STEP_UCODE(0x19, 0x7c, 0x0, 0x0a70c008), + ZEN_MODEL_STEP_UCODE(0x19, 0xa0, 0x2, 0x0aa00216), + {}, +}; + +static void tsa_init(struct cpuinfo_x86 *c) +{ + if (cpu_has(c, X86_FEATURE_HYPERVISOR)) + return; + + if (cpu_has(c, X86_FEATURE_ZEN3) || + cpu_has(c, X86_FEATURE_ZEN4)) { + if (x86_match_min_microcode_rev(amd_tsa_microcode)) + setup_force_cpu_cap(X86_FEATURE_VERW_CLEAR); + else + pr_debug("%s: current revision: 0x%x\n", __func__, c->microcode); + } else { + setup_force_cpu_cap(X86_FEATURE_TSA_SQ_NO); + setup_force_cpu_cap(X86_FEATURE_TSA_L1_NO); + } +} + static void bsp_init_amd(struct cpuinfo_x86 *c) { if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { @@ -489,6 +530,11 @@ static void bsp_init_amd(struct cpuinfo_x86 *c) } bsp_determine_snp(c); + tsa_init(c); + + if (cpu_has(c, X86_FEATURE_GP_ON_USER_CPUID)) + setup_force_cpu_cap(X86_FEATURE_CPUID_FAULT); + return; warn: @@ -930,6 +976,16 @@ static void init_amd_zen2(struct cpuinfo_x86 *c) init_spectral_chicken(c); fix_erratum_1386(c); zen2_zenbleed_check(c); + + /* Disable RDSEED on AMD Cyan Skillfish because of an error. */ + if (c->x86_model == 0x47 && c->x86_stepping == 0x0) { + clear_cpu_cap(c, X86_FEATURE_RDSEED); + msr_clear_bit(MSR_AMD64_CPUID_FN_7, 18); + pr_emerg("RDSEED is not reliable on this platform; disabling.\n"); + } + + /* Correct misconfigured CPUID on some clients. */ + clear_cpu_cap(c, X86_FEATURE_INVLPGB); } static void init_amd_zen3(struct cpuinfo_x86 *c) @@ -1270,8 +1326,8 @@ static const char * const s5_reset_reason_txt[] = { static __init int print_s5_reset_status_mmio(void) { - unsigned long value; void __iomem *addr; + u32 value; int i; if (!cpu_feature_enabled(X86_FEATURE_ZEN)) @@ -1284,12 +1340,16 @@ static __init int print_s5_reset_status_mmio(void) value = ioread32(addr); iounmap(addr); + /* Value with "all bits set" is an error response and should be ignored. */ + if (value == U32_MAX) + return 0; + for (i = 0; i < ARRAY_SIZE(s5_reset_reason_txt); i++) { if (!(value & BIT(i))) continue; if (s5_reset_reason_txt[i]) { - pr_info("x86/amd: Previous system reset reason [0x%08lx]: %s\n", + pr_info("x86/amd: Previous system reset reason [0x%08x]: %s\n", value, s5_reset_reason_txt[i]); } } diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 7f94e6a5497d..36dcfc5105be 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -94,6 +94,11 @@ static void __init bhi_apply_mitigation(void); static void __init its_select_mitigation(void); static void __init its_update_mitigation(void); static void __init its_apply_mitigation(void); +static void __init tsa_select_mitigation(void); +static void __init tsa_apply_mitigation(void); +static void __init vmscape_select_mitigation(void); +static void __init vmscape_update_mitigation(void); +static void __init vmscape_apply_mitigation(void); /* The base value of the SPEC_CTRL MSR without task-specific bits set */ u64 x86_spec_ctrl_base; @@ -103,6 +108,14 @@ EXPORT_SYMBOL_GPL(x86_spec_ctrl_base); DEFINE_PER_CPU(u64, x86_spec_ctrl_current); EXPORT_PER_CPU_SYMBOL_GPL(x86_spec_ctrl_current); +/* + * Set when the CPU has run a potentially malicious guest. An IBPB will + * be needed to before running userspace. That IBPB will flush the branch + * predictor content. + */ +DEFINE_PER_CPU(bool, x86_ibpb_exit_to_user); +EXPORT_PER_CPU_SYMBOL_GPL(x86_ibpb_exit_to_user); + u64 x86_pred_cmd __ro_after_init = PRED_CMD_IBPB; static u64 __ro_after_init x86_arch_cap_msr; @@ -113,10 +126,9 @@ void (*x86_return_thunk)(void) __ro_after_init = __x86_return_thunk; static void __init set_return_thunk(void *thunk) { - if (x86_return_thunk != __x86_return_thunk) - pr_warn("x86/bugs: return thunk changed\n"); - x86_return_thunk = thunk; + + pr_info("active return thunk: %ps\n", thunk); } /* Update SPEC_CTRL MSR and its cached copy unconditionally */ @@ -169,9 +181,9 @@ DEFINE_STATIC_KEY_FALSE(switch_mm_always_ibpb); DEFINE_STATIC_KEY_FALSE(switch_vcpu_ibpb); EXPORT_SYMBOL_GPL(switch_vcpu_ibpb); -/* Control MDS CPU buffer clear before idling (halt, mwait) */ -DEFINE_STATIC_KEY_FALSE(mds_idle_clear); -EXPORT_SYMBOL_GPL(mds_idle_clear); +/* Control CPU buffer clear before idling (halt, mwait) */ +DEFINE_STATIC_KEY_FALSE(cpu_buf_idle_clear); +EXPORT_SYMBOL_GPL(cpu_buf_idle_clear); /* * Controls whether l1d flush based mitigations are enabled, @@ -188,6 +200,39 @@ DEFINE_STATIC_KEY_FALSE(switch_mm_cond_l1d_flush); DEFINE_STATIC_KEY_FALSE(cpu_buf_vm_clear); EXPORT_SYMBOL_GPL(cpu_buf_vm_clear); +#undef pr_fmt +#define pr_fmt(fmt) "mitigations: " fmt + +static void __init cpu_print_attack_vectors(void) +{ + pr_info("Enabled attack vectors: "); + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL)) + pr_cont("user_kernel, "); + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER)) + pr_cont("user_user, "); + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST)) + pr_cont("guest_host, "); + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST)) + pr_cont("guest_guest, "); + + pr_cont("SMT mitigations: "); + + switch (smt_mitigations) { + case SMT_MITIGATIONS_OFF: + pr_cont("off\n"); + break; + case SMT_MITIGATIONS_AUTO: + pr_cont("auto\n"); + break; + case SMT_MITIGATIONS_ON: + pr_cont("on\n"); + } +} + void __init cpu_select_mitigations(void) { /* @@ -208,6 +253,8 @@ void __init cpu_select_mitigations(void) x86_arch_cap_msr = x86_read_arch_cap_msr(); + cpu_print_attack_vectors(); + /* Select the proper CPU mitigations before patching alternatives: */ spectre_v1_select_mitigation(); spectre_v2_select_mitigation(); @@ -225,6 +272,8 @@ void __init cpu_select_mitigations(void) gds_select_mitigation(); its_select_mitigation(); bhi_select_mitigation(); + tsa_select_mitigation(); + vmscape_select_mitigation(); /* * After mitigations are selected, some may need to update their @@ -256,6 +305,7 @@ void __init cpu_select_mitigations(void) bhi_update_mitigation(); /* srso_update_mitigation() depends on retbleed_update_mitigation(). */ srso_update_mitigation(); + vmscape_update_mitigation(); spectre_v1_apply_mitigation(); spectre_v2_apply_mitigation(); @@ -272,6 +322,8 @@ void __init cpu_select_mitigations(void) gds_apply_mitigation(); its_apply_mitigation(); bhi_apply_mitigation(); + tsa_apply_mitigation(); + vmscape_apply_mitigation(); } /* @@ -329,6 +381,65 @@ static void x86_amd_ssb_disable(void) #undef pr_fmt #define pr_fmt(fmt) "MDS: " fmt +/* + * Returns true if vulnerability should be mitigated based on the + * selected attack vector controls. + * + * See Documentation/admin-guide/hw-vuln/attack_vector_controls.rst + */ +static bool __init should_mitigate_vuln(unsigned int bug) +{ + switch (bug) { + /* + * The only runtime-selected spectre_v1 mitigations in the kernel are + * related to SWAPGS protection on kernel entry. Therefore, protection + * is only required for the user->kernel attack vector. + */ + case X86_BUG_SPECTRE_V1: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL); + + case X86_BUG_SPECTRE_V2: + case X86_BUG_RETBLEED: + case X86_BUG_L1TF: + case X86_BUG_ITS: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST); + + case X86_BUG_SPECTRE_V2_USER: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST); + + /* + * All the vulnerabilities below allow potentially leaking data + * across address spaces. Therefore, mitigation is required for + * any of these 4 attack vectors. + */ + case X86_BUG_MDS: + case X86_BUG_TAA: + case X86_BUG_MMIO_STALE_DATA: + case X86_BUG_RFDS: + case X86_BUG_SRBDS: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST) || + cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST); + + case X86_BUG_GDS: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST) || + cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST) || + (smt_mitigations != SMT_MITIGATIONS_OFF); + + case X86_BUG_SPEC_STORE_BYPASS: + return cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER); + + default: + WARN(1, "Unknown bug %x\n", bug); + return false; + } +} + /* Default mitigation for MDS-affected CPUs */ static enum mds_mitigations mds_mitigation __ro_after_init = IS_ENABLED(CONFIG_MITIGATION_MDS) ? MDS_MITIGATION_AUTO : MDS_MITIGATION_OFF; @@ -382,13 +493,17 @@ static bool verw_clear_cpu_buf_mitigation_selected __ro_after_init; static void __init mds_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_MDS) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_MDS)) { mds_mitigation = MDS_MITIGATION_OFF; return; } - if (mds_mitigation == MDS_MITIGATION_AUTO) - mds_mitigation = MDS_MITIGATION_FULL; + if (mds_mitigation == MDS_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_MDS)) + mds_mitigation = MDS_MITIGATION_FULL; + else + mds_mitigation = MDS_MITIGATION_OFF; + } if (mds_mitigation == MDS_MITIGATION_OFF) return; @@ -398,7 +513,7 @@ static void __init mds_select_mitigation(void) static void __init mds_update_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_MDS) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_MDS)) return; /* If TAA, MMIO, or RFDS are being mitigated, MDS gets mitigated too. */ @@ -419,7 +534,7 @@ static void __init mds_apply_mitigation(void) mds_mitigation == MDS_MITIGATION_VMWERV) { setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF); if (!boot_cpu_has(X86_BUG_MSBDS_ONLY) && - (mds_nosmt || cpu_mitigations_auto_nosmt())) + (mds_nosmt || smt_mitigations == SMT_MITIGATIONS_ON)) cpu_smt_disable(false); } } @@ -475,12 +590,13 @@ static void __init taa_select_mitigation(void) return; } - if (cpu_mitigations_off()) - taa_mitigation = TAA_MITIGATION_OFF; - /* Microcode will be checked in taa_update_mitigation(). */ - if (taa_mitigation == TAA_MITIGATION_AUTO) - taa_mitigation = TAA_MITIGATION_VERW; + if (taa_mitigation == TAA_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_TAA)) + taa_mitigation = TAA_MITIGATION_VERW; + else + taa_mitigation = TAA_MITIGATION_OFF; + } if (taa_mitigation != TAA_MITIGATION_OFF) verw_clear_cpu_buf_mitigation_selected = true; @@ -488,7 +604,7 @@ static void __init taa_select_mitigation(void) static void __init taa_update_mitigation(void) { - if (!taa_vulnerable() || cpu_mitigations_off()) + if (!taa_vulnerable()) return; if (verw_clear_cpu_buf_mitigation_selected) @@ -529,7 +645,7 @@ static void __init taa_apply_mitigation(void) */ setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF); - if (taa_nosmt || cpu_mitigations_auto_nosmt()) + if (taa_nosmt || smt_mitigations == SMT_MITIGATIONS_ON) cpu_smt_disable(false); } } @@ -575,8 +691,12 @@ static void __init mmio_select_mitigation(void) } /* Microcode will be checked in mmio_update_mitigation(). */ - if (mmio_mitigation == MMIO_MITIGATION_AUTO) - mmio_mitigation = MMIO_MITIGATION_VERW; + if (mmio_mitigation == MMIO_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_MMIO_STALE_DATA)) + mmio_mitigation = MMIO_MITIGATION_VERW; + else + mmio_mitigation = MMIO_MITIGATION_OFF; + } if (mmio_mitigation == MMIO_MITIGATION_OFF) return; @@ -591,7 +711,7 @@ static void __init mmio_select_mitigation(void) static void __init mmio_update_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_MMIO_STALE_DATA) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_MMIO_STALE_DATA)) return; if (verw_clear_cpu_buf_mitigation_selected) @@ -637,9 +757,9 @@ static void __init mmio_apply_mitigation(void) * is required irrespective of SMT state. */ if (!(x86_arch_cap_msr & ARCH_CAP_FBSDP_NO)) - static_branch_enable(&mds_idle_clear); + static_branch_enable(&cpu_buf_idle_clear); - if (mmio_nosmt || cpu_mitigations_auto_nosmt()) + if (mmio_nosmt || smt_mitigations == SMT_MITIGATIONS_ON) cpu_smt_disable(false); } @@ -680,13 +800,17 @@ static inline bool __init verw_clears_cpu_reg_file(void) static void __init rfds_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_RFDS) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_RFDS)) { rfds_mitigation = RFDS_MITIGATION_OFF; return; } - if (rfds_mitigation == RFDS_MITIGATION_AUTO) - rfds_mitigation = RFDS_MITIGATION_VERW; + if (rfds_mitigation == RFDS_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_RFDS)) + rfds_mitigation = RFDS_MITIGATION_VERW; + else + rfds_mitigation = RFDS_MITIGATION_OFF; + } if (rfds_mitigation == RFDS_MITIGATION_OFF) return; @@ -697,7 +821,7 @@ static void __init rfds_select_mitigation(void) static void __init rfds_update_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_RFDS) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_RFDS)) return; if (verw_clear_cpu_buf_mitigation_selected) @@ -798,13 +922,19 @@ void update_srbds_msr(void) static void __init srbds_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_SRBDS) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_SRBDS)) { srbds_mitigation = SRBDS_MITIGATION_OFF; return; } - if (srbds_mitigation == SRBDS_MITIGATION_AUTO) - srbds_mitigation = SRBDS_MITIGATION_FULL; + if (srbds_mitigation == SRBDS_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_SRBDS)) + srbds_mitigation = SRBDS_MITIGATION_FULL; + else { + srbds_mitigation = SRBDS_MITIGATION_OFF; + return; + } + } /* * Check to see if this is one of the MDS_NO systems supporting TSX that @@ -952,12 +1082,13 @@ static void __init gds_select_mitigation(void) return; } - if (cpu_mitigations_off()) - gds_mitigation = GDS_MITIGATION_OFF; /* Will verify below that mitigation _can_ be disabled */ - - if (gds_mitigation == GDS_MITIGATION_AUTO) - gds_mitigation = GDS_MITIGATION_FULL; + if (gds_mitigation == GDS_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_GDS)) + gds_mitigation = GDS_MITIGATION_FULL; + else + gds_mitigation = GDS_MITIGATION_OFF; + } /* No microcode */ if (!(x86_arch_cap_msr & ARCH_CAP_GDS_CTRL)) { @@ -1063,13 +1194,16 @@ static bool smap_works_speculatively(void) static void __init spectre_v1_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1)) + spectre_v1_mitigation = SPECTRE_V1_MITIGATION_NONE; + + if (!should_mitigate_vuln(X86_BUG_SPECTRE_V1)) spectre_v1_mitigation = SPECTRE_V1_MITIGATION_NONE; } static void __init spectre_v1_apply_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1)) return; if (spectre_v1_mitigation == SPECTRE_V1_MITIGATION_AUTO) { @@ -1120,6 +1254,20 @@ early_param("nospectre_v1", nospectre_v1_cmdline); enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init = SPECTRE_V2_NONE; +/* Depends on spectre_v2 mitigation selected already */ +static inline bool cdt_possible(enum spectre_v2_mitigation mode) +{ + if (!IS_ENABLED(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) || + !IS_ENABLED(CONFIG_MITIGATION_RETPOLINE)) + return false; + + if (mode == SPECTRE_V2_RETPOLINE || + mode == SPECTRE_V2_EIBRS_RETPOLINE) + return true; + + return false; +} + #undef pr_fmt #define pr_fmt(fmt) "RETBleed: " fmt @@ -1158,6 +1306,21 @@ static enum retbleed_mitigation retbleed_mitigation __ro_after_init = static int __ro_after_init retbleed_nosmt = false; +enum srso_mitigation { + SRSO_MITIGATION_NONE, + SRSO_MITIGATION_AUTO, + SRSO_MITIGATION_UCODE_NEEDED, + SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED, + SRSO_MITIGATION_MICROCODE, + SRSO_MITIGATION_NOSMT, + SRSO_MITIGATION_SAFE_RET, + SRSO_MITIGATION_IBPB, + SRSO_MITIGATION_IBPB_ON_VMEXIT, + SRSO_MITIGATION_BP_SPEC_REDUCE, +}; + +static enum srso_mitigation srso_mitigation __ro_after_init = SRSO_MITIGATION_AUTO; + static int __init retbleed_parse_cmdline(char *str) { if (!str) @@ -1200,7 +1363,7 @@ early_param("retbleed", retbleed_parse_cmdline); static void __init retbleed_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_RETBLEED) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_RETBLEED)) { retbleed_mitigation = RETBLEED_MITIGATION_NONE; return; } @@ -1237,6 +1400,11 @@ static void __init retbleed_select_mitigation(void) if (retbleed_mitigation != RETBLEED_MITIGATION_AUTO) return; + if (!should_mitigate_vuln(X86_BUG_RETBLEED)) { + retbleed_mitigation = RETBLEED_MITIGATION_NONE; + return; + } + /* Intel mitigation selected in retbleed_update_mitigation() */ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD || boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { @@ -1247,35 +1415,36 @@ static void __init retbleed_select_mitigation(void) retbleed_mitigation = RETBLEED_MITIGATION_IBPB; else retbleed_mitigation = RETBLEED_MITIGATION_NONE; + } else if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) { + /* Final mitigation depends on spectre-v2 selection */ + if (boot_cpu_has(X86_FEATURE_IBRS_ENHANCED)) + retbleed_mitigation = RETBLEED_MITIGATION_EIBRS; + else if (boot_cpu_has(X86_FEATURE_IBRS)) + retbleed_mitigation = RETBLEED_MITIGATION_IBRS; + else + retbleed_mitigation = RETBLEED_MITIGATION_NONE; } } static void __init retbleed_update_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_RETBLEED) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_RETBLEED)) return; - if (retbleed_mitigation == RETBLEED_MITIGATION_NONE) - goto out; + /* ITS can also enable stuffing */ + if (its_mitigation == ITS_MITIGATION_RETPOLINE_STUFF) + retbleed_mitigation = RETBLEED_MITIGATION_STUFF; - /* - * retbleed=stuff is only allowed on Intel. If stuffing can't be used - * then a different mitigation will be selected below. - * - * its=stuff will also attempt to enable stuffing. - */ - if (retbleed_mitigation == RETBLEED_MITIGATION_STUFF || - its_mitigation == ITS_MITIGATION_RETPOLINE_STUFF) { - if (spectre_v2_enabled != SPECTRE_V2_RETPOLINE) { - pr_err("WARNING: retbleed=stuff depends on spectre_v2=retpoline\n"); - retbleed_mitigation = RETBLEED_MITIGATION_AUTO; - } else { - if (retbleed_mitigation != RETBLEED_MITIGATION_STUFF) - pr_info("Retbleed mitigation updated to stuffing\n"); + /* If SRSO is using IBPB, that works for retbleed too */ + if (srso_mitigation == SRSO_MITIGATION_IBPB) + retbleed_mitigation = RETBLEED_MITIGATION_IBPB; - retbleed_mitigation = RETBLEED_MITIGATION_STUFF; - } + if (retbleed_mitigation == RETBLEED_MITIGATION_STUFF && + !cdt_possible(spectre_v2_enabled)) { + pr_err("WARNING: retbleed=stuff depends on retpoline\n"); + retbleed_mitigation = RETBLEED_MITIGATION_NONE; } + /* * Let IBRS trump all on Intel without affecting the effects of the * retbleed= cmdline option except for call depth based stuffing @@ -1294,15 +1463,11 @@ static void __init retbleed_update_mitigation(void) if (retbleed_mitigation != RETBLEED_MITIGATION_STUFF) pr_err(RETBLEED_INTEL_MSG); } - /* If nothing has set the mitigation yet, default to NONE. */ - if (retbleed_mitigation == RETBLEED_MITIGATION_AUTO) - retbleed_mitigation = RETBLEED_MITIGATION_NONE; } -out: + pr_info("%s\n", retbleed_strings[retbleed_mitigation]); } - static void __init retbleed_apply_mitigation(void) { bool mitigate_smt = false; @@ -1358,7 +1523,7 @@ static void __init retbleed_apply_mitigation(void) } if (mitigate_smt && !boot_cpu_has(X86_FEATURE_STIBP) && - (retbleed_nosmt || cpu_mitigations_auto_nosmt())) + (retbleed_nosmt || smt_mitigations == SMT_MITIGATIONS_ON)) cpu_smt_disable(false); } @@ -1403,13 +1568,17 @@ early_param("indirect_target_selection", its_parse_cmdline); static void __init its_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_ITS) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_ITS)) { its_mitigation = ITS_MITIGATION_OFF; return; } - if (its_mitigation == ITS_MITIGATION_AUTO) - its_mitigation = ITS_MITIGATION_ALIGNED_THUNKS; + if (its_mitigation == ITS_MITIGATION_AUTO) { + if (should_mitigate_vuln(X86_BUG_ITS)) + its_mitigation = ITS_MITIGATION_ALIGNED_THUNKS; + else + its_mitigation = ITS_MITIGATION_OFF; + } if (its_mitigation == ITS_MITIGATION_OFF) return; @@ -1440,15 +1609,17 @@ static void __init its_select_mitigation(void) static void __init its_update_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_ITS) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_ITS)) return; switch (spectre_v2_enabled) { case SPECTRE_V2_NONE: - pr_err("WARNING: Spectre-v2 mitigation is off, disabling ITS\n"); + if (its_mitigation != ITS_MITIGATION_OFF) + pr_err("WARNING: Spectre-v2 mitigation is off, disabling ITS\n"); its_mitigation = ITS_MITIGATION_OFF; break; case SPECTRE_V2_RETPOLINE: + case SPECTRE_V2_EIBRS_RETPOLINE: /* Retpoline+CDT mitigates ITS */ if (retbleed_mitigation == RETBLEED_MITIGATION_STUFF) its_mitigation = ITS_MITIGATION_RETPOLINE_STUFF; @@ -1462,13 +1633,8 @@ static void __init its_update_mitigation(void) break; } - /* - * retbleed_update_mitigation() will try to do stuffing if its=stuff. - * If it can't, such as if spectre_v2!=retpoline, then fall back to - * aligned thunks. - */ if (its_mitigation == ITS_MITIGATION_RETPOLINE_STUFF && - retbleed_mitigation != RETBLEED_MITIGATION_STUFF) + !cdt_possible(spectre_v2_enabled)) its_mitigation = ITS_MITIGATION_ALIGNED_THUNKS; pr_info("%s\n", its_strings[its_mitigation]); @@ -1476,15 +1642,127 @@ static void __init its_update_mitigation(void) static void __init its_apply_mitigation(void) { - /* its=stuff forces retbleed stuffing and is enabled there. */ - if (its_mitigation != ITS_MITIGATION_ALIGNED_THUNKS) + switch (its_mitigation) { + case ITS_MITIGATION_OFF: + case ITS_MITIGATION_AUTO: + case ITS_MITIGATION_VMEXIT_ONLY: + break; + case ITS_MITIGATION_ALIGNED_THUNKS: + if (!boot_cpu_has(X86_FEATURE_RETPOLINE)) + setup_force_cpu_cap(X86_FEATURE_INDIRECT_THUNK_ITS); + + setup_force_cpu_cap(X86_FEATURE_RETHUNK); + set_return_thunk(its_return_thunk); + break; + case ITS_MITIGATION_RETPOLINE_STUFF: + setup_force_cpu_cap(X86_FEATURE_RETHUNK); + setup_force_cpu_cap(X86_FEATURE_CALL_DEPTH); + set_return_thunk(call_depth_return_thunk); + break; + } +} + +#undef pr_fmt +#define pr_fmt(fmt) "Transient Scheduler Attacks: " fmt + +enum tsa_mitigations { + TSA_MITIGATION_NONE, + TSA_MITIGATION_AUTO, + TSA_MITIGATION_UCODE_NEEDED, + TSA_MITIGATION_USER_KERNEL, + TSA_MITIGATION_VM, + TSA_MITIGATION_FULL, +}; + +static const char * const tsa_strings[] = { + [TSA_MITIGATION_NONE] = "Vulnerable", + [TSA_MITIGATION_UCODE_NEEDED] = "Vulnerable: No microcode", + [TSA_MITIGATION_USER_KERNEL] = "Mitigation: Clear CPU buffers: user/kernel boundary", + [TSA_MITIGATION_VM] = "Mitigation: Clear CPU buffers: VM", + [TSA_MITIGATION_FULL] = "Mitigation: Clear CPU buffers", +}; + +static enum tsa_mitigations tsa_mitigation __ro_after_init = + IS_ENABLED(CONFIG_MITIGATION_TSA) ? TSA_MITIGATION_AUTO : TSA_MITIGATION_NONE; + +static int __init tsa_parse_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (!strcmp(str, "off")) + tsa_mitigation = TSA_MITIGATION_NONE; + else if (!strcmp(str, "on")) + tsa_mitigation = TSA_MITIGATION_FULL; + else if (!strcmp(str, "user")) + tsa_mitigation = TSA_MITIGATION_USER_KERNEL; + else if (!strcmp(str, "vm")) + tsa_mitigation = TSA_MITIGATION_VM; + else + pr_err("Ignoring unknown tsa=%s option.\n", str); + + return 0; +} +early_param("tsa", tsa_parse_cmdline); + +static void __init tsa_select_mitigation(void) +{ + if (!boot_cpu_has_bug(X86_BUG_TSA)) { + tsa_mitigation = TSA_MITIGATION_NONE; + return; + } + + if (tsa_mitigation == TSA_MITIGATION_AUTO) { + bool vm = false, uk = false; + + tsa_mitigation = TSA_MITIGATION_NONE; + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL) || + cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER)) { + tsa_mitigation = TSA_MITIGATION_USER_KERNEL; + uk = true; + } + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST)) { + tsa_mitigation = TSA_MITIGATION_VM; + vm = true; + } + + if (uk && vm) + tsa_mitigation = TSA_MITIGATION_FULL; + } + + if (tsa_mitigation == TSA_MITIGATION_NONE) return; - if (!boot_cpu_has(X86_FEATURE_RETPOLINE)) - setup_force_cpu_cap(X86_FEATURE_INDIRECT_THUNK_ITS); + if (!boot_cpu_has(X86_FEATURE_VERW_CLEAR)) + tsa_mitigation = TSA_MITIGATION_UCODE_NEEDED; - setup_force_cpu_cap(X86_FEATURE_RETHUNK); - set_return_thunk(its_return_thunk); + /* + * No need to set verw_clear_cpu_buf_mitigation_selected - it + * doesn't fit all cases here and it is not needed because this + * is the only VERW-based mitigation on AMD. + */ + pr_info("%s\n", tsa_strings[tsa_mitigation]); +} + +static void __init tsa_apply_mitigation(void) +{ + switch (tsa_mitigation) { + case TSA_MITIGATION_USER_KERNEL: + setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF); + break; + case TSA_MITIGATION_VM: + setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF_VM); + break; + case TSA_MITIGATION_FULL: + setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF); + setup_force_cpu_cap(X86_FEATURE_CLEAR_CPU_BUF_VM); + break; + default: + break; + } } #undef pr_fmt @@ -1609,7 +1887,7 @@ static enum spectre_v2_user_cmd __init spectre_v2_parse_user_cmdline(void) char arg[20]; int ret, i; - if (cpu_mitigations_off() || !IS_ENABLED(CONFIG_MITIGATION_SPECTRE_V2)) + if (!IS_ENABLED(CONFIG_MITIGATION_SPECTRE_V2)) return SPECTRE_V2_USER_CMD_NONE; ret = cmdline_find_option(boot_command_line, "spectre_v2_user", @@ -1647,6 +1925,13 @@ static void __init spectre_v2_user_select_mitigation(void) spectre_v2_user_stibp = SPECTRE_V2_USER_STRICT; break; case SPECTRE_V2_USER_CMD_AUTO: + if (!should_mitigate_vuln(X86_BUG_SPECTRE_V2_USER)) + break; + spectre_v2_user_ibpb = SPECTRE_V2_USER_PRCTL; + if (smt_mitigations == SMT_MITIGATIONS_OFF) + break; + spectre_v2_user_stibp = SPECTRE_V2_USER_PRCTL; + break; case SPECTRE_V2_USER_CMD_PRCTL: spectre_v2_user_ibpb = SPECTRE_V2_USER_PRCTL; spectre_v2_user_stibp = SPECTRE_V2_USER_PRCTL; @@ -1798,8 +2083,7 @@ static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void) int ret, i; cmd = IS_ENABLED(CONFIG_MITIGATION_SPECTRE_V2) ? SPECTRE_V2_CMD_AUTO : SPECTRE_V2_CMD_NONE; - if (cmdline_find_option_bool(boot_command_line, "nospectre_v2") || - cpu_mitigations_off()) + if (cmdline_find_option_bool(boot_command_line, "nospectre_v2")) return SPECTRE_V2_CMD_NONE; ret = cmdline_find_option(boot_command_line, "spectre_v2", arg, sizeof(arg)); @@ -2002,11 +2286,20 @@ early_param("spectre_bhi", spectre_bhi_parse_cmdline); static void __init bhi_select_mitigation(void) { - if (!boot_cpu_has(X86_BUG_BHI) || cpu_mitigations_off()) + if (!boot_cpu_has(X86_BUG_BHI)) bhi_mitigation = BHI_MITIGATION_OFF; - if (bhi_mitigation == BHI_MITIGATION_AUTO) - bhi_mitigation = BHI_MITIGATION_ON; + if (bhi_mitigation != BHI_MITIGATION_AUTO) + return; + + if (cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST)) { + if (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL)) + bhi_mitigation = BHI_MITIGATION_ON; + else + bhi_mitigation = BHI_MITIGATION_VMEXIT_ONLY; + } else { + bhi_mitigation = BHI_MITIGATION_OFF; + } } static void __init bhi_update_mitigation(void) @@ -2062,8 +2355,11 @@ static void __init spectre_v2_select_mitigation(void) case SPECTRE_V2_CMD_NONE: return; - case SPECTRE_V2_CMD_FORCE: case SPECTRE_V2_CMD_AUTO: + if (!should_mitigate_vuln(X86_BUG_SPECTRE_V2)) + break; + fallthrough; + case SPECTRE_V2_CMD_FORCE: if (boot_cpu_has(X86_FEATURE_IBRS_ENHANCED)) { spectre_v2_enabled = SPECTRE_V2_EIBRS; break; @@ -2117,7 +2413,7 @@ static void __init spectre_v2_update_mitigation(void) } } - if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) && !cpu_mitigations_off()) + if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) pr_info("%s\n", spectre_v2_strings[spectre_v2_enabled]); } @@ -2249,76 +2545,13 @@ static void update_mds_branch_idle(void) return; if (sched_smt_active()) { - static_branch_enable(&mds_idle_clear); + static_branch_enable(&cpu_buf_idle_clear); } else if (mmio_mitigation == MMIO_MITIGATION_OFF || (x86_arch_cap_msr & ARCH_CAP_FBSDP_NO)) { - static_branch_disable(&mds_idle_clear); + static_branch_disable(&cpu_buf_idle_clear); } } -#define MDS_MSG_SMT "MDS CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html for more details.\n" -#define TAA_MSG_SMT "TAA CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/tsx_async_abort.html for more details.\n" -#define MMIO_MSG_SMT "MMIO Stale Data CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/processor_mmio_stale_data.html for more details.\n" - -void cpu_bugs_smt_update(void) -{ - mutex_lock(&spec_ctrl_mutex); - - if (sched_smt_active() && unprivileged_ebpf_enabled() && - spectre_v2_enabled == SPECTRE_V2_EIBRS_LFENCE) - pr_warn_once(SPECTRE_V2_EIBRS_LFENCE_EBPF_SMT_MSG); - - switch (spectre_v2_user_stibp) { - case SPECTRE_V2_USER_NONE: - break; - case SPECTRE_V2_USER_STRICT: - case SPECTRE_V2_USER_STRICT_PREFERRED: - update_stibp_strict(); - break; - case SPECTRE_V2_USER_PRCTL: - case SPECTRE_V2_USER_SECCOMP: - update_indir_branch_cond(); - break; - } - - switch (mds_mitigation) { - case MDS_MITIGATION_FULL: - case MDS_MITIGATION_AUTO: - case MDS_MITIGATION_VMWERV: - if (sched_smt_active() && !boot_cpu_has(X86_BUG_MSBDS_ONLY)) - pr_warn_once(MDS_MSG_SMT); - update_mds_branch_idle(); - break; - case MDS_MITIGATION_OFF: - break; - } - - switch (taa_mitigation) { - case TAA_MITIGATION_VERW: - case TAA_MITIGATION_AUTO: - case TAA_MITIGATION_UCODE_NEEDED: - if (sched_smt_active()) - pr_warn_once(TAA_MSG_SMT); - break; - case TAA_MITIGATION_TSX_DISABLED: - case TAA_MITIGATION_OFF: - break; - } - - switch (mmio_mitigation) { - case MMIO_MITIGATION_VERW: - case MMIO_MITIGATION_AUTO: - case MMIO_MITIGATION_UCODE_NEEDED: - if (sched_smt_active()) - pr_warn_once(MMIO_MSG_SMT); - break; - case MMIO_MITIGATION_OFF: - break; - } - - mutex_unlock(&spec_ctrl_mutex); -} - #undef pr_fmt #define pr_fmt(fmt) "Speculative Store Bypass: " fmt @@ -2413,6 +2646,11 @@ static void __init ssb_select_mitigation(void) ssb_mode = SPEC_STORE_BYPASS_DISABLE; break; case SPEC_STORE_BYPASS_CMD_AUTO: + if (should_mitigate_vuln(X86_BUG_SPEC_STORE_BYPASS)) + ssb_mode = SPEC_STORE_BYPASS_PRCTL; + else + ssb_mode = SPEC_STORE_BYPASS_NONE; + break; case SPEC_STORE_BYPASS_CMD_PRCTL: ssb_mode = SPEC_STORE_BYPASS_PRCTL; break; @@ -2750,17 +2988,23 @@ static void override_cache_bits(struct cpuinfo_x86 *c) static void __init l1tf_select_mitigation(void) { - if (!boot_cpu_has_bug(X86_BUG_L1TF) || cpu_mitigations_off()) { + if (!boot_cpu_has_bug(X86_BUG_L1TF)) { l1tf_mitigation = L1TF_MITIGATION_OFF; return; } - if (l1tf_mitigation == L1TF_MITIGATION_AUTO) { - if (cpu_mitigations_auto_nosmt()) - l1tf_mitigation = L1TF_MITIGATION_FLUSH_NOSMT; - else - l1tf_mitigation = L1TF_MITIGATION_FLUSH; + if (l1tf_mitigation != L1TF_MITIGATION_AUTO) + return; + + if (!should_mitigate_vuln(X86_BUG_L1TF)) { + l1tf_mitigation = L1TF_MITIGATION_OFF; + return; } + + if (smt_mitigations == SMT_MITIGATIONS_ON) + l1tf_mitigation = L1TF_MITIGATION_FLUSH_NOSMT; + else + l1tf_mitigation = L1TF_MITIGATION_FLUSH; } static void __init l1tf_apply_mitigation(void) @@ -2834,31 +3078,18 @@ early_param("l1tf", l1tf_cmdline); #undef pr_fmt #define pr_fmt(fmt) "Speculative Return Stack Overflow: " fmt -enum srso_mitigation { - SRSO_MITIGATION_NONE, - SRSO_MITIGATION_AUTO, - SRSO_MITIGATION_UCODE_NEEDED, - SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED, - SRSO_MITIGATION_MICROCODE, - SRSO_MITIGATION_SAFE_RET, - SRSO_MITIGATION_IBPB, - SRSO_MITIGATION_IBPB_ON_VMEXIT, - SRSO_MITIGATION_BP_SPEC_REDUCE, -}; - static const char * const srso_strings[] = { [SRSO_MITIGATION_NONE] = "Vulnerable", [SRSO_MITIGATION_UCODE_NEEDED] = "Vulnerable: No microcode", [SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED] = "Vulnerable: Safe RET, no microcode", [SRSO_MITIGATION_MICROCODE] = "Vulnerable: Microcode, no safe RET", + [SRSO_MITIGATION_NOSMT] = "Mitigation: SMT disabled", [SRSO_MITIGATION_SAFE_RET] = "Mitigation: Safe RET", [SRSO_MITIGATION_IBPB] = "Mitigation: IBPB", [SRSO_MITIGATION_IBPB_ON_VMEXIT] = "Mitigation: IBPB on VMEXIT only", [SRSO_MITIGATION_BP_SPEC_REDUCE] = "Mitigation: Reduced Speculation" }; -static enum srso_mitigation srso_mitigation __ro_after_init = SRSO_MITIGATION_AUTO; - static int __init srso_parse_cmdline(char *str) { if (!str) @@ -2885,35 +3116,54 @@ early_param("spec_rstack_overflow", srso_parse_cmdline); static void __init srso_select_mitigation(void) { - bool has_microcode; - - if (!boot_cpu_has_bug(X86_BUG_SRSO) || cpu_mitigations_off()) + if (!boot_cpu_has_bug(X86_BUG_SRSO)) { srso_mitigation = SRSO_MITIGATION_NONE; - - if (srso_mitigation == SRSO_MITIGATION_NONE) return; + } - if (srso_mitigation == SRSO_MITIGATION_AUTO) - srso_mitigation = SRSO_MITIGATION_SAFE_RET; - - has_microcode = boot_cpu_has(X86_FEATURE_IBPB_BRTYPE); - if (has_microcode) { + if (srso_mitigation == SRSO_MITIGATION_AUTO) { /* - * Zen1/2 with SMT off aren't vulnerable after the right - * IBPB microcode has been applied. + * Use safe-RET if user->kernel or guest->host protection is + * required. Otherwise the 'microcode' mitigation is sufficient + * to protect the user->user and guest->guest vectors. */ - if (boot_cpu_data.x86 < 0x19 && !cpu_smt_possible()) { - setup_force_cpu_cap(X86_FEATURE_SRSO_NO); + if (cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_HOST) || + (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL) && + !boot_cpu_has(X86_FEATURE_SRSO_USER_KERNEL_NO))) { + srso_mitigation = SRSO_MITIGATION_SAFE_RET; + } else if (cpu_attack_vector_mitigated(CPU_MITIGATE_USER_USER) || + cpu_attack_vector_mitigated(CPU_MITIGATE_GUEST_GUEST)) { + srso_mitigation = SRSO_MITIGATION_MICROCODE; + } else { srso_mitigation = SRSO_MITIGATION_NONE; return; } - } else { + } + + /* Zen1/2 with SMT off aren't vulnerable to SRSO. */ + if (boot_cpu_data.x86 < 0x19 && !cpu_smt_possible()) { + srso_mitigation = SRSO_MITIGATION_NOSMT; + return; + } + + if (!boot_cpu_has(X86_FEATURE_IBPB_BRTYPE)) { pr_warn("IBPB-extending microcode not applied!\n"); pr_warn(SRSO_NOTICE); + + /* + * Safe-RET provides partial mitigation without microcode, but + * other mitigations require microcode to provide any + * mitigations. + */ + if (srso_mitigation == SRSO_MITIGATION_SAFE_RET) + srso_mitigation = SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED; + else + srso_mitigation = SRSO_MITIGATION_UCODE_NEEDED; } switch (srso_mitigation) { case SRSO_MITIGATION_SAFE_RET: + case SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED: if (boot_cpu_has(X86_FEATURE_SRSO_USER_KERNEL_NO)) { srso_mitigation = SRSO_MITIGATION_IBPB_ON_VMEXIT; goto ibpb_on_vmexit; @@ -2923,9 +3173,6 @@ static void __init srso_select_mitigation(void) pr_err("WARNING: kernel not compiled with MITIGATION_SRSO.\n"); srso_mitigation = SRSO_MITIGATION_NONE; } - - if (!has_microcode) - srso_mitigation = SRSO_MITIGATION_SAFE_RET_UCODE_NEEDED; break; ibpb_on_vmexit: case SRSO_MITIGATION_IBPB_ON_VMEXIT: @@ -2940,9 +3187,6 @@ static void __init srso_select_mitigation(void) pr_err("WARNING: kernel not compiled with MITIGATION_IBPB_ENTRY.\n"); srso_mitigation = SRSO_MITIGATION_NONE; } - - if (!has_microcode) - srso_mitigation = SRSO_MITIGATION_UCODE_NEEDED; break; default: break; @@ -2957,8 +3201,7 @@ static void __init srso_update_mitigation(void) srso_mitigation = SRSO_MITIGATION_IBPB; if (boot_cpu_has_bug(X86_BUG_SRSO) && - !cpu_mitigations_off() && - !boot_cpu_has(X86_FEATURE_SRSO_NO)) + !cpu_mitigations_off()) pr_info("%s\n", srso_strings[srso_mitigation]); } @@ -3019,9 +3262,185 @@ static void __init srso_apply_mitigation(void) } } +#undef pr_fmt +#define pr_fmt(fmt) "VMSCAPE: " fmt + +enum vmscape_mitigations { + VMSCAPE_MITIGATION_NONE, + VMSCAPE_MITIGATION_AUTO, + VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER, + VMSCAPE_MITIGATION_IBPB_ON_VMEXIT, +}; + +static const char * const vmscape_strings[] = { + [VMSCAPE_MITIGATION_NONE] = "Vulnerable", + /* [VMSCAPE_MITIGATION_AUTO] */ + [VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER] = "Mitigation: IBPB before exit to userspace", + [VMSCAPE_MITIGATION_IBPB_ON_VMEXIT] = "Mitigation: IBPB on VMEXIT", +}; + +static enum vmscape_mitigations vmscape_mitigation __ro_after_init = + IS_ENABLED(CONFIG_MITIGATION_VMSCAPE) ? VMSCAPE_MITIGATION_AUTO : VMSCAPE_MITIGATION_NONE; + +static int __init vmscape_parse_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (!strcmp(str, "off")) { + vmscape_mitigation = VMSCAPE_MITIGATION_NONE; + } else if (!strcmp(str, "ibpb")) { + vmscape_mitigation = VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER; + } else if (!strcmp(str, "force")) { + setup_force_cpu_bug(X86_BUG_VMSCAPE); + vmscape_mitigation = VMSCAPE_MITIGATION_AUTO; + } else { + pr_err("Ignoring unknown vmscape=%s option.\n", str); + } + + return 0; +} +early_param("vmscape", vmscape_parse_cmdline); + +static void __init vmscape_select_mitigation(void) +{ + if (cpu_mitigations_off() || + !boot_cpu_has_bug(X86_BUG_VMSCAPE) || + !boot_cpu_has(X86_FEATURE_IBPB)) { + vmscape_mitigation = VMSCAPE_MITIGATION_NONE; + return; + } + + if (vmscape_mitigation == VMSCAPE_MITIGATION_AUTO) + vmscape_mitigation = VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER; +} + +static void __init vmscape_update_mitigation(void) +{ + if (!boot_cpu_has_bug(X86_BUG_VMSCAPE)) + return; + + if (retbleed_mitigation == RETBLEED_MITIGATION_IBPB || + srso_mitigation == SRSO_MITIGATION_IBPB_ON_VMEXIT) + vmscape_mitigation = VMSCAPE_MITIGATION_IBPB_ON_VMEXIT; + + pr_info("%s\n", vmscape_strings[vmscape_mitigation]); +} + +static void __init vmscape_apply_mitigation(void) +{ + if (vmscape_mitigation == VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER) + setup_force_cpu_cap(X86_FEATURE_IBPB_EXIT_TO_USER); +} + #undef pr_fmt #define pr_fmt(fmt) fmt +#define MDS_MSG_SMT "MDS CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html for more details.\n" +#define TAA_MSG_SMT "TAA CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/tsx_async_abort.html for more details.\n" +#define MMIO_MSG_SMT "MMIO Stale Data CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/processor_mmio_stale_data.html for more details.\n" +#define VMSCAPE_MSG_SMT "VMSCAPE: SMT on, STIBP is required for full protection. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/vmscape.html for more details.\n" + +void cpu_bugs_smt_update(void) +{ + mutex_lock(&spec_ctrl_mutex); + + if (sched_smt_active() && unprivileged_ebpf_enabled() && + spectre_v2_enabled == SPECTRE_V2_EIBRS_LFENCE) + pr_warn_once(SPECTRE_V2_EIBRS_LFENCE_EBPF_SMT_MSG); + + switch (spectre_v2_user_stibp) { + case SPECTRE_V2_USER_NONE: + break; + case SPECTRE_V2_USER_STRICT: + case SPECTRE_V2_USER_STRICT_PREFERRED: + update_stibp_strict(); + break; + case SPECTRE_V2_USER_PRCTL: + case SPECTRE_V2_USER_SECCOMP: + update_indir_branch_cond(); + break; + } + + switch (mds_mitigation) { + case MDS_MITIGATION_FULL: + case MDS_MITIGATION_AUTO: + case MDS_MITIGATION_VMWERV: + if (sched_smt_active() && !boot_cpu_has(X86_BUG_MSBDS_ONLY)) + pr_warn_once(MDS_MSG_SMT); + update_mds_branch_idle(); + break; + case MDS_MITIGATION_OFF: + break; + } + + switch (taa_mitigation) { + case TAA_MITIGATION_VERW: + case TAA_MITIGATION_AUTO: + case TAA_MITIGATION_UCODE_NEEDED: + if (sched_smt_active()) + pr_warn_once(TAA_MSG_SMT); + break; + case TAA_MITIGATION_TSX_DISABLED: + case TAA_MITIGATION_OFF: + break; + } + + switch (mmio_mitigation) { + case MMIO_MITIGATION_VERW: + case MMIO_MITIGATION_AUTO: + case MMIO_MITIGATION_UCODE_NEEDED: + if (sched_smt_active()) + pr_warn_once(MMIO_MSG_SMT); + break; + case MMIO_MITIGATION_OFF: + break; + } + + switch (tsa_mitigation) { + case TSA_MITIGATION_USER_KERNEL: + case TSA_MITIGATION_VM: + case TSA_MITIGATION_AUTO: + case TSA_MITIGATION_FULL: + /* + * TSA-SQ can potentially lead to info leakage between + * SMT threads. + */ + if (sched_smt_active()) + static_branch_enable(&cpu_buf_idle_clear); + else + static_branch_disable(&cpu_buf_idle_clear); + break; + case TSA_MITIGATION_NONE: + case TSA_MITIGATION_UCODE_NEEDED: + break; + } + + switch (vmscape_mitigation) { + case VMSCAPE_MITIGATION_NONE: + case VMSCAPE_MITIGATION_AUTO: + break; + case VMSCAPE_MITIGATION_IBPB_ON_VMEXIT: + case VMSCAPE_MITIGATION_IBPB_EXIT_TO_USER: + /* + * Hypervisors can be attacked across-threads, warn for SMT when + * STIBP is not already enabled system-wide. + * + * Intel eIBRS (!AUTOIBRS) implies STIBP on. + */ + if (!sched_smt_active() || + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED || + (spectre_v2_in_eibrs_mode(spectre_v2_enabled) && + !boot_cpu_has(X86_FEATURE_AUTOIBRS))) + break; + pr_warn_once(VMSCAPE_MSG_SMT); + break; + } + + mutex_unlock(&spec_ctrl_mutex); +} + #ifdef CONFIG_SYSFS #define L1TF_DEFAULT_MSG "Mitigation: PTE Inversion" @@ -3254,9 +3673,6 @@ static ssize_t retbleed_show_state(char *buf) static ssize_t srso_show_state(char *buf) { - if (boot_cpu_has(X86_FEATURE_SRSO_NO)) - return sysfs_emit(buf, "Mitigation: SMT disabled\n"); - return sysfs_emit(buf, "%s\n", srso_strings[srso_mitigation]); } @@ -3265,6 +3681,16 @@ static ssize_t gds_show_state(char *buf) return sysfs_emit(buf, "%s\n", gds_strings[gds_mitigation]); } +static ssize_t tsa_show_state(char *buf) +{ + return sysfs_emit(buf, "%s\n", tsa_strings[tsa_mitigation]); +} + +static ssize_t vmscape_show_state(char *buf) +{ + return sysfs_emit(buf, "%s\n", vmscape_strings[vmscape_mitigation]); +} + static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr, char *buf, unsigned int bug) { @@ -3328,6 +3754,12 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr case X86_BUG_ITS: return its_show_state(buf); + case X86_BUG_TSA: + return tsa_show_state(buf); + + case X86_BUG_VMSCAPE: + return vmscape_show_state(buf); + default: break; } @@ -3414,6 +3846,16 @@ ssize_t cpu_show_indirect_target_selection(struct device *dev, struct device_att { return cpu_show_common(dev, attr, buf, X86_BUG_ITS); } + +ssize_t cpu_show_tsa(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_TSA); +} + +ssize_t cpu_show_vmscape(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_VMSCAPE); +} #endif void __warn_thunk(void) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 8feb8fd2957a..f98ec9c7fc07 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1233,55 +1234,73 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { #define ITS BIT(8) /* CPU is affected by Indirect Target Selection, but guest-host isolation is not affected */ #define ITS_NATIVE_ONLY BIT(9) +/* CPU is affected by Transient Scheduler Attacks */ +#define TSA BIT(10) +/* CPU is affected by VMSCAPE */ +#define VMSCAPE BIT(11) static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { - VULNBL_INTEL_STEPS(INTEL_IVYBRIDGE, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_HASWELL, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_HASWELL_L, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_HASWELL_G, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_HASWELL_X, X86_STEP_MAX, MMIO), - VULNBL_INTEL_STEPS(INTEL_BROADWELL_D, X86_STEP_MAX, MMIO), - VULNBL_INTEL_STEPS(INTEL_BROADWELL_G, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_BROADWELL_X, X86_STEP_MAX, MMIO), - VULNBL_INTEL_STEPS(INTEL_BROADWELL, X86_STEP_MAX, SRBDS), - VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, 0x5, MMIO | RETBLEED | GDS), - VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, X86_STEP_MAX, MMIO | RETBLEED | GDS | ITS), - VULNBL_INTEL_STEPS(INTEL_SKYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), - VULNBL_INTEL_STEPS(INTEL_SKYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), - VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, 0xb, MMIO | RETBLEED | GDS | SRBDS), - VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS), - VULNBL_INTEL_STEPS(INTEL_KABYLAKE, 0xc, MMIO | RETBLEED | GDS | SRBDS), - VULNBL_INTEL_STEPS(INTEL_KABYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS), - VULNBL_INTEL_STEPS(INTEL_CANNONLAKE_L, X86_STEP_MAX, RETBLEED), + VULNBL_INTEL_STEPS(INTEL_SANDYBRIDGE_X, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SANDYBRIDGE, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_IVYBRIDGE_X, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_IVYBRIDGE, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_HASWELL, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_HASWELL_L, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_HASWELL_G, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_HASWELL_X, X86_STEP_MAX, MMIO | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_BROADWELL_D, X86_STEP_MAX, MMIO | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_BROADWELL_X, X86_STEP_MAX, MMIO | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_BROADWELL_G, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_BROADWELL, X86_STEP_MAX, SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, 0x5, MMIO | RETBLEED | GDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, X86_STEP_MAX, MMIO | RETBLEED | GDS | ITS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SKYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SKYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, 0xb, MMIO | RETBLEED | GDS | SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_KABYLAKE, 0xc, MMIO | RETBLEED | GDS | SRBDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_KABYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_CANNONLAKE_L, X86_STEP_MAX, RETBLEED | VMSCAPE), VULNBL_INTEL_STEPS(INTEL_ICELAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY), VULNBL_INTEL_STEPS(INTEL_ICELAKE_D, X86_STEP_MAX, MMIO | GDS | ITS | ITS_NATIVE_ONLY), VULNBL_INTEL_STEPS(INTEL_ICELAKE_X, X86_STEP_MAX, MMIO | GDS | ITS | ITS_NATIVE_ONLY), - VULNBL_INTEL_STEPS(INTEL_COMETLAKE, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS), - VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, 0x0, MMIO | RETBLEED | ITS), - VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS), + VULNBL_INTEL_STEPS(INTEL_COMETLAKE, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, 0x0, MMIO | RETBLEED | ITS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS | VMSCAPE), VULNBL_INTEL_STEPS(INTEL_TIGERLAKE_L, X86_STEP_MAX, GDS | ITS | ITS_NATIVE_ONLY), VULNBL_INTEL_STEPS(INTEL_TIGERLAKE, X86_STEP_MAX, GDS | ITS | ITS_NATIVE_ONLY), VULNBL_INTEL_STEPS(INTEL_LAKEFIELD, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED), VULNBL_INTEL_STEPS(INTEL_ROCKETLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY), - VULNBL_INTEL_TYPE(INTEL_ALDERLAKE, ATOM, RFDS), - VULNBL_INTEL_STEPS(INTEL_ALDERLAKE_L, X86_STEP_MAX, RFDS), - VULNBL_INTEL_TYPE(INTEL_RAPTORLAKE, ATOM, RFDS), - VULNBL_INTEL_STEPS(INTEL_RAPTORLAKE_P, X86_STEP_MAX, RFDS), - VULNBL_INTEL_STEPS(INTEL_RAPTORLAKE_S, X86_STEP_MAX, RFDS), - VULNBL_INTEL_STEPS(INTEL_ATOM_GRACEMONT, X86_STEP_MAX, RFDS), + VULNBL_INTEL_TYPE(INTEL_ALDERLAKE, ATOM, RFDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ALDERLAKE, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ALDERLAKE_L, X86_STEP_MAX, RFDS | VMSCAPE), + VULNBL_INTEL_TYPE(INTEL_RAPTORLAKE, ATOM, RFDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_RAPTORLAKE, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_RAPTORLAKE_P, X86_STEP_MAX, RFDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_RAPTORLAKE_S, X86_STEP_MAX, RFDS | VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_METEORLAKE_L, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ARROWLAKE_H, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ARROWLAKE, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ARROWLAKE_U, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_LUNARLAKE_M, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_SAPPHIRERAPIDS_X, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_GRANITERAPIDS_X, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_EMERALDRAPIDS_X, X86_STEP_MAX, VMSCAPE), + VULNBL_INTEL_STEPS(INTEL_ATOM_GRACEMONT, X86_STEP_MAX, RFDS | VMSCAPE), VULNBL_INTEL_STEPS(INTEL_ATOM_TREMONT, X86_STEP_MAX, MMIO | MMIO_SBDS | RFDS), VULNBL_INTEL_STEPS(INTEL_ATOM_TREMONT_D, X86_STEP_MAX, MMIO | RFDS), VULNBL_INTEL_STEPS(INTEL_ATOM_TREMONT_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RFDS), VULNBL_INTEL_STEPS(INTEL_ATOM_GOLDMONT, X86_STEP_MAX, RFDS), VULNBL_INTEL_STEPS(INTEL_ATOM_GOLDMONT_D, X86_STEP_MAX, RFDS), VULNBL_INTEL_STEPS(INTEL_ATOM_GOLDMONT_PLUS, X86_STEP_MAX, RFDS), + VULNBL_INTEL_STEPS(INTEL_ATOM_CRESTMONT_X, X86_STEP_MAX, VMSCAPE), VULNBL_AMD(0x15, RETBLEED), VULNBL_AMD(0x16, RETBLEED), - VULNBL_AMD(0x17, RETBLEED | SMT_RSB | SRSO), - VULNBL_HYGON(0x18, RETBLEED | SMT_RSB | SRSO), - VULNBL_AMD(0x19, SRSO), - VULNBL_AMD(0x1a, SRSO), + VULNBL_AMD(0x17, RETBLEED | SMT_RSB | SRSO | VMSCAPE), + VULNBL_HYGON(0x18, RETBLEED | SMT_RSB | SRSO | VMSCAPE), + VULNBL_AMD(0x19, SRSO | TSA | VMSCAPE), + VULNBL_AMD(0x1a, SRSO | VMSCAPE), {} }; @@ -1530,6 +1549,24 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) setup_force_cpu_bug(X86_BUG_ITS_NATIVE_ONLY); } + if (c->x86_vendor == X86_VENDOR_AMD) { + if (!cpu_has(c, X86_FEATURE_TSA_SQ_NO) || + !cpu_has(c, X86_FEATURE_TSA_L1_NO)) { + if (cpu_matches(cpu_vuln_blacklist, TSA) || + /* Enable bug on Zen guests to allow for live migration. */ + (cpu_has(c, X86_FEATURE_HYPERVISOR) && cpu_has(c, X86_FEATURE_ZEN))) + setup_force_cpu_bug(X86_BUG_TSA); + } + } + + /* + * Set the bug only on bare-metal. A nested hypervisor should already be + * deploying IBPB to isolate itself from nested guests. + */ + if (cpu_matches(cpu_vuln_blacklist, VMSCAPE) && + !boot_cpu_has(X86_FEATURE_HYPERVISOR)) + setup_force_cpu_bug(X86_BUG_VMSCAPE); + if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN)) return; @@ -2243,20 +2280,16 @@ EXPORT_PER_CPU_SYMBOL(__stack_chk_guard); #endif #endif -/* - * Clear all 6 debug registers: - */ -static void clear_all_debug_regs(void) +static void initialize_debug_regs(void) { - int i; - - for (i = 0; i < 8; i++) { - /* Ignore db4, db5 */ - if ((i == 4) || (i == 5)) - continue; - - set_debugreg(0, i); - } + /* Control register first -- to make sure everything is disabled. */ + set_debugreg(DR7_FIXED_1, 7); + set_debugreg(DR6_RESERVED, 6); + /* dr5 and dr4 don't exist */ + set_debugreg(0, 3); + set_debugreg(0, 2); + set_debugreg(0, 1); + set_debugreg(0, 0); } #ifdef CONFIG_KGDB @@ -2417,7 +2450,7 @@ void cpu_init(void) load_mm_ldt(&init_mm); - clear_all_debug_regs(); + initialize_debug_regs(); dbg_restore_debug_regs(); doublefault_init_cpu_tss(); @@ -2529,6 +2562,12 @@ void __init arch_cpu_finalize_init(void) fpu__init_system(); fpu__init_cpu(); + /* + * This needs to follow the FPU initializtion, since EFI depends on it. + */ + if (efi_enabled(EFI_RUNTIME_SERVICES)) + efi_enter_virtual_mode(); + /* * Ensure that access to the per CPU representation has the initial * boot CPU configuration. diff --git a/arch/x86/kernel/cpu/hygon.c b/arch/x86/kernel/cpu/hygon.c index 2154f12766fb..1fda6c3a2b65 100644 --- a/arch/x86/kernel/cpu/hygon.c +++ b/arch/x86/kernel/cpu/hygon.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "cpu.h" @@ -117,6 +118,8 @@ static void bsp_init_hygon(struct cpuinfo_x86 *c) x86_amd_ls_cfg_ssbd_mask = 1ULL << 10; } } + + resctrl_cpu_detect(c); } static void early_init_hygon(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 076eaa41b8c8..98ae4c37c93e 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -262,7 +262,7 @@ static void early_init_intel(struct cpuinfo_x86 *c) if (c->x86_power & (1 << 8)) { set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC); - } else if ((c->x86_vfm >= INTEL_P4_PRESCOTT && c->x86_vfm <= INTEL_P4_WILLAMETTE) || + } else if ((c->x86_vfm >= INTEL_P4_PRESCOTT && c->x86_vfm <= INTEL_P4_CEDARMILL) || (c->x86_vfm >= INTEL_CORE_YONAH && c->x86_vfm <= INTEL_IVYBRIDGE)) { set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); } diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index 9d852c3b2cb5..5c4eb28c3ac9 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -350,7 +350,6 @@ static void smca_configure(unsigned int bank, unsigned int cpu) struct thresh_restart { struct threshold_block *b; - int reset; int set_lvt_off; int lvt_off; u16 old_limit; @@ -432,13 +431,13 @@ static void threshold_restart_bank(void *_tr) rdmsr(tr->b->address, lo, hi); - if (tr->b->threshold_limit < (hi & THRESHOLD_MAX)) - tr->reset = 1; /* limit cannot be lower than err count */ - - if (tr->reset) { /* reset err count and overflow bit */ - hi = - (hi & ~(MASK_ERR_COUNT_HI | MASK_OVERFLOW_HI)) | - (THRESHOLD_MAX - tr->b->threshold_limit); + /* + * Reset error count and overflow bit. + * This is done during init or after handling an interrupt. + */ + if (hi & MASK_OVERFLOW_HI || tr->set_lvt_off) { + hi &= ~(MASK_ERR_COUNT_HI | MASK_OVERFLOW_HI); + hi |= THRESHOLD_MAX - tr->b->threshold_limit; } else if (tr->old_limit) { /* change limit w/o reset */ int new_count = (hi & THRESHOLD_MAX) + (tr->old_limit - tr->b->threshold_limit); @@ -1113,13 +1112,20 @@ static const char *get_name(unsigned int cpu, unsigned int bank, struct threshol } bank_type = smca_get_bank_type(cpu, bank); - if (bank_type >= N_SMCA_BANK_TYPES) - return NULL; if (b && (bank_type == SMCA_UMC || bank_type == SMCA_UMC_V2)) { if (b->block < ARRAY_SIZE(smca_umc_block_names)) return smca_umc_block_names[b->block]; - return NULL; + } + + if (b && b->block) { + snprintf(buf_mcatype, MAX_MCATYPE_NAME_LEN, "th_block_%u", b->block); + return buf_mcatype; + } + + if (bank_type >= N_SMCA_BANK_TYPES) { + snprintf(buf_mcatype, MAX_MCATYPE_NAME_LEN, "th_bank_%u", bank); + return buf_mcatype; } if (per_cpu(smca_bank_counts, cpu)[bank_type] == 1) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index e9b3c5d4a52e..4da4eab56c81 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1740,6 +1740,11 @@ static void mc_poll_banks_default(void) void (*mc_poll_banks)(void) = mc_poll_banks_default; +static bool should_enable_timer(unsigned long iv) +{ + return !mca_cfg.ignore_ce && iv; +} + static void mce_timer_fn(struct timer_list *t) { struct timer_list *cpu_t = this_cpu_ptr(&mce_timer); @@ -1763,7 +1768,7 @@ static void mce_timer_fn(struct timer_list *t) if (mce_get_storm_mode()) { __start_timer(t, HZ); - } else { + } else if (should_enable_timer(iv)) { __this_cpu_write(mce_next_interval, iv); __start_timer(t, iv); } @@ -2156,11 +2161,10 @@ static void mce_start_timer(struct timer_list *t) { unsigned long iv = check_interval * HZ; - if (mca_cfg.ignore_ce || !iv) - return; - - this_cpu_write(mce_next_interval, iv); - __start_timer(t, iv); + if (should_enable_timer(iv)) { + this_cpu_write(mce_next_interval, iv); + __start_timer(t, iv); + } } static void __mcheck_cpu_setup_timer(void) @@ -2801,15 +2805,9 @@ static int mce_cpu_dead(unsigned int cpu) static int mce_cpu_online(unsigned int cpu) { struct timer_list *t = this_cpu_ptr(&mce_timer); - int ret; mce_device_create(cpu); - - ret = mce_threshold_create_device(cpu); - if (ret) { - mce_device_remove(cpu); - return ret; - } + mce_threshold_create_device(cpu); mce_reenable_cpu(); mce_start_timer(t); return 0; diff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c index efcf21e9552e..9b149b9c4109 100644 --- a/arch/x86/kernel/cpu/mce/intel.c +++ b/arch/x86/kernel/cpu/mce/intel.c @@ -478,6 +478,7 @@ void mce_intel_feature_init(struct cpuinfo_x86 *c) void mce_intel_feature_clear(struct cpuinfo_x86 *c) { intel_clear_lmce(); + cmci_clear(); } bool intel_filter_mce(struct mce *m) diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 097e39327942..514f63340880 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -171,8 +171,28 @@ static int cmp_id(const void *key, const void *elem) return 1; } +static u32 cpuid_to_ucode_rev(unsigned int val) +{ + union zen_patch_rev p = {}; + union cpuid_1_eax c; + + c.full = val; + + p.stepping = c.stepping; + p.model = c.model; + p.ext_model = c.ext_model; + p.ext_fam = c.ext_fam; + + return p.ucode_rev; +} + static bool need_sha_check(u32 cur_rev) { + if (!cur_rev) { + cur_rev = cpuid_to_ucode_rev(bsp_cpuid_1_eax); + pr_info_once("No current revision, generating the lowest one: 0x%x\n", cur_rev); + } + switch (cur_rev >> 8) { case 0x80012: return cur_rev <= 0x800126f; break; case 0x80082: return cur_rev <= 0x800820f; break; @@ -749,8 +769,6 @@ static struct ucode_patch *cache_find_patch(struct ucode_cpu_info *uci, u16 equi n.equiv_cpu = equiv_cpu; n.patch_id = uci->cpu_sig.rev; - WARN_ON_ONCE(!n.patch_id); - list_for_each_entry(p, µcode_cache, plist) if (patch_cpus_equivalent(p, &n, false)) return p; diff --git a/arch/x86/kernel/cpu/microcode/amd_shas.c b/arch/x86/kernel/cpu/microcode/amd_shas.c index 2a1655b1fdd8..1fd349cfc802 100644 --- a/arch/x86/kernel/cpu/microcode/amd_shas.c +++ b/arch/x86/kernel/cpu/microcode/amd_shas.c @@ -231,6 +231,13 @@ static const struct patch_digest phashes[] = { 0x0d,0x5b,0x65,0x34,0x69,0xb2,0x62,0x21, } }, + { 0xa0011d7, { + 0x35,0x07,0xcd,0x40,0x94,0xbc,0x81,0x6b, + 0xfc,0x61,0x56,0x1a,0xe2,0xdb,0x96,0x12, + 0x1c,0x1c,0x31,0xb1,0x02,0x6f,0xe5,0xd2, + 0xfe,0x1b,0x04,0x03,0x2c,0x8f,0x4c,0x36, + } + }, { 0xa001223, { 0xfb,0x32,0x5f,0xc6,0x83,0x4f,0x8c,0xb8, 0xa4,0x05,0xf9,0x71,0x53,0x01,0x16,0xc4, @@ -294,6 +301,13 @@ static const struct patch_digest phashes[] = { 0xc0,0xcd,0x33,0xf2,0x8d,0xf9,0xef,0x59, } }, + { 0xa00123b, { + 0xef,0xa1,0x1e,0x71,0xf1,0xc3,0x2c,0xe2, + 0xc3,0xef,0x69,0x41,0x7a,0x54,0xca,0xc3, + 0x8f,0x62,0x84,0xee,0xc2,0x39,0xd9,0x28, + 0x95,0xa7,0x12,0x49,0x1e,0x30,0x71,0x72, + } + }, { 0xa00820c, { 0xa8,0x0c,0x81,0xc0,0xa6,0x00,0xe7,0xf3, 0x5f,0x65,0xd3,0xb9,0x6f,0xea,0x93,0x63, @@ -301,6 +315,13 @@ static const struct patch_digest phashes[] = { 0xe1,0x3b,0x8d,0xb2,0xf8,0x22,0x03,0xe2, } }, + { 0xa00820d, { + 0xf9,0x2a,0xc0,0xf4,0x9e,0xa4,0x87,0xa4, + 0x7d,0x87,0x00,0xfd,0xab,0xda,0x19,0xca, + 0x26,0x51,0x32,0xc1,0x57,0x91,0xdf,0xc1, + 0x05,0xeb,0x01,0x7c,0x5a,0x95,0x21,0xb7, + } + }, { 0xa10113e, { 0x05,0x3c,0x66,0xd7,0xa9,0x5a,0x33,0x10, 0x1b,0xf8,0x9c,0x8f,0xed,0xfc,0xa7,0xa0, @@ -322,6 +343,13 @@ static const struct patch_digest phashes[] = { 0xf1,0x5e,0xb0,0xde,0xb4,0x98,0xae,0xc4, } }, + { 0xa10114c, { + 0x9e,0xb6,0xa2,0xd9,0x87,0x38,0xc5,0x64, + 0xd8,0x88,0xfa,0x78,0x98,0xf9,0x6f,0x74, + 0x39,0x90,0x1b,0xa5,0xcf,0x5e,0xb4,0x2a, + 0x02,0xff,0xd4,0x8c,0x71,0x8b,0xe2,0xc0, + } + }, { 0xa10123e, { 0x03,0xb9,0x2c,0x76,0x48,0x93,0xc9,0x18, 0xfb,0x56,0xfd,0xf7,0xe2,0x1d,0xca,0x4d, @@ -343,6 +371,13 @@ static const struct patch_digest phashes[] = { 0x1b,0x7d,0x64,0x9d,0x4b,0x53,0x13,0x75, } }, + { 0xa10124c, { + 0x29,0xea,0xf1,0x2c,0xb2,0xe4,0xef,0x90, + 0xa4,0xcd,0x1d,0x86,0x97,0x17,0x61,0x46, + 0xfc,0x22,0xcb,0x57,0x75,0x19,0xc8,0xcc, + 0x0c,0xf5,0xbc,0xac,0x81,0x9d,0x9a,0xd2, + } + }, { 0xa108108, { 0xed,0xc2,0xec,0xa1,0x15,0xc6,0x65,0xe9, 0xd0,0xef,0x39,0xaa,0x7f,0x55,0x06,0xc6, @@ -350,6 +385,13 @@ static const struct patch_digest phashes[] = { 0x28,0x1e,0x9c,0x59,0x69,0x99,0x4d,0x16, } }, + { 0xa108109, { + 0x85,0xb4,0xbd,0x7c,0x49,0xa7,0xbd,0xfa, + 0x49,0x36,0x80,0x81,0xc5,0xb7,0x39,0x1b, + 0x9a,0xaa,0x50,0xde,0x9b,0xe9,0x32,0x35, + 0x42,0x7e,0x51,0x4f,0x52,0x2c,0x28,0x59, + } + }, { 0xa20102d, { 0xf9,0x6e,0xf2,0x32,0xd3,0x0f,0x5f,0x11, 0x59,0xa1,0xfe,0xcc,0xcd,0x9b,0x42,0x89, @@ -357,6 +399,13 @@ static const struct patch_digest phashes[] = { 0x8c,0xe9,0x19,0x3e,0xcc,0x3f,0x7b,0xb4, } }, + { 0xa20102e, { + 0xbe,0x1f,0x32,0x04,0x0d,0x3c,0x9c,0xdd, + 0xe1,0xa4,0xbf,0x76,0x3a,0xec,0xc2,0xf6, + 0x11,0x00,0xa7,0xaf,0x0f,0xe5,0x02,0xc5, + 0x54,0x3a,0x1f,0x8c,0x16,0xb5,0xff,0xbe, + } + }, { 0xa201210, { 0xe8,0x6d,0x51,0x6a,0x8e,0x72,0xf3,0xfe, 0x6e,0x16,0xbc,0x62,0x59,0x40,0x17,0xe9, @@ -364,6 +413,13 @@ static const struct patch_digest phashes[] = { 0xf7,0x55,0xf0,0x13,0xbb,0x22,0xf6,0x41, } }, + { 0xa201211, { + 0x69,0xa1,0x17,0xec,0xd0,0xf6,0x6c,0x95, + 0xe2,0x1e,0xc5,0x59,0x1a,0x52,0x0a,0x27, + 0xc4,0xed,0xd5,0x59,0x1f,0xbf,0x00,0xff, + 0x08,0x88,0xb5,0xe1,0x12,0xb6,0xcc,0x27, + } + }, { 0xa404107, { 0xbb,0x04,0x4e,0x47,0xdd,0x5e,0x26,0x45, 0x1a,0xc9,0x56,0x24,0xa4,0x4c,0x82,0xb0, @@ -371,6 +427,13 @@ static const struct patch_digest phashes[] = { 0x13,0xbc,0xc5,0x25,0xe4,0xc5,0xc3,0x99, } }, + { 0xa404108, { + 0x69,0x67,0x43,0x06,0xf8,0x0c,0x62,0xdc, + 0xa4,0x21,0x30,0x4f,0x0f,0x21,0x2c,0xcb, + 0xcc,0x37,0xf1,0x1c,0xc3,0xf8,0x2f,0x19, + 0xdf,0x53,0x53,0x46,0xb1,0x15,0xea,0x00, + } + }, { 0xa500011, { 0x23,0x3d,0x70,0x7d,0x03,0xc3,0xc4,0xf4, 0x2b,0x82,0xc6,0x05,0xda,0x80,0x0a,0xf1, @@ -378,6 +441,13 @@ static const struct patch_digest phashes[] = { 0x11,0x5e,0x96,0x7e,0x71,0xe9,0xfc,0x74, } }, + { 0xa500012, { + 0xeb,0x74,0x0d,0x47,0xa1,0x8e,0x09,0xe4, + 0x93,0x4c,0xad,0x03,0x32,0x4c,0x38,0x16, + 0x10,0x39,0xdd,0x06,0xaa,0xce,0xd6,0x0f, + 0x62,0x83,0x9d,0x8e,0x64,0x55,0xbe,0x63, + } + }, { 0xa601209, { 0x66,0x48,0xd4,0x09,0x05,0xcb,0x29,0x32, 0x66,0xb7,0x9a,0x76,0xcd,0x11,0xf3,0x30, @@ -385,6 +455,13 @@ static const struct patch_digest phashes[] = { 0xe8,0x73,0xe2,0xd6,0xdb,0xd2,0x77,0x1d, } }, + { 0xa60120a, { + 0x0c,0x8b,0x3d,0xfd,0x52,0x52,0x85,0x7d, + 0x20,0x3a,0xe1,0x7e,0xa4,0x21,0x3b,0x7b, + 0x17,0x86,0xae,0xac,0x13,0xb8,0x63,0x9d, + 0x06,0x01,0xd0,0xa0,0x51,0x9a,0x91,0x2c, + } + }, { 0xa704107, { 0xf3,0xc6,0x58,0x26,0xee,0xac,0x3f,0xd6, 0xce,0xa1,0x72,0x47,0x3b,0xba,0x2b,0x93, @@ -392,6 +469,13 @@ static const struct patch_digest phashes[] = { 0x64,0x39,0x71,0x8c,0xce,0xe7,0x41,0x39, } }, + { 0xa704108, { + 0xd7,0x55,0x15,0x2b,0xfe,0xc4,0xbc,0x93, + 0xec,0x91,0xa0,0xae,0x45,0xb7,0xc3,0x98, + 0x4e,0xff,0x61,0x77,0x88,0xc2,0x70,0x49, + 0xe0,0x3a,0x1d,0x84,0x38,0x52,0xbf,0x5a, + } + }, { 0xa705206, { 0x8d,0xc0,0x76,0xbd,0x58,0x9f,0x8f,0xa4, 0x12,0x9d,0x21,0xfb,0x48,0x21,0xbc,0xe7, @@ -399,6 +483,13 @@ static const struct patch_digest phashes[] = { 0x03,0x35,0xe9,0xbe,0xfb,0x06,0xdf,0xfc, } }, + { 0xa705208, { + 0x30,0x1d,0x55,0x24,0xbc,0x6b,0x5a,0x19, + 0x0c,0x7d,0x1d,0x74,0xaa,0xd1,0xeb,0xd2, + 0x16,0x62,0xf7,0x5b,0xe1,0x1f,0x18,0x11, + 0x5c,0xf0,0x94,0x90,0x26,0xec,0x69,0xff, + } + }, { 0xa708007, { 0x6b,0x76,0xcc,0x78,0xc5,0x8a,0xa3,0xe3, 0x32,0x2d,0x79,0xe4,0xc3,0x80,0xdb,0xb2, @@ -406,6 +497,13 @@ static const struct patch_digest phashes[] = { 0xdf,0x92,0x73,0x84,0x87,0x3c,0x73,0x93, } }, + { 0xa708008, { + 0x08,0x6e,0xf0,0x22,0x4b,0x8e,0xc4,0x46, + 0x58,0x34,0xe6,0x47,0xa2,0x28,0xfd,0xab, + 0x22,0x3d,0xdd,0xd8,0x52,0x9e,0x1d,0x16, + 0xfa,0x01,0x68,0x14,0x79,0x3e,0xe8,0x6b, + } + }, { 0xa70c005, { 0x88,0x5d,0xfb,0x79,0x64,0xd8,0x46,0x3b, 0x4a,0x83,0x8e,0x77,0x7e,0xcf,0xb3,0x0f, @@ -413,6 +511,13 @@ static const struct patch_digest phashes[] = { 0xee,0x49,0xac,0xe1,0x8b,0x13,0xc5,0x13, } }, + { 0xa70c008, { + 0x0f,0xdb,0x37,0xa1,0x10,0xaf,0xd4,0x21, + 0x94,0x0d,0xa4,0xa2,0xe9,0x86,0x6c,0x0e, + 0x85,0x7c,0x36,0x30,0xa3,0x3a,0x78,0x66, + 0x18,0x10,0x60,0x0d,0x78,0x3d,0x44,0xd0, + } + }, { 0xaa00116, { 0xe8,0x4c,0x2c,0x88,0xa1,0xac,0x24,0x63, 0x65,0xe5,0xaa,0x2d,0x16,0xa9,0xc3,0xf5, @@ -441,4 +546,11 @@ static const struct patch_digest phashes[] = { 0x68,0x2f,0x46,0xee,0xfe,0xc6,0x6d,0xef, } }, + { 0xaa00216, { + 0x79,0xfb,0x5b,0x9f,0xb6,0xe6,0xa8,0xf5, + 0x4e,0x7c,0x4f,0x8e,0x1d,0xad,0xd0,0x08, + 0xc2,0x43,0x7c,0x8b,0xe6,0xdb,0xd0,0xd2, + 0xe8,0x39,0x26,0xc1,0xe5,0x5a,0x48,0xf1, + } + }, }; diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index fe50eb5b7c4a..b92e09a87c69 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -17,8 +17,8 @@ #define pr_fmt(fmt) "microcode: " fmt -#include #include +#include #include #include #include @@ -249,7 +249,7 @@ static void reload_early_microcode(unsigned int cpu) } /* fake device for request_firmware */ -static struct platform_device *microcode_pdev; +static struct faux_device *microcode_fdev; #ifdef CONFIG_MICROCODE_LATE_LOADING /* @@ -690,7 +690,7 @@ static int load_late_locked(void) if (!setup_cpus()) return -EBUSY; - switch (microcode_ops->request_microcode_fw(0, µcode_pdev->dev)) { + switch (microcode_ops->request_microcode_fw(0, µcode_fdev->dev)) { case UCODE_NEW: return load_late_stop_cpus(false); case UCODE_NEW_SAFE: @@ -841,9 +841,9 @@ static int __init microcode_init(void) if (early_data.new_rev) pr_info_once("Updated early from: 0x%08x\n", early_data.old_rev); - microcode_pdev = platform_device_register_simple("microcode", -1, NULL, 0); - if (IS_ERR(microcode_pdev)) - return PTR_ERR(microcode_pdev); + microcode_fdev = faux_device_create("microcode", NULL, NULL); + if (!microcode_fdev) + return -ENODEV; dev_root = bus_get_dev_root(&cpu_subsys); if (dev_root) { @@ -862,7 +862,7 @@ static int __init microcode_init(void) return 0; out_pdev: - platform_device_unregister(microcode_pdev); + faux_device_destroy(microcode_fdev); return error; } diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c index 7109cbfcad4f..187d527ef73b 100644 --- a/arch/x86/kernel/cpu/resctrl/core.c +++ b/arch/x86/kernel/cpu/resctrl/core.c @@ -498,6 +498,7 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) struct rdt_hw_mon_domain *hw_dom; struct rdt_domain_hdr *hdr; struct rdt_mon_domain *d; + struct cacheinfo *ci; int err; lockdep_assert_held(&domain_list_lock); @@ -525,12 +526,13 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) d = &hw_dom->d_resctrl; d->hdr.id = id; d->hdr.type = RESCTRL_MON_DOMAIN; - d->ci = get_cpu_cacheinfo_level(cpu, RESCTRL_L3_CACHE); - if (!d->ci) { + ci = get_cpu_cacheinfo_level(cpu, RESCTRL_L3_CACHE); + if (!ci) { pr_warn_once("Can't find L3 cache for CPU:%d resource %s\n", cpu, r->name); mon_domain_free(hw_dom); return; } + d->ci_id = ci->id; cpumask_set_cpu(cpu, &d->hdr.cpu_mask); arch_mon_domain_online(r, d); diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c index dbf6d71bdf18..6b868afb26c3 100644 --- a/arch/x86/kernel/cpu/scattered.c +++ b/arch/x86/kernel/cpu/scattered.c @@ -48,8 +48,11 @@ static const struct cpuid_bit cpuid_bits[] = { { X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 }, { X86_FEATURE_AMD_FAST_CPPC, CPUID_EDX, 15, 0x80000007, 0 }, { X86_FEATURE_MBA, CPUID_EBX, 6, 0x80000008, 0 }, + { X86_FEATURE_COHERENCY_SFW_NO, CPUID_EBX, 31, 0x8000001f, 0 }, { X86_FEATURE_SMBA, CPUID_EBX, 2, 0x80000020, 0 }, { X86_FEATURE_BMEC, CPUID_EBX, 3, 0x80000020, 0 }, + { X86_FEATURE_TSA_SQ_NO, CPUID_ECX, 1, 0x80000021, 0 }, + { X86_FEATURE_TSA_L1_NO, CPUID_ECX, 2, 0x80000021, 0 }, { X86_FEATURE_AMD_WORKLOAD_CLASS, CPUID_EAX, 22, 0x80000021, 0 }, { X86_FEATURE_PERFMON_V2, CPUID_EAX, 0, 0x80000022, 0 }, { X86_FEATURE_AMD_LBR_V2, CPUID_EAX, 1, 0x80000022, 0 }, diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c index 279148e72459..308dbbae6c6e 100644 --- a/arch/x86/kernel/cpu/sgx/encl.c +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -279,7 +279,7 @@ static struct sgx_encl_page *__sgx_encl_load_page(struct sgx_encl *encl, static struct sgx_encl_page *sgx_encl_load_page_in_vma(struct sgx_encl *encl, unsigned long addr, - unsigned long vm_flags) + vm_flags_t vm_flags) { unsigned long vm_prot_bits = vm_flags & VM_ACCESS_FLAGS; struct sgx_encl_page *entry; @@ -520,9 +520,9 @@ static void sgx_vma_open(struct vm_area_struct *vma) * Return: 0 on success, -EACCES otherwise */ int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start, - unsigned long end, unsigned long vm_flags) + unsigned long end, vm_flags_t vm_flags) { - unsigned long vm_prot_bits = vm_flags & VM_ACCESS_FLAGS; + vm_flags_t vm_prot_bits = vm_flags & VM_ACCESS_FLAGS; struct sgx_encl_page *page; unsigned long count = 0; int ret = 0; @@ -605,7 +605,7 @@ static int sgx_encl_debug_write(struct sgx_encl *encl, struct sgx_encl_page *pag */ static struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl, unsigned long addr, - unsigned long vm_flags) + vm_flags_t vm_flags) { struct sgx_encl_page *entry; diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h index f94ff14c9486..8ff47f6652b9 100644 --- a/arch/x86/kernel/cpu/sgx/encl.h +++ b/arch/x86/kernel/cpu/sgx/encl.h @@ -101,7 +101,7 @@ static inline int sgx_encl_find(struct mm_struct *mm, unsigned long addr, } int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start, - unsigned long end, unsigned long vm_flags); + unsigned long end, vm_flags_t vm_flags); bool current_is_ksgxd(void); void sgx_encl_release(struct kref *ref); diff --git a/arch/x86/kernel/cpu/topology_amd.c b/arch/x86/kernel/cpu/topology_amd.c index 843b1655ab45..c79ebbb639cb 100644 --- a/arch/x86/kernel/cpu/topology_amd.c +++ b/arch/x86/kernel/cpu/topology_amd.c @@ -81,20 +81,25 @@ static bool parse_8000_001e(struct topo_scan *tscan, bool has_topoext) cpuid_leaf(0x8000001e, &leaf); - tscan->c->topo.initial_apicid = leaf.ext_apic_id; - /* - * If leaf 0xb is available, then the domain shifts are set - * already and nothing to do here. Only valid for family >= 0x17. + * If leaf 0xb/0x26 is available, then the APIC ID and the domain + * shifts are set already. */ - if (!has_topoext && tscan->c->x86 >= 0x17) { - /* - * Leaf 0x80000008 set the CORE domain shift already. - * Update the SMT domain, but do not propagate it. - */ - unsigned int nthreads = leaf.core_nthreads + 1; + if (!has_topoext) { + tscan->c->topo.initial_apicid = leaf.ext_apic_id; - topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads); + /* + * Leaf 0x8000008 sets the CORE domain shift but not the + * SMT domain shift. On CPUs with family >= 0x17, there + * might be hyperthreads. + */ + if (tscan->c->x86 >= 0x17) { + /* Update the SMT domain, but do not propagate it. */ + unsigned int nthreads = leaf.core_nthreads + 1; + + topology_update_dom(tscan, TOPO_SMT_DOMAIN, + get_count_order(nthreads), nthreads); + } } store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id); @@ -170,27 +175,30 @@ static void topoext_fixup(struct topo_scan *tscan) static void parse_topology_amd(struct topo_scan *tscan) { - bool has_topoext = false; - /* - * If the extended topology leaf 0x8000_001e is available - * try to get SMT, CORE, TILE, and DIE shifts from extended + * Try to get SMT, CORE, TILE, and DIE shifts from extended * CPUID leaf 0x8000_0026 on supported processors first. If * extended CPUID leaf 0x8000_0026 is not supported, try to - * get SMT and CORE shift from leaf 0xb first, then try to - * get the CORE shift from leaf 0x8000_0008. + * get SMT and CORE shift from leaf 0xb. If either leaf is + * available, cpu_parse_topology_ext() will return true. */ - if (cpu_feature_enabled(X86_FEATURE_TOPOEXT)) - has_topoext = cpu_parse_topology_ext(tscan); + bool has_xtopology = cpu_parse_topology_ext(tscan); if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) tscan->c->topo.cpu_type = cpuid_ebx(0x80000026); - if (!has_topoext && !parse_8000_0008(tscan)) + /* + * If XTOPOLOGY leaves (0x26/0xb) are not available, try to + * get the CORE shift from leaf 0x8000_0008 first. + */ + if (!has_xtopology && !parse_8000_0008(tscan)) return; - /* Prefer leaf 0x8000001e if available */ - if (parse_8000_001e(tscan, has_topoext)) + /* + * Prefer leaf 0x8000001e if available to get the SMT shift and + * the initial APIC ID if XTOPOLOGY leaves are not available. + */ + if (parse_8000_001e(tscan, has_xtopology)) return; /* Try the NODEID MSR */ diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index bcb534688dfe..c6b12bed173d 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -163,10 +163,10 @@ static struct crash_mem *fill_up_crash_elf_data(void) return NULL; /* - * Exclusion of crash region and/or crashk_low_res may cause - * another range split. So add extra two slots here. + * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges + * may cause range splits. So add extra slots here. */ - nr_ranges += 2; + nr_ranges += 2 + crashk_cma_cnt; cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); if (!cmem) return NULL; @@ -184,6 +184,7 @@ static struct crash_mem *fill_up_crash_elf_data(void) static int elf_header_exclude_ranges(struct crash_mem *cmem) { int ret = 0; + int i; /* Exclude the low 1M because it is always reserved */ ret = crash_exclude_mem_range(cmem, 0, SZ_1M - 1); @@ -198,8 +199,17 @@ static int elf_header_exclude_ranges(struct crash_mem *cmem) if (crashk_low_res.end) ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); + if (ret) + return ret; - return ret; + for (i = 0; i < crashk_cma_cnt; ++i) { + ret = crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start, + crashk_cma_ranges[i].end); + if (ret) + return ret; + } + + return 0; } static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg) @@ -374,6 +384,14 @@ int crash_setup_memmap_entries(struct kimage *image, struct boot_params *params) add_e820_entry(params, &ei); } + for (i = 0; i < crashk_cma_cnt; ++i) { + ei.addr = crashk_cma_ranges[i].start; + ei.size = crashk_cma_ranges[i].end - + crashk_cma_ranges[i].start + 1; + ei.type = E820_TYPE_RAM; + add_e820_entry(params, &ei); + } + out: vfree(cmem); return ret; diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c index ea138583dd92..aefd412a23dc 100644 --- a/arch/x86/kernel/fpu/core.c +++ b/arch/x86/kernel/fpu/core.c @@ -37,6 +37,7 @@ DEFINE_PER_CPU(u64, xfd_state); /* The FPU state configuration data for kernel and user space */ struct fpu_state_config fpu_kernel_cfg __ro_after_init; struct fpu_state_config fpu_user_cfg __ro_after_init; +struct vcpu_fpu_config guest_default_cfg __ro_after_init; /* * Represents the initial FPU state. It's mostly (but not completely) zeroes, @@ -217,7 +218,7 @@ void fpu_reset_from_exception_fixup(void) } #if IS_ENABLED(CONFIG_KVM) -static void __fpstate_reset(struct fpstate *fpstate, u64 xfd); +static void __fpstate_reset(struct fpstate *fpstate); static void fpu_lock_guest_permissions(void) { @@ -242,19 +243,21 @@ bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu) struct fpstate *fpstate; unsigned int size; - size = fpu_kernel_cfg.default_size + ALIGN(offsetof(struct fpstate, regs), 64); + size = guest_default_cfg.size + ALIGN(offsetof(struct fpstate, regs), 64); + fpstate = vzalloc(size); if (!fpstate) return false; - /* Leave xfd to 0 (the reset value defined by spec) */ - __fpstate_reset(fpstate, 0); - fpstate_init_user(fpstate); + /* Initialize indicators to reflect properties of the fpstate */ fpstate->is_valloc = true; fpstate->is_guest = true; + __fpstate_reset(fpstate); + fpstate_init_user(fpstate); + gfpu->fpstate = fpstate; - gfpu->xfeatures = fpu_kernel_cfg.default_features; + gfpu->xfeatures = guest_default_cfg.features; /* * KVM sets the FP+SSE bits in the XSAVE header when copying FPU state @@ -541,28 +544,50 @@ void fpstate_init_user(struct fpstate *fpstate) fpstate_init_fstate(fpstate); } -static void __fpstate_reset(struct fpstate *fpstate, u64 xfd) +static void __fpstate_reset(struct fpstate *fpstate) { - /* Initialize sizes and feature masks */ - fpstate->size = fpu_kernel_cfg.default_size; + /* + * Supervisor features (and thus sizes) may diverge between guest + * FPUs and host FPUs, as some supervisor features are supported + * for guests despite not being utilized by the host. User + * features and sizes are always identical, which allows for + * common guest and userspace ABI. + * + * For the host, set XFD to the kernel's desired initialization + * value. For guests, set XFD to its architectural RESET value. + */ + if (fpstate->is_guest) { + fpstate->size = guest_default_cfg.size; + fpstate->xfeatures = guest_default_cfg.features; + fpstate->xfd = 0; + } else { + fpstate->size = fpu_kernel_cfg.default_size; + fpstate->xfeatures = fpu_kernel_cfg.default_features; + fpstate->xfd = init_fpstate.xfd; + } + fpstate->user_size = fpu_user_cfg.default_size; - fpstate->xfeatures = fpu_kernel_cfg.default_features; fpstate->user_xfeatures = fpu_user_cfg.default_features; - fpstate->xfd = xfd; } void fpstate_reset(struct fpu *fpu) { /* Set the fpstate pointer to the default fpstate */ fpu->fpstate = &fpu->__fpstate; - __fpstate_reset(fpu->fpstate, init_fpstate.xfd); + __fpstate_reset(fpu->fpstate); /* Initialize the permission related info in fpu */ fpu->perm.__state_perm = fpu_kernel_cfg.default_features; fpu->perm.__state_size = fpu_kernel_cfg.default_size; fpu->perm.__user_state_size = fpu_user_cfg.default_size; - /* Same defaults for guests */ - fpu->guest_perm = fpu->perm; + + fpu->guest_perm.__state_perm = guest_default_cfg.features; + fpu->guest_perm.__state_size = guest_default_cfg.size; + /* + * User features and sizes are always identical between host and + * guest FPUs, which allows for common guest and userspace ABI. + */ + fpu->guest_perm.__user_state_size = fpu_user_cfg.default_size; } static inline void fpu_inherit_perms(struct fpu *dst_fpu) diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c index 99db41bf9fa6..ff988b9ea39f 100644 --- a/arch/x86/kernel/fpu/init.c +++ b/arch/x86/kernel/fpu/init.c @@ -205,6 +205,7 @@ static void __init fpu__init_system_xstate_size_legacy(void) fpu_kernel_cfg.default_size = size; fpu_user_cfg.max_size = size; fpu_user_cfg.default_size = size; + guest_default_cfg.size = size; } /* diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 9aa9ac8399ae..28e4fd65c9da 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -57,7 +57,7 @@ static const char *xfeature_names[] = "Protection Keys User registers", "PASID state", "Control-flow User registers", - "Control-flow Kernel registers (unused)", + "Control-flow Kernel registers (KVM only)", "unknown xstate feature", "unknown xstate feature", "unknown xstate feature", @@ -81,6 +81,7 @@ static unsigned short xsave_cpuid_features[] __initdata = { [XFEATURE_PKRU] = X86_FEATURE_OSPKE, [XFEATURE_PASID] = X86_FEATURE_ENQCMD, [XFEATURE_CET_USER] = X86_FEATURE_SHSTK, + [XFEATURE_CET_KERNEL] = X86_FEATURE_SHSTK, [XFEATURE_XTILE_CFG] = X86_FEATURE_AMX_TILE, [XFEATURE_XTILE_DATA] = X86_FEATURE_AMX_TILE, [XFEATURE_APX] = X86_FEATURE_APX, @@ -372,6 +373,7 @@ static __init void os_xrstor_booting(struct xregs_state *xstate) XFEATURE_MASK_BNDCSR | \ XFEATURE_MASK_PASID | \ XFEATURE_MASK_CET_USER | \ + XFEATURE_MASK_CET_KERNEL | \ XFEATURE_MASK_XTILE | \ XFEATURE_MASK_APX) @@ -573,6 +575,7 @@ static bool __init check_xstate_against_struct(int nr) case XFEATURE_PASID: return XCHECK_SZ(sz, nr, struct ia32_pasid_state); case XFEATURE_XTILE_CFG: return XCHECK_SZ(sz, nr, struct xtile_cfg); case XFEATURE_CET_USER: return XCHECK_SZ(sz, nr, struct cet_user_state); + case XFEATURE_CET_KERNEL: return XCHECK_SZ(sz, nr, struct cet_supervisor_state); case XFEATURE_APX: return XCHECK_SZ(sz, nr, struct apx_state); case XFEATURE_XTILE_DATA: check_xtile_data_against_struct(sz); return true; default: @@ -743,6 +746,9 @@ static int __init init_xstate_size(void) fpu_user_cfg.default_size = xstate_calculate_size(fpu_user_cfg.default_features, false); + guest_default_cfg.size = + xstate_calculate_size(guest_default_cfg.features, compacted); + return 0; } @@ -763,6 +769,7 @@ static void __init fpu__init_disable_system_xstate(unsigned int legacy_size) fpu_kernel_cfg.default_size = legacy_size; fpu_user_cfg.max_size = legacy_size; fpu_user_cfg.default_size = legacy_size; + guest_default_cfg.size = legacy_size; /* * Prevent enabling the static branch which enables writes to the @@ -773,6 +780,24 @@ static void __init fpu__init_disable_system_xstate(unsigned int legacy_size) fpstate_reset(x86_task_fpu(current)); } +static u64 __init host_default_mask(void) +{ + /* + * Exclude dynamic features (require userspace opt-in) and features + * that are supported only for KVM guests. + */ + return ~((u64)XFEATURE_MASK_USER_DYNAMIC | XFEATURE_MASK_GUEST_SUPERVISOR); +} + +static u64 __init guest_default_mask(void) +{ + /* + * Exclude dynamic features, which require userspace opt-in even + * for KVM guests. + */ + return ~(u64)XFEATURE_MASK_USER_DYNAMIC; +} + /* * Enable and initialize the xsave feature. * Called once per system bootup. @@ -855,12 +880,13 @@ void __init fpu__init_system_xstate(unsigned int legacy_size) fpu_user_cfg.max_features = fpu_kernel_cfg.max_features; fpu_user_cfg.max_features &= XFEATURE_MASK_USER_SUPPORTED; - /* Clean out dynamic features from default */ - fpu_kernel_cfg.default_features = fpu_kernel_cfg.max_features; - fpu_kernel_cfg.default_features &= ~XFEATURE_MASK_USER_DYNAMIC; - - fpu_user_cfg.default_features = fpu_user_cfg.max_features; - fpu_user_cfg.default_features &= ~XFEATURE_MASK_USER_DYNAMIC; + /* + * Now, given maximum feature set, determine default values by + * applying default masks. + */ + fpu_kernel_cfg.default_features = fpu_kernel_cfg.max_features & host_default_mask(); + fpu_user_cfg.default_features = fpu_user_cfg.max_features & host_default_mask(); + guest_default_cfg.features = fpu_kernel_cfg.max_features & guest_default_mask(); /* Store it for paranoia check at the end */ xfeatures = fpu_kernel_cfg.max_features; @@ -1855,19 +1881,20 @@ long fpu_xstate_prctl(int option, unsigned long arg2) #ifdef CONFIG_PROC_PID_ARCH_STATUS /* * Report the amount of time elapsed in millisecond since last AVX512 - * use in the task. + * use in the task. Report -1 if no AVX-512 usage. */ static void avx512_status(struct seq_file *m, struct task_struct *task) { - unsigned long timestamp = READ_ONCE(x86_task_fpu(task)->avx512_timestamp); - long delta; + unsigned long timestamp; + long delta = -1; - if (!timestamp) { - /* - * Report -1 if no AVX512 usage - */ - delta = -1; - } else { + /* AVX-512 usage is not tracked for kernel threads. Don't report anything. */ + if (task->flags & (PF_KTHREAD | PF_USER_WORKER)) + return; + + timestamp = READ_ONCE(x86_task_fpu(task)->avx512_timestamp); + + if (timestamp) { delta = (long)(jiffies - timestamp); /* * Cap to LONG_MAX if time difference > LONG_MAX diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 252e82bcfd2f..4450acec9390 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -263,7 +263,7 @@ void arch_ftrace_update_code(int command) static inline void *alloc_tramp(unsigned long size) { - return execmem_alloc(EXECMEM_FTRACE, size); + return execmem_alloc_rw(EXECMEM_FTRACE, size); } static inline void tramp_free(void *tramp) { diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 9ed29ff10e59..10721a125226 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -256,26 +256,59 @@ static __always_inline void handle_irq(struct irq_desc *desc, __handle_irq(desc, regs); } -static __always_inline int call_irq_handler(int vector, struct pt_regs *regs) +static struct irq_desc *reevaluate_vector(int vector) { - struct irq_desc *desc; - int ret = 0; + struct irq_desc *desc = __this_cpu_read(vector_irq[vector]); + + if (!IS_ERR_OR_NULL(desc)) + return desc; + + if (desc == VECTOR_UNUSED) + pr_emerg_ratelimited("No irq handler for %d.%u\n", smp_processor_id(), vector); + else + __this_cpu_write(vector_irq[vector], VECTOR_UNUSED); + return NULL; +} + +static __always_inline bool call_irq_handler(int vector, struct pt_regs *regs) +{ + struct irq_desc *desc = __this_cpu_read(vector_irq[vector]); - desc = __this_cpu_read(vector_irq[vector]); if (likely(!IS_ERR_OR_NULL(desc))) { handle_irq(desc, regs); - } else { - ret = -EINVAL; - if (desc == VECTOR_UNUSED) { - pr_emerg_ratelimited("%s: %d.%u No irq handler for vector\n", - __func__, smp_processor_id(), - vector); - } else { - __this_cpu_write(vector_irq[vector], VECTOR_UNUSED); - } + return true; } - return ret; + /* + * Reevaluate with vector_lock held to prevent a race against + * request_irq() setting up the vector: + * + * CPU0 CPU1 + * interrupt is raised in APIC IRR + * but not handled + * free_irq() + * per_cpu(vector_irq, CPU1)[vector] = VECTOR_SHUTDOWN; + * + * request_irq() common_interrupt() + * d = this_cpu_read(vector_irq[vector]); + * + * per_cpu(vector_irq, CPU1)[vector] = desc; + * + * if (d == VECTOR_SHUTDOWN) + * this_cpu_write(vector_irq[vector], VECTOR_UNUSED); + * + * This requires that the same vector on the same target CPU is + * handed out or that a spurious interrupt hits that CPU/vector. + */ + lock_vector_lock(); + desc = reevaluate_vector(vector); + unlock_vector_lock(); + + if (!desc) + return false; + + handle_irq(desc, regs); + return true; } /* @@ -289,7 +322,7 @@ DEFINE_IDTENTRY_IRQ(common_interrupt) /* entry code tells RCU that we're not quiescent. Check it. */ RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU"); - if (unlikely(call_irq_handler(vector, regs))) + if (unlikely(!call_irq_handler(vector, regs))) apic_eoi(); set_irq_regs(old_regs); diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c index 9cea1fc36c18..243a769fdd97 100644 --- a/arch/x86/kernel/itmt.c +++ b/arch/x86/kernel/itmt.c @@ -59,6 +59,18 @@ static ssize_t sched_itmt_enabled_write(struct file *filp, return result; } +static int sched_core_priority_show(struct seq_file *s, void *unused) +{ + int cpu; + + seq_puts(s, "CPU #\tPriority\n"); + for_each_possible_cpu(cpu) + seq_printf(s, "%d\t%d\n", cpu, arch_asym_cpu_priority(cpu)); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(sched_core_priority); + static const struct file_operations dfs_sched_itmt_fops = { .read = debugfs_read_file_bool, .write = sched_itmt_enabled_write, @@ -67,6 +79,7 @@ static const struct file_operations dfs_sched_itmt_fops = { }; static struct dentry *dfs_sched_itmt; +static struct dentry *dfs_sched_core_prio; /** * sched_set_itmt_support() - Indicate platform supports ITMT @@ -102,6 +115,14 @@ int sched_set_itmt_support(void) return -ENOMEM; } + dfs_sched_core_prio = debugfs_create_file("sched_core_priority", 0644, + arch_debugfs_dir, NULL, + &sched_core_priority_fops); + if (IS_ERR_OR_NULL(dfs_sched_core_prio)) { + dfs_sched_core_prio = NULL; + return -ENOMEM; + } + sched_itmt_capable = true; sysctl_sched_itmt_enabled = 1; @@ -133,6 +154,8 @@ void sched_clear_itmt_support(void) debugfs_remove(dfs_sched_itmt); dfs_sched_itmt = NULL; + debugfs_remove(dfs_sched_core_prio); + dfs_sched_core_prio = NULL; if (sysctl_sched_itmt_enabled) { /* disable sched_itmt if we are no longer ITMT capable */ diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 102641fd2172..8b1a9733d13e 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -385,7 +385,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) struct perf_event *bp; /* Disable hardware debugging while we are in kgdb: */ - set_debugreg(0UL, 7); + set_debugreg(DR7_FIXED_1, 7); for (i = 0; i < HBP_NUM; i++) { if (!breakinfo[i].enabled) continue; diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 47cb8eb138ba..6079d15dab8c 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c @@ -481,24 +481,6 @@ static int prepare_singlestep(kprobe_opcode_t *buf, struct kprobe *p, return len; } -/* Make page to RO mode when allocate it */ -void *alloc_insn_page(void) -{ - void *page; - - page = execmem_alloc(EXECMEM_KPROBES, PAGE_SIZE); - if (!page) - return NULL; - - /* - * TODO: Once additional kernel code protection mechanisms are set, ensure - * that the page was not maliciously altered and it is still zeroed. - */ - set_memory_rox((unsigned long)page, 1); - - return page; -} - /* Kprobe x86 instruction emulation - only regs->ip or IF flag modifiers */ static void kprobe_emulate_ifmodifiers(struct kprobe *p, struct pt_regs *regs) diff --git a/arch/x86/kernel/ksysfs.c b/arch/x86/kernel/ksysfs.c index b68d4be9464e..d547de9b3ed8 100644 --- a/arch/x86/kernel/ksysfs.c +++ b/arch/x86/kernel/ksysfs.c @@ -40,7 +40,7 @@ static const struct bin_attribute boot_params_data_attr = { .name = "data", .mode = S_IRUGO, }, - .read_new = boot_params_data_read, + .read = boot_params_data_read, .size = sizeof(boot_params), }; @@ -56,7 +56,7 @@ static const struct bin_attribute *const boot_params_data_attrs[] = { static const struct attribute_group boot_params_attr_group = { .attrs = boot_params_version_attrs, - .bin_attrs_new = boot_params_data_attrs, + .bin_attrs = boot_params_data_attrs, }; static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr) @@ -250,7 +250,7 @@ static struct bin_attribute data_attr __ro_after_init = { .name = "data", .mode = S_IRUGO, }, - .read_new = setup_data_data_read, + .read = setup_data_data_read, }; static struct attribute *setup_data_type_attrs[] = { @@ -265,7 +265,7 @@ static const struct bin_attribute *const setup_data_data_attrs[] = { static const struct attribute_group setup_data_attr_group = { .attrs = setup_data_type_attrs, - .bin_attrs_new = setup_data_data_attrs, + .bin_attrs = setup_data_data_attrs, }; static int __init create_setup_data_node(struct kobject *parent, diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 921c1c783bc1..8ae750cde0c6 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -420,7 +420,7 @@ static u64 kvm_steal_clock(int cpu) return steal; } -static inline void __set_percpu_decrypted(void *ptr, unsigned long size) +static inline __init void __set_percpu_decrypted(void *ptr, unsigned long size) { early_set_memory_decrypted((unsigned long) ptr, size); } diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 704883c21f3a..1b7960cf6eb0 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -334,13 +334,21 @@ DEFINE_PER_CPU(u64, msr_misc_features_shadow); static void set_cpuid_faulting(bool on) { - u64 msrval; - msrval = this_cpu_read(msr_misc_features_shadow); - msrval &= ~MSR_MISC_FEATURES_ENABLES_CPUID_FAULT; - msrval |= (on << MSR_MISC_FEATURES_ENABLES_CPUID_FAULT_BIT); - this_cpu_write(msr_misc_features_shadow, msrval); - wrmsrq(MSR_MISC_FEATURES_ENABLES, msrval); + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) { + u64 msrval; + + msrval = this_cpu_read(msr_misc_features_shadow); + msrval &= ~MSR_MISC_FEATURES_ENABLES_CPUID_FAULT; + msrval |= (on << MSR_MISC_FEATURES_ENABLES_CPUID_FAULT_BIT); + this_cpu_write(msr_misc_features_shadow, msrval); + wrmsrq(MSR_MISC_FEATURES_ENABLES, msrval); + } else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { + if (on) + msr_set_bit(MSR_K7_HWCR, MSR_K7_HWCR_CPUID_USER_DIS_BIT); + else + msr_clear_bit(MSR_K7_HWCR, MSR_K7_HWCR_CPUID_USER_DIS_BIT); + } } static void disable_cpuid(void) @@ -907,16 +915,24 @@ static __init bool prefer_mwait_c1_over_halt(void) */ static __cpuidle void mwait_idle(void) { + if (need_resched()) + return; + + x86_idle_clear_cpu_buffers(); + if (!current_set_polling_and_test()) { const void *addr = ¤t_thread_info()->flags; alternative_input("", "clflush (%[addr])", X86_BUG_CLFLUSH_MONITOR, [addr] "a" (addr)); __monitor(addr, 0, 0); - if (!need_resched()) { - __sti_mwait(0, 0); - raw_local_irq_disable(); - } + if (need_resched()) + goto out; + + __sti_mwait(0, 0); + raw_local_irq_disable(); } + +out: __current_clr_polling(); } diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index a10e180cbf23..3ef15c2f152f 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -93,7 +93,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode, /* Only print out debug registers if they are in their non-default state. */ if ((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) && - (d6 == DR6_RESERVED) && (d7 == 0x400)) + (d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1)) return; printk("%sDR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n", diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 8d6cf25127aa..52a5c03c353c 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -133,7 +133,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode, /* Only print out debug registers if they are in their non-default state. */ if (!((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) && - (d6 == DR6_RESERVED) && (d7 == 0x400))) { + (d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1))) { printk("%sDR0: %016lx DR1: %016lx DR2: %016lx\n", log_lvl, d0, d1, d2); printk("%sDR3: %016lx DR6: %016lx DR7: %016lx\n", @@ -707,6 +707,10 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) /* Load the Intel cache allocation PQR MSR. */ resctrl_arch_sched_in(next_p); + /* Reset hw history on AMD CPUs */ + if (cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS)) + wrmsrl(MSR_AMD_WORKLOAD_HRST, 0x1); + return prev_p; } diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 095f04bdabdc..3dcadc13f09a 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1236,7 +1236,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, static struct user_regset x86_64_regsets[] __ro_after_init = { [REGSET64_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct user_regs_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -1244,7 +1244,7 @@ static struct user_regset x86_64_regsets[] __ro_after_init = { .set = genregs_set }, [REGSET64_FP] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct fxregs_state) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -1253,7 +1253,7 @@ static struct user_regset x86_64_regsets[] __ro_after_init = { .set = xfpregs_set }, [REGSET64_XSTATE] = { - .core_note_type = NT_X86_XSTATE, + USER_REGSET_NOTE_TYPE(X86_XSTATE), .size = sizeof(u64), .align = sizeof(u64), .active = xstateregs_active, @@ -1261,7 +1261,7 @@ static struct user_regset x86_64_regsets[] __ro_after_init = { .set = xstateregs_set }, [REGSET64_IOPERM] = { - .core_note_type = NT_386_IOPERM, + USER_REGSET_NOTE_TYPE(386_IOPERM), .n = IO_BITMAP_LONGS, .size = sizeof(long), .align = sizeof(long), @@ -1270,7 +1270,7 @@ static struct user_regset x86_64_regsets[] __ro_after_init = { }, #ifdef CONFIG_X86_USER_SHADOW_STACK [REGSET64_SSP] = { - .core_note_type = NT_X86_SHSTK, + USER_REGSET_NOTE_TYPE(X86_SHSTK), .n = 1, .size = sizeof(u64), .align = sizeof(u64), @@ -1297,7 +1297,7 @@ static const struct user_regset_view user_x86_64_view = { #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION static struct user_regset x86_32_regsets[] __ro_after_init = { [REGSET32_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct user_regs_struct32) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -1305,7 +1305,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .set = genregs32_set }, [REGSET32_FP] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_i387_ia32_struct) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -1314,7 +1314,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .set = fpregs_set }, [REGSET32_XFP] = { - .core_note_type = NT_PRXFPREG, + USER_REGSET_NOTE_TYPE(PRXFPREG), .n = sizeof(struct fxregs_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -1323,7 +1323,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .set = xfpregs_set }, [REGSET32_XSTATE] = { - .core_note_type = NT_X86_XSTATE, + USER_REGSET_NOTE_TYPE(X86_XSTATE), .size = sizeof(u64), .align = sizeof(u64), .active = xstateregs_active, @@ -1331,7 +1331,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .set = xstateregs_set }, [REGSET32_TLS] = { - .core_note_type = NT_386_TLS, + USER_REGSET_NOTE_TYPE(386_TLS), .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, .size = sizeof(struct user_desc), @@ -1341,7 +1341,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .set = regset_tls_set }, [REGSET32_IOPERM] = { - .core_note_type = NT_386_IOPERM, + USER_REGSET_NOTE_TYPE(386_IOPERM), .n = IO_BITMAP_BYTES / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 0792f31961ac..1b2edd07a3e1 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -603,7 +603,7 @@ static void __init memblock_x86_reserve_range_setup_data(void) static void __init arch_reserve_crashkernel(void) { - unsigned long long crash_base, crash_size, low_size = 0; + unsigned long long crash_base, crash_size, low_size = 0, cma_size = 0; bool high = false; int ret; @@ -612,7 +612,7 @@ static void __init arch_reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), &crash_size, &crash_base, - &low_size, &high); + &low_size, &cma_size, &high); if (ret) return; @@ -622,6 +622,7 @@ static void __init arch_reserve_crashkernel(void) } reserve_crashkernel_generic(crash_size, crash_base, low_size, high); + reserve_crashkernel_cma(cma_size); } static struct resource standard_io_resources[] = { diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index 98123ff10506..42bbc42bd350 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c @@ -152,6 +152,8 @@ SYSCALL32_DEFINE0(sigreturn) struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8); sigset_t set; + prevent_single_step_upon_eretu(regs); + if (!access_ok(frame, sizeof(*frame))) goto badframe; if (__get_user(set.sig[0], &frame->sc.oldmask) @@ -175,6 +177,8 @@ SYSCALL32_DEFINE0(rt_sigreturn) struct rt_sigframe_ia32 __user *frame; sigset_t set; + prevent_single_step_upon_eretu(regs); + frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4); if (!access_ok(frame, sizeof(*frame))) diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index ee9453891901..d483b585c6c6 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c @@ -250,6 +250,8 @@ SYSCALL_DEFINE0(rt_sigreturn) sigset_t set; unsigned long uc_flags; + prevent_single_step_upon_eretu(regs); + frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); if (!access_ok(frame, sizeof(*frame))) goto badframe; @@ -366,6 +368,8 @@ COMPAT_SYSCALL_DEFINE0(x32_rt_sigreturn) sigset_t set; unsigned long uc_flags; + prevent_single_step_upon_eretu(regs); + frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); if (!access_ok(frame, sizeof(*frame))) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 58ede3fa6a75..33e166f6ab12 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -478,44 +478,41 @@ static int x86_cluster_flags(void) */ static bool x86_has_numa_in_package; -static struct sched_domain_topology_level x86_topology[6]; +static struct sched_domain_topology_level x86_topology[] = { + SDTL_INIT(cpu_smt_mask, cpu_smt_flags, SMT), +#ifdef CONFIG_SCHED_CLUSTER + SDTL_INIT(cpu_clustergroup_mask, x86_cluster_flags, CLS), +#endif +#ifdef CONFIG_SCHED_MC + SDTL_INIT(cpu_coregroup_mask, x86_core_flags, MC), +#endif + SDTL_INIT(cpu_cpu_mask, x86_sched_itmt_flags, PKG), + { NULL }, +}; static void __init build_sched_topology(void) { - int i = 0; + struct sched_domain_topology_level *topology = x86_topology; -#ifdef CONFIG_SCHED_SMT - x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) - }; -#endif -#ifdef CONFIG_SCHED_CLUSTER - x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_clustergroup_mask, x86_cluster_flags, SD_INIT_NAME(CLS) - }; -#endif -#ifdef CONFIG_SCHED_MC - x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_coregroup_mask, x86_core_flags, SD_INIT_NAME(MC) - }; -#endif /* - * When there is NUMA topology inside the package skip the PKG domain - * since the NUMA domains will auto-magically create the right spanning - * domains based on the SLIT. + * When there is NUMA topology inside the package invalidate the + * PKG domain since the NUMA domains will auto-magically create the + * right spanning domains based on the SLIT. */ - if (!x86_has_numa_in_package) { - x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_cpu_mask, x86_sched_itmt_flags, SD_INIT_NAME(PKG) - }; + if (x86_has_numa_in_package) { + unsigned int pkgdom = ARRAY_SIZE(x86_topology) - 2; + + memset(&x86_topology[pkgdom], 0, sizeof(x86_topology[pkgdom])); } /* - * There must be one trailing NULL entry left. + * Drop the SMT domains if there is only one thread per-core + * since it'll get degenerated by the scheduler anyways. */ - BUG_ON(i >= ARRAY_SIZE(x86_topology)-1); + if (cpu_smt_num_threads <= 1) + ++topology; - set_sched_topology(x86_topology); + set_sched_topology(topology); } void set_cpu_sibling_map(int cpu) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index c5c897a86418..36354b470590 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -1022,24 +1022,32 @@ static bool is_sysenter_singlestep(struct pt_regs *regs) #endif } -static __always_inline unsigned long debug_read_clear_dr6(void) +static __always_inline unsigned long debug_read_reset_dr6(void) { unsigned long dr6; + get_debugreg(dr6, 6); + dr6 ^= DR6_RESERVED; /* Flip to positive polarity */ + /* * The Intel SDM says: * - * Certain debug exceptions may clear bits 0-3. The remaining - * contents of the DR6 register are never cleared by the - * processor. To avoid confusion in identifying debug - * exceptions, debug handlers should clear the register before - * returning to the interrupted task. + * Certain debug exceptions may clear bits 0-3 of DR6. * - * Keep it simple: clear DR6 immediately. + * BLD induced #DB clears DR6.BLD and any other debug + * exception doesn't modify DR6.BLD. + * + * RTM induced #DB clears DR6.RTM and any other debug + * exception sets DR6.RTM. + * + * To avoid confusion in identifying debug exceptions, + * debug handlers should set DR6.BLD and DR6.RTM, and + * clear other DR6 bits before returning. + * + * Keep it simple: write DR6 with its architectural reset + * value 0xFFFF0FF0, defined as DR6_RESERVED, immediately. */ - get_debugreg(dr6, 6); set_debugreg(DR6_RESERVED, 6); - dr6 ^= DR6_RESERVED; /* Flip to positive polarity */ return dr6; } @@ -1239,13 +1247,13 @@ static noinstr void exc_debug_user(struct pt_regs *regs, unsigned long dr6) /* IST stack entry */ DEFINE_IDTENTRY_DEBUG(exc_debug) { - exc_debug_kernel(regs, debug_read_clear_dr6()); + exc_debug_kernel(regs, debug_read_reset_dr6()); } /* User entry, runs on regular task stack */ DEFINE_IDTENTRY_DEBUG_USER(exc_debug) { - exc_debug_user(regs, debug_read_clear_dr6()); + exc_debug_user(regs, debug_read_reset_dr6()); } #ifdef CONFIG_X86_FRED @@ -1264,7 +1272,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug) { /* * FRED #DB stores DR6 on the stack in the format which - * debug_read_clear_dr6() returns for the IDT entry points. + * debug_read_reset_dr6() returns for the IDT entry points. */ unsigned long dr6 = fred_event_data(regs); @@ -1279,7 +1287,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug) /* 32 bit does not have separate entry points. */ DEFINE_IDTENTRY_RAW(exc_debug) { - unsigned long dr6 = debug_read_clear_dr6(); + unsigned long dr6 = debug_read_reset_dr6(); if (user_mode(regs)) exc_debug_user(regs, dr6); diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 2eeffcec5382..2c86673155c9 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -166,6 +166,16 @@ config KVM_AMD_SEV Encrypted State (SEV-ES), and Secure Encrypted Virtualization with Secure Nested Paging (SEV-SNP) technologies on AMD processors. +config KVM_IOAPIC + bool "I/O APIC, PIC, and PIT emulation" + default y + depends on KVM + help + Provides support for KVM to emulate an I/O APIC, PIC, and PIT, i.e. + for full in-kernel APIC emulation. + + If unsure, say Y. + config KVM_SMM bool "System Management Mode emulation" default y diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index a5d362c7b504..c4b8950c7abe 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -5,12 +5,11 @@ ccflags-$(CONFIG_KVM_WERROR) += -Werror include $(srctree)/virt/kvm/Makefile.kvm -kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ - i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ - debugfs.o mmu/mmu.o mmu/page_track.o \ - mmu/spte.o +kvm-y += x86.o emulate.o irq.o lapic.o cpuid.o pmu.o mtrr.o \ + debugfs.o mmu/mmu.o mmu/page_track.o mmu/spte.o kvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o +kvm-$(CONFIG_KVM_IOAPIC) += i8259.o i8254.o ioapic.o kvm-$(CONFIG_KVM_HYPERV) += hyperv.o kvm-$(CONFIG_KVM_XEN) += xen.o kvm-$(CONFIG_KVM_SMM) += smm.o diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index b2d006756e02..e2836a255b16 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -979,6 +979,7 @@ void kvm_set_cpu_caps(void) F(FSRS), F(FSRC), F(WRMSRNS), + X86_64_F(LKGS), F(AMX_FP16), F(AVX_IFMA), F(LAM), @@ -1165,6 +1166,8 @@ void kvm_set_cpu_caps(void) */ SYNTHESIZED_F(LFENCE_RDTSC), /* SmmPgCfgLock */ + /* 4: Resv */ + SYNTHESIZED_F(VERW_CLEAR), F(NULL_SEL_CLR_BASE), /* UpperAddressIgnore */ F(AUTOIBRS), @@ -1179,6 +1182,11 @@ void kvm_set_cpu_caps(void) F(SRSO_USER_KERNEL_NO), ); + kvm_cpu_cap_init(CPUID_8000_0021_ECX, + SYNTHESIZED_F(TSA_SQ_NO), + SYNTHESIZED_F(TSA_L1_NO), + ); + kvm_cpu_cap_init(CPUID_8000_0022_EAX, F(PERFMON_V2), ); @@ -1748,8 +1756,9 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) entry->eax = entry->ebx = entry->ecx = entry->edx = 0; break; case 0x80000021: - entry->ebx = entry->ecx = entry->edx = 0; + entry->ebx = entry->edx = 0; cpuid_entry_override(entry, CPUID_8000_0021_EAX); + cpuid_entry_override(entry, CPUID_8000_0021_ECX); break; /* AMD Extended Performance Monitoring and Debug */ case 0x80000022: { diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 24f0318c50d7..72b19a88a776 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -497,15 +497,19 @@ static int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint) return ret; } -int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vpidx, u32 sint) +int kvm_hv_synic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status) { struct kvm_vcpu_hv_synic *synic; - synic = synic_get(kvm, vpidx); + if (!level) + return -1; + + synic = synic_get(kvm, e->hv_sint.vcpu); if (!synic) return -EINVAL; - return synic_set_irq(synic, sint); + return synic_set_irq(synic, e->hv_sint.sint); } void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector) @@ -1979,6 +1983,9 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu) if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY) goto out_flush_all; + if (is_noncanonical_invlpg_address(entries[i], vcpu)) + continue; + /* * Lower 12 bits of 'address' encode the number of additional * pages to flush. @@ -2001,11 +2008,11 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu) static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc) { struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu); + unsigned long *vcpu_mask = hv_vcpu->vcpu_mask; u64 *sparse_banks = hv_vcpu->sparse_banks; struct kvm *kvm = vcpu->kvm; struct hv_tlb_flush_ex flush_ex; struct hv_tlb_flush flush; - DECLARE_BITMAP(vcpu_mask, KVM_MAX_VCPUS); struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo; /* * Normally, there can be no more than 'KVM_HV_TLB_FLUSH_FIFO_SIZE' diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h index 913bfc96959c..6ce160ffa678 100644 --- a/arch/x86/kvm/hyperv.h +++ b/arch/x86/kvm/hyperv.h @@ -103,7 +103,8 @@ static inline bool kvm_hv_hypercall_enabled(struct kvm_vcpu *vcpu) int kvm_hv_hypercall(struct kvm_vcpu *vcpu); void kvm_hv_irq_routing_update(struct kvm *kvm); -int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint); +int kvm_hv_synic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status); void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector); int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages); diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 739aa6c0d0c3..850972deac8e 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -248,8 +248,8 @@ static void pit_do_work(struct kthread_work *work) if (atomic_read(&ps->reinject) && !atomic_xchg(&ps->irq_ack, 0)) return; - kvm_set_irq(kvm, pit->irq_source_id, 0, 1, false); - kvm_set_irq(kvm, pit->irq_source_id, 0, 0, false); + kvm_set_irq(kvm, KVM_PIT_IRQ_SOURCE_ID, 0, 1, false); + kvm_set_irq(kvm, KVM_PIT_IRQ_SOURCE_ID, 0, 0, false); /* * Provides NMI watchdog support via Virtual Wire mode. @@ -288,7 +288,7 @@ static inline void kvm_pit_reset_reinject(struct kvm_pit *pit) atomic_set(&pit->pit_state.irq_ack, 1); } -void kvm_pit_set_reinject(struct kvm_pit *pit, bool reinject) +static void kvm_pit_set_reinject(struct kvm_pit *pit, bool reinject) { struct kvm_kpit_state *ps = &pit->pit_state; struct kvm *kvm = pit->kvm; @@ -400,8 +400,8 @@ static void pit_load_count(struct kvm_pit *pit, int channel, u32 val) } } -void kvm_pit_load_count(struct kvm_pit *pit, int channel, u32 val, - int hpet_legacy_start) +static void kvm_pit_load_count(struct kvm_pit *pit, int channel, u32 val, + int hpet_legacy_start) { u8 saved_mode; @@ -641,7 +641,7 @@ static void kvm_pit_reset(struct kvm_pit *pit) kvm_pit_reset_reinject(pit); } -static void pit_mask_notifer(struct kvm_irq_mask_notifier *kimn, bool mask) +static void pit_mask_notifier(struct kvm_irq_mask_notifier *kimn, bool mask) { struct kvm_pit *pit = container_of(kimn, struct kvm_pit, mask_notifier); @@ -649,6 +649,79 @@ static void pit_mask_notifer(struct kvm_irq_mask_notifier *kimn, bool mask) kvm_pit_reset_reinject(pit); } +int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps) +{ + struct kvm_kpit_state *kps = &kvm->arch.vpit->pit_state; + + BUILD_BUG_ON(sizeof(*ps) != sizeof(kps->channels)); + + mutex_lock(&kps->lock); + memcpy(ps, &kps->channels, sizeof(*ps)); + mutex_unlock(&kps->lock); + return 0; +} + +int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps) +{ + int i; + struct kvm_pit *pit = kvm->arch.vpit; + + mutex_lock(&pit->pit_state.lock); + memcpy(&pit->pit_state.channels, ps, sizeof(*ps)); + for (i = 0; i < 3; i++) + kvm_pit_load_count(pit, i, ps->channels[i].count, 0); + mutex_unlock(&pit->pit_state.lock); + return 0; +} + +int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) +{ + mutex_lock(&kvm->arch.vpit->pit_state.lock); + memcpy(ps->channels, &kvm->arch.vpit->pit_state.channels, + sizeof(ps->channels)); + ps->flags = kvm->arch.vpit->pit_state.flags; + mutex_unlock(&kvm->arch.vpit->pit_state.lock); + memset(&ps->reserved, 0, sizeof(ps->reserved)); + return 0; +} + +int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) +{ + int start = 0; + int i; + u32 prev_legacy, cur_legacy; + struct kvm_pit *pit = kvm->arch.vpit; + + mutex_lock(&pit->pit_state.lock); + prev_legacy = pit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY; + cur_legacy = ps->flags & KVM_PIT_FLAGS_HPET_LEGACY; + if (!prev_legacy && cur_legacy) + start = 1; + memcpy(&pit->pit_state.channels, &ps->channels, + sizeof(pit->pit_state.channels)); + pit->pit_state.flags = ps->flags; + for (i = 0; i < 3; i++) + kvm_pit_load_count(pit, i, pit->pit_state.channels[i].count, + start && i == 0); + mutex_unlock(&pit->pit_state.lock); + return 0; +} + +int kvm_vm_ioctl_reinject(struct kvm *kvm, struct kvm_reinject_control *control) +{ + struct kvm_pit *pit = kvm->arch.vpit; + + /* pit->pit_state.lock was overloaded to prevent userspace from getting + * an inconsistent state after running multiple KVM_REINJECT_CONTROL + * ioctls in parallel. Use a separate lock if that ioctl isn't rare. + */ + mutex_lock(&pit->pit_state.lock); + kvm_pit_set_reinject(pit, control->pit_reinject); + mutex_unlock(&pit->pit_state.lock); + + return 0; +} + static const struct kvm_io_device_ops pit_dev_ops = { .read = pit_ioport_read, .write = pit_ioport_write, @@ -671,10 +744,6 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags) if (!pit) return NULL; - pit->irq_source_id = kvm_request_irq_source_id(kvm); - if (pit->irq_source_id < 0) - goto fail_request; - mutex_init(&pit->pit_state.lock); pid = get_pid(task_tgid(current)); @@ -694,7 +763,7 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags) pit_state->irq_ack_notifier.gsi = 0; pit_state->irq_ack_notifier.irq_acked = kvm_pit_ack_irq; - pit->mask_notifier.func = pit_mask_notifer; + pit->mask_notifier.func = pit_mask_notifier; kvm_pit_reset(pit); @@ -726,8 +795,6 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags) kvm_pit_set_reinject(pit, false); kthread_destroy_worker(pit->worker); fail_kthread: - kvm_free_irq_source_id(kvm, pit->irq_source_id); -fail_request: kfree(pit); return NULL; } @@ -744,7 +811,6 @@ void kvm_free_pit(struct kvm *kvm) kvm_pit_set_reinject(pit, false); hrtimer_cancel(&pit->pit_state.timer); kthread_destroy_worker(pit->worker); - kvm_free_irq_source_id(kvm, pit->irq_source_id); kfree(pit); } } diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h index a768212ba821..60fa499d2f8a 100644 --- a/arch/x86/kvm/i8254.h +++ b/arch/x86/kvm/i8254.h @@ -6,6 +6,11 @@ #include +#include + +#include "ioapic.h" + +#ifdef CONFIG_KVM_IOAPIC struct kvm_kpit_channel_state { u32 count; /* can be 65536 */ u16 latched_count; @@ -42,7 +47,6 @@ struct kvm_pit { struct kvm_io_device speaker_dev; struct kvm *kvm; struct kvm_kpit_state pit_state; - int irq_source_id; struct kvm_irq_mask_notifier mask_notifier; struct kthread_worker *worker; struct kthread_work expired; @@ -55,11 +59,14 @@ struct kvm_pit { #define KVM_MAX_PIT_INTR_INTERVAL HZ / 100 #define KVM_PIT_CHANNEL_MASK 0x3 +int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps); +int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps); +int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps); +int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps); +int kvm_vm_ioctl_reinject(struct kvm *kvm, struct kvm_reinject_control *control); + struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags); void kvm_free_pit(struct kvm *kvm); - -void kvm_pit_load_count(struct kvm_pit *pit, int channel, u32 val, - int hpet_legacy_start); -void kvm_pit_set_reinject(struct kvm_pit *pit, bool reinject); +#endif /* CONFIG_KVM_IOAPIC */ #endif diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index a8fb19940975..2ac7f1678c46 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -31,6 +31,8 @@ #include #include #include + +#include "ioapic.h" #include "irq.h" #include @@ -185,8 +187,11 @@ void kvm_pic_update_irq(struct kvm_pic *s) pic_unlock(s); } -int kvm_pic_set_irq(struct kvm_pic *s, int irq, int irq_source_id, int level) +int kvm_pic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status) { + struct kvm_pic *s = kvm->arch.vpic; + int irq = e->irqchip.pin; int ret, irq_level; BUG_ON(irq < 0 || irq >= PIC_NUM_PINS); @@ -203,16 +208,6 @@ int kvm_pic_set_irq(struct kvm_pic *s, int irq, int irq_source_id, int level) return ret; } -void kvm_pic_clear_all(struct kvm_pic *s, int irq_source_id) -{ - int i; - - pic_lock(s); - for (i = 0; i < PIC_NUM_PINS; i++) - __clear_bit(irq_source_id, &s->irq_states[i]); - pic_unlock(s); -} - /* * acknowledge interrupt 'irq' */ diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index 45dae2d5d2f1..2b5d389bca5f 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c @@ -41,11 +41,11 @@ #include #include #include -#include #include "ioapic.h" #include "lapic.h" #include "irq.h" +#include "trace.h" static int ioapic_service(struct kvm_ioapic *vioapic, int irq, bool line_status); @@ -310,6 +310,42 @@ void kvm_arch_post_irq_ack_notifier_list_update(struct kvm *kvm) kvm_make_scan_ioapic_request(kvm); } +void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn) +{ + struct kvm_ioapic *ioapic = kvm->arch.vioapic; + + mutex_lock(&kvm->irq_lock); + kimn->irq = irq; + hlist_add_head_rcu(&kimn->link, &ioapic->mask_notifier_list); + mutex_unlock(&kvm->irq_lock); +} + +void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn) +{ + mutex_lock(&kvm->irq_lock); + hlist_del_rcu(&kimn->link); + mutex_unlock(&kvm->irq_lock); + synchronize_srcu(&kvm->irq_srcu); +} + +void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, + bool mask) +{ + struct kvm_ioapic *ioapic = kvm->arch.vioapic; + struct kvm_irq_mask_notifier *kimn; + int idx, gsi; + + idx = srcu_read_lock(&kvm->irq_srcu); + gsi = kvm_irq_map_chip_pin(kvm, irqchip, pin); + if (gsi != -1) + hlist_for_each_entry_rcu(kimn, &ioapic->mask_notifier_list, link) + if (kimn->irq == gsi) + kimn->func(kimn, mask); + srcu_read_unlock(&kvm->irq_srcu, idx); +} + static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) { unsigned index; @@ -479,9 +515,11 @@ static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status) return ret; } -int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id, - int level, bool line_status) +int kvm_ioapic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status) { + struct kvm_ioapic *ioapic = kvm->arch.vioapic; + int irq = e->irqchip.pin; int ret, irq_level; BUG_ON(irq < 0 || irq >= IOAPIC_NUM_PINS); @@ -496,16 +534,6 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id, return ret; } -void kvm_ioapic_clear_all(struct kvm_ioapic *ioapic, int irq_source_id) -{ - int i; - - spin_lock(&ioapic->lock); - for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++) - __clear_bit(irq_source_id, &ioapic->irq_states[i]); - spin_unlock(&ioapic->lock); -} - static void kvm_ioapic_eoi_inject_work(struct work_struct *work) { int i; @@ -718,6 +746,7 @@ int kvm_ioapic_init(struct kvm *kvm) return -ENOMEM; spin_lock_init(&ioapic->lock); INIT_DELAYED_WORK(&ioapic->eoi_inject, kvm_ioapic_eoi_inject_work); + INIT_HLIST_HEAD(&ioapic->mask_notifier_list); kvm->arch.vioapic = ioapic; kvm_ioapic_reset(ioapic); kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops); diff --git a/arch/x86/kvm/ioapic.h b/arch/x86/kvm/ioapic.h index aa8cb4ac0479..bf28dbc11ff6 100644 --- a/arch/x86/kvm/ioapic.h +++ b/arch/x86/kvm/ioapic.h @@ -86,8 +86,24 @@ struct kvm_ioapic { struct delayed_work eoi_inject; u32 irq_eoi[IOAPIC_NUM_PINS]; u32 irr_delivered; + + /* reads protected by irq_srcu, writes by irq_lock */ + struct hlist_head mask_notifier_list; }; +struct kvm_irq_mask_notifier { + void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked); + int irq; + struct hlist_node link; +}; + +void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn); +void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn); +void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, + bool mask); + #ifdef DEBUG #define ASSERT(x) \ do { \ @@ -103,7 +119,7 @@ do { \ static inline int ioapic_in_kernel(struct kvm *kvm) { - return irqchip_kernel(kvm); + return irqchip_full(kvm); } void kvm_rtc_eoi_tracking_restore_one(struct kvm_vcpu *vcpu); @@ -111,9 +127,9 @@ void kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu, int vector, int trigger_mode); int kvm_ioapic_init(struct kvm *kvm); void kvm_ioapic_destroy(struct kvm *kvm); -int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id, - int level, bool line_status); -void kvm_ioapic_clear_all(struct kvm_ioapic *ioapic, int irq_source_id); +int kvm_ioapic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status); + void kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state); void kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state); void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 97d68d837929..16da89259011 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -11,9 +11,12 @@ #include #include +#include +#include "hyperv.h" +#include "ioapic.h" #include "irq.h" -#include "i8254.h" +#include "trace.h" #include "x86.h" #include "xen.h" @@ -41,6 +44,14 @@ static int pending_userspace_extint(struct kvm_vcpu *v) return v->arch.pending_external_vector != -1; } +static int get_userspace_extint(struct kvm_vcpu *vcpu) +{ + int vector = vcpu->arch.pending_external_vector; + + vcpu->arch.pending_external_vector = -1; + return vector; +} + /* * check if there is pending interrupt from * non-APIC source without intack. @@ -67,10 +78,13 @@ int kvm_cpu_has_extint(struct kvm_vcpu *v) if (!kvm_apic_accept_pic_intr(v)) return 0; - if (irqchip_split(v->kvm)) - return pending_userspace_extint(v); - else +#ifdef CONFIG_KVM_IOAPIC + if (pic_in_kernel(v->kvm)) return v->kvm->arch.vpic->output; +#endif + + WARN_ON_ONCE(!irqchip_split(v->kvm)); + return pending_userspace_extint(v); } /* @@ -126,13 +140,13 @@ int kvm_cpu_get_extint(struct kvm_vcpu *v) return v->kvm->arch.xen.upcall_vector; #endif - if (irqchip_split(v->kvm)) { - int vector = v->arch.pending_external_vector; - - v->arch.pending_external_vector = -1; - return vector; - } else +#ifdef CONFIG_KVM_IOAPIC + if (pic_in_kernel(v->kvm)) return kvm_pic_read_irq(v->kvm); /* PIC */ +#endif + + WARN_ON_ONCE(!irqchip_split(v->kvm)); + return get_userspace_extint(v); } EXPORT_SYMBOL_GPL(kvm_cpu_get_extint); @@ -163,7 +177,9 @@ void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu) void __kvm_migrate_timers(struct kvm_vcpu *vcpu) { __kvm_migrate_apic_timer(vcpu); +#ifdef CONFIG_KVM_IOAPIC __kvm_migrate_pit_timer(vcpu); +#endif kvm_x86_call(migrate_timers)(vcpu); } @@ -171,10 +187,532 @@ bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args) { bool resample = args->flags & KVM_IRQFD_FLAG_RESAMPLE; - return resample ? irqchip_kernel(kvm) : irqchip_in_kernel(kvm); + return resample ? irqchip_full(kvm) : irqchip_in_kernel(kvm); } bool kvm_arch_irqchip_in_kernel(struct kvm *kvm) { return irqchip_in_kernel(kvm); } + +int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, + struct kvm_lapic_irq *irq, struct dest_map *dest_map) +{ + int r = -1; + struct kvm_vcpu *vcpu, *lowest = NULL; + unsigned long i, dest_vcpu_bitmap[BITS_TO_LONGS(KVM_MAX_VCPUS)]; + unsigned int dest_vcpus = 0; + + if (kvm_irq_delivery_to_apic_fast(kvm, src, irq, &r, dest_map)) + return r; + + if (irq->dest_mode == APIC_DEST_PHYSICAL && + irq->dest_id == 0xff && kvm_lowest_prio_delivery(irq)) { + pr_info("apic: phys broadcast and lowest prio\n"); + irq->delivery_mode = APIC_DM_FIXED; + } + + memset(dest_vcpu_bitmap, 0, sizeof(dest_vcpu_bitmap)); + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!kvm_apic_present(vcpu)) + continue; + + if (!kvm_apic_match_dest(vcpu, src, irq->shorthand, + irq->dest_id, irq->dest_mode)) + continue; + + if (!kvm_lowest_prio_delivery(irq)) { + if (r < 0) + r = 0; + r += kvm_apic_set_irq(vcpu, irq, dest_map); + } else if (kvm_apic_sw_enabled(vcpu->arch.apic)) { + if (!kvm_vector_hashing_enabled()) { + if (!lowest) + lowest = vcpu; + else if (kvm_apic_compare_prio(vcpu, lowest) < 0) + lowest = vcpu; + } else { + __set_bit(i, dest_vcpu_bitmap); + dest_vcpus++; + } + } + } + + if (dest_vcpus != 0) { + int idx = kvm_vector_to_index(irq->vector, dest_vcpus, + dest_vcpu_bitmap, KVM_MAX_VCPUS); + + lowest = kvm_get_vcpu(kvm, idx); + } + + if (lowest) + r = kvm_apic_set_irq(lowest, irq, dest_map); + + return r; +} + +static void kvm_msi_to_lapic_irq(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e, + struct kvm_lapic_irq *irq) +{ + struct msi_msg msg = { .address_lo = e->msi.address_lo, + .address_hi = e->msi.address_hi, + .data = e->msi.data }; + + trace_kvm_msi_set_irq(msg.address_lo | (kvm->arch.x2apic_format ? + (u64)msg.address_hi << 32 : 0), msg.data); + + irq->dest_id = x86_msi_msg_get_destid(&msg, kvm->arch.x2apic_format); + irq->vector = msg.arch_data.vector; + irq->dest_mode = kvm_lapic_irq_dest_mode(msg.arch_addr_lo.dest_mode_logical); + irq->trig_mode = msg.arch_data.is_level; + irq->delivery_mode = msg.arch_data.delivery_mode << 8; + irq->msi_redir_hint = msg.arch_addr_lo.redirect_hint; + irq->level = 1; + irq->shorthand = APIC_DEST_NOSHORT; +} + +static inline bool kvm_msi_route_invalid(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e) +{ + return kvm->arch.x2apic_format && (e->msi.address_hi & 0xff); +} + +int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, bool line_status) +{ + struct kvm_lapic_irq irq; + + if (kvm_msi_route_invalid(kvm, e)) + return -EINVAL; + + if (!level) + return -1; + + kvm_msi_to_lapic_irq(kvm, e, &irq); + + return kvm_irq_delivery_to_apic(kvm, NULL, &irq, NULL); +} + +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) +{ + struct kvm_lapic_irq irq; + int r; + + switch (e->type) { +#ifdef CONFIG_KVM_HYPERV + case KVM_IRQ_ROUTING_HV_SINT: + return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level, + line_status); +#endif + + case KVM_IRQ_ROUTING_MSI: + if (kvm_msi_route_invalid(kvm, e)) + return -EINVAL; + + kvm_msi_to_lapic_irq(kvm, e, &irq); + + if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r, NULL)) + return r; + break; + +#ifdef CONFIG_KVM_XEN + case KVM_IRQ_ROUTING_XEN_EVTCHN: + if (!level) + return -1; + + return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm); +#endif + default: + break; + } + + return -EWOULDBLOCK; +} + +int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event, + bool line_status) +{ + if (!irqchip_in_kernel(kvm)) + return -ENXIO; + + irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, + irq_event->irq, irq_event->level, + line_status); + return 0; +} + +bool kvm_arch_can_set_irq_routing(struct kvm *kvm) +{ + return irqchip_in_kernel(kvm); +} + +int kvm_set_routing_entry(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) +{ + /* We can't check irqchip_in_kernel() here as some callers are + * currently initializing the irqchip. Other callers should therefore + * check kvm_arch_can_set_irq_routing() before calling this function. + */ + switch (ue->type) { +#ifdef CONFIG_KVM_IOAPIC + case KVM_IRQ_ROUTING_IRQCHIP: + if (irqchip_split(kvm)) + return -EINVAL; + e->irqchip.pin = ue->u.irqchip.pin; + switch (ue->u.irqchip.irqchip) { + case KVM_IRQCHIP_PIC_SLAVE: + e->irqchip.pin += PIC_NUM_PINS / 2; + fallthrough; + case KVM_IRQCHIP_PIC_MASTER: + if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) + return -EINVAL; + e->set = kvm_pic_set_irq; + break; + case KVM_IRQCHIP_IOAPIC: + if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) + return -EINVAL; + e->set = kvm_ioapic_set_irq; + break; + default: + return -EINVAL; + } + e->irqchip.irqchip = ue->u.irqchip.irqchip; + break; +#endif + case KVM_IRQ_ROUTING_MSI: + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + + if (kvm_msi_route_invalid(kvm, e)) + return -EINVAL; + break; +#ifdef CONFIG_KVM_HYPERV + case KVM_IRQ_ROUTING_HV_SINT: + e->set = kvm_hv_synic_set_irq; + e->hv_sint.vcpu = ue->u.hv_sint.vcpu; + e->hv_sint.sint = ue->u.hv_sint.sint; + break; +#endif +#ifdef CONFIG_KVM_XEN + case KVM_IRQ_ROUTING_XEN_EVTCHN: + return kvm_xen_setup_evtchn(kvm, e, ue); +#endif + default: + return -EINVAL; + } + + return 0; +} + +bool kvm_intr_is_single_vcpu(struct kvm *kvm, struct kvm_lapic_irq *irq, + struct kvm_vcpu **dest_vcpu) +{ + int r = 0; + unsigned long i; + struct kvm_vcpu *vcpu; + + if (kvm_intr_is_single_vcpu_fast(kvm, irq, dest_vcpu)) + return true; + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!kvm_apic_present(vcpu)) + continue; + + if (!kvm_apic_match_dest(vcpu, NULL, irq->shorthand, + irq->dest_id, irq->dest_mode)) + continue; + + if (++r == 2) + return false; + + *dest_vcpu = vcpu; + } + + return r == 1; +} +EXPORT_SYMBOL_GPL(kvm_intr_is_single_vcpu); + +void kvm_scan_ioapic_irq(struct kvm_vcpu *vcpu, u32 dest_id, u16 dest_mode, + u8 vector, unsigned long *ioapic_handled_vectors) +{ + /* + * Intercept EOI if the vCPU is the target of the new IRQ routing, or + * the vCPU has a pending IRQ from the old routing, i.e. if the vCPU + * may receive a level-triggered IRQ in the future, or already received + * level-triggered IRQ. The EOI needs to be intercepted and forwarded + * to I/O APIC emulation so that the IRQ can be de-asserted. + */ + if (kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT, dest_id, dest_mode)) { + __set_bit(vector, ioapic_handled_vectors); + } else if (kvm_apic_pending_eoi(vcpu, vector)) { + __set_bit(vector, ioapic_handled_vectors); + + /* + * Track the highest pending EOI for which the vCPU is NOT the + * target in the new routing. Only the EOI for the IRQ that is + * in-flight (for the old routing) needs to be intercepted, any + * future IRQs that arrive on this vCPU will be coincidental to + * the level-triggered routing and don't need to be intercepted. + */ + if ((int)vector > vcpu->arch.highest_stale_pending_ioapic_eoi) + vcpu->arch.highest_stale_pending_ioapic_eoi = vector; + } +} + +void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, + ulong *ioapic_handled_vectors) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_kernel_irq_routing_entry *entry; + struct kvm_irq_routing_table *table; + u32 i, nr_ioapic_pins; + int idx; + + idx = srcu_read_lock(&kvm->irq_srcu); + table = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); + nr_ioapic_pins = min_t(u32, table->nr_rt_entries, + kvm->arch.nr_reserved_ioapic_pins); + for (i = 0; i < nr_ioapic_pins; ++i) { + hlist_for_each_entry(entry, &table->map[i], link) { + struct kvm_lapic_irq irq; + + if (entry->type != KVM_IRQ_ROUTING_MSI) + continue; + + kvm_msi_to_lapic_irq(vcpu->kvm, entry, &irq); + + if (!irq.trig_mode) + continue; + + kvm_scan_ioapic_irq(vcpu, irq.dest_id, irq.dest_mode, + irq.vector, ioapic_handled_vectors); + } + } + srcu_read_unlock(&kvm->irq_srcu, idx); +} + +void kvm_arch_irq_routing_update(struct kvm *kvm) +{ +#ifdef CONFIG_KVM_HYPERV + kvm_hv_irq_routing_update(kvm); +#endif + + if (irqchip_split(kvm)) + kvm_make_scan_ioapic_request(kvm); +} + +static int kvm_pi_update_irte(struct kvm_kernel_irqfd *irqfd, + struct kvm_kernel_irq_routing_entry *entry) +{ + unsigned int host_irq = irqfd->producer->irq; + struct kvm *kvm = irqfd->kvm; + struct kvm_vcpu *vcpu = NULL; + struct kvm_lapic_irq irq; + int r; + + if (WARN_ON_ONCE(!irqchip_in_kernel(kvm) || !kvm_arch_has_irq_bypass())) + return -EINVAL; + + if (entry && entry->type == KVM_IRQ_ROUTING_MSI) { + kvm_msi_to_lapic_irq(kvm, entry, &irq); + + /* + * Force remapped mode if hardware doesn't support posting the + * virtual interrupt to a vCPU. Only IRQs are postable (NMIs, + * SMIs, etc. are not), and neither AMD nor Intel IOMMUs support + * posting multicast/broadcast IRQs. If the interrupt can't be + * posted, the device MSI needs to be routed to the host so that + * the guest's desired interrupt can be synthesized by KVM. + * + * This means that KVM can only post lowest-priority interrupts + * if they have a single CPU as the destination, e.g. only if + * the guest has affined the interrupt to a single vCPU. + */ + if (!kvm_intr_is_single_vcpu(kvm, &irq, &vcpu) || + !kvm_irq_is_postable(&irq)) + vcpu = NULL; + } + + if (!irqfd->irq_bypass_vcpu && !vcpu) + return 0; + + r = kvm_x86_call(pi_update_irte)(irqfd, irqfd->kvm, host_irq, irqfd->gsi, + vcpu, irq.vector); + if (r) { + WARN_ON_ONCE(irqfd->irq_bypass_vcpu && !vcpu); + irqfd->irq_bypass_vcpu = NULL; + return r; + } + + irqfd->irq_bypass_vcpu = vcpu; + + trace_kvm_pi_irte_update(host_irq, vcpu, irqfd->gsi, irq.vector, !!vcpu); + return 0; +} + +int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons, + struct irq_bypass_producer *prod) +{ + struct kvm_kernel_irqfd *irqfd = + container_of(cons, struct kvm_kernel_irqfd, consumer); + struct kvm *kvm = irqfd->kvm; + int ret = 0; + + spin_lock_irq(&kvm->irqfds.lock); + irqfd->producer = prod; + + if (!kvm->arch.nr_possible_bypass_irqs++) + kvm_x86_call(pi_start_bypass)(kvm); + + if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) { + ret = kvm_pi_update_irte(irqfd, &irqfd->irq_entry); + if (ret) + kvm->arch.nr_possible_bypass_irqs--; + } + spin_unlock_irq(&kvm->irqfds.lock); + + return ret; +} + +void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons, + struct irq_bypass_producer *prod) +{ + struct kvm_kernel_irqfd *irqfd = + container_of(cons, struct kvm_kernel_irqfd, consumer); + struct kvm *kvm = irqfd->kvm; + int ret; + + WARN_ON(irqfd->producer != prod); + + /* + * If the producer of an IRQ that is currently being posted to a vCPU + * is unregistered, change the associated IRTE back to remapped mode as + * the IRQ has been released (or repurposed) by the device driver, i.e. + * KVM must relinquish control of the IRTE. + */ + spin_lock_irq(&kvm->irqfds.lock); + + if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) { + ret = kvm_pi_update_irte(irqfd, NULL); + if (ret) + pr_info("irq bypass consumer (eventfd %p) unregistration fails: %d\n", + irqfd->consumer.eventfd, ret); + } + irqfd->producer = NULL; + + kvm->arch.nr_possible_bypass_irqs--; + + spin_unlock_irq(&kvm->irqfds.lock); +} + +void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd, + struct kvm_kernel_irq_routing_entry *old, + struct kvm_kernel_irq_routing_entry *new) +{ + if (new->type != KVM_IRQ_ROUTING_MSI && + old->type != KVM_IRQ_ROUTING_MSI) + return; + + if (old->type == KVM_IRQ_ROUTING_MSI && + new->type == KVM_IRQ_ROUTING_MSI && + !memcmp(&old->msi, &new->msi, sizeof(new->msi))) + return; + + kvm_pi_update_irte(irqfd, new); +} + +#ifdef CONFIG_KVM_IOAPIC +#define IOAPIC_ROUTING_ENTRY(irq) \ + { .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP, \ + .u.irqchip = { .irqchip = KVM_IRQCHIP_IOAPIC, .pin = (irq) } } +#define ROUTING_ENTRY1(irq) IOAPIC_ROUTING_ENTRY(irq) + +#define PIC_ROUTING_ENTRY(irq) \ + { .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP, \ + .u.irqchip = { .irqchip = SELECT_PIC(irq), .pin = (irq) % 8 } } +#define ROUTING_ENTRY2(irq) \ + IOAPIC_ROUTING_ENTRY(irq), PIC_ROUTING_ENTRY(irq) + +static const struct kvm_irq_routing_entry default_routing[] = { + ROUTING_ENTRY2(0), ROUTING_ENTRY2(1), + ROUTING_ENTRY2(2), ROUTING_ENTRY2(3), + ROUTING_ENTRY2(4), ROUTING_ENTRY2(5), + ROUTING_ENTRY2(6), ROUTING_ENTRY2(7), + ROUTING_ENTRY2(8), ROUTING_ENTRY2(9), + ROUTING_ENTRY2(10), ROUTING_ENTRY2(11), + ROUTING_ENTRY2(12), ROUTING_ENTRY2(13), + ROUTING_ENTRY2(14), ROUTING_ENTRY2(15), + ROUTING_ENTRY1(16), ROUTING_ENTRY1(17), + ROUTING_ENTRY1(18), ROUTING_ENTRY1(19), + ROUTING_ENTRY1(20), ROUTING_ENTRY1(21), + ROUTING_ENTRY1(22), ROUTING_ENTRY1(23), +}; + +int kvm_setup_default_ioapic_and_pic_routing(struct kvm *kvm) +{ + return kvm_set_irq_routing(kvm, default_routing, + ARRAY_SIZE(default_routing), 0); +} + +int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) +{ + struct kvm_pic *pic = kvm->arch.vpic; + int r; + + r = 0; + switch (chip->chip_id) { + case KVM_IRQCHIP_PIC_MASTER: + memcpy(&chip->chip.pic, &pic->pics[0], + sizeof(struct kvm_pic_state)); + break; + case KVM_IRQCHIP_PIC_SLAVE: + memcpy(&chip->chip.pic, &pic->pics[1], + sizeof(struct kvm_pic_state)); + break; + case KVM_IRQCHIP_IOAPIC: + kvm_get_ioapic(kvm, &chip->chip.ioapic); + break; + default: + r = -EINVAL; + break; + } + return r; +} + +int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) +{ + struct kvm_pic *pic = kvm->arch.vpic; + int r; + + r = 0; + switch (chip->chip_id) { + case KVM_IRQCHIP_PIC_MASTER: + spin_lock(&pic->lock); + memcpy(&pic->pics[0], &chip->chip.pic, + sizeof(struct kvm_pic_state)); + spin_unlock(&pic->lock); + break; + case KVM_IRQCHIP_PIC_SLAVE: + spin_lock(&pic->lock); + memcpy(&pic->pics[1], &chip->chip.pic, + sizeof(struct kvm_pic_state)); + spin_unlock(&pic->lock); + break; + case KVM_IRQCHIP_IOAPIC: + kvm_set_ioapic(kvm, &chip->chip.ioapic); + break; + default: + r = -EINVAL; + break; + } + kvm_pic_update_irq(pic); + return r; +} +#endif diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h index 76d46b2f41dd..5e62c1f79ce6 100644 --- a/arch/x86/kvm/irq.h +++ b/arch/x86/kvm/irq.h @@ -18,6 +18,8 @@ #include #include "lapic.h" +#ifdef CONFIG_KVM_IOAPIC + #define PIC_NUM_PINS 16 #define SELECT_PIC(irq) \ ((irq) < 8 ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE) @@ -63,6 +65,34 @@ int kvm_pic_init(struct kvm *kvm); void kvm_pic_destroy(struct kvm *kvm); int kvm_pic_read_irq(struct kvm *kvm); void kvm_pic_update_irq(struct kvm_pic *s); +int kvm_pic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status); + +int kvm_setup_default_ioapic_and_pic_routing(struct kvm *kvm); + +int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip); +int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip); + +static inline int irqchip_full(struct kvm *kvm) +{ + int mode = kvm->arch.irqchip_mode; + + /* Matches smp_wmb() when setting irqchip_mode */ + smp_rmb(); + return mode == KVM_IRQCHIP_KERNEL; +} +#else /* CONFIG_KVM_IOAPIC */ +static __always_inline int irqchip_full(struct kvm *kvm) +{ + return false; +} +#endif + +static inline int pic_in_kernel(struct kvm *kvm) +{ + return irqchip_full(kvm); +} + static inline int irqchip_split(struct kvm *kvm) { @@ -73,20 +103,6 @@ static inline int irqchip_split(struct kvm *kvm) return mode == KVM_IRQCHIP_SPLIT; } -static inline int irqchip_kernel(struct kvm *kvm) -{ - int mode = kvm->arch.irqchip_mode; - - /* Matches smp_wmb() when setting irqchip_mode */ - smp_rmb(); - return mode == KVM_IRQCHIP_KERNEL; -} - -static inline int pic_in_kernel(struct kvm *kvm) -{ - return irqchip_kernel(kvm); -} - static inline int irqchip_in_kernel(struct kvm *kvm) { int mode = kvm->arch.irqchip_mode; @@ -105,7 +121,6 @@ void __kvm_migrate_timers(struct kvm_vcpu *vcpu); int apic_has_pending_timer(struct kvm_vcpu *vcpu); -int kvm_setup_default_irq_routing(struct kvm *kvm); int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, struct kvm_lapic_irq *irq, struct dest_map *dest_map); diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c deleted file mode 100644 index d6d792b5d1bd..000000000000 --- a/arch/x86/kvm/irq_comm.c +++ /dev/null @@ -1,469 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * irq_comm.c: Common API for in kernel interrupt controller - * Copyright (c) 2007, Intel Corporation. - * - * Authors: - * Yaozu (Eddie) Dong - * - * Copyright 2010 Red Hat, Inc. and/or its affiliates. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include - -#include - -#include "irq.h" - -#include "ioapic.h" - -#include "lapic.h" - -#include "hyperv.h" -#include "x86.h" -#include "xen.h" - -static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level, - bool line_status) -{ - struct kvm_pic *pic = kvm->arch.vpic; - return kvm_pic_set_irq(pic, e->irqchip.pin, irq_source_id, level); -} - -static int kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level, - bool line_status) -{ - struct kvm_ioapic *ioapic = kvm->arch.vioapic; - return kvm_ioapic_set_irq(ioapic, e->irqchip.pin, irq_source_id, level, - line_status); -} - -int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, - struct kvm_lapic_irq *irq, struct dest_map *dest_map) -{ - int r = -1; - struct kvm_vcpu *vcpu, *lowest = NULL; - unsigned long i, dest_vcpu_bitmap[BITS_TO_LONGS(KVM_MAX_VCPUS)]; - unsigned int dest_vcpus = 0; - - if (kvm_irq_delivery_to_apic_fast(kvm, src, irq, &r, dest_map)) - return r; - - if (irq->dest_mode == APIC_DEST_PHYSICAL && - irq->dest_id == 0xff && kvm_lowest_prio_delivery(irq)) { - pr_info("apic: phys broadcast and lowest prio\n"); - irq->delivery_mode = APIC_DM_FIXED; - } - - memset(dest_vcpu_bitmap, 0, sizeof(dest_vcpu_bitmap)); - - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!kvm_apic_present(vcpu)) - continue; - - if (!kvm_apic_match_dest(vcpu, src, irq->shorthand, - irq->dest_id, irq->dest_mode)) - continue; - - if (!kvm_lowest_prio_delivery(irq)) { - if (r < 0) - r = 0; - r += kvm_apic_set_irq(vcpu, irq, dest_map); - } else if (kvm_apic_sw_enabled(vcpu->arch.apic)) { - if (!kvm_vector_hashing_enabled()) { - if (!lowest) - lowest = vcpu; - else if (kvm_apic_compare_prio(vcpu, lowest) < 0) - lowest = vcpu; - } else { - __set_bit(i, dest_vcpu_bitmap); - dest_vcpus++; - } - } - } - - if (dest_vcpus != 0) { - int idx = kvm_vector_to_index(irq->vector, dest_vcpus, - dest_vcpu_bitmap, KVM_MAX_VCPUS); - - lowest = kvm_get_vcpu(kvm, idx); - } - - if (lowest) - r = kvm_apic_set_irq(lowest, irq, dest_map); - - return r; -} - -void kvm_set_msi_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, - struct kvm_lapic_irq *irq) -{ - struct msi_msg msg = { .address_lo = e->msi.address_lo, - .address_hi = e->msi.address_hi, - .data = e->msi.data }; - - trace_kvm_msi_set_irq(msg.address_lo | (kvm->arch.x2apic_format ? - (u64)msg.address_hi << 32 : 0), msg.data); - - irq->dest_id = x86_msi_msg_get_destid(&msg, kvm->arch.x2apic_format); - irq->vector = msg.arch_data.vector; - irq->dest_mode = kvm_lapic_irq_dest_mode(msg.arch_addr_lo.dest_mode_logical); - irq->trig_mode = msg.arch_data.is_level; - irq->delivery_mode = msg.arch_data.delivery_mode << 8; - irq->msi_redir_hint = msg.arch_addr_lo.redirect_hint; - irq->level = 1; - irq->shorthand = APIC_DEST_NOSHORT; -} -EXPORT_SYMBOL_GPL(kvm_set_msi_irq); - -static inline bool kvm_msi_route_invalid(struct kvm *kvm, - struct kvm_kernel_irq_routing_entry *e) -{ - return kvm->arch.x2apic_format && (e->msi.address_hi & 0xff); -} - -int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level, bool line_status) -{ - struct kvm_lapic_irq irq; - - if (kvm_msi_route_invalid(kvm, e)) - return -EINVAL; - - if (!level) - return -1; - - kvm_set_msi_irq(kvm, e, &irq); - - return kvm_irq_delivery_to_apic(kvm, NULL, &irq, NULL); -} - -#ifdef CONFIG_KVM_HYPERV -static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level, - bool line_status) -{ - if (!level) - return -1; - - return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint); -} -#endif - -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) -{ - struct kvm_lapic_irq irq; - int r; - - switch (e->type) { -#ifdef CONFIG_KVM_HYPERV - case KVM_IRQ_ROUTING_HV_SINT: - return kvm_hv_set_sint(e, kvm, irq_source_id, level, - line_status); -#endif - - case KVM_IRQ_ROUTING_MSI: - if (kvm_msi_route_invalid(kvm, e)) - return -EINVAL; - - kvm_set_msi_irq(kvm, e, &irq); - - if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r, NULL)) - return r; - break; - -#ifdef CONFIG_KVM_XEN - case KVM_IRQ_ROUTING_XEN_EVTCHN: - if (!level) - return -1; - - return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm); -#endif - default: - break; - } - - return -EWOULDBLOCK; -} - -int kvm_request_irq_source_id(struct kvm *kvm) -{ - unsigned long *bitmap = &kvm->arch.irq_sources_bitmap; - int irq_source_id; - - mutex_lock(&kvm->irq_lock); - irq_source_id = find_first_zero_bit(bitmap, BITS_PER_LONG); - - if (irq_source_id >= BITS_PER_LONG) { - pr_warn("exhausted allocatable IRQ sources!\n"); - irq_source_id = -EFAULT; - goto unlock; - } - - ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); - ASSERT(irq_source_id != KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID); - set_bit(irq_source_id, bitmap); -unlock: - mutex_unlock(&kvm->irq_lock); - - return irq_source_id; -} - -void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) -{ - ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); - ASSERT(irq_source_id != KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID); - - mutex_lock(&kvm->irq_lock); - if (irq_source_id < 0 || - irq_source_id >= BITS_PER_LONG) { - pr_err("IRQ source ID out of range!\n"); - goto unlock; - } - clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); - if (!irqchip_kernel(kvm)) - goto unlock; - - kvm_ioapic_clear_all(kvm->arch.vioapic, irq_source_id); - kvm_pic_clear_all(kvm->arch.vpic, irq_source_id); -unlock: - mutex_unlock(&kvm->irq_lock); -} - -void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn) -{ - mutex_lock(&kvm->irq_lock); - kimn->irq = irq; - hlist_add_head_rcu(&kimn->link, &kvm->arch.mask_notifier_list); - mutex_unlock(&kvm->irq_lock); -} - -void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn) -{ - mutex_lock(&kvm->irq_lock); - hlist_del_rcu(&kimn->link); - mutex_unlock(&kvm->irq_lock); - synchronize_srcu(&kvm->irq_srcu); -} - -void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, - bool mask) -{ - struct kvm_irq_mask_notifier *kimn; - int idx, gsi; - - idx = srcu_read_lock(&kvm->irq_srcu); - gsi = kvm_irq_map_chip_pin(kvm, irqchip, pin); - if (gsi != -1) - hlist_for_each_entry_rcu(kimn, &kvm->arch.mask_notifier_list, link) - if (kimn->irq == gsi) - kimn->func(kimn, mask); - srcu_read_unlock(&kvm->irq_srcu, idx); -} - -bool kvm_arch_can_set_irq_routing(struct kvm *kvm) -{ - return irqchip_in_kernel(kvm); -} - -int kvm_set_routing_entry(struct kvm *kvm, - struct kvm_kernel_irq_routing_entry *e, - const struct kvm_irq_routing_entry *ue) -{ - /* We can't check irqchip_in_kernel() here as some callers are - * currently initializing the irqchip. Other callers should therefore - * check kvm_arch_can_set_irq_routing() before calling this function. - */ - switch (ue->type) { - case KVM_IRQ_ROUTING_IRQCHIP: - if (irqchip_split(kvm)) - return -EINVAL; - e->irqchip.pin = ue->u.irqchip.pin; - switch (ue->u.irqchip.irqchip) { - case KVM_IRQCHIP_PIC_SLAVE: - e->irqchip.pin += PIC_NUM_PINS / 2; - fallthrough; - case KVM_IRQCHIP_PIC_MASTER: - if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) - return -EINVAL; - e->set = kvm_set_pic_irq; - break; - case KVM_IRQCHIP_IOAPIC: - if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) - return -EINVAL; - e->set = kvm_set_ioapic_irq; - break; - default: - return -EINVAL; - } - e->irqchip.irqchip = ue->u.irqchip.irqchip; - break; - case KVM_IRQ_ROUTING_MSI: - e->set = kvm_set_msi; - e->msi.address_lo = ue->u.msi.address_lo; - e->msi.address_hi = ue->u.msi.address_hi; - e->msi.data = ue->u.msi.data; - - if (kvm_msi_route_invalid(kvm, e)) - return -EINVAL; - break; -#ifdef CONFIG_KVM_HYPERV - case KVM_IRQ_ROUTING_HV_SINT: - e->set = kvm_hv_set_sint; - e->hv_sint.vcpu = ue->u.hv_sint.vcpu; - e->hv_sint.sint = ue->u.hv_sint.sint; - break; -#endif -#ifdef CONFIG_KVM_XEN - case KVM_IRQ_ROUTING_XEN_EVTCHN: - return kvm_xen_setup_evtchn(kvm, e, ue); -#endif - default: - return -EINVAL; - } - - return 0; -} - -bool kvm_intr_is_single_vcpu(struct kvm *kvm, struct kvm_lapic_irq *irq, - struct kvm_vcpu **dest_vcpu) -{ - int r = 0; - unsigned long i; - struct kvm_vcpu *vcpu; - - if (kvm_intr_is_single_vcpu_fast(kvm, irq, dest_vcpu)) - return true; - - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!kvm_apic_present(vcpu)) - continue; - - if (!kvm_apic_match_dest(vcpu, NULL, irq->shorthand, - irq->dest_id, irq->dest_mode)) - continue; - - if (++r == 2) - return false; - - *dest_vcpu = vcpu; - } - - return r == 1; -} -EXPORT_SYMBOL_GPL(kvm_intr_is_single_vcpu); - -#define IOAPIC_ROUTING_ENTRY(irq) \ - { .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP, \ - .u.irqchip = { .irqchip = KVM_IRQCHIP_IOAPIC, .pin = (irq) } } -#define ROUTING_ENTRY1(irq) IOAPIC_ROUTING_ENTRY(irq) - -#define PIC_ROUTING_ENTRY(irq) \ - { .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP, \ - .u.irqchip = { .irqchip = SELECT_PIC(irq), .pin = (irq) % 8 } } -#define ROUTING_ENTRY2(irq) \ - IOAPIC_ROUTING_ENTRY(irq), PIC_ROUTING_ENTRY(irq) - -static const struct kvm_irq_routing_entry default_routing[] = { - ROUTING_ENTRY2(0), ROUTING_ENTRY2(1), - ROUTING_ENTRY2(2), ROUTING_ENTRY2(3), - ROUTING_ENTRY2(4), ROUTING_ENTRY2(5), - ROUTING_ENTRY2(6), ROUTING_ENTRY2(7), - ROUTING_ENTRY2(8), ROUTING_ENTRY2(9), - ROUTING_ENTRY2(10), ROUTING_ENTRY2(11), - ROUTING_ENTRY2(12), ROUTING_ENTRY2(13), - ROUTING_ENTRY2(14), ROUTING_ENTRY2(15), - ROUTING_ENTRY1(16), ROUTING_ENTRY1(17), - ROUTING_ENTRY1(18), ROUTING_ENTRY1(19), - ROUTING_ENTRY1(20), ROUTING_ENTRY1(21), - ROUTING_ENTRY1(22), ROUTING_ENTRY1(23), -}; - -int kvm_setup_default_irq_routing(struct kvm *kvm) -{ - return kvm_set_irq_routing(kvm, default_routing, - ARRAY_SIZE(default_routing), 0); -} - -void kvm_arch_post_irq_routing_update(struct kvm *kvm) -{ - if (!irqchip_split(kvm)) - return; - kvm_make_scan_ioapic_request(kvm); -} - -void kvm_scan_ioapic_irq(struct kvm_vcpu *vcpu, u32 dest_id, u16 dest_mode, - u8 vector, unsigned long *ioapic_handled_vectors) -{ - /* - * Intercept EOI if the vCPU is the target of the new IRQ routing, or - * the vCPU has a pending IRQ from the old routing, i.e. if the vCPU - * may receive a level-triggered IRQ in the future, or already received - * level-triggered IRQ. The EOI needs to be intercepted and forwarded - * to I/O APIC emulation so that the IRQ can be de-asserted. - */ - if (kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT, dest_id, dest_mode)) { - __set_bit(vector, ioapic_handled_vectors); - } else if (kvm_apic_pending_eoi(vcpu, vector)) { - __set_bit(vector, ioapic_handled_vectors); - - /* - * Track the highest pending EOI for which the vCPU is NOT the - * target in the new routing. Only the EOI for the IRQ that is - * in-flight (for the old routing) needs to be intercepted, any - * future IRQs that arrive on this vCPU will be coincidental to - * the level-triggered routing and don't need to be intercepted. - */ - if ((int)vector > vcpu->arch.highest_stale_pending_ioapic_eoi) - vcpu->arch.highest_stale_pending_ioapic_eoi = vector; - } -} - -void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, - ulong *ioapic_handled_vectors) -{ - struct kvm *kvm = vcpu->kvm; - struct kvm_kernel_irq_routing_entry *entry; - struct kvm_irq_routing_table *table; - u32 i, nr_ioapic_pins; - int idx; - - idx = srcu_read_lock(&kvm->irq_srcu); - table = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); - nr_ioapic_pins = min_t(u32, table->nr_rt_entries, - kvm->arch.nr_reserved_ioapic_pins); - for (i = 0; i < nr_ioapic_pins; ++i) { - hlist_for_each_entry(entry, &table->map[i], link) { - struct kvm_lapic_irq irq; - - if (entry->type != KVM_IRQ_ROUTING_MSI) - continue; - - kvm_set_msi_irq(vcpu->kvm, entry, &irq); - - if (!irq.trig_mode) - continue; - - kvm_scan_ioapic_irq(vcpu, irq.dest_id, irq.dest_mode, - irq.vector, ioapic_handled_vectors); - } - } - srcu_read_unlock(&kvm->irq_srcu, idx); -} - -void kvm_arch_irq_routing_update(struct kvm *kvm) -{ -#ifdef CONFIG_KVM_HYPERV - kvm_hv_irq_routing_update(kvm); -#endif -} diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 73418dc0ebb2..5fc437341e03 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -55,9 +56,6 @@ /* 14 is the version for Xeon and Pentium 8.4.8*/ #define APIC_VERSION 0x14UL #define LAPIC_MMIO_LENGTH (1 << 12) -/* followed define is not in apicdef.h */ -#define MAX_APIC_VECTOR 256 -#define APIC_VECTORS_PER_REG 32 /* * Enable local APIC timer advancement (tscdeadline mode only) with adaptive @@ -79,42 +77,20 @@ module_param(lapic_timer_advance, bool, 0444); static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data); static int kvm_lapic_msr_write(struct kvm_lapic *apic, u32 reg, u64 data); -static inline void __kvm_lapic_set_reg(char *regs, int reg_off, u32 val) -{ - *((u32 *) (regs + reg_off)) = val; -} - static inline void kvm_lapic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val) { - __kvm_lapic_set_reg(apic->regs, reg_off, val); -} - -static __always_inline u64 __kvm_lapic_get_reg64(char *regs, int reg) -{ - BUILD_BUG_ON(reg != APIC_ICR); - return *((u64 *) (regs + reg)); + apic_set_reg(apic->regs, reg_off, val); } static __always_inline u64 kvm_lapic_get_reg64(struct kvm_lapic *apic, int reg) { - return __kvm_lapic_get_reg64(apic->regs, reg); -} - -static __always_inline void __kvm_lapic_set_reg64(char *regs, int reg, u64 val) -{ - BUILD_BUG_ON(reg != APIC_ICR); - *((u64 *) (regs + reg)) = val; + return apic_get_reg64(apic->regs, reg); } static __always_inline void kvm_lapic_set_reg64(struct kvm_lapic *apic, int reg, u64 val) { - __kvm_lapic_set_reg64(apic->regs, reg, val); -} - -static inline int apic_test_vector(int vec, void *bitmap) -{ - return test_bit(VEC_POS(vec), (bitmap) + REG_POS(vec)); + apic_set_reg64(apic->regs, reg, val); } bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector) @@ -125,16 +101,6 @@ bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector) apic_test_vector(vector, apic->regs + APIC_IRR); } -static inline int __apic_test_and_set_vector(int vec, void *bitmap) -{ - return __test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec)); -} - -static inline int __apic_test_and_clear_vector(int vec, void *bitmap) -{ - return __test_and_clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec)); -} - __read_mostly DEFINE_STATIC_KEY_FALSE(kvm_has_noapic_vcpu); EXPORT_SYMBOL_GPL(kvm_has_noapic_vcpu); @@ -626,21 +592,6 @@ static const unsigned int apic_lvt_mask[KVM_APIC_MAX_NR_LVT_ENTRIES] = { [LVT_CMCI] = LVT_MASK | APIC_MODE_MASK }; -static int find_highest_vector(void *bitmap) -{ - int vec; - u32 *reg; - - for (vec = MAX_APIC_VECTOR - APIC_VECTORS_PER_REG; - vec >= 0; vec -= APIC_VECTORS_PER_REG) { - reg = bitmap + REG_POS(vec); - if (*reg) - return __fls(*reg) + vec; - } - - return -1; -} - static u8 count_vectors(void *bitmap) { int vec; @@ -648,7 +599,7 @@ static u8 count_vectors(void *bitmap) u8 count = 0; for (vec = 0; vec < MAX_APIC_VECTOR; vec += APIC_VECTORS_PER_REG) { - reg = bitmap + REG_POS(vec); + reg = bitmap + APIC_VECTOR_TO_REG_OFFSET(vec); count += hweight32(*reg); } @@ -706,7 +657,7 @@ EXPORT_SYMBOL_GPL(kvm_apic_update_irr); static inline int apic_search_irr(struct kvm_lapic *apic) { - return find_highest_vector(apic->regs + APIC_IRR); + return apic_find_highest_vector(apic->regs + APIC_IRR); } static inline int apic_find_highest_irr(struct kvm_lapic *apic) @@ -729,10 +680,10 @@ static inline int apic_find_highest_irr(struct kvm_lapic *apic) static inline void apic_clear_irr(int vec, struct kvm_lapic *apic) { if (unlikely(apic->apicv_active)) { - kvm_lapic_clear_vector(vec, apic->regs + APIC_IRR); + apic_clear_vector(vec, apic->regs + APIC_IRR); } else { apic->irr_pending = false; - kvm_lapic_clear_vector(vec, apic->regs + APIC_IRR); + apic_clear_vector(vec, apic->regs + APIC_IRR); if (apic_search_irr(apic) != -1) apic->irr_pending = true; } @@ -744,9 +695,15 @@ void kvm_apic_clear_irr(struct kvm_vcpu *vcpu, int vec) } EXPORT_SYMBOL_GPL(kvm_apic_clear_irr); +static void *apic_vector_to_isr(int vec, struct kvm_lapic *apic) +{ + return apic->regs + APIC_ISR + APIC_VECTOR_TO_REG_OFFSET(vec); +} + static inline void apic_set_isr(int vec, struct kvm_lapic *apic) { - if (__apic_test_and_set_vector(vec, apic->regs + APIC_ISR)) + if (__test_and_set_bit(APIC_VECTOR_TO_BIT_NUMBER(vec), + apic_vector_to_isr(vec, apic))) return; /* @@ -781,7 +738,7 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic) if (likely(apic->highest_isr_cache != -1)) return apic->highest_isr_cache; - result = find_highest_vector(apic->regs + APIC_ISR); + result = apic_find_highest_vector(apic->regs + APIC_ISR); ASSERT(result == -1 || result >= 16); return result; @@ -789,7 +746,8 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic) static inline void apic_clear_isr(int vec, struct kvm_lapic *apic) { - if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR)) + if (!__test_and_clear_bit(APIC_VECTOR_TO_BIT_NUMBER(vec), + apic_vector_to_isr(vec, apic))) return; /* @@ -852,6 +810,8 @@ static int __pv_send_ipi(unsigned long *ipi_bitmap, struct kvm_apic_map *map, 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]) { @@ -1332,11 +1292,9 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, if (apic_test_vector(vector, apic->regs + APIC_TMR) != !!trig_mode) { if (trig_mode) - kvm_lapic_set_vector(vector, - apic->regs + APIC_TMR); + apic_set_vector(vector, apic->regs + APIC_TMR); else - kvm_lapic_clear_vector(vector, - apic->regs + APIC_TMR); + apic_clear_vector(vector, apic->regs + APIC_TMR); } kvm_x86_call(deliver_interrupt)(apic, delivery_mode, @@ -1455,7 +1413,7 @@ static bool kvm_ioapic_handles_vector(struct kvm_lapic *apic, int vector) static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector) { - int trigger_mode; + int __maybe_unused trigger_mode; /* Eoi the ioapic only if the ioapic doesn't own the vector. */ if (!kvm_ioapic_handles_vector(apic, vector)) @@ -1476,12 +1434,14 @@ static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector) return; } +#ifdef CONFIG_KVM_IOAPIC if (apic_test_vector(vector, apic->regs + APIC_TMR)) trigger_mode = IOAPIC_LEVEL_TRIG; else trigger_mode = IOAPIC_EDGE_TRIG; kvm_ioapic_update_eoi(apic->vcpu, vector, trigger_mode); +#endif } static int apic_set_eoi(struct kvm_lapic *apic) @@ -3084,12 +3044,12 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu, if (!kvm_x86_ops.x2apic_icr_is_split) { if (set) { - icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) | - (u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32; - __kvm_lapic_set_reg64(s->regs, APIC_ICR, icr); + icr = apic_get_reg(s->regs, APIC_ICR) | + (u64)apic_get_reg(s->regs, APIC_ICR2) << 32; + apic_set_reg64(s->regs, APIC_ICR, icr); } else { - icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR); - __kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32); + icr = apic_get_reg64(s->regs, APIC_ICR); + apic_set_reg(s->regs, APIC_ICR2, icr >> 32); } } } @@ -3105,8 +3065,7 @@ int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) * Get calculated timer current count for remaining timer period (if * any) and store it in the returned register set. */ - __kvm_lapic_set_reg(s->regs, APIC_TMCCT, - __apic_read(vcpu->arch.apic, APIC_TMCCT)); + apic_set_reg(s->regs, APIC_TMCCT, __apic_read(vcpu->arch.apic, APIC_TMCCT)); return kvm_apic_state_fixup(vcpu, s, false); } @@ -3146,8 +3105,11 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) kvm_x86_call(hwapic_isr_update)(vcpu, apic_find_highest_isr(apic)); } kvm_make_request(KVM_REQ_EVENT, vcpu); + +#ifdef CONFIG_KVM_IOAPIC if (ioapic_in_kernel(vcpu->kvm)) kvm_rtc_eoi_tracking_restore_one(vcpu); +#endif vcpu->arch.apic_arb_prio = 0; diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 4ce30db65828..72de14527698 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -4,6 +4,8 @@ #include +#include + #include #include "hyperv.h" @@ -21,6 +23,8 @@ #define APIC_BROADCAST 0xFF #define X2APIC_BROADCAST 0xFFFFFFFFul +#define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4)) + enum lapic_mode { LAPIC_MODE_DISABLED = 0, LAPIC_MODE_INVALID = X2APIC_ENABLE, @@ -145,22 +149,9 @@ void kvm_lapic_exit(void); u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); -#define VEC_POS(v) ((v) & (32 - 1)) -#define REG_POS(v) (((v) >> 5) << 4) - -static inline void kvm_lapic_clear_vector(int vec, void *bitmap) -{ - clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec)); -} - -static inline void kvm_lapic_set_vector(int vec, void *bitmap) -{ - set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec)); -} - static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) { - kvm_lapic_set_vector(vec, apic->regs + APIC_IRR); + apic_set_vector(vec, apic->regs + APIC_IRR); /* * irr_pending must be true if any interrupt is pending; set it after * APIC_IRR to avoid race with apic_clear_irr @@ -168,14 +159,9 @@ static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) apic->irr_pending = true; } -static inline u32 __kvm_lapic_get_reg(char *regs, int reg_off) -{ - return *((u32 *) (regs + reg_off)); -} - static inline u32 kvm_lapic_get_reg(struct kvm_lapic *apic, int reg_off) { - return __kvm_lapic_get_reg(apic->regs, reg_off); + return apic_get_reg(apic->regs, reg_off); } DECLARE_STATIC_KEY_FALSE(kvm_has_noapic_vcpu); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 4e06e2e89a8f..6e838cb6c9e1 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -1983,14 +1983,35 @@ static bool sp_has_gptes(struct kvm_mmu_page *sp) return true; } +static __ro_after_init HLIST_HEAD(empty_page_hash); + +static struct hlist_head *kvm_get_mmu_page_hash(struct kvm *kvm, gfn_t gfn) +{ + /* + * Ensure the load of the hash table pointer itself is ordered before + * loads to walk the table. The pointer is set at runtime outside of + * mmu_lock when the TDP MMU is enabled, i.e. when the hash table of + * shadow pages becomes necessary only when KVM needs to shadow L1's + * TDP for an L2 guest. Pairs with the smp_store_release() in + * kvm_mmu_alloc_page_hash(). + */ + struct hlist_head *page_hash = smp_load_acquire(&kvm->arch.mmu_page_hash); + + lockdep_assert_held(&kvm->mmu_lock); + + if (!page_hash) + return &empty_page_hash; + + return &page_hash[kvm_page_table_hashfn(gfn)]; +} + #define for_each_valid_sp(_kvm, _sp, _list) \ hlist_for_each_entry(_sp, _list, hash_link) \ if (is_obsolete_sp((_kvm), (_sp))) { \ } else #define for_each_gfn_valid_sp_with_gptes(_kvm, _sp, _gfn) \ - for_each_valid_sp(_kvm, _sp, \ - &(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)]) \ + for_each_valid_sp(_kvm, _sp, kvm_get_mmu_page_hash(_kvm, _gfn)) \ if ((_sp)->gfn != (_gfn) || !sp_has_gptes(_sp)) {} else static bool kvm_sync_page_check(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) @@ -2358,6 +2379,12 @@ static struct kvm_mmu_page *__kvm_mmu_get_shadow_page(struct kvm *kvm, struct kvm_mmu_page *sp; bool created = false; + /* + * No need for memory barriers, unlike in kvm_get_mmu_page_hash(), as + * mmu_page_hash must be set prior to creating the first shadow root, + * i.e. reaching this point is fully serialized by slots_arch_lock. + */ + BUG_ON(!kvm->arch.mmu_page_hash); sp_list = &kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; sp = kvm_mmu_find_shadow_page(kvm, vcpu, gfn, sp_list, role); @@ -3882,6 +3909,28 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu) return r; } +static int kvm_mmu_alloc_page_hash(struct kvm *kvm) +{ + struct hlist_head *h; + + if (kvm->arch.mmu_page_hash) + return 0; + + h = kvcalloc(KVM_NUM_MMU_PAGES, sizeof(*h), GFP_KERNEL_ACCOUNT); + if (!h) + return -ENOMEM; + + /* + * Ensure the hash table pointer is set only after all stores to zero + * the memory are retired. Pairs with the smp_load_acquire() in + * kvm_get_mmu_page_hash(). Note, mmu_lock must be held for write to + * add (or remove) shadow pages, and so readers are guaranteed to see + * an empty list for their current mmu_lock critical section. + */ + smp_store_release(&kvm->arch.mmu_page_hash, h); + return 0; +} + static int mmu_first_shadow_root_alloc(struct kvm *kvm) { struct kvm_memslots *slots; @@ -3901,9 +3950,13 @@ static int mmu_first_shadow_root_alloc(struct kvm *kvm) if (kvm_shadow_root_allocated(kvm)) goto out_unlock; + r = kvm_mmu_alloc_page_hash(kvm); + if (r) + goto out_unlock; + /* - * Check if anything actually needs to be allocated, e.g. all metadata - * will be allocated upfront if TDP is disabled. + * Check if memslot metadata actually needs to be allocated, e.g. all + * metadata will be allocated upfront if TDP is disabled. */ if (kvm_memslots_have_rmaps(kvm) && kvm_page_track_write_tracking_enabled(kvm)) @@ -6682,15 +6735,22 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm) kvm_tdp_mmu_zap_invalidated_roots(kvm, true); } -void kvm_mmu_init_vm(struct kvm *kvm) +int kvm_mmu_init_vm(struct kvm *kvm) { + int r; + kvm->arch.shadow_mmio_value = shadow_mmio_value; INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); INIT_LIST_HEAD(&kvm->arch.possible_nx_huge_pages); spin_lock_init(&kvm->arch.mmu_unsync_pages_lock); - if (tdp_mmu_enabled) + if (tdp_mmu_enabled) { kvm_mmu_init_tdp_mmu(kvm); + } else { + r = kvm_mmu_alloc_page_hash(kvm); + if (r) + return r; + } kvm->arch.split_page_header_cache.kmem_cache = mmu_page_header_cache; kvm->arch.split_page_header_cache.gfp_zero = __GFP_ZERO; @@ -6699,6 +6759,7 @@ void kvm_mmu_init_vm(struct kvm *kvm) kvm->arch.split_desc_cache.kmem_cache = pte_list_desc_cache; kvm->arch.split_desc_cache.gfp_zero = __GFP_ZERO; + return 0; } static void mmu_free_vm_memory_caches(struct kvm *kvm) @@ -6710,6 +6771,8 @@ static void mmu_free_vm_memory_caches(struct kvm *kvm) void kvm_mmu_uninit_vm(struct kvm *kvm) { + kvfree(kvm->arch.mmu_page_hash); + if (tdp_mmu_enabled) kvm_mmu_uninit_tdp_mmu(kvm); diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index db8f33e4de62..65f3c89d7c5d 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -103,6 +103,9 @@ struct kvm_mmu_page { int root_count; refcount_t tdp_mmu_root_count; }; + + bool has_mapped_host_mmio; + union { /* These two members aren't used for TDP MMU */ struct { diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 68e323568e95..ed762bb4b007 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -804,9 +804,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r != RET_PF_CONTINUE) return r; +#if PTTYPE != PTTYPE_EPT /* - * Do not change pte_access if the pfn is a mmio page, otherwise - * we will cache the incorrect access into mmio spte. + * Treat the guest PTE protections as writable, supervisor-only if this + * is a supervisor write fault and CR0.WP=0 (supervisor accesses ignore + * PTE.W if CR0.WP=0). Don't change the access type for emulated MMIO, + * otherwise KVM will cache incorrect access information in the SPTE. */ if (fault->write && !(walker.pte_access & ACC_WRITE_MASK) && !is_cr0_wp(vcpu->arch.mmu) && !fault->user && fault->slot) { @@ -822,6 +825,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (is_cr4_smep(vcpu->arch.mmu)) walker.pte_access &= ~ACC_EXEC_MASK; } +#endif r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index cfce03d8f123..df31039b5d63 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -104,7 +104,7 @@ u64 make_mmio_spte(struct kvm_vcpu *vcpu, u64 gfn, unsigned int access) return spte; } -static bool kvm_is_mmio_pfn(kvm_pfn_t pfn) +static bool __kvm_is_mmio_pfn(kvm_pfn_t pfn) { if (pfn_valid(pfn)) return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn)) && @@ -125,6 +125,35 @@ static bool kvm_is_mmio_pfn(kvm_pfn_t pfn) E820_TYPE_RAM); } +static bool kvm_is_mmio_pfn(kvm_pfn_t pfn, int *is_host_mmio) +{ + /* + * Determining if a PFN is host MMIO is relative expensive. Cache the + * result locally (in the sole caller) to avoid doing the full query + * multiple times when creating a single SPTE. + */ + if (*is_host_mmio < 0) + *is_host_mmio = __kvm_is_mmio_pfn(pfn); + + return *is_host_mmio; +} + +static void kvm_track_host_mmio_mapping(struct kvm_vcpu *vcpu) +{ + struct kvm_mmu_page *root = root_to_sp(vcpu->arch.mmu->root.hpa); + + if (root) + WRITE_ONCE(root->has_mapped_host_mmio, true); + else + WRITE_ONCE(vcpu->kvm->arch.has_mapped_host_mmio, true); + + /* + * Force vCPUs to exit and flush CPU buffers if the vCPU is using the + * affected root(s). + */ + kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_OUTSIDE_GUEST_MODE); +} + /* * Returns true if the SPTE needs to be updated atomically due to having bits * that may be changed without holding mmu_lock, and for which KVM must not @@ -162,6 +191,7 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, { int level = sp->role.level; u64 spte = SPTE_MMU_PRESENT_MASK; + int is_host_mmio = -1; bool wrprot = false; /* @@ -209,13 +239,15 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, if (level > PG_LEVEL_4K) spte |= PT_PAGE_SIZE_MASK; - spte |= kvm_x86_call(get_mt_mask)(vcpu, gfn, kvm_is_mmio_pfn(pfn)); + if (kvm_x86_ops.get_mt_mask) + spte |= kvm_x86_call(get_mt_mask)(vcpu, gfn, + kvm_is_mmio_pfn(pfn, &is_host_mmio)); if (host_writable) spte |= shadow_host_writable_mask; else pte_access &= ~ACC_WRITE_MASK; - if (shadow_me_value && !kvm_is_mmio_pfn(pfn)) + if (shadow_me_value && !kvm_is_mmio_pfn(pfn, &is_host_mmio)) spte |= shadow_me_value; spte |= (u64)pfn << PAGE_SHIFT; @@ -260,6 +292,11 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, mark_page_dirty_in_slot(vcpu->kvm, slot, gfn); } + if (static_branch_unlikely(&cpu_buf_vm_clear) && + !kvm_vcpu_can_access_host_mmio(vcpu) && + kvm_is_mmio_pfn(pfn, &is_host_mmio)) + kvm_track_host_mmio_mapping(vcpu); + *new_spte = spte; return wrprot; } diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 1e94f081bdaf..3133f066927e 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -280,6 +280,16 @@ static inline bool is_mirror_sptep(tdp_ptep_t sptep) return is_mirror_sp(sptep_to_sp(rcu_dereference(sptep))); } +static inline bool kvm_vcpu_can_access_host_mmio(struct kvm_vcpu *vcpu) +{ + struct kvm_mmu_page *root = root_to_sp(vcpu->arch.mmu->root.hpa); + + if (root) + return READ_ONCE(root->has_mapped_host_mmio); + + return READ_ONCE(vcpu->kvm->arch.has_mapped_host_mmio); +} + static inline bool is_mmio_spte(struct kvm *kvm, u64 spte) { return (spte & shadow_mmio_mask) == kvm->arch.shadow_mmio_value && diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h index fde0ae986003..c53b92379e6e 100644 --- a/arch/x86/kvm/reverse_cpuid.h +++ b/arch/x86/kvm/reverse_cpuid.h @@ -52,6 +52,10 @@ /* CPUID level 0x80000022 (EAX) */ #define KVM_X86_FEATURE_PERFMON_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 0) +/* CPUID level 0x80000021 (ECX) */ +#define KVM_X86_FEATURE_TSA_SQ_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 1) +#define KVM_X86_FEATURE_TSA_L1_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 2) + struct cpuid_reg { u32 function; u32 index; @@ -82,6 +86,7 @@ static const struct cpuid_reg reverse_cpuid[] = { [CPUID_8000_0022_EAX] = {0x80000022, 0, CPUID_EAX}, [CPUID_7_2_EDX] = { 7, 2, CPUID_EDX}, [CPUID_24_0_EBX] = { 0x24, 0, CPUID_EBX}, + [CPUID_8000_0021_ECX] = {0x80000021, 0, CPUID_ECX}, }; /* @@ -121,6 +126,8 @@ static __always_inline u32 __feature_translate(int x86_feature) KVM_X86_TRANSLATE_FEATURE(PERFMON_V2); KVM_X86_TRANSLATE_FEATURE(RRSBA_CTRL); KVM_X86_TRANSLATE_FEATURE(BHI_CTRL); + KVM_X86_TRANSLATE_FEATURE(TSA_SQ_NO); + KVM_X86_TRANSLATE_FEATURE(TSA_L1_NO); default: return x86_feature; } diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 067f8e3f5a0d..a34c5c3b164e 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -29,36 +30,39 @@ #include "svm.h" /* - * Encode the arbitrary VM ID and the vCPU's default APIC ID, i.e the vCPU ID, - * into the GATag so that KVM can retrieve the correct vCPU from a GALog entry - * if an interrupt can't be delivered, e.g. because the vCPU isn't running. + * Encode the arbitrary VM ID and the vCPU's _index_ into the GATag so that + * KVM can retrieve the correct vCPU from a GALog entry if an interrupt can't + * be delivered, e.g. because the vCPU isn't running. Use the vCPU's index + * instead of its ID (a.k.a. its default APIC ID), as KVM is guaranteed a fast + * lookup on the index, where as vCPUs whose index doesn't match their ID need + * to walk the entire xarray of vCPUs in the worst case scenario. * - * For the vCPU ID, use however many bits are currently allowed for the max + * For the vCPU index, use however many bits are currently allowed for the max * guest physical APIC ID (limited by the size of the physical ID table), and * use whatever bits remain to assign arbitrary AVIC IDs to VMs. Note, the * size of the GATag is defined by hardware (32 bits), but is an opaque value * as far as hardware is concerned. */ -#define AVIC_VCPU_ID_MASK AVIC_PHYSICAL_MAX_INDEX_MASK +#define AVIC_VCPU_IDX_MASK AVIC_PHYSICAL_MAX_INDEX_MASK #define AVIC_VM_ID_SHIFT HWEIGHT32(AVIC_PHYSICAL_MAX_INDEX_MASK) #define AVIC_VM_ID_MASK (GENMASK(31, AVIC_VM_ID_SHIFT) >> AVIC_VM_ID_SHIFT) #define AVIC_GATAG_TO_VMID(x) ((x >> AVIC_VM_ID_SHIFT) & AVIC_VM_ID_MASK) -#define AVIC_GATAG_TO_VCPUID(x) (x & AVIC_VCPU_ID_MASK) +#define AVIC_GATAG_TO_VCPUIDX(x) (x & AVIC_VCPU_IDX_MASK) -#define __AVIC_GATAG(vm_id, vcpu_id) ((((vm_id) & AVIC_VM_ID_MASK) << AVIC_VM_ID_SHIFT) | \ - ((vcpu_id) & AVIC_VCPU_ID_MASK)) -#define AVIC_GATAG(vm_id, vcpu_id) \ +#define __AVIC_GATAG(vm_id, vcpu_idx) ((((vm_id) & AVIC_VM_ID_MASK) << AVIC_VM_ID_SHIFT) | \ + ((vcpu_idx) & AVIC_VCPU_IDX_MASK)) +#define AVIC_GATAG(vm_id, vcpu_idx) \ ({ \ - u32 ga_tag = __AVIC_GATAG(vm_id, vcpu_id); \ + u32 ga_tag = __AVIC_GATAG(vm_id, vcpu_idx); \ \ - WARN_ON_ONCE(AVIC_GATAG_TO_VCPUID(ga_tag) != (vcpu_id)); \ + WARN_ON_ONCE(AVIC_GATAG_TO_VCPUIDX(ga_tag) != (vcpu_idx)); \ WARN_ON_ONCE(AVIC_GATAG_TO_VMID(ga_tag) != (vm_id)); \ ga_tag; \ }) -static_assert(__AVIC_GATAG(AVIC_VM_ID_MASK, AVIC_VCPU_ID_MASK) == -1u); +static_assert(__AVIC_GATAG(AVIC_VM_ID_MASK, AVIC_VCPU_IDX_MASK) == -1u); static bool force_avic; module_param_unsafe(force_avic, bool, 0444); @@ -75,14 +79,6 @@ static bool next_vm_id_wrapped = 0; static DEFINE_SPINLOCK(svm_vm_data_hash_lock); bool x2avic_enabled; -/* - * This is a wrapper of struct amd_iommu_ir_data. - */ -struct amd_svm_iommu_ir { - struct list_head node; /* Used by SVM for per-vcpu ir_list */ - void *data; /* Storing pointer to struct amd_ir_data */ -}; - static void avic_activate_vmcb(struct vcpu_svm *svm) { struct vmcb *vmcb = svm->vmcb01.ptr; @@ -147,16 +143,16 @@ int avic_ga_log_notifier(u32 ga_tag) struct kvm_svm *kvm_svm; struct kvm_vcpu *vcpu = NULL; u32 vm_id = AVIC_GATAG_TO_VMID(ga_tag); - u32 vcpu_id = AVIC_GATAG_TO_VCPUID(ga_tag); + u32 vcpu_idx = AVIC_GATAG_TO_VCPUIDX(ga_tag); - pr_debug("SVM: %s: vm_id=%#x, vcpu_id=%#x\n", __func__, vm_id, vcpu_id); - trace_kvm_avic_ga_log(vm_id, vcpu_id); + pr_debug("SVM: %s: vm_id=%#x, vcpu_idx=%#x\n", __func__, vm_id, vcpu_idx); + trace_kvm_avic_ga_log(vm_id, vcpu_idx); spin_lock_irqsave(&svm_vm_data_hash_lock, flags); hash_for_each_possible(svm_vm_data_hash, kvm_svm, hnode, vm_id) { if (kvm_svm->avic_vm_id != vm_id) continue; - vcpu = kvm_get_vcpu_by_id(&kvm_svm->kvm, vcpu_id); + vcpu = kvm_get_vcpu(&kvm_svm->kvm, vcpu_idx); break; } spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags); @@ -180,10 +176,8 @@ void avic_vm_destroy(struct kvm *kvm) if (!enable_apicv) return; - if (kvm_svm->avic_logical_id_table_page) - __free_page(kvm_svm->avic_logical_id_table_page); - if (kvm_svm->avic_physical_id_table_page) - __free_page(kvm_svm->avic_physical_id_table_page); + free_page((unsigned long)kvm_svm->avic_logical_id_table); + free_page((unsigned long)kvm_svm->avic_physical_id_table); spin_lock_irqsave(&svm_vm_data_hash_lock, flags); hash_del(&kvm_svm->hnode); @@ -196,27 +190,19 @@ int avic_vm_init(struct kvm *kvm) int err = -ENOMEM; struct kvm_svm *kvm_svm = to_kvm_svm(kvm); struct kvm_svm *k2; - struct page *p_page; - struct page *l_page; u32 vm_id; if (!enable_apicv) return 0; - /* Allocating physical APIC ID table (4KB) */ - p_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); - if (!p_page) + kvm_svm->avic_physical_id_table = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT); + if (!kvm_svm->avic_physical_id_table) goto free_avic; - kvm_svm->avic_physical_id_table_page = p_page; - - /* Allocating logical APIC ID table (4KB) */ - l_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); - if (!l_page) + kvm_svm->avic_logical_id_table = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT); + if (!kvm_svm->avic_logical_id_table) goto free_avic; - kvm_svm->avic_logical_id_table_page = l_page; - spin_lock_irqsave(&svm_vm_data_hash_lock, flags); again: vm_id = next_vm_id = (next_vm_id + 1) & AVIC_VM_ID_MASK; @@ -242,17 +228,19 @@ int avic_vm_init(struct kvm *kvm) return err; } +static phys_addr_t avic_get_backing_page_address(struct vcpu_svm *svm) +{ + return __sme_set(__pa(svm->vcpu.arch.apic->regs)); +} + void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb) { struct kvm_svm *kvm_svm = to_kvm_svm(svm->vcpu.kvm); - phys_addr_t bpa = __sme_set(page_to_phys(svm->avic_backing_page)); - phys_addr_t lpa = __sme_set(page_to_phys(kvm_svm->avic_logical_id_table_page)); - phys_addr_t ppa = __sme_set(page_to_phys(kvm_svm->avic_physical_id_table_page)); - vmcb->control.avic_backing_page = bpa & AVIC_HPA_MASK; - vmcb->control.avic_logical_id = lpa & AVIC_HPA_MASK; - vmcb->control.avic_physical_id = ppa & AVIC_HPA_MASK; - vmcb->control.avic_vapic_bar = APIC_DEFAULT_PHYS_BASE & VMCB_AVIC_APIC_BAR_MASK; + vmcb->control.avic_backing_page = avic_get_backing_page_address(svm); + vmcb->control.avic_logical_id = __sme_set(__pa(kvm_svm->avic_logical_id_table)); + vmcb->control.avic_physical_id = __sme_set(__pa(kvm_svm->avic_physical_id_table)); + vmcb->control.avic_vapic_bar = APIC_DEFAULT_PHYS_BASE; if (kvm_apicv_activated(svm->vcpu.kvm)) avic_activate_vmcb(svm); @@ -260,32 +248,31 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb) avic_deactivate_vmcb(svm); } -static u64 *avic_get_physical_id_entry(struct kvm_vcpu *vcpu, - unsigned int index) -{ - u64 *avic_physical_id_table; - struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); - - if ((!x2avic_enabled && index > AVIC_MAX_PHYSICAL_ID) || - (index > X2AVIC_MAX_PHYSICAL_ID)) - return NULL; - - avic_physical_id_table = page_address(kvm_svm->avic_physical_id_table_page); - - return &avic_physical_id_table[index]; -} - static int avic_init_backing_page(struct kvm_vcpu *vcpu) { - u64 *entry, new_entry; - int id = vcpu->vcpu_id; + struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); struct vcpu_svm *svm = to_svm(vcpu); + u32 id = vcpu->vcpu_id; + u64 new_entry; + /* + * Inhibit AVIC if the vCPU ID is bigger than what is supported by AVIC + * hardware. Immediately clear apicv_active, i.e. don't wait until the + * KVM_REQ_APICV_UPDATE request is processed on the first KVM_RUN, as + * avic_vcpu_load() expects to be called if and only if the vCPU has + * fully initialized AVIC. + */ if ((!x2avic_enabled && id > AVIC_MAX_PHYSICAL_ID) || - (id > X2AVIC_MAX_PHYSICAL_ID)) - return -EINVAL; + (id > X2AVIC_MAX_PHYSICAL_ID)) { + kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG); + vcpu->arch.apic->apicv_active = false; + return 0; + } - if (!vcpu->arch.apic->regs) + BUILD_BUG_ON((AVIC_MAX_PHYSICAL_ID + 1) * sizeof(new_entry) > PAGE_SIZE || + (X2AVIC_MAX_PHYSICAL_ID + 1) * sizeof(new_entry) > PAGE_SIZE); + + if (WARN_ON_ONCE(!vcpu->arch.apic->regs)) return -EINVAL; if (kvm_apicv_activated(vcpu->kvm)) { @@ -302,19 +289,21 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu) return ret; } - svm->avic_backing_page = virt_to_page(vcpu->arch.apic->regs); + /* Note, fls64() returns the bit position, +1. */ + BUILD_BUG_ON(__PHYSICAL_MASK_SHIFT > + fls64(AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK)); /* Setting AVIC backing page address in the phy APIC ID table */ - entry = avic_get_physical_id_entry(vcpu, id); - if (!entry) - return -EINVAL; + new_entry = avic_get_backing_page_address(svm) | + AVIC_PHYSICAL_ID_ENTRY_VALID_MASK; + svm->avic_physical_id_entry = new_entry; - new_entry = __sme_set((page_to_phys(svm->avic_backing_page) & - AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK) | - AVIC_PHYSICAL_ID_ENTRY_VALID_MASK); - WRITE_ONCE(*entry, new_entry); - - svm->avic_physical_id_cache = entry; + /* + * Initialize the real table, as vCPUs must have a valid entry in order + * for broadcast IPIs to function correctly (broadcast IPIs ignore + * invalid entries, i.e. aren't guaranteed to generate a VM-Exit). + */ + WRITE_ONCE(kvm_svm->avic_physical_id_table[id], new_entry); return 0; } @@ -448,7 +437,7 @@ static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source if (apic_x2apic_mode(source)) avic_logical_id_table = NULL; else - avic_logical_id_table = page_address(kvm_svm->avic_logical_id_table_page); + avic_logical_id_table = kvm_svm->avic_logical_id_table; /* * AVIC is inhibited if vCPUs aren't mapped 1:1 with logical @@ -550,7 +539,6 @@ unsigned long avic_vcpu_get_apicv_inhibit_reasons(struct kvm_vcpu *vcpu) static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat) { struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); - u32 *logical_apic_id_table; u32 cluster, index; ldr = GET_APIC_LOGICAL_ID(ldr); @@ -571,9 +559,7 @@ static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat) return NULL; index += (cluster << 2); - logical_apic_id_table = (u32 *) page_address(kvm_svm->avic_logical_id_table_page); - - return &logical_apic_id_table[index]; + return &kvm_svm->avic_logical_id_table[index]; } static void avic_ldr_write(struct kvm_vcpu *vcpu, u8 g_physical_id, u32 ldr) @@ -722,6 +708,9 @@ int avic_init_vcpu(struct vcpu_svm *svm) int ret; struct kvm_vcpu *vcpu = &svm->vcpu; + INIT_LIST_HEAD(&svm->ir_list); + spin_lock_init(&svm->ir_list_lock); + if (!enable_apicv || !irqchip_in_kernel(vcpu->kvm)) return 0; @@ -729,8 +718,6 @@ int avic_init_vcpu(struct vcpu_svm *svm) if (ret) return ret; - INIT_LIST_HEAD(&svm->ir_list); - spin_lock_init(&svm->ir_list_lock); svm->dfr_reg = APIC_DFR_FLAT; return ret; @@ -742,316 +729,161 @@ void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu) avic_handle_ldr_update(vcpu); } -static int avic_set_pi_irte_mode(struct kvm_vcpu *vcpu, bool activate) +static void svm_ir_list_del(struct kvm_kernel_irqfd *irqfd) { - int ret = 0; + struct kvm_vcpu *vcpu = irqfd->irq_bypass_vcpu; unsigned long flags; - struct amd_svm_iommu_ir *ir; - struct vcpu_svm *svm = to_svm(vcpu); - if (!kvm_arch_has_assigned_device(vcpu->kvm)) - return 0; + if (!vcpu) + return; + spin_lock_irqsave(&to_svm(vcpu)->ir_list_lock, flags); + list_del(&irqfd->vcpu_list); + spin_unlock_irqrestore(&to_svm(vcpu)->ir_list_lock, flags); +} + +int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm, + unsigned int host_irq, uint32_t guest_irq, + struct kvm_vcpu *vcpu, u32 vector) +{ /* - * Here, we go through the per-vcpu ir_list to update all existing - * interrupt remapping table entry targeting this vcpu. + * If the IRQ was affined to a different vCPU, remove the IRTE metadata + * from the *previous* vCPU's list. */ - spin_lock_irqsave(&svm->ir_list_lock, flags); + svm_ir_list_del(irqfd); - if (list_empty(&svm->ir_list)) - goto out; + if (vcpu) { + /* + * Try to enable guest_mode in IRTE, unless AVIC is inhibited, + * in which case configure the IRTE for legacy mode, but track + * the IRTE metadata so that it can be converted to guest mode + * if AVIC is enabled/uninhibited in the future. + */ + struct amd_iommu_pi_data pi_data = { + .ga_tag = AVIC_GATAG(to_kvm_svm(kvm)->avic_vm_id, + vcpu->vcpu_idx), + .is_guest_mode = kvm_vcpu_apicv_active(vcpu), + .vapic_addr = avic_get_backing_page_address(to_svm(vcpu)), + .vector = vector, + }; + struct vcpu_svm *svm = to_svm(vcpu); + u64 entry; + int ret; - list_for_each_entry(ir, &svm->ir_list, node) { - if (activate) - ret = amd_iommu_activate_guest_mode(ir->data); - else - ret = amd_iommu_deactivate_guest_mode(ir->data); + /* + * Prevent the vCPU from being scheduled out or migrated until + * the IRTE is updated and its metadata has been added to the + * list of IRQs being posted to the vCPU, to ensure the IRTE + * isn't programmed with stale pCPU/IsRunning information. + */ + guard(spinlock_irqsave)(&svm->ir_list_lock); + + /* + * Update the target pCPU for IOMMU doorbells if the vCPU is + * running. If the vCPU is NOT running, i.e. is blocking or + * scheduled out, KVM will update the pCPU info when the vCPU + * is awakened and/or scheduled in. See also avic_vcpu_load(). + */ + entry = svm->avic_physical_id_entry; + if (entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK) { + pi_data.cpu = entry & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK; + } else { + pi_data.cpu = -1; + pi_data.ga_log_intr = entry & AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR; + } + + ret = irq_set_vcpu_affinity(host_irq, &pi_data); if (ret) - break; - } -out: - spin_unlock_irqrestore(&svm->ir_list_lock, flags); - return ret; -} + return ret; -static void svm_ir_list_del(struct vcpu_svm *svm, struct amd_iommu_pi_data *pi) -{ - unsigned long flags; - struct amd_svm_iommu_ir *cur; - - spin_lock_irqsave(&svm->ir_list_lock, flags); - list_for_each_entry(cur, &svm->ir_list, node) { - if (cur->data != pi->ir_data) - continue; - list_del(&cur->node); - kfree(cur); - break; - } - spin_unlock_irqrestore(&svm->ir_list_lock, flags); -} - -static int svm_ir_list_add(struct vcpu_svm *svm, struct amd_iommu_pi_data *pi) -{ - int ret = 0; - unsigned long flags; - struct amd_svm_iommu_ir *ir; - u64 entry; - - if (WARN_ON_ONCE(!pi->ir_data)) - return -EINVAL; - - /** - * In some cases, the existing irte is updated and re-set, - * so we need to check here if it's already been * added - * to the ir_list. - */ - if (pi->prev_ga_tag) { - struct kvm *kvm = svm->vcpu.kvm; - u32 vcpu_id = AVIC_GATAG_TO_VCPUID(pi->prev_ga_tag); - struct kvm_vcpu *prev_vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id); - struct vcpu_svm *prev_svm; - - if (!prev_vcpu) { - ret = -EINVAL; - goto out; + /* + * Revert to legacy mode if the IOMMU didn't provide metadata + * for the IRTE, which KVM needs to keep the IRTE up-to-date, + * e.g. if the vCPU is migrated or AVIC is disabled. + */ + if (WARN_ON_ONCE(!pi_data.ir_data)) { + irq_set_vcpu_affinity(host_irq, NULL); + return -EIO; } - prev_svm = to_svm(prev_vcpu); - svm_ir_list_del(prev_svm, pi); + irqfd->irq_bypass_data = pi_data.ir_data; + list_add(&irqfd->vcpu_list, &svm->ir_list); + return 0; } + return irq_set_vcpu_affinity(host_irq, NULL); +} - /** - * Allocating new amd_iommu_pi_data, which will get - * add to the per-vcpu ir_list. +enum avic_vcpu_action { + /* + * There is no need to differentiate between activate and deactivate, + * as KVM only refreshes AVIC state when the vCPU is scheduled in and + * isn't blocking, i.e. the pCPU must always be (in)valid when AVIC is + * being (de)activated. */ - ir = kzalloc(sizeof(struct amd_svm_iommu_ir), GFP_ATOMIC | __GFP_ACCOUNT); - if (!ir) { - ret = -ENOMEM; - goto out; - } - ir->data = pi->ir_data; - - spin_lock_irqsave(&svm->ir_list_lock, flags); + AVIC_TOGGLE_ON_OFF = BIT(0), + AVIC_ACTIVATE = AVIC_TOGGLE_ON_OFF, + AVIC_DEACTIVATE = AVIC_TOGGLE_ON_OFF, /* - * Update the target pCPU for IOMMU doorbells if the vCPU is running. - * If the vCPU is NOT running, i.e. is blocking or scheduled out, KVM - * will update the pCPU info when the vCPU awkened and/or scheduled in. - * See also avic_vcpu_load(). + * No unique action is required to deal with a vCPU that stops/starts + * running. A vCPU that starts running by definition stops blocking as + * well, and a vCPU that stops running can't have been blocking, i.e. + * doesn't need to toggle GALogIntr. */ - entry = READ_ONCE(*(svm->avic_physical_id_cache)); - if (entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK) - amd_iommu_update_ga(entry & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK, - true, pi->ir_data); + AVIC_START_RUNNING = 0, + AVIC_STOP_RUNNING = 0, - list_add(&ir->node, &svm->ir_list); - spin_unlock_irqrestore(&svm->ir_list_lock, flags); -out: - return ret; -} + /* + * When a vCPU starts blocking, KVM needs to set the GALogIntr flag + * int all associated IRTEs so that KVM can wake the vCPU if an IRQ is + * sent to the vCPU. + */ + AVIC_START_BLOCKING = BIT(1), +}; -/* - * Note: - * The HW cannot support posting multicast/broadcast - * interrupts to a vCPU. So, we still use legacy interrupt - * remapping for these kind of interrupts. - * - * For lowest-priority interrupts, we only support - * those with single CPU as the destination, e.g. user - * configures the interrupts via /proc/irq or uses - * irqbalance to make the interrupts single-CPU. - */ -static int -get_pi_vcpu_info(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, - struct vcpu_data *vcpu_info, struct vcpu_svm **svm) +static void avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, + enum avic_vcpu_action action) { - struct kvm_lapic_irq irq; - struct kvm_vcpu *vcpu = NULL; - - kvm_set_msi_irq(kvm, e, &irq); - - if (!kvm_intr_is_single_vcpu(kvm, &irq, &vcpu) || - !kvm_irq_is_postable(&irq)) { - pr_debug("SVM: %s: use legacy intr remap mode for irq %u\n", - __func__, irq.vector); - return -1; - } - - pr_debug("SVM: %s: use GA mode for irq %u\n", __func__, - irq.vector); - *svm = to_svm(vcpu); - vcpu_info->pi_desc_addr = __sme_set(page_to_phys((*svm)->avic_backing_page)); - vcpu_info->vector = irq.vector; - - return 0; -} - -/* - * avic_pi_update_irte - set IRTE for Posted-Interrupts - * - * @kvm: kvm - * @host_irq: host irq of the interrupt - * @guest_irq: gsi of the interrupt - * @set: set or unset PI - * returns 0 on success, < 0 on failure - */ -int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set) -{ - struct kvm_kernel_irq_routing_entry *e; - struct kvm_irq_routing_table *irq_rt; - bool enable_remapped_mode = true; - int idx, ret = 0; - - if (!kvm_arch_has_assigned_device(kvm) || !kvm_arch_has_irq_bypass()) - return 0; - - pr_debug("SVM: %s: host_irq=%#x, guest_irq=%#x, set=%#x\n", - __func__, host_irq, guest_irq, set); - - idx = srcu_read_lock(&kvm->irq_srcu); - irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); - - if (guest_irq >= irq_rt->nr_rt_entries || - hlist_empty(&irq_rt->map[guest_irq])) { - pr_warn_once("no route for guest_irq %u/%u (broken user space?)\n", - guest_irq, irq_rt->nr_rt_entries); - goto out; - } - - hlist_for_each_entry(e, &irq_rt->map[guest_irq], link) { - struct vcpu_data vcpu_info; - struct vcpu_svm *svm = NULL; - - if (e->type != KVM_IRQ_ROUTING_MSI) - continue; - - /** - * Here, we setup with legacy mode in the following cases: - * 1. When cannot target interrupt to a specific vcpu. - * 2. Unsetting posted interrupt. - * 3. APIC virtualization is disabled for the vcpu. - * 4. IRQ has incompatible delivery mode (SMI, INIT, etc) - */ - if (!get_pi_vcpu_info(kvm, e, &vcpu_info, &svm) && set && - kvm_vcpu_apicv_active(&svm->vcpu)) { - struct amd_iommu_pi_data pi; - - enable_remapped_mode = false; - - /* Try to enable guest_mode in IRTE */ - pi.base = __sme_set(page_to_phys(svm->avic_backing_page) & - AVIC_HPA_MASK); - pi.ga_tag = AVIC_GATAG(to_kvm_svm(kvm)->avic_vm_id, - svm->vcpu.vcpu_id); - pi.is_guest_mode = true; - pi.vcpu_data = &vcpu_info; - ret = irq_set_vcpu_affinity(host_irq, &pi); - - /** - * Here, we successfully setting up vcpu affinity in - * IOMMU guest mode. Now, we need to store the posted - * interrupt information in a per-vcpu ir_list so that - * we can reference to them directly when we update vcpu - * scheduling information in IOMMU irte. - */ - if (!ret && pi.is_guest_mode) - svm_ir_list_add(svm, &pi); - } - - if (!ret && svm) { - trace_kvm_pi_irte_update(host_irq, svm->vcpu.vcpu_id, - e->gsi, vcpu_info.vector, - vcpu_info.pi_desc_addr, set); - } - - if (ret < 0) { - pr_err("%s: failed to update PI IRTE\n", __func__); - goto out; - } - } - - ret = 0; - if (enable_remapped_mode) { - /* Use legacy mode in IRTE */ - struct amd_iommu_pi_data pi; - - /** - * Here, pi is used to: - * - Tell IOMMU to use legacy mode for this interrupt. - * - Retrieve ga_tag of prior interrupt remapping data. - */ - pi.prev_ga_tag = 0; - pi.is_guest_mode = false; - ret = irq_set_vcpu_affinity(host_irq, &pi); - - /** - * Check if the posted interrupt was previously - * setup with the guest_mode by checking if the ga_tag - * was cached. If so, we need to clean up the per-vcpu - * ir_list. - */ - if (!ret && pi.prev_ga_tag) { - int id = AVIC_GATAG_TO_VCPUID(pi.prev_ga_tag); - struct kvm_vcpu *vcpu; - - vcpu = kvm_get_vcpu_by_id(kvm, id); - if (vcpu) - svm_ir_list_del(to_svm(vcpu), &pi); - } - } -out: - srcu_read_unlock(&kvm->irq_srcu, idx); - return ret; -} - -static inline int -avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, bool r) -{ - int ret = 0; - struct amd_svm_iommu_ir *ir; + bool ga_log_intr = (action & AVIC_START_BLOCKING); struct vcpu_svm *svm = to_svm(vcpu); + struct kvm_kernel_irqfd *irqfd; lockdep_assert_held(&svm->ir_list_lock); - if (!kvm_arch_has_assigned_device(vcpu->kvm)) - return 0; - /* * Here, we go through the per-vcpu ir_list to update all existing * interrupt remapping table entry targeting this vcpu. */ if (list_empty(&svm->ir_list)) - return 0; + return; - list_for_each_entry(ir, &svm->ir_list, node) { - ret = amd_iommu_update_ga(cpu, r, ir->data); - if (ret) - return ret; + list_for_each_entry(irqfd, &svm->ir_list, vcpu_list) { + void *data = irqfd->irq_bypass_data; + + if (!(action & AVIC_TOGGLE_ON_OFF)) + WARN_ON_ONCE(amd_iommu_update_ga(data, cpu, ga_log_intr)); + else if (cpu >= 0) + WARN_ON_ONCE(amd_iommu_activate_guest_mode(data, cpu, ga_log_intr)); + else + WARN_ON_ONCE(amd_iommu_deactivate_guest_mode(data)); } - return 0; } -void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu, + enum avic_vcpu_action action) { - u64 entry; + struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); int h_physical_id = kvm_cpu_get_apicid(cpu); struct vcpu_svm *svm = to_svm(vcpu); unsigned long flags; + u64 entry; lockdep_assert_preemption_disabled(); if (WARN_ON(h_physical_id & ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK)) return; - /* - * No need to update anything if the vCPU is blocking, i.e. if the vCPU - * is being scheduled in after being preempted. The CPU entries in the - * Physical APIC table and IRTE are consumed iff IsRun{ning} is '1'. - * If the vCPU was migrated, its new CPU value will be stuffed when the - * vCPU unblocks. - */ - if (kvm_vcpu_is_blocking(vcpu)) + if (WARN_ON_ONCE(vcpu->vcpu_id * sizeof(entry) >= PAGE_SIZE)) return; /* @@ -1063,38 +895,57 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) */ spin_lock_irqsave(&svm->ir_list_lock, flags); - entry = READ_ONCE(*(svm->avic_physical_id_cache)); + entry = svm->avic_physical_id_entry; WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK); - entry &= ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK; + entry &= ~(AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK | + AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR); entry |= (h_physical_id & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK); entry |= AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK; - WRITE_ONCE(*(svm->avic_physical_id_cache), entry); - avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, true); + svm->avic_physical_id_entry = entry; + + /* + * If IPI virtualization is disabled, clear IsRunning when updating the + * actual Physical ID table, so that the CPU never sees IsRunning=1. + * Keep the APIC ID up-to-date in the entry to minimize the chances of + * things going sideways if hardware peeks at the ID. + */ + if (!enable_ipiv) + entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK; + + WRITE_ONCE(kvm_svm->avic_physical_id_table[vcpu->vcpu_id], entry); + + avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, action); spin_unlock_irqrestore(&svm->ir_list_lock, flags); } -void avic_vcpu_put(struct kvm_vcpu *vcpu) +void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { - u64 entry; + /* + * No need to update anything if the vCPU is blocking, i.e. if the vCPU + * is being scheduled in after being preempted. The CPU entries in the + * Physical APIC table and IRTE are consumed iff IsRun{ning} is '1'. + * If the vCPU was migrated, its new CPU value will be stuffed when the + * vCPU unblocks. + */ + if (kvm_vcpu_is_blocking(vcpu)) + return; + + __avic_vcpu_load(vcpu, cpu, AVIC_START_RUNNING); +} + +static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action) +{ + struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); struct vcpu_svm *svm = to_svm(vcpu); unsigned long flags; + u64 entry = svm->avic_physical_id_entry; lockdep_assert_preemption_disabled(); - /* - * Note, reading the Physical ID entry outside of ir_list_lock is safe - * as only the pCPU that has loaded (or is loading) the vCPU is allowed - * to modify the entry, and preemption is disabled. I.e. the vCPU - * can't be scheduled out and thus avic_vcpu_{put,load}() can't run - * recursively. - */ - entry = READ_ONCE(*(svm->avic_physical_id_cache)); - - /* Nothing to do if IsRunning == '0' due to vCPU blocking. */ - if (!(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK)) + if (WARN_ON_ONCE(vcpu->vcpu_id * sizeof(entry) >= PAGE_SIZE)) return; /* @@ -1107,13 +958,62 @@ void avic_vcpu_put(struct kvm_vcpu *vcpu) */ spin_lock_irqsave(&svm->ir_list_lock, flags); - avic_update_iommu_vcpu_affinity(vcpu, -1, 0); + avic_update_iommu_vcpu_affinity(vcpu, -1, action); + WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR); + + /* + * Keep the previous APIC ID in the entry so that a rogue doorbell from + * hardware is at least restricted to a CPU associated with the vCPU. + */ entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK; - WRITE_ONCE(*(svm->avic_physical_id_cache), entry); + + if (enable_ipiv) + WRITE_ONCE(kvm_svm->avic_physical_id_table[vcpu->vcpu_id], entry); + + /* + * Note! Don't set AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR in the table as + * it's a synthetic flag that usurps an unused should-be-zero bit. + */ + if (action & AVIC_START_BLOCKING) + entry |= AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR; + + svm->avic_physical_id_entry = entry; spin_unlock_irqrestore(&svm->ir_list_lock, flags); +} +void avic_vcpu_put(struct kvm_vcpu *vcpu) +{ + /* + * Note, reading the Physical ID entry outside of ir_list_lock is safe + * as only the pCPU that has loaded (or is loading) the vCPU is allowed + * to modify the entry, and preemption is disabled. I.e. the vCPU + * can't be scheduled out and thus avic_vcpu_{put,load}() can't run + * recursively. + */ + u64 entry = to_svm(vcpu)->avic_physical_id_entry; + + /* + * Nothing to do if IsRunning == '0' due to vCPU blocking, i.e. if the + * vCPU is preempted while its in the process of blocking. WARN if the + * vCPU wasn't running and isn't blocking, KVM shouldn't attempt to put + * the AVIC if it wasn't previously loaded. + */ + if (!(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK)) { + if (WARN_ON_ONCE(!kvm_vcpu_is_blocking(vcpu))) + return; + + /* + * The vCPU was preempted while blocking, ensure its IRTEs are + * configured to generate GA Log Interrupts. + */ + if (!(WARN_ON_ONCE(!(entry & AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR)))) + return; + } + + __avic_vcpu_put(vcpu, kvm_vcpu_is_blocking(vcpu) ? AVIC_START_BLOCKING : + AVIC_STOP_RUNNING); } void avic_refresh_virtual_apic_mode(struct kvm_vcpu *vcpu) @@ -1142,19 +1042,18 @@ void avic_refresh_virtual_apic_mode(struct kvm_vcpu *vcpu) void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) { - bool activated = kvm_vcpu_apicv_active(vcpu); - if (!enable_apicv) return; + /* APICv should only be toggled on/off while the vCPU is running. */ + WARN_ON_ONCE(kvm_vcpu_is_blocking(vcpu)); + avic_refresh_virtual_apic_mode(vcpu); - if (activated) - avic_vcpu_load(vcpu, vcpu->cpu); + if (kvm_vcpu_apicv_active(vcpu)) + __avic_vcpu_load(vcpu, vcpu->cpu, AVIC_ACTIVATE); else - avic_vcpu_put(vcpu); - - avic_set_pi_irte_mode(vcpu, activated); + __avic_vcpu_put(vcpu, AVIC_DEACTIVATE); } void avic_vcpu_blocking(struct kvm_vcpu *vcpu) @@ -1162,20 +1061,25 @@ void avic_vcpu_blocking(struct kvm_vcpu *vcpu) if (!kvm_vcpu_apicv_active(vcpu)) return; - /* - * Unload the AVIC when the vCPU is about to block, _before_ - * the vCPU actually blocks. - * - * Any IRQs that arrive before IsRunning=0 will not cause an - * incomplete IPI vmexit on the source, therefore vIRR will also - * be checked by kvm_vcpu_check_block() before blocking. The - * memory barrier implicit in set_current_state orders writing - * IsRunning=0 before reading the vIRR. The processor needs a - * matching memory barrier on interrupt delivery between writing - * IRR and reading IsRunning; the lack of this barrier might be - * the cause of errata #1235). - */ - avic_vcpu_put(vcpu); + /* + * Unload the AVIC when the vCPU is about to block, _before_ the vCPU + * actually blocks. + * + * Note, any IRQs that arrive before IsRunning=0 will not cause an + * incomplete IPI vmexit on the source; kvm_vcpu_check_block() handles + * this by checking vIRR one last time before blocking. The memory + * barrier implicit in set_current_state orders writing IsRunning=0 + * before reading the vIRR. The processor needs a matching memory + * barrier on interrupt delivery between writing IRR and reading + * IsRunning; the lack of this barrier might be the cause of errata #1235). + * + * Clear IsRunning=0 even if guest IRQs are disabled, i.e. even if KVM + * doesn't need to detect events for scheduling purposes. The doorbell + * used to signal running vCPUs cannot be blocked, i.e. will perturb the + * CPU and cause noisy neighbor problems if the VM is sending interrupts + * to the vCPU while it's scheduled out. + */ + __avic_vcpu_put(vcpu, AVIC_START_BLOCKING); } void avic_vcpu_unblocking(struct kvm_vcpu *vcpu) @@ -1228,6 +1132,14 @@ bool avic_hardware_setup(void) if (x2avic_enabled) pr_info("x2AVIC enabled\n"); + /* + * Disable IPI virtualization for AMD Family 17h CPUs (Zen1 and Zen2) + * due to erratum 1235, which results in missed VM-Exits on the sender + * and thus missed wake events for blocking vCPUs due to the CPU + * failing to see a software update to clear IsRunning. + */ + enable_ipiv = enable_ipiv && boot_cpu_data.x86 != 0x17; + amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier); return true; diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 8427a48b8b7a..b7fd2e869998 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -184,13 +184,88 @@ void recalc_intercepts(struct vcpu_svm *svm) } } +/* + * This array (and its actual size) holds the set of offsets (indexing by chunk + * size) to process when merging vmcb12's MSRPM with vmcb01's MSRPM. Note, the + * set of MSRs for which interception is disabled in vmcb01 is per-vCPU, e.g. + * based on CPUID features. This array only tracks MSRs that *might* be passed + * through to the guest. + * + * Hardcode the capacity of the array based on the maximum number of _offsets_. + * MSRs are batched together, so there are fewer offsets than MSRs. + */ +static int nested_svm_msrpm_merge_offsets[7] __ro_after_init; +static int nested_svm_nr_msrpm_merge_offsets __ro_after_init; +typedef unsigned long nsvm_msrpm_merge_t; + +int __init nested_svm_init_msrpm_merge_offsets(void) +{ + static const u32 merge_msrs[] __initconst = { + MSR_STAR, + MSR_IA32_SYSENTER_CS, + MSR_IA32_SYSENTER_EIP, + MSR_IA32_SYSENTER_ESP, + #ifdef CONFIG_X86_64 + MSR_GS_BASE, + MSR_FS_BASE, + MSR_KERNEL_GS_BASE, + MSR_LSTAR, + MSR_CSTAR, + MSR_SYSCALL_MASK, + #endif + MSR_IA32_SPEC_CTRL, + MSR_IA32_PRED_CMD, + MSR_IA32_FLUSH_CMD, + MSR_IA32_APERF, + MSR_IA32_MPERF, + MSR_IA32_LASTBRANCHFROMIP, + MSR_IA32_LASTBRANCHTOIP, + MSR_IA32_LASTINTFROMIP, + MSR_IA32_LASTINTTOIP, + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(merge_msrs); i++) { + int bit_nr = svm_msrpm_bit_nr(merge_msrs[i]); + u32 offset; + + if (WARN_ON(bit_nr < 0)) + return -EIO; + + /* + * Merging is done in chunks to reduce the number of accesses + * to L1's bitmap. + */ + offset = bit_nr / BITS_PER_BYTE / sizeof(nsvm_msrpm_merge_t); + + for (j = 0; j < nested_svm_nr_msrpm_merge_offsets; j++) { + if (nested_svm_msrpm_merge_offsets[j] == offset) + break; + } + + if (j < nested_svm_nr_msrpm_merge_offsets) + continue; + + if (WARN_ON(j >= ARRAY_SIZE(nested_svm_msrpm_merge_offsets))) + return -EIO; + + nested_svm_msrpm_merge_offsets[j] = offset; + nested_svm_nr_msrpm_merge_offsets++; + } + + return 0; +} + /* * Merge L0's (KVM) and L1's (Nested VMCB) MSR permission bitmaps. The function * is optimized in that it only merges the parts where KVM MSR permission bitmap * may contain zero bits. */ -static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) +static bool nested_svm_merge_msrpm(struct kvm_vcpu *vcpu) { + struct vcpu_svm *svm = to_svm(vcpu); + nsvm_msrpm_merge_t *msrpm02 = svm->nested.msrpm; + nsvm_msrpm_merge_t *msrpm01 = svm->msrpm; int i; /* @@ -205,7 +280,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) if (!svm->nested.force_msr_bitmap_recalc) { struct hv_vmcb_enlightenments *hve = &svm->nested.ctl.hv_enlightenments; - if (kvm_hv_hypercall_enabled(&svm->vcpu) && + if (kvm_hv_hypercall_enabled(vcpu) && hve->hv_enlightenments_control.msr_bitmap && (svm->nested.ctl.clean & BIT(HV_VMCB_NESTED_ENLIGHTENMENTS))) goto set_msrpm_base_pa; @@ -215,25 +290,17 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) if (!(vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT))) return true; - for (i = 0; i < MSRPM_OFFSETS; i++) { - u32 value, p; - u64 offset; + for (i = 0; i < nested_svm_nr_msrpm_merge_offsets; i++) { + const int p = nested_svm_msrpm_merge_offsets[i]; + nsvm_msrpm_merge_t l1_val; + gpa_t gpa; - if (msrpm_offsets[i] == 0xffffffff) - break; + gpa = svm->nested.ctl.msrpm_base_pa + (p * sizeof(l1_val)); - p = msrpm_offsets[i]; - - /* x2apic msrs are intercepted always for the nested guest */ - if (is_x2apic_msrpm_offset(p)) - continue; - - offset = svm->nested.ctl.msrpm_base_pa + (p * 4); - - if (kvm_vcpu_read_guest(&svm->vcpu, offset, &value, 4)) + if (kvm_vcpu_read_guest(vcpu, gpa, &l1_val, sizeof(l1_val))) return false; - svm->nested.msrpm[p] = svm->msrpm[p] | value; + msrpm02[p] = msrpm01[p] | l1_val; } svm->nested.force_msr_bitmap_recalc = false; @@ -937,7 +1004,7 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) if (enter_svm_guest_mode(vcpu, vmcb12_gpa, vmcb12, true)) goto out_exit_err; - if (nested_svm_vmrun_msrpm(svm)) + if (nested_svm_merge_msrpm(vcpu)) goto out; out_exit_err: @@ -1230,7 +1297,6 @@ int svm_allocate_nested(struct vcpu_svm *svm) svm->nested.msrpm = svm_vcpu_alloc_msrpm(); if (!svm->nested.msrpm) goto err_free_vmcb02; - svm_vcpu_init_msrpm(&svm->vcpu, svm->nested.msrpm); svm->nested.initialized = true; return 0; @@ -1290,26 +1356,26 @@ void svm_leave_nested(struct kvm_vcpu *vcpu) static int nested_svm_exit_handled_msr(struct vcpu_svm *svm) { - u32 offset, msr, value; - int write, mask; + gpa_t base = svm->nested.ctl.msrpm_base_pa; + int write, bit_nr; + u8 value, mask; + u32 msr; if (!(vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT))) return NESTED_EXIT_HOST; msr = svm->vcpu.arch.regs[VCPU_REGS_RCX]; - offset = svm_msrpm_offset(msr); + bit_nr = svm_msrpm_bit_nr(msr); write = svm->vmcb->control.exit_info_1 & 1; - mask = 1 << ((2 * (msr & 0xf)) + write); - if (offset == MSR_INVALID) + if (bit_nr < 0) return NESTED_EXIT_DONE; - /* Offset is in 32 bit units but need in 8 bit units */ - offset *= 4; - - if (kvm_vcpu_read_guest(&svm->vcpu, svm->nested.ctl.msrpm_base_pa + offset, &value, 4)) + if (kvm_vcpu_read_guest(&svm->vcpu, base + bit_nr / BITS_PER_BYTE, + &value, sizeof(value))) return NESTED_EXIT_DONE; + mask = BIT(write) << (bit_nr & (BITS_PER_BYTE - 1)); return (value & mask) ? NESTED_EXIT_DONE : NESTED_EXIT_HOST; } @@ -1819,13 +1885,11 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) { - struct vcpu_svm *svm = to_svm(vcpu); - if (WARN_ON(!is_guest_mode(vcpu))) return true; if (!vcpu->arch.pdptrs_from_userspace && - !nested_npt_enabled(svm) && is_pae_paging(vcpu)) + !nested_npt_enabled(to_svm(vcpu)) && is_pae_paging(vcpu)) /* * Reload the guest's PDPTRs since after a migration * the guest CR3 might be restored prior to setting the nested @@ -1834,7 +1898,7 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) if (CC(!load_pdptrs(vcpu, vcpu->arch.cr3))) return false; - if (!nested_svm_vmrun_msrpm(svm)) { + if (!nested_svm_merge_msrpm(vcpu)) { vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 459c3b791fd4..0635bd71c10e 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -117,6 +117,7 @@ static int sev_flush_asids(unsigned int min_asid, unsigned int max_asid) */ down_write(&sev_deactivate_lock); + /* SNP firmware requires use of WBINVD for ASID recycling. */ wbinvd_on_all_cpus(); if (sev_snp_enabled) @@ -446,7 +447,12 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, init_args.probe = false; ret = sev_platform_init(&init_args); if (ret) - goto e_free; + goto e_free_asid; + + if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { + ret = -ENOMEM; + goto e_free_asid; + } /* This needs to happen after SEV/SNP firmware initialization. */ if (vm_type == KVM_X86_SNP_VM) { @@ -464,6 +470,8 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, return 0; e_free: + free_cpumask_var(sev->have_run_cpus); +e_free_asid: argp->error = init_args.error; sev_asid_free(sev); sev->asid = 0; @@ -708,6 +716,29 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages) } } +static void sev_writeback_caches(struct kvm *kvm) +{ + /* + * Ensure that all dirty guest tagged cache entries are written back + * before releasing the pages back to the system for use. CLFLUSH will + * not do this without SME_COHERENT, and flushing many cache lines + * individually is slower than blasting WBINVD for large VMs, so issue + * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported) + * on CPUs that have done VMRUN, i.e. may have dirtied data using the + * VM's ASID. + * + * For simplicity, never remove CPUs from the bitmap. Ideally, KVM + * would clear the mask when flushing caches, but doing so requires + * serializing multiple calls and having responding CPUs (to the IPI) + * mark themselves as still running if they are running (or about to + * run) a vCPU for the VM. + * + * Note, the caller is responsible for ensuring correctness if the mask + * can be modified, e.g. if a CPU could be doing VMRUN. + */ + wbnoinvd_on_cpus_mask(to_kvm_sev_info(kvm)->have_run_cpus); +} + static unsigned long get_num_contig_pages(unsigned long idx, struct page **inpages, unsigned long npages) { @@ -1971,6 +2002,10 @@ 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)) + return -EBUSY; + if (!sev_es_guest(src)) return 0; @@ -2033,6 +2068,17 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) if (ret) goto out_source_vcpu; + /* + * Allocate a new have_run_cpus for the destination, i.e. don't copy + * the set of CPUs from the source. If a CPU was used to run a vCPU in + * the source VM but is never used for the destination VM, then the CPU + * can only have cached memory that was accessible to the source VM. + */ + if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { + ret = -ENOMEM; + goto out_source_vcpu; + } + sev_migrate_from(kvm, source_kvm); kvm_vm_dead(source_kvm); cg_cleanup_sev = src_sev; @@ -2131,11 +2177,7 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) return -EINVAL; /* Check for policy bits that must be set */ - if (!(params.policy & SNP_POLICY_MASK_RSVD_MBO) || - !(params.policy & SNP_POLICY_MASK_SMT)) - return -EINVAL; - - if (params.policy & SNP_POLICY_MASK_SINGLE_SOCKET) + if (!(params.policy & SNP_POLICY_MASK_RSVD_MBO)) return -EINVAL; sev->policy = params.policy; @@ -2694,12 +2736,7 @@ int sev_mem_enc_unregister_region(struct kvm *kvm, goto failed; } - /* - * Ensure that all guest tagged cache entries are flushed before - * releasing the pages back to the system for use. CLFLUSH will - * not do this, so issue a WBINVD. - */ - wbinvd_on_all_cpus(); + sev_writeback_caches(kvm); __unregister_enc_region_locked(kvm, region); @@ -2741,13 +2778,18 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) goto e_unlock; } + mirror_sev = to_kvm_sev_info(kvm); + if (!zalloc_cpumask_var(&mirror_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { + ret = -ENOMEM; + goto e_unlock; + } + /* * The mirror kvm holds an enc_context_owner ref so its asid can't * disappear until we're done with it */ source_sev = to_kvm_sev_info(source_kvm); kvm_get_kvm(source_kvm); - mirror_sev = to_kvm_sev_info(kvm); list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); /* Set enc_context_owner and copy its encryption context over */ @@ -2809,7 +2851,13 @@ void sev_vm_destroy(struct kvm *kvm) WARN_ON(!list_empty(&sev->mirror_vms)); - /* If this is a mirror_kvm release the enc_context_owner and skip sev cleanup */ + free_cpumask_var(sev->have_run_cpus); + + /* + * If this is a mirror VM, remove it from the owner's list of a mirrors + * and skip ASID cleanup (the ASID is tied to the lifetime of the owner). + * Note, mirror VMs don't support registering encrypted regions. + */ if (is_mirroring_enc_context(kvm)) { struct kvm *owner_kvm = sev->enc_context_owner; @@ -2820,12 +2868,6 @@ void sev_vm_destroy(struct kvm *kvm) return; } - /* - * Ensure that all guest tagged cache entries are flushed before - * releasing the pages back to the system for use. CLFLUSH will - * not do this, so issue a WBINVD. - */ - wbinvd_on_all_cpus(); /* * if userspace was terminated before unregistering the memory regions @@ -3095,30 +3137,29 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va) /* * VM Page Flush takes a host virtual address and a guest ASID. Fall - * back to WBINVD if this faults so as not to make any problems worse - * by leaving stale encrypted data in the cache. + * back to full writeback of caches if this faults so as not to make + * any problems worse by leaving stale encrypted data in the cache. */ if (WARN_ON_ONCE(wrmsrq_safe(MSR_AMD64_VM_PAGE_FLUSH, addr | asid))) - goto do_wbinvd; + goto do_sev_writeback_caches; return; -do_wbinvd: - wbinvd_on_all_cpus(); +do_sev_writeback_caches: + sev_writeback_caches(vcpu->kvm); } void sev_guest_memory_reclaimed(struct kvm *kvm) { /* * With SNP+gmem, private/encrypted memory is unreachable via the - * hva-based mmu notifiers, so these events are only actually - * pertaining to shared pages where there is no need to perform - * the WBINVD to flush associated caches. + * hva-based mmu notifiers, i.e. these events are explicitly scoped to + * shared pages, where there's no need to flush caches. */ if (!sev_guest(kvm) || sev_snp_guest(kvm)) return; - wbinvd_on_all_cpus(); + sev_writeback_caches(kvm); } void sev_free_vcpu(struct kvm_vcpu *vcpu) @@ -3450,6 +3491,15 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu) if (sev_es_guest(kvm) && !VALID_PAGE(svm->vmcb->control.vmsa_pa)) return -EINVAL; + /* + * To optimize cache flushes when memory is reclaimed from an SEV VM, + * track physical CPUs that enter the guest for SEV VMs and thus can + * have encrypted, dirty data in the cache, and flush caches only for + * CPUs that have entered the guest. + */ + if (!cpumask_test_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus)) + cpumask_set_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus); + /* Assign the asid allocated with this SEV guest */ svm->asid = asid; @@ -3882,9 +3932,9 @@ void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) * From this point forward, the VMSA will always be a guest-mapped page * rather than the initial one allocated by KVM in svm->sev_es.vmsa. In * theory, svm->sev_es.vmsa could be free'd and cleaned up here, but - * that involves cleanups like wbinvd_on_all_cpus() which would ideally - * be handled during teardown rather than guest boot. Deferring that - * also allows the existing logic for SEV-ES VMSAs to be re-used with + * that involves cleanups like flushing caches, which would ideally be + * handled during teardown rather than guest boot. Deferring that also + * allows the existing logic for SEV-ES VMSAs to be re-used with * minimal SNP-specific changes. */ svm->sev_es.snp_has_guest_vmsa = true; @@ -4386,16 +4436,17 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in) count, in); } -static void sev_es_vcpu_after_set_cpuid(struct vcpu_svm *svm) +void sev_es_recalc_msr_intercepts(struct kvm_vcpu *vcpu) { - struct kvm_vcpu *vcpu = &svm->vcpu; + /* Clear intercepts on MSRs that are context switched by hardware. */ + svm_disable_intercept_for_msr(vcpu, MSR_AMD64_SEV_ES_GHCB, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_EFER, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_IA32_CR_PAT, MSR_TYPE_RW); - if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) { - bool v_tsc_aux = guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP) || - guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID); - - set_msr_interception(vcpu, svm->msrpm, MSR_TSC_AUX, v_tsc_aux, v_tsc_aux); - } + if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) + svm_set_intercept_for_msr(vcpu, MSR_TSC_AUX, MSR_TYPE_RW, + !guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP) && + !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID)); /* * For SEV-ES, accesses to MSR_IA32_XSS should not be intercepted if @@ -4409,11 +4460,9 @@ static void sev_es_vcpu_after_set_cpuid(struct vcpu_svm *svm) * XSAVES being exposed to the guest so that KVM can at least honor * guest CPUID for RDMSR and WRMSR. */ - if (guest_cpu_cap_has(vcpu, X86_FEATURE_XSAVES) && - guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)) - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_XSS, 1, 1); - else - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_XSS, 0, 0); + svm_set_intercept_for_msr(vcpu, MSR_IA32_XSS, MSR_TYPE_RW, + !guest_cpu_cap_has(vcpu, X86_FEATURE_XSAVES) || + !guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)); } void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm) @@ -4425,16 +4474,12 @@ void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm) best = kvm_find_cpuid_entry(vcpu, 0x8000001F); if (best) vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx & 0x3f)); - - if (sev_es_guest(svm->vcpu.kvm)) - sev_es_vcpu_after_set_cpuid(svm); } static void sev_es_init_vmcb(struct vcpu_svm *svm) { struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm); struct vmcb *vmcb = svm->vmcb01.ptr; - struct kvm_vcpu *vcpu = &svm->vcpu; svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; @@ -4445,8 +4490,12 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) * the VMSA will be NULL if this vCPU is the destination for intrahost * migration, and will be copied later. */ - if (svm->sev_es.vmsa && !svm->sev_es.snp_has_guest_vmsa) - svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); + if (!svm->sev_es.snp_has_guest_vmsa) { + if (svm->sev_es.vmsa) + svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); + else + svm->vmcb->control.vmsa_pa = INVALID_PAGE; + } if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES)) svm->vmcb->control.allowed_sev_features = sev->vmsa_features | @@ -4488,10 +4537,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) /* Can't intercept XSETBV, HV can't modify XCR0 directly */ svm_clr_intercept(svm, INTERCEPT_XSETBV); - - /* Clear intercepts on selected MSRs */ - set_msr_interception(vcpu, svm->msrpm, MSR_EFER, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_CR_PAT, 1, 1); } void sev_init_vmcb(struct vcpu_svm *svm) @@ -4880,7 +4925,7 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) /* * SEV-ES avoids host/guest cache coherency issues through - * WBINVD hooks issued via MMU notifiers during run-time, and + * WBNOINVD hooks issued via MMU notifiers during run-time, and * KVM's VM destroy path at shutdown. Those MMU notifier events * don't cover gmem since there is no requirement to map pages * to a HVA in order to use them for a running guest. While the diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ab9b947dbf4f..d9931c6c4bc6 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -72,8 +72,6 @@ MODULE_DEVICE_TABLE(x86cpu, svm_cpu_id); static bool erratum_383_found __read_mostly; -u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; - /* * Set osvw_len to higher value when updated Revision Guides * are published and we know what the new status bits are @@ -82,72 +80,6 @@ static uint64_t osvw_len = 4, osvw_status; static DEFINE_PER_CPU(u64, current_tsc_ratio); -#define X2APIC_MSR(x) (APIC_BASE_MSR + (x >> 4)) - -static const struct svm_direct_access_msrs { - u32 index; /* Index of the MSR */ - bool always; /* True if intercept is initially cleared */ -} direct_access_msrs[MAX_DIRECT_ACCESS_MSRS] = { - { .index = MSR_STAR, .always = true }, - { .index = MSR_IA32_SYSENTER_CS, .always = true }, - { .index = MSR_IA32_SYSENTER_EIP, .always = false }, - { .index = MSR_IA32_SYSENTER_ESP, .always = false }, -#ifdef CONFIG_X86_64 - { .index = MSR_GS_BASE, .always = true }, - { .index = MSR_FS_BASE, .always = true }, - { .index = MSR_KERNEL_GS_BASE, .always = true }, - { .index = MSR_LSTAR, .always = true }, - { .index = MSR_CSTAR, .always = true }, - { .index = MSR_SYSCALL_MASK, .always = true }, -#endif - { .index = MSR_IA32_SPEC_CTRL, .always = false }, - { .index = MSR_IA32_PRED_CMD, .always = false }, - { .index = MSR_IA32_FLUSH_CMD, .always = false }, - { .index = MSR_IA32_DEBUGCTLMSR, .always = false }, - { .index = MSR_IA32_LASTBRANCHFROMIP, .always = false }, - { .index = MSR_IA32_LASTBRANCHTOIP, .always = false }, - { .index = MSR_IA32_LASTINTFROMIP, .always = false }, - { .index = MSR_IA32_LASTINTTOIP, .always = false }, - { .index = MSR_IA32_XSS, .always = false }, - { .index = MSR_EFER, .always = false }, - { .index = MSR_IA32_CR_PAT, .always = false }, - { .index = MSR_AMD64_SEV_ES_GHCB, .always = true }, - { .index = MSR_TSC_AUX, .always = false }, - { .index = X2APIC_MSR(APIC_ID), .always = false }, - { .index = X2APIC_MSR(APIC_LVR), .always = false }, - { .index = X2APIC_MSR(APIC_TASKPRI), .always = false }, - { .index = X2APIC_MSR(APIC_ARBPRI), .always = false }, - { .index = X2APIC_MSR(APIC_PROCPRI), .always = false }, - { .index = X2APIC_MSR(APIC_EOI), .always = false }, - { .index = X2APIC_MSR(APIC_RRR), .always = false }, - { .index = X2APIC_MSR(APIC_LDR), .always = false }, - { .index = X2APIC_MSR(APIC_DFR), .always = false }, - { .index = X2APIC_MSR(APIC_SPIV), .always = false }, - { .index = X2APIC_MSR(APIC_ISR), .always = false }, - { .index = X2APIC_MSR(APIC_TMR), .always = false }, - { .index = X2APIC_MSR(APIC_IRR), .always = false }, - { .index = X2APIC_MSR(APIC_ESR), .always = false }, - { .index = X2APIC_MSR(APIC_ICR), .always = false }, - { .index = X2APIC_MSR(APIC_ICR2), .always = false }, - - /* - * Note: - * AMD does not virtualize APIC TSC-deadline timer mode, but it is - * emulated by KVM. When setting APIC LVTT (0x832) register bit 18, - * the AVIC hardware would generate GP fault. Therefore, always - * intercept the MSR 0x832, and do not setup direct_access_msr. - */ - { .index = X2APIC_MSR(APIC_LVTTHMR), .always = false }, - { .index = X2APIC_MSR(APIC_LVTPC), .always = false }, - { .index = X2APIC_MSR(APIC_LVT0), .always = false }, - { .index = X2APIC_MSR(APIC_LVT1), .always = false }, - { .index = X2APIC_MSR(APIC_LVTERR), .always = false }, - { .index = X2APIC_MSR(APIC_TMICT), .always = false }, - { .index = X2APIC_MSR(APIC_TMCCT), .always = false }, - { .index = X2APIC_MSR(APIC_TDCR), .always = false }, - { .index = MSR_INVALID, .always = false }, -}; - /* * These 2 parameters are used to config the controls for Pause-Loop Exiting: * pause_filter_count: On processors that support Pause filtering(indicated @@ -232,6 +164,7 @@ module_param(tsc_scaling, int, 0444); */ static bool avic; module_param(avic, bool, 0444); +module_param(enable_ipiv, bool, 0444); module_param(enable_device_posted_irqs, bool, 0444); @@ -264,33 +197,6 @@ static DEFINE_MUTEX(vmcb_dump_mutex); */ static int tsc_aux_uret_slot __read_mostly = -1; -static const u32 msrpm_ranges[] = {0, 0xc0000000, 0xc0010000}; - -#define NUM_MSR_MAPS ARRAY_SIZE(msrpm_ranges) -#define MSRS_RANGE_SIZE 2048 -#define MSRS_IN_RANGE (MSRS_RANGE_SIZE * 8 / 2) - -u32 svm_msrpm_offset(u32 msr) -{ - u32 offset; - int i; - - for (i = 0; i < NUM_MSR_MAPS; i++) { - if (msr < msrpm_ranges[i] || - msr >= msrpm_ranges[i] + MSRS_IN_RANGE) - continue; - - offset = (msr - msrpm_ranges[i]) / 4; /* 4 msrs per u8 */ - offset += (i * MSRS_RANGE_SIZE); /* add range offset */ - - /* Now we have the u8 offset - but need the u32 offset */ - return offset / 4; - } - - /* MSR not in any range */ - return MSR_INVALID; -} - static int get_npt_level(void) { #ifdef CONFIG_X86_64 @@ -757,50 +663,8 @@ static void clr_dr_intercepts(struct vcpu_svm *svm) recalc_intercepts(svm); } -static int direct_access_msr_slot(u32 msr) -{ - u32 i; - - for (i = 0; direct_access_msrs[i].index != MSR_INVALID; i++) - if (direct_access_msrs[i].index == msr) - return i; - - return -ENOENT; -} - -static void set_shadow_msr_intercept(struct kvm_vcpu *vcpu, u32 msr, int read, - int write) -{ - struct vcpu_svm *svm = to_svm(vcpu); - int slot = direct_access_msr_slot(msr); - - if (slot == -ENOENT) - return; - - /* Set the shadow bitmaps to the desired intercept states */ - if (read) - set_bit(slot, svm->shadow_msr_intercept.read); - else - clear_bit(slot, svm->shadow_msr_intercept.read); - - if (write) - set_bit(slot, svm->shadow_msr_intercept.write); - else - clear_bit(slot, svm->shadow_msr_intercept.write); -} - -static bool valid_msr_intercept(u32 index) -{ - return direct_access_msr_slot(index) != -ENOENT; -} - static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) { - u8 bit_write; - unsigned long tmp; - u32 offset; - u32 *msrpm; - /* * For non-nested case: * If the L01 MSR bitmap does not intercept the MSR, then we need to @@ -810,90 +674,102 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) * If the L02 MSR bitmap does not intercept the MSR, then we need to * save it. */ - msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm: - to_svm(vcpu)->msrpm; + void *msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm : + to_svm(vcpu)->msrpm; - offset = svm_msrpm_offset(msr); - bit_write = 2 * (msr & 0x0f) + 1; - tmp = msrpm[offset]; - - BUG_ON(offset == MSR_INVALID); - - return test_bit(bit_write, &tmp); + return svm_test_msr_bitmap_write(msrpm, msr); } -static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm, - u32 msr, int read, int write) +void svm_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set) { struct vcpu_svm *svm = to_svm(vcpu); - u8 bit_read, bit_write; - unsigned long tmp; - u32 offset; + void *msrpm = svm->msrpm; - /* - * If this warning triggers extend the direct_access_msrs list at the - * beginning of the file - */ - WARN_ON(!valid_msr_intercept(msr)); + /* Don't disable interception for MSRs userspace wants to handle. */ + if (type & MSR_TYPE_R) { + if (!set && kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) + svm_clear_msr_bitmap_read(msrpm, msr); + else + svm_set_msr_bitmap_read(msrpm, msr); + } - /* Enforce non allowed MSRs to trap */ - if (read && !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) - read = 0; - - if (write && !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) - write = 0; - - offset = svm_msrpm_offset(msr); - bit_read = 2 * (msr & 0x0f); - bit_write = 2 * (msr & 0x0f) + 1; - tmp = msrpm[offset]; - - BUG_ON(offset == MSR_INVALID); - - read ? clear_bit(bit_read, &tmp) : set_bit(bit_read, &tmp); - write ? clear_bit(bit_write, &tmp) : set_bit(bit_write, &tmp); - - msrpm[offset] = tmp; + if (type & MSR_TYPE_W) { + if (!set && kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) + svm_clear_msr_bitmap_write(msrpm, msr); + else + svm_set_msr_bitmap_write(msrpm, msr); + } svm_hv_vmcb_dirty_nested_enlightenments(vcpu); svm->nested.force_msr_bitmap_recalc = true; } -void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr, - int read, int write) +void *svm_alloc_permissions_map(unsigned long size, gfp_t gfp_mask) { - set_shadow_msr_intercept(vcpu, msr, read, write); - set_msr_interception_bitmap(vcpu, msrpm, msr, read, write); -} - -u32 *svm_vcpu_alloc_msrpm(void) -{ - unsigned int order = get_order(MSRPM_SIZE); - struct page *pages = alloc_pages(GFP_KERNEL_ACCOUNT, order); - u32 *msrpm; + unsigned int order = get_order(size); + struct page *pages = alloc_pages(gfp_mask, order); + void *pm; if (!pages) return NULL; - msrpm = page_address(pages); - memset(msrpm, 0xff, PAGE_SIZE * (1 << order)); + /* + * Set all bits in the permissions map so that all MSR and I/O accesses + * are intercepted by default. + */ + pm = page_address(pages); + memset(pm, 0xff, PAGE_SIZE * (1 << order)); - return msrpm; + return pm; } -void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm) +static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu) { - int i; + bool intercept = !(to_svm(vcpu)->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK); - for (i = 0; direct_access_msrs[i].index != MSR_INVALID; i++) { - if (!direct_access_msrs[i].always) - continue; - set_msr_interception(vcpu, msrpm, direct_access_msrs[i].index, 1, 1); - } + svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTBRANCHFROMIP, MSR_TYPE_RW, intercept); + svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTBRANCHTOIP, MSR_TYPE_RW, intercept); + 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)) + svm_set_intercept_for_msr(vcpu, MSR_IA32_DEBUGCTLMSR, MSR_TYPE_RW, intercept); } void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) { + static const u32 x2avic_passthrough_msrs[] = { + X2APIC_MSR(APIC_ID), + X2APIC_MSR(APIC_LVR), + X2APIC_MSR(APIC_TASKPRI), + X2APIC_MSR(APIC_ARBPRI), + X2APIC_MSR(APIC_PROCPRI), + X2APIC_MSR(APIC_EOI), + X2APIC_MSR(APIC_RRR), + X2APIC_MSR(APIC_LDR), + X2APIC_MSR(APIC_DFR), + X2APIC_MSR(APIC_SPIV), + X2APIC_MSR(APIC_ISR), + X2APIC_MSR(APIC_TMR), + X2APIC_MSR(APIC_IRR), + X2APIC_MSR(APIC_ESR), + X2APIC_MSR(APIC_ICR), + X2APIC_MSR(APIC_ICR2), + + /* + * Note! Always intercept LVTT, as TSC-deadline timer mode + * isn't virtualized by hardware, and the CPU will generate a + * #GP instead of a #VMEXIT. + */ + X2APIC_MSR(APIC_LVTTHMR), + X2APIC_MSR(APIC_LVTPC), + X2APIC_MSR(APIC_LVT0), + X2APIC_MSR(APIC_LVT1), + X2APIC_MSR(APIC_LVTERR), + X2APIC_MSR(APIC_TMICT), + X2APIC_MSR(APIC_TMCCT), + X2APIC_MSR(APIC_TDCR), + }; int i; if (intercept == svm->x2avic_msrs_intercepted) @@ -902,84 +778,79 @@ void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) if (!x2avic_enabled) return; - for (i = 0; i < MAX_DIRECT_ACCESS_MSRS; i++) { - int index = direct_access_msrs[i].index; - - if ((index < APIC_BASE_MSR) || - (index > APIC_BASE_MSR + 0xff)) - continue; - set_msr_interception(&svm->vcpu, svm->msrpm, index, - !intercept, !intercept); - } + for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) + svm_set_intercept_for_msr(&svm->vcpu, x2avic_passthrough_msrs[i], + MSR_TYPE_RW, intercept); svm->x2avic_msrs_intercepted = intercept; } -void svm_vcpu_free_msrpm(u32 *msrpm) +void svm_vcpu_free_msrpm(void *msrpm) { __free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE)); } -static void svm_msr_filter_changed(struct kvm_vcpu *vcpu) +static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - u32 i; + + svm_disable_intercept_for_msr(vcpu, MSR_STAR, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW); + +#ifdef CONFIG_X86_64 + svm_disable_intercept_for_msr(vcpu, MSR_GS_BASE, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_FS_BASE, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_KERNEL_GS_BASE, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_LSTAR, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_CSTAR, MSR_TYPE_RW); + svm_disable_intercept_for_msr(vcpu, MSR_SYSCALL_MASK, MSR_TYPE_RW); +#endif + + if (lbrv) + svm_recalc_lbr_msr_intercepts(vcpu); + + if (cpu_feature_enabled(X86_FEATURE_IBPB)) + svm_set_intercept_for_msr(vcpu, MSR_IA32_PRED_CMD, MSR_TYPE_W, + !guest_has_pred_cmd_msr(vcpu)); + + if (cpu_feature_enabled(X86_FEATURE_FLUSH_L1D)) + svm_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, + !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); /* - * Set intercept permissions for all direct access MSRs again. They - * will automatically get filtered through the MSR filter, so we are - * back in sync after this. + * Disable interception of SPEC_CTRL if KVM doesn't need to manually + * context switch the MSR (SPEC_CTRL is virtualized by the CPU), or if + * the guest has a non-zero SPEC_CTRL value, i.e. is likely actively + * using SPEC_CTRL. */ - for (i = 0; direct_access_msrs[i].index != MSR_INVALID; i++) { - u32 msr = direct_access_msrs[i].index; - u32 read = test_bit(i, svm->shadow_msr_intercept.read); - u32 write = test_bit(i, svm->shadow_msr_intercept.write); - - set_msr_interception_bitmap(vcpu, svm->msrpm, msr, read, write); - } -} - -static void add_msr_offset(u32 offset) -{ - int i; - - for (i = 0; i < MSRPM_OFFSETS; ++i) { - - /* Offset already in list? */ - if (msrpm_offsets[i] == offset) - return; - - /* Slot used by another offset? */ - if (msrpm_offsets[i] != MSR_INVALID) - continue; - - /* Add offset to list */ - msrpm_offsets[i] = offset; - - return; - } + if (cpu_feature_enabled(X86_FEATURE_V_SPEC_CTRL)) + svm_set_intercept_for_msr(vcpu, MSR_IA32_SPEC_CTRL, MSR_TYPE_RW, + !guest_has_spec_ctrl_msr(vcpu)); + else + svm_set_intercept_for_msr(vcpu, MSR_IA32_SPEC_CTRL, MSR_TYPE_RW, + !svm->spec_ctrl); /* - * If this BUG triggers the msrpm_offsets table has an overflow. Just - * increase MSRPM_OFFSETS in this case. + * Intercept SYSENTER_EIP and SYSENTER_ESP when emulating an Intel CPU, + * as AMD hardware only store 32 bits, whereas Intel CPUs track 64 bits. */ - BUG(); -} + svm_set_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW, + guest_cpuid_is_intel_compatible(vcpu)); + svm_set_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW, + guest_cpuid_is_intel_compatible(vcpu)); -static void init_msrpm_offsets(void) -{ - int i; - - memset(msrpm_offsets, 0xff, sizeof(msrpm_offsets)); - - for (i = 0; direct_access_msrs[i].index != MSR_INVALID; i++) { - u32 offset; - - offset = svm_msrpm_offset(direct_access_msrs[i].index); - BUG_ON(offset == MSR_INVALID); - - add_msr_offset(offset); + if (kvm_aperfmperf_in_guest(vcpu->kvm)) { + svm_disable_intercept_for_msr(vcpu, MSR_IA32_APERF, MSR_TYPE_R); + svm_disable_intercept_for_msr(vcpu, MSR_IA32_MPERF, MSR_TYPE_R); } + + if (sev_es_guest(vcpu->kvm)) + sev_es_recalc_msr_intercepts(vcpu); + + /* + * x2APIC intercepts are modified on-demand and cannot be filtered by + * userspace. + */ } void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb) @@ -998,13 +869,7 @@ void svm_enable_lbrv(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); - - if (sev_es_guest(vcpu->kvm)) - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_DEBUGCTLMSR, 1, 1); + svm_recalc_lbr_msr_intercepts(vcpu); /* Move the LBR msrs to the vmcb02 so that the guest can see them. */ if (is_guest_mode(vcpu)) @@ -1016,12 +881,8 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm); - svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 0, 0); + svm_recalc_lbr_msr_intercepts(vcpu); /* * Move the LBR msrs back to the vmcb01 to avoid copying them @@ -1176,9 +1037,10 @@ void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu) } /* Evaluate instruction intercepts that depend on guest CPUID features. */ -static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu, - struct vcpu_svm *svm) +static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) { + struct vcpu_svm *svm = to_svm(vcpu); + /* * Intercept INVPCID if shadow paging is enabled to sync/free shadow * roots, or if INVPCID is disabled in the guest to inject #UD. @@ -1197,24 +1059,11 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu, else svm_set_intercept(svm, INTERCEPT_RDTSCP); } -} - -static inline void init_vmcb_after_set_cpuid(struct kvm_vcpu *vcpu) -{ - struct vcpu_svm *svm = to_svm(vcpu); if (guest_cpuid_is_intel_compatible(vcpu)) { - /* - * We must intercept SYSENTER_EIP and SYSENTER_ESP - * accesses because the processor only stores 32 bits. - * For the same reason we cannot use virtual VMLOAD/VMSAVE. - */ svm_set_intercept(svm, INTERCEPT_VMLOAD); svm_set_intercept(svm, INTERCEPT_VMSAVE); svm->vmcb->control.virt_ext &= ~VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; - - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_EIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_ESP, 0, 0); } else { /* * If hardware supports Virtual VMLOAD VMSAVE then enable it @@ -1225,12 +1074,15 @@ static inline void init_vmcb_after_set_cpuid(struct kvm_vcpu *vcpu) svm_clr_intercept(svm, INTERCEPT_VMSAVE); svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; } - /* No need to intercept these MSRs */ - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_EIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_ESP, 1, 1); } } +static void svm_recalc_intercepts_after_set_cpuid(struct kvm_vcpu *vcpu) +{ + svm_recalc_instruction_intercepts(vcpu); + svm_recalc_msr_intercepts(vcpu); +} + static void init_vmcb(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1353,15 +1205,6 @@ static void init_vmcb(struct kvm_vcpu *vcpu) svm_clr_intercept(svm, INTERCEPT_PAUSE); } - svm_recalc_instruction_intercepts(vcpu, svm); - - /* - * If the host supports V_SPEC_CTRL then disable the interception - * of MSR_IA32_SPEC_CTRL. - */ - if (boot_cpu_has(X86_FEATURE_V_SPEC_CTRL)) - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1); - if (kvm_vcpu_apicv_active(vcpu)) avic_init_vmcb(svm, vmcb); @@ -1381,7 +1224,8 @@ static void init_vmcb(struct kvm_vcpu *vcpu) sev_init_vmcb(svm); svm_hv_init_vmcb(vmcb); - init_vmcb_after_set_cpuid(vcpu); + + svm_recalc_intercepts_after_set_cpuid(vcpu); vmcb_mark_all_dirty(vmcb); @@ -1392,8 +1236,6 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - svm_vcpu_init_msrpm(vcpu, svm->msrpm); - svm_init_osvw(vcpu); if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) @@ -1490,13 +1332,15 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + WARN_ON_ONCE(!list_empty(&svm->ir_list)); + svm_leave_nested(vcpu); svm_free_nested(svm); sev_free_vcpu(vcpu); __free_page(__sme_pa_to_page(svm->vmcb01.pa)); - __free_pages(virt_to_page(svm->msrpm), get_order(MSRPM_SIZE)); + svm_vcpu_free_msrpm(svm->msrpm); } #ifdef CONFIG_CPU_MITIGATIONS @@ -2880,12 +2724,11 @@ static int svm_get_feature_msr(u32 msr, u64 *data) return 0; } -static bool -sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, struct msr_data *msr_info) +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 && - svm_msrpm_offset(msr_info->index) != MSR_INVALID && !msr_write_intercepted(vcpu, msr_info->index); } @@ -3116,11 +2959,11 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) * * For nested: * The handling of the MSR bitmap for L2 guests is done in - * nested_svm_vmrun_msrpm. + * nested_svm_merge_msrpm(). * We update the L1 MSR bit as well since it will end up * touching the MSR anyway now. */ - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1); + svm_disable_intercept_for_msr(vcpu, MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); break; case MSR_AMD64_VIRT_SPEC_CTRL: if (!msr->host_initiated && @@ -3186,8 +3029,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) /* * TSC_AUX is usually changed only during boot and never read - * directly. Intercept TSC_AUX instead of exposing it to the - * guest via direct_access_msrs, and switch it via user return. + * directly. Intercept TSC_AUX and switch it via user return. */ preempt_disable(); ret = kvm_set_user_return_msr(tsc_aux_uret_slot, data, -1ull); @@ -4389,9 +4231,9 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, bool spec_ctrl_in guest_state_exit_irqoff(); } -static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, - bool force_immediate_exit) +static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { + bool force_immediate_exit = run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT; struct vcpu_svm *svm = to_svm(vcpu); bool spec_ctrl_intercepted = msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL); @@ -4438,10 +4280,13 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, svm_hv_update_vp_id(svm->vmcb, vcpu); /* - * Run with all-zero DR6 unless needed, so that we can get the exact cause - * of a #DB. + * Run with all-zero DR6 unless the guest can write DR6 freely, so that + * KVM can get the exact cause of a #DB. Note, loading guest DR6 from + * KVM's snapshot is only necessary when DR accesses won't exit. */ - if (likely(!(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))) + if (unlikely(run_flags & KVM_RUN_LOAD_GUEST_DR6)) + svm_set_dr6(vcpu, vcpu->arch.dr6); + else if (likely(!(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))) svm_set_dr6(vcpu, DR6_ACTIVE_LOW); clgi(); @@ -4621,20 +4466,10 @@ 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); - svm_recalc_instruction_intercepts(vcpu, svm); - - if (boot_cpu_has(X86_FEATURE_IBPB)) - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_PRED_CMD, 0, - !!guest_has_pred_cmd_msr(vcpu)); - - if (boot_cpu_has(X86_FEATURE_FLUSH_L1D)) - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_FLUSH_CMD, 0, - !!guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - if (sev_guest(vcpu->kvm)) sev_vcpu_after_set_cpuid(svm); - init_vmcb_after_set_cpuid(vcpu); + svm_recalc_intercepts_after_set_cpuid(vcpu); } static bool svm_has_wbinvd_exit(void) @@ -5185,7 +5020,7 @@ static int svm_vm_init(struct kvm *kvm) } if (!pause_filter_count || !pause_filter_thresh) - kvm->arch.pause_in_guest = true; + kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE); if (enable_apicv) { int ret = avic_vm_init(kvm); @@ -5252,7 +5087,6 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .set_idt = svm_set_idt, .get_gdt = svm_get_gdt, .set_gdt = svm_set_gdt, - .set_dr6 = svm_set_dr6, .set_dr7 = svm_set_dr7, .sync_dirty_debug_regs = svm_sync_dirty_debug_regs, .cache_reg = svm_cache_reg, @@ -5337,7 +5171,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .apic_init_signal_blocked = svm_apic_init_signal_blocked, - .msr_filter_changed = svm_msr_filter_changed, + .recalc_msr_intercepts = svm_recalc_msr_intercepts, .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, @@ -5473,11 +5307,8 @@ static __init void svm_set_cpu_caps(void) static __init int svm_hardware_setup(void) { - int cpu; - struct page *iopm_pages; void *iopm_va; - int r; - unsigned int order = get_order(IOPM_SIZE); + int cpu, r; /* * NX is required for shadow paging and for NPT if the NX huge pages @@ -5489,17 +5320,6 @@ static __init int svm_hardware_setup(void) } kvm_enable_efer_bits(EFER_NX); - iopm_pages = alloc_pages(GFP_KERNEL, order); - - if (!iopm_pages) - return -ENOMEM; - - iopm_va = page_address(iopm_pages); - memset(iopm_va, 0xff, PAGE_SIZE * (1 << order)); - iopm_base = __sme_page_pa(iopm_pages); - - init_msrpm_offsets(); - kvm_caps.supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); @@ -5533,6 +5353,10 @@ static __init int svm_hardware_setup(void) if (nested) { pr_info("Nested Virtualization enabled\n"); kvm_enable_efer_bits(EFER_SVME | EFER_LMSLE); + + r = nested_svm_init_msrpm_merge_offsets(); + if (r) + return r; } /* @@ -5564,6 +5388,13 @@ static __init int svm_hardware_setup(void) else pr_info("LBR virtualization supported\n"); } + + iopm_va = svm_alloc_permissions_map(IOPM_SIZE, GFP_KERNEL); + if (!iopm_va) + return -ENOMEM; + + iopm_base = __sme_set(__pa(iopm_va)); + /* * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which * may be modified by svm_adjust_mmio_mask()), as well as nrips. @@ -5581,6 +5412,7 @@ static __init int svm_hardware_setup(void) enable_apicv = avic = avic && avic_hardware_setup(); if (!enable_apicv) { + enable_ipiv = false; svm_x86_ops.vcpu_blocking = NULL; svm_x86_ops.vcpu_unblocking = NULL; svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL; @@ -5662,6 +5494,8 @@ static int __init svm_init(void) { int r; + KVM_SANITY_CHECK_VM_STRUCT_SIZE(kvm_svm); + __unused_size_checks(); if (!kvm_is_svm_supported()) diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index e6f3c6a153a0..58b9d168e0c8 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -44,9 +44,6 @@ static inline struct page *__sme_pa_to_page(unsigned long pa) #define IOPM_SIZE PAGE_SIZE * 3 #define MSRPM_SIZE PAGE_SIZE * 2 -#define MAX_DIRECT_ACCESS_MSRS 48 -#define MSRPM_OFFSETS 32 -extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; extern bool npt_enabled; extern int nrips; extern int vgif; @@ -113,6 +110,7 @@ struct kvm_sev_info { void *guest_req_buf; /* Bounce buffer for SNP Guest Request input */ void *guest_resp_buf; /* Bounce buffer for SNP Guest Request output */ struct mutex guest_req_mutex; /* Must acquire before using bounce buffers */ + cpumask_var_t have_run_cpus; /* CPUs that have done VMRUN for this VM. */ }; #define SEV_POLICY_NODBG BIT_ULL(0) @@ -123,8 +121,8 @@ struct kvm_svm { /* Struct members for AVIC */ u32 avic_vm_id; - struct page *avic_logical_id_table_page; - struct page *avic_physical_id_table_page; + u32 *avic_logical_id_table; + u64 *avic_physical_id_table; struct hlist_node hnode; struct kvm_sev_info sev_info; @@ -189,8 +187,11 @@ struct svm_nested_state { u64 vmcb12_gpa; u64 last_vmcb12_gpa; - /* These are the merged vectors */ - u32 *msrpm; + /* + * The MSR permissions map used for vmcb02, which is the merge result + * of vmcb01 and vmcb12 + */ + void *msrpm; /* A VMRUN has started but has not yet been performed, so * we cannot inject a nested vmexit yet. */ @@ -271,7 +272,7 @@ struct vcpu_svm { */ u64 virt_spec_ctrl; - u32 *msrpm; + void *msrpm; ulong nmi_iret_rip; @@ -306,24 +307,26 @@ struct vcpu_svm { u32 ldr_reg; u32 dfr_reg; - struct page *avic_backing_page; - u64 *avic_physical_id_cache; + + /* This is essentially a shadow of the vCPU's actual entry in the + * Physical ID table that is programmed into the VMCB, i.e. that is + * seen by the CPU. If IPI virtualization is disabled, IsRunning is + * only ever set in the shadow, i.e. is never propagated to the "real" + * table, so that hardware never sees IsRunning=1. + */ + u64 avic_physical_id_entry; /* - * Per-vcpu list of struct amd_svm_iommu_ir: - * This is used mainly to store interrupt remapping information used - * when update the vcpu affinity. This avoids the need to scan for - * IRTE and try to match ga_tag in the IOMMU driver. + * Per-vCPU list of irqfds that are eligible to post IRQs directly to + * the vCPU (a.k.a. device posted IRQs, a.k.a. IRQ bypass). The list + * is used to reconfigure IRTEs when the vCPU is loaded/put (to set the + * target pCPU), when AVIC is toggled on/off (to (de)activate bypass), + * and if the irqfd becomes ineligible for posting (to put the IRTE + * back into remapped mode). */ struct list_head ir_list; spinlock_t ir_list_lock; - /* Save desired MSR intercept (read: pass-through) state */ - struct { - DECLARE_BITMAP(read, MAX_DIRECT_ACCESS_MSRS); - DECLARE_BITMAP(write, MAX_DIRECT_ACCESS_MSRS); - } shadow_msr_intercept; - struct vcpu_sev_es_state sev_es; bool guest_state_loaded; @@ -613,17 +616,74 @@ static inline void svm_vmgexit_no_action(struct vcpu_svm *svm, u64 data) svm_vmgexit_set_return_code(svm, GHCB_HV_RESP_NO_ACTION, data); } -/* svm.c */ -#define MSR_INVALID 0xffffffffU +/* + * The MSRPM is 8KiB in size, divided into four 2KiB ranges (the fourth range + * is reserved). Each MSR within a range is covered by two bits, one each for + * read (bit 0) and write (bit 1), where a bit value of '1' means intercepted. + */ +#define SVM_MSRPM_BYTES_PER_RANGE 2048 +#define SVM_BITS_PER_MSR 2 +#define SVM_MSRS_PER_BYTE (BITS_PER_BYTE / SVM_BITS_PER_MSR) +#define SVM_MSRS_PER_RANGE (SVM_MSRPM_BYTES_PER_RANGE * SVM_MSRS_PER_BYTE) +static_assert(SVM_MSRS_PER_RANGE == 8192); +#define SVM_MSRPM_OFFSET_MASK (SVM_MSRS_PER_RANGE - 1) + +static __always_inline int svm_msrpm_bit_nr(u32 msr) +{ + int range_nr; + + switch (msr & ~SVM_MSRPM_OFFSET_MASK) { + case 0: + range_nr = 0; + break; + case 0xc0000000: + range_nr = 1; + break; + case 0xc0010000: + range_nr = 2; + break; + default: + return -EINVAL; + } + + return range_nr * SVM_MSRPM_BYTES_PER_RANGE * BITS_PER_BYTE + + (msr & SVM_MSRPM_OFFSET_MASK) * SVM_BITS_PER_MSR; +} + +#define __BUILD_SVM_MSR_BITMAP_HELPER(rtype, action, bitop, access, bit_rw) \ +static inline rtype svm_##action##_msr_bitmap_##access(unsigned long *bitmap, \ + u32 msr) \ +{ \ + int bit_nr; \ + \ + bit_nr = svm_msrpm_bit_nr(msr); \ + if (bit_nr < 0) \ + return (rtype)true; \ + \ + return bitop##_bit(bit_nr + bit_rw, bitmap); \ +} + +#define BUILD_SVM_MSR_BITMAP_HELPERS(ret_type, action, bitop) \ + __BUILD_SVM_MSR_BITMAP_HELPER(ret_type, action, bitop, read, 0) \ + __BUILD_SVM_MSR_BITMAP_HELPER(ret_type, action, bitop, write, 1) + +BUILD_SVM_MSR_BITMAP_HELPERS(bool, test, test) +BUILD_SVM_MSR_BITMAP_HELPERS(void, clear, __clear) +BUILD_SVM_MSR_BITMAP_HELPERS(void, set, __set) #define DEBUGCTL_RESERVED_BITS (~DEBUGCTLMSR_LBR) +/* svm.c */ extern bool dump_invalid_vmcb; -u32 svm_msrpm_offset(u32 msr); -u32 *svm_vcpu_alloc_msrpm(void); -void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm); -void svm_vcpu_free_msrpm(u32 *msrpm); +void *svm_alloc_permissions_map(unsigned long size, gfp_t gfp_mask); + +static inline void *svm_vcpu_alloc_msrpm(void) +{ + return svm_alloc_permissions_map(MSRPM_SIZE, GFP_KERNEL_ACCOUNT); +} + +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); @@ -643,6 +703,20 @@ void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool disable); void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, int trig_mode, int vec); +void svm_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set); + +static inline void svm_disable_intercept_for_msr(struct kvm_vcpu *vcpu, + u32 msr, int type) +{ + svm_set_intercept_for_msr(vcpu, msr, type, false); +} + +static inline void svm_enable_intercept_for_msr(struct kvm_vcpu *vcpu, + u32 msr, int type) +{ + svm_set_intercept_for_msr(vcpu, msr, type, true); +} + /* nested.c */ #define NESTED_EXIT_HOST 0 /* Exit handled on host level */ @@ -671,6 +745,8 @@ static inline bool nested_exit_on_nmi(struct vcpu_svm *svm) return vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_NMI); } +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); void svm_leave_nested(struct kvm_vcpu *vcpu); @@ -721,7 +797,8 @@ extern struct kvm_x86_nested_ops svm_nested_ops; BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED) | \ BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | \ BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) | \ - BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) \ + BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) | \ + BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG) \ ) bool avic_hardware_setup(void); @@ -736,8 +813,9 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu); void avic_vcpu_put(struct kvm_vcpu *vcpu); void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu); void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu); -int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set); +int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm, + unsigned int host_irq, uint32_t guest_irq, + struct kvm_vcpu *vcpu, u32 vector); void avic_vcpu_blocking(struct kvm_vcpu *vcpu); void avic_vcpu_unblocking(struct kvm_vcpu *vcpu); void avic_ring_doorbell(struct kvm_vcpu *vcpu); @@ -752,6 +830,7 @@ void sev_init_vmcb(struct vcpu_svm *svm); void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm); int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in); void sev_es_vcpu_reset(struct vcpu_svm *svm); +void sev_es_recalc_msr_intercepts(struct kvm_vcpu *vcpu); void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector); void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_area *hostsa); void sev_es_unmap_ghcb(struct vcpu_svm *svm); diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 0c61153b275f..235c4af6b692 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -169,6 +169,9 @@ SYM_FUNC_START(__svm_vcpu_run) #endif mov VCPU_RDI(%_ASM_DI), %_ASM_DI + /* Clobbers EFLAGS.ZF */ + VM_CLEAR_CPU_BUFFERS + /* Enter guest mode */ 3: vmrun %_ASM_AX 4: @@ -335,6 +338,9 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) mov SVM_current_vmcb(%rdi), %rax mov KVM_VMCB_pa(%rax), %rax + /* Clobbers EFLAGS.ZF */ + VM_CLEAR_CPU_BUFFERS + /* Enter guest mode */ 1: vmrun %rax 2: diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index ba736cbb0587..57d79fd31df0 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -260,6 +260,86 @@ TRACE_EVENT(kvm_cpuid, __entry->used_max_basic ? ", used max basic" : "") ); +#define kvm_deliver_mode \ + {0x0, "Fixed"}, \ + {0x1, "LowPrio"}, \ + {0x2, "SMI"}, \ + {0x3, "Res3"}, \ + {0x4, "NMI"}, \ + {0x5, "INIT"}, \ + {0x6, "SIPI"}, \ + {0x7, "ExtINT"} + +#ifdef CONFIG_KVM_IOAPIC +TRACE_EVENT(kvm_ioapic_set_irq, + TP_PROTO(__u64 e, int pin, bool coalesced), + TP_ARGS(e, pin, coalesced), + + TP_STRUCT__entry( + __field( __u64, e ) + __field( int, pin ) + __field( bool, coalesced ) + ), + + TP_fast_assign( + __entry->e = e; + __entry->pin = pin; + __entry->coalesced = coalesced; + ), + + TP_printk("pin %u dst %x vec %u (%s|%s|%s%s)%s", + __entry->pin, (u8)(__entry->e >> 56), (u8)__entry->e, + __print_symbolic((__entry->e >> 8 & 0x7), kvm_deliver_mode), + (__entry->e & (1<<11)) ? "logical" : "physical", + (__entry->e & (1<<15)) ? "level" : "edge", + (__entry->e & (1<<16)) ? "|masked" : "", + __entry->coalesced ? " (coalesced)" : "") +); + +TRACE_EVENT(kvm_ioapic_delayed_eoi_inj, + TP_PROTO(__u64 e), + TP_ARGS(e), + + TP_STRUCT__entry( + __field( __u64, e ) + ), + + TP_fast_assign( + __entry->e = e; + ), + + TP_printk("dst %x vec %u (%s|%s|%s%s)", + (u8)(__entry->e >> 56), (u8)__entry->e, + __print_symbolic((__entry->e >> 8 & 0x7), kvm_deliver_mode), + (__entry->e & (1<<11)) ? "logical" : "physical", + (__entry->e & (1<<15)) ? "level" : "edge", + (__entry->e & (1<<16)) ? "|masked" : "") +); +#endif + +TRACE_EVENT(kvm_msi_set_irq, + TP_PROTO(__u64 address, __u64 data), + TP_ARGS(address, data), + + TP_STRUCT__entry( + __field( __u64, address ) + __field( __u64, data ) + ), + + TP_fast_assign( + __entry->address = address; + __entry->data = data; + ), + + TP_printk("dst %llx vec %u (%s|%s|%s%s)", + (u8)(__entry->address >> 12) | ((__entry->address >> 32) & 0xffffff00), + (u8)__entry->data, + __print_symbolic((__entry->data >> 8 & 0x7), kvm_deliver_mode), + (__entry->address & (1<<2)) ? "logical" : "physical", + (__entry->data & (1<<15)) ? "level" : "edge", + (__entry->address & (1<<3)) ? "|rh" : "") +); + #define AREG(x) { APIC_##x, "APIC_" #x } #define kvm_trace_symbol_apic \ @@ -1096,37 +1176,32 @@ TRACE_EVENT(kvm_smm_transition, * Tracepoint for VT-d posted-interrupts and AMD-Vi Guest Virtual APIC. */ TRACE_EVENT(kvm_pi_irte_update, - TP_PROTO(unsigned int host_irq, unsigned int vcpu_id, - unsigned int gsi, unsigned int gvec, - u64 pi_desc_addr, bool set), - TP_ARGS(host_irq, vcpu_id, gsi, gvec, pi_desc_addr, set), + TP_PROTO(unsigned int host_irq, struct kvm_vcpu *vcpu, + unsigned int gsi, unsigned int gvec, bool set), + TP_ARGS(host_irq, vcpu, gsi, gvec, set), TP_STRUCT__entry( __field( unsigned int, host_irq ) - __field( unsigned int, vcpu_id ) + __field( int, vcpu_id ) __field( unsigned int, gsi ) __field( unsigned int, gvec ) - __field( u64, pi_desc_addr ) __field( bool, set ) ), TP_fast_assign( __entry->host_irq = host_irq; - __entry->vcpu_id = vcpu_id; + __entry->vcpu_id = vcpu ? vcpu->vcpu_id : -1; __entry->gsi = gsi; __entry->gvec = gvec; - __entry->pi_desc_addr = pi_desc_addr; __entry->set = set; ), - TP_printk("PI is %s for irq %u, vcpu %u, gsi: 0x%x, " - "gvec: 0x%x, pi_desc_addr: 0x%llx", + TP_printk("PI is %s for irq %u, vcpu %d, gsi: 0x%x, gvec: 0x%x", __entry->set ? "enabled and being updated" : "disabled", __entry->host_irq, __entry->vcpu_id, __entry->gsi, - __entry->gvec, - __entry->pi_desc_addr) + __entry->gvec) ); /* diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index cb6588238f46..5316c27f6099 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -15,7 +15,6 @@ 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_pml; -extern bool __read_mostly enable_ipiv; extern int __read_mostly pt_mode; #define PT_MODE_SYSTEM 0 diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h index a0c5e8781c33..bc5ece76533a 100644 --- a/arch/x86/kvm/vmx/common.h +++ b/arch/x86/kvm/vmx/common.h @@ -53,8 +53,6 @@ struct vcpu_vt { #ifdef CONFIG_X86_64 u64 msr_host_kernel_gs_base; #endif - - unsigned long host_debugctlmsr; }; #ifdef CONFIG_KVM_INTEL_TDX diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index d1e02e567b57..dbab1c15b0cd 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -29,40 +29,8 @@ static __init int vt_hardware_setup(void) if (ret) return ret; - /* - * Update vt_x86_ops::vm_size here so it is ready before - * kvm_ops_update() is called in kvm_x86_vendor_init(). - * - * Note, the actual bringing up of TDX must be done after - * kvm_ops_update() because enabling TDX requires enabling - * hardware virtualization first, i.e., all online CPUs must - * be in post-VMXON state. This means the @vm_size here - * may be updated to TDX's size but TDX may fail to enable - * at later time. - * - * The VMX/VT code could update kvm_x86_ops::vm_size again - * after bringing up TDX, but this would require exporting - * either kvm_x86_ops or kvm_ops_update() from the base KVM - * module, which looks overkill. Anyway, the worst case here - * is KVM may allocate couple of more bytes than needed for - * each VM. - */ - if (enable_tdx) { - vt_x86_ops.vm_size = max_t(unsigned int, vt_x86_ops.vm_size, - sizeof(struct kvm_tdx)); - /* - * Note, TDX may fail to initialize in a later time in - * vt_init(), in which case it is not necessary to setup - * those callbacks. But making them valid here even - * when TDX fails to init later is fine because those - * callbacks won't be called if the VM isn't TDX guest. - */ - vt_x86_ops.link_external_spt = tdx_sept_link_private_spt; - vt_x86_ops.set_external_spte = tdx_sept_set_private_spte; - 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; - } + if (enable_tdx) + tdx_hardware_setup(); return 0; } @@ -175,12 +143,12 @@ static int vt_vcpu_pre_run(struct kvm_vcpu *vcpu) return vmx_vcpu_pre_run(vcpu); } -static fastpath_t vt_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) +static fastpath_t vt_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { if (is_td_vcpu(vcpu)) - return tdx_vcpu_run(vcpu, force_immediate_exit); + return tdx_vcpu_run(vcpu, run_flags); - return vmx_vcpu_run(vcpu, force_immediate_exit); + return vmx_vcpu_run(vcpu, run_flags); } static int vt_handle_exit(struct kvm_vcpu *vcpu, @@ -220,7 +188,7 @@ static int vt_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return vmx_get_msr(vcpu, msr_info); } -static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) +static void vt_recalc_msr_intercepts(struct kvm_vcpu *vcpu) { /* * TDX doesn't allow VMM to configure interception of MSR accesses. @@ -231,7 +199,7 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) if (is_td_vcpu(vcpu)) return; - vmx_msr_filter_changed(vcpu); + vmx_recalc_msr_intercepts(vcpu); } static int vt_complete_emulated_msr(struct kvm_vcpu *vcpu, int err) @@ -489,14 +457,6 @@ static void vt_set_gdt(struct kvm_vcpu *vcpu, struct desc_ptr *dt) vmx_set_gdt(vcpu, dt); } -static void vt_set_dr6(struct kvm_vcpu *vcpu, unsigned long val) -{ - if (is_td_vcpu(vcpu)) - return; - - vmx_set_dr6(vcpu, val); -} - static void vt_set_dr7(struct kvm_vcpu *vcpu, unsigned long val) { if (is_td_vcpu(vcpu)) @@ -923,6 +883,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .vcpu_load = vt_op(vcpu_load), .vcpu_put = vt_op(vcpu_put), + .HOST_OWNED_DEBUGCTL = VMX_HOST_OWNED_DEBUGCTL_BITS, + .update_exception_bitmap = vt_op(update_exception_bitmap), .get_feature_msr = vmx_get_feature_msr, .get_msr = vt_op(get_msr), @@ -943,7 +905,6 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .set_idt = vt_op(set_idt), .get_gdt = vt_op(get_gdt), .set_gdt = vt_op(set_gdt), - .set_dr6 = vt_op(set_dr6), .set_dr7 = vt_op(set_dr7), .sync_dirty_debug_regs = vt_op(sync_dirty_debug_regs), .cache_reg = vt_op(cache_reg), @@ -1014,7 +975,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .nested_ops = &vmx_nested_ops, .pi_update_irte = vmx_pi_update_irte, - .pi_start_assignment = vmx_pi_start_assignment, + .pi_start_bypass = vmx_pi_start_bypass, #ifdef CONFIG_X86_64 .set_hv_timer = vt_op(set_hv_timer), @@ -1034,7 +995,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .apic_init_signal_blocked = vt_op(apic_init_signal_blocked), .migrate_timers = vmx_migrate_timers, - .msr_filter_changed = vt_op(msr_filter_changed), + .recalc_msr_intercepts = vt_op(recalc_msr_intercepts), .complete_emulated_msr = vt_op(complete_emulated_msr), .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 7211c71d4241..b8ea1969113d 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -715,6 +715,12 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, MSR_IA32_FLUSH_CMD, MSR_TYPE_W); + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_IA32_APERF, MSR_TYPE_R); + + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_IA32_MPERF, MSR_TYPE_R); + kvm_vcpu_unmap(vcpu, &map); vmx->nested.force_msr_bitmap_recalc = false; @@ -2663,10 +2669,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, if (vmx->nested.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) { kvm_set_dr(vcpu, 7, vmcs12->guest_dr7); - vmcs_write64(GUEST_IA32_DEBUGCTL, vmcs12->guest_ia32_debugctl); + vmx_guest_debugctl_write(vcpu, vmcs12->guest_ia32_debugctl & + vmx_get_supported_debugctl(vcpu, false)); } else { kvm_set_dr(vcpu, 7, vcpu->arch.dr7); - vmcs_write64(GUEST_IA32_DEBUGCTL, vmx->nested.pre_vmenter_debugctl); + vmx_guest_debugctl_write(vcpu, vmx->nested.pre_vmenter_debugctl); } if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) @@ -3156,7 +3163,8 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, return -EINVAL; if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS) && - CC(!kvm_dr7_valid(vmcs12->guest_dr7))) + (CC(!kvm_dr7_valid(vmcs12->guest_dr7)) || + CC(!vmx_is_valid_debugctl(vcpu, vmcs12->guest_ia32_debugctl, false)))) return -EINVAL; if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PAT) && @@ -3530,7 +3538,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, if (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) - vmx->nested.pre_vmenter_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL); + vmx->nested.pre_vmenter_debugctl = vmx_guest_debugctl_read(); if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) @@ -4608,6 +4616,12 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) (vmcs12->vm_entry_controls & ~VM_ENTRY_IA32E_MODE) | (vm_entry_controls_get(to_vmx(vcpu)) & VM_ENTRY_IA32E_MODE); + /* + * Note! Save DR7, but intentionally don't grab DEBUGCTL from vmcs02. + * Writes to DEBUGCTL that aren't intercepted by L1 are immediately + * propagated to vmcs12 (see vmx_set_msr()), as the value loaded into + * vmcs02 doesn't strictly track vmcs12. + */ if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_DEBUG_CONTROLS) vmcs12->guest_dr7 = vcpu->arch.dr7; @@ -4798,7 +4812,7 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, __vmx_set_segment(vcpu, &seg, VCPU_SREG_LDTR); kvm_set_dr(vcpu, 7, 0x400); - vmcs_write64(GUEST_IA32_DEBUGCTL, 0); + vmx_guest_debugctl_write(vcpu, 0); if (nested_vmx_load_msr(vcpu, vmcs12->vm_exit_msr_load_addr, vmcs12->vm_exit_msr_load_count)) @@ -4853,6 +4867,9 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu) WARN_ON(kvm_set_dr(vcpu, 7, vmcs_readl(GUEST_DR7))); } + /* Reload DEBUGCTL to ensure vmcs01 has a fresh FREEZE_IN_SMM value. */ + vmx_reload_guest_debugctl(vcpu); + /* * Note that calling vmx_set_{efer,cr0,cr4} is important as they * handle a variety of side effects to KVM's software model. diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index bbf4509f32d0..0b173602821b 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -653,11 +653,11 @@ static void intel_pmu_reset(struct kvm_vcpu *vcpu) */ static void intel_pmu_legacy_freezing_lbrs_on_pmi(struct kvm_vcpu *vcpu) { - u64 data = vmcs_read64(GUEST_IA32_DEBUGCTL); + u64 data = vmx_guest_debugctl_read(); if (data & DEBUGCTLMSR_FREEZE_LBRS_ON_PMI) { data &= ~DEBUGCTLMSR_LBR; - vmcs_write64(GUEST_IA32_DEBUGCTL, data); + vmx_guest_debugctl_write(vcpu, data); } } @@ -730,7 +730,7 @@ void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu) if (!lbr_desc->event) { vmx_disable_lbr_msrs_passthrough(vcpu); - if (vmcs_read64(GUEST_IA32_DEBUGCTL) & DEBUGCTLMSR_LBR) + if (vmx_guest_debugctl_read() & DEBUGCTLMSR_LBR) goto warn; if (test_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use)) goto warn; @@ -752,7 +752,7 @@ void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu) static void intel_pmu_cleanup(struct kvm_vcpu *vcpu) { - if (!(vmcs_read64(GUEST_IA32_DEBUGCTL) & DEBUGCTLMSR_LBR)) + if (!(vmx_guest_debugctl_read() & DEBUGCTLMSR_LBR)) intel_pmu_release_guest_lbr_event(vcpu); } diff --git a/arch/x86/kvm/vmx/posted_intr.c b/arch/x86/kvm/vmx/posted_intr.c index 5c615e5845bf..4a6d9a17da23 100644 --- a/arch/x86/kvm/vmx/posted_intr.c +++ b/arch/x86/kvm/vmx/posted_intr.c @@ -2,6 +2,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include @@ -72,13 +73,10 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu) /* * If the vCPU wasn't on the wakeup list and wasn't migrated, then the * full update can be skipped as neither the vector nor the destination - * needs to be changed. + * needs to be changed. Clear SN even if there is no assigned device, + * again for simplicity. */ if (pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR && vcpu->cpu == cpu) { - /* - * Clear SN if it was set due to being preempted. Again, do - * this even if there is no assigned device for simplicity. - */ if (pi_test_and_clear_sn(pi_desc)) goto after_clear_sn; return; @@ -148,8 +146,13 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu) static bool vmx_can_use_vtd_pi(struct kvm *kvm) { + /* + * Note, reading the number of possible bypass IRQs can race with a + * bypass IRQ being attached to the VM. vmx_pi_start_bypass() ensures + * blockng vCPUs will see an elevated count or get KVM_REQ_UNBLOCK. + */ return irqchip_in_kernel(kvm) && kvm_arch_has_irq_bypass() && - kvm_arch_has_assigned_device(kvm); + READ_ONCE(kvm->arch.nr_possible_bypass_irqs); } /* @@ -224,17 +227,23 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu) if (!vmx_needs_pi_wakeup(vcpu)) return; - if (kvm_vcpu_is_blocking(vcpu) && + /* + * If the vCPU is blocking with IRQs enabled and ISN'T being preempted, + * enable the wakeup handler so that notification IRQ wakes the vCPU as + * expected. There is no need to enable the wakeup handler if the vCPU + * is preempted between setting its wait state and manually scheduling + * out, as the task is still runnable, i.e. doesn't need a wake event + * from KVM to be scheduled in. + * + * If the wakeup handler isn't being enabled, Suppress Notifications as + * the cost of propagating PIR.IRR to PID.ON is negligible compared to + * the cost of a spurious IRQ, and vCPU put/load is a slow path. + */ + if (!vcpu->preempted && kvm_vcpu_is_blocking(vcpu) && ((is_td_vcpu(vcpu) && tdx_interrupt_allowed(vcpu)) || (!is_td_vcpu(vcpu) && !vmx_interrupt_blocked(vcpu)))) pi_enable_wakeup_handler(vcpu); - - /* - * Set SN when the vCPU is preempted. Note, the vCPU can both be seen - * as blocking and preempted, e.g. if it's preempted between setting - * its wait state and manually scheduling out. - */ - if (vcpu->preempted) + else pi_set_sn(pi_desc); } @@ -281,99 +290,30 @@ bool pi_has_pending_interrupt(struct kvm_vcpu *vcpu) /* - * Bail out of the block loop if the VM has an assigned - * device, but the blocking vCPU didn't reconfigure the - * PI.NV to the wakeup vector, i.e. the assigned device - * came along after the initial check in vmx_vcpu_pi_put(). + * Kick all vCPUs when the first possible bypass IRQ is attached to a VM, as + * blocking vCPUs may scheduled out without reconfiguring PID.NV to the wakeup + * vector, i.e. if the bypass IRQ came along after vmx_vcpu_pi_put(). */ -void vmx_pi_start_assignment(struct kvm *kvm) +void vmx_pi_start_bypass(struct kvm *kvm) { - if (!kvm_arch_has_irq_bypass()) + if (WARN_ON_ONCE(!vmx_can_use_vtd_pi(kvm))) return; kvm_make_all_cpus_request(kvm, KVM_REQ_UNBLOCK); } -/* - * vmx_pi_update_irte - set IRTE for Posted-Interrupts - * - * @kvm: kvm - * @host_irq: host irq of the interrupt - * @guest_irq: gsi of the interrupt - * @set: set or unset PI - * returns 0 on success, < 0 on failure - */ -int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set) +int vmx_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm, + unsigned int host_irq, uint32_t guest_irq, + struct kvm_vcpu *vcpu, u32 vector) { - struct kvm_kernel_irq_routing_entry *e; - struct kvm_irq_routing_table *irq_rt; - bool enable_remapped_mode = true; - struct kvm_lapic_irq irq; - struct kvm_vcpu *vcpu; - struct vcpu_data vcpu_info; - int idx, ret = 0; + if (vcpu) { + struct intel_iommu_pi_data pi_data = { + .pi_desc_addr = __pa(vcpu_to_pi_desc(vcpu)), + .vector = vector, + }; - if (!vmx_can_use_vtd_pi(kvm)) - return 0; - - idx = srcu_read_lock(&kvm->irq_srcu); - irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); - if (guest_irq >= irq_rt->nr_rt_entries || - hlist_empty(&irq_rt->map[guest_irq])) { - pr_warn_once("no route for guest_irq %u/%u (broken user space?)\n", - guest_irq, irq_rt->nr_rt_entries); - goto out; + return irq_set_vcpu_affinity(host_irq, &pi_data); + } else { + return irq_set_vcpu_affinity(host_irq, NULL); } - - hlist_for_each_entry(e, &irq_rt->map[guest_irq], link) { - if (e->type != KVM_IRQ_ROUTING_MSI) - continue; - /* - * VT-d PI cannot support posting multicast/broadcast - * interrupts to a vCPU, we still use interrupt remapping - * for these kind of interrupts. - * - * For lowest-priority interrupts, we only support - * those with single CPU as the destination, e.g. user - * configures the interrupts via /proc/irq or uses - * irqbalance to make the interrupts single-CPU. - * - * We will support full lowest-priority interrupt later. - * - * In addition, we can only inject generic interrupts using - * the PI mechanism, refuse to route others through it. - */ - - kvm_set_msi_irq(kvm, e, &irq); - if (!kvm_intr_is_single_vcpu(kvm, &irq, &vcpu) || - !kvm_irq_is_postable(&irq)) - continue; - - vcpu_info.pi_desc_addr = __pa(vcpu_to_pi_desc(vcpu)); - vcpu_info.vector = irq.vector; - - trace_kvm_pi_irte_update(host_irq, vcpu->vcpu_id, e->gsi, - vcpu_info.vector, vcpu_info.pi_desc_addr, set); - - if (!set) - continue; - - enable_remapped_mode = false; - - ret = irq_set_vcpu_affinity(host_irq, &vcpu_info); - if (ret < 0) { - printk(KERN_INFO "%s: failed to update PI IRTE\n", - __func__); - goto out; - } - } - - if (enable_remapped_mode) - ret = irq_set_vcpu_affinity(host_irq, NULL); - - ret = 0; -out: - srcu_read_unlock(&kvm->irq_srcu, idx); - return ret; } diff --git a/arch/x86/kvm/vmx/posted_intr.h b/arch/x86/kvm/vmx/posted_intr.h index 80499ea0e674..a4af39948cf0 100644 --- a/arch/x86/kvm/vmx/posted_intr.h +++ b/arch/x86/kvm/vmx/posted_intr.h @@ -3,6 +3,9 @@ #define __KVM_X86_VMX_POSTED_INTR_H #include +#include +#include + #include void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu); @@ -11,9 +14,10 @@ void pi_wakeup_handler(void); void __init pi_init_cpu(int cpu); void pi_apicv_pre_state_restore(struct kvm_vcpu *vcpu); bool pi_has_pending_interrupt(struct kvm_vcpu *vcpu); -int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set); -void vmx_pi_start_assignment(struct kvm *kvm); +int vmx_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm, + unsigned int host_irq, uint32_t guest_irq, + struct kvm_vcpu *vcpu, u32 vector); +void vmx_pi_start_bypass(struct kvm *kvm); static inline int pi_find_highest_vector(struct pi_desc *pi_desc) { diff --git a/arch/x86/kvm/vmx/run_flags.h b/arch/x86/kvm/vmx/run_flags.h index 6a9bfdfbb6e5..2f20fb170def 100644 --- a/arch/x86/kvm/vmx/run_flags.h +++ b/arch/x86/kvm/vmx/run_flags.h @@ -2,10 +2,12 @@ #ifndef __KVM_X86_VMX_RUN_FLAGS_H #define __KVM_X86_VMX_RUN_FLAGS_H -#define VMX_RUN_VMRESUME_SHIFT 0 -#define VMX_RUN_SAVE_SPEC_CTRL_SHIFT 1 +#define VMX_RUN_VMRESUME_SHIFT 0 +#define VMX_RUN_SAVE_SPEC_CTRL_SHIFT 1 +#define VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO_SHIFT 2 -#define VMX_RUN_VMRESUME BIT(VMX_RUN_VMRESUME_SHIFT) -#define VMX_RUN_SAVE_SPEC_CTRL BIT(VMX_RUN_SAVE_SPEC_CTRL_SHIFT) +#define VMX_RUN_VMRESUME BIT(VMX_RUN_VMRESUME_SHIFT) +#define VMX_RUN_SAVE_SPEC_CTRL BIT(VMX_RUN_SAVE_SPEC_CTRL_SHIFT) +#define VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO_SHIFT) #endif /* __KVM_X86_VMX_RUN_FLAGS_H */ diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b952bc673271..66744f5768c8 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -173,6 +173,8 @@ static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char i tdx_clear_unsupported_cpuid(entry); } +#define TDVMCALLINFO_SETUP_EVENT_NOTIFY_INTERRUPT BIT(1) + static int init_kvm_tdx_caps(const struct tdx_sys_info_td_conf *td_conf, struct kvm_tdx_capabilities *caps) { @@ -188,6 +190,9 @@ static int init_kvm_tdx_caps(const struct tdx_sys_info_td_conf *td_conf, caps->cpuid.nent = td_conf->num_cpuid_config; + caps->user_tdvmcallinfo_1_r11 = + TDVMCALLINFO_SETUP_EVENT_NOTIFY_INTERRUPT; + for (i = 0; i < td_conf->num_cpuid_config; i++) td_init_cpuid_entry2(&caps->cpuid.entries[i], i); @@ -738,7 +743,7 @@ bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu) !to_tdx(vcpu)->vp_enter_args.r12; } -bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu) +static bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu) { u64 vcpu_state_details; @@ -778,8 +783,6 @@ void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) else vt->msr_host_kernel_gs_base = read_msr(MSR_KERNEL_GS_BASE); - vt->host_debugctlmsr = get_debugctlmsr(); - vt->guest_state_loaded = true; } @@ -1020,20 +1023,20 @@ static void tdx_load_host_xsave_state(struct kvm_vcpu *vcpu) DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI | \ DEBUGCTLMSR_FREEZE_IN_SMM) -fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) +fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { struct vcpu_tdx *tdx = to_tdx(vcpu); struct vcpu_vt *vt = to_vt(vcpu); /* - * force_immediate_exit requires vCPU entering for events injection with - * an immediately exit followed. But The TDX module doesn't guarantee - * entry, it's already possible for KVM to _think_ it completely entry - * to the guest without actually having done so. - * Since KVM never needs to force an immediate exit for TDX, and can't - * do direct injection, just warn on force_immediate_exit. + * WARN if KVM wants to force an immediate exit, as the TDX module does + * not guarantee entry into the guest, i.e. it's possible for KVM to + * _think_ it completed entry to the guest and forced an immediate exit + * without actually having done so. Luckily, KVM never needs to force + * an immediate exit for TDX (KVM can't do direct event injection, so + * just WARN and continue on. */ - WARN_ON_ONCE(force_immediate_exit); + WARN_ON_ONCE(run_flags); /* * Wait until retry of SEPT-zap-related SEAMCALL completes before @@ -1043,7 +1046,7 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) if (unlikely(READ_ONCE(to_kvm_tdx(vcpu->kvm)->wait_for_sept_zap))) return EXIT_FASTPATH_EXIT_HANDLED; - trace_kvm_entry(vcpu, force_immediate_exit); + trace_kvm_entry(vcpu, run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT); if (pi_test_on(&vt->pi_desc)) { apic->send_IPI_self(POSTED_INTR_VECTOR); @@ -1055,8 +1058,8 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) tdx_vcpu_enter_exit(vcpu); - if (vt->host_debugctlmsr & ~TDX_DEBUGCTL_PRESERVED) - update_debugctlmsr(vt->host_debugctlmsr); + if (vcpu->arch.host_debugctl & ~TDX_DEBUGCTL_PRESERVED) + update_debugctlmsr(vcpu->arch.host_debugctl); tdx_load_host_xsave_state(vcpu); tdx->guest_entered = true; @@ -1212,11 +1215,13 @@ static int tdx_map_gpa(struct kvm_vcpu *vcpu) /* * Converting TDVMCALL_MAP_GPA to KVM_HC_MAP_GPA_RANGE requires * userspace to enable KVM_CAP_EXIT_HYPERCALL with KVM_HC_MAP_GPA_RANGE - * bit set. If not, the error code is not defined in GHCI for TDX, use - * TDVMCALL_STATUS_INVALID_OPERAND for this case. + * bit set. This is a base call so it should always be supported, but + * KVM has no way to ensure that userspace implements the GHCI correctly. + * So if KVM_HC_MAP_GPA_RANGE does not cause a VMEXIT, return an error + * to the guest. */ if (!user_exit_on_hypercall(vcpu->kvm, KVM_HC_MAP_GPA_RANGE)) { - ret = TDVMCALL_STATUS_INVALID_OPERAND; + ret = TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED; goto error; } @@ -1449,20 +1454,106 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu) return 1; } +static int tdx_complete_get_td_vm_call_info(struct kvm_vcpu *vcpu) +{ + struct vcpu_tdx *tdx = to_tdx(vcpu); + + tdvmcall_set_return_code(vcpu, vcpu->run->tdx.get_tdvmcall_info.ret); + + /* + * For now, there is no TDVMCALL beyond GHCI base API supported by KVM + * directly without the support from userspace, just set the value + * returned from userspace. + */ + tdx->vp_enter_args.r11 = vcpu->run->tdx.get_tdvmcall_info.r11; + tdx->vp_enter_args.r12 = vcpu->run->tdx.get_tdvmcall_info.r12; + tdx->vp_enter_args.r13 = vcpu->run->tdx.get_tdvmcall_info.r13; + tdx->vp_enter_args.r14 = vcpu->run->tdx.get_tdvmcall_info.r14; + + return 1; +} + static int tdx_get_td_vm_call_info(struct kvm_vcpu *vcpu) { struct vcpu_tdx *tdx = to_tdx(vcpu); - if (tdx->vp_enter_args.r12) - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); - else { + switch (tdx->vp_enter_args.r12) { + case 0: tdx->vp_enter_args.r11 = 0; + tdx->vp_enter_args.r12 = 0; tdx->vp_enter_args.r13 = 0; tdx->vp_enter_args.r14 = 0; + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_SUCCESS); + return 1; + case 1: + vcpu->run->tdx.get_tdvmcall_info.leaf = tdx->vp_enter_args.r12; + vcpu->run->exit_reason = KVM_EXIT_TDX; + vcpu->run->tdx.flags = 0; + vcpu->run->tdx.nr = TDVMCALL_GET_TD_VM_CALL_INFO; + vcpu->run->tdx.get_tdvmcall_info.ret = TDVMCALL_STATUS_SUCCESS; + vcpu->run->tdx.get_tdvmcall_info.r11 = 0; + vcpu->run->tdx.get_tdvmcall_info.r12 = 0; + vcpu->run->tdx.get_tdvmcall_info.r13 = 0; + vcpu->run->tdx.get_tdvmcall_info.r14 = 0; + vcpu->arch.complete_userspace_io = tdx_complete_get_td_vm_call_info; + return 0; + default: + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); + return 1; } +} + +static int tdx_complete_simple(struct kvm_vcpu *vcpu) +{ + tdvmcall_set_return_code(vcpu, vcpu->run->tdx.unknown.ret); return 1; } +static int tdx_get_quote(struct kvm_vcpu *vcpu) +{ + struct vcpu_tdx *tdx = to_tdx(vcpu); + u64 gpa = tdx->vp_enter_args.r12; + u64 size = tdx->vp_enter_args.r13; + + /* The gpa of buffer must have shared bit set. */ + if (vt_is_tdx_private_gpa(vcpu->kvm, gpa)) { + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); + return 1; + } + + vcpu->run->exit_reason = KVM_EXIT_TDX; + vcpu->run->tdx.flags = 0; + vcpu->run->tdx.nr = TDVMCALL_GET_QUOTE; + vcpu->run->tdx.get_quote.ret = TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED; + vcpu->run->tdx.get_quote.gpa = gpa & ~gfn_to_gpa(kvm_gfn_direct_bits(tdx->vcpu.kvm)); + vcpu->run->tdx.get_quote.size = size; + + vcpu->arch.complete_userspace_io = tdx_complete_simple; + + return 0; +} + +static int tdx_setup_event_notify_interrupt(struct kvm_vcpu *vcpu) +{ + struct vcpu_tdx *tdx = to_tdx(vcpu); + u64 vector = tdx->vp_enter_args.r12; + + if (vector < 32 || vector > 255) { + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); + return 1; + } + + vcpu->run->exit_reason = KVM_EXIT_TDX; + vcpu->run->tdx.flags = 0; + vcpu->run->tdx.nr = TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT; + vcpu->run->tdx.setup_event_notify.ret = TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED; + vcpu->run->tdx.setup_event_notify.vector = vector; + + vcpu->arch.complete_userspace_io = tdx_complete_simple; + + return 0; +} + static int handle_tdvmcall(struct kvm_vcpu *vcpu) { switch (tdvmcall_leaf(vcpu)) { @@ -1472,11 +1563,15 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu) return tdx_report_fatal_error(vcpu); case TDVMCALL_GET_TD_VM_CALL_INFO: return tdx_get_td_vm_call_info(vcpu); + case TDVMCALL_GET_QUOTE: + return tdx_get_quote(vcpu); + case TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: + return tdx_setup_event_notify_interrupt(vcpu); default: break; } - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED); return 1; } @@ -1543,8 +1638,8 @@ static int tdx_mem_page_record_premap_cnt(struct kvm *kvm, gfn_t gfn, return 0; } -int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, kvm_pfn_t pfn) +static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, + enum pg_level level, kvm_pfn_t pfn) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); struct page *page = pfn_to_page(pfn); @@ -1624,8 +1719,8 @@ static int tdx_sept_drop_private_spte(struct kvm *kvm, gfn_t gfn, return 0; } -int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) +static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, + enum pg_level level, void *private_spt) { int tdx_level = pg_level_to_tdx_sept_level(level); gpa_t gpa = gfn_to_gpa(gfn); @@ -1760,8 +1855,8 @@ static void tdx_track(struct kvm *kvm) kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE); } -int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) +static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, + enum pg_level level, void *private_spt) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); @@ -1783,8 +1878,8 @@ int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, return tdx_reclaim_page(virt_to_page(private_spt)); } -int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, kvm_pfn_t pfn) +static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, + enum pg_level level, kvm_pfn_t pfn) { struct page *page = pfn_to_page(pfn); int ret; @@ -2172,25 +2267,26 @@ static int tdx_get_capabilities(struct kvm_tdx_cmd *cmd) const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf; struct kvm_tdx_capabilities __user *user_caps; struct kvm_tdx_capabilities *caps = NULL; + u32 nr_user_entries; int ret = 0; /* flags is reserved for future use */ if (cmd->flags) return -EINVAL; - caps = kmalloc(sizeof(*caps) + + caps = kzalloc(sizeof(*caps) + sizeof(struct kvm_cpuid_entry2) * td_conf->num_cpuid_config, GFP_KERNEL); if (!caps) return -ENOMEM; user_caps = u64_to_user_ptr(cmd->data); - if (copy_from_user(caps, user_caps, sizeof(*caps))) { + if (get_user(nr_user_entries, &user_caps->cpuid.nent)) { ret = -EFAULT; goto out; } - if (caps->cpuid.nent < td_conf->num_cpuid_config) { + if (nr_user_entries < td_conf->num_cpuid_config) { ret = -E2BIG; goto out; } @@ -3507,10 +3603,14 @@ int __init tdx_bringup(void) r = __tdx_bringup(); if (r) { /* - * Disable TDX only but don't fail to load module if - * the TDX module could not be loaded. No need to print - * message saying "module is not loaded" because it was - * printed when the first SEAMCALL failed. + * Disable TDX only but don't fail to load module if the TDX + * module could not be loaded. No need to print message saying + * "module is not loaded" because it was printed when the first + * SEAMCALL failed. Don't bother unwinding the S-EPT hooks or + * vm_size, as kvm_x86_ops have already been finalized (and are + * intentionally not exported). The S-EPT code is unreachable, + * and allocating a few more bytes per VM in a should-be-rare + * failure scenario is a non-issue. */ if (r == -ENODEV) goto success_disable_tdx; @@ -3524,3 +3624,20 @@ int __init tdx_bringup(void) 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; + vt_x86_ops.set_external_spte = tdx_sept_set_private_spte; + 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; +} diff --git a/arch/x86/kvm/vmx/tdx.h b/arch/x86/kvm/vmx/tdx.h index 51f98443e8a2..ca39a9391db1 100644 --- a/arch/x86/kvm/vmx/tdx.h +++ b/arch/x86/kvm/vmx/tdx.h @@ -8,6 +8,7 @@ #ifdef CONFIG_KVM_INTEL_TDX #include "common.h" +void tdx_hardware_setup(void); int tdx_bringup(void); void tdx_cleanup(void); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 4953846cb30d..aa157fe5b7b3 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -75,6 +75,8 @@ #include "vmx_onhyperv.h" #include "posted_intr.h" +#include "mmu/spte.h" + MODULE_AUTHOR("Qumranet"); MODULE_DESCRIPTION("KVM support for VMX (Intel VT-x) extensions"); MODULE_LICENSE("GPL"); @@ -113,8 +115,6 @@ static bool __read_mostly fasteoi = 1; module_param(fasteoi, bool, 0444); module_param(enable_apicv, bool, 0444); - -bool __read_mostly enable_ipiv = true; module_param(enable_ipiv, bool, 0444); module_param(enable_device_posted_irqs, bool, 0444); @@ -167,31 +167,6 @@ module_param(allow_smaller_maxphyaddr, bool, S_IRUGO); RTIT_STATUS_ERROR | RTIT_STATUS_STOPPED | \ RTIT_STATUS_BYTECNT)) -/* - * List of MSRs that can be directly passed to the guest. - * In addition to these x2apic, PT and LBR MSRs are handled specially. - */ -static u32 vmx_possible_passthrough_msrs[MAX_POSSIBLE_PASSTHROUGH_MSRS] = { - MSR_IA32_SPEC_CTRL, - MSR_IA32_PRED_CMD, - MSR_IA32_FLUSH_CMD, - MSR_IA32_TSC, -#ifdef CONFIG_X86_64 - MSR_FS_BASE, - MSR_GS_BASE, - MSR_KERNEL_GS_BASE, - MSR_IA32_XFD, - MSR_IA32_XFD_ERR, -#endif - MSR_IA32_SYSENTER_CS, - MSR_IA32_SYSENTER_ESP, - MSR_IA32_SYSENTER_EIP, - MSR_CORE_C1_RES, - MSR_CORE_C3_RESIDENCY, - MSR_CORE_C6_RESIDENCY, - MSR_CORE_C7_RESIDENCY, -}; - /* * These 2 parameters are used to config the controls for Pause-Loop Exiting: * ple_gap: upper bound on the amount of time between two successive @@ -674,40 +649,6 @@ static inline bool cpu_need_virtualize_apic_accesses(struct kvm_vcpu *vcpu) return flexpriority_enabled && lapic_in_kernel(vcpu); } -static int vmx_get_passthrough_msr_slot(u32 msr) -{ - int i; - - switch (msr) { - case 0x800 ... 0x8ff: - /* x2APIC MSRs. These are handled in vmx_update_msr_bitmap_x2apic() */ - return -ENOENT; - case MSR_IA32_RTIT_STATUS: - case MSR_IA32_RTIT_OUTPUT_BASE: - case MSR_IA32_RTIT_OUTPUT_MASK: - case MSR_IA32_RTIT_CR3_MATCH: - case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: - /* PT MSRs. These are handled in pt_update_intercept_for_msr() */ - case MSR_LBR_SELECT: - case MSR_LBR_TOS: - case MSR_LBR_INFO_0 ... MSR_LBR_INFO_0 + 31: - case MSR_LBR_NHM_FROM ... MSR_LBR_NHM_FROM + 31: - case MSR_LBR_NHM_TO ... MSR_LBR_NHM_TO + 31: - case MSR_LBR_CORE_FROM ... MSR_LBR_CORE_FROM + 8: - case MSR_LBR_CORE_TO ... MSR_LBR_CORE_TO + 8: - /* LBR MSRs. These are handled in vmx_update_intercept_for_lbr_msrs() */ - return -ENOENT; - } - - for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) { - if (vmx_possible_passthrough_msrs[i] == msr) - return i; - } - - WARN(1, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr); - return -ENOENT; -} - struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr) { int i; @@ -963,6 +904,10 @@ unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx) if (!msr_write_intercepted(vmx, MSR_IA32_SPEC_CTRL)) flags |= VMX_RUN_SAVE_SPEC_CTRL; + if (static_branch_unlikely(&cpu_buf_vm_clear) && + kvm_vcpu_can_access_host_mmio(&vmx->vcpu)) + flags |= VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO; + return flags; } @@ -2149,7 +2094,7 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; break; case MSR_IA32_DEBUGCTLMSR: - msr_info->data = vmcs_read64(GUEST_IA32_DEBUGCTL); + msr_info->data = vmx_guest_debugctl_read(); break; default: find_uret_msr: @@ -2174,7 +2119,7 @@ static u64 nested_vmx_truncate_sysenter_addr(struct kvm_vcpu *vcpu, return (unsigned long)data; } -static u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated) +u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated) { u64 debugctl = 0; @@ -2186,9 +2131,25 @@ static u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated (host_initiated || intel_pmu_lbr_is_enabled(vcpu))) debugctl |= DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI; + if (boot_cpu_has(X86_FEATURE_RTM) && + (host_initiated || guest_cpu_cap_has(vcpu, X86_FEATURE_RTM))) + debugctl |= DEBUGCTLMSR_RTM_DEBUG; + return debugctl; } +bool vmx_is_valid_debugctl(struct kvm_vcpu *vcpu, u64 data, bool host_initiated) +{ + u64 invalid; + + invalid = data & ~vmx_get_supported_debugctl(vcpu, host_initiated); + if (invalid & (DEBUGCTLMSR_BTF | DEBUGCTLMSR_LBR)) { + kvm_pr_unimpl_wrmsr(vcpu, MSR_IA32_DEBUGCTLMSR, data); + invalid &= ~(DEBUGCTLMSR_BTF | DEBUGCTLMSR_LBR); + } + return !invalid; +} + /* * Writes msr value into the appropriate "register". * Returns 0 on success, non-0 otherwise. @@ -2257,29 +2218,22 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } vmcs_writel(GUEST_SYSENTER_ESP, data); break; - case MSR_IA32_DEBUGCTLMSR: { - u64 invalid; - - invalid = data & ~vmx_get_supported_debugctl(vcpu, msr_info->host_initiated); - if (invalid & (DEBUGCTLMSR_BTF|DEBUGCTLMSR_LBR)) { - kvm_pr_unimpl_wrmsr(vcpu, msr_index, data); - data &= ~(DEBUGCTLMSR_BTF|DEBUGCTLMSR_LBR); - invalid &= ~(DEBUGCTLMSR_BTF|DEBUGCTLMSR_LBR); - } - - if (invalid) + case MSR_IA32_DEBUGCTLMSR: + if (!vmx_is_valid_debugctl(vcpu, data, msr_info->host_initiated)) return 1; + data &= vmx_get_supported_debugctl(vcpu, msr_info->host_initiated); + if (is_guest_mode(vcpu) && get_vmcs12(vcpu)->vm_exit_controls & VM_EXIT_SAVE_DEBUG_CONTROLS) get_vmcs12(vcpu)->guest_ia32_debugctl = data; - vmcs_write64(GUEST_IA32_DEBUGCTL, data); + vmx_guest_debugctl_write(vcpu, data); + if (intel_pmu_lbr_is_enabled(vcpu) && !to_vmx(vcpu)->lbr_desc.event && (data & DEBUGCTLMSR_LBR)) intel_pmu_create_guest_lbr_event(vcpu); return 0; - } case MSR_IA32_BNDCFGS: if (!kvm_mpx_supported() || (!msr_info->host_initiated && @@ -4013,76 +3967,29 @@ static void vmx_msr_bitmap_l01_changed(struct vcpu_vmx *vmx) vmx->nested.force_msr_bitmap_recalc = true; } -void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type) +void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set) { struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap; - int idx; if (!cpu_has_vmx_msr_bitmap()) return; vmx_msr_bitmap_l01_changed(vmx); - /* - * Mark the desired intercept state in shadow bitmap, this is needed - * for resync when the MSR filters change. - */ - idx = vmx_get_passthrough_msr_slot(msr); - if (idx >= 0) { - if (type & MSR_TYPE_R) - clear_bit(idx, vmx->shadow_msr_intercept.read); - if (type & MSR_TYPE_W) - clear_bit(idx, vmx->shadow_msr_intercept.write); + if (type & MSR_TYPE_R) { + if (!set && kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) + vmx_clear_msr_bitmap_read(msr_bitmap, msr); + else + vmx_set_msr_bitmap_read(msr_bitmap, msr); } - if ((type & MSR_TYPE_R) && - !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) { - vmx_set_msr_bitmap_read(msr_bitmap, msr); - type &= ~MSR_TYPE_R; + if (type & MSR_TYPE_W) { + if (!set && kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) + vmx_clear_msr_bitmap_write(msr_bitmap, msr); + else + vmx_set_msr_bitmap_write(msr_bitmap, msr); } - - if ((type & MSR_TYPE_W) && - !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) { - vmx_set_msr_bitmap_write(msr_bitmap, msr); - type &= ~MSR_TYPE_W; - } - - if (type & MSR_TYPE_R) - vmx_clear_msr_bitmap_read(msr_bitmap, msr); - - if (type & MSR_TYPE_W) - vmx_clear_msr_bitmap_write(msr_bitmap, msr); -} - -void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap; - int idx; - - if (!cpu_has_vmx_msr_bitmap()) - return; - - vmx_msr_bitmap_l01_changed(vmx); - - /* - * Mark the desired intercept state in shadow bitmap, this is needed - * for resync when the MSR filter changes. - */ - idx = vmx_get_passthrough_msr_slot(msr); - if (idx >= 0) { - if (type & MSR_TYPE_R) - set_bit(idx, vmx->shadow_msr_intercept.read); - if (type & MSR_TYPE_W) - set_bit(idx, vmx->shadow_msr_intercept.write); - } - - if (type & MSR_TYPE_R) - vmx_set_msr_bitmap_read(msr_bitmap, msr); - - if (type & MSR_TYPE_W) - vmx_set_msr_bitmap_write(msr_bitmap, msr); } static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) @@ -4161,35 +4068,57 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) } } -void vmx_msr_filter_changed(struct kvm_vcpu *vcpu) +void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) { - struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 i; - if (!cpu_has_vmx_msr_bitmap()) return; - /* - * Redo intercept permissions for MSRs that KVM is passing through to - * the guest. Disabling interception will check the new MSR filter and - * ensure that KVM enables interception if usersepace wants to filter - * the MSR. MSRs that KVM is already intercepting don't need to be - * refreshed since KVM is going to intercept them regardless of what - * userspace wants. - */ - for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) { - u32 msr = vmx_possible_passthrough_msrs[i]; - - if (!test_bit(i, vmx->shadow_msr_intercept.read)) - vmx_disable_intercept_for_msr(vcpu, msr, MSR_TYPE_R); - - if (!test_bit(i, vmx->shadow_msr_intercept.write)) - vmx_disable_intercept_for_msr(vcpu, msr, MSR_TYPE_W); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC, MSR_TYPE_R); +#ifdef CONFIG_X86_64 + vmx_disable_intercept_for_msr(vcpu, MSR_FS_BASE, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_GS_BASE, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_KERNEL_GS_BASE, MSR_TYPE_RW); +#endif + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW); + if (kvm_cstate_in_guest(vcpu->kvm)) { + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C1_RES, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C3_RESIDENCY, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C6_RESIDENCY, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C7_RESIDENCY, MSR_TYPE_R); + } + if (kvm_aperfmperf_in_guest(vcpu->kvm)) { + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_APERF, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_MPERF, MSR_TYPE_R); } /* PT MSRs can be passed through iff PT is exposed to the guest. */ if (vmx_pt_mode_is_host_guest()) pt_update_intercept_for_msr(vcpu); + + if (vcpu->arch.xfd_no_write_intercept) + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_XFD, MSR_TYPE_RW); + + vmx_set_intercept_for_msr(vcpu, MSR_IA32_SPEC_CTRL, MSR_TYPE_RW, + !to_vmx(vcpu)->spec_ctrl); + + if (kvm_cpu_cap_has(X86_FEATURE_XFD)) + vmx_set_intercept_for_msr(vcpu, MSR_IA32_XFD_ERR, MSR_TYPE_R, + !guest_cpu_cap_has(vcpu, X86_FEATURE_XFD)); + + if (cpu_feature_enabled(X86_FEATURE_IBPB)) + vmx_set_intercept_for_msr(vcpu, MSR_IA32_PRED_CMD, MSR_TYPE_W, + !guest_has_pred_cmd_msr(vcpu)); + + if (cpu_feature_enabled(X86_FEATURE_FLUSH_L1D)) + vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, + !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); + + /* + * x2APIC and LBR MSR intercepts are modified on-demand and cannot be + * filtered by userspace. + */ } static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu, @@ -4790,7 +4719,8 @@ static void init_vmcs(struct vcpu_vmx *vmx) vmcs_write32(GUEST_SYSENTER_CS, 0); vmcs_writel(GUEST_SYSENTER_ESP, 0); vmcs_writel(GUEST_SYSENTER_EIP, 0); - vmcs_write64(GUEST_IA32_DEBUGCTL, 0); + + vmx_guest_debugctl_write(&vmx->vcpu, 0); if (cpu_has_vmx_tpr_shadow()) { vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0); @@ -5606,12 +5536,6 @@ void vmx_sync_dirty_debug_regs(struct kvm_vcpu *vcpu) set_debugreg(DR6_RESERVED, 6); } -void vmx_set_dr6(struct kvm_vcpu *vcpu, unsigned long val) -{ - lockdep_assert_irqs_disabled(); - set_debugreg(vcpu->arch.dr6, 6); -} - void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val) { vmcs_writel(GUEST_DR7, val); @@ -7290,8 +7214,8 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, if (static_branch_unlikely(&vmx_l1d_should_flush)) vmx_l1d_flush(vcpu); else if (static_branch_unlikely(&cpu_buf_vm_clear) && - kvm_arch_has_assigned_device(vcpu->kvm)) - mds_clear_cpu_buffers(); + (flags & VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO)) + x86_clear_cpu_buffers(); vmx_disable_fb_clear(vmx); @@ -7323,8 +7247,9 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, guest_state_exit_irqoff(); } -fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) +fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { + bool force_immediate_exit = run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT; struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long cr3, cr4; @@ -7369,6 +7294,12 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit) vmcs_writel(GUEST_RIP, vcpu->arch.regs[VCPU_REGS_RIP]); vcpu->arch.regs_dirty = 0; + if (run_flags & KVM_RUN_LOAD_GUEST_DR6) + set_debugreg(vcpu->arch.dr6, 6); + + if (run_flags & KVM_RUN_LOAD_DEBUGCTL) + vmx_reload_guest_debugctl(vcpu); + /* * Refresh vmcs.HOST_CR3 if necessary. This must be done immediately * prior to VM-Enter, as the kernel may load a new ASID (PCID) any time @@ -7543,26 +7474,6 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu) evmcs->hv_enlightenments_control.msr_bitmap = 1; } - /* The MSR bitmap starts with all ones */ - bitmap_fill(vmx->shadow_msr_intercept.read, MAX_POSSIBLE_PASSTHROUGH_MSRS); - bitmap_fill(vmx->shadow_msr_intercept.write, MAX_POSSIBLE_PASSTHROUGH_MSRS); - - vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC, MSR_TYPE_R); -#ifdef CONFIG_X86_64 - vmx_disable_intercept_for_msr(vcpu, MSR_FS_BASE, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(vcpu, MSR_GS_BASE, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(vcpu, MSR_KERNEL_GS_BASE, MSR_TYPE_RW); -#endif - vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW); - if (kvm_cstate_in_guest(vcpu->kvm)) { - vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C1_RES, MSR_TYPE_R); - vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C3_RESIDENCY, MSR_TYPE_R); - vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C6_RESIDENCY, MSR_TYPE_R); - vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C7_RESIDENCY, MSR_TYPE_R); - } - vmx->loaded_vmcs = &vmx->vmcs01; if (cpu_need_virtualize_apic_accesses(vcpu)) { @@ -7612,7 +7523,7 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu) int vmx_vm_init(struct kvm *kvm) { if (!ple_gap) - kvm->arch.pause_in_guest = true; + kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE); if (boot_cpu_has(X86_BUG_L1TF) && enable_ept) { switch (l1tf_mitigation) { @@ -7849,18 +7760,6 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) } } - if (kvm_cpu_cap_has(X86_FEATURE_XFD)) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_XFD_ERR, MSR_TYPE_R, - !guest_cpu_cap_has(vcpu, X86_FEATURE_XFD)); - - if (boot_cpu_has(X86_FEATURE_IBPB)) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_PRED_CMD, MSR_TYPE_W, - !guest_has_pred_cmd_msr(vcpu)); - - if (boot_cpu_has(X86_FEATURE_FLUSH_L1D)) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, - !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - set_cr4_guest_host_mask(vmx); vmx_write_encls_bitmap(vcpu, NULL); @@ -7876,6 +7775,9 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) vmx->msr_ia32_feature_control_valid_bits &= ~FEAT_CTL_SGX_LC_ENABLED; + /* Recalc MSR interception to account for feature changes. */ + vmx_recalc_msr_intercepts(vcpu); + /* Refresh #PF interception to account for MAXPHYADDR changes. */ vmx_update_exception_bitmap(vcpu); } @@ -8650,6 +8552,8 @@ int __init vmx_init(void) { int r, cpu; + KVM_SANITY_CHECK_VM_STRUCT_SIZE(kvm_vmx); + if (!kvm_is_vmx_supported()) return -EOPNOTSUPP; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index b5758c33c60f..d3389baf3ab3 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -19,8 +19,6 @@ #include "../mmu.h" #include "common.h" -#define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4)) - #ifdef CONFIG_X86_64 #define MAX_NR_USER_RETURN_MSRS 7 #else @@ -296,13 +294,6 @@ struct vcpu_vmx { struct pt_desc pt_desc; struct lbr_desc lbr_desc; - /* Save desired MSR intercept (read: pass-through) state */ -#define MAX_POSSIBLE_PASSTHROUGH_MSRS 16 - struct { - DECLARE_BITMAP(read, MAX_POSSIBLE_PASSTHROUGH_MSRS); - DECLARE_BITMAP(write, MAX_POSSIBLE_PASSTHROUGH_MSRS); - } shadow_msr_intercept; - /* ve_info must be page aligned. */ struct vmx_ve_information *ve_info; }; @@ -395,24 +386,54 @@ bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned long *regs, int vmx_find_loadstore_msr_slot(struct vmx_msrs *m, u32 msr); void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu); -void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type); -void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type); +void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set); + +static inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, + u32 msr, int type) +{ + vmx_set_intercept_for_msr(vcpu, msr, type, false); +} + +static inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, + u32 msr, int type) +{ + vmx_set_intercept_for_msr(vcpu, msr, type, true); +} u64 vmx_get_l2_tsc_offset(struct kvm_vcpu *vcpu); u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu); gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags); -static inline void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, - int type, bool value) +void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu); + +u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated); +bool vmx_is_valid_debugctl(struct kvm_vcpu *vcpu, u64 data, bool host_initiated); + +#define VMX_HOST_OWNED_DEBUGCTL_BITS (DEBUGCTLMSR_FREEZE_IN_SMM) + +static inline void vmx_guest_debugctl_write(struct kvm_vcpu *vcpu, u64 val) { - if (value) - vmx_enable_intercept_for_msr(vcpu, msr, type); - else - vmx_disable_intercept_for_msr(vcpu, msr, type); + WARN_ON_ONCE(val & VMX_HOST_OWNED_DEBUGCTL_BITS); + + val |= vcpu->arch.host_debugctl & VMX_HOST_OWNED_DEBUGCTL_BITS; + vmcs_write64(GUEST_IA32_DEBUGCTL, val); } -void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu); +static inline u64 vmx_guest_debugctl_read(void) +{ + return vmcs_read64(GUEST_IA32_DEBUGCTL) & ~VMX_HOST_OWNED_DEBUGCTL_BITS; +} + +static inline void vmx_reload_guest_debugctl(struct kvm_vcpu *vcpu) +{ + u64 val = vmcs_read64(GUEST_IA32_DEBUGCTL); + + if (!((val ^ vcpu->arch.host_debugctl) & VMX_HOST_OWNED_DEBUGCTL_BITS)) + return; + + vmx_guest_debugctl_write(vcpu, val & ~VMX_HOST_OWNED_DEBUGCTL_BITS); +} /* * Note, early Intel manuals have the write-low and read-high bitmap offsets diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h index b4596f651232..2b3424f638db 100644 --- a/arch/x86/kvm/vmx/x86_ops.h +++ b/arch/x86/kvm/vmx/x86_ops.h @@ -21,7 +21,7 @@ void vmx_vm_destroy(struct kvm *kvm); int vmx_vcpu_precreate(struct kvm *kvm); int vmx_vcpu_create(struct kvm_vcpu *vcpu); int vmx_vcpu_pre_run(struct kvm_vcpu *vcpu); -fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit); +fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags); void vmx_vcpu_free(struct kvm_vcpu *vcpu); void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event); void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu); @@ -52,7 +52,7 @@ void vmx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, int trig_mode, int vector); void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu); bool vmx_has_emulated_msr(struct kvm *kvm, u32 index); -void vmx_msr_filter_changed(struct kvm_vcpu *vcpu); +void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu); void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu); int vmx_get_feature_msr(u32 msr, u64 *data); @@ -133,10 +133,9 @@ void tdx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event); void tdx_vcpu_free(struct kvm_vcpu *vcpu); void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu); int tdx_vcpu_pre_run(struct kvm_vcpu *vcpu); -fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit); +fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags); void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); void tdx_vcpu_put(struct kvm_vcpu *vcpu); -bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu); int tdx_handle_exit(struct kvm_vcpu *vcpu, enum exit_fastpath_completion fastpath); @@ -151,15 +150,6 @@ int tdx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr); int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp); -int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt); -int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt); -int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, kvm_pfn_t pfn); -int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, kvm_pfn_t pfn); - void tdx_flush_tlb_current(struct kvm_vcpu *vcpu); void tdx_flush_tlb_all(struct kvm_vcpu *vcpu); void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b58a74c1722d..706b6fd56d3c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -226,6 +226,9 @@ EXPORT_SYMBOL_GPL(allow_smaller_maxphyaddr); bool __read_mostly enable_apicv = true; EXPORT_SYMBOL_GPL(enable_apicv); +bool __read_mostly enable_ipiv = true; +EXPORT_SYMBOL_GPL(enable_ipiv); + bool __read_mostly enable_device_posted_irqs = true; EXPORT_SYMBOL_GPL(enable_device_posted_irqs); @@ -3258,9 +3261,11 @@ int kvm_guest_time_update(struct kvm_vcpu *v) /* With all the info we got, fill in the values */ - if (kvm_caps.has_tsc_control) + if (kvm_caps.has_tsc_control) { tgt_tsc_khz = kvm_scale_tsc(tgt_tsc_khz, v->arch.l1_tsc_scaling_ratio); + tgt_tsc_khz = tgt_tsc_khz ? : 1; + } if (unlikely(vcpu->hw_tsc_khz != tgt_tsc_khz)) { kvm_get_time_scale(NSEC_PER_SEC, tgt_tsc_khz * 1000LL, @@ -4577,6 +4582,9 @@ static u64 kvm_get_allowed_disable_exits(void) { u64 r = KVM_X86_DISABLE_EXITS_PAUSE; + if (boot_cpu_has(X86_FEATURE_APERFMPERF)) + r |= KVM_X86_DISABLE_EXITS_APERFMPERF; + if (!mitigate_smt_rsb) { r |= KVM_X86_DISABLE_EXITS_HLT | KVM_X86_DISABLE_EXITS_CSTATE; @@ -4632,17 +4640,20 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_EXT_CPUID: case KVM_CAP_EXT_EMUL_CPUID: case KVM_CAP_CLOCKSOURCE: +#ifdef CONFIG_KVM_IOAPIC case KVM_CAP_PIT: + case KVM_CAP_PIT2: + case KVM_CAP_PIT_STATE2: + case KVM_CAP_REINJECT_CONTROL: +#endif case KVM_CAP_NOP_IO_DELAY: case KVM_CAP_MP_STATE: case KVM_CAP_SYNC_MMU: case KVM_CAP_USER_NMI: - case KVM_CAP_REINJECT_CONTROL: case KVM_CAP_IRQ_INJECT_STATUS: case KVM_CAP_IOEVENTFD: case KVM_CAP_IOEVENTFD_NO_LENGTH: - case KVM_CAP_PIT2: - case KVM_CAP_PIT_STATE2: + case KVM_CAP_SET_IDENTITY_MAP_ADDR: case KVM_CAP_VCPU_EVENTS: #ifdef CONFIG_KVM_HYPERV @@ -4983,11 +4994,6 @@ long kvm_arch_dev_ioctl(struct file *filp, return r; } -static void wbinvd_ipi(void *garbage) -{ - wbinvd(); -} - static bool need_emulate_wbinvd(struct kvm_vcpu *vcpu) { return kvm_arch_has_noncoherent_dma(vcpu->kvm); @@ -5011,8 +5017,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (kvm_x86_call(has_wbinvd_exit)()) cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask); else if (vcpu->cpu != -1 && vcpu->cpu != cpu) - smp_call_function_single(vcpu->cpu, - wbinvd_ipi, NULL, 1); + wbinvd_on_cpu(vcpu->cpu); } kvm_x86_call(vcpu_load)(vcpu, cpu); @@ -5487,12 +5492,6 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) return -EINVAL; - /* INITs are latched while in SMM */ - if (events->flags & KVM_VCPUEVENT_VALID_SMM && - (events->smi.smm || events->smi.pending) && - vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) - return -EINVAL; - process_nmi(vcpu); /* @@ -6186,6 +6185,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp, u32 user_tsc_khz; r = -EINVAL; + + if (vcpu->arch.guest_tsc_protected) + goto out; + user_tsc_khz = (u32)arg; if (kvm_caps.has_tsc_control && @@ -6395,135 +6398,6 @@ static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm, return 0; } -static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) -{ - struct kvm_pic *pic = kvm->arch.vpic; - int r; - - r = 0; - switch (chip->chip_id) { - case KVM_IRQCHIP_PIC_MASTER: - memcpy(&chip->chip.pic, &pic->pics[0], - sizeof(struct kvm_pic_state)); - break; - case KVM_IRQCHIP_PIC_SLAVE: - memcpy(&chip->chip.pic, &pic->pics[1], - sizeof(struct kvm_pic_state)); - break; - case KVM_IRQCHIP_IOAPIC: - kvm_get_ioapic(kvm, &chip->chip.ioapic); - break; - default: - r = -EINVAL; - break; - } - return r; -} - -static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) -{ - struct kvm_pic *pic = kvm->arch.vpic; - int r; - - r = 0; - switch (chip->chip_id) { - case KVM_IRQCHIP_PIC_MASTER: - spin_lock(&pic->lock); - memcpy(&pic->pics[0], &chip->chip.pic, - sizeof(struct kvm_pic_state)); - spin_unlock(&pic->lock); - break; - case KVM_IRQCHIP_PIC_SLAVE: - spin_lock(&pic->lock); - memcpy(&pic->pics[1], &chip->chip.pic, - sizeof(struct kvm_pic_state)); - spin_unlock(&pic->lock); - break; - case KVM_IRQCHIP_IOAPIC: - kvm_set_ioapic(kvm, &chip->chip.ioapic); - break; - default: - r = -EINVAL; - break; - } - kvm_pic_update_irq(pic); - return r; -} - -static int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps) -{ - struct kvm_kpit_state *kps = &kvm->arch.vpit->pit_state; - - BUILD_BUG_ON(sizeof(*ps) != sizeof(kps->channels)); - - mutex_lock(&kps->lock); - memcpy(ps, &kps->channels, sizeof(*ps)); - mutex_unlock(&kps->lock); - return 0; -} - -static int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps) -{ - int i; - struct kvm_pit *pit = kvm->arch.vpit; - - mutex_lock(&pit->pit_state.lock); - memcpy(&pit->pit_state.channels, ps, sizeof(*ps)); - for (i = 0; i < 3; i++) - kvm_pit_load_count(pit, i, ps->channels[i].count, 0); - mutex_unlock(&pit->pit_state.lock); - return 0; -} - -static int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) -{ - mutex_lock(&kvm->arch.vpit->pit_state.lock); - memcpy(ps->channels, &kvm->arch.vpit->pit_state.channels, - sizeof(ps->channels)); - ps->flags = kvm->arch.vpit->pit_state.flags; - mutex_unlock(&kvm->arch.vpit->pit_state.lock); - memset(&ps->reserved, 0, sizeof(ps->reserved)); - return 0; -} - -static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) -{ - int start = 0; - int i; - u32 prev_legacy, cur_legacy; - struct kvm_pit *pit = kvm->arch.vpit; - - mutex_lock(&pit->pit_state.lock); - prev_legacy = pit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY; - cur_legacy = ps->flags & KVM_PIT_FLAGS_HPET_LEGACY; - if (!prev_legacy && cur_legacy) - start = 1; - memcpy(&pit->pit_state.channels, &ps->channels, - sizeof(pit->pit_state.channels)); - pit->pit_state.flags = ps->flags; - for (i = 0; i < 3; i++) - kvm_pit_load_count(pit, i, pit->pit_state.channels[i].count, - start && i == 0); - mutex_unlock(&pit->pit_state.lock); - return 0; -} - -static int kvm_vm_ioctl_reinject(struct kvm *kvm, - struct kvm_reinject_control *control) -{ - struct kvm_pit *pit = kvm->arch.vpit; - - /* pit->pit_state.lock was overloaded to prevent userspace from getting - * an inconsistent state after running multiple KVM_REINJECT_CONTROL - * ioctls in parallel. Use a separate lock if that ioctl isn't rare. - */ - mutex_lock(&pit->pit_state.lock); - kvm_pit_set_reinject(pit, control->pit_reinject); - mutex_unlock(&pit->pit_state.lock); - - return 0; -} - void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) { @@ -6543,18 +6417,6 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) kvm_vcpu_kick(vcpu); } -int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event, - bool line_status) -{ - if (!irqchip_in_kernel(kvm)) - return -ENXIO; - - irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, - irq_event->irq, irq_event->level, - line_status); - return 0; -} - int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) { @@ -6619,17 +6481,11 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, if (!mitigate_smt_rsb && boot_cpu_has_bug(X86_BUG_SMT_RSB) && cpu_smt_possible() && - (cap->args[0] & ~KVM_X86_DISABLE_EXITS_PAUSE)) + (cap->args[0] & ~(KVM_X86_DISABLE_EXITS_PAUSE | + KVM_X86_DISABLE_EXITS_APERFMPERF))) pr_warn_once(SMT_RSB_MSG); - if (cap->args[0] & KVM_X86_DISABLE_EXITS_PAUSE) - kvm->arch.pause_in_guest = true; - if (cap->args[0] & KVM_X86_DISABLE_EXITS_MWAIT) - kvm->arch.mwait_in_guest = true; - if (cap->args[0] & KVM_X86_DISABLE_EXITS_HLT) - kvm->arch.hlt_in_guest = true; - if (cap->args[0] & KVM_X86_DISABLE_EXITS_CSTATE) - kvm->arch.cstate_in_guest = true; + kvm_disable_exits(kvm, cap->args[0]); r = 0; disable_exits_unlock: mutex_unlock(&kvm->lock); @@ -7066,9 +6922,11 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) struct kvm *kvm = filp->private_data; void __user *argp = (void __user *)arg; int r = -ENOTTY; + +#ifdef CONFIG_KVM_IOAPIC /* * This union makes it completely explicit to gcc-3.x - * that these two variables' stack usage should be + * that these three variables' stack usage should be * combined, not added together. */ union { @@ -7076,6 +6934,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) struct kvm_pit_state2 ps2; struct kvm_pit_config pit_config; } u; +#endif switch (ioctl) { case KVM_SET_TSS_ADDR: @@ -7099,6 +6958,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) case KVM_SET_NR_MMU_PAGES: r = kvm_vm_ioctl_set_nr_mmu_pages(kvm, arg); break; +#ifdef CONFIG_KVM_IOAPIC case KVM_CREATE_IRQCHIP: { mutex_lock(&kvm->lock); @@ -7120,7 +6980,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) goto create_irqchip_unlock; } - r = kvm_setup_default_irq_routing(kvm); + r = kvm_setup_default_ioapic_and_pic_routing(kvm); if (r) { kvm_ioapic_destroy(kvm); kvm_pic_destroy(kvm); @@ -7168,7 +7028,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) } r = -ENXIO; - if (!irqchip_kernel(kvm)) + if (!irqchip_full(kvm)) goto get_irqchip_out; r = kvm_vm_ioctl_get_irqchip(kvm, chip); if (r) @@ -7192,7 +7052,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) } r = -ENXIO; - if (!irqchip_kernel(kvm)) + if (!irqchip_full(kvm)) goto set_irqchip_out; r = kvm_vm_ioctl_set_irqchip(kvm, chip); set_irqchip_out: @@ -7265,6 +7125,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) r = kvm_vm_ioctl_reinject(kvm, &control); break; } +#endif case KVM_SET_BOOT_CPU_ID: r = 0; mutex_lock(&kvm->lock); @@ -7335,9 +7196,12 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) if (user_tsc_khz == 0) user_tsc_khz = tsc_khz; - WRITE_ONCE(kvm->arch.default_tsc_khz, user_tsc_khz); - r = 0; - + mutex_lock(&kvm->lock); + if (!kvm->created_vcpus) { + WRITE_ONCE(kvm->arch.default_tsc_khz, user_tsc_khz); + r = 0; + } + mutex_unlock(&kvm->lock); goto out; } case KVM_GET_TSC_KHZ: { @@ -8289,8 +8153,7 @@ static int kvm_emulate_wbinvd_noskip(struct kvm_vcpu *vcpu) int cpu = get_cpu(); cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask); - on_each_cpu_mask(vcpu->arch.wbinvd_dirty_mask, - wbinvd_ipi, NULL, 1); + wbinvd_on_cpus_mask(vcpu->arch.wbinvd_dirty_mask); put_cpu(); cpumask_clear(vcpu->arch.wbinvd_dirty_mask); } else @@ -10045,8 +9908,11 @@ static void kvm_sched_yield(struct kvm_vcpu *vcpu, unsigned long dest_id) rcu_read_lock(); map = rcu_dereference(vcpu->kvm->arch.apic_map); - if (likely(map) && dest_id <= map->max_apic_id && map->phys_map[dest_id]) - target = map->phys_map[dest_id]->vcpu; + if (likely(map) && dest_id <= map->max_apic_id) { + dest_id = array_index_nospec(dest_id, map->max_apic_id + 1); + if (map->phys_map[dest_id]) + target = map->phys_map[dest_id]->vcpu; + } rcu_read_unlock(); @@ -10724,8 +10590,10 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu) if (irqchip_split(vcpu->kvm)) kvm_scan_ioapic_routes(vcpu, vcpu->arch.ioapic_handled_vectors); +#ifdef CONFIG_KVM_IOAPIC else if (ioapic_in_kernel(vcpu->kvm)) kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors); +#endif if (is_guest_mode(vcpu)) vcpu->arch.load_eoi_exitmap_pending = true; @@ -10779,6 +10647,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) dm_request_for_irq_injection(vcpu) && kvm_cpu_accept_dm_intr(vcpu); fastpath_t exit_fastpath; + u64 run_flags, debug_ctl; bool req_immediate_exit = false; @@ -10926,8 +10795,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_vcpu_update_apicv(vcpu); if (kvm_check_request(KVM_REQ_APF_READY, vcpu)) kvm_check_async_pf_completion(vcpu); + + /* + * Recalc MSR intercepts as userspace may want to intercept + * accesses to MSRs that KVM would otherwise pass through to + * the guest. + */ if (kvm_check_request(KVM_REQ_MSR_FILTER_CHANGED, vcpu)) - kvm_x86_call(msr_filter_changed)(vcpu); + kvm_x86_call(recalc_msr_intercepts)(vcpu); if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu)) kvm_x86_call(update_cpu_dirty_logging)(vcpu); @@ -11023,8 +10898,11 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) goto cancel_injection; } - if (req_immediate_exit) + run_flags = 0; + if (req_immediate_exit) { + run_flags |= KVM_RUN_FORCE_IMMEDIATE_EXIT; kvm_make_request(KVM_REQ_EVENT, vcpu); + } fpregs_assert_state_consistent(); if (test_thread_flag(TIF_NEED_FPU_LOAD)) @@ -11035,19 +10913,29 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (unlikely(vcpu->arch.switch_db_regs && !(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) { - set_debugreg(0, 7); + set_debugreg(DR7_FIXED_1, 7); set_debugreg(vcpu->arch.eff_db[0], 0); set_debugreg(vcpu->arch.eff_db[1], 1); set_debugreg(vcpu->arch.eff_db[2], 2); set_debugreg(vcpu->arch.eff_db[3], 3); /* When KVM_DEBUGREG_WONT_EXIT, dr6 is accessible in guest. */ if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) - kvm_x86_call(set_dr6)(vcpu, vcpu->arch.dr6); + run_flags |= KVM_RUN_LOAD_GUEST_DR6; } else if (unlikely(hw_breakpoint_active())) { - set_debugreg(0, 7); + set_debugreg(DR7_FIXED_1, 7); } - vcpu->arch.host_debugctl = get_debugctlmsr(); + /* + * Refresh the host DEBUGCTL snapshot after disabling IRQs, as DEBUGCTL + * can be modified in IRQ context, e.g. via SMP function calls. Inform + * vendor code if any host-owned bits were changed, e.g. so that the + * value loaded into hardware while running the guest can be updated. + */ + debug_ctl = get_debugctlmsr(); + if ((debug_ctl ^ vcpu->arch.host_debugctl) & kvm_x86_ops.HOST_OWNED_DEBUGCTL && + !vcpu->arch.guest_state_protected) + run_flags |= KVM_RUN_LOAD_DEBUGCTL; + vcpu->arch.host_debugctl = debug_ctl; guest_timing_enter_irqoff(); @@ -11061,8 +10949,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) WARN_ON_ONCE((kvm_vcpu_apicv_activated(vcpu) != kvm_vcpu_apicv_active(vcpu)) && (kvm_get_apic_mode(vcpu) != LAPIC_MODE_DISABLED)); - exit_fastpath = kvm_x86_call(vcpu_run)(vcpu, - req_immediate_exit); + exit_fastpath = kvm_x86_call(vcpu_run)(vcpu, run_flags); if (likely(exit_fastpath != EXIT_FASTPATH_REENTER_GUEST)) break; @@ -11074,6 +10961,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) break; } + run_flags = 0; + /* Note, VM-Exits that go down the "slow" path are accounted below. */ ++vcpu->stat.exits; } @@ -11121,6 +11010,15 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (vcpu->arch.guest_fpu.xfd_err) wrmsrq(MSR_IA32_XFD_ERR, 0); + /* + * Mark this CPU as needing a branch predictor flush before running + * userspace. Must be done before enabling preemption to ensure it gets + * set for the CPU that actually ran the guest, and not the CPU that it + * may migrate to. + */ + if (cpu_feature_enabled(X86_FEATURE_IBPB_EXIT_TO_USER)) + this_cpu_write(x86_ibpb_exit_to_user, true); + /* * Consume any pending interrupts, including the possible source of * VM-Exit on SVM and any ticks that occur between VM-Exit and now. @@ -11547,6 +11445,28 @@ static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu) trace_kvm_fpu(0); } +static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu) +{ + /* + * SIPI_RECEIVED is obsolete; KVM leaves the vCPU in Wait-For-SIPI and + * tracks the pending SIPI separately. SIPI_RECEIVED is still accepted + * by KVM_SET_VCPU_EVENTS for backwards compatibility, but should be + * converted to INIT_RECEIVED. + */ + if (WARN_ON_ONCE(vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED)) + return -EINVAL; + + /* + * Disallow running the vCPU if userspace forced it into an impossible + * MP_STATE, e.g. if the vCPU is in WFS but SIPI is blocked. + */ + if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED && + !kvm_apic_init_sipi_allowed(vcpu)) + return -EINVAL; + + return kvm_x86_call(vcpu_pre_run)(vcpu); +} + int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) { struct kvm_queued_exception *ex = &vcpu->arch.exception; @@ -11649,7 +11569,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) goto out; } - r = kvm_x86_call(vcpu_pre_run)(vcpu); + r = kvm_x86_vcpu_pre_run(vcpu); if (r <= 0) goto out; @@ -11893,21 +11813,16 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, } /* - * Pending INITs are reported using KVM_SET_VCPU_EVENTS, disallow - * forcing the guest into INIT/SIPI if those events are supposed to be - * blocked. KVM prioritizes SMI over INIT, so reject INIT/SIPI state - * if an SMI is pending as well. + * SIPI_RECEIVED is obsolete and no longer used internally; KVM instead + * leaves the vCPU in INIT_RECIEVED (Wait-For-SIPI) and pends the SIPI. + * Translate SIPI_RECEIVED as appropriate for backwards compatibility. */ - if ((!kvm_apic_init_sipi_allowed(vcpu) || vcpu->arch.smi_pending) && - (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED || - mp_state->mp_state == KVM_MP_STATE_INIT_RECEIVED)) - goto out; - if (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED) { - kvm_set_mp_state(vcpu, KVM_MP_STATE_INIT_RECEIVED); + mp_state->mp_state = KVM_MP_STATE_INIT_RECEIVED; set_bit(KVM_APIC_SIPI, &vcpu->arch.apic->pending_events); - } else - kvm_set_mp_state(vcpu, mp_state->mp_state); + } + + kvm_set_mp_state(vcpu, mp_state->mp_state); kvm_make_request(KVM_REQ_EVENT, vcpu); ret = 0; @@ -12789,21 +12704,16 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) if (ret) goto out; - kvm_mmu_init_vm(kvm); + ret = kvm_mmu_init_vm(kvm); + if (ret) + goto out_cleanup_page_track; ret = kvm_x86_call(vm_init)(kvm); if (ret) goto out_uninit_mmu; - INIT_HLIST_HEAD(&kvm->arch.mask_notifier_list); atomic_set(&kvm->arch.noncoherent_dma_count, 0); - /* Reserve bit 0 of irq_sources_bitmap for userspace irq source */ - set_bit(KVM_USERSPACE_IRQ_SOURCE_ID, &kvm->arch.irq_sources_bitmap); - /* Reserve bit 1 of irq_sources_bitmap for irqfd-resampler */ - set_bit(KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID, - &kvm->arch.irq_sources_bitmap); - raw_spin_lock_init(&kvm->arch.tsc_write_lock); mutex_init(&kvm->arch.apic_map_lock); seqcount_raw_spinlock_init(&kvm->arch.pvclock_sc, &kvm->arch.tsc_write_lock); @@ -12842,6 +12752,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) out_uninit_mmu: kvm_mmu_uninit_vm(kvm); +out_cleanup_page_track: kvm_page_track_cleanup(kvm); out: return ret; @@ -12934,7 +12845,9 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm) cancel_delayed_work_sync(&kvm->arch.kvmclock_sync_work); cancel_delayed_work_sync(&kvm->arch.kvmclock_update_work); +#ifdef CONFIG_KVM_IOAPIC kvm_free_pit(kvm); +#endif kvm_mmu_pre_destroy_vm(kvm); static_call_cond(kvm_x86_vm_pre_destroy)(kvm); @@ -12958,8 +12871,10 @@ void kvm_arch_destroy_vm(struct kvm *kvm) } kvm_destroy_vcpus(kvm); kvm_free_msr_filter(srcu_dereference_check(kvm->arch.msr_filter, &kvm->srcu, 1)); +#ifdef CONFIG_KVM_IOAPIC kvm_pic_destroy(kvm); kvm_ioapic_destroy(kvm); +#endif kvfree(rcu_dereference_check(kvm->arch.apic_map, 1)); kfree(srcu_dereference_check(kvm->arch.pmu_event_filter, &kvm->srcu, 1)); kvm_mmu_uninit_vm(kvm); @@ -13569,25 +13484,6 @@ bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu) return kvm_lapic_enabled(vcpu) && apf_pageready_slot_free(vcpu); } -void kvm_arch_start_assignment(struct kvm *kvm) -{ - if (atomic_inc_return(&kvm->arch.assigned_device_count) == 1) - kvm_x86_call(pi_start_assignment)(kvm); -} -EXPORT_SYMBOL_GPL(kvm_arch_start_assignment); - -void kvm_arch_end_assignment(struct kvm *kvm) -{ - atomic_dec(&kvm->arch.assigned_device_count); -} -EXPORT_SYMBOL_GPL(kvm_arch_end_assignment); - -bool noinstr kvm_arch_has_assigned_device(struct kvm *kvm) -{ - return raw_atomic_read(&kvm->arch.assigned_device_count); -} -EXPORT_SYMBOL_GPL(kvm_arch_has_assigned_device); - static void kvm_noncoherent_dma_assignment_start_or_stop(struct kvm *kvm) { /* @@ -13623,77 +13519,6 @@ bool kvm_arch_has_noncoherent_dma(struct kvm *kvm) } EXPORT_SYMBOL_GPL(kvm_arch_has_noncoherent_dma); -int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons, - struct irq_bypass_producer *prod) -{ - struct kvm_kernel_irqfd *irqfd = - container_of(cons, struct kvm_kernel_irqfd, consumer); - struct kvm *kvm = irqfd->kvm; - int ret; - - kvm_arch_start_assignment(irqfd->kvm); - - spin_lock_irq(&kvm->irqfds.lock); - irqfd->producer = prod; - - ret = kvm_x86_call(pi_update_irte)(irqfd->kvm, - prod->irq, irqfd->gsi, 1); - if (ret) - kvm_arch_end_assignment(irqfd->kvm); - - spin_unlock_irq(&kvm->irqfds.lock); - - - return ret; -} - -void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons, - struct irq_bypass_producer *prod) -{ - int ret; - struct kvm_kernel_irqfd *irqfd = - container_of(cons, struct kvm_kernel_irqfd, consumer); - struct kvm *kvm = irqfd->kvm; - - WARN_ON(irqfd->producer != prod); - - /* - * When producer of consumer is unregistered, we change back to - * remapped mode, so we can re-use the current implementation - * when the irq is masked/disabled or the consumer side (KVM - * int this case doesn't want to receive the interrupts. - */ - spin_lock_irq(&kvm->irqfds.lock); - irqfd->producer = NULL; - - ret = kvm_x86_call(pi_update_irte)(irqfd->kvm, - prod->irq, irqfd->gsi, 0); - if (ret) - printk(KERN_INFO "irq bypass consumer (token %p) unregistration" - " fails: %d\n", irqfd->consumer.token, ret); - - spin_unlock_irq(&kvm->irqfds.lock); - - - kvm_arch_end_assignment(irqfd->kvm); -} - -int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set) -{ - return kvm_x86_call(pi_update_irte)(kvm, host_irq, guest_irq, set); -} - -bool kvm_arch_irqfd_route_changed(struct kvm_kernel_irq_routing_entry *old, - struct kvm_kernel_irq_routing_entry *new) -{ - if (old->type != KVM_IRQ_ROUTING_MSI || - new->type != KVM_IRQ_ROUTING_MSI) - return true; - - return !!memcmp(&old->msi, &new->msi, sizeof(new->msi)); -} - bool kvm_vector_hashing_enabled(void) { return vector_hashing; @@ -14093,7 +13918,6 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_nested_intercepts); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_write_tsc_offset); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_ple_window_update); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_pml_full); -EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_pi_irte_update); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_unaccelerated_access); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_incomplete_ipi); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_ga_log); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 832f0faf4779..bcfd9b719ada 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -55,6 +55,28 @@ struct kvm_host_values { void kvm_spurious_fault(void); +#define SIZE_OF_MEMSLOTS_HASHTABLE \ + (sizeof(((struct kvm_memslots *)0)->id_hash) * 2 * KVM_MAX_NR_ADDRESS_SPACES) + +/* Sanity check the size of the memslot hash tables. */ +static_assert(SIZE_OF_MEMSLOTS_HASHTABLE == + (1024 * (1 + IS_ENABLED(CONFIG_X86_64)) * (1 + IS_ENABLED(CONFIG_KVM_SMM)))); + +/* + * Assert that "struct kvm_{svm,vmx,tdx}" is an order-0 or order-1 allocation. + * Spilling over to an order-2 allocation isn't fundamentally problematic, but + * isn't expected to happen in the foreseeable future (O(years)). Assert that + * the size is an order-0 allocation when ignoring the memslot hash tables, to + * help detect and debug unexpected size increases. + */ +#define KVM_SANITY_CHECK_VM_STRUCT_SIZE(x) \ +do { \ + BUILD_BUG_ON(get_order(sizeof(struct x) - SIZE_OF_MEMSLOTS_HASHTABLE) && \ + !IS_ENABLED(CONFIG_DEBUG_KERNEL) && !IS_ENABLED(CONFIG_KASAN)); \ + BUILD_BUG_ON(get_order(sizeof(struct x)) > 1 && \ + !IS_ENABLED(CONFIG_DEBUG_KERNEL) && !IS_ENABLED(CONFIG_KASAN)); \ +} while (0) + #define KVM_NESTED_VMENTER_CONSISTENCY_CHECK(consistency_check) \ ({ \ bool failed = (consistency_check); \ @@ -499,24 +521,34 @@ static inline u64 nsec_to_cycles(struct kvm_vcpu *vcpu, u64 nsec) __rem; \ }) +static inline void kvm_disable_exits(struct kvm *kvm, u64 mask) +{ + kvm->arch.disabled_exits |= mask; +} + static inline bool kvm_mwait_in_guest(struct kvm *kvm) { - return kvm->arch.mwait_in_guest; + return kvm->arch.disabled_exits & KVM_X86_DISABLE_EXITS_MWAIT; } static inline bool kvm_hlt_in_guest(struct kvm *kvm) { - return kvm->arch.hlt_in_guest; + return kvm->arch.disabled_exits & KVM_X86_DISABLE_EXITS_HLT; } static inline bool kvm_pause_in_guest(struct kvm *kvm) { - return kvm->arch.pause_in_guest; + return kvm->arch.disabled_exits & KVM_X86_DISABLE_EXITS_PAUSE; } static inline bool kvm_cstate_in_guest(struct kvm *kvm) { - return kvm->arch.cstate_in_guest; + return kvm->arch.disabled_exits & KVM_X86_DISABLE_EXITS_CSTATE; +} + +static inline bool kvm_aperfmperf_in_guest(struct kvm *kvm) +{ + return kvm->arch.disabled_exits & KVM_X86_DISABLE_EXITS_APERFMPERF; } static inline bool kvm_notify_vmexit_enabled(struct kvm *kvm) diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 9b029bb29a16..d6b2a665b499 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1526,7 +1526,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode, if (kvm_read_guest_virt(vcpu, (gva_t)sched_poll.ports, ports, sched_poll.nr_ports * sizeof(*ports), &e)) { *r = -EFAULT; - return true; + goto out; } for (i = 0; i < sched_poll.nr_ports; i++) { @@ -1971,8 +1971,19 @@ int kvm_xen_setup_evtchn(struct kvm *kvm, { struct kvm_vcpu *vcpu; - if (ue->u.xen_evtchn.port >= max_evtchn_port(kvm)) - return -EINVAL; + /* + * Don't check for the port being within range of max_evtchn_port(). + * Userspace can configure what ever targets it likes; events just won't + * be delivered if/while the target is invalid, just like userspace can + * configure MSIs which target non-existent APICs. + * + * This allow on Live Migration and Live Update, the IRQ routing table + * can be restored *independently* of other things like creating vCPUs, + * without imposing an ordering dependency on userspace. In this + * particular case, the problematic ordering would be with setting the + * Xen 'long mode' flag, which changes max_evtchn_port() to allow 4096 + * instead of 1024 event channels. + */ /* We only support 2 level event channels for now */ if (ue->u.xen_evtchn.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) diff --git a/arch/x86/lib/.gitignore b/arch/x86/lib/.gitignore index 8ae0f93ecbfd..ec2131c9fd20 100644 --- a/arch/x86/lib/.gitignore +++ b/arch/x86/lib/.gitignore @@ -1,2 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only + +# This now-removed directory used to contain generated files. +/crypto/ + inat-tables.c diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 4fa5c4e1ba8a..2dba7f83ef97 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -3,8 +3,6 @@ # Makefile for x86 specific library files. # -obj-y += crypto/ - # Produces uninteresting flaky coverage. KCOV_INSTRUMENT_delay.o := n @@ -40,16 +38,6 @@ lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o lib-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o lib-$(CONFIG_MITIGATION_RETPOLINE) += retpoline.o -obj-$(CONFIG_CRC32_ARCH) += crc32-x86.o -crc32-x86-y := crc32.o crc32-pclmul.o -crc32-x86-$(CONFIG_64BIT) += crc32c-3way.o - -obj-$(CONFIG_CRC64_ARCH) += crc64-x86.o -crc64-x86-y := crc64.o crc64-pclmul.o - -obj-$(CONFIG_CRC_T10DIF_ARCH) += crc-t10dif-x86.o -crc-t10dif-x86-y := crc-t10dif.o crc16-msb-pclmul.o - obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o obj-y += iomem.o diff --git a/arch/x86/lib/cache-smp.c b/arch/x86/lib/cache-smp.c index 7af743bd3b13..c5c60d07308c 100644 --- a/arch/x86/lib/cache-smp.c +++ b/arch/x86/lib/cache-smp.c @@ -14,9 +14,31 @@ void wbinvd_on_cpu(int cpu) } EXPORT_SYMBOL(wbinvd_on_cpu); -int wbinvd_on_all_cpus(void) +void wbinvd_on_all_cpus(void) { on_each_cpu(__wbinvd, NULL, 1); - return 0; } EXPORT_SYMBOL(wbinvd_on_all_cpus); + +void wbinvd_on_cpus_mask(struct cpumask *cpus) +{ + on_each_cpu_mask(cpus, __wbinvd, NULL, 1); +} +EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask); + +static void __wbnoinvd(void *dummy) +{ + wbnoinvd(); +} + +void wbnoinvd_on_all_cpus(void) +{ + on_each_cpu(__wbnoinvd, NULL, 1); +} +EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus); + +void wbnoinvd_on_cpus_mask(struct cpumask *cpus) +{ + on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1); +} +EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask); diff --git a/arch/x86/lib/crc32.c b/arch/x86/lib/crc32.c deleted file mode 100644 index d09343e2cea9..000000000000 --- a/arch/x86/lib/crc32.c +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * x86-optimized CRC32 functions - * - * Copyright (C) 2008 Intel Corporation - * Copyright 2012 Xyratex Technology Limited - * Copyright 2024 Google LLC - */ - -#include -#include -#include "crc-pclmul-template.h" - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_crc32); -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_pclmulqdq); - -DECLARE_CRC_PCLMUL_FUNCS(crc32_lsb, u32); - -u32 crc32_le_arch(u32 crc, const u8 *p, size_t len) -{ - CRC_PCLMUL(crc, p, len, crc32_lsb, crc32_lsb_0xedb88320_consts, - have_pclmulqdq); - return crc32_le_base(crc, p, len); -} -EXPORT_SYMBOL(crc32_le_arch); - -#ifdef CONFIG_X86_64 -#define CRC32_INST "crc32q %1, %q0" -#else -#define CRC32_INST "crc32l %1, %0" -#endif - -/* - * Use carryless multiply version of crc32c when buffer size is >= 512 to - * account for FPU state save/restore overhead. - */ -#define CRC32C_PCLMUL_BREAKEVEN 512 - -asmlinkage u32 crc32c_x86_3way(u32 crc, const u8 *buffer, size_t len); - -u32 crc32c_arch(u32 crc, const u8 *p, size_t len) -{ - size_t num_longs; - - if (!static_branch_likely(&have_crc32)) - return crc32c_base(crc, p, len); - - if (IS_ENABLED(CONFIG_X86_64) && len >= CRC32C_PCLMUL_BREAKEVEN && - static_branch_likely(&have_pclmulqdq) && crypto_simd_usable()) { - kernel_fpu_begin(); - crc = crc32c_x86_3way(crc, p, len); - kernel_fpu_end(); - return crc; - } - - for (num_longs = len / sizeof(unsigned long); - num_longs != 0; num_longs--, p += sizeof(unsigned long)) - asm(CRC32_INST : "+r" (crc) : ASM_INPUT_RM (*(unsigned long *)p)); - - if (sizeof(unsigned long) > 4 && (len & 4)) { - asm("crc32l %1, %0" : "+r" (crc) : ASM_INPUT_RM (*(u32 *)p)); - p += 4; - } - if (len & 2) { - asm("crc32w %1, %0" : "+r" (crc) : ASM_INPUT_RM (*(u16 *)p)); - p += 2; - } - if (len & 1) - asm("crc32b %1, %0" : "+r" (crc) : ASM_INPUT_RM (*p)); - - return crc; -} -EXPORT_SYMBOL(crc32c_arch); - -u32 crc32_be_arch(u32 crc, const u8 *p, size_t len) -{ - return crc32_be_base(crc, p, len); -} -EXPORT_SYMBOL(crc32_be_arch); - -static int __init crc32_x86_init(void) -{ - if (boot_cpu_has(X86_FEATURE_XMM4_2)) - static_branch_enable(&have_crc32); - if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) { - static_branch_enable(&have_pclmulqdq); - INIT_CRC_PCLMUL(crc32_lsb); - } - return 0; -} -subsys_initcall(crc32_x86_init); - -static void __exit crc32_x86_exit(void) -{ -} -module_exit(crc32_x86_exit); - -u32 crc32_optimizations(void) -{ - u32 optimizations = 0; - - if (static_key_enabled(&have_crc32)) - optimizations |= CRC32C_OPTIMIZATION; - if (static_key_enabled(&have_pclmulqdq)) - optimizations |= CRC32_LE_OPTIMIZATION; - return optimizations; -} -EXPORT_SYMBOL(crc32_optimizations); - -MODULE_DESCRIPTION("x86-optimized CRC32 functions"); -MODULE_LICENSE("GPL"); diff --git a/arch/x86/lib/crypto/sha256.c b/arch/x86/lib/crypto/sha256.c deleted file mode 100644 index 80380f8fdcee..000000000000 --- a/arch/x86/lib/crypto/sha256.c +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SHA-256 optimized for x86_64 - * - * Copyright 2025 Google LLC - */ -#include -#include -#include -#include -#include - -asmlinkage void sha256_transform_ssse3(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -asmlinkage void sha256_transform_avx(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -asmlinkage void sha256_transform_rorx(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); -asmlinkage void sha256_ni_transform(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks); - -static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_sha256_x86); - -DEFINE_STATIC_CALL(sha256_blocks_x86, sha256_transform_ssse3); - -void sha256_blocks_simd(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - if (static_branch_likely(&have_sha256_x86)) { - kernel_fpu_begin(); - static_call(sha256_blocks_x86)(state, data, nblocks); - kernel_fpu_end(); - } else { - sha256_blocks_generic(state, data, nblocks); - } -} -EXPORT_SYMBOL_GPL(sha256_blocks_simd); - -void sha256_blocks_arch(u32 state[SHA256_STATE_WORDS], - const u8 *data, size_t nblocks) -{ - sha256_blocks_generic(state, data, nblocks); -} -EXPORT_SYMBOL_GPL(sha256_blocks_arch); - -bool sha256_is_arch_optimized(void) -{ - return static_key_enabled(&have_sha256_x86); -} -EXPORT_SYMBOL_GPL(sha256_is_arch_optimized); - -static int __init sha256_x86_mod_init(void) -{ - if (boot_cpu_has(X86_FEATURE_SHA_NI)) { - static_call_update(sha256_blocks_x86, sha256_ni_transform); - } else if (cpu_has_xfeatures(XFEATURE_MASK_SSE | - XFEATURE_MASK_YMM, NULL) && - boot_cpu_has(X86_FEATURE_AVX)) { - if (boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_BMI2)) - static_call_update(sha256_blocks_x86, - sha256_transform_rorx); - else - static_call_update(sha256_blocks_x86, - sha256_transform_avx); - } else if (!boot_cpu_has(X86_FEATURE_SSSE3)) { - return 0; - } - static_branch_enable(&have_sha256_x86); - return 0; -} -subsys_initcall(sha256_x86_mod_init); - -static void __exit sha256_x86_mod_exit(void) -{ -} -module_exit(sha256_x86_mod_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-256 optimized for x86_64"); diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c index bf8dab18be97..2fdc1f1f5adb 100644 --- a/arch/x86/mm/extable.c +++ b/arch/x86/mm/extable.c @@ -122,13 +122,12 @@ static bool ex_handler_sgx(const struct exception_table_entry *fixup, static bool ex_handler_fprestore(const struct exception_table_entry *fixup, struct pt_regs *regs) { - regs->ip = ex_fixup_addr(fixup); - WARN_ONCE(1, "Bad FPU state detected at %pB, reinitializing FPU registers.", (void *)instruction_pointer(regs)); fpu_reset_from_exception_fixup(); - return true; + + return ex_handler_default(fixup, regs); } /* diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 7456df985d96..bb57e93b4caf 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -1063,13 +1063,9 @@ unsigned long arch_max_swapfile_size(void) static struct execmem_info execmem_info __ro_after_init; #ifdef CONFIG_ARCH_HAS_EXECMEM_ROX -void execmem_fill_trapping_insns(void *ptr, size_t size, bool writeable) +void execmem_fill_trapping_insns(void *ptr, size_t size) { - /* fill memory with INT3 instructions */ - if (writeable) - memset(ptr, INT3_INSN_OPCODE, size); - else - text_poke_set(ptr, INT3_INSN_OPCODE, size); + memset(ptr, INT3_INSN_OPCODE, size); } #endif @@ -1102,7 +1098,21 @@ struct execmem_info __init *execmem_arch_setup(void) .pgprot = pgprot, .alignment = MODULE_ALIGN, }, - [EXECMEM_KPROBES ... EXECMEM_BPF] = { + [EXECMEM_KPROBES] = { + .flags = flags, + .start = start, + .end = MODULES_END, + .pgprot = PAGE_KERNEL_ROX, + .alignment = MODULE_ALIGN, + }, + [EXECMEM_FTRACE] = { + .flags = flags, + .start = start, + .end = MODULES_END, + .pgprot = pgprot, + .alignment = MODULE_ALIGN, + }, + [EXECMEM_BPF] = { .flags = EXECMEM_KASAN_SHADOW, .start = start, .end = MODULES_END, diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 607d6a2e66e2..8a34fff6ab2b 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -749,8 +748,6 @@ void mark_rodata_ro(void) pr_info("Write protecting kernel text and read-only data: %luk\n", size >> 10); - execmem_cache_make_ro(); - kernel_set_to_readonly = 1; #ifdef CONFIG_CPA_DEBUG diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index ee66fae9ebcc..b9426fce5f3e 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -224,6 +223,24 @@ static void sync_global_pgds(unsigned long start, unsigned long end) sync_global_pgds_l4(start, end); } +/* + * Make kernel mappings visible in all page tables in the system. + * This is necessary except when the init task populates kernel mappings + * during the boot process. In that case, all processes originating from + * the init task copies the kernel mappings, so there is no issue. + * Otherwise, missing synchronization could lead to kernel crashes due + * to missing page table entries for certain kernel mappings. + * + * Synchronization is performed at the top level, which is the PGD in + * 5-level paging systems. But in 4-level paging systems, however, + * pgd_populate() is a no-op, so synchronization is done at the P4D level. + * sync_global_pgds() handles this difference between paging levels. + */ +void arch_sync_kernel_mappings(unsigned long start, unsigned long end) +{ + sync_global_pgds(start, end); +} + /* * NOTE: This function is marked __ref because it calls __init function * (alloc_bootmem_pages). It's safe to do it ONLY when after_bootmem == 0. @@ -806,7 +823,7 @@ kernel_physical_mapping_change(unsigned long paddr_start, } #ifndef CONFIG_NUMA -static inline void x86_numa_init(void) +static __always_inline void x86_numa_init(void) { memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); } @@ -1392,8 +1409,6 @@ void mark_rodata_ro(void) (end - start) >> 10); set_memory_ro(start, (end - start) >> PAGE_SHIFT); - execmem_cache_make_ro(); - kernel_set_to_readonly = 1; /* diff --git a/arch/x86/mm/pat/memtype.c b/arch/x86/mm/pat/memtype.c index 2e7923844afe..c09284302dd3 100644 --- a/arch/x86/mm/pat/memtype.c +++ b/arch/x86/mm/pat/memtype.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index 46edc11726b7..8834c76f91c9 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -1257,6 +1257,9 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr, pgprot_t pgprot; int i = 0; + if (!cpu_feature_enabled(X86_FEATURE_PSE)) + return 0; + addr &= PMD_MASK; pte = pte_offset_kernel(pmd, addr); first = *pte; diff --git a/arch/x86/mm/pgprot.c b/arch/x86/mm/pgprot.c index c84bd9540b16..dc1afd5c839d 100644 --- a/arch/x86/mm/pgprot.c +++ b/arch/x86/mm/pgprot.c @@ -32,7 +32,7 @@ void add_encrypt_protection_map(void) protection_map[i] = pgprot_encrypted(protection_map[i]); } -pgprot_t vm_get_page_prot(unsigned long vm_flags) +pgprot_t vm_get_page_prot(vm_flags_t vm_flags) { unsigned long val = pgprot_val(protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]); diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c index 190299834011..b10d4d131dce 100644 --- a/arch/x86/mm/pti.c +++ b/arch/x86/mm/pti.c @@ -38,6 +38,7 @@ #include #include #include +#include #undef pr_fmt #define pr_fmt(fmt) "Kernel/User page tables isolation: " fmt @@ -84,7 +85,8 @@ void __init pti_check_boottime_disable(void) return; } - if (cpu_mitigations_off()) + if (pti_mode == PTI_AUTO && + !cpu_attack_vector_mitigated(CPU_MITIGATE_USER_KERNEL)) pti_mode = PTI_FORCE_OFF; if (pti_mode == PTI_FORCE_OFF) { pti_print_if_insecure("disabled on command line."); @@ -98,6 +100,11 @@ void __init pti_check_boottime_disable(void) return; setup_force_cpu_cap(X86_FEATURE_PTI); + + if (cpu_feature_enabled(X86_FEATURE_INVLPGB)) { + pr_debug("PTI enabled, disabling INVLPGB\n"); + setup_clear_cpu_cap(X86_FEATURE_INVLPGB); + } } 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 15672cb926fc..7e3fca164620 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -3501,13 +3501,6 @@ int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_func return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs, image, buf); } -static const char *bpf_get_prog_name(struct bpf_prog *prog) -{ - if (prog->aux->ksym.prog) - return prog->aux->ksym.name; - return prog->aux->name; -} - 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; @@ -3531,7 +3524,7 @@ static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size if (stack_ptr[0] != PRIV_STACK_GUARD_VAL || stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL) { pr_err("BPF private stack overflow/underflow detected for prog %sx\n", - bpf_get_prog_name(prog)); + bpf_jit_get_prog_name(prog)); break; } } @@ -3845,7 +3838,6 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp } return; #endif - WARN(1, "verification of programs using bpf_throw should have failed\n"); } void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke, diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c index f8126821a94d..aaa7017416f7 100644 --- a/arch/x86/platform/ce4100/ce4100.c +++ b/arch/x86/platform/ce4100/ce4100.c @@ -5,19 +5,12 @@ * (C) Copyright 2010 Intel Corporation */ #include -#include -#include #include -#include -#include #include #include #include -#include #include -#include -#include /* * The CE4100 platform has an internal 8051 Microcontroller which is @@ -31,94 +24,6 @@ static void ce4100_power_off(void) outb(0x4, 0xcf9); } -#ifdef CONFIG_SERIAL_8250 - -static unsigned int mem_serial_in(struct uart_port *p, int offset) -{ - offset = offset << p->regshift; - return readl(p->membase + offset); -} - -/* - * The UART Tx interrupts are not set under some conditions and therefore serial - * transmission hangs. This is a silicon issue and has not been root caused. The - * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT - * bit of LSR register in interrupt handler to see whether at least one of these - * two bits is set, if so then process the transmit request. If this workaround - * is not applied, then the serial transmission may hang. This workaround is for - * errata number 9 in Errata - B step. -*/ - -static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) -{ - unsigned int ret, ier, lsr; - - if (offset == UART_IIR) { - offset = offset << p->regshift; - ret = readl(p->membase + offset); - if (ret & UART_IIR_NO_INT) { - /* see if the TX interrupt should have really set */ - ier = mem_serial_in(p, UART_IER); - /* see if the UART's XMIT interrupt is enabled */ - if (ier & UART_IER_THRI) { - lsr = mem_serial_in(p, UART_LSR); - /* now check to see if the UART should be - generating an interrupt (but isn't) */ - if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) - ret &= ~UART_IIR_NO_INT; - } - } - } else - ret = mem_serial_in(p, offset); - return ret; -} - -static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) -{ - offset = offset << p->regshift; - writel(value, p->membase + offset); -} - -static void ce4100_serial_fixup(int port, struct uart_port *up, - u32 *capabilities) -{ -#ifdef CONFIG_EARLY_PRINTK - /* - * Over ride the legacy port configuration that comes from - * asm/serial.h. Using the ioport driver then switching to the - * PCI memmaped driver hangs the IOAPIC - */ - if (up->iotype != UPIO_MEM32) { - up->uartclk = 14745600; - up->mapbase = 0xdffe0200; - set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, - up->mapbase & PAGE_MASK); - up->membase = - (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); - up->membase += up->mapbase & ~PAGE_MASK; - up->mapbase += port * 0x100; - up->membase += port * 0x100; - up->iotype = UPIO_MEM32; - up->regshift = 2; - up->irq = 4; - } -#endif - up->iobase = 0; - up->serial_in = ce4100_mem_serial_in; - up->serial_out = ce4100_mem_serial_out; - - *capabilities |= (1 << 12); -} - -static __init void sdv_serial_fixup(void) -{ - serial8250_set_isa_configurator(ce4100_serial_fixup); -} - -#else -static inline void sdv_serial_fixup(void) {}; -#endif - static void __init sdv_arch_setup(void) { sdv_serial_fixup(); diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index e7e8f77f77f8..b4409df2105a 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -216,8 +216,8 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) * When SEV-ES is active, the GHCB as set by the kernel will be used * by firmware. Create a 1:1 unencrypted mapping for each GHCB. */ - if (sev_es_efi_map_ghcbs(pgd)) { - pr_err("Failed to create 1:1 mapping for the GHCBs!\n"); + if (sev_es_efi_map_ghcbs_cas(pgd)) { + pr_err("Failed to create 1:1 mapping for the GHCBs and CAs!\n"); return 1; } diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index ebdfd7b84feb..e0a607a14e7e 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -35,7 +35,7 @@ targets += purgatory.ro purgatory.chk PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel PURGATORY_CFLAGS := -mcmodel=small -ffreestanding -fno-zero-initialized-in-bss -g0 PURGATORY_CFLAGS += -fpic -fvisibility=hidden -PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING +PURGATORY_CFLAGS += $(DISABLE_KSTACK_ERASE) -DDISABLE_BRANCH_PROFILING PURGATORY_CFLAGS += -fno-stack-protector # Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That diff --git a/arch/x86/purgatory/purgatory.c b/arch/x86/purgatory/purgatory.c index aea47e793963..655139dd0532 100644 --- a/arch/x86/purgatory/purgatory.c +++ b/arch/x86/purgatory/purgatory.c @@ -25,7 +25,7 @@ static int verify_sha256_digest(void) { struct kexec_sha_region *ptr, *end; u8 digest[SHA256_DIGEST_SIZE]; - struct sha256_state sctx; + struct sha256_ctx sctx; sha256_init(&sctx); end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions); diff --git a/arch/x86/tools/insn_decoder_test.c b/arch/x86/tools/insn_decoder_test.c index 08cd913cbd4e..8bf15c4aefa9 100644 --- a/arch/x86/tools/insn_decoder_test.c +++ b/arch/x86/tools/insn_decoder_test.c @@ -167,7 +167,7 @@ int main(int argc, char **argv) pr_warn("Decoded and checked %d instructions with %d " "failures\n", insns, warnings); else - fprintf(stdout, "%s: success: Decoded and checked %d" + fprintf(stdout, " %s: success: Decoded and checked %d" " instructions\n", prog, insns); return 0; } diff --git a/arch/x86/tools/insn_sanity.c b/arch/x86/tools/insn_sanity.c index 213f35f94feb..e743f0ea01ee 100644 --- a/arch/x86/tools/insn_sanity.c +++ b/arch/x86/tools/insn_sanity.c @@ -253,9 +253,9 @@ int main(int argc, char **argv) } fprintf((errors) ? stderr : stdout, - "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", + " %s: %s: Decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", prog, - (errors) ? "Failure" : "Success", + (errors) ? "failure" : "success", insns, (input_file) ? "given" : "random", errors, diff --git a/arch/x86/um/asm/syscall.h b/arch/x86/um/asm/syscall.h index 56a2f0913e3c..d6208d0fad51 100644 --- a/arch/x86/um/asm/syscall.h +++ b/arch/x86/um/asm/syscall.h @@ -9,6 +9,8 @@ typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); +extern const sys_call_ptr_t sys_call_table[]; + static inline int syscall_get_arch(struct task_struct *task) { #ifdef CONFIG_X86_32 diff --git a/arch/x86/um/ptrace.c b/arch/x86/um/ptrace.c index 3275870330fe..2635ca2595a3 100644 --- a/arch/x86/um/ptrace.c +++ b/arch/x86/um/ptrace.c @@ -161,7 +161,7 @@ static int fpregs_legacy_set(struct task_struct *target, from = kbuf; } - return um_fxsr_from_i387(fxsave, &buf); + return um_fxsr_from_i387(fxsave, from); } #endif @@ -236,7 +236,7 @@ static int generic_fpregs_set(struct task_struct *target, static struct user_regset uml_regsets[] __ro_after_init = { [REGSET_GENERAL] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct user_regs_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -246,7 +246,7 @@ static struct user_regset uml_regsets[] __ro_after_init = { #ifdef CONFIG_X86_32 /* Old FP registers, they are needed in signal frames */ [REGSET_FP_LEGACY] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_i387_ia32_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), @@ -257,10 +257,10 @@ static struct user_regset uml_regsets[] __ro_after_init = { #endif [REGSET_FP] = { #ifdef CONFIG_X86_32 - .core_note_type = NT_PRXFPREG, + USER_REGSET_NOTE_TYPE(PRXFPREG), .n = sizeof(struct user32_fxsr_struct) / sizeof(long), #else - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(struct user_i387_struct) / sizeof(long), #endif .size = sizeof(long), @@ -270,7 +270,7 @@ static struct user_regset uml_regsets[] __ro_after_init = { .set = generic_fpregs_set, }, [REGSET_XSTATE] = { - .core_note_type = NT_X86_XSTATE, + USER_REGSET_NOTE_TYPE(X86_XSTATE), .size = sizeof(long), .align = sizeof(long), .active = generic_fpregs_active, diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h index 8f7476ff6e95..572ea2d79131 100644 --- a/arch/x86/um/shared/sysdep/ptrace.h +++ b/arch/x86/um/shared/sysdep/ptrace.h @@ -44,18 +44,6 @@ #include "ptrace_64.h" #endif -struct syscall_args { - unsigned long args[6]; -}; - -#define SYSCALL_ARGS(r) ((struct syscall_args) \ - { .args = { UPT_SYSCALL_ARG1(r), \ - UPT_SYSCALL_ARG2(r), \ - UPT_SYSCALL_ARG3(r), \ - UPT_SYSCALL_ARG4(r), \ - UPT_SYSCALL_ARG5(r), \ - UPT_SYSCALL_ARG6(r) } } ) - extern unsigned long host_fp_size; struct uml_pt_regs { diff --git a/arch/x86/um/shared/sysdep/syscalls.h b/arch/x86/um/shared/sysdep/syscalls.h deleted file mode 100644 index b2060ac707f0..000000000000 --- a/arch/x86/um/shared/sysdep/syscalls.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifdef __i386__ -#include "syscalls_32.h" -#else -#include "syscalls_64.h" -#endif diff --git a/arch/x86/um/shared/sysdep/syscalls_32.h b/arch/x86/um/shared/sysdep/syscalls_32.h deleted file mode 100644 index f6e9f84397e7..000000000000 --- a/arch/x86/um/shared/sysdep/syscalls_32.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2000 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) - * Licensed under the GPL - */ - -#include -#include - -typedef long syscall_handler_t(struct syscall_args); - -extern syscall_handler_t *sys_call_table[]; - -#define EXECUTE_SYSCALL(syscall, regs) \ - ((*sys_call_table[syscall]))(SYSCALL_ARGS(®s->regs)) diff --git a/arch/x86/um/shared/sysdep/syscalls_64.h b/arch/x86/um/shared/sysdep/syscalls_64.h deleted file mode 100644 index b6b997225841..000000000000 --- a/arch/x86/um/shared/sysdep/syscalls_64.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2003 PathScale, Inc. - * - * Licensed under the GPL - */ - -#ifndef __SYSDEP_X86_64_SYSCALLS_H__ -#define __SYSDEP_X86_64_SYSCALLS_H__ - -#include -#include - -typedef long syscall_handler_t(long, long, long, long, long, long); - -extern syscall_handler_t *sys_call_table[]; - -#define EXECUTE_SYSCALL(syscall, regs) \ - (((*sys_call_table[syscall]))(UPT_SYSCALL_ARG1(®s->regs), \ - UPT_SYSCALL_ARG2(®s->regs), \ - UPT_SYSCALL_ARG3(®s->regs), \ - UPT_SYSCALL_ARG4(®s->regs), \ - UPT_SYSCALL_ARG5(®s->regs), \ - UPT_SYSCALL_ARG6(®s->regs))) - -extern syscall_handler_t sys_modify_ldt; -extern syscall_handler_t sys_arch_prctl; - -#endif diff --git a/arch/x86/um/tls_32.c b/arch/x86/um/tls_32.c index cb3f17627d16..1909c2e640b2 100644 --- a/arch/x86/um/tls_32.c +++ b/arch/x86/um/tls_32.c @@ -186,7 +186,7 @@ int arch_switch_tls(struct task_struct *to) /* * We have no need whatsoever to switch TLS for kernel threads; beyond * that, that would also result in us calling os_set_thread_area with - * userspace_pid[cpu] == 0, which gives an error. + * task->mm == NULL, which would cause a crash. */ if (likely(to->mm)) return load_TLS(O_FORCE, to); diff --git a/arch/x86/video/video-common.c b/arch/x86/video/video-common.c index 81fc97a2a837..e0aeee99bc99 100644 --- a/arch/x86/video/video-common.c +++ b/arch/x86/video/video-common.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -27,6 +28,11 @@ EXPORT_SYMBOL(pgprot_framebuffer); bool video_is_primary_device(struct device *dev) { +#ifdef CONFIG_SCREEN_INFO + struct screen_info *si = &screen_info; + struct resource res[SCREEN_INFO_MAX_RESOURCES]; + ssize_t i, numres; +#endif struct pci_dev *pdev; if (!dev_is_pci(dev)) @@ -34,7 +40,24 @@ bool video_is_primary_device(struct device *dev) pdev = to_pci_dev(dev); - return (pdev == vga_default_device()); + if (!pci_is_display(pdev)) + return false; + + if (pdev == vga_default_device()) + return true; + +#ifdef CONFIG_SCREEN_INFO + numres = screen_info_resources(si, res, ARRAY_SIZE(res)); + for (i = 0; i < numres; ++i) { + if (!(res[i].flags & IORESOURCE_MEM)) + continue; + + if (pci_find_resource(pdev, &res[i])) + return true; + } +#endif + + return false; } EXPORT_SYMBOL(video_is_primary_device); diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 2457d13c3f9e..c7a9a087ccaf 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -75,8 +75,9 @@ static inline void seamcall_err_ret(u64 fn, u64 err, args->r9, args->r10, args->r11); } -static inline int sc_retry_prerr(sc_func_t func, sc_err_func_t err_func, - u64 fn, struct tdx_module_args *args) +static __always_inline int sc_retry_prerr(sc_func_t func, + sc_err_func_t err_func, + u64 fn, struct tdx_module_args *args) { u64 sret = sc_retry(func, fn, args); diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index cc5dba738389..13fe45dea296 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild @@ -3,7 +3,6 @@ generated-y += syscall_table.h generic-y += extable.h generic-y += kvm_para.h generic-y += mcs_spinlock.h -generic-y += param.h generic-y += parport.h generic-y += qrwlock.h generic-y += qspinlock.h diff --git a/arch/xtensa/include/asm/bootparam.h b/arch/xtensa/include/asm/bootparam.h index 6333bd1eb9d2..a459ffbaf7ab 100644 --- a/arch/xtensa/include/asm/bootparam.h +++ b/arch/xtensa/include/asm/bootparam.h @@ -27,7 +27,7 @@ #define BP_TAG_FIRST 0x7B0B /* first tag with a version number */ #define BP_TAG_LAST 0x7E0B /* last tag */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* All records are aligned to 4 bytes */ diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h index 95e33a913962..b6db4838b175 100644 --- a/arch/xtensa/include/asm/cmpxchg.h +++ b/arch/xtensa/include/asm/cmpxchg.h @@ -11,7 +11,7 @@ #ifndef _XTENSA_CMPXCHG_H #define _XTENSA_CMPXCHG_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -220,6 +220,6 @@ __arch_xchg(unsigned long x, volatile void * ptr, int size) } } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _XTENSA_CMPXCHG_H */ diff --git a/arch/xtensa/include/asm/coprocessor.h b/arch/xtensa/include/asm/coprocessor.h index 3b1a0d5d2169..e0447bcc52c5 100644 --- a/arch/xtensa/include/asm/coprocessor.h +++ b/arch/xtensa/include/asm/coprocessor.h @@ -16,7 +16,7 @@ #include #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ # include .macro xchal_sa_start a b @@ -69,7 +69,7 @@ -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ /* * XTENSA_HAVE_COPROCESSOR(x) returns 1 if coprocessor x is configured. @@ -87,7 +87,7 @@ #define XTENSA_HAVE_IO_PORTS \ XCHAL_CP_PORT_MASK -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * Additional registers. @@ -151,5 +151,5 @@ void local_coprocessors_flush_release_all(void); #endif /* XTENSA_HAVE_COPROCESSORS */ -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _XTENSA_COPROCESSOR_H */ diff --git a/arch/xtensa/include/asm/current.h b/arch/xtensa/include/asm/current.h index df275d554788..7b483538f066 100644 --- a/arch/xtensa/include/asm/current.h +++ b/arch/xtensa/include/asm/current.h @@ -13,7 +13,7 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 0ea4f84cd558..f676d209d110 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,20 +12,20 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long return_address(unsigned level); #define ftrace_return_address(n) return_address(n) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #ifdef CONFIG_FUNCTION_TRACER #define MCOUNT_ADDR ((unsigned long)(_mcount)) #define MCOUNT_INSN_SIZE 3 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern void _mcount(void); #define mcount _mcount -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* CONFIG_FUNCTION_TRACER */ #endif /* _XTENSA_FTRACE_H */ diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h index 574795a20d6f..101bcb87e15b 100644 --- a/arch/xtensa/include/asm/initialize_mmu.h +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -34,7 +34,7 @@ #define CA_WRITEBACK (0x4) #endif -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define XTENSA_HWVERSION_RC_2009_0 230000 @@ -240,6 +240,6 @@ .endm -#endif /*__ASSEMBLY__*/ +#endif /*__ASSEMBLER__*/ #endif /* _XTENSA_INITIALIZE_MMU_H */ diff --git a/arch/xtensa/include/asm/jump_label.h b/arch/xtensa/include/asm/jump_label.h index 46c8596259d2..38e3e2a9b0fb 100644 --- a/arch/xtensa/include/asm/jump_label.h +++ b/arch/xtensa/include/asm/jump_label.h @@ -4,7 +4,7 @@ #ifndef _ASM_XTENSA_JUMP_LABEL_H #define _ASM_XTENSA_JUMP_LABEL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -61,5 +61,5 @@ struct jump_entry { jump_label_t key; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/xtensa/include/asm/kasan.h b/arch/xtensa/include/asm/kasan.h index 8d2b4248466f..0da91b64fab9 100644 --- a/arch/xtensa/include/asm/kasan.h +++ b/arch/xtensa/include/asm/kasan.h @@ -2,7 +2,7 @@ #ifndef __ASM_KASAN_H #define __ASM_KASAN_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifdef CONFIG_KASAN diff --git a/arch/xtensa/include/asm/kmem_layout.h b/arch/xtensa/include/asm/kmem_layout.h index 6fc05cba61a2..6949724625a0 100644 --- a/arch/xtensa/include/asm/kmem_layout.h +++ b/arch/xtensa/include/asm/kmem_layout.h @@ -80,7 +80,7 @@ #if (!XCHAL_HAVE_PTP_MMU || XCHAL_HAVE_SPANNING_WAY) && defined(CONFIG_USE_OF) #define XCHAL_KIO_PADDR xtensa_get_kio_paddr() -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long xtensa_kio_paddr; static inline unsigned long xtensa_get_kio_paddr(void) diff --git a/arch/xtensa/include/asm/page.h b/arch/xtensa/include/asm/page.h index 644413792bf3..20655174b111 100644 --- a/arch/xtensa/include/asm/page.h +++ b/arch/xtensa/include/asm/page.h @@ -80,7 +80,7 @@ #endif -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define __pgprot(x) (x) @@ -172,7 +172,7 @@ static inline unsigned long ___pa(unsigned long va) #define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #include #endif /* _XTENSA_PAGE_H */ diff --git a/arch/xtensa/include/asm/pgtable.h b/arch/xtensa/include/asm/pgtable.h index d62aa1c316fc..d6eb695f2b26 100644 --- a/arch/xtensa/include/asm/pgtable.h +++ b/arch/xtensa/include/asm/pgtable.h @@ -203,7 +203,7 @@ * What follows is the closest we can get by reasonable means.. * See linux/mm/mmap.c for protection_map[] array that uses these definitions. */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define pte_ERROR(e) \ printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) @@ -366,10 +366,10 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) return pte; } -#endif /* !defined (__ASSEMBLY__) */ +#endif /* !defined (__ASSEMBLER__) */ -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ /* Assembly macro _PGD_INDEX is the same as C pgd_index(unsigned long), * _PGD_OFFSET as C pgd_offset(struct mm_struct*, unsigned long), @@ -408,7 +408,7 @@ void update_mmu_tlb_range(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, unsigned int nr); #define update_mmu_tlb_range update_mmu_tlb_range -#endif /* !defined (__ASSEMBLY__) */ +#endif /* !defined (__ASSEMBLER__) */ #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG #define __HAVE_ARCH_PTEP_GET_AND_CLEAR diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 47b5df86ab5c..60a211356335 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -105,7 +105,7 @@ #error Unsupported xtensa ABI #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #if defined(__XTENSA_WINDOWED_ABI__) @@ -263,5 +263,5 @@ static inline unsigned long get_er(unsigned long addr) #endif /* XCHAL_HAVE_EXTERN_REGS */ -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _XTENSA_PROCESSOR_H */ diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h index 4871e5a4d6fb..d0568ff6d349 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -41,7 +41,7 @@ #define NO_SYSCALL (-1) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -106,11 +106,11 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) int do_syscall_trace_enter(struct pt_regs *regs); void do_syscall_trace_leave(struct pt_regs *regs); -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLER__ */ # include #define PT_REGS_OFFSET (KERNEL_STACK_SIZE - PT_USER_SIZE) -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* _XTENSA_PTRACE_H */ diff --git a/arch/xtensa/include/asm/signal.h b/arch/xtensa/include/asm/signal.h index de169b4eaeef..d301e68573cc 100644 --- a/arch/xtensa/include/asm/signal.h +++ b/arch/xtensa/include/asm/signal.h @@ -14,10 +14,10 @@ #include -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define __ARCH_HAS_SA_RESTORER #include -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _XTENSA_SIGNAL_H */ diff --git a/arch/xtensa/include/asm/thread_info.h b/arch/xtensa/include/asm/thread_info.h index e0dffcc43b9e..5b74dfc35ef9 100644 --- a/arch/xtensa/include/asm/thread_info.h +++ b/arch/xtensa/include/asm/thread_info.h @@ -16,7 +16,7 @@ #define CURRENT_SHIFT KERNEL_STACK_SHIFT -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ # include #endif @@ -28,7 +28,7 @@ * must also be changed */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #if XTENSA_HAVE_COPROCESSORS @@ -80,7 +80,7 @@ struct thread_info { * macros/functions for gaining access to the thread information structure */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define INIT_THREAD_INFO(tsk) \ { \ @@ -99,7 +99,7 @@ static __always_inline struct thread_info *current_thread_info(void) return ti; } -#else /* !__ASSEMBLY__ */ +#else /* !__ASSEMBLER__ */ /* how to get the thread information struct from ASM */ #define GET_THREAD_INFO(reg,sp) \ diff --git a/arch/xtensa/include/asm/tlbflush.h b/arch/xtensa/include/asm/tlbflush.h index 573df8cea200..3edaebeef423 100644 --- a/arch/xtensa/include/asm/tlbflush.h +++ b/arch/xtensa/include/asm/tlbflush.h @@ -20,7 +20,7 @@ #define ITLB_HIT_BIT 3 #define DTLB_HIT_BIT 4 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* TLB flushing: * @@ -201,5 +201,5 @@ static inline unsigned long read_itlb_translation (int way) return tmp; } -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _XTENSA_TLBFLUSH_H */ diff --git a/arch/xtensa/include/uapi/asm/param.h b/arch/xtensa/include/uapi/asm/param.h deleted file mode 100644 index e6feb4ee0590..000000000000 --- a/arch/xtensa/include/uapi/asm/param.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * include/asm-xtensa/param.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 _UAPI_XTENSA_PARAM_H -#define _UAPI_XTENSA_PARAM_H - -#ifndef __KERNEL__ -# define HZ 100 -#endif - -#define EXEC_PAGESIZE 4096 - -#ifndef NGROUPS -#define NGROUPS 32 -#endif - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#endif /* _UAPI_XTENSA_PARAM_H */ diff --git a/arch/xtensa/include/uapi/asm/ptrace.h b/arch/xtensa/include/uapi/asm/ptrace.h index 9115e86ebc75..6e89ea301438 100644 --- a/arch/xtensa/include/uapi/asm/ptrace.h +++ b/arch/xtensa/include/uapi/asm/ptrace.h @@ -42,7 +42,7 @@ #define PTRACE_GETFDPIC_EXEC 0 #define PTRACE_GETFDPIC_INTERP 1 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct user_pt_regs { __u32 pc; diff --git a/arch/xtensa/include/uapi/asm/signal.h b/arch/xtensa/include/uapi/asm/signal.h index b8c824dd4b74..8060f1914400 100644 --- a/arch/xtensa/include/uapi/asm/signal.h +++ b/arch/xtensa/include/uapi/asm/signal.h @@ -19,7 +19,7 @@ #define _NSIG_BPW 32 #define _NSIG_WORDS (_NSIG / _NSIG_BPW) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -77,7 +77,7 @@ typedef struct { #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -106,5 +106,5 @@ typedef struct sigaltstack { __kernel_size_t ss_size; } stack_t; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _UAPI_XTENSA_SIGNAL_H */ diff --git a/arch/xtensa/include/uapi/asm/types.h b/arch/xtensa/include/uapi/asm/types.h index 12db8ac38750..2e9217a06ebf 100644 --- a/arch/xtensa/include/uapi/asm/types.h +++ b/arch/xtensa/include/uapi/asm/types.h @@ -14,7 +14,7 @@ #include -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ # define __XTENSA_UL(x) (x) # define __XTENSA_UL_CONST(x) x #else @@ -23,7 +23,7 @@ # define __XTENSA_UL_CONST(x) ___XTENSA_UL_CONST(x) #endif -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #endif diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 9056cd1a8302..ff0600a0584c 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -193,7 +193,7 @@ enum xtensa_regset { static const struct user_regset xtensa_regsets[] = { [REGSET_GPR] = { - .core_note_type = NT_PRSTATUS, + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = sizeof(struct user_pt_regs) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), @@ -201,7 +201,7 @@ static const struct user_regset xtensa_regsets[] = { .set = gpr_set, }, [REGSET_TIE] = { - .core_note_type = NT_PRFPREG, + USER_REGSET_NOTE_TYPE(PRFPREG), .n = sizeof(elf_xtregs_t) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl index f657a77314f8..374e4cb788d8 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl @@ -438,3 +438,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 0cb1e9873aab..50e51047e1fe 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -454,17 +454,10 @@ static struct bfq_io_cq *icq_to_bic(struct io_cq *icq) */ static struct bfq_io_cq *bfq_bic_lookup(struct request_queue *q) { - struct bfq_io_cq *icq; - unsigned long flags; - if (!current->io_context) return NULL; - spin_lock_irqsave(&q->queue_lock, flags); - icq = icq_to_bic(ioc_lookup_icq(q)); - spin_unlock_irqrestore(&q->queue_lock, flags); - - return icq; + return icq_to_bic(ioc_lookup_icq(q)); } /* @@ -701,17 +694,13 @@ static void bfq_limit_depth(blk_opf_t opf, struct blk_mq_alloc_data *data) { struct bfq_data *bfqd = data->q->elevator->elevator_data; struct bfq_io_cq *bic = bfq_bic_lookup(data->q); - int depth; - unsigned limit = data->q->nr_requests; - unsigned int act_idx; + unsigned int limit, act_idx; /* Sync reads have full depth available */ - if (op_is_sync(opf) && !op_is_write(opf)) { - depth = 0; - } else { - depth = bfqd->word_depths[!!bfqd->wr_busy_queues][op_is_sync(opf)]; - limit = (limit * depth) >> bfqd->full_depth_shift; - } + if (op_is_sync(opf) && !op_is_write(opf)) + limit = data->q->nr_requests; + else + limit = bfqd->async_depths[!!bfqd->wr_busy_queues][op_is_sync(opf)]; for (act_idx = 0; bic && act_idx < bfqd->num_actuators; act_idx++) { /* Fast path to check if bfqq is already allocated. */ @@ -725,14 +714,16 @@ static void bfq_limit_depth(blk_opf_t opf, struct blk_mq_alloc_data *data) * available requests and thus starve other entities. */ if (bfqq_request_over_limit(bfqd, bic, opf, act_idx, limit)) { - depth = 1; + limit = 1; break; } } + bfq_log(bfqd, "[%s] wr_busy %d sync %d depth %u", - __func__, bfqd->wr_busy_queues, op_is_sync(opf), depth); - if (depth) - data->shallow_depth = depth; + __func__, bfqd->wr_busy_queues, op_is_sync(opf), limit); + + if (limit < data->q->nr_requests) + data->shallow_depth = limit; } static struct bfq_queue * @@ -2457,15 +2448,8 @@ static bool bfq_bio_merge(struct request_queue *q, struct bio *bio, unsigned int nr_segs) { struct bfq_data *bfqd = q->elevator->elevator_data; - struct request *free = NULL; - /* - * bfq_bic_lookup grabs the queue_lock: invoke it now and - * store its return value for later use, to avoid nesting - * queue_lock inside the bfqd->lock. We assume that the bic - * returned by bfq_bic_lookup does not go away before - * bfqd->lock is taken. - */ struct bfq_io_cq *bic = bfq_bic_lookup(q); + struct request *free = NULL; bool ret; spin_lock_irq(&bfqd->lock); @@ -5863,8 +5847,7 @@ static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, goto out; } - bfqq = kmem_cache_alloc_node(bfq_pool, - GFP_NOWAIT | __GFP_ZERO | __GFP_NOWARN, + bfqq = kmem_cache_alloc_node(bfq_pool, GFP_NOWAIT | __GFP_ZERO, bfqd->queue->node); if (bfqq) { @@ -7128,9 +7111,8 @@ void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) */ static void bfq_update_depths(struct bfq_data *bfqd, struct sbitmap_queue *bt) { - unsigned int depth = 1U << bt->sb.shift; + unsigned int nr_requests = bfqd->queue->nr_requests; - bfqd->full_depth_shift = bt->sb.shift; /* * In-word depths if no bfq_queue is being weight-raised: * leaving 25% of tags only for sync reads. @@ -7142,13 +7124,13 @@ static void bfq_update_depths(struct bfq_data *bfqd, struct sbitmap_queue *bt) * limit 'something'. */ /* no more than 50% of tags for async I/O */ - bfqd->word_depths[0][0] = max(depth >> 1, 1U); + bfqd->async_depths[0][0] = max(nr_requests >> 1, 1U); /* * no more than 75% of tags for sync writes (25% extra tags * w.r.t. async I/O, to prevent async I/O from starving sync * writes) */ - bfqd->word_depths[0][1] = max((depth * 3) >> 2, 1U); + bfqd->async_depths[0][1] = max((nr_requests * 3) >> 2, 1U); /* * In-word depths in case some bfq_queue is being weight- @@ -7158,9 +7140,9 @@ static void bfq_update_depths(struct bfq_data *bfqd, struct sbitmap_queue *bt) * shortage. */ /* no more than ~18% of tags for async I/O */ - bfqd->word_depths[1][0] = max((depth * 3) >> 4, 1U); + bfqd->async_depths[1][0] = max((nr_requests * 3) >> 4, 1U); /* no more than ~37% of tags for sync writes (~20% extra tags) */ - bfqd->word_depths[1][1] = max((depth * 6) >> 4, 1U); + bfqd->async_depths[1][1] = max((nr_requests * 6) >> 4, 1U); } static void bfq_depth_updated(struct blk_mq_hw_ctx *hctx) @@ -7232,22 +7214,16 @@ static void bfq_init_root_group(struct bfq_group *root_group, root_group->sched_data.bfq_class_idle_last_service = jiffies; } -static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) +static int bfq_init_queue(struct request_queue *q, struct elevator_queue *eq) { struct bfq_data *bfqd; - struct elevator_queue *eq; unsigned int i; struct blk_independent_access_ranges *ia_ranges = q->disk->ia_ranges; - eq = elevator_alloc(q, e); - if (!eq) + bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node); + if (!bfqd) return -ENOMEM; - bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node); - if (!bfqd) { - kobject_put(&eq->kobj); - return -ENOMEM; - } eq->elevator_data = bfqd; spin_lock_irq(&q->queue_lock); @@ -7405,7 +7381,6 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) out_free: kfree(bfqd); - kobject_put(&eq->kobj); return -ENOMEM; } diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h index 687a3a7ba784..34a498e6b2a5 100644 --- a/block/bfq-iosched.h +++ b/block/bfq-iosched.h @@ -427,9 +427,6 @@ struct bfq_iocq_bfqq_data { */ bool saved_IO_bound; - u64 saved_io_start_time; - u64 saved_tot_idle_time; - /* * Same purpose as the previous fields for the values of the * field keeping the queue's belonging to a large burst @@ -450,6 +447,9 @@ struct bfq_iocq_bfqq_data { */ unsigned int saved_weight; + u64 saved_io_start_time; + u64 saved_tot_idle_time; + /* * Similar to previous fields: save wr information. */ @@ -457,13 +457,13 @@ struct bfq_iocq_bfqq_data { unsigned long saved_last_wr_start_finish; unsigned long saved_service_from_wr; unsigned long saved_wr_start_at_switch_to_srt; - unsigned int saved_wr_cur_max_time; struct bfq_ttime saved_ttime; + unsigned int saved_wr_cur_max_time; /* Save also injection state */ - u64 saved_last_serv_time_ns; unsigned int saved_inject_limit; unsigned long saved_decrease_time_jif; + u64 saved_last_serv_time_ns; /* candidate queue for a stable merge (due to close creation time) */ struct bfq_queue *stable_merge_bfqq; @@ -813,8 +813,7 @@ struct bfq_data { * Depth limits used in bfq_limit_depth (see comments on the * function) */ - unsigned int word_depths[2][2]; - unsigned int full_depth_shift; + unsigned int async_depths[2][2]; /* * Number of independent actuators. This is equal to 1 in diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c index 9c6657664792..687952f63bbb 100644 --- a/block/bio-integrity-auto.c +++ b/block/bio-integrity-auto.c @@ -54,10 +54,10 @@ static bool bi_offload_capable(struct blk_integrity *bi) { switch (bi->csum_type) { case BLK_INTEGRITY_CSUM_CRC64: - return bi->tuple_size == sizeof(struct crc64_pi_tuple); + return bi->metadata_size == sizeof(struct crc64_pi_tuple); case BLK_INTEGRITY_CSUM_CRC: case BLK_INTEGRITY_CSUM_IP: - return bi->tuple_size == sizeof(struct t10_pi_tuple); + return bi->metadata_size == sizeof(struct t10_pi_tuple); default: pr_warn_once("%s: unknown integrity checksum type:%d\n", __func__, bi->csum_type); diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 10912988c8f5..6b077ca937f6 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -128,6 +128,9 @@ 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)) + return 0; + if (bvec_try_merge_hw_page(q, bv, page, len, offset)) { bip->bip_iter.bi_size += len; return len; diff --git a/block/bio.c b/block/bio.c index 3c0a558c90f5..3b371a5da159 100644 --- a/block/bio.c +++ b/block/bio.c @@ -653,13 +653,13 @@ static void bio_truncate(struct bio *bio, unsigned new_size) bio_for_each_segment(bv, bio, iter) { if (done + bv.bv_len > new_size) { - unsigned offset; + size_t offset; if (!truncated) offset = new_size - done; else offset = 0; - zero_user(bv.bv_page, bv.bv_offset + offset, + memzero_page(bv.bv_page, bv.bv_offset + offset, bv.bv_len - offset); truncated = true; } @@ -930,8 +930,6 @@ static bool bvec_try_merge_page(struct bio_vec *bv, struct page *page, return false; if (xen_domain() && !xen_biovec_phys_mergeable(bv, page)) return false; - if (!zone_device_pages_have_same_pgmap(bv->bv_page, page)) - return false; if ((vec_end_addr & PAGE_MASK) != ((page_addr + off) & PAGE_MASK)) { if (IS_ENABLED(CONFIG_KMSAN)) @@ -982,6 +980,9 @@ void __bio_add_page(struct bio *bio, struct page *page, WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); WARN_ON_ONCE(bio_full(bio, len)); + if (is_pci_p2pdma_page(page)) + bio->bi_opf |= REQ_P2PDMA | REQ_NOMERGE; + bvec_set_page(&bio->bi_io_vec[bio->bi_vcnt], page, len, off); bio->bi_iter.bi_size += len; bio->bi_vcnt++; @@ -1022,11 +1023,16 @@ int bio_add_page(struct bio *bio, struct page *page, if (bio->bi_iter.bi_size > UINT_MAX - len) return 0; - if (bio->bi_vcnt > 0 && - bvec_try_merge_page(&bio->bi_io_vec[bio->bi_vcnt - 1], - page, len, offset)) { - bio->bi_iter.bi_size += len; - return len; + 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)) + return 0; + + if (bvec_try_merge_page(bv, page, len, offset)) { + bio->bi_iter.bi_size += len; + return len; + } } if (bio->bi_vcnt >= bio->bi_max_vecs) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 5936db7f8475..fe9ebd6a2e14 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -394,7 +394,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, /* allocate */ if (!new_blkg) { - new_blkg = blkg_alloc(blkcg, disk, GFP_NOWAIT | __GFP_NOWARN); + new_blkg = blkg_alloc(blkcg, disk, GFP_NOWAIT); if (unlikely(!new_blkg)) { ret = -ENOMEM; goto err_put_css; @@ -1467,7 +1467,7 @@ blkcg_css_alloc(struct cgroup_subsys_state *parent_css) spin_lock_init(&blkcg->lock); refcount_set(&blkcg->online_pin, 1); - INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT | __GFP_NOWARN); + INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT); INIT_HLIST_HEAD(&blkcg->blkg_list); #ifdef CONFIG_CGROUP_WRITEBACK INIT_LIST_HEAD(&blkcg->cgwb_list); @@ -1630,7 +1630,7 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) pd_prealloc = NULL; } else { pd = pol->pd_alloc_fn(disk, blkg->blkcg, - GFP_NOWAIT | __GFP_NOWARN); + GFP_NOWAIT); } if (!pd) { diff --git a/block/blk-core.c b/block/blk-core.c index fdac48aec5ef..4201504158a1 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -557,7 +557,7 @@ static inline int bio_check_eod(struct bio *bio) sector_t maxsector = bdev_nr_sectors(bio->bi_bdev); unsigned int nr_sectors = bio_sectors(bio); - if (nr_sectors && + if (nr_sectors && maxsector && (nr_sectors > maxsector || bio->bi_iter.bi_sector > maxsector - nr_sectors)) { pr_info_ratelimited("%s: attempt to access beyond end of device\n" diff --git a/block/blk-integrity.c b/block/blk-integrity.c index e4e2567061f9..056b8948369d 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "blk.h" @@ -54,6 +55,73 @@ int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio) return segments; } +int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd, + struct logical_block_metadata_cap __user *argp) +{ + struct blk_integrity *bi = blk_get_integrity(bdev->bd_disk); + struct logical_block_metadata_cap meta_cap = {}; + size_t usize = _IOC_SIZE(cmd); + + if (_IOC_DIR(cmd) != _IOC_DIR(FS_IOC_GETLBMD_CAP) || + _IOC_TYPE(cmd) != _IOC_TYPE(FS_IOC_GETLBMD_CAP) || + _IOC_NR(cmd) != _IOC_NR(FS_IOC_GETLBMD_CAP) || + _IOC_SIZE(cmd) < LBMD_SIZE_VER0) + return -ENOIOCTLCMD; + + if (!bi) + goto out; + + if (bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE) + meta_cap.lbmd_flags |= LBMD_PI_CAP_INTEGRITY; + if (bi->flags & BLK_INTEGRITY_REF_TAG) + meta_cap.lbmd_flags |= LBMD_PI_CAP_REFTAG; + meta_cap.lbmd_interval = 1 << bi->interval_exp; + meta_cap.lbmd_size = bi->metadata_size; + meta_cap.lbmd_pi_size = bi->pi_tuple_size; + meta_cap.lbmd_pi_offset = bi->pi_offset; + meta_cap.lbmd_opaque_size = bi->metadata_size - bi->pi_tuple_size; + if (meta_cap.lbmd_opaque_size && !bi->pi_offset) + meta_cap.lbmd_opaque_offset = bi->pi_tuple_size; + + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_NONE: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_NONE; + break; + case BLK_INTEGRITY_CSUM_IP: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_IP; + break; + case BLK_INTEGRITY_CSUM_CRC: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_CRC16_T10DIF; + break; + case BLK_INTEGRITY_CSUM_CRC64: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_CRC64_NVME; + break; + } + + if (bi->csum_type != BLK_INTEGRITY_CSUM_NONE) + meta_cap.lbmd_app_tag_size = 2; + + if (bi->flags & BLK_INTEGRITY_REF_TAG) { + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_CRC64: + meta_cap.lbmd_ref_tag_size = + sizeof_field(struct crc64_pi_tuple, ref_tag); + break; + case BLK_INTEGRITY_CSUM_CRC: + case BLK_INTEGRITY_CSUM_IP: + meta_cap.lbmd_ref_tag_size = + sizeof_field(struct t10_pi_tuple, ref_tag); + break; + default: + break; + } + } + +out: + return copy_struct_to_user(argp, usize, &meta_cap, sizeof(meta_cap), + NULL); +} + /** * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist * @rq: request to map @@ -239,7 +307,7 @@ static ssize_t format_show(struct device *dev, struct device_attribute *attr, { struct blk_integrity *bi = dev_to_bi(dev); - if (!bi->tuple_size) + if (!bi->metadata_size) return sysfs_emit(page, "none\n"); return sysfs_emit(page, "%s\n", blk_integrity_profile_name(bi)); } diff --git a/block/blk-ioc.c b/block/blk-ioc.c index ce82770c72ab..9fda3906e5f5 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -308,24 +308,23 @@ int __copy_io(unsigned long clone_flags, struct task_struct *tsk) #ifdef CONFIG_BLK_ICQ /** - * ioc_lookup_icq - lookup io_cq from ioc + * ioc_lookup_icq - lookup io_cq from ioc in io issue path * @q: the associated request_queue * * Look up io_cq associated with @ioc - @q pair from @ioc. Must be called - * with @q->queue_lock held. + * from io issue path, either return NULL if current issue io to @q for the + * first time, or return a valid icq. */ struct io_cq *ioc_lookup_icq(struct request_queue *q) { struct io_context *ioc = current->io_context; struct io_cq *icq; - lockdep_assert_held(&q->queue_lock); - /* * icq's are indexed from @ioc using radix tree and hint pointer, - * both of which are protected with RCU. All removals are done - * holding both q and ioc locks, and we're holding q lock - if we - * find a icq which points to us, it's guaranteed to be valid. + * both of which are protected with RCU, io issue path ensures that + * both request_queue and current task are valid, the found icq + * is guaranteed to be valid until the io is done. */ rcu_read_lock(); icq = rcu_dereference(ioc->icq_hint); @@ -419,10 +418,7 @@ struct io_cq *ioc_find_get_icq(struct request_queue *q) task_unlock(current); } else { get_io_context(ioc); - - spin_lock_irq(&q->queue_lock); icq = ioc_lookup_icq(q); - spin_unlock_irq(&q->queue_lock); } if (!icq) { diff --git a/block/blk-mq-cpumap.c b/block/blk-mq-cpumap.c index 444798c5374f..705da074ad6c 100644 --- a/block/blk-mq-cpumap.c +++ b/block/blk-mq-cpumap.c @@ -12,16 +12,56 @@ #include #include #include +#include #include "blk.h" #include "blk-mq.h" +static unsigned int blk_mq_num_queues(const struct cpumask *mask, + unsigned int max_queues) +{ + unsigned int num; + + num = cpumask_weight(mask); + return min_not_zero(num, max_queues); +} + +/** + * blk_mq_num_possible_queues - Calc nr of queues for multiqueue devices + * @max_queues: The maximum number of queues the hardware/driver + * supports. If max_queues is 0, the argument is + * ignored. + * + * Calculates the number of queues to be used for a multiqueue + * device based on the number of possible CPUs. + */ +unsigned int blk_mq_num_possible_queues(unsigned int max_queues) +{ + return blk_mq_num_queues(cpu_possible_mask, max_queues); +} +EXPORT_SYMBOL_GPL(blk_mq_num_possible_queues); + +/** + * blk_mq_num_online_queues - Calc nr of queues for multiqueue devices + * @max_queues: The maximum number of queues the hardware/driver + * supports. If max_queues is 0, the argument is + * ignored. + * + * Calculates the number of queues to be used for a multiqueue + * device based on the number of online CPUs. + */ +unsigned int blk_mq_num_online_queues(unsigned int max_queues) +{ + return blk_mq_num_queues(cpu_online_mask, max_queues); +} +EXPORT_SYMBOL_GPL(blk_mq_num_online_queues); + void blk_mq_map_queues(struct blk_mq_queue_map *qmap) { const struct cpumask *masks; - unsigned int queue, cpu; + unsigned int queue, cpu, nr_masks; - masks = group_cpus_evenly(qmap->nr_queues); + masks = group_cpus_evenly(qmap->nr_queues, &nr_masks); if (!masks) { for_each_possible_cpu(cpu) qmap->mq_map[cpu] = qmap->queue_offset; @@ -29,7 +69,7 @@ void blk_mq_map_queues(struct blk_mq_queue_map *qmap) } for (queue = 0; queue < qmap->nr_queues; queue++) { - for_each_cpu(cpu, &masks[queue]) + for_each_cpu(cpu, &masks[queue % nr_masks]) qmap->mq_map[cpu] = qmap->queue_offset + queue; } kfree(masks); diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 29b3540dd180..32c65efdda46 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -95,6 +95,7 @@ static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(SQ_SCHED), QUEUE_FLAG_NAME(DISABLE_WBT_DEF), QUEUE_FLAG_NAME(NO_ELV_SWITCH), + QUEUE_FLAG_NAME(QOS_ENABLED), }; #undef QUEUE_FLAG_NAME @@ -521,7 +522,7 @@ CTX_RQ_SEQ_OPS(poll, HCTX_TYPE_POLL); static int blk_mq_debugfs_show(struct seq_file *m, void *v) { const struct blk_mq_debugfs_attr *attr = m->private; - void *data = d_inode(m->file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(m->file); return attr->show(data, m); } @@ -531,7 +532,7 @@ static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf, { struct seq_file *m = file->private_data; const struct blk_mq_debugfs_attr *attr = m->private; - void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(file); /* * Attributes that only implement .seq_ops are read-only and 'attr' is @@ -546,7 +547,7 @@ static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf, static int blk_mq_debugfs_open(struct inode *inode, struct file *file) { const struct blk_mq_debugfs_attr *attr = inode->i_private; - void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(file); struct seq_file *m; int ret; @@ -612,11 +613,9 @@ static void debugfs_create_files(struct dentry *parent, void *data, if (IS_ERR_OR_NULL(parent)) return; - d_inode(parent)->i_private = data; - for (; attr->name; attr++) - debugfs_create_file(attr->name, attr->mode, parent, - (void *)attr, &blk_mq_debugfs_fops); + debugfs_create_file_aux(attr->name, attr->mode, parent, + (void *)attr, data, &blk_mq_debugfs_fops); } void blk_mq_debugfs_register(struct request_queue *q) diff --git a/block/blk-mq-dma.c b/block/blk-mq-dma.c index 82bae475dfa4..ad283017caef 100644 --- a/block/blk-mq-dma.c +++ b/block/blk-mq-dma.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2025 Christoph Hellwig */ +#include #include "blk.h" struct phys_vec { @@ -61,6 +62,166 @@ static bool blk_map_iter_next(struct request *req, struct req_iterator *iter, return true; } +/* + * The IOVA-based DMA API wants to be able to coalesce at the minimal IOMMU page + * size granularity (which is guaranteed to be <= PAGE_SIZE and usually 4k), so + * we need to ensure our segments are aligned to this as well. + * + * Note that there is no point in using the slightly more complicated IOVA based + * path for single segment mappings. + */ +static inline bool blk_can_dma_map_iova(struct request *req, + struct device *dma_dev) +{ + return !((queue_virt_boundary(req->q) + 1) & + dma_get_merge_boundary(dma_dev)); +} + +static bool blk_dma_map_bus(struct blk_dma_iter *iter, struct phys_vec *vec) +{ + iter->addr = pci_p2pdma_bus_addr_map(&iter->p2pdma, vec->paddr); + iter->len = vec->len; + return true; +} + +static bool blk_dma_map_direct(struct request *req, struct device *dma_dev, + struct blk_dma_iter *iter, struct phys_vec *vec) +{ + iter->addr = dma_map_page(dma_dev, phys_to_page(vec->paddr), + offset_in_page(vec->paddr), vec->len, rq_dma_dir(req)); + if (dma_mapping_error(dma_dev, iter->addr)) { + iter->status = BLK_STS_RESOURCE; + return false; + } + iter->len = vec->len; + return true; +} + +static bool blk_rq_dma_map_iova(struct request *req, struct device *dma_dev, + struct dma_iova_state *state, struct blk_dma_iter *iter, + struct phys_vec *vec) +{ + enum dma_data_direction dir = rq_dma_dir(req); + unsigned int mapped = 0; + int error; + + iter->addr = state->addr; + iter->len = dma_iova_size(state); + + do { + error = dma_iova_link(dma_dev, state, vec->paddr, mapped, + vec->len, dir, 0); + if (error) + break; + mapped += vec->len; + } while (blk_map_iter_next(req, &iter->iter, vec)); + + error = dma_iova_sync(dma_dev, state, 0, mapped); + if (error) { + iter->status = errno_to_blk_status(error); + return false; + } + + return true; +} + +/** + * blk_rq_dma_map_iter_start - map the first DMA segment for a request + * @req: request to map + * @dma_dev: device to map to + * @state: DMA IOVA state + * @iter: block layer DMA iterator + * + * Start DMA mapping @req to @dma_dev. @state and @iter are provided by the + * caller and don't need to be initialized. @state needs to be stored for use + * at unmap time, @iter is only needed at map time. + * + * Returns %false if there is no segment to map, including due to an error, or + * %true ft it did map a segment. + * + * If a segment was mapped, the DMA address for it is returned in @iter.addr and + * the length in @iter.len. If no segment was mapped the status code is + * returned in @iter.status. + * + * The caller can call blk_rq_dma_map_coalesce() to check if further segments + * need to be mapped after this, or go straight to blk_rq_dma_map_iter_next() + * to try to map the following segments. + */ +bool blk_rq_dma_map_iter_start(struct request *req, struct device *dma_dev, + struct dma_iova_state *state, struct blk_dma_iter *iter) +{ + unsigned int total_len = blk_rq_payload_bytes(req); + struct phys_vec vec; + + iter->iter.bio = req->bio; + iter->iter.iter = req->bio->bi_iter; + memset(&iter->p2pdma, 0, sizeof(iter->p2pdma)); + iter->status = BLK_STS_OK; + + /* + * Grab the first segment ASAP because we'll need it to check for P2P + * transfers. + */ + if (!blk_map_iter_next(req, &iter->iter, &vec)) + return false; + + if (IS_ENABLED(CONFIG_PCI_P2PDMA) && (req->cmd_flags & REQ_P2PDMA)) { + switch (pci_p2pdma_state(&iter->p2pdma, dma_dev, + phys_to_page(vec.paddr))) { + case PCI_P2PDMA_MAP_BUS_ADDR: + return blk_dma_map_bus(iter, &vec); + case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE: + /* + * P2P transfers through the host bridge are treated the + * same as non-P2P transfers below and during unmap. + */ + req->cmd_flags &= ~REQ_P2PDMA; + break; + default: + iter->status = BLK_STS_INVAL; + return false; + } + } + + if (blk_can_dma_map_iova(req, dma_dev) && + dma_iova_try_alloc(dma_dev, state, vec.paddr, total_len)) + return blk_rq_dma_map_iova(req, dma_dev, state, iter, &vec); + return blk_dma_map_direct(req, dma_dev, iter, &vec); +} +EXPORT_SYMBOL_GPL(blk_rq_dma_map_iter_start); + +/** + * blk_rq_dma_map_iter_next - map the next DMA segment for a request + * @req: request to map + * @dma_dev: device to map to + * @state: DMA IOVA state + * @iter: block layer DMA iterator + * + * Iterate to the next mapping after a previous call to + * blk_rq_dma_map_iter_start(). See there for a detailed description of the + * arguments. + * + * Returns %false if there is no segment to map, including due to an error, or + * %true ft it did map a segment. + * + * If a segment was mapped, the DMA address for it is returned in @iter.addr and + * the length in @iter.len. If no segment was mapped the status code is + * returned in @iter.status. + */ +bool blk_rq_dma_map_iter_next(struct request *req, struct device *dma_dev, + struct dma_iova_state *state, struct blk_dma_iter *iter) +{ + struct phys_vec vec; + + if (!blk_map_iter_next(req, &iter->iter, &vec)) + return false; + + if (iter->p2pdma.map == PCI_P2PDMA_MAP_BUS_ADDR) + return blk_dma_map_bus(iter, &vec); + return blk_dma_map_direct(req, dma_dev, iter, &vec); +} +EXPORT_SYMBOL_GPL(blk_rq_dma_map_iter_next); + static inline struct scatterlist * blk_next_sg(struct scatterlist **sg, struct scatterlist *sglist) { diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 55a0fd105147..e2ce4a28e6c9 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -374,64 +374,17 @@ bool blk_mq_sched_try_insert_merge(struct request_queue *q, struct request *rq, } EXPORT_SYMBOL_GPL(blk_mq_sched_try_insert_merge); -static int blk_mq_sched_alloc_map_and_rqs(struct request_queue *q, - struct blk_mq_hw_ctx *hctx, - unsigned int hctx_idx) -{ - if (blk_mq_is_shared_tags(q->tag_set->flags)) { - hctx->sched_tags = q->sched_shared_tags; - return 0; - } - - hctx->sched_tags = blk_mq_alloc_map_and_rqs(q->tag_set, hctx_idx, - q->nr_requests); - - if (!hctx->sched_tags) - return -ENOMEM; - return 0; -} - -static void blk_mq_exit_sched_shared_tags(struct request_queue *queue) -{ - blk_mq_free_rq_map(queue->sched_shared_tags); - queue->sched_shared_tags = NULL; -} - /* called in queue's release handler, tagset has gone away */ static void blk_mq_sched_tags_teardown(struct request_queue *q, unsigned int flags) { struct blk_mq_hw_ctx *hctx; unsigned long i; - queue_for_each_hw_ctx(q, hctx, i) { - if (hctx->sched_tags) { - if (!blk_mq_is_shared_tags(flags)) - blk_mq_free_rq_map(hctx->sched_tags); - hctx->sched_tags = NULL; - } - } + queue_for_each_hw_ctx(q, hctx, i) + hctx->sched_tags = NULL; if (blk_mq_is_shared_tags(flags)) - blk_mq_exit_sched_shared_tags(q); -} - -static int blk_mq_init_sched_shared_tags(struct request_queue *queue) -{ - struct blk_mq_tag_set *set = queue->tag_set; - - /* - * Set initial depth at max so that we don't need to reallocate for - * updating nr_requests. - */ - queue->sched_shared_tags = blk_mq_alloc_map_and_rqs(set, - BLK_MQ_NO_HCTX_IDX, - MAX_SCHED_RQ); - if (!queue->sched_shared_tags) - return -ENOMEM; - - blk_mq_tag_update_sched_shared_tags(queue); - - return 0; + q->sched_shared_tags = NULL; } void blk_mq_sched_reg_debugfs(struct request_queue *q) @@ -458,8 +411,140 @@ void blk_mq_sched_unreg_debugfs(struct request_queue *q) mutex_unlock(&q->debugfs_mutex); } +void blk_mq_free_sched_tags(struct elevator_tags *et, + struct blk_mq_tag_set *set) +{ + unsigned long i; + + /* Shared tags are stored at index 0 in @tags. */ + if (blk_mq_is_shared_tags(set->flags)) + blk_mq_free_map_and_rqs(set, et->tags[0], BLK_MQ_NO_HCTX_IDX); + else { + for (i = 0; i < et->nr_hw_queues; i++) + blk_mq_free_map_and_rqs(set, et->tags[i], i); + } + + kfree(et); +} + +void blk_mq_free_sched_tags_batch(struct xarray *et_table, + struct blk_mq_tag_set *set) +{ + struct request_queue *q; + struct elevator_tags *et; + + lockdep_assert_held_write(&set->update_nr_hwq_lock); + + list_for_each_entry(q, &set->tag_list, tag_set_list) { + /* + * Accessing q->elevator without holding q->elevator_lock is + * safe because we're holding here set->update_nr_hwq_lock in + * the writer context. So, scheduler update/switch code (which + * acquires the same lock but in the reader context) can't run + * concurrently. + */ + if (q->elevator) { + et = xa_load(et_table, q->id); + if (unlikely(!et)) + WARN_ON_ONCE(1); + else + blk_mq_free_sched_tags(et, set); + } + } +} + +struct elevator_tags *blk_mq_alloc_sched_tags(struct blk_mq_tag_set *set, + unsigned int nr_hw_queues) +{ + unsigned int nr_tags; + int i; + struct elevator_tags *et; + gfp_t gfp = GFP_NOIO | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY; + + if (blk_mq_is_shared_tags(set->flags)) + nr_tags = 1; + else + nr_tags = nr_hw_queues; + + et = kmalloc(sizeof(struct elevator_tags) + + nr_tags * sizeof(struct blk_mq_tags *), gfp); + if (!et) + return NULL; + /* + * Default to double of smaller one between hw queue_depth and + * 128, since we don't split into sync/async like the old code + * did. Additionally, this is a per-hw queue depth. + */ + et->nr_requests = 2 * min_t(unsigned int, set->queue_depth, + BLKDEV_DEFAULT_RQ); + et->nr_hw_queues = nr_hw_queues; + + if (blk_mq_is_shared_tags(set->flags)) { + /* Shared tags are stored at index 0 in @tags. */ + et->tags[0] = blk_mq_alloc_map_and_rqs(set, BLK_MQ_NO_HCTX_IDX, + MAX_SCHED_RQ); + if (!et->tags[0]) + goto out; + } else { + for (i = 0; i < et->nr_hw_queues; i++) { + et->tags[i] = blk_mq_alloc_map_and_rqs(set, i, + et->nr_requests); + if (!et->tags[i]) + goto out_unwind; + } + } + + return et; +out_unwind: + while (--i >= 0) + blk_mq_free_map_and_rqs(set, et->tags[i], i); +out: + kfree(et); + return NULL; +} + +int blk_mq_alloc_sched_tags_batch(struct xarray *et_table, + struct blk_mq_tag_set *set, unsigned int nr_hw_queues) +{ + struct request_queue *q; + struct elevator_tags *et; + gfp_t gfp = GFP_NOIO | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY; + + lockdep_assert_held_write(&set->update_nr_hwq_lock); + + list_for_each_entry(q, &set->tag_list, tag_set_list) { + /* + * Accessing q->elevator without holding q->elevator_lock is + * safe because we're holding here set->update_nr_hwq_lock in + * the writer context. So, scheduler update/switch code (which + * acquires the same lock but in the reader context) can't run + * concurrently. + */ + if (q->elevator) { + et = blk_mq_alloc_sched_tags(set, nr_hw_queues); + if (!et) + goto out_unwind; + if (xa_insert(et_table, q->id, et, gfp)) + goto out_free_tags; + } + } + return 0; +out_free_tags: + blk_mq_free_sched_tags(et, set); +out_unwind: + list_for_each_entry_continue_reverse(q, &set->tag_list, tag_set_list) { + if (q->elevator) { + et = xa_load(et_table, q->id); + if (et) + blk_mq_free_sched_tags(et, set); + } + } + return -ENOMEM; +} + /* caller must have a reference to @e, will grab another one if successful */ -int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) +int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e, + struct elevator_tags *et) { unsigned int flags = q->tag_set->flags; struct blk_mq_hw_ctx *hctx; @@ -467,36 +552,33 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) unsigned long i; int ret; - /* - * Default to double of smaller one between hw queue_depth and 128, - * since we don't split into sync/async like the old code did. - * Additionally, this is a per-hw queue depth. - */ - q->nr_requests = 2 * min_t(unsigned int, q->tag_set->queue_depth, - BLKDEV_DEFAULT_RQ); + eq = elevator_alloc(q, e, et); + if (!eq) + return -ENOMEM; + + q->nr_requests = et->nr_requests; if (blk_mq_is_shared_tags(flags)) { - ret = blk_mq_init_sched_shared_tags(q); - if (ret) - return ret; + /* Shared tags are stored at index 0 in @et->tags. */ + q->sched_shared_tags = et->tags[0]; + blk_mq_tag_update_sched_shared_tags(q); } queue_for_each_hw_ctx(q, hctx, i) { - ret = blk_mq_sched_alloc_map_and_rqs(q, hctx, i); - if (ret) - goto err_free_map_and_rqs; + if (blk_mq_is_shared_tags(flags)) + hctx->sched_tags = q->sched_shared_tags; + else + hctx->sched_tags = et->tags[i]; } - ret = e->ops.init_sched(q, e); + ret = e->ops.init_sched(q, eq); if (ret) - goto err_free_map_and_rqs; + goto out; queue_for_each_hw_ctx(q, hctx, i) { if (e->ops.init_hctx) { ret = e->ops.init_hctx(hctx, i); if (ret) { - eq = q->elevator; - blk_mq_sched_free_rqs(q); blk_mq_exit_sched(q, eq); kobject_put(&eq->kobj); return ret; @@ -505,10 +587,9 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) } return 0; -err_free_map_and_rqs: - blk_mq_sched_free_rqs(q); +out: blk_mq_sched_tags_teardown(q, flags); - + kobject_put(&eq->kobj); q->elevator = NULL; return ret; } diff --git a/block/blk-mq-sched.h b/block/blk-mq-sched.h index 1326526bb733..b554e1d55950 100644 --- a/block/blk-mq-sched.h +++ b/block/blk-mq-sched.h @@ -18,10 +18,20 @@ void __blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx); void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx); -int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e); +int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e, + struct elevator_tags *et); void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e); void blk_mq_sched_free_rqs(struct request_queue *q); +struct elevator_tags *blk_mq_alloc_sched_tags(struct blk_mq_tag_set *set, + unsigned int nr_hw_queues); +int blk_mq_alloc_sched_tags_batch(struct xarray *et_table, + struct blk_mq_tag_set *set, unsigned int nr_hw_queues); +void blk_mq_free_sched_tags(struct elevator_tags *et, + struct blk_mq_tag_set *set); +void blk_mq_free_sched_tags_batch(struct xarray *et_table, + struct blk_mq_tag_set *set); + static inline void blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx) { if (test_bit(BLK_MQ_S_SCHED_RESTART, &hctx->state)) diff --git a/block/blk-mq.c b/block/blk-mq.c index 4806b867e37d..ba3a4b77f578 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -883,7 +883,8 @@ static void blk_complete_request(struct request *req) /* Completion has already been traced */ bio_clear_flag(bio, BIO_TRACE_COMPLETION); - blk_zone_update_request_bio(req, bio); + if (blk_req_bio_is_zone_append(req, bio)) + blk_zone_append_update_request_bio(req, bio); if (!is_flush) bio_endio(bio); @@ -982,7 +983,8 @@ bool blk_update_request(struct request *req, blk_status_t error, /* Don't actually finish bio if it's part of flush sequence */ if (!bio->bi_iter.bi_size) { - blk_zone_update_request_bio(req, bio); + if (blk_req_bio_is_zone_append(req, bio)) + blk_zone_append_update_request_bio(req, bio); if (!is_flush) bio_endio(bio); } @@ -3169,8 +3171,10 @@ void blk_mq_submit_bio(struct bio *bio) if (blk_mq_attempt_bio_merge(q, bio, nr_segs)) goto queue_exit; - if (blk_queue_is_zoned(q) && blk_zone_plug_bio(bio, nr_segs)) - goto queue_exit; + if (bio_needs_zone_write_plugging(bio)) { + if (blk_zone_plug_bio(bio, nr_segs)) + goto queue_exit; + } new_request: if (rq) { @@ -4966,6 +4970,61 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr) return ret; } +/* + * Switch back to the elevator type stored in the xarray. + */ +static void blk_mq_elv_switch_back(struct request_queue *q, + struct xarray *elv_tbl, struct xarray *et_tbl) +{ + struct elevator_type *e = xa_load(elv_tbl, q->id); + struct elevator_tags *t = xa_load(et_tbl, q->id); + + /* The elv_update_nr_hw_queues unfreezes the queue. */ + elv_update_nr_hw_queues(q, e, t); + + /* Drop the reference acquired in blk_mq_elv_switch_none. */ + if (e) + elevator_put(e); +} + +/* + * Stores elevator type in xarray and set current elevator to none. It uses + * q->id as an index to store the elevator type into the xarray. + */ +static int blk_mq_elv_switch_none(struct request_queue *q, + struct xarray *elv_tbl) +{ + int ret = 0; + + lockdep_assert_held_write(&q->tag_set->update_nr_hwq_lock); + + /* + * Accessing q->elevator without holding q->elevator_lock is safe here + * because we're called from nr_hw_queue update which is protected by + * set->update_nr_hwq_lock in the writer context. So, scheduler update/ + * switch code (which acquires the same lock in the reader context) + * can't run concurrently. + */ + if (q->elevator) { + + ret = xa_insert(elv_tbl, q->id, q->elevator->type, GFP_KERNEL); + if (WARN_ON_ONCE(ret)) + return ret; + + /* + * Before we switch elevator to 'none', take a reference to + * the elevator module so that while nr_hw_queue update is + * running, no one can remove elevator module. We'd put the + * reference to elevator module later when we switch back + * elevator. + */ + __elevator_get(q->elevator->type); + + elevator_set_none(q); + } + return ret; +} + static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues) { @@ -4973,6 +5032,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int prev_nr_hw_queues = set->nr_hw_queues; unsigned int memflags; int i; + struct xarray elv_tbl, et_tbl; + bool queues_frozen = false; lockdep_assert_held(&set->tag_list_lock); @@ -4984,19 +5045,32 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, return; memflags = memalloc_noio_save(); + + xa_init(&et_tbl); + if (blk_mq_alloc_sched_tags_batch(&et_tbl, set, nr_hw_queues) < 0) + goto out_memalloc_restore; + + xa_init(&elv_tbl); + list_for_each_entry(q, &set->tag_list, tag_set_list) { blk_mq_debugfs_unregister_hctxs(q); blk_mq_sysfs_unregister_hctxs(q); } + /* + * Switch IO scheduler to 'none', cleaning up the data associated + * with the previous scheduler. We will switch back once we are done + * updating the new sw to hw queue mappings. + */ + list_for_each_entry(q, &set->tag_list, tag_set_list) + if (blk_mq_elv_switch_none(q, &elv_tbl)) + goto switch_back; + list_for_each_entry(q, &set->tag_list, tag_set_list) blk_mq_freeze_queue_nomemsave(q); - - if (blk_mq_realloc_tag_set_tags(set, nr_hw_queues) < 0) { - list_for_each_entry(q, &set->tag_list, tag_set_list) - blk_mq_unfreeze_queue_nomemrestore(q); - goto reregister; - } + queues_frozen = true; + if (blk_mq_realloc_tag_set_tags(set, nr_hw_queues) < 0) + goto switch_back; fallback: blk_mq_update_queue_map(set); @@ -5016,12 +5090,15 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, } blk_mq_map_swqueue(q); } +switch_back: + /* The blk_mq_elv_switch_back unfreezes queue for us. */ + list_for_each_entry(q, &set->tag_list, tag_set_list) { + /* switch_back expects queue to be frozen */ + if (!queues_frozen) + blk_mq_freeze_queue_nomemsave(q); + blk_mq_elv_switch_back(q, &elv_tbl, &et_tbl); + } - /* elv_update_nr_hw_queues() unfreeze queue for us */ - list_for_each_entry(q, &set->tag_list, tag_set_list) - elv_update_nr_hw_queues(q); - -reregister: list_for_each_entry(q, &set->tag_list, tag_set_list) { blk_mq_sysfs_register_hctxs(q); blk_mq_debugfs_register_hctxs(q); @@ -5029,6 +5106,10 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, blk_mq_remove_hw_queues_cpuhp(q); blk_mq_add_hw_queues_cpuhp(q); } + + xa_destroy(&elv_tbl); + xa_destroy(&et_tbl); +out_memalloc_restore: memalloc_noio_restore(memflags); /* Free the excess tags when nr_hw_queues shrink. */ diff --git a/block/blk-rq-qos.c b/block/blk-rq-qos.c index 848591fb3c57..654478dfbc20 100644 --- a/block/blk-rq-qos.c +++ b/block/blk-rq-qos.c @@ -2,8 +2,6 @@ #include "blk-rq-qos.h" -__read_mostly DEFINE_STATIC_KEY_FALSE(block_rq_qos); - /* * Increment 'v', if 'v' is below 'below'. Returns true if we succeeded, * false if 'v' + 1 would be bigger than 'below'. @@ -319,8 +317,8 @@ void rq_qos_exit(struct request_queue *q) struct rq_qos *rqos = q->rq_qos; q->rq_qos = rqos->next; rqos->ops->exit(rqos); - static_branch_dec(&block_rq_qos); } + blk_queue_flag_clear(QUEUE_FLAG_QOS_ENABLED, q); mutex_unlock(&q->rq_qos_mutex); } @@ -346,7 +344,7 @@ int rq_qos_add(struct rq_qos *rqos, struct gendisk *disk, enum rq_qos_id id, goto ebusy; rqos->next = q->rq_qos; q->rq_qos = rqos; - static_branch_inc(&block_rq_qos); + blk_queue_flag_set(QUEUE_FLAG_QOS_ENABLED, q); blk_mq_unfreeze_queue(q, memflags); @@ -377,6 +375,8 @@ void rq_qos_del(struct rq_qos *rqos) break; } } + if (!q->rq_qos) + blk_queue_flag_clear(QUEUE_FLAG_QOS_ENABLED, q); blk_mq_unfreeze_queue(q, memflags); mutex_lock(&q->debugfs_mutex); diff --git a/block/blk-rq-qos.h b/block/blk-rq-qos.h index 39749f4066fb..b538f2c0febc 100644 --- a/block/blk-rq-qos.h +++ b/block/blk-rq-qos.h @@ -12,7 +12,6 @@ #include "blk-mq-debugfs.h" struct blk_mq_debugfs_attr; -extern struct static_key_false block_rq_qos; enum rq_qos_id { RQ_QOS_WBT, @@ -113,43 +112,58 @@ void __rq_qos_queue_depth_changed(struct rq_qos *rqos); static inline void rq_qos_cleanup(struct request_queue *q, struct bio *bio) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) __rq_qos_cleanup(q->rq_qos, bio); } static inline void rq_qos_done(struct request_queue *q, struct request *rq) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos && - !blk_rq_is_passthrough(rq)) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos && !blk_rq_is_passthrough(rq)) __rq_qos_done(q->rq_qos, rq); } static inline void rq_qos_issue(struct request_queue *q, struct request *rq) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) __rq_qos_issue(q->rq_qos, rq); } static inline void rq_qos_requeue(struct request_queue *q, struct request *rq) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) __rq_qos_requeue(q->rq_qos, rq); } static inline void rq_qos_done_bio(struct bio *bio) { - if (static_branch_unlikely(&block_rq_qos) && - bio->bi_bdev && (bio_flagged(bio, BIO_QOS_THROTTLED) || - bio_flagged(bio, BIO_QOS_MERGED))) { - struct request_queue *q = bdev_get_queue(bio->bi_bdev); - if (q->rq_qos) - __rq_qos_done_bio(q->rq_qos, bio); - } + struct request_queue *q; + + if (!bio->bi_bdev || (!bio_flagged(bio, BIO_QOS_THROTTLED) && + !bio_flagged(bio, BIO_QOS_MERGED))) + return; + + q = bdev_get_queue(bio->bi_bdev); + + /* + * A BIO may carry BIO_QOS_* flags even if the associated request_queue + * does not have rq_qos enabled. This can happen with stacked block + * devices — for example, NVMe multipath, where it's possible that the + * bottom device has QoS enabled but the top device does not. Therefore, + * always verify that q->rq_qos is present and QoS is enabled before + * calling __rq_qos_done_bio(). + */ + if (test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags) && q->rq_qos) + __rq_qos_done_bio(q->rq_qos, bio); } static inline void rq_qos_throttle(struct request_queue *q, struct bio *bio) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) { + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) { bio_set_flag(bio, BIO_QOS_THROTTLED); __rq_qos_throttle(q->rq_qos, bio); } @@ -158,14 +172,16 @@ static inline void rq_qos_throttle(struct request_queue *q, struct bio *bio) static inline void rq_qos_track(struct request_queue *q, struct request *rq, struct bio *bio) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) __rq_qos_track(q->rq_qos, rq, bio); } static inline void rq_qos_merge(struct request_queue *q, struct request *rq, struct bio *bio) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) { + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) { bio_set_flag(bio, BIO_QOS_MERGED); __rq_qos_merge(q->rq_qos, rq, bio); } @@ -173,7 +189,8 @@ static inline void rq_qos_merge(struct request_queue *q, struct request *rq, static inline void rq_qos_queue_depth_changed(struct request_queue *q) { - if (static_branch_unlikely(&block_rq_qos) && q->rq_qos) + if (unlikely(test_bit(QUEUE_FLAG_QOS_ENABLED, &q->queue_flags)) && + q->rq_qos) __rq_qos_queue_depth_changed(q->rq_qos); } diff --git a/block/blk-settings.c b/block/blk-settings.c index a000daafbfb4..d6438e6c276d 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "blk.h" #include "blk-rq-qos.h" @@ -50,6 +52,8 @@ void blk_set_stacking_limits(struct queue_limits *lim) lim->max_sectors = UINT_MAX; lim->max_dev_sectors = UINT_MAX; lim->max_write_zeroes_sectors = UINT_MAX; + lim->max_hw_wzeroes_unmap_sectors = UINT_MAX; + lim->max_user_wzeroes_unmap_sectors = UINT_MAX; lim->max_hw_zone_append_sectors = UINT_MAX; lim->max_user_discard_sectors = UINT_MAX; } @@ -58,16 +62,24 @@ EXPORT_SYMBOL(blk_set_stacking_limits); void blk_apply_bdi_limits(struct backing_dev_info *bdi, struct queue_limits *lim) { + u64 io_opt = lim->io_opt; + /* * For read-ahead of large files to be effective, we need to read ahead - * at least twice the optimal I/O size. + * at least twice the optimal I/O size. For rotational devices that do + * not report an optimal I/O size (e.g. ATA HDDs), use the maximum I/O + * size to avoid falling back to the (rather inefficient) small default + * read-ahead size. * * There is no hardware limitation for the read-ahead size and the user * might have increased the read-ahead size through sysfs, so don't ever * decrease it. */ + if (!io_opt && (lim->features & BLK_FEAT_ROTATIONAL)) + io_opt = (u64)lim->max_sectors << SECTOR_SHIFT; + bdi->ra_pages = max3(bdi->ra_pages, - lim->io_opt * 2 / PAGE_SIZE, + io_opt * 2 >> PAGE_SHIFT, VM_READAHEAD_PAGES); bdi->io_pages = lim->max_sectors >> PAGE_SECTORS_SHIFT; } @@ -114,7 +126,7 @@ static int blk_validate_integrity_limits(struct queue_limits *lim) { struct blk_integrity *bi = &lim->integrity; - if (!bi->tuple_size) { + if (!bi->metadata_size) { if (bi->csum_type != BLK_INTEGRITY_CSUM_NONE || bi->tag_size || ((bi->flags & BLK_INTEGRITY_REF_TAG))) { pr_warn("invalid PI settings.\n"); @@ -135,6 +147,39 @@ static int blk_validate_integrity_limits(struct queue_limits *lim) return -EINVAL; } + if (bi->pi_tuple_size > bi->metadata_size) { + pr_warn("pi_tuple_size (%u) exceeds metadata_size (%u)\n", + bi->pi_tuple_size, + bi->metadata_size); + return -EINVAL; + } + + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_NONE: + if (bi->pi_tuple_size) { + pr_warn("pi_tuple_size must be 0 when checksum type is none\n"); + return -EINVAL; + } + break; + case BLK_INTEGRITY_CSUM_CRC: + case BLK_INTEGRITY_CSUM_IP: + if (bi->pi_tuple_size != sizeof(struct t10_pi_tuple)) { + pr_warn("pi_tuple_size mismatch for T10 PI: expected %zu, got %u\n", + sizeof(struct t10_pi_tuple), + bi->pi_tuple_size); + return -EINVAL; + } + break; + case BLK_INTEGRITY_CSUM_CRC64: + if (bi->pi_tuple_size != sizeof(struct crc64_pi_tuple)) { + pr_warn("pi_tuple_size mismatch for CRC64 PI: expected %zu, got %u\n", + sizeof(struct crc64_pi_tuple), + bi->pi_tuple_size); + return -EINVAL; + } + break; + } + if (!bi->interval_exp) bi->interval_exp = ilog2(lim->logical_block_size); @@ -181,6 +226,8 @@ static void blk_atomic_writes_update_limits(struct queue_limits *lim) static void blk_validate_atomic_write_limits(struct queue_limits *lim) { unsigned int boundary_sectors; + unsigned int atomic_write_hw_max_sectors = + lim->atomic_write_hw_max >> SECTOR_SHIFT; if (!(lim->features & BLK_FEAT_ATOMIC_WRITES)) goto unsupported; @@ -202,6 +249,10 @@ static void blk_validate_atomic_write_limits(struct queue_limits *lim) lim->atomic_write_hw_max)) goto unsupported; + if (WARN_ON_ONCE(lim->chunk_sectors && + atomic_write_hw_max_sectors > lim->chunk_sectors)) + goto unsupported; + boundary_sectors = lim->atomic_write_hw_boundary >> SECTOR_SHIFT; if (boundary_sectors) { @@ -266,8 +317,12 @@ int blk_validate_limits(struct queue_limits *lim) pr_warn("Invalid logical block size (%d)\n", lim->logical_block_size); return -EINVAL; } - if (lim->physical_block_size < lim->logical_block_size) + if (lim->physical_block_size < lim->logical_block_size) { lim->physical_block_size = lim->logical_block_size; + } else if (!is_power_of_2(lim->physical_block_size)) { + pr_warn("Invalid physical block size (%d)\n", lim->physical_block_size); + return -EINVAL; + } /* * The minimum I/O size defaults to the physical block size unless @@ -333,15 +388,28 @@ int blk_validate_limits(struct queue_limits *lim) if (!lim->max_segments) lim->max_segments = BLK_MAX_SEGMENTS; + if (lim->max_hw_wzeroes_unmap_sectors && + lim->max_hw_wzeroes_unmap_sectors != lim->max_write_zeroes_sectors) + return -EINVAL; + lim->max_wzeroes_unmap_sectors = min(lim->max_hw_wzeroes_unmap_sectors, + lim->max_user_wzeroes_unmap_sectors); + lim->max_discard_sectors = min(lim->max_hw_discard_sectors, lim->max_user_discard_sectors); + /* + * When discard is not supported, discard_granularity should be reported + * as 0 to userspace. + */ + if (lim->max_discard_sectors) + lim->discard_granularity = + max(lim->discard_granularity, lim->physical_block_size); + else + lim->discard_granularity = 0; + if (!lim->max_discard_segments) lim->max_discard_segments = 1; - if (lim->discard_granularity < lim->physical_block_size) - lim->discard_granularity = lim->physical_block_size; - /* * By default there is no limit on the segment boundary alignment, * but if there is one it can't be smaller than the page size as @@ -418,10 +486,11 @@ int blk_set_default_limits(struct queue_limits *lim) { /* * Most defaults are set by capping the bounds in blk_validate_limits, - * but max_user_discard_sectors is special and needs an explicit - * initialization to the max value here. + * but these limits are special and need an explicit initialization to + * the max value here. */ lim->max_user_discard_sectors = UINT_MAX; + lim->max_user_wzeroes_unmap_sectors = UINT_MAX; return blk_validate_limits(lim); } @@ -589,6 +658,38 @@ static bool blk_stack_atomic_writes_boundary_head(struct queue_limits *t, return true; } +static void blk_stack_atomic_writes_chunk_sectors(struct queue_limits *t) +{ + unsigned int chunk_bytes; + + if (!t->chunk_sectors) + return; + + /* + * If chunk sectors is so large that its value in bytes overflows + * UINT_MAX, then just shift it down so it definitely will fit. + * We don't support atomic writes of such a large size anyway. + */ + if (check_shl_overflow(t->chunk_sectors, SECTOR_SHIFT, &chunk_bytes)) + chunk_bytes = t->chunk_sectors; + + /* + * Find values for limits which work for chunk size. + * b->atomic_write_hw_unit_{min, max} may not be aligned with chunk + * size, as the chunk size is not restricted to a power-of-2. + * So we need to find highest power-of-2 which works for the chunk + * size. + * As an example scenario, we could have t->unit_max = 16K and + * t->chunk_sectors = 24KB. For this case, reduce t->unit_max to a + * value aligned with both limits, i.e. 8K in this example. + */ + t->atomic_write_hw_unit_max = min(t->atomic_write_hw_unit_max, + max_pow_of_two_factor(chunk_bytes)); + + t->atomic_write_hw_unit_min = min(t->atomic_write_hw_unit_min, + t->atomic_write_hw_unit_max); + t->atomic_write_hw_max = min(t->atomic_write_hw_max, chunk_bytes); +} /* Check stacking of first bottom device */ static bool blk_stack_atomic_writes_head(struct queue_limits *t, @@ -598,32 +699,9 @@ static bool blk_stack_atomic_writes_head(struct queue_limits *t, !blk_stack_atomic_writes_boundary_head(t, b)) return false; - if (t->io_min <= SECTOR_SIZE) { - /* No chunk sectors, so use bottom device values directly */ - t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; - t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min; - t->atomic_write_hw_max = b->atomic_write_hw_max; - return true; - } - - /* - * Find values for limits which work for chunk size. - * b->atomic_write_hw_unit_{min, max} may not be aligned with chunk - * size (t->io_min), as chunk size is not restricted to a power-of-2. - * So we need to find highest power-of-2 which works for the chunk - * size. - * As an example scenario, we could have b->unit_max = 16K and - * t->io_min = 24K. For this case, reduce t->unit_max to a value - * aligned with both limits, i.e. 8K in this example. - */ t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; - while (t->io_min % t->atomic_write_hw_unit_max) - t->atomic_write_hw_unit_max /= 2; - - t->atomic_write_hw_unit_min = min(b->atomic_write_hw_unit_min, - t->atomic_write_hw_unit_max); - t->atomic_write_hw_max = min(b->atomic_write_hw_max, t->io_min); - + t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min; + t->atomic_write_hw_max = b->atomic_write_hw_max; return true; } @@ -651,6 +729,7 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t, if (!blk_stack_atomic_writes_head(t, b)) goto unsupported; + blk_stack_atomic_writes_chunk_sectors(t); return; unsupported: @@ -708,6 +787,13 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, t->max_dev_sectors = min_not_zero(t->max_dev_sectors, b->max_dev_sectors); t->max_write_zeroes_sectors = min(t->max_write_zeroes_sectors, b->max_write_zeroes_sectors); + t->max_user_wzeroes_unmap_sectors = + min(t->max_user_wzeroes_unmap_sectors, + b->max_user_wzeroes_unmap_sectors); + t->max_hw_wzeroes_unmap_sectors = + min(t->max_hw_wzeroes_unmap_sectors, + b->max_hw_wzeroes_unmap_sectors); + t->max_hw_zone_append_sectors = min(t->max_hw_zone_append_sectors, b->max_hw_zone_append_sectors); @@ -779,7 +865,7 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, } /* chunk_sectors a multiple of the physical block size? */ - if ((t->chunk_sectors << 9) & (t->physical_block_size - 1)) { + if (t->chunk_sectors % (t->physical_block_size >> SECTOR_SHIFT)) { t->chunk_sectors = 0; t->flags |= BLK_FLAG_MISALIGNED; ret = -1; @@ -875,7 +961,7 @@ bool queue_limits_stack_integrity(struct queue_limits *t, return true; if (ti->flags & BLK_INTEGRITY_STACKED) { - if (ti->tuple_size != bi->tuple_size) + if (ti->metadata_size != bi->metadata_size) goto incompatible; if (ti->interval_exp != bi->interval_exp) goto incompatible; @@ -883,6 +969,8 @@ bool queue_limits_stack_integrity(struct queue_limits *t, goto incompatible; if (ti->csum_type != bi->csum_type) goto incompatible; + if (ti->pi_tuple_size != bi->pi_tuple_size) + goto incompatible; if ((ti->flags & BLK_INTEGRITY_REF_TAG) != (bi->flags & BLK_INTEGRITY_REF_TAG)) goto incompatible; @@ -891,7 +979,8 @@ bool queue_limits_stack_integrity(struct queue_limits *t, ti->flags |= (bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE) | (bi->flags & BLK_INTEGRITY_REF_TAG); ti->csum_type = bi->csum_type; - ti->tuple_size = bi->tuple_size; + ti->pi_tuple_size = bi->pi_tuple_size; + ti->metadata_size = bi->metadata_size; ti->pi_offset = bi->pi_offset; ti->interval_exp = bi->interval_exp; ti->tag_size = bi->tag_size; diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index b2b9b89d6967..4a7f1a349998 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -161,6 +161,8 @@ static ssize_t queue_##_field##_show(struct gendisk *disk, char *page) \ QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_discard_sectors) QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_hw_discard_sectors) QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_write_zeroes_sectors) +QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_hw_wzeroes_unmap_sectors) +QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_wzeroes_unmap_sectors) QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(atomic_write_max_sectors) QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(atomic_write_boundary_sectors) QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_zone_append_sectors) @@ -205,6 +207,24 @@ static int queue_max_discard_sectors_store(struct gendisk *disk, return 0; } +static int queue_max_wzeroes_unmap_sectors_store(struct gendisk *disk, + const char *page, size_t count, struct queue_limits *lim) +{ + unsigned long max_zeroes_bytes, max_hw_zeroes_bytes; + ssize_t ret; + + ret = queue_var_store(&max_zeroes_bytes, page, count); + if (ret < 0) + return ret; + + max_hw_zeroes_bytes = lim->max_hw_wzeroes_unmap_sectors << SECTOR_SHIFT; + if (max_zeroes_bytes != 0 && max_zeroes_bytes != max_hw_zeroes_bytes) + return -EINVAL; + + lim->max_user_wzeroes_unmap_sectors = max_zeroes_bytes >> SECTOR_SHIFT; + return 0; +} + static int queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count, struct queue_limits *lim) @@ -514,6 +534,10 @@ QUEUE_LIM_RO_ENTRY(queue_atomic_write_unit_min, "atomic_write_unit_min_bytes"); QUEUE_RO_ENTRY(queue_write_same_max, "write_same_max_bytes"); QUEUE_LIM_RO_ENTRY(queue_max_write_zeroes_sectors, "write_zeroes_max_bytes"); +QUEUE_LIM_RO_ENTRY(queue_max_hw_wzeroes_unmap_sectors, + "write_zeroes_unmap_max_hw_bytes"); +QUEUE_LIM_RW_ENTRY(queue_max_wzeroes_unmap_sectors, + "write_zeroes_unmap_max_bytes"); QUEUE_LIM_RO_ENTRY(queue_max_zone_append_sectors, "zone_append_max_bytes"); QUEUE_LIM_RO_ENTRY(queue_zone_write_granularity, "zone_write_granularity"); @@ -662,6 +686,8 @@ static struct attribute *queue_attrs[] = { &queue_atomic_write_unit_min_entry.attr, &queue_atomic_write_unit_max_entry.attr, &queue_max_write_zeroes_sectors_entry.attr, + &queue_max_hw_wzeroes_unmap_sectors_entry.attr, + &queue_max_wzeroes_unmap_sectors_entry.attr, &queue_max_zone_append_sectors_entry.attr, &queue_zone_write_granularity_entry.attr, &queue_rotational_entry.attr, @@ -821,7 +847,7 @@ static void blk_queue_release(struct kobject *kobj) /* nothing to do here, all data is associated with the parent gendisk */ } -static const struct kobj_type blk_queue_ktype = { +const struct kobj_type blk_queue_ktype = { .default_groups = blk_queue_attr_groups, .sysfs_ops = &queue_sysfs_ops, .release = blk_queue_release, @@ -849,15 +875,14 @@ int blk_register_queue(struct gendisk *disk) struct request_queue *q = disk->queue; int ret; - kobject_init(&disk->queue_kobj, &blk_queue_ktype); ret = kobject_add(&disk->queue_kobj, &disk_to_dev(disk)->kobj, "queue"); if (ret < 0) - goto out_put_queue_kobj; + return ret; if (queue_is_mq(q)) { ret = blk_mq_sysfs_register(disk); if (ret) - goto out_put_queue_kobj; + goto out_del_queue_kobj; } mutex_lock(&q->sysfs_lock); @@ -877,9 +902,9 @@ int blk_register_queue(struct gendisk *disk) if (queue_is_mq(q)) elevator_set_default(q); - wbt_enable_default(disk); blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); + wbt_enable_default(disk); /* Now everything is ready and send out KOBJ_ADD uevent */ kobject_uevent(&disk->queue_kobj, KOBJ_ADD); @@ -908,8 +933,8 @@ int blk_register_queue(struct gendisk *disk) mutex_unlock(&q->sysfs_lock); if (queue_is_mq(q)) blk_mq_sysfs_unregister(disk); -out_put_queue_kobj: - kobject_put(&disk->queue_kobj); +out_del_queue_kobj: + kobject_del(&disk->queue_kobj); return ret; } diff --git a/block/blk-wbt.c b/block/blk-wbt.c index a50d4cd55f41..eb8037bae0bd 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -85,8 +85,8 @@ struct rq_wb { u64 sync_issue; void *sync_cookie; - unsigned long last_issue; /* last non-throttled issue */ - unsigned long last_comp; /* last non-throttled comp */ + unsigned long last_issue; /* issue time of last read rq */ + unsigned long last_comp; /* completion time of last read rq */ unsigned long min_lat_nsec; struct rq_qos rqos; struct rq_wait rq_wait[WBT_NUM_RWQ]; @@ -248,13 +248,14 @@ static void wbt_done(struct rq_qos *rqos, struct request *rq) struct rq_wb *rwb = RQWB(rqos); if (!wbt_is_tracked(rq)) { - if (rwb->sync_cookie == rq) { - rwb->sync_issue = 0; - rwb->sync_cookie = NULL; - } + if (wbt_is_read(rq)) { + if (rwb->sync_cookie == rq) { + rwb->sync_issue = 0; + rwb->sync_cookie = NULL; + } - if (wbt_is_read(rq)) wb_timestamp(rwb, &rwb->last_comp); + } } else { WARN_ON_ONCE(rq == rwb->sync_cookie); __wbt_done(rqos, wbt_flags(rq)); diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 351d659280e1..5e2a5788dc3b 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -17,6 +17,8 @@ #include #include +#include + #include "blk.h" #include "blk-mq-sched.h" #include "blk-mq-debugfs.h" @@ -177,6 +179,7 @@ static int blkdev_zone_reset_all(struct block_device *bdev) struct bio bio; bio_init(&bio, bdev, NULL, 0, REQ_OP_ZONE_RESET_ALL | REQ_SYNC); + trace_blkdev_zone_mgmt(&bio, 0); return submit_bio_wait(&bio); } @@ -240,6 +243,7 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_op op, cond_resched(); } + trace_blkdev_zone_mgmt(bio, nr_sectors); ret = submit_bio_wait(bio); bio_put(bio); @@ -818,6 +822,8 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk, * at the tail of the list to preserve the sequential write order. */ bio_list_add(&zwplug->bio_list, bio); + trace_disk_zone_wplug_add_bio(zwplug->disk->queue, zwplug->zone_no, + bio->bi_iter.bi_sector, bio_sectors(bio)); zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED; @@ -1116,25 +1122,7 @@ bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs) { struct block_device *bdev = bio->bi_bdev; - if (!bdev->bd_disk->zone_wplugs_hash) - return false; - - /* - * If the BIO already has the plugging flag set, then it was already - * handled through this path and this is a submission from the zone - * plug bio submit work. - */ - if (bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING)) - return false; - - /* - * We do not need to do anything special for empty flush BIOs, e.g - * BIOs such as issued by blkdev_issue_flush(). The is because it is - * the responsibility of the user to first wait for the completion of - * write operations for flush to have any effect on the persistence of - * the written data. - */ - if (op_is_flush(bio->bi_opf) && !bio_sectors(bio)) + if (WARN_ON_ONCE(!bdev->bd_disk->zone_wplugs_hash)) return false; /* @@ -1205,6 +1193,20 @@ static void disk_zone_wplug_unplug_bio(struct gendisk *disk, spin_unlock_irqrestore(&zwplug->lock, flags); } +void blk_zone_append_update_request_bio(struct request *rq, struct bio *bio) +{ + /* + * For zone append requests, the request sector indicates the location + * at which the BIO data was written. Return this value to the BIO + * issuer through the BIO iter sector. + * For plugged zone writes, which include emulated zone append, we need + * the original BIO sector so that blk_zone_write_plug_bio_endio() can + * lookup the zone write plug. + */ + bio->bi_iter.bi_sector = rq->__sector; + trace_blk_zone_append_update_request_bio(rq); +} + void blk_zone_write_plug_bio_endio(struct bio *bio) { struct gendisk *disk = bio->bi_bdev->bd_disk; @@ -1284,14 +1286,14 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) struct block_device *bdev; unsigned long flags; struct bio *bio; + bool prepared; /* * Submit the next plugged BIO. If we do not have any, clear * the plugged flag. */ - spin_lock_irqsave(&zwplug->lock, flags); - again: + spin_lock_irqsave(&zwplug->lock, flags); bio = bio_list_pop(&zwplug->bio_list); if (!bio) { zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; @@ -1299,13 +1301,17 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) goto put_zwplug; } - if (!blk_zone_wplug_prepare_bio(zwplug, bio)) { + trace_blk_zone_wplug_bio(zwplug->disk->queue, zwplug->zone_no, + bio->bi_iter.bi_sector, bio_sectors(bio)); + + prepared = blk_zone_wplug_prepare_bio(zwplug, bio); + spin_unlock_irqrestore(&zwplug->lock, flags); + + if (!prepared) { blk_zone_wplug_bio_io_error(zwplug, bio); goto again; } - spin_unlock_irqrestore(&zwplug->lock, flags); - bdev = bio->bi_bdev; /* diff --git a/block/blk.h b/block/blk.h index 37ec459fe656..46f566f9b126 100644 --- a/block/blk.h +++ b/block/blk.h @@ -12,6 +12,16 @@ #include "blk-crypto-internal.h" struct elevator_type; +struct elevator_tags; + +/* + * Default upper limit for the software max_sectors limit used for regular I/Os. + * This can be increased through sysfs. + * + * This should not be confused with the max_hw_sector limit that is entirely + * controlled by the block device driver, usually based on hardware limits. + */ +#define BLK_DEF_MAX_SECTORS_CAP (SZ_4M >> SECTOR_SHIFT) #define BLK_DEV_MAX_SECTORS (LLONG_MAX >> 9) #define BLK_MIN_SEGMENT_SIZE 4096 @@ -19,6 +29,7 @@ struct elevator_type; /* Max future timer expiry for timeouts */ #define BLK_MAX_TIMEOUT (5 * HZ) +extern const struct kobj_type blk_queue_ktype; extern struct dentry *blk_debugfs_root; struct blk_flush_queue { @@ -321,7 +332,8 @@ bool blk_bio_list_merge(struct request_queue *q, struct list_head *list, bool blk_insert_flush(struct request *rq); -void elv_update_nr_hw_queues(struct request_queue *q); +void elv_update_nr_hw_queues(struct request_queue *q, struct elevator_type *e, + struct elevator_tags *t); void elevator_set_default(struct request_queue *q); void elevator_set_none(struct request_queue *q); @@ -467,23 +479,15 @@ static inline bool bio_zone_write_plugging(struct bio *bio) { return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING); } +static inline bool blk_req_bio_is_zone_append(struct request *rq, + struct bio *bio) +{ + return req_op(rq) == REQ_OP_ZONE_APPEND || + bio_flagged(bio, BIO_EMULATES_ZONE_APPEND); +} void blk_zone_write_plug_bio_merged(struct bio *bio); void blk_zone_write_plug_init_request(struct request *rq); -static inline void blk_zone_update_request_bio(struct request *rq, - struct bio *bio) -{ - /* - * For zone append requests, the request sector indicates the location - * at which the BIO data was written. Return this value to the BIO - * issuer through the BIO iter sector. - * For plugged zone writes, which include emulated zone append, we need - * the original BIO sector so that blk_zone_write_plug_bio_endio() can - * lookup the zone write plug. - */ - if (req_op(rq) == REQ_OP_ZONE_APPEND || - bio_flagged(bio, BIO_EMULATES_ZONE_APPEND)) - bio->bi_iter.bi_sector = rq->__sector; -} +void blk_zone_append_update_request_bio(struct request *rq, struct bio *bio); void blk_zone_write_plug_bio_endio(struct bio *bio); static inline void blk_zone_bio_endio(struct bio *bio) { @@ -516,14 +520,19 @@ static inline bool bio_zone_write_plugging(struct bio *bio) { return false; } +static inline bool blk_req_bio_is_zone_append(struct request *req, + struct bio *bio) +{ + return false; +} static inline void blk_zone_write_plug_bio_merged(struct bio *bio) { } static inline void blk_zone_write_plug_init_request(struct request *rq) { } -static inline void blk_zone_update_request_bio(struct request *rq, - struct bio *bio) +static inline void blk_zone_append_update_request_bio(struct request *rq, + struct bio *bio) { } static inline void blk_zone_bio_endio(struct bio *bio) diff --git a/block/elevator.c b/block/elevator.c index ab22542e6cf0..fe96c6f4753c 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -54,6 +54,8 @@ struct elv_change_ctx { struct elevator_queue *old; /* for registering new elevator */ struct elevator_queue *new; + /* holds sched tags data */ + struct elevator_tags *et; }; static DEFINE_SPINLOCK(elv_list_lock); @@ -132,7 +134,7 @@ static struct elevator_type *elevator_find_get(const char *name) static const struct kobj_type elv_ktype; struct elevator_queue *elevator_alloc(struct request_queue *q, - struct elevator_type *e) + struct elevator_type *e, struct elevator_tags *et) { struct elevator_queue *eq; @@ -145,10 +147,10 @@ struct elevator_queue *elevator_alloc(struct request_queue *q, kobject_init(&eq->kobj, &elv_ktype); mutex_init(&eq->sysfs_lock); hash_init(eq->hash); + eq->et = et; return eq; } -EXPORT_SYMBOL(elevator_alloc); static void elevator_release(struct kobject *kobj) { @@ -166,7 +168,6 @@ static void elevator_exit(struct request_queue *q) lockdep_assert_held(&q->elevator_lock); ioc_clear_queue(q); - blk_mq_sched_free_rqs(q); mutex_lock(&e->sysfs_lock); blk_mq_exit_sched(q, e); @@ -592,7 +593,7 @@ static int elevator_switch(struct request_queue *q, struct elv_change_ctx *ctx) } if (new_e) { - ret = blk_mq_init_sched(q, new_e); + ret = blk_mq_init_sched(q, new_e, ctx->et); if (ret) goto out_unfreeze; ctx->new = q->elevator; @@ -627,8 +628,10 @@ static void elv_exit_and_release(struct request_queue *q) elevator_exit(q); mutex_unlock(&q->elevator_lock); blk_mq_unfreeze_queue(q, memflags); - if (e) + if (e) { + blk_mq_free_sched_tags(e->et, q->tag_set); kobject_put(&e->kobj); + } } static int elevator_change_done(struct request_queue *q, @@ -641,6 +644,7 @@ static int elevator_change_done(struct request_queue *q, &ctx->old->flags); elv_unregister_queue(q, ctx->old); + blk_mq_free_sched_tags(ctx->old->et, q->tag_set); kobject_put(&ctx->old->kobj); if (enable_wbt) wbt_enable_default(q->disk); @@ -659,9 +663,16 @@ static int elevator_change_done(struct request_queue *q, static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx) { unsigned int memflags; + struct blk_mq_tag_set *set = q->tag_set; int ret = 0; - lockdep_assert_held(&q->tag_set->update_nr_hwq_lock); + lockdep_assert_held(&set->update_nr_hwq_lock); + + if (strncmp(ctx->name, "none", 4)) { + ctx->et = blk_mq_alloc_sched_tags(set, set->nr_hw_queues); + if (!ctx->et) + return -ENOMEM; + } memflags = blk_mq_freeze_queue(q); /* @@ -681,6 +692,11 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx) blk_mq_unfreeze_queue(q, memflags); if (!ret) ret = elevator_change_done(q, ctx); + /* + * Free sched tags if it's allocated but we couldn't switch elevator. + */ + if (ctx->et && !ctx->new) + blk_mq_free_sched_tags(ctx->et, set); return ret; } @@ -689,24 +705,32 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx) * The I/O scheduler depends on the number of hardware queues, this forces a * reattachment when nr_hw_queues changes. */ -void elv_update_nr_hw_queues(struct request_queue *q) +void elv_update_nr_hw_queues(struct request_queue *q, struct elevator_type *e, + struct elevator_tags *t) { + struct blk_mq_tag_set *set = q->tag_set; struct elv_change_ctx ctx = {}; int ret = -ENODEV; WARN_ON_ONCE(q->mq_freeze_depth == 0); - mutex_lock(&q->elevator_lock); - if (q->elevator && !blk_queue_dying(q) && blk_queue_registered(q)) { - ctx.name = q->elevator->type->elevator_name; + if (e && !blk_queue_dying(q) && blk_queue_registered(q)) { + ctx.name = e->elevator_name; + ctx.et = t; + mutex_lock(&q->elevator_lock); /* force to reattach elevator after nr_hw_queue is updated */ ret = elevator_switch(q, &ctx); + mutex_unlock(&q->elevator_lock); } - mutex_unlock(&q->elevator_lock); blk_mq_unfreeze_queue_nomemrestore(q); if (!ret) WARN_ON_ONCE(elevator_change_done(q, &ctx)); + /* + * Free sched tags if it's allocated but we couldn't switch elevator. + */ + if (t && !ctx.new) + blk_mq_free_sched_tags(t, set); } /* @@ -719,7 +743,8 @@ void elevator_set_default(struct request_queue *q) .name = "mq-deadline", .no_uevent = true, }; - int err = 0; + int err; + struct elevator_type *e; /* now we allow to switch elevator */ blk_queue_flag_clear(QUEUE_FLAG_NO_ELV_SWITCH, q); @@ -732,12 +757,18 @@ void elevator_set_default(struct request_queue *q) * have multiple queues or mq-deadline is not available, default * to "none". */ - if (elevator_find_get(ctx.name) && (q->nr_hw_queues == 1 || - blk_mq_is_shared_tags(q->tag_set->flags))) + e = elevator_find_get(ctx.name); + if (!e) + return; + + if ((q->nr_hw_queues == 1 || + blk_mq_is_shared_tags(q->tag_set->flags))) { err = elevator_change(q, &ctx); - if (err < 0) - pr_warn("\"%s\" elevator initialization, failed %d, " - "falling back to \"none\"\n", ctx.name, err); + if (err < 0) + pr_warn("\"%s\" elevator initialization, failed %d, falling back to \"none\"\n", + ctx.name, err); + } + elevator_put(e); } void elevator_set_none(struct request_queue *q) diff --git a/block/elevator.h b/block/elevator.h index a07ce773a38f..adc5c157e17e 100644 --- a/block/elevator.h +++ b/block/elevator.h @@ -23,8 +23,17 @@ enum elv_merge { struct blk_mq_alloc_data; struct blk_mq_hw_ctx; +struct elevator_tags { + /* num. of hardware queues for which tags are allocated */ + unsigned int nr_hw_queues; + /* depth used while allocating tags */ + unsigned int nr_requests; + /* shared tag is stored at index 0 */ + struct blk_mq_tags *tags[]; +}; + struct elevator_mq_ops { - int (*init_sched)(struct request_queue *, struct elevator_type *); + int (*init_sched)(struct request_queue *, struct elevator_queue *); void (*exit_sched)(struct elevator_queue *); int (*init_hctx)(struct blk_mq_hw_ctx *, unsigned int); void (*exit_hctx)(struct blk_mq_hw_ctx *, unsigned int); @@ -113,6 +122,7 @@ struct request *elv_rqhash_find(struct request_queue *q, sector_t offset); struct elevator_queue { struct elevator_type *type; + struct elevator_tags *et; void *elevator_data; struct kobject kobj; struct mutex sysfs_lock; @@ -152,8 +162,8 @@ ssize_t elv_iosched_show(struct gendisk *disk, char *page); ssize_t elv_iosched_store(struct gendisk *disk, const char *page, size_t count); extern bool elv_bio_merge_ok(struct request *, struct bio *); -extern struct elevator_queue *elevator_alloc(struct request_queue *, - struct elevator_type *); +struct elevator_queue *elevator_alloc(struct request_queue *, + struct elevator_type *, struct elevator_tags *); /* * Helper functions. diff --git a/block/fops.c b/block/fops.c index 1309861d4c2c..ddbc69c0922b 100644 --- a/block/fops.c +++ b/block/fops.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,6 @@ static ssize_t __blkdev_direct_IO_simple(struct kiocb *iocb, struct bio bio; ssize_t ret; - WARN_ON_ONCE(iocb->ki_flags & IOCB_HAS_METADATA); if (nr_pages <= DIO_INLINE_BIO_VECS) vecs = inline_vecs; else { @@ -131,7 +131,7 @@ static void blkdev_bio_end_io(struct bio *bio) if (bio->bi_status && !dio->bio.bi_status) dio->bio.bi_status = bio->bi_status; - if (!is_sync && (dio->iocb->ki_flags & IOCB_HAS_METADATA)) + if (bio_integrity(bio)) bio_integrity_unmap_user(bio); if (atomic_dec_and_test(&dio->ref)) { @@ -233,7 +233,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, } bio->bi_opf |= REQ_NOWAIT; } - if (!is_sync && (iocb->ki_flags & IOCB_HAS_METADATA)) { + if (iocb->ki_flags & IOCB_HAS_METADATA) { ret = bio_integrity_map_iter(bio, iocb->private); if (unlikely(ret)) goto fail; @@ -301,7 +301,7 @@ static void blkdev_bio_end_io_async(struct bio *bio) ret = blk_status_to_errno(bio->bi_status); } - if (iocb->ki_flags & IOCB_HAS_METADATA) + if (bio_integrity(bio)) bio_integrity_unmap_user(bio); iocb->ki_complete(iocb, ret); @@ -422,7 +422,8 @@ static ssize_t blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter) } nr_pages = bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS + 1); - if (likely(nr_pages <= BIO_MAX_VECS)) { + if (likely(nr_pages <= BIO_MAX_VECS && + !(iocb->ki_flags & IOCB_HAS_METADATA))) { if (is_sync_kiocb(iocb)) return __blkdev_direct_IO_simple(iocb, iter, bdev, nr_pages); @@ -496,18 +497,21 @@ static void blkdev_readahead(struct readahead_control *rac) mpage_readahead(rac, blkdev_get_block); } -static int blkdev_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, struct folio **foliop, void **fsdata) +static int blkdev_write_begin(const struct kiocb *iocb, + struct address_space *mapping, loff_t pos, + unsigned len, struct folio **foliop, + void **fsdata) { return block_write_begin(mapping, pos, len, foliop, blkdev_get_block); } -static int blkdev_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, struct folio *folio, - void *fsdata) +static int blkdev_write_end(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct folio *folio, void *fsdata) { int ret; - ret = block_write_end(file, mapping, pos, len, copied, folio, fsdata); + ret = block_write_end(pos, len, copied, folio); folio_unlock(folio); folio_put(folio); @@ -537,30 +541,42 @@ static void blkdev_readahead(struct readahead_control *rac) iomap_readahead(rac, &blkdev_iomap_ops); } -static int blkdev_map_blocks(struct iomap_writepage_ctx *wpc, - struct inode *inode, loff_t offset, unsigned int len) +static ssize_t blkdev_writeback_range(struct iomap_writepage_ctx *wpc, + struct folio *folio, u64 offset, unsigned int len, u64 end_pos) { - loff_t isize = i_size_read(inode); + loff_t isize = i_size_read(wpc->inode); if (WARN_ON_ONCE(offset >= isize)) return -EIO; - if (offset >= wpc->iomap.offset && - offset < wpc->iomap.offset + wpc->iomap.length) - return 0; - return blkdev_iomap_begin(inode, offset, isize - offset, - IOMAP_WRITE, &wpc->iomap, NULL); + + if (offset < wpc->iomap.offset || + offset >= wpc->iomap.offset + wpc->iomap.length) { + int error; + + error = blkdev_iomap_begin(wpc->inode, offset, isize - offset, + IOMAP_WRITE, &wpc->iomap, NULL); + if (error) + return error; + } + + return iomap_add_to_ioend(wpc, folio, offset, end_pos, len); } static const struct iomap_writeback_ops blkdev_writeback_ops = { - .map_blocks = blkdev_map_blocks, + .writeback_range = blkdev_writeback_range, + .writeback_submit = iomap_ioend_writeback_submit, }; static int blkdev_writepages(struct address_space *mapping, struct writeback_control *wbc) { - struct iomap_writepage_ctx wpc = { }; + struct iomap_writepage_ctx wpc = { + .inode = mapping->host, + .wbc = wbc, + .ops = &blkdev_writeback_ops + }; - return iomap_writepages(mapping, wbc, &wpc, &blkdev_writeback_ops); + return iomap_writepages(&wpc); } const struct address_space_operations def_blk_aops = { @@ -672,6 +688,8 @@ static int blkdev_open(struct inode *inode, struct file *filp) if (bdev_can_atomic_write(bdev)) filp->f_mode |= FMODE_CAN_ATOMIC_WRITE; + if (blk_get_integrity(bdev->bd_disk)) + filp->f_mode |= FMODE_HAS_METADATA; ret = bdev_open(bdev, mode, filp->private_data, NULL, filp); if (ret) @@ -711,7 +729,8 @@ blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from) static ssize_t blkdev_buffered_write(struct kiocb *iocb, struct iov_iter *from) { - return iomap_file_buffered_write(iocb, from, &blkdev_iomap_ops, NULL); + return iomap_file_buffered_write(iocb, from, &blkdev_iomap_ops, NULL, + NULL); } /* @@ -841,7 +860,7 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to) #define BLKDEV_FALLOC_FL_SUPPORTED \ (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \ - FALLOC_FL_ZERO_RANGE) + FALLOC_FL_ZERO_RANGE | FALLOC_FL_WRITE_ZEROES) static long blkdev_fallocate(struct file *file, int mode, loff_t start, loff_t len) @@ -850,11 +869,19 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start, struct block_device *bdev = I_BDEV(inode); loff_t end = start + len - 1; loff_t isize; + unsigned int flags; int error; /* Fail if we don't recognize the flags. */ if (mode & ~BLKDEV_FALLOC_FL_SUPPORTED) return -EOPNOTSUPP; + /* + * Don't allow writing zeroes if the device does not enable the + * unmap write zeroes operation. + */ + if ((mode & FALLOC_FL_WRITE_ZEROES) && + !bdev_write_zeroes_unmap_sectors(bdev)) + return -EOPNOTSUPP; /* Don't go off the end of the device. */ isize = bdev_nr_bytes(bdev); @@ -877,48 +904,46 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start, inode_lock(inode); filemap_invalidate_lock(inode->i_mapping); + switch (mode) { + case FALLOC_FL_ZERO_RANGE: + case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE: + flags = BLKDEV_ZERO_NOUNMAP; + break; + case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE: + flags = BLKDEV_ZERO_NOFALLBACK; + break; + case FALLOC_FL_WRITE_ZEROES: + flags = 0; + break; + default: + error = -EOPNOTSUPP; + goto fail; + } + /* * Invalidate the page cache, including dirty pages, for valid * de-allocate mode calls to fallocate(). */ - switch (mode) { - case FALLOC_FL_ZERO_RANGE: - case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE: - error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end); - if (error) - goto fail; - - error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT, - len >> SECTOR_SHIFT, GFP_KERNEL, - BLKDEV_ZERO_NOUNMAP); - break; - case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE: - error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end); - if (error) - goto fail; - - error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT, - len >> SECTOR_SHIFT, GFP_KERNEL, - BLKDEV_ZERO_NOFALLBACK); - break; - default: - error = -EOPNOTSUPP; - } + error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end); + if (error) + goto fail; + error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT, + len >> SECTOR_SHIFT, GFP_KERNEL, flags); fail: filemap_invalidate_unlock(inode->i_mapping); inode_unlock(inode); return error; } -static int blkdev_mmap(struct file *file, struct vm_area_struct *vma) +static int blkdev_mmap_prepare(struct vm_area_desc *desc) { - struct inode *bd_inode = bdev_file_inode(file); + struct file *file = desc->file; - if (bdev_read_only(I_BDEV(bd_inode))) - return generic_file_readonly_mmap(file, vma); + if (bdev_read_only(I_BDEV(bdev_file_inode(file)))) + return generic_file_readonly_mmap_prepare(desc); - return generic_file_mmap(file, vma); + return generic_file_mmap_prepare(desc); } const struct file_operations def_blk_fops = { @@ -928,7 +953,7 @@ const struct file_operations def_blk_fops = { .read_iter = blkdev_read_iter, .write_iter = blkdev_write_iter, .iopoll = iocb_bio_iopoll, - .mmap = blkdev_mmap, + .mmap_prepare = blkdev_mmap_prepare, .fsync = blkdev_fsync, .unlocked_ioctl = blkdev_ioctl, #ifdef CONFIG_COMPAT diff --git a/block/genhd.c b/block/genhd.c index 8171a6bc3210..9bbc38d12792 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -128,23 +128,27 @@ static void part_stat_read_all(struct block_device *part, static void bdev_count_inflight_rw(struct block_device *part, unsigned int inflight[2], bool mq_driver) { + int write = 0; + int read = 0; int cpu; if (mq_driver) { blk_mq_in_driver_rw(part, inflight); - } else { - for_each_possible_cpu(cpu) { - inflight[READ] += part_stat_local_read_cpu( - part, in_flight[READ], cpu); - inflight[WRITE] += part_stat_local_read_cpu( - part, in_flight[WRITE], cpu); - } + return; } - if (WARN_ON_ONCE((int)inflight[READ] < 0)) - inflight[READ] = 0; - if (WARN_ON_ONCE((int)inflight[WRITE] < 0)) - inflight[WRITE] = 0; + for_each_possible_cpu(cpu) { + read += part_stat_local_read_cpu(part, in_flight[READ], cpu); + write += part_stat_local_read_cpu(part, in_flight[WRITE], cpu); + } + + /* + * While iterating all CPUs, some IOs may be issued from a CPU already + * traversed and complete on a CPU that has not yet been traversed, + * causing the inflight number to be negative. + */ + inflight[READ] = read > 0 ? read : 0; + inflight[WRITE] = write > 0 ? write : 0; } /** @@ -1299,6 +1303,7 @@ static void disk_release(struct device *dev) disk_free_zone_resources(disk); xa_destroy(&disk->part_tbl); + kobject_put(&disk->queue_kobj); disk->queue->disk = NULL; blk_put_queue(disk->queue); @@ -1482,6 +1487,7 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id, INIT_LIST_HEAD(&disk->slave_bdevs); #endif mutex_init(&disk->rqos_state_mutex); + kobject_init(&disk->queue_kobj, &blk_queue_ktype); return disk; out_erase_part0: diff --git a/block/ioctl.c b/block/ioctl.c index e472cc1030c6..f7b0006ca45d 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "blk.h" #include "blk-crypto-internal.h" @@ -644,7 +645,7 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode, case IOC_PR_CLEAR: return blkdev_pr_clear(bdev, mode, argp); default: - return -ENOIOCTLCMD; + return blk_get_meta_cap(bdev, cmd, argp); } } diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c index 4dba8405bd01..70cbc7b2deb4 100644 --- a/block/kyber-iosched.c +++ b/block/kyber-iosched.c @@ -157,10 +157,7 @@ struct kyber_queue_data { */ struct sbitmap_queue domain_tokens[KYBER_NUM_DOMAINS]; - /* - * Async request percentage, converted to per-word depth for - * sbitmap_get_shallow(). - */ + /* Number of allowed async requests. */ unsigned int async_depth; struct kyber_cpu_latency __percpu *cpu_latency; @@ -402,20 +399,13 @@ static struct kyber_queue_data *kyber_queue_data_alloc(struct request_queue *q) return ERR_PTR(ret); } -static int kyber_init_sched(struct request_queue *q, struct elevator_type *e) +static int kyber_init_sched(struct request_queue *q, struct elevator_queue *eq) { struct kyber_queue_data *kqd; - struct elevator_queue *eq; - - eq = elevator_alloc(q, e); - if (!eq) - return -ENOMEM; kqd = kyber_queue_data_alloc(q); - if (IS_ERR(kqd)) { - kobject_put(&eq->kobj); + if (IS_ERR(kqd)) return PTR_ERR(kqd); - } blk_stat_enable_accounting(q); @@ -454,10 +444,8 @@ static void kyber_depth_updated(struct blk_mq_hw_ctx *hctx) { struct kyber_queue_data *kqd = hctx->queue->elevator->elevator_data; struct blk_mq_tags *tags = hctx->sched_tags; - unsigned int shift = tags->bitmap_tags.sb.shift; - - kqd->async_depth = (1U << shift) * KYBER_ASYNC_PERCENT / 100U; + kqd->async_depth = hctx->queue->nr_requests * KYBER_ASYNC_PERCENT / 100U; sbitmap_queue_min_shallow_depth(&tags->bitmap_tags, kqd->async_depth); } diff --git a/block/mq-deadline.c b/block/mq-deadline.c index 2edf1cac06d5..b9b7cdf1d3c9 100644 --- a/block/mq-deadline.c +++ b/block/mq-deadline.c @@ -487,20 +487,6 @@ static struct request *dd_dispatch_request(struct blk_mq_hw_ctx *hctx) return rq; } -/* - * 'depth' is a number in the range 1..INT_MAX representing a number of - * requests. Scale it with a factor (1 << bt->sb.shift) / q->nr_requests since - * 1..(1 << bt->sb.shift) is the range expected by sbitmap_get_shallow(). - * Values larger than q->nr_requests have the same effect as q->nr_requests. - */ -static int dd_to_word_depth(struct blk_mq_hw_ctx *hctx, unsigned int qdepth) -{ - struct sbitmap_queue *bt = &hctx->sched_tags->bitmap_tags; - const unsigned int nrr = hctx->queue->nr_requests; - - return ((qdepth << bt->sb.shift) + nrr - 1) / nrr; -} - /* * Called by __blk_mq_alloc_request(). The shallow_depth value set by this * function is used by __blk_mq_get_tag(). @@ -517,7 +503,7 @@ static void dd_limit_depth(blk_opf_t opf, struct blk_mq_alloc_data *data) * Throttle asynchronous requests and writes such that these requests * do not block the allocation of synchronous requests. */ - data->shallow_depth = dd_to_word_depth(data->hctx, dd->async_depth); + data->shallow_depth = dd->async_depth; } /* Called by blk_mq_update_nr_requests(). */ @@ -568,20 +554,14 @@ static void dd_exit_sched(struct elevator_queue *e) /* * initialize elevator private data (deadline_data). */ -static int dd_init_sched(struct request_queue *q, struct elevator_type *e) +static int dd_init_sched(struct request_queue *q, struct elevator_queue *eq) { struct deadline_data *dd; - struct elevator_queue *eq; enum dd_prio prio; - int ret = -ENOMEM; - - eq = elevator_alloc(q, e); - if (!eq) - return ret; dd = kzalloc_node(sizeof(*dd), GFP_KERNEL, q->node); if (!dd) - goto put_eq; + return -ENOMEM; eq->elevator_data = dd; @@ -608,10 +588,6 @@ static int dd_init_sched(struct request_queue *q, struct elevator_type *e) q->elevator = eq; return 0; - -put_eq: - kobject_put(&eq->kobj); - return ret; } /* diff --git a/block/t10-pi.c b/block/t10-pi.c index 851db518ee5e..0c4ed9702146 100644 --- a/block/t10-pi.c +++ b/block/t10-pi.c @@ -56,7 +56,7 @@ static void t10_pi_generate(struct blk_integrity_iter *iter, pi->ref_tag = 0; iter->data_buf += iter->interval; - iter->prot_buf += bi->tuple_size; + iter->prot_buf += bi->metadata_size; iter->seed++; } } @@ -105,7 +105,7 @@ static blk_status_t t10_pi_verify(struct blk_integrity_iter *iter, next: iter->data_buf += iter->interval; - iter->prot_buf += bi->tuple_size; + iter->prot_buf += bi->metadata_size; iter->seed++; } @@ -125,7 +125,7 @@ static blk_status_t t10_pi_verify(struct blk_integrity_iter *iter, static void t10_pi_type1_prepare(struct request *rq) { struct blk_integrity *bi = &rq->q->limits.integrity; - const int tuple_sz = bi->tuple_size; + const int tuple_sz = bi->metadata_size; u32 ref_tag = t10_pi_ref_tag(rq); u8 offset = bi->pi_offset; struct bio *bio; @@ -177,7 +177,7 @@ static void t10_pi_type1_complete(struct request *rq, unsigned int nr_bytes) { struct blk_integrity *bi = &rq->q->limits.integrity; unsigned intervals = nr_bytes >> bi->interval_exp; - const int tuple_sz = bi->tuple_size; + const int tuple_sz = bi->metadata_size; u32 ref_tag = t10_pi_ref_tag(rq); u8 offset = bi->pi_offset; struct bio *bio; @@ -234,7 +234,7 @@ static void ext_pi_crc64_generate(struct blk_integrity_iter *iter, put_unaligned_be48(0ULL, pi->ref_tag); iter->data_buf += iter->interval; - iter->prot_buf += bi->tuple_size; + iter->prot_buf += bi->metadata_size; iter->seed++; } } @@ -289,7 +289,7 @@ static blk_status_t ext_pi_crc64_verify(struct blk_integrity_iter *iter, next: iter->data_buf += iter->interval; - iter->prot_buf += bi->tuple_size; + iter->prot_buf += bi->metadata_size; iter->seed++; } @@ -299,7 +299,7 @@ static blk_status_t ext_pi_crc64_verify(struct blk_integrity_iter *iter, static void ext_pi_type1_prepare(struct request *rq) { struct blk_integrity *bi = &rq->q->limits.integrity; - const int tuple_sz = bi->tuple_size; + const int tuple_sz = bi->metadata_size; u64 ref_tag = ext_pi_ref_tag(rq); u8 offset = bi->pi_offset; struct bio *bio; @@ -340,7 +340,7 @@ static void ext_pi_type1_complete(struct request *rq, unsigned int nr_bytes) { struct blk_integrity *bi = &rq->q->limits.integrity; unsigned intervals = nr_bytes >> bi->interval_exp; - const int tuple_sz = bi->tuple_size; + const int tuple_sz = bi->metadata_size; u64 ref_tag = ext_pi_ref_tag(rq); u8 offset = bi->pi_offset; struct bio *bio; diff --git a/crypto/Kconfig b/crypto/Kconfig index e9fee7818e27..23bd98981ae8 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -176,16 +176,33 @@ config CRYPTO_USER config CRYPTO_SELFTESTS bool "Enable cryptographic self-tests" - depends on DEBUG_KERNEL + depends on EXPERT help Enable the cryptographic self-tests. The cryptographic self-tests run at boot time, or at algorithm registration time if algorithms are dynamically loaded later. - This is primarily intended for developer use. It should not be - enabled in production kernels, unless you are trying to use these - tests to fulfill a FIPS testing requirement. + There are two main use cases for these tests: + + - Development and pre-release testing. In this case, also enable + CRYPTO_SELFTESTS_FULL to get the full set of tests. All crypto code + in the kernel is expected to pass the full set of tests. + + - Production kernels, to help prevent buggy drivers from being used + and/or meet FIPS 140-3 pre-operational testing requirements. In + this case, enable CRYPTO_SELFTESTS but not CRYPTO_SELFTESTS_FULL. + +config CRYPTO_SELFTESTS_FULL + bool "Enable the full set of cryptographic self-tests" + depends on CRYPTO_SELFTESTS + help + Enable the full set of cryptographic self-tests for each algorithm. + + The full set of tests should be enabled for development and + pre-release testing, but not in production kernels. + + All crypto code in the kernel is expected to pass the full tests. config CRYPTO_NULL tristate "Null algorithms" @@ -969,15 +986,16 @@ config CRYPTO_SHA1 select CRYPTO_HASH select CRYPTO_LIB_SHA1 help - SHA-1 secure hash algorithm (FIPS 180, ISO/IEC 10118-3) + SHA-1 secure hash algorithm (FIPS 180, ISO/IEC 10118-3), including + HMAC support. config CRYPTO_SHA256 tristate "SHA-224 and SHA-256" select CRYPTO_HASH select CRYPTO_LIB_SHA256 - select CRYPTO_LIB_SHA256_GENERIC help - SHA-224 and SHA-256 secure hash algorithms (FIPS 180, ISO/IEC 10118-3) + SHA-224 and SHA-256 secure hash algorithms (FIPS 180, ISO/IEC + 10118-3), including HMAC support. This is required for IPsec AH (XFRM_AH) and IPsec ESP (XFRM_ESP). Used by the btrfs filesystem, Ceph, NFS, and SMB. @@ -985,8 +1003,10 @@ config CRYPTO_SHA256 config CRYPTO_SHA512 tristate "SHA-384 and SHA-512" select CRYPTO_HASH + select CRYPTO_LIB_SHA512 help - SHA-384 and SHA-512 secure hash algorithms (FIPS 180, ISO/IEC 10118-3) + SHA-384 and SHA-512 secure hash algorithms (FIPS 180, ISO/IEC + 10118-3), including HMAC support. config CRYPTO_SHA3 tristate "SHA-3" @@ -1403,9 +1423,6 @@ config CRYPTO_USER_API_ENABLE_OBSOLETE endmenu -config CRYPTO_HASH_INFO - bool - if !KMSAN # avoid false positives from assembly if ARM source "arch/arm/crypto/Kconfig" diff --git a/crypto/Makefile b/crypto/Makefile index 017df3a2e4bb..6c5d59369dac 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -75,10 +75,9 @@ obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o obj-$(CONFIG_CRYPTO_MD4) += md4.o obj-$(CONFIG_CRYPTO_MD5) += md5.o obj-$(CONFIG_CRYPTO_RMD160) += rmd160.o -obj-$(CONFIG_CRYPTO_SHA1) += sha1_generic.o +obj-$(CONFIG_CRYPTO_SHA1) += sha1.o obj-$(CONFIG_CRYPTO_SHA256) += sha256.o -CFLAGS_sha256.o += -DARCH=$(ARCH) -obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o +obj-$(CONFIG_CRYPTO_SHA512) += sha512.o obj-$(CONFIG_CRYPTO_SHA3) += sha3_generic.o obj-$(CONFIG_CRYPTO_SM3_GENERIC) += sm3_generic.o obj-$(CONFIG_CRYPTO_STREEBOG) += streebog_generic.o @@ -154,10 +153,8 @@ obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c-cryptoapi.o crc32c-cryptoapi-y := crc32c.o -CFLAGS_crc32c.o += -DARCH=$(ARCH) obj-$(CONFIG_CRYPTO_CRC32) += crc32-cryptoapi.o crc32-cryptoapi-y := crc32.o -CFLAGS_crc32.o += -DARCH=$(ARCH) obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o obj-$(CONFIG_CRYPTO_KRB5ENC) += krb5enc.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o lzo-rle.o @@ -205,7 +202,6 @@ obj-$(CONFIG_CRYPTO_ECRDSA) += ecrdsa_generic.o obj-$(CONFIG_XOR_BLOCKS) += xor.o obj-$(CONFIG_ASYNC_CORE) += async_tx/ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/ -obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o crypto_simd-y := simd.o obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o diff --git a/crypto/ahash.c b/crypto/ahash.c index e10bc2659ae4..a227793d2c5b 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -29,19 +29,6 @@ #define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000e -struct crypto_hash_walk { - const char *data; - - unsigned int offset; - unsigned int flags; - - struct page *pg; - unsigned int entrylen; - - unsigned int total; - struct scatterlist *sg; -}; - static int ahash_def_finup(struct ahash_request *req); static inline bool crypto_ahash_block_only(struct crypto_ahash *tfm) @@ -112,8 +99,8 @@ static int hash_walk_new_entry(struct crypto_hash_walk *walk) return hash_walk_next(walk); } -static int crypto_hash_walk_first(struct ahash_request *req, - struct crypto_hash_walk *walk) +int crypto_hash_walk_first(struct ahash_request *req, + struct crypto_hash_walk *walk) { walk->total = req->nbytes; walk->entrylen = 0; @@ -133,8 +120,9 @@ static int crypto_hash_walk_first(struct ahash_request *req, return hash_walk_new_entry(walk); } +EXPORT_SYMBOL_GPL(crypto_hash_walk_first); -static int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) +int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) { if ((walk->flags & CRYPTO_AHASH_REQ_VIRT)) return err; @@ -160,11 +148,7 @@ static int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) return hash_walk_new_entry(walk); } - -static inline int crypto_hash_walk_last(struct crypto_hash_walk *walk) -{ - return !(walk->entrylen | walk->total); -} +EXPORT_SYMBOL_GPL(crypto_hash_walk_done); /* * For an ahash tfm that is using an shash algorithm (instead of an ahash @@ -347,6 +331,12 @@ static int ahash_do_req_chain(struct ahash_request *req, if (crypto_ahash_statesize(tfm) > HASH_MAX_STATESIZE) return -ENOSYS; + if (!crypto_ahash_need_fallback(tfm)) + return -ENOSYS; + + if (crypto_hash_no_export_core(tfm)) + return -ENOSYS; + { u8 state[HASH_MAX_STATESIZE]; @@ -600,12 +590,14 @@ static void ahash_def_finup_done2(void *data, int err) static int ahash_def_finup_finish1(struct ahash_request *req, int err) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + if (err) goto out; req->base.complete = ahash_def_finup_done2; - err = crypto_ahash_final(req); + err = crypto_ahash_alg(tfm)->final(req); if (err == -EINPROGRESS || err == -EBUSY) return err; @@ -952,6 +944,10 @@ static int ahash_prepare_alg(struct ahash_alg *alg) base->cra_reqsize > MAX_SYNC_HASH_REQSIZE) return -EINVAL; + if (base->cra_flags & CRYPTO_ALG_NEED_FALLBACK && + base->cra_flags & CRYPTO_ALG_NO_FALLBACK) + return -EINVAL; + err = hash_prepare_alg(&alg->halg); if (err) return err; @@ -960,7 +956,8 @@ static int ahash_prepare_alg(struct ahash_alg *alg) base->cra_flags |= CRYPTO_ALG_TYPE_AHASH; if ((base->cra_flags ^ CRYPTO_ALG_REQ_VIRT) & - (CRYPTO_ALG_ASYNC | CRYPTO_ALG_REQ_VIRT)) + (CRYPTO_ALG_ASYNC | CRYPTO_ALG_REQ_VIRT) && + !(base->cra_flags & CRYPTO_ALG_NO_FALLBACK)) base->cra_flags |= CRYPTO_ALG_NEED_FALLBACK; if (!alg->setkey) diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index 5e2b2680d7db..9e4bb7fbde25 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -119,7 +119,7 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, for (i = 0; i < disks; i++) { if (blocks[i] == NULL) { BUG_ON(i > disks - 3); /* P or Q can't be zero */ - srcs[i] = (void*)raid6_empty_zero_page; + srcs[i] = raid6_get_zero_page(); } else { srcs[i] = page_address(blocks[i]) + offsets[i]; diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index 354b8cd5537f..539ea5b378dc 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -414,7 +414,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = (void *) raid6_empty_zero_page; + ptrs[i] = raid6_get_zero_page(); else ptrs[i] = page_address(blocks[i]) + offs[i]; @@ -497,7 +497,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = (void*)raid6_empty_zero_page; + ptrs[i] = raid6_get_zero_page(); else ptrs[i] = page_address(blocks[i]) + offs[i]; diff --git a/crypto/crc32.c b/crypto/crc32.c index cc371d42601f..489cbed9422e 100644 --- a/crypto/crc32.c +++ b/crypto/crc32.c @@ -59,29 +59,12 @@ static int crc32_update(struct shash_desc *desc, const u8 *data, { u32 *crcp = shash_desc_ctx(desc); - *crcp = crc32_le_base(*crcp, data, len); - return 0; -} - -static int crc32_update_arch(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - u32 *crcp = shash_desc_ctx(desc); - *crcp = crc32_le(*crcp, data, len); return 0; } /* No final XOR 0xFFFFFFFF, like crc32_le */ -static int __crc32_finup(u32 *crcp, const u8 *data, unsigned int len, - u8 *out) -{ - put_unaligned_le32(crc32_le_base(*crcp, data, len), out); - return 0; -} - -static int __crc32_finup_arch(u32 *crcp, const u8 *data, unsigned int len, - u8 *out) +static int __crc32_finup(u32 *crcp, const u8 *data, unsigned int len, u8 *out) { put_unaligned_le32(crc32_le(*crcp, data, len), out); return 0; @@ -93,12 +76,6 @@ static int crc32_finup(struct shash_desc *desc, const u8 *data, return __crc32_finup(shash_desc_ctx(desc), data, len, out); } -static int crc32_finup_arch(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return __crc32_finup_arch(shash_desc_ctx(desc), data, len, out); -} - static int crc32_final(struct shash_desc *desc, u8 *out) { u32 *crcp = shash_desc_ctx(desc); @@ -113,13 +90,7 @@ static int crc32_digest(struct shash_desc *desc, const u8 *data, return __crc32_finup(crypto_shash_ctx(desc->tfm), data, len, out); } -static int crc32_digest_arch(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return __crc32_finup_arch(crypto_shash_ctx(desc->tfm), data, len, out); -} - -static struct shash_alg algs[] = {{ +static struct shash_alg alg = { .setkey = crc32_setkey, .init = crc32_init, .update = crc32_update, @@ -130,46 +101,23 @@ static struct shash_alg algs[] = {{ .digestsize = CHKSUM_DIGEST_SIZE, .base.cra_name = "crc32", - .base.cra_driver_name = "crc32-generic", + .base.cra_driver_name = "crc32-lib", .base.cra_priority = 100, .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, .base.cra_blocksize = CHKSUM_BLOCK_SIZE, .base.cra_ctxsize = sizeof(u32), .base.cra_module = THIS_MODULE, .base.cra_init = crc32_cra_init, -}, { - .setkey = crc32_setkey, - .init = crc32_init, - .update = crc32_update_arch, - .final = crc32_final, - .finup = crc32_finup_arch, - .digest = crc32_digest_arch, - .descsize = sizeof(u32), - .digestsize = CHKSUM_DIGEST_SIZE, - - .base.cra_name = "crc32", - .base.cra_driver_name = "crc32-" __stringify(ARCH), - .base.cra_priority = 150, - .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, - .base.cra_blocksize = CHKSUM_BLOCK_SIZE, - .base.cra_ctxsize = sizeof(u32), - .base.cra_module = THIS_MODULE, - .base.cra_init = crc32_cra_init, -}}; - -static int num_algs; +}; static int __init crc32_mod_init(void) { - /* register the arch flavor only if it differs from the generic one */ - num_algs = 1 + ((crc32_optimizations() & CRC32_LE_OPTIMIZATION) != 0); - - return crypto_register_shashes(algs, num_algs); + return crypto_register_shash(&alg); } static void __exit crc32_mod_fini(void) { - crypto_unregister_shashes(algs, num_algs); + crypto_unregister_shash(&alg); } module_init(crc32_mod_init); @@ -179,4 +127,3 @@ MODULE_AUTHOR("Alexander Boyko "); MODULE_DESCRIPTION("CRC32 calculations wrapper for lib/crc32"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CRYPTO("crc32"); -MODULE_ALIAS_CRYPTO("crc32-generic"); diff --git a/crypto/crc32c.c b/crypto/crc32c.c index e5377898414a..1eff54dde2f7 100644 --- a/crypto/crc32c.c +++ b/crypto/crc32c.c @@ -85,15 +85,6 @@ static int chksum_update(struct shash_desc *desc, const u8 *data, { struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - ctx->crc = crc32c_base(ctx->crc, data, length); - return 0; -} - -static int chksum_update_arch(struct shash_desc *desc, const u8 *data, - unsigned int length) -{ - struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - ctx->crc = crc32c(ctx->crc, data, length); return 0; } @@ -107,13 +98,6 @@ static int chksum_final(struct shash_desc *desc, u8 *out) } static int __chksum_finup(u32 *crcp, const u8 *data, unsigned int len, u8 *out) -{ - put_unaligned_le32(~crc32c_base(*crcp, data, len), out); - return 0; -} - -static int __chksum_finup_arch(u32 *crcp, const u8 *data, unsigned int len, - u8 *out) { put_unaligned_le32(~crc32c(*crcp, data, len), out); return 0; @@ -127,14 +111,6 @@ static int chksum_finup(struct shash_desc *desc, const u8 *data, return __chksum_finup(&ctx->crc, data, len, out); } -static int chksum_finup_arch(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - - return __chksum_finup_arch(&ctx->crc, data, len, out); -} - static int chksum_digest(struct shash_desc *desc, const u8 *data, unsigned int length, u8 *out) { @@ -143,14 +119,6 @@ static int chksum_digest(struct shash_desc *desc, const u8 *data, return __chksum_finup(&mctx->key, data, length, out); } -static int chksum_digest_arch(struct shash_desc *desc, const u8 *data, - unsigned int length, u8 *out) -{ - struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm); - - return __chksum_finup_arch(&mctx->key, data, length, out); -} - static int crc32c_cra_init(struct crypto_tfm *tfm) { struct chksum_ctx *mctx = crypto_tfm_ctx(tfm); @@ -159,7 +127,7 @@ static int crc32c_cra_init(struct crypto_tfm *tfm) return 0; } -static struct shash_alg algs[] = {{ +static struct shash_alg alg = { .digestsize = CHKSUM_DIGEST_SIZE, .setkey = chksum_setkey, .init = chksum_init, @@ -170,46 +138,23 @@ static struct shash_alg algs[] = {{ .descsize = sizeof(struct chksum_desc_ctx), .base.cra_name = "crc32c", - .base.cra_driver_name = "crc32c-generic", + .base.cra_driver_name = "crc32c-lib", .base.cra_priority = 100, .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, .base.cra_blocksize = CHKSUM_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct chksum_ctx), .base.cra_module = THIS_MODULE, .base.cra_init = crc32c_cra_init, -}, { - .digestsize = CHKSUM_DIGEST_SIZE, - .setkey = chksum_setkey, - .init = chksum_init, - .update = chksum_update_arch, - .final = chksum_final, - .finup = chksum_finup_arch, - .digest = chksum_digest_arch, - .descsize = sizeof(struct chksum_desc_ctx), - - .base.cra_name = "crc32c", - .base.cra_driver_name = "crc32c-" __stringify(ARCH), - .base.cra_priority = 150, - .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, - .base.cra_blocksize = CHKSUM_BLOCK_SIZE, - .base.cra_ctxsize = sizeof(struct chksum_ctx), - .base.cra_module = THIS_MODULE, - .base.cra_init = crc32c_cra_init, -}}; - -static int num_algs; +}; static int __init crc32c_mod_init(void) { - /* register the arch flavor only if it differs from the generic one */ - num_algs = 1 + ((crc32_optimizations() & CRC32C_OPTIMIZATION) != 0); - - return crypto_register_shashes(algs, num_algs); + return crypto_register_shash(&alg); } static void __exit crc32c_mod_fini(void) { - crypto_unregister_shashes(algs, num_algs); + crypto_unregister_shash(&alg); } module_init(crc32c_mod_init); @@ -219,4 +164,3 @@ MODULE_AUTHOR("Clay Haapala "); MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations wrapper for lib/crc32c"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CRYPTO("crc32c"); -MODULE_ALIAS_CRYPTO("crc32c-generic"); diff --git a/crypto/cryptd.c b/crypto/cryptd.c index 5bb6f8d88cc2..efff54e707cb 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -34,6 +34,7 @@ MODULE_PARM_DESC(cryptd_max_cpu_qlen, "Set cryptd Max queue depth"); static struct workqueue_struct *cryptd_wq; struct cryptd_cpu_queue { + local_lock_t bh_lock; struct crypto_queue queue; struct work_struct work; }; @@ -110,6 +111,7 @@ static int cryptd_init_queue(struct cryptd_queue *queue, cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu); crypto_init_queue(&cpu_queue->queue, max_cpu_qlen); INIT_WORK(&cpu_queue->work, cryptd_queue_worker); + local_lock_init(&cpu_queue->bh_lock); } pr_info("cryptd: max_cpu_qlen set to %d\n", max_cpu_qlen); return 0; @@ -135,6 +137,7 @@ static int cryptd_enqueue_request(struct cryptd_queue *queue, refcount_t *refcnt; local_bh_disable(); + local_lock_nested_bh(&queue->cpu_queue->bh_lock); cpu_queue = this_cpu_ptr(queue->cpu_queue); err = crypto_enqueue_request(&cpu_queue->queue, request); @@ -151,6 +154,7 @@ static int cryptd_enqueue_request(struct cryptd_queue *queue, refcount_inc(refcnt); out: + local_unlock_nested_bh(&queue->cpu_queue->bh_lock); local_bh_enable(); return err; @@ -169,8 +173,10 @@ static void cryptd_queue_worker(struct work_struct *work) * Only handle one request at a time to avoid hogging crypto workqueue. */ local_bh_disable(); + __local_lock_nested_bh(&cpu_queue->bh_lock); backlog = crypto_get_backlog(&cpu_queue->queue); req = crypto_dequeue_request(&cpu_queue->queue); + __local_unlock_nested_bh(&cpu_queue->bh_lock); local_bh_enable(); if (!req) diff --git a/crypto/crypto_engine.c b/crypto/crypto_engine.c index 445d3c113ee1..18e1689efe12 100644 --- a/crypto/crypto_engine.c +++ b/crypto/crypto_engine.c @@ -74,7 +74,6 @@ static void crypto_pump_requests(struct crypto_engine *engine, struct crypto_engine_alg *alg; struct crypto_engine_op *op; unsigned long flags; - bool was_busy = false; int ret; spin_lock_irqsave(&engine->queue_lock, flags); @@ -83,12 +82,6 @@ static void crypto_pump_requests(struct crypto_engine *engine, if (!engine->retry_support && engine->cur_req) goto out; - /* If another context is idling then defer */ - if (engine->idling) { - kthread_queue_work(engine->kworker, &engine->pump_requests); - goto out; - } - /* Check if the engine queue is idle */ if (!crypto_queue_len(&engine->queue) || !engine->running) { if (!engine->busy) @@ -102,15 +95,6 @@ static void crypto_pump_requests(struct crypto_engine *engine, } engine->busy = false; - engine->idling = true; - spin_unlock_irqrestore(&engine->queue_lock, flags); - - if (engine->unprepare_crypt_hardware && - engine->unprepare_crypt_hardware(engine)) - dev_err(engine->dev, "failed to unprepare crypt hardware\n"); - - spin_lock_irqsave(&engine->queue_lock, flags); - engine->idling = false; goto out; } @@ -129,22 +113,11 @@ static void crypto_pump_requests(struct crypto_engine *engine, if (!engine->retry_support) engine->cur_req = async_req; - if (engine->busy) - was_busy = true; - else + if (!engine->busy) engine->busy = true; spin_unlock_irqrestore(&engine->queue_lock, flags); - /* Until here we get the request need to be encrypted successfully */ - if (!was_busy && engine->prepare_crypt_hardware) { - ret = engine->prepare_crypt_hardware(engine); - if (ret) { - dev_err(engine->dev, "failed to prepare crypt hardware\n"); - goto req_err_1; - } - } - alg = container_of(async_req->tfm->__crt_alg, struct crypto_engine_alg, base); op = &alg->op; @@ -195,17 +168,6 @@ static void crypto_pump_requests(struct crypto_engine *engine, out: spin_unlock_irqrestore(&engine->queue_lock, flags); - /* - * Batch requests is possible only if - * hardware can enqueue multiple requests - */ - if (engine->do_batch_requests) { - ret = engine->do_batch_requests(engine); - if (ret) - dev_err(engine->dev, "failed to do batch requests: %d\n", - ret); - } - return; } @@ -462,12 +424,6 @@ EXPORT_SYMBOL_GPL(crypto_engine_stop); * crypto-engine queue. * @dev: the device attached with one hardware engine * @retry_support: whether hardware has support for retry mechanism - * @cbk_do_batch: pointer to a callback function to be invoked when executing - * a batch of requests. - * This has the form: - * callback(struct crypto_engine *engine) - * where: - * engine: the crypto engine structure. * @rt: whether this queue is set to run as a realtime task * @qlen: maximum size of the crypto-engine queue * @@ -476,7 +432,6 @@ EXPORT_SYMBOL_GPL(crypto_engine_stop); */ struct crypto_engine *crypto_engine_alloc_init_and_set(struct device *dev, bool retry_support, - int (*cbk_do_batch)(struct crypto_engine *engine), bool rt, int qlen) { struct crypto_engine *engine; @@ -492,14 +447,8 @@ struct crypto_engine *crypto_engine_alloc_init_and_set(struct device *dev, engine->rt = rt; engine->running = false; engine->busy = false; - engine->idling = false; engine->retry_support = retry_support; engine->priv_data = dev; - /* - * Batch requests is possible only if - * hardware has support for retry mechanism. - */ - engine->do_batch_requests = retry_support ? cbk_do_batch : NULL; snprintf(engine->name, sizeof(engine->name), "%s-engine", dev_name(dev)); @@ -534,7 +483,7 @@ EXPORT_SYMBOL_GPL(crypto_engine_alloc_init_and_set); */ struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt) { - return crypto_engine_alloc_init_and_set(dev, false, NULL, rt, + return crypto_engine_alloc_init_and_set(dev, false, rt, CRYPTO_ENGINE_MAX_QLEN); } EXPORT_SYMBOL_GPL(crypto_engine_alloc_init); diff --git a/crypto/deflate.c b/crypto/deflate.c index fe8e4ad0fee1..21404515dc77 100644 --- a/crypto/deflate.c +++ b/crypto/deflate.c @@ -48,9 +48,14 @@ static void *deflate_alloc_stream(void) return ctx; } +static void deflate_free_stream(void *ctx) +{ + kvfree(ctx); +} + static struct crypto_acomp_streams deflate_streams = { .alloc_ctx = deflate_alloc_stream, - .cfree_ctx = kvfree, + .free_ctx = deflate_free_stream, }; static int deflate_compress_one(struct acomp_req *req, diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c index c24d4ff2b4a8..1266eb790708 100644 --- a/crypto/jitterentropy-kcapi.c +++ b/crypto/jitterentropy-kcapi.c @@ -144,7 +144,7 @@ int jent_hash_time(void *hash_state, __u64 time, u8 *addtl, * Inject the data from the previous loop into the pool. This data is * not considered to contain any entropy, but it stirs the pool a bit. */ - ret = crypto_shash_update(desc, intermediary, sizeof(intermediary)); + ret = crypto_shash_update(hash_state_desc, intermediary, sizeof(intermediary)); if (ret) goto err; @@ -157,11 +157,12 @@ int jent_hash_time(void *hash_state, __u64 time, u8 *addtl, * conditioning operation to have an identical amount of input data * according to section 3.1.5. */ - if (!stuck) { - ret = crypto_shash_update(hash_state_desc, (u8 *)&time, - sizeof(__u64)); + if (stuck) { + time = 0; } + ret = crypto_shash_update(hash_state_desc, (u8 *)&time, sizeof(__u64)); + err: shash_desc_zero(desc); memzero_explicit(intermediary, sizeof(intermediary)); diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c index 3b390bd6c119..3f93cdc9a7af 100644 --- a/crypto/jitterentropy.c +++ b/crypto/jitterentropy.c @@ -145,6 +145,7 @@ struct rand_data { */ #define JENT_ENTROPY_SAFETY_FACTOR 64 +#include #include #include #include "jitterentropy.h" @@ -178,7 +179,6 @@ static const unsigned int jent_apt_cutoff_lookup[15] = { static const unsigned int jent_apt_cutoff_permanent_lookup[15] = { 355, 447, 479, 494, 502, 507, 510, 512, 512, 512, 512, 512, 512, 512, 512 }; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static void jent_apt_init(struct rand_data *ec, unsigned int osr) { diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c index 2a81a6315a0d..4519c572d37e 100644 --- a/crypto/krb5/selftest.c +++ b/crypto/krb5/selftest.c @@ -152,6 +152,7 @@ static int krb5_test_one_prf(const struct krb5_prf_test *test) out: clear_buf(&result); + clear_buf(&prf); clear_buf(&octet); clear_buf(&key); return ret; diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index c33d29a523e0..c3a9d4f2995c 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -178,7 +178,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req) static int pcrypt_aead_init_tfm(struct crypto_aead *tfm) { - int cpu, cpu_index; + int cpu_index; struct aead_instance *inst = aead_alg_instance(tfm); struct pcrypt_instance_ctx *ictx = aead_instance_ctx(inst); struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(tfm); @@ -187,10 +187,7 @@ static int pcrypt_aead_init_tfm(struct crypto_aead *tfm) cpu_index = (unsigned int)atomic_inc_return(&ictx->tfm_count) % cpumask_weight(cpu_online_mask); - ctx->cb_cpu = cpumask_first(cpu_online_mask); - for (cpu = 0; cpu < cpu_index; cpu++) - ctx->cb_cpu = cpumask_next(ctx->cb_cpu, cpu_online_mask); - + ctx->cb_cpu = cpumask_nth(cpu_index, cpu_online_mask); cipher = crypto_spawn_aead(&ictx->spawn); if (IS_ERR(cipher)) diff --git a/crypto/sha1.c b/crypto/sha1.c new file mode 100644 index 000000000000..4fbf61cf0370 --- /dev/null +++ b/crypto/sha1.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Crypto API support for SHA-1 and HMAC-SHA1 + * + * Copyright (c) Alan Smithee. + * Copyright (c) Andrew McDonald + * Copyright (c) Jean-Francois Dive + * Copyright 2025 Google LLC + */ +#include +#include +#include +#include + +/* + * Export and import functions. crypto_shash wants a particular format that + * matches that used by some legacy drivers. It currently is the same as the + * library SHA context, except the value in bytecount must be block-aligned and + * the remainder must be stored in an extra u8 appended to the struct. + */ + +#define SHA1_SHASH_STATE_SIZE (sizeof(struct sha1_ctx) + 1) +static_assert(sizeof(struct sha1_ctx) == sizeof(struct sha1_state)); +static_assert(offsetof(struct sha1_ctx, state) == offsetof(struct sha1_state, state)); +static_assert(offsetof(struct sha1_ctx, bytecount) == offsetof(struct sha1_state, count)); +static_assert(offsetof(struct sha1_ctx, buf) == offsetof(struct sha1_state, buffer)); + +static int __crypto_sha1_export(const struct sha1_ctx *ctx0, void *out) +{ + struct sha1_ctx ctx = *ctx0; + unsigned int partial; + u8 *p = out; + + partial = ctx.bytecount % SHA1_BLOCK_SIZE; + ctx.bytecount -= partial; + memcpy(p, &ctx, sizeof(ctx)); + p += sizeof(ctx); + *p = partial; + return 0; +} + +static int __crypto_sha1_import(struct sha1_ctx *ctx, const void *in) +{ + const u8 *p = in; + + memcpy(ctx, p, sizeof(*ctx)); + p += sizeof(*ctx); + ctx->bytecount += *p; + return 0; +} + +static int __crypto_sha1_export_core(const struct sha1_ctx *ctx, void *out) +{ + memcpy(out, ctx, offsetof(struct sha1_ctx, buf)); + return 0; +} + +static int __crypto_sha1_import_core(struct sha1_ctx *ctx, const void *in) +{ + memcpy(ctx, in, offsetof(struct sha1_ctx, buf)); + return 0; +} + +const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, + 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, + 0xaf, 0xd8, 0x07, 0x09 +}; +EXPORT_SYMBOL_GPL(sha1_zero_message_hash); + +#define SHA1_CTX(desc) ((struct sha1_ctx *)shash_desc_ctx(desc)) + +static int crypto_sha1_init(struct shash_desc *desc) +{ + sha1_init(SHA1_CTX(desc)); + return 0; +} + +static int crypto_sha1_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + sha1_update(SHA1_CTX(desc), data, len); + return 0; +} + +static int crypto_sha1_final(struct shash_desc *desc, u8 *out) +{ + sha1_final(SHA1_CTX(desc), out); + return 0; +} + +static int crypto_sha1_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) +{ + sha1(data, len, out); + return 0; +} + +static int crypto_sha1_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha1_export(SHA1_CTX(desc), out); +} + +static int crypto_sha1_import(struct shash_desc *desc, const void *in) +{ + return __crypto_sha1_import(SHA1_CTX(desc), in); +} + +static int crypto_sha1_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha1_export_core(SHA1_CTX(desc), out); +} + +static int crypto_sha1_import_core(struct shash_desc *desc, const void *in) +{ + return __crypto_sha1_import_core(SHA1_CTX(desc), in); +} + +#define HMAC_SHA1_KEY(tfm) ((struct hmac_sha1_key *)crypto_shash_ctx(tfm)) +#define HMAC_SHA1_CTX(desc) ((struct hmac_sha1_ctx *)shash_desc_ctx(desc)) + +static int crypto_hmac_sha1_setkey(struct crypto_shash *tfm, + const u8 *raw_key, unsigned int keylen) +{ + hmac_sha1_preparekey(HMAC_SHA1_KEY(tfm), raw_key, keylen); + return 0; +} + +static int crypto_hmac_sha1_init(struct shash_desc *desc) +{ + hmac_sha1_init(HMAC_SHA1_CTX(desc), HMAC_SHA1_KEY(desc->tfm)); + return 0; +} + +static int crypto_hmac_sha1_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + hmac_sha1_update(HMAC_SHA1_CTX(desc), data, len); + return 0; +} + +static int crypto_hmac_sha1_final(struct shash_desc *desc, u8 *out) +{ + hmac_sha1_final(HMAC_SHA1_CTX(desc), out); + return 0; +} + +static int crypto_hmac_sha1_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) +{ + hmac_sha1(HMAC_SHA1_KEY(desc->tfm), data, len, out); + return 0; +} + +static int crypto_hmac_sha1_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha1_export(&HMAC_SHA1_CTX(desc)->sha_ctx, out); +} + +static int crypto_hmac_sha1_import(struct shash_desc *desc, const void *in) +{ + struct hmac_sha1_ctx *ctx = HMAC_SHA1_CTX(desc); + + ctx->ostate = HMAC_SHA1_KEY(desc->tfm)->ostate; + return __crypto_sha1_import(&ctx->sha_ctx, in); +} + +static int crypto_hmac_sha1_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha1_export_core(&HMAC_SHA1_CTX(desc)->sha_ctx, out); +} + +static int crypto_hmac_sha1_import_core(struct shash_desc *desc, const void *in) +{ + struct hmac_sha1_ctx *ctx = HMAC_SHA1_CTX(desc); + + ctx->ostate = HMAC_SHA1_KEY(desc->tfm)->ostate; + return __crypto_sha1_import_core(&ctx->sha_ctx, in); +} + +static struct shash_alg algs[] = { + { + .base.cra_name = "sha1", + .base.cra_driver_name = "sha1-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA1_BLOCK_SIZE, + .base.cra_module = THIS_MODULE, + .digestsize = SHA1_DIGEST_SIZE, + .init = crypto_sha1_init, + .update = crypto_sha1_update, + .final = crypto_sha1_final, + .digest = crypto_sha1_digest, + .export = crypto_sha1_export, + .import = crypto_sha1_import, + .export_core = crypto_sha1_export_core, + .import_core = crypto_sha1_import_core, + .descsize = sizeof(struct sha1_ctx), + .statesize = SHA1_SHASH_STATE_SIZE, + }, + { + .base.cra_name = "hmac(sha1)", + .base.cra_driver_name = "hmac-sha1-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA1_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct hmac_sha1_key), + .base.cra_module = THIS_MODULE, + .digestsize = SHA1_DIGEST_SIZE, + .setkey = crypto_hmac_sha1_setkey, + .init = crypto_hmac_sha1_init, + .update = crypto_hmac_sha1_update, + .final = crypto_hmac_sha1_final, + .digest = crypto_hmac_sha1_digest, + .export = crypto_hmac_sha1_export, + .import = crypto_hmac_sha1_import, + .export_core = crypto_hmac_sha1_export_core, + .import_core = crypto_hmac_sha1_import_core, + .descsize = sizeof(struct hmac_sha1_ctx), + .statesize = SHA1_SHASH_STATE_SIZE, + }, +}; + +static int __init crypto_sha1_mod_init(void) +{ + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} +module_init(crypto_sha1_mod_init); + +static void __exit crypto_sha1_mod_exit(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} +module_exit(crypto_sha1_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Crypto API support for SHA-1 and HMAC-SHA1"); + +MODULE_ALIAS_CRYPTO("sha1"); +MODULE_ALIAS_CRYPTO("sha1-lib"); +MODULE_ALIAS_CRYPTO("hmac(sha1)"); +MODULE_ALIAS_CRYPTO("hmac-sha1-lib"); diff --git a/crypto/sha1_generic.c b/crypto/sha1_generic.c deleted file mode 100644 index 024e8043bab0..000000000000 --- a/crypto/sha1_generic.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API. - * - * SHA1 Secure Hash Algorithm. - * - * Derived from cryptoapi implementation, adapted for in-place - * scatterlist interface. - * - * Copyright (c) Alan Smithee. - * Copyright (c) Andrew McDonald - * Copyright (c) Jean-Francois Dive - */ -#include -#include -#include -#include -#include -#include - -const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE] = { - 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, - 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, - 0xaf, 0xd8, 0x07, 0x09 -}; -EXPORT_SYMBOL_GPL(sha1_zero_message_hash); - -static void sha1_generic_block_fn(struct sha1_state *sst, u8 const *src, - int blocks) -{ - u32 temp[SHA1_WORKSPACE_WORDS]; - - while (blocks--) { - sha1_transform(sst->state, src, temp); - src += SHA1_BLOCK_SIZE; - } - memzero_explicit(temp, sizeof(temp)); -} - -static int crypto_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha1_base_do_update_blocks(desc, data, len, - sha1_generic_block_fn); -} - -static int crypto_sha1_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - sha1_base_do_finup(desc, data, len, sha1_generic_block_fn); - return sha1_base_finish(desc, out); -} - -static struct shash_alg alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_base_init, - .update = crypto_sha1_update, - .finup = crypto_sha1_finup, - .descsize = SHA1_STATE_SIZE, - .base = { - .cra_name = "sha1", - .cra_driver_name= "sha1-generic", - .cra_priority = 100, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; - -static int __init sha1_generic_mod_init(void) -{ - return crypto_register_shash(&alg); -} - -static void __exit sha1_generic_mod_fini(void) -{ - crypto_unregister_shash(&alg); -} - -module_init(sha1_generic_mod_init); -module_exit(sha1_generic_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm"); - -MODULE_ALIAS_CRYPTO("sha1"); -MODULE_ALIAS_CRYPTO("sha1-generic"); diff --git a/crypto/sha256.c b/crypto/sha256.c index 4aeb213bab11..fb81defe084c 100644 --- a/crypto/sha256.c +++ b/crypto/sha256.c @@ -1,17 +1,70 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Crypto API wrapper for the SHA-256 and SHA-224 library functions + * Crypto API support for SHA-224, SHA-256, HMAC-SHA224, and HMAC-SHA256 * * Copyright (c) Jean-Luc Cooke * Copyright (c) Andrew McDonald * Copyright (c) 2002 James Morris * SHA224 Support Copyright 2007 Intel Corporation + * Copyright 2025 Google LLC */ #include -#include +#include #include #include +/* + * Export and import functions. crypto_shash wants a particular format that + * matches that used by some legacy drivers. It currently is the same as the + * library SHA context, except the value in bytecount must be block-aligned and + * the remainder must be stored in an extra u8 appended to the struct. + */ + +#define SHA256_SHASH_STATE_SIZE 105 +static_assert(offsetof(struct __sha256_ctx, state) == 0); +static_assert(offsetof(struct __sha256_ctx, bytecount) == 32); +static_assert(offsetof(struct __sha256_ctx, buf) == 40); +static_assert(sizeof(struct __sha256_ctx) + 1 == SHA256_SHASH_STATE_SIZE); + +static int __crypto_sha256_export(const struct __sha256_ctx *ctx0, void *out) +{ + struct __sha256_ctx ctx = *ctx0; + unsigned int partial; + u8 *p = out; + + partial = ctx.bytecount % SHA256_BLOCK_SIZE; + ctx.bytecount -= partial; + memcpy(p, &ctx, sizeof(ctx)); + p += sizeof(ctx); + *p = partial; + return 0; +} + +static int __crypto_sha256_import(struct __sha256_ctx *ctx, const void *in) +{ + const u8 *p = in; + + memcpy(ctx, p, sizeof(*ctx)); + p += sizeof(*ctx); + ctx->bytecount += *p; + return 0; +} + +static int __crypto_sha256_export_core(const struct __sha256_ctx *ctx, + void *out) +{ + memcpy(out, ctx, offsetof(struct __sha256_ctx, buf)); + return 0; +} + +static int __crypto_sha256_import_core(struct __sha256_ctx *ctx, const void *in) +{ + memcpy(ctx, in, offsetof(struct __sha256_ctx, buf)); + return 0; +} + +/* SHA-224 */ + const u8 sha224_zero_message_hash[SHA224_DIGEST_SIZE] = { 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2, @@ -20,6 +73,56 @@ const u8 sha224_zero_message_hash[SHA224_DIGEST_SIZE] = { }; EXPORT_SYMBOL_GPL(sha224_zero_message_hash); +#define SHA224_CTX(desc) ((struct sha224_ctx *)shash_desc_ctx(desc)) + +static int crypto_sha224_init(struct shash_desc *desc) +{ + sha224_init(SHA224_CTX(desc)); + return 0; +} + +static int crypto_sha224_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + sha224_update(SHA224_CTX(desc), data, len); + return 0; +} + +static int crypto_sha224_final(struct shash_desc *desc, u8 *out) +{ + sha224_final(SHA224_CTX(desc), out); + return 0; +} + +static int crypto_sha224_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) +{ + sha224(data, len, out); + return 0; +} + +static int crypto_sha224_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export(&SHA224_CTX(desc)->ctx, out); +} + +static int crypto_sha224_import(struct shash_desc *desc, const void *in) +{ + return __crypto_sha256_import(&SHA224_CTX(desc)->ctx, in); +} + +static int crypto_sha224_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export_core(&SHA224_CTX(desc)->ctx, out); +} + +static int crypto_sha224_import_core(struct shash_desc *desc, const void *in) +{ + return __crypto_sha256_import_core(&SHA224_CTX(desc)->ctx, in); +} + +/* SHA-256 */ + const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE] = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, @@ -28,256 +131,289 @@ const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE] = { }; EXPORT_SYMBOL_GPL(sha256_zero_message_hash); +#define SHA256_CTX(desc) ((struct sha256_ctx *)shash_desc_ctx(desc)) + static int crypto_sha256_init(struct shash_desc *desc) { - sha256_block_init(shash_desc_ctx(desc)); + sha256_init(SHA256_CTX(desc)); return 0; } -static inline int crypto_sha256_update(struct shash_desc *desc, const u8 *data, - unsigned int len, bool force_generic) +static int crypto_sha256_update(struct shash_desc *desc, + const u8 *data, unsigned int len) { - struct crypto_sha256_state *sctx = shash_desc_ctx(desc); - int remain = len % SHA256_BLOCK_SIZE; - - sctx->count += len - remain; - sha256_choose_blocks(sctx->state, data, len / SHA256_BLOCK_SIZE, - force_generic, !force_generic); - return remain; -} - -static int crypto_sha256_update_generic(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return crypto_sha256_update(desc, data, len, true); -} - -static int crypto_sha256_update_lib(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - sha256_update(shash_desc_ctx(desc), data, len); + sha256_update(SHA256_CTX(desc), data, len); return 0; } -static int crypto_sha256_update_arch(struct shash_desc *desc, const u8 *data, - unsigned int len) +static int crypto_sha256_final(struct shash_desc *desc, u8 *out) { - return crypto_sha256_update(desc, data, len, false); -} - -static int crypto_sha256_final_lib(struct shash_desc *desc, u8 *out) -{ - sha256_final(shash_desc_ctx(desc), out); + sha256_final(SHA256_CTX(desc), out); return 0; } -static __always_inline int crypto_sha256_finup(struct shash_desc *desc, - const u8 *data, - unsigned int len, u8 *out, - bool force_generic) -{ - struct crypto_sha256_state *sctx = shash_desc_ctx(desc); - unsigned int remain = len; - u8 *buf; - - if (len >= SHA256_BLOCK_SIZE) - remain = crypto_sha256_update(desc, data, len, force_generic); - sctx->count += remain; - buf = memcpy(sctx + 1, data + len - remain, remain); - sha256_finup(sctx, buf, remain, out, - crypto_shash_digestsize(desc->tfm), force_generic, - !force_generic); - return 0; -} - -static int crypto_sha256_finup_generic(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return crypto_sha256_finup(desc, data, len, out, true); -} - -static int crypto_sha256_finup_arch(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - return crypto_sha256_finup(desc, data, len, out, false); -} - -static int crypto_sha256_digest_generic(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - crypto_sha256_init(desc); - return crypto_sha256_finup_generic(desc, data, len, out); -} - -static int crypto_sha256_digest_lib(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) +static int crypto_sha256_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) { sha256(data, len, out); return 0; } -static int crypto_sha256_digest_arch(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) +static int crypto_sha256_export(struct shash_desc *desc, void *out) { - crypto_sha256_init(desc); - return crypto_sha256_finup_arch(desc, data, len, out); + return __crypto_sha256_export(&SHA256_CTX(desc)->ctx, out); } -static int crypto_sha224_init(struct shash_desc *desc) +static int crypto_sha256_import(struct shash_desc *desc, const void *in) { - sha224_block_init(shash_desc_ctx(desc)); + return __crypto_sha256_import(&SHA256_CTX(desc)->ctx, in); +} + +static int crypto_sha256_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export_core(&SHA256_CTX(desc)->ctx, out); +} + +static int crypto_sha256_import_core(struct shash_desc *desc, const void *in) +{ + return __crypto_sha256_import_core(&SHA256_CTX(desc)->ctx, in); +} + +/* HMAC-SHA224 */ + +#define HMAC_SHA224_KEY(tfm) ((struct hmac_sha224_key *)crypto_shash_ctx(tfm)) +#define HMAC_SHA224_CTX(desc) ((struct hmac_sha224_ctx *)shash_desc_ctx(desc)) + +static int crypto_hmac_sha224_setkey(struct crypto_shash *tfm, + const u8 *raw_key, unsigned int keylen) +{ + hmac_sha224_preparekey(HMAC_SHA224_KEY(tfm), raw_key, keylen); return 0; } -static int crypto_sha224_final_lib(struct shash_desc *desc, u8 *out) +static int crypto_hmac_sha224_init(struct shash_desc *desc) { - sha224_final(shash_desc_ctx(desc), out); + hmac_sha224_init(HMAC_SHA224_CTX(desc), HMAC_SHA224_KEY(desc->tfm)); return 0; } -static int crypto_sha256_import_lib(struct shash_desc *desc, const void *in) +static int crypto_hmac_sha224_update(struct shash_desc *desc, + const u8 *data, unsigned int len) { - struct sha256_state *sctx = shash_desc_ctx(desc); - const u8 *p = in; - - memcpy(sctx, p, sizeof(*sctx)); - p += sizeof(*sctx); - sctx->count += *p; + hmac_sha224_update(HMAC_SHA224_CTX(desc), data, len); return 0; } -static int crypto_sha256_export_lib(struct shash_desc *desc, void *out) +static int crypto_hmac_sha224_final(struct shash_desc *desc, u8 *out) { - struct sha256_state *sctx0 = shash_desc_ctx(desc); - struct sha256_state sctx = *sctx0; - unsigned int partial; - u8 *p = out; - - partial = sctx.count % SHA256_BLOCK_SIZE; - sctx.count -= partial; - memcpy(p, &sctx, sizeof(sctx)); - p += sizeof(sctx); - *p = partial; + hmac_sha224_final(HMAC_SHA224_CTX(desc), out); return 0; } +static int crypto_hmac_sha224_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, + u8 *out) +{ + hmac_sha224(HMAC_SHA224_KEY(desc->tfm), data, len, out); + return 0; +} + +static int crypto_hmac_sha224_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export(&HMAC_SHA224_CTX(desc)->ctx.sha_ctx, out); +} + +static int crypto_hmac_sha224_import(struct shash_desc *desc, const void *in) +{ + struct hmac_sha224_ctx *ctx = HMAC_SHA224_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA224_KEY(desc->tfm)->key.ostate; + return __crypto_sha256_import(&ctx->ctx.sha_ctx, in); +} + +static int crypto_hmac_sha224_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export_core(&HMAC_SHA224_CTX(desc)->ctx.sha_ctx, + out); +} + +static int crypto_hmac_sha224_import_core(struct shash_desc *desc, + const void *in) +{ + struct hmac_sha224_ctx *ctx = HMAC_SHA224_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA224_KEY(desc->tfm)->key.ostate; + return __crypto_sha256_import_core(&ctx->ctx.sha_ctx, in); +} + +/* HMAC-SHA256 */ + +#define HMAC_SHA256_KEY(tfm) ((struct hmac_sha256_key *)crypto_shash_ctx(tfm)) +#define HMAC_SHA256_CTX(desc) ((struct hmac_sha256_ctx *)shash_desc_ctx(desc)) + +static int crypto_hmac_sha256_setkey(struct crypto_shash *tfm, + const u8 *raw_key, unsigned int keylen) +{ + hmac_sha256_preparekey(HMAC_SHA256_KEY(tfm), raw_key, keylen); + return 0; +} + +static int crypto_hmac_sha256_init(struct shash_desc *desc) +{ + hmac_sha256_init(HMAC_SHA256_CTX(desc), HMAC_SHA256_KEY(desc->tfm)); + return 0; +} + +static int crypto_hmac_sha256_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + hmac_sha256_update(HMAC_SHA256_CTX(desc), data, len); + return 0; +} + +static int crypto_hmac_sha256_final(struct shash_desc *desc, u8 *out) +{ + hmac_sha256_final(HMAC_SHA256_CTX(desc), out); + return 0; +} + +static int crypto_hmac_sha256_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, + u8 *out) +{ + hmac_sha256(HMAC_SHA256_KEY(desc->tfm), data, len, out); + return 0; +} + +static int crypto_hmac_sha256_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export(&HMAC_SHA256_CTX(desc)->ctx.sha_ctx, out); +} + +static int crypto_hmac_sha256_import(struct shash_desc *desc, const void *in) +{ + struct hmac_sha256_ctx *ctx = HMAC_SHA256_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA256_KEY(desc->tfm)->key.ostate; + return __crypto_sha256_import(&ctx->ctx.sha_ctx, in); +} + +static int crypto_hmac_sha256_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha256_export_core(&HMAC_SHA256_CTX(desc)->ctx.sha_ctx, + out); +} + +static int crypto_hmac_sha256_import_core(struct shash_desc *desc, + const void *in) +{ + struct hmac_sha256_ctx *ctx = HMAC_SHA256_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA256_KEY(desc->tfm)->key.ostate; + return __crypto_sha256_import_core(&ctx->ctx.sha_ctx, in); +} + +/* Algorithm definitions */ + static struct shash_alg algs[] = { - { - .base.cra_name = "sha256", - .base.cra_driver_name = "sha256-generic", - .base.cra_priority = 100, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA256_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, - .digestsize = SHA256_DIGEST_SIZE, - .init = crypto_sha256_init, - .update = crypto_sha256_update_generic, - .finup = crypto_sha256_finup_generic, - .digest = crypto_sha256_digest_generic, - .descsize = sizeof(struct crypto_sha256_state), - }, { .base.cra_name = "sha224", - .base.cra_driver_name = "sha224-generic", - .base.cra_priority = 100, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, + .base.cra_driver_name = "sha224-lib", + .base.cra_priority = 300, .base.cra_blocksize = SHA224_BLOCK_SIZE, .base.cra_module = THIS_MODULE, .digestsize = SHA224_DIGEST_SIZE, .init = crypto_sha224_init, - .update = crypto_sha256_update_generic, - .finup = crypto_sha256_finup_generic, - .descsize = sizeof(struct crypto_sha256_state), + .update = crypto_sha224_update, + .final = crypto_sha224_final, + .digest = crypto_sha224_digest, + .export = crypto_sha224_export, + .import = crypto_sha224_import, + .export_core = crypto_sha224_export_core, + .import_core = crypto_sha224_import_core, + .descsize = sizeof(struct sha224_ctx), + .statesize = SHA256_SHASH_STATE_SIZE, }, { .base.cra_name = "sha256", .base.cra_driver_name = "sha256-lib", + .base.cra_priority = 300, .base.cra_blocksize = SHA256_BLOCK_SIZE, .base.cra_module = THIS_MODULE, .digestsize = SHA256_DIGEST_SIZE, .init = crypto_sha256_init, - .update = crypto_sha256_update_lib, - .final = crypto_sha256_final_lib, - .digest = crypto_sha256_digest_lib, - .descsize = sizeof(struct sha256_state), - .statesize = sizeof(struct crypto_sha256_state) + - SHA256_BLOCK_SIZE + 1, - .import = crypto_sha256_import_lib, - .export = crypto_sha256_export_lib, + .update = crypto_sha256_update, + .final = crypto_sha256_final, + .digest = crypto_sha256_digest, + .export = crypto_sha256_export, + .import = crypto_sha256_import, + .export_core = crypto_sha256_export_core, + .import_core = crypto_sha256_import_core, + .descsize = sizeof(struct sha256_ctx), + .statesize = SHA256_SHASH_STATE_SIZE, }, { - .base.cra_name = "sha224", - .base.cra_driver_name = "sha224-lib", + .base.cra_name = "hmac(sha224)", + .base.cra_driver_name = "hmac-sha224-lib", + .base.cra_priority = 300, .base.cra_blocksize = SHA224_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct hmac_sha224_key), .base.cra_module = THIS_MODULE, .digestsize = SHA224_DIGEST_SIZE, - .init = crypto_sha224_init, - .update = crypto_sha256_update_lib, - .final = crypto_sha224_final_lib, - .descsize = sizeof(struct sha256_state), - .statesize = sizeof(struct crypto_sha256_state) + - SHA256_BLOCK_SIZE + 1, - .import = crypto_sha256_import_lib, - .export = crypto_sha256_export_lib, + .setkey = crypto_hmac_sha224_setkey, + .init = crypto_hmac_sha224_init, + .update = crypto_hmac_sha224_update, + .final = crypto_hmac_sha224_final, + .digest = crypto_hmac_sha224_digest, + .export = crypto_hmac_sha224_export, + .import = crypto_hmac_sha224_import, + .export_core = crypto_hmac_sha224_export_core, + .import_core = crypto_hmac_sha224_import_core, + .descsize = sizeof(struct hmac_sha224_ctx), + .statesize = SHA256_SHASH_STATE_SIZE, }, { - .base.cra_name = "sha256", - .base.cra_driver_name = "sha256-" __stringify(ARCH), + .base.cra_name = "hmac(sha256)", + .base.cra_driver_name = "hmac-sha256-lib", .base.cra_priority = 300, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, .base.cra_blocksize = SHA256_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct hmac_sha256_key), .base.cra_module = THIS_MODULE, .digestsize = SHA256_DIGEST_SIZE, - .init = crypto_sha256_init, - .update = crypto_sha256_update_arch, - .finup = crypto_sha256_finup_arch, - .digest = crypto_sha256_digest_arch, - .descsize = sizeof(struct crypto_sha256_state), - }, - { - .base.cra_name = "sha224", - .base.cra_driver_name = "sha224-" __stringify(ARCH), - .base.cra_priority = 300, - .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .base.cra_blocksize = SHA224_BLOCK_SIZE, - .base.cra_module = THIS_MODULE, - .digestsize = SHA224_DIGEST_SIZE, - .init = crypto_sha224_init, - .update = crypto_sha256_update_arch, - .finup = crypto_sha256_finup_arch, - .descsize = sizeof(struct crypto_sha256_state), + .setkey = crypto_hmac_sha256_setkey, + .init = crypto_hmac_sha256_init, + .update = crypto_hmac_sha256_update, + .final = crypto_hmac_sha256_final, + .digest = crypto_hmac_sha256_digest, + .export = crypto_hmac_sha256_export, + .import = crypto_hmac_sha256_import, + .export_core = crypto_hmac_sha256_export_core, + .import_core = crypto_hmac_sha256_import_core, + .descsize = sizeof(struct hmac_sha256_ctx), + .statesize = SHA256_SHASH_STATE_SIZE, }, }; -static unsigned int num_algs; - static int __init crypto_sha256_mod_init(void) { - /* register the arch flavours only if they differ from generic */ - num_algs = ARRAY_SIZE(algs); - BUILD_BUG_ON(ARRAY_SIZE(algs) <= 2); - if (!sha256_is_arch_optimized()) - num_algs -= 2; return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } module_init(crypto_sha256_mod_init); static void __exit crypto_sha256_mod_exit(void) { - crypto_unregister_shashes(algs, num_algs); + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); } module_exit(crypto_sha256_mod_exit); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Crypto API wrapper for the SHA-256 and SHA-224 library functions"); +MODULE_DESCRIPTION("Crypto API support for SHA-224, SHA-256, HMAC-SHA224, and HMAC-SHA256"); -MODULE_ALIAS_CRYPTO("sha256"); -MODULE_ALIAS_CRYPTO("sha256-generic"); -MODULE_ALIAS_CRYPTO("sha256-" __stringify(ARCH)); MODULE_ALIAS_CRYPTO("sha224"); -MODULE_ALIAS_CRYPTO("sha224-generic"); -MODULE_ALIAS_CRYPTO("sha224-" __stringify(ARCH)); +MODULE_ALIAS_CRYPTO("sha224-lib"); +MODULE_ALIAS_CRYPTO("sha256"); +MODULE_ALIAS_CRYPTO("sha256-lib"); +MODULE_ALIAS_CRYPTO("hmac(sha224)"); +MODULE_ALIAS_CRYPTO("hmac-sha224-lib"); +MODULE_ALIAS_CRYPTO("hmac(sha256)"); +MODULE_ALIAS_CRYPTO("hmac-sha256-lib"); diff --git a/crypto/sha512.c b/crypto/sha512.c new file mode 100644 index 000000000000..d320fe53913f --- /dev/null +++ b/crypto/sha512.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Crypto API support for SHA-384, SHA-512, HMAC-SHA384, and HMAC-SHA512 + * + * Copyright (c) Jean-Luc Cooke + * Copyright (c) Andrew McDonald + * Copyright (c) 2003 Kyle McMartin + * Copyright 2025 Google LLC + */ +#include +#include +#include +#include + +/* + * Export and import functions. crypto_shash wants a particular format that + * matches that used by some legacy drivers. It currently is the same as the + * library SHA context, except the value in bytecount_lo must be block-aligned + * and the remainder must be stored in an extra u8 appended to the struct. + */ + +#define SHA512_SHASH_STATE_SIZE 209 +static_assert(offsetof(struct __sha512_ctx, state) == 0); +static_assert(offsetof(struct __sha512_ctx, bytecount_lo) == 64); +static_assert(offsetof(struct __sha512_ctx, bytecount_hi) == 72); +static_assert(offsetof(struct __sha512_ctx, buf) == 80); +static_assert(sizeof(struct __sha512_ctx) + 1 == SHA512_SHASH_STATE_SIZE); + +static int __crypto_sha512_export(const struct __sha512_ctx *ctx0, void *out) +{ + struct __sha512_ctx ctx = *ctx0; + unsigned int partial; + u8 *p = out; + + partial = ctx.bytecount_lo % SHA512_BLOCK_SIZE; + ctx.bytecount_lo -= partial; + memcpy(p, &ctx, sizeof(ctx)); + p += sizeof(ctx); + *p = partial; + return 0; +} + +static int __crypto_sha512_import(struct __sha512_ctx *ctx, const void *in) +{ + const u8 *p = in; + + memcpy(ctx, p, sizeof(*ctx)); + p += sizeof(*ctx); + ctx->bytecount_lo += *p; + return 0; +} + +static int __crypto_sha512_export_core(const struct __sha512_ctx *ctx, + void *out) +{ + memcpy(out, ctx, offsetof(struct __sha512_ctx, buf)); + return 0; +} + +static int __crypto_sha512_import_core(struct __sha512_ctx *ctx, const void *in) +{ + memcpy(ctx, in, offsetof(struct __sha512_ctx, buf)); + return 0; +} + +/* SHA-384 */ + +const u8 sha384_zero_message_hash[SHA384_DIGEST_SIZE] = { + 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, + 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, + 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, + 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, + 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, + 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b +}; +EXPORT_SYMBOL_GPL(sha384_zero_message_hash); + +#define SHA384_CTX(desc) ((struct sha384_ctx *)shash_desc_ctx(desc)) + +static int crypto_sha384_init(struct shash_desc *desc) +{ + sha384_init(SHA384_CTX(desc)); + return 0; +} + +static int crypto_sha384_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + sha384_update(SHA384_CTX(desc), data, len); + return 0; +} + +static int crypto_sha384_final(struct shash_desc *desc, u8 *out) +{ + sha384_final(SHA384_CTX(desc), out); + return 0; +} + +static int crypto_sha384_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) +{ + sha384(data, len, out); + return 0; +} + +static int crypto_sha384_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export(&SHA384_CTX(desc)->ctx, out); +} + +static int crypto_sha384_import(struct shash_desc *desc, const void *in) +{ + return __crypto_sha512_import(&SHA384_CTX(desc)->ctx, in); +} + +static int crypto_sha384_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export_core(&SHA384_CTX(desc)->ctx, out); +} + +static int crypto_sha384_import_core(struct shash_desc *desc, const void *in) +{ + return __crypto_sha512_import_core(&SHA384_CTX(desc)->ctx, in); +} + +/* SHA-512 */ + +const u8 sha512_zero_message_hash[SHA512_DIGEST_SIZE] = { + 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, + 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, + 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, + 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, + 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, + 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, + 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, + 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e +}; +EXPORT_SYMBOL_GPL(sha512_zero_message_hash); + +#define SHA512_CTX(desc) ((struct sha512_ctx *)shash_desc_ctx(desc)) + +static int crypto_sha512_init(struct shash_desc *desc) +{ + sha512_init(SHA512_CTX(desc)); + return 0; +} + +static int crypto_sha512_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + sha512_update(SHA512_CTX(desc), data, len); + return 0; +} + +static int crypto_sha512_final(struct shash_desc *desc, u8 *out) +{ + sha512_final(SHA512_CTX(desc), out); + return 0; +} + +static int crypto_sha512_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, u8 *out) +{ + sha512(data, len, out); + return 0; +} + +static int crypto_sha512_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export(&SHA512_CTX(desc)->ctx, out); +} + +static int crypto_sha512_import(struct shash_desc *desc, const void *in) +{ + return __crypto_sha512_import(&SHA512_CTX(desc)->ctx, in); +} + +static int crypto_sha512_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export_core(&SHA512_CTX(desc)->ctx, out); +} + +static int crypto_sha512_import_core(struct shash_desc *desc, const void *in) +{ + return __crypto_sha512_import_core(&SHA512_CTX(desc)->ctx, in); +} + +/* HMAC-SHA384 */ + +#define HMAC_SHA384_KEY(tfm) ((struct hmac_sha384_key *)crypto_shash_ctx(tfm)) +#define HMAC_SHA384_CTX(desc) ((struct hmac_sha384_ctx *)shash_desc_ctx(desc)) + +static int crypto_hmac_sha384_setkey(struct crypto_shash *tfm, + const u8 *raw_key, unsigned int keylen) +{ + hmac_sha384_preparekey(HMAC_SHA384_KEY(tfm), raw_key, keylen); + return 0; +} + +static int crypto_hmac_sha384_init(struct shash_desc *desc) +{ + hmac_sha384_init(HMAC_SHA384_CTX(desc), HMAC_SHA384_KEY(desc->tfm)); + return 0; +} + +static int crypto_hmac_sha384_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + hmac_sha384_update(HMAC_SHA384_CTX(desc), data, len); + return 0; +} + +static int crypto_hmac_sha384_final(struct shash_desc *desc, u8 *out) +{ + hmac_sha384_final(HMAC_SHA384_CTX(desc), out); + return 0; +} + +static int crypto_hmac_sha384_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, + u8 *out) +{ + hmac_sha384(HMAC_SHA384_KEY(desc->tfm), data, len, out); + return 0; +} + +static int crypto_hmac_sha384_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export(&HMAC_SHA384_CTX(desc)->ctx.sha_ctx, out); +} + +static int crypto_hmac_sha384_import(struct shash_desc *desc, const void *in) +{ + struct hmac_sha384_ctx *ctx = HMAC_SHA384_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA384_KEY(desc->tfm)->key.ostate; + return __crypto_sha512_import(&ctx->ctx.sha_ctx, in); +} + +static int crypto_hmac_sha384_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export_core(&HMAC_SHA384_CTX(desc)->ctx.sha_ctx, + out); +} + +static int crypto_hmac_sha384_import_core(struct shash_desc *desc, + const void *in) +{ + struct hmac_sha384_ctx *ctx = HMAC_SHA384_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA384_KEY(desc->tfm)->key.ostate; + return __crypto_sha512_import_core(&ctx->ctx.sha_ctx, in); +} + +/* HMAC-SHA512 */ + +#define HMAC_SHA512_KEY(tfm) ((struct hmac_sha512_key *)crypto_shash_ctx(tfm)) +#define HMAC_SHA512_CTX(desc) ((struct hmac_sha512_ctx *)shash_desc_ctx(desc)) + +static int crypto_hmac_sha512_setkey(struct crypto_shash *tfm, + const u8 *raw_key, unsigned int keylen) +{ + hmac_sha512_preparekey(HMAC_SHA512_KEY(tfm), raw_key, keylen); + return 0; +} + +static int crypto_hmac_sha512_init(struct shash_desc *desc) +{ + hmac_sha512_init(HMAC_SHA512_CTX(desc), HMAC_SHA512_KEY(desc->tfm)); + return 0; +} + +static int crypto_hmac_sha512_update(struct shash_desc *desc, + const u8 *data, unsigned int len) +{ + hmac_sha512_update(HMAC_SHA512_CTX(desc), data, len); + return 0; +} + +static int crypto_hmac_sha512_final(struct shash_desc *desc, u8 *out) +{ + hmac_sha512_final(HMAC_SHA512_CTX(desc), out); + return 0; +} + +static int crypto_hmac_sha512_digest(struct shash_desc *desc, + const u8 *data, unsigned int len, + u8 *out) +{ + hmac_sha512(HMAC_SHA512_KEY(desc->tfm), data, len, out); + return 0; +} + +static int crypto_hmac_sha512_export(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export(&HMAC_SHA512_CTX(desc)->ctx.sha_ctx, out); +} + +static int crypto_hmac_sha512_import(struct shash_desc *desc, const void *in) +{ + struct hmac_sha512_ctx *ctx = HMAC_SHA512_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA512_KEY(desc->tfm)->key.ostate; + return __crypto_sha512_import(&ctx->ctx.sha_ctx, in); +} + +static int crypto_hmac_sha512_export_core(struct shash_desc *desc, void *out) +{ + return __crypto_sha512_export_core(&HMAC_SHA512_CTX(desc)->ctx.sha_ctx, + out); +} + +static int crypto_hmac_sha512_import_core(struct shash_desc *desc, + const void *in) +{ + struct hmac_sha512_ctx *ctx = HMAC_SHA512_CTX(desc); + + ctx->ctx.ostate = HMAC_SHA512_KEY(desc->tfm)->key.ostate; + return __crypto_sha512_import_core(&ctx->ctx.sha_ctx, in); +} + +/* Algorithm definitions */ + +static struct shash_alg algs[] = { + { + .base.cra_name = "sha384", + .base.cra_driver_name = "sha384-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA384_BLOCK_SIZE, + .base.cra_module = THIS_MODULE, + .digestsize = SHA384_DIGEST_SIZE, + .init = crypto_sha384_init, + .update = crypto_sha384_update, + .final = crypto_sha384_final, + .digest = crypto_sha384_digest, + .export = crypto_sha384_export, + .import = crypto_sha384_import, + .export_core = crypto_sha384_export_core, + .import_core = crypto_sha384_import_core, + .descsize = sizeof(struct sha384_ctx), + .statesize = SHA512_SHASH_STATE_SIZE, + }, + { + .base.cra_name = "sha512", + .base.cra_driver_name = "sha512-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA512_BLOCK_SIZE, + .base.cra_module = THIS_MODULE, + .digestsize = SHA512_DIGEST_SIZE, + .init = crypto_sha512_init, + .update = crypto_sha512_update, + .final = crypto_sha512_final, + .digest = crypto_sha512_digest, + .export = crypto_sha512_export, + .import = crypto_sha512_import, + .export_core = crypto_sha512_export_core, + .import_core = crypto_sha512_import_core, + .descsize = sizeof(struct sha512_ctx), + .statesize = SHA512_SHASH_STATE_SIZE, + }, + { + .base.cra_name = "hmac(sha384)", + .base.cra_driver_name = "hmac-sha384-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA384_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct hmac_sha384_key), + .base.cra_module = THIS_MODULE, + .digestsize = SHA384_DIGEST_SIZE, + .setkey = crypto_hmac_sha384_setkey, + .init = crypto_hmac_sha384_init, + .update = crypto_hmac_sha384_update, + .final = crypto_hmac_sha384_final, + .digest = crypto_hmac_sha384_digest, + .export = crypto_hmac_sha384_export, + .import = crypto_hmac_sha384_import, + .export_core = crypto_hmac_sha384_export_core, + .import_core = crypto_hmac_sha384_import_core, + .descsize = sizeof(struct hmac_sha384_ctx), + .statesize = SHA512_SHASH_STATE_SIZE, + }, + { + .base.cra_name = "hmac(sha512)", + .base.cra_driver_name = "hmac-sha512-lib", + .base.cra_priority = 300, + .base.cra_blocksize = SHA512_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct hmac_sha512_key), + .base.cra_module = THIS_MODULE, + .digestsize = SHA512_DIGEST_SIZE, + .setkey = crypto_hmac_sha512_setkey, + .init = crypto_hmac_sha512_init, + .update = crypto_hmac_sha512_update, + .final = crypto_hmac_sha512_final, + .digest = crypto_hmac_sha512_digest, + .export = crypto_hmac_sha512_export, + .import = crypto_hmac_sha512_import, + .export_core = crypto_hmac_sha512_export_core, + .import_core = crypto_hmac_sha512_import_core, + .descsize = sizeof(struct hmac_sha512_ctx), + .statesize = SHA512_SHASH_STATE_SIZE, + }, +}; + +static int __init crypto_sha512_mod_init(void) +{ + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} +module_init(crypto_sha512_mod_init); + +static void __exit crypto_sha512_mod_exit(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} +module_exit(crypto_sha512_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Crypto API support for SHA-384, SHA-512, HMAC-SHA384, and HMAC-SHA512"); + +MODULE_ALIAS_CRYPTO("sha384"); +MODULE_ALIAS_CRYPTO("sha384-lib"); +MODULE_ALIAS_CRYPTO("sha512"); +MODULE_ALIAS_CRYPTO("sha512-lib"); +MODULE_ALIAS_CRYPTO("hmac(sha384)"); +MODULE_ALIAS_CRYPTO("hmac-sha384-lib"); +MODULE_ALIAS_CRYPTO("hmac(sha512)"); +MODULE_ALIAS_CRYPTO("hmac-sha512-lib"); diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c deleted file mode 100644 index 7368173f545e..000000000000 --- a/crypto/sha512_generic.c +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* SHA-512 code by Jean-Luc Cooke - * - * Copyright (c) Jean-Luc Cooke - * Copyright (c) Andrew McDonald - * Copyright (c) 2003 Kyle McMartin - */ -#include -#include -#include -#include -#include -#include - -const u8 sha384_zero_message_hash[SHA384_DIGEST_SIZE] = { - 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, - 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, - 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, - 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, - 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, - 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b -}; -EXPORT_SYMBOL_GPL(sha384_zero_message_hash); - -const u8 sha512_zero_message_hash[SHA512_DIGEST_SIZE] = { - 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, - 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, - 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, - 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, - 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, - 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, - 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, - 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e -}; -EXPORT_SYMBOL_GPL(sha512_zero_message_hash); - -static inline u64 Ch(u64 x, u64 y, u64 z) -{ - return z ^ (x & (y ^ z)); -} - -static inline u64 Maj(u64 x, u64 y, u64 z) -{ - return (x & y) | (z & (x | y)); -} - -static const u64 sha512_K[80] = { - 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, - 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, - 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, - 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, - 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, - 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, - 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, - 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, - 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, - 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, - 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, - 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, - 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, - 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, -}; - -#define e0(x) (ror64(x,28) ^ ror64(x,34) ^ ror64(x,39)) -#define e1(x) (ror64(x,14) ^ ror64(x,18) ^ ror64(x,41)) -#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) -#define s1(x) (ror64(x,19) ^ ror64(x,61) ^ (x >> 6)) - -static inline void LOAD_OP(int I, u64 *W, const u8 *input) -{ - W[I] = get_unaligned_be64((__u64 *)input + I); -} - -static inline void BLEND_OP(int I, u64 *W) -{ - W[I & 15] += s1(W[(I-2) & 15]) + W[(I-7) & 15] + s0(W[(I-15) & 15]); -} - -static void -sha512_transform(u64 *state, const u8 *input) -{ - u64 a, b, c, d, e, f, g, h, t1, t2; - - int i; - u64 W[16]; - - /* load the state into our registers */ - a=state[0]; b=state[1]; c=state[2]; d=state[3]; - e=state[4]; f=state[5]; g=state[6]; h=state[7]; - - /* now iterate */ - for (i=0; i<80; i+=8) { - if (!(i & 8)) { - int j; - - if (i < 16) { - /* load the input */ - for (j = 0; j < 16; j++) - LOAD_OP(i + j, W, input); - } else { - for (j = 0; j < 16; j++) { - BLEND_OP(i + j, W); - } - } - } - - t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[(i & 15)]; - t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; - t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[(i & 15) + 1]; - t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; - t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[(i & 15) + 2]; - t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; - t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[(i & 15) + 3]; - t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; - t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[(i & 15) + 4]; - t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; - t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[(i & 15) + 5]; - t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; - t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[(i & 15) + 6]; - t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; - t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[(i & 15) + 7]; - t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; - } - - state[0] += a; state[1] += b; state[2] += c; state[3] += d; - state[4] += e; state[5] += f; state[6] += g; state[7] += h; -} - -void sha512_generic_block_fn(struct sha512_state *sst, u8 const *src, - int blocks) -{ - do { - sha512_transform(sst->state, src); - src += SHA512_BLOCK_SIZE; - } while (--blocks); -} -EXPORT_SYMBOL_GPL(sha512_generic_block_fn); - -static int crypto_sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - return sha512_base_do_update_blocks(desc, data, len, - sha512_generic_block_fn); -} - -static int crypto_sha512_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *hash) -{ - sha512_base_do_finup(desc, data, len, sha512_generic_block_fn); - return sha512_base_finish(desc, hash); -} - -static struct shash_alg sha512_algs[2] = { { - .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_base_init, - .update = crypto_sha512_update, - .finup = crypto_sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha512", - .cra_driver_name = "sha512-generic", - .cra_priority = 100, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}, { - .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_base_init, - .update = crypto_sha512_update, - .finup = crypto_sha512_finup, - .descsize = SHA512_STATE_SIZE, - .base = { - .cra_name = "sha384", - .cra_driver_name = "sha384-generic", - .cra_priority = 100, - .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY | - CRYPTO_AHASH_ALG_FINUP_MAX, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -} }; - -static int __init sha512_generic_mod_init(void) -{ - return crypto_register_shashes(sha512_algs, ARRAY_SIZE(sha512_algs)); -} - -static void __exit sha512_generic_mod_fini(void) -{ - crypto_unregister_shashes(sha512_algs, ARRAY_SIZE(sha512_algs)); -} - -module_init(sha512_generic_mod_init); -module_exit(sha512_generic_mod_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SHA-512 and SHA-384 Secure Hash Algorithms"); - -MODULE_ALIAS_CRYPTO("sha384"); -MODULE_ALIAS_CRYPTO("sha384-generic"); -MODULE_ALIAS_CRYPTO("sha512"); -MODULE_ALIAS_CRYPTO("sha512-generic"); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 72005074a5c2..ee33ba21ae2b 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -45,6 +45,7 @@ static bool notests; module_param(notests, bool, 0644); MODULE_PARM_DESC(notests, "disable all crypto self-tests"); +#ifdef CONFIG_CRYPTO_SELFTESTS_FULL static bool noslowtests; module_param(noslowtests, bool, 0644); MODULE_PARM_DESC(noslowtests, "disable slow crypto self-tests"); @@ -52,6 +53,10 @@ MODULE_PARM_DESC(noslowtests, "disable slow crypto self-tests"); static unsigned int fuzz_iterations = 100; module_param(fuzz_iterations, uint, 0644); MODULE_PARM_DESC(fuzz_iterations, "number of fuzz test iterations"); +#else +#define noslowtests 1 +#define fuzz_iterations 0 +#endif #ifndef CONFIG_CRYPTO_SELFTESTS @@ -319,9 +324,9 @@ struct testvec_config { /* * The following are the lists of testvec_configs to test for each algorithm - * type when the fast crypto self-tests are enabled. They aim to provide good - * test coverage, while keeping the test time much shorter than the full tests - * so that the fast tests can be used to fulfill FIPS 140 testing requirements. + * type when the "fast" crypto self-tests are enabled. They aim to provide good + * test coverage, while keeping the test time much shorter than the "full" tests + * so that the "fast" tests can be enabled in a wider range of circumstances. */ /* Configs for skciphers and aeads */ @@ -1183,14 +1188,18 @@ static void generate_random_testvec_config(struct rnd_state *rng, static void crypto_disable_simd_for_test(void) { +#ifdef CONFIG_CRYPTO_SELFTESTS_FULL migrate_disable(); __this_cpu_write(crypto_simd_disabled_for_test, true); +#endif } static void crypto_reenable_simd_for_test(void) { +#ifdef CONFIG_CRYPTO_SELFTESTS_FULL __this_cpu_write(crypto_simd_disabled_for_test, false); migrate_enable(); +#endif } /* @@ -3541,59 +3550,6 @@ static int alg_test_comp(const struct alg_test_desc *desc, const char *driver, return err; } -static int alg_test_crc32c(const struct alg_test_desc *desc, - const char *driver, u32 type, u32 mask) -{ - struct crypto_shash *tfm; - __le32 val; - int err; - - err = alg_test_hash(desc, driver, type, mask); - if (err) - return err; - - tfm = crypto_alloc_shash(driver, type, mask); - if (IS_ERR(tfm)) { - if (PTR_ERR(tfm) == -ENOENT) { - /* - * This crc32c implementation is only available through - * ahash API, not the shash API, so the remaining part - * of the test is not applicable to it. - */ - return 0; - } - printk(KERN_ERR "alg: crc32c: Failed to load transform for %s: " - "%ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - driver = crypto_shash_driver_name(tfm); - - do { - SHASH_DESC_ON_STACK(shash, tfm); - u32 *ctx = (u32 *)shash_desc_ctx(shash); - - shash->tfm = tfm; - - *ctx = 420553207; - err = crypto_shash_final(shash, (u8 *)&val); - if (err) { - printk(KERN_ERR "alg: crc32c: Operation failed for " - "%s: %d\n", driver, err); - break; - } - - if (val != cpu_to_le32(~420553207)) { - pr_err("alg: crc32c: Test failed for %s: %u\n", - driver, le32_to_cpu(val)); - err = -EINVAL; - } - } while (0); - - crypto_free_shash(tfm); - - return err; -} - static int alg_test_cprng(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask) { @@ -4228,19 +4184,21 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "authenc(hmac(sha1),cbc(aes))", + .generic_driver = "authenc(hmac-sha1-lib,cbc(aes-generic))", .test = alg_test_aead, - .fips_allowed = 1, .suite = { .aead = __VECS(hmac_sha1_aes_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha1),cbc(des))", + .generic_driver = "authenc(hmac-sha1-lib,cbc(des-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha1_des_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha1),cbc(des3_ede))", + .generic_driver = "authenc(hmac-sha1-lib,cbc(des3_ede-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha1_des3_ede_cbc_tv_temp) @@ -4248,9 +4206,9 @@ static const struct alg_test_desc alg_test_descs[] = { }, { .alg = "authenc(hmac(sha1),ctr(aes))", .test = alg_test_null, - .fips_allowed = 1, }, { .alg = "authenc(hmac(sha1),ecb(cipher_null))", + .generic_driver = "authenc(hmac-sha1-lib,ecb-cipher_null)", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha1_ecb_cipher_null_tv_temp) @@ -4258,21 +4216,23 @@ static const struct alg_test_desc alg_test_descs[] = { }, { .alg = "authenc(hmac(sha1),rfc3686(ctr(aes)))", .test = alg_test_null, - .fips_allowed = 1, }, { .alg = "authenc(hmac(sha224),cbc(des))", + .generic_driver = "authenc(hmac-sha224-lib,cbc(des-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha224_des_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha224),cbc(des3_ede))", + .generic_driver = "authenc(hmac-sha224-lib,cbc(des3_ede-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha224_des3_ede_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha256),cbc(aes))", + .generic_driver = "authenc(hmac-sha256-lib,cbc(aes-generic))", .test = alg_test_aead, .fips_allowed = 1, .suite = { @@ -4280,12 +4240,14 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "authenc(hmac(sha256),cbc(des))", + .generic_driver = "authenc(hmac-sha256-lib,cbc(des-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha256_des_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha256),cbc(des3_ede))", + .generic_driver = "authenc(hmac-sha256-lib,cbc(des3_ede-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha256_des3_ede_cbc_tv_temp) @@ -4296,6 +4258,7 @@ static const struct alg_test_desc alg_test_descs[] = { .fips_allowed = 1, }, { .alg = "authenc(hmac(sha256),cts(cbc(aes)))", + .generic_driver = "authenc(hmac-sha256-lib,cts(cbc(aes-generic)))", .test = alg_test_aead, .suite = { .aead = __VECS(krb5_test_aes128_cts_hmac_sha256_128) @@ -4306,12 +4269,14 @@ static const struct alg_test_desc alg_test_descs[] = { .fips_allowed = 1, }, { .alg = "authenc(hmac(sha384),cbc(des))", + .generic_driver = "authenc(hmac-sha384-lib,cbc(des-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha384_des_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha384),cbc(des3_ede))", + .generic_driver = "authenc(hmac-sha384-lib,cbc(des3_ede-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha384_des3_ede_cbc_tv_temp) @@ -4322,6 +4287,7 @@ static const struct alg_test_desc alg_test_descs[] = { .fips_allowed = 1, }, { .alg = "authenc(hmac(sha384),cts(cbc(aes)))", + .generic_driver = "authenc(hmac-sha384-lib,cts(cbc(aes-generic)))", .test = alg_test_aead, .suite = { .aead = __VECS(krb5_test_aes256_cts_hmac_sha384_192) @@ -4332,6 +4298,7 @@ static const struct alg_test_desc alg_test_descs[] = { .fips_allowed = 1, }, { .alg = "authenc(hmac(sha512),cbc(aes))", + .generic_driver = "authenc(hmac-sha512-lib,cbc(aes-generic))", .fips_allowed = 1, .test = alg_test_aead, .suite = { @@ -4339,12 +4306,14 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "authenc(hmac(sha512),cbc(des))", + .generic_driver = "authenc(hmac-sha512-lib,cbc(des-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha512_des_cbc_tv_temp) } }, { .alg = "authenc(hmac(sha512),cbc(des3_ede))", + .generic_driver = "authenc(hmac-sha512-lib,cbc(des3_ede-generic))", .test = alg_test_aead, .suite = { .aead = __VECS(hmac_sha512_des3_ede_cbc_tv_temp) @@ -4546,6 +4515,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "crc32", + .generic_driver = "crc32-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -4553,7 +4523,8 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "crc32c", - .test = alg_test_crc32c, + .generic_driver = "crc32c-lib", + .test = alg_test_hash, .fips_allowed = 1, .suite = { .hash = __VECS(crc32c_tv_template) @@ -4738,6 +4709,7 @@ static const struct alg_test_desc alg_test_descs[] = { */ .alg = "drbg_nopr_hmac_sha384", .test = alg_test_null, + .fips_allowed = 1 }, { .alg = "drbg_nopr_hmac_sha512", .test = alg_test_drbg, @@ -4756,6 +4728,7 @@ static const struct alg_test_desc alg_test_descs[] = { /* covered by drbg_nopr_sha256 test */ .alg = "drbg_nopr_sha384", .test = alg_test_null, + .fips_allowed = 1 }, { .alg = "drbg_nopr_sha512", .fips_allowed = 1, @@ -4787,6 +4760,7 @@ static const struct alg_test_desc alg_test_descs[] = { /* covered by drbg_pr_hmac_sha256 test */ .alg = "drbg_pr_hmac_sha384", .test = alg_test_null, + .fips_allowed = 1 }, { .alg = "drbg_pr_hmac_sha512", .test = alg_test_null, @@ -4802,6 +4776,7 @@ static const struct alg_test_desc alg_test_descs[] = { /* covered by drbg_pr_sha256 test */ .alg = "drbg_pr_sha384", .test = alg_test_null, + .fips_allowed = 1 }, { .alg = "drbg_pr_sha512", .fips_allowed = 1, @@ -5000,6 +4975,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "essiv(authenc(hmac(sha256),cbc(aes)),sha256)", + .generic_driver = "essiv(authenc(hmac-sha256-lib,cbc(aes-generic)),sha256-lib)", .test = alg_test_aead, .fips_allowed = 1, .suite = { @@ -5007,6 +4983,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "essiv(cbc(aes),sha256)", + .generic_driver = "essiv(cbc(aes-generic),sha256-lib)", .test = alg_test_skcipher, .fips_allowed = 1, .suite = { @@ -5099,13 +5076,14 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "hmac(sha1)", + .generic_driver = "hmac-sha1-lib", .test = alg_test_hash, - .fips_allowed = 1, .suite = { .hash = __VECS(hmac_sha1_tv_template) } }, { .alg = "hmac(sha224)", + .generic_driver = "hmac-sha224-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5113,6 +5091,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "hmac(sha256)", + .generic_driver = "hmac-sha256-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5148,6 +5127,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "hmac(sha384)", + .generic_driver = "hmac-sha384-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5155,6 +5135,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "hmac(sha512)", + .generic_driver = "hmac-sha512-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5310,6 +5291,36 @@ static const struct alg_test_desc alg_test_descs[] = { .cipher = __VECS(fcrypt_pcbc_tv_template) } }, { +#if IS_ENABLED(CONFIG_CRYPTO_PHMAC_S390) + .alg = "phmac(sha224)", + .test = alg_test_hash, + .fips_allowed = 1, + .suite = { + .hash = __VECS(hmac_sha224_tv_template) + } + }, { + .alg = "phmac(sha256)", + .test = alg_test_hash, + .fips_allowed = 1, + .suite = { + .hash = __VECS(hmac_sha256_tv_template) + } + }, { + .alg = "phmac(sha384)", + .test = alg_test_hash, + .fips_allowed = 1, + .suite = { + .hash = __VECS(hmac_sha384_tv_template) + } + }, { + .alg = "phmac(sha512)", + .test = alg_test_hash, + .fips_allowed = 1, + .suite = { + .hash = __VECS(hmac_sha512_tv_template) + } + }, { +#endif .alg = "pkcs1(rsa,none)", .test = alg_test_sig, .suite = { @@ -5435,13 +5446,14 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "sha1", + .generic_driver = "sha1-lib", .test = alg_test_hash, - .fips_allowed = 1, .suite = { .hash = __VECS(sha1_tv_template) } }, { .alg = "sha224", + .generic_driver = "sha224-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5449,6 +5461,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "sha256", + .generic_driver = "sha256-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5484,6 +5497,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "sha384", + .generic_driver = "sha384-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { @@ -5491,6 +5505,7 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "sha512", + .generic_driver = "sha512-lib", .test = alg_test_hash, .fips_allowed = 1, .suite = { diff --git a/crypto/wp512.c b/crypto/wp512.c index 41f13d490333..229b189a7988 100644 --- a/crypto/wp512.c +++ b/crypto/wp512.c @@ -21,10 +21,10 @@ */ #include #include +#include #include -#include -#include -#include +#include +#include #define WP512_DIGEST_SIZE 64 #define WP384_DIGEST_SIZE 48 @@ -37,9 +37,6 @@ struct wp512_ctx { u8 bitLength[WP512_LENGTHBYTES]; - u8 buffer[WP512_BLOCK_SIZE]; - int bufferBits; - int bufferPos; u64 hash[WP512_DIGEST_SIZE/8]; }; @@ -779,16 +776,16 @@ static const u64 rc[WHIRLPOOL_ROUNDS] = { * The core Whirlpool transform. */ -static __no_kmsan_checks void wp512_process_buffer(struct wp512_ctx *wctx) { +static __no_kmsan_checks void wp512_process_buffer(struct wp512_ctx *wctx, + const u8 *buffer) { int i, r; u64 K[8]; /* the round key */ u64 block[8]; /* mu(buffer) */ u64 state[8]; /* the cipher state */ u64 L[8]; - const __be64 *buffer = (const __be64 *)wctx->buffer; for (i = 0; i < 8; i++) - block[i] = be64_to_cpu(buffer[i]); + block[i] = get_unaligned_be64(buffer + i * 8); state[0] = block[0] ^ (K[0] = wctx->hash[0]); state[1] = block[1] ^ (K[1] = wctx->hash[1]); @@ -991,8 +988,6 @@ static int wp512_init(struct shash_desc *desc) { int i; memset(wctx->bitLength, 0, 32); - wctx->bufferBits = wctx->bufferPos = 0; - wctx->buffer[0] = 0; for (i = 0; i < 8; i++) { wctx->hash[i] = 0L; } @@ -1000,84 +995,54 @@ static int wp512_init(struct shash_desc *desc) { return 0; } -static int wp512_update(struct shash_desc *desc, const u8 *source, - unsigned int len) +static void wp512_add_length(u8 *bitLength, u64 value) { - struct wp512_ctx *wctx = shash_desc_ctx(desc); - int sourcePos = 0; - unsigned int bits_len = len * 8; // convert to number of bits - int sourceGap = (8 - ((int)bits_len & 7)) & 7; - int bufferRem = wctx->bufferBits & 7; + u32 carry; int i; - u32 b, carry; - u8 *buffer = wctx->buffer; - u8 *bitLength = wctx->bitLength; - int bufferBits = wctx->bufferBits; - int bufferPos = wctx->bufferPos; - u64 value = bits_len; for (i = 31, carry = 0; i >= 0 && (carry != 0 || value != 0ULL); i--) { carry += bitLength[i] + ((u32)value & 0xff); bitLength[i] = (u8)carry; carry >>= 8; value >>= 8; } - while (bits_len > 8) { - b = ((source[sourcePos] << sourceGap) & 0xff) | - ((source[sourcePos + 1] & 0xff) >> (8 - sourceGap)); - buffer[bufferPos++] |= (u8)(b >> bufferRem); - bufferBits += 8 - bufferRem; - if (bufferBits == WP512_BLOCK_SIZE * 8) { - wp512_process_buffer(wctx); - bufferBits = bufferPos = 0; - } - buffer[bufferPos] = b << (8 - bufferRem); - bufferBits += bufferRem; - bits_len -= 8; - sourcePos++; - } - if (bits_len > 0) { - b = (source[sourcePos] << sourceGap) & 0xff; - buffer[bufferPos] |= b >> bufferRem; - } else { - b = 0; - } - if (bufferRem + bits_len < 8) { - bufferBits += bits_len; - } else { - bufferPos++; - bufferBits += 8 - bufferRem; - bits_len -= 8 - bufferRem; - if (bufferBits == WP512_BLOCK_SIZE * 8) { - wp512_process_buffer(wctx); - bufferBits = bufferPos = 0; - } - buffer[bufferPos] = b << (8 - bufferRem); - bufferBits += (int)bits_len; - } - - wctx->bufferBits = bufferBits; - wctx->bufferPos = bufferPos; - - return 0; } -static int wp512_final(struct shash_desc *desc, u8 *out) +static int wp512_update(struct shash_desc *desc, const u8 *source, + unsigned int len) +{ + struct wp512_ctx *wctx = shash_desc_ctx(desc); + unsigned int remain = len % WP512_BLOCK_SIZE; + u64 bits_len = (len - remain) * 8ull; + u8 *bitLength = wctx->bitLength; + + wp512_add_length(bitLength, bits_len); + do { + wp512_process_buffer(wctx, source); + source += WP512_BLOCK_SIZE; + bits_len -= WP512_BLOCK_SIZE * 8; + } while (bits_len); + + return remain; +} + +static int wp512_finup(struct shash_desc *desc, const u8 *src, + unsigned int bufferPos, u8 *out) { struct wp512_ctx *wctx = shash_desc_ctx(desc); int i; - u8 *buffer = wctx->buffer; u8 *bitLength = wctx->bitLength; - int bufferBits = wctx->bufferBits; - int bufferPos = wctx->bufferPos; __be64 *digest = (__be64 *)out; + u8 buffer[WP512_BLOCK_SIZE]; - buffer[bufferPos] |= 0x80U >> (bufferBits & 7); + wp512_add_length(bitLength, bufferPos * 8); + memcpy(buffer, src, bufferPos); + buffer[bufferPos] = 0x80U; bufferPos++; if (bufferPos > WP512_BLOCK_SIZE - WP512_LENGTHBYTES) { if (bufferPos < WP512_BLOCK_SIZE) memset(&buffer[bufferPos], 0, WP512_BLOCK_SIZE - bufferPos); - wp512_process_buffer(wctx); + wp512_process_buffer(wctx, buffer); bufferPos = 0; } if (bufferPos < WP512_BLOCK_SIZE - WP512_LENGTHBYTES) @@ -1086,31 +1051,32 @@ static int wp512_final(struct shash_desc *desc, u8 *out) bufferPos = WP512_BLOCK_SIZE - WP512_LENGTHBYTES; memcpy(&buffer[WP512_BLOCK_SIZE - WP512_LENGTHBYTES], bitLength, WP512_LENGTHBYTES); - wp512_process_buffer(wctx); + wp512_process_buffer(wctx, buffer); + memzero_explicit(buffer, sizeof(buffer)); for (i = 0; i < WP512_DIGEST_SIZE/8; i++) digest[i] = cpu_to_be64(wctx->hash[i]); - wctx->bufferBits = bufferBits; - wctx->bufferPos = bufferPos; return 0; } -static int wp384_final(struct shash_desc *desc, u8 *out) +static int wp384_finup(struct shash_desc *desc, const u8 *src, + unsigned int len, u8 *out) { u8 D[64]; - wp512_final(desc, D); + wp512_finup(desc, src, len, D); memcpy(out, D, WP384_DIGEST_SIZE); memzero_explicit(D, WP512_DIGEST_SIZE); return 0; } -static int wp256_final(struct shash_desc *desc, u8 *out) +static int wp256_finup(struct shash_desc *desc, const u8 *src, + unsigned int len, u8 *out) { u8 D[64]; - wp512_final(desc, D); + wp512_finup(desc, src, len, D); memcpy(out, D, WP256_DIGEST_SIZE); memzero_explicit(D, WP512_DIGEST_SIZE); @@ -1121,11 +1087,12 @@ static struct shash_alg wp_algs[3] = { { .digestsize = WP512_DIGEST_SIZE, .init = wp512_init, .update = wp512_update, - .final = wp512_final, + .finup = wp512_finup, .descsize = sizeof(struct wp512_ctx), .base = { .cra_name = "wp512", .cra_driver_name = "wp512-generic", + .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, .cra_blocksize = WP512_BLOCK_SIZE, .cra_module = THIS_MODULE, } @@ -1133,11 +1100,12 @@ static struct shash_alg wp_algs[3] = { { .digestsize = WP384_DIGEST_SIZE, .init = wp512_init, .update = wp512_update, - .final = wp384_final, + .finup = wp384_finup, .descsize = sizeof(struct wp512_ctx), .base = { .cra_name = "wp384", .cra_driver_name = "wp384-generic", + .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, .cra_blocksize = WP512_BLOCK_SIZE, .cra_module = THIS_MODULE, } @@ -1145,11 +1113,12 @@ static struct shash_alg wp_algs[3] = { { .digestsize = WP256_DIGEST_SIZE, .init = wp512_init, .update = wp512_update, - .final = wp256_final, + .finup = wp256_finup, .descsize = sizeof(struct wp512_ctx), .base = { .cra_name = "wp256", .cra_driver_name = "wp256-generic", + .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY, .cra_blocksize = WP512_BLOCK_SIZE, .cra_module = THIS_MODULE, } diff --git a/crypto/zstd.c b/crypto/zstd.c index 7570e11b4ee6..c2a19cb0879d 100644 --- a/crypto/zstd.c +++ b/crypto/zstd.c @@ -12,188 +12,304 @@ #include #include #include -#include +#include +#include -#define ZSTD_DEF_LEVEL 3 +#define ZSTD_DEF_LEVEL 3 +#define ZSTD_MAX_WINDOWLOG 18 +#define ZSTD_MAX_SIZE BIT(ZSTD_MAX_WINDOWLOG) struct zstd_ctx { zstd_cctx *cctx; zstd_dctx *dctx; - void *cwksp; - void *dwksp; + size_t wksp_size; + zstd_parameters params; + u8 wksp[] __aligned(8); }; -static zstd_parameters zstd_params(void) +static DEFINE_MUTEX(zstd_stream_lock); + +static void *zstd_alloc_stream(void) { - return zstd_get_params(ZSTD_DEF_LEVEL, 0); -} - -static int zstd_comp_init(struct zstd_ctx *ctx) -{ - int ret = 0; - const zstd_parameters params = zstd_params(); - const size_t wksp_size = zstd_cctx_workspace_bound(¶ms.cParams); - - ctx->cwksp = vzalloc(wksp_size); - if (!ctx->cwksp) { - ret = -ENOMEM; - goto out; - } - - ctx->cctx = zstd_init_cctx(ctx->cwksp, wksp_size); - if (!ctx->cctx) { - ret = -EINVAL; - goto out_free; - } -out: - return ret; -out_free: - vfree(ctx->cwksp); - goto out; -} - -static int zstd_decomp_init(struct zstd_ctx *ctx) -{ - int ret = 0; - const size_t wksp_size = zstd_dctx_workspace_bound(); - - ctx->dwksp = vzalloc(wksp_size); - if (!ctx->dwksp) { - ret = -ENOMEM; - goto out; - } - - ctx->dctx = zstd_init_dctx(ctx->dwksp, wksp_size); - if (!ctx->dctx) { - ret = -EINVAL; - goto out_free; - } -out: - return ret; -out_free: - vfree(ctx->dwksp); - goto out; -} - -static void zstd_comp_exit(struct zstd_ctx *ctx) -{ - vfree(ctx->cwksp); - ctx->cwksp = NULL; - ctx->cctx = NULL; -} - -static void zstd_decomp_exit(struct zstd_ctx *ctx) -{ - vfree(ctx->dwksp); - ctx->dwksp = NULL; - ctx->dctx = NULL; -} - -static int __zstd_init(void *ctx) -{ - int ret; - - ret = zstd_comp_init(ctx); - if (ret) - return ret; - ret = zstd_decomp_init(ctx); - if (ret) - zstd_comp_exit(ctx); - return ret; -} - -static void *zstd_alloc_ctx(void) -{ - int ret; + zstd_parameters params; struct zstd_ctx *ctx; + size_t wksp_size; - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + params = zstd_get_params(ZSTD_DEF_LEVEL, ZSTD_MAX_SIZE); + + wksp_size = max_t(size_t, + zstd_cstream_workspace_bound(¶ms.cParams), + zstd_dstream_workspace_bound(ZSTD_MAX_SIZE)); + if (!wksp_size) + return ERR_PTR(-EINVAL); + + ctx = kvmalloc(sizeof(*ctx) + wksp_size, GFP_KERNEL); if (!ctx) return ERR_PTR(-ENOMEM); - ret = __zstd_init(ctx); - if (ret) { - kfree(ctx); - return ERR_PTR(ret); - } + ctx->params = params; + ctx->wksp_size = wksp_size; return ctx; } -static void __zstd_exit(void *ctx) +static void zstd_free_stream(void *ctx) { - zstd_comp_exit(ctx); - zstd_decomp_exit(ctx); + kvfree(ctx); } -static void zstd_free_ctx(void *ctx) +static struct crypto_acomp_streams zstd_streams = { + .alloc_ctx = zstd_alloc_stream, + .free_ctx = zstd_free_stream, +}; + +static int zstd_init(struct crypto_acomp *acomp_tfm) { - __zstd_exit(ctx); - kfree_sensitive(ctx); + int ret = 0; + + mutex_lock(&zstd_stream_lock); + ret = crypto_acomp_alloc_streams(&zstd_streams); + mutex_unlock(&zstd_stream_lock); + + return ret; } -static int __zstd_compress(const u8 *src, unsigned int slen, - u8 *dst, unsigned int *dlen, void *ctx) +static void zstd_exit(struct crypto_acomp *acomp_tfm) { - size_t out_len; - struct zstd_ctx *zctx = ctx; - const zstd_parameters params = zstd_params(); + crypto_acomp_free_streams(&zstd_streams); +} - out_len = zstd_compress_cctx(zctx->cctx, dst, *dlen, src, slen, ¶ms); +static int zstd_compress_one(struct acomp_req *req, struct zstd_ctx *ctx, + const void *src, void *dst, unsigned int *dlen) +{ + unsigned int out_len; + + ctx->cctx = zstd_init_cctx(ctx->wksp, ctx->wksp_size); + if (!ctx->cctx) + return -EINVAL; + + out_len = zstd_compress_cctx(ctx->cctx, dst, req->dlen, src, req->slen, + &ctx->params); if (zstd_is_error(out_len)) return -EINVAL; + *dlen = out_len; + return 0; } -static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int *dlen, - void *ctx) +static int zstd_compress(struct acomp_req *req) { - return __zstd_compress(src, slen, dst, dlen, ctx); -} + struct crypto_acomp_stream *s; + unsigned int pos, scur, dcur; + unsigned int total_out = 0; + bool data_available = true; + zstd_out_buffer outbuf; + struct acomp_walk walk; + zstd_in_buffer inbuf; + struct zstd_ctx *ctx; + size_t pending_bytes; + size_t num_bytes; + int ret; -static int __zstd_decompress(const u8 *src, unsigned int slen, - u8 *dst, unsigned int *dlen, void *ctx) -{ - size_t out_len; - struct zstd_ctx *zctx = ctx; + s = crypto_acomp_lock_stream_bh(&zstd_streams); + ctx = s->ctx; - out_len = zstd_decompress_dctx(zctx->dctx, dst, *dlen, src, slen); - if (zstd_is_error(out_len)) - return -EINVAL; - *dlen = out_len; - return 0; -} + ret = acomp_walk_virt(&walk, req, true); + if (ret) + goto out; -static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int *dlen, - void *ctx) -{ - return __zstd_decompress(src, slen, dst, dlen, ctx); -} - -static struct scomp_alg scomp = { - .alloc_ctx = zstd_alloc_ctx, - .free_ctx = zstd_free_ctx, - .compress = zstd_scompress, - .decompress = zstd_sdecompress, - .base = { - .cra_name = "zstd", - .cra_driver_name = "zstd-scomp", - .cra_module = THIS_MODULE, + ctx->cctx = zstd_init_cstream(&ctx->params, 0, ctx->wksp, ctx->wksp_size); + if (!ctx->cctx) { + ret = -EINVAL; + goto out; } + + do { + dcur = acomp_walk_next_dst(&walk); + if (!dcur) { + ret = -ENOSPC; + goto out; + } + + outbuf.pos = 0; + outbuf.dst = (u8 *)walk.dst.virt.addr; + outbuf.size = dcur; + + do { + scur = acomp_walk_next_src(&walk); + if (dcur == req->dlen && scur == req->slen) { + ret = zstd_compress_one(req, ctx, walk.src.virt.addr, + walk.dst.virt.addr, &total_out); + acomp_walk_done_src(&walk, scur); + acomp_walk_done_dst(&walk, dcur); + goto out; + } + + if (scur) { + inbuf.pos = 0; + inbuf.src = walk.src.virt.addr; + inbuf.size = scur; + } else { + data_available = false; + break; + } + + num_bytes = zstd_compress_stream(ctx->cctx, &outbuf, &inbuf); + if (ZSTD_isError(num_bytes)) { + ret = -EIO; + goto out; + } + + pending_bytes = zstd_flush_stream(ctx->cctx, &outbuf); + if (ZSTD_isError(pending_bytes)) { + ret = -EIO; + goto out; + } + acomp_walk_done_src(&walk, inbuf.pos); + } while (dcur != outbuf.pos); + + total_out += outbuf.pos; + acomp_walk_done_dst(&walk, dcur); + } while (data_available); + + pos = outbuf.pos; + num_bytes = zstd_end_stream(ctx->cctx, &outbuf); + if (ZSTD_isError(num_bytes)) + ret = -EIO; + else + total_out += (outbuf.pos - pos); + +out: + if (ret) + req->dlen = 0; + else + req->dlen = total_out; + + crypto_acomp_unlock_stream_bh(s); + + return ret; +} + +static int zstd_decompress_one(struct acomp_req *req, struct zstd_ctx *ctx, + const void *src, void *dst, unsigned int *dlen) +{ + size_t out_len; + + ctx->dctx = zstd_init_dctx(ctx->wksp, ctx->wksp_size); + if (!ctx->dctx) + return -EINVAL; + + out_len = zstd_decompress_dctx(ctx->dctx, dst, req->dlen, src, req->slen); + if (zstd_is_error(out_len)) + return -EINVAL; + + *dlen = out_len; + + return 0; +} + +static int zstd_decompress(struct acomp_req *req) +{ + struct crypto_acomp_stream *s; + unsigned int total_out = 0; + unsigned int scur, dcur; + zstd_out_buffer outbuf; + struct acomp_walk walk; + zstd_in_buffer inbuf; + struct zstd_ctx *ctx; + size_t pending_bytes; + int ret; + + s = crypto_acomp_lock_stream_bh(&zstd_streams); + ctx = s->ctx; + + ret = acomp_walk_virt(&walk, req, true); + if (ret) + goto out; + + ctx->dctx = zstd_init_dstream(ZSTD_MAX_SIZE, ctx->wksp, ctx->wksp_size); + if (!ctx->dctx) { + ret = -EINVAL; + goto out; + } + + do { + scur = acomp_walk_next_src(&walk); + if (scur) { + inbuf.pos = 0; + inbuf.size = scur; + inbuf.src = walk.src.virt.addr; + } else { + break; + } + + do { + dcur = acomp_walk_next_dst(&walk); + if (dcur == req->dlen && scur == req->slen) { + ret = zstd_decompress_one(req, ctx, walk.src.virt.addr, + walk.dst.virt.addr, &total_out); + acomp_walk_done_dst(&walk, dcur); + acomp_walk_done_src(&walk, scur); + goto out; + } + + if (!dcur) { + ret = -ENOSPC; + goto out; + } + + outbuf.pos = 0; + outbuf.dst = (u8 *)walk.dst.virt.addr; + outbuf.size = dcur; + + pending_bytes = zstd_decompress_stream(ctx->dctx, &outbuf, &inbuf); + if (ZSTD_isError(pending_bytes)) { + ret = -EIO; + goto out; + } + + total_out += outbuf.pos; + + acomp_walk_done_dst(&walk, outbuf.pos); + } while (inbuf.pos != scur); + + acomp_walk_done_src(&walk, scur); + } while (ret == 0); + +out: + if (ret) + req->dlen = 0; + else + req->dlen = total_out; + + crypto_acomp_unlock_stream_bh(s); + + return ret; +} + +static struct acomp_alg zstd_acomp = { + .base = { + .cra_name = "zstd", + .cra_driver_name = "zstd-generic", + .cra_flags = CRYPTO_ALG_REQ_VIRT, + .cra_module = THIS_MODULE, + }, + .init = zstd_init, + .exit = zstd_exit, + .compress = zstd_compress, + .decompress = zstd_decompress, }; static int __init zstd_mod_init(void) { - return crypto_register_scomp(&scomp); + return crypto_register_acomp(&zstd_acomp); } static void __exit zstd_mod_fini(void) { - crypto_unregister_scomp(&scomp); + crypto_unregister_acomp(&zstd_acomp); } module_init(zstd_mod_init); diff --git a/drivers/Kconfig b/drivers/Kconfig index c5edbd2288a1..4915a63866b0 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -10,6 +10,12 @@ source "drivers/cxl/Kconfig" source "drivers/pcmcia/Kconfig" source "drivers/rapidio/Kconfig" +config PC104 + bool "PC/104 support" if EXPERT + help + Expose PC/104 form factor device drivers and options available for + selection and configuration. Enable this option if your target + machine has a PC/104 bus. source "drivers/base/Kconfig" @@ -77,6 +83,8 @@ source "drivers/pps/Kconfig" source "drivers/ptp/Kconfig" +source "drivers/dpll/Kconfig" + source "drivers/pinctrl/Kconfig" source "drivers/gpio/Kconfig" @@ -243,6 +251,4 @@ source "drivers/hte/Kconfig" source "drivers/cdx/Kconfig" -source "drivers/dpll/Kconfig" - endmenu diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig index 5b9490367a39..bb01cebc42bf 100644 --- a/drivers/accel/Kconfig +++ b/drivers/accel/Kconfig @@ -28,5 +28,6 @@ source "drivers/accel/amdxdna/Kconfig" source "drivers/accel/habanalabs/Kconfig" source "drivers/accel/ivpu/Kconfig" source "drivers/accel/qaic/Kconfig" +source "drivers/accel/rocket/Kconfig" endif diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile index a301fb6089d4..ffc3fa588666 100644 --- a/drivers/accel/Makefile +++ b/drivers/accel/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/ obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/ obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/ obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/ +obj-$(CONFIG_DRM_ACCEL_ROCKET) += rocket/ \ No newline at end of file diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile index 0e9adf6890a0..6797dac65efa 100644 --- a/drivers/accel/amdxdna/Makefile +++ b/drivers/accel/amdxdna/Makefile @@ -15,6 +15,7 @@ amdxdna-y := \ amdxdna_mailbox_helper.o \ amdxdna_pci_drv.o \ amdxdna_sysfs.o \ + amdxdna_ubuf.o \ npu1_regs.o \ npu2_regs.o \ npu4_regs.o \ diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 2cff5419bd2f..e9f9b1fa5dc1 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -46,6 +46,17 @@ static void aie2_job_put(struct amdxdna_sched_job *job) kref_put(&job->refcnt, aie2_job_release); } +static void aie2_hwctx_status_shift_stop(struct amdxdna_hwctx *hwctx) +{ + hwctx->old_status = hwctx->status; + hwctx->status = HWCTX_STAT_STOP; +} + +static void aie2_hwctx_status_restore(struct amdxdna_hwctx *hwctx) +{ + hwctx->status = hwctx->old_status; +} + /* The bad_job is used in aie2_sched_job_timedout, otherwise, set it to NULL */ static void aie2_hwctx_stop(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx, struct drm_sched_job *bad_job) @@ -89,25 +100,6 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw return ret; } -void aie2_restart_ctx(struct amdxdna_client *client) -{ - struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_hwctx *hwctx; - unsigned long hwctx_id; - - drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - mutex_lock(&client->hwctx_lock); - amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { - if (hwctx->status != HWCTX_STAT_STOP) - continue; - - hwctx->status = hwctx->old_status; - XDNA_DBG(xdna, "Resetting %s", hwctx->name); - aie2_hwctx_restart(xdna, hwctx); - } - mutex_unlock(&client->hwctx_lock); -} - static struct dma_fence *aie2_cmd_get_out_fence(struct amdxdna_hwctx *hwctx, u64 seq) { struct dma_fence *fence, *out_fence = NULL; @@ -141,34 +133,49 @@ static void aie2_hwctx_wait_for_idle(struct amdxdna_hwctx *hwctx) dma_fence_put(fence); } -void aie2_hwctx_suspend(struct amdxdna_hwctx *hwctx) +static int aie2_hwctx_suspend_cb(struct amdxdna_hwctx *hwctx, void *arg) { struct amdxdna_dev *xdna = hwctx->client->xdna; + aie2_hwctx_wait_for_idle(hwctx); + aie2_hwctx_stop(xdna, hwctx, NULL); + aie2_hwctx_status_shift_stop(hwctx); + + return 0; +} + +void aie2_hwctx_suspend(struct amdxdna_client *client) +{ + struct amdxdna_dev *xdna = client->xdna; + /* * Command timeout is unlikely. But if it happens, it doesn't * break the system. aie2_hwctx_stop() will destroy mailbox * and abort all commands. */ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - aie2_hwctx_wait_for_idle(hwctx); - aie2_hwctx_stop(xdna, hwctx, NULL); - hwctx->old_status = hwctx->status; - hwctx->status = HWCTX_STAT_STOP; + amdxdna_hwctx_walk(client, NULL, aie2_hwctx_suspend_cb); } -void aie2_hwctx_resume(struct amdxdna_hwctx *hwctx) +static int aie2_hwctx_resume_cb(struct amdxdna_hwctx *hwctx, void *arg) { struct amdxdna_dev *xdna = hwctx->client->xdna; + aie2_hwctx_status_restore(hwctx); + return aie2_hwctx_restart(xdna, hwctx); +} + +int aie2_hwctx_resume(struct amdxdna_client *client) +{ + struct amdxdna_dev *xdna = client->xdna; + /* * The resume path cannot guarantee that mailbox channel can be * regenerated. If this happen, when submit message to this * mailbox channel, error will return. */ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - hwctx->status = hwctx->old_status; - aie2_hwctx_restart(xdna, hwctx); + return amdxdna_hwctx_walk(client, NULL, aie2_hwctx_resume_cb); } static void @@ -192,7 +199,7 @@ aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size) { struct amdxdna_sched_job *job = handle; struct amdxdna_gem_obj *cmd_abo; - u32 ret = 0; + int ret = 0; u32 status; cmd_abo = job->cmd_bo; @@ -222,7 +229,7 @@ static int aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size) { struct amdxdna_sched_job *job = handle; - u32 ret = 0; + int ret = 0; u32 status; if (unlikely(!data)) @@ -250,7 +257,7 @@ aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size) u32 fail_cmd_status; u32 fail_cmd_idx; u32 cmd_status; - u32 ret = 0; + int ret = 0; cmd_abo = job->cmd_bo; if (unlikely(!data) || unlikely(size != sizeof(u32) * 3)) { diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index 82412eec9a4b..9caad083543d 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -290,18 +290,25 @@ int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u6 return 0; } +static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg) +{ + u32 *bitmap = arg; + + *bitmap |= GENMASK(hwctx->start_col + hwctx->num_col - 1, hwctx->start_col); + + return 0; +} + int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled) { DECLARE_AIE2_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS); struct amdxdna_dev *xdna = ndev->xdna; struct amdxdna_client *client; - struct amdxdna_hwctx *hwctx; - unsigned long hwctx_id; dma_addr_t dma_addr; u32 aie_bitmap = 0; u8 *buff_addr; - int ret, idx; + int ret; buff_addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr, DMA_FROM_DEVICE, GFP_KERNEL); @@ -309,12 +316,8 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, return -ENOMEM; /* Go through each hardware context and mark the AIE columns that are active */ - list_for_each_entry(client, &xdna->client_list, node) { - idx = srcu_read_lock(&client->hwctx_srcu); - amdxdna_for_each_hwctx(client, hwctx_id, hwctx) - aie_bitmap |= amdxdna_hwctx_col_map(hwctx); - srcu_read_unlock(&client->hwctx_srcu, idx); - } + list_for_each_entry(client, &xdna->client_list, node) + amdxdna_hwctx_walk(client, &aie_bitmap, amdxdna_hwctx_col_map); *cols_filled = 0; req.dump_buff_addr = dma_addr; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index c6cf7068d23c..87c425e3d2b9 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -440,6 +441,40 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) return ret; } +static int aie2_hw_suspend(struct amdxdna_dev *xdna) +{ + struct amdxdna_client *client; + + guard(mutex)(&xdna->dev_lock); + list_for_each_entry(client, &xdna->client_list, node) + aie2_hwctx_suspend(client); + + aie2_hw_stop(xdna); + + return 0; +} + +static int aie2_hw_resume(struct amdxdna_dev *xdna) +{ + struct amdxdna_client *client; + int ret; + + guard(mutex)(&xdna->dev_lock); + ret = aie2_hw_start(xdna); + if (ret) { + XDNA_ERR(xdna, "Start hardware failed, %d", ret); + return ret; + } + + list_for_each_entry(client, &xdna->client_list, node) { + ret = aie2_hwctx_resume(client); + if (ret) + break; + } + + return ret; +} + static int aie2_init(struct amdxdna_dev *xdna) { struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); @@ -520,14 +555,14 @@ static int aie2_init(struct amdxdna_dev *xdna) if (!ndev->psp_hdl) { XDNA_ERR(xdna, "failed to create psp"); ret = -ENOMEM; - goto free_irq; + goto release_fw; } xdna->dev_handle = ndev; ret = aie2_hw_start(xdna); if (ret) { XDNA_ERR(xdna, "start npu failed, ret %d", ret); - goto free_irq; + goto release_fw; } ret = aie2_mgmt_fw_query(ndev); @@ -578,8 +613,6 @@ static int aie2_init(struct amdxdna_dev *xdna) aie2_error_async_events_free(ndev); stop_hw: aie2_hw_stop(xdna); -free_irq: - pci_free_irq_vectors(pdev); release_fw: release_firmware(fw); @@ -588,12 +621,10 @@ static int aie2_init(struct amdxdna_dev *xdna) static void aie2_fini(struct amdxdna_dev *xdna) { - struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); struct amdxdna_dev_hdl *ndev = xdna->dev_handle; aie2_hw_stop(xdna); aie2_error_async_events_free(ndev); - pci_free_irq_vectors(pdev); } static int aie2_get_aie_status(struct amdxdna_client *client, @@ -752,65 +783,68 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client, return ret; } -static int aie2_get_hwctx_status(struct amdxdna_client *client, - struct amdxdna_drm_get_info *args) +static int aie2_hwctx_status_cb(struct amdxdna_hwctx *hwctx, void *arg) { - struct amdxdna_drm_query_hwctx __user *buf; - struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_drm_query_hwctx *tmp; - struct amdxdna_client *tmp_client; - struct amdxdna_hwctx *hwctx; - unsigned long hwctx_id; - bool overflow = false; - u32 req_bytes = 0; - u32 hw_i = 0; - int ret = 0; - int idx; + struct amdxdna_drm_hwctx_entry *tmp __free(kfree) = NULL; + struct amdxdna_drm_get_array *array_args = arg; + struct amdxdna_drm_hwctx_entry __user *buf; + u32 size; - drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + if (!array_args->num_element) + return -EINVAL; tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) return -ENOMEM; - buf = u64_to_user_ptr(args->buffer); + tmp->pid = hwctx->client->pid; + tmp->context_id = hwctx->id; + tmp->start_col = hwctx->start_col; + tmp->num_col = hwctx->num_col; + tmp->command_submissions = hwctx->priv->seq; + tmp->command_completions = hwctx->priv->completed; + tmp->pasid = hwctx->client->pasid; + tmp->priority = hwctx->qos.priority; + tmp->gops = hwctx->qos.gops; + tmp->fps = hwctx->qos.fps; + tmp->dma_bandwidth = hwctx->qos.dma_bandwidth; + tmp->latency = hwctx->qos.latency; + tmp->frame_exec_time = hwctx->qos.frame_exec_time; + tmp->state = AMDXDNA_HWCTX_STATE_ACTIVE; + + buf = u64_to_user_ptr(array_args->buffer); + size = min(sizeof(*tmp), array_args->element_size); + + if (copy_to_user(buf, tmp, size)) + return -EFAULT; + + array_args->buffer += size; + array_args->num_element--; + + return 0; +} + +static int aie2_get_hwctx_status(struct amdxdna_client *client, + struct amdxdna_drm_get_info *args) +{ + struct amdxdna_drm_get_array array_args; + struct amdxdna_dev *xdna = client->xdna; + struct amdxdna_client *tmp_client; + int ret; + + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + + array_args.element_size = sizeof(struct amdxdna_drm_query_hwctx); + array_args.buffer = args->buffer; + array_args.num_element = args->buffer_size / array_args.element_size; list_for_each_entry(tmp_client, &xdna->client_list, node) { - idx = srcu_read_lock(&tmp_client->hwctx_srcu); - amdxdna_for_each_hwctx(tmp_client, hwctx_id, hwctx) { - req_bytes += sizeof(*tmp); - if (args->buffer_size < req_bytes) { - /* Continue iterating to get the required size */ - overflow = true; - continue; - } - - memset(tmp, 0, sizeof(*tmp)); - tmp->pid = tmp_client->pid; - tmp->context_id = hwctx->id; - tmp->start_col = hwctx->start_col; - tmp->num_col = hwctx->num_col; - tmp->command_submissions = hwctx->priv->seq; - tmp->command_completions = hwctx->priv->completed; - - if (copy_to_user(&buf[hw_i], tmp, sizeof(*tmp))) { - ret = -EFAULT; - srcu_read_unlock(&tmp_client->hwctx_srcu, idx); - goto out; - } - hw_i++; - } - srcu_read_unlock(&tmp_client->hwctx_srcu, idx); + ret = amdxdna_hwctx_walk(tmp_client, &array_args, + aie2_hwctx_status_cb); + if (ret) + break; } - if (overflow) { - XDNA_ERR(xdna, "Invalid buffer size. Given: %u Need: %u.", - args->buffer_size, req_bytes); - ret = -EINVAL; - } - -out: - kfree(tmp); - args->buffer_size = req_bytes; + args->buffer_size -= (u32)(array_args.buffer - args->buffer); return ret; } @@ -854,6 +888,58 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i return ret; } +static int aie2_query_ctx_status_array(struct amdxdna_client *client, + struct amdxdna_drm_get_array *args) +{ + struct amdxdna_drm_get_array array_args; + struct amdxdna_dev *xdna = client->xdna; + struct amdxdna_client *tmp_client; + int ret; + + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + + array_args.element_size = min(args->element_size, + sizeof(struct amdxdna_drm_hwctx_entry)); + array_args.buffer = args->buffer; + array_args.num_element = args->num_element * args->element_size / + array_args.element_size; + list_for_each_entry(tmp_client, &xdna->client_list, node) { + ret = amdxdna_hwctx_walk(tmp_client, &array_args, + aie2_hwctx_status_cb); + if (ret) + break; + } + + args->element_size = array_args.element_size; + args->num_element = (u32)((array_args.buffer - args->buffer) / + args->element_size); + + return ret; +} + +static int aie2_get_array(struct amdxdna_client *client, + struct amdxdna_drm_get_array *args) +{ + struct amdxdna_dev *xdna = client->xdna; + int ret, idx; + + if (!drm_dev_enter(&xdna->ddev, &idx)) + return -ENODEV; + + switch (args->param) { + case DRM_AMDXDNA_HW_CONTEXT_ALL: + ret = aie2_query_ctx_status_array(client, args); + break; + default: + XDNA_ERR(xdna, "Not supported request parameter %u", args->param); + ret = -EOPNOTSUPP; + } + XDNA_DBG(xdna, "Got param %d", args->param); + + drm_dev_exit(idx); + return ret; +} + static int aie2_set_power_mode(struct amdxdna_client *client, struct amdxdna_drm_set_state *args) { @@ -903,17 +989,16 @@ static int aie2_set_state(struct amdxdna_client *client, } const struct amdxdna_dev_ops aie2_ops = { - .init = aie2_init, - .fini = aie2_fini, - .resume = aie2_hw_start, - .suspend = aie2_hw_stop, - .get_aie_info = aie2_get_info, - .set_aie_state = aie2_set_state, - .hwctx_init = aie2_hwctx_init, - .hwctx_fini = aie2_hwctx_fini, - .hwctx_config = aie2_hwctx_config, - .cmd_submit = aie2_cmd_submit, + .init = aie2_init, + .fini = aie2_fini, + .resume = aie2_hw_resume, + .suspend = aie2_hw_suspend, + .get_aie_info = aie2_get_info, + .set_aie_state = aie2_set_state, + .hwctx_init = aie2_hwctx_init, + .hwctx_fini = aie2_hwctx_fini, + .hwctx_config = aie2_hwctx_config, + .cmd_submit = aie2_cmd_submit, .hmm_invalidate = aie2_hmm_invalidate, - .hwctx_suspend = aie2_hwctx_suspend, - .hwctx_resume = aie2_hwctx_resume, + .get_array = aie2_get_array, }; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 385914840eaa..91a8e948f82a 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -288,10 +288,9 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, int aie2_hwctx_init(struct amdxdna_hwctx *hwctx); void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx); int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size); -void aie2_hwctx_suspend(struct amdxdna_hwctx *hwctx); -void aie2_hwctx_resume(struct amdxdna_hwctx *hwctx); +void aie2_hwctx_suspend(struct amdxdna_client *client); +int aie2_hwctx_resume(struct amdxdna_client *client); int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq); void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo, unsigned long cur_seq); -void aie2_restart_ctx(struct amdxdna_client *client); #endif /* _AIE2_PCI_H_ */ diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index be073224bd69..4bfe4ef20550 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -60,32 +60,6 @@ static struct dma_fence *amdxdna_fence_create(struct amdxdna_hwctx *hwctx) return &fence->base; } -void amdxdna_hwctx_suspend(struct amdxdna_client *client) -{ - struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_hwctx *hwctx; - unsigned long hwctx_id; - - drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - mutex_lock(&client->hwctx_lock); - amdxdna_for_each_hwctx(client, hwctx_id, hwctx) - xdna->dev_info->ops->hwctx_suspend(hwctx); - mutex_unlock(&client->hwctx_lock); -} - -void amdxdna_hwctx_resume(struct amdxdna_client *client) -{ - struct amdxdna_dev *xdna = client->xdna; - struct amdxdna_hwctx *hwctx; - unsigned long hwctx_id; - - drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - mutex_lock(&client->hwctx_lock); - amdxdna_for_each_hwctx(client, hwctx_id, hwctx) - xdna->dev_info->ops->hwctx_resume(hwctx); - mutex_unlock(&client->hwctx_lock); -} - static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx, struct srcu_struct *ss) { @@ -94,14 +68,30 @@ static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx, synchronize_srcu(ss); /* At this point, user is not able to submit new commands */ - mutex_lock(&xdna->dev_lock); xdna->dev_info->ops->hwctx_fini(hwctx); - mutex_unlock(&xdna->dev_lock); kfree(hwctx->name); kfree(hwctx); } +int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg, + int (*walk)(struct amdxdna_hwctx *hwctx, void *arg)) +{ + struct amdxdna_hwctx *hwctx; + unsigned long hwctx_id; + int ret = 0, idx; + + idx = srcu_read_lock(&client->hwctx_srcu); + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { + ret = walk(hwctx, arg); + if (ret) + break; + } + srcu_read_unlock(&client->hwctx_srcu, idx); + + return ret; +} + void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size) { struct amdxdna_cmd *cmd = abo->mem.kva; @@ -152,16 +142,12 @@ void amdxdna_hwctx_remove_all(struct amdxdna_client *client) struct amdxdna_hwctx *hwctx; unsigned long hwctx_id; - mutex_lock(&client->hwctx_lock); amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { XDNA_DBG(client->xdna, "PID %d close HW context %d", client->pid, hwctx->id); xa_erase(&client->hwctx_xa, hwctx->id); - mutex_unlock(&client->hwctx_lock); amdxdna_hwctx_destroy_rcu(hwctx, &client->hwctx_srcu); - mutex_lock(&client->hwctx_lock); } - mutex_unlock(&client->hwctx_lock); } int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) @@ -251,6 +237,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d if (!drm_dev_enter(dev, &idx)) return -ENODEV; + mutex_lock(&xdna->dev_lock); hwctx = xa_erase(&client->hwctx_xa, args->handle); if (!hwctx) { ret = -EINVAL; @@ -267,6 +254,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); out: + mutex_unlock(&xdna->dev_lock); drm_dev_exit(idx); return ret; } diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h index f0a4a8586d85..7cd7a55936f0 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.h +++ b/drivers/accel/amdxdna/amdxdna_ctx.h @@ -139,16 +139,10 @@ amdxdna_cmd_get_state(struct amdxdna_gem_obj *abo) void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size); int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo); -static inline u32 amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx) -{ - return GENMASK(hwctx->start_col + hwctx->num_col - 1, - hwctx->start_col); -} - void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job); void amdxdna_hwctx_remove_all(struct amdxdna_client *client); -void amdxdna_hwctx_suspend(struct amdxdna_client *client); -void amdxdna_hwctx_resume(struct amdxdna_client *client); +int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg, + int (*walk)(struct amdxdna_hwctx *hwctx, void *arg)); int amdxdna_cmd_submit(struct amdxdna_client *client, u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt, diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 0f85a0105178..d407a36eb412 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -18,6 +18,7 @@ #include "amdxdna_ctx.h" #include "amdxdna_gem.h" #include "amdxdna_pci_drv.h" +#include "amdxdna_ubuf.h" #define XDNA_MAX_CMD_BO_SIZE SZ_32K @@ -296,7 +297,7 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, vma->vm_private_data = NULL; vma->vm_ops = NULL; - ret = dma_buf_mmap(to_gobj(abo)->dma_buf, vma, 0); + ret = dma_buf_mmap(abo->dma_buf, vma, 0); if (ret) { XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); return ret; @@ -391,10 +392,47 @@ static const struct dma_buf_ops amdxdna_dmabuf_ops = { .vunmap = drm_gem_dmabuf_vunmap, }; +static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); + + iosys_map_clear(map); + + dma_resv_assert_held(obj->resv); + + if (is_import_bo(abo)) + dma_buf_vmap(abo->dma_buf, map); + else + drm_gem_shmem_object_vmap(obj, map); + + if (!map->vaddr) + return -ENOMEM; + + return 0; +} + +static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); + + dma_resv_assert_held(obj->resv); + + if (is_import_bo(abo)) + dma_buf_vunmap(abo->dma_buf, map); + else + drm_gem_shmem_object_vunmap(obj, map); +} + static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags) { + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + if (abo->dma_buf) { + get_dma_buf(abo->dma_buf); + return abo->dma_buf; + } + exp_info.ops = &amdxdna_dmabuf_ops; exp_info.size = gobj->size; exp_info.flags = flags; @@ -451,8 +489,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, .get_sg_table = drm_gem_shmem_object_get_sg_table, - .vmap = drm_gem_shmem_object_vmap, - .vunmap = drm_gem_shmem_object_vunmap, + .vmap = amdxdna_gem_obj_vmap, + .vunmap = amdxdna_gem_obj_vunmap, .mmap = amdxdna_gem_obj_mmap, .vm_ops = &drm_gem_shmem_vm_ops, .export = amdxdna_gem_prime_export, @@ -494,6 +532,68 @@ amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size) return to_gobj(abo); } +static struct amdxdna_gem_obj * +amdxdna_gem_create_shmem_object(struct drm_device *dev, size_t size) +{ + struct drm_gem_shmem_object *shmem = drm_gem_shmem_create(dev, size); + + if (IS_ERR(shmem)) + return ERR_CAST(shmem); + + shmem->map_wc = false; + return to_xdna_obj(&shmem->base); +} + +static struct amdxdna_gem_obj * +amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + enum amdxdna_ubuf_flag flags = 0; + struct amdxdna_drm_va_tbl va_tbl; + struct drm_gem_object *gobj; + struct dma_buf *dma_buf; + + if (copy_from_user(&va_tbl, u64_to_user_ptr(args->vaddr), sizeof(va_tbl))) { + XDNA_DBG(xdna, "Access va table failed"); + return ERR_PTR(-EINVAL); + } + + if (va_tbl.num_entries) { + if (args->type == AMDXDNA_BO_CMD) + flags |= AMDXDNA_UBUF_FLAG_MAP_DMA; + + dma_buf = amdxdna_get_ubuf(dev, flags, va_tbl.num_entries, + u64_to_user_ptr(args->vaddr + sizeof(va_tbl))); + } else { + dma_buf = dma_buf_get(va_tbl.dmabuf_fd); + } + + if (IS_ERR(dma_buf)) + return ERR_CAST(dma_buf); + + gobj = amdxdna_gem_prime_import(dev, dma_buf); + if (IS_ERR(gobj)) { + dma_buf_put(dma_buf); + return ERR_CAST(gobj); + } + + dma_buf_put(dma_buf); + + return to_xdna_obj(gobj); +} + +static struct amdxdna_gem_obj * +amdxdna_gem_create_object(struct drm_device *dev, + struct amdxdna_drm_create_bo *args) +{ + size_t aligned_sz = PAGE_ALIGN(args->size); + + if (args->vaddr) + return amdxdna_gem_create_ubuf_object(dev, args); + + return amdxdna_gem_create_shmem_object(dev, aligned_sz); +} + struct drm_gem_object * amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) { @@ -545,16 +645,12 @@ amdxdna_drm_alloc_shmem(struct drm_device *dev, struct drm_file *filp) { struct amdxdna_client *client = filp->driver_priv; - struct drm_gem_shmem_object *shmem; struct amdxdna_gem_obj *abo; - shmem = drm_gem_shmem_create(dev, args->size); - if (IS_ERR(shmem)) - return ERR_CAST(shmem); + abo = amdxdna_gem_create_object(dev, args); + if (IS_ERR(abo)) + return ERR_CAST(abo); - shmem->map_wc = false; - - abo = to_xdna_obj(&shmem->base); abo->client = client; abo->type = AMDXDNA_BO_SHMEM; @@ -569,7 +665,6 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev, struct amdxdna_client *client = filp->driver_priv; struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); struct amdxdna_dev *xdna = to_xdna_dev(dev); - struct drm_gem_shmem_object *shmem; struct amdxdna_gem_obj *abo; int ret; @@ -586,14 +681,12 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev, goto mm_unlock; } - shmem = drm_gem_shmem_create(dev, args->size); - if (IS_ERR(shmem)) { - ret = PTR_ERR(shmem); + abo = amdxdna_gem_create_object(dev, args); + if (IS_ERR(abo)) { + ret = PTR_ERR(abo); goto mm_unlock; } - shmem->map_wc = false; - abo = to_xdna_obj(&shmem->base); abo->type = AMDXDNA_BO_DEV_HEAP; abo->client = client; abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base; @@ -657,7 +750,6 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev, { struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); struct amdxdna_dev *xdna = to_xdna_dev(dev); - struct drm_gem_shmem_object *shmem; struct amdxdna_gem_obj *abo; int ret; @@ -671,12 +763,9 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev, return ERR_PTR(-EINVAL); } - shmem = drm_gem_shmem_create(dev, args->size); - if (IS_ERR(shmem)) - return ERR_CAST(shmem); - - shmem->map_wc = false; - abo = to_xdna_obj(&shmem->base); + abo = amdxdna_gem_create_object(dev, args); + if (IS_ERR(abo)) + return ERR_CAST(abo); abo->type = AMDXDNA_BO_CMD; abo->client = filp->driver_priv; @@ -691,7 +780,7 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev, return abo; release_obj: - drm_gem_shmem_free(shmem); + drm_gem_object_put(to_gobj(abo)); return ERR_PTR(ret); } @@ -702,7 +791,7 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f struct amdxdna_gem_obj *abo; int ret; - if (args->flags || args->vaddr || !args->size) + if (args->flags) return -EINVAL; XDNA_DBG(xdna, "BO arg type %d vaddr 0x%llx size 0x%llx flags 0x%llx", diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index f2bf1d374cc7..569cd703729d 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -26,6 +26,13 @@ MODULE_FIRMWARE("amdnpu/17f0_10/npu.sbin"); MODULE_FIRMWARE("amdnpu/17f0_11/npu.sbin"); MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin"); +/* + * 0.0: Initial version + * 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY + */ +#define AMDXDNA_DRIVER_MAJOR 0 +#define AMDXDNA_DRIVER_MINOR 1 + /* * Bind the driver base on (vendor_id, device_id) pair and later use the * (device_id, rev_id) pair as a key to select the devices. The devices with @@ -81,7 +88,6 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) ret = -ENODEV; goto unbind_sva; } - mutex_init(&client->hwctx_lock); init_srcu_struct(&client->hwctx_srcu); xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); mutex_init(&client->mm_lock); @@ -116,7 +122,6 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp) xa_destroy(&client->hwctx_xa); cleanup_srcu_struct(&client->hwctx_srcu); - mutex_destroy(&client->hwctx_lock); mutex_destroy(&client->mm_lock); if (client->dev_heap) drm_gem_object_put(to_gobj(client->dev_heap)); @@ -142,8 +147,8 @@ static int amdxdna_flush(struct file *f, fl_owner_t id) mutex_lock(&xdna->dev_lock); list_del_init(&client->node); - mutex_unlock(&xdna->dev_lock); amdxdna_hwctx_remove_all(client); + mutex_unlock(&xdna->dev_lock); drm_dev_exit(idx); return 0; @@ -166,6 +171,23 @@ static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct return ret; } +static int amdxdna_drm_get_array_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct amdxdna_client *client = filp->driver_priv; + struct amdxdna_dev *xdna = to_xdna_dev(dev); + struct amdxdna_drm_get_array *args = data; + + if (!xdna->dev_info->ops->get_array) + return -EOPNOTSUPP; + + if (args->pad || !args->num_element || !args->element_size) + return -EINVAL; + + guard(mutex)(&xdna->dev_lock); + return xdna->dev_info->ops->get_array(client, args); +} + static int amdxdna_drm_set_state_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct amdxdna_client *client = filp->driver_priv; @@ -197,6 +219,7 @@ static const struct drm_ioctl_desc amdxdna_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(AMDXDNA_EXEC_CMD, amdxdna_drm_submit_cmd_ioctl, 0), /* AIE hardware */ DRM_IOCTL_DEF_DRV(AMDXDNA_GET_INFO, amdxdna_drm_get_info_ioctl, 0), + DRM_IOCTL_DEF_DRV(AMDXDNA_GET_ARRAY, amdxdna_drm_get_array_ioctl, 0), DRM_IOCTL_DEF_DRV(AMDXDNA_SET_STATE, amdxdna_drm_set_state_ioctl, DRM_ROOT_ONLY), }; @@ -220,6 +243,8 @@ const struct drm_driver amdxdna_drm_drv = { .fops = &amdxdna_fops, .name = "amdxdna_accel_driver", .desc = "AMD XDNA DRM implementation", + .major = AMDXDNA_DRIVER_MAJOR, + .minor = AMDXDNA_DRIVER_MINOR, .open = amdxdna_drm_open, .postclose = amdxdna_drm_close, .ioctls = amdxdna_drm_ioctls, @@ -330,11 +355,8 @@ static void amdxdna_remove(struct pci_dev *pdev) struct amdxdna_client, node); while (client) { list_del_init(&client->node); - mutex_unlock(&xdna->dev_lock); - amdxdna_hwctx_remove_all(client); - mutex_lock(&xdna->dev_lock); client = list_first_entry_or_null(&xdna->client_list, struct amdxdna_client, node); } @@ -343,89 +365,29 @@ static void amdxdna_remove(struct pci_dev *pdev) mutex_unlock(&xdna->dev_lock); } -static int amdxdna_dev_suspend_nolock(struct amdxdna_dev *xdna) -{ - if (xdna->dev_info->ops->suspend) - xdna->dev_info->ops->suspend(xdna); - - return 0; -} - -static int amdxdna_dev_resume_nolock(struct amdxdna_dev *xdna) -{ - if (xdna->dev_info->ops->resume) - return xdna->dev_info->ops->resume(xdna); - - return 0; -} - static int amdxdna_pmops_suspend(struct device *dev) { struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev)); - struct amdxdna_client *client; - mutex_lock(&xdna->dev_lock); - list_for_each_entry(client, &xdna->client_list, node) - amdxdna_hwctx_suspend(client); + if (!xdna->dev_info->ops->suspend) + return -EOPNOTSUPP; - amdxdna_dev_suspend_nolock(xdna); - mutex_unlock(&xdna->dev_lock); - - return 0; + return xdna->dev_info->ops->suspend(xdna); } static int amdxdna_pmops_resume(struct device *dev) { struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev)); - struct amdxdna_client *client; - int ret; - XDNA_INFO(xdna, "firmware resuming..."); - mutex_lock(&xdna->dev_lock); - ret = amdxdna_dev_resume_nolock(xdna); - if (ret) { - XDNA_ERR(xdna, "resume NPU firmware failed"); - mutex_unlock(&xdna->dev_lock); - return ret; - } + if (!xdna->dev_info->ops->resume) + return -EOPNOTSUPP; - XDNA_INFO(xdna, "hardware context resuming..."); - list_for_each_entry(client, &xdna->client_list, node) - amdxdna_hwctx_resume(client); - mutex_unlock(&xdna->dev_lock); - - return 0; -} - -static int amdxdna_rpmops_suspend(struct device *dev) -{ - struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev)); - int ret; - - mutex_lock(&xdna->dev_lock); - ret = amdxdna_dev_suspend_nolock(xdna); - mutex_unlock(&xdna->dev_lock); - - XDNA_DBG(xdna, "Runtime suspend done ret: %d", ret); - return ret; -} - -static int amdxdna_rpmops_resume(struct device *dev) -{ - struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev)); - int ret; - - mutex_lock(&xdna->dev_lock); - ret = amdxdna_dev_resume_nolock(xdna); - mutex_unlock(&xdna->dev_lock); - - XDNA_DBG(xdna, "Runtime resume done ret: %d", ret); - return ret; + return xdna->dev_info->ops->resume(xdna); } static const struct dev_pm_ops amdxdna_pm_ops = { SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume) - RUNTIME_PM_OPS(amdxdna_rpmops_suspend, amdxdna_rpmops_resume, NULL) + RUNTIME_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume, NULL) }; static struct pci_driver amdxdna_pci_driver = { diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index ab79600911aa..72d6696d49da 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -50,16 +50,15 @@ struct amdxdna_dev_ops { int (*init)(struct amdxdna_dev *xdna); void (*fini)(struct amdxdna_dev *xdna); int (*resume)(struct amdxdna_dev *xdna); - void (*suspend)(struct amdxdna_dev *xdna); + int (*suspend)(struct amdxdna_dev *xdna); int (*hwctx_init)(struct amdxdna_hwctx *hwctx); void (*hwctx_fini)(struct amdxdna_hwctx *hwctx); int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size); void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq); - void (*hwctx_suspend)(struct amdxdna_hwctx *hwctx); - void (*hwctx_resume)(struct amdxdna_hwctx *hwctx); int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq); int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args); int (*set_aie_state)(struct amdxdna_client *client, struct amdxdna_drm_set_state *args); + int (*get_array)(struct amdxdna_client *client, struct amdxdna_drm_get_array *args); }; /* @@ -118,8 +117,6 @@ struct amdxdna_device_id { struct amdxdna_client { struct list_head node; pid_t pid; - struct mutex hwctx_lock; /* protect hwctx */ - /* do NOT wait this srcu when hwctx_lock is held */ struct srcu_struct hwctx_srcu; struct xarray hwctx_xa; u32 next_hwctxid; diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c new file mode 100644 index 000000000000..077b2261cf2a --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "amdxdna_pci_drv.h" +#include "amdxdna_ubuf.h" + +struct amdxdna_ubuf_priv { + struct page **pages; + u64 nr_pages; + enum amdxdna_ubuf_flag flags; + struct mm_struct *mm; +}; + +static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach, + enum dma_data_direction direction) +{ + struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; + struct sg_table *sg; + int ret; + + sg = kzalloc(sizeof(*sg), GFP_KERNEL); + if (!sg) + return ERR_PTR(-ENOMEM); + + ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0, + ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL); + if (ret) + return ERR_PTR(ret); + + if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) { + ret = dma_map_sgtable(attach->dev, sg, direction, 0); + if (ret) + return ERR_PTR(ret); + } + + return sg; +} + +static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach, + struct sg_table *sg, + enum dma_data_direction direction) +{ + struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; + + if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) + dma_unmap_sgtable(attach->dev, sg, direction, 0); + + sg_free_table(sg); + kfree(sg); +} + +static void amdxdna_ubuf_release(struct dma_buf *dbuf) +{ + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; + + unpin_user_pages(ubuf->pages, ubuf->nr_pages); + kvfree(ubuf->pages); + atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); + mmdrop(ubuf->mm); + kfree(ubuf); +} + +static vm_fault_t amdxdna_ubuf_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct amdxdna_ubuf_priv *ubuf; + unsigned long pfn; + pgoff_t pgoff; + + ubuf = vma->vm_private_data; + pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; + + pfn = page_to_pfn(ubuf->pages[pgoff]); + return vmf_insert_pfn(vma, vmf->address, pfn); +} + +static const struct vm_operations_struct amdxdna_ubuf_vm_ops = { + .fault = amdxdna_ubuf_vm_fault, +}; + +static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) +{ + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; + + vma->vm_ops = &amdxdna_ubuf_vm_ops; + vma->vm_private_data = ubuf; + vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); + + return 0; +} + +static int amdxdna_ubuf_vmap(struct dma_buf *dbuf, struct iosys_map *map) +{ + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; + void *kva; + + kva = vmap(ubuf->pages, ubuf->nr_pages, VM_MAP, PAGE_KERNEL); + if (!kva) + return -EINVAL; + + iosys_map_set_vaddr(map, kva); + return 0; +} + +static void amdxdna_ubuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map) +{ + vunmap(map->vaddr); +} + +static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = { + .map_dma_buf = amdxdna_ubuf_map, + .unmap_dma_buf = amdxdna_ubuf_unmap, + .release = amdxdna_ubuf_release, + .mmap = amdxdna_ubuf_mmap, + .vmap = amdxdna_ubuf_vmap, + .vunmap = amdxdna_ubuf_vunmap, +}; + +struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, + enum amdxdna_ubuf_flag flags, + u32 num_entries, void __user *va_entries) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + unsigned long lock_limit, new_pinned; + struct amdxdna_drm_va_entry *va_ent; + struct amdxdna_ubuf_priv *ubuf; + u32 npages, start = 0; + struct dma_buf *dbuf; + int i, ret; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + if (!can_do_mlock()) + return ERR_PTR(-EPERM); + + ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); + if (!ubuf) + return ERR_PTR(-ENOMEM); + + ubuf->flags = flags; + ubuf->mm = current->mm; + mmgrab(ubuf->mm); + + va_ent = kvcalloc(num_entries, sizeof(*va_ent), GFP_KERNEL); + if (!va_ent) { + ret = -ENOMEM; + goto free_ubuf; + } + + if (copy_from_user(va_ent, va_entries, sizeof(*va_ent) * num_entries)) { + XDNA_DBG(xdna, "Access va entries failed"); + ret = -EINVAL; + goto free_ent; + } + + for (i = 0, exp_info.size = 0; i < num_entries; i++) { + if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) || + !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) { + XDNA_ERR(xdna, "Invalid address or len %llx, %llx", + va_ent[i].vaddr, va_ent[i].len); + ret = -EINVAL; + goto free_ent; + } + + exp_info.size += va_ent[i].len; + } + + ubuf->nr_pages = exp_info.size >> PAGE_SHIFT; + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm); + if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) { + XDNA_DBG(xdna, "New pin %ld, limit %ld, cap %d", + new_pinned, lock_limit, capable(CAP_IPC_LOCK)); + ret = -ENOMEM; + goto sub_pin_cnt; + } + + ubuf->pages = kvmalloc_array(ubuf->nr_pages, sizeof(*ubuf->pages), GFP_KERNEL); + if (!ubuf->pages) { + ret = -ENOMEM; + goto sub_pin_cnt; + } + + for (i = 0; i < num_entries; i++) { + npages = va_ent[i].len >> PAGE_SHIFT; + + ret = pin_user_pages_fast(va_ent[i].vaddr, npages, + FOLL_WRITE | FOLL_LONGTERM, + &ubuf->pages[start]); + if (ret < 0 || ret != npages) { + ret = -ENOMEM; + XDNA_ERR(xdna, "Failed to pin pages ret %d", ret); + goto destroy_pages; + } + + start += ret; + } + + exp_info.ops = &amdxdna_ubuf_dmabuf_ops; + exp_info.priv = ubuf; + exp_info.flags = O_RDWR | O_CLOEXEC; + + dbuf = dma_buf_export(&exp_info); + if (IS_ERR(dbuf)) { + ret = PTR_ERR(dbuf); + goto destroy_pages; + } + kvfree(va_ent); + + return dbuf; + +destroy_pages: + if (start) + unpin_user_pages(ubuf->pages, start); + kvfree(ubuf->pages); +sub_pin_cnt: + atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); +free_ent: + kvfree(va_ent); +free_ubuf: + mmdrop(ubuf->mm); + kfree(ubuf); + return ERR_PTR(ret); +} diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.h b/drivers/accel/amdxdna/amdxdna_ubuf.h new file mode 100644 index 000000000000..e5cb3bdb3ec9 --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_ubuf.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025, Advanced Micro Devices, Inc. + */ +#ifndef _AMDXDNA_UBUF_H_ +#define _AMDXDNA_UBUF_H_ + +#include +#include + +enum amdxdna_ubuf_flag { + AMDXDNA_UBUF_FLAG_MAP_DMA = 1, +}; + +struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, + enum amdxdna_ubuf_flag flags, + u32 num_entries, void __user *va_entries); + +#endif /* _AMDXDNA_UBUF_H_ */ diff --git a/drivers/accel/habanalabs/common/memory.c b/drivers/accel/habanalabs/common/memory.c index 601fdbe70179..61472a381904 100644 --- a/drivers/accel/habanalabs/common/memory.c +++ b/drivers/accel/habanalabs/common/memory.c @@ -1829,9 +1829,6 @@ static void hl_release_dmabuf(struct dma_buf *dmabuf) struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv; struct hl_ctx *ctx; - if (!hl_dmabuf) - return; - ctx = hl_dmabuf->ctx; if (hl_dmabuf->memhash_hnode) @@ -1859,7 +1856,12 @@ static int export_dmabuf(struct hl_ctx *ctx, { DEFINE_DMA_BUF_EXPORT_INFO(exp_info); struct hl_device *hdev = ctx->hdev; - int rc, fd; + CLASS(get_unused_fd, fd)(flags); + + if (fd < 0) { + dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf, %d\n", fd); + return fd; + } exp_info.ops = &habanalabs_dmabuf_ops; exp_info.size = total_size; @@ -1872,13 +1874,6 @@ static int export_dmabuf(struct hl_ctx *ctx, return PTR_ERR(hl_dmabuf->dmabuf); } - fd = dma_buf_fd(hl_dmabuf->dmabuf, flags); - if (fd < 0) { - dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf, %d\n", fd); - rc = fd; - goto err_dma_buf_put; - } - hl_dmabuf->ctx = ctx; hl_ctx_get(hl_dmabuf->ctx); atomic_inc(&ctx->hdev->dmabuf_export_cnt); @@ -1890,13 +1885,9 @@ static int export_dmabuf(struct hl_ctx *ctx, get_file(ctx->hpriv->file_priv->filp); *dmabuf_fd = fd; + fd_install(take_fd(fd), hl_dmabuf->dmabuf->file); return 0; - -err_dma_buf_put: - hl_dmabuf->dmabuf->priv = NULL; - dma_buf_put(hl_dmabuf->dmabuf); - return rc; } static int validate_export_params_common(struct hl_device *hdev, u64 addr, u64 size, u64 offset) diff --git a/drivers/accel/habanalabs/common/sysfs.c b/drivers/accel/habanalabs/common/sysfs.c index 9d58efa2ff38..82f66520ec18 100644 --- a/drivers/accel/habanalabs/common/sysfs.c +++ b/drivers/accel/habanalabs/common/sysfs.c @@ -446,7 +446,7 @@ static DEVICE_ATTR_RO(parent_device); static const struct bin_attribute bin_attr_eeprom = { .attr = {.name = "eeprom", .mode = (0444)}, .size = PAGE_SIZE, - .read_new = eeprom_read_handler + .read = eeprom_read_handler }; static struct attribute *hl_dev_attrs[] = { @@ -479,7 +479,7 @@ static const struct bin_attribute *const hl_dev_bin_attrs[] = { static struct attribute_group hl_dev_attr_group = { .attrs = hl_dev_attrs, - .bin_attrs_new = hl_dev_bin_attrs, + .bin_attrs = hl_dev_bin_attrs, }; static struct attribute_group hl_dev_clks_attr_group; diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2.c b/drivers/accel/habanalabs/gaudi2/gaudi2.c index a38b88baadf2..5722e4128d3c 100644 --- a/drivers/accel/habanalabs/gaudi2/gaudi2.c +++ b/drivers/accel/habanalabs/gaudi2/gaudi2.c @@ -10437,7 +10437,7 @@ static int gaudi2_memset_device_memory(struct hl_device *hdev, u64 addr, u64 siz (u64 *)(lin_dma_pkts_arr), DEBUGFS_WRITE64); WREG32(sob_addr, 0); - kfree(lin_dma_pkts_arr); + kvfree(lin_dma_pkts_arr); return rc; } diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 3d6d52492536..3289751b4757 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -677,7 +677,7 @@ static void ivpu_bo_unbind_all_user_contexts(struct ivpu_device *vdev) static void ivpu_dev_fini(struct ivpu_device *vdev) { ivpu_jobs_abort_all(vdev); - ivpu_pm_cancel_recovery(vdev); + ivpu_pm_disable_recovery(vdev); ivpu_pm_disable(vdev); ivpu_prepare_for_reset(vdev); ivpu_shutdown(vdev); diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h index 9a3935be1c05..7081913fb0dd 100644 --- a/drivers/accel/ivpu/ivpu_fw.h +++ b/drivers/accel/ivpu/ivpu_fw.h @@ -45,7 +45,7 @@ struct ivpu_fw_info { int ivpu_fw_init(struct ivpu_device *vdev); void ivpu_fw_fini(struct ivpu_device *vdev); void ivpu_fw_load(struct ivpu_device *vdev); -void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *bp); +void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params); static inline bool ivpu_fw_is_cold_boot(struct ivpu_device *vdev) { diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index b236c7234daa..afdb3b2aa72a 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -33,7 +33,6 @@ #define PLL_CDYN_DEFAULT 0x80 #define PLL_EPP_DEFAULT 0x80 -#define PLL_CONFIG_DEFAULT 0x0 #define PLL_REF_CLK_FREQ 50000000ull #define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ) @@ -303,7 +302,7 @@ static void prepare_wp_request(struct ivpu_device *vdev, struct wp_request *wp, wp->epp = 0; } else { wp->target = hw->pll.pn_ratio; - wp->cfg = enable ? PLL_CONFIG_DEFAULT : 0; + wp->cfg = 0; wp->cdyn = enable ? PLL_CDYN_DEFAULT : 0; wp->epp = enable ? PLL_EPP_DEFAULT : 0; } diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.h b/drivers/accel/ivpu/ivpu_hw_btrs.h index d2d82651976d..032c384ac3d4 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.h +++ b/drivers/accel/ivpu/ivpu_hw_btrs.h @@ -36,7 +36,7 @@ u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev); bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq); bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq); int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable); -void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 dct_percent); +void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent); u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev); u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev); u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index 39f83225c181..5f00809d448a 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -141,7 +141,6 @@ ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, struct ivpu_ipc_rx_msg *rx_msg; lockdep_assert_held(&ipc->cons_lock); - lockdep_assert_irqs_disabled(); rx_msg = kzalloc(sizeof(*rx_msg), GFP_ATOMIC); if (!rx_msg) { diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index eacda1dbe840..475ddc94f1cf 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -417,10 +417,10 @@ void ivpu_pm_init(struct ivpu_device *vdev) ivpu_dbg(vdev, PM, "Autosuspend delay = %d\n", delay); } -void ivpu_pm_cancel_recovery(struct ivpu_device *vdev) +void ivpu_pm_disable_recovery(struct ivpu_device *vdev) { drm_WARN_ON(&vdev->drm, delayed_work_pending(&vdev->pm->job_timeout_work)); - cancel_work_sync(&vdev->pm->recovery_work); + disable_work_sync(&vdev->pm->recovery_work); } void ivpu_pm_enable(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_pm.h b/drivers/accel/ivpu/ivpu_pm.h index 89b264cc0e3e..a2aa7a27f32e 100644 --- a/drivers/accel/ivpu/ivpu_pm.h +++ b/drivers/accel/ivpu/ivpu_pm.h @@ -25,7 +25,7 @@ struct ivpu_pm_info { void ivpu_pm_init(struct ivpu_device *vdev); void ivpu_pm_enable(struct ivpu_device *vdev); void ivpu_pm_disable(struct ivpu_device *vdev); -void ivpu_pm_cancel_recovery(struct ivpu_device *vdev); +void ivpu_pm_disable_recovery(struct ivpu_device *vdev); int ivpu_pm_suspend_cb(struct device *dev); int ivpu_pm_resume_cb(struct device *dev); diff --git a/drivers/accel/rocket/Kconfig b/drivers/accel/rocket/Kconfig new file mode 100644 index 000000000000..16465abe0660 --- /dev/null +++ b/drivers/accel/rocket/Kconfig @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config DRM_ACCEL_ROCKET + tristate "Rocket (support for Rockchip NPUs)" + depends on DRM_ACCEL + depends on (ARCH_ROCKCHIP && ARM64) || COMPILE_TEST + depends on ROCKCHIP_IOMMU || COMPILE_TEST + depends on MMU + select DRM_SCHED + select DRM_GEM_SHMEM_HELPER + help + Choose this option if you have a Rockchip SoC that contains a + compatible Neural Processing Unit (NPU), such as the RK3588. Called by + Rockchip either RKNN or RKNPU, it accelerates inference of neural + networks. + + The interface exposed to userspace is described in + include/uapi/drm/rocket_accel.h and is used by the Rocket userspace + driver in Mesa3D. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called rocket. diff --git a/drivers/accel/rocket/Makefile b/drivers/accel/rocket/Makefile new file mode 100644 index 000000000000..3713dfe223d6 --- /dev/null +++ b/drivers/accel/rocket/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_DRM_ACCEL_ROCKET) := rocket.o + +rocket-y := \ + rocket_core.o \ + rocket_device.o \ + rocket_drv.o \ + rocket_gem.o \ + rocket_job.o diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c new file mode 100644 index 000000000000..abe7719c1db4 --- /dev/null +++ b/drivers/accel/rocket/rocket_core.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2024-2025 Tomeu Vizoso */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rocket_core.h" +#include "rocket_job.h" + +int rocket_core_init(struct rocket_core *core) +{ + struct device *dev = core->dev; + struct platform_device *pdev = to_platform_device(dev); + u32 version; + int err = 0; + + core->resets[0].id = "srst_a"; + core->resets[1].id = "srst_h"; + err = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(core->resets), + core->resets); + if (err) + return dev_err_probe(dev, err, "failed to get resets for core %d\n", core->index); + + err = devm_clk_bulk_get(dev, ARRAY_SIZE(core->clks), core->clks); + if (err) + return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index); + + core->pc_iomem = devm_platform_ioremap_resource_byname(pdev, "pc"); + if (IS_ERR(core->pc_iomem)) { + dev_err(dev, "couldn't find PC registers %ld\n", PTR_ERR(core->pc_iomem)); + return PTR_ERR(core->pc_iomem); + } + + core->cna_iomem = devm_platform_ioremap_resource_byname(pdev, "cna"); + if (IS_ERR(core->cna_iomem)) { + dev_err(dev, "couldn't find CNA registers %ld\n", PTR_ERR(core->cna_iomem)); + return PTR_ERR(core->cna_iomem); + } + + core->core_iomem = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(core->core_iomem)) { + dev_err(dev, "couldn't find CORE registers %ld\n", PTR_ERR(core->core_iomem)); + return PTR_ERR(core->core_iomem); + } + + dma_set_max_seg_size(dev, UINT_MAX); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + if (err) + return err; + + core->iommu_group = iommu_group_get(dev); + + err = rocket_job_init(core); + if (err) + return err; + + pm_runtime_use_autosuspend(dev); + + /* + * As this NPU will be most often used as part of a media pipeline that + * ends presenting in a display, choose 50 ms (~3 frames at 60Hz) as an + * autosuspend delay as that will keep the device powered up while the + * pipeline is running. + */ + pm_runtime_set_autosuspend_delay(dev, 50); + + pm_runtime_enable(dev); + + err = pm_runtime_resume_and_get(dev); + if (err) { + rocket_job_fini(core); + return err; + } + + version = rocket_pc_readl(core, VERSION); + version += rocket_pc_readl(core, VERSION_NUM) & 0xffff; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + dev_info(dev, "Rockchip NPU core %d version: %d\n", core->index, version); + + return 0; +} + +void rocket_core_fini(struct rocket_core *core) +{ + pm_runtime_dont_use_autosuspend(core->dev); + pm_runtime_disable(core->dev); + iommu_group_put(core->iommu_group); + core->iommu_group = NULL; + rocket_job_fini(core); +} + +void rocket_core_reset(struct rocket_core *core) +{ + reset_control_bulk_assert(ARRAY_SIZE(core->resets), core->resets); + + udelay(10); + + reset_control_bulk_deassert(ARRAY_SIZE(core->resets), core->resets); +} diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h new file mode 100644 index 000000000000..f6d7382854ca --- /dev/null +++ b/drivers/accel/rocket/rocket_core.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#ifndef __ROCKET_CORE_H__ +#define __ROCKET_CORE_H__ + +#include +#include +#include +#include +#include + +#include "rocket_registers.h" + +#define rocket_pc_readl(core, reg) \ + readl((core)->pc_iomem + (REG_PC_##reg)) +#define rocket_pc_writel(core, reg, value) \ + writel(value, (core)->pc_iomem + (REG_PC_##reg)) + +#define rocket_cna_readl(core, reg) \ + readl((core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS) +#define rocket_cna_writel(core, reg, value) \ + writel(value, (core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS) + +#define rocket_core_readl(core, reg) \ + readl((core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS) +#define rocket_core_writel(core, reg, value) \ + writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS) + +struct rocket_core { + struct device *dev; + struct rocket_device *rdev; + unsigned int index; + + int irq; + void __iomem *pc_iomem; + void __iomem *cna_iomem; + void __iomem *core_iomem; + struct clk_bulk_data clks[4]; + struct reset_control_bulk_data resets[2]; + + struct iommu_group *iommu_group; + + struct mutex job_lock; + struct rocket_job *in_flight_job; + + spinlock_t fence_lock; + + struct { + struct workqueue_struct *wq; + struct work_struct work; + atomic_t pending; + } reset; + + struct drm_gpu_scheduler sched; + u64 fence_context; + u64 emit_seqno; +}; + +int rocket_core_init(struct rocket_core *core); +void rocket_core_fini(struct rocket_core *core); +void rocket_core_reset(struct rocket_core *core); + +#endif diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c new file mode 100644 index 000000000000..46e6ee1e72c5 --- /dev/null +++ b/drivers/accel/rocket/rocket_device.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2024-2025 Tomeu Vizoso */ + +#include +#include +#include +#include +#include +#include + +#include "rocket_device.h" + +struct rocket_device *rocket_device_init(struct platform_device *pdev, + const struct drm_driver *rocket_drm_driver) +{ + struct device *dev = &pdev->dev; + struct device_node *core_node; + struct rocket_device *rdev; + struct drm_device *ddev; + unsigned int num_cores = 0; + int err; + + rdev = devm_drm_dev_alloc(dev, rocket_drm_driver, struct rocket_device, ddev); + if (IS_ERR(rdev)) + return rdev; + + ddev = &rdev->ddev; + dev_set_drvdata(dev, rdev); + + for_each_compatible_node(core_node, NULL, "rockchip,rk3588-rknn-core") + if (of_device_is_available(core_node)) + num_cores++; + + rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL); + if (!rdev->cores) + return ERR_PTR(-ENOMEM); + + dma_set_max_seg_size(dev, UINT_MAX); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + if (err) + return ERR_PTR(err); + + err = devm_mutex_init(dev, &rdev->sched_lock); + if (err) + return ERR_PTR(-ENOMEM); + + err = drm_dev_register(ddev, 0); + if (err) + return ERR_PTR(err); + + return rdev; +} + +void rocket_device_fini(struct rocket_device *rdev) +{ + WARN_ON(rdev->num_cores > 0); + + drm_dev_unregister(&rdev->ddev); +} diff --git a/drivers/accel/rocket/rocket_device.h b/drivers/accel/rocket/rocket_device.h new file mode 100644 index 000000000000..ce662abc01d3 --- /dev/null +++ b/drivers/accel/rocket/rocket_device.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#ifndef __ROCKET_DEVICE_H__ +#define __ROCKET_DEVICE_H__ + +#include +#include +#include +#include +#include + +#include "rocket_core.h" + +struct rocket_device { + struct drm_device ddev; + + struct mutex sched_lock; + + struct rocket_core *cores; + unsigned int num_cores; +}; + +struct rocket_device *rocket_device_init(struct platform_device *pdev, + const struct drm_driver *rocket_drm_driver); +void rocket_device_fini(struct rocket_device *rdev); +#define to_rocket_device(drm_dev) \ + ((struct rocket_device *)(container_of((drm_dev), struct rocket_device, ddev))) + +#endif /* __ROCKET_DEVICE_H__ */ diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c new file mode 100644 index 000000000000..5c0b63f0a8f0 --- /dev/null +++ b/drivers/accel/rocket/rocket_drv.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2024-2025 Tomeu Vizoso */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rocket_drv.h" +#include "rocket_gem.h" +#include "rocket_job.h" + +/* + * Facade device, used to expose a single DRM device to userspace, that + * schedules jobs to any RKNN cores in the system. + */ +static struct platform_device *drm_dev; +static struct rocket_device *rdev; + +static void +rocket_iommu_domain_destroy(struct kref *kref) +{ + struct rocket_iommu_domain *domain = container_of(kref, struct rocket_iommu_domain, kref); + + iommu_domain_free(domain->domain); + domain->domain = NULL; + kfree(domain); +} + +static struct rocket_iommu_domain* +rocket_iommu_domain_create(struct device *dev) +{ + struct rocket_iommu_domain *domain = kmalloc(sizeof(*domain), GFP_KERNEL); + void *err; + + if (!domain) + return ERR_PTR(-ENOMEM); + + domain->domain = iommu_paging_domain_alloc(dev); + if (IS_ERR(domain->domain)) { + err = ERR_CAST(domain->domain); + kfree(domain); + return err; + } + kref_init(&domain->kref); + + return domain; +} + +struct rocket_iommu_domain * +rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv) +{ + kref_get(&rocket_priv->domain->kref); + return rocket_priv->domain; +} + +void +rocket_iommu_domain_put(struct rocket_iommu_domain *domain) +{ + kref_put(&domain->kref, rocket_iommu_domain_destroy); +} + +static int +rocket_open(struct drm_device *dev, struct drm_file *file) +{ + struct rocket_device *rdev = to_rocket_device(dev); + struct rocket_file_priv *rocket_priv; + u64 start, end; + int ret; + + if (!try_module_get(THIS_MODULE)) + return -EINVAL; + + rocket_priv = kzalloc(sizeof(*rocket_priv), GFP_KERNEL); + if (!rocket_priv) { + ret = -ENOMEM; + goto err_put_mod; + } + + rocket_priv->rdev = rdev; + rocket_priv->domain = rocket_iommu_domain_create(rdev->cores[0].dev); + if (IS_ERR(rocket_priv->domain)) { + ret = PTR_ERR(rocket_priv->domain); + goto err_free; + } + + file->driver_priv = rocket_priv; + + start = rocket_priv->domain->domain->geometry.aperture_start; + end = rocket_priv->domain->domain->geometry.aperture_end; + drm_mm_init(&rocket_priv->mm, start, end - start + 1); + mutex_init(&rocket_priv->mm_lock); + + ret = rocket_job_open(rocket_priv); + if (ret) + goto err_mm_takedown; + + return 0; + +err_mm_takedown: + mutex_destroy(&rocket_priv->mm_lock); + drm_mm_takedown(&rocket_priv->mm); + rocket_iommu_domain_put(rocket_priv->domain); +err_free: + kfree(rocket_priv); +err_put_mod: + module_put(THIS_MODULE); + return ret; +} + +static void +rocket_postclose(struct drm_device *dev, struct drm_file *file) +{ + struct rocket_file_priv *rocket_priv = file->driver_priv; + + rocket_job_close(rocket_priv); + mutex_destroy(&rocket_priv->mm_lock); + drm_mm_takedown(&rocket_priv->mm); + rocket_iommu_domain_put(rocket_priv->domain); + kfree(rocket_priv); + module_put(THIS_MODULE); +} + +static const struct drm_ioctl_desc rocket_drm_driver_ioctls[] = { +#define ROCKET_IOCTL(n, func) \ + DRM_IOCTL_DEF_DRV(ROCKET_##n, rocket_ioctl_##func, 0) + + ROCKET_IOCTL(CREATE_BO, create_bo), + ROCKET_IOCTL(SUBMIT, submit), + ROCKET_IOCTL(PREP_BO, prep_bo), + ROCKET_IOCTL(FINI_BO, fini_bo), +}; + +DEFINE_DRM_ACCEL_FOPS(rocket_accel_driver_fops); + +/* + * Rocket driver version: + * - 1.0 - initial interface + */ +static const struct drm_driver rocket_drm_driver = { + .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM, + .open = rocket_open, + .postclose = rocket_postclose, + .gem_create_object = rocket_gem_create_object, + .ioctls = rocket_drm_driver_ioctls, + .num_ioctls = ARRAY_SIZE(rocket_drm_driver_ioctls), + .fops = &rocket_accel_driver_fops, + .name = "rocket", + .desc = "rocket DRM", +}; + +static int rocket_probe(struct platform_device *pdev) +{ + if (rdev == NULL) { + /* First core probing, initialize DRM device. */ + rdev = rocket_device_init(drm_dev, &rocket_drm_driver); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to initialize rocket device\n"); + return PTR_ERR(rdev); + } + } + + unsigned int core = rdev->num_cores; + + dev_set_drvdata(&pdev->dev, rdev); + + rdev->cores[core].rdev = rdev; + rdev->cores[core].dev = &pdev->dev; + rdev->cores[core].index = core; + + rdev->num_cores++; + + return rocket_core_init(&rdev->cores[core]); +} + +static void rocket_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + for (unsigned int core = 0; core < rdev->num_cores; core++) { + if (rdev->cores[core].dev == dev) { + rocket_core_fini(&rdev->cores[core]); + rdev->num_cores--; + break; + } + } + + if (rdev->num_cores == 0) { + /* Last core removed, deinitialize DRM device. */ + rocket_device_fini(rdev); + rdev = NULL; + } +} + +static const struct of_device_id dt_match[] = { + { .compatible = "rockchip,rk3588-rknn-core" }, + {} +}; +MODULE_DEVICE_TABLE(of, dt_match); + +static int find_core_for_dev(struct device *dev) +{ + struct rocket_device *rdev = dev_get_drvdata(dev); + + for (unsigned int core = 0; core < rdev->num_cores; core++) { + if (dev == rdev->cores[core].dev) + return core; + } + + return -1; +} + +static int rocket_device_runtime_resume(struct device *dev) +{ + struct rocket_device *rdev = dev_get_drvdata(dev); + int core = find_core_for_dev(dev); + int err = 0; + + if (core < 0) + return -ENODEV; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks); + if (err) { + dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core); + return err; + } + + return 0; +} + +static int rocket_device_runtime_suspend(struct device *dev) +{ + struct rocket_device *rdev = dev_get_drvdata(dev); + int core = find_core_for_dev(dev); + + if (core < 0) + return -ENODEV; + + if (!rocket_job_is_idle(&rdev->cores[core])) + return -EBUSY; + + clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks); + + return 0; +} + +EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = { + RUNTIME_PM_OPS(rocket_device_runtime_suspend, rocket_device_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +static struct platform_driver rocket_driver = { + .probe = rocket_probe, + .remove = rocket_remove, + .driver = { + .name = "rocket", + .pm = pm_ptr(&rocket_pm_ops), + .of_match_table = dt_match, + }, +}; + +static int __init rocket_register(void) +{ + drm_dev = platform_device_register_simple("rknn", -1, NULL, 0); + if (IS_ERR(drm_dev)) + return PTR_ERR(drm_dev); + + return platform_driver_register(&rocket_driver); +} + +static void __exit rocket_unregister(void) +{ + platform_driver_unregister(&rocket_driver); + + platform_device_unregister(drm_dev); +} + +module_init(rocket_register); +module_exit(rocket_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DRM driver for the Rockchip NPU IP"); +MODULE_AUTHOR("Tomeu Vizoso"); diff --git a/drivers/accel/rocket/rocket_drv.h b/drivers/accel/rocket/rocket_drv.h new file mode 100644 index 000000000000..2c673bb99ccc --- /dev/null +++ b/drivers/accel/rocket/rocket_drv.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#ifndef __ROCKET_DRV_H__ +#define __ROCKET_DRV_H__ + +#include +#include + +#include "rocket_device.h" + +extern const struct dev_pm_ops rocket_pm_ops; + +struct rocket_iommu_domain { + struct iommu_domain *domain; + struct kref kref; +}; + +struct rocket_file_priv { + struct rocket_device *rdev; + + struct rocket_iommu_domain *domain; + struct drm_mm mm; + struct mutex mm_lock; + + struct drm_sched_entity sched_entity; +}; + +struct rocket_iommu_domain *rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv); +void rocket_iommu_domain_put(struct rocket_iommu_domain *domain); + +#endif diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c new file mode 100644 index 000000000000..0551e11cc184 --- /dev/null +++ b/drivers/accel/rocket/rocket_gem.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2024-2025 Tomeu Vizoso */ + +#include +#include +#include +#include +#include + +#include "rocket_drv.h" +#include "rocket_gem.h" + +static void rocket_gem_bo_free(struct drm_gem_object *obj) +{ + struct rocket_gem_object *bo = to_rocket_bo(obj); + struct rocket_file_priv *rocket_priv = bo->driver_priv; + size_t unmapped; + + drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1); + + unmapped = iommu_unmap(bo->domain->domain, bo->mm.start, bo->size); + drm_WARN_ON(obj->dev, unmapped != bo->size); + + mutex_lock(&rocket_priv->mm_lock); + drm_mm_remove_node(&bo->mm); + mutex_unlock(&rocket_priv->mm_lock); + + rocket_iommu_domain_put(bo->domain); + bo->domain = NULL; + + drm_gem_shmem_free(&bo->base); +} + +static const struct drm_gem_object_funcs rocket_gem_funcs = { + .free = rocket_gem_bo_free, + .print_info = drm_gem_shmem_object_print_info, + .pin = drm_gem_shmem_object_pin, + .unpin = drm_gem_shmem_object_unpin, + .get_sg_table = drm_gem_shmem_object_get_sg_table, + .vmap = drm_gem_shmem_object_vmap, + .vunmap = drm_gem_shmem_object_vunmap, + .mmap = drm_gem_shmem_object_mmap, + .vm_ops = &drm_gem_shmem_vm_ops, +}; + +struct drm_gem_object *rocket_gem_create_object(struct drm_device *dev, size_t size) +{ + struct rocket_gem_object *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return ERR_PTR(-ENOMEM); + + obj->base.base.funcs = &rocket_gem_funcs; + + return &obj->base.base; +} + +int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct rocket_file_priv *rocket_priv = file->driver_priv; + struct drm_rocket_create_bo *args = data; + struct drm_gem_shmem_object *shmem_obj; + struct rocket_gem_object *rkt_obj; + struct drm_gem_object *gem_obj; + struct sg_table *sgt; + int ret; + + shmem_obj = drm_gem_shmem_create(dev, args->size); + if (IS_ERR(shmem_obj)) + return PTR_ERR(shmem_obj); + + gem_obj = &shmem_obj->base; + rkt_obj = to_rocket_bo(gem_obj); + + rkt_obj->driver_priv = rocket_priv; + rkt_obj->domain = rocket_iommu_domain_get(rocket_priv); + rkt_obj->size = args->size; + rkt_obj->offset = 0; + + ret = drm_gem_handle_create(file, gem_obj, &args->handle); + drm_gem_object_put(gem_obj); + if (ret) + goto err; + + sgt = drm_gem_shmem_get_pages_sgt(shmem_obj); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err; + } + + mutex_lock(&rocket_priv->mm_lock); + ret = drm_mm_insert_node_generic(&rocket_priv->mm, &rkt_obj->mm, + rkt_obj->size, PAGE_SIZE, + 0, 0); + mutex_unlock(&rocket_priv->mm_lock); + + ret = iommu_map_sgtable(rocket_priv->domain->domain, + rkt_obj->mm.start, + shmem_obj->sgt, + IOMMU_READ | IOMMU_WRITE); + if (ret < 0 || ret < args->size) { + drm_err(dev, "failed to map buffer: size=%d request_size=%u\n", + ret, args->size); + ret = -ENOMEM; + goto err_remove_node; + } + + /* iommu_map_sgtable might have aligned the size */ + rkt_obj->size = ret; + args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); + args->dma_address = rkt_obj->mm.start; + + return 0; + +err_remove_node: + mutex_lock(&rocket_priv->mm_lock); + drm_mm_remove_node(&rkt_obj->mm); + mutex_unlock(&rocket_priv->mm_lock); + +err: + drm_gem_shmem_object_free(gem_obj); + + return ret; +} + +int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_rocket_prep_bo *args = data; + unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns); + struct drm_gem_object *gem_obj; + struct drm_gem_shmem_object *shmem_obj; + long ret = 0; + + if (args->reserved != 0) { + drm_dbg(dev, "Reserved field in drm_rocket_prep_bo struct should be 0.\n"); + return -EINVAL; + } + + gem_obj = drm_gem_object_lookup(file, args->handle); + if (!gem_obj) + return -ENOENT; + + ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_WRITE, true, timeout); + if (!ret) + ret = timeout ? -ETIMEDOUT : -EBUSY; + + shmem_obj = &to_rocket_bo(gem_obj)->base; + + dma_sync_sgtable_for_cpu(dev->dev, shmem_obj->sgt, DMA_BIDIRECTIONAL); + + drm_gem_object_put(gem_obj); + + return ret; +} + +int rocket_ioctl_fini_bo(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_rocket_fini_bo *args = data; + struct drm_gem_shmem_object *shmem_obj; + struct rocket_gem_object *rkt_obj; + struct drm_gem_object *gem_obj; + + if (args->reserved != 0) { + drm_dbg(dev, "Reserved field in drm_rocket_fini_bo struct should be 0.\n"); + return -EINVAL; + } + + gem_obj = drm_gem_object_lookup(file, args->handle); + if (!gem_obj) + return -ENOENT; + + rkt_obj = to_rocket_bo(gem_obj); + shmem_obj = &rkt_obj->base; + + dma_sync_sgtable_for_device(dev->dev, shmem_obj->sgt, DMA_BIDIRECTIONAL); + + drm_gem_object_put(gem_obj); + + return 0; +} diff --git a/drivers/accel/rocket/rocket_gem.h b/drivers/accel/rocket/rocket_gem.h new file mode 100644 index 000000000000..240430334509 --- /dev/null +++ b/drivers/accel/rocket/rocket_gem.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#ifndef __ROCKET_GEM_H__ +#define __ROCKET_GEM_H__ + +#include + +struct rocket_gem_object { + struct drm_gem_shmem_object base; + + struct rocket_file_priv *driver_priv; + + struct rocket_iommu_domain *domain; + struct drm_mm_node mm; + size_t size; + u32 offset; +}; + +struct drm_gem_object *rocket_gem_create_object(struct drm_device *dev, size_t size); + +int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file *file); + +int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *file); + +int rocket_ioctl_fini_bo(struct drm_device *dev, void *data, struct drm_file *file); + +static inline +struct rocket_gem_object *to_rocket_bo(struct drm_gem_object *obj) +{ + return container_of(to_drm_gem_shmem_obj(obj), struct rocket_gem_object, base); +} + +#endif diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c new file mode 100644 index 000000000000..acd606160dc9 --- /dev/null +++ b/drivers/accel/rocket/rocket_job.c @@ -0,0 +1,637 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2019 Linaro, Ltd, Rob Herring */ +/* Copyright 2019 Collabora ltd. */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rocket_core.h" +#include "rocket_device.h" +#include "rocket_drv.h" +#include "rocket_job.h" +#include "rocket_registers.h" + +#define JOB_TIMEOUT_MS 500 + +static struct rocket_job * +to_rocket_job(struct drm_sched_job *sched_job) +{ + return container_of(sched_job, struct rocket_job, base); +} + +static const char *rocket_fence_get_driver_name(struct dma_fence *fence) +{ + return "rocket"; +} + +static const char *rocket_fence_get_timeline_name(struct dma_fence *fence) +{ + return "rockchip-npu"; +} + +static const struct dma_fence_ops rocket_fence_ops = { + .get_driver_name = rocket_fence_get_driver_name, + .get_timeline_name = rocket_fence_get_timeline_name, +}; + +static struct dma_fence *rocket_fence_create(struct rocket_core *core) +{ + struct dma_fence *fence; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return ERR_PTR(-ENOMEM); + + dma_fence_init(fence, &rocket_fence_ops, &core->fence_lock, + core->fence_context, ++core->emit_seqno); + + return fence; +} + +static int +rocket_copy_tasks(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_rocket_job *job, + struct rocket_job *rjob) +{ + int ret = 0; + + if (job->task_struct_size < sizeof(struct drm_rocket_task)) + return -EINVAL; + + rjob->task_count = job->task_count; + + if (!rjob->task_count) + return 0; + + rjob->tasks = kvmalloc_array(job->task_count, sizeof(*rjob->tasks), GFP_KERNEL); + if (!rjob->tasks) { + drm_dbg(dev, "Failed to allocate task array\n"); + return -ENOMEM; + } + + for (int i = 0; i < rjob->task_count; i++) { + struct drm_rocket_task task = {0}; + + if (copy_from_user(&task, + u64_to_user_ptr(job->tasks) + i * job->task_struct_size, + sizeof(task))) { + drm_dbg(dev, "Failed to copy incoming tasks\n"); + ret = -EFAULT; + goto fail; + } + + if (task.regcmd_count == 0) { + drm_dbg(dev, "regcmd_count field in drm_rocket_task should be > 0.\n"); + ret = -EINVAL; + goto fail; + } + + rjob->tasks[i].regcmd = task.regcmd; + rjob->tasks[i].regcmd_count = task.regcmd_count; + } + + return 0; + +fail: + kvfree(rjob->tasks); + return ret; +} + +static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *job) +{ + struct rocket_task *task; + unsigned int extra_bit; + + /* Don't queue the job if a reset is in progress */ + if (atomic_read(&core->reset.pending)) + return; + + /* GO ! */ + + task = &job->tasks[job->next_task_idx]; + job->next_task_idx++; + + rocket_pc_writel(core, BASE_ADDRESS, 0x1); + + /* From rknpu, in the TRM this bit is marked as reserved */ + extra_bit = 0x10000000 * core->index; + rocket_cna_writel(core, S_POINTER, CNA_S_POINTER_POINTER_PP_EN(1) | + CNA_S_POINTER_EXECUTER_PP_EN(1) | + CNA_S_POINTER_POINTER_PP_MODE(1) | + extra_bit); + + rocket_core_writel(core, S_POINTER, CORE_S_POINTER_POINTER_PP_EN(1) | + CORE_S_POINTER_EXECUTER_PP_EN(1) | + CORE_S_POINTER_POINTER_PP_MODE(1) | + extra_bit); + + rocket_pc_writel(core, BASE_ADDRESS, task->regcmd); + rocket_pc_writel(core, REGISTER_AMOUNTS, + PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT((task->regcmd_count + 1) / 2 - 1)); + + rocket_pc_writel(core, INTERRUPT_MASK, PC_INTERRUPT_MASK_DPU_0 | PC_INTERRUPT_MASK_DPU_1); + rocket_pc_writel(core, INTERRUPT_CLEAR, PC_INTERRUPT_CLEAR_DPU_0 | PC_INTERRUPT_CLEAR_DPU_1); + + rocket_pc_writel(core, TASK_CON, PC_TASK_CON_RESERVED_0(1) | + PC_TASK_CON_TASK_COUNT_CLEAR(1) | + PC_TASK_CON_TASK_NUMBER(1) | + PC_TASK_CON_TASK_PP_EN(1)); + + rocket_pc_writel(core, TASK_DMA_BASE_ADDR, PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR(0x0)); + + rocket_pc_writel(core, OPERATION_ENABLE, PC_OPERATION_ENABLE_OP_EN(1)); + + dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d", task->regcmd, core->index); +} + +static int rocket_acquire_object_fences(struct drm_gem_object **bos, + int bo_count, + struct drm_sched_job *job, + bool is_write) +{ + int i, ret; + + for (i = 0; i < bo_count; i++) { + ret = dma_resv_reserve_fences(bos[i]->resv, 1); + if (ret) + return ret; + + ret = drm_sched_job_add_implicit_dependencies(job, bos[i], + is_write); + if (ret) + return ret; + } + + return 0; +} + +static void rocket_attach_object_fences(struct drm_gem_object **bos, + int bo_count, + struct dma_fence *fence) +{ + int i; + + for (i = 0; i < bo_count; i++) + dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE); +} + +static int rocket_job_push(struct rocket_job *job) +{ + struct rocket_device *rdev = job->rdev; + struct drm_gem_object **bos; + struct ww_acquire_ctx acquire_ctx; + int ret = 0; + + bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *), + GFP_KERNEL); + memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *)); + memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *)); + + ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx); + if (ret) + goto err; + + scoped_guard(mutex, &rdev->sched_lock) { + drm_sched_job_arm(&job->base); + + job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished); + + ret = rocket_acquire_object_fences(job->in_bos, job->in_bo_count, &job->base, false); + if (ret) + goto err_unlock; + + ret = rocket_acquire_object_fences(job->out_bos, job->out_bo_count, &job->base, true); + if (ret) + goto err_unlock; + + kref_get(&job->refcount); /* put by scheduler job completion */ + + drm_sched_entity_push_job(&job->base); + } + + rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence); + +err_unlock: + drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx); +err: + kvfree(bos); + + return ret; +} + +static void rocket_job_cleanup(struct kref *ref) +{ + struct rocket_job *job = container_of(ref, struct rocket_job, + refcount); + unsigned int i; + + rocket_iommu_domain_put(job->domain); + + dma_fence_put(job->done_fence); + dma_fence_put(job->inference_done_fence); + + if (job->in_bos) { + for (i = 0; i < job->in_bo_count; i++) + drm_gem_object_put(job->in_bos[i]); + + kvfree(job->in_bos); + } + + if (job->out_bos) { + for (i = 0; i < job->out_bo_count; i++) + drm_gem_object_put(job->out_bos[i]); + + kvfree(job->out_bos); + } + + kvfree(job->tasks); + + kfree(job); +} + +static void rocket_job_put(struct rocket_job *job) +{ + kref_put(&job->refcount, rocket_job_cleanup); +} + +static void rocket_job_free(struct drm_sched_job *sched_job) +{ + struct rocket_job *job = to_rocket_job(sched_job); + + drm_sched_job_cleanup(sched_job); + + rocket_job_put(job); +} + +static struct rocket_core *sched_to_core(struct rocket_device *rdev, + struct drm_gpu_scheduler *sched) +{ + unsigned int core; + + for (core = 0; core < rdev->num_cores; core++) { + if (&rdev->cores[core].sched == sched) + return &rdev->cores[core]; + } + + return NULL; +} + +static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job) +{ + struct rocket_job *job = to_rocket_job(sched_job); + struct rocket_device *rdev = job->rdev; + struct rocket_core *core = sched_to_core(rdev, sched_job->sched); + struct dma_fence *fence = NULL; + int ret; + + if (unlikely(job->base.s_fence->finished.error)) + return NULL; + + /* + * Nothing to execute: can happen if the job has finished while + * we were resetting the NPU. + */ + if (job->next_task_idx == job->task_count) + return NULL; + + fence = rocket_fence_create(core); + if (IS_ERR(fence)) + return fence; + + if (job->done_fence) + dma_fence_put(job->done_fence); + job->done_fence = dma_fence_get(fence); + + ret = pm_runtime_get_sync(core->dev); + if (ret < 0) + return fence; + + ret = iommu_attach_group(job->domain->domain, core->iommu_group); + if (ret < 0) + return fence; + + scoped_guard(mutex, &core->job_lock) { + core->in_flight_job = job; + rocket_job_hw_submit(core, job); + } + + return fence; +} + +static void rocket_job_handle_irq(struct rocket_core *core) +{ + pm_runtime_mark_last_busy(core->dev); + + rocket_pc_writel(core, OPERATION_ENABLE, 0x0); + rocket_pc_writel(core, INTERRUPT_CLEAR, 0x1ffff); + + scoped_guard(mutex, &core->job_lock) + if (core->in_flight_job) { + if (core->in_flight_job->next_task_idx < core->in_flight_job->task_count) { + rocket_job_hw_submit(core, core->in_flight_job); + return; + } + + iommu_detach_group(NULL, iommu_group_get(core->dev)); + dma_fence_signal(core->in_flight_job->done_fence); + pm_runtime_put_autosuspend(core->dev); + core->in_flight_job = NULL; + } +} + +static void +rocket_reset(struct rocket_core *core, struct drm_sched_job *bad) +{ + if (!atomic_read(&core->reset.pending)) + return; + + drm_sched_stop(&core->sched, bad); + + /* + * Remaining interrupts have been handled, but we might still have + * stuck jobs. Let's make sure the PM counters stay balanced by + * manually calling pm_runtime_put_noidle(). + */ + scoped_guard(mutex, &core->job_lock) { + if (core->in_flight_job) + pm_runtime_put_noidle(core->dev); + + iommu_detach_group(NULL, core->iommu_group); + + core->in_flight_job = NULL; + } + + /* Proceed with reset now. */ + rocket_core_reset(core); + + /* NPU has been reset, we can clear the reset pending bit. */ + atomic_set(&core->reset.pending, 0); + + /* Restart the scheduler */ + drm_sched_start(&core->sched, 0); +} + +static enum drm_gpu_sched_stat rocket_job_timedout(struct drm_sched_job *sched_job) +{ + struct rocket_job *job = to_rocket_job(sched_job); + struct rocket_device *rdev = job->rdev; + struct rocket_core *core = sched_to_core(rdev, sched_job->sched); + + dev_err(core->dev, "NPU job timed out"); + + atomic_set(&core->reset.pending, 1); + rocket_reset(core, sched_job); + + return DRM_GPU_SCHED_STAT_RESET; +} + +static void rocket_reset_work(struct work_struct *work) +{ + struct rocket_core *core; + + core = container_of(work, struct rocket_core, reset.work); + rocket_reset(core, NULL); +} + +static const struct drm_sched_backend_ops rocket_sched_ops = { + .run_job = rocket_job_run, + .timedout_job = rocket_job_timedout, + .free_job = rocket_job_free +}; + +static irqreturn_t rocket_job_irq_handler_thread(int irq, void *data) +{ + struct rocket_core *core = data; + + rocket_job_handle_irq(core); + + return IRQ_HANDLED; +} + +static irqreturn_t rocket_job_irq_handler(int irq, void *data) +{ + struct rocket_core *core = data; + u32 raw_status = rocket_pc_readl(core, INTERRUPT_RAW_STATUS); + + WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR); + WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR); + + if (!(raw_status & PC_INTERRUPT_RAW_STATUS_DPU_0 || + raw_status & PC_INTERRUPT_RAW_STATUS_DPU_1)) + return IRQ_NONE; + + rocket_pc_writel(core, INTERRUPT_MASK, 0x0); + + return IRQ_WAKE_THREAD; +} + +int rocket_job_init(struct rocket_core *core) +{ + struct drm_sched_init_args args = { + .ops = &rocket_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, + .credit_limit = 1, + .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), + .name = dev_name(core->dev), + .dev = core->dev, + }; + int ret; + + INIT_WORK(&core->reset.work, rocket_reset_work); + spin_lock_init(&core->fence_lock); + mutex_init(&core->job_lock); + + core->irq = platform_get_irq(to_platform_device(core->dev), 0); + if (core->irq < 0) + return core->irq; + + ret = devm_request_threaded_irq(core->dev, core->irq, + rocket_job_irq_handler, + rocket_job_irq_handler_thread, + IRQF_SHARED, dev_name(core->dev), + core); + if (ret) { + dev_err(core->dev, "failed to request job irq"); + return ret; + } + + core->reset.wq = alloc_ordered_workqueue("rocket-reset-%d", 0, core->index); + if (!core->reset.wq) + return -ENOMEM; + + core->fence_context = dma_fence_context_alloc(1); + + args.timeout_wq = core->reset.wq; + ret = drm_sched_init(&core->sched, &args); + if (ret) { + dev_err(core->dev, "Failed to create scheduler: %d.", ret); + goto err_sched; + } + + return 0; + +err_sched: + drm_sched_fini(&core->sched); + + destroy_workqueue(core->reset.wq); + return ret; +} + +void rocket_job_fini(struct rocket_core *core) +{ + drm_sched_fini(&core->sched); + + cancel_work_sync(&core->reset.work); + destroy_workqueue(core->reset.wq); +} + +int rocket_job_open(struct rocket_file_priv *rocket_priv) +{ + struct rocket_device *rdev = rocket_priv->rdev; + struct drm_gpu_scheduler **scheds = kmalloc_array(rdev->num_cores, + sizeof(*scheds), + GFP_KERNEL); + unsigned int core; + int ret; + + for (core = 0; core < rdev->num_cores; core++) + scheds[core] = &rdev->cores[core].sched; + + ret = drm_sched_entity_init(&rocket_priv->sched_entity, + DRM_SCHED_PRIORITY_NORMAL, + scheds, + rdev->num_cores, NULL); + if (WARN_ON(ret)) + return ret; + + return 0; +} + +void rocket_job_close(struct rocket_file_priv *rocket_priv) +{ + struct drm_sched_entity *entity = &rocket_priv->sched_entity; + + kfree(entity->sched_list); + drm_sched_entity_destroy(entity); +} + +int rocket_job_is_idle(struct rocket_core *core) +{ + /* If there are any jobs in this HW queue, we're not idle */ + if (atomic_read(&core->sched.credit_count)) + return false; + + return true; +} + +static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file, + struct drm_rocket_job *job) +{ + struct rocket_device *rdev = to_rocket_device(dev); + struct rocket_file_priv *file_priv = file->driver_priv; + struct rocket_job *rjob = NULL; + int ret = 0; + + if (job->task_count == 0) + return -EINVAL; + + rjob = kzalloc(sizeof(*rjob), GFP_KERNEL); + if (!rjob) + return -ENOMEM; + + kref_init(&rjob->refcount); + + rjob->rdev = rdev; + + ret = drm_sched_job_init(&rjob->base, + &file_priv->sched_entity, + 1, NULL, file->client_id); + if (ret) + goto out_put_job; + + ret = rocket_copy_tasks(dev, file, job, rjob); + if (ret) + goto out_cleanup_job; + + ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->in_bo_handles), + job->in_bo_handle_count, &rjob->in_bos); + if (ret) + goto out_cleanup_job; + + rjob->in_bo_count = job->in_bo_handle_count; + + ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->out_bo_handles), + job->out_bo_handle_count, &rjob->out_bos); + if (ret) + goto out_cleanup_job; + + rjob->out_bo_count = job->out_bo_handle_count; + + rjob->domain = rocket_iommu_domain_get(file_priv); + + ret = rocket_job_push(rjob); + if (ret) + goto out_cleanup_job; + +out_cleanup_job: + if (ret) + drm_sched_job_cleanup(&rjob->base); +out_put_job: + rocket_job_put(rjob); + + return ret; +} + +int rocket_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_rocket_submit *args = data; + struct drm_rocket_job *jobs; + int ret = 0; + unsigned int i = 0; + + if (args->job_count == 0) + return 0; + + if (args->job_struct_size < sizeof(struct drm_rocket_job)) { + drm_dbg(dev, "job_struct_size field in drm_rocket_submit struct is too small.\n"); + return -EINVAL; + } + + if (args->reserved != 0) { + drm_dbg(dev, "Reserved field in drm_rocket_submit struct should be 0.\n"); + return -EINVAL; + } + + jobs = kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL); + if (!jobs) { + drm_dbg(dev, "Failed to allocate incoming job array\n"); + return -ENOMEM; + } + + for (i = 0; i < args->job_count; i++) { + if (copy_from_user(&jobs[i], + u64_to_user_ptr(args->jobs) + i * args->job_struct_size, + sizeof(*jobs))) { + ret = -EFAULT; + drm_dbg(dev, "Failed to copy incoming job array\n"); + goto exit; + } + } + + + for (i = 0; i < args->job_count; i++) + rocket_ioctl_submit_job(dev, file, &jobs[i]); + +exit: + kvfree(jobs); + + return ret; +} diff --git a/drivers/accel/rocket/rocket_job.h b/drivers/accel/rocket/rocket_job.h new file mode 100644 index 000000000000..4ae00feec3b9 --- /dev/null +++ b/drivers/accel/rocket/rocket_job.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso */ + +#ifndef __ROCKET_JOB_H__ +#define __ROCKET_JOB_H__ + +#include +#include + +#include "rocket_core.h" +#include "rocket_drv.h" + +struct rocket_task { + u64 regcmd; + u32 regcmd_count; +}; + +struct rocket_job { + struct drm_sched_job base; + + struct rocket_device *rdev; + + struct drm_gem_object **in_bos; + struct drm_gem_object **out_bos; + + u32 in_bo_count; + u32 out_bo_count; + + struct rocket_task *tasks; + u32 task_count; + u32 next_task_idx; + + /* Fence to be signaled by drm-sched once its done with the job */ + struct dma_fence *inference_done_fence; + + /* Fence to be signaled by IRQ handler when the job is complete. */ + struct dma_fence *done_fence; + + struct rocket_iommu_domain *domain; + + struct kref refcount; +}; + +int rocket_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file); + +int rocket_job_init(struct rocket_core *core); +void rocket_job_fini(struct rocket_core *core); +int rocket_job_open(struct rocket_file_priv *rocket_priv); +void rocket_job_close(struct rocket_file_priv *rocket_priv); +int rocket_job_is_idle(struct rocket_core *core); + +#endif diff --git a/drivers/accel/rocket/rocket_registers.h b/drivers/accel/rocket/rocket_registers.h new file mode 100644 index 000000000000..9aef614c3470 --- /dev/null +++ b/drivers/accel/rocket/rocket_registers.h @@ -0,0 +1,4404 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ + +#ifndef __ROCKET_REGISTERS_XML__ +#define __ROCKET_REGISTERS_XML__ + +/* Autogenerated file, DO NOT EDIT manually! + +This file was generated by the rules-ng-ng gen_header.py tool in this git repository: +http://gitlab.freedesktop.org/mesa/mesa/ +git clone https://gitlab.freedesktop.org/mesa/mesa.git + +The rules-ng-ng source files this header was generated from are: + +- /home/tomeu/src/mesa/src/gallium/drivers/rocket/registers.xml ( 60076 bytes, from Wed Jun 12 10:02:25 2024) + +Copyright (C) 2024-2025 by the following authors: +- Tomeu Vizoso +*/ + +#define REG_PC_VERSION 0x00000000 +#define PC_VERSION_VERSION__MASK 0xffffffff +#define PC_VERSION_VERSION__SHIFT 0 +static inline uint32_t PC_VERSION_VERSION(uint32_t val) +{ + return ((val) << PC_VERSION_VERSION__SHIFT) & PC_VERSION_VERSION__MASK; +} + +#define REG_PC_VERSION_NUM 0x00000004 +#define PC_VERSION_NUM_VERSION_NUM__MASK 0xffffffff +#define PC_VERSION_NUM_VERSION_NUM__SHIFT 0 +static inline uint32_t PC_VERSION_NUM_VERSION_NUM(uint32_t val) +{ + return ((val) << PC_VERSION_NUM_VERSION_NUM__SHIFT) & PC_VERSION_NUM_VERSION_NUM__MASK; +} + +#define REG_PC_OPERATION_ENABLE 0x00000008 +#define PC_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define PC_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t PC_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << PC_OPERATION_ENABLE_RESERVED_0__SHIFT) & PC_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define PC_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define PC_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t PC_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << PC_OPERATION_ENABLE_OP_EN__SHIFT) & PC_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_PC_BASE_ADDRESS 0x00000010 +#define PC_BASE_ADDRESS_PC_SOURCE_ADDR__MASK 0xfffffff0 +#define PC_BASE_ADDRESS_PC_SOURCE_ADDR__SHIFT 4 +static inline uint32_t PC_BASE_ADDRESS_PC_SOURCE_ADDR(uint32_t val) +{ + return ((val) << PC_BASE_ADDRESS_PC_SOURCE_ADDR__SHIFT) & PC_BASE_ADDRESS_PC_SOURCE_ADDR__MASK; +} +#define PC_BASE_ADDRESS_RESERVED_0__MASK 0x0000000e +#define PC_BASE_ADDRESS_RESERVED_0__SHIFT 1 +static inline uint32_t PC_BASE_ADDRESS_RESERVED_0(uint32_t val) +{ + return ((val) << PC_BASE_ADDRESS_RESERVED_0__SHIFT) & PC_BASE_ADDRESS_RESERVED_0__MASK; +} +#define PC_BASE_ADDRESS_PC_SEL__MASK 0x00000001 +#define PC_BASE_ADDRESS_PC_SEL__SHIFT 0 +static inline uint32_t PC_BASE_ADDRESS_PC_SEL(uint32_t val) +{ + return ((val) << PC_BASE_ADDRESS_PC_SEL__SHIFT) & PC_BASE_ADDRESS_PC_SEL__MASK; +} + +#define REG_PC_REGISTER_AMOUNTS 0x00000014 +#define PC_REGISTER_AMOUNTS_RESERVED_0__MASK 0xffff0000 +#define PC_REGISTER_AMOUNTS_RESERVED_0__SHIFT 16 +static inline uint32_t PC_REGISTER_AMOUNTS_RESERVED_0(uint32_t val) +{ + return ((val) << PC_REGISTER_AMOUNTS_RESERVED_0__SHIFT) & PC_REGISTER_AMOUNTS_RESERVED_0__MASK; +} +#define PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK 0x0000ffff +#define PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__SHIFT 0 +static inline uint32_t PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT(uint32_t val) +{ + return ((val) << PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__SHIFT) & PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK; +} + +#define REG_PC_INTERRUPT_MASK 0x00000020 +#define PC_INTERRUPT_MASK_RESERVED_0__MASK 0xffffc000 +#define PC_INTERRUPT_MASK_RESERVED_0__SHIFT 14 +static inline uint32_t PC_INTERRUPT_MASK_RESERVED_0(uint32_t val) +{ + return ((val) << PC_INTERRUPT_MASK_RESERVED_0__SHIFT) & PC_INTERRUPT_MASK_RESERVED_0__MASK; +} +#define PC_INTERRUPT_MASK_DMA_WRITE_ERROR 0x00002000 +#define PC_INTERRUPT_MASK_DMA_READ_ERROR 0x00001000 +#define PC_INTERRUPT_MASK_PPU_1 0x00000800 +#define PC_INTERRUPT_MASK_PPU_0 0x00000400 +#define PC_INTERRUPT_MASK_DPU_1 0x00000200 +#define PC_INTERRUPT_MASK_DPU_0 0x00000100 +#define PC_INTERRUPT_MASK_CORE_1 0x00000080 +#define PC_INTERRUPT_MASK_CORE_0 0x00000040 +#define PC_INTERRUPT_MASK_CNA_CSC_1 0x00000020 +#define PC_INTERRUPT_MASK_CNA_CSC_0 0x00000010 +#define PC_INTERRUPT_MASK_CNA_WEIGHT_1 0x00000008 +#define PC_INTERRUPT_MASK_CNA_WEIGHT_0 0x00000004 +#define PC_INTERRUPT_MASK_CNA_FEATURE_1 0x00000002 +#define PC_INTERRUPT_MASK_CNA_FEATURE_0 0x00000001 + +#define REG_PC_INTERRUPT_CLEAR 0x00000024 +#define PC_INTERRUPT_CLEAR_RESERVED_0__MASK 0xffffc000 +#define PC_INTERRUPT_CLEAR_RESERVED_0__SHIFT 14 +static inline uint32_t PC_INTERRUPT_CLEAR_RESERVED_0(uint32_t val) +{ + return ((val) << PC_INTERRUPT_CLEAR_RESERVED_0__SHIFT) & PC_INTERRUPT_CLEAR_RESERVED_0__MASK; +} +#define PC_INTERRUPT_CLEAR_DMA_WRITE_ERROR 0x00002000 +#define PC_INTERRUPT_CLEAR_DMA_READ_ERROR 0x00001000 +#define PC_INTERRUPT_CLEAR_PPU_1 0x00000800 +#define PC_INTERRUPT_CLEAR_PPU_0 0x00000400 +#define PC_INTERRUPT_CLEAR_DPU_1 0x00000200 +#define PC_INTERRUPT_CLEAR_DPU_0 0x00000100 +#define PC_INTERRUPT_CLEAR_CORE_1 0x00000080 +#define PC_INTERRUPT_CLEAR_CORE_0 0x00000040 +#define PC_INTERRUPT_CLEAR_CNA_CSC_1 0x00000020 +#define PC_INTERRUPT_CLEAR_CNA_CSC_0 0x00000010 +#define PC_INTERRUPT_CLEAR_CNA_WEIGHT_1 0x00000008 +#define PC_INTERRUPT_CLEAR_CNA_WEIGHT_0 0x00000004 +#define PC_INTERRUPT_CLEAR_CNA_FEATURE_1 0x00000002 +#define PC_INTERRUPT_CLEAR_CNA_FEATURE_0 0x00000001 + +#define REG_PC_INTERRUPT_STATUS 0x00000028 +#define PC_INTERRUPT_STATUS_RESERVED_0__MASK 0xffffc000 +#define PC_INTERRUPT_STATUS_RESERVED_0__SHIFT 14 +static inline uint32_t PC_INTERRUPT_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << PC_INTERRUPT_STATUS_RESERVED_0__SHIFT) & PC_INTERRUPT_STATUS_RESERVED_0__MASK; +} +#define PC_INTERRUPT_STATUS_DMA_WRITE_ERROR 0x00002000 +#define PC_INTERRUPT_STATUS_DMA_READ_ERROR 0x00001000 +#define PC_INTERRUPT_STATUS_PPU_1 0x00000800 +#define PC_INTERRUPT_STATUS_PPU_0 0x00000400 +#define PC_INTERRUPT_STATUS_DPU_1 0x00000200 +#define PC_INTERRUPT_STATUS_DPU_0 0x00000100 +#define PC_INTERRUPT_STATUS_CORE_1 0x00000080 +#define PC_INTERRUPT_STATUS_CORE_0 0x00000040 +#define PC_INTERRUPT_STATUS_CNA_CSC_1 0x00000020 +#define PC_INTERRUPT_STATUS_CNA_CSC_0 0x00000010 +#define PC_INTERRUPT_STATUS_CNA_WEIGHT_1 0x00000008 +#define PC_INTERRUPT_STATUS_CNA_WEIGHT_0 0x00000004 +#define PC_INTERRUPT_STATUS_CNA_FEATURE_1 0x00000002 +#define PC_INTERRUPT_STATUS_CNA_FEATURE_0 0x00000001 + +#define REG_PC_INTERRUPT_RAW_STATUS 0x0000002c +#define PC_INTERRUPT_RAW_STATUS_RESERVED_0__MASK 0xffffc000 +#define PC_INTERRUPT_RAW_STATUS_RESERVED_0__SHIFT 14 +static inline uint32_t PC_INTERRUPT_RAW_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << PC_INTERRUPT_RAW_STATUS_RESERVED_0__SHIFT) & PC_INTERRUPT_RAW_STATUS_RESERVED_0__MASK; +} +#define PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR 0x00002000 +#define PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR 0x00001000 +#define PC_INTERRUPT_RAW_STATUS_PPU_1 0x00000800 +#define PC_INTERRUPT_RAW_STATUS_PPU_0 0x00000400 +#define PC_INTERRUPT_RAW_STATUS_DPU_1 0x00000200 +#define PC_INTERRUPT_RAW_STATUS_DPU_0 0x00000100 +#define PC_INTERRUPT_RAW_STATUS_CORE_1 0x00000080 +#define PC_INTERRUPT_RAW_STATUS_CORE_0 0x00000040 +#define PC_INTERRUPT_RAW_STATUS_CNA_CSC_1 0x00000020 +#define PC_INTERRUPT_RAW_STATUS_CNA_CSC_0 0x00000010 +#define PC_INTERRUPT_RAW_STATUS_CNA_WEIGHT_1 0x00000008 +#define PC_INTERRUPT_RAW_STATUS_CNA_WEIGHT_0 0x00000004 +#define PC_INTERRUPT_RAW_STATUS_CNA_FEATURE_1 0x00000002 +#define PC_INTERRUPT_RAW_STATUS_CNA_FEATURE_0 0x00000001 + +#define REG_PC_TASK_CON 0x00000030 +#define PC_TASK_CON_RESERVED_0__MASK 0xffffc000 +#define PC_TASK_CON_RESERVED_0__SHIFT 14 +static inline uint32_t PC_TASK_CON_RESERVED_0(uint32_t val) +{ + return ((val) << PC_TASK_CON_RESERVED_0__SHIFT) & PC_TASK_CON_RESERVED_0__MASK; +} +#define PC_TASK_CON_TASK_COUNT_CLEAR__MASK 0x00002000 +#define PC_TASK_CON_TASK_COUNT_CLEAR__SHIFT 13 +static inline uint32_t PC_TASK_CON_TASK_COUNT_CLEAR(uint32_t val) +{ + return ((val) << PC_TASK_CON_TASK_COUNT_CLEAR__SHIFT) & PC_TASK_CON_TASK_COUNT_CLEAR__MASK; +} +#define PC_TASK_CON_TASK_PP_EN__MASK 0x00001000 +#define PC_TASK_CON_TASK_PP_EN__SHIFT 12 +static inline uint32_t PC_TASK_CON_TASK_PP_EN(uint32_t val) +{ + return ((val) << PC_TASK_CON_TASK_PP_EN__SHIFT) & PC_TASK_CON_TASK_PP_EN__MASK; +} +#define PC_TASK_CON_TASK_NUMBER__MASK 0x00000fff +#define PC_TASK_CON_TASK_NUMBER__SHIFT 0 +static inline uint32_t PC_TASK_CON_TASK_NUMBER(uint32_t val) +{ + return ((val) << PC_TASK_CON_TASK_NUMBER__SHIFT) & PC_TASK_CON_TASK_NUMBER__MASK; +} + +#define REG_PC_TASK_DMA_BASE_ADDR 0x00000034 +#define PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__MASK 0xfffffff0 +#define PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__SHIFT 4 +static inline uint32_t PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR(uint32_t val) +{ + return ((val) << PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__SHIFT) & PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__MASK; +} +#define PC_TASK_DMA_BASE_ADDR_RESERVED_0__MASK 0x0000000f +#define PC_TASK_DMA_BASE_ADDR_RESERVED_0__SHIFT 0 +static inline uint32_t PC_TASK_DMA_BASE_ADDR_RESERVED_0(uint32_t val) +{ + return ((val) << PC_TASK_DMA_BASE_ADDR_RESERVED_0__SHIFT) & PC_TASK_DMA_BASE_ADDR_RESERVED_0__MASK; +} + +#define REG_PC_TASK_STATUS 0x0000003c +#define PC_TASK_STATUS_RESERVED_0__MASK 0xf0000000 +#define PC_TASK_STATUS_RESERVED_0__SHIFT 28 +static inline uint32_t PC_TASK_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << PC_TASK_STATUS_RESERVED_0__SHIFT) & PC_TASK_STATUS_RESERVED_0__MASK; +} +#define PC_TASK_STATUS_TASK_STATUS__MASK 0x0fffffff +#define PC_TASK_STATUS_TASK_STATUS__SHIFT 0 +static inline uint32_t PC_TASK_STATUS_TASK_STATUS(uint32_t val) +{ + return ((val) << PC_TASK_STATUS_TASK_STATUS__SHIFT) & PC_TASK_STATUS_TASK_STATUS__MASK; +} + +#define REG_CNA_S_STATUS 0x00001000 +#define CNA_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define CNA_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t CNA_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_S_STATUS_RESERVED_0__SHIFT) & CNA_S_STATUS_RESERVED_0__MASK; +} +#define CNA_S_STATUS_STATUS_1__MASK 0x00030000 +#define CNA_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t CNA_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << CNA_S_STATUS_STATUS_1__SHIFT) & CNA_S_STATUS_STATUS_1__MASK; +} +#define CNA_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define CNA_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t CNA_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_S_STATUS_RESERVED_1__SHIFT) & CNA_S_STATUS_RESERVED_1__MASK; +} +#define CNA_S_STATUS_STATUS_0__MASK 0x00000003 +#define CNA_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t CNA_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << CNA_S_STATUS_STATUS_0__SHIFT) & CNA_S_STATUS_STATUS_0__MASK; +} + +#define REG_CNA_S_POINTER 0x00001004 +#define CNA_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define CNA_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t CNA_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_S_POINTER_RESERVED_0__SHIFT) & CNA_S_POINTER_RESERVED_0__MASK; +} +#define CNA_S_POINTER_EXECUTER__MASK 0x00010000 +#define CNA_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t CNA_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << CNA_S_POINTER_EXECUTER__SHIFT) & CNA_S_POINTER_EXECUTER__MASK; +} +#define CNA_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define CNA_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t CNA_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_S_POINTER_RESERVED_1__SHIFT) & CNA_S_POINTER_RESERVED_1__MASK; +} +#define CNA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define CNA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t CNA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << CNA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & CNA_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define CNA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define CNA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t CNA_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << CNA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & CNA_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define CNA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define CNA_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t CNA_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << CNA_S_POINTER_POINTER_PP_MODE__SHIFT) & CNA_S_POINTER_POINTER_PP_MODE__MASK; +} +#define CNA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define CNA_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t CNA_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << CNA_S_POINTER_EXECUTER_PP_EN__SHIFT) & CNA_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define CNA_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define CNA_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t CNA_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << CNA_S_POINTER_POINTER_PP_EN__SHIFT) & CNA_S_POINTER_POINTER_PP_EN__MASK; +} +#define CNA_S_POINTER_POINTER__MASK 0x00000001 +#define CNA_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t CNA_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << CNA_S_POINTER_POINTER__SHIFT) & CNA_S_POINTER_POINTER__MASK; +} + +#define REG_CNA_OPERATION_ENABLE 0x00001008 +#define CNA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define CNA_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t CNA_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_OPERATION_ENABLE_RESERVED_0__SHIFT) & CNA_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define CNA_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define CNA_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t CNA_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << CNA_OPERATION_ENABLE_OP_EN__SHIFT) & CNA_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_CNA_CONV_CON1 0x0000100c +#define CNA_CONV_CON1_RESERVED_0__MASK 0x80000000 +#define CNA_CONV_CON1_RESERVED_0__SHIFT 31 +static inline uint32_t CNA_CONV_CON1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_RESERVED_0__SHIFT) & CNA_CONV_CON1_RESERVED_0__MASK; +} +#define CNA_CONV_CON1_NONALIGN_DMA__MASK 0x40000000 +#define CNA_CONV_CON1_NONALIGN_DMA__SHIFT 30 +static inline uint32_t CNA_CONV_CON1_NONALIGN_DMA(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_NONALIGN_DMA__SHIFT) & CNA_CONV_CON1_NONALIGN_DMA__MASK; +} +#define CNA_CONV_CON1_GROUP_LINE_OFF__MASK 0x20000000 +#define CNA_CONV_CON1_GROUP_LINE_OFF__SHIFT 29 +static inline uint32_t CNA_CONV_CON1_GROUP_LINE_OFF(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_GROUP_LINE_OFF__SHIFT) & CNA_CONV_CON1_GROUP_LINE_OFF__MASK; +} +#define CNA_CONV_CON1_RESERVED_1__MASK 0x1ffe0000 +#define CNA_CONV_CON1_RESERVED_1__SHIFT 17 +static inline uint32_t CNA_CONV_CON1_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_RESERVED_1__SHIFT) & CNA_CONV_CON1_RESERVED_1__MASK; +} +#define CNA_CONV_CON1_DECONV__MASK 0x00010000 +#define CNA_CONV_CON1_DECONV__SHIFT 16 +static inline uint32_t CNA_CONV_CON1_DECONV(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_DECONV__SHIFT) & CNA_CONV_CON1_DECONV__MASK; +} +#define CNA_CONV_CON1_ARGB_IN__MASK 0x0000f000 +#define CNA_CONV_CON1_ARGB_IN__SHIFT 12 +static inline uint32_t CNA_CONV_CON1_ARGB_IN(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_ARGB_IN__SHIFT) & CNA_CONV_CON1_ARGB_IN__MASK; +} +#define CNA_CONV_CON1_RESERVED_2__MASK 0x00000c00 +#define CNA_CONV_CON1_RESERVED_2__SHIFT 10 +static inline uint32_t CNA_CONV_CON1_RESERVED_2(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_RESERVED_2__SHIFT) & CNA_CONV_CON1_RESERVED_2__MASK; +} +#define CNA_CONV_CON1_PROC_PRECISION__MASK 0x00000380 +#define CNA_CONV_CON1_PROC_PRECISION__SHIFT 7 +static inline uint32_t CNA_CONV_CON1_PROC_PRECISION(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_PROC_PRECISION__SHIFT) & CNA_CONV_CON1_PROC_PRECISION__MASK; +} +#define CNA_CONV_CON1_IN_PRECISION__MASK 0x00000070 +#define CNA_CONV_CON1_IN_PRECISION__SHIFT 4 +static inline uint32_t CNA_CONV_CON1_IN_PRECISION(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_IN_PRECISION__SHIFT) & CNA_CONV_CON1_IN_PRECISION__MASK; +} +#define CNA_CONV_CON1_CONV_MODE__MASK 0x0000000f +#define CNA_CONV_CON1_CONV_MODE__SHIFT 0 +static inline uint32_t CNA_CONV_CON1_CONV_MODE(uint32_t val) +{ + return ((val) << CNA_CONV_CON1_CONV_MODE__SHIFT) & CNA_CONV_CON1_CONV_MODE__MASK; +} + +#define REG_CNA_CONV_CON2 0x00001010 +#define CNA_CONV_CON2_RESERVED_0__MASK 0xff000000 +#define CNA_CONV_CON2_RESERVED_0__SHIFT 24 +static inline uint32_t CNA_CONV_CON2_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_RESERVED_0__SHIFT) & CNA_CONV_CON2_RESERVED_0__MASK; +} +#define CNA_CONV_CON2_KERNEL_GROUP__MASK 0x00ff0000 +#define CNA_CONV_CON2_KERNEL_GROUP__SHIFT 16 +static inline uint32_t CNA_CONV_CON2_KERNEL_GROUP(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_KERNEL_GROUP__SHIFT) & CNA_CONV_CON2_KERNEL_GROUP__MASK; +} +#define CNA_CONV_CON2_RESERVED_1__MASK 0x0000c000 +#define CNA_CONV_CON2_RESERVED_1__SHIFT 14 +static inline uint32_t CNA_CONV_CON2_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_RESERVED_1__SHIFT) & CNA_CONV_CON2_RESERVED_1__MASK; +} +#define CNA_CONV_CON2_FEATURE_GRAINS__MASK 0x00003ff0 +#define CNA_CONV_CON2_FEATURE_GRAINS__SHIFT 4 +static inline uint32_t CNA_CONV_CON2_FEATURE_GRAINS(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_FEATURE_GRAINS__SHIFT) & CNA_CONV_CON2_FEATURE_GRAINS__MASK; +} +#define CNA_CONV_CON2_RESERVED_2__MASK 0x00000008 +#define CNA_CONV_CON2_RESERVED_2__SHIFT 3 +static inline uint32_t CNA_CONV_CON2_RESERVED_2(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_RESERVED_2__SHIFT) & CNA_CONV_CON2_RESERVED_2__MASK; +} +#define CNA_CONV_CON2_CSC_WO_EN__MASK 0x00000004 +#define CNA_CONV_CON2_CSC_WO_EN__SHIFT 2 +static inline uint32_t CNA_CONV_CON2_CSC_WO_EN(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_CSC_WO_EN__SHIFT) & CNA_CONV_CON2_CSC_WO_EN__MASK; +} +#define CNA_CONV_CON2_CSC_DO_EN__MASK 0x00000002 +#define CNA_CONV_CON2_CSC_DO_EN__SHIFT 1 +static inline uint32_t CNA_CONV_CON2_CSC_DO_EN(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_CSC_DO_EN__SHIFT) & CNA_CONV_CON2_CSC_DO_EN__MASK; +} +#define CNA_CONV_CON2_CMD_FIFO_SRST__MASK 0x00000001 +#define CNA_CONV_CON2_CMD_FIFO_SRST__SHIFT 0 +static inline uint32_t CNA_CONV_CON2_CMD_FIFO_SRST(uint32_t val) +{ + return ((val) << CNA_CONV_CON2_CMD_FIFO_SRST__SHIFT) & CNA_CONV_CON2_CMD_FIFO_SRST__MASK; +} + +#define REG_CNA_CONV_CON3 0x00001014 +#define CNA_CONV_CON3_RESERVED_0__MASK 0x80000000 +#define CNA_CONV_CON3_RESERVED_0__SHIFT 31 +static inline uint32_t CNA_CONV_CON3_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_RESERVED_0__SHIFT) & CNA_CONV_CON3_RESERVED_0__MASK; +} +#define CNA_CONV_CON3_NN_MODE__MASK 0x70000000 +#define CNA_CONV_CON3_NN_MODE__SHIFT 28 +static inline uint32_t CNA_CONV_CON3_NN_MODE(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_NN_MODE__SHIFT) & CNA_CONV_CON3_NN_MODE__MASK; +} +#define CNA_CONV_CON3_RESERVED_1__MASK 0x0c000000 +#define CNA_CONV_CON3_RESERVED_1__SHIFT 26 +static inline uint32_t CNA_CONV_CON3_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_RESERVED_1__SHIFT) & CNA_CONV_CON3_RESERVED_1__MASK; +} +#define CNA_CONV_CON3_ATROUS_Y_DILATION__MASK 0x03e00000 +#define CNA_CONV_CON3_ATROUS_Y_DILATION__SHIFT 21 +static inline uint32_t CNA_CONV_CON3_ATROUS_Y_DILATION(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_ATROUS_Y_DILATION__SHIFT) & CNA_CONV_CON3_ATROUS_Y_DILATION__MASK; +} +#define CNA_CONV_CON3_ATROUS_X_DILATION__MASK 0x001f0000 +#define CNA_CONV_CON3_ATROUS_X_DILATION__SHIFT 16 +static inline uint32_t CNA_CONV_CON3_ATROUS_X_DILATION(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_ATROUS_X_DILATION__SHIFT) & CNA_CONV_CON3_ATROUS_X_DILATION__MASK; +} +#define CNA_CONV_CON3_RESERVED_2__MASK 0x0000c000 +#define CNA_CONV_CON3_RESERVED_2__SHIFT 14 +static inline uint32_t CNA_CONV_CON3_RESERVED_2(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_RESERVED_2__SHIFT) & CNA_CONV_CON3_RESERVED_2__MASK; +} +#define CNA_CONV_CON3_DECONV_Y_STRIDE__MASK 0x00003800 +#define CNA_CONV_CON3_DECONV_Y_STRIDE__SHIFT 11 +static inline uint32_t CNA_CONV_CON3_DECONV_Y_STRIDE(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_DECONV_Y_STRIDE__SHIFT) & CNA_CONV_CON3_DECONV_Y_STRIDE__MASK; +} +#define CNA_CONV_CON3_DECONV_X_STRIDE__MASK 0x00000700 +#define CNA_CONV_CON3_DECONV_X_STRIDE__SHIFT 8 +static inline uint32_t CNA_CONV_CON3_DECONV_X_STRIDE(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_DECONV_X_STRIDE__SHIFT) & CNA_CONV_CON3_DECONV_X_STRIDE__MASK; +} +#define CNA_CONV_CON3_RESERVED_3__MASK 0x000000c0 +#define CNA_CONV_CON3_RESERVED_3__SHIFT 6 +static inline uint32_t CNA_CONV_CON3_RESERVED_3(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_RESERVED_3__SHIFT) & CNA_CONV_CON3_RESERVED_3__MASK; +} +#define CNA_CONV_CON3_CONV_Y_STRIDE__MASK 0x00000038 +#define CNA_CONV_CON3_CONV_Y_STRIDE__SHIFT 3 +static inline uint32_t CNA_CONV_CON3_CONV_Y_STRIDE(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_CONV_Y_STRIDE__SHIFT) & CNA_CONV_CON3_CONV_Y_STRIDE__MASK; +} +#define CNA_CONV_CON3_CONV_X_STRIDE__MASK 0x00000007 +#define CNA_CONV_CON3_CONV_X_STRIDE__SHIFT 0 +static inline uint32_t CNA_CONV_CON3_CONV_X_STRIDE(uint32_t val) +{ + return ((val) << CNA_CONV_CON3_CONV_X_STRIDE__SHIFT) & CNA_CONV_CON3_CONV_X_STRIDE__MASK; +} + +#define REG_CNA_DATA_SIZE0 0x00001020 +#define CNA_DATA_SIZE0_RESERVED_0__MASK 0xf8000000 +#define CNA_DATA_SIZE0_RESERVED_0__SHIFT 27 +static inline uint32_t CNA_DATA_SIZE0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE0_RESERVED_0__SHIFT) & CNA_DATA_SIZE0_RESERVED_0__MASK; +} +#define CNA_DATA_SIZE0_DATAIN_WIDTH__MASK 0x07ff0000 +#define CNA_DATA_SIZE0_DATAIN_WIDTH__SHIFT 16 +static inline uint32_t CNA_DATA_SIZE0_DATAIN_WIDTH(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE0_DATAIN_WIDTH__SHIFT) & CNA_DATA_SIZE0_DATAIN_WIDTH__MASK; +} +#define CNA_DATA_SIZE0_RESERVED_1__MASK 0x0000f800 +#define CNA_DATA_SIZE0_RESERVED_1__SHIFT 11 +static inline uint32_t CNA_DATA_SIZE0_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE0_RESERVED_1__SHIFT) & CNA_DATA_SIZE0_RESERVED_1__MASK; +} +#define CNA_DATA_SIZE0_DATAIN_HEIGHT__MASK 0x000007ff +#define CNA_DATA_SIZE0_DATAIN_HEIGHT__SHIFT 0 +static inline uint32_t CNA_DATA_SIZE0_DATAIN_HEIGHT(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE0_DATAIN_HEIGHT__SHIFT) & CNA_DATA_SIZE0_DATAIN_HEIGHT__MASK; +} + +#define REG_CNA_DATA_SIZE1 0x00001024 +#define CNA_DATA_SIZE1_RESERVED_0__MASK 0xc0000000 +#define CNA_DATA_SIZE1_RESERVED_0__SHIFT 30 +static inline uint32_t CNA_DATA_SIZE1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE1_RESERVED_0__SHIFT) & CNA_DATA_SIZE1_RESERVED_0__MASK; +} +#define CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__MASK 0x3fff0000 +#define CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__SHIFT 16 +static inline uint32_t CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__SHIFT) & CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__MASK; +} +#define CNA_DATA_SIZE1_DATAIN_CHANNEL__MASK 0x0000ffff +#define CNA_DATA_SIZE1_DATAIN_CHANNEL__SHIFT 0 +static inline uint32_t CNA_DATA_SIZE1_DATAIN_CHANNEL(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE1_DATAIN_CHANNEL__SHIFT) & CNA_DATA_SIZE1_DATAIN_CHANNEL__MASK; +} + +#define REG_CNA_DATA_SIZE2 0x00001028 +#define CNA_DATA_SIZE2_RESERVED_0__MASK 0xfffff800 +#define CNA_DATA_SIZE2_RESERVED_0__SHIFT 11 +static inline uint32_t CNA_DATA_SIZE2_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE2_RESERVED_0__SHIFT) & CNA_DATA_SIZE2_RESERVED_0__MASK; +} +#define CNA_DATA_SIZE2_DATAOUT_WIDTH__MASK 0x000007ff +#define CNA_DATA_SIZE2_DATAOUT_WIDTH__SHIFT 0 +static inline uint32_t CNA_DATA_SIZE2_DATAOUT_WIDTH(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE2_DATAOUT_WIDTH__SHIFT) & CNA_DATA_SIZE2_DATAOUT_WIDTH__MASK; +} + +#define REG_CNA_DATA_SIZE3 0x0000102c +#define CNA_DATA_SIZE3_RESERVED_0__MASK 0xff000000 +#define CNA_DATA_SIZE3_RESERVED_0__SHIFT 24 +static inline uint32_t CNA_DATA_SIZE3_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE3_RESERVED_0__SHIFT) & CNA_DATA_SIZE3_RESERVED_0__MASK; +} +#define CNA_DATA_SIZE3_SURF_MODE__MASK 0x00c00000 +#define CNA_DATA_SIZE3_SURF_MODE__SHIFT 22 +static inline uint32_t CNA_DATA_SIZE3_SURF_MODE(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE3_SURF_MODE__SHIFT) & CNA_DATA_SIZE3_SURF_MODE__MASK; +} +#define CNA_DATA_SIZE3_DATAOUT_ATOMICS__MASK 0x003fffff +#define CNA_DATA_SIZE3_DATAOUT_ATOMICS__SHIFT 0 +static inline uint32_t CNA_DATA_SIZE3_DATAOUT_ATOMICS(uint32_t val) +{ + return ((val) << CNA_DATA_SIZE3_DATAOUT_ATOMICS__SHIFT) & CNA_DATA_SIZE3_DATAOUT_ATOMICS__MASK; +} + +#define REG_CNA_WEIGHT_SIZE0 0x00001030 +#define CNA_WEIGHT_SIZE0_WEIGHT_BYTES__MASK 0xffffffff +#define CNA_WEIGHT_SIZE0_WEIGHT_BYTES__SHIFT 0 +static inline uint32_t CNA_WEIGHT_SIZE0_WEIGHT_BYTES(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE0_WEIGHT_BYTES__SHIFT) & CNA_WEIGHT_SIZE0_WEIGHT_BYTES__MASK; +} + +#define REG_CNA_WEIGHT_SIZE1 0x00001034 +#define CNA_WEIGHT_SIZE1_RESERVED_0__MASK 0xfff80000 +#define CNA_WEIGHT_SIZE1_RESERVED_0__SHIFT 19 +static inline uint32_t CNA_WEIGHT_SIZE1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE1_RESERVED_0__SHIFT) & CNA_WEIGHT_SIZE1_RESERVED_0__MASK; +} +#define CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__MASK 0x0007ffff +#define CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__SHIFT 0 +static inline uint32_t CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__SHIFT) & CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__MASK; +} + +#define REG_CNA_WEIGHT_SIZE2 0x00001038 +#define CNA_WEIGHT_SIZE2_RESERVED_0__MASK 0xe0000000 +#define CNA_WEIGHT_SIZE2_RESERVED_0__SHIFT 29 +static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_RESERVED_0__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_0__MASK; +} +#define CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__MASK 0x1f000000 +#define CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__SHIFT 24 +static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_WIDTH(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__MASK; +} +#define CNA_WEIGHT_SIZE2_RESERVED_1__MASK 0x00e00000 +#define CNA_WEIGHT_SIZE2_RESERVED_1__SHIFT 21 +static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_RESERVED_1__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_1__MASK; +} +#define CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__MASK 0x001f0000 +#define CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__SHIFT 16 +static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__MASK; +} +#define CNA_WEIGHT_SIZE2_RESERVED_2__MASK 0x0000c000 +#define CNA_WEIGHT_SIZE2_RESERVED_2__SHIFT 14 +static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_2(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_RESERVED_2__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_2__MASK; +} +#define CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__MASK 0x00003fff +#define CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__SHIFT 0 +static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_KERNELS(uint32_t val) +{ + return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__MASK; +} + +#define REG_CNA_CBUF_CON0 0x00001040 +#define CNA_CBUF_CON0_RESERVED_0__MASK 0xffffc000 +#define CNA_CBUF_CON0_RESERVED_0__SHIFT 14 +static inline uint32_t CNA_CBUF_CON0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_RESERVED_0__SHIFT) & CNA_CBUF_CON0_RESERVED_0__MASK; +} +#define CNA_CBUF_CON0_WEIGHT_REUSE__MASK 0x00002000 +#define CNA_CBUF_CON0_WEIGHT_REUSE__SHIFT 13 +static inline uint32_t CNA_CBUF_CON0_WEIGHT_REUSE(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_WEIGHT_REUSE__SHIFT) & CNA_CBUF_CON0_WEIGHT_REUSE__MASK; +} +#define CNA_CBUF_CON0_DATA_REUSE__MASK 0x00001000 +#define CNA_CBUF_CON0_DATA_REUSE__SHIFT 12 +static inline uint32_t CNA_CBUF_CON0_DATA_REUSE(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_DATA_REUSE__SHIFT) & CNA_CBUF_CON0_DATA_REUSE__MASK; +} +#define CNA_CBUF_CON0_RESERVED_1__MASK 0x00000800 +#define CNA_CBUF_CON0_RESERVED_1__SHIFT 11 +static inline uint32_t CNA_CBUF_CON0_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_RESERVED_1__SHIFT) & CNA_CBUF_CON0_RESERVED_1__MASK; +} +#define CNA_CBUF_CON0_FC_DATA_BANK__MASK 0x00000700 +#define CNA_CBUF_CON0_FC_DATA_BANK__SHIFT 8 +static inline uint32_t CNA_CBUF_CON0_FC_DATA_BANK(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_FC_DATA_BANK__SHIFT) & CNA_CBUF_CON0_FC_DATA_BANK__MASK; +} +#define CNA_CBUF_CON0_WEIGHT_BANK__MASK 0x000000f0 +#define CNA_CBUF_CON0_WEIGHT_BANK__SHIFT 4 +static inline uint32_t CNA_CBUF_CON0_WEIGHT_BANK(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_WEIGHT_BANK__SHIFT) & CNA_CBUF_CON0_WEIGHT_BANK__MASK; +} +#define CNA_CBUF_CON0_DATA_BANK__MASK 0x0000000f +#define CNA_CBUF_CON0_DATA_BANK__SHIFT 0 +static inline uint32_t CNA_CBUF_CON0_DATA_BANK(uint32_t val) +{ + return ((val) << CNA_CBUF_CON0_DATA_BANK__SHIFT) & CNA_CBUF_CON0_DATA_BANK__MASK; +} + +#define REG_CNA_CBUF_CON1 0x00001044 +#define CNA_CBUF_CON1_RESERVED_0__MASK 0xffffc000 +#define CNA_CBUF_CON1_RESERVED_0__SHIFT 14 +static inline uint32_t CNA_CBUF_CON1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CBUF_CON1_RESERVED_0__SHIFT) & CNA_CBUF_CON1_RESERVED_0__MASK; +} +#define CNA_CBUF_CON1_DATA_ENTRIES__MASK 0x00003fff +#define CNA_CBUF_CON1_DATA_ENTRIES__SHIFT 0 +static inline uint32_t CNA_CBUF_CON1_DATA_ENTRIES(uint32_t val) +{ + return ((val) << CNA_CBUF_CON1_DATA_ENTRIES__SHIFT) & CNA_CBUF_CON1_DATA_ENTRIES__MASK; +} + +#define REG_CNA_CVT_CON0 0x0000104c +#define CNA_CVT_CON0_RESERVED_0__MASK 0xf0000000 +#define CNA_CVT_CON0_RESERVED_0__SHIFT 28 +static inline uint32_t CNA_CVT_CON0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_RESERVED_0__SHIFT) & CNA_CVT_CON0_RESERVED_0__MASK; +} +#define CNA_CVT_CON0_CVT_TRUNCATE_3__MASK 0x0fc00000 +#define CNA_CVT_CON0_CVT_TRUNCATE_3__SHIFT 22 +static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_3(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_3__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_3__MASK; +} +#define CNA_CVT_CON0_CVT_TRUNCATE_2__MASK 0x003f0000 +#define CNA_CVT_CON0_CVT_TRUNCATE_2__SHIFT 16 +static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_2(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_2__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_2__MASK; +} +#define CNA_CVT_CON0_CVT_TRUNCATE_1__MASK 0x0000fc00 +#define CNA_CVT_CON0_CVT_TRUNCATE_1__SHIFT 10 +static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_1(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_1__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_1__MASK; +} +#define CNA_CVT_CON0_CVT_TRUNCATE_0__MASK 0x000003f0 +#define CNA_CVT_CON0_CVT_TRUNCATE_0__SHIFT 4 +static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_0(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_0__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_0__MASK; +} +#define CNA_CVT_CON0_DATA_SIGN__MASK 0x00000008 +#define CNA_CVT_CON0_DATA_SIGN__SHIFT 3 +static inline uint32_t CNA_CVT_CON0_DATA_SIGN(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_DATA_SIGN__SHIFT) & CNA_CVT_CON0_DATA_SIGN__MASK; +} +#define CNA_CVT_CON0_ROUND_TYPE__MASK 0x00000004 +#define CNA_CVT_CON0_ROUND_TYPE__SHIFT 2 +static inline uint32_t CNA_CVT_CON0_ROUND_TYPE(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_ROUND_TYPE__SHIFT) & CNA_CVT_CON0_ROUND_TYPE__MASK; +} +#define CNA_CVT_CON0_CVT_TYPE__MASK 0x00000002 +#define CNA_CVT_CON0_CVT_TYPE__SHIFT 1 +static inline uint32_t CNA_CVT_CON0_CVT_TYPE(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_TYPE__SHIFT) & CNA_CVT_CON0_CVT_TYPE__MASK; +} +#define CNA_CVT_CON0_CVT_BYPASS__MASK 0x00000001 +#define CNA_CVT_CON0_CVT_BYPASS__SHIFT 0 +static inline uint32_t CNA_CVT_CON0_CVT_BYPASS(uint32_t val) +{ + return ((val) << CNA_CVT_CON0_CVT_BYPASS__SHIFT) & CNA_CVT_CON0_CVT_BYPASS__MASK; +} + +#define REG_CNA_CVT_CON1 0x00001050 +#define CNA_CVT_CON1_CVT_SCALE0__MASK 0xffff0000 +#define CNA_CVT_CON1_CVT_SCALE0__SHIFT 16 +static inline uint32_t CNA_CVT_CON1_CVT_SCALE0(uint32_t val) +{ + return ((val) << CNA_CVT_CON1_CVT_SCALE0__SHIFT) & CNA_CVT_CON1_CVT_SCALE0__MASK; +} +#define CNA_CVT_CON1_CVT_OFFSET0__MASK 0x0000ffff +#define CNA_CVT_CON1_CVT_OFFSET0__SHIFT 0 +static inline uint32_t CNA_CVT_CON1_CVT_OFFSET0(uint32_t val) +{ + return ((val) << CNA_CVT_CON1_CVT_OFFSET0__SHIFT) & CNA_CVT_CON1_CVT_OFFSET0__MASK; +} + +#define REG_CNA_CVT_CON2 0x00001054 +#define CNA_CVT_CON2_CVT_SCALE1__MASK 0xffff0000 +#define CNA_CVT_CON2_CVT_SCALE1__SHIFT 16 +static inline uint32_t CNA_CVT_CON2_CVT_SCALE1(uint32_t val) +{ + return ((val) << CNA_CVT_CON2_CVT_SCALE1__SHIFT) & CNA_CVT_CON2_CVT_SCALE1__MASK; +} +#define CNA_CVT_CON2_CVT_OFFSET1__MASK 0x0000ffff +#define CNA_CVT_CON2_CVT_OFFSET1__SHIFT 0 +static inline uint32_t CNA_CVT_CON2_CVT_OFFSET1(uint32_t val) +{ + return ((val) << CNA_CVT_CON2_CVT_OFFSET1__SHIFT) & CNA_CVT_CON2_CVT_OFFSET1__MASK; +} + +#define REG_CNA_CVT_CON3 0x00001058 +#define CNA_CVT_CON3_CVT_SCALE2__MASK 0xffff0000 +#define CNA_CVT_CON3_CVT_SCALE2__SHIFT 16 +static inline uint32_t CNA_CVT_CON3_CVT_SCALE2(uint32_t val) +{ + return ((val) << CNA_CVT_CON3_CVT_SCALE2__SHIFT) & CNA_CVT_CON3_CVT_SCALE2__MASK; +} +#define CNA_CVT_CON3_CVT_OFFSET2__MASK 0x0000ffff +#define CNA_CVT_CON3_CVT_OFFSET2__SHIFT 0 +static inline uint32_t CNA_CVT_CON3_CVT_OFFSET2(uint32_t val) +{ + return ((val) << CNA_CVT_CON3_CVT_OFFSET2__SHIFT) & CNA_CVT_CON3_CVT_OFFSET2__MASK; +} + +#define REG_CNA_CVT_CON4 0x0000105c +#define CNA_CVT_CON4_CVT_SCALE3__MASK 0xffff0000 +#define CNA_CVT_CON4_CVT_SCALE3__SHIFT 16 +static inline uint32_t CNA_CVT_CON4_CVT_SCALE3(uint32_t val) +{ + return ((val) << CNA_CVT_CON4_CVT_SCALE3__SHIFT) & CNA_CVT_CON4_CVT_SCALE3__MASK; +} +#define CNA_CVT_CON4_CVT_OFFSET3__MASK 0x0000ffff +#define CNA_CVT_CON4_CVT_OFFSET3__SHIFT 0 +static inline uint32_t CNA_CVT_CON4_CVT_OFFSET3(uint32_t val) +{ + return ((val) << CNA_CVT_CON4_CVT_OFFSET3__SHIFT) & CNA_CVT_CON4_CVT_OFFSET3__MASK; +} + +#define REG_CNA_FC_CON0 0x00001060 +#define CNA_FC_CON0_FC_SKIP_DATA__MASK 0xffff0000 +#define CNA_FC_CON0_FC_SKIP_DATA__SHIFT 16 +static inline uint32_t CNA_FC_CON0_FC_SKIP_DATA(uint32_t val) +{ + return ((val) << CNA_FC_CON0_FC_SKIP_DATA__SHIFT) & CNA_FC_CON0_FC_SKIP_DATA__MASK; +} +#define CNA_FC_CON0_RESERVED_0__MASK 0x0000fffe +#define CNA_FC_CON0_RESERVED_0__SHIFT 1 +static inline uint32_t CNA_FC_CON0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_FC_CON0_RESERVED_0__SHIFT) & CNA_FC_CON0_RESERVED_0__MASK; +} +#define CNA_FC_CON0_FC_SKIP_EN__MASK 0x00000001 +#define CNA_FC_CON0_FC_SKIP_EN__SHIFT 0 +static inline uint32_t CNA_FC_CON0_FC_SKIP_EN(uint32_t val) +{ + return ((val) << CNA_FC_CON0_FC_SKIP_EN__SHIFT) & CNA_FC_CON0_FC_SKIP_EN__MASK; +} + +#define REG_CNA_FC_CON1 0x00001064 +#define CNA_FC_CON1_RESERVED_0__MASK 0xfffe0000 +#define CNA_FC_CON1_RESERVED_0__SHIFT 17 +static inline uint32_t CNA_FC_CON1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_FC_CON1_RESERVED_0__SHIFT) & CNA_FC_CON1_RESERVED_0__MASK; +} +#define CNA_FC_CON1_DATA_OFFSET__MASK 0x0001ffff +#define CNA_FC_CON1_DATA_OFFSET__SHIFT 0 +static inline uint32_t CNA_FC_CON1_DATA_OFFSET(uint32_t val) +{ + return ((val) << CNA_FC_CON1_DATA_OFFSET__SHIFT) & CNA_FC_CON1_DATA_OFFSET__MASK; +} + +#define REG_CNA_PAD_CON0 0x00001068 +#define CNA_PAD_CON0_RESERVED_0__MASK 0xffffff00 +#define CNA_PAD_CON0_RESERVED_0__SHIFT 8 +static inline uint32_t CNA_PAD_CON0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_PAD_CON0_RESERVED_0__SHIFT) & CNA_PAD_CON0_RESERVED_0__MASK; +} +#define CNA_PAD_CON0_PAD_LEFT__MASK 0x000000f0 +#define CNA_PAD_CON0_PAD_LEFT__SHIFT 4 +static inline uint32_t CNA_PAD_CON0_PAD_LEFT(uint32_t val) +{ + return ((val) << CNA_PAD_CON0_PAD_LEFT__SHIFT) & CNA_PAD_CON0_PAD_LEFT__MASK; +} +#define CNA_PAD_CON0_PAD_TOP__MASK 0x0000000f +#define CNA_PAD_CON0_PAD_TOP__SHIFT 0 +static inline uint32_t CNA_PAD_CON0_PAD_TOP(uint32_t val) +{ + return ((val) << CNA_PAD_CON0_PAD_TOP__SHIFT) & CNA_PAD_CON0_PAD_TOP__MASK; +} + +#define REG_CNA_FEATURE_DATA_ADDR 0x00001070 +#define CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__MASK 0xffffffff +#define CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__SHIFT 0 +static inline uint32_t CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR(uint32_t val) +{ + return ((val) << CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__SHIFT) & CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__MASK; +} + +#define REG_CNA_FC_CON2 0x00001074 +#define CNA_FC_CON2_RESERVED_0__MASK 0xfffe0000 +#define CNA_FC_CON2_RESERVED_0__SHIFT 17 +static inline uint32_t CNA_FC_CON2_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_FC_CON2_RESERVED_0__SHIFT) & CNA_FC_CON2_RESERVED_0__MASK; +} +#define CNA_FC_CON2_WEIGHT_OFFSET__MASK 0x0001ffff +#define CNA_FC_CON2_WEIGHT_OFFSET__SHIFT 0 +static inline uint32_t CNA_FC_CON2_WEIGHT_OFFSET(uint32_t val) +{ + return ((val) << CNA_FC_CON2_WEIGHT_OFFSET__SHIFT) & CNA_FC_CON2_WEIGHT_OFFSET__MASK; +} + +#define REG_CNA_DMA_CON0 0x00001078 +#define CNA_DMA_CON0_OV4K_BYPASS__MASK 0x80000000 +#define CNA_DMA_CON0_OV4K_BYPASS__SHIFT 31 +static inline uint32_t CNA_DMA_CON0_OV4K_BYPASS(uint32_t val) +{ + return ((val) << CNA_DMA_CON0_OV4K_BYPASS__SHIFT) & CNA_DMA_CON0_OV4K_BYPASS__MASK; +} +#define CNA_DMA_CON0_RESERVED_0__MASK 0x7ff00000 +#define CNA_DMA_CON0_RESERVED_0__SHIFT 20 +static inline uint32_t CNA_DMA_CON0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DMA_CON0_RESERVED_0__SHIFT) & CNA_DMA_CON0_RESERVED_0__MASK; +} +#define CNA_DMA_CON0_WEIGHT_BURST_LEN__MASK 0x000f0000 +#define CNA_DMA_CON0_WEIGHT_BURST_LEN__SHIFT 16 +static inline uint32_t CNA_DMA_CON0_WEIGHT_BURST_LEN(uint32_t val) +{ + return ((val) << CNA_DMA_CON0_WEIGHT_BURST_LEN__SHIFT) & CNA_DMA_CON0_WEIGHT_BURST_LEN__MASK; +} +#define CNA_DMA_CON0_RESERVED_1__MASK 0x0000fff0 +#define CNA_DMA_CON0_RESERVED_1__SHIFT 4 +static inline uint32_t CNA_DMA_CON0_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_DMA_CON0_RESERVED_1__SHIFT) & CNA_DMA_CON0_RESERVED_1__MASK; +} +#define CNA_DMA_CON0_DATA_BURST_LEN__MASK 0x0000000f +#define CNA_DMA_CON0_DATA_BURST_LEN__SHIFT 0 +static inline uint32_t CNA_DMA_CON0_DATA_BURST_LEN(uint32_t val) +{ + return ((val) << CNA_DMA_CON0_DATA_BURST_LEN__SHIFT) & CNA_DMA_CON0_DATA_BURST_LEN__MASK; +} + +#define REG_CNA_DMA_CON1 0x0000107c +#define CNA_DMA_CON1_RESERVED_0__MASK 0xf0000000 +#define CNA_DMA_CON1_RESERVED_0__SHIFT 28 +static inline uint32_t CNA_DMA_CON1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DMA_CON1_RESERVED_0__SHIFT) & CNA_DMA_CON1_RESERVED_0__MASK; +} +#define CNA_DMA_CON1_LINE_STRIDE__MASK 0x0fffffff +#define CNA_DMA_CON1_LINE_STRIDE__SHIFT 0 +static inline uint32_t CNA_DMA_CON1_LINE_STRIDE(uint32_t val) +{ + return ((val) << CNA_DMA_CON1_LINE_STRIDE__SHIFT) & CNA_DMA_CON1_LINE_STRIDE__MASK; +} + +#define REG_CNA_DMA_CON2 0x00001080 +#define CNA_DMA_CON2_RESERVED_0__MASK 0xf0000000 +#define CNA_DMA_CON2_RESERVED_0__SHIFT 28 +static inline uint32_t CNA_DMA_CON2_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DMA_CON2_RESERVED_0__SHIFT) & CNA_DMA_CON2_RESERVED_0__MASK; +} +#define CNA_DMA_CON2_SURF_STRIDE__MASK 0x0fffffff +#define CNA_DMA_CON2_SURF_STRIDE__SHIFT 0 +static inline uint32_t CNA_DMA_CON2_SURF_STRIDE(uint32_t val) +{ + return ((val) << CNA_DMA_CON2_SURF_STRIDE__SHIFT) & CNA_DMA_CON2_SURF_STRIDE__MASK; +} + +#define REG_CNA_FC_DATA_SIZE0 0x00001084 +#define CNA_FC_DATA_SIZE0_RESERVED_0__MASK 0xc0000000 +#define CNA_FC_DATA_SIZE0_RESERVED_0__SHIFT 30 +static inline uint32_t CNA_FC_DATA_SIZE0_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE0_RESERVED_0__SHIFT) & CNA_FC_DATA_SIZE0_RESERVED_0__MASK; +} +#define CNA_FC_DATA_SIZE0_DMA_WIDTH__MASK 0x3fff0000 +#define CNA_FC_DATA_SIZE0_DMA_WIDTH__SHIFT 16 +static inline uint32_t CNA_FC_DATA_SIZE0_DMA_WIDTH(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE0_DMA_WIDTH__SHIFT) & CNA_FC_DATA_SIZE0_DMA_WIDTH__MASK; +} +#define CNA_FC_DATA_SIZE0_RESERVED_1__MASK 0x0000f800 +#define CNA_FC_DATA_SIZE0_RESERVED_1__SHIFT 11 +static inline uint32_t CNA_FC_DATA_SIZE0_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE0_RESERVED_1__SHIFT) & CNA_FC_DATA_SIZE0_RESERVED_1__MASK; +} +#define CNA_FC_DATA_SIZE0_DMA_HEIGHT__MASK 0x000007ff +#define CNA_FC_DATA_SIZE0_DMA_HEIGHT__SHIFT 0 +static inline uint32_t CNA_FC_DATA_SIZE0_DMA_HEIGHT(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE0_DMA_HEIGHT__SHIFT) & CNA_FC_DATA_SIZE0_DMA_HEIGHT__MASK; +} + +#define REG_CNA_FC_DATA_SIZE1 0x00001088 +#define CNA_FC_DATA_SIZE1_RESERVED_0__MASK 0xffff0000 +#define CNA_FC_DATA_SIZE1_RESERVED_0__SHIFT 16 +static inline uint32_t CNA_FC_DATA_SIZE1_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE1_RESERVED_0__SHIFT) & CNA_FC_DATA_SIZE1_RESERVED_0__MASK; +} +#define CNA_FC_DATA_SIZE1_DMA_CHANNEL__MASK 0x0000ffff +#define CNA_FC_DATA_SIZE1_DMA_CHANNEL__SHIFT 0 +static inline uint32_t CNA_FC_DATA_SIZE1_DMA_CHANNEL(uint32_t val) +{ + return ((val) << CNA_FC_DATA_SIZE1_DMA_CHANNEL__SHIFT) & CNA_FC_DATA_SIZE1_DMA_CHANNEL__MASK; +} + +#define REG_CNA_CLK_GATE 0x00001090 +#define CNA_CLK_GATE_RESERVED_0__MASK 0xffffffe0 +#define CNA_CLK_GATE_RESERVED_0__SHIFT 5 +static inline uint32_t CNA_CLK_GATE_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_RESERVED_0__SHIFT) & CNA_CLK_GATE_RESERVED_0__MASK; +} +#define CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__MASK 0x00000010 +#define CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__SHIFT 4 +static inline uint32_t CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__MASK; +} +#define CNA_CLK_GATE_RESERVED_1__MASK 0x00000008 +#define CNA_CLK_GATE_RESERVED_1__SHIFT 3 +static inline uint32_t CNA_CLK_GATE_RESERVED_1(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_RESERVED_1__SHIFT) & CNA_CLK_GATE_RESERVED_1__MASK; +} +#define CNA_CLK_GATE_CSC_DISABLE_CLKGATE__MASK 0x00000004 +#define CNA_CLK_GATE_CSC_DISABLE_CLKGATE__SHIFT 2 +static inline uint32_t CNA_CLK_GATE_CSC_DISABLE_CLKGATE(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_CSC_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CSC_DISABLE_CLKGATE__MASK; +} +#define CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__MASK 0x00000002 +#define CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__SHIFT 1 +static inline uint32_t CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__MASK; +} +#define CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__MASK 0x00000001 +#define CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__SHIFT 0 +static inline uint32_t CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE(uint32_t val) +{ + return ((val) << CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__MASK; +} + +#define REG_CNA_DCOMP_CTRL 0x00001100 +#define CNA_DCOMP_CTRL_RESERVED_0__MASK 0xfffffff0 +#define CNA_DCOMP_CTRL_RESERVED_0__SHIFT 4 +static inline uint32_t CNA_DCOMP_CTRL_RESERVED_0(uint32_t val) +{ + return ((val) << CNA_DCOMP_CTRL_RESERVED_0__SHIFT) & CNA_DCOMP_CTRL_RESERVED_0__MASK; +} +#define CNA_DCOMP_CTRL_WT_DEC_BYPASS__MASK 0x00000008 +#define CNA_DCOMP_CTRL_WT_DEC_BYPASS__SHIFT 3 +static inline uint32_t CNA_DCOMP_CTRL_WT_DEC_BYPASS(uint32_t val) +{ + return ((val) << CNA_DCOMP_CTRL_WT_DEC_BYPASS__SHIFT) & CNA_DCOMP_CTRL_WT_DEC_BYPASS__MASK; +} +#define CNA_DCOMP_CTRL_DECOMP_CONTROL__MASK 0x00000007 +#define CNA_DCOMP_CTRL_DECOMP_CONTROL__SHIFT 0 +static inline uint32_t CNA_DCOMP_CTRL_DECOMP_CONTROL(uint32_t val) +{ + return ((val) << CNA_DCOMP_CTRL_DECOMP_CONTROL__SHIFT) & CNA_DCOMP_CTRL_DECOMP_CONTROL__MASK; +} + +#define REG_CNA_DCOMP_REGNUM 0x00001104 +#define CNA_DCOMP_REGNUM_DCOMP_REGNUM__MASK 0xffffffff +#define CNA_DCOMP_REGNUM_DCOMP_REGNUM__SHIFT 0 +static inline uint32_t CNA_DCOMP_REGNUM_DCOMP_REGNUM(uint32_t val) +{ + return ((val) << CNA_DCOMP_REGNUM_DCOMP_REGNUM__SHIFT) & CNA_DCOMP_REGNUM_DCOMP_REGNUM__MASK; +} + +#define REG_CNA_DCOMP_ADDR0 0x00001110 +#define CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__MASK 0xffffffff +#define CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__SHIFT 0 +static inline uint32_t CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0(uint32_t val) +{ + return ((val) << CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__SHIFT) & CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT0 0x00001140 +#define CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__SHIFT) & CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT1 0x00001144 +#define CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__SHIFT) & CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT2 0x00001148 +#define CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__SHIFT) & CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT3 0x0000114c +#define CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__SHIFT) & CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT4 0x00001150 +#define CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__SHIFT) & CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT5 0x00001154 +#define CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__SHIFT) & CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT6 0x00001158 +#define CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__SHIFT) & CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT7 0x0000115c +#define CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__SHIFT) & CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT8 0x00001160 +#define CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__SHIFT) & CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT9 0x00001164 +#define CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__SHIFT) & CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT10 0x00001168 +#define CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__SHIFT) & CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT11 0x0000116c +#define CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__SHIFT) & CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT12 0x00001170 +#define CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__SHIFT) & CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT13 0x00001174 +#define CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__SHIFT) & CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT14 0x00001178 +#define CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__SHIFT) & CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__MASK; +} + +#define REG_CNA_DCOMP_AMOUNT15 0x0000117c +#define CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__MASK 0xffffffff +#define CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__SHIFT 0 +static inline uint32_t CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15(uint32_t val) +{ + return ((val) << CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__SHIFT) & CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__MASK; +} + +#define REG_CNA_CVT_CON5 0x00001180 +#define CNA_CVT_CON5_PER_CHANNEL_CVT_EN__MASK 0xffffffff +#define CNA_CVT_CON5_PER_CHANNEL_CVT_EN__SHIFT 0 +static inline uint32_t CNA_CVT_CON5_PER_CHANNEL_CVT_EN(uint32_t val) +{ + return ((val) << CNA_CVT_CON5_PER_CHANNEL_CVT_EN__SHIFT) & CNA_CVT_CON5_PER_CHANNEL_CVT_EN__MASK; +} + +#define REG_CNA_PAD_CON1 0x00001184 +#define CNA_PAD_CON1_PAD_VALUE__MASK 0xffffffff +#define CNA_PAD_CON1_PAD_VALUE__SHIFT 0 +static inline uint32_t CNA_PAD_CON1_PAD_VALUE(uint32_t val) +{ + return ((val) << CNA_PAD_CON1_PAD_VALUE__SHIFT) & CNA_PAD_CON1_PAD_VALUE__MASK; +} + +#define REG_CORE_S_STATUS 0x00003000 +#define CORE_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define CORE_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t CORE_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_S_STATUS_RESERVED_0__SHIFT) & CORE_S_STATUS_RESERVED_0__MASK; +} +#define CORE_S_STATUS_STATUS_1__MASK 0x00030000 +#define CORE_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t CORE_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << CORE_S_STATUS_STATUS_1__SHIFT) & CORE_S_STATUS_STATUS_1__MASK; +} +#define CORE_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define CORE_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t CORE_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << CORE_S_STATUS_RESERVED_1__SHIFT) & CORE_S_STATUS_RESERVED_1__MASK; +} +#define CORE_S_STATUS_STATUS_0__MASK 0x00000003 +#define CORE_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t CORE_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << CORE_S_STATUS_STATUS_0__SHIFT) & CORE_S_STATUS_STATUS_0__MASK; +} + +#define REG_CORE_S_POINTER 0x00003004 +#define CORE_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define CORE_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t CORE_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_S_POINTER_RESERVED_0__SHIFT) & CORE_S_POINTER_RESERVED_0__MASK; +} +#define CORE_S_POINTER_EXECUTER__MASK 0x00010000 +#define CORE_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t CORE_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << CORE_S_POINTER_EXECUTER__SHIFT) & CORE_S_POINTER_EXECUTER__MASK; +} +#define CORE_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define CORE_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t CORE_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << CORE_S_POINTER_RESERVED_1__SHIFT) & CORE_S_POINTER_RESERVED_1__MASK; +} +#define CORE_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define CORE_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t CORE_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << CORE_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & CORE_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define CORE_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define CORE_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t CORE_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << CORE_S_POINTER_POINTER_PP_CLEAR__SHIFT) & CORE_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define CORE_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define CORE_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t CORE_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << CORE_S_POINTER_POINTER_PP_MODE__SHIFT) & CORE_S_POINTER_POINTER_PP_MODE__MASK; +} +#define CORE_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define CORE_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t CORE_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << CORE_S_POINTER_EXECUTER_PP_EN__SHIFT) & CORE_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define CORE_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define CORE_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t CORE_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << CORE_S_POINTER_POINTER_PP_EN__SHIFT) & CORE_S_POINTER_POINTER_PP_EN__MASK; +} +#define CORE_S_POINTER_POINTER__MASK 0x00000001 +#define CORE_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t CORE_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << CORE_S_POINTER_POINTER__SHIFT) & CORE_S_POINTER_POINTER__MASK; +} + +#define REG_CORE_OPERATION_ENABLE 0x00003008 +#define CORE_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define CORE_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t CORE_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_OPERATION_ENABLE_RESERVED_0__SHIFT) & CORE_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define CORE_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define CORE_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t CORE_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << CORE_OPERATION_ENABLE_OP_EN__SHIFT) & CORE_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_CORE_MAC_GATING 0x0000300c +#define CORE_MAC_GATING_RESERVED_0__MASK 0xf8000000 +#define CORE_MAC_GATING_RESERVED_0__SHIFT 27 +static inline uint32_t CORE_MAC_GATING_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_MAC_GATING_RESERVED_0__SHIFT) & CORE_MAC_GATING_RESERVED_0__MASK; +} +#define CORE_MAC_GATING_SLCG_OP_EN__MASK 0x07ffffff +#define CORE_MAC_GATING_SLCG_OP_EN__SHIFT 0 +static inline uint32_t CORE_MAC_GATING_SLCG_OP_EN(uint32_t val) +{ + return ((val) << CORE_MAC_GATING_SLCG_OP_EN__SHIFT) & CORE_MAC_GATING_SLCG_OP_EN__MASK; +} + +#define REG_CORE_MISC_CFG 0x00003010 +#define CORE_MISC_CFG_RESERVED_0__MASK 0xfff00000 +#define CORE_MISC_CFG_RESERVED_0__SHIFT 20 +static inline uint32_t CORE_MISC_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_RESERVED_0__SHIFT) & CORE_MISC_CFG_RESERVED_0__MASK; +} +#define CORE_MISC_CFG_SOFT_GATING__MASK 0x000fc000 +#define CORE_MISC_CFG_SOFT_GATING__SHIFT 14 +static inline uint32_t CORE_MISC_CFG_SOFT_GATING(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_SOFT_GATING__SHIFT) & CORE_MISC_CFG_SOFT_GATING__MASK; +} +#define CORE_MISC_CFG_RESERVED_1__MASK 0x00003800 +#define CORE_MISC_CFG_RESERVED_1__SHIFT 11 +static inline uint32_t CORE_MISC_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_RESERVED_1__SHIFT) & CORE_MISC_CFG_RESERVED_1__MASK; +} +#define CORE_MISC_CFG_PROC_PRECISION__MASK 0x00000700 +#define CORE_MISC_CFG_PROC_PRECISION__SHIFT 8 +static inline uint32_t CORE_MISC_CFG_PROC_PRECISION(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_PROC_PRECISION__SHIFT) & CORE_MISC_CFG_PROC_PRECISION__MASK; +} +#define CORE_MISC_CFG_RESERVED_2__MASK 0x000000fc +#define CORE_MISC_CFG_RESERVED_2__SHIFT 2 +static inline uint32_t CORE_MISC_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_RESERVED_2__SHIFT) & CORE_MISC_CFG_RESERVED_2__MASK; +} +#define CORE_MISC_CFG_DW_EN__MASK 0x00000002 +#define CORE_MISC_CFG_DW_EN__SHIFT 1 +static inline uint32_t CORE_MISC_CFG_DW_EN(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_DW_EN__SHIFT) & CORE_MISC_CFG_DW_EN__MASK; +} +#define CORE_MISC_CFG_QD_EN__MASK 0x00000001 +#define CORE_MISC_CFG_QD_EN__SHIFT 0 +static inline uint32_t CORE_MISC_CFG_QD_EN(uint32_t val) +{ + return ((val) << CORE_MISC_CFG_QD_EN__SHIFT) & CORE_MISC_CFG_QD_EN__MASK; +} + +#define REG_CORE_DATAOUT_SIZE_0 0x00003014 +#define CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__MASK 0xffff0000 +#define CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__SHIFT 16 +static inline uint32_t CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT(uint32_t val) +{ + return ((val) << CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__SHIFT) & CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__MASK; +} +#define CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__MASK 0x0000ffff +#define CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__SHIFT 0 +static inline uint32_t CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH(uint32_t val) +{ + return ((val) << CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__SHIFT) & CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__MASK; +} + +#define REG_CORE_DATAOUT_SIZE_1 0x00003018 +#define CORE_DATAOUT_SIZE_1_RESERVED_0__MASK 0xffff0000 +#define CORE_DATAOUT_SIZE_1_RESERVED_0__SHIFT 16 +static inline uint32_t CORE_DATAOUT_SIZE_1_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_DATAOUT_SIZE_1_RESERVED_0__SHIFT) & CORE_DATAOUT_SIZE_1_RESERVED_0__MASK; +} +#define CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__MASK 0x0000ffff +#define CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__SHIFT 0 +static inline uint32_t CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL(uint32_t val) +{ + return ((val) << CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__SHIFT) & CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__MASK; +} + +#define REG_CORE_CLIP_TRUNCATE 0x0000301c +#define CORE_CLIP_TRUNCATE_RESERVED_0__MASK 0xffffff80 +#define CORE_CLIP_TRUNCATE_RESERVED_0__SHIFT 7 +static inline uint32_t CORE_CLIP_TRUNCATE_RESERVED_0(uint32_t val) +{ + return ((val) << CORE_CLIP_TRUNCATE_RESERVED_0__SHIFT) & CORE_CLIP_TRUNCATE_RESERVED_0__MASK; +} +#define CORE_CLIP_TRUNCATE_ROUND_TYPE__MASK 0x00000040 +#define CORE_CLIP_TRUNCATE_ROUND_TYPE__SHIFT 6 +static inline uint32_t CORE_CLIP_TRUNCATE_ROUND_TYPE(uint32_t val) +{ + return ((val) << CORE_CLIP_TRUNCATE_ROUND_TYPE__SHIFT) & CORE_CLIP_TRUNCATE_ROUND_TYPE__MASK; +} +#define CORE_CLIP_TRUNCATE_RESERVED_1__MASK 0x00000020 +#define CORE_CLIP_TRUNCATE_RESERVED_1__SHIFT 5 +static inline uint32_t CORE_CLIP_TRUNCATE_RESERVED_1(uint32_t val) +{ + return ((val) << CORE_CLIP_TRUNCATE_RESERVED_1__SHIFT) & CORE_CLIP_TRUNCATE_RESERVED_1__MASK; +} +#define CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__MASK 0x0000001f +#define CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__SHIFT 0 +static inline uint32_t CORE_CLIP_TRUNCATE_CLIP_TRUNCATE(uint32_t val) +{ + return ((val) << CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__SHIFT) & CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__MASK; +} + +#define REG_DPU_S_STATUS 0x00004000 +#define DPU_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define DPU_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t DPU_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_S_STATUS_RESERVED_0__SHIFT) & DPU_S_STATUS_RESERVED_0__MASK; +} +#define DPU_S_STATUS_STATUS_1__MASK 0x00030000 +#define DPU_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t DPU_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << DPU_S_STATUS_STATUS_1__SHIFT) & DPU_S_STATUS_STATUS_1__MASK; +} +#define DPU_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define DPU_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t DPU_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_S_STATUS_RESERVED_1__SHIFT) & DPU_S_STATUS_RESERVED_1__MASK; +} +#define DPU_S_STATUS_STATUS_0__MASK 0x00000003 +#define DPU_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t DPU_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << DPU_S_STATUS_STATUS_0__SHIFT) & DPU_S_STATUS_STATUS_0__MASK; +} + +#define REG_DPU_S_POINTER 0x00004004 +#define DPU_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define DPU_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t DPU_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_S_POINTER_RESERVED_0__SHIFT) & DPU_S_POINTER_RESERVED_0__MASK; +} +#define DPU_S_POINTER_EXECUTER__MASK 0x00010000 +#define DPU_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t DPU_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << DPU_S_POINTER_EXECUTER__SHIFT) & DPU_S_POINTER_EXECUTER__MASK; +} +#define DPU_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define DPU_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t DPU_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_S_POINTER_RESERVED_1__SHIFT) & DPU_S_POINTER_RESERVED_1__MASK; +} +#define DPU_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define DPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t DPU_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << DPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & DPU_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define DPU_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define DPU_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t DPU_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << DPU_S_POINTER_POINTER_PP_CLEAR__SHIFT) & DPU_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define DPU_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define DPU_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t DPU_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << DPU_S_POINTER_POINTER_PP_MODE__SHIFT) & DPU_S_POINTER_POINTER_PP_MODE__MASK; +} +#define DPU_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define DPU_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t DPU_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << DPU_S_POINTER_EXECUTER_PP_EN__SHIFT) & DPU_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define DPU_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define DPU_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t DPU_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << DPU_S_POINTER_POINTER_PP_EN__SHIFT) & DPU_S_POINTER_POINTER_PP_EN__MASK; +} +#define DPU_S_POINTER_POINTER__MASK 0x00000001 +#define DPU_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t DPU_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << DPU_S_POINTER_POINTER__SHIFT) & DPU_S_POINTER_POINTER__MASK; +} + +#define REG_DPU_OPERATION_ENABLE 0x00004008 +#define DPU_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define DPU_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t DPU_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_OPERATION_ENABLE_RESERVED_0__SHIFT) & DPU_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define DPU_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define DPU_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t DPU_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << DPU_OPERATION_ENABLE_OP_EN__SHIFT) & DPU_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_DPU_FEATURE_MODE_CFG 0x0000400c +#define DPU_FEATURE_MODE_CFG_COMB_USE__MASK 0x80000000 +#define DPU_FEATURE_MODE_CFG_COMB_USE__SHIFT 31 +static inline uint32_t DPU_FEATURE_MODE_CFG_COMB_USE(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_COMB_USE__SHIFT) & DPU_FEATURE_MODE_CFG_COMB_USE__MASK; +} +#define DPU_FEATURE_MODE_CFG_TP_EN__MASK 0x40000000 +#define DPU_FEATURE_MODE_CFG_TP_EN__SHIFT 30 +static inline uint32_t DPU_FEATURE_MODE_CFG_TP_EN(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_TP_EN__SHIFT) & DPU_FEATURE_MODE_CFG_TP_EN__MASK; +} +#define DPU_FEATURE_MODE_CFG_RGP_TYPE__MASK 0x3c000000 +#define DPU_FEATURE_MODE_CFG_RGP_TYPE__SHIFT 26 +static inline uint32_t DPU_FEATURE_MODE_CFG_RGP_TYPE(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_RGP_TYPE__SHIFT) & DPU_FEATURE_MODE_CFG_RGP_TYPE__MASK; +} +#define DPU_FEATURE_MODE_CFG_NONALIGN__MASK 0x02000000 +#define DPU_FEATURE_MODE_CFG_NONALIGN__SHIFT 25 +static inline uint32_t DPU_FEATURE_MODE_CFG_NONALIGN(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_NONALIGN__SHIFT) & DPU_FEATURE_MODE_CFG_NONALIGN__MASK; +} +#define DPU_FEATURE_MODE_CFG_SURF_LEN__MASK 0x01fffe00 +#define DPU_FEATURE_MODE_CFG_SURF_LEN__SHIFT 9 +static inline uint32_t DPU_FEATURE_MODE_CFG_SURF_LEN(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_SURF_LEN__SHIFT) & DPU_FEATURE_MODE_CFG_SURF_LEN__MASK; +} +#define DPU_FEATURE_MODE_CFG_BURST_LEN__MASK 0x000001e0 +#define DPU_FEATURE_MODE_CFG_BURST_LEN__SHIFT 5 +static inline uint32_t DPU_FEATURE_MODE_CFG_BURST_LEN(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_BURST_LEN__SHIFT) & DPU_FEATURE_MODE_CFG_BURST_LEN__MASK; +} +#define DPU_FEATURE_MODE_CFG_CONV_MODE__MASK 0x00000018 +#define DPU_FEATURE_MODE_CFG_CONV_MODE__SHIFT 3 +static inline uint32_t DPU_FEATURE_MODE_CFG_CONV_MODE(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_CONV_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_CONV_MODE__MASK; +} +#define DPU_FEATURE_MODE_CFG_OUTPUT_MODE__MASK 0x00000006 +#define DPU_FEATURE_MODE_CFG_OUTPUT_MODE__SHIFT 1 +static inline uint32_t DPU_FEATURE_MODE_CFG_OUTPUT_MODE(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_OUTPUT_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_OUTPUT_MODE__MASK; +} +#define DPU_FEATURE_MODE_CFG_FLYING_MODE__MASK 0x00000001 +#define DPU_FEATURE_MODE_CFG_FLYING_MODE__SHIFT 0 +static inline uint32_t DPU_FEATURE_MODE_CFG_FLYING_MODE(uint32_t val) +{ + return ((val) << DPU_FEATURE_MODE_CFG_FLYING_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_FLYING_MODE__MASK; +} + +#define REG_DPU_DATA_FORMAT 0x00004010 +#define DPU_DATA_FORMAT_OUT_PRECISION__MASK 0xe0000000 +#define DPU_DATA_FORMAT_OUT_PRECISION__SHIFT 29 +static inline uint32_t DPU_DATA_FORMAT_OUT_PRECISION(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_OUT_PRECISION__SHIFT) & DPU_DATA_FORMAT_OUT_PRECISION__MASK; +} +#define DPU_DATA_FORMAT_IN_PRECISION__MASK 0x1c000000 +#define DPU_DATA_FORMAT_IN_PRECISION__SHIFT 26 +static inline uint32_t DPU_DATA_FORMAT_IN_PRECISION(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_IN_PRECISION__SHIFT) & DPU_DATA_FORMAT_IN_PRECISION__MASK; +} +#define DPU_DATA_FORMAT_EW_TRUNCATE_NEG__MASK 0x03ff0000 +#define DPU_DATA_FORMAT_EW_TRUNCATE_NEG__SHIFT 16 +static inline uint32_t DPU_DATA_FORMAT_EW_TRUNCATE_NEG(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_EW_TRUNCATE_NEG__SHIFT) & DPU_DATA_FORMAT_EW_TRUNCATE_NEG__MASK; +} +#define DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__MASK 0x0000fc00 +#define DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__SHIFT 10 +static inline uint32_t DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__SHIFT) & DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__MASK; +} +#define DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__MASK 0x000003f0 +#define DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__SHIFT 4 +static inline uint32_t DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__SHIFT) & DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__MASK; +} +#define DPU_DATA_FORMAT_MC_SURF_OUT__MASK 0x00000008 +#define DPU_DATA_FORMAT_MC_SURF_OUT__SHIFT 3 +static inline uint32_t DPU_DATA_FORMAT_MC_SURF_OUT(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_MC_SURF_OUT__SHIFT) & DPU_DATA_FORMAT_MC_SURF_OUT__MASK; +} +#define DPU_DATA_FORMAT_PROC_PRECISION__MASK 0x00000007 +#define DPU_DATA_FORMAT_PROC_PRECISION__SHIFT 0 +static inline uint32_t DPU_DATA_FORMAT_PROC_PRECISION(uint32_t val) +{ + return ((val) << DPU_DATA_FORMAT_PROC_PRECISION__SHIFT) & DPU_DATA_FORMAT_PROC_PRECISION__MASK; +} + +#define REG_DPU_OFFSET_PEND 0x00004014 +#define DPU_OFFSET_PEND_RESERVED_0__MASK 0xffff0000 +#define DPU_OFFSET_PEND_RESERVED_0__SHIFT 16 +static inline uint32_t DPU_OFFSET_PEND_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_OFFSET_PEND_RESERVED_0__SHIFT) & DPU_OFFSET_PEND_RESERVED_0__MASK; +} +#define DPU_OFFSET_PEND_OFFSET_PEND__MASK 0x0000ffff +#define DPU_OFFSET_PEND_OFFSET_PEND__SHIFT 0 +static inline uint32_t DPU_OFFSET_PEND_OFFSET_PEND(uint32_t val) +{ + return ((val) << DPU_OFFSET_PEND_OFFSET_PEND__SHIFT) & DPU_OFFSET_PEND_OFFSET_PEND__MASK; +} + +#define REG_DPU_DST_BASE_ADDR 0x00004020 +#define DPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK 0xffffffff +#define DPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT 0 +static inline uint32_t DPU_DST_BASE_ADDR_DST_BASE_ADDR(uint32_t val) +{ + return ((val) << DPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT) & DPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK; +} + +#define REG_DPU_DST_SURF_STRIDE 0x00004024 +#define DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK 0xfffffff0 +#define DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT 4 +static inline uint32_t DPU_DST_SURF_STRIDE_DST_SURF_STRIDE(uint32_t val) +{ + return ((val) << DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT) & DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK; +} +#define DPU_DST_SURF_STRIDE_RESERVED_0__MASK 0x0000000f +#define DPU_DST_SURF_STRIDE_RESERVED_0__SHIFT 0 +static inline uint32_t DPU_DST_SURF_STRIDE_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_DST_SURF_STRIDE_RESERVED_0__SHIFT) & DPU_DST_SURF_STRIDE_RESERVED_0__MASK; +} + +#define REG_DPU_DATA_CUBE_WIDTH 0x00004030 +#define DPU_DATA_CUBE_WIDTH_RESERVED_0__MASK 0xffffe000 +#define DPU_DATA_CUBE_WIDTH_RESERVED_0__SHIFT 13 +static inline uint32_t DPU_DATA_CUBE_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_WIDTH_RESERVED_0__SHIFT) & DPU_DATA_CUBE_WIDTH_RESERVED_0__MASK; +} +#define DPU_DATA_CUBE_WIDTH_WIDTH__MASK 0x00001fff +#define DPU_DATA_CUBE_WIDTH_WIDTH__SHIFT 0 +static inline uint32_t DPU_DATA_CUBE_WIDTH_WIDTH(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_WIDTH_WIDTH__SHIFT) & DPU_DATA_CUBE_WIDTH_WIDTH__MASK; +} + +#define REG_DPU_DATA_CUBE_HEIGHT 0x00004034 +#define DPU_DATA_CUBE_HEIGHT_RESERVED_0__MASK 0xfe000000 +#define DPU_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT 25 +static inline uint32_t DPU_DATA_CUBE_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT) & DPU_DATA_CUBE_HEIGHT_RESERVED_0__MASK; +} +#define DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__MASK 0x01c00000 +#define DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__SHIFT 22 +static inline uint32_t DPU_DATA_CUBE_HEIGHT_MINMAX_CTL(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__SHIFT) & DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__MASK; +} +#define DPU_DATA_CUBE_HEIGHT_RESERVED_1__MASK 0x003fe000 +#define DPU_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_DATA_CUBE_HEIGHT_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT) & DPU_DATA_CUBE_HEIGHT_RESERVED_1__MASK; +} +#define DPU_DATA_CUBE_HEIGHT_HEIGHT__MASK 0x00001fff +#define DPU_DATA_CUBE_HEIGHT_HEIGHT__SHIFT 0 +static inline uint32_t DPU_DATA_CUBE_HEIGHT_HEIGHT(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_HEIGHT_HEIGHT__SHIFT) & DPU_DATA_CUBE_HEIGHT_HEIGHT__MASK; +} + +#define REG_DPU_DATA_CUBE_NOTCH_ADDR 0x00004038 +#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__MASK 0xe0000000 +#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__SHIFT 29 +static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__MASK; +} +#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__MASK 0x1fff0000 +#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__SHIFT 16 +static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__MASK; +} +#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__MASK 0x0000e000 +#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__MASK; +} +#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__MASK 0x00001fff +#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__SHIFT 0 +static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__MASK; +} + +#define REG_DPU_DATA_CUBE_CHANNEL 0x0000403c +#define DPU_DATA_CUBE_CHANNEL_RESERVED_0__MASK 0xe0000000 +#define DPU_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT 29 +static inline uint32_t DPU_DATA_CUBE_CHANNEL_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT) & DPU_DATA_CUBE_CHANNEL_RESERVED_0__MASK; +} +#define DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__MASK 0x1fff0000 +#define DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__SHIFT 16 +static inline uint32_t DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__SHIFT) & DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__MASK; +} +#define DPU_DATA_CUBE_CHANNEL_RESERVED_1__MASK 0x0000e000 +#define DPU_DATA_CUBE_CHANNEL_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_DATA_CUBE_CHANNEL_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_CHANNEL_RESERVED_1__SHIFT) & DPU_DATA_CUBE_CHANNEL_RESERVED_1__MASK; +} +#define DPU_DATA_CUBE_CHANNEL_CHANNEL__MASK 0x00001fff +#define DPU_DATA_CUBE_CHANNEL_CHANNEL__SHIFT 0 +static inline uint32_t DPU_DATA_CUBE_CHANNEL_CHANNEL(uint32_t val) +{ + return ((val) << DPU_DATA_CUBE_CHANNEL_CHANNEL__SHIFT) & DPU_DATA_CUBE_CHANNEL_CHANNEL__MASK; +} + +#define REG_DPU_BS_CFG 0x00004040 +#define DPU_BS_CFG_RESERVED_0__MASK 0xfff00000 +#define DPU_BS_CFG_RESERVED_0__SHIFT 20 +static inline uint32_t DPU_BS_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BS_CFG_RESERVED_0__SHIFT) & DPU_BS_CFG_RESERVED_0__MASK; +} +#define DPU_BS_CFG_BS_ALU_ALGO__MASK 0x000f0000 +#define DPU_BS_CFG_BS_ALU_ALGO__SHIFT 16 +static inline uint32_t DPU_BS_CFG_BS_ALU_ALGO(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_ALU_ALGO__SHIFT) & DPU_BS_CFG_BS_ALU_ALGO__MASK; +} +#define DPU_BS_CFG_RESERVED_1__MASK 0x0000fe00 +#define DPU_BS_CFG_RESERVED_1__SHIFT 9 +static inline uint32_t DPU_BS_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_BS_CFG_RESERVED_1__SHIFT) & DPU_BS_CFG_RESERVED_1__MASK; +} +#define DPU_BS_CFG_BS_ALU_SRC__MASK 0x00000100 +#define DPU_BS_CFG_BS_ALU_SRC__SHIFT 8 +static inline uint32_t DPU_BS_CFG_BS_ALU_SRC(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_ALU_SRC__SHIFT) & DPU_BS_CFG_BS_ALU_SRC__MASK; +} +#define DPU_BS_CFG_BS_RELUX_EN__MASK 0x00000080 +#define DPU_BS_CFG_BS_RELUX_EN__SHIFT 7 +static inline uint32_t DPU_BS_CFG_BS_RELUX_EN(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_RELUX_EN__SHIFT) & DPU_BS_CFG_BS_RELUX_EN__MASK; +} +#define DPU_BS_CFG_BS_RELU_BYPASS__MASK 0x00000040 +#define DPU_BS_CFG_BS_RELU_BYPASS__SHIFT 6 +static inline uint32_t DPU_BS_CFG_BS_RELU_BYPASS(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_RELU_BYPASS__SHIFT) & DPU_BS_CFG_BS_RELU_BYPASS__MASK; +} +#define DPU_BS_CFG_BS_MUL_PRELU__MASK 0x00000020 +#define DPU_BS_CFG_BS_MUL_PRELU__SHIFT 5 +static inline uint32_t DPU_BS_CFG_BS_MUL_PRELU(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_MUL_PRELU__SHIFT) & DPU_BS_CFG_BS_MUL_PRELU__MASK; +} +#define DPU_BS_CFG_BS_MUL_BYPASS__MASK 0x00000010 +#define DPU_BS_CFG_BS_MUL_BYPASS__SHIFT 4 +static inline uint32_t DPU_BS_CFG_BS_MUL_BYPASS(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_MUL_BYPASS__SHIFT) & DPU_BS_CFG_BS_MUL_BYPASS__MASK; +} +#define DPU_BS_CFG_RESERVED_2__MASK 0x0000000c +#define DPU_BS_CFG_RESERVED_2__SHIFT 2 +static inline uint32_t DPU_BS_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << DPU_BS_CFG_RESERVED_2__SHIFT) & DPU_BS_CFG_RESERVED_2__MASK; +} +#define DPU_BS_CFG_BS_ALU_BYPASS__MASK 0x00000002 +#define DPU_BS_CFG_BS_ALU_BYPASS__SHIFT 1 +static inline uint32_t DPU_BS_CFG_BS_ALU_BYPASS(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_ALU_BYPASS__SHIFT) & DPU_BS_CFG_BS_ALU_BYPASS__MASK; +} +#define DPU_BS_CFG_BS_BYPASS__MASK 0x00000001 +#define DPU_BS_CFG_BS_BYPASS__SHIFT 0 +static inline uint32_t DPU_BS_CFG_BS_BYPASS(uint32_t val) +{ + return ((val) << DPU_BS_CFG_BS_BYPASS__SHIFT) & DPU_BS_CFG_BS_BYPASS__MASK; +} + +#define REG_DPU_BS_ALU_CFG 0x00004044 +#define DPU_BS_ALU_CFG_BS_ALU_OPERAND__MASK 0xffffffff +#define DPU_BS_ALU_CFG_BS_ALU_OPERAND__SHIFT 0 +static inline uint32_t DPU_BS_ALU_CFG_BS_ALU_OPERAND(uint32_t val) +{ + return ((val) << DPU_BS_ALU_CFG_BS_ALU_OPERAND__SHIFT) & DPU_BS_ALU_CFG_BS_ALU_OPERAND__MASK; +} + +#define REG_DPU_BS_MUL_CFG 0x00004048 +#define DPU_BS_MUL_CFG_BS_MUL_OPERAND__MASK 0xffff0000 +#define DPU_BS_MUL_CFG_BS_MUL_OPERAND__SHIFT 16 +static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_OPERAND(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_BS_MUL_OPERAND__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_OPERAND__MASK; +} +#define DPU_BS_MUL_CFG_RESERVED_0__MASK 0x0000c000 +#define DPU_BS_MUL_CFG_RESERVED_0__SHIFT 14 +static inline uint32_t DPU_BS_MUL_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_RESERVED_0__SHIFT) & DPU_BS_MUL_CFG_RESERVED_0__MASK; +} +#define DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__MASK 0x00003f00 +#define DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__SHIFT 8 +static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__MASK; +} +#define DPU_BS_MUL_CFG_RESERVED_1__MASK 0x000000fc +#define DPU_BS_MUL_CFG_RESERVED_1__SHIFT 2 +static inline uint32_t DPU_BS_MUL_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_RESERVED_1__SHIFT) & DPU_BS_MUL_CFG_RESERVED_1__MASK; +} +#define DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__MASK 0x00000002 +#define DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__SHIFT 1 +static inline uint32_t DPU_BS_MUL_CFG_BS_TRUNCATE_SRC(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__SHIFT) & DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__MASK; +} +#define DPU_BS_MUL_CFG_BS_MUL_SRC__MASK 0x00000001 +#define DPU_BS_MUL_CFG_BS_MUL_SRC__SHIFT 0 +static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_SRC(uint32_t val) +{ + return ((val) << DPU_BS_MUL_CFG_BS_MUL_SRC__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_SRC__MASK; +} + +#define REG_DPU_BS_RELUX_CMP_VALUE 0x0000404c +#define DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__MASK 0xffffffff +#define DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__SHIFT 0 +static inline uint32_t DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT(uint32_t val) +{ + return ((val) << DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__SHIFT) & DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__MASK; +} + +#define REG_DPU_BS_OW_CFG 0x00004050 +#define DPU_BS_OW_CFG_RGP_CNTER__MASK 0xf0000000 +#define DPU_BS_OW_CFG_RGP_CNTER__SHIFT 28 +static inline uint32_t DPU_BS_OW_CFG_RGP_CNTER(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_RGP_CNTER__SHIFT) & DPU_BS_OW_CFG_RGP_CNTER__MASK; +} +#define DPU_BS_OW_CFG_TP_ORG_EN__MASK 0x08000000 +#define DPU_BS_OW_CFG_TP_ORG_EN__SHIFT 27 +static inline uint32_t DPU_BS_OW_CFG_TP_ORG_EN(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_TP_ORG_EN__SHIFT) & DPU_BS_OW_CFG_TP_ORG_EN__MASK; +} +#define DPU_BS_OW_CFG_RESERVED_0__MASK 0x07fff800 +#define DPU_BS_OW_CFG_RESERVED_0__SHIFT 11 +static inline uint32_t DPU_BS_OW_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_RESERVED_0__SHIFT) & DPU_BS_OW_CFG_RESERVED_0__MASK; +} +#define DPU_BS_OW_CFG_SIZE_E_2__MASK 0x00000700 +#define DPU_BS_OW_CFG_SIZE_E_2__SHIFT 8 +static inline uint32_t DPU_BS_OW_CFG_SIZE_E_2(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_SIZE_E_2__SHIFT) & DPU_BS_OW_CFG_SIZE_E_2__MASK; +} +#define DPU_BS_OW_CFG_SIZE_E_1__MASK 0x000000e0 +#define DPU_BS_OW_CFG_SIZE_E_1__SHIFT 5 +static inline uint32_t DPU_BS_OW_CFG_SIZE_E_1(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_SIZE_E_1__SHIFT) & DPU_BS_OW_CFG_SIZE_E_1__MASK; +} +#define DPU_BS_OW_CFG_SIZE_E_0__MASK 0x0000001c +#define DPU_BS_OW_CFG_SIZE_E_0__SHIFT 2 +static inline uint32_t DPU_BS_OW_CFG_SIZE_E_0(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_SIZE_E_0__SHIFT) & DPU_BS_OW_CFG_SIZE_E_0__MASK; +} +#define DPU_BS_OW_CFG_OD_BYPASS__MASK 0x00000002 +#define DPU_BS_OW_CFG_OD_BYPASS__SHIFT 1 +static inline uint32_t DPU_BS_OW_CFG_OD_BYPASS(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_OD_BYPASS__SHIFT) & DPU_BS_OW_CFG_OD_BYPASS__MASK; +} +#define DPU_BS_OW_CFG_OW_SRC__MASK 0x00000001 +#define DPU_BS_OW_CFG_OW_SRC__SHIFT 0 +static inline uint32_t DPU_BS_OW_CFG_OW_SRC(uint32_t val) +{ + return ((val) << DPU_BS_OW_CFG_OW_SRC__SHIFT) & DPU_BS_OW_CFG_OW_SRC__MASK; +} + +#define REG_DPU_BS_OW_OP 0x00004054 +#define DPU_BS_OW_OP_RESERVED_0__MASK 0xffff0000 +#define DPU_BS_OW_OP_RESERVED_0__SHIFT 16 +static inline uint32_t DPU_BS_OW_OP_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BS_OW_OP_RESERVED_0__SHIFT) & DPU_BS_OW_OP_RESERVED_0__MASK; +} +#define DPU_BS_OW_OP_OW_OP__MASK 0x0000ffff +#define DPU_BS_OW_OP_OW_OP__SHIFT 0 +static inline uint32_t DPU_BS_OW_OP_OW_OP(uint32_t val) +{ + return ((val) << DPU_BS_OW_OP_OW_OP__SHIFT) & DPU_BS_OW_OP_OW_OP__MASK; +} + +#define REG_DPU_WDMA_SIZE_0 0x00004058 +#define DPU_WDMA_SIZE_0_RESERVED_0__MASK 0xf0000000 +#define DPU_WDMA_SIZE_0_RESERVED_0__SHIFT 28 +static inline uint32_t DPU_WDMA_SIZE_0_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_0_RESERVED_0__SHIFT) & DPU_WDMA_SIZE_0_RESERVED_0__MASK; +} +#define DPU_WDMA_SIZE_0_TP_PRECISION__MASK 0x08000000 +#define DPU_WDMA_SIZE_0_TP_PRECISION__SHIFT 27 +static inline uint32_t DPU_WDMA_SIZE_0_TP_PRECISION(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_0_TP_PRECISION__SHIFT) & DPU_WDMA_SIZE_0_TP_PRECISION__MASK; +} +#define DPU_WDMA_SIZE_0_SIZE_C_WDMA__MASK 0x07ff0000 +#define DPU_WDMA_SIZE_0_SIZE_C_WDMA__SHIFT 16 +static inline uint32_t DPU_WDMA_SIZE_0_SIZE_C_WDMA(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_0_SIZE_C_WDMA__SHIFT) & DPU_WDMA_SIZE_0_SIZE_C_WDMA__MASK; +} +#define DPU_WDMA_SIZE_0_RESERVED_1__MASK 0x0000e000 +#define DPU_WDMA_SIZE_0_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_WDMA_SIZE_0_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_0_RESERVED_1__SHIFT) & DPU_WDMA_SIZE_0_RESERVED_1__MASK; +} +#define DPU_WDMA_SIZE_0_CHANNEL_WDMA__MASK 0x00001fff +#define DPU_WDMA_SIZE_0_CHANNEL_WDMA__SHIFT 0 +static inline uint32_t DPU_WDMA_SIZE_0_CHANNEL_WDMA(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_0_CHANNEL_WDMA__SHIFT) & DPU_WDMA_SIZE_0_CHANNEL_WDMA__MASK; +} + +#define REG_DPU_WDMA_SIZE_1 0x0000405c +#define DPU_WDMA_SIZE_1_RESERVED_0__MASK 0xe0000000 +#define DPU_WDMA_SIZE_1_RESERVED_0__SHIFT 29 +static inline uint32_t DPU_WDMA_SIZE_1_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_1_RESERVED_0__SHIFT) & DPU_WDMA_SIZE_1_RESERVED_0__MASK; +} +#define DPU_WDMA_SIZE_1_HEIGHT_WDMA__MASK 0x1fff0000 +#define DPU_WDMA_SIZE_1_HEIGHT_WDMA__SHIFT 16 +static inline uint32_t DPU_WDMA_SIZE_1_HEIGHT_WDMA(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_1_HEIGHT_WDMA__SHIFT) & DPU_WDMA_SIZE_1_HEIGHT_WDMA__MASK; +} +#define DPU_WDMA_SIZE_1_RESERVED_1__MASK 0x0000e000 +#define DPU_WDMA_SIZE_1_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_WDMA_SIZE_1_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_1_RESERVED_1__SHIFT) & DPU_WDMA_SIZE_1_RESERVED_1__MASK; +} +#define DPU_WDMA_SIZE_1_WIDTH_WDMA__MASK 0x00001fff +#define DPU_WDMA_SIZE_1_WIDTH_WDMA__SHIFT 0 +static inline uint32_t DPU_WDMA_SIZE_1_WIDTH_WDMA(uint32_t val) +{ + return ((val) << DPU_WDMA_SIZE_1_WIDTH_WDMA__SHIFT) & DPU_WDMA_SIZE_1_WIDTH_WDMA__MASK; +} + +#define REG_DPU_BN_CFG 0x00004060 +#define DPU_BN_CFG_RESERVED_0__MASK 0xfff00000 +#define DPU_BN_CFG_RESERVED_0__SHIFT 20 +static inline uint32_t DPU_BN_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BN_CFG_RESERVED_0__SHIFT) & DPU_BN_CFG_RESERVED_0__MASK; +} +#define DPU_BN_CFG_BN_ALU_ALGO__MASK 0x000f0000 +#define DPU_BN_CFG_BN_ALU_ALGO__SHIFT 16 +static inline uint32_t DPU_BN_CFG_BN_ALU_ALGO(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_ALU_ALGO__SHIFT) & DPU_BN_CFG_BN_ALU_ALGO__MASK; +} +#define DPU_BN_CFG_RESERVED_1__MASK 0x0000fe00 +#define DPU_BN_CFG_RESERVED_1__SHIFT 9 +static inline uint32_t DPU_BN_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_BN_CFG_RESERVED_1__SHIFT) & DPU_BN_CFG_RESERVED_1__MASK; +} +#define DPU_BN_CFG_BN_ALU_SRC__MASK 0x00000100 +#define DPU_BN_CFG_BN_ALU_SRC__SHIFT 8 +static inline uint32_t DPU_BN_CFG_BN_ALU_SRC(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_ALU_SRC__SHIFT) & DPU_BN_CFG_BN_ALU_SRC__MASK; +} +#define DPU_BN_CFG_BN_RELUX_EN__MASK 0x00000080 +#define DPU_BN_CFG_BN_RELUX_EN__SHIFT 7 +static inline uint32_t DPU_BN_CFG_BN_RELUX_EN(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_RELUX_EN__SHIFT) & DPU_BN_CFG_BN_RELUX_EN__MASK; +} +#define DPU_BN_CFG_BN_RELU_BYPASS__MASK 0x00000040 +#define DPU_BN_CFG_BN_RELU_BYPASS__SHIFT 6 +static inline uint32_t DPU_BN_CFG_BN_RELU_BYPASS(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_RELU_BYPASS__SHIFT) & DPU_BN_CFG_BN_RELU_BYPASS__MASK; +} +#define DPU_BN_CFG_BN_MUL_PRELU__MASK 0x00000020 +#define DPU_BN_CFG_BN_MUL_PRELU__SHIFT 5 +static inline uint32_t DPU_BN_CFG_BN_MUL_PRELU(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_MUL_PRELU__SHIFT) & DPU_BN_CFG_BN_MUL_PRELU__MASK; +} +#define DPU_BN_CFG_BN_MUL_BYPASS__MASK 0x00000010 +#define DPU_BN_CFG_BN_MUL_BYPASS__SHIFT 4 +static inline uint32_t DPU_BN_CFG_BN_MUL_BYPASS(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_MUL_BYPASS__SHIFT) & DPU_BN_CFG_BN_MUL_BYPASS__MASK; +} +#define DPU_BN_CFG_RESERVED_2__MASK 0x0000000c +#define DPU_BN_CFG_RESERVED_2__SHIFT 2 +static inline uint32_t DPU_BN_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << DPU_BN_CFG_RESERVED_2__SHIFT) & DPU_BN_CFG_RESERVED_2__MASK; +} +#define DPU_BN_CFG_BN_ALU_BYPASS__MASK 0x00000002 +#define DPU_BN_CFG_BN_ALU_BYPASS__SHIFT 1 +static inline uint32_t DPU_BN_CFG_BN_ALU_BYPASS(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_ALU_BYPASS__SHIFT) & DPU_BN_CFG_BN_ALU_BYPASS__MASK; +} +#define DPU_BN_CFG_BN_BYPASS__MASK 0x00000001 +#define DPU_BN_CFG_BN_BYPASS__SHIFT 0 +static inline uint32_t DPU_BN_CFG_BN_BYPASS(uint32_t val) +{ + return ((val) << DPU_BN_CFG_BN_BYPASS__SHIFT) & DPU_BN_CFG_BN_BYPASS__MASK; +} + +#define REG_DPU_BN_ALU_CFG 0x00004064 +#define DPU_BN_ALU_CFG_BN_ALU_OPERAND__MASK 0xffffffff +#define DPU_BN_ALU_CFG_BN_ALU_OPERAND__SHIFT 0 +static inline uint32_t DPU_BN_ALU_CFG_BN_ALU_OPERAND(uint32_t val) +{ + return ((val) << DPU_BN_ALU_CFG_BN_ALU_OPERAND__SHIFT) & DPU_BN_ALU_CFG_BN_ALU_OPERAND__MASK; +} + +#define REG_DPU_BN_MUL_CFG 0x00004068 +#define DPU_BN_MUL_CFG_BN_MUL_OPERAND__MASK 0xffff0000 +#define DPU_BN_MUL_CFG_BN_MUL_OPERAND__SHIFT 16 +static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_OPERAND(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_BN_MUL_OPERAND__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_OPERAND__MASK; +} +#define DPU_BN_MUL_CFG_RESERVED_0__MASK 0x0000c000 +#define DPU_BN_MUL_CFG_RESERVED_0__SHIFT 14 +static inline uint32_t DPU_BN_MUL_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_RESERVED_0__SHIFT) & DPU_BN_MUL_CFG_RESERVED_0__MASK; +} +#define DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__MASK 0x00003f00 +#define DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__SHIFT 8 +static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__MASK; +} +#define DPU_BN_MUL_CFG_RESERVED_1__MASK 0x000000fc +#define DPU_BN_MUL_CFG_RESERVED_1__SHIFT 2 +static inline uint32_t DPU_BN_MUL_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_RESERVED_1__SHIFT) & DPU_BN_MUL_CFG_RESERVED_1__MASK; +} +#define DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__MASK 0x00000002 +#define DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__SHIFT 1 +static inline uint32_t DPU_BN_MUL_CFG_BN_TRUNCATE_SRC(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__SHIFT) & DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__MASK; +} +#define DPU_BN_MUL_CFG_BN_MUL_SRC__MASK 0x00000001 +#define DPU_BN_MUL_CFG_BN_MUL_SRC__SHIFT 0 +static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_SRC(uint32_t val) +{ + return ((val) << DPU_BN_MUL_CFG_BN_MUL_SRC__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_SRC__MASK; +} + +#define REG_DPU_BN_RELUX_CMP_VALUE 0x0000406c +#define DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__MASK 0xffffffff +#define DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__SHIFT 0 +static inline uint32_t DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT(uint32_t val) +{ + return ((val) << DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__SHIFT) & DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__MASK; +} + +#define REG_DPU_EW_CFG 0x00004070 +#define DPU_EW_CFG_EW_CVT_TYPE__MASK 0x80000000 +#define DPU_EW_CFG_EW_CVT_TYPE__SHIFT 31 +static inline uint32_t DPU_EW_CFG_EW_CVT_TYPE(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_CVT_TYPE__SHIFT) & DPU_EW_CFG_EW_CVT_TYPE__MASK; +} +#define DPU_EW_CFG_EW_CVT_ROUND__MASK 0x40000000 +#define DPU_EW_CFG_EW_CVT_ROUND__SHIFT 30 +static inline uint32_t DPU_EW_CFG_EW_CVT_ROUND(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_CVT_ROUND__SHIFT) & DPU_EW_CFG_EW_CVT_ROUND__MASK; +} +#define DPU_EW_CFG_EW_DATA_MODE__MASK 0x30000000 +#define DPU_EW_CFG_EW_DATA_MODE__SHIFT 28 +static inline uint32_t DPU_EW_CFG_EW_DATA_MODE(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_DATA_MODE__SHIFT) & DPU_EW_CFG_EW_DATA_MODE__MASK; +} +#define DPU_EW_CFG_RESERVED_0__MASK 0x0f000000 +#define DPU_EW_CFG_RESERVED_0__SHIFT 24 +static inline uint32_t DPU_EW_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_EW_CFG_RESERVED_0__SHIFT) & DPU_EW_CFG_RESERVED_0__MASK; +} +#define DPU_EW_CFG_EDATA_SIZE__MASK 0x00c00000 +#define DPU_EW_CFG_EDATA_SIZE__SHIFT 22 +static inline uint32_t DPU_EW_CFG_EDATA_SIZE(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EDATA_SIZE__SHIFT) & DPU_EW_CFG_EDATA_SIZE__MASK; +} +#define DPU_EW_CFG_EW_EQUAL_EN__MASK 0x00200000 +#define DPU_EW_CFG_EW_EQUAL_EN__SHIFT 21 +static inline uint32_t DPU_EW_CFG_EW_EQUAL_EN(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_EQUAL_EN__SHIFT) & DPU_EW_CFG_EW_EQUAL_EN__MASK; +} +#define DPU_EW_CFG_EW_BINARY_EN__MASK 0x00100000 +#define DPU_EW_CFG_EW_BINARY_EN__SHIFT 20 +static inline uint32_t DPU_EW_CFG_EW_BINARY_EN(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_BINARY_EN__SHIFT) & DPU_EW_CFG_EW_BINARY_EN__MASK; +} +#define DPU_EW_CFG_EW_ALU_ALGO__MASK 0x000f0000 +#define DPU_EW_CFG_EW_ALU_ALGO__SHIFT 16 +static inline uint32_t DPU_EW_CFG_EW_ALU_ALGO(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_ALU_ALGO__SHIFT) & DPU_EW_CFG_EW_ALU_ALGO__MASK; +} +#define DPU_EW_CFG_RESERVED_1__MASK 0x0000f800 +#define DPU_EW_CFG_RESERVED_1__SHIFT 11 +static inline uint32_t DPU_EW_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_EW_CFG_RESERVED_1__SHIFT) & DPU_EW_CFG_RESERVED_1__MASK; +} +#define DPU_EW_CFG_EW_RELUX_EN__MASK 0x00000400 +#define DPU_EW_CFG_EW_RELUX_EN__SHIFT 10 +static inline uint32_t DPU_EW_CFG_EW_RELUX_EN(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_RELUX_EN__SHIFT) & DPU_EW_CFG_EW_RELUX_EN__MASK; +} +#define DPU_EW_CFG_EW_RELU_BYPASS__MASK 0x00000200 +#define DPU_EW_CFG_EW_RELU_BYPASS__SHIFT 9 +static inline uint32_t DPU_EW_CFG_EW_RELU_BYPASS(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_RELU_BYPASS__SHIFT) & DPU_EW_CFG_EW_RELU_BYPASS__MASK; +} +#define DPU_EW_CFG_EW_OP_CVT_BYPASS__MASK 0x00000100 +#define DPU_EW_CFG_EW_OP_CVT_BYPASS__SHIFT 8 +static inline uint32_t DPU_EW_CFG_EW_OP_CVT_BYPASS(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_OP_CVT_BYPASS__SHIFT) & DPU_EW_CFG_EW_OP_CVT_BYPASS__MASK; +} +#define DPU_EW_CFG_EW_LUT_BYPASS__MASK 0x00000080 +#define DPU_EW_CFG_EW_LUT_BYPASS__SHIFT 7 +static inline uint32_t DPU_EW_CFG_EW_LUT_BYPASS(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_LUT_BYPASS__SHIFT) & DPU_EW_CFG_EW_LUT_BYPASS__MASK; +} +#define DPU_EW_CFG_EW_OP_SRC__MASK 0x00000040 +#define DPU_EW_CFG_EW_OP_SRC__SHIFT 6 +static inline uint32_t DPU_EW_CFG_EW_OP_SRC(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_OP_SRC__SHIFT) & DPU_EW_CFG_EW_OP_SRC__MASK; +} +#define DPU_EW_CFG_EW_MUL_PRELU__MASK 0x00000020 +#define DPU_EW_CFG_EW_MUL_PRELU__SHIFT 5 +static inline uint32_t DPU_EW_CFG_EW_MUL_PRELU(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_MUL_PRELU__SHIFT) & DPU_EW_CFG_EW_MUL_PRELU__MASK; +} +#define DPU_EW_CFG_RESERVED_2__MASK 0x00000018 +#define DPU_EW_CFG_RESERVED_2__SHIFT 3 +static inline uint32_t DPU_EW_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << DPU_EW_CFG_RESERVED_2__SHIFT) & DPU_EW_CFG_RESERVED_2__MASK; +} +#define DPU_EW_CFG_EW_OP_TYPE__MASK 0x00000004 +#define DPU_EW_CFG_EW_OP_TYPE__SHIFT 2 +static inline uint32_t DPU_EW_CFG_EW_OP_TYPE(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_OP_TYPE__SHIFT) & DPU_EW_CFG_EW_OP_TYPE__MASK; +} +#define DPU_EW_CFG_EW_OP_BYPASS__MASK 0x00000002 +#define DPU_EW_CFG_EW_OP_BYPASS__SHIFT 1 +static inline uint32_t DPU_EW_CFG_EW_OP_BYPASS(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_OP_BYPASS__SHIFT) & DPU_EW_CFG_EW_OP_BYPASS__MASK; +} +#define DPU_EW_CFG_EW_BYPASS__MASK 0x00000001 +#define DPU_EW_CFG_EW_BYPASS__SHIFT 0 +static inline uint32_t DPU_EW_CFG_EW_BYPASS(uint32_t val) +{ + return ((val) << DPU_EW_CFG_EW_BYPASS__SHIFT) & DPU_EW_CFG_EW_BYPASS__MASK; +} + +#define REG_DPU_EW_CVT_OFFSET_VALUE 0x00004074 +#define DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__MASK 0xffffffff +#define DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__SHIFT 0 +static inline uint32_t DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET(uint32_t val) +{ + return ((val) << DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__SHIFT) & DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__MASK; +} + +#define REG_DPU_EW_CVT_SCALE_VALUE 0x00004078 +#define DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__MASK 0xffc00000 +#define DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__SHIFT 22 +static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE(uint32_t val) +{ + return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__MASK; +} +#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__MASK 0x003f0000 +#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__SHIFT 16 +static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT(uint32_t val) +{ + return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__MASK; +} +#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__MASK 0x0000ffff +#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__SHIFT 0 +static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE(uint32_t val) +{ + return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__MASK; +} + +#define REG_DPU_EW_RELUX_CMP_VALUE 0x0000407c +#define DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__MASK 0xffffffff +#define DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__SHIFT 0 +static inline uint32_t DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT(uint32_t val) +{ + return ((val) << DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__SHIFT) & DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__MASK; +} + +#define REG_DPU_OUT_CVT_OFFSET 0x00004080 +#define DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__MASK 0xffffffff +#define DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__SHIFT 0 +static inline uint32_t DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__SHIFT) & DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__MASK; +} + +#define REG_DPU_OUT_CVT_SCALE 0x00004084 +#define DPU_OUT_CVT_SCALE_RESERVED_0__MASK 0xfffe0000 +#define DPU_OUT_CVT_SCALE_RESERVED_0__SHIFT 17 +static inline uint32_t DPU_OUT_CVT_SCALE_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SCALE_RESERVED_0__SHIFT) & DPU_OUT_CVT_SCALE_RESERVED_0__MASK; +} +#define DPU_OUT_CVT_SCALE_FP32TOFP16_EN__MASK 0x00010000 +#define DPU_OUT_CVT_SCALE_FP32TOFP16_EN__SHIFT 16 +static inline uint32_t DPU_OUT_CVT_SCALE_FP32TOFP16_EN(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SCALE_FP32TOFP16_EN__SHIFT) & DPU_OUT_CVT_SCALE_FP32TOFP16_EN__MASK; +} +#define DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__MASK 0x0000ffff +#define DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__SHIFT 0 +static inline uint32_t DPU_OUT_CVT_SCALE_OUT_CVT_SCALE(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__SHIFT) & DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__MASK; +} + +#define REG_DPU_OUT_CVT_SHIFT 0x00004088 +#define DPU_OUT_CVT_SHIFT_CVT_TYPE__MASK 0x80000000 +#define DPU_OUT_CVT_SHIFT_CVT_TYPE__SHIFT 31 +static inline uint32_t DPU_OUT_CVT_SHIFT_CVT_TYPE(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SHIFT_CVT_TYPE__SHIFT) & DPU_OUT_CVT_SHIFT_CVT_TYPE__MASK; +} +#define DPU_OUT_CVT_SHIFT_CVT_ROUND__MASK 0x40000000 +#define DPU_OUT_CVT_SHIFT_CVT_ROUND__SHIFT 30 +static inline uint32_t DPU_OUT_CVT_SHIFT_CVT_ROUND(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SHIFT_CVT_ROUND__SHIFT) & DPU_OUT_CVT_SHIFT_CVT_ROUND__MASK; +} +#define DPU_OUT_CVT_SHIFT_RESERVED_0__MASK 0x3ff00000 +#define DPU_OUT_CVT_SHIFT_RESERVED_0__SHIFT 20 +static inline uint32_t DPU_OUT_CVT_SHIFT_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SHIFT_RESERVED_0__SHIFT) & DPU_OUT_CVT_SHIFT_RESERVED_0__MASK; +} +#define DPU_OUT_CVT_SHIFT_MINUS_EXP__MASK 0x000ff000 +#define DPU_OUT_CVT_SHIFT_MINUS_EXP__SHIFT 12 +static inline uint32_t DPU_OUT_CVT_SHIFT_MINUS_EXP(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SHIFT_MINUS_EXP__SHIFT) & DPU_OUT_CVT_SHIFT_MINUS_EXP__MASK; +} +#define DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__MASK 0x00000fff +#define DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__SHIFT 0 +static inline uint32_t DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT(uint32_t val) +{ + return ((val) << DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__SHIFT) & DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__MASK; +} + +#define REG_DPU_EW_OP_VALUE_0 0x00004090 +#define DPU_EW_OP_VALUE_0_EW_OPERAND_0__MASK 0xffffffff +#define DPU_EW_OP_VALUE_0_EW_OPERAND_0__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_0_EW_OPERAND_0(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_0_EW_OPERAND_0__SHIFT) & DPU_EW_OP_VALUE_0_EW_OPERAND_0__MASK; +} + +#define REG_DPU_EW_OP_VALUE_1 0x00004094 +#define DPU_EW_OP_VALUE_1_EW_OPERAND_1__MASK 0xffffffff +#define DPU_EW_OP_VALUE_1_EW_OPERAND_1__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_1_EW_OPERAND_1(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_1_EW_OPERAND_1__SHIFT) & DPU_EW_OP_VALUE_1_EW_OPERAND_1__MASK; +} + +#define REG_DPU_EW_OP_VALUE_2 0x00004098 +#define DPU_EW_OP_VALUE_2_EW_OPERAND_2__MASK 0xffffffff +#define DPU_EW_OP_VALUE_2_EW_OPERAND_2__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_2_EW_OPERAND_2(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_2_EW_OPERAND_2__SHIFT) & DPU_EW_OP_VALUE_2_EW_OPERAND_2__MASK; +} + +#define REG_DPU_EW_OP_VALUE_3 0x0000409c +#define DPU_EW_OP_VALUE_3_EW_OPERAND_3__MASK 0xffffffff +#define DPU_EW_OP_VALUE_3_EW_OPERAND_3__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_3_EW_OPERAND_3(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_3_EW_OPERAND_3__SHIFT) & DPU_EW_OP_VALUE_3_EW_OPERAND_3__MASK; +} + +#define REG_DPU_EW_OP_VALUE_4 0x000040a0 +#define DPU_EW_OP_VALUE_4_EW_OPERAND_4__MASK 0xffffffff +#define DPU_EW_OP_VALUE_4_EW_OPERAND_4__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_4_EW_OPERAND_4(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_4_EW_OPERAND_4__SHIFT) & DPU_EW_OP_VALUE_4_EW_OPERAND_4__MASK; +} + +#define REG_DPU_EW_OP_VALUE_5 0x000040a4 +#define DPU_EW_OP_VALUE_5_EW_OPERAND_5__MASK 0xffffffff +#define DPU_EW_OP_VALUE_5_EW_OPERAND_5__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_5_EW_OPERAND_5(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_5_EW_OPERAND_5__SHIFT) & DPU_EW_OP_VALUE_5_EW_OPERAND_5__MASK; +} + +#define REG_DPU_EW_OP_VALUE_6 0x000040a8 +#define DPU_EW_OP_VALUE_6_EW_OPERAND_6__MASK 0xffffffff +#define DPU_EW_OP_VALUE_6_EW_OPERAND_6__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_6_EW_OPERAND_6(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_6_EW_OPERAND_6__SHIFT) & DPU_EW_OP_VALUE_6_EW_OPERAND_6__MASK; +} + +#define REG_DPU_EW_OP_VALUE_7 0x000040ac +#define DPU_EW_OP_VALUE_7_EW_OPERAND_7__MASK 0xffffffff +#define DPU_EW_OP_VALUE_7_EW_OPERAND_7__SHIFT 0 +static inline uint32_t DPU_EW_OP_VALUE_7_EW_OPERAND_7(uint32_t val) +{ + return ((val) << DPU_EW_OP_VALUE_7_EW_OPERAND_7__SHIFT) & DPU_EW_OP_VALUE_7_EW_OPERAND_7__MASK; +} + +#define REG_DPU_SURFACE_ADD 0x000040c0 +#define DPU_SURFACE_ADD_SURF_ADD__MASK 0xfffffff0 +#define DPU_SURFACE_ADD_SURF_ADD__SHIFT 4 +static inline uint32_t DPU_SURFACE_ADD_SURF_ADD(uint32_t val) +{ + return ((val) << DPU_SURFACE_ADD_SURF_ADD__SHIFT) & DPU_SURFACE_ADD_SURF_ADD__MASK; +} +#define DPU_SURFACE_ADD_RESERVED_0__MASK 0x0000000f +#define DPU_SURFACE_ADD_RESERVED_0__SHIFT 0 +static inline uint32_t DPU_SURFACE_ADD_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_SURFACE_ADD_RESERVED_0__SHIFT) & DPU_SURFACE_ADD_RESERVED_0__MASK; +} + +#define REG_DPU_LUT_ACCESS_CFG 0x00004100 +#define DPU_LUT_ACCESS_CFG_RESERVED_0__MASK 0xfffc0000 +#define DPU_LUT_ACCESS_CFG_RESERVED_0__SHIFT 18 +static inline uint32_t DPU_LUT_ACCESS_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_CFG_RESERVED_0__SHIFT) & DPU_LUT_ACCESS_CFG_RESERVED_0__MASK; +} +#define DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__MASK 0x00020000 +#define DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__SHIFT 17 +static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__MASK; +} +#define DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__MASK 0x00010000 +#define DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__SHIFT 16 +static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_TABLE_ID(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__MASK; +} +#define DPU_LUT_ACCESS_CFG_RESERVED_1__MASK 0x0000fc00 +#define DPU_LUT_ACCESS_CFG_RESERVED_1__SHIFT 10 +static inline uint32_t DPU_LUT_ACCESS_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_CFG_RESERVED_1__SHIFT) & DPU_LUT_ACCESS_CFG_RESERVED_1__MASK; +} +#define DPU_LUT_ACCESS_CFG_LUT_ADDR__MASK 0x000003ff +#define DPU_LUT_ACCESS_CFG_LUT_ADDR__SHIFT 0 +static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_ADDR(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_CFG_LUT_ADDR__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_ADDR__MASK; +} + +#define REG_DPU_LUT_ACCESS_DATA 0x00004104 +#define DPU_LUT_ACCESS_DATA_RESERVED_0__MASK 0xffff0000 +#define DPU_LUT_ACCESS_DATA_RESERVED_0__SHIFT 16 +static inline uint32_t DPU_LUT_ACCESS_DATA_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_DATA_RESERVED_0__SHIFT) & DPU_LUT_ACCESS_DATA_RESERVED_0__MASK; +} +#define DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__MASK 0x0000ffff +#define DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__SHIFT 0 +static inline uint32_t DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA(uint32_t val) +{ + return ((val) << DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__SHIFT) & DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__MASK; +} + +#define REG_DPU_LUT_CFG 0x00004108 +#define DPU_LUT_CFG_RESERVED_0__MASK 0xffffff00 +#define DPU_LUT_CFG_RESERVED_0__SHIFT 8 +static inline uint32_t DPU_LUT_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_RESERVED_0__SHIFT) & DPU_LUT_CFG_RESERVED_0__MASK; +} +#define DPU_LUT_CFG_LUT_CAL_SEL__MASK 0x00000080 +#define DPU_LUT_CFG_LUT_CAL_SEL__SHIFT 7 +static inline uint32_t DPU_LUT_CFG_LUT_CAL_SEL(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_CAL_SEL__SHIFT) & DPU_LUT_CFG_LUT_CAL_SEL__MASK; +} +#define DPU_LUT_CFG_LUT_HYBRID_PRIORITY__MASK 0x00000040 +#define DPU_LUT_CFG_LUT_HYBRID_PRIORITY__SHIFT 6 +static inline uint32_t DPU_LUT_CFG_LUT_HYBRID_PRIORITY(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_HYBRID_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_HYBRID_PRIORITY__MASK; +} +#define DPU_LUT_CFG_LUT_OFLOW_PRIORITY__MASK 0x00000020 +#define DPU_LUT_CFG_LUT_OFLOW_PRIORITY__SHIFT 5 +static inline uint32_t DPU_LUT_CFG_LUT_OFLOW_PRIORITY(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_OFLOW_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_OFLOW_PRIORITY__MASK; +} +#define DPU_LUT_CFG_LUT_UFLOW_PRIORITY__MASK 0x00000010 +#define DPU_LUT_CFG_LUT_UFLOW_PRIORITY__SHIFT 4 +static inline uint32_t DPU_LUT_CFG_LUT_UFLOW_PRIORITY(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_UFLOW_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_UFLOW_PRIORITY__MASK; +} +#define DPU_LUT_CFG_LUT_LO_LE_MUX__MASK 0x0000000c +#define DPU_LUT_CFG_LUT_LO_LE_MUX__SHIFT 2 +static inline uint32_t DPU_LUT_CFG_LUT_LO_LE_MUX(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_LO_LE_MUX__SHIFT) & DPU_LUT_CFG_LUT_LO_LE_MUX__MASK; +} +#define DPU_LUT_CFG_LUT_EXPAND_EN__MASK 0x00000002 +#define DPU_LUT_CFG_LUT_EXPAND_EN__SHIFT 1 +static inline uint32_t DPU_LUT_CFG_LUT_EXPAND_EN(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_EXPAND_EN__SHIFT) & DPU_LUT_CFG_LUT_EXPAND_EN__MASK; +} +#define DPU_LUT_CFG_LUT_ROAD_SEL__MASK 0x00000001 +#define DPU_LUT_CFG_LUT_ROAD_SEL__SHIFT 0 +static inline uint32_t DPU_LUT_CFG_LUT_ROAD_SEL(uint32_t val) +{ + return ((val) << DPU_LUT_CFG_LUT_ROAD_SEL__SHIFT) & DPU_LUT_CFG_LUT_ROAD_SEL__MASK; +} + +#define REG_DPU_LUT_INFO 0x0000410c +#define DPU_LUT_INFO_RESERVED_0__MASK 0xff000000 +#define DPU_LUT_INFO_RESERVED_0__SHIFT 24 +static inline uint32_t DPU_LUT_INFO_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_INFO_RESERVED_0__SHIFT) & DPU_LUT_INFO_RESERVED_0__MASK; +} +#define DPU_LUT_INFO_LUT_LO_INDEX_SELECT__MASK 0x00ff0000 +#define DPU_LUT_INFO_LUT_LO_INDEX_SELECT__SHIFT 16 +static inline uint32_t DPU_LUT_INFO_LUT_LO_INDEX_SELECT(uint32_t val) +{ + return ((val) << DPU_LUT_INFO_LUT_LO_INDEX_SELECT__SHIFT) & DPU_LUT_INFO_LUT_LO_INDEX_SELECT__MASK; +} +#define DPU_LUT_INFO_LUT_LE_INDEX_SELECT__MASK 0x0000ff00 +#define DPU_LUT_INFO_LUT_LE_INDEX_SELECT__SHIFT 8 +static inline uint32_t DPU_LUT_INFO_LUT_LE_INDEX_SELECT(uint32_t val) +{ + return ((val) << DPU_LUT_INFO_LUT_LE_INDEX_SELECT__SHIFT) & DPU_LUT_INFO_LUT_LE_INDEX_SELECT__MASK; +} +#define DPU_LUT_INFO_RESERVED_1__MASK 0x000000ff +#define DPU_LUT_INFO_RESERVED_1__SHIFT 0 +static inline uint32_t DPU_LUT_INFO_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_LUT_INFO_RESERVED_1__SHIFT) & DPU_LUT_INFO_RESERVED_1__MASK; +} + +#define REG_DPU_LUT_LE_START 0x00004110 +#define DPU_LUT_LE_START_LUT_LE_START__MASK 0xffffffff +#define DPU_LUT_LE_START_LUT_LE_START__SHIFT 0 +static inline uint32_t DPU_LUT_LE_START_LUT_LE_START(uint32_t val) +{ + return ((val) << DPU_LUT_LE_START_LUT_LE_START__SHIFT) & DPU_LUT_LE_START_LUT_LE_START__MASK; +} + +#define REG_DPU_LUT_LE_END 0x00004114 +#define DPU_LUT_LE_END_LUT_LE_END__MASK 0xffffffff +#define DPU_LUT_LE_END_LUT_LE_END__SHIFT 0 +static inline uint32_t DPU_LUT_LE_END_LUT_LE_END(uint32_t val) +{ + return ((val) << DPU_LUT_LE_END_LUT_LE_END__SHIFT) & DPU_LUT_LE_END_LUT_LE_END__MASK; +} + +#define REG_DPU_LUT_LO_START 0x00004118 +#define DPU_LUT_LO_START_LUT_LO_START__MASK 0xffffffff +#define DPU_LUT_LO_START_LUT_LO_START__SHIFT 0 +static inline uint32_t DPU_LUT_LO_START_LUT_LO_START(uint32_t val) +{ + return ((val) << DPU_LUT_LO_START_LUT_LO_START__SHIFT) & DPU_LUT_LO_START_LUT_LO_START__MASK; +} + +#define REG_DPU_LUT_LO_END 0x0000411c +#define DPU_LUT_LO_END_LUT_LO_END__MASK 0xffffffff +#define DPU_LUT_LO_END_LUT_LO_END__SHIFT 0 +static inline uint32_t DPU_LUT_LO_END_LUT_LO_END(uint32_t val) +{ + return ((val) << DPU_LUT_LO_END_LUT_LO_END__SHIFT) & DPU_LUT_LO_END_LUT_LO_END__MASK; +} + +#define REG_DPU_LUT_LE_SLOPE_SCALE 0x00004120 +#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__MASK 0xffff0000 +#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__SHIFT 16 +static inline uint32_t DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE(uint32_t val) +{ + return ((val) << DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__SHIFT) & DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__MASK; +} +#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__MASK 0x0000ffff +#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__SHIFT 0 +static inline uint32_t DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE(uint32_t val) +{ + return ((val) << DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__SHIFT) & DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__MASK; +} + +#define REG_DPU_LUT_LE_SLOPE_SHIFT 0x00004124 +#define DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__MASK 0xfffffc00 +#define DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__SHIFT 10 +static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__MASK; +} +#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__MASK 0x000003e0 +#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__SHIFT 5 +static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT(uint32_t val) +{ + return ((val) << DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__MASK; +} +#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__MASK 0x0000001f +#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__SHIFT 0 +static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT(uint32_t val) +{ + return ((val) << DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__MASK; +} + +#define REG_DPU_LUT_LO_SLOPE_SCALE 0x00004128 +#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__MASK 0xffff0000 +#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__SHIFT 16 +static inline uint32_t DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE(uint32_t val) +{ + return ((val) << DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__SHIFT) & DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__MASK; +} +#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__MASK 0x0000ffff +#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__SHIFT 0 +static inline uint32_t DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE(uint32_t val) +{ + return ((val) << DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__SHIFT) & DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__MASK; +} + +#define REG_DPU_LUT_LO_SLOPE_SHIFT 0x0000412c +#define DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__MASK 0xfffffc00 +#define DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__SHIFT 10 +static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__MASK; +} +#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__MASK 0x000003e0 +#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__SHIFT 5 +static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT(uint32_t val) +{ + return ((val) << DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__MASK; +} +#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__MASK 0x0000001f +#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__SHIFT 0 +static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT(uint32_t val) +{ + return ((val) << DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__MASK; +} + +#define REG_DPU_RDMA_RDMA_S_STATUS 0x00005000 +#define DPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define DPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t DPU_RDMA_RDMA_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK 0x00030000 +#define DPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t DPU_RDMA_RDMA_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT) & DPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK; +} +#define DPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define DPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t DPU_RDMA_RDMA_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK; +} +#define DPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK 0x00000003 +#define DPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT) & DPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK; +} + +#define REG_DPU_RDMA_RDMA_S_POINTER 0x00005004 +#define DPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define DPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK 0x00010000 +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define DPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK; +} +#define DPU_RDMA_RDMA_S_POINTER_POINTER__MASK 0x00000001 +#define DPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER__MASK; +} + +#define REG_DPU_RDMA_RDMA_OPERATION_ENABLE 0x00005008 +#define DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT) & DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_DPU_RDMA_RDMA_DATA_CUBE_WIDTH 0x0000500c +#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__MASK 0xffffe000 +#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__SHIFT 13 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__MASK 0x00001fff +#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__MASK; +} + +#define REG_DPU_RDMA_RDMA_DATA_CUBE_HEIGHT 0x00005010 +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__MASK 0xe0000000 +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT 29 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__MASK 0x1fff0000 +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__SHIFT 16 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__MASK; +} +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__MASK 0x0000e000 +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT 13 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__MASK; +} +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__MASK 0x00001fff +#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__MASK; +} + +#define REG_DPU_RDMA_RDMA_DATA_CUBE_CHANNEL 0x00005014 +#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__MASK 0xffffe000 +#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT 13 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__MASK 0x00001fff +#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__MASK; +} + +#define REG_DPU_RDMA_RDMA_SRC_BASE_ADDR 0x00005018 +#define DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK 0xffffffff +#define DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK; +} + +#define REG_DPU_RDMA_RDMA_BRDMA_CFG 0x0000501c +#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__MASK 0xffffffe0 +#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__SHIFT 5 +static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__MASK 0x0000001e +#define DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__MASK; +} +#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__MASK 0x00000001 +#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__MASK; +} + +#define REG_DPU_RDMA_RDMA_BS_BASE_ADDR 0x00005020 +#define DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__MASK 0xffffffff +#define DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__MASK; +} + +#define REG_DPU_RDMA_RDMA_NRDMA_CFG 0x00005028 +#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__MASK 0xffffffe0 +#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__SHIFT 5 +static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__MASK 0x0000001e +#define DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__MASK; +} +#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__MASK 0x00000001 +#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__MASK; +} + +#define REG_DPU_RDMA_RDMA_BN_BASE_ADDR 0x0000502c +#define DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__MASK 0xffffffff +#define DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__MASK; +} + +#define REG_DPU_RDMA_RDMA_ERDMA_CFG 0x00005034 +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__MASK 0xc0000000 +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__SHIFT 30 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__MASK 0x20000000 +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__SHIFT 29 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__MASK 0x10000000 +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__SHIFT 28 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__MASK 0x0ffffff0 +#define DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__MASK 0x0000000c +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__SHIFT 2 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__MASK 0x00000002 +#define DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__MASK; +} +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__MASK 0x00000001 +#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__MASK; +} + +#define REG_DPU_RDMA_RDMA_EW_BASE_ADDR 0x00005038 +#define DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__MASK 0xffffffff +#define DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__MASK; +} + +#define REG_DPU_RDMA_RDMA_EW_SURF_STRIDE 0x00005040 +#define DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__MASK 0xfffffff0 +#define DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__SHIFT) & DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__MASK; +} +#define DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__MASK 0x0000000f +#define DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__MASK; +} + +#define REG_DPU_RDMA_RDMA_FEATURE_MODE_CFG 0x00005044 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__MASK 0xfffc0000 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__SHIFT 18 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__MASK 0x00038000 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__SHIFT 15 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__MASK 0x00007800 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__SHIFT 11 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__MASK 0x00000700 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__SHIFT 8 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__MASK 0x000000e0 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__SHIFT 5 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__MASK 0x00000010 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__MASK 0x00000008 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__SHIFT 3 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__MASK 0x00000006 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__SHIFT 1 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__MASK; +} +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__MASK 0x00000001 +#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__MASK; +} + +#define REG_DPU_RDMA_RDMA_SRC_DMA_CFG 0x00005048 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__MASK 0xfff80000 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__SHIFT 19 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__MASK 0x0007c000 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__SHIFT 14 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__MASK 0x00002000 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__SHIFT 13 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__MASK 0x00001000 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__SHIFT 12 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__MASK 0x00000e00 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__SHIFT 9 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__MASK 0x000001c0 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__SHIFT 6 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__MASK 0x00000038 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__SHIFT 3 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__MASK; +} +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__MASK 0x00000007 +#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__MASK; +} + +#define REG_DPU_RDMA_RDMA_SURF_NOTCH 0x0000504c +#define DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__MASK 0xfffffff0 +#define DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__MASK; +} +#define DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__MASK 0x0000000f +#define DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__MASK; +} + +#define REG_DPU_RDMA_RDMA_PAD_CFG 0x00005064 +#define DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__MASK 0xffff0000 +#define DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__SHIFT 16 +static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__MASK; +} +#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__MASK 0x0000ff80 +#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__SHIFT 7 +static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__MASK; +} +#define DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__MASK 0x00000070 +#define DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_TOP(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__MASK; +} +#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__MASK 0x00000008 +#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__SHIFT 3 +static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__MASK; +} +#define DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__MASK 0x00000007 +#define DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__MASK; +} + +#define REG_DPU_RDMA_RDMA_WEIGHT 0x00005068 +#define DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__MASK 0xff000000 +#define DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__SHIFT 24 +static inline uint32_t DPU_RDMA_RDMA_WEIGHT_E_WEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__MASK; +} +#define DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__MASK 0x00ff0000 +#define DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__SHIFT 16 +static inline uint32_t DPU_RDMA_RDMA_WEIGHT_N_WEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__MASK; +} +#define DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__MASK 0x0000ff00 +#define DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__SHIFT 8 +static inline uint32_t DPU_RDMA_RDMA_WEIGHT_B_WEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__MASK; +} +#define DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__MASK 0x000000ff +#define DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_WEIGHT_M_WEIGHT(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__MASK; +} + +#define REG_DPU_RDMA_RDMA_EW_SURF_NOTCH 0x0000506c +#define DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__MASK 0xfffffff0 +#define DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__SHIFT 4 +static inline uint32_t DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__SHIFT) & DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__MASK; +} +#define DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__MASK 0x0000000f +#define DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__SHIFT 0 +static inline uint32_t DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0(uint32_t val) +{ + return ((val) << DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__MASK; +} + +#define REG_PPU_S_STATUS 0x00006000 +#define PPU_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define PPU_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t PPU_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_S_STATUS_RESERVED_0__SHIFT) & PPU_S_STATUS_RESERVED_0__MASK; +} +#define PPU_S_STATUS_STATUS_1__MASK 0x00030000 +#define PPU_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t PPU_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << PPU_S_STATUS_STATUS_1__SHIFT) & PPU_S_STATUS_STATUS_1__MASK; +} +#define PPU_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define PPU_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t PPU_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_S_STATUS_RESERVED_1__SHIFT) & PPU_S_STATUS_RESERVED_1__MASK; +} +#define PPU_S_STATUS_STATUS_0__MASK 0x00000003 +#define PPU_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t PPU_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << PPU_S_STATUS_STATUS_0__SHIFT) & PPU_S_STATUS_STATUS_0__MASK; +} + +#define REG_PPU_S_POINTER 0x00006004 +#define PPU_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define PPU_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t PPU_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_S_POINTER_RESERVED_0__SHIFT) & PPU_S_POINTER_RESERVED_0__MASK; +} +#define PPU_S_POINTER_EXECUTER__MASK 0x00010000 +#define PPU_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t PPU_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << PPU_S_POINTER_EXECUTER__SHIFT) & PPU_S_POINTER_EXECUTER__MASK; +} +#define PPU_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define PPU_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t PPU_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_S_POINTER_RESERVED_1__SHIFT) & PPU_S_POINTER_RESERVED_1__MASK; +} +#define PPU_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define PPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t PPU_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << PPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & PPU_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define PPU_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define PPU_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t PPU_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << PPU_S_POINTER_POINTER_PP_CLEAR__SHIFT) & PPU_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define PPU_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define PPU_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t PPU_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << PPU_S_POINTER_POINTER_PP_MODE__SHIFT) & PPU_S_POINTER_POINTER_PP_MODE__MASK; +} +#define PPU_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define PPU_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t PPU_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << PPU_S_POINTER_EXECUTER_PP_EN__SHIFT) & PPU_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define PPU_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define PPU_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t PPU_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << PPU_S_POINTER_POINTER_PP_EN__SHIFT) & PPU_S_POINTER_POINTER_PP_EN__MASK; +} +#define PPU_S_POINTER_POINTER__MASK 0x00000001 +#define PPU_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t PPU_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << PPU_S_POINTER_POINTER__SHIFT) & PPU_S_POINTER_POINTER__MASK; +} + +#define REG_PPU_OPERATION_ENABLE 0x00006008 +#define PPU_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define PPU_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t PPU_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_OPERATION_ENABLE_RESERVED_0__SHIFT) & PPU_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define PPU_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define PPU_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t PPU_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << PPU_OPERATION_ENABLE_OP_EN__SHIFT) & PPU_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_PPU_DATA_CUBE_IN_WIDTH 0x0000600c +#define PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_IN_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK 0x00001fff +#define PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT) & PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK; +} + +#define REG_PPU_DATA_CUBE_IN_HEIGHT 0x00006010 +#define PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK 0x00001fff +#define PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT) & PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK; +} + +#define REG_PPU_DATA_CUBE_IN_CHANNEL 0x00006014 +#define PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK 0x00001fff +#define PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT) & PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK; +} + +#define REG_PPU_DATA_CUBE_OUT_WIDTH 0x00006018 +#define PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__MASK 0x00001fff +#define PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__SHIFT) & PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__MASK; +} + +#define REG_PPU_DATA_CUBE_OUT_HEIGHT 0x0000601c +#define PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__MASK 0x00001fff +#define PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__SHIFT) & PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__MASK; +} + +#define REG_PPU_DATA_CUBE_OUT_CHANNEL 0x00006020 +#define PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__MASK 0xffffe000 +#define PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__MASK; +} +#define PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__MASK 0x00001fff +#define PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__SHIFT 0 +static inline uint32_t PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL(uint32_t val) +{ + return ((val) << PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__SHIFT) & PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__MASK; +} + +#define REG_PPU_OPERATION_MODE_CFG 0x00006024 +#define PPU_OPERATION_MODE_CFG_RESERVED_0__MASK 0x80000000 +#define PPU_OPERATION_MODE_CFG_RESERVED_0__SHIFT 31 +static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_0__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_0__MASK; +} +#define PPU_OPERATION_MODE_CFG_INDEX_EN__MASK 0x40000000 +#define PPU_OPERATION_MODE_CFG_INDEX_EN__SHIFT 30 +static inline uint32_t PPU_OPERATION_MODE_CFG_INDEX_EN(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_INDEX_EN__SHIFT) & PPU_OPERATION_MODE_CFG_INDEX_EN__MASK; +} +#define PPU_OPERATION_MODE_CFG_RESERVED_1__MASK 0x20000000 +#define PPU_OPERATION_MODE_CFG_RESERVED_1__SHIFT 29 +static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_1__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_1__MASK; +} +#define PPU_OPERATION_MODE_CFG_NOTCH_ADDR__MASK 0x1fff0000 +#define PPU_OPERATION_MODE_CFG_NOTCH_ADDR__SHIFT 16 +static inline uint32_t PPU_OPERATION_MODE_CFG_NOTCH_ADDR(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_NOTCH_ADDR__SHIFT) & PPU_OPERATION_MODE_CFG_NOTCH_ADDR__MASK; +} +#define PPU_OPERATION_MODE_CFG_RESERVED_2__MASK 0x0000ff00 +#define PPU_OPERATION_MODE_CFG_RESERVED_2__SHIFT 8 +static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_2__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_2__MASK; +} +#define PPU_OPERATION_MODE_CFG_USE_CNT__MASK 0x000000e0 +#define PPU_OPERATION_MODE_CFG_USE_CNT__SHIFT 5 +static inline uint32_t PPU_OPERATION_MODE_CFG_USE_CNT(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_USE_CNT__SHIFT) & PPU_OPERATION_MODE_CFG_USE_CNT__MASK; +} +#define PPU_OPERATION_MODE_CFG_FLYING_MODE__MASK 0x00000010 +#define PPU_OPERATION_MODE_CFG_FLYING_MODE__SHIFT 4 +static inline uint32_t PPU_OPERATION_MODE_CFG_FLYING_MODE(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_FLYING_MODE__SHIFT) & PPU_OPERATION_MODE_CFG_FLYING_MODE__MASK; +} +#define PPU_OPERATION_MODE_CFG_RESERVED_3__MASK 0x0000000c +#define PPU_OPERATION_MODE_CFG_RESERVED_3__SHIFT 2 +static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_3(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_3__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_3__MASK; +} +#define PPU_OPERATION_MODE_CFG_POOLING_METHOD__MASK 0x00000003 +#define PPU_OPERATION_MODE_CFG_POOLING_METHOD__SHIFT 0 +static inline uint32_t PPU_OPERATION_MODE_CFG_POOLING_METHOD(uint32_t val) +{ + return ((val) << PPU_OPERATION_MODE_CFG_POOLING_METHOD__SHIFT) & PPU_OPERATION_MODE_CFG_POOLING_METHOD__MASK; +} + +#define REG_PPU_POOLING_KERNEL_CFG 0x00006034 +#define PPU_POOLING_KERNEL_CFG_RESERVED_0__MASK 0xff000000 +#define PPU_POOLING_KERNEL_CFG_RESERVED_0__SHIFT 24 +static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_0__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_0__MASK; +} +#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__MASK 0x00f00000 +#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__SHIFT 20 +static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__MASK; +} +#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__MASK 0x000f0000 +#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__SHIFT 16 +static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__MASK; +} +#define PPU_POOLING_KERNEL_CFG_RESERVED_1__MASK 0x0000f000 +#define PPU_POOLING_KERNEL_CFG_RESERVED_1__SHIFT 12 +static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_1__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_1__MASK; +} +#define PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__MASK 0x00000f00 +#define PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__SHIFT 8 +static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__MASK; +} +#define PPU_POOLING_KERNEL_CFG_RESERVED_2__MASK 0x000000f0 +#define PPU_POOLING_KERNEL_CFG_RESERVED_2__SHIFT 4 +static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_2__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_2__MASK; +} +#define PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__MASK 0x0000000f +#define PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__SHIFT 0 +static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH(uint32_t val) +{ + return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__MASK; +} + +#define REG_PPU_RECIP_KERNEL_WIDTH 0x00006038 +#define PPU_RECIP_KERNEL_WIDTH_RESERVED_0__MASK 0xfffe0000 +#define PPU_RECIP_KERNEL_WIDTH_RESERVED_0__SHIFT 17 +static inline uint32_t PPU_RECIP_KERNEL_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RECIP_KERNEL_WIDTH_RESERVED_0__SHIFT) & PPU_RECIP_KERNEL_WIDTH_RESERVED_0__MASK; +} +#define PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__MASK 0x0001ffff +#define PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__SHIFT 0 +static inline uint32_t PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH(uint32_t val) +{ + return ((val) << PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__SHIFT) & PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__MASK; +} + +#define REG_PPU_RECIP_KERNEL_HEIGHT 0x0000603c +#define PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__MASK 0xfffe0000 +#define PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__SHIFT 17 +static inline uint32_t PPU_RECIP_KERNEL_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__SHIFT) & PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__MASK; +} +#define PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__MASK 0x0001ffff +#define PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__SHIFT 0 +static inline uint32_t PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT(uint32_t val) +{ + return ((val) << PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__SHIFT) & PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__MASK; +} + +#define REG_PPU_POOLING_PADDING_CFG 0x00006040 +#define PPU_POOLING_PADDING_CFG_RESERVED_0__MASK 0xffff8000 +#define PPU_POOLING_PADDING_CFG_RESERVED_0__SHIFT 15 +static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_0__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_0__MASK; +} +#define PPU_POOLING_PADDING_CFG_PAD_BOTTOM__MASK 0x00007000 +#define PPU_POOLING_PADDING_CFG_PAD_BOTTOM__SHIFT 12 +static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_BOTTOM(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_PAD_BOTTOM__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_BOTTOM__MASK; +} +#define PPU_POOLING_PADDING_CFG_RESERVED_1__MASK 0x00000800 +#define PPU_POOLING_PADDING_CFG_RESERVED_1__SHIFT 11 +static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_1__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_1__MASK; +} +#define PPU_POOLING_PADDING_CFG_PAD_RIGHT__MASK 0x00000700 +#define PPU_POOLING_PADDING_CFG_PAD_RIGHT__SHIFT 8 +static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_RIGHT(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_PAD_RIGHT__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_RIGHT__MASK; +} +#define PPU_POOLING_PADDING_CFG_RESERVED_2__MASK 0x00000080 +#define PPU_POOLING_PADDING_CFG_RESERVED_2__SHIFT 7 +static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_2(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_2__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_2__MASK; +} +#define PPU_POOLING_PADDING_CFG_PAD_TOP__MASK 0x00000070 +#define PPU_POOLING_PADDING_CFG_PAD_TOP__SHIFT 4 +static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_TOP(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_PAD_TOP__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_TOP__MASK; +} +#define PPU_POOLING_PADDING_CFG_RESERVED_3__MASK 0x00000008 +#define PPU_POOLING_PADDING_CFG_RESERVED_3__SHIFT 3 +static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_3(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_3__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_3__MASK; +} +#define PPU_POOLING_PADDING_CFG_PAD_LEFT__MASK 0x00000007 +#define PPU_POOLING_PADDING_CFG_PAD_LEFT__SHIFT 0 +static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_LEFT(uint32_t val) +{ + return ((val) << PPU_POOLING_PADDING_CFG_PAD_LEFT__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_LEFT__MASK; +} + +#define REG_PPU_PADDING_VALUE_1_CFG 0x00006044 +#define PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__MASK 0xffffffff +#define PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__SHIFT 0 +static inline uint32_t PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0(uint32_t val) +{ + return ((val) << PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__SHIFT) & PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__MASK; +} + +#define REG_PPU_PADDING_VALUE_2_CFG 0x00006048 +#define PPU_PADDING_VALUE_2_CFG_RESERVED_0__MASK 0xfffffff8 +#define PPU_PADDING_VALUE_2_CFG_RESERVED_0__SHIFT 3 +static inline uint32_t PPU_PADDING_VALUE_2_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_PADDING_VALUE_2_CFG_RESERVED_0__SHIFT) & PPU_PADDING_VALUE_2_CFG_RESERVED_0__MASK; +} +#define PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__MASK 0x00000007 +#define PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__SHIFT 0 +static inline uint32_t PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1(uint32_t val) +{ + return ((val) << PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__SHIFT) & PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__MASK; +} + +#define REG_PPU_DST_BASE_ADDR 0x00006070 +#define PPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK 0xfffffff0 +#define PPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT 4 +static inline uint32_t PPU_DST_BASE_ADDR_DST_BASE_ADDR(uint32_t val) +{ + return ((val) << PPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT) & PPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK; +} +#define PPU_DST_BASE_ADDR_RESERVED_0__MASK 0x0000000f +#define PPU_DST_BASE_ADDR_RESERVED_0__SHIFT 0 +static inline uint32_t PPU_DST_BASE_ADDR_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DST_BASE_ADDR_RESERVED_0__SHIFT) & PPU_DST_BASE_ADDR_RESERVED_0__MASK; +} + +#define REG_PPU_DST_SURF_STRIDE 0x0000607c +#define PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK 0xfffffff0 +#define PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT 4 +static inline uint32_t PPU_DST_SURF_STRIDE_DST_SURF_STRIDE(uint32_t val) +{ + return ((val) << PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT) & PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK; +} +#define PPU_DST_SURF_STRIDE_RESERVED_0__MASK 0x0000000f +#define PPU_DST_SURF_STRIDE_RESERVED_0__SHIFT 0 +static inline uint32_t PPU_DST_SURF_STRIDE_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_DST_SURF_STRIDE_RESERVED_0__SHIFT) & PPU_DST_SURF_STRIDE_RESERVED_0__MASK; +} + +#define REG_PPU_DATA_FORMAT 0x00006084 +#define PPU_DATA_FORMAT_INDEX_ADD__MASK 0xfffffff0 +#define PPU_DATA_FORMAT_INDEX_ADD__SHIFT 4 +static inline uint32_t PPU_DATA_FORMAT_INDEX_ADD(uint32_t val) +{ + return ((val) << PPU_DATA_FORMAT_INDEX_ADD__SHIFT) & PPU_DATA_FORMAT_INDEX_ADD__MASK; +} +#define PPU_DATA_FORMAT_DPU_FLYIN__MASK 0x00000008 +#define PPU_DATA_FORMAT_DPU_FLYIN__SHIFT 3 +static inline uint32_t PPU_DATA_FORMAT_DPU_FLYIN(uint32_t val) +{ + return ((val) << PPU_DATA_FORMAT_DPU_FLYIN__SHIFT) & PPU_DATA_FORMAT_DPU_FLYIN__MASK; +} +#define PPU_DATA_FORMAT_PROC_PRECISION__MASK 0x00000007 +#define PPU_DATA_FORMAT_PROC_PRECISION__SHIFT 0 +static inline uint32_t PPU_DATA_FORMAT_PROC_PRECISION(uint32_t val) +{ + return ((val) << PPU_DATA_FORMAT_PROC_PRECISION__SHIFT) & PPU_DATA_FORMAT_PROC_PRECISION__MASK; +} + +#define REG_PPU_MISC_CTRL 0x000060dc +#define PPU_MISC_CTRL_SURF_LEN__MASK 0xffff0000 +#define PPU_MISC_CTRL_SURF_LEN__SHIFT 16 +static inline uint32_t PPU_MISC_CTRL_SURF_LEN(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_SURF_LEN__SHIFT) & PPU_MISC_CTRL_SURF_LEN__MASK; +} +#define PPU_MISC_CTRL_RESERVED_0__MASK 0x0000fe00 +#define PPU_MISC_CTRL_RESERVED_0__SHIFT 9 +static inline uint32_t PPU_MISC_CTRL_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_RESERVED_0__SHIFT) & PPU_MISC_CTRL_RESERVED_0__MASK; +} +#define PPU_MISC_CTRL_MC_SURF_OUT__MASK 0x00000100 +#define PPU_MISC_CTRL_MC_SURF_OUT__SHIFT 8 +static inline uint32_t PPU_MISC_CTRL_MC_SURF_OUT(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_MC_SURF_OUT__SHIFT) & PPU_MISC_CTRL_MC_SURF_OUT__MASK; +} +#define PPU_MISC_CTRL_NONALIGN__MASK 0x00000080 +#define PPU_MISC_CTRL_NONALIGN__SHIFT 7 +static inline uint32_t PPU_MISC_CTRL_NONALIGN(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_NONALIGN__SHIFT) & PPU_MISC_CTRL_NONALIGN__MASK; +} +#define PPU_MISC_CTRL_RESERVED_1__MASK 0x00000070 +#define PPU_MISC_CTRL_RESERVED_1__SHIFT 4 +static inline uint32_t PPU_MISC_CTRL_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_RESERVED_1__SHIFT) & PPU_MISC_CTRL_RESERVED_1__MASK; +} +#define PPU_MISC_CTRL_BURST_LEN__MASK 0x0000000f +#define PPU_MISC_CTRL_BURST_LEN__SHIFT 0 +static inline uint32_t PPU_MISC_CTRL_BURST_LEN(uint32_t val) +{ + return ((val) << PPU_MISC_CTRL_BURST_LEN__SHIFT) & PPU_MISC_CTRL_BURST_LEN__MASK; +} + +#define REG_PPU_RDMA_RDMA_S_STATUS 0x00007000 +#define PPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK 0xfffc0000 +#define PPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT 18 +static inline uint32_t PPU_RDMA_RDMA_S_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK 0x00030000 +#define PPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT 16 +static inline uint32_t PPU_RDMA_RDMA_S_STATUS_STATUS_1(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT) & PPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK; +} +#define PPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK 0x0000fffc +#define PPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT 2 +static inline uint32_t PPU_RDMA_RDMA_S_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT) & PPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK; +} +#define PPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK 0x00000003 +#define PPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_S_STATUS_STATUS_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT) & PPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK; +} + +#define REG_PPU_RDMA_RDMA_S_POINTER 0x00007004 +#define PPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK 0xfffe0000 +#define PPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT 17 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK 0x00010000 +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT 16 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK 0x0000ffc0 +#define PPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT 6 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_RESERVED_1(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT) & PPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020 +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010 +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008 +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT 3 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004 +#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT 2 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK 0x00000002 +#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT 1 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK; +} +#define PPU_RDMA_RDMA_S_POINTER_POINTER__MASK 0x00000001 +#define PPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER__MASK; +} + +#define REG_PPU_RDMA_RDMA_OPERATION_ENABLE 0x00007008 +#define PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe +#define PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT 1 +static inline uint32_t PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK 0x00000001 +#define PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT) & PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK; +} + +#define REG_PPU_RDMA_RDMA_CUBE_IN_WIDTH 0x0000700c +#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__MASK 0xffffe000 +#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK 0x00001fff +#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK; +} + +#define REG_PPU_RDMA_RDMA_CUBE_IN_HEIGHT 0x00007010 +#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__MASK 0xffffe000 +#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK 0x00001fff +#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK; +} + +#define REG_PPU_RDMA_RDMA_CUBE_IN_CHANNEL 0x00007014 +#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__MASK 0xffffe000 +#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT 13 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK 0x00001fff +#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK; +} + +#define REG_PPU_RDMA_RDMA_SRC_BASE_ADDR 0x0000701c +#define PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK 0xffffffff +#define PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT) & PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK; +} + +#define REG_PPU_RDMA_RDMA_SRC_LINE_STRIDE 0x00007024 +#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__MASK 0xfffffff0 +#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__SHIFT 4 +static inline uint32_t PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__SHIFT) & PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__MASK; +} +#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__MASK 0x0000000f +#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__MASK; +} + +#define REG_PPU_RDMA_RDMA_SRC_SURF_STRIDE 0x00007028 +#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__MASK 0xfffffff0 +#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__SHIFT 4 +static inline uint32_t PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__SHIFT) & PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__MASK; +} +#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__MASK 0x0000000f +#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__MASK; +} + +#define REG_PPU_RDMA_RDMA_DATA_FORMAT 0x00007030 +#define PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__MASK 0xfffffffc +#define PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__SHIFT 2 +static inline uint32_t PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__MASK; +} +#define PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__MASK 0x00000003 +#define PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__SHIFT 0 +static inline uint32_t PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION(uint32_t val) +{ + return ((val) << PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__SHIFT) & PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__MASK; +} + +#define REG_DDMA_CFG_OUTSTANDING 0x00008000 +#define DDMA_CFG_OUTSTANDING_RESERVED_0__MASK 0xffff0000 +#define DDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT 16 +static inline uint32_t DDMA_CFG_OUTSTANDING_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT) & DDMA_CFG_OUTSTANDING_RESERVED_0__MASK; +} +#define DDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK 0x0000ff00 +#define DDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT 8 +static inline uint32_t DDMA_CFG_OUTSTANDING_WR_OS_CNT(uint32_t val) +{ + return ((val) << DDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT) & DDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK; +} +#define DDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK 0x000000ff +#define DDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT 0 +static inline uint32_t DDMA_CFG_OUTSTANDING_RD_OS_CNT(uint32_t val) +{ + return ((val) << DDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT) & DDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK; +} + +#define REG_DDMA_RD_WEIGHT_0 0x00008004 +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK 0xff000000 +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT 24 +static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK; +} +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK 0x00ff0000 +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT 16 +static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK; +} +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK 0x0000ff00 +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT 8 +static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK; +} +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK 0x000000ff +#define DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT 0 +static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK; +} + +#define REG_DDMA_WR_WEIGHT_0 0x00008008 +#define DDMA_WR_WEIGHT_0_RESERVED_0__MASK 0xffff0000 +#define DDMA_WR_WEIGHT_0_RESERVED_0__SHIFT 16 +static inline uint32_t DDMA_WR_WEIGHT_0_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_WR_WEIGHT_0_RESERVED_0__SHIFT) & DDMA_WR_WEIGHT_0_RESERVED_0__MASK; +} +#define DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK 0x0000ff00 +#define DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT 8 +static inline uint32_t DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP(uint32_t val) +{ + return ((val) << DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT) & DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK; +} +#define DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK 0x000000ff +#define DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT 0 +static inline uint32_t DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU(uint32_t val) +{ + return ((val) << DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT) & DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK; +} + +#define REG_DDMA_CFG_ID_ERROR 0x0000800c +#define DDMA_CFG_ID_ERROR_RESERVED_0__MASK 0xfffffc00 +#define DDMA_CFG_ID_ERROR_RESERVED_0__SHIFT 10 +static inline uint32_t DDMA_CFG_ID_ERROR_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_ID_ERROR_RESERVED_0__SHIFT) & DDMA_CFG_ID_ERROR_RESERVED_0__MASK; +} +#define DDMA_CFG_ID_ERROR_WR_RESP_ID__MASK 0x000003c0 +#define DDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT 6 +static inline uint32_t DDMA_CFG_ID_ERROR_WR_RESP_ID(uint32_t val) +{ + return ((val) << DDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT) & DDMA_CFG_ID_ERROR_WR_RESP_ID__MASK; +} +#define DDMA_CFG_ID_ERROR_RESERVED_1__MASK 0x00000020 +#define DDMA_CFG_ID_ERROR_RESERVED_1__SHIFT 5 +static inline uint32_t DDMA_CFG_ID_ERROR_RESERVED_1(uint32_t val) +{ + return ((val) << DDMA_CFG_ID_ERROR_RESERVED_1__SHIFT) & DDMA_CFG_ID_ERROR_RESERVED_1__MASK; +} +#define DDMA_CFG_ID_ERROR_RD_RESP_ID__MASK 0x0000001f +#define DDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT 0 +static inline uint32_t DDMA_CFG_ID_ERROR_RD_RESP_ID(uint32_t val) +{ + return ((val) << DDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT) & DDMA_CFG_ID_ERROR_RD_RESP_ID__MASK; +} + +#define REG_DDMA_RD_WEIGHT_1 0x00008010 +#define DDMA_RD_WEIGHT_1_RESERVED_0__MASK 0xffffff00 +#define DDMA_RD_WEIGHT_1_RESERVED_0__SHIFT 8 +static inline uint32_t DDMA_RD_WEIGHT_1_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_1_RESERVED_0__SHIFT) & DDMA_RD_WEIGHT_1_RESERVED_0__MASK; +} +#define DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK 0x000000ff +#define DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT 0 +static inline uint32_t DDMA_RD_WEIGHT_1_RD_WEIGHT_PC(uint32_t val) +{ + return ((val) << DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT) & DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK; +} + +#define REG_DDMA_CFG_DMA_FIFO_CLR 0x00008014 +#define DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK 0xfffffffe +#define DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT 1 +static inline uint32_t DDMA_CFG_DMA_FIFO_CLR_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT) & DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK; +} +#define DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK 0x00000001 +#define DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT) & DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK; +} + +#define REG_DDMA_CFG_DMA_ARB 0x00008018 +#define DDMA_CFG_DMA_ARB_RESERVED_0__MASK 0xfffffc00 +#define DDMA_CFG_DMA_ARB_RESERVED_0__SHIFT 10 +static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_RESERVED_0__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_0__MASK; +} +#define DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK 0x00000200 +#define DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT 9 +static inline uint32_t DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT) & DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK; +} +#define DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK 0x00000100 +#define DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT 8 +static inline uint32_t DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT) & DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK; +} +#define DDMA_CFG_DMA_ARB_RESERVED_1__MASK 0x00000080 +#define DDMA_CFG_DMA_ARB_RESERVED_1__SHIFT 7 +static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_1(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_RESERVED_1__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_1__MASK; +} +#define DDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK 0x00000070 +#define DDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT 4 +static inline uint32_t DDMA_CFG_DMA_ARB_WR_FIX_ARB(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT) & DDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK; +} +#define DDMA_CFG_DMA_ARB_RESERVED_2__MASK 0x00000008 +#define DDMA_CFG_DMA_ARB_RESERVED_2__SHIFT 3 +static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_2(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_RESERVED_2__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_2__MASK; +} +#define DDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK 0x00000007 +#define DDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_ARB_RD_FIX_ARB(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT) & DDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK; +} + +#define REG_DDMA_CFG_DMA_RD_QOS 0x00008020 +#define DDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK 0xfffffc00 +#define DDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT 10 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT) & DDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK; +} +#define DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK 0x00000300 +#define DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT 8 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_PC_QOS(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK; +} +#define DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK 0x000000c0 +#define DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT 6 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK; +} +#define DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK 0x00000030 +#define DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT 4 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK; +} +#define DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK 0x0000000c +#define DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT 2 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK; +} +#define DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK 0x00000003 +#define DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK; +} + +#define REG_DDMA_CFG_DMA_RD_CFG 0x00008024 +#define DDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK 0xffffe000 +#define DDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT 13 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT) & DDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK; +} +#define DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK 0x00001000 +#define DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT 12 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARLOCK(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK; +} +#define DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK 0x00000f00 +#define DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT 8 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARCACHE(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK; +} +#define DDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK 0x000000e0 +#define DDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT 5 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARPROT(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK; +} +#define DDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK 0x00000018 +#define DDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT 3 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARBURST(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK; +} +#define DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK 0x00000007 +#define DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARSIZE(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK; +} + +#define REG_DDMA_CFG_DMA_WR_CFG 0x00008028 +#define DDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK 0xffffe000 +#define DDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT 13 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT) & DDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK; +} +#define DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK 0x00001000 +#define DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT 12 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWLOCK(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK; +} +#define DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK 0x00000f00 +#define DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT 8 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWCACHE(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK; +} +#define DDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK 0x000000e0 +#define DDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT 5 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWPROT(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK; +} +#define DDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK 0x00000018 +#define DDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT 3 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWBURST(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK; +} +#define DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK 0x00000007 +#define DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWSIZE(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK; +} + +#define REG_DDMA_CFG_DMA_WSTRB 0x0000802c +#define DDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK 0xffffffff +#define DDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT 0 +static inline uint32_t DDMA_CFG_DMA_WSTRB_WR_WSTRB(uint32_t val) +{ + return ((val) << DDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT) & DDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK; +} + +#define REG_DDMA_CFG_STATUS 0x00008030 +#define DDMA_CFG_STATUS_RESERVED_0__MASK 0xfffffe00 +#define DDMA_CFG_STATUS_RESERVED_0__SHIFT 9 +static inline uint32_t DDMA_CFG_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << DDMA_CFG_STATUS_RESERVED_0__SHIFT) & DDMA_CFG_STATUS_RESERVED_0__MASK; +} +#define DDMA_CFG_STATUS_IDEL__MASK 0x00000100 +#define DDMA_CFG_STATUS_IDEL__SHIFT 8 +static inline uint32_t DDMA_CFG_STATUS_IDEL(uint32_t val) +{ + return ((val) << DDMA_CFG_STATUS_IDEL__SHIFT) & DDMA_CFG_STATUS_IDEL__MASK; +} +#define DDMA_CFG_STATUS_RESERVED_1__MASK 0x000000ff +#define DDMA_CFG_STATUS_RESERVED_1__SHIFT 0 +static inline uint32_t DDMA_CFG_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << DDMA_CFG_STATUS_RESERVED_1__SHIFT) & DDMA_CFG_STATUS_RESERVED_1__MASK; +} + +#define REG_SDMA_CFG_OUTSTANDING 0x00009000 +#define SDMA_CFG_OUTSTANDING_RESERVED_0__MASK 0xffff0000 +#define SDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT 16 +static inline uint32_t SDMA_CFG_OUTSTANDING_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT) & SDMA_CFG_OUTSTANDING_RESERVED_0__MASK; +} +#define SDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK 0x0000ff00 +#define SDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT 8 +static inline uint32_t SDMA_CFG_OUTSTANDING_WR_OS_CNT(uint32_t val) +{ + return ((val) << SDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT) & SDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK; +} +#define SDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK 0x000000ff +#define SDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT 0 +static inline uint32_t SDMA_CFG_OUTSTANDING_RD_OS_CNT(uint32_t val) +{ + return ((val) << SDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT) & SDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK; +} + +#define REG_SDMA_RD_WEIGHT_0 0x00009004 +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK 0xff000000 +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT 24 +static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK; +} +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK 0x00ff0000 +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT 16 +static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK; +} +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK 0x0000ff00 +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT 8 +static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK; +} +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK 0x000000ff +#define SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT 0 +static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK; +} + +#define REG_SDMA_WR_WEIGHT_0 0x00009008 +#define SDMA_WR_WEIGHT_0_RESERVED_0__MASK 0xffff0000 +#define SDMA_WR_WEIGHT_0_RESERVED_0__SHIFT 16 +static inline uint32_t SDMA_WR_WEIGHT_0_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_WR_WEIGHT_0_RESERVED_0__SHIFT) & SDMA_WR_WEIGHT_0_RESERVED_0__MASK; +} +#define SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK 0x0000ff00 +#define SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT 8 +static inline uint32_t SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP(uint32_t val) +{ + return ((val) << SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT) & SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK; +} +#define SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK 0x000000ff +#define SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT 0 +static inline uint32_t SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU(uint32_t val) +{ + return ((val) << SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT) & SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK; +} + +#define REG_SDMA_CFG_ID_ERROR 0x0000900c +#define SDMA_CFG_ID_ERROR_RESERVED_0__MASK 0xfffffc00 +#define SDMA_CFG_ID_ERROR_RESERVED_0__SHIFT 10 +static inline uint32_t SDMA_CFG_ID_ERROR_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_ID_ERROR_RESERVED_0__SHIFT) & SDMA_CFG_ID_ERROR_RESERVED_0__MASK; +} +#define SDMA_CFG_ID_ERROR_WR_RESP_ID__MASK 0x000003c0 +#define SDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT 6 +static inline uint32_t SDMA_CFG_ID_ERROR_WR_RESP_ID(uint32_t val) +{ + return ((val) << SDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT) & SDMA_CFG_ID_ERROR_WR_RESP_ID__MASK; +} +#define SDMA_CFG_ID_ERROR_RESERVED_1__MASK 0x00000020 +#define SDMA_CFG_ID_ERROR_RESERVED_1__SHIFT 5 +static inline uint32_t SDMA_CFG_ID_ERROR_RESERVED_1(uint32_t val) +{ + return ((val) << SDMA_CFG_ID_ERROR_RESERVED_1__SHIFT) & SDMA_CFG_ID_ERROR_RESERVED_1__MASK; +} +#define SDMA_CFG_ID_ERROR_RD_RESP_ID__MASK 0x0000001f +#define SDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT 0 +static inline uint32_t SDMA_CFG_ID_ERROR_RD_RESP_ID(uint32_t val) +{ + return ((val) << SDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT) & SDMA_CFG_ID_ERROR_RD_RESP_ID__MASK; +} + +#define REG_SDMA_RD_WEIGHT_1 0x00009010 +#define SDMA_RD_WEIGHT_1_RESERVED_0__MASK 0xffffff00 +#define SDMA_RD_WEIGHT_1_RESERVED_0__SHIFT 8 +static inline uint32_t SDMA_RD_WEIGHT_1_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_1_RESERVED_0__SHIFT) & SDMA_RD_WEIGHT_1_RESERVED_0__MASK; +} +#define SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK 0x000000ff +#define SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT 0 +static inline uint32_t SDMA_RD_WEIGHT_1_RD_WEIGHT_PC(uint32_t val) +{ + return ((val) << SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT) & SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK; +} + +#define REG_SDMA_CFG_DMA_FIFO_CLR 0x00009014 +#define SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK 0xfffffffe +#define SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT 1 +static inline uint32_t SDMA_CFG_DMA_FIFO_CLR_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT) & SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK; +} +#define SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK 0x00000001 +#define SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT) & SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK; +} + +#define REG_SDMA_CFG_DMA_ARB 0x00009018 +#define SDMA_CFG_DMA_ARB_RESERVED_0__MASK 0xfffffc00 +#define SDMA_CFG_DMA_ARB_RESERVED_0__SHIFT 10 +static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_RESERVED_0__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_0__MASK; +} +#define SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK 0x00000200 +#define SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT 9 +static inline uint32_t SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT) & SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK; +} +#define SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK 0x00000100 +#define SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT 8 +static inline uint32_t SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT) & SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK; +} +#define SDMA_CFG_DMA_ARB_RESERVED_1__MASK 0x00000080 +#define SDMA_CFG_DMA_ARB_RESERVED_1__SHIFT 7 +static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_1(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_RESERVED_1__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_1__MASK; +} +#define SDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK 0x00000070 +#define SDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT 4 +static inline uint32_t SDMA_CFG_DMA_ARB_WR_FIX_ARB(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT) & SDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK; +} +#define SDMA_CFG_DMA_ARB_RESERVED_2__MASK 0x00000008 +#define SDMA_CFG_DMA_ARB_RESERVED_2__SHIFT 3 +static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_2(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_RESERVED_2__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_2__MASK; +} +#define SDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK 0x00000007 +#define SDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_ARB_RD_FIX_ARB(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT) & SDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK; +} + +#define REG_SDMA_CFG_DMA_RD_QOS 0x00009020 +#define SDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK 0xfffffc00 +#define SDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT 10 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT) & SDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK; +} +#define SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK 0x00000300 +#define SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT 8 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_PC_QOS(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK; +} +#define SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK 0x000000c0 +#define SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT 6 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK; +} +#define SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK 0x00000030 +#define SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT 4 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK; +} +#define SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK 0x0000000c +#define SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT 2 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK; +} +#define SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK 0x00000003 +#define SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK; +} + +#define REG_SDMA_CFG_DMA_RD_CFG 0x00009024 +#define SDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK 0xffffe000 +#define SDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT 13 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT) & SDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK; +} +#define SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK 0x00001000 +#define SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT 12 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARLOCK(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK; +} +#define SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK 0x00000f00 +#define SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT 8 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARCACHE(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK; +} +#define SDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK 0x000000e0 +#define SDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT 5 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARPROT(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK; +} +#define SDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK 0x00000018 +#define SDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT 3 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARBURST(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK; +} +#define SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK 0x00000007 +#define SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARSIZE(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK; +} + +#define REG_SDMA_CFG_DMA_WR_CFG 0x00009028 +#define SDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK 0xffffe000 +#define SDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT 13 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT) & SDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK; +} +#define SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK 0x00001000 +#define SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT 12 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWLOCK(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK; +} +#define SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK 0x00000f00 +#define SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT 8 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWCACHE(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK; +} +#define SDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK 0x000000e0 +#define SDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT 5 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWPROT(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK; +} +#define SDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK 0x00000018 +#define SDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT 3 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWBURST(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK; +} +#define SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK 0x00000007 +#define SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWSIZE(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK; +} + +#define REG_SDMA_CFG_DMA_WSTRB 0x0000902c +#define SDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK 0xffffffff +#define SDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT 0 +static inline uint32_t SDMA_CFG_DMA_WSTRB_WR_WSTRB(uint32_t val) +{ + return ((val) << SDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT) & SDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK; +} + +#define REG_SDMA_CFG_STATUS 0x00009030 +#define SDMA_CFG_STATUS_RESERVED_0__MASK 0xfffffe00 +#define SDMA_CFG_STATUS_RESERVED_0__SHIFT 9 +static inline uint32_t SDMA_CFG_STATUS_RESERVED_0(uint32_t val) +{ + return ((val) << SDMA_CFG_STATUS_RESERVED_0__SHIFT) & SDMA_CFG_STATUS_RESERVED_0__MASK; +} +#define SDMA_CFG_STATUS_IDEL__MASK 0x00000100 +#define SDMA_CFG_STATUS_IDEL__SHIFT 8 +static inline uint32_t SDMA_CFG_STATUS_IDEL(uint32_t val) +{ + return ((val) << SDMA_CFG_STATUS_IDEL__SHIFT) & SDMA_CFG_STATUS_IDEL__MASK; +} +#define SDMA_CFG_STATUS_RESERVED_1__MASK 0x000000ff +#define SDMA_CFG_STATUS_RESERVED_1__SHIFT 0 +static inline uint32_t SDMA_CFG_STATUS_RESERVED_1(uint32_t val) +{ + return ((val) << SDMA_CFG_STATUS_RESERVED_1__SHIFT) & SDMA_CFG_STATUS_RESERVED_1__MASK; +} + +#define REG_GLOBAL_OPERATION_ENABLE 0x0000f008 +#define GLOBAL_OPERATION_ENABLE_RESERVED_0__MASK 0xffffff80 +#define GLOBAL_OPERATION_ENABLE_RESERVED_0__SHIFT 7 +static inline uint32_t GLOBAL_OPERATION_ENABLE_RESERVED_0(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_RESERVED_0__SHIFT) & GLOBAL_OPERATION_ENABLE_RESERVED_0__MASK; +} +#define GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__MASK 0x00000040 +#define GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__SHIFT 6 +static inline uint32_t GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__MASK; +} +#define GLOBAL_OPERATION_ENABLE_PPU_OP_EN__MASK 0x00000020 +#define GLOBAL_OPERATION_ENABLE_PPU_OP_EN__SHIFT 5 +static inline uint32_t GLOBAL_OPERATION_ENABLE_PPU_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_PPU_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_PPU_OP_EN__MASK; +} +#define GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__MASK 0x00000010 +#define GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__SHIFT 4 +static inline uint32_t GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__MASK; +} +#define GLOBAL_OPERATION_ENABLE_DPU_OP_EN__MASK 0x00000008 +#define GLOBAL_OPERATION_ENABLE_DPU_OP_EN__SHIFT 3 +static inline uint32_t GLOBAL_OPERATION_ENABLE_DPU_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_DPU_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_DPU_OP_EN__MASK; +} +#define GLOBAL_OPERATION_ENABLE_CORE_OP_EN__MASK 0x00000004 +#define GLOBAL_OPERATION_ENABLE_CORE_OP_EN__SHIFT 2 +static inline uint32_t GLOBAL_OPERATION_ENABLE_CORE_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_CORE_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_CORE_OP_EN__MASK; +} +#define GLOBAL_OPERATION_ENABLE_RESERVED_1__MASK 0x00000002 +#define GLOBAL_OPERATION_ENABLE_RESERVED_1__SHIFT 1 +static inline uint32_t GLOBAL_OPERATION_ENABLE_RESERVED_1(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_RESERVED_1__SHIFT) & GLOBAL_OPERATION_ENABLE_RESERVED_1__MASK; +} +#define GLOBAL_OPERATION_ENABLE_CNA_OP_EN__MASK 0x00000001 +#define GLOBAL_OPERATION_ENABLE_CNA_OP_EN__SHIFT 0 +static inline uint32_t GLOBAL_OPERATION_ENABLE_CNA_OP_EN(uint32_t val) +{ + return ((val) << GLOBAL_OPERATION_ENABLE_CNA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_CNA_OP_EN__MASK; +} + +#endif /* __ROCKET_REGISTERS_XML__ */ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 7bc40c2735ac..b594780a57d7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -394,6 +394,7 @@ config ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD config ACPI_DEBUG bool "Debug Statements" + default y help The ACPI subsystem can produce debug output. Saying Y enables this output and increases the kernel size by around 50K. diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 7cf6101cb4c7..2a99f5eb6962 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -275,7 +275,7 @@ static inline int acpi_processor_hotadd_init(struct acpi_processor *pr, static int acpi_processor_get_info(struct acpi_device *device) { - union acpi_object object = { 0 }; + union acpi_object object = { .processor = { 0 } }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); int device_declaration = 0; diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 825c2a8acea4..91d7d90c47da 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -233,7 +233,7 @@ static ssize_t time_show(struct device *dev, struct device_attribute *attr, if (ret) return ret; - return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", + return sysfs_emit(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, rt.tz, rt.daylight); } @@ -428,7 +428,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, { struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); - return sprintf(buf, "0x%02X\n", dd->capabilities); + return sysfs_emit(buf, "0x%02X\n", dd->capabilities); } static DEVICE_ATTR_RO(caps); diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index c8f37f4e6626..fef6fb29ece4 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -483,6 +483,13 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread, return_ACPI_STATUS(AE_NULL_OBJECT); } + if (this_walk_state->num_operands < obj_desc->method.param_count) { + ACPI_ERROR((AE_INFO, "Missing argument for method [%4.4s]", + acpi_ut_get_node_name(method_node))); + + return_ACPI_STATUS(AE_AML_UNINITIALIZED_ARG); + } + /* Init for new method, possibly wait on method mutex */ status = diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c index d34497f3576a..36934d4f26fb 100644 --- a/drivers/acpi/acpica/extrace.c +++ b/drivers/acpi/acpica/extrace.c @@ -136,9 +136,9 @@ acpi_ex_trace_point(acpi_trace_event_type type, if (pathname) { ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, - "%s %s [0x%p:%s] execution.\n", + "%s %s [%s] execution.\n", acpi_ex_get_trace_event_name(type), - begin ? "Begin" : "End", aml, pathname)); + begin ? "Begin" : "End", pathname)); } else { ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, "%s %s [0x%p] execution.\n", diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index cd2766c69d78..77c10a7a7a9f 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -131,7 +131,7 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus) int apei_osc_setup(void); -int einj_get_available_error_type(u32 *type); +int einj_get_available_error_type(u32 *type, int einj_action); int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 param4); int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2, diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 9b041415a9d0..2561b045acc7 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -33,6 +33,8 @@ #define SLEEP_UNIT_MAX 5000 /* 5ms */ /* Firmware should respond within 1 seconds */ #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC) +#define COMPONENT_LEN 16 +#define ACPI65_EINJV2_SUPP BIT(30) #define ACPI5_VENDOR_BIT BIT(31) #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ ACPI_EINJ_MEMORY_UNCORRECTABLE | \ @@ -49,6 +51,28 @@ */ static int acpi5; +struct syndrome_array { + union { + u8 acpi_id[COMPONENT_LEN]; + u8 device_id[COMPONENT_LEN]; + u8 pcie_sbdf[COMPONENT_LEN]; + u8 vendor_id[COMPONENT_LEN]; + } comp_id; + union { + u8 proc_synd[COMPONENT_LEN]; + u8 mem_synd[COMPONENT_LEN]; + u8 pcie_synd[COMPONENT_LEN]; + u8 vendor_synd[COMPONENT_LEN]; + } comp_synd; +}; + +struct einjv2_extension_struct { + u32 length; + u16 revision; + u16 component_arr_count; + struct syndrome_array component_arr[] __counted_by(component_arr_count); +}; + struct set_error_type_with_address { u32 type; u32 vendor_extension; @@ -57,11 +81,13 @@ struct set_error_type_with_address { u64 memory_address; u64 memory_address_range; u32 pcie_sbdf; + struct einjv2_extension_struct einjv2_struct; }; enum { SETWA_FLAGS_APICID = 1, SETWA_FLAGS_MEM = 2, SETWA_FLAGS_PCIE_SBDF = 4, + SETWA_FLAGS_EINJV2 = 8, }; /* @@ -83,7 +109,10 @@ static struct debugfs_blob_wrapper vendor_blob; static struct debugfs_blob_wrapper vendor_errors; static char vendor_dev[64]; +static u32 max_nr_components; static u32 available_error_type; +static u32 available_error_type_v2; +static struct syndrome_array *syndrome_data; /* * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the @@ -151,7 +180,9 @@ static DEFINE_MUTEX(einj_mutex); */ bool einj_initialized __ro_after_init; -static void *einj_param; +static void __iomem *einj_param; +static u32 v5param_size; +static bool is_v2; static void einj_exec_ctx_init(struct apei_exec_context *ctx) { @@ -159,13 +190,13 @@ static void einj_exec_ctx_init(struct apei_exec_context *ctx) EINJ_TAB_ENTRY(einj_tab), einj_tab->entries); } -static int __einj_get_available_error_type(u32 *type) +static int __einj_get_available_error_type(u32 *type, int einj_action) { struct apei_exec_context ctx; int rc; einj_exec_ctx_init(&ctx); - rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE); + rc = apei_exec_run(&ctx, einj_action); if (rc) return rc; *type = apei_exec_ctx_get_output(&ctx); @@ -174,17 +205,34 @@ static int __einj_get_available_error_type(u32 *type) } /* Get error injection capabilities of the platform */ -int einj_get_available_error_type(u32 *type) +int einj_get_available_error_type(u32 *type, int einj_action) { int rc; mutex_lock(&einj_mutex); - rc = __einj_get_available_error_type(type); + rc = __einj_get_available_error_type(type, einj_action); mutex_unlock(&einj_mutex); return rc; } +static int einj_get_available_error_types(u32 *type1, u32 *type2) +{ + int rc; + + rc = einj_get_available_error_type(type1, ACPI_EINJ_GET_ERROR_TYPE); + if (rc) + return rc; + if (*type1 & ACPI65_EINJV2_SUPP) { + rc = einj_get_available_error_type(type2, + ACPI_EINJV2_GET_ERROR_TYPE); + if (rc) + return rc; + } + + return 0; +} + static int einj_timedout(u64 *t) { if ((s64)*t < SLEEP_UNIT_MIN) { @@ -216,24 +264,26 @@ static void check_vendor_extension(u64 paddr, struct set_error_type_with_address *v5param) { int offset = v5param->vendor_extension; - struct vendor_error_type_extension *v; + struct vendor_error_type_extension v; + struct vendor_error_type_extension __iomem *p; u32 sbdf; if (!offset) return; - v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); - if (!v) + p = acpi_os_map_iomem(paddr + offset, sizeof(*p)); + if (!p) return; - get_oem_vendor_struct(paddr, offset, v); - sbdf = v->pcie_sbdf; + memcpy_fromio(&v, p, sizeof(v)); + get_oem_vendor_struct(paddr, offset, &v); + sbdf = v.pcie_sbdf; sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sbdf >> 24, (sbdf >> 16) & 0xff, (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, - v->vendor_id, v->device_id, v->rev_id); - acpi_os_unmap_iomem(v, sizeof(*v)); + v.vendor_id, v.device_id, v.rev_id); + acpi_os_unmap_iomem(p, sizeof(v)); } -static void *einj_get_parameter_address(void) +static void __iomem *einj_get_parameter_address(void) { int i; u64 pa_v4 = 0, pa_v5 = 0; @@ -254,26 +304,50 @@ static void *einj_get_parameter_address(void) entry++; } if (pa_v5) { - struct set_error_type_with_address *v5param; + struct set_error_type_with_address v5param; + struct set_error_type_with_address __iomem *p; - v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param)); - if (v5param) { + v5param_size = sizeof(v5param); + p = acpi_os_map_iomem(pa_v5, sizeof(*p)); + if (p) { + int offset, len; + + memcpy_fromio(&v5param, p, v5param_size); acpi5 = 1; - check_vendor_extension(pa_v5, v5param); - return v5param; + check_vendor_extension(pa_v5, &v5param); + if (is_v2 && available_error_type & ACPI65_EINJV2_SUPP) { + len = v5param.einjv2_struct.length; + offset = offsetof(struct einjv2_extension_struct, component_arr); + max_nr_components = (len - offset) / + sizeof(v5param.einjv2_struct.component_arr[0]); + /* + * The first call to acpi_os_map_iomem above does not include the + * component array, instead it is used to read and calculate maximum + * number of components supported by the system. Below, the mapping + * is expanded to include the component array. + */ + acpi_os_unmap_iomem(p, v5param_size); + offset = offsetof(struct set_error_type_with_address, einjv2_struct); + v5param_size = offset + struct_size(&v5param.einjv2_struct, + component_arr, max_nr_components); + p = acpi_os_map_iomem(pa_v5, v5param_size); + } + return p; } } if (param_extension && pa_v4) { - struct einj_parameter *v4param; + struct einj_parameter v4param; + struct einj_parameter __iomem *p; - v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param)); - if (!v4param) + p = acpi_os_map_iomem(pa_v4, sizeof(*p)); + if (!p) return NULL; - if (v4param->reserved1 || v4param->reserved2) { - acpi_os_unmap_iomem(v4param, sizeof(*v4param)); + memcpy_fromio(&v4param, p, sizeof(v4param)); + if (v4param.reserved1 || v4param.reserved2) { + acpi_os_unmap_iomem(p, sizeof(v4param)); return NULL; } - return v4param; + return p; } return NULL; @@ -319,7 +393,8 @@ static struct acpi_generic_address *einj_get_trigger_parameter_region( static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) { - struct acpi_einj_trigger *trigger_tab = NULL; + struct acpi_einj_trigger trigger_tab; + struct acpi_einj_trigger *full_trigger_tab; struct apei_exec_context trigger_ctx; struct apei_resources trigger_resources; struct acpi_whea_header *trigger_entry; @@ -327,54 +402,60 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, u32 table_size; int rc = -EIO; struct acpi_generic_address *trigger_param_region = NULL; + struct acpi_einj_trigger __iomem *p = NULL; - r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), + r = request_mem_region(trigger_paddr, sizeof(trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr + - sizeof(*trigger_tab) - 1); + sizeof(trigger_tab) - 1); goto out; } - trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); - if (!trigger_tab) { + p = ioremap_cache(trigger_paddr, sizeof(*p)); + if (!p) { pr_err("Failed to map trigger table!\n"); goto out_rel_header; } - rc = einj_check_trigger_header(trigger_tab); + memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab)); + rc = einj_check_trigger_header(&trigger_tab); if (rc) { pr_warn(FW_BUG "Invalid trigger error action table.\n"); goto out_rel_header; } /* No action structures in the TRIGGER_ERROR table, nothing to do */ - if (!trigger_tab->entry_count) + if (!trigger_tab.entry_count) goto out_rel_header; rc = -EIO; - table_size = trigger_tab->table_size; - r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), - table_size - sizeof(*trigger_tab), + table_size = trigger_tab.table_size; + full_trigger_tab = kmalloc(table_size, GFP_KERNEL); + if (!full_trigger_tab) + goto out_rel_header; + r = request_mem_region(trigger_paddr + sizeof(trigger_tab), + table_size - sizeof(trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", - (unsigned long long)trigger_paddr + sizeof(*trigger_tab), + (unsigned long long)trigger_paddr + sizeof(trigger_tab), (unsigned long long)trigger_paddr + table_size - 1); - goto out_rel_header; + goto out_free_trigger_tab; } - iounmap(trigger_tab); - trigger_tab = ioremap_cache(trigger_paddr, table_size); - if (!trigger_tab) { + iounmap(p); + p = ioremap_cache(trigger_paddr, table_size); + if (!p) { pr_err("Failed to map trigger table!\n"); goto out_rel_entry; } + memcpy_fromio(full_trigger_tab, p, table_size); trigger_entry = (struct acpi_whea_header *) - ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); + ((char *)full_trigger_tab + sizeof(struct acpi_einj_trigger)); apei_resources_init(&trigger_resources); apei_exec_ctx_init(&trigger_ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), - trigger_entry, trigger_tab->entry_count); + trigger_entry, trigger_tab.entry_count); rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources); if (rc) goto out_fini; @@ -392,7 +473,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, apei_resources_init(&addr_resources); trigger_param_region = einj_get_trigger_parameter_region( - trigger_tab, param1, param2); + full_trigger_tab, param1, param2); if (trigger_param_region) { rc = apei_resources_add(&addr_resources, trigger_param_region->address, @@ -421,23 +502,33 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, out_fini: apei_resources_fini(&trigger_resources); out_rel_entry: - release_mem_region(trigger_paddr + sizeof(*trigger_tab), - table_size - sizeof(*trigger_tab)); + release_mem_region(trigger_paddr + sizeof(trigger_tab), + table_size - sizeof(trigger_tab)); +out_free_trigger_tab: + kfree(full_trigger_tab); out_rel_header: - release_mem_region(trigger_paddr, sizeof(*trigger_tab)); + release_mem_region(trigger_paddr, sizeof(trigger_tab)); out: - if (trigger_tab) - iounmap(trigger_tab); + if (p) + iounmap(p); return rc; } +static bool is_end_of_list(u8 *val) +{ + for (int i = 0; i < COMPONENT_LEN; ++i) { + if (val[i] != 0xFF) + return false; + } + return true; +} static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 param4) { struct apei_exec_context ctx; u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; - int rc; + int i, rc; einj_exec_ctx_init(&ctx); @@ -446,8 +537,13 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, return rc; apei_exec_ctx_set_input(&ctx, type); if (acpi5) { - struct set_error_type_with_address *v5param = einj_param; + struct set_error_type_with_address *v5param; + v5param = kmalloc(v5param_size, GFP_KERNEL); + if (!v5param) + return -ENOMEM; + + memcpy_fromio(v5param, einj_param, v5param_size); v5param->type = type; if (type & ACPI5_VENDOR_BIT) { switch (vendor_flags) { @@ -467,8 +563,21 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, v5param->flags = flags; v5param->memory_address = param1; v5param->memory_address_range = param2; - v5param->apicid = param3; - v5param->pcie_sbdf = param4; + + if (is_v2) { + for (i = 0; i < max_nr_components; i++) { + if (is_end_of_list(syndrome_data[i].comp_id.acpi_id)) + break; + v5param->einjv2_struct.component_arr[i].comp_id = + syndrome_data[i].comp_id; + v5param->einjv2_struct.component_arr[i].comp_synd = + syndrome_data[i].comp_synd; + } + v5param->einjv2_struct.component_arr_count = i; + } else { + v5param->apicid = param3; + v5param->pcie_sbdf = param4; + } } else { switch (type) { case ACPI_EINJ_PROCESSOR_CORRECTABLE: @@ -492,15 +601,19 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, break; } } + memcpy_toio(einj_param, v5param, v5param_size); + kfree(v5param); } else { rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); if (rc) return rc; if (einj_param) { - struct einj_parameter *v4param = einj_param; + struct einj_parameter v4param; - v4param->param1 = param1; - v4param->param2 = param2; + memcpy_fromio(&v4param, einj_param, sizeof(v4param)); + v4param.param1 = param1; + v4param.param2 = param2; + memcpy_toio(einj_param, &v4param, sizeof(v4param)); } } rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); @@ -551,10 +664,15 @@ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 base_addr, size; /* If user manually set "flags", make sure it is legal */ - if (flags && (flags & - ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) + if (flags && (flags & ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM | + 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. @@ -632,6 +750,8 @@ static u64 error_param2; static u64 error_param3; static u64 error_param4; static struct dentry *einj_debug_dir; +static char einj_buf[32]; +static bool einj_v2_enabled; static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(0), "Processor Correctable" }, { BIT(1), "Processor Uncorrectable non-fatal" }, @@ -648,6 +768,12 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(31), "Vendor Defined Error Types" }, }; +static struct { u32 mask; const char *str; } const einjv2_error_type_string[] = { + { BIT(0), "EINJV2 Processor Error" }, + { BIT(1), "EINJV2 Memory Error" }, + { BIT(2), "EINJV2 PCI Express Error" }, +}; + static int available_error_type_show(struct seq_file *m, void *v) { @@ -655,17 +781,22 @@ static int available_error_type_show(struct seq_file *m, void *v) if (available_error_type & einj_error_type_string[pos].mask) seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask, einj_error_type_string[pos].str); - + if ((available_error_type & ACPI65_EINJV2_SUPP) && einj_v2_enabled) { + for (int pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++) { + if (available_error_type_v2 & einjv2_error_type_string[pos].mask) + seq_printf(m, "V2_0x%08x\t%s\n", einjv2_error_type_string[pos].mask, + einjv2_error_type_string[pos].str); + } + } return 0; } DEFINE_SHOW_ATTRIBUTE(available_error_type); -static int error_type_get(void *data, u64 *val) +static ssize_t error_type_get(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { - *val = error_type; - - return 0; + return simple_read_from_buffer(buf, count, ppos, einj_buf, strlen(einj_buf)); } bool einj_is_cxl_error_type(u64 type) @@ -692,15 +823,35 @@ int einj_validate_error_type(u64 type) if (tval & (tval - 1)) return -EINVAL; if (!vendor) - if (!(type & available_error_type)) + if (!(type & (available_error_type | available_error_type_v2))) return -EINVAL; return 0; } -static int error_type_set(void *data, u64 val) +static ssize_t error_type_set(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { int rc; + u64 val; + + /* Leave the last character for the NUL terminator */ + if (count > sizeof(einj_buf) - 1) + return -EINVAL; + + memset(einj_buf, 0, sizeof(einj_buf)); + if (copy_from_user(einj_buf, buf, count)) + return -EFAULT; + + if (strncmp(einj_buf, "V2_", 3) == 0) { + if (!sscanf(einj_buf, "V2_%llx", &val)) + return -EINVAL; + is_v2 = true; + } else { + if (!sscanf(einj_buf, "%llx", &val)) + return -EINVAL; + is_v2 = false; + } rc = einj_validate_error_type(val); if (rc) @@ -708,17 +859,24 @@ static int error_type_set(void *data, u64 val) error_type = val; - return 0; + return count; } -DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set, - "0x%llx\n"); +static const struct file_operations error_type_fops = { + .read = error_type_get, + .write = error_type_set, +}; static int error_inject_set(void *data, u64 val) { if (!error_type) return -EINVAL; + if (is_v2) + error_flags |= SETWA_FLAGS_EINJV2; + else + error_flags &= ~SETWA_FLAGS_EINJV2; + return einj_error_inject(error_type, error_flags, error_param1, error_param2, error_param3, error_param4); } @@ -741,6 +899,98 @@ static int einj_check_table(struct acpi_table_einj *einj_tab) return 0; } +static ssize_t u128_read(struct file *f, char __user *buf, size_t count, loff_t *off) +{ + char output[2 * COMPONENT_LEN + 1]; + u8 *data = f->f_inode->i_private; + int i; + + if (*off >= sizeof(output)) + return 0; + + for (i = 0; i < COMPONENT_LEN; i++) + sprintf(output + 2 * i, "%.02x", data[COMPONENT_LEN - i - 1]); + output[2 * COMPONENT_LEN] = '\n'; + + return simple_read_from_buffer(buf, count, off, output, sizeof(output)); +} + +static ssize_t u128_write(struct file *f, const char __user *buf, size_t count, loff_t *off) +{ + char input[2 + 2 * COMPONENT_LEN + 2]; + u8 *save = f->f_inode->i_private; + u8 tmp[COMPONENT_LEN]; + char byte[3] = {}; + char *s, *e; + ssize_t c; + long val; + int i; + + /* Require that user supply whole input line in one write(2) syscall */ + if (*off) + return -EINVAL; + + c = simple_write_to_buffer(input, sizeof(input), off, buf, count); + if (c < 0) + return c; + + if (c < 1 || input[c - 1] != '\n') + return -EINVAL; + + /* Empty line means invalidate this entry */ + if (c == 1) { + memset(save, 0xff, COMPONENT_LEN); + return c; + } + + if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')) + s = input + 2; + else + s = input; + e = input + c - 1; + + for (i = 0; i < COMPONENT_LEN; i++) { + byte[1] = *--e; + byte[0] = e > s ? *--e : '0'; + if (kstrtol(byte, 16, &val)) + return -EINVAL; + tmp[i] = val; + if (e <= s) + break; + } + while (++i < COMPONENT_LEN) + tmp[i] = 0; + + memcpy(save, tmp, COMPONENT_LEN); + + return c; +} + +static const struct file_operations u128_fops = { + .read = u128_read, + .write = u128_write, +}; + +static bool setup_einjv2_component_files(void) +{ + char name[32]; + + syndrome_data = kcalloc(max_nr_components, sizeof(syndrome_data[0]), GFP_KERNEL); + if (!syndrome_data) + return false; + + for (int i = 0; i < max_nr_components; i++) { + sprintf(name, "component_id%d", i); + debugfs_create_file(name, 0600, einj_debug_dir, + &syndrome_data[i].comp_id, &u128_fops); + sprintf(name, "component_syndrome%d", i); + debugfs_create_file(name, 0600, einj_debug_dir, + &syndrome_data[i].comp_synd, &u128_fops); + } + + return true; +} + static int __init einj_probe(struct faux_device *fdev) { int rc; @@ -764,7 +1014,7 @@ static int __init einj_probe(struct faux_device *fdev) goto err_put_table; } - rc = einj_get_available_error_type(&available_error_type); + rc = einj_get_available_error_types(&available_error_type, &available_error_type_v2); if (rc) goto err_put_table; @@ -812,6 +1062,8 @@ static int __init einj_probe(struct faux_device *fdev) &error_param4); debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, einj_debug_dir, ¬rigger); + if (available_error_type & ACPI65_EINJV2_SUPP) + einj_v2_enabled = setup_einjv2_component_files(); } if (vendor_dev[0]) { @@ -842,13 +1094,13 @@ static int __init einj_probe(struct faux_device *fdev) return rc; } -static void __exit einj_remove(struct faux_device *fdev) +static void einj_remove(struct faux_device *fdev) { struct apei_exec_context ctx; if (einj_param) { acpi_size size = (acpi5) ? - sizeof(struct set_error_type_with_address) : + v5param_size : sizeof(struct einj_parameter); acpi_os_unmap_iomem(einj_param, size); @@ -860,19 +1112,14 @@ static void __exit einj_remove(struct faux_device *fdev) apei_resources_release(&einj_resources); apei_resources_fini(&einj_resources); debugfs_remove_recursive(einj_debug_dir); + kfree(syndrome_data); acpi_put_table((struct acpi_table_header *)einj_tab); } static struct faux_device *einj_dev; -/* - * einj_remove() lives in .exit.text. For drivers registered via - * platform_driver_probe() this is ok because they cannot get unbound at - * runtime. So mark the driver struct with __refdata to prevent modpost - * triggering a section mismatch warning. - */ -static struct faux_device_ops einj_device_ops __refdata = { +static struct faux_device_ops einj_device_ops = { .probe = einj_probe, - .remove = __exit_p(einj_remove), + .remove = einj_remove, }; static int __init einj_init(void) diff --git a/drivers/acpi/apei/einj-cxl.c b/drivers/acpi/apei/einj-cxl.c index 78da9ae543a2..e70a416ec925 100644 --- a/drivers/acpi/apei/einj-cxl.c +++ b/drivers/acpi/apei/einj-cxl.c @@ -30,7 +30,7 @@ int einj_cxl_available_error_type_show(struct seq_file *m, void *v) int cxl_err, rc; u32 available_error_type = 0; - rc = einj_get_available_error_type(&available_error_type); + rc = einj_get_available_error_type(&available_error_type, ACPI_EINJ_GET_ERROR_TYPE); if (rc) return rc; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index f0584ccad451..a0d54993edb3 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -464,28 +464,41 @@ static void ghes_clear_estatus(struct ghes *ghes, ghes_ack_error(ghes->generic_v2); } -/* - * Called as task_work before returning to user-space. - * Ensure any queued work has been done before we return to the context that - * triggered the notification. +/** + * struct ghes_task_work - for synchronous RAS event + * + * @twork: callback_head for task work + * @pfn: page frame number of corrupted page + * @flags: work control flags + * + * Structure to pass task work to be handled before + * returning to user-space via task_work_add(). */ -static void ghes_kick_task_work(struct callback_head *head) +struct ghes_task_work { + struct callback_head twork; + u64 pfn; + int flags; +}; + +static void memory_failure_cb(struct callback_head *twork) { - struct acpi_hest_generic_status *estatus; - struct ghes_estatus_node *estatus_node; - u32 node_len; + struct ghes_task_work *twcb = container_of(twork, struct ghes_task_work, twork); + int ret; - estatus_node = container_of(head, struct ghes_estatus_node, task_work); - if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) - memory_failure_queue_kick(estatus_node->task_work_cpu); + ret = memory_failure(twcb->pfn, twcb->flags); + gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); - estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); - gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len); + if (!ret || ret == -EHWPOISON || ret == -EOPNOTSUPP) + return; + + pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\n", + twcb->pfn, current->comm, task_pid_nr(current)); + force_sig(SIGBUS); } static bool ghes_do_memory_failure(u64 physical_addr, int flags) { + struct ghes_task_work *twcb; unsigned long pfn; if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) @@ -499,6 +512,18 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags) return false; } + if (flags == MF_ACTION_REQUIRED && current->mm) { + twcb = (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); + if (!twcb) + return false; + + twcb->pfn = pfn; + twcb->flags = flags; + init_task_work(&twcb->twork, memory_failure_cb); + task_work_add(current, &twcb->twork, TWA_RESUME); + return true; + } + memory_failure_queue(pfn, flags); return true; } @@ -842,7 +867,7 @@ int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) } EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); -static bool ghes_do_proc(struct ghes *ghes, +static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { int sev, sec_sev; @@ -902,7 +927,16 @@ static bool ghes_do_proc(struct ghes *ghes, } } - return queued; + /* + * If no memory failure work is queued for abnormal synchronous + * errors, do a force kill. + */ + if (sync && !queued) { + dev_err(ghes->dev, + HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", + current->comm, task_pid_nr(current)); + force_sig(SIGBUS); + } } static void __ghes_print_estatus(const char *pfx, @@ -1088,6 +1122,8 @@ static void __ghes_panic(struct ghes *ghes, __ghes_print_estatus(KERN_EMERG, ghes->generic, estatus); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); + ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx); if (!panic_timeout) @@ -1206,9 +1242,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; struct acpi_hest_generic_status *estatus; - bool task_work_pending; u32 len, node_len; - int ret; llnode = llist_del_all(&ghes_estatus_llist); /* @@ -1223,25 +1257,16 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) estatus = GHES_ESTATUS_FROM_NODE(estatus_node); len = cper_estatus_len(estatus); node_len = GHES_ESTATUS_NODE_LEN(len); - task_work_pending = ghes_do_proc(estatus_node->ghes, estatus); + + ghes_do_proc(estatus_node->ghes, estatus); + if (!ghes_estatus_cached(estatus)) { generic = estatus_node->generic; if (ghes_print_estatus(NULL, generic, estatus)) ghes_estatus_cache_add(generic, estatus); } - - if (task_work_pending && current->mm) { - estatus_node->task_work.func = ghes_kick_task_work; - estatus_node->task_work_cpu = smp_processor_id(); - ret = task_work_add(current, &estatus_node->task_work, - TWA_RESUME); - if (ret) - estatus_node->task_work.func = NULL; - } - - if (!estatus_node->task_work.func) - gen_pool_free(ghes_estatus_pool, - (unsigned long)estatus_node, node_len); + gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, + node_len); llnode = next; } @@ -1302,7 +1327,6 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, estatus_node->ghes = ghes; estatus_node->generic = ghes->generic; - estatus_node->task_work.func = NULL; estatus = GHES_ESTATUS_FROM_NODE(estatus_node); if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 98759d6199d3..65f0f56ad753 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -937,8 +937,10 @@ static u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start, new_sids = krealloc_array(sids, count + new_count, sizeof(*new_sids), GFP_KERNEL); - if (!new_sids) + if (!new_sids) { + kfree(sids); return NULL; + } for (i = count; i < total_count; i++) new_sids[i] = id_start++; diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 45593612a4db..6905b56bf3e4 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -243,23 +243,10 @@ static int acpi_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_POWER_NOW: - if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) { + if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) ret = -ENODEV; - break; - } - - val->intval = battery->rate_now * 1000; - /* - * When discharging, the current should be reported as a - * negative number as per the power supply class interface - * definition. - */ - if (psp == POWER_SUPPLY_PROP_CURRENT_NOW && - (battery->state & ACPI_BATTERY_STATE_DISCHARGING) && - acpi_battery_handle_discharging(battery) - == POWER_SUPPLY_STATUS_DISCHARGING) - val->intval = -val->intval; - + else + val->intval = battery->rate_now * 1000; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c index 35ece8e9f15d..0fdd581ef96f 100644 --- a/drivers/acpi/bgrt.c +++ b/drivers/acpi/bgrt.c @@ -47,7 +47,7 @@ static const struct bin_attribute *const bgrt_bin_attributes[] = { static const struct attribute_group bgrt_attribute_group = { .attrs = bgrt_attributes, - .bin_attrs_new = bgrt_bin_attributes, + .bin_attrs = bgrt_bin_attributes, }; int __init acpi_parse_bgrt(struct acpi_table_header *table) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c2ab2783303f..a984ccd4a2a0 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1406,7 +1406,7 @@ static int __init acpi_bus_init(void) goto error1; /* - * Register the for all standard device notifications. + * Register for all standard device notifications. */ status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index dbd4446025ec..4e0583274b8f 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -1119,6 +1119,8 @@ int acpi_subsys_prepare(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); + dev_pm_set_strict_midlayer(dev, true); + if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) { int ret = dev->driver->pm->prepare(dev); @@ -1147,6 +1149,8 @@ void acpi_subsys_complete(struct device *dev) */ if (pm_runtime_suspended(dev) && pm_resume_via_firmware()) pm_request_resume(dev); + + dev_pm_set_strict_midlayer(dev, false); } EXPORT_SYMBOL_GPL(acpi_subsys_complete); @@ -1362,6 +1366,8 @@ static int acpi_subsys_poweroff_noirq(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ +static void acpi_dev_pm_detach(struct device *dev, bool power_off); + static struct dev_pm_domain acpi_general_pm_domain = { .ops = { .runtime_suspend = acpi_subsys_runtime_suspend, @@ -1382,6 +1388,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .restore_early = acpi_subsys_restore_early, #endif }, + .detach = acpi_dev_pm_detach, }; /** @@ -1465,7 +1472,6 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) acpi_device_wakeup_disable(adev); } - dev->pm_domain->detach = acpi_dev_pm_detach; return 1; } EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c index e8caf4106ff9..776914f31b9e 100644 --- a/drivers/acpi/dptf/dptf_power.c +++ b/drivers/acpi/dptf/dptf_power.c @@ -238,6 +238,8 @@ static const struct acpi_device_id int3407_device_ids[] = { {"INTC10A5", 0}, {"INTC10D8", 0}, {"INTC10D9", 0}, + {"INTC1100", 0}, + {"INTC1101", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, int3407_device_ids); diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c index aef7aca2161d..a222df059a16 100644 --- a/drivers/acpi/dptf/int340x_thermal.c +++ b/drivers/acpi/dptf/int340x_thermal.c @@ -61,6 +61,13 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = { {"INTC10D7"}, {"INTC10D8"}, {"INTC10D9"}, + {"INTC10FC"}, + {"INTC10FD"}, + {"INTC10FE"}, + {"INTC10FF"}, + {"INTC1100"}, + {"INTC1101"}, + {"INTC1102"}, {""}, }; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 75c7db8b156a..7855bbf752b1 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2033,7 +2033,7 @@ void __init acpi_ec_ecdt_probe(void) goto out; } - if (!strstarts(ecdt_ptr->id, "\\")) { + if (!strlen(ecdt_ptr->id)) { /* * The ECDT table on some MSI notebooks contains invalid data, together * with an empty ID string (""). @@ -2042,9 +2042,13 @@ void __init acpi_ec_ecdt_probe(void) * a "fully qualified reference to the (...) embedded controller device", * so this string always has to start with a backslash. * - * By verifying this we can avoid such faulty ECDT tables in a safe way. + * However some ThinkBook machines have a ECDT table with a valid EC + * description but an invalid ID string ("_SB.PC00.LPCB.EC0"). + * + * Because of this we only check if the ID string is empty in order to + * avoid the obvious cases. */ - pr_err(FW_BUG "Ignoring ECDT due to invalid ID string \"%s\"\n", ecdt_ptr->id); + pr_err(FW_BUG "Ignoring ECDT due to empty ID string\n"); goto out; } diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 15eba1c70e66..8a28a72a7c6a 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -20,6 +20,7 @@ {"INTC106A", }, /* Fan for Lunar Lake generation */ \ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \ {"INTC10D6", }, /* Fan for Panther Lake generation */ \ + {"INTC10FE", }, /* Fan for Wildcat Lake generation */ \ {"PNP0C0B", } /* Generic ACPI fan */ #define ACPI_FPS_NAME_LEN 20 diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c index 22d29ac2447c..c1afb7b5ed3d 100644 --- a/drivers/acpi/fan_attr.c +++ b/drivers/acpi/fan_attr.c @@ -22,9 +22,9 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha int count; if (fps->control == 0xFFFFFFFF || fps->control > 100) - count = scnprintf(buf, PAGE_SIZE, "not-defined:"); + count = sysfs_emit(buf, "not-defined:"); else - count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); + count = sysfs_emit(buf, "%lld:", fps->control); if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) count += sysfs_emit_at(buf, count, "not-defined:"); @@ -59,7 +59,7 @@ static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, if (status) return status; - return sprintf(buf, "%lld\n", fst.speed); + return sysfs_emit(buf, "%lld\n", fst.speed); } static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) @@ -67,7 +67,7 @@ static ssize_t show_fine_grain_control(struct device *dev, struct device_attribu struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); struct acpi_fan *fan = acpi_driver_data(acpi_dev); - return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl); + return sysfs_emit(buf, "%d\n", fan->fif.fine_grain_ctrl); } int acpi_fan_create_attributes(struct acpi_device *device) diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 8ad12ad3aaaf..095502086b41 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -102,7 +102,7 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) break; } if (i == fan->fps_count) { - dev_dbg(&device->dev, "Invalid control value returned\n"); + dev_dbg(&device->dev, "No matching fps control value\n"); return -EINVAL; } diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index 3902759abcba..bce6f6a18426 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -55,10 +55,9 @@ static unsigned long intel_security_flags(struct nvdimm *nvdimm, { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); unsigned long security_flags = 0; - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_get_security_state cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE, .nd_family = NVDIMM_FAMILY_INTEL, @@ -120,10 +119,9 @@ static unsigned long intel_security_flags(struct nvdimm *nvdimm, static int intel_security_freeze(struct nvdimm *nvdimm) { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_freeze_lock cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_FREEZE_LOCK, .nd_family = NVDIMM_FAMILY_INTEL, @@ -153,10 +151,9 @@ static int intel_security_change_key(struct nvdimm *nvdimm, unsigned int cmd = ptype == NVDIMM_MASTER ? NVDIMM_INTEL_SET_MASTER_PASSPHRASE : NVDIMM_INTEL_SET_PASSPHRASE; - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_set_passphrase cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_family = NVDIMM_FAMILY_INTEL, .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2, @@ -195,10 +192,9 @@ static int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data) { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_unlock_unit cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_UNLOCK_UNIT, .nd_family = NVDIMM_FAMILY_INTEL, @@ -234,10 +230,9 @@ static int intel_security_disable(struct nvdimm *nvdimm, { int rc; struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_disable_passphrase cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE, .nd_family = NVDIMM_FAMILY_INTEL, @@ -277,10 +272,9 @@ static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm, struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); unsigned int cmd = ptype == NVDIMM_MASTER ? NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE; - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_secure_erase cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_family = NVDIMM_FAMILY_INTEL, .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, @@ -318,10 +312,9 @@ static int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm) { int rc; struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_query_overwrite cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE, .nd_family = NVDIMM_FAMILY_INTEL, @@ -354,10 +347,9 @@ static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm, { int rc; struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_overwrite cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_OVERWRITE, .nd_family = NVDIMM_FAMILY_INTEL, @@ -407,10 +399,9 @@ const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; static int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc, struct nd_intel_bus_fw_activate_businfo *info) { - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_bus_fw_activate_businfo cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, .nd_family = NVDIMM_BUS_FAMILY_INTEL, @@ -518,33 +509,31 @@ static enum nvdimm_fwa_capability intel_bus_fwa_capability( static int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_bus_fw_activate cmd; - } nd_cmd = { - .pkg = { - .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE, - .nd_family = NVDIMM_BUS_FAMILY_INTEL, - .nd_size_in = sizeof(nd_cmd.cmd.iodev_state), - .nd_size_out = - sizeof(struct nd_intel_bus_fw_activate), - .nd_fw_size = - sizeof(struct nd_intel_bus_fw_activate), - }, + ) nd_cmd; + int rc; + + nd_cmd.pkg = (struct nd_cmd_pkg) { + .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE, + .nd_family = NVDIMM_BUS_FAMILY_INTEL, + .nd_size_in = sizeof(nd_cmd.cmd.iodev_state), + .nd_size_out = + sizeof(struct nd_intel_bus_fw_activate), + .nd_fw_size = + sizeof(struct nd_intel_bus_fw_activate), + }; + nd_cmd.cmd = (struct nd_intel_bus_fw_activate) { /* * Even though activate is run from a suspended context, * for safety, still ask platform firmware to force * quiesce devices by default. Let a module * parameter override that policy. */ - .cmd = { - .iodev_state = acpi_desc->fwa_noidle - ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE - : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE, - }, + .iodev_state = acpi_desc->fwa_noidle + ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE + : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE, }; - int rc; - switch (intel_bus_fwa_state(nd_desc)) { case NVDIMM_FWA_ARMED: case NVDIMM_FWA_ARM_OVERFLOW: @@ -582,10 +571,9 @@ const struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops; static int intel_fwa_dimminfo(struct nvdimm *nvdimm, struct nd_intel_fw_activate_dimminfo *info) { - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_fw_activate_dimminfo cmd; - } nd_cmd = { + ) nd_cmd = { .pkg = { .nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, .nd_family = NVDIMM_FAMILY_INTEL, @@ -688,27 +676,24 @@ static int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm) { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc; - struct { - struct nd_cmd_pkg pkg; + TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload, struct nd_intel_fw_activate_arm cmd; - } nd_cmd = { - .pkg = { - .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM, - .nd_family = NVDIMM_FAMILY_INTEL, - .nd_size_in = sizeof(nd_cmd.cmd.activate_arm), - .nd_size_out = - sizeof(struct nd_intel_fw_activate_arm), - .nd_fw_size = - sizeof(struct nd_intel_fw_activate_arm), - }, - .cmd = { - .activate_arm = arm == NVDIMM_FWA_ARM - ? ND_INTEL_DIMM_FWA_ARM - : ND_INTEL_DIMM_FWA_DISARM, - }, - }; + ) nd_cmd; int rc; + nd_cmd.pkg = (struct nd_cmd_pkg) { + .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = sizeof(nd_cmd.cmd.activate_arm), + .nd_size_out = sizeof(struct nd_intel_fw_activate_arm), + .nd_fw_size = sizeof(struct nd_intel_fw_activate_arm), + }; + nd_cmd.cmd = (struct nd_intel_fw_activate_arm) { + .activate_arm = arm == NVDIMM_FWA_ARM ? + ND_INTEL_DIMM_FWA_ARM : + ND_INTEL_DIMM_FWA_DISARM, + }; + switch (intel_fwa_state(nvdimm)) { case NVDIMM_FWA_INVALID: return -ENXIO; diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c index 9d9052258e92..4958301f5417 100644 --- a/drivers/acpi/numa/hmat.c +++ b/drivers/acpi/numa/hmat.c @@ -962,10 +962,10 @@ static int hmat_callback(struct notifier_block *self, unsigned long action, void *arg) { struct memory_target *target; - struct memory_notify *mnb = arg; - int pxm, nid = mnb->status_change_nid; + struct node_notify *nn = arg; + int pxm, nid = nn->nid; - if (nid == NUMA_NO_NODE || action != MEM_ONLINE) + if (action != NODE_ADDED_FIRST_MEMORY) return NOTIFY_OK; pxm = node_to_pxm(nid); @@ -1118,7 +1118,7 @@ static __init int hmat_init(void) hmat_register_targets(); /* Keep the table and structures if the notifier may use them */ - if (hotplug_memory_notifier(hmat_callback, HMAT_CALLBACK_PRI)) + if (hotplug_node_notifier(hmat_callback, HMAT_CALLBACK_PRI)) goto out_put; if (!hmat_set_default_dram_perf()) diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 08e10b6226dc..e4560b33b8ad 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -268,7 +268,7 @@ static int acpi_pci_link_get_current(struct acpi_pci_link *link) link->irq.active = irq; - acpi_handle_debug(handle, "Link at IRQ %d \n", link->irq.active); + acpi_handle_debug(handle, "Link at IRQ %d\n", link->irq.active); end: return result; diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 031d1ba81b86..11b1c2828005 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -127,8 +127,11 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, pfru_dev->rev_id, PFRU_FUNC_QUERY_UPDATE_CAP, NULL, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Query cap failed with no object\n"); return ret; + } if (out_obj->package.count < CAP_NR_IDX || out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER || @@ -141,13 +144,17 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER || out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER || - out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) + out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) { + dev_dbg(pfru_dev->parent_dev, + "Query cap failed with invalid package count/type\n"); goto free_acpi_buffer; + } cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; if (cap_hdr->status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status); + dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n", + cap_hdr->status); goto free_acpi_buffer; } @@ -193,24 +200,32 @@ static int query_buffer(struct pfru_com_buf_info *info, out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF, NULL, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with no object\n"); return ret; + } if (out_obj->package.count < BUF_NR_IDX || out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER || - out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) + out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) { + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with invalid package count/type\n"); goto free_acpi_buffer; + } info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value; info->ext_status = out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value; if (info->status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status); - dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status); + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with Error Status:%d\n", info->status); + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with Error Extended Status:%d\n", info->ext_status); goto free_acpi_buffer; } @@ -295,12 +310,16 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap, m_img_hdr = data + size; type = get_image_type(m_img_hdr, pfru_dev); - if (type < 0) + if (type < 0) { + dev_dbg(pfru_dev->parent_dev, "Invalid image type\n"); return false; + } size = adjust_efi_size(m_img_hdr, size); - if (size < 0) + if (size < 0) { + dev_dbg(pfru_dev->parent_dev, "Invalid image size\n"); return false; + } auth = data + size; size += sizeof(u64) + auth->auth_info.hdr.len; @@ -310,7 +329,7 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap, if (type == PFRU_CODE_INJECT_TYPE) return payload_hdr->rt_ver >= cap->code_rt_version; - return payload_hdr->rt_ver >= cap->drv_rt_version; + return payload_hdr->svn_ver >= cap->drv_svn; } static void print_update_debug_info(struct pfru_updated_result *result, @@ -346,8 +365,11 @@ static int start_update(int action, struct pfru_device *pfru_dev) out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, pfru_dev->rev_id, PFRU_FUNC_START, &in_obj, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Update failed to start with no object\n"); return ret; + } if (out_obj->package.count < UPDATE_NR_IDX || out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER || @@ -355,8 +377,11 @@ static int start_update(int action, struct pfru_device *pfru_dev) out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || - out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) { + dev_dbg(pfru_dev->parent_dev, + "Update failed with invalid package count/type\n"); goto free_acpi_buffer; + } update_result.status = out_obj->package.elements[UPDATE_STATUS_IDX].integer.value; @@ -365,8 +390,10 @@ static int start_update(int action, struct pfru_device *pfru_dev) if (update_result.status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status); - dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", + dev_dbg(pfru_dev->parent_dev, + "Update failed with Error Status:%d\n", update_result.status); + dev_dbg(pfru_dev->parent_dev, + "Update failed with Error Extended Status:%d\n", update_result.ext_status); goto free_acpi_buffer; @@ -450,8 +477,10 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, if (ret) return ret; - if (len > buf_info.buf_size) + if (len > buf_info.buf_size) { + dev_dbg(pfru_dev->parent_dev, "Capsule image size too large\n"); return -EINVAL; + } iov.iov_base = (void __user *)buf; iov.iov_len = len; @@ -460,10 +489,14 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, /* map the communication buffer */ phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); - if (!buf_ptr) + if (!buf_ptr) { + dev_dbg(pfru_dev->parent_dev, "Failed to remap the buffer\n"); return -ENOMEM; + } if (!copy_from_iter_full(buf_ptr, len, &iter)) { + dev_dbg(pfru_dev->parent_dev, + "Failed to copy the data from the user space buffer\n"); ret = -EINVAL; goto unmap; } diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c index e549914a636c..be033bbb126a 100644 --- a/drivers/acpi/prmt.c +++ b/drivers/acpi/prmt.c @@ -85,8 +85,6 @@ static u64 efi_pa_va_lookup(efi_guid_t *guid, u64 pa) } } - pr_warn("Failed to find VA for GUID: %pUL, PA: 0x%llx", guid, pa); - return 0; } @@ -154,13 +152,37 @@ acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end) guid_copy(&th->guid, (guid_t *)handler_info->handler_guid); th->handler_addr = (void *)efi_pa_va_lookup(&th->guid, handler_info->handler_address); + /* + * Print a warning message if handler_addr is zero which is not expected to + * ever happen. + */ + if (unlikely(!th->handler_addr)) + pr_warn("Failed to find VA of handler for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->handler_address); th->static_data_buffer_addr = efi_pa_va_lookup(&th->guid, handler_info->static_data_buffer_address); + /* + * According to the PRM specification, static_data_buffer_address can be zero, + * so avoid printing a warning message in that case. Otherwise, if the + * return value of efi_pa_va_lookup() is zero, print the message. + */ + if (unlikely(!th->static_data_buffer_addr && handler_info->static_data_buffer_address)) + pr_warn("Failed to find VA of static data buffer for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->static_data_buffer_address); th->acpi_param_buffer_addr = efi_pa_va_lookup(&th->guid, handler_info->acpi_param_buffer_address); + /* + * According to the PRM specification, acpi_param_buffer_address can be zero, + * so avoid printing a warning message in that case. Otherwise, if the + * return value of efi_pa_va_lookup() is zero, print the message. + */ + if (unlikely(!th->acpi_param_buffer_addr && handler_info->acpi_param_buffer_address)) + pr_warn("Failed to find VA of acpi param buffer for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->acpi_param_buffer_address); + } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info))); return 0; diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 4322f2da6d10..c08ead07252b 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include -#include +#include #include #include #include @@ -30,17 +30,16 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) if (!dev->wakeup.flags.valid) continue; - seq_printf(seq, "%s\t S%d\t", + seq_printf(seq, "%s\t S%llu\t", dev->pnp.bus_id, - (u32) dev->wakeup.sleep_state); + dev->wakeup.sleep_state); mutex_lock(&dev->physical_node_lock); if (!dev->physical_node_count) { seq_printf(seq, "%c%-8s\n", dev->wakeup.flags.valid ? '*' : ' ', - device_may_wakeup(&dev->dev) ? - "enabled" : "disabled"); + str_enabled_disabled(device_may_wakeup(&dev->dev))); } else { struct device *ldev; list_for_each_entry(entry, &dev->physical_node_list, @@ -55,9 +54,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "%c%-8s %s:%s\n", dev->wakeup.flags.valid ? '*' : ' ', - (device_may_wakeup(&dev->dev) || - device_may_wakeup(ldev)) ? - "enabled" : "disabled", + str_enabled_disabled(device_may_wakeup(ldev) || + device_may_wakeup(&dev->dev)), ldev->bus ? ldev->bus->name : "no-bus", dev_name(ldev)); put_device(ldev); @@ -141,6 +139,5 @@ static const struct proc_ops acpi_system_wakeup_device_proc_ops = { void __init acpi_sleep_proc_init(void) { /* 'wakeup device' [R/W] */ - proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, - acpi_root_dir, &acpi_system_wakeup_device_proc_ops); + proc_create("wakeup", 0644, acpi_root_dir, &acpi_system_wakeup_device_proc_ops); } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 64b8d1e19594..8972446b7162 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -173,6 +173,9 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy) { unsigned int cpu; + if (ignore_ppc == 1) + return; + for_each_cpu(cpu, policy->related_cpus) { struct acpi_processor *pr = per_cpu(processors, cpu); int ret; @@ -193,6 +196,14 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy) if (ret < 0) pr_err("Failed to add freq constraint for CPU%d (%d)\n", cpu, ret); + + if (!pr->performance) + continue; + + ret = acpi_processor_get_platform_limit(pr); + if (ret) + pr_err("Failed to update freq constraint for CPU%d (%d)\n", + cpu, ret); } } diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index d1541a386fbc..f9c2bc1d4a3a 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -235,7 +235,7 @@ static int acpi_processor_throttling_notifier(unsigned long event, void *data) if (pr->throttling_platform_limit > target_state) target_state = pr->throttling_platform_limit; if (target_state >= p_throttling->state_count) { - pr_warn("Exceed the limit of T-state \n"); + pr_warn("Exceed the limit of T-state\n"); target_state = p_throttling->state_count - 1; } p_tstate->target_state = target_state; diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c index 4cdff387deff..42c1a9052470 100644 --- a/drivers/acpi/riscv/cppc.c +++ b/drivers/acpi/riscv/cppc.c @@ -37,10 +37,8 @@ static int __init sbi_cppc_init(void) { if (sbi_spec_version >= sbi_mk_version(2, 0) && sbi_probe_extension(SBI_EXT_CPPC) > 0) { - pr_info("SBI CPPC extension detected\n"); cppc_ext_present = true; } else { - pr_info("SBI CPPC extension NOT detected!!\n"); cppc_ext_present = false; } @@ -121,7 +119,7 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) *val = data.ret.value; - return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; + return data.ret.error; } return -EINVAL; @@ -150,7 +148,7 @@ int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val) smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1); - return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; + return data.ret.error; } return -EINVAL; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index a48ebbf768f9..e596224302f4 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -372,7 +372,7 @@ static int acpi_table_attr_init(struct kobject *tables_obj, } table_attr->attr.size = table_header->length; - table_attr->attr.read_new = acpi_table_show; + table_attr->attr.read = acpi_table_show; table_attr->attr.attr.name = table_attr->filename; table_attr->attr.attr.mode = 0400; @@ -495,7 +495,7 @@ static int acpi_table_data_init(struct acpi_table_header *th) if (!data_attr) return -ENOMEM; sysfs_attr_init(&data_attr->attr.attr); - data_attr->attr.read_new = acpi_data_show; + data_attr->attr.read = acpi_data_show; data_attr->attr.attr.mode = 0400; return acpi_data_objs[i].fn(th, data_attr); } diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index b02bf770aead..ff6dc957bc11 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -42,7 +42,7 @@ void acpi_enable_wakeup_devices(u8 sleep_state) list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, wakeup_list) { if (!dev->wakeup.flags.valid - || sleep_state > (u32) dev->wakeup.sleep_state + || sleep_state > dev->wakeup.sleep_state || !(device_may_wakeup(&dev->dev) || dev->wakeup.prepare_count)) continue; @@ -67,7 +67,7 @@ void acpi_disable_wakeup_devices(u8 sleep_state) list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, wakeup_list) { if (!dev->wakeup.flags.valid - || sleep_state > (u32) dev->wakeup.sleep_state + || sleep_state > dev->wakeup.sleep_state || !(device_may_wakeup(&dev->dev) || dev->wakeup.prepare_count)) continue; diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c index 258440b899a9..6daa6372f980 100644 --- a/drivers/acpi/x86/lpss.c +++ b/drivers/acpi/x86/lpss.c @@ -387,9 +387,6 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, - /* Wildcat Point LPSS devices */ - { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) }, - { } }; diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 71482d639a6d..74e34a07ef72 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -138,7 +138,7 @@ static int amba_read_periphid(struct amba_device *dev) void __iomem *tmp; int i, ret; - ret = dev_pm_domain_attach(&dev->dev, true); + ret = dev_pm_domain_attach(&dev->dev, PD_FLAG_ATTACH_POWER_ON); if (ret) { dev_dbg(&dev->dev, "can't get PM domain: %d\n", ret); goto err_out; @@ -291,7 +291,7 @@ static int amba_probe(struct device *dev) if (ret < 0) break; - ret = dev_pm_domain_attach(dev, true); + ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON); if (ret) break; diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 07aa8ae0a058..5b3b8041f827 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -37,14 +37,15 @@ config ANDROID_BINDER_DEVICES created. Each binder device has its own context manager, and is therefore logically separated from the other devices. -config ANDROID_BINDER_IPC_SELFTEST - bool "Android Binder IPC Driver Selftest" - depends on ANDROID_BINDER_IPC +config ANDROID_BINDER_ALLOC_KUNIT_TEST + tristate "KUnit Tests for Android Binder Alloc" if !KUNIT_ALL_TESTS + depends on ANDROID_BINDER_IPC && KUNIT + default KUNIT_ALL_TESTS help - This feature allows binder selftest to run. + This feature builds the binder alloc KUnit tests. - Binder selftest checks the allocation and free of binder buffers - exhaustively with combinations of various buffer sizes and - alignments. + Each test case runs using a pared-down binder_alloc struct and + test-specific freelist, which allows this KUnit module to be loaded + for testing without interfering with a running system. endmenu diff --git a/drivers/android/Makefile b/drivers/android/Makefile index c9d3d0c99c25..c5d47be0276c 100644 --- a/drivers/android/Makefile +++ b/drivers/android/Makefile @@ -3,4 +3,4 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o -obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o +obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += tests/ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index c463ca4a8fff..312b462e349d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -68,6 +68,8 @@ #include #include +#include + #include #include @@ -1585,11 +1587,10 @@ static struct binder_thread *binder_get_txn_from( { struct binder_thread *from; - spin_lock(&t->lock); + guard(spinlock)(&t->lock); from = t->from; if (from) atomic_inc(&from->tmp_ref); - spin_unlock(&t->lock); return from; } @@ -3144,10 +3145,8 @@ static void binder_transaction(struct binder_proc *proc, } if (!target_node) { binder_txn_error("%d:%d cannot find target node\n", - thread->pid, proc->pid); - /* - * return_error is set above - */ + proc->pid, thread->pid); + /* return_error is set above */ return_error_param = -EINVAL; return_error_line = __LINE__; goto err_dead_binder; @@ -5384,10 +5383,9 @@ static int binder_ioctl_write_read(struct file *filp, unsigned long arg, void __user *ubuf = (void __user *)arg; struct binder_write_read bwr; - if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { - ret = -EFAULT; - goto out; - } + if (copy_from_user(&bwr, ubuf, sizeof(bwr))) + return -EFAULT; + binder_debug(BINDER_DEBUG_READ_WRITE, "%d:%d write %lld at %016llx, read %lld at %016llx\n", proc->pid, thread->pid, @@ -5402,8 +5400,6 @@ static int binder_ioctl_write_read(struct file *filp, unsigned long arg, trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) - ret = -EFAULT; goto out; } } @@ -5417,22 +5413,17 @@ static int binder_ioctl_write_read(struct file *filp, unsigned long arg, if (!binder_worklist_empty_ilocked(&proc->todo)) binder_wakeup_proc_ilocked(proc); binder_inner_proc_unlock(proc); - if (ret < 0) { - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) - ret = -EFAULT; + if (ret < 0) goto out; - } } binder_debug(BINDER_DEBUG_READ_WRITE, "%d:%d wrote %lld of %lld, read return %lld of %lld\n", proc->pid, thread->pid, (u64)bwr.write_consumed, (u64)bwr.write_size, (u64)bwr.read_consumed, (u64)bwr.read_size); - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { - ret = -EFAULT; - goto out; - } out: + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + ret = -EFAULT; return ret; } @@ -5445,32 +5436,28 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, struct binder_node *new_node; kuid_t curr_euid = current_euid(); - mutex_lock(&context->context_mgr_node_lock); + guard(mutex)(&context->context_mgr_node_lock); if (context->binder_context_mgr_node) { pr_err("BINDER_SET_CONTEXT_MGR already set\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } ret = security_binder_set_context_mgr(proc->cred); if (ret < 0) - goto out; + return ret; if (uid_valid(context->binder_context_mgr_uid)) { if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) { pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", from_kuid(&init_user_ns, curr_euid), from_kuid(&init_user_ns, context->binder_context_mgr_uid)); - ret = -EPERM; - goto out; + return -EPERM; } } else { context->binder_context_mgr_uid = curr_euid; } new_node = binder_new_node(proc, fbo); - if (!new_node) { - ret = -ENOMEM; - goto out; - } + if (!new_node) + return -ENOMEM; binder_node_lock(new_node); new_node->local_weak_refs++; new_node->local_strong_refs++; @@ -5479,8 +5466,6 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, context->binder_context_mgr_node = new_node; binder_node_unlock(new_node); binder_put_node(new_node); -out: - mutex_unlock(&context->context_mgr_node_lock); return ret; } @@ -5716,11 +5701,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct binder_thread *thread; void __user *ubuf = (void __user *)arg; - /*pr_info("binder_ioctl: %d:%d %x %lx\n", - proc->pid, current->pid, cmd, arg);*/ - - binder_selftest_alloc(&proc->alloc); - trace_binder_ioctl(cmd, arg); ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); @@ -5956,10 +5936,11 @@ static void binder_vma_close(struct vm_area_struct *vma) binder_alloc_vma_close(&proc->alloc); } -static vm_fault_t binder_vm_fault(struct vm_fault *vmf) +VISIBLE_IF_KUNIT vm_fault_t binder_vm_fault(struct vm_fault *vmf) { return VM_FAULT_SIGBUS; } +EXPORT_SYMBOL_IF_KUNIT(binder_vm_fault); static const struct vm_operations_struct binder_vm_ops = { .open = binder_vma_open, @@ -6128,7 +6109,7 @@ static int binder_release(struct inode *nodp, struct file *filp) debugfs_remove(proc->debugfs_entry); if (proc->binderfs_entry) { - binderfs_remove_file(proc->binderfs_entry); + simple_recursive_removal(proc->binderfs_entry, NULL); proc->binderfs_entry = NULL; } @@ -6322,14 +6303,13 @@ static DECLARE_WORK(binder_deferred_work, binder_deferred_func); static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) { - mutex_lock(&binder_deferred_lock); + guard(mutex)(&binder_deferred_lock); proc->deferred_work |= defer; if (hlist_unhashed(&proc->deferred_work_node)) { hlist_add_head(&proc->deferred_work_node, &binder_deferred_list); schedule_work(&binder_deferred_work); } - mutex_unlock(&binder_deferred_lock); } static void print_binder_transaction_ilocked(struct seq_file *m, @@ -6871,14 +6851,13 @@ static int proc_show(struct seq_file *m, void *unused) struct binder_proc *itr; int pid = (unsigned long)m->private; - mutex_lock(&binder_procs_lock); + guard(mutex)(&binder_procs_lock); hlist_for_each_entry(itr, &binder_procs, proc_node) { if (itr->pid == pid) { seq_puts(m, "binder proc state:\n"); print_binder_proc(m, itr, true, false); } } - mutex_unlock(&binder_procs_lock); return 0; } @@ -6996,16 +6975,14 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = { void binder_add_device(struct binder_device *device) { - spin_lock(&binder_devices_lock); + guard(spinlock)(&binder_devices_lock); hlist_add_head(&device->hlist, &binder_devices); - spin_unlock(&binder_devices_lock); } void binder_remove_device(struct binder_device *device) { - spin_lock(&binder_devices_lock); + guard(spinlock)(&binder_devices_lock); hlist_del_init(&device->hlist); - spin_unlock(&binder_devices_lock); } static int __init init_binder_device(const char *name) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index fcfaf1b899c8..979c96b74cad 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -23,10 +23,11 @@ #include #include #include +#include #include "binder_alloc.h" #include "binder_trace.h" -struct list_lru binder_freelist; +static struct list_lru binder_freelist; static DEFINE_MUTEX(binder_alloc_mmap_lock); @@ -57,13 +58,14 @@ static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer) return list_entry(buffer->entry.prev, struct binder_buffer, entry); } -static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, - struct binder_buffer *buffer) +VISIBLE_IF_KUNIT size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer) { if (list_is_last(&buffer->entry, &alloc->buffers)) return alloc->vm_start + alloc->buffer_size - buffer->user_data; return binder_buffer_next(buffer)->user_data - buffer->user_data; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_buffer_size); static void binder_insert_free_buffer(struct binder_alloc *alloc, struct binder_buffer *new_buffer) @@ -167,12 +169,8 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked( struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, unsigned long user_ptr) { - struct binder_buffer *buffer; - - mutex_lock(&alloc->mutex); - buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); - mutex_unlock(&alloc->mutex); - return buffer; + guard(mutex)(&alloc->mutex); + return binder_alloc_prepare_to_free_locked(alloc, user_ptr); } static inline void @@ -210,7 +208,7 @@ static void binder_lru_freelist_add(struct binder_alloc *alloc, trace_binder_free_lru_start(alloc, index); - ret = list_lru_add(&binder_freelist, + ret = list_lru_add(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -409,7 +407,7 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc, if (page) { trace_binder_alloc_lru_start(alloc, index); - on_lru = list_lru_del(&binder_freelist, + on_lru = list_lru_del(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -699,6 +697,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, out: return buffer; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_new_buf); static unsigned long buffer_start_page(struct binder_buffer *buffer) { @@ -877,6 +876,7 @@ void binder_alloc_free_buf(struct binder_alloc *alloc, binder_free_buf_locked(alloc, buffer); mutex_unlock(&alloc->mutex); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_buf); /** * binder_alloc_mmap_handler() - map virtual address space for proc @@ -959,7 +959,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, failure_string, ret); return ret; } - +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_mmap_handler); void binder_alloc_deferred_release(struct binder_alloc *alloc) { @@ -1007,7 +1007,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) if (!page) continue; - on_lru = list_lru_del(&binder_freelist, + on_lru = list_lru_del(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -1028,6 +1028,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) "%s: %d buffers %d, pages %d\n", __func__, alloc->pid, buffers, page_count); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_deferred_release); /** * binder_alloc_print_allocated() - print buffer info @@ -1043,7 +1044,7 @@ void binder_alloc_print_allocated(struct seq_file *m, struct binder_buffer *buffer; struct rb_node *n; - mutex_lock(&alloc->mutex); + guard(mutex)(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) { buffer = rb_entry(n, struct binder_buffer, rb_node); seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n", @@ -1053,7 +1054,6 @@ void binder_alloc_print_allocated(struct seq_file *m, buffer->extra_buffers_size, buffer->transaction ? "active" : "delivered"); } - mutex_unlock(&alloc->mutex); } /** @@ -1102,10 +1102,9 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) struct rb_node *n; int count = 0; - mutex_lock(&alloc->mutex); + guard(mutex)(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) count++; - mutex_unlock(&alloc->mutex); return count; } @@ -1122,6 +1121,7 @@ void binder_alloc_vma_close(struct binder_alloc *alloc) { binder_alloc_set_mapped(alloc, false); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_vma_close); /** * binder_alloc_free_page() - shrinker callback to free pages @@ -1213,6 +1213,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item, err_mmget: return LRU_SKIP; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_page); static unsigned long binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) @@ -1229,6 +1230,18 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) static struct shrinker *binder_shrinker; +VISIBLE_IF_KUNIT void __binder_alloc_init(struct binder_alloc *alloc, + struct list_lru *freelist) +{ + alloc->pid = current->group_leader->pid; + alloc->mm = current->mm; + mmgrab(alloc->mm); + mutex_init(&alloc->mutex); + INIT_LIST_HEAD(&alloc->buffers); + alloc->freelist = freelist; +} +EXPORT_SYMBOL_IF_KUNIT(__binder_alloc_init); + /** * binder_alloc_init() - called by binder_open() for per-proc initialization * @alloc: binder_alloc for this proc @@ -1238,11 +1251,7 @@ static struct shrinker *binder_shrinker; */ void binder_alloc_init(struct binder_alloc *alloc) { - alloc->pid = current->group_leader->pid; - alloc->mm = current->mm; - mmgrab(alloc->mm); - mutex_init(&alloc->mutex); - INIT_LIST_HEAD(&alloc->buffers); + __binder_alloc_init(alloc, &binder_freelist); } int binder_alloc_shrinker_init(void) diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index feecd7414241..d6f1f6f2d00e 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -15,7 +15,6 @@ #include #include -extern struct list_lru binder_freelist; struct binder_transaction; /** @@ -91,6 +90,7 @@ static inline struct list_head *page_to_lru(struct page *p) * @free_async_space: VA space available for async buffers. This is * initialized at mmap time to 1/2 the full VA space * @pages: array of struct page * + * @freelist: lru list to use for free pages (invariant after init) * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) * @pages_high: high watermark of offset in @pages @@ -113,6 +113,7 @@ struct binder_alloc { struct rb_root allocated_buffers; size_t free_async_space; struct page **pages; + struct list_lru *freelist; size_t buffer_size; int pid; size_t pages_high; @@ -120,11 +121,6 @@ struct binder_alloc { bool oneway_spam_detected; }; -#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST -void binder_selftest_alloc(struct binder_alloc *alloc); -#else -static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} -#endif enum lru_status binder_alloc_free_page(struct list_head *item, struct list_lru_one *lru, void *cb_arg); @@ -160,12 +156,8 @@ void binder_alloc_print_pages(struct seq_file *m, static inline size_t binder_alloc_get_free_async_space(struct binder_alloc *alloc) { - size_t free_async_space; - - mutex_lock(&alloc->mutex); - free_async_space = alloc->free_async_space; - mutex_unlock(&alloc->mutex); - return free_async_space; + guard(mutex)(&alloc->mutex); + return alloc->free_async_space; } unsigned long @@ -187,5 +179,11 @@ int binder_alloc_copy_from_buffer(struct binder_alloc *alloc, binder_size_t buffer_offset, size_t bytes); +#if IS_ENABLED(CONFIG_KUNIT) +void __binder_alloc_init(struct binder_alloc *alloc, struct list_lru *freelist); +size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer); +#endif + #endif /* _LINUX_BINDER_ALLOC_H */ diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c deleted file mode 100644 index c88735c54848..000000000000 --- a/drivers/android/binder_alloc_selftest.c +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* binder_alloc_selftest.c - * - * Android IPC Subsystem - * - * Copyright (C) 2017 Google, Inc. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include "binder_alloc.h" - -#define BUFFER_NUM 5 -#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) - -static bool binder_selftest_run = true; -static int binder_selftest_failures; -static DEFINE_MUTEX(binder_selftest_lock); - -/** - * enum buf_end_align_type - Page alignment of a buffer - * end with regard to the end of the previous buffer. - * - * In the pictures below, buf2 refers to the buffer we - * are aligning. buf1 refers to previous buffer by addr. - * Symbol [ means the start of a buffer, ] means the end - * of a buffer, and | means page boundaries. - */ -enum buf_end_align_type { - /** - * @SAME_PAGE_UNALIGNED: The end of this buffer is on - * the same page as the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 ][ ... - * buf1 ]|[ buf2 ][ ... - */ - SAME_PAGE_UNALIGNED = 0, - /** - * @SAME_PAGE_ALIGNED: When the end of the previous buffer - * is not page aligned, the end of this buffer is on the - * same page as the end of the previous buffer and is page - * aligned. When the previous buffer is page aligned, the - * end of this buffer is aligned to the next page boundary. - * Examples: - * buf1 ][ buf2 ]| ... - * buf1 ]|[ buf2 ]| ... - */ - SAME_PAGE_ALIGNED, - /** - * @NEXT_PAGE_UNALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 ][ ... - */ - NEXT_PAGE_UNALIGNED, - /** - * @NEXT_PAGE_ALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is page aligned. Examples: - * buf1 ][ buf2 | buf2 ]| ... - * buf1 ]|[ buf2 | buf2 ]| ... - */ - NEXT_PAGE_ALIGNED, - /** - * @NEXT_NEXT_UNALIGNED: The end of this buffer is on - * the page that follows the page after the end of the - * previous buffer and is not page aligned. Examples: - * buf1 ][ buf2 | buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 | buf2 ][ ... - */ - NEXT_NEXT_UNALIGNED, - /** - * @LOOP_END: The number of enum values in &buf_end_align_type. - * It is used for controlling loop termination. - */ - LOOP_END, -}; - -static void pr_err_size_seq(size_t *sizes, int *seq) -{ - int i; - - pr_err("alloc sizes: "); - for (i = 0; i < BUFFER_NUM; i++) - pr_cont("[%zu]", sizes[i]); - pr_cont("\n"); - pr_err("free seq: "); - for (i = 0; i < BUFFER_NUM; i++) - pr_cont("[%d]", seq[i]); - pr_cont("\n"); -} - -static bool check_buffer_pages_allocated(struct binder_alloc *alloc, - struct binder_buffer *buffer, - size_t size) -{ - unsigned long page_addr; - unsigned long end; - int page_index; - - end = PAGE_ALIGN(buffer->user_data + size); - page_addr = buffer->user_data; - for (; page_addr < end; page_addr += PAGE_SIZE) { - page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; - if (!alloc->pages[page_index] || - !list_empty(page_to_lru(alloc->pages[page_index]))) { - pr_err("expect alloc but is %s at page index %d\n", - alloc->pages[page_index] ? - "lru" : "free", page_index); - return false; - } - } - return true; -} - -static void binder_selftest_alloc_buf(struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) { - buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); - if (IS_ERR(buffers[i]) || - !check_buffer_pages_allocated(alloc, buffers[i], - sizes[i])) { - pr_err_size_seq(sizes, seq); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_free_buf(struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq, size_t end) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) - binder_alloc_free_buf(alloc, buffers[seq[i]]); - - for (i = 0; i < end / PAGE_SIZE; i++) { - /** - * Error message on a free page can be false positive - * if binder shrinker ran during binder_alloc_free_buf - * calls above. - */ - if (list_empty(page_to_lru(alloc->pages[i]))) { - pr_err_size_seq(sizes, seq); - pr_err("expect lru but is %s at page index %d\n", - alloc->pages[i] ? "alloc" : "free", i); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_free_page(struct binder_alloc *alloc) -{ - int i; - unsigned long count; - - while ((count = list_lru_count(&binder_freelist))) { - list_lru_walk(&binder_freelist, binder_alloc_free_page, - NULL, count); - } - - for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { - if (alloc->pages[i]) { - pr_err("expect free but is %s at page index %d\n", - list_empty(page_to_lru(alloc->pages[i])) ? - "alloc" : "lru", i); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_alloc_free(struct binder_alloc *alloc, - size_t *sizes, int *seq, size_t end) -{ - struct binder_buffer *buffers[BUFFER_NUM]; - - binder_selftest_alloc_buf(alloc, buffers, sizes, seq); - binder_selftest_free_buf(alloc, buffers, sizes, seq, end); - - /* Allocate from lru. */ - binder_selftest_alloc_buf(alloc, buffers, sizes, seq); - if (list_lru_count(&binder_freelist)) - pr_err("lru list should be empty but is not\n"); - - binder_selftest_free_buf(alloc, buffers, sizes, seq, end); - binder_selftest_free_page(alloc); -} - -static bool is_dup(int *seq, int index, int val) -{ - int i; - - for (i = 0; i < index; i++) { - if (seq[i] == val) - return true; - } - return false; -} - -/* Generate BUFFER_NUM factorial free orders. */ -static void binder_selftest_free_seq(struct binder_alloc *alloc, - size_t *sizes, int *seq, - int index, size_t end) -{ - int i; - - if (index == BUFFER_NUM) { - binder_selftest_alloc_free(alloc, sizes, seq, end); - return; - } - for (i = 0; i < BUFFER_NUM; i++) { - if (is_dup(seq, index, i)) - continue; - seq[index] = i; - binder_selftest_free_seq(alloc, sizes, seq, index + 1, end); - } -} - -static void binder_selftest_alloc_size(struct binder_alloc *alloc, - size_t *end_offset) -{ - int i; - int seq[BUFFER_NUM] = {0}; - size_t front_sizes[BUFFER_NUM]; - size_t back_sizes[BUFFER_NUM]; - size_t last_offset, offset = 0; - - for (i = 0; i < BUFFER_NUM; i++) { - last_offset = offset; - offset = end_offset[i]; - front_sizes[i] = offset - last_offset; - back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; - } - /* - * Buffers share the first or last few pages. - * Only BUFFER_NUM - 1 buffer sizes are adjustable since - * we need one giant buffer before getting to the last page. - */ - back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; - binder_selftest_free_seq(alloc, front_sizes, seq, 0, - end_offset[BUFFER_NUM - 1]); - binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size); -} - -static void binder_selftest_alloc_offset(struct binder_alloc *alloc, - size_t *end_offset, int index) -{ - int align; - size_t end, prev; - - if (index == BUFFER_NUM) { - binder_selftest_alloc_size(alloc, end_offset); - return; - } - prev = index == 0 ? 0 : end_offset[index - 1]; - end = prev; - - BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); - - for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { - if (align % 2) - end = ALIGN(end, PAGE_SIZE); - else - end += BUFFER_MIN_SIZE; - end_offset[index] = end; - binder_selftest_alloc_offset(alloc, end_offset, index + 1); - } -} - -/** - * binder_selftest_alloc() - Test alloc and free of buffer pages. - * @alloc: Pointer to alloc struct. - * - * Allocate BUFFER_NUM buffers to cover all page alignment cases, - * then free them in all orders possible. Check that pages are - * correctly allocated, put onto lru when buffers are freed, and - * are freed when binder_alloc_free_page is called. - */ -void binder_selftest_alloc(struct binder_alloc *alloc) -{ - size_t end_offset[BUFFER_NUM]; - - if (!binder_selftest_run) - return; - mutex_lock(&binder_selftest_lock); - if (!binder_selftest_run || !alloc->mapped) - goto done; - pr_info("STARTED\n"); - binder_selftest_alloc_offset(alloc, end_offset, 0); - binder_selftest_run = false; - if (binder_selftest_failures > 0) - pr_info("%d tests FAILED\n", binder_selftest_failures); - else - pr_info("PASSED\n"); - -done: - mutex_unlock(&binder_selftest_lock); -} diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 1ba5caf1d88d..8b08976146ba 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -81,7 +81,6 @@ extern bool is_binderfs_device(const struct inode *inode); extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name, const struct file_operations *fops, void *data); -extern void binderfs_remove_file(struct dentry *dentry); #else static inline bool is_binderfs_device(const struct inode *inode) { @@ -94,7 +93,6 @@ static inline struct dentry *binderfs_create_file(struct dentry *dir, { return NULL; } -static inline void binderfs_remove_file(struct dentry *dentry) {} #endif #ifdef CONFIG_ANDROID_BINDERFS @@ -592,4 +590,8 @@ void binder_add_device(struct binder_device *device); */ void binder_remove_device(struct binder_device *device); +#if IS_ENABLED(CONFIG_KUNIT) +vm_fault_t binder_vm_fault(struct vm_fault *vmf); +#endif + #endif /* _LINUX_BINDER_INTERNAL_H */ diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 16de1b9e72f7..97a78e5623db 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -34,27 +34,6 @@ TRACE_EVENT(binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); -DECLARE_EVENT_CLASS(binder_lock_class, - TP_PROTO(const char *tag), - TP_ARGS(tag), - TP_STRUCT__entry( - __field(const char *, tag) - ), - TP_fast_assign( - __entry->tag = tag; - ), - TP_printk("tag=%s", __entry->tag) -); - -#define DEFINE_BINDER_LOCK_EVENT(name) \ -DEFINE_EVENT(binder_lock_class, name, \ - TP_PROTO(const char *func), \ - TP_ARGS(func)) - -DEFINE_BINDER_LOCK_EVENT(binder_lock); -DEFINE_BINDER_LOCK_EVENT(binder_locked); -DEFINE_BINDER_LOCK_EVENT(binder_unlock); - DECLARE_EVENT_CLASS(binder_function_return_class, TP_PROTO(int ret), TP_ARGS(ret), diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 024275dbfdd8..0d9d95a7fb60 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -117,7 +117,6 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct dentry *dentry, *root; struct binder_device *device; char *name = NULL; - size_t name_len; struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; @@ -161,9 +160,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, inode->i_gid = info->root_gid; req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */ - name_len = strlen(req->name); - /* Make sure to include terminating NUL byte */ - name = kmemdup(req->name, name_len + 1, GFP_KERNEL); + name = kstrdup(req->name, GFP_KERNEL); if (!name) goto err; @@ -500,21 +497,6 @@ static struct dentry *binderfs_create_dentry(struct dentry *parent, return dentry; } -void binderfs_remove_file(struct dentry *dentry) -{ - struct inode *parent_inode; - - parent_inode = d_inode(dentry->d_parent); - inode_lock(parent_inode); - if (simple_positive(dentry)) { - dget(dentry); - simple_unlink(parent_inode, dentry); - d_delete(dentry); - dput(dentry); - } - inode_unlock(parent_inode); -} - struct dentry *binderfs_create_file(struct dentry *parent, const char *name, const struct file_operations *fops, void *data) diff --git a/drivers/android/tests/.kunitconfig b/drivers/android/tests/.kunitconfig new file mode 100644 index 000000000000..39b76bab9d9a --- /dev/null +++ b/drivers/android/tests/.kunitconfig @@ -0,0 +1,7 @@ +# +# Copyright 2025 Google LLC. +# + +CONFIG_KUNIT=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST=y diff --git a/drivers/android/tests/Makefile b/drivers/android/tests/Makefile new file mode 100644 index 000000000000..27268418eb03 --- /dev/null +++ b/drivers/android/tests/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright 2025 Google LLC. +# + +obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += binder_alloc_kunit.o diff --git a/drivers/android/tests/binder_alloc_kunit.c b/drivers/android/tests/binder_alloc_kunit.c new file mode 100644 index 000000000000..9b884d977f76 --- /dev/null +++ b/drivers/android/tests/binder_alloc_kunit.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for binder allocator code. + * + * Copyright 2025 Google LLC. + * Author: Tiffany Yang + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../binder_alloc.h" +#include "../binder_internal.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +#define BINDER_MMAP_SIZE SZ_128K + +#define BUFFER_NUM 5 +#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) + +#define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1) + +#define ALIGN_TYPE_STRLEN (12) + +#define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1) + +#define PRINT_ALL_CASES (0) + +/* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */ +#define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120) + +/** + * enum buf_end_align_type - Page alignment of a buffer + * end with regard to the end of the previous buffer. + * + * In the pictures below, buf2 refers to the buffer we + * are aligning. buf1 refers to previous buffer by addr. + * Symbol [ means the start of a buffer, ] means the end + * of a buffer, and | means page boundaries. + */ +enum buf_end_align_type { + /** + * @SAME_PAGE_UNALIGNED: The end of this buffer is on + * the same page as the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 ][ ... + * buf1 ]|[ buf2 ][ ... + */ + SAME_PAGE_UNALIGNED = 0, + /** + * @SAME_PAGE_ALIGNED: When the end of the previous buffer + * is not page aligned, the end of this buffer is on the + * same page as the end of the previous buffer and is page + * aligned. When the previous buffer is page aligned, the + * end of this buffer is aligned to the next page boundary. + * Examples: + * buf1 ][ buf2 ]| ... + * buf1 ]|[ buf2 ]| ... + */ + SAME_PAGE_ALIGNED, + /** + * @NEXT_PAGE_UNALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 ][ ... + */ + NEXT_PAGE_UNALIGNED, + /** + * @NEXT_PAGE_ALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is page aligned. Examples: + * buf1 ][ buf2 | buf2 ]| ... + * buf1 ]|[ buf2 | buf2 ]| ... + */ + NEXT_PAGE_ALIGNED, + /** + * @NEXT_NEXT_UNALIGNED: The end of this buffer is on + * the page that follows the page after the end of the + * previous buffer and is not page aligned. Examples: + * buf1 ][ buf2 | buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 | buf2 ][ ... + */ + NEXT_NEXT_UNALIGNED, + /** + * @LOOP_END: The number of enum values in &buf_end_align_type. + * It is used for controlling loop termination. + */ + LOOP_END, +}; + +static const char *const buf_end_align_type_strs[LOOP_END] = { + [SAME_PAGE_UNALIGNED] = "SP_UNALIGNED", + [SAME_PAGE_ALIGNED] = " SP_ALIGNED ", + [NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED", + [NEXT_PAGE_ALIGNED] = " NP_ALIGNED ", + [NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED", +}; + +struct binder_alloc_test_case_info { + char alignments[ALIGNMENTS_BUFLEN]; + struct seq_buf alignments_sb; + size_t *buffer_sizes; + int *free_sequence; + bool front_pages; +}; + +static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) + seq_buf_printf(sb, "[%d]", seq[i]); + + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); +} + +static void stringify_alignments(struct kunit *test, int *alignments, + struct seq_buf *sb) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) + seq_buf_printf(sb, "[ %d:%s ]", i, + buf_end_align_type_strs[alignments[i]]); + + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); +} + +static bool check_buffer_pages_allocated(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffer, + size_t size) +{ + unsigned long page_addr; + unsigned long end; + int page_index; + + end = PAGE_ALIGN(buffer->user_data + size); + page_addr = buffer->user_data; + for (; page_addr < end; page_addr += PAGE_SIZE) { + page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; + if (!alloc->pages[page_index] || + !list_empty(page_to_lru(alloc->pages[page_index]))) { + kunit_err(test, "expect alloc but is %s at page index %d\n", + alloc->pages[page_index] ? + "lru" : "free", page_index); + return false; + } + } + return true; +} + +static unsigned long binder_alloc_test_alloc_buf(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq) +{ + unsigned long failures = 0; + int i; + + for (i = 0; i < BUFFER_NUM; i++) { + buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); + if (IS_ERR(buffers[i]) || + !check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i])) + failures++; + } + + return failures; +} + +static unsigned long binder_alloc_test_free_buf(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq, size_t end) +{ + unsigned long failures = 0; + int i; + + for (i = 0; i < BUFFER_NUM; i++) + binder_alloc_free_buf(alloc, buffers[seq[i]]); + + for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) { + if (list_empty(page_to_lru(alloc->pages[i]))) { + kunit_err(test, "expect lru but is %s at page index %d\n", + alloc->pages[i] ? "alloc" : "free", i); + failures++; + } + } + + return failures; +} + +static unsigned long binder_alloc_test_free_page(struct kunit *test, + struct binder_alloc *alloc) +{ + unsigned long failures = 0; + unsigned long count; + int i; + + while ((count = list_lru_count(alloc->freelist))) { + list_lru_walk(alloc->freelist, binder_alloc_free_page, + NULL, count); + } + + for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { + if (alloc->pages[i]) { + kunit_err(test, "expect free but is %s at page index %d\n", + list_empty(page_to_lru(alloc->pages[i])) ? + "alloc" : "lru", i); + failures++; + } + } + + return failures; +} + +/* Executes one full test run for the given test case. */ +static bool binder_alloc_test_alloc_free(struct kunit *test, + struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + size_t end) +{ + unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE; + struct binder_buffer *buffers[BUFFER_NUM]; + unsigned long failures; + bool failed = false; + + failures = binder_alloc_test_alloc_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Initial allocation failed: %lu/%u buffers with errors", + failures, BUFFER_NUM); + + failures = binder_alloc_test_free_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence, end); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Initial buffers not freed correctly: %lu/%lu pages not on lru list", + failures, pages); + + /* Allocate from lru. */ + failures = binder_alloc_test_alloc_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Reallocation failed: %lu/%u buffers with errors", + failures, BUFFER_NUM); + + failures = list_lru_count(alloc->freelist); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "lru list should be empty after reallocation but still has %lu pages", + failures); + + failures = binder_alloc_test_free_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence, end); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Reallocated buffers not freed correctly: %lu/%lu pages not on lru list", + failures, pages); + + failures = binder_alloc_test_free_page(test, alloc); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Failed to clean up allocated pages: %lu/%lu pages still installed", + failures, (alloc->buffer_size / PAGE_SIZE)); + + return failed; +} + +static bool is_dup(int *seq, int index, int val) +{ + int i; + + for (i = 0; i < index; i++) { + if (seq[i] == val) + return true; + } + return false; +} + +/* Generate BUFFER_NUM factorial free orders. */ +static void permute_frees(struct kunit *test, struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + unsigned long *runs, unsigned long *failures, + int index, size_t end) +{ + bool case_failed; + int i; + + if (index == BUFFER_NUM) { + DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN); + + case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end); + *runs += 1; + *failures += case_failed; + + if (case_failed || PRINT_ALL_CASES) { + stringify_free_seq(test, tc->free_sequence, + &freeseq_sb); + kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs, + case_failed ? "FAILED" : "PASSED", + tc->front_pages ? "front" : "back ", + seq_buf_str(&tc->alignments_sb), + seq_buf_str(&freeseq_sb)); + } + + return; + } + for (i = 0; i < BUFFER_NUM; i++) { + if (is_dup(tc->free_sequence, index, i)) + continue; + tc->free_sequence[index] = i; + permute_frees(test, alloc, tc, runs, failures, index + 1, end); + } +} + +static void gen_buf_sizes(struct kunit *test, + struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + size_t *end_offset, unsigned long *runs, + unsigned long *failures) +{ + size_t last_offset, offset = 0; + size_t front_sizes[BUFFER_NUM]; + size_t back_sizes[BUFFER_NUM]; + int seq[BUFFER_NUM] = {0}; + int i; + + tc->free_sequence = seq; + for (i = 0; i < BUFFER_NUM; i++) { + last_offset = offset; + offset = end_offset[i]; + front_sizes[i] = offset - last_offset; + back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; + } + back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; + + /* + * Buffers share the first or last few pages. + * Only BUFFER_NUM - 1 buffer sizes are adjustable since + * we need one giant buffer before getting to the last page. + */ + tc->front_pages = true; + tc->buffer_sizes = front_sizes; + permute_frees(test, alloc, tc, runs, failures, 0, + end_offset[BUFFER_NUM - 1]); + + tc->front_pages = false; + tc->buffer_sizes = back_sizes; + permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size); +} + +static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc, + size_t *end_offset, int *alignments, + unsigned long *runs, unsigned long *failures, + int index) +{ + size_t end, prev; + int align; + + if (index == BUFFER_NUM) { + struct binder_alloc_test_case_info tc = {0}; + + seq_buf_init(&tc.alignments_sb, tc.alignments, + ALIGNMENTS_BUFLEN); + stringify_alignments(test, alignments, &tc.alignments_sb); + + gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures); + return; + } + prev = index == 0 ? 0 : end_offset[index - 1]; + end = prev; + + BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); + + for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { + if (align % 2) + end = ALIGN(end, PAGE_SIZE); + else + end += BUFFER_MIN_SIZE; + end_offset[index] = end; + alignments[index] = align; + gen_buf_offsets(test, alloc, end_offset, alignments, runs, + failures, index + 1); + } +} + +struct binder_alloc_test { + struct binder_alloc alloc; + struct list_lru binder_test_freelist; + struct file *filp; + unsigned long mmap_uaddr; +}; + +static void binder_alloc_test_init_freelist(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + + KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist, + &priv->binder_test_freelist); +} + +static void binder_alloc_test_mmap(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + struct binder_alloc *alloc = &priv->alloc; + struct binder_buffer *buf; + struct rb_node *n; + + KUNIT_EXPECT_EQ(test, alloc->mapped, true); + KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE); + + n = rb_first(&alloc->allocated_buffers); + KUNIT_EXPECT_PTR_EQ(test, n, NULL); + + n = rb_first(&alloc->free_buffers); + buf = rb_entry(n, struct binder_buffer, rb_node); + KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf), + BINDER_MMAP_SIZE); + KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers)); +} + +/** + * binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages. + * @test: The test context object. + * + * Allocate BUFFER_NUM buffers to cover all page alignment cases, + * then free them in all orders possible. Check that pages are + * correctly allocated, put onto lru when buffers are freed, and + * are freed when binder_alloc_free_page() is called. + */ +static void binder_alloc_exhaustive_test(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + size_t end_offset[BUFFER_NUM]; + int alignments[BUFFER_NUM]; + unsigned long failures = 0; + unsigned long runs = 0; + + gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs, + &failures, 0); + + KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES); + KUNIT_EXPECT_EQ(test, failures, 0); +} + +/* ===== End test cases ===== */ + +static void binder_alloc_test_vma_close(struct vm_area_struct *vma) +{ + struct binder_alloc *alloc = vma->vm_private_data; + + binder_alloc_vma_close(alloc); +} + +static const struct vm_operations_struct binder_alloc_test_vm_ops = { + .close = binder_alloc_test_vma_close, + .fault = binder_vm_fault, +}; + +static int binder_alloc_test_mmap_handler(struct file *filp, + struct vm_area_struct *vma) +{ + struct binder_alloc *alloc = filp->private_data; + + vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE); + + vma->vm_ops = &binder_alloc_test_vm_ops; + vma->vm_private_data = alloc; + + return binder_alloc_mmap_handler(alloc, vma); +} + +static const struct file_operations binder_alloc_test_fops = { + .mmap = binder_alloc_test_mmap_handler, +}; + +static int binder_alloc_test_init(struct kunit *test) +{ + struct binder_alloc_test *priv; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + test->priv = priv; + + ret = list_lru_init(&priv->binder_test_freelist); + if (ret) { + kunit_err(test, "Failed to initialize test freelist\n"); + return ret; + } + + /* __binder_alloc_init requires mm to be attached */ + ret = kunit_attach_mm(); + if (ret) { + kunit_err(test, "Failed to attach mm\n"); + return ret; + } + __binder_alloc_init(&priv->alloc, &priv->binder_test_freelist); + + priv->filp = anon_inode_getfile("binder_alloc_kunit", + &binder_alloc_test_fops, &priv->alloc, + O_RDWR | O_CLOEXEC); + if (IS_ERR_OR_NULL(priv->filp)) { + kunit_err(test, "Failed to open binder alloc test driver file\n"); + return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM; + } + + priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE, + PROT_READ, MAP_PRIVATE | MAP_NORESERVE, + 0); + if (!priv->mmap_uaddr) { + kunit_err(test, "Could not map the test's transaction memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void binder_alloc_test_exit(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + + /* Close the backing file to make sure binder_alloc_vma_close runs */ + if (!IS_ERR_OR_NULL(priv->filp)) + fput(priv->filp); + + if (priv->alloc.mm) + binder_alloc_deferred_release(&priv->alloc); + + /* Make sure freelist is empty */ + KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0); + list_lru_destroy(&priv->binder_test_freelist); +} + +static struct kunit_case binder_alloc_test_cases[] = { + KUNIT_CASE(binder_alloc_test_init_freelist), + KUNIT_CASE(binder_alloc_test_mmap), + KUNIT_CASE(binder_alloc_exhaustive_test), + {} +}; + +static struct kunit_suite binder_alloc_test_suite = { + .name = "binder_alloc", + .test_cases = binder_alloc_test_cases, + .init = binder_alloc_test_init, + .exit = binder_alloc_test_exit, +}; + +kunit_test_suite(binder_alloc_test_suite); + +MODULE_AUTHOR("Tiffany Yang "); +MODULE_DESCRIPTION("Binder Alloc KUnit tests"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e00536b49552..120a2b7067fc 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -117,23 +117,39 @@ config SATA_AHCI config SATA_MOBILE_LPM_POLICY int "Default SATA Link Power Management policy" - range 0 4 + range 0 5 default 3 depends on SATA_AHCI help Select the Default SATA Link Power Management (LPM) policy to use for chipsets / "South Bridges" supporting low-power modes. Such chipsets are ubiquitous across laptops, desktops and servers. + Each policy combines power saving states and features: + - Partial: The Phy logic is powered but is in a reduced power + state. The exit latency from this state is no longer than + 10us). + - Slumber: The Phy logic is powered but is in an even lower power + state. The exit latency from this state is potentially + longer, but no longer than 10ms. + - DevSleep: The Phy logic may be powered down. The exit latency from + this state is no longer than 20 ms, unless otherwise + specified by DETO in the device Identify Device Data log. + - HIPM: Host Initiated Power Management (host automatically + transitions to partial and slumber). + - DIPM: Device Initiated Power Management (device automatically + transitions to partial and slumber). - The value set has the following meanings: + The possible values for the default SATA link power management + policies are: 0 => Keep firmware settings - 1 => Maximum performance - 2 => Medium power - 3 => Medium power with Device Initiated PM enabled - 4 => Minimum power + 1 => No power savings (maximum performance) + 2 => HIPM (Partial) + 3 => HIPM (Partial) and DIPM (Partial and Slumber) + 4 => HIPM (Partial and DevSleep) and DIPM (Partial and Slumber) + 5 => HIPM (Slumber and DevSleep) and DIPM (Partial and Slumber) - Note "Minimum power" is known to cause issues, including disk - corruption, with some disks and should not be used. + Excluding the value 0, higher values represent policies with higher + power savings. config SATA_AHCI_PLATFORM tristate "Platform AHCI SATA support" diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 163ac909bd06..7a7f88b3fa2b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -110,17 +110,17 @@ static const struct scsi_host_template ahci_sht = { static struct ata_port_operations ahci_vt8251_ops = { .inherits = &ahci_ops, - .hardreset = ahci_vt8251_hardreset, + .reset.hardreset = ahci_vt8251_hardreset, }; static struct ata_port_operations ahci_p5wdh_ops = { .inherits = &ahci_ops, - .hardreset = ahci_p5wdh_hardreset, + .reset.hardreset = ahci_p5wdh_hardreset, }; static struct ata_port_operations ahci_avn_ops = { .inherits = &ahci_ops, - .hardreset = ahci_avn_hardreset, + .reset.hardreset = ahci_avn_hardreset, }; static const struct ata_port_info ahci_port_info[] = { @@ -674,7 +674,9 @@ MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)"); static int mobile_lpm_policy = -1; module_param(mobile_lpm_policy, int, 0644); -MODULE_PARM_DESC(mobile_lpm_policy, "Default LPM policy for mobile chipsets"); +MODULE_PARM_DESC(mobile_lpm_policy, + "Default LPM policy. Despite its name, this parameter applies " + "to all chipsets, including desktop and server chipsets"); static char *ahci_mask_port_map; module_param_named(mask_port_map, ahci_mask_port_map, charp, 0444); @@ -687,40 +689,50 @@ MODULE_PARM_DESC(mask_port_map, "where is the PCI ID of an AHCI controller in the " "form \"domain:bus:dev.func\""); -static void ahci_apply_port_map_mask(struct device *dev, - struct ahci_host_priv *hpriv, char *mask_s) +static char *ahci_mask_port_ext; +module_param_named(mask_port_ext, ahci_mask_port_ext, charp, 0444); +MODULE_PARM_DESC(mask_port_ext, + "32-bits mask to ignore the external/hotplug capability of ports. " + "Valid values are: " + "\"\" to apply the same mask to all AHCI controller " + "devices, and \"=,=,...\" to " + "specify different masks for the controllers specified, " + "where is the PCI ID of an AHCI controller in the " + "form \"domain:bus:dev.func\""); + +static u32 ahci_port_mask(struct device *dev, char *mask_s) { unsigned int mask; if (kstrtouint(mask_s, 0, &mask)) { dev_err(dev, "Invalid port map mask\n"); - return; + return 0; } - hpriv->mask_port_map = mask; + return mask; } -static void ahci_get_port_map_mask(struct device *dev, - struct ahci_host_priv *hpriv) +static u32 ahci_get_port_mask(struct device *dev, char *mask_p) { char *param, *end, *str, *mask_s; char *name; + u32 mask = 0; - if (!strlen(ahci_mask_port_map)) - return; + if (!mask_p || !strlen(mask_p)) + return 0; - str = kstrdup(ahci_mask_port_map, GFP_KERNEL); + str = kstrdup(mask_p, GFP_KERNEL); if (!str) - return; + return 0; /* Handle single mask case */ if (!strchr(str, '=')) { - ahci_apply_port_map_mask(dev, hpriv, str); + mask = ahci_port_mask(dev, str); goto free; } /* - * Mask list case: parse the parameter to apply the mask only if + * Mask list case: parse the parameter to get the mask only if * the device name matches. */ param = str; @@ -750,11 +762,13 @@ static void ahci_get_port_map_mask(struct device *dev, param++; } - ahci_apply_port_map_mask(dev, hpriv, mask_s); + mask = ahci_port_mask(dev, mask_s); } free: kfree(str); + + return mask; } static void ahci_pci_save_initial_config(struct pci_dev *pdev, @@ -780,8 +794,10 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev, } /* Handle port map masks passed as module parameter. */ - if (ahci_mask_port_map) - ahci_get_port_map_mask(&pdev->dev, hpriv); + hpriv->mask_port_map = + ahci_get_port_mask(&pdev->dev, ahci_mask_port_map); + hpriv->mask_port_ext = + ahci_get_port_mask(&pdev->dev, ahci_mask_port_ext); ahci_save_initial_config(&pdev->dev, hpriv); } @@ -1410,8 +1426,15 @@ static bool ahci_broken_suspend(struct pci_dev *pdev) static bool ahci_broken_lpm(struct pci_dev *pdev) { + /* + * Platforms with LPM problems. + * If driver_data is NULL, there is no existing BIOS version with + * functioning LPM. + * If driver_data is non-NULL, then driver_data contains the DMI BIOS + * build date of the first BIOS version with functioning LPM (i.e. older + * BIOS versions have broken LPM). + */ static const struct dmi_system_id sysids[] = { - /* Various Lenovo 50 series have LPM issues with older BIOSen */ { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), @@ -1438,13 +1461,30 @@ static bool ahci_broken_lpm(struct pci_dev *pdev) DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W541"), }, + .driver_data = "20180409", /* 2.35 */ + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ASUSPRO D840MB_M840SA"), + }, + /* 320 is broken, there is no known good version. */ + }, + { /* - * Note date based on release notes, 2.35 has been - * reported to be good, but I've been unable to get - * a hold of the reporter to get the DMI BIOS date. - * TODO: fix this. + * AMD 500 Series Chipset SATA Controller [1022:43eb] + * on this motherboard timeouts on ports 5 and 6 when + * LPM is enabled, at least with WDC WD20EFAX-68FB5N0 + * hard drives. LPM with the same drive works fine on + * all other ports on the same controller. */ - .driver_data = "20180310", /* 2.35 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, + "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, + "ROG STRIX B550-F GAMING (WI-FI)"), + }, + /* 3621 is broken, there is no known good version. */ }, { } /* terminate list */ }; @@ -1455,6 +1495,9 @@ static bool ahci_broken_lpm(struct pci_dev *pdev) if (!dmi) return false; + if (!dmi->driver_data) + return true; + dmi_get_date(DMI_BIOS_DATE, &year, &month, &date); snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date); @@ -1728,11 +1771,20 @@ static void ahci_mark_external_port(struct ata_port *ap) void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; - /* mark external ports (hotplug-capable, eSATA) */ + /* + * Mark external ports (hotplug-capable, eSATA), unless we were asked to + * ignore this feature. + */ tmp = readl(port_mmio + PORT_CMD); if (((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)) || - (tmp & PORT_CMD_HPCP)) + (tmp & PORT_CMD_HPCP)) { + if (hpriv->mask_port_ext & (1U << ap->port_no)) { + ata_port_info(ap, + "Ignoring external/hotplug capability\n"); + return; + } ap->pflags |= ATA_PFLAG_EXTERNAL; + } } static void ahci_update_initial_lpm_policy(struct ata_port *ap) @@ -1747,15 +1799,26 @@ static void ahci_update_initial_lpm_policy(struct ata_port *ap) * LPM if the port advertises itself as an external port. */ if (ap->pflags & ATA_PFLAG_EXTERNAL) { - ata_port_dbg(ap, "external port, not enabling LPM\n"); + ap->flags |= ATA_FLAG_NO_LPM; + ap->target_lpm_policy = ATA_LPM_MAX_POWER; return; } + /* If no Partial or no Slumber, we cannot support DIPM. */ + if ((ap->host->flags & ATA_HOST_NO_PART) || + (ap->host->flags & ATA_HOST_NO_SSC)) { + ata_port_dbg(ap, "Host does not support DIPM\n"); + ap->flags |= ATA_FLAG_NO_DIPM; + } + /* If no LPM states are supported by the HBA, do not bother with LPM */ if ((ap->host->flags & ATA_HOST_NO_PART) && (ap->host->flags & ATA_HOST_NO_SSC) && (ap->host->flags & ATA_HOST_NO_DEVSLP)) { - ata_port_dbg(ap, "no LPM states supported, not enabling LPM\n"); + ata_port_dbg(ap, + "No LPM states supported, forcing LPM max_power\n"); + ap->flags |= ATA_FLAG_NO_LPM; + ap->target_lpm_policy = ATA_LPM_MAX_POWER; return; } diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 2c10c8f440d1..293b7fb216b5 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -330,6 +330,7 @@ struct ahci_host_priv { /* Input fields */ unsigned int flags; /* AHCI_HFLAG_* */ u32 mask_port_map; /* Mask of valid ports */ + u32 mask_port_ext; /* Mask of ports ext capability */ void __iomem * mmio; /* bus-independent mem map */ u32 cap; /* cap to use */ diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index ca0924dc5bd2..f97566c420f8 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -137,13 +137,13 @@ static int ahci_da850_hardreset(struct ata_link *link, static struct ata_port_operations ahci_da850_port_ops = { .inherits = &ahci_platform_ops, - .softreset = ahci_da850_softreset, + .reset.softreset = ahci_da850_softreset, /* * No need to override .pmp_softreset - it's only used for actual * PMP-enabled ports. */ - .hardreset = ahci_da850_hardreset, - .pmp_hardreset = ahci_da850_hardreset, + .reset.hardreset = ahci_da850_hardreset, + .pmp_reset.hardreset = ahci_da850_hardreset, }; static const struct ata_port_info ahci_da850_port_info = { diff --git a/drivers/ata/ahci_dm816.c b/drivers/ata/ahci_dm816.c index b08547b877a1..93faed2cfeb6 100644 --- a/drivers/ata/ahci_dm816.c +++ b/drivers/ata/ahci_dm816.c @@ -124,7 +124,7 @@ static int ahci_dm816_softreset(struct ata_link *link, static struct ata_port_operations ahci_dm816_port_ops = { .inherits = &ahci_platform_ops, - .softreset = ahci_dm816_softreset, + .reset.softreset = ahci_dm816_softreset, }; static const struct ata_port_info ahci_dm816_port_info = { diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index f01f08048f97..86aedd5923ac 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -642,18 +642,19 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, int ret; if (imxpriv->type == AHCI_IMX53) - ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline); + ret = ahci_pmp_retry_srst_ops.reset.softreset(link, class, + deadline); else - ret = ahci_ops.softreset(link, class, deadline); + ret = ahci_ops.reset.softreset(link, class, deadline); return ret; } static struct ata_port_operations ahci_imx_ops = { - .inherits = &ahci_ops, - .host_stop = ahci_imx_host_stop, - .error_handler = ahci_imx_error_handler, - .softreset = ahci_imx_softreset, + .inherits = &ahci_ops, + .host_stop = ahci_imx_host_stop, + .error_handler = ahci_imx_error_handler, + .reset.softreset = ahci_imx_softreset, }; static const struct ata_port_info ahci_imx_port_info = { diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c index 30e39885b64e..0dec1a17e5b1 100644 --- a/drivers/ata/ahci_qoriq.c +++ b/drivers/ata/ahci_qoriq.c @@ -146,8 +146,8 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class, } static struct ata_port_operations ahci_qoriq_ops = { - .inherits = &ahci_ops, - .hardreset = ahci_qoriq_hardreset, + .inherits = &ahci_ops, + .reset.hardreset = ahci_qoriq_hardreset, }; static const struct ata_port_info ahci_qoriq_port_info = { diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index dfbd8c53abcb..6b8844646fcd 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -450,7 +450,6 @@ static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, { int pmp = sata_srst_pmp(link); struct ata_port *ap = link->ap; - u32 rc; void __iomem *port_mmio = ahci_port_base(ap); u32 port_fbs; @@ -463,9 +462,7 @@ static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, port_fbs |= pmp << PORT_FBS_DEV_OFFSET; writel(port_fbs, port_mmio + PORT_FBS); - rc = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); - - return rc; + return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); } /** @@ -500,7 +497,7 @@ static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class, u32 port_fbs; u32 port_fbs_save; u32 retry = 1; - u32 rc; + int rc; port_fbs_save = readl(port_mmio + PORT_FBS); @@ -613,11 +610,11 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance) static struct ata_port_operations xgene_ahci_v1_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, - .hardreset = xgene_ahci_hardreset, + .reset.hardreset = xgene_ahci_hardreset, + .reset.softreset = xgene_ahci_softreset, + .pmp_reset.softreset = xgene_ahci_pmp_softreset, .read_id = xgene_ahci_read_id, .qc_issue = xgene_ahci_qc_issue, - .softreset = xgene_ahci_softreset, - .pmp_softreset = xgene_ahci_pmp_softreset }; static const struct ata_port_info xgene_ahci_v1_port_info = { @@ -630,7 +627,7 @@ static const struct ata_port_info xgene_ahci_v1_port_info = { static struct ata_port_operations xgene_ahci_v2_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, - .hardreset = xgene_ahci_hardreset, + .reset.hardreset = xgene_ahci_hardreset, .read_id = xgene_ahci_read_id, }; diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index d441246fa357..495fa096dd65 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1074,7 +1074,7 @@ static struct ata_port_operations piix_pata_ops = { .cable_detect = ata_cable_40wire, .set_piomode = piix_set_piomode, .set_dmamode = piix_set_dmamode, - .prereset = piix_pata_prereset, + .reset.prereset = piix_pata_prereset, }; static struct ata_port_operations piix_vmw_ops = { @@ -1089,6 +1089,7 @@ static struct ata_port_operations ich_pata_ops = { }; static struct attribute *piix_sidpr_shost_attrs[] = { + &dev_attr_link_power_management_supported.attr, &dev_attr_link_power_management_policy.attr, NULL }; @@ -1102,7 +1103,7 @@ static const struct scsi_host_template piix_sidpr_sht = { static struct ata_port_operations piix_sidpr_sata_ops = { .inherits = &piix_sata_ops, - .hardreset = sata_std_hardreset, + .reset.hardreset = sata_std_hardreset, .scr_read = piix_sidpr_scr_read, .scr_write = piix_sidpr_scr_write, .set_lpm = piix_sidpr_set_lpm, diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 4e9c82f36df1..c79abdfcd7a9 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -111,6 +111,7 @@ static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO, static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL); static struct attribute *ahci_shost_attrs[] = { + &dev_attr_link_power_management_supported.attr, &dev_attr_link_power_management_policy.attr, &dev_attr_em_message_type.attr, &dev_attr_em_message.attr, @@ -162,10 +163,10 @@ struct ata_port_operations ahci_ops = { .freeze = ahci_freeze, .thaw = ahci_thaw, - .softreset = ahci_softreset, - .hardreset = ahci_hardreset, - .postreset = ahci_postreset, - .pmp_softreset = ahci_softreset, + .reset.softreset = ahci_softreset, + .reset.hardreset = ahci_hardreset, + .reset.postreset = ahci_postreset, + .pmp_reset.softreset = ahci_softreset, .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, .dev_config = ahci_dev_config, @@ -192,7 +193,7 @@ EXPORT_SYMBOL_GPL(ahci_ops); struct ata_port_operations ahci_pmp_retry_srst_ops = { .inherits = &ahci_ops, - .softreset = ahci_pmp_retry_softreset, + .reset.softreset = ahci_pmp_retry_softreset, }; EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops); diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index b7f0bf795521..f2140fc06ba0 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -514,15 +514,19 @@ unsigned int ata_acpi_gtm_xfermask(struct ata_device *dev, EXPORT_SYMBOL_GPL(ata_acpi_gtm_xfermask); /** - * ata_acpi_cbl_80wire - Check for 80 wire cable + * ata_acpi_cbl_pata_type - Return PATA cable type * @ap: Port to check - * @gtm: GTM data to use * - * Return 1 if the @gtm indicates the BIOS selected an 80wire mode. + * Return ATA_CBL_PATA* according to the transfer mode selected by BIOS */ -int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm) +int ata_acpi_cbl_pata_type(struct ata_port *ap) { struct ata_device *dev; + int ret = ATA_CBL_PATA_UNK; + const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); + + if (!gtm) + return ATA_CBL_PATA40; ata_for_each_dev(dev, &ap->link, ENABLED) { unsigned int xfer_mask, udma_mask; @@ -530,13 +534,17 @@ int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm) xfer_mask = ata_acpi_gtm_xfermask(dev, gtm); ata_unpack_xfermask(xfer_mask, NULL, NULL, &udma_mask); - if (udma_mask & ~ATA_UDMA_MASK_40C) - return 1; + ret = ATA_CBL_PATA40; + + if (udma_mask & ~ATA_UDMA_MASK_40C) { + ret = ATA_CBL_PATA80; + break; + } } - return 0; + return ret; } -EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire); +EXPORT_SYMBOL_GPL(ata_acpi_cbl_pata_type); static void ata_acpi_gtf_to_tf(struct ata_device *dev, const struct ata_acpi_gtf *gtf, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 79b20da0a256..ff53f5f029b4 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -65,8 +65,8 @@ #include "libata-transport.h" const struct ata_port_operations ata_base_port_ops = { - .prereset = ata_std_prereset, - .postreset = ata_std_postreset, + .reset.prereset = ata_std_prereset, + .reset.postreset = ata_std_postreset, .error_handler = ata_std_error_handler, .sched_eh = ata_std_sched_eh, .end_eh = ata_std_end_eh, @@ -2154,14 +2154,46 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log, return err_mask; } +static inline void ata_clear_log_directory(struct ata_device *dev) +{ + memset(dev->gp_log_dir, 0, ATA_SECT_SIZE); +} + +static int ata_read_log_directory(struct ata_device *dev) +{ + u16 version; + + /* If the log page is already cached, do nothing. */ + version = get_unaligned_le16(&dev->gp_log_dir[0]); + if (version == 0x0001) + return 0; + + if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->gp_log_dir, 1)) { + ata_clear_log_directory(dev); + return -EIO; + } + + version = get_unaligned_le16(&dev->gp_log_dir[0]); + if (version != 0x0001) { + ata_dev_err(dev, "Invalid log directory version 0x%04x\n", + version); + ata_clear_log_directory(dev); + dev->quirks |= ATA_QUIRK_NO_LOG_DIR; + return -EINVAL; + } + + return 0; +} + static int ata_log_supported(struct ata_device *dev, u8 log) { if (dev->quirks & ATA_QUIRK_NO_LOG_DIR) return 0; - if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->sector_buf, 1)) + if (ata_read_log_directory(dev)) return 0; - return get_unaligned_le16(&dev->sector_buf[log * 2]); + + return get_unaligned_le16(&dev->gp_log_dir[log * 2]); } static bool ata_identify_page_supported(struct ata_device *dev, u8 page) @@ -2421,18 +2453,7 @@ static void ata_dev_config_zac(struct ata_device *dev) dev->zac_zones_optimal_nonseq = U32_MAX; dev->zac_zones_max_open = U32_MAX; - /* - * Always set the 'ZAC' flag for Host-managed devices. - */ - if (dev->class == ATA_DEV_ZAC) - dev->flags |= ATA_DFLAG_ZAC; - else if (ata_id_zoned_cap(dev->id) == 0x01) - /* - * Check for host-aware devices. - */ - dev->flags |= ATA_DFLAG_ZAC; - - if (!(dev->flags & ATA_DFLAG_ZAC)) + if (!ata_dev_is_zac(dev)) return; if (!ata_identify_page_supported(dev, ATA_LOG_ZONED_INFORMATION)) { @@ -2495,7 +2516,7 @@ static void ata_dev_config_trusted(struct ata_device *dev) dev->flags |= ATA_DFLAG_TRUSTED; } -void ata_dev_cleanup_cdl_resources(struct ata_device *dev) +static void ata_dev_cleanup_cdl_resources(struct ata_device *dev) { kfree(dev->cdl); dev->cdl = NULL; @@ -2801,17 +2822,70 @@ static void ata_dev_config_cpr(struct ata_device *dev) kfree(buf); } +/* + * Configure features related to link power management. + */ +static void ata_dev_config_lpm(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + + if (ap->flags & ATA_FLAG_NO_LPM) { + /* + * When the port does not support LPM, we cannot support it on + * the device either. + */ + dev->quirks |= ATA_QUIRK_NOLPM; + } else { + /* + * Some WD SATA-1 drives have issues with LPM, turn on NOLPM for + * them. + */ + if ((dev->quirks & ATA_QUIRK_WD_BROKEN_LPM) && + (dev->id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) + dev->quirks |= ATA_QUIRK_NOLPM; + + /* ATI specific quirk */ + if ((dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI) && + ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI)) + dev->quirks |= ATA_QUIRK_NOLPM; + } + + if (dev->quirks & ATA_QUIRK_NOLPM && + ap->target_lpm_policy != ATA_LPM_MAX_POWER) { + ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); + ap->target_lpm_policy = ATA_LPM_MAX_POWER; + } + + /* + * Device Initiated Power Management (DIPM) is normally disabled by + * default on a device. However, DIPM may have been enabled and that + * setting kept even after COMRESET because of the Software Settings + * Preservation feature. So if the port does not support DIPM and the + * device does, disable DIPM on the device. + */ + if (ap->flags & ATA_FLAG_NO_DIPM && ata_id_has_dipm(dev->id)) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_DISABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) + ata_dev_err(dev, "Disable DIPM failed, Emask 0x%x\n", + err_mask); + } +} + static void ata_dev_print_features(struct ata_device *dev) { if (!(dev->flags & ATA_DFLAG_FEATURES_MASK)) return; ata_dev_info(dev, - "Features:%s%s%s%s%s%s%s%s\n", + "Features:%s%s%s%s%s%s%s%s%s%s\n", dev->flags & ATA_DFLAG_FUA ? " FUA" : "", dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "", dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "", dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "", + ata_id_has_hipm(dev->id) ? " HIPM" : "", + ata_id_has_dipm(dev->id) ? " DIPM" : "", dev->flags & ATA_DFLAG_NCQ_SEND_RECV ? " NCQ-sndrcv" : "", dev->flags & ATA_DFLAG_NCQ_PRIO ? " NCQ-prio" : "", dev->flags & ATA_DFLAG_CDL ? " CDL" : "", @@ -2848,6 +2922,9 @@ int ata_dev_configure(struct ata_device *dev) return 0; } + /* Clear the general purpose log directory cache. */ + ata_clear_log_directory(dev); + /* Set quirks */ dev->quirks |= ata_dev_quirks(dev); ata_force_quirks(dev); @@ -2871,23 +2948,6 @@ int ata_dev_configure(struct ata_device *dev) if (rc) return rc; - /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */ - if ((dev->quirks & ATA_QUIRK_WD_BROKEN_LPM) && - (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI && - ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI)) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (ap->flags & ATA_FLAG_NO_LPM) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (dev->quirks & ATA_QUIRK_NOLPM) { - ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); - dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; - } - /* let ACPI work its magic */ rc = ata_acpi_on_devcfg(dev); if (rc) @@ -2974,6 +3034,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_config_chs(dev); } + ata_dev_config_lpm(dev); ata_dev_config_fua(dev); ata_dev_config_devslp(dev); ata_dev_config_sense_reporting(dev); @@ -3449,7 +3510,7 @@ static int ata_dev_set_mode(struct ata_device *dev) } /** - * ata_do_set_mode - Program timings and issue SET FEATURES - XFER + * ata_set_mode - Program timings and issue SET FEATURES - XFER * @link: link on which timings will be programmed * @r_failed_dev: out parameter for failed device * @@ -3465,7 +3526,7 @@ static int ata_dev_set_mode(struct ata_device *dev) * 0 on success, negative errno otherwise */ -int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) +int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) { struct ata_port *ap = link->ap; struct ata_device *dev; @@ -3546,7 +3607,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) *r_failed_dev = dev; return rc; } -EXPORT_SYMBOL_GPL(ata_do_set_mode); +EXPORT_SYMBOL_GPL(ata_set_mode); /** * ata_wait_ready - wait for link to become ready @@ -4541,7 +4602,7 @@ static unsigned int ata_dev_init_params(struct ata_device *dev, return AC_ERR_INVALID; /* set up init dev params taskfile */ - ata_dev_dbg(dev, "init dev params \n"); + ata_dev_dbg(dev, "init dev params\n"); ata_tf_init(dev, &tf); tf.command = ATA_CMD_INIT_DEV_PARAMS; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 1f90fcd2b3c4..2586e77ebf45 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -153,8 +153,6 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { #undef CMDS static void __ata_port_freeze(struct ata_port *ap); -static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, - struct ata_device **r_failed_dev); #ifdef CONFIG_PM static void ata_eh_handle_port_suspend(struct ata_port *ap); static void ata_eh_handle_port_resume(struct ata_port *ap); @@ -825,7 +823,7 @@ void ata_port_wait_eh(struct ata_port *ap) retry: spin_lock_irqsave(ap->lock, flags); - while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) { + while (ata_port_eh_scheduled(ap)) { prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_irqrestore(ap->lock, flags); schedule(); @@ -909,7 +907,7 @@ void ata_eh_fastdrain_timerfn(struct timer_list *t) * LOCKING: * spin_lock_irqsave(host lock) */ -static void ata_eh_set_pending(struct ata_port *ap, int fastdrain) +static void ata_eh_set_pending(struct ata_port *ap, bool fastdrain) { unsigned int cnt; @@ -949,7 +947,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; qc->flags |= ATA_QCFLAG_EH; - ata_eh_set_pending(ap, 1); + ata_eh_set_pending(ap, true); /* The following will fail if timeout has already expired. * ata_scsi_error() takes care of such scmds on EH entry. @@ -971,7 +969,7 @@ void ata_std_sched_eh(struct ata_port *ap) if (ap->pflags & ATA_PFLAG_INITIALIZING) return; - ata_eh_set_pending(ap, 1); + ata_eh_set_pending(ap, true); scsi_schedule_eh(ap->scsi_host); trace_ata_std_sched_eh(ap); @@ -1022,7 +1020,7 @@ static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link) int tag, nr_aborted = 0; /* we're gonna abort all commands, no need for fast drain */ - ata_eh_set_pending(ap, 0); + ata_eh_set_pending(ap, false); /* include internal tag in iteration */ ata_qc_for_each_with_internal(ap, qc, tag) { @@ -2073,6 +2071,188 @@ static void ata_eh_get_success_sense(struct ata_link *link) ata_eh_done(link, dev, ATA_EH_GET_SUCCESS_SENSE); } +/* + * Check if a link is established. This is a relaxed version of + * ata_phys_link_online() which accounts for the fact that this is potentially + * called after changing the link power management policy, which may not be + * reflected immediately in the SStatus register (e.g., we may still be seeing + * the PHY in partial, slumber or devsleep Partial power management state. + * So check that: + * - A device is still present, that is, DET is 1h (Device presence detected + * but Phy communication not established) or 3h (Device presence detected and + * Phy communication established) + * - Communication is established, that is, IPM is not 0h, indicating that PHY + * is online or in a low power state. + */ +static bool ata_eh_link_established(struct ata_link *link) +{ + u32 sstatus; + u8 det, ipm; + + /* + * For old IDE/PATA adapters that do not have a valid scr_read method, + * or if reading the SStatus register fails, assume that the device is + * present. Device probe will determine if that is really the case. + */ + if (sata_scr_read(link, SCR_STATUS, &sstatus)) + return true; + + det = sstatus & 0x0f; + ipm = (sstatus >> 8) & 0x0f; + + return (det & 0x01) && ipm; +} + +/** + * ata_eh_link_set_lpm - configure SATA interface power management + * @link: link to configure + * @policy: the link power management policy + * @r_failed_dev: out parameter for failed device + * + * Enable SATA Interface power management. This will enable + * Device Interface Power Management (DIPM) for min_power and + * medium_power_with_dipm policies, and then call driver specific + * callbacks for enabling Host Initiated Power management. + * + * LOCKING: + * EH context. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +static int ata_eh_link_set_lpm(struct ata_link *link, + enum ata_lpm_policy policy, + struct ata_device **r_failed_dev) +{ + struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL; + struct ata_eh_context *ehc = &link->eh_context; + struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; + enum ata_lpm_policy old_policy = link->lpm_policy; + bool host_has_dipm = !(link->ap->flags & ATA_FLAG_NO_DIPM); + unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; + unsigned int err_mask; + int rc; + + /* if the link or host doesn't do LPM, noop */ + if (!IS_ENABLED(CONFIG_SATA_HOST) || + (link->flags & ATA_LFLAG_NO_LPM) || (ap && !ap->ops->set_lpm)) + return 0; + + /* + * This function currently assumes that it will never be supplied policy + * ATA_LPM_UNKNOWN. + */ + if (WARN_ON_ONCE(policy == ATA_LPM_UNKNOWN)) + return 0; + + ata_link_dbg(link, "Set LPM policy: %d -> %d\n", old_policy, policy); + + /* + * DIPM is enabled only for ATA_LPM_MIN_POWER, + * ATA_LPM_MIN_POWER_WITH_PARTIAL, and ATA_LPM_MED_POWER_WITH_DIPM, as + * some devices misbehave when the host NACKs transition to SLUMBER. + */ + ata_for_each_dev(dev, link, ENABLED) { + bool dev_has_hipm = ata_id_has_hipm(dev->id); + bool dev_has_dipm = ata_id_has_dipm(dev->id); + + /* find the first enabled and LPM enabled devices */ + if (!link_dev) + link_dev = dev; + + if (!lpm_dev && + (dev_has_hipm || (dev_has_dipm && host_has_dipm))) + lpm_dev = dev; + + hints &= ~ATA_LPM_EMPTY; + if (!dev_has_hipm) + hints &= ~ATA_LPM_HIPM; + + /* disable DIPM before changing link config */ + if (dev_has_dipm) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_DISABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) { + ata_dev_warn(dev, + "failed to disable DIPM, Emask 0x%x\n", + err_mask); + rc = -EIO; + goto fail; + } + } + } + + if (ap) { + rc = ap->ops->set_lpm(link, policy, hints); + if (!rc && ap->slave_link) + rc = ap->ops->set_lpm(ap->slave_link, policy, hints); + } else + rc = sata_pmp_set_lpm(link, policy, hints); + + /* + * Attribute link config failure to the first (LPM) enabled + * device on the link. + */ + if (rc) { + if (rc == -EOPNOTSUPP) { + link->flags |= ATA_LFLAG_NO_LPM; + return 0; + } + dev = lpm_dev ? lpm_dev : link_dev; + goto fail; + } + + /* + * Low level driver acked the transition. Issue DIPM command + * with the new policy set. + */ + link->lpm_policy = policy; + if (ap && ap->slave_link) + ap->slave_link->lpm_policy = policy; + + /* + * Host config updated, enable DIPM if transitioning to + * ATA_LPM_MIN_POWER, ATA_LPM_MIN_POWER_WITH_PARTIAL, or + * ATA_LPM_MED_POWER_WITH_DIPM. + */ + ata_for_each_dev(dev, link, ENABLED) { + bool dev_has_dipm = ata_id_has_dipm(dev->id); + + if (policy >= ATA_LPM_MED_POWER_WITH_DIPM && host_has_dipm && + dev_has_dipm) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_ENABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) { + ata_dev_warn(dev, + "failed to enable DIPM, Emask 0x%x\n", + err_mask); + rc = -EIO; + goto fail; + } + } + } + + link->last_lpm_change = jiffies; + link->flags |= ATA_LFLAG_CHANGED; + + return 0; + +fail: + /* restore the old policy */ + link->lpm_policy = old_policy; + if (ap && ap->slave_link) + ap->slave_link->lpm_policy = old_policy; + + /* if no device or only one more chance is left, disable LPM */ + if (!dev || ehc->tries[dev->devno] <= 2) { + ata_link_warn(link, "disabling LPM on the link\n"); + link->flags |= ATA_LFLAG_NO_LPM; + } + if (r_failed_dev) + *r_failed_dev = dev; + return rc; +} + /** * ata_eh_link_autopsy - analyze error and determine recovery action * @link: host link to perform autopsy on @@ -2606,25 +2786,28 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset, return reset(link, classes, deadline); } -static int ata_eh_followup_srst_needed(struct ata_link *link, int rc) +static bool ata_eh_followup_srst_needed(struct ata_link *link, int rc) { if ((link->flags & ATA_LFLAG_NO_SRST) || ata_link_offline(link)) - return 0; + return false; if (rc == -EAGAIN) - return 1; + return true; if (sata_pmp_supported(link->ap) && ata_is_host_link(link)) - return 1; - return 0; + return true; + return false; } int ata_eh_reset(struct ata_link *link, int classify, - ata_prereset_fn_t prereset, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) + struct ata_reset_operations *reset_ops) { struct ata_port *ap = link->ap; struct ata_link *slave = ap->slave_link; struct ata_eh_context *ehc = &link->eh_context; struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL; + ata_reset_fn_t hardreset = reset_ops->hardreset; + ata_reset_fn_t softreset = reset_ops->softreset; + ata_prereset_fn_t prereset = reset_ops->prereset; + ata_postreset_fn_t postreset = reset_ops->postreset; unsigned int *classes = ehc->classes; unsigned int lflags = link->flags; int verbose = !(ehc->i.flags & ATA_EHI_QUIET); @@ -3123,13 +3306,13 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, * to ap->target_lpm_policy after revalidation is done. */ if (link->lpm_policy > ATA_LPM_MAX_POWER) { - rc = ata_eh_set_lpm(link, ATA_LPM_MAX_POWER, - r_failed_dev); + rc = ata_eh_link_set_lpm(link, ATA_LPM_MAX_POWER, + r_failed_dev); if (rc) goto err; } - if (ata_phys_link_offline(ata_dev_phys_link(dev))) { + if (!ata_eh_link_established(ata_dev_phys_link(dev))) { rc = -EIO; goto err; } @@ -3233,12 +3416,12 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, } /** - * ata_set_mode - Program timings and issue SET FEATURES - XFER + * ata_eh_set_mode - Program timings and issue SET FEATURES - XFER * @link: link on which timings will be programmed * @r_failed_dev: out parameter for failed device * * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If - * ata_set_mode() fails, pointer to the failing device is + * ata_eh_set_mode() fails, pointer to the failing device is * returned in @r_failed_dev. * * LOCKING: @@ -3247,7 +3430,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, * RETURNS: * 0 on success, negative errno otherwise */ -int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) +static int ata_eh_set_mode(struct ata_link *link, + struct ata_device **r_failed_dev) { struct ata_port *ap = link->ap; struct ata_device *dev; @@ -3268,7 +3452,7 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) if (ap->ops->set_mode) rc = ap->ops->set_mode(link, r_failed_dev); else - rc = ata_do_set_mode(link, r_failed_dev); + rc = ata_set_mode(link, r_failed_dev); /* if transfer mode has changed, set DUBIOUS_XFER on device */ ata_for_each_dev(dev, link, ENABLED) { @@ -3408,153 +3592,6 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev) return rc; } -/** - * ata_eh_set_lpm - configure SATA interface power management - * @link: link to configure power management - * @policy: the link power management policy - * @r_failed_dev: out parameter for failed device - * - * Enable SATA Interface power management. This will enable - * Device Interface Power Management (DIPM) for min_power and - * medium_power_with_dipm policies, and then call driver specific - * callbacks for enabling Host Initiated Power management. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, - struct ata_device **r_failed_dev) -{ - struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL; - struct ata_eh_context *ehc = &link->eh_context; - struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; - enum ata_lpm_policy old_policy = link->lpm_policy; - bool host_has_dipm = !(link->ap->flags & ATA_FLAG_NO_DIPM); - unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; - unsigned int err_mask; - int rc; - - /* if the link or host doesn't do LPM, noop */ - if (!IS_ENABLED(CONFIG_SATA_HOST) || - (link->flags & ATA_LFLAG_NO_LPM) || (ap && !ap->ops->set_lpm)) - return 0; - - /* - * This function currently assumes that it will never be supplied policy - * ATA_LPM_UNKNOWN. - */ - if (WARN_ON_ONCE(policy == ATA_LPM_UNKNOWN)) - return 0; - - /* - * DIPM is enabled only for ATA_LPM_MIN_POWER, - * ATA_LPM_MIN_POWER_WITH_PARTIAL, and ATA_LPM_MED_POWER_WITH_DIPM, as - * some devices misbehave when the host NACKs transition to SLUMBER. - */ - ata_for_each_dev(dev, link, ENABLED) { - bool dev_has_hipm = ata_id_has_hipm(dev->id); - bool dev_has_dipm = ata_id_has_dipm(dev->id); - - /* find the first enabled and LPM enabled devices */ - if (!link_dev) - link_dev = dev; - - if (!lpm_dev && - (dev_has_hipm || (dev_has_dipm && host_has_dipm))) - lpm_dev = dev; - - hints &= ~ATA_LPM_EMPTY; - if (!dev_has_hipm) - hints &= ~ATA_LPM_HIPM; - - /* disable DIPM before changing link config */ - if (dev_has_dipm) { - err_mask = ata_dev_set_feature(dev, - SETFEATURES_SATA_DISABLE, SATA_DIPM); - if (err_mask && err_mask != AC_ERR_DEV) { - ata_dev_warn(dev, - "failed to disable DIPM, Emask 0x%x\n", - err_mask); - rc = -EIO; - goto fail; - } - } - } - - if (ap) { - rc = ap->ops->set_lpm(link, policy, hints); - if (!rc && ap->slave_link) - rc = ap->ops->set_lpm(ap->slave_link, policy, hints); - } else - rc = sata_pmp_set_lpm(link, policy, hints); - - /* - * Attribute link config failure to the first (LPM) enabled - * device on the link. - */ - if (rc) { - if (rc == -EOPNOTSUPP) { - link->flags |= ATA_LFLAG_NO_LPM; - return 0; - } - dev = lpm_dev ? lpm_dev : link_dev; - goto fail; - } - - /* - * Low level driver acked the transition. Issue DIPM command - * with the new policy set. - */ - link->lpm_policy = policy; - if (ap && ap->slave_link) - ap->slave_link->lpm_policy = policy; - - /* - * Host config updated, enable DIPM if transitioning to - * ATA_LPM_MIN_POWER, ATA_LPM_MIN_POWER_WITH_PARTIAL, or - * ATA_LPM_MED_POWER_WITH_DIPM. - */ - ata_for_each_dev(dev, link, ENABLED) { - bool dev_has_dipm = ata_id_has_dipm(dev->id); - - if (policy >= ATA_LPM_MED_POWER_WITH_DIPM && host_has_dipm && - dev_has_dipm) { - err_mask = ata_dev_set_feature(dev, - SETFEATURES_SATA_ENABLE, SATA_DIPM); - if (err_mask && err_mask != AC_ERR_DEV) { - ata_dev_warn(dev, - "failed to enable DIPM, Emask 0x%x\n", - err_mask); - rc = -EIO; - goto fail; - } - } - } - - link->last_lpm_change = jiffies; - link->flags |= ATA_LFLAG_CHANGED; - - return 0; - -fail: - /* restore the old policy */ - link->lpm_policy = old_policy; - if (ap && ap->slave_link) - ap->slave_link->lpm_policy = old_policy; - - /* if no device or only one more chance is left, disable LPM */ - if (!dev || ehc->tries[dev->devno] <= 2) { - ata_link_warn(link, "disabling LPM on the link\n"); - link->flags |= ATA_LFLAG_NO_LPM; - } - if (r_failed_dev) - *r_failed_dev = dev; - return rc; -} - int ata_link_nr_enabled(struct ata_link *link) { struct ata_device *dev; @@ -3727,10 +3764,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err) /** * ata_eh_recover - recover host port after error * @ap: host port to recover - * @prereset: prereset method (can be NULL) - * @softreset: softreset method (can be NULL) - * @hardreset: hardreset method (can be NULL) - * @postreset: postreset method (can be NULL) + * @reset_ops: The set of reset operations to use * @r_failed_link: out parameter for failed link * * This is the alpha and omega, eum and yang, heart and soul of @@ -3746,9 +3780,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err) * RETURNS: * 0 on success, -errno on failure. */ -int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset, +int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops, struct ata_link **r_failed_link) { struct ata_link *link; @@ -3816,8 +3848,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, if (!(ehc->i.action & ATA_EH_RESET)) continue; - rc = ata_eh_reset(link, ata_link_nr_vacant(link), - prereset, softreset, hardreset, postreset); + rc = ata_eh_reset(link, ata_link_nr_vacant(link), reset_ops); if (rc) { ata_link_err(link, "reset failed, giving up\n"); goto out; @@ -3898,7 +3929,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, /* configure transfer mode if necessary */ if (ehc->i.flags & ATA_EHI_SETMODE) { - rc = ata_set_mode(link, &dev); + rc = ata_eh_set_mode(link, &dev); if (rc) goto rest_fail; ehc->i.flags &= ~ATA_EHI_SETMODE; @@ -3943,7 +3974,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, config_lpm: /* configure link power saving */ if (link->lpm_policy != ap->target_lpm_policy) { - rc = ata_eh_set_lpm(link, ap->target_lpm_policy, &dev); + rc = ata_eh_link_set_lpm(link, ap->target_lpm_policy, + &dev); if (rc) goto rest_fail; } @@ -4037,59 +4069,39 @@ void ata_eh_finish(struct ata_port *ap) } /** - * ata_do_eh - do standard error handling + * ata_std_error_handler - standard error handler * @ap: host port to handle error for * - * @prereset: prereset method (can be NULL) - * @softreset: softreset method (can be NULL) - * @hardreset: hardreset method (can be NULL) - * @postreset: postreset method (can be NULL) - * * Perform standard error handling sequence. * * LOCKING: * Kernel thread context (may sleep). */ -void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset) +void ata_std_error_handler(struct ata_port *ap) { - struct ata_device *dev; + struct ata_reset_operations *reset_ops = &ap->ops->reset; + struct ata_link *link = &ap->link; int rc; + /* Ignore built-in hardresets if SCR access is not available */ + if ((reset_ops->hardreset == sata_std_hardreset || + reset_ops->hardreset == sata_sff_hardreset) && + !sata_scr_valid(link)) + link->flags |= ATA_LFLAG_NO_HRST; + ata_eh_autopsy(ap); ata_eh_report(ap); - rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset, - NULL); + rc = ata_eh_recover(ap, reset_ops, NULL); if (rc) { - ata_for_each_dev(dev, &ap->link, ALL) + struct ata_device *dev; + + ata_for_each_dev(dev, link, ALL) ata_dev_disable(dev); } ata_eh_finish(ap); } - -/** - * ata_std_error_handler - standard error handler - * @ap: host port to handle error for - * - * Standard error handler - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_std_error_handler(struct ata_port *ap) -{ - struct ata_port_operations *ops = ap->ops; - ata_reset_fn_t hardreset = ops->hardreset; - - /* ignore built-in hardreset if SCR access is not available */ - if (hardreset == sata_std_hardreset && !sata_scr_valid(&ap->link)) - hardreset = NULL; - - ata_do_eh(ap, ops->prereset, ops->softreset, hardreset, ops->postreset); -} EXPORT_SYMBOL_GPL(ata_std_error_handler); #ifdef CONFIG_PM diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index d5d189328ae6..57023324a56f 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -15,9 +15,9 @@ const struct ata_port_operations sata_pmp_port_ops = { .inherits = &sata_port_ops, - .pmp_prereset = ata_std_prereset, - .pmp_hardreset = sata_std_hardreset, - .pmp_postreset = ata_std_postreset, + .pmp_reset.prereset = ata_std_prereset, + .pmp_reset.hardreset = sata_std_hardreset, + .pmp_reset.postreset = ata_std_postreset, .error_handler = sata_pmp_error_handler, }; @@ -727,10 +727,7 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev) /** * sata_pmp_eh_recover_pmp - recover PMP * @ap: ATA port PMP is attached to - * @prereset: prereset method (can be NULL) - * @softreset: softreset method - * @hardreset: hardreset method - * @postreset: postreset method (can be NULL) + * @reset_ops: The set of reset operations to use * * Recover PMP attached to @ap. Recovery procedure is somewhat * similar to that of ata_eh_recover() except that reset should @@ -744,8 +741,7 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev) * 0 on success, -errno on failure. */ static int sata_pmp_eh_recover_pmp(struct ata_port *ap, - ata_prereset_fn_t prereset, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) + struct ata_reset_operations *reset_ops) { struct ata_link *link = &ap->link; struct ata_eh_context *ehc = &link->eh_context; @@ -767,8 +763,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap, struct ata_link *tlink; /* reset */ - rc = ata_eh_reset(link, 0, prereset, softreset, hardreset, - postreset); + rc = ata_eh_reset(link, 0, reset_ops); if (rc) { ata_link_err(link, "failed to reset PMP, giving up\n"); goto fail; @@ -932,8 +927,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap) retry: /* PMP attached? */ if (!sata_pmp_attached(ap)) { - rc = ata_eh_recover(ap, ops->prereset, ops->softreset, - ops->hardreset, ops->postreset, NULL); + rc = ata_eh_recover(ap, &ops->reset, NULL); if (rc) { ata_for_each_dev(dev, &ap->link, ALL) ata_dev_disable(dev); @@ -951,8 +945,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap) } /* recover pmp */ - rc = sata_pmp_eh_recover_pmp(ap, ops->prereset, ops->softreset, - ops->hardreset, ops->postreset); + rc = sata_pmp_eh_recover_pmp(ap, &ops->reset); if (rc) goto pmp_fail; @@ -978,8 +971,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap) goto pmp_fail; /* recover links */ - rc = ata_eh_recover(ap, ops->pmp_prereset, ops->pmp_softreset, - ops->pmp_hardreset, ops->pmp_postreset, &link); + rc = ata_eh_recover(ap, &ops->pmp_reset, &link); if (rc) goto link_fail; diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index cb46ce276bb1..b2817a2995d6 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -900,14 +900,52 @@ static const char *ata_lpm_policy_names[] = { [ATA_LPM_MIN_POWER] = "min_power", }; +/* + * Check if a port supports link power management. + * Must be called with the port locked. + */ +static bool ata_scsi_lpm_supported(struct ata_port *ap) +{ + struct ata_link *link; + struct ata_device *dev; + + if (ap->flags & ATA_FLAG_NO_LPM) + return false; + + ata_for_each_link(link, ap, EDGE) { + ata_for_each_dev(dev, &ap->link, ENABLED) { + if (dev->quirks & ATA_QUIRK_NOLPM) + return false; + } + } + + return true; +} + +static ssize_t ata_scsi_lpm_supported_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + unsigned long flags; + bool supported; + + spin_lock_irqsave(ap->lock, flags); + supported = ata_scsi_lpm_supported(ap); + spin_unlock_irqrestore(ap->lock, flags); + + return sysfs_emit(buf, "%d\n", supported); +} +DEVICE_ATTR(link_power_management_supported, S_IRUGO, + ata_scsi_lpm_supported_show, NULL); +EXPORT_SYMBOL_GPL(dev_attr_link_power_management_supported); + static ssize_t ata_scsi_lpm_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(device); struct ata_port *ap = ata_shost_to_port(shost); - struct ata_link *link; - struct ata_device *dev; enum ata_lpm_policy policy; unsigned long flags; @@ -924,13 +962,9 @@ static ssize_t ata_scsi_lpm_store(struct device *device, spin_lock_irqsave(ap->lock, flags); - ata_for_each_link(link, ap, EDGE) { - ata_for_each_dev(dev, &ap->link, ENABLED) { - if (dev->quirks & ATA_QUIRK_NOLPM) { - count = -EOPNOTSUPP; - goto out_unlock; - } - } + if (!ata_scsi_lpm_supported(ap)) { + count = -EOPNOTSUPP; + goto out_unlock; } ap->target_lpm_policy = policy; @@ -1699,6 +1733,6 @@ const struct ata_port_operations sata_port_ops = { .inherits = &ata_base_port_ops, .qc_defer = ata_std_qc_defer, - .hardreset = sata_std_hardreset, + .reset.hardreset = sata_std_hardreset, }; EXPORT_SYMBOL_GPL(sata_port_ops); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a21c9895408d..2ded5e476d6e 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -859,18 +859,14 @@ static void ata_to_sense_error(u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc, {0xFF, 0xFF, 0xFF, 0xFF}, // END mark }; static const unsigned char stat_table[][4] = { - /* Must be first because BUSY means no other bits valid */ - {0x80, ABORTED_COMMAND, 0x47, 0x00}, - // Busy, fake parity for now - {0x40, ILLEGAL_REQUEST, 0x21, 0x04}, - // Device ready, unaligned write command - {0x20, HARDWARE_ERROR, 0x44, 0x00}, - // Device fault, internal target failure - {0x08, ABORTED_COMMAND, 0x47, 0x00}, - // Timed out in xfer, fake parity for now - {0x04, RECOVERED_ERROR, 0x11, 0x00}, - // Recovered ECC error Medium error, recovered - {0xFF, 0xFF, 0xFF, 0xFF}, // END mark + /* Busy: must be first because BUSY means no other bits valid */ + { ATA_BUSY, ABORTED_COMMAND, 0x00, 0x00 }, + /* Device fault: INTERNAL TARGET FAILURE */ + { ATA_DF, HARDWARE_ERROR, 0x44, 0x00 }, + /* Corrected data error */ + { ATA_CORR, RECOVERED_ERROR, 0x00, 0x00 }, + + { 0xFF, 0xFF, 0xFF, 0xFF }, /* END mark */ }; /* @@ -942,6 +938,8 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) { ata_dev_dbg(dev, "missing result TF: can't generate ATA PT sense data\n"); + if (qc->err_mask) + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); return; } @@ -996,8 +994,8 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) { ata_dev_dbg(dev, - "missing result TF: can't generate sense data\n"); - return; + "Missing result TF: reporting aborted command\n"); + goto aborted; } /* Use ata_to_sense_error() to map status register bits @@ -1008,13 +1006,15 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) ata_to_sense_error(tf->status, tf->error, &sense_key, &asc, &ascq); ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq); - } else { - /* Could not decode error */ - ata_dev_warn(dev, "could not decode error status 0x%x err_mask 0x%x\n", - tf->status, qc->err_mask); - ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); return; } + + /* Could not decode error */ + ata_dev_warn(dev, + "Could not decode error 0x%x, status 0x%x (err_mask=0x%x)\n", + tf->error, tf->status, qc->err_mask); +aborted: + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); } void ata_scsi_sdev_config(struct scsi_device *sdev) @@ -1923,8 +1923,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_device *dev, }; for (i = 0; i < sizeof(pages); i++) { - if (pages[i] == 0xb6 && - !(dev->flags & ATA_DFLAG_ZAC)) + if (pages[i] == 0xb6 && !ata_dev_is_zac(dev)) continue; rbuf[num_pages + 4] = pages[i]; num_pages++; @@ -2181,7 +2180,7 @@ static unsigned int ata_scsiop_inq_b2(struct ata_device *dev, static unsigned int ata_scsiop_inq_b6(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf) { - if (!(dev->flags & ATA_DFLAG_ZAC)) { + if (!ata_dev_is_zac(dev)) { ata_scsi_set_invalid_field(dev, cmd, 2, 0xff); return 0; } @@ -3905,21 +3904,16 @@ static int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc, /* Check cdl_ctrl */ switch (buf[0] & 0x03) { case 0: - /* Disable CDL if it is enabled */ - if (!(dev->flags & ATA_DFLAG_CDL_ENABLED)) - return 0; + /* Disable CDL */ ata_dev_dbg(dev, "Disabling CDL\n"); cdl_action = 0; dev->flags &= ~ATA_DFLAG_CDL_ENABLED; break; case 0x02: /* - * Enable CDL if not already enabled. Since this is mutually - * exclusive with NCQ priority, allow this only if NCQ priority - * is disabled. + * Enable CDL. Since CDL is mutually exclusive with NCQ + * priority, allow this only if NCQ priority is disabled. */ - if (dev->flags & ATA_DFLAG_CDL_ENABLED) - return 0; if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED) { ata_dev_err(dev, "NCQ priority must be disabled to enable CDL\n"); @@ -4317,9 +4311,10 @@ int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev) * scsi_queue_rq() will defer commands if scsi_host_in_recovery(). * However, this check is done without holding the ap->lock (a libata * specific lock), so we can have received an error irq since then, - * therefore we must check if EH is pending, while holding ap->lock. + * therefore we must check if EH is pending or running, while holding + * ap->lock. */ - if (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) + if (ata_port_eh_scheduled(ap)) return SCSI_MLQUEUE_DEVICE_BUSY; if (unlikely(!scmd->cmd_len)) @@ -4634,24 +4629,23 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync) * ata_scsi_offline_dev - offline attached SCSI device * @dev: ATA device to offline attached SCSI device for * - * This function is called from ata_eh_hotplug() and responsible - * for taking the SCSI device attached to @dev offline. This - * function is called with host lock which protects dev->sdev - * against clearing. + * This function is called from ata_eh_detach_dev() and is responsible for + * taking the SCSI device attached to @dev offline. This function is + * called with host lock which protects dev->sdev against clearing. * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: - * 1 if attached SCSI device exists, 0 otherwise. + * true if attached SCSI device exists, false otherwise. */ -int ata_scsi_offline_dev(struct ata_device *dev) +bool ata_scsi_offline_dev(struct ata_device *dev) { if (dev->sdev) { scsi_device_set_state(dev->sdev, SDEV_OFFLINE); - return 1; + return true; } - return 0; + return false; } /** diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 5a46c066abc3..7fc407255eb4 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -31,10 +31,10 @@ const struct ata_port_operations ata_sff_port_ops = { .freeze = ata_sff_freeze, .thaw = ata_sff_thaw, - .prereset = ata_sff_prereset, - .softreset = ata_sff_softreset, - .hardreset = sata_sff_hardreset, - .postreset = ata_sff_postreset, + .reset.prereset = ata_sff_prereset, + .reset.softreset = ata_sff_softreset, + .reset.hardreset = sata_sff_hardreset, + .reset.postreset = ata_sff_postreset, .error_handler = ata_sff_error_handler, .sff_dev_select = ata_sff_dev_select, @@ -2054,8 +2054,6 @@ EXPORT_SYMBOL_GPL(ata_sff_drain_fifo); */ void ata_sff_error_handler(struct ata_port *ap) { - ata_reset_fn_t softreset = ap->ops->softreset; - ata_reset_fn_t hardreset = ap->ops->hardreset; struct ata_queued_cmd *qc; unsigned long flags; @@ -2077,13 +2075,7 @@ void ata_sff_error_handler(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); - /* ignore built-in hardresets if SCR access is not available */ - if ((hardreset == sata_std_hardreset || - hardreset == sata_sff_hardreset) && !sata_scr_valid(&ap->link)) - hardreset = NULL; - - ata_do_eh(ap, ap->ops->prereset, softreset, hardreset, - ap->ops->postreset); + ata_std_error_handler(ap); } EXPORT_SYMBOL_GPL(ata_sff_error_handler); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index e898be49df6b..62415fe67a11 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -202,7 +202,7 @@ show_ata_port_##name(struct device *dev, \ { \ struct ata_port *ap = transport_class_to_port(dev); \ \ - return scnprintf(buf, 20, format_string, cast ap->field); \ + return sysfs_emit(buf, format_string, cast ap->field); \ } #define ata_port_simple_attr(field, name, format_string, type) \ @@ -389,7 +389,7 @@ show_ata_dev_##field(struct device *dev, \ { \ struct ata_device *ata_dev = transport_class_to_dev(dev); \ \ - return scnprintf(buf, 20, format_string, cast ata_dev->field); \ + return sysfs_emit(buf, format_string, cast ata_dev->field); \ } #define ata_dev_simple_attr(field, format_string, type) \ diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index ce5c628fa6fd..e5b977a8d3e1 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -44,6 +44,18 @@ static inline bool ata_sstatus_online(u32 sstatus) return (sstatus & 0xf) == 0x3; } +static inline bool ata_dev_is_zac(struct ata_device *dev) +{ + /* Host managed device or host aware device */ + return dev->class == ATA_DEV_ZAC || + ata_id_zoned_cap(dev->id) == 0x01; +} + +static inline bool ata_port_eh_scheduled(struct ata_port *ap) +{ + return ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS); +} + #ifdef CONFIG_ATA_FORCE extern void ata_force_cbl(struct ata_port *ap); #else @@ -90,7 +102,6 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern const char *sata_spd_string(unsigned int spd); extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors); -void ata_dev_cleanup_cdl_resources(struct ata_device *dev); #define to_ata_port(d) container_of(d, struct ata_port, tdev) @@ -137,7 +148,7 @@ extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap, extern int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap, int sync); -extern int ata_scsi_offline_dev(struct ata_device *dev); +extern bool ata_scsi_offline_dev(struct ata_device *dev); extern bool ata_scsi_sense_is_valid(u8 sk, u8 asc, u8 ascq); extern void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); @@ -169,12 +180,9 @@ extern void ata_eh_autopsy(struct ata_port *ap); const char *ata_get_cmd_name(u8 command); extern void ata_eh_report(struct ata_port *ap); extern int ata_eh_reset(struct ata_link *link, int classify, - ata_prereset_fn_t prereset, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset); -extern int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev); -extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset, + struct ata_reset_operations *reset_ops); +extern int ata_eh_recover(struct ata_port *ap, + struct ata_reset_operations *reset_ops, struct ata_link **r_failed_disk); extern void ata_eh_finish(struct ata_port *ap); extern int ata_ering_map(struct ata_ering *ering, diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index ab38871b5e00..23fff10af2ac 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -216,7 +216,7 @@ static struct ata_port_operations pacpi_ops = { .mode_filter = pacpi_mode_filter, .set_piomode = pacpi_set_piomode, .set_dmamode = pacpi_set_dmamode, - .prereset = pacpi_pre_reset, + .reset.prereset = pacpi_pre_reset, .port_start = pacpi_port_start, }; diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index bb790edd6036..9d5cb9c34c52 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -392,11 +392,11 @@ static struct ata_port_operations ali_20_port_ops = { * Port operations for DMA capable ALi with cable detect */ static struct ata_port_operations ali_c2_port_ops = { - .inherits = &ali_dma_base_ops, - .check_atapi_dma = ali_check_atapi_dma, - .cable_detect = ali_c2_cable_detect, - .dev_config = ali_lock_sectors, - .postreset = ali_c2_c3_postreset, + .inherits = &ali_dma_base_ops, + .check_atapi_dma = ali_check_atapi_dma, + .cable_detect = ali_c2_cable_detect, + .dev_config = ali_lock_sectors, + .reset.postreset = ali_c2_c3_postreset, }; /* diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 5b02b89748b7..a2fecadc927d 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -394,7 +394,7 @@ static const struct scsi_host_template amd_sht = { static const struct ata_port_operations amd_base_port_ops = { .inherits = &ata_bmdma32_port_ops, - .prereset = amd_pre_reset, + .reset.prereset = amd_pre_reset, }; static struct ata_port_operations amd33_port_ops = { @@ -429,7 +429,7 @@ static const struct ata_port_operations nv_base_port_ops = { .inherits = &ata_bmdma_port_ops, .cable_detect = ata_cable_ignore, .mode_filter = nv_mode_filter, - .prereset = nv_pre_reset, + .reset.prereset = nv_pre_reset, .host_stop = nv_host_stop, }; diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index 40544282f455..6160414172a3 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -301,7 +301,7 @@ static struct ata_port_operations artop6210_ops = { .cable_detect = ata_cable_40wire, .set_piomode = artop6210_set_piomode, .set_dmamode = artop6210_set_dmamode, - .prereset = artop62x0_pre_reset, + .reset.prereset = artop62x0_pre_reset, .qc_defer = artop6210_qc_defer, }; @@ -310,7 +310,7 @@ static struct ata_port_operations artop6260_ops = { .cable_detect = artop6260_cable_detect, .set_piomode = artop6260_set_piomode, .set_dmamode = artop6260_set_dmamode, - .prereset = artop62x0_pre_reset, + .reset.prereset = artop62x0_pre_reset, }; static void atp8xx_fixup(struct pci_dev *pdev) diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index 8c5cc803aab3..4c612f9543f6 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -264,7 +264,7 @@ static struct ata_port_operations atiixp_port_ops = { .bmdma_start = atiixp_bmdma_start, .bmdma_stop = atiixp_bmdma_stop, - .prereset = atiixp_prereset, + .reset.prereset = atiixp_prereset, .cable_detect = atiixp_cable_detect, .set_piomode = atiixp_set_piomode, .set_dmamode = atiixp_set_dmamode, diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index b811efd2cc34..73e81e160c91 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -27,7 +27,7 @@ #include #include -#ifdef CONFIG_X86_32 +#if defined(CONFIG_X86) && defined(CONFIG_X86_32) #include static int use_msr; module_param_named(msr, use_msr, int, 0644); diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index 2e6eccf2902f..6fe49b303fee 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -243,7 +243,7 @@ static struct ata_port_operations efar_ops = { .cable_detect = efar_cable_detect, .set_piomode = efar_set_piomode, .set_dmamode = efar_set_dmamode, - .prereset = efar_pre_reset, + .reset.prereset = efar_pre_reset, }; diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c index e8cda988feb5..b2b9e0058333 100644 --- a/drivers/ata/pata_ep93xx.c +++ b/drivers/ata/pata_ep93xx.c @@ -879,8 +879,8 @@ static const struct scsi_host_template ep93xx_pata_sht = { static struct ata_port_operations ep93xx_pata_port_ops = { .inherits = &ata_bmdma_port_ops, - .softreset = ep93xx_pata_softreset, - .hardreset = ATA_OP_NULL, + .reset.softreset = ep93xx_pata_softreset, + .reset.hardreset = ATA_OP_NULL, .sff_dev_select = ep93xx_pata_dev_select, .sff_set_devctl = ep93xx_pata_set_devctl, diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index 5280e9960025..b96e8bd2a3f8 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -322,7 +322,7 @@ static const struct scsi_host_template hpt36x_sht = { static struct ata_port_operations hpt366_port_ops = { .inherits = &ata_bmdma_port_ops, - .prereset = hpt366_prereset, + .reset.prereset = hpt366_prereset, .cable_detect = hpt36x_cable_detect, .mode_filter = hpt366_filter, .set_piomode = hpt366_set_piomode, diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index 4af22b819416..07e3a984cbb1 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -543,7 +543,7 @@ static struct ata_port_operations hpt370_port_ops = { .cable_detect = hpt37x_cable_detect, .set_piomode = hpt37x_set_piomode, .set_dmamode = hpt37x_set_dmamode, - .prereset = hpt37x_pre_reset, + .reset.prereset = hpt37x_pre_reset, }; /* @@ -567,7 +567,7 @@ static struct ata_port_operations hpt302_port_ops = { .cable_detect = hpt37x_cable_detect, .set_piomode = hpt37x_set_piomode, .set_dmamode = hpt37x_set_dmamode, - .prereset = hpt37x_pre_reset, + .reset.prereset = hpt37x_pre_reset, }; /* diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 5b1ecccf3c83..2cc57fcf2c46 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -356,7 +356,7 @@ static struct ata_port_operations hpt3xxn_port_ops = { .cable_detect = hpt3x2n_cable_detect, .set_piomode = hpt3x2n_set_piomode, .set_dmamode = hpt3x2n_set_dmamode, - .prereset = hpt3x2n_pre_reset, + .reset.prereset = hpt3x2n_pre_reset, }; /* diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c index 61d8760f09d9..70f056e47e6b 100644 --- a/drivers/ata/pata_icside.c +++ b/drivers/ata/pata_icside.c @@ -336,7 +336,7 @@ static struct ata_port_operations pata_icside_port_ops = { .cable_detect = ata_cable_40wire, .set_dmamode = pata_icside_set_dmamode, - .postreset = pata_icside_postreset, + .reset.postreset = pata_icside_postreset, .port_start = ATA_OP_NULL, /* don't need PRD table */ }; diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index 9cbe2132ce59..a6f2cfc1602e 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -238,7 +238,7 @@ static struct ata_port_operations it8213_ops = { .cable_detect = it8213_cable_detect, .set_piomode = it8213_set_piomode, .set_dmamode = it8213_set_dmamode, - .prereset = it8213_pre_reset, + .reset.prereset = it8213_pre_reset, }; diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index f51fb8219762..b885f33e8980 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -113,7 +113,7 @@ static const struct scsi_host_template jmicron_sht = { static struct ata_port_operations jmicron_ops = { .inherits = &ata_bmdma_port_ops, - .prereset = jmicron_pre_reset, + .reset.prereset = jmicron_pre_reset, }; diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index fbf5f07ea357..9eefdc5df5df 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -758,7 +758,7 @@ static void pata_macio_irq_clear(struct ata_port *ap) static void pata_macio_reset_hw(struct pata_macio_priv *priv, int resume) { - dev_dbg(priv->dev, "Enabling & resetting... \n"); + dev_dbg(priv->dev, "Enabling & resetting...\n"); if (priv->mediabay) return; @@ -1298,7 +1298,7 @@ static int pata_macio_pci_attach(struct pci_dev *pdev, priv->dev = &pdev->dev; /* Get MMIO regions */ - if (pci_request_regions(pdev, "pata-macio")) { + if (pcim_request_all_regions(pdev, "pata-macio")) { dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); return -EBUSY; diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index 8119caaad605..deab67328388 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -99,7 +99,7 @@ static const struct scsi_host_template marvell_sht = { static struct ata_port_operations marvell_ops = { .inherits = &ata_bmdma_port_ops, .cable_detect = marvell_cable_detect, - .prereset = marvell_pre_reset, + .reset.prereset = marvell_pre_reset, }; diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 69e4baf27d72..ce310ae7c93a 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -145,7 +145,7 @@ static struct ata_port_operations mpiix_port_ops = { .qc_issue = mpiix_qc_issue, .cable_detect = ata_cable_40wire, .set_piomode = mpiix_set_piomode, - .prereset = mpiix_pre_reset, + .reset.prereset = mpiix_pre_reset, .sff_data_xfer = ata_sff_data_xfer32, }; diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 44cc24d21d5f..bdb55c1a3280 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -123,7 +123,7 @@ static struct ata_port_operations ns87410_port_ops = { .qc_issue = ns87410_qc_issue, .cable_detect = ata_cable_40wire, .set_piomode = ns87410_set_piomode, - .prereset = ns87410_pre_reset, + .reset.prereset = ns87410_pre_reset, }; static int ns87410_init_one(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 2d32125c16fd..df42ebe98db7 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -941,7 +941,7 @@ static int octeon_cf_probe(struct platform_device *pdev) /* 16 bit but not True IDE */ base = cs0 + 0x800; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16; - octeon_cf_ops.softreset = octeon_cf_softreset16; + octeon_cf_ops.reset.softreset = octeon_cf_softreset16; octeon_cf_ops.sff_check_status = octeon_cf_check_status16; octeon_cf_ops.sff_tf_read = octeon_cf_tf_read16; octeon_cf_ops.sff_tf_load = octeon_cf_tf_load16; diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 3d01b7000e41..81a7f3eb5654 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -214,7 +214,7 @@ static struct ata_port_operations oldpiix_pata_ops = { .cable_detect = ata_cable_40wire, .set_piomode = oldpiix_set_piomode, .set_dmamode = oldpiix_set_dmamode, - .prereset = oldpiix_pre_reset, + .reset.prereset = oldpiix_pre_reset, }; diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index 3d23f57eb128..3db1b95d1404 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -156,7 +156,7 @@ static struct ata_port_operations opti_port_ops = { .inherits = &ata_sff_port_ops, .cable_detect = ata_cable_40wire, .set_piomode = opti_set_piomode, - .prereset = opti_pre_reset, + .reset.prereset = opti_pre_reset, }; static int opti_init_one(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index dfc36b4ec9c6..b42dba5f4e05 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -322,7 +322,9 @@ static int optidma_set_mode(struct ata_link *link, struct ata_device **r_failed) u8 r; int nybble = 4 * ap->port_no; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - int rc = ata_do_set_mode(link, r_failed); + int rc; + + rc = ata_set_mode(link, r_failed); if (rc == 0) { pci_read_config_byte(pdev, 0x43, &r); @@ -344,7 +346,7 @@ static struct ata_port_operations optidma_port_ops = { .set_piomode = optidma_set_pio_mode, .set_dmamode = optidma_set_dma_mode, .set_mode = optidma_set_mode, - .prereset = optidma_pre_reset, + .reset.prereset = optidma_pre_reset, }; static struct ata_port_operations optiplus_port_ops = { diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c index 93ebf566b54e..22bd3ff6b7ae 100644 --- a/drivers/ata/pata_parport/pata_parport.c +++ b/drivers/ata/pata_parport/pata_parport.c @@ -321,8 +321,8 @@ static void pata_parport_drain_fifo(struct ata_queued_cmd *qc) static struct ata_port_operations pata_parport_port_ops = { .inherits = &ata_sff_port_ops, - .softreset = pata_parport_softreset, - .hardreset = NULL, + .reset.softreset = pata_parport_softreset, + .reset.hardreset = NULL, .sff_dev_select = pata_parport_dev_select, .sff_set_devctl = pata_parport_set_devctl, diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 5b602206c522..cf3810933a27 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -46,7 +46,7 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d struct ata_device *slave = &link->device[1]; if (!ata_dev_enabled(master) || !ata_dev_enabled(slave)) - return ata_do_set_mode(link, r_failed_dev); + return ata_set_mode(link, r_failed_dev); if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV, ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0) { @@ -58,7 +58,7 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d ata_dev_disable(slave); } } - return ata_do_set_mode(link, r_failed_dev); + return ata_set_mode(link, r_failed_dev); } /** diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 6820c5597b14..ae914dcb0c83 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -130,7 +130,7 @@ static struct ata_port_operations pdc2027x_pata100_ops = { .inherits = &ata_bmdma_port_ops, .check_atapi_dma = pdc2027x_check_atapi_dma, .cable_detect = pdc2027x_cable_detect, - .prereset = pdc2027x_prereset, + .reset.prereset = pdc2027x_prereset, }; static struct ata_port_operations pdc2027x_pata133_ops = { @@ -295,7 +295,7 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev) } /* Set the PIO timing registers using value table for 133MHz */ - ata_port_dbg(ap, "Set pio regs... \n"); + ata_port_dbg(ap, "Set PIO regs...\n"); ctcr0 = ioread32(dev_mmio(ap, adev, PDC_CTCR0)); ctcr0 &= 0xffff0000; @@ -308,7 +308,7 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev) ctcr1 |= (pdc2027x_pio_timing_tbl[pio].value2 << 24); iowrite32(ctcr1, dev_mmio(ap, adev, PDC_CTCR1)); - ata_port_dbg(ap, "Set to pio mode[%u] \n", pio); + ata_port_dbg(ap, "Set to PIO mode[%u]\n", pio); } /** @@ -341,7 +341,7 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev) iowrite32(ctcr1 & ~(1 << 7), dev_mmio(ap, adev, PDC_CTCR1)); } - ata_port_dbg(ap, "Set udma regs... \n"); + ata_port_dbg(ap, "Set UDMA regs...\n"); ctcr1 = ioread32(dev_mmio(ap, adev, PDC_CTCR1)); ctcr1 &= 0xff000000; @@ -350,14 +350,14 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev) (pdc2027x_udma_timing_tbl[udma_mode].value2 << 16); iowrite32(ctcr1, dev_mmio(ap, adev, PDC_CTCR1)); - ata_port_dbg(ap, "Set to udma mode[%u] \n", udma_mode); + ata_port_dbg(ap, "Set to UDMA mode[%u]\n", udma_mode); } else if ((dma_mode >= XFER_MW_DMA_0) && (dma_mode <= XFER_MW_DMA_2)) { /* Set the MDMA timing registers with value table for 133MHz */ unsigned int mdma_mode = dma_mode & 0x07; - ata_port_dbg(ap, "Set mdma regs... \n"); + ata_port_dbg(ap, "Set MDMA regs...\n"); ctcr0 = ioread32(dev_mmio(ap, adev, PDC_CTCR0)); ctcr0 &= 0x0000ffff; @@ -366,7 +366,7 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev) iowrite32(ctcr0, dev_mmio(ap, adev, PDC_CTCR0)); - ata_port_dbg(ap, "Set to mdma mode[%u] \n", mdma_mode); + ata_port_dbg(ap, "Set to MDMA mode[%u]\n", mdma_mode); } else { ata_port_err(ap, "Unknown dma mode [%u] ignored\n", dma_mode); } @@ -387,7 +387,7 @@ static int pdc2027x_set_mode(struct ata_link *link, struct ata_device **r_failed struct ata_device *dev; int rc; - rc = ata_do_set_mode(link, r_failed); + rc = ata_set_mode(link, r_failed); if (rc < 0) return rc; diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c index 09792aac7f9d..6ff4c11e937d 100644 --- a/drivers/ata/pata_rdc.c +++ b/drivers/ata/pata_rdc.c @@ -276,7 +276,7 @@ static struct ata_port_operations rdc_pata_ops = { .cable_detect = rdc_pata_cable_detect, .set_piomode = rdc_set_piomode, .set_dmamode = rdc_set_dmamode, - .prereset = rdc_pata_prereset, + .reset.prereset = rdc_pata_prereset, }; static const struct ata_port_info rdc_port_info = { @@ -359,8 +359,8 @@ static void rdc_remove_one(struct pci_dev *pdev) } static const struct pci_device_id rdc_pci_tbl[] = { - { PCI_DEVICE(0x17F3, 0x1011), }, - { PCI_DEVICE(0x17F3, 0x1012), }, + { PCI_VDEVICE(RDC, 0x1011) }, + { PCI_VDEVICE(RDC, 0x1012) }, { } /* terminate list */ }; diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 31de06b66221..2b751e393771 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -552,7 +552,7 @@ static struct ata_port_operations sis_133_for_sata_ops = { static struct ata_port_operations sis_base_ops = { .inherits = &ata_bmdma_port_ops, - .prereset = sis_pre_reset, + .reset.prereset = sis_pre_reset, }; static struct ata_port_operations sis_133_ops = { diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 93882e976ede..2d24c6b3e9d9 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -248,7 +248,7 @@ static struct ata_port_operations sl82c105_port_ops = { .bmdma_stop = sl82c105_bmdma_stop, .cable_detect = ata_cable_40wire, .set_piomode = sl82c105_set_piomode, - .prereset = sl82c105_pre_reset, + .reset.prereset = sl82c105_pre_reset, .sff_irq_check = sl82c105_sff_irq_check, }; diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index 26d448a869e2..596e86a031b3 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -170,7 +170,7 @@ static struct ata_port_operations triflex_port_ops = { .bmdma_stop = triflex_bmdma_stop, .cable_detect = ata_cable_40wire, .set_piomode = triflex_set_piomode, - .prereset = triflex_prereset, + .reset.prereset = triflex_prereset, }; static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 696b99720dcb..a8c9cf685b4b 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -201,11 +201,9 @@ static int via_cable_detect(struct ata_port *ap) { two drives */ if (ata66 & (0x10100000 >> (16 * ap->port_no))) return ATA_CBL_PATA80; + /* Check with ACPI so we can spot BIOS reported SATA bridges */ - if (ata_acpi_init_gtm(ap) && - ata_acpi_cbl_80wire(ap, ata_acpi_init_gtm(ap))) - return ATA_CBL_PATA80; - return ATA_CBL_PATA40; + return ata_acpi_cbl_pata_type(ap); } static int via_pre_reset(struct ata_link *link, unsigned long deadline) @@ -368,7 +366,8 @@ static unsigned int via_mode_filter(struct ata_device *dev, unsigned int mask) } if (dev->class == ATA_DEV_ATAPI && - dmi_check_system(no_atapi_dma_dmi_table)) { + (dmi_check_system(no_atapi_dma_dmi_table) || + config->id == PCI_DEVICE_ID_VIA_6415)) { ata_dev_warn(dev, "controller locks up on ATAPI DMA, forcing PIO\n"); mask &= ATA_MASK_PIO; } @@ -452,7 +451,7 @@ static struct ata_port_operations via_port_ops = { .cable_detect = via_cable_detect, .set_piomode = via_set_piomode, .set_dmamode = via_set_dmamode, - .prereset = via_pre_reset, + .reset.prereset = via_pre_reset, .sff_tf_load = via_tf_load, .port_start = via_port_start, .mode_filter = via_mode_filter, diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 8e6b2599f0d5..17a5a59861c3 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -140,7 +140,7 @@ static struct ata_port_operations adma_ata_ops = { .freeze = adma_freeze, .thaw = adma_thaw, - .prereset = adma_prereset, + .reset.prereset = adma_prereset, .port_start = adma_port_start, .port_stop = adma_port_stop, diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 6e1dd0d9c035..7a4f59202156 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1097,7 +1097,7 @@ static struct ata_port_operations sata_dwc_ops = { .inherits = &ata_sff_port_ops, .error_handler = sata_dwc_error_handler, - .hardreset = sata_dwc_hardreset, + .reset.hardreset = sata_dwc_hardreset, .qc_issue = sata_dwc_qc_issue, diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 87e91a937a44..84da8d6ef28e 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1395,9 +1395,9 @@ static struct ata_port_operations sata_fsl_ops = { .freeze = sata_fsl_freeze, .thaw = sata_fsl_thaw, - .softreset = sata_fsl_softreset, - .hardreset = sata_fsl_hardreset, - .pmp_softreset = sata_fsl_softreset, + .reset.softreset = sata_fsl_softreset, + .reset.hardreset = sata_fsl_hardreset, + .pmp_reset.softreset = sata_fsl_softreset, .error_handler = sata_fsl_error_handler, .post_internal_cmd = sata_fsl_post_internal_cmd, diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index c8c817c51230..3421039f4bae 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -428,7 +428,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, static struct ata_port_operations ahci_highbank_ops = { .inherits = &ahci_ops, - .hardreset = ahci_highbank_hardreset, + .reset.hardreset = ahci_highbank_hardreset, .transmit_led_message = ecx_transmit_led_message, }; diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index db9c255dc9f2..46a8c20daf18 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -730,7 +730,7 @@ static struct ata_port_operations inic_port_ops = { .freeze = inic_freeze, .thaw = inic_thaw, - .hardreset = inic_hardreset, + .reset.hardreset = inic_hardreset, .error_handler = inic_error_handler, .post_internal_cmd = inic_post_internal_cmd, diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index bcbf96867f89..ffb396f61731 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -687,7 +687,7 @@ static struct ata_port_operations mv5_ops = { .freeze = mv_eh_freeze, .thaw = mv_eh_thaw, - .hardreset = mv_hardreset, + .reset.hardreset = mv_hardreset, .scr_read = mv5_scr_read, .scr_write = mv5_scr_write, @@ -709,10 +709,10 @@ static struct ata_port_operations mv6_ops = { .freeze = mv_eh_freeze, .thaw = mv_eh_thaw, - .hardreset = mv_hardreset, - .softreset = mv_softreset, - .pmp_hardreset = mv_pmp_hardreset, - .pmp_softreset = mv_softreset, + .reset.hardreset = mv_hardreset, + .reset.softreset = mv_softreset, + .pmp_reset.hardreset = mv_pmp_hardreset, + .pmp_reset.softreset = mv_softreset, .error_handler = mv_pmp_error_handler, .scr_read = mv_scr_read, diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index f36e2915ccf1..841e7de2bba6 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -462,7 +462,7 @@ static struct ata_port_operations nv_generic_ops = { .lost_interrupt = ATA_OP_NULL, .scr_read = nv_scr_read, .scr_write = nv_scr_write, - .hardreset = nv_hardreset, + .reset.hardreset = nv_hardreset, }; static struct ata_port_operations nv_nf2_ops = { diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 2df1a070b25a..2a005aede123 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -188,7 +188,7 @@ static struct ata_port_operations pdc_sata_ops = { .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, .port_start = pdc_sata_port_start, - .hardreset = pdc_sata_hardreset, + .reset.hardreset = pdc_sata_hardreset, }; /* First-generation chips need a more restrictive ->check_atapi_dma op, @@ -206,7 +206,7 @@ static struct ata_port_operations pdc_pata_ops = { .freeze = pdc_freeze, .thaw = pdc_thaw, .port_start = pdc_common_port_start, - .softreset = pdc_pata_softreset, + .reset.softreset = pdc_pata_softreset, }; static const struct ata_port_info pdc_port_info[] = { diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 8a6286159044..cfb9b5b61cd7 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -123,8 +123,8 @@ static struct ata_port_operations qs_ata_ops = { .freeze = qs_freeze, .thaw = qs_thaw, - .prereset = qs_prereset, - .softreset = ATA_OP_NULL, + .reset.prereset = qs_prereset, + .reset.softreset = ATA_OP_NULL, .error_handler = qs_error_handler, .lost_interrupt = ATA_OP_NULL, diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 22820a02d740..487eadd4073f 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -624,7 +624,7 @@ static struct ata_port_operations sata_rcar_port_ops = { .freeze = sata_rcar_freeze, .thaw = sata_rcar_thaw, - .softreset = sata_rcar_softreset, + .reset.softreset = sata_rcar_softreset, .scr_read = sata_rcar_scr_read, .scr_write = sata_rcar_scr_write, diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 3a99f66198a9..1b6dc950a42a 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -351,7 +351,7 @@ static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed) u32 tmp, dev_mode[2] = { }; int rc; - rc = ata_do_set_mode(link, r_failed); + rc = ata_set_mode(link, r_failed); if (rc) return rc; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 87f4cde6a686..d642ece9f07a 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -393,10 +393,10 @@ static struct ata_port_operations sil24_ops = { .freeze = sil24_freeze, .thaw = sil24_thaw, - .softreset = sil24_softreset, - .hardreset = sil24_hardreset, - .pmp_softreset = sil24_softreset, - .pmp_hardreset = sil24_pmp_hardreset, + .reset.softreset = sil24_softreset, + .reset.hardreset = sil24_hardreset, + .pmp_reset.softreset = sil24_softreset, + .pmp_reset.hardreset = sil24_pmp_hardreset, .error_handler = sil24_error_handler, .post_internal_cmd = sil24_post_internal_cmd, .dev_config = sil24_dev_config, diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index 598a872f6a08..c5d6aa36c9c3 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -340,8 +340,8 @@ static const struct scsi_host_template k2_sata_sht = { static struct ata_port_operations k2_sata_ops = { .inherits = &ata_bmdma_port_ops, - .softreset = k2_sata_softreset, - .hardreset = k2_sata_hardreset, + .reset.softreset = k2_sata_softreset, + .reset.hardreset = k2_sata_hardreset, .sff_tf_load = k2_sata_tf_load, .sff_tf_read = k2_sata_tf_read, .sff_check_status = k2_stat_check_status, diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index f7f5131af937..0986ebd1eb4e 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -241,7 +241,7 @@ static struct ata_port_operations pdc_20621_ops = { .freeze = pdc_freeze, .thaw = pdc_thaw, - .softreset = pdc_softreset, + .reset.softreset = pdc_softreset, .error_handler = pdc_error_handler, .lost_interrupt = ATA_OP_NULL, .post_internal_cmd = pdc_post_internal_cmd, diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index 52894ff49dcb..44985796cc47 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -67,7 +67,7 @@ static struct ata_port_operations uli_ops = { .inherits = &ata_bmdma_port_ops, .scr_read = uli_scr_read, .scr_write = uli_scr_write, - .hardreset = ATA_OP_NULL, + .reset.hardreset = ATA_OP_NULL, }; static const struct ata_port_info uli_port_info = { diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 4ecd8f33b082..68e9003ec2d4 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -120,7 +120,7 @@ static struct ata_port_operations svia_base_ops = { static struct ata_port_operations vt6420_sata_ops = { .inherits = &svia_base_ops, .freeze = svia_noop_freeze, - .prereset = vt6420_prereset, + .reset.prereset = vt6420_prereset, .bmdma_start = vt6420_bmdma_start, }; @@ -140,7 +140,7 @@ static struct ata_port_operations vt6421_sata_ops = { static struct ata_port_operations vt8251_ops = { .inherits = &svia_base_ops, - .hardreset = sata_std_hardreset, + .reset.hardreset = sata_std_hardreset, .scr_read = vt8251_scr_read, .scr_write = vt8251_scr_write, }; diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c index d4aa0f353b6c..fa3c76a2b49d 100644 --- a/drivers/atm/atmtcp.c +++ b/drivers/atm/atmtcp.c @@ -279,6 +279,19 @@ static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) 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) { @@ -288,7 +301,6 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) struct sk_buff *new_skb; int result = 0; - if (!skb->len) return 0; dev = vcc->dev_data; hdr = (struct atmtcp_hdr *) skb->data; if (hdr->length == ATMTCP_HDR_MAGIC) { @@ -345,6 +357,7 @@ static const struct atmdev_ops atmtcp_v_dev_ops = { static const struct atmdev_ops atmtcp_c_dev_ops = { .close = atmtcp_c_close, + .pre_send = atmtcp_c_pre_send, .send = atmtcp_c_send }; diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 1206ab764ba9..f2e91b7d79f0 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -852,6 +852,8 @@ queue_skb(struct idt77252_dev *card, struct vc_map *vc, 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; @@ -1857,6 +1859,8 @@ add_rx_skb(struct idt77252_dev *card, int queue, 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)) { @@ -1871,6 +1875,7 @@ add_rx_skb(struct idt77252_dev *card, int queue, dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE); +outpoolrm: handle = IDT77252_PRV_POOL(skb); card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL; diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 2a1fe3080712..0dfa2cdc897c 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -755,7 +755,7 @@ static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) /* 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 awhile -- up to 370ms for a full 128KB buffer + * 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. */ diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index dba7c8e13a53..12ffdd843756 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -217,7 +217,7 @@ static int auxiliary_bus_probe(struct device *dev) struct auxiliary_device *auxdev = to_auxiliary_dev(dev); int ret; - ret = dev_pm_domain_attach(dev, true); + ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON); if (ret) { dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret); return ret; @@ -399,6 +399,7 @@ static void auxiliary_device_release(struct device *dev) { struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + of_node_put(dev->of_node); kfree(auxdev); } @@ -435,6 +436,7 @@ struct auxiliary_device *auxiliary_device_create(struct device *dev, ret = auxiliary_device_init(auxdev); if (ret) { + of_node_put(auxdev->dev.of_node); kfree(auxdev); return NULL; } diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index cf0d455209d7..613410705a47 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -183,6 +184,54 @@ static bool cache_node_is_unified(struct cacheinfo *this_leaf, return of_property_read_bool(np, "cache-unified"); } +static bool match_cache_node(struct device_node *cpu, + const struct device_node *cache_node) +{ + struct device_node *prev, *cache = of_find_next_cache_node(cpu); + + while (cache) { + if (cache == cache_node) { + of_node_put(cache); + return true; + } + + prev = cache; + cache = of_find_next_cache_node(cache); + of_node_put(prev); + } + + return false; +} + +#ifndef arch_compact_of_hwid +#define arch_compact_of_hwid(_x) (_x) +#endif + +static void cache_of_set_id(struct cacheinfo *this_leaf, + struct device_node *cache_node) +{ + struct device_node *cpu; + u32 min_id = ~0; + + for_each_of_cpu_node(cpu) { + u64 id = of_get_cpu_hwid(cpu, 0); + + id = arch_compact_of_hwid(id); + if (FIELD_GET(GENMASK_ULL(63, 32), id)) { + of_node_put(cpu); + return; + } + + if (match_cache_node(cpu, cache_node)) + min_id = min(min_id, id); + } + + if (min_id != ~0) { + this_leaf->id = min_id; + this_leaf->attributes |= CACHE_ID; + } +} + static void cache_of_set_props(struct cacheinfo *this_leaf, struct device_node *np) { @@ -198,6 +247,7 @@ static void cache_of_set_props(struct cacheinfo *this_leaf, cache_get_line_size(this_leaf, np); cache_nr_sets(this_leaf, np); cache_associativity(this_leaf); + cache_of_set_id(this_leaf, np); } static int cache_setup_of_node(unsigned int cpu) diff --git a/drivers/base/core.c b/drivers/base/core.c index cbc0099d8ef2..d22d6b23e758 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -460,9 +460,9 @@ static ssize_t auto_remove_on_show(struct device *dev, struct device_link *link = to_devlink(dev); const char *output; - if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) + if (device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER)) output = "supplier unbind"; - else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) + else if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) output = "consumer unbind"; else output = "never"; @@ -476,7 +476,7 @@ static ssize_t runtime_pm_show(struct device *dev, { struct device_link *link = to_devlink(dev); - return sysfs_emit(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME)); + return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_PM_RUNTIME)); } static DEVICE_ATTR_RO(runtime_pm); @@ -485,8 +485,7 @@ static ssize_t sync_state_only_show(struct device *dev, { struct device_link *link = to_devlink(dev); - return sysfs_emit(buf, "%d\n", - !!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); } static DEVICE_ATTR_RO(sync_state_only); @@ -792,12 +791,12 @@ struct device_link *device_link_add(struct device *consumer, if (link->consumer != consumer) continue; - if (link->flags & DL_FLAG_INFERRED && + if (device_link_test(link, DL_FLAG_INFERRED) && !(flags & DL_FLAG_INFERRED)) link->flags &= ~DL_FLAG_INFERRED; if (flags & DL_FLAG_PM_RUNTIME) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) { + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) { pm_runtime_new_link(consumer); link->flags |= DL_FLAG_PM_RUNTIME; } @@ -807,8 +806,8 @@ struct device_link *device_link_add(struct device *consumer, if (flags & DL_FLAG_STATELESS) { kref_get(&link->kref); - if (link->flags & DL_FLAG_SYNC_STATE_ONLY && - !(link->flags & DL_FLAG_STATELESS)) { + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) && + !device_link_test(link, DL_FLAG_STATELESS)) { link->flags |= DL_FLAG_STATELESS; goto reorder; } else { @@ -823,7 +822,7 @@ struct device_link *device_link_add(struct device *consumer, * update the existing link to stay around longer. */ if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) { - if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) { + if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) { link->flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER; link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER; } @@ -831,12 +830,12 @@ struct device_link *device_link_add(struct device *consumer, link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER); } - if (!(link->flags & DL_FLAG_MANAGED)) { + if (!device_link_test(link, DL_FLAG_MANAGED)) { kref_get(&link->kref); link->flags |= DL_FLAG_MANAGED; device_link_init_status(link, consumer, supplier); } - if (link->flags & DL_FLAG_SYNC_STATE_ONLY && + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) && !(flags & DL_FLAG_SYNC_STATE_ONLY)) { link->flags &= ~DL_FLAG_SYNC_STATE_ONLY; goto reorder; @@ -940,7 +939,7 @@ static void __device_link_del(struct kref *kref) static void device_link_put_kref(struct device_link *link) { - if (link->flags & DL_FLAG_STATELESS) + if (device_link_test(link, DL_FLAG_STATELESS)) kref_put(&link->kref, __device_link_del); else if (!device_is_registered(link->consumer)) __device_link_del(&link->kref); @@ -1004,7 +1003,7 @@ static void device_links_missing_supplier(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } @@ -1072,14 +1071,14 @@ int device_links_check_suppliers(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_AVAILABLE && - !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) { + !device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) { if (dev_is_best_effort(dev) && - link->flags & DL_FLAG_INFERRED && + device_link_test(link, DL_FLAG_INFERRED) && !link->supplier->can_match) { ret = -EAGAIN; continue; @@ -1128,7 +1127,7 @@ static void __device_links_queue_sync_state(struct device *dev, return; list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_ACTIVE) return; @@ -1268,7 +1267,7 @@ void device_links_force_bind(struct device *dev) device_links_write_lock(); list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_AVAILABLE) { @@ -1329,7 +1328,7 @@ void device_links_driver_bound(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; /* @@ -1345,7 +1344,7 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_DORMANT); WRITE_ONCE(link->status, DL_STATE_AVAILABLE); - if (link->flags & DL_FLAG_AUTOPROBE_CONSUMER) + if (device_link_test(link, DL_FLAG_AUTOPROBE_CONSUMER)) driver_deferred_probe_add(link->consumer); } @@ -1357,11 +1356,11 @@ void device_links_driver_bound(struct device *dev) list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { struct device *supplier; - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; supplier = link->supplier; - if (link->flags & DL_FLAG_SYNC_STATE_ONLY) { + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) { /* * When DL_FLAG_SYNC_STATE_ONLY is set, it means no * other DL_MANAGED_LINK_FLAGS have been set. So, it's @@ -1369,7 +1368,7 @@ void device_links_driver_bound(struct device *dev) */ device_link_drop_managed(link); } else if (dev_is_best_effort(dev) && - link->flags & DL_FLAG_INFERRED && + device_link_test(link, DL_FLAG_INFERRED) && link->status != DL_STATE_CONSUMER_PROBE && !link->supplier->can_match) { /* @@ -1421,10 +1420,10 @@ static void __device_links_no_driver(struct device *dev) struct device_link *link, *ln; list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; - if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) { + if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) { device_link_drop_managed(link); continue; } @@ -1436,7 +1435,7 @@ static void __device_links_no_driver(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } @@ -1461,7 +1460,7 @@ void device_links_no_driver(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; /* @@ -1498,10 +1497,10 @@ void device_links_driver_cleanup(struct device *dev) device_links_write_lock(); list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; - WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER); + WARN_ON(device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)); WARN_ON(link->status != DL_STATE_SUPPLIER_UNBIND); /* @@ -1510,7 +1509,7 @@ void device_links_driver_cleanup(struct device *dev) * has moved to DL_STATE_SUPPLIER_UNBIND. */ if (link->status == DL_STATE_SUPPLIER_UNBIND && - link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) + device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER)) device_link_drop_managed(link); WRITE_ONCE(link->status, DL_STATE_DORMANT); @@ -1544,7 +1543,7 @@ bool device_links_busy(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status == DL_STATE_CONSUMER_PROBE @@ -1586,8 +1585,8 @@ void device_links_unbind_consumers(struct device *dev) list_for_each_entry(link, &dev->links.consumers, s_node) { enum device_link_state status; - if (!(link->flags & DL_FLAG_MANAGED) || - link->flags & DL_FLAG_SYNC_STATE_ONLY) + if (!device_link_test(link, DL_FLAG_MANAGED) || + device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) continue; status = link->status; @@ -1743,7 +1742,7 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) static void fw_devlink_relax_link(struct device_link *link) { - if (!(link->flags & DL_FLAG_INFERRED)) + if (!device_link_test(link, DL_FLAG_INFERRED)) return; if (device_link_flag_is_sync_state_only(link->flags)) @@ -1779,7 +1778,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) struct device_link *link = to_devlink(dev); struct device *sup = link->supplier; - if (!(link->flags & DL_FLAG_MANAGED) || + if (!device_link_test(link, DL_FLAG_MANAGED) || link->status == DL_STATE_ACTIVE || sup->state_synced || !dev_has_sync_state(sup)) return 0; @@ -1881,8 +1880,6 @@ static void fw_devlink_unblock_consumers(struct device *dev) device_links_write_unlock(); } -#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) - static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) { struct device *dev; @@ -2063,7 +2060,7 @@ static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle, * such due to a cycle. */ if (device_link_flag_is_sync_state_only(dev_link->flags) && - !(dev_link->flags & DL_FLAG_CYCLE)) + !device_link_test(dev_link, DL_FLAG_CYCLE)) continue; if (__fw_devlink_relax_cycles(con_handle, @@ -5281,6 +5278,12 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(device_set_node); +struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode) +{ + return get_device((fwnode)->dev); +} +EXPORT_SYMBOL_GPL(get_dev_from_fwnode); + int device_match_name(struct device *dev, const void *name) { return sysfs_streq(dev_name(dev), name); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 7779ab0ca7ce..008da0354fba 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -602,6 +602,8 @@ CPU_SHOW_VULN_FALLBACK(reg_file_data_sampling); CPU_SHOW_VULN_FALLBACK(ghostwrite); CPU_SHOW_VULN_FALLBACK(old_microcode); CPU_SHOW_VULN_FALLBACK(indirect_target_selection); +CPU_SHOW_VULN_FALLBACK(tsa); +CPU_SHOW_VULN_FALLBACK(vmscape); static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL); static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL); @@ -620,6 +622,8 @@ static DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling static DEVICE_ATTR(ghostwrite, 0444, cpu_show_ghostwrite, NULL); static DEVICE_ATTR(old_microcode, 0444, cpu_show_old_microcode, NULL); static DEVICE_ATTR(indirect_target_selection, 0444, cpu_show_indirect_target_selection, NULL); +static DEVICE_ATTR(tsa, 0444, cpu_show_tsa, NULL); +static DEVICE_ATTR(vmscape, 0444, cpu_show_vmscape, NULL); static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_meltdown.attr, @@ -639,6 +643,8 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_ghostwrite.attr, &dev_attr_old_microcode.attr, &dev_attr_indirect_target_selection.attr, + &dev_attr_tsa.attr, + &dev_attr_vmscape.attr, NULL }; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b526e0e0f52d..13ab98e033ea 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -552,6 +553,7 @@ static void device_unbind_cleanup(struct device *dev) dev->dma_range_map = NULL; device_set_driver(dev, NULL); dev_set_drvdata(dev, NULL); + dev_pm_domain_detach(dev, dev->power.detach_power_off); if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 03a39c417dc4..37faf6156d7c 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -140,7 +140,7 @@ static const struct bin_attribute *const devcd_dev_bin_attrs[] = { }; static const struct attribute_group devcd_dev_group = { - .bin_attrs_new = devcd_dev_bin_attrs, + .bin_attrs = devcd_dev_bin_attrs, }; static const struct attribute_group *devcd_dev_groups[] = { diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 44486b2c7172..6942c62fa59d 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -822,26 +822,6 @@ static void fw_log_firmware_info(const struct firmware *fw, const char *name, {} #endif -/* - * Reject firmware file names with ".." path components. - * There are drivers that construct firmware file names from device-supplied - * strings, and we don't want some device to be able to tell us "I would like to - * be sent my firmware from ../../../etc/shadow, please". - * - * Search for ".." surrounded by either '/' or start/end of string. - * - * This intentionally only looks at the firmware name, not at the firmware base - * directory or at symlink contents. - */ -static bool name_contains_dotdot(const char *name) -{ - size_t name_len = strlen(name); - - return strcmp(name, "..") == 0 || strncmp(name, "../", 3) == 0 || - strstr(name, "/../") != NULL || - (name_len >= 3 && strcmp(name+name_len-3, "/..") == 0); -} - /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, @@ -862,6 +842,17 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } + + /* + * Reject firmware file names with ".." path components. + * There are drivers that construct firmware file names from + * device-supplied strings, and we don't want some device to be + * able to tell us "I would like to be sent my firmware from + * ../../../etc/shadow, please". + * + * This intentionally only looks at the firmware name, not at + * the firmware base directory or at symlink contents. + */ if (name_contains_dotdot(name)) { dev_warn(device, "Firmware load for '%s' refused, path contains '..' component\n", diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index d254ceb56d84..add0b9b75edd 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -359,8 +359,8 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, static const struct bin_attribute firmware_attr_data = { .attr = { .name = "data", .mode = 0644 }, .size = 0, - .read_new = firmware_data_read, - .write_new = firmware_data_write, + .read = firmware_data_read, + .write = firmware_data_write, }; static struct attribute *fw_dev_attrs[] = { @@ -381,7 +381,7 @@ static const struct bin_attribute *const fw_dev_bin_attrs[] = { static const struct attribute_group fw_dev_attr_group = { .attrs = fw_dev_attrs, - .bin_attrs_new = fw_dev_bin_attrs, + .bin_attrs = fw_dev_bin_attrs, #ifdef CONFIG_FW_UPLOAD .is_visible = fw_upload_is_visible, #endif diff --git a/drivers/base/memory.c b/drivers/base/memory.c index ed3e69dc785c..5c6c1d6bb59f 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -48,22 +49,8 @@ int mhp_online_type_from_str(const char *str) #define to_memory_block(dev) container_of(dev, struct memory_block, dev) -static int sections_per_block; - -static inline unsigned long memory_block_id(unsigned long section_nr) -{ - return section_nr / sections_per_block; -} - -static inline unsigned long pfn_to_block_id(unsigned long pfn) -{ - return memory_block_id(pfn_to_section_nr(pfn)); -} - -static inline unsigned long phys_to_block_id(unsigned long phys) -{ - return pfn_to_block_id(PFN_DOWN(phys)); -} +int sections_per_block; +EXPORT_SYMBOL(sections_per_block); static int memory_subsys_online(struct device *dev); static int memory_subsys_offline(struct device *dev); @@ -683,7 +670,7 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn) * * Called under device_hotplug_lock. */ -static struct memory_block *find_memory_block_by_id(unsigned long block_id) +struct memory_block *find_memory_block_by_id(unsigned long block_id) { struct memory_block *mem; diff --git a/drivers/base/node.c b/drivers/base/node.c index c19094481630..3399594136b2 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -21,6 +21,7 @@ #include #include #include +#include static const struct bus_type node_subsys = { .name = "node", @@ -111,6 +112,27 @@ static const struct attribute_group *node_access_node_groups[] = { NULL, }; +#ifdef CONFIG_MEMORY_HOTPLUG +static BLOCKING_NOTIFIER_HEAD(node_chain); + +int register_node_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&node_chain, nb); +} +EXPORT_SYMBOL(register_node_notifier); + +void unregister_node_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&node_chain, nb); +} +EXPORT_SYMBOL(unregister_node_notifier); + +int node_notify(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&node_chain, val, v); +} +#endif + static void node_remove_accesses(struct node *node) { struct node_access_nodes *c, *cnext; @@ -478,7 +500,7 @@ static ssize_t node_read_meminfo(struct device *dev, nid, K(node_page_state(pgdat, NR_SECONDARY_PAGETABLE)), nid, 0UL, nid, 0UL, - nid, K(node_page_state(pgdat, NR_WRITEBACK_TEMP)), + nid, 0UL, nid, K(sreclaimable + node_page_state(pgdat, NR_KERNEL_MISC_RECLAIMABLE)), nid, K(sreclaimable + sunreclaimable), @@ -597,7 +619,7 @@ static const struct bin_attribute *node_dev_bin_attrs[] = { static const struct attribute_group node_dev_group = { .attrs = node_dev_attrs, - .bin_attrs_new = node_dev_bin_attrs, + .bin_attrs = node_dev_bin_attrs, }; static const struct attribute_group *node_dev_groups[] = { @@ -637,6 +659,7 @@ static int register_node(struct node *node, int num) } else { hugetlb_register_node(node); compaction_register_node(node); + reclaim_register_node(node); } return error; @@ -653,6 +676,7 @@ void unregister_node(struct node *node) { hugetlb_unregister_node(node); compaction_unregister_node(node); + reclaim_unregister_node(node); node_remove_accesses(node); node_remove_caches(node); device_unregister(&node->dev); @@ -756,15 +780,6 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) } #ifdef CONFIG_MEMORY_HOTPLUG -static int __ref get_nid_for_pfn(unsigned long pfn) -{ -#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT - if (system_state < SYSTEM_RUNNING) - return early_pfn_to_nid(pfn); -#endif - return pfn_to_nid(pfn); -} - static void do_register_memory_block_under_node(int nid, struct memory_block *mem_blk, enum meminit_context context) @@ -791,46 +806,6 @@ static void do_register_memory_block_under_node(int nid, ret); } -/* register memory section under specified node if it spans that node */ -static int register_mem_block_under_node_early(struct memory_block *mem_blk, - void *arg) -{ - unsigned long memory_block_pfns = memory_block_size_bytes() / PAGE_SIZE; - unsigned long start_pfn = section_nr_to_pfn(mem_blk->start_section_nr); - unsigned long end_pfn = start_pfn + memory_block_pfns - 1; - int nid = *(int *)arg; - unsigned long pfn; - - for (pfn = start_pfn; pfn <= end_pfn; pfn++) { - int page_nid; - - /* - * memory block could have several absent sections from start. - * skip pfn range from absent section - */ - if (!pfn_in_present_section(pfn)) { - pfn = round_down(pfn + PAGES_PER_SECTION, - PAGES_PER_SECTION) - 1; - continue; - } - - /* - * We need to check if page belongs to nid only at the boot - * case because node's ranges can be interleaved. - */ - page_nid = get_nid_for_pfn(pfn); - if (page_nid < 0) - continue; - if (page_nid != nid) - continue; - - do_register_memory_block_under_node(nid, mem_blk, MEMINIT_EARLY); - return 0; - } - /* mem section does not span the specified node */ - return 0; -} - /* * During hotplug we know that all pages in the memory block belong to the same * node. @@ -859,24 +834,44 @@ void unregister_memory_block_under_nodes(struct memory_block *mem_blk) kobject_name(&node_devices[mem_blk->nid]->dev.kobj)); } -void register_memory_blocks_under_node(int nid, unsigned long start_pfn, - unsigned long end_pfn, - enum meminit_context context) +/* register all memory blocks under the corresponding nodes */ +static void register_memory_blocks_under_nodes(void) { - walk_memory_blocks_func_t func; + struct memblock_region *r; - if (context == MEMINIT_HOTPLUG) - func = register_mem_block_under_node_hotplug; - else - func = register_mem_block_under_node_early; + for_each_mem_region(r) { + const unsigned long start_block_id = phys_to_block_id(r->base); + const unsigned long end_block_id = phys_to_block_id(r->base + r->size - 1); + const int nid = memblock_get_region_node(r); + unsigned long block_id; + if (!node_online(nid)) + continue; + + for (block_id = start_block_id; block_id <= end_block_id; block_id++) { + struct memory_block *mem; + + mem = find_memory_block_by_id(block_id); + if (!mem) + continue; + + do_register_memory_block_under_node(nid, mem, MEMINIT_EARLY); + put_device(&mem->dev); + } + + } +} + +void register_memory_blocks_under_node_hotplug(int nid, unsigned long start_pfn, + unsigned long end_pfn) +{ walk_memory_blocks(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn), - (void *)&nid, func); + (void *)&nid, register_mem_block_under_node_hotplug); return; } #endif /* CONFIG_MEMORY_HOTPLUG */ -int __register_one_node(int nid) +int register_one_node(int nid) { int error; int cpu; @@ -980,11 +975,13 @@ void __init node_dev_init(void) /* * Create all node devices, which will properly link the node - * to applicable memory block devices and already created cpu devices. + * to already created cpu devices. */ for_each_online_node(i) { - ret = register_one_node(i); + ret = register_one_node(i); if (ret) panic("%s() failed to add node: %d\n", __func__, ret); } + + register_memory_blocks_under_nodes(); } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 075ec1d1b73a..09450349cf32 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1396,15 +1396,13 @@ static int platform_probe(struct device *_dev) if (ret < 0) return ret; - ret = dev_pm_domain_attach(_dev, true); + ret = dev_pm_domain_attach(_dev, PD_FLAG_ATTACH_POWER_ON | + PD_FLAG_DETACH_POWER_OFF); if (ret) goto out; - if (drv->probe) { + if (drv->probe) ret = drv->probe(dev); - if (ret) - dev_pm_domain_detach(_dev, true); - } out: if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { @@ -1422,7 +1420,6 @@ static void platform_remove(struct device *_dev) if (drv->remove) drv->remove(dev); - dev_pm_domain_detach(_dev, true); } static void platform_shutdown(struct device *_dev) diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 781968a128ff..6ecf9ce4a4e6 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -83,7 +83,7 @@ EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data); /** * dev_pm_domain_attach - Attach a device to its PM domain. * @dev: Device to attach. - * @power_on: Used to indicate whether we should power on the device. + * @flags: indicate whether we should power on/off the device on attach/detach * * The @dev may only be attached to a single PM domain. By iterating through * the available alternatives we try to find a valid PM domain for the device. @@ -100,17 +100,20 @@ EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data); * Returns 0 on successfully attached PM domain, or when it is found that the * device doesn't need a PM domain, else a negative error code. */ -int dev_pm_domain_attach(struct device *dev, bool power_on) +int dev_pm_domain_attach(struct device *dev, u32 flags) { int ret; if (dev->pm_domain) return 0; - ret = acpi_dev_pm_attach(dev, power_on); + ret = acpi_dev_pm_attach(dev, !!(flags & PD_FLAG_ATTACH_POWER_ON)); if (!ret) ret = genpd_dev_pm_attach(dev); + if (dev->pm_domain) + dev->power.detach_power_off = !!(flags & PD_FLAG_DETACH_POWER_OFF); + return ret < 0 ? ret : 0; } EXPORT_SYMBOL_GPL(dev_pm_domain_attach); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 63f3a48f434c..2ea6e05e6ec9 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -661,14 +661,27 @@ static void dpm_async_resume_children(struct device *dev, async_func_t func) /* * Start processing "async" children of the device unless it's been * started already for them. - * - * This could have been done for the device's "async" consumers too, but - * they either need to wait for their parents or the processing has - * already started for them after their parents were processed. */ device_for_each_child(dev, func, dpm_async_with_cleanup); } +static void dpm_async_resume_subordinate(struct device *dev, async_func_t func) +{ + struct device_link *link; + int idx; + + dpm_async_resume_children(dev, func); + + idx = device_links_read_lock(); + + /* Start processing the device's "async" consumers. */ + list_for_each_entry_rcu_locked(link, &dev->links.consumers, s_node) + if (READ_ONCE(link->status) != DL_STATE_DORMANT) + dpm_async_with_cleanup(link->consumer, func); + + device_links_read_unlock(idx); +} + static void dpm_clear_async_state(struct device *dev) { reinit_completion(&dev->power.completion); @@ -677,7 +690,14 @@ static void dpm_clear_async_state(struct device *dev) static bool dpm_root_device(struct device *dev) { - return !dev->parent; + lockdep_assert_held(&dpm_list_mtx); + + /* + * Since this function is required to run under dpm_list_mtx, the + * list_empty() below will only return true if the device's list of + * consumers is actually empty before calling it. + */ + return !dev->parent && list_empty(&dev->links.suppliers); } static void async_resume_noirq(void *data, async_cookie_t cookie); @@ -761,12 +781,12 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy TRACE_RESUME(error); if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async noirq" : " noirq", error); } - dpm_async_resume_children(dev, async_resume_noirq); + dpm_async_resume_subordinate(dev, async_resume_noirq); } static void async_resume_noirq(void *data, async_cookie_t cookie) @@ -818,7 +838,7 @@ static void dpm_noirq_resume_devices(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, 0, "noirq"); - if (async_error) + if (READ_ONCE(async_error)) dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); @@ -904,12 +924,12 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy complete_all(&dev->power.completion); if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async early" : " early", error); } - dpm_async_resume_children(dev, async_resume_early); + dpm_async_resume_subordinate(dev, async_resume_early); } static void async_resume_early(void *data, async_cookie_t cookie) @@ -965,7 +985,7 @@ void dpm_resume_early(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, 0, "early"); - if (async_error) + if (READ_ONCE(async_error)) dpm_save_failed_step(SUSPEND_RESUME_EARLY); trace_suspend_resume(TPS("dpm_resume_early"), state.event, false); @@ -1080,12 +1100,12 @@ static void device_resume(struct device *dev, pm_message_t state, bool async) TRACE_RESUME(error); if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async" : "", error); } - dpm_async_resume_children(dev, async_resume); + dpm_async_resume_subordinate(dev, async_resume); } static void async_resume(void *data, async_cookie_t cookie) @@ -1109,7 +1129,6 @@ void dpm_resume(pm_message_t state) ktime_t starttime = ktime_get(); trace_suspend_resume(TPS("dpm_resume"), state.event, true); - might_sleep(); pm_transition = state; async_error = 0; @@ -1145,7 +1164,7 @@ void dpm_resume(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, 0, NULL); - if (async_error) + if (READ_ONCE(async_error)) dpm_save_failed_step(SUSPEND_RESUME); cpufreq_resume(); @@ -1212,7 +1231,6 @@ void dpm_complete(pm_message_t state) struct list_head list; trace_suspend_resume(TPS("dpm_complete"), state.event, true); - might_sleep(); INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); @@ -1251,6 +1269,7 @@ void dpm_complete(pm_message_t state) void dpm_resume_end(pm_message_t state) { dpm_resume(state); + pm_restore_gfp_mask(); dpm_complete(state); } EXPORT_SYMBOL_GPL(dpm_resume_end); @@ -1271,10 +1290,15 @@ static bool dpm_leaf_device(struct device *dev) return false; } - return true; + /* + * Since this function is required to run under dpm_list_mtx, the + * list_empty() below will only return true if the device's list of + * consumers is actually empty before calling it. + */ + return list_empty(&dev->links.consumers); } -static void dpm_async_suspend_parent(struct device *dev, async_func_t func) +static bool dpm_async_suspend_parent(struct device *dev, async_func_t func) { guard(mutex)(&dpm_list_mtx); @@ -1286,11 +1310,47 @@ static void dpm_async_suspend_parent(struct device *dev, async_func_t func) * deleted before it. */ if (!device_pm_initialized(dev)) - return; + return false; /* Start processing the device's parent if it is "async". */ if (dev->parent) dpm_async_with_cleanup(dev->parent, func); + + return true; +} + +static void dpm_async_suspend_superior(struct device *dev, async_func_t func) +{ + struct device_link *link; + int idx; + + if (!dpm_async_suspend_parent(dev, func)) + return; + + idx = device_links_read_lock(); + + /* Start processing the device's "async" suppliers. */ + list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) + if (READ_ONCE(link->status) != DL_STATE_DORMANT) + dpm_async_with_cleanup(link->supplier, func); + + device_links_read_unlock(idx); +} + +static void dpm_async_suspend_complete_all(struct list_head *device_list) +{ + struct device *dev; + + guard(mutex)(&async_wip_mtx); + + list_for_each_entry_reverse(dev, device_list, power.entry) { + /* + * In case the device is being waited for and async processing + * has not started for it yet, let the waiters make progress. + */ + if (!dev->power.work_in_progress) + complete_all(&dev->power.completion); + } } /** @@ -1341,7 +1401,7 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie); * The driver of @dev will not receive interrupts while this function is being * executed. */ -static int device_suspend_noirq(struct device *dev, pm_message_t state, bool async) +static void device_suspend_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -1352,7 +1412,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy dpm_wait_for_subordinate(dev, async); - if (async_error) + if (READ_ONCE(async_error)) goto Complete; if (dev->power.syscore || dev->power.direct_complete) @@ -1385,7 +1445,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy Run: error = dpm_run_callback(callback, dev, state, info); if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async noirq" : " noirq", error); goto Complete; @@ -1411,12 +1471,10 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy complete_all(&dev->power.completion); TRACE_SUSPEND(error); - if (error || async_error) - return error; + if (error || READ_ONCE(async_error)) + return; - dpm_async_suspend_parent(dev, async_suspend_noirq); - - return 0; + dpm_async_suspend_superior(dev, async_suspend_noirq); } static void async_suspend_noirq(void *data, async_cookie_t cookie) @@ -1431,7 +1489,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state) { ktime_t starttime = ktime_get(); struct device *dev; - int error = 0; + int error; trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); @@ -1462,13 +1520,14 @@ static int dpm_noirq_suspend_devices(pm_message_t state) mutex_unlock(&dpm_list_mtx); - error = device_suspend_noirq(dev, state, false); + device_suspend_noirq(dev, state, false); put_device(dev); mutex_lock(&dpm_list_mtx); - if (error || async_error) { + if (READ_ONCE(async_error)) { + dpm_async_suspend_complete_all(&dpm_late_early_list); /* * Move all devices to the target list to resume them * properly. @@ -1481,9 +1540,8 @@ static int dpm_noirq_suspend_devices(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - if (!error) - error = async_error; + error = READ_ONCE(async_error); if (error) dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); @@ -1538,7 +1596,7 @@ static void async_suspend_late(void *data, async_cookie_t cookie); * * Runtime PM is disabled for @dev while this function is being executed. */ -static int device_suspend_late(struct device *dev, pm_message_t state, bool async) +static void device_suspend_late(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -1555,11 +1613,11 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn dpm_wait_for_subordinate(dev, async); - if (async_error) + if (READ_ONCE(async_error)) goto Complete; if (pm_wakeup_pending()) { - async_error = -EBUSY; + WRITE_ONCE(async_error, -EBUSY); goto Complete; } @@ -1593,7 +1651,7 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn Run: error = dpm_run_callback(callback, dev, state, info); if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async late" : " late", error); goto Complete; @@ -1607,12 +1665,10 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn TRACE_SUSPEND(error); complete_all(&dev->power.completion); - if (error || async_error) - return error; + if (error || READ_ONCE(async_error)) + return; - dpm_async_suspend_parent(dev, async_suspend_late); - - return 0; + dpm_async_suspend_superior(dev, async_suspend_late); } static void async_suspend_late(void *data, async_cookie_t cookie) @@ -1631,7 +1687,7 @@ int dpm_suspend_late(pm_message_t state) { ktime_t starttime = ktime_get(); struct device *dev; - int error = 0; + int error; trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true); @@ -1664,13 +1720,14 @@ int dpm_suspend_late(pm_message_t state) mutex_unlock(&dpm_list_mtx); - error = device_suspend_late(dev, state, false); + device_suspend_late(dev, state, false); put_device(dev); mutex_lock(&dpm_list_mtx); - if (error || async_error) { + if (READ_ONCE(async_error)) { + dpm_async_suspend_complete_all(&dpm_suspended_list); /* * Move all devices to the target list to resume them * properly. @@ -1683,9 +1740,8 @@ int dpm_suspend_late(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - if (!error) - error = async_error; + error = READ_ONCE(async_error); if (error) { dpm_save_failed_step(SUSPEND_SUSPEND_LATE); dpm_resume_early(resume_event(state)); @@ -1774,7 +1830,7 @@ static void async_suspend(void *data, async_cookie_t cookie); * @state: PM transition of the system being carried out. * @async: If true, the device is being suspended asynchronously. */ -static int device_suspend(struct device *dev, pm_message_t state, bool async) +static void device_suspend(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -1786,7 +1842,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async) dpm_wait_for_subordinate(dev, async); - if (async_error) { + if (READ_ONCE(async_error)) { dev->power.direct_complete = false; goto Complete; } @@ -1806,7 +1862,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async) if (pm_wakeup_pending()) { dev->power.direct_complete = false; - async_error = -EBUSY; + WRITE_ONCE(async_error, -EBUSY); goto Complete; } @@ -1890,7 +1946,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async) Complete: if (error) { - async_error = error; + WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async" : "", error); } @@ -1898,12 +1954,10 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async) complete_all(&dev->power.completion); TRACE_SUSPEND(error); - if (error || async_error) - return error; + if (error || READ_ONCE(async_error)) + return; - dpm_async_suspend_parent(dev, async_suspend); - - return 0; + dpm_async_suspend_superior(dev, async_suspend); } static void async_suspend(void *data, async_cookie_t cookie) @@ -1922,7 +1976,7 @@ int dpm_suspend(pm_message_t state) { ktime_t starttime = ktime_get(); struct device *dev; - int error = 0; + int error; trace_suspend_resume(TPS("dpm_suspend"), state.event, true); might_sleep(); @@ -1957,13 +2011,14 @@ int dpm_suspend(pm_message_t state) mutex_unlock(&dpm_list_mtx); - error = device_suspend(dev, state, false); + device_suspend(dev, state, false); put_device(dev); mutex_lock(&dpm_list_mtx); - if (error || async_error) { + if (READ_ONCE(async_error)) { + dpm_async_suspend_complete_all(&dpm_prepared_list); /* * Move all devices to the target list to resume them * properly. @@ -1976,9 +2031,8 @@ int dpm_suspend(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - if (!error) - error = async_error; + error = READ_ONCE(async_error); if (error) dpm_save_failed_step(SUSPEND_SUSPEND); @@ -2012,7 +2066,7 @@ static bool device_prepare_smart_suspend(struct device *dev) idx = device_links_read_lock(); list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) continue; if (!dev_pm_smart_suspend(link->supplier) && @@ -2123,7 +2177,6 @@ int dpm_prepare(pm_message_t state) int error = 0; trace_suspend_resume(TPS("dpm_prepare"), state.event, true); - might_sleep(); /* * Give a chance for the known devices to complete their probes, before @@ -2190,8 +2243,10 @@ int dpm_suspend_start(pm_message_t state) error = dpm_prepare(state); if (error) dpm_save_failed_step(SUSPEND_PREPARE); - else + else { + pm_restrict_gfp_mask(); error = dpm_suspend(state); + } dpm_show_time(starttime, state, error, "start"); return error; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index c55a7c70bc1a..3e84dc4122de 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -19,10 +19,24 @@ typedef int (*pm_callback_t)(struct device *); +static inline pm_callback_t get_callback_ptr(const void *start, size_t offset) +{ + return *(pm_callback_t *)(start + offset); +} + +static pm_callback_t __rpm_get_driver_callback(struct device *dev, + size_t cb_offset) +{ + if (dev->driver && dev->driver->pm) + return get_callback_ptr(dev->driver->pm, cb_offset); + + return NULL; +} + static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset) { - pm_callback_t cb; const struct dev_pm_ops *ops; + pm_callback_t cb = NULL; if (dev->pm_domain) ops = &dev->pm_domain->ops; @@ -36,12 +50,10 @@ static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset) ops = NULL; if (ops) - cb = *(pm_callback_t *)((void *)ops + cb_offset); - else - cb = NULL; + cb = get_callback_ptr(ops, cb_offset); - if (!cb && dev->driver && dev->driver->pm) - cb = *(pm_callback_t *)((void *)dev->driver->pm + cb_offset); + if (!cb) + cb = __rpm_get_driver_callback(dev, cb_offset); return cb; } @@ -290,7 +302,7 @@ static int rpm_get_suppliers(struct device *dev) device_links_read_lock_held()) { int retval; - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) continue; retval = pm_runtime_get_sync(link->supplier); @@ -1191,10 +1203,12 @@ EXPORT_SYMBOL_GPL(__pm_runtime_resume); * * Return -EINVAL if runtime PM is disabled for @dev. * - * Otherwise, if the runtime PM status of @dev is %RPM_ACTIVE and either - * @ign_usage_count is %true or the runtime PM usage counter of @dev is not - * zero, increment the usage counter of @dev and return 1. Otherwise, return 0 - * without changing the usage counter. + * Otherwise, if its runtime PM status is %RPM_ACTIVE and (1) @ign_usage_count + * is set, or (2) @dev is not ignoring children and its active child count is + * nonero, or (3) the runtime PM usage counter of @dev is not zero, increment + * the usage counter of @dev and return 1. + * + * Otherwise, return 0 without changing the usage counter. * * If @ign_usage_count is %true, this function can be used to prevent suspending * the device when its runtime PM status is %RPM_ACTIVE. @@ -1216,7 +1230,8 @@ static int pm_runtime_get_conditional(struct device *dev, bool ign_usage_count) retval = -EINVAL; } else if (dev->power.runtime_status != RPM_ACTIVE) { retval = 0; - } else if (ign_usage_count) { + } else if (ign_usage_count || (!dev->power.ignore_children && + atomic_read(&dev->power.child_count) > 0)) { retval = 1; atomic_inc(&dev->power.usage_count); } else { @@ -1249,10 +1264,16 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_active); * @dev: Target device. * * Increment the runtime PM usage counter of @dev if its runtime PM status is - * %RPM_ACTIVE and its runtime PM usage counter is greater than 0, in which case - * it returns 1. If the device is in a different state or its usage_count is 0, - * 0 is returned. -EINVAL is returned if runtime PM is disabled for the device, - * in which case also the usage_count will remain unmodified. + * %RPM_ACTIVE and its runtime PM usage counter is greater than 0 or it is not + * ignoring children and its active child count is nonzero. 1 is returned in + * this case. + * + * If @dev is in a different state or it is not in use (that is, its usage + * counter is 0, or it is ignoring children, or its active child count is 0), + * 0 is returned. + * + * -EINVAL is returned if runtime PM is disabled for the device, in which case + * also the usage counter of @dev is not updated. */ int pm_runtime_get_if_in_use(struct device *dev) { @@ -1827,7 +1848,7 @@ void pm_runtime_init(struct device *dev) dev->power.request_pending = false; dev->power.request = RPM_REQ_NONE; dev->power.deferred_resume = false; - dev->power.needs_force_resume = 0; + dev->power.needs_force_resume = false; INIT_WORK(&dev->power.work, pm_runtime_work); dev->power.timer_expires = 0; @@ -1854,6 +1875,11 @@ void pm_runtime_reinit(struct device *dev) pm_runtime_put(dev->parent); } } + /* + * Clear power.needs_force_resume in case it has been set by + * pm_runtime_force_suspend() invoked from a driver remove callback. + */ + dev->power.needs_force_resume = false; } /** @@ -1879,7 +1905,7 @@ void pm_runtime_get_suppliers(struct device *dev) list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, device_links_read_lock_held()) - if (link->flags & DL_FLAG_PM_RUNTIME) { + if (device_link_test(link, DL_FLAG_PM_RUNTIME)) { link->supplier_preactivated = true; pm_runtime_get_sync(link->supplier); } @@ -1933,7 +1959,7 @@ static void pm_runtime_drop_link_count(struct device *dev) */ void pm_runtime_drop_link(struct device_link *link) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) return; pm_runtime_drop_link_count(link->consumer); @@ -1941,13 +1967,23 @@ void pm_runtime_drop_link(struct device_link *link) pm_request_idle(link->supplier); } -bool pm_runtime_need_not_resume(struct device *dev) +static pm_callback_t get_callback(struct device *dev, size_t cb_offset) { - return atomic_read(&dev->power.usage_count) <= 1 && - (atomic_read(&dev->power.child_count) == 0 || - dev->power.ignore_children); + /* + * Setting power.strict_midlayer means that the middle layer + * code does not want its runtime PM callbacks to be invoked via + * pm_runtime_force_suspend() and pm_runtime_force_resume(), so + * return a direct pointer to the driver callback in that case. + */ + if (dev_pm_strict_midlayer_is_set(dev)) + return __rpm_get_driver_callback(dev, cb_offset); + + return __rpm_get_callback(dev, cb_offset); } +#define GET_CALLBACK(dev, callback) \ + get_callback(dev, offsetof(struct dev_pm_ops, callback)) + /** * pm_runtime_force_suspend - Force a device into suspend state if needed. * @dev: Device to suspend. @@ -1964,10 +2000,6 @@ bool pm_runtime_need_not_resume(struct device *dev) * sure the device is put into low power state and it should only be used during * system-wide PM transitions to sleep states. It assumes that the analogous * pm_runtime_force_resume() will be used to resume the device. - * - * Do not use with DPM_FLAG_SMART_SUSPEND as this can lead to an inconsistent - * state where this function has called the ->runtime_suspend callback but the - * PM core marks the driver as runtime active. */ int pm_runtime_force_suspend(struct device *dev) { @@ -1975,10 +2007,10 @@ int pm_runtime_force_suspend(struct device *dev) int ret; pm_runtime_disable(dev); - if (pm_runtime_status_suspended(dev)) + if (pm_runtime_status_suspended(dev) || dev->power.needs_force_resume) return 0; - callback = RPM_GET_CALLBACK(dev, runtime_suspend); + callback = GET_CALLBACK(dev, runtime_suspend); dev_pm_enable_wake_irq_check(dev, true); ret = callback ? callback(dev) : 0; @@ -1990,15 +2022,16 @@ int pm_runtime_force_suspend(struct device *dev) /* * If the device can stay in suspend after the system-wide transition * to the working state that will follow, drop the children counter of - * its parent, but set its status to RPM_SUSPENDED anyway in case this - * function will be called again for it in the meantime. + * its parent and the usage counters of its suppliers. Otherwise, set + * power.needs_force_resume to let pm_runtime_force_resume() know that + * the device needs to be taken care of and to prevent this function + * from handling the device again in case the device is passed to it + * once more subsequently. */ - if (pm_runtime_need_not_resume(dev)) { + if (pm_runtime_need_not_resume(dev)) pm_runtime_set_suspended(dev); - } else { - __update_runtime_status(dev, RPM_SUSPENDED); - dev->power.needs_force_resume = 1; - } + else + dev->power.needs_force_resume = true; return 0; @@ -2009,33 +2042,37 @@ int pm_runtime_force_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); +#ifdef CONFIG_PM_SLEEP + /** * pm_runtime_force_resume - Force a device into resume state if needed. * @dev: Device to resume. * - * Prior invoking this function we expect the user to have brought the device - * into low power state by a call to pm_runtime_force_suspend(). Here we reverse - * those actions and bring the device into full power, if it is expected to be - * used on system resume. In the other case, we defer the resume to be managed - * via runtime PM. + * This function expects that either pm_runtime_force_suspend() has put the + * device into a low-power state prior to calling it, or the device had been + * runtime-suspended before the preceding system-wide suspend transition and it + * was left in suspend during that transition. * - * Typically this function may be invoked from a system resume callback. + * The actions carried out by pm_runtime_force_suspend(), or by a runtime + * suspend in general, are reversed and the device is brought back into full + * power if it is expected to be used on system resume, which is the case when + * its needs_force_resume flag is set or when its smart_suspend flag is set and + * its runtime PM status is "active". + * + * In other cases, the resume is deferred to be managed via runtime PM. + * + * Typically, this function may be invoked from a system resume callback. */ int pm_runtime_force_resume(struct device *dev) { int (*callback)(struct device *); int ret = 0; - if (!dev->power.needs_force_resume) + if (!dev->power.needs_force_resume && (!dev_pm_smart_suspend(dev) || + pm_runtime_status_suspended(dev))) goto out; - /* - * The value of the parent's children counter is correct already, so - * just update the status of the device. - */ - __update_runtime_status(dev, RPM_ACTIVE); - - callback = RPM_GET_CALLBACK(dev, runtime_resume); + callback = GET_CALLBACK(dev, runtime_resume); dev_pm_disable_wake_irq_check(dev, false); ret = callback ? callback(dev) : 0; @@ -2046,9 +2083,30 @@ int pm_runtime_force_resume(struct device *dev) } pm_runtime_mark_last_busy(dev); + out: - dev->power.needs_force_resume = 0; + /* + * The smart_suspend flag can be cleared here because it is not going + * to be necessary until the next system-wide suspend transition that + * will update it again. + */ + dev->power.smart_suspend = false; + /* + * Also clear needs_force_resume to make this function skip devices that + * have been seen by it once. + */ + dev->power.needs_force_resume = false; + pm_runtime_enable(dev); return ret; } EXPORT_SYMBOL_GPL(pm_runtime_force_resume); + +bool pm_runtime_need_not_resume(struct device *dev) +{ + return atomic_read(&dev->power.usage_count) <= 1 && + (atomic_read(&dev->power.child_count) == 0 || + dev->power.ignore_children); +} + +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index fb84cda92a75..c9b4c04b1cf6 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -470,10 +470,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file, if (err) return count; - err = debugfs_file_get(file->f_path.dentry); - if (err) - return err; - map->lock(map->lock_arg); if (new_val && !map->cache_only) { @@ -486,7 +482,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file, map->cache_only = new_val; map->unlock(map->lock_arg); - debugfs_file_put(file->f_path.dentry); if (require_sync) { err = regcache_sync(map); @@ -517,10 +512,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file, if (err) return count; - err = debugfs_file_get(file->f_path.dentry); - if (err) - return err; - map->lock(map->lock_arg); if (new_val && !map->cache_bypass) { @@ -532,7 +523,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file, map->cache_bypass = new_val; map->unlock(map->lock_arg); - debugfs_file_put(file->f_path.dentry); return count; } diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index d1585f073776..6112d942499b 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -21,6 +21,7 @@ struct regmap_irq_chip_data { struct mutex lock; + struct lock_class_key lock_key; struct irq_chip irq_chip; struct regmap *map; @@ -801,7 +802,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, goto err_alloc; } - mutex_init(&d->lock); + /* + * If one regmap-irq is the parent of another then we'll try + * to lock the child with the parent locked, use an explicit + * lock_key so lockdep can figure out what's going on. + */ + lockdep_register_key(&d->lock_key); + mutex_init_with_key(&d->lock, &d->lock_key); for (i = 0; i < chip->num_irqs; i++) d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] @@ -816,7 +823,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, d->mask_buf[i], chip->irq_drv_data); if (ret) - goto err_alloc; + goto err_mutex; } if (chip->mask_base && !chip->handle_mask_sync) { @@ -827,7 +834,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (ret) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } @@ -838,7 +845,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (ret) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } @@ -855,7 +862,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); - goto err_alloc; + goto err_mutex; } } @@ -879,7 +886,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (ret != 0) { dev_err(map->dev, "Failed to ack 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } } @@ -901,7 +908,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } } @@ -910,7 +917,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (chip->status_is_level) { ret = read_irq_data(d); if (ret < 0) - goto err_alloc; + goto err_mutex; memcpy(d->prev_status_buf, d->status_buf, array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0]))); @@ -918,7 +925,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ret = regmap_irq_create_domain(fwnode, irq_base, chip, d); if (ret) - goto err_alloc; + goto err_mutex; ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags | IRQF_ONESHOT, @@ -935,6 +942,9 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, err_domain: /* Should really dispose of the domain but... */ +err_mutex: + mutex_destroy(&d->lock); + lockdep_unregister_key(&d->lock_key); err_alloc: kfree(d->type_buf); kfree(d->type_buf_def); @@ -1027,6 +1037,8 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->config_buf[i]); kfree(d->config_buf); } + mutex_destroy(&d->lock); + lockdep_unregister_key(&d->lock_key); kfree(d); } EXPORT_SYMBOL_GPL(regmap_del_irq_chip); diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 64ea340950b6..95c5bf2a78ee 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -736,7 +736,7 @@ static void stride(struct kunit *test) } } -static struct regmap_range_cfg test_range = { +static const struct regmap_range_cfg test_range = { .selector_reg = 1, .selector_mask = 0xff, diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f2843f814675..1f3f782a04ba 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1173,6 +1173,8 @@ struct regmap *__regmap_init(struct device *dev, err_map: kfree(map); err: + if (bus && bus->free_on_exit) + kfree(bus); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(__regmap_init); diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 8b42df05feff..c890e2a5b428 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -179,7 +179,7 @@ static umode_t topology_is_visible(struct kobject *kobj, static const struct attribute_group topology_attr_group = { .attrs = default_attrs, - .bin_attrs_new = bin_attrs, + .bin_attrs = bin_attrs, .is_visible = topology_is_visible, .name = "topology" }; diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index f021e27644e0..658c7e2ac8bf 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -186,7 +186,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) chip->request = bcma_gpio_request; chip->free = bcma_gpio_free; chip->get = bcma_gpio_get_value; - chip->set_rv = bcma_gpio_set_value; + chip->set = bcma_gpio_set_value; chip->direction_input = bcma_gpio_direction_input; chip->direction_output = bcma_gpio_direction_output; chip->parent = bus->dev; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 0f70e2374e7f..df38fb364904 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -256,49 +256,6 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. -config CDROM_PKTCDVD - tristate "Packet writing on CD/DVD media (DEPRECATED)" - depends on !UML - depends on SCSI - select CDROM - help - Note: This driver is deprecated and will be removed from the - kernel in the near future! - - If you have a CDROM/DVD drive that supports packet writing, say - Y to include support. It should work with any MMC/Mt Fuji - compliant ATAPI or SCSI drive, which is just about any newer - DVD/CD writer. - - Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs - is possible. - DVD-RW disks must be in restricted overwrite mode. - - See the file - for further information on the use of this driver. - - To compile this driver as a module, choose M here: the - module will be called pktcdvd. - -config CDROM_PKTCDVD_BUFFERS - int "Free buffers for data gathering" - depends on CDROM_PKTCDVD - default "8" - help - This controls the maximum number of active concurrent packets. More - concurrent packets can increase write performance, but also require - more memory. Each concurrent packet will require approximately 64Kb - of non-swappable kernel memory, memory which will be allocated when - a disc is opened for writing. - -config CDROM_PKTCDVD_WCACHE - bool "Enable write caching" - depends on CDROM_PKTCDVD - help - If enabled, write caching will be set for the CD-R/W device. For now - this option is dangerous unless the CD-RW media is known good, as we - don't do deferred write error handling yet. - config ATA_OVER_ETH tristate "ATA over Ethernet support" depends on NET diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 097707aca725..a695ce74ef22 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o obj-$(CONFIG_N64CART) += n64cart.o obj-$(CONFIG_BLK_DEV_RAM) += brd.o obj-$(CONFIG_BLK_DEV_LOOP) += loop.o -obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_SUNVDC) += sunvdc.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 749ae1246f4c..d35caa3c69e1 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -80,6 +80,7 @@ enum { DEVFL_NEWSIZE = (1<<6), /* need to update dev size in block layer */ DEVFL_FREEING = (1<<7), /* set when device is being cleaned up */ DEVFL_FREED = (1<<8), /* device has been cleaned up */ + DEVFL_DEAD = (1<<9), /* device has timed out of aoe_deadsecs */ }; enum { diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 50cc90f6ab35..6298f8e271e3 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -754,7 +754,7 @@ rexmit_timer(struct timer_list *timer) utgts = count_targets(d, NULL); - if (d->flags & DEVFL_TKILL) { + if (d->flags & (DEVFL_TKILL | DEVFL_DEAD)) { spin_unlock_irqrestore(&d->lock, flags); return; } @@ -786,7 +786,8 @@ rexmit_timer(struct timer_list *timer) * to clean up. */ list_splice(&flist, &d->factive[0]); - aoedev_downdev(d); + d->flags |= DEVFL_DEAD; + queue_work(aoe_wq, &d->work); goto out; } @@ -898,6 +899,9 @@ aoecmd_sleepwork(struct work_struct *work) { struct aoedev *d = container_of(work, struct aoedev, work); + if (d->flags & DEVFL_DEAD) + aoedev_downdev(d); + if (d->flags & DEVFL_GDALLOC) aoeblk_gdalloc(d); diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index bba05f0c5bbd..3a240755045b 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -198,9 +198,13 @@ aoedev_downdev(struct aoedev *d) { struct aoetgt *t, **tt, **te; struct list_head *head, *pos, *nx; + struct request *rq, *rqnext; int i; + unsigned long flags; - d->flags &= ~DEVFL_UP; + spin_lock_irqsave(&d->lock, flags); + d->flags &= ~(DEVFL_UP | DEVFL_DEAD); + spin_unlock_irqrestore(&d->lock, flags); /* clean out active and to-be-retransmitted buffers */ for (i = 0; i < NFACTIVE; i++) { @@ -223,6 +227,13 @@ aoedev_downdev(struct aoedev *d) /* clean out the in-process request (if any) */ aoe_failip(d); + /* clean out any queued block requests */ + list_for_each_entry_safe(rq, rqnext, &d->rq_list, queuelist) { + list_del_init(&rq->queuelist); + blk_mq_start_request(rq); + blk_mq_end_request(rq, BLK_STS_IOERR); + } + /* fast fail all pending I/O */ if (d->blkq) { /* UP is cleared, freeze+quiesce to insure all are errored */ diff --git a/drivers/block/brd.c b/drivers/block/brd.c index b1be6c510372..0c2eabe14af3 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -64,13 +64,15 @@ static struct page *brd_insert_page(struct brd_device *brd, sector_t sector, rcu_read_unlock(); page = alloc_page(gfp | __GFP_ZERO | __GFP_HIGHMEM); - rcu_read_lock(); - if (!page) + if (!page) { + rcu_read_lock(); return ERR_PTR(-ENOMEM); + } xa_lock(&brd->brd_pages); ret = __xa_cmpxchg(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT, NULL, page, gfp); + rcu_read_lock(); if (ret) { xa_unlock(&brd->brd_pages); __free_page(page); diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index e21492981f7d..f6d6276974ee 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -380,6 +380,9 @@ enum { /* this is/was a write request */ __EE_WRITE, + /* hand back using mempool_free(e, drbd_buffer_page_pool) */ + __EE_RELEASE_TO_MEMPOOL, + /* this is/was a write same request */ __EE_WRITE_SAME, @@ -402,6 +405,7 @@ enum { #define EE_IN_INTERVAL_TREE (1<<__EE_IN_INTERVAL_TREE) #define EE_SUBMITTED (1<<__EE_SUBMITTED) #define EE_WRITE (1<<__EE_WRITE) +#define EE_RELEASE_TO_MEMPOOL (1<<__EE_RELEASE_TO_MEMPOOL) #define EE_WRITE_SAME (1<<__EE_WRITE_SAME) #define EE_APPLICATION (1<<__EE_APPLICATION) #define EE_RS_THIN_REQ (1<<__EE_RS_THIN_REQ) @@ -858,7 +862,6 @@ struct drbd_device { struct list_head sync_ee; /* IO in progress (P_RS_DATA_REPLY gets written to disk) */ struct list_head done_ee; /* need to send P_WRITE_ACK */ struct list_head read_ee; /* [RS]P_DATA_REQUEST being read */ - struct list_head net_ee; /* zero-copy network send in progress */ struct list_head resync_reads; atomic_t pp_in_use; /* allocated from page pool */ @@ -1329,24 +1332,6 @@ extern struct kmem_cache *drbd_al_ext_cache; /* activity log extents */ extern mempool_t drbd_request_mempool; extern mempool_t drbd_ee_mempool; -/* drbd's page pool, used to buffer data received from the peer, - * or data requested by the peer. - * - * This does not have an emergency reserve. - * - * When allocating from this pool, it first takes pages from the pool. - * Only if the pool is depleted will try to allocate from the system. - * - * The assumption is that pages taken from this pool will be processed, - * and given back, "quickly", and then can be recycled, so we can avoid - * frequent calls to alloc_page(), and still will be able to make progress even - * under memory pressure. - */ -extern struct page *drbd_pp_pool; -extern spinlock_t drbd_pp_lock; -extern int drbd_pp_vacant; -extern wait_queue_head_t drbd_pp_wait; - /* We also need a standard (emergency-reserve backed) page pool * for meta data IO (activity log, bitmap). * We can keep it global, as long as it is used as "N pages at a time". @@ -1354,6 +1339,7 @@ extern wait_queue_head_t drbd_pp_wait; */ #define DRBD_MIN_POOL_PAGES 128 extern mempool_t drbd_md_io_page_pool; +extern mempool_t drbd_buffer_page_pool; /* We also need to make sure we get a bio * when we need it for housekeeping purposes */ @@ -1488,10 +1474,7 @@ extern struct drbd_peer_request *drbd_alloc_peer_req(struct drbd_peer_device *, sector_t, unsigned int, unsigned int, gfp_t) __must_hold(local); -extern void __drbd_free_peer_req(struct drbd_device *, struct drbd_peer_request *, - int); -#define drbd_free_peer_req(m,e) __drbd_free_peer_req(m, e, 0) -#define drbd_free_net_peer_req(m,e) __drbd_free_peer_req(m, e, 1) +extern void drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *req); extern struct page *drbd_alloc_pages(struct drbd_peer_device *, unsigned int, bool); extern void _drbd_clear_done_ee(struct drbd_device *device, struct list_head *to_be_freed); extern int drbd_connected(struct drbd_peer_device *); @@ -1610,16 +1593,6 @@ static inline struct page *page_chain_next(struct page *page) for (; page && ({ n = page_chain_next(page); 1; }); page = n) -static inline int drbd_peer_req_has_active_page(struct drbd_peer_request *peer_req) -{ - struct page *page = peer_req->pages; - page_chain_for_each(page) { - if (page_count(page) > 1) - return 1; - } - return 0; -} - static inline union drbd_state drbd_read_state(struct drbd_device *device) { struct drbd_resource *resource = device->resource; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 52724b79be30..c73376886e7a 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -114,20 +114,10 @@ struct kmem_cache *drbd_al_ext_cache; /* activity log extents */ mempool_t drbd_request_mempool; mempool_t drbd_ee_mempool; mempool_t drbd_md_io_page_pool; +mempool_t drbd_buffer_page_pool; struct bio_set drbd_md_io_bio_set; struct bio_set drbd_io_bio_set; -/* I do not use a standard mempool, because: - 1) I want to hand out the pre-allocated objects first. - 2) I want to be able to interrupt sleeping allocation with a signal. - Note: This is a single linked list, the next pointer is the private - member of struct page. - */ -struct page *drbd_pp_pool; -DEFINE_SPINLOCK(drbd_pp_lock); -int drbd_pp_vacant; -wait_queue_head_t drbd_pp_wait; - DEFINE_RATELIMIT_STATE(drbd_ratelimit_state, 5 * HZ, 5); static const struct block_device_operations drbd_ops = { @@ -1611,6 +1601,7 @@ static int _drbd_send_zc_bio(struct drbd_peer_device *peer_device, struct bio *b static int _drbd_send_zc_ee(struct drbd_peer_device *peer_device, struct drbd_peer_request *peer_req) { + bool use_sendpage = !(peer_req->flags & EE_RELEASE_TO_MEMPOOL); struct page *page = peer_req->pages; unsigned len = peer_req->i.size; int err; @@ -1619,8 +1610,13 @@ static int _drbd_send_zc_ee(struct drbd_peer_device *peer_device, page_chain_for_each(page) { unsigned l = min_t(unsigned, len, PAGE_SIZE); - err = _drbd_send_page(peer_device, page, 0, l, - page_chain_next(page) ? MSG_MORE : 0); + if (likely(use_sendpage)) + err = _drbd_send_page(peer_device, page, 0, l, + page_chain_next(page) ? MSG_MORE : 0); + else + err = _drbd_no_send_page(peer_device, page, 0, l, + page_chain_next(page) ? MSG_MORE : 0); + if (err) return err; len -= l; @@ -1962,7 +1958,6 @@ void drbd_init_set_defaults(struct drbd_device *device) INIT_LIST_HEAD(&device->sync_ee); INIT_LIST_HEAD(&device->done_ee); INIT_LIST_HEAD(&device->read_ee); - INIT_LIST_HEAD(&device->net_ee); INIT_LIST_HEAD(&device->resync_reads); INIT_LIST_HEAD(&device->resync_work.list); INIT_LIST_HEAD(&device->unplug_work.list); @@ -2043,7 +2038,6 @@ void drbd_device_cleanup(struct drbd_device *device) D_ASSERT(device, list_empty(&device->sync_ee)); D_ASSERT(device, list_empty(&device->done_ee)); D_ASSERT(device, list_empty(&device->read_ee)); - D_ASSERT(device, list_empty(&device->net_ee)); D_ASSERT(device, list_empty(&device->resync_reads)); D_ASSERT(device, list_empty(&first_peer_device(device)->connection->sender_work.q)); D_ASSERT(device, list_empty(&device->resync_work.list)); @@ -2055,19 +2049,11 @@ void drbd_device_cleanup(struct drbd_device *device) static void drbd_destroy_mempools(void) { - struct page *page; - - while (drbd_pp_pool) { - page = drbd_pp_pool; - drbd_pp_pool = (struct page *)page_private(page); - __free_page(page); - drbd_pp_vacant--; - } - /* D_ASSERT(device, atomic_read(&drbd_pp_vacant)==0); */ bioset_exit(&drbd_io_bio_set); bioset_exit(&drbd_md_io_bio_set); + mempool_exit(&drbd_buffer_page_pool); mempool_exit(&drbd_md_io_page_pool); mempool_exit(&drbd_ee_mempool); mempool_exit(&drbd_request_mempool); @@ -2086,9 +2072,8 @@ static void drbd_destroy_mempools(void) static int drbd_create_mempools(void) { - struct page *page; const int number = (DRBD_MAX_BIO_SIZE/PAGE_SIZE) * drbd_minor_count; - int i, ret; + int ret; /* caches */ drbd_request_cache = kmem_cache_create( @@ -2125,6 +2110,10 @@ static int drbd_create_mempools(void) if (ret) goto Enomem; + ret = mempool_init_page_pool(&drbd_buffer_page_pool, number, 0); + if (ret) + goto Enomem; + ret = mempool_init_slab_pool(&drbd_request_mempool, number, drbd_request_cache); if (ret) @@ -2134,15 +2123,6 @@ static int drbd_create_mempools(void) if (ret) goto Enomem; - for (i = 0; i < number; i++) { - page = alloc_page(GFP_HIGHUSER); - if (!page) - goto Enomem; - set_page_private(page, (unsigned long)drbd_pp_pool); - drbd_pp_pool = page; - } - drbd_pp_vacant = number; - return 0; Enomem: @@ -2169,10 +2149,6 @@ static void drbd_release_all_peer_reqs(struct drbd_device *device) rr = drbd_free_peer_reqs(device, &device->done_ee); if (rr) drbd_err(device, "%d EEs in done list found!\n", rr); - - rr = drbd_free_peer_reqs(device, &device->net_ee); - if (rr) - drbd_err(device, "%d EEs in net list found!\n", rr); } /* caution. no locking. */ @@ -2863,11 +2839,6 @@ static int __init drbd_init(void) return err; } - /* - * allocate all necessary structs - */ - init_waitqueue_head(&drbd_pp_wait); - drbd_proc = NULL; /* play safe for drbd_cleanup */ idr_init(&drbd_devices); diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index e5a2e5f7887b..caaf2781136d 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "drbd_int.h" #include "drbd_protocol.h" #include "drbd_req.h" @@ -63,182 +64,31 @@ static int e_end_block(struct drbd_work *, int); #define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN) -/* - * some helper functions to deal with single linked page lists, - * page->private being our "next" pointer. - */ - -/* If at least n pages are linked at head, get n pages off. - * Otherwise, don't modify head, and return NULL. - * Locking is the responsibility of the caller. - */ -static struct page *page_chain_del(struct page **head, int n) -{ - struct page *page; - struct page *tmp; - - BUG_ON(!n); - BUG_ON(!head); - - page = *head; - - if (!page) - return NULL; - - while (page) { - tmp = page_chain_next(page); - if (--n == 0) - break; /* found sufficient pages */ - if (tmp == NULL) - /* insufficient pages, don't use any of them. */ - return NULL; - page = tmp; - } - - /* add end of list marker for the returned list */ - set_page_private(page, 0); - /* actual return value, and adjustment of head */ - page = *head; - *head = tmp; - return page; -} - -/* may be used outside of locks to find the tail of a (usually short) - * "private" page chain, before adding it back to a global chain head - * with page_chain_add() under a spinlock. */ -static struct page *page_chain_tail(struct page *page, int *len) -{ - struct page *tmp; - int i = 1; - while ((tmp = page_chain_next(page))) { - ++i; - page = tmp; - } - if (len) - *len = i; - return page; -} - -static int page_chain_free(struct page *page) -{ - struct page *tmp; - int i = 0; - page_chain_for_each_safe(page, tmp) { - put_page(page); - ++i; - } - return i; -} - -static void page_chain_add(struct page **head, - struct page *chain_first, struct page *chain_last) -{ -#if 1 - struct page *tmp; - tmp = page_chain_tail(chain_first, NULL); - BUG_ON(tmp != chain_last); -#endif - - /* add chain to head */ - set_page_private(chain_last, (unsigned long)*head); - *head = chain_first; -} - -static struct page *__drbd_alloc_pages(struct drbd_device *device, - unsigned int number) +static struct page *__drbd_alloc_pages(unsigned int number) { struct page *page = NULL; struct page *tmp = NULL; unsigned int i = 0; - /* Yes, testing drbd_pp_vacant outside the lock is racy. - * So what. It saves a spin_lock. */ - if (drbd_pp_vacant >= number) { - spin_lock(&drbd_pp_lock); - page = page_chain_del(&drbd_pp_pool, number); - if (page) - drbd_pp_vacant -= number; - spin_unlock(&drbd_pp_lock); - if (page) - return page; - } - /* GFP_TRY, because we must not cause arbitrary write-out: in a DRBD * "criss-cross" setup, that might cause write-out on some other DRBD, * which in turn might block on the other node at this very place. */ for (i = 0; i < number; i++) { - tmp = alloc_page(GFP_TRY); + tmp = mempool_alloc(&drbd_buffer_page_pool, GFP_TRY); if (!tmp) - break; + goto fail; set_page_private(tmp, (unsigned long)page); page = tmp; } - - if (i == number) - return page; - - /* Not enough pages immediately available this time. - * No need to jump around here, drbd_alloc_pages will retry this - * function "soon". */ - if (page) { - tmp = page_chain_tail(page, NULL); - spin_lock(&drbd_pp_lock); - page_chain_add(&drbd_pp_pool, page, tmp); - drbd_pp_vacant += i; - spin_unlock(&drbd_pp_lock); + return page; +fail: + page_chain_for_each_safe(page, tmp) { + set_page_private(page, 0); + mempool_free(page, &drbd_buffer_page_pool); } return NULL; } -static void reclaim_finished_net_peer_reqs(struct drbd_device *device, - struct list_head *to_be_freed) -{ - struct drbd_peer_request *peer_req, *tmp; - - /* The EEs are always appended to the end of the list. Since - they are sent in order over the wire, they have to finish - in order. As soon as we see the first not finished we can - stop to examine the list... */ - - list_for_each_entry_safe(peer_req, tmp, &device->net_ee, w.list) { - if (drbd_peer_req_has_active_page(peer_req)) - break; - list_move(&peer_req->w.list, to_be_freed); - } -} - -static void drbd_reclaim_net_peer_reqs(struct drbd_device *device) -{ - LIST_HEAD(reclaimed); - struct drbd_peer_request *peer_req, *t; - - spin_lock_irq(&device->resource->req_lock); - reclaim_finished_net_peer_reqs(device, &reclaimed); - spin_unlock_irq(&device->resource->req_lock); - list_for_each_entry_safe(peer_req, t, &reclaimed, w.list) - drbd_free_net_peer_req(device, peer_req); -} - -static void conn_reclaim_net_peer_reqs(struct drbd_connection *connection) -{ - struct drbd_peer_device *peer_device; - int vnr; - - rcu_read_lock(); - idr_for_each_entry(&connection->peer_devices, peer_device, vnr) { - struct drbd_device *device = peer_device->device; - if (!atomic_read(&device->pp_in_use_by_net)) - continue; - - kref_get(&device->kref); - rcu_read_unlock(); - drbd_reclaim_net_peer_reqs(device); - kref_put(&device->kref, drbd_destroy_device); - rcu_read_lock(); - } - rcu_read_unlock(); -} - /** * drbd_alloc_pages() - Returns @number pages, retries forever (or until signalled) * @peer_device: DRBD device. @@ -263,9 +113,8 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int bool retry) { struct drbd_device *device = peer_device->device; - struct page *page = NULL; + struct page *page; struct net_conf *nc; - DEFINE_WAIT(wait); unsigned int mxb; rcu_read_lock(); @@ -273,37 +122,9 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int mxb = nc ? nc->max_buffers : 1000000; rcu_read_unlock(); - if (atomic_read(&device->pp_in_use) < mxb) - page = __drbd_alloc_pages(device, number); - - /* Try to keep the fast path fast, but occasionally we need - * to reclaim the pages we lended to the network stack. */ - if (page && atomic_read(&device->pp_in_use_by_net) > 512) - drbd_reclaim_net_peer_reqs(device); - - while (page == NULL) { - prepare_to_wait(&drbd_pp_wait, &wait, TASK_INTERRUPTIBLE); - - drbd_reclaim_net_peer_reqs(device); - - if (atomic_read(&device->pp_in_use) < mxb) { - page = __drbd_alloc_pages(device, number); - if (page) - break; - } - - if (!retry) - break; - - if (signal_pending(current)) { - drbd_warn(device, "drbd_alloc_pages interrupted!\n"); - break; - } - - if (schedule_timeout(HZ/10) == 0) - mxb = UINT_MAX; - } - finish_wait(&drbd_pp_wait, &wait); + if (atomic_read(&device->pp_in_use) >= mxb) + schedule_timeout_interruptible(HZ / 10); + page = __drbd_alloc_pages(number); if (page) atomic_add(number, &device->pp_in_use); @@ -314,29 +135,25 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int * Is also used from inside an other spin_lock_irq(&resource->req_lock); * Either links the page chain back to the global pool, * or returns all pages to the system. */ -static void drbd_free_pages(struct drbd_device *device, struct page *page, int is_net) +static void drbd_free_pages(struct drbd_device *device, struct page *page) { - atomic_t *a = is_net ? &device->pp_in_use_by_net : &device->pp_in_use; - int i; + struct page *tmp; + int i = 0; if (page == NULL) return; - if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE) * drbd_minor_count) - i = page_chain_free(page); - else { - struct page *tmp; - tmp = page_chain_tail(page, &i); - spin_lock(&drbd_pp_lock); - page_chain_add(&drbd_pp_pool, page, tmp); - drbd_pp_vacant += i; - spin_unlock(&drbd_pp_lock); + page_chain_for_each_safe(page, tmp) { + set_page_private(page, 0); + if (page_count(page) == 1) + mempool_free(page, &drbd_buffer_page_pool); + else + put_page(page); + i++; } - i = atomic_sub_return(i, a); + i = atomic_sub_return(i, &device->pp_in_use); if (i < 0) - drbd_warn(device, "ASSERTION FAILED: %s: %d < 0\n", - is_net ? "pp_in_use_by_net" : "pp_in_use", i); - wake_up(&drbd_pp_wait); + drbd_warn(device, "ASSERTION FAILED: pp_in_use: %d < 0\n", i); } /* @@ -380,6 +197,8 @@ drbd_alloc_peer_req(struct drbd_peer_device *peer_device, u64 id, sector_t secto gfpflags_allow_blocking(gfp_mask)); if (!page) goto fail; + if (!mempool_is_saturated(&drbd_buffer_page_pool)) + peer_req->flags |= EE_RELEASE_TO_MEMPOOL; } memset(peer_req, 0, sizeof(*peer_req)); @@ -403,13 +222,12 @@ drbd_alloc_peer_req(struct drbd_peer_device *peer_device, u64 id, sector_t secto return NULL; } -void __drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *peer_req, - int is_net) +void drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *peer_req) { might_sleep(); if (peer_req->flags & EE_HAS_DIGEST) kfree(peer_req->digest); - drbd_free_pages(device, peer_req->pages, is_net); + drbd_free_pages(device, peer_req->pages); D_ASSERT(device, atomic_read(&peer_req->pending_bios) == 0); D_ASSERT(device, drbd_interval_empty(&peer_req->i)); if (!expect(device, !(peer_req->flags & EE_CALL_AL_COMPLETE_IO))) { @@ -424,14 +242,13 @@ int drbd_free_peer_reqs(struct drbd_device *device, struct list_head *list) LIST_HEAD(work_list); struct drbd_peer_request *peer_req, *t; int count = 0; - int is_net = list == &device->net_ee; spin_lock_irq(&device->resource->req_lock); list_splice_init(list, &work_list); spin_unlock_irq(&device->resource->req_lock); list_for_each_entry_safe(peer_req, t, &work_list, w.list) { - __drbd_free_peer_req(device, peer_req, is_net); + drbd_free_peer_req(device, peer_req); count++; } return count; @@ -443,18 +260,13 @@ int drbd_free_peer_reqs(struct drbd_device *device, struct list_head *list) static int drbd_finish_peer_reqs(struct drbd_device *device) { LIST_HEAD(work_list); - LIST_HEAD(reclaimed); struct drbd_peer_request *peer_req, *t; int err = 0; spin_lock_irq(&device->resource->req_lock); - reclaim_finished_net_peer_reqs(device, &reclaimed); list_splice_init(&device->done_ee, &work_list); spin_unlock_irq(&device->resource->req_lock); - list_for_each_entry_safe(peer_req, t, &reclaimed, w.list) - drbd_free_net_peer_req(device, peer_req); - /* possible callbacks here: * e_end_block, and e_end_resync_block, e_send_superseded. * all ignore the last argument. @@ -1975,7 +1787,7 @@ static int drbd_drain_block(struct drbd_peer_device *peer_device, int data_size) data_size -= len; } kunmap(page); - drbd_free_pages(peer_device->device, page, 0); + drbd_free_pages(peer_device->device, page); return err; } @@ -2500,7 +2312,11 @@ static int handle_write_conflicts(struct drbd_device *device, peer_req->w.cb = superseded ? e_send_superseded : e_send_retry_write; list_add_tail(&peer_req->w.list, &device->done_ee); - queue_work(connection->ack_sender, &peer_req->peer_device->send_acks_work); + /* put is in drbd_send_acks_wf() */ + kref_get(&device->kref); + if (!queue_work(connection->ack_sender, + &peer_req->peer_device->send_acks_work)) + kref_put(&device->kref, drbd_destroy_device); err = -ENOENT; goto out; @@ -5220,16 +5036,6 @@ static int drbd_disconnected(struct drbd_peer_device *peer_device) put_ldev(device); } - /* tcp_close and release of sendpage pages can be deferred. I don't - * want to use SO_LINGER, because apparently it can be deferred for - * more than 20 seconds (longest time I checked). - * - * Actually we don't care for exactly when the network stack does its - * put_page(), but release our reference on these pages right here. - */ - i = drbd_free_peer_reqs(device, &device->net_ee); - if (i) - drbd_info(device, "net_ee not empty, killed %u entries\n", i); i = atomic_read(&device->pp_in_use_by_net); if (i) drbd_info(device, "pp_in_use_by_net = %d, expected 0\n", i); @@ -5976,8 +5782,6 @@ int drbd_ack_receiver(struct drbd_thread *thi) while (get_t_state(thi) == RUNNING) { drbd_thread_current_set_cpu(thi); - conn_reclaim_net_peer_reqs(connection); - if (test_and_clear_bit(SEND_PING, &connection->flags)) { if (drbd_send_ping(connection)) { drbd_err(connection, "drbd_send_ping has failed\n"); diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index a6ea737b3b71..dea3e79d044f 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -1030,22 +1030,6 @@ int drbd_resync_finished(struct drbd_peer_device *peer_device) return 1; } -/* helper */ -static void move_to_net_ee_or_free(struct drbd_device *device, struct drbd_peer_request *peer_req) -{ - if (drbd_peer_req_has_active_page(peer_req)) { - /* This might happen if sendpage() has not finished */ - int i = PFN_UP(peer_req->i.size); - atomic_add(i, &device->pp_in_use_by_net); - atomic_sub(i, &device->pp_in_use); - spin_lock_irq(&device->resource->req_lock); - list_add_tail(&peer_req->w.list, &device->net_ee); - spin_unlock_irq(&device->resource->req_lock); - wake_up(&drbd_pp_wait); - } else - drbd_free_peer_req(device, peer_req); -} - /** * w_e_end_data_req() - Worker callback, to send a P_DATA_REPLY packet in response to a P_DATA_REQUEST * @w: work object. @@ -1059,9 +1043,8 @@ int w_e_end_data_req(struct drbd_work *w, int cancel) int err; if (unlikely(cancel)) { - drbd_free_peer_req(device, peer_req); - dec_unacked(device); - return 0; + err = 0; + goto out; } if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) { @@ -1074,12 +1057,12 @@ int w_e_end_data_req(struct drbd_work *w, int cancel) err = drbd_send_ack(peer_device, P_NEG_DREPLY, peer_req); } - dec_unacked(device); - - move_to_net_ee_or_free(device, peer_req); - if (unlikely(err)) drbd_err(device, "drbd_send_block() failed\n"); +out: + dec_unacked(device); + drbd_free_peer_req(device, peer_req); + return err; } @@ -1120,9 +1103,8 @@ int w_e_end_rsdata_req(struct drbd_work *w, int cancel) int err; if (unlikely(cancel)) { - drbd_free_peer_req(device, peer_req); - dec_unacked(device); - return 0; + err = 0; + goto out; } if (get_ldev_if_state(device, D_FAILED)) { @@ -1155,13 +1137,12 @@ int w_e_end_rsdata_req(struct drbd_work *w, int cancel) /* update resync data with failure */ drbd_rs_failed_io(peer_device, peer_req->i.sector, peer_req->i.size); } - - dec_unacked(device); - - move_to_net_ee_or_free(device, peer_req); - if (unlikely(err)) drbd_err(device, "drbd_send_block() failed\n"); +out: + dec_unacked(device); + drbd_free_peer_req(device, peer_req); + return err; } @@ -1176,9 +1157,8 @@ int w_e_end_csum_rs_req(struct drbd_work *w, int cancel) int err, eq = 0; if (unlikely(cancel)) { - drbd_free_peer_req(device, peer_req); - dec_unacked(device); - return 0; + err = 0; + goto out; } if (get_ldev(device)) { @@ -1220,12 +1200,12 @@ int w_e_end_csum_rs_req(struct drbd_work *w, int cancel) if (drbd_ratelimit()) drbd_err(device, "Sending NegDReply. I guess it gets messy.\n"); } - - dec_unacked(device); - move_to_net_ee_or_free(device, peer_req); - if (unlikely(err)) drbd_err(device, "drbd_send_block/ack() failed\n"); +out: + dec_unacked(device); + drbd_free_peer_req(device, peer_req); + return err; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index e97432032f01..24be0c2c4075 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3411,7 +3411,7 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode, struct floppy_max_errors max_errors; struct floppy_drive_params dp; } inparam; /* parameters coming from user space */ - const void *outparam; /* parameters passed back to user space */ + const void *outparam = NULL; /* parameters passed back to user space */ /* convert compatibility eject ioctls into floppy eject ioctl. * We do this in order to provide a means to eject floppy disks before diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 500840e4a74e..053a086d547e 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -137,20 +137,35 @@ static void loop_global_unlock(struct loop_device *lo, bool global) static int max_part; static int part_shift; -static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file) +static loff_t lo_calculate_size(struct loop_device *lo, struct file *file) { loff_t loopsize; + int ret; - /* Compute loopsize in bytes */ - loopsize = i_size_read(file->f_mapping->host); - if (offset > 0) - loopsize -= offset; + if (S_ISBLK(file_inode(file)->i_mode)) { + loopsize = i_size_read(file->f_mapping->host); + } else { + struct kstat stat; + + /* + * Get the accurate file size. This provides better results than + * cached inode data, particularly for network filesystems where + * metadata may be stale. + */ + ret = vfs_getattr_nosec(&file->f_path, &stat, STATX_SIZE, 0); + if (ret) + return 0; + + loopsize = stat.size; + } + + if (lo->lo_offset > 0) + loopsize -= lo->lo_offset; /* offset is beyond i_size, weird but possible */ if (loopsize < 0) return 0; - - if (sizelimit > 0 && sizelimit < loopsize) - loopsize = sizelimit; + if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) + loopsize = lo->lo_sizelimit; /* * Unfortunately, if we want to do I/O on the device, * the number of 512-byte sectors has to fit into a sector_t. @@ -158,11 +173,6 @@ static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file) return loopsize >> 9; } -static loff_t get_loop_size(struct loop_device *lo, struct file *file) -{ - return get_size(lo->lo_offset, lo->lo_sizelimit, file); -} - /* * We support direct I/O only if lo_offset is aligned with the logical I/O size * of backing device, and the logical block size of loop is bigger than that of @@ -308,14 +318,13 @@ static void lo_complete_rq(struct request *rq) static void lo_rw_aio_do_completion(struct loop_cmd *cmd) { struct request *rq = blk_mq_rq_from_pdu(cmd); - struct loop_device *lo = rq->q->queuedata; if (!atomic_dec_and_test(&cmd->ref)) return; kfree(cmd->bvec); cmd->bvec = NULL; if (req_op(rq) == REQ_OP_WRITE) - file_end_write(lo->lo_backing_file); + kiocb_end_write(&cmd->iocb); if (likely(!blk_should_fake_timeout(rq->q))) blk_mq_complete_request(rq); } @@ -391,7 +400,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, } if (rw == ITER_SOURCE) { - file_start_write(lo->lo_backing_file); + kiocb_start_write(&cmd->iocb); ret = file->f_op->write_iter(&cmd->iocb, &iter); } else ret = file->f_op->read_iter(&cmd->iocb, &iter); @@ -570,7 +579,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, error = -EINVAL; /* size of the new backing store needs to be the same */ - if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) + if (lo_calculate_size(lo, file) != lo_calculate_size(lo, old_file)) goto out_err; /* @@ -1064,7 +1073,7 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode, loop_update_dio(lo); loop_sysfs_init(lo); - size = get_loop_size(lo, file); + size = lo_calculate_size(lo, file); loop_set_size(lo, size); /* Order wrt reading lo_state in loop_validate_file(). */ @@ -1256,8 +1265,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (partscan) clear_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state); if (!err && size_changed) { - loff_t new_size = get_size(lo->lo_offset, lo->lo_sizelimit, - lo->lo_backing_file); + loff_t new_size = lo_calculate_size(lo, lo->lo_backing_file); loop_set_size(lo, new_size); } out_unlock: @@ -1400,7 +1408,7 @@ static int loop_set_capacity(struct loop_device *lo) if (unlikely(lo->lo_state != Lo_bound)) return -ENXIO; - size = get_loop_size(lo, lo->lo_backing_file); + size = lo_calculate_size(lo, lo->lo_backing_file); loop_set_size(lo, size); return 0; @@ -1432,17 +1440,34 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg) return 0; } -static int loop_set_block_size(struct loop_device *lo, unsigned long arg) +static int loop_set_block_size(struct loop_device *lo, blk_mode_t mode, + struct block_device *bdev, unsigned long arg) { struct queue_limits lim; unsigned int memflags; int err = 0; - if (lo->lo_state != Lo_bound) - return -ENXIO; + /* + * If we don't hold exclusive handle for the device, upgrade to it + * here to avoid changing device under exclusive owner. + */ + if (!(mode & BLK_OPEN_EXCL)) { + err = bd_prepare_to_claim(bdev, loop_set_block_size, NULL); + if (err) + return err; + } + + err = mutex_lock_killable(&lo->lo_mutex); + if (err) + goto abort_claim; + + if (lo->lo_state != Lo_bound) { + err = -ENXIO; + goto unlock; + } if (lo->lo_queue->limits.logical_block_size == arg) - return 0; + goto unlock; sync_blockdev(lo->lo_device); invalidate_bdev(lo->lo_device); @@ -1455,6 +1480,11 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg) loop_update_dio(lo); blk_mq_unfreeze_queue(lo->lo_queue, memflags); +unlock: + mutex_unlock(&lo->lo_mutex); +abort_claim: + if (!(mode & BLK_OPEN_EXCL)) + bd_abort_claiming(bdev, loop_set_block_size); return err; } @@ -1473,9 +1503,6 @@ static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd, case LOOP_SET_DIRECT_IO: err = loop_set_dio(lo, arg); break; - case LOOP_SET_BLOCK_SIZE: - err = loop_set_block_size(lo, arg); - break; default: err = -EINVAL; } @@ -1530,9 +1557,12 @@ static int lo_ioctl(struct block_device *bdev, blk_mode_t mode, break; case LOOP_GET_STATUS64: return loop_get_status64(lo, argp); + case LOOP_SET_BLOCK_SIZE: + if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN)) + return -EPERM; + return loop_set_block_size(lo, mode, bdev, arg); case LOOP_SET_CAPACITY: case LOOP_SET_DIRECT_IO: - case LOOP_SET_BLOCK_SIZE: if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN)) return -EPERM; fallthrough; diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 66ce6b81c7d9..8fc7761397bd 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -2040,11 +2040,12 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, * @dir Direction (read or write) * * return value - * None + * 0 The IO completed successfully. + * -ENOMEM The DMA mapping failed. */ -static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq, - struct mtip_cmd *command, - struct blk_mq_hw_ctx *hctx) +static int mtip_hw_submit_io(struct driver_data *dd, struct request *rq, + struct mtip_cmd *command, + struct blk_mq_hw_ctx *hctx) { struct mtip_cmd_hdr *hdr = dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag; @@ -2056,13 +2057,15 @@ static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq, unsigned int nents; /* Map the scatter list for DMA access */ - nents = blk_rq_map_sg(rq, command->sg); - nents = dma_map_sg(&dd->pdev->dev, command->sg, nents, dma_dir); + command->scatter_ents = blk_rq_map_sg(rq, command->sg); + nents = dma_map_sg(&dd->pdev->dev, command->sg, + command->scatter_ents, dma_dir); + if (!nents) + return -ENOMEM; + prefetch(&port->flags); - command->scatter_ents = nents; - /* * The number of retries for this command before it is * reported as a failure to the upper layers. @@ -2112,11 +2115,13 @@ static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq, if (unlikely(port->flags & MTIP_PF_PAUSE_IO)) { set_bit(rq->tag, port->cmds_to_issue); set_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); - return; + return 0; } /* Issue the command to the hardware */ mtip_issue_ncq_command(port, rq->tag); + + return 0; } /* @@ -3315,7 +3320,9 @@ static blk_status_t mtip_queue_rq(struct blk_mq_hw_ctx *hctx, blk_mq_start_request(rq); - mtip_hw_submit_io(dd, rq, cmd, hctx); + if (mtip_hw_submit_io(dd, rq, cmd, hctx)) + return BLK_STS_IOERR; + return BLK_STS_OK; } diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 7bdc7eb808ea..6463d0e8d0ce 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1473,7 +1473,17 @@ static int nbd_start_device(struct nbd_device *nbd) return -EINVAL; } - blk_mq_update_nr_hw_queues(&nbd->tag_set, config->num_connections); +retry: + mutex_unlock(&nbd->config_lock); + blk_mq_update_nr_hw_queues(&nbd->tag_set, num_connections); + mutex_lock(&nbd->config_lock); + + /* if another code path updated nr_hw_queues, retry until succeed */ + if (num_connections != config->num_connections) { + num_connections = config->num_connections; + goto retry; + } + nbd->pid = task_pid_nr(current); nbd_parse_flags(nbd); @@ -2198,9 +2208,7 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) goto out; } } - ret = nbd_start_device(nbd); - if (ret) - goto out; + if (info->attrs[NBD_ATTR_BACKEND_IDENTIFIER]) { nbd->backend = nla_strdup(info->attrs[NBD_ATTR_BACKEND_IDENTIFIER], GFP_KERNEL); @@ -2216,6 +2224,8 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) goto out; } set_bit(NBD_RT_HAS_BACKEND_FILE, &config->runtime_flags); + + ret = nbd_start_device(nbd); out: mutex_unlock(&nbd->config_lock); if (!ret) { diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index aa163ae9b2aa..91642c9a3b29 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1179,7 +1179,7 @@ static int copy_from_nullb(struct nullb *nullb, struct page *dest, memcpy_page(dest, off + count, t_page->page, offset, temp); else - zero_user(dest, off + count, temp); + memzero_page(dest, off + count, temp); count += temp; sector += temp >> SECTOR_SHIFT; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c deleted file mode 100644 index d5cc7bd2875c..000000000000 --- a/drivers/block/pktcdvd.c +++ /dev/null @@ -1,2916 +0,0 @@ -/* - * Copyright (C) 2000 Jens Axboe - * Copyright (C) 2001-2004 Peter Osterlund - * Copyright (C) 2006 Thomas Maier - * - * May be copied or modified under the terms of the GNU General Public - * License. See linux/COPYING for more information. - * - * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and - * DVD-RAM devices. - * - * Theory of operation: - * - * At the lowest level, there is the standard driver for the CD/DVD device, - * such as drivers/scsi/sr.c. This driver can handle read and write requests, - * but it doesn't know anything about the special restrictions that apply to - * packet writing. One restriction is that write requests must be aligned to - * packet boundaries on the physical media, and the size of a write request - * must be equal to the packet size. Another restriction is that a - * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read - * command, if the previous command was a write. - * - * The purpose of the packet writing driver is to hide these restrictions from - * higher layers, such as file systems, and present a block device that can be - * randomly read and written using 2kB-sized blocks. - * - * The lowest layer in the packet writing driver is the packet I/O scheduler. - * Its data is defined by the struct packet_iosched and includes two bio - * queues with pending read and write requests. These queues are processed - * by the pkt_iosched_process_queue() function. The write requests in this - * queue are already properly aligned and sized. This layer is responsible for - * issuing the flush cache commands and scheduling the I/O in a good order. - * - * The next layer transforms unaligned write requests to aligned writes. This - * transformation requires reading missing pieces of data from the underlying - * block device, assembling the pieces to full packets and queuing them to the - * packet I/O scheduler. - * - * At the top layer there is a custom ->submit_bio function that forwards - * read requests directly to the iosched queue and puts write requests in the - * unaligned write queue. A kernel thread performs the necessary read - * gathering to convert the unaligned writes to aligned writes and then feeds - * them to the packet I/O scheduler. - * - *************************************************************************/ - -#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 - -#define DRIVER_NAME "pktcdvd" - -#define MAX_SPEED 0xffff - -static DEFINE_MUTEX(pktcdvd_mutex); -static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; -static struct proc_dir_entry *pkt_proc; -static int pktdev_major; -static int write_congestion_on = PKT_WRITE_CONGESTION_ON; -static int write_congestion_off = PKT_WRITE_CONGESTION_OFF; -static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ -static mempool_t psd_pool; -static struct bio_set pkt_bio_set; - -/* /sys/class/pktcdvd */ -static struct class class_pktcdvd; -static struct dentry *pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */ - -/* forward declaration */ -static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev); -static int pkt_remove_dev(dev_t pkt_dev); - -static sector_t get_zone(sector_t sector, struct pktcdvd_device *pd) -{ - return (sector + pd->offset) & ~(sector_t)(pd->settings.size - 1); -} - -/********************************************************** - * sysfs interface for pktcdvd - * by (C) 2006 Thomas Maier - - /sys/class/pktcdvd/pktcdvd[0-7]/ - stat/reset - stat/packets_started - stat/packets_finished - stat/kb_written - stat/kb_read - stat/kb_read_gather - write_queue/size - write_queue/congestion_off - write_queue/congestion_on - **********************************************************/ - -static ssize_t packets_started_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", pd->stats.pkt_started); -} -static DEVICE_ATTR_RO(packets_started); - -static ssize_t packets_finished_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", pd->stats.pkt_ended); -} -static DEVICE_ATTR_RO(packets_finished); - -static ssize_t kb_written_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", pd->stats.secs_w >> 1); -} -static DEVICE_ATTR_RO(kb_written); - -static ssize_t kb_read_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", pd->stats.secs_r >> 1); -} -static DEVICE_ATTR_RO(kb_read); - -static ssize_t kb_read_gather_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%lu\n", pd->stats.secs_rg >> 1); -} -static DEVICE_ATTR_RO(kb_read_gather); - -static ssize_t reset_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - - if (len > 0) { - pd->stats.pkt_started = 0; - pd->stats.pkt_ended = 0; - pd->stats.secs_w = 0; - pd->stats.secs_rg = 0; - pd->stats.secs_r = 0; - } - return len; -} -static DEVICE_ATTR_WO(reset); - -static struct attribute *pkt_stat_attrs[] = { - &dev_attr_packets_finished.attr, - &dev_attr_packets_started.attr, - &dev_attr_kb_read.attr, - &dev_attr_kb_written.attr, - &dev_attr_kb_read_gather.attr, - &dev_attr_reset.attr, - NULL, -}; - -static const struct attribute_group pkt_stat_group = { - .name = "stat", - .attrs = pkt_stat_attrs, -}; - -static ssize_t size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - int n; - - spin_lock(&pd->lock); - n = sysfs_emit(buf, "%d\n", pd->bio_queue_size); - spin_unlock(&pd->lock); - return n; -} -static DEVICE_ATTR_RO(size); - -static void init_write_congestion_marks(int* lo, int* hi) -{ - if (*hi > 0) { - *hi = max(*hi, 500); - *hi = min(*hi, 1000000); - if (*lo <= 0) - *lo = *hi - 100; - else { - *lo = min(*lo, *hi - 100); - *lo = max(*lo, 100); - } - } else { - *hi = -1; - *lo = -1; - } -} - -static ssize_t congestion_off_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - int n; - - spin_lock(&pd->lock); - n = sysfs_emit(buf, "%d\n", pd->write_congestion_off); - spin_unlock(&pd->lock); - return n; -} - -static ssize_t congestion_off_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - int val, ret; - - ret = kstrtoint(buf, 10, &val); - if (ret) - return ret; - - spin_lock(&pd->lock); - pd->write_congestion_off = val; - init_write_congestion_marks(&pd->write_congestion_off, &pd->write_congestion_on); - spin_unlock(&pd->lock); - return len; -} -static DEVICE_ATTR_RW(congestion_off); - -static ssize_t congestion_on_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - int n; - - spin_lock(&pd->lock); - n = sysfs_emit(buf, "%d\n", pd->write_congestion_on); - spin_unlock(&pd->lock); - return n; -} - -static ssize_t congestion_on_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct pktcdvd_device *pd = dev_get_drvdata(dev); - int val, ret; - - ret = kstrtoint(buf, 10, &val); - if (ret) - return ret; - - spin_lock(&pd->lock); - pd->write_congestion_on = val; - init_write_congestion_marks(&pd->write_congestion_off, &pd->write_congestion_on); - spin_unlock(&pd->lock); - return len; -} -static DEVICE_ATTR_RW(congestion_on); - -static struct attribute *pkt_wq_attrs[] = { - &dev_attr_congestion_on.attr, - &dev_attr_congestion_off.attr, - &dev_attr_size.attr, - NULL, -}; - -static const struct attribute_group pkt_wq_group = { - .name = "write_queue", - .attrs = pkt_wq_attrs, -}; - -static const struct attribute_group *pkt_groups[] = { - &pkt_stat_group, - &pkt_wq_group, - NULL, -}; - -static void pkt_sysfs_dev_new(struct pktcdvd_device *pd) -{ - if (class_is_registered(&class_pktcdvd)) { - pd->dev = device_create_with_groups(&class_pktcdvd, NULL, - MKDEV(0, 0), pd, pkt_groups, - "%s", pd->disk->disk_name); - if (IS_ERR(pd->dev)) - pd->dev = NULL; - } -} - -static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd) -{ - if (class_is_registered(&class_pktcdvd)) - device_unregister(pd->dev); -} - - -/******************************************************************** - /sys/class/pktcdvd/ - add map block device - remove unmap packet dev - device_map show mappings - *******************************************************************/ - -static ssize_t device_map_show(const struct class *c, const struct class_attribute *attr, - char *data) -{ - int n = 0; - int idx; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - for (idx = 0; idx < MAX_WRITERS; idx++) { - struct pktcdvd_device *pd = pkt_devs[idx]; - if (!pd) - continue; - n += sysfs_emit_at(data, n, "%s %u:%u %u:%u\n", - pd->disk->disk_name, - MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev), - MAJOR(file_bdev(pd->bdev_file)->bd_dev), - MINOR(file_bdev(pd->bdev_file)->bd_dev)); - } - mutex_unlock(&ctl_mutex); - return n; -} -static CLASS_ATTR_RO(device_map); - -static ssize_t add_store(const struct class *c, const struct class_attribute *attr, - const char *buf, size_t count) -{ - unsigned int major, minor; - - if (sscanf(buf, "%u:%u", &major, &minor) == 2) { - /* pkt_setup_dev() expects caller to hold reference to self */ - if (!try_module_get(THIS_MODULE)) - return -ENODEV; - - pkt_setup_dev(MKDEV(major, minor), NULL); - - module_put(THIS_MODULE); - - return count; - } - - return -EINVAL; -} -static CLASS_ATTR_WO(add); - -static ssize_t remove_store(const struct class *c, const struct class_attribute *attr, - const char *buf, size_t count) -{ - unsigned int major, minor; - if (sscanf(buf, "%u:%u", &major, &minor) == 2) { - pkt_remove_dev(MKDEV(major, minor)); - return count; - } - return -EINVAL; -} -static CLASS_ATTR_WO(remove); - -static struct attribute *class_pktcdvd_attrs[] = { - &class_attr_add.attr, - &class_attr_remove.attr, - &class_attr_device_map.attr, - NULL, -}; -ATTRIBUTE_GROUPS(class_pktcdvd); - -static struct class class_pktcdvd = { - .name = DRIVER_NAME, - .class_groups = class_pktcdvd_groups, -}; - -static int pkt_sysfs_init(void) -{ - /* - * create control files in sysfs - * /sys/class/pktcdvd/... - */ - return class_register(&class_pktcdvd); -} - -static void pkt_sysfs_cleanup(void) -{ - class_unregister(&class_pktcdvd); -} - -/******************************************************************** - entries in debugfs - - /sys/kernel/debug/pktcdvd[0-7]/ - info - - *******************************************************************/ - -static void pkt_count_states(struct pktcdvd_device *pd, int *states) -{ - struct packet_data *pkt; - int i; - - for (i = 0; i < PACKET_NUM_STATES; i++) - states[i] = 0; - - spin_lock(&pd->cdrw.active_list_lock); - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - states[pkt->state]++; - } - spin_unlock(&pd->cdrw.active_list_lock); -} - -static int pkt_seq_show(struct seq_file *m, void *p) -{ - struct pktcdvd_device *pd = m->private; - char *msg; - int states[PACKET_NUM_STATES]; - - seq_printf(m, "Writer %s mapped to %pg:\n", pd->disk->disk_name, - file_bdev(pd->bdev_file)); - - seq_printf(m, "\nSettings:\n"); - seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2); - - if (pd->settings.write_type == 0) - msg = "Packet"; - else - msg = "Unknown"; - seq_printf(m, "\twrite type:\t\t%s\n", msg); - - seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable"); - seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss); - - seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode); - - if (pd->settings.block_mode == PACKET_BLOCK_MODE1) - msg = "Mode 1"; - else if (pd->settings.block_mode == PACKET_BLOCK_MODE2) - msg = "Mode 2"; - else - msg = "Unknown"; - seq_printf(m, "\tblock mode:\t\t%s\n", msg); - - seq_printf(m, "\nStatistics:\n"); - seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started); - seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended); - seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1); - seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1); - seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1); - - seq_printf(m, "\nMisc:\n"); - seq_printf(m, "\treference count:\t%d\n", pd->refcnt); - seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags); - seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed); - seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed); - seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset); - seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset); - - seq_printf(m, "\nQueue state:\n"); - seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size); - seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios)); - seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", pd->current_sector); - - pkt_count_states(pd, states); - seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", - states[0], states[1], states[2], states[3], states[4], states[5]); - - seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n", - pd->write_congestion_off, - pd->write_congestion_on); - return 0; -} -DEFINE_SHOW_ATTRIBUTE(pkt_seq); - -static void pkt_debugfs_dev_new(struct pktcdvd_device *pd) -{ - if (!pkt_debugfs_root) - return; - pd->dfs_d_root = debugfs_create_dir(pd->disk->disk_name, pkt_debugfs_root); - - pd->dfs_f_info = debugfs_create_file("info", 0444, pd->dfs_d_root, - pd, &pkt_seq_fops); -} - -static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd) -{ - if (!pkt_debugfs_root) - return; - debugfs_remove(pd->dfs_f_info); - debugfs_remove(pd->dfs_d_root); - pd->dfs_f_info = NULL; - pd->dfs_d_root = NULL; -} - -static void pkt_debugfs_init(void) -{ - pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL); -} - -static void pkt_debugfs_cleanup(void) -{ - debugfs_remove(pkt_debugfs_root); - pkt_debugfs_root = NULL; -} - -/* ----------------------------------------------------------*/ - - -static void pkt_bio_finished(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - - BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0); - if (atomic_dec_and_test(&pd->cdrw.pending_bios)) { - dev_dbg(ddev, "queue empty\n"); - atomic_set(&pd->iosched.attention, 1); - wake_up(&pd->wqueue); - } -} - -/* - * Allocate a packet_data struct - */ -static struct packet_data *pkt_alloc_packet_data(int frames) -{ - int i; - struct packet_data *pkt; - - pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL); - if (!pkt) - goto no_pkt; - - pkt->frames = frames; - pkt->w_bio = bio_kmalloc(frames, GFP_KERNEL); - if (!pkt->w_bio) - goto no_bio; - - for (i = 0; i < frames / FRAMES_PER_PAGE; i++) { - pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO); - if (!pkt->pages[i]) - goto no_page; - } - - spin_lock_init(&pkt->lock); - bio_list_init(&pkt->orig_bios); - - for (i = 0; i < frames; i++) { - pkt->r_bios[i] = bio_kmalloc(1, GFP_KERNEL); - if (!pkt->r_bios[i]) - goto no_rd_bio; - } - - return pkt; - -no_rd_bio: - for (i = 0; i < frames; i++) - kfree(pkt->r_bios[i]); -no_page: - for (i = 0; i < frames / FRAMES_PER_PAGE; i++) - if (pkt->pages[i]) - __free_page(pkt->pages[i]); - kfree(pkt->w_bio); -no_bio: - kfree(pkt); -no_pkt: - return NULL; -} - -/* - * Free a packet_data struct - */ -static void pkt_free_packet_data(struct packet_data *pkt) -{ - int i; - - for (i = 0; i < pkt->frames; i++) - kfree(pkt->r_bios[i]); - for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++) - __free_page(pkt->pages[i]); - kfree(pkt->w_bio); - kfree(pkt); -} - -static void pkt_shrink_pktlist(struct pktcdvd_device *pd) -{ - struct packet_data *pkt, *next; - - BUG_ON(!list_empty(&pd->cdrw.pkt_active_list)); - - list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) { - pkt_free_packet_data(pkt); - } - INIT_LIST_HEAD(&pd->cdrw.pkt_free_list); -} - -static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets) -{ - struct packet_data *pkt; - - BUG_ON(!list_empty(&pd->cdrw.pkt_free_list)); - - while (nr_packets > 0) { - pkt = pkt_alloc_packet_data(pd->settings.size >> 2); - if (!pkt) { - pkt_shrink_pktlist(pd); - return 0; - } - pkt->id = nr_packets; - pkt->pd = pd; - list_add(&pkt->list, &pd->cdrw.pkt_free_list); - nr_packets--; - } - return 1; -} - -static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node) -{ - struct rb_node *n = rb_next(&node->rb_node); - if (!n) - return NULL; - return rb_entry(n, struct pkt_rb_node, rb_node); -} - -static void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node) -{ - rb_erase(&node->rb_node, &pd->bio_queue); - mempool_free(node, &pd->rb_pool); - pd->bio_queue_size--; - BUG_ON(pd->bio_queue_size < 0); -} - -/* - * Find the first node in the pd->bio_queue rb tree with a starting sector >= s. - */ -static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s) -{ - struct rb_node *n = pd->bio_queue.rb_node; - struct rb_node *next; - struct pkt_rb_node *tmp; - - if (!n) { - BUG_ON(pd->bio_queue_size > 0); - return NULL; - } - - for (;;) { - tmp = rb_entry(n, struct pkt_rb_node, rb_node); - if (s <= tmp->bio->bi_iter.bi_sector) - next = n->rb_left; - else - next = n->rb_right; - if (!next) - break; - n = next; - } - - if (s > tmp->bio->bi_iter.bi_sector) { - tmp = pkt_rbtree_next(tmp); - if (!tmp) - return NULL; - } - BUG_ON(s > tmp->bio->bi_iter.bi_sector); - return tmp; -} - -/* - * Insert a node into the pd->bio_queue rb tree. - */ -static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node) -{ - struct rb_node **p = &pd->bio_queue.rb_node; - struct rb_node *parent = NULL; - sector_t s = node->bio->bi_iter.bi_sector; - struct pkt_rb_node *tmp; - - while (*p) { - parent = *p; - tmp = rb_entry(parent, struct pkt_rb_node, rb_node); - if (s < tmp->bio->bi_iter.bi_sector) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - rb_link_node(&node->rb_node, parent, p); - rb_insert_color(&node->rb_node, &pd->bio_queue); - pd->bio_queue_size++; -} - -/* - * Send a packet_command to the underlying block device and - * wait for completion. - */ -static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc) -{ - struct request_queue *q = bdev_get_queue(file_bdev(pd->bdev_file)); - struct scsi_cmnd *scmd; - struct request *rq; - int ret = 0; - - rq = scsi_alloc_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? - REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); - if (IS_ERR(rq)) - return PTR_ERR(rq); - scmd = blk_mq_rq_to_pdu(rq); - - if (cgc->buflen) { - ret = blk_rq_map_kern(rq, cgc->buffer, cgc->buflen, - GFP_NOIO); - if (ret) - goto out; - } - - scmd->cmd_len = COMMAND_SIZE(cgc->cmd[0]); - memcpy(scmd->cmnd, cgc->cmd, CDROM_PACKET_SIZE); - - rq->timeout = 60*HZ; - if (cgc->quiet) - rq->rq_flags |= RQF_QUIET; - - blk_execute_rq(rq, false); - if (scmd->result) - ret = -EIO; -out: - blk_mq_free_request(rq); - return ret; -} - -static const char *sense_key_string(__u8 index) -{ - static const char * const info[] = { - "No sense", "Recovered error", "Not ready", - "Medium error", "Hardware error", "Illegal request", - "Unit attention", "Data protect", "Blank check", - }; - - return index < ARRAY_SIZE(info) ? info[index] : "INVALID"; -} - -/* - * A generic sense dump / resolve mechanism should be implemented across - * all ATAPI + SCSI devices. - */ -static void pkt_dump_sense(struct pktcdvd_device *pd, - struct packet_command *cgc) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct scsi_sense_hdr *sshdr = cgc->sshdr; - - if (sshdr) - dev_err(ddev, "%*ph - sense %02x.%02x.%02x (%s)\n", - CDROM_PACKET_SIZE, cgc->cmd, - sshdr->sense_key, sshdr->asc, sshdr->ascq, - sense_key_string(sshdr->sense_key)); - else - dev_err(ddev, "%*ph - no sense\n", CDROM_PACKET_SIZE, cgc->cmd); -} - -/* - * flush the drive cache to media - */ -static int pkt_flush_cache(struct pktcdvd_device *pd) -{ - struct packet_command cgc; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_FLUSH_CACHE; - cgc.quiet = 1; - - /* - * the IMMED bit -- we default to not setting it, although that - * would allow a much faster close, this is safer - */ -#if 0 - cgc.cmd[1] = 1 << 1; -#endif - return pkt_generic_packet(pd, &cgc); -} - -/* - * speed is given as the normal factor, e.g. 4 for 4x - */ -static noinline_for_stack int pkt_set_speed(struct pktcdvd_device *pd, - unsigned write_speed, unsigned read_speed) -{ - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - int ret; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.sshdr = &sshdr; - cgc.cmd[0] = GPCMD_SET_SPEED; - put_unaligned_be16(read_speed, &cgc.cmd[2]); - put_unaligned_be16(write_speed, &cgc.cmd[4]); - - ret = pkt_generic_packet(pd, &cgc); - if (ret) - pkt_dump_sense(pd, &cgc); - - return ret; -} - -/* - * Queue a bio for processing by the low-level CD device. Must be called - * from process context. - */ -static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio) -{ - /* - * Some CDRW drives can not handle writes larger than one packet, - * even if the size is a multiple of the packet size. - */ - bio->bi_opf |= REQ_NOMERGE; - - spin_lock(&pd->iosched.lock); - if (bio_data_dir(bio) == READ) - bio_list_add(&pd->iosched.read_queue, bio); - else - bio_list_add(&pd->iosched.write_queue, bio); - spin_unlock(&pd->iosched.lock); - - atomic_set(&pd->iosched.attention, 1); - wake_up(&pd->wqueue); -} - -/* - * Process the queued read/write requests. This function handles special - * requirements for CDRW drives: - * - A cache flush command must be inserted before a read request if the - * previous request was a write. - * - Switching between reading and writing is slow, so don't do it more often - * than necessary. - * - Optimize for throughput at the expense of latency. This means that streaming - * writes will never be interrupted by a read, but if the drive has to seek - * before the next write, switch to reading instead if there are any pending - * read requests. - * - Set the read speed according to current usage pattern. When only reading - * from the device, it's best to use the highest possible read speed, but - * when switching often between reading and writing, it's better to have the - * same read and write speeds. - */ -static void pkt_iosched_process_queue(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - - if (atomic_read(&pd->iosched.attention) == 0) - return; - atomic_set(&pd->iosched.attention, 0); - - for (;;) { - struct bio *bio; - int reads_queued, writes_queued; - - spin_lock(&pd->iosched.lock); - reads_queued = !bio_list_empty(&pd->iosched.read_queue); - writes_queued = !bio_list_empty(&pd->iosched.write_queue); - spin_unlock(&pd->iosched.lock); - - if (!reads_queued && !writes_queued) - break; - - if (pd->iosched.writing) { - int need_write_seek = 1; - spin_lock(&pd->iosched.lock); - bio = bio_list_peek(&pd->iosched.write_queue); - spin_unlock(&pd->iosched.lock); - if (bio && (bio->bi_iter.bi_sector == - pd->iosched.last_write)) - need_write_seek = 0; - if (need_write_seek && reads_queued) { - if (atomic_read(&pd->cdrw.pending_bios) > 0) { - dev_dbg(ddev, "write, waiting\n"); - break; - } - pkt_flush_cache(pd); - pd->iosched.writing = 0; - } - } else { - if (!reads_queued && writes_queued) { - if (atomic_read(&pd->cdrw.pending_bios) > 0) { - dev_dbg(ddev, "read, waiting\n"); - break; - } - pd->iosched.writing = 1; - } - } - - spin_lock(&pd->iosched.lock); - if (pd->iosched.writing) - bio = bio_list_pop(&pd->iosched.write_queue); - else - bio = bio_list_pop(&pd->iosched.read_queue); - spin_unlock(&pd->iosched.lock); - - if (!bio) - continue; - - if (bio_data_dir(bio) == READ) - pd->iosched.successive_reads += - bio->bi_iter.bi_size >> 10; - else { - pd->iosched.successive_reads = 0; - pd->iosched.last_write = bio_end_sector(bio); - } - if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) { - if (pd->read_speed == pd->write_speed) { - pd->read_speed = MAX_SPEED; - pkt_set_speed(pd, pd->write_speed, pd->read_speed); - } - } else { - if (pd->read_speed != pd->write_speed) { - pd->read_speed = pd->write_speed; - pkt_set_speed(pd, pd->write_speed, pd->read_speed); - } - } - - atomic_inc(&pd->cdrw.pending_bios); - submit_bio_noacct(bio); - } -} - -/* - * Special care is needed if the underlying block device has a small - * max_phys_segments value. - */ -static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q) -{ - struct device *ddev = disk_to_dev(pd->disk); - - if ((pd->settings.size << 9) / CD_FRAMESIZE <= queue_max_segments(q)) { - /* - * The cdrom device can handle one segment/frame - */ - clear_bit(PACKET_MERGE_SEGS, &pd->flags); - return 0; - } - - if ((pd->settings.size << 9) / PAGE_SIZE <= queue_max_segments(q)) { - /* - * We can handle this case at the expense of some extra memory - * copies during write operations - */ - set_bit(PACKET_MERGE_SEGS, &pd->flags); - return 0; - } - - dev_err(ddev, "cdrom max_phys_segments too small\n"); - return -EIO; -} - -static void pkt_end_io_read(struct bio *bio) -{ - struct packet_data *pkt = bio->bi_private; - struct pktcdvd_device *pd = pkt->pd; - BUG_ON(!pd); - - dev_dbg(disk_to_dev(pd->disk), "bio=%p sec0=%llx sec=%llx err=%d\n", - bio, pkt->sector, bio->bi_iter.bi_sector, bio->bi_status); - - if (bio->bi_status) - atomic_inc(&pkt->io_errors); - bio_uninit(bio); - if (atomic_dec_and_test(&pkt->io_wait)) { - atomic_inc(&pkt->run_sm); - wake_up(&pd->wqueue); - } - pkt_bio_finished(pd); -} - -static void pkt_end_io_packet_write(struct bio *bio) -{ - struct packet_data *pkt = bio->bi_private; - struct pktcdvd_device *pd = pkt->pd; - BUG_ON(!pd); - - dev_dbg(disk_to_dev(pd->disk), "id=%d, err=%d\n", pkt->id, bio->bi_status); - - pd->stats.pkt_ended++; - - bio_uninit(bio); - pkt_bio_finished(pd); - atomic_dec(&pkt->io_wait); - atomic_inc(&pkt->run_sm); - wake_up(&pd->wqueue); -} - -/* - * Schedule reads for the holes in a packet - */ -static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) -{ - struct device *ddev = disk_to_dev(pd->disk); - int frames_read = 0; - struct bio *bio; - int f; - char written[PACKET_MAX_SIZE]; - - BUG_ON(bio_list_empty(&pkt->orig_bios)); - - atomic_set(&pkt->io_wait, 0); - atomic_set(&pkt->io_errors, 0); - - /* - * Figure out which frames we need to read before we can write. - */ - memset(written, 0, sizeof(written)); - spin_lock(&pkt->lock); - bio_list_for_each(bio, &pkt->orig_bios) { - int first_frame = (bio->bi_iter.bi_sector - pkt->sector) / - (CD_FRAMESIZE >> 9); - int num_frames = bio->bi_iter.bi_size / CD_FRAMESIZE; - pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9); - BUG_ON(first_frame < 0); - BUG_ON(first_frame + num_frames > pkt->frames); - for (f = first_frame; f < first_frame + num_frames; f++) - written[f] = 1; - } - spin_unlock(&pkt->lock); - - if (pkt->cache_valid) { - dev_dbg(ddev, "zone %llx cached\n", pkt->sector); - goto out_account; - } - - /* - * Schedule reads for missing parts of the packet. - */ - for (f = 0; f < pkt->frames; f++) { - int p, offset; - - if (written[f]) - continue; - - bio = pkt->r_bios[f]; - bio_init(bio, file_bdev(pd->bdev_file), bio->bi_inline_vecs, 1, - REQ_OP_READ); - bio->bi_iter.bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9); - bio->bi_end_io = pkt_end_io_read; - bio->bi_private = pkt; - - p = (f * CD_FRAMESIZE) / PAGE_SIZE; - offset = (f * CD_FRAMESIZE) % PAGE_SIZE; - dev_dbg(ddev, "Adding frame %d, page:%p offs:%d\n", f, - pkt->pages[p], offset); - if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset)) - BUG(); - - atomic_inc(&pkt->io_wait); - pkt_queue_bio(pd, bio); - frames_read++; - } - -out_account: - dev_dbg(ddev, "need %d frames for zone %llx\n", frames_read, pkt->sector); - pd->stats.pkt_started++; - pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); -} - -/* - * Find a packet matching zone, or the least recently used packet if - * there is no match. - */ -static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone) -{ - struct packet_data *pkt; - - list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) { - if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) { - list_del_init(&pkt->list); - if (pkt->sector != zone) - pkt->cache_valid = 0; - return pkt; - } - } - BUG(); - return NULL; -} - -static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) -{ - if (pkt->cache_valid) { - list_add(&pkt->list, &pd->cdrw.pkt_free_list); - } else { - list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list); - } -} - -static inline void pkt_set_state(struct device *ddev, struct packet_data *pkt, - enum packet_data_state state) -{ - static const char *state_name[] = { - "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED" - }; - enum packet_data_state old_state = pkt->state; - - dev_dbg(ddev, "pkt %2d : s=%6llx %s -> %s\n", - pkt->id, pkt->sector, state_name[old_state], state_name[state]); - - pkt->state = state; -} - -/* - * Scan the work queue to see if we can start a new packet. - * returns non-zero if any work was done. - */ -static int pkt_handle_queue(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_data *pkt, *p; - struct bio *bio = NULL; - sector_t zone = 0; /* Suppress gcc warning */ - struct pkt_rb_node *node, *first_node; - struct rb_node *n; - - atomic_set(&pd->scan_queue, 0); - - if (list_empty(&pd->cdrw.pkt_free_list)) { - dev_dbg(ddev, "no pkt\n"); - return 0; - } - - /* - * Try to find a zone we are not already working on. - */ - spin_lock(&pd->lock); - first_node = pkt_rbtree_find(pd, pd->current_sector); - if (!first_node) { - n = rb_first(&pd->bio_queue); - if (n) - first_node = rb_entry(n, struct pkt_rb_node, rb_node); - } - node = first_node; - while (node) { - bio = node->bio; - zone = get_zone(bio->bi_iter.bi_sector, pd); - list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) { - if (p->sector == zone) { - bio = NULL; - goto try_next_bio; - } - } - break; -try_next_bio: - node = pkt_rbtree_next(node); - if (!node) { - n = rb_first(&pd->bio_queue); - if (n) - node = rb_entry(n, struct pkt_rb_node, rb_node); - } - if (node == first_node) - node = NULL; - } - spin_unlock(&pd->lock); - if (!bio) { - dev_dbg(ddev, "no bio\n"); - return 0; - } - - pkt = pkt_get_packet_data(pd, zone); - - pd->current_sector = zone + pd->settings.size; - pkt->sector = zone; - BUG_ON(pkt->frames != pd->settings.size >> 2); - pkt->write_size = 0; - - /* - * Scan work queue for bios in the same zone and link them - * to this packet. - */ - spin_lock(&pd->lock); - dev_dbg(ddev, "looking for zone %llx\n", zone); - while ((node = pkt_rbtree_find(pd, zone)) != NULL) { - sector_t tmp = get_zone(node->bio->bi_iter.bi_sector, pd); - - bio = node->bio; - dev_dbg(ddev, "found zone=%llx\n", tmp); - if (tmp != zone) - break; - pkt_rbtree_erase(pd, node); - spin_lock(&pkt->lock); - bio_list_add(&pkt->orig_bios, bio); - pkt->write_size += bio->bi_iter.bi_size / CD_FRAMESIZE; - spin_unlock(&pkt->lock); - } - /* check write congestion marks, and if bio_queue_size is - * below, wake up any waiters - */ - if (pd->congested && - pd->bio_queue_size <= pd->write_congestion_off) { - pd->congested = false; - wake_up_var(&pd->congested); - } - spin_unlock(&pd->lock); - - pkt->sleep_time = max(PACKET_WAIT_TIME, 1); - pkt_set_state(ddev, pkt, PACKET_WAITING_STATE); - atomic_set(&pkt->run_sm, 1); - - spin_lock(&pd->cdrw.active_list_lock); - list_add(&pkt->list, &pd->cdrw.pkt_active_list); - spin_unlock(&pd->cdrw.active_list_lock); - - return 1; -} - -/** - * bio_list_copy_data - copy contents of data buffers from one chain of bios to - * another - * @src: source bio list - * @dst: destination bio list - * - * Stops when it reaches the end of either the @src list or @dst list - that is, - * copies min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of - * bios). - */ -static void bio_list_copy_data(struct bio *dst, struct bio *src) -{ - struct bvec_iter src_iter = src->bi_iter; - struct bvec_iter dst_iter = dst->bi_iter; - - while (1) { - if (!src_iter.bi_size) { - src = src->bi_next; - if (!src) - break; - - src_iter = src->bi_iter; - } - - if (!dst_iter.bi_size) { - dst = dst->bi_next; - if (!dst) - break; - - dst_iter = dst->bi_iter; - } - - bio_copy_data_iter(dst, &dst_iter, src, &src_iter); - } -} - -/* - * Assemble a bio to write one packet and queue the bio for processing - * by the underlying block device. - */ -static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) -{ - struct device *ddev = disk_to_dev(pd->disk); - int f; - - bio_init(pkt->w_bio, file_bdev(pd->bdev_file), pkt->w_bio->bi_inline_vecs, - pkt->frames, REQ_OP_WRITE); - pkt->w_bio->bi_iter.bi_sector = pkt->sector; - pkt->w_bio->bi_end_io = pkt_end_io_packet_write; - pkt->w_bio->bi_private = pkt; - - /* XXX: locking? */ - for (f = 0; f < pkt->frames; f++) { - struct page *page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE]; - unsigned offset = (f * CD_FRAMESIZE) % PAGE_SIZE; - - if (!bio_add_page(pkt->w_bio, page, CD_FRAMESIZE, offset)) - BUG(); - } - dev_dbg(ddev, "vcnt=%d\n", pkt->w_bio->bi_vcnt); - - /* - * Fill-in bvec with data from orig_bios. - */ - spin_lock(&pkt->lock); - bio_list_copy_data(pkt->w_bio, pkt->orig_bios.head); - - pkt_set_state(ddev, pkt, PACKET_WRITE_WAIT_STATE); - spin_unlock(&pkt->lock); - - dev_dbg(ddev, "Writing %d frames for zone %llx\n", pkt->write_size, pkt->sector); - - if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) - pkt->cache_valid = 1; - else - pkt->cache_valid = 0; - - /* Start the write request */ - atomic_set(&pkt->io_wait, 1); - pkt_queue_bio(pd, pkt->w_bio); -} - -static void pkt_finish_packet(struct packet_data *pkt, blk_status_t status) -{ - struct bio *bio; - - if (status) - pkt->cache_valid = 0; - - /* Finish all bios corresponding to this packet */ - while ((bio = bio_list_pop(&pkt->orig_bios))) { - bio->bi_status = status; - bio_endio(bio); - } -} - -static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt) -{ - struct device *ddev = disk_to_dev(pd->disk); - - dev_dbg(ddev, "pkt %d\n", pkt->id); - - for (;;) { - switch (pkt->state) { - case PACKET_WAITING_STATE: - if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0)) - return; - - pkt->sleep_time = 0; - pkt_gather_data(pd, pkt); - pkt_set_state(ddev, pkt, PACKET_READ_WAIT_STATE); - break; - - case PACKET_READ_WAIT_STATE: - if (atomic_read(&pkt->io_wait) > 0) - return; - - if (atomic_read(&pkt->io_errors) > 0) { - pkt_set_state(ddev, pkt, PACKET_RECOVERY_STATE); - } else { - pkt_start_write(pd, pkt); - } - break; - - case PACKET_WRITE_WAIT_STATE: - if (atomic_read(&pkt->io_wait) > 0) - return; - - if (!pkt->w_bio->bi_status) { - pkt_set_state(ddev, pkt, PACKET_FINISHED_STATE); - } else { - pkt_set_state(ddev, pkt, PACKET_RECOVERY_STATE); - } - break; - - case PACKET_RECOVERY_STATE: - dev_dbg(ddev, "No recovery possible\n"); - pkt_set_state(ddev, pkt, PACKET_FINISHED_STATE); - break; - - case PACKET_FINISHED_STATE: - pkt_finish_packet(pkt, pkt->w_bio->bi_status); - return; - - default: - BUG(); - break; - } - } -} - -static void pkt_handle_packets(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_data *pkt, *next; - - /* - * Run state machine for active packets - */ - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - if (atomic_read(&pkt->run_sm) > 0) { - atomic_set(&pkt->run_sm, 0); - pkt_run_state_machine(pd, pkt); - } - } - - /* - * Move no longer active packets to the free list - */ - spin_lock(&pd->cdrw.active_list_lock); - list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) { - if (pkt->state == PACKET_FINISHED_STATE) { - list_del(&pkt->list); - pkt_put_packet_data(pd, pkt); - pkt_set_state(ddev, pkt, PACKET_IDLE_STATE); - atomic_set(&pd->scan_queue, 1); - } - } - spin_unlock(&pd->cdrw.active_list_lock); -} - -/* - * kcdrwd is woken up when writes have been queued for one of our - * registered devices - */ -static int kcdrwd(void *foobar) -{ - struct pktcdvd_device *pd = foobar; - struct device *ddev = disk_to_dev(pd->disk); - struct packet_data *pkt; - int states[PACKET_NUM_STATES]; - long min_sleep_time, residue; - - set_user_nice(current, MIN_NICE); - set_freezable(); - - for (;;) { - DECLARE_WAITQUEUE(wait, current); - - /* - * Wait until there is something to do - */ - add_wait_queue(&pd->wqueue, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - - /* Check if we need to run pkt_handle_queue */ - if (atomic_read(&pd->scan_queue) > 0) - goto work_to_do; - - /* Check if we need to run the state machine for some packet */ - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - if (atomic_read(&pkt->run_sm) > 0) - goto work_to_do; - } - - /* Check if we need to process the iosched queues */ - if (atomic_read(&pd->iosched.attention) != 0) - goto work_to_do; - - /* Otherwise, go to sleep */ - pkt_count_states(pd, states); - dev_dbg(ddev, "i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", - states[0], states[1], states[2], states[3], states[4], states[5]); - - min_sleep_time = MAX_SCHEDULE_TIMEOUT; - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - if (pkt->sleep_time && pkt->sleep_time < min_sleep_time) - min_sleep_time = pkt->sleep_time; - } - - dev_dbg(ddev, "sleeping\n"); - residue = schedule_timeout(min_sleep_time); - dev_dbg(ddev, "wake up\n"); - - /* make swsusp happy with our thread */ - try_to_freeze(); - - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - if (!pkt->sleep_time) - continue; - pkt->sleep_time -= min_sleep_time - residue; - if (pkt->sleep_time <= 0) { - pkt->sleep_time = 0; - atomic_inc(&pkt->run_sm); - } - } - - if (kthread_should_stop()) - break; - } -work_to_do: - set_current_state(TASK_RUNNING); - remove_wait_queue(&pd->wqueue, &wait); - - if (kthread_should_stop()) - break; - - /* - * if pkt_handle_queue returns true, we can queue - * another request. - */ - while (pkt_handle_queue(pd)) - ; - - /* - * Handle packet state machine - */ - pkt_handle_packets(pd); - - /* - * Handle iosched queues - */ - pkt_iosched_process_queue(pd); - } - - return 0; -} - -static void pkt_print_settings(struct pktcdvd_device *pd) -{ - dev_info(disk_to_dev(pd->disk), "%s packets, %u blocks, Mode-%c disc\n", - pd->settings.fp ? "Fixed" : "Variable", - pd->settings.size >> 2, - pd->settings.block_mode == 8 ? '1' : '2'); -} - -static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control) -{ - memset(cgc->cmd, 0, sizeof(cgc->cmd)); - - cgc->cmd[0] = GPCMD_MODE_SENSE_10; - cgc->cmd[2] = page_code | (page_control << 6); - put_unaligned_be16(cgc->buflen, &cgc->cmd[7]); - cgc->data_direction = CGC_DATA_READ; - return pkt_generic_packet(pd, cgc); -} - -static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc) -{ - memset(cgc->cmd, 0, sizeof(cgc->cmd)); - memset(cgc->buffer, 0, 2); - cgc->cmd[0] = GPCMD_MODE_SELECT_10; - cgc->cmd[1] = 0x10; /* PF */ - put_unaligned_be16(cgc->buflen, &cgc->cmd[7]); - cgc->data_direction = CGC_DATA_WRITE; - return pkt_generic_packet(pd, cgc); -} - -static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di) -{ - struct packet_command cgc; - int ret; - - /* set up command and get the disc info */ - init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DISC_INFO; - cgc.cmd[8] = cgc.buflen = 2; - cgc.quiet = 1; - - ret = pkt_generic_packet(pd, &cgc); - if (ret) - return ret; - - /* not all drives have the same disc_info length, so requeue - * packet with the length the drive tells us it can supply - */ - cgc.buflen = be16_to_cpu(di->disc_information_length) + - sizeof(di->disc_information_length); - - if (cgc.buflen > sizeof(disc_information)) - cgc.buflen = sizeof(disc_information); - - cgc.cmd[8] = cgc.buflen; - return pkt_generic_packet(pd, &cgc); -} - -static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti) -{ - struct packet_command cgc; - int ret; - - init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO; - cgc.cmd[1] = type & 3; - put_unaligned_be16(track, &cgc.cmd[4]); - cgc.cmd[8] = 8; - cgc.quiet = 1; - - ret = pkt_generic_packet(pd, &cgc); - if (ret) - return ret; - - cgc.buflen = be16_to_cpu(ti->track_information_length) + - sizeof(ti->track_information_length); - - if (cgc.buflen > sizeof(track_information)) - cgc.buflen = sizeof(track_information); - - cgc.cmd[8] = cgc.buflen; - return pkt_generic_packet(pd, &cgc); -} - -static noinline_for_stack int pkt_get_last_written(struct pktcdvd_device *pd, - long *last_written) -{ - disc_information di; - track_information ti; - __u32 last_track; - int ret; - - ret = pkt_get_disc_info(pd, &di); - if (ret) - return ret; - - last_track = (di.last_track_msb << 8) | di.last_track_lsb; - ret = pkt_get_track_info(pd, last_track, 1, &ti); - if (ret) - return ret; - - /* if this track is blank, try the previous. */ - if (ti.blank) { - last_track--; - ret = pkt_get_track_info(pd, last_track, 1, &ti); - if (ret) - return ret; - } - - /* if last recorded field is valid, return it. */ - if (ti.lra_v) { - *last_written = be32_to_cpu(ti.last_rec_address); - } else { - /* make it up instead */ - *last_written = be32_to_cpu(ti.track_start) + - be32_to_cpu(ti.track_size); - if (ti.free_blocks) - *last_written -= (be32_to_cpu(ti.free_blocks) + 7); - } - return 0; -} - -/* - * write mode select package based on pd->settings - */ -static noinline_for_stack int pkt_set_write_settings(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - write_param_page *wp; - char buffer[128]; - int ret, size; - - /* doesn't apply to DVD+RW or DVD-RAM */ - if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12)) - return 0; - - memset(buffer, 0, sizeof(buffer)); - init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ); - cgc.sshdr = &sshdr; - ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - - size = 2 + get_unaligned_be16(&buffer[0]); - pd->mode_offset = get_unaligned_be16(&buffer[6]); - if (size > sizeof(buffer)) - size = sizeof(buffer); - - /* - * now get it all - */ - init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ); - cgc.sshdr = &sshdr; - ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - - /* - * write page is offset header + block descriptor length - */ - wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset]; - - wp->fp = pd->settings.fp; - wp->track_mode = pd->settings.track_mode; - wp->write_type = pd->settings.write_type; - wp->data_block_type = pd->settings.block_mode; - - wp->multi_session = 0; - -#ifdef PACKET_USE_LS - wp->link_size = 7; - wp->ls_v = 1; -#endif - - if (wp->data_block_type == PACKET_BLOCK_MODE1) { - wp->session_format = 0; - wp->subhdr2 = 0x20; - } else if (wp->data_block_type == PACKET_BLOCK_MODE2) { - wp->session_format = 0x20; - wp->subhdr2 = 8; -#if 0 - wp->mcn[0] = 0x80; - memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1); -#endif - } else { - /* - * paranoia - */ - dev_err(ddev, "write mode wrong %d\n", wp->data_block_type); - return 1; - } - wp->packet_size = cpu_to_be32(pd->settings.size >> 2); - - cgc.buflen = cgc.cmd[8] = size; - ret = pkt_mode_select(pd, &cgc); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - - pkt_print_settings(pd); - return 0; -} - -/* - * 1 -- we can write to this track, 0 -- we can't - */ -static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti) -{ - struct device *ddev = disk_to_dev(pd->disk); - - switch (pd->mmc3_profile) { - case 0x1a: /* DVD+RW */ - case 0x12: /* DVD-RAM */ - /* The track is always writable on DVD+RW/DVD-RAM */ - return 1; - default: - break; - } - - if (!ti->packet || !ti->fp) - return 0; - - /* - * "good" settings as per Mt Fuji. - */ - if (ti->rt == 0 && ti->blank == 0) - return 1; - - if (ti->rt == 0 && ti->blank == 1) - return 1; - - if (ti->rt == 1 && ti->blank == 0) - return 1; - - dev_err(ddev, "bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); - return 0; -} - -/* - * 1 -- we can write to this disc, 0 -- we can't - */ -static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di) -{ - struct device *ddev = disk_to_dev(pd->disk); - - switch (pd->mmc3_profile) { - case 0x0a: /* CD-RW */ - case 0xffff: /* MMC3 not supported */ - break; - case 0x1a: /* DVD+RW */ - case 0x13: /* DVD-RW */ - case 0x12: /* DVD-RAM */ - return 1; - default: - dev_dbg(ddev, "Wrong disc profile (%x)\n", pd->mmc3_profile); - return 0; - } - - /* - * for disc type 0xff we should probably reserve a new track. - * but i'm not sure, should we leave this to user apps? probably. - */ - if (di->disc_type == 0xff) { - dev_notice(ddev, "unknown disc - no track?\n"); - return 0; - } - - if (di->disc_type != 0x20 && di->disc_type != 0) { - dev_err(ddev, "wrong disc type (%x)\n", di->disc_type); - return 0; - } - - if (di->erasable == 0) { - dev_err(ddev, "disc not erasable\n"); - return 0; - } - - if (di->border_status == PACKET_SESSION_RESERVED) { - dev_err(ddev, "can't write to last track (reserved)\n"); - return 0; - } - - return 1; -} - -static noinline_for_stack int pkt_probe_settings(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_command cgc; - unsigned char buf[12]; - disc_information di; - track_information ti; - int ret, track; - - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_GET_CONFIGURATION; - cgc.cmd[8] = 8; - ret = pkt_generic_packet(pd, &cgc); - pd->mmc3_profile = ret ? 0xffff : get_unaligned_be16(&buf[6]); - - memset(&di, 0, sizeof(disc_information)); - memset(&ti, 0, sizeof(track_information)); - - ret = pkt_get_disc_info(pd, &di); - if (ret) { - dev_err(ddev, "failed get_disc\n"); - return ret; - } - - if (!pkt_writable_disc(pd, &di)) - return -EROFS; - - pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; - - track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ - ret = pkt_get_track_info(pd, track, 1, &ti); - if (ret) { - dev_err(ddev, "failed get_track\n"); - return ret; - } - - if (!pkt_writable_track(pd, &ti)) { - dev_err(ddev, "can't write to this track\n"); - return -EROFS; - } - - /* - * we keep packet size in 512 byte units, makes it easier to - * deal with request calculations. - */ - pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; - if (pd->settings.size == 0) { - dev_notice(ddev, "detected zero packet size!\n"); - return -ENXIO; - } - if (pd->settings.size > PACKET_MAX_SECTORS) { - dev_err(ddev, "packet size is too big\n"); - return -EROFS; - } - pd->settings.fp = ti.fp; - pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); - - if (ti.nwa_v) { - pd->nwa = be32_to_cpu(ti.next_writable); - set_bit(PACKET_NWA_VALID, &pd->flags); - } - - /* - * in theory we could use lra on -RW media as well and just zero - * blocks that haven't been written yet, but in practice that - * is just a no-go. we'll use that for -R, naturally. - */ - if (ti.lra_v) { - pd->lra = be32_to_cpu(ti.last_rec_address); - set_bit(PACKET_LRA_VALID, &pd->flags); - } else { - pd->lra = 0xffffffff; - set_bit(PACKET_LRA_VALID, &pd->flags); - } - - /* - * fine for now - */ - pd->settings.link_loss = 7; - pd->settings.write_type = 0; /* packet */ - pd->settings.track_mode = ti.track_mode; - - /* - * mode1 or mode2 disc - */ - switch (ti.data_mode) { - case PACKET_MODE1: - pd->settings.block_mode = PACKET_BLOCK_MODE1; - break; - case PACKET_MODE2: - pd->settings.block_mode = PACKET_BLOCK_MODE2; - break; - default: - dev_err(ddev, "unknown data mode\n"); - return -EROFS; - } - return 0; -} - -/* - * enable/disable write caching on drive - */ -static noinline_for_stack int pkt_write_caching(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - unsigned char buf[64]; - bool set = IS_ENABLED(CONFIG_CDROM_PKTCDVD_WCACHE); - int ret; - - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.sshdr = &sshdr; - cgc.buflen = pd->mode_offset + 12; - - /* - * caching mode page might not be there, so quiet this command - */ - cgc.quiet = 1; - - ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0); - if (ret) - return ret; - - /* - * use drive write caching -- we need deferred error handling to be - * able to successfully recover with this option (drive will return good - * status as soon as the cdb is validated). - */ - buf[pd->mode_offset + 10] |= (set << 2); - - cgc.buflen = cgc.cmd[8] = 2 + get_unaligned_be16(&buf[0]); - ret = pkt_mode_select(pd, &cgc); - if (ret) { - dev_err(ddev, "write caching control failed\n"); - pkt_dump_sense(pd, &cgc); - } else if (!ret && set) - dev_notice(ddev, "enabled write caching\n"); - return ret; -} - -static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag) -{ - struct packet_command cgc; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; - cgc.cmd[4] = lockflag ? 1 : 0; - return pkt_generic_packet(pd, &cgc); -} - -/* - * Returns drive maximum write speed - */ -static noinline_for_stack int pkt_get_max_speed(struct pktcdvd_device *pd, - unsigned *write_speed) -{ - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - unsigned char buf[256+18]; - unsigned char *cap_buf; - int ret, offset; - - cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset]; - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); - cgc.sshdr = &sshdr; - - ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); - if (ret) { - cgc.buflen = pd->mode_offset + cap_buf[1] + 2 + - sizeof(struct mode_page_header); - ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - } - - offset = 20; /* Obsoleted field, used by older drives */ - if (cap_buf[1] >= 28) - offset = 28; /* Current write speed selected */ - if (cap_buf[1] >= 30) { - /* If the drive reports at least one "Logical Unit Write - * Speed Performance Descriptor Block", use the information - * in the first block. (contains the highest speed) - */ - int num_spdb = get_unaligned_be16(&cap_buf[30]); - if (num_spdb > 0) - offset = 34; - } - - *write_speed = get_unaligned_be16(&cap_buf[offset]); - return 0; -} - -/* These tables from cdrecord - I don't have orange book */ -/* standard speed CD-RW (1-4x) */ -static char clv_to_speed[16] = { - /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ - 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; -/* high speed CD-RW (-10x) */ -static char hs_clv_to_speed[16] = { - /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ - 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; -/* ultra high speed CD-RW */ -static char us_clv_to_speed[16] = { - /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ - 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0 -}; - -/* - * reads the maximum media speed from ATIP - */ -static noinline_for_stack int pkt_media_speed(struct pktcdvd_device *pd, - unsigned *speed) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - unsigned char buf[64]; - unsigned int size, st, sp; - int ret; - - init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ); - cgc.sshdr = &sshdr; - cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - cgc.cmd[1] = 2; - cgc.cmd[2] = 4; /* READ ATIP */ - cgc.cmd[8] = 2; - ret = pkt_generic_packet(pd, &cgc); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - size = 2 + get_unaligned_be16(&buf[0]); - if (size > sizeof(buf)) - size = sizeof(buf); - - init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); - cgc.sshdr = &sshdr; - cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - cgc.cmd[1] = 2; - cgc.cmd[2] = 4; - cgc.cmd[8] = size; - ret = pkt_generic_packet(pd, &cgc); - if (ret) { - pkt_dump_sense(pd, &cgc); - return ret; - } - - if (!(buf[6] & 0x40)) { - dev_notice(ddev, "disc type is not CD-RW\n"); - return 1; - } - if (!(buf[6] & 0x4)) { - dev_notice(ddev, "A1 values on media are not valid, maybe not CDRW?\n"); - return 1; - } - - st = (buf[6] >> 3) & 0x7; /* disc sub-type */ - - sp = buf[16] & 0xf; /* max speed from ATIP A1 field */ - - /* Info from cdrecord */ - switch (st) { - case 0: /* standard speed */ - *speed = clv_to_speed[sp]; - break; - case 1: /* high speed */ - *speed = hs_clv_to_speed[sp]; - break; - case 2: /* ultra high speed */ - *speed = us_clv_to_speed[sp]; - break; - default: - dev_notice(ddev, "unknown disc sub-type %d\n", st); - return 1; - } - if (*speed) { - dev_info(ddev, "maximum media speed: %d\n", *speed); - return 0; - } else { - dev_notice(ddev, "unknown speed %d for sub-type %d\n", sp, st); - return 1; - } -} - -static noinline_for_stack int pkt_perform_opc(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - struct packet_command cgc; - struct scsi_sense_hdr sshdr; - int ret; - - dev_dbg(ddev, "Performing OPC\n"); - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.sshdr = &sshdr; - cgc.timeout = 60*HZ; - cgc.cmd[0] = GPCMD_SEND_OPC; - cgc.cmd[1] = 1; - ret = pkt_generic_packet(pd, &cgc); - if (ret) - pkt_dump_sense(pd, &cgc); - return ret; -} - -static int pkt_open_write(struct pktcdvd_device *pd) -{ - struct device *ddev = disk_to_dev(pd->disk); - int ret; - unsigned int write_speed, media_write_speed, read_speed; - - ret = pkt_probe_settings(pd); - if (ret) { - dev_dbg(ddev, "failed probe\n"); - return ret; - } - - ret = pkt_set_write_settings(pd); - if (ret) { - dev_notice(ddev, "failed saving write settings\n"); - return -EIO; - } - - pkt_write_caching(pd); - - ret = pkt_get_max_speed(pd, &write_speed); - if (ret) - write_speed = 16 * 177; - switch (pd->mmc3_profile) { - case 0x13: /* DVD-RW */ - case 0x1a: /* DVD+RW */ - case 0x12: /* DVD-RAM */ - dev_notice(ddev, "write speed %ukB/s\n", write_speed); - break; - default: - ret = pkt_media_speed(pd, &media_write_speed); - if (ret) - media_write_speed = 16; - write_speed = min(write_speed, media_write_speed * 177); - dev_notice(ddev, "write speed %ux\n", write_speed / 176); - break; - } - read_speed = write_speed; - - ret = pkt_set_speed(pd, write_speed, read_speed); - if (ret) { - dev_notice(ddev, "couldn't set write speed\n"); - return -EIO; - } - pd->write_speed = write_speed; - pd->read_speed = read_speed; - - ret = pkt_perform_opc(pd); - if (ret) - dev_notice(ddev, "Optimum Power Calibration failed\n"); - - return 0; -} - -/* - * called at open time. - */ -static int pkt_open_dev(struct pktcdvd_device *pd, bool write) -{ - struct device *ddev = disk_to_dev(pd->disk); - int ret; - long lba; - struct request_queue *q; - struct file *bdev_file; - - /* - * We need to re-open the cdrom device without O_NONBLOCK to be able - * to read/write from/to it. It is already opened in O_NONBLOCK mode - * so open should not fail. - */ - bdev_file = bdev_file_open_by_dev(file_bdev(pd->bdev_file)->bd_dev, - BLK_OPEN_READ, pd, NULL); - if (IS_ERR(bdev_file)) { - ret = PTR_ERR(bdev_file); - goto out; - } - pd->f_open_bdev = bdev_file; - - ret = pkt_get_last_written(pd, &lba); - if (ret) { - dev_err(ddev, "pkt_get_last_written failed\n"); - goto out_putdev; - } - - set_capacity(pd->disk, lba << 2); - set_capacity_and_notify(file_bdev(pd->bdev_file)->bd_disk, lba << 2); - - q = bdev_get_queue(file_bdev(pd->bdev_file)); - if (write) { - ret = pkt_open_write(pd); - if (ret) - goto out_putdev; - set_bit(PACKET_WRITABLE, &pd->flags); - } else { - pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); - clear_bit(PACKET_WRITABLE, &pd->flags); - } - - ret = pkt_set_segment_merging(pd, q); - if (ret) - goto out_putdev; - - if (write) { - if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { - dev_err(ddev, "not enough memory for buffers\n"); - ret = -ENOMEM; - goto out_putdev; - } - dev_info(ddev, "%lukB available on disc\n", lba << 1); - } - set_blocksize(bdev_file, CD_FRAMESIZE); - - return 0; - -out_putdev: - fput(bdev_file); -out: - return ret; -} - -/* - * called when the device is closed. makes sure that the device flushes - * the internal cache before we close. - */ -static void pkt_release_dev(struct pktcdvd_device *pd, int flush) -{ - struct device *ddev = disk_to_dev(pd->disk); - - if (flush && pkt_flush_cache(pd)) - dev_notice(ddev, "not flushing cache\n"); - - pkt_lock_door(pd, 0); - - pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); - fput(pd->f_open_bdev); - pd->f_open_bdev = NULL; - - pkt_shrink_pktlist(pd); -} - -static struct pktcdvd_device *pkt_find_dev_from_minor(unsigned int dev_minor) -{ - if (dev_minor >= MAX_WRITERS) - return NULL; - - dev_minor = array_index_nospec(dev_minor, MAX_WRITERS); - return pkt_devs[dev_minor]; -} - -static int pkt_open(struct gendisk *disk, blk_mode_t mode) -{ - struct pktcdvd_device *pd = NULL; - int ret; - - mutex_lock(&pktcdvd_mutex); - mutex_lock(&ctl_mutex); - pd = pkt_find_dev_from_minor(disk->first_minor); - if (!pd) { - ret = -ENODEV; - goto out; - } - BUG_ON(pd->refcnt < 0); - - pd->refcnt++; - if (pd->refcnt > 1) { - if ((mode & BLK_OPEN_WRITE) && - !test_bit(PACKET_WRITABLE, &pd->flags)) { - ret = -EBUSY; - goto out_dec; - } - } else { - ret = pkt_open_dev(pd, mode & BLK_OPEN_WRITE); - if (ret) - goto out_dec; - } - mutex_unlock(&ctl_mutex); - mutex_unlock(&pktcdvd_mutex); - return 0; - -out_dec: - pd->refcnt--; -out: - mutex_unlock(&ctl_mutex); - mutex_unlock(&pktcdvd_mutex); - return ret; -} - -static void pkt_release(struct gendisk *disk) -{ - struct pktcdvd_device *pd = disk->private_data; - - mutex_lock(&pktcdvd_mutex); - mutex_lock(&ctl_mutex); - pd->refcnt--; - BUG_ON(pd->refcnt < 0); - if (pd->refcnt == 0) { - int flush = test_bit(PACKET_WRITABLE, &pd->flags); - pkt_release_dev(pd, flush); - } - mutex_unlock(&ctl_mutex); - mutex_unlock(&pktcdvd_mutex); -} - - -static void pkt_end_io_read_cloned(struct bio *bio) -{ - struct packet_stacked_data *psd = bio->bi_private; - struct pktcdvd_device *pd = psd->pd; - - psd->bio->bi_status = bio->bi_status; - bio_put(bio); - bio_endio(psd->bio); - mempool_free(psd, &psd_pool); - pkt_bio_finished(pd); -} - -static void pkt_make_request_read(struct pktcdvd_device *pd, struct bio *bio) -{ - struct bio *cloned_bio = bio_alloc_clone(file_bdev(pd->bdev_file), bio, - GFP_NOIO, &pkt_bio_set); - struct packet_stacked_data *psd = mempool_alloc(&psd_pool, GFP_NOIO); - - psd->pd = pd; - psd->bio = bio; - cloned_bio->bi_private = psd; - cloned_bio->bi_end_io = pkt_end_io_read_cloned; - pd->stats.secs_r += bio_sectors(bio); - pkt_queue_bio(pd, cloned_bio); -} - -static void pkt_make_request_write(struct bio *bio) -{ - struct pktcdvd_device *pd = bio->bi_bdev->bd_disk->private_data; - sector_t zone; - struct packet_data *pkt; - int was_empty, blocked_bio; - struct pkt_rb_node *node; - - zone = get_zone(bio->bi_iter.bi_sector, pd); - - /* - * If we find a matching packet in state WAITING or READ_WAIT, we can - * just append this bio to that packet. - */ - spin_lock(&pd->cdrw.active_list_lock); - blocked_bio = 0; - list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { - if (pkt->sector == zone) { - spin_lock(&pkt->lock); - if ((pkt->state == PACKET_WAITING_STATE) || - (pkt->state == PACKET_READ_WAIT_STATE)) { - bio_list_add(&pkt->orig_bios, bio); - pkt->write_size += - bio->bi_iter.bi_size / CD_FRAMESIZE; - if ((pkt->write_size >= pkt->frames) && - (pkt->state == PACKET_WAITING_STATE)) { - atomic_inc(&pkt->run_sm); - wake_up(&pd->wqueue); - } - spin_unlock(&pkt->lock); - spin_unlock(&pd->cdrw.active_list_lock); - return; - } else { - blocked_bio = 1; - } - spin_unlock(&pkt->lock); - } - } - spin_unlock(&pd->cdrw.active_list_lock); - - /* - * Test if there is enough room left in the bio work queue - * (queue size >= congestion on mark). - * If not, wait till the work queue size is below the congestion off mark. - */ - spin_lock(&pd->lock); - if (pd->write_congestion_on > 0 - && pd->bio_queue_size >= pd->write_congestion_on) { - struct wait_bit_queue_entry wqe; - - init_wait_var_entry(&wqe, &pd->congested, 0); - for (;;) { - prepare_to_wait_event(__var_waitqueue(&pd->congested), - &wqe.wq_entry, - TASK_UNINTERRUPTIBLE); - if (pd->bio_queue_size <= pd->write_congestion_off) - break; - pd->congested = true; - spin_unlock(&pd->lock); - schedule(); - spin_lock(&pd->lock); - } - } - spin_unlock(&pd->lock); - - /* - * No matching packet found. Store the bio in the work queue. - */ - node = mempool_alloc(&pd->rb_pool, GFP_NOIO); - node->bio = bio; - spin_lock(&pd->lock); - BUG_ON(pd->bio_queue_size < 0); - was_empty = (pd->bio_queue_size == 0); - pkt_rbtree_insert(pd, node); - spin_unlock(&pd->lock); - - /* - * Wake up the worker thread. - */ - atomic_set(&pd->scan_queue, 1); - if (was_empty) { - /* This wake_up is required for correct operation */ - wake_up(&pd->wqueue); - } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) { - /* - * This wake up is not required for correct operation, - * but improves performance in some cases. - */ - wake_up(&pd->wqueue); - } -} - -static void pkt_submit_bio(struct bio *bio) -{ - struct pktcdvd_device *pd = bio->bi_bdev->bd_disk->private_data; - struct device *ddev = disk_to_dev(pd->disk); - struct bio *split; - - bio = bio_split_to_limits(bio); - if (!bio) - return; - - dev_dbg(ddev, "start = %6llx stop = %6llx\n", - bio->bi_iter.bi_sector, bio_end_sector(bio)); - - /* - * Clone READ bios so we can have our own bi_end_io callback. - */ - if (bio_data_dir(bio) == READ) { - pkt_make_request_read(pd, bio); - return; - } - - if (!test_bit(PACKET_WRITABLE, &pd->flags)) { - dev_notice(ddev, "WRITE for ro device (%llu)\n", bio->bi_iter.bi_sector); - goto end_io; - } - - if (!bio->bi_iter.bi_size || (bio->bi_iter.bi_size % CD_FRAMESIZE)) { - dev_err(ddev, "wrong bio size\n"); - goto end_io; - } - - do { - sector_t zone = get_zone(bio->bi_iter.bi_sector, pd); - sector_t last_zone = get_zone(bio_end_sector(bio) - 1, pd); - - if (last_zone != zone) { - BUG_ON(last_zone != zone + pd->settings.size); - - split = bio_split(bio, last_zone - - bio->bi_iter.bi_sector, - GFP_NOIO, &pkt_bio_set); - bio_chain(split, bio); - } else { - split = bio; - } - - pkt_make_request_write(split); - } while (split != bio); - - return; -end_io: - bio_io_error(bio); -} - -static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) -{ - struct device *ddev = disk_to_dev(pd->disk); - int i; - struct file *bdev_file; - struct scsi_device *sdev; - - if (pd->pkt_dev == dev) { - dev_err(ddev, "recursive setup not allowed\n"); - return -EBUSY; - } - for (i = 0; i < MAX_WRITERS; i++) { - struct pktcdvd_device *pd2 = pkt_devs[i]; - if (!pd2) - continue; - if (file_bdev(pd2->bdev_file)->bd_dev == dev) { - dev_err(ddev, "%pg already setup\n", - file_bdev(pd2->bdev_file)); - return -EBUSY; - } - if (pd2->pkt_dev == dev) { - dev_err(ddev, "can't chain pktcdvd devices\n"); - return -EBUSY; - } - } - - bdev_file = bdev_file_open_by_dev(dev, BLK_OPEN_READ | BLK_OPEN_NDELAY, - NULL, NULL); - if (IS_ERR(bdev_file)) - return PTR_ERR(bdev_file); - sdev = scsi_device_from_queue(file_bdev(bdev_file)->bd_disk->queue); - if (!sdev) { - fput(bdev_file); - return -EINVAL; - } - put_device(&sdev->sdev_gendev); - - /* This is safe, since we have a reference from open(). */ - __module_get(THIS_MODULE); - - pd->bdev_file = bdev_file; - - atomic_set(&pd->cdrw.pending_bios, 0); - pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->disk->disk_name); - if (IS_ERR(pd->cdrw.thread)) { - dev_err(ddev, "can't start kernel thread\n"); - goto out_mem; - } - - proc_create_single_data(pd->disk->disk_name, 0, pkt_proc, pkt_seq_show, pd); - dev_notice(ddev, "writer mapped to %pg\n", file_bdev(bdev_file)); - return 0; - -out_mem: - fput(bdev_file); - /* This is safe: open() is still holding a reference. */ - module_put(THIS_MODULE); - return -ENOMEM; -} - -static int pkt_ioctl(struct block_device *bdev, blk_mode_t mode, - unsigned int cmd, unsigned long arg) -{ - struct pktcdvd_device *pd = bdev->bd_disk->private_data; - struct device *ddev = disk_to_dev(pd->disk); - int ret; - - dev_dbg(ddev, "cmd %x, dev %d:%d\n", cmd, MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); - - mutex_lock(&pktcdvd_mutex); - switch (cmd) { - case CDROMEJECT: - /* - * The door gets locked when the device is opened, so we - * have to unlock it or else the eject command fails. - */ - if (pd->refcnt == 1) - pkt_lock_door(pd, 0); - fallthrough; - /* - * forward selected CDROM ioctls to CD-ROM, for UDF - */ - case CDROMMULTISESSION: - case CDROMREADTOCENTRY: - case CDROM_LAST_WRITTEN: - case CDROM_SEND_PACKET: - case SCSI_IOCTL_SEND_COMMAND: - if (!bdev->bd_disk->fops->ioctl) - ret = -ENOTTY; - else - ret = bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg); - break; - default: - dev_dbg(ddev, "Unknown ioctl (%x)\n", cmd); - ret = -ENOTTY; - } - mutex_unlock(&pktcdvd_mutex); - - return ret; -} - -static unsigned int pkt_check_events(struct gendisk *disk, - unsigned int clearing) -{ - struct pktcdvd_device *pd = disk->private_data; - struct gendisk *attached_disk; - - if (!pd) - return 0; - if (!pd->bdev_file) - return 0; - attached_disk = file_bdev(pd->bdev_file)->bd_disk; - if (!attached_disk || !attached_disk->fops->check_events) - return 0; - return attached_disk->fops->check_events(attached_disk, clearing); -} - -static char *pkt_devnode(struct gendisk *disk, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "pktcdvd/%s", disk->disk_name); -} - -static const struct block_device_operations pktcdvd_ops = { - .owner = THIS_MODULE, - .submit_bio = pkt_submit_bio, - .open = pkt_open, - .release = pkt_release, - .ioctl = pkt_ioctl, - .compat_ioctl = blkdev_compat_ptr_ioctl, - .check_events = pkt_check_events, - .devnode = pkt_devnode, -}; - -/* - * Set up mapping from pktcdvd device to CD-ROM device. - */ -static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) -{ - struct queue_limits lim = { - .max_hw_sectors = PACKET_MAX_SECTORS, - .logical_block_size = CD_FRAMESIZE, - .features = BLK_FEAT_ROTATIONAL, - }; - int idx; - int ret = -ENOMEM; - struct pktcdvd_device *pd; - struct gendisk *disk; - - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - - for (idx = 0; idx < MAX_WRITERS; idx++) - if (!pkt_devs[idx]) - break; - if (idx == MAX_WRITERS) { - pr_err("max %d writers supported\n", MAX_WRITERS); - ret = -EBUSY; - goto out_mutex; - } - - pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); - if (!pd) - goto out_mutex; - - ret = mempool_init_kmalloc_pool(&pd->rb_pool, PKT_RB_POOL_SIZE, - sizeof(struct pkt_rb_node)); - if (ret) - goto out_mem; - - INIT_LIST_HEAD(&pd->cdrw.pkt_free_list); - INIT_LIST_HEAD(&pd->cdrw.pkt_active_list); - spin_lock_init(&pd->cdrw.active_list_lock); - - spin_lock_init(&pd->lock); - spin_lock_init(&pd->iosched.lock); - bio_list_init(&pd->iosched.read_queue); - bio_list_init(&pd->iosched.write_queue); - init_waitqueue_head(&pd->wqueue); - pd->bio_queue = RB_ROOT; - - pd->write_congestion_on = write_congestion_on; - pd->write_congestion_off = write_congestion_off; - - disk = blk_alloc_disk(&lim, NUMA_NO_NODE); - if (IS_ERR(disk)) { - ret = PTR_ERR(disk); - goto out_mem; - } - pd->disk = disk; - disk->major = pktdev_major; - disk->first_minor = idx; - disk->minors = 1; - disk->fops = &pktcdvd_ops; - disk->flags = GENHD_FL_REMOVABLE | GENHD_FL_NO_PART; - snprintf(disk->disk_name, sizeof(disk->disk_name), DRIVER_NAME"%d", idx); - disk->private_data = pd; - - pd->pkt_dev = MKDEV(pktdev_major, idx); - ret = pkt_new_dev(pd, dev); - if (ret) - goto out_mem2; - - /* inherit events of the host device */ - disk->events = file_bdev(pd->bdev_file)->bd_disk->events; - - ret = add_disk(disk); - if (ret) - goto out_mem2; - - pkt_sysfs_dev_new(pd); - pkt_debugfs_dev_new(pd); - - pkt_devs[idx] = pd; - if (pkt_dev) - *pkt_dev = pd->pkt_dev; - - mutex_unlock(&ctl_mutex); - return 0; - -out_mem2: - put_disk(disk); -out_mem: - mempool_exit(&pd->rb_pool); - kfree(pd); -out_mutex: - mutex_unlock(&ctl_mutex); - pr_err("setup of pktcdvd device failed\n"); - return ret; -} - -/* - * Tear down mapping from pktcdvd device to CD-ROM device. - */ -static int pkt_remove_dev(dev_t pkt_dev) -{ - struct pktcdvd_device *pd; - struct device *ddev; - int idx; - int ret = 0; - - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - - for (idx = 0; idx < MAX_WRITERS; idx++) { - pd = pkt_devs[idx]; - if (pd && (pd->pkt_dev == pkt_dev)) - break; - } - if (idx == MAX_WRITERS) { - pr_debug("dev not setup\n"); - ret = -ENXIO; - goto out; - } - - if (pd->refcnt > 0) { - ret = -EBUSY; - goto out; - } - - ddev = disk_to_dev(pd->disk); - - if (!IS_ERR(pd->cdrw.thread)) - kthread_stop(pd->cdrw.thread); - - pkt_devs[idx] = NULL; - - pkt_debugfs_dev_remove(pd); - pkt_sysfs_dev_remove(pd); - - fput(pd->bdev_file); - - remove_proc_entry(pd->disk->disk_name, pkt_proc); - dev_notice(ddev, "writer unmapped\n"); - - del_gendisk(pd->disk); - put_disk(pd->disk); - - mempool_exit(&pd->rb_pool); - kfree(pd); - - /* This is safe: open() is still holding a reference. */ - module_put(THIS_MODULE); - -out: - mutex_unlock(&ctl_mutex); - return ret; -} - -static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) -{ - struct pktcdvd_device *pd; - - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - - pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); - if (pd) { - ctrl_cmd->dev = new_encode_dev(file_bdev(pd->bdev_file)->bd_dev); - ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); - } else { - ctrl_cmd->dev = 0; - ctrl_cmd->pkt_dev = 0; - } - ctrl_cmd->num_devices = MAX_WRITERS; - - mutex_unlock(&ctl_mutex); -} - -static long pkt_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - struct pkt_ctrl_command ctrl_cmd; - int ret = 0; - dev_t pkt_dev = 0; - - if (cmd != PACKET_CTRL_CMD) - return -ENOTTY; - - if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command))) - return -EFAULT; - - switch (ctrl_cmd.command) { - case PKT_CTRL_CMD_SETUP: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev); - ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev); - break; - case PKT_CTRL_CMD_TEARDOWN: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev)); - break; - case PKT_CTRL_CMD_STATUS: - pkt_get_status(&ctrl_cmd); - break; - default: - return -ENOTTY; - } - - if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command))) - return -EFAULT; - return ret; -} - -#ifdef CONFIG_COMPAT -static long pkt_ctl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return pkt_ctl_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static const struct file_operations pkt_ctl_fops = { - .open = nonseekable_open, - .unlocked_ioctl = pkt_ctl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = pkt_ctl_compat_ioctl, -#endif - .owner = THIS_MODULE, -}; - -static struct miscdevice pkt_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = DRIVER_NAME, - .nodename = "pktcdvd/control", - .fops = &pkt_ctl_fops -}; - -static int __init pkt_init(void) -{ - int ret; - - mutex_init(&ctl_mutex); - - ret = mempool_init_kmalloc_pool(&psd_pool, PSD_POOL_SIZE, - sizeof(struct packet_stacked_data)); - if (ret) - return ret; - ret = bioset_init(&pkt_bio_set, BIO_POOL_SIZE, 0, 0); - if (ret) { - mempool_exit(&psd_pool); - return ret; - } - - ret = register_blkdev(pktdev_major, DRIVER_NAME); - if (ret < 0) { - pr_err("unable to register block device\n"); - goto out2; - } - if (!pktdev_major) - pktdev_major = ret; - - ret = pkt_sysfs_init(); - if (ret) - goto out; - - pkt_debugfs_init(); - - ret = misc_register(&pkt_misc); - if (ret) { - pr_err("unable to register misc device\n"); - goto out_misc; - } - - pkt_proc = proc_mkdir("driver/"DRIVER_NAME, NULL); - - return 0; - -out_misc: - pkt_debugfs_cleanup(); - pkt_sysfs_cleanup(); -out: - unregister_blkdev(pktdev_major, DRIVER_NAME); -out2: - mempool_exit(&psd_pool); - bioset_exit(&pkt_bio_set); - return ret; -} - -static void __exit pkt_exit(void) -{ - remove_proc_entry("driver/"DRIVER_NAME, NULL); - misc_deregister(&pkt_misc); - - pkt_debugfs_cleanup(); - pkt_sysfs_cleanup(); - - unregister_blkdev(pktdev_major, DRIVER_NAME); - mempool_exit(&psd_pool); - bioset_exit(&pkt_bio_set); -} - -MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); -MODULE_AUTHOR("Jens Axboe "); -MODULE_LICENSE("GPL"); - -module_init(pkt_init); -module_exit(pkt_exit); diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index b5727dea15bd..7af21fe67671 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -957,8 +957,10 @@ static bool vdc_port_mpgroup_check(struct vio_dev *vdev) dev = device_find_child(vdev->dev.parent, &port_data, vdc_device_probed); - if (dev) + if (dev) { + put_device(dev); return true; + } return false; } diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index c637ea010d34..67d4a867aec4 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -48,6 +48,8 @@ #define UBLK_MINORS (1U << MINORBITS) +#define UBLK_INVALID_BUF_IDX ((u16)-1) + /* private ioctl command mirror */ #define UBLK_CMD_DEL_DEV_ASYNC _IOC_NR(UBLK_U_CMD_DEL_DEV_ASYNC) #define UBLK_CMD_UPDATE_SIZE _IOC_NR(UBLK_U_CMD_UPDATE_SIZE) @@ -70,7 +72,8 @@ | UBLK_F_UPDATE_SIZE \ | UBLK_F_AUTO_BUF_REG \ | UBLK_F_QUIESCE \ - | UBLK_F_PER_IO_DAEMON) + | UBLK_F_PER_IO_DAEMON \ + | UBLK_F_BUF_REG_OFF_DAEMON) #define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \ | UBLK_F_USER_RECOVERY_REISSUE \ @@ -82,14 +85,6 @@ UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED | \ UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT) -struct ublk_rq_data { - refcount_t ref; - - /* for auto-unregister buffer in case of UBLK_F_AUTO_BUF_REG */ - u16 buf_index; - void *buf_ctx_handle; -}; - struct ublk_uring_cmd_pdu { /* * Store requests in same batch temporarily for queuing them to @@ -110,8 +105,6 @@ struct ublk_uring_cmd_pdu { */ struct ublk_queue *ubq; - struct ublk_auto_buf_reg buf; - u16 tag; }; @@ -155,9 +148,19 @@ struct ublk_uring_cmd_pdu { /* atomic RW with ubq->cancel_lock */ #define UBLK_IO_FLAG_CANCELED 0x80000000 +/* + * Initialize refcount to a large number to include any registered buffers. + * UBLK_IO_COMMIT_AND_FETCH_REQ will release these references minus those for + * any buffers registered on the io daemon task. + */ +#define UBLK_REFCOUNT_INIT (REFCOUNT_MAX / 2) + struct ublk_io { /* userspace buffer address from io cmd */ - __u64 addr; + union { + __u64 addr; + struct ublk_auto_buf_reg buf; + }; unsigned int flags; int res; @@ -169,7 +172,24 @@ struct ublk_io { }; struct task_struct *task; -}; + + /* + * The number of uses of this I/O by the ublk server + * if user copy or zero copy are enabled: + * - UBLK_REFCOUNT_INIT from dispatch to the server + * until UBLK_IO_COMMIT_AND_FETCH_REQ + * - 1 for each inflight ublk_ch_{read,write}_iter() call + * - 1 for each io_uring registered buffer not registered on task + * The I/O can only be completed once all references are dropped. + * User copy and buffer registration operations are only permitted + * if the reference count is nonzero. + */ + refcount_t ref; + /* Count of buffers registered on task and not yet unregistered */ + unsigned task_registered_buffers; + + void *buf_ctx_handle; +} ____cacheline_aligned_in_smp; struct ublk_queue { int q_id; @@ -215,7 +235,11 @@ struct ublk_device { struct completion completion; unsigned int nr_queues_ready; - unsigned int nr_privileged_daemon; + bool unprivileged_daemons; + struct mutex cancel_mutex; + bool canceling; + pid_t ublksrv_tgid; + struct delayed_work exit_work; }; /* header of ublk_params */ @@ -228,7 +252,8 @@ static void ublk_io_release(void *priv); static void ublk_stop_dev_unlocked(struct ublk_device *ub); static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq); static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, - const struct ublk_queue *ubq, int tag, size_t offset); + const struct ublk_queue *ubq, struct ublk_io *io, + size_t offset); static inline unsigned int ublk_req_build_flags(struct request *req); static inline struct ublksrv_io_desc * @@ -673,38 +698,29 @@ static inline bool ublk_need_req_ref(const struct ublk_queue *ubq) } static inline void ublk_init_req_ref(const struct ublk_queue *ubq, - struct request *req) + struct ublk_io *io) { - if (ublk_need_req_ref(ubq)) { - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); - - refcount_set(&data->ref, 1); - } + if (ublk_need_req_ref(ubq)) + refcount_set(&io->ref, UBLK_REFCOUNT_INIT); } -static inline bool ublk_get_req_ref(const struct ublk_queue *ubq, - struct request *req) +static inline bool ublk_get_req_ref(struct ublk_io *io) { - if (ublk_need_req_ref(ubq)) { - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); - - return refcount_inc_not_zero(&data->ref); - } - - return true; + return refcount_inc_not_zero(&io->ref); } -static inline void ublk_put_req_ref(const struct ublk_queue *ubq, - struct request *req) +static inline void ublk_put_req_ref(struct ublk_io *io, struct request *req) { - if (ublk_need_req_ref(ubq)) { - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); - - if (refcount_dec_and_test(&data->ref)) - __ublk_complete_rq(req); - } else { + if (refcount_dec_and_test(&io->ref)) __ublk_complete_rq(req); - } +} + +static inline bool ublk_sub_req_ref(struct ublk_io *io) +{ + unsigned sub_refs = UBLK_REFCOUNT_INIT - io->task_registered_buffers; + + io->task_registered_buffers = 0; + return refcount_sub_and_test(sub_refs, &io->ref); } static inline bool ublk_need_get_data(const struct ublk_queue *ubq) @@ -981,7 +997,7 @@ static inline bool ublk_need_unmap_req(const struct request *req) } static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req, - struct ublk_io *io) + const struct ublk_io *io) { const unsigned int rq_bytes = blk_rq_bytes(req); @@ -1005,7 +1021,7 @@ static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req, static int ublk_unmap_io(const struct ublk_queue *ubq, const struct request *req, - struct ublk_io *io) + const struct ublk_io *io) { const unsigned int rq_bytes = blk_rq_bytes(req); @@ -1140,7 +1156,7 @@ static inline void __ublk_complete_rq(struct request *req) if (blk_update_request(req, BLK_STS_OK, io->res)) blk_mq_requeue_request(req, true); - else + else if (likely(!blk_should_fake_timeout(req->q))) __blk_mq_end_request(req, BLK_STS_OK); return; @@ -1148,8 +1164,8 @@ static inline void __ublk_complete_rq(struct request *req) blk_mq_end_request(req, res); } -static void ublk_complete_io_cmd(struct ublk_io *io, struct request *req, - int res, unsigned issue_flags) +static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io, + struct request *req) { /* read cmd first because req will overwrite it */ struct io_uring_cmd *cmd = io->cmd; @@ -1164,6 +1180,13 @@ static void ublk_complete_io_cmd(struct ublk_io *io, struct request *req, io->flags &= ~UBLK_IO_FLAG_ACTIVE; io->req = req; + return cmd; +} + +static void ublk_complete_io_cmd(struct ublk_io *io, struct request *req, + int res, unsigned issue_flags) +{ + struct io_uring_cmd *cmd = __ublk_prep_compl_io_cmd(io, req); /* tell ublksrv one io request is coming */ io_uring_cmd_done(cmd, res, 0, issue_flags); @@ -1181,39 +1204,33 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq, blk_mq_end_request(rq, BLK_STS_IOERR); } -static void ublk_auto_buf_reg_fallback(struct request *req) +static void +ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, struct ublk_io *io) { - const struct ublk_queue *ubq = req->mq_hctx->driver_data; - struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); + unsigned tag = io - ubq->ios; + struct ublksrv_io_desc *iod = ublk_get_iod(ubq, tag); iod->op_flags |= UBLK_IO_F_NEED_REG_BUF; - refcount_set(&data->ref, 1); } -static bool ublk_auto_buf_reg(struct request *req, struct ublk_io *io, - unsigned int issue_flags) +static bool ublk_auto_buf_reg(const struct ublk_queue *ubq, struct request *req, + struct ublk_io *io, unsigned int issue_flags) { - struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(io->cmd); - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); int ret; ret = io_buffer_register_bvec(io->cmd, req, ublk_io_release, - pdu->buf.index, issue_flags); + io->buf.index, issue_flags); if (ret) { - if (pdu->buf.flags & UBLK_AUTO_BUF_REG_FALLBACK) { - ublk_auto_buf_reg_fallback(req); + if (io->buf.flags & UBLK_AUTO_BUF_REG_FALLBACK) { + ublk_auto_buf_reg_fallback(ubq, io); return true; } blk_mq_end_request(req, BLK_STS_IOERR); return false; } - /* one extra reference is dropped by ublk_io_release */ - refcount_set(&data->ref, 2); - data->buf_ctx_handle = io_uring_cmd_ctx_handle(io->cmd); - /* store buffer index in request payload */ - data->buf_index = pdu->buf.index; + io->task_registered_buffers = 1; + io->buf_ctx_handle = io_uring_cmd_ctx_handle(io->cmd); io->flags |= UBLK_IO_FLAG_AUTO_BUF_REG; return true; } @@ -1222,10 +1239,10 @@ static bool ublk_prep_auto_buf_reg(struct ublk_queue *ubq, struct request *req, struct ublk_io *io, unsigned int issue_flags) { + ublk_init_req_ref(ubq, io); if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req)) - return ublk_auto_buf_reg(req, io, issue_flags); + return ublk_auto_buf_reg(ubq, req, io, issue_flags); - ublk_init_req_ref(ubq, req); return true; } @@ -1349,14 +1366,23 @@ static void ublk_queue_cmd_list(struct ublk_io *io, struct rq_list *l) static enum blk_eh_timer_return ublk_timeout(struct request *rq) { struct ublk_queue *ubq = rq->mq_hctx->driver_data; - struct ublk_io *io = &ubq->ios[rq->tag]; + pid_t tgid = ubq->dev->ublksrv_tgid; + struct task_struct *p; + struct pid *pid; - if (ubq->flags & UBLK_F_UNPRIVILEGED_DEV) { - send_sig(SIGKILL, io->task, 0); - return BLK_EH_DONE; - } + if (!(ubq->flags & UBLK_F_UNPRIVILEGED_DEV)) + return BLK_EH_RESET_TIMER; - return BLK_EH_RESET_TIMER; + if (unlikely(!tgid)) + return BLK_EH_RESET_TIMER; + + rcu_read_lock(); + pid = find_vpid(tgid); + p = pid_task(pid, PIDTYPE_PID); + if (p) + send_sig(SIGKILL, p, 0); + rcu_read_unlock(); + return BLK_EH_DONE; } static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq, @@ -1364,7 +1390,7 @@ static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq, { blk_status_t res; - if (unlikely(ubq->fail_io)) + if (unlikely(READ_ONCE(ubq->fail_io))) return BLK_STS_TARGET; /* With recovery feature enabled, force_abort is set in @@ -1376,7 +1402,8 @@ static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq, * Note: force_abort is guaranteed to be seen because it is set * before request queue is unqiuesced. */ - if (ublk_nosrv_should_queue_io(ubq) && unlikely(ubq->force_abort)) + if (ublk_nosrv_should_queue_io(ubq) && + unlikely(READ_ONCE(ubq->force_abort))) return BLK_STS_IOERR; if (check_cancel && unlikely(ubq->canceling)) @@ -1416,6 +1443,14 @@ static blk_status_t ublk_queue_rq(struct blk_mq_hw_ctx *hctx, return BLK_STS_OK; } +static inline bool ublk_belong_to_same_batch(const struct ublk_io *io, + const struct ublk_io *io2) +{ + return (io_uring_cmd_ctx_handle(io->cmd) == + io_uring_cmd_ctx_handle(io2->cmd)) && + (io->task == io2->task); +} + static void ublk_queue_rqs(struct rq_list *rqlist) { struct rq_list requeue_list = { }; @@ -1427,14 +1462,16 @@ static void ublk_queue_rqs(struct rq_list *rqlist) struct ublk_queue *this_q = req->mq_hctx->driver_data; struct ublk_io *this_io = &this_q->ios[req->tag]; - if (io && io->task != this_io->task && !rq_list_empty(&submit_list)) + if (ublk_prep_req(this_q, req, true) != BLK_STS_OK) { + rq_list_add_tail(&requeue_list, req); + continue; + } + + if (io && !ublk_belong_to_same_batch(io, this_io) && + !rq_list_empty(&submit_list)) ublk_queue_cmd_list(io, &submit_list); io = this_io; - - if (ublk_prep_req(this_q, req, true) == BLK_STS_OK) - rq_list_add_tail(&submit_list, req); - else - rq_list_add_tail(&requeue_list, req); + rq_list_add_tail(&submit_list, req); } if (!rq_list_empty(&submit_list)) @@ -1487,6 +1524,9 @@ static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq) put_task_struct(io->task); io->task = NULL; } + + WARN_ON_ONCE(refcount_read(&io->ref)); + WARN_ON_ONCE(io->task_registered_buffers); } } @@ -1498,6 +1538,7 @@ static int ublk_ch_open(struct inode *inode, struct file *filp) if (test_and_set_bit(UB_STATE_OPEN, &ub->state)) return -EBUSY; filp->private_data = ub; + ub->ublksrv_tgid = current->tgid; return 0; } @@ -1511,7 +1552,8 @@ static void ublk_reset_ch_dev(struct ublk_device *ub) /* set to NULL, otherwise new tasks cannot mmap io_cmd_buf */ ub->mm = NULL; ub->nr_queues_ready = 0; - ub->nr_privileged_daemon = 0; + ub->unprivileged_daemons = false; + ub->ublksrv_tgid = -1; } static struct gendisk *ublk_get_disk(struct ublk_device *ub) @@ -1533,12 +1575,83 @@ static void ublk_put_disk(struct gendisk *disk) put_device(disk_to_dev(disk)); } -static int ublk_ch_release(struct inode *inode, struct file *filp) +/* + * Use this function to ensure that ->canceling is consistently set for + * the device and all queues. Do not set these flags directly. + * + * Caller must ensure that: + * - cancel_mutex is held. This ensures that there is no concurrent + * access to ub->canceling and no concurrent writes to ubq->canceling. + * - there are no concurrent reads of ubq->canceling from the queue_rq + * path. This can be done by quiescing the queue, or through other + * means. + */ +static void ublk_set_canceling(struct ublk_device *ub, bool canceling) + __must_hold(&ub->cancel_mutex) { - struct ublk_device *ub = filp->private_data; + int i; + + ub->canceling = canceling; + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) + ublk_get_queue(ub, i)->canceling = canceling; +} + +static bool ublk_check_and_reset_active_ref(struct ublk_device *ub) +{ + int i, j; + + if (!(ub->dev_info.flags & (UBLK_F_SUPPORT_ZERO_COPY | + UBLK_F_AUTO_BUF_REG))) + return false; + + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { + struct ublk_queue *ubq = ublk_get_queue(ub, i); + + for (j = 0; j < ubq->q_depth; j++) { + struct ublk_io *io = &ubq->ios[j]; + unsigned int refs = refcount_read(&io->ref) + + io->task_registered_buffers; + + /* + * UBLK_REFCOUNT_INIT or zero means no active + * reference + */ + if (refs != UBLK_REFCOUNT_INIT && refs != 0) + return true; + + /* reset to zero if the io hasn't active references */ + refcount_set(&io->ref, 0); + io->task_registered_buffers = 0; + } + } + return false; +} + +static void ublk_ch_release_work_fn(struct work_struct *work) +{ + struct ublk_device *ub = + container_of(work, struct ublk_device, exit_work.work); struct gendisk *disk; int i; + /* + * For zero-copy and auto buffer register modes, I/O references + * might not be dropped naturally when the daemon is killed, but + * io_uring guarantees that registered bvec kernel buffers are + * unregistered finally when freeing io_uring context, then the + * active references are dropped. + * + * Wait until active references are dropped for avoiding use-after-free + * + * registered buffer may be unregistered in io_ring's release hander, + * so have to wait by scheduling work function for avoiding the two + * file release dependency. + */ + if (ublk_check_and_reset_active_ref(ub)) { + schedule_delayed_work(&ub->exit_work, 1); + return; + } + /* * disk isn't attached yet, either device isn't live, or it has * been removed already, so we needn't to do anything @@ -1561,12 +1674,11 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) * All requests may be inflight, so ->canceling may not be set, set * it now. */ - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { - struct ublk_queue *ubq = ublk_get_queue(ub, i); - - ubq->canceling = true; - ublk_abort_queue(ub, ubq); - } + mutex_lock(&ub->cancel_mutex); + ublk_set_canceling(ub, true); + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) + ublk_abort_queue(ub, ublk_get_queue(ub, i)); + mutex_unlock(&ub->cancel_mutex); blk_mq_kick_requeue_list(disk->queue); /* @@ -1584,7 +1696,6 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) * Transition the device to the nosrv state. What exactly this * means depends on the recovery flags */ - blk_mq_quiesce_queue(disk->queue); if (ublk_nosrv_should_stop_dev(ub)) { /* * Allow any pending/future I/O to pass through quickly @@ -1592,8 +1703,7 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) * waits for all pending I/O to complete */ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) - ublk_get_queue(ub, i)->force_abort = true; - blk_mq_unquiesce_queue(disk->queue); + WRITE_ONCE(ublk_get_queue(ub, i)->force_abort, true); ublk_stop_dev_unlocked(ub); } else { @@ -1603,9 +1713,8 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) } else { ub->dev_info.state = UBLK_S_DEV_FAIL_IO; for (i = 0; i < ub->dev_info.nr_hw_queues; i++) - ublk_get_queue(ub, i)->fail_io = true; + WRITE_ONCE(ublk_get_queue(ub, i)->fail_io, true); } - blk_mq_unquiesce_queue(disk->queue); } unlock: mutex_unlock(&ub->mutex); @@ -1615,6 +1724,23 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) ublk_reset_ch_dev(ub); out: clear_bit(UB_STATE_OPEN, &ub->state); + + /* put the reference grabbed in ublk_ch_release() */ + ublk_put_device(ub); +} + +static int ublk_ch_release(struct inode *inode, struct file *filp) +{ + struct ublk_device *ub = filp->private_data; + + /* + * Grab ublk device reference, so it won't be gone until we are + * really released from work function. + */ + ublk_get_device(ub); + + INIT_DELAYED_WORK(&ub->exit_work, ublk_ch_release_work_fn); + schedule_delayed_work(&ub->exit_work, 0); return 0; } @@ -1689,23 +1815,17 @@ static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq) } } -/* Must be called when queue is frozen */ -static void ublk_mark_queue_canceling(struct ublk_queue *ubq) +static void ublk_start_cancel(struct ublk_device *ub) { - spin_lock(&ubq->cancel_lock); - if (!ubq->canceling) - ubq->canceling = true; - spin_unlock(&ubq->cancel_lock); -} - -static void ublk_start_cancel(struct ublk_queue *ubq) -{ - struct ublk_device *ub = ubq->dev; struct gendisk *disk = ublk_get_disk(ub); /* Our disk has been dead */ if (!disk) return; + + mutex_lock(&ub->cancel_mutex); + if (ub->canceling) + goto out; /* * Now we are serialized with ublk_queue_rq() * @@ -1714,8 +1834,10 @@ static void ublk_start_cancel(struct ublk_queue *ubq) * touch completed uring_cmd */ blk_mq_quiesce_queue(disk->queue); - ublk_mark_queue_canceling(ubq); + ublk_set_canceling(ub, true); blk_mq_unquiesce_queue(disk->queue); +out: + mutex_unlock(&ub->cancel_mutex); ublk_put_disk(disk); } @@ -1788,8 +1910,7 @@ static void ublk_uring_cmd_cancel_fn(struct io_uring_cmd *cmd, if (WARN_ON_ONCE(task && task != io->task)) return; - if (!ubq->canceling) - ublk_start_cancel(ubq); + ublk_start_cancel(ubq->dev); WARN_ON_ONCE(io->cmd != cmd); ublk_cancel_cmd(ubq, pdu->tag, issue_flags); @@ -1913,9 +2034,11 @@ static void ublk_reset_io_flags(struct ublk_device *ub) for (j = 0; j < ubq->q_depth; j++) ubq->ios[j].flags &= ~UBLK_IO_FLAG_CANCELED; spin_unlock(&ubq->cancel_lock); - ubq->canceling = false; ubq->fail_io = false; } + mutex_lock(&ub->cancel_mutex); + ublk_set_canceling(ub, false); + mutex_unlock(&ub->cancel_mutex); } /* device can only be started after all IOs are ready */ @@ -1923,12 +2046,10 @@ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq) __must_hold(&ub->mutex) { ubq->nr_io_ready++; - if (ublk_queue_ready(ubq)) { + if (ublk_queue_ready(ubq)) ub->nr_queues_ready++; - - if (capable(CAP_SYS_ADMIN)) - ub->nr_privileged_daemon++; - } + if (!ub->unprivileged_daemons && !capable(CAP_SYS_ADMIN)) + ub->unprivileged_daemons = true; if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues) { /* now we are ready for handling ublk io request */ @@ -1950,12 +2071,66 @@ static inline int ublk_check_cmd_op(u32 cmd_op) return 0; } -static inline void ublk_fill_io_cmd(struct ublk_io *io, - struct io_uring_cmd *cmd, unsigned long buf_addr) +static inline int ublk_set_auto_buf_reg(struct ublk_io *io, struct io_uring_cmd *cmd) { + io->buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr)); + + if (io->buf.reserved0 || io->buf.reserved1) + return -EINVAL; + + if (io->buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK) + return -EINVAL; + return 0; +} + +static int ublk_handle_auto_buf_reg(struct ublk_io *io, + struct io_uring_cmd *cmd, + u16 *buf_idx) +{ + if (io->flags & UBLK_IO_FLAG_AUTO_BUF_REG) { + io->flags &= ~UBLK_IO_FLAG_AUTO_BUF_REG; + + /* + * `UBLK_F_AUTO_BUF_REG` only works iff `UBLK_IO_FETCH_REQ` + * and `UBLK_IO_COMMIT_AND_FETCH_REQ` are issued from same + * `io_ring_ctx`. + * + * If this uring_cmd's io_ring_ctx isn't same with the + * one for registering the buffer, it is ublk server's + * responsibility for unregistering the buffer, otherwise + * this ublk request gets stuck. + */ + if (io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd)) + *buf_idx = io->buf.index; + } + + return ublk_set_auto_buf_reg(io, cmd); +} + +/* Once we return, `io->req` can't be used any more */ +static inline struct request * +ublk_fill_io_cmd(struct ublk_io *io, struct io_uring_cmd *cmd) +{ + struct request *req = io->req; + io->cmd = cmd; io->flags |= UBLK_IO_FLAG_ACTIVE; + /* now this cmd slot is owned by ublk driver */ + io->flags &= ~UBLK_IO_FLAG_OWNED_BY_SRV; + + return req; +} + +static inline int +ublk_config_io_buf(const struct ublk_queue *ubq, struct ublk_io *io, + struct io_uring_cmd *cmd, unsigned long buf_addr, + u16 *buf_idx) +{ + if (ublk_support_auto_buf_reg(ubq)) + return ublk_handle_auto_buf_reg(io, cmd, buf_idx); + io->addr = buf_addr; + return 0; } static inline void ublk_prep_cancel(struct io_uring_cmd *cmd, @@ -1973,30 +2148,25 @@ static inline void ublk_prep_cancel(struct io_uring_cmd *cmd, io_uring_cmd_mark_cancelable(cmd, issue_flags); } -static inline int ublk_set_auto_buf_reg(struct io_uring_cmd *cmd) -{ - struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd); - - pdu->buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr)); - - if (pdu->buf.reserved0 || pdu->buf.reserved1) - return -EINVAL; - - if (pdu->buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK) - return -EINVAL; - return 0; -} - static void ublk_io_release(void *priv) { struct request *rq = priv; struct ublk_queue *ubq = rq->mq_hctx->driver_data; + struct ublk_io *io = &ubq->ios[rq->tag]; - ublk_put_req_ref(ubq, rq); + /* + * task_registered_buffers may be 0 if buffers were registered off task + * but unregistered on task. Or after UBLK_IO_COMMIT_AND_FETCH_REQ. + */ + if (current == io->task && io->task_registered_buffers) + io->task_registered_buffers--; + else + ublk_put_req_ref(io, rq); } static int ublk_register_io_buf(struct io_uring_cmd *cmd, - const struct ublk_queue *ubq, unsigned int tag, + const struct ublk_queue *ubq, + struct ublk_io *io, unsigned int index, unsigned int issue_flags) { struct ublk_device *ub = cmd->file->private_data; @@ -2006,30 +2176,75 @@ static int ublk_register_io_buf(struct io_uring_cmd *cmd, if (!ublk_support_zero_copy(ubq)) return -EINVAL; - req = __ublk_check_and_get_req(ub, ubq, tag, 0); + req = __ublk_check_and_get_req(ub, ubq, io, 0); if (!req) return -EINVAL; ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index, issue_flags); if (ret) { - ublk_put_req_ref(ubq, req); + ublk_put_req_ref(io, req); return ret; } return 0; } +static int +ublk_daemon_register_io_buf(struct io_uring_cmd *cmd, + const struct ublk_queue *ubq, struct ublk_io *io, + unsigned index, unsigned issue_flags) +{ + unsigned new_registered_buffers; + struct request *req = io->req; + int ret; + + /* + * Ensure there are still references for ublk_sub_req_ref() to release. + * If not, fall back on the thread-safe buffer registration. + */ + new_registered_buffers = io->task_registered_buffers + 1; + if (unlikely(new_registered_buffers >= UBLK_REFCOUNT_INIT)) + return ublk_register_io_buf(cmd, ubq, io, index, issue_flags); + + if (!ublk_support_zero_copy(ubq) || !ublk_rq_has_data(req)) + return -EINVAL; + + ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index, + issue_flags); + if (ret) + return ret; + + io->task_registered_buffers = new_registered_buffers; + return 0; +} + static int ublk_unregister_io_buf(struct io_uring_cmd *cmd, - const struct ublk_queue *ubq, + const struct ublk_device *ub, unsigned int index, unsigned int issue_flags) { - if (!ublk_support_zero_copy(ubq)) + if (!(ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY)) return -EINVAL; return io_buffer_unregister_bvec(cmd, index, issue_flags); } +static int ublk_check_fetch_buf(const struct ublk_queue *ubq, __u64 buf_addr) +{ + if (ublk_need_map_io(ubq)) { + /* + * FETCH_RQ has to provide IO buffer if NEED GET + * DATA is not enabled + */ + if (!buf_addr && !ublk_need_get_data(ubq)) + return -EINVAL; + } else if (buf_addr) { + /* User copy requires addr to be unset */ + return -EINVAL; + } + return 0; +} + static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_queue *ubq, struct ublk_io *io, __u64 buf_addr) { @@ -2056,26 +2271,11 @@ static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_queue *ubq, WARN_ON_ONCE(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV); - if (ublk_need_map_io(ubq)) { - /* - * FETCH_RQ has to provide IO buffer if NEED GET - * DATA is not enabled - */ - if (!buf_addr && !ublk_need_get_data(ubq)) - goto out; - } else if (buf_addr) { - /* User copy requires addr to be unset */ - ret = -EINVAL; + ublk_fill_io_cmd(io, cmd); + ret = ublk_config_io_buf(ubq, io, cmd, buf_addr, NULL); + if (ret) goto out; - } - if (ublk_support_auto_buf_reg(ubq)) { - ret = ublk_set_auto_buf_reg(cmd); - if (ret) - goto out; - } - - ublk_fill_io_cmd(io, cmd, buf_addr); WRITE_ONCE(io->task, get_task_struct(current)); ublk_mark_io_ready(ub, ubq); out: @@ -2083,10 +2283,8 @@ static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_queue *ubq, return ret; } -static int ublk_commit_and_fetch(const struct ublk_queue *ubq, - struct ublk_io *io, struct io_uring_cmd *cmd, - const struct ublksrv_io_cmd *ub_cmd, - unsigned int issue_flags) +static int ublk_check_commit_and_fetch(const struct ublk_queue *ubq, + struct ublk_io *io, __u64 buf_addr) { struct request *req = io->req; @@ -2095,10 +2293,10 @@ static int ublk_commit_and_fetch(const struct ublk_queue *ubq, * COMMIT_AND_FETCH_REQ has to provide IO buffer if * NEED GET DATA is not enabled or it is Read IO. */ - if (!ub_cmd->addr && (!ublk_need_get_data(ubq) || + if (!buf_addr && (!ublk_need_get_data(ubq) || req_op(req) == REQ_OP_READ)) return -EINVAL; - } else if (req_op(req) != REQ_OP_ZONE_APPEND && ub_cmd->addr) { + } else if (req_op(req) != REQ_OP_ZONE_APPEND && buf_addr) { /* * User copy requires addr to be unset when command is * not zone append @@ -2106,52 +2304,20 @@ static int ublk_commit_and_fetch(const struct ublk_queue *ubq, return -EINVAL; } - if (ublk_support_auto_buf_reg(ubq)) { - int ret; - - /* - * `UBLK_F_AUTO_BUF_REG` only works iff `UBLK_IO_FETCH_REQ` - * and `UBLK_IO_COMMIT_AND_FETCH_REQ` are issued from same - * `io_ring_ctx`. - * - * If this uring_cmd's io_ring_ctx isn't same with the - * one for registering the buffer, it is ublk server's - * responsibility for unregistering the buffer, otherwise - * this ublk request gets stuck. - */ - if (io->flags & UBLK_IO_FLAG_AUTO_BUF_REG) { - struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); - - if (data->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd)) - io_buffer_unregister_bvec(cmd, data->buf_index, - issue_flags); - io->flags &= ~UBLK_IO_FLAG_AUTO_BUF_REG; - } - - ret = ublk_set_auto_buf_reg(cmd); - if (ret) - return ret; - } - - ublk_fill_io_cmd(io, cmd, ub_cmd->addr); - - /* now this cmd slot is owned by ublk driver */ - io->flags &= ~UBLK_IO_FLAG_OWNED_BY_SRV; - io->res = ub_cmd->result; - - if (req_op(req) == REQ_OP_ZONE_APPEND) - req->__sector = ub_cmd->zone_append_lba; - - if (likely(!blk_should_fake_timeout(req->q))) - ublk_put_req_ref(ubq, req); - return 0; } -static bool ublk_get_data(const struct ublk_queue *ubq, struct ublk_io *io) +static bool ublk_need_complete_req(const struct ublk_queue *ubq, + struct ublk_io *io) { - struct request *req = io->req; + if (ublk_need_req_ref(ubq)) + return ublk_sub_req_ref(io); + return true; +} +static bool ublk_get_data(const struct ublk_queue *ubq, struct ublk_io *io, + struct request *req) +{ /* * We have handled UBLK_IO_NEED_GET_DATA command, * so clear UBLK_IO_FLAG_NEED_GET_DATA now and just @@ -2171,18 +2337,33 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags, const struct ublksrv_io_cmd *ub_cmd) { + u16 buf_idx = UBLK_INVALID_BUF_IDX; struct ublk_device *ub = cmd->file->private_data; - struct task_struct *task; struct ublk_queue *ubq; struct ublk_io *io; u32 cmd_op = cmd->cmd_op; unsigned tag = ub_cmd->tag; - int ret = -EINVAL; + struct request *req; + int ret; + bool compl; pr_devel("%s: received: cmd op %d queue %d tag %d result %d\n", __func__, cmd->cmd_op, ub_cmd->q_id, tag, ub_cmd->result); + ret = ublk_check_cmd_op(cmd_op); + if (ret) + goto out; + + /* + * io_buffer_unregister_bvec() doesn't access the ubq or io, + * so no need to validate the q_id, tag, or task + */ + if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF) + return ublk_unregister_io_buf(cmd, ub, ub_cmd->addr, + issue_flags); + + ret = -EINVAL; if (ub_cmd->q_id >= ub->dev_info.nr_hw_queues) goto out; @@ -2192,20 +2373,36 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, goto out; io = &ubq->ios[tag]; - task = READ_ONCE(io->task); - if (task && task != current) - goto out; + /* UBLK_IO_FETCH_REQ can be handled on any task, which sets io->task */ + if (unlikely(_IOC_NR(cmd_op) == UBLK_IO_FETCH_REQ)) { + ret = ublk_check_fetch_buf(ubq, ub_cmd->addr); + if (ret) + goto out; + ret = ublk_fetch(cmd, ubq, io, ub_cmd->addr); + if (ret) + goto out; + + ublk_prep_cancel(cmd, issue_flags, ubq, tag); + return -EIOCBQUEUED; + } + + if (READ_ONCE(io->task) != current) { + /* + * ublk_register_io_buf() accesses only the io's refcount, + * so can be handled on any task + */ + if (_IOC_NR(cmd_op) == UBLK_IO_REGISTER_IO_BUF) + return ublk_register_io_buf(cmd, ubq, io, ub_cmd->addr, + issue_flags); - /* there is pending io cmd, something must be wrong */ - if (io->flags & UBLK_IO_FLAG_ACTIVE) { - ret = -EBUSY; goto out; } - /* only UBLK_IO_FETCH_REQ is allowed if io is not OWNED_BY_SRV */ - if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV) && - _IOC_NR(cmd_op) != UBLK_IO_FETCH_REQ) + /* there is pending io cmd, something must be wrong */ + if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)) { + ret = -EBUSY; goto out; + } /* * ensure that the user issues UBLK_IO_NEED_GET_DATA @@ -2215,32 +2412,44 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, ^ (_IOC_NR(cmd_op) == UBLK_IO_NEED_GET_DATA)) goto out; - ret = ublk_check_cmd_op(cmd_op); - if (ret) - goto out; - - ret = -EINVAL; switch (_IOC_NR(cmd_op)) { case UBLK_IO_REGISTER_IO_BUF: - return ublk_register_io_buf(cmd, ubq, tag, ub_cmd->addr, issue_flags); - case UBLK_IO_UNREGISTER_IO_BUF: - return ublk_unregister_io_buf(cmd, ubq, ub_cmd->addr, issue_flags); - case UBLK_IO_FETCH_REQ: - ret = ublk_fetch(cmd, ubq, io, ub_cmd->addr); + return ublk_daemon_register_io_buf(cmd, ubq, io, ub_cmd->addr, + issue_flags); + case UBLK_IO_COMMIT_AND_FETCH_REQ: + ret = ublk_check_commit_and_fetch(ubq, io, ub_cmd->addr); if (ret) goto out; - break; - case UBLK_IO_COMMIT_AND_FETCH_REQ: - ret = ublk_commit_and_fetch(ubq, io, cmd, ub_cmd, issue_flags); + io->res = ub_cmd->result; + req = ublk_fill_io_cmd(io, cmd); + ret = ublk_config_io_buf(ubq, io, cmd, ub_cmd->addr, &buf_idx); + compl = ublk_need_complete_req(ubq, io); + + /* can't touch 'ublk_io' any more */ + if (buf_idx != UBLK_INVALID_BUF_IDX) + io_buffer_unregister_bvec(cmd, buf_idx, issue_flags); + if (req_op(req) == REQ_OP_ZONE_APPEND) + req->__sector = ub_cmd->zone_append_lba; + if (compl) + __ublk_complete_rq(req); + if (ret) goto out; break; case UBLK_IO_NEED_GET_DATA: - io->addr = ub_cmd->addr; - if (!ublk_get_data(ubq, io)) - return -EIOCBQUEUED; - - return UBLK_IO_RES_OK; + /* + * ublk_get_data() may fail and fallback to requeue, so keep + * uring_cmd active first and prepare for handling new requeued + * request + */ + req = ublk_fill_io_cmd(io, cmd); + ret = ublk_config_io_buf(ubq, io, cmd, ub_cmd->addr, NULL); + WARN_ON_ONCE(ret); + if (likely(ublk_get_data(ubq, io, req))) { + __ublk_prep_compl_io_cmd(io, req); + return UBLK_IO_RES_OK; + } + break; default: goto out; } @@ -2254,15 +2463,20 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, } static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, - const struct ublk_queue *ubq, int tag, size_t offset) + const struct ublk_queue *ubq, struct ublk_io *io, size_t offset) { + unsigned tag = io - ubq->ios; struct request *req; + /* + * can't use io->req in case of concurrent UBLK_IO_COMMIT_AND_FETCH_REQ, + * which would overwrite it with io->cmd + */ req = blk_mq_tag_to_rq(ub->tag_set.tags[ubq->q_id], tag); if (!req) return NULL; - if (!ublk_get_req_ref(ubq, req)) + if (!ublk_get_req_ref(io)) return NULL; if (unlikely(!blk_mq_request_started(req) || req->tag != tag)) @@ -2276,7 +2490,7 @@ static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, return req; fail_put: - ublk_put_req_ref(ubq, req); + ublk_put_req_ref(io, req); return NULL; } @@ -2343,7 +2557,8 @@ static inline bool ublk_check_ubuf_dir(const struct request *req, } static struct request *ublk_check_and_get_req(struct kiocb *iocb, - struct iov_iter *iter, size_t *off, int dir) + struct iov_iter *iter, size_t *off, int dir, + struct ublk_io **io) { struct ublk_device *ub = iocb->ki_filp->private_data; struct ublk_queue *ubq; @@ -2377,7 +2592,8 @@ static struct request *ublk_check_and_get_req(struct kiocb *iocb, if (tag >= ubq->q_depth) return ERR_PTR(-EINVAL); - req = __ublk_check_and_get_req(ub, ubq, tag, buf_off); + *io = &ubq->ios[tag]; + req = __ublk_check_and_get_req(ub, ubq, *io, buf_off); if (!req) return ERR_PTR(-EINVAL); @@ -2390,42 +2606,40 @@ static struct request *ublk_check_and_get_req(struct kiocb *iocb, *off = buf_off; return req; fail: - ublk_put_req_ref(ubq, req); + ublk_put_req_ref(*io, req); return ERR_PTR(-EACCES); } static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct ublk_queue *ubq; struct request *req; + struct ublk_io *io; size_t buf_off; size_t ret; - req = ublk_check_and_get_req(iocb, to, &buf_off, ITER_DEST); + req = ublk_check_and_get_req(iocb, to, &buf_off, ITER_DEST, &io); if (IS_ERR(req)) return PTR_ERR(req); ret = ublk_copy_user_pages(req, buf_off, to, ITER_DEST); - ubq = req->mq_hctx->driver_data; - ublk_put_req_ref(ubq, req); + ublk_put_req_ref(io, req); return ret; } static ssize_t ublk_ch_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct ublk_queue *ubq; struct request *req; + struct ublk_io *io; size_t buf_off; size_t ret; - req = ublk_check_and_get_req(iocb, from, &buf_off, ITER_SOURCE); + req = ublk_check_and_get_req(iocb, from, &buf_off, ITER_SOURCE, &io); if (IS_ERR(req)) return PTR_ERR(req); ret = ublk_copy_user_pages(req, buf_off, from, ITER_SOURCE); - ubq = req->mq_hctx->driver_data; - ublk_put_req_ref(ubq, req); + ublk_put_req_ref(io, req); return ret; } @@ -2450,6 +2664,8 @@ static void ublk_deinit_queue(struct ublk_device *ub, int q_id) struct ublk_io *io = &ubq->ios[i]; if (io->task) put_task_struct(io->task); + WARN_ON_ONCE(refcount_read(&io->ref)); + WARN_ON_ONCE(io->task_registered_buffers); } if (ubq->io_cmd_buf) @@ -2488,7 +2704,7 @@ static void ublk_deinit_queues(struct ublk_device *ub) for (i = 0; i < nr_queues; i++) ublk_deinit_queue(ub, i); - kfree(ub->__queues); + kvfree(ub->__queues); } static int ublk_init_queues(struct ublk_device *ub) @@ -2499,7 +2715,7 @@ static int ublk_init_queues(struct ublk_device *ub) int i, ret = -ENOMEM; ub->queue_size = ubq_size; - ub->__queues = kcalloc(nr_queues, ubq_size, GFP_KERNEL); + ub->__queues = kvcalloc(nr_queues, ubq_size, GFP_KERNEL); if (!ub->__queues) return ret; @@ -2555,6 +2771,7 @@ static void ublk_cdev_rel(struct device *dev) ublk_deinit_queues(ub); ublk_free_dev_number(ub); mutex_destroy(&ub->mutex); + mutex_destroy(&ub->cancel_mutex); kfree(ub); } @@ -2602,7 +2819,6 @@ static int ublk_add_tag_set(struct ublk_device *ub) ub->tag_set.nr_hw_queues = ub->dev_info.nr_hw_queues; ub->tag_set.queue_depth = ub->dev_info.queue_depth; ub->tag_set.numa_node = NUMA_NO_NODE; - ub->tag_set.cmd_size = sizeof(struct ublk_rq_data); ub->tag_set.driver_data = ub; return blk_mq_alloc_tag_set(&ub->tag_set); } @@ -2704,6 +2920,9 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, if (wait_for_completion_interruptible(&ub->completion) != 0) return -EINTR; + if (ub->ublksrv_tgid != ublksrv_pid) + return -EINVAL; + mutex_lock(&ub->mutex); if (ub->dev_info.state == UBLK_S_DEV_LIVE || test_bit(UB_STATE_USED, &ub->state)) { @@ -2725,8 +2944,8 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, ublk_apply_params(ub); - /* don't probe partitions if any one ubq daemon is un-trusted */ - if (ub->nr_privileged_daemon != ub->nr_queues_ready) + /* don't probe partitions if any daemon task is un-trusted */ + if (ub->unprivileged_daemons) set_bit(GD_SUPPRESS_PART_SCAN, &disk->state); ublk_get_device(ub); @@ -2825,6 +3044,10 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; + if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth || + info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues) + return -EINVAL; + if (capable(CAP_SYS_ADMIN)) info.flags &= ~UBLK_F_UNPRIVILEGED_DEV; else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV)) @@ -2904,6 +3127,7 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) goto out_unlock; mutex_init(&ub->mutex); spin_lock_init(&ub->lock); + mutex_init(&ub->cancel_mutex); ret = ublk_alloc_dev_number(ub, header->dev_id); if (ret < 0) @@ -2924,7 +3148,8 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE | UBLK_F_URING_CMD_COMP_IN_TASK | - UBLK_F_PER_IO_DAEMON; + UBLK_F_PER_IO_DAEMON | + UBLK_F_BUF_REG_OFF_DAEMON; /* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */ if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY | @@ -2974,6 +3199,7 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) ublk_free_dev_number(ub); out_free_ub: mutex_destroy(&ub->mutex); + mutex_destroy(&ub->cancel_mutex); kfree(ub); out_unlock: mutex_unlock(&ublk_ctl_mutex); @@ -3198,6 +3424,9 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub, pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__, header->dev_id); + if (ub->ublksrv_tgid != ublksrv_pid) + return -EINVAL; + mutex_lock(&ub->mutex); if (ublk_nosrv_should_stop_dev(ub)) goto out_unlock; @@ -3311,7 +3540,7 @@ static int ublk_ctrl_quiesce_dev(struct ublk_device *ub, /* zero means wait forever */ u64 timeout_ms = header->data[0]; struct gendisk *disk; - int i, ret = -ENODEV; + int ret = -ENODEV; if (!(ub->dev_info.flags & UBLK_F_QUIESCE)) return -EOPNOTSUPP; @@ -3328,14 +3557,12 @@ static int ublk_ctrl_quiesce_dev(struct ublk_device *ub, if (ub->dev_info.state != UBLK_S_DEV_LIVE) goto put_disk; - /* Mark all queues as canceling */ + /* Mark the device as canceling */ + mutex_lock(&ub->cancel_mutex); blk_mq_quiesce_queue(disk->queue); - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { - struct ublk_queue *ubq = ublk_get_queue(ub, i); - - ubq->canceling = true; - } + ublk_set_canceling(ub, true); blk_mq_unquiesce_queue(disk->queue); + mutex_unlock(&ub->cancel_mutex); if (!timeout_ms) timeout_ms = UINT_MAX; diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 30bca8cb7106..e649fa67bac1 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -976,9 +976,8 @@ static int init_vq(struct virtio_blk *vblk) return -EINVAL; } - num_vqs = min_t(unsigned int, - min_not_zero(num_request_queues, nr_cpu_ids), - num_vqs); + num_vqs = blk_mq_num_possible_queues( + min_not_zero(num_request_queues, num_vqs)); num_poll_vqs = min_t(unsigned int, poll_queues, num_vqs - 1); diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c index 553b1a713ab9..a423228e201b 100644 --- a/drivers/block/zloop.c +++ b/drivers/block/zloop.c @@ -700,6 +700,8 @@ static void zloop_free_disk(struct gendisk *disk) struct zloop_device *zlo = disk->private_data; unsigned int i; + blk_mq_free_tag_set(&zlo->tag_set); + for (i = 0; i < zlo->nr_zones; i++) { struct zloop_zone *zone = &zlo->zones[i]; @@ -1080,7 +1082,6 @@ static int zloop_ctl_remove(struct zloop_options *opts) del_gendisk(zlo->disk); put_disk(zlo->disk); - blk_mq_free_tag_set(&zlo->tag_set); pr_info("Removed device %d\n", opts->id); diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index d26a58c67e95..b1bd1daa0060 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "zcomp.h" @@ -89,23 +90,21 @@ bool zcomp_available_algorithm(const char *comp) } /* show available compressors */ -ssize_t zcomp_available_show(const char *comp, char *buf) +ssize_t zcomp_available_show(const char *comp, char *buf, ssize_t at) { - ssize_t sz = 0; int i; for (i = 0; i < ARRAY_SIZE(backends) - 1; i++) { if (!strcmp(comp, backends[i]->name)) { - sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, - "[%s] ", backends[i]->name); + at += sysfs_emit_at(buf, at, "[%s] ", + backends[i]->name); } else { - sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, - "%s ", backends[i]->name); + at += sysfs_emit_at(buf, at, "%s ", backends[i]->name); } } - sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); - return sz; + at += sysfs_emit_at(buf, at, "\n"); + return at; } struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index 4acffe671a5e..eacfd3f7d61d 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -79,7 +79,7 @@ struct zcomp { int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node); int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node); -ssize_t zcomp_available_show(const char *comp, char *buf); +ssize_t zcomp_available_show(const char *comp, char *buf, ssize_t at); bool zcomp_available_algorithm(const char *comp); struct zcomp *zcomp_create(const char *alg, struct zcomp_params *params); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 54c57103715f..8acad3cc6e6e 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -373,7 +373,7 @@ static ssize_t initstate_show(struct device *dev, val = init_done(zram); up_read(&zram->init_lock); - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t disksize_show(struct device *dev, @@ -381,7 +381,7 @@ static ssize_t disksize_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); + return sysfs_emit(buf, "%llu\n", zram->disksize); } static ssize_t mem_limit_store(struct device *dev, @@ -532,7 +532,7 @@ static ssize_t writeback_limit_enable_show(struct device *dev, spin_unlock(&zram->wb_limit_lock); up_read(&zram->init_lock); - return scnprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t writeback_limit_store(struct device *dev, @@ -567,7 +567,7 @@ static ssize_t writeback_limit_show(struct device *dev, spin_unlock(&zram->wb_limit_lock); up_read(&zram->init_lock); - return scnprintf(buf, PAGE_SIZE, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static void reset_bdev(struct zram *zram) @@ -1225,12 +1225,13 @@ static void comp_algorithm_set(struct zram *zram, u32 prio, const char *alg) zram->comp_algs[prio] = alg; } -static ssize_t __comp_algorithm_show(struct zram *zram, u32 prio, char *buf) +static ssize_t __comp_algorithm_show(struct zram *zram, u32 prio, + char *buf, ssize_t at) { ssize_t sz; down_read(&zram->init_lock); - sz = zcomp_available_show(zram->comp_algs[prio], buf); + sz = zcomp_available_show(zram->comp_algs[prio], buf, at); up_read(&zram->init_lock); return sz; @@ -1387,7 +1388,7 @@ static ssize_t comp_algorithm_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return __comp_algorithm_show(zram, ZRAM_PRIMARY_COMP, buf); + return __comp_algorithm_show(zram, ZRAM_PRIMARY_COMP, buf, 0); } static ssize_t comp_algorithm_store(struct device *dev, @@ -1415,8 +1416,8 @@ static ssize_t recomp_algorithm_show(struct device *dev, if (!zram->comp_algs[prio]) continue; - sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, "#%d: ", prio); - sz += __comp_algorithm_show(zram, prio, buf + sz); + sz += sysfs_emit_at(buf, sz, "#%d: ", prio); + sz += __comp_algorithm_show(zram, prio, buf, sz); } return sz; @@ -1488,7 +1489,7 @@ static ssize_t io_stat_show(struct device *dev, ssize_t ret; down_read(&zram->init_lock); - ret = scnprintf(buf, PAGE_SIZE, + ret = sysfs_emit(buf, "%8llu %8llu 0 %8llu\n", (u64)atomic64_read(&zram->stats.failed_reads), (u64)atomic64_read(&zram->stats.failed_writes), @@ -1518,7 +1519,7 @@ static ssize_t mm_stat_show(struct device *dev, orig_size = atomic64_read(&zram->stats.pages_stored); max_used = atomic_long_read(&zram->stats.max_used_pages); - ret = scnprintf(buf, PAGE_SIZE, + ret = sysfs_emit(buf, "%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu %8llu\n", orig_size << PAGE_SHIFT, (u64)atomic64_read(&zram->stats.compr_data_size), @@ -1543,8 +1544,8 @@ static ssize_t bd_stat_show(struct device *dev, ssize_t ret; down_read(&zram->init_lock); - ret = scnprintf(buf, PAGE_SIZE, - "%8llu %8llu %8llu\n", + ret = sysfs_emit(buf, + "%8llu %8llu %8llu\n", FOUR_K((u64)atomic64_read(&zram->stats.bd_count)), FOUR_K((u64)atomic64_read(&zram->stats.bd_reads)), FOUR_K((u64)atomic64_read(&zram->stats.bd_writes))); @@ -1562,7 +1563,7 @@ static ssize_t debug_stat_show(struct device *dev, ssize_t ret; down_read(&zram->init_lock); - ret = scnprintf(buf, PAGE_SIZE, + ret = sysfs_emit(buf, "version: %d\n0 %8llu\n", version, (u64)atomic64_read(&zram->stats.miss_free)); @@ -2810,7 +2811,7 @@ static ssize_t hot_add_show(const struct class *class, if (ret < 0) return ret; - return scnprintf(buf, PAGE_SIZE, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /* This attribute must be set to 0400, so CLASS_ATTR_RO() can not be used */ static struct class_attribute class_attr_hot_add = diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 0d6ad50da046..8df310983bf6 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -670,7 +670,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i hdev->flush = bfusb_flush; hdev->send = bfusb_send_frame; - set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_COMMANDS); if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 1fa58c059cbf..8b43dfc755de 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -398,7 +398,7 @@ static int bpa10x_probe(struct usb_interface *intf, hdev->send = bpa10x_send_frame; hdev->set_diag = bpa10x_set_diag; - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); err = hci_register_dev(hdev); if (err < 0) { diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 0a60660fc8ce..3a3a56ddbb06 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -135,7 +135,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) if (btbcm_set_bdaddr_from_efi(hdev) != 0) { bt_dev_info(hdev, "BCM: Using default device address (%pMR)", &bda->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } } @@ -467,7 +467,7 @@ static int btbcm_print_controller_features(struct hci_dev *hdev) /* Read DMI and disable broken Read LE Min/Max Tx Power */ if (dmi_first_match(disable_broken_read_transmit_power)) - set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER); return 0; } @@ -706,7 +706,7 @@ int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_m btbcm_check_bdaddr(hdev); - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); return 0; } @@ -769,7 +769,7 @@ int btbcm_setup_apple(struct hci_dev *hdev) kfree_skb(skb); } - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); return 0; } diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 55cc1652bfe4..be69d21c9aa7 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -88,7 +88,7 @@ int btintel_check_bdaddr(struct hci_dev *hdev) if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) { bt_dev_err(hdev, "Found Intel default device address (%pMR)", &bda->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } kfree_skb(skb); @@ -555,7 +555,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev, /* Consume Command Complete Status field */ skb_pull(skb, 1); - /* Event parameters contatin multiple TLVs. Read each of them + /* Event parameters contain multiple TLVs. Read each of them * and only keep the required data. Also, it use existing legacy * version field like hw_platform, hw_variant, and fw_variant * to keep the existing setup flow @@ -889,7 +889,7 @@ int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param) params.boot_param = cpu_to_le32(boot_param); - skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params), ¶ms, + skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_RESET, sizeof(params), ¶ms, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Failed to send Intel Reset command"); @@ -1287,7 +1287,7 @@ static void btintel_reset_to_bootloader(struct hci_dev *hdev) params.boot_option = 0x00; params.boot_param = cpu_to_le32(0x00000000); - skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params), + skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_RESET, sizeof(params), ¶ms, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "FW download error recovery failed (%ld)", @@ -2027,7 +2027,7 @@ static int btintel_download_fw(struct hci_dev *hdev, */ if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) { bt_dev_info(hdev, "No device address configured"); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } download: @@ -2295,7 +2295,7 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev, */ if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) { bt_dev_info(hdev, "No device address configured"); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } } @@ -2670,7 +2670,7 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb) * Distinguish ISO data packets form ACL data packets * based on their connection handle value range. */ - if (hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) { + if (iso_capable(hdev) && hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) { __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); if (hci_handle(handle) >= BTINTEL_ISODATA_HANDLE_BASE) @@ -3435,9 +3435,9 @@ static int btintel_setup_combined(struct hci_dev *hdev) } /* Apply the common HCI quirks for Intel device */ - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_DIAG); /* Set up the quality report callback for Intel devices */ hdev->set_quality_report = btintel_set_quality_report; @@ -3475,8 +3475,8 @@ static int btintel_setup_combined(struct hci_dev *hdev) */ if (!btintel_test_flag(hdev, INTEL_ROM_LEGACY_NO_WBS_SUPPORT)) - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, - &hdev->quirks); + hci_set_quirk(hdev, + HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); err = btintel_legacy_rom_setup(hdev, &ver); break; @@ -3491,11 +3491,11 @@ static int btintel_setup_combined(struct hci_dev *hdev) * * All Legacy bootloader devices support WBS */ - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, - &hdev->quirks); + hci_set_quirk(hdev, + HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* These variants don't seem to support LE Coded PHY */ - set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, ver.hw_variant); @@ -3571,10 +3571,10 @@ static int btintel_setup_combined(struct hci_dev *hdev) * * All Legacy bootloader devices support WBS */ - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* These variants don't seem to support LE Coded PHY */ - set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, ver.hw_variant); @@ -3600,7 +3600,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) * * All TLV based devices support WBS */ - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 1d12c4113c66..431998049e68 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -52,6 +52,8 @@ struct intel_tlv { u8 val[]; } __packed; +#define BTINTEL_HCI_OP_RESET 0xfc01 + #define BTINTEL_CNVI_BLAZARI 0x900 #define BTINTEL_CNVI_BLAZARIW 0x901 #define BTINTEL_CNVI_GAP 0x910 diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 563165c5efae..6e7bbbd35279 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -35,12 +35,20 @@ /* Intel Bluetooth PCIe device id table */ static const struct pci_device_id btintel_pcie_table[] = { + { BTINTEL_PCI_DEVICE(0x4D76, PCI_ANY_ID) }, { BTINTEL_PCI_DEVICE(0xA876, PCI_ANY_ID) }, { BTINTEL_PCI_DEVICE(0xE476, PCI_ANY_ID) }, { 0 } }; MODULE_DEVICE_TABLE(pci, btintel_pcie_table); +struct btintel_pcie_dev_recovery { + struct list_head list; + u8 count; + time64_t last_error; + char name[]; +}; + /* Intel PCIe uses 4 bytes of HCI type instead of 1 byte BT SIG HCI type */ #define BTINTEL_PCIE_HCI_TYPE_LEN 4 #define BTINTEL_PCIE_HCI_CMD_PKT 0x00000001 @@ -62,6 +70,9 @@ MODULE_DEVICE_TABLE(pci, btintel_pcie_table); #define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER 0x17A2 #define BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT 0x1E61 +#define BTINTEL_PCIE_RESET_WINDOW_SECS 5 +#define BTINTEL_PCIE_FLR_MAX_RETRY 1 + /* Alive interrupt context */ enum { BTINTEL_PCIE_ROM, @@ -99,6 +110,36 @@ struct btintel_pcie_dbgc_ctxt { struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT]; }; +struct btintel_pcie_removal { + struct pci_dev *pdev; + struct work_struct work; +}; + +static LIST_HEAD(btintel_pcie_recovery_list); +static DEFINE_SPINLOCK(btintel_pcie_recovery_lock); + +static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt) +{ + switch (alive_intr_ctxt) { + case BTINTEL_PCIE_ROM: + return "rom"; + case BTINTEL_PCIE_FW_DL: + return "fw_dl"; + case BTINTEL_PCIE_D0: + return "d0"; + case BTINTEL_PCIE_D3: + return "d3"; + case BTINTEL_PCIE_HCI_RESET: + return "hci_reset"; + case BTINTEL_PCIE_INTEL_HCI_RESET1: + return "intel_reset1"; + case BTINTEL_PCIE_INTEL_HCI_RESET2: + return "intel_reset2"; + default: + return "unknown"; + } +} + /* This function initializes the memory for DBGC buffers and formats the * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and * size as the payload @@ -299,10 +340,14 @@ static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev) } static int btintel_pcie_send_sync(struct btintel_pcie_data *data, - struct sk_buff *skb) + struct sk_buff *skb, u32 pkt_type, u16 opcode) { int ret; u16 tfd_index; + u32 old_ctxt; + bool wait_on_alive = false; + struct hci_dev *hdev = data->hdev; + struct txq *txq = &data->txq; tfd_index = data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM]; @@ -310,6 +355,26 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data, if (tfd_index > txq->count) return -ERANGE; + /* Firmware raises alive interrupt on HCI_OP_RESET or + * BTINTEL_HCI_OP_RESET + */ + wait_on_alive = (pkt_type == BTINTEL_PCIE_HCI_CMD_PKT && + (opcode == BTINTEL_HCI_OP_RESET || opcode == HCI_OP_RESET)); + + if (wait_on_alive) { + data->gp0_received = false; + old_ctxt = data->alive_intr_ctxt; + data->alive_intr_ctxt = + (opcode == BTINTEL_HCI_OP_RESET ? BTINTEL_PCIE_INTEL_HCI_RESET1 : + BTINTEL_PCIE_HCI_RESET); + bt_dev_dbg(data->hdev, "sending cmd: 0x%4.4x alive context changed: %s -> %s", + opcode, btintel_pcie_alivectxt_state2str(old_ctxt), + btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt)); + } + + memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &pkt_type, + BTINTEL_PCIE_HCI_TYPE_LEN); + /* Prepare for TX. It updates the TFD with the length of data and * address of the DMA buffer, and copy the data to the DMA buffer */ @@ -328,11 +393,24 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data, ret = wait_event_timeout(data->tx_wait_q, data->tx_wait_done, msecs_to_jiffies(BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS)); if (!ret) { - bt_dev_err(data->hdev, "tx completion timeout"); + bt_dev_err(data->hdev, "Timeout (%u ms) on tx completion", + BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS); btintel_pcie_dump_debug_registers(data->hdev); return -ETIME; } + if (wait_on_alive) { + ret = wait_event_timeout(data->gp0_wait_q, + data->gp0_received, + msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); + if (!ret) { + hdev->stat.err_tx++; + bt_dev_err(hdev, "Timeout (%u ms) on alive interrupt, alive context: %s", + BTINTEL_DEFAULT_INTR_TIMEOUT_MS, + btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt)); + return -ETIME; + } + } return 0; } @@ -811,28 +889,6 @@ static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data, btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG, dxstate); } -static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt) -{ - switch (alive_intr_ctxt) { - case BTINTEL_PCIE_ROM: - return "rom"; - case BTINTEL_PCIE_FW_DL: - return "fw_dl"; - case BTINTEL_PCIE_D0: - return "d0"; - case BTINTEL_PCIE_D3: - return "d3"; - case BTINTEL_PCIE_HCI_RESET: - return "hci_reset"; - case BTINTEL_PCIE_INTEL_HCI_RESET1: - return "intel_reset1"; - case BTINTEL_PCIE_INTEL_HCI_RESET2: - return "intel_reset2"; - default: - return "unknown"; - } -} - static int btintel_pcie_read_device_mem(struct btintel_pcie_data *data, void *buf, u32 dev_addr, int len) { @@ -928,11 +984,13 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) case BTINTEL_PCIE_INTEL_HCI_RESET1: if (btintel_pcie_in_op(data)) { submit_rx = true; + signal_waitq = true; break; } if (btintel_pcie_in_iml(data)) { submit_rx = true; + signal_waitq = true; data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL; break; } @@ -1930,7 +1988,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, __u16 opcode = ~0; int ret; u32 type; - u32 old_ctxt; + + if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) + return -ENODEV; /* Due to the fw limitation, the type header of the packet should be * 4 bytes unlike 1 byte for UART. In UART, the firmware can read @@ -1955,17 +2015,14 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, struct hci_command_hdr *cmd = (void *)skb->data; __u16 opcode = le16_to_cpu(cmd->opcode); - /* When the 0xfc01 command is issued to boot into - * the operational firmware, it will actually not - * send a command complete event. To keep the flow + /* When the BTINTEL_HCI_OP_RESET command is issued to + * boot into the operational firmware, it will actually + * not send a command complete event. To keep the flow * control working inject that event here. */ - if (opcode == 0xfc01) + if (opcode == BTINTEL_HCI_OP_RESET) btintel_pcie_inject_cmd_complete(hdev, opcode); } - /* Firmware raises alive interrupt on HCI_OP_RESET */ - if (opcode == HCI_OP_RESET) - data->gp0_received = false; hdev->stat.cmd_tx++; break; @@ -1984,38 +2041,14 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, bt_dev_err(hdev, "Unknown HCI packet type"); return -EILSEQ; } - memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &type, - BTINTEL_PCIE_HCI_TYPE_LEN); - ret = btintel_pcie_send_sync(data, skb); + ret = btintel_pcie_send_sync(data, skb, type, opcode); if (ret) { hdev->stat.err_tx++; bt_dev_err(hdev, "Failed to send frame (%d)", ret); goto exit_error; } - if (type == BTINTEL_PCIE_HCI_CMD_PKT && - (opcode == HCI_OP_RESET || opcode == 0xfc01)) { - old_ctxt = data->alive_intr_ctxt; - data->alive_intr_ctxt = - (opcode == 0xfc01 ? BTINTEL_PCIE_INTEL_HCI_RESET1 : - BTINTEL_PCIE_HCI_RESET); - bt_dev_dbg(data->hdev, "sent cmd: 0x%4.4x alive context changed: %s -> %s", - opcode, btintel_pcie_alivectxt_state2str(old_ctxt), - btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt)); - if (opcode == HCI_OP_RESET) { - ret = wait_event_timeout(data->gp0_wait_q, - data->gp0_received, - msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); - if (!ret) { - hdev->stat.err_tx++; - bt_dev_err(hdev, "No alive interrupt received for %s", - btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt)); - ret = -ETIME; - goto exit_error; - } - } - } hdev->stat.byte_tx += skb->len; kfree_skb(skb); @@ -2033,6 +2066,28 @@ static void btintel_pcie_release_hdev(struct btintel_pcie_data *data) data->hdev = NULL; } +static void btintel_pcie_disable_interrupts(struct btintel_pcie_data *data) +{ + spin_lock(&data->irq_lock); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, data->fh_init_mask); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, data->hw_init_mask); + spin_unlock(&data->irq_lock); +} + +static void btintel_pcie_enable_interrupts(struct btintel_pcie_data *data) +{ + spin_lock(&data->irq_lock); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, ~data->fh_init_mask); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, ~data->hw_init_mask); + spin_unlock(&data->irq_lock); +} + +static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data) +{ + for (int i = 0; i < data->alloc_vecs; i++) + synchronize_irq(data->msix_entries[i].vector); +} + static int btintel_pcie_setup_internal(struct hci_dev *hdev) { struct btintel_pcie_data *data = hci_get_drvdata(hdev); @@ -2059,9 +2114,9 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev) } /* Apply the common HCI quirks for Intel device */ - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_DIAG); /* Set up the quality report callback for Intel devices */ hdev->set_quality_report = btintel_set_quality_report; @@ -2101,7 +2156,7 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev) * * All TLV based devices support WBS */ - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, @@ -2152,6 +2207,8 @@ static int btintel_pcie_setup(struct hci_dev *hdev) bt_dev_err(hdev, "Firmware download retry count: %d", fw_dl_retry); btintel_pcie_dump_debug_registers(hdev); + btintel_pcie_disable_interrupts(data); + btintel_pcie_synchronize_irqs(data); err = btintel_pcie_reset_bt(data); if (err) { bt_dev_err(hdev, "Failed to do shr reset: %d", err); @@ -2159,6 +2216,7 @@ static int btintel_pcie_setup(struct hci_dev *hdev) } usleep_range(10000, 12000); btintel_pcie_reset_ia(data); + btintel_pcie_enable_interrupts(data); btintel_pcie_config_msix(data); err = btintel_pcie_enable_bt(data); if (err) { @@ -2167,9 +2225,191 @@ static int btintel_pcie_setup(struct hci_dev *hdev) } btintel_pcie_start_rx(data); } + + if (!err) + set_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags); return err; } +static struct btintel_pcie_dev_recovery * +btintel_pcie_get_recovery(struct pci_dev *pdev, struct device *dev) +{ + struct btintel_pcie_dev_recovery *tmp, *data = NULL; + const char *name = pci_name(pdev); + struct hci_dev *hdev = to_hci_dev(dev); + + spin_lock(&btintel_pcie_recovery_lock); + list_for_each_entry(tmp, &btintel_pcie_recovery_list, list) { + if (strcmp(tmp->name, name)) + continue; + data = tmp; + break; + } + spin_unlock(&btintel_pcie_recovery_lock); + + if (data) { + bt_dev_dbg(hdev, "Found restart data for BDF: %s", data->name); + return data; + } + + data = kzalloc(struct_size(data, name, strlen(name) + 1), GFP_ATOMIC); + if (!data) + return NULL; + + strscpy_pad(data->name, name, strlen(name) + 1); + spin_lock(&btintel_pcie_recovery_lock); + list_add_tail(&data->list, &btintel_pcie_recovery_list); + spin_unlock(&btintel_pcie_recovery_lock); + + return data; +} + +static void btintel_pcie_free_restart_list(void) +{ + struct btintel_pcie_dev_recovery *tmp; + + while ((tmp = list_first_entry_or_null(&btintel_pcie_recovery_list, + typeof(*tmp), list))) { + list_del(&tmp->list); + kfree(tmp); + } +} + +static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, + struct device *dev) +{ + struct btintel_pcie_dev_recovery *data; + time64_t retry_window; + + data = btintel_pcie_get_recovery(pdev, dev); + if (!data) + return; + + retry_window = ktime_get_boottime_seconds() - data->last_error; + if (data->count == 0) { + data->last_error = ktime_get_boottime_seconds(); + data->count++; + } else if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && + data->count <= BTINTEL_PCIE_FLR_MAX_RETRY) { + data->count++; + } else if (retry_window > BTINTEL_PCIE_RESET_WINDOW_SECS) { + data->last_error = 0; + data->count = 0; + } +} + +static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); + +static void btintel_pcie_removal_work(struct work_struct *wk) +{ + struct btintel_pcie_removal *removal = + container_of(wk, struct btintel_pcie_removal, work); + struct pci_dev *pdev = removal->pdev; + struct btintel_pcie_data *data; + int err; + + pci_lock_rescan_remove(); + + if (!pdev->bus) + goto error; + + data = pci_get_drvdata(pdev); + + btintel_pcie_disable_interrupts(data); + btintel_pcie_synchronize_irqs(data); + + flush_work(&data->rx_work); + flush_work(&data->hdev->dump.dump_rx); + + bt_dev_dbg(data->hdev, "Release bluetooth interface"); + btintel_pcie_release_hdev(data); + + err = pci_reset_function(pdev); + if (err) { + BT_ERR("Failed resetting the pcie device (%d)", err); + goto error; + } + + btintel_pcie_enable_interrupts(data); + btintel_pcie_config_msix(data); + + err = btintel_pcie_enable_bt(data); + if (err) { + BT_ERR("Failed to enable bluetooth hardware after reset (%d)", + err); + goto error; + } + + btintel_pcie_reset_ia(data); + btintel_pcie_start_rx(data); + data->flags = 0; + + err = btintel_pcie_setup_hdev(data); + if (err) { + BT_ERR("Failed registering hdev (%d)", err); + goto error; + } +error: + pci_dev_put(pdev); + pci_unlock_rescan_remove(); + kfree(removal); +} + +static void btintel_pcie_reset(struct hci_dev *hdev) +{ + struct btintel_pcie_removal *removal; + struct btintel_pcie_data *data; + + data = hci_get_drvdata(hdev); + + if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) + return; + + if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return; + + removal = kzalloc(sizeof(*removal), GFP_ATOMIC); + if (!removal) + return; + + removal->pdev = data->pdev; + INIT_WORK(&removal->work, btintel_pcie_removal_work); + pci_dev_get(removal->pdev); + schedule_work(&removal->work); +} + +static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) +{ + struct btintel_pcie_dev_recovery *data; + struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev); + struct pci_dev *pdev = dev_data->pdev; + time64_t retry_window; + + if (code == 0x13) { + bt_dev_err(hdev, "Encountered top exception"); + return; + } + + data = btintel_pcie_get_recovery(pdev, &hdev->dev); + if (!data) + return; + + retry_window = ktime_get_boottime_seconds() - data->last_error; + + if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && + data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { + bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d", + BTINTEL_PCIE_FLR_MAX_RETRY, data->count); + bt_dev_dbg(hdev, "Boot time: %lld seconds", + ktime_get_boottime_seconds()); + bt_dev_dbg(hdev, "last error at: %lld seconds", + data->last_error); + return; + } + btintel_pcie_inc_recovery_count(pdev, &hdev->dev); + btintel_pcie_reset(hdev); +} + static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) { int err; @@ -2191,9 +2431,10 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) hdev->send = btintel_pcie_send_frame; hdev->setup = btintel_pcie_setup; hdev->shutdown = btintel_shutdown_combined; - hdev->hw_error = btintel_hw_error; + hdev->hw_error = btintel_pcie_hw_error; hdev->set_diag = btintel_set_diag; hdev->set_bdaddr = btintel_set_bdaddr; + hdev->reset = btintel_pcie_reset; err = hci_register_dev(hdev); if (err < 0) { @@ -2291,6 +2532,12 @@ static void btintel_pcie_remove(struct pci_dev *pdev) data = pci_get_drvdata(pdev); + btintel_pcie_disable_interrupts(data); + + btintel_pcie_synchronize_irqs(data); + + flush_work(&data->rx_work); + btintel_pcie_reset_bt(data); for (int i = 0; i < data->alloc_vecs; i++) { struct msix_entry *msix_entry; @@ -2303,8 +2550,6 @@ static void btintel_pcie_remove(struct pci_dev *pdev) btintel_pcie_release_hdev(data); - flush_work(&data->rx_work); - destroy_workqueue(data->workqueue); btintel_pcie_free(data); @@ -2337,7 +2582,20 @@ static struct pci_driver btintel_pcie_driver = { .driver.coredump = btintel_pcie_coredump #endif }; -module_pci_driver(btintel_pcie_driver); + +static int __init btintel_pcie_init(void) +{ + return pci_register_driver(&btintel_pcie_driver); +} + +static void __exit btintel_pcie_exit(void) +{ + pci_unregister_driver(&btintel_pcie_driver); + btintel_pcie_free_restart_list(); +} + +module_init(btintel_pcie_init); +module_exit(btintel_pcie_exit); MODULE_AUTHOR("Tedd Ho-Jeong An "); MODULE_DESCRIPTION("Intel Bluetooth PCIe transport driver ver " VERSION); diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 7dad4523236c..0fa876c5b954 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -117,7 +117,9 @@ enum { enum { BTINTEL_PCIE_CORE_HALTED, BTINTEL_PCIE_HWEXP_INPROGRESS, - BTINTEL_PCIE_COREDUMP_INPROGRESS + BTINTEL_PCIE_COREDUMP_INPROGRESS, + BTINTEL_PCIE_RECOVERY_IN_PROGRESS, + BTINTEL_PCIE_SETUP_DONE }; enum btintel_pcie_tlv_type { diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 4390fd571dbd..a8c520dc09e1 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -642,12 +642,7 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, * WMT command. */ err = wait_on_bit_timeout(&data->flags, BTMTK_TX_WAIT_VND_EVT, - TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); - if (err == -EINTR) { - bt_dev_err(hdev, "Execution of wmt command interrupted"); - clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags); - goto err_free_wc; - } + TASK_UNINTERRUPTIBLE, HCI_INIT_TIMEOUT); if (err) { bt_dev_err(hdev, "Execution of wmt command timed out"); diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index c16a3518b8ff..4fc673640bfc 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -1141,7 +1141,7 @@ static int btmtksdio_setup(struct hci_dev *hdev) } /* Enable WBS with mSBC codec */ - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* Enable GPIO reset mechanism */ if (bdev->reset) { @@ -1384,7 +1384,7 @@ static int btmtksdio_probe(struct sdio_func *func, SET_HCIDEV_DEV(hdev, &func->dev); hdev->manufacturer = 70; - set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); sdio_set_drvdata(func, bdev); diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index c97e260fcb0c..76995cfcd534 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -316,7 +316,7 @@ mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count, /* Resync STP when unexpected data is being read */ if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) { - bt_dev_err(bdev->hdev, "stp format unexpect (%d, %d)", + bt_dev_err(bdev->hdev, "stp format unexpected (%d, %d)", shdr->prefix, bdev->stp_dlen); bdev->stp_cursor = 2; bdev->stp_dlen = 0; @@ -872,7 +872,7 @@ static int btmtkuart_probe(struct serdev_device *serdev) SET_HCIDEV_DEV(hdev, &serdev->dev); hdev->manufacturer = 70; - set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); if (btmtkuart_is_standalone(bdev)) { err = clk_prepare_enable(bdev->osc); diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 1088db6056a4..76e7f857fb7d 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -73,7 +75,8 @@ #define FW_AUTH_ENC 0xc0 #define HCI_NXP_PRI_BAUDRATE 115200 -#define HCI_NXP_SEC_BAUDRATE 3000000 +#define HCI_NXP_SEC_BAUDRATE_3M 3000000 +#define HCI_NXP_SEC_BAUDRATE_4M 4000000 #define MAX_FW_FILE_NAME_LEN 50 @@ -201,12 +204,14 @@ struct btnxpuart_dev { u32 new_baudrate; u32 current_baudrate; u32 fw_init_baudrate; + u32 secondary_baudrate; enum bootloader_param_change timeout_changed; enum bootloader_param_change baudrate_changed; bool helper_downloaded; struct ps_data psdata; struct btnxpuart_data *nxp_data; + struct reset_control *pdn; }; #define NXP_V1_FW_REQ_PKT 0xa5 @@ -365,17 +370,26 @@ static u8 crc8_table[CRC8_TABLE_SIZE]; static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, - void *param) + void *param, + bool resp) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct ps_data *psdata = &nxpdev->psdata; - struct sk_buff *skb; + struct sk_buff *skb = NULL; /* set flag to prevent nxp_enqueue from parsing values from this command and * calling hci_cmd_sync_queue() again. */ psdata->driver_sent_cmd = true; - skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + if (resp) { + skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + } else { + __hci_cmd_send(hdev, opcode, plen, param); + /* Allow command to be sent before tx_work is cancelled + * by btnxpuart_flush() + */ + msleep(20); + } psdata->driver_sent_cmd = false; return skb; @@ -529,10 +543,10 @@ static int ps_setup(struct hci_dev *hdev) } if (psdata->wakeup_source) { - ret = devm_request_irq(&serdev->dev, psdata->irq_handler, - ps_host_wakeup_irq_handler, - IRQF_ONESHOT | IRQF_TRIGGER_FALLING, - dev_name(&serdev->dev), nxpdev); + ret = devm_request_threaded_irq(&serdev->dev, psdata->irq_handler, + NULL, ps_host_wakeup_irq_handler, + IRQF_ONESHOT, + dev_name(&serdev->dev), nxpdev); if (ret) bt_dev_info(hdev, "error setting wakeup IRQ handler, ignoring\n"); disable_irq(psdata->irq_handler); @@ -595,7 +609,8 @@ static int send_ps_cmd(struct hci_dev *hdev, void *data) pcmd.ps_cmd = BT_PS_DISABLE; pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval); - skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), + &pcmd, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -644,7 +659,8 @@ static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data) break; } - skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), + &pcmd, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -802,7 +818,10 @@ static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len) nxpdev->fw_v3_offset_correction += req_len; } else if (req_len == sizeof(uart_config)) { uart_config.clkdiv.address = __cpu_to_le32(clkdivaddr); - uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); + if (nxpdev->new_baudrate == HCI_NXP_SEC_BAUDRATE_4M) + uart_config.clkdiv.value = __cpu_to_le32(0x01000000); + else + uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); uart_config.uartdiv.address = __cpu_to_le32(uartdivaddr); uart_config.uartdiv.value = __cpu_to_le32(1); uart_config.mcr.address = __cpu_to_le32(uartmcraddr); @@ -966,12 +985,13 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } if (nxpdev->baudrate_changed != changed) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; if (nxp_fw_change_baudrate(hdev, len)) { nxpdev->baudrate_changed = changed; serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + nxpdev->secondary_baudrate); serdev_device_set_flow_control(nxpdev->serdev, true); - nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + nxpdev->current_baudrate = nxpdev->secondary_baudrate; } goto free_skb; } @@ -992,7 +1012,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) nxpdev->helper_downloaded = true; serdev_device_wait_until_sent(nxpdev->serdev, 0); serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + HCI_NXP_SEC_BAUDRATE_3M); serdev_device_set_flow_control(nxpdev->serdev, true); } else { clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); @@ -1216,12 +1236,13 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } if (nxpdev->baudrate_changed != changed) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; if (nxp_fw_change_baudrate(hdev, len)) { nxpdev->baudrate_changed = cmd_sent; serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + nxpdev->secondary_baudrate); serdev_device_set_flow_control(nxpdev->serdev, true); - nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + nxpdev->current_baudrate = nxpdev->secondary_baudrate; } goto free_skb; } @@ -1265,7 +1286,8 @@ static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data) if (!psdata) return 0; - skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, (u8 *)&new_baudrate); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, + (u8 *)&new_baudrate, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -1323,7 +1345,7 @@ static void nxp_coredump(struct hci_dev *hdev) struct sk_buff *skb; u8 pcmd = 2; - skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd, true); if (IS_ERR(skb)) bt_dev_err(hdev, "Failed to trigger FW Dump. (%ld)", PTR_ERR(skb)); else @@ -1365,7 +1387,6 @@ static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb) if (buf_len == 0) { bt_dev_warn(hdev, "==== FW dump complete ==="); - clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state); hci_devcd_complete(hdev); nxp_set_ind_reset(hdev, NULL); } @@ -1419,6 +1440,10 @@ static int nxp_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) static int nxp_setup(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct serdev_device *serdev = nxpdev->serdev; + char device_string[30]; + char event_string[50]; + char *envp[] = {device_string, event_string, NULL}; int err = 0; if (nxp_check_boot_sign(nxpdev)) { @@ -1431,6 +1456,12 @@ static int nxp_setup(struct hci_dev *hdev) clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); } + snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev)); + snprintf(event_string, 50, "BTNXPUART_STATE=FW_READY"); + bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string, + event_string); + kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp); + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); nxpdev->current_baudrate = nxpdev->fw_init_baudrate; @@ -1447,8 +1478,8 @@ static int nxp_post_init(struct hci_dev *hdev) struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct ps_data *psdata = &nxpdev->psdata; - if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { - nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + if (nxpdev->current_baudrate != nxpdev->secondary_baudrate) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; nxp_set_baudrate_cmd(hdev, NULL); } if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode) @@ -1479,7 +1510,13 @@ static int nxp_shutdown(struct hci_dev *hdev) u8 pcmd = 0; if (ind_reset_in_progress(nxpdev)) { - skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); + if (test_and_clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, + &nxpdev->tx_state)) + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, + &pcmd, false); + else + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, + &pcmd, true); serdev_device_set_flow_control(nxpdev->serdev, false); set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); /* HCI_NXP_IND_RESET command may not returns any response */ @@ -1745,11 +1782,41 @@ static const struct serdev_device_ops btnxpuart_client_ops = { .write_wakeup = btnxpuart_write_wakeup, }; +static void nxp_coredump_notify(struct hci_dev *hdev, int state) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct serdev_device *serdev = nxpdev->serdev; + char device_string[30]; + char event_string[50]; + char *envp[] = {device_string, event_string, NULL}; + + snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev)); + switch (state) { + case HCI_DEVCOREDUMP_ACTIVE: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_ACTIVE"); + break; + case HCI_DEVCOREDUMP_DONE: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_DONE"); + break; + case HCI_DEVCOREDUMP_TIMEOUT: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_TIMEOUT"); + break; + default: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_STATE_%d", + state); + break; + } + bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string, + event_string); + kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp); +} + static int nxp_serdev_probe(struct serdev_device *serdev) { struct hci_dev *hdev; struct btnxpuart_dev *nxpdev; bdaddr_t ba = {0}; + int err; nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL); if (!nxpdev) @@ -1773,10 +1840,31 @@ static int nxp_serdev_probe(struct serdev_device *serdev) if (!nxpdev->fw_init_baudrate) nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE; + device_property_read_u32(&nxpdev->serdev->dev, "max-speed", + &nxpdev->secondary_baudrate); + if (!nxpdev->secondary_baudrate || + (nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_3M && + nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_4M)) { + if (nxpdev->secondary_baudrate) + dev_err(&serdev->dev, + "Invalid max-speed. Using default 3000000."); + nxpdev->secondary_baudrate = HCI_NXP_SEC_BAUDRATE_3M; + } + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); crc8_populate_msb(crc8_table, POLYNOMIAL8); + nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL); + if (IS_ERR(nxpdev->pdn)) + return PTR_ERR(nxpdev->pdn); + + err = devm_regulator_get_enable(&serdev->dev, "vcc"); + if (err) { + dev_err(&serdev->dev, "Failed to enable vcc regulator\n"); + return err; + } + /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { @@ -1784,6 +1872,8 @@ static int nxp_serdev_probe(struct serdev_device *serdev) return -ENOMEM; } + reset_control_deassert(nxpdev->pdn); + nxpdev->hdev = hdev; hdev->bus = HCI_UART; @@ -1807,7 +1897,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev) "local-bd-address", (u8 *)&ba, sizeof(ba)); if (bacmp(&ba, BDADDR_ANY)) - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); if (hci_register_dev(hdev) < 0) { dev_err(&serdev->dev, "Can't register HCI device\n"); @@ -1817,11 +1907,13 @@ static int nxp_serdev_probe(struct serdev_device *serdev) if (ps_setup(hdev)) goto probe_fail; - hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, NULL); + hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, + nxp_coredump_notify); return 0; probe_fail: + reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); return -ENODEV; } @@ -1849,6 +1941,7 @@ static void nxp_serdev_remove(struct serdev_device *serdev) ps_cleanup(nxpdev); hci_unregister_dev(hdev); + reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); } diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index edefb9dc76aa..7c958d6065be 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -739,7 +739,7 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co bda = (struct hci_rp_read_bd_addr *)skb->data; if (!bacmp(&bda->bdaddr, &config->bdaddr)) - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); kfree_skb(skb); diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c index c0eb71d6ffd3..d2e13fcb6bab 100644 --- a/drivers/bluetooth/btqcomsmd.c +++ b/drivers/bluetooth/btqcomsmd.c @@ -117,7 +117,7 @@ static int btqcomsmd_setup(struct hci_dev *hdev) /* Devices do not have persistent storage for BD address. Retrieve * it from the firmware node property. */ - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); return 0; } diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 7838c89e529e..6abd962502e3 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -693,7 +693,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, /* Loop from the end of the firmware parsing instructions, until * we find an instruction that identifies the "project ID" for the - * hardware supported by this firwmare file. + * hardware supported by this firmware file. * Once we have that, we double-check that project_id is suitable * for the hardware we are working with. */ @@ -1287,7 +1287,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) /* Enable controller to do both LE scan and BR/EDR inquiry * simultaneously. */ - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); /* Enable central-peripheral role (able to create new connections with * an existing connection in slave role). @@ -1301,7 +1301,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8851B: case CHIP_ID_8922A: case CHIP_ID_8852BT: - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* RTL8852C needs to transmit mSBC data continuously without * the zero length of USB packets for the ALT 6 supported chips @@ -1312,7 +1312,8 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) if (btrtl_dev->project_id == CHIP_ID_8852A || btrtl_dev->project_id == CHIP_ID_8852B || btrtl_dev->project_id == CHIP_ID_8852C) - set_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks); + hci_set_quirk(hdev, + HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER); hci_set_aosp_capable(hdev); break; @@ -1331,8 +1332,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) * but it doesn't support any features from page 2 - * it either responds with garbage or with error status */ - set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, - &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2); break; default: break; diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index a69feb08486a..8325655ce6aa 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -327,7 +327,7 @@ static int btsdio_probe(struct sdio_func *func, hdev->send = btsdio_send_frame; if (func->vendor == 0x0104 && func->device == 0x00c5) - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); err = hci_register_dev(hdev); if (err < 0) { diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9ab661d2d1e6..8085fabadde8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -298,44 +298,6 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, /* QCA WCN6855 chipset */ - { USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x0489, 0xe0cc), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x0489, 0xe0d6), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x0489, 0xe0e3), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9309), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9409), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9108), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9109), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9208), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9209), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9308), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9408), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9508), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9509), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9608), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9609), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x10ab, 0x9f09), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, - { USB_DEVICE(0x04ca, 0x3022), .driver_info = BTUSB_QCA_WCN6855 | - BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0c9), .driver_info = BTUSB_QCA_WCN6855 | @@ -344,18 +306,28 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0cb), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe0cc), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0ce), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe0d6), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0de), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0df), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0e1), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe0e3), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0ea), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe0ec), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x04ca, 0x3022), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x3023), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x3024), .driver_info = BTUSB_QCA_WCN6855 | @@ -368,6 +340,36 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x3a27), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9108), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9109), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9208), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9209), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9308), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9309), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9408), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9409), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9508), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9509), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9608), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9609), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x10ab, 0x9f09), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x28de, 0x1401), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH }, /* QCA WCN785x chipset */ { USB_DEVICE(0x0cf3, 0xe700), .driver_info = BTUSB_QCA_WCN6855 | @@ -515,6 +517,11 @@ static const struct usb_device_id quirks_table[] = { /* Realtek 8851BE Bluetooth devices */ { USB_DEVICE(0x0bda, 0xb850), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3600), .driver_info = BTUSB_REALTEK }, + { USB_DEVICE(0x13d3, 0x3601), .driver_info = BTUSB_REALTEK }, + + /* Realtek 8851BU Bluetooth devices */ + { USB_DEVICE(0x3625, 0x010b), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8852AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | @@ -565,6 +572,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3591), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe123), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe125), .driver_info = BTUSB_REALTEK | @@ -705,6 +714,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe139), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe14e), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe14f), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe150), .driver_info = BTUSB_MEDIATEK | @@ -725,6 +736,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3630), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2c7c, 0x7009), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8723AE Bluetooth devices */ { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK }, @@ -2472,18 +2485,18 @@ static int btusb_setup_csr(struct hci_dev *hdev) * Probably will need to be expanded in the future; * without these the controller will lock up. */ - set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks); - set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_READ_VOICE_SETTING, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_READ_PAGE_SCAN_TYPE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ERR_DATA_REPORTING); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL); + hci_set_quirk(hdev, HCI_QUIRK_NO_SUSPEND_NOTIFIER); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_VOICE_SETTING); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_PAGE_SCAN_TYPE); /* Clear the reset quirk since this is not an actual * early Bluetooth 1.1 device from CSR. */ - clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); - clear_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_clear_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); + hci_clear_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); /* * Special workaround for these BT 4.0 chip clones, and potentially more: @@ -2594,12 +2607,12 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) else urb = alloc_ctrl_urb(hdev, skb); - /* When the 0xfc01 command is issued to boot into - * the operational firmware, it will actually not - * send a command complete event. To keep the flow + /* When the BTINTEL_HCI_OP_RESET command is issued to + * boot into the operational firmware, it will actually + * not send a command complete event. To keep the flow * control working inject that event here. */ - if (opcode == 0xfc01) + if (opcode == BTINTEL_HCI_OP_RESET) inject_cmd_complete(hdev, opcode); } else { urb = alloc_ctrl_urb(hdev, skb); @@ -3179,6 +3192,12 @@ struct qca_device_info { u8 ver_offset; /* offset of version structure in rampatch */ }; +struct qca_custom_firmware { + u32 rom_version; + u16 board_id; + const char *subdirectory; +}; + static const struct qca_device_info qca_devices_table[] = { { 0x00000100, 20, 4, 8 }, /* Rome 1.0 */ { 0x00000101, 20, 4, 8 }, /* Rome 1.1 */ @@ -3192,6 +3211,57 @@ static const struct qca_device_info qca_devices_table[] = { { 0x00190200, 40, 4, 16 }, /* WCN785x 2.0 */ }; +static const struct qca_custom_firmware qca_custom_btfws[] = { + { 0x00130201, 0x030A, "QCA2066" }, + { }, +}; + +static u16 qca_extract_board_id(const struct qca_version *ver) +{ + u16 flag = le16_to_cpu(ver->flag); + u16 board_id = 0; + + if (((flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) { + /* The board_id should be split into two bytes + * The 1st byte is chip ID, and the 2nd byte is platform ID + * For example, board ID 0x010A, 0x01 is platform ID. 0x0A is chip ID + * we have several platforms, and platform IDs are continuously added + * Platform ID: + * 0x00 is for Mobile + * 0x01 is for X86 + * 0x02 is for Automotive + * 0x03 is for Consumer electronic + */ + board_id = (ver->chip_id << 8) + ver->platform_id; + } + + /* Take 0xffff as invalid board ID */ + if (board_id == 0xffff) + board_id = 0; + + return board_id; +} + +static const char *qca_get_fw_subdirectory(const struct qca_version *ver) +{ + const struct qca_custom_firmware *ptr; + u32 rom_ver; + u16 board_id; + + rom_ver = le32_to_cpu(ver->rom_version); + board_id = qca_extract_board_id(ver); + if (!board_id) + return NULL; + + for (ptr = qca_custom_btfws; ptr->rom_version; ptr++) { + if (ptr->rom_version == rom_ver && + ptr->board_id == board_id) + return ptr->subdirectory; + } + + return NULL; +} + static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request, void *data, u16 size) { @@ -3296,15 +3366,22 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev, { struct qca_rampatch_version *rver; const struct firmware *fw; + const char *fw_subdir; u32 ver_rom, ver_patch, rver_rom; u16 rver_rom_low, rver_rom_high, rver_patch; - char fwname[64]; + char fwname[80]; int err; ver_rom = le32_to_cpu(ver->rom_version); ver_patch = le32_to_cpu(ver->patch_version); - snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin", ver_rom); + fw_subdir = qca_get_fw_subdirectory(ver); + if (fw_subdir) + snprintf(fwname, sizeof(fwname), "qca/%s/rampatch_usb_%08x.bin", + fw_subdir, ver_rom); + else + snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin", + ver_rom); err = request_firmware(&fw, fwname, &hdev->dev); if (err) { @@ -3348,44 +3425,34 @@ static void btusb_generate_qca_nvm_name(char *fwname, size_t max_size, const struct qca_version *ver) { u32 rom_version = le32_to_cpu(ver->rom_version); - u16 flag = le16_to_cpu(ver->flag); + const char *variant, *fw_subdir; + int len; + u16 board_id; - if (((flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) { - /* The board_id should be split into two bytes - * The 1st byte is chip ID, and the 2nd byte is platform ID - * For example, board ID 0x010A, 0x01 is platform ID. 0x0A is chip ID - * we have several platforms, and platform IDs are continuously added - * Platform ID: - * 0x00 is for Mobile - * 0x01 is for X86 - * 0x02 is for Automotive - * 0x03 is for Consumer electronic - */ - u16 board_id = (ver->chip_id << 8) + ver->platform_id; - const char *variant; + fw_subdir = qca_get_fw_subdirectory(ver); + board_id = qca_extract_board_id(ver); - switch (le32_to_cpu(ver->ram_version)) { - case WCN6855_2_0_RAM_VERSION_GF: - case WCN6855_2_1_RAM_VERSION_GF: - variant = "_gf"; - break; - default: - variant = ""; - break; - } - - if (board_id == 0) { - snprintf(fwname, max_size, "qca/nvm_usb_%08x%s.bin", - rom_version, variant); - } else { - snprintf(fwname, max_size, "qca/nvm_usb_%08x%s_%04x.bin", - rom_version, variant, board_id); - } - } else { - snprintf(fwname, max_size, "qca/nvm_usb_%08x.bin", - rom_version); + switch (le32_to_cpu(ver->ram_version)) { + case WCN6855_2_0_RAM_VERSION_GF: + case WCN6855_2_1_RAM_VERSION_GF: + variant = "_gf"; + break; + default: + variant = NULL; + break; } + if (fw_subdir) + len = snprintf(fwname, max_size, "qca/%s/nvm_usb_%08x", + fw_subdir, rom_version); + else + len = snprintf(fwname, max_size, "qca/nvm_usb_%08x", + rom_version); + if (variant) + len += snprintf(fwname + len, max_size - len, "%s", variant); + if (board_id) + len += snprintf(fwname + len, max_size - len, "_%04x", board_id); + len += snprintf(fwname + len, max_size - len, ".bin"); } static int btusb_setup_qca_load_nvm(struct hci_dev *hdev, @@ -3393,7 +3460,7 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev, const struct qca_device_info *info) { const struct firmware *fw; - char fwname[64]; + char fwname[80]; int err; btusb_generate_qca_nvm_name(fwname, sizeof(fwname), ver); @@ -3494,7 +3561,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) /* Mark HCI_OP_ENHANCED_SETUP_SYNC_CONN as broken as it doesn't seem to * work with the likes of HSP/HFP mSBC. */ - set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN); return 0; } @@ -3792,6 +3859,8 @@ static int btusb_hci_drv_supported_altsettings(struct hci_dev *hdev, void *data, /* There are at most 7 alt (0 - 6) */ rp = kmalloc(sizeof(*rp) + 7, GFP_KERNEL); + if (!rp) + return -ENOMEM; rp->num = 0; if (!drvdata->isoc) @@ -4008,10 +4077,10 @@ static int btusb_probe(struct usb_interface *intf, } #endif if (id->driver_info & BTUSB_CW6622) - set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); if (id->driver_info & BTUSB_BCM2045) - set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); if (id->driver_info & BTUSB_BCM92035) hdev->setup = btusb_setup_bcm92035; @@ -4068,8 +4137,8 @@ static int btusb_probe(struct usb_interface *intf, hdev->reset = btmtk_reset_sync; hdev->set_bdaddr = btmtk_set_bdaddr; hdev->send = btusb_send_frame_mtk; - set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks); - set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); data->recv_acl = btmtk_usb_recv_acl; data->suspend = btmtk_usb_suspend; data->resume = btmtk_usb_resume; @@ -4077,20 +4146,20 @@ static int btusb_probe(struct usb_interface *intf, } if (id->driver_info & BTUSB_SWAVE) { - set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_FIXUP_INQUIRY_MODE); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_COMMANDS); } if (id->driver_info & BTUSB_INTEL_BOOT) { hdev->manufacturer = 2; - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE); } if (id->driver_info & BTUSB_ATH3012) { data->setup_on_usb = btusb_setup_qca; hdev->set_bdaddr = btusb_set_bdaddr_ath3012; - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); } if (id->driver_info & BTUSB_QCA_ROME) { @@ -4098,7 +4167,7 @@ static int btusb_probe(struct usb_interface *intf, hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_ath3012; hdev->reset = btusb_qca_reset; - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); btusb_check_needs_reset_resume(intf); } @@ -4112,7 +4181,7 @@ static int btusb_probe(struct usb_interface *intf, hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_wcn6855; hdev->reset = btusb_qca_reset; - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); hci_set_msft_opcode(hdev, 0xFD70); } @@ -4140,35 +4209,35 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ACTIONS_SEMI) { /* Support is advertised, but not implemented */ - set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_EXT_CREATE_CONN, &hdev->quirks); - set_bit(HCI_QUIRK_BROKEN_WRITE_AUTH_PAYLOAD_TIMEOUT, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ERR_DATA_REPORTING); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_CREATE_CONN); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_WRITE_AUTH_PAYLOAD_TIMEOUT); } if (!reset) - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) { if (!disable_scofix) - set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_FIXUP_BUFFER_SIZE); } if (id->driver_info & BTUSB_BROKEN_ISOC) data->isoc = NULL; if (id->driver_info & BTUSB_WIDEBAND_SPEECH) - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); if (id->driver_info & BTUSB_INVALID_LE_STATES) - set_bit(HCI_QUIRK_BROKEN_LE_STATES, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES); if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); } if (id->driver_info & BTUSB_CSR) { @@ -4177,10 +4246,10 @@ static int btusb_probe(struct usb_interface *intf, /* Old firmware would otherwise execute USB reset */ if (bcdDevice < 0x117) - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); /* This must be set first in case we disable it for fakes */ - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); /* Fake CSR devices with broken commands */ if (le16_to_cpu(udev->descriptor.idVendor) == 0x0a12 && @@ -4193,7 +4262,7 @@ static int btusb_probe(struct usb_interface *intf, /* New sniffer firmware has crippled HCI interface */ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997) - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE); } if (id->driver_info & BTUSB_INTEL_BOOT) { diff --git a/drivers/bluetooth/hci_aml.c b/drivers/bluetooth/hci_aml.c index 1394c575aa6d..707e90f80130 100644 --- a/drivers/bluetooth/hci_aml.c +++ b/drivers/bluetooth/hci_aml.c @@ -424,7 +424,7 @@ static int aml_check_bdaddr(struct hci_dev *hdev) if (!bacmp(&paddr->bdaddr, AML_BDADDR_DEFAULT)) { bt_dev_info(hdev, "amlbt using default bdaddr (%pM)", &paddr->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } exit: diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 9684eb16059b..f96617b85d87 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -643,8 +643,8 @@ static int bcm_setup(struct hci_uart *hu) * Allow the bootloader to set a valid address through the * device tree. */ - if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks)) - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hu->hdev->quirks); + if (hci_test_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR)) + hci_set_quirk(hu->hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); if (!bcm_request_irq(bcm)) err = bcm_setup_sleep(hu); diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c index 9bce53e49cfa..45e6d84224ee 100644 --- a/drivers/bluetooth/hci_bcm4377.c +++ b/drivers/bluetooth/hci_bcm4377.c @@ -420,7 +420,7 @@ struct bcm4377_ring_state { * payloads_dma:DMA address for payload buffer * events: pointer to array of completions if waiting is allowed * msgids: bitmap to keep track of used message ids - * lock: Spinlock to protect access to ring structurs used in the irq handler + * lock: Spinlock to protect access to ring structures used in the irq handler */ struct bcm4377_transfer_ring { enum bcm4377_transfer_ring_id ring_id; @@ -1435,7 +1435,7 @@ static int bcm4377_check_bdaddr(struct bcm4377_data *bcm4377) bda = (struct hci_rp_read_bd_addr *)skb->data; if (!bcm4377_is_valid_bdaddr(bcm4377, &bda->bdaddr)) - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &bcm4377->hdev->quirks); + hci_set_quirk(bcm4377->hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); kfree_skb(skb); return 0; @@ -2389,13 +2389,13 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id) hdev->setup = bcm4377_hci_setup; if (bcm4377->hw->broken_mws_transport_config) - set_bit(HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG); if (bcm4377->hw->broken_ext_scan) - set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN); if (bcm4377->hw->broken_le_coded) - set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED); if (bcm4377->hw->broken_le_ext_adv_report_phy) - set_bit(HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY); pci_set_drvdata(pdev, bcm4377); hci_set_drvdata(hdev, bcm4377); diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 811f33701f84..9b353c3d6442 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -660,7 +660,7 @@ static int intel_setup(struct hci_uart *hu) */ if (!bacmp(¶ms.otp_bdaddr, BDADDR_ANY)) { bt_dev_info(hdev, "No device address configured"); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR); } /* With this Intel bootloader only the hardware variant and device @@ -1029,12 +1029,12 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu) struct hci_command_hdr *cmd = (void *)skb->data; __u16 opcode = le16_to_cpu(cmd->opcode); - /* When the 0xfc01 command is issued to boot into - * the operational firmware, it will actually not - * send a command complete event. To keep the flow - * control working inject that event here. + /* When the BTINTEL_HCI_OP_RESET command is issued to boot into + * the operational firmware, it will actually not send a command + * complete event. To keep the flow control working inject that + * event here. */ - if (opcode == 0xfc01) + if (opcode == BTINTEL_HCI_OP_RESET) inject_cmd_complete(hu->hdev, opcode); } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index acba83156de9..d0adae3267b4 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -667,13 +667,13 @@ static int hci_uart_register_dev(struct hci_uart *hu) SET_HCIDEV_DEV(hdev, hu->tty->dev); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE); if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) - set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG); if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); /* Only call open() for the protocol after hdev is fully initialized as * open() (or a timer/workqueue it starts) may attempt to reference it. diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index e19e9bd49555..7044c86325ce 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -649,11 +649,11 @@ static int ll_setup(struct hci_uart *hu) /* This means that there was an error getting the BD address * during probe, so mark the device as having a bad address. */ - set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks); + hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR); } else if (bacmp(&lldev->bdaddr, BDADDR_ANY)) { err = ll_set_bdaddr(hu->hdev, &lldev->bdaddr); if (err) - set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks); + hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR); } /* Operational speed if any */ diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c index 9fc10a16fd96..cd7575c20f65 100644 --- a/drivers/bluetooth/hci_nokia.c +++ b/drivers/bluetooth/hci_nokia.c @@ -439,7 +439,7 @@ static int nokia_setup(struct hci_uart *hu) if (btdev->man_id == NOKIA_ID_BCM2048) { hu->hdev->set_bdaddr = btbcm_set_bdaddr; - set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks); + hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR); dev_dbg(dev, "bcm2048 has invalid bluetooth address!"); } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 5fe5879881f5..4cff4d9be313 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1264,6 +1264,7 @@ static const struct h4_recv_pkt qca_recv_pkts[] = { { H4_RECV_ACL, .recv = qca_recv_acl_data }, { H4_RECV_SCO, .recv = hci_recv_frame }, { H4_RECV_EVENT, .recv = qca_recv_event }, + { H4_RECV_ISO, .recv = hci_recv_frame }, { QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind }, { QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack }, { QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind }, @@ -1892,7 +1893,7 @@ static int qca_setup(struct hci_uart *hu) /* Enable controller to do both LE scan and BR/EDR inquiry * simultaneously. */ - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); switch (soc_type) { case QCA_QCA2066: @@ -1944,7 +1945,7 @@ static int qca_setup(struct hci_uart *hu) case QCA_WCN7850: qcadev = serdev_device_get_drvdata(hu->serdev); if (qcadev->bdaddr_property_broken) - set_bit(HCI_QUIRK_BDADDR_PROPERTY_BROKEN, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN); hci_set_aosp_capable(hdev); @@ -2392,10 +2393,17 @@ static int qca_serdev_probe(struct serdev_device *serdev) */ qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->dev, "bluetooth"); - if (IS_ERR(qcadev->bt_power->pwrseq)) - return PTR_ERR(qcadev->bt_power->pwrseq); - break; + /* + * Some modules have BT_EN enabled via a hardware pull-up, + * meaning it is not defined in the DTS and is not controlled + * through the power sequence. In such cases, fall through + * to follow the legacy flow. + */ + if (IS_ERR(qcadev->bt_power->pwrseq)) + qcadev->bt_power->pwrseq = NULL; + else + break; } fallthrough; case QCA_WCN3950: @@ -2480,7 +2488,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) hdev = qcadev->serdev_hu.hdev; if (power_ctrl_enabled) { - set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); hdev->shutdown = qca_power_off; } @@ -2489,11 +2497,11 @@ static int qca_serdev_probe(struct serdev_device *serdev) * be queried via hci. Same with the valid le states quirk. */ if (data->capabilities & QCA_CAP_WIDEBAND_SPEECH) - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, - &hdev->quirks); + hci_set_quirk(hdev, + HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); if (!(data->capabilities & QCA_CAP_VALID_LE_STATES)) - set_bit(HCI_QUIRK_BROKEN_LE_STATES, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES); } return 0; @@ -2543,7 +2551,7 @@ static void qca_serdev_shutdown(struct device *dev) * invoked and the SOC is already in the initial state, so * don't also need to send the VSC. */ - if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks) || + if (hci_test_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP) || hci_dev_test_flag(hdev, HCI_SETUP)) return; diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 89a22e9b3253..593d9cefbbf9 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -152,7 +152,7 @@ static int hci_uart_close(struct hci_dev *hdev) * BT SOC is completely powered OFF during BT OFF, holding port * open may drain the battery. */ - if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) { + if (hci_test_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP)) { clear_bit(HCI_UART_PROTO_READY, &hu->flags); serdev_device_close(hu->serdev); } @@ -358,13 +358,13 @@ int hci_uart_register_device_priv(struct hci_uart *hu, SET_HCIDEV_DEV(hdev, &hu->serdev->dev); if (test_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &hu->flags)) - set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_NO_SUSPEND_NOTIFIER); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE); if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) - set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG); if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return 0; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 59f4d7bdffdc..2fef08254d78 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -380,6 +380,28 @@ static const struct file_operations force_devcoredump_fops = { .write = force_devcd_write, }; +static void vhci_debugfs_init(struct vhci_data *data) +{ + struct hci_dev *hdev = data->hdev; + + debugfs_create_file("force_suspend", 0644, hdev->debugfs, data, + &force_suspend_fops); + + debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data, + &force_wakeup_fops); + + if (IS_ENABLED(CONFIG_BT_MSFTEXT)) + debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data, + &msft_opcode_fops); + + if (IS_ENABLED(CONFIG_BT_AOSPEXT)) + debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, + &aosp_capable_fops); + + debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data, + &force_devcoredump_fops); +} + static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; @@ -415,16 +437,16 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) hdev->get_codec_config_data = vhci_get_codec_config_data; hdev->wakeup = vhci_wakeup; hdev->setup = vhci_setup; - set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); - set_bit(HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); + hci_set_quirk(hdev, HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED); /* bit 6 is for external configuration */ if (opcode & 0x40) - set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG); /* bit 7 is for raw device */ if (opcode & 0x80) - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE); if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); @@ -434,22 +456,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) return -EBUSY; } - debugfs_create_file("force_suspend", 0644, hdev->debugfs, data, - &force_suspend_fops); - - debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data, - &force_wakeup_fops); - - if (IS_ENABLED(CONFIG_BT_MSFTEXT)) - debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data, - &msft_opcode_fops); - - if (IS_ENABLED(CONFIG_BT_AOSPEXT)) - debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, - &aosp_capable_fops); - - debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data, - &force_devcoredump_fops); + if (!IS_ERR_OR_NULL(hdev->debugfs)) + vhci_debugfs_init(data); hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; @@ -651,6 +659,21 @@ static int vhci_open(struct inode *inode, struct file *file) return 0; } +static void vhci_debugfs_remove(struct hci_dev *hdev) +{ + debugfs_lookup_and_remove("force_suspend", hdev->debugfs); + + debugfs_lookup_and_remove("force_wakeup", hdev->debugfs); + + if (IS_ENABLED(CONFIG_BT_MSFTEXT)) + debugfs_lookup_and_remove("msft_opcode", hdev->debugfs); + + if (IS_ENABLED(CONFIG_BT_AOSPEXT)) + debugfs_lookup_and_remove("aosp_capable", hdev->debugfs); + + debugfs_lookup_and_remove("force_devcoredump", hdev->debugfs); +} + static int vhci_release(struct inode *inode, struct file *file) { struct vhci_data *data = file->private_data; @@ -662,6 +685,8 @@ static int vhci_release(struct inode *inode, struct file *file) hdev = data->hdev; if (hdev) { + if (!IS_ERR_OR_NULL(hdev->debugfs)) + vhci_debugfs_remove(hdev); hci_unregister_dev(hdev); hci_free_dev(hdev); } diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 756f292df9e8..6f1a37e85c6a 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -327,17 +327,17 @@ static int virtbt_probe(struct virtio_device *vdev) hdev->setup = virtbt_setup_intel; hdev->shutdown = virtbt_shutdown_generic; hdev->set_bdaddr = virtbt_set_bdaddr_intel; - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); break; case VIRTIO_BT_CONFIG_VENDOR_REALTEK: hdev->manufacturer = 93; hdev->setup = virtbt_setup_realtek; hdev->shutdown = virtbt_shutdown_generic; - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); break; } } diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index ff669a8ccad9..fe7600283e70 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -87,6 +87,12 @@ config HISILICON_LPC Driver to enable I/O access to devices attached to the Low Pin Count bus on the HiSilicon Hip06/7 SoC. +config IMX_AIPSTZ + tristate "Support for IMX Secure AHB to IP Slave bus (AIPSTZ) bridge" + depends on ARCH_MXC + help + Enable support for IMX AIPSTZ bridge. + config IMX_WEIM bool "Freescale EIM DRIVER" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index cddd4984d6af..8e693fe8a03a 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -15,6 +15,7 @@ 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 obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 7671bd158545..c1c0a4759c7e 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -943,6 +943,7 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, struct fsl_mc_obj_desc endpoint_desc = {{ 0 }}; struct dprc_endpoint endpoint1 = {{ 0 }}; struct dprc_endpoint endpoint2 = {{ 0 }}; + struct fsl_mc_bus *mc_bus; int state, err; mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); @@ -966,6 +967,8 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, strcpy(endpoint_desc.type, endpoint2.type); endpoint_desc.id = endpoint2.id; endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); + if (endpoint) + return endpoint; /* * We know that the device has an endpoint because we verified by @@ -973,17 +976,13 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, * yet discovered by the fsl-mc bus, thus the lookup returned NULL. * Force a rescan of the devices in this container and retry the lookup. */ - if (!endpoint) { - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); - - if (mutex_trylock(&mc_bus->scan_mutex)) { - err = dprc_scan_objects(mc_bus_dev, true); - mutex_unlock(&mc_bus->scan_mutex); - } - - if (err < 0) - return ERR_PTR(err); + mc_bus = to_fsl_mc_bus(mc_bus_dev); + if (mutex_trylock(&mc_bus->scan_mutex)) { + err = dprc_scan_objects(mc_bus_dev, true); + mutex_unlock(&mc_bus->scan_mutex); } + if (err < 0) + return ERR_PTR(err); endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); /* diff --git a/drivers/bus/imx-aipstz.c b/drivers/bus/imx-aipstz.c new file mode 100644 index 000000000000..5fdf377f5d06 --- /dev/null +++ b/drivers/bus/imx-aipstz.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 NXP + */ + +#include +#include +#include +#include +#include +#include + +#define IMX_AIPSTZ_MPR0 0x0 + +struct imx_aipstz_config { + u32 mpr0; +}; + +struct imx_aipstz_data { + void __iomem *base; + const struct imx_aipstz_config *default_cfg; +}; + +static void imx_aipstz_apply_default(struct imx_aipstz_data *data) +{ + writel(data->default_cfg->mpr0, data->base + IMX_AIPSTZ_MPR0); +} + +static const struct of_device_id imx_aipstz_match_table[] = { + { .compatible = "simple-bus", }, + { } +}; + +static int imx_aipstz_probe(struct platform_device *pdev) +{ + struct imx_aipstz_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return dev_err_probe(&pdev->dev, -ENOMEM, + "failed to allocate data memory\n"); + + data->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(data->base)) + return dev_err_probe(&pdev->dev, -ENOMEM, + "failed to get/ioremap AC memory\n"); + + data->default_cfg = of_device_get_match_data(&pdev->dev); + + imx_aipstz_apply_default(data); + + dev_set_drvdata(&pdev->dev, data); + + pm_runtime_set_active(&pdev->dev); + devm_pm_runtime_enable(&pdev->dev); + + return of_platform_populate(pdev->dev.of_node, imx_aipstz_match_table, + NULL, &pdev->dev); +} + +static void imx_aipstz_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); +} + +static int imx_aipstz_runtime_resume(struct device *dev) +{ + struct imx_aipstz_data *data = dev_get_drvdata(dev); + + /* restore potentially lost configuration during domain power-off */ + imx_aipstz_apply_default(data); + + return 0; +} + +static const struct dev_pm_ops imx_aipstz_pm_ops = { + RUNTIME_PM_OPS(NULL, imx_aipstz_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +/* + * following configuration is equivalent to: + * masters 0-7 => trusted for R/W + use AHB's HPROT[1] to det. privilege + */ +static const struct imx_aipstz_config imx8mp_aipstz_default_cfg = { + .mpr0 = 0x77777777, +}; + +static const struct of_device_id imx_aipstz_of_ids[] = { + { .compatible = "fsl,imx8mp-aipstz", .data = &imx8mp_aipstz_default_cfg }, + { } +}; +MODULE_DEVICE_TABLE(of, imx_aipstz_of_ids); + +static struct platform_driver imx_aipstz_of_driver = { + .probe = imx_aipstz_probe, + .remove = imx_aipstz_remove, + .driver = { + .name = "imx-aipstz", + .of_match_table = imx_aipstz_of_ids, + .pm = pm_ptr(&imx_aipstz_pm_ops), + }, +}; +module_platform_driver(imx_aipstz_of_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IMX secure AHB to IP Slave bus (AIPSTZ) bridge driver"); +MODULE_AUTHOR("Laurentiu Mihalcea "); diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index efa3b6dddf4d..205d83ac069f 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -31,8 +31,8 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, int ret; for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) { - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = mhi_buf->len; + bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr); + bhi_vec->size = cpu_to_le64(mhi_buf->len); } dev_dbg(dev, "BHIe programming for RDDM\n"); @@ -431,8 +431,8 @@ static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl, while (remainder) { to_cpy = min(remainder, mhi_buf->len); memcpy(mhi_buf->buf, buf, to_cpy); - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = to_cpy; + bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr); + bhi_vec->size = cpu_to_le64(to_cpy); buf += to_cpy; remainder -= to_cpy; diff --git a/drivers/bus/mhi/host/debugfs.c b/drivers/bus/mhi/host/debugfs.c index cfec7811dfbb..39e45748a24c 100644 --- a/drivers/bus/mhi/host/debugfs.c +++ b/drivers/bus/mhi/host/debugfs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "internal.h" static int mhi_debugfs_states_show(struct seq_file *m, void *d) @@ -22,7 +23,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d) mhi_is_active(mhi_cntrl) ? "Active" : "Inactive", mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee), - mhi_cntrl->wake_set ? "true" : "false"); + str_true_false(mhi_cntrl->wake_set)); /* counters */ seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2, diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 13e7a55f54ff..7f72aab38ce9 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -176,7 +176,7 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl, return 0; } -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) +static void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) { int i; struct mhi_event *mhi_event = mhi_cntrl->mhi_event; @@ -191,7 +191,7 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) free_irq(mhi_cntrl->irq[0], mhi_cntrl); } -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) +static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) { struct mhi_event *mhi_event = mhi_cntrl->mhi_event; struct device *dev = &mhi_cntrl->mhi_dev->dev; @@ -254,7 +254,7 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) return ret; } -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) +static void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) { int i; struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt; @@ -299,7 +299,7 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) mhi_cntrl->mhi_ctxt = NULL; } -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) +static int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) { struct mhi_ctxt *mhi_ctxt; struct mhi_chan_ctxt *chan_ctxt; diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index ce566f7d2e92..034be33565b7 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -25,8 +25,8 @@ struct mhi_ctxt { }; struct bhi_vec_entry { - u64 dma_addr; - u64 size; + __le64 dma_addr; + __le64 size; }; enum mhi_fw_load_type { @@ -383,19 +383,12 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, /* Initialization methods */ int mhi_init_mmio(struct mhi_controller *mhi_cntrl); -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl); -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl); -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl); -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, struct image_info *img_info); void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); /* Automatically allocate and queue inbound buffers */ #define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) -int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, unsigned int flags); - int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 9bb0df43ceef..52bef663e182 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -602,7 +602,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, { dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); struct mhi_ring_element *local_rp, *ev_tre; - void *dev_rp; + void *dev_rp, *next_rp; struct mhi_buf_info *buf_info; u16 xfer_len; @@ -621,6 +621,16 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, result.dir = mhi_chan->dir; local_rp = tre_ring->rp; + + next_rp = local_rp + 1; + if (next_rp >= tre_ring->base + tre_ring->len) + next_rp = tre_ring->base; + if (dev_rp != next_rp && !MHI_TRE_DATA_GET_CHAIN(local_rp)) { + dev_err(&mhi_cntrl->mhi_dev->dev, + "Event element points to an unexpected TRE\n"); + break; + } + while (local_rp != dev_rp) { buf_info = buf_ring->rp; /* If it's the last TRE, get length from the event */ @@ -1435,7 +1445,7 @@ static void mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, mutex_unlock(&mhi_chan->mutex); } -int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, +static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, unsigned int flags) { int ret = 0; diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 589cb6722316..4edb5bb476ba 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -43,6 +43,7 @@ * @mru_default: default MRU size for MBIM network packets * @sideband_wake: Devices using dedicated sideband GPIO for wakeup instead * of inband wake support (such as sdx24) + * @no_m3: M3 not supported */ struct mhi_pci_dev_info { const struct mhi_controller_config *config; @@ -54,6 +55,7 @@ struct mhi_pci_dev_info { unsigned int dma_data_width; unsigned int mru_default; bool sideband_wake; + bool no_m3; }; #define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \ @@ -295,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = { .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, .sideband_wake = false, + .no_m3 = true, }; static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = { @@ -490,6 +493,23 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), }; +static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = { + MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), + MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), + MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), + MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0), + MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0), + MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0), + MHI_CHANNEL_CONFIG_UL(50, "NMEA", 32, 0), + MHI_CHANNEL_CONFIG_DL(51, "NMEA", 32, 0), + MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2), + MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), +}; + static struct mhi_event_config mhi_foxconn_sdx55_events[] = { MHI_EVENT_CONFIG_CTRL(0, 128), MHI_EVENT_CONFIG_DATA(1, 128), @@ -506,6 +526,15 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = { .event_cfg = mhi_foxconn_sdx55_events, }; +static const struct mhi_controller_config modem_foxconn_sdx61_config = { + .max_channels = 128, + .timeout_ms = 20000, + .num_channels = ARRAY_SIZE(mhi_foxconn_sdx61_channels), + .ch_cfg = mhi_foxconn_sdx61_channels, + .num_events = ARRAY_SIZE(mhi_foxconn_sdx55_events), + .event_cfg = mhi_foxconn_sdx55_events, +}; + static const struct mhi_controller_config modem_foxconn_sdx72_config = { .max_channels = 128, .timeout_ms = 20000, @@ -593,8 +622,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = { .sideband_wake = false, }; -static const struct mhi_pci_dev_info mhi_foxconn_t99w515_info = { - .name = "foxconn-t99w515", +static const struct mhi_pci_dev_info mhi_foxconn_t99w640_info = { + .name = "foxconn-t99w640", .edl = "qcom/sdx72m/foxconn/edl.mbn", .edl_trigger = true, .config = &modem_foxconn_sdx72_config, @@ -615,6 +644,17 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5934e_info = { .sideband_wake = false, }; +static const struct mhi_pci_dev_info mhi_foxconn_t99w696_info = { + .name = "foxconn-t99w696", + .edl = "qcom/sdx61/foxconn/prog_firehose_lite.elf", + .edl_trigger = true, + .config = &modem_foxconn_sdx61_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = false, +}; + static const struct mhi_channel_config mhi_mv3x_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 64, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 64, 0), @@ -695,6 +735,7 @@ static const struct mhi_pci_dev_info mhi_sierra_em919x_info = { .config = &modem_sierra_em919x_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, + .mru_default = 32768, .sideband_wake = false, }; @@ -818,6 +859,16 @@ static const struct mhi_pci_dev_info mhi_telit_fn920c04_info = { .edl_trigger = true, }; +static const struct mhi_pci_dev_info mhi_telit_fn990b40_info = { + .name = "telit-fn990b40", + .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", @@ -852,6 +903,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200), .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, + /* EM929x (sdx65), use the same configuration as EM919x */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x18d7, 0x0301), + .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, /* Telit FN980 hardware revision v1 */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000), .driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info }, @@ -863,8 +917,26 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FE990A */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015), .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info }, + /* Foxconn T99W696.01, Lenovo Generic SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe142), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.02, Lenovo X1 Carbon SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe143), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.03, Lenovo X1 2in1 SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe144), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.04, Lenovo PRC SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe145), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.00, Foxconn SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe146), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info }, + /* Telit FN990B40 (sdx72) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0309, 0x1c5d, 0x201a), + .driver_data = (kernel_ulong_t) &mhi_telit_fn990b40_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info }, /* QDU100, x100-DU */ @@ -920,9 +992,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* DW5932e (sdx62), Non-eSIM */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9), .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5932e_info }, - /* T99W515 (sdx72) */ + /* T99W640 (sdx72) */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe118), - .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w515_info }, + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w640_info }, /* DW5934e(sdx72), With eSIM */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe11d), .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5934e_info }, @@ -1306,8 +1378,8 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* start health check */ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); - /* Only allow runtime-suspend if PME capable (for wakeup) */ - if (pci_pme_capable(pdev, PCI_D3hot)) { + /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */ + if (pci_pme_capable(pdev, PCI_D3hot) && !(info->no_m3)) { pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev); diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c index 6c3e5c5dae10..7ce61d629a87 100644 --- a/drivers/bus/moxtet.c +++ b/drivers/bus/moxtet.c @@ -737,8 +737,7 @@ static int moxtet_irq_setup(struct moxtet *moxtet) { int i, ret; - moxtet->irq.domain = irq_domain_create_simple(of_fwnode_handle(moxtet->dev->of_node), - MOXTET_NIRQS, 0, + moxtet->irq.domain = irq_domain_create_simple(dev_fwnode(moxtet->dev), MOXTET_NIRQS, 0, &moxtet_irq_domain, moxtet); if (moxtet->irq.domain == NULL) { dev_err(moxtet->dev, "Could not add IRQ domain\n"); diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 9f624e5da991..5566ad11399e 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -2170,9 +2170,8 @@ static int sysc_reset(struct sysc *ddata) static int sysc_init_module(struct sysc *ddata) { bool rstctrl_deasserted = false; - int error = 0; + int error = sysc_clockdomain_init(ddata); - error = sysc_clockdomain_init(ddata); if (error) return error; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 21a10552da61..31ba1f8c1f78 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -624,9 +624,6 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi) if (check_media_type == 1) cdi->options |= (int) CDO_CHECK_TYPE; - if (CDROM_CAN(CDC_MRW_W)) - cdi->exit = cdrom_mrw_exit; - if (cdi->ops->read_cdda_bpc) cdi->cdda_method = CDDA_BPC_FULL; else @@ -651,9 +648,6 @@ void unregister_cdrom(struct cdrom_device_info *cdi) list_del(&cdi->list); mutex_unlock(&cdrom_mutex); - if (cdi->exit) - cdi->exit(cdi); - cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); } EXPORT_SYMBOL(unregister_cdrom); @@ -1264,6 +1258,8 @@ void cdrom_release(struct cdrom_device_info *cdi) cd_dbg(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); cdrom_dvd_rw_close_write(cdi); + if (CDROM_CAN(CDC_MRW_W)) + cdrom_mrw_exit(cdi); if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) { cd_dbg(CD_CLOSE, "Unlocking door!\n"); diff --git a/drivers/cdx/Kconfig b/drivers/cdx/Kconfig index a08958485e31..3af41f51cf38 100644 --- a/drivers/cdx/Kconfig +++ b/drivers/cdx/Kconfig @@ -7,7 +7,8 @@ config CDX_BUS bool "CDX Bus driver" - depends on OF && ARM64 + depends on OF && ARM64 || COMPILE_TEST + select GENERIC_MSI_IRQ help Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus exposes Fabric devices which uses composable DMA IP to the diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index f8e729761aee..0641a4c21e66 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -9,6 +9,7 @@ if CDX_BUS config CDX_CONTROLLER tristate "CDX bus controller" + depends on HAS_DMA select GENERIC_MSI_IRQ select REMOTEPROC select RPMSG diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index d623f9c7517a..fca83141e3e6 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -195,19 +195,16 @@ static int xlnx_cdx_probe(struct platform_device *pdev) /* Create MSI domain */ cdx->msi_domain = cdx_msi_domain_init(&pdev->dev); if (!cdx->msi_domain) { - dev_err(&pdev->dev, "cdx_msi_domain_init() failed"); - ret = -ENODEV; + ret = dev_err_probe(&pdev->dev, -ENODEV, "cdx_msi_domain_init() failed"); goto cdx_msi_fail; } ret = cdx_setup_rpmsg(pdev); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n"); + dev_err_probe(&pdev->dev, ret, "Failed to register CDX RPMsg transport\n"); goto cdx_rpmsg_fail; } - dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n"); return 0; cdx_rpmsg_fail: @@ -246,31 +243,13 @@ MODULE_DEVICE_TABLE(of, cdx_match_table); static struct platform_driver cdx_pdriver = { .driver = { .name = "cdx-controller", - .pm = NULL, .of_match_table = cdx_match_table, }, .probe = xlnx_cdx_probe, .remove = xlnx_cdx_remove, }; -static int __init cdx_controller_init(void) -{ - int ret; - - ret = platform_driver_register(&cdx_pdriver); - if (ret) - pr_err("platform_driver_register() failed: %d\n", ret); - - return ret; -} - -static void __exit cdx_controller_exit(void) -{ - platform_driver_unregister(&cdx_pdriver); -} - -module_init(cdx_controller_init); -module_exit(cdx_controller_exit); +module_platform_driver(cdx_pdriver); MODULE_AUTHOR("AMD Inc."); MODULE_DESCRIPTION("CDX controller for AMD devices"); diff --git a/drivers/cdx/controller/cdx_rpmsg.c b/drivers/cdx/controller/cdx_rpmsg.c index 04b578a0be17..61f1a290ff08 100644 --- a/drivers/cdx/controller/cdx_rpmsg.c +++ b/drivers/cdx/controller/cdx_rpmsg.c @@ -129,8 +129,7 @@ static int cdx_rpmsg_probe(struct rpmsg_device *rpdev) chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = rpdev->dst; - strscpy(chinfo.name, cdx_rpmsg_id_table[0].name, - strlen(cdx_rpmsg_id_table[0].name)); + strscpy(chinfo.name, cdx_rpmsg_id_table[0].name, sizeof(chinfo.name)); cdx_mcdi->ept = rpmsg_create_ept(rpdev, cdx_rpmsg_cb, NULL, chinfo); if (!cdx_mcdi->ept) { diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ae6196760556..d2cfc584e202 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -237,7 +237,7 @@ config APPLICOM config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" - depends on X86_32 && PCI && INPUT + depends on X86_32 && PCI && INPUT && HAS_IOPORT depends on ACPI_EC || !ACPI help This driver enables access to the Sony Programmable I/O Control diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index bf490967241a..2505df1f4e69 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -720,11 +720,6 @@ static const struct pci_device_id agp_amd64_pci_table[] = { MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table); -static const struct pci_device_id agp_amd64_pci_promisc_table[] = { - { PCI_DEVICE_CLASS(0, 0) }, - { } -}; - static DEFINE_SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, NULL, agp_amd64_resume); static struct pci_driver agp_amd64_pci_driver = { @@ -739,6 +734,7 @@ static struct pci_driver agp_amd64_pci_driver = { /* Not static due to IOMMU code calling it early. */ int __init agp_amd64_init(void) { + struct pci_dev *pdev = NULL; int err = 0; if (agp_off) @@ -767,9 +763,13 @@ int __init agp_amd64_init(void) } /* Look for any AGP bridge */ - agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table; - err = driver_attach(&agp_amd64_pci_driver.driver); - if (err == 0 && agp_bridges_found == 0) { + for_each_pci_dev(pdev) + if (pci_find_capability(pdev, PCI_CAP_ID_AGP)) + pci_add_dynid(&agp_amd64_pci_driver, + pdev->vendor, pdev->device, + pdev->subsystem_vendor, + pdev->subsystem_device, 0, 0, 0); + if (agp_bridges_found == 0) { pci_unregister_driver(&agp_amd64_pci_driver); err = -ENODEV; } diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c index d2b00458761e..6ed24be3481d 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -80,7 +80,6 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, ret = 4; out: - pm_runtime_mark_last_busy(trng->dev); pm_runtime_put_sync_autosuspend(trng->dev); return ret; } diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c index 4db198849695..a5be9258037f 100644 --- a/drivers/char/hw_random/cctrng.c +++ b/drivers/char/hw_random/cctrng.c @@ -98,7 +98,6 @@ static void cc_trng_pm_put_suspend(struct device *dev) { int rc = 0; - pm_runtime_mark_last_busy(dev); rc = pm_runtime_put_autosuspend(dev); if (rc) dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc); diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index b7fa1bc1122b..5808d09d12c4 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -98,7 +98,6 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) max -= sizeof(u32); } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_sync_autosuspend(priv->dev); return retval || !wait ? retval : -EIO; @@ -143,7 +142,9 @@ static int mtk_rng_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); - devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; dev_info(&pdev->dev, "registered RNG driver\n"); diff --git a/drivers/char/hw_random/npcm-rng.c b/drivers/char/hw_random/npcm-rng.c index 3e308c890bd2..40d6e29dea03 100644 --- a/drivers/char/hw_random/npcm-rng.c +++ b/drivers/char/hw_random/npcm-rng.c @@ -80,7 +80,6 @@ static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) max--; } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_sync_autosuspend(priv->dev); return retval || !wait ? retval : -EIO; diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c index 8064c792caf0..aa71f61c3dc9 100644 --- a/drivers/char/hw_random/omap3-rom-rng.c +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -56,7 +56,6 @@ static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) else r = 4; - pm_runtime_mark_last_busy(ddata->dev); pm_runtime_put_autosuspend(ddata->dev); return r; diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c index fb4a30b95507..6e3ed4b85605 100644 --- a/drivers/char/hw_random/rockchip-rng.c +++ b/drivers/char/hw_random/rockchip-rng.c @@ -223,7 +223,6 @@ static int rk3568_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) /* Read random data stored in the registers */ memcpy_fromio(buf, rk_rng->base + TRNG_RNG_DOUT, to_read); out: - pm_runtime_mark_last_busy(rk_rng->dev); pm_runtime_put_sync_autosuspend(rk_rng->dev); return (ret < 0) ? ret : to_read; @@ -263,7 +262,6 @@ static int rk3576_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) memcpy_fromio(buf, rk_rng->base + RKRNG_TRNG_DATA0, to_read); out: - pm_runtime_mark_last_busy(rk_rng->dev); pm_runtime_put_sync_autosuspend(rk_rng->dev); return (ret < 0) ? ret : to_read; @@ -355,7 +353,6 @@ static int rk3588_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) /* close the TRNG */ rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); - pm_runtime_mark_last_busy(rk_rng->dev); pm_runtime_put_sync_autosuspend(rk_rng->dev); return (ret < 0) ? ret : to_read; diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 98edbe796bc5..9a8c00586ab0 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -255,7 +255,6 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) } exit_rpm: - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_sync_autosuspend(priv->dev); return retval || !wait ? retval : -EIO; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 064944ae9fdc..8e9050f99e9e 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4607,10 +4607,10 @@ static int handle_one_recv_msg(struct ipmi_smi *intf, * The NetFN and Command in the response is not even * marginally correct. */ - dev_warn(intf->si_dev, - "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", - (msg->data[0] >> 2) | 1, msg->data[1], - msg->rsp[0] >> 2, msg->rsp[1]); + dev_warn_ratelimited(intf->si_dev, + "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", + (msg->data[0] >> 2) | 1, msg->data[1], + msg->rsp[0] >> 2, msg->rsp[1]); goto return_unspecified; } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index bb42dfe1c6a8..8b5524069c15 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2108,7 +2108,6 @@ static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2) static int __init init_ipmi_si(void) { struct smi_info *e, *e2; - enum ipmi_addr_src type = SI_INVALID; if (initialized) return 0; @@ -2190,9 +2189,6 @@ static int __init init_ipmi_si(void) initialized = true; mutex_unlock(&smi_infos_lock); - if (type) - return 0; - mutex_lock(&smi_infos_lock); if (unload_when_empty && list_empty(&smi_infos)) { mutex_unlock(&smi_infos_lock); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index ab759b492fdd..a013ddbf1466 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1146,14 +1146,8 @@ static struct ipmi_smi_watcher smi_watcher = { .smi_gone = ipmi_smi_gone }; -static int action_op(const char *inval, char *outval) +static int action_op_set_val(const char *inval) { - if (outval) - strcpy(outval, action); - - if (!inval) - return 0; - if (strcmp(inval, "reset") == 0) action_val = WDOG_TIMEOUT_RESET; else if (strcmp(inval, "none") == 0) @@ -1164,18 +1158,26 @@ static int action_op(const char *inval, char *outval) action_val = WDOG_TIMEOUT_POWER_DOWN; else return -EINVAL; - strcpy(action, inval); return 0; } -static int preaction_op(const char *inval, char *outval) +static int action_op(const char *inval, char *outval) { + int rv; + if (outval) - strcpy(outval, preaction); + strcpy(outval, action); if (!inval) return 0; + rv = action_op_set_val(inval); + if (!rv) + strcpy(action, inval); + return rv; +} +static int preaction_op_set_val(const char *inval) +{ if (strcmp(inval, "pre_none") == 0) preaction_val = WDOG_PRETIMEOUT_NONE; else if (strcmp(inval, "pre_smi") == 0) @@ -1188,18 +1190,26 @@ static int preaction_op(const char *inval, char *outval) preaction_val = WDOG_PRETIMEOUT_MSG_INT; else return -EINVAL; - strcpy(preaction, inval); return 0; } -static int preop_op(const char *inval, char *outval) +static int preaction_op(const char *inval, char *outval) { + int rv; + if (outval) - strcpy(outval, preop); + strcpy(outval, preaction); if (!inval) return 0; + rv = preaction_op_set_val(inval); + if (!rv) + strcpy(preaction, inval); + return 0; +} +static int preop_op_set_val(const char *inval) +{ if (strcmp(inval, "preop_none") == 0) preop_val = WDOG_PREOP_NONE; else if (strcmp(inval, "preop_panic") == 0) @@ -1208,7 +1218,22 @@ static int preop_op(const char *inval, char *outval) preop_val = WDOG_PREOP_GIVE_DATA; else return -EINVAL; - strcpy(preop, inval); + return 0; +} + +static int preop_op(const char *inval, char *outval) +{ + int rv; + + if (outval) + strcpy(outval, preop); + + if (!inval) + return 0; + + rv = preop_op_set_val(inval); + if (!rv) + strcpy(preop, inval); return 0; } @@ -1245,18 +1270,18 @@ static int __init ipmi_wdog_init(void) { int rv; - if (action_op(action, NULL)) { + if (action_op_set_val(action)) { action_op("reset", NULL); pr_info("Unknown action '%s', defaulting to reset\n", action); } - if (preaction_op(preaction, NULL)) { + if (preaction_op_set_val(preaction)) { preaction_op("pre_none", NULL); pr_info("Unknown preaction '%s', defaulting to none\n", preaction); } - if (preop_op(preop, NULL)) { + if (preop_op_set_val(preop)) { preop_op("preop_none", NULL); pr_info("Unknown preop '%s', defaulting to none\n", preop); } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index d5accc10a110..558302a64dd9 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -289,15 +289,15 @@ EXPORT_SYMBOL(misc_deregister); static int __init misc_init(void) { int err; - struct proc_dir_entry *ret; + struct proc_dir_entry *misc_proc_file; - ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); + misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops); err = class_register(&misc_class); if (err) goto fail_remove; - err = -EIO; - if (__register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops)) + err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops); + if (err < 0) goto fail_printk; return 0; @@ -305,7 +305,7 @@ static int __init misc_init(void) pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); class_unregister(&misc_class); fail_remove: - if (ret) + if (misc_proc_file) remove_proc_entry("misc", NULL); return err; } diff --git a/drivers/char/tpm/eventlog/common.c b/drivers/char/tpm/eventlog/common.c index 4c0bbba64ee5..691813d2a5a2 100644 --- a/drivers/char/tpm/eventlog/common.c +++ b/drivers/char/tpm/eventlog/common.c @@ -32,7 +32,7 @@ static int tpm_bios_measurements_open(struct inode *inode, struct tpm_chip *chip; inode_lock(inode); - if (!inode->i_private) { + if (!inode->i_nlink) { inode_unlock(inode); return -ENODEV; } @@ -105,7 +105,7 @@ static int tpm_read_log(struct tpm_chip *chip) void tpm_bios_log_setup(struct tpm_chip *chip) { const char *name = dev_name(&chip->dev); - unsigned int cnt; + struct dentry *dentry; int log_version; int rc = 0; @@ -117,14 +117,12 @@ void tpm_bios_log_setup(struct tpm_chip *chip) return; log_version = rc; - cnt = 0; - chip->bios_dir[cnt] = securityfs_create_dir(name, NULL); + chip->bios_dir = securityfs_create_dir(name, NULL); /* NOTE: securityfs_create_dir can return ENODEV if securityfs is * compiled out. The caller should ignore the ENODEV return code. */ - if (IS_ERR(chip->bios_dir[cnt])) - goto err; - cnt++; + if (IS_ERR(chip->bios_dir)) + return; chip->bin_log_seqops.chip = chip; if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) @@ -135,14 +133,13 @@ void tpm_bios_log_setup(struct tpm_chip *chip) &tpm1_binary_b_measurements_seqops; - chip->bios_dir[cnt] = + dentry = securityfs_create_file("binary_bios_measurements", - 0440, chip->bios_dir[0], + 0440, chip->bios_dir, (void *)&chip->bin_log_seqops, &tpm_bios_measurements_ops); - if (IS_ERR(chip->bios_dir[cnt])) + if (IS_ERR(dentry)) goto err; - cnt++; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { @@ -150,42 +147,23 @@ void tpm_bios_log_setup(struct tpm_chip *chip) chip->ascii_log_seqops.seqops = &tpm1_ascii_b_measurements_seqops; - chip->bios_dir[cnt] = + dentry = securityfs_create_file("ascii_bios_measurements", - 0440, chip->bios_dir[0], + 0440, chip->bios_dir, (void *)&chip->ascii_log_seqops, &tpm_bios_measurements_ops); - if (IS_ERR(chip->bios_dir[cnt])) + if (IS_ERR(dentry)) goto err; - cnt++; } return; err: - chip->bios_dir[cnt] = NULL; tpm_bios_log_teardown(chip); return; } void tpm_bios_log_teardown(struct tpm_chip *chip) { - int i; - struct inode *inode; - - /* securityfs_remove currently doesn't take care of handling sync - * between removal and opening of pseudo files. To handle this, a - * workaround is added by making i_private = NULL here during removal - * and to check it during open(), both within inode_lock()/unlock(). - * This design ensures that open() either safely gets kref or fails. - */ - for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) { - if (chip->bios_dir[i]) { - inode = d_inode(chip->bios_dir[i]); - inode_lock(inode); - inode->i_private = NULL; - inode_unlock(inode); - securityfs_remove(chip->bios_dir[i]); - } - } + securityfs_remove(chip->bios_dir); } diff --git a/drivers/char/tpm/eventlog/of.c b/drivers/char/tpm/eventlog/of.c index 930fe43d5daf..92cec9722ee4 100644 --- a/drivers/char/tpm/eventlog/of.c +++ b/drivers/char/tpm/eventlog/of.c @@ -24,16 +24,10 @@ static int tpm_read_log_memory_region(struct tpm_chip *chip) { - struct device_node *node; struct resource res; int rc; - node = of_parse_phandle(chip->dev.parent->of_node, "memory-region", 0); - if (!node) - return -ENODEV; - - rc = of_address_to_resource(node, 0, &res); - of_node_put(node); + rc = of_reserved_mem_region_to_resource(chip->dev.parent->of_node, 0, &res); if (rc) return rc; diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index c0771980bc2f..2ed7815e4899 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -300,7 +300,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) * send TPM commands through the I2C bus. */ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, - size_t len) + size_t bufsiz, size_t len) { struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u32 status, i, size, ordinal; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 8d7e4da6ed53..b71725827743 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -82,6 +82,13 @@ static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status) return chip->ops->req_canceled(chip, status); } +static bool tpm_transmit_completed(u8 status, struct tpm_chip *chip) +{ + u8 status_masked = status & chip->ops->req_complete_mask; + + return status_masked == chip->ops->req_complete_val; +} + static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { struct tpm_header *header = buf; @@ -106,7 +113,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) return -E2BIG; } - rc = chip->ops->send(chip, buf, count); + rc = chip->ops->send(chip, buf, bufsiz, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, @@ -114,8 +121,19 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) return rc; } - /* A sanity check. send() should just return zero on success e.g. - * not the command length. + /* + * Synchronous devices return the response directly during the send() + * call in the same buffer. + */ + if (chip->flags & TPM_CHIP_FLAG_SYNC) { + len = rc; + rc = 0; + goto out_sync; + } + + /* + * A sanity check. send() of asynchronous devices should just return + * zero on success e.g. not the command length. */ if (rc > 0) { dev_warn(&chip->dev, @@ -129,8 +147,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { u8 status = tpm_chip_status(chip); - if ((status & chip->ops->req_complete_mask) == - chip->ops->req_complete_val) + if (tpm_transmit_completed(status, chip)) goto out_recv; if (tpm_chip_req_canceled(chip, status)) { @@ -142,6 +159,13 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) rmb(); } while (time_before(jiffies, stop)); + /* + * Check for completion one more time, just in case the device reported + * it while the driver was sleeping in the busy loop above. + */ + if (tpm_transmit_completed(tpm_chip_status(chip), chip)) + goto out_recv; + tpm_chip_cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); return -ETIME; @@ -151,7 +175,10 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) if (len < 0) { rc = len; dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc); - } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) + return rc; + } +out_sync: + if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; return rc ? rc : len; diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 7b5049b3d476..bdb119453dfb 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -390,7 +390,7 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, * on every operation, so we weld the hmac init and final functions in * here to give it the same usage characteristics as a regular hash */ -static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len) +static void tpm2_hmac_init(struct sha256_ctx *sctx, u8 *key, u32 key_len) { u8 pad[SHA256_BLOCK_SIZE]; int i; @@ -406,7 +406,7 @@ static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len) sha256_update(sctx, pad, sizeof(pad)); } -static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, +static void tpm2_hmac_final(struct sha256_ctx *sctx, u8 *key, u32 key_len, u8 *out) { u8 pad[SHA256_BLOCK_SIZE]; @@ -440,7 +440,7 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, const __be32 bits = cpu_to_be32(bytes * 8); while (bytes > 0) { - struct sha256_state sctx; + struct sha256_ctx sctx; __be32 c = cpu_to_be32(counter); tpm2_hmac_init(&sctx, key, key_len); @@ -467,7 +467,7 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, u8 *out) { - struct sha256_state sctx; + struct sha256_ctx sctx; /* * this should be an iterative counter, but because we know * we're only taking 32 bytes for the point using a sha256 @@ -592,7 +592,7 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) u8 *hmac = NULL; u32 attrs; u8 cphash[SHA256_DIGEST_SIZE]; - struct sha256_state sctx; + struct sha256_ctx sctx; if (!auth) return; @@ -750,7 +750,7 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, off_t offset_s, offset_p; u8 rphash[SHA256_DIGEST_SIZE]; u32 attrs, cc; - struct sha256_state sctx; + struct sha256_ctx sctx; u16 tag = be16_to_cpu(head->tag); int parm_len, len, i, handles; diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 54a0360a3c95..f25faf468bba 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -148,7 +148,8 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) return size; } -static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); int i; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 876edf2705ab..ed97344f2324 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -426,7 +426,7 @@ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) } #endif -static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); int rc = 0; diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c index 4ead61f01299..755b77b32ea4 100644 --- a/drivers/char/tpm/tpm_crb_ffa.c +++ b/drivers/char/tpm/tpm_crb_ffa.c @@ -10,8 +10,16 @@ #define pr_fmt(fmt) "CRB_FFA: " fmt #include +#include +#include #include "tpm_crb_ffa.h" +static unsigned int busy_timeout_ms = 2000; + +module_param(busy_timeout_ms, uint, 0644); +MODULE_PARM_DESC(busy_timeout_ms, + "Maximum time in ms to retry before giving up on busy"); + /* TPM service function status codes */ #define CRB_FFA_OK 0x05000001 #define CRB_FFA_OK_RESULTS_RETURNED 0x05000002 @@ -115,6 +123,7 @@ struct tpm_crb_ffa { }; static struct tpm_crb_ffa *tpm_crb_ffa; +static struct ffa_driver tpm_crb_ffa_driver; static int tpm_crb_ffa_to_linux_errno(int errno) { @@ -168,50 +177,51 @@ static int tpm_crb_ffa_to_linux_errno(int errno) */ int tpm_crb_ffa_init(void) { + int ret = 0; + + if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) { + ret = ffa_register(&tpm_crb_ffa_driver); + if (ret) { + tpm_crb_ffa = ERR_PTR(-ENODEV); + return ret; + } + } + if (!tpm_crb_ffa) - return -ENOENT; + ret = -ENOENT; if (IS_ERR_VALUE(tpm_crb_ffa)) - return -ENODEV; + ret = -ENODEV; - return 0; + return ret; } EXPORT_SYMBOL_GPL(tpm_crb_ffa_init); -static int __tpm_crb_ffa_send_recieve(unsigned long func_id, - unsigned long a0, - unsigned long a1, - unsigned long a2) +static int __tpm_crb_ffa_try_send_receive(unsigned long func_id, + unsigned long a0, unsigned long a1, + unsigned long a2) { const struct ffa_msg_ops *msg_ops; int ret; - if (!tpm_crb_ffa) - return -ENOENT; - msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops; if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { - memset(&tpm_crb_ffa->direct_msg_data2, 0x00, - sizeof(struct ffa_send_direct_data2)); - - tpm_crb_ffa->direct_msg_data2.data[0] = func_id; - tpm_crb_ffa->direct_msg_data2.data[1] = a0; - tpm_crb_ffa->direct_msg_data2.data[2] = a1; - tpm_crb_ffa->direct_msg_data2.data[3] = a2; + tpm_crb_ffa->direct_msg_data2 = (struct ffa_send_direct_data2){ + .data = { func_id, a0, a1, a2 }, + }; ret = msg_ops->sync_send_receive2(tpm_crb_ffa->ffa_dev, &tpm_crb_ffa->direct_msg_data2); if (!ret) ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data2.data[0]); } else { - memset(&tpm_crb_ffa->direct_msg_data, 0x00, - sizeof(struct ffa_send_direct_data)); - - tpm_crb_ffa->direct_msg_data.data1 = func_id; - tpm_crb_ffa->direct_msg_data.data2 = a0; - tpm_crb_ffa->direct_msg_data.data3 = a1; - tpm_crb_ffa->direct_msg_data.data4 = a2; + tpm_crb_ffa->direct_msg_data = (struct ffa_send_direct_data){ + .data1 = func_id, + .data2 = a0, + .data3 = a1, + .data4 = a2, + }; ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev, &tpm_crb_ffa->direct_msg_data); @@ -219,6 +229,33 @@ static int __tpm_crb_ffa_send_recieve(unsigned long func_id, ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1); } + return ret; +} + +static int __tpm_crb_ffa_send_receive(unsigned long func_id, unsigned long a0, + unsigned long a1, unsigned long a2) +{ + ktime_t start, stop; + int ret; + + if (!tpm_crb_ffa) + return -ENOENT; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(busy_timeout_ms)); + + for (;;) { + ret = __tpm_crb_ffa_try_send_receive(func_id, a0, a1, a2); + if (ret != -EBUSY) + break; + + usleep_range(50, 100); + if (ktime_after(ktime_get(), stop)) { + dev_warn(&tpm_crb_ffa->ffa_dev->dev, + "Busy retry timed out\n"); + break; + } + } return ret; } @@ -236,7 +273,7 @@ static int __tpm_crb_ffa_send_recieve(unsigned long func_id, * * Return: 0 on success, negative error code on failure. */ -int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) +static int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) { int rc; @@ -251,7 +288,7 @@ int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) guard(mutex)(&tpm_crb_ffa->msg_data_lock); - rc = __tpm_crb_ffa_send_recieve(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); + rc = __tpm_crb_ffa_send_receive(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); if (!rc) { if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]); @@ -264,7 +301,6 @@ int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) return rc; } -EXPORT_SYMBOL_GPL(tpm_crb_ffa_get_interface_version); /** * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB @@ -289,7 +325,7 @@ int tpm_crb_ffa_start(int request_type, int locality) guard(mutex)(&tpm_crb_ffa->msg_data_lock); - return __tpm_crb_ffa_send_recieve(CRB_FFA_START, request_type, locality, 0x00); + return __tpm_crb_ffa_send_receive(CRB_FFA_START, request_type, locality, 0x00); } EXPORT_SYMBOL_GPL(tpm_crb_ffa_start); @@ -369,7 +405,9 @@ static struct ffa_driver tpm_crb_ffa_driver = { .id_table = tpm_crb_ffa_device_id, }; +#ifdef MODULE module_ffa_driver(tpm_crb_ffa_driver); +#endif MODULE_AUTHOR("Arm"); MODULE_DESCRIPTION("TPM CRB FFA driver"); diff --git a/drivers/char/tpm/tpm_crb_ffa.h b/drivers/char/tpm/tpm_crb_ffa.h index 645c41ede10e..d7e1344ea003 100644 --- a/drivers/char/tpm/tpm_crb_ffa.h +++ b/drivers/char/tpm/tpm_crb_ffa.h @@ -11,11 +11,9 @@ #if IS_REACHABLE(CONFIG_TCG_ARM_CRB_FFA) int tpm_crb_ffa_init(void); -int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor); int tpm_crb_ffa_start(int request_type, int locality); #else static inline int tpm_crb_ffa_init(void) { return 0; } -static inline int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) { return 0; } static inline int tpm_crb_ffa_start(int request_type, int locality) { return 0; } #endif diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 53ba28ccd5d3..4e63c30aeaf1 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -31,45 +31,19 @@ static const uuid_t ftpm_ta_uuid = 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96); /** - * ftpm_tee_tpm_op_recv() - retrieve fTPM response. - * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h. - * @buf: the buffer to store data. - * @count: the number of bytes to read. - * - * Return: - * In case of success the number of bytes received. - * On failure, -errno. - */ -static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) -{ - struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); - size_t len; - - len = pvt_data->resp_len; - if (count < len) { - dev_err(&chip->dev, - "%s: Invalid size in recv: count=%zd, resp_len=%zd\n", - __func__, count, len); - return -EIO; - } - - memcpy(buf, pvt_data->resp_buf, len); - pvt_data->resp_len = 0; - - return len; -} - -/** - * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory. + * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory + * and retrieve the response. * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h - * @buf: the buffer to send. - * @len: the number of bytes to send. + * @buf: the buffer to send and to store the response. + * @bufsiz: the size of the buffer. + * @cmd_len: the number of bytes to send. * * Return: - * In case of success, returns 0. + * In case of success, returns the number of bytes received. * On failure, -errno */ -static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t cmd_len) { struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); size_t resp_len; @@ -80,16 +54,15 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) struct tee_param command_params[4]; struct tee_shm *shm = pvt_data->shm; - if (len > MAX_COMMAND_SIZE) { + if (cmd_len > MAX_COMMAND_SIZE) { dev_err(&chip->dev, "%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n", - __func__, len); + __func__, cmd_len); return -EIO; } memset(&transceive_args, 0, sizeof(transceive_args)); memset(command_params, 0, sizeof(command_params)); - pvt_data->resp_len = 0; /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */ transceive_args = (struct tee_ioctl_invoke_arg) { @@ -103,7 +76,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, .u.memref = { .shm = shm, - .size = len, + .size = cmd_len, .shm_offs = 0, }, }; @@ -115,7 +88,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) return PTR_ERR(temp_buf); } memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE)); - memcpy(temp_buf, buf, len); + memcpy(temp_buf, buf, cmd_len); command_params[1] = (struct tee_param) { .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, @@ -156,17 +129,20 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) __func__, resp_len); return -EIO; } + if (resp_len > bufsiz) { + dev_err(&chip->dev, + "%s: resp_len=%zd exceeds bufsiz=%zd\n", + __func__, resp_len, bufsiz); + return -EIO; + } - /* sanity checks look good, cache the response */ - memcpy(pvt_data->resp_buf, temp_buf, resp_len); - pvt_data->resp_len = resp_len; + memcpy(buf, temp_buf, resp_len); - return 0; + return resp_len; } static const struct tpm_class_ops ftpm_tee_tpm_ops = { .flags = TPM_OPS_AUTO_STARTUP, - .recv = ftpm_tee_tpm_op_recv, .send = ftpm_tee_tpm_op_send, }; @@ -251,7 +227,7 @@ static int ftpm_tee_probe(struct device *dev) } pvt_data->chip = chip; - pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2; + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC; /* Create a character device for the fTPM */ rc = tpm_chip_register(pvt_data->chip); diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h index e39903b7ea07..8d5c3f0d2879 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.h +++ b/drivers/char/tpm/tpm_ftpm_tee.h @@ -22,16 +22,12 @@ * struct ftpm_tee_private - fTPM's private data * @chip: struct tpm_chip instance registered with tpm framework. * @session: fTPM TA session identifier. - * @resp_len: cached response buffer length. - * @resp_buf: cached response buffer. * @ctx: TEE context handler. * @shm: Memory pool shared with fTPM TA in TEE. */ struct ftpm_tee_private { struct tpm_chip *chip; u32 session; - size_t resp_len; - u8 resp_buf[MAX_RESPONSE_SIZE]; struct tee_context *ctx; struct tee_shm *shm; }; diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index d1d27fdfe523..4f229656a8e2 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -37,7 +37,8 @@ struct priv_data { u8 buffer[sizeof(struct tpm_header) + 25]; }; -static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 81d8a78dc655..bdf1f329a679 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -514,7 +514,8 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) return size; } -static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { int rc, status; ssize_t burstcnt; diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 3c3ee5f551db..d44903b29929 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -350,7 +350,8 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * tpm.c can skip polling for the data to be available as the interrupt is * waited for here */ -static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct device *dev = chip->dev.parent; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 76d048f63d55..4734a69406ce 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -191,13 +191,15 @@ static int tpm_ibmvtpm_resume(struct device *dev) * tpm_ibmvtpm_send() - Send a TPM command * @chip: tpm chip struct * @buf: buffer contains data to send - * @count: size of buffer + * @bufsiz: size of the buffer + * @count: length of the command * * Return: * 0 on success, * -errno on error */ -static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); bool retry = true; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 2d2ae37153ba..7638b65b851b 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -312,7 +312,8 @@ static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } -static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) +static int tpm_inf_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { int i; int ret; diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 0f62bbc940da..879ac88f5783 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -178,7 +178,8 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return size; } -static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) +static int tpm_nsc_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); u8 data; diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index bc7b1b4501b3..d53fce1c9d6f 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -52,7 +52,7 @@ static ssize_t tpm_show_ppi_version(struct device *dev, { struct tpm_chip *chip = to_tpm_chip(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); + return sysfs_emit(buf, "%s\n", chip->ppi_version); } static ssize_t tpm_show_ppi_request(struct device *dev, @@ -87,12 +87,10 @@ static ssize_t tpm_show_ppi_request(struct device *dev, else { req = obj->package.elements[1].integer.value; if (tpm_ppi_req_has_parameter(req)) - size = scnprintf(buf, PAGE_SIZE, - "%llu %llu\n", req, - obj->package.elements[2].integer.value); + size = sysfs_emit(buf, "%llu %llu\n", req, + obj->package.elements[2].integer.value); else - size = scnprintf(buf, PAGE_SIZE, - "%llu\n", req); + size = sysfs_emit(buf, "%llu\n", req); } } else if (obj->package.count == 2 && obj->package.elements[0].type == ACPI_TYPE_INTEGER && @@ -100,8 +98,8 @@ static ssize_t tpm_show_ppi_request(struct device *dev, if (obj->package.elements[0].integer.value) size = -EFAULT; else - size = scnprintf(buf, PAGE_SIZE, "%llu\n", - obj->package.elements[1].integer.value); + size = sysfs_emit(buf, "%llu\n", + obj->package.elements[1].integer.value); } ACPI_FREE(obj); @@ -211,10 +209,10 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, } if (ret < ARRAY_SIZE(info) - 1) - status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); + status = sysfs_emit(buf, "%d: %s\n", ret, info[ret]); else - status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, - info[ARRAY_SIZE(info)-1]); + status = sysfs_emit(buf, "%d: %s\n", ret, + info[ARRAY_SIZE(info) - 1]); return status; } @@ -255,23 +253,23 @@ static ssize_t tpm_show_ppi_response(struct device *dev, res = ret_obj[2].integer.value; if (req) { if (res == 0) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0: Success"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0: Success"); else if (res == 0xFFFFFFF0) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0xFFFFFFF0: User Abort"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0xFFFFFFF0: User Abort"); else if (res == 0xFFFFFFF1) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0xFFFFFFF1: BIOS Failure"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0xFFFFFFF1: BIOS Failure"); else if (res >= 1 && res <= 0x00000FFF) - status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", - req, res, "Corresponding TPM error"); + status = sysfs_emit(buf, "%llu %llu: %s\n", + req, res, "Corresponding TPM error"); else - status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", - req, res, "Error"); + status = sysfs_emit(buf, "%llu %llu: %s\n", + req, res, "Error"); } else { - status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", - req, "No Recent Request"); + status = sysfs_emit(buf, "%llu: %s\n", + req, "No Recent Request"); } cleanup: @@ -284,7 +282,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, { int i; u32 ret; - char *str = buf; + int len = 0; union acpi_object *obj, tmp; union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp); @@ -314,11 +312,11 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, } if (ret > 0 && ret < ARRAY_SIZE(info)) - str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", - i, ret, info[ret]); + len += sysfs_emit_at(buf, len, "%d %d: %s\n", + i, ret, info[ret]); } - return str - buf; + return len; } static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c index 4280edf427d6..f5ba0f64850b 100644 --- a/drivers/char/tpm/tpm_svsm.c +++ b/drivers/char/tpm/tpm_svsm.c @@ -25,37 +25,32 @@ struct tpm_svsm_priv { void *buffer; }; -static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t cmd_len) { struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); int ret; - ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, cmd_len); if (ret) return ret; /* * The SVSM call uses the same buffer for the command and for the - * response, so after this call, the buffer will contain the response - * that can be used by .recv() op. + * response, so after this call, the buffer will contain the response. + * + * Note: we have to use an internal buffer because the device in SVSM + * expects the svsm_vtpm header + data to be physically contiguous. */ - return snp_svsm_vtpm_send_command(priv->buffer); -} + ret = snp_svsm_vtpm_send_command(priv->buffer); + if (ret) + return ret; -static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) -{ - struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); - - /* - * The internal buffer contains the response after we send the command - * to SVSM. - */ - return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz); } static struct tpm_class_ops tpm_chip_ops = { .flags = TPM_OPS_AUTO_STARTUP, - .recv = tpm_svsm_recv, .send = tpm_svsm_send, }; @@ -84,6 +79,7 @@ static int __init tpm_svsm_probe(struct platform_device *pdev) dev_set_drvdata(&chip->dev, priv); + chip->flags |= TPM_CHIP_FLAG_SYNC; err = tpm2_probe(chip); if (err) return err; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index ed0d3d8449b3..4b12c4b9da8b 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -580,7 +580,8 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) return rc; } -static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { int rc, irq; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index 3b55a7b05c46..fc6891a0b693 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -546,13 +546,15 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len) * tpm_cr50_i2c_tis_send() - TPM transmission callback. * @chip: A TPM chip. * @buf: Buffer to send. - * @len: Buffer length. + * @bufsiz: Buffer size. + * @len: Command length. * * Return: * - 0: Success. * - -errno: A POSIX error code. */ -static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { size_t burstcnt, limit, sent = 0; u8 tpm_go[4] = { TPM_STS_GO }; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 8fe4a01eea12..0818bb517805 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -321,12 +321,14 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, * * @chip: tpm chip to use * @buf: send buffer + * @bufsiz: size of the buffer * @count: bytes to send * * Return: * 0 in case of success, negative error value otherwise. */ -static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 80cca3b83b22..556bf2256716 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -131,7 +131,8 @@ static size_t shr_data_offset(struct vtpm_shared_page *shr) return struct_size(shr, extra_pages, shr->nr_extra_pages); } -static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_private *priv = dev_get_drvdata(&chip->dev); struct vtpm_shared_page *shr = priv->shr; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 19c1ed280fd7..4d56475f94fc 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -61,7 +61,6 @@ config LMK04832 config COMMON_CLK_APPLE_NCO tristate "Clock driver for Apple SoC NCOs" depends on ARCH_APPLE || COMPILE_TEST - default ARCH_APPLE help This driver supports NCO (Numerically Controlled Oscillator) blocks found on Apple SoCs such as t8103 (M1). The blocks are typically @@ -88,6 +87,15 @@ config COMMON_CLK_RK808 These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each. Clkout1 is always on, Clkout2 can off by control register. +config COMMON_CLK_RP1 + tristate "Raspberry Pi RP1-based clock support" + depends on MISC_RP1 || COMPILE_TEST + default MISC_RP1 + help + Enable common clock framework support for Raspberry Pi RP1. + This multi-function device has 3 main PLLs and several clock + generators to drive the internal sub-peripherals. + config COMMON_CLK_HI655X tristate "Clock driver for Hi655x" if EXPERT depends on (MFD_HI655X_PMIC || COMPILE_TEST) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 42867cd37c33..18ed29cfdc11 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -18,6 +18,7 @@ clk-test-y := clk_test.o \ kunit_clk_assigned_rates_without_consumer.dtbo.o \ kunit_clk_assigned_rates_zero.dtbo.o \ kunit_clk_assigned_rates_zero_consumer.dtbo.o \ + kunit_clk_hw_get_dev_of_node.dtbo.o \ kunit_clk_parent_data_test.dtbo.o obj-$(CONFIG_COMMON_CLK) += clk-divider.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o @@ -84,6 +85,7 @@ obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o +obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index cbb8b220f16b..ffab32b047a0 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -61,44 +61,44 @@ static const struct clk_master_layout sam9x7_master_layout = { /* Fractional PLL core output range. */ static const struct clk_range plla_core_outputs[] = { - { .min = 375000000, .max = 1600000000 }, + { .min = 800000000, .max = 1600000000 }, }; static const struct clk_range upll_core_outputs[] = { - { .min = 600000000, .max = 1200000000 }, + { .min = 600000000, .max = 960000000 }, }; static const struct clk_range lvdspll_core_outputs[] = { - { .min = 400000000, .max = 800000000 }, + { .min = 600000000, .max = 1200000000 }, }; static const struct clk_range audiopll_core_outputs[] = { - { .min = 400000000, .max = 800000000 }, + { .min = 600000000, .max = 1200000000 }, }; static const struct clk_range plladiv2_core_outputs[] = { - { .min = 375000000, .max = 1600000000 }, + { .min = 800000000, .max = 1600000000 }, }; /* Fractional PLL output range. */ static const struct clk_range plla_outputs[] = { - { .min = 732421, .max = 800000000 }, + { .min = 400000000, .max = 800000000 }, }; static const struct clk_range upll_outputs[] = { - { .min = 300000000, .max = 600000000 }, + { .min = 300000000, .max = 480000000 }, }; static const struct clk_range lvdspll_outputs[] = { - { .min = 10000000, .max = 800000000 }, + { .min = 175000000, .max = 550000000 }, }; static const struct clk_range audiopll_outputs[] = { - { .min = 10000000, .max = 800000000 }, + { .min = 0, .max = 300000000 }, }; static const struct clk_range plladiv2_outputs[] = { - { .min = 366210, .max = 400000000 }, + { .min = 200000000, .max = 400000000 }, }; /* PLL characteristics. */ diff --git a/drivers/clk/baikal-t1/clk-ccu-div.c b/drivers/clk/baikal-t1/clk-ccu-div.c index 84555a00f950..17d75e8e2e8f 100644 --- a/drivers/clk/baikal-t1/clk-ccu-div.c +++ b/drivers/clk/baikal-t1/clk-ccu-div.c @@ -405,7 +405,7 @@ static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer) { int idx; - /* Uninstall only the clocks registered on the specfied stage */ + /* 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; diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/baikal-t1/clk-ccu-pll.c index fce02ce77347..921b87024feb 100644 --- a/drivers/clk/baikal-t1/clk-ccu-pll.c +++ b/drivers/clk/baikal-t1/clk-ccu-pll.c @@ -196,7 +196,7 @@ static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer) { int idx; - /* Uninstall only the clocks registered on the specfied stage */ + /* 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; diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index fb04734afc80..02215ea79403 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -570,18 +570,23 @@ static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate, return rate >> A2W_PLL_FRAC_BITS; } -static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int bcm2835_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); const struct bcm2835_pll_data *data = pll->data; u32 ndiv, fdiv; - rate = clamp(rate, data->min_rate, data->max_rate); + req->rate = clamp(req->rate, data->min_rate, data->max_rate); - bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv); + bcm2835_pll_choose_ndiv_and_fdiv(req->rate, req->best_parent_rate, + &ndiv, &fdiv); - return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1); + req->rate = bcm2835_pll_rate_from_divisors(req->best_parent_rate, + ndiv, fdiv, + 1); + + return 0; } static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw, @@ -783,7 +788,7 @@ static const struct clk_ops bcm2835_pll_clk_ops = { .unprepare = bcm2835_pll_off, .recalc_rate = bcm2835_pll_get_rate, .set_rate = bcm2835_pll_set_rate, - .round_rate = bcm2835_pll_round_rate, + .determine_rate = bcm2835_pll_determine_rate, .debug_init = bcm2835_pll_debug_init, }; @@ -1550,7 +1555,7 @@ static const char *const bcm2835_clock_osc_parents[] = { .parents = bcm2835_clock_osc_parents, \ __VA_ARGS__) -/* main peripherial parent mux */ +/* main peripheral parent mux */ static const char *const bcm2835_clock_per_parents[] = { "gnd", "xosc", diff --git a/drivers/clk/bcm/clk-bcm53573-ilp.c b/drivers/clk/bcm/clk-bcm53573-ilp.c index 83ef41d618be..b2fc05b60783 100644 --- a/drivers/clk/bcm/clk-bcm53573-ilp.c +++ b/drivers/clk/bcm/clk-bcm53573-ilp.c @@ -59,7 +59,7 @@ static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw, /* * At minimum we should loop for a bit to let hardware do the * measurement. This isn't very accurate however, so for a better - * precision lets try getting 20 different values for and use average. + * precision let's try getting 20 different values and use average. */ while (num < 20) { regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val); diff --git a/drivers/clk/berlin/berlin2-avpll.c b/drivers/clk/berlin/berlin2-avpll.c index aa89b4c9464e..79f3d37a0ee0 100644 --- a/drivers/clk/berlin/berlin2-avpll.c +++ b/drivers/clk/berlin/berlin2-avpll.c @@ -319,7 +319,7 @@ berlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) /* * AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide. - * AV2/AV3 form a fractional divider, where only specfic values for AV3 + * AV2/AV3 form a fractional divider, where only specific values for AV3 * are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass. */ if (ch->index < 6) { diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index 3432c801f1bd..595cfa533fb9 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -92,8 +92,8 @@ static const struct asm9260_div_clk asm9260_div_clks[] __initconst = { { CLKID_SYS_CPU, "cpu_div", "main_gate", HW_CPUCLKDIV }, { CLKID_SYS_AHB, "ahb_div", "cpu_div", HW_SYSAHBCLKDIV }, - /* i2s has two deviders: one for only external mclk and internal - * devider for all clks. */ + /* i2s has two dividers: one for only external mclk and internal + * divider for all clks. */ { CLKID_SYS_I2S0M, "i2s0m_div", "i2s0_mclk", HW_I2S0MCLKDIV }, { CLKID_SYS_I2S1M, "i2s1m_div", "i2s1_mclk", HW_I2S1MCLKDIV }, { CLKID_SYS_I2S0S, "i2s0s_div", "i2s0_gate", HW_I2S0SCLKDIV }, diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c index faf88324f7b1..114afc13d640 100644 --- a/drivers/clk/clk-ast2600.c +++ b/drivers/clk/clk-ast2600.c @@ -92,7 +92,7 @@ static u8 soc_rev; * * There are some gates that do not have an associated reset; these are * handled by using -1 as the index for the reset, and the consumer must - * explictly assert/deassert reset lines as required. + * explicitly assert/deassert reset lines as required. * * Clocks marked with CLK_IS_CRITICAL: * diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 934e53a96ddd..aec62301fa06 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -6,14 +6,17 @@ * Author: Lars-Peter Clausen */ -#include +#include +#include #include #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #define AXI_CLKGEN_V2_REG_RESET 0x40 #define AXI_CLKGEN_V2_REG_CLKSEL 0x44 @@ -28,6 +31,9 @@ #define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) +#define ADI_CLKGEN_REG_FPGA_VOLTAGE 0x0140 +#define ADI_CLKGEN_INFO_FPGA_VOLTAGE(val) ((val) & GENMASK(15, 0)) + #define MMCM_REG_CLKOUT5_2 0x07 #define MMCM_REG_CLKOUT0_1 0x08 #define MMCM_REG_CLKOUT0_2 0x09 @@ -90,7 +96,7 @@ static uint32_t axi_clkgen_lookup_filter(unsigned int m) } } -static const uint32_t axi_clkgen_lock_table[] = { +static const u32 axi_clkgen_lock_table[] = { 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, @@ -102,7 +108,7 @@ static const uint32_t axi_clkgen_lock_table[] = { 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, }; -static uint32_t axi_clkgen_lookup_lock(unsigned int m) +static u32 axi_clkgen_lookup_lock(unsigned int m) { if (m < ARRAY_SIZE(axi_clkgen_lock_table)) return axi_clkgen_lock_table[m]; @@ -118,14 +124,15 @@ static const struct axi_clkgen_limits axi_clkgen_zynqmp_default_limits = { static const struct axi_clkgen_limits axi_clkgen_zynq_default_limits = { .fpfd_min = 10000, - .fpfd_max = 300000, + .fpfd_max = 450000, .fvco_min = 600000, .fvco_max = 1200000, }; static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits, - unsigned long fin, unsigned long fout, - unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) + unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, + unsigned int *best_dout) { unsigned long d, d_min, d_max, _d_min, _d_max; unsigned long m, m_min, m_max; @@ -141,15 +148,15 @@ static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits, *best_m = 0; *best_dout = 0; - d_min = max_t(unsigned long, DIV_ROUND_UP(fin, limits->fpfd_max), 1); - d_max = min_t(unsigned long, fin / limits->fpfd_min, 80); + d_min = max(DIV_ROUND_UP(fin, limits->fpfd_max), 1); + d_max = min(fin / limits->fpfd_min, 80); again: fvco_min_fract = limits->fvco_min << fract_shift; fvco_max_fract = limits->fvco_max << fract_shift; - m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1); - m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift); + m_min = max(DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1); + m_max = min(fvco_max_fract * d_max / fin, 64 << fract_shift); for (m = m_min; m <= m_max; m++) { _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract)); @@ -172,7 +179,7 @@ static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits, } } - /* Lets see if we find a better setting in fractional mode */ + /* Let's see if we find a better setting in fractional mode */ if (fract_shift == 0) { fract_shift = 3; goto again; @@ -192,9 +199,9 @@ struct axi_clkgen_div_params { }; static void axi_clkgen_calc_clk_params(unsigned int divider, - unsigned int frac_divider, struct axi_clkgen_div_params *params) + unsigned int frac_divider, + struct axi_clkgen_div_params *params) { - memset(params, 0x0, sizeof(*params)); if (divider == 1) { @@ -222,7 +229,7 @@ static void axi_clkgen_calc_clk_params(unsigned int divider, if (params->edge == 0 || frac_divider == 1) params->low--; if (((params->edge == 0) ^ (frac_divider == 1)) || - (divider == 2 && frac_divider == 1)) + (divider == 2 && frac_divider == 1)) params->frac_wf_f = 1; params->frac_phase = params->edge * 4 + frac_divider / 2; @@ -230,13 +237,13 @@ static void axi_clkgen_calc_clk_params(unsigned int divider, } static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int val) + unsigned int reg, unsigned int val) { writel(val, axi_clkgen->base + reg); } static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int *val) + unsigned int reg, unsigned int *val) { *val = readl(axi_clkgen->base + reg); } @@ -257,7 +264,7 @@ static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) } static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int *val) + unsigned int reg, unsigned int *val) { unsigned int reg_val; int ret; @@ -281,7 +288,8 @@ static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, } static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int val, unsigned int mask) + unsigned int reg, unsigned int val, + unsigned int mask) { unsigned int reg_val = 0; int ret; @@ -302,8 +310,7 @@ static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, return 0; } -static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, - bool enable) +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, bool enable) { unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; @@ -319,31 +326,31 @@ static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) } static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen, - unsigned int reg1, unsigned int reg2, unsigned int reg3, - struct axi_clkgen_div_params *params) + unsigned int reg1, unsigned int reg2, + unsigned int reg3, + struct axi_clkgen_div_params *params) { axi_clkgen_mmcm_write(axi_clkgen, reg1, - (params->high << 6) | params->low, 0xefff); + (params->high << 6) | params->low, 0xefff); axi_clkgen_mmcm_write(axi_clkgen, reg2, - (params->frac << 12) | (params->frac_en << 11) | - (params->frac_wf_r << 10) | (params->edge << 7) | - (params->nocount << 6), 0x7fff); + (params->frac << 12) | (params->frac_en << 11) | + (params->frac_wf_r << 10) | (params->edge << 7) | + (params->nocount << 6), 0x7fff); if (reg3 != 0) { axi_clkgen_mmcm_write(axi_clkgen, reg3, - (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00); + (params->frac_phase << 11) | (params->frac_wf_f << 10), + 0x3c00); } } -static int axi_clkgen_set_rate(struct clk_hw *clk_hw, - unsigned long rate, unsigned long parent_rate) +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, unsigned long rate, + unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); const struct axi_clkgen_limits *limits = &axi_clkgen->limits; unsigned int d, m, dout; struct axi_clkgen_div_params params; - uint32_t power = 0; - uint32_t filter; - uint32_t lock; + u32 power = 0, filter, lock; if (parent_rate == 0 || rate == 0) return -EINVAL; @@ -363,22 +370,22 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, ¶ms); axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2, - MMCM_REG_CLKOUT5_2, ¶ms); + MMCM_REG_CLKOUT5_2, ¶ms); axi_clkgen_calc_clk_params(d, 0, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, - (params.edge << 13) | (params.nocount << 12) | - (params.high << 6) | params.low, 0x3fff); + (params.edge << 13) | (params.nocount << 12) | + (params.high << 6) | params.low, 0x3fff); axi_clkgen_calc_clk_params(m >> 3, m & 0x7, ¶ms); axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2, - MMCM_REG_CLKOUT6_2, ¶ms); + MMCM_REG_CLKOUT6_2, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, - (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3, - (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900); @@ -407,7 +414,7 @@ static int axi_clkgen_determine_rate(struct clk_hw *hw, } static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen, - unsigned int reg1, unsigned int reg2) + unsigned int reg1, unsigned int reg2) { unsigned int val1, val2; unsigned int div; @@ -434,7 +441,7 @@ static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen, } static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); unsigned int d, m, dout; @@ -442,9 +449,9 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned int val; dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1, - MMCM_REG_CLKOUT0_2); + MMCM_REG_CLKOUT0_2); m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1, - MMCM_REG_CLK_FB2); + MMCM_REG_CLK_FB2); axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val); if (val & MMCM_CLK_DIV_NOCOUNT) @@ -496,6 +503,54 @@ static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw) return parent; } +static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, + struct device *dev) +{ + unsigned int tech, family, speed_grade, reg_value; + + axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, ®_value); + tech = ADI_AXI_INFO_FPGA_TECH(reg_value); + family = ADI_AXI_INFO_FPGA_FAMILY(reg_value); + speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value); + + axi_clkgen->limits.fpfd_min = 10000; + axi_clkgen->limits.fvco_min = 600000; + + switch (speed_grade) { + case ADI_AXI_FPGA_SPEED_1 ... ADI_AXI_FPGA_SPEED_1LV: + axi_clkgen->limits.fvco_max = 1200000; + axi_clkgen->limits.fpfd_max = 450000; + break; + case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2LV: + axi_clkgen->limits.fvco_max = 1440000; + axi_clkgen->limits.fpfd_max = 500000; + if (family == ADI_AXI_FPGA_FAMILY_KINTEX || family == ADI_AXI_FPGA_FAMILY_ARTIX) { + axi_clkgen_read(axi_clkgen, ADI_CLKGEN_REG_FPGA_VOLTAGE, + ®_value); + if (ADI_CLKGEN_INFO_FPGA_VOLTAGE(reg_value) < 950) { + axi_clkgen->limits.fvco_max = 1200000; + axi_clkgen->limits.fpfd_max = 450000; + } + } + break; + case ADI_AXI_FPGA_SPEED_3: + axi_clkgen->limits.fvco_max = 1600000; + axi_clkgen->limits.fpfd_max = 550000; + break; + default: + return dev_err_probe(dev, -ENODEV, "Unknown speed grade %d\n", + speed_grade); + }; + + /* Overwrite vco limits for ultrascale+ */ + if (tech == ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS) { + axi_clkgen->limits.fvco_max = 1600000; + axi_clkgen->limits.fvco_min = 800000; + } + + return 0; +} + static const struct clk_ops axi_clkgen_ops = { .recalc_rate = axi_clkgen_recalc_rate, .determine_rate = axi_clkgen_determine_rate, @@ -510,6 +565,7 @@ static int axi_clkgen_probe(struct platform_device *pdev) { const struct axi_clkgen_limits *dflt_limits; struct axi_clkgen *axi_clkgen; + unsigned int pcore_version; struct clk_init_data init; const char *parent_names[2]; const char *clk_name; @@ -555,11 +611,20 @@ static int axi_clkgen_probe(struct platform_device *pdev) return -EINVAL; } - memcpy(&axi_clkgen->limits, dflt_limits, sizeof(axi_clkgen->limits)); + axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION, &pcore_version); + + if (ADI_AXI_PCORE_VER_MAJOR(pcore_version) > 0x04) { + ret = axi_clkgen_setup_limits(axi_clkgen, &pdev->dev); + if (ret) + return ret; + } else { + memcpy(&axi_clkgen->limits, dflt_limits, + sizeof(axi_clkgen->limits)); + } clk_name = pdev->dev.of_node->name; of_property_read_string(pdev->dev.of_node, "clock-output-names", - &clk_name); + &clk_name); init.name = clk_name; init.ops = &axi_clkgen_ops; diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index f8417ee2961a..402ab74d9bfb 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -99,7 +99,7 @@ static void __init clps711x_clk_init_dt(struct device_node *np) */ tmp &= ~(SYSCON1_TC1M | SYSCON1_TC1S); /* Timer2 in prescale mode. - * Value writen is automatically re-loaded when + * Value written is automatically re-loaded when * the counter underflows. */ tmp |= SYSCON1_TC2M | SYSCON1_TC2S; diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c index 640c25788487..ea1c3d78e7cd 100644 --- a/drivers/clk/clk-eyeq.c +++ b/drivers/clk/clk-eyeq.c @@ -131,7 +131,7 @@ struct eqc_early_match_data { * Both factors (mult and div) must fit in 32 bits. When an operation overflows, * this function throws away low bits so that factors still fit in 32 bits. * - * Precision loss depends on amplitude of mult and div. Worst theorical + * Precision loss depends on amplitude of mult and div. Worst theoretical * loss is: (UINT_MAX+1) / UINT_MAX - 1 = 2.3e-10. * This is 1Hz every 4.3GHz. */ diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 68e585a02fd9..4746f8219132 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -15,7 +15,7 @@ #include /** - * DOC: basic gatable clock which can gate and ungate its output + * DOC: basic gateable clock which can gate and ungate its output * * Traits of this clock: * prepare - clk_(un)prepare only ensures parent is (un)prepared diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c index 5d2a90addf1a..921523fc26f2 100644 --- a/drivers/clk/clk-hsdk-pll.c +++ b/drivers/clk/clk-hsdk-pll.c @@ -265,7 +265,7 @@ static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *clk, return -EINVAL; /* - * Program divider to div-by-1 if we succesfuly set core clock below + * Program divider to div-by-1 if we successfully set core clock below * 500MHz threshold. */ if (rate <= CORE_IF_CLK_THRESHOLD_HZ) diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c index bd4f21c22004..4709f0338e37 100644 --- a/drivers/clk/clk-pwm.c +++ b/drivers/clk/clk-pwm.c @@ -14,6 +14,7 @@ struct clk_pwm { struct clk_hw hw; struct pwm_device *pwm; + struct pwm_state state; u32 fixed_rate; }; @@ -22,11 +23,28 @@ static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw) return container_of(hw, struct clk_pwm, hw); } +static int clk_pwm_enable(struct clk_hw *hw) +{ + struct clk_pwm *clk_pwm = to_clk_pwm(hw); + + return pwm_apply_atomic(clk_pwm->pwm, &clk_pwm->state); +} + +static void clk_pwm_disable(struct clk_hw *hw) +{ + struct clk_pwm *clk_pwm = to_clk_pwm(hw); + struct pwm_state state = clk_pwm->state; + + state.enabled = false; + + pwm_apply_atomic(clk_pwm->pwm, &state); +} + static int clk_pwm_prepare(struct clk_hw *hw) { struct clk_pwm *clk_pwm = to_clk_pwm(hw); - return pwm_enable(clk_pwm->pwm); + return pwm_apply_might_sleep(clk_pwm->pwm, &clk_pwm->state); } static void clk_pwm_unprepare(struct clk_hw *hw) @@ -48,8 +66,11 @@ static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) { struct clk_pwm *clk_pwm = to_clk_pwm(hw); struct pwm_state state; + int ret; - pwm_get_state(clk_pwm->pwm, &state); + ret = pwm_get_state_hw(clk_pwm->pwm, &state); + if (ret) + return ret; duty->num = state.duty_cycle; duty->den = state.period; @@ -57,6 +78,13 @@ static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) return 0; } +static const struct clk_ops clk_pwm_ops_atomic = { + .enable = clk_pwm_enable, + .disable = clk_pwm_disable, + .recalc_rate = clk_pwm_recalc_rate, + .get_duty_cycle = clk_pwm_get_duty_cycle, +}; + static const struct clk_ops clk_pwm_ops = { .prepare = clk_pwm_prepare, .unprepare = clk_pwm_unprepare, @@ -103,20 +131,19 @@ static int clk_pwm_probe(struct platform_device *pdev) return -EINVAL; } - /* - * FIXME: pwm_apply_args() should be removed when switching to the - * atomic PWM API. - */ - pwm_apply_args(pwm); - ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period); - if (ret < 0) - return ret; + pwm_init_state(pwm, &clk_pwm->state); + pwm_set_relative_duty_cycle(&clk_pwm->state, 1, 2); + clk_pwm->state.enabled = true; clk_name = node->name; of_property_read_string(node, "clock-output-names", &clk_name); init.name = clk_name; - init.ops = &clk_pwm_ops; + if (pwm_might_sleep(pwm)) + init.ops = &clk_pwm_ops; + else + init.ops = &clk_pwm_ops_atomic; + init.flags = 0; init.num_parents = 0; diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c new file mode 100644 index 000000000000..afff90d48734 --- /dev/null +++ b/drivers/clk/clk-rp1.c @@ -0,0 +1,1494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Raspberry Pi Ltd. + * + * Clock driver for RP1 PCIe multifunction chip. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PLL_SYS_OFFSET 0x08000 +#define PLL_SYS_CS (PLL_SYS_OFFSET + 0x00) +#define PLL_SYS_PWR (PLL_SYS_OFFSET + 0x04) +#define PLL_SYS_FBDIV_INT (PLL_SYS_OFFSET + 0x08) +#define PLL_SYS_FBDIV_FRAC (PLL_SYS_OFFSET + 0x0c) +#define PLL_SYS_PRIM (PLL_SYS_OFFSET + 0x10) +#define PLL_SYS_SEC (PLL_SYS_OFFSET + 0x14) + +#define PLL_AUDIO_OFFSET 0x0c000 +#define PLL_AUDIO_CS (PLL_AUDIO_OFFSET + 0x00) +#define PLL_AUDIO_PWR (PLL_AUDIO_OFFSET + 0x04) +#define PLL_AUDIO_FBDIV_INT (PLL_AUDIO_OFFSET + 0x08) +#define PLL_AUDIO_FBDIV_FRAC (PLL_AUDIO_OFFSET + 0x0c) +#define PLL_AUDIO_PRIM (PLL_AUDIO_OFFSET + 0x10) +#define PLL_AUDIO_SEC (PLL_AUDIO_OFFSET + 0x14) +#define PLL_AUDIO_TERN (PLL_AUDIO_OFFSET + 0x18) + +#define PLL_VIDEO_OFFSET 0x10000 +#define PLL_VIDEO_CS (PLL_VIDEO_OFFSET + 0x00) +#define PLL_VIDEO_PWR (PLL_VIDEO_OFFSET + 0x04) +#define PLL_VIDEO_FBDIV_INT (PLL_VIDEO_OFFSET + 0x08) +#define PLL_VIDEO_FBDIV_FRAC (PLL_VIDEO_OFFSET + 0x0c) +#define PLL_VIDEO_PRIM (PLL_VIDEO_OFFSET + 0x10) +#define PLL_VIDEO_SEC (PLL_VIDEO_OFFSET + 0x14) + +#define GPCLK_OE_CTRL 0x00000 + +#define CLK_SYS_OFFSET 0x00014 +#define CLK_SYS_CTRL (CLK_SYS_OFFSET + 0x00) +#define CLK_SYS_DIV_INT (CLK_SYS_OFFSET + 0x04) +#define CLK_SYS_SEL (CLK_SYS_OFFSET + 0x0c) + +#define CLK_SLOW_OFFSET 0x00024 +#define CLK_SLOW_SYS_CTRL (CLK_SLOW_OFFSET + 0x00) +#define CLK_SLOW_SYS_DIV_INT (CLK_SLOW_OFFSET + 0x04) +#define CLK_SLOW_SYS_SEL (CLK_SLOW_OFFSET + 0x0c) + +#define CLK_DMA_OFFSET 0x00044 +#define CLK_DMA_CTRL (CLK_DMA_OFFSET + 0x00) +#define CLK_DMA_DIV_INT (CLK_DMA_OFFSET + 0x04) +#define CLK_DMA_SEL (CLK_DMA_OFFSET + 0x0c) + +#define CLK_UART_OFFSET 0x00054 +#define CLK_UART_CTRL (CLK_UART_OFFSET + 0x00) +#define CLK_UART_DIV_INT (CLK_UART_OFFSET + 0x04) +#define CLK_UART_SEL (CLK_UART_OFFSET + 0x0c) + +#define CLK_ETH_OFFSET 0x00064 +#define CLK_ETH_CTRL (CLK_ETH_OFFSET + 0x00) +#define CLK_ETH_DIV_INT (CLK_ETH_OFFSET + 0x04) +#define CLK_ETH_SEL (CLK_ETH_OFFSET + 0x0c) + +#define CLK_PWM0_OFFSET 0x00074 +#define CLK_PWM0_CTRL (CLK_PWM0_OFFSET + 0x00) +#define CLK_PWM0_DIV_INT (CLK_PWM0_OFFSET + 0x04) +#define CLK_PWM0_DIV_FRAC (CLK_PWM0_OFFSET + 0x08) +#define CLK_PWM0_SEL (CLK_PWM0_OFFSET + 0x0c) + +#define CLK_PWM1_OFFSET 0x00084 +#define CLK_PWM1_CTRL (CLK_PWM1_OFFSET + 0x00) +#define CLK_PWM1_DIV_INT (CLK_PWM1_OFFSET + 0x04) +#define CLK_PWM1_DIV_FRAC (CLK_PWM1_OFFSET + 0x08) +#define CLK_PWM1_SEL (CLK_PWM1_OFFSET + 0x0c) + +#define CLK_AUDIO_IN_OFFSET 0x00094 +#define CLK_AUDIO_IN_CTRL (CLK_AUDIO_IN_OFFSET + 0x00) +#define CLK_AUDIO_IN_DIV_INT (CLK_AUDIO_IN_OFFSET + 0x04) +#define CLK_AUDIO_IN_SEL (CLK_AUDIO_IN_OFFSET + 0x0c) + +#define CLK_AUDIO_OUT_OFFSET 0x000a4 +#define CLK_AUDIO_OUT_CTRL (CLK_AUDIO_OUT_OFFSET + 0x00) +#define CLK_AUDIO_OUT_DIV_INT (CLK_AUDIO_OUT_OFFSET + 0x04) +#define CLK_AUDIO_OUT_SEL (CLK_AUDIO_OUT_OFFSET + 0x0c) + +#define CLK_I2S_OFFSET 0x000b4 +#define CLK_I2S_CTRL (CLK_I2S_OFFSET + 0x00) +#define CLK_I2S_DIV_INT (CLK_I2S_OFFSET + 0x04) +#define CLK_I2S_SEL (CLK_I2S_OFFSET + 0x0c) + +#define CLK_MIPI0_CFG_OFFSET 0x000c4 +#define CLK_MIPI0_CFG_CTRL (CLK_MIPI0_CFG_OFFSET + 0x00) +#define CLK_MIPI0_CFG_DIV_INT (CLK_MIPI0_CFG_OFFSET + 0x04) +#define CLK_MIPI0_CFG_SEL (CLK_MIPI0_CFG_OFFSET + 0x0c) + +#define CLK_MIPI1_CFG_OFFSET 0x000d4 +#define CLK_MIPI1_CFG_CTRL (CLK_MIPI1_CFG_OFFSET + 0x00) +#define CLK_MIPI1_CFG_DIV_INT (CLK_MIPI1_CFG_OFFSET + 0x04) +#define CLK_MIPI1_CFG_SEL (CLK_MIPI1_CFG_OFFSET + 0x0c) + +#define CLK_PCIE_AUX_OFFSET 0x000e4 +#define CLK_PCIE_AUX_CTRL (CLK_PCIE_AUX_OFFSET + 0x00) +#define CLK_PCIE_AUX_DIV_INT (CLK_PCIE_AUX_OFFSET + 0x04) +#define CLK_PCIE_AUX_SEL (CLK_PCIE_AUX_OFFSET + 0x0c) + +#define CLK_USBH0_MICROFRAME_OFFSET 0x000f4 +#define CLK_USBH0_MICROFRAME_CTRL (CLK_USBH0_MICROFRAME_OFFSET + 0x00) +#define CLK_USBH0_MICROFRAME_DIV_INT (CLK_USBH0_MICROFRAME_OFFSET + 0x04) +#define CLK_USBH0_MICROFRAME_SEL (CLK_USBH0_MICROFRAME_OFFSET + 0x0c) + +#define CLK_USBH1_MICROFRAME_OFFSET 0x00104 +#define CLK_USBH1_MICROFRAME_CTRL (CLK_USBH1_MICROFRAME_OFFSET + 0x00) +#define CLK_USBH1_MICROFRAME_DIV_INT (CLK_USBH1_MICROFRAME_OFFSET + 0x04) +#define CLK_USBH1_MICROFRAME_SEL (CLK_USBH1_MICROFRAME_OFFSET + 0x0c) + +#define CLK_USBH0_SUSPEND_OFFSET 0x00114 +#define CLK_USBH0_SUSPEND_CTRL (CLK_USBH0_SUSPEND_OFFSET + 0x00) +#define CLK_USBH0_SUSPEND_DIV_INT (CLK_USBH0_SUSPEND_OFFSET + 0x04) +#define CLK_USBH0_SUSPEND_SEL (CLK_USBH0_SUSPEND_OFFSET + 0x0c) + +#define CLK_USBH1_SUSPEND_OFFSET 0x00124 +#define CLK_USBH1_SUSPEND_CTRL (CLK_USBH1_SUSPEND_OFFSET + 0x00) +#define CLK_USBH1_SUSPEND_DIV_INT (CLK_USBH1_SUSPEND_OFFSET + 0x04) +#define CLK_USBH1_SUSPEND_SEL (CLK_USBH1_SUSPEND_OFFSET + 0x0c) + +#define CLK_ETH_TSU_OFFSET 0x00134 +#define CLK_ETH_TSU_CTRL (CLK_ETH_TSU_OFFSET + 0x00) +#define CLK_ETH_TSU_DIV_INT (CLK_ETH_TSU_OFFSET + 0x04) +#define CLK_ETH_TSU_SEL (CLK_ETH_TSU_OFFSET + 0x0c) + +#define CLK_ADC_OFFSET 0x00144 +#define CLK_ADC_CTRL (CLK_ADC_OFFSET + 0x00) +#define CLK_ADC_DIV_INT (CLK_ADC_OFFSET + 0x04) +#define CLK_ADC_SEL (CLK_ADC_OFFSET + 0x0c) + +#define CLK_SDIO_TIMER_OFFSET 0x00154 +#define CLK_SDIO_TIMER_CTRL (CLK_SDIO_TIMER_OFFSET + 0x00) +#define CLK_SDIO_TIMER_DIV_INT (CLK_SDIO_TIMER_OFFSET + 0x04) +#define CLK_SDIO_TIMER_SEL (CLK_SDIO_TIMER_OFFSET + 0x0c) + +#define CLK_SDIO_ALT_SRC_OFFSET 0x00164 +#define CLK_SDIO_ALT_SRC_CTRL (CLK_SDIO_ALT_SRC_OFFSET + 0x00) +#define CLK_SDIO_ALT_SRC_DIV_INT (CLK_SDIO_ALT_SRC_OFFSET + 0x04) +#define CLK_SDIO_ALT_SRC_SEL (CLK_SDIO_ALT_SRC_OFFSET + 0x0c) + +#define CLK_GP0_OFFSET 0x00174 +#define CLK_GP0_CTRL (CLK_GP0_OFFSET + 0x00) +#define CLK_GP0_DIV_INT (CLK_GP0_OFFSET + 0x04) +#define CLK_GP0_DIV_FRAC (CLK_GP0_OFFSET + 0x08) +#define CLK_GP0_SEL (CLK_GP0_OFFSET + 0x0c) + +#define CLK_GP1_OFFSET 0x00184 +#define CLK_GP1_CTRL (CLK_GP1_OFFSET + 0x00) +#define CLK_GP1_DIV_INT (CLK_GP1_OFFSET + 0x04) +#define CLK_GP1_DIV_FRAC (CLK_GP1_OFFSET + 0x08) +#define CLK_GP1_SEL (CLK_GP1_OFFSET + 0x0c) + +#define CLK_GP2_OFFSET 0x00194 +#define CLK_GP2_CTRL (CLK_GP2_OFFSET + 0x00) +#define CLK_GP2_DIV_INT (CLK_GP2_OFFSET + 0x04) +#define CLK_GP2_DIV_FRAC (CLK_GP2_OFFSET + 0x08) +#define CLK_GP2_SEL (CLK_GP2_OFFSET + 0x0c) + +#define CLK_GP3_OFFSET 0x001a4 +#define CLK_GP3_CTRL (CLK_GP3_OFFSET + 0x00) +#define CLK_GP3_DIV_INT (CLK_GP3_OFFSET + 0x04) +#define CLK_GP3_DIV_FRAC (CLK_GP3_OFFSET + 0x08) +#define CLK_GP3_SEL (CLK_GP3_OFFSET + 0x0c) + +#define CLK_GP4_OFFSET 0x001b4 +#define CLK_GP4_CTRL (CLK_GP4_OFFSET + 0x00) +#define CLK_GP4_DIV_INT (CLK_GP4_OFFSET + 0x04) +#define CLK_GP4_DIV_FRAC (CLK_GP4_OFFSET + 0x08) +#define CLK_GP4_SEL (CLK_GP4_OFFSET + 0x0c) + +#define CLK_GP5_OFFSET 0x001c4 +#define CLK_GP5_CTRL (CLK_GP5_OFFSET + 0x00) +#define CLK_GP5_DIV_INT (CLK_GP5_OFFSET + 0x04) +#define CLK_GP5_DIV_FRAC (CLK_GP5_OFFSET + 0x08) +#define CLK_GP5_SEL (CLK_GP5_OFFSET + 0x0c) + +#define CLK_SYS_RESUS_CTRL 0x0020c + +#define CLK_SLOW_SYS_RESUS_CTRL 0x00214 + +#define FC0_OFFSET 0x0021c +#define FC0_REF_KHZ (FC0_OFFSET + 0x00) +#define FC0_MIN_KHZ (FC0_OFFSET + 0x04) +#define FC0_MAX_KHZ (FC0_OFFSET + 0x08) +#define FC0_DELAY (FC0_OFFSET + 0x0c) +#define FC0_INTERVAL (FC0_OFFSET + 0x10) +#define FC0_SRC (FC0_OFFSET + 0x14) +#define FC0_STATUS (FC0_OFFSET + 0x18) +#define FC0_RESULT (FC0_OFFSET + 0x1c) +#define FC_SIZE 0x20 +#define FC_COUNT 8 +#define FC_NUM(idx, off) ((idx) * 32 + (off)) + +#define AUX_SEL 1 + +#define VIDEO_CLOCKS_OFFSET 0x4000 +#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000) +#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004) +#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c) +#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010) +#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014) +#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c) +#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020) +#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024) +#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028) +#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c) +#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030) +#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034) +#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038) +#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c) + +#define DIV_INT_8BIT_MAX GENMASK(7, 0) /* max divide for most clocks */ +#define DIV_INT_16BIT_MAX GENMASK(15, 0) /* max divide for GPx, PWM */ +#define DIV_INT_24BIT_MAX GENMASK(23, 0) /* max divide for CLK_SYS */ + +#define FC0_STATUS_DONE BIT(4) +#define FC0_STATUS_RUNNING BIT(8) +#define FC0_RESULT_FRAC_SHIFT 5 + +#define PLL_PRIM_DIV1_MASK GENMASK(18, 16) +#define PLL_PRIM_DIV2_MASK GENMASK(14, 12) + +#define PLL_SEC_DIV_MASK GENMASK(12, 8) + +#define PLL_CS_LOCK BIT(31) +#define PLL_CS_REFDIV_MASK BIT(1) + +#define PLL_PWR_PD BIT(0) +#define PLL_PWR_DACPD BIT(1) +#define PLL_PWR_DSMPD BIT(2) +#define PLL_PWR_POSTDIVPD BIT(3) +#define PLL_PWR_4PHASEPD BIT(4) +#define PLL_PWR_VCOPD BIT(5) +#define PLL_PWR_MASK GENMASK(5, 0) + +#define PLL_SEC_RST BIT(16) +#define PLL_SEC_IMPL BIT(31) + +/* PLL phase output for both PRI and SEC */ +#define PLL_PH_EN BIT(4) +#define PLL_PH_PHASE_SHIFT 0 + +#define RP1_PLL_PHASE_0 0 +#define RP1_PLL_PHASE_90 1 +#define RP1_PLL_PHASE_180 2 +#define RP1_PLL_PHASE_270 3 + +/* Clock fields for all clocks */ +#define CLK_CTRL_ENABLE BIT(11) +#define CLK_CTRL_AUXSRC_MASK GENMASK(9, 5) +#define CLK_CTRL_SRC_SHIFT 0 +#define CLK_DIV_FRAC_BITS 16 + +#define LOCK_TIMEOUT_US 100000 +#define LOCK_POLL_DELAY_US 5 + +#define MAX_CLK_PARENTS 16 + +#define PLL_DIV_INVALID 19 +/* + * Secondary PLL channel output divider table. + * Divider values range from 8 to 19, where + * 19 means invalid. + */ +static const struct clk_div_table pll_sec_div_table[] = { + { 0x00, PLL_DIV_INVALID }, + { 0x01, PLL_DIV_INVALID }, + { 0x02, PLL_DIV_INVALID }, + { 0x03, PLL_DIV_INVALID }, + { 0x04, PLL_DIV_INVALID }, + { 0x05, PLL_DIV_INVALID }, + { 0x06, PLL_DIV_INVALID }, + { 0x07, PLL_DIV_INVALID }, + { 0x08, 8 }, + { 0x09, 9 }, + { 0x0a, 10 }, + { 0x0b, 11 }, + { 0x0c, 12 }, + { 0x0d, 13 }, + { 0x0e, 14 }, + { 0x0f, 15 }, + { 0x10, 16 }, + { 0x11, 17 }, + { 0x12, 18 }, + { 0x13, PLL_DIV_INVALID }, + { 0x14, PLL_DIV_INVALID }, + { 0x15, PLL_DIV_INVALID }, + { 0x16, PLL_DIV_INVALID }, + { 0x17, PLL_DIV_INVALID }, + { 0x18, PLL_DIV_INVALID }, + { 0x19, PLL_DIV_INVALID }, + { 0x1a, PLL_DIV_INVALID }, + { 0x1b, PLL_DIV_INVALID }, + { 0x1c, PLL_DIV_INVALID }, + { 0x1d, PLL_DIV_INVALID }, + { 0x1e, PLL_DIV_INVALID }, + { 0x1f, PLL_DIV_INVALID }, + { 0 } +}; + +struct rp1_clockman { + struct device *dev; + void __iomem *regs; + struct regmap *regmap; + spinlock_t regs_lock; /* spinlock for all clocks */ + + /* Must be last */ + struct clk_hw_onecell_data onecell; +}; + +struct rp1_pll_core_data { + u32 cs_reg; + u32 pwr_reg; + u32 fbdiv_int_reg; + u32 fbdiv_frac_reg; + u32 fc0_src; +}; + +struct rp1_pll_data { + u32 ctrl_reg; + u32 fc0_src; +}; + +struct rp1_pll_ph_data { + unsigned int phase; + unsigned int fixed_divider; + u32 ph_reg; + u32 fc0_src; +}; + +struct rp1_pll_divider_data { + u32 sec_reg; + u32 fc0_src; +}; + +struct rp1_clock_data { + int num_std_parents; + int num_aux_parents; + u32 oe_mask; + u32 clk_src_mask; + u32 ctrl_reg; + u32 div_int_reg; + u32 div_frac_reg; + u32 sel_reg; + u32 div_int_max; + unsigned long max_freq; + u32 fc0_src; +}; + +struct rp1_clk_desc { + struct clk_hw *(*clk_register)(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc); + const void *data; + struct clk_hw hw; + struct rp1_clockman *clockman; + unsigned long cached_rate; + struct clk_divider div; +}; + +static inline +void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val) +{ + regmap_write(clockman->regmap, reg, val); +} + +static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg) +{ + u32 val; + + regmap_read(clockman->regmap, reg, &val); + + return val; +} + +static int rp1_pll_core_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 pwr = clockman_read(clockman, data->pwr_reg); + + return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD); +} + +static int rp1_pll_core_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_frac, val; + int ret; + + spin_lock(&clockman->regs_lock); + + if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) { + /* Reset to a known state. */ + clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK); + clockman_write(clockman, data->fbdiv_int_reg, 20); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + clockman_write(clockman, data->cs_reg, PLL_CS_REFDIV_MASK); + } + + /* Come out of reset. */ + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + spin_unlock(&clockman->regs_lock); + + /* Wait for the PLL to lock. */ + ret = regmap_read_poll_timeout(clockman->regmap, data->cs_reg, val, + val & PLL_CS_LOCK, + LOCK_POLL_DELAY_US, LOCK_TIMEOUT_US); + if (ret) + dev_err(clockman->dev, "%s: can't lock PLL\n", + clk_hw_get_name(hw)); + + return ret; +} + +static void rp1_pll_core_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, 0); + spin_unlock(&clockman->regs_lock); +} + +static inline unsigned long get_pll_core_divider(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u32 *div_int, u32 *div_frac) +{ + u32 fbdiv_int, fbdiv_frac; + unsigned long calc_rate; + u64 shifted_fbdiv_int; + u64 div_fp64; /* 32.32 fixed point fraction. */ + + /* Factor of reference clock to VCO frequency. */ + div_fp64 = (u64)(rate) << 32; + div_fp64 = DIV_ROUND_CLOSEST_ULL(div_fp64, parent_rate); + + /* Round the fractional component at 24 bits. */ + div_fp64 += 1 << (32 - 24 - 1); + + fbdiv_int = div_fp64 >> 32; + fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff; + + shifted_fbdiv_int = (u64)fbdiv_int << 24; + calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac); + calc_rate += BIT(23); + calc_rate >>= 24; + + *div_int = fbdiv_int; + *div_frac = fbdiv_frac; + + return calc_rate; +} + +static int rp1_pll_core_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + unsigned long calc_rate; + u32 fbdiv_int, fbdiv_frac; + + /* Disable dividers to start with. */ + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->fbdiv_int_reg, 0); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + spin_unlock(&clockman->regs_lock); + + calc_rate = get_pll_core_divider(hw, rate, parent_rate, + &fbdiv_int, &fbdiv_frac); + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int); + clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac); + spin_unlock(&clockman->regs_lock); + + /* Check that reference frequency is no greater than VCO / 16. */ + if (WARN_ON_ONCE(parent_rate > (rate / 16))) + return -ERANGE; + + pll_core->cached_rate = calc_rate; + + spin_lock(&clockman->regs_lock); + /* Don't need to divide ref unless parent_rate > (output freq / 16) */ + clockman_write(clockman, data->cs_reg, + clockman_read(clockman, data->cs_reg) | + PLL_CS_REFDIV_MASK); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_int, fbdiv_frac; + unsigned long calc_rate; + u64 shifted_fbdiv_int; + + fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg); + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + + shifted_fbdiv_int = (u64)fbdiv_int << 24; + calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac); + calc_rate += BIT(23); + calc_rate >>= 24; + + return calc_rate; +} + +static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 fbdiv_int, fbdiv_frac; + + return get_pll_core_divider(hw, rate, *parent_rate, + &fbdiv_int, &fbdiv_frac); +} + +static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate, + u32 *divider1, u32 *divider2) +{ + unsigned int div1, div2; + unsigned int best_div1 = 7, best_div2 = 7; + unsigned long best_rate_diff = + abs_diff(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate); + unsigned long rate_diff, calc_rate; + + for (div1 = 1; div1 <= 7; div1++) { + for (div2 = 1; div2 <= div1; div2++) { + calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2); + rate_diff = abs_diff(calc_rate, rate); + + if (calc_rate == rate) { + best_div1 = div1; + best_div2 = div2; + goto done; + } else if (rate_diff < best_rate_diff) { + best_div1 = div1; + best_div2 = div2; + best_rate_diff = rate_diff; + } + } + } + +done: + *divider1 = best_div1; + *divider2 = best_div2; +} + +static int rp1_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + + u32 prim, prim_div1, prim_div2; + + get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2); + + spin_lock(&clockman->regs_lock); + prim = clockman_read(clockman, data->ctrl_reg); + prim &= ~PLL_PRIM_DIV1_MASK; + prim |= FIELD_PREP(PLL_PRIM_DIV1_MASK, prim_div1); + prim &= ~PLL_PRIM_DIV2_MASK; + prim |= FIELD_PREP(PLL_PRIM_DIV2_MASK, prim_div2); + clockman_write(clockman, data->ctrl_reg, prim); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + u32 prim, prim_div1, prim_div2; + + prim = clockman_read(clockman, data->ctrl_reg); + prim_div1 = FIELD_GET(PLL_PRIM_DIV1_MASK, prim); + prim_div2 = FIELD_GET(PLL_PRIM_DIV2_MASK, prim); + + if (!prim_div1 || !prim_div2) { + dev_err(clockman->dev, "%s: (%s) zero divider value\n", + __func__, clk_hw_get_name(hw)); + return 0; + } + + return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2); +} + +static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 div1, div2; + + get_pll_prim_dividers(rate, *parent_rate, &div1, &div2); + + return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2); +} + +static int rp1_pll_ph_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + + return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN); +} + +static int rp1_pll_ph_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + u32 ph_reg; + + spin_lock(&clockman->regs_lock); + ph_reg = clockman_read(clockman, data->ph_reg); + ph_reg |= data->phase << PLL_PH_PHASE_SHIFT; + ph_reg |= PLL_PH_EN; + clockman_write(clockman, data->ph_reg, ph_reg); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_pll_ph_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ph_reg, + clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN); + spin_unlock(&clockman->regs_lock); +} + +static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return parent_rate / data->fixed_divider; +} + +static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return *parent_rate / data->fixed_divider; +} + +static int rp1_pll_divider_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST); +} + +static int rp1_pll_divider_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + /* Check the implementation bit is set! */ + WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL)); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_pll_divider_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) | PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); +} + +static int rp1_pll_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + u32 div, sec; + + div = DIV_ROUND_UP_ULL(parent_rate, rate); + div = clamp(div, 8u, 19u); + + spin_lock(&clockman->regs_lock); + sec = clockman_read(clockman, data->ctrl_reg); + sec &= ~PLL_SEC_DIV_MASK; + sec |= FIELD_PREP(PLL_SEC_DIV_MASK, div); + + /* Must keep the divider in reset to change the value. */ + sec |= PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + + /* must sleep 10 pll vco cycles */ + ndelay(div64_ul(10ULL * div * NSEC_PER_SEC, parent_rate)); + + sec &= ~PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long rp1_pll_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return clk_divider_ops.round_rate(hw, rate, parent_rate); +} + +static int rp1_clock_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE); +} + +static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u64 calc_rate; + u64 div; + u32 frac; + + div = clockman_read(clockman, data->div_int_reg); + frac = (data->div_frac_reg != 0) ? + clockman_read(clockman, data->div_frac_reg) : 0; + + /* If the integer portion of the divider is 0, treat it as 2^16 */ + if (!div) + div = 1 << 16; + + div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS)); + + calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS; + calc_rate = div64_u64(calc_rate, div); + + return calc_rate; +} + +static int rp1_clock_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn on the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) | data->oe_mask); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_clock_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn off the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) & ~data->oe_mask); + spin_unlock(&clockman->regs_lock); +} + +static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate, + const struct rp1_clock_data *data) +{ + u64 div; + + /* + * Due to earlier rounding, calculated parent_rate may differ from + * expected value. Don't fail on a small discrepancy near unity divide. + */ + if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS)) + return 0; + + /* + * Always express div in fixed-point format for fractional division; + * If no fractional divider is present, the fraction part will be zero. + */ + if (data->div_frac_reg) { + div = (u64)parent_rate << CLK_DIV_FRAC_BITS; + div = DIV_ROUND_CLOSEST_ULL(div, rate); + } else { + div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate); + div <<= CLK_DIV_FRAC_BITS; + } + + div = clamp(div, + 1ull << CLK_DIV_FRAC_BITS, + (u64)data->div_int_max << CLK_DIV_FRAC_BITS); + + return div; +} + +static u8 rp1_clock_get_parent(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 sel, ctrl; + u8 parent; + + /* Sel is one-hot, so find the first bit set */ + sel = clockman_read(clockman, data->sel_reg); + parent = ffs(sel) - 1; + + /* sel == 0 implies the parent clock is not enabled yet. */ + if (!sel) { + /* Read the clock src from the CTRL register instead */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT; + } + + if (parent >= data->num_std_parents) + parent = AUX_SEL; + + if (parent == AUX_SEL) { + /* + * Clock parent is an auxiliary source, so get the parent from + * the AUXSRC register field. + */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = FIELD_GET(CLK_CTRL_AUXSRC_MASK, ctrl); + parent += data->num_std_parents; + } + + return parent; +} + +static int rp1_clock_set_parent(struct clk_hw *hw, u8 index) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 ctrl, sel; + + spin_lock(&clockman->regs_lock); + ctrl = clockman_read(clockman, data->ctrl_reg); + + if (index >= data->num_std_parents) { + /* This is an aux source request */ + if (index >= data->num_std_parents + data->num_aux_parents) { + spin_unlock(&clockman->regs_lock); + return -EINVAL; + } + + /* Select parent from aux list */ + ctrl &= ~CLK_CTRL_AUXSRC_MASK; + ctrl |= FIELD_PREP(CLK_CTRL_AUXSRC_MASK, index - data->num_std_parents); + /* Set src to aux list */ + ctrl &= ~data->clk_src_mask; + ctrl |= (AUX_SEL << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask; + } else { + ctrl &= ~data->clk_src_mask; + ctrl |= (index << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask; + } + + clockman_write(clockman, data->ctrl_reg, ctrl); + spin_unlock(&clockman->regs_lock); + + sel = rp1_clock_get_parent(hw); + if (sel != index) + return -EINVAL; + + return 0; +} + +static int rp1_clock_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 parent) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 div = rp1_clock_choose_div(rate, parent_rate, data); + + spin_lock(&clockman->regs_lock); + + clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS); + if (data->div_frac_reg) + clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS)); + + spin_unlock(&clockman->regs_lock); + + if (parent != 0xff) + return rp1_clock_set_parent(hw, parent); + + return 0; +} + +static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff); +} + +static void rp1_clock_choose_div_and_prate(struct clk_hw *hw, + int parent_idx, + unsigned long rate, + unsigned long *prate, + unsigned long *calc_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_clock_data *data = clock->data; + struct clk_hw *parent; + u32 div; + u64 tmp; + + parent = clk_hw_get_parent_by_index(hw, parent_idx); + + *prate = clk_hw_get_rate(parent); + div = rp1_clock_choose_div(rate, *prate, data); + + if (!div) { + *calc_rate = 0; + return; + } + + /* Recalculate to account for rounding errors */ + tmp = (u64)*prate << CLK_DIV_FRAC_BITS; + tmp = div_u64(tmp, div); + + /* + * Prevent overclocks - if all parent choices result in + * a downstream clock in excess of the maximum, then the + * call to set the clock will fail. + */ + if (tmp > data->max_freq) + *calc_rate = 0; + else + *calc_rate = tmp; +} + +static int rp1_clock_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *parent, *best_parent = NULL; + unsigned long best_rate = 0; + unsigned long best_prate = 0; + unsigned long best_rate_diff = ULONG_MAX; + unsigned long prate, calc_rate; + size_t i; + + /* + * If the NO_REPARENT flag is set, try to use existing parent. + */ + if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) { + i = rp1_clock_get_parent(hw); + parent = clk_hw_get_parent_by_index(hw, i); + if (parent) { + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + if (calc_rate > 0) { + req->best_parent_hw = parent; + req->best_parent_rate = prate; + req->rate = calc_rate; + return 0; + } + } + } + + /* + * Select parent clock that results in the closest rate (lower or + * higher) + */ + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + + if (abs_diff(calc_rate, req->rate) < best_rate_diff) { + best_parent = parent; + best_prate = prate; + best_rate = calc_rate; + best_rate_diff = abs_diff(calc_rate, req->rate); + + if (best_rate_diff == 0) + break; + } + } + + if (best_rate == 0) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = best_prate; + req->rate = best_rate; + + return 0; +} + +static const struct clk_ops rp1_pll_core_ops = { + .is_prepared = rp1_pll_core_is_on, + .prepare = rp1_pll_core_on, + .unprepare = rp1_pll_core_off, + .set_rate = rp1_pll_core_set_rate, + .recalc_rate = rp1_pll_core_recalc_rate, + .round_rate = rp1_pll_core_round_rate, +}; + +static const struct clk_ops rp1_pll_ops = { + .set_rate = rp1_pll_set_rate, + .recalc_rate = rp1_pll_recalc_rate, + .round_rate = rp1_pll_round_rate, +}; + +static const struct clk_ops rp1_pll_ph_ops = { + .is_prepared = rp1_pll_ph_is_on, + .prepare = rp1_pll_ph_on, + .unprepare = rp1_pll_ph_off, + .recalc_rate = rp1_pll_ph_recalc_rate, + .round_rate = rp1_pll_ph_round_rate, +}; + +static const struct clk_ops rp1_pll_divider_ops = { + .is_prepared = rp1_pll_divider_is_on, + .prepare = rp1_pll_divider_on, + .unprepare = rp1_pll_divider_off, + .set_rate = rp1_pll_divider_set_rate, + .recalc_rate = rp1_pll_divider_recalc_rate, + .round_rate = rp1_pll_divider_round_rate, +}; + +static const struct clk_ops rp1_clk_ops = { + .is_prepared = rp1_clock_is_on, + .prepare = rp1_clock_on, + .unprepare = rp1_clock_off, + .recalc_rate = rp1_clock_recalc_rate, + .get_parent = rp1_clock_get_parent, + .set_parent = rp1_clock_set_parent, + .set_rate_and_parent = rp1_clock_set_rate_and_parent, + .set_rate = rp1_clock_set_rate, + .determine_rate = rp1_clock_determine_rate, +}; + +static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + int ret; + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->hw); + if (ret) + return ERR_PTR(ret); + + return &desc->hw; +} + +static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + const struct rp1_pll_data *divider_data = desc->data; + int ret; + + desc->div.reg = clockman->regs + divider_data->ctrl_reg; + desc->div.shift = __ffs(PLL_SEC_DIV_MASK); + desc->div.width = __ffs(~(PLL_SEC_DIV_MASK >> desc->div.shift)); + desc->div.flags = CLK_DIVIDER_ROUND_CLOSEST; + desc->div.lock = &clockman->regs_lock; + desc->div.hw.init = desc->hw.init; + desc->div.table = pll_sec_div_table; + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->div.hw); + if (ret) + return ERR_PTR(ret); + + return &desc->div.hw; +} + +static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + const struct rp1_clock_data *clock_data = desc->data; + int ret; + + if (WARN_ON_ONCE(MAX_CLK_PARENTS < + clock_data->num_std_parents + clock_data->num_aux_parents)) + return ERR_PTR(-EINVAL); + + /* There must be a gap for the AUX selector */ + if (WARN_ON_ONCE(clock_data->num_std_parents > AUX_SEL && + desc->hw.init->parent_data[AUX_SEL].index != -1)) + return ERR_PTR(-EINVAL); + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->hw); + if (ret) + return ERR_PTR(ret); + + return &desc->hw; +} + +/* Assignment helper macros for different clock types. */ +#define _REGISTER(f, ...) { .clk_register = f, __VA_ARGS__ } + +#define CLK_DATA(type, ...) .data = &(struct type) { __VA_ARGS__ } + +#define REGISTER_PLL(...) _REGISTER(&rp1_register_pll, \ + __VA_ARGS__) + +#define REGISTER_PLL_DIV(...) _REGISTER(&rp1_register_pll_divider, \ + __VA_ARGS__) + +#define REGISTER_CLK(...) _REGISTER(&rp1_register_clock, \ + __VA_ARGS__) + +static struct rp1_clk_desc pll_sys_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_SYS_CS, + .pwr_reg = PLL_SYS_PWR, + .fbdiv_int_reg = PLL_SYS_FBDIV_INT, + .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_audio_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_AUDIO_CS, + .pwr_reg = PLL_AUDIO_PWR, + .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT, + .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_video_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_video_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_VIDEO_CS, + .pwr_reg = PLL_VIDEO_PWR, + .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT, + .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_sys_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_core_desc.hw } + }, + &rp1_pll_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_SYS_PRIM, + .fc0_src = FC_NUM(0, 2), + ) +); + +static struct rp1_clk_desc pll_sys_sec_desc = REGISTER_PLL_DIV( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_sec", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_core_desc.hw } + }, + &rp1_pll_divider_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_SYS_SEC, + .fc0_src = FC_NUM(2, 2), + ) +); + +static struct rp1_clk_desc clk_eth_tsu_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_eth_tsu", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_ETH_TSU_CTRL, + .div_int_reg = CLK_ETH_TSU_DIV_INT, + .sel_reg = CLK_ETH_TSU_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 7), + ) +); + +static const struct clk_parent_data clk_eth_parents[] = { + { .hw = &pll_sys_sec_desc.div.hw }, + { .hw = &pll_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_eth_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_eth", + clk_eth_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 2, + .ctrl_reg = CLK_ETH_CTRL, + .div_int_reg = CLK_ETH_DIV_INT, + .sel_reg = CLK_ETH_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 125 * HZ_PER_MHZ, + .fc0_src = FC_NUM(4, 6), + ) +); + +static const struct clk_parent_data clk_sys_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .hw = &pll_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_sys_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_sys", + clk_sys_parents, + &rp1_clk_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 3, + .num_aux_parents = 0, + .ctrl_reg = CLK_SYS_CTRL, + .div_int_reg = CLK_SYS_DIV_INT, + .sel_reg = CLK_SYS_SEL, + .div_int_max = DIV_INT_24BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(0, 4), + .clk_src_mask = 0x3, + ) +); + +static struct rp1_clk_desc pll_sys_pri_ph_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_pri_ph", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_desc.hw } + }, + &rp1_pll_ph_ops, + 0 + ), + CLK_DATA(rp1_pll_ph_data, + .ph_reg = PLL_SYS_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(1, 2), + ) +); + +static struct rp1_clk_desc *const clk_desc_array[] = { + [RP1_PLL_SYS_CORE] = &pll_sys_core_desc, + [RP1_PLL_AUDIO_CORE] = &pll_audio_core_desc, + [RP1_PLL_VIDEO_CORE] = &pll_video_core_desc, + [RP1_PLL_SYS] = &pll_sys_desc, + [RP1_CLK_ETH_TSU] = &clk_eth_tsu_desc, + [RP1_CLK_ETH] = &clk_eth_desc, + [RP1_CLK_SYS] = &clk_sys_desc, + [RP1_PLL_SYS_PRI_PH] = &pll_sys_pri_ph_desc, + [RP1_PLL_SYS_SEC] = &pll_sys_sec_desc, +}; + +static const struct regmap_range rp1_reg_ranges[] = { + regmap_reg_range(PLL_SYS_CS, PLL_SYS_SEC), + regmap_reg_range(PLL_AUDIO_CS, PLL_AUDIO_TERN), + regmap_reg_range(PLL_VIDEO_CS, PLL_VIDEO_SEC), + regmap_reg_range(GPCLK_OE_CTRL, GPCLK_OE_CTRL), + regmap_reg_range(CLK_SYS_CTRL, CLK_SYS_DIV_INT), + regmap_reg_range(CLK_SYS_SEL, CLK_SYS_SEL), + regmap_reg_range(CLK_SLOW_SYS_CTRL, CLK_SLOW_SYS_DIV_INT), + regmap_reg_range(CLK_SLOW_SYS_SEL, CLK_SLOW_SYS_SEL), + regmap_reg_range(CLK_DMA_CTRL, CLK_DMA_DIV_INT), + regmap_reg_range(CLK_DMA_SEL, CLK_DMA_SEL), + regmap_reg_range(CLK_UART_CTRL, CLK_UART_DIV_INT), + regmap_reg_range(CLK_UART_SEL, CLK_UART_SEL), + regmap_reg_range(CLK_ETH_CTRL, CLK_ETH_DIV_INT), + regmap_reg_range(CLK_ETH_SEL, CLK_ETH_SEL), + regmap_reg_range(CLK_PWM0_CTRL, CLK_PWM0_SEL), + regmap_reg_range(CLK_PWM1_CTRL, CLK_PWM1_SEL), + regmap_reg_range(CLK_AUDIO_IN_CTRL, CLK_AUDIO_IN_DIV_INT), + regmap_reg_range(CLK_AUDIO_IN_SEL, CLK_AUDIO_IN_SEL), + regmap_reg_range(CLK_AUDIO_OUT_CTRL, CLK_AUDIO_OUT_DIV_INT), + regmap_reg_range(CLK_AUDIO_OUT_SEL, CLK_AUDIO_OUT_SEL), + regmap_reg_range(CLK_I2S_CTRL, CLK_I2S_DIV_INT), + regmap_reg_range(CLK_I2S_SEL, CLK_I2S_SEL), + regmap_reg_range(CLK_MIPI0_CFG_CTRL, CLK_MIPI0_CFG_DIV_INT), + regmap_reg_range(CLK_MIPI0_CFG_SEL, CLK_MIPI0_CFG_SEL), + regmap_reg_range(CLK_MIPI1_CFG_CTRL, CLK_MIPI1_CFG_DIV_INT), + regmap_reg_range(CLK_MIPI1_CFG_SEL, CLK_MIPI1_CFG_SEL), + regmap_reg_range(CLK_PCIE_AUX_CTRL, CLK_PCIE_AUX_DIV_INT), + regmap_reg_range(CLK_PCIE_AUX_SEL, CLK_PCIE_AUX_SEL), + regmap_reg_range(CLK_USBH0_MICROFRAME_CTRL, CLK_USBH0_MICROFRAME_DIV_INT), + regmap_reg_range(CLK_USBH0_MICROFRAME_SEL, CLK_USBH0_MICROFRAME_SEL), + regmap_reg_range(CLK_USBH1_MICROFRAME_CTRL, CLK_USBH1_MICROFRAME_DIV_INT), + regmap_reg_range(CLK_USBH1_MICROFRAME_SEL, CLK_USBH1_MICROFRAME_SEL), + regmap_reg_range(CLK_USBH0_SUSPEND_CTRL, CLK_USBH0_SUSPEND_DIV_INT), + regmap_reg_range(CLK_USBH0_SUSPEND_SEL, CLK_USBH0_SUSPEND_SEL), + regmap_reg_range(CLK_USBH1_SUSPEND_CTRL, CLK_USBH1_SUSPEND_DIV_INT), + regmap_reg_range(CLK_USBH1_SUSPEND_SEL, CLK_USBH1_SUSPEND_SEL), + regmap_reg_range(CLK_ETH_TSU_CTRL, CLK_ETH_TSU_DIV_INT), + regmap_reg_range(CLK_ETH_TSU_SEL, CLK_ETH_TSU_SEL), + regmap_reg_range(CLK_ADC_CTRL, CLK_ADC_DIV_INT), + regmap_reg_range(CLK_ADC_SEL, CLK_ADC_SEL), + regmap_reg_range(CLK_SDIO_TIMER_CTRL, CLK_SDIO_TIMER_DIV_INT), + regmap_reg_range(CLK_SDIO_TIMER_SEL, CLK_SDIO_TIMER_SEL), + regmap_reg_range(CLK_SDIO_ALT_SRC_CTRL, CLK_SDIO_ALT_SRC_DIV_INT), + regmap_reg_range(CLK_SDIO_ALT_SRC_SEL, CLK_SDIO_ALT_SRC_SEL), + regmap_reg_range(CLK_GP0_CTRL, CLK_GP0_SEL), + regmap_reg_range(CLK_GP1_CTRL, CLK_GP1_SEL), + regmap_reg_range(CLK_GP2_CTRL, CLK_GP2_SEL), + regmap_reg_range(CLK_GP3_CTRL, CLK_GP3_SEL), + regmap_reg_range(CLK_GP4_CTRL, CLK_GP4_SEL), + regmap_reg_range(CLK_GP5_CTRL, CLK_GP5_SEL), + regmap_reg_range(CLK_SYS_RESUS_CTRL, CLK_SYS_RESUS_CTRL), + regmap_reg_range(CLK_SLOW_SYS_RESUS_CTRL, CLK_SLOW_SYS_RESUS_CTRL), + regmap_reg_range(FC0_REF_KHZ, FC0_RESULT), + regmap_reg_range(VIDEO_CLK_VEC_CTRL, VIDEO_CLK_VEC_DIV_INT), + regmap_reg_range(VIDEO_CLK_VEC_SEL, VIDEO_CLK_DPI_DIV_INT), + regmap_reg_range(VIDEO_CLK_DPI_SEL, VIDEO_CLK_MIPI1_DPI_SEL), +}; + +static const struct regmap_access_table rp1_reg_table = { + .yes_ranges = rp1_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(rp1_reg_ranges), +}; + +static const struct regmap_config rp1_clk_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = PLL_VIDEO_SEC, + .name = "rp1-clk", + .rd_table = &rp1_reg_table, + .disable_locking = true, +}; + +static int rp1_clk_probe(struct platform_device *pdev) +{ + const size_t asize = ARRAY_SIZE(clk_desc_array); + struct rp1_clk_desc *desc; + struct device *dev = &pdev->dev; + struct rp1_clockman *clockman; + struct clk_hw **hws; + unsigned int i; + + clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize), + GFP_KERNEL); + if (!clockman) + return -ENOMEM; + + spin_lock_init(&clockman->regs_lock); + clockman->dev = dev; + + clockman->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(clockman->regs)) + return PTR_ERR(clockman->regs); + + clockman->regmap = devm_regmap_init_mmio(dev, clockman->regs, + &rp1_clk_regmap_cfg); + if (IS_ERR(clockman->regmap)) { + dev_err_probe(dev, PTR_ERR(clockman->regmap), + "could not init clock regmap\n"); + return PTR_ERR(clockman->regmap); + } + + clockman->onecell.num = asize; + hws = clockman->onecell.hws; + + for (i = 0; i < asize; i++) { + desc = clk_desc_array[i]; + if (desc && desc->clk_register && desc->data) + hws[i] = desc->clk_register(clockman, desc); + } + + platform_set_drvdata(pdev, clockman); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &clockman->onecell); +} + +static const struct of_device_id rp1_clk_of_match[] = { + { .compatible = "raspberrypi,rp1-clocks" }, + {} +}; +MODULE_DEVICE_TABLE(of, rp1_clk_of_match); + +static struct platform_driver rp1_clk_driver = { + .driver = { + .name = "rp1-clk", + .of_match_table = rp1_clk_of_match, + }, + .probe = rp1_clk_probe, +}; + +module_platform_driver(rp1_clk_driver); + +MODULE_AUTHOR("Naushir Patuck "); +MODULE_AUTHOR("Andrea della Porta "); +MODULE_DESCRIPTION("RP1 clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 8ddf3a9a53df..d4e9c3577b35 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -235,7 +235,7 @@ MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); * through platform_device_id. * * However if device's DT node contains proper clock compatible and driver is - * built as a module, then the *module* matching will be done trough DT aliases. + * built as a module, then the *module* matching will be done through DT aliases. * This requires of_device_id table. In the same time this will not change the * actual *device* matching so do not add .of_match_table. */ diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 15510c2ff21c..d2408403283f 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -404,6 +404,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) const struct scmi_handle *handle = sdev->handle; struct scmi_protocol_handle *ph; const struct clk_ops *scmi_clk_ops_db[SCMI_MAX_CLK_OPS] = {}; + struct scmi_clk *sclks; if (!handle) return -ENODEV; @@ -430,18 +431,21 @@ static int scmi_clocks_probe(struct scmi_device *sdev) transport_is_atomic = handle->is_transport_atomic(handle, &atomic_threshold_us); - for (idx = 0; idx < count; idx++) { - struct scmi_clk *sclk; - const struct clk_ops *scmi_ops; + sclks = devm_kcalloc(dev, count, sizeof(*sclks), GFP_KERNEL); + if (!sclks) + return -ENOMEM; - sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL); - if (!sclk) - return -ENOMEM; + for (idx = 0; idx < count; idx++) + hws[idx] = &sclks[idx].hw; + + for (idx = 0; idx < count; idx++) { + struct scmi_clk *sclk = &sclks[idx]; + const struct clk_ops *scmi_ops; sclk->info = scmi_proto_clk_ops->info_get(ph, idx); if (!sclk->info) { dev_dbg(dev, "invalid clock info for idx %d\n", idx); - devm_kfree(dev, sclk); + hws[idx] = NULL; continue; } @@ -451,7 +455,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) /* * Note that the scmi_clk_ops_db is on the stack, not global, - * because it cannot be shared between mulitple probe-sequences + * because it cannot be shared between multiple probe-sequences * to avoid sharing the devm_ allocated clk_ops between multiple * SCMI clk driver instances. */ @@ -479,13 +483,11 @@ static int scmi_clocks_probe(struct scmi_device *sdev) if (err) { dev_err(dev, "failed to register clock %d\n", idx); devm_kfree(dev, sclk->parent_data); - devm_kfree(dev, sclk); hws[idx] = NULL; } else { dev_dbg(dev, "Registered clock:%s%s\n", sclk->info->name, scmi_ops->enable ? " (atomic ops)" : ""); - hws[idx] = &sclk->hw; } } diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index a4c92c5ef3ff..e755db545e2e 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -655,7 +655,7 @@ static int si5351_msynth_determine_rate(struct clk_hw *hw, unsigned long a, b, c; int divby4; - /* multisync6-7 can only handle freqencies < 150MHz */ + /* multisync6-7 can only handle frequencies < 150MHz */ if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ) rate = SI5351_MULTISYNTH67_MAX_FREQ; @@ -1048,11 +1048,11 @@ static int si5351_clkout_determine_rate(struct clk_hw *hw, unsigned long rate = req->rate; unsigned char rdiv; - /* clkout6/7 can only handle output freqencies < 150MHz */ + /* clkout6/7 can only handle output frequencies < 150MHz */ if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ) rate = SI5351_CLKOUT67_MAX_FREQ; - /* clkout freqency is 8kHz - 160MHz */ + /* clkout frequency is 8kHz - 160MHz */ if (rate > SI5351_CLKOUT_MAX_FREQ) rate = SI5351_CLKOUT_MAX_FREQ; if (rate < SI5351_CLKOUT_MIN_FREQ) diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c index c88650558f32..ca3473efa314 100644 --- a/drivers/clk/clk-si544.c +++ b/drivers/clk/clk-si544.c @@ -39,7 +39,7 @@ /* Max freq depends on speed grade */ #define SI544_MIN_FREQ 200000U -/* Si544 Internal oscilator runs at 55.05 MHz */ +/* Si544 Internal oscillator runs at 55.05 MHz */ #define FXO 55050000U /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */ diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c index a549ea13be20..e97fe90443a6 100644 --- a/drivers/clk/clk-si570.c +++ b/drivers/clk/clk-si570.c @@ -63,7 +63,7 @@ struct clk_si570_info { * struct clk_si570: * @hw: Clock hw struct * @regmap: Device's regmap - * @div_offset: Rgister offset for dividers + * @div_offset: Register offset for dividers * @info: Device info * @fxtal: Factory xtal frequency * @n1: Clock divider N1 @@ -181,7 +181,7 @@ static int si570_update_rfreq(struct clk_si570 *data) } /** - * si570_calc_divs() - Caluclate clock dividers + * si570_calc_divs() - Calculate clock dividers * @frequency: Target frequency * @data: Driver data structure * @out_rfreq: RFREG fractional multiplier (output) diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c index 7cb7d501d7a6..95d66191df4b 100644 --- a/drivers/clk/clk-sp7021.c +++ b/drivers/clk/clk-sp7021.c @@ -14,7 +14,7 @@ #include -/* speical div_width values for PLLTV/PLLA */ +/* special div_width values for PLLTV/PLLA */ #define DIV_TV 33 #define DIV_A 34 diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 85e23961ec34..719cddc82ae6 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -19,7 +19,7 @@ #include /* - * Include list of clocks wich are not derived from system clock (SYSCLOCK) + * Include list of clocks which are not derived from system clock (SYSCLOCK) * The index of these clocks is the secondary index of DT bindings * */ diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 6d31cd54d7cf..4200022d2084 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -136,7 +136,7 @@ #define VC5_MAX_FOD_NUM 4 /* flags to describe chip features */ -/* chip has built-in oscilator */ +/* chip has built-in oscillator */ #define VC5_HAS_INTERNAL_XTAL BIT(0) /* chip has PFD requency doubler */ #define VC5_HAS_PFD_FREQ_DBL BIT(1) diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c index f323263e32c3..483285b30c13 100644 --- a/drivers/clk/clk-versaclock7.c +++ b/drivers/clk/clk-versaclock7.c @@ -1257,7 +1257,7 @@ static const struct vc7_chip_info vc7_rc21008a_info = { .num_outputs = 8, }; -static struct regmap_range_cfg vc7_range_cfg[] = { +static const struct regmap_range_cfg vc7_range_cfg[] = { { .range_min = 0, .range_max = VC7_MAX_REG, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0565c87656cf..b821b2cdb155 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -365,6 +365,18 @@ const char *clk_hw_get_name(const struct clk_hw *hw) } EXPORT_SYMBOL_GPL(clk_hw_get_name); +struct device *clk_hw_get_dev(const struct clk_hw *hw) +{ + return hw->core->dev; +} +EXPORT_SYMBOL_GPL(clk_hw_get_dev); + +struct device_node *clk_hw_get_of_node(const struct clk_hw *hw) +{ + return hw->core->of_node; +} +EXPORT_SYMBOL_GPL(clk_hw_get_of_node); + struct clk_hw *__clk_get_hw(struct clk *clk) { return !clk ? NULL : clk->core->hw; diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index f08feeaa3750..a268d7b5d4cb 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -292,7 +292,7 @@ static void clk_test_set_set_get_rate(struct kunit *test) } /* - * Test that clk_round_rate and clk_set_rate are consitent and will + * Test that clk_round_rate and clk_set_rate are consistent and will * return the same frequency. */ static void clk_test_round_set_get_rate(struct kunit *test) @@ -2794,49 +2794,49 @@ static struct kunit_suite clk_register_clk_parent_data_of_suite = { }; /** - * struct clk_register_clk_parent_data_device_ctx - Context for clk_parent_data device tests - * @dev: device of clk under test - * @hw: clk_hw for clk under test + * struct platform_driver_dev_ctx - Context to stash platform device + * @dev: device under test * @pdrv: driver to attach to find @dev */ -struct clk_register_clk_parent_data_device_ctx { +struct platform_driver_dev_ctx { struct device *dev; - struct clk_hw hw; struct platform_driver pdrv; }; -static inline struct clk_register_clk_parent_data_device_ctx * -clk_register_clk_parent_data_driver_to_test_context(struct platform_device *pdev) +static inline struct platform_driver_dev_ctx * +pdev_to_platform_driver_dev_ctx(struct platform_device *pdev) { return container_of(to_platform_driver(pdev->dev.driver), - struct clk_register_clk_parent_data_device_ctx, pdrv); + struct platform_driver_dev_ctx, pdrv); } -static int clk_register_clk_parent_data_device_probe(struct platform_device *pdev) +static int kunit_platform_driver_dev_probe(struct platform_device *pdev) { - struct clk_register_clk_parent_data_device_ctx *ctx; + struct platform_driver_dev_ctx *ctx; - ctx = clk_register_clk_parent_data_driver_to_test_context(pdev); + ctx = pdev_to_platform_driver_dev_ctx(pdev); ctx->dev = &pdev->dev; return 0; } -static void clk_register_clk_parent_data_device_driver(struct kunit *test) +static struct device * +kunit_of_platform_driver_dev(struct kunit *test, const struct of_device_id *match_table) { - struct clk_register_clk_parent_data_device_ctx *ctx = test->priv; - static const struct of_device_id match_table[] = { - { .compatible = "test,clk-parent-data" }, - { } - }; + struct platform_driver_dev_ctx *ctx; - ctx->pdrv.probe = clk_register_clk_parent_data_device_probe; + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->pdrv.probe = kunit_platform_driver_dev_probe; ctx->pdrv.driver.of_match_table = match_table; ctx->pdrv.driver.name = __func__; ctx->pdrv.driver.owner = THIS_MODULE; KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv)); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev); + + return ctx->dev; } static const struct clk_register_clk_parent_data_test_case @@ -2909,30 +2909,34 @@ KUNIT_ARRAY_PARAM(clk_register_clk_parent_data_device_test, */ static void clk_register_clk_parent_data_device_test(struct kunit *test) { - struct clk_register_clk_parent_data_device_ctx *ctx; + struct device *dev; + struct clk_hw *hw; const struct clk_register_clk_parent_data_test_case *test_param; struct clk_hw *parent_hw; struct clk_init_data init = { }; struct clk *expected_parent, *actual_parent; + static const struct of_device_id match_table[] = { + { .compatible = "test,clk-parent-data" }, + { } + }; - ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - test->priv = ctx; + dev = kunit_of_platform_driver_dev(test, match_table); - clk_register_clk_parent_data_device_driver(test); - - expected_parent = clk_get_kunit(test, ctx->dev, "50"); + expected_parent = clk_get_kunit(test, dev, "50"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_parent); + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + test_param = test->param_value; init.parent_data = &test_param->pdata; init.num_parents = 1; init.name = "parent_data_device_test_clk"; init.ops = &clk_dummy_single_parent_ops; - ctx->hw.init = &init; - KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, ctx->dev, &ctx->hw)); + hw->init = &init; + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw)); - parent_hw = clk_hw_get_parent(&ctx->hw); + parent_hw = clk_hw_get_parent(hw); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); actual_parent = clk_hw_get_clk_kunit(test, parent_hw, __func__); @@ -3016,18 +3020,19 @@ KUNIT_ARRAY_PARAM(clk_register_clk_parent_data_device_hw_test, */ static void clk_register_clk_parent_data_device_hw_test(struct kunit *test) { - struct clk_register_clk_parent_data_device_ctx *ctx; + struct device *dev; + struct clk_hw *hw; const struct clk_register_clk_parent_data_test_case *test_param; struct clk_dummy_context *parent; struct clk_hw *parent_hw; struct clk_parent_data pdata = { }; struct clk_init_data init = { }; + static const struct of_device_id match_table[] = { + { .compatible = "test,clk-parent-data" }, + { } + }; - ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - test->priv = ctx; - - clk_register_clk_parent_data_device_driver(test); + dev = kunit_of_platform_driver_dev(test, match_table); parent = kunit_kzalloc(test, sizeof(*parent), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent); @@ -3036,7 +3041,10 @@ static void clk_register_clk_parent_data_device_hw_test(struct kunit *test) parent_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk", &clk_dummy_rate_ops, 0); - KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, ctx->dev, parent_hw)); + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, parent_hw)); + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); test_param = test->param_value; memcpy(&pdata, &test_param->pdata, sizeof(pdata)); @@ -3045,10 +3053,10 @@ static void clk_register_clk_parent_data_device_hw_test(struct kunit *test) init.num_parents = 1; init.ops = &clk_dummy_single_parent_ops; init.name = "parent_data_device_hw_test_clk"; - ctx->hw.init = &init; - KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, ctx->dev, &ctx->hw)); + hw->init = &init; + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw)); - KUNIT_EXPECT_PTR_EQ(test, parent_hw, clk_hw_get_parent(&ctx->hw)); + KUNIT_EXPECT_PTR_EQ(test, parent_hw, clk_hw_get_parent(hw)); } static struct kunit_case clk_register_clk_parent_data_device_test_cases[] = { @@ -3395,8 +3403,148 @@ static struct kunit_suite clk_assigned_rates_suite = { .init = clk_assigned_rates_test_init, }; +static const struct clk_init_data clk_hw_get_dev_of_node_init_data = { + .name = "clk_hw_get_dev_of_node", + .ops = &empty_clk_ops, +}; + +/* + * Test that a clk registered with a struct device returns the device from + * clk_hw_get_dev() and the node from clk_hw_get_of_node() + */ +static void clk_hw_register_dev_get_dev_returns_dev(struct kunit *test) +{ + struct device *dev; + struct clk_hw *hw; + static const struct of_device_id match_table[] = { + { .compatible = "test,clk-hw-get-dev-of-node" }, + { } + }; + + KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, kunit_clk_hw_get_dev_of_node)); + + dev = kunit_of_platform_driver_dev(test, match_table); + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + + hw->init = &clk_hw_get_dev_of_node_init_data; + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw)); + + KUNIT_EXPECT_PTR_EQ(test, dev, clk_hw_get_dev(hw)); + KUNIT_EXPECT_PTR_EQ(test, dev_of_node(dev), clk_hw_get_of_node(hw)); +} + +/* + * Test that a clk registered with a struct device that's not associated with + * an OF node returns the device from clk_hw_get_dev() and NULL from + * clk_hw_get_of_node() + */ +static void clk_hw_register_dev_no_node_get_dev_returns_dev(struct kunit *test) +{ + struct platform_device *pdev; + struct device *dev; + struct clk_hw *hw; + + pdev = kunit_platform_device_alloc(test, "clk_hw_register_dev_no_node", -1); + KUNIT_ASSERT_NOT_NULL(test, pdev); + KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev)); + dev = &pdev->dev; + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + + hw->init = &clk_hw_get_dev_of_node_init_data; + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw)); + + KUNIT_EXPECT_PTR_EQ(test, dev, clk_hw_get_dev(hw)); + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw)); +} + +/* + * Test that a clk registered without a struct device returns NULL from + * clk_hw_get_dev() + */ +static void clk_hw_register_NULL_get_dev_of_node_returns_NULL(struct kunit *test) +{ + struct clk_hw *hw; + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + + hw->init = &clk_hw_get_dev_of_node_init_data; + + KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, hw)); + + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw)); + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw)); +} + +/* + * Test that a clk registered with an of_node returns the node from + * clk_hw_get_of_node() and NULL from clk_hw_get_dev() + */ +static void of_clk_hw_register_node_get_of_node_returns_node(struct kunit *test) +{ + struct device_node *np; + struct clk_hw *hw; + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + + KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, kunit_clk_hw_get_dev_of_node)); + + np = of_find_compatible_node(NULL, NULL, "test,clk-hw-get-dev-of-node"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + of_node_put_kunit(test, np); + + hw->init = &clk_hw_get_dev_of_node_init_data; + KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, hw)); + + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw)); + KUNIT_EXPECT_PTR_EQ(test, np, clk_hw_get_of_node(hw)); +} + +/* + * Test that a clk registered without an of_node returns the node from + * clk_hw_get_of_node() and clk_hw_get_dev() + */ +static void of_clk_hw_register_NULL_get_of_node_returns_NULL(struct kunit *test) +{ + struct clk_hw *hw; + + hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw); + + hw->init = &clk_hw_get_dev_of_node_init_data; + KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, NULL, hw)); + + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw)); + KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw)); +} + +static struct kunit_case clk_hw_get_dev_of_node_test_cases[] = { + KUNIT_CASE(clk_hw_register_dev_get_dev_returns_dev), + KUNIT_CASE(clk_hw_register_dev_no_node_get_dev_returns_dev), + KUNIT_CASE(clk_hw_register_NULL_get_dev_of_node_returns_NULL), + KUNIT_CASE(of_clk_hw_register_node_get_of_node_returns_node), + KUNIT_CASE(of_clk_hw_register_NULL_get_of_node_returns_NULL), + {} +}; + +/* + * Test suite to verify clk_hw_get_dev() and clk_hw_get_of_node() when clk + * registered with clk_hw_register() and of_clk_hw_register() + */ +static struct kunit_suite clk_hw_get_dev_of_node_test_suite = { + .name = "clk_hw_get_dev_of_node_test_suite", + .test_cases = clk_hw_get_dev_of_node_test_cases, +}; + + kunit_test_suites( &clk_assigned_rates_suite, + &clk_hw_get_dev_of_node_test_suite, &clk_leaf_mux_set_rate_parent_test_suite, &clk_test_suite, &clk_multiple_parents_mux_test_suite, diff --git a/drivers/clk/davinci/pll.h b/drivers/clk/davinci/pll.h index 20bfcec2d3b5..ad286ba4ce0c 100644 --- a/drivers/clk/davinci/pll.h +++ b/drivers/clk/davinci/pll.h @@ -80,7 +80,7 @@ static const struct davinci_pll_sysclk_info n = { \ * @name: The name of the clock * @parent_names: Array of names of the parent clocks * @num_parents: Length of @parent_names - * @table: Array of values to write to OCSEL[OCSRC] cooresponding to + * @table: Array of values to write to OCSEL[OCSRC] corresponding to * @parent_names * @ocsrc_mask: Bitmask for OCSEL[OCSRC] */ diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c index b48322176c21..f3ee9397bb0c 100644 --- a/drivers/clk/davinci/psc.c +++ b/drivers/clk/davinci/psc.c @@ -277,6 +277,11 @@ davinci_lpsc_clk_register(struct device *dev, const char *name, lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s", best_dev_name(dev), name); + if (!lpsc->pm_domain.name) { + clk_hw_unregister(&lpsc->hw); + kfree(lpsc); + return ERR_PTR(-ENOMEM); + } lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev; lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev; lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK; diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c index 90d858522967..21d4297f3225 100644 --- a/drivers/clk/hisilicon/clkgate-separated.c +++ b/drivers/clk/hisilicon/clkgate-separated.c @@ -17,9 +17,9 @@ #include "clk.h" /* clock separated gate register offset */ -#define CLKGATE_SEPERATED_ENABLE 0x0 -#define CLKGATE_SEPERATED_DISABLE 0x4 -#define CLKGATE_SEPERATED_STATUS 0x8 +#define CLKGATE_SEPARATED_ENABLE 0x0 +#define CLKGATE_SEPARATED_DISABLE 0x4 +#define CLKGATE_SEPARATED_STATUS 0x8 struct clkgate_separated { struct clk_hw hw; @@ -40,7 +40,7 @@ static int clkgate_separated_enable(struct clk_hw *hw) spin_lock_irqsave(sclk->lock, flags); reg = BIT(sclk->bit_idx); writel_relaxed(reg, sclk->enable); - readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS); if (sclk->lock) spin_unlock_irqrestore(sclk->lock, flags); return 0; @@ -56,8 +56,8 @@ static void clkgate_separated_disable(struct clk_hw *hw) if (sclk->lock) spin_lock_irqsave(sclk->lock, flags); reg = BIT(sclk->bit_idx); - writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE); - readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + writel_relaxed(reg, sclk->enable + CLKGATE_SEPARATED_DISABLE); + readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS); if (sclk->lock) spin_unlock_irqrestore(sclk->lock, flags); } @@ -68,7 +68,7 @@ static int clkgate_separated_is_enabled(struct clk_hw *hw) u32 reg; sclk = container_of(hw, struct clkgate_separated, hw); - reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + reg = readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS); reg &= BIT(sclk->bit_idx); return reg ? 1 : 0; @@ -100,7 +100,7 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); - sclk->enable = reg + CLKGATE_SEPERATED_ENABLE; + sclk->enable = reg + CLKGATE_SEPARATED_ENABLE; sclk->bit_idx = bit_idx; sclk->flags = clk_gate_flags; sclk->hw.init = &init; diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c index f163df952ccc..eb27c6fee359 100644 --- a/drivers/clk/imx/clk-busy.c +++ b/drivers/clk/imx/clk-busy.c @@ -46,12 +46,12 @@ static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw, return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate); } -static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_busy_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_busy_divider *busy = to_clk_busy_divider(hw); - return busy->div_ops->round_rate(&busy->div.hw, rate, prate); + return busy->div_ops->determine_rate(&busy->div.hw, req); } static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, @@ -69,7 +69,7 @@ static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops clk_busy_divider_ops = { .recalc_rate = clk_busy_divider_recalc_rate, - .round_rate = clk_busy_divider_round_rate, + .determine_rate = clk_busy_divider_determine_rate, .set_rate = clk_busy_divider_set_rate, }; diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index f187582ba491..1467d0a1b934 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -73,21 +73,6 @@ static int imx8m_clk_composite_compute_dividers(unsigned long rate, return ret; } -static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *prate) -{ - int prediv_value; - int div_value; - - imx8m_clk_composite_compute_dividers(rate, *prate, - &prediv_value, &div_value); - rate = DIV_ROUND_UP(*prate, prediv_value); - - return DIV_ROUND_UP(rate, div_value); - -} - static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) @@ -153,7 +138,6 @@ static int imx8m_divider_determine_rate(struct clk_hw *hw, static const struct clk_ops imx8m_clk_composite_divider_ops = { .recalc_rate = imx8m_clk_composite_divider_recalc_rate, - .round_rate = imx8m_clk_composite_divider_round_rate, .set_rate = imx8m_clk_composite_divider_set_rate, .determine_rate = imx8m_divider_determine_rate, }; diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c index 6c6c5a30f328..513d74a39d3b 100644 --- a/drivers/clk/imx/clk-composite-93.c +++ b/drivers/clk/imx/clk-composite-93.c @@ -98,12 +98,6 @@ imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_ return clk_divider_ops.recalc_rate(hw, parent_rate); } -static long -imx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) -{ - return clk_divider_ops.round_rate(hw, rate, prate); -} - static int imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { @@ -141,7 +135,6 @@ static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long static const struct clk_ops imx93_clk_composite_divider_ops = { .recalc_rate = imx93_clk_composite_divider_recalc_rate, - .round_rate = imx93_clk_composite_divider_round_rate, .determine_rate = imx93_clk_composite_divider_determine_rate, .set_rate = imx93_clk_composite_divider_set_rate, }; diff --git a/drivers/clk/imx/clk-cpu.c b/drivers/clk/imx/clk-cpu.c index cb6ca4cf0535..43637cb61693 100644 --- a/drivers/clk/imx/clk-cpu.c +++ b/drivers/clk/imx/clk-cpu.c @@ -30,12 +30,14 @@ static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, return clk_get_rate(cpu->div); } -static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_cpu_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_cpu *cpu = to_clk_cpu(hw); - return clk_round_rate(cpu->pll, rate); + req->rate = clk_round_rate(cpu->pll, req->rate); + + return 0; } static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, @@ -66,7 +68,7 @@ static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops clk_cpu_ops = { .recalc_rate = clk_cpu_recalc_rate, - .round_rate = clk_cpu_round_rate, + .determine_rate = clk_cpu_determine_rate, .set_rate = clk_cpu_set_rate, }; diff --git a/drivers/clk/imx/clk-fixup-div.c b/drivers/clk/imx/clk-fixup-div.c index 100ca828b052..aa6addbeb5a8 100644 --- a/drivers/clk/imx/clk-fixup-div.c +++ b/drivers/clk/imx/clk-fixup-div.c @@ -18,7 +18,7 @@ * @fixup: a hook to fixup the write value * * The imx fixup divider clock is a subclass of basic clk_divider - * with an addtional fixup hook. + * with an additional fixup hook. */ struct clk_fixup_div { struct clk_divider divider; @@ -41,12 +41,12 @@ static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw, return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate); } -static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_fixup_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); - return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate); + return fixup_div->ops->determine_rate(&fixup_div->divider.hw, req); } static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate, @@ -81,7 +81,7 @@ static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops clk_fixup_div_ops = { .recalc_rate = clk_fixup_div_recalc_rate, - .round_rate = clk_fixup_div_round_rate, + .determine_rate = clk_fixup_div_determine_rate, .set_rate = clk_fixup_div_set_rate, }; diff --git a/drivers/clk/imx/clk-fixup-mux.c b/drivers/clk/imx/clk-fixup-mux.c index b48701864ef0..418ac9fe2c26 100644 --- a/drivers/clk/imx/clk-fixup-mux.c +++ b/drivers/clk/imx/clk-fixup-mux.c @@ -17,7 +17,7 @@ * @fixup: a hook to fixup the write value * * The imx fixup multiplexer clock is a subclass of basic clk_mux - * with an addtional fixup hook. + * with an additional fixup hook. */ struct clk_fixup_mux { struct clk_mux mux; diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c index c703056fae85..eb668faaa38f 100644 --- a/drivers/clk/imx/clk-frac-pll.c +++ b/drivers/clk/imx/clk-frac-pll.c @@ -119,19 +119,19 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, return rate; } -static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - u64 parent_rate = *prate; + u64 parent_rate = req->best_parent_rate; u32 divff, divfi; u64 temp64; parent_rate *= 8; - rate *= 2; - temp64 = rate; + req->rate *= 2; + temp64 = req->rate; do_div(temp64, parent_rate); divfi = temp64; - temp64 = rate - divfi * parent_rate; + temp64 = req->rate - divfi * parent_rate; temp64 *= PLL_FRAC_DENOM; do_div(temp64, parent_rate); divff = temp64; @@ -140,9 +140,11 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, temp64 *= divff; do_div(temp64, PLL_FRAC_DENOM); - rate = parent_rate * divfi + temp64; + req->rate = parent_rate * divfi + temp64; - return rate / 2; + req->rate = req->rate / 2; + + return 0; } /* @@ -198,7 +200,7 @@ static const struct clk_ops clk_frac_pll_ops = { .unprepare = clk_pll_unprepare, .is_prepared = clk_pll_is_prepared, .recalc_rate = clk_pll_recalc_rate, - .round_rate = clk_pll_round_rate, + .determine_rate = clk_pll_determine_rate, .set_rate = clk_pll_set_rate, }; diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index 85771afd4698..090d60867250 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -134,8 +134,8 @@ imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate) return NULL; } -static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_fracn_gppll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; @@ -143,11 +143,16 @@ static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, /* Assuming rate_table is in descending order */ for (i = 0; i < pll->rate_count; i++) - if (rate >= rate_table[i].rate) - return rate_table[i].rate; + if (req->rate >= rate_table[i].rate) { + req->rate = rate_table[i].rate; + + return 0; + } /* return minimum supported value */ - return rate_table[pll->rate_count - 1].rate; + req->rate = rate_table[pll->rate_count - 1].rate; + + return 0; } static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -345,7 +350,7 @@ static const struct clk_ops clk_fracn_gppll_ops = { .unprepare = clk_fracn_gppll_unprepare, .is_prepared = clk_fracn_gppll_is_prepared, .recalc_rate = clk_fracn_gppll_recalc_rate, - .round_rate = clk_fracn_gppll_round_rate, + .determine_rate = clk_fracn_gppll_determine_rate, .set_rate = clk_fracn_gppll_set_rate, }; diff --git a/drivers/clk/imx/clk-gate-exclusive.c b/drivers/clk/imx/clk-gate-exclusive.c index 77342893bb71..7017e9d4e188 100644 --- a/drivers/clk/imx/clk-gate-exclusive.c +++ b/drivers/clk/imx/clk-gate-exclusive.c @@ -18,7 +18,7 @@ * gate clock * * The imx exclusive gate clock is a subclass of basic clk_gate - * with an addtional mask to indicate which other gate bits in the same + * with an additional mask to indicate which other gate bits in the same * register is mutually exclusive to this gate clock. */ struct clk_gate_exclusive { diff --git a/drivers/clk/imx/clk-imx5.c b/drivers/clk/imx/clk-imx5.c index b82044911603..9c5f489b3975 100644 --- a/drivers/clk/imx/clk-imx5.c +++ b/drivers/clk/imx/clk-imx5.c @@ -454,7 +454,7 @@ static void __init mx51_clocks_init(struct device_node *np) * longer supported. Set to one for better power saving. * * The effect of not setting these bits is that MIPI clocks can't be - * enabled without the IPU clock being enabled aswell. + * enabled without the IPU clock being enabled as well. */ val = readl(MXC_CCM_CCDR); val |= 1 << 18; diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c index c169fe53a35f..790f7e44b11e 100644 --- a/drivers/clk/imx/clk-imx8-acm.c +++ b/drivers/clk/imx/clk-imx8-acm.c @@ -22,7 +22,7 @@ * struct clk_imx_acm_pm_domains - structure for multi power domain * @pd_dev: power domain device * @pd_dev_link: power domain device link - * @num_domains: power domain nummber + * @num_domains: power domain number */ struct clk_imx_acm_pm_domains { struct device **pd_dev; diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c index d0ccaa040225..1dae3410ee99 100644 --- a/drivers/clk/imx/clk-imx8qxp-lpcg.c +++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c @@ -267,7 +267,6 @@ static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev, if (ret) goto unreg; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c index 25974947ad0c..7e88877a6245 100644 --- a/drivers/clk/imx/clk-imx95-blk-ctl.c +++ b/drivers/clk/imx/clk-imx95-blk-ctl.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2024 NXP + * Copyright 2024-2025 NXP */ +#include #include #include #include @@ -156,7 +157,7 @@ static const struct imx95_blk_ctl_dev_data camblk_dev_data = { .clk_reg_offset = 0, }; -static const struct imx95_blk_ctl_clk_dev_data lvds_clk_dev_data[] = { +static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = { [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = { .name = "ldb_phy_div", .parent_names = (const char *[]){ "ldbpll", }, @@ -213,17 +214,21 @@ static const struct imx95_blk_ctl_clk_dev_data lvds_clk_dev_data[] = { }, }; -static const struct imx95_blk_ctl_dev_data lvds_csr_dev_data = { - .num_clks = ARRAY_SIZE(lvds_clk_dev_data), - .clk_dev_data = lvds_clk_dev_data, +static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = { + .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data), + .clk_dev_data = imx95_lvds_clk_dev_data, .clk_reg_offset = 0, }; -static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = { +static const char * const imx95_disp_engine_parents[] = { + "videopll1", "dsi_pll", "ldb_pll_div7" +}; + +static const struct imx95_blk_ctl_clk_dev_data imx95_dispmix_csr_clk_dev_data[] = { [IMX95_CLK_DISPMIX_ENG0_SEL] = { .name = "disp_engine0_sel", - .parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", }, - .num_parents = 4, + .parent_names = imx95_disp_engine_parents, + .num_parents = ARRAY_SIZE(imx95_disp_engine_parents), .reg = 0, .bit_idx = 0, .bit_width = 2, @@ -232,8 +237,8 @@ static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = { }, [IMX95_CLK_DISPMIX_ENG1_SEL] = { .name = "disp_engine1_sel", - .parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", }, - .num_parents = 4, + .parent_names = imx95_disp_engine_parents, + .num_parents = ARRAY_SIZE(imx95_disp_engine_parents), .reg = 0, .bit_idx = 2, .bit_width = 2, @@ -242,9 +247,9 @@ static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = { } }; -static const struct imx95_blk_ctl_dev_data dispmix_csr_dev_data = { - .num_clks = ARRAY_SIZE(dispmix_csr_clk_dev_data), - .clk_dev_data = dispmix_csr_clk_dev_data, +static const struct imx95_blk_ctl_dev_data imx95_dispmix_csr_dev_data = { + .num_clks = ARRAY_SIZE(imx95_dispmix_csr_clk_dev_data), + .clk_dev_data = imx95_dispmix_csr_clk_dev_data, .clk_reg_offset = 0, }; @@ -296,6 +301,51 @@ static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = { .clk_reg_offset = 0, }; +static const struct imx95_blk_ctl_clk_dev_data imx94_lvds_clk_dev_data[] = { + [IMX94_CLK_DISPMIX_LVDS_CLK_GATE] = { + .name = "lvds_clk_gate", + .parent_names = (const char *[]){ "ldbpll", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 1, + .bit_width = 1, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + .flags2 = CLK_GATE_SET_TO_DISABLE, + }, +}; + +static const struct imx95_blk_ctl_dev_data imx94_lvds_csr_dev_data = { + .num_clks = ARRAY_SIZE(imx94_lvds_clk_dev_data), + .clk_dev_data = imx94_lvds_clk_dev_data, + .clk_reg_offset = 0, + .rpm_enabled = true, +}; + +static const char * const imx94_disp_engine_parents[] = { + "disppix", "ldb_pll_div7" +}; + +static const struct imx95_blk_ctl_clk_dev_data imx94_dispmix_csr_clk_dev_data[] = { + [IMX94_CLK_DISPMIX_CLK_SEL] = { + .name = "disp_clk_sel", + .parent_names = imx94_disp_engine_parents, + .num_parents = ARRAY_SIZE(imx94_disp_engine_parents), + .reg = 0, + .bit_idx = 1, + .bit_width = 1, + .type = CLK_MUX, + .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, + }, +}; + +static const struct imx95_blk_ctl_dev_data imx94_dispmix_csr_dev_data = { + .num_clks = ARRAY_SIZE(imx94_dispmix_csr_clk_dev_data), + .clk_dev_data = imx94_dispmix_csr_clk_dev_data, + .clk_reg_offset = 0, + .rpm_enabled = true, +}; + static int imx95_bc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -338,8 +388,10 @@ static int imx95_bc_probe(struct platform_device *pdev) if (!clk_hw_data) return -ENOMEM; - if (bc_data->rpm_enabled) - pm_runtime_enable(&pdev->dev); + if (bc_data->rpm_enabled) { + devm_pm_runtime_enable(&pdev->dev); + pm_runtime_resume_and_get(&pdev->dev); + } clk_hw_data->num = bc_data->num_clks; hws = clk_hw_data->hws; @@ -379,8 +431,10 @@ static int imx95_bc_probe(struct platform_device *pdev) goto cleanup; } - if (pm_runtime_enabled(bc->dev)) + if (pm_runtime_enabled(bc->dev)) { + pm_runtime_put_sync(&pdev->dev); clk_disable_unprepare(bc->clk_apb); + } return 0; @@ -391,9 +445,6 @@ static int imx95_bc_probe(struct platform_device *pdev) clk_hw_unregister(hws[i]); } - if (bc_data->rpm_enabled) - pm_runtime_disable(&pdev->dev); - return ret; } @@ -462,10 +513,12 @@ static const struct dev_pm_ops imx95_bc_pm_ops = { }; static const struct of_device_id imx95_bc_of_match[] = { + { .compatible = "nxp,imx94-display-csr", .data = &imx94_dispmix_csr_dev_data }, + { .compatible = "nxp,imx94-lvds-csr", .data = &imx94_lvds_csr_dev_data }, { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data }, { .compatible = "nxp,imx95-display-master-csr", }, - { .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data }, - { .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data }, + { .compatible = "nxp,imx95-display-csr", .data = &imx95_dispmix_csr_dev_data }, + { .compatible = "nxp,imx95-lvds-csr", .data = &imx95_lvds_csr_dev_data }, { .compatible = "nxp,imx95-hsio-blk-ctl", .data = &hsio_blk_ctl_dev_data }, { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data }, { .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data}, diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c index 5cf0149dfa15..31220fa7882b 100644 --- a/drivers/clk/imx/clk-pfd.c +++ b/drivers/clk/imx/clk-pfd.c @@ -62,24 +62,26 @@ static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw, return tmp; } -static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pfd_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - u64 tmp = *prate; + u64 tmp = req->best_parent_rate; u8 frac; - tmp = tmp * 18 + rate / 2; - do_div(tmp, rate); + tmp = tmp * 18 + req->rate / 2; + do_div(tmp, req->rate); frac = tmp; if (frac < 12) frac = 12; else if (frac > 35) frac = 35; - tmp = *prate; + tmp = req->best_parent_rate; tmp *= 18; do_div(tmp, frac); - return tmp; + req->rate = tmp; + + return 0; } static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate, @@ -117,7 +119,7 @@ static const struct clk_ops clk_pfd_ops = { .enable = clk_pfd_enable, .disable = clk_pfd_disable, .recalc_rate = clk_pfd_recalc_rate, - .round_rate = clk_pfd_round_rate, + .determine_rate = clk_pfd_determine_rate, .set_rate = clk_pfd_set_rate, .is_enabled = clk_pfd_is_enabled, }; diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index f290981ea13b..36d0e80b55b8 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -216,8 +216,8 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat t->mdiv, t->kdiv); } -static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pll1416x_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_pll14xx *pll = to_clk_pll14xx(hw); const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; @@ -225,22 +225,29 @@ static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate, /* Assuming rate_table is in descending order */ for (i = 0; i < pll->rate_count; i++) - if (rate >= rate_table[i].rate) - return rate_table[i].rate; + if (req->rate >= rate_table[i].rate) { + req->rate = rate_table[i].rate; + + return 0; + } /* return minimum supported value */ - return rate_table[pll->rate_count - 1].rate; + req->rate = rate_table[pll->rate_count - 1].rate; + + return 0; } -static long clk_pll1443x_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pll1443x_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_pll14xx *pll = to_clk_pll14xx(hw); struct imx_pll14xx_rate_table t; - imx_pll14xx_calc_settings(pll, rate, *prate, &t); + imx_pll14xx_calc_settings(pll, req->rate, req->best_parent_rate, &t); - return t.rate; + req->rate = t.rate; + + return 0; } static unsigned long clk_pll14xx_recalc_rate(struct clk_hw *hw, @@ -470,7 +477,7 @@ static const struct clk_ops clk_pll1416x_ops = { .unprepare = clk_pll14xx_unprepare, .is_prepared = clk_pll14xx_is_prepared, .recalc_rate = clk_pll14xx_recalc_rate, - .round_rate = clk_pll1416x_round_rate, + .determine_rate = clk_pll1416x_determine_rate, .set_rate = clk_pll1416x_set_rate, }; @@ -483,7 +490,7 @@ static const struct clk_ops clk_pll1443x_ops = { .unprepare = clk_pll14xx_unprepare, .is_prepared = clk_pll14xx_is_prepared, .recalc_rate = clk_pll14xx_recalc_rate, - .round_rate = clk_pll1443x_round_rate, + .determine_rate = clk_pll1443x_determine_rate, .set_rate = clk_pll1443x_set_rate, }; diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c index ff17f0664faa..bb497ad5e0ae 100644 --- a/drivers/clk/imx/clk-pllv2.c +++ b/drivers/clk/imx/clk-pllv2.c @@ -178,18 +178,25 @@ static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv2_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { u32 dp_op, dp_mfd, dp_mfn; int ret; - ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn); - if (ret) - return ret; + ret = __clk_pllv2_set_rate(req->rate, req->best_parent_rate, &dp_op, + &dp_mfd, &dp_mfn); + if (ret) { + req->rate = ret; - return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN, - dp_op, dp_mfd, dp_mfn); + return 0; + } + + req->rate = __clk_pllv2_recalc_rate(req->best_parent_rate, + MXC_PLL_DP_CTL_DPDCK0_2_EN, dp_op, + dp_mfd, dp_mfn); + + return 0; } static int clk_pllv2_prepare(struct clk_hw *hw) @@ -235,7 +242,7 @@ static const struct clk_ops clk_pllv2_ops = { .prepare = clk_pllv2_prepare, .unprepare = clk_pllv2_unprepare, .recalc_rate = clk_pllv2_recalc_rate, - .round_rate = clk_pllv2_round_rate, + .determine_rate = clk_pllv2_determine_rate, .set_rate = clk_pllv2_set_rate, }; diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index 11fb238ee8f0..b99508367bcb 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -117,13 +117,14 @@ static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, return (div == 1) ? parent_rate * 22 : parent_rate * 20; } -static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv3_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long parent_rate = *prate; + unsigned long parent_rate = req->best_parent_rate; - return (rate >= parent_rate * 22) ? parent_rate * 22 : - parent_rate * 20; + req->rate = (req->rate >= parent_rate * 22) ? parent_rate * 22 : parent_rate * 20; + + return 0; } static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate, @@ -152,7 +153,7 @@ static const struct clk_ops clk_pllv3_ops = { .unprepare = clk_pllv3_unprepare, .is_prepared = clk_pllv3_is_prepared, .recalc_rate = clk_pllv3_recalc_rate, - .round_rate = clk_pllv3_round_rate, + .determine_rate = clk_pllv3_determine_rate, .set_rate = clk_pllv3_set_rate, }; @@ -165,21 +166,23 @@ static unsigned long clk_pllv3_sys_recalc_rate(struct clk_hw *hw, return parent_rate * div / 2; } -static long clk_pllv3_sys_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv3_sys_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long parent_rate = *prate; + unsigned long parent_rate = req->best_parent_rate; unsigned long min_rate = parent_rate * 54 / 2; unsigned long max_rate = parent_rate * 108 / 2; u32 div; - if (rate > max_rate) - rate = max_rate; - else if (rate < min_rate) - rate = min_rate; - div = rate * 2 / parent_rate; + if (req->rate > max_rate) + req->rate = max_rate; + else if (req->rate < min_rate) + req->rate = min_rate; + div = req->rate * 2 / parent_rate; - return parent_rate * div / 2; + req->rate = parent_rate * div / 2; + + return 0; } static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate, @@ -207,7 +210,7 @@ static const struct clk_ops clk_pllv3_sys_ops = { .unprepare = clk_pllv3_unprepare, .is_prepared = clk_pllv3_is_prepared, .recalc_rate = clk_pllv3_sys_recalc_rate, - .round_rate = clk_pllv3_sys_round_rate, + .determine_rate = clk_pllv3_sys_determine_rate, .set_rate = clk_pllv3_sys_set_rate, }; @@ -226,10 +229,10 @@ static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw, return parent_rate * div + (unsigned long)temp64; } -static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv3_av_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long parent_rate = *prate; + unsigned long parent_rate = req->best_parent_rate; unsigned long min_rate = parent_rate * 27; unsigned long max_rate = parent_rate * 54; u32 div; @@ -237,16 +240,16 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, u32 max_mfd = 0x3FFFFFFF; u64 temp64; - if (rate > max_rate) - rate = max_rate; - else if (rate < min_rate) - rate = min_rate; + if (req->rate > max_rate) + req->rate = max_rate; + else if (req->rate < min_rate) + req->rate = min_rate; if (parent_rate <= max_mfd) mfd = parent_rate; - div = rate / parent_rate; - temp64 = (u64) (rate - div * parent_rate); + div = req->rate / parent_rate; + temp64 = (u64) (req->rate - div * parent_rate); temp64 *= mfd; temp64 = div64_ul(temp64, parent_rate); mfn = temp64; @@ -255,7 +258,9 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, temp64 *= mfn; do_div(temp64, mfd); - return parent_rate * div + (unsigned long)temp64; + req->rate = parent_rate * div + (unsigned long)temp64; + + return 0; } static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, @@ -296,7 +301,7 @@ static const struct clk_ops clk_pllv3_av_ops = { .unprepare = clk_pllv3_unprepare, .is_prepared = clk_pllv3_is_prepared, .recalc_rate = clk_pllv3_av_recalc_rate, - .round_rate = clk_pllv3_av_round_rate, + .determine_rate = clk_pllv3_av_determine_rate, .set_rate = clk_pllv3_av_set_rate, }; @@ -355,12 +360,15 @@ static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw, return clk_pllv3_vf610_mf_to_rate(parent_rate, mf); } -static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv3_vf610_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(*prate, rate); + struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(req->best_parent_rate, + req->rate); - return clk_pllv3_vf610_mf_to_rate(*prate, mf); + req->rate = clk_pllv3_vf610_mf_to_rate(req->best_parent_rate, mf); + + return 0; } static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate, @@ -389,7 +397,7 @@ static const struct clk_ops clk_pllv3_vf610_ops = { .unprepare = clk_pllv3_unprepare, .is_prepared = clk_pllv3_is_prepared, .recalc_rate = clk_pllv3_vf610_recalc_rate, - .round_rate = clk_pllv3_vf610_round_rate, + .determine_rate = clk_pllv3_vf610_determine_rate, .set_rate = clk_pllv3_vf610_set_rate, }; diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c index 9b136c951762..01d05b5d5438 100644 --- a/drivers/clk/imx/clk-pllv4.c +++ b/drivers/clk/imx/clk-pllv4.c @@ -95,11 +95,11 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, return (parent_rate * mult) + (u32)temp64; } -static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pllv4_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_pllv4 *pll = to_clk_pllv4(hw); - unsigned long parent_rate = *prate; + unsigned long parent_rate = req->best_parent_rate; unsigned long round_rate, i; u32 mfn, mfd = DEFAULT_MFD; bool found = false; @@ -107,7 +107,7 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, u32 mult; if (pll->use_mult_range) { - temp64 = (u64)rate; + temp64 = (u64) req->rate; do_div(temp64, parent_rate); mult = temp64; if (mult >= pllv4_mult_range[1] && @@ -118,7 +118,7 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, } else { for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { round_rate = parent_rate * pllv4_mult_table[i]; - if (rate >= round_rate) { + if (req->rate >= round_rate) { found = true; break; } @@ -127,14 +127,16 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, if (!found) { pr_warn("%s: unable to round rate %lu, parent rate %lu\n", - clk_hw_get_name(hw), rate, parent_rate); + clk_hw_get_name(hw), req->rate, parent_rate); + req->rate = 0; + return 0; } if (parent_rate <= MAX_MFD) mfd = parent_rate; - temp64 = (u64)(rate - round_rate); + temp64 = (u64)(req->rate - round_rate); temp64 *= mfd; do_div(temp64, parent_rate); mfn = temp64; @@ -145,14 +147,19 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, * pair of mfn/mfd, we simply return the round_rate without using * the frac part. */ - if (mfn >= mfd) - return round_rate; + if (mfn >= mfd) { + req->rate = round_rate; + + return 0; + } temp64 = (u64)parent_rate; temp64 *= mfn; do_div(temp64, mfd); - return round_rate + (u32)temp64; + req->rate = round_rate + (u32)temp64; + + return 0; } static bool clk_pllv4_is_valid_mult(struct clk_pllv4 *pll, unsigned int mult) @@ -229,7 +236,7 @@ static void clk_pllv4_unprepare(struct clk_hw *hw) static const struct clk_ops clk_pllv4_ops = { .recalc_rate = clk_pllv4_recalc_rate, - .round_rate = clk_pllv4_round_rate, + .determine_rate = clk_pllv4_determine_rate, .set_rate = clk_pllv4_set_rate, .prepare = clk_pllv4_prepare, .unprepare = clk_pllv4_unprepare, diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index b27186aaf2a1..34c9dc1fb20e 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -269,24 +269,6 @@ static int clk_scu_determine_rate(struct clk_hw *hw, return 0; } -/* - * clk_scu_round_rate - Round clock rate for a SCU clock - * @hw: clock to round rate for - * @rate: rate to round - * @parent_rate: parent rate provided by common clock framework, not used - * - * Returns the current clock rate, or zero in failure. - */ -static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - /* - * Assume we support all the requested rate and let the SCU firmware - * to handle the left work - */ - return rate; -} - static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -454,7 +436,7 @@ static const struct clk_ops clk_scu_ops = { static const struct clk_ops clk_scu_cpu_ops = { .recalc_rate = clk_scu_recalc_rate, - .round_rate = clk_scu_round_rate, + .determine_rate = clk_scu_determine_rate, .set_rate = clk_scu_atf_set_cpu_rate, .prepare = clk_scu_prepare, .unprepare = clk_scu_unprepare, @@ -462,7 +444,7 @@ static const struct clk_ops clk_scu_cpu_ops = { static const struct clk_ops clk_scu_pi_ops = { .recalc_rate = clk_scu_recalc_rate, - .round_rate = clk_scu_round_rate, + .determine_rate = clk_scu_determine_rate, .set_rate = clk_scu_set_rate, }; @@ -567,7 +549,6 @@ static int imx_clk_scu_probe(struct platform_device *pdev) if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) || (clk->rsrc == IMX_SC_R_A72))) { - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); } @@ -729,7 +710,7 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name, if (ret) goto put_device; - /* For API backwards compatiblilty, simply return NULL for success */ + /* For API backwards compatibility, simply return NULL for success */ return NULL; put_device: @@ -766,15 +747,15 @@ static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw, return err ? 0 : rate; } -static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_gpr_div_scu_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - if (rate < *prate) - rate = *prate / 2; + if (req->rate < req->best_parent_rate) + req->rate = req->best_parent_rate / 2; else - rate = *prate; + req->rate = req->best_parent_rate; - return rate; + return 0; } static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, @@ -793,7 +774,7 @@ static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops clk_gpr_div_scu_ops = { .recalc_rate = clk_gpr_div_scu_recalc_rate, - .round_rate = clk_gpr_div_scu_round_rate, + .determine_rate = clk_gpr_div_scu_determine_rate, .set_rate = clk_gpr_div_scu_set_rate, }; diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h index 99da9bd86e63..0d417d69dab7 100644 --- a/drivers/clk/ingenic/cgu.h +++ b/drivers/clk/ingenic/cgu.h @@ -239,7 +239,7 @@ ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, * * Register the clocks described by the CGU with the common clock framework. * - * Return: 0 on success or -errno if unsuccesful. + * Return: 0 on success or -errno if unsuccessful. */ int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu); diff --git a/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso b/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso new file mode 100644 index 000000000000..760717da3235 --- /dev/null +++ b/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + kunit-clock-controller { + compatible = "test,clk-hw-get-dev-of-node"; + #clock-cells = <0>; + }; +}; diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index ff003dc5ab20..7197d23543b8 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -5,6 +5,7 @@ menu "Clock support for Amlogic platforms" config COMMON_CLK_MESON_REGMAP tristate select REGMAP + select MFD_SYSCON config COMMON_CLK_MESON_DUALDIV tristate @@ -106,7 +107,8 @@ config COMMON_CLK_AXG_AUDIO select COMMON_CLK_MESON_SCLK_DIV select COMMON_CLK_MESON_CLKC_UTILS select REGMAP_MMIO - select RESET_CONTROLLER + select AUXILIARY_BUS + imply RESET_MESON_AUX help Support for the audio clock controller on AmLogic A113D devices, aka axg, Say Y if you want audio subsystem to work. diff --git a/drivers/clk/meson/a1-peripherals.c b/drivers/clk/meson/a1-peripherals.c index 36489e0f948a..1f5d445d44fe 100644 --- a/drivers/clk/meson/a1-peripherals.c +++ b/drivers/clk/meson/a1-peripherals.c @@ -10,13 +10,42 @@ #include #include #include -#include "a1-peripherals.h" #include "clk-dualdiv.h" #include "clk-regmap.h" #include "meson-clkc-utils.h" #include +#define SYS_OSCIN_CTRL 0x0 +#define RTC_BY_OSCIN_CTRL0 0x4 +#define RTC_BY_OSCIN_CTRL1 0x8 +#define RTC_CTRL 0xc +#define SYS_CLK_CTRL0 0x10 +#define SYS_CLK_EN0 0x1c +#define SYS_CLK_EN1 0x20 +#define AXI_CLK_EN 0x24 +#define DSPA_CLK_EN 0x28 +#define DSPB_CLK_EN 0x2c +#define DSPA_CLK_CTRL0 0x30 +#define DSPB_CLK_CTRL0 0x34 +#define CLK12_24_CTRL 0x38 +#define GEN_CLK_CTRL 0x3c +#define SAR_ADC_CLK_CTRL 0xc0 +#define PWM_CLK_AB_CTRL 0xc4 +#define PWM_CLK_CD_CTRL 0xc8 +#define PWM_CLK_EF_CTRL 0xcc +#define SPICC_CLK_CTRL 0xd0 +#define TS_CLK_CTRL 0xd4 +#define SPIFC_CLK_CTRL 0xd8 +#define USB_BUSCLK_CTRL 0xdc +#define SD_EMMC_CLK_CTRL 0xe0 +#define CECA_CLK_CTRL0 0xe4 +#define CECA_CLK_CTRL1 0xe8 +#define CECB_CLK_CTRL0 0xec +#define CECB_CLK_CTRL1 0xf0 +#define PSRAM_CLK_CTRL 0xf4 +#define DMC_CLK_CTRL 0xf8 + static struct clk_regmap xtal_in = { .data = &(struct clk_regmap_gate_data){ .offset = SYS_OSCIN_CTRL, @@ -2026,163 +2055,6 @@ static struct clk_hw *a1_periphs_hw_clks[] = { [CLKID_DMC_SEL2] = &dmc_sel2.hw, }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const a1_periphs_regmaps[] = { - &xtal_in, - &fixpll_in, - &usb_phy_in, - &usb_ctrl_in, - &hifipll_in, - &syspll_in, - &dds_in, - &sys, - &clktree, - &reset_ctrl, - &analog_ctrl, - &pwr_ctrl, - &pad_ctrl, - &sys_ctrl, - &temp_sensor, - &am2axi_dev, - &spicc_b, - &spicc_a, - &msr, - &audio, - &jtag_ctrl, - &saradc_en, - &pwm_ef, - &pwm_cd, - &pwm_ab, - &cec, - &i2c_s, - &ir_ctrl, - &i2c_m_d, - &i2c_m_c, - &i2c_m_b, - &i2c_m_a, - &acodec, - &otp, - &sd_emmc_a, - &usb_phy, - &usb_ctrl, - &sys_dspb, - &sys_dspa, - &dma, - &irq_ctrl, - &nic, - &gic, - &uart_c, - &uart_b, - &uart_a, - &sys_psram, - &rsa, - &coresight, - &am2axi_vad, - &audio_vad, - &axi_dmc, - &axi_psram, - &ramb, - &rama, - &axi_spifc, - &axi_nic, - &axi_dma, - &cpu_ctrl, - &rom, - &prod_i2c, - &dspa_sel, - &dspb_sel, - &dspa_en, - &dspa_en_nic, - &dspb_en, - &dspb_en_nic, - &rtc, - &ceca_32k_out, - &cecb_32k_out, - &clk_24m, - &clk_12m, - &fclk_div2_divn, - &gen, - &saradc_sel, - &saradc, - &pwm_a, - &pwm_b, - &pwm_c, - &pwm_d, - &pwm_e, - &pwm_f, - &spicc, - &ts, - &spifc, - &usb_bus, - &sd_emmc, - &psram, - &dmc, - &sys_a_sel, - &sys_a_div, - &sys_a, - &sys_b_sel, - &sys_b_div, - &sys_b, - &dspa_a_sel, - &dspa_a_div, - &dspa_a, - &dspa_b_sel, - &dspa_b_div, - &dspa_b, - &dspb_a_sel, - &dspb_a_div, - &dspb_a, - &dspb_b_sel, - &dspb_b_div, - &dspb_b, - &rtc_32k_in, - &rtc_32k_div, - &rtc_32k_xtal, - &rtc_32k_sel, - &cecb_32k_in, - &cecb_32k_div, - &cecb_32k_sel_pre, - &cecb_32k_sel, - &ceca_32k_in, - &ceca_32k_div, - &ceca_32k_sel_pre, - &ceca_32k_sel, - &fclk_div2_divn_pre, - &gen_sel, - &gen_div, - &saradc_div, - &pwm_a_sel, - &pwm_a_div, - &pwm_b_sel, - &pwm_b_div, - &pwm_c_sel, - &pwm_c_div, - &pwm_d_sel, - &pwm_d_div, - &pwm_e_sel, - &pwm_e_div, - &pwm_f_sel, - &pwm_f_div, - &spicc_sel, - &spicc_div, - &spicc_sel2, - &ts_div, - &spifc_sel, - &spifc_div, - &spifc_sel2, - &usb_bus_sel, - &usb_bus_div, - &sd_emmc_sel, - &sd_emmc_div, - &sd_emmc_sel2, - &psram_sel, - &psram_div, - &psram_sel2, - &dmc_sel, - &dmc_div, - &dmc_sel2, -}; - static const struct regmap_config a1_periphs_regmap_cfg = { .reg_bits = 32, .val_bits = 32, @@ -2200,7 +2072,7 @@ static int meson_a1_periphs_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; void __iomem *base; struct regmap *map; - int clkid, i, err; + int clkid, err; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -2212,10 +2084,6 @@ static int meson_a1_periphs_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(map), "can't init regmap mmio region\n"); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(a1_periphs_regmaps); i++) - a1_periphs_regmaps[i]->map = map; - for (clkid = 0; clkid < a1_periphs_clks.num; clkid++) { err = devm_clk_hw_register(dev, a1_periphs_clks.hws[clkid]); if (err) diff --git a/drivers/clk/meson/a1-peripherals.h b/drivers/clk/meson/a1-peripherals.h deleted file mode 100644 index 26de8530184a..000000000000 --- a/drivers/clk/meson/a1-peripherals.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Amlogic A1 Peripherals Clock Controller internals - * - * Copyright (c) 2019 Amlogic, Inc. All rights reserved. - * Author: Jian Hu - * - * Copyright (c) 2023, SberDevices. All Rights Reserved. - * Author: Dmitry Rokosov - */ - -#ifndef __A1_PERIPHERALS_H -#define __A1_PERIPHERALS_H - -/* peripherals clock controller register offset */ -#define SYS_OSCIN_CTRL 0x0 -#define RTC_BY_OSCIN_CTRL0 0x4 -#define RTC_BY_OSCIN_CTRL1 0x8 -#define RTC_CTRL 0xc -#define SYS_CLK_CTRL0 0x10 -#define SYS_CLK_EN0 0x1c -#define SYS_CLK_EN1 0x20 -#define AXI_CLK_EN 0x24 -#define DSPA_CLK_EN 0x28 -#define DSPB_CLK_EN 0x2c -#define DSPA_CLK_CTRL0 0x30 -#define DSPB_CLK_CTRL0 0x34 -#define CLK12_24_CTRL 0x38 -#define GEN_CLK_CTRL 0x3c -#define SAR_ADC_CLK_CTRL 0xc0 -#define PWM_CLK_AB_CTRL 0xc4 -#define PWM_CLK_CD_CTRL 0xc8 -#define PWM_CLK_EF_CTRL 0xcc -#define SPICC_CLK_CTRL 0xd0 -#define TS_CLK_CTRL 0xd4 -#define SPIFC_CLK_CTRL 0xd8 -#define USB_BUSCLK_CTRL 0xdc -#define SD_EMMC_CLK_CTRL 0xe0 -#define CECA_CLK_CTRL0 0xe4 -#define CECA_CLK_CTRL1 0xe8 -#define CECB_CLK_CTRL0 0xec -#define CECB_CLK_CTRL1 0xf0 -#define PSRAM_CLK_CTRL 0xf4 -#define DMC_CLK_CTRL 0xf8 - -#endif /* __A1_PERIPHERALS_H */ diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c index 86d8159f3319..dabd4fad1f57 100644 --- a/drivers/clk/meson/a1-pll.c +++ b/drivers/clk/meson/a1-pll.c @@ -10,10 +10,20 @@ #include #include #include -#include "a1-pll.h" +#include "clk-pll.h" #include "clk-regmap.h" #include "meson-clkc-utils.h" +#define ANACTRL_FIXPLL_CTRL0 0x0 +#define ANACTRL_FIXPLL_CTRL1 0x4 +#define ANACTRL_FIXPLL_STS 0x14 +#define ANACTRL_HIFIPLL_CTRL0 0xc0 +#define ANACTRL_HIFIPLL_CTRL1 0xc4 +#define ANACTRL_HIFIPLL_CTRL2 0xc8 +#define ANACTRL_HIFIPLL_CTRL3 0xcc +#define ANACTRL_HIFIPLL_CTRL4 0xd0 +#define ANACTRL_HIFIPLL_STS 0xd4 + #include static struct clk_regmap fixed_pll_dco = { @@ -285,16 +295,6 @@ static struct clk_hw *a1_pll_hw_clks[] = { [CLKID_HIFI_PLL] = &hifi_pll.hw, }; -static struct clk_regmap *const a1_pll_regmaps[] = { - &fixed_pll_dco, - &fixed_pll, - &fclk_div2, - &fclk_div3, - &fclk_div5, - &fclk_div7, - &hifi_pll, -}; - static const struct regmap_config a1_pll_regmap_cfg = { .reg_bits = 32, .val_bits = 32, @@ -312,7 +312,7 @@ static int meson_a1_pll_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; void __iomem *base; struct regmap *map; - int clkid, i, err; + int clkid, err; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -324,10 +324,6 @@ static int meson_a1_pll_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(map), "can't init regmap mmio region\n"); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(a1_pll_regmaps); i++) - a1_pll_regmaps[i]->map = map; - /* Register clocks */ for (clkid = 0; clkid < a1_pll_clks.num; clkid++) { err = devm_clk_hw_register(dev, a1_pll_clks.hws[clkid]); diff --git a/drivers/clk/meson/a1-pll.h b/drivers/clk/meson/a1-pll.h deleted file mode 100644 index 4be17b2bf383..000000000000 --- a/drivers/clk/meson/a1-pll.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Amlogic A1 PLL Clock Controller internals - * - * Copyright (c) 2019 Amlogic, Inc. All rights reserved. - * Author: Jian Hu - * - * Copyright (c) 2023, SberDevices. All Rights Reserved. - * Author: Dmitry Rokosov - */ - -#ifndef __A1_PLL_H -#define __A1_PLL_H - -#include "clk-pll.h" - -/* PLL register offset */ -#define ANACTRL_FIXPLL_CTRL0 0x0 -#define ANACTRL_FIXPLL_CTRL1 0x4 -#define ANACTRL_FIXPLL_STS 0x14 -#define ANACTRL_HIFIPLL_CTRL0 0xc0 -#define ANACTRL_HIFIPLL_CTRL1 0xc4 -#define ANACTRL_HIFIPLL_CTRL2 0xc8 -#define ANACTRL_HIFIPLL_CTRL3 0xcc -#define ANACTRL_HIFIPLL_CTRL4 0xd0 -#define ANACTRL_HIFIPLL_STS 0xd4 - -#endif /* __A1_PLL_H */ diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c index f44091ffb57d..cd5d0b5ebdb2 100644 --- a/drivers/clk/meson/axg-aoclk.c +++ b/drivers/clk/meson/axg-aoclk.c @@ -270,26 +270,6 @@ static const unsigned int axg_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_regmap *axg_aoclk_regmap[] = { - &axg_aoclk_remote, - &axg_aoclk_i2c_master, - &axg_aoclk_i2c_slave, - &axg_aoclk_uart1, - &axg_aoclk_uart2, - &axg_aoclk_ir_blaster, - &axg_aoclk_saradc, - &axg_aoclk_cts_oscin, - &axg_aoclk_32k_pre, - &axg_aoclk_32k_div, - &axg_aoclk_32k_sel, - &axg_aoclk_32k, - &axg_aoclk_cts_rtc_oscin, - &axg_aoclk_clk81, - &axg_aoclk_saradc_mux, - &axg_aoclk_saradc_div, - &axg_aoclk_saradc_gate, -}; - static struct clk_hw *axg_aoclk_hw_clks[] = { [CLKID_AO_REMOTE] = &axg_aoclk_remote.hw, [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master.hw, @@ -314,8 +294,6 @@ static const struct meson_aoclk_data axg_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(axg_aoclk_reset), .reset = axg_aoclk_reset, - .num_clks = ARRAY_SIZE(axg_aoclk_regmap), - .clks = axg_aoclk_regmap, .hw_clks = { .hws = axg_aoclk_hw_clks, .num = ARRAY_SIZE(axg_aoclk_hw_clks), diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 9df627b142f8..fd7eca652261 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -4,6 +4,7 @@ * Author: Jerome Brunet */ +#include #include #include #include @@ -12,17 +13,70 @@ #include #include #include -#include #include #include "meson-clkc-utils.h" -#include "axg-audio.h" #include "clk-regmap.h" #include "clk-phase.h" #include "sclk-div.h" #include +/* Audio clock register offsets */ +#define AUDIO_CLK_GATE_EN 0x000 +#define AUDIO_MCLK_A_CTRL 0x004 +#define AUDIO_MCLK_B_CTRL 0x008 +#define AUDIO_MCLK_C_CTRL 0x00C +#define AUDIO_MCLK_D_CTRL 0x010 +#define AUDIO_MCLK_E_CTRL 0x014 +#define AUDIO_MCLK_F_CTRL 0x018 +#define AUDIO_MST_PAD_CTRL0 0x01c +#define AUDIO_MST_PAD_CTRL1 0x020 +#define AUDIO_SW_RESET 0x024 +#define AUDIO_MST_A_SCLK_CTRL0 0x040 +#define AUDIO_MST_A_SCLK_CTRL1 0x044 +#define AUDIO_MST_B_SCLK_CTRL0 0x048 +#define AUDIO_MST_B_SCLK_CTRL1 0x04C +#define AUDIO_MST_C_SCLK_CTRL0 0x050 +#define AUDIO_MST_C_SCLK_CTRL1 0x054 +#define AUDIO_MST_D_SCLK_CTRL0 0x058 +#define AUDIO_MST_D_SCLK_CTRL1 0x05C +#define AUDIO_MST_E_SCLK_CTRL0 0x060 +#define AUDIO_MST_E_SCLK_CTRL1 0x064 +#define AUDIO_MST_F_SCLK_CTRL0 0x068 +#define AUDIO_MST_F_SCLK_CTRL1 0x06C +#define AUDIO_CLK_TDMIN_A_CTRL 0x080 +#define AUDIO_CLK_TDMIN_B_CTRL 0x084 +#define AUDIO_CLK_TDMIN_C_CTRL 0x088 +#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C +#define AUDIO_CLK_TDMOUT_A_CTRL 0x090 +#define AUDIO_CLK_TDMOUT_B_CTRL 0x094 +#define AUDIO_CLK_TDMOUT_C_CTRL 0x098 +#define AUDIO_CLK_SPDIFIN_CTRL 0x09C +#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0 +#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4 +#define AUDIO_CLK_LOCKER_CTRL 0x0A8 +#define AUDIO_CLK_PDMIN_CTRL0 0x0AC +#define AUDIO_CLK_PDMIN_CTRL1 0x0B0 +#define AUDIO_CLK_SPDIFOUT_B_CTRL 0x0B4 + +/* SM1 introduce new register and some shifts :( */ +#define AUDIO_CLK_GATE_EN1 0x004 +#define AUDIO_SM1_MCLK_A_CTRL 0x008 +#define AUDIO_SM1_MCLK_B_CTRL 0x00C +#define AUDIO_SM1_MCLK_C_CTRL 0x010 +#define AUDIO_SM1_MCLK_D_CTRL 0x014 +#define AUDIO_SM1_MCLK_E_CTRL 0x018 +#define AUDIO_SM1_MCLK_F_CTRL 0x01C +#define AUDIO_SM1_MST_PAD_CTRL0 0x020 +#define AUDIO_SM1_MST_PAD_CTRL1 0x024 +#define AUDIO_SM1_SW_RESET0 0x028 +#define AUDIO_SM1_SW_RESET1 0x02C +#define AUDIO_CLK81_CTRL 0x030 +#define AUDIO_CLK81_EN 0x034 +#define AUDIO_EARCRX_CMDC_CLK_CTRL 0x0D0 +#define AUDIO_EARCRX_DMAC_CLK_CTRL 0x0D4 + #define AUD_GATE(_name, _reg, _bit, _pname, _iflags) { \ .data = &(struct clk_regmap_gate_data){ \ .offset = (_reg), \ @@ -1257,505 +1311,6 @@ static struct clk_hw *sm1_audio_hw_clks[] = { [AUD_CLKID_EARCRX_DMAC] = &sm1_earcrx_dmac_clk.hw, }; - -/* Convenience table to populate regmap in .probe(). */ -static struct clk_regmap *const axg_clk_regmaps[] = { - &ddr_arb, - &pdm, - &tdmin_a, - &tdmin_b, - &tdmin_c, - &tdmin_lb, - &tdmout_a, - &tdmout_b, - &tdmout_c, - &frddr_a, - &frddr_b, - &frddr_c, - &toddr_a, - &toddr_b, - &toddr_c, - &loopback, - &spdifin, - &spdifout, - &resample, - &power_detect, - &mst_a_mclk_sel, - &mst_b_mclk_sel, - &mst_c_mclk_sel, - &mst_d_mclk_sel, - &mst_e_mclk_sel, - &mst_f_mclk_sel, - &mst_a_mclk_div, - &mst_b_mclk_div, - &mst_c_mclk_div, - &mst_d_mclk_div, - &mst_e_mclk_div, - &mst_f_mclk_div, - &mst_a_mclk, - &mst_b_mclk, - &mst_c_mclk, - &mst_d_mclk, - &mst_e_mclk, - &mst_f_mclk, - &spdifout_clk_sel, - &spdifout_clk_div, - &spdifout_clk, - &spdifin_clk_sel, - &spdifin_clk_div, - &spdifin_clk, - &pdm_dclk_sel, - &pdm_dclk_div, - &pdm_dclk, - &pdm_sysclk_sel, - &pdm_sysclk_div, - &pdm_sysclk, - &mst_a_sclk_pre_en, - &mst_b_sclk_pre_en, - &mst_c_sclk_pre_en, - &mst_d_sclk_pre_en, - &mst_e_sclk_pre_en, - &mst_f_sclk_pre_en, - &mst_a_sclk_div, - &mst_b_sclk_div, - &mst_c_sclk_div, - &mst_d_sclk_div, - &mst_e_sclk_div, - &mst_f_sclk_div, - &mst_a_sclk_post_en, - &mst_b_sclk_post_en, - &mst_c_sclk_post_en, - &mst_d_sclk_post_en, - &mst_e_sclk_post_en, - &mst_f_sclk_post_en, - &mst_a_sclk, - &mst_b_sclk, - &mst_c_sclk, - &mst_d_sclk, - &mst_e_sclk, - &mst_f_sclk, - &mst_a_lrclk_div, - &mst_b_lrclk_div, - &mst_c_lrclk_div, - &mst_d_lrclk_div, - &mst_e_lrclk_div, - &mst_f_lrclk_div, - &mst_a_lrclk, - &mst_b_lrclk, - &mst_c_lrclk, - &mst_d_lrclk, - &mst_e_lrclk, - &mst_f_lrclk, - &tdmin_a_sclk_sel, - &tdmin_b_sclk_sel, - &tdmin_c_sclk_sel, - &tdmin_lb_sclk_sel, - &tdmout_a_sclk_sel, - &tdmout_b_sclk_sel, - &tdmout_c_sclk_sel, - &tdmin_a_sclk_pre_en, - &tdmin_b_sclk_pre_en, - &tdmin_c_sclk_pre_en, - &tdmin_lb_sclk_pre_en, - &tdmout_a_sclk_pre_en, - &tdmout_b_sclk_pre_en, - &tdmout_c_sclk_pre_en, - &tdmin_a_sclk_post_en, - &tdmin_b_sclk_post_en, - &tdmin_c_sclk_post_en, - &tdmin_lb_sclk_post_en, - &tdmout_a_sclk_post_en, - &tdmout_b_sclk_post_en, - &tdmout_c_sclk_post_en, - &tdmin_a_sclk, - &tdmin_b_sclk, - &tdmin_c_sclk, - &tdmin_lb_sclk, - &axg_tdmout_a_sclk, - &axg_tdmout_b_sclk, - &axg_tdmout_c_sclk, - &tdmin_a_lrclk, - &tdmin_b_lrclk, - &tdmin_c_lrclk, - &tdmin_lb_lrclk, - &tdmout_a_lrclk, - &tdmout_b_lrclk, - &tdmout_c_lrclk, -}; - -static struct clk_regmap *const g12a_clk_regmaps[] = { - &ddr_arb, - &pdm, - &tdmin_a, - &tdmin_b, - &tdmin_c, - &tdmin_lb, - &tdmout_a, - &tdmout_b, - &tdmout_c, - &frddr_a, - &frddr_b, - &frddr_c, - &toddr_a, - &toddr_b, - &toddr_c, - &loopback, - &spdifin, - &spdifout, - &resample, - &power_detect, - &spdifout_b, - &mst_a_mclk_sel, - &mst_b_mclk_sel, - &mst_c_mclk_sel, - &mst_d_mclk_sel, - &mst_e_mclk_sel, - &mst_f_mclk_sel, - &mst_a_mclk_div, - &mst_b_mclk_div, - &mst_c_mclk_div, - &mst_d_mclk_div, - &mst_e_mclk_div, - &mst_f_mclk_div, - &mst_a_mclk, - &mst_b_mclk, - &mst_c_mclk, - &mst_d_mclk, - &mst_e_mclk, - &mst_f_mclk, - &spdifout_clk_sel, - &spdifout_clk_div, - &spdifout_clk, - &spdifin_clk_sel, - &spdifin_clk_div, - &spdifin_clk, - &pdm_dclk_sel, - &pdm_dclk_div, - &pdm_dclk, - &pdm_sysclk_sel, - &pdm_sysclk_div, - &pdm_sysclk, - &mst_a_sclk_pre_en, - &mst_b_sclk_pre_en, - &mst_c_sclk_pre_en, - &mst_d_sclk_pre_en, - &mst_e_sclk_pre_en, - &mst_f_sclk_pre_en, - &mst_a_sclk_div, - &mst_b_sclk_div, - &mst_c_sclk_div, - &mst_d_sclk_div, - &mst_e_sclk_div, - &mst_f_sclk_div, - &mst_a_sclk_post_en, - &mst_b_sclk_post_en, - &mst_c_sclk_post_en, - &mst_d_sclk_post_en, - &mst_e_sclk_post_en, - &mst_f_sclk_post_en, - &mst_a_sclk, - &mst_b_sclk, - &mst_c_sclk, - &mst_d_sclk, - &mst_e_sclk, - &mst_f_sclk, - &mst_a_lrclk_div, - &mst_b_lrclk_div, - &mst_c_lrclk_div, - &mst_d_lrclk_div, - &mst_e_lrclk_div, - &mst_f_lrclk_div, - &mst_a_lrclk, - &mst_b_lrclk, - &mst_c_lrclk, - &mst_d_lrclk, - &mst_e_lrclk, - &mst_f_lrclk, - &tdmin_a_sclk_sel, - &tdmin_b_sclk_sel, - &tdmin_c_sclk_sel, - &tdmin_lb_sclk_sel, - &tdmout_a_sclk_sel, - &tdmout_b_sclk_sel, - &tdmout_c_sclk_sel, - &tdmin_a_sclk_pre_en, - &tdmin_b_sclk_pre_en, - &tdmin_c_sclk_pre_en, - &tdmin_lb_sclk_pre_en, - &tdmout_a_sclk_pre_en, - &tdmout_b_sclk_pre_en, - &tdmout_c_sclk_pre_en, - &tdmin_a_sclk_post_en, - &tdmin_b_sclk_post_en, - &tdmin_c_sclk_post_en, - &tdmin_lb_sclk_post_en, - &tdmout_a_sclk_post_en, - &tdmout_b_sclk_post_en, - &tdmout_c_sclk_post_en, - &tdmin_a_sclk, - &tdmin_b_sclk, - &tdmin_c_sclk, - &tdmin_lb_sclk, - &g12a_tdmout_a_sclk, - &g12a_tdmout_b_sclk, - &g12a_tdmout_c_sclk, - &tdmin_a_lrclk, - &tdmin_b_lrclk, - &tdmin_c_lrclk, - &tdmin_lb_lrclk, - &tdmout_a_lrclk, - &tdmout_b_lrclk, - &tdmout_c_lrclk, - &spdifout_b_clk_sel, - &spdifout_b_clk_div, - &spdifout_b_clk, - &g12a_tdm_mclk_pad_0, - &g12a_tdm_mclk_pad_1, - &g12a_tdm_lrclk_pad_0, - &g12a_tdm_lrclk_pad_1, - &g12a_tdm_lrclk_pad_2, - &g12a_tdm_sclk_pad_0, - &g12a_tdm_sclk_pad_1, - &g12a_tdm_sclk_pad_2, - &toram, - &eqdrc, -}; - -static struct clk_regmap *const sm1_clk_regmaps[] = { - &ddr_arb, - &pdm, - &tdmin_a, - &tdmin_b, - &tdmin_c, - &tdmin_lb, - &tdmout_a, - &tdmout_b, - &tdmout_c, - &frddr_a, - &frddr_b, - &frddr_c, - &toddr_a, - &toddr_b, - &toddr_c, - &loopback, - &spdifin, - &spdifout, - &resample, - &spdifout_b, - &sm1_mst_a_mclk_sel, - &sm1_mst_b_mclk_sel, - &sm1_mst_c_mclk_sel, - &sm1_mst_d_mclk_sel, - &sm1_mst_e_mclk_sel, - &sm1_mst_f_mclk_sel, - &sm1_mst_a_mclk_div, - &sm1_mst_b_mclk_div, - &sm1_mst_c_mclk_div, - &sm1_mst_d_mclk_div, - &sm1_mst_e_mclk_div, - &sm1_mst_f_mclk_div, - &sm1_mst_a_mclk, - &sm1_mst_b_mclk, - &sm1_mst_c_mclk, - &sm1_mst_d_mclk, - &sm1_mst_e_mclk, - &sm1_mst_f_mclk, - &spdifout_clk_sel, - &spdifout_clk_div, - &spdifout_clk, - &spdifin_clk_sel, - &spdifin_clk_div, - &spdifin_clk, - &pdm_dclk_sel, - &pdm_dclk_div, - &pdm_dclk, - &pdm_sysclk_sel, - &pdm_sysclk_div, - &pdm_sysclk, - &mst_a_sclk_pre_en, - &mst_b_sclk_pre_en, - &mst_c_sclk_pre_en, - &mst_d_sclk_pre_en, - &mst_e_sclk_pre_en, - &mst_f_sclk_pre_en, - &mst_a_sclk_div, - &mst_b_sclk_div, - &mst_c_sclk_div, - &mst_d_sclk_div, - &mst_e_sclk_div, - &mst_f_sclk_div, - &mst_a_sclk_post_en, - &mst_b_sclk_post_en, - &mst_c_sclk_post_en, - &mst_d_sclk_post_en, - &mst_e_sclk_post_en, - &mst_f_sclk_post_en, - &mst_a_sclk, - &mst_b_sclk, - &mst_c_sclk, - &mst_d_sclk, - &mst_e_sclk, - &mst_f_sclk, - &mst_a_lrclk_div, - &mst_b_lrclk_div, - &mst_c_lrclk_div, - &mst_d_lrclk_div, - &mst_e_lrclk_div, - &mst_f_lrclk_div, - &mst_a_lrclk, - &mst_b_lrclk, - &mst_c_lrclk, - &mst_d_lrclk, - &mst_e_lrclk, - &mst_f_lrclk, - &tdmin_a_sclk_sel, - &tdmin_b_sclk_sel, - &tdmin_c_sclk_sel, - &tdmin_lb_sclk_sel, - &tdmout_a_sclk_sel, - &tdmout_b_sclk_sel, - &tdmout_c_sclk_sel, - &tdmin_a_sclk_pre_en, - &tdmin_b_sclk_pre_en, - &tdmin_c_sclk_pre_en, - &tdmin_lb_sclk_pre_en, - &tdmout_a_sclk_pre_en, - &tdmout_b_sclk_pre_en, - &tdmout_c_sclk_pre_en, - &tdmin_a_sclk_post_en, - &tdmin_b_sclk_post_en, - &tdmin_c_sclk_post_en, - &tdmin_lb_sclk_post_en, - &tdmout_a_sclk_post_en, - &tdmout_b_sclk_post_en, - &tdmout_c_sclk_post_en, - &tdmin_a_sclk, - &tdmin_b_sclk, - &tdmin_c_sclk, - &tdmin_lb_sclk, - &g12a_tdmout_a_sclk, - &g12a_tdmout_b_sclk, - &g12a_tdmout_c_sclk, - &tdmin_a_lrclk, - &tdmin_b_lrclk, - &tdmin_c_lrclk, - &tdmin_lb_lrclk, - &tdmout_a_lrclk, - &tdmout_b_lrclk, - &tdmout_c_lrclk, - &spdifout_b_clk_sel, - &spdifout_b_clk_div, - &spdifout_b_clk, - &sm1_tdm_mclk_pad_0, - &sm1_tdm_mclk_pad_1, - &sm1_tdm_lrclk_pad_0, - &sm1_tdm_lrclk_pad_1, - &sm1_tdm_lrclk_pad_2, - &sm1_tdm_sclk_pad_0, - &sm1_tdm_sclk_pad_1, - &sm1_tdm_sclk_pad_2, - &sm1_aud_top, - &toram, - &eqdrc, - &resample_b, - &tovad, - &locker, - &spdifin_lb, - &frddr_d, - &toddr_d, - &loopback_b, - &sm1_clk81_en, - &sm1_sysclk_a_div, - &sm1_sysclk_a_en, - &sm1_sysclk_b_div, - &sm1_sysclk_b_en, - &earcrx, - &sm1_earcrx_cmdc_clk_sel, - &sm1_earcrx_cmdc_clk_div, - &sm1_earcrx_cmdc_clk, - &sm1_earcrx_dmac_clk_sel, - &sm1_earcrx_dmac_clk_div, - &sm1_earcrx_dmac_clk, -}; - -struct axg_audio_reset_data { - struct reset_controller_dev rstc; - struct regmap *map; - unsigned int offset; -}; - -static void axg_audio_reset_reg_and_bit(struct axg_audio_reset_data *rst, - unsigned long id, - unsigned int *reg, - unsigned int *bit) -{ - unsigned int stride = regmap_get_reg_stride(rst->map); - - *reg = (id / (stride * BITS_PER_BYTE)) * stride; - *reg += rst->offset; - *bit = id % (stride * BITS_PER_BYTE); -} - -static int axg_audio_reset_update(struct reset_controller_dev *rcdev, - unsigned long id, bool assert) -{ - struct axg_audio_reset_data *rst = - container_of(rcdev, struct axg_audio_reset_data, rstc); - unsigned int offset, bit; - - axg_audio_reset_reg_and_bit(rst, id, &offset, &bit); - - regmap_update_bits(rst->map, offset, BIT(bit), - assert ? BIT(bit) : 0); - - return 0; -} - -static int axg_audio_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct axg_audio_reset_data *rst = - container_of(rcdev, struct axg_audio_reset_data, rstc); - unsigned int val, offset, bit; - - axg_audio_reset_reg_and_bit(rst, id, &offset, &bit); - - regmap_read(rst->map, offset, &val); - - return !!(val & BIT(bit)); -} - -static int axg_audio_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return axg_audio_reset_update(rcdev, id, true); -} - -static int axg_audio_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return axg_audio_reset_update(rcdev, id, false); -} - -static int axg_audio_reset_toggle(struct reset_controller_dev *rcdev, - unsigned long id) -{ - int ret; - - ret = axg_audio_reset_assert(rcdev, id); - if (ret) - return ret; - - return axg_audio_reset_deassert(rcdev, id); -} - -static const struct reset_control_ops axg_audio_rstc_ops = { - .assert = axg_audio_reset_assert, - .deassert = axg_audio_reset_deassert, - .reset = axg_audio_reset_toggle, - .status = axg_audio_reset_status, -}; - static struct regmap_config axg_audio_regmap_cfg = { .reg_bits = 32, .val_bits = 32, @@ -1763,11 +1318,8 @@ static struct regmap_config axg_audio_regmap_cfg = { }; struct audioclk_data { - struct clk_regmap *const *regmap_clks; - unsigned int regmap_clk_num; struct meson_clk_hw_data hw_clks; - unsigned int reset_offset; - unsigned int reset_num; + const char *rst_drvname; unsigned int max_register; }; @@ -1775,7 +1327,7 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct audioclk_data *data; - struct axg_audio_reset_data *rst; + struct auxiliary_device *auxdev; struct regmap *map; void __iomem *regs; struct clk_hw *hw; @@ -1808,10 +1360,6 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) return ret; } - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < data->regmap_clk_num; i++) - data->regmap_clks[i]->map = map; - /* Take care to skip the registered input clocks */ for (i = AUD_CLKID_DDR_ARB; i < data->hw_clks.num; i++) { const char *name; @@ -1834,27 +1382,18 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) if (ret) return ret; - /* Stop here if there is no reset */ - if (!data->reset_num) - return 0; + /* Register auxiliary reset driver when applicable */ + if (data->rst_drvname) { + auxdev = __devm_auxiliary_device_create(dev, dev->driver->name, + data->rst_drvname, NULL, 0); + if (!auxdev) + return -ENODEV; + } - rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL); - if (!rst) - return -ENOMEM; - - rst->map = map; - rst->offset = data->reset_offset; - rst->rstc.nr_resets = data->reset_num; - rst->rstc.ops = &axg_audio_rstc_ops; - rst->rstc.of_node = dev->of_node; - rst->rstc.owner = THIS_MODULE; - - return devm_reset_controller_register(dev, &rst->rstc); + return 0; } static const struct audioclk_data axg_audioclk_data = { - .regmap_clks = axg_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), .hw_clks = { .hws = axg_audio_hw_clks, .num = ARRAY_SIZE(axg_audio_hw_clks), @@ -1863,26 +1402,20 @@ static const struct audioclk_data axg_audioclk_data = { }; static const struct audioclk_data g12a_audioclk_data = { - .regmap_clks = g12a_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), .hw_clks = { .hws = g12a_audio_hw_clks, .num = ARRAY_SIZE(g12a_audio_hw_clks), }, - .reset_offset = AUDIO_SW_RESET, - .reset_num = 26, + .rst_drvname = "rst-g12a", .max_register = AUDIO_CLK_SPDIFOUT_B_CTRL, }; static const struct audioclk_data sm1_audioclk_data = { - .regmap_clks = sm1_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(sm1_clk_regmaps), .hw_clks = { .hws = sm1_audio_hw_clks, .num = ARRAY_SIZE(sm1_audio_hw_clks), }, - .reset_offset = AUDIO_SM1_SW_RESET0, - .reset_num = 39, + .rst_drvname = "rst-sm1", .max_register = AUDIO_EARCRX_DMAC_CLK_CTRL, }; diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h deleted file mode 100644 index 9e7765b630c9..000000000000 --- a/drivers/clk/meson/axg-audio.h +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -/* - * Copyright (c) 2018 BayLibre, SAS. - * Author: Jerome Brunet - */ - -#ifndef __AXG_AUDIO_CLKC_H -#define __AXG_AUDIO_CLKC_H - -/* - * Audio Clock register offsets - * - * Register offsets from the datasheet must be multiplied by 4 before - * to get the right offset - */ -#define AUDIO_CLK_GATE_EN 0x000 -#define AUDIO_MCLK_A_CTRL 0x004 -#define AUDIO_MCLK_B_CTRL 0x008 -#define AUDIO_MCLK_C_CTRL 0x00C -#define AUDIO_MCLK_D_CTRL 0x010 -#define AUDIO_MCLK_E_CTRL 0x014 -#define AUDIO_MCLK_F_CTRL 0x018 -#define AUDIO_MST_PAD_CTRL0 0x01c -#define AUDIO_MST_PAD_CTRL1 0x020 -#define AUDIO_SW_RESET 0x024 -#define AUDIO_MST_A_SCLK_CTRL0 0x040 -#define AUDIO_MST_A_SCLK_CTRL1 0x044 -#define AUDIO_MST_B_SCLK_CTRL0 0x048 -#define AUDIO_MST_B_SCLK_CTRL1 0x04C -#define AUDIO_MST_C_SCLK_CTRL0 0x050 -#define AUDIO_MST_C_SCLK_CTRL1 0x054 -#define AUDIO_MST_D_SCLK_CTRL0 0x058 -#define AUDIO_MST_D_SCLK_CTRL1 0x05C -#define AUDIO_MST_E_SCLK_CTRL0 0x060 -#define AUDIO_MST_E_SCLK_CTRL1 0x064 -#define AUDIO_MST_F_SCLK_CTRL0 0x068 -#define AUDIO_MST_F_SCLK_CTRL1 0x06C -#define AUDIO_CLK_TDMIN_A_CTRL 0x080 -#define AUDIO_CLK_TDMIN_B_CTRL 0x084 -#define AUDIO_CLK_TDMIN_C_CTRL 0x088 -#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C -#define AUDIO_CLK_TDMOUT_A_CTRL 0x090 -#define AUDIO_CLK_TDMOUT_B_CTRL 0x094 -#define AUDIO_CLK_TDMOUT_C_CTRL 0x098 -#define AUDIO_CLK_SPDIFIN_CTRL 0x09C -#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0 -#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4 -#define AUDIO_CLK_LOCKER_CTRL 0x0A8 -#define AUDIO_CLK_PDMIN_CTRL0 0x0AC -#define AUDIO_CLK_PDMIN_CTRL1 0x0B0 -#define AUDIO_CLK_SPDIFOUT_B_CTRL 0x0B4 - -/* SM1 introduce new register and some shifts :( */ -#define AUDIO_CLK_GATE_EN1 0x004 -#define AUDIO_SM1_MCLK_A_CTRL 0x008 -#define AUDIO_SM1_MCLK_B_CTRL 0x00C -#define AUDIO_SM1_MCLK_C_CTRL 0x010 -#define AUDIO_SM1_MCLK_D_CTRL 0x014 -#define AUDIO_SM1_MCLK_E_CTRL 0x018 -#define AUDIO_SM1_MCLK_F_CTRL 0x01C -#define AUDIO_SM1_MST_PAD_CTRL0 0x020 -#define AUDIO_SM1_MST_PAD_CTRL1 0x024 -#define AUDIO_SM1_SW_RESET0 0x028 -#define AUDIO_SM1_SW_RESET1 0x02C -#define AUDIO_CLK81_CTRL 0x030 -#define AUDIO_CLK81_EN 0x034 -#define AUDIO_EARCRX_CMDC_CLK_CTRL 0x0D0 -#define AUDIO_EARCRX_DMAC_CLK_CTRL 0x0D4 - -#endif /*__AXG_AUDIO_CLKC_H */ diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 448eece246ca..208833c3ee95 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -18,11 +18,96 @@ #include "clk-regmap.h" #include "clk-pll.h" #include "clk-mpll.h" -#include "axg.h" #include "meson-eeclk.h" #include +#define HHI_GP0_PLL_CNTL 0x40 +#define HHI_GP0_PLL_CNTL2 0x44 +#define HHI_GP0_PLL_CNTL3 0x48 +#define HHI_GP0_PLL_CNTL4 0x4c +#define HHI_GP0_PLL_CNTL5 0x50 +#define HHI_GP0_PLL_STS 0x54 +#define HHI_GP0_PLL_CNTL1 0x58 +#define HHI_HIFI_PLL_CNTL 0x80 +#define HHI_HIFI_PLL_CNTL2 0x84 +#define HHI_HIFI_PLL_CNTL3 0x88 +#define HHI_HIFI_PLL_CNTL4 0x8C +#define HHI_HIFI_PLL_CNTL5 0x90 +#define HHI_HIFI_PLL_STS 0x94 +#define HHI_HIFI_PLL_CNTL1 0x98 + +#define HHI_XTAL_DIVN_CNTL 0xbc +#define HHI_GCLK2_MPEG0 0xc0 +#define HHI_GCLK2_MPEG1 0xc4 +#define HHI_GCLK2_MPEG2 0xc8 +#define HHI_GCLK2_OTHER 0xd0 +#define HHI_GCLK2_AO 0xd4 +#define HHI_PCIE_PLL_CNTL 0xd8 +#define HHI_PCIE_PLL_CNTL1 0xdC +#define HHI_PCIE_PLL_CNTL2 0xe0 +#define HHI_PCIE_PLL_CNTL3 0xe4 +#define HHI_PCIE_PLL_CNTL4 0xe8 +#define HHI_PCIE_PLL_CNTL5 0xec +#define HHI_PCIE_PLL_CNTL6 0xf0 +#define HHI_PCIE_PLL_STS 0xf4 + +#define HHI_MEM_PD_REG0 0x100 +#define HHI_VPU_MEM_PD_REG0 0x104 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12c + +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_AO 0x154 +#define HHI_SYS_CPU_CLK_CNTL1 0x15c +#define HHI_SYS_CPU_RESET_CNTL 0x160 +#define HHI_VID_CLK_DIV 0x164 +#define HHI_SPICC_HCLK_CNTL 0x168 + +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_TS_CLK_CNTL 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_VID_PLL_CLK_DIV 0x1a0 +#define HHI_VPU_CLK_CNTL 0x1bC + +#define HHI_VAPBCLK_CNTL 0x1F4 + +#define HHI_GEN_CLK_CNTL 0x228 + +#define HHI_VDIN_MEAS_CLK_CNTL 0x250 +#define HHI_NAND_CLK_CNTL 0x25C +#define HHI_SD_EMMC_CLK_CNTL 0x264 + +#define HHI_MPLL_CNTL 0x280 +#define HHI_MPLL_CNTL2 0x284 +#define HHI_MPLL_CNTL3 0x288 +#define HHI_MPLL_CNTL4 0x28C +#define HHI_MPLL_CNTL5 0x290 +#define HHI_MPLL_CNTL6 0x294 +#define HHI_MPLL_CNTL7 0x298 +#define HHI_MPLL_CNTL8 0x29C +#define HHI_MPLL_CNTL9 0x2A0 +#define HHI_MPLL_CNTL10 0x2A4 + +#define HHI_MPLL3_CNTL0 0x2E0 +#define HHI_MPLL3_CNTL1 0x2E4 +#define HHI_PLL_TOP_MISC 0x2E8 + +#define HHI_SYS_PLL_CNTL1 0x2FC +#define HHI_SYS_PLL_CNTL 0x300 +#define HHI_SYS_PLL_CNTL2 0x304 +#define HHI_SYS_PLL_CNTL3 0x308 +#define HHI_SYS_PLL_CNTL4 0x30c +#define HHI_SYS_PLL_CNTL5 0x310 +#define HHI_SYS_PLL_STS 0x314 +#define HHI_DPLL_TOP_I 0x318 +#define HHI_DPLL_TOP2_I 0x31C + static struct clk_regmap axg_fixed_pll_dco = { .data = &(struct meson_clk_pll_data){ .en = { @@ -918,7 +1003,7 @@ static const struct clk_parent_data axg_sd_emmc_clk0_parent_data[] = { /* * Following these parent clocks, we should also have had mpll2, mpll3 * and gp0_pll but these clocks are too precious to be used here. All - * the necessary rates for MMC and NAND operation can be acheived using + * the necessary rates for MMC and NAND operation can be achieved using * xtal or fclk_div clocks */ }; @@ -2025,138 +2110,7 @@ static struct clk_hw *axg_hw_clks[] = { [CLKID_VDIN_MEAS] = &axg_vdin_meas.hw, }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const axg_clk_regmaps[] = { - &axg_clk81, - &axg_ddr, - &axg_audio_locker, - &axg_mipi_dsi_host, - &axg_isa, - &axg_pl301, - &axg_periphs, - &axg_spicc_0, - &axg_i2c, - &axg_rng0, - &axg_uart0, - &axg_mipi_dsi_phy, - &axg_spicc_1, - &axg_pcie_a, - &axg_pcie_b, - &axg_hiu_reg, - &axg_assist_misc, - &axg_emmc_b, - &axg_emmc_c, - &axg_dma, - &axg_spi, - &axg_audio, - &axg_eth_core, - &axg_uart1, - &axg_g2d, - &axg_usb0, - &axg_usb1, - &axg_reset, - &axg_usb_general, - &axg_ahb_arb0, - &axg_efuse, - &axg_boot_rom, - &axg_ahb_data_bus, - &axg_ahb_ctrl_bus, - &axg_usb1_to_ddr, - &axg_usb0_to_ddr, - &axg_mmc_pclk, - &axg_vpu_intr, - &axg_sec_ahb_ahb3_bridge, - &axg_gic, - &axg_ao_media_cpu, - &axg_ao_ahb_sram, - &axg_ao_ahb_bus, - &axg_ao_iface, - &axg_ao_i2c, - &axg_sd_emmc_b_clk0, - &axg_sd_emmc_c_clk0, - &axg_mpeg_clk_div, - &axg_sd_emmc_b_clk0_div, - &axg_sd_emmc_c_clk0_div, - &axg_mpeg_clk_sel, - &axg_sd_emmc_b_clk0_sel, - &axg_sd_emmc_c_clk0_sel, - &axg_mpll0, - &axg_mpll1, - &axg_mpll2, - &axg_mpll3, - &axg_mpll0_div, - &axg_mpll1_div, - &axg_mpll2_div, - &axg_mpll3_div, - &axg_fixed_pll, - &axg_sys_pll, - &axg_gp0_pll, - &axg_hifi_pll, - &axg_mpll_prediv, - &axg_fclk_div2, - &axg_fclk_div3, - &axg_fclk_div4, - &axg_fclk_div5, - &axg_fclk_div7, - &axg_pcie_pll_dco, - &axg_pcie_pll_od, - &axg_pcie_pll, - &axg_pcie_mux, - &axg_pcie_ref, - &axg_pcie_cml_en0, - &axg_pcie_cml_en1, - &axg_gen_clk_sel, - &axg_gen_clk_div, - &axg_gen_clk, - &axg_fixed_pll_dco, - &axg_sys_pll_dco, - &axg_gp0_pll_dco, - &axg_hifi_pll_dco, - &axg_pcie_pll_dco, - &axg_pcie_pll_od, - &axg_vpu_0_div, - &axg_vpu_0_sel, - &axg_vpu_0, - &axg_vpu_1_div, - &axg_vpu_1_sel, - &axg_vpu_1, - &axg_vpu, - &axg_vapb_0_div, - &axg_vapb_0_sel, - &axg_vapb_0, - &axg_vapb_1_div, - &axg_vapb_1_sel, - &axg_vapb_1, - &axg_vapb_sel, - &axg_vapb, - &axg_vclk, - &axg_vclk2, - &axg_vclk_sel, - &axg_vclk2_sel, - &axg_vclk_input, - &axg_vclk2_input, - &axg_vclk_div, - &axg_vclk_div1, - &axg_vclk2_div, - &axg_vclk2_div1, - &axg_vclk_div2_en, - &axg_vclk_div4_en, - &axg_vclk_div6_en, - &axg_vclk_div12_en, - &axg_vclk2_div2_en, - &axg_vclk2_div4_en, - &axg_vclk2_div6_en, - &axg_vclk2_div12_en, - &axg_cts_encl_sel, - &axg_cts_encl, - &axg_vdin_meas_sel, - &axg_vdin_meas_div, - &axg_vdin_meas, -}; - static const struct meson_eeclkc_data axg_clkc_data = { - .regmap_clks = axg_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), .hw_clks = { .hws = axg_hw_clks, .num = ARRAY_SIZE(axg_hw_clks), diff --git a/drivers/clk/meson/axg.h b/drivers/clk/meson/axg.h deleted file mode 100644 index 624d8d3ce7c4..000000000000 --- a/drivers/clk/meson/axg.h +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ -/* - * Copyright (c) 2016 AmLogic, Inc. - * Author: Michael Turquette - * - * Copyright (c) 2017 Amlogic, inc. - * Author: Qiufang Dai - * - */ -#ifndef __AXG_H -#define __AXG_H - -/* - * Clock controller register offsets - * - * Register offsets from the data sheet must be multiplied by 4 before - * adding them to the base address to get the right value. - */ -#define HHI_GP0_PLL_CNTL 0x40 -#define HHI_GP0_PLL_CNTL2 0x44 -#define HHI_GP0_PLL_CNTL3 0x48 -#define HHI_GP0_PLL_CNTL4 0x4c -#define HHI_GP0_PLL_CNTL5 0x50 -#define HHI_GP0_PLL_STS 0x54 -#define HHI_GP0_PLL_CNTL1 0x58 -#define HHI_HIFI_PLL_CNTL 0x80 -#define HHI_HIFI_PLL_CNTL2 0x84 -#define HHI_HIFI_PLL_CNTL3 0x88 -#define HHI_HIFI_PLL_CNTL4 0x8C -#define HHI_HIFI_PLL_CNTL5 0x90 -#define HHI_HIFI_PLL_STS 0x94 -#define HHI_HIFI_PLL_CNTL1 0x98 - -#define HHI_XTAL_DIVN_CNTL 0xbc -#define HHI_GCLK2_MPEG0 0xc0 -#define HHI_GCLK2_MPEG1 0xc4 -#define HHI_GCLK2_MPEG2 0xc8 -#define HHI_GCLK2_OTHER 0xd0 -#define HHI_GCLK2_AO 0xd4 -#define HHI_PCIE_PLL_CNTL 0xd8 -#define HHI_PCIE_PLL_CNTL1 0xdC -#define HHI_PCIE_PLL_CNTL2 0xe0 -#define HHI_PCIE_PLL_CNTL3 0xe4 -#define HHI_PCIE_PLL_CNTL4 0xe8 -#define HHI_PCIE_PLL_CNTL5 0xec -#define HHI_PCIE_PLL_CNTL6 0xf0 -#define HHI_PCIE_PLL_STS 0xf4 - -#define HHI_MEM_PD_REG0 0x100 -#define HHI_VPU_MEM_PD_REG0 0x104 -#define HHI_VIID_CLK_DIV 0x128 -#define HHI_VIID_CLK_CNTL 0x12c - -#define HHI_GCLK_MPEG0 0x140 -#define HHI_GCLK_MPEG1 0x144 -#define HHI_GCLK_MPEG2 0x148 -#define HHI_GCLK_OTHER 0x150 -#define HHI_GCLK_AO 0x154 -#define HHI_SYS_CPU_CLK_CNTL1 0x15c -#define HHI_SYS_CPU_RESET_CNTL 0x160 -#define HHI_VID_CLK_DIV 0x164 -#define HHI_SPICC_HCLK_CNTL 0x168 - -#define HHI_MPEG_CLK_CNTL 0x174 -#define HHI_VID_CLK_CNTL 0x17c -#define HHI_TS_CLK_CNTL 0x190 -#define HHI_VID_CLK_CNTL2 0x194 -#define HHI_SYS_CPU_CLK_CNTL0 0x19c -#define HHI_VID_PLL_CLK_DIV 0x1a0 -#define HHI_VPU_CLK_CNTL 0x1bC - -#define HHI_VAPBCLK_CNTL 0x1F4 - -#define HHI_GEN_CLK_CNTL 0x228 - -#define HHI_VDIN_MEAS_CLK_CNTL 0x250 -#define HHI_NAND_CLK_CNTL 0x25C -#define HHI_SD_EMMC_CLK_CNTL 0x264 - -#define HHI_MPLL_CNTL 0x280 -#define HHI_MPLL_CNTL2 0x284 -#define HHI_MPLL_CNTL3 0x288 -#define HHI_MPLL_CNTL4 0x28C -#define HHI_MPLL_CNTL5 0x290 -#define HHI_MPLL_CNTL6 0x294 -#define HHI_MPLL_CNTL7 0x298 -#define HHI_MPLL_CNTL8 0x29C -#define HHI_MPLL_CNTL9 0x2A0 -#define HHI_MPLL_CNTL10 0x2A4 - -#define HHI_MPLL3_CNTL0 0x2E0 -#define HHI_MPLL3_CNTL1 0x2E4 -#define HHI_PLL_TOP_MISC 0x2E8 - -#define HHI_SYS_PLL_CNTL1 0x2FC -#define HHI_SYS_PLL_CNTL 0x300 -#define HHI_SYS_PLL_CNTL2 0x304 -#define HHI_SYS_PLL_CNTL3 0x308 -#define HHI_SYS_PLL_CNTL4 0x30c -#define HHI_SYS_PLL_CNTL5 0x310 -#define HHI_SYS_PLL_STS 0x314 -#define HHI_DPLL_TOP_I 0x318 -#define HHI_DPLL_TOP2_I 0x31C - -#endif /* __AXG_H */ diff --git a/drivers/clk/meson/c3-peripherals.c b/drivers/clk/meson/c3-peripherals.c index 2075668ed306..a25e7d5dc669 100644 --- a/drivers/clk/meson/c3-peripherals.c +++ b/drivers/clk/meson/c3-peripherals.c @@ -2092,210 +2092,6 @@ static struct clk_hw *c3_periphs_hw_clks[] = { [CLKID_VAPB] = &vapb.hw, }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const c3_periphs_clk_regmaps[] = { - &rtc_xtal_clkin, - &rtc_32k_div, - &rtc_32k_mux, - &rtc_32k, - &rtc_clk, - &sys_reset_ctrl, - &sys_pwr_ctrl, - &sys_pad_ctrl, - &sys_ctrl, - &sys_ts_pll, - &sys_dev_arb, - &sys_mmc_pclk, - &sys_cpu_ctrl, - &sys_jtag_ctrl, - &sys_ir_ctrl, - &sys_irq_ctrl, - &sys_msr_clk, - &sys_rom, - &sys_uart_f, - &sys_cpu_apb, - &sys_rsa, - &sys_sar_adc, - &sys_startup, - &sys_secure, - &sys_spifc, - &sys_nna, - &sys_eth_mac, - &sys_gic, - &sys_rama, - &sys_big_nic, - &sys_ramb, - &sys_audio_pclk, - &sys_pwm_kl, - &sys_pwm_ij, - &sys_usb, - &sys_sd_emmc_a, - &sys_sd_emmc_c, - &sys_pwm_ab, - &sys_pwm_cd, - &sys_pwm_ef, - &sys_pwm_gh, - &sys_spicc_1, - &sys_spicc_0, - &sys_uart_a, - &sys_uart_b, - &sys_uart_c, - &sys_uart_d, - &sys_uart_e, - &sys_i2c_m_a, - &sys_i2c_m_b, - &sys_i2c_m_c, - &sys_i2c_m_d, - &sys_i2c_s_a, - &sys_rtc, - &sys_ge2d, - &sys_isp, - &sys_gpv_isp_nic, - &sys_gpv_cve_nic, - &sys_mipi_dsi_host, - &sys_mipi_dsi_phy, - &sys_eth_phy, - &sys_acodec, - &sys_dwap, - &sys_dos, - &sys_cve, - &sys_vout, - &sys_vc9000e, - &sys_pwm_mn, - &sys_sd_emmc_b, - &axi_sys_nic, - &axi_isp_nic, - &axi_cve_nic, - &axi_ramb, - &axi_rama, - &axi_cpu_dmc, - &axi_nic, - &axi_dma, - &axi_mux_nic, - &axi_cve, - &axi_dev1_dmc, - &axi_dev0_dmc, - &axi_dsp_dmc, - &clk_12_24m_in, - &clk_12_24m, - &fclk_25m_div, - &fclk_25m, - &gen_sel, - &gen_div, - &gen, - &saradc_sel, - &saradc_div, - &saradc, - &pwm_a_sel, - &pwm_a_div, - &pwm_a, - &pwm_b_sel, - &pwm_b_div, - &pwm_b, - &pwm_c_sel, - &pwm_c_div, - &pwm_c, - &pwm_d_sel, - &pwm_d_div, - &pwm_d, - &pwm_e_sel, - &pwm_e_div, - &pwm_e, - &pwm_f_sel, - &pwm_f_div, - &pwm_f, - &pwm_g_sel, - &pwm_g_div, - &pwm_g, - &pwm_h_sel, - &pwm_h_div, - &pwm_h, - &pwm_i_sel, - &pwm_i_div, - &pwm_i, - &pwm_j_sel, - &pwm_j_div, - &pwm_j, - &pwm_k_sel, - &pwm_k_div, - &pwm_k, - &pwm_l_sel, - &pwm_l_div, - &pwm_l, - &pwm_m_sel, - &pwm_m_div, - &pwm_m, - &pwm_n_sel, - &pwm_n_div, - &pwm_n, - &spicc_a_sel, - &spicc_a_div, - &spicc_a, - &spicc_b_sel, - &spicc_b_div, - &spicc_b, - &spifc_sel, - &spifc_div, - &spifc, - &sd_emmc_a_sel, - &sd_emmc_a_div, - &sd_emmc_a, - &sd_emmc_b_sel, - &sd_emmc_b_div, - &sd_emmc_b, - &sd_emmc_c_sel, - &sd_emmc_c_div, - &sd_emmc_c, - &ts_div, - &ts, - ð_125m, - ð_rmii_div, - ð_rmii, - &mipi_dsi_meas_sel, - &mipi_dsi_meas_div, - &mipi_dsi_meas, - &dsi_phy_sel, - &dsi_phy_div, - &dsi_phy, - &vout_mclk_sel, - &vout_mclk_div, - &vout_mclk, - &vout_enc_sel, - &vout_enc_div, - &vout_enc, - &hcodec_0_sel, - &hcodec_0_div, - &hcodec_0, - &hcodec_1_sel, - &hcodec_1_div, - &hcodec_1, - &hcodec, - &vc9000e_aclk_sel, - &vc9000e_aclk_div, - &vc9000e_aclk, - &vc9000e_core_sel, - &vc9000e_core_div, - &vc9000e_core, - &csi_phy0_sel, - &csi_phy0_div, - &csi_phy0, - &dewarpa_sel, - &dewarpa_div, - &dewarpa, - &isp0_sel, - &isp0_div, - &isp0, - &nna_core_sel, - &nna_core_div, - &nna_core, - &ge2d_sel, - &ge2d_div, - &ge2d, - &vapb_sel, - &vapb_div, - &vapb, -}; - static const struct regmap_config clkc_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -2313,7 +2109,7 @@ static int c3_peripherals_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct regmap *regmap; void __iomem *base; - int clkid, ret, i; + int clkid, ret; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -2323,10 +2119,6 @@ static int c3_peripherals_probe(struct platform_device *pdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(c3_periphs_clk_regmaps); i++) - c3_periphs_clk_regmaps[i]->map = regmap; - for (clkid = 0; clkid < c3_periphs_clks.num; clkid++) { /* array might be sparse */ if (!c3_periphs_clks.hws[clkid]) diff --git a/drivers/clk/meson/c3-pll.c b/drivers/clk/meson/c3-pll.c index ed4bc495862e..2c5594b8e49a 100644 --- a/drivers/clk/meson/c3-pll.c +++ b/drivers/clk/meson/c3-pll.c @@ -653,32 +653,6 @@ static struct clk_hw *c3_pll_hw_clks[] = { [CLKID_MCLK1] = &mclk1.hw }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const c3_pll_clk_regmaps[] = { - &fclk_50m_en, - &fclk_div2, - &fclk_div2p5, - &fclk_div3, - &fclk_div4, - &fclk_div5, - &fclk_div7, - &gp0_pll_dco, - &gp0_pll, - &hifi_pll_dco, - &hifi_pll, - &mclk_pll_dco, - &mclk_pll_od, - &mclk_pll, - &mclk0_sel, - &mclk0_div_en, - &mclk0_div, - &mclk0, - &mclk1_sel, - &mclk1_div_en, - &mclk1_div, - &mclk1, -}; - static const struct regmap_config clkc_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -696,7 +670,7 @@ static int c3_pll_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct regmap *regmap; void __iomem *base; - int clkid, ret, i; + int clkid, ret; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -706,10 +680,6 @@ static int c3_pll_probe(struct platform_device *pdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(c3_pll_clk_regmaps); i++) - c3_pll_clk_regmaps[i]->map = regmap; - for (clkid = 0; clkid < c3_pll_clks.num; clkid++) { /* array might be sparse */ if (!c3_pll_clks.hws[clkid]) diff --git a/drivers/clk/meson/clk-cpu-dyndiv.c b/drivers/clk/meson/clk-cpu-dyndiv.c index cb043b52b65d..83aedbfd2891 100644 --- a/drivers/clk/meson/clk-cpu-dyndiv.c +++ b/drivers/clk/meson/clk-cpu-dyndiv.c @@ -61,6 +61,7 @@ static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate, }; const struct clk_ops meson_clk_cpu_dyndiv_ops = { + .init = clk_regmap_init, .recalc_rate = meson_clk_cpu_dyndiv_recalc_rate, .determine_rate = meson_clk_cpu_dyndiv_determine_rate, .set_rate = meson_clk_cpu_dyndiv_set_rate, diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c index c896cf29b318..787df6cdf841 100644 --- a/drivers/clk/meson/clk-dualdiv.c +++ b/drivers/clk/meson/clk-dualdiv.c @@ -126,6 +126,7 @@ static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate, } const struct clk_ops meson_clk_dualdiv_ops = { + .init = clk_regmap_init, .recalc_rate = meson_clk_dualdiv_recalc_rate, .determine_rate = meson_clk_dualdiv_determine_rate, .set_rate = meson_clk_dualdiv_set_rate, @@ -133,6 +134,7 @@ const struct clk_ops meson_clk_dualdiv_ops = { EXPORT_SYMBOL_NS_GPL(meson_clk_dualdiv_ops, "CLK_MESON"); const struct clk_ops meson_clk_dualdiv_ro_ops = { + .init = clk_regmap_init, .recalc_rate = meson_clk_dualdiv_recalc_rate, }; EXPORT_SYMBOL_NS_GPL(meson_clk_dualdiv_ro_ops, "CLK_MESON"); diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index ee91e32b4050..7f8dada66e16 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -128,6 +128,11 @@ static int mpll_init(struct clk_hw *hw) { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk); + int ret; + + ret = clk_regmap_init(hw); + if (ret) + return ret; if (mpll->init_count) regmap_multi_reg_write(clk->map, mpll->init_regs, @@ -151,6 +156,7 @@ static int mpll_init(struct clk_hw *hw) } const struct clk_ops meson_clk_mpll_ro_ops = { + .init = clk_regmap_init, .recalc_rate = mpll_recalc_rate, .determine_rate = mpll_determine_rate, }; diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c index 701211120610..58dd982e6878 100644 --- a/drivers/clk/meson/clk-phase.c +++ b/drivers/clk/meson/clk-phase.c @@ -58,6 +58,7 @@ static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees) } const struct clk_ops meson_clk_phase_ops = { + .init = clk_regmap_init, .get_phase = meson_clk_phase_get_phase, .set_phase = meson_clk_phase_set_phase, }; @@ -83,6 +84,11 @@ static int meson_clk_triphase_sync(struct clk_hw *hw) struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); unsigned int val; + int ret; + + ret = clk_regmap_init(hw); + if (ret) + return ret; /* Get phase 0 and sync it to phase 1 and 2 */ val = meson_parm_read(clk->map, &tph->ph0); @@ -142,6 +148,11 @@ static int meson_sclk_ws_inv_sync(struct clk_hw *hw) struct clk_regmap *clk = to_clk_regmap(hw); struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk); unsigned int val; + int ret; + + ret = clk_regmap_init(hw); + if (ret) + return ret; /* Get phase and sync the inverted value to ws */ val = meson_parm_read(clk->map, &tph->ph); diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index e8e53855b00a..1ea6579a760f 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -311,6 +311,11 @@ static int meson_clk_pll_init(struct clk_hw *hw) { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); + int ret; + + ret = clk_regmap_init(hw); + if (ret) + return ret; /* * Keep the clock running, which was already initialized and enabled @@ -468,6 +473,7 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, * the other ops except set_rate since the rate is fixed. */ const struct clk_ops meson_clk_pcie_pll_ops = { + .init = clk_regmap_init, .recalc_rate = meson_clk_pll_recalc_rate, .determine_rate = meson_clk_pll_determine_rate, .is_enabled = meson_clk_pll_is_enabled, @@ -488,6 +494,7 @@ const struct clk_ops meson_clk_pll_ops = { EXPORT_SYMBOL_NS_GPL(meson_clk_pll_ops, "CLK_MESON"); const struct clk_ops meson_clk_pll_ro_ops = { + .init = clk_regmap_init, .recalc_rate = meson_clk_pll_recalc_rate, .is_enabled = meson_clk_pll_is_enabled, }; diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c index f3e504f67571..1ed56fe63cae 100644 --- a/drivers/clk/meson/clk-regmap.c +++ b/drivers/clk/meson/clk-regmap.c @@ -4,9 +4,52 @@ * Author: Jerome Brunet */ +#include #include +#include #include "clk-regmap.h" +int clk_regmap_init(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct device_node *np, *parent_np; + struct device *dev; + + /* Allow regmap to be preset as it was historically done */ + if (clk->map) + return 0; + + /* + * FIXME: what follows couples the controller implementation + * and clk_regmap clock type. This situation is not desirable + * but temporary, until the controller is able to register + * a hook to initialize a clock type + */ + + /* Check the usual dev enabled controller with an basic IO regmap */ + dev = clk_hw_get_dev(hw); + if (dev) { + clk->map = dev_get_regmap(dev, NULL); + if (clk->map) + return 0; + } + + /* Move on to early and syscon based controllers */ + np = clk_hw_get_of_node(hw); + if (np) { + parent_np = of_get_parent(np); + clk->map = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + + if (!IS_ERR_OR_NULL(clk->map)) + return 0; + } + + /* Bail out if regmap can't be found */ + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(clk_regmap_init, "CLK_MESON"); + static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) { struct clk_regmap *clk = to_clk_regmap(hw); @@ -45,6 +88,7 @@ static int clk_regmap_gate_is_enabled(struct clk_hw *hw) } const struct clk_ops clk_regmap_gate_ops = { + .init = clk_regmap_init, .enable = clk_regmap_gate_enable, .disable = clk_regmap_gate_disable, .is_enabled = clk_regmap_gate_is_enabled, @@ -52,6 +96,7 @@ const struct clk_ops clk_regmap_gate_ops = { EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, "CLK_MESON"); const struct clk_ops clk_regmap_gate_ro_ops = { + .init = clk_regmap_init, .is_enabled = clk_regmap_gate_is_enabled, }; EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, "CLK_MESON"); @@ -121,6 +166,7 @@ static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ const struct clk_ops clk_regmap_divider_ops = { + .init = clk_regmap_init, .recalc_rate = clk_regmap_div_recalc_rate, .determine_rate = clk_regmap_div_determine_rate, .set_rate = clk_regmap_div_set_rate, @@ -128,6 +174,7 @@ const struct clk_ops clk_regmap_divider_ops = { EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, "CLK_MESON"); const struct clk_ops clk_regmap_divider_ro_ops = { + .init = clk_regmap_init, .recalc_rate = clk_regmap_div_recalc_rate, .determine_rate = clk_regmap_div_determine_rate, }; @@ -170,6 +217,7 @@ static int clk_regmap_mux_determine_rate(struct clk_hw *hw, } const struct clk_ops clk_regmap_mux_ops = { + .init = clk_regmap_init, .get_parent = clk_regmap_mux_get_parent, .set_parent = clk_regmap_mux_set_parent, .determine_rate = clk_regmap_mux_determine_rate, @@ -177,6 +225,7 @@ const struct clk_ops clk_regmap_mux_ops = { EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, "CLK_MESON"); const struct clk_ops clk_regmap_mux_ro_ops = { + .init = clk_regmap_init, .get_parent = clk_regmap_mux_get_parent, }; EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, "CLK_MESON"); diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h index e365312da54e..f8cac2df5755 100644 --- a/drivers/clk/meson/clk-regmap.h +++ b/drivers/clk/meson/clk-regmap.h @@ -7,6 +7,7 @@ #ifndef __CLK_REGMAP_H #define __CLK_REGMAP_H +#include #include #include @@ -31,6 +32,9 @@ static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw) return container_of(hw, struct clk_regmap, hw); } +/* clk_regmap init op to get and cache regmap from the controllers */ +int clk_regmap_init(struct clk_hw *hw); + /** * struct clk_regmap_gate_data - regmap backed gate specific data * diff --git a/drivers/clk/meson/g12a-aoclk.c b/drivers/clk/meson/g12a-aoclk.c index 71c758ffa493..4095a1b2bb80 100644 --- a/drivers/clk/meson/g12a-aoclk.c +++ b/drivers/clk/meson/g12a-aoclk.c @@ -381,38 +381,6 @@ static const unsigned int g12a_aoclk_reset[] = { [RESET_AO_IR_OUT] = 23, }; -static struct clk_regmap *g12a_aoclk_regmap[] = { - &g12a_aoclk_ahb, - &g12a_aoclk_ir_in, - &g12a_aoclk_i2c_m0, - &g12a_aoclk_i2c_s0, - &g12a_aoclk_uart, - &g12a_aoclk_prod_i2c, - &g12a_aoclk_uart2, - &g12a_aoclk_ir_out, - &g12a_aoclk_saradc, - &g12a_aoclk_mailbox, - &g12a_aoclk_m3, - &g12a_aoclk_ahb_sram, - &g12a_aoclk_rti, - &g12a_aoclk_m4_fclk, - &g12a_aoclk_m4_hclk, - &g12a_aoclk_cts_oscin, - &g12a_aoclk_32k_by_oscin_pre, - &g12a_aoclk_32k_by_oscin_div, - &g12a_aoclk_32k_by_oscin_sel, - &g12a_aoclk_32k_by_oscin, - &g12a_aoclk_cec_pre, - &g12a_aoclk_cec_div, - &g12a_aoclk_cec_sel, - &g12a_aoclk_cec, - &g12a_aoclk_cts_rtc_oscin, - &g12a_aoclk_clk81, - &g12a_aoclk_saradc_mux, - &g12a_aoclk_saradc_div, - &g12a_aoclk_saradc_gate, -}; - static struct clk_hw *g12a_aoclk_hw_clks[] = { [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw, [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw, @@ -449,8 +417,6 @@ static const struct meson_aoclk_data g12a_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(g12a_aoclk_reset), .reset = g12a_aoclk_reset, - .num_clks = ARRAY_SIZE(g12a_aoclk_regmap), - .clks = g12a_aoclk_regmap, .hw_clks = { .hws = g12a_aoclk_hw_clks, .num = ARRAY_SIZE(g12a_aoclk_hw_clks), diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index d9e546e006d7..66f0e817e416 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -24,10 +24,119 @@ #include "vid-pll-div.h" #include "vclk.h" #include "meson-eeclk.h" -#include "g12a.h" #include +#define HHI_MIPI_CNTL0 0x000 +#define HHI_MIPI_CNTL1 0x004 +#define HHI_MIPI_CNTL2 0x008 +#define HHI_MIPI_STS 0x00c +#define HHI_GP0_PLL_CNTL0 0x040 +#define HHI_GP0_PLL_CNTL1 0x044 +#define HHI_GP0_PLL_CNTL2 0x048 +#define HHI_GP0_PLL_CNTL3 0x04c +#define HHI_GP0_PLL_CNTL4 0x050 +#define HHI_GP0_PLL_CNTL5 0x054 +#define HHI_GP0_PLL_CNTL6 0x058 +#define HHI_GP0_PLL_STS 0x05c +#define HHI_GP1_PLL_CNTL0 0x060 +#define HHI_GP1_PLL_CNTL1 0x064 +#define HHI_GP1_PLL_CNTL2 0x068 +#define HHI_GP1_PLL_CNTL3 0x06c +#define HHI_GP1_PLL_CNTL4 0x070 +#define HHI_GP1_PLL_CNTL5 0x074 +#define HHI_GP1_PLL_CNTL6 0x078 +#define HHI_GP1_PLL_STS 0x07c +#define HHI_PCIE_PLL_CNTL0 0x098 +#define HHI_PCIE_PLL_CNTL1 0x09c +#define HHI_PCIE_PLL_CNTL2 0x0a0 +#define HHI_PCIE_PLL_CNTL3 0x0a4 +#define HHI_PCIE_PLL_CNTL4 0x0a8 +#define HHI_PCIE_PLL_CNTL5 0x0ac +#define HHI_PCIE_PLL_STS 0x0b8 +#define HHI_HIFI_PLL_CNTL0 0x0d8 +#define HHI_HIFI_PLL_CNTL1 0x0dc +#define HHI_HIFI_PLL_CNTL2 0x0e0 +#define HHI_HIFI_PLL_CNTL3 0x0e4 +#define HHI_HIFI_PLL_CNTL4 0x0e8 +#define HHI_HIFI_PLL_CNTL5 0x0ec +#define HHI_HIFI_PLL_CNTL6 0x0f0 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12c +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_OTHER2 0x154 +#define HHI_SYS_CPU_CLK_CNTL1 0x15c +#define HHI_VID_CLK_DIV 0x164 +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_AUD_CLK_CNTL 0x178 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_TS_CLK_CNTL 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_VID_PLL_CLK_DIV 0x1a0 +#define HHI_MALI_CLK_CNTL 0x1b0 +#define HHI_VPU_CLKC_CNTL 0x1b4 +#define HHI_VPU_CLK_CNTL 0x1bc +#define HHI_ISP_CLK_CNTL 0x1c0 +#define HHI_NNA_CLK_CNTL 0x1c8 +#define HHI_HDMI_CLK_CNTL 0x1cc +#define HHI_VDEC_CLK_CNTL 0x1e0 +#define HHI_VDEC2_CLK_CNTL 0x1e4 +#define HHI_VDEC3_CLK_CNTL 0x1e8 +#define HHI_VDEC4_CLK_CNTL 0x1ec +#define HHI_HDCP22_CLK_CNTL 0x1f0 +#define HHI_VAPBCLK_CNTL 0x1f4 +#define HHI_SYS_CPUB_CLK_CNTL1 0x200 +#define HHI_SYS_CPUB_CLK_CNTL 0x208 +#define HHI_VPU_CLKB_CNTL 0x20c +#define HHI_SYS_CPU_CLK_CNTL2 0x210 +#define HHI_SYS_CPU_CLK_CNTL3 0x214 +#define HHI_SYS_CPU_CLK_CNTL4 0x218 +#define HHI_SYS_CPU_CLK_CNTL5 0x21c +#define HHI_SYS_CPU_CLK_CNTL6 0x220 +#define HHI_GEN_CLK_CNTL 0x228 +#define HHI_VDIN_MEAS_CLK_CNTL 0x250 +#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254 +#define HHI_NAND_CLK_CNTL 0x25c +#define HHI_SD_EMMC_CLK_CNTL 0x264 +#define HHI_MPLL_CNTL0 0x278 +#define HHI_MPLL_CNTL1 0x27c +#define HHI_MPLL_CNTL2 0x280 +#define HHI_MPLL_CNTL3 0x284 +#define HHI_MPLL_CNTL4 0x288 +#define HHI_MPLL_CNTL5 0x28c +#define HHI_MPLL_CNTL6 0x290 +#define HHI_MPLL_CNTL7 0x294 +#define HHI_MPLL_CNTL8 0x298 +#define HHI_FIX_PLL_CNTL0 0x2a0 +#define HHI_FIX_PLL_CNTL1 0x2a4 +#define HHI_FIX_PLL_CNTL3 0x2ac +#define HHI_SYS_PLL_CNTL0 0x2f4 +#define HHI_SYS_PLL_CNTL1 0x2f8 +#define HHI_SYS_PLL_CNTL2 0x2fc +#define HHI_SYS_PLL_CNTL3 0x300 +#define HHI_SYS_PLL_CNTL4 0x304 +#define HHI_SYS_PLL_CNTL5 0x308 +#define HHI_SYS_PLL_CNTL6 0x30c +#define HHI_HDMI_PLL_CNTL0 0x320 +#define HHI_HDMI_PLL_CNTL1 0x324 +#define HHI_HDMI_PLL_CNTL2 0x328 +#define HHI_HDMI_PLL_CNTL3 0x32c +#define HHI_HDMI_PLL_CNTL4 0x330 +#define HHI_HDMI_PLL_CNTL5 0x334 +#define HHI_HDMI_PLL_CNTL6 0x338 +#define HHI_SPICC_CLK_CNTL 0x3dc +#define HHI_SYS1_PLL_CNTL0 0x380 +#define HHI_SYS1_PLL_CNTL1 0x384 +#define HHI_SYS1_PLL_CNTL2 0x388 +#define HHI_SYS1_PLL_CNTL3 0x38c +#define HHI_SYS1_PLL_CNTL4 0x390 +#define HHI_SYS1_PLL_CNTL5 0x394 +#define HHI_SYS1_PLL_CNTL6 0x398 + static struct clk_regmap g12a_fixed_pll_dco = { .data = &(struct meson_clk_pll_data){ .en = { @@ -2489,7 +2598,7 @@ static const struct clk_parent_data g12a_sd_emmc_clk0_parent_data[] = { /* * Following these parent clocks, we should also have had mpll2, mpll3 * and gp0_pll but these clocks are too precious to be used here. All - * the necessary rates for MMC and NAND operation can be acheived using + * the necessary rates for MMC and NAND operation can be achieved using * g12a_ee_core or fclk_div clocks */ }; @@ -3753,8 +3862,8 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = { }; /* - * FIXME: Force as bypass by forcing a single /1 table entry, and doensn't on boot value - * when setting a clock whith this node in the clock path, but doesn't garantee the divider + * FIXME: Force as bypass by forcing a single /1 table entry, and doesn't on boot value + * when setting a clock with this node in the clock path, but doesn't guarantee the divider * is at /1 at boot until a rate is set. */ static const struct clk_div_table g12a_mipi_dsi_pxclk_div_table[] = { @@ -5126,261 +5235,6 @@ static struct clk_hw *sm1_hw_clks[] = { [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw, }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const g12a_clk_regmaps[] = { - &g12a_clk81, - &g12a_dos, - &g12a_ddr, - &g12a_audio_locker, - &g12a_mipi_dsi_host, - &g12a_eth_phy, - &g12a_isa, - &g12a_pl301, - &g12a_periphs, - &g12a_spicc_0, - &g12a_i2c, - &g12a_sana, - &g12a_sd, - &g12a_rng0, - &g12a_uart0, - &g12a_spicc_1, - &g12a_hiu_reg, - &g12a_mipi_dsi_phy, - &g12a_assist_misc, - &g12a_emmc_a, - &g12a_emmc_b, - &g12a_emmc_c, - &g12a_audio_codec, - &g12a_audio, - &g12a_eth_core, - &g12a_demux, - &g12a_audio_ififo, - &g12a_adc, - &g12a_uart1, - &g12a_g2d, - &g12a_reset, - &g12a_pcie_comb, - &g12a_parser, - &g12a_usb_general, - &g12a_pcie_phy, - &g12a_ahb_arb0, - &g12a_ahb_data_bus, - &g12a_ahb_ctrl_bus, - &g12a_htx_hdcp22, - &g12a_htx_pclk, - &g12a_bt656, - &g12a_usb1_to_ddr, - &g12a_mmc_pclk, - &g12a_uart2, - &g12a_vpu_intr, - &g12a_gic, - &g12a_sd_emmc_a_clk0, - &g12a_sd_emmc_b_clk0, - &g12a_sd_emmc_c_clk0, - &g12a_mpeg_clk_div, - &g12a_sd_emmc_a_clk0_div, - &g12a_sd_emmc_b_clk0_div, - &g12a_sd_emmc_c_clk0_div, - &g12a_mpeg_clk_sel, - &g12a_sd_emmc_a_clk0_sel, - &g12a_sd_emmc_b_clk0_sel, - &g12a_sd_emmc_c_clk0_sel, - &g12a_mpll0, - &g12a_mpll1, - &g12a_mpll2, - &g12a_mpll3, - &g12a_mpll0_div, - &g12a_mpll1_div, - &g12a_mpll2_div, - &g12a_mpll3_div, - &g12a_fixed_pll, - &g12a_sys_pll, - &g12a_gp0_pll, - &g12a_hifi_pll, - &g12a_vclk2_venci0, - &g12a_vclk2_venci1, - &g12a_vclk2_vencp0, - &g12a_vclk2_vencp1, - &g12a_vclk2_venct0, - &g12a_vclk2_venct1, - &g12a_vclk2_other, - &g12a_vclk2_enci, - &g12a_vclk2_encp, - &g12a_dac_clk, - &g12a_aoclk_gate, - &g12a_iec958_gate, - &g12a_enc480p, - &g12a_rng1, - &g12a_vclk2_enct, - &g12a_vclk2_encl, - &g12a_vclk2_venclmmc, - &g12a_vclk2_vencl, - &g12a_vclk2_other1, - &g12a_fixed_pll_dco, - &g12a_sys_pll_dco, - &g12a_gp0_pll_dco, - &g12a_hifi_pll_dco, - &g12a_fclk_div2, - &g12a_fclk_div3, - &g12a_fclk_div4, - &g12a_fclk_div5, - &g12a_fclk_div7, - &g12a_fclk_div2p5, - &g12a_dma, - &g12a_efuse, - &g12a_rom_boot, - &g12a_reset_sec, - &g12a_sec_ahb_apb3, - &g12a_vpu_0_sel, - &g12a_vpu_0_div, - &g12a_vpu_0, - &g12a_vpu_1_sel, - &g12a_vpu_1_div, - &g12a_vpu_1, - &g12a_vpu, - &g12a_vapb_0_sel, - &g12a_vapb_0_div, - &g12a_vapb_0, - &g12a_vapb_1_sel, - &g12a_vapb_1_div, - &g12a_vapb_1, - &g12a_vapb_sel, - &g12a_vapb, - &g12a_hdmi_pll_dco, - &g12a_hdmi_pll_od, - &g12a_hdmi_pll_od2, - &g12a_hdmi_pll, - &g12a_vid_pll_div, - &g12a_vid_pll_sel, - &g12a_vid_pll, - &g12a_vclk_sel, - &g12a_vclk2_sel, - &g12a_vclk_input, - &g12a_vclk2_input, - &g12a_vclk_div, - &g12a_vclk2_div, - &g12a_vclk, - &g12a_vclk2, - &g12a_vclk_div1, - &g12a_vclk_div2_en, - &g12a_vclk_div4_en, - &g12a_vclk_div6_en, - &g12a_vclk_div12_en, - &g12a_vclk2_div1, - &g12a_vclk2_div2_en, - &g12a_vclk2_div4_en, - &g12a_vclk2_div6_en, - &g12a_vclk2_div12_en, - &g12a_cts_enci_sel, - &g12a_cts_encp_sel, - &g12a_cts_encl_sel, - &g12a_cts_vdac_sel, - &g12a_hdmi_tx_sel, - &g12a_cts_enci, - &g12a_cts_encp, - &g12a_cts_encl, - &g12a_cts_vdac, - &g12a_hdmi_tx, - &g12a_hdmi_sel, - &g12a_hdmi_div, - &g12a_hdmi, - &g12a_mali_0_sel, - &g12a_mali_0_div, - &g12a_mali_0, - &g12a_mali_1_sel, - &g12a_mali_1_div, - &g12a_mali_1, - &g12a_mali, - &g12a_mpll_50m, - &g12a_sys_pll_div16_en, - &g12a_cpu_clk_premux0, - &g12a_cpu_clk_mux0_div, - &g12a_cpu_clk_postmux0, - &g12a_cpu_clk_premux1, - &g12a_cpu_clk_mux1_div, - &g12a_cpu_clk_postmux1, - &g12a_cpu_clk_dyn, - &g12a_cpu_clk, - &g12a_cpu_clk_div16_en, - &g12a_cpu_clk_apb_div, - &g12a_cpu_clk_apb, - &g12a_cpu_clk_atb_div, - &g12a_cpu_clk_atb, - &g12a_cpu_clk_axi_div, - &g12a_cpu_clk_axi, - &g12a_cpu_clk_trace_div, - &g12a_cpu_clk_trace, - &g12a_pcie_pll_od, - &g12a_pcie_pll_dco, - &g12a_vdec_1_sel, - &g12a_vdec_1_div, - &g12a_vdec_1, - &g12a_vdec_hevc_sel, - &g12a_vdec_hevc_div, - &g12a_vdec_hevc, - &g12a_vdec_hevcf_sel, - &g12a_vdec_hevcf_div, - &g12a_vdec_hevcf, - &g12a_ts_div, - &g12a_ts, - &g12b_cpu_clk, - &g12b_sys1_pll_dco, - &g12b_sys1_pll, - &g12b_sys1_pll_div16_en, - &g12b_cpub_clk_premux0, - &g12b_cpub_clk_mux0_div, - &g12b_cpub_clk_postmux0, - &g12b_cpub_clk_premux1, - &g12b_cpub_clk_mux1_div, - &g12b_cpub_clk_postmux1, - &g12b_cpub_clk_dyn, - &g12b_cpub_clk, - &g12b_cpub_clk_div16_en, - &g12b_cpub_clk_apb_sel, - &g12b_cpub_clk_apb, - &g12b_cpub_clk_atb_sel, - &g12b_cpub_clk_atb, - &g12b_cpub_clk_axi_sel, - &g12b_cpub_clk_axi, - &g12b_cpub_clk_trace_sel, - &g12b_cpub_clk_trace, - &sm1_gp1_pll_dco, - &sm1_gp1_pll, - &sm1_dsu_clk_premux0, - &sm1_dsu_clk_premux1, - &sm1_dsu_clk_mux0_div, - &sm1_dsu_clk_postmux0, - &sm1_dsu_clk_mux1_div, - &sm1_dsu_clk_postmux1, - &sm1_dsu_clk_dyn, - &sm1_dsu_final_clk, - &sm1_dsu_clk, - &sm1_cpu1_clk, - &sm1_cpu2_clk, - &sm1_cpu3_clk, - &g12a_spicc0_sclk_sel, - &g12a_spicc0_sclk_div, - &g12a_spicc0_sclk, - &g12a_spicc1_sclk_sel, - &g12a_spicc1_sclk_div, - &g12a_spicc1_sclk, - &sm1_nna_axi_clk_sel, - &sm1_nna_axi_clk_div, - &sm1_nna_axi_clk, - &sm1_nna_core_clk_sel, - &sm1_nna_core_clk_div, - &sm1_nna_core_clk, - &g12a_mipi_dsi_pxclk_sel, - &g12a_mipi_dsi_pxclk_div, - &g12a_mipi_dsi_pxclk, - &g12b_mipi_isp_sel, - &g12b_mipi_isp_div, - &g12b_mipi_isp, - &g12b_mipi_isp_gate, - &g12b_csi_phy1, - &g12b_csi_phy0, -}; - static const struct reg_sequence g12a_init_regs[] = { { .reg = HHI_MPLL_CNTL0, .def = 0x00000543 }, }; @@ -5559,8 +5413,6 @@ static int meson_g12a_probe(struct platform_device *pdev) static const struct meson_g12a_data g12a_clkc_data = { .eeclkc_data = { - .regmap_clks = g12a_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), .hw_clks = { .hws = g12a_hw_clks, .num = ARRAY_SIZE(g12a_hw_clks), @@ -5573,8 +5425,6 @@ static const struct meson_g12a_data g12a_clkc_data = { static const struct meson_g12a_data g12b_clkc_data = { .eeclkc_data = { - .regmap_clks = g12a_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), .hw_clks = { .hws = g12b_hw_clks, .num = ARRAY_SIZE(g12b_hw_clks), @@ -5585,8 +5435,6 @@ static const struct meson_g12a_data g12b_clkc_data = { static const struct meson_g12a_data sm1_clkc_data = { .eeclkc_data = { - .regmap_clks = g12a_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), .hw_clks = { .hws = sm1_hw_clks, .num = ARRAY_SIZE(sm1_hw_clks), diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h deleted file mode 100644 index 27df99c4565a..000000000000 --- a/drivers/clk/meson/g12a.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ -/* - * Copyright (c) 2016 Amlogic, Inc. - * Author: Michael Turquette - * - * Copyright (c) 2018 Amlogic, inc. - * Author: Qiufang Dai - * Author: Jian Hu - * - */ -#ifndef __G12A_H -#define __G12A_H - -/* - * Clock controller register offsets - * - * Register offsets from the data sheet must be multiplied by 4 before - * adding them to the base address to get the right value. - */ -#define HHI_MIPI_CNTL0 0x000 -#define HHI_MIPI_CNTL1 0x004 -#define HHI_MIPI_CNTL2 0x008 -#define HHI_MIPI_STS 0x00C -#define HHI_GP0_PLL_CNTL0 0x040 -#define HHI_GP0_PLL_CNTL1 0x044 -#define HHI_GP0_PLL_CNTL2 0x048 -#define HHI_GP0_PLL_CNTL3 0x04C -#define HHI_GP0_PLL_CNTL4 0x050 -#define HHI_GP0_PLL_CNTL5 0x054 -#define HHI_GP0_PLL_CNTL6 0x058 -#define HHI_GP0_PLL_STS 0x05C -#define HHI_GP1_PLL_CNTL0 0x060 -#define HHI_GP1_PLL_CNTL1 0x064 -#define HHI_GP1_PLL_CNTL2 0x068 -#define HHI_GP1_PLL_CNTL3 0x06C -#define HHI_GP1_PLL_CNTL4 0x070 -#define HHI_GP1_PLL_CNTL5 0x074 -#define HHI_GP1_PLL_CNTL6 0x078 -#define HHI_GP1_PLL_STS 0x07C -#define HHI_PCIE_PLL_CNTL0 0x098 -#define HHI_PCIE_PLL_CNTL1 0x09C -#define HHI_PCIE_PLL_CNTL2 0x0A0 -#define HHI_PCIE_PLL_CNTL3 0x0A4 -#define HHI_PCIE_PLL_CNTL4 0x0A8 -#define HHI_PCIE_PLL_CNTL5 0x0AC -#define HHI_PCIE_PLL_STS 0x0B8 -#define HHI_HIFI_PLL_CNTL0 0x0D8 -#define HHI_HIFI_PLL_CNTL1 0x0DC -#define HHI_HIFI_PLL_CNTL2 0x0E0 -#define HHI_HIFI_PLL_CNTL3 0x0E4 -#define HHI_HIFI_PLL_CNTL4 0x0E8 -#define HHI_HIFI_PLL_CNTL5 0x0EC -#define HHI_HIFI_PLL_CNTL6 0x0F0 -#define HHI_VIID_CLK_DIV 0x128 -#define HHI_VIID_CLK_CNTL 0x12C -#define HHI_GCLK_MPEG0 0x140 -#define HHI_GCLK_MPEG1 0x144 -#define HHI_GCLK_MPEG2 0x148 -#define HHI_GCLK_OTHER 0x150 -#define HHI_GCLK_OTHER2 0x154 -#define HHI_SYS_CPU_CLK_CNTL1 0x15c -#define HHI_VID_CLK_DIV 0x164 -#define HHI_MPEG_CLK_CNTL 0x174 -#define HHI_AUD_CLK_CNTL 0x178 -#define HHI_VID_CLK_CNTL 0x17c -#define HHI_TS_CLK_CNTL 0x190 -#define HHI_VID_CLK_CNTL2 0x194 -#define HHI_SYS_CPU_CLK_CNTL0 0x19c -#define HHI_VID_PLL_CLK_DIV 0x1A0 -#define HHI_MALI_CLK_CNTL 0x1b0 -#define HHI_VPU_CLKC_CNTL 0x1b4 -#define HHI_VPU_CLK_CNTL 0x1bC -#define HHI_ISP_CLK_CNTL 0x1C0 -#define HHI_NNA_CLK_CNTL 0x1C8 -#define HHI_HDMI_CLK_CNTL 0x1CC -#define HHI_VDEC_CLK_CNTL 0x1E0 -#define HHI_VDEC2_CLK_CNTL 0x1E4 -#define HHI_VDEC3_CLK_CNTL 0x1E8 -#define HHI_VDEC4_CLK_CNTL 0x1EC -#define HHI_HDCP22_CLK_CNTL 0x1F0 -#define HHI_VAPBCLK_CNTL 0x1F4 -#define HHI_SYS_CPUB_CLK_CNTL1 0x200 -#define HHI_SYS_CPUB_CLK_CNTL 0x208 -#define HHI_VPU_CLKB_CNTL 0x20C -#define HHI_SYS_CPU_CLK_CNTL2 0x210 -#define HHI_SYS_CPU_CLK_CNTL3 0x214 -#define HHI_SYS_CPU_CLK_CNTL4 0x218 -#define HHI_SYS_CPU_CLK_CNTL5 0x21c -#define HHI_SYS_CPU_CLK_CNTL6 0x220 -#define HHI_GEN_CLK_CNTL 0x228 -#define HHI_VDIN_MEAS_CLK_CNTL 0x250 -#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254 -#define HHI_NAND_CLK_CNTL 0x25C -#define HHI_SD_EMMC_CLK_CNTL 0x264 -#define HHI_MPLL_CNTL0 0x278 -#define HHI_MPLL_CNTL1 0x27C -#define HHI_MPLL_CNTL2 0x280 -#define HHI_MPLL_CNTL3 0x284 -#define HHI_MPLL_CNTL4 0x288 -#define HHI_MPLL_CNTL5 0x28c -#define HHI_MPLL_CNTL6 0x290 -#define HHI_MPLL_CNTL7 0x294 -#define HHI_MPLL_CNTL8 0x298 -#define HHI_FIX_PLL_CNTL0 0x2A0 -#define HHI_FIX_PLL_CNTL1 0x2A4 -#define HHI_FIX_PLL_CNTL3 0x2AC -#define HHI_SYS_PLL_CNTL0 0x2f4 -#define HHI_SYS_PLL_CNTL1 0x2f8 -#define HHI_SYS_PLL_CNTL2 0x2fc -#define HHI_SYS_PLL_CNTL3 0x300 -#define HHI_SYS_PLL_CNTL4 0x304 -#define HHI_SYS_PLL_CNTL5 0x308 -#define HHI_SYS_PLL_CNTL6 0x30c -#define HHI_HDMI_PLL_CNTL0 0x320 -#define HHI_HDMI_PLL_CNTL1 0x324 -#define HHI_HDMI_PLL_CNTL2 0x328 -#define HHI_HDMI_PLL_CNTL3 0x32c -#define HHI_HDMI_PLL_CNTL4 0x330 -#define HHI_HDMI_PLL_CNTL5 0x334 -#define HHI_HDMI_PLL_CNTL6 0x338 -#define HHI_SPICC_CLK_CNTL 0x3dc -#define HHI_SYS1_PLL_CNTL0 0x380 -#define HHI_SYS1_PLL_CNTL1 0x384 -#define HHI_SYS1_PLL_CNTL2 0x388 -#define HHI_SYS1_PLL_CNTL3 0x38c -#define HHI_SYS1_PLL_CNTL4 0x390 -#define HHI_SYS1_PLL_CNTL5 0x394 -#define HHI_SYS1_PLL_CNTL6 0x398 - -#endif /* __G12A_H */ diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index 43940232f718..f075fbd450f3 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -237,23 +237,6 @@ static const unsigned int gxbb_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_regmap *gxbb_aoclk[] = { - &remote_ao, - &i2c_master_ao, - &i2c_slave_ao, - &uart1_ao, - &uart2_ao, - &ir_blaster_ao, - &ao_cts_oscin, - &ao_32k_pre, - &ao_32k_div, - &ao_32k_sel, - &ao_32k, - &ao_cts_rtc_oscin, - &ao_clk81, - &ao_cts_cec, -}; - static struct clk_hw *gxbb_aoclk_hw_clks[] = { [CLKID_AO_REMOTE] = &remote_ao.hw, [CLKID_AO_I2C_MASTER] = &i2c_master_ao.hw, @@ -275,8 +258,6 @@ static const struct meson_aoclk_data gxbb_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(gxbb_aoclk_reset), .reset = gxbb_aoclk_reset, - .num_clks = ARRAY_SIZE(gxbb_aoclk), - .clks = gxbb_aoclk, .hw_clks = { .hws = gxbb_aoclk_hw_clks, .num = ARRAY_SIZE(gxbb_aoclk_hw_clks), diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 3abb44a2532b..362d1b87ea5b 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -10,7 +10,6 @@ #include #include -#include "gxbb.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-mpll.h" @@ -19,6 +18,104 @@ #include +#define SCR 0x2c +#define TIMEOUT_VALUE 0x3c + +#define HHI_GP0_PLL_CNTL 0x40 +#define HHI_GP0_PLL_CNTL2 0x44 +#define HHI_GP0_PLL_CNTL3 0x48 +#define HHI_GP0_PLL_CNTL4 0x4c +#define HHI_GP0_PLL_CNTL5 0x50 +#define HHI_GP0_PLL_CNTL1 0x58 + +#define HHI_XTAL_DIVN_CNTL 0xbc +#define HHI_TIMER90K 0xec + +#define HHI_MEM_PD_REG0 0x100 +#define HHI_MEM_PD_REG1 0x104 +#define HHI_VPU_MEM_PD_REG1 0x108 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12c + +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_AO 0x154 +#define HHI_SYS_OSCIN_CNTL 0x158 +#define HHI_SYS_CPU_CLK_CNTL1 0x15c +#define HHI_SYS_CPU_RESET_CNTL 0x160 +#define HHI_VID_CLK_DIV 0x164 + +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_AUD_CLK_CNTL 0x178 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_AUD_CLK_CNTL2 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_VID_PLL_CLK_DIV 0x1a0 +#define HHI_AUD_CLK_CNTL3 0x1a4 +#define HHI_MALI_CLK_CNTL 0x1b0 +#define HHI_VPU_CLK_CNTL 0x1bc + +#define HHI_HDMI_CLK_CNTL 0x1cc +#define HHI_VDEC_CLK_CNTL 0x1e0 +#define HHI_VDEC2_CLK_CNTL 0x1e4 +#define HHI_VDEC3_CLK_CNTL 0x1e8 +#define HHI_VDEC4_CLK_CNTL 0x1ec +#define HHI_HDCP22_CLK_CNTL 0x1f0 +#define HHI_VAPBCLK_CNTL 0x1f4 + +#define HHI_VPU_CLKB_CNTL 0x20c +#define HHI_USB_CLK_CNTL 0x220 +#define HHI_32K_CLK_CNTL 0x224 +#define HHI_GEN_CLK_CNTL 0x228 + +#define HHI_PCM_CLK_CNTL 0x258 +#define HHI_NAND_CLK_CNTL 0x25c +#define HHI_SD_EMMC_CLK_CNTL 0x264 + +#define HHI_MPLL_CNTL 0x280 +#define HHI_MPLL_CNTL2 0x284 +#define HHI_MPLL_CNTL3 0x288 +#define HHI_MPLL_CNTL4 0x28c +#define HHI_MPLL_CNTL5 0x290 +#define HHI_MPLL_CNTL6 0x294 +#define HHI_MPLL_CNTL7 0x298 +#define HHI_MPLL_CNTL8 0x29c +#define HHI_MPLL_CNTL9 0x2a0 +#define HHI_MPLL_CNTL10 0x2a4 + +#define HHI_MPLL3_CNTL0 0x2e0 +#define HHI_MPLL3_CNTL1 0x2e4 +#define HHI_VDAC_CNTL0 0x2f4 +#define HHI_VDAC_CNTL1 0x2f8 + +#define HHI_SYS_PLL_CNTL 0x300 +#define HHI_SYS_PLL_CNTL2 0x304 +#define HHI_SYS_PLL_CNTL3 0x308 +#define HHI_SYS_PLL_CNTL4 0x30c +#define HHI_SYS_PLL_CNTL5 0x310 +#define HHI_DPLL_TOP_I 0x318 +#define HHI_DPLL_TOP2_I 0x31c +#define HHI_HDMI_PLL_CNTL 0x320 +#define HHI_HDMI_PLL_CNTL2 0x324 +#define HHI_HDMI_PLL_CNTL3 0x328 +#define HHI_HDMI_PLL_CNTL4 0x32c +#define HHI_HDMI_PLL_CNTL5 0x330 +#define HHI_HDMI_PLL_CNTL6 0x334 +#define HHI_HDMI_PLL_CNTL_I 0x338 +#define HHI_HDMI_PLL_CNTL7 0x33c + +#define HHI_HDMI_PHY_CNTL0 0x3a0 +#define HHI_HDMI_PHY_CNTL1 0x3a4 +#define HHI_HDMI_PHY_CNTL2 0x3a8 +#define HHI_HDMI_PHY_CNTL3 0x3ac + +#define HHI_VID_LOCK_CLK_CNTL 0x3c8 +#define HHI_BT656_CLK_CNTL 0x3d4 +#define HHI_SAR_CLK_CNTL 0x3d8 + static const struct pll_params_table gxbb_gp0_pll_params_table[] = { PLL_PARAMS(32, 1), PLL_PARAMS(33, 1), @@ -1335,7 +1432,7 @@ static const struct clk_parent_data gxbb_sd_emmc_clk0_parent_data[] = { /* * Following these parent clocks, we should also have had mpll2, mpll3 * and gp0_pll but these clocks are too precious to be used here. All - * the necessary rates for MMC and NAND operation can be acheived using + * the necessary rates for MMC and NAND operation can be achieved using * xtal or fclk_div clocks */ }; @@ -3140,398 +3237,7 @@ static struct clk_hw *gxl_hw_clks[] = { [CLKID_ACODEC] = &gxl_acodec.hw, }; -static struct clk_regmap *const gxbb_clk_regmaps[] = { - &gxbb_clk81, - &gxbb_ddr, - &gxbb_dos, - &gxbb_isa, - &gxbb_pl301, - &gxbb_periphs, - &gxbb_spicc, - &gxbb_i2c, - &gxbb_sar_adc, - &gxbb_smart_card, - &gxbb_rng0, - &gxbb_uart0, - &gxbb_sdhc, - &gxbb_stream, - &gxbb_async_fifo, - &gxbb_sdio, - &gxbb_abuf, - &gxbb_hiu_iface, - &gxbb_assist_misc, - &gxbb_spi, - &gxbb_i2s_spdif, - &gxbb_eth, - &gxbb_demux, - &gxbb_aiu_glue, - &gxbb_iec958, - &gxbb_i2s_out, - &gxbb_amclk, - &gxbb_aififo2, - &gxbb_mixer, - &gxbb_mixer_iface, - &gxbb_adc, - &gxbb_blkmv, - &gxbb_aiu, - &gxbb_uart1, - &gxbb_g2d, - &gxbb_usb0, - &gxbb_usb1, - &gxbb_reset, - &gxbb_nand, - &gxbb_dos_parser, - &gxbb_usb, - &gxbb_vdin1, - &gxbb_ahb_arb0, - &gxbb_efuse, - &gxbb_boot_rom, - &gxbb_ahb_data_bus, - &gxbb_ahb_ctrl_bus, - &gxbb_hdmi_intr_sync, - &gxbb_hdmi_pclk, - &gxbb_usb1_ddr_bridge, - &gxbb_usb0_ddr_bridge, - &gxbb_mmc_pclk, - &gxbb_dvin, - &gxbb_uart2, - &gxbb_sana, - &gxbb_vpu_intr, - &gxbb_sec_ahb_ahb3_bridge, - &gxbb_clk81_a53, - &gxbb_vclk2_venci0, - &gxbb_vclk2_venci1, - &gxbb_vclk2_vencp0, - &gxbb_vclk2_vencp1, - &gxbb_gclk_venci_int0, - &gxbb_gclk_vencp_int, - &gxbb_dac_clk, - &gxbb_aoclk_gate, - &gxbb_iec958_gate, - &gxbb_enc480p, - &gxbb_rng1, - &gxbb_gclk_venci_int1, - &gxbb_vclk2_venclmcc, - &gxbb_vclk2_vencl, - &gxbb_vclk_other, - &gxbb_edp, - &gxbb_ao_media_cpu, - &gxbb_ao_ahb_sram, - &gxbb_ao_ahb_bus, - &gxbb_ao_iface, - &gxbb_ao_i2c, - &gxbb_emmc_a, - &gxbb_emmc_b, - &gxbb_emmc_c, - &gxbb_sar_adc_clk, - &gxbb_mali_0, - &gxbb_mali_1, - &gxbb_cts_amclk, - &gxbb_cts_mclk_i958, - &gxbb_32k_clk, - &gxbb_sd_emmc_a_clk0, - &gxbb_sd_emmc_b_clk0, - &gxbb_sd_emmc_c_clk0, - &gxbb_vpu_0, - &gxbb_vpu_1, - &gxbb_vapb_0, - &gxbb_vapb_1, - &gxbb_vapb, - &gxbb_mpeg_clk_div, - &gxbb_sar_adc_clk_div, - &gxbb_mali_0_div, - &gxbb_mali_1_div, - &gxbb_cts_mclk_i958_div, - &gxbb_32k_clk_div, - &gxbb_sd_emmc_a_clk0_div, - &gxbb_sd_emmc_b_clk0_div, - &gxbb_sd_emmc_c_clk0_div, - &gxbb_vpu_0_div, - &gxbb_vpu_1_div, - &gxbb_vapb_0_div, - &gxbb_vapb_1_div, - &gxbb_mpeg_clk_sel, - &gxbb_sar_adc_clk_sel, - &gxbb_mali_0_sel, - &gxbb_mali_1_sel, - &gxbb_mali, - &gxbb_cts_amclk_sel, - &gxbb_cts_mclk_i958_sel, - &gxbb_cts_i958, - &gxbb_32k_clk_sel, - &gxbb_sd_emmc_a_clk0_sel, - &gxbb_sd_emmc_b_clk0_sel, - &gxbb_sd_emmc_c_clk0_sel, - &gxbb_vpu_0_sel, - &gxbb_vpu_1_sel, - &gxbb_vpu, - &gxbb_vapb_0_sel, - &gxbb_vapb_1_sel, - &gxbb_vapb_sel, - &gxbb_mpll0, - &gxbb_mpll1, - &gxbb_mpll2, - &gxbb_mpll0_div, - &gxbb_mpll1_div, - &gxbb_mpll2_div, - &gxbb_cts_amclk_div, - &gxbb_fixed_pll, - &gxbb_sys_pll, - &gxbb_mpll_prediv, - &gxbb_fclk_div2, - &gxbb_fclk_div3, - &gxbb_fclk_div4, - &gxbb_fclk_div5, - &gxbb_fclk_div7, - &gxbb_vdec_1_sel, - &gxbb_vdec_1_div, - &gxbb_vdec_1, - &gxbb_vdec_hevc_sel, - &gxbb_vdec_hevc_div, - &gxbb_vdec_hevc, - &gxbb_gen_clk_sel, - &gxbb_gen_clk_div, - &gxbb_gen_clk, - &gxbb_fixed_pll_dco, - &gxbb_sys_pll_dco, - &gxbb_gp0_pll, - &gxbb_vid_pll, - &gxbb_vid_pll_sel, - &gxbb_vid_pll_div, - &gxbb_vclk, - &gxbb_vclk_sel, - &gxbb_vclk_div, - &gxbb_vclk_input, - &gxbb_vclk_div1, - &gxbb_vclk_div2_en, - &gxbb_vclk_div4_en, - &gxbb_vclk_div6_en, - &gxbb_vclk_div12_en, - &gxbb_vclk2, - &gxbb_vclk2_sel, - &gxbb_vclk2_div, - &gxbb_vclk2_input, - &gxbb_vclk2_div1, - &gxbb_vclk2_div2_en, - &gxbb_vclk2_div4_en, - &gxbb_vclk2_div6_en, - &gxbb_vclk2_div12_en, - &gxbb_cts_enci, - &gxbb_cts_enci_sel, - &gxbb_cts_encp, - &gxbb_cts_encp_sel, - &gxbb_cts_vdac, - &gxbb_cts_vdac_sel, - &gxbb_hdmi_tx, - &gxbb_hdmi_tx_sel, - &gxbb_hdmi_sel, - &gxbb_hdmi_div, - &gxbb_hdmi, - &gxbb_gp0_pll_dco, - &gxbb_hdmi_pll, - &gxbb_hdmi_pll_od, - &gxbb_hdmi_pll_od2, - &gxbb_hdmi_pll_dco, -}; - -static struct clk_regmap *const gxl_clk_regmaps[] = { - &gxbb_clk81, - &gxbb_ddr, - &gxbb_dos, - &gxbb_isa, - &gxbb_pl301, - &gxbb_periphs, - &gxbb_spicc, - &gxbb_i2c, - &gxbb_sar_adc, - &gxbb_smart_card, - &gxbb_rng0, - &gxbb_uart0, - &gxbb_sdhc, - &gxbb_stream, - &gxbb_async_fifo, - &gxbb_sdio, - &gxbb_abuf, - &gxbb_hiu_iface, - &gxbb_assist_misc, - &gxbb_spi, - &gxbb_i2s_spdif, - &gxbb_eth, - &gxbb_demux, - &gxbb_aiu_glue, - &gxbb_iec958, - &gxbb_i2s_out, - &gxbb_amclk, - &gxbb_aififo2, - &gxbb_mixer, - &gxbb_mixer_iface, - &gxbb_adc, - &gxbb_blkmv, - &gxbb_aiu, - &gxbb_uart1, - &gxbb_g2d, - &gxbb_usb0, - &gxbb_usb1, - &gxbb_reset, - &gxbb_nand, - &gxbb_dos_parser, - &gxbb_usb, - &gxbb_vdin1, - &gxbb_ahb_arb0, - &gxbb_efuse, - &gxbb_boot_rom, - &gxbb_ahb_data_bus, - &gxbb_ahb_ctrl_bus, - &gxbb_hdmi_intr_sync, - &gxbb_hdmi_pclk, - &gxbb_usb1_ddr_bridge, - &gxbb_usb0_ddr_bridge, - &gxbb_mmc_pclk, - &gxbb_dvin, - &gxbb_uart2, - &gxbb_sana, - &gxbb_vpu_intr, - &gxbb_sec_ahb_ahb3_bridge, - &gxbb_clk81_a53, - &gxbb_vclk2_venci0, - &gxbb_vclk2_venci1, - &gxbb_vclk2_vencp0, - &gxbb_vclk2_vencp1, - &gxbb_gclk_venci_int0, - &gxbb_gclk_vencp_int, - &gxbb_dac_clk, - &gxbb_aoclk_gate, - &gxbb_iec958_gate, - &gxbb_enc480p, - &gxbb_rng1, - &gxbb_gclk_venci_int1, - &gxbb_vclk2_venclmcc, - &gxbb_vclk2_vencl, - &gxbb_vclk_other, - &gxbb_edp, - &gxbb_ao_media_cpu, - &gxbb_ao_ahb_sram, - &gxbb_ao_ahb_bus, - &gxbb_ao_iface, - &gxbb_ao_i2c, - &gxbb_emmc_a, - &gxbb_emmc_b, - &gxbb_emmc_c, - &gxbb_sar_adc_clk, - &gxbb_mali_0, - &gxbb_mali_1, - &gxbb_cts_amclk, - &gxbb_cts_mclk_i958, - &gxbb_32k_clk, - &gxbb_sd_emmc_a_clk0, - &gxbb_sd_emmc_b_clk0, - &gxbb_sd_emmc_c_clk0, - &gxbb_vpu_0, - &gxbb_vpu_1, - &gxbb_vapb_0, - &gxbb_vapb_1, - &gxbb_vapb, - &gxbb_mpeg_clk_div, - &gxbb_sar_adc_clk_div, - &gxbb_mali_0_div, - &gxbb_mali_1_div, - &gxbb_cts_mclk_i958_div, - &gxbb_32k_clk_div, - &gxbb_sd_emmc_a_clk0_div, - &gxbb_sd_emmc_b_clk0_div, - &gxbb_sd_emmc_c_clk0_div, - &gxbb_vpu_0_div, - &gxbb_vpu_1_div, - &gxbb_vapb_0_div, - &gxbb_vapb_1_div, - &gxbb_mpeg_clk_sel, - &gxbb_sar_adc_clk_sel, - &gxbb_mali_0_sel, - &gxbb_mali_1_sel, - &gxbb_mali, - &gxbb_cts_amclk_sel, - &gxbb_cts_mclk_i958_sel, - &gxbb_cts_i958, - &gxbb_32k_clk_sel, - &gxbb_sd_emmc_a_clk0_sel, - &gxbb_sd_emmc_b_clk0_sel, - &gxbb_sd_emmc_c_clk0_sel, - &gxbb_vpu_0_sel, - &gxbb_vpu_1_sel, - &gxbb_vpu, - &gxbb_vapb_0_sel, - &gxbb_vapb_1_sel, - &gxbb_vapb_sel, - &gxbb_mpll0, - &gxbb_mpll1, - &gxbb_mpll2, - &gxl_mpll0_div, - &gxbb_mpll1_div, - &gxbb_mpll2_div, - &gxbb_cts_amclk_div, - &gxbb_fixed_pll, - &gxbb_sys_pll, - &gxbb_mpll_prediv, - &gxbb_fclk_div2, - &gxbb_fclk_div3, - &gxbb_fclk_div4, - &gxbb_fclk_div5, - &gxbb_fclk_div7, - &gxbb_vdec_1_sel, - &gxbb_vdec_1_div, - &gxbb_vdec_1, - &gxbb_vdec_hevc_sel, - &gxbb_vdec_hevc_div, - &gxbb_vdec_hevc, - &gxbb_gen_clk_sel, - &gxbb_gen_clk_div, - &gxbb_gen_clk, - &gxbb_fixed_pll_dco, - &gxbb_sys_pll_dco, - &gxbb_gp0_pll, - &gxbb_vid_pll, - &gxbb_vid_pll_sel, - &gxbb_vid_pll_div, - &gxbb_vclk, - &gxbb_vclk_sel, - &gxbb_vclk_div, - &gxbb_vclk_input, - &gxbb_vclk_div1, - &gxbb_vclk_div2_en, - &gxbb_vclk_div4_en, - &gxbb_vclk_div6_en, - &gxbb_vclk_div12_en, - &gxbb_vclk2, - &gxbb_vclk2_sel, - &gxbb_vclk2_div, - &gxbb_vclk2_input, - &gxbb_vclk2_div1, - &gxbb_vclk2_div2_en, - &gxbb_vclk2_div4_en, - &gxbb_vclk2_div6_en, - &gxbb_vclk2_div12_en, - &gxbb_cts_enci, - &gxbb_cts_enci_sel, - &gxbb_cts_encp, - &gxbb_cts_encp_sel, - &gxbb_cts_vdac, - &gxbb_cts_vdac_sel, - &gxbb_hdmi_tx, - &gxbb_hdmi_tx_sel, - &gxbb_hdmi_sel, - &gxbb_hdmi_div, - &gxbb_hdmi, - &gxl_gp0_pll_dco, - &gxl_hdmi_pll, - &gxl_hdmi_pll_od, - &gxl_hdmi_pll_od2, - &gxl_hdmi_pll_dco, - &gxl_acodec, -}; - static const struct meson_eeclkc_data gxbb_clkc_data = { - .regmap_clks = gxbb_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(gxbb_clk_regmaps), .hw_clks = { .hws = gxbb_hw_clks, .num = ARRAY_SIZE(gxbb_hw_clks), @@ -3539,8 +3245,6 @@ static const struct meson_eeclkc_data gxbb_clkc_data = { }; static const struct meson_eeclkc_data gxl_clkc_data = { - .regmap_clks = gxl_clk_regmaps, - .regmap_clk_num = ARRAY_SIZE(gxl_clk_regmaps), .hw_clks = { .hws = gxl_hw_clks, .num = ARRAY_SIZE(gxl_hw_clks), diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h deleted file mode 100644 index ba5f39a8d746..000000000000 --- a/drivers/clk/meson/gxbb.h +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * Copyright (c) 2016 AmLogic, Inc. - * Author: Michael Turquette - */ - -#ifndef __GXBB_H -#define __GXBB_H - -/* - * Clock controller register offsets - * - * Register offsets from the data sheet are listed in comment blocks below. - * Those offsets must be multiplied by 4 before adding them to the base address - * to get the right value - */ -#define SCR 0x2C /* 0x0b offset in data sheet */ -#define TIMEOUT_VALUE 0x3c /* 0x0f offset in data sheet */ - -#define HHI_GP0_PLL_CNTL 0x40 /* 0x10 offset in data sheet */ -#define HHI_GP0_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */ -#define HHI_GP0_PLL_CNTL3 0x48 /* 0x12 offset in data sheet */ -#define HHI_GP0_PLL_CNTL4 0x4c /* 0x13 offset in data sheet */ -#define HHI_GP0_PLL_CNTL5 0x50 /* 0x14 offset in data sheet */ -#define HHI_GP0_PLL_CNTL1 0x58 /* 0x16 offset in data sheet */ - -#define HHI_XTAL_DIVN_CNTL 0xbc /* 0x2f offset in data sheet */ -#define HHI_TIMER90K 0xec /* 0x3b offset in data sheet */ - -#define HHI_MEM_PD_REG0 0x100 /* 0x40 offset in data sheet */ -#define HHI_MEM_PD_REG1 0x104 /* 0x41 offset in data sheet */ -#define HHI_VPU_MEM_PD_REG1 0x108 /* 0x42 offset in data sheet */ -#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */ -#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */ - -#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */ -#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */ -#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ -#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */ -#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */ -#define HHI_SYS_OSCIN_CNTL 0x158 /* 0x56 offset in data sheet */ -#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */ -#define HHI_SYS_CPU_RESET_CNTL 0x160 /* 0x58 offset in data sheet */ -#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ - -#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */ -#define HHI_AUD_CLK_CNTL 0x178 /* 0x5e offset in data sheet */ -#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ -#define HHI_AUD_CLK_CNTL2 0x190 /* 0x64 offset in data sheet */ -#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ -#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ -#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */ -#define HHI_AUD_CLK_CNTL3 0x1a4 /* 0x69 offset in data sheet */ -#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */ -#define HHI_VPU_CLK_CNTL 0x1bC /* 0x6f offset in data sheet */ - -#define HHI_HDMI_CLK_CNTL 0x1CC /* 0x73 offset in data sheet */ -#define HHI_VDEC_CLK_CNTL 0x1E0 /* 0x78 offset in data sheet */ -#define HHI_VDEC2_CLK_CNTL 0x1E4 /* 0x79 offset in data sheet */ -#define HHI_VDEC3_CLK_CNTL 0x1E8 /* 0x7a offset in data sheet */ -#define HHI_VDEC4_CLK_CNTL 0x1EC /* 0x7b offset in data sheet */ -#define HHI_HDCP22_CLK_CNTL 0x1F0 /* 0x7c offset in data sheet */ -#define HHI_VAPBCLK_CNTL 0x1F4 /* 0x7d offset in data sheet */ - -#define HHI_VPU_CLKB_CNTL 0x20C /* 0x83 offset in data sheet */ -#define HHI_USB_CLK_CNTL 0x220 /* 0x88 offset in data sheet */ -#define HHI_32K_CLK_CNTL 0x224 /* 0x89 offset in data sheet */ -#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */ - -#define HHI_PCM_CLK_CNTL 0x258 /* 0x96 offset in data sheet */ -#define HHI_NAND_CLK_CNTL 0x25C /* 0x97 offset in data sheet */ -#define HHI_SD_EMMC_CLK_CNTL 0x264 /* 0x99 offset in data sheet */ - -#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ -#define HHI_MPLL_CNTL2 0x284 /* 0xa1 offset in data sheet */ -#define HHI_MPLL_CNTL3 0x288 /* 0xa2 offset in data sheet */ -#define HHI_MPLL_CNTL4 0x28C /* 0xa3 offset in data sheet */ -#define HHI_MPLL_CNTL5 0x290 /* 0xa4 offset in data sheet */ -#define HHI_MPLL_CNTL6 0x294 /* 0xa5 offset in data sheet */ -#define HHI_MPLL_CNTL7 0x298 /* MP0, 0xa6 offset in data sheet */ -#define HHI_MPLL_CNTL8 0x29C /* MP1, 0xa7 offset in data sheet */ -#define HHI_MPLL_CNTL9 0x2A0 /* MP2, 0xa8 offset in data sheet */ -#define HHI_MPLL_CNTL10 0x2A4 /* MP2, 0xa9 offset in data sheet */ - -#define HHI_MPLL3_CNTL0 0x2E0 /* 0xb8 offset in data sheet */ -#define HHI_MPLL3_CNTL1 0x2E4 /* 0xb9 offset in data sheet */ -#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ -#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ - -#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */ -#define HHI_SYS_PLL_CNTL2 0x304 /* 0xc1 offset in data sheet */ -#define HHI_SYS_PLL_CNTL3 0x308 /* 0xc2 offset in data sheet */ -#define HHI_SYS_PLL_CNTL4 0x30c /* 0xc3 offset in data sheet */ -#define HHI_SYS_PLL_CNTL5 0x310 /* 0xc4 offset in data sheet */ -#define HHI_DPLL_TOP_I 0x318 /* 0xc6 offset in data sheet */ -#define HHI_DPLL_TOP2_I 0x31C /* 0xc7 offset in data sheet */ -#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ -#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */ -#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */ -#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */ -#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */ -#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */ -#define HHI_HDMI_PLL_CNTL_I 0x338 /* 0xce offset in data sheet */ -#define HHI_HDMI_PLL_CNTL7 0x33C /* 0xcf offset in data sheet */ - -#define HHI_HDMI_PHY_CNTL0 0x3A0 /* 0xe8 offset in data sheet */ -#define HHI_HDMI_PHY_CNTL1 0x3A4 /* 0xe9 offset in data sheet */ -#define HHI_HDMI_PHY_CNTL2 0x3A8 /* 0xea offset in data sheet */ -#define HHI_HDMI_PHY_CNTL3 0x3AC /* 0xeb offset in data sheet */ - -#define HHI_VID_LOCK_CLK_CNTL 0x3C8 /* 0xf2 offset in data sheet */ -#define HHI_BT656_CLK_CNTL 0x3D4 /* 0xf5 offset in data sheet */ -#define HHI_SAR_CLK_CNTL 0x3D8 /* 0xf6 offset in data sheet */ - -#endif /* __GXBB_H */ diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index 995be51987f4..894c02fda072 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -18,6 +18,7 @@ #include #include "meson-aoclk.h" +#include "clk-regmap.h" static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev, unsigned long id) @@ -70,10 +71,6 @@ int meson_aoclkc_probe(struct platform_device *pdev) return ret; } - /* Populate regmap */ - for (clkid = 0; clkid < data->num_clks; clkid++) - data->clks[clkid]->map = regmap; - /* Register all clks */ for (clkid = 0; clkid < data->hw_clks.num; clkid++) { if (!data->hw_clks.hws[clkid]) diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h index 308be3e4814a..ea5fc61308af 100644 --- a/drivers/clk/meson/meson-aoclk.h +++ b/drivers/clk/meson/meson-aoclk.h @@ -23,8 +23,6 @@ struct meson_aoclk_data { const unsigned int reset_reg; const int num_reset; const unsigned int *reset; - const int num_clks; - struct clk_regmap **clks; struct meson_clk_hw_data hw_clks; }; diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c index 3053ee7425eb..6236bf970d79 100644 --- a/drivers/clk/meson/meson-eeclk.c +++ b/drivers/clk/meson/meson-eeclk.c @@ -39,10 +39,6 @@ int meson_eeclkc_probe(struct platform_device *pdev) if (data->init_count) regmap_multi_reg_write(map, data->init_regs, data->init_count); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < data->regmap_clk_num; i++) - data->regmap_clks[i]->map = map; - for (i = 0; i < data->hw_clks.num; i++) { /* array might be sparse */ if (!data->hw_clks.hws[i]) diff --git a/drivers/clk/meson/meson-eeclk.h b/drivers/clk/meson/meson-eeclk.h index 37a48b75c660..6a81d67b46b2 100644 --- a/drivers/clk/meson/meson-eeclk.h +++ b/drivers/clk/meson/meson-eeclk.h @@ -14,8 +14,6 @@ struct platform_device; struct meson_eeclkc_data { - struct clk_regmap *const *regmap_clks; - unsigned int regmap_clk_num; const struct reg_sequence *init_regs; unsigned int init_count; struct meson_clk_hw_data hw_clks; diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c index 4b73ea244b63..1975fc3987e2 100644 --- a/drivers/clk/meson/meson8-ddr.c +++ b/drivers/clk/meson/meson8-ddr.c @@ -85,11 +85,6 @@ static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = { .num = 2, }; -static struct clk_regmap *const meson8_ddr_clk_regmaps[] = { - &meson8_ddr_pll_dco, - &meson8_ddr_pll, -}; - static const struct regmap_config meson8_ddr_clkc_regmap_config = { .reg_bits = 8, .val_bits = 32, @@ -113,10 +108,6 @@ static int meson8_ddr_clkc_probe(struct platform_device *pdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); - /* Populate regmap */ - for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++) - meson8_ddr_clk_regmaps[i]->map = regmap; - /* Register all clks */ for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) { hw = meson8_ddr_clk_hw_onecell_data.hws[i]; diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index e4b474c5f86c..206538326614 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -16,7 +16,6 @@ #include #include -#include "meson8b.h" #include "clk-regmap.h" #include "meson-clkc-utils.h" #include "clk-pll.h" @@ -25,6 +24,72 @@ #include #include +/* + * Clock controller register offsets + * + * Register offsets from the HardKernel[0] data sheet must be multiplied + * by 4 before adding them to the base address to get the right value + * + * [0] https://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf + */ +#define HHI_GP_PLL_CNTL 0x40 +#define HHI_GP_PLL_CNTL2 0x44 +#define HHI_GP_PLL_CNTL3 0x48 +#define HHI_GP_PLL_CNTL4 0x4C +#define HHI_GP_PLL_CNTL5 0x50 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12c +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_AO 0x154 +#define HHI_SYS_CPU_CLK_CNTL1 0x15c +#define HHI_VID_CLK_DIV 0x164 +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_AUD_CLK_CNTL 0x178 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_AUD_CLK_CNTL2 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_VID_DIVIDER_CNTL 0x198 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_MALI_CLK_CNTL 0x1b0 +#define HHI_VPU_CLK_CNTL 0x1bc +#define HHI_HDMI_CLK_CNTL 0x1cc +#define HHI_VDEC_CLK_CNTL 0x1e0 +#define HHI_VDEC2_CLK_CNTL 0x1e4 +#define HHI_VDEC3_CLK_CNTL 0x1e8 +#define HHI_NAND_CLK_CNTL 0x25c +#define HHI_MPLL_CNTL 0x280 +#define HHI_SYS_PLL_CNTL 0x300 +#define HHI_VID_PLL_CNTL 0x320 +#define HHI_VID_PLL_CNTL2 0x324 +#define HHI_VID_PLL_CNTL3 0x328 +#define HHI_VID_PLL_CNTL4 0x32c +#define HHI_VID_PLL_CNTL5 0x330 +#define HHI_VID_PLL_CNTL6 0x334 +#define HHI_VID2_PLL_CNTL 0x380 +#define HHI_VID2_PLL_CNTL2 0x384 +#define HHI_VID2_PLL_CNTL3 0x388 +#define HHI_VID2_PLL_CNTL4 0x38c +#define HHI_VID2_PLL_CNTL5 0x390 +#define HHI_VID2_PLL_CNTL6 0x394 + +/* + * MPLL register offeset taken from the S905 datasheet. Vendor kernel source + * confirm these are the same for the S805. + */ +#define HHI_MPLL_CNTL 0x280 +#define HHI_MPLL_CNTL2 0x284 +#define HHI_MPLL_CNTL3 0x288 +#define HHI_MPLL_CNTL4 0x28c +#define HHI_MPLL_CNTL5 0x290 +#define HHI_MPLL_CNTL6 0x294 +#define HHI_MPLL_CNTL7 0x298 +#define HHI_MPLL_CNTL8 0x29c +#define HHI_MPLL_CNTL9 0x2a0 +#define HHI_MPLL_CNTL10 0x2a4 + struct meson8b_clk_reset { struct reset_controller_dev reset; struct regmap *regmap; @@ -3407,202 +3472,6 @@ static struct clk_hw *meson8m2_hw_clks[] = { [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw, }; -static struct clk_regmap *const meson8b_clk_regmaps[] = { - &meson8b_clk81, - &meson8b_ddr, - &meson8b_dos, - &meson8b_isa, - &meson8b_pl301, - &meson8b_periphs, - &meson8b_spicc, - &meson8b_i2c, - &meson8b_sar_adc, - &meson8b_smart_card, - &meson8b_rng0, - &meson8b_uart0, - &meson8b_sdhc, - &meson8b_stream, - &meson8b_async_fifo, - &meson8b_sdio, - &meson8b_abuf, - &meson8b_hiu_iface, - &meson8b_assist_misc, - &meson8b_spi, - &meson8b_i2s_spdif, - &meson8b_eth, - &meson8b_demux, - &meson8b_aiu_glue, - &meson8b_iec958, - &meson8b_i2s_out, - &meson8b_amclk, - &meson8b_aififo2, - &meson8b_mixer, - &meson8b_mixer_iface, - &meson8b_adc, - &meson8b_blkmv, - &meson8b_aiu, - &meson8b_uart1, - &meson8b_g2d, - &meson8b_usb0, - &meson8b_usb1, - &meson8b_reset, - &meson8b_nand, - &meson8b_dos_parser, - &meson8b_usb, - &meson8b_vdin1, - &meson8b_ahb_arb0, - &meson8b_efuse, - &meson8b_boot_rom, - &meson8b_ahb_data_bus, - &meson8b_ahb_ctrl_bus, - &meson8b_hdmi_intr_sync, - &meson8b_hdmi_pclk, - &meson8b_usb1_ddr_bridge, - &meson8b_usb0_ddr_bridge, - &meson8b_mmc_pclk, - &meson8b_dvin, - &meson8b_uart2, - &meson8b_sana, - &meson8b_vpu_intr, - &meson8b_sec_ahb_ahb3_bridge, - &meson8b_clk81_a9, - &meson8b_vclk2_venci0, - &meson8b_vclk2_venci1, - &meson8b_vclk2_vencp0, - &meson8b_vclk2_vencp1, - &meson8b_gclk_venci_int, - &meson8b_gclk_vencp_int, - &meson8b_dac_clk, - &meson8b_aoclk_gate, - &meson8b_iec958_gate, - &meson8b_enc480p, - &meson8b_rng1, - &meson8b_gclk_vencl_int, - &meson8b_vclk2_venclmcc, - &meson8b_vclk2_vencl, - &meson8b_vclk2_other, - &meson8b_edp, - &meson8b_ao_media_cpu, - &meson8b_ao_ahb_sram, - &meson8b_ao_ahb_bus, - &meson8b_ao_iface, - &meson8b_mpeg_clk_div, - &meson8b_mpeg_clk_sel, - &meson8b_mpll0, - &meson8b_mpll1, - &meson8b_mpll2, - &meson8b_mpll0_div, - &meson8b_mpll1_div, - &meson8b_mpll2_div, - &meson8b_fixed_pll, - &meson8b_sys_pll, - &meson8b_cpu_in_sel, - &meson8b_cpu_scale_div, - &meson8b_cpu_scale_out_sel, - &meson8b_cpu_clk, - &meson8b_mpll_prediv, - &meson8b_fclk_div2, - &meson8b_fclk_div3, - &meson8b_fclk_div4, - &meson8b_fclk_div5, - &meson8b_fclk_div7, - &meson8b_nand_clk_sel, - &meson8b_nand_clk_div, - &meson8b_nand_clk_gate, - &meson8b_fixed_pll_dco, - &meson8b_hdmi_pll_dco, - &meson8b_sys_pll_dco, - &meson8b_apb_clk_sel, - &meson8b_apb_clk_gate, - &meson8b_periph_clk_sel, - &meson8b_periph_clk_gate, - &meson8b_axi_clk_sel, - &meson8b_axi_clk_gate, - &meson8b_l2_dram_clk_sel, - &meson8b_l2_dram_clk_gate, - &meson8b_hdmi_pll_lvds_out, - &meson8b_hdmi_pll_hdmi_out, - &meson8b_vid_pll_in_sel, - &meson8b_vid_pll_in_en, - &meson8b_vid_pll_pre_div, - &meson8b_vid_pll_post_div, - &meson8b_vid_pll, - &meson8b_vid_pll_final_div, - &meson8b_vclk_in_sel, - &meson8b_vclk_in_en, - &meson8b_vclk_en, - &meson8b_vclk_div1_gate, - &meson8b_vclk_div2_div_gate, - &meson8b_vclk_div4_div_gate, - &meson8b_vclk_div6_div_gate, - &meson8b_vclk_div12_div_gate, - &meson8b_vclk2_in_sel, - &meson8b_vclk2_clk_in_en, - &meson8b_vclk2_clk_en, - &meson8b_vclk2_div1_gate, - &meson8b_vclk2_div2_div_gate, - &meson8b_vclk2_div4_div_gate, - &meson8b_vclk2_div6_div_gate, - &meson8b_vclk2_div12_div_gate, - &meson8b_cts_enct_sel, - &meson8b_cts_enct, - &meson8b_cts_encp_sel, - &meson8b_cts_encp, - &meson8b_cts_enci_sel, - &meson8b_cts_enci, - &meson8b_hdmi_tx_pixel_sel, - &meson8b_hdmi_tx_pixel, - &meson8b_cts_encl_sel, - &meson8b_cts_encl, - &meson8b_cts_vdac0_sel, - &meson8b_cts_vdac0, - &meson8b_hdmi_sys_sel, - &meson8b_hdmi_sys_div, - &meson8b_hdmi_sys, - &meson8b_mali_0_sel, - &meson8b_mali_0_div, - &meson8b_mali_0, - &meson8b_mali_1_sel, - &meson8b_mali_1_div, - &meson8b_mali_1, - &meson8b_mali, - &meson8m2_gp_pll_dco, - &meson8m2_gp_pll, - &meson8b_vpu_0_sel, - &meson8m2_vpu_0_sel, - &meson8b_vpu_0_div, - &meson8b_vpu_0, - &meson8b_vpu_1_sel, - &meson8m2_vpu_1_sel, - &meson8b_vpu_1_div, - &meson8b_vpu_1, - &meson8b_vpu, - &meson8b_vdec_1_sel, - &meson8b_vdec_1_1_div, - &meson8b_vdec_1_1, - &meson8b_vdec_1_2_div, - &meson8b_vdec_1_2, - &meson8b_vdec_1, - &meson8b_vdec_hcodec_sel, - &meson8b_vdec_hcodec_div, - &meson8b_vdec_hcodec, - &meson8b_vdec_2_sel, - &meson8b_vdec_2_div, - &meson8b_vdec_2, - &meson8b_vdec_hevc_sel, - &meson8b_vdec_hevc_div, - &meson8b_vdec_hevc_en, - &meson8b_vdec_hevc, - &meson8b_cts_amclk, - &meson8b_cts_amclk_sel, - &meson8b_cts_amclk_div, - &meson8b_cts_mclk_i958_sel, - &meson8b_cts_mclk_i958_div, - &meson8b_cts_mclk_i958, - &meson8b_cts_i958, - &meson8b_vid_pll_lvds_en, -}; - static const struct meson8b_clk_reset_line { u32 reg; u8 bit_idx; @@ -3819,10 +3688,6 @@ static void __init meson8b_clkc_init_common(struct device_node *np, return; } - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(meson8b_clk_regmaps); i++) - meson8b_clk_regmaps[i]->map = map; - /* * register all clks and start with the first used ID (which is * CLKID_PLL_FIXED) diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h deleted file mode 100644 index a5b6e67eeefb..000000000000 --- a/drivers/clk/meson/meson8b.h +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2015 Endless Mobile, Inc. - * Author: Carlo Caione - * - * Copyright (c) 2016 BayLibre, Inc. - * Michael Turquette - */ - -#ifndef __MESON8B_H -#define __MESON8B_H - -/* - * Clock controller register offsets - * - * Register offsets from the HardKernel[0] data sheet are listed in comment - * blocks below. Those offsets must be multiplied by 4 before adding them to - * the base address to get the right value - * - * [0] https://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf - */ -#define HHI_GP_PLL_CNTL 0x40 /* 0x10 offset in data sheet */ -#define HHI_GP_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */ -#define HHI_GP_PLL_CNTL3 0x48 /* 0x12 offset in data sheet */ -#define HHI_GP_PLL_CNTL4 0x4C /* 0x13 offset in data sheet */ -#define HHI_GP_PLL_CNTL5 0x50 /* 0x14 offset in data sheet */ -#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */ -#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */ -#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */ -#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */ -#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ -#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */ -#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */ -#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */ -#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ -#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */ -#define HHI_AUD_CLK_CNTL 0x178 /* 0x5e offset in data sheet */ -#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ -#define HHI_AUD_CLK_CNTL2 0x190 /* 0x64 offset in data sheet */ -#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ -#define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */ -#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ -#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */ -#define HHI_VPU_CLK_CNTL 0x1bc /* 0x6f offset in data sheet */ -#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ -#define HHI_VDEC_CLK_CNTL 0x1e0 /* 0x78 offset in data sheet */ -#define HHI_VDEC2_CLK_CNTL 0x1e4 /* 0x79 offset in data sheet */ -#define HHI_VDEC3_CLK_CNTL 0x1e8 /* 0x7a offset in data sheet */ -#define HHI_NAND_CLK_CNTL 0x25c /* 0x97 offset in data sheet */ -#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ -#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */ -#define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ -#define HHI_VID_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */ -#define HHI_VID_PLL_CNTL3 0x328 /* 0xca offset in data sheet */ -#define HHI_VID_PLL_CNTL4 0x32c /* 0xcb offset in data sheet */ -#define HHI_VID_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */ -#define HHI_VID_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */ -#define HHI_VID2_PLL_CNTL 0x380 /* 0xe0 offset in data sheet */ -#define HHI_VID2_PLL_CNTL2 0x384 /* 0xe1 offset in data sheet */ -#define HHI_VID2_PLL_CNTL3 0x388 /* 0xe2 offset in data sheet */ -#define HHI_VID2_PLL_CNTL4 0x38c /* 0xe3 offset in data sheet */ -#define HHI_VID2_PLL_CNTL5 0x390 /* 0xe4 offset in data sheet */ -#define HHI_VID2_PLL_CNTL6 0x394 /* 0xe5 offset in data sheet */ - -/* - * MPLL register offeset taken from the S905 datasheet. Vendor kernel source - * confirm these are the same for the S805. - */ -#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ -#define HHI_MPLL_CNTL2 0x284 /* 0xa1 offset in data sheet */ -#define HHI_MPLL_CNTL3 0x288 /* 0xa2 offset in data sheet */ -#define HHI_MPLL_CNTL4 0x28C /* 0xa3 offset in data sheet */ -#define HHI_MPLL_CNTL5 0x290 /* 0xa4 offset in data sheet */ -#define HHI_MPLL_CNTL6 0x294 /* 0xa5 offset in data sheet */ -#define HHI_MPLL_CNTL7 0x298 /* 0xa6 offset in data sheet */ -#define HHI_MPLL_CNTL8 0x29C /* 0xa7 offset in data sheet */ -#define HHI_MPLL_CNTL9 0x2A0 /* 0xa8 offset in data sheet */ -#define HHI_MPLL_CNTL10 0x2A4 /* 0xa9 offset in data sheet */ - -#endif /* __MESON8B_H */ diff --git a/drivers/clk/meson/s4-peripherals.c b/drivers/clk/meson/s4-peripherals.c index 8a4037377787..c9400cf54c84 100644 --- a/drivers/clk/meson/s4-peripherals.c +++ b/drivers/clk/meson/s4-peripherals.c @@ -13,10 +13,55 @@ #include "clk-regmap.h" #include "vid-pll-div.h" #include "clk-dualdiv.h" -#include "s4-peripherals.h" #include "meson-clkc-utils.h" #include +#define CLKCTRL_RTC_BY_OSCIN_CTRL0 0x008 +#define CLKCTRL_RTC_BY_OSCIN_CTRL1 0x00c +#define CLKCTRL_RTC_CTRL 0x010 +#define CLKCTRL_SYS_CLK_CTRL0 0x040 +#define CLKCTRL_SYS_CLK_EN0_REG0 0x044 +#define CLKCTRL_SYS_CLK_EN0_REG1 0x048 +#define CLKCTRL_SYS_CLK_EN0_REG2 0x04c +#define CLKCTRL_SYS_CLK_EN0_REG3 0x050 +#define CLKCTRL_CECA_CTRL0 0x088 +#define CLKCTRL_CECA_CTRL1 0x08c +#define CLKCTRL_CECB_CTRL0 0x090 +#define CLKCTRL_CECB_CTRL1 0x094 +#define CLKCTRL_SC_CLK_CTRL 0x098 +#define CLKCTRL_CLK12_24_CTRL 0x0a8 +#define CLKCTRL_VID_CLK_CTRL 0x0c0 +#define CLKCTRL_VID_CLK_CTRL2 0x0c4 +#define CLKCTRL_VID_CLK_DIV 0x0c8 +#define CLKCTRL_VIID_CLK_DIV 0x0cc +#define CLKCTRL_VIID_CLK_CTRL 0x0d0 +#define CLKCTRL_HDMI_CLK_CTRL 0x0e0 +#define CLKCTRL_VID_PLL_CLK_DIV 0x0e4 +#define CLKCTRL_VPU_CLK_CTRL 0x0e8 +#define CLKCTRL_VPU_CLKB_CTRL 0x0ec +#define CLKCTRL_VPU_CLKC_CTRL 0x0f0 +#define CLKCTRL_VID_LOCK_CLK_CTRL 0x0f4 +#define CLKCTRL_VDIN_MEAS_CLK_CTRL 0x0f8 +#define CLKCTRL_VAPBCLK_CTRL 0x0fc +#define CLKCTRL_HDCP22_CTRL 0x100 +#define CLKCTRL_VDEC_CLK_CTRL 0x140 +#define CLKCTRL_VDEC2_CLK_CTRL 0x144 +#define CLKCTRL_VDEC3_CLK_CTRL 0x148 +#define CLKCTRL_VDEC4_CLK_CTRL 0x14c +#define CLKCTRL_TS_CLK_CTRL 0x158 +#define CLKCTRL_MALI_CLK_CTRL 0x15c +#define CLKCTRL_NAND_CLK_CTRL 0x168 +#define CLKCTRL_SD_EMMC_CLK_CTRL 0x16c +#define CLKCTRL_SPICC_CLK_CTRL 0x174 +#define CLKCTRL_GEN_CLK_CTRL 0x178 +#define CLKCTRL_SAR_CLK_CTRL 0x17c +#define CLKCTRL_PWM_CLK_AB_CTRL 0x180 +#define CLKCTRL_PWM_CLK_CD_CTRL 0x184 +#define CLKCTRL_PWM_CLK_EF_CTRL 0x188 +#define CLKCTRL_PWM_CLK_GH_CTRL 0x18c +#define CLKCTRL_PWM_CLK_IJ_CTRL 0x190 +#define CLKCTRL_DEMOD_CLK_CTRL 0x200 + static struct clk_regmap s4_rtc_32k_by_oscin_clkin = { .data = &(struct clk_regmap_gate_data){ .offset = CLKCTRL_RTC_BY_OSCIN_CTRL0, @@ -3129,118 +3174,6 @@ static struct clk_regmap s4_gen_clk = { }, }; -static const struct clk_parent_data s4_adc_extclk_in_parent_data[] = { - { .fw_name = "xtal", }, - { .fw_name = "fclk_div4", }, - { .fw_name = "fclk_div3", }, - { .fw_name = "fclk_div5", }, - { .fw_name = "fclk_div7", }, - { .fw_name = "mpll2", }, - { .fw_name = "gp0_pll", }, - { .fw_name = "hifi_pll", }, -}; - -static struct clk_regmap s4_adc_extclk_in_mux = { - .data = &(struct clk_regmap_mux_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .mask = 0x7, - .shift = 25, - }, - .hw.init = &(struct clk_init_data){ - .name = "adc_extclk_in_mux", - .ops = &clk_regmap_mux_ops, - .parent_data = s4_adc_extclk_in_parent_data, - .num_parents = ARRAY_SIZE(s4_adc_extclk_in_parent_data), - .flags = 0, - }, -}; - -static struct clk_regmap s4_adc_extclk_in_div = { - .data = &(struct clk_regmap_div_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .shift = 16, - .width = 7, - }, - .hw.init = &(struct clk_init_data){ - .name = "adc_extclk_in_div", - .ops = &clk_regmap_divider_ops, - .parent_hws = (const struct clk_hw *[]) { - &s4_adc_extclk_in_mux.hw - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - }, -}; - -static struct clk_regmap s4_adc_extclk_in_gate = { - .data = &(struct clk_regmap_gate_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .bit_idx = 24, - }, - .hw.init = &(struct clk_init_data){ - .name = "adc_extclk_in", - .ops = &clk_regmap_gate_ops, - .parent_hws = (const struct clk_hw *[]) { - &s4_adc_extclk_in_div.hw - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - }, -}; - -static struct clk_regmap s4_demod_core_clk_mux = { - .data = &(struct clk_regmap_mux_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .mask = 0x3, - .shift = 9, - }, - .hw.init = &(struct clk_init_data){ - .name = "demod_core_clk_mux", - .ops = &clk_regmap_mux_ops, - .parent_data = (const struct clk_parent_data []) { - { .fw_name = "xtal", }, - { .fw_name = "fclk_div7", }, - { .fw_name = "fclk_div4", }, - { .hw = &s4_adc_extclk_in_gate.hw } - }, - .num_parents = 4, - .flags = CLK_SET_RATE_PARENT, - }, -}; - -static struct clk_regmap s4_demod_core_clk_div = { - .data = &(struct clk_regmap_div_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .shift = 0, - .width = 7, - }, - .hw.init = &(struct clk_init_data){ - .name = "demod_core_clk_div", - .ops = &clk_regmap_divider_ops, - .parent_hws = (const struct clk_hw *[]) { - &s4_demod_core_clk_mux.hw - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - }, -}; - -static struct clk_regmap s4_demod_core_clk_gate = { - .data = &(struct clk_regmap_gate_data) { - .offset = CLKCTRL_DEMOD_CLK_CTRL, - .bit_idx = 8, - }, - .hw.init = &(struct clk_init_data){ - .name = "demod_core_clk", - .ops = &clk_regmap_gate_ops, - .parent_hws = (const struct clk_hw *[]) { - &s4_demod_core_clk_div.hw - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - }, -}; - #define MESON_GATE(_name, _reg, _bit) \ MESON_PCLK(_name, _reg, _bit, &s4_sys_clk.hw) @@ -3522,231 +3455,6 @@ static struct clk_hw *s4_periphs_hw_clks[] = { [CLKID_HDCP22_SKPCLK] = &s4_hdcp22_skpclk_gate.hw, }; -/* Convenience table to populate regmap in .probe */ -static struct clk_regmap *const s4_periphs_clk_regmaps[] = { - &s4_rtc_32k_by_oscin_clkin, - &s4_rtc_32k_by_oscin_div, - &s4_rtc_32k_by_oscin_sel, - &s4_rtc_32k_by_oscin, - &s4_rtc_clk, - &s4_sysclk_b_sel, - &s4_sysclk_b_div, - &s4_sysclk_b, - &s4_sysclk_a_sel, - &s4_sysclk_a_div, - &s4_sysclk_a, - &s4_sys_clk, - &s4_ceca_32k_clkin, - &s4_ceca_32k_div, - &s4_ceca_32k_sel_pre, - &s4_ceca_32k_sel, - &s4_ceca_32k_clkout, - &s4_cecb_32k_clkin, - &s4_cecb_32k_div, - &s4_cecb_32k_sel_pre, - &s4_cecb_32k_sel, - &s4_cecb_32k_clkout, - &s4_sc_clk_mux, - &s4_sc_clk_div, - &s4_sc_clk_gate, - &s4_12_24M_clk_gate, - &s4_12_24M_clk, - &s4_vid_pll_div, - &s4_vid_pll_sel, - &s4_vid_pll, - &s4_vclk_sel, - &s4_vclk2_sel, - &s4_vclk_input, - &s4_vclk2_input, - &s4_vclk_div, - &s4_vclk2_div, - &s4_vclk, - &s4_vclk2, - &s4_vclk_div1, - &s4_vclk_div2_en, - &s4_vclk_div4_en, - &s4_vclk_div6_en, - &s4_vclk_div12_en, - &s4_vclk2_div1, - &s4_vclk2_div2_en, - &s4_vclk2_div4_en, - &s4_vclk2_div6_en, - &s4_vclk2_div12_en, - &s4_cts_enci_sel, - &s4_cts_encp_sel, - &s4_cts_vdac_sel, - &s4_hdmi_tx_sel, - &s4_cts_enci, - &s4_cts_encp, - &s4_cts_vdac, - &s4_hdmi_tx, - &s4_hdmi_sel, - &s4_hdmi_div, - &s4_hdmi, - &s4_ts_clk_div, - &s4_ts_clk_gate, - &s4_mali_0_sel, - &s4_mali_0_div, - &s4_mali_0, - &s4_mali_1_sel, - &s4_mali_1_div, - &s4_mali_1, - &s4_mali_mux, - &s4_vdec_p0_mux, - &s4_vdec_p0_div, - &s4_vdec_p0, - &s4_vdec_p1_mux, - &s4_vdec_p1_div, - &s4_vdec_p1, - &s4_vdec_mux, - &s4_hevcf_p0_mux, - &s4_hevcf_p0_div, - &s4_hevcf_p0, - &s4_hevcf_p1_mux, - &s4_hevcf_p1_div, - &s4_hevcf_p1, - &s4_hevcf_mux, - &s4_vpu_0_sel, - &s4_vpu_0_div, - &s4_vpu_0, - &s4_vpu_1_sel, - &s4_vpu_1_div, - &s4_vpu_1, - &s4_vpu, - &s4_vpu_clkb_tmp_mux, - &s4_vpu_clkb_tmp_div, - &s4_vpu_clkb_tmp, - &s4_vpu_clkb_div, - &s4_vpu_clkb, - &s4_vpu_clkc_p0_mux, - &s4_vpu_clkc_p0_div, - &s4_vpu_clkc_p0, - &s4_vpu_clkc_p1_mux, - &s4_vpu_clkc_p1_div, - &s4_vpu_clkc_p1, - &s4_vpu_clkc_mux, - &s4_vapb_0_sel, - &s4_vapb_0_div, - &s4_vapb_0, - &s4_vapb_1_sel, - &s4_vapb_1_div, - &s4_vapb_1, - &s4_vapb, - &s4_ge2d_gate, - &s4_hdcp22_esmclk_mux, - &s4_hdcp22_esmclk_div, - &s4_hdcp22_esmclk_gate, - &s4_hdcp22_skpclk_mux, - &s4_hdcp22_skpclk_div, - &s4_hdcp22_skpclk_gate, - &s4_vdin_meas_mux, - &s4_vdin_meas_div, - &s4_vdin_meas_gate, - &s4_sd_emmc_c_clk0_sel, - &s4_sd_emmc_c_clk0_div, - &s4_sd_emmc_c_clk0, - &s4_sd_emmc_a_clk0_sel, - &s4_sd_emmc_a_clk0_div, - &s4_sd_emmc_a_clk0, - &s4_sd_emmc_b_clk0_sel, - &s4_sd_emmc_b_clk0_div, - &s4_sd_emmc_b_clk0, - &s4_spicc0_mux, - &s4_spicc0_div, - &s4_spicc0_gate, - &s4_pwm_a_mux, - &s4_pwm_a_div, - &s4_pwm_a_gate, - &s4_pwm_b_mux, - &s4_pwm_b_div, - &s4_pwm_b_gate, - &s4_pwm_c_mux, - &s4_pwm_c_div, - &s4_pwm_c_gate, - &s4_pwm_d_mux, - &s4_pwm_d_div, - &s4_pwm_d_gate, - &s4_pwm_e_mux, - &s4_pwm_e_div, - &s4_pwm_e_gate, - &s4_pwm_f_mux, - &s4_pwm_f_div, - &s4_pwm_f_gate, - &s4_pwm_g_mux, - &s4_pwm_g_div, - &s4_pwm_g_gate, - &s4_pwm_h_mux, - &s4_pwm_h_div, - &s4_pwm_h_gate, - &s4_pwm_i_mux, - &s4_pwm_i_div, - &s4_pwm_i_gate, - &s4_pwm_j_mux, - &s4_pwm_j_div, - &s4_pwm_j_gate, - &s4_saradc_mux, - &s4_saradc_div, - &s4_saradc_gate, - &s4_gen_clk_sel, - &s4_gen_clk_div, - &s4_gen_clk, - &s4_ddr, - &s4_dos, - &s4_ethphy, - &s4_mali, - &s4_aocpu, - &s4_aucpu, - &s4_cec, - &s4_sdemmca, - &s4_sdemmcb, - &s4_nand, - &s4_smartcard, - &s4_acodec, - &s4_spifc, - &s4_msr_clk, - &s4_ir_ctrl, - &s4_audio, - &s4_eth, - &s4_uart_a, - &s4_uart_b, - &s4_uart_c, - &s4_uart_d, - &s4_uart_e, - &s4_aififo, - &s4_ts_ddr, - &s4_ts_pll, - &s4_g2d, - &s4_spicc0, - &s4_usb, - &s4_i2c_m_a, - &s4_i2c_m_b, - &s4_i2c_m_c, - &s4_i2c_m_d, - &s4_i2c_m_e, - &s4_hdmitx_apb, - &s4_i2c_s_a, - &s4_usb1_to_ddr, - &s4_hdcp22, - &s4_mmc_apb, - &s4_rsa, - &s4_cpu_debug, - &s4_vpu_intr, - &s4_demod, - &s4_sar_adc, - &s4_gic, - &s4_pwm_ab, - &s4_pwm_cd, - &s4_pwm_ef, - &s4_pwm_gh, - &s4_pwm_ij, - &s4_demod_core_clk_mux, - &s4_demod_core_clk_div, - &s4_demod_core_clk_gate, - &s4_adc_extclk_in_mux, - &s4_adc_extclk_in_div, - &s4_adc_extclk_in_gate, -}; - static const struct regmap_config clkc_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -3776,10 +3484,6 @@ static int meson_s4_periphs_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(regmap), "can't init regmap mmio region\n"); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(s4_periphs_clk_regmaps); i++) - s4_periphs_clk_regmaps[i]->map = regmap; - for (i = 0; i < s4_periphs_clks.num; i++) { /* array might be sparse */ if (!s4_periphs_clks.hws[i]) diff --git a/drivers/clk/meson/s4-peripherals.h b/drivers/clk/meson/s4-peripherals.h deleted file mode 100644 index 1e298713c2b2..000000000000 --- a/drivers/clk/meson/s4-peripherals.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ -/* - * Copyright (c) 2022-2023 Amlogic, inc. All rights reserved - * Author: Yu Tu - */ - -#ifndef __MESON_S4_PERIPHERALS_H__ -#define __MESON_S4_PERIPHERALS_H__ - -#define CLKCTRL_RTC_BY_OSCIN_CTRL0 0x008 -#define CLKCTRL_RTC_BY_OSCIN_CTRL1 0x00c -#define CLKCTRL_RTC_CTRL 0x010 -#define CLKCTRL_SYS_CLK_CTRL0 0x040 -#define CLKCTRL_SYS_CLK_EN0_REG0 0x044 -#define CLKCTRL_SYS_CLK_EN0_REG1 0x048 -#define CLKCTRL_SYS_CLK_EN0_REG2 0x04c -#define CLKCTRL_SYS_CLK_EN0_REG3 0x050 -#define CLKCTRL_CECA_CTRL0 0x088 -#define CLKCTRL_CECA_CTRL1 0x08c -#define CLKCTRL_CECB_CTRL0 0x090 -#define CLKCTRL_CECB_CTRL1 0x094 -#define CLKCTRL_SC_CLK_CTRL 0x098 -#define CLKCTRL_CLK12_24_CTRL 0x0a8 -#define CLKCTRL_VID_CLK_CTRL 0x0c0 -#define CLKCTRL_VID_CLK_CTRL2 0x0c4 -#define CLKCTRL_VID_CLK_DIV 0x0c8 -#define CLKCTRL_VIID_CLK_DIV 0x0cc -#define CLKCTRL_VIID_CLK_CTRL 0x0d0 -#define CLKCTRL_HDMI_CLK_CTRL 0x0e0 -#define CLKCTRL_VID_PLL_CLK_DIV 0x0e4 -#define CLKCTRL_VPU_CLK_CTRL 0x0e8 -#define CLKCTRL_VPU_CLKB_CTRL 0x0ec -#define CLKCTRL_VPU_CLKC_CTRL 0x0f0 -#define CLKCTRL_VID_LOCK_CLK_CTRL 0x0f4 -#define CLKCTRL_VDIN_MEAS_CLK_CTRL 0x0f8 -#define CLKCTRL_VAPBCLK_CTRL 0x0fc -#define CLKCTRL_HDCP22_CTRL 0x100 -#define CLKCTRL_VDEC_CLK_CTRL 0x140 -#define CLKCTRL_VDEC2_CLK_CTRL 0x144 -#define CLKCTRL_VDEC3_CLK_CTRL 0x148 -#define CLKCTRL_VDEC4_CLK_CTRL 0x14c -#define CLKCTRL_TS_CLK_CTRL 0x158 -#define CLKCTRL_MALI_CLK_CTRL 0x15c -#define CLKCTRL_NAND_CLK_CTRL 0x168 -#define CLKCTRL_SD_EMMC_CLK_CTRL 0x16c -#define CLKCTRL_SPICC_CLK_CTRL 0x174 -#define CLKCTRL_GEN_CLK_CTRL 0x178 -#define CLKCTRL_SAR_CLK_CTRL 0x17c -#define CLKCTRL_PWM_CLK_AB_CTRL 0x180 -#define CLKCTRL_PWM_CLK_CD_CTRL 0x184 -#define CLKCTRL_PWM_CLK_EF_CTRL 0x188 -#define CLKCTRL_PWM_CLK_GH_CTRL 0x18c -#define CLKCTRL_PWM_CLK_IJ_CTRL 0x190 -#define CLKCTRL_DEMOD_CLK_CTRL 0x200 - -#endif /* __MESON_S4_PERIPHERALS_H__ */ diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c index f9cc05a506e3..3d689d2f003e 100644 --- a/drivers/clk/meson/s4-pll.c +++ b/drivers/clk/meson/s4-pll.c @@ -13,10 +13,37 @@ #include "clk-mpll.h" #include "clk-pll.h" #include "clk-regmap.h" -#include "s4-pll.h" #include "meson-clkc-utils.h" #include +#define ANACTRL_FIXPLL_CTRL0 0x040 +#define ANACTRL_FIXPLL_CTRL1 0x044 +#define ANACTRL_FIXPLL_CTRL3 0x04c +#define ANACTRL_GP0PLL_CTRL0 0x080 +#define ANACTRL_GP0PLL_CTRL1 0x084 +#define ANACTRL_GP0PLL_CTRL2 0x088 +#define ANACTRL_GP0PLL_CTRL3 0x08c +#define ANACTRL_GP0PLL_CTRL4 0x090 +#define ANACTRL_GP0PLL_CTRL5 0x094 +#define ANACTRL_GP0PLL_CTRL6 0x098 +#define ANACTRL_HIFIPLL_CTRL0 0x100 +#define ANACTRL_HIFIPLL_CTRL1 0x104 +#define ANACTRL_HIFIPLL_CTRL2 0x108 +#define ANACTRL_HIFIPLL_CTRL3 0x10c +#define ANACTRL_HIFIPLL_CTRL4 0x110 +#define ANACTRL_HIFIPLL_CTRL5 0x114 +#define ANACTRL_HIFIPLL_CTRL6 0x118 +#define ANACTRL_MPLL_CTRL0 0x180 +#define ANACTRL_MPLL_CTRL1 0x184 +#define ANACTRL_MPLL_CTRL2 0x188 +#define ANACTRL_MPLL_CTRL3 0x18c +#define ANACTRL_MPLL_CTRL4 0x190 +#define ANACTRL_MPLL_CTRL5 0x194 +#define ANACTRL_MPLL_CTRL6 0x198 +#define ANACTRL_MPLL_CTRL7 0x19c +#define ANACTRL_MPLL_CTRL8 0x1a0 +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 + /* * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode. * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable @@ -767,33 +794,6 @@ static struct clk_hw *s4_pll_hw_clks[] = { [CLKID_MPLL3] = &s4_mpll3.hw, }; -static struct clk_regmap *const s4_pll_clk_regmaps[] = { - &s4_fixed_pll_dco, - &s4_fixed_pll, - &s4_fclk_div2, - &s4_fclk_div3, - &s4_fclk_div4, - &s4_fclk_div5, - &s4_fclk_div7, - &s4_fclk_div2p5, - &s4_gp0_pll_dco, - &s4_gp0_pll, - &s4_hifi_pll_dco, - &s4_hifi_pll, - &s4_hdmi_pll_dco, - &s4_hdmi_pll_od, - &s4_hdmi_pll, - &s4_mpll_50m, - &s4_mpll0_div, - &s4_mpll0, - &s4_mpll1_div, - &s4_mpll1, - &s4_mpll2_div, - &s4_mpll2, - &s4_mpll3_div, - &s4_mpll3, -}; - static const struct reg_sequence s4_init_regs[] = { { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, }; @@ -832,10 +832,6 @@ static int meson_s4_pll_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to init registers\n"); - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) - s4_pll_clk_regmaps[i]->map = regmap; - /* Register clocks */ for (i = 0; i < s4_pll_clks.num; i++) { /* array might be sparse */ diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h deleted file mode 100644 index ff7d58302f2a..000000000000 --- a/drivers/clk/meson/s4-pll.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ -/* - * Copyright (c) 2022-2023 Amlogic, inc. All rights reserved - * Author: Yu Tu - */ - -#ifndef __MESON_S4_PLL_H__ -#define __MESON_S4_PLL_H__ - -#define ANACTRL_FIXPLL_CTRL0 0x040 -#define ANACTRL_FIXPLL_CTRL1 0x044 -#define ANACTRL_FIXPLL_CTRL3 0x04c -#define ANACTRL_GP0PLL_CTRL0 0x080 -#define ANACTRL_GP0PLL_CTRL1 0x084 -#define ANACTRL_GP0PLL_CTRL2 0x088 -#define ANACTRL_GP0PLL_CTRL3 0x08c -#define ANACTRL_GP0PLL_CTRL4 0x090 -#define ANACTRL_GP0PLL_CTRL5 0x094 -#define ANACTRL_GP0PLL_CTRL6 0x098 -#define ANACTRL_HIFIPLL_CTRL0 0x100 -#define ANACTRL_HIFIPLL_CTRL1 0x104 -#define ANACTRL_HIFIPLL_CTRL2 0x108 -#define ANACTRL_HIFIPLL_CTRL3 0x10c -#define ANACTRL_HIFIPLL_CTRL4 0x110 -#define ANACTRL_HIFIPLL_CTRL5 0x114 -#define ANACTRL_HIFIPLL_CTRL6 0x118 -#define ANACTRL_MPLL_CTRL0 0x180 -#define ANACTRL_MPLL_CTRL1 0x184 -#define ANACTRL_MPLL_CTRL2 0x188 -#define ANACTRL_MPLL_CTRL3 0x18c -#define ANACTRL_MPLL_CTRL4 0x190 -#define ANACTRL_MPLL_CTRL5 0x194 -#define ANACTRL_MPLL_CTRL6 0x198 -#define ANACTRL_MPLL_CTRL7 0x19c -#define ANACTRL_MPLL_CTRL8 0x1a0 -#define ANACTRL_HDMIPLL_CTRL0 0x1c0 - -#endif /* __MESON_S4_PLL_H__ */ diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c index 9c4945234f26..4ba3d82810e8 100644 --- a/drivers/clk/meson/sclk-div.c +++ b/drivers/clk/meson/sclk-div.c @@ -222,6 +222,11 @@ static int sclk_div_init(struct clk_hw *hw) struct clk_regmap *clk = to_clk_regmap(hw); struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); unsigned int val; + int ret; + + ret = clk_regmap_init(hw); + if (ret) + return ret; val = meson_parm_read(clk->map, &sclk->div); diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c index 6a167ebdc8d7..009bd1193042 100644 --- a/drivers/clk/meson/vclk.c +++ b/drivers/clk/meson/vclk.c @@ -45,6 +45,7 @@ static int meson_vclk_gate_is_enabled(struct clk_hw *hw) } const struct clk_ops meson_vclk_gate_ops = { + .init = clk_regmap_init, .enable = meson_vclk_gate_enable, .disable = meson_vclk_gate_disable, .is_enabled = meson_vclk_gate_is_enabled, @@ -127,6 +128,7 @@ static int meson_vclk_div_is_enabled(struct clk_hw *hw) } const struct clk_ops meson_vclk_div_ops = { + .init = clk_regmap_init, .recalc_rate = meson_vclk_div_recalc_rate, .determine_rate = meson_vclk_div_determine_rate, .set_rate = meson_vclk_div_set_rate, diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c index 965ed7281f57..2a3cdbe6d86a 100644 --- a/drivers/clk/meson/vid-pll-div.c +++ b/drivers/clk/meson/vid-pll-div.c @@ -90,6 +90,7 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw, } const struct clk_ops meson_vid_pll_div_ro_ops = { + .init = clk_regmap_init, .recalc_rate = meson_vid_pll_div_recalc_rate, }; EXPORT_SYMBOL_NS_GPL(meson_vid_pll_div_ro_ops, "CLK_MESON"); diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-core.c index 1b4f023cdc8b..6fbc6dc50ca3 100644 --- a/drivers/clk/microchip/clk-core.c +++ b/drivers/clk/microchip/clk-core.c @@ -326,7 +326,7 @@ static void roclk_calc_div_trim(unsigned long rate, * i.e. fout = fin / 2 * DIV * whereas DIV = rodiv + (rotrim / 512) * - * Since kernel does not perform floating-point arithmatic so + * Since kernel does not perform floating-point arithmetic so * (rotrim/512) will be zero. And DIV & rodiv will result same. * * ie. fout = (fin * 256) / [(512 * rodiv) + rotrim] ... from (1) diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c index 350eeb3e9e25..6855815ee8be 100644 --- a/drivers/clk/mmp/clk-gate.c +++ b/drivers/clk/mmp/clk-gate.c @@ -15,7 +15,7 @@ #include "clk.h" /* - * Some clocks will have mutiple bits to enable the clocks, and + * Some clocks will have multiple bits to enable the clocks, and * the bits to disable the clock is not same as enabling bits. */ diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index 45665655a258..8d31a595a27c 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -7,7 +7,6 @@ * Gregory CLEMENT * Sebastian Hesselbarth * Andrew Lunn - * */ #include @@ -19,8 +18,8 @@ /* * Core Clocks * - * Armada XP Sample At Reset is a 64 bit bitfiled split in two - * register of 32 bits + * Armada XP Sample At Reset is a 64 bit bitfield split in two + * registers of 32 bits */ #define SARL 0 /* Low part [0:31] */ diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c index 928e8b1c46a1..0a78ef380646 100644 --- a/drivers/clk/mxs/clk-div.c +++ b/drivers/clk/mxs/clk-div.c @@ -16,7 +16,7 @@ * @busy: busy bit shift * * The mxs divider clock is a subclass of basic clk_divider with an - * addtional busy bit. + * additional busy bit. */ struct clk_div { struct clk_divider divider; diff --git a/drivers/clk/nuvoton/Kconfig b/drivers/clk/nuvoton/Kconfig index fe4b7f62f467..e7019b69ea74 100644 --- a/drivers/clk/nuvoton/Kconfig +++ b/drivers/clk/nuvoton/Kconfig @@ -4,7 +4,7 @@ config COMMON_CLK_NUVOTON bool "Nuvoton clock controller common support" depends on ARCH_MA35 || COMPILE_TEST - default y + default ARCH_MA35 help Say y here to enable common clock controller for Nuvoton platforms. @@ -12,7 +12,7 @@ if COMMON_CLK_NUVOTON config CLK_MA35D1 bool "Nuvoton MA35D1 clock controller support" - default y + default ARCH_MA35 help Build the clock controller driver for MA35D1 SoC. diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c index ddb28b38f549..751b786d73f8 100644 --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -148,7 +148,7 @@ static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable) val |= LPC18XX_CCU_RUN; } else { /* - * To safely disable a branch clock a squence of two separate + * To safely disable a branch clock a sequence of two separate * writes must be used. First write should set the AUTO bit * and the next write should clear the RUN bit. */ diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 7d5dac26b244..6cb6cd3e1778 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -493,6 +493,25 @@ config QCM_DISPCC_2290 Say Y if you want to support display devices and functionality such as splash screen. +config QCS_DISPCC_615 + tristate "QCS615 Display Clock Controller" + select QCM_GCC_615 + help + Support for the display clock controller on Qualcomm Technologies, Inc + QCS615 devices. + Say Y if you want to support display devices and functionality such as + splash screen. + +config QCS_CAMCC_615 + tristate "QCS615 Camera Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCS_GCC_615 + help + Support for the camera clock controller on Qualcomm Technologies, Inc + QCS615 devices. + Say Y if you want to support camera devices and functionality such as + capturing pictures. + config QCS_GCC_404 tristate "QCS404 Global Clock Controller" help @@ -529,6 +548,22 @@ config QCS_GCC_615 Say Y if you want to use multimedia devices or peripheral devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc. +config QCS_GPUCC_615 + tristate "QCS615 Graphics clock controller" + select QCS_GCC_615 + help + Support for the graphics clock controller on QCS615 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + +config QCS_VIDEOCC_615 + tristate "QCS615 Video Clock Controller" + select QCS_GCC_615 + help + Support for the video clock controller on QCS615 devices. + Say Y if you want to support video devices and functionality such as + video encode and decode. + config SC_CAMCC_7180 tristate "SC7180 Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -549,6 +584,16 @@ config SC_CAMCC_7280 Say Y if you want to support camera devices and functionality such as capturing pictures. +config SC_CAMCC_8180X + tristate "SC8180X Camera Clock Controller" + depends on ARM64 || COMPILE_TEST + select SC_GCC_8180X + help + Support for the camera clock controller on Qualcomm Technologies, Inc + SC8180X devices. + Say Y if you want to support camera devices and functionality such as + capturing pictures. + config SC_CAMCC_8280XP tristate "SC8280XP Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -924,6 +969,14 @@ config SM_CAMCC_7150 Support for the camera clock controller on SM7150 devices. Say Y if you want to support camera devices and camera functionality. +config SM_CAMCC_MILOS + tristate "Milos Camera Clock Controller" + depends on ARM64 || COMPILE_TEST + select SM_GCC_MILOS + help + Support for the camera clock controller on Milos devices. + Say Y if you want to support camera devices and camera functionality. + config SM_CAMCC_8150 tristate "SM8150 Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1036,6 +1089,16 @@ config SM_DISPCC_6375 Say Y if you want to support display devices and functionality such as splash screen. +config SM_DISPCC_MILOS + tristate "Milos Display Clock Controller" + depends on ARM64 || COMPILE_TEST + depends on SM_GCC_MILOS + help + Support for the display clock controller on Qualcomm Technologies, Inc + Milos devices. + Say Y if you want to support display devices and functionality such as + splash screen. + config SM_DISPCC_8450 tristate "SM8450 Display Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1121,6 +1184,15 @@ config SM_GCC_7150 Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, SD/UFS, PCIe etc. +config SM_GCC_MILOS + tristate "Milos Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the global clock controller on Milos devices. + Say Y if you want to use peripheral devices such as UART, + SPI, I2C, USB, SD/UFS, PCIe etc. + config SM_GCC_8150 tristate "SM8150 Global Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1230,6 +1302,15 @@ config SM_GPUCC_6350 Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config SM_GPUCC_MILOS + tristate "Milos Graphics Clock Controller" + depends on ARM64 || COMPILE_TEST + select SM_GCC_MILOS + help + Support for the graphics clock controller on Milos devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config SM_GPUCC_8150 tristate "SM8150 Graphics Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1329,6 +1410,15 @@ config SA_VIDEOCC_8775P Say Y if you want to support video devices and functionality such as video encode/decode. +config SM_VIDEOCC_6350 + tristate "SM6350 Video Clock Controller" + select SM_GCC_6350 + select QCOM_GDSC + help + Support for the video clock controller on SM6350 devices. + Say Y if you want to support video devices and functionality such as + video encode and decode. + config SM_VIDEOCC_7150 tristate "SM7150 Video Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1339,6 +1429,17 @@ config SM_VIDEOCC_7150 Say Y if you want to support video devices and functionality such as video encode and decode. +config SM_VIDEOCC_MILOS + tristate "Milos Video Clock Controller" + depends on ARM64 || COMPILE_TEST + select SM_GCC_MILOS + select QCOM_GDSC + help + Support for the video clock controller on Qualcomm Technologies, Inc. + Milos devices. + Say Y if you want to support video devices and functionality such as + video encode/decode. + config SM_VIDEOCC_8150 tristate "SM8150 Video Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1372,11 +1473,10 @@ config SM_VIDEOCC_8350 config SM_VIDEOCC_8550 tristate "SM8550 Video Clock Controller" depends on ARM64 || COMPILE_TEST - depends on SM_GCC_8550 || SM_GCC_8650 select QCOM_GDSC help Support for the video clock controller on Qualcomm Technologies, Inc. - SM8550 or SM8650 devices. + SM8550 or SM8650 or X1E80100 devices. Say Y if you want to support video devices and functionality such as video encode/decode. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 96862e99e5d4..ddb7e06fae40 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -73,15 +73,20 @@ obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o obj-$(CONFIG_QCM_GCC_2290) += gcc-qcm2290.o obj-$(CONFIG_QCM_DISPCC_2290) += dispcc-qcm2290.o +obj-$(CONFIG_QCS_DISPCC_615) += dispcc-qcs615.o +obj-$(CONFIG_QCS_CAMCC_615) += camcc-qcs615.o obj-$(CONFIG_QCS_GCC_404) += gcc-qcs404.o obj-$(CONFIG_QCS_GCC_615) += gcc-qcs615.o obj-$(CONFIG_QCS_GCC_8300) += gcc-qcs8300.o +obj-$(CONFIG_QCS_GPUCC_615) += gpucc-qcs615.o +obj-$(CONFIG_QCS_VIDEOCC_615) += videocc-qcs615.o obj-$(CONFIG_QCS_Q6SSTOP_404) += q6sstop-qcs404.o obj-$(CONFIG_QCS_TURING_404) += turingcc-qcs404.o obj-$(CONFIG_QDU_ECPRICC_1000) += ecpricc-qdu1000.o obj-$(CONFIG_QDU_GCC_1000) += gcc-qdu1000.o obj-$(CONFIG_SC_CAMCC_7180) += camcc-sc7180.o obj-$(CONFIG_SC_CAMCC_7280) += camcc-sc7280.o +obj-$(CONFIG_SC_CAMCC_8180X) += camcc-sc8180x.o obj-$(CONFIG_SC_CAMCC_8280XP) += camcc-sc8280xp.o obj-$(CONFIG_SC_DISPCC_7180) += dispcc-sc7180.o obj-$(CONFIG_SC_DISPCC_7280) += dispcc-sc7280.o @@ -126,6 +131,7 @@ obj-$(CONFIG_SM_CAMCC_8250) += camcc-sm8250.o obj-$(CONFIG_SM_CAMCC_8450) += camcc-sm8450.o obj-$(CONFIG_SM_CAMCC_8550) += camcc-sm8550.o obj-$(CONFIG_SM_CAMCC_8650) += camcc-sm8650.o +obj-$(CONFIG_SM_CAMCC_MILOS) += camcc-milos.o obj-$(CONFIG_SM_DISPCC_4450) += dispcc-sm4450.o obj-$(CONFIG_SM_DISPCC_6115) += dispcc-sm6115.o obj-$(CONFIG_SM_DISPCC_6125) += dispcc-sm6125.o @@ -136,6 +142,7 @@ obj-$(CONFIG_SM_DISPCC_8250) += dispcc-sm8250.o obj-$(CONFIG_SM_DISPCC_8450) += dispcc-sm8450.o obj-$(CONFIG_SM_DISPCC_8550) += dispcc-sm8550.o obj-$(CONFIG_SM_DISPCC_8750) += dispcc-sm8750.o +obj-$(CONFIG_SM_DISPCC_MILOS) += dispcc-milos.o obj-$(CONFIG_SM_GCC_4450) += gcc-sm4450.o obj-$(CONFIG_SM_GCC_6115) += gcc-sm6115.o obj-$(CONFIG_SM_GCC_6125) += gcc-sm6125.o @@ -149,6 +156,7 @@ obj-$(CONFIG_SM_GCC_8450) += gcc-sm8450.o obj-$(CONFIG_SM_GCC_8550) += gcc-sm8550.o obj-$(CONFIG_SM_GCC_8650) += gcc-sm8650.o obj-$(CONFIG_SM_GCC_8750) += gcc-sm8750.o +obj-$(CONFIG_SM_GCC_MILOS) += gcc-milos.o obj-$(CONFIG_SM_GPUCC_4450) += gpucc-sm4450.o obj-$(CONFIG_SM_GPUCC_6115) += gpucc-sm6115.o obj-$(CONFIG_SM_GPUCC_6125) += gpucc-sm6125.o @@ -160,16 +168,19 @@ 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_MILOS) += gpucc-milos.o obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o obj-$(CONFIG_SM_TCSRCC_8650) += tcsrcc-sm8650.o obj-$(CONFIG_SM_TCSRCC_8750) += tcsrcc-sm8750.o +obj-$(CONFIG_SM_VIDEOCC_6350) += videocc-sm6350.o obj-$(CONFIG_SM_VIDEOCC_7150) += videocc-sm7150.o obj-$(CONFIG_SM_VIDEOCC_8150) += videocc-sm8150.o obj-$(CONFIG_SM_VIDEOCC_8250) += videocc-sm8250.o obj-$(CONFIG_SM_VIDEOCC_8350) += videocc-sm8350.o obj-$(CONFIG_SM_VIDEOCC_8450) += videocc-sm8450.o obj-$(CONFIG_SM_VIDEOCC_8550) += videocc-sm8550.o +obj-$(CONFIG_SM_VIDEOCC_MILOS) += videocc-milos.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o diff --git a/drivers/clk/qcom/apcs-sdx55.c b/drivers/clk/qcom/apcs-sdx55.c index 3ba01622d8f0..90dd1f1855c2 100644 --- a/drivers/clk/qcom/apcs-sdx55.c +++ b/drivers/clk/qcom/apcs-sdx55.c @@ -111,7 +111,7 @@ static int qcom_apcs_sdx55_clk_probe(struct platform_device *pdev) * driver, there seems to be no better place to do this. So do it here! */ cpu_dev = get_cpu_device(0); - ret = dev_pm_domain_attach(cpu_dev, true); + ret = dev_pm_domain_attach(cpu_dev, PD_FLAG_ATTACH_POWER_ON); if (ret) { dev_err_probe(dev, ret, "can't get PM domain: %d\n", ret); goto err; diff --git a/drivers/clk/qcom/camcc-milos.c b/drivers/clk/qcom/camcc-milos.c new file mode 100644 index 000000000000..75bd939f7dd1 --- /dev/null +++ b/drivers/clk/qcom/camcc-milos.c @@ -0,0 +1,2161 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss + */ + +#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" + +/* Need to match the order of clocks in DT binding */ +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_IFACE, +}; + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL0_OUT_ODD, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL1_OUT_MAIN, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_EVEN, + P_CAM_CC_PLL4_OUT_EVEN, + P_CAM_CC_PLL4_OUT_MAIN, + P_CAM_CC_PLL5_OUT_EVEN, + P_CAM_CC_PLL5_OUT_MAIN, + P_CAM_CC_PLL6_OUT_EVEN, + P_CAM_CC_PLL6_OUT_MAIN, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +static const struct pll_vco rivian_ole_vco[] = { + { 777000000, 1285000000, 0 }, +}; + +/* 1200.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x3e, + .alpha = 0x8000, + .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 = 0x00008400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .config = &cam_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 = "cam_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = { + { 0x2, 3 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = { + .offset = 0x0, + .post_div_shift = 14, + .post_div_table = post_div_table_cam_cc_pll0_out_odd, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_odd", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 600.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x1f, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .config = &cam_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 = "cam_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 clk_div_table post_div_table_cam_cc_pll1_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll1_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll1.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 960.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x10000030, + .config_ctl_hi_val = 0x80890263, + .config_ctl_hi1_val = 0x00000217, + .user_ctl_val = 0x00000001, + .user_ctl_hi_val = 0x00100000, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .config = &cam_cc_pll2_config, + .vco_table = rivian_ole_vco, + .num_vco = ARRAY_SIZE(rivian_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_rivian_evo_ops, + }, + }, +}; + +/* 600.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x1f, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .config = &cam_cc_pll3_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 = "cam_cc_pll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll3_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = { + .offset = 0x3000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll3_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll3_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll3.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 700.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll4_config = { + .l = 0x24, + .alpha = 0x7555, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll4 = { + .offset = 0x4000, + .config = &cam_cc_pll4_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 = "cam_cc_pll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll4_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll4_out_even = { + .offset = 0x4000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll4_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll4_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll4_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll4.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 700.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll5_config = { + .l = 0x24, + .alpha = 0x7555, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll5 = { + .offset = 0x5000, + .config = &cam_cc_pll5_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 = "cam_cc_pll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll5_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll5_out_even = { + .offset = 0x5000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll5_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll5_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll5_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll5.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 700.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll6_config = { + .l = 0x24, + .alpha = 0x7555, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll6 = { + .offset = 0x6000, + .config = &cam_cc_pll6_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 = "cam_cc_pll6", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll6_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll6_out_even = { + .offset = 0x6000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll6_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll6_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll6.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_MAIN, 4 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL1_OUT_MAIN, 2 }, + { P_CAM_CC_PLL1_OUT_EVEN, 3 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll1.clkr.hw }, + { .hw = &cam_cc_pll1_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL3_OUT_EVEN, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll3_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_6[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_6_ao[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map cam_cc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL4_OUT_EVEN, 2 }, + { P_CAM_CC_PLL4_OUT_MAIN, 3 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_7[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll4_out_even.clkr.hw }, + { .hw = &cam_cc_pll4.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL5_OUT_EVEN, 2 }, + { P_CAM_CC_PLL5_OUT_MAIN, 3 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_8[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll5_out_even.clkr.hw }, + { .hw = &cam_cc_pll5.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_9[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL6_OUT_EVEN, 2 }, + { P_CAM_CC_PLL6_OUT_MAIN, 3 }, + { P_CAM_CC_PLL0_OUT_ODD, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_9[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll6_out_even.clkr.hw }, + { .hw = &cam_cc_pll6.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_10[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_10[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(300000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(410000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(460000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(700000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x1a004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_camnoc_axi_clk_src[] = { + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(240000000, P_CAM_CC_PLL0_OUT_EVEN, 2.5, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_camnoc_axi_clk_src = { + .cmd_rcgr = 0x2401c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_camnoc_axi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0x21004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0x22004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x1c05c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cre_clk_src[] = { + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cre_clk_src = { + .cmd_rcgr = 0x27004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cre_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cre_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x19004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x19028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x1904c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x19070, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 6, 0, 0), + F(240000000, P_CAM_CC_PLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x1a030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0x20014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_CAM_CC_PLL2_OUT_MAIN, 1, 1, 50), + F(24000000, P_CAM_CC_PLL2_OUT_MAIN, 10, 1, 4), + F(64000000, P_CAM_CC_PLL2_OUT_MAIN, 15, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x18004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x18024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x18044, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x18064, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x18084, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ope_0_clk_src[] = { + F(300000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(410000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(520000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(645000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(700000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ope_0_clk_src = { + .cmd_rcgr = 0x1b004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_ope_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ope_0_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sleep_clk_src = { + .cmd_rcgr = 0x25044, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_6, + .freq_tbl = ftbl_cam_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sleep_clk_src", + .parent_data = cam_cc_parent_data_6_ao, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_6_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x1a04c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_tfe_0_clk_src[] = { + F(350000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(570000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(725000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_tfe_0_clk_src = { + .cmd_rcgr = 0x1c004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_7, + .freq_tbl = ftbl_cam_cc_tfe_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_clk_src", + .parent_data = cam_cc_parent_data_7, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_tfe_0_csid_clk_src = { + .cmd_rcgr = 0x1c030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_tfe_1_clk_src[] = { + F(350000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(570000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(725000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_tfe_1_clk_src = { + .cmd_rcgr = 0x1d004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_8, + .freq_tbl = ftbl_cam_cc_tfe_1_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_clk_src", + .parent_data = cam_cc_parent_data_8, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_tfe_1_csid_clk_src = { + .cmd_rcgr = 0x1d030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_tfe_2_clk_src[] = { + F(350000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(570000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(725000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_tfe_2_clk_src = { + .cmd_rcgr = 0x1e004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_9, + .freq_tbl = ftbl_cam_cc_tfe_2_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_clk_src", + .parent_data = cam_cc_parent_data_9, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_9), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_tfe_2_csid_clk_src = { + .cmd_rcgr = 0x1e030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_xo_clk_src = { + .cmd_rcgr = 0x25020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_10, + .freq_tbl = ftbl_cam_cc_xo_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_xo_clk_src", + .parent_data = cam_cc_parent_data_10, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_10), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x1a064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_areg_clk = { + .halt_reg = 0x1a048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x1a01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_atb_clk = { + .halt_reg = 0x24040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_hf_clk = { + .halt_reg = 0x24010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_hf_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_sf_clk = { + .halt_reg = 0x24004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_sf_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_nrt_axi_clk = { + .halt_reg = 0x2404c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2404c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2404c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_nrt_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_rt_axi_clk = { + .halt_reg = 0x24034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_rt_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0x2101c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2101c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0x2201c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2201c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0x2501c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x23004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cre_ahb_clk = { + .halt_reg = 0x27020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x27020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cre_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cre_clk = { + .halt_reg = 0x2701c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2701c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cre_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cre_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x1901c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1901c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x19040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x19064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x19088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi3phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x19020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x19044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x19068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x1908c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1908c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_atb_clk = { + .halt_reg = 0x20004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0x2002c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2002c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_cti_clk = { + .halt_reg = 0x20008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_cti_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ts_clk = { + .halt_reg = 0x2000c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2000c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_ts_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x1801c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1801c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x1803c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1803c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x1805c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1805c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x1807c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1807c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x1809c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1809c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ope_0_ahb_clk = { + .halt_reg = 0x1b034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1b034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ope_0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ope_0_areg_clk = { + .halt_reg = 0x1b030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1b030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ope_0_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ope_0_clk = { + .halt_reg = 0x1b01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1b01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ope_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ope_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_soc_ahb_clk = { + .halt_reg = 0x25018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_soc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sys_tmr_clk = { + .halt_reg = 0x20038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sys_tmr_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_0_ahb_clk = { + .halt_reg = 0x1c078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_0_clk = { + .halt_reg = 0x1c01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_0_cphy_rx_clk = { + .halt_reg = 0x1c074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_0_csid_clk = { + .halt_reg = 0x1c048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_0_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_0_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_1_ahb_clk = { + .halt_reg = 0x1d058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1d058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_1_clk = { + .halt_reg = 0x1d01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1d01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_1_cphy_rx_clk = { + .halt_reg = 0x1d054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1d054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_1_csid_clk = { + .halt_reg = 0x1d048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1d048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_1_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_1_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_2_ahb_clk = { + .halt_reg = 0x1e058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1e058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_2_clk = { + .halt_reg = 0x1e01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1e01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_2_cphy_rx_clk = { + .halt_reg = 0x1e054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1e054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_tfe_2_csid_clk = { + .halt_reg = 0x1e048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1e048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_tfe_2_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_tfe_2_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_top_shift_clk = { + .halt_reg = 0x25040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x25040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_top_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc cam_cc_camss_top_gdsc = { + .gdscr = 0x25004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_camss_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *cam_cc_milos_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_CAMNOC_ATB_CLK] = &cam_cc_camnoc_atb_clk.clkr, + [CAM_CC_CAMNOC_AXI_CLK_SRC] = &cam_cc_camnoc_axi_clk_src.clkr, + [CAM_CC_CAMNOC_AXI_HF_CLK] = &cam_cc_camnoc_axi_hf_clk.clkr, + [CAM_CC_CAMNOC_AXI_SF_CLK] = &cam_cc_camnoc_axi_sf_clk.clkr, + [CAM_CC_CAMNOC_NRT_AXI_CLK] = &cam_cc_camnoc_nrt_axi_clk.clkr, + [CAM_CC_CAMNOC_RT_AXI_CLK] = &cam_cc_camnoc_rt_axi_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CRE_AHB_CLK] = &cam_cc_cre_ahb_clk.clkr, + [CAM_CC_CRE_CLK] = &cam_cc_cre_clk.clkr, + [CAM_CC_CRE_CLK_SRC] = &cam_cc_cre_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_ICP_ATB_CLK] = &cam_cc_icp_atb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_ICP_CTI_CLK] = &cam_cc_icp_cti_clk.clkr, + [CAM_CC_ICP_TS_CLK] = &cam_cc_icp_ts_clk.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_OPE_0_AHB_CLK] = &cam_cc_ope_0_ahb_clk.clkr, + [CAM_CC_OPE_0_AREG_CLK] = &cam_cc_ope_0_areg_clk.clkr, + [CAM_CC_OPE_0_CLK] = &cam_cc_ope_0_clk.clkr, + [CAM_CC_OPE_0_CLK_SRC] = &cam_cc_ope_0_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr, + [CAM_CC_PLL4] = &cam_cc_pll4.clkr, + [CAM_CC_PLL4_OUT_EVEN] = &cam_cc_pll4_out_even.clkr, + [CAM_CC_PLL5] = &cam_cc_pll5.clkr, + [CAM_CC_PLL5_OUT_EVEN] = &cam_cc_pll5_out_even.clkr, + [CAM_CC_PLL6] = &cam_cc_pll6.clkr, + [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, + [CAM_CC_SLEEP_CLK_SRC] = &cam_cc_sleep_clk_src.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_SOC_AHB_CLK] = &cam_cc_soc_ahb_clk.clkr, + [CAM_CC_SYS_TMR_CLK] = &cam_cc_sys_tmr_clk.clkr, + [CAM_CC_TFE_0_AHB_CLK] = &cam_cc_tfe_0_ahb_clk.clkr, + [CAM_CC_TFE_0_CLK] = &cam_cc_tfe_0_clk.clkr, + [CAM_CC_TFE_0_CLK_SRC] = &cam_cc_tfe_0_clk_src.clkr, + [CAM_CC_TFE_0_CPHY_RX_CLK] = &cam_cc_tfe_0_cphy_rx_clk.clkr, + [CAM_CC_TFE_0_CSID_CLK] = &cam_cc_tfe_0_csid_clk.clkr, + [CAM_CC_TFE_0_CSID_CLK_SRC] = &cam_cc_tfe_0_csid_clk_src.clkr, + [CAM_CC_TFE_1_AHB_CLK] = &cam_cc_tfe_1_ahb_clk.clkr, + [CAM_CC_TFE_1_CLK] = &cam_cc_tfe_1_clk.clkr, + [CAM_CC_TFE_1_CLK_SRC] = &cam_cc_tfe_1_clk_src.clkr, + [CAM_CC_TFE_1_CPHY_RX_CLK] = &cam_cc_tfe_1_cphy_rx_clk.clkr, + [CAM_CC_TFE_1_CSID_CLK] = &cam_cc_tfe_1_csid_clk.clkr, + [CAM_CC_TFE_1_CSID_CLK_SRC] = &cam_cc_tfe_1_csid_clk_src.clkr, + [CAM_CC_TFE_2_AHB_CLK] = &cam_cc_tfe_2_ahb_clk.clkr, + [CAM_CC_TFE_2_CLK] = &cam_cc_tfe_2_clk.clkr, + [CAM_CC_TFE_2_CLK_SRC] = &cam_cc_tfe_2_clk_src.clkr, + [CAM_CC_TFE_2_CPHY_RX_CLK] = &cam_cc_tfe_2_cphy_rx_clk.clkr, + [CAM_CC_TFE_2_CSID_CLK] = &cam_cc_tfe_2_csid_clk.clkr, + [CAM_CC_TFE_2_CSID_CLK_SRC] = &cam_cc_tfe_2_csid_clk_src.clkr, + [CAM_CC_TOP_SHIFT_CLK] = &cam_cc_top_shift_clk.clkr, + [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr, +}; + +static const struct qcom_reset_map cam_cc_milos_resets[] = { + [CAM_CC_BPS_BCR] = { 0x1a000 }, + [CAM_CC_CAMNOC_BCR] = { 0x24000 }, + [CAM_CC_CAMSS_TOP_BCR] = { 0x25000 }, + [CAM_CC_CCI_0_BCR] = { 0x21000 }, + [CAM_CC_CCI_1_BCR] = { 0x22000 }, + [CAM_CC_CPAS_BCR] = { 0x23000 }, + [CAM_CC_CRE_BCR] = { 0x27000 }, + [CAM_CC_CSI0PHY_BCR] = { 0x19000 }, + [CAM_CC_CSI1PHY_BCR] = { 0x19024 }, + [CAM_CC_CSI2PHY_BCR] = { 0x19048 }, + [CAM_CC_CSI3PHY_BCR] = { 0x1906c }, + [CAM_CC_ICP_BCR] = { 0x20000 }, + [CAM_CC_MCLK0_BCR] = { 0x18000 }, + [CAM_CC_MCLK1_BCR] = { 0x18020 }, + [CAM_CC_MCLK2_BCR] = { 0x18040 }, + [CAM_CC_MCLK3_BCR] = { 0x18060 }, + [CAM_CC_MCLK4_BCR] = { 0x18080 }, + [CAM_CC_OPE_0_BCR] = { 0x1b000 }, + [CAM_CC_TFE_0_BCR] = { 0x1c000 }, + [CAM_CC_TFE_1_BCR] = { 0x1d000 }, + [CAM_CC_TFE_2_BCR] = { 0x1e000 }, +}; + +static struct gdsc *cam_cc_milos_gdscs[] = { + [CAM_CC_CAMSS_TOP_GDSC] = &cam_cc_camss_top_gdsc, +}; + +static struct clk_alpha_pll *cam_cc_milos_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll5, + &cam_cc_pll6, +}; + +static u32 cam_cc_milos_critical_cbcrs[] = { + 0x25038, /* CAM_CC_GDSC_CLK */ + 0x2505c, /* CAM_CC_SLEEP_CLK */ +}; + +static const struct regmap_config cam_cc_milos_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x30728, + .fast_io = true, +}; + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_milos_critical_cbcrs), +}; + +static struct qcom_cc_desc cam_cc_milos_desc = { + .config = &cam_cc_milos_regmap_config, + .clks = cam_cc_milos_clocks, + .num_clks = ARRAY_SIZE(cam_cc_milos_clocks), + .resets = cam_cc_milos_resets, + .num_resets = ARRAY_SIZE(cam_cc_milos_resets), + .gdscs = cam_cc_milos_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_milos_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_milos_driver_data, +}; + +static const struct of_device_id cam_cc_milos_match_table[] = { + { .compatible = "qcom,milos-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_milos_match_table); + +static int cam_cc_milos_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &cam_cc_milos_desc); +} + +static struct platform_driver cam_cc_milos_driver = { + .probe = cam_cc_milos_probe, + .driver = { + .name = "cam_cc-milos", + .of_match_table = cam_cc_milos_match_table, + }, +}; + +module_platform_driver(cam_cc_milos_driver); + +MODULE_DESCRIPTION("QTI CAM_CC Milos Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/camcc-qcs615.c b/drivers/clk/qcom/camcc-qcs615.c new file mode 100644 index 000000000000..c063a3bfacd0 --- /dev/null +++ b/drivers/clk/qcom/camcc-qcs615.c @@ -0,0 +1,1597 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#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, +}; + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_AUX, + P_CAM_CC_PLL1_OUT_AUX, + P_CAM_CC_PLL2_OUT_AUX2, + P_CAM_CC_PLL2_OUT_EARLY, + P_CAM_CC_PLL3_OUT_MAIN, +}; + +static const struct pll_vco brammo_vco[] = { + { 500000000, 1250000000, 0 }, +}; + +static const struct pll_vco spark_vco[] = { + { 1000000000, 2100000000, 0 }, + { 750000000, 1500000000, 1 }, + { 500000000, 1000000000, 2 }, + { 300000000, 500000000, 3 }, + { 550000000, 1100000000, 4 }, +}; + +/* 600MHz configuration VCO - 2 */ +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x1f, + .alpha_hi = 0x40, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .aux_output_mask = BIT(1), + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .config = &cam_cc_pll0_config, + .vco_table = spark_vco, + .num_vco = ARRAY_SIZE(spark_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +/* 808MHz configuration VCO - 2 */ +static struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x2a, + .alpha_hi = 0x15, + .alpha = 0x55555555, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .aux_output_mask = BIT(1), + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .config = &cam_cc_pll1_config, + .vco_table = spark_vco, + .num_vco = ARRAY_SIZE(spark_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +/* 960MHz configuration VCO - 0 */ +static struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .vco_val = 0x0, + .vco_mask = GENMASK(21, 20), + .early_output_mask = BIT(3), + .aux2_output_mask = BIT(2), + .post_div_val = 0x1 << 8, + .post_div_mask = 0x3 << 8, + .config_ctl_val = 0x04289, + .test_ctl_val = 0x08000000, + .test_ctl_mask = 0x08000000, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .config = &cam_cc_pll2_config, + .vco_table = brammo_vco, + .num_vco = ARRAY_SIZE(brammo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll2_out_aux2[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll2_out_aux2 = { + .offset = 0x2000, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll2_out_aux2, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll2_out_aux2), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2_out_aux2", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll2.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ops, + }, +}; + +/* 1080MHz configuration - VCO - 0 */ +static struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x38, + .alpha_hi = 0x40, + .alpha_en_mask = BIT(24), + .vco_val = 0x0, + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .config = &cam_cc_pll3_config, + .vco_table = spark_vco, + .num_vco = ARRAY_SIZE(spark_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_AUX, 2 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1.clkr.hw }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EARLY, 4 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, + { .hw = &cam_cc_pll3.clkr.hw }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_AUX, 2 }, + { P_CAM_CC_PLL2_OUT_EARLY, 4 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1.clkr.hw }, + { .hw = &cam_cc_pll2.clkr.hw }, + { .hw = &cam_cc_pll3.clkr.hw }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_AUX2, 1 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2_out_aux2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll3.clkr.hw }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_AUX, 2 }, + { P_CAM_CC_PLL3_OUT_MAIN, 5 }, + { P_CAM_CC_PLL0_OUT_AUX, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1.clkr.hw }, + { .hw = &cam_cc_pll3.clkr.hw }, + { .hw = &cam_cc_pll0.clkr.hw }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0), + F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x6010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_clk_src[] = { + F(37500000, P_CAM_CC_PLL0_OUT_AUX, 16, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_AUX, 12, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_clk_src = { + .cmd_rcgr = 0xb0d8, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_cci_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(269333333, P_CAM_CC_PLL1_OUT_AUX, 3, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0), + F(384000000, P_CAM_CC_PLL2_OUT_EARLY, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x9064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(269333333, P_CAM_CC_PLL1_OUT_AUX, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x5004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x5028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x504c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_AUX, 2, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x603c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(240000000, P_CAM_CC_PLL0_OUT_AUX, 2.5, 0, 0), + F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0), + F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0xb088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(240000000, P_CAM_CC_PLL0_OUT_AUX, 2.5, 0, 0), + F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0), + F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x9010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_csid_clk_src[] = { + F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0), + F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_csid_clk_src = { + .cmd_rcgr = 0x903c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_csid_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_clk_src = { + .cmd_rcgr = 0xa010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_csid_clk_src = { + .cmd_rcgr = 0xa034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_csid_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0xb004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0xb024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ipe_0_clk_src = { + .cmd_rcgr = 0x7010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_jpeg_clk_src[] = { + F(66666667, P_CAM_CC_PLL0_OUT_AUX, 9, 0, 0), + F(133333333, P_CAM_CC_PLL0_OUT_AUX, 4.5, 0, 0), + F(216000000, P_CAM_CC_PLL3_OUT_MAIN, 5, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0xb04c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_jpeg_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = { + F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0), + F(216000000, P_CAM_CC_PLL3_OUT_MAIN, 5, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_AUX, 2, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_lrme_clk_src = { + .cmd_rcgr = 0xb0f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_6, + .freq_tbl = ftbl_cam_cc_lrme_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_lrme_clk_src", + .parent_data = cam_cc_parent_data_6, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_6), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_AUX2, 10, 1, 2), + F(34285714, P_CAM_CC_PLL2_OUT_AUX2, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x4024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x4044, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x4064, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(80000000, P_CAM_CC_PLL0_OUT_AUX, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x6058, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x6070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_areg_clk = { + .halt_reg = 0x6054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_axi_clk = { + .halt_reg = 0x6038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x6028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_clk = { + .halt_reg = 0xb124, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb124, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_clk = { + .halt_reg = 0xb0f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0xb144, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0xb11c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb11c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x5040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x5064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x5020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x5044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x5068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0xb0a0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0a0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_axi_clk = { + .halt_reg = 0x9080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x9028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_cphy_rx_clk = { + .halt_reg = 0x907c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x907c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_csid_clk = { + .halt_reg = 0x9054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x9038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_axi_clk = { + .halt_reg = 0xa058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_clk = { + .halt_reg = 0xa028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_cphy_rx_clk = { + .halt_reg = 0xa054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_csid_clk = { + .halt_reg = 0xa04c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_dsp_clk = { + .halt_reg = 0xa030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0xb01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0xb044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0xb03c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb03c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_ahb_clk = { + .halt_reg = 0x7040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_areg_clk = { + .halt_reg = 0x703c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x703c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_axi_clk = { + .halt_reg = 0x7038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_clk = { + .halt_reg = 0x7028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0xb064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_jpeg_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_lrme_clk = { + .halt_reg = 0xb110, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb110, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_lrme_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_lrme_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x401c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x401c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x403c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x403c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x405c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x405c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x407c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x407c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_soc_ahb_clk = { + .halt_reg = 0xb140, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb140, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_soc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sys_tmr_clk = { + .halt_reg = 0xb0a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sys_tmr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc titan_top_gdsc = { + .gdscr = 0xb134, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "titan_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc bps_gdsc = { + .gdscr = 0x6004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "bps_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_0_gdsc = { + .gdscr = 0x9004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_1_gdsc = { + .gdscr = 0xa004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ipe_0_gdsc = { + .gdscr = 0x7004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ipe_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *cam_cc_qcs615_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr, + [CAM_CC_BPS_AXI_CLK] = &cam_cc_bps_axi_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_CAMNOC_AXI_CLK] = &cam_cc_camnoc_axi_clk.clkr, + [CAM_CC_CCI_CLK] = &cam_cc_cci_clk.clkr, + [CAM_CC_CCI_CLK_SRC] = &cam_cc_cci_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_IFE_0_AXI_CLK] = &cam_cc_ife_0_axi_clk.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_CPHY_RX_CLK] = &cam_cc_ife_0_cphy_rx_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK] = &cam_cc_ife_0_csid_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK_SRC] = &cam_cc_ife_0_csid_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_1_AXI_CLK] = &cam_cc_ife_1_axi_clk.clkr, + [CAM_CC_IFE_1_CLK] = &cam_cc_ife_1_clk.clkr, + [CAM_CC_IFE_1_CLK_SRC] = &cam_cc_ife_1_clk_src.clkr, + [CAM_CC_IFE_1_CPHY_RX_CLK] = &cam_cc_ife_1_cphy_rx_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK] = &cam_cc_ife_1_csid_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK_SRC] = &cam_cc_ife_1_csid_clk_src.clkr, + [CAM_CC_IFE_1_DSP_CLK] = &cam_cc_ife_1_dsp_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_0_AHB_CLK] = &cam_cc_ipe_0_ahb_clk.clkr, + [CAM_CC_IPE_0_AREG_CLK] = &cam_cc_ipe_0_areg_clk.clkr, + [CAM_CC_IPE_0_AXI_CLK] = &cam_cc_ipe_0_axi_clk.clkr, + [CAM_CC_IPE_0_CLK] = &cam_cc_ipe_0_clk.clkr, + [CAM_CC_IPE_0_CLK_SRC] = &cam_cc_ipe_0_clk_src.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_LRME_CLK] = &cam_cc_lrme_clk.clkr, + [CAM_CC_LRME_CLK_SRC] = &cam_cc_lrme_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL2_OUT_AUX2] = &cam_cc_pll2_out_aux2.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_SOC_AHB_CLK] = &cam_cc_soc_ahb_clk.clkr, + [CAM_CC_SYS_TMR_CLK] = &cam_cc_sys_tmr_clk.clkr, +}; + +static struct gdsc *cam_cc_qcs615_gdscs[] = { + [BPS_GDSC] = &bps_gdsc, + [IFE_0_GDSC] = &ife_0_gdsc, + [IFE_1_GDSC] = &ife_1_gdsc, + [IPE_0_GDSC] = &ipe_0_gdsc, + [TITAN_TOP_GDSC] = &titan_top_gdsc, +}; + +static const struct qcom_reset_map cam_cc_qcs615_resets[] = { + [CAM_CC_BPS_BCR] = { 0x6000 }, + [CAM_CC_CAMNOC_BCR] = { 0xb120 }, + [CAM_CC_CCI_BCR] = { 0xb0d4 }, + [CAM_CC_CPAS_BCR] = { 0xb118 }, + [CAM_CC_CSI0PHY_BCR] = { 0x5000 }, + [CAM_CC_CSI1PHY_BCR] = { 0x5024 }, + [CAM_CC_CSI2PHY_BCR] = { 0x5048 }, + [CAM_CC_ICP_BCR] = { 0xb074 }, + [CAM_CC_IFE_0_BCR] = { 0x9000 }, + [CAM_CC_IFE_1_BCR] = { 0xa000 }, + [CAM_CC_IFE_LITE_BCR] = { 0xb000 }, + [CAM_CC_IPE_0_BCR] = { 0x7000 }, + [CAM_CC_JPEG_BCR] = { 0xb048 }, + [CAM_CC_LRME_BCR] = { 0xb0f4 }, + [CAM_CC_MCLK0_BCR] = { 0x4000 }, + [CAM_CC_MCLK1_BCR] = { 0x4020 }, + [CAM_CC_MCLK2_BCR] = { 0x4040 }, + [CAM_CC_MCLK3_BCR] = { 0x4060 }, + [CAM_CC_TITAN_TOP_BCR] = { 0xb130 }, +}; + +static struct clk_alpha_pll *cam_cc_qcs615_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, +}; + +static const struct regmap_config cam_cc_qcs615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd004, + .fast_io = true, +}; + +static 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), +}; + +static const struct qcom_cc_desc cam_cc_qcs615_desc = { + .config = &cam_cc_qcs615_regmap_config, + .clks = cam_cc_qcs615_clocks, + .num_clks = ARRAY_SIZE(cam_cc_qcs615_clocks), + .resets = cam_cc_qcs615_resets, + .num_resets = ARRAY_SIZE(cam_cc_qcs615_resets), + .gdscs = cam_cc_qcs615_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_qcs615_gdscs), + .driver_data = &cam_cc_qcs615_driver_data, +}; + +static const struct of_device_id cam_cc_qcs615_match_table[] = { + { .compatible = "qcom,qcs615-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_qcs615_match_table); + +static int cam_cc_qcs615_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &cam_cc_qcs615_desc); +} + +static struct platform_driver cam_cc_qcs615_driver = { + .probe = cam_cc_qcs615_probe, + .driver = { + .name = "camcc-qcs615", + .of_match_table = cam_cc_qcs615_match_table, + }, +}; + +module_platform_driver(cam_cc_qcs615_driver); + +MODULE_DESCRIPTION("QTI CAMCC QCS615 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/camcc-sc8180x.c b/drivers/clk/qcom/camcc-sc8180x.c new file mode 100644 index 000000000000..388fedf1dc81 --- /dev/null +++ b/drivers/clk/qcom/camcc-sc8180x.c @@ -0,0 +1,2889 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#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 "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_IFACE, + DT_BI_TCXO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL0_OUT_ODD, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL2_OUT_EARLY, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_EVEN, + P_CAM_CC_PLL4_OUT_EVEN, + P_CAM_CC_PLL5_OUT_EVEN, + P_CAM_CC_PLL6_OUT_EVEN, + P_SLEEP_CLK, +}; + +static const struct pll_vco regera_vco[] = { + { 600000000, 3300000000, 0 }, +}; + +static const struct pll_vco trion_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x3e, + .alpha = 0x8000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00003100, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_trion_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = { + { 0x3, 3 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = { + .offset = 0x0, + .post_div_shift = 12, + .post_div_table = post_div_table_cam_cc_pll0_out_odd, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_odd", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_trion_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x13, + .alpha = 0x8800, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x10000807, + .config_ctl_hi_val = 0x00000011, + .config_ctl_hi1_val = 0x04300142, + .test_ctl_val = 0x04000400, + .test_ctl_hi_val = 0x00004000, + .test_ctl_hi1_val = 0x00000000, + .user_ctl_val = 0x00000100, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .vco_table = regera_vco, + .num_vco = ARRAY_SIZE(regera_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_REGERA], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_regera_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll2_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll2_out_main = { + .offset = 0x2000, + .post_div_shift = 8, + .post_div_table = post_div_table_cam_cc_pll2_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll2_out_main), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_REGERA], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2_out_main", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll2.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_trion_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x14, + .alpha = 0xd555, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct alpha_pll_config cam_cc_pll4_config = { + .l = 0x14, + .alpha = 0xd555, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll4 = { + .offset = 0x4000, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct alpha_pll_config cam_cc_pll5_config = { + .l = 0x14, + .alpha = 0xd555, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll5 = { + .offset = 0x4078, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct alpha_pll_config cam_cc_pll6_config = { + .l = 0x14, + .alpha = 0xd555, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_hi1_val = 0x00000020, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll cam_cc_pll6 = { + .offset = 0x40f0, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_EVEN, 2 }, + { P_CAM_CC_PLL0_OUT_ODD, 3 }, + { P_CAM_CC_PLL2_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll2_out_main.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EARLY, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL3_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll3.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL4_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll4.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL5_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll5.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL6_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll6.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_EVEN, 4 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_7[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_7[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x7010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_camnoc_axi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(266666667, P_CAM_CC_PLL0_OUT_ODD, 1.5, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_camnoc_axi_clk_src = { + .cmd_rcgr = 0xc170, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_camnoc_axi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0xc108, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0xc124, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_2_clk_src = { + .cmd_rcgr = 0xc204, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_2_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_3_clk_src = { + .cmd_rcgr = 0xc220, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_3_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0xa064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x6004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x6028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x604c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x6070, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x703c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fd_core_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fd_core_clk_src = { + .cmd_rcgr = 0xc0e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fd_core_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0xc0b8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(558000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(637000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(760000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0xa010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_csid_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_csid_clk_src = { + .cmd_rcgr = 0xa03c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_1_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(558000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(637000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(760000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_1_clk_src = { + .cmd_rcgr = 0xb010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_ife_1_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_csid_clk_src = { + .cmd_rcgr = 0xb034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_2_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(558000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(637000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(760000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_2_clk_src = { + .cmd_rcgr = 0xf010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_ife_2_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_2_csid_clk_src = { + .cmd_rcgr = 0xf03c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_3_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(558000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(637000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(760000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_3_clk_src = { + .cmd_rcgr = 0xf07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_ife_3_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_3_csid_clk_src = { + .cmd_rcgr = 0xf0a8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_lite_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_MAIN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_lite_0_clk_src = { + .cmd_rcgr = 0xc004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_0_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_0_csid_clk_src = { + .cmd_rcgr = 0xc020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_0_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_1_clk_src = { + .cmd_rcgr = 0xc048, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_1_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_1_csid_clk_src = { + .cmd_rcgr = 0xc064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_1_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_2_clk_src = { + .cmd_rcgr = 0xc240, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_2_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_2_csid_clk_src = { + .cmd_rcgr = 0xc25c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_2_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_3_clk_src = { + .cmd_rcgr = 0xc284, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_3_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_3_csid_clk_src = { + .cmd_rcgr = 0xc2a0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_3_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(375000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(475000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(520000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_0_clk_src = { + .cmd_rcgr = 0x8010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_6, + .freq_tbl = ftbl_cam_cc_ipe_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_clk_src", + .parent_data = cam_cc_parent_data_6, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_6), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0xc08c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(240000000, P_CAM_CC_PLL2_OUT_MAIN, 2, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_MAIN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_lrme_clk_src = { + .cmd_rcgr = 0xc144, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_lrme_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_lrme_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_EARLY, 10, 1, 4), + F(68571429, P_CAM_CC_PLL2_OUT_EARLY, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x5004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x5024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x5044, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x5064, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x5084, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk5_clk_src = { + .cmd_rcgr = 0x50a4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk6_clk_src = { + .cmd_rcgr = 0x50c4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk7_clk_src = { + .cmd_rcgr = 0x50e4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x7058, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_xo_clk_src = { + .cmd_rcgr = 0xc1cc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_7, + .freq_tbl = ftbl_cam_cc_xo_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_xo_clk_src", + .parent_data = cam_cc_parent_data_7, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x7070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_areg_clk = { + .halt_reg = 0x7054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_axi_clk = { + .halt_reg = 0x7038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x7028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_clk = { + .halt_reg = 0xc18c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc18c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_dcd_xo_clk = { + .halt_reg = 0xc194, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc194, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_dcd_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0xc120, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc120, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0xc13c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc13c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_2_clk = { + .halt_reg = 0xc21c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc21c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_3_clk = { + .halt_reg = 0xc238, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc238, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0xc1c8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xc1c8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0xc168, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc168, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x601c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x601c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x6040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x6064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x6088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi3phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x6020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x6044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x6068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x608c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x608c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_fd_core_clk = { + .halt_reg = 0xc0f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc0f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fd_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fd_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_fd_core_uar_clk = { + .halt_reg = 0xc100, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc100, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fd_core_uar_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fd_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ahb_clk = { + .halt_reg = 0xc0d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc0d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0xc0d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc0d0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_axi_clk = { + .halt_reg = 0xa080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0xa028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_cphy_rx_clk = { + .halt_reg = 0xa07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_csid_clk = { + .halt_reg = 0xa054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0xa038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_axi_clk = { + .halt_reg = 0xb058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_clk = { + .halt_reg = 0xb028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_cphy_rx_clk = { + .halt_reg = 0xb054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_csid_clk = { + .halt_reg = 0xb04c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_dsp_clk = { + .halt_reg = 0xb030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_axi_clk = { + .halt_reg = 0xf068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_clk = { + .halt_reg = 0xf028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_cphy_rx_clk = { + .halt_reg = 0xf064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_csid_clk = { + .halt_reg = 0xf054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_2_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_dsp_clk = { + .halt_reg = 0xf038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_3_axi_clk = { + .halt_reg = 0xf0d4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf0d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_3_clk = { + .halt_reg = 0xf094, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_3_cphy_rx_clk = { + .halt_reg = 0xf0d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf0d0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_3_csid_clk = { + .halt_reg = 0xf0c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf0c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_3_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_3_dsp_clk = { + .halt_reg = 0xf0a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf0a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_3_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_0_clk = { + .halt_reg = 0xc01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_0_cphy_rx_clk = { + .halt_reg = 0xc040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_0_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_0_csid_clk = { + .halt_reg = 0xc038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_0_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_0_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_1_clk = { + .halt_reg = 0xc060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_1_cphy_rx_clk = { + .halt_reg = 0xc084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_1_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_1_csid_clk = { + .halt_reg = 0xc07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_1_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_1_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_2_clk = { + .halt_reg = 0xc258, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc258, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_2_cphy_rx_clk = { + .halt_reg = 0xc27c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc27c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_2_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_2_csid_clk = { + .halt_reg = 0xc274, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc274, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_2_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_2_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_3_clk = { + .halt_reg = 0xc29c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc29c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_3_cphy_rx_clk = { + .halt_reg = 0xc2c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc2c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_3_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_3_csid_clk = { + .halt_reg = 0xc2b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc2b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_3_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_3_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_ahb_clk = { + .halt_reg = 0x8040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_areg_clk = { + .halt_reg = 0x803c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x803c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_axi_clk = { + .halt_reg = 0x8038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_clk = { + .halt_reg = 0x8028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_ahb_clk = { + .halt_reg = 0x9028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_areg_clk = { + .halt_reg = 0x9024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_1_areg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_axi_clk = { + .halt_reg = 0x9020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_1_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_clk = { + .halt_reg = 0x9010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0xc0a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc0a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_jpeg_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_lrme_clk = { + .halt_reg = 0xc15c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc15c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_lrme_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_lrme_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x503c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x503c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x505c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x505c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x507c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x507c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x509c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x509c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk5_clk = { + .halt_reg = 0x50bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x50bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk6_clk = { + .halt_reg = 0x50dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x50dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk7_clk = { + .halt_reg = 0x50fc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x50fc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc titan_top_gdsc = { + .gdscr = 0xc1bc, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "titan_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc bps_gdsc = { + .gdscr = 0x7004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "bps_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_0_gdsc = { + .gdscr = 0xa004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_1_gdsc = { + .gdscr = 0xb004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_2_gdsc = { + .gdscr = 0xf004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_2_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ife_3_gdsc = { + .gdscr = 0xf070, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ife_3_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ipe_0_gdsc = { + .gdscr = 0x8004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ipe_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc ipe_1_gdsc = { + .gdscr = 0x9004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ipe_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR, +}; + +static struct clk_regmap *cam_cc_sc8180x_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr, + [CAM_CC_BPS_AXI_CLK] = &cam_cc_bps_axi_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_CAMNOC_AXI_CLK] = &cam_cc_camnoc_axi_clk.clkr, + [CAM_CC_CAMNOC_AXI_CLK_SRC] = &cam_cc_camnoc_axi_clk_src.clkr, + [CAM_CC_CAMNOC_DCD_XO_CLK] = &cam_cc_camnoc_dcd_xo_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CCI_2_CLK] = &cam_cc_cci_2_clk.clkr, + [CAM_CC_CCI_2_CLK_SRC] = &cam_cc_cci_2_clk_src.clkr, + [CAM_CC_CCI_3_CLK] = &cam_cc_cci_3_clk.clkr, + [CAM_CC_CCI_3_CLK_SRC] = &cam_cc_cci_3_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_FD_CORE_CLK] = &cam_cc_fd_core_clk.clkr, + [CAM_CC_FD_CORE_CLK_SRC] = &cam_cc_fd_core_clk_src.clkr, + [CAM_CC_FD_CORE_UAR_CLK] = &cam_cc_fd_core_uar_clk.clkr, + [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_IFE_0_AXI_CLK] = &cam_cc_ife_0_axi_clk.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_CPHY_RX_CLK] = &cam_cc_ife_0_cphy_rx_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK] = &cam_cc_ife_0_csid_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK_SRC] = &cam_cc_ife_0_csid_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_1_AXI_CLK] = &cam_cc_ife_1_axi_clk.clkr, + [CAM_CC_IFE_1_CLK] = &cam_cc_ife_1_clk.clkr, + [CAM_CC_IFE_1_CLK_SRC] = &cam_cc_ife_1_clk_src.clkr, + [CAM_CC_IFE_1_CPHY_RX_CLK] = &cam_cc_ife_1_cphy_rx_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK] = &cam_cc_ife_1_csid_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK_SRC] = &cam_cc_ife_1_csid_clk_src.clkr, + [CAM_CC_IFE_1_DSP_CLK] = &cam_cc_ife_1_dsp_clk.clkr, + [CAM_CC_IFE_2_AXI_CLK] = &cam_cc_ife_2_axi_clk.clkr, + [CAM_CC_IFE_2_CLK] = &cam_cc_ife_2_clk.clkr, + [CAM_CC_IFE_2_CLK_SRC] = &cam_cc_ife_2_clk_src.clkr, + [CAM_CC_IFE_2_CPHY_RX_CLK] = &cam_cc_ife_2_cphy_rx_clk.clkr, + [CAM_CC_IFE_2_CSID_CLK] = &cam_cc_ife_2_csid_clk.clkr, + [CAM_CC_IFE_2_CSID_CLK_SRC] = &cam_cc_ife_2_csid_clk_src.clkr, + [CAM_CC_IFE_2_DSP_CLK] = &cam_cc_ife_2_dsp_clk.clkr, + [CAM_CC_IFE_3_AXI_CLK] = &cam_cc_ife_3_axi_clk.clkr, + [CAM_CC_IFE_3_CLK] = &cam_cc_ife_3_clk.clkr, + [CAM_CC_IFE_3_CLK_SRC] = &cam_cc_ife_3_clk_src.clkr, + [CAM_CC_IFE_3_CPHY_RX_CLK] = &cam_cc_ife_3_cphy_rx_clk.clkr, + [CAM_CC_IFE_3_CSID_CLK] = &cam_cc_ife_3_csid_clk.clkr, + [CAM_CC_IFE_3_CSID_CLK_SRC] = &cam_cc_ife_3_csid_clk_src.clkr, + [CAM_CC_IFE_3_DSP_CLK] = &cam_cc_ife_3_dsp_clk.clkr, + [CAM_CC_IFE_LITE_0_CLK] = &cam_cc_ife_lite_0_clk.clkr, + [CAM_CC_IFE_LITE_0_CLK_SRC] = &cam_cc_ife_lite_0_clk_src.clkr, + [CAM_CC_IFE_LITE_0_CPHY_RX_CLK] = &cam_cc_ife_lite_0_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_0_CSID_CLK] = &cam_cc_ife_lite_0_csid_clk.clkr, + [CAM_CC_IFE_LITE_0_CSID_CLK_SRC] = &cam_cc_ife_lite_0_csid_clk_src.clkr, + [CAM_CC_IFE_LITE_1_CLK] = &cam_cc_ife_lite_1_clk.clkr, + [CAM_CC_IFE_LITE_1_CLK_SRC] = &cam_cc_ife_lite_1_clk_src.clkr, + [CAM_CC_IFE_LITE_1_CPHY_RX_CLK] = &cam_cc_ife_lite_1_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_1_CSID_CLK] = &cam_cc_ife_lite_1_csid_clk.clkr, + [CAM_CC_IFE_LITE_1_CSID_CLK_SRC] = &cam_cc_ife_lite_1_csid_clk_src.clkr, + [CAM_CC_IFE_LITE_2_CLK] = &cam_cc_ife_lite_2_clk.clkr, + [CAM_CC_IFE_LITE_2_CLK_SRC] = &cam_cc_ife_lite_2_clk_src.clkr, + [CAM_CC_IFE_LITE_2_CPHY_RX_CLK] = &cam_cc_ife_lite_2_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_2_CSID_CLK] = &cam_cc_ife_lite_2_csid_clk.clkr, + [CAM_CC_IFE_LITE_2_CSID_CLK_SRC] = &cam_cc_ife_lite_2_csid_clk_src.clkr, + [CAM_CC_IFE_LITE_3_CLK] = &cam_cc_ife_lite_3_clk.clkr, + [CAM_CC_IFE_LITE_3_CLK_SRC] = &cam_cc_ife_lite_3_clk_src.clkr, + [CAM_CC_IFE_LITE_3_CPHY_RX_CLK] = &cam_cc_ife_lite_3_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_3_CSID_CLK] = &cam_cc_ife_lite_3_csid_clk.clkr, + [CAM_CC_IFE_LITE_3_CSID_CLK_SRC] = &cam_cc_ife_lite_3_csid_clk_src.clkr, + [CAM_CC_IPE_0_AHB_CLK] = &cam_cc_ipe_0_ahb_clk.clkr, + [CAM_CC_IPE_0_AREG_CLK] = &cam_cc_ipe_0_areg_clk.clkr, + [CAM_CC_IPE_0_AXI_CLK] = &cam_cc_ipe_0_axi_clk.clkr, + [CAM_CC_IPE_0_CLK] = &cam_cc_ipe_0_clk.clkr, + [CAM_CC_IPE_0_CLK_SRC] = &cam_cc_ipe_0_clk_src.clkr, + [CAM_CC_IPE_1_AHB_CLK] = &cam_cc_ipe_1_ahb_clk.clkr, + [CAM_CC_IPE_1_AREG_CLK] = &cam_cc_ipe_1_areg_clk.clkr, + [CAM_CC_IPE_1_AXI_CLK] = &cam_cc_ipe_1_axi_clk.clkr, + [CAM_CC_IPE_1_CLK] = &cam_cc_ipe_1_clk.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_LRME_CLK] = &cam_cc_lrme_clk.clkr, + [CAM_CC_LRME_CLK_SRC] = &cam_cc_lrme_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_MCLK5_CLK] = &cam_cc_mclk5_clk.clkr, + [CAM_CC_MCLK5_CLK_SRC] = &cam_cc_mclk5_clk_src.clkr, + [CAM_CC_MCLK6_CLK] = &cam_cc_mclk6_clk.clkr, + [CAM_CC_MCLK6_CLK_SRC] = &cam_cc_mclk6_clk_src.clkr, + [CAM_CC_MCLK7_CLK] = &cam_cc_mclk7_clk.clkr, + [CAM_CC_MCLK7_CLK_SRC] = &cam_cc_mclk7_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL2_OUT_MAIN] = &cam_cc_pll2_out_main.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL4] = &cam_cc_pll4.clkr, + [CAM_CC_PLL5] = &cam_cc_pll5.clkr, + [CAM_CC_PLL6] = &cam_cc_pll6.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr, +}; + +static struct gdsc *cam_cc_sc8180x_gdscs[] = { + [BPS_GDSC] = &bps_gdsc, + [IFE_0_GDSC] = &ife_0_gdsc, + [IFE_1_GDSC] = &ife_1_gdsc, + [IFE_2_GDSC] = &ife_2_gdsc, + [IFE_3_GDSC] = &ife_3_gdsc, + [IPE_0_GDSC] = &ipe_0_gdsc, + [IPE_1_GDSC] = &ipe_1_gdsc, + [TITAN_TOP_GDSC] = &titan_top_gdsc, +}; + +static const struct qcom_reset_map cam_cc_sc8180x_resets[] = { + [CAM_CC_BPS_BCR] = { 0x7000 }, + [CAM_CC_CAMNOC_BCR] = { 0xc16c }, + [CAM_CC_CCI_BCR] = { 0xc104 }, + [CAM_CC_CPAS_BCR] = { 0xc164 }, + [CAM_CC_CSI0PHY_BCR] = { 0x6000 }, + [CAM_CC_CSI1PHY_BCR] = { 0x6024 }, + [CAM_CC_CSI2PHY_BCR] = { 0x6048 }, + [CAM_CC_CSI3PHY_BCR] = { 0x606c }, + [CAM_CC_FD_BCR] = { 0xc0dc }, + [CAM_CC_ICP_BCR] = { 0xc0b4 }, + [CAM_CC_IFE_0_BCR] = { 0xa000 }, + [CAM_CC_IFE_1_BCR] = { 0xb000 }, + [CAM_CC_IFE_2_BCR] = { 0xf000 }, + [CAM_CC_IFE_3_BCR] = { 0xf06c }, + [CAM_CC_IFE_LITE_0_BCR] = { 0xc000 }, + [CAM_CC_IFE_LITE_1_BCR] = { 0xc044 }, + [CAM_CC_IFE_LITE_2_BCR] = { 0xc23c }, + [CAM_CC_IFE_LITE_3_BCR] = { 0xc280 }, + [CAM_CC_IPE_0_BCR] = { 0x8000 }, + [CAM_CC_IPE_1_BCR] = { 0x9000 }, + [CAM_CC_JPEG_BCR] = { 0xc088 }, + [CAM_CC_LRME_BCR] = { 0xc140 }, + [CAM_CC_MCLK0_BCR] = { 0x5000 }, + [CAM_CC_MCLK1_BCR] = { 0x5020 }, + [CAM_CC_MCLK2_BCR] = { 0x5040 }, + [CAM_CC_MCLK3_BCR] = { 0x5060 }, + [CAM_CC_MCLK4_BCR] = { 0x5080 }, + [CAM_CC_MCLK5_BCR] = { 0x50a0 }, + [CAM_CC_MCLK6_BCR] = { 0x50c0 }, + [CAM_CC_MCLK7_BCR] = { 0x50e0 }, +}; + +static const struct regmap_config cam_cc_sc8180x_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf0d4, + .fast_io = true, +}; + +static const struct qcom_cc_desc cam_cc_sc8180x_desc = { + .config = &cam_cc_sc8180x_regmap_config, + .clks = cam_cc_sc8180x_clocks, + .num_clks = ARRAY_SIZE(cam_cc_sc8180x_clocks), + .resets = cam_cc_sc8180x_resets, + .num_resets = ARRAY_SIZE(cam_cc_sc8180x_resets), + .gdscs = cam_cc_sc8180x_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_sc8180x_gdscs), +}; + +static const struct of_device_id cam_cc_sc8180x_match_table[] = { + { .compatible = "qcom,sc8180x-camcc" }, + { } +}; +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; +} + +static struct platform_driver cam_cc_sc8180x_driver = { + .probe = cam_cc_sc8180x_probe, + .driver = { + .name = "camcc-sc8180x", + .of_match_table = cam_cc_sc8180x_match_table, + }, +}; + +module_platform_driver(cam_cc_sc8180x_driver); + +MODULE_DESCRIPTION("QTI CAMCC SC8180X Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/camcc-sm8450.c b/drivers/clk/qcom/camcc-sm8450.c index 08982737e490..4dd8be8cc988 100644 --- a/drivers/clk/qcom/camcc-sm8450.c +++ b/drivers/clk/qcom/camcc-sm8450.c @@ -86,6 +86,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll0_config = { static struct clk_alpha_pll cam_cc_pll0 = { .offset = 0x0, + .config = &cam_cc_pll0_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -191,6 +192,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll1_config = { static struct clk_alpha_pll cam_cc_pll1 = { .offset = 0x1000, + .config = &cam_cc_pll1_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -257,6 +259,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll2_config = { static struct clk_alpha_pll cam_cc_pll2 = { .offset = 0x2000, + .config = &cam_cc_pll2_config, .vco_table = rivian_evo_vco, .num_vco = ARRAY_SIZE(rivian_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], @@ -296,6 +299,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll3_config = { static struct clk_alpha_pll cam_cc_pll3 = { .offset = 0x3000, + .config = &cam_cc_pll3_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -368,6 +372,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll4_config = { static struct clk_alpha_pll cam_cc_pll4 = { .offset = 0x4000, + .config = &cam_cc_pll4_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -440,6 +445,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll5_config = { static struct clk_alpha_pll cam_cc_pll5 = { .offset = 0x5000, + .config = &cam_cc_pll5_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -512,6 +518,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll6_config = { static struct clk_alpha_pll cam_cc_pll6 = { .offset = 0x6000, + .config = &cam_cc_pll6_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -584,6 +591,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll7_config = { static struct clk_alpha_pll cam_cc_pll7 = { .offset = 0x7000, + .config = &cam_cc_pll7_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -656,6 +664,7 @@ static const struct alpha_pll_config sm8475_cam_cc_pll8_config = { static struct clk_alpha_pll cam_cc_pll8 = { .offset = 0x8000, + .config = &cam_cc_pll8_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -1476,24 +1485,6 @@ static struct clk_rcg2 cam_cc_xo_clk_src = { }, }; -static struct clk_branch cam_cc_gdsc_clk = { - .halt_reg = 0x1320c, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0x1320c, - .enable_mask = BIT(0), - .hw.init = &(const struct clk_init_data) { - .name = "cam_cc_gdsc_clk", - .parent_hws = (const struct clk_hw*[]) { - &cam_cc_xo_clk_src.clkr.hw, - }, - .num_parents = 1, - .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch cam_cc_bps_ahb_clk = { .halt_reg = 0x1004c, .halt_check = BRANCH_HALT, @@ -2819,7 +2810,6 @@ static struct clk_regmap *cam_cc_sm8450_clocks[] = { [CAM_CC_CSIPHY4_CLK] = &cam_cc_csiphy4_clk.clkr, [CAM_CC_CSIPHY5_CLK] = &cam_cc_csiphy5_clk.clkr, [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, - [CAM_CC_GDSC_CLK] = &cam_cc_gdsc_clk.clkr, [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, @@ -2913,6 +2903,22 @@ static const struct qcom_reset_map cam_cc_sm8450_resets[] = { [CAM_CC_SFE_1_BCR] = { 0x13094 }, }; +static struct clk_alpha_pll *cam_cc_sm8450_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll5, + &cam_cc_pll6, + &cam_cc_pll7, + &cam_cc_pll8, +}; + +static u32 cam_cc_sm8450_critical_cbcrs[] = { + 0x1320c, /* CAM_CC_GDSC_CLK */ +}; + static const struct regmap_config cam_cc_sm8450_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -3021,6 +3027,13 @@ static struct gdsc *cam_cc_sm8450_gdscs[] = { [TITAN_TOP_GDSC] = &titan_top_gdsc, }; +static 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, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_sm8450_critical_cbcrs), +}; + static const struct qcom_cc_desc cam_cc_sm8450_desc = { .config = &cam_cc_sm8450_regmap_config, .clks = cam_cc_sm8450_clocks, @@ -3029,6 +3042,8 @@ static const struct qcom_cc_desc cam_cc_sm8450_desc = { .num_resets = ARRAY_SIZE(cam_cc_sm8450_resets), .gdscs = cam_cc_sm8450_gdscs, .num_gdscs = ARRAY_SIZE(cam_cc_sm8450_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_sm8450_driver_data, }; static const struct of_device_id cam_cc_sm8450_match_table[] = { @@ -3040,12 +3055,6 @@ MODULE_DEVICE_TABLE(of, cam_cc_sm8450_match_table); static int cam_cc_sm8450_probe(struct platform_device *pdev) { - struct regmap *regmap; - - regmap = qcom_cc_map(pdev, &cam_cc_sm8450_desc); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - if (of_device_is_compatible(pdev->dev.of_node, "qcom,sm8475-camcc")) { /* Update CAMCC PLL0 */ cam_cc_pll0.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE]; @@ -3092,28 +3101,18 @@ static int cam_cc_sm8450_probe(struct platform_device *pdev) cam_cc_pll8_out_even.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE]; cam_cc_pll8_out_even.clkr.hw.init = &sm8475_cam_cc_pll8_out_even_init; - clk_lucid_ole_pll_configure(&cam_cc_pll0, regmap, &sm8475_cam_cc_pll0_config); - clk_lucid_ole_pll_configure(&cam_cc_pll1, regmap, &sm8475_cam_cc_pll1_config); - clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &sm8475_cam_cc_pll2_config); - clk_lucid_ole_pll_configure(&cam_cc_pll3, regmap, &sm8475_cam_cc_pll3_config); - clk_lucid_ole_pll_configure(&cam_cc_pll4, regmap, &sm8475_cam_cc_pll4_config); - clk_lucid_ole_pll_configure(&cam_cc_pll5, regmap, &sm8475_cam_cc_pll5_config); - clk_lucid_ole_pll_configure(&cam_cc_pll6, regmap, &sm8475_cam_cc_pll6_config); - clk_lucid_ole_pll_configure(&cam_cc_pll7, regmap, &sm8475_cam_cc_pll7_config); - clk_lucid_ole_pll_configure(&cam_cc_pll8, regmap, &sm8475_cam_cc_pll8_config); - } else { - clk_lucid_evo_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); - clk_lucid_evo_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); - clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); - clk_lucid_evo_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); - clk_lucid_evo_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); - clk_lucid_evo_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config); - clk_lucid_evo_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); - clk_lucid_evo_pll_configure(&cam_cc_pll7, regmap, &cam_cc_pll7_config); - clk_lucid_evo_pll_configure(&cam_cc_pll8, regmap, &cam_cc_pll8_config); + cam_cc_pll0.config = &sm8475_cam_cc_pll0_config; + cam_cc_pll1.config = &sm8475_cam_cc_pll1_config; + cam_cc_pll2.config = &sm8475_cam_cc_pll2_config; + cam_cc_pll3.config = &sm8475_cam_cc_pll3_config; + cam_cc_pll4.config = &sm8475_cam_cc_pll4_config; + cam_cc_pll5.config = &sm8475_cam_cc_pll5_config; + cam_cc_pll6.config = &sm8475_cam_cc_pll6_config; + cam_cc_pll7.config = &sm8475_cam_cc_pll7_config; + cam_cc_pll8.config = &sm8475_cam_cc_pll8_config; } - return qcom_cc_really_probe(&pdev->dev, &cam_cc_sm8450_desc, regmap); + return qcom_cc_probe(pdev, &cam_cc_sm8450_desc); } static struct platform_driver cam_cc_sm8450_driver = { diff --git a/drivers/clk/qcom/camcc-sm8550.c b/drivers/clk/qcom/camcc-sm8550.c index 871155783c79..63aed9e4c362 100644 --- a/drivers/clk/qcom/camcc-sm8550.c +++ b/drivers/clk/qcom/camcc-sm8550.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -74,6 +73,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -151,6 +151,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -201,6 +202,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 = rivian_ole_vco, .num_vco = ARRAY_SIZE(rivian_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], @@ -232,6 +234,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -286,6 +289,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -340,6 +344,7 @@ static const struct alpha_pll_config cam_cc_pll5_config = { static struct clk_alpha_pll cam_cc_pll5 = { .offset = 0x5000, + .config = &cam_cc_pll5_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -394,6 +399,7 @@ static const struct alpha_pll_config cam_cc_pll6_config = { static struct clk_alpha_pll cam_cc_pll6 = { .offset = 0x6000, + .config = &cam_cc_pll6_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -448,6 +454,7 @@ static const struct alpha_pll_config cam_cc_pll7_config = { static struct clk_alpha_pll cam_cc_pll7 = { .offset = 0x7000, + .config = &cam_cc_pll7_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -502,6 +509,7 @@ static const struct alpha_pll_config cam_cc_pll8_config = { static struct clk_alpha_pll cam_cc_pll8 = { .offset = 0x8000, + .config = &cam_cc_pll8_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -556,6 +564,7 @@ static const struct alpha_pll_config cam_cc_pll9_config = { static struct clk_alpha_pll cam_cc_pll9 = { .offset = 0x9000, + .config = &cam_cc_pll9_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -610,6 +619,7 @@ static const struct alpha_pll_config cam_cc_pll10_config = { static struct clk_alpha_pll cam_cc_pll10 = { .offset = 0xa000, + .config = &cam_cc_pll10_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -664,6 +674,7 @@ static const struct alpha_pll_config cam_cc_pll11_config = { static struct clk_alpha_pll cam_cc_pll11 = { .offset = 0xb000, + .config = &cam_cc_pll11_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -718,6 +729,7 @@ static const struct alpha_pll_config cam_cc_pll12_config = { static struct clk_alpha_pll cam_cc_pll12 = { .offset = 0xc000, + .config = &cam_cc_pll12_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -3479,6 +3491,27 @@ static const struct qcom_reset_map cam_cc_sm8550_resets[] = { [CAM_CC_SFE_1_BCR] = { 0x133dc }, }; +static struct clk_alpha_pll *cam_cc_sm8550_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll5, + &cam_cc_pll6, + &cam_cc_pll7, + &cam_cc_pll8, + &cam_cc_pll9, + &cam_cc_pll10, + &cam_cc_pll11, + &cam_cc_pll12, +}; + +static u32 cam_cc_sm8550_critical_cbcrs[] = { + 0x1419c, /* CAM_CC_GDSC_CLK */ + 0x142cc, /* CAM_CC_SLEEP_CLK */ +}; + static const struct regmap_config cam_cc_sm8550_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -3487,6 +3520,13 @@ static const struct regmap_config cam_cc_sm8550_regmap_config = { .fast_io = true, }; +static 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, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_sm8550_critical_cbcrs), +}; + static const struct qcom_cc_desc cam_cc_sm8550_desc = { .config = &cam_cc_sm8550_regmap_config, .clks = cam_cc_sm8550_clocks, @@ -3495,6 +3535,8 @@ static const struct qcom_cc_desc cam_cc_sm8550_desc = { .num_resets = ARRAY_SIZE(cam_cc_sm8550_resets), .gdscs = cam_cc_sm8550_gdscs, .num_gdscs = ARRAY_SIZE(cam_cc_sm8550_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_sm8550_driver_data, }; static const struct of_device_id cam_cc_sm8550_match_table[] = { @@ -3505,46 +3547,7 @@ MODULE_DEVICE_TABLE(of, cam_cc_sm8550_match_table); static int cam_cc_sm8550_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_sm8550_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); - } - - clk_lucid_ole_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); - clk_lucid_ole_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); - clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); - clk_lucid_ole_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); - clk_lucid_ole_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); - clk_lucid_ole_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config); - clk_lucid_ole_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); - clk_lucid_ole_pll_configure(&cam_cc_pll7, regmap, &cam_cc_pll7_config); - clk_lucid_ole_pll_configure(&cam_cc_pll8, regmap, &cam_cc_pll8_config); - clk_lucid_ole_pll_configure(&cam_cc_pll9, regmap, &cam_cc_pll9_config); - clk_lucid_ole_pll_configure(&cam_cc_pll10, regmap, &cam_cc_pll10_config); - clk_lucid_ole_pll_configure(&cam_cc_pll11, regmap, &cam_cc_pll11_config); - clk_lucid_ole_pll_configure(&cam_cc_pll12, regmap, &cam_cc_pll12_config); - - /* Keep some clocks always-on */ - qcom_branch_set_clk_en(regmap, 0x1419c); /* CAM_CC_GDSC_CLK */ - qcom_branch_set_clk_en(regmap, 0x142cc); /* CAM_CC_SLEEP_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &cam_cc_sm8550_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &cam_cc_sm8550_desc); } static struct platform_driver cam_cc_sm8550_driver = { diff --git a/drivers/clk/qcom/camcc-sm8650.c b/drivers/clk/qcom/camcc-sm8650.c index 0ccd6de8ba78..8b388904f56f 100644 --- a/drivers/clk/qcom/camcc-sm8650.c +++ b/drivers/clk/qcom/camcc-sm8650.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -72,6 +71,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -149,6 +149,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -199,6 +200,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 = rivian_ole_vco, .num_vco = ARRAY_SIZE(rivian_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], @@ -230,6 +232,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -284,6 +287,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -338,6 +342,7 @@ static const struct alpha_pll_config cam_cc_pll5_config = { static struct clk_alpha_pll cam_cc_pll5 = { .offset = 0x5000, + .config = &cam_cc_pll5_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -392,6 +397,7 @@ static const struct alpha_pll_config cam_cc_pll6_config = { static struct clk_alpha_pll cam_cc_pll6 = { .offset = 0x6000, + .config = &cam_cc_pll6_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -446,6 +452,7 @@ static const struct alpha_pll_config cam_cc_pll7_config = { static struct clk_alpha_pll cam_cc_pll7 = { .offset = 0x7000, + .config = &cam_cc_pll7_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -500,6 +507,7 @@ static const struct alpha_pll_config cam_cc_pll8_config = { static struct clk_alpha_pll cam_cc_pll8 = { .offset = 0x8000, + .config = &cam_cc_pll8_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -554,6 +562,7 @@ static const struct alpha_pll_config cam_cc_pll9_config = { static struct clk_alpha_pll cam_cc_pll9 = { .offset = 0x9000, + .config = &cam_cc_pll9_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -631,6 +640,7 @@ static const struct alpha_pll_config cam_cc_pll10_config = { static struct clk_alpha_pll cam_cc_pll10 = { .offset = 0xa000, + .config = &cam_cc_pll10_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -3509,6 +3519,27 @@ static const struct qcom_reset_map cam_cc_sm8650_resets[] = { [CAM_CC_SFE_2_BCR] = { 0x130f4 }, }; +static struct clk_alpha_pll *cam_cc_sm8650_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll5, + &cam_cc_pll6, + &cam_cc_pll7, + &cam_cc_pll8, + &cam_cc_pll9, + &cam_cc_pll10, +}; + +static u32 cam_cc_sm8650_critical_cbcrs[] = { + 0x132ec, /* CAM_CC_GDSC_CLK */ + 0x13308, /* CAM_CC_SLEEP_CLK */ + 0x13314, /* CAM_CC_DRV_XO_CLK */ + 0x13318, /* CAM_CC_DRV_AHB_CLK */ +}; + static const struct regmap_config cam_cc_sm8650_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -3517,6 +3548,13 @@ static const struct regmap_config cam_cc_sm8650_regmap_config = { .fast_io = true, }; +static 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, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_sm8650_critical_cbcrs), +}; + static const struct qcom_cc_desc cam_cc_sm8650_desc = { .config = &cam_cc_sm8650_regmap_config, .clks = cam_cc_sm8650_clocks, @@ -3525,6 +3563,8 @@ static const struct qcom_cc_desc cam_cc_sm8650_desc = { .num_resets = ARRAY_SIZE(cam_cc_sm8650_resets), .gdscs = cam_cc_sm8650_gdscs, .num_gdscs = ARRAY_SIZE(cam_cc_sm8650_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_sm8650_driver_data, }; static const struct of_device_id cam_cc_sm8650_match_table[] = { @@ -3535,46 +3575,7 @@ MODULE_DEVICE_TABLE(of, cam_cc_sm8650_match_table); static int cam_cc_sm8650_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_sm8650_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); - } - - clk_lucid_ole_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); - clk_lucid_ole_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); - clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); - clk_lucid_ole_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); - clk_lucid_ole_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); - clk_lucid_ole_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config); - clk_lucid_ole_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); - clk_lucid_ole_pll_configure(&cam_cc_pll7, regmap, &cam_cc_pll7_config); - clk_lucid_ole_pll_configure(&cam_cc_pll8, regmap, &cam_cc_pll8_config); - clk_lucid_ole_pll_configure(&cam_cc_pll9, regmap, &cam_cc_pll9_config); - clk_lucid_ole_pll_configure(&cam_cc_pll10, regmap, &cam_cc_pll10_config); - - /* Keep clocks always enabled */ - qcom_branch_set_clk_en(regmap, 0x13318); /* CAM_CC_DRV_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0x13314); /* CAM_CC_DRV_XO_CLK */ - qcom_branch_set_clk_en(regmap, 0x132ec); /* CAM_CC_GDSC_CLK */ - qcom_branch_set_clk_en(regmap, 0x13308); /* CAM_CC_SLEEP_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &cam_cc_sm8650_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &cam_cc_sm8650_desc); } static struct platform_driver cam_cc_sm8650_driver = { diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index b73524ae64b1..cbcc1c9fcb34 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -67,6 +66,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -144,6 +144,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -194,6 +195,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 = rivian_ole_vco, .num_vco = ARRAY_SIZE(rivian_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], @@ -225,6 +227,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -279,6 +282,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 = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -333,6 +337,7 @@ static const struct alpha_pll_config cam_cc_pll6_config = { static struct clk_alpha_pll cam_cc_pll6 = { .offset = 0x6000, + .config = &cam_cc_pll6_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -387,6 +392,7 @@ static const struct alpha_pll_config cam_cc_pll8_config = { static struct clk_alpha_pll cam_cc_pll8 = { .offset = 0x8000, + .config = &cam_cc_pll8_config, .vco_table = lucid_ole_vco, .num_vco = ARRAY_SIZE(lucid_ole_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], @@ -2418,6 +2424,21 @@ static const struct qcom_reset_map cam_cc_x1e80100_resets[] = { [CAM_CC_SFE_0_BCR] = { 0x1327c }, }; +static struct clk_alpha_pll *cam_cc_x1e80100_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll6, + &cam_cc_pll8, +}; + +static u32 cam_cc_x1e80100_critical_cbcrs[] = { + 0x13a9c, /* CAM_CC_GDSC_CLK */ + 0x13ab8, /* CAM_CC_SLEEP_CLK */ +}; + static const struct regmap_config cam_cc_x1e80100_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2426,6 +2447,13 @@ static const struct regmap_config cam_cc_x1e80100_regmap_config = { .fast_io = true, }; +static 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, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_x1e80100_critical_cbcrs), +}; + static const struct qcom_cc_desc cam_cc_x1e80100_desc = { .config = &cam_cc_x1e80100_regmap_config, .clks = cam_cc_x1e80100_clocks, @@ -2434,6 +2462,8 @@ static const struct qcom_cc_desc cam_cc_x1e80100_desc = { .num_resets = ARRAY_SIZE(cam_cc_x1e80100_resets), .gdscs = cam_cc_x1e80100_gdscs, .num_gdscs = ARRAY_SIZE(cam_cc_x1e80100_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_x1e80100_driver_data, }; static const struct of_device_id cam_cc_x1e80100_match_table[] = { @@ -2444,40 +2474,7 @@ MODULE_DEVICE_TABLE(of, cam_cc_x1e80100_match_table); static int cam_cc_x1e80100_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_x1e80100_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); - } - - clk_lucid_ole_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); - clk_lucid_ole_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); - clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); - clk_lucid_ole_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); - clk_lucid_ole_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); - clk_lucid_ole_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); - clk_lucid_ole_pll_configure(&cam_cc_pll8, regmap, &cam_cc_pll8_config); - - /* Keep clocks always enabled */ - qcom_branch_set_clk_en(regmap, 0x13a9c); /* CAM_CC_GDSC_CLK */ - qcom_branch_set_clk_en(regmap, 0x13ab8); /* CAM_CC_SLEEP_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &cam_cc_x1e80100_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &cam_cc_x1e80100_desc); } static struct platform_driver cam_cc_x1e80100_driver = { diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index cec0afea8e44..fec6eb376e27 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -63,6 +63,8 @@ #define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE]) #define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC]) +#define GET_PLL_TYPE(pll) (((pll)->regs - clk_alpha_pll_regs[0]) / PLL_OFF_MAX_REGS) + const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [CLK_ALPHA_PLL_TYPE_DEFAULT] = { [PLL_OFF_L_VAL] = 0x04, @@ -788,6 +790,29 @@ static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll, return __clk_alpha_pll_update_latch(pll); } +static void clk_alpha_pll_update_configs(struct clk_alpha_pll *pll, const struct pll_vco *vco, + u32 l, u64 alpha, u32 alpha_width, bool alpha_en) +{ + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); + + if (alpha_width > ALPHA_BITWIDTH) + alpha <<= alpha_width - ALPHA_BITWIDTH; + + if (alpha_width > 32) + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), upper_32_bits(alpha)); + + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), lower_32_bits(alpha)); + + if (vco) { + regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + PLL_VCO_MASK << PLL_VCO_SHIFT, + vco->val << PLL_VCO_SHIFT); + } + + if (alpha_en) + regmap_set_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN); +} + static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate, int (*is_enabled)(struct clk_hw *)) @@ -805,24 +830,7 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; } - regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); - - if (alpha_width > ALPHA_BITWIDTH) - a <<= alpha_width - ALPHA_BITWIDTH; - - if (alpha_width > 32) - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); - - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); - - if (vco) { - regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), - PLL_VCO_MASK << PLL_VCO_SHIFT, - vco->val << PLL_VCO_SHIFT); - } - - regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), - PLL_ALPHA_EN, PLL_ALPHA_EN); + clk_alpha_pll_update_configs(pll, vco, l, a, alpha_width, true); return clk_alpha_pll_update_latch(pll, is_enabled); } @@ -2960,3 +2968,208 @@ const struct clk_ops clk_alpha_pll_regera_ops = { .set_rate = clk_zonda_pll_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regera_ops); + +void qcom_clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap) +{ + const struct clk_init_data *init = pll->clkr.hw.init; + + switch (GET_PLL_TYPE(pll)) { + case CLK_ALPHA_PLL_TYPE_LUCID_OLE: + clk_lucid_ole_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_LUCID_EVO: + clk_lucid_evo_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_TAYCAN_ELU: + clk_taycan_elu_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_RIVIAN_EVO: + clk_rivian_evo_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_TRION: + clk_trion_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_HUAYRA_2290: + clk_huayra_2290_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_FABIA: + clk_fabia_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_AGERA: + clk_agera_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_PONGO_ELU: + clk_pongo_elu_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_ZONDA: + case CLK_ALPHA_PLL_TYPE_ZONDA_OLE: + clk_zonda_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_STROMER: + case CLK_ALPHA_PLL_TYPE_STROMER_PLUS: + clk_stromer_pll_configure(pll, regmap, pll->config); + break; + case CLK_ALPHA_PLL_TYPE_DEFAULT: + case CLK_ALPHA_PLL_TYPE_DEFAULT_EVO: + case CLK_ALPHA_PLL_TYPE_HUAYRA: + case CLK_ALPHA_PLL_TYPE_HUAYRA_APSS: + case CLK_ALPHA_PLL_TYPE_BRAMMO: + case CLK_ALPHA_PLL_TYPE_BRAMMO_EVO: + clk_alpha_pll_configure(pll, regmap, pll->config); + break; + default: + WARN(1, "%s: invalid pll type\n", init->name); + break; + } +} +EXPORT_SYMBOL_GPL(qcom_clk_alpha_pll_configure); + +static int clk_alpha_pll_slew_update(struct clk_alpha_pll *pll) +{ + u32 val; + int ret; + + regmap_set_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE); + regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); + + ret = wait_for_pll_update(pll); + if (ret) + return ret; + /* + * Hardware programming mandates a wait of at least 570ns before polling the LOCK + * detect bit. Have a delay of 1us just to be safe. + */ + udelay(1); + + return wait_for_pll_enable_lock(pll); +} + +static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + const struct pll_vco *curr_vco, *vco; + unsigned long freq_hz; + u64 a; + u32 l; + + freq_hz = alpha_pll_round_rate(rate, parent_rate, &l, &a, ALPHA_REG_BITWIDTH); + if (freq_hz != rate) { + pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); + return -EINVAL; + } + + curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); + if (!curr_vco) { + pr_err("alpha pll: not in a valid vco range\n"); + return -EINVAL; + } + + vco = alpha_pll_find_vco(pll, freq_hz); + if (!vco) { + pr_err("alpha pll: not in a valid vco range\n"); + return -EINVAL; + } + + /* + * Dynamic pll update will not support switching frequencies across + * vco ranges. In those cases fall back to normal alpha set rate. + */ + if (curr_vco->val != vco->val) + return clk_alpha_pll_set_rate(hw, rate, parent_rate); + + clk_alpha_pll_update_configs(pll, NULL, l, a, ALPHA_REG_BITWIDTH, false); + + /* Ensure that the write above goes before slewing the PLL */ + mb(); + + if (clk_hw_is_enabled(hw)) + return clk_alpha_pll_slew_update(pll); + + return 0; +} + +/* + * Slewing plls should be bought up at frequency which is in the middle of the + * desired VCO range. So after bringing up the pll at calibration freq, set it + * back to desired frequency(that was set by previous clk_set_rate). + */ +static int clk_alpha_pll_calibrate(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + struct clk_hw *parent; + const struct pll_vco *vco; + unsigned long calibration_freq, freq_hz; + u64 a; + u32 l; + int rc; + + parent = clk_hw_get_parent(hw); + if (!parent) { + pr_err("alpha pll: no valid parent found\n"); + return -EINVAL; + } + + vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); + if (!vco) { + pr_err("alpha pll: not in a valid vco range\n"); + return -EINVAL; + } + + /* + * As during slewing plls vco_sel won't be allowed to change, vco table + * should have only one entry table, i.e. index = 0, find the + * calibration frequency. + */ + calibration_freq = (pll->vco_table[0].min_freq + pll->vco_table[0].max_freq) / 2; + + freq_hz = alpha_pll_round_rate(calibration_freq, clk_hw_get_rate(parent), + &l, &a, ALPHA_REG_BITWIDTH); + if (freq_hz != calibration_freq) { + pr_err("alpha_pll: call clk_set_rate with rounded rates!\n"); + return -EINVAL; + } + + clk_alpha_pll_update_configs(pll, vco, l, a, ALPHA_REG_BITWIDTH, false); + + /* Bringup the pll at calibration frequency */ + rc = clk_alpha_pll_enable(hw); + if (rc) { + pr_err("alpha pll calibration failed\n"); + return rc; + } + + /* + * PLL is already running at calibration frequency. + * So slew pll to the previously set frequency. + */ + freq_hz = alpha_pll_round_rate(clk_hw_get_rate(hw), + clk_hw_get_rate(parent), &l, &a, ALPHA_REG_BITWIDTH); + + pr_debug("pll %s: setting back to required rate %lu, freq_hz %ld\n", + clk_hw_get_name(hw), clk_hw_get_rate(hw), freq_hz); + + clk_alpha_pll_update_configs(pll, NULL, l, a, ALPHA_REG_BITWIDTH, true); + + return clk_alpha_pll_slew_update(pll); +} + +static int clk_alpha_pll_slew_enable(struct clk_hw *hw) +{ + int rc; + + rc = clk_alpha_pll_calibrate(hw); + if (rc) + return rc; + + return clk_alpha_pll_enable(hw); +} + +const struct clk_ops clk_alpha_pll_slew_ops = { + .enable = clk_alpha_pll_slew_enable, + .disable = clk_alpha_pll_disable, + .recalc_rate = clk_alpha_pll_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, + .set_rate = clk_alpha_pll_slew_set_rate, +}; +EXPORT_SYMBOL(clk_alpha_pll_slew_ops); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 79aca8525262..ff41aeab0ab9 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -81,6 +81,7 @@ struct pll_vco { * struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers * @regs: alpha pll register map (see @clk_alpha_pll_regs) + * @config: array of pll settings * @vco_table: array of VCO settings * @num_vco: number of VCO settings in @vco_table * @flags: bitmask to indicate features supported by the hardware @@ -90,6 +91,7 @@ struct clk_alpha_pll { u32 offset; const u8 *regs; + const struct alpha_pll_config *config; const struct pll_vco *vco_table; size_t num_vco; #define SUPPORTS_OFFLINE_REQ BIT(0) @@ -204,6 +206,7 @@ extern const struct clk_ops clk_alpha_pll_rivian_evo_ops; #define clk_alpha_pll_postdiv_rivian_evo_ops clk_alpha_pll_postdiv_fabia_ops extern const struct clk_ops clk_alpha_pll_regera_ops; +extern const struct clk_ops clk_alpha_pll_slew_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); @@ -237,5 +240,6 @@ void clk_stromer_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); void clk_regera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); +void qcom_clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap); #endif diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index ccc112c21667..be0145631197 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -351,15 +351,15 @@ static int clk_rpm_set_rate(struct clk_hw *hw, return 0; } -static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_rpm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { /* * RPM handles rate rounding and we don't have a way to * know what the rate will be, so just return whatever * rate is requested. */ - return rate; + return 0; } static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, @@ -383,7 +383,7 @@ static const struct clk_ops clk_rpm_xo_ops = { static const struct clk_ops clk_rpm_fixed_ops = { .prepare = clk_rpm_fixed_prepare, .unprepare = clk_rpm_fixed_unprepare, - .round_rate = clk_rpm_round_rate, + .determine_rate = clk_rpm_determine_rate, .recalc_rate = clk_rpm_recalc_rate, }; @@ -391,7 +391,7 @@ static const struct clk_ops clk_rpm_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, .set_rate = clk_rpm_set_rate, - .round_rate = clk_rpm_round_rate, + .determine_rate = clk_rpm_determine_rate, .recalc_rate = clk_rpm_recalc_rate, }; diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 00fb3e53a388..1496fb3de4be 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -321,10 +321,10 @@ static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_rpmh_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - return rate; + return 0; } static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, @@ -339,7 +339,7 @@ static const struct clk_ops clk_rpmh_bcm_ops = { .prepare = clk_rpmh_bcm_prepare, .unprepare = clk_rpmh_bcm_unprepare, .set_rate = clk_rpmh_bcm_set_rate, - .round_rate = clk_rpmh_round_rate, + .determine_rate = clk_rpmh_determine_rate, .recalc_rate = clk_rpmh_bcm_recalc_rate, }; @@ -386,6 +386,8 @@ DEFINE_CLK_RPMH_VRM(clk6, _a2, "clka6", 2); DEFINE_CLK_RPMH_VRM(clk7, _a2, "clka7", 2); DEFINE_CLK_RPMH_VRM(clk8, _a2, "clka8", 2); +DEFINE_CLK_RPMH_VRM(clk7, _a4, "clka7", 4); + DEFINE_CLK_RPMH_VRM(div_clk1, _div2, "divclka1", 2); DEFINE_CLK_RPMH_BCM(ce, "CE0"); @@ -541,6 +543,29 @@ static const struct clk_rpmh_desc clk_rpmh_sc8180x = { .num_clks = ARRAY_SIZE(sc8180x_rpmh_clocks), }; +static struct clk_hw *milos_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div4.hw, + [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div4_ao.hw, + [RPMH_LN_BB_CLK2] = &clk_rpmh_clk7_a4.hw, + [RPMH_LN_BB_CLK2_A] = &clk_rpmh_clk7_a4_ao.hw, + /* + * RPMH_LN_BB_CLK3(_A) and RPMH_LN_BB_CLK4(_A) are marked as optional + * downstream, but do not exist in cmd-db on SM7635, so skip them. + */ + [RPMH_RF_CLK1] = &clk_rpmh_clk1_a1.hw, + [RPMH_RF_CLK1_A] = &clk_rpmh_clk1_a1_ao.hw, + [RPMH_RF_CLK2] = &clk_rpmh_clk2_a1.hw, + [RPMH_RF_CLK2_A] = &clk_rpmh_clk2_a1_ao.hw, + [RPMH_RF_CLK3] = &clk_rpmh_clk3_a1.hw, + [RPMH_RF_CLK3_A] = &clk_rpmh_clk3_a1_ao.hw, + [RPMH_IPA_CLK] = &clk_rpmh_ipa.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_milos = { + .clks = milos_rpmh_clocks, + .num_clks = ARRAY_SIZE(milos_rpmh_clocks), +}; + static struct clk_hw *sm8250_rpmh_clocks[] = { [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div2.hw, [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div2_ao.hw, @@ -943,6 +968,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) } static const struct of_device_id clk_rpmh_match_table[] = { + { .compatible = "qcom,milos-rpmh-clk", .data = &clk_rpmh_milos}, { .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/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 3fbaa646286f..3bf6df3884a5 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -370,15 +370,15 @@ static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_smd_rpm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { /* * RPM handles rate rounding and we don't have a way to * know what the rate will be, so just return whatever * rate is requested. */ - return rate; + return 0; } static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, @@ -427,7 +427,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, .set_rate = clk_smd_rpm_set_rate, - .round_rate = clk_smd_rpm_round_rate, + .determine_rate = clk_smd_rpm_determine_rate, .recalc_rate = clk_smd_rpm_recalc_rate, }; diff --git a/drivers/clk/qcom/clk-spmi-pmic-div.c b/drivers/clk/qcom/clk-spmi-pmic-div.c index 41a0a4f3b4fb..3e2ac6745325 100644 --- a/drivers/clk/qcom/clk-spmi-pmic-div.c +++ b/drivers/clk/qcom/clk-spmi-pmic-div.c @@ -112,16 +112,18 @@ static void clk_spmi_pmic_div_disable(struct clk_hw *hw) spin_unlock_irqrestore(&clkdiv->lock, flags); } -static long clk_spmi_pmic_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_spmi_pmic_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { unsigned int div, div_factor; - div = DIV_ROUND_UP(*parent_rate, rate); + div = DIV_ROUND_UP(req->best_parent_rate, req->rate); div_factor = div_to_div_factor(div); div = div_factor_to_div(div_factor); - return *parent_rate / div; + req->rate = req->best_parent_rate / div; + + return 0; } static unsigned long @@ -169,7 +171,7 @@ static const struct clk_ops clk_spmi_pmic_div_ops = { .disable = clk_spmi_pmic_div_disable, .set_rate = clk_spmi_pmic_div_set_rate, .recalc_rate = clk_spmi_pmic_div_recalc_rate, - .round_rate = clk_spmi_pmic_div_round_rate, + .determine_rate = clk_spmi_pmic_div_determine_rate, }; struct spmi_pmic_div_clk_cc { diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 9e3380fd7181..37c3008e6c1b 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -9,10 +9,13 @@ #include #include #include +#include #include #include #include "common.h" +#include "clk-alpha-pll.h" +#include "clk-branch.h" #include "clk-rcg.h" #include "clk-regmap.h" #include "reset.h" @@ -284,6 +287,40 @@ static int qcom_cc_icc_register(struct device *dev, desc->num_icc_hws, icd); } +static int qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data *data, + struct regmap *regmap) +{ + const struct clk_init_data *init; + struct clk_alpha_pll *pll; + int i; + + for (i = 0; i < data->num_alpha_plls; i++) { + pll = data->alpha_plls[i]; + init = pll->clkr.hw.init; + + if (!pll->config || !pll->regs) { + pr_err("%s: missing pll config or regs\n", init->name); + return -EINVAL; + } + + qcom_clk_alpha_pll_configure(pll, regmap); + } + + return 0; +} + +static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_driver_data *data, + struct regmap *regmap) +{ + int i; + + for (i = 0; i < data->num_clk_cbcrs; i++) + qcom_branch_set_clk_en(regmap, data->clk_cbcrs[i]); + + if (data->clk_regs_configure) + data->clk_regs_configure(dev, regmap); +} + int qcom_cc_really_probe(struct device *dev, const struct qcom_cc_desc *desc, struct regmap *regmap) { @@ -304,6 +341,24 @@ int qcom_cc_really_probe(struct device *dev, if (ret < 0 && ret != -EEXIST) return ret; + if (desc->use_rpm) { + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + } + + if (desc->driver_data) { + ret = qcom_cc_clk_pll_configure(desc->driver_data, regmap); + if (ret) + goto put_rpm; + + qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap); + } + reset = &cc->reset; reset->rcdev.of_node = dev->of_node; reset->rcdev.ops = &qcom_reset_ops; @@ -314,23 +369,35 @@ int qcom_cc_really_probe(struct device *dev, ret = devm_reset_controller_register(dev, &reset->rcdev); if (ret) - return ret; + goto put_rpm; if (desc->gdscs && desc->num_gdscs) { scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL); - if (!scd) - return -ENOMEM; + if (!scd) { + ret = -ENOMEM; + goto put_rpm; + } scd->dev = dev; scd->scs = desc->gdscs; scd->num = desc->num_gdscs; scd->pd_list = cc->pd_list; ret = gdsc_register(scd, &reset->rcdev, regmap); if (ret) - return ret; + goto put_rpm; ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister, scd); if (ret) - return ret; + goto put_rpm; + } + + if (desc->driver_data && + desc->driver_data->dfs_rcgs && + desc->driver_data->num_dfs_rcgs) { + ret = qcom_cc_register_rcg_dfs(regmap, + desc->driver_data->dfs_rcgs, + desc->driver_data->num_dfs_rcgs); + if (ret) + goto put_rpm; } cc->rclks = rclks; @@ -341,7 +408,7 @@ int qcom_cc_really_probe(struct device *dev, for (i = 0; i < num_clk_hws; i++) { ret = devm_clk_hw_register(dev, clk_hws[i]); if (ret) - return ret; + goto put_rpm; } for (i = 0; i < num_clks; i++) { @@ -350,14 +417,20 @@ int qcom_cc_really_probe(struct device *dev, ret = devm_clk_register_regmap(dev, rclks[i]); if (ret) - return ret; + goto put_rpm; } ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc); if (ret) - return ret; + goto put_rpm; - return qcom_cc_icc_register(dev, desc); + ret = qcom_cc_icc_register(dev, desc); + +put_rpm: + if (desc->use_rpm) + pm_runtime_put(dev); + + return ret; } EXPORT_SYMBOL_GPL(qcom_cc_really_probe); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 7ace5d7f5836..953c91f7b145 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -25,6 +25,16 @@ struct qcom_icc_hws_data { int clk_id; }; +struct qcom_cc_driver_data { + struct clk_alpha_pll **alpha_plls; + size_t num_alpha_plls; + u32 *clk_cbcrs; + size_t num_clk_cbcrs; + const struct clk_rcg_dfs_data *dfs_rcgs; + size_t num_dfs_rcgs; + void (*clk_regs_configure)(struct device *dev, struct regmap *regmap); +}; + struct qcom_cc_desc { const struct regmap_config *config; struct clk_regmap **clks; @@ -38,6 +48,8 @@ struct qcom_cc_desc { const struct qcom_icc_hws_data *icc_hws; size_t num_icc_hws; unsigned int icc_first_node_id; + bool use_rpm; + struct qcom_cc_driver_data *driver_data; }; /** diff --git a/drivers/clk/qcom/dispcc-milos.c b/drivers/clk/qcom/dispcc-milos.c new file mode 100644 index 000000000000..602d3a498d33 --- /dev/null +++ b/drivers/clk/qcom/dispcc-milos.c @@ -0,0 +1,974 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss + */ + +#include +#include +#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 "reset.h" +#include "gdsc.h" + +/* Need to match the order of clocks in DT binding */ +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_AHB_CLK, + DT_GCC_DISP_GPLL0_CLK, + DT_DSI0_PHY_PLL_OUT_BYTECLK, + DT_DSI0_PHY_PLL_OUT_DSICLK, + DT_DP0_PHY_PLL_LINK_CLK, + DT_DP0_PHY_PLL_VCO_DIV_CLK, +}; + +#define DISP_CC_MISC_CMD 0xF000 + +enum { + P_BI_TCXO, + P_DISP_CC_PLL0_OUT_EVEN, + P_DISP_CC_PLL0_OUT_MAIN, + P_DP0_PHY_PLL_LINK_CLK, + P_DP0_PHY_PLL_VCO_DIV_CLK, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_GCC_DISP_GPLL0_CLK, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +/* 257.142858 MHz Configuration */ +static const struct alpha_pll_config disp_cc_pll0_config = { + .l = 0xd, + .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 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 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 }, +}; + +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 }, +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_DP0_PHY_PLL_LINK_CLK, 1 }, + { P_DP0_PHY_PLL_VCO_DIV_CLK, 2 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP0_PHY_PLL_LINK_CLK }, + { .index = DT_DP0_PHY_PLL_VCO_DIV_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_GCC_DISP_GPLL0_CLK, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .index = DT_GCC_DISP_GPLL0_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_DP0_PHY_PLL_LINK_CLK, 1 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP0_PHY_PLL_LINK_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 2 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_BYTECLK }, +}; + +static const struct parent_map disp_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_GCC_DISP_GPLL0_CLK, 4 }, + { P_DISP_CC_PLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll0.clkr.hw }, + { .index = DT_GCC_DISP_GPLL0_CLK }, + { .hw = &disp_cc_pll0.clkr.hw }, +}; + +static const struct parent_map disp_cc_parent_map_7[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_7_ao[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_GCC_DISP_GPLL0_CLK, 8, 0, 0), + F(75000000, P_GCC_DISP_GPLL0_CLK, 4, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = { + .cmd_rcgr = 0x8130, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .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_3, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x8098, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .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, + .ops = &clk_byte2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dptx0_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dptx0_aux_clk_src = { + .cmd_rcgr = 0x8118, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dptx0_aux_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 = 0x80cc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_link_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_mdss_dptx0_pixel0_clk_src = { + .cmd_rcgr = 0x80e8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_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_dptx0_pixel1_clk_src = { + .cmd_rcgr = 0x8100, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_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 const struct freq_tbl ftbl_disp_cc_mdss_esc0_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(12800000, P_BI_TCXO, 1.5, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x80b4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_esc0_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_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(100000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(200000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(342000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(402000000, 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(630000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x8068, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .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_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_pclk0_clk_src = { + .cmd_rcgr = 0x8050, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .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, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x8080, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dptx0_aux_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_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_sleep_clk_src = { + .cmd_rcgr = 0xe054, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_7, + .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_7_ao, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_7_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_xo_clk_src = { + .cmd_rcgr = 0xe034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dptx0_aux_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_xo_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_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x80b0, + .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, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dptx0_link_div_clk_src = { + .reg = 0x80e4, + .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_branch disp_cc_mdss_accu_clk = { + .halt_reg = 0xe050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xe050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_accu_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 = 0xa020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa020, + .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 = 0x804c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x804c, + .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 = 0x8024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8024, + .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 = 0x8028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8028, + .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_dptx0_aux_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_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 = 0x803c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x803c, + .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 = 0x8030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8030, + .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 = 0x8038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8038, + .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 = 0x8040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8040, + .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 = 0x8044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8044, + .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 = 0x8034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8034, + .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_esc0_clk = { + .halt_reg = 0x802c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x802c, + .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_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 = 0x8008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8008, + .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_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut1_clk = { + .halt_reg = 0xa010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa010, + .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 = 0x8014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8014, + .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_rscc_ahb_clk = { + .halt_reg = 0xc00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rscc_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_rscc_vsync_clk = { + .halt_reg = 0xc008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rscc_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_mdss_vsync1_clk = { + .halt_reg = 0xa01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa01c, + .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 = 0x8020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8020, + .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 gdsc disp_cc_mdss_core_gdsc = { + .gdscr = 0x9000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "disp_cc_mdss_core_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | HW_CTRL | RETAIN_FF_ENABLE, +}; + +static struct gdsc disp_cc_mdss_core_int2_gdsc = { + .gdscr = 0xb000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "disp_cc_mdss_core_int2_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | HW_CTRL | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *disp_cc_milos_clocks[] = { + [DISP_CC_MDSS_ACCU_CLK] = &disp_cc_mdss_accu_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_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_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, + [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_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_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, + [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_clk.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_PLL0] = &disp_cc_pll0.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_milos_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_milos_gdscs[] = { + [DISP_CC_MDSS_CORE_GDSC] = &disp_cc_mdss_core_gdsc, + [DISP_CC_MDSS_CORE_INT2_GDSC] = &disp_cc_mdss_core_int2_gdsc, +}; + +static struct clk_alpha_pll *disp_cc_milos_plls[] = { + &disp_cc_pll0, +}; + +static u32 disp_cc_milos_critical_cbcrs[] = { + 0xe06c, /* DISP_CC_SLEEP_CLK */ + 0xe04c, /* DISP_CC_XO_CLK */ +}; + +static const struct regmap_config disp_cc_milos_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x11008, + .fast_io = true, +}; + +static void disp_cc_milos_clk_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* Enable clock gating for MDP clocks */ + regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10); +} + + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(disp_cc_milos_critical_cbcrs), + .clk_regs_configure = disp_cc_milos_clk_regs_configure, +}; + +static struct qcom_cc_desc disp_cc_milos_desc = { + .config = &disp_cc_milos_regmap_config, + .clks = disp_cc_milos_clocks, + .num_clks = ARRAY_SIZE(disp_cc_milos_clocks), + .resets = disp_cc_milos_resets, + .num_resets = ARRAY_SIZE(disp_cc_milos_resets), + .gdscs = disp_cc_milos_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_milos_gdscs), + .use_rpm = true, + .driver_data = &disp_cc_milos_driver_data, +}; + +static const struct of_device_id disp_cc_milos_match_table[] = { + { .compatible = "qcom,milos-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_milos_match_table); + +static int disp_cc_milos_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &disp_cc_milos_desc); +} + +static struct platform_driver disp_cc_milos_driver = { + .probe = disp_cc_milos_probe, + .driver = { + .name = "disp_cc-milos", + .of_match_table = disp_cc_milos_match_table, + }, +}; + +module_platform_driver(disp_cc_milos_driver); + +MODULE_DESCRIPTION("QTI DISP_CC Milos Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/dispcc-qcs615.c b/drivers/clk/qcom/dispcc-qcs615.c new file mode 100644 index 000000000000..4a6d78466098 --- /dev/null +++ b/drivers/clk/qcom/dispcc-qcs615.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#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, + DT_DSI0_PHY_PLL_OUT_BYTECLK, + DT_DSI0_PHY_PLL_OUT_DSICLK, + DT_DSI1_PHY_PLL_OUT_DSICLK, + DT_DP_PHY_PLL_LINK_CLK, + DT_DP_PHY_PLL_VCO_DIV_CLK, +}; + +enum { + P_BI_TCXO, + P_DISP_CC_PLL0_OUT_MAIN, + P_DP_PHY_PLL_LINK_CLK, + P_DP_PHY_PLL_VCO_DIV_CLK, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_DSI1_PHY_PLL_OUT_DSICLK, + P_GPLL0_OUT_MAIN, +}; + +static const struct pll_vco disp_cc_pll_vco[] = { + { 500000000, 1000000000, 2 }, +}; + +/* 576MHz configuration VCO - 2 */ +static struct alpha_pll_config disp_cc_pll0_config = { + .l = 0x1e, + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, +}; + +static struct clk_alpha_pll disp_cc_pll0 = { + .offset = 0x0, + .config = &disp_cc_pll0_config, + .vco_table = disp_cc_pll_vco, + .num_vco = ARRAY_SIZE(disp_cc_pll_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .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_slew_ops, + }, + }, +}; + +static const struct parent_map disp_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_DP_PHY_PLL_LINK_CLK, 1 }, + { P_DP_PHY_PLL_VCO_DIV_CLK, 2 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP_PHY_PLL_LINK_CLK }, + { .index = DT_DP_PHY_PLL_VCO_DIV_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_BYTECLK }, +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_MAIN, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll0.clkr.hw }, + { .index = DT_GPLL0 }, +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_GPLL0 }, +}; + +static const struct parent_map disp_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 2 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_DSICLK }, + { .index = DT_DSI1_PHY_PLL_OUT_DSICLK }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(75000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = { + .cmd_rcgr = 0x2170, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .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_4, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x20c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte0_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_byte2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux1_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_aux_clk_src = { + .cmd_rcgr = 0x2158, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_crypto_clk_src = { + .cmd_rcgr = 0x2110, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_crypto_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = { + .cmd_rcgr = 0x20f4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_link_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_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel1_clk_src = { + .cmd_rcgr = 0x2140, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_pixel1_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_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel_clk_src = { + .cmd_rcgr = 0x2128, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_pixel_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_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x20dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(192000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(256000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(307000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x2078, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .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_3, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { + .cmd_rcgr = 0x2060, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk0_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_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_rot_clk_src = { + .cmd_rcgr = 0x2090, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rot_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_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x20a8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x20d8, + .shift = 0, + .width = 2, + .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_ro_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dp_link_div_clk_src = { + .reg = 0x210c, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_link_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb_clk = { + .halt_reg = 0x2048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2048, + .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 = 0x2024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2024, + .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 = 0x2028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2028, + .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_dp_aux_clk = { + .halt_reg = 0x2044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_crypto_clk = { + .halt_reg = 0x2038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_crypto_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_clk = { + .halt_reg = 0x2030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_link_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_intf_clk = { + .halt_reg = 0x2034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_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_dp_pixel1_clk = { + .halt_reg = 0x2040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_pixel1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_pixel1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel_clk = { + .halt_reg = 0x203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x203c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dp_pixel_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dp_pixel_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 = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .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_mdp_clk = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2008, + .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_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut_clk = { + .halt_reg = 0x2018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2018, + .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 = 0x4004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x4004, + .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 = 0x2004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2004, + .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_rot_clk = { + .halt_reg = 0x2010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rot_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_rot_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_ahb_clk = { + .halt_reg = 0x400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x400c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rscc_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_rscc_vsync_clk = { + .halt_reg = 0x4008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_rscc_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_mdss_vsync_clk = { + .halt_reg = 0x2020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2020, + .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 gdsc mdss_core_gdsc = { + .gdscr = 0x3000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "mdss_core_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *disp_cc_qcs615_clocks[] = { + [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_DP_AUX_CLK] = &disp_cc_mdss_dp_aux_clk.clkr, + [DISP_CC_MDSS_DP_AUX_CLK_SRC] = &disp_cc_mdss_dp_aux_clk_src.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK] = &disp_cc_mdss_dp_crypto_clk.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK_SRC] = &disp_cc_mdss_dp_crypto_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_CLK] = &disp_cc_mdss_dp_link_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK_SRC] = &disp_cc_mdss_dp_link_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_DIV_CLK_SRC] = &disp_cc_mdss_dp_link_div_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_INTF_CLK] = &disp_cc_mdss_dp_link_intf_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK] = &disp_cc_mdss_dp_pixel1_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK_SRC] = &disp_cc_mdss_dp_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK] = &disp_cc_mdss_dp_pixel_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK_SRC] = &disp_cc_mdss_dp_pixel_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_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_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_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr, + [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr, + [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, + [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_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_PLL0] = &disp_cc_pll0.clkr, +}; + +static struct gdsc *disp_cc_qcs615_gdscs[] = { + [MDSS_CORE_GDSC] = &mdss_core_gdsc, +}; + +static struct clk_alpha_pll *disp_cc_qcs615_plls[] = { + &disp_cc_pll0, +}; + +static u32 disp_cc_qcs615_critical_cbcrs[] = { + 0x6054, /* DISP_CC_XO_CLK */ +}; + +static const struct regmap_config disp_cc_qcs615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(disp_cc_qcs615_critical_cbcrs), +}; + +static const struct qcom_cc_desc disp_cc_qcs615_desc = { + .config = &disp_cc_qcs615_regmap_config, + .clks = disp_cc_qcs615_clocks, + .num_clks = ARRAY_SIZE(disp_cc_qcs615_clocks), + .gdscs = disp_cc_qcs615_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_qcs615_gdscs), + .driver_data = &disp_cc_qcs615_driver_data, +}; + +static const struct of_device_id disp_cc_qcs615_match_table[] = { + { .compatible = "qcom,qcs615-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_qcs615_match_table); + +static int disp_cc_qcs615_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &disp_cc_qcs615_desc); +} + +static struct platform_driver disp_cc_qcs615_driver = { + .probe = disp_cc_qcs615_probe, + .driver = { + .name = "dispcc-qcs615", + .of_match_table = disp_cc_qcs615_match_table, + }, +}; + +module_platform_driver(disp_cc_qcs615_driver); + +MODULE_DESCRIPTION("QTI DISPCC QCS615 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/dispcc-sm8750.c b/drivers/clk/qcom/dispcc-sm8750.c index 877b40d50e6f..ca09da111a50 100644 --- a/drivers/clk/qcom/dispcc-sm8750.c +++ b/drivers/clk/qcom/dispcc-sm8750.c @@ -393,7 +393,7 @@ static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { .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, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_byte2_ops, }, }; @@ -408,7 +408,7 @@ static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { .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, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_byte2_ops, }, }; @@ -712,7 +712,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { .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, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_pixel_ops, }, }; @@ -727,7 +727,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { .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, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_pixel_ops, }, }; @@ -742,7 +742,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk2_clk_src = { .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, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_pixel_ops, }, }; diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index d38628b52268..5ac44cfb53ce 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -125,21 +125,23 @@ static const struct clk_fepll_vco gcc_fepll_vco = { * It looks up the frequency table and returns the next higher frequency * supported in hardware. */ -static long clk_cpu_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *p_rate) +static int clk_cpu_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_fepll *pll = to_clk_fepll(hw); struct clk_hw *p_hw; const struct freq_tbl *f; - f = qcom_find_freq(pll->freq_tbl, rate); + f = qcom_find_freq(pll->freq_tbl, req->rate); if (!f) return -EINVAL; p_hw = clk_hw_get_parent_by_index(hw, f->src); - *p_rate = clk_hw_get_rate(p_hw); + req->best_parent_rate = clk_hw_get_rate(p_hw); - return f->freq; + req->rate = f->freq; + + return 0; }; /* @@ -205,7 +207,7 @@ clk_cpu_div_recalc_rate(struct clk_hw *hw, }; static const struct clk_ops clk_regmap_cpu_div_ops = { - .round_rate = clk_cpu_div_round_rate, + .determine_rate = clk_cpu_div_determine_rate, .set_rate = clk_cpu_div_set_rate, .recalc_rate = clk_cpu_div_recalc_rate, }; diff --git a/drivers/clk/qcom/gcc-ipq5018.c b/drivers/clk/qcom/gcc-ipq5018.c index 70f5dcb96700..dcda2be8c1a5 100644 --- a/drivers/clk/qcom/gcc-ipq5018.c +++ b/drivers/clk/qcom/gcc-ipq5018.c @@ -1371,7 +1371,7 @@ static struct clk_branch gcc_xo_clk = { &gcc_xo_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -3660,7 +3660,7 @@ static const struct qcom_reset_map gcc_ipq5018_resets[] = { [GCC_WCSS_AXI_S_ARES] = { 0x59008, 6 }, [GCC_WCSS_Q6_BCR] = { 0x18004, 0 }, [GCC_WCSSAON_RESET] = { 0x59010, 0}, - [GCC_GEPHY_MISC_ARES] = { 0x56004, 0 }, + [GCC_GEPHY_MISC_ARES] = { 0x56004, .bitmask = GENMASK(3, 0) }, }; static const struct of_device_id gcc_ipq5018_match_table[] = { diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index 7258ba5c0900..1329ea28d703 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -1895,10 +1895,10 @@ static const struct freq_conf ftbl_nss_port6_tx_clk_src_125[] = { static const struct freq_multi_tbl ftbl_nss_port6_tx_clk_src[] = { FMS(19200000, P_XO, 1, 0, 0), FM(25000000, ftbl_nss_port6_tx_clk_src_25), - FMS(78125000, P_UNIPHY1_RX, 4, 0, 0), + FMS(78125000, P_UNIPHY2_TX, 4, 0, 0), FM(125000000, ftbl_nss_port6_tx_clk_src_125), - FMS(156250000, P_UNIPHY1_RX, 2, 0, 0), - FMS(312500000, P_UNIPHY1_RX, 1, 0, 0), + FMS(156250000, P_UNIPHY2_TX, 2, 0, 0), + FMS(312500000, P_UNIPHY2_TX, 1, 0, 0), { } }; diff --git a/drivers/clk/qcom/gcc-milos.c b/drivers/clk/qcom/gcc-milos.c new file mode 100644 index 000000000000..c9d61b05bafa --- /dev/null +++ b/drivers/clk/qcom/gcc-milos.c @@ -0,0 +1,3225 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.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_SLEEP_CLK, + DT_PCIE_0_PIPE, + DT_PCIE_1_PIPE, + DT_UFS_PHY_RX_SYMBOL_0, + DT_UFS_PHY_RX_SYMBOL_1, + DT_UFS_PHY_TX_SYMBOL_0, + DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE, +}; + +enum { + P_BI_TCXO, + P_GCC_GPLL0_OUT_EVEN, + P_GCC_GPLL0_OUT_MAIN, + P_GCC_GPLL0_OUT_ODD, + P_GCC_GPLL2_OUT_MAIN, + P_GCC_GPLL4_OUT_MAIN, + P_GCC_GPLL6_OUT_MAIN, + P_GCC_GPLL7_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_gpll2 = { + .offset = 0x2000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "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 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_gpll6 = { + .offset = 0x6000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll6", + .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_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_GPLL6_OUT_MAIN, 2 }, + { P_GCC_GPLL7_OUT_MAIN, 3 }, + { 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_gpll6.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 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_PCIE_0_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_PCIE_0_PIPE }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_PCIE_1_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_PCIE_1_PIPE }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { 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_8[] = { + { .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_9[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL6_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_ODD, 3 }, + { P_GCC_GPLL2_OUT_MAIN, 4 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_9[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll6.clkr.hw }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll2.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL6_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_ODD, 3 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_10[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll6.clkr.hw }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_11[] = { + { 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_11[] = { + { .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_12[] = { + { P_UFS_PHY_RX_SYMBOL_0_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_12[] = { + { .index = DT_UFS_PHY_RX_SYMBOL_0 }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_UFS_PHY_RX_SYMBOL_1_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_13[] = { + { .index = DT_UFS_PHY_RX_SYMBOL_1 }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_14[] = { + { P_UFS_PHY_TX_SYMBOL_0_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_14[] = { + { .index = DT_UFS_PHY_TX_SYMBOL_0 }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_15[] = { + { P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_15[] = { + { .index = DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE }, + { .index = DT_BI_TCXO }, +}; + +static struct clk_regmap_mux gcc_pcie_0_pipe_clk_src = { + .reg = 0x6b070, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_6, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_pcie_1_pipe_clk_src = { + .reg = 0x9006c, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_7, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_ufs_phy_rx_symbol_0_clk_src = { + .reg = 0x77064, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_12, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_ufs_phy_rx_symbol_1_clk_src = { + .reg = 0x770e0, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_13, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk_src", + .parent_data = gcc_parent_data_13, + .num_parents = ARRAY_SIZE(gcc_parent_data_13), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_ufs_phy_tx_symbol_0_clk_src = { + .reg = 0x77054, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_14, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk_src", + .parent_data = gcc_parent_data_14, + .num_parents = ARRAY_SIZE(gcc_parent_data_14), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x3906c, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_15, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = gcc_parent_data_15, + .num_parents = ARRAY_SIZE(gcc_parent_data_15), + .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, + .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), + .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, + .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), + .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, + .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), + .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 = 0x6b074, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_phy_rchng_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_phy_rchng_clk_src = { + .cmd_rcgr = 0x6b058, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { + .cmd_rcgr = 0x90070, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_rchng_clk_src = { + .cmd_rcgr = 0x90054, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .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), + .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, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_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(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap0_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap0_qspi_ref_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_wrap0_qspi_ref_clk_src = { + .cmd_rcgr = 0x18768, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_qspi_ref_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_qspi_ref_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_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(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_wrap0_s0_clk_src_init = { + .name = "gcc_qupv3_wrap0_s0_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_wrap0_s0_clk_src = { + .cmd_rcgr = 0x18010, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { + .name = "gcc_qupv3_wrap0_s1_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_wrap0_s1_clk_src = { + .cmd_rcgr = 0x18148, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_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(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_wrap0_s3_clk_src_init = { + .name = "gcc_qupv3_wrap0_s3_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_wrap0_s3_clk_src = { + .cmd_rcgr = 0x18290, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s4_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(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(128000000, P_GCC_GPLL6_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { + .name = "gcc_qupv3_wrap0_s4_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { + .cmd_rcgr = 0x183c8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s4_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { + .name = "gcc_qupv3_wrap0_s5_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_wrap0_s5_clk_src = { + .cmd_rcgr = 0x18500, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s6_clk_src_init = { + .name = "gcc_qupv3_wrap0_s6_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_wrap0_s6_clk_src = { + .cmd_rcgr = 0x18638, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s6_clk_src_init, +}; + +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_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_qspi_ref_clk_src = { + .cmd_rcgr = 0x1e768, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_qspi_ref_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_qspi_ref_clk_src_init, +}; + +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), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x1e010, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .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), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x1e148, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +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), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x1e290, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .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_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x1e3c8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s4_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +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), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x1e500, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .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_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { + .cmd_rcgr = 0x1e638, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s3_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_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_GPLL6_OUT_MAIN, 2, 0, 0), + F(384000000, P_GCC_GPLL6_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0xa3014, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .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_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_shared_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 = 0xa3038, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .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_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_shared_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 = 0x14018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_MAIN, 4, 0, 0), + F(300000000, P_GCC_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x77030, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .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), + .ops = &clk_rcg2_shared_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 = 0x77080, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .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), + .ops = &clk_rcg2_shared_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 = 0x770b4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_phy_aux_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = { + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_MAIN, 4, 0, 0), + F(300000000, P_GCC_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { + .cmd_rcgr = 0x77098, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_unipro_core_clk_src, + .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), + .ops = &clk_rcg2_shared_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 = 0x3902c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x39044, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x39070, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .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), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_0_pipe_div2_clk_src = { + .reg = 0x6b094, + .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 = 0x90090, + .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_wrap0_s2_clk_src = { + .reg = 0x18280, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_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_qupv3_wrap1_s2_clk_src = { + .reg = 0x1e280, + .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 = 0x3905c, + .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 = 0x1005c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1005c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(12), + .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 = 0x770e4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770e4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770e4, + .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_ufs_phy_axi_hw_ctl_clk = { + .halt_reg = 0x770e4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770e4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770e4, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_ufs_phy_axi_hw_ctl_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 = 0x39090, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x39090, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39090, + .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 = 0x26010, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x26010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26010, + .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 = 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_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 = 0x3908c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3908c, + .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 = 0x7115c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x7115c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7115c, + .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 = 0x1006c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1006c, + .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_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_gpll0_div_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_disp_hf_axi_clk = { + .halt_reg = 0x2700c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x2700c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2700c, + .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_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_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_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_div_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_memnoc_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_memnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_snoc_dvm_gfx_clk = { + .halt_reg = 0x71018, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x71018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_snoc_dvm_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x6b03c, + .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 = 0x6b038, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b038, + .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 = 0x6b02c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b02c, + .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 = 0x6b054, + .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 = 0x6b048, + .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 = 0x6b098, + .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 = 0x90038, + .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 = 0x90034, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x90034, + .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 = 0x90028, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x90028, + .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 = 0x90050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(8), + .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 = 0x90044, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(7), + .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 = 0x90094, + .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 = 0x9001c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x9001c, + .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 = 0x90018, + .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_pcie_rscc_cfg_ahb_clk = { + .halt_reg = 0x11004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x11004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_rscc_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_rscc_xo_clk = { + .halt_reg = 0x11008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_rscc_xo_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_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_disp_ahb_clk = { + .halt_reg = 0x27008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x27008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x27008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_disp_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_cv_cpu_ahb_clk = { + .halt_reg = 0x32014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x32014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_video_cv_cpu_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_video_cvp_ahb_clk = { + .halt_reg = 0x32008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x32008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_video_cvp_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_wrap0_core_2x_clk = { + .halt_reg = 0x23018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_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_wrap0_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_qspi_ref_clk = { + .halt_reg = 0x18764, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_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_wrap0_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s1_clk = { + .halt_reg = 0x1813c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s2_clk = { + .halt_reg = 0x18274, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s3_clk = { + .halt_reg = 0x18284, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s4_clk = { + .halt_reg = 0x183bc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s5_clk = { + .halt_reg = 0x184f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s6_clk = { + .halt_reg = 0x1862c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x23168, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(3), + .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 = 0x23158, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(0), + .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 = 0x1e764, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(30), + .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 = 0x1e004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(4), + .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 = 0x1e13c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(5), + .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 = 0x1e274, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(6), + .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 = 0x1e284, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(7), + .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 = 0x1e3bc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(8), + .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 = 0x1e4f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(9), + .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 = 0x1e62c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(10), + .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_wrap_0_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_0_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_0_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_0_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_m_ahb_clk = { + .halt_reg = 0x23150, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(2), + .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 = 0x23154, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23154, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0xa3004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa3004, + .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 = 0xa3008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa3008, + .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 = 0xa302c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xa302c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xa302c, + .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 = 0x14010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14010, + .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 = 0x77024, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77024, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77024, + .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_axi_hw_ctl_clk = { + .halt_reg = 0x77018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77018, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_hw_ctl_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 = 0x77074, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77074, + .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_ice_core_hw_ctl_clk = { + .halt_reg = 0x77074, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77074, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_hw_ctl_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 = 0x770b0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770b0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770b0, + .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_phy_aux_hw_ctl_clk = { + .halt_reg = 0x770b0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770b0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770b0, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_hw_ctl_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 = 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_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 = 0x770cc, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x770cc, + .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 = 0x77028, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x77028, + .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 = 0x77068, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77068, + .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_ufs_phy_unipro_core_hw_ctl_clk = { + .halt_reg = 0x77068, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77068, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_core_hw_ctl_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 = 0x39088, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x39088, + .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 = 0x39028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39028, + .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 = 0x39024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39024, + .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 = 0x39060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39060, + .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 = 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_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 = 0x39068, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x39068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39068, + .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 gdsc pcie_0_gdsc = { + .gdscr = 0x6b004, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(0), + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "pcie_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc pcie_0_phy_gdsc = { + .gdscr = 0x6c000, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(1), + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "pcie_0_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc pcie_1_gdsc = { + .gdscr = 0x90004, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(3), + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "pcie_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc pcie_1_phy_gdsc = { + .gdscr = 0xa2000, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(4), + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "pcie_1_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ufs_phy_gdsc = { + .gdscr = 0x77004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ufs_mem_phy_gdsc = { + .gdscr = 0x9e000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "ufs_mem_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc usb30_prim_gdsc = { + .gdscr = 0x39004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_RET_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc usb3_phy_gdsc = { + .gdscr = 0x5000c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "usb3_phy_gdsc", + }, + .pwrsts = PWRSTS_RET_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gcc_milos_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_UFS_PHY_AXI_HW_CTL_CLK] = &gcc_aggre_ufs_phy_axi_hw_ctl_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_GPLL0_DIV_CLK_SRC] = &gcc_disp_gpll0_div_clk_src.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_GPLL2] = &gcc_gpll2.clkr, + [GCC_GPLL4] = &gcc_gpll4.clkr, + [GCC_GPLL6] = &gcc_gpll6.clkr, + [GCC_GPLL7] = &gcc_gpll7.clkr, + [GCC_GPLL9] = &gcc_gpll9.clkr, + [GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr, + [GCC_GPU_MEMNOC_GFX_CLK] = &gcc_gpu_memnoc_gfx_clk.clkr, + [GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_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_PCIE_RSCC_CFG_AHB_CLK] = &gcc_pcie_rscc_cfg_ahb_clk.clkr, + [GCC_PCIE_RSCC_XO_CLK] = &gcc_pcie_rscc_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_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_DISP_AHB_CLK] = &gcc_qmip_disp_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_CV_CPU_AHB_CLK] = &gcc_qmip_video_cv_cpu_ahb_clk.clkr, + [GCC_QMIP_VIDEO_CVP_AHB_CLK] = &gcc_qmip_video_cvp_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_WRAP0_CORE_2X_CLK] = &gcc_qupv3_wrap0_core_2x_clk.clkr, + [GCC_QUPV3_WRAP0_CORE_CLK] = &gcc_qupv3_wrap0_core_clk.clkr, + [GCC_QUPV3_WRAP0_QSPI_REF_CLK] = &gcc_qupv3_wrap0_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP0_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap0_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP0_S0_CLK] = &gcc_qupv3_wrap0_s0_clk.clkr, + [GCC_QUPV3_WRAP0_S0_CLK_SRC] = &gcc_qupv3_wrap0_s0_clk_src.clkr, + [GCC_QUPV3_WRAP0_S1_CLK] = &gcc_qupv3_wrap0_s1_clk.clkr, + [GCC_QUPV3_WRAP0_S1_CLK_SRC] = &gcc_qupv3_wrap0_s1_clk_src.clkr, + [GCC_QUPV3_WRAP0_S2_CLK] = &gcc_qupv3_wrap0_s2_clk.clkr, + [GCC_QUPV3_WRAP0_S2_CLK_SRC] = &gcc_qupv3_wrap0_s2_clk_src.clkr, + [GCC_QUPV3_WRAP0_S3_CLK] = &gcc_qupv3_wrap0_s3_clk.clkr, + [GCC_QUPV3_WRAP0_S3_CLK_SRC] = &gcc_qupv3_wrap0_s3_clk_src.clkr, + [GCC_QUPV3_WRAP0_S4_CLK] = &gcc_qupv3_wrap0_s4_clk.clkr, + [GCC_QUPV3_WRAP0_S4_CLK_SRC] = &gcc_qupv3_wrap0_s4_clk_src.clkr, + [GCC_QUPV3_WRAP0_S5_CLK] = &gcc_qupv3_wrap0_s5_clk.clkr, + [GCC_QUPV3_WRAP0_S5_CLK_SRC] = &gcc_qupv3_wrap0_s5_clk_src.clkr, + [GCC_QUPV3_WRAP0_S6_CLK] = &gcc_qupv3_wrap0_s6_clk.clkr, + [GCC_QUPV3_WRAP0_S6_CLK_SRC] = &gcc_qupv3_wrap0_s6_clk_src.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_WRAP_0_M_AHB_CLK] = &gcc_qupv3_wrap_0_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_0_S_AHB_CLK] = &gcc_qupv3_wrap_0_s_ahb_clk.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_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_AXI_HW_CTL_CLK] = &gcc_ufs_phy_axi_hw_ctl_clk.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_ICE_CORE_HW_CTL_CLK] = &gcc_ufs_phy_ice_core_hw_ctl_clk.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_PHY_AUX_HW_CTL_CLK] = &gcc_ufs_phy_phy_aux_hw_ctl_clk.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_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK] = &gcc_ufs_phy_unipro_core_hw_ctl_clk.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, +}; + +static const struct qcom_reset_map gcc_milos_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] = { 0x90000 }, + [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_RSCC_BCR] = { 0x11000 }, + [GCC_PDM_BCR] = { 0x33000 }, + [GCC_QUPV3_WRAPPER_0_BCR] = { 0x18000 }, + [GCC_QUPV3_WRAPPER_1_BCR] = { 0x1e000 }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 }, + [GCC_SDCC1_BCR] = { 0xa3000 }, + [GCC_SDCC2_BCR] = { 0x14000 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0x39000 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_VIDEO_AXI0_CLK_ARES] = { 0x32018, 2 }, + [GCC_VIDEO_BCR] = { 0x32000 }, +}; + +static const struct clk_rcg_dfs_data gcc_milos_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap0_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s6_clk_src), + 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), +}; + +static struct gdsc *gcc_milos_gdscs[] = { + [PCIE_0_GDSC] = &pcie_0_gdsc, + [PCIE_0_PHY_GDSC] = &pcie_0_phy_gdsc, + [PCIE_1_GDSC] = &pcie_1_gdsc, + [PCIE_1_PHY_GDSC] = &pcie_1_phy_gdsc, + [UFS_PHY_GDSC] = &ufs_phy_gdsc, + [UFS_MEM_PHY_GDSC] = &ufs_mem_phy_gdsc, + [USB30_PRIM_GDSC] = &usb30_prim_gdsc, + [USB3_PHY_GDSC] = &usb3_phy_gdsc, +}; + +static u32 gcc_milos_critical_cbcrs[] = { + 0x26004, /* GCC_CAMERA_AHB_CLK */ + 0x26018, /* GCC_CAMERA_HF_XO_CLK */ + 0x2601c, /* GCC_CAMERA_SF_XO_CLK */ + 0x27004, /* GCC_DISP_AHB_CLK */ + 0x27018, /* GCC_DISP_XO_CLK */ + 0x71004, /* GCC_GPU_CFG_AHB_CLK */ + 0x32004, /* GCC_VIDEO_AHB_CLK */ + 0x32024, /* GCC_VIDEO_XO_CLK */ +}; + +static const struct regmap_config gcc_milos_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f41f0, + .fast_io = true, +}; + +static 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, + .num_dfs_rcgs = ARRAY_SIZE(gcc_milos_dfs_clocks), +}; + +static const struct qcom_cc_desc gcc_milos_desc = { + .config = &gcc_milos_regmap_config, + .clks = gcc_milos_clocks, + .num_clks = ARRAY_SIZE(gcc_milos_clocks), + .resets = gcc_milos_resets, + .num_resets = ARRAY_SIZE(gcc_milos_resets), + .gdscs = gcc_milos_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_milos_gdscs), + .use_rpm = true, + .driver_data = &gcc_milos_driver_data, +}; + +static const struct of_device_id gcc_milos_match_table[] = { + { .compatible = "qcom,milos-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_milos_match_table); + +static int gcc_milos_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_milos_desc); +} + +static struct platform_driver gcc_milos_driver = { + .probe = gcc_milos_probe, + .driver = { + .name = "gcc-milos", + .of_match_table = gcc_milos_match_table, + }, +}; + +static int __init gcc_milos_init(void) +{ + return platform_driver_register(&gcc_milos_driver); +} +subsys_initcall(gcc_milos_init); + +static void __exit gcc_milos_exit(void) +{ + platform_driver_unregister(&gcc_milos_driver); +} +module_exit(gcc_milos_exit); + +MODULE_DESCRIPTION("QTI GCC Milos Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-qcm2290.c b/drivers/clk/qcom/gcc-qcm2290.c index 9a6703365e61..6684cab63ae1 100644 --- a/drivers/clk/qcom/gcc-qcm2290.c +++ b/drivers/clk/qcom/gcc-qcm2290.c @@ -2720,6 +2720,7 @@ static struct gdsc gcc_vcodec0_gdsc = { .pd = { .name = "gcc_vcodec0", }, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/gcc-sm8150.c b/drivers/clk/qcom/gcc-sm8150.c index cefceb780889..a93d1f412a7b 100644 --- a/drivers/clk/qcom/gcc-sm8150.c +++ b/drivers/clk/qcom/gcc-sm8150.c @@ -1245,7 +1245,7 @@ static struct clk_branch gcc_boot_rom_ahb_clk = { }; /* - * Clock ON depends on external parent 'config noc', so cant poll + * Clock ON depends on external parent 'config noc', so can't poll * delay and also mark as crtitical for camss boot */ static struct clk_branch gcc_camera_ahb_clk = { @@ -1398,7 +1398,7 @@ static struct clk_branch gcc_ddrss_gpu_axi_clk = { }; /* - * Clock ON depends on external parent 'config noc', so cant poll + * Clock ON depends on external parent 'config noc', so can't poll * delay and also mark as crtitical for disp boot */ static struct clk_branch gcc_disp_ahb_clk = { @@ -3339,7 +3339,7 @@ static struct clk_branch gcc_usb3_sec_phy_pipe_clk = { }; /* - * Clock ON depends on external parent 'config noc', so cant poll + * Clock ON depends on external parent 'config noc', so can't poll * delay and also mark as crtitical for video boot */ static struct clk_branch gcc_video_ahb_clk = { diff --git a/drivers/clk/qcom/gcc-x1e80100.c b/drivers/clk/qcom/gcc-x1e80100.c index 3e44757e25d3..301fc9fc32d8 100644 --- a/drivers/clk/qcom/gcc-x1e80100.c +++ b/drivers/clk/qcom/gcc-x1e80100.c @@ -6674,6 +6674,8 @@ static const struct qcom_reset_map gcc_x1e80100_resets[] = { [GCC_USB_1_PHY_BCR] = { 0x2a020 }, [GCC_USB_2_PHY_BCR] = { 0xa3020 }, [GCC_VIDEO_BCR] = { 0x32000 }, + [GCC_VIDEO_AXI0_CLK_ARES] = { .reg = 0x32018, .bit = 2, .udelay = 1000 }, + [GCC_VIDEO_AXI1_CLK_ARES] = { .reg = 0x32024, .bit = 2, .udelay = 1000 }, }; static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { diff --git a/drivers/clk/qcom/gpucc-milos.c b/drivers/clk/qcom/gpucc-milos.c new file mode 100644 index 000000000000..4ee09879156e --- /dev/null +++ b/drivers/clk/qcom/gpucc-milos.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss + */ + +#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" + +/* Need to match the order of clocks in DT binding */ +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 lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +/* 700.0 MHz Configuration */ +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x24, + .alpha = 0x7555, + .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 = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .config = &gpu_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 = "gpu_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_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_LUCID_OLE], + .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_lucid_ole_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(350000000, 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_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_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_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_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_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_milos_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_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_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_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_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_milos_gdscs[] = { + [GPU_CC_CX_GDSC] = &gpu_cc_cx_gdsc, +}; + +static const struct qcom_reset_map gpu_cc_milos_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_RBCPR_BCR] = { 0x91e0 }, + [GPU_CC_XO_BCR] = { 0x9000 }, +}; + +static struct clk_alpha_pll *gpu_cc_milos_plls[] = { + &gpu_cc_pll0, +}; + +static u32 gpu_cc_milos_critical_cbcrs[] = { + 0x93a4, /* GPU_CC_CB_CLK */ + 0x9008, /* GPU_CC_CXO_AON_CLK */ + 0x9010, /* GPU_CC_DEMET_CLK */ + 0x9064, /* GPU_CC_GX_AHB_FF_CLK */ + 0x93a8, /* GPU_CC_RSCC_HUB_AON_CLK */ + 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ + 0x90cc, /* GPU_CC_SLEEP_CLK */ +}; + +static const struct regmap_config gpu_cc_milos_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x95e8, + .fast_io = true, +}; + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(gpu_cc_milos_critical_cbcrs), +}; + +static const struct qcom_cc_desc gpu_cc_milos_desc = { + .config = &gpu_cc_milos_regmap_config, + .clks = gpu_cc_milos_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_milos_clocks), + .resets = gpu_cc_milos_resets, + .num_resets = ARRAY_SIZE(gpu_cc_milos_resets), + .gdscs = gpu_cc_milos_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_milos_gdscs), + .use_rpm = true, + .driver_data = &gpu_cc_milos_driver_data, +}; + +static const struct of_device_id gpu_cc_milos_match_table[] = { + { .compatible = "qcom,milos-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_milos_match_table); + +static int gpu_cc_milos_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gpu_cc_milos_desc); +} + +static struct platform_driver gpu_cc_milos_driver = { + .probe = gpu_cc_milos_probe, + .driver = { + .name = "gpu_cc-milos", + .of_match_table = gpu_cc_milos_match_table, + }, +}; + +module_platform_driver(gpu_cc_milos_driver); + +MODULE_DESCRIPTION("QTI GPU_CC Milos Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gpucc-qcs615.c b/drivers/clk/qcom/gpucc-qcs615.c new file mode 100644 index 000000000000..ec6739c08425 --- /dev/null +++ b/drivers/clk/qcom/gpucc-qcs615.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#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_2X_CLK, + P_CRC_DIV_PLL0_OUT_AUX2, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL1_OUT_AUX, + P_CRC_DIV_PLL1_OUT_AUX2, + P_GPU_CC_PLL1_OUT_MAIN, +}; + +static const struct pll_vco gpu_cc_pll0_vco[] = { + { 1000000000, 2100000000, 0 }, +}; + +static struct pll_vco gpu_cc_pll1_vco[] = { + { 500000000, 1000000000, 2 }, +}; + +/* 1020MHz configuration VCO - 0 */ +static struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x35, + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, + .alpha_hi = 0x20, + .alpha = 0x00, + .alpha_en_mask = BIT(24), + .vco_val = 0x0, + .vco_mask = GENMASK(21, 20), + .aux2_output_mask = BIT(2), +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .config = &gpu_cc_pll0_config, + .vco_table = gpu_cc_pll0_vco, + .num_vco = ARRAY_SIZE(gpu_cc_pll0_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .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_slew_ops, + }, + }, +}; + +/* 930MHz configuration VCO - 2 */ +static struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x30, + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, + .alpha_hi = 0x70, + .alpha = 0x00, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .aux2_output_mask = BIT(2), +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .config = &gpu_cc_pll1_config, + .vco_table = gpu_cc_pll1_vco, + .num_vco = ARRAY_SIZE(gpu_cc_pll1_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_slew_ops, + }, + } +}; + +/* Clock Ramp Controller */ +static struct clk_fixed_factor crc_div_pll0 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "crc_div_pll0", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +/* Clock Ramp Controller */ +static struct clk_fixed_factor crc_div_pll1 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "crc_div_pll1", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_pll1.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { 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 }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .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_2X_CLK, 1 }, + { P_CRC_DIV_PLL0_OUT_AUX2, 2 }, + { P_GPU_CC_PLL1_OUT_AUX, 3 }, + { P_CRC_DIV_PLL1_OUT_AUX2, 4 }, + { P_GPLL0_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .hw = &crc_div_pll0.hw }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .hw = &crc_div_pll1.hw }, + { .index = DT_GPLL0_OUT_MAIN }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .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_0, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src[] = { + F(290000000, P_CRC_DIV_PLL1_OUT_AUX2, 1, 0, 0), + F(350000000, P_CRC_DIV_PLL1_OUT_AUX2, 1, 0, 0), + F(435000000, P_CRC_DIV_PLL1_OUT_AUX2, 1, 0, 0), + F(500000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(550000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(650000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(700000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(745000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(845000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + F(895000000, P_CRC_DIV_PLL0_OUT_AUX2, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = { + .cmd_rcgr = 0x101c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_gx_gfx3d_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_gfx3d_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_crc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_clk = { + .halt_reg = 0x10a4, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_gfx3d_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gx_gfx3d_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_slv_clk = { + .halt_reg = 0x10a8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_gfx3d_slv_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gx_gfx3d_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 = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .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_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .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_gx_gfx3d_clk = { + .halt_reg = 0x1054, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x1054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_gfx3d_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gx_gfx3d_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 = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .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 = 0x5000, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x5000, + .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_sleep_clk = { + .halt_reg = 0x1090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_hw *gpu_cc_qcs615_hws[] = { + [CRC_DIV_PLL0] = &crc_div_pll0.hw, + [CRC_DIV_PLL1] = &crc_div_pll1.hw, +}; + +static struct gdsc cx_gdsc = { + .gdscr = 0x106c, + .gds_hw_ctrl = 0x1540, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x8, + .pd = { + .name = "cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR, +}; + +static struct gdsc gx_gdsc = { + .gdscr = 0x100c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR, +}; + +static struct clk_regmap *gpu_cc_qcs615_clocks[] = { + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_GFX3D_CLK] = &gpu_cc_cx_gfx3d_clk.clkr, + [GPU_CC_CX_GFX3D_SLV_CLK] = &gpu_cc_cx_gfx3d_slv_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_GFX3D_CLK] = &gpu_cc_gx_gfx3d_clk.clkr, + [GPU_CC_GX_GFX3D_CLK_SRC] = &gpu_cc_gx_gfx3d_clk_src.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_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL1] = &gpu_cc_pll1.clkr, + [GPU_CC_SLEEP_CLK] = &gpu_cc_sleep_clk.clkr, +}; + +static struct gdsc *gpu_cc_qcs615_gdscs[] = { + [CX_GDSC] = &cx_gdsc, + [GX_GDSC] = &gx_gdsc, +}; + +static const struct qcom_reset_map gpu_cc_qcs615_resets[] = { + [GPU_CC_CX_BCR] = { 0x1068 }, + [GPU_CC_GFX3D_AON_BCR] = { 0x10a0 }, + [GPU_CC_GMU_BCR] = { 0x111c }, + [GPU_CC_GX_BCR] = { 0x1008 }, + [GPU_CC_XO_BCR] = { 0x1000 }, +}; + +static struct clk_alpha_pll *gpu_cc_qcs615_plls[] = { + &gpu_cc_pll0, + &gpu_cc_pll1, +}; + +static u32 gpu_cc_qcs615_critical_cbcrs[] = { + 0x1078, /* GPU_CC_AHB_CLK */ +}; + +static const struct regmap_config gpu_cc_qcs615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7008, + .fast_io = true, +}; + +static void clk_qcs615_regs_crc_configure(struct device *dev, struct regmap *regmap) +{ + /* Recommended WAKEUP/SLEEP settings for the gpu_cc_cx_gmu_clk */ + regmap_update_bits(regmap, gpu_cc_cx_gmu_clk.clkr.enable_reg, 0xff0, 0xff0); + + /* + * After POR, Clock Ramp Controller(CRC) will be in bypass mode. + * Software needs to do the following operation to enable the CRC + * for GFX3D clock and divide the input clock by div by 2. + */ + regmap_update_bits(regmap, 0x1028, 0x00015011, 0x00015011); + regmap_update_bits(regmap, 0x1024, 0x00800000, 0x00800000); +} + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(gpu_cc_qcs615_critical_cbcrs), + .clk_regs_configure = clk_qcs615_regs_crc_configure, +}; + +static const struct qcom_cc_desc gpu_cc_qcs615_desc = { + .config = &gpu_cc_qcs615_regmap_config, + .clks = gpu_cc_qcs615_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_qcs615_clocks), + .clk_hws = gpu_cc_qcs615_hws, + .num_clk_hws = ARRAY_SIZE(gpu_cc_qcs615_hws), + .resets = gpu_cc_qcs615_resets, + .num_resets = ARRAY_SIZE(gpu_cc_qcs615_resets), + .gdscs = gpu_cc_qcs615_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_qcs615_gdscs), + .driver_data = &gpu_cc_qcs615_driver_data, +}; + +static const struct of_device_id gpu_cc_qcs615_match_table[] = { + { .compatible = "qcom,qcs615-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_qcs615_match_table); + +static int gpu_cc_qcs615_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gpu_cc_qcs615_desc); +} + +static struct platform_driver gpu_cc_qcs615_driver = { + .probe = gpu_cc_qcs615_probe, + .driver = { + .name = "gpucc-qcs615", + .of_match_table = gpu_cc_qcs615_match_table, + }, +}; + +module_platform_driver(gpu_cc_qcs615_driver); + +MODULE_DESCRIPTION("QTI GPUCC QCS615 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 432d4c4b7aa6..b3d7169c63e5 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ /* @@ -16,6 +16,10 @@ * are supplied to GCC (24 MHZ as XO and 32 KHZ as sleep clock), and to PCS * with 31.25 MHZ. * + * On the IPQ5424 SoC, there is an output clock from CMN PLL to PPE at 375 MHZ, + * and an output clock to NSS (network subsystem) at 300 MHZ. The other output + * clocks from CMN PLL on IPQ5424 are the same as IPQ9574. + * * +---------+ * | GCC | * +--+---+--+ @@ -46,6 +50,8 @@ #include #include +#include +#include #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) @@ -105,6 +111,26 @@ static const struct regmap_config ipq_cmn_pll_regmap_config = { .fast_io = true, }; +static const struct cmn_pll_fixed_output_clk ipq5018_output_clks[] = { + CLK_PLL_OUTPUT(IPQ5018_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), + CLK_PLL_OUTPUT(IPQ5018_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), + CLK_PLL_OUTPUT(IPQ5018_ETH_50MHZ_CLK, "eth-50mhz", 50000000UL), + { /* 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), + CLK_PLL_OUTPUT(IPQ5424_PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), + CLK_PLL_OUTPUT(IPQ5424_NSS_300MHZ_CLK, "nss-300mhz", 300000000UL), + CLK_PLL_OUTPUT(IPQ5424_PPE_375MHZ_CLK, "ppe-375mhz", 375000000UL), + CLK_PLL_OUTPUT(IPQ5424_ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), + CLK_PLL_OUTPUT(IPQ5424_ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), + CLK_PLL_OUTPUT(IPQ5424_ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), + CLK_PLL_OUTPUT(IPQ5424_ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), + { /* Sentinel */ } +}; + static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { CLK_PLL_OUTPUT(XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), @@ -115,6 +141,7 @@ static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), + { /* Sentinel */ } }; /* @@ -297,7 +324,7 @@ static struct clk_hw *ipq_cmn_pll_clk_hw_register(struct platform_device *pdev) static int ipq_cmn_pll_register_clks(struct platform_device *pdev) { - const struct cmn_pll_fixed_output_clk *fixed_clk; + const struct cmn_pll_fixed_output_clk *p, *fixed_clk; struct clk_hw_onecell_data *hw_data; struct device *dev = &pdev->dev; struct clk_hw *cmn_pll_hw; @@ -305,8 +332,13 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev) struct clk_hw *hw; int ret, i; - fixed_clk = ipq9574_output_clks; - num_clks = ARRAY_SIZE(ipq9574_output_clks); + fixed_clk = device_get_match_data(dev); + if (!fixed_clk) + return -EINVAL; + + num_clks = 0; + for (p = fixed_clk; p->name; p++) + num_clks++; hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks + 1), GFP_KERNEL); @@ -375,11 +407,11 @@ static int ipq_cmn_pll_clk_probe(struct platform_device *pdev) */ ret = pm_clk_add(dev, "ahb"); if (ret) - return dev_err_probe(dev, ret, "Fail to add AHB clock\n"); + return dev_err_probe(dev, ret, "Failed to add AHB clock\n"); ret = pm_clk_add(dev, "sys"); if (ret) - return dev_err_probe(dev, ret, "Fail to add SYS clock\n"); + return dev_err_probe(dev, ret, "Failed to add SYS clock\n"); ret = pm_runtime_resume_and_get(dev); if (ret) @@ -390,7 +422,7 @@ static int ipq_cmn_pll_clk_probe(struct platform_device *pdev) pm_runtime_put(dev); if (ret) return dev_err_probe(dev, ret, - "Fail to register CMN PLL clocks\n"); + "Failed to register CMN PLL clocks\n"); return 0; } @@ -415,7 +447,9 @@ static const struct dev_pm_ops ipq_cmn_pll_pm_ops = { }; static const struct of_device_id ipq_cmn_pll_clk_ids[] = { - { .compatible = "qcom,ipq9574-cmn-pll", }, + { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, + { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, + { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, { } }; MODULE_DEVICE_TABLE(of, ipq_cmn_pll_clk_ids); diff --git a/drivers/clk/qcom/lpassaudiocc-sc7280.c b/drivers/clk/qcom/lpassaudiocc-sc7280.c index 22169da08a51..3ff123bffa11 100644 --- a/drivers/clk/qcom/lpassaudiocc-sc7280.c +++ b/drivers/clk/qcom/lpassaudiocc-sc7280.c @@ -799,7 +799,6 @@ static int lpass_audio_cc_sc7280_probe(struct platform_device *pdev) goto exit; } - pm_runtime_mark_last_busy(&pdev->dev); exit: pm_runtime_put_autosuspend(&pdev->dev); @@ -868,7 +867,6 @@ static int lpass_aon_cc_sc7280_probe(struct platform_device *pdev) goto exit; } - pm_runtime_mark_last_busy(&pdev->dev); exit: pm_runtime_put_autosuspend(&pdev->dev); diff --git a/drivers/clk/qcom/lpasscorecc-sc7180.c b/drivers/clk/qcom/lpasscorecc-sc7180.c index 605516d03993..5937b071533b 100644 --- a/drivers/clk/qcom/lpasscorecc-sc7180.c +++ b/drivers/clk/qcom/lpasscorecc-sc7180.c @@ -412,7 +412,6 @@ static int lpass_core_cc_sc7180_probe(struct platform_device *pdev) ret = qcom_cc_really_probe(&pdev->dev, &lpass_core_cc_sc7180_desc, regmap); - pm_runtime_mark_last_busy(&pdev->dev); exit: pm_runtime_put_autosuspend(&pdev->dev); @@ -433,7 +432,6 @@ static int lpass_hm_core_probe(struct platform_device *pdev) ret = qcom_cc_probe_by_index(pdev, 0, desc); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return ret; diff --git a/drivers/clk/qcom/tcsrcc-sm8650.c b/drivers/clk/qcom/tcsrcc-sm8650.c index 11c7d6df48c7..3685dcde9a4b 100644 --- a/drivers/clk/qcom/tcsrcc-sm8650.c +++ b/drivers/clk/qcom/tcsrcc-sm8650.c @@ -148,6 +148,7 @@ static const struct qcom_cc_desc tcsr_cc_sm8650_desc = { }; static const struct of_device_id tcsr_cc_sm8650_match_table[] = { + { .compatible = "qcom,milos-tcsr" }, { .compatible = "qcom,sm8650-tcsr" }, { } }; @@ -155,6 +156,13 @@ MODULE_DEVICE_TABLE(of, tcsr_cc_sm8650_match_table); static int tcsr_cc_sm8650_probe(struct platform_device *pdev) { + if (of_device_is_compatible(pdev->dev.of_node, "qcom,milos-tcsr")) { + tcsr_ufs_clkref_en.halt_reg = 0x31118; + tcsr_ufs_clkref_en.clkr.enable_reg = 0x31118; + tcsr_cc_sm8650_clocks[TCSR_USB2_CLKREF_EN] = NULL; + tcsr_cc_sm8650_clocks[TCSR_USB3_CLKREF_EN] = NULL; + } + return qcom_cc_probe(pdev, &tcsr_cc_sm8650_desc); } diff --git a/drivers/clk/qcom/videocc-milos.c b/drivers/clk/qcom/videocc-milos.c new file mode 100644 index 000000000000..998301e0ba88 --- /dev/null +++ b/drivers/clk/qcom/videocc-milos.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss + */ + +#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 "common.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_SLEEP_CLK, + DT_IFACE, +}; + +enum { + P_BI_TCXO, + P_SLEEP_CLK, + P_VIDEO_CC_PLL0_OUT_MAIN, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +/* 604.8 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll0_config = { + .l = 0x1f, + .alpha = 0x8000, + .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 video_cc_pll0 = { + .offset = 0x0, + .config = &video_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 = "video_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_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 clk_parent_data video_cc_parent_data_0_ao[] = { + { .index = DT_BI_TCXO_AO }, +}; + +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_ao[] = { + { .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 = 0x8030, + .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_ao, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { + F(604800000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + 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(1656000000, 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 = 0x8128, + .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_ao, + .num_parents = ARRAY_SIZE(video_cc_parent_data_2_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 video_cc_xo_clk_src = { + .cmd_rcgr = 0x810c, + .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_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs0_div_clk_src = { + .reg = 0x80c4, + .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 = 0x8070, + .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_branch video_cc_mvs0_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_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_shift_clk = { + .halt_reg = 0x8144, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x8144, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x8144, + .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 = 0x8064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8064, + .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_shift_clk = { + .halt_reg = 0x8148, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x8148, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x8148, + .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 gdsc video_cc_mvs0c_gdsc = { + .gdscr = 0x804c, + .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 = 0x80a4, + .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, + .parent = &video_cc_mvs0c_gdsc.pd, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | HW_CTRL_TRIGGER, +}; + +static struct clk_regmap *video_cc_milos_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_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_SHIFT_CLK] = &video_cc_mvs0c_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_milos_gdscs[] = { + [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, + [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, +}; + +static const struct qcom_reset_map video_cc_milos_resets[] = { + [VIDEO_CC_INTERFACE_BCR] = { 0x80f0 }, + [VIDEO_CC_MVS0_BCR] = { 0x80a0 }, + [VIDEO_CC_MVS0C_CLK_ARES] = { 0x8064, 2 }, + [VIDEO_CC_MVS0C_BCR] = { 0x8048 }, +}; + +static struct clk_alpha_pll *video_cc_milos_plls[] = { + &video_cc_pll0, +}; + +static u32 video_cc_milos_critical_cbcrs[] = { + 0x80f4, /* VIDEO_CC_AHB_CLK */ + 0x8140, /* VIDEO_CC_SLEEP_CLK */ + 0x8124, /* VIDEO_CC_XO_CLK */ +}; + +static const struct regmap_config video_cc_milos_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9f50, + .fast_io = true, +}; + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_milos_critical_cbcrs), +}; + +static struct qcom_cc_desc video_cc_milos_desc = { + .config = &video_cc_milos_regmap_config, + .clks = video_cc_milos_clocks, + .num_clks = ARRAY_SIZE(video_cc_milos_clocks), + .resets = video_cc_milos_resets, + .num_resets = ARRAY_SIZE(video_cc_milos_resets), + .gdscs = video_cc_milos_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_milos_gdscs), + .use_rpm = true, + .driver_data = &video_cc_milos_driver_data, +}; + +static const struct of_device_id video_cc_milos_match_table[] = { + { .compatible = "qcom,milos-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_milos_match_table); + +static int video_cc_milos_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &video_cc_milos_desc); +} + +static struct platform_driver video_cc_milos_driver = { + .probe = video_cc_milos_probe, + .driver = { + .name = "video_cc-milos", + .of_match_table = video_cc_milos_match_table, + }, +}; + +module_platform_driver(video_cc_milos_driver); + +MODULE_DESCRIPTION("QTI VIDEO_CC Milos Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/videocc-qcs615.c b/drivers/clk/qcom/videocc-qcs615.c new file mode 100644 index 000000000000..1b41fa44c17e --- /dev/null +++ b/drivers/clk/qcom/videocc-qcs615.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#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_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_SLEEP_CLK, + P_VIDEO_PLL0_OUT_AUX, + P_VIDEO_PLL0_OUT_AUX2, + P_VIDEO_PLL0_OUT_MAIN, +}; + +static const struct pll_vco video_cc_pll0_vco[] = { + { 500000000, 1000000000, 2 }, +}; + +/* 600MHz configuration VCO - 2 */ +static struct alpha_pll_config video_pll0_config = { + .l = 0x1f, + .alpha_hi = 0x40, + .alpha = 0x00, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x1, + .test_ctl_hi_mask = 0x1, +}; + +static struct clk_alpha_pll video_pll0 = { + .offset = 0x42c, + .config = &video_pll0_config, + .vco_table = video_cc_pll0_vco, + .num_vco = ARRAY_SIZE(video_cc_pll0_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_slew_ops, + }, + }, +}; + +static const struct parent_map video_cc_parent_map_0[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_0_ao[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map video_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_PLL0_OUT_MAIN, 1 }, + { P_VIDEO_PLL0_OUT_AUX, 2 }, + { P_VIDEO_PLL0_OUT_AUX2, 3 }, +}; + +static const struct clk_parent_data video_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_pll0.clkr.hw }, + { .hw = &video_pll0.clkr.hw }, + { .hw = &video_pll0.clkr.hw }, +}; + +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 = 0xaf8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .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_0_ao, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0_ao), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_venus_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(133333333, P_VIDEO_PLL0_OUT_MAIN, 4.5, 0, 0), + F(240000000, P_VIDEO_PLL0_OUT_MAIN, 2.5, 0, 0), + F(300000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(380000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(410000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(460000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_venus_clk_src = { + .cmd_rcgr = 0x7f0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_venus_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_venus_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 struct clk_branch video_cc_sleep_clk = { + .halt_reg = 0xb18, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb18, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "video_cc_sleep_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &video_cc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_vcodec0_axi_clk = { + .halt_reg = 0x8f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_vcodec0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_vcodec0_core_clk = { + .halt_reg = 0x890, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x890, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_vcodec0_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_venus_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_venus_ahb_clk = { + .halt_reg = 0x9b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_venus_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_venus_ctl_axi_clk = { + .halt_reg = 0x8d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8d0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_venus_ctl_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_venus_ctl_core_clk = { + .halt_reg = 0x850, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x850, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_venus_ctl_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_venus_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc vcodec0_gdsc = { + .gdscr = 0x874, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "vcodec0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR, +}; + +static struct gdsc venus_gdsc = { + .gdscr = 0x814, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "venus_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR, +}; + +static struct clk_regmap *video_cc_qcs615_clocks[] = { + [VIDEO_CC_SLEEP_CLK] = &video_cc_sleep_clk.clkr, + [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr, + [VIDEO_CC_VCODEC0_AXI_CLK] = &video_cc_vcodec0_axi_clk.clkr, + [VIDEO_CC_VCODEC0_CORE_CLK] = &video_cc_vcodec0_core_clk.clkr, + [VIDEO_CC_VENUS_AHB_CLK] = &video_cc_venus_ahb_clk.clkr, + [VIDEO_CC_VENUS_CLK_SRC] = &video_cc_venus_clk_src.clkr, + [VIDEO_CC_VENUS_CTL_AXI_CLK] = &video_cc_venus_ctl_axi_clk.clkr, + [VIDEO_CC_VENUS_CTL_CORE_CLK] = &video_cc_venus_ctl_core_clk.clkr, + [VIDEO_PLL0] = &video_pll0.clkr, +}; + +static struct gdsc *video_cc_qcs615_gdscs[] = { + [VCODEC0_GDSC] = &vcodec0_gdsc, + [VENUS_GDSC] = &venus_gdsc, +}; + +static const struct qcom_reset_map video_cc_qcs615_resets[] = { + [VIDEO_CC_INTERFACE_BCR] = { 0x8b0 }, + [VIDEO_CC_VCODEC0_BCR] = { 0x870 }, + [VIDEO_CC_VENUS_BCR] = { 0x810 }, +}; + +static struct clk_alpha_pll *video_cc_qcs615_plls[] = { + &video_pll0, +}; + +static u32 video_cc_qcs615_critical_cbcrs[] = { + 0xab8, /* VIDEO_CC_XO_CLK */ +}; + +static const struct regmap_config video_cc_qcs615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb94, + .fast_io = true, +}; + +static 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, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_qcs615_critical_cbcrs), +}; + +static const struct qcom_cc_desc video_cc_qcs615_desc = { + .config = &video_cc_qcs615_regmap_config, + .clks = video_cc_qcs615_clocks, + .num_clks = ARRAY_SIZE(video_cc_qcs615_clocks), + .resets = video_cc_qcs615_resets, + .num_resets = ARRAY_SIZE(video_cc_qcs615_resets), + .gdscs = video_cc_qcs615_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_qcs615_gdscs), + .driver_data = &video_cc_qcs615_driver_data, +}; + +static const struct of_device_id video_cc_qcs615_match_table[] = { + { .compatible = "qcom,qcs615-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_qcs615_match_table); + +static int video_cc_qcs615_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &video_cc_qcs615_desc); +} + +static struct platform_driver video_cc_qcs615_driver = { + .probe = video_cc_qcs615_probe, + .driver = { + .name = "videocc-qcs615", + .of_match_table = video_cc_qcs615_match_table, + }, +}; + +module_platform_driver(video_cc_qcs615_driver); + +MODULE_DESCRIPTION("QTI VIDEOCC QCS615 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/videocc-sc7180.c b/drivers/clk/qcom/videocc-sc7180.c index d7f845480396..dd2441d6aa83 100644 --- a/drivers/clk/qcom/videocc-sc7180.c +++ b/drivers/clk/qcom/videocc-sc7180.c @@ -166,7 +166,7 @@ static struct gdsc vcodec0_gdsc = { .pd = { .name = "vcodec0_gdsc", }, - .flags = HW_CTRL, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c index f77a07779477..6dedc80a8b3e 100644 --- a/drivers/clk/qcom/videocc-sdm845.c +++ b/drivers/clk/qcom/videocc-sdm845.c @@ -260,7 +260,7 @@ static struct gdsc vcodec0_gdsc = { }, .cxcs = (unsigned int []){ 0x890, 0x930 }, .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR, .pwrsts = PWRSTS_OFF_ON, }; @@ -271,7 +271,7 @@ static struct gdsc vcodec1_gdsc = { }, .cxcs = (unsigned int []){ 0x8d0, 0x950 }, .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/videocc-sm6350.c b/drivers/clk/qcom/videocc-sm6350.c new file mode 100644 index 000000000000..34bdc5aa865a --- /dev/null +++ b/drivers/clk/qcom/videocc-sm6350.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Konrad Dybcio + * Copyright (c) 2025, Luca Weiss + */ + +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" + +enum { + DT_IFACE, + DT_BI_TCXO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_CHIP_SLEEP_CLK, + P_VIDEO_PLL0_OUT_EVEN, +}; + +static const struct pll_vco fabia_vco[] = { + { 125000000, 1000000000, 1 }, +}; + +/* 600 MHz */ +static const struct alpha_pll_config video_pll0_config = { + .l = 0x1f, + .alpha = 0x4000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002067, + .test_ctl_val = 0x40000000, + .test_ctl_hi_val = 0x00000002, + .user_ctl_val = 0x00000101, + .user_ctl_hi_val = 0x00004005, +}; + +static struct clk_alpha_pll video_pll0 = { + .offset = 0x0, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_video_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv video_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_video_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_video_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &video_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct parent_map video_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_PLL0_OUT_EVEN, 3 }, +}; + +static const struct clk_parent_data video_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_pll0_out_even.clkr.hw }, +}; + +static const struct parent_map video_cc_parent_map_1[] = { + { P_CHIP_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_1[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_video_cc_iris_clk_src[] = { + F(133250000, P_VIDEO_PLL0_OUT_EVEN, 2, 0, 0), + F(240000000, P_VIDEO_PLL0_OUT_EVEN, 1.5, 0, 0), + F(300000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0), + F(380000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0), + F(460000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_iris_clk_src = { + .cmd_rcgr = 0x1000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .freq_tbl = ftbl_video_cc_iris_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_iris_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_sleep_clk_src[] = { + F(32764, P_CHIP_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_sleep_clk_src = { + .cmd_rcgr = 0x701c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .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_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch video_cc_iris_ahb_clk = { + .halt_reg = 0x5004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x5004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_iris_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_iris_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_axi_clk = { + .halt_reg = 0x800c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x800c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_core_clk = { + .halt_reg = 0x3010, + .halt_check = BRANCH_VOTED, + .hwcg_reg = 0x3010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_iris_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvsc_core_clk = { + .halt_reg = 0x2014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvsc_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_iris_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvsc_ctl_axi_clk = { + .halt_reg = 0x8004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvsc_ctl_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_sleep_clk = { + .halt_reg = 0x7034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_venus_ahb_clk = { + .halt_reg = 0x801c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x801c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_venus_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc mvsc_gdsc = { + .gdscr = 0x2004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "mvsc_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc mvs0_gdsc = { + .gdscr = 0x3004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "mvs0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL_TRIGGER, +}; + +static struct gdsc *video_cc_sm6350_gdscs[] = { + [MVSC_GDSC] = &mvsc_gdsc, + [MVS0_GDSC] = &mvs0_gdsc, +}; + +static struct clk_regmap *video_cc_sm6350_clocks[] = { + [VIDEO_CC_IRIS_AHB_CLK] = &video_cc_iris_ahb_clk.clkr, + [VIDEO_CC_IRIS_CLK_SRC] = &video_cc_iris_clk_src.clkr, + [VIDEO_CC_MVS0_AXI_CLK] = &video_cc_mvs0_axi_clk.clkr, + [VIDEO_CC_MVS0_CORE_CLK] = &video_cc_mvs0_core_clk.clkr, + [VIDEO_CC_MVSC_CORE_CLK] = &video_cc_mvsc_core_clk.clkr, + [VIDEO_CC_MVSC_CTL_AXI_CLK] = &video_cc_mvsc_ctl_axi_clk.clkr, + [VIDEO_CC_SLEEP_CLK] = &video_cc_sleep_clk.clkr, + [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr, + [VIDEO_CC_VENUS_AHB_CLK] = &video_cc_venus_ahb_clk.clkr, + [VIDEO_PLL0] = &video_pll0.clkr, + [VIDEO_PLL0_OUT_EVEN] = &video_pll0_out_even.clkr, +}; + +static const struct regmap_config video_cc_sm6350_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb000, + .fast_io = true, +}; + +static const struct qcom_cc_desc video_cc_sm6350_desc = { + .config = &video_cc_sm6350_regmap_config, + .clks = video_cc_sm6350_clocks, + .num_clks = ARRAY_SIZE(video_cc_sm6350_clocks), + .gdscs = video_cc_sm6350_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_sm6350_gdscs), +}; + +static const struct of_device_id video_cc_sm6350_match_table[] = { + { .compatible = "qcom,sm6350-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_sm6350_match_table); + +static int video_cc_sm6350_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &video_cc_sm6350_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_fabia_pll_configure(&video_pll0, regmap, &video_pll0_config); + + /* Keep some clocks always-on */ + qcom_branch_set_clk_en(regmap, 0x7018); /* VIDEO_CC_XO_CLK */ + + return qcom_cc_really_probe(&pdev->dev, &video_cc_sm6350_desc, regmap); +} + +static struct platform_driver video_cc_sm6350_driver = { + .probe = video_cc_sm6350_probe, + .driver = { + .name = "video_cc-sm6350", + .of_match_table = video_cc_sm6350_match_table, + }, +}; + +module_platform_driver(video_cc_sm6350_driver); + +MODULE_DESCRIPTION("QTI VIDEO_CC SM6350 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/videocc-sm7150.c b/drivers/clk/qcom/videocc-sm7150.c index 14ef7f561753..b6912560ef9b 100644 --- a/drivers/clk/qcom/videocc-sm7150.c +++ b/drivers/clk/qcom/videocc-sm7150.c @@ -271,7 +271,7 @@ static struct gdsc vcodec0_gdsc = { }, .cxcs = (unsigned int []){ 0x890, 0x9ec }, .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR, .pwrsts = PWRSTS_OFF_ON, }; @@ -282,7 +282,7 @@ static struct gdsc vcodec1_gdsc = { }, .cxcs = (unsigned int []){ 0x8d0, 0xa0c }, .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/videocc-sm8150.c b/drivers/clk/qcom/videocc-sm8150.c index daab3237eec1..3024f6fc89c8 100644 --- a/drivers/clk/qcom/videocc-sm8150.c +++ b/drivers/clk/qcom/videocc-sm8150.c @@ -179,7 +179,7 @@ static struct gdsc vcodec0_gdsc = { .pd = { .name = "vcodec0_gdsc", }, - .flags = HW_CTRL, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; @@ -188,7 +188,7 @@ static struct gdsc vcodec1_gdsc = { .pd = { .name = "vcodec1_gdsc", }, - .flags = HW_CTRL, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; static struct clk_regmap *video_cc_sm8150_clocks[] = { diff --git a/drivers/clk/qcom/videocc-sm8450.c b/drivers/clk/qcom/videocc-sm8450.c index 2e11dcffb664..dc168ce199cc 100644 --- a/drivers/clk/qcom/videocc-sm8450.c +++ b/drivers/clk/qcom/videocc-sm8450.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -63,6 +62,7 @@ static const struct alpha_pll_config sm8475_video_cc_pll0_config = { static struct clk_alpha_pll video_cc_pll0 = { .offset = 0x0, + .config = &video_cc_pll0_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -106,6 +106,7 @@ static const struct alpha_pll_config sm8475_video_cc_pll1_config = { static struct clk_alpha_pll video_cc_pll1 = { .offset = 0x1000, + .config = &video_cc_pll1_config, .vco_table = lucid_evo_vco, .num_vco = ARRAY_SIZE(lucid_evo_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], @@ -347,7 +348,7 @@ static struct gdsc video_cc_mvs0_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .parent = &video_cc_mvs0c_gdsc.pd, - .flags = RETAIN_FF_ENABLE | HW_CTRL, + .flags = HW_CTRL_TRIGGER | RETAIN_FF_ENABLE, }; static struct gdsc video_cc_mvs1c_gdsc = { @@ -372,7 +373,7 @@ static struct gdsc video_cc_mvs1_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .parent = &video_cc_mvs1c_gdsc.pd, - .flags = RETAIN_FF_ENABLE | HW_CTRL, + .flags = HW_CTRL_TRIGGER | RETAIN_FF_ENABLE, }; static struct clk_regmap *video_cc_sm8450_clocks[] = { @@ -407,6 +408,17 @@ static const struct qcom_reset_map video_cc_sm8450_resets[] = { [VIDEO_CC_MVS1C_CLK_ARES] = { .reg = 0x808c, .bit = 2, .udelay = 1000 }, }; +static struct clk_alpha_pll *video_cc_sm8450_plls[] = { + &video_cc_pll0, + &video_cc_pll1, +}; + +static u32 video_cc_sm8450_critical_cbcrs[] = { + 0x80e4, /* VIDEO_CC_AHB_CLK */ + 0x8114, /* VIDEO_CC_XO_CLK */ + 0x8130, /* VIDEO_CC_SLEEP_CLK */ +}; + static const struct regmap_config video_cc_sm8450_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -415,6 +427,13 @@ static const struct regmap_config video_cc_sm8450_regmap_config = { .fast_io = true, }; +static 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, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_sm8450_critical_cbcrs), +}; + static const struct qcom_cc_desc video_cc_sm8450_desc = { .config = &video_cc_sm8450_regmap_config, .clks = video_cc_sm8450_clocks, @@ -423,6 +442,8 @@ static const struct qcom_cc_desc video_cc_sm8450_desc = { .num_resets = ARRAY_SIZE(video_cc_sm8450_resets), .gdscs = video_cc_sm8450_gdscs, .num_gdscs = ARRAY_SIZE(video_cc_sm8450_gdscs), + .use_rpm = true, + .driver_data = &video_cc_sm8450_driver_data, }; static const struct of_device_id video_cc_sm8450_match_table[] = { @@ -434,23 +455,6 @@ MODULE_DEVICE_TABLE(of, video_cc_sm8450_match_table); static int video_cc_sm8450_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, &video_cc_sm8450_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); - } - if (of_device_is_compatible(pdev->dev.of_node, "qcom,sm8475-videocc")) { /* Update VideoCC PLL0 */ video_cc_pll0.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE]; @@ -458,23 +462,11 @@ static int video_cc_sm8450_probe(struct platform_device *pdev) /* Update VideoCC PLL1 */ video_cc_pll1.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE]; - clk_lucid_ole_pll_configure(&video_cc_pll0, regmap, &sm8475_video_cc_pll0_config); - clk_lucid_ole_pll_configure(&video_cc_pll1, regmap, &sm8475_video_cc_pll1_config); - } else { - clk_lucid_evo_pll_configure(&video_cc_pll0, regmap, &video_cc_pll0_config); - clk_lucid_evo_pll_configure(&video_cc_pll1, regmap, &video_cc_pll1_config); + video_cc_pll0.config = &sm8475_video_cc_pll0_config; + video_cc_pll1.config = &sm8475_video_cc_pll1_config; } - /* Keep some clocks always-on */ - qcom_branch_set_clk_en(regmap, 0x80e4); /* VIDEO_CC_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0x8130); /* VIDEO_CC_SLEEP_CLK */ - qcom_branch_set_clk_en(regmap, 0x8114); /* VIDEO_CC_XO_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &video_cc_sm8450_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &video_cc_sm8450_desc); } static struct platform_driver video_cc_sm8450_driver = { diff --git a/drivers/clk/qcom/videocc-sm8550.c b/drivers/clk/qcom/videocc-sm8550.c index fcfe0cade6d0..32a6505abe26 100644 --- a/drivers/clk/qcom/videocc-sm8550.c +++ b/drivers/clk/qcom/videocc-sm8550.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -51,6 +50,7 @@ static struct alpha_pll_config video_cc_pll0_config = { static struct clk_alpha_pll video_cc_pll0 = { .offset = 0x0, + .config = &video_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], @@ -82,6 +82,7 @@ static struct alpha_pll_config video_cc_pll1_config = { static struct clk_alpha_pll video_cc_pll1 = { .offset = 0x1000, + .config = &video_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], @@ -144,6 +145,16 @@ static const struct freq_tbl ftbl_video_cc_mvs0_clk_src_sm8650[] = { { } }; +static const struct freq_tbl ftbl_video_cc_mvs0_clk_src_x1e80100[] = { + F(576000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + 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(1443000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + static struct clk_rcg2 video_cc_mvs0_clk_src = { .cmd_rcgr = 0x8000, .mnd_width = 0, @@ -176,6 +187,15 @@ static const struct freq_tbl ftbl_video_cc_mvs1_clk_src_sm8650[] = { { } }; +static const struct freq_tbl ftbl_video_cc_mvs1_clk_src_x1e80100[] = { + F(840000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1050000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1350000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1500000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1650000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + static struct clk_rcg2 video_cc_mvs1_clk_src = { .cmd_rcgr = 0x8018, .mnd_width = 0, @@ -511,6 +531,23 @@ static const struct qcom_reset_map video_cc_sm8550_resets[] = { [VIDEO_CC_XO_CLK_ARES] = { .reg = 0x8124, .bit = 2, .udelay = 100 }, }; +static struct clk_alpha_pll *video_cc_sm8550_plls[] = { + &video_cc_pll0, + &video_cc_pll1, +}; + +static 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[] = { + 0x80f4, /* VIDEO_CC_AHB_CLK */ + 0x8124, /* VIDEO_CC_XO_CLK */ + 0x8150, /* VIDEO_CC_SLEEP_CLK */ +}; + static const struct regmap_config video_cc_sm8550_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -519,6 +556,13 @@ static const struct regmap_config video_cc_sm8550_regmap_config = { .fast_io = true, }; +static struct qcom_cc_driver_data video_cc_sm8550_driver_data = { + .alpha_plls = video_cc_sm8550_plls, + .num_alpha_plls = ARRAY_SIZE(video_cc_sm8550_plls), + .clk_cbcrs = video_cc_sm8550_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_sm8550_critical_cbcrs), +}; + static const struct qcom_cc_desc video_cc_sm8550_desc = { .config = &video_cc_sm8550_regmap_config, .clks = video_cc_sm8550_clocks, @@ -527,37 +571,30 @@ static const struct qcom_cc_desc video_cc_sm8550_desc = { .num_resets = ARRAY_SIZE(video_cc_sm8550_resets), .gdscs = video_cc_sm8550_gdscs, .num_gdscs = ARRAY_SIZE(video_cc_sm8550_gdscs), + .use_rpm = true, + .driver_data = &video_cc_sm8550_driver_data, }; static const struct of_device_id video_cc_sm8550_match_table[] = { { .compatible = "qcom,sm8550-videocc" }, { .compatible = "qcom,sm8650-videocc" }, + { .compatible = "qcom,x1e80100-videocc" }, { } }; MODULE_DEVICE_TABLE(of, video_cc_sm8550_match_table); static int video_cc_sm8550_probe(struct platform_device *pdev) { - struct regmap *regmap; - int ret; - u32 sleep_clk_offset = 0x8140; - - 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, &video_cc_sm8550_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); + if (of_device_is_compatible(pdev->dev.of_node, "qcom,x1e80100-videocc")) { + video_cc_pll0_config.l = 0x1e; + video_cc_pll0_config.alpha = 0x0000; + video_cc_pll1_config.l = 0x2b; + video_cc_pll1_config.alpha = 0xc000; + video_cc_mvs0_clk_src.freq_tbl = ftbl_video_cc_mvs0_clk_src_x1e80100; + video_cc_mvs1_clk_src.freq_tbl = ftbl_video_cc_mvs1_clk_src_x1e80100; } if (of_device_is_compatible(pdev->dev.of_node, "qcom,sm8650-videocc")) { - sleep_clk_offset = 0x8150; video_cc_pll0_config.l = 0x1e; video_cc_pll0_config.alpha = 0xa000; video_cc_pll1_config.l = 0x2b; @@ -569,21 +606,13 @@ static int video_cc_sm8550_probe(struct platform_device *pdev) video_cc_sm8550_clocks[VIDEO_CC_MVS1_SHIFT_CLK] = &video_cc_mvs1_shift_clk.clkr; video_cc_sm8550_clocks[VIDEO_CC_MVS1C_SHIFT_CLK] = &video_cc_mvs1c_shift_clk.clkr; video_cc_sm8550_clocks[VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr; + + video_cc_sm8550_driver_data.clk_cbcrs = video_cc_sm8650_critical_cbcrs; + video_cc_sm8550_driver_data.num_clk_cbcrs = + ARRAY_SIZE(video_cc_sm8650_critical_cbcrs); } - clk_lucid_ole_pll_configure(&video_cc_pll0, regmap, &video_cc_pll0_config); - clk_lucid_ole_pll_configure(&video_cc_pll1, regmap, &video_cc_pll1_config); - - /* Keep some clocks always-on */ - qcom_branch_set_clk_en(regmap, 0x80f4); /* VIDEO_CC_AHB_CLK */ - qcom_branch_set_clk_en(regmap, sleep_clk_offset); /* VIDEO_CC_SLEEP_CLK */ - qcom_branch_set_clk_en(regmap, 0x8124); /* VIDEO_CC_XO_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &video_cc_sm8550_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &video_cc_sm8550_desc); } static struct platform_driver video_cc_sm8550_driver = { diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 50c20119d12a..6a5a04664990 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -43,6 +43,8 @@ config CLK_RENESAS select CLK_R9A09G047 if ARCH_R9A09G047 select CLK_R9A09G056 if ARCH_R9A09G056 select CLK_R9A09G057 if ARCH_R9A09G057 + select CLK_R9A09G077 if ARCH_R9A09G077 + select CLK_R9A09G087 if ARCH_R9A09G087 select CLK_SH73A0 if ARCH_SH73A0 if CLK_RENESAS @@ -208,6 +210,14 @@ config CLK_R9A09G057 bool "RZ/V2H(P) clock support" if COMPILE_TEST select CLK_RZV2H +config CLK_R9A09G077 + bool "RZ/T2H clock support" if COMPILE_TEST + select CLK_RENESAS_CPG_MSSR + +config CLK_R9A09G087 + bool "RZ/N2H clock support" if COMPILE_TEST + select CLK_RENESAS_CPG_MSSR + config CLK_SH73A0 bool "SH-Mobile AG5 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index f9075bca6e95..d28eb276a153 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -40,6 +40,8 @@ obj-$(CONFIG_CLK_R9A09G011) += r9a09g011-cpg.o obj-$(CONFIG_CLK_R9A09G047) += r9a09g047-cpg.o obj-$(CONFIG_CLK_R9A09G056) += r9a09g056-cpg.o obj-$(CONFIG_CLK_R9A09G057) += r9a09g057-cpg.o +obj-$(CONFIG_CLK_R9A09G077) += r9a09g077-cpg.o +obj-$(CONFIG_CLK_R9A09G087) += r9a09g077-cpg.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o # Family diff --git a/drivers/clk/renesas/r7s9210-cpg-mssr.c b/drivers/clk/renesas/r7s9210-cpg-mssr.c index e1812867a6da..a8ed87c11ba1 100644 --- a/drivers/clk/renesas/r7s9210-cpg-mssr.c +++ b/drivers/clk/renesas/r7s9210-cpg-mssr.c @@ -159,12 +159,13 @@ static void __init r7s9210_update_clk_table(struct clk *extal_clk, static struct clk * __init rza2_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers) + struct cpg_mssr_pub *pub) { - struct clk *parent; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; unsigned int mult = 1; unsigned int div = 1; + struct clk *parent; parent = clks[core->parent]; if (IS_ERR(parent)) diff --git a/drivers/clk/renesas/r8a77970-cpg-mssr.c b/drivers/clk/renesas/r8a77970-cpg-mssr.c index 3cec0f501b94..e2bda2c10730 100644 --- a/drivers/clk/renesas/r8a77970-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77970-cpg-mssr.c @@ -219,10 +219,11 @@ static int __init r8a77970_cpg_mssr_init(struct device *dev) static struct clk * __init r8a77970_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers) + struct cpg_mssr_pub *pub) { const struct clk_div_table *table; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; const struct clk *parent; unsigned int shift; @@ -236,8 +237,7 @@ static struct clk * __init r8a77970_cpg_clk_register(struct device *dev, shift = 4; break; default: - return rcar_gen3_cpg_clk_register(dev, core, info, clks, base, - notifiers); + return rcar_gen3_cpg_clk_register(dev, core, info, pub); } parent = clks[core->parent]; diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c index fce2eecfa8c0..02dc5cecfd8d 100644 --- a/drivers/clk/renesas/r9a07g043-cpg.c +++ b/drivers/clk/renesas/r9a07g043-cpg.c @@ -164,143 +164,143 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = { static const struct rzg2l_mod_clk r9a07g043_mod_clks[] = { #ifdef CONFIG_ARM64 DEF_MOD("gic", R9A07G043_GIC600_GICCLK, R9A07G043_CLK_P1, - 0x514, 0), + 0x514, 0, 0), DEF_MOD("ia55_pclk", R9A07G043_IA55_PCLK, R9A07G043_CLK_P2, - 0x518, 0), + 0x518, 0, 0), DEF_MOD("ia55_clk", R9A07G043_IA55_CLK, R9A07G043_CLK_P1, - 0x518, 1), + 0x518, 1, 0), #endif #ifdef CONFIG_RISCV DEF_MOD("iax45_pclk", R9A07G043_IAX45_PCLK, R9A07G043_CLK_P2, - 0x518, 0), + 0x518, 0, 0), DEF_MOD("iax45_clk", R9A07G043_IAX45_CLK, R9A07G043_CLK_P1, - 0x518, 1), + 0x518, 1, 0), #endif DEF_MOD("dmac_aclk", R9A07G043_DMAC_ACLK, R9A07G043_CLK_P1, - 0x52c, 0), + 0x52c, 0, 0), DEF_MOD("dmac_pclk", R9A07G043_DMAC_PCLK, CLK_P1_DIV2, - 0x52c, 1), + 0x52c, 1, 0), DEF_MOD("ostm0_pclk", R9A07G043_OSTM0_PCLK, R9A07G043_CLK_P0, - 0x534, 0), + 0x534, 0, 0), DEF_MOD("ostm1_pclk", R9A07G043_OSTM1_PCLK, R9A07G043_CLK_P0, - 0x534, 1), + 0x534, 1, 0), DEF_MOD("ostm2_pclk", R9A07G043_OSTM2_PCLK, R9A07G043_CLK_P0, - 0x534, 2), + 0x534, 2, 0), DEF_MOD("mtu_x_mck", R9A07G043_MTU_X_MCK_MTU3, R9A07G043_CLK_P0, - 0x538, 0), + 0x538, 0, 0), DEF_MOD("wdt0_pclk", R9A07G043_WDT0_PCLK, R9A07G043_CLK_P0, - 0x548, 0), + 0x548, 0, 0), DEF_MOD("wdt0_clk", R9A07G043_WDT0_CLK, R9A07G043_OSCCLK, - 0x548, 1), + 0x548, 1, 0), DEF_MOD("spi_clk2", R9A07G043_SPI_CLK2, R9A07G043_CLK_SPI1, - 0x550, 0), + 0x550, 0, 0), DEF_MOD("spi_clk", R9A07G043_SPI_CLK, R9A07G043_CLK_SPI0, - 0x550, 1), + 0x550, 1, 0), DEF_MOD("sdhi0_imclk", R9A07G043_SDHI0_IMCLK, CLK_SD0_DIV4, - 0x554, 0), + 0x554, 0, 0), DEF_MOD("sdhi0_imclk2", R9A07G043_SDHI0_IMCLK2, CLK_SD0_DIV4, - 0x554, 1), + 0x554, 1, 0), DEF_MOD("sdhi0_clk_hs", R9A07G043_SDHI0_CLK_HS, R9A07G043_CLK_SD0, - 0x554, 2), + 0x554, 2, 0), DEF_MOD("sdhi0_aclk", R9A07G043_SDHI0_ACLK, R9A07G043_CLK_P1, - 0x554, 3), + 0x554, 3, 0), DEF_MOD("sdhi1_imclk", R9A07G043_SDHI1_IMCLK, CLK_SD1_DIV4, - 0x554, 4), + 0x554, 4, 0), DEF_MOD("sdhi1_imclk2", R9A07G043_SDHI1_IMCLK2, CLK_SD1_DIV4, - 0x554, 5), + 0x554, 5, 0), DEF_MOD("sdhi1_clk_hs", R9A07G043_SDHI1_CLK_HS, R9A07G043_CLK_SD1, - 0x554, 6), + 0x554, 6, 0), DEF_MOD("sdhi1_aclk", R9A07G043_SDHI1_ACLK, R9A07G043_CLK_P1, - 0x554, 7), + 0x554, 7, 0), #ifdef CONFIG_ARM64 DEF_MOD("cru_sysclk", R9A07G043_CRU_SYSCLK, CLK_M2_DIV2, - 0x564, 0), + 0x564, 0, 0), DEF_MOD("cru_vclk", R9A07G043_CRU_VCLK, R9A07G043_CLK_M2, - 0x564, 1), + 0x564, 1, 0), DEF_MOD("cru_pclk", R9A07G043_CRU_PCLK, R9A07G043_CLK_ZT, - 0x564, 2), + 0x564, 2, 0), DEF_MOD("cru_aclk", R9A07G043_CRU_ACLK, R9A07G043_CLK_M0, - 0x564, 3), + 0x564, 3, 0), DEF_COUPLED("lcdc_clk_a", R9A07G043_LCDC_CLK_A, R9A07G043_CLK_M0, - 0x56c, 0), + 0x56c, 0, 0), DEF_COUPLED("lcdc_clk_p", R9A07G043_LCDC_CLK_P, R9A07G043_CLK_ZT, - 0x56c, 0), + 0x56c, 0, 0), DEF_MOD("lcdc_clk_d", R9A07G043_LCDC_CLK_D, R9A07G043_CLK_M3, - 0x56c, 1), + 0x56c, 1, 0), #endif DEF_MOD("ssi0_pclk", R9A07G043_SSI0_PCLK2, R9A07G043_CLK_P0, - 0x570, 0), + 0x570, 0, 0), DEF_MOD("ssi0_sfr", R9A07G043_SSI0_PCLK_SFR, R9A07G043_CLK_P0, - 0x570, 1), + 0x570, 1, 0), DEF_MOD("ssi1_pclk", R9A07G043_SSI1_PCLK2, R9A07G043_CLK_P0, - 0x570, 2), + 0x570, 2, 0), DEF_MOD("ssi1_sfr", R9A07G043_SSI1_PCLK_SFR, R9A07G043_CLK_P0, - 0x570, 3), + 0x570, 3, 0), DEF_MOD("ssi2_pclk", R9A07G043_SSI2_PCLK2, R9A07G043_CLK_P0, - 0x570, 4), + 0x570, 4, 0), DEF_MOD("ssi2_sfr", R9A07G043_SSI2_PCLK_SFR, R9A07G043_CLK_P0, - 0x570, 5), + 0x570, 5, 0), DEF_MOD("ssi3_pclk", R9A07G043_SSI3_PCLK2, R9A07G043_CLK_P0, - 0x570, 6), + 0x570, 6, 0), DEF_MOD("ssi3_sfr", R9A07G043_SSI3_PCLK_SFR, R9A07G043_CLK_P0, - 0x570, 7), + 0x570, 7, 0), DEF_MOD("usb0_host", R9A07G043_USB_U2H0_HCLK, R9A07G043_CLK_P1, - 0x578, 0), + 0x578, 0, 0), DEF_MOD("usb1_host", R9A07G043_USB_U2H1_HCLK, R9A07G043_CLK_P1, - 0x578, 1), + 0x578, 1, 0), DEF_MOD("usb0_func", R9A07G043_USB_U2P_EXR_CPUCLK, R9A07G043_CLK_P1, - 0x578, 2), + 0x578, 2, 0), DEF_MOD("usb_pclk", R9A07G043_USB_PCLK, R9A07G043_CLK_P1, - 0x578, 3), + 0x578, 3, 0), DEF_COUPLED("eth0_axi", R9A07G043_ETH0_CLK_AXI, R9A07G043_CLK_M0, - 0x57c, 0), + 0x57c, 0, 0), DEF_COUPLED("eth0_chi", R9A07G043_ETH0_CLK_CHI, R9A07G043_CLK_ZT, - 0x57c, 0), + 0x57c, 0, 0), DEF_COUPLED("eth1_axi", R9A07G043_ETH1_CLK_AXI, R9A07G043_CLK_M0, - 0x57c, 1), + 0x57c, 1, 0), DEF_COUPLED("eth1_chi", R9A07G043_ETH1_CLK_CHI, R9A07G043_CLK_ZT, - 0x57c, 1), + 0x57c, 1, 0), DEF_MOD("i2c0", R9A07G043_I2C0_PCLK, R9A07G043_CLK_P0, - 0x580, 0), + 0x580, 0, 0), DEF_MOD("i2c1", R9A07G043_I2C1_PCLK, R9A07G043_CLK_P0, - 0x580, 1), + 0x580, 1, 0), DEF_MOD("i2c2", R9A07G043_I2C2_PCLK, R9A07G043_CLK_P0, - 0x580, 2), + 0x580, 2, 0), DEF_MOD("i2c3", R9A07G043_I2C3_PCLK, R9A07G043_CLK_P0, - 0x580, 3), + 0x580, 3, 0), DEF_MOD("scif0", R9A07G043_SCIF0_CLK_PCK, R9A07G043_CLK_P0, - 0x584, 0), + 0x584, 0, 0), DEF_MOD("scif1", R9A07G043_SCIF1_CLK_PCK, R9A07G043_CLK_P0, - 0x584, 1), + 0x584, 1, 0), DEF_MOD("scif2", R9A07G043_SCIF2_CLK_PCK, R9A07G043_CLK_P0, - 0x584, 2), + 0x584, 2, 0), DEF_MOD("scif3", R9A07G043_SCIF3_CLK_PCK, R9A07G043_CLK_P0, - 0x584, 3), + 0x584, 3, 0), DEF_MOD("scif4", R9A07G043_SCIF4_CLK_PCK, R9A07G043_CLK_P0, - 0x584, 4), + 0x584, 4, 0), DEF_MOD("sci0", R9A07G043_SCI0_CLKP, R9A07G043_CLK_P0, - 0x588, 0), + 0x588, 0, 0), DEF_MOD("sci1", R9A07G043_SCI1_CLKP, R9A07G043_CLK_P0, - 0x588, 1), + 0x588, 1, 0), DEF_MOD("rspi0", R9A07G043_RSPI0_CLKB, R9A07G043_CLK_P0, - 0x590, 0), + 0x590, 0, 0), DEF_MOD("rspi1", R9A07G043_RSPI1_CLKB, R9A07G043_CLK_P0, - 0x590, 1), + 0x590, 1, 0), DEF_MOD("rspi2", R9A07G043_RSPI2_CLKB, R9A07G043_CLK_P0, - 0x590, 2), + 0x590, 2, 0), DEF_MOD("canfd", R9A07G043_CANFD_PCLK, R9A07G043_CLK_P0, - 0x594, 0), + 0x594, 0, 0), DEF_MOD("gpio", R9A07G043_GPIO_HCLK, R9A07G043_OSCCLK, - 0x598, 0), + 0x598, 0, 0), DEF_MOD("adc_adclk", R9A07G043_ADC_ADCLK, R9A07G043_CLK_TSU, - 0x5a8, 0), + 0x5a8, 0, 0), DEF_MOD("adc_pclk", R9A07G043_ADC_PCLK, R9A07G043_CLK_P0, - 0x5a8, 1), + 0x5a8, 1, 0), DEF_MOD("tsu_pclk", R9A07G043_TSU_PCLK, R9A07G043_CLK_TSU, - 0x5ac, 0), + 0x5ac, 0, 0), #ifdef CONFIG_RISCV DEF_MOD("nceplic_aclk", R9A07G043_NCEPLIC_ACLK, R9A07G043_CLK_P1, - 0x608, 0), + 0x608, 0, 0), #endif }; diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c index 77ca3a789568..c851d4eeebbe 100644 --- a/drivers/clk/renesas/r9a07g044-cpg.c +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -242,176 +242,176 @@ static const struct { } mod_clks = { .common = { DEF_MOD("gic", R9A07G044_GIC600_GICCLK, R9A07G044_CLK_P1, - 0x514, 0), + 0x514, 0, 0), DEF_MOD("ia55_pclk", R9A07G044_IA55_PCLK, R9A07G044_CLK_P2, - 0x518, 0), + 0x518, 0, 0), DEF_MOD("ia55_clk", R9A07G044_IA55_CLK, R9A07G044_CLK_P1, - 0x518, 1), + 0x518, 1, 0), DEF_MOD("dmac_aclk", R9A07G044_DMAC_ACLK, R9A07G044_CLK_P1, - 0x52c, 0), + 0x52c, 0, 0), DEF_MOD("dmac_pclk", R9A07G044_DMAC_PCLK, CLK_P1_DIV2, - 0x52c, 1), + 0x52c, 1, 0), DEF_MOD("ostm0_pclk", R9A07G044_OSTM0_PCLK, R9A07G044_CLK_P0, - 0x534, 0), + 0x534, 0, 0), DEF_MOD("ostm1_pclk", R9A07G044_OSTM1_PCLK, R9A07G044_CLK_P0, - 0x534, 1), + 0x534, 1, 0), DEF_MOD("ostm2_pclk", R9A07G044_OSTM2_PCLK, R9A07G044_CLK_P0, - 0x534, 2), + 0x534, 2, 0), DEF_MOD("mtu_x_mck", R9A07G044_MTU_X_MCK_MTU3, R9A07G044_CLK_P0, - 0x538, 0), + 0x538, 0, 0), DEF_MOD("gpt_pclk", R9A07G044_GPT_PCLK, R9A07G044_CLK_P0, - 0x540, 0), + 0x540, 0, 0), DEF_MOD("poeg_a_clkp", R9A07G044_POEG_A_CLKP, R9A07G044_CLK_P0, - 0x544, 0), + 0x544, 0, 0), DEF_MOD("poeg_b_clkp", R9A07G044_POEG_B_CLKP, R9A07G044_CLK_P0, - 0x544, 1), + 0x544, 1, 0), DEF_MOD("poeg_c_clkp", R9A07G044_POEG_C_CLKP, R9A07G044_CLK_P0, - 0x544, 2), + 0x544, 2, 0), DEF_MOD("poeg_d_clkp", R9A07G044_POEG_D_CLKP, R9A07G044_CLK_P0, - 0x544, 3), + 0x544, 3, 0), DEF_MOD("wdt0_pclk", R9A07G044_WDT0_PCLK, R9A07G044_CLK_P0, - 0x548, 0), + 0x548, 0, 0), DEF_MOD("wdt0_clk", R9A07G044_WDT0_CLK, R9A07G044_OSCCLK, - 0x548, 1), + 0x548, 1, 0), DEF_MOD("wdt1_pclk", R9A07G044_WDT1_PCLK, R9A07G044_CLK_P0, - 0x548, 2), + 0x548, 2, 0), DEF_MOD("wdt1_clk", R9A07G044_WDT1_CLK, R9A07G044_OSCCLK, - 0x548, 3), + 0x548, 3, 0), DEF_MOD("spi_clk2", R9A07G044_SPI_CLK2, R9A07G044_CLK_SPI1, - 0x550, 0), + 0x550, 0, 0), DEF_MOD("spi_clk", R9A07G044_SPI_CLK, R9A07G044_CLK_SPI0, - 0x550, 1), + 0x550, 1, 0), DEF_MOD("sdhi0_imclk", R9A07G044_SDHI0_IMCLK, CLK_SD0_DIV4, - 0x554, 0), + 0x554, 0, 0), DEF_MOD("sdhi0_imclk2", R9A07G044_SDHI0_IMCLK2, CLK_SD0_DIV4, - 0x554, 1), + 0x554, 1, 0), DEF_MOD("sdhi0_clk_hs", R9A07G044_SDHI0_CLK_HS, R9A07G044_CLK_SD0, - 0x554, 2), + 0x554, 2, 0), DEF_MOD("sdhi0_aclk", R9A07G044_SDHI0_ACLK, R9A07G044_CLK_P1, - 0x554, 3), + 0x554, 3, 0), DEF_MOD("sdhi1_imclk", R9A07G044_SDHI1_IMCLK, CLK_SD1_DIV4, - 0x554, 4), + 0x554, 4, 0), DEF_MOD("sdhi1_imclk2", R9A07G044_SDHI1_IMCLK2, CLK_SD1_DIV4, - 0x554, 5), + 0x554, 5, 0), DEF_MOD("sdhi1_clk_hs", R9A07G044_SDHI1_CLK_HS, R9A07G044_CLK_SD1, - 0x554, 6), + 0x554, 6, 0), DEF_MOD("sdhi1_aclk", R9A07G044_SDHI1_ACLK, R9A07G044_CLK_P1, - 0x554, 7), + 0x554, 7, 0), DEF_MOD("gpu_clk", R9A07G044_GPU_CLK, R9A07G044_CLK_G, - 0x558, 0), + 0x558, 0, 0), DEF_MOD("gpu_axi_clk", R9A07G044_GPU_AXI_CLK, R9A07G044_CLK_P1, - 0x558, 1), + 0x558, 1, 0), DEF_MOD("gpu_ace_clk", R9A07G044_GPU_ACE_CLK, R9A07G044_CLK_P1, - 0x558, 2), + 0x558, 2, 0), DEF_MOD("cru_sysclk", R9A07G044_CRU_SYSCLK, CLK_M2_DIV2, - 0x564, 0), + 0x564, 0, 0), DEF_MOD("cru_vclk", R9A07G044_CRU_VCLK, R9A07G044_CLK_M2, - 0x564, 1), + 0x564, 1, 0), DEF_MOD("cru_pclk", R9A07G044_CRU_PCLK, R9A07G044_CLK_ZT, - 0x564, 2), + 0x564, 2, 0), DEF_MOD("cru_aclk", R9A07G044_CRU_ACLK, R9A07G044_CLK_M0, - 0x564, 3), + 0x564, 3, 0), DEF_MOD("dsi_pll_clk", R9A07G044_MIPI_DSI_PLLCLK, R9A07G044_CLK_M1, - 0x568, 0), + 0x568, 0, 0), DEF_MOD("dsi_sys_clk", R9A07G044_MIPI_DSI_SYSCLK, CLK_M2_DIV2, - 0x568, 1), + 0x568, 1, 0), DEF_MOD("dsi_aclk", R9A07G044_MIPI_DSI_ACLK, R9A07G044_CLK_P1, - 0x568, 2), + 0x568, 2, 0), DEF_MOD("dsi_pclk", R9A07G044_MIPI_DSI_PCLK, R9A07G044_CLK_P2, - 0x568, 3), + 0x568, 3, 0), DEF_MOD("dsi_vclk", R9A07G044_MIPI_DSI_VCLK, R9A07G044_CLK_M3, - 0x568, 4), + 0x568, 4, 0), DEF_MOD("dsi_lpclk", R9A07G044_MIPI_DSI_LPCLK, R9A07G044_CLK_M4, - 0x568, 5), + 0x568, 5, 0), DEF_COUPLED("lcdc_a", R9A07G044_LCDC_CLK_A, R9A07G044_CLK_M0, - 0x56c, 0), + 0x56c, 0, 0), DEF_COUPLED("lcdc_p", R9A07G044_LCDC_CLK_P, R9A07G044_CLK_ZT, - 0x56c, 0), + 0x56c, 0, 0), DEF_MOD("lcdc_clk_d", R9A07G044_LCDC_CLK_D, R9A07G044_CLK_M3, - 0x56c, 1), + 0x56c, 1, 0), DEF_MOD("ssi0_pclk", R9A07G044_SSI0_PCLK2, R9A07G044_CLK_P0, - 0x570, 0), + 0x570, 0, 0), DEF_MOD("ssi0_sfr", R9A07G044_SSI0_PCLK_SFR, R9A07G044_CLK_P0, - 0x570, 1), + 0x570, 1, 0), DEF_MOD("ssi1_pclk", R9A07G044_SSI1_PCLK2, R9A07G044_CLK_P0, - 0x570, 2), + 0x570, 2, 0), DEF_MOD("ssi1_sfr", R9A07G044_SSI1_PCLK_SFR, R9A07G044_CLK_P0, - 0x570, 3), + 0x570, 3, 0), DEF_MOD("ssi2_pclk", R9A07G044_SSI2_PCLK2, R9A07G044_CLK_P0, - 0x570, 4), + 0x570, 4, 0), DEF_MOD("ssi2_sfr", R9A07G044_SSI2_PCLK_SFR, R9A07G044_CLK_P0, - 0x570, 5), + 0x570, 5, 0), DEF_MOD("ssi3_pclk", R9A07G044_SSI3_PCLK2, R9A07G044_CLK_P0, - 0x570, 6), + 0x570, 6, 0), DEF_MOD("ssi3_sfr", R9A07G044_SSI3_PCLK_SFR, R9A07G044_CLK_P0, - 0x570, 7), + 0x570, 7, 0), DEF_MOD("usb0_host", R9A07G044_USB_U2H0_HCLK, R9A07G044_CLK_P1, - 0x578, 0), + 0x578, 0, 0), DEF_MOD("usb1_host", R9A07G044_USB_U2H1_HCLK, R9A07G044_CLK_P1, - 0x578, 1), + 0x578, 1, 0), DEF_MOD("usb0_func", R9A07G044_USB_U2P_EXR_CPUCLK, R9A07G044_CLK_P1, - 0x578, 2), + 0x578, 2, 0), DEF_MOD("usb_pclk", R9A07G044_USB_PCLK, R9A07G044_CLK_P1, - 0x578, 3), + 0x578, 3, 0), DEF_COUPLED("eth0_axi", R9A07G044_ETH0_CLK_AXI, R9A07G044_CLK_M0, - 0x57c, 0), + 0x57c, 0, 0), DEF_COUPLED("eth0_chi", R9A07G044_ETH0_CLK_CHI, R9A07G044_CLK_ZT, - 0x57c, 0), + 0x57c, 0, 0), DEF_COUPLED("eth1_axi", R9A07G044_ETH1_CLK_AXI, R9A07G044_CLK_M0, - 0x57c, 1), + 0x57c, 1, 0), DEF_COUPLED("eth1_chi", R9A07G044_ETH1_CLK_CHI, R9A07G044_CLK_ZT, - 0x57c, 1), + 0x57c, 1, 0), DEF_MOD("i2c0", R9A07G044_I2C0_PCLK, R9A07G044_CLK_P0, - 0x580, 0), + 0x580, 0, 0), DEF_MOD("i2c1", R9A07G044_I2C1_PCLK, R9A07G044_CLK_P0, - 0x580, 1), + 0x580, 1, 0), DEF_MOD("i2c2", R9A07G044_I2C2_PCLK, R9A07G044_CLK_P0, - 0x580, 2), + 0x580, 2, 0), DEF_MOD("i2c3", R9A07G044_I2C3_PCLK, R9A07G044_CLK_P0, - 0x580, 3), + 0x580, 3, 0), DEF_MOD("scif0", R9A07G044_SCIF0_CLK_PCK, R9A07G044_CLK_P0, - 0x584, 0), + 0x584, 0, 0), DEF_MOD("scif1", R9A07G044_SCIF1_CLK_PCK, R9A07G044_CLK_P0, - 0x584, 1), + 0x584, 1, 0), DEF_MOD("scif2", R9A07G044_SCIF2_CLK_PCK, R9A07G044_CLK_P0, - 0x584, 2), + 0x584, 2, 0), DEF_MOD("scif3", R9A07G044_SCIF3_CLK_PCK, R9A07G044_CLK_P0, - 0x584, 3), + 0x584, 3, 0), DEF_MOD("scif4", R9A07G044_SCIF4_CLK_PCK, R9A07G044_CLK_P0, - 0x584, 4), + 0x584, 4, 0), DEF_MOD("sci0", R9A07G044_SCI0_CLKP, R9A07G044_CLK_P0, - 0x588, 0), + 0x588, 0, 0), DEF_MOD("sci1", R9A07G044_SCI1_CLKP, R9A07G044_CLK_P0, - 0x588, 1), + 0x588, 1, 0), DEF_MOD("rspi0", R9A07G044_RSPI0_CLKB, R9A07G044_CLK_P0, - 0x590, 0), + 0x590, 0, 0), DEF_MOD("rspi1", R9A07G044_RSPI1_CLKB, R9A07G044_CLK_P0, - 0x590, 1), + 0x590, 1, 0), DEF_MOD("rspi2", R9A07G044_RSPI2_CLKB, R9A07G044_CLK_P0, - 0x590, 2), + 0x590, 2, 0), DEF_MOD("canfd", R9A07G044_CANFD_PCLK, R9A07G044_CLK_P0, - 0x594, 0), + 0x594, 0, 0), DEF_MOD("gpio", R9A07G044_GPIO_HCLK, R9A07G044_OSCCLK, - 0x598, 0), + 0x598, 0, 0), DEF_MOD("adc_adclk", R9A07G044_ADC_ADCLK, R9A07G044_CLK_TSU, - 0x5a8, 0), + 0x5a8, 0, 0), DEF_MOD("adc_pclk", R9A07G044_ADC_PCLK, R9A07G044_CLK_P0, - 0x5a8, 1), + 0x5a8, 1, 0), DEF_MOD("tsu_pclk", R9A07G044_TSU_PCLK, R9A07G044_CLK_TSU, - 0x5ac, 0), + 0x5ac, 0, 0), }, #ifdef CONFIG_CLK_R9A07G054 .drp = { DEF_MOD("stpai_initclk", R9A07G054_STPAI_INITCLK, R9A07G044_OSCCLK, - 0x5e8, 0), + 0x5e8, 0, 0), DEF_MOD("stpai_aclk", R9A07G054_STPAI_ACLK, R9A07G044_CLK_P1, - 0x5e8, 1), + 0x5e8, 1, 0), DEF_MOD("stpai_mclk", R9A07G054_STPAI_MCLK, R9A07G054_CLK_DRP_M, - 0x5e8, 2), + 0x5e8, 2, 0), DEF_MOD("stpai_dclkin", R9A07G054_STPAI_DCLKIN, R9A07G054_CLK_DRP_D, - 0x5e8, 3), + 0x5e8, 3, 0), DEF_MOD("stpai_aclk_drp", R9A07G054_STPAI_ACLK_DRP, R9A07G054_CLK_DRP_A, - 0x5e8, 4), + 0x5e8, 4, 0), }, #endif }; diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 4035f3443598..ed0661997928 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -192,58 +192,107 @@ static const struct cpg_core_clk r9a08g045_core_clks[] __initconst = { }; static const struct rzg2l_mod_clk r9a08g045_mod_clks[] = { - DEF_MOD("gic_gicclk", R9A08G045_GIC600_GICCLK, R9A08G045_CLK_P1, 0x514, 0), - DEF_MOD("ia55_pclk", R9A08G045_IA55_PCLK, R9A08G045_CLK_P2, 0x518, 0), - DEF_MOD("ia55_clk", R9A08G045_IA55_CLK, R9A08G045_CLK_P1, 0x518, 1), - DEF_MOD("dmac_aclk", R9A08G045_DMAC_ACLK, R9A08G045_CLK_P3, 0x52c, 0), - DEF_MOD("dmac_pclk", R9A08G045_DMAC_PCLK, CLK_P3_DIV2, 0x52c, 1), - DEF_MOD("wdt0_pclk", R9A08G045_WDT0_PCLK, R9A08G045_CLK_P0, 0x548, 0), - DEF_MOD("wdt0_clk", R9A08G045_WDT0_CLK, R9A08G045_OSCCLK, 0x548, 1), - DEF_MOD("sdhi0_imclk", R9A08G045_SDHI0_IMCLK, CLK_SD0_DIV4, 0x554, 0), - DEF_MOD("sdhi0_imclk2", R9A08G045_SDHI0_IMCLK2, CLK_SD0_DIV4, 0x554, 1), - DEF_MOD("sdhi0_clk_hs", R9A08G045_SDHI0_CLK_HS, R9A08G045_CLK_SD0, 0x554, 2), - DEF_MOD("sdhi0_aclk", R9A08G045_SDHI0_ACLK, R9A08G045_CLK_P1, 0x554, 3), - DEF_MOD("sdhi1_imclk", R9A08G045_SDHI1_IMCLK, CLK_SD1_DIV4, 0x554, 4), - DEF_MOD("sdhi1_imclk2", R9A08G045_SDHI1_IMCLK2, CLK_SD1_DIV4, 0x554, 5), - DEF_MOD("sdhi1_clk_hs", R9A08G045_SDHI1_CLK_HS, R9A08G045_CLK_SD1, 0x554, 6), - DEF_MOD("sdhi1_aclk", R9A08G045_SDHI1_ACLK, R9A08G045_CLK_P1, 0x554, 7), - DEF_MOD("sdhi2_imclk", R9A08G045_SDHI2_IMCLK, CLK_SD2_DIV4, 0x554, 8), - DEF_MOD("sdhi2_imclk2", R9A08G045_SDHI2_IMCLK2, CLK_SD2_DIV4, 0x554, 9), - DEF_MOD("sdhi2_clk_hs", R9A08G045_SDHI2_CLK_HS, R9A08G045_CLK_SD2, 0x554, 10), - DEF_MOD("sdhi2_aclk", R9A08G045_SDHI2_ACLK, R9A08G045_CLK_P1, 0x554, 11), - DEF_MOD("ssi0_pclk2", R9A08G045_SSI0_PCLK2, R9A08G045_CLK_P0, 0x570, 0), - DEF_MOD("ssi0_sfr", R9A08G045_SSI0_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 1), - DEF_MOD("ssi1_pclk2", R9A08G045_SSI1_PCLK2, R9A08G045_CLK_P0, 0x570, 2), - DEF_MOD("ssi1_sfr", R9A08G045_SSI1_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 3), - DEF_MOD("ssi2_pclk2", R9A08G045_SSI2_PCLK2, R9A08G045_CLK_P0, 0x570, 4), - DEF_MOD("ssi2_sfr", R9A08G045_SSI2_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 5), - DEF_MOD("ssi3_pclk2", R9A08G045_SSI3_PCLK2, R9A08G045_CLK_P0, 0x570, 6), - DEF_MOD("ssi3_sfr", R9A08G045_SSI3_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 7), - DEF_MOD("usb0_host", R9A08G045_USB_U2H0_HCLK, R9A08G045_CLK_P1, 0x578, 0), - DEF_MOD("usb1_host", R9A08G045_USB_U2H1_HCLK, R9A08G045_CLK_P1, 0x578, 1), - DEF_MOD("usb0_func", R9A08G045_USB_U2P_EXR_CPUCLK, R9A08G045_CLK_P1, 0x578, 2), - DEF_MOD("usb_pclk", R9A08G045_USB_PCLK, R9A08G045_CLK_P1, 0x578, 3), - DEF_COUPLED("eth0_axi", R9A08G045_ETH0_CLK_AXI, R9A08G045_CLK_M0, 0x57c, 0), - DEF_COUPLED("eth0_chi", R9A08G045_ETH0_CLK_CHI, R9A08G045_CLK_ZT, 0x57c, 0), - DEF_MOD("eth0_refclk", R9A08G045_ETH0_REFCLK, R9A08G045_CLK_HP, 0x57c, 8), - DEF_COUPLED("eth1_axi", R9A08G045_ETH1_CLK_AXI, R9A08G045_CLK_M0, 0x57c, 1), - DEF_COUPLED("eth1_chi", R9A08G045_ETH1_CLK_CHI, R9A08G045_CLK_ZT, 0x57c, 1), - DEF_MOD("eth1_refclk", R9A08G045_ETH1_REFCLK, R9A08G045_CLK_HP, 0x57c, 9), - DEF_MOD("i2c0_pclk", R9A08G045_I2C0_PCLK, R9A08G045_CLK_P0, 0x580, 0), - DEF_MOD("i2c1_pclk", R9A08G045_I2C1_PCLK, R9A08G045_CLK_P0, 0x580, 1), - DEF_MOD("i2c2_pclk", R9A08G045_I2C2_PCLK, R9A08G045_CLK_P0, 0x580, 2), - DEF_MOD("i2c3_pclk", R9A08G045_I2C3_PCLK, R9A08G045_CLK_P0, 0x580, 3), - DEF_MOD("scif0_clk_pck", R9A08G045_SCIF0_CLK_PCK, R9A08G045_CLK_P0, 0x584, 0), - DEF_MOD("scif1_clk_pck", R9A08G045_SCIF1_CLK_PCK, R9A08G045_CLK_P0, 0x584, 1), - DEF_MOD("scif2_clk_pck", R9A08G045_SCIF2_CLK_PCK, R9A08G045_CLK_P0, 0x584, 2), - DEF_MOD("scif3_clk_pck", R9A08G045_SCIF3_CLK_PCK, R9A08G045_CLK_P0, 0x584, 3), - DEF_MOD("scif4_clk_pck", R9A08G045_SCIF4_CLK_PCK, R9A08G045_CLK_P0, 0x584, 4), - DEF_MOD("scif5_clk_pck", R9A08G045_SCIF5_CLK_PCK, R9A08G045_CLK_P0, 0x584, 5), - DEF_MOD("gpio_hclk", R9A08G045_GPIO_HCLK, R9A08G045_OSCCLK, 0x598, 0), - DEF_MOD("adc_adclk", R9A08G045_ADC_ADCLK, R9A08G045_CLK_TSU, 0x5a8, 0), - DEF_MOD("adc_pclk", R9A08G045_ADC_PCLK, R9A08G045_CLK_TSU, 0x5a8, 1), - DEF_MOD("tsu_pclk", R9A08G045_TSU_PCLK, R9A08G045_CLK_TSU, 0x5ac, 0), - DEF_MOD("vbat_bclk", R9A08G045_VBAT_BCLK, R9A08G045_OSCCLK, 0x614, 0), + DEF_MOD("gic_gicclk", R9A08G045_GIC600_GICCLK, R9A08G045_CLK_P1, 0x514, 0, + MSTOP(BUS_ACPU, BIT(3))), + DEF_MOD("ia55_pclk", R9A08G045_IA55_PCLK, R9A08G045_CLK_P2, 0x518, 0, + MSTOP(BUS_PERI_CPU, BIT(13))), + DEF_MOD("ia55_clk", R9A08G045_IA55_CLK, R9A08G045_CLK_P1, 0x518, 1, + MSTOP(BUS_PERI_CPU, BIT(13))), + DEF_MOD("dmac_aclk", R9A08G045_DMAC_ACLK, R9A08G045_CLK_P3, 0x52c, 0, + MSTOP(BUS_REG1, BIT(2))), + DEF_MOD("dmac_pclk", R9A08G045_DMAC_PCLK, CLK_P3_DIV2, 0x52c, 1, + MSTOP(BUS_REG1, BIT(3))), + DEF_MOD("wdt0_pclk", R9A08G045_WDT0_PCLK, R9A08G045_CLK_P0, 0x548, 0, + MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("wdt0_clk", R9A08G045_WDT0_CLK, R9A08G045_OSCCLK, 0x548, 1, + MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("sdhi0_imclk", R9A08G045_SDHI0_IMCLK, CLK_SD0_DIV4, 0x554, 0, + MSTOP(BUS_PERI_COM, BIT(0))), + DEF_MOD("sdhi0_imclk2", R9A08G045_SDHI0_IMCLK2, CLK_SD0_DIV4, 0x554, 1, + MSTOP(BUS_PERI_COM, BIT(0))), + DEF_MOD("sdhi0_clk_hs", R9A08G045_SDHI0_CLK_HS, R9A08G045_CLK_SD0, 0x554, 2, + MSTOP(BUS_PERI_COM, BIT(0))), + DEF_MOD("sdhi0_aclk", R9A08G045_SDHI0_ACLK, R9A08G045_CLK_P1, 0x554, 3, + MSTOP(BUS_PERI_COM, BIT(0))), + DEF_MOD("sdhi1_imclk", R9A08G045_SDHI1_IMCLK, CLK_SD1_DIV4, 0x554, 4, + MSTOP(BUS_PERI_COM, BIT(1))), + DEF_MOD("sdhi1_imclk2", R9A08G045_SDHI1_IMCLK2, CLK_SD1_DIV4, 0x554, 5, + MSTOP(BUS_PERI_COM, BIT(1))), + DEF_MOD("sdhi1_clk_hs", R9A08G045_SDHI1_CLK_HS, R9A08G045_CLK_SD1, 0x554, 6, + MSTOP(BUS_PERI_COM, BIT(1))), + DEF_MOD("sdhi1_aclk", R9A08G045_SDHI1_ACLK, R9A08G045_CLK_P1, 0x554, 7, + MSTOP(BUS_PERI_COM, BIT(1))), + DEF_MOD("sdhi2_imclk", R9A08G045_SDHI2_IMCLK, CLK_SD2_DIV4, 0x554, 8, + MSTOP(BUS_PERI_COM, BIT(11))), + DEF_MOD("sdhi2_imclk2", R9A08G045_SDHI2_IMCLK2, CLK_SD2_DIV4, 0x554, 9, + MSTOP(BUS_PERI_COM, BIT(11))), + DEF_MOD("sdhi2_clk_hs", R9A08G045_SDHI2_CLK_HS, R9A08G045_CLK_SD2, 0x554, 10, + MSTOP(BUS_PERI_COM, BIT(11))), + DEF_MOD("sdhi2_aclk", R9A08G045_SDHI2_ACLK, R9A08G045_CLK_P1, 0x554, 11, + MSTOP(BUS_PERI_COM, BIT(11))), + DEF_MOD("ssi0_pclk2", R9A08G045_SSI0_PCLK2, R9A08G045_CLK_P0, 0x570, 0, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi0_sfr", R9A08G045_SSI0_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 1, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi1_pclk2", R9A08G045_SSI1_PCLK2, R9A08G045_CLK_P0, 0x570, 2, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi1_sfr", R9A08G045_SSI1_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 3, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi2_pclk2", R9A08G045_SSI2_PCLK2, R9A08G045_CLK_P0, 0x570, 4, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi2_sfr", R9A08G045_SSI2_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 5, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi3_pclk2", R9A08G045_SSI3_PCLK2, R9A08G045_CLK_P0, 0x570, 6, + MSTOP(BUS_MCPU1, BIT(13))), + DEF_MOD("ssi3_sfr", R9A08G045_SSI3_PCLK_SFR, R9A08G045_CLK_P0, 0x570, 7, + MSTOP(BUS_MCPU1, BIT(13))), + DEF_MOD("usb0_host", R9A08G045_USB_U2H0_HCLK, R9A08G045_CLK_P1, 0x578, 0, + MSTOP(BUS_PERI_COM, BIT(5))), + DEF_MOD("usb1_host", R9A08G045_USB_U2H1_HCLK, R9A08G045_CLK_P1, 0x578, 1, + MSTOP(BUS_PERI_COM, BIT(7))), + DEF_MOD("usb0_func", R9A08G045_USB_U2P_EXR_CPUCLK, R9A08G045_CLK_P1, 0x578, 2, + MSTOP(BUS_PERI_COM, BIT(6))), + DEF_MOD("usb_pclk", R9A08G045_USB_PCLK, R9A08G045_CLK_P1, 0x578, 3, + MSTOP(BUS_PERI_COM, BIT(4))), + DEF_COUPLED("eth0_axi", R9A08G045_ETH0_CLK_AXI, R9A08G045_CLK_M0, 0x57c, 0, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_chi", R9A08G045_ETH0_CLK_CHI, R9A08G045_CLK_ZT, 0x57c, 0, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth0_refclk", R9A08G045_ETH0_REFCLK, R9A08G045_CLK_HP, 0x57c, 8, 0), + DEF_COUPLED("eth1_axi", R9A08G045_ETH1_CLK_AXI, R9A08G045_CLK_M0, 0x57c, 1, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_chi", R9A08G045_ETH1_CLK_CHI, R9A08G045_CLK_ZT, 0x57c, 1, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth1_refclk", R9A08G045_ETH1_REFCLK, R9A08G045_CLK_HP, 0x57c, 9, 0), + DEF_MOD("i2c0_pclk", R9A08G045_I2C0_PCLK, R9A08G045_CLK_P0, 0x580, 0, + MSTOP(BUS_MCPU2, BIT(10))), + DEF_MOD("i2c1_pclk", R9A08G045_I2C1_PCLK, R9A08G045_CLK_P0, 0x580, 1, + MSTOP(BUS_MCPU2, BIT(11))), + DEF_MOD("i2c2_pclk", R9A08G045_I2C2_PCLK, R9A08G045_CLK_P0, 0x580, 2, + MSTOP(BUS_MCPU2, BIT(12))), + DEF_MOD("i2c3_pclk", R9A08G045_I2C3_PCLK, R9A08G045_CLK_P0, 0x580, 3, + MSTOP(BUS_MCPU2, BIT(13))), + DEF_MOD("scif0_clk_pck", R9A08G045_SCIF0_CLK_PCK, R9A08G045_CLK_P0, 0x584, 0, + MSTOP(BUS_MCPU2, BIT(1))), + DEF_MOD("scif1_clk_pck", R9A08G045_SCIF1_CLK_PCK, R9A08G045_CLK_P0, 0x584, 1, + MSTOP(BUS_MCPU2, BIT(2))), + DEF_MOD("scif2_clk_pck", R9A08G045_SCIF2_CLK_PCK, R9A08G045_CLK_P0, 0x584, 2, + MSTOP(BUS_MCPU2, BIT(3))), + DEF_MOD("scif3_clk_pck", R9A08G045_SCIF3_CLK_PCK, R9A08G045_CLK_P0, 0x584, 3, + MSTOP(BUS_MCPU2, BIT(4))), + DEF_MOD("scif4_clk_pck", R9A08G045_SCIF4_CLK_PCK, R9A08G045_CLK_P0, 0x584, 4, + MSTOP(BUS_MCPU2, BIT(5))), + DEF_MOD("scif5_clk_pck", R9A08G045_SCIF5_CLK_PCK, R9A08G045_CLK_P0, 0x584, 5, + MSTOP(BUS_MCPU3, BIT(4))), + DEF_MOD("gpio_hclk", R9A08G045_GPIO_HCLK, R9A08G045_OSCCLK, 0x598, 0, 0), + DEF_MOD("adc_adclk", R9A08G045_ADC_ADCLK, R9A08G045_CLK_TSU, 0x5a8, 0, + MSTOP(BUS_MCPU2, BIT(14))), + DEF_MOD("adc_pclk", R9A08G045_ADC_PCLK, R9A08G045_CLK_TSU, 0x5a8, 1, + MSTOP(BUS_MCPU2, BIT(14))), + DEF_MOD("tsu_pclk", R9A08G045_TSU_PCLK, R9A08G045_CLK_TSU, 0x5ac, 0, + MSTOP(BUS_MCPU2, BIT(15))), + DEF_MOD("vbat_bclk", R9A08G045_VBAT_BCLK, R9A08G045_OSCCLK, 0x614, 0, + MSTOP(BUS_MCPU3, GENMASK(8, 7))), }; static const struct rzg2l_reset r9a08g045_resets[] = { @@ -293,78 +342,6 @@ static const unsigned int r9a08g045_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A08G045_VBAT_BCLK, }; -static const struct rzg2l_cpg_pm_domain_init_data r9a08g045_pm_domains[] = { - /* Keep always-on domain on the first position for proper domains registration. */ - DEF_PD("always-on", R9A08G045_PD_ALWAYS_ON, - DEF_REG_CONF(0, 0), - GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), - DEF_PD("gic", R9A08G045_PD_GIC, - DEF_REG_CONF(CPG_BUS_ACPU_MSTOP, BIT(3)), - GENPD_FLAG_ALWAYS_ON), - DEF_PD("ia55", R9A08G045_PD_IA55, - DEF_REG_CONF(CPG_BUS_PERI_CPU_MSTOP, BIT(13)), - GENPD_FLAG_ALWAYS_ON), - DEF_PD("dmac", R9A08G045_PD_DMAC, - DEF_REG_CONF(CPG_BUS_REG1_MSTOP, GENMASK(3, 0)), - GENPD_FLAG_ALWAYS_ON), - DEF_PD("wdt0", R9A08G045_PD_WDT0, - DEF_REG_CONF(CPG_BUS_REG0_MSTOP, BIT(0)), - GENPD_FLAG_IRQ_SAFE), - DEF_PD("sdhi0", R9A08G045_PD_SDHI0, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(0)), 0), - DEF_PD("sdhi1", R9A08G045_PD_SDHI1, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(1)), 0), - DEF_PD("sdhi2", R9A08G045_PD_SDHI2, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(11)), 0), - DEF_PD("ssi0", R9A08G045_PD_SSI0, - DEF_REG_CONF(CPG_BUS_MCPU1_MSTOP, BIT(10)), 0), - DEF_PD("ssi1", R9A08G045_PD_SSI1, - DEF_REG_CONF(CPG_BUS_MCPU1_MSTOP, BIT(11)), 0), - DEF_PD("ssi2", R9A08G045_PD_SSI2, - DEF_REG_CONF(CPG_BUS_MCPU1_MSTOP, BIT(12)), 0), - DEF_PD("ssi3", R9A08G045_PD_SSI3, - DEF_REG_CONF(CPG_BUS_MCPU1_MSTOP, BIT(13)), 0), - DEF_PD("usb0", R9A08G045_PD_USB0, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, GENMASK(6, 5)), 0), - DEF_PD("usb1", R9A08G045_PD_USB1, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(7)), 0), - DEF_PD("usb-phy", R9A08G045_PD_USB_PHY, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(4)), 0), - DEF_PD("eth0", R9A08G045_PD_ETHER0, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(2)), 0), - DEF_PD("eth1", R9A08G045_PD_ETHER1, - DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(3)), 0), - DEF_PD("i2c0", R9A08G045_PD_I2C0, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(10)), 0), - DEF_PD("i2c1", R9A08G045_PD_I2C1, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(11)), 0), - DEF_PD("i2c2", R9A08G045_PD_I2C2, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(12)), 0), - DEF_PD("i2c3", R9A08G045_PD_I2C3, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(13)), 0), - DEF_PD("scif0", R9A08G045_PD_SCIF0, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(1)), 0), - DEF_PD("scif1", R9A08G045_PD_SCIF1, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(2)), 0), - DEF_PD("scif2", R9A08G045_PD_SCIF2, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(3)), 0), - DEF_PD("scif3", R9A08G045_PD_SCIF3, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(4)), 0), - DEF_PD("scif4", R9A08G045_PD_SCIF4, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(5)), 0), - DEF_PD("scif5", R9A08G045_PD_SCIF5, - DEF_REG_CONF(CPG_BUS_MCPU3_MSTOP, BIT(4)), 0), - DEF_PD("adc", R9A08G045_PD_ADC, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(14)), 0), - DEF_PD("tsu", R9A08G045_PD_TSU, - DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(15)), 0), - DEF_PD("vbat", R9A08G045_PD_VBAT, - DEF_REG_CONF(CPG_BUS_MCPU3_MSTOP, BIT(8)), - GENPD_FLAG_ALWAYS_ON), - DEF_PD("rtc", R9A08G045_PD_RTC, - DEF_REG_CONF(CPG_BUS_MCPU3_MSTOP, BIT(7)), 0), -}; - const struct rzg2l_cpg_info r9a08g045_cpg_info = { /* Core Clocks */ .core_clks = r9a08g045_core_clks, @@ -385,9 +362,5 @@ const struct rzg2l_cpg_info r9a08g045_cpg_info = { .resets = r9a08g045_resets, .num_resets = R9A08G045_VBAT_BRESETN + 1, /* Last reset ID + 1 */ - /* Power domains */ - .pm_domains = r9a08g045_pm_domains, - .num_pm_domains = ARRAY_SIZE(r9a08g045_pm_domains), - .has_clk_mon_regs = true, }; diff --git a/drivers/clk/renesas/r9a09g011-cpg.c b/drivers/clk/renesas/r9a09g011-cpg.c index 22272279b104..ba25429c244d 100644 --- a/drivers/clk/renesas/r9a09g011-cpg.c +++ b/drivers/clk/renesas/r9a09g011-cpg.c @@ -151,64 +151,64 @@ static const struct cpg_core_clk r9a09g011_core_clks[] __initconst = { }; static const struct rzg2l_mod_clk r9a09g011_mod_clks[] __initconst = { - DEF_MOD("pfc", R9A09G011_PFC_PCLK, CLK_MAIN, 0x400, 2), - DEF_MOD("gic", R9A09G011_GIC_CLK, CLK_SEL_B_D2, 0x400, 5), - DEF_MOD("sdi0_aclk", R9A09G011_SDI0_ACLK, CLK_SEL_D, 0x408, 0), - DEF_MOD("sdi0_imclk", R9A09G011_SDI0_IMCLK, CLK_SEL_SDI, 0x408, 1), - DEF_MOD("sdi0_imclk2", R9A09G011_SDI0_IMCLK2, CLK_SEL_SDI, 0x408, 2), - DEF_MOD("sdi0_clk_hs", R9A09G011_SDI0_CLK_HS, CLK_PLL2_800, 0x408, 3), - DEF_MOD("sdi1_aclk", R9A09G011_SDI1_ACLK, CLK_SEL_D, 0x408, 4), - DEF_MOD("sdi1_imclk", R9A09G011_SDI1_IMCLK, CLK_SEL_SDI, 0x408, 5), - DEF_MOD("sdi1_imclk2", R9A09G011_SDI1_IMCLK2, CLK_SEL_SDI, 0x408, 6), - DEF_MOD("sdi1_clk_hs", R9A09G011_SDI1_CLK_HS, CLK_PLL2_800, 0x408, 7), - DEF_MOD("emm_aclk", R9A09G011_EMM_ACLK, CLK_SEL_D, 0x408, 8), - DEF_MOD("emm_imclk", R9A09G011_EMM_IMCLK, CLK_SEL_SDI, 0x408, 9), - DEF_MOD("emm_imclk2", R9A09G011_EMM_IMCLK2, CLK_SEL_SDI, 0x408, 10), - DEF_MOD("emm_clk_hs", R9A09G011_EMM_CLK_HS, CLK_PLL2_800, 0x408, 11), - DEF_COUPLED("eth_axi", R9A09G011_ETH0_CLK_AXI, CLK_PLL2_200, 0x40c, 8), - DEF_COUPLED("eth_chi", R9A09G011_ETH0_CLK_CHI, CLK_PLL2_100, 0x40c, 8), - DEF_MOD("eth_clk_gptp", R9A09G011_ETH0_GPTP_EXT, CLK_PLL2_100, 0x40c, 9), - DEF_MOD("usb_aclk_h", R9A09G011_USB_ACLK_H, CLK_SEL_D, 0x40c, 4), - DEF_MOD("usb_aclk_p", R9A09G011_USB_ACLK_P, CLK_SEL_D, 0x40c, 5), - DEF_MOD("usb_pclk", R9A09G011_USB_PCLK, CLK_SEL_E, 0x40c, 6), - DEF_MOD("syc_cnt_clk", R9A09G011_SYC_CNT_CLK, CLK_MAIN_24, 0x41c, 12), - DEF_MOD("iic_pclk0", R9A09G011_IIC_PCLK0, CLK_SEL_E, 0x420, 12), - DEF_MOD("cperi_grpb", R9A09G011_CPERI_GRPB_PCLK, CLK_SEL_E, 0x424, 0), - DEF_MOD("tim_clk_8", R9A09G011_TIM8_CLK, CLK_MAIN_2, 0x424, 4), - DEF_MOD("tim_clk_9", R9A09G011_TIM9_CLK, CLK_MAIN_2, 0x424, 5), - DEF_MOD("tim_clk_10", R9A09G011_TIM10_CLK, CLK_MAIN_2, 0x424, 6), - DEF_MOD("tim_clk_11", R9A09G011_TIM11_CLK, CLK_MAIN_2, 0x424, 7), - DEF_MOD("tim_clk_12", R9A09G011_TIM12_CLK, CLK_MAIN_2, 0x424, 8), - DEF_MOD("tim_clk_13", R9A09G011_TIM13_CLK, CLK_MAIN_2, 0x424, 9), - DEF_MOD("tim_clk_14", R9A09G011_TIM14_CLK, CLK_MAIN_2, 0x424, 10), - DEF_MOD("tim_clk_15", R9A09G011_TIM15_CLK, CLK_MAIN_2, 0x424, 11), - DEF_MOD("iic_pclk1", R9A09G011_IIC_PCLK1, CLK_SEL_E, 0x424, 12), - DEF_MOD("cperi_grpc", R9A09G011_CPERI_GRPC_PCLK, CLK_SEL_E, 0x428, 0), - DEF_MOD("tim_clk_16", R9A09G011_TIM16_CLK, CLK_MAIN_2, 0x428, 4), - DEF_MOD("tim_clk_17", R9A09G011_TIM17_CLK, CLK_MAIN_2, 0x428, 5), - DEF_MOD("tim_clk_18", R9A09G011_TIM18_CLK, CLK_MAIN_2, 0x428, 6), - DEF_MOD("tim_clk_19", R9A09G011_TIM19_CLK, CLK_MAIN_2, 0x428, 7), - DEF_MOD("tim_clk_20", R9A09G011_TIM20_CLK, CLK_MAIN_2, 0x428, 8), - DEF_MOD("tim_clk_21", R9A09G011_TIM21_CLK, CLK_MAIN_2, 0x428, 9), - DEF_MOD("tim_clk_22", R9A09G011_TIM22_CLK, CLK_MAIN_2, 0x428, 10), - DEF_MOD("tim_clk_23", R9A09G011_TIM23_CLK, CLK_MAIN_2, 0x428, 11), - DEF_MOD("wdt0_pclk", R9A09G011_WDT0_PCLK, CLK_SEL_E, 0x428, 12), - DEF_MOD("wdt0_clk", R9A09G011_WDT0_CLK, CLK_MAIN, 0x428, 13), - DEF_MOD("cperi_grpf", R9A09G011_CPERI_GRPF_PCLK, CLK_SEL_E, 0x434, 0), - DEF_MOD("pwm8_clk", R9A09G011_PWM8_CLK, CLK_MAIN, 0x434, 4), - DEF_MOD("pwm9_clk", R9A09G011_PWM9_CLK, CLK_MAIN, 0x434, 5), - DEF_MOD("pwm10_clk", R9A09G011_PWM10_CLK, CLK_MAIN, 0x434, 6), - DEF_MOD("pwm11_clk", R9A09G011_PWM11_CLK, CLK_MAIN, 0x434, 7), - DEF_MOD("pwm12_clk", R9A09G011_PWM12_CLK, CLK_MAIN, 0x434, 8), - DEF_MOD("pwm13_clk", R9A09G011_PWM13_CLK, CLK_MAIN, 0x434, 9), - DEF_MOD("pwm14_clk", R9A09G011_PWM14_CLK, CLK_MAIN, 0x434, 10), - DEF_MOD("cperi_grpg", R9A09G011_CPERI_GRPG_PCLK, CLK_SEL_E, 0x438, 0), - DEF_MOD("cperi_grph", R9A09G011_CPERI_GRPH_PCLK, CLK_SEL_E, 0x438, 1), - DEF_MOD("urt_pclk", R9A09G011_URT_PCLK, CLK_SEL_E, 0x438, 4), - DEF_MOD("urt0_clk", R9A09G011_URT0_CLK, CLK_SEL_W0, 0x438, 5), - DEF_MOD("csi0_clk", R9A09G011_CSI0_CLK, CLK_SEL_CSI0, 0x438, 8), - DEF_MOD("csi4_clk", R9A09G011_CSI4_CLK, CLK_SEL_CSI4, 0x438, 12), - DEF_MOD("ca53", R9A09G011_CA53_CLK, CLK_DIV_A, 0x448, 0), + DEF_MOD("pfc", R9A09G011_PFC_PCLK, CLK_MAIN, 0x400, 2, 0), + DEF_MOD("gic", R9A09G011_GIC_CLK, CLK_SEL_B_D2, 0x400, 5, 0), + DEF_MOD("sdi0_aclk", R9A09G011_SDI0_ACLK, CLK_SEL_D, 0x408, 0, 0), + DEF_MOD("sdi0_imclk", R9A09G011_SDI0_IMCLK, CLK_SEL_SDI, 0x408, 1, 0), + DEF_MOD("sdi0_imclk2", R9A09G011_SDI0_IMCLK2, CLK_SEL_SDI, 0x408, 2, 0), + DEF_MOD("sdi0_clk_hs", R9A09G011_SDI0_CLK_HS, CLK_PLL2_800, 0x408, 3, 0), + DEF_MOD("sdi1_aclk", R9A09G011_SDI1_ACLK, CLK_SEL_D, 0x408, 4, 0), + DEF_MOD("sdi1_imclk", R9A09G011_SDI1_IMCLK, CLK_SEL_SDI, 0x408, 5, 0), + DEF_MOD("sdi1_imclk2", R9A09G011_SDI1_IMCLK2, CLK_SEL_SDI, 0x408, 6, 0), + DEF_MOD("sdi1_clk_hs", R9A09G011_SDI1_CLK_HS, CLK_PLL2_800, 0x408, 7, 0), + DEF_MOD("emm_aclk", R9A09G011_EMM_ACLK, CLK_SEL_D, 0x408, 8, 0), + DEF_MOD("emm_imclk", R9A09G011_EMM_IMCLK, CLK_SEL_SDI, 0x408, 9, 0), + DEF_MOD("emm_imclk2", R9A09G011_EMM_IMCLK2, CLK_SEL_SDI, 0x408, 10, 0), + DEF_MOD("emm_clk_hs", R9A09G011_EMM_CLK_HS, CLK_PLL2_800, 0x408, 11, 0), + DEF_COUPLED("eth_axi", R9A09G011_ETH0_CLK_AXI, CLK_PLL2_200, 0x40c, 8, 0), + DEF_COUPLED("eth_chi", R9A09G011_ETH0_CLK_CHI, CLK_PLL2_100, 0x40c, 8, 0), + DEF_MOD("eth_clk_gptp", R9A09G011_ETH0_GPTP_EXT, CLK_PLL2_100, 0x40c, 9, 0), + DEF_MOD("usb_aclk_h", R9A09G011_USB_ACLK_H, CLK_SEL_D, 0x40c, 4, 0), + DEF_MOD("usb_aclk_p", R9A09G011_USB_ACLK_P, CLK_SEL_D, 0x40c, 5, 0), + DEF_MOD("usb_pclk", R9A09G011_USB_PCLK, CLK_SEL_E, 0x40c, 6, 0), + DEF_MOD("syc_cnt_clk", R9A09G011_SYC_CNT_CLK, CLK_MAIN_24, 0x41c, 12, 0), + DEF_MOD("iic_pclk0", R9A09G011_IIC_PCLK0, CLK_SEL_E, 0x420, 12, 0), + DEF_MOD("cperi_grpb", R9A09G011_CPERI_GRPB_PCLK, CLK_SEL_E, 0x424, 0, 0), + DEF_MOD("tim_clk_8", R9A09G011_TIM8_CLK, CLK_MAIN_2, 0x424, 4, 0), + DEF_MOD("tim_clk_9", R9A09G011_TIM9_CLK, CLK_MAIN_2, 0x424, 5, 0), + DEF_MOD("tim_clk_10", R9A09G011_TIM10_CLK, CLK_MAIN_2, 0x424, 6, 0), + DEF_MOD("tim_clk_11", R9A09G011_TIM11_CLK, CLK_MAIN_2, 0x424, 7, 0), + DEF_MOD("tim_clk_12", R9A09G011_TIM12_CLK, CLK_MAIN_2, 0x424, 8, 0), + DEF_MOD("tim_clk_13", R9A09G011_TIM13_CLK, CLK_MAIN_2, 0x424, 9, 0), + DEF_MOD("tim_clk_14", R9A09G011_TIM14_CLK, CLK_MAIN_2, 0x424, 10, 0), + DEF_MOD("tim_clk_15", R9A09G011_TIM15_CLK, CLK_MAIN_2, 0x424, 11, 0), + DEF_MOD("iic_pclk1", R9A09G011_IIC_PCLK1, CLK_SEL_E, 0x424, 12, 0), + DEF_MOD("cperi_grpc", R9A09G011_CPERI_GRPC_PCLK, CLK_SEL_E, 0x428, 0, 0), + DEF_MOD("tim_clk_16", R9A09G011_TIM16_CLK, CLK_MAIN_2, 0x428, 4, 0), + DEF_MOD("tim_clk_17", R9A09G011_TIM17_CLK, CLK_MAIN_2, 0x428, 5, 0), + DEF_MOD("tim_clk_18", R9A09G011_TIM18_CLK, CLK_MAIN_2, 0x428, 6, 0), + DEF_MOD("tim_clk_19", R9A09G011_TIM19_CLK, CLK_MAIN_2, 0x428, 7, 0), + DEF_MOD("tim_clk_20", R9A09G011_TIM20_CLK, CLK_MAIN_2, 0x428, 8, 0), + DEF_MOD("tim_clk_21", R9A09G011_TIM21_CLK, CLK_MAIN_2, 0x428, 9, 0), + DEF_MOD("tim_clk_22", R9A09G011_TIM22_CLK, CLK_MAIN_2, 0x428, 10, 0), + DEF_MOD("tim_clk_23", R9A09G011_TIM23_CLK, CLK_MAIN_2, 0x428, 11, 0), + DEF_MOD("wdt0_pclk", R9A09G011_WDT0_PCLK, CLK_SEL_E, 0x428, 12, 0), + DEF_MOD("wdt0_clk", R9A09G011_WDT0_CLK, CLK_MAIN, 0x428, 13, 0), + DEF_MOD("cperi_grpf", R9A09G011_CPERI_GRPF_PCLK, CLK_SEL_E, 0x434, 0, 0), + DEF_MOD("pwm8_clk", R9A09G011_PWM8_CLK, CLK_MAIN, 0x434, 4, 0), + DEF_MOD("pwm9_clk", R9A09G011_PWM9_CLK, CLK_MAIN, 0x434, 5, 0), + DEF_MOD("pwm10_clk", R9A09G011_PWM10_CLK, CLK_MAIN, 0x434, 6, 0), + DEF_MOD("pwm11_clk", R9A09G011_PWM11_CLK, CLK_MAIN, 0x434, 7, 0), + DEF_MOD("pwm12_clk", R9A09G011_PWM12_CLK, CLK_MAIN, 0x434, 8, 0), + DEF_MOD("pwm13_clk", R9A09G011_PWM13_CLK, CLK_MAIN, 0x434, 9, 0), + DEF_MOD("pwm14_clk", R9A09G011_PWM14_CLK, CLK_MAIN, 0x434, 10, 0), + DEF_MOD("cperi_grpg", R9A09G011_CPERI_GRPG_PCLK, CLK_SEL_E, 0x438, 0, 0), + DEF_MOD("cperi_grph", R9A09G011_CPERI_GRPH_PCLK, CLK_SEL_E, 0x438, 1, 0), + DEF_MOD("urt_pclk", R9A09G011_URT_PCLK, CLK_SEL_E, 0x438, 4, 0), + DEF_MOD("urt0_clk", R9A09G011_URT0_CLK, CLK_SEL_W0, 0x438, 5, 0), + DEF_MOD("csi0_clk", R9A09G011_CSI0_CLK, CLK_SEL_CSI0, 0x438, 8, 0), + DEF_MOD("csi4_clk", R9A09G011_CSI4_CLK, CLK_SEL_CSI4, 0x438, 12, 0), + DEF_MOD("ca53", R9A09G011_CA53_CLK, CLK_DIV_A, 0x448, 0, 0), }; static const struct rzg2l_reset r9a09g011_resets[] = { diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 21699999cedd..26e2be7667eb 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -29,6 +29,7 @@ enum clk_ids { CLK_PLLDTY, CLK_PLLCA55, CLK_PLLVDO, + CLK_PLLETH, /* Internal Core Clocks */ CLK_PLLCM33_DIV3, @@ -46,6 +47,15 @@ enum clk_ids { CLK_PLLDTY_ACPU, CLK_PLLDTY_ACPU_DIV2, CLK_PLLDTY_ACPU_DIV4, + CLK_PLLDTY_DIV8, + CLK_PLLETH_DIV_250_FIX, + CLK_PLLETH_DIV_125_FIX, + CLK_CSDIV_PLLETH_GBE0, + CLK_CSDIV_PLLETH_GBE1, + CLK_SMUX2_GBE0_TXCLK, + CLK_SMUX2_GBE0_RXCLK, + CLK_SMUX2_GBE1_TXCLK, + CLK_SMUX2_GBE1_RXCLK, CLK_PLLDTY_DIV16, CLK_PLLVDO_CRU0, CLK_PLLVDO_GPU, @@ -85,7 +95,18 @@ static const struct clk_div_table dtable_2_64[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_100[] = { + {0, 2}, + {1, 10}, + {2, 100}, + {0, 0}, +}; + /* Mux clock tables */ +static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; +static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; +static const char * const smux2_gbe1_rxclk[] = { ".plleth_gbe1", "et1_rxclk" }; +static const char * const smux2_gbe1_txclk[] = { ".plleth_gbe1", "et1_txclk" }; static const char * const smux2_xspi_clk0[] = { ".pllcm33_div3", ".pllcm33_div4" }; static const char * const smux2_xspi_clk1[] = { ".smux2_xspi_clk0", ".pllcm33_div5" }; @@ -100,6 +121,7 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_FIXED(".pllcln", CLK_PLLCLN, CLK_QEXTAL, 200, 3), DEF_FIXED(".plldty", CLK_PLLDTY, CLK_QEXTAL, 200, 3), DEF_PLL(".pllca55", CLK_PLLCA55, CLK_QEXTAL, PLLCA55), + DEF_FIXED(".plleth", CLK_PLLETH, CLK_QEXTAL, 125, 3), DEF_FIXED(".pllvdo", CLK_PLLVDO, CLK_QEXTAL, 105, 2), /* Internal Core Clocks */ @@ -122,6 +144,18 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_DDIV(".plldty_acpu", CLK_PLLDTY_ACPU, CLK_PLLDTY, CDDIV0_DIVCTL2, dtable_2_64), DEF_FIXED(".plldty_acpu_div2", CLK_PLLDTY_ACPU_DIV2, CLK_PLLDTY_ACPU, 1, 2), DEF_FIXED(".plldty_acpu_div4", CLK_PLLDTY_ACPU_DIV4, CLK_PLLDTY_ACPU, 1, 4), + DEF_FIXED(".plldty_div8", CLK_PLLDTY_DIV8, CLK_PLLDTY, 1, 8), + + DEF_FIXED(".plleth_250_fix", CLK_PLLETH_DIV_250_FIX, CLK_PLLETH, 1, 4), + DEF_FIXED(".plleth_125_fix", CLK_PLLETH_DIV_125_FIX, CLK_PLLETH_DIV_250_FIX, 1, 2), + DEF_CSDIV(".plleth_gbe0", CLK_CSDIV_PLLETH_GBE0, CLK_PLLETH_DIV_250_FIX, + CSDIV0_DIVCTL0, dtable_2_100), + DEF_CSDIV(".plleth_gbe1", CLK_CSDIV_PLLETH_GBE1, CLK_PLLETH_DIV_250_FIX, + CSDIV0_DIVCTL1, dtable_2_100), + DEF_SMUX(".smux2_gbe0_txclk", CLK_SMUX2_GBE0_TXCLK, SSEL0_SELCTL2, smux2_gbe0_txclk), + DEF_SMUX(".smux2_gbe0_rxclk", CLK_SMUX2_GBE0_RXCLK, SSEL0_SELCTL3, smux2_gbe0_rxclk), + DEF_SMUX(".smux2_gbe1_txclk", CLK_SMUX2_GBE1_TXCLK, SSEL1_SELCTL0, smux2_gbe1_txclk), + DEF_SMUX(".smux2_gbe1_rxclk", CLK_SMUX2_GBE1_RXCLK, SSEL1_SELCTL1, smux2_gbe1_rxclk), DEF_FIXED(".plldty_div16", CLK_PLLDTY_DIV16, CLK_PLLDTY, 1, 16), DEF_DDIV(".pllvdo_cru0", CLK_PLLVDO_CRU0, CLK_PLLVDO, CDDIV3_DIVCTL3, dtable_2_4), @@ -139,6 +173,10 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { CDDIV1_DIVCTL3, dtable_1_8), DEF_FIXED("iotop_0_shclk", R9A09G047_IOTOP_0_SHCLK, CLK_PLLCM33_DIV16, 1, 1), DEF_FIXED("spi_clk_spi", R9A09G047_SPI_CLK_SPI, CLK_PLLCM33_XSPI, 1, 2), + DEF_FIXED("gbeth_0_clk_ptp_ref_i", R9A09G047_GBETH_0_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), + DEF_FIXED("gbeth_1_clk_ptp_ref_i", R9A09G047_GBETH_1_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), }; static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { @@ -160,6 +198,12 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(5, BIT(13))), 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, + BUS_MSTOP(10, BIT(15))), + DEF_MOD("i3c_0_pclk", CLK_PLLCLN_DIV16, 9, 1, 4, 17, + BUS_MSTOP(10, BIT(15))), + DEF_MOD("i3c_0_tclk", CLK_PLLCLN_DIV8, 9, 2, 4, 18, + BUS_MSTOP(10, BIT(15))), DEF_MOD("riic_8_ckm", CLK_PLLCM33_DIV16, 9, 3, 4, 19, BUS_MSTOP(3, BIT(13))), DEF_MOD("riic_0_ckm", CLK_PLLCLN_DIV16, 9, 4, 4, 20, @@ -214,6 +258,30 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(8, BIT(4))), DEF_MOD("sdhi_2_aclk", CLK_PLLDTY_ACPU_DIV4, 10, 14, 5, 14, BUS_MSTOP(8, BIT(4))), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_i", CLK_SMUX2_GBE0_TXCLK, 11, 8, 5, 24, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_i", CLK_SMUX2_GBE0_RXCLK, 11, 9, 5, 25, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_180_i", CLK_SMUX2_GBE0_TXCLK, 11, 10, 5, 26, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_180_i", CLK_SMUX2_GBE0_RXCLK, 11, 11, 5, 27, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD("gbeth_0_aclk_csr_i", CLK_PLLDTY_DIV8, 11, 12, 5, 28, + BUS_MSTOP(8, BIT(5))), + DEF_MOD("gbeth_0_aclk_i", CLK_PLLDTY_DIV8, 11, 13, 5, 29, + BUS_MSTOP(8, BIT(5))), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_i", CLK_SMUX2_GBE1_TXCLK, 11, 14, 5, 30, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_i", CLK_SMUX2_GBE1_RXCLK, 11, 15, 5, 31, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_180_i", CLK_SMUX2_GBE1_TXCLK, 12, 0, 6, 0, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_180_i", CLK_SMUX2_GBE1_RXCLK, 12, 1, 6, 1, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD("gbeth_1_aclk_csr_i", CLK_PLLDTY_DIV8, 12, 2, 6, 2, + 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("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, @@ -239,6 +307,8 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(7, 7, 3, 8), /* WDT_2_RESET */ DEF_RST(7, 8, 3, 9), /* WDT_3_RESET */ DEF_RST(9, 5, 4, 6), /* SCIF_0_RST_SYSTEM_N */ + DEF_RST(9, 6, 4, 7), /* I3C_0_PRESETN */ + DEF_RST(9, 7, 4, 8), /* I3C_0_TRESETN */ DEF_RST(9, 8, 4, 9), /* RIIC_0_MRST */ DEF_RST(9, 9, 4, 10), /* RIIC_1_MRST */ DEF_RST(9, 10, 4, 11), /* RIIC_2_MRST */ @@ -255,6 +325,8 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(10, 7, 4, 24), /* SDHI_0_IXRST */ DEF_RST(10, 8, 4, 25), /* SDHI_1_IXRST */ DEF_RST(10, 9, 4, 26), /* SDHI_2_IXRST */ + DEF_RST(11, 0, 5, 1), /* GBETH_0_ARESETN_I */ + DEF_RST(11, 1, 5, 2), /* GBETH_1_ARESETN_I */ 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 e2712a25c43a..437af86f49dd 100644 --- a/drivers/clk/renesas/r9a09g056-cpg.c +++ b/drivers/clk/renesas/r9a09g056-cpg.c @@ -16,7 +16,7 @@ enum clk_ids { /* Core Clock Outputs exported to DT */ - LAST_DT_CORE_CLK = R9A09G056_GBETH_1_CLK_PTP_REF_I, + LAST_DT_CORE_CLK = R9A09G056_SPI_CLK_SPI, /* External Input Clocks */ CLK_AUDIO_EXTAL, @@ -28,13 +28,34 @@ enum clk_ids { CLK_PLLCLN, CLK_PLLDTY, CLK_PLLCA55, + CLK_PLLETH, + CLK_PLLGPU, /* Internal Core Clocks */ + CLK_PLLCM33_DIV3, + CLK_PLLCM33_DIV4, + CLK_PLLCM33_DIV5, CLK_PLLCM33_DIV16, + CLK_SMUX2_XSPI_CLK0, + CLK_SMUX2_XSPI_CLK1, + CLK_PLLCM33_XSPI, + CLK_PLLCM33_GEAR, CLK_PLLCLN_DIV2, CLK_PLLCLN_DIV8, + CLK_PLLCLN_DIV16, CLK_PLLDTY_ACPU, + CLK_PLLDTY_ACPU_DIV2, CLK_PLLDTY_ACPU_DIV4, + CLK_PLLDTY_DIV8, + CLK_PLLETH_DIV_250_FIX, + CLK_PLLETH_DIV_125_FIX, + CLK_CSDIV_PLLETH_GBE0, + CLK_CSDIV_PLLETH_GBE1, + CLK_SMUX2_GBE0_TXCLK, + CLK_SMUX2_GBE0_RXCLK, + CLK_SMUX2_GBE1_TXCLK, + CLK_SMUX2_GBE1_RXCLK, + CLK_PLLGPU_GEAR, /* Module Clocks */ MOD_CLK_BASE, @@ -48,6 +69,14 @@ static const struct clk_div_table dtable_1_8[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_16[] = { + {0, 2}, + {1, 4}, + {2, 8}, + {3, 16}, + {0, 0}, +}; + static const struct clk_div_table dtable_2_64[] = { {0, 2}, {1, 4}, @@ -57,6 +86,21 @@ static const struct clk_div_table dtable_2_64[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_100[] = { + {0, 2}, + {1, 10}, + {2, 100}, + {0, 0}, +}; + +/* Mux clock tables */ +static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; +static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; +static const char * const smux2_gbe1_rxclk[] = { ".plleth_gbe1", "et1_rxclk" }; +static const char * const smux2_gbe1_txclk[] = { ".plleth_gbe1", "et1_txclk" }; +static const char * const smux2_xspi_clk0[] = { ".pllcm33_div3", ".pllcm33_div4" }; +static const char * const smux2_xspi_clk1[] = { ".smux2_xspi_clk0", ".pllcm33_div5" }; + static const struct cpg_core_clk r9a09g056_core_clks[] __initconst = { /* External Clock Inputs */ DEF_INPUT("audio_extal", CLK_AUDIO_EXTAL), @@ -68,15 +112,41 @@ static const struct cpg_core_clk r9a09g056_core_clks[] __initconst = { DEF_FIXED(".pllcln", CLK_PLLCLN, CLK_QEXTAL, 200, 3), DEF_FIXED(".plldty", CLK_PLLDTY, CLK_QEXTAL, 200, 3), DEF_PLL(".pllca55", CLK_PLLCA55, CLK_QEXTAL, PLLCA55), + DEF_FIXED(".plleth", CLK_PLLETH, CLK_QEXTAL, 125, 3), + DEF_PLL(".pllgpu", CLK_PLLGPU, CLK_QEXTAL, PLLGPU), /* Internal Core Clocks */ + DEF_FIXED(".pllcm33_div3", CLK_PLLCM33_DIV3, CLK_PLLCM33, 1, 3), + DEF_FIXED(".pllcm33_div4", CLK_PLLCM33_DIV4, CLK_PLLCM33, 1, 4), + DEF_FIXED(".pllcm33_div5", CLK_PLLCM33_DIV5, CLK_PLLCM33, 1, 5), DEF_FIXED(".pllcm33_div16", CLK_PLLCM33_DIV16, CLK_PLLCM33, 1, 16), + DEF_SMUX(".smux2_xspi_clk0", CLK_SMUX2_XSPI_CLK0, SSEL1_SELCTL2, smux2_xspi_clk0), + DEF_SMUX(".smux2_xspi_clk1", CLK_SMUX2_XSPI_CLK1, SSEL1_SELCTL3, smux2_xspi_clk1), + DEF_CSDIV(".pllcm33_xspi", CLK_PLLCM33_XSPI, CLK_SMUX2_XSPI_CLK1, CSDIV0_DIVCTL3, + dtable_2_16), + DEF_DDIV(".pllcm33_gear", CLK_PLLCM33_GEAR, CLK_PLLCM33_DIV4, CDDIV0_DIVCTL1, dtable_2_64), DEF_FIXED(".pllcln_div2", CLK_PLLCLN_DIV2, CLK_PLLCLN, 1, 2), DEF_FIXED(".pllcln_div8", CLK_PLLCLN_DIV8, CLK_PLLCLN, 1, 8), + DEF_FIXED(".pllcln_div16", CLK_PLLCLN_DIV16, CLK_PLLCLN, 1, 16), DEF_DDIV(".plldty_acpu", CLK_PLLDTY_ACPU, CLK_PLLDTY, CDDIV0_DIVCTL2, dtable_2_64), + DEF_FIXED(".plldty_acpu_div2", CLK_PLLDTY_ACPU_DIV2, CLK_PLLDTY_ACPU, 1, 2), DEF_FIXED(".plldty_acpu_div4", CLK_PLLDTY_ACPU_DIV4, CLK_PLLDTY_ACPU, 1, 4), + DEF_FIXED(".plldty_div8", CLK_PLLDTY_DIV8, CLK_PLLDTY, 1, 8), + + DEF_FIXED(".plleth_250_fix", CLK_PLLETH_DIV_250_FIX, CLK_PLLETH, 1, 4), + DEF_FIXED(".plleth_125_fix", CLK_PLLETH_DIV_125_FIX, CLK_PLLETH_DIV_250_FIX, 1, 2), + DEF_CSDIV(".plleth_gbe0", CLK_CSDIV_PLLETH_GBE0, + CLK_PLLETH_DIV_250_FIX, CSDIV0_DIVCTL0, dtable_2_100), + DEF_CSDIV(".plleth_gbe1", CLK_CSDIV_PLLETH_GBE1, + CLK_PLLETH_DIV_250_FIX, CSDIV0_DIVCTL1, dtable_2_100), + DEF_SMUX(".smux2_gbe0_txclk", CLK_SMUX2_GBE0_TXCLK, SSEL0_SELCTL2, smux2_gbe0_txclk), + DEF_SMUX(".smux2_gbe0_rxclk", CLK_SMUX2_GBE0_RXCLK, SSEL0_SELCTL3, smux2_gbe0_rxclk), + DEF_SMUX(".smux2_gbe1_txclk", CLK_SMUX2_GBE1_TXCLK, SSEL1_SELCTL0, smux2_gbe1_txclk), + DEF_SMUX(".smux2_gbe1_rxclk", CLK_SMUX2_GBE1_RXCLK, SSEL1_SELCTL1, smux2_gbe1_rxclk), + + DEF_DDIV(".pllgpu_gear", CLK_PLLGPU_GEAR, CLK_PLLGPU, CDDIV3_DIVCTL1, dtable_2_64), /* Core Clocks */ DEF_FIXED("sys_0_pclk", R9A09G056_SYS_0_PCLK, CLK_QEXTAL, 1, 1), @@ -89,13 +159,76 @@ static const struct cpg_core_clk r9a09g056_core_clks[] __initconst = { DEF_DDIV("ca55_0_coreclk3", R9A09G056_CA55_0_CORE_CLK3, CLK_PLLCA55, CDDIV1_DIVCTL3, dtable_1_8), DEF_FIXED("iotop_0_shclk", R9A09G056_IOTOP_0_SHCLK, CLK_PLLCM33_DIV16, 1, 1), + DEF_FIXED("usb2_0_clk_core0", R9A09G056_USB2_0_CLK_CORE0, CLK_QEXTAL, 1, 1), + DEF_FIXED("gbeth_0_clk_ptp_ref_i", R9A09G056_GBETH_0_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), + DEF_FIXED("gbeth_1_clk_ptp_ref_i", R9A09G056_GBETH_1_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), + DEF_FIXED_MOD_STATUS("spi_clk_spi", R9A09G056_SPI_CLK_SPI, CLK_PLLCM33_XSPI, 1, 2, + FIXED_MOD_CONF_XSPI), }; static const struct rzv2h_mod_clk r9a09g056_mod_clks[] __initconst = { DEF_MOD_CRITICAL("gic_0_gicclk", CLK_PLLDTY_ACPU_DIV4, 1, 3, 0, 19, BUS_MSTOP(3, BIT(5))), + DEF_MOD("gtm_0_pclk", CLK_PLLCM33_DIV16, 4, 3, 2, 3, + BUS_MSTOP(5, BIT(10))), + DEF_MOD("gtm_1_pclk", CLK_PLLCM33_DIV16, 4, 4, 2, 4, + BUS_MSTOP(5, BIT(11))), + DEF_MOD("gtm_2_pclk", CLK_PLLCLN_DIV16, 4, 5, 2, 5, + BUS_MSTOP(2, BIT(13))), + DEF_MOD("gtm_3_pclk", CLK_PLLCLN_DIV16, 4, 6, 2, 6, + BUS_MSTOP(2, BIT(14))), + DEF_MOD("gtm_4_pclk", CLK_PLLCLN_DIV16, 4, 7, 2, 7, + BUS_MSTOP(11, BIT(13))), + DEF_MOD("gtm_5_pclk", CLK_PLLCLN_DIV16, 4, 8, 2, 8, + BUS_MSTOP(11, BIT(14))), + DEF_MOD("gtm_6_pclk", CLK_PLLCLN_DIV16, 4, 9, 2, 9, + 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("scif_0_clk_pck", CLK_PLLCM33_DIV16, 8, 15, 4, 15, BUS_MSTOP(3, BIT(14))), + DEF_MOD("riic_8_ckm", CLK_PLLCM33_DIV16, 9, 3, 4, 19, + BUS_MSTOP(3, BIT(13))), + DEF_MOD("riic_0_ckm", CLK_PLLCLN_DIV16, 9, 4, 4, 20, + BUS_MSTOP(1, BIT(1))), + DEF_MOD("riic_1_ckm", CLK_PLLCLN_DIV16, 9, 5, 4, 21, + BUS_MSTOP(1, BIT(2))), + DEF_MOD("riic_2_ckm", CLK_PLLCLN_DIV16, 9, 6, 4, 22, + BUS_MSTOP(1, BIT(3))), + DEF_MOD("riic_3_ckm", CLK_PLLCLN_DIV16, 9, 7, 4, 23, + BUS_MSTOP(1, BIT(4))), + DEF_MOD("riic_4_ckm", CLK_PLLCLN_DIV16, 9, 8, 4, 24, + BUS_MSTOP(1, BIT(5))), + DEF_MOD("riic_5_ckm", CLK_PLLCLN_DIV16, 9, 9, 4, 25, + BUS_MSTOP(1, BIT(6))), + DEF_MOD("riic_6_ckm", CLK_PLLCLN_DIV16, 9, 10, 4, 26, + BUS_MSTOP(1, BIT(7))), + DEF_MOD("riic_7_ckm", CLK_PLLCLN_DIV16, 9, 11, 4, 27, + BUS_MSTOP(1, BIT(8))), + DEF_MOD("spi_hclk", CLK_PLLCM33_GEAR, 9, 15, 4, 31, + BUS_MSTOP(4, BIT(5))), + DEF_MOD("spi_aclk", CLK_PLLCM33_GEAR, 10, 0, 5, 0, + BUS_MSTOP(4, BIT(5))), + DEF_MOD("spi_clk_spix2", CLK_PLLCM33_XSPI, 10, 1, 5, 2, + BUS_MSTOP(4, BIT(5))), DEF_MOD("sdhi_0_imclk", CLK_PLLCLN_DIV8, 10, 3, 5, 3, BUS_MSTOP(8, BIT(2))), DEF_MOD("sdhi_0_imclk2", CLK_PLLCLN_DIV8, 10, 4, 5, 4, @@ -120,16 +253,83 @@ static const struct rzv2h_mod_clk r9a09g056_mod_clks[] __initconst = { BUS_MSTOP(8, BIT(4))), DEF_MOD("sdhi_2_aclk", CLK_PLLDTY_ACPU_DIV4, 10, 14, 5, 14, BUS_MSTOP(8, BIT(4))), + DEF_MOD("usb2_0_u2h0_hclk", CLK_PLLDTY_DIV8, 11, 3, 5, 19, + BUS_MSTOP(7, BIT(7))), + DEF_MOD("usb2_0_u2p_exr_cpuclk", CLK_PLLDTY_ACPU_DIV4, 11, 5, 5, 21, + BUS_MSTOP(7, BIT(9))), + DEF_MOD("usb2_0_pclk_usbtst0", CLK_PLLDTY_ACPU_DIV4, 11, 6, 5, 22, + BUS_MSTOP(7, BIT(10))), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_i", CLK_SMUX2_GBE0_TXCLK, 11, 8, 5, 24, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_i", CLK_SMUX2_GBE0_RXCLK, 11, 9, 5, 25, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_180_i", CLK_SMUX2_GBE0_TXCLK, 11, 10, 5, 26, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_180_i", CLK_SMUX2_GBE0_RXCLK, 11, 11, 5, 27, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD("gbeth_0_aclk_csr_i", CLK_PLLDTY_DIV8, 11, 12, 5, 28, + BUS_MSTOP(8, BIT(5))), + DEF_MOD("gbeth_0_aclk_i", CLK_PLLDTY_DIV8, 11, 13, 5, 29, + BUS_MSTOP(8, BIT(5))), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_i", CLK_SMUX2_GBE1_TXCLK, 11, 14, 5, 30, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_i", CLK_SMUX2_GBE1_RXCLK, 11, 15, 5, 31, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_180_i", CLK_SMUX2_GBE1_TXCLK, 12, 0, 6, 0, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_180_i", CLK_SMUX2_GBE1_RXCLK, 12, 1, 6, 1, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD("gbeth_1_aclk_csr_i", CLK_PLLDTY_DIV8, 12, 2, 6, 2, + 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("gpu_0_clk", CLK_PLLGPU_GEAR, 15, 0, 7, 16, + BUS_MSTOP(3, BIT(4))), + DEF_MOD("gpu_0_axi_clk", CLK_PLLDTY_ACPU_DIV2, 15, 1, 7, 17, + BUS_MSTOP(3, BIT(4))), + DEF_MOD("gpu_0_ace_clk", CLK_PLLDTY_ACPU_DIV2, 15, 2, 7, 18, + BUS_MSTOP(3, BIT(4))), }; static const struct rzv2h_reset r9a09g056_resets[] __initconst = { DEF_RST(3, 0, 1, 1), /* SYS_0_PRESETN */ DEF_RST(3, 8, 1, 9), /* GIC_0_GICRESET_N */ DEF_RST(3, 9, 1, 10), /* GIC_0_DBG_GICRESET_N */ + DEF_RST(6, 13, 2, 30), /* GTM_0_PRESETZ */ + DEF_RST(6, 14, 2, 31), /* GTM_1_PRESETZ */ + DEF_RST(6, 15, 3, 0), /* GTM_2_PRESETZ */ + DEF_RST(7, 0, 3, 1), /* GTM_3_PRESETZ */ + DEF_RST(7, 1, 3, 2), /* GTM_4_PRESETZ */ + 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(9, 5, 4, 6), /* SCIF_0_RST_SYSTEM_N */ + DEF_RST(9, 8, 4, 9), /* RIIC_0_MRST */ + DEF_RST(9, 9, 4, 10), /* RIIC_1_MRST */ + DEF_RST(9, 10, 4, 11), /* RIIC_2_MRST */ + DEF_RST(9, 11, 4, 12), /* RIIC_3_MRST */ + DEF_RST(9, 12, 4, 13), /* RIIC_4_MRST */ + DEF_RST(9, 13, 4, 14), /* RIIC_5_MRST */ + DEF_RST(9, 14, 4, 15), /* RIIC_6_MRST */ + DEF_RST(9, 15, 4, 16), /* RIIC_7_MRST */ + DEF_RST(10, 0, 4, 17), /* RIIC_8_MRST */ + DEF_RST(10, 3, 4, 20), /* SPI_HRESETN */ + DEF_RST(10, 4, 4, 21), /* SPI_ARESETN */ DEF_RST(10, 7, 4, 24), /* SDHI_0_IXRST */ DEF_RST(10, 8, 4, 25), /* SDHI_1_IXRST */ DEF_RST(10, 9, 4, 26), /* SDHI_2_IXRST */ + DEF_RST(10, 12, 4, 29), /* USB2_0_U2H0_HRESETN */ + DEF_RST(10, 14, 4, 31), /* USB2_0_U2P_EXL_SYSRST */ + 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(13, 13, 6, 14), /* GPU_0_RESETN */ + DEF_RST(13, 14, 6, 15), /* GPU_0_AXI_RESETN */ + DEF_RST(13, 15, 6, 16), /* GPU_0_ACE_RESETN */ }; const struct rzv2h_cpg_info r9a09g056_cpg_info __initconst = { diff --git a/drivers/clk/renesas/r9a09g057-cpg.c b/drivers/clk/renesas/r9a09g057-cpg.c index 3c40e36259fe..f7de69a93de1 100644 --- a/drivers/clk/renesas/r9a09g057-cpg.c +++ b/drivers/clk/renesas/r9a09g057-cpg.c @@ -16,7 +16,7 @@ enum clk_ids { /* Core Clock Outputs exported to DT */ - LAST_DT_CORE_CLK = R9A09G057_GBETH_1_CLK_PTP_REF_I, + LAST_DT_CORE_CLK = R9A09G057_SPI_CLK_SPI, /* External Input Clocks */ CLK_AUDIO_EXTAL, @@ -29,12 +29,18 @@ enum clk_ids { CLK_PLLDTY, CLK_PLLCA55, CLK_PLLVDO, + CLK_PLLETH, CLK_PLLGPU, /* Internal Core Clocks */ + CLK_PLLCM33_DIV3, CLK_PLLCM33_DIV4, - CLK_PLLCM33_DIV4_PLLCM33, + CLK_PLLCM33_DIV5, CLK_PLLCM33_DIV16, + CLK_PLLCM33_GEAR, + CLK_SMUX2_XSPI_CLK0, + CLK_SMUX2_XSPI_CLK1, + CLK_PLLCM33_XSPI, CLK_PLLCLN_DIV2, CLK_PLLCLN_DIV8, CLK_PLLCLN_DIV16, @@ -49,6 +55,14 @@ enum clk_ids { CLK_PLLVDO_CRU1, CLK_PLLVDO_CRU2, CLK_PLLVDO_CRU3, + CLK_PLLETH_DIV_250_FIX, + CLK_PLLETH_DIV_125_FIX, + CLK_CSDIV_PLLETH_GBE0, + CLK_CSDIV_PLLETH_GBE1, + CLK_SMUX2_GBE0_TXCLK, + CLK_SMUX2_GBE0_RXCLK, + CLK_SMUX2_GBE1_TXCLK, + CLK_SMUX2_GBE1_RXCLK, CLK_PLLGPU_GEAR, /* Module Clocks */ @@ -69,6 +83,14 @@ static const struct clk_div_table dtable_2_4[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_16[] = { + {0, 2}, + {1, 4}, + {2, 8}, + {3, 16}, + {0, 0}, +}; + static const struct clk_div_table dtable_2_64[] = { {0, 2}, {1, 4}, @@ -78,6 +100,21 @@ static const struct clk_div_table dtable_2_64[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_100[] = { + {0, 2}, + {1, 10}, + {2, 100}, + {0, 0}, +}; + +/* Mux clock tables */ +static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; +static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; +static const char * const smux2_gbe1_rxclk[] = { ".plleth_gbe1", "et1_rxclk" }; +static const char * const smux2_gbe1_txclk[] = { ".plleth_gbe1", "et1_txclk" }; +static const char * const smux2_xspi_clk0[] = { ".pllcm33_div3", ".pllcm33_div4" }; +static const char * const smux2_xspi_clk1[] = { ".smux2_xspi_clk0", ".pllcm33_div5" }; + static const struct cpg_core_clk r9a09g057_core_clks[] __initconst = { /* External Clock Inputs */ DEF_INPUT("audio_extal", CLK_AUDIO_EXTAL), @@ -90,13 +127,20 @@ static const struct cpg_core_clk r9a09g057_core_clks[] __initconst = { DEF_FIXED(".plldty", CLK_PLLDTY, CLK_QEXTAL, 200, 3), DEF_PLL(".pllca55", CLK_PLLCA55, CLK_QEXTAL, PLLCA55), DEF_FIXED(".pllvdo", CLK_PLLVDO, CLK_QEXTAL, 105, 2), + DEF_FIXED(".plleth", CLK_PLLETH, CLK_QEXTAL, 125, 3), DEF_PLL(".pllgpu", CLK_PLLGPU, CLK_QEXTAL, PLLGPU), /* Internal Core Clocks */ + DEF_FIXED(".pllcm33_div3", CLK_PLLCM33_DIV3, CLK_PLLCM33, 1, 3), DEF_FIXED(".pllcm33_div4", CLK_PLLCM33_DIV4, CLK_PLLCM33, 1, 4), - DEF_DDIV(".pllcm33_div4_pllcm33", CLK_PLLCM33_DIV4_PLLCM33, + DEF_FIXED(".pllcm33_div5", CLK_PLLCM33_DIV5, CLK_PLLCM33, 1, 5), + DEF_DDIV(".pllcm33_gear", CLK_PLLCM33_GEAR, CLK_PLLCM33_DIV4, CDDIV0_DIVCTL1, dtable_2_64), DEF_FIXED(".pllcm33_div16", CLK_PLLCM33_DIV16, CLK_PLLCM33, 1, 16), + DEF_SMUX(".smux2_xspi_clk0", CLK_SMUX2_XSPI_CLK0, SSEL1_SELCTL2, smux2_xspi_clk0), + DEF_SMUX(".smux2_xspi_clk1", CLK_SMUX2_XSPI_CLK1, SSEL1_SELCTL3, smux2_xspi_clk1), + DEF_CSDIV(".pllcm33_xspi", CLK_PLLCM33_XSPI, CLK_SMUX2_XSPI_CLK1, CSDIV0_DIVCTL3, + dtable_2_16), DEF_FIXED(".pllcln_div2", CLK_PLLCLN_DIV2, CLK_PLLCLN, 1, 2), DEF_FIXED(".pllcln_div8", CLK_PLLCLN_DIV8, CLK_PLLCLN, 1, 8), @@ -115,6 +159,17 @@ static const struct cpg_core_clk r9a09g057_core_clks[] __initconst = { DEF_DDIV(".pllvdo_cru2", CLK_PLLVDO_CRU2, CLK_PLLVDO, CDDIV4_DIVCTL1, dtable_2_4), DEF_DDIV(".pllvdo_cru3", CLK_PLLVDO_CRU3, CLK_PLLVDO, CDDIV4_DIVCTL2, dtable_2_4), + DEF_FIXED(".plleth_250_fix", CLK_PLLETH_DIV_250_FIX, CLK_PLLETH, 1, 4), + DEF_FIXED(".plleth_125_fix", CLK_PLLETH_DIV_125_FIX, CLK_PLLETH_DIV_250_FIX, 1, 2), + DEF_CSDIV(".plleth_gbe0", CLK_CSDIV_PLLETH_GBE0, + CLK_PLLETH_DIV_250_FIX, CSDIV0_DIVCTL0, dtable_2_100), + DEF_CSDIV(".plleth_gbe1", CLK_CSDIV_PLLETH_GBE1, + CLK_PLLETH_DIV_250_FIX, CSDIV0_DIVCTL1, dtable_2_100), + DEF_SMUX(".smux2_gbe0_txclk", CLK_SMUX2_GBE0_TXCLK, SSEL0_SELCTL2, smux2_gbe0_txclk), + DEF_SMUX(".smux2_gbe0_rxclk", CLK_SMUX2_GBE0_RXCLK, SSEL0_SELCTL3, smux2_gbe0_rxclk), + DEF_SMUX(".smux2_gbe1_txclk", CLK_SMUX2_GBE1_TXCLK, SSEL1_SELCTL0, smux2_gbe1_txclk), + DEF_SMUX(".smux2_gbe1_rxclk", CLK_SMUX2_GBE1_RXCLK, SSEL1_SELCTL1, smux2_gbe1_rxclk), + DEF_DDIV(".pllgpu_gear", CLK_PLLGPU_GEAR, CLK_PLLGPU, CDDIV3_DIVCTL1, dtable_2_64), /* Core Clocks */ @@ -130,10 +185,16 @@ static const struct cpg_core_clk r9a09g057_core_clks[] __initconst = { DEF_FIXED("iotop_0_shclk", R9A09G057_IOTOP_0_SHCLK, CLK_PLLCM33_DIV16, 1, 1), DEF_FIXED("usb2_0_clk_core0", R9A09G057_USB2_0_CLK_CORE0, CLK_QEXTAL, 1, 1), DEF_FIXED("usb2_0_clk_core1", R9A09G057_USB2_0_CLK_CORE1, CLK_QEXTAL, 1, 1), + DEF_FIXED("gbeth_0_clk_ptp_ref_i", R9A09G057_GBETH_0_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), + DEF_FIXED("gbeth_1_clk_ptp_ref_i", R9A09G057_GBETH_1_CLK_PTP_REF_I, + CLK_PLLETH_DIV_125_FIX, 1, 1), + DEF_FIXED_MOD_STATUS("spi_clk_spi", R9A09G057_SPI_CLK_SPI, CLK_PLLCM33_XSPI, 1, 2, + FIXED_MOD_CONF_XSPI), }; static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { - DEF_MOD("dmac_0_aclk", CLK_PLLCM33_DIV4_PLLCM33, 0, 0, 0, 0, + DEF_MOD("dmac_0_aclk", CLK_PLLCM33_GEAR, 0, 0, 0, 0, BUS_MSTOP(5, BIT(9))), DEF_MOD("dmac_1_aclk", CLK_PLLDTY_ACPU_DIV2, 0, 1, 0, 1, BUS_MSTOP(3, BIT(2))), @@ -179,6 +240,24 @@ static const struct rzv2h_mod_clk r9a09g057_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("scif_0_clk_pck", CLK_PLLCM33_DIV16, 8, 15, 4, 15, BUS_MSTOP(3, BIT(14))), DEF_MOD("riic_8_ckm", CLK_PLLCM33_DIV16, 9, 3, 4, 19, @@ -199,6 +278,12 @@ static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { BUS_MSTOP(1, BIT(7))), DEF_MOD("riic_7_ckm", CLK_PLLCLN_DIV16, 9, 11, 4, 27, BUS_MSTOP(1, BIT(8))), + DEF_MOD("spi_hclk", CLK_PLLCM33_GEAR, 9, 15, 4, 31, + BUS_MSTOP(4, BIT(5))), + DEF_MOD("spi_aclk", CLK_PLLCM33_GEAR, 10, 0, 5, 0, + BUS_MSTOP(4, BIT(5))), + DEF_MOD("spi_clk_spix2", CLK_PLLCM33_XSPI, 10, 1, 5, 2, + BUS_MSTOP(4, BIT(5))), DEF_MOD("sdhi_0_imclk", CLK_PLLCLN_DIV8, 10, 3, 5, 3, BUS_MSTOP(8, BIT(2))), DEF_MOD("sdhi_0_imclk2", CLK_PLLCLN_DIV8, 10, 4, 5, 4, @@ -233,6 +318,30 @@ static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { BUS_MSTOP(7, BIT(10))), DEF_MOD("usb2_0_pclk_usbtst1", CLK_PLLDTY_ACPU_DIV4, 11, 7, 5, 23, BUS_MSTOP(7, BIT(11))), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_i", CLK_SMUX2_GBE0_TXCLK, 11, 8, 5, 24, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_i", CLK_SMUX2_GBE0_RXCLK, 11, 9, 5, 25, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_tx_180_i", CLK_SMUX2_GBE0_TXCLK, 11, 10, 5, 26, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_0_clk_rx_180_i", CLK_SMUX2_GBE0_RXCLK, 11, 11, 5, 27, + BUS_MSTOP(8, BIT(5)), 1), + DEF_MOD("gbeth_0_aclk_csr_i", CLK_PLLDTY_DIV8, 11, 12, 5, 28, + BUS_MSTOP(8, BIT(5))), + DEF_MOD("gbeth_0_aclk_i", CLK_PLLDTY_DIV8, 11, 13, 5, 29, + BUS_MSTOP(8, BIT(5))), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_i", CLK_SMUX2_GBE1_TXCLK, 11, 14, 5, 30, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_i", CLK_SMUX2_GBE1_RXCLK, 11, 15, 5, 31, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_tx_180_i", CLK_SMUX2_GBE1_TXCLK, 12, 0, 6, 0, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD_MUX_EXTERNAL("gbeth_1_clk_rx_180_i", CLK_SMUX2_GBE1_RXCLK, 12, 1, 6, 1, + BUS_MSTOP(8, BIT(6)), 1), + DEF_MOD("gbeth_1_aclk_csr_i", CLK_PLLDTY_DIV8, 12, 2, 6, 2, + 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("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, @@ -287,6 +396,12 @@ static const struct rzv2h_reset r9a09g057_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(9, 5, 4, 6), /* SCIF_0_RST_SYSTEM_N */ DEF_RST(9, 8, 4, 9), /* RIIC_0_MRST */ DEF_RST(9, 9, 4, 10), /* RIIC_1_MRST */ @@ -297,6 +412,8 @@ static const struct rzv2h_reset r9a09g057_resets[] __initconst = { DEF_RST(9, 14, 4, 15), /* RIIC_6_MRST */ DEF_RST(9, 15, 4, 16), /* RIIC_7_MRST */ DEF_RST(10, 0, 4, 17), /* RIIC_8_MRST */ + DEF_RST(10, 3, 4, 20), /* SPI_HRESETN */ + DEF_RST(10, 4, 4, 21), /* SPI_ARESETN */ DEF_RST(10, 7, 4, 24), /* SDHI_0_IXRST */ DEF_RST(10, 8, 4, 25), /* SDHI_1_IXRST */ DEF_RST(10, 9, 4, 26), /* SDHI_2_IXRST */ @@ -304,6 +421,8 @@ static const struct rzv2h_reset r9a09g057_resets[] __initconst = { DEF_RST(10, 13, 4, 30), /* USB2_0_U2H1_HRESETN */ DEF_RST(10, 14, 4, 31), /* USB2_0_U2P_EXL_SYSRST */ 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(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/r9a09g077-cpg.c b/drivers/clk/renesas/r9a09g077-cpg.c new file mode 100644 index 000000000000..c920d6a9707f --- /dev/null +++ b/drivers/clk/renesas/r9a09g077-cpg.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r9a09g077 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2025 Renesas Electronics Corp. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "renesas-cpg-mssr.h" + +#define RZT2H_REG_BLOCK_SHIFT 11 +#define RZT2H_REG_OFFSET_MASK GENMASK(10, 0) +#define RZT2H_REG_CONF(block, offset) (((block) << RZT2H_REG_BLOCK_SHIFT) | \ + ((offset) & RZT2H_REG_OFFSET_MASK)) + +#define RZT2H_REG_BLOCK(x) ((x) >> RZT2H_REG_BLOCK_SHIFT) +#define RZT2H_REG_OFFSET(x) ((x) & RZT2H_REG_OFFSET_MASK) + +#define SCKCR RZT2H_REG_CONF(0, 0x00) +#define SCKCR2 RZT2H_REG_CONF(1, 0x04) +#define SCKCR3 RZT2H_REG_CONF(0, 0x08) + +#define OFFSET_MASK GENMASK(31, 20) +#define SHIFT_MASK GENMASK(19, 12) +#define WIDTH_MASK GENMASK(11, 8) + +#define CONF_PACK(offset, shift, width) \ + (FIELD_PREP_CONST(OFFSET_MASK, (offset)) | \ + FIELD_PREP_CONST(SHIFT_MASK, (shift)) | \ + FIELD_PREP_CONST(WIDTH_MASK, (width))) + +#define GET_SHIFT(val) FIELD_GET(SHIFT_MASK, val) +#define GET_WIDTH(val) FIELD_GET(WIDTH_MASK, val) +#define GET_REG_OFFSET(val) FIELD_GET(OFFSET_MASK, val) + +#define DIVCA55C0 CONF_PACK(SCKCR2, 8, 1) +#define DIVCA55C1 CONF_PACK(SCKCR2, 9, 1) +#define DIVCA55C2 CONF_PACK(SCKCR2, 10, 1) +#define DIVCA55C3 CONF_PACK(SCKCR2, 11, 1) +#define DIVCA55S CONF_PACK(SCKCR2, 12, 1) + +#define DIVSCI0ASYNC CONF_PACK(SCKCR3, 6, 2) + +#define SEL_PLL CONF_PACK(SCKCR, 22, 1) + + +enum rzt2h_clk_types { + CLK_TYPE_RZT2H_DIV = CLK_TYPE_CUSTOM, /* Clock with divider */ + CLK_TYPE_RZT2H_MUX, /* Clock with clock source selector */ +}; + +#define DEF_DIV(_name, _id, _parent, _conf, _dtable) \ + DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_DIV, .conf = _conf, \ + .parent = _parent, .dtable = _dtable, .flag = 0) +#define DEF_MUX(_name, _id, _conf, _parent_names, _num_parents, _mux_flags) \ + DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_MUX, .conf = _conf, \ + .parent_names = _parent_names, .num_parents = _num_parents, \ + .flag = 0, .mux_flags = _mux_flags) + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R9A09G077_SDHI_CLKHS, + + /* External Input Clocks */ + CLK_EXTAL, + + /* Internal Core Clocks */ + CLK_LOCO, + CLK_PLL0, + CLK_PLL1, + CLK_PLL2, + CLK_PLL4, + CLK_SEL_CLK_PLL0, + CLK_SEL_CLK_PLL1, + CLK_SEL_CLK_PLL2, + CLK_SEL_CLK_PLL4, + CLK_PLL4D1, + CLK_SCI0ASYNC, + + /* Module Clocks */ + MOD_CLK_BASE, +}; + +static const struct clk_div_table dtable_1_2[] = { + {0, 2}, + {1, 1}, + {0, 0}, +}; + +static const struct clk_div_table dtable_24_25_30_32[] = { + {0, 32}, + {1, 30}, + {2, 25}, + {3, 24}, + {0, 0}, +}; + +/* Mux clock tables */ + +static const char * const sel_clk_pll0[] = { ".loco", ".pll0" }; +static const char * const sel_clk_pll1[] = { ".loco", ".pll1" }; +static const char * const sel_clk_pll2[] = { ".loco", ".pll2" }; +static const char * const sel_clk_pll4[] = { ".loco", ".pll4" }; + +static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + + /* Internal Core Clocks */ + DEF_RATE(".loco", CLK_LOCO, 1000 * 1000), + DEF_FIXED(".pll0", CLK_PLL0, CLK_EXTAL, 1, 48), + DEF_FIXED(".pll1", CLK_PLL1, CLK_EXTAL, 1, 40), + DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 1, 32), + DEF_FIXED(".pll4", CLK_PLL4, CLK_EXTAL, 1, 96), + + DEF_MUX(".sel_clk_pll0", CLK_SEL_CLK_PLL0, SEL_PLL, + sel_clk_pll0, ARRAY_SIZE(sel_clk_pll0), CLK_MUX_READ_ONLY), + DEF_MUX(".sel_clk_pll1", CLK_SEL_CLK_PLL1, SEL_PLL, + sel_clk_pll1, ARRAY_SIZE(sel_clk_pll1), CLK_MUX_READ_ONLY), + DEF_MUX(".sel_clk_pll2", CLK_SEL_CLK_PLL2, SEL_PLL, + sel_clk_pll2, ARRAY_SIZE(sel_clk_pll2), CLK_MUX_READ_ONLY), + DEF_MUX(".sel_clk_pll4", CLK_SEL_CLK_PLL4, SEL_PLL, + sel_clk_pll4, ARRAY_SIZE(sel_clk_pll4), CLK_MUX_READ_ONLY), + + DEF_FIXED(".pll4d1", CLK_PLL4D1, CLK_SEL_CLK_PLL4, 1, 1), + DEF_DIV(".sci0async", CLK_SCI0ASYNC, CLK_PLL4D1, DIVSCI0ASYNC, + dtable_24_25_30_32), + + /* Core output clk */ + DEF_DIV("CA55C0", R9A09G077_CLK_CA55C0, CLK_SEL_CLK_PLL0, DIVCA55C0, + dtable_1_2), + DEF_DIV("CA55C1", R9A09G077_CLK_CA55C1, CLK_SEL_CLK_PLL0, DIVCA55C1, + dtable_1_2), + DEF_DIV("CA55C2", R9A09G077_CLK_CA55C2, CLK_SEL_CLK_PLL0, DIVCA55C2, + dtable_1_2), + DEF_DIV("CA55C3", R9A09G077_CLK_CA55C3, CLK_SEL_CLK_PLL0, DIVCA55C3, + dtable_1_2), + DEF_DIV("CA55S", R9A09G077_CLK_CA55S, CLK_SEL_CLK_PLL0, DIVCA55S, + dtable_1_2), + DEF_FIXED("PCLKGPTL", R9A09G077_CLK_PCLKGPTL, CLK_SEL_CLK_PLL1, 2, 1), + DEF_FIXED("PCLKM", R9A09G077_CLK_PCLKM, CLK_SEL_CLK_PLL1, 8, 1), + DEF_FIXED("PCLKL", R9A09G077_CLK_PCLKL, CLK_SEL_CLK_PLL1, 16, 1), + DEF_FIXED("PCLKAM", R9A09G077_CLK_PCLKAM, CLK_PLL4D1, 12, 1), + DEF_FIXED("SDHI_CLKHS", R9A09G077_SDHI_CLKHS, CLK_SEL_CLK_PLL2, 1, 1), +}; + +static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = { + DEF_MOD("sci0fck", 8, CLK_SCI0ASYNC), + DEF_MOD("iic0", 100, R9A09G077_CLK_PCLKL), + DEF_MOD("iic1", 101, R9A09G077_CLK_PCLKL), + DEF_MOD("iic2", 601, R9A09G077_CLK_PCLKL), + DEF_MOD("sdhi0", 1212, R9A09G077_CLK_PCLKAM), + DEF_MOD("sdhi1", 1213, R9A09G077_CLK_PCLKAM), +}; + +static struct clk * __init +r9a09g077_cpg_div_clk_register(struct device *dev, + const struct cpg_core_clk *core, + void __iomem *addr, struct cpg_mssr_pub *pub) +{ + const struct clk *parent; + const char *parent_name; + struct clk_hw *clk_hw; + + parent = pub->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = __clk_get_name(parent); + + if (core->dtable) + clk_hw = clk_hw_register_divider_table(dev, core->name, + parent_name, 0, + addr, + GET_SHIFT(core->conf), + GET_WIDTH(core->conf), + core->flag, + core->dtable, + &pub->rmw_lock); + else + clk_hw = clk_hw_register_divider(dev, core->name, + parent_name, 0, + addr, + GET_SHIFT(core->conf), + GET_WIDTH(core->conf), + core->flag, &pub->rmw_lock); + + if (IS_ERR(clk_hw)) + return ERR_CAST(clk_hw); + + return clk_hw->clk; + +} + +static struct clk * __init +r9a09g077_cpg_mux_clk_register(struct device *dev, + const struct cpg_core_clk *core, + void __iomem *addr, struct cpg_mssr_pub *pub) +{ + struct clk_hw *clk_hw; + + clk_hw = devm_clk_hw_register_mux(dev, core->name, + core->parent_names, core->num_parents, + core->flag, + addr, + GET_SHIFT(core->conf), + GET_WIDTH(core->conf), + core->mux_flags, &pub->rmw_lock); + if (IS_ERR(clk_hw)) + return ERR_CAST(clk_hw); + + return clk_hw->clk; +} + +static struct clk * __init +r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct cpg_mssr_pub *pub) +{ + u32 offset = GET_REG_OFFSET(core->conf); + void __iomem *base = RZT2H_REG_BLOCK(offset) ? pub->base1 : pub->base0; + void __iomem *addr = base + RZT2H_REG_OFFSET(offset); + + switch (core->type) { + case CLK_TYPE_RZT2H_DIV: + return r9a09g077_cpg_div_clk_register(dev, core, addr, pub); + case CLK_TYPE_RZT2H_MUX: + return r9a09g077_cpg_mux_clk_register(dev, core, addr, pub); + default: + return ERR_PTR(-EINVAL); + } +} + +const struct cpg_mssr_info r9a09g077_cpg_mssr_info = { + /* Core Clocks */ + .core_clks = r9a09g077_core_clks, + .num_core_clks = ARRAY_SIZE(r9a09g077_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r9a09g077_mod_clks, + .num_mod_clks = ARRAY_SIZE(r9a09g077_mod_clks), + .num_hw_mod_clks = 14 * 32, + + .reg_layout = CLK_REG_LAYOUT_RZ_T2H, + .cpg_clk_register = r9a09g077_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/rcar-gen2-cpg.c b/drivers/clk/renesas/rcar-gen2-cpg.c index 4c3764972bad..ab34bb8c3e07 100644 --- a/drivers/clk/renesas/rcar-gen2-cpg.c +++ b/drivers/clk/renesas/rcar-gen2-cpg.c @@ -274,10 +274,11 @@ static const struct soc_device_attribute cpg_quirks_match[] __initconst = { struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers) + struct cpg_mssr_pub *pub) { const struct clk_div_table *table = NULL; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; const struct clk *parent; const char *parent_name; unsigned int mult = 1; diff --git a/drivers/clk/renesas/rcar-gen2-cpg.h b/drivers/clk/renesas/rcar-gen2-cpg.h index bdcd4a38d48d..3d4b127fdeaf 100644 --- a/drivers/clk/renesas/rcar-gen2-cpg.h +++ b/drivers/clk/renesas/rcar-gen2-cpg.h @@ -32,8 +32,7 @@ struct rcar_gen2_cpg_pll_config { struct clk *rcar_gen2_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers); + struct cpg_mssr_pub *pub); int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config, unsigned int pll0_div, u32 mode); diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 027100e84ee4..10ae20489df9 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -345,9 +345,11 @@ static const struct soc_device_attribute cpg_quirks_match[] __initconst = { struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers) + struct cpg_mssr_pub *pub) { + struct raw_notifier_head *notifiers = &pub->notifiers; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; const struct clk *parent; unsigned int mult = 1; unsigned int div = 1; diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index bfdc649bdf12..d15a5d1df71c 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -81,8 +81,7 @@ struct rcar_gen3_cpg_pll_config { struct clk *rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers); + struct cpg_mssr_pub *pub); int rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, unsigned int clk_extalr, u32 mode); diff --git a/drivers/clk/renesas/rcar-gen4-cpg.c b/drivers/clk/renesas/rcar-gen4-cpg.c index 31aa790fd003..fb9a876aaba5 100644 --- a/drivers/clk/renesas/rcar-gen4-cpg.c +++ b/drivers/clk/renesas/rcar-gen4-cpg.c @@ -418,9 +418,11 @@ static const struct clk_div_table cpg_rpcsrc_div_table[] = { struct clk * __init rcar_gen4_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers) + struct cpg_mssr_pub *pub) { + struct raw_notifier_head *notifiers = &pub->notifiers; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; const struct clk *parent; unsigned int mult = 1; unsigned int div = 1; diff --git a/drivers/clk/renesas/rcar-gen4-cpg.h b/drivers/clk/renesas/rcar-gen4-cpg.h index 717fd148464f..6c8280b37c37 100644 --- a/drivers/clk/renesas/rcar-gen4-cpg.h +++ b/drivers/clk/renesas/rcar-gen4-cpg.h @@ -78,8 +78,7 @@ struct rcar_gen4_cpg_pll_config { struct clk *rcar_gen4_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers); + struct cpg_mssr_pub *pub); int rcar_gen4_cpg_init(const struct rcar_gen4_cpg_pll_config *config, unsigned int clk_extalr, u32 mode); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 71431970d6e6..5ff6ee1f7d4b 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -80,6 +80,37 @@ static const u16 mstpcr_for_gen4[] = { 0x2D60, 0x2D64, 0x2D68, 0x2D6C, 0x2D70, 0x2D74, }; +/* + * Module Stop Control Register (RZ/T2H) + * RZ/T2H has 2 registers blocks, + * Bit 12 is used to differentiate them + */ + +#define RZT2H_MSTPCR_BLOCK_SHIFT 12 +#define RZT2H_MSTPCR_OFFSET_MASK GENMASK(11, 0) +#define RZT2H_MSTPCR(block, offset) (((block) << RZT2H_MSTPCR_BLOCK_SHIFT) | \ + ((offset) & RZT2H_MSTPCR_OFFSET_MASK)) + +#define RZT2H_MSTPCR_BLOCK(x) ((x) >> RZT2H_MSTPCR_BLOCK_SHIFT) +#define RZT2H_MSTPCR_OFFSET(x) ((x) & RZT2H_MSTPCR_OFFSET_MASK) + +static const u16 mstpcr_for_rzt2h[] = { + RZT2H_MSTPCR(0, 0x300), /* MSTPCRA */ + RZT2H_MSTPCR(0, 0x304), /* MSTPCRB */ + RZT2H_MSTPCR(0, 0x308), /* MSTPCRC */ + RZT2H_MSTPCR(0, 0x30c), /* MSTPCRD */ + RZT2H_MSTPCR(0, 0x310), /* MSTPCRE */ + 0, + RZT2H_MSTPCR(1, 0x318), /* MSTPCRG */ + 0, + RZT2H_MSTPCR(1, 0x320), /* MSTPCRI */ + RZT2H_MSTPCR(0, 0x324), /* MSTPCRJ */ + RZT2H_MSTPCR(0, 0x328), /* MSTPCRK */ + RZT2H_MSTPCR(0, 0x32c), /* MSTPCRL */ + RZT2H_MSTPCR(0, 0x330), /* MSTPCRM */ + RZT2H_MSTPCR(1, 0x334), /* MSTPCRN */ +}; + /* * Standby Control Register offsets (RZ/A) * Base address is FRQCR register @@ -126,16 +157,14 @@ static const u16 srstclr_for_gen4[] = { * struct cpg_mssr_priv - Clock Pulse Generator / Module Standby * and Software Reset Private Data * + * @pub: Data passed to clock registration callback * @rcdev: Optional reset controller entity * @dev: CPG/MSSR device - * @base: CPG/MSSR register block base address * @reg_layout: CPG/MSSR register layout - * @rmw_lock: protects RMW register accesses * @np: Device node in DT for this CPG/MSSR module * @num_core_clks: Number of Core Clocks in clks[] * @num_mod_clks: Number of Module Clocks in clks[] * @last_dt_core_clk: ID of the last Core Clock exported to DT - * @notifiers: Notifier chain to save/restore clock state for system resume * @status_regs: Pointer to status registers array * @control_regs: Pointer to control registers array * @reset_regs: Pointer to reset registers array @@ -147,20 +176,18 @@ static const u16 srstclr_for_gen4[] = { * @clks: Array containing all Core and Module Clocks */ struct cpg_mssr_priv { + struct cpg_mssr_pub pub; #ifdef CONFIG_RESET_CONTROLLER struct reset_controller_dev rcdev; #endif struct device *dev; - void __iomem *base; enum clk_reg_layout reg_layout; - spinlock_t rmw_lock; struct device_node *np; unsigned int num_core_clks; unsigned int num_mod_clks; unsigned int last_dt_core_clk; - struct raw_notifier_head notifiers; const u16 *status_regs; const u16 *control_regs; const u16 *reset_regs; @@ -192,6 +219,26 @@ struct mstp_clock { #define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) +static u32 cpg_rzt2h_mstp_read(struct clk_hw *hw, u16 offset) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct cpg_mssr_priv *priv = clock->priv; + void __iomem *base = + RZT2H_MSTPCR_BLOCK(offset) ? priv->pub.base1 : priv->pub.base0; + + return readl(base + RZT2H_MSTPCR_OFFSET(offset)); +} + +static void cpg_rzt2h_mstp_write(struct clk_hw *hw, u16 offset, u32 value) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct cpg_mssr_priv *priv = clock->priv; + void __iomem *base = + RZT2H_MSTPCR_BLOCK(offset) ? priv->pub.base1 : priv->pub.base0; + + writel(value, base + RZT2H_MSTPCR_OFFSET(offset)); +} + static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) { struct mstp_clock *clock = to_mstp_clock(hw); @@ -206,38 +253,52 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk, str_on_off(enable)); - spin_lock_irqsave(&priv->rmw_lock, flags); + spin_lock_irqsave(&priv->pub.rmw_lock, flags); if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { - value = readb(priv->base + priv->control_regs[reg]); + value = readb(priv->pub.base0 + priv->control_regs[reg]); if (enable) value &= ~bitmask; else value |= bitmask; - writeb(value, priv->base + priv->control_regs[reg]); + writeb(value, priv->pub.base0 + priv->control_regs[reg]); /* dummy read to ensure write has completed */ - readb(priv->base + priv->control_regs[reg]); - barrier_data(priv->base + priv->control_regs[reg]); - } else { - value = readl(priv->base + priv->control_regs[reg]); + readb(priv->pub.base0 + priv->control_regs[reg]); + barrier_data(priv->pub.base0 + priv->control_regs[reg]); + + } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { + value = cpg_rzt2h_mstp_read(hw, + priv->control_regs[reg]); + if (enable) value &= ~bitmask; else value |= bitmask; - writel(value, priv->base + priv->control_regs[reg]); + + cpg_rzt2h_mstp_write(hw, + priv->control_regs[reg], + value); + } else { + value = readl(priv->pub.base0 + priv->control_regs[reg]); + if (enable) + value &= ~bitmask; + else + value |= bitmask; + writel(value, priv->pub.base0 + priv->control_regs[reg]); } - spin_unlock_irqrestore(&priv->rmw_lock, flags); + spin_unlock_irqrestore(&priv->pub.rmw_lock, flags); - if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A) + if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A || + priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) return 0; - error = readl_poll_timeout_atomic(priv->base + priv->status_regs[reg], + error = readl_poll_timeout_atomic(priv->pub.base0 + priv->status_regs[reg], value, !(value & bitmask), 0, 10); if (error) dev_err(dev, "Failed to enable SMSTP %p[%d]\n", - priv->base + priv->control_regs[reg], bit); + priv->pub.base0 + priv->control_regs[reg], bit); return error; } @@ -256,12 +317,16 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) { struct mstp_clock *clock = to_mstp_clock(hw); struct cpg_mssr_priv *priv = clock->priv; + unsigned int reg = clock->index / 32; u32 value; if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) - value = readb(priv->base + priv->control_regs[clock->index / 32]); + value = readb(priv->pub.base0 + priv->control_regs[reg]); + else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) + value = cpg_rzt2h_mstp_read(hw, + priv->control_regs[reg]); else - value = readl(priv->base + priv->status_regs[clock->index / 32]); + value = readl(priv->pub.base0 + priv->status_regs[reg]); return !(value & BIT(clock->index % 32)); } @@ -348,7 +413,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_DIV6P1: case CLK_TYPE_DIV6_RO: WARN_DEBUG(core->parent >= priv->num_core_clks); - parent = priv->clks[core->parent]; + parent = priv->pub.clks[core->parent]; if (IS_ERR(parent)) { clk = parent; goto fail; @@ -358,12 +423,12 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, if (core->type == CLK_TYPE_DIV6_RO) /* Multiply with the DIV6 register value */ - div *= (readl(priv->base + core->offset) & 0x3f) + 1; + div *= (readl(priv->pub.base0 + core->offset) & 0x3f) + 1; if (core->type == CLK_TYPE_DIV6P1) { clk = cpg_div6_register(core->name, 1, &parent_name, - priv->base + core->offset, - &priv->notifiers); + priv->pub.base0 + core->offset, + &priv->pub.notifiers); } else { clk = clk_register_fixed_factor(NULL, core->name, parent_name, 0, @@ -379,8 +444,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, default: if (info->cpg_clk_register) clk = info->cpg_clk_register(dev, core, info, - priv->clks, priv->base, - &priv->notifiers); + &priv->pub); else dev_err(dev, "%s has unsupported core clock type %u\n", core->name, core->type); @@ -391,7 +455,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, goto fail; dev_dbg(dev, "Core clock %pC at %lu Hz\n", clk, clk_get_rate(clk)); - priv->clks[id] = clk; + priv->pub.clks[id] = clk; return; fail: @@ -414,14 +478,14 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, WARN_DEBUG(id < priv->num_core_clks); WARN_DEBUG(id >= priv->num_core_clks + priv->num_mod_clks); WARN_DEBUG(mod->parent >= priv->num_core_clks + priv->num_mod_clks); - WARN_DEBUG(PTR_ERR(priv->clks[id]) != -ENOENT); + WARN_DEBUG(PTR_ERR(priv->pub.clks[id]) != -ENOENT); if (!mod->name) { /* Skip NULLified clock */ return; } - parent = priv->clks[mod->parent]; + parent = priv->pub.clks[mod->parent]; if (IS_ERR(parent)) { clk = parent; goto fail; @@ -623,13 +687,13 @@ static int cpg_mssr_reset(struct reset_controller_dev *rcdev, dev_dbg(priv->dev, "reset %u%02u\n", reg, bit); /* Reset module */ - writel(bitmask, priv->base + priv->reset_regs[reg]); + writel(bitmask, priv->pub.base0 + priv->reset_regs[reg]); /* Wait for at least one cycle of the RCLK clock (@ ca. 32 kHz) */ udelay(35); /* Release module from reset state */ - writel(bitmask, priv->base + priv->reset_clear_regs[reg]); + writel(bitmask, priv->pub.base0 + priv->reset_clear_regs[reg]); return 0; } @@ -643,7 +707,7 @@ static int cpg_mssr_assert(struct reset_controller_dev *rcdev, unsigned long id) dev_dbg(priv->dev, "assert %u%02u\n", reg, bit); - writel(bitmask, priv->base + priv->reset_regs[reg]); + writel(bitmask, priv->pub.base0 + priv->reset_regs[reg]); return 0; } @@ -657,7 +721,7 @@ static int cpg_mssr_deassert(struct reset_controller_dev *rcdev, dev_dbg(priv->dev, "deassert %u%02u\n", reg, bit); - writel(bitmask, priv->base + priv->reset_clear_regs[reg]); + writel(bitmask, priv->pub.base0 + priv->reset_clear_regs[reg]); return 0; } @@ -669,7 +733,7 @@ static int cpg_mssr_status(struct reset_controller_dev *rcdev, unsigned int bit = id % 32; u32 bitmask = BIT(bit); - return !!(readl(priv->base + priv->reset_regs[reg]) & bitmask); + return !!(readl(priv->pub.base0 + priv->reset_regs[reg]) & bitmask); } static const struct reset_control_ops cpg_mssr_reset_ops = { @@ -871,6 +935,18 @@ static const struct of_device_id cpg_mssr_match[] = { .compatible = "renesas,r8a779h0-cpg-mssr", .data = &r8a779h0_cpg_mssr_info, }, +#endif +#ifdef CONFIG_CLK_R9A09G077 + { + .compatible = "renesas,r9a09g077-cpg-mssr", + .data = &r9a09g077_cpg_mssr_info, + }, +#endif +#ifdef CONFIG_CLK_R9A09G087 + { + .compatible = "renesas,r9a09g087-cpg-mssr", + .data = &r9a09g077_cpg_mssr_info, + }, #endif { /* sentinel */ } }; @@ -895,12 +971,12 @@ static int cpg_mssr_suspend_noirq(struct device *dev) if (priv->smstpcr_saved[reg].mask) priv->smstpcr_saved[reg].val = priv->reg_layout == CLK_REG_LAYOUT_RZ_A ? - readb(priv->base + priv->control_regs[reg]) : - readl(priv->base + priv->control_regs[reg]); + readb(priv->pub.base0 + priv->control_regs[reg]) : + readl(priv->pub.base0 + priv->control_regs[reg]); } /* Save core clocks */ - raw_notifier_call_chain(&priv->notifiers, PM_EVENT_SUSPEND, NULL); + raw_notifier_call_chain(&priv->pub.notifiers, PM_EVENT_SUSPEND, NULL); return 0; } @@ -917,7 +993,7 @@ static int cpg_mssr_resume_noirq(struct device *dev) return 0; /* Restore core clocks */ - raw_notifier_call_chain(&priv->notifiers, PM_EVENT_RESUME, NULL); + raw_notifier_call_chain(&priv->pub.notifiers, PM_EVENT_RESUME, NULL); /* Restore module clocks */ for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) { @@ -926,29 +1002,29 @@ static int cpg_mssr_resume_noirq(struct device *dev) continue; if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) - oldval = readb(priv->base + priv->control_regs[reg]); + oldval = readb(priv->pub.base0 + priv->control_regs[reg]); else - oldval = readl(priv->base + priv->control_regs[reg]); + oldval = readl(priv->pub.base0 + priv->control_regs[reg]); newval = oldval & ~mask; newval |= priv->smstpcr_saved[reg].val & mask; if (newval == oldval) continue; if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { - writeb(newval, priv->base + priv->control_regs[reg]); + writeb(newval, priv->pub.base0 + priv->control_regs[reg]); /* dummy read to ensure write has completed */ - readb(priv->base + priv->control_regs[reg]); - barrier_data(priv->base + priv->control_regs[reg]); + readb(priv->pub.base0 + priv->control_regs[reg]); + barrier_data(priv->pub.base0 + priv->control_regs[reg]); continue; } else - writel(newval, priv->base + priv->control_regs[reg]); + writel(newval, priv->pub.base0 + priv->control_regs[reg]); /* Wait until enabled clocks are really enabled */ mask &= ~priv->smstpcr_saved[reg].val; if (!mask) continue; - error = readl_poll_timeout_atomic(priv->base + priv->status_regs[reg], + error = readl_poll_timeout_atomic(priv->pub.base0 + priv->status_regs[reg], oldval, !(oldval & mask), 0, 10); if (error) dev_warn(dev, "Failed to enable SMSTP%u[0x%x]\n", reg, @@ -1058,20 +1134,28 @@ static int __init cpg_mssr_common_init(struct device *dev, if (!priv) return -ENOMEM; + priv->pub.clks = priv->clks; priv->np = np; priv->dev = dev; - spin_lock_init(&priv->rmw_lock); + spin_lock_init(&priv->pub.rmw_lock); - priv->base = of_iomap(np, 0); - if (!priv->base) { + priv->pub.base0 = of_iomap(np, 0); + if (!priv->pub.base0) { error = -ENOMEM; goto out_err; } + if (info->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { + priv->pub.base1 = of_iomap(np, 1); + if (!priv->pub.base1) { + error = -ENOMEM; + goto out_err; + } + } priv->num_core_clks = info->num_total_core_clks; priv->num_mod_clks = info->num_hw_mod_clks; priv->last_dt_core_clk = info->last_dt_core_clk; - RAW_INIT_NOTIFIER_HEAD(&priv->notifiers); + RAW_INIT_NOTIFIER_HEAD(&priv->pub.notifiers); priv->reg_layout = info->reg_layout; if (priv->reg_layout == CLK_REG_LAYOUT_RCAR_GEN2_AND_GEN3) { priv->status_regs = mstpsr; @@ -1080,6 +1164,8 @@ static int __init cpg_mssr_common_init(struct device *dev, priv->reset_clear_regs = srstclr; } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { priv->control_regs = stbcr; + } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { + priv->control_regs = mstpcr_for_rzt2h; } else if (priv->reg_layout == CLK_REG_LAYOUT_RCAR_GEN4) { priv->status_regs = mstpsr_for_gen4; priv->control_regs = mstpcr_for_gen4; @@ -1091,7 +1177,7 @@ static int __init cpg_mssr_common_init(struct device *dev, } for (i = 0; i < nclks; i++) - priv->clks[i] = ERR_PTR(-ENOENT); + priv->pub.clks[i] = ERR_PTR(-ENOENT); error = cpg_mssr_reserved_init(priv, info); if (error) @@ -1108,8 +1194,10 @@ static int __init cpg_mssr_common_init(struct device *dev, reserve_err: cpg_mssr_reserved_exit(priv); out_err: - if (priv->base) - iounmap(priv->base); + if (priv->pub.base0) + iounmap(priv->pub.base0); + if (priv->pub.base1) + iounmap(priv->pub.base1); kfree(priv); return error; @@ -1174,7 +1262,8 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) goto reserve_exit; /* Reset Controller not supported for Standby Control SoCs */ - if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A || + priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) goto reserve_exit; error = cpg_mssr_reset_controller_register(priv); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index a1d6e0cbcff9..ad11ab5f0069 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -8,6 +8,8 @@ #ifndef __CLK_RENESAS_CPG_MSSR_H__ #define __CLK_RENESAS_CPG_MSSR_H__ +#include + /* * Definitions of CPG Core Clocks * @@ -27,6 +29,31 @@ struct cpg_core_clk { unsigned int div; unsigned int mult; unsigned int offset; + union { + const char * const *parent_names; + const struct clk_div_table *dtable; + }; + u32 conf; + u16 flag; + u8 mux_flags; + u8 num_parents; +}; + +/** + * struct cpg_mssr_pub - data shared with device-specific clk registration code + * + * @base0: CPG/MSSR register block base0 address + * @base1: CPG/MSSR register block base1 address + * @notifiers: Notifier chain to save/restore clock state for system resume + * @rmw_lock: protects RMW register accesses + * @clks: pointer to clocks + */ +struct cpg_mssr_pub { + void __iomem *base0; + void __iomem *base1; + struct raw_notifier_head notifiers; + spinlock_t rmw_lock; + struct clk **clks; }; enum clk_types { @@ -89,6 +116,7 @@ enum clk_reg_layout { CLK_REG_LAYOUT_RCAR_GEN2_AND_GEN3 = 0, CLK_REG_LAYOUT_RZ_A, CLK_REG_LAYOUT_RCAR_GEN4, + CLK_REG_LAYOUT_RZ_T2H, }; /** @@ -153,8 +181,7 @@ struct cpg_mssr_info { struct clk *(*cpg_clk_register)(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base, - struct raw_notifier_head *notifiers); + struct cpg_mssr_pub *pub); }; extern const struct cpg_mssr_info r7s9210_cpg_mssr_info; @@ -181,6 +208,7 @@ extern const struct cpg_mssr_info r8a779a0_cpg_mssr_info; extern const struct cpg_mssr_info r8a779f0_cpg_mssr_info; extern const struct cpg_mssr_info r8a779g0_cpg_mssr_info; extern const struct cpg_mssr_info r8a779h0_cpg_mssr_info; +extern const struct cpg_mssr_info r9a09g077_cpg_mssr_info; void __init cpg_mssr_early_init(struct device_node *np, const struct cpg_mssr_info *info); diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index a8628f64a03b..187233302818 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -11,10 +11,13 @@ * Copyright (C) 2015 Renesas Electronics Corp. */ +#include #include +#include #include #include #include +#include #include #include #include @@ -68,6 +71,9 @@ #define MAX_VCLK_FREQ (148500000) +#define MSTOP_OFF(conf) FIELD_GET(GENMASK(31, 16), (conf)) +#define MSTOP_MASK(conf) FIELD_GET(GENMASK(15, 0), (conf)) + /** * struct clk_hw_data - clock hardware data * @hw: clock hw @@ -142,6 +148,7 @@ struct rzg2l_pll5_mux_dsi_div_param { * @num_resets: Number of Module Resets in info->resets[] * @last_dt_core_clk: ID of the last Core Clock exported to DT * @info: Pointer to platform data + * @genpd: PM domain * @mux_dsi_div_params: pll5 mux and dsi div parameters */ struct rzg2l_cpg_priv { @@ -158,6 +165,8 @@ struct rzg2l_cpg_priv { const struct rzg2l_cpg_info *info; + struct generic_pm_domain genpd; + struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params; }; @@ -1182,29 +1191,146 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core, } /** - * struct mstp_clock - MSTP gating clock - * - * @hw: handle between common and hardware-specific interfaces - * @off: register offset - * @bit: ON/MON bit - * @enabled: soft state of the clock, if it is coupled with another clock - * @priv: CPG/MSTP private data - * @sibling: pointer to the other coupled clock + * struct mstop - MSTOP specific data structure + * @usecnt: Usage counter for MSTOP settings (when zero the settings + * are applied to register) + * @conf: MSTOP configuration (register offset, setup bits) */ -struct mstp_clock { - struct clk_hw hw; - u16 off; - u8 bit; - bool enabled; - struct rzg2l_cpg_priv *priv; - struct mstp_clock *sibling; +struct mstop { + atomic_t usecnt; + u32 conf; }; -#define to_mod_clock(_hw) container_of(_hw, struct mstp_clock, hw) +/** + * struct mod_clock - Module clock + * + * @hw: handle between common and hardware-specific interfaces + * @priv: CPG/MSTP private data + * @sibling: pointer to the other coupled clock + * @mstop: MSTOP configuration + * @shared_mstop_clks: clocks sharing the MSTOP with this clock + * @off: register offset + * @bit: ON/MON bit + * @num_shared_mstop_clks: number of the clocks sharing MSTOP with this clock + * @enabled: soft state of the clock, if it is coupled with another clock + */ +struct mod_clock { + struct clk_hw hw; + struct rzg2l_cpg_priv *priv; + struct mod_clock *sibling; + struct mstop *mstop; + struct mod_clock **shared_mstop_clks; + u16 off; + u8 bit; + u8 num_shared_mstop_clks; + bool enabled; +}; + +#define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) + +#define for_each_mod_clock(mod_clock, hw, priv) \ + for (unsigned int i = 0; (priv) && i < (priv)->num_mod_clks; i++) \ + if ((priv)->clks[(priv)->num_core_clks + i] == ERR_PTR(-ENOENT)) \ + continue; \ + else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + i])) && \ + ((mod_clock) = to_mod_clock(hw))) + +/* Need to be called with a lock held to avoid concurrent access to mstop->usecnt. */ +static void rzg2l_mod_clock_module_set_state(struct mod_clock *clock, + bool standby) +{ + struct rzg2l_cpg_priv *priv = clock->priv; + struct mstop *mstop = clock->mstop; + bool update = false; + u32 value; + + if (!mstop) + return; + + value = MSTOP_MASK(mstop->conf) << 16; + + if (standby) { + unsigned int criticals = 0; + + for (unsigned int i = 0; i < clock->num_shared_mstop_clks; i++) { + struct mod_clock *clk = clock->shared_mstop_clks[i]; + + if (clk_hw_get_flags(&clk->hw) & CLK_IS_CRITICAL) + criticals++; + } + + if (!clock->num_shared_mstop_clks && + clk_hw_get_flags(&clock->hw) & CLK_IS_CRITICAL) + criticals++; + + /* + * If this is a shared MSTOP and it is shared with critical clocks, + * and the system boots up with this clock enabled but no driver + * uses it the CCF will disable it (as it is unused). As we don't + * increment reference counter for it at registration (to avoid + * messing with clocks enabled at probe but later used by drivers) + * do not set the MSTOP here too if it is shared with critical + * clocks and ref counted only by those critical clocks. + */ + if (criticals && criticals == atomic_read(&mstop->usecnt)) + return; + + value |= MSTOP_MASK(mstop->conf); + + /* Allow updates on probe when usecnt = 0. */ + if (!atomic_read(&mstop->usecnt)) + update = true; + else + update = atomic_dec_and_test(&mstop->usecnt); + } else { + if (!atomic_read(&mstop->usecnt)) + update = true; + atomic_inc(&mstop->usecnt); + } + + if (update) + writel(value, priv->base + MSTOP_OFF(mstop->conf)); +} + +static int rzg2l_mod_clock_mstop_show(struct seq_file *s, void *what) +{ + struct rzg2l_cpg_priv *priv = s->private; + struct mod_clock *clk; + struct clk_hw *hw; + + seq_printf(s, "%-20s %-5s %-10s\n", "", "", "MSTOP"); + seq_printf(s, "%-20s %-5s %-10s\n", "", "clk", "-------------------------"); + seq_printf(s, "%-20s %-5s %-5s %-5s %-6s %-6s\n", + "clk_name", "cnt", "cnt", "off", "val", "shared"); + seq_printf(s, "%-20s %-5s %-5s %-5s %-6s %-6s\n", + "--------", "-----", "-----", "-----", "------", "------"); + + for_each_mod_clock(clk, hw, priv) { + u32 val; + + if (!clk->mstop) + continue; + + val = readl(priv->base + MSTOP_OFF(clk->mstop->conf)) & + MSTOP_MASK(clk->mstop->conf); + + seq_printf(s, "%-20s %-5d %-5d 0x%-3lx 0x%-4x", clk_hw_get_name(hw), + __clk_get_enable_count(hw->clk), atomic_read(&clk->mstop->usecnt), + MSTOP_OFF(clk->mstop->conf), val); + + for (unsigned int i = 0; i < clk->num_shared_mstop_clks; i++) + seq_printf(s, " %pC", clk->shared_mstop_clks[i]->hw.clk); + + seq_puts(s, "\n"); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rzg2l_mod_clock_mstop); static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) { - struct mstp_clock *clock = to_mod_clock(hw); + struct mod_clock *clock = to_mod_clock(hw); struct rzg2l_cpg_priv *priv = clock->priv; unsigned int reg = clock->off; struct device *dev = priv->dev; @@ -1224,7 +1350,15 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) if (enable) value |= bitmask; - writel(value, priv->base + CLK_ON_R(reg)); + 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); + } else { + rzg2l_mod_clock_module_set_state(clock, true); + writel(value, priv->base + CLK_ON_R(reg)); + } + } if (!enable) return 0; @@ -1243,7 +1377,7 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) static int rzg2l_mod_clock_enable(struct clk_hw *hw) { - struct mstp_clock *clock = to_mod_clock(hw); + struct mod_clock *clock = to_mod_clock(hw); if (clock->sibling) { struct rzg2l_cpg_priv *priv = clock->priv; @@ -1263,7 +1397,7 @@ static int rzg2l_mod_clock_enable(struct clk_hw *hw) static void rzg2l_mod_clock_disable(struct clk_hw *hw) { - struct mstp_clock *clock = to_mod_clock(hw); + struct mod_clock *clock = to_mod_clock(hw); if (clock->sibling) { struct rzg2l_cpg_priv *priv = clock->priv; @@ -1283,7 +1417,7 @@ static void rzg2l_mod_clock_disable(struct clk_hw *hw) static int rzg2l_mod_clock_is_enabled(struct clk_hw *hw) { - struct mstp_clock *clock = to_mod_clock(hw); + struct mod_clock *clock = to_mod_clock(hw); struct rzg2l_cpg_priv *priv = clock->priv; u32 bitmask = BIT(clock->bit); u32 value; @@ -1310,21 +1444,14 @@ static const struct clk_ops rzg2l_mod_clock_ops = { .is_enabled = rzg2l_mod_clock_is_enabled, }; -static struct mstp_clock -*rzg2l_mod_clock_get_sibling(struct mstp_clock *clock, +static struct mod_clock +*rzg2l_mod_clock_get_sibling(struct mod_clock *clock, struct rzg2l_cpg_priv *priv) { + struct mod_clock *clk; struct clk_hw *hw; - unsigned int i; - for (i = 0; i < priv->num_mod_clks; i++) { - struct mstp_clock *clk; - - if (priv->clks[priv->num_core_clks + i] == ERR_PTR(-ENOENT)) - continue; - - hw = __clk_get_hw(priv->clks[priv->num_core_clks + i]); - clk = to_mod_clock(hw); + for_each_mod_clock(clk, hw, priv) { if (clock->off == clk->off && clock->bit == clk->bit) return clk; } @@ -1332,12 +1459,89 @@ static struct mstp_clock return NULL; } +static struct mstop *rzg2l_mod_clock_get_mstop(struct rzg2l_cpg_priv *priv, u32 conf) +{ + struct mod_clock *clk; + struct clk_hw *hw; + + for_each_mod_clock(clk, hw, priv) { + if (!clk->mstop) + continue; + + if (clk->mstop->conf == conf) + return clk->mstop; + } + + return NULL; +} + +static void rzg2l_mod_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->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); + } + } +} + +static int rzg2l_mod_clock_update_shared_mstop_clks(struct rzg2l_cpg_priv *priv, + struct mod_clock *clock) +{ + struct mod_clock *clk; + struct clk_hw *hw; + + if (!clock->mstop) + return 0; + + for_each_mod_clock(clk, hw, priv) { + int num_shared_mstop_clks, incr = 1; + struct mod_clock **new_clks; + + if (clk->mstop != clock->mstop) + continue; + + num_shared_mstop_clks = clk->num_shared_mstop_clks; + if (!num_shared_mstop_clks) + incr++; + + new_clks = devm_krealloc(priv->dev, clk->shared_mstop_clks, + (num_shared_mstop_clks + incr) * sizeof(*new_clks), + GFP_KERNEL); + if (!new_clks) + return -ENOMEM; + + if (!num_shared_mstop_clks) + new_clks[num_shared_mstop_clks++] = clk; + new_clks[num_shared_mstop_clks++] = clock; + + for (unsigned int i = 0; i < num_shared_mstop_clks; i++) { + new_clks[i]->shared_mstop_clks = new_clks; + new_clks[i]->num_shared_mstop_clks = num_shared_mstop_clks; + } + break; + } + + return 0; +} + static void __init rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod, const struct rzg2l_cpg_info *info, struct rzg2l_cpg_priv *priv) { - struct mstp_clock *clock = NULL; + struct mod_clock *clock = NULL; struct device *dev = priv->dev; unsigned int id = mod->id; struct clk_init_data init; @@ -1383,18 +1587,29 @@ rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod, clock->priv = priv; clock->hw.init = &init; + if (mod->mstop_conf) { + struct mstop *mstop = rzg2l_mod_clock_get_mstop(priv, mod->mstop_conf); + + if (!mstop) { + mstop = devm_kzalloc(dev, sizeof(*mstop), GFP_KERNEL); + if (!mstop) { + clk = ERR_PTR(-ENOMEM); + goto fail; + } + mstop->conf = mod->mstop_conf; + atomic_set(&mstop->usecnt, 0); + } + clock->mstop = mstop; + } + ret = devm_clk_hw_register(dev, &clock->hw); if (ret) { clk = ERR_PTR(ret); goto fail; } - clk = clock->hw.clk; - dev_dbg(dev, "Module clock %pC at %lu Hz\n", clk, clk_get_rate(clk)); - priv->clks[id] = clk; - if (mod->is_coupled) { - struct mstp_clock *sibling; + struct mod_clock *sibling; clock->enabled = rzg2l_mod_clock_is_enabled(&clock->hw); sibling = rzg2l_mod_clock_get_sibling(clock, priv); @@ -1404,6 +1619,17 @@ rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod, } } + /* Keep this before priv->clks[id] is updated. */ + ret = rzg2l_mod_clock_update_shared_mstop_clks(priv, clock); + if (ret) { + clk = ERR_PTR(ret); + goto fail; + } + + clk = clock->hw.clk; + dev_dbg(dev, "Module clock %pC at %lu Hz\n", clk, clk_get_rate(clk)); + priv->clks[id] = clk; + return; fail: @@ -1540,39 +1766,14 @@ static int rzg2l_cpg_reset_controller_register(struct rzg2l_cpg_priv *priv) return devm_reset_controller_register(priv->dev, &priv->rcdev); } -/** - * struct rzg2l_cpg_pm_domains - RZ/G2L PM domains data structure - * @onecell_data: cell data - * @domains: generic PM domains - */ -struct rzg2l_cpg_pm_domains { - struct genpd_onecell_data onecell_data; - struct generic_pm_domain *domains[]; -}; - -/** - * struct rzg2l_cpg_pd - RZ/G2L power domain data structure - * @genpd: generic PM domain - * @priv: pointer to CPG private data structure - * @conf: CPG PM domain configuration info - * @id: RZ/G2L power domain ID - */ -struct rzg2l_cpg_pd { - struct generic_pm_domain genpd; - struct rzg2l_cpg_priv *priv; - struct rzg2l_cpg_pm_domain_conf conf; - u16 id; -}; - -static bool rzg2l_cpg_is_pm_clk(struct rzg2l_cpg_pd *pd, +static bool rzg2l_cpg_is_pm_clk(struct rzg2l_cpg_priv *priv, const struct of_phandle_args *clkspec) { - if (clkspec->np != pd->genpd.dev.of_node || clkspec->args_count != 2) + if (clkspec->np != priv->genpd.dev.of_node || clkspec->args_count != 2) return false; switch (clkspec->args[0]) { case CPG_MOD: { - struct rzg2l_cpg_priv *priv = pd->priv; const struct rzg2l_cpg_info *info = priv->info; unsigned int id = clkspec->args[1]; @@ -1597,7 +1798,7 @@ static bool rzg2l_cpg_is_pm_clk(struct rzg2l_cpg_pd *pd, static int rzg2l_cpg_attach_dev(struct generic_pm_domain *domain, struct device *dev) { - struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd); + struct rzg2l_cpg_priv *priv = container_of(domain, struct rzg2l_cpg_priv, genpd); struct device_node *np = dev->of_node; struct of_phandle_args clkspec; bool once = true; @@ -1606,7 +1807,7 @@ static int rzg2l_cpg_attach_dev(struct generic_pm_domain *domain, struct device int error; for (i = 0; !of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, &clkspec); i++) { - if (!rzg2l_cpg_is_pm_clk(pd, &clkspec)) { + if (!rzg2l_cpg_is_pm_clk(priv, &clkspec)) { of_node_put(clkspec.np); continue; } @@ -1651,183 +1852,31 @@ static void rzg2l_cpg_detach_dev(struct generic_pm_domain *unused, struct device } static void rzg2l_cpg_genpd_remove(void *data) -{ - struct genpd_onecell_data *celldata = data; - - for (unsigned int i = 0; i < celldata->num_domains; i++) - pm_genpd_remove(celldata->domains[i]); -} - -static void rzg2l_cpg_genpd_remove_simple(void *data) { pm_genpd_remove(data); } -static int rzg2l_cpg_power_on(struct generic_pm_domain *domain) -{ - struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd); - struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop; - struct rzg2l_cpg_priv *priv = pd->priv; - - /* Set MSTOP. */ - if (mstop.mask) - writel(mstop.mask << 16, priv->base + mstop.off); - - return 0; -} - -static int rzg2l_cpg_power_off(struct generic_pm_domain *domain) -{ - struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd); - struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop; - struct rzg2l_cpg_priv *priv = pd->priv; - - /* Set MSTOP. */ - if (mstop.mask) - writel(mstop.mask | (mstop.mask << 16), priv->base + mstop.off); - - return 0; -} - -static int __init rzg2l_cpg_pd_setup(struct rzg2l_cpg_pd *pd) -{ - bool always_on = !!(pd->genpd.flags & GENPD_FLAG_ALWAYS_ON); - struct dev_power_governor *governor; - int ret; - - if (always_on) - governor = &pm_domain_always_on_gov; - else - governor = &simple_qos_governor; - - pd->genpd.flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - pd->genpd.attach_dev = rzg2l_cpg_attach_dev; - pd->genpd.detach_dev = rzg2l_cpg_detach_dev; - pd->genpd.power_on = rzg2l_cpg_power_on; - pd->genpd.power_off = rzg2l_cpg_power_off; - - ret = pm_genpd_init(&pd->genpd, governor, !always_on); - if (ret) - return ret; - - if (always_on) - ret = rzg2l_cpg_power_on(&pd->genpd); - - return ret; -} - static int __init rzg2l_cpg_add_clk_domain(struct rzg2l_cpg_priv *priv) { struct device *dev = priv->dev; struct device_node *np = dev->of_node; - struct rzg2l_cpg_pd *pd; + struct generic_pm_domain *genpd = &priv->genpd; int ret; - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->genpd.name = np->name; - pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; - pd->priv = priv; - ret = rzg2l_cpg_pd_setup(pd); + genpd->name = np->name; + genpd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON | + GENPD_FLAG_ACTIVE_WAKEUP; + genpd->attach_dev = rzg2l_cpg_attach_dev; + genpd->detach_dev = rzg2l_cpg_detach_dev; + ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false); if (ret) return ret; - ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove_simple, &pd->genpd); + ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, genpd); if (ret) return ret; - return of_genpd_add_provider_simple(np, &pd->genpd); -} - -static struct generic_pm_domain * -rzg2l_cpg_pm_domain_xlate(const struct of_phandle_args *spec, void *data) -{ - struct generic_pm_domain *domain = ERR_PTR(-ENOENT); - struct genpd_onecell_data *genpd = data; - - if (spec->args_count != 1) - return ERR_PTR(-EINVAL); - - for (unsigned int i = 0; i < genpd->num_domains; i++) { - struct rzg2l_cpg_pd *pd = container_of(genpd->domains[i], struct rzg2l_cpg_pd, - genpd); - - if (pd->id == spec->args[0]) { - domain = &pd->genpd; - break; - } - } - - return domain; -} - -static int __init rzg2l_cpg_add_pm_domains(struct rzg2l_cpg_priv *priv) -{ - const struct rzg2l_cpg_info *info = priv->info; - struct device *dev = priv->dev; - struct device_node *np = dev->of_node; - struct rzg2l_cpg_pm_domains *domains; - struct generic_pm_domain *parent; - u32 ncells; - int ret; - - ret = of_property_read_u32(np, "#power-domain-cells", &ncells); - if (ret) - return ret; - - /* For backward compatibility. */ - if (!ncells) - return rzg2l_cpg_add_clk_domain(priv); - - domains = devm_kzalloc(dev, struct_size(domains, domains, info->num_pm_domains), - GFP_KERNEL); - if (!domains) - return -ENOMEM; - - domains->onecell_data.domains = domains->domains; - domains->onecell_data.num_domains = info->num_pm_domains; - domains->onecell_data.xlate = rzg2l_cpg_pm_domain_xlate; - - ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, &domains->onecell_data); - if (ret) - return ret; - - for (unsigned int i = 0; i < info->num_pm_domains; i++) { - struct rzg2l_cpg_pd *pd; - - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->genpd.name = info->pm_domains[i].name; - pd->genpd.flags = info->pm_domains[i].genpd_flags; - pd->conf = info->pm_domains[i].conf; - pd->id = info->pm_domains[i].id; - pd->priv = priv; - - ret = rzg2l_cpg_pd_setup(pd); - if (ret) - return ret; - - domains->domains[i] = &pd->genpd; - /* Parent should be on the very first entry of info->pm_domains[]. */ - if (!i) { - parent = &pd->genpd; - continue; - } - - ret = pm_genpd_add_subdomain(parent, &pd->genpd); - if (ret) - return ret; - } - - ret = of_genpd_add_provider_onecell(np, &domains->onecell_data); - if (ret) - return ret; - - return 0; + return of_genpd_add_provider_simple(np, genpd); } static int __init rzg2l_cpg_probe(struct platform_device *pdev) @@ -1875,6 +1924,13 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev) for (i = 0; i < info->num_mod_clks; i++) rzg2l_cpg_register_mod_clk(&info->mod_clks[i], info, priv); + /* + * Initialize MSTOP after all the clocks were registered to avoid + * invalid reference counting when multiple clocks (critical, + * non-critical) share the same MSTOP. + */ + rzg2l_mod_clock_init_mstop(priv); + error = of_clk_add_provider(np, rzg2l_cpg_clk_src_twocell_get, priv); if (error) return error; @@ -1883,7 +1939,7 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev) if (error) return error; - error = rzg2l_cpg_add_pm_domains(priv); + error = rzg2l_cpg_add_clk_domain(priv); if (error) return error; @@ -1891,9 +1947,23 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev) if (error) return error; + debugfs_create_file("mstop", 0444, NULL, priv, &rzg2l_mod_clock_mstop_fops); return 0; } +static int rzg2l_cpg_resume(struct device *dev) +{ + struct rzg2l_cpg_priv *priv = dev_get_drvdata(dev); + + rzg2l_mod_clock_init_mstop(priv); + + return 0; +} + +static const struct dev_pm_ops rzg2l_cpg_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, rzg2l_cpg_resume) +}; + static const struct of_device_id rzg2l_cpg_match[] = { #ifdef CONFIG_CLK_R9A07G043 { @@ -1932,6 +2002,7 @@ static struct platform_driver rzg2l_cpg_driver = { .driver = { .name = "rzg2l-cpg", .of_match_table = rzg2l_cpg_match, + .pm = pm_sleep_ptr(&rzg2l_cpg_pm_ops), }, }; diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index b6eece5ffa20..0a71c5ec24b6 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -82,6 +82,8 @@ #define SEL_PLL6_2 SEL_PLL_PACK(CPG_PL6_ETH_SSEL, 0, 1) #define SEL_GPU2 SEL_PLL_PACK(CPG_PL6_SSEL, 12, 1) +#define MSTOP(name, bitmask) ((CPG_##name##_MSTOP) << 16 | (bitmask)) + #define EXTAL_FREQ_IN_MEGA_HZ (24) /** @@ -201,6 +203,7 @@ enum clk_types { * @name: handle between common and hardware-specific interfaces * @id: clock index in array containing all Core and Module Clocks * @parent: id of parent clock + * @mstop_conf: MSTOP configuration * @off: register offset * @bit: ON/MON bit * @is_coupled: flag to indicate coupled clock @@ -209,26 +212,28 @@ struct rzg2l_mod_clk { const char *name; unsigned int id; unsigned int parent; + u32 mstop_conf; u16 off; u8 bit; bool is_coupled; }; -#define DEF_MOD_BASE(_name, _id, _parent, _off, _bit, _is_coupled) \ +#define DEF_MOD_BASE(_name, _id, _parent, _off, _bit, _mstop_conf, _is_coupled) \ { \ .name = _name, \ .id = MOD_CLK_BASE + (_id), \ .parent = (_parent), \ + .mstop_conf = (_mstop_conf), \ .off = (_off), \ .bit = (_bit), \ .is_coupled = (_is_coupled), \ } -#define DEF_MOD(_name, _id, _parent, _off, _bit) \ - DEF_MOD_BASE(_name, _id, _parent, _off, _bit, false) +#define DEF_MOD(_name, _id, _parent, _off, _bit, _mstop_conf) \ + DEF_MOD_BASE(_name, _id, _parent, _off, _bit, _mstop_conf, false) -#define DEF_COUPLED(_name, _id, _parent, _off, _bit) \ - DEF_MOD_BASE(_name, _id, _parent, _off, _bit, true) +#define DEF_COUPLED(_name, _id, _parent, _off, _bit, _mstop_conf) \ + DEF_MOD_BASE(_name, _id, _parent, _off, _bit, _mstop_conf, true) /** * struct rzg2l_reset - Reset definitions @@ -252,51 +257,6 @@ struct rzg2l_reset { #define DEF_RST(_id, _off, _bit) \ DEF_RST_MON(_id, _off, _bit, -1) -/** - * struct rzg2l_cpg_reg_conf - RZ/G2L register configuration data structure - * @off: register offset - * @mask: register mask - */ -struct rzg2l_cpg_reg_conf { - u16 off; - u16 mask; -}; - -#define DEF_REG_CONF(_off, _mask) ((struct rzg2l_cpg_reg_conf) { .off = (_off), .mask = (_mask) }) - -/** - * struct rzg2l_cpg_pm_domain_conf - PM domain configuration data structure - * @mstop: MSTOP register configuration - */ -struct rzg2l_cpg_pm_domain_conf { - struct rzg2l_cpg_reg_conf mstop; -}; - -/** - * struct rzg2l_cpg_pm_domain_init_data - PM domain init data - * @name: PM domain name - * @conf: PM domain configuration - * @genpd_flags: genpd flags (see GENPD_FLAG_*) - * @id: PM domain ID (similar to the ones defined in - * include/dt-bindings/clock/-cpg.h) - */ -struct rzg2l_cpg_pm_domain_init_data { - const char * const name; - struct rzg2l_cpg_pm_domain_conf conf; - u32 genpd_flags; - u16 id; -}; - -#define DEF_PD(_name, _id, _mstop_conf, _flags) \ - { \ - .name = (_name), \ - .id = (_id), \ - .conf = { \ - .mstop = (_mstop_conf), \ - }, \ - .genpd_flags = (_flags), \ - } - /** * struct rzg2l_cpg_info - SoC-specific CPG Description * @@ -315,8 +275,6 @@ struct rzg2l_cpg_pm_domain_init_data { * @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[] - * @pm_domains: PM domains init data array - * @num_pm_domains: Number of PM domains * @has_clk_mon_regs: Flag indicating whether the SoC has CLK_MON registers */ struct rzg2l_cpg_info { @@ -343,10 +301,6 @@ struct rzg2l_cpg_info { const unsigned int *crit_mod_clks; unsigned int num_crit_mod_clks; - /* Power domain. */ - const struct rzg2l_cpg_pm_domain_init_data *pm_domains; - unsigned int num_pm_domains; - bool has_clk_mon_regs; }; diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index bcc496e8cbcd..f468afbb54e2 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -77,6 +77,7 @@ * @resets: Array of resets * @num_resets: Number of Module Resets in info->resets[] * @last_dt_core_clk: ID of the last Core Clock exported to DT + * @ff_mod_status_ops: Fixed Factor Module Status Clock operations * @mstop_count: Array of mstop values * @rcdev: Reset controller entity */ @@ -92,6 +93,8 @@ struct rzv2h_cpg_priv { unsigned int num_resets; unsigned int last_dt_core_clk; + struct clk_ops *ff_mod_status_ops; + atomic_t *mstop_count; struct reset_controller_dev rcdev; @@ -101,7 +104,6 @@ struct rzv2h_cpg_priv { struct pll_clk { struct rzv2h_cpg_priv *priv; - void __iomem *base; struct clk_hw hw; struct pll pll; }; @@ -119,6 +121,7 @@ struct pll_clk { * @on_bit: ON/MON bit * @mon_index: monitor register offset * @mon_bit: monitor bit + * @ext_clk_mux_index: mux index for external clock source, or -1 if internal */ struct mod_clock { struct rzv2h_cpg_priv *priv; @@ -129,6 +132,7 @@ struct mod_clock { u8 on_bit; s8 mon_index; u8 mon_bit; + s8 ext_clk_mux_index; }; #define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) @@ -148,6 +152,22 @@ struct ddiv_clk { #define to_ddiv_clock(_div) container_of(_div, struct ddiv_clk, div) +/** + * struct rzv2h_ff_mod_status_clk - Fixed Factor Module Status Clock + * + * @priv: CPG private data + * @conf: fixed mod configuration + * @fix: fixed factor clock + */ +struct rzv2h_ff_mod_status_clk { + struct rzv2h_cpg_priv *priv; + struct fixed_mod_conf conf; + struct clk_fixed_factor fix; +}; + +#define to_rzv2h_ff_mod_status_clk(_hw) \ + container_of(_hw, struct rzv2h_ff_mod_status_clk, fix.hw) + static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); @@ -228,7 +248,6 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core, struct rzv2h_cpg_priv *priv, const struct clk_ops *ops) { - void __iomem *base = priv->base; struct device *dev = priv->dev; struct clk_init_data init; const struct clk *parent; @@ -253,7 +272,6 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core, pll_clk->hw.init = &init; pll_clk->pll = core->cfg.pll; - pll_clk->base = base; pll_clk->priv = priv; ret = devm_clk_hw_register(dev, &pll_clk->hw); @@ -381,6 +399,7 @@ rzv2h_cpg_ddiv_clk_register(const struct cpg_core_clk *core, init.ops = &rzv2h_ddiv_clk_divider_ops; init.parent_names = &parent_name; init.num_parents = 1; + init.flags = CLK_SET_RATE_PARENT; ddiv->priv = priv; ddiv->mon = cfg_ddiv.monbit; @@ -418,6 +437,65 @@ rzv2h_cpg_mux_clk_register(const struct cpg_core_clk *core, return clk_hw->clk; } +static int +rzv2h_clk_ff_mod_status_is_enabled(struct clk_hw *hw) +{ + struct rzv2h_ff_mod_status_clk *fix = to_rzv2h_ff_mod_status_clk(hw); + struct rzv2h_cpg_priv *priv = fix->priv; + u32 offset = GET_CLK_MON_OFFSET(fix->conf.mon_index); + u32 bitmask = BIT(fix->conf.mon_bit); + u32 val; + + val = readl(priv->base + offset); + return !!(val & bitmask); +} + +static struct clk * __init +rzv2h_cpg_fixed_mod_status_clk_register(const struct cpg_core_clk *core, + struct rzv2h_cpg_priv *priv) +{ + struct rzv2h_ff_mod_status_clk *clk_hw_data; + struct clk_init_data init = { }; + struct clk_fixed_factor *fix; + const struct clk *parent; + const char *parent_name; + int ret; + + WARN_DEBUG(core->parent >= priv->num_core_clks); + parent = priv->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = __clk_get_name(parent); + parent = priv->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); + if (!clk_hw_data) + return ERR_PTR(-ENOMEM); + + clk_hw_data->priv = priv; + clk_hw_data->conf = core->cfg.fixed_mod; + + init.name = core->name; + init.ops = priv->ff_mod_status_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + + fix = &clk_hw_data->fix; + fix->hw.init = &init; + fix->mult = core->mult; + fix->div = core->div; + + ret = devm_clk_hw_register(priv->dev, &clk_hw_data->fix.hw); + if (ret) + return ERR_PTR(ret); + + return clk_hw_data->fix.hw.clk; +} + static struct clk *rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, void *data) @@ -496,6 +574,20 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, else clk = clk_hw->clk; break; + case CLK_TYPE_FF_MOD_STATUS: + if (!priv->ff_mod_status_ops) { + priv->ff_mod_status_ops = + devm_kzalloc(dev, sizeof(*priv->ff_mod_status_ops), GFP_KERNEL); + if (!priv->ff_mod_status_ops) { + clk = ERR_PTR(-ENOMEM); + goto fail; + } + memcpy(priv->ff_mod_status_ops, &clk_fixed_factor_ops, + sizeof(const struct clk_ops)); + priv->ff_mod_status_ops->is_enabled = rzv2h_clk_ff_mod_status_is_enabled; + } + clk = rzv2h_cpg_fixed_mod_status_clk_register(core, priv); + break; case CLK_TYPE_PLL: clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_pll_ops); break; @@ -563,15 +655,38 @@ static void rzv2h_mod_clock_mstop_disable(struct rzv2h_cpg_priv *priv, spin_unlock_irqrestore(&priv->rmw_lock, flags); } +static int rzv2h_parent_clk_mux_to_index(struct clk_hw *hw) +{ + struct clk_hw *parent_hw; + struct clk *parent_clk; + struct clk_mux *mux; + u32 val; + + /* This will always succeed, so no need to check for IS_ERR() */ + parent_clk = clk_get_parent(hw->clk); + + parent_hw = __clk_get_hw(parent_clk); + mux = to_clk_mux(parent_hw); + + val = readl(mux->reg) >> mux->shift; + val &= mux->mask; + return clk_mux_val_to_index(parent_hw, mux->table, 0, val); +} + static int rzv2h_mod_clock_is_enabled(struct clk_hw *hw) { struct mod_clock *clock = to_mod_clock(hw); struct rzv2h_cpg_priv *priv = clock->priv; + int mon_index = clock->mon_index; u32 bitmask; u32 offset; - if (clock->mon_index >= 0) { - offset = GET_CLK_MON_OFFSET(clock->mon_index); + if (clock->ext_clk_mux_index >= 0 && + rzv2h_parent_clk_mux_to_index(hw) == clock->ext_clk_mux_index) + mon_index = -1; + + if (mon_index >= 0) { + offset = GET_CLK_MON_OFFSET(mon_index); bitmask = BIT(clock->mon_bit); if (!(readl(priv->base + offset) & bitmask)) @@ -687,6 +802,7 @@ rzv2h_cpg_register_mod_clk(const struct rzv2h_mod_clk *mod, clock->mon_index = mod->mon_index; clock->mon_bit = mod->mon_bit; clock->no_pm = mod->no_pm; + clock->ext_clk_mux_index = mod->ext_clk_mux_index; clock->priv = priv; clock->hw.init = &init; clock->mstop_data = mod->mstop_data; @@ -1004,8 +1120,8 @@ static int __init rzv2h_cpg_probe(struct platform_device *pdev) /* Adjust for CPG_BUS_m_MSTOP starting from m = 1 */ priv->mstop_count -= 16; - priv->resets = devm_kmemdup(dev, info->resets, sizeof(*info->resets) * - info->num_resets, GFP_KERNEL); + priv->resets = devm_kmemdup_array(dev, info->resets, info->num_resets, + sizeof(*info->resets), GFP_KERNEL); if (!priv->resets) return -ENOMEM; diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index 9104b1cd276c..840eed25aeda 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -9,6 +9,7 @@ #define __RENESAS_RZV2H_CPG_H__ #include +#include /** * struct pll - Structure for PLL configuration @@ -93,6 +94,24 @@ struct smuxed { .width = (_width), \ }) +/** + * struct fixed_mod_conf - Structure for fixed module configuration + * + * @mon_index: monitor index + * @mon_bit: monitor bit + */ +struct fixed_mod_conf { + u8 mon_index; + u8 mon_bit; +}; + +#define FIXED_MOD_CONF_PACK(_index, _bit) \ + ((struct fixed_mod_conf){ \ + .mon_index = (_index), \ + .mon_bit = (_bit), \ + }) + +#define CPG_SSEL0 (0x300) #define CPG_SSEL1 (0x304) #define CPG_CDDIV0 (0x400) #define CPG_CDDIV1 (0x404) @@ -113,8 +132,14 @@ struct smuxed { #define CDDIV4_DIVCTL1 DDIV_PACK(CPG_CDDIV4, 4, 1, 17) #define CDDIV4_DIVCTL2 DDIV_PACK(CPG_CDDIV4, 8, 1, 18) +#define CSDIV0_DIVCTL0 DDIV_PACK(CPG_CSDIV0, 0, 2, CSDIV_NO_MON) +#define CSDIV0_DIVCTL1 DDIV_PACK(CPG_CSDIV0, 4, 2, CSDIV_NO_MON) #define CSDIV0_DIVCTL3 DDIV_PACK_NO_RMW(CPG_CSDIV0, 12, 2, CSDIV_NO_MON) +#define SSEL0_SELCTL2 SMUX_PACK(CPG_SSEL0, 8, 1) +#define SSEL0_SELCTL3 SMUX_PACK(CPG_SSEL0, 12, 1) +#define SSEL1_SELCTL0 SMUX_PACK(CPG_SSEL1, 0, 1) +#define SSEL1_SELCTL1 SMUX_PACK(CPG_SSEL1, 4, 1) #define SSEL1_SELCTL2 SMUX_PACK(CPG_SSEL1, 8, 1) #define SSEL1_SELCTL3 SMUX_PACK(CPG_SSEL1, 12, 1) @@ -124,6 +149,8 @@ struct smuxed { FIELD_PREP_CONST(BUS_MSTOP_BITS_MASK, (mask))) #define BUS_MSTOP_NONE GENMASK(31, 0) +#define FIXED_MOD_CONF_XSPI FIXED_MOD_CONF_PACK(5, 1) + /** * Definitions of CPG Core Clocks * @@ -144,6 +171,7 @@ struct cpg_core_clk { struct ddiv ddiv; struct pll pll; struct smuxed smux; + struct fixed_mod_conf fixed_mod; } cfg; const struct clk_div_table *dtable; const char * const *parent_names; @@ -156,6 +184,7 @@ enum clk_types { /* Generic */ CLK_TYPE_IN, /* External Clock Input */ CLK_TYPE_FF, /* Fixed Factor Clock */ + CLK_TYPE_FF_MOD_STATUS, /* Fixed Factor Clock which can report the status of module clock */ CLK_TYPE_PLL, CLK_TYPE_DDIV, /* Dynamic Switching Divider */ CLK_TYPE_SMUX, /* Static Mux */ @@ -171,6 +200,9 @@ enum clk_types { DEF_TYPE(_name, _id, CLK_TYPE_IN) #define DEF_FIXED(_name, _id, _parent, _mult, _div) \ DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult) +#define DEF_FIXED_MOD_STATUS(_name, _id, _parent, _mult, _div, _gate) \ + DEF_BASE(_name, _id, CLK_TYPE_FF_MOD_STATUS, _parent, .div = _div, \ + .mult = _mult, .cfg.fixed_mod = _gate) #define DEF_DDIV(_name, _id, _parent, _ddiv_packed, _dtable) \ DEF_TYPE(_name, _id, CLK_TYPE_DDIV, \ .cfg.ddiv = _ddiv_packed, \ @@ -199,6 +231,7 @@ enum clk_types { * @on_bit: ON bit * @mon_index: monitor register index * @mon_bit: monitor bit + * @ext_clk_mux_index: mux index for external clock source, or -1 if internal */ struct rzv2h_mod_clk { const char *name; @@ -210,9 +243,11 @@ struct rzv2h_mod_clk { u8 on_bit; s8 mon_index; u8 mon_bit; + s8 ext_clk_mux_index; }; -#define DEF_MOD_BASE(_name, _mstop, _parent, _critical, _no_pm, _onindex, _onbit, _monindex, _monbit) \ +#define DEF_MOD_BASE(_name, _mstop, _parent, _critical, _no_pm, _onindex, \ + _onbit, _monindex, _monbit, _ext_clk_mux_index) \ { \ .name = (_name), \ .mstop_data = (_mstop), \ @@ -223,16 +258,22 @@ struct rzv2h_mod_clk { .on_bit = (_onbit), \ .mon_index = (_monindex), \ .mon_bit = (_monbit), \ + .ext_clk_mux_index = (_ext_clk_mux_index), \ } #define DEF_MOD(_name, _parent, _onindex, _onbit, _monindex, _monbit, _mstop) \ - DEF_MOD_BASE(_name, _mstop, _parent, false, false, _onindex, _onbit, _monindex, _monbit) + DEF_MOD_BASE(_name, _mstop, _parent, false, false, _onindex, _onbit, _monindex, _monbit, -1) #define DEF_MOD_CRITICAL(_name, _parent, _onindex, _onbit, _monindex, _monbit, _mstop) \ - DEF_MOD_BASE(_name, _mstop, _parent, true, false, _onindex, _onbit, _monindex, _monbit) + DEF_MOD_BASE(_name, _mstop, _parent, true, false, _onindex, _onbit, _monindex, _monbit, -1) #define DEF_MOD_NO_PM(_name, _parent, _onindex, _onbit, _monindex, _monbit, _mstop) \ - DEF_MOD_BASE(_name, _mstop, _parent, false, true, _onindex, _onbit, _monindex, _monbit) + DEF_MOD_BASE(_name, _mstop, _parent, false, true, _onindex, _onbit, _monindex, _monbit, -1) + +#define DEF_MOD_MUX_EXTERNAL(_name, _parent, _onindex, _onbit, _monindex, _monbit, _mstop, \ + _ext_clk_mux_index) \ + DEF_MOD_BASE(_name, _mstop, _parent, false, false, _onindex, _onbit, _monindex, _monbit, \ + _ext_clk_mux_index) /** * struct rzv2h_reset - Reset definitions diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 398a226ad34e..dcc9dcb597ae 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -16,14 +16,14 @@ * of the SoC or supplied after the SoC characterization. * * The below implementation of the CPU clock allows the rate changes of the CPU - * clock and the corresponding rate changes of the auxillary clocks of the CPU + * clock and the corresponding rate changes of the auxiliary clocks of the CPU * domain. The platform clock driver provides a clock register configuration * for each configurable rate which is then used to program the clock hardware - * registers to acheive a fast co-oridinated rate change for all the CPU domain + * registers to achieve a fast co-oridinated rate change for all the CPU domain * clocks. * * On a rate change request for the CPU clock, the rate change is propagated - * upto the PLL supplying the clock to the CPU domain clock blocks. While the + * up to the PLL supplying the clock to the CPU domain clock blocks. While the * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an * alternate clock source. If required, the alternate clock source is divided * down in order to keep the output clock rate within the previous OPP limits. diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index b3ed8e7523e5..8b1292c56863 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -174,11 +174,11 @@ static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb, /* * rockchip_mmc_clk is mostly used by mmc controllers to sample - * the intput data, which expects the fixed phase after the tuning + * the input data, which expects the fixed phase after the tuning * process. However if the clock rate is changed, the phase is stale * and may break the data sampling. So here we try to restore the phase * for that case, except that - * (1) cached_phase is invaild since we inevitably cached it when the + * (1) cached_phase is invalid since we inevitably cached it when the * clock provider be reparented from orphan to its real parent in the * first place. Otherwise we may mess up the initialization of MMC cards * since we only set the default sample phase and drive phase later on. diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index af74439a7457..c9d599c31923 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -68,7 +68,7 @@ static long rockchip_pll_round_rate(struct clk_hw *hw, const struct rockchip_pll_rate_table *rate_table = pll->rate_table; int i; - /* Assumming rate_table is in descending order */ + /* Assuming rate_table is in descending order */ for (i = 0; i < pll->rate_count; i++) { if (drate >= rate_table[i].rate) return rate_table[i].rate; diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c index d48ab9d6c064..97d279399ae8 100644 --- a/drivers/clk/rockchip/clk-rk3568.c +++ b/drivers/clk/rockchip/clk-rk3568.c @@ -79,6 +79,7 @@ static struct rockchip_pll_rate_table rk3568_pll_rates[] = { RK3036_PLL_RATE(200000000, 1, 100, 3, 4, 1, 0), RK3036_PLL_RATE(148500000, 1, 99, 4, 4, 1, 0), RK3036_PLL_RATE(135000000, 2, 45, 4, 1, 1, 0), + RK3036_PLL_RATE(132000000, 1, 66, 6, 2, 1, 0), RK3036_PLL_RATE(128000000, 1, 16, 3, 1, 1, 0), RK3036_PLL_RATE(126400000, 1, 79, 5, 3, 1, 0), RK3036_PLL_RATE(119000000, 3, 119, 4, 2, 1, 0), diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 1e9c3c0d31e3..7c5e74c7a2e2 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -532,7 +532,7 @@ struct rockchip_pll_rate_table { * * Flags: * ROCKCHIP_PLL_SYNC_RATE - check rate parameters to match against the - * rate_table parameters and ajust them if necessary. + * rate_table parameters and adjust them if necessary. * ROCKCHIP_PLL_FIXED_MODE - the pll operates in normal mode only */ struct rockchip_pll_clock { diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c index 97982662e1a6..4e1ebd8a30b1 100644 --- a/drivers/clk/samsung/clk-cpu.c +++ b/drivers/clk/samsung/clk-cpu.c @@ -243,7 +243,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { /* * In Exynos4210, ATB clock parent is also mout_core. So - * ATB clock also needs to be mantained at safe speed. + * ATB clock also needs to be maintained at safe speed. */ alt_div |= E4210_DIV0_ATB_MASK; alt_div_mask |= E4210_DIV0_ATB_MASK; diff --git a/drivers/clk/samsung/clk-exynos850.c b/drivers/clk/samsung/clk-exynos850.c index cf7e08cca78e..56f27697c76b 100644 --- a/drivers/clk/samsung/clk-exynos850.c +++ b/drivers/clk/samsung/clk-exynos850.c @@ -1360,7 +1360,7 @@ static const unsigned long cpucl1_clk_regs[] __initconst = { CLK_CON_GAT_GATE_CLK_CPUCL1_CPU, }; -/* List of parent clocks for Muxes in CMU_CPUCL0 */ +/* List of parent clocks for Muxes in CMU_CPUCL1 */ PNAME(mout_pll_cpucl1_p) = { "oscclk", "fout_cpucl1_pll" }; PNAME(mout_cpucl1_switch_user_p) = { "oscclk", "dout_cpucl1_switch" }; PNAME(mout_cpucl1_dbg_user_p) = { "oscclk", "dout_cpucl1_dbg" }; diff --git a/drivers/clk/samsung/clk-exynosautov920.c b/drivers/clk/samsung/clk-exynosautov920.c index da4afe8ac2ab..572b6ace14ac 100644 --- a/drivers/clk/samsung/clk-exynosautov920.c +++ b/drivers/clk/samsung/clk-exynosautov920.c @@ -26,6 +26,7 @@ #define CLKS_NR_MISC (CLK_DOUT_MISC_OSC_DIV2 + 1) #define CLKS_NR_HSI0 (CLK_DOUT_HSI0_PCIE_APB + 1) #define CLKS_NR_HSI1 (CLK_MOUT_HSI1_USBDRD + 1) +#define CLKS_NR_HSI2 (CLK_DOUT_HSI2_ETHERNET_PTP + 1) /* ---- CMU_TOP ------------------------------------------------------------ */ @@ -1752,6 +1753,74 @@ static const struct samsung_cmu_info hsi1_cmu_info __initconst = { .clk_name = "noc", }; +/* ---- CMU_HSI2 --------------------------------------------------------- */ + +/* Register Offset definitions for CMU_HSI2 (0x16b00000) */ +#define PLL_LOCKTIME_PLL_ETH 0x0 +#define PLL_CON3_PLL_ETH 0x10c +#define PLL_CON0_MUX_CLKCMU_HSI2_ETHERNET_USER 0x600 +#define PLL_CON0_MUX_CLKCMU_HSI2_NOC_UFS_USER 0x610 +#define PLL_CON0_MUX_CLKCMU_HSI2_UFS_EMBD_USER 0x630 +#define CLK_CON_MUX_MUX_CLK_HSI2_ETHERNET 0x1000 +#define CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET 0x1800 +#define CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET_PTP 0x1804 + +static const unsigned long hsi2_clk_regs[] __initconst = { + PLL_LOCKTIME_PLL_ETH, + PLL_CON3_PLL_ETH, + PLL_CON0_MUX_CLKCMU_HSI2_ETHERNET_USER, + PLL_CON0_MUX_CLKCMU_HSI2_NOC_UFS_USER, + PLL_CON0_MUX_CLKCMU_HSI2_UFS_EMBD_USER, + CLK_CON_MUX_MUX_CLK_HSI2_ETHERNET, + CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET, + CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET_PTP, +}; + +static const struct samsung_pll_clock hsi2_pll_clks[] __initconst = { + /* CMU_HSI2_PLL */ + PLL(pll_531x, FOUT_PLL_ETH, "fout_pll_eth", "oscclk", + PLL_LOCKTIME_PLL_ETH, PLL_CON3_PLL_ETH, NULL), +}; + +/* List of parent clocks for Muxes in CMU_HSI2 */ +PNAME(mout_clkcmu_hsi2_noc_ufs_user_p) = { "oscclk", "dout_clkcmu_hsi2_noc_ufs" }; +PNAME(mout_clkcmu_hsi2_ufs_embd_user_p) = { "oscclk", "dout_clkcmu_hsi2_ufs_embd" }; +PNAME(mout_hsi2_ethernet_p) = { "fout_pll_eth", "mout_clkcmu_hsi2_ethernet_user" }; +PNAME(mout_clkcmu_hsi2_ethernet_user_p) = { "oscclk", "dout_clkcmu_hsi2_ethernet" }; + +static const struct samsung_mux_clock hsi2_mux_clks[] __initconst = { + MUX(CLK_MOUT_HSI2_NOC_UFS_USER, "mout_clkcmu_hsi2_noc_ufs_user", + mout_clkcmu_hsi2_noc_ufs_user_p, PLL_CON0_MUX_CLKCMU_HSI2_NOC_UFS_USER, 4, 1), + MUX(CLK_MOUT_HSI2_UFS_EMBD_USER, "mout_clkcmu_hsi2_ufs_embd_user", + mout_clkcmu_hsi2_ufs_embd_user_p, PLL_CON0_MUX_CLKCMU_HSI2_UFS_EMBD_USER, 4, 1), + MUX(CLK_MOUT_HSI2_ETHERNET, "mout_hsi2_ethernet", + mout_hsi2_ethernet_p, CLK_CON_MUX_MUX_CLK_HSI2_ETHERNET, 0, 1), + MUX(CLK_MOUT_HSI2_ETHERNET_USER, "mout_clkcmu_hsi2_ethernet_user", + mout_clkcmu_hsi2_ethernet_user_p, PLL_CON0_MUX_CLKCMU_HSI2_ETHERNET_USER, 4, 1), +}; + +static const struct samsung_div_clock hsi2_div_clks[] __initconst = { + DIV(CLK_DOUT_HSI2_ETHERNET, "dout_hsi2_ethernet", + "mout_hsi2_ethernet", CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET, + 0, 4), + DIV(CLK_DOUT_HSI2_ETHERNET_PTP, "dout_hsi2_ethernet_ptp", + "mout_hsi2_ethernet", CLK_CON_DIV_DIV_CLK_HSI2_ETHERNET_PTP, + 0, 4), +}; + +static const struct samsung_cmu_info hsi2_cmu_info __initconst = { + .pll_clks = hsi2_pll_clks, + .nr_pll_clks = ARRAY_SIZE(hsi2_pll_clks), + .mux_clks = hsi2_mux_clks, + .nr_mux_clks = ARRAY_SIZE(hsi2_mux_clks), + .div_clks = hsi2_div_clks, + .nr_div_clks = ARRAY_SIZE(hsi2_div_clks), + .nr_clk_ids = CLKS_NR_HSI2, + .clk_regs = hsi2_clk_regs, + .nr_clk_regs = ARRAY_SIZE(hsi2_clk_regs), + .clk_name = "noc", +}; + static int __init exynosautov920_cmu_probe(struct platform_device *pdev) { const struct samsung_cmu_info *info; @@ -1779,6 +1848,9 @@ static const struct of_device_id exynosautov920_cmu_of_match[] = { }, { .compatible = "samsung,exynosautov920-cmu-hsi1", .data = &hsi1_cmu_info, + }, { + .compatible = "samsung,exynosautov920-cmu-hsi2", + .data = &hsi2_cmu_info, }, { } }; diff --git a/drivers/clk/samsung/clk-gs101.c b/drivers/clk/samsung/clk-gs101.c index f9c3d68d449c..70b26db9b95a 100644 --- a/drivers/clk/samsung/clk-gs101.c +++ b/drivers/clk/samsung/clk-gs101.c @@ -1154,7 +1154,7 @@ static const struct samsung_div_clock cmu_top_div_clks[] __initconst = { CLK_CON_DIV_CLKCMU_G2D_MSCL, 0, 4), DIV(CLK_DOUT_CMU_G3AA_G3AA, "dout_cmu_g3aa_g3aa", "gout_cmu_g3aa_g3aa", CLK_CON_DIV_CLKCMU_G3AA_G3AA, 0, 4), - DIV(CLK_DOUT_CMU_G3D_SWITCH, "dout_cmu_g3d_busd", "gout_cmu_g3d_busd", + DIV(CLK_DOUT_CMU_G3D_BUSD, "dout_cmu_g3d_busd", "gout_cmu_g3d_busd", CLK_CON_DIV_CLKCMU_G3D_BUSD, 0, 4), DIV(CLK_DOUT_CMU_G3D_GLB, "dout_cmu_g3d_glb", "gout_cmu_g3d_glb", CLK_CON_DIV_CLKCMU_G3D_GLB, 0, 4), @@ -2129,7 +2129,7 @@ PNAME(mout_hsi0_usbdpdbg_user_p) = { "oscclk", "dout_cmu_hsi0_usbdpdbg" }; PNAME(mout_hsi0_bus_p) = { "mout_hsi0_bus_user", "mout_hsi0_alt_user" }; -PNAME(mout_hsi0_usb20_ref_p) = { "fout_usb_pll", +PNAME(mout_hsi0_usb20_ref_p) = { "mout_pll_usb", "mout_hsi0_tcxo_user" }; PNAME(mout_hsi0_usb31drd_p) = { "fout_usb_pll", "mout_hsi0_usb31drd_user", diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index fe8abe442c51..e4faf02b631e 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -56,7 +56,7 @@ static long samsung_pll_round_rate(struct clk_hw *hw, const struct samsung_pll_rate_table *rate_table = pll->rate_table; int i; - /* Assumming rate_table is in descending order */ + /* Assuming rate_table is in descending order */ for (i = 0; i < pll->rate_count; i++) { if (drate >= rate_table[i].rate) return rate_table[i].rate; diff --git a/drivers/clk/sophgo/clk-sg2042-clkgen.c b/drivers/clk/sophgo/clk-sg2042-clkgen.c index a334963e83ce..9e61288d34f3 100644 --- a/drivers/clk/sophgo/clk-sg2042-clkgen.c +++ b/drivers/clk/sophgo/clk-sg2042-clkgen.c @@ -968,7 +968,7 @@ static int sg2042_mux_notifier_cb(struct notifier_block *nb, /* * "1" is the array index of the second parent input source of * mux. For SG2042, it's fpll for all mux clocks. - * "0" is the array index of the frist parent input source of + * "0" is the array index of the first parent input source of * mux, For SG2042, it's mpll. * FIXME, any good idea to avoid magic number? */ diff --git a/drivers/clk/sophgo/clk-sg2042-pll.c b/drivers/clk/sophgo/clk-sg2042-pll.c index 1537f4f05860..e5fb0bb7ac4f 100644 --- a/drivers/clk/sophgo/clk-sg2042-pll.c +++ b/drivers/clk/sophgo/clk-sg2042-pll.c @@ -155,7 +155,7 @@ static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value, numerator = (u64)parent_rate * ctrl_table.fbdiv; denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2; - do_div(numerator, denominator); + numerator = div64_u64(numerator, denominator); return numerator; } @@ -212,7 +212,7 @@ static int sg2042_pll_get_postdiv_1_2(unsigned long rate, tmp0 *= fbdiv; /* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */ - do_div(tmp0, rate); + tmp0 = div64_ul(tmp0, rate); /* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */ if (tmp0 <= 7) { diff --git a/drivers/clk/spacemit/Kconfig b/drivers/clk/spacemit/Kconfig index 4c4df845b3cb..3854f6ae6d0e 100644 --- a/drivers/clk/spacemit/Kconfig +++ b/drivers/clk/spacemit/Kconfig @@ -3,6 +3,7 @@ config SPACEMIT_CCU tristate "Clock support for SpacemiT SoCs" depends on ARCH_SPACEMIT || COMPILE_TEST + select AUXILIARY_BUS select MFD_SYSCON help Say Y to enable clock controller unit support for SpacemiT SoCs. diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index cdde37a05235..65e6de030717 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -5,12 +5,16 @@ */ #include +#include #include #include +#include #include #include #include #include +#include +#include #include "ccu_common.h" #include "ccu_pll.h" @@ -19,121 +23,14 @@ #include -/* APBS register offset */ -#define APBS_PLL1_SWCR1 0x100 -#define APBS_PLL1_SWCR2 0x104 -#define APBS_PLL1_SWCR3 0x108 -#define APBS_PLL2_SWCR1 0x118 -#define APBS_PLL2_SWCR2 0x11c -#define APBS_PLL2_SWCR3 0x120 -#define APBS_PLL3_SWCR1 0x124 -#define APBS_PLL3_SWCR2 0x128 -#define APBS_PLL3_SWCR3 0x12c - -/* MPMU register offset */ -#define MPMU_POSR 0x0010 -#define POSR_PLL1_LOCK BIT(27) -#define POSR_PLL2_LOCK BIT(28) -#define POSR_PLL3_LOCK BIT(29) -#define MPMU_SUCCR 0x0014 -#define MPMU_ISCCR 0x0044 -#define MPMU_WDTPCR 0x0200 -#define MPMU_RIPCCR 0x0210 -#define MPMU_ACGR 0x1024 -#define MPMU_APBCSCR 0x1050 -#define MPMU_SUCCR_1 0x10b0 - -/* APBC register offset */ -#define APBC_UART1_CLK_RST 0x00 -#define APBC_UART2_CLK_RST 0x04 -#define APBC_GPIO_CLK_RST 0x08 -#define APBC_PWM0_CLK_RST 0x0c -#define APBC_PWM1_CLK_RST 0x10 -#define APBC_PWM2_CLK_RST 0x14 -#define APBC_PWM3_CLK_RST 0x18 -#define APBC_TWSI8_CLK_RST 0x20 -#define APBC_UART3_CLK_RST 0x24 -#define APBC_RTC_CLK_RST 0x28 -#define APBC_TWSI0_CLK_RST 0x2c -#define APBC_TWSI1_CLK_RST 0x30 -#define APBC_TIMERS1_CLK_RST 0x34 -#define APBC_TWSI2_CLK_RST 0x38 -#define APBC_AIB_CLK_RST 0x3c -#define APBC_TWSI4_CLK_RST 0x40 -#define APBC_TIMERS2_CLK_RST 0x44 -#define APBC_ONEWIRE_CLK_RST 0x48 -#define APBC_TWSI5_CLK_RST 0x4c -#define APBC_DRO_CLK_RST 0x58 -#define APBC_IR_CLK_RST 0x5c -#define APBC_TWSI6_CLK_RST 0x60 -#define APBC_COUNTER_CLK_SEL 0x64 -#define APBC_TWSI7_CLK_RST 0x68 -#define APBC_TSEN_CLK_RST 0x6c -#define APBC_UART4_CLK_RST 0x70 -#define APBC_UART5_CLK_RST 0x74 -#define APBC_UART6_CLK_RST 0x78 -#define APBC_SSP3_CLK_RST 0x7c -#define APBC_SSPA0_CLK_RST 0x80 -#define APBC_SSPA1_CLK_RST 0x84 -#define APBC_IPC_AP2AUD_CLK_RST 0x90 -#define APBC_UART7_CLK_RST 0x94 -#define APBC_UART8_CLK_RST 0x98 -#define APBC_UART9_CLK_RST 0x9c -#define APBC_CAN0_CLK_RST 0xa0 -#define APBC_PWM4_CLK_RST 0xa8 -#define APBC_PWM5_CLK_RST 0xac -#define APBC_PWM6_CLK_RST 0xb0 -#define APBC_PWM7_CLK_RST 0xb4 -#define APBC_PWM8_CLK_RST 0xb8 -#define APBC_PWM9_CLK_RST 0xbc -#define APBC_PWM10_CLK_RST 0xc0 -#define APBC_PWM11_CLK_RST 0xc4 -#define APBC_PWM12_CLK_RST 0xc8 -#define APBC_PWM13_CLK_RST 0xcc -#define APBC_PWM14_CLK_RST 0xd0 -#define APBC_PWM15_CLK_RST 0xd4 -#define APBC_PWM16_CLK_RST 0xd8 -#define APBC_PWM17_CLK_RST 0xdc -#define APBC_PWM18_CLK_RST 0xe0 -#define APBC_PWM19_CLK_RST 0xe4 - -/* APMU register offset */ -#define APMU_JPG_CLK_RES_CTRL 0x020 -#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x024 -#define APMU_ISP_CLK_RES_CTRL 0x038 -#define APMU_LCD_CLK_RES_CTRL1 0x044 -#define APMU_LCD_SPI_CLK_RES_CTRL 0x048 -#define APMU_LCD_CLK_RES_CTRL2 0x04c -#define APMU_CCIC_CLK_RES_CTRL 0x050 -#define APMU_SDH0_CLK_RES_CTRL 0x054 -#define APMU_SDH1_CLK_RES_CTRL 0x058 -#define APMU_USB_CLK_RES_CTRL 0x05c -#define APMU_QSPI_CLK_RES_CTRL 0x060 -#define APMU_DMA_CLK_RES_CTRL 0x064 -#define APMU_AES_CLK_RES_CTRL 0x068 -#define APMU_VPU_CLK_RES_CTRL 0x0a4 -#define APMU_GPU_CLK_RES_CTRL 0x0cc -#define APMU_SDH2_CLK_RES_CTRL 0x0e0 -#define APMU_PMUA_MC_CTRL 0x0e8 -#define APMU_PMU_CC2_AP 0x100 -#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 -#define APMU_AUDIO_CLK_RES_CTRL 0x14c -#define APMU_HDMI_CLK_RES_CTRL 0x1b8 -#define APMU_CCI550_CLK_CTRL 0x300 -#define APMU_ACLK_CLK_CTRL 0x388 -#define APMU_CPU_C0_CLK_CTRL 0x38C -#define APMU_CPU_C1_CLK_CTRL 0x390 -#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc -#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4 -#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc -#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 -#define APMU_EMAC1_CLK_RES_CTRL 0x3ec - struct spacemit_ccu_data { + const char *reset_name; struct clk_hw **hws; size_t num; }; +static DEFINE_IDA(auxiliary_ids); + /* APBS clocks start, APBS region contains and only contains all PLL clocks */ /* @@ -170,7 +67,8 @@ CCU_FACTOR_GATE_DEFINE(pll1_d4, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(3), 4, CCU_FACTOR_GATE_DEFINE(pll1_d5, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(4), 5, 1); CCU_FACTOR_GATE_DEFINE(pll1_d6, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(5), 6, 1); CCU_FACTOR_GATE_DEFINE(pll1_d7, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(6), 7, 1); -CCU_FACTOR_GATE_DEFINE(pll1_d8, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(7), 8, 1); +CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d8, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(7), 8, 1, + CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d11_223p4, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(15), 11, 1); CCU_FACTOR_GATE_DEFINE(pll1_d13_189, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(16), 13, 1); CCU_FACTOR_GATE_DEFINE(pll1_d23_106p8, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(20), 23, 1); @@ -819,8 +717,9 @@ static struct clk_hw *k1_ccu_pll_hws[] = { }; static const struct spacemit_ccu_data k1_ccu_pll_data = { - .hws = k1_ccu_pll_hws, - .num = ARRAY_SIZE(k1_ccu_pll_hws), + /* The PLL CCU implements no resets */ + .hws = k1_ccu_pll_hws, + .num = ARRAY_SIZE(k1_ccu_pll_hws), }; static struct clk_hw *k1_ccu_mpmu_hws[] = { @@ -860,8 +759,9 @@ static struct clk_hw *k1_ccu_mpmu_hws[] = { }; static const struct spacemit_ccu_data k1_ccu_mpmu_data = { - .hws = k1_ccu_mpmu_hws, - .num = ARRAY_SIZE(k1_ccu_mpmu_hws), + .reset_name = "mpmu-reset", + .hws = k1_ccu_mpmu_hws, + .num = ARRAY_SIZE(k1_ccu_mpmu_hws), }; static struct clk_hw *k1_ccu_apbc_hws[] = { @@ -968,8 +868,9 @@ static struct clk_hw *k1_ccu_apbc_hws[] = { }; static const struct spacemit_ccu_data k1_ccu_apbc_data = { - .hws = k1_ccu_apbc_hws, - .num = ARRAY_SIZE(k1_ccu_apbc_hws), + .reset_name = "apbc-reset", + .hws = k1_ccu_apbc_hws, + .num = ARRAY_SIZE(k1_ccu_apbc_hws), }; static struct clk_hw *k1_ccu_apmu_hws[] = { @@ -1038,8 +939,21 @@ static struct clk_hw *k1_ccu_apmu_hws[] = { }; static const struct spacemit_ccu_data k1_ccu_apmu_data = { - .hws = k1_ccu_apmu_hws, - .num = ARRAY_SIZE(k1_ccu_apmu_hws), + .reset_name = "apmu-reset", + .hws = k1_ccu_apmu_hws, + .num = ARRAY_SIZE(k1_ccu_apmu_hws), +}; + +static const struct spacemit_ccu_data k1_ccu_rcpu_data = { + .reset_name = "rcpu-reset", +}; + +static const struct spacemit_ccu_data k1_ccu_rcpu2_data = { + .reset_name = "rcpu2-reset", +}; + +static const struct spacemit_ccu_data k1_ccu_apbc2_data = { + .reset_name = "apbc2-reset", }; static int spacemit_ccu_register(struct device *dev, @@ -1050,6 +964,10 @@ static int spacemit_ccu_register(struct device *dev, struct clk_hw_onecell_data *clk_data; int i, ret; + /* Nothing to do if the CCU does not implement any clocks */ + if (!data->hws) + return 0; + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, data->num), GFP_KERNEL); if (!clk_data) @@ -1090,9 +1008,74 @@ static int spacemit_ccu_register(struct device *dev, return ret; } +static void spacemit_cadev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + ida_free(&auxiliary_ids, adev->id); + kfree(to_spacemit_ccu_adev(adev)); +} + +static void spacemit_adev_unregister(void *data) +{ + struct auxiliary_device *adev = data; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static int spacemit_ccu_reset_register(struct device *dev, + struct regmap *regmap, + const char *reset_name) +{ + struct spacemit_ccu_adev *cadev; + struct auxiliary_device *adev; + int ret; + + /* Nothing to do if the CCU does not implement a reset controller */ + if (!reset_name) + return 0; + + cadev = kzalloc(sizeof(*cadev), GFP_KERNEL); + if (!cadev) + return -ENOMEM; + + cadev->regmap = regmap; + + adev = &cadev->adev; + adev->name = reset_name; + adev->dev.parent = dev; + adev->dev.release = spacemit_cadev_release; + adev->dev.of_node = dev->of_node; + ret = ida_alloc(&auxiliary_ids, GFP_KERNEL); + if (ret < 0) + goto err_free_cadev; + adev->id = ret; + + ret = auxiliary_device_init(adev); + if (ret) + goto err_free_aux_id; + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(dev, spacemit_adev_unregister, adev); + +err_free_aux_id: + ida_free(&auxiliary_ids, adev->id); +err_free_cadev: + kfree(cadev); + + return ret; +} + static int k1_ccu_probe(struct platform_device *pdev) { struct regmap *base_regmap, *lock_regmap = NULL; + const struct spacemit_ccu_data *data; struct device *dev = &pdev->dev; int ret; @@ -1121,11 +1104,16 @@ static int k1_ccu_probe(struct platform_device *pdev) "failed to get lock regmap\n"); } - ret = spacemit_ccu_register(dev, base_regmap, lock_regmap, - of_device_get_match_data(dev)); + data = of_device_get_match_data(dev); + + ret = spacemit_ccu_register(dev, base_regmap, lock_regmap, data); if (ret) return dev_err_probe(dev, ret, "failed to register clocks\n"); + ret = spacemit_ccu_reset_register(dev, base_regmap, data->reset_name); + if (ret) + return dev_err_probe(dev, ret, "failed to register resets\n"); + return 0; } @@ -1146,6 +1134,18 @@ static const struct of_device_id of_k1_ccu_match[] = { .compatible = "spacemit,k1-syscon-apmu", .data = &k1_ccu_apmu_data, }, + { + .compatible = "spacemit,k1-syscon-rcpu", + .data = &k1_ccu_rcpu_data, + }, + { + .compatible = "spacemit,k1-syscon-rcpu2", + .data = &k1_ccu_rcpu2_data, + }, + { + .compatible = "spacemit,k1-syscon-apbc2", + .data = &k1_ccu_apbc2_data, + }, { } }; MODULE_DEVICE_TABLE(of, of_k1_ccu_match); diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h index 51d19f5d6aac..54d40cd39b27 100644 --- a/drivers/clk/spacemit/ccu_mix.h +++ b/drivers/clk/spacemit/ccu_mix.h @@ -101,17 +101,22 @@ static struct ccu_mix _name = { \ } \ } -#define CCU_FACTOR_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \ - _mul) \ +#define CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \ + _mul, _flags) \ static struct ccu_mix _name = { \ .gate = CCU_GATE_INIT(_mask_gate), \ .factor = CCU_FACTOR_INIT(_div, _mul), \ .common = { \ .reg_ctrl = _reg_ctrl, \ - CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_gate_ops, 0) \ + CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_gate_ops, _flags) \ } \ } +#define CCU_FACTOR_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \ + _mul) \ + CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \ + _mul, 0) + #define CCU_MUX_GATE_DEFINE(_name, _parents, _reg_ctrl, _shift, _width, \ _mask_gate, _flags) \ static struct ccu_mix _name = { \ diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c index 4427dcfbbb97..45f540073a65 100644 --- a/drivers/clk/spacemit/ccu_pll.c +++ b/drivers/clk/spacemit/ccu_pll.c @@ -122,7 +122,7 @@ static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, WARN_ON_ONCE(!entry); - return entry ? entry->rate : -EINVAL; + return entry ? entry->rate : 0; } static long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 361d344bfaf0..fdfb26c67188 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -199,7 +199,7 @@ static struct frac_rate_tbl amba_synth_rtbl[] = { * We can program this synthesizer to make cpu run on different clock * frequencies. * Following table provides configuration values to let cpu run on 200, - * 250, 332, 400 or 500 MHz considering different possibilites of input + * 250, 332, 400 or 500 MHz considering different possibilities of input * (vco1div2) clock. * * -------------------------------------------------------------------- diff --git a/drivers/clk/sprd/gate.h b/drivers/clk/sprd/gate.h index e738dafa4fe9..775519eb1cb6 100644 --- a/drivers/clk/sprd/gate.h +++ b/drivers/clk/sprd/gate.h @@ -26,7 +26,7 @@ struct sprd_gate { * CLK_GATE_BIG_ENDIAN BIT(2) * so we define new flags from BIT(3) */ -#define SPRD_GATE_NON_AON BIT(3) /* not alway powered on, check before read */ +#define SPRD_GATE_NON_AON BIT(3) /* not always powered on, check before read */ #define SPRD_SC_GATE_CLK_HW_INIT_FN(_struct, _name, _parent, _reg, \ _sc_offset, _enable_mask, _flags, \ diff --git a/drivers/clk/sprd/ums512-clk.c b/drivers/clk/sprd/ums512-clk.c index 9384ecc6c741..f763d83de9ee 100644 --- a/drivers/clk/sprd/ums512-clk.c +++ b/drivers/clk/sprd/ums512-clk.c @@ -1550,7 +1550,7 @@ static struct sprd_clk_desc ums512_aon_gate_desc = { /* audcp apb gates */ /* Audcp apb clocks configure CLK_IGNORE_UNUSED because these clocks may be - * controlled by audcp sys at the same time. It may be cause an execption if + * controlled by audcp sys at the same time. It may cause an exception if * kernel gates these clock. */ static SPRD_SC_GATE_CLK_HW(audcp_wdg_eb, "audcp-wdg-eb", @@ -1592,7 +1592,7 @@ static const struct sprd_clk_desc ums512_audcpapb_gate_desc = { /* audcp ahb gates */ /* Audcp aphb clocks configure CLK_IGNORE_UNUSED because these clocks may be - * controlled by audcp sys at the same time. It may be cause an execption if + * controlled by audcp sys at the same time. It may cause an exception if * kernel gates these clock. */ static SPRD_SC_GATE_CLK_HW(audcp_iis0_eb, "audcp-iis0-eb", diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c index e9d8168d02b8..52833d4241c5 100644 --- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -376,7 +376,7 @@ EXPORT_SYMBOL_GPL(jh7110_reset_controller_register); /* * This clock notifier is called when the rate of PLL0 clock is to be changed. - * The cpu_root clock should save the curent parent clock and switch its parent + * The cpu_root clock should save the current parent clock and switch its parent * clock to osc before PLL0 rate will be changed. Then switch its parent clock * back after the PLL0 rate is completed. */ diff --git a/drivers/clk/stm32/Kconfig b/drivers/clk/stm32/Kconfig index dca409d52652..4d2eb993ea08 100644 --- a/drivers/clk/stm32/Kconfig +++ b/drivers/clk/stm32/Kconfig @@ -4,7 +4,7 @@ menuconfig COMMON_CLK_STM32MP bool "Clock support for common STM32MP clocks" depends on ARCH_STM32 || COMPILE_TEST - default y + default ARCH_STM32 select RESET_CONTROLLER help Support for STM32MP SoC family clocks. @@ -14,21 +14,21 @@ if COMMON_CLK_STM32MP config COMMON_CLK_STM32MP135 bool "Clock driver for stm32mp13x clocks" depends on ARM || COMPILE_TEST - default y + default ARCH_STM32 help Support for stm32mp13x SoC family clocks. config COMMON_CLK_STM32MP157 bool "Clock driver for stm32mp15x clocks" depends on ARM || COMPILE_TEST - default y + default ARCH_STM32 help Support for stm32mp15x SoC family clocks. config COMMON_CLK_STM32MP257 bool "Clock driver for stm32mp25x clocks" depends on ARM64 || COMPILE_TEST - default y + default ARCH_STM32 help Support for stm32mp25x SoC family clocks. diff --git a/drivers/clk/stm32/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c index 5fcc4c77c11f..b8b45ed22f98 100644 --- a/drivers/clk/stm32/clk-stm32mp1.c +++ b/drivers/clk/stm32/clk-stm32mp1.c @@ -2041,7 +2041,7 @@ static const struct clock_config stm32mp1_clock_cfg[] = { KCLK(ADFSDM_K, "adfsdm_k", sai_src, 0, G_ADFSDM, M_SAI1), KCLK(USBO_K, "usbo_k", usbo_src, 0, G_USBO, M_USBO), - /* Particulary Kernel Clocks (no mux or no gate) */ + /* Particularly Kernel Clocks (no mux or no gate) */ MGATE_MP1(DFSDM_K, "dfsdm_k", "ck_mcu", 0, G_DFSDM), MGATE_MP1(DSI_PX, "dsi_px", "pll4_q", CLK_SET_RATE_PARENT, G_DSI), MGATE_MP1(LTDC_PX, "ltdc_px", "pll4_q", CLK_SET_RATE_PARENT, G_LTDC), diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c index acb4e8b9b1ba..d24fa3449303 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c @@ -80,7 +80,7 @@ static struct ccu_div r_apb2_clk = { * in the BSP source code, although most of them are unused. The existence * of the hardware block is verified with "3.1 Memory Mapping" chapter in * "Allwinner H6 V200 User Manual V1.1"; and the parent APB buses are verified - * with "3.3.2.1 System Bus Tree" chapter inthe same document. + * with "3.3.2.1 System Bus Tree" chapter in the same document. */ static SUNXI_CCU_GATE(r_apb1_timer_clk, "r-apb1-timer", "r-apb1", 0x11c, BIT(0), 0); diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c index b5464d8083c8..70ce0ca0cb7d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c @@ -204,6 +204,7 @@ static struct ccu_reset_map sun55i_a523_r_ccu_resets[] = { [RST_BUS_R_IR_RX] = { 0x1cc, BIT(16) }, [RST_BUS_R_RTC] = { 0x20c, BIT(16) }, [RST_BUS_R_CPUCFG] = { 0x22c, BIT(16) }, + [RST_BUS_R_PPU0] = { 0x1ac, BIT(16) }, }; static const struct sunxi_ccu_desc sun55i_a523_r_ccu_desc = { diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c index 9efb9fd24b42..1a9a1cb869e2 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c @@ -385,7 +385,8 @@ static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, 0, 0, /* no P */ 24, 3, /* mux */ BIT(31), /* gate */ - 0, CCU_FEATURE_UPDATE_BIT); + CLK_IS_CRITICAL, + CCU_FEATURE_UPDATE_BIT); static const struct clk_hw *mbus_hws[] = { &mbus_clk.common.hw }; diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c index 8b729c9b3545..44565830881d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c @@ -439,7 +439,7 @@ static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb2", 0x06c, BIT(3), 0); /* - * In datasheet here's "Reserved", however the gate exists in BSP soucre + * In datasheet here's "Reserved", however the gate exists in BSP source * code. */ static SUNXI_CCU_GATE(bus_can_clk, "bus-can", "apb2", diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index 579a81bb46df..05595ac51b76 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -347,12 +347,13 @@ static SUNXI_CCU_GATE(dram_ohci_clk, "dram-ohci", "dram", static const char * const de_parents[] = { "pll-video", "pll-periph0" }; static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, - 0x104, 0, 4, 24, 2, BIT(31), - CLK_SET_RATE_PARENT); + 0x104, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_NO_REPARENT); -static const char * const tcon_parents[] = { "pll-video" }; +static const char * const tcon_parents[] = { "pll-video", "pll-periph0" }; static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents, - 0x118, 0, 4, 24, 3, BIT(31), 0); + 0x118, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_NO_REPARENT); static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", 0x130, BIT(31), 0); @@ -362,11 +363,11 @@ static const char * const csi_mclk_parents[] = { "osc24M", "pll-video", static SUNXI_CCU_M_WITH_MUX_GATE(csi0_mclk_clk, "csi0-mclk", csi_mclk_parents, 0x130, 0, 5, 8, 3, BIT(15), 0); -static const char * const csi1_sclk_parents[] = { "pll-video", "pll-isp" }; -static SUNXI_CCU_M_WITH_MUX_GATE(csi1_sclk_clk, "csi-sclk", csi1_sclk_parents, +static const char * const csi_sclk_parents[] = { "pll-video", "pll-isp" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents, 0x134, 16, 4, 24, 3, BIT(31), 0); -static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi-mclk", csi_mclk_parents, +static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi1-mclk", csi_mclk_parents, 0x134, 0, 5, 8, 3, BIT(15), 0); static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", @@ -452,7 +453,7 @@ static struct ccu_common *sun8i_v3s_ccu_clks[] = { &tcon_clk.common, &csi_misc_clk.common, &csi0_mclk_clk.common, - &csi1_sclk_clk.common, + &csi_sclk_clk.common, &csi1_mclk_clk.common, &ve_clk.common, &ac_dig_clk.common, @@ -551,7 +552,7 @@ static struct clk_hw_onecell_data sun8i_v3s_hw_clks = { [CLK_TCON0] = &tcon_clk.common.hw, [CLK_CSI_MISC] = &csi_misc_clk.common.hw, [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, - [CLK_CSI1_SCLK] = &csi1_sclk_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, [CLK_VE] = &ve_clk.common.hw, [CLK_AC_DIG] = &ac_dig_clk.common.hw, @@ -633,7 +634,7 @@ static struct clk_hw_onecell_data sun8i_v3_hw_clks = { [CLK_TCON0] = &tcon_clk.common.hw, [CLK_CSI_MISC] = &csi_misc_clk.common.hw, [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, - [CLK_CSI1_SCLK] = &csi1_sclk_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, [CLK_VE] = &ve_clk.common.hw, [CLK_AC_DIG] = &ac_dig_clk.common.hw, @@ -754,6 +755,21 @@ static int sun8i_v3s_ccu_probe(struct platform_device *pdev) val &= ~GENMASK(19, 16); writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG); + /* + * Assign the DE and TCON clock to the video PLL. Both clocks need to + * have the same parent for the units to work together. + */ + + val = readl(reg + de_clk.common.reg); + val &= ~GENMASK(de_clk.mux.shift + de_clk.mux.width - 1, + de_clk.mux.shift); + writel(val, reg + de_clk.common.reg); + + val = readl(reg + tcon_clk.common.reg); + val &= ~GENMASK(tcon_clk.mux.shift + tcon_clk.mux.width - 1, + tcon_clk.mux.shift); + writel(val, reg + tcon_clk.common.reg); + return devm_sunxi_ccu_probe(&pdev->dev, reg, desc); } diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index 88ed89658d45..c7e00f0c29a5 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c @@ -66,7 +66,7 @@ EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, "SUNXI_CCU"); * changed. In common PLL designs, changes to the dividers take effect * almost immediately, while changes to the multipliers (implemented * as dividers in the feedback loop) take a few cycles to work into - * the feedback loop for the PLL to stablize. + * the feedback loop for the PLL to stabilize. * * Sometimes when the PLL clock rate is changed, the decrease in the * divider is too much for the decrease in the multiplier to catch up. diff --git a/drivers/clk/sunxi-ng/ccu_gate.c b/drivers/clk/sunxi-ng/ccu_gate.c index 474a9e8831f8..30673fe4e3c2 100644 --- a/drivers/clk/sunxi-ng/ccu_gate.c +++ b/drivers/clk/sunxi-ng/ccu_gate.c @@ -91,8 +91,8 @@ static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw, return rate; } -static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int ccu_gate_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct ccu_gate *cg = hw_to_ccu_gate(hw); int div = 1; @@ -101,14 +101,16 @@ static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate, div = cg->common.prediv; if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { - unsigned long best_parent = rate; + unsigned long best_parent = req->rate; if (cg->common.features & CCU_FEATURE_ALL_PREDIV) best_parent *= div; - *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); + req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); } - return *prate / div; + req->rate = req->best_parent_rate / div; + + return 0; } static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate, @@ -127,7 +129,7 @@ const struct clk_ops ccu_gate_ops = { .disable = ccu_gate_disable, .enable = ccu_gate_enable, .is_enabled = ccu_gate_is_enabled, - .round_rate = ccu_gate_round_rate, + .determine_rate = ccu_gate_determine_rate, .set_rate = ccu_gate_set_rate, .recalc_rate = ccu_gate_recalc_rate, }; diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c index 555e99de2cc6..5db748fbb5bd 100644 --- a/drivers/clk/sunxi-ng/ccu_nk.c +++ b/drivers/clk/sunxi-ng/ccu_nk.c @@ -92,26 +92,26 @@ static unsigned long ccu_nk_recalc_rate(struct clk_hw *hw, return rate; } -static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int ccu_nk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct ccu_nk *nk = hw_to_ccu_nk(hw); struct _ccu_nk _nk; if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate *= nk->fixed_post_div; + req->rate *= nk->fixed_post_div; _nk.min_n = nk->n.min ?: 1; _nk.max_n = nk->n.max ?: 1 << nk->n.width; _nk.min_k = nk->k.min ?: 1; _nk.max_k = nk->k.max ?: 1 << nk->k.width; - rate = ccu_nk_find_best(*parent_rate, rate, &_nk); + req->rate = ccu_nk_find_best(req->best_parent_rate, req->rate, &_nk); if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate = rate / nk->fixed_post_div; + req->rate = req->rate / nk->fixed_post_div; - return rate; + return 0; } static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -155,7 +155,7 @@ const struct clk_ops ccu_nk_ops = { .is_enabled = ccu_nk_is_enabled, .recalc_rate = ccu_nk_recalc_rate, - .round_rate = ccu_nk_round_rate, + .determine_rate = ccu_nk_determine_rate, .set_rate = ccu_nk_set_rate, }; EXPORT_SYMBOL_NS_GPL(ccu_nk_ops, "SUNXI_CCU"); diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index 6e03b69d4028..25efb5b37607 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -127,20 +127,20 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw, return rate; } -static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int ccu_nkmp_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); struct _ccu_nkmp _nkmp; if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate *= nkmp->fixed_post_div; + req->rate *= nkmp->fixed_post_div; - if (nkmp->max_rate && rate > nkmp->max_rate) { - rate = nkmp->max_rate; + if (nkmp->max_rate && req->rate > nkmp->max_rate) { + req->rate = nkmp->max_rate; if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nkmp->fixed_post_div; - return rate; + req->rate /= nkmp->fixed_post_div; + return 0; } _nkmp.min_n = nkmp->n.min ?: 1; @@ -152,12 +152,13 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, _nkmp.min_p = 1; _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); - rate = ccu_nkmp_find_best(*parent_rate, rate, &_nkmp); + req->rate = ccu_nkmp_find_best(req->best_parent_rate, req->rate, + &_nkmp); if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate = rate / nkmp->fixed_post_div; + req->rate = req->rate / nkmp->fixed_post_div; - return rate; + return 0; } static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, @@ -227,7 +228,7 @@ const struct clk_ops ccu_nkmp_ops = { .is_enabled = ccu_nkmp_is_enabled, .recalc_rate = ccu_nkmp_recalc_rate, - .round_rate = ccu_nkmp_round_rate, + .determine_rate = ccu_nkmp_determine_rate, .set_rate = ccu_nkmp_set_rate, }; EXPORT_SYMBOL_NS_GPL(ccu_nkmp_ops, "SUNXI_CCU"); diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index a4e2243b8d6b..df01ed3b37a6 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -116,39 +116,39 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, return rate; } -static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int ccu_nm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct ccu_nm *nm = hw_to_ccu_nm(hw); struct _ccu_nm _nm; if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate *= nm->fixed_post_div; + req->rate *= nm->fixed_post_div; - if (rate < nm->min_rate) { - rate = nm->min_rate; + if (req->rate < nm->min_rate) { + req->rate = nm->min_rate; if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nm->fixed_post_div; - return rate; + req->rate /= nm->fixed_post_div; + return 0; } - if (nm->max_rate && rate > nm->max_rate) { - rate = nm->max_rate; + if (nm->max_rate && req->rate > nm->max_rate) { + req->rate = nm->max_rate; if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nm->fixed_post_div; - return rate; + req->rate /= nm->fixed_post_div; + return 0; } - if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, req->rate)) { if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nm->fixed_post_div; - return rate; + req->rate /= nm->fixed_post_div; + return 0; } - if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) { + if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, req->rate)) { if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nm->fixed_post_div; - return rate; + req->rate /= nm->fixed_post_div; + return 0; } _nm.min_n = nm->n.min ?: 1; @@ -156,12 +156,13 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; - rate = ccu_nm_find_best(&nm->common, *parent_rate, rate, &_nm); + req->rate = ccu_nm_find_best(&nm->common, req->best_parent_rate, + req->rate, &_nm); if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) - rate /= nm->fixed_post_div; + req->rate /= nm->fixed_post_div; - return rate; + return 0; } static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, @@ -233,7 +234,7 @@ const struct clk_ops ccu_nm_ops = { .is_enabled = ccu_nm_is_enabled, .recalc_rate = ccu_nm_recalc_rate, - .round_rate = ccu_nm_round_rate, + .determine_rate = ccu_nm_determine_rate, .set_rate = ccu_nm_set_rate, }; EXPORT_SYMBOL_NS_GPL(ccu_nm_ops, "SUNXI_CCU"); diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 0626650a7011..fa0cd7bb8ee6 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -51,7 +51,7 @@ static int clk_periph_determine_rate(struct clk_hw *hw, struct tegra_clk_periph *periph = to_clk_periph(hw); const struct clk_ops *div_ops = periph->div_ops; struct clk_hw *div_hw = &periph->divider.hw; - unsigned long rate; + long rate; __clk_hw_set_clk(div_hw, hw); @@ -59,7 +59,7 @@ static int clk_periph_determine_rate(struct clk_hw *hw, if (rate < 0) return rate; - req->rate = rate; + req->rate = (unsigned long)rate; return 0; } @@ -132,7 +132,7 @@ static void clk_periph_restore_context(struct clk_hw *hw) clk_periph_set_parent(hw, parent_id); } -const struct clk_ops tegra_clk_periph_ops = { +static const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, .recalc_rate = clk_periph_recalc_rate, diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index a3488aaac3f7..412902f573b5 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -255,7 +255,7 @@ /* VIC register to handle during MBIST WAR */ #define NV_PVIC_THI_SLCG_OVERRIDE_LOW 0x8c -/* APE, DISPA and VIC base addesses needed for MBIST WAR */ +/* APE, DISPA and VIC base addresses needed for MBIST WAR */ #define TEGRA210_AHUB_BASE 0x702d0000 #define TEGRA210_DISPA_BASE 0x54200000 #define TEGRA210_VIC_BASE 0x54340000 diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 5d80d8b79b8e..9ea839af14bc 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -629,7 +629,6 @@ struct tegra_clk_periph { #define TEGRA_CLK_PERIPH_MAGIC 0x18221223 -extern const struct clk_ops tegra_clk_periph_ops; struct clk *tegra_clk_register_periph(const char *name, const char * const *parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c index ebfb1d59401d..cf1bba58f641 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -42,8 +42,9 @@ struct ccu_common { }; struct ccu_mux { - struct ccu_internal mux; - struct ccu_common common; + int clkid; + u32 reg; + struct clk_mux mux; }; struct ccu_gate { @@ -75,6 +76,17 @@ struct ccu_pll { .flags = _flags, \ } +#define TH_CCU_MUX(_name, _parents, _shift, _width) \ + { \ + .mask = GENMASK(_width - 1, 0), \ + .shift = _shift, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA( \ + _name, \ + _parents, \ + &clk_mux_ops, \ + 0), \ + } + #define CCU_GATE(_clkid, _struct, _name, _parent, _reg, _gate, _flags) \ struct ccu_gate _struct = { \ .enable = _gate, \ @@ -94,13 +106,6 @@ static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw) return container_of(hw, struct ccu_common, hw); } -static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw) -{ - struct ccu_common *common = hw_to_ccu_common(hw); - - return container_of(common, struct ccu_mux, common); -} - static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); @@ -415,32 +420,20 @@ static const struct clk_parent_data c910_i0_parents[] = { }; static struct ccu_mux c910_i0_clk = { - .mux = TH_CCU_ARG(1, 1), - .common = { - .clkid = CLK_C910_I0, - .cfg0 = 0x100, - .hw.init = CLK_HW_INIT_PARENTS_DATA("c910-i0", - c910_i0_parents, - &clk_mux_ops, - 0), - } + .clkid = CLK_C910_I0, + .reg = 0x100, + .mux = TH_CCU_MUX("c910-i0", c910_i0_parents, 1, 1), }; static const struct clk_parent_data c910_parents[] = { - { .hw = &c910_i0_clk.common.hw }, + { .hw = &c910_i0_clk.mux.hw }, { .hw = &cpu_pll1_clk.common.hw } }; static struct ccu_mux c910_clk = { - .mux = TH_CCU_ARG(0, 1), - .common = { - .clkid = CLK_C910, - .cfg0 = 0x100, - .hw.init = CLK_HW_INIT_PARENTS_DATA("c910", - c910_parents, - &clk_mux_ops, - 0), - } + .clkid = CLK_C910, + .reg = 0x100, + .mux = TH_CCU_MUX("c910", c910_parents, 0, 1), }; static const struct clk_parent_data ahb2_cpusys_parents[] = { @@ -582,7 +575,14 @@ static const struct clk_parent_data peri2sys_apb_pclk_pd[] = { { .hw = &peri2sys_apb_pclk.common.hw } }; -static CLK_FIXED_FACTOR_FW_NAME(osc12m_clk, "osc_12m", "osc_24m", 2, 1, 0); +static struct clk_fixed_factor osc12m_clk = { + .div = 2, + .mult = 1, + .hw.init = CLK_HW_INIT_PARENTS_DATA("osc_12m", + osc_24m_clk, + &clk_fixed_factor_ops, + 0), +}; static const char * const out_parents[] = { "osc_24m", "osc_12m" }; @@ -792,11 +792,12 @@ static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "aon2cpu-a2x", axi4_cpusys2_ac 0x134, BIT(8), 0); static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", axi4_cpusys2_aclk_pd, 0x134, BIT(7), 0); -static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_pd, 0x138, BIT(8), 0); +static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_pd, + 0x138, BIT(8), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", axi4_cpusys2_aclk_pd, 0x140, BIT(9), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hclk", perisys_ahb_hclk_pd, - 0x150, BIT(9), 0); + 0x150, BIT(9), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hclk", perisys_ahb_hclk_pd, 0x150, BIT(10), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hclk", perisys_ahb_hclk_pd, @@ -917,15 +918,9 @@ static const struct clk_parent_data uart_sclk_parents[] = { }; static struct ccu_mux uart_sclk = { - .mux = TH_CCU_ARG(0, 1), - .common = { - .clkid = CLK_UART_SCLK, - .cfg0 = 0x210, - .hw.init = CLK_HW_INIT_PARENTS_DATA("uart-sclk", - uart_sclk_parents, - &clk_mux_ops, - 0), - } + .clkid = CLK_UART_SCLK, + .reg = 0x210, + .mux = TH_CCU_MUX("uart-sclk", uart_sclk_parents, 0, 1), }; static struct ccu_common *th1520_pll_clks[] = { @@ -962,10 +957,10 @@ static struct ccu_common *th1520_div_clks[] = { &dpu1_clk.common, }; -static struct ccu_common *th1520_mux_clks[] = { - &c910_i0_clk.common, - &c910_clk.common, - &uart_sclk.common, +static struct ccu_mux *th1520_mux_clks[] = { + &c910_i0_clk, + &c910_clk, + &uart_sclk, }; static struct ccu_common *th1520_gate_clks[] = { @@ -1067,7 +1062,7 @@ static const struct regmap_config th1520_clk_regmap_config = { struct th1520_plat_data { struct ccu_common **th1520_pll_clks; struct ccu_common **th1520_div_clks; - struct ccu_common **th1520_mux_clks; + struct ccu_mux **th1520_mux_clks; struct ccu_common **th1520_gate_clks; int nr_clks; @@ -1154,23 +1149,15 @@ static int th1520_clk_probe(struct platform_device *pdev) } for (i = 0; i < plat_data->nr_mux_clks; i++) { - struct ccu_mux *cm = hw_to_ccu_mux(&plat_data->th1520_mux_clks[i]->hw); - const struct clk_init_data *init = cm->common.hw.init; + struct ccu_mux *cm = plat_data->th1520_mux_clks[i]; - plat_data->th1520_mux_clks[i]->map = map; - hw = devm_clk_hw_register_mux_parent_data_table(dev, - init->name, - init->parent_data, - init->num_parents, - 0, - base + cm->common.cfg0, - cm->mux.shift, - cm->mux.width, - 0, NULL, NULL); - if (IS_ERR(hw)) - return PTR_ERR(hw); + cm->mux.reg = base + cm->reg; - priv->hws[cm->common.clkid] = hw; + ret = devm_clk_hw_register(dev, &cm->mux.hw); + if (ret) + return ret; + + priv->hws[cm->clkid] = &cm->mux.hw; } for (i = 0; i < plat_data->nr_gate_clks; i++) { diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index 27e6b9cb1881..a99aaf2e7684 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -30,7 +30,7 @@ static LIST_HEAD(autoidle_clks); /* * we have some non-atomic read/write - * operations behind it, so lets + * operations behind it, so let's * take one lock for handling autoidle * of all clocks */ diff --git a/drivers/clk/ti/clk-43xx.c b/drivers/clk/ti/clk-43xx.c index f24f6eb2157a..35af3079c002 100644 --- a/drivers/clk/ti/clk-43xx.c +++ b/drivers/clk/ti/clk-43xx.c @@ -286,7 +286,7 @@ int __init am43xx_dt_clk_init(void) /* * cpsw_cpts_rft_clk has got the choice of 3 clocksources * dpll_core_m4_ck, dpll_core_m5_ck and dpll_disp_m2_ck. - * By default dpll_core_m4_ck is selected, witn this as clock + * By default dpll_core_m4_ck is selected, with this as clock * source the CPTS doesnot work properly. It gives clockcheck errors * while running PTP. * clockcheck: clock jumped backward or running slower than expected! diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 9c75dcc9a534..693a4459a01b 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -118,13 +118,10 @@ int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops) * Eventually we could standardize to using '_' for clk-*.c files to follow the * TRM naming. */ -static struct device_node *ti_find_clock_provider(struct device_node *from, - const char *name) +static struct device_node *ti_find_clock_provider(const char *name) { char *tmp __free(kfree) = NULL; struct device_node *np; - bool found = false; - const char *n; char *p; tmp = kstrdup_and_replace(name, '-', '_', GFP_KERNEL); @@ -137,25 +134,13 @@ static struct device_node *ti_find_clock_provider(struct device_node *from, *p = '\0'; /* Node named "clock" with "clock-output-names" */ - for_each_of_allnodes_from(from, np) { - if (of_property_read_string_index(np, "clock-output-names", - 0, &n)) - continue; - - if (!strncmp(n, tmp, strlen(tmp))) { - of_node_get(np); - found = true; - break; - } - } - - if (found) { - of_node_put(from); - return np; + for_each_node_with_property(np, "clock-output-names") { + if (of_property_match_string(np, "clock-output-names", tmp) == 0) + return np; } /* Fall back to using old node name base provider name */ - return of_find_node_by_name(from, tmp); + return of_find_node_by_name(NULL, tmp); } /** @@ -208,7 +193,7 @@ void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) if (num_args && clkctrl_nodes_missing) continue; - node = ti_find_clock_provider(NULL, buf); + node = ti_find_clock_provider(buf); if (num_args && compat_mode) { parent = node; child = of_get_child_by_name(parent, "clock"); diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index f684fc306ecc..d6d247ff2be5 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -84,7 +84,7 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index) } /** - * clk_mux_save_context - Save the parent selcted in the mux + * clk_mux_save_context - Save the parent selected in the mux * @hw: pointer struct clk_hw * * Save the parent mux value. diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c index d5cb372f0901..b69c3fbdfbce 100644 --- a/drivers/clk/versatile/clk-icst.c +++ b/drivers/clk/versatile/clk-icst.c @@ -194,7 +194,7 @@ static int vco_set(struct clk_icst *icst, struct icst_vco vco) pr_err("ICST error: tried to use RDW != 22\n"); break; default: - /* Regular auxilary oscillator */ + /* Regular auxiliary oscillator */ mask = VERSATILE_AUX_OSC_BITS; val = vco.v | (vco.r << 9) | (vco.s << 16); break; diff --git a/drivers/clk/visconti/pll.c b/drivers/clk/visconti/pll.c index 3f929cf8dd2f..8ca1bad61864 100644 --- a/drivers/clk/visconti/pll.c +++ b/drivers/clk/visconti/pll.c @@ -107,7 +107,7 @@ static long visconti_pll_round_rate(struct clk_hw *hw, const struct visconti_pll_rate_table *rate_table = pll->rate_table; int i; - /* Assumming rate_table is in descending order */ + /* Assuming rate_table is in descending order */ for (i = 0; i < pll->rate_count; i++) if (rate >= rate_table[i].rate) return rate_table[i].rate; diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index bbf7714480e7..0295a13a811c 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -669,7 +669,7 @@ static long clk_wzrd_ver_round_rate_all(struct clk_hw *hw, unsigned long rate, u32 m, d, o, div, f; int err; - err = clk_wzrd_get_divisors(hw, rate, *prate); + err = clk_wzrd_get_divisors_ver(hw, rate, *prate); if (err) return err; diff --git a/drivers/clk/xilinx/xlnx_vcu.c b/drivers/clk/xilinx/xlnx_vcu.c index 81501b48412e..1ded67bee06c 100644 --- a/drivers/clk/xilinx/xlnx_vcu.c +++ b/drivers/clk/xilinx/xlnx_vcu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ * @dev: Platform device * @pll_ref: pll ref clock source * @aclk: axi clock source + * @reset_gpio: vcu reset gpio * @logicore_reg_ba: logicore reg base address * @vcu_slcr_ba: vcu_slcr Register base address * @pll: handle for the VCU PLL @@ -61,6 +63,7 @@ struct xvcu_device { struct device *dev; struct clk *pll_ref; struct clk *aclk; + struct gpio_desc *reset_gpio; struct regmap *logicore_reg_ba; void __iomem *vcu_slcr_ba; struct clk_hw *pll; @@ -587,8 +590,8 @@ static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu) xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]); if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE])) xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]); - - clk_hw_unregister_fixed_factor(xvcu->pll_post); + if (!IS_ERR_OR_NULL(xvcu->pll_post)) + clk_hw_unregister_fixed_factor(xvcu->pll_post); } /** @@ -676,6 +679,24 @@ static int xvcu_probe(struct platform_device *pdev) * Bit 0 : Gasket isolation * Bit 1 : put VCU out of reset */ + xvcu->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(xvcu->reset_gpio)) { + ret = PTR_ERR(xvcu->reset_gpio); + dev_err_probe(&pdev->dev, ret, "failed to get reset gpio for vcu.\n"); + goto error_get_gpio; + } + + if (xvcu->reset_gpio) { + gpiod_set_value(xvcu->reset_gpio, 0); + /* min 2 clock cycle of vcu pll_ref, slowest freq is 33.33KHz */ + usleep_range(60, 120); + gpiod_set_value(xvcu->reset_gpio, 1); + usleep_range(60, 120); + } else { + dev_dbg(&pdev->dev, "No reset gpio info found in dts for VCU. This may result in incorrect functionality if VCU isolation is removed after initialization in designs where the VCU reset is driven by gpio.\n"); + } + regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE); ret = xvcu_register_clock_provider(xvcu); @@ -690,6 +711,7 @@ static int xvcu_probe(struct platform_device *pdev) error_clk_provider: xvcu_unregister_clock_provider(xvcu); +error_get_gpio: clk_disable_unprepare(xvcu->aclk); return ret; } @@ -711,6 +733,13 @@ static void xvcu_remove(struct platform_device *pdev) xvcu_unregister_clock_provider(xvcu); /* Add the Gasket isolation and put the VCU in reset. */ + if (xvcu->reset_gpio) { + gpiod_set_value(xvcu->reset_gpio, 0); + /* min 2 clock cycle of vcu pll_ref, slowest freq is 33.33KHz */ + usleep_range(60, 120); + gpiod_set_value(xvcu->reset_gpio, 1); + usleep_range(60, 120); + } regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0); clk_disable_unprepare(xvcu->aclk); diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 981a578043a5..80ba6a54248c 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -243,7 +243,7 @@ static u64 arch_counter_read(struct clocksource *cs) return arch_timer_read_counter(); } -static u64 arch_counter_read_cc(const struct cyclecounter *cc) +static u64 arch_counter_read_cc(struct cyclecounter *cc) { return arch_timer_read_counter(); } diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 09549451dd51..2edc13ca184e 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/clocksource/timer-orion.c b/drivers/clocksource/timer-orion.c index 49e86cb70a7a..61f1e27fc41e 100644 --- a/drivers/clocksource/timer-orion.c +++ b/drivers/clocksource/timer-orion.c @@ -43,7 +43,7 @@ static struct delay_timer orion_delay_timer = { .read_current_timer = orion_read_timer, }; -static void orion_delay_timer_init(unsigned long rate) +static void __init orion_delay_timer_init(unsigned long rate) { orion_delay_timer.freq = rate; register_current_timer_delay(&orion_delay_timer); diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 3383a7ce27ff..7e2f2b1a1c36 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -787,6 +787,7 @@ static int is_device_busy(struct comedi_device *dev) struct comedi_subdevice *s; int i; + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); if (!dev->attached) return 0; @@ -795,7 +796,16 @@ static int is_device_busy(struct comedi_device *dev) s = &dev->subdevices[i]; if (s->busy) return 1; - if (s->async && comedi_buf_is_mmapped(s)) + if (!s->async) + continue; + if (comedi_buf_is_mmapped(s)) + return 1; + /* + * There may be tasks still waiting on the subdevice's wait + * queue, although they should already be about to be removed + * from it since the subdevice has no active async command. + */ + if (wq_has_sleeper(&s->async->wait_head)) return 1; } @@ -825,15 +835,22 @@ static int do_devconfig_ioctl(struct comedi_device *dev, return -EPERM; if (!arg) { - if (is_device_busy(dev)) - return -EBUSY; - if (dev->attached) { - struct module *driver_module = dev->driver->module; + int rc = 0; - comedi_device_detach(dev); - module_put(driver_module); + if (dev->attached) { + down_write(&dev->attach_lock); + if (is_device_busy(dev)) { + rc = -EBUSY; + } else { + struct module *driver_module = + dev->driver->module; + + comedi_device_detach_locked(dev); + module_put(driver_module); + } + up_write(&dev->attach_lock); } - return 0; + return rc; } if (copy_from_user(&it, arg, sizeof(it))) @@ -1556,21 +1573,30 @@ static int do_insnlist_ioctl(struct comedi_device *dev, } for (i = 0; i < n_insns; ++i) { + unsigned int n = insns[i].n; + if (insns[i].insn & INSN_MASK_WRITE) { if (copy_from_user(data, insns[i].data, - insns[i].n * sizeof(unsigned int))) { + n * sizeof(unsigned int))) { dev_dbg(dev->class_dev, "copy_from_user failed\n"); ret = -EFAULT; goto error; } + if (n < MIN_SAMPLES) { + memset(&data[n], 0, (MIN_SAMPLES - n) * + sizeof(unsigned int)); + } + } else { + memset(data, 0, max_t(unsigned int, n, MIN_SAMPLES) * + sizeof(unsigned int)); } ret = parse_insn(dev, insns + i, data, file); if (ret < 0) goto error; if (insns[i].insn & INSN_MASK_READ) { if (copy_to_user(insns[i].data, data, - insns[i].n * sizeof(unsigned int))) { + n * sizeof(unsigned int))) { dev_dbg(dev->class_dev, "copy_to_user failed\n"); ret = -EFAULT; @@ -1589,6 +1615,16 @@ static int do_insnlist_ioctl(struct comedi_device *dev, return i; } +#define MAX_INSNS MAX_SAMPLES +static int check_insnlist_len(struct comedi_device *dev, unsigned int n_insns) +{ + if (n_insns > MAX_INSNS) { + dev_dbg(dev->class_dev, "insnlist length too large\n"); + return -EINVAL; + } + return 0; +} + /* * COMEDI_INSN ioctl * synchronous instruction @@ -1633,6 +1669,12 @@ static int do_insn_ioctl(struct comedi_device *dev, ret = -EFAULT; goto error; } + if (insn->n < MIN_SAMPLES) { + memset(&data[insn->n], 0, + (MIN_SAMPLES - insn->n) * sizeof(unsigned int)); + } + } else { + memset(data, 0, n_data * sizeof(unsigned int)); } ret = parse_insn(dev, insn, data, file); if (ret < 0) @@ -2239,6 +2281,9 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, rc = -EFAULT; break; } + rc = check_insnlist_len(dev, insnlist.n_insns); + if (rc) + break; insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL); if (!insns) { rc = -ENOMEM; @@ -3142,6 +3187,9 @@ static int compat_insnlist(struct file *file, unsigned long arg) if (copy_from_user(&insnlist32, compat_ptr(arg), sizeof(insnlist32))) return -EFAULT; + rc = check_insnlist_len(dev, insnlist32.n_insns); + if (rc) + return rc; insns = kcalloc(insnlist32.n_insns, sizeof(*insns), GFP_KERNEL); if (!insns) return -ENOMEM; diff --git a/drivers/comedi/comedi_internal.h b/drivers/comedi/comedi_internal.h index 9b3631a654c8..cf10ba016ebc 100644 --- a/drivers/comedi/comedi_internal.h +++ b/drivers/comedi/comedi_internal.h @@ -50,6 +50,7 @@ extern struct mutex comedi_drivers_list_lock; int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data); +void comedi_device_detach_locked(struct comedi_device *dev); void comedi_device_detach(struct comedi_device *dev); int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it); diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index 376130bfba8a..c9ebaadc5e82 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -158,7 +158,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev) int i; struct comedi_subdevice *s; - lockdep_assert_held(&dev->attach_lock); + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); if (dev->subdevices) { for (i = 0; i < dev->n_subdevices; i++) { @@ -196,16 +196,23 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev) comedi_clear_hw_dev(dev); } -void comedi_device_detach(struct comedi_device *dev) +void comedi_device_detach_locked(struct comedi_device *dev) { + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); comedi_device_cancel_all(dev); - down_write(&dev->attach_lock); dev->attached = false; dev->detach_count++; if (dev->driver) dev->driver->detach(dev); comedi_device_detach_cleanup(dev); +} + +void comedi_device_detach(struct comedi_device *dev) +{ + lockdep_assert_held(&dev->mutex); + down_write(&dev->attach_lock); + comedi_device_detach_locked(dev); up_write(&dev->attach_lock); } @@ -339,10 +346,10 @@ int comedi_dio_insn_config(struct comedi_device *dev, unsigned int *data, unsigned int mask) { - unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec); + unsigned int chan = CR_CHAN(insn->chanspec); - if (!mask) - mask = chan_mask; + if (!mask && chan < 32) + mask = 1U << chan; switch (data[0]) { case INSN_CONFIG_DIO_INPUT: @@ -382,7 +389,7 @@ EXPORT_SYMBOL_GPL(comedi_dio_insn_config); unsigned int comedi_dio_update_state(struct comedi_subdevice *s, unsigned int *data) { - unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1) + unsigned int chanmask = (s->n_chan < 32) ? ((1U << s->n_chan) - 1) : 0xffffffff; unsigned int mask = data[0] & chanmask; unsigned int bits = data[1]; @@ -613,6 +620,7 @@ static int insn_rw_emulate_bits(struct comedi_device *dev, unsigned int chan = CR_CHAN(insn->chanspec); unsigned int base_chan = (chan < 32) ? 0 : chan; unsigned int _data[2]; + unsigned int i; int ret; memset(_data, 0, sizeof(_data)); @@ -625,18 +633,21 @@ static int insn_rw_emulate_bits(struct comedi_device *dev, if (insn->insn == INSN_WRITE) { if (!(s->subdev_flags & SDF_WRITABLE)) return -EINVAL; - _data[0] = 1 << (chan - base_chan); /* mask */ - _data[1] = data[0] ? (1 << (chan - base_chan)) : 0; /* bits */ + _data[0] = 1U << (chan - base_chan); /* mask */ + } + for (i = 0; i < insn->n; i++) { + if (insn->insn == INSN_WRITE) + _data[1] = data[i] ? _data[0] : 0; /* bits */ + + ret = s->insn_bits(dev, s, &_insn, _data); + if (ret < 0) + return ret; + + if (insn->insn == INSN_READ) + data[i] = (_data[1] >> (chan - base_chan)) & 1; } - ret = s->insn_bits(dev, s, &_insn, _data); - if (ret < 0) - return ret; - - if (insn->insn == INSN_READ) - data[0] = (_data[1] >> (chan - base_chan)) & 1; - - return 1; + return insn->n; } static int __comedi_device_postconfig_async(struct comedi_device *dev, @@ -709,7 +720,7 @@ static int __comedi_device_postconfig(struct comedi_device *dev) if (s->type == COMEDI_SUBD_DO) { if (s->n_chan < 32) - s->io_bits = (1 << s->n_chan) - 1; + s->io_bits = (1U << s->n_chan) - 1; else s->io_bits = 0xffffffff; } diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index b00fab0b89d4..739cc4db52ac 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -177,7 +177,8 @@ static int aio_iiro_16_attach(struct comedi_device *dev, * Digital input change of state interrupts are optionally supported * using IRQ 2-7, 10-12, 14, or 15. */ - if ((1 << it->options[1]) & 0xdcfc) { + if (it->options[1] > 0 && it->options[1] < 16 && + (1 << it->options[1]) & 0xdcfc) { ret = request_irq(it->options[1], aio_iiro_16_cos, 0, dev->board_name, dev); if (ret == 0) diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 9747e6d1f6eb..7984950f0f99 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -792,7 +792,7 @@ static void waveform_detach(struct comedi_device *dev) { struct waveform_private *devpriv = dev->private; - if (devpriv) { + if (devpriv && dev->n_subdevices) { timer_delete_sync(&devpriv->ai_timer); timer_delete_sync(&devpriv->ao_timer); } diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index b8ea737ad3d1..1b638f5b5a4f 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -522,7 +522,8 @@ static int das16m1_attach(struct comedi_device *dev, devpriv->extra_iobase = dev->iobase + DAS16M1_8255_IOBASE; /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */ - if ((1 << it->options[1]) & 0xdcfc) { + if (it->options[1] >= 2 && it->options[1] <= 15 && + (1 << it->options[1]) & 0xdcfc) { ret = request_irq(it->options[1], das16m1_interrupt, 0, dev->board_name, dev); if (ret == 0) diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 68f95330de45..7660487e563c 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -567,7 +567,8 @@ static int das6402_attach(struct comedi_device *dev, das6402_reset(dev); /* IRQs 2,3,5,6,7, 10,11,15 are valid for "enhanced" mode */ - if ((1 << it->options[1]) & 0x8cec) { + if (it->options[1] > 0 && it->options[1] < 16 && + (1 << it->options[1]) & 0x8cec) { ret = request_irq(it->options[1], das6402_interrupt, 0, dev->board_name, dev); if (ret == 0) { diff --git a/drivers/comedi/drivers/pcl726.c b/drivers/comedi/drivers/pcl726.c index 0430630e6ebb..b542896fa0e4 100644 --- a/drivers/comedi/drivers/pcl726.c +++ b/drivers/comedi/drivers/pcl726.c @@ -328,7 +328,8 @@ static int pcl726_attach(struct comedi_device *dev, * Hook up the external trigger source interrupt only if the * user config option is valid and the board supports interrupts. */ - if (it->options[1] && (board->irq_mask & (1 << it->options[1]))) { + if (it->options[1] > 0 && it->options[1] < 16 && + (board->irq_mask & (1U << it->options[1]))) { ret = request_irq(it->options[1], pcl726_interrupt, 0, dev->board_name, dev); if (ret == 0) { diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index 0df639c6a595..abca61a72cf7 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -1149,7 +1149,8 @@ static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (IS_ERR(dev->pacer)) return PTR_ERR(dev->pacer); - if ((1 << it->options[1]) & board->irq_bits) { + if (it->options[1] > 0 && it->options[1] < 16 && + (1 << it->options[1]) & board->irq_bits) { ret = request_irq(it->options[1], pcl812_interrupt, 0, dev->board_name, dev); if (ret == 0) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0d46402e3094..9be0503df55a 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -28,7 +28,6 @@ config ARM_APPLE_SOC_CPUFREQ tristate "Apple Silicon SoC CPUFreq support" depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) select PM_OPP - default ARCH_APPLE help This adds the CPUFreq driver for Apple Silicon machines (e.g. Apple M1). @@ -238,7 +237,7 @@ config ARM_TEGRA20_CPUFREQ This adds the CPUFreq driver support for Tegra20/30 SOCs. config ARM_TEGRA124_CPUFREQ - bool "Tegra124 CPUFreq support" + tristate "Tegra124 CPUFreq support" depends on ARCH_TEGRA || COMPILE_TEST depends on CPUFREQ_DT default ARCH_TEGRA diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d38526b8e063..681d687b5a18 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o # Traces CFLAGS_amd-pstate-trace.o := -I$(src) +CFLAGS_powernv-cpufreq.o := -I$(src) amd_pstate-y := amd-pstate.o amd-pstate-trace.o ################################################################################## diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index f3477ab37742..b4c79fde1979 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -826,6 +826,13 @@ static void amd_pstate_init_prefcore(struct amd_cpudata *cpudata) if (!amd_pstate_prefcore) return; + /* should use amd-hfi instead */ + if (cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS) && + IS_ENABLED(CONFIG_AMD_HFI)) { + amd_pstate_prefcore = false; + return; + } + cpudata->hw_prefcore = true; /* Priorities must be initialized before ITMT support can be toggled on. */ @@ -1547,13 +1554,15 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy) pr_debug("CPU %d exiting\n", policy->cpu); } -static int amd_pstate_epp_update_limit(struct cpufreq_policy *policy) +static int amd_pstate_epp_update_limit(struct cpufreq_policy *policy, bool policy_change) { struct amd_cpudata *cpudata = policy->driver_data; union perf_cached perf; u8 epp; - if (policy->min != cpudata->min_limit_freq || policy->max != cpudata->max_limit_freq) + if (policy_change || + policy->min != cpudata->min_limit_freq || + policy->max != cpudata->max_limit_freq) amd_pstate_update_min_max_limit(policy); if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) @@ -1577,7 +1586,7 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy) cpudata->policy = policy->policy; - ret = amd_pstate_epp_update_limit(policy); + ret = amd_pstate_epp_update_limit(policy, true); if (ret) return ret; @@ -1619,13 +1628,14 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy) * min_perf value across kexec reboots. If this CPU is just resumed back without kexec, * the limits, epp and desired perf will get reset to the cached values in cpudata struct */ - ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false); + ret = amd_pstate_update_perf(policy, perf.bios_min_perf, + FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached), + FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached), + FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached), + false); if (ret) return ret; - /* invalidate to ensure it's rewritten during resume */ - cpudata->cppc_req_cached = 0; - /* set this flag to avoid setting core offline*/ cpudata->suspended = true; @@ -1651,7 +1661,7 @@ static int amd_pstate_epp_resume(struct cpufreq_policy *policy) int ret; /* enable amd pstate from suspend state*/ - ret = amd_pstate_epp_update_limit(policy); + ret = amd_pstate_epp_update_limit(policy, false); if (ret) return ret; diff --git a/drivers/cpufreq/armada-8k-cpufreq.c b/drivers/cpufreq/armada-8k-cpufreq.c index 5a3545bd0d8d..d96c1718f7f8 100644 --- a/drivers/cpufreq/armada-8k-cpufreq.c +++ b/drivers/cpufreq/armada-8k-cpufreq.c @@ -103,7 +103,7 @@ static void armada_8k_cpufreq_free_table(struct freq_table *freq_tables) { int opps_index, nb_cpus = num_possible_cpus(); - for (opps_index = 0 ; opps_index <= nb_cpus; opps_index++) { + for (opps_index = 0 ; opps_index < nb_cpus; opps_index++) { int i; /* If cpu_dev is NULL then we reached the end of the array */ @@ -132,7 +132,7 @@ static int __init armada_8k_cpufreq_init(void) int ret = 0, opps_index = 0, cpu, nb_cpus; struct freq_table *freq_tables; struct device_node *node; - static struct cpumask cpus; + static struct cpumask cpus, shared_cpus; node = of_find_matching_node_and_match(NULL, armada_8k_cpufreq_of_match, NULL); @@ -154,7 +154,6 @@ static int __init armada_8k_cpufreq_init(void) * divisions of it). */ for_each_cpu(cpu, &cpus) { - struct cpumask shared_cpus; struct device *cpu_dev; struct clk *clk; diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 7b841a086acc..5940d262374f 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -765,7 +765,7 @@ static void brcm_avs_cpufreq_remove(struct platform_device *pdev) } static const struct of_device_id brcm_avs_cpufreq_match[] = { - { .compatible = BRCM_AVS_CPU_DATA }, + { .compatible = "brcm,avs-cpu-data-mem" }, { } }; MODULE_DEVICE_TABLE(of, brcm_avs_cpufreq_match); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index b7c688a5659c..4a17162a392d 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -26,14 +26,6 @@ #include -/* - * This list contains information parsed from per CPU ACPI _CPC and _PSD - * structures: e.g. the highest and lowest supported performance, capabilities, - * desired performance, level requested etc. Depending on the share_type, not - * all CPUs will have an entry in the list. - */ -static LIST_HEAD(cpu_data_list); - static struct cpufreq_driver cppc_cpufreq_driver; #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE @@ -352,7 +344,6 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) #if defined(CONFIG_ARM64) && defined(CONFIG_ENERGY_MODEL) static DEFINE_PER_CPU(unsigned int, efficiency_class); -static void cppc_cpufreq_register_em(struct cpufreq_policy *policy); /* Create an artificial performance state every CPPC_EM_CAP_STEP capacity unit. */ #define CPPC_EM_CAP_STEP (20) @@ -488,7 +479,19 @@ static int cppc_get_cpu_cost(struct device *cpu_dev, unsigned long KHz, return 0; } -static int populate_efficiency_class(void) +static void cppc_cpufreq_register_em(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data; + struct em_data_callback em_cb = + EM_ADV_DATA_CB(cppc_get_cpu_power, cppc_get_cpu_cost); + + cpu_data = policy->driver_data; + em_dev_register_perf_domain(get_cpu_device(policy->cpu), + get_perf_level_count(policy), &em_cb, + cpu_data->shared_cpu_map, 0); +} + +static void populate_efficiency_class(void) { struct acpi_madt_generic_interrupt *gicc; DECLARE_BITMAP(used_classes, 256) = {}; @@ -503,7 +506,7 @@ static int populate_efficiency_class(void) if (bitmap_weight(used_classes, 256) <= 1) { pr_debug("Efficiency classes are all equal (=%d). " "No EM registered", class); - return -EINVAL; + return; } /* @@ -520,26 +523,11 @@ static int populate_efficiency_class(void) index++; } cppc_cpufreq_driver.register_em = cppc_cpufreq_register_em; - - return 0; -} - -static void cppc_cpufreq_register_em(struct cpufreq_policy *policy) -{ - struct cppc_cpudata *cpu_data; - struct em_data_callback em_cb = - EM_ADV_DATA_CB(cppc_get_cpu_power, cppc_get_cpu_cost); - - cpu_data = policy->driver_data; - em_dev_register_perf_domain(get_cpu_device(policy->cpu), - get_perf_level_count(policy), &em_cb, - cpu_data->shared_cpu_map, 0); } #else -static int populate_efficiency_class(void) +static void populate_efficiency_class(void) { - return 0; } #endif @@ -567,8 +555,6 @@ static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu) goto free_mask; } - list_add(&cpu_data->node, &cpu_data_list); - return cpu_data; free_mask: @@ -583,7 +569,6 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) { struct cppc_cpudata *cpu_data = policy->driver_data; - list_del(&cpu_data->node); free_cpumask_var(cpu_data->shared_cpu_map); kfree(cpu_data); policy->driver_data = NULL; @@ -925,7 +910,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = { }; static struct cpufreq_driver cppc_cpufreq_driver = { - .flags = CPUFREQ_CONST_LOOPS, + .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, .verify = cppc_verify_policy, .target = cppc_cpufreq_set_target, .get = cppc_cpufreq_get_rate, @@ -954,24 +939,10 @@ static int __init cppc_cpufreq_init(void) return ret; } -static inline void free_cpu_data(void) -{ - struct cppc_cpudata *iter, *tmp; - - list_for_each_entry_safe(iter, tmp, &cpu_data_list, node) { - free_cpumask_var(iter->shared_cpu_map); - list_del(&iter->node); - kfree(iter); - } - -} - static void __exit cppc_cpufreq_exit(void) { cpufreq_unregister_driver(&cppc_cpufreq_driver); cppc_freq_invariance_exit(); - - free_cpu_data(); } module_exit(cppc_cpufreq_exit); diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index a010da0f6337..015dd393eaba 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -143,6 +143,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "nvidia,tegra20", }, { .compatible = "nvidia,tegra30", }, + { .compatible = "nvidia,tegra114", }, { .compatible = "nvidia,tegra124", }, { .compatible = "nvidia,tegra210", }, { .compatible = "nvidia,tegra234", }, diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index e80dd982a3e2..506437489b4d 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -329,6 +329,17 @@ static struct platform_driver dt_cpufreq_platdrv = { }; module_platform_driver(dt_cpufreq_platdrv); +struct platform_device *cpufreq_dt_pdev_register(struct device *dev) +{ + struct platform_device_info cpufreq_dt_devinfo = {}; + + cpufreq_dt_devinfo.name = "cpufreq-dt"; + cpufreq_dt_devinfo.parent = dev; + + return platform_device_register_full(&cpufreq_dt_devinfo); +} +EXPORT_SYMBOL_GPL(cpufreq_dt_pdev_register); + MODULE_ALIAS("platform:cpufreq-dt"); MODULE_AUTHOR("Viresh Kumar "); MODULE_AUTHOR("Shawn Guo "); diff --git a/drivers/cpufreq/cpufreq-dt.h b/drivers/cpufreq/cpufreq-dt.h index 28c8af7ec5ef..fc1889aeb4f1 100644 --- a/drivers/cpufreq/cpufreq-dt.h +++ b/drivers/cpufreq/cpufreq-dt.h @@ -22,4 +22,6 @@ struct cpufreq_dt_platform_data { int (*resume)(struct cpufreq_policy *policy); }; +struct platform_device *cpufreq_dt_pdev_register(struct device *dev); + #endif /* __CPUFREQ_DT_H__ */ diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d7426e1d8bdd..fc7eace8b65b 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -109,6 +109,8 @@ void disable_cpufreq(void) { off = 1; } +EXPORT_SYMBOL_GPL(disable_cpufreq); + static DEFINE_MUTEX(cpufreq_governor_mutex); bool have_governor_per_policy(void) @@ -967,6 +969,7 @@ static struct attribute *cpufreq_attrs[] = { &cpuinfo_min_freq.attr, &cpuinfo_max_freq.attr, &cpuinfo_transition_latency.attr, + &scaling_cur_freq.attr, &scaling_min_freq.attr, &scaling_max_freq.attr, &affected_cpus.attr, @@ -1095,10 +1098,6 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy) return ret; } - ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); - if (ret) - return ret; - if (cpufreq_driver->bios_limit) { ret = sysfs_create_file(&policy->kobj, &bios_limit.attr); if (ret) @@ -1284,6 +1283,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) goto err_free_real_cpus; } + init_rwsem(&policy->rwsem); + freq_constraints_init(&policy->constraints); policy->nb_min.notifier_call = cpufreq_notifier_min; @@ -1306,7 +1307,6 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) } INIT_LIST_HEAD(&policy->policy_list); - init_rwsem(&policy->rwsem); spin_lock_init(&policy->transition_lock); init_waitqueue_head(&policy->transition_wait); INIT_WORK(&policy->update, handle_update); @@ -1694,14 +1694,13 @@ static void __cpufreq_offline(unsigned int cpu, struct cpufreq_policy *policy) return; } - if (has_target()) + if (has_target()) { strscpy(policy->last_governor, policy->governor->name, CPUFREQ_NAME_LEN); - else - policy->last_policy = policy->policy; - - if (has_target()) cpufreq_exit_governor(policy); + } else { + policy->last_policy = policy->policy; + } /* * Perform the ->offline() during light-weight tear-down, as @@ -1803,6 +1802,9 @@ static unsigned int cpufreq_verify_current_freq(struct cpufreq_policy *policy, b { unsigned int new_freq; + if (!cpufreq_driver->get) + return 0; + new_freq = cpufreq_driver->get(policy->cpu); if (!new_freq) return 0; @@ -1925,10 +1927,7 @@ unsigned int cpufreq_get(unsigned int cpu) guard(cpufreq_policy_read)(policy); - if (cpufreq_driver->get) - return __cpufreq_get(policy); - - return 0; + return __cpufreq_get(policy); } EXPORT_SYMBOL(cpufreq_get); @@ -2482,8 +2481,7 @@ int cpufreq_start_governor(struct cpufreq_policy *policy) pr_debug("%s: for CPU %u\n", __func__, policy->cpu); - if (cpufreq_driver->get) - cpufreq_verify_current_freq(policy, false); + cpufreq_verify_current_freq(policy, false); if (policy->governor->start) { ret = policy->governor->start(policy); @@ -2715,10 +2713,12 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, pr_debug("starting governor %s failed\n", policy->governor->name); if (old_gov) { policy->governor = old_gov; - if (cpufreq_init_governor(policy)) + if (cpufreq_init_governor(policy)) { policy->governor = NULL; - else - cpufreq_start_governor(policy); + } else if (cpufreq_start_governor(policy)) { + cpufreq_exit_governor(policy); + policy->governor = NULL; + } } return ret; @@ -2944,15 +2944,6 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) cpufreq_driver = driver_data; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - /* - * Mark support for the scheduler's frequency invariance engine for - * drivers that implement target(), target_index() or fast_switch(). - */ - if (!cpufreq_driver->setpolicy) { - static_branch_enable_cpuslocked(&cpufreq_freq_invariance); - pr_debug("supports frequency invariance"); - } - if (driver_data->setpolicy) driver_data->flags |= CPUFREQ_CONST_LOOPS; @@ -2983,6 +2974,15 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) hp_online = ret; ret = 0; + /* + * Mark support for the scheduler's frequency invariance engine for + * drivers that implement target(), target_index() or fast_switch(). + */ + if (!cpufreq_driver->setpolicy) { + static_branch_enable_cpuslocked(&cpufreq_freq_invariance); + pr_debug("supports frequency invariance"); + } + pr_debug("driver %s up and running\n", driver_data->name); goto out; diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index 2c42fee76daa..77d62152cd38 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -134,6 +134,7 @@ static struct cpufreq_governor cpufreq_gov_userspace = { .store_setspeed = cpufreq_set, .show_setspeed = show_speed, .owner = THIS_MODULE, + .flags = CPUFREQ_GOV_STRICT_TARGET, }; MODULE_AUTHOR("Dominik Brodowski , " diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 64587d318267..0d5d283a5429 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1034,8 +1034,8 @@ static bool hybrid_register_perf_domain(unsigned int cpu) if (!cpu_dev) return false; - if (em_dev_register_perf_domain(cpu_dev, HYBRID_EM_STATE_COUNT, &cb, - cpumask_of(cpu), false)) + if (em_dev_register_pd_no_update(cpu_dev, HYBRID_EM_STATE_COUNT, &cb, + cpumask_of(cpu), false)) return false; cpudata->pd_registered = true; @@ -2775,6 +2775,8 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = { X86_MATCH(INTEL_TIGERLAKE, core_funcs), X86_MATCH(INTEL_SAPPHIRERAPIDS_X, core_funcs), X86_MATCH(INTEL_EMERALDRAPIDS_X, core_funcs), + X86_MATCH(INTEL_GRANITERAPIDS_D, core_funcs), + X86_MATCH(INTEL_GRANITERAPIDS_X, core_funcs), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids); @@ -2791,6 +2793,7 @@ static const struct x86_cpu_id intel_pstate_cpu_oob_ids[] __initconst = { X86_MATCH(INTEL_GRANITERAPIDS_X, core_funcs), X86_MATCH(INTEL_ATOM_CRESTMONT, core_funcs), X86_MATCH(INTEL_ATOM_CRESTMONT_X, core_funcs), + X86_MATCH(INTEL_ATOM_DARKMONT_X, core_funcs), {} }; #endif @@ -3249,8 +3252,8 @@ static int intel_cpufreq_update_pstate(struct cpufreq_policy *policy, int max_pstate = policy->strict_target ? target_pstate : cpu->max_perf_ratio; - intel_cpufreq_hwp_update(cpu, target_pstate, max_pstate, 0, - fast_switch); + intel_cpufreq_hwp_update(cpu, target_pstate, max_pstate, + target_pstate, fast_switch); } else if (target_pstate != old_pstate) { intel_cpufreq_perf_ctl_update(cpu, target_pstate, fast_switch); } diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index a8943e2a93be..7d9a5f656de8 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -30,6 +29,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include "powernv-trace.h" + #define POWERNV_MAX_PSTATES_ORDER 8 #define POWERNV_MAX_PSTATES (1UL << (POWERNV_MAX_PSTATES_ORDER)) #define PMSR_PSAFE_ENABLE (1UL << 30) diff --git a/drivers/cpufreq/powernv-trace.h b/drivers/cpufreq/powernv-trace.h new file mode 100644 index 000000000000..8cadb7c9427b --- /dev/null +++ b/drivers/cpufreq/powernv-trace.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if !defined(_POWERNV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _POWERNV_TRACE_H + +#include +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM power + +TRACE_EVENT(powernv_throttle, + + TP_PROTO(int chip_id, const char *reason, int pmax), + + TP_ARGS(chip_id, reason, pmax), + + TP_STRUCT__entry( + __field(int, chip_id) + __string(reason, reason) + __field(int, pmax) + ), + + TP_fast_assign( + __entry->chip_id = chip_id; + __assign_str(reason); + __entry->pmax = pmax; + ), + + TP_printk("Chip %d Pmax %d %s", __entry->chip_id, + __entry->pmax, __get_str(reason)) +); + +#endif /* _POWERNV_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE powernv-trace + +#include diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 43c87d0259b6..7e1fbf9a091f 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -9,7 +9,6 @@ cpumask::CpumaskVar, device::{Core, Device}, error::code::*, - fmt, macros::vtable, module_platform_driver, of, opp, platform, prelude::*, @@ -19,8 +18,9 @@ /// Finds exact supply name from the OF node. fn find_supply_name_exact(dev: &Device, name: &str) -> Option { - let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?; - dev.property_present(&prop_name) + let prop_name = CString::try_from_fmt(fmt!("{name}-supply")).ok()?; + dev.fwnode()? + .property_present(&prop_name) .then(|| CString::try_from_fmt(fmt!("{name}")).ok()) .flatten() } @@ -220,7 +220,7 @@ fn probe( module_platform_driver! { type: CPUFreqDTDriver, name: "cpufreq-dt", - author: "Viresh Kumar ", + authors: ["Viresh Kumar "], description: "Generic CPUFreq DT driver", license: "GPL v2", } diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c index 514146d98bca..f8a76bbecef9 100644 --- a/drivers/cpufreq/tegra124-cpufreq.c +++ b/drivers/cpufreq/tegra124-cpufreq.c @@ -16,6 +16,10 @@ #include #include +#include "cpufreq-dt.h" + +static struct platform_device *tegra124_cpufreq_pdev; + struct tegra124_cpufreq_priv { struct clk *cpu_clk; struct clk *pllp_clk; @@ -55,7 +59,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev) struct device_node *np __free(device_node) = of_cpu_device_node_get(0); struct tegra124_cpufreq_priv *priv; struct device *cpu_dev; - struct platform_device_info cpufreq_dt_devinfo = {}; int ret; if (!np) @@ -95,11 +98,7 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev) if (ret) goto out_put_pllp_clk; - cpufreq_dt_devinfo.name = "cpufreq-dt"; - cpufreq_dt_devinfo.parent = &pdev->dev; - - priv->cpufreq_dt_pdev = - platform_device_register_full(&cpufreq_dt_devinfo); + priv->cpufreq_dt_pdev = cpufreq_dt_pdev_register(&pdev->dev); if (IS_ERR(priv->cpufreq_dt_pdev)) { ret = PTR_ERR(priv->cpufreq_dt_pdev); goto out_put_pllp_clk; @@ -173,6 +172,21 @@ static int __maybe_unused tegra124_cpufreq_resume(struct device *dev) return err; } +static void tegra124_cpufreq_remove(struct platform_device *pdev) +{ + struct tegra124_cpufreq_priv *priv = dev_get_drvdata(&pdev->dev); + + if (!IS_ERR(priv->cpufreq_dt_pdev)) { + platform_device_unregister(priv->cpufreq_dt_pdev); + priv->cpufreq_dt_pdev = ERR_PTR(-ENODEV); + } + + clk_put(priv->pllp_clk); + clk_put(priv->pllx_clk); + clk_put(priv->dfll_clk); + clk_put(priv->cpu_clk); +} + static const struct dev_pm_ops tegra124_cpufreq_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend, tegra124_cpufreq_resume) @@ -182,15 +196,16 @@ static struct platform_driver tegra124_cpufreq_platdrv = { .driver.name = "cpufreq-tegra124", .driver.pm = &tegra124_cpufreq_pm_ops, .probe = tegra124_cpufreq_probe, + .remove = tegra124_cpufreq_remove, }; static int __init tegra_cpufreq_init(void) { int ret; - struct platform_device *pdev; - if (!(of_machine_is_compatible("nvidia,tegra124") || - of_machine_is_compatible("nvidia,tegra210"))) + if (!(of_machine_is_compatible("nvidia,tegra114") || + of_machine_is_compatible("nvidia,tegra124") || + of_machine_is_compatible("nvidia,tegra210"))) return -ENODEV; /* @@ -201,15 +216,25 @@ static int __init tegra_cpufreq_init(void) if (ret) return ret; - pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0); - if (IS_ERR(pdev)) { + tegra124_cpufreq_pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0); + if (IS_ERR(tegra124_cpufreq_pdev)) { platform_driver_unregister(&tegra124_cpufreq_platdrv); - return PTR_ERR(pdev); + return PTR_ERR(tegra124_cpufreq_pdev); } return 0; } module_init(tegra_cpufreq_init); +static void __exit tegra_cpufreq_module_exit(void) +{ + if (!IS_ERR_OR_NULL(tegra124_cpufreq_pdev)) + platform_device_unregister(tegra124_cpufreq_pdev); + + platform_driver_unregister(&tegra124_cpufreq_platdrv); +} +module_exit(tegra_cpufreq_module_exit); + MODULE_AUTHOR("Tuomas Tynkkynen "); MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index 2041f59116ce..37c41209eaf9 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -28,7 +28,6 @@ struct psci_pd_provider { }; static LIST_HEAD(psci_pd_providers); -static bool psci_pd_allow_domain_state; static int psci_pd_power_off(struct generic_pm_domain *pd) { @@ -38,9 +37,6 @@ static int psci_pd_power_off(struct generic_pm_domain *pd) if (!state->data) return 0; - if (!psci_pd_allow_domain_state) - return -EBUSY; - /* OSI mode is enabled, set the corresponding domain state. */ pd_state = state->data; psci_set_domain_state(pd, pd->state_idx, *pd_state); @@ -126,15 +122,6 @@ static void psci_pd_remove(void) } } -static void psci_cpuidle_domain_sync_state(struct device *dev) -{ - /* - * All devices have now been attached/probed to the PM domain topology, - * hence it's fine to allow domain states to be picked. - */ - psci_pd_allow_domain_state = true; -} - static const struct of_device_id psci_of_match[] = { { .compatible = "arm,psci-1.0" }, {} @@ -195,7 +182,6 @@ static struct platform_driver psci_cpuidle_domain_driver = { .driver = { .name = "psci-cpuidle-domain", .of_match_table = psci_of_match, - .sync_state = psci_cpuidle_domain_sync_state, }, }; diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index 4e1ba35deda9..b19bc60cc627 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -45,7 +45,6 @@ struct psci_cpuidle_domain_state { static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data); static DEFINE_PER_CPU(struct psci_cpuidle_domain_state, psci_domain_state); static bool psci_cpuidle_use_syscore; -static bool psci_cpuidle_use_cpuhp; void psci_set_domain_state(struct generic_pm_domain *pd, unsigned int state_idx, u32 state) @@ -124,8 +123,12 @@ static int psci_idle_cpuhp_up(unsigned int cpu) { struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); - if (pd_dev) - pm_runtime_get_sync(pd_dev); + if (pd_dev) { + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + pm_runtime_get_sync(pd_dev); + else + dev_pm_genpd_resume(pd_dev); + } return 0; } @@ -135,7 +138,11 @@ static int psci_idle_cpuhp_down(unsigned int cpu) struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); if (pd_dev) { - pm_runtime_put_sync(pd_dev); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + pm_runtime_put_sync(pd_dev); + else + dev_pm_genpd_suspend(pd_dev); + /* Clear domain state to start fresh at next online. */ psci_clear_domain_state(); } @@ -196,9 +203,6 @@ static void psci_idle_init_cpuhp(void) { int err; - if (!psci_cpuidle_use_cpuhp) - return; - err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING, "cpuidle/psci:online", psci_idle_cpuhp_up, @@ -259,10 +263,8 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv, * s2ram and s2idle. */ drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state; - if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) drv->states[state_count - 1].enter = psci_enter_domain_idle_state; - psci_cpuidle_use_cpuhp = true; - } return 0; } @@ -339,7 +341,6 @@ static void psci_cpu_deinit_idle(int cpu) dt_idle_detach_cpu(data->dev); psci_cpuidle_use_syscore = false; - psci_cpuidle_use_cpuhp = false; } static int psci_idle_init_cpu(struct device *dev, int cpu) diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 0fe1ece9fbdc..a360bc4d20b7 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -44,7 +44,6 @@ static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data); static DEFINE_PER_CPU(struct sbi_domain_state, domain_state); static bool sbi_cpuidle_use_osi; static bool sbi_cpuidle_use_cpuhp; -static bool sbi_cpuidle_pd_allow_domain_state; static inline void sbi_set_domain_state(u32 state) { @@ -345,15 +344,6 @@ static int sbi_cpuidle_init_cpu(struct device *dev, int cpu) return ret; } -static void sbi_cpuidle_domain_sync_state(struct device *dev) -{ - /* - * All devices have now been attached/probed to the PM domain - * topology, hence it's fine to allow domain states to be picked. - */ - sbi_cpuidle_pd_allow_domain_state = true; -} - #ifdef CONFIG_DT_IDLE_GENPD static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) @@ -364,9 +354,6 @@ static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) if (!state->data) return 0; - if (!sbi_cpuidle_pd_allow_domain_state) - return -EBUSY; - /* OSI mode is enabled, set the corresponding domain state. */ pd_state = state->data; sbi_set_domain_state(*pd_state); @@ -564,7 +551,6 @@ static struct platform_driver sbi_cpuidle_driver = { .probe = sbi_cpuidle_probe, .driver = { .name = "sbi-cpuidle", - .sync_state = sbi_cpuidle_domain_sync_state, }, }; diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c index 97feb7d8fb23..558d49838990 100644 --- a/drivers/cpuidle/dt_idle_states.c +++ b/drivers/cpuidle/dt_idle_states.c @@ -98,7 +98,6 @@ static bool idle_state_valid(struct device_node *state_node, unsigned int idx, { int cpu; struct device_node *cpu_node, *curr_state_node; - bool valid = true; /* * Compare idle state phandles for index idx on all CPUs in the @@ -107,20 +106,17 @@ static bool idle_state_valid(struct device_node *state_node, unsigned int idx, * retrieved from. If a mismatch is found bail out straight * away since we certainly hit a firmware misconfiguration. */ - for (cpu = cpumask_next(cpumask_first(cpumask), cpumask); - cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) { + cpu = cpumask_first(cpumask) + 1; + for_each_cpu_from(cpu, cpumask) { cpu_node = of_cpu_device_node_get(cpu); curr_state_node = of_get_cpu_state_node(cpu_node, idx); - if (state_node != curr_state_node) - valid = false; - of_node_put(curr_state_node); of_node_put(cpu_node); - if (!valid) - break; + if (state_node != curr_state_node) + return false; } - return valid; + return true; } /** diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 52d5d26fc7c6..b2e3d0b0a116 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -97,6 +97,14 @@ static inline int which_bucket(u64 duration_ns) static DEFINE_PER_CPU(struct menu_device, menu_devices); +static void menu_update_intervals(struct menu_device *data, unsigned int interval_us) +{ + /* Update the repeating-pattern data. */ + data->intervals[data->interval_ptr++] = interval_us; + if (data->interval_ptr >= INTERVALS) + data->interval_ptr = 0; +} + static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); /* @@ -222,6 +230,14 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, if (data->needs_update) { menu_update(drv, dev); data->needs_update = 0; + } else if (!dev->last_residency_ns) { + /* + * This happens when the driver rejects the previously selected + * idle state and returns an error, so update the recent + * intervals table to prevent invalid information from being + * used going forward. + */ + menu_update_intervals(data, UINT_MAX); } /* Find the shortest expected idle interval. */ @@ -271,20 +287,15 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, return 0; } - if (tick_nohz_tick_stopped()) { - /* - * If the tick is already stopped, the cost of possible short - * idle duration misprediction is much higher, because the CPU - * may be stuck in a shallow idle state for a long time as a - * result of it. In that case say we might mispredict and use - * the known time till the closest timer event for the idle - * state selection. - */ - if (predicted_ns < TICK_NSEC) - predicted_ns = data->next_timer_ns; - } else if (latency_req > predicted_ns) { - latency_req = predicted_ns; - } + /* + * If the tick is already stopped, the cost of possible short idle + * duration misprediction is much higher, because the CPU may be stuck + * in a shallow idle state for a long time as a result of it. In that + * case, say we might mispredict and use the known time till the closest + * timer event for the idle state selection. + */ + if (tick_nohz_tick_stopped() && predicted_ns < TICK_NSEC) + predicted_ns = data->next_timer_ns; /* * Find the idle state with the lowest power while satisfying @@ -300,13 +311,15 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, if (idx == -1) idx = i; /* first enabled state */ + if (s->exit_latency_ns > latency_req) + break; + if (s->target_residency_ns > predicted_ns) { /* * Use a physical idle state, not busy polling, unless * a timer is going to trigger soon enough. */ if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) && - s->exit_latency_ns <= latency_req && s->target_residency_ns <= data->next_timer_ns) { predicted_ns = s->target_residency_ns; idx = i; @@ -338,8 +351,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, return idx; } - if (s->exit_latency_ns > latency_req) - break; idx = i; } @@ -482,10 +493,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) data->correction_factor[data->bucket] = new_factor; - /* update the repeating-pattern data */ - data->intervals[data->interval_ptr++] = ktime_to_us(measured_ns); - if (data->interval_ptr >= INTERVALS) - data->interval_ptr = 0; + menu_update_intervals(data, ktime_to_us(measured_ns)); } /** diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 9f8a3a5bed7e..04b4c43b6bae 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -188,6 +188,19 @@ config CRYPTO_PAES_S390 Select this option if you want to use the paes cipher for example to use protected key encrypted devices. +config CRYPTO_PHMAC_S390 + tristate "PHMAC cipher algorithms" + depends on S390 + depends on PKEY + select CRYPTO_HASH + select CRYPTO_ENGINE + help + This is the s390 hardware accelerated implementation of the + protected key HMAC support for SHA224, SHA256, SHA384 and SHA512. + + Select this option if you want to use the phmac digests + for example to use dm-integrity with secure/protected keys. + config S390_PRNG tristate "Pseudo random number generator device driver" depends on S390 diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c index f9cf00d690e2..5663df49dd81 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c @@ -206,15 +206,14 @@ static int sun8i_ce_cipher_prepare(struct crypto_engine *engine, void *async_req cet->t_key = desc_addr_val_le32(ce, rctx->addr_key); ivsize = crypto_skcipher_ivsize(tfm); - if (areq->iv && crypto_skcipher_ivsize(tfm) > 0) { - rctx->ivlen = ivsize; + if (areq->iv && ivsize > 0) { if (rctx->op_dir & CE_DECRYPTION) { offset = areq->cryptlen - ivsize; scatterwalk_map_and_copy(chan->backup_iv, areq->src, offset, ivsize, 0); } memcpy(chan->bounce_iv, areq->iv, ivsize); - rctx->addr_iv = dma_map_single(ce->dev, chan->bounce_iv, rctx->ivlen, + rctx->addr_iv = dma_map_single(ce->dev, chan->bounce_iv, ivsize, DMA_TO_DEVICE); if (dma_mapping_error(ce->dev, rctx->addr_iv)) { dev_err(ce->dev, "Cannot DMA MAP IV\n"); @@ -278,8 +277,8 @@ static int sun8i_ce_cipher_prepare(struct crypto_engine *engine, void *async_req } chan->timeout = areq->cryptlen; - rctx->nr_sgs = nr_sgs; - rctx->nr_sgd = nr_sgd; + rctx->nr_sgs = ns; + rctx->nr_sgd = nd; return 0; theend_sgs: @@ -296,7 +295,8 @@ static int sun8i_ce_cipher_prepare(struct crypto_engine *engine, void *async_req theend_iv: if (areq->iv && ivsize > 0) { if (!dma_mapping_error(ce->dev, rctx->addr_iv)) - dma_unmap_single(ce->dev, rctx->addr_iv, rctx->ivlen, DMA_TO_DEVICE); + dma_unmap_single(ce->dev, rctx->addr_iv, ivsize, + DMA_TO_DEVICE); offset = areq->cryptlen - ivsize; if (rctx->op_dir & CE_DECRYPTION) { @@ -345,7 +345,8 @@ static void sun8i_ce_cipher_unprepare(struct crypto_engine *engine, if (areq->iv && ivsize > 0) { if (cet->t_iv) - dma_unmap_single(ce->dev, rctx->addr_iv, rctx->ivlen, DMA_TO_DEVICE); + dma_unmap_single(ce->dev, rctx->addr_iv, ivsize, + DMA_TO_DEVICE); offset = areq->cryptlen - ivsize; if (rctx->op_dir & CE_DECRYPTION) { memcpy(areq->iv, chan->backup_iv, ivsize); diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c index bef44f350167..13bdfb8a2c62 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c @@ -342,8 +342,8 @@ int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq) algt = container_of(alg, struct sun8i_ce_alg_template, alg.hash.base); ce = algt->ce; - bs = algt->alg.hash.base.halg.base.cra_blocksize; - digestsize = algt->alg.hash.base.halg.digestsize; + bs = crypto_ahash_blocksize(tfm); + digestsize = crypto_ahash_digestsize(tfm); if (digestsize == SHA224_DIGEST_SIZE) digestsize = SHA256_DIGEST_SIZE; if (digestsize == SHA384_DIGEST_SIZE) @@ -455,7 +455,7 @@ int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq) err_unmap_result: dma_unmap_single(ce->dev, addr_res, digestsize, DMA_FROM_DEVICE); if (!err) - memcpy(areq->result, result, algt->alg.hash.base.halg.digestsize); + memcpy(areq->result, result, crypto_ahash_digestsize(tfm)); err_unmap_src: dma_unmap_sg(ce->dev, areq->src, ns, DMA_TO_DEVICE); diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h index 83df4d719053..0f9a89067016 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h @@ -260,7 +260,6 @@ static inline __le32 desc_addr_val_le32(struct sun8i_ce_dev *dev, * struct sun8i_cipher_req_ctx - context for a skcipher request * @op_dir: direction (encrypt vs decrypt) for this request * @flow: the flow to use for this request - * @ivlen: size of bounce_iv * @nr_sgs: The number of source SG (as given by dma_map_sg()) * @nr_sgd: The number of destination SG (as given by dma_map_sg()) * @addr_iv: The IV addr returned by dma_map_single, need to unmap later @@ -270,7 +269,6 @@ static inline __le32 desc_addr_val_le32(struct sun8i_ce_dev *dev, struct sun8i_cipher_req_ctx { u32 op_dir; int flow; - unsigned int ivlen; int nr_sgs; int nr_sgd; dma_addr_t addr_iv; diff --git a/drivers/crypto/aspeed/aspeed-hace-hash.c b/drivers/crypto/aspeed/aspeed-hace-hash.c index 0b6e49c06eff..f8f37c9d5f3c 100644 --- a/drivers/crypto/aspeed/aspeed-hace-hash.c +++ b/drivers/crypto/aspeed/aspeed-hace-hash.c @@ -5,7 +5,6 @@ #include "aspeed-hace.h" #include -#include #include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include #include #ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG @@ -59,6 +59,46 @@ static const __be64 sha512_iv[8] = { cpu_to_be64(SHA512_H6), cpu_to_be64(SHA512_H7) }; +static int aspeed_sham_init(struct ahash_request *req); +static int aspeed_ahash_req_update(struct aspeed_hace_dev *hace_dev); + +static int aspeed_sham_export(struct ahash_request *req, void *out) +{ + struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); + union { + u8 *u8; + u64 *u64; + } p = { .u8 = out }; + + memcpy(out, rctx->digest, rctx->ivsize); + p.u8 += rctx->ivsize; + put_unaligned(rctx->digcnt[0], p.u64++); + if (rctx->ivsize == 64) + put_unaligned(rctx->digcnt[1], p.u64); + return 0; +} + +static int aspeed_sham_import(struct ahash_request *req, const void *in) +{ + struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); + union { + const u8 *u8; + const u64 *u64; + } p = { .u8 = in }; + int err; + + err = aspeed_sham_init(req); + if (err) + return err; + + memcpy(rctx->digest, in, rctx->ivsize); + p.u8 += rctx->ivsize; + rctx->digcnt[0] = get_unaligned(p.u64++); + if (rctx->ivsize == 64) + rctx->digcnt[1] = get_unaligned(p.u64); + return 0; +} + /* The purpose of this padding is to ensure that the padded message is a * multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512). * The bit "1" is appended at the end of the message followed by @@ -74,10 +114,10 @@ static const __be64 sha512_iv[8] = { * - if message length < 112 bytes then padlen = 112 - message length * - else padlen = 128 + 112 - message length */ -static void aspeed_ahash_fill_padding(struct aspeed_hace_dev *hace_dev, - struct aspeed_sham_reqctx *rctx) +static int aspeed_ahash_fill_padding(struct aspeed_hace_dev *hace_dev, + struct aspeed_sham_reqctx *rctx, u8 *buf) { - unsigned int index, padlen; + unsigned int index, padlen, bitslen; __be64 bits[2]; AHASH_DBG(hace_dev, "rctx flags:0x%x\n", (u32)rctx->flags); @@ -87,25 +127,32 @@ static void aspeed_ahash_fill_padding(struct aspeed_hace_dev *hace_dev, case SHA_FLAGS_SHA224: case SHA_FLAGS_SHA256: bits[0] = cpu_to_be64(rctx->digcnt[0] << 3); - index = rctx->bufcnt & 0x3f; + index = rctx->digcnt[0] & 0x3f; padlen = (index < 56) ? (56 - index) : ((64 + 56) - index); - *(rctx->buffer + rctx->bufcnt) = 0x80; - memset(rctx->buffer + rctx->bufcnt + 1, 0, padlen - 1); - memcpy(rctx->buffer + rctx->bufcnt + padlen, bits, 8); - rctx->bufcnt += padlen + 8; + bitslen = 8; break; default: bits[1] = cpu_to_be64(rctx->digcnt[0] << 3); bits[0] = cpu_to_be64(rctx->digcnt[1] << 3 | rctx->digcnt[0] >> 61); - index = rctx->bufcnt & 0x7f; + index = rctx->digcnt[0] & 0x7f; padlen = (index < 112) ? (112 - index) : ((128 + 112) - index); - *(rctx->buffer + rctx->bufcnt) = 0x80; - memset(rctx->buffer + rctx->bufcnt + 1, 0, padlen - 1); - memcpy(rctx->buffer + rctx->bufcnt + padlen, bits, 16); - rctx->bufcnt += padlen + 16; + bitslen = 16; break; } + buf[0] = 0x80; + memset(buf + 1, 0, padlen - 1); + memcpy(buf + padlen, bits, bitslen); + return padlen + bitslen; +} + +static void aspeed_ahash_update_counter(struct aspeed_sham_reqctx *rctx, + unsigned int len) +{ + rctx->offset += len; + rctx->digcnt[0] += len; + if (rctx->digcnt[0] < len) + rctx->digcnt[1]++; } /* @@ -117,31 +164,31 @@ static int aspeed_ahash_dma_prepare(struct aspeed_hace_dev *hace_dev) struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; struct ahash_request *req = hash_engine->req; struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - int length, remain; + unsigned int length, remain; + bool final = false; - length = rctx->total + rctx->bufcnt; - remain = length % rctx->block_size; + length = rctx->total - rctx->offset; + remain = length - round_down(length, rctx->block_size); AHASH_DBG(hace_dev, "length:0x%x, remain:0x%x\n", length, remain); - if (rctx->bufcnt) - memcpy(hash_engine->ahash_src_addr, rctx->buffer, rctx->bufcnt); + if (length > ASPEED_HASH_SRC_DMA_BUF_LEN) + length = ASPEED_HASH_SRC_DMA_BUF_LEN; + else if (rctx->flags & SHA_FLAGS_FINUP) { + if (round_up(length, rctx->block_size) + rctx->block_size > + ASPEED_CRYPTO_SRC_DMA_BUF_LEN) + length = round_down(length - 1, rctx->block_size); + else + final = true; + } else + length -= remain; + scatterwalk_map_and_copy(hash_engine->ahash_src_addr, rctx->src_sg, + rctx->offset, length, 0); + aspeed_ahash_update_counter(rctx, length); + if (final) + length += aspeed_ahash_fill_padding( + hace_dev, rctx, hash_engine->ahash_src_addr + length); - if (rctx->total + rctx->bufcnt < ASPEED_CRYPTO_SRC_DMA_BUF_LEN) { - scatterwalk_map_and_copy(hash_engine->ahash_src_addr + - rctx->bufcnt, rctx->src_sg, - rctx->offset, rctx->total - remain, 0); - rctx->offset += rctx->total - remain; - - } else { - dev_warn(hace_dev->dev, "Hash data length is too large\n"); - return -EINVAL; - } - - scatterwalk_map_and_copy(rctx->buffer, rctx->src_sg, - rctx->offset, remain, 0); - - rctx->bufcnt = remain; rctx->digest_dma_addr = dma_map_single(hace_dev->dev, rctx->digest, SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); @@ -150,7 +197,7 @@ static int aspeed_ahash_dma_prepare(struct aspeed_hace_dev *hace_dev) return -ENOMEM; } - hash_engine->src_length = length - remain; + hash_engine->src_length = length; hash_engine->src_dma = hash_engine->ahash_src_dma_addr; hash_engine->digest_dma = rctx->digest_dma_addr; @@ -166,16 +213,20 @@ static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev) struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; struct ahash_request *req = hash_engine->req; struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); + bool final = rctx->flags & SHA_FLAGS_FINUP; + int remain, sg_len, i, max_sg_nents; + unsigned int length, offset, total; struct aspeed_sg_list *src_list; struct scatterlist *s; - int length, remain, sg_len, i; int rc = 0; - remain = (rctx->total + rctx->bufcnt) % rctx->block_size; - length = rctx->total + rctx->bufcnt - remain; + offset = rctx->offset; + length = rctx->total - offset; + remain = final ? 0 : length - round_down(length, rctx->block_size); + length -= remain; - AHASH_DBG(hace_dev, "%s:0x%x, %s:%zu, %s:0x%x, %s:0x%x\n", - "rctx total", rctx->total, "bufcnt", rctx->bufcnt, + AHASH_DBG(hace_dev, "%s:0x%x, %s:0x%x, %s:0x%x\n", + "rctx total", rctx->total, "length", length, "remain", remain); sg_len = dma_map_sg(hace_dev->dev, rctx->src_sg, rctx->src_nents, @@ -186,6 +237,8 @@ static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev) goto end; } + max_sg_nents = ASPEED_HASH_SRC_DMA_BUF_LEN / sizeof(*src_list) - final; + sg_len = min(sg_len, max_sg_nents); src_list = (struct aspeed_sg_list *)hash_engine->ahash_src_addr; rctx->digest_dma_addr = dma_map_single(hace_dev->dev, rctx->digest, SHA512_DIGEST_SIZE, @@ -196,13 +249,47 @@ static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev) goto free_src_sg; } - if (rctx->bufcnt != 0) { - u32 phy_addr; - u32 len; + total = 0; + for_each_sg(rctx->src_sg, s, sg_len, i) { + u32 phy_addr = sg_dma_address(s); + u32 len = sg_dma_len(s); + if (len <= offset) { + offset -= len; + continue; + } + + len -= offset; + phy_addr += offset; + offset = 0; + + if (length > len) + length -= len; + else { + /* Last sg list */ + len = length; + length = 0; + } + + total += len; + src_list[i].phy_addr = cpu_to_le32(phy_addr); + src_list[i].len = cpu_to_le32(len); + } + + if (length != 0) { + total = round_down(total, rctx->block_size); + final = false; + } + + aspeed_ahash_update_counter(rctx, total); + if (final) { + int len = aspeed_ahash_fill_padding(hace_dev, rctx, + rctx->buffer); + + total += len; rctx->buffer_dma_addr = dma_map_single(hace_dev->dev, rctx->buffer, - rctx->block_size * 2, + sizeof(rctx->buffer), DMA_TO_DEVICE); if (dma_mapping_error(hace_dev->dev, rctx->buffer_dma_addr)) { dev_warn(hace_dev->dev, "dma_map() rctx buffer error\n"); @@ -210,54 +297,18 @@ static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev) goto free_rctx_digest; } - phy_addr = rctx->buffer_dma_addr; - len = rctx->bufcnt; - length -= len; - - /* Last sg list */ - if (length == 0) - len |= HASH_SG_LAST_LIST; - - src_list[0].phy_addr = cpu_to_le32(phy_addr); - src_list[0].len = cpu_to_le32(len); - src_list++; + src_list[i].phy_addr = cpu_to_le32(rctx->buffer_dma_addr); + src_list[i].len = cpu_to_le32(len); + i++; } + src_list[i - 1].len |= cpu_to_le32(HASH_SG_LAST_LIST); - if (length != 0) { - for_each_sg(rctx->src_sg, s, sg_len, i) { - u32 phy_addr = sg_dma_address(s); - u32 len = sg_dma_len(s); - - if (length > len) - length -= len; - else { - /* Last sg list */ - len = length; - len |= HASH_SG_LAST_LIST; - length = 0; - } - - src_list[i].phy_addr = cpu_to_le32(phy_addr); - src_list[i].len = cpu_to_le32(len); - } - } - - if (length != 0) { - rc = -EINVAL; - goto free_rctx_buffer; - } - - rctx->offset = rctx->total - remain; - hash_engine->src_length = rctx->total + rctx->bufcnt - remain; + hash_engine->src_length = total; hash_engine->src_dma = hash_engine->ahash_src_dma_addr; hash_engine->digest_dma = rctx->digest_dma_addr; return 0; -free_rctx_buffer: - if (rctx->bufcnt != 0) - dma_unmap_single(hace_dev->dev, rctx->buffer_dma_addr, - rctx->block_size * 2, DMA_TO_DEVICE); free_rctx_digest: dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); @@ -269,24 +320,6 @@ static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev) } static int aspeed_ahash_complete(struct aspeed_hace_dev *hace_dev) -{ - struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; - struct ahash_request *req = hash_engine->req; - - AHASH_DBG(hace_dev, "\n"); - - hash_engine->flags &= ~CRYPTO_FLAGS_BUSY; - - crypto_finalize_hash_request(hace_dev->crypt_engine_hash, req, 0); - - return 0; -} - -/* - * Copy digest to the corresponding request result. - * This function will be called at final() stage. - */ -static int aspeed_ahash_transfer(struct aspeed_hace_dev *hace_dev) { struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; struct ahash_request *req = hash_engine->req; @@ -297,12 +330,19 @@ static int aspeed_ahash_transfer(struct aspeed_hace_dev *hace_dev) dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); - dma_unmap_single(hace_dev->dev, rctx->buffer_dma_addr, - rctx->block_size * 2, DMA_TO_DEVICE); + if (rctx->total - rctx->offset >= rctx->block_size || + (rctx->total != rctx->offset && rctx->flags & SHA_FLAGS_FINUP)) + return aspeed_ahash_req_update(hace_dev); - memcpy(req->result, rctx->digest, rctx->digsize); + hash_engine->flags &= ~CRYPTO_FLAGS_BUSY; - return aspeed_ahash_complete(hace_dev); + if (rctx->flags & SHA_FLAGS_FINUP) + memcpy(req->result, rctx->digest, rctx->digsize); + + crypto_finalize_hash_request(hace_dev->crypt_engine_hash, req, + rctx->total - rctx->offset); + + return 0; } /* @@ -338,118 +378,6 @@ static int aspeed_hace_ahash_trigger(struct aspeed_hace_dev *hace_dev, return -EINPROGRESS; } -/* - * HMAC resume aims to do the second pass produces - * the final HMAC code derived from the inner hash - * result and the outer key. - */ -static int aspeed_ahash_hmac_resume(struct aspeed_hace_dev *hace_dev) -{ - struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; - struct ahash_request *req = hash_engine->req; - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); - struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); - struct aspeed_sha_hmac_ctx *bctx = tctx->base; - int rc = 0; - - AHASH_DBG(hace_dev, "\n"); - - dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, - SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); - - dma_unmap_single(hace_dev->dev, rctx->buffer_dma_addr, - rctx->block_size * 2, DMA_TO_DEVICE); - - /* o key pad + hash sum 1 */ - memcpy(rctx->buffer, bctx->opad, rctx->block_size); - memcpy(rctx->buffer + rctx->block_size, rctx->digest, rctx->digsize); - - rctx->bufcnt = rctx->block_size + rctx->digsize; - rctx->digcnt[0] = rctx->block_size + rctx->digsize; - - aspeed_ahash_fill_padding(hace_dev, rctx); - memcpy(rctx->digest, rctx->sha_iv, rctx->ivsize); - - rctx->digest_dma_addr = dma_map_single(hace_dev->dev, rctx->digest, - SHA512_DIGEST_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(hace_dev->dev, rctx->digest_dma_addr)) { - dev_warn(hace_dev->dev, "dma_map() rctx digest error\n"); - rc = -ENOMEM; - goto end; - } - - rctx->buffer_dma_addr = dma_map_single(hace_dev->dev, rctx->buffer, - rctx->block_size * 2, - DMA_TO_DEVICE); - if (dma_mapping_error(hace_dev->dev, rctx->buffer_dma_addr)) { - dev_warn(hace_dev->dev, "dma_map() rctx buffer error\n"); - rc = -ENOMEM; - goto free_rctx_digest; - } - - hash_engine->src_dma = rctx->buffer_dma_addr; - hash_engine->src_length = rctx->bufcnt; - hash_engine->digest_dma = rctx->digest_dma_addr; - - return aspeed_hace_ahash_trigger(hace_dev, aspeed_ahash_transfer); - -free_rctx_digest: - dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, - SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); -end: - return rc; -} - -static int aspeed_ahash_req_final(struct aspeed_hace_dev *hace_dev) -{ - struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; - struct ahash_request *req = hash_engine->req; - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - int rc = 0; - - AHASH_DBG(hace_dev, "\n"); - - aspeed_ahash_fill_padding(hace_dev, rctx); - - rctx->digest_dma_addr = dma_map_single(hace_dev->dev, - rctx->digest, - SHA512_DIGEST_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(hace_dev->dev, rctx->digest_dma_addr)) { - dev_warn(hace_dev->dev, "dma_map() rctx digest error\n"); - rc = -ENOMEM; - goto end; - } - - rctx->buffer_dma_addr = dma_map_single(hace_dev->dev, - rctx->buffer, - rctx->block_size * 2, - DMA_TO_DEVICE); - if (dma_mapping_error(hace_dev->dev, rctx->buffer_dma_addr)) { - dev_warn(hace_dev->dev, "dma_map() rctx buffer error\n"); - rc = -ENOMEM; - goto free_rctx_digest; - } - - hash_engine->src_dma = rctx->buffer_dma_addr; - hash_engine->src_length = rctx->bufcnt; - hash_engine->digest_dma = rctx->digest_dma_addr; - - if (rctx->flags & SHA_FLAGS_HMAC) - return aspeed_hace_ahash_trigger(hace_dev, - aspeed_ahash_hmac_resume); - - return aspeed_hace_ahash_trigger(hace_dev, aspeed_ahash_transfer); - -free_rctx_digest: - dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, - SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); -end: - return rc; -} - static int aspeed_ahash_update_resume_sg(struct aspeed_hace_dev *hace_dev) { struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; @@ -461,40 +389,12 @@ static int aspeed_ahash_update_resume_sg(struct aspeed_hace_dev *hace_dev) dma_unmap_sg(hace_dev->dev, rctx->src_sg, rctx->src_nents, DMA_TO_DEVICE); - if (rctx->bufcnt != 0) + if (rctx->flags & SHA_FLAGS_FINUP && rctx->total == rctx->offset) dma_unmap_single(hace_dev->dev, rctx->buffer_dma_addr, - rctx->block_size * 2, - DMA_TO_DEVICE); + sizeof(rctx->buffer), DMA_TO_DEVICE); - dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, - SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); - - scatterwalk_map_and_copy(rctx->buffer, rctx->src_sg, rctx->offset, - rctx->total - rctx->offset, 0); - - rctx->bufcnt = rctx->total - rctx->offset; rctx->cmd &= ~HASH_CMD_HASH_SRC_SG_CTRL; - if (rctx->flags & SHA_FLAGS_FINUP) - return aspeed_ahash_req_final(hace_dev); - - return aspeed_ahash_complete(hace_dev); -} - -static int aspeed_ahash_update_resume(struct aspeed_hace_dev *hace_dev) -{ - struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine; - struct ahash_request *req = hash_engine->req; - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - - AHASH_DBG(hace_dev, "\n"); - - dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr, - SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL); - - if (rctx->flags & SHA_FLAGS_FINUP) - return aspeed_ahash_req_final(hace_dev); - return aspeed_ahash_complete(hace_dev); } @@ -513,7 +413,7 @@ static int aspeed_ahash_req_update(struct aspeed_hace_dev *hace_dev) resume = aspeed_ahash_update_resume_sg; } else { - resume = aspeed_ahash_update_resume; + resume = aspeed_ahash_complete; } ret = hash_engine->dma_prepare(hace_dev); @@ -530,26 +430,47 @@ static int aspeed_hace_hash_handle_queue(struct aspeed_hace_dev *hace_dev, hace_dev->crypt_engine_hash, req); } +static noinline int aspeed_ahash_fallback(struct ahash_request *req) +{ + struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); + HASH_FBREQ_ON_STACK(fbreq, req); + u8 *state = rctx->buffer; + struct scatterlist sg[2]; + struct scatterlist *ssg; + int ret; + + ssg = scatterwalk_ffwd(sg, req->src, rctx->offset); + ahash_request_set_crypt(fbreq, ssg, req->result, + rctx->total - rctx->offset); + + ret = aspeed_sham_export(req, state) ?: + crypto_ahash_import_core(fbreq, state); + + if (rctx->flags & SHA_FLAGS_FINUP) + ret = ret ?: crypto_ahash_finup(fbreq); + else + ret = ret ?: crypto_ahash_update(fbreq) ?: + crypto_ahash_export_core(fbreq, state) ?: + aspeed_sham_import(req, state); + HASH_REQUEST_ZERO(fbreq); + return ret; +} + static int aspeed_ahash_do_request(struct crypto_engine *engine, void *areq) { struct ahash_request *req = ahash_request_cast(areq); - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); struct aspeed_hace_dev *hace_dev = tctx->hace_dev; struct aspeed_engine_hash *hash_engine; - int ret = 0; + int ret; hash_engine = &hace_dev->hash_engine; hash_engine->flags |= CRYPTO_FLAGS_BUSY; - if (rctx->op == SHA_OP_UPDATE) - ret = aspeed_ahash_req_update(hace_dev); - else if (rctx->op == SHA_OP_FINAL) - ret = aspeed_ahash_req_final(hace_dev); - + ret = aspeed_ahash_req_update(hace_dev); if (ret != -EINPROGRESS) - return ret; + return aspeed_ahash_fallback(req); return 0; } @@ -590,45 +511,7 @@ static int aspeed_sham_update(struct ahash_request *req) rctx->total = req->nbytes; rctx->src_sg = req->src; rctx->offset = 0; - rctx->src_nents = sg_nents(req->src); - rctx->op = SHA_OP_UPDATE; - - rctx->digcnt[0] += rctx->total; - if (rctx->digcnt[0] < rctx->total) - rctx->digcnt[1]++; - - if (rctx->bufcnt + rctx->total < rctx->block_size) { - scatterwalk_map_and_copy(rctx->buffer + rctx->bufcnt, - rctx->src_sg, rctx->offset, - rctx->total, 0); - rctx->bufcnt += rctx->total; - - return 0; - } - - return aspeed_hace_hash_handle_queue(hace_dev, req); -} - -static int aspeed_sham_shash_digest(struct crypto_shash *tfm, u32 flags, - const u8 *data, unsigned int len, u8 *out) -{ - SHASH_DESC_ON_STACK(shash, tfm); - - shash->tfm = tfm; - - return crypto_shash_digest(shash, data, len, out); -} - -static int aspeed_sham_final(struct ahash_request *req) -{ - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); - struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); - struct aspeed_hace_dev *hace_dev = tctx->hace_dev; - - AHASH_DBG(hace_dev, "req->nbytes:%d, rctx->total:%d\n", - req->nbytes, rctx->total); - rctx->op = SHA_OP_FINAL; + rctx->src_nents = sg_nents_for_len(req->src, req->nbytes); return aspeed_hace_hash_handle_queue(hace_dev, req); } @@ -639,23 +522,12 @@ static int aspeed_sham_finup(struct ahash_request *req) struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); struct aspeed_hace_dev *hace_dev = tctx->hace_dev; - int rc1, rc2; AHASH_DBG(hace_dev, "req->nbytes: %d\n", req->nbytes); rctx->flags |= SHA_FLAGS_FINUP; - rc1 = aspeed_sham_update(req); - if (rc1 == -EINPROGRESS || rc1 == -EBUSY) - return rc1; - - /* - * final() has to be always called to cleanup resources - * even if update() failed, except EINPROGRESS - */ - rc2 = aspeed_sham_final(req); - - return rc1 ? : rc2; + return aspeed_sham_update(req); } static int aspeed_sham_init(struct ahash_request *req) @@ -664,7 +536,6 @@ static int aspeed_sham_init(struct ahash_request *req) struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); struct aspeed_hace_dev *hace_dev = tctx->hace_dev; - struct aspeed_sha_hmac_ctx *bctx = tctx->base; AHASH_DBG(hace_dev, "%s: digest size:%d\n", crypto_tfm_alg_name(&tfm->base), @@ -679,7 +550,6 @@ static int aspeed_sham_init(struct ahash_request *req) rctx->flags |= SHA_FLAGS_SHA1; rctx->digsize = SHA1_DIGEST_SIZE; rctx->block_size = SHA1_BLOCK_SIZE; - rctx->sha_iv = sha1_iv; rctx->ivsize = 32; memcpy(rctx->digest, sha1_iv, rctx->ivsize); break; @@ -688,7 +558,6 @@ static int aspeed_sham_init(struct ahash_request *req) rctx->flags |= SHA_FLAGS_SHA224; rctx->digsize = SHA224_DIGEST_SIZE; rctx->block_size = SHA224_BLOCK_SIZE; - rctx->sha_iv = sha224_iv; rctx->ivsize = 32; memcpy(rctx->digest, sha224_iv, rctx->ivsize); break; @@ -697,7 +566,6 @@ static int aspeed_sham_init(struct ahash_request *req) rctx->flags |= SHA_FLAGS_SHA256; rctx->digsize = SHA256_DIGEST_SIZE; rctx->block_size = SHA256_BLOCK_SIZE; - rctx->sha_iv = sha256_iv; rctx->ivsize = 32; memcpy(rctx->digest, sha256_iv, rctx->ivsize); break; @@ -707,7 +575,6 @@ static int aspeed_sham_init(struct ahash_request *req) rctx->flags |= SHA_FLAGS_SHA384; rctx->digsize = SHA384_DIGEST_SIZE; rctx->block_size = SHA384_BLOCK_SIZE; - rctx->sha_iv = (const __be32 *)sha384_iv; rctx->ivsize = 64; memcpy(rctx->digest, sha384_iv, rctx->ivsize); break; @@ -717,7 +584,6 @@ static int aspeed_sham_init(struct ahash_request *req) rctx->flags |= SHA_FLAGS_SHA512; rctx->digsize = SHA512_DIGEST_SIZE; rctx->block_size = SHA512_BLOCK_SIZE; - rctx->sha_iv = (const __be32 *)sha512_iv; rctx->ivsize = 64; memcpy(rctx->digest, sha512_iv, rctx->ivsize); break; @@ -727,19 +593,10 @@ static int aspeed_sham_init(struct ahash_request *req) return -EINVAL; } - rctx->bufcnt = 0; rctx->total = 0; rctx->digcnt[0] = 0; rctx->digcnt[1] = 0; - /* HMAC init */ - if (tctx->flags & SHA_FLAGS_HMAC) { - rctx->digcnt[0] = rctx->block_size; - rctx->bufcnt = rctx->block_size; - memcpy(rctx->buffer, bctx->ipad, rctx->block_size); - rctx->flags |= SHA_FLAGS_HMAC; - } - return 0; } @@ -748,102 +605,14 @@ static int aspeed_sham_digest(struct ahash_request *req) return aspeed_sham_init(req) ? : aspeed_sham_finup(req); } -static int aspeed_sham_setkey(struct crypto_ahash *tfm, const u8 *key, - unsigned int keylen) +static int aspeed_sham_cra_init(struct crypto_ahash *tfm) { + struct ahash_alg *alg = crypto_ahash_alg(tfm); struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm); - struct aspeed_hace_dev *hace_dev = tctx->hace_dev; - struct aspeed_sha_hmac_ctx *bctx = tctx->base; - int ds = crypto_shash_digestsize(bctx->shash); - int bs = crypto_shash_blocksize(bctx->shash); - int err = 0; - int i; - - AHASH_DBG(hace_dev, "%s: keylen:%d\n", crypto_tfm_alg_name(&tfm->base), - keylen); - - if (keylen > bs) { - err = aspeed_sham_shash_digest(bctx->shash, - crypto_shash_get_flags(bctx->shash), - key, keylen, bctx->ipad); - if (err) - return err; - keylen = ds; - - } else { - memcpy(bctx->ipad, key, keylen); - } - - memset(bctx->ipad + keylen, 0, bs - keylen); - memcpy(bctx->opad, bctx->ipad, bs); - - for (i = 0; i < bs; i++) { - bctx->ipad[i] ^= HMAC_IPAD_VALUE; - bctx->opad[i] ^= HMAC_OPAD_VALUE; - } - - return err; -} - -static int aspeed_sham_cra_init(struct crypto_tfm *tfm) -{ - struct ahash_alg *alg = __crypto_ahash_alg(tfm->__crt_alg); - struct aspeed_sham_ctx *tctx = crypto_tfm_ctx(tfm); struct aspeed_hace_alg *ast_alg; ast_alg = container_of(alg, struct aspeed_hace_alg, alg.ahash.base); tctx->hace_dev = ast_alg->hace_dev; - tctx->flags = 0; - - crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), - sizeof(struct aspeed_sham_reqctx)); - - if (ast_alg->alg_base) { - /* hmac related */ - struct aspeed_sha_hmac_ctx *bctx = tctx->base; - - tctx->flags |= SHA_FLAGS_HMAC; - bctx->shash = crypto_alloc_shash(ast_alg->alg_base, 0, - CRYPTO_ALG_NEED_FALLBACK); - if (IS_ERR(bctx->shash)) { - dev_warn(ast_alg->hace_dev->dev, - "base driver '%s' could not be loaded.\n", - ast_alg->alg_base); - return PTR_ERR(bctx->shash); - } - } - - return 0; -} - -static void aspeed_sham_cra_exit(struct crypto_tfm *tfm) -{ - struct aspeed_sham_ctx *tctx = crypto_tfm_ctx(tfm); - struct aspeed_hace_dev *hace_dev = tctx->hace_dev; - - AHASH_DBG(hace_dev, "%s\n", crypto_tfm_alg_name(tfm)); - - if (tctx->flags & SHA_FLAGS_HMAC) { - struct aspeed_sha_hmac_ctx *bctx = tctx->base; - - crypto_free_shash(bctx->shash); - } -} - -static int aspeed_sham_export(struct ahash_request *req, void *out) -{ - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - - memcpy(out, rctx, sizeof(*rctx)); - - return 0; -} - -static int aspeed_sham_import(struct ahash_request *req, const void *in) -{ - struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req); - - memcpy(rctx, in, sizeof(*rctx)); return 0; } @@ -853,11 +622,11 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .alg.ahash.base = { .init = aspeed_sham_init, .update = aspeed_sham_update, - .final = aspeed_sham_final, .finup = aspeed_sham_finup, .digest = aspeed_sham_digest, .export = aspeed_sham_export, .import = aspeed_sham_import, + .init_tfm = aspeed_sham_cra_init, .halg = { .digestsize = SHA1_DIGEST_SIZE, .statesize = sizeof(struct aspeed_sham_reqctx), @@ -867,13 +636,13 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | + CRYPTO_AHASH_ALG_BLOCK_ONLY | CRYPTO_ALG_KERN_DRIVER_ONLY, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aspeed_sham_ctx), + .cra_reqsize = sizeof(struct aspeed_sham_reqctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, } } }, @@ -885,11 +654,11 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .alg.ahash.base = { .init = aspeed_sham_init, .update = aspeed_sham_update, - .final = aspeed_sham_final, .finup = aspeed_sham_finup, .digest = aspeed_sham_digest, .export = aspeed_sham_export, .import = aspeed_sham_import, + .init_tfm = aspeed_sham_cra_init, .halg = { .digestsize = SHA256_DIGEST_SIZE, .statesize = sizeof(struct aspeed_sham_reqctx), @@ -899,13 +668,13 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | + CRYPTO_AHASH_ALG_BLOCK_ONLY | CRYPTO_ALG_KERN_DRIVER_ONLY, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aspeed_sham_ctx), + .cra_reqsize = sizeof(struct aspeed_sham_reqctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, } } }, @@ -917,11 +686,11 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .alg.ahash.base = { .init = aspeed_sham_init, .update = aspeed_sham_update, - .final = aspeed_sham_final, .finup = aspeed_sham_finup, .digest = aspeed_sham_digest, .export = aspeed_sham_export, .import = aspeed_sham_import, + .init_tfm = aspeed_sham_cra_init, .halg = { .digestsize = SHA224_DIGEST_SIZE, .statesize = sizeof(struct aspeed_sham_reqctx), @@ -931,118 +700,13 @@ static struct aspeed_hace_alg aspeed_ahash_algs[] = { .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | + CRYPTO_AHASH_ALG_BLOCK_ONLY | CRYPTO_ALG_KERN_DRIVER_ONLY, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aspeed_sham_ctx), + .cra_reqsize = sizeof(struct aspeed_sham_reqctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, - } - } - }, - .alg.ahash.op = { - .do_one_request = aspeed_ahash_do_one, - }, - }, - { - .alg_base = "sha1", - .alg.ahash.base = { - .init = aspeed_sham_init, - .update = aspeed_sham_update, - .final = aspeed_sham_final, - .finup = aspeed_sham_finup, - .digest = aspeed_sham_digest, - .setkey = aspeed_sham_setkey, - .export = aspeed_sham_export, - .import = aspeed_sham_import, - .halg = { - .digestsize = SHA1_DIGEST_SIZE, - .statesize = sizeof(struct aspeed_sham_reqctx), - .base = { - .cra_name = "hmac(sha1)", - .cra_driver_name = "aspeed-hmac-sha1", - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct aspeed_sham_ctx) + - sizeof(struct aspeed_sha_hmac_ctx), - .cra_alignmask = 0, - .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, - } - } - }, - .alg.ahash.op = { - .do_one_request = aspeed_ahash_do_one, - }, - }, - { - .alg_base = "sha224", - .alg.ahash.base = { - .init = aspeed_sham_init, - .update = aspeed_sham_update, - .final = aspeed_sham_final, - .finup = aspeed_sham_finup, - .digest = aspeed_sham_digest, - .setkey = aspeed_sham_setkey, - .export = aspeed_sham_export, - .import = aspeed_sham_import, - .halg = { - .digestsize = SHA224_DIGEST_SIZE, - .statesize = sizeof(struct aspeed_sham_reqctx), - .base = { - .cra_name = "hmac(sha224)", - .cra_driver_name = "aspeed-hmac-sha224", - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = SHA224_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct aspeed_sham_ctx) + - sizeof(struct aspeed_sha_hmac_ctx), - .cra_alignmask = 0, - .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, - } - } - }, - .alg.ahash.op = { - .do_one_request = aspeed_ahash_do_one, - }, - }, - { - .alg_base = "sha256", - .alg.ahash.base = { - .init = aspeed_sham_init, - .update = aspeed_sham_update, - .final = aspeed_sham_final, - .finup = aspeed_sham_finup, - .digest = aspeed_sham_digest, - .setkey = aspeed_sham_setkey, - .export = aspeed_sham_export, - .import = aspeed_sham_import, - .halg = { - .digestsize = SHA256_DIGEST_SIZE, - .statesize = sizeof(struct aspeed_sham_reqctx), - .base = { - .cra_name = "hmac(sha256)", - .cra_driver_name = "aspeed-hmac-sha256", - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = SHA256_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct aspeed_sham_ctx) + - sizeof(struct aspeed_sha_hmac_ctx), - .cra_alignmask = 0, - .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, } } }, @@ -1057,11 +721,11 @@ static struct aspeed_hace_alg aspeed_ahash_algs_g6[] = { .alg.ahash.base = { .init = aspeed_sham_init, .update = aspeed_sham_update, - .final = aspeed_sham_final, .finup = aspeed_sham_finup, .digest = aspeed_sham_digest, .export = aspeed_sham_export, .import = aspeed_sham_import, + .init_tfm = aspeed_sham_cra_init, .halg = { .digestsize = SHA384_DIGEST_SIZE, .statesize = sizeof(struct aspeed_sham_reqctx), @@ -1071,13 +735,13 @@ static struct aspeed_hace_alg aspeed_ahash_algs_g6[] = { .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | + CRYPTO_AHASH_ALG_BLOCK_ONLY | CRYPTO_ALG_KERN_DRIVER_ONLY, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aspeed_sham_ctx), + .cra_reqsize = sizeof(struct aspeed_sham_reqctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, } } }, @@ -1089,11 +753,11 @@ static struct aspeed_hace_alg aspeed_ahash_algs_g6[] = { .alg.ahash.base = { .init = aspeed_sham_init, .update = aspeed_sham_update, - .final = aspeed_sham_final, .finup = aspeed_sham_finup, .digest = aspeed_sham_digest, .export = aspeed_sham_export, .import = aspeed_sham_import, + .init_tfm = aspeed_sham_cra_init, .halg = { .digestsize = SHA512_DIGEST_SIZE, .statesize = sizeof(struct aspeed_sham_reqctx), @@ -1103,83 +767,13 @@ static struct aspeed_hace_alg aspeed_ahash_algs_g6[] = { .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | + CRYPTO_AHASH_ALG_BLOCK_ONLY | CRYPTO_ALG_KERN_DRIVER_ONLY, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aspeed_sham_ctx), + .cra_reqsize = sizeof(struct aspeed_sham_reqctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, - } - } - }, - .alg.ahash.op = { - .do_one_request = aspeed_ahash_do_one, - }, - }, - { - .alg_base = "sha384", - .alg.ahash.base = { - .init = aspeed_sham_init, - .update = aspeed_sham_update, - .final = aspeed_sham_final, - .finup = aspeed_sham_finup, - .digest = aspeed_sham_digest, - .setkey = aspeed_sham_setkey, - .export = aspeed_sham_export, - .import = aspeed_sham_import, - .halg = { - .digestsize = SHA384_DIGEST_SIZE, - .statesize = sizeof(struct aspeed_sham_reqctx), - .base = { - .cra_name = "hmac(sha384)", - .cra_driver_name = "aspeed-hmac-sha384", - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = SHA384_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct aspeed_sham_ctx) + - sizeof(struct aspeed_sha_hmac_ctx), - .cra_alignmask = 0, - .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, - } - } - }, - .alg.ahash.op = { - .do_one_request = aspeed_ahash_do_one, - }, - }, - { - .alg_base = "sha512", - .alg.ahash.base = { - .init = aspeed_sham_init, - .update = aspeed_sham_update, - .final = aspeed_sham_final, - .finup = aspeed_sham_finup, - .digest = aspeed_sham_digest, - .setkey = aspeed_sham_setkey, - .export = aspeed_sham_export, - .import = aspeed_sham_import, - .halg = { - .digestsize = SHA512_DIGEST_SIZE, - .statesize = sizeof(struct aspeed_sham_reqctx), - .base = { - .cra_name = "hmac(sha512)", - .cra_driver_name = "aspeed-hmac-sha512", - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = SHA512_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct aspeed_sham_ctx) + - sizeof(struct aspeed_sha_hmac_ctx), - .cra_alignmask = 0, - .cra_module = THIS_MODULE, - .cra_init = aspeed_sham_cra_init, - .cra_exit = aspeed_sham_cra_exit, } } }, diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed-hace.h index 68f70e01fccb..b1d07730d543 100644 --- a/drivers/crypto/aspeed/aspeed-hace.h +++ b/drivers/crypto/aspeed/aspeed-hace.h @@ -119,7 +119,6 @@ #define SHA_FLAGS_SHA512 BIT(4) #define SHA_FLAGS_SHA512_224 BIT(5) #define SHA_FLAGS_SHA512_256 BIT(6) -#define SHA_FLAGS_HMAC BIT(8) #define SHA_FLAGS_FINUP BIT(9) #define SHA_FLAGS_MASK (0xff) @@ -161,22 +160,18 @@ struct aspeed_engine_hash { aspeed_hace_fn_t dma_prepare; }; -struct aspeed_sha_hmac_ctx { - struct crypto_shash *shash; - u8 ipad[SHA512_BLOCK_SIZE]; - u8 opad[SHA512_BLOCK_SIZE]; -}; - struct aspeed_sham_ctx { struct aspeed_hace_dev *hace_dev; - unsigned long flags; /* hmac flag */ - - struct aspeed_sha_hmac_ctx base[]; }; struct aspeed_sham_reqctx { + /* DMA buffer written by hardware */ + u8 digest[SHA512_DIGEST_SIZE] __aligned(64); + + /* Software state sorted by size. */ + u64 digcnt[2]; + unsigned long flags; /* final update flag should no use*/ - unsigned long op; /* final or update */ u32 cmd; /* trigger cmd */ /* walk state */ @@ -188,17 +183,12 @@ struct aspeed_sham_reqctx { size_t digsize; size_t block_size; size_t ivsize; - const __be32 *sha_iv; - /* remain data buffer */ - u8 buffer[SHA512_BLOCK_SIZE * 2]; dma_addr_t buffer_dma_addr; - size_t bufcnt; /* buffer counter */ - - /* output buffer */ - u8 digest[SHA512_DIGEST_SIZE] __aligned(64); dma_addr_t digest_dma_addr; - u64 digcnt[2]; + + /* This is DMA too but read-only for hardware. */ + u8 buffer[SHA512_BLOCK_SIZE + 16]; }; struct aspeed_engine_crypto { diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index 27c5d000b4b2..3a2684208dda 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -2297,6 +2297,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) /* keep only major version number */ switch (dd->hw_version & 0xff0) { + case 0x800: case 0x700: case 0x600: case 0x500: diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 2cc36da163e8..3d7573c7bd1c 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -2534,6 +2534,7 @@ static void atmel_sha_get_cap(struct atmel_sha_dev *dd) /* keep only major version number */ switch (dd->hw_version & 0xff0) { + case 0x800: case 0x700: case 0x600: case 0x510: diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index acf1b197eb84..d2eaf5205b1c 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile @@ -25,10 +25,6 @@ caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.o caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o -ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),) - ccflags-y += -DCONFIG_CAAM_QI -endif - caam-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_CRYPTO_DEV_FSL_DPAA2_CAAM) += dpaa2_caam.o diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 38ff931059b4..a93be395c878 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -24,7 +24,7 @@ bool caam_dpaa2; EXPORT_SYMBOL(caam_dpaa2); -#ifdef CONFIG_CAAM_QI +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI #include "qi.h" #endif @@ -573,7 +573,7 @@ static const struct soc_device_attribute caam_imx_soc_table[] = { { .soc_id = "i.MX7*", .data = &caam_imx7_data }, { .soc_id = "i.MX8M*", .data = &caam_imx7_data }, { .soc_id = "i.MX8ULP", .data = &caam_imx8ulp_data }, - { .soc_id = "i.MX8QM", .data = &caam_imx8ulp_data }, + { .soc_id = "i.MX8Q*", .data = &caam_imx8ulp_data }, { .soc_id = "VF*", .data = &caam_vf610_data }, { .family = "Freescale i.MX" }, { /* sentinel */ } @@ -831,7 +831,7 @@ static int caam_ctrl_suspend(struct device *dev) { const struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); - if (ctrlpriv->caam_off_during_pm && !ctrlpriv->optee_en) + if (ctrlpriv->caam_off_during_pm && !ctrlpriv->no_page0) caam_state_save(dev); return 0; @@ -842,7 +842,7 @@ static int caam_ctrl_resume(struct device *dev) struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); int ret = 0; - if (ctrlpriv->caam_off_during_pm && !ctrlpriv->optee_en) { + if (ctrlpriv->caam_off_during_pm && !ctrlpriv->no_page0) { caam_state_restore(dev); /* HW and rng will be reset so deinstantiation can be removed */ @@ -908,6 +908,7 @@ static int caam_probe(struct platform_device *pdev) imx_soc_data = imx_soc_match->data; reg_access = reg_access && imx_soc_data->page0_access; + ctrlpriv->no_page0 = !reg_access; /* * CAAM clocks cannot be controlled from kernel. */ @@ -967,7 +968,7 @@ static int caam_probe(struct platform_device *pdev) caam_dpaa2 = !!(comp_params & CTPR_MS_DPAA2); ctrlpriv->qi_present = !!(comp_params & CTPR_MS_QI_MASK); -#ifdef CONFIG_CAAM_QI +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI /* If (DPAA 1.x) QI present, check whether dependencies are available */ if (ctrlpriv->qi_present && !caam_dpaa2) { ret = qman_is_probed(); @@ -1098,7 +1099,7 @@ static int caam_probe(struct platform_device *pdev) wr_reg32(&ctrlpriv->qi->qi_control_lo, QICTL_DQEN); /* If QMAN driver is present, init CAAM-QI backend */ -#ifdef CONFIG_CAAM_QI +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI ret = caam_qi_init(pdev); if (ret) dev_err(dev, "caam qi i/f init failed: %d\n", ret); diff --git a/drivers/crypto/caam/debugfs.c b/drivers/crypto/caam/debugfs.c index 6358d3cabf57..718352b7afb5 100644 --- a/drivers/crypto/caam/debugfs.c +++ b/drivers/crypto/caam/debugfs.c @@ -22,7 +22,7 @@ static int caam_debugfs_u32_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(caam_fops_u32_ro, caam_debugfs_u32_get, NULL, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(caam_fops_u64_ro, caam_debugfs_u64_get, NULL, "%llu\n"); -#ifdef CONFIG_CAAM_QI +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI /* * This is a counter for the number of times the congestion group (where all * the request and response queueus are) reached congestion. Incremented diff --git a/drivers/crypto/caam/debugfs.h b/drivers/crypto/caam/debugfs.h index 8b5d1acd21a7..ef238c71f92a 100644 --- a/drivers/crypto/caam/debugfs.h +++ b/drivers/crypto/caam/debugfs.h @@ -18,7 +18,7 @@ static inline void caam_debugfs_init(struct caam_drv_private *ctrlpriv, {} #endif -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_CAAM_QI) +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) void caam_debugfs_qi_congested(void); void caam_debugfs_qi_init(struct caam_drv_private *ctrlpriv); #else diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index e51320150872..a88da0d31b23 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -115,6 +115,7 @@ struct caam_drv_private { u8 blob_present; /* Nonzero if BLOB support present in device */ u8 mc_en; /* Nonzero if MC f/w is active */ u8 optee_en; /* Nonzero if OP-TEE f/w is active */ + u8 no_page0; /* Nonzero if register page 0 is not controlled by Linux */ bool pr_support; /* RNG prediction resistance available */ int secvio_irq; /* Security violation interrupt number */ int virt_en; /* Virtualization enabled in CAAM */ @@ -226,7 +227,7 @@ static inline int caam_prng_register(struct device *dev) static inline void caam_prng_unregister(void *data) {} #endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API */ -#ifdef CONFIG_CAAM_QI +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI int caam_qi_algapi_init(struct device *dev); void caam_qi_algapi_exit(void); @@ -242,7 +243,7 @@ static inline void caam_qi_algapi_exit(void) { } -#endif /* CONFIG_CAAM_QI */ +#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI */ static inline u64 caam_get_dma_mask(struct device *dev) { diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 9fcdb64084ac..0ef00df9730e 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -629,8 +629,7 @@ static int caam_jr_probe(struct platform_device *pdev) } /* Initialize crypto engine */ - jrpriv->engine = crypto_engine_alloc_init_and_set(jrdev, true, NULL, - false, + jrpriv->engine = crypto_engine_alloc_init_and_set(jrdev, true, false, CRYPTO_ENGINE_MAX_QLEN); if (!jrpriv->engine) { dev_err(jrdev, "Could not init crypto-engine\n"); diff --git a/drivers/crypto/caam/qi.c b/drivers/crypto/caam/qi.c index b6e7c0b29d4e..1e731ed8702b 100644 --- a/drivers/crypto/caam/qi.c +++ b/drivers/crypto/caam/qi.c @@ -442,11 +442,8 @@ struct caam_drv_ctx *caam_drv_ctx_init(struct device *qidev, if (!cpumask_test_cpu(*cpu, cpus)) { int *pcpu = &get_cpu_var(last_cpu); - *pcpu = cpumask_next(*pcpu, cpus); - if (*pcpu >= nr_cpu_ids) - *pcpu = cpumask_first(cpus); + *pcpu = cpumask_next_wrap(*pcpu, cpus); *cpu = *pcpu; - put_cpu_var(last_cpu); } drv_ctx->cpu = *cpu; diff --git a/drivers/crypto/ccp/ccp-debugfs.c b/drivers/crypto/ccp/ccp-debugfs.c index a1055554b47a..dc26bc22c91d 100644 --- a/drivers/crypto/ccp/ccp-debugfs.c +++ b/drivers/crypto/ccp/ccp-debugfs.c @@ -319,5 +319,8 @@ void ccp5_debugfs_setup(struct ccp_device *ccp) void ccp5_debugfs_destroy(void) { + mutex_lock(&ccp_debugfs_lock); debugfs_remove_recursive(ccp_debugfs_dir); + ccp_debugfs_dir = NULL; + mutex_unlock(&ccp_debugfs_lock); } diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c index 109b5aef4034..d78865d9d5f0 100644 --- a/drivers/crypto/ccp/ccp-ops.c +++ b/drivers/crypto/ccp/ccp-ops.c @@ -633,10 +633,16 @@ static noinline_for_stack int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_aes_engine *aes = &cmd->u.aes; - struct ccp_dm_workarea key, ctx, final_wa, tag; - struct ccp_data src, dst; - struct ccp_data aad; - struct ccp_op op; + struct { + struct ccp_dm_workarea key; + struct ccp_dm_workarea ctx; + struct ccp_dm_workarea final; + struct ccp_dm_workarea tag; + struct ccp_data src; + struct ccp_data dst; + struct ccp_data aad; + struct ccp_op op; + } *wa __cleanup(kfree) = kzalloc(sizeof *wa, GFP_KERNEL); unsigned int dm_offset; unsigned int authsize; unsigned int jobid; @@ -650,6 +656,9 @@ ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) struct scatterlist *p_outp, sg_outp[2]; struct scatterlist *p_aad; + if (!wa) + return -ENOMEM; + if (!aes->iv) return -EINVAL; @@ -696,26 +705,26 @@ ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) jobid = CCP_NEW_JOBID(cmd_q->ccp); - memset(&op, 0, sizeof(op)); - op.cmd_q = cmd_q; - op.jobid = jobid; - op.sb_key = cmd_q->sb_key; /* Pre-allocated */ - op.sb_ctx = cmd_q->sb_ctx; /* Pre-allocated */ - op.init = 1; - op.u.aes.type = aes->type; + memset(&wa->op, 0, sizeof(wa->op)); + wa->op.cmd_q = cmd_q; + wa->op.jobid = jobid; + wa->op.sb_key = cmd_q->sb_key; /* Pre-allocated */ + wa->op.sb_ctx = cmd_q->sb_ctx; /* Pre-allocated */ + wa->op.init = 1; + wa->op.u.aes.type = aes->type; /* Copy the key to the LSB */ - ret = ccp_init_dm_workarea(&key, cmd_q, + ret = ccp_init_dm_workarea(&wa->key, cmd_q, CCP_AES_CTX_SB_COUNT * CCP_SB_BYTES, DMA_TO_DEVICE); if (ret) return ret; dm_offset = CCP_SB_BYTES - aes->key_len; - ret = ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len); + ret = ccp_set_dm_area(&wa->key, dm_offset, aes->key, 0, aes->key_len); if (ret) goto e_key; - ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key, + ret = ccp_copy_to_sb(cmd_q, &wa->key, wa->op.jobid, wa->op.sb_key, CCP_PASSTHRU_BYTESWAP_256BIT); if (ret) { cmd->engine_error = cmd_q->cmd_error; @@ -726,58 +735,58 @@ ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) * There is an assumption here that the IV is 96 bits in length, plus * a nonce of 32 bits. If no IV is present, use a zeroed buffer. */ - ret = ccp_init_dm_workarea(&ctx, cmd_q, + ret = ccp_init_dm_workarea(&wa->ctx, cmd_q, CCP_AES_CTX_SB_COUNT * CCP_SB_BYTES, DMA_BIDIRECTIONAL); if (ret) goto e_key; dm_offset = CCP_AES_CTX_SB_COUNT * CCP_SB_BYTES - aes->iv_len; - ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len); + ret = ccp_set_dm_area(&wa->ctx, dm_offset, aes->iv, 0, aes->iv_len); if (ret) goto e_ctx; - ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx, + ret = ccp_copy_to_sb(cmd_q, &wa->ctx, wa->op.jobid, wa->op.sb_ctx, CCP_PASSTHRU_BYTESWAP_256BIT); if (ret) { cmd->engine_error = cmd_q->cmd_error; goto e_ctx; } - op.init = 1; + wa->op.init = 1; if (aes->aad_len > 0) { /* Step 1: Run a GHASH over the Additional Authenticated Data */ - ret = ccp_init_data(&aad, cmd_q, p_aad, aes->aad_len, + ret = ccp_init_data(&wa->aad, cmd_q, p_aad, aes->aad_len, AES_BLOCK_SIZE, DMA_TO_DEVICE); if (ret) goto e_ctx; - op.u.aes.mode = CCP_AES_MODE_GHASH; - op.u.aes.action = CCP_AES_GHASHAAD; + wa->op.u.aes.mode = CCP_AES_MODE_GHASH; + wa->op.u.aes.action = CCP_AES_GHASHAAD; - while (aad.sg_wa.bytes_left) { - ccp_prepare_data(&aad, NULL, &op, AES_BLOCK_SIZE, true); + while (wa->aad.sg_wa.bytes_left) { + ccp_prepare_data(&wa->aad, NULL, &wa->op, AES_BLOCK_SIZE, true); - ret = cmd_q->ccp->vdata->perform->aes(&op); + ret = cmd_q->ccp->vdata->perform->aes(&wa->op); if (ret) { cmd->engine_error = cmd_q->cmd_error; goto e_aad; } - ccp_process_data(&aad, NULL, &op); - op.init = 0; + ccp_process_data(&wa->aad, NULL, &wa->op); + wa->op.init = 0; } } - op.u.aes.mode = CCP_AES_MODE_GCTR; - op.u.aes.action = aes->action; + wa->op.u.aes.mode = CCP_AES_MODE_GCTR; + wa->op.u.aes.action = aes->action; if (ilen > 0) { /* Step 2: Run a GCTR over the plaintext */ in_place = (sg_virt(p_inp) == sg_virt(p_outp)) ? true : false; - ret = ccp_init_data(&src, cmd_q, p_inp, ilen, + ret = ccp_init_data(&wa->src, cmd_q, p_inp, ilen, AES_BLOCK_SIZE, in_place ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE); @@ -785,52 +794,52 @@ ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) goto e_aad; if (in_place) { - dst = src; + wa->dst = wa->src; } else { - ret = ccp_init_data(&dst, cmd_q, p_outp, ilen, + ret = ccp_init_data(&wa->dst, cmd_q, p_outp, ilen, AES_BLOCK_SIZE, DMA_FROM_DEVICE); if (ret) goto e_src; } - op.soc = 0; - op.eom = 0; - op.init = 1; - while (src.sg_wa.bytes_left) { - ccp_prepare_data(&src, &dst, &op, AES_BLOCK_SIZE, true); - if (!src.sg_wa.bytes_left) { + wa->op.soc = 0; + wa->op.eom = 0; + wa->op.init = 1; + while (wa->src.sg_wa.bytes_left) { + ccp_prepare_data(&wa->src, &wa->dst, &wa->op, AES_BLOCK_SIZE, true); + if (!wa->src.sg_wa.bytes_left) { unsigned int nbytes = ilen % AES_BLOCK_SIZE; if (nbytes) { - op.eom = 1; - op.u.aes.size = (nbytes * 8) - 1; + wa->op.eom = 1; + wa->op.u.aes.size = (nbytes * 8) - 1; } } - ret = cmd_q->ccp->vdata->perform->aes(&op); + ret = cmd_q->ccp->vdata->perform->aes(&wa->op); if (ret) { cmd->engine_error = cmd_q->cmd_error; goto e_dst; } - ccp_process_data(&src, &dst, &op); - op.init = 0; + ccp_process_data(&wa->src, &wa->dst, &wa->op); + wa->op.init = 0; } } /* Step 3: Update the IV portion of the context with the original IV */ - ret = ccp_copy_from_sb(cmd_q, &ctx, op.jobid, op.sb_ctx, + ret = ccp_copy_from_sb(cmd_q, &wa->ctx, wa->op.jobid, wa->op.sb_ctx, CCP_PASSTHRU_BYTESWAP_256BIT); if (ret) { cmd->engine_error = cmd_q->cmd_error; goto e_dst; } - ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len); + ret = ccp_set_dm_area(&wa->ctx, dm_offset, aes->iv, 0, aes->iv_len); if (ret) goto e_dst; - ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx, + ret = ccp_copy_to_sb(cmd_q, &wa->ctx, wa->op.jobid, wa->op.sb_ctx, CCP_PASSTHRU_BYTESWAP_256BIT); if (ret) { cmd->engine_error = cmd_q->cmd_error; @@ -840,75 +849,75 @@ ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) /* Step 4: Concatenate the lengths of the AAD and source, and * hash that 16 byte buffer. */ - ret = ccp_init_dm_workarea(&final_wa, cmd_q, AES_BLOCK_SIZE, + ret = ccp_init_dm_workarea(&wa->final, cmd_q, AES_BLOCK_SIZE, DMA_BIDIRECTIONAL); if (ret) goto e_dst; - final = (__be64 *)final_wa.address; + final = (__be64 *)wa->final.address; final[0] = cpu_to_be64(aes->aad_len * 8); final[1] = cpu_to_be64(ilen * 8); - memset(&op, 0, sizeof(op)); - op.cmd_q = cmd_q; - op.jobid = jobid; - op.sb_key = cmd_q->sb_key; /* Pre-allocated */ - op.sb_ctx = cmd_q->sb_ctx; /* Pre-allocated */ - op.init = 1; - op.u.aes.type = aes->type; - op.u.aes.mode = CCP_AES_MODE_GHASH; - op.u.aes.action = CCP_AES_GHASHFINAL; - op.src.type = CCP_MEMTYPE_SYSTEM; - op.src.u.dma.address = final_wa.dma.address; - op.src.u.dma.length = AES_BLOCK_SIZE; - op.dst.type = CCP_MEMTYPE_SYSTEM; - op.dst.u.dma.address = final_wa.dma.address; - op.dst.u.dma.length = AES_BLOCK_SIZE; - op.eom = 1; - op.u.aes.size = 0; - ret = cmd_q->ccp->vdata->perform->aes(&op); + memset(&wa->op, 0, sizeof(wa->op)); + wa->op.cmd_q = cmd_q; + wa->op.jobid = jobid; + wa->op.sb_key = cmd_q->sb_key; /* Pre-allocated */ + wa->op.sb_ctx = cmd_q->sb_ctx; /* Pre-allocated */ + wa->op.init = 1; + wa->op.u.aes.type = aes->type; + wa->op.u.aes.mode = CCP_AES_MODE_GHASH; + wa->op.u.aes.action = CCP_AES_GHASHFINAL; + wa->op.src.type = CCP_MEMTYPE_SYSTEM; + wa->op.src.u.dma.address = wa->final.dma.address; + wa->op.src.u.dma.length = AES_BLOCK_SIZE; + wa->op.dst.type = CCP_MEMTYPE_SYSTEM; + wa->op.dst.u.dma.address = wa->final.dma.address; + wa->op.dst.u.dma.length = AES_BLOCK_SIZE; + wa->op.eom = 1; + wa->op.u.aes.size = 0; + ret = cmd_q->ccp->vdata->perform->aes(&wa->op); if (ret) goto e_final_wa; if (aes->action == CCP_AES_ACTION_ENCRYPT) { /* Put the ciphered tag after the ciphertext. */ - ccp_get_dm_area(&final_wa, 0, p_tag, 0, authsize); + ccp_get_dm_area(&wa->final, 0, p_tag, 0, authsize); } else { /* Does this ciphered tag match the input? */ - ret = ccp_init_dm_workarea(&tag, cmd_q, authsize, + ret = ccp_init_dm_workarea(&wa->tag, cmd_q, authsize, DMA_BIDIRECTIONAL); if (ret) goto e_final_wa; - ret = ccp_set_dm_area(&tag, 0, p_tag, 0, authsize); + ret = ccp_set_dm_area(&wa->tag, 0, p_tag, 0, authsize); if (ret) { - ccp_dm_free(&tag); + ccp_dm_free(&wa->tag); goto e_final_wa; } - ret = crypto_memneq(tag.address, final_wa.address, + ret = crypto_memneq(wa->tag.address, wa->final.address, authsize) ? -EBADMSG : 0; - ccp_dm_free(&tag); + ccp_dm_free(&wa->tag); } e_final_wa: - ccp_dm_free(&final_wa); + ccp_dm_free(&wa->final); e_dst: if (ilen > 0 && !in_place) - ccp_free_data(&dst, cmd_q); + ccp_free_data(&wa->dst, cmd_q); e_src: if (ilen > 0) - ccp_free_data(&src, cmd_q); + ccp_free_data(&wa->src, cmd_q); e_aad: if (aes->aad_len) - ccp_free_data(&aad, cmd_q); + ccp_free_data(&wa->aad, cmd_q); e_ctx: - ccp_dm_free(&ctx); + ccp_dm_free(&wa->ctx); e_key: - ccp_dm_free(&key); + ccp_dm_free(&wa->key); return ret; } diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 3451bada884e..e058ba027792 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, boo return rc; } -static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order) +static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order, bool locked) { unsigned long npages = 1ul << order, paddr; struct sev_device *sev; @@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order) return page; paddr = __pa((unsigned long)page_address(page)); - if (rmp_mark_pages_firmware(paddr, npages, false)) + if (rmp_mark_pages_firmware(paddr, npages, locked)) return NULL; return page; @@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) { struct page *page; - page = __snp_alloc_firmware_pages(gfp_mask, 0); + page = __snp_alloc_firmware_pages(gfp_mask, 0, false); return page ? page_address(page) : NULL; } @@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) { struct page *page; - page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len)); + page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len), true); if (!page) return NULL; @@ -1276,9 +1276,11 @@ static int __sev_platform_init_handle_init_ex_path(struct sev_device *sev) static int __sev_platform_init_locked(int *error) { - int rc, psp_ret = SEV_RET_NO_FW_CALL; + int rc, psp_ret, dfflush_error; struct sev_device *sev; + psp_ret = dfflush_error = SEV_RET_NO_FW_CALL; + if (!psp_master || !psp_master->sev_data) return -ENODEV; @@ -1320,10 +1322,10 @@ static int __sev_platform_init_locked(int *error) /* Prepare for first SEV guest launch after INIT */ wbinvd_on_all_cpus(); - rc = __sev_do_cmd_locked(SEV_CMD_DF_FLUSH, NULL, error); + rc = __sev_do_cmd_locked(SEV_CMD_DF_FLUSH, NULL, &dfflush_error); if (rc) { dev_err(sev->dev, "SEV: DF_FLUSH failed %#x, rc %d\n", - *error, rc); + dfflush_error, rc); return rc; } @@ -1785,8 +1787,14 @@ static int __sev_snp_shutdown_locked(int *error, bool panic) sev->snp_initialized = false; dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n"); - atomic_notifier_chain_unregister(&panic_notifier_list, - &snp_panic_notifier); + /* + * __sev_snp_shutdown_locked() deadlocks when it tries to unregister + * itself during panic as the panic notifier is called with RCU read + * lock held and notifier unregistration does RCU synchronization. + */ + if (!panic) + atomic_notifier_chain_unregister(&panic_notifier_list, + &snp_panic_notifier); /* Reset TMR size back to default */ sev_es_tmr_size = SEV_TMR_SIZE; diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c index e1be2072d680..e7bb803912a6 100644 --- a/drivers/crypto/ccp/sp-pci.c +++ b/drivers/crypto/ccp/sp-pci.c @@ -453,6 +453,7 @@ static const struct psp_vdata pspv6 = { .cmdresp_reg = 0x10944, /* C2PMSG_17 */ .cmdbuff_addr_lo_reg = 0x10948, /* C2PMSG_18 */ .cmdbuff_addr_hi_reg = 0x1094c, /* C2PMSG_19 */ + .bootloader_info_reg = 0x109ec, /* C2PMSG_59 */ .feature_reg = 0x109fc, /* C2PMSG_63 */ .inten_reg = 0x10510, /* P2CMSG_INTEN */ .intsts_reg = 0x10514, /* P2CMSG_INTSTS */ diff --git a/drivers/crypto/ccree/cc_buffer_mgr.c b/drivers/crypto/ccree/cc_buffer_mgr.c index bcca55bff910..3963bb91321f 100644 --- a/drivers/crypto/ccree/cc_buffer_mgr.c +++ b/drivers/crypto/ccree/cc_buffer_mgr.c @@ -224,7 +224,7 @@ static int cc_generate_mlli(struct device *dev, struct buffer_array *sg_data, /* Set MLLI size for the bypass operation */ mlli_params->mlli_len = (total_nents * LLI_ENTRY_BYTE_SIZE); - dev_dbg(dev, "MLLI params: virt_addr=%pK dma_addr=%pad mlli_len=0x%X\n", + dev_dbg(dev, "MLLI params: virt_addr=%p dma_addr=%pad mlli_len=0x%X\n", mlli_params->mlli_virt_addr, &mlli_params->mlli_dma_addr, mlli_params->mlli_len); @@ -239,7 +239,7 @@ static void cc_add_sg_entry(struct device *dev, struct buffer_array *sgl_data, { unsigned int index = sgl_data->num_of_buffers; - dev_dbg(dev, "index=%u nents=%u sgl=%pK data_len=0x%08X is_last=%d\n", + dev_dbg(dev, "index=%u nents=%u sgl=%p data_len=0x%08X is_last=%d\n", index, nents, sgl, data_len, is_last_table); sgl_data->nents[index] = nents; sgl_data->entry[index].sgl = sgl; @@ -298,7 +298,7 @@ cc_set_aead_conf_buf(struct device *dev, struct aead_req_ctx *areq_ctx, dev_err(dev, "dma_map_sg() config buffer failed\n"); return -ENOMEM; } - dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n", + dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%p offset=%u length=%u\n", &sg_dma_address(&areq_ctx->ccm_adata_sg), sg_page(&areq_ctx->ccm_adata_sg), sg_virt(&areq_ctx->ccm_adata_sg), @@ -323,7 +323,7 @@ static int cc_set_hash_buf(struct device *dev, struct ahash_req_ctx *areq_ctx, dev_err(dev, "dma_map_sg() src buffer failed\n"); return -ENOMEM; } - dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n", + dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%p offset=%u length=%u\n", &sg_dma_address(areq_ctx->buff_sg), sg_page(areq_ctx->buff_sg), sg_virt(areq_ctx->buff_sg), areq_ctx->buff_sg->offset, areq_ctx->buff_sg->length); @@ -359,11 +359,11 @@ void cc_unmap_cipher_request(struct device *dev, void *ctx, if (src != dst) { dma_unmap_sg(dev, src, req_ctx->in_nents, DMA_TO_DEVICE); dma_unmap_sg(dev, dst, req_ctx->out_nents, DMA_FROM_DEVICE); - dev_dbg(dev, "Unmapped req->dst=%pK\n", sg_virt(dst)); - dev_dbg(dev, "Unmapped req->src=%pK\n", sg_virt(src)); + dev_dbg(dev, "Unmapped req->dst=%p\n", sg_virt(dst)); + dev_dbg(dev, "Unmapped req->src=%p\n", sg_virt(src)); } else { dma_unmap_sg(dev, src, req_ctx->in_nents, DMA_BIDIRECTIONAL); - dev_dbg(dev, "Unmapped req->src=%pK\n", sg_virt(src)); + dev_dbg(dev, "Unmapped req->src=%p\n", sg_virt(src)); } } @@ -391,11 +391,11 @@ int cc_map_cipher_request(struct cc_drvdata *drvdata, void *ctx, req_ctx->gen_ctx.iv_dma_addr = dma_map_single(dev, info, ivsize, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, req_ctx->gen_ctx.iv_dma_addr)) { - dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping iv %u B at va=%p for DMA failed\n", ivsize, info); return -ENOMEM; } - dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped iv %u B at va=%p to dma=%pad\n", ivsize, info, &req_ctx->gen_ctx.iv_dma_addr); } else { req_ctx->gen_ctx.iv_dma_addr = 0; @@ -506,7 +506,7 @@ void cc_unmap_aead_request(struct device *dev, struct aead_request *req) if ((areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI || areq_ctx->data_buff_type == CC_DMA_BUF_MLLI) && (areq_ctx->mlli_params.mlli_virt_addr)) { - dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n", + dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%p\n", &areq_ctx->mlli_params.mlli_dma_addr, areq_ctx->mlli_params.mlli_virt_addr); dma_pool_free(areq_ctx->mlli_params.curr_pool, @@ -514,13 +514,13 @@ void cc_unmap_aead_request(struct device *dev, struct aead_request *req) areq_ctx->mlli_params.mlli_dma_addr); } - dev_dbg(dev, "Unmapping src sgl: req->src=%pK areq_ctx->src.nents=%u areq_ctx->assoc.nents=%u assoclen:%u cryptlen=%u\n", + dev_dbg(dev, "Unmapping src sgl: req->src=%p areq_ctx->src.nents=%u areq_ctx->assoc.nents=%u assoclen:%u cryptlen=%u\n", sg_virt(req->src), areq_ctx->src.nents, areq_ctx->assoc.nents, areq_ctx->assoclen, req->cryptlen); dma_unmap_sg(dev, req->src, areq_ctx->src.mapped_nents, src_direction); if (req->src != req->dst) { - dev_dbg(dev, "Unmapping dst sgl: req->dst=%pK\n", + dev_dbg(dev, "Unmapping dst sgl: req->dst=%p\n", sg_virt(req->dst)); dma_unmap_sg(dev, req->dst, areq_ctx->dst.mapped_nents, DMA_FROM_DEVICE); } @@ -566,7 +566,7 @@ static int cc_aead_chain_iv(struct cc_drvdata *drvdata, dma_map_single(dev, areq_ctx->gen_ctx.iv, hw_iv_size, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, areq_ctx->gen_ctx.iv_dma_addr)) { - dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping iv %u B at va=%p for DMA failed\n", hw_iv_size, req->iv); kfree_sensitive(areq_ctx->gen_ctx.iv); areq_ctx->gen_ctx.iv = NULL; @@ -574,7 +574,7 @@ static int cc_aead_chain_iv(struct cc_drvdata *drvdata, goto chain_iv_exit; } - dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped iv %u B at va=%p to dma=%pad\n", hw_iv_size, req->iv, &areq_ctx->gen_ctx.iv_dma_addr); chain_iv_exit: @@ -977,7 +977,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) dma_addr = dma_map_single(dev, areq_ctx->mac_buf, MAX_MAC_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping mac_buf %u B at va=%p for DMA failed\n", MAX_MAC_SIZE, areq_ctx->mac_buf); rc = -ENOMEM; goto aead_map_failure; @@ -991,7 +991,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping mac_buf %u B at va=%p for DMA failed\n", AES_BLOCK_SIZE, addr); areq_ctx->ccm_iv0_dma_addr = 0; rc = -ENOMEM; @@ -1009,7 +1009,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) dma_addr = dma_map_single(dev, areq_ctx->hkey, AES_BLOCK_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping hkey %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping hkey %u B at va=%p for DMA failed\n", AES_BLOCK_SIZE, areq_ctx->hkey); rc = -ENOMEM; goto aead_map_failure; @@ -1019,7 +1019,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) dma_addr = dma_map_single(dev, &areq_ctx->gcm_len_block, AES_BLOCK_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping gcm_len_block %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping gcm_len_block %u B at va=%p for DMA failed\n", AES_BLOCK_SIZE, &areq_ctx->gcm_len_block); rc = -ENOMEM; goto aead_map_failure; @@ -1030,7 +1030,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) AES_BLOCK_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping gcm_iv_inc1 %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping gcm_iv_inc1 %u B at va=%p for DMA failed\n", AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc1)); areq_ctx->gcm_iv_inc1_dma_addr = 0; rc = -ENOMEM; @@ -1042,7 +1042,7 @@ int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req) AES_BLOCK_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "Mapping gcm_iv_inc2 %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping gcm_iv_inc2 %u B at va=%p for DMA failed\n", AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc2)); areq_ctx->gcm_iv_inc2_dma_addr = 0; rc = -ENOMEM; @@ -1152,7 +1152,7 @@ int cc_map_hash_request_final(struct cc_drvdata *drvdata, void *ctx, u32 dummy = 0; u32 mapped_nents = 0; - dev_dbg(dev, "final params : curr_buff=%pK curr_buff_cnt=0x%X nbytes = 0x%X src=%pK curr_index=%u\n", + dev_dbg(dev, "final params : curr_buff=%p curr_buff_cnt=0x%X nbytes = 0x%X src=%p curr_index=%u\n", curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index); /* Init the type of the dma buffer */ areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL; @@ -1236,7 +1236,7 @@ int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx, u32 dummy = 0; u32 mapped_nents = 0; - dev_dbg(dev, " update params : curr_buff=%pK curr_buff_cnt=0x%X nbytes=0x%X src=%pK curr_index=%u\n", + dev_dbg(dev, " update params : curr_buff=%p curr_buff_cnt=0x%X nbytes=0x%X src=%p curr_index=%u\n", curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index); /* Init the type of the dma buffer */ areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL; @@ -1246,7 +1246,7 @@ int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx, areq_ctx->in_nents = 0; if (total_in_len < block_size) { - dev_dbg(dev, " less than one block: curr_buff=%pK *curr_buff_cnt=0x%X copy_to=%pK\n", + dev_dbg(dev, " less than one block: curr_buff=%p *curr_buff_cnt=0x%X copy_to=%p\n", curr_buff, *curr_buff_cnt, &curr_buff[*curr_buff_cnt]); areq_ctx->in_nents = sg_nents_for_len(src, nbytes); sg_copy_to_buffer(src, areq_ctx->in_nents, @@ -1265,7 +1265,7 @@ int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx, /* Copy the new residue to next buffer */ if (*next_buff_cnt) { - dev_dbg(dev, " handle residue: next buff %pK skip data %u residue %u\n", + dev_dbg(dev, " handle residue: next buff %p skip data %u residue %u\n", next_buff, (update_data_len - *curr_buff_cnt), *next_buff_cnt); cc_copy_sg_portion(dev, next_buff, src, @@ -1338,7 +1338,7 @@ void cc_unmap_hash_request(struct device *dev, void *ctx, *allocated and should be released */ if (areq_ctx->mlli_params.curr_pool) { - dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n", + dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%p\n", &areq_ctx->mlli_params.mlli_dma_addr, areq_ctx->mlli_params.mlli_virt_addr); dma_pool_free(areq_ctx->mlli_params.curr_pool, @@ -1347,14 +1347,14 @@ void cc_unmap_hash_request(struct device *dev, void *ctx, } if (src && areq_ctx->in_nents) { - dev_dbg(dev, "Unmapped sg src: virt=%pK dma=%pad len=0x%X\n", + dev_dbg(dev, "Unmapped sg src: virt=%p dma=%pad len=0x%X\n", sg_virt(src), &sg_dma_address(src), sg_dma_len(src)); dma_unmap_sg(dev, src, areq_ctx->in_nents, DMA_TO_DEVICE); } if (*prev_len) { - dev_dbg(dev, "Unmapped buffer: areq_ctx->buff_sg=%pK dma=%pad len 0x%X\n", + dev_dbg(dev, "Unmapped buffer: areq_ctx->buff_sg=%p dma=%pad len 0x%X\n", sg_virt(areq_ctx->buff_sg), &sg_dma_address(areq_ctx->buff_sg), sg_dma_len(areq_ctx->buff_sg)); diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c index d39c067672fd..e2cbfdf7a0e4 100644 --- a/drivers/crypto/ccree/cc_cipher.c +++ b/drivers/crypto/ccree/cc_cipher.c @@ -211,11 +211,11 @@ static int cc_cipher_init(struct crypto_tfm *tfm) max_key_buf_size, DMA_TO_DEVICE); if (dma_mapping_error(dev, ctx_p->user.key_dma_addr)) { - dev_err(dev, "Mapping Key %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping Key %u B at va=%p for DMA failed\n", max_key_buf_size, ctx_p->user.key); goto free_key; } - dev_dbg(dev, "Mapped key %u B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped key %u B at va=%p to dma=%pad\n", max_key_buf_size, ctx_p->user.key, &ctx_p->user.key_dma_addr); return 0; diff --git a/drivers/crypto/ccree/cc_hash.c b/drivers/crypto/ccree/cc_hash.c index d0612bec4d58..c6d085c8ff79 100644 --- a/drivers/crypto/ccree/cc_hash.c +++ b/drivers/crypto/ccree/cc_hash.c @@ -125,7 +125,7 @@ static int cc_map_result(struct device *dev, struct ahash_req_ctx *state, digestsize); return -ENOMEM; } - dev_dbg(dev, "Mapped digest result buffer %u B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped digest result buffer %u B at va=%p to dma=%pad\n", digestsize, state->digest_result_buff, &state->digest_result_dma_addr); @@ -184,11 +184,11 @@ static int cc_map_req(struct device *dev, struct ahash_req_ctx *state, dma_map_single(dev, state->digest_buff, ctx->inter_digestsize, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, state->digest_buff_dma_addr)) { - dev_err(dev, "Mapping digest len %d B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping digest len %d B at va=%p for DMA failed\n", ctx->inter_digestsize, state->digest_buff); return -EINVAL; } - dev_dbg(dev, "Mapped digest %d B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped digest %d B at va=%p to dma=%pad\n", ctx->inter_digestsize, state->digest_buff, &state->digest_buff_dma_addr); @@ -197,11 +197,11 @@ static int cc_map_req(struct device *dev, struct ahash_req_ctx *state, dma_map_single(dev, state->digest_bytes_len, HASH_MAX_LEN_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, state->digest_bytes_len_dma_addr)) { - dev_err(dev, "Mapping digest len %u B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping digest len %u B at va=%p for DMA failed\n", HASH_MAX_LEN_SIZE, state->digest_bytes_len); goto unmap_digest_buf; } - dev_dbg(dev, "Mapped digest len %u B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped digest len %u B at va=%p to dma=%pad\n", HASH_MAX_LEN_SIZE, state->digest_bytes_len, &state->digest_bytes_len_dma_addr); } @@ -212,12 +212,12 @@ static int cc_map_req(struct device *dev, struct ahash_req_ctx *state, ctx->inter_digestsize, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, state->opad_digest_dma_addr)) { - dev_err(dev, "Mapping opad digest %d B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping opad digest %d B at va=%p for DMA failed\n", ctx->inter_digestsize, state->opad_digest_buff); goto unmap_digest_len; } - dev_dbg(dev, "Mapped opad digest %d B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped opad digest %d B at va=%p to dma=%pad\n", ctx->inter_digestsize, state->opad_digest_buff, &state->opad_digest_dma_addr); } @@ -272,7 +272,7 @@ static void cc_unmap_result(struct device *dev, struct ahash_req_ctx *state, if (state->digest_result_dma_addr) { dma_unmap_single(dev, state->digest_result_dma_addr, digestsize, DMA_BIDIRECTIONAL); - dev_dbg(dev, "unmpa digest result buffer va (%pK) pa (%pad) len %u\n", + dev_dbg(dev, "unmpa digest result buffer va (%p) pa (%pad) len %u\n", state->digest_result_buff, &state->digest_result_dma_addr, digestsize); memcpy(result, state->digest_result_buff, digestsize); @@ -287,7 +287,7 @@ static void cc_update_complete(struct device *dev, void *cc_req, int err) struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct cc_hash_ctx *ctx = crypto_ahash_ctx_dma(tfm); - dev_dbg(dev, "req=%pK\n", req); + dev_dbg(dev, "req=%p\n", req); if (err != -EINPROGRESS) { /* Not a BACKLOG notification */ @@ -306,7 +306,7 @@ static void cc_digest_complete(struct device *dev, void *cc_req, int err) struct cc_hash_ctx *ctx = crypto_ahash_ctx_dma(tfm); u32 digestsize = crypto_ahash_digestsize(tfm); - dev_dbg(dev, "req=%pK\n", req); + dev_dbg(dev, "req=%p\n", req); if (err != -EINPROGRESS) { /* Not a BACKLOG notification */ @@ -326,7 +326,7 @@ static void cc_hash_complete(struct device *dev, void *cc_req, int err) struct cc_hash_ctx *ctx = crypto_ahash_ctx_dma(tfm); u32 digestsize = crypto_ahash_digestsize(tfm); - dev_dbg(dev, "req=%pK\n", req); + dev_dbg(dev, "req=%p\n", req); if (err != -EINPROGRESS) { /* Not a BACKLOG notification */ @@ -1077,11 +1077,11 @@ static int cc_alloc_ctx(struct cc_hash_ctx *ctx) dma_map_single(dev, ctx->digest_buff, sizeof(ctx->digest_buff), DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, ctx->digest_buff_dma_addr)) { - dev_err(dev, "Mapping digest len %zu B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping digest len %zu B at va=%p for DMA failed\n", sizeof(ctx->digest_buff), ctx->digest_buff); goto fail; } - dev_dbg(dev, "Mapped digest %zu B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped digest %zu B at va=%p to dma=%pad\n", sizeof(ctx->digest_buff), ctx->digest_buff, &ctx->digest_buff_dma_addr); @@ -1090,12 +1090,12 @@ static int cc_alloc_ctx(struct cc_hash_ctx *ctx) sizeof(ctx->opad_tmp_keys_buff), DMA_BIDIRECTIONAL); if (dma_mapping_error(dev, ctx->opad_tmp_keys_dma_addr)) { - dev_err(dev, "Mapping opad digest %zu B at va=%pK for DMA failed\n", + dev_err(dev, "Mapping opad digest %zu B at va=%p for DMA failed\n", sizeof(ctx->opad_tmp_keys_buff), ctx->opad_tmp_keys_buff); goto fail; } - dev_dbg(dev, "Mapped opad_tmp_keys %zu B at va=%pK to dma=%pad\n", + dev_dbg(dev, "Mapped opad_tmp_keys %zu B at va=%p to dma=%pad\n", sizeof(ctx->opad_tmp_keys_buff), ctx->opad_tmp_keys_buff, &ctx->opad_tmp_keys_dma_addr); diff --git a/drivers/crypto/ccree/cc_pm.c b/drivers/crypto/ccree/cc_pm.c index 6124fbbbed94..bbd118f8de0e 100644 --- a/drivers/crypto/ccree/cc_pm.c +++ b/drivers/crypto/ccree/cc_pm.c @@ -77,6 +77,5 @@ int cc_pm_get(struct device *dev) void cc_pm_put_suspend(struct device *dev) { - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c index af37477ffd8d..be21e4e2016c 100644 --- a/drivers/crypto/chelsio/chcr_algo.c +++ b/drivers/crypto/chelsio/chcr_algo.c @@ -314,30 +314,30 @@ static int chcr_compute_partial_hash(struct shash_desc *desc, if (digest_size == SHA1_DIGEST_SIZE) { error = crypto_shash_init(desc) ?: crypto_shash_update(desc, iopad, SHA1_BLOCK_SIZE) ?: - crypto_shash_export(desc, (void *)&sha1_st); + crypto_shash_export_core(desc, &sha1_st); memcpy(result_hash, sha1_st.state, SHA1_DIGEST_SIZE); } else if (digest_size == SHA224_DIGEST_SIZE) { error = crypto_shash_init(desc) ?: crypto_shash_update(desc, iopad, SHA256_BLOCK_SIZE) ?: - crypto_shash_export(desc, (void *)&sha256_st); + crypto_shash_export_core(desc, &sha256_st); memcpy(result_hash, sha256_st.state, SHA256_DIGEST_SIZE); } else if (digest_size == SHA256_DIGEST_SIZE) { error = crypto_shash_init(desc) ?: crypto_shash_update(desc, iopad, SHA256_BLOCK_SIZE) ?: - crypto_shash_export(desc, (void *)&sha256_st); + crypto_shash_export_core(desc, &sha256_st); memcpy(result_hash, sha256_st.state, SHA256_DIGEST_SIZE); } else if (digest_size == SHA384_DIGEST_SIZE) { error = crypto_shash_init(desc) ?: crypto_shash_update(desc, iopad, SHA512_BLOCK_SIZE) ?: - crypto_shash_export(desc, (void *)&sha512_st); + crypto_shash_export_core(desc, &sha512_st); memcpy(result_hash, sha512_st.state, SHA512_DIGEST_SIZE); } else if (digest_size == SHA512_DIGEST_SIZE) { error = crypto_shash_init(desc) ?: crypto_shash_update(desc, iopad, SHA512_BLOCK_SIZE) ?: - crypto_shash_export(desc, (void *)&sha512_st); + crypto_shash_export_core(desc, &sha512_st); memcpy(result_hash, sha512_st.state, SHA512_DIGEST_SIZE); } else { error = -EINVAL; diff --git a/drivers/crypto/hisilicon/hpre/hpre_crypto.c b/drivers/crypto/hisilicon/hpre/hpre_crypto.c index 61b5e1c5d019..1550c3818383 100644 --- a/drivers/crypto/hisilicon/hpre/hpre_crypto.c +++ b/drivers/crypto/hisilicon/hpre/hpre_crypto.c @@ -1491,11 +1491,13 @@ static void hpre_ecdh_cb(struct hpre_ctx *ctx, void *resp) if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld)) atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value); + /* Do unmap before data processing */ + hpre_ecdh_hw_data_clr_all(ctx, req, areq->dst, areq->src); + p = sg_virt(areq->dst); memmove(p, p + ctx->key_sz - curve_sz, curve_sz); memmove(p + curve_sz, p + areq->dst_len - curve_sz, curve_sz); - hpre_ecdh_hw_data_clr_all(ctx, req, areq->dst, areq->src); kpp_request_complete(areq, ret); atomic64_inc(&dfx[HPRE_RECV_CNT].value); @@ -1808,9 +1810,11 @@ static void hpre_curve25519_cb(struct hpre_ctx *ctx, void *resp) if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld)) atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value); + /* Do unmap before data processing */ + hpre_curve25519_hw_data_clr_all(ctx, req, areq->dst, areq->src); + hpre_key_to_big_end(sg_virt(areq->dst), CURVE25519_KEY_SIZE); - hpre_curve25519_hw_data_clr_all(ctx, req, areq->dst, areq->src); kpp_request_complete(areq, ret); atomic64_inc(&dfx[HPRE_RECV_CNT].value); diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 7c41f9593d03..2e4ee7ecfdfb 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -912,7 +912,6 @@ static void qm_pm_put_sync(struct hisi_qm *qm) if (!test_bit(QM_SUPPORT_RPM, &qm->caps)) return; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } diff --git a/drivers/crypto/hisilicon/sec2/sec.h b/drivers/crypto/hisilicon/sec2/sec.h index 703920b49c7c..81d0beda93b2 100644 --- a/drivers/crypto/hisilicon/sec2/sec.h +++ b/drivers/crypto/hisilicon/sec2/sec.h @@ -7,6 +7,12 @@ #include #include "sec_crypto.h" +#define SEC_PBUF_SZ 512 +#define SEC_MAX_MAC_LEN 64 +#define SEC_IV_SIZE 24 +#define SEC_SGE_NR_NUM 4 +#define SEC_SGL_ALIGN_SIZE 64 + /* Algorithm resource per hardware SEC queue */ struct sec_alg_res { u8 *pbuf; @@ -20,6 +26,40 @@ struct sec_alg_res { u16 depth; }; +struct sec_hw_sge { + dma_addr_t buf; + void *page_ctrl; + __le32 len; + __le32 pad; + __le32 pad0; + __le32 pad1; +}; + +struct sec_hw_sgl { + dma_addr_t next_dma; + __le16 entry_sum_in_chain; + __le16 entry_sum_in_sgl; + __le16 entry_length_in_sgl; + __le16 pad0; + __le64 pad1[5]; + struct sec_hw_sgl *next; + struct sec_hw_sge sge_entries[SEC_SGE_NR_NUM]; +} __aligned(SEC_SGL_ALIGN_SIZE); + +struct sec_src_dst_buf { + struct sec_hw_sgl in; + struct sec_hw_sgl out; +}; + +struct sec_request_buf { + union { + struct sec_src_dst_buf data_buf; + __u8 pbuf[SEC_PBUF_SZ]; + }; + dma_addr_t in_dma; + dma_addr_t out_dma; +}; + /* Cipher request of SEC private */ struct sec_cipher_req { struct hisi_acc_hw_sgl *c_out; @@ -29,6 +69,7 @@ struct sec_cipher_req { struct skcipher_request *sk_req; u32 c_len; bool encrypt; + __u8 c_ivin_buf[SEC_IV_SIZE]; }; struct sec_aead_req { @@ -37,6 +78,13 @@ struct sec_aead_req { u8 *a_ivin; dma_addr_t a_ivin_dma; struct aead_request *aead_req; + __u8 a_ivin_buf[SEC_IV_SIZE]; + __u8 out_mac_buf[SEC_MAX_MAC_LEN]; +}; + +struct sec_instance_backlog { + struct list_head list; + spinlock_t lock; }; /* SEC request of Crypto */ @@ -55,15 +103,17 @@ struct sec_req { dma_addr_t in_dma; struct sec_cipher_req c_req; struct sec_aead_req aead_req; - struct list_head backlog_head; + struct crypto_async_request *base; int err_type; int req_id; u32 flag; - /* Status of the SEC request */ - bool fake_busy; bool use_pbuf; + + struct list_head list; + struct sec_instance_backlog *backlog; + struct sec_request_buf buf; }; /** @@ -119,9 +169,11 @@ struct sec_qp_ctx { struct sec_alg_res *res; struct sec_ctx *ctx; spinlock_t req_lock; - struct list_head backlog; + spinlock_t id_lock; struct hisi_acc_sgl_pool *c_in_pool; struct hisi_acc_sgl_pool *c_out_pool; + struct sec_instance_backlog backlog; + u16 send_head; }; enum sec_alg_type { @@ -139,9 +191,6 @@ struct sec_ctx { /* Half queues for encipher, and half for decipher */ u32 hlf_q_num; - /* Threshold for fake busy, trigger to return -EBUSY to user */ - u32 fake_req_limit; - /* Current cyclic index to select a queue for encipher */ atomic_t enc_qcyclic; diff --git a/drivers/crypto/hisilicon/sec2/sec_crypto.c b/drivers/crypto/hisilicon/sec2/sec_crypto.c index 8ea5305bc320..d044ded0f290 100644 --- a/drivers/crypto/hisilicon/sec2/sec_crypto.c +++ b/drivers/crypto/hisilicon/sec2/sec_crypto.c @@ -67,7 +67,6 @@ #define SEC_MAX_CCM_AAD_LEN 65279 #define SEC_TOTAL_MAC_SZ(depth) (SEC_MAX_MAC_LEN * (depth)) -#define SEC_PBUF_SZ 512 #define SEC_PBUF_IV_OFFSET SEC_PBUF_SZ #define SEC_PBUF_MAC_OFFSET (SEC_PBUF_SZ + SEC_IV_SIZE) #define SEC_PBUF_PKG (SEC_PBUF_SZ + SEC_IV_SIZE + \ @@ -102,6 +101,8 @@ #define IV_LAST_BYTE_MASK 0xFF #define IV_CTR_INIT 0x1 #define IV_BYTE_OFFSET 0x8 +#define SEC_GCM_MIN_AUTH_SZ 0x8 +#define SEC_RETRY_MAX_CNT 5U static DEFINE_MUTEX(sec_algs_lock); static unsigned int sec_available_devs; @@ -116,40 +117,19 @@ struct sec_aead { struct aead_alg alg; }; -/* Get an en/de-cipher queue cyclically to balance load over queues of TFM */ -static inline u32 sec_alloc_queue_id(struct sec_ctx *ctx, struct sec_req *req) -{ - if (req->c_req.encrypt) - return (u32)atomic_inc_return(&ctx->enc_qcyclic) % - ctx->hlf_q_num; - - return (u32)atomic_inc_return(&ctx->dec_qcyclic) % ctx->hlf_q_num + - ctx->hlf_q_num; -} - -static inline void sec_free_queue_id(struct sec_ctx *ctx, struct sec_req *req) -{ - if (req->c_req.encrypt) - atomic_dec(&ctx->enc_qcyclic); - else - atomic_dec(&ctx->dec_qcyclic); -} +static int sec_aead_soft_crypto(struct sec_ctx *ctx, + struct aead_request *aead_req, + bool encrypt); +static int sec_skcipher_soft_crypto(struct sec_ctx *ctx, + struct skcipher_request *sreq, bool encrypt); static int sec_alloc_req_id(struct sec_req *req, struct sec_qp_ctx *qp_ctx) { int req_id; - spin_lock_bh(&qp_ctx->req_lock); + spin_lock_bh(&qp_ctx->id_lock); req_id = idr_alloc_cyclic(&qp_ctx->req_idr, NULL, 0, qp_ctx->qp->sq_depth, GFP_ATOMIC); - spin_unlock_bh(&qp_ctx->req_lock); - if (unlikely(req_id < 0)) { - dev_err(req->ctx->dev, "alloc req id fail!\n"); - return req_id; - } - - req->qp_ctx = qp_ctx; - qp_ctx->req_list[req_id] = req; - + spin_unlock_bh(&qp_ctx->id_lock); return req_id; } @@ -163,12 +143,9 @@ static void sec_free_req_id(struct sec_req *req) return; } - qp_ctx->req_list[req_id] = NULL; - req->qp_ctx = NULL; - - spin_lock_bh(&qp_ctx->req_lock); + spin_lock_bh(&qp_ctx->id_lock); idr_remove(&qp_ctx->req_idr, req_id); - spin_unlock_bh(&qp_ctx->req_lock); + spin_unlock_bh(&qp_ctx->id_lock); } static u8 pre_parse_finished_bd(struct bd_status *status, void *resp) @@ -229,6 +206,90 @@ static int sec_cb_status_check(struct sec_req *req, return 0; } +static int qp_send_message(struct sec_req *req) +{ + struct sec_qp_ctx *qp_ctx = req->qp_ctx; + int ret; + + if (atomic_read(&qp_ctx->qp->qp_status.used) == qp_ctx->qp->sq_depth - 1) + return -EBUSY; + + spin_lock_bh(&qp_ctx->req_lock); + if (atomic_read(&qp_ctx->qp->qp_status.used) == qp_ctx->qp->sq_depth - 1) { + spin_unlock_bh(&qp_ctx->req_lock); + return -EBUSY; + } + + if (qp_ctx->ctx->type_supported == SEC_BD_TYPE2) { + req->sec_sqe.type2.tag = cpu_to_le16((u16)qp_ctx->send_head); + qp_ctx->req_list[qp_ctx->send_head] = req; + } + + ret = hisi_qp_send(qp_ctx->qp, &req->sec_sqe); + if (ret) { + spin_unlock_bh(&qp_ctx->req_lock); + return ret; + } + if (qp_ctx->ctx->type_supported == SEC_BD_TYPE2) + qp_ctx->send_head = (qp_ctx->send_head + 1) % qp_ctx->qp->sq_depth; + + spin_unlock_bh(&qp_ctx->req_lock); + + atomic64_inc(&req->ctx->sec->debug.dfx.send_cnt); + return -EINPROGRESS; +} + +static void sec_alg_send_backlog_soft(struct sec_ctx *ctx, struct sec_qp_ctx *qp_ctx) +{ + struct sec_req *req, *tmp; + int ret; + + list_for_each_entry_safe(req, tmp, &qp_ctx->backlog.list, list) { + list_del(&req->list); + ctx->req_op->buf_unmap(ctx, req); + if (req->req_id >= 0) + sec_free_req_id(req); + + if (ctx->alg_type == SEC_AEAD) + ret = sec_aead_soft_crypto(ctx, req->aead_req.aead_req, + req->c_req.encrypt); + else + ret = sec_skcipher_soft_crypto(ctx, req->c_req.sk_req, + req->c_req.encrypt); + + /* Wake up the busy thread first, then return the errno. */ + crypto_request_complete(req->base, -EINPROGRESS); + crypto_request_complete(req->base, ret); + } +} + +static void sec_alg_send_backlog(struct sec_ctx *ctx, struct sec_qp_ctx *qp_ctx) +{ + struct sec_req *req, *tmp; + int ret; + + spin_lock_bh(&qp_ctx->backlog.lock); + list_for_each_entry_safe(req, tmp, &qp_ctx->backlog.list, list) { + ret = qp_send_message(req); + switch (ret) { + case -EINPROGRESS: + list_del(&req->list); + crypto_request_complete(req->base, -EINPROGRESS); + break; + case -EBUSY: + /* Device is busy and stop send any request. */ + goto unlock; + default: + /* Release memory resources and send all requests through software. */ + sec_alg_send_backlog_soft(ctx, qp_ctx); + goto unlock; + } + } + +unlock: + spin_unlock_bh(&qp_ctx->backlog.lock); +} + static void sec_req_cb(struct hisi_qp *qp, void *resp) { struct sec_qp_ctx *qp_ctx = qp->qp_ctx; @@ -273,40 +334,54 @@ static void sec_req_cb(struct hisi_qp *qp, void *resp) ctx->req_op->callback(ctx, req, err); } -static int sec_bd_send(struct sec_ctx *ctx, struct sec_req *req) +static int sec_alg_send_message_retry(struct sec_req *req) { - struct sec_qp_ctx *qp_ctx = req->qp_ctx; + int ctr = 0; int ret; - if (ctx->fake_req_limit <= - atomic_read(&qp_ctx->qp->qp_status.used) && - !(req->flag & CRYPTO_TFM_REQ_MAY_BACKLOG)) - return -EBUSY; - - spin_lock_bh(&qp_ctx->req_lock); - ret = hisi_qp_send(qp_ctx->qp, &req->sec_sqe); - if (ctx->fake_req_limit <= - atomic_read(&qp_ctx->qp->qp_status.used) && !ret) { - list_add_tail(&req->backlog_head, &qp_ctx->backlog); - atomic64_inc(&ctx->sec->debug.dfx.send_cnt); - atomic64_inc(&ctx->sec->debug.dfx.send_busy_cnt); - spin_unlock_bh(&qp_ctx->req_lock); - return -EBUSY; - } - spin_unlock_bh(&qp_ctx->req_lock); - - if (unlikely(ret == -EBUSY)) - return -ENOBUFS; - - if (likely(!ret)) { - ret = -EINPROGRESS; - atomic64_inc(&ctx->sec->debug.dfx.send_cnt); - } + do { + ret = qp_send_message(req); + } while (ret == -EBUSY && ctr++ < SEC_RETRY_MAX_CNT); return ret; } -/* Get DMA memory resources */ +static int sec_alg_try_enqueue(struct sec_req *req) +{ + /* Check if any request is already backlogged */ + if (!list_empty(&req->backlog->list)) + return -EBUSY; + + /* Try to enqueue to HW ring */ + return qp_send_message(req); +} + + +static int sec_alg_send_message_maybacklog(struct sec_req *req) +{ + int ret; + + ret = sec_alg_try_enqueue(req); + if (ret != -EBUSY) + return ret; + + spin_lock_bh(&req->backlog->lock); + ret = sec_alg_try_enqueue(req); + if (ret == -EBUSY) + list_add_tail(&req->list, &req->backlog->list); + spin_unlock_bh(&req->backlog->lock); + + return ret; +} + +static int sec_bd_send(struct sec_ctx *ctx, struct sec_req *req) +{ + if (req->flag & CRYPTO_TFM_REQ_MAY_BACKLOG) + return sec_alg_send_message_maybacklog(req); + + return sec_alg_send_message_retry(req); +} + static int sec_alloc_civ_resource(struct device *dev, struct sec_alg_res *res) { u16 q_depth = res->depth; @@ -558,7 +633,10 @@ static int sec_create_qp_ctx(struct sec_ctx *ctx, int qp_ctx_id) spin_lock_init(&qp_ctx->req_lock); idr_init(&qp_ctx->req_idr); - INIT_LIST_HEAD(&qp_ctx->backlog); + spin_lock_init(&qp_ctx->backlog.lock); + spin_lock_init(&qp_ctx->id_lock); + INIT_LIST_HEAD(&qp_ctx->backlog.list); + qp_ctx->send_head = 0; ret = sec_alloc_qp_ctx_resource(ctx, qp_ctx); if (ret) @@ -602,9 +680,6 @@ static int sec_ctx_base_init(struct sec_ctx *ctx) ctx->hlf_q_num = sec->ctx_q_num >> 1; ctx->pbuf_supported = ctx->sec->iommu_used; - - /* Half of queue depth is taken as fake requests limit in the queue. */ - ctx->fake_req_limit = ctx->qps[0]->sq_depth >> 1; ctx->qp_ctx = kcalloc(sec->ctx_q_num, sizeof(struct sec_qp_ctx), GFP_KERNEL); if (!ctx->qp_ctx) { @@ -706,7 +781,7 @@ static int sec_skcipher_init(struct crypto_skcipher *tfm) int ret; ctx->alg_type = SEC_SKCIPHER; - crypto_skcipher_set_reqsize(tfm, sizeof(struct sec_req)); + crypto_skcipher_set_reqsize_dma(tfm, sizeof(struct sec_req)); ctx->c_ctx.ivsize = crypto_skcipher_ivsize(tfm); if (ctx->c_ctx.ivsize > SEC_IV_SIZE) { pr_err("get error skcipher iv size!\n"); @@ -883,24 +958,25 @@ GEN_SEC_SETKEY_FUNC(sm4_ctr, SEC_CALG_SM4, SEC_CMODE_CTR) static int sec_cipher_pbuf_map(struct sec_ctx *ctx, struct sec_req *req, struct scatterlist *src) { - struct sec_aead_req *a_req = &req->aead_req; - struct aead_request *aead_req = a_req->aead_req; + struct aead_request *aead_req = req->aead_req.aead_req; struct sec_cipher_req *c_req = &req->c_req; struct sec_qp_ctx *qp_ctx = req->qp_ctx; + struct sec_request_buf *buf = &req->buf; struct device *dev = ctx->dev; int copy_size, pbuf_length; int req_id = req->req_id; struct crypto_aead *tfm; + u8 *mac_offset, *pbuf; size_t authsize; - u8 *mac_offset; if (ctx->alg_type == SEC_AEAD) copy_size = aead_req->cryptlen + aead_req->assoclen; else copy_size = c_req->c_len; - pbuf_length = sg_copy_to_buffer(src, sg_nents(src), - qp_ctx->res[req_id].pbuf, copy_size); + + pbuf = req->req_id < 0 ? buf->pbuf : qp_ctx->res[req_id].pbuf; + pbuf_length = sg_copy_to_buffer(src, sg_nents(src), pbuf, copy_size); if (unlikely(pbuf_length != copy_size)) { dev_err(dev, "copy src data to pbuf error!\n"); return -EINVAL; @@ -908,8 +984,17 @@ static int sec_cipher_pbuf_map(struct sec_ctx *ctx, struct sec_req *req, if (!c_req->encrypt && ctx->alg_type == SEC_AEAD) { tfm = crypto_aead_reqtfm(aead_req); authsize = crypto_aead_authsize(tfm); - mac_offset = qp_ctx->res[req_id].pbuf + copy_size - authsize; - memcpy(a_req->out_mac, mac_offset, authsize); + mac_offset = pbuf + copy_size - authsize; + memcpy(req->aead_req.out_mac, mac_offset, authsize); + } + + if (req->req_id < 0) { + buf->in_dma = dma_map_single(dev, buf->pbuf, SEC_PBUF_SZ, DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, buf->in_dma))) + return -ENOMEM; + + buf->out_dma = buf->in_dma; + return 0; } req->in_dma = qp_ctx->res[req_id].pbuf_dma; @@ -924,6 +1009,7 @@ static void sec_cipher_pbuf_unmap(struct sec_ctx *ctx, struct sec_req *req, struct aead_request *aead_req = req->aead_req.aead_req; struct sec_cipher_req *c_req = &req->c_req; struct sec_qp_ctx *qp_ctx = req->qp_ctx; + struct sec_request_buf *buf = &req->buf; int copy_size, pbuf_length; int req_id = req->req_id; @@ -932,10 +1018,16 @@ static void sec_cipher_pbuf_unmap(struct sec_ctx *ctx, struct sec_req *req, else copy_size = c_req->c_len; - pbuf_length = sg_copy_from_buffer(dst, sg_nents(dst), - qp_ctx->res[req_id].pbuf, copy_size); + if (req->req_id < 0) + pbuf_length = sg_copy_from_buffer(dst, sg_nents(dst), buf->pbuf, copy_size); + else + pbuf_length = sg_copy_from_buffer(dst, sg_nents(dst), qp_ctx->res[req_id].pbuf, + copy_size); if (unlikely(pbuf_length != copy_size)) dev_err(ctx->dev, "copy pbuf data to dst error!\n"); + + if (req->req_id < 0) + dma_unmap_single(ctx->dev, buf->in_dma, SEC_PBUF_SZ, DMA_BIDIRECTIONAL); } static int sec_aead_mac_init(struct sec_aead_req *req) @@ -957,14 +1049,95 @@ static int sec_aead_mac_init(struct sec_aead_req *req) return 0; } -static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, - struct scatterlist *src, struct scatterlist *dst) +static void fill_sg_to_hw_sge(struct scatterlist *sgl, struct sec_hw_sge *hw_sge) +{ + hw_sge->buf = sg_dma_address(sgl); + hw_sge->len = cpu_to_le32(sg_dma_len(sgl)); + hw_sge->page_ctrl = sg_virt(sgl); +} + +static int sec_cipher_to_hw_sgl(struct device *dev, struct scatterlist *src, + struct sec_hw_sgl *src_in, dma_addr_t *hw_sgl_dma, + int dma_dir) +{ + struct sec_hw_sge *curr_hw_sge = src_in->sge_entries; + u32 i, sg_n, sg_n_mapped; + struct scatterlist *sg; + u32 sge_var = 0; + + sg_n = sg_nents(src); + sg_n_mapped = dma_map_sg(dev, src, sg_n, dma_dir); + if (unlikely(!sg_n_mapped)) { + dev_err(dev, "dma mapping for SG error!\n"); + return -EINVAL; + } else if (unlikely(sg_n_mapped > SEC_SGE_NR_NUM)) { + dev_err(dev, "the number of entries in input scatterlist error!\n"); + dma_unmap_sg(dev, src, sg_n, dma_dir); + return -EINVAL; + } + + for_each_sg(src, sg, sg_n_mapped, i) { + fill_sg_to_hw_sge(sg, curr_hw_sge); + curr_hw_sge++; + sge_var++; + } + + src_in->entry_sum_in_sgl = cpu_to_le16(sge_var); + src_in->entry_sum_in_chain = cpu_to_le16(SEC_SGE_NR_NUM); + src_in->entry_length_in_sgl = cpu_to_le16(SEC_SGE_NR_NUM); + *hw_sgl_dma = dma_map_single(dev, src_in, sizeof(struct sec_hw_sgl), dma_dir); + if (unlikely(dma_mapping_error(dev, *hw_sgl_dma))) { + dma_unmap_sg(dev, src, sg_n, dma_dir); + return -ENOMEM; + } + + return 0; +} + +static void sec_cipher_put_hw_sgl(struct device *dev, struct scatterlist *src, + dma_addr_t src_in, int dma_dir) +{ + dma_unmap_single(dev, src_in, sizeof(struct sec_hw_sgl), dma_dir); + dma_unmap_sg(dev, src, sg_nents(src), dma_dir); +} + +static int sec_cipher_map_sgl(struct device *dev, struct sec_req *req, + struct scatterlist *src, struct scatterlist *dst) +{ + struct sec_hw_sgl *src_in = &req->buf.data_buf.in; + struct sec_hw_sgl *dst_out = &req->buf.data_buf.out; + int ret; + + if (dst == src) { + ret = sec_cipher_to_hw_sgl(dev, src, src_in, &req->buf.in_dma, + DMA_BIDIRECTIONAL); + req->buf.out_dma = req->buf.in_dma; + return ret; + } + + ret = sec_cipher_to_hw_sgl(dev, src, src_in, &req->buf.in_dma, DMA_TO_DEVICE); + if (unlikely(ret)) + return ret; + + ret = sec_cipher_to_hw_sgl(dev, dst, dst_out, &req->buf.out_dma, + DMA_FROM_DEVICE); + if (unlikely(ret)) { + sec_cipher_put_hw_sgl(dev, src, req->buf.in_dma, DMA_TO_DEVICE); + return ret; + } + + return 0; +} + +static int sec_cipher_map_inner(struct sec_ctx *ctx, struct sec_req *req, + struct scatterlist *src, struct scatterlist *dst) { struct sec_cipher_req *c_req = &req->c_req; struct sec_aead_req *a_req = &req->aead_req; struct sec_qp_ctx *qp_ctx = req->qp_ctx; struct sec_alg_res *res = &qp_ctx->res[req->req_id]; struct device *dev = ctx->dev; + enum dma_data_direction src_direction; int ret; if (req->use_pbuf) { @@ -977,10 +1150,9 @@ static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, a_req->out_mac_dma = res->pbuf_dma + SEC_PBUF_MAC_OFFSET; } - ret = sec_cipher_pbuf_map(ctx, req, src); - - return ret; + return sec_cipher_pbuf_map(ctx, req, src); } + c_req->c_ivin = res->c_ivin; c_req->c_ivin_dma = res->c_ivin_dma; if (ctx->alg_type == SEC_AEAD) { @@ -990,10 +1162,11 @@ static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, a_req->out_mac_dma = res->out_mac_dma; } + src_direction = dst == src ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE; req->in = hisi_acc_sg_buf_map_to_hw_sgl(dev, src, qp_ctx->c_in_pool, req->req_id, - &req->in_dma); + &req->in_dma, src_direction); if (IS_ERR(req->in)) { dev_err(dev, "fail to dma map input sgl buffers!\n"); return PTR_ERR(req->in); @@ -1003,7 +1176,7 @@ static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, ret = sec_aead_mac_init(a_req); if (unlikely(ret)) { dev_err(dev, "fail to init mac data for ICV!\n"); - hisi_acc_sg_buf_unmap(dev, src, req->in); + hisi_acc_sg_buf_unmap(dev, src, req->in, src_direction); return ret; } } @@ -1015,11 +1188,12 @@ static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, c_req->c_out = hisi_acc_sg_buf_map_to_hw_sgl(dev, dst, qp_ctx->c_out_pool, req->req_id, - &c_req->c_out_dma); + &c_req->c_out_dma, + DMA_FROM_DEVICE); if (IS_ERR(c_req->c_out)) { dev_err(dev, "fail to dma map output sgl buffers!\n"); - hisi_acc_sg_buf_unmap(dev, src, req->in); + hisi_acc_sg_buf_unmap(dev, src, req->in, src_direction); return PTR_ERR(c_req->c_out); } } @@ -1027,19 +1201,108 @@ static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, return 0; } +static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, + struct scatterlist *src, struct scatterlist *dst) +{ + struct sec_aead_req *a_req = &req->aead_req; + struct sec_cipher_req *c_req = &req->c_req; + bool is_aead = (ctx->alg_type == SEC_AEAD); + struct device *dev = ctx->dev; + int ret = -ENOMEM; + + if (req->req_id >= 0) + return sec_cipher_map_inner(ctx, req, src, dst); + + c_req->c_ivin = c_req->c_ivin_buf; + c_req->c_ivin_dma = dma_map_single(dev, c_req->c_ivin, + SEC_IV_SIZE, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, c_req->c_ivin_dma))) + return -ENOMEM; + + if (is_aead) { + a_req->a_ivin = a_req->a_ivin_buf; + a_req->out_mac = a_req->out_mac_buf; + a_req->a_ivin_dma = dma_map_single(dev, a_req->a_ivin, + SEC_IV_SIZE, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, a_req->a_ivin_dma))) + goto free_c_ivin_dma; + + a_req->out_mac_dma = dma_map_single(dev, a_req->out_mac, + SEC_MAX_MAC_LEN, DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, a_req->out_mac_dma))) + goto free_a_ivin_dma; + } + if (req->use_pbuf) { + ret = sec_cipher_pbuf_map(ctx, req, src); + if (unlikely(ret)) + goto free_out_mac_dma; + + return 0; + } + + if (!c_req->encrypt && is_aead) { + ret = sec_aead_mac_init(a_req); + if (unlikely(ret)) { + dev_err(dev, "fail to init mac data for ICV!\n"); + goto free_out_mac_dma; + } + } + + ret = sec_cipher_map_sgl(dev, req, src, dst); + if (unlikely(ret)) { + dev_err(dev, "fail to dma map input sgl buffers!\n"); + goto free_out_mac_dma; + } + + return 0; + +free_out_mac_dma: + if (is_aead) + dma_unmap_single(dev, a_req->out_mac_dma, SEC_MAX_MAC_LEN, DMA_BIDIRECTIONAL); +free_a_ivin_dma: + if (is_aead) + dma_unmap_single(dev, a_req->a_ivin_dma, SEC_IV_SIZE, DMA_TO_DEVICE); +free_c_ivin_dma: + dma_unmap_single(dev, c_req->c_ivin_dma, SEC_IV_SIZE, DMA_TO_DEVICE); + return ret; +} + static void sec_cipher_unmap(struct sec_ctx *ctx, struct sec_req *req, struct scatterlist *src, struct scatterlist *dst) { + struct sec_aead_req *a_req = &req->aead_req; struct sec_cipher_req *c_req = &req->c_req; struct device *dev = ctx->dev; + if (req->req_id >= 0) { + if (req->use_pbuf) { + sec_cipher_pbuf_unmap(ctx, req, dst); + } else { + if (dst != src) { + hisi_acc_sg_buf_unmap(dev, dst, c_req->c_out, DMA_FROM_DEVICE); + hisi_acc_sg_buf_unmap(dev, src, req->in, DMA_TO_DEVICE); + } else { + hisi_acc_sg_buf_unmap(dev, src, req->in, DMA_BIDIRECTIONAL); + } + } + return; + } + if (req->use_pbuf) { sec_cipher_pbuf_unmap(ctx, req, dst); } else { - if (dst != src) - hisi_acc_sg_buf_unmap(dev, src, req->in); + if (dst != src) { + sec_cipher_put_hw_sgl(dev, dst, req->buf.out_dma, DMA_FROM_DEVICE); + sec_cipher_put_hw_sgl(dev, src, req->buf.in_dma, DMA_TO_DEVICE); + } else { + sec_cipher_put_hw_sgl(dev, src, req->buf.in_dma, DMA_BIDIRECTIONAL); + } + } - hisi_acc_sg_buf_unmap(dev, dst, c_req->c_out); + dma_unmap_single(dev, c_req->c_ivin_dma, SEC_IV_SIZE, DMA_TO_DEVICE); + if (ctx->alg_type == SEC_AEAD) { + dma_unmap_single(dev, a_req->a_ivin_dma, SEC_IV_SIZE, DMA_TO_DEVICE); + dma_unmap_single(dev, a_req->out_mac_dma, SEC_MAX_MAC_LEN, DMA_BIDIRECTIONAL); } } @@ -1257,8 +1520,15 @@ static int sec_skcipher_bd_fill(struct sec_ctx *ctx, struct sec_req *req) sec_sqe->type2.c_key_addr = cpu_to_le64(c_ctx->c_key_dma); sec_sqe->type2.c_ivin_addr = cpu_to_le64(c_req->c_ivin_dma); - sec_sqe->type2.data_src_addr = cpu_to_le64(req->in_dma); - sec_sqe->type2.data_dst_addr = cpu_to_le64(c_req->c_out_dma); + if (req->req_id < 0) { + sec_sqe->type2.data_src_addr = cpu_to_le64(req->buf.in_dma); + sec_sqe->type2.data_dst_addr = cpu_to_le64(req->buf.out_dma); + } else { + sec_sqe->type2.data_src_addr = cpu_to_le64(req->in_dma); + sec_sqe->type2.data_dst_addr = cpu_to_le64(c_req->c_out_dma); + } + if (sec_sqe->type2.data_src_addr != sec_sqe->type2.data_dst_addr) + de = 0x1 << SEC_DE_OFFSET; sec_sqe->type2.icvw_kmode |= cpu_to_le16(((u16)c_ctx->c_mode) << SEC_CMODE_OFFSET); @@ -1284,13 +1554,10 @@ static int sec_skcipher_bd_fill(struct sec_ctx *ctx, struct sec_req *req) sec_sqe->sdm_addr_type |= da_type; scene = SEC_COMM_SCENE << SEC_SCENE_OFFSET; - if (req->in_dma != c_req->c_out_dma) - de = 0x1 << SEC_DE_OFFSET; sec_sqe->sds_sa_type = (de | scene | sa_type); sec_sqe->type2.clen_ivhlen |= cpu_to_le32(c_req->c_len); - sec_sqe->type2.tag = cpu_to_le16((u16)req->req_id); return 0; } @@ -1307,8 +1574,15 @@ static int sec_skcipher_bd_fill_v3(struct sec_ctx *ctx, struct sec_req *req) sec_sqe3->c_key_addr = cpu_to_le64(c_ctx->c_key_dma); sec_sqe3->no_scene.c_ivin_addr = cpu_to_le64(c_req->c_ivin_dma); - sec_sqe3->data_src_addr = cpu_to_le64(req->in_dma); - sec_sqe3->data_dst_addr = cpu_to_le64(c_req->c_out_dma); + if (req->req_id < 0) { + sec_sqe3->data_src_addr = cpu_to_le64(req->buf.in_dma); + sec_sqe3->data_dst_addr = cpu_to_le64(req->buf.out_dma); + } else { + sec_sqe3->data_src_addr = cpu_to_le64(req->in_dma); + sec_sqe3->data_dst_addr = cpu_to_le64(c_req->c_out_dma); + } + if (sec_sqe3->data_src_addr != sec_sqe3->data_dst_addr) + bd_param |= 0x1 << SEC_DE_OFFSET_V3; sec_sqe3->c_mode_alg = ((u8)c_ctx->c_alg << SEC_CALG_OFFSET_V3) | c_ctx->c_mode; @@ -1334,8 +1608,6 @@ static int sec_skcipher_bd_fill_v3(struct sec_ctx *ctx, struct sec_req *req) } bd_param |= SEC_COMM_SCENE << SEC_SCENE_OFFSET_V3; - if (req->in_dma != c_req->c_out_dma) - bd_param |= 0x1 << SEC_DE_OFFSET_V3; bd_param |= SEC_BD_TYPE3; sec_sqe3->bd_param = cpu_to_le32(bd_param); @@ -1367,15 +1639,12 @@ static void sec_update_iv(struct sec_req *req, enum sec_alg_type alg_type) size_t sz; u8 *iv; - if (req->c_req.encrypt) - sgl = alg_type == SEC_SKCIPHER ? sk_req->dst : aead_req->dst; - else - sgl = alg_type == SEC_SKCIPHER ? sk_req->src : aead_req->src; - if (alg_type == SEC_SKCIPHER) { + sgl = req->c_req.encrypt ? sk_req->dst : sk_req->src; iv = sk_req->iv; cryptlen = sk_req->cryptlen; } else { + sgl = req->c_req.encrypt ? aead_req->dst : aead_req->src; iv = aead_req->iv; cryptlen = aead_req->cryptlen; } @@ -1386,57 +1655,26 @@ static void sec_update_iv(struct sec_req *req, enum sec_alg_type alg_type) if (unlikely(sz != iv_size)) dev_err(req->ctx->dev, "copy output iv error!\n"); } else { - sz = cryptlen / iv_size; - if (cryptlen % iv_size) - sz += 1; + sz = (cryptlen + iv_size - 1) / iv_size; ctr_iv_inc(iv, iv_size, sz); } } -static struct sec_req *sec_back_req_clear(struct sec_ctx *ctx, - struct sec_qp_ctx *qp_ctx) -{ - struct sec_req *backlog_req = NULL; - - spin_lock_bh(&qp_ctx->req_lock); - if (ctx->fake_req_limit >= - atomic_read(&qp_ctx->qp->qp_status.used) && - !list_empty(&qp_ctx->backlog)) { - backlog_req = list_first_entry(&qp_ctx->backlog, - typeof(*backlog_req), backlog_head); - list_del(&backlog_req->backlog_head); - } - spin_unlock_bh(&qp_ctx->req_lock); - - return backlog_req; -} - static void sec_skcipher_callback(struct sec_ctx *ctx, struct sec_req *req, int err) { - struct skcipher_request *sk_req = req->c_req.sk_req; struct sec_qp_ctx *qp_ctx = req->qp_ctx; - struct skcipher_request *backlog_sk_req; - struct sec_req *backlog_req; - sec_free_req_id(req); + if (req->req_id >= 0) + sec_free_req_id(req); /* IV output at encrypto of CBC/CTR mode */ if (!err && (ctx->c_ctx.c_mode == SEC_CMODE_CBC || ctx->c_ctx.c_mode == SEC_CMODE_CTR) && req->c_req.encrypt) sec_update_iv(req, SEC_SKCIPHER); - while (1) { - backlog_req = sec_back_req_clear(ctx, qp_ctx); - if (!backlog_req) - break; - - backlog_sk_req = backlog_req->c_req.sk_req; - skcipher_request_complete(backlog_sk_req, -EINPROGRESS); - atomic64_inc(&ctx->sec->debug.dfx.recv_busy_cnt); - } - - skcipher_request_complete(sk_req, err); + crypto_request_complete(req->base, err); + sec_alg_send_backlog(ctx, qp_ctx); } static void set_aead_auth_iv(struct sec_ctx *ctx, struct sec_req *req) @@ -1675,21 +1913,14 @@ static void sec_aead_callback(struct sec_ctx *c, struct sec_req *req, int err) struct aead_request *a_req = req->aead_req.aead_req; struct crypto_aead *tfm = crypto_aead_reqtfm(a_req); size_t authsize = crypto_aead_authsize(tfm); - struct sec_aead_req *aead_req = &req->aead_req; - struct sec_cipher_req *c_req = &req->c_req; struct sec_qp_ctx *qp_ctx = req->qp_ctx; - struct aead_request *backlog_aead_req; - struct sec_req *backlog_req; size_t sz; - if (!err && c->c_ctx.c_mode == SEC_CMODE_CBC && c_req->encrypt) - sec_update_iv(req, SEC_AEAD); + if (!err && req->c_req.encrypt) { + if (c->c_ctx.c_mode == SEC_CMODE_CBC) + sec_update_iv(req, SEC_AEAD); - /* Copy output mac */ - if (!err && c_req->encrypt) { - struct scatterlist *sgl = a_req->dst; - - sz = sg_pcopy_from_buffer(sgl, sg_nents(sgl), aead_req->out_mac, + sz = sg_pcopy_from_buffer(a_req->dst, sg_nents(a_req->dst), req->aead_req.out_mac, authsize, a_req->cryptlen + a_req->assoclen); if (unlikely(sz != authsize)) { dev_err(c->dev, "copy out mac err!\n"); @@ -1697,48 +1928,39 @@ static void sec_aead_callback(struct sec_ctx *c, struct sec_req *req, int err) } } - sec_free_req_id(req); + if (req->req_id >= 0) + sec_free_req_id(req); - while (1) { - backlog_req = sec_back_req_clear(c, qp_ctx); - if (!backlog_req) - break; - - backlog_aead_req = backlog_req->aead_req.aead_req; - aead_request_complete(backlog_aead_req, -EINPROGRESS); - atomic64_inc(&c->sec->debug.dfx.recv_busy_cnt); - } - - aead_request_complete(a_req, err); + crypto_request_complete(req->base, err); + sec_alg_send_backlog(c, qp_ctx); } -static void sec_request_uninit(struct sec_ctx *ctx, struct sec_req *req) +static void sec_request_uninit(struct sec_req *req) { - sec_free_req_id(req); - sec_free_queue_id(ctx, req); + if (req->req_id >= 0) + sec_free_req_id(req); } static int sec_request_init(struct sec_ctx *ctx, struct sec_req *req) { struct sec_qp_ctx *qp_ctx; - int queue_id; + int i; - /* To load balance */ - queue_id = sec_alloc_queue_id(ctx, req); - qp_ctx = &ctx->qp_ctx[queue_id]; - - req->req_id = sec_alloc_req_id(req, qp_ctx); - if (unlikely(req->req_id < 0)) { - sec_free_queue_id(ctx, req); - return req->req_id; + for (i = 0; i < ctx->sec->ctx_q_num; i++) { + qp_ctx = &ctx->qp_ctx[i]; + req->req_id = sec_alloc_req_id(req, qp_ctx); + if (req->req_id >= 0) + break; } + req->qp_ctx = qp_ctx; + req->backlog = &qp_ctx->backlog; + return 0; } static int sec_process(struct sec_ctx *ctx, struct sec_req *req) { - struct sec_cipher_req *c_req = &req->c_req; int ret; ret = sec_request_init(ctx, req); @@ -1755,8 +1977,7 @@ static int sec_process(struct sec_ctx *ctx, struct sec_req *req) sec_update_iv(req, ctx->alg_type); ret = ctx->req_op->bd_send(ctx, req); - if (unlikely((ret != -EBUSY && ret != -EINPROGRESS) || - (ret == -EBUSY && !(req->flag & CRYPTO_TFM_REQ_MAY_BACKLOG)))) { + if (unlikely((ret != -EBUSY && ret != -EINPROGRESS))) { dev_err_ratelimited(ctx->dev, "send sec request failed!\n"); goto err_send_req; } @@ -1767,16 +1988,23 @@ static int sec_process(struct sec_ctx *ctx, struct sec_req *req) /* As failing, restore the IV from user */ if (ctx->c_ctx.c_mode == SEC_CMODE_CBC && !req->c_req.encrypt) { if (ctx->alg_type == SEC_SKCIPHER) - memcpy(req->c_req.sk_req->iv, c_req->c_ivin, + memcpy(req->c_req.sk_req->iv, req->c_req.c_ivin, ctx->c_ctx.ivsize); else - memcpy(req->aead_req.aead_req->iv, c_req->c_ivin, + memcpy(req->aead_req.aead_req->iv, req->c_req.c_ivin, ctx->c_ctx.ivsize); } sec_request_untransfer(ctx, req); + err_uninit_req: - sec_request_uninit(ctx, req); + sec_request_uninit(req); + if (ctx->alg_type == SEC_AEAD) + ret = sec_aead_soft_crypto(ctx, req->aead_req.aead_req, + req->c_req.encrypt); + else + ret = sec_skcipher_soft_crypto(ctx, req->c_req.sk_req, + req->c_req.encrypt); return ret; } @@ -1850,7 +2078,7 @@ static int sec_aead_init(struct crypto_aead *tfm) struct sec_ctx *ctx = crypto_aead_ctx(tfm); int ret; - crypto_aead_set_reqsize(tfm, sizeof(struct sec_req)); + crypto_aead_set_reqsize_dma(tfm, sizeof(struct sec_req)); ctx->alg_type = SEC_AEAD; ctx->c_ctx.ivsize = crypto_aead_ivsize(tfm); if (ctx->c_ctx.ivsize < SEC_AIV_SIZE || @@ -2087,7 +2315,7 @@ static int sec_skcipher_soft_crypto(struct sec_ctx *ctx, static int sec_skcipher_crypto(struct skcipher_request *sk_req, bool encrypt) { struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(sk_req); - struct sec_req *req = skcipher_request_ctx(sk_req); + struct sec_req *req = skcipher_request_ctx_dma(sk_req); struct sec_ctx *ctx = crypto_skcipher_ctx(tfm); bool need_fallback = false; int ret; @@ -2102,6 +2330,7 @@ static int sec_skcipher_crypto(struct skcipher_request *sk_req, bool encrypt) req->c_req.sk_req = sk_req; req->c_req.encrypt = encrypt; req->ctx = ctx; + req->base = &sk_req->base; ret = sec_skcipher_param_check(ctx, req, &need_fallback); if (unlikely(ret)) @@ -2236,6 +2465,9 @@ static int sec_aead_spec_check(struct sec_ctx *ctx, struct sec_req *sreq) return -EINVAL; if (unlikely(ctx->a_ctx.a_key_len & WORD_MASK)) return -EINVAL; + } else if (c_mode == SEC_CMODE_GCM) { + if (unlikely(sz < SEC_GCM_MIN_AUTH_SZ)) + return -EINVAL; } return 0; @@ -2309,7 +2541,7 @@ static int sec_aead_soft_crypto(struct sec_ctx *ctx, static int sec_aead_crypto(struct aead_request *a_req, bool encrypt) { struct crypto_aead *tfm = crypto_aead_reqtfm(a_req); - struct sec_req *req = aead_request_ctx(a_req); + struct sec_req *req = aead_request_ctx_dma(a_req); struct sec_ctx *ctx = crypto_aead_ctx(tfm); size_t sz = crypto_aead_authsize(tfm); bool need_fallback = false; @@ -2319,6 +2551,7 @@ static int sec_aead_crypto(struct aead_request *a_req, bool encrypt) req->aead_req.aead_req = a_req; req->c_req.encrypt = encrypt; req->ctx = ctx; + req->base = &a_req->base; req->c_req.c_len = a_req->cryptlen - (req->c_req.encrypt ? 0 : sz); ret = sec_aead_param_check(ctx, req, &need_fallback); diff --git a/drivers/crypto/hisilicon/sgl.c b/drivers/crypto/hisilicon/sgl.c index c974f95cd126..7a9ef2a9972a 100644 --- a/drivers/crypto/hisilicon/sgl.c +++ b/drivers/crypto/hisilicon/sgl.c @@ -210,15 +210,15 @@ static void clear_hw_sgl_sge(struct hisi_acc_hw_sgl *hw_sgl) * @pool: Pool which hw sgl memory will be allocated in. * @index: Index of hisi_acc_hw_sgl in pool. * @hw_sgl_dma: The dma address of allocated hw sgl. + * @dir: DMA direction. * * This function builds hw sgl according input sgl, user can use hw_sgl_dma * as src/dst in its BD. Only support single hw sgl currently. */ struct hisi_acc_hw_sgl * -hisi_acc_sg_buf_map_to_hw_sgl(struct device *dev, - struct scatterlist *sgl, - struct hisi_acc_sgl_pool *pool, - u32 index, dma_addr_t *hw_sgl_dma) +hisi_acc_sg_buf_map_to_hw_sgl(struct device *dev, struct scatterlist *sgl, + struct hisi_acc_sgl_pool *pool, u32 index, + dma_addr_t *hw_sgl_dma, enum dma_data_direction dir) { struct hisi_acc_hw_sgl *curr_hw_sgl; unsigned int i, sg_n_mapped; @@ -232,7 +232,7 @@ hisi_acc_sg_buf_map_to_hw_sgl(struct device *dev, sg_n = sg_nents(sgl); - sg_n_mapped = dma_map_sg(dev, sgl, sg_n, DMA_BIDIRECTIONAL); + sg_n_mapped = dma_map_sg(dev, sgl, sg_n, dir); if (!sg_n_mapped) { dev_err(dev, "DMA mapping for SG error!\n"); return ERR_PTR(-EINVAL); @@ -276,16 +276,17 @@ EXPORT_SYMBOL_GPL(hisi_acc_sg_buf_map_to_hw_sgl); * @dev: The device which hw sgl belongs to. * @sgl: Related scatterlist. * @hw_sgl: Virtual address of hw sgl. + * @dir: DMA direction. * * This function unmaps allocated hw sgl. */ void hisi_acc_sg_buf_unmap(struct device *dev, struct scatterlist *sgl, - struct hisi_acc_hw_sgl *hw_sgl) + struct hisi_acc_hw_sgl *hw_sgl, enum dma_data_direction dir) { if (!dev || !sgl || !hw_sgl) return; - dma_unmap_sg(dev, sgl, sg_nents(sgl), DMA_BIDIRECTIONAL); + dma_unmap_sg(dev, sgl, sg_nents(sgl), dir); clear_hw_sgl_sge(hw_sgl); hw_sgl->entry_sum_in_chain = 0; hw_sgl->entry_sum_in_sgl = 0; diff --git a/drivers/crypto/hisilicon/zip/zip_crypto.c b/drivers/crypto/hisilicon/zip/zip_crypto.c index 7327f8f29b01..b97513981a3b 100644 --- a/drivers/crypto/hisilicon/zip/zip_crypto.c +++ b/drivers/crypto/hisilicon/zip/zip_crypto.c @@ -224,7 +224,8 @@ static int hisi_zip_do_work(struct hisi_zip_qp_ctx *qp_ctx, return -EINVAL; req->hw_src = hisi_acc_sg_buf_map_to_hw_sgl(dev, a_req->src, pool, - req->req_id << 1, &req->dma_src); + req->req_id << 1, &req->dma_src, + DMA_TO_DEVICE); if (IS_ERR(req->hw_src)) { dev_err(dev, "failed to map the src buffer to hw sgl (%ld)!\n", PTR_ERR(req->hw_src)); @@ -233,7 +234,7 @@ static int hisi_zip_do_work(struct hisi_zip_qp_ctx *qp_ctx, req->hw_dst = hisi_acc_sg_buf_map_to_hw_sgl(dev, a_req->dst, pool, (req->req_id << 1) + 1, - &req->dma_dst); + &req->dma_dst, DMA_FROM_DEVICE); if (IS_ERR(req->hw_dst)) { ret = PTR_ERR(req->hw_dst); dev_err(dev, "failed to map the dst buffer to hw slg (%d)!\n", @@ -258,9 +259,9 @@ static int hisi_zip_do_work(struct hisi_zip_qp_ctx *qp_ctx, return -EINPROGRESS; err_unmap_output: - hisi_acc_sg_buf_unmap(dev, a_req->dst, req->hw_dst); + hisi_acc_sg_buf_unmap(dev, a_req->dst, req->hw_dst, DMA_FROM_DEVICE); err_unmap_input: - hisi_acc_sg_buf_unmap(dev, a_req->src, req->hw_src); + hisi_acc_sg_buf_unmap(dev, a_req->src, req->hw_src, DMA_TO_DEVICE); return ret; } @@ -303,8 +304,8 @@ static void hisi_zip_acomp_cb(struct hisi_qp *qp, void *data) err = -EIO; } - hisi_acc_sg_buf_unmap(dev, acomp_req->src, req->hw_src); - hisi_acc_sg_buf_unmap(dev, acomp_req->dst, req->hw_dst); + hisi_acc_sg_buf_unmap(dev, acomp_req->dst, req->hw_dst, DMA_FROM_DEVICE); + hisi_acc_sg_buf_unmap(dev, acomp_req->src, req->hw_src, DMA_TO_DEVICE); acomp_req->dlen = ops->get_dstlen(sqe); diff --git a/drivers/crypto/img-hash.c b/drivers/crypto/img-hash.c index e050f5ff5efb..76b7ecb5624b 100644 --- a/drivers/crypto/img-hash.c +++ b/drivers/crypto/img-hash.c @@ -436,7 +436,7 @@ static int img_hash_write_via_dma_stop(struct img_hash_dev *hdev) struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); if (ctx->flags & DRIVER_FLAGS_SG) - dma_unmap_sg(hdev->dev, ctx->sg, ctx->dma_ct, DMA_TO_DEVICE); + dma_unmap_sg(hdev->dev, ctx->sg, 1, DMA_TO_DEVICE); return 0; } @@ -705,17 +705,17 @@ static int img_hash_cra_md5_init(struct crypto_tfm *tfm) static int img_hash_cra_sha1_init(struct crypto_tfm *tfm) { - return img_hash_cra_init(tfm, "sha1-generic"); + return img_hash_cra_init(tfm, "sha1-lib"); } static int img_hash_cra_sha224_init(struct crypto_tfm *tfm) { - return img_hash_cra_init(tfm, "sha224-generic"); + return img_hash_cra_init(tfm, "sha224-lib"); } static int img_hash_cra_sha256_init(struct crypto_tfm *tfm) { - return img_hash_cra_init(tfm, "sha256-generic"); + return img_hash_cra_init(tfm, "sha256-lib"); } static void img_hash_cra_exit(struct crypto_tfm *tfm) diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index 9ca80d082c4f..c3b2b22934b7 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -1218,7 +1218,6 @@ static struct safexcel_alg_template *safexcel_algs[] = { &safexcel_alg_xts_aes, &safexcel_alg_gcm, &safexcel_alg_ccm, - &safexcel_alg_crc32, &safexcel_alg_cbcmac, &safexcel_alg_xcbcmac, &safexcel_alg_cmac, diff --git a/drivers/crypto/inside-secure/safexcel.h b/drivers/crypto/inside-secure/safexcel.h index 0c79ad78d1c0..0f27367a85fa 100644 --- a/drivers/crypto/inside-secure/safexcel.h +++ b/drivers/crypto/inside-secure/safexcel.h @@ -959,7 +959,6 @@ extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_ctr_aes; extern struct safexcel_alg_template safexcel_alg_xts_aes; extern struct safexcel_alg_template safexcel_alg_gcm; extern struct safexcel_alg_template safexcel_alg_ccm; -extern struct safexcel_alg_template safexcel_alg_crc32; extern struct safexcel_alg_template safexcel_alg_cbcmac; extern struct safexcel_alg_template safexcel_alg_xcbcmac; extern struct safexcel_alg_template safexcel_alg_cmac; diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c index d2b632193beb..ef0ba4832928 100644 --- a/drivers/crypto/inside-secure/safexcel_hash.c +++ b/drivers/crypto/inside-secure/safexcel_hash.c @@ -249,7 +249,9 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, safexcel_complete(priv, ring); if (sreq->nents) { - dma_unmap_sg(priv->dev, areq->src, sreq->nents, DMA_TO_DEVICE); + dma_unmap_sg(priv->dev, areq->src, + sg_nents_for_len(areq->src, areq->nbytes), + DMA_TO_DEVICE); sreq->nents = 0; } @@ -289,14 +291,8 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, return 1; } - if (unlikely(sreq->digest == CONTEXT_CONTROL_DIGEST_XCM && - ctx->alg == CONTEXT_CONTROL_CRYPTO_ALG_CRC32)) { - /* Undo final XOR with 0xffffffff ...*/ - *(__le32 *)areq->result = ~sreq->state[0]; - } else { - memcpy(areq->result, sreq->state, - crypto_ahash_digestsize(ahash)); - } + memcpy(areq->result, sreq->state, + crypto_ahash_digestsize(ahash)); } cache_len = safexcel_queued_len(sreq); @@ -497,7 +493,9 @@ static int safexcel_ahash_send_req(struct crypto_async_request *async, int ring, DMA_FROM_DEVICE); unmap_sg: if (req->nents) { - dma_unmap_sg(priv->dev, areq->src, req->nents, DMA_TO_DEVICE); + dma_unmap_sg(priv->dev, areq->src, + sg_nents_for_len(areq->src, areq->nbytes), + DMA_TO_DEVICE); req->nents = 0; } cdesc_rollback: @@ -1881,88 +1879,6 @@ struct safexcel_alg_template safexcel_alg_hmac_md5 = { }, }; -static int safexcel_crc32_cra_init(struct crypto_tfm *tfm) -{ - struct safexcel_ahash_ctx *ctx = crypto_tfm_ctx(tfm); - int ret = safexcel_ahash_cra_init(tfm); - - /* Default 'key' is all zeroes */ - memset(&ctx->base.ipad, 0, sizeof(u32)); - return ret; -} - -static int safexcel_crc32_init(struct ahash_request *areq) -{ - struct safexcel_ahash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); - struct safexcel_ahash_req *req = ahash_request_ctx_dma(areq); - - memset(req, 0, sizeof(*req)); - - /* Start from loaded key */ - req->state[0] = cpu_to_le32(~ctx->base.ipad.word[0]); - /* Set processed to non-zero to enable invalidation detection */ - req->len = sizeof(u32); - req->processed = sizeof(u32); - - ctx->alg = CONTEXT_CONTROL_CRYPTO_ALG_CRC32; - req->digest = CONTEXT_CONTROL_DIGEST_XCM; - req->state_sz = sizeof(u32); - req->digest_sz = sizeof(u32); - req->block_sz = sizeof(u32); - - return 0; -} - -static int safexcel_crc32_setkey(struct crypto_ahash *tfm, const u8 *key, - unsigned int keylen) -{ - struct safexcel_ahash_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); - - if (keylen != sizeof(u32)) - return -EINVAL; - - memcpy(&ctx->base.ipad, key, sizeof(u32)); - return 0; -} - -static int safexcel_crc32_digest(struct ahash_request *areq) -{ - return safexcel_crc32_init(areq) ?: safexcel_ahash_finup(areq); -} - -struct safexcel_alg_template safexcel_alg_crc32 = { - .type = SAFEXCEL_ALG_TYPE_AHASH, - .algo_mask = 0, - .alg.ahash = { - .init = safexcel_crc32_init, - .update = safexcel_ahash_update, - .final = safexcel_ahash_final, - .finup = safexcel_ahash_finup, - .digest = safexcel_crc32_digest, - .setkey = safexcel_crc32_setkey, - .export = safexcel_ahash_export, - .import = safexcel_ahash_import, - .halg = { - .digestsize = sizeof(u32), - .statesize = sizeof(struct safexcel_ahash_export_state), - .base = { - .cra_name = "crc32", - .cra_driver_name = "safexcel-crc32", - .cra_priority = SAFEXCEL_CRA_PRIORITY, - .cra_flags = CRYPTO_ALG_OPTIONAL_KEY | - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_ALLOCATES_MEMORY | - CRYPTO_ALG_KERN_DRIVER_ONLY, - .cra_blocksize = 1, - .cra_ctxsize = sizeof(struct safexcel_ahash_ctx), - .cra_init = safexcel_crc32_cra_init, - .cra_exit = safexcel_ahash_cra_exit, - .cra_module = THIS_MODULE, - }, - }, - }, -}; - static int safexcel_cbcmac_init(struct ahash_request *areq) { struct safexcel_ahash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); diff --git a/drivers/crypto/intel/keembay/keembay-ocs-hcu-core.c b/drivers/crypto/intel/keembay/keembay-ocs-hcu-core.c index 95dc8979918d..8f9e21ced0fe 100644 --- a/drivers/crypto/intel/keembay/keembay-ocs-hcu-core.c +++ b/drivers/crypto/intel/keembay/keembay-ocs-hcu-core.c @@ -68,6 +68,7 @@ struct ocs_hcu_ctx { * @sg_data_total: Total data in the SG list at any time. * @sg_data_offset: Offset into the data of the current individual SG node. * @sg_dma_nents: Number of sg entries mapped in dma_list. + * @nents: Number of entries in the scatterlist. */ struct ocs_hcu_rctx { struct ocs_hcu_dev *hcu_dev; @@ -91,6 +92,7 @@ struct ocs_hcu_rctx { unsigned int sg_data_total; unsigned int sg_data_offset; unsigned int sg_dma_nents; + unsigned int nents; }; /** @@ -199,7 +201,7 @@ static void kmb_ocs_hcu_dma_cleanup(struct ahash_request *req, /* Unmap req->src (if mapped). */ if (rctx->sg_dma_nents) { - dma_unmap_sg(dev, req->src, rctx->sg_dma_nents, DMA_TO_DEVICE); + dma_unmap_sg(dev, req->src, rctx->nents, DMA_TO_DEVICE); rctx->sg_dma_nents = 0; } @@ -260,6 +262,10 @@ static int kmb_ocs_dma_prepare(struct ahash_request *req) rc = -ENOMEM; goto cleanup; } + + /* Save the value of nents to pass to dma_unmap_sg. */ + rctx->nents = nents; + /* * The value returned by dma_map_sg() can be < nents; so update * nents accordingly. diff --git a/drivers/crypto/intel/keembay/ocs-aes.c b/drivers/crypto/intel/keembay/ocs-aes.c index be9f32fc8f42..bb6f33f6b4d3 100644 --- a/drivers/crypto/intel/keembay/ocs-aes.c +++ b/drivers/crypto/intel/keembay/ocs-aes.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -1473,8 +1474,7 @@ int ocs_create_linked_list_from_sg(const struct ocs_aes_dev *aes_dev, ll = dll_desc->vaddr; for (i = 0; i < dma_nents; i++, sg = sg_next(sg)) { ll[i].src_addr = sg_dma_address(sg) + data_offset; - ll[i].src_len = (sg_dma_len(sg) - data_offset) < data_size ? - (sg_dma_len(sg) - data_offset) : data_size; + ll[i].src_len = min(sg_dma_len(sg) - data_offset, data_size); data_offset = 0; data_size -= ll[i].src_len; /* Current element points to the DMA address of the next one. */ diff --git a/drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.c b/drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.c index 7c3c0f561c95..53fa91d577ed 100644 --- a/drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.c +++ b/drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.c @@ -191,7 +191,6 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) ICP_ACCEL_CAPABILITIES_SM4 | ICP_ACCEL_CAPABILITIES_AES_V2 | ICP_ACCEL_CAPABILITIES_ZUC | - ICP_ACCEL_CAPABILITIES_ZUC_256 | ICP_ACCEL_CAPABILITIES_WIRELESS_CRYPTO_EXT | ICP_ACCEL_CAPABILITIES_EXT_ALGCHAIN; @@ -223,17 +222,11 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) if (fusectl1 & ICP_ACCEL_GEN4_MASK_WCP_WAT_SLICE) { capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC; - capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC_256; capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_WIRELESS_CRYPTO_EXT; } - if (fusectl1 & ICP_ACCEL_GEN4_MASK_EIA3_SLICE) { + if (fusectl1 & ICP_ACCEL_GEN4_MASK_EIA3_SLICE) capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC; - capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC_256; - } - - if (fusectl1 & ICP_ACCEL_GEN4_MASK_ZUC_256_SLICE) - capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_ZUC_256; capabilities_asym = ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | ICP_ACCEL_CAPABILITIES_SM2 | @@ -303,11 +296,13 @@ static void adf_init_rl_data(struct adf_rl_hw_data *rl_data) rl_data->pcie_scale_div = ADF_420XX_RL_PCIE_SCALE_FACTOR_DIV; rl_data->pcie_scale_mul = ADF_420XX_RL_PCIE_SCALE_FACTOR_MUL; rl_data->dcpr_correction = ADF_420XX_RL_DCPR_CORRECTION; - rl_data->max_tp[ADF_SVC_ASYM] = ADF_420XX_RL_MAX_TP_ASYM; - rl_data->max_tp[ADF_SVC_SYM] = ADF_420XX_RL_MAX_TP_SYM; - rl_data->max_tp[ADF_SVC_DC] = ADF_420XX_RL_MAX_TP_DC; + rl_data->max_tp[SVC_ASYM] = ADF_420XX_RL_MAX_TP_ASYM; + rl_data->max_tp[SVC_SYM] = ADF_420XX_RL_MAX_TP_SYM; + rl_data->max_tp[SVC_DC] = ADF_420XX_RL_MAX_TP_DC; rl_data->scan_interval = ADF_420XX_RL_SCANS_PER_SEC; rl_data->scale_ref = ADF_420XX_RL_SLICE_REF; + + adf_gen4_init_num_svc_aes(rl_data); } static int get_rp_group(struct adf_accel_dev *accel_dev, u32 ae_mask) @@ -473,6 +468,7 @@ void adf_init_hw_data_420xx(struct adf_hw_device_data *hw_data, u32 dev_id) hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE; hw_data->clock_frequency = ADF_420XX_AE_FREQ; hw_data->services_supported = adf_gen4_services_supported; + hw_data->get_svc_slice_cnt = adf_gen4_get_svc_slice_cnt; adf_gen4_set_err_mask(&hw_data->dev_err_mask); adf_gen4_init_hw_csr_ops(&hw_data->csr_ops); diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c b/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c index bd0b1b1015c0..740f68a36ac5 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -221,11 +222,13 @@ static void adf_init_rl_data(struct adf_rl_hw_data *rl_data) rl_data->pcie_scale_div = ADF_4XXX_RL_PCIE_SCALE_FACTOR_DIV; rl_data->pcie_scale_mul = ADF_4XXX_RL_PCIE_SCALE_FACTOR_MUL; rl_data->dcpr_correction = ADF_4XXX_RL_DCPR_CORRECTION; - rl_data->max_tp[ADF_SVC_ASYM] = ADF_4XXX_RL_MAX_TP_ASYM; - rl_data->max_tp[ADF_SVC_SYM] = ADF_4XXX_RL_MAX_TP_SYM; - rl_data->max_tp[ADF_SVC_DC] = ADF_4XXX_RL_MAX_TP_DC; + rl_data->max_tp[SVC_ASYM] = ADF_4XXX_RL_MAX_TP_ASYM; + rl_data->max_tp[SVC_SYM] = ADF_4XXX_RL_MAX_TP_SYM; + rl_data->max_tp[SVC_DC] = ADF_4XXX_RL_MAX_TP_DC; rl_data->scan_interval = ADF_4XXX_RL_SCANS_PER_SEC; rl_data->scale_ref = ADF_4XXX_RL_SLICE_REF; + + adf_gen4_init_num_svc_aes(rl_data); } static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev) @@ -448,8 +451,8 @@ void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id) hw_data->get_ring_to_svc_map = adf_gen4_get_ring_to_svc_map; hw_data->disable_iov = adf_disable_sriov; hw_data->ring_pair_reset = adf_gen4_ring_pair_reset; - hw_data->bank_state_save = adf_gen4_bank_state_save; - hw_data->bank_state_restore = adf_gen4_bank_state_restore; + hw_data->bank_state_save = adf_bank_state_save; + hw_data->bank_state_restore = adf_bank_state_restore; hw_data->enable_pm = adf_gen4_enable_pm; hw_data->handle_pm_interrupt = adf_gen4_handle_pm_interrupt; hw_data->dev_config = adf_gen4_dev_config; @@ -459,6 +462,7 @@ void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id) hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE; hw_data->clock_frequency = ADF_4XXX_AE_FREQ; hw_data->services_supported = adf_gen4_services_supported; + hw_data->get_svc_slice_cnt = adf_gen4_get_svc_slice_cnt; adf_gen4_set_err_mask(&hw_data->dev_err_mask); adf_gen4_init_hw_csr_ops(&hw_data->csr_ops); diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c index 359a6447ccb8..bed88d3ce8ca 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include "adf_6xxx_hw_data.h" #include "icp_qat_fw_comp.h" @@ -76,6 +78,10 @@ static const unsigned long thrd_mask_dcc[ADF_6XXX_MAX_ACCELENGINES] = { 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x03, 0x03, 0x00 }; +static const unsigned long thrd_mask_dcpr[ADF_6XXX_MAX_ACCELENGINES] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00 +}; + static const char *const adf_6xxx_fw_objs[] = { [ADF_FW_CY_OBJ] = ADF_6XXX_CY_OBJ, [ADF_FW_DC_OBJ] = ADF_6XXX_DC_OBJ, @@ -97,7 +103,7 @@ static bool services_supported(unsigned long mask) { int num_svc; - if (mask >= BIT(SVC_BASE_COUNT)) + if (mask >= BIT(SVC_COUNT)) return false; num_svc = hweight_long(mask); @@ -126,10 +132,13 @@ static int get_service(unsigned long *mask) if (test_and_clear_bit(SVC_DCC, mask)) return SVC_DCC; + if (test_and_clear_bit(SVC_DECOMP, mask)) + return SVC_DECOMP; + return -EINVAL; } -static enum adf_cfg_service_type get_ring_type(enum adf_services service) +static enum adf_cfg_service_type get_ring_type(unsigned int service) { switch (service) { case SVC_SYM: @@ -139,12 +148,14 @@ static enum adf_cfg_service_type get_ring_type(enum adf_services service) case SVC_DC: case SVC_DCC: return COMP; + case SVC_DECOMP: + return DECOMP; default: return UNUSED; } } -static const unsigned long *get_thrd_mask(enum adf_services service) +static const unsigned long *get_thrd_mask(unsigned int service) { switch (service) { case SVC_SYM: @@ -155,6 +166,8 @@ static const unsigned long *get_thrd_mask(enum adf_services service) return thrd_mask_cpr; case SVC_DCC: return thrd_mask_dcc; + case SVC_DECOMP: + return thrd_mask_dcpr; default: return NULL; } @@ -511,6 +524,55 @@ static int adf_gen6_init_thd2arb_map(struct adf_accel_dev *accel_dev) return 0; } +static void init_num_svc_aes(struct adf_rl_hw_data *device_data) +{ + enum adf_fw_objs obj_type, obj_iter; + unsigned int svc, i, num_grp; + u32 ae_mask; + + for (svc = 0; svc < SVC_BASE_COUNT; svc++) { + switch (svc) { + case SVC_SYM: + case SVC_ASYM: + obj_type = ADF_FW_CY_OBJ; + break; + case SVC_DC: + case SVC_DECOMP: + obj_type = ADF_FW_DC_OBJ; + break; + } + + num_grp = ARRAY_SIZE(adf_default_fw_config); + for (i = 0; i < num_grp; i++) { + obj_iter = adf_default_fw_config[i].obj; + if (obj_iter == obj_type) { + ae_mask = adf_default_fw_config[i].ae_mask; + device_data->svc_ae_mask[svc] = hweight32(ae_mask); + break; + } + } + } +} + +static u32 adf_gen6_get_svc_slice_cnt(struct adf_accel_dev *accel_dev, + enum adf_base_services svc) +{ + struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data; + + switch (svc) { + case SVC_SYM: + return device_data->slices.cph_cnt; + case SVC_ASYM: + return device_data->slices.pke_cnt; + case SVC_DC: + return device_data->slices.cpr_cnt + device_data->slices.dcpr_cnt; + case SVC_DECOMP: + return device_data->slices.dcpr_cnt; + default: + return 0; + } +} + static void set_vc_csr_for_bank(void __iomem *csr, u32 bank_number) { u32 value; @@ -520,8 +582,8 @@ static void set_vc_csr_for_bank(void __iomem *csr, u32 bank_number) * driver must program the ringmodectl CSRs. */ value = ADF_CSR_RD(csr, ADF_GEN6_CSR_RINGMODECTL(bank_number)); - value |= FIELD_PREP(ADF_GEN6_RINGMODECTL_TC_MASK, ADF_GEN6_RINGMODECTL_TC_DEFAULT); - value |= FIELD_PREP(ADF_GEN6_RINGMODECTL_TC_EN_MASK, ADF_GEN6_RINGMODECTL_TC_EN_OP1); + FIELD_MODIFY(ADF_GEN6_RINGMODECTL_TC_MASK, &value, ADF_GEN6_RINGMODECTL_TC_DEFAULT); + FIELD_MODIFY(ADF_GEN6_RINGMODECTL_TC_EN_MASK, &value, ADF_GEN6_RINGMODECTL_TC_EN_OP1); ADF_CSR_WR(csr, ADF_GEN6_CSR_RINGMODECTL(bank_number), value); } @@ -537,7 +599,7 @@ static int set_vc_config(struct adf_accel_dev *accel_dev) * Read PVC0CTL then write the masked values. */ pci_read_config_dword(pdev, ADF_GEN6_PVC0CTL_OFFSET, &value); - value |= FIELD_PREP(ADF_GEN6_PVC0CTL_TCVCMAP_MASK, ADF_GEN6_PVC0CTL_TCVCMAP_DEFAULT); + FIELD_MODIFY(ADF_GEN6_PVC0CTL_TCVCMAP_MASK, &value, ADF_GEN6_PVC0CTL_TCVCMAP_DEFAULT); err = pci_write_config_dword(pdev, ADF_GEN6_PVC0CTL_OFFSET, value); if (err) { dev_err(&GET_DEV(accel_dev), "pci write to PVC0CTL failed\n"); @@ -546,8 +608,8 @@ static int set_vc_config(struct adf_accel_dev *accel_dev) /* Read PVC1CTL then write masked values */ pci_read_config_dword(pdev, ADF_GEN6_PVC1CTL_OFFSET, &value); - value |= FIELD_PREP(ADF_GEN6_PVC1CTL_TCVCMAP_MASK, ADF_GEN6_PVC1CTL_TCVCMAP_DEFAULT); - value |= FIELD_PREP(ADF_GEN6_PVC1CTL_VCEN_MASK, ADF_GEN6_PVC1CTL_VCEN_ON); + FIELD_MODIFY(ADF_GEN6_PVC1CTL_TCVCMAP_MASK, &value, ADF_GEN6_PVC1CTL_TCVCMAP_DEFAULT); + FIELD_MODIFY(ADF_GEN6_PVC1CTL_VCEN_MASK, &value, ADF_GEN6_PVC1CTL_VCEN_ON); err = pci_write_config_dword(pdev, ADF_GEN6_PVC1CTL_OFFSET, value); if (err) dev_err(&GET_DEV(accel_dev), "pci write to PVC1CTL failed\n"); @@ -618,7 +680,6 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CHACHA_POLY; capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AESGCM_SPC; capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AES_V2; - capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER; } if (fusectl1 & ICP_ACCEL_GEN6_MASK_AUTH_SLICE) { capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION; @@ -627,7 +688,15 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) capabilities_sym &= ~ICP_ACCEL_CAPABILITIES_CIPHER; } - capabilities_asym = 0; + capabilities_asym = ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | + ICP_ACCEL_CAPABILITIES_SM2 | + ICP_ACCEL_CAPABILITIES_ECEDMONT; + + if (fusectl1 & ICP_ACCEL_GEN6_MASK_PKE_SLICE) { + capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; + capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_SM2; + capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_ECEDMONT; + } capabilities_dc = ICP_ACCEL_CAPABILITIES_COMPRESSION | ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION | @@ -648,7 +717,7 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) caps |= capabilities_asym; if (test_bit(SVC_SYM, &mask)) caps |= capabilities_sym; - if (test_bit(SVC_DC, &mask)) + if (test_bit(SVC_DC, &mask) || test_bit(SVC_DECOMP, &mask)) caps |= capabilities_dc; if (test_bit(SVC_DCC, &mask)) { /* @@ -744,7 +813,16 @@ static int adf_init_device(struct adf_accel_dev *accel_dev) static int enable_pm(struct adf_accel_dev *accel_dev) { - return adf_init_admin_pm(accel_dev, ADF_GEN6_PM_DEFAULT_IDLE_FILTER); + int ret; + + ret = adf_init_admin_pm(accel_dev, ADF_GEN6_PM_DEFAULT_IDLE_FILTER); + if (ret) + return ret; + + /* Initialize PM internal data */ + adf_gen6_init_dev_pm_data(accel_dev); + + return 0; } static int dev_config(struct adf_accel_dev *accel_dev) @@ -776,6 +854,25 @@ static int dev_config(struct adf_accel_dev *accel_dev) return ret; } +static void adf_gen6_init_rl_data(struct adf_rl_hw_data *rl_data) +{ + rl_data->pciout_tb_offset = ADF_GEN6_RL_TOKEN_PCIEOUT_BUCKET_OFFSET; + rl_data->pciin_tb_offset = ADF_GEN6_RL_TOKEN_PCIEIN_BUCKET_OFFSET; + rl_data->r2l_offset = ADF_GEN6_RL_R2L_OFFSET; + rl_data->l2c_offset = ADF_GEN6_RL_L2C_OFFSET; + rl_data->c2s_offset = ADF_GEN6_RL_C2S_OFFSET; + rl_data->pcie_scale_div = ADF_6XXX_RL_PCIE_SCALE_FACTOR_DIV; + rl_data->pcie_scale_mul = ADF_6XXX_RL_PCIE_SCALE_FACTOR_MUL; + rl_data->max_tp[SVC_ASYM] = ADF_6XXX_RL_MAX_TP_ASYM; + rl_data->max_tp[SVC_SYM] = ADF_6XXX_RL_MAX_TP_SYM; + rl_data->max_tp[SVC_DC] = ADF_6XXX_RL_MAX_TP_DC; + rl_data->max_tp[SVC_DECOMP] = ADF_6XXX_RL_MAX_TP_DECOMP; + rl_data->scan_interval = ADF_6XXX_RL_SCANS_PER_SEC; + rl_data->scale_ref = ADF_6XXX_RL_SLICE_REF; + + init_num_svc_aes(rl_data); +} + void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &adf_6xxx_class; @@ -824,6 +921,8 @@ void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data) hw_data->disable_iov = adf_disable_sriov; hw_data->ring_pair_reset = ring_pair_reset; hw_data->dev_config = dev_config; + hw_data->bank_state_save = adf_bank_state_save; + hw_data->bank_state_restore = adf_bank_state_restore; hw_data->get_hb_clock = get_heartbeat_clock; hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE; hw_data->start_timer = adf_timer_start; @@ -831,11 +930,17 @@ void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data) hw_data->init_device = adf_init_device; hw_data->enable_pm = enable_pm; hw_data->services_supported = services_supported; + hw_data->num_rps = ADF_GEN6_ETR_MAX_BANKS; + hw_data->clock_frequency = ADF_6XXX_AE_FREQ; + hw_data->get_svc_slice_cnt = adf_gen6_get_svc_slice_cnt; adf_gen6_init_hw_csr_ops(&hw_data->csr_ops); adf_gen6_init_pf_pfvf_ops(&hw_data->pfvf_ops); adf_gen6_init_dc_ops(&hw_data->dc_ops); + adf_gen6_init_vf_mig_ops(&hw_data->vfmig_ops); adf_gen6_init_ras_ops(&hw_data->ras_ops); + adf_gen6_init_tl_data(&hw_data->tl_data); + adf_gen6_init_rl_data(&hw_data->rl_data); } void adf_clean_hw_data_6xxx(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h index 78e2e2c5816e..d822911fe68c 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h @@ -99,7 +99,7 @@ #define ADF_GEN6_PVC0CTL_OFFSET 0x204 #define ADF_GEN6_PVC0CTL_TCVCMAP_OFFSET 1 #define ADF_GEN6_PVC0CTL_TCVCMAP_MASK GENMASK(7, 1) -#define ADF_GEN6_PVC0CTL_TCVCMAP_DEFAULT 0x7F +#define ADF_GEN6_PVC0CTL_TCVCMAP_DEFAULT 0x3F /* VC1 Resource Control Register */ #define ADF_GEN6_PVC1CTL_OFFSET 0x210 @@ -122,6 +122,13 @@ /* Number of heartbeat counter pairs */ #define ADF_NUM_HB_CNT_PER_AE ADF_NUM_THREADS_PER_AE +/* Rate Limiting */ +#define ADF_GEN6_RL_R2L_OFFSET 0x508000 +#define ADF_GEN6_RL_L2C_OFFSET 0x509000 +#define ADF_GEN6_RL_C2S_OFFSET 0x508818 +#define ADF_GEN6_RL_TOKEN_PCIEIN_BUCKET_OFFSET 0x508800 +#define ADF_GEN6_RL_TOKEN_PCIEOUT_BUCKET_OFFSET 0x508804 + /* Physical function fuses */ #define ADF_6XXX_ACCELENGINES_MASK GENMASK(8, 0) #define ADF_6XXX_ADMIN_AE_MASK GENMASK(8, 8) @@ -133,6 +140,19 @@ #define ADF_6XXX_DC_OBJ "qat_6xxx_dc.bin" #define ADF_6XXX_ADMIN_OBJ "qat_6xxx_admin.bin" +/* RL constants */ +#define ADF_6XXX_RL_PCIE_SCALE_FACTOR_DIV 100 +#define ADF_6XXX_RL_PCIE_SCALE_FACTOR_MUL 102 +#define ADF_6XXX_RL_SCANS_PER_SEC 954 +#define ADF_6XXX_RL_MAX_TP_ASYM 173750UL +#define ADF_6XXX_RL_MAX_TP_SYM 95000UL +#define ADF_6XXX_RL_MAX_TP_DC 40000UL +#define ADF_6XXX_RL_MAX_TP_DECOMP 40000UL +#define ADF_6XXX_RL_SLICE_REF 1000UL + +/* Clock frequency */ +#define ADF_6XXX_AE_FREQ (1000 * HZ_PER_MHZ) + enum icp_qat_gen6_slice_mask { ICP_ACCEL_GEN6_MASK_UCS_SLICE = BIT(0), ICP_ACCEL_GEN6_MASK_AUTH_SLICE = BIT(1), diff --git a/drivers/crypto/intel/qat/qat_common/Makefile b/drivers/crypto/intel/qat/qat_common/Makefile index 66bb295ace28..89845754841b 100644 --- a/drivers/crypto/intel/qat/qat_common/Makefile +++ b/drivers/crypto/intel/qat/qat_common/Makefile @@ -4,6 +4,7 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"CRYPTO_QAT"' intel_qat-y := adf_accel_engine.o \ adf_admin.o \ adf_aer.o \ + adf_bank_state.o \ adf_cfg.o \ adf_cfg_services.o \ adf_clock.o \ @@ -48,9 +49,12 @@ intel_qat-$(CONFIG_DEBUG_FS) += adf_cnv_dbgfs.o \ adf_fw_counters.o \ adf_gen4_pm_debugfs.o \ adf_gen4_tl.o \ + adf_gen6_pm_dbgfs.o \ + adf_gen6_tl.o \ adf_heartbeat_dbgfs.o \ adf_heartbeat.o \ adf_pm_dbgfs.o \ + adf_pm_dbgfs_utils.o \ adf_telemetry.o \ adf_tl_debugfs.o \ adf_transport_debug.o diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 2ee526063213..9fe3239f0114 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -157,39 +157,7 @@ struct admin_info { u32 mailbox_offset; }; -struct ring_config { - u64 base; - u32 config; - u32 head; - u32 tail; - u32 reserved0; -}; - -struct bank_state { - u32 ringstat0; - u32 ringstat1; - u32 ringuostat; - u32 ringestat; - u32 ringnestat; - u32 ringnfstat; - u32 ringfstat; - u32 ringcstat0; - u32 ringcstat1; - u32 ringcstat2; - u32 ringcstat3; - u32 iaintflagen; - u32 iaintflagreg; - u32 iaintflagsrcsel0; - u32 iaintflagsrcsel1; - u32 iaintcolen; - u32 iaintcolctl; - u32 iaintflagandcolen; - u32 ringexpstat; - u32 ringexpintenable; - u32 ringsrvarben; - u32 reserved0; - struct ring_config rings[ADF_ETR_MAX_RINGS_PER_BANK]; -}; +struct adf_bank_state; struct adf_hw_csr_ops { u64 (*build_csr_ring_base_addr)(dma_addr_t addr, u32 size); @@ -338,9 +306,9 @@ struct adf_hw_device_data { void (*set_ssm_wdtimer)(struct adf_accel_dev *accel_dev); int (*ring_pair_reset)(struct adf_accel_dev *accel_dev, u32 bank_nr); int (*bank_state_save)(struct adf_accel_dev *accel_dev, u32 bank_number, - struct bank_state *state); + struct adf_bank_state *state); int (*bank_state_restore)(struct adf_accel_dev *accel_dev, - u32 bank_number, struct bank_state *state); + u32 bank_number, struct adf_bank_state *state); void (*reset_device)(struct adf_accel_dev *accel_dev); void (*set_msix_rttable)(struct adf_accel_dev *accel_dev); const char *(*uof_get_name)(struct adf_accel_dev *accel_dev, u32 obj_num); @@ -351,6 +319,8 @@ struct adf_hw_device_data { u32 (*get_ena_thd_mask)(struct adf_accel_dev *accel_dev, u32 obj_num); int (*dev_config)(struct adf_accel_dev *accel_dev); bool (*services_supported)(unsigned long mask); + u32 (*get_svc_slice_cnt)(struct adf_accel_dev *accel_dev, + enum adf_base_services svc); struct adf_pfvf_ops pfvf_ops; struct adf_hw_csr_ops csr_ops; struct adf_dc_ops dc_ops; diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 4cb8bd83f570..35679b21ff63 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -229,7 +229,7 @@ const struct pci_error_handlers adf_err_handler = { }; EXPORT_SYMBOL_GPL(adf_err_handler); -int adf_dev_autoreset(struct adf_accel_dev *accel_dev) +static int adf_dev_autoreset(struct adf_accel_dev *accel_dev) { if (accel_dev->autoreset_on_error) return adf_dev_aer_schedule_reset(accel_dev, ADF_DEV_RESET_ASYNC); diff --git a/drivers/crypto/intel/qat/qat_common/adf_bank_state.c b/drivers/crypto/intel/qat/qat_common/adf_bank_state.c new file mode 100644 index 000000000000..225d55d56a4b --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_bank_state.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2025 Intel Corporation */ + +#define pr_fmt(fmt) "QAT: " fmt + +#include +#include +#include +#include "adf_accel_devices.h" +#include "adf_bank_state.h" +#include "adf_common_drv.h" + +/* Ring interrupt masks */ +#define ADF_RP_INT_SRC_SEL_F_RISE_MASK GENMASK(1, 0) +#define ADF_RP_INT_SRC_SEL_F_FALL_MASK GENMASK(2, 0) +#define ADF_RP_INT_SRC_SEL_RANGE_WIDTH 4 + +static inline int check_stat(u32 (*op)(void __iomem *, u32), u32 expect_val, + const char *name, void __iomem *base, u32 bank) +{ + u32 actual_val = op(base, bank); + + if (expect_val == actual_val) + return 0; + + pr_err("Fail to restore %s register. Expected %#x, actual %#x\n", + name, expect_val, actual_val); + + return -EINVAL; +} + +static void bank_state_save(struct adf_hw_csr_ops *ops, void __iomem *base, + u32 bank, struct adf_bank_state *state, u32 num_rings) +{ + u32 i; + + state->ringstat0 = ops->read_csr_stat(base, bank); + state->ringuostat = ops->read_csr_uo_stat(base, bank); + state->ringestat = ops->read_csr_e_stat(base, bank); + state->ringnestat = ops->read_csr_ne_stat(base, bank); + state->ringnfstat = ops->read_csr_nf_stat(base, bank); + state->ringfstat = ops->read_csr_f_stat(base, bank); + state->ringcstat0 = ops->read_csr_c_stat(base, bank); + state->iaintflagen = ops->read_csr_int_en(base, bank); + state->iaintflagreg = ops->read_csr_int_flag(base, bank); + state->iaintflagsrcsel0 = ops->read_csr_int_srcsel(base, bank); + state->iaintcolen = ops->read_csr_int_col_en(base, bank); + state->iaintcolctl = ops->read_csr_int_col_ctl(base, bank); + state->iaintflagandcolen = ops->read_csr_int_flag_and_col(base, bank); + state->ringexpstat = ops->read_csr_exp_stat(base, bank); + state->ringexpintenable = ops->read_csr_exp_int_en(base, bank); + state->ringsrvarben = ops->read_csr_ring_srv_arb_en(base, bank); + + for (i = 0; i < num_rings; i++) { + state->rings[i].head = ops->read_csr_ring_head(base, bank, i); + state->rings[i].tail = ops->read_csr_ring_tail(base, bank, i); + state->rings[i].config = ops->read_csr_ring_config(base, bank, i); + state->rings[i].base = ops->read_csr_ring_base(base, bank, i); + } +} + +static int bank_state_restore(struct adf_hw_csr_ops *ops, void __iomem *base, + u32 bank, struct adf_bank_state *state, u32 num_rings, + int tx_rx_gap) +{ + u32 val, tmp_val, i; + int ret; + + for (i = 0; i < num_rings; i++) + ops->write_csr_ring_base(base, bank, i, state->rings[i].base); + + for (i = 0; i < num_rings; i++) + ops->write_csr_ring_config(base, bank, i, state->rings[i].config); + + for (i = 0; i < num_rings / 2; i++) { + int tx = i * (tx_rx_gap + 1); + int rx = tx + tx_rx_gap; + + ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head); + ops->write_csr_ring_tail(base, bank, tx, state->rings[tx].tail); + + /* + * The TX ring head needs to be updated again to make sure that + * the HW will not consider the ring as full when it is empty + * and the correct state flags are set to match the recovered state. + */ + if (state->ringestat & BIT(tx)) { + val = ops->read_csr_int_srcsel(base, bank); + val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK; + ops->write_csr_int_srcsel_w_val(base, bank, val); + ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head); + } + + ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail); + val = ops->read_csr_int_srcsel(base, bank); + val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH; + ops->write_csr_int_srcsel_w_val(base, bank, val); + + ops->write_csr_ring_head(base, bank, rx, state->rings[rx].head); + val = ops->read_csr_int_srcsel(base, bank); + val |= ADF_RP_INT_SRC_SEL_F_FALL_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH; + ops->write_csr_int_srcsel_w_val(base, bank, val); + + /* + * The RX ring tail needs to be updated again to make sure that + * the HW will not consider the ring as empty when it is full + * and the correct state flags are set to match the recovered state. + */ + if (state->ringfstat & BIT(rx)) + ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail); + } + + ops->write_csr_int_flag_and_col(base, bank, state->iaintflagandcolen); + ops->write_csr_int_en(base, bank, state->iaintflagen); + ops->write_csr_int_col_en(base, bank, state->iaintcolen); + ops->write_csr_int_srcsel_w_val(base, bank, state->iaintflagsrcsel0); + ops->write_csr_exp_int_en(base, bank, state->ringexpintenable); + ops->write_csr_int_col_ctl(base, bank, state->iaintcolctl); + + /* + * Verify whether any exceptions were raised during the bank save process. + * If exceptions occurred, the status and exception registers cannot + * be directly restored. Consequently, further restoration is not + * feasible, and the current state of the ring should be maintained. + */ + val = state->ringexpstat; + if (val) { + pr_info("Bank %u state not fully restored due to exception in saved state (%#x)\n", + bank, val); + return 0; + } + + /* Ensure that the restoration process completed without exceptions */ + tmp_val = ops->read_csr_exp_stat(base, bank); + if (tmp_val) { + pr_err("Bank %u restored with exception: %#x\n", bank, tmp_val); + return -EFAULT; + } + + ops->write_csr_ring_srv_arb_en(base, bank, state->ringsrvarben); + + /* Check that all ring statuses match the saved state. */ + ret = check_stat(ops->read_csr_stat, state->ringstat0, "ringstat", + base, bank); + if (ret) + return ret; + + ret = check_stat(ops->read_csr_e_stat, state->ringestat, "ringestat", + base, bank); + if (ret) + return ret; + + ret = check_stat(ops->read_csr_ne_stat, state->ringnestat, "ringnestat", + base, bank); + if (ret) + return ret; + + ret = check_stat(ops->read_csr_nf_stat, state->ringnfstat, "ringnfstat", + base, bank); + if (ret) + return ret; + + ret = check_stat(ops->read_csr_f_stat, state->ringfstat, "ringfstat", + base, bank); + if (ret) + return ret; + + ret = check_stat(ops->read_csr_c_stat, state->ringcstat0, "ringcstat", + base, bank); + if (ret) + return ret; + + return 0; +} + +/** + * adf_bank_state_save() - save state of bank-related registers + * @accel_dev: Pointer to the device structure + * @bank_number: Bank number + * @state: Pointer to bank state structure + * + * This function saves the state of a bank by reading the bank CSRs and + * writing them in the @state structure. + * + * Returns 0 on success, error code otherwise + */ +int adf_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number, + struct adf_bank_state *state) +{ + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); + void __iomem *csr_base = adf_get_etr_base(accel_dev); + + if (bank_number >= hw_data->num_banks || !state) + return -EINVAL; + + dev_dbg(&GET_DEV(accel_dev), "Saving state of bank %d\n", bank_number); + + bank_state_save(csr_ops, csr_base, bank_number, state, + hw_data->num_rings_per_bank); + + return 0; +} +EXPORT_SYMBOL_GPL(adf_bank_state_save); + +/** + * adf_bank_state_restore() - restore state of bank-related registers + * @accel_dev: Pointer to the device structure + * @bank_number: Bank number + * @state: Pointer to bank state structure + * + * This function attempts to restore the state of a bank by writing the + * bank CSRs to the values in the state structure. + * + * Returns 0 on success, error code otherwise + */ +int adf_bank_state_restore(struct adf_accel_dev *accel_dev, u32 bank_number, + struct adf_bank_state *state) +{ + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); + void __iomem *csr_base = adf_get_etr_base(accel_dev); + int ret; + + if (bank_number >= hw_data->num_banks || !state) + return -EINVAL; + + dev_dbg(&GET_DEV(accel_dev), "Restoring state of bank %d\n", bank_number); + + ret = bank_state_restore(csr_ops, csr_base, bank_number, state, + hw_data->num_rings_per_bank, hw_data->tx_rx_gap); + if (ret) + dev_err(&GET_DEV(accel_dev), + "Unable to restore state of bank %d\n", bank_number); + + return ret; +} +EXPORT_SYMBOL_GPL(adf_bank_state_restore); diff --git a/drivers/crypto/intel/qat/qat_common/adf_bank_state.h b/drivers/crypto/intel/qat/qat_common/adf_bank_state.h new file mode 100644 index 000000000000..48b573d692dd --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_bank_state.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2025 Intel Corporation */ +#ifndef ADF_BANK_STATE_H_ +#define ADF_BANK_STATE_H_ + +#include + +struct adf_accel_dev; + +struct ring_config { + u64 base; + u32 config; + u32 head; + u32 tail; + u32 reserved0; +}; + +struct adf_bank_state { + u32 ringstat0; + u32 ringstat1; + u32 ringuostat; + u32 ringestat; + u32 ringnestat; + u32 ringnfstat; + u32 ringfstat; + u32 ringcstat0; + u32 ringcstat1; + u32 ringcstat2; + u32 ringcstat3; + u32 iaintflagen; + u32 iaintflagreg; + u32 iaintflagsrcsel0; + u32 iaintflagsrcsel1; + u32 iaintcolen; + u32 iaintcolctl; + u32 iaintflagandcolen; + u32 ringexpstat; + u32 ringexpintenable; + u32 ringsrvarben; + u32 reserved0; + struct ring_config rings[ADF_ETR_MAX_RINGS_PER_BANK]; +}; + +int adf_bank_state_restore(struct adf_accel_dev *accel_dev, u32 bank_number, + struct adf_bank_state *state); +int adf_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number, + struct adf_bank_state *state); + +#endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h index 15fdf9854b81..81e9e9d7eccd 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h @@ -29,6 +29,7 @@ enum adf_cfg_service_type { COMP, SYM, ASYM, + DECOMP, USED }; diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c index c39871291da7..7d00bcb41ce7 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c @@ -7,6 +7,7 @@ #include #include #include "adf_cfg.h" +#include "adf_cfg_common.h" #include "adf_cfg_services.h" #include "adf_cfg_strings.h" @@ -15,13 +16,14 @@ static const char *const adf_cfg_services[] = { [SVC_SYM] = ADF_CFG_SYM, [SVC_DC] = ADF_CFG_DC, [SVC_DCC] = ADF_CFG_DCC, + [SVC_DECOMP] = ADF_CFG_DECOMP, }; /* * Ensure that the size of the array matches the number of services, - * SVC_BASE_COUNT, that is used to size the bitmap. + * SVC_COUNT, that is used to size the bitmap. */ -static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_BASE_COUNT); +static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_COUNT); /* * Ensure that the maximum number of concurrent services that can be @@ -34,7 +36,7 @@ static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC); * Ensure that the number of services fit a single unsigned long, as each * service is represented by a bit in the mask. */ -static_assert(BITS_PER_LONG >= SVC_BASE_COUNT); +static_assert(BITS_PER_LONG >= SVC_COUNT); /* * Ensure that size of the concatenation of all service strings is smaller @@ -43,6 +45,7 @@ static_assert(BITS_PER_LONG >= SVC_BASE_COUNT); static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER ADF_CFG_ASYM ADF_SERVICES_DELIMITER ADF_CFG_DC ADF_SERVICES_DELIMITER + ADF_CFG_DECOMP ADF_SERVICES_DELIMITER ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES); static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf, @@ -88,7 +91,7 @@ static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len) if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES) return -ENOSPC; - for_each_set_bit(bit, &mask, SVC_BASE_COUNT) { + for_each_set_bit(bit, &mask, SVC_COUNT) { if (offset) offset += scnprintf(buf + offset, len - offset, ADF_SERVICES_DELIMITER); @@ -167,9 +170,43 @@ int adf_get_service_enabled(struct adf_accel_dev *accel_dev) if (test_bit(SVC_DC, &mask)) return SVC_DC; + if (test_bit(SVC_DECOMP, &mask)) + return SVC_DECOMP; + if (test_bit(SVC_DCC, &mask)) return SVC_DCC; return -EINVAL; } EXPORT_SYMBOL_GPL(adf_get_service_enabled); + +enum adf_cfg_service_type adf_srv_to_cfg_svc_type(enum adf_base_services svc) +{ + switch (svc) { + case SVC_ASYM: + return ASYM; + case SVC_SYM: + return SYM; + case SVC_DC: + return COMP; + case SVC_DECOMP: + return DECOMP; + default: + return UNUSED; + } +} + +bool adf_is_service_enabled(struct adf_accel_dev *accel_dev, enum adf_base_services svc) +{ + enum adf_cfg_service_type arb_srv = adf_srv_to_cfg_svc_type(svc); + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + u8 rps_per_bundle = hw_data->num_banks_per_vf; + int i; + + for (i = 0; i < rps_per_bundle; i++) { + if (GET_SRV_TYPE(accel_dev, i) == arb_srv) + return true; + } + + return false; +} diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.h index 3742c450878f..913d717280af 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.h @@ -7,16 +7,21 @@ struct adf_accel_dev; -enum adf_services { +enum adf_base_services { SVC_ASYM = 0, SVC_SYM, SVC_DC, - SVC_DCC, + SVC_DECOMP, SVC_BASE_COUNT }; +enum adf_extended_services { + SVC_DCC = SVC_BASE_COUNT, + SVC_COUNT +}; + enum adf_composed_services { - SVC_SYM_ASYM = SVC_BASE_COUNT, + SVC_SYM_ASYM = SVC_COUNT, SVC_SYM_DC, SVC_ASYM_DC, }; @@ -33,5 +38,7 @@ int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in, size_t in_len, char *out, size_t out_len); int adf_get_service_enabled(struct adf_accel_dev *accel_dev); int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask); +enum adf_cfg_service_type adf_srv_to_cfg_svc_type(enum adf_base_services svc); +bool adf_is_service_enabled(struct adf_accel_dev *accel_dev, enum adf_base_services svc); #endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_strings.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_strings.h index b79982c4a856..30107a02ee7f 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_strings.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_strings.h @@ -24,6 +24,7 @@ #define ADF_CY "Cy" #define ADF_DC "Dc" #define ADF_CFG_DC "dc" +#define ADF_CFG_DECOMP "decomp" #define ADF_CFG_CY "sym;asym" #define ADF_CFG_SYM "sym" #define ADF_CFG_ASYM "asym" diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index eaa6388a6678..6cf3a95489e8 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -86,7 +86,6 @@ int adf_ae_stop(struct adf_accel_dev *accel_dev); extern const struct pci_error_handlers adf_err_handler; void adf_reset_sbr(struct adf_accel_dev *accel_dev); void adf_reset_flr(struct adf_accel_dev *accel_dev); -int adf_dev_autoreset(struct adf_accel_dev *accel_dev); void adf_dev_restore(struct adf_accel_dev *accel_dev); int adf_init_aer(void); void adf_exit_aer(void); @@ -189,6 +188,7 @@ void adf_exit_misc_wq(void); bool adf_misc_wq_queue_work(struct work_struct *work); bool adf_misc_wq_queue_delayed_work(struct delayed_work *work, unsigned long delay); +void adf_misc_wq_flush(void); #if defined(CONFIG_PCI_IOV) int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.c b/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.c index 0406cb09c5bb..349fdb323763 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.c +++ b/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) /* Copyright(c) 2020 Intel Corporation */ + +#define pr_fmt(fmt) "QAT: " fmt + #include #include #include @@ -259,7 +262,10 @@ bool adf_gen4_services_supported(unsigned long mask) { unsigned long num_svc = hweight_long(mask); - if (mask >= BIT(SVC_BASE_COUNT)) + if (mask >= BIT(SVC_COUNT)) + return false; + + if (test_bit(SVC_DECOMP, &mask)) return false; switch (num_svc) { @@ -485,187 +491,6 @@ int adf_gen4_bank_drain_start(struct adf_accel_dev *accel_dev, return ret; } -static void bank_state_save(struct adf_hw_csr_ops *ops, void __iomem *base, - u32 bank, struct bank_state *state, u32 num_rings) -{ - u32 i; - - state->ringstat0 = ops->read_csr_stat(base, bank); - state->ringuostat = ops->read_csr_uo_stat(base, bank); - state->ringestat = ops->read_csr_e_stat(base, bank); - state->ringnestat = ops->read_csr_ne_stat(base, bank); - state->ringnfstat = ops->read_csr_nf_stat(base, bank); - state->ringfstat = ops->read_csr_f_stat(base, bank); - state->ringcstat0 = ops->read_csr_c_stat(base, bank); - state->iaintflagen = ops->read_csr_int_en(base, bank); - state->iaintflagreg = ops->read_csr_int_flag(base, bank); - state->iaintflagsrcsel0 = ops->read_csr_int_srcsel(base, bank); - state->iaintcolen = ops->read_csr_int_col_en(base, bank); - state->iaintcolctl = ops->read_csr_int_col_ctl(base, bank); - state->iaintflagandcolen = ops->read_csr_int_flag_and_col(base, bank); - state->ringexpstat = ops->read_csr_exp_stat(base, bank); - state->ringexpintenable = ops->read_csr_exp_int_en(base, bank); - state->ringsrvarben = ops->read_csr_ring_srv_arb_en(base, bank); - - for (i = 0; i < num_rings; i++) { - state->rings[i].head = ops->read_csr_ring_head(base, bank, i); - state->rings[i].tail = ops->read_csr_ring_tail(base, bank, i); - state->rings[i].config = ops->read_csr_ring_config(base, bank, i); - state->rings[i].base = ops->read_csr_ring_base(base, bank, i); - } -} - -#define CHECK_STAT(op, expect_val, name, args...) \ -({ \ - u32 __expect_val = (expect_val); \ - u32 actual_val = op(args); \ - (__expect_val == actual_val) ? 0 : \ - (pr_err("QAT: Fail to restore %s register. Expected 0x%x, actual 0x%x\n", \ - name, __expect_val, actual_val), -EINVAL); \ -}) - -static int bank_state_restore(struct adf_hw_csr_ops *ops, void __iomem *base, - u32 bank, struct bank_state *state, u32 num_rings, - int tx_rx_gap) -{ - u32 val, tmp_val, i; - int ret; - - for (i = 0; i < num_rings; i++) - ops->write_csr_ring_base(base, bank, i, state->rings[i].base); - - for (i = 0; i < num_rings; i++) - ops->write_csr_ring_config(base, bank, i, state->rings[i].config); - - for (i = 0; i < num_rings / 2; i++) { - int tx = i * (tx_rx_gap + 1); - int rx = tx + tx_rx_gap; - - ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head); - ops->write_csr_ring_tail(base, bank, tx, state->rings[tx].tail); - - /* - * The TX ring head needs to be updated again to make sure that - * the HW will not consider the ring as full when it is empty - * and the correct state flags are set to match the recovered state. - */ - if (state->ringestat & BIT(tx)) { - val = ops->read_csr_int_srcsel(base, bank); - val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK; - ops->write_csr_int_srcsel_w_val(base, bank, val); - ops->write_csr_ring_head(base, bank, tx, state->rings[tx].head); - } - - ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail); - val = ops->read_csr_int_srcsel(base, bank); - val |= ADF_RP_INT_SRC_SEL_F_RISE_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH; - ops->write_csr_int_srcsel_w_val(base, bank, val); - - ops->write_csr_ring_head(base, bank, rx, state->rings[rx].head); - val = ops->read_csr_int_srcsel(base, bank); - val |= ADF_RP_INT_SRC_SEL_F_FALL_MASK << ADF_RP_INT_SRC_SEL_RANGE_WIDTH; - ops->write_csr_int_srcsel_w_val(base, bank, val); - - /* - * The RX ring tail needs to be updated again to make sure that - * the HW will not consider the ring as empty when it is full - * and the correct state flags are set to match the recovered state. - */ - if (state->ringfstat & BIT(rx)) - ops->write_csr_ring_tail(base, bank, rx, state->rings[rx].tail); - } - - ops->write_csr_int_flag_and_col(base, bank, state->iaintflagandcolen); - ops->write_csr_int_en(base, bank, state->iaintflagen); - ops->write_csr_int_col_en(base, bank, state->iaintcolen); - ops->write_csr_int_srcsel_w_val(base, bank, state->iaintflagsrcsel0); - ops->write_csr_exp_int_en(base, bank, state->ringexpintenable); - ops->write_csr_int_col_ctl(base, bank, state->iaintcolctl); - ops->write_csr_ring_srv_arb_en(base, bank, state->ringsrvarben); - - /* Check that all ring statuses match the saved state. */ - ret = CHECK_STAT(ops->read_csr_stat, state->ringstat0, "ringstat", - base, bank); - if (ret) - return ret; - - ret = CHECK_STAT(ops->read_csr_e_stat, state->ringestat, "ringestat", - base, bank); - if (ret) - return ret; - - ret = CHECK_STAT(ops->read_csr_ne_stat, state->ringnestat, "ringnestat", - base, bank); - if (ret) - return ret; - - ret = CHECK_STAT(ops->read_csr_nf_stat, state->ringnfstat, "ringnfstat", - base, bank); - if (ret) - return ret; - - ret = CHECK_STAT(ops->read_csr_f_stat, state->ringfstat, "ringfstat", - base, bank); - if (ret) - return ret; - - ret = CHECK_STAT(ops->read_csr_c_stat, state->ringcstat0, "ringcstat", - base, bank); - if (ret) - return ret; - - tmp_val = ops->read_csr_exp_stat(base, bank); - val = state->ringexpstat; - if (tmp_val && !val) { - pr_err("QAT: Bank was restored with exception: 0x%x\n", val); - return -EINVAL; - } - - return 0; -} - -int adf_gen4_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number, - struct bank_state *state) -{ - struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); - struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); - void __iomem *csr_base = adf_get_etr_base(accel_dev); - - if (bank_number >= hw_data->num_banks || !state) - return -EINVAL; - - dev_dbg(&GET_DEV(accel_dev), "Saving state of bank %d\n", bank_number); - - bank_state_save(csr_ops, csr_base, bank_number, state, - hw_data->num_rings_per_bank); - - return 0; -} -EXPORT_SYMBOL_GPL(adf_gen4_bank_state_save); - -int adf_gen4_bank_state_restore(struct adf_accel_dev *accel_dev, u32 bank_number, - struct bank_state *state) -{ - struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); - struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); - void __iomem *csr_base = adf_get_etr_base(accel_dev); - int ret; - - if (bank_number >= hw_data->num_banks || !state) - return -EINVAL; - - dev_dbg(&GET_DEV(accel_dev), "Restoring state of bank %d\n", bank_number); - - ret = bank_state_restore(csr_ops, csr_base, bank_number, state, - hw_data->num_rings_per_bank, hw_data->tx_rx_gap); - if (ret) - dev_err(&GET_DEV(accel_dev), - "Unable to restore state of bank %d\n", bank_number); - - return ret; -} -EXPORT_SYMBOL_GPL(adf_gen4_bank_state_restore); - static int adf_gen4_build_comp_block(void *ctx, enum adf_dc_algo algo) { struct icp_qat_fw_comp_req *req_tmpl = ctx; @@ -733,3 +558,43 @@ void adf_gen4_init_dc_ops(struct adf_dc_ops *dc_ops) dc_ops->build_decomp_block = adf_gen4_build_decomp_block; } EXPORT_SYMBOL_GPL(adf_gen4_init_dc_ops); + +void adf_gen4_init_num_svc_aes(struct adf_rl_hw_data *device_data) +{ + struct adf_hw_device_data *hw_data; + unsigned int i; + u32 ae_cnt; + + hw_data = container_of(device_data, struct adf_hw_device_data, rl_data); + ae_cnt = hweight32(hw_data->get_ae_mask(hw_data)); + if (!ae_cnt) + return; + + for (i = 0; i < SVC_BASE_COUNT; i++) + device_data->svc_ae_mask[i] = ae_cnt - 1; + + /* + * The decompression service is not supported on QAT GEN4 devices. + * Therefore, set svc_ae_mask to 0. + */ + device_data->svc_ae_mask[SVC_DECOMP] = 0; +} +EXPORT_SYMBOL_GPL(adf_gen4_init_num_svc_aes); + +u32 adf_gen4_get_svc_slice_cnt(struct adf_accel_dev *accel_dev, + enum adf_base_services svc) +{ + struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data; + + switch (svc) { + case SVC_SYM: + return device_data->slices.cph_cnt; + case SVC_ASYM: + return device_data->slices.pke_cnt; + case SVC_DC: + return device_data->slices.dcpr_cnt; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(adf_gen4_get_svc_slice_cnt); diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.h b/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.h index e4f4d5fa616d..cd26b6724c43 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.h +++ b/drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.h @@ -84,9 +84,6 @@ #define ADF_WQM_CSR_RPRESETSTS(bank) (ADF_WQM_CSR_RPRESETCTL(bank) + 4) /* Ring interrupt */ -#define ADF_RP_INT_SRC_SEL_F_RISE_MASK GENMASK(1, 0) -#define ADF_RP_INT_SRC_SEL_F_FALL_MASK GENMASK(2, 0) -#define ADF_RP_INT_SRC_SEL_RANGE_WIDTH 4 #define ADF_COALESCED_POLL_TIMEOUT_US (1 * USEC_PER_SEC) #define ADF_COALESCED_POLL_DELAY_US 1000 #define ADF_WQM_CSR_RPINTSOU(bank) (0x200000 + ((bank) << 12)) @@ -176,11 +173,10 @@ int adf_gen4_bank_drain_start(struct adf_accel_dev *accel_dev, u32 bank_number, int timeout_us); void adf_gen4_bank_drain_finish(struct adf_accel_dev *accel_dev, u32 bank_number); -int adf_gen4_bank_state_save(struct adf_accel_dev *accel_dev, u32 bank_number, - struct bank_state *state); -int adf_gen4_bank_state_restore(struct adf_accel_dev *accel_dev, - u32 bank_number, struct bank_state *state); bool adf_gen4_services_supported(unsigned long service_mask); void adf_gen4_init_dc_ops(struct adf_dc_ops *dc_ops); +void adf_gen4_init_num_svc_aes(struct adf_rl_hw_data *device_data); +u32 adf_gen4_get_svc_slice_cnt(struct adf_accel_dev *accel_dev, + enum adf_base_services svc); #endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen4_pm_debugfs.c b/drivers/crypto/intel/qat/qat_common/adf_gen4_pm_debugfs.c index 2e4095c4c12c..b7e38842a46d 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen4_pm_debugfs.c +++ b/drivers/crypto/intel/qat/qat_common/adf_gen4_pm_debugfs.c @@ -1,47 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2023 Intel Corporation */ #include -#include #include -#include #include "adf_accel_devices.h" #include "adf_admin.h" #include "adf_common_drv.h" #include "adf_gen4_pm.h" +#include "adf_pm_dbgfs_utils.h" #include "icp_qat_fw_init_admin.h" -/* - * This is needed because a variable is used to index the mask at - * pm_scnprint_table(), making it not compile time constant, so the compile - * asserts from FIELD_GET() or u32_get_bits() won't be fulfilled. - */ -#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) - -#define PM_INFO_MEMBER_OFF(member) \ - (offsetof(struct icp_qat_fw_init_admin_pm_info, member) / sizeof(u32)) - -#define PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, _mask_) \ -{ \ - .reg_offset = PM_INFO_MEMBER_OFF(_reg_), \ - .key = __stringify(_field_), \ - .field_mask = _mask_, \ -} - -#define PM_INFO_REGSET_ENTRY32(_reg_, _field_) \ - PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, GENMASK(31, 0)) - #define PM_INFO_REGSET_ENTRY(_reg_, _field_) \ PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, ADF_GEN4_PM_##_field_##_MASK) -#define PM_INFO_MAX_KEY_LEN 21 - -struct pm_status_row { - int reg_offset; - u32 field_mask; - const char *key; -}; - static const struct pm_status_row pm_fuse_rows[] = { PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM), PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM_IDLE), @@ -109,44 +80,6 @@ static const struct pm_status_row pm_csrs_rows[] = { PM_INFO_REGSET_ENTRY32(pm.pwrreq, CPM_PM_PWRREQ), }; -static int pm_scnprint_table(char *buff, const struct pm_status_row *table, - u32 *pm_info_regs, size_t buff_size, int table_len, - bool lowercase) -{ - char key[PM_INFO_MAX_KEY_LEN]; - int wr = 0; - int i; - - for (i = 0; i < table_len; i++) { - if (lowercase) - string_lower(key, table[i].key); - else - string_upper(key, table[i].key); - - wr += scnprintf(&buff[wr], buff_size - wr, "%s: %#x\n", key, - field_get(table[i].field_mask, - pm_info_regs[table[i].reg_offset])); - } - - return wr; -} - -static int pm_scnprint_table_upper_keys(char *buff, const struct pm_status_row *table, - u32 *pm_info_regs, size_t buff_size, - int table_len) -{ - return pm_scnprint_table(buff, table, pm_info_regs, buff_size, - table_len, false); -} - -static int pm_scnprint_table_lower_keys(char *buff, const struct pm_status_row *table, - u32 *pm_info_regs, size_t buff_size, - int table_len) -{ - return pm_scnprint_table(buff, table, pm_info_regs, buff_size, - table_len, true); -} - static_assert(sizeof(struct icp_qat_fw_init_admin_pm_info) < PAGE_SIZE); static ssize_t adf_gen4_print_pm_status(struct adf_accel_dev *accel_dev, @@ -191,9 +124,9 @@ static ssize_t adf_gen4_print_pm_status(struct adf_accel_dev *accel_dev, /* Fusectl related */ len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "----------- PM Fuse info ---------\n"); - len += pm_scnprint_table_lower_keys(&pm_kv[len], pm_fuse_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_fuse_rows)); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_fuse_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_fuse_rows)); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "max_pwrreq: %#x\n", pm_info->max_pwrreq); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "min_pwrreq: %#x\n", @@ -204,28 +137,28 @@ static ssize_t adf_gen4_print_pm_status(struct adf_accel_dev *accel_dev, "------------ PM Info ------------\n"); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "power_level: %s\n", pm_info->pwr_state == PM_SET_MIN ? "min" : "max"); - len += pm_scnprint_table_lower_keys(&pm_kv[len], pm_info_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_info_rows)); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_info_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_info_rows)); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "pm_mode: STATIC\n"); /* SSM related */ len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "----------- SSM_PM Info ----------\n"); - len += pm_scnprint_table_lower_keys(&pm_kv[len], pm_ssm_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_ssm_rows)); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_ssm_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_ssm_rows)); /* Log related */ len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "------------- PM Log -------------\n"); - len += pm_scnprint_table_lower_keys(&pm_kv[len], pm_log_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_log_rows)); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_log_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_log_rows)); - len += pm_scnprint_table_lower_keys(&pm_kv[len], pm_event_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_event_rows)); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_event_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_event_rows)); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "idle_irq_count: %#x\n", pm->idle_irq_counters); @@ -241,9 +174,9 @@ static ssize_t adf_gen4_print_pm_status(struct adf_accel_dev *accel_dev, /* CSRs content */ len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "----------- HW PM CSRs -----------\n"); - len += pm_scnprint_table_upper_keys(&pm_kv[len], pm_csrs_rows, - pm_info_regs, PAGE_SIZE - len, - ARRAY_SIZE(pm_csrs_rows)); + len += adf_pm_scnprint_table_upper_keys(&pm_kv[len], pm_csrs_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_csrs_rows)); val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_HOST_MSG); len += scnprintf(&pm_kv[len], PAGE_SIZE - len, diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c b/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c index a62eb5e8dbe6..adb21656a3ba 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c +++ b/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c @@ -9,6 +9,7 @@ #include #include "adf_accel_devices.h" +#include "adf_bank_state.h" #include "adf_common_drv.h" #include "adf_gen4_hw_data.h" #include "adf_gen4_pfvf.h" @@ -358,7 +359,7 @@ static int adf_gen4_vfmig_load_etr_regs(struct adf_mstate_mgr *sub_mgr, pf_bank_nr = vf_bank_info->bank_nr + vf_bank_info->vf_nr * hw_data->num_banks_per_vf; ret = hw_data->bank_state_restore(accel_dev, pf_bank_nr, - (struct bank_state *)state); + (struct adf_bank_state *)state); if (ret) { dev_err(&GET_DEV(accel_dev), "Failed to load regs for vf%d bank%d\n", @@ -585,7 +586,7 @@ static int adf_gen4_vfmig_save_etr_regs(struct adf_mstate_mgr *subs, u8 *state, pf_bank_nr += vf_bank_info->vf_nr * hw_data->num_banks_per_vf; ret = hw_data->bank_state_save(accel_dev, pf_bank_nr, - (struct bank_state *)state); + (struct adf_bank_state *)state); if (ret) { dev_err(&GET_DEV(accel_dev), "Failed to save regs for vf%d bank%d\n", @@ -593,7 +594,7 @@ static int adf_gen4_vfmig_save_etr_regs(struct adf_mstate_mgr *subs, u8 *state, return ret; } - return sizeof(struct bank_state); + return sizeof(struct adf_bank_state); } static int adf_gen4_vfmig_save_etr_bank(struct adf_accel_dev *accel_dev, diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_pm.h b/drivers/crypto/intel/qat/qat_common/adf_gen6_pm.h index 9a5b995f7ada..4c0d576e8c21 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen6_pm.h +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_pm.h @@ -24,5 +24,29 @@ struct adf_accel_dev; /* cpm_pm_status bitfields */ #define ADF_GEN6_PM_INIT_STATE BIT(21) +#define ADF_GEN6_PM_CPM_PM_STATE_MASK GENMASK(22, 20) + +/* fusectl0 bitfields */ +#define ADF_GEN6_PM_ENABLE_PM_MASK BIT(21) +#define ADF_GEN6_PM_ENABLE_PM_IDLE_MASK BIT(22) +#define ADF_GEN6_PM_ENABLE_DEEP_PM_IDLE_MASK BIT(23) + +/* cpm_pm_fw_init bitfields */ +#define ADF_GEN6_PM_IDLE_FILTER_MASK GENMASK(5, 3) +#define ADF_GEN6_PM_IDLE_ENABLE_MASK BIT(2) + +/* ssm_pm_enable bitfield */ +#define ADF_GEN6_PM_SSM_PM_ENABLE_MASK BIT(0) + +/* ssm_pm_domain_status bitfield */ +#define ADF_GEN6_PM_DOMAIN_POWERED_UP_MASK BIT(0) + +#ifdef CONFIG_DEBUG_FS +void adf_gen6_init_dev_pm_data(struct adf_accel_dev *accel_dev); +#else +static inline void adf_gen6_init_dev_pm_data(struct adf_accel_dev *accel_dev) +{ +} +#endif /* CONFIG_DEBUG_FS */ #endif /* ADF_GEN6_PM_H */ diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_pm_dbgfs.c b/drivers/crypto/intel/qat/qat_common/adf_gen6_pm_dbgfs.c new file mode 100644 index 000000000000..603aefba0fdb --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_pm_dbgfs.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2025 Intel Corporation */ +#include +#include +#include + +#include "adf_admin.h" +#include "adf_common_drv.h" +#include "adf_gen6_pm.h" +#include "adf_pm_dbgfs_utils.h" +#include "icp_qat_fw_init_admin.h" + +#define PM_INFO_REGSET_ENTRY(_reg_, _field_) \ + PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, ADF_GEN6_PM_##_field_##_MASK) + +static struct pm_status_row pm_fuse_rows[] = { + PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM), + PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_PM_IDLE), + PM_INFO_REGSET_ENTRY(fusectl0, ENABLE_DEEP_PM_IDLE), +}; + +static struct pm_status_row pm_info_rows[] = { + PM_INFO_REGSET_ENTRY(pm.status, CPM_PM_STATE), + PM_INFO_REGSET_ENTRY(pm.fw_init, IDLE_ENABLE), + PM_INFO_REGSET_ENTRY(pm.fw_init, IDLE_FILTER), +}; + +static struct pm_status_row pm_ssm_rows[] = { + PM_INFO_REGSET_ENTRY(ssm.pm_enable, SSM_PM_ENABLE), + PM_INFO_REGSET_ENTRY(ssm.pm_domain_status, DOMAIN_POWERED_UP), +}; + +static struct pm_status_row pm_csrs_rows[] = { + PM_INFO_REGSET_ENTRY32(pm.fw_init, CPM_PM_FW_INIT), + PM_INFO_REGSET_ENTRY32(pm.status, CPM_PM_STATUS), +}; + +static_assert(sizeof(struct icp_qat_fw_init_admin_pm_info) < PAGE_SIZE); + +static ssize_t adf_gen6_print_pm_status(struct adf_accel_dev *accel_dev, + char __user *buf, size_t count, + loff_t *pos) +{ + void __iomem *pmisc = adf_get_pmisc_base(accel_dev); + struct icp_qat_fw_init_admin_pm_info *pm_info; + dma_addr_t p_state_addr; + u32 *pm_info_regs; + size_t len = 0; + char *pm_kv; + u32 val; + int ret; + + pm_info = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!pm_info) + return -ENOMEM; + + pm_kv = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!pm_kv) { + kfree(pm_info); + return -ENOMEM; + } + + p_state_addr = dma_map_single(&GET_DEV(accel_dev), pm_info, PAGE_SIZE, + DMA_FROM_DEVICE); + ret = dma_mapping_error(&GET_DEV(accel_dev), p_state_addr); + if (ret) + goto out_free; + + /* Query power management information from QAT FW */ + ret = adf_get_pm_info(accel_dev, p_state_addr, PAGE_SIZE); + dma_unmap_single(&GET_DEV(accel_dev), p_state_addr, PAGE_SIZE, + DMA_FROM_DEVICE); + if (ret) + goto out_free; + + pm_info_regs = (u32 *)pm_info; + + /* Fuse control register */ + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, + "----------- PM Fuse info ---------\n"); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_fuse_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_fuse_rows)); + + /* Power management */ + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, + "----------- PM Info --------------\n"); + + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_info_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_info_rows)); + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "pm_mode: ACTIVE\n"); + + /* Shared Slice Module */ + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, + "----------- SSM_PM Info ----------\n"); + len += adf_pm_scnprint_table_lower_keys(&pm_kv[len], pm_ssm_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_ssm_rows)); + + /* Control status register content */ + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, + "----------- HW PM CSRs -----------\n"); + len += adf_pm_scnprint_table_upper_keys(&pm_kv[len], pm_csrs_rows, + pm_info_regs, PAGE_SIZE - len, + ARRAY_SIZE(pm_csrs_rows)); + + val = ADF_CSR_RD(pmisc, ADF_GEN6_PM_INTERRUPT); + len += scnprintf(&pm_kv[len], PAGE_SIZE - len, "CPM_PM_INTERRUPT: %#x\n", val); + ret = simple_read_from_buffer(buf, count, pos, pm_kv, len); + +out_free: + kfree(pm_info); + kfree(pm_kv); + + return ret; +} + +void adf_gen6_init_dev_pm_data(struct adf_accel_dev *accel_dev) +{ + accel_dev->power_management.print_pm_status = adf_gen6_print_pm_status; + accel_dev->power_management.present = true; +} +EXPORT_SYMBOL_GPL(adf_gen6_init_dev_pm_data); diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.c b/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.c index 58a072e2f936..c9b151006dca 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.c +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.c @@ -5,6 +5,7 @@ #include "adf_gen4_config.h" #include "adf_gen4_hw_csr_data.h" #include "adf_gen4_pfvf.h" +#include "adf_gen4_vf_mig.h" #include "adf_gen6_shared.h" struct adf_accel_dev; @@ -47,3 +48,9 @@ int adf_gen6_no_dev_config(struct adf_accel_dev *accel_dev) return adf_no_dev_config(accel_dev); } EXPORT_SYMBOL_GPL(adf_gen6_no_dev_config); + +void adf_gen6_init_vf_mig_ops(struct qat_migdev_ops *vfmig_ops) +{ + adf_gen4_init_vf_mig_ops(vfmig_ops); +} +EXPORT_SYMBOL_GPL(adf_gen6_init_vf_mig_ops); diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.h b/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.h index bc8e71e984fc..fc6fad029a70 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.h +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_shared.h @@ -4,6 +4,7 @@ #define ADF_GEN6_SHARED_H_ struct adf_hw_csr_ops; +struct qat_migdev_ops; struct adf_accel_dev; struct adf_pfvf_ops; @@ -12,4 +13,5 @@ void adf_gen6_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops); int adf_gen6_cfg_dev_init(struct adf_accel_dev *accel_dev); int adf_gen6_comp_dev_config(struct adf_accel_dev *accel_dev); int adf_gen6_no_dev_config(struct adf_accel_dev *accel_dev); +void adf_gen6_init_vf_mig_ops(struct qat_migdev_ops *vfmig_ops); #endif/* ADF_GEN6_SHARED_H_ */ diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.c b/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.c new file mode 100644 index 000000000000..cf804f95838a --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Intel Corporation. */ +#include + +#include "adf_gen6_tl.h" +#include "adf_telemetry.h" +#include "adf_tl_debugfs.h" +#include "icp_qat_fw_init_admin.h" + +#define ADF_GEN6_TL_DEV_REG_OFF(reg) ADF_TL_DEV_REG_OFF(reg, gen6) + +#define ADF_GEN6_TL_RP_REG_OFF(reg) ADF_TL_RP_REG_OFF(reg, gen6) + +#define ADF_GEN6_TL_SL_UTIL_COUNTER(_name) \ + ADF_TL_COUNTER("util_" #_name, ADF_TL_SIMPLE_COUNT, \ + ADF_TL_SLICE_REG_OFF(_name, reg_tm_slice_util, gen6)) + +#define ADF_GEN6_TL_SL_EXEC_COUNTER(_name) \ + ADF_TL_COUNTER("exec_" #_name, ADF_TL_SIMPLE_COUNT, \ + ADF_TL_SLICE_REG_OFF(_name, reg_tm_slice_exec_cnt, gen6)) + +#define SLICE_IDX(sl) offsetof(struct icp_qat_fw_init_admin_slice_cnt, sl##_cnt) + +/* Device level counters. */ +static const struct adf_tl_dbg_counter dev_counters[] = { + /* PCIe partial transactions. */ + ADF_TL_COUNTER(PCI_TRANS_CNT_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_prt_trans_cnt)), + /* Max read latency[ns]. */ + ADF_TL_COUNTER(MAX_RD_LAT_NAME, ADF_TL_COUNTER_NS, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_rd_lat_max)), + /* Read latency average[ns]. */ + ADF_TL_COUNTER_LATENCY(RD_LAT_ACC_NAME, ADF_TL_COUNTER_NS_AVG, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_rd_lat_acc), + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_rd_cmpl_cnt)), + /* Max "get to put" latency[ns]. */ + ADF_TL_COUNTER(MAX_LAT_NAME, ADF_TL_COUNTER_NS, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_gp_lat_max)), + /* "Get to put" latency average[ns]. */ + ADF_TL_COUNTER_LATENCY(LAT_ACC_NAME, ADF_TL_COUNTER_NS_AVG, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_gp_lat_acc), + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_ae_put_cnt)), + /* PCIe write bandwidth[Mbps]. */ + ADF_TL_COUNTER(BW_IN_NAME, ADF_TL_COUNTER_MBPS, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_bw_in)), + /* PCIe read bandwidth[Mbps]. */ + ADF_TL_COUNTER(BW_OUT_NAME, ADF_TL_COUNTER_MBPS, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_bw_out)), + /* Page request latency average[ns]. */ + ADF_TL_COUNTER_LATENCY(PAGE_REQ_LAT_NAME, ADF_TL_COUNTER_NS_AVG, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_at_page_req_lat_acc), + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_at_page_req_cnt)), + /* Page translation latency average[ns]. */ + ADF_TL_COUNTER_LATENCY(AT_TRANS_LAT_NAME, ADF_TL_COUNTER_NS_AVG, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_at_trans_lat_acc), + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_at_trans_lat_cnt)), + /* Maximum uTLB used. */ + ADF_TL_COUNTER(AT_MAX_UTLB_USED_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_DEV_REG_OFF(reg_tl_at_max_utlb_used)), +}; + +/* Accelerator utilization counters */ +static const struct adf_tl_dbg_counter sl_util_counters[ADF_TL_SL_CNT_COUNT] = { + /* Compression accelerator utilization. */ + [SLICE_IDX(cpr)] = ADF_GEN6_TL_SL_UTIL_COUNTER(cnv), + /* Decompression accelerator utilization. */ + [SLICE_IDX(dcpr)] = ADF_GEN6_TL_SL_UTIL_COUNTER(dcprz), + /* PKE accelerator utilization. */ + [SLICE_IDX(pke)] = ADF_GEN6_TL_SL_UTIL_COUNTER(pke), + /* Wireless Authentication accelerator utilization. */ + [SLICE_IDX(wat)] = ADF_GEN6_TL_SL_UTIL_COUNTER(wat), + /* Wireless Cipher accelerator utilization. */ + [SLICE_IDX(wcp)] = ADF_GEN6_TL_SL_UTIL_COUNTER(wcp), + /* UCS accelerator utilization. */ + [SLICE_IDX(ucs)] = ADF_GEN6_TL_SL_UTIL_COUNTER(ucs), + /* Authentication accelerator utilization. */ + [SLICE_IDX(ath)] = ADF_GEN6_TL_SL_UTIL_COUNTER(ath), +}; + +/* Accelerator execution counters */ +static const struct adf_tl_dbg_counter sl_exec_counters[ADF_TL_SL_CNT_COUNT] = { + /* Compression accelerator execution count. */ + [SLICE_IDX(cpr)] = ADF_GEN6_TL_SL_EXEC_COUNTER(cnv), + /* Decompression accelerator execution count. */ + [SLICE_IDX(dcpr)] = ADF_GEN6_TL_SL_EXEC_COUNTER(dcprz), + /* PKE execution count. */ + [SLICE_IDX(pke)] = ADF_GEN6_TL_SL_EXEC_COUNTER(pke), + /* Wireless Authentication accelerator execution count. */ + [SLICE_IDX(wat)] = ADF_GEN6_TL_SL_EXEC_COUNTER(wat), + /* Wireless Cipher accelerator execution count. */ + [SLICE_IDX(wcp)] = ADF_GEN6_TL_SL_EXEC_COUNTER(wcp), + /* UCS accelerator execution count. */ + [SLICE_IDX(ucs)] = ADF_GEN6_TL_SL_EXEC_COUNTER(ucs), + /* Authentication accelerator execution count. */ + [SLICE_IDX(ath)] = ADF_GEN6_TL_SL_EXEC_COUNTER(ath), +}; + +/* Ring pair counters. */ +static const struct adf_tl_dbg_counter rp_counters[] = { + /* PCIe partial transactions. */ + ADF_TL_COUNTER(PCI_TRANS_CNT_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_prt_trans_cnt)), + /* "Get to put" latency average[ns]. */ + ADF_TL_COUNTER_LATENCY(LAT_ACC_NAME, ADF_TL_COUNTER_NS_AVG, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_gp_lat_acc), + ADF_GEN6_TL_RP_REG_OFF(reg_tl_ae_put_cnt)), + /* PCIe write bandwidth[Mbps]. */ + ADF_TL_COUNTER(BW_IN_NAME, ADF_TL_COUNTER_MBPS, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_bw_in)), + /* PCIe read bandwidth[Mbps]. */ + ADF_TL_COUNTER(BW_OUT_NAME, ADF_TL_COUNTER_MBPS, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_bw_out)), + /* Message descriptor DevTLB hit rate. */ + ADF_TL_COUNTER(AT_GLOB_DTLB_HIT_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_at_glob_devtlb_hit)), + /* Message descriptor DevTLB miss rate. */ + ADF_TL_COUNTER(AT_GLOB_DTLB_MISS_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_at_glob_devtlb_miss)), + /* Payload DevTLB hit rate. */ + ADF_TL_COUNTER(AT_PAYLD_DTLB_HIT_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_at_payld_devtlb_hit)), + /* Payload DevTLB miss rate. */ + ADF_TL_COUNTER(AT_PAYLD_DTLB_MISS_NAME, ADF_TL_SIMPLE_COUNT, + ADF_GEN6_TL_RP_REG_OFF(reg_tl_at_payld_devtlb_miss)), +}; + +void adf_gen6_init_tl_data(struct adf_tl_hw_data *tl_data) +{ + tl_data->layout_sz = ADF_GEN6_TL_LAYOUT_SZ; + tl_data->slice_reg_sz = ADF_GEN6_TL_SLICE_REG_SZ; + tl_data->rp_reg_sz = ADF_GEN6_TL_RP_REG_SZ; + tl_data->num_hbuff = ADF_GEN6_TL_NUM_HIST_BUFFS; + tl_data->max_rp = ADF_GEN6_TL_MAX_RP_NUM; + tl_data->msg_cnt_off = ADF_GEN6_TL_MSG_CNT_OFF; + tl_data->cpp_ns_per_cycle = ADF_GEN6_CPP_NS_PER_CYCLE; + tl_data->bw_units_to_bytes = ADF_GEN6_TL_BW_HW_UNITS_TO_BYTES; + + tl_data->dev_counters = dev_counters; + tl_data->num_dev_counters = ARRAY_SIZE(dev_counters); + tl_data->sl_util_counters = sl_util_counters; + tl_data->sl_exec_counters = sl_exec_counters; + tl_data->rp_counters = rp_counters; + tl_data->num_rp_counters = ARRAY_SIZE(rp_counters); + tl_data->max_sl_cnt = ADF_GEN6_TL_MAX_SLICES_PER_TYPE; +} +EXPORT_SYMBOL_GPL(adf_gen6_init_tl_data); diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.h b/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.h new file mode 100644 index 000000000000..49db660b8eb9 --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_tl.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2025 Intel Corporation. */ +#ifndef ADF_GEN6_TL_H +#define ADF_GEN6_TL_H + +#include + +struct adf_tl_hw_data; + +/* Computation constants. */ +#define ADF_GEN6_CPP_NS_PER_CYCLE 2 +#define ADF_GEN6_TL_BW_HW_UNITS_TO_BYTES 64 + +/* Maximum aggregation time. Value is in milliseconds. */ +#define ADF_GEN6_TL_MAX_AGGR_TIME_MS 4000 +/* Number of buffers to store historic values. */ +#define ADF_GEN6_TL_NUM_HIST_BUFFS \ + (ADF_GEN6_TL_MAX_AGGR_TIME_MS / ADF_TL_DATA_WR_INTERVAL_MS) + +/* Max number of HW resources of one type */ +#define ADF_GEN6_TL_MAX_SLICES_PER_TYPE 32 +#define MAX_ATH_SL_COUNT 7 +#define MAX_CNV_SL_COUNT 2 +#define MAX_DCPRZ_SL_COUNT 2 +#define MAX_PKE_SL_COUNT 32 +#define MAX_UCS_SL_COUNT 4 +#define MAX_WAT_SL_COUNT 5 +#define MAX_WCP_SL_COUNT 5 + +#define MAX_ATH_CMDQ_COUNT 14 +#define MAX_CNV_CMDQ_COUNT 6 +#define MAX_DCPRZ_CMDQ_COUNT 6 +#define MAX_PKE_CMDQ_COUNT 32 +#define MAX_UCS_CMDQ_COUNT 12 +#define MAX_WAT_CMDQ_COUNT 35 +#define MAX_WCP_CMDQ_COUNT 35 + +/* Max number of simultaneously monitored ring pairs. */ +#define ADF_GEN6_TL_MAX_RP_NUM 4 + +/** + * struct adf_gen6_tl_slice_data_regs - HW slice data as populated by FW. + * @reg_tm_slice_exec_cnt: Slice execution count. + * @reg_tm_slice_util: Slice utilization. + */ +struct adf_gen6_tl_slice_data_regs { + __u32 reg_tm_slice_exec_cnt; + __u32 reg_tm_slice_util; +}; + +#define ADF_GEN6_TL_SLICE_REG_SZ sizeof(struct adf_gen6_tl_slice_data_regs) + +/** + * struct adf_gen6_tl_cmdq_data_regs - HW CMDQ data as populated by FW. + * @reg_tm_cmdq_wait_cnt: CMDQ wait count. + * @reg_tm_cmdq_exec_cnt: CMDQ execution count. + * @reg_tm_cmdq_drain_cnt: CMDQ drain count. + */ +struct adf_gen6_tl_cmdq_data_regs { + __u32 reg_tm_cmdq_wait_cnt; + __u32 reg_tm_cmdq_exec_cnt; + __u32 reg_tm_cmdq_drain_cnt; + __u32 reserved; +}; + +#define ADF_GEN6_TL_CMDQ_REG_SZ sizeof(struct adf_gen6_tl_cmdq_data_regs) + +/** + * struct adf_gen6_tl_device_data_regs - This structure stores device telemetry + * counter values as are being populated periodically by device. + * @reg_tl_rd_lat_acc: read latency accumulator + * @reg_tl_gp_lat_acc: "get to put" latency accumulator + * @reg_tl_at_page_req_lat_acc: AT/DevTLB page request latency accumulator + * @reg_tl_at_trans_lat_acc: DevTLB transaction latency accumulator + * @reg_tl_re_acc: accumulated ring empty time + * @reg_tl_prt_trans_cnt: PCIe partial transactions + * @reg_tl_rd_lat_max: maximum logged read latency + * @reg_tl_rd_cmpl_cnt: read requests completed count + * @reg_tl_gp_lat_max: maximum logged get to put latency + * @reg_tl_ae_put_cnt: Accelerator Engine put counts across all rings + * @reg_tl_bw_in: PCIe write bandwidth + * @reg_tl_bw_out: PCIe read bandwidth + * @reg_tl_at_page_req_cnt: DevTLB page requests count + * @reg_tl_at_trans_lat_cnt: DevTLB transaction latency samples count + * @reg_tl_at_max_utlb_used: maximum uTLB used + * @reg_tl_re_cnt: ring empty time samples count + * @reserved: reserved + * @ath_slices: array of Authentication slices utilization registers + * @cnv_slices: array of Compression slices utilization registers + * @dcprz_slices: array of Decompression slices utilization registers + * @pke_slices: array of PKE slices utilization registers + * @ucs_slices: array of UCS slices utilization registers + * @wat_slices: array of Wireless Authentication slices utilization registers + * @wcp_slices: array of Wireless Cipher slices utilization registers + * @ath_cmdq: array of Authentication cmdq telemetry registers + * @cnv_cmdq: array of Compression cmdq telemetry registers + * @dcprz_cmdq: array of Decomopression cmdq telemetry registers + * @pke_cmdq: array of PKE cmdq telemetry registers + * @ucs_cmdq: array of UCS cmdq telemetry registers + * @wat_cmdq: array of Wireless Authentication cmdq telemetry registers + * @wcp_cmdq: array of Wireless Cipher cmdq telemetry registers + */ +struct adf_gen6_tl_device_data_regs { + __u64 reg_tl_rd_lat_acc; + __u64 reg_tl_gp_lat_acc; + __u64 reg_tl_at_page_req_lat_acc; + __u64 reg_tl_at_trans_lat_acc; + __u64 reg_tl_re_acc; + __u32 reg_tl_prt_trans_cnt; + __u32 reg_tl_rd_lat_max; + __u32 reg_tl_rd_cmpl_cnt; + __u32 reg_tl_gp_lat_max; + __u32 reg_tl_ae_put_cnt; + __u32 reg_tl_bw_in; + __u32 reg_tl_bw_out; + __u32 reg_tl_at_page_req_cnt; + __u32 reg_tl_at_trans_lat_cnt; + __u32 reg_tl_at_max_utlb_used; + __u32 reg_tl_re_cnt; + __u32 reserved; + struct adf_gen6_tl_slice_data_regs ath_slices[MAX_ATH_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs cnv_slices[MAX_CNV_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs dcprz_slices[MAX_DCPRZ_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs pke_slices[MAX_PKE_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs ucs_slices[MAX_UCS_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs wat_slices[MAX_WAT_SL_COUNT]; + struct adf_gen6_tl_slice_data_regs wcp_slices[MAX_WCP_SL_COUNT]; + struct adf_gen6_tl_cmdq_data_regs ath_cmdq[MAX_ATH_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs cnv_cmdq[MAX_CNV_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs dcprz_cmdq[MAX_DCPRZ_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs pke_cmdq[MAX_PKE_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs ucs_cmdq[MAX_UCS_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs wat_cmdq[MAX_WAT_CMDQ_COUNT]; + struct adf_gen6_tl_cmdq_data_regs wcp_cmdq[MAX_WCP_CMDQ_COUNT]; +}; + +/** + * struct adf_gen6_tl_ring_pair_data_regs - This structure stores ring pair + * telemetry counter values as they are being populated periodically by device. + * @reg_tl_gp_lat_acc: get-put latency accumulator + * @reg_tl_re_acc: accumulated ring empty time + * @reg_tl_pci_trans_cnt: PCIe partial transactions + * @reg_tl_ae_put_cnt: Accelerator Engine put counts across all rings + * @reg_tl_bw_in: PCIe write bandwidth + * @reg_tl_bw_out: PCIe read bandwidth + * @reg_tl_at_glob_devtlb_hit: Message descriptor DevTLB hit rate + * @reg_tl_at_glob_devtlb_miss: Message descriptor DevTLB miss rate + * @reg_tl_at_payld_devtlb_hit: Payload DevTLB hit rate + * @reg_tl_at_payld_devtlb_miss: Payload DevTLB miss rate + * @reg_tl_re_cnt: ring empty time samples count + * @reserved1: reserved + */ +struct adf_gen6_tl_ring_pair_data_regs { + __u64 reg_tl_gp_lat_acc; + __u64 reg_tl_re_acc; + __u32 reg_tl_prt_trans_cnt; + __u32 reg_tl_ae_put_cnt; + __u32 reg_tl_bw_in; + __u32 reg_tl_bw_out; + __u32 reg_tl_at_glob_devtlb_hit; + __u32 reg_tl_at_glob_devtlb_miss; + __u32 reg_tl_at_payld_devtlb_hit; + __u32 reg_tl_at_payld_devtlb_miss; + __u32 reg_tl_re_cnt; + __u32 reserved1; +}; + +#define ADF_GEN6_TL_RP_REG_SZ sizeof(struct adf_gen6_tl_ring_pair_data_regs) + +/** + * struct adf_gen6_tl_layout - This structure represents the entire telemetry + * counters data: Device + 4 Ring Pairs as they are being populated periodically + * by device. + * @tl_device_data_regs: structure of device telemetry registers + * @tl_ring_pairs_data_regs: array of ring pairs telemetry registers + * @reg_tl_msg_cnt: telemetry message counter + * @reserved: reserved + */ +struct adf_gen6_tl_layout { + struct adf_gen6_tl_device_data_regs tl_device_data_regs; + struct adf_gen6_tl_ring_pair_data_regs + tl_ring_pairs_data_regs[ADF_GEN6_TL_MAX_RP_NUM]; + __u32 reg_tl_msg_cnt; + __u32 reserved; +}; + +#define ADF_GEN6_TL_LAYOUT_SZ sizeof(struct adf_gen6_tl_layout) +#define ADF_GEN6_TL_MSG_CNT_OFF \ + offsetof(struct adf_gen6_tl_layout, reg_tl_msg_cnt) + +#ifdef CONFIG_DEBUG_FS +void adf_gen6_init_tl_data(struct adf_tl_hw_data *tl_data); +#else +static inline void adf_gen6_init_tl_data(struct adf_tl_hw_data *tl_data) +{ +} +#endif /* CONFIG_DEBUG_FS */ +#endif /* ADF_GEN6_TL_H */ diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index f189cce7d153..46491048e0bb 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -404,6 +404,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) hw_data->exit_admin_comms(accel_dev); adf_cleanup_etr_data(accel_dev); + adf_misc_wq_flush(); adf_dev_restore(accel_dev); } diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c index cae1aee5479a..12e565613661 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_isr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c @@ -407,3 +407,8 @@ bool adf_misc_wq_queue_delayed_work(struct delayed_work *work, { return queue_delayed_work(adf_misc_wq, work, delay); } + +void adf_misc_wq_flush(void) +{ + flush_workqueue(adf_misc_wq); +} diff --git a/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.c b/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.c new file mode 100644 index 000000000000..69295a9ddf0a --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2025 Intel Corporation */ +#include +#include +#include + +#include "adf_pm_dbgfs_utils.h" + +/* + * This is needed because a variable is used to index the mask at + * pm_scnprint_table(), making it not compile time constant, so the compile + * asserts from FIELD_GET() or u32_get_bits() won't be fulfilled. + */ +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) + +#define PM_INFO_MAX_KEY_LEN 21 + +static int pm_scnprint_table(char *buff, const struct pm_status_row *table, + u32 *pm_info_regs, size_t buff_size, int table_len, + bool lowercase) +{ + char key[PM_INFO_MAX_KEY_LEN]; + int wr = 0; + int i; + + for (i = 0; i < table_len; i++) { + if (lowercase) + string_lower(key, table[i].key); + else + string_upper(key, table[i].key); + + wr += scnprintf(&buff[wr], buff_size - wr, "%s: %#x\n", key, + field_get(table[i].field_mask, + pm_info_regs[table[i].reg_offset])); + } + + return wr; +} + +int adf_pm_scnprint_table_upper_keys(char *buff, const struct pm_status_row *table, + u32 *pm_info_regs, size_t buff_size, int table_len) +{ + return pm_scnprint_table(buff, table, pm_info_regs, buff_size, + table_len, false); +} + +int adf_pm_scnprint_table_lower_keys(char *buff, const struct pm_status_row *table, + u32 *pm_info_regs, size_t buff_size, int table_len) +{ + return pm_scnprint_table(buff, table, pm_info_regs, buff_size, + table_len, true); +} diff --git a/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.h b/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.h new file mode 100644 index 000000000000..854f058b35ed --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2025 Intel Corporation */ +#ifndef ADF_PM_DBGFS_UTILS_H_ +#define ADF_PM_DBGFS_UTILS_H_ + +#include +#include +#include +#include "icp_qat_fw_init_admin.h" + +#define PM_INFO_MEMBER_OFF(member) \ + (offsetof(struct icp_qat_fw_init_admin_pm_info, member) / sizeof(u32)) + +#define PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, _mask_) \ +{ \ + .reg_offset = PM_INFO_MEMBER_OFF(_reg_), \ + .key = __stringify(_field_), \ + .field_mask = _mask_, \ +} + +#define PM_INFO_REGSET_ENTRY32(_reg_, _field_) \ + PM_INFO_REGSET_ENTRY_MASK(_reg_, _field_, GENMASK(31, 0)) + +struct pm_status_row { + int reg_offset; + u32 field_mask; + const char *key; +}; + +int adf_pm_scnprint_table_upper_keys(char *buff, const struct pm_status_row *table, + u32 *pm_info_regs, size_t buff_size, int table_len); + +int adf_pm_scnprint_table_lower_keys(char *buff, const struct pm_status_row *table, + u32 *pm_info_regs, size_t buff_size, int table_len); + +#endif /* ADF_PM_DBGFS_UTILS_H_ */ diff --git a/drivers/crypto/intel/qat/qat_common/adf_rl.c b/drivers/crypto/intel/qat/qat_common/adf_rl.c index e782c23fc1bf..c6a54e465931 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_rl.c +++ b/drivers/crypto/intel/qat/qat_common/adf_rl.c @@ -13,6 +13,7 @@ #include #include "adf_accel_devices.h" +#include "adf_cfg_services.h" #include "adf_common_drv.h" #include "adf_rl_admin.h" #include "adf_rl.h" @@ -55,7 +56,7 @@ static int validate_user_input(struct adf_accel_dev *accel_dev, } } - if (sla_in->srv >= ADF_SVC_NONE) { + if (sla_in->srv >= SVC_BASE_COUNT) { dev_notice(&GET_DEV(accel_dev), "Wrong service type\n"); return -EINVAL; @@ -168,20 +169,6 @@ static struct rl_sla *find_parent(struct adf_rl *rl_data, return NULL; } -static enum adf_cfg_service_type srv_to_cfg_svc_type(enum adf_base_services rl_srv) -{ - switch (rl_srv) { - case ADF_SVC_ASYM: - return ASYM; - case ADF_SVC_SYM: - return SYM; - case ADF_SVC_DC: - return COMP; - default: - return UNUSED; - } -} - /** * adf_rl_get_sla_arr_of_type() - Returns a pointer to SLA type specific array * @rl_data: pointer to ratelimiting data @@ -209,22 +196,6 @@ u32 adf_rl_get_sla_arr_of_type(struct adf_rl *rl_data, enum rl_node_type type, } } -static bool is_service_enabled(struct adf_accel_dev *accel_dev, - enum adf_base_services rl_srv) -{ - enum adf_cfg_service_type arb_srv = srv_to_cfg_svc_type(rl_srv); - struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); - u8 rps_per_bundle = hw_data->num_banks_per_vf; - int i; - - for (i = 0; i < rps_per_bundle; i++) { - if (GET_SRV_TYPE(accel_dev, i) == arb_srv) - return true; - } - - return false; -} - /** * prepare_rp_ids() - Creates an array of ring pair IDs from bitmask * @accel_dev: pointer to acceleration device structure @@ -243,7 +214,7 @@ static bool is_service_enabled(struct adf_accel_dev *accel_dev, static int prepare_rp_ids(struct adf_accel_dev *accel_dev, struct rl_sla *sla, const unsigned long rp_mask) { - enum adf_cfg_service_type arb_srv = srv_to_cfg_svc_type(sla->srv); + enum adf_cfg_service_type arb_srv = adf_srv_to_cfg_svc_type(sla->srv); u16 rps_per_bundle = GET_HW_DATA(accel_dev)->num_banks_per_vf; bool *rp_in_use = accel_dev->rate_limiting->rp_in_use; size_t rp_cnt_max = ARRAY_SIZE(sla->ring_pairs_ids); @@ -558,21 +529,9 @@ u32 adf_rl_calculate_slice_tokens(struct adf_accel_dev *accel_dev, u32 sla_val, if (!sla_val) return 0; + /* Handle generation specific slice count adjustment */ avail_slice_cycles = hw_data->clock_frequency; - - switch (svc_type) { - case ADF_SVC_ASYM: - avail_slice_cycles *= device_data->slices.pke_cnt; - break; - case ADF_SVC_SYM: - avail_slice_cycles *= device_data->slices.cph_cnt; - break; - case ADF_SVC_DC: - avail_slice_cycles *= device_data->slices.dcpr_cnt; - break; - default: - break; - } + avail_slice_cycles *= hw_data->get_svc_slice_cnt(accel_dev, svc_type); do_div(avail_slice_cycles, device_data->scan_interval); allocated_tokens = avail_slice_cycles * sla_val; @@ -581,6 +540,17 @@ u32 adf_rl_calculate_slice_tokens(struct adf_accel_dev *accel_dev, u32 sla_val, return allocated_tokens; } +static u32 adf_rl_get_num_svc_aes(struct adf_accel_dev *accel_dev, + enum adf_base_services svc) +{ + struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data; + + if (svc >= SVC_BASE_COUNT) + return 0; + + return device_data->svc_ae_mask[svc]; +} + u32 adf_rl_calculate_ae_cycles(struct adf_accel_dev *accel_dev, u32 sla_val, enum adf_base_services svc_type) { @@ -592,7 +562,7 @@ u32 adf_rl_calculate_ae_cycles(struct adf_accel_dev *accel_dev, u32 sla_val, return 0; avail_ae_cycles = hw_data->clock_frequency; - avail_ae_cycles *= hw_data->get_num_aes(hw_data) - 1; + avail_ae_cycles *= adf_rl_get_num_svc_aes(accel_dev, svc_type); do_div(avail_ae_cycles, device_data->scan_interval); sla_val *= device_data->max_tp[svc_type]; @@ -617,9 +587,8 @@ u32 adf_rl_calculate_pci_bw(struct adf_accel_dev *accel_dev, u32 sla_val, sla_to_bytes *= device_data->max_tp[svc_type]; do_div(sla_to_bytes, device_data->scale_ref); - sla_to_bytes *= (svc_type == ADF_SVC_ASYM) ? RL_TOKEN_ASYM_SIZE : - BYTES_PER_MBIT; - if (svc_type == ADF_SVC_DC && is_bw_out) + sla_to_bytes *= (svc_type == SVC_ASYM) ? RL_TOKEN_ASYM_SIZE : BYTES_PER_MBIT; + if (svc_type == SVC_DC && is_bw_out) sla_to_bytes *= device_data->slices.dcpr_cnt - device_data->dcpr_correction; @@ -660,7 +629,7 @@ static int add_new_sla_entry(struct adf_accel_dev *accel_dev, } *sla_out = sla; - if (!is_service_enabled(accel_dev, sla_in->srv)) { + if (!adf_is_service_enabled(accel_dev, sla_in->srv)) { dev_notice(&GET_DEV(accel_dev), "Provided service is not enabled\n"); ret = -EINVAL; @@ -730,8 +699,8 @@ static int initialize_default_nodes(struct adf_accel_dev *accel_dev) sla_in.type = RL_ROOT; sla_in.parent_id = RL_PARENT_DEFAULT_ID; - for (i = 0; i < ADF_SVC_NONE; i++) { - if (!is_service_enabled(accel_dev, i)) + for (i = 0; i < SVC_BASE_COUNT; i++) { + if (!adf_is_service_enabled(accel_dev, i)) continue; sla_in.cir = device_data->scale_ref; @@ -745,10 +714,9 @@ static int initialize_default_nodes(struct adf_accel_dev *accel_dev) /* Init default cluster for each root */ sla_in.type = RL_CLUSTER; - for (i = 0; i < ADF_SVC_NONE; i++) { + for (i = 0; i < SVC_BASE_COUNT; i++) { if (!rl_data->root[i]) continue; - sla_in.cir = rl_data->root[i]->cir; sla_in.pir = sla_in.cir; sla_in.srv = rl_data->root[i]->srv; @@ -987,7 +955,7 @@ int adf_rl_get_capability_remaining(struct adf_accel_dev *accel_dev, struct rl_sla *sla = NULL; int i; - if (srv >= ADF_SVC_NONE) + if (srv >= SVC_BASE_COUNT) return -EINVAL; if (sla_id > RL_SLA_EMPTY_ID && !validate_sla_id(accel_dev, sla_id)) { @@ -1086,9 +1054,9 @@ int adf_rl_init(struct adf_accel_dev *accel_dev) int ret = 0; /* Validate device parameters */ - if (RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[ADF_SVC_ASYM]) || - RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[ADF_SVC_SYM]) || - RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[ADF_SVC_DC]) || + if (RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_ASYM]) || + RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_SYM]) || + RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_DC]) || RL_VALIDATE_NON_ZERO(rl_hw_data->scan_interval) || RL_VALIDATE_NON_ZERO(rl_hw_data->pcie_scale_div) || RL_VALIDATE_NON_ZERO(rl_hw_data->pcie_scale_mul) || diff --git a/drivers/crypto/intel/qat/qat_common/adf_rl.h b/drivers/crypto/intel/qat/qat_common/adf_rl.h index bfe750ea0e83..c1f3f9a51195 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_rl.h +++ b/drivers/crypto/intel/qat/qat_common/adf_rl.h @@ -7,6 +7,8 @@ #include #include +#include "adf_cfg_services.h" + struct adf_accel_dev; #define RL_ROOT_MAX 4 @@ -24,13 +26,6 @@ enum rl_node_type { RL_LEAF, }; -enum adf_base_services { - ADF_SVC_ASYM = 0, - ADF_SVC_SYM, - ADF_SVC_DC, - ADF_SVC_NONE, -}; - /** * struct adf_rl_sla_input_data - ratelimiting user input data structure * @rp_mask: 64 bit bitmask of ring pair IDs which will be assigned to SLA. @@ -73,6 +68,7 @@ struct rl_slice_cnt { u8 dcpr_cnt; u8 pke_cnt; u8 cph_cnt; + u8 cpr_cnt; }; struct adf_rl_interface_data { @@ -94,6 +90,7 @@ struct adf_rl_hw_data { u32 pcie_scale_div; u32 dcpr_correction; u32 max_tp[RL_ROOT_MAX]; + u32 svc_ae_mask[SVC_BASE_COUNT]; struct rl_slice_cnt slices; }; diff --git a/drivers/crypto/intel/qat/qat_common/adf_rl_admin.c b/drivers/crypto/intel/qat/qat_common/adf_rl_admin.c index 698a14f4ce66..4a3e0591fdba 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_rl_admin.c +++ b/drivers/crypto/intel/qat/qat_common/adf_rl_admin.c @@ -63,6 +63,7 @@ int adf_rl_send_admin_init_msg(struct adf_accel_dev *accel_dev, slices_int->pke_cnt = slices_resp.pke_cnt; /* For symmetric crypto, slice tokens are relative to the UCS slice */ slices_int->cph_cnt = slices_resp.ucs_cnt; + slices_int->cpr_cnt = slices_resp.cpr_cnt; return 0; } diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index c75d0b6cb0ad..31d1ef0cb1f5 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -155,7 +155,6 @@ static int adf_do_enable_sriov(struct adf_accel_dev *accel_dev) if (!device_iommu_mapped(&GET_DEV(accel_dev))) { dev_warn(&GET_DEV(accel_dev), "IOMMU should be enabled for SR-IOV to work correctly\n"); - return -EINVAL; } if (adf_dev_started(accel_dev)) { diff --git a/drivers/crypto/intel/qat/qat_common/adf_sysfs.c b/drivers/crypto/intel/qat/qat_common/adf_sysfs.c index 6c39194647f0..79c63dfa8ff3 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sysfs.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sysfs.c @@ -269,6 +269,8 @@ static ssize_t rp2srv_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%s\n", ADF_CFG_SYM); case ASYM: return sysfs_emit(buf, "%s\n", ADF_CFG_ASYM); + case DECOMP: + return sysfs_emit(buf, "%s\n", ADF_CFG_DECOMP); default: break; } diff --git a/drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c b/drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c index bedb514d4e30..f31556beed8b 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c @@ -32,9 +32,10 @@ enum rl_params { }; static const char *const rl_services[] = { - [ADF_SVC_ASYM] = "asym", - [ADF_SVC_SYM] = "sym", - [ADF_SVC_DC] = "dc", + [SVC_ASYM] = "asym", + [SVC_SYM] = "sym", + [SVC_DC] = "dc", + [SVC_DECOMP] = "decomp", }; static const char *const rl_operations[] = { @@ -282,7 +283,7 @@ static ssize_t srv_show(struct device *dev, struct device_attribute *attr, if (ret) return ret; - if (get == ADF_SVC_NONE) + if (get == SVC_BASE_COUNT) return -EINVAL; return sysfs_emit(buf, "%s\n", rl_services[get]); @@ -291,14 +292,22 @@ static ssize_t srv_show(struct device *dev, struct device_attribute *attr, static ssize_t srv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct adf_accel_dev *accel_dev; unsigned int val; int ret; + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + ret = sysfs_match_string(rl_services, buf); if (ret < 0) return ret; val = ret; + if (!adf_is_service_enabled(accel_dev, val)) + return -EINVAL; + ret = set_param_u(dev, SRV, val); if (ret) return ret; @@ -439,8 +448,8 @@ int adf_sysfs_rl_add(struct adf_accel_dev *accel_dev) dev_err(&GET_DEV(accel_dev), "Failed to create qat_rl attribute group\n"); - data->cap_rem_srv = ADF_SVC_NONE; - data->input.srv = ADF_SVC_NONE; + data->cap_rem_srv = SVC_BASE_COUNT; + data->input.srv = SVC_BASE_COUNT; data->sysfs_added = true; return ret; diff --git a/drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c b/drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c index f20ae7e35a0d..a32db273842a 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c +++ b/drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c @@ -538,6 +538,9 @@ static void tl_print_rp_srv(struct adf_accel_dev *accel_dev, struct seq_file *s, case ASYM: seq_printf(s, "%*s\n", TL_VALUE_MIN_PADDING, ADF_CFG_ASYM); break; + case DECOMP: + seq_printf(s, "%*s\n", TL_VALUE_MIN_PADDING, ADF_CFG_DECOMP); + break; default: seq_printf(s, "%*s\n", TL_VALUE_MIN_PADDING, TL_RP_SRV_UNKNOWN); break; diff --git a/drivers/crypto/intel/qat/qat_common/adf_transport_debug.c b/drivers/crypto/intel/qat/qat_common/adf_transport_debug.c index e2dd568b87b5..6c22bc9b28e4 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_transport_debug.c +++ b/drivers/crypto/intel/qat/qat_common/adf_transport_debug.c @@ -10,16 +10,21 @@ static DEFINE_MUTEX(ring_read_lock); static DEFINE_MUTEX(bank_read_lock); +#define ADF_RING_NUM_MSGS(ring) \ + (ADF_SIZE_TO_RING_SIZE_IN_BYTES(ring->ring_size) / \ + ADF_MSG_SIZE_TO_BYTES(ring->msg_size)) + static void *adf_ring_start(struct seq_file *sfile, loff_t *pos) { struct adf_etr_ring_data *ring = sfile->private; + unsigned int num_msg = ADF_RING_NUM_MSGS(ring); + loff_t val = *pos; mutex_lock(&ring_read_lock); - if (*pos == 0) + if (val == 0) return SEQ_START_TOKEN; - if (*pos >= (ADF_SIZE_TO_RING_SIZE_IN_BYTES(ring->ring_size) / - ADF_MSG_SIZE_TO_BYTES(ring->msg_size))) + if (val >= num_msg) return NULL; return ring->base_addr + @@ -29,13 +34,15 @@ static void *adf_ring_start(struct seq_file *sfile, loff_t *pos) static void *adf_ring_next(struct seq_file *sfile, void *v, loff_t *pos) { struct adf_etr_ring_data *ring = sfile->private; + unsigned int num_msg = ADF_RING_NUM_MSGS(ring); + loff_t val = *pos; - if (*pos >= (ADF_SIZE_TO_RING_SIZE_IN_BYTES(ring->ring_size) / - ADF_MSG_SIZE_TO_BYTES(ring->msg_size))) + (*pos)++; + + if (val >= num_msg) return NULL; - return ring->base_addr + - (ADF_MSG_SIZE_TO_BYTES(ring->msg_size) * (*pos)++); + return ring->base_addr + (ADF_MSG_SIZE_TO_BYTES(ring->msg_size) * val); } static int adf_ring_show(struct seq_file *sfile, void *v) diff --git a/drivers/crypto/intel/qat/qat_common/qat_algs.c b/drivers/crypto/intel/qat/qat_common/qat_algs.c index 3c4bba4a8779..43e6dd9b77b7 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_algs.c @@ -5,11 +5,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -154,19 +154,19 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, switch (ctx->qat_hash_alg) { case ICP_QAT_HW_AUTH_ALGO_SHA1: - if (crypto_shash_export(shash, &ctx->sha1)) + if (crypto_shash_export_core(shash, &ctx->sha1)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(ctx->sha1.state[i]); break; case ICP_QAT_HW_AUTH_ALGO_SHA256: - if (crypto_shash_export(shash, &ctx->sha256)) + if (crypto_shash_export_core(shash, &ctx->sha256)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(ctx->sha256.state[i]); break; case ICP_QAT_HW_AUTH_ALGO_SHA512: - if (crypto_shash_export(shash, &ctx->sha512)) + if (crypto_shash_export_core(shash, &ctx->sha512)) return -EFAULT; for (i = 0; i < digest_size >> 3; i++, hash512_state_out++) *hash512_state_out = cpu_to_be64(ctx->sha512.state[i]); @@ -190,19 +190,19 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, switch (ctx->qat_hash_alg) { case ICP_QAT_HW_AUTH_ALGO_SHA1: - if (crypto_shash_export(shash, &ctx->sha1)) + if (crypto_shash_export_core(shash, &ctx->sha1)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(ctx->sha1.state[i]); break; case ICP_QAT_HW_AUTH_ALGO_SHA256: - if (crypto_shash_export(shash, &ctx->sha256)) + if (crypto_shash_export_core(shash, &ctx->sha256)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(ctx->sha256.state[i]); break; case ICP_QAT_HW_AUTH_ALGO_SHA512: - if (crypto_shash_export(shash, &ctx->sha512)) + if (crypto_shash_export_core(shash, &ctx->sha512)) return -EFAULT; for (i = 0; i < digest_size >> 3; i++, hash512_state_out++) *hash512_state_out = cpu_to_be64(ctx->sha512.state[i]); @@ -1277,7 +1277,7 @@ static struct aead_alg qat_aeads[] = { { .base = { .cra_name = "authenc(hmac(sha1),cbc(aes))", .cra_driver_name = "qat_aes_cbc_hmac_sha1", - .cra_priority = 4001, + .cra_priority = 100, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct qat_alg_aead_ctx), @@ -1294,7 +1294,7 @@ static struct aead_alg qat_aeads[] = { { .base = { .cra_name = "authenc(hmac(sha256),cbc(aes))", .cra_driver_name = "qat_aes_cbc_hmac_sha256", - .cra_priority = 4001, + .cra_priority = 100, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct qat_alg_aead_ctx), @@ -1311,7 +1311,7 @@ static struct aead_alg qat_aeads[] = { { .base = { .cra_name = "authenc(hmac(sha512),cbc(aes))", .cra_driver_name = "qat_aes_cbc_hmac_sha512", - .cra_priority = 4001, + .cra_priority = 100, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct qat_alg_aead_ctx), @@ -1329,7 +1329,7 @@ static struct aead_alg qat_aeads[] = { { static struct skcipher_alg qat_skciphers[] = { { .base.cra_name = "cbc(aes)", .base.cra_driver_name = "qat_aes_cbc", - .base.cra_priority = 4001, + .base.cra_priority = 100, .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .base.cra_blocksize = AES_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct qat_alg_skcipher_ctx), @@ -1347,7 +1347,7 @@ static struct skcipher_alg qat_skciphers[] = { { }, { .base.cra_name = "ctr(aes)", .base.cra_driver_name = "qat_aes_ctr", - .base.cra_priority = 4001, + .base.cra_priority = 100, .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .base.cra_blocksize = 1, .base.cra_ctxsize = sizeof(struct qat_alg_skcipher_ctx), @@ -1365,7 +1365,7 @@ static struct skcipher_alg qat_skciphers[] = { { }, { .base.cra_name = "xts(aes)", .base.cra_driver_name = "qat_aes_xts", - .base.cra_priority = 4001, + .base.cra_priority = 100, .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ALLOCATES_MEMORY, .base.cra_blocksize = AES_BLOCK_SIZE, diff --git a/drivers/crypto/intel/qat/qat_common/qat_bl.c b/drivers/crypto/intel/qat/qat_common/qat_bl.c index 5e4dad4693ca..9b2338f58d97 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_bl.c +++ b/drivers/crypto/intel/qat/qat_common/qat_bl.c @@ -38,7 +38,7 @@ void qat_bl_free_bufl(struct adf_accel_dev *accel_dev, for (i = 0; i < blout->num_mapped_bufs; i++) { dma_unmap_single(dev, blout->buffers[i].addr, blout->buffers[i].len, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); } dma_unmap_single(dev, blpout, sz_out, DMA_TO_DEVICE); @@ -162,7 +162,7 @@ static int __qat_bl_sgl_to_bufl(struct adf_accel_dev *accel_dev, } buffers[y].addr = dma_map_single(dev, sg_virt(sg) + left, sg->length - left, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(dev, buffers[y].addr))) goto err_out; buffers[y].len = sg->length; @@ -204,7 +204,7 @@ static int __qat_bl_sgl_to_bufl(struct adf_accel_dev *accel_dev, if (!dma_mapping_error(dev, buflout->buffers[i].addr)) dma_unmap_single(dev, buflout->buffers[i].addr, buflout->buffers[i].len, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); } if (!buf->sgl_dst_valid) diff --git a/drivers/crypto/intel/qat/qat_common/qat_compression.c b/drivers/crypto/intel/qat/qat_common/qat_compression.c index c285b45b8679..53a4db5507ec 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_compression.c +++ b/drivers/crypto/intel/qat/qat_common/qat_compression.c @@ -196,7 +196,7 @@ static int qat_compression_alloc_dc_data(struct adf_accel_dev *accel_dev) struct adf_dc_data *dc_data = NULL; u8 *obuff = NULL; - dc_data = devm_kzalloc(dev, sizeof(*dc_data), GFP_KERNEL); + dc_data = kzalloc_node(sizeof(*dc_data), GFP_KERNEL, dev_to_node(dev)); if (!dc_data) goto err; @@ -204,7 +204,7 @@ static int qat_compression_alloc_dc_data(struct adf_accel_dev *accel_dev) if (!obuff) goto err; - obuff_p = dma_map_single(dev, obuff, ovf_buff_sz, DMA_FROM_DEVICE); + obuff_p = dma_map_single(dev, obuff, ovf_buff_sz, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(dev, obuff_p))) goto err; @@ -232,9 +232,9 @@ static void qat_free_dc_data(struct adf_accel_dev *accel_dev) return; dma_unmap_single(dev, dc_data->ovf_buff_p, dc_data->ovf_buff_sz, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); kfree_sensitive(dc_data->ovf_buff); - devm_kfree(dev, dc_data); + kfree(dc_data); accel_dev->dc_data = NULL; } diff --git a/drivers/crypto/marvell/cesa/cipher.c b/drivers/crypto/marvell/cesa/cipher.c index 48c5c8ea8c43..3fe0fd9226cf 100644 --- a/drivers/crypto/marvell/cesa/cipher.c +++ b/drivers/crypto/marvell/cesa/cipher.c @@ -75,9 +75,12 @@ mv_cesa_skcipher_dma_cleanup(struct skcipher_request *req) static inline void mv_cesa_skcipher_cleanup(struct skcipher_request *req) { struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req); + struct mv_cesa_engine *engine = creq->base.engine; if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ) mv_cesa_skcipher_dma_cleanup(req); + + atomic_sub(req->cryptlen, &engine->load); } static void mv_cesa_skcipher_std_step(struct skcipher_request *req) @@ -212,7 +215,6 @@ mv_cesa_skcipher_complete(struct crypto_async_request *req) struct mv_cesa_engine *engine = creq->base.engine; unsigned int ivsize; - atomic_sub(skreq->cryptlen, &engine->load); ivsize = crypto_skcipher_ivsize(crypto_skcipher_reqtfm(skreq)); if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ) { diff --git a/drivers/crypto/marvell/cesa/hash.c b/drivers/crypto/marvell/cesa/hash.c index 6815eddc9068..5103d36cdfdb 100644 --- a/drivers/crypto/marvell/cesa/hash.c +++ b/drivers/crypto/marvell/cesa/hash.c @@ -110,9 +110,12 @@ static inline void mv_cesa_ahash_dma_cleanup(struct ahash_request *req) static inline void mv_cesa_ahash_cleanup(struct ahash_request *req) { struct mv_cesa_ahash_req *creq = ahash_request_ctx(req); + struct mv_cesa_engine *engine = creq->base.engine; if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ) mv_cesa_ahash_dma_cleanup(req); + + atomic_sub(req->nbytes, &engine->load); } static void mv_cesa_ahash_last_cleanup(struct ahash_request *req) @@ -362,16 +365,13 @@ static void mv_cesa_ahash_complete(struct crypto_async_request *req) if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ && (creq->base.chain.last->flags & CESA_TDMA_TYPE_MSK) == CESA_TDMA_RESULT) { - __le32 *data = NULL; + const void *data; /* * Result is already in the correct endianness when the SA is * used */ data = creq->base.chain.last->op->ctx.hash.hash; - for (i = 0; i < digsize / 4; i++) - creq->state[i] = le32_to_cpu(data[i]); - memcpy(ahashreq->result, data, digsize); } else { for (i = 0; i < digsize / 4; i++) @@ -395,8 +395,6 @@ static void mv_cesa_ahash_complete(struct crypto_async_request *req) } } } - - atomic_sub(ahashreq->nbytes, &engine->load); } static void mv_cesa_ahash_prepare(struct crypto_async_request *req, diff --git a/drivers/crypto/marvell/octeontx2/otx2_cpt_common.h b/drivers/crypto/marvell/octeontx2/otx2_cpt_common.h index d529bcb03775..062def303dce 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cpt_common.h +++ b/drivers/crypto/marvell/octeontx2/otx2_cpt_common.h @@ -18,9 +18,8 @@ #define OTX2_CPT_MAX_VFS_NUM 128 #define OTX2_CPT_RVU_FUNC_ADDR_S(blk, slot, offs) \ (((blk) << 20) | ((slot) << 12) | (offs)) -#define OTX2_CPT_RVU_PFFUNC(pf, func) \ - ((((pf) & RVU_PFVF_PF_MASK) << RVU_PFVF_PF_SHIFT) | \ - (((func) & RVU_PFVF_FUNC_MASK) << RVU_PFVF_FUNC_SHIFT)) + +#define OTX2_CPT_RVU_PFFUNC(pdev, pf, func) rvu_make_pcifunc(pdev, pf, func) #define OTX2_CPT_INVALID_CRYPTO_ENG_GRP 0xFF #define OTX2_CPT_NAME_LENGTH 64 diff --git a/drivers/crypto/marvell/octeontx2/otx2_cpt_reqmgr.h b/drivers/crypto/marvell/octeontx2/otx2_cpt_reqmgr.h index e27e849b01df..e64ca30335de 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cpt_reqmgr.h +++ b/drivers/crypto/marvell/octeontx2/otx2_cpt_reqmgr.h @@ -34,6 +34,9 @@ #define SG_COMP_2 2 #define SG_COMP_1 1 +#define OTX2_CPT_DPTR_RPTR_ALIGN 8 +#define OTX2_CPT_RES_ADDR_ALIGN 32 + union otx2_cpt_opcode { u16 flags; struct { @@ -347,22 +350,48 @@ static inline struct otx2_cpt_inst_info * cn10k_sgv2_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, gfp_t gfp) { - u32 dlen = 0, g_len, sg_len, info_len; - int align = OTX2_CPT_DMA_MINALIGN; + u32 dlen = 0, g_len, s_len, sg_len, info_len; struct otx2_cpt_inst_info *info; - u16 g_sz_bytes, s_sz_bytes; u32 total_mem_len; int i; - g_sz_bytes = ((req->in_cnt + 2) / 3) * - sizeof(struct cn10kb_cpt_sglist_component); - s_sz_bytes = ((req->out_cnt + 2) / 3) * - sizeof(struct cn10kb_cpt_sglist_component); + /* Allocate memory to meet below alignment requirement: + * ------------------------------------ + * | struct otx2_cpt_inst_info | + * | (No alignment required) | + * | --------------------------------| + * | | padding for ARCH_DMA_MINALIGN | + * | | alignment | + * |------------------------------------| + * | SG List Gather/Input memory | + * | Length = multiple of 32Bytes | + * | Alignment = 8Byte | + * |---------------------------------- | + * | SG List Scatter/Output memory | + * | Length = multiple of 32Bytes | + * | Alignment = 8Byte | + * | -------------------------------| + * | | padding for 32B alignment | + * |------------------------------------| + * | Result response memory | + * | Alignment = 32Byte | + * ------------------------------------ + */ - g_len = ALIGN(g_sz_bytes, align); - sg_len = ALIGN(g_len + s_sz_bytes, align); - info_len = ALIGN(sizeof(*info), align); - total_mem_len = sg_len + info_len + sizeof(union otx2_cpt_res_s); + info_len = sizeof(*info); + + g_len = ((req->in_cnt + 2) / 3) * + sizeof(struct cn10kb_cpt_sglist_component); + s_len = ((req->out_cnt + 2) / 3) * + sizeof(struct cn10kb_cpt_sglist_component); + sg_len = g_len + s_len; + + /* Allocate extra memory for SG and response address alignment */ + total_mem_len = ALIGN(info_len, OTX2_CPT_DPTR_RPTR_ALIGN); + total_mem_len += (ARCH_DMA_MINALIGN - 1) & + ~(OTX2_CPT_DPTR_RPTR_ALIGN - 1); + total_mem_len += ALIGN(sg_len, OTX2_CPT_RES_ADDR_ALIGN); + total_mem_len += sizeof(union otx2_cpt_res_s); info = kzalloc(total_mem_len, gfp); if (unlikely(!info)) @@ -372,7 +401,8 @@ cn10k_sgv2_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, dlen += req->in[i].size; info->dlen = dlen; - info->in_buffer = (u8 *)info + info_len; + info->in_buffer = PTR_ALIGN((u8 *)info + info_len, ARCH_DMA_MINALIGN); + info->out_buffer = info->in_buffer + g_len; info->gthr_sz = req->in_cnt; info->sctr_sz = req->out_cnt; @@ -384,7 +414,7 @@ cn10k_sgv2_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, } if (sgv2io_components_setup(pdev, req->out, req->out_cnt, - &info->in_buffer[g_len])) { + info->out_buffer)) { dev_err(&pdev->dev, "Failed to setup scatter list\n"); goto destroy_info; } @@ -401,8 +431,10 @@ cn10k_sgv2_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, * Get buffer for union otx2_cpt_res_s response * structure and its physical address */ - info->completion_addr = info->in_buffer + sg_len; - info->comp_baddr = info->dptr_baddr + sg_len; + info->completion_addr = PTR_ALIGN((info->in_buffer + sg_len), + OTX2_CPT_RES_ADDR_ALIGN); + info->comp_baddr = ALIGN((info->dptr_baddr + sg_len), + OTX2_CPT_RES_ADDR_ALIGN); return info; @@ -417,10 +449,9 @@ static inline struct otx2_cpt_inst_info * otx2_sg_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, gfp_t gfp) { - int align = OTX2_CPT_DMA_MINALIGN; struct otx2_cpt_inst_info *info; - u32 dlen, align_dlen, info_len; - u16 g_sz_bytes, s_sz_bytes; + u32 dlen, info_len; + u16 g_len, s_len; u32 total_mem_len; if (unlikely(req->in_cnt > OTX2_CPT_MAX_SG_IN_CNT || @@ -429,22 +460,54 @@ otx2_sg_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, return NULL; } - g_sz_bytes = ((req->in_cnt + 3) / 4) * - sizeof(struct otx2_cpt_sglist_component); - s_sz_bytes = ((req->out_cnt + 3) / 4) * - sizeof(struct otx2_cpt_sglist_component); + /* Allocate memory to meet below alignment requirement: + * ------------------------------------ + * | struct otx2_cpt_inst_info | + * | (No alignment required) | + * | --------------------------------| + * | | padding for ARCH_DMA_MINALIGN | + * | | alignment | + * |------------------------------------| + * | SG List Header of 8 Byte | + * |------------------------------------| + * | SG List Gather/Input memory | + * | Length = multiple of 32Bytes | + * | Alignment = 8Byte | + * |---------------------------------- | + * | SG List Scatter/Output memory | + * | Length = multiple of 32Bytes | + * | Alignment = 8Byte | + * | -------------------------------| + * | | padding for 32B alignment | + * |------------------------------------| + * | Result response memory | + * | Alignment = 32Byte | + * ------------------------------------ + */ - dlen = g_sz_bytes + s_sz_bytes + SG_LIST_HDR_SIZE; - align_dlen = ALIGN(dlen, align); - info_len = ALIGN(sizeof(*info), align); - total_mem_len = align_dlen + info_len + sizeof(union otx2_cpt_res_s); + info_len = sizeof(*info); + + g_len = ((req->in_cnt + 3) / 4) * + sizeof(struct otx2_cpt_sglist_component); + s_len = ((req->out_cnt + 3) / 4) * + sizeof(struct otx2_cpt_sglist_component); + + dlen = g_len + s_len + SG_LIST_HDR_SIZE; + + /* Allocate extra memory for SG and response address alignment */ + total_mem_len = ALIGN(info_len, OTX2_CPT_DPTR_RPTR_ALIGN); + total_mem_len += (ARCH_DMA_MINALIGN - 1) & + ~(OTX2_CPT_DPTR_RPTR_ALIGN - 1); + total_mem_len += ALIGN(dlen, OTX2_CPT_RES_ADDR_ALIGN); + total_mem_len += sizeof(union otx2_cpt_res_s); info = kzalloc(total_mem_len, gfp); if (unlikely(!info)) return NULL; info->dlen = dlen; - info->in_buffer = (u8 *)info + info_len; + info->in_buffer = PTR_ALIGN((u8 *)info + info_len, ARCH_DMA_MINALIGN); + info->out_buffer = info->in_buffer + SG_LIST_HDR_SIZE + g_len; ((u16 *)info->in_buffer)[0] = req->out_cnt; ((u16 *)info->in_buffer)[1] = req->in_cnt; @@ -460,7 +523,7 @@ otx2_sg_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, } if (setup_sgio_components(pdev, req->out, req->out_cnt, - &info->in_buffer[8 + g_sz_bytes])) { + info->out_buffer)) { dev_err(&pdev->dev, "Failed to setup scatter list\n"); goto destroy_info; } @@ -476,8 +539,10 @@ otx2_sg_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req, * Get buffer for union otx2_cpt_res_s response * structure and its physical address */ - info->completion_addr = info->in_buffer + align_dlen; - info->comp_baddr = info->dptr_baddr + align_dlen; + info->completion_addr = PTR_ALIGN((info->in_buffer + dlen), + OTX2_CPT_RES_ADDR_ALIGN); + info->comp_baddr = ALIGN((info->dptr_baddr + dlen), + OTX2_CPT_RES_ADDR_ALIGN); return info; @@ -490,6 +555,7 @@ struct otx2_cptlf_wqe; int otx2_cpt_do_request(struct pci_dev *pdev, struct otx2_cpt_req_info *req, int cpu_num); void otx2_cpt_post_process(struct otx2_cptlf_wqe *wqe); -int otx2_cpt_get_kcrypto_eng_grp_num(struct pci_dev *pdev); +int otx2_cpt_get_eng_grp_num(struct pci_dev *pdev, + enum otx2_cpt_eng_type); #endif /* __OTX2_CPT_REQMGR_H */ diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptlf.h b/drivers/crypto/marvell/octeontx2/otx2_cptlf.h index 6e004a5568d8..1b9f75214d18 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptlf.h +++ b/drivers/crypto/marvell/octeontx2/otx2_cptlf.h @@ -124,7 +124,8 @@ struct otx2_cptlfs_info { struct cpt_hw_ops *ops; u8 are_lfs_attached; /* Whether CPT LFs are attached */ u8 lfs_num; /* Number of CPT LFs */ - u8 kcrypto_eng_grp_num; /* Kernel crypto engine group number */ + u8 kcrypto_se_eng_grp_num; /* Crypto symmetric engine group number */ + u8 kcrypto_ae_eng_grp_num; /* Crypto asymmetric engine group number */ u8 kvf_limits; /* Kernel crypto limits */ atomic_t state; /* LF's state. started/reset */ int blkaddr; /* CPT blkaddr: BLKADDR_CPT0/BLKADDR_CPT1 */ diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptpf_mbox.c b/drivers/crypto/marvell/octeontx2/otx2_cptpf_mbox.c index 12c0e966fa65..b4b2d3d1cbc2 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptpf_mbox.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptpf_mbox.c @@ -142,7 +142,7 @@ static int send_inline_ipsec_inbound_msg(struct otx2_cptpf_dev *cptpf, memset(req, 0, sizeof(*req)); req->hdr.id = MBOX_MSG_CPT_INLINE_IPSEC_CFG; req->hdr.sig = OTX2_MBOX_REQ_SIG; - req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptpf->pf_id, 0); + req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptpf->pdev, cptpf->pf_id, 0); req->dir = CPT_INLINE_INBOUND; req->slot = slot; req->sso_pf_func_ovrd = cptpf->sso_pf_func_ovrd; @@ -184,7 +184,8 @@ static int rx_inline_ipsec_lf_cfg(struct otx2_cptpf_dev *cptpf, u8 egrp, nix_req->gen_cfg.opcode = cpt_inline_rx_opcode(pdev); nix_req->gen_cfg.param1 = req->param1; nix_req->gen_cfg.param2 = req->param2; - nix_req->inst_qsel.cpt_pf_func = OTX2_CPT_RVU_PFFUNC(cptpf->pf_id, 0); + nix_req->inst_qsel.cpt_pf_func = + OTX2_CPT_RVU_PFFUNC(cptpf->pdev, cptpf->pf_id, 0); nix_req->inst_qsel.cpt_slot = 0; ret = otx2_cpt_send_mbox_msg(&cptpf->afpf_mbox, pdev); if (ret) @@ -392,9 +393,8 @@ void otx2_cptpf_vfpf_mbox_handler(struct work_struct *work) msg = (struct mbox_msghdr *)(mdev->mbase + offset); /* Set which VF sent this message based on mbox IRQ */ - msg->pcifunc = ((u16)cptpf->pf_id << RVU_PFVF_PF_SHIFT) | - ((vf->vf_id + 1) & RVU_PFVF_FUNC_MASK); - + msg->pcifunc = rvu_make_pcifunc(cptpf->pdev, cptpf->pf_id, + (vf->vf_id + 1)); err = cptpf_handle_vf_req(cptpf, vf, msg, msg->next_msgoff - offset); /* @@ -469,8 +469,7 @@ static void process_afpf_mbox_msg(struct otx2_cptpf_dev *cptpf, switch (msg->id) { case MBOX_MSG_READY: - cptpf->pf_id = (msg->pcifunc >> RVU_PFVF_PF_SHIFT) & - RVU_PFVF_PF_MASK; + cptpf->pf_id = rvu_get_pf(cptpf->pdev, msg->pcifunc); break; case MBOX_MSG_MSIX_OFFSET: rsp_msix = (struct msix_offset_rsp *) msg; diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c b/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c index 78367849c3d5..cc47e361089a 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c @@ -176,7 +176,9 @@ static int cptx_set_ucode_base(struct otx2_cpt_eng_grp_info *eng_grp, /* Set PF number for microcode fetches */ ret = otx2_cpt_write_af_reg(&cptpf->afpf_mbox, cptpf->pdev, CPT_AF_PF_FUNC, - cptpf->pf_id << RVU_PFVF_PF_SHIFT, blkaddr); + rvu_make_pcifunc(cptpf->pdev, + cptpf->pf_id, 0), + blkaddr); if (ret) return ret; @@ -1491,11 +1493,13 @@ int otx2_cpt_discover_eng_capabilities(struct otx2_cptpf_dev *cptpf) union otx2_cpt_opcode opcode; union otx2_cpt_res_s *result; union otx2_cpt_inst_s inst; + dma_addr_t result_baddr; dma_addr_t rptr_baddr; struct pci_dev *pdev; - u32 len, compl_rlen; + int timeout = 10000; + void *base, *rptr; int ret, etype; - void *rptr; + u32 len; /* * We don't get capabilities if it was already done @@ -1518,22 +1522,28 @@ int otx2_cpt_discover_eng_capabilities(struct otx2_cptpf_dev *cptpf) if (ret) goto delete_grps; - compl_rlen = ALIGN(sizeof(union otx2_cpt_res_s), OTX2_CPT_DMA_MINALIGN); - len = compl_rlen + LOADFVC_RLEN; + /* Allocate extra memory for "rptr" and "result" pointer alignment */ + len = LOADFVC_RLEN + ARCH_DMA_MINALIGN + + sizeof(union otx2_cpt_res_s) + OTX2_CPT_RES_ADDR_ALIGN; - result = kzalloc(len, GFP_KERNEL); - if (!result) { + base = kzalloc(len, GFP_KERNEL); + if (!base) { ret = -ENOMEM; goto lf_cleanup; } - rptr_baddr = dma_map_single(&pdev->dev, (void *)result, len, - DMA_BIDIRECTIONAL); + + rptr = PTR_ALIGN(base, ARCH_DMA_MINALIGN); + rptr_baddr = dma_map_single(&pdev->dev, rptr, len, DMA_BIDIRECTIONAL); if (dma_mapping_error(&pdev->dev, rptr_baddr)) { dev_err(&pdev->dev, "DMA mapping failed\n"); ret = -EFAULT; - goto free_result; + goto free_rptr; } - rptr = (u8 *)result + compl_rlen; + + result = (union otx2_cpt_res_s *)PTR_ALIGN(rptr + LOADFVC_RLEN, + OTX2_CPT_RES_ADDR_ALIGN); + result_baddr = ALIGN(rptr_baddr + LOADFVC_RLEN, + OTX2_CPT_RES_ADDR_ALIGN); /* Fill in the command */ opcode.s.major = LOADFVC_MAJOR_OP; @@ -1545,27 +1555,38 @@ int otx2_cpt_discover_eng_capabilities(struct otx2_cptpf_dev *cptpf) /* 64-bit swap for microcode data reads, not needed for addresses */ cpu_to_be64s(&iq_cmd.cmd.u); iq_cmd.dptr = 0; - iq_cmd.rptr = rptr_baddr + compl_rlen; + iq_cmd.rptr = rptr_baddr; iq_cmd.cptr.u = 0; for (etype = 1; etype < OTX2_CPT_MAX_ENG_TYPES; etype++) { result->s.compcode = OTX2_CPT_COMPLETION_CODE_INIT; iq_cmd.cptr.s.grp = otx2_cpt_get_eng_grp(&cptpf->eng_grps, etype); - otx2_cpt_fill_inst(&inst, &iq_cmd, rptr_baddr); + otx2_cpt_fill_inst(&inst, &iq_cmd, result_baddr); lfs->ops->send_cmd(&inst, 1, &cptpf->lfs.lf[0]); + timeout = 10000; while (lfs->ops->cpt_get_compcode(result) == - OTX2_CPT_COMPLETION_CODE_INIT) + OTX2_CPT_COMPLETION_CODE_INIT) { cpu_relax(); + udelay(1); + timeout--; + if (!timeout) { + ret = -ENODEV; + cptpf->is_eng_caps_discovered = false; + dev_warn(&pdev->dev, "Timeout on CPT load_fvc completion poll\n"); + goto error_no_response; + } + } cptpf->eng_caps[etype].u = be64_to_cpup(rptr); } - dma_unmap_single(&pdev->dev, rptr_baddr, len, DMA_BIDIRECTIONAL); cptpf->is_eng_caps_discovered = true; -free_result: - kfree(result); +error_no_response: + dma_unmap_single(&pdev->dev, rptr_baddr, len, DMA_BIDIRECTIONAL); +free_rptr: + kfree(base); lf_cleanup: otx2_cptlf_shutdown(lfs); delete_grps: diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.c index 7eb0bc13994d..8d9f394d6b50 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.c @@ -384,7 +384,8 @@ static inline int cpt_enc_dec(struct skcipher_request *req, u32 enc) req_info->req_type = OTX2_CPT_ENC_DEC_REQ; req_info->is_enc = enc; req_info->is_trunc_hmac = false; - req_info->ctrl.s.grp = otx2_cpt_get_kcrypto_eng_grp_num(pdev); + req_info->ctrl.s.grp = otx2_cpt_get_eng_grp_num(pdev, + OTX2_CPT_SE_TYPES); req_info->req.cptr = ctx->er_ctx.hw_ctx; req_info->req.cptr_dma = ctx->er_ctx.cptr_dma; @@ -1288,7 +1289,8 @@ static int cpt_aead_enc_dec(struct aead_request *req, u8 reg_type, u8 enc) if (status) return status; - req_info->ctrl.s.grp = otx2_cpt_get_kcrypto_eng_grp_num(pdev); + req_info->ctrl.s.grp = otx2_cpt_get_eng_grp_num(pdev, + OTX2_CPT_SE_TYPES); /* * We perform an asynchronous send and once diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c index 56904bdfd6e8..c1c44a7b89fa 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c @@ -265,17 +265,33 @@ static int cptvf_lf_init(struct otx2_cptvf_dev *cptvf) u8 eng_grp_msk; /* Get engine group number for symmetric crypto */ - cptvf->lfs.kcrypto_eng_grp_num = OTX2_CPT_INVALID_CRYPTO_ENG_GRP; + cptvf->lfs.kcrypto_se_eng_grp_num = OTX2_CPT_INVALID_CRYPTO_ENG_GRP; ret = otx2_cptvf_send_eng_grp_num_msg(cptvf, OTX2_CPT_SE_TYPES); if (ret) return ret; - if (cptvf->lfs.kcrypto_eng_grp_num == OTX2_CPT_INVALID_CRYPTO_ENG_GRP) { - dev_err(dev, "Engine group for kernel crypto not available\n"); - ret = -ENOENT; - return ret; + if (cptvf->lfs.kcrypto_se_eng_grp_num == + OTX2_CPT_INVALID_CRYPTO_ENG_GRP) { + dev_err(dev, + "Symmetric Engine group for crypto not available\n"); + return -ENOENT; } - eng_grp_msk = 1 << cptvf->lfs.kcrypto_eng_grp_num; + + /* Get engine group number for asymmetric crypto */ + cptvf->lfs.kcrypto_ae_eng_grp_num = OTX2_CPT_INVALID_CRYPTO_ENG_GRP; + ret = otx2_cptvf_send_eng_grp_num_msg(cptvf, OTX2_CPT_AE_TYPES); + if (ret) + return ret; + + if (cptvf->lfs.kcrypto_ae_eng_grp_num == + OTX2_CPT_INVALID_CRYPTO_ENG_GRP) { + dev_err(dev, + "Asymmetric Engine group for crypto not available\n"); + return -ENOENT; + } + + eng_grp_msk = BIT(cptvf->lfs.kcrypto_se_eng_grp_num) | + BIT(cptvf->lfs.kcrypto_ae_eng_grp_num); ret = otx2_cptvf_send_kvf_limits_msg(cptvf); if (ret) diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_mbox.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_mbox.c index 931b72580fd9..5277bcfa275e 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_mbox.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_mbox.c @@ -75,6 +75,7 @@ static void process_pfvf_mbox_mbox_msg(struct otx2_cptvf_dev *cptvf, struct otx2_cpt_caps_rsp *eng_caps; struct cpt_rd_wr_reg_msg *rsp_reg; struct msix_offset_rsp *rsp_msix; + u8 grp_num; int i; if (msg->id >= MBOX_MSG_MAX) { @@ -122,7 +123,11 @@ static void process_pfvf_mbox_mbox_msg(struct otx2_cptvf_dev *cptvf, break; case MBOX_MSG_GET_ENG_GRP_NUM: rsp_grp = (struct otx2_cpt_egrp_num_rsp *) msg; - cptvf->lfs.kcrypto_eng_grp_num = rsp_grp->eng_grp_num; + grp_num = rsp_grp->eng_grp_num; + if (rsp_grp->eng_type == OTX2_CPT_SE_TYPES) + cptvf->lfs.kcrypto_se_eng_grp_num = grp_num; + else if (rsp_grp->eng_type == OTX2_CPT_AE_TYPES) + cptvf->lfs.kcrypto_ae_eng_grp_num = grp_num; break; case MBOX_MSG_GET_KVF_LIMITS: rsp_limits = (struct otx2_cpt_kvf_limits_rsp *) msg; @@ -189,7 +194,7 @@ int otx2_cptvf_send_eng_grp_num_msg(struct otx2_cptvf_dev *cptvf, int eng_type) } req->hdr.id = MBOX_MSG_GET_ENG_GRP_NUM; req->hdr.sig = OTX2_MBOX_REQ_SIG; - req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->vf_id, 0); + req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0); req->eng_type = eng_type; return otx2_cpt_send_mbox_msg(mbox, pdev); @@ -210,7 +215,7 @@ int otx2_cptvf_send_kvf_limits_msg(struct otx2_cptvf_dev *cptvf) } req->id = MBOX_MSG_GET_KVF_LIMITS; req->sig = OTX2_MBOX_REQ_SIG; - req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->vf_id, 0); + req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0); return otx2_cpt_send_mbox_msg(mbox, pdev); } @@ -230,7 +235,7 @@ int otx2_cptvf_send_caps_msg(struct otx2_cptvf_dev *cptvf) } req->id = MBOX_MSG_GET_CAPS; req->sig = OTX2_MBOX_REQ_SIG; - req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->vf_id, 0); + req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0); return otx2_cpt_send_mbox_msg(mbox, pdev); } diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_reqmgr.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_reqmgr.c index 426244107037..e71494486c64 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_reqmgr.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_reqmgr.c @@ -391,9 +391,19 @@ void otx2_cpt_post_process(struct otx2_cptlf_wqe *wqe) &wqe->lfs->lf[wqe->lf_num].pqueue); } -int otx2_cpt_get_kcrypto_eng_grp_num(struct pci_dev *pdev) +int otx2_cpt_get_eng_grp_num(struct pci_dev *pdev, + enum otx2_cpt_eng_type eng_type) { struct otx2_cptvf_dev *cptvf = pci_get_drvdata(pdev); - return cptvf->lfs.kcrypto_eng_grp_num; + switch (eng_type) { + case OTX2_CPT_SE_TYPES: + return cptvf->lfs.kcrypto_se_eng_grp_num; + case OTX2_CPT_AE_TYPES: + return cptvf->lfs.kcrypto_ae_eng_grp_num; + default: + dev_err(&cptvf->pdev->dev, "Unsupported engine type"); + break; + } + return -ENXIO; } diff --git a/drivers/crypto/omap-aes-gcm.c b/drivers/crypto/omap-aes-gcm.c index c498950402e8..1f4586509ca4 100644 --- a/drivers/crypto/omap-aes-gcm.c +++ b/drivers/crypto/omap-aes-gcm.c @@ -38,7 +38,6 @@ static void omap_aes_gcm_finish_req(struct omap_aes_dev *dd, int ret) crypto_finalize_aead_request(dd->engine, req, ret); - pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); } diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 1ecf5f6ac04e..244e24e52987 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -400,7 +400,6 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err) crypto_finalize_skcipher_request(dd->engine, req, err); - pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); } diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c index a099460d5f21..9c5538ae17db 100644 --- a/drivers/crypto/omap-des.c +++ b/drivers/crypto/omap-des.c @@ -489,7 +489,6 @@ static void omap_des_finish_req(struct omap_des_dev *dd, int err) crypto_finalize_skcipher_request(dd->engine, req, err); - pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); } diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 56f192cb976d..6328e8026b91 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1167,7 +1167,6 @@ static void omap_sham_finish_req(struct ahash_request *req, int err) dd->flags &= ~(BIT(FLAGS_FINAL) | BIT(FLAGS_CPU) | BIT(FLAGS_DMA_READY) | BIT(FLAGS_OUTPUT_READY)); - pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); ctx->offset = 0; diff --git a/drivers/crypto/starfive/jh7110-hash.c b/drivers/crypto/starfive/jh7110-hash.c index 2c60a1047bc3..6cfe0238f615 100644 --- a/drivers/crypto/starfive/jh7110-hash.c +++ b/drivers/crypto/starfive/jh7110-hash.c @@ -493,25 +493,25 @@ static int starfive_hash_setkey(struct crypto_ahash *hash, static int starfive_sha224_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "sha224-generic", + return starfive_hash_init_tfm(hash, "sha224-lib", STARFIVE_HASH_SHA224, 0); } static int starfive_sha256_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "sha256-generic", + return starfive_hash_init_tfm(hash, "sha256-lib", STARFIVE_HASH_SHA256, 0); } static int starfive_sha384_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "sha384-generic", + return starfive_hash_init_tfm(hash, "sha384-lib", STARFIVE_HASH_SHA384, 0); } static int starfive_sha512_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "sha512-generic", + return starfive_hash_init_tfm(hash, "sha512-lib", STARFIVE_HASH_SHA512, 0); } @@ -523,25 +523,25 @@ static int starfive_sm3_init_tfm(struct crypto_ahash *hash) static int starfive_hmac_sha224_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "hmac(sha224-generic)", + return starfive_hash_init_tfm(hash, "hmac-sha224-lib", STARFIVE_HASH_SHA224, 1); } static int starfive_hmac_sha256_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "hmac(sha256-generic)", + return starfive_hash_init_tfm(hash, "hmac-sha256-lib", STARFIVE_HASH_SHA256, 1); } static int starfive_hmac_sha384_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "hmac(sha384-generic)", + return starfive_hash_init_tfm(hash, "hmac-sha384-lib", STARFIVE_HASH_SHA384, 1); } static int starfive_hmac_sha512_init_tfm(struct crypto_ahash *hash) { - return starfive_hash_init_tfm(hash, "hmac(sha512-generic)", + return starfive_hash_init_tfm(hash, "hmac-sha512-lib", STARFIVE_HASH_SHA512, 1); } diff --git a/drivers/crypto/stm32/Kconfig b/drivers/crypto/stm32/Kconfig index 49dfd161e9b9..d6dc848c82ee 100644 --- a/drivers/crypto/stm32/Kconfig +++ b/drivers/crypto/stm32/Kconfig @@ -1,13 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -config CRYPTO_DEV_STM32_CRC - tristate "Support for STM32 crc accelerators" - depends on ARCH_STM32 - select CRYPTO_HASH - select CRC32 - help - This enables support for the CRC32 hw accelerator which can be found - on STMicroelectronics STM32 SOC. - config CRYPTO_DEV_STM32_HASH tristate "Support for STM32 hash accelerators" depends on ARCH_STM32 || ARCH_U8500 diff --git a/drivers/crypto/stm32/Makefile b/drivers/crypto/stm32/Makefile index 518e0e0b11a9..c63004026afb 100644 --- a/drivers/crypto/stm32/Makefile +++ b/drivers/crypto/stm32/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_CRYPTO_DEV_STM32_CRC) += stm32-crc32.o obj-$(CONFIG_CRYPTO_DEV_STM32_HASH) += stm32-hash.o obj-$(CONFIG_CRYPTO_DEV_STM32_CRYP) += stm32-cryp.o diff --git a/drivers/crypto/stm32/stm32-crc32.c b/drivers/crypto/stm32/stm32-crc32.c deleted file mode 100644 index fd29785a3ecf..000000000000 --- a/drivers/crypto/stm32/stm32-crc32.c +++ /dev/null @@ -1,480 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) STMicroelectronics SA 2017 - * Author: Fabien Dessenne - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define DRIVER_NAME "stm32-crc32" -#define CHKSUM_DIGEST_SIZE 4 -#define CHKSUM_BLOCK_SIZE 1 - -/* Registers */ -#define CRC_DR 0x00000000 -#define CRC_CR 0x00000008 -#define CRC_INIT 0x00000010 -#define CRC_POL 0x00000014 - -/* Registers values */ -#define CRC_CR_RESET BIT(0) -#define CRC_CR_REV_IN_WORD (BIT(6) | BIT(5)) -#define CRC_CR_REV_IN_BYTE BIT(5) -#define CRC_CR_REV_OUT BIT(7) -#define CRC32C_INIT_DEFAULT 0xFFFFFFFF - -#define CRC_AUTOSUSPEND_DELAY 50 - -static unsigned int burst_size; -module_param(burst_size, uint, 0644); -MODULE_PARM_DESC(burst_size, "Select burst byte size (0 unlimited)"); - -struct stm32_crc { - struct list_head list; - struct device *dev; - void __iomem *regs; - struct clk *clk; - spinlock_t lock; -}; - -struct stm32_crc_list { - struct list_head dev_list; - spinlock_t lock; /* protect dev_list */ -}; - -static struct stm32_crc_list crc_list = { - .dev_list = LIST_HEAD_INIT(crc_list.dev_list), - .lock = __SPIN_LOCK_UNLOCKED(crc_list.lock), -}; - -struct stm32_crc_ctx { - u32 key; - u32 poly; -}; - -struct stm32_crc_desc_ctx { - u32 partial; /* crc32c: partial in first 4 bytes of that struct */ -}; - -static int stm32_crc32_cra_init(struct crypto_tfm *tfm) -{ - struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm); - - mctx->key = 0; - mctx->poly = CRC32_POLY_LE; - return 0; -} - -static int stm32_crc32c_cra_init(struct crypto_tfm *tfm) -{ - struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm); - - mctx->key = CRC32C_INIT_DEFAULT; - mctx->poly = CRC32C_POLY_LE; - return 0; -} - -static int stm32_crc_setkey(struct crypto_shash *tfm, const u8 *key, - unsigned int keylen) -{ - struct stm32_crc_ctx *mctx = crypto_shash_ctx(tfm); - - if (keylen != sizeof(u32)) - return -EINVAL; - - mctx->key = get_unaligned_le32(key); - return 0; -} - -static struct stm32_crc *stm32_crc_get_next_crc(void) -{ - struct stm32_crc *crc; - - spin_lock_bh(&crc_list.lock); - crc = list_first_entry_or_null(&crc_list.dev_list, struct stm32_crc, list); - if (crc) - list_move_tail(&crc->list, &crc_list.dev_list); - spin_unlock_bh(&crc_list.lock); - - return crc; -} - -static int stm32_crc_init(struct shash_desc *desc) -{ - struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); - struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); - struct stm32_crc *crc; - unsigned long flags; - - crc = stm32_crc_get_next_crc(); - if (!crc) - return -ENODEV; - - pm_runtime_get_sync(crc->dev); - - spin_lock_irqsave(&crc->lock, flags); - - /* Reset, set key, poly and configure in bit reverse mode */ - writel_relaxed(bitrev32(mctx->key), crc->regs + CRC_INIT); - writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL); - writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, - crc->regs + CRC_CR); - - /* Store partial result */ - ctx->partial = readl_relaxed(crc->regs + CRC_DR); - - spin_unlock_irqrestore(&crc->lock, flags); - - pm_runtime_mark_last_busy(crc->dev); - pm_runtime_put_autosuspend(crc->dev); - - return 0; -} - -static int burst_update(struct shash_desc *desc, const u8 *d8, - size_t length) -{ - struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); - struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); - struct stm32_crc *crc; - - crc = stm32_crc_get_next_crc(); - if (!crc) - return -ENODEV; - - pm_runtime_get_sync(crc->dev); - - if (!spin_trylock(&crc->lock)) { - /* Hardware is busy, calculate crc32 by software */ - if (mctx->poly == CRC32_POLY_LE) - ctx->partial = crc32_le(ctx->partial, d8, length); - else - ctx->partial = crc32c(ctx->partial, d8, length); - - goto pm_out; - } - - /* - * Restore previously calculated CRC for this context as init value - * Restore polynomial configuration - * Configure in register for word input data, - * Configure out register in reversed bit mode data. - */ - writel_relaxed(bitrev32(ctx->partial), crc->regs + CRC_INIT); - writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL); - writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, - crc->regs + CRC_CR); - - if (d8 != PTR_ALIGN(d8, sizeof(u32))) { - /* Configure for byte data */ - writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT, - crc->regs + CRC_CR); - while (d8 != PTR_ALIGN(d8, sizeof(u32)) && length) { - writeb_relaxed(*d8++, crc->regs + CRC_DR); - length--; - } - /* Configure for word data */ - writel_relaxed(CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, - crc->regs + CRC_CR); - } - - for (; length >= sizeof(u32); d8 += sizeof(u32), length -= sizeof(u32)) - writel_relaxed(*((u32 *)d8), crc->regs + CRC_DR); - - if (length) { - /* Configure for byte data */ - writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT, - crc->regs + CRC_CR); - while (length--) - writeb_relaxed(*d8++, crc->regs + CRC_DR); - } - - /* Store partial result */ - ctx->partial = readl_relaxed(crc->regs + CRC_DR); - - spin_unlock(&crc->lock); - -pm_out: - pm_runtime_mark_last_busy(crc->dev); - pm_runtime_put_autosuspend(crc->dev); - - return 0; -} - -static int stm32_crc_update(struct shash_desc *desc, const u8 *d8, - unsigned int length) -{ - const unsigned int burst_sz = burst_size; - unsigned int rem_sz; - const u8 *cur; - size_t size; - int ret; - - if (!burst_sz) - return burst_update(desc, d8, length); - - /* Digest first bytes not 32bit aligned at first pass in the loop */ - size = min_t(size_t, length, burst_sz + (size_t)d8 - - ALIGN_DOWN((size_t)d8, sizeof(u32))); - for (rem_sz = length, cur = d8; rem_sz; - rem_sz -= size, cur += size, size = min(rem_sz, burst_sz)) { - ret = burst_update(desc, cur, size); - if (ret) - return ret; - } - - return 0; -} - -static int stm32_crc_final(struct shash_desc *desc, u8 *out) -{ - struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); - struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); - - /* Send computed CRC */ - put_unaligned_le32(mctx->poly == CRC32C_POLY_LE ? - ~ctx->partial : ctx->partial, out); - - return 0; -} - -static int stm32_crc_finup(struct shash_desc *desc, const u8 *data, - unsigned int length, u8 *out) -{ - return stm32_crc_update(desc, data, length) ?: - stm32_crc_final(desc, out); -} - -static int stm32_crc_digest(struct shash_desc *desc, const u8 *data, - unsigned int length, u8 *out) -{ - return stm32_crc_init(desc) ?: stm32_crc_finup(desc, data, length, out); -} - -static unsigned int refcnt; -static DEFINE_MUTEX(refcnt_lock); -static struct shash_alg algs[] = { - /* CRC-32 */ - { - .setkey = stm32_crc_setkey, - .init = stm32_crc_init, - .update = stm32_crc_update, - .final = stm32_crc_final, - .finup = stm32_crc_finup, - .digest = stm32_crc_digest, - .descsize = sizeof(struct stm32_crc_desc_ctx), - .digestsize = CHKSUM_DIGEST_SIZE, - .base = { - .cra_name = "crc32", - .cra_driver_name = "stm32-crc32-crc32", - .cra_priority = 200, - .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, - .cra_blocksize = CHKSUM_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct stm32_crc_ctx), - .cra_module = THIS_MODULE, - .cra_init = stm32_crc32_cra_init, - } - }, - /* CRC-32Castagnoli */ - { - .setkey = stm32_crc_setkey, - .init = stm32_crc_init, - .update = stm32_crc_update, - .final = stm32_crc_final, - .finup = stm32_crc_finup, - .digest = stm32_crc_digest, - .descsize = sizeof(struct stm32_crc_desc_ctx), - .digestsize = CHKSUM_DIGEST_SIZE, - .base = { - .cra_name = "crc32c", - .cra_driver_name = "stm32-crc32-crc32c", - .cra_priority = 200, - .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, - .cra_blocksize = CHKSUM_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct stm32_crc_ctx), - .cra_module = THIS_MODULE, - .cra_init = stm32_crc32c_cra_init, - } - } -}; - -static int stm32_crc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct stm32_crc *crc; - int ret; - - crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL); - if (!crc) - return -ENOMEM; - - crc->dev = dev; - - crc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(crc->regs)) { - dev_err(dev, "Cannot map CRC IO\n"); - return PTR_ERR(crc->regs); - } - - crc->clk = devm_clk_get(dev, NULL); - if (IS_ERR(crc->clk)) { - dev_err(dev, "Could not get clock\n"); - return PTR_ERR(crc->clk); - } - - ret = clk_prepare_enable(crc->clk); - if (ret) { - dev_err(crc->dev, "Failed to enable clock\n"); - return ret; - } - - pm_runtime_set_autosuspend_delay(dev, CRC_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(dev); - - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_irq_safe(dev); - pm_runtime_enable(dev); - - spin_lock_init(&crc->lock); - - platform_set_drvdata(pdev, crc); - - spin_lock(&crc_list.lock); - list_add(&crc->list, &crc_list.dev_list); - spin_unlock(&crc_list.lock); - - mutex_lock(&refcnt_lock); - if (!refcnt) { - ret = crypto_register_shashes(algs, ARRAY_SIZE(algs)); - if (ret) { - mutex_unlock(&refcnt_lock); - dev_err(dev, "Failed to register\n"); - clk_disable_unprepare(crc->clk); - return ret; - } - } - refcnt++; - mutex_unlock(&refcnt_lock); - - dev_info(dev, "Initialized\n"); - - pm_runtime_put_sync(dev); - - return 0; -} - -static void stm32_crc_remove(struct platform_device *pdev) -{ - struct stm32_crc *crc = platform_get_drvdata(pdev); - int ret = pm_runtime_get_sync(crc->dev); - - spin_lock(&crc_list.lock); - list_del(&crc->list); - spin_unlock(&crc_list.lock); - - mutex_lock(&refcnt_lock); - if (!--refcnt) - crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); - mutex_unlock(&refcnt_lock); - - pm_runtime_disable(crc->dev); - pm_runtime_put_noidle(crc->dev); - - if (ret >= 0) - clk_disable(crc->clk); - clk_unprepare(crc->clk); -} - -static int __maybe_unused stm32_crc_suspend(struct device *dev) -{ - struct stm32_crc *crc = dev_get_drvdata(dev); - int ret; - - ret = pm_runtime_force_suspend(dev); - if (ret) - return ret; - - clk_unprepare(crc->clk); - - return 0; -} - -static int __maybe_unused stm32_crc_resume(struct device *dev) -{ - struct stm32_crc *crc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare(crc->clk); - if (ret) { - dev_err(crc->dev, "Failed to prepare clock\n"); - return ret; - } - - return pm_runtime_force_resume(dev); -} - -static int __maybe_unused stm32_crc_runtime_suspend(struct device *dev) -{ - struct stm32_crc *crc = dev_get_drvdata(dev); - - clk_disable(crc->clk); - - return 0; -} - -static int __maybe_unused stm32_crc_runtime_resume(struct device *dev) -{ - struct stm32_crc *crc = dev_get_drvdata(dev); - int ret; - - ret = clk_enable(crc->clk); - if (ret) { - dev_err(crc->dev, "Failed to enable clock\n"); - return ret; - } - - return 0; -} - -static const struct dev_pm_ops stm32_crc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_crc_suspend, - stm32_crc_resume) - SET_RUNTIME_PM_OPS(stm32_crc_runtime_suspend, - stm32_crc_runtime_resume, NULL) -}; - -static const struct of_device_id stm32_dt_ids[] = { - { .compatible = "st,stm32f7-crc", }, - {}, -}; -MODULE_DEVICE_TABLE(of, stm32_dt_ids); - -static struct platform_driver stm32_crc_driver = { - .probe = stm32_crc_probe, - .remove = stm32_crc_remove, - .driver = { - .name = DRIVER_NAME, - .pm = &stm32_crc_pm_ops, - .of_match_table = stm32_dt_ids, - }, -}; - -module_platform_driver(stm32_crc_driver); - -MODULE_AUTHOR("Fabien Dessenne "); -MODULE_DESCRIPTION("STMicrolectronics STM32 CRC32 hardware driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/stm32/stm32-cryp.c b/drivers/crypto/stm32/stm32-cryp.c index 5ce88e7a8f65..a89b4c5d62a0 100644 --- a/drivers/crypto/stm32/stm32-cryp.c +++ b/drivers/crypto/stm32/stm32-cryp.c @@ -851,7 +851,6 @@ static void stm32_cryp_finish_req(struct stm32_cryp *cryp, int err) if (!err && (!(is_gcm(cryp) || is_ccm(cryp) || is_ecb(cryp)))) stm32_cryp_get_iv(cryp); - pm_runtime_mark_last_busy(cryp->dev); pm_runtime_put_autosuspend(cryp->dev); if (is_gcm(cryp) || is_ccm(cryp)) diff --git a/drivers/crypto/stm32/stm32-hash.c b/drivers/crypto/stm32/stm32-hash.c index 768b27de4737..a4436728b0db 100644 --- a/drivers/crypto/stm32/stm32-hash.c +++ b/drivers/crypto/stm32/stm32-hash.c @@ -1373,7 +1373,6 @@ static void stm32_hash_unprepare_request(struct ahash_request *req) *preg++ = stm32_hash_read(hdev, HASH_CSR(i)); pm_runtime: - pm_runtime_mark_last_busy(hdev->dev); pm_runtime_put_autosuspend(hdev->dev); } diff --git a/drivers/crypto/virtio/virtio_crypto_common.h b/drivers/crypto/virtio/virtio_crypto_common.h index 7059bbe5a2eb..19c934af3df6 100644 --- a/drivers/crypto/virtio/virtio_crypto_common.h +++ b/drivers/crypto/virtio/virtio_crypto_common.h @@ -113,8 +113,6 @@ struct virtio_crypto_request { int virtcrypto_devmgr_add_dev(struct virtio_crypto *vcrypto_dev); struct list_head *virtcrypto_devmgr_get_head(void); void virtcrypto_devmgr_rm_dev(struct virtio_crypto *vcrypto_dev); -struct virtio_crypto *virtcrypto_devmgr_get_first(void); -int virtcrypto_dev_in_use(struct virtio_crypto *vcrypto_dev); int virtcrypto_dev_get(struct virtio_crypto *vcrypto_dev); void virtcrypto_dev_put(struct virtio_crypto *vcrypto_dev); int virtcrypto_dev_started(struct virtio_crypto *vcrypto_dev); diff --git a/drivers/crypto/virtio/virtio_crypto_core.c b/drivers/crypto/virtio/virtio_crypto_core.c index 0d522049f595..3d241446099c 100644 --- a/drivers/crypto/virtio/virtio_crypto_core.c +++ b/drivers/crypto/virtio/virtio_crypto_core.c @@ -139,7 +139,7 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi) spin_lock_init(&vi->data_vq[i].lock); vi->data_vq[i].vq = vqs[i]; /* Initialize crypto engine */ - vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, true, NULL, true, + vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, true, true, virtqueue_get_vring_size(vqs[i])); if (!vi->data_vq[i].engine) { ret = -ENOMEM; diff --git a/drivers/crypto/virtio/virtio_crypto_mgr.c b/drivers/crypto/virtio/virtio_crypto_mgr.c index bddbd8ebfebe..06c74fa132cd 100644 --- a/drivers/crypto/virtio/virtio_crypto_mgr.c +++ b/drivers/crypto/virtio/virtio_crypto_mgr.c @@ -81,42 +81,6 @@ void virtcrypto_devmgr_rm_dev(struct virtio_crypto *vcrypto_dev) mutex_unlock(&table_lock); } -/* - * virtcrypto_devmgr_get_first() - * - * Function returns the first virtio crypto device from the acceleration - * framework. - * - * To be used by virtio crypto device specific drivers. - * - * Return: pointer to vcrypto_dev or NULL if not found. - */ -struct virtio_crypto *virtcrypto_devmgr_get_first(void) -{ - struct virtio_crypto *dev = NULL; - - mutex_lock(&table_lock); - if (!list_empty(&virtio_crypto_table)) - dev = list_first_entry(&virtio_crypto_table, - struct virtio_crypto, - list); - mutex_unlock(&table_lock); - return dev; -} - -/* - * virtcrypto_dev_in_use() - Check whether vcrypto_dev is currently in use - * @vcrypto_dev: Pointer to virtio crypto device. - * - * To be used by virtio crypto device specific drivers. - * - * Return: 1 when device is in use, 0 otherwise. - */ -int virtcrypto_dev_in_use(struct virtio_crypto *vcrypto_dev) -{ - return atomic_read(&vcrypto_dev->ref_count) != 0; -} - /* * virtcrypto_dev_get() - Increment vcrypto_dev reference count * @vcrypto_dev: Pointer to virtio crypto device. diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index a1a99ec3f12c..712624cba2b6 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -335,6 +335,63 @@ static int add_or_reset_cxl_resource(struct resource *parent, struct resource *r return rc; } +static int cxl_acpi_set_cache_size(struct cxl_root_decoder *cxlrd) +{ + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + struct range *hpa = &cxld->hpa_range; + resource_size_t size = range_len(hpa); + resource_size_t start = hpa->start; + resource_size_t cache_size; + struct resource res; + int nid, rc; + + res = DEFINE_RES(start, size, 0); + nid = phys_to_target_node(start); + + rc = hmat_get_extended_linear_cache_size(&res, nid, &cache_size); + if (rc) + return rc; + + /* + * The cache range is expected to be within the CFMWS. + * Currently there is only support cache_size == cxl_size. CXL + * size is then half of the total CFMWS window size. + */ + size = size >> 1; + if (cache_size && size != cache_size) { + dev_warn(&cxld->dev, + "Extended Linear Cache size %pa != CXL size %pa. No Support!", + &cache_size, &size); + return -ENXIO; + } + + cxlrd->cache_size = cache_size; + + return 0; +} + +static void cxl_setup_extended_linear_cache(struct cxl_root_decoder *cxlrd) +{ + int rc; + + rc = cxl_acpi_set_cache_size(cxlrd); + if (!rc) + return; + + if (rc != -EOPNOTSUPP) { + /* + * Failing to support extended linear cache region resize does not + * prevent the region from functioning. Only causes cxl list showing + * incorrect region size. + */ + dev_warn(cxlrd->cxlsd.cxld.dev.parent, + "Extended linear cache calculation failed rc:%d\n", rc); + } + + /* Ignoring return code */ + cxlrd->cache_size = 0; +} + DEFINE_FREE(put_cxlrd, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev)) DEFINE_FREE(del_cxl_resource, struct resource *, if (_T) del_cxl_resource(_T)) @@ -394,6 +451,8 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, ig = CXL_DECODER_MIN_GRANULARITY; cxld->interleave_granularity = ig; + cxl_setup_extended_linear_cache(cxlrd); + if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { if (ways != 1 && ways != 3) { cxims_ctx = (struct cxl_cxims_context) { diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 79e2ef81fde8..5ad8fef210b5 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -15,7 +15,6 @@ cxl_core-y += hdm.o cxl_core-y += pmu.o cxl_core-y += cdat.o cxl_core-y += ras.o -cxl_core-y += acpi.o cxl_core-$(CONFIG_TRACING) += trace.o cxl_core-$(CONFIG_CXL_REGION) += region.o cxl_core-$(CONFIG_CXL_MCE) += mce.o diff --git a/drivers/cxl/core/acpi.c b/drivers/cxl/core/acpi.c deleted file mode 100644 index f13b4dae6ac5..000000000000 --- a/drivers/cxl/core/acpi.c +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ -#include -#include "cxl.h" -#include "core.h" - -int cxl_acpi_get_extended_linear_cache_size(struct resource *backing_res, - int nid, resource_size_t *size) -{ - return hmat_get_extended_linear_cache_size(backing_res, nid, size); -} diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c index 0ccef2f2a26a..c0af645425f4 100644 --- a/drivers/cxl/core/cdat.c +++ b/drivers/cxl/core/cdat.c @@ -336,7 +336,7 @@ static int match_cxlrd_hb(struct device *dev, void *data) cxlrd = to_cxl_root_decoder(dev); cxlsd = &cxlrd->cxlsd; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); for (int i = 0; i < cxlsd->nr_targets; i++) { if (host_bridge == cxlsd->target[i]->dport_dev) return 1; @@ -987,7 +987,7 @@ void cxl_region_shared_upstream_bandwidth_update(struct cxl_region *cxlr) bool is_root; int rc; - lockdep_assert_held(&cxl_dpa_rwsem); + lockdep_assert_held(&cxl_rwsem.dpa); struct xarray *usp_xa __free(free_perf_xa) = kzalloc(sizeof(*usp_xa), GFP_KERNEL); @@ -1057,7 +1057,7 @@ void cxl_region_perf_data_calculate(struct cxl_region *cxlr, { struct cxl_dpa_perf *perf; - lockdep_assert_held(&cxl_dpa_rwsem); + lockdep_assert_held(&cxl_rwsem.dpa); perf = cxled_get_dpa_perf(cxled); if (IS_ERR(perf)) diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 29b61828a847..2669f251d677 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -5,6 +5,7 @@ #define __CXL_CORE_H__ #include +#include extern const struct device_type cxl_nvdimm_bridge_type; extern const struct device_type cxl_nvdimm_type; @@ -12,6 +13,11 @@ extern const struct device_type cxl_pmu_type; extern struct attribute_group cxl_base_attribute_group; +enum cxl_detach_mode { + DETACH_ONLY, + DETACH_INVALIDATE, +}; + #ifdef CONFIG_CXL_REGION extern struct device_attribute dev_attr_create_pmem_region; extern struct device_attribute dev_attr_create_ram_region; @@ -20,7 +26,11 @@ extern struct device_attribute dev_attr_region; extern const struct device_type cxl_pmem_region_type; extern const struct device_type cxl_dax_region_type; extern const struct device_type cxl_region_type; -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled); + +int cxl_decoder_detach(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos, + enum cxl_detach_mode mode); + #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr) #define CXL_REGION_TYPE(x) (&cxl_region_type) #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr), @@ -48,8 +58,11 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port) { return 0; } -static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled) +static inline int cxl_decoder_detach(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, + int pos, enum cxl_detach_mode mode) { + return 0; } static inline int cxl_region_init(void) { @@ -80,6 +93,7 @@ int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size); int cxl_dpa_free(struct cxl_endpoint_decoder *cxled); resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled); resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled); +bool cxl_resource_contains_addr(const struct resource *res, const resource_size_t addr); enum cxl_rcrb { CXL_RCRB_DOWNSTREAM, @@ -96,8 +110,20 @@ u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb); #define PCI_RCRB_CAP_HDR_NEXT_MASK GENMASK(15, 8) #define PCI_CAP_EXP_SIZEOF 0x3c -extern struct rw_semaphore cxl_dpa_rwsem; -extern struct rw_semaphore cxl_region_rwsem; +struct cxl_rwsem { + /* + * All changes to HPA (interleave configuration) occur with this + * lock held for write. + */ + struct rw_semaphore region; + /* + * All changes to a device DPA space occur with this lock held + * for write. + */ + struct rw_semaphore dpa; +}; + +extern struct cxl_rwsem cxl_rwsem; int cxl_memdev_init(void); void cxl_memdev_exit(void); @@ -120,8 +146,6 @@ int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port, int cxl_ras_init(void); void cxl_ras_exit(void); int cxl_gpf_port_setup(struct cxl_dport *dport); -int cxl_acpi_get_extended_linear_cache_size(struct resource *backing_res, - int nid, resource_size_t *size); #ifdef CONFIG_CXL_FEATURES struct cxl_feat_entry * diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c index 2cbc664e5d62..79994ca9bc9f 100644 --- a/drivers/cxl/core/edac.c +++ b/drivers/cxl/core/edac.c @@ -103,10 +103,10 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx, u8 *cap, u16 *cycle, u8 *flags, u8 *min_cycle) { struct cxl_mailbox *cxl_mbox; - u8 min_scrub_cycle = U8_MAX; struct cxl_region_params *p; struct cxl_memdev *cxlmd; struct cxl_region *cxlr; + u8 min_scrub_cycle = 0; int i, ret; if (!cxl_ps_ctx->cxlr) { @@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx, flags, min_cycle); } - struct rw_semaphore *region_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_region_rwsem); - if (!region_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) + return ret; cxlr = cxl_ps_ctx->cxlr; p = &cxlr->params; @@ -133,8 +132,12 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx, if (ret) return ret; + /* + * The min_scrub_cycle of a region is the max of minimum scrub + * cycles supported by memdevs that back the region. + */ if (min_cycle) - min_scrub_cycle = min(*min_cycle, min_scrub_cycle); + min_scrub_cycle = max(*min_cycle, min_scrub_cycle); } if (min_cycle) @@ -154,10 +157,9 @@ static int cxl_scrub_set_attrbs_region(struct device *dev, struct cxl_region *cxlr; int ret, i; - struct rw_semaphore *region_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_region_rwsem); - if (!region_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) + return ret; cxlr = cxl_ps_ctx->cxlr; p = &cxlr->params; @@ -693,7 +695,7 @@ static int cxl_set_ecs_threshold(struct device *dev, u8 *log_cap, u16 *config, ECS_THRESHOLD_IDX_4096); break; default: - dev_dbg(dev, "Invalid CXL ECS threshold count(%d) to set\n", + dev_dbg(dev, "Invalid CXL ECS threshold count(%u) to set\n", val); dev_dbg(dev, "Supported ECS threshold counts: %u, %u, %u\n", ECS_THRESHOLD_256, ECS_THRESHOLD_1024, @@ -1099,8 +1101,10 @@ int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd, union cxl_event *evt) old_rec = xa_store(&array_rec->rec_gen_media, le64_to_cpu(rec->media_hdr.phys_addr), rec, GFP_KERNEL); - if (xa_is_err(old_rec)) + if (xa_is_err(old_rec)) { + kfree(rec); return xa_err(old_rec); + } kfree(old_rec); @@ -1127,8 +1131,10 @@ int cxl_store_rec_dram(struct cxl_memdev *cxlmd, union cxl_event *evt) old_rec = xa_store(&array_rec->rec_dram, le64_to_cpu(rec->media_hdr.phys_addr), rec, GFP_KERNEL); - if (xa_is_err(old_rec)) + if (xa_is_err(old_rec)) { + kfree(rec); return xa_err(old_rec); + } kfree(old_rec); @@ -1315,7 +1321,7 @@ cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, attrbs.bank = ctx->bank; break; case EDAC_REPAIR_RANK_SPARING: - attrbs.repair_type = CXL_BANK_SPARING; + attrbs.repair_type = CXL_RANK_SPARING; break; default: return NULL; @@ -1332,16 +1338,15 @@ cxl_mem_perform_sparing(struct device *dev, struct cxl_memdev_sparing_in_payload sparing_pi; struct cxl_event_dram *rec = NULL; u16 validity_flags = 0; + int ret; - struct rw_semaphore *region_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_region_rwsem); - if (!region_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) + return ret; - struct rw_semaphore *dpa_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_dpa_rwsem); - if (!dpa_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) + return ret; if (!cxl_sparing_ctx->cap_safe_when_in_use) { /* Memory to repair must be offline */ @@ -1515,7 +1520,7 @@ static int cxl_mem_sparing_set_dpa(struct device *dev, void *drv_data, u64 dpa) struct cxl_memdev *cxlmd = ctx->cxlmd; struct cxl_dev_state *cxlds = cxlmd->cxlds; - if (dpa < cxlds->dpa_res.start || dpa > cxlds->dpa_res.end) + if (!cxl_resource_contains_addr(&cxlds->dpa_res, dpa)) return -EINVAL; ctx->dpa = dpa; @@ -1779,16 +1784,15 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx) struct cxl_memdev_ppr_maintenance_attrbs maintenance_attrbs; struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd; struct cxl_mem_repair_attrbs attrbs = { 0 }; + int ret; - struct rw_semaphore *region_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_region_rwsem); - if (!region_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) + return ret; - struct rw_semaphore *dpa_lock __free(rwsem_read_release) = - rwsem_read_intr_acquire(&cxl_dpa_rwsem); - if (!dpa_lock) - return -EINTR; + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); + if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) + return ret; if (!cxl_ppr_ctx->media_accessible || !cxl_ppr_ctx->data_retained) { /* Memory to repair must be offline */ @@ -1884,7 +1888,7 @@ static int cxl_ppr_set_dpa(struct device *dev, void *drv_data, u64 dpa) struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd; struct cxl_dev_state *cxlds = cxlmd->cxlds; - if (dpa < cxlds->dpa_res.start || dpa > cxlds->dpa_res.end) + if (!cxl_resource_contains_addr(&cxlds->dpa_res, dpa)) return -EINVAL; cxl_ppr_ctx->dpa = dpa; @@ -1915,8 +1919,11 @@ static int cxl_ppr_set_nibble_mask(struct device *dev, void *drv_data, static int cxl_do_ppr(struct device *dev, void *drv_data, u32 val) { struct cxl_ppr_context *cxl_ppr_ctx = drv_data; + struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd; + struct cxl_dev_state *cxlds = cxlmd->cxlds; - if (!cxl_ppr_ctx->dpa || val != EDAC_DO_MEM_REPAIR) + if (val != EDAC_DO_MEM_REPAIR || + !cxl_resource_contains_addr(&cxlds->dpa_res, cxl_ppr_ctx->dpa)) return -EINVAL; return cxl_mem_perform_ppr(cxl_ppr_ctx); diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 6f2eae1eb126..7c750599ea69 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -544,7 +544,7 @@ static bool cxlctl_validate_set_features(struct cxl_features_state *cxlfs, u32 flags; if (rpc_in->op_size < sizeof(uuid_t)) - return ERR_PTR(-EINVAL); + return false; feat = cxl_feature_info(cxlfs, &rpc_in->set_feat_in.uuid); if (IS_ERR(feat)) diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index ab1007495f6b..e9e1d555cec6 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -16,7 +16,10 @@ * for enumerating these registers and capabilities. */ -DECLARE_RWSEM(cxl_dpa_rwsem); +struct cxl_rwsem cxl_rwsem = { + .region = __RWSEM_INITIALIZER(cxl_rwsem.region), + .dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa), +}; static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, int *target_map) @@ -214,7 +217,7 @@ void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds) { struct resource *p1, *p2; - guard(rwsem_read)(&cxl_dpa_rwsem); + guard(rwsem_read)(&cxl_rwsem.dpa); for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) { __cxl_dpa_debug(file, p1, 0); for (p2 = p1->child; p2; p2 = p2->sibling) @@ -266,7 +269,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled) struct resource *res = cxled->dpa_res; resource_size_t skip_start; - lockdep_assert_held_write(&cxl_dpa_rwsem); + lockdep_assert_held_write(&cxl_rwsem.dpa); /* save @skip_start, before @res is released */ skip_start = res->start - cxled->skip; @@ -281,7 +284,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled) static void cxl_dpa_release(void *cxled) { - guard(rwsem_write)(&cxl_dpa_rwsem); + guard(rwsem_write)(&cxl_rwsem.dpa); __cxl_dpa_release(cxled); } @@ -293,7 +296,7 @@ static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled) { struct cxl_port *port = cxled_to_port(cxled); - lockdep_assert_held_write(&cxl_dpa_rwsem); + lockdep_assert_held_write(&cxl_rwsem.dpa); devm_remove_action(&port->dev, cxl_dpa_release, cxled); __cxl_dpa_release(cxled); } @@ -361,7 +364,7 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled, struct resource *res; int rc; - lockdep_assert_held_write(&cxl_dpa_rwsem); + lockdep_assert_held_write(&cxl_rwsem.dpa); if (!len) { dev_warn(dev, "decoder%d.%d: empty reservation attempted\n", @@ -470,7 +473,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info) { struct device *dev = cxlds->dev; - guard(rwsem_write)(&cxl_dpa_rwsem); + guard(rwsem_write)(&cxl_rwsem.dpa); if (cxlds->nr_partitions) return -EBUSY; @@ -516,9 +519,8 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled, struct cxl_port *port = cxled_to_port(cxled); int rc; - down_write(&cxl_dpa_rwsem); - rc = __cxl_dpa_reserve(cxled, base, len, skipped); - up_write(&cxl_dpa_rwsem); + scoped_guard(rwsem_write, &cxl_rwsem.dpa) + rc = __cxl_dpa_reserve(cxled, base, len, skipped); if (rc) return rc; @@ -529,7 +531,7 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_dpa_reserve, "CXL"); resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled) { - guard(rwsem_read)(&cxl_dpa_rwsem); + guard(rwsem_read)(&cxl_rwsem.dpa); if (cxled->dpa_res) return resource_size(cxled->dpa_res); @@ -540,19 +542,26 @@ resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled) { resource_size_t base = -1; - lockdep_assert_held(&cxl_dpa_rwsem); + lockdep_assert_held(&cxl_rwsem.dpa); if (cxled->dpa_res) base = cxled->dpa_res->start; return base; } +bool cxl_resource_contains_addr(const struct resource *res, const resource_size_t addr) +{ + struct resource _addr = DEFINE_RES_MEM(addr, 1); + + return resource_contains(res, &_addr); +} + int cxl_dpa_free(struct cxl_endpoint_decoder *cxled) { struct cxl_port *port = cxled_to_port(cxled); struct device *dev = &cxled->cxld.dev; - guard(rwsem_write)(&cxl_dpa_rwsem); + guard(rwsem_write)(&cxl_rwsem.dpa); if (!cxled->dpa_res) return 0; if (cxled->cxld.region) { @@ -582,7 +591,7 @@ int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled, struct device *dev = &cxled->cxld.dev; int part; - guard(rwsem_write)(&cxl_dpa_rwsem); + guard(rwsem_write)(&cxl_rwsem.dpa); if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) return -EBUSY; @@ -614,7 +623,7 @@ static int __cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size) struct resource *p, *last; int part; - guard(rwsem_write)(&cxl_dpa_rwsem); + guard(rwsem_write)(&cxl_rwsem.dpa); if (cxled->cxld.region) { dev_dbg(dev, "decoder attached to %s\n", dev_name(&cxled->cxld.region->dev)); @@ -764,46 +773,12 @@ static int cxld_await_commit(void __iomem *hdm, int id) return -ETIMEDOUT; } -static int cxl_decoder_commit(struct cxl_decoder *cxld) +static void setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm) { - struct cxl_port *port = to_cxl_port(cxld->dev.parent); - struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); - void __iomem *hdm = cxlhdm->regs.hdm_decoder; - int id = cxld->id, rc; + int id = cxld->id; u64 base, size; u32 ctrl; - if (cxld->flags & CXL_DECODER_F_ENABLE) - return 0; - - if (cxl_num_decoders_committed(port) != id) { - dev_dbg(&port->dev, - "%s: out of order commit, expected decoder%d.%d\n", - dev_name(&cxld->dev), port->id, - cxl_num_decoders_committed(port)); - return -EBUSY; - } - - /* - * For endpoint decoders hosted on CXL memory devices that - * support the sanitize operation, make sure sanitize is not in-flight. - */ - if (is_endpoint_decoder(&cxld->dev)) { - struct cxl_endpoint_decoder *cxled = - to_cxl_endpoint_decoder(&cxld->dev); - struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); - struct cxl_memdev_state *mds = - to_cxl_memdev_state(cxlmd->cxlds); - - if (mds && mds->security.sanitize_active) { - dev_dbg(&cxlmd->dev, - "attempted to commit %s during sanitize\n", - dev_name(&cxld->dev)); - return -EBUSY; - } - } - - down_read(&cxl_dpa_rwsem); /* common decoder settings */ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id)); cxld_set_interleave(cxld, &ctrl); @@ -837,7 +812,47 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld) } writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); - up_read(&cxl_dpa_rwsem); +} + +static int cxl_decoder_commit(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); + void __iomem *hdm = cxlhdm->regs.hdm_decoder; + int id = cxld->id, rc; + + if (cxld->flags & CXL_DECODER_F_ENABLE) + return 0; + + if (cxl_num_decoders_committed(port) != id) { + dev_dbg(&port->dev, + "%s: out of order commit, expected decoder%d.%d\n", + dev_name(&cxld->dev), port->id, + cxl_num_decoders_committed(port)); + return -EBUSY; + } + + /* + * For endpoint decoders hosted on CXL memory devices that + * support the sanitize operation, make sure sanitize is not in-flight. + */ + if (is_endpoint_decoder(&cxld->dev)) { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_memdev_state *mds = + to_cxl_memdev_state(cxlmd->cxlds); + + if (mds && mds->security.sanitize_active) { + dev_dbg(&cxlmd->dev, + "attempted to commit %s during sanitize\n", + dev_name(&cxld->dev)); + return -EBUSY; + } + } + + scoped_guard(rwsem_read, &cxl_rwsem.dpa) + setup_hw_decoder(cxld, hdm); port->commit_end++; rc = cxld_await_commit(hdm, cxld->id); @@ -875,7 +890,7 @@ void cxl_port_commit_reap(struct cxl_decoder *cxld) { struct cxl_port *port = to_cxl_port(cxld->dev.parent); - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); /* * Once the highest committed decoder is disabled, free any other @@ -907,7 +922,6 @@ static void cxl_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); - down_read(&cxl_dpa_rwsem); ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT; writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); @@ -916,7 +930,6 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld) writel(0, hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id)); writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id)); writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id)); - up_read(&cxl_dpa_rwsem); cxld->flags &= ~CXL_DECODER_F_ENABLE; @@ -1025,7 +1038,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, else cxld->target_type = CXL_DECODER_DEVMEM; - guard(rwsem_write)(&cxl_region_rwsem); + guard(rwsem_write)(&cxl_rwsem.region); if (cxld->id != cxl_num_decoders_committed(port)) { dev_warn(&port->dev, "decoder%d.%d: Committed out of order\n", diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 2689e6453c5a..fa6dd0c94656 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -899,6 +899,10 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd, trace_cxl_generic_event(cxlmd, type, uuid, &evt->generic); return; } + if (event_type == CXL_CPER_EVENT_MEM_SPARING) { + trace_cxl_memory_sparing(cxlmd, type, &evt->mem_sparing); + return; + } if (trace_cxl_general_media_enabled() || trace_cxl_dram_enabled()) { u64 dpa, hpa = ULLONG_MAX, hpa_alias = ULLONG_MAX; @@ -909,8 +913,8 @@ 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(rwsem_read)(&cxl_region_rwsem); - guard(rwsem_read)(&cxl_dpa_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); + guard(rwsem_read)(&cxl_rwsem.dpa); dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK; cxlr = cxl_dpa_to_region(cxlmd, dpa); @@ -926,12 +930,30 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd, if (cxl_store_rec_gen_media((struct cxl_memdev *)cxlmd, evt)) dev_dbg(&cxlmd->dev, "CXL store rec_gen_media failed\n"); + if (evt->gen_media.media_hdr.descriptor & + CXL_GMER_EVT_DESC_THRESHOLD_EVENT) + WARN_ON_ONCE((evt->gen_media.media_hdr.type & + CXL_GMER_MEM_EVT_TYPE_AP_CME_COUNTER_EXPIRE) && + !get_unaligned_le24(evt->gen_media.cme_count)); + else + WARN_ON_ONCE(evt->gen_media.media_hdr.type & + CXL_GMER_MEM_EVT_TYPE_AP_CME_COUNTER_EXPIRE); + trace_cxl_general_media(cxlmd, type, cxlr, hpa, hpa_alias, &evt->gen_media); } else if (event_type == CXL_CPER_EVENT_DRAM) { if (cxl_store_rec_dram((struct cxl_memdev *)cxlmd, evt)) dev_dbg(&cxlmd->dev, "CXL store rec_dram failed\n"); + if (evt->dram.media_hdr.descriptor & + CXL_GMER_EVT_DESC_THRESHOLD_EVENT) + WARN_ON_ONCE((evt->dram.media_hdr.type & + CXL_DER_MEM_EVT_TYPE_AP_CME_COUNTER_EXPIRE) && + !get_unaligned_le24(evt->dram.cvme_count)); + else + WARN_ON_ONCE(evt->dram.media_hdr.type & + CXL_DER_MEM_EVT_TYPE_AP_CME_COUNTER_EXPIRE); + trace_cxl_dram(cxlmd, type, cxlr, hpa, hpa_alias, &evt->dram); } @@ -952,6 +974,8 @@ static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd, ev_type = CXL_CPER_EVENT_DRAM; else if (uuid_equal(uuid, &CXL_EVENT_MEM_MODULE_UUID)) ev_type = CXL_CPER_EVENT_MEM_MODULE; + else if (uuid_equal(uuid, &CXL_EVENT_MEM_SPARING_UUID)) + ev_type = CXL_CPER_EVENT_MEM_SPARING; cxl_event_trace_record(cxlmd, type, ev_type, uuid, &record->event); } @@ -1265,7 +1289,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd) /* synchronize with cxl_mem_probe() and decoder write operations */ guard(device)(&cxlmd->dev); endpoint = cxlmd->endpoint; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); /* * Require an endpoint to be safe otherwise the driver can not * be sure that the device is unmapped. @@ -1401,8 +1425,8 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, int nr_records = 0; int rc; - rc = mutex_lock_interruptible(&mds->poison.lock); - if (rc) + ACQUIRE(mutex_intr, lock)(&mds->poison.mutex); + if ((rc = ACQUIRE_ERR(mutex_intr, &lock))) return rc; po = mds->poison.list_out; @@ -1437,7 +1461,6 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, } } while (po->flags & CXL_POISON_FLAG_MORE); - mutex_unlock(&mds->poison.lock); return rc; } EXPORT_SYMBOL_NS_GPL(cxl_mem_get_poison, "CXL"); @@ -1473,7 +1496,7 @@ int cxl_poison_state_init(struct cxl_memdev_state *mds) return rc; } - mutex_init(&mds->poison.lock); + mutex_init(&mds->poison.mutex); return 0; } EXPORT_SYMBOL_NS_GPL(cxl_poison_state_init, "CXL"); diff --git a/drivers/cxl/core/mce.h b/drivers/cxl/core/mce.h index ace73424eeb6..ca272e8db6c7 100644 --- a/drivers/cxl/core/mce.h +++ b/drivers/cxl/core/mce.h @@ -7,7 +7,7 @@ #ifdef CONFIG_CXL_MCE int devm_cxl_register_mce_notifier(struct device *dev, - struct notifier_block *mce_notifer); + struct notifier_block *mce_notifier); #else static inline int devm_cxl_register_mce_notifier(struct device *dev, diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index f88a13adf7fa..c569e00a511f 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -232,15 +232,13 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd) if (!port || !is_cxl_endpoint(port)) return -EINVAL; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) return rc; - rc = down_read_interruptible(&cxl_dpa_rwsem); - if (rc) { - up_read(&cxl_region_rwsem); + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) return rc; - } if (cxl_num_decoders_committed(port) == 0) { /* No regions mapped to this memdev */ @@ -249,8 +247,6 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd) /* Regions mapped, collect poison by endpoint */ rc = cxl_get_poison_by_endpoint(port); } - up_read(&cxl_dpa_rwsem); - up_read(&cxl_region_rwsem); return rc; } @@ -267,7 +263,7 @@ static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa) dev_dbg(cxlds->dev, "device has no dpa resource\n"); return -EINVAL; } - if (dpa < cxlds->dpa_res.start || dpa > cxlds->dpa_res.end) { + if (!cxl_resource_contains_addr(&cxlds->dpa_res, dpa)) { dev_dbg(cxlds->dev, "dpa:0x%llx not in resource:%pR\n", dpa, &cxlds->dpa_res); return -EINVAL; @@ -292,19 +288,17 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) return rc; - rc = down_read_interruptible(&cxl_dpa_rwsem); - if (rc) { - up_read(&cxl_region_rwsem); + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) return rc; - } rc = cxl_validate_poison_dpa(cxlmd, dpa); if (rc) - goto out; + return rc; inject.address = cpu_to_le64(dpa); mbox_cmd = (struct cxl_mbox_cmd) { @@ -314,7 +308,7 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) }; rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); if (rc) - goto out; + return rc; cxlr = cxl_dpa_to_region(cxlmd, dpa); if (cxlr) @@ -327,11 +321,8 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) .length = cpu_to_le32(1), }; trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_INJECT); -out: - up_read(&cxl_dpa_rwsem); - up_read(&cxl_region_rwsem); - return rc; + return 0; } EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL"); @@ -347,19 +338,17 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa) if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) return rc; - rc = down_read_interruptible(&cxl_dpa_rwsem); - if (rc) { - up_read(&cxl_region_rwsem); + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) return rc; - } rc = cxl_validate_poison_dpa(cxlmd, dpa); if (rc) - goto out; + return rc; /* * In CXL 3.0 Spec 8.2.9.8.4.3, the Clear Poison mailbox command @@ -378,7 +367,7 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa) rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); if (rc) - goto out; + return rc; cxlr = cxl_dpa_to_region(cxlmd, dpa); if (cxlr) @@ -391,11 +380,8 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa) .length = cpu_to_le32(1), }; trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_CLEAR); -out: - up_read(&cxl_dpa_rwsem); - up_read(&cxl_region_rwsem); - return rc; + return 0; } EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL"); diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index eb46c6764d20..29197376b18e 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -30,18 +30,12 @@ * instantiated by the core. */ -/* - * All changes to the interleave configuration occur with this lock held - * for write. - */ -DECLARE_RWSEM(cxl_region_rwsem); - static DEFINE_IDA(cxl_port_ida); static DEFINE_XARRAY(cxl_root_buses); int cxl_num_decoders_committed(struct cxl_port *port) { - lockdep_assert_held(&cxl_region_rwsem); + lockdep_assert_held(&cxl_rwsem.region); return port->commit_end + 1; } @@ -176,7 +170,7 @@ static ssize_t target_list_show(struct device *dev, ssize_t offset; int rc; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); rc = emit_target_list(cxlsd, buf); if (rc < 0) return rc; @@ -196,7 +190,7 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr, struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); struct cxl_dev_state *cxlds = cxlmd->cxlds; - /* without @cxl_dpa_rwsem, make sure @part is not reloaded */ + /* without @cxl_rwsem.dpa, make sure @part is not reloaded */ int part = READ_ONCE(cxled->part); const char *desc; @@ -235,7 +229,7 @@ static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *at { struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); - guard(rwsem_read)(&cxl_dpa_rwsem); + guard(rwsem_read)(&cxl_rwsem.dpa); return sysfs_emit(buf, "%#llx\n", (u64)cxl_dpa_resource_start(cxled)); } static DEVICE_ATTR_RO(dpa_resource); @@ -560,7 +554,7 @@ static ssize_t decoders_committed_show(struct device *dev, { struct cxl_port *port = to_cxl_port(dev); - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); return sysfs_emit(buf, "%d\n", cxl_num_decoders_committed(port)); } @@ -1722,7 +1716,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, if (xa_empty(&port->dports)) return -EINVAL; - guard(rwsem_write)(&cxl_region_rwsem); + guard(rwsem_write)(&cxl_rwsem.region); for (i = 0; i < cxlsd->cxld.interleave_ways; i++) { struct cxl_dport *dport = find_dport(port, target_map[i]); @@ -2001,12 +1995,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL"); static void cxld_unregister(void *dev) { - struct cxl_endpoint_decoder *cxled; - - if (is_endpoint_decoder(dev)) { - cxled = to_cxl_endpoint_decoder(dev); - cxl_decoder_kill_region(cxled); - } + if (is_endpoint_decoder(dev)) + cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1, + DETACH_INVALIDATE); device_unregister(dev); } @@ -2293,7 +2284,7 @@ static const struct attribute_group *cxl_bus_attribute_groups[] = { NULL, }; -struct bus_type cxl_bus_type = { +const struct bus_type cxl_bus_type = { .name = "cxl", .uevent = cxl_bus_uevent, .match = cxl_bus_match, diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 485a831695c7..2731ba3a0799 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -31,40 +31,38 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, ras_cap.header_log); } -static void cxl_cper_trace_corr_prot_err(struct pci_dev *pdev, - struct cxl_ras_capability_regs ras_cap) +static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, + struct cxl_ras_capability_regs ras_cap) { u32 status = ras_cap.cor_status & ~ras_cap.cor_mask; - struct cxl_dev_state *cxlds; - cxlds = pci_get_drvdata(pdev); - if (!cxlds) - return; - - trace_cxl_aer_correctable_error(cxlds->cxlmd, status); + trace_cxl_aer_correctable_error(cxlmd, status); } -static void cxl_cper_trace_uncorr_prot_err(struct pci_dev *pdev, - struct cxl_ras_capability_regs ras_cap) +static void +cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, + struct cxl_ras_capability_regs ras_cap) { u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; - struct cxl_dev_state *cxlds; u32 fe; - cxlds = pci_get_drvdata(pdev); - if (!cxlds) - return; - if (hweight32(status) > 1) fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, ras_cap.cap_control)); else fe = status; - trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, ras_cap.header_log); } +static int match_memdev_by_parent(struct device *dev, const void *uport) +{ + if (is_cxl_memdev(dev) && dev->parent == uport) + return 1; + return 0; +} + static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) { unsigned int devfn = PCI_DEVFN(data->prot_err.agent_addr.device, @@ -73,13 +71,12 @@ static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) pci_get_domain_bus_and_slot(data->prot_err.agent_addr.segment, data->prot_err.agent_addr.bus, devfn); + struct cxl_memdev *cxlmd; int port_type; if (!pdev) return; - guard(device)(&pdev->dev); - port_type = pci_pcie_type(pdev); if (port_type == PCI_EXP_TYPE_ROOT_PORT || port_type == PCI_EXP_TYPE_DOWNSTREAM || @@ -92,10 +89,20 @@ static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) return; } + guard(device)(&pdev->dev); + if (!pdev->dev.driver) + return; + + struct device *mem_dev __free(put_device) = bus_find_device( + &cxl_bus_type, NULL, pdev, match_memdev_by_parent); + if (!mem_dev) + return; + + cxlmd = to_cxl_memdev(mem_dev); if (data->severity == AER_CORRECTABLE) - cxl_cper_trace_corr_prot_err(pdev, data->ras_cap); + cxl_cper_trace_corr_prot_err(cxlmd, data->ras_cap); else - cxl_cper_trace_uncorr_prot_err(pdev, data->ras_cap); + cxl_cper_trace_uncorr_prot_err(cxlmd, data->ras_cap); } static void cxl_cper_prot_err_work_fn(struct work_struct *work) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 6e5e1460068d..71cc42d05248 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -141,16 +141,12 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *attr, struct cxl_region_params *p = &cxlr->params; ssize_t rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem))) return rc; if (cxlr->mode != CXL_PARTMODE_PMEM) - rc = sysfs_emit(buf, "\n"); - else - rc = sysfs_emit(buf, "%pUb\n", &p->uuid); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "%pUb\n", &p->uuid); } static int is_dup(struct device *match, void *data) @@ -162,7 +158,7 @@ static int is_dup(struct device *match, void *data) if (!is_cxl_region(match)) return 0; - lockdep_assert_held(&cxl_region_rwsem); + lockdep_assert_held(&cxl_rwsem.region); cxlr = to_cxl_region(match); p = &cxlr->params; @@ -192,27 +188,22 @@ static ssize_t uuid_store(struct device *dev, struct device_attribute *attr, if (uuid_is_null(&temp)) return -EINVAL; - rc = down_write_killable(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_write_kill, region_rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, ®ion_rwsem))) return rc; if (uuid_equal(&p->uuid, &temp)) - goto out; + return len; - rc = -EBUSY; if (p->state >= CXL_CONFIG_ACTIVE) - goto out; + return -EBUSY; rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup); if (rc < 0) - goto out; + return rc; uuid_copy(&p->uuid, &temp); -out: - up_write(&cxl_region_rwsem); - if (rc) - return rc; return len; } static DEVICE_ATTR_RW(uuid); @@ -349,6 +340,58 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr) return rc; } +static int queue_reset(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + int rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + /* Already in the requested state? */ + if (p->state < CXL_CONFIG_COMMIT) + return 0; + + p->state = CXL_CONFIG_RESET_PENDING; + + return 0; +} + +static int __commit(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + int rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + /* Already in the requested state? */ + if (p->state >= CXL_CONFIG_COMMIT) + return 0; + + /* Not ready to commit? */ + if (p->state < CXL_CONFIG_ACTIVE) + return -ENXIO; + + /* + * Invalidate caches before region setup to drop any speculative + * consumption of this address space + */ + rc = cxl_region_invalidate_memregion(cxlr); + if (rc) + return rc; + + rc = cxl_region_decode_commit(cxlr); + if (rc) + return rc; + + p->state = CXL_CONFIG_COMMIT; + + return 0; +} + static ssize_t commit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -361,55 +404,38 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr, if (rc) return rc; - rc = down_write_killable(&cxl_region_rwsem); + if (commit) { + rc = __commit(cxlr); + if (rc) + return rc; + return len; + } + + rc = queue_reset(cxlr); if (rc) return rc; - /* Already in the requested state? */ - if (commit && p->state >= CXL_CONFIG_COMMIT) - goto out; - if (!commit && p->state < CXL_CONFIG_COMMIT) - goto out; - - /* Not ready to commit? */ - if (commit && p->state < CXL_CONFIG_ACTIVE) { - rc = -ENXIO; - goto out; - } - /* - * Invalidate caches before region setup to drop any speculative - * consumption of this address space + * Unmap the region and depend the reset-pending state to ensure + * it does not go active again until post reset */ - rc = cxl_region_invalidate_memregion(cxlr); - if (rc) - goto out; + device_release_driver(&cxlr->dev); - if (commit) { - rc = cxl_region_decode_commit(cxlr); - if (rc == 0) - p->state = CXL_CONFIG_COMMIT; - } else { - p->state = CXL_CONFIG_RESET_PENDING; - up_write(&cxl_region_rwsem); - device_release_driver(&cxlr->dev); - down_write(&cxl_region_rwsem); + /* + * With the reset pending take cxl_rwsem.region unconditionally + * to ensure the reset gets handled before returning. + */ + guard(rwsem_write)(&cxl_rwsem.region); - /* - * The lock was dropped, so need to revalidate that the reset is - * still pending. - */ - if (p->state == CXL_CONFIG_RESET_PENDING) { - cxl_region_decode_reset(cxlr, p->interleave_ways); - p->state = CXL_CONFIG_ACTIVE; - } + /* + * Revalidate that the reset is still pending in case another + * thread already handled this reset. + */ + if (p->state == CXL_CONFIG_RESET_PENDING) { + cxl_region_decode_reset(cxlr, p->interleave_ways); + p->state = CXL_CONFIG_ACTIVE; } -out: - up_write(&cxl_region_rwsem); - - if (rc) - return rc; return len; } @@ -420,13 +446,10 @@ static ssize_t commit_show(struct device *dev, struct device_attribute *attr, struct cxl_region_params *p = &cxlr->params; ssize_t rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; - rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT); } static DEVICE_ATTR_RW(commit); @@ -450,15 +473,12 @@ static ssize_t interleave_ways_show(struct device *dev, { struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_region_params *p = &cxlr->params; - ssize_t rc; + int rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; - rc = sysfs_emit(buf, "%d\n", p->interleave_ways); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%d\n", p->interleave_ways); } static const struct attribute_group *get_cxl_region_target_group(void); @@ -493,23 +513,21 @@ static ssize_t interleave_ways_store(struct device *dev, return -EINVAL; } - rc = down_write_killable(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) return rc; - if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { - rc = -EBUSY; - goto out; - } + + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) + return -EBUSY; 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; -out: - up_write(&cxl_region_rwsem); - if (rc) return rc; + } + return len; } static DEVICE_ATTR_RW(interleave_ways); @@ -520,15 +538,12 @@ static ssize_t interleave_granularity_show(struct device *dev, { struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_region_params *p = &cxlr->params; - ssize_t rc; + int rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; - rc = sysfs_emit(buf, "%d\n", p->interleave_granularity); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%d\n", p->interleave_granularity); } static ssize_t interleave_granularity_store(struct device *dev, @@ -561,19 +576,15 @@ static ssize_t interleave_granularity_store(struct device *dev, if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) return -EINVAL; - rc = down_write_killable(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) return rc; - if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { - rc = -EBUSY; - goto out; - } + + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) + return -EBUSY; p->interleave_granularity = val; -out: - up_write(&cxl_region_rwsem); - if (rc) - return rc; + return len; } static DEVICE_ATTR_RW(interleave_granularity); @@ -584,17 +595,15 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_region_params *p = &cxlr->params; u64 resource = -1ULL; - ssize_t rc; + int rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; + if (p->res) resource = p->res->start; - rc = sysfs_emit(buf, "%#llx\n", resource); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%#llx\n", resource); } static DEVICE_ATTR_RO(resource); @@ -622,7 +631,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) struct resource *res; u64 remainder = 0; - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); /* Nothing to do... */ if (p->res && resource_size(p->res) == size) @@ -664,7 +673,7 @@ static void cxl_region_iomem_release(struct cxl_region *cxlr) struct cxl_region_params *p = &cxlr->params; if (device_is_registered(&cxlr->dev)) - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); if (p->res) { /* * Autodiscovered regions may not have been able to insert their @@ -681,7 +690,7 @@ static int free_hpa(struct cxl_region *cxlr) { struct cxl_region_params *p = &cxlr->params; - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); if (!p->res) return 0; @@ -705,15 +714,14 @@ static ssize_t size_store(struct device *dev, struct device_attribute *attr, if (rc) return rc; - rc = down_write_killable(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) return rc; if (val) rc = alloc_hpa(cxlr, val); else rc = free_hpa(cxlr); - up_write(&cxl_region_rwsem); if (rc) return rc; @@ -729,15 +737,12 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr, u64 size = 0; ssize_t rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; if (p->res) size = resource_size(p->res); - rc = sysfs_emit(buf, "%#llx\n", size); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%#llx\n", size); } static DEVICE_ATTR_RW(size); @@ -763,26 +768,20 @@ static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos) struct cxl_endpoint_decoder *cxled; int rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; if (pos >= p->interleave_ways) { dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, p->interleave_ways); - rc = -ENXIO; - goto out; + return -ENXIO; } cxled = p->targets[pos]; if (!cxled) - rc = sysfs_emit(buf, "\n"); - else - rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev)); -out: - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev)); } static int check_commit_order(struct device *dev, void *data) @@ -897,7 +896,7 @@ cxl_port_pick_region_decoder(struct cxl_port *port, /* * This decoder is pinned registered as long as the endpoint decoder is * registered, and endpoint decoder unregistration holds the - * cxl_region_rwsem over unregister events, so no need to hold on to + * cxl_rwsem.region over unregister events, so no need to hold on to * this extra reference. */ put_device(dev); @@ -1088,7 +1087,7 @@ static int cxl_port_attach_region(struct cxl_port *port, unsigned long index; int rc = -EBUSY; - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); cxl_rr = cxl_rr_load(port, cxlr); if (cxl_rr) { @@ -1198,7 +1197,7 @@ static void cxl_port_detach_region(struct cxl_port *port, struct cxl_region_ref *cxl_rr; struct cxl_ep *ep = NULL; - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); cxl_rr = cxl_rr_load(port, cxlr); if (!cxl_rr) @@ -2094,27 +2093,43 @@ static int cxl_region_attach(struct cxl_region *cxlr, return 0; } -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) +static struct cxl_region * +__cxl_decoder_detach(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos, + enum cxl_detach_mode mode) { - struct cxl_port *iter, *ep_port = cxled_to_port(cxled); - struct cxl_region *cxlr = cxled->cxld.region; struct cxl_region_params *p; - int rc = 0; - lockdep_assert_held_write(&cxl_region_rwsem); + lockdep_assert_held_write(&cxl_rwsem.region); - if (!cxlr) - return 0; + if (!cxled) { + p = &cxlr->params; - p = &cxlr->params; - get_device(&cxlr->dev); + if (pos >= p->interleave_ways) { + dev_dbg(&cxlr->dev, "position %d out of range %d\n", + pos, p->interleave_ways); + return NULL; + } + + if (!p->targets[pos]) + return NULL; + cxled = p->targets[pos]; + } else { + cxlr = cxled->cxld.region; + if (!cxlr) + return NULL; + p = &cxlr->params; + } + + if (mode == DETACH_INVALIDATE) + cxled->part = -1; if (p->state > CXL_CONFIG_ACTIVE) { cxl_region_decode_reset(cxlr, p->interleave_ways); p->state = CXL_CONFIG_ACTIVE; } - for (iter = ep_port; !is_cxl_root(iter); + for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter); iter = to_cxl_port(iter->dev.parent)) cxl_port_detach_region(iter, cxlr, cxled); @@ -2125,7 +2140,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n", dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), cxled->pos); - goto out; + return NULL; } if (p->state == CXL_CONFIG_ACTIVE) { @@ -2139,74 +2154,79 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) .end = -1, }; - /* notify the region driver that one of its targets has departed */ - up_write(&cxl_region_rwsem); - device_release_driver(&cxlr->dev); - down_write(&cxl_region_rwsem); -out: - put_device(&cxlr->dev); - return rc; + get_device(&cxlr->dev); + return cxlr; } -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled) +/* + * Cleanup a decoder's interest in a region. There are 2 cases to + * handle, removing an unknown @cxled from a known position in a region + * (detach_target()) or removing a known @cxled from an unknown @cxlr + * (cxld_unregister()) + * + * When the detachment finds a region release the region driver. + */ +int cxl_decoder_detach(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos, + enum cxl_detach_mode mode) { - down_write(&cxl_region_rwsem); - cxled->part = -1; - cxl_region_detach(cxled); - up_write(&cxl_region_rwsem); + struct cxl_region *detach; + + /* when the decoder is being destroyed lock unconditionally */ + if (mode == DETACH_INVALIDATE) { + guard(rwsem_write)(&cxl_rwsem.region); + detach = __cxl_decoder_detach(cxlr, cxled, pos, mode); + } else { + int rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + detach = __cxl_decoder_detach(cxlr, cxled, pos, mode); + } + + if (detach) { + device_release_driver(&detach->dev); + put_device(&detach->dev); + } + return 0; +} + +static int __attach_target(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos, + unsigned int state) +{ + int rc; + + if (state == TASK_INTERRUPTIBLE) { + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + guard(rwsem_read)(&cxl_rwsem.dpa); + return cxl_region_attach(cxlr, cxled, pos); + } + guard(rwsem_write)(&cxl_rwsem.region); + guard(rwsem_read)(&cxl_rwsem.dpa); + return cxl_region_attach(cxlr, cxled, pos); } static int attach_target(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, int pos, unsigned int state) { - int rc = 0; + int rc = __attach_target(cxlr, cxled, pos, state); - if (state == TASK_INTERRUPTIBLE) - rc = down_write_killable(&cxl_region_rwsem); - else - down_write(&cxl_region_rwsem); - if (rc) - return rc; - - down_read(&cxl_dpa_rwsem); - rc = cxl_region_attach(cxlr, cxled, pos); - up_read(&cxl_dpa_rwsem); - up_write(&cxl_region_rwsem); - - if (rc) - dev_warn(cxled->cxld.dev.parent, - "failed to attach %s to %s: %d\n", - dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc); + if (rc == 0) + return 0; + dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n", + dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc); return rc; } static int detach_target(struct cxl_region *cxlr, int pos) { - struct cxl_region_params *p = &cxlr->params; - int rc; - - rc = down_write_killable(&cxl_region_rwsem); - if (rc) - return rc; - - if (pos >= p->interleave_ways) { - dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, - p->interleave_ways); - rc = -ENXIO; - goto out; - } - - if (!p->targets[pos]) { - rc = 0; - goto out; - } - - rc = cxl_region_detach(p->targets[pos]); -out: - up_write(&cxl_region_rwsem); - return rc; + return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY); } static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos, @@ -2451,16 +2471,16 @@ static int cxl_region_perf_attrs_callback(struct notifier_block *nb, unsigned long action, void *arg) { struct cxl_region *cxlr = container_of(nb, struct cxl_region, - memory_notifier); - struct memory_notify *mnb = arg; - int nid = mnb->status_change_nid; + node_notifier); + struct node_notify *nn = arg; + int nid = nn->nid; int region_nid; - if (nid == NUMA_NO_NODE || action != MEM_ONLINE) + if (action != NODE_ADDED_FIRST_MEMORY) return NOTIFY_DONE; /* - * No need to hold cxl_region_rwsem; region parameters are stable + * No need to hold cxl_rwsem.region; region parameters are stable * within the cxl_region driver. */ region_nid = phys_to_target_node(cxlr->params.res->start); @@ -2483,7 +2503,7 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb, int region_nid; /* - * No need to hold cxl_region_rwsem; region parameters are stable + * No need to hold cxl_rwsem.region; region parameters are stable * within the cxl_region driver. */ region_nid = phys_to_target_node(cxlr->params.res->start); @@ -2632,17 +2652,13 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr, struct cxl_decoder *cxld = to_cxl_decoder(dev); ssize_t rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) return rc; if (cxld->region) - rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev)); - else - rc = sysfs_emit(buf, "\n"); - up_read(&cxl_region_rwsem); - - return rc; + return sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev)); + return sysfs_emit(buf, "\n"); } DEVICE_ATTR_RO(region); @@ -2847,7 +2863,7 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg) if (!cxled || !cxled->dpa_res || !resource_size(cxled->dpa_res)) return 0; - if (dpa > cxled->dpa_res->end || dpa < cxled->dpa_res->start) + if (!cxl_resource_contains_addr(cxled->dpa_res, dpa)) return 0; /* @@ -2959,7 +2975,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, if (cxlrd->hpa_to_spa) hpa = cxlrd->hpa_to_spa(cxlrd, hpa); - if (hpa < p->res->start || hpa > p->res->end) { + if (!cxl_resource_contains_addr(p->res, hpa)) { dev_dbg(&cxlr->dev, "Addr trans fail: hpa 0x%llx not in region\n", hpa); return ULLONG_MAX; @@ -2981,7 +2997,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr) struct device *dev; int i; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); if (p->state != CXL_CONFIG_COMMIT) return -ENXIO; @@ -2993,7 +3009,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr) cxlr_pmem->hpa_range.start = p->res->start; cxlr_pmem->hpa_range.end = p->res->end; - /* Snapshot the region configuration underneath the cxl_region_rwsem */ + /* 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]; @@ -3070,7 +3086,7 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) struct cxl_dax_region *cxlr_dax; struct device *dev; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); if (p->state != CXL_CONFIG_COMMIT) return ERR_PTR(-ENXIO); @@ -3270,7 +3286,7 @@ static int match_region_by_range(struct device *dev, const void *data) cxlr = to_cxl_region(dev); p = &cxlr->params; - guard(rwsem_read)(&cxl_region_rwsem); + guard(rwsem_read)(&cxl_rwsem.region); if (p->res && p->res->start == r->start && p->res->end == r->end) return 1; @@ -3282,15 +3298,10 @@ static int cxl_extended_linear_cache_resize(struct cxl_region *cxlr, { struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_region_params *p = &cxlr->params; - int nid = phys_to_target_node(res->start); resource_size_t size = resource_size(res); resource_size_t cache_size, start; - int rc; - - rc = cxl_acpi_get_extended_linear_cache_size(res, nid, &cache_size); - if (rc) - return rc; + cache_size = cxlrd->cache_size; if (!cache_size) return 0; @@ -3330,7 +3341,7 @@ static int __construct_region(struct cxl_region *cxlr, struct resource *res; int rc; - guard(rwsem_write)(&cxl_region_rwsem); + guard(rwsem_write)(&cxl_rwsem.region); p = &cxlr->params; if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { dev_err(cxlmd->dev.parent, @@ -3466,10 +3477,10 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) attach_target(cxlr, cxled, -1, TASK_UNINTERRUPTIBLE); - down_read(&cxl_region_rwsem); - p = &cxlr->params; - attach = p->state == CXL_CONFIG_COMMIT; - up_read(&cxl_region_rwsem); + scoped_guard(rwsem_read, &cxl_rwsem.region) { + p = &cxlr->params; + attach = p->state == CXL_CONFIG_COMMIT; + } if (attach) { /* @@ -3494,12 +3505,12 @@ u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa) if (!endpoint) return ~0ULL; - guard(rwsem_write)(&cxl_region_rwsem); + guard(rwsem_write)(&cxl_rwsem.region); xa_for_each(&endpoint->regions, index, iter) { struct cxl_region_params *p = &iter->region->params; - if (p->res->start <= spa && spa <= p->res->end) { + if (cxl_resource_contains_addr(p->res, spa)) { if (!p->cache_size) return ~0ULL; @@ -3527,48 +3538,53 @@ static void shutdown_notifiers(void *_cxlr) { struct cxl_region *cxlr = _cxlr; - unregister_memory_notifier(&cxlr->memory_notifier); + unregister_node_notifier(&cxlr->node_notifier); unregister_mt_adistance_algorithm(&cxlr->adist_notifier); } +static int cxl_region_can_probe(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + int rc; + + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) { + dev_dbg(&cxlr->dev, "probe interrupted\n"); + return rc; + } + + if (p->state < CXL_CONFIG_COMMIT) { + dev_dbg(&cxlr->dev, "config state: %d\n", p->state); + return -ENXIO; + } + + if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) { + dev_err(&cxlr->dev, + "failed to activate, re-commit region and retry\n"); + return -ENXIO; + } + + return 0; +} + static int cxl_region_probe(struct device *dev) { struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_region_params *p = &cxlr->params; int rc; - rc = down_read_interruptible(&cxl_region_rwsem); - if (rc) { - dev_dbg(&cxlr->dev, "probe interrupted\n"); + rc = cxl_region_can_probe(cxlr); + if (rc) return rc; - } - - if (p->state < CXL_CONFIG_COMMIT) { - dev_dbg(&cxlr->dev, "config state: %d\n", p->state); - rc = -ENXIO; - goto out; - } - - if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) { - dev_err(&cxlr->dev, - "failed to activate, re-commit region and retry\n"); - rc = -ENXIO; - goto out; - } /* * From this point on any path that changes the region's state away from * CXL_CONFIG_COMMIT is also responsible for releasing the driver. */ -out: - up_read(&cxl_region_rwsem); - if (rc) - return rc; - - cxlr->memory_notifier.notifier_call = cxl_region_perf_attrs_callback; - cxlr->memory_notifier.priority = CXL_CALLBACK_PRI; - register_memory_notifier(&cxlr->memory_notifier); + cxlr->node_notifier.notifier_call = cxl_region_perf_attrs_callback; + cxlr->node_notifier.priority = CXL_CALLBACK_PRI; + register_node_notifier(&cxlr->node_notifier); cxlr->adist_notifier.notifier_call = cxl_region_calculate_adistance; cxlr->adist_notifier.priority = 100; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index 25ebfbc1616c..a53ec4798b12 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -214,12 +214,16 @@ TRACE_EVENT(cxl_overflow, #define CXL_EVENT_RECORD_FLAG_PERF_DEGRADED BIT(4) #define CXL_EVENT_RECORD_FLAG_HW_REPLACE BIT(5) #define CXL_EVENT_RECORD_FLAG_MAINT_OP_SUB_CLASS_VALID BIT(6) +#define CXL_EVENT_RECORD_FLAG_LD_ID_VALID BIT(7) +#define CXL_EVENT_RECORD_FLAG_HEAD_ID_VALID BIT(8) #define show_hdr_flags(flags) __print_flags(flags, " | ", \ { CXL_EVENT_RECORD_FLAG_PERMANENT, "PERMANENT_CONDITION" }, \ { CXL_EVENT_RECORD_FLAG_MAINT_NEEDED, "MAINTENANCE_NEEDED" }, \ { CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, "PERFORMANCE_DEGRADED" }, \ { CXL_EVENT_RECORD_FLAG_HW_REPLACE, "HARDWARE_REPLACEMENT_NEEDED" }, \ - { CXL_EVENT_RECORD_FLAG_MAINT_OP_SUB_CLASS_VALID, "MAINT_OP_SUB_CLASS_VALID" } \ + { CXL_EVENT_RECORD_FLAG_MAINT_OP_SUB_CLASS_VALID, "MAINT_OP_SUB_CLASS_VALID" }, \ + { CXL_EVENT_RECORD_FLAG_LD_ID_VALID, "LD_ID_VALID" }, \ + { CXL_EVENT_RECORD_FLAG_HEAD_ID_VALID, "HEAD_ID_VALID" } \ ) /* @@ -247,7 +251,9 @@ TRACE_EVENT(cxl_overflow, __field(u64, hdr_timestamp) \ __field(u8, hdr_length) \ __field(u8, hdr_maint_op_class) \ - __field(u8, hdr_maint_op_sub_class) + __field(u8, hdr_maint_op_sub_class) \ + __field(u16, hdr_ld_id) \ + __field(u8, hdr_head_id) #define CXL_EVT_TP_fast_assign(cxlmd, l, hdr) \ __assign_str(memdev); \ @@ -260,18 +266,22 @@ TRACE_EVENT(cxl_overflow, __entry->hdr_related_handle = le16_to_cpu((hdr).related_handle); \ __entry->hdr_timestamp = le64_to_cpu((hdr).timestamp); \ __entry->hdr_maint_op_class = (hdr).maint_op_class; \ - __entry->hdr_maint_op_sub_class = (hdr).maint_op_sub_class + __entry->hdr_maint_op_sub_class = (hdr).maint_op_sub_class; \ + __entry->hdr_ld_id = le16_to_cpu((hdr).ld_id); \ + __entry->hdr_head_id = (hdr).head_id #define CXL_EVT_TP_printk(fmt, ...) \ TP_printk("memdev=%s host=%s serial=%lld log=%s : time=%llu uuid=%pUb " \ "len=%d flags='%s' handle=%x related_handle=%x " \ - "maint_op_class=%u maint_op_sub_class=%u : " fmt, \ + "maint_op_class=%u maint_op_sub_class=%u " \ + "ld_id=%x head_id=%x : " fmt, \ __get_str(memdev), __get_str(host), __entry->serial, \ cxl_event_log_type_str(__entry->log), \ __entry->hdr_timestamp, &__entry->hdr_uuid, __entry->hdr_length,\ show_hdr_flags(__entry->hdr_flags), __entry->hdr_handle, \ __entry->hdr_related_handle, __entry->hdr_maint_op_class, \ __entry->hdr_maint_op_sub_class, \ + __entry->hdr_ld_id, __entry->hdr_head_id, \ ##__VA_ARGS__) TRACE_EVENT(cxl_generic_event, @@ -496,7 +506,10 @@ TRACE_EVENT(cxl_general_media, uuid_copy(&__entry->region_uuid, &uuid_null); } __entry->cme_threshold_ev_flags = rec->cme_threshold_ev_flags; - __entry->cme_count = get_unaligned_le24(rec->cme_count); + if (rec->media_hdr.descriptor & CXL_GMER_EVT_DESC_THRESHOLD_EVENT) + __entry->cme_count = get_unaligned_le24(rec->cme_count); + else + __entry->cme_count = 0; ), CXL_EVT_TP_printk("dpa=%llx dpa_flags='%s' " \ @@ -648,7 +661,10 @@ TRACE_EVENT(cxl_dram, CXL_EVENT_GEN_MED_COMP_ID_SIZE); __entry->sub_channel = rec->sub_channel; __entry->cme_threshold_ev_flags = rec->cme_threshold_ev_flags; - __entry->cvme_count = get_unaligned_le24(rec->cvme_count); + if (rec->media_hdr.descriptor & CXL_GMER_EVT_DESC_THRESHOLD_EVENT) + __entry->cvme_count = get_unaligned_le24(rec->cvme_count); + else + __entry->cvme_count = 0; ), CXL_EVT_TP_printk("dpa=%llx dpa_flags='%s' descriptor='%s' type='%s' sub_type='%s' " \ @@ -871,6 +887,111 @@ TRACE_EVENT(cxl_memory_module, ) ); +/* + * Memory Sparing Event Record - MSER + * + * CXL rev 3.2 section 8.2.10.2.1.4; Table 8-60 + */ +#define CXL_MSER_QUERY_RESOURCE_FLAG BIT(0) +#define CXL_MSER_HARD_SPARING_FLAG BIT(1) +#define CXL_MSER_DEV_INITED_FLAG BIT(2) +#define show_mem_sparing_flags(flags) __print_flags(flags, "|", \ + { CXL_MSER_QUERY_RESOURCE_FLAG, "Query Resources" }, \ + { CXL_MSER_HARD_SPARING_FLAG, "Hard Sparing" }, \ + { CXL_MSER_DEV_INITED_FLAG, "Device Initiated Sparing" } \ +) + +#define CXL_MSER_VALID_CHANNEL BIT(0) +#define CXL_MSER_VALID_RANK BIT(1) +#define CXL_MSER_VALID_NIBBLE BIT(2) +#define CXL_MSER_VALID_BANK_GROUP BIT(3) +#define CXL_MSER_VALID_BANK BIT(4) +#define CXL_MSER_VALID_ROW BIT(5) +#define CXL_MSER_VALID_COLUMN BIT(6) +#define CXL_MSER_VALID_COMPONENT_ID BIT(7) +#define CXL_MSER_VALID_COMPONENT_ID_FORMAT BIT(8) +#define CXL_MSER_VALID_SUB_CHANNEL BIT(9) +#define show_mem_sparing_valid_flags(flags) __print_flags(flags, "|", \ + { CXL_MSER_VALID_CHANNEL, "CHANNEL" }, \ + { CXL_MSER_VALID_RANK, "RANK" }, \ + { CXL_MSER_VALID_NIBBLE, "NIBBLE" }, \ + { CXL_MSER_VALID_BANK_GROUP, "BANK GROUP" }, \ + { CXL_MSER_VALID_BANK, "BANK" }, \ + { CXL_MSER_VALID_ROW, "ROW" }, \ + { CXL_MSER_VALID_COLUMN, "COLUMN" }, \ + { CXL_MSER_VALID_COMPONENT_ID, "COMPONENT ID" }, \ + { CXL_MSER_VALID_COMPONENT_ID_FORMAT, "COMPONENT ID PLDM FORMAT" }, \ + { CXL_MSER_VALID_SUB_CHANNEL, "SUB CHANNEL" } \ +) + +TRACE_EVENT(cxl_memory_sparing, + + TP_PROTO(const struct cxl_memdev *cxlmd, enum cxl_event_log_type log, + struct cxl_event_mem_sparing *rec), + + TP_ARGS(cxlmd, log, rec), + + TP_STRUCT__entry( + CXL_EVT_TP_entry + + /* Memory Sparing Event */ + __field(u8, flags) + __field(u8, result) + __field(u16, validity_flags) + __field(u16, res_avail) + __field(u8, channel) + __field(u8, rank) + __field(u32, nibble_mask) + __field(u8, bank_group) + __field(u8, bank) + __field(u32, row) + __field(u16, column) + __field(u8, sub_channel) + __array(u8, comp_id, CXL_EVENT_GEN_MED_COMP_ID_SIZE) + ), + + TP_fast_assign( + CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); + __entry->hdr_uuid = CXL_EVENT_MEM_SPARING_UUID; + + /* Memory Sparing Event */ + __entry->flags = rec->flags; + __entry->result = rec->result; + __entry->validity_flags = le16_to_cpu(rec->validity_flags); + __entry->res_avail = le16_to_cpu(rec->res_avail); + __entry->channel = rec->channel; + __entry->rank = rec->rank; + __entry->nibble_mask = get_unaligned_le24(rec->nibble_mask); + __entry->bank_group = rec->bank_group; + __entry->bank = rec->bank; + __entry->row = get_unaligned_le24(rec->row); + __entry->column = le16_to_cpu(rec->column); + __entry->sub_channel = rec->sub_channel; + memcpy(__entry->comp_id, &rec->component_id, + CXL_EVENT_GEN_MED_COMP_ID_SIZE); + ), + + CXL_EVT_TP_printk("flags='%s' result=%u validity_flags='%s' " \ + "spare resource avail=%u channel=%u rank=%u " \ + "nibble_mask=%x bank_group=%u bank=%u " \ + "row=%u column=%u sub_channel=%u " \ + "comp_id=%s comp_id_pldm_valid_flags='%s' " \ + "pldm_entity_id=%s pldm_resource_id=%s", + show_mem_sparing_flags(__entry->flags), + __entry->result, + show_mem_sparing_valid_flags(__entry->validity_flags), + __entry->res_avail, __entry->channel, __entry->rank, + __entry->nibble_mask, __entry->bank_group, __entry->bank, + __entry->row, __entry->column, __entry->sub_channel, + __print_hex(__entry->comp_id, CXL_EVENT_GEN_MED_COMP_ID_SIZE), + show_comp_id_pldm_flags(__entry->comp_id[0]), + show_pldm_entity_id(__entry->validity_flags, CXL_MSER_VALID_COMPONENT_ID, + CXL_MSER_VALID_COMPONENT_ID_FORMAT, __entry->comp_id), + show_pldm_resource_id(__entry->validity_flags, CXL_MSER_VALID_COMPONENT_ID, + CXL_MSER_VALID_COMPONENT_ID_FORMAT, __entry->comp_id) + ) +); + #define show_poison_trace_type(type) \ __print_symbolic(type, \ { CXL_POISON_TRACE_LIST, "List" }, \ diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 3f1695c96abc..847e37be42c4 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -11,6 +11,7 @@ #include #include #include +#include extern const struct nvdimm_security_ops *cxl_security_ops; @@ -423,6 +424,7 @@ typedef u64 (*cxl_hpa_to_spa_fn)(struct cxl_root_decoder *cxlrd, u64 hpa); /** * struct cxl_root_decoder - Static platform CXL address decoder * @res: host / parent resource for region allocations + * @cache_size: extended linear cache size if exists, otherwise zero. * @region_id: region id for next region provisioning event * @hpa_to_spa: translate CXL host-physical-address to Platform system-physical-address * @platform_data: platform specific configuration data @@ -432,6 +434,7 @@ typedef u64 (*cxl_hpa_to_spa_fn)(struct cxl_root_decoder *cxlrd, u64 hpa); */ struct cxl_root_decoder { struct resource *res; + resource_size_t cache_size; atomic_t region_id; cxl_hpa_to_spa_fn hpa_to_spa; void *platform_data; @@ -469,7 +472,7 @@ enum cxl_config_state { * @nr_targets: number of targets * @cache_size: extended linear cache size if exists, otherwise zero. * - * State transitions are protected by the cxl_region_rwsem + * State transitions are protected by cxl_rwsem.region */ struct cxl_region_params { enum cxl_config_state state; @@ -513,7 +516,7 @@ enum cxl_partition_mode { * @flags: Region state flags * @params: active + config params for the region * @coord: QoS access coordinates for the region - * @memory_notifier: notifier for setting the access coordinates to node + * @node_notifier: notifier for setting the access coordinates to node * @adist_notifier: notifier for calculating the abstract distance of node */ struct cxl_region { @@ -526,7 +529,7 @@ struct cxl_region { unsigned long flags; struct cxl_region_params params; struct access_coordinate coord[ACCESS_COORDINATE_MAX]; - struct notifier_block memory_notifier; + struct notifier_block node_notifier; struct notifier_block adist_notifier; }; @@ -815,7 +818,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, bool is_cxl_region(struct device *dev); -extern struct bus_type cxl_bus_type; +extern const struct bus_type cxl_bus_type; struct cxl_driver { const char *name; @@ -912,15 +915,4 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port); #endif u16 cxl_gpf_get_dvsec(struct device *dev); - -static inline struct rw_semaphore *rwsem_read_intr_acquire(struct rw_semaphore *rwsem) -{ - if (down_read_interruptible(rwsem)) - return NULL; - - return rwsem; -} - -DEFINE_FREE(rwsem_read_release, struct rw_semaphore *, if (_T) up_read(_T)) - #endif /* __CXL_H__ */ diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 551b0ba2caa1..751478dfc410 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -254,7 +254,7 @@ enum security_cmd_enabled_bits { * @max_errors: Maximum media error records held in device cache * @enabled_cmds: All poison commands enabled in the CEL * @list_out: The poison list payload returned by device - * @lock: Protect reads of the poison list + * @mutex: Protect reads of the poison list * * Reads of the poison list are synchronized to ensure that a reader * does not get an incomplete list because their request overlapped @@ -265,7 +265,7 @@ struct cxl_poison_state { u32 max_errors; DECLARE_BITMAP(enabled_cmds, CXL_POISON_ENABLED_MAX); struct cxl_mbox_poison_out *list_out; - struct mutex lock; /* Protect reads of poison list */ + struct mutex mutex; /* Protect reads of poison list */ }; /* @@ -633,6 +633,14 @@ struct cxl_mbox_identify { UUID_INIT(0xfe927475, 0xdd59, 0x4339, 0xa5, 0x86, 0x79, 0xba, 0xb1, \ 0x13, 0xb7, 0x74) +/* + * Memory Sparing Event Record UUID + * CXL rev 3.2 section 8.2.10.2.1.4: Table 8-60 + */ +#define CXL_EVENT_MEM_SPARING_UUID \ + UUID_INIT(0xe71f3a40, 0x2d29, 0x4092, 0x8a, 0x39, 0x4d, 0x1c, 0x96, \ + 0x6c, 0x7c, 0x65) + /* * Get Event Records output payload * CXL rev 3.0 section 8.2.9.2.2; Table 8-50 diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 785aa2af5eaa..bd100ac31672 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -379,7 +379,7 @@ static int cxl_pci_mbox_send(struct cxl_mailbox *cxl_mbox, { int rc; - mutex_lock_io(&cxl_mbox->mbox_mutex); + mutex_lock(&cxl_mbox->mbox_mutex); rc = __cxl_pci_mbox_send_cmd(cxl_mbox, cmd); mutex_unlock(&cxl_mbox->mbox_mutex); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index fe4b593331da..cf32dc50b7a6 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -181,7 +181,7 @@ static const struct bin_attribute *const cxl_cdat_bin_attributes[] = { }; static const struct attribute_group cxl_cdat_attribute_group = { - .bin_attrs_new = cxl_cdat_bin_attributes, + .bin_attrs = cxl_cdat_bin_attributes, .is_bin_visible = cxl_port_bin_attr_is_visible, }; diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 328231cfb028..2bb40a6060af 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -73,7 +72,7 @@ __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, return -1; } -static void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, +static void dax_set_mapping(struct vm_fault *vmf, unsigned long pfn, unsigned long fault_size) { unsigned long i, nr_pages = fault_size / PAGE_SIZE; @@ -89,7 +88,7 @@ static void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, ALIGN_DOWN(vmf->address, fault_size)); for (i = 0; i < nr_pages; i++) { - struct folio *folio = pfn_folio(pfn_t_to_pfn(pfn) + i); + struct folio *folio = pfn_folio(pfn + i); if (folio->mapping) continue; @@ -104,7 +103,7 @@ static vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax, { struct device *dev = &dev_dax->dev; phys_addr_t phys; - pfn_t pfn; + unsigned long pfn; unsigned int fault_size = PAGE_SIZE; if (check_vma(dev_dax, vmf->vma, __func__)) @@ -125,11 +124,11 @@ static vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax, return VM_FAULT_SIGBUS; } - pfn = phys_to_pfn_t(phys, 0); + pfn = PHYS_PFN(phys); dax_set_mapping(vmf, pfn, fault_size); - return vmf_insert_page_mkwrite(vmf, pfn_t_to_page(pfn), + return vmf_insert_page_mkwrite(vmf, pfn_to_page(pfn), vmf->flags & FAULT_FLAG_WRITE); } @@ -140,7 +139,7 @@ static vm_fault_t __dev_dax_pmd_fault(struct dev_dax *dev_dax, struct device *dev = &dev_dax->dev; phys_addr_t phys; pgoff_t pgoff; - pfn_t pfn; + unsigned long pfn; unsigned int fault_size = PMD_SIZE; if (check_vma(dev_dax, vmf->vma, __func__)) @@ -169,11 +168,11 @@ static vm_fault_t __dev_dax_pmd_fault(struct dev_dax *dev_dax, return VM_FAULT_SIGBUS; } - pfn = phys_to_pfn_t(phys, 0); + pfn = PHYS_PFN(phys); dax_set_mapping(vmf, pfn, fault_size); - return vmf_insert_folio_pmd(vmf, page_folio(pfn_t_to_page(pfn)), + return vmf_insert_folio_pmd(vmf, page_folio(pfn_to_page(pfn)), vmf->flags & FAULT_FLAG_WRITE); } @@ -185,7 +184,7 @@ static vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax, struct device *dev = &dev_dax->dev; phys_addr_t phys; pgoff_t pgoff; - pfn_t pfn; + unsigned long pfn; unsigned int fault_size = PUD_SIZE; @@ -215,11 +214,11 @@ static vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax, return VM_FAULT_SIGBUS; } - pfn = phys_to_pfn_t(phys, 0); + pfn = PHYS_PFN(phys); dax_set_mapping(vmf, pfn, fault_size); - return vmf_insert_folio_pud(vmf, page_folio(pfn_t_to_page(pfn)), + return vmf_insert_folio_pud(vmf, page_folio(pfn_to_page(pfn)), vmf->flags & FAULT_FLAG_WRITE); } #else diff --git a/drivers/dax/hmem/hmem.c b/drivers/dax/hmem/hmem.c index 5e7c53f18491..c18451a37e4f 100644 --- a/drivers/dax/hmem/hmem.c +++ b/drivers/dax/hmem/hmem.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include "../bus.h" diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index 584c70a34b52..c036e4d0b610 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index c8ebf4e281f2..bee93066a849 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -2,7 +2,6 @@ /* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ #include #include -#include #include "../nvdimm/pfn.h" #include "../nvdimm/nd.h" #include "bus.h" diff --git a/drivers/dax/super.c b/drivers/dax/super.c index e16d1d40d773..54c480e874cb 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -148,7 +147,7 @@ enum dax_device_flags { * pages accessible at the device relative @pgoff. */ long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages, - enum dax_access_mode mode, void **kaddr, pfn_t *pfn) + enum dax_access_mode mode, void **kaddr, unsigned long *pfn) { long avail; diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 3c4862a752b5..c999c4a1e567 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -90,6 +90,17 @@ config ARM_EXYNOS_BUS_DEVFREQ and adjusts the operating frequencies and voltages with OPP support. This does not yet operate with optimal voltages. +config ARM_HISI_UNCORE_DEVFREQ + tristate "HiSilicon uncore DEVFREQ Driver" + depends on ACPI && ACPI_PPTT && PCC + select DEVFREQ_GOV_PERFORMANCE + select DEVFREQ_GOV_USERSPACE + help + This adds a DEVFREQ driver that manages uncore frequency scaling for + HiSilicon Kunpeng SoCs. This enables runtime management of uncore + frequency scaling from kernel and userspace. The uncore domain + contains system interconnects and L3 cache. + config ARM_IMX_BUS_DEVFREQ tristate "i.MX Generic Bus DEVFREQ Driver" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index bf40d04928d0..404179d79a9d 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_HISI_UNCORE_DEVFREQ) += hisi_uncore_freq.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 98657d3b9435..2e8d01d47f69 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -152,11 +152,8 @@ void devfreq_get_freq_range(struct devfreq *devfreq, (unsigned long)HZ_PER_KHZ * qos_max_freq); /* Apply constraints from OPP interface */ - *min_freq = max(*min_freq, devfreq->scaling_min_freq); - *max_freq = min(*max_freq, devfreq->scaling_max_freq); - - if (*min_freq > *max_freq) - *min_freq = *max_freq; + *max_freq = clamp(*max_freq, devfreq->scaling_min_freq, devfreq->scaling_max_freq); + *min_freq = clamp(*min_freq, devfreq->scaling_min_freq, *max_freq); } EXPORT_SYMBOL(devfreq_get_freq_range); @@ -807,7 +804,6 @@ struct devfreq *devfreq_add_device(struct device *dev, { struct devfreq *devfreq; struct devfreq_governor *governor; - unsigned long min_freq, max_freq; int err = 0; if (!dev || !profile || !governor_name) { @@ -835,6 +831,7 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; + devfreq->dev.groups = profile->dev_groups; devfreq->dev.release = devfreq_dev_release; INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; @@ -875,8 +872,6 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_dev; } - devfreq_get_freq_range(devfreq, &min_freq, &max_freq); - devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); devfreq->opp_table = dev_pm_opp_get_opp_table(dev); if (IS_ERR(devfreq->opp_table)) @@ -1382,15 +1377,11 @@ int devfreq_remove_governor(struct devfreq_governor *governor) int ret; struct device *dev = devfreq->dev.parent; + if (!devfreq->governor) + continue; + if (!strncmp(devfreq->governor->name, governor->name, DEVFREQ_NAME_LEN)) { - /* we should have a devfreq governor! */ - if (!devfreq->governor) { - dev_warn(dev, "%s: Governor %s NOT present\n", - __func__, governor->name); - continue; - /* Fall through */ - } ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); if (ret) { @@ -1743,7 +1734,7 @@ static ssize_t trans_stat_show(struct device *dev, for (i = 0; i < max_state; i++) { if (len >= PAGE_SIZE - 1) break; - if (df->freq_table[2] == df->previous_freq) + if (df->freq_table[i] == df->previous_freq) len += sysfs_emit_at(buf, len, "*"); else len += sysfs_emit_at(buf, len, " "); diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index d1aa6806b683..175de0c0b50e 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,10 +40,13 @@ static ssize_t set_freq_store(struct device *dev, struct device_attribute *attr, unsigned long wanted; int err = 0; + err = kstrtoul(buf, 0, &wanted); + if (err) + return err; + mutex_lock(&devfreq->lock); data = devfreq->governor_data; - sscanf(buf, "%lu", &wanted); data->user_frequency = wanted; data->valid = true; err = update_devfreq(devfreq); diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c new file mode 100644 index 000000000000..96d1815059e3 --- /dev/null +++ b/drivers/devfreq/hisi_uncore_freq.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HiSilicon uncore frequency scaling driver + * + * Copyright (c) 2025 HiSilicon Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "governor.h" + +struct hisi_uncore_pcc_data { + u16 status; + u16 resv; + u32 data; +}; + +struct hisi_uncore_pcc_shmem { + struct acpi_pcct_shared_memory head; + struct hisi_uncore_pcc_data pcc_data; +}; + +enum hisi_uncore_pcc_cmd_type { + HUCF_PCC_CMD_GET_CAP = 0, + HUCF_PCC_CMD_GET_FREQ, + HUCF_PCC_CMD_SET_FREQ, + HUCF_PCC_CMD_GET_MODE, + HUCF_PCC_CMD_SET_MODE, + HUCF_PCC_CMD_GET_PLAT_FREQ_NUM, + HUCF_PCC_CMD_GET_PLAT_FREQ_BY_IDX, + HUCF_PCC_CMD_MAX = 256 +}; + +static int hisi_platform_gov_usage; +static DEFINE_MUTEX(hisi_platform_gov_usage_lock); + +enum hisi_uncore_freq_mode { + HUCF_MODE_PLATFORM = 0, + HUCF_MODE_OS, + HUCF_MODE_MAX +}; + +#define HUCF_CAP_PLATFORM_CTRL BIT(0) + +/** + * struct hisi_uncore_freq - hisi uncore frequency scaling device data + * @dev: device of this frequency scaling driver + * @cl: mailbox client object + * @pchan: PCC mailbox channel + * @chan_id: PCC channel ID + * @last_cmd_cmpl_time: timestamp of the last completed PCC command + * @pcc_lock: PCC channel lock + * @devfreq: devfreq data of this hisi_uncore_freq device + * @related_cpus: CPUs whose performance is majorly affected by this + * uncore frequency domain + * @cap: capability flag + */ +struct hisi_uncore_freq { + struct device *dev; + struct mbox_client cl; + struct pcc_mbox_chan *pchan; + int chan_id; + ktime_t last_cmd_cmpl_time; + struct mutex pcc_lock; + struct devfreq *devfreq; + struct cpumask related_cpus; + u32 cap; +}; + +/* PCC channel timeout = PCC nominal latency * NUM */ +#define HUCF_PCC_POLL_TIMEOUT_NUM 1000 +#define HUCF_PCC_POLL_INTERVAL_US 5 + +/* Default polling interval in ms for devfreq governors*/ +#define HUCF_DEFAULT_POLLING_MS 100 + +static void hisi_uncore_free_pcc_chan(struct hisi_uncore_freq *uncore) +{ + guard(mutex)(&uncore->pcc_lock); + pcc_mbox_free_channel(uncore->pchan); + uncore->pchan = NULL; +} + +static void devm_hisi_uncore_free_pcc_chan(void *data) +{ + hisi_uncore_free_pcc_chan(data); +} + +static int hisi_uncore_request_pcc_chan(struct hisi_uncore_freq *uncore) +{ + struct device *dev = uncore->dev; + struct pcc_mbox_chan *pcc_chan; + + uncore->cl = (struct mbox_client) { + .dev = dev, + .tx_block = false, + .knows_txdone = true, + }; + + pcc_chan = pcc_mbox_request_channel(&uncore->cl, uncore->chan_id); + if (IS_ERR(pcc_chan)) + return dev_err_probe(dev, PTR_ERR(pcc_chan), + "Failed to request PCC channel %u\n", uncore->chan_id); + + if (!pcc_chan->shmem_base_addr) { + pcc_mbox_free_channel(pcc_chan); + return dev_err_probe(dev, -EINVAL, + "Invalid PCC shared memory address\n"); + } + + if (pcc_chan->shmem_size < sizeof(struct hisi_uncore_pcc_shmem)) { + pcc_mbox_free_channel(pcc_chan); + return dev_err_probe(dev, -EINVAL, + "Invalid PCC shared memory size (%lluB)\n", + pcc_chan->shmem_size); + } + + uncore->pchan = pcc_chan; + + return devm_add_action_or_reset(uncore->dev, + devm_hisi_uncore_free_pcc_chan, uncore); +} + +static acpi_status hisi_uncore_pcc_reg_scan(struct acpi_resource *res, + void *ctx) +{ + struct acpi_resource_generic_register *reg; + struct hisi_uncore_freq *uncore; + + if (!res || res->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) + return AE_OK; + + reg = &res->data.generic_reg; + if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) + return AE_OK; + + if (!ctx) + return AE_ERROR; + + uncore = ctx; + /* PCC subspace ID stored in Access Size */ + uncore->chan_id = reg->access_size; + + return AE_CTRL_TERMINATE; +} + +static int hisi_uncore_init_pcc_chan(struct hisi_uncore_freq *uncore) +{ + acpi_handle handle = ACPI_HANDLE(uncore->dev); + acpi_status status; + int rc; + + uncore->chan_id = -1; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + hisi_uncore_pcc_reg_scan, uncore); + if (ACPI_FAILURE(status) || uncore->chan_id < 0) + return dev_err_probe(uncore->dev, -ENODEV, + "Failed to get a PCC channel\n"); + + + rc = devm_mutex_init(uncore->dev, &uncore->pcc_lock); + if (rc) + return rc; + + return hisi_uncore_request_pcc_chan(uncore); +} + +static int hisi_uncore_cmd_send(struct hisi_uncore_freq *uncore, + u8 cmd, u32 *data) +{ + struct hisi_uncore_pcc_shmem __iomem *addr; + struct hisi_uncore_pcc_shmem shmem; + struct pcc_mbox_chan *pchan; + unsigned int mrtt; + s64 time_delta; + u16 status; + int rc; + + guard(mutex)(&uncore->pcc_lock); + + pchan = uncore->pchan; + if (!pchan) + return -ENODEV; + + addr = (struct hisi_uncore_pcc_shmem __iomem *)pchan->shmem; + if (!addr) + return -EINVAL; + + /* Handle the Minimum Request Turnaround Time (MRTT) */ + mrtt = pchan->min_turnaround_time; + time_delta = ktime_us_delta(ktime_get(), uncore->last_cmd_cmpl_time); + if (mrtt > time_delta) + udelay(mrtt - time_delta); + + /* Copy data */ + shmem.head = (struct acpi_pcct_shared_memory) { + .signature = PCC_SIGNATURE | uncore->chan_id, + .command = cmd, + }; + shmem.pcc_data.data = *data; + memcpy_toio(addr, &shmem, sizeof(shmem)); + + /* Ring doorbell */ + rc = mbox_send_message(pchan->mchan, &cmd); + if (rc < 0) { + dev_err(uncore->dev, "Failed to send mbox message, %d\n", rc); + return rc; + } + + /* Wait status */ + rc = readw_poll_timeout(&addr->head.status, status, + status & (PCC_STATUS_CMD_COMPLETE | + PCC_STATUS_ERROR), + HUCF_PCC_POLL_INTERVAL_US, + pchan->latency * HUCF_PCC_POLL_TIMEOUT_NUM); + if (rc) { + dev_err(uncore->dev, "PCC channel response timeout, cmd=%u\n", cmd); + } else if (status & PCC_STATUS_ERROR) { + dev_err(uncore->dev, "PCC cmd error, cmd=%u\n", cmd); + rc = -EIO; + } + + uncore->last_cmd_cmpl_time = ktime_get(); + + /* Copy data back */ + memcpy_fromio(data, &addr->pcc_data.data, sizeof(*data)); + + /* Clear mailbox active req */ + mbox_client_txdone(pchan->mchan, rc); + + return rc; +} + +static int hisi_uncore_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev); + struct dev_pm_opp *opp; + u32 data; + + if (WARN_ON(!uncore || !uncore->pchan)) + return -ENODEV; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) { + dev_err(dev, "Failed to get opp for freq %lu hz\n", *freq); + return PTR_ERR(opp); + } + dev_pm_opp_put(opp); + + data = (u32)(dev_pm_opp_get_freq(opp) / HZ_PER_MHZ); + + return hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_FREQ, &data); +} + +static int hisi_uncore_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + /* Not used */ + return 0; +} + +static int hisi_uncore_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev); + u32 data = 0; + int rc; + + if (WARN_ON(!uncore || !uncore->pchan)) + return -ENODEV; + + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_FREQ, &data); + + /* + * Upon a failure, 'data' remains 0 and 'freq' is set to 0 rather than a + * random value. devfreq shouldn't use 'freq' in that case though. + */ + *freq = data * HZ_PER_MHZ; + + return rc; +} + +static void devm_hisi_uncore_remove_opp(void *data) +{ + struct hisi_uncore_freq *uncore = data; + + dev_pm_opp_remove_all_dynamic(uncore->dev); +} + +static int hisi_uncore_init_opp(struct hisi_uncore_freq *uncore) +{ + struct device *dev = uncore->dev; + unsigned long freq_mhz; + u32 num, index; + u32 data = 0; + int rc; + + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_PLAT_FREQ_NUM, + &data); + if (rc) + return dev_err_probe(dev, rc, "Failed to get plat freq num\n"); + + num = data; + + for (index = 0; index < num; index++) { + data = index; + rc = hisi_uncore_cmd_send(uncore, + HUCF_PCC_CMD_GET_PLAT_FREQ_BY_IDX, + &data); + if (rc) { + dev_pm_opp_remove_all_dynamic(dev); + return dev_err_probe(dev, rc, + "Failed to get plat freq at index %u\n", index); + } + freq_mhz = data; + + /* Don't care OPP voltage, take 1V as default */ + rc = dev_pm_opp_add(dev, freq_mhz * HZ_PER_MHZ, 1000000); + if (rc) { + dev_pm_opp_remove_all_dynamic(dev); + return dev_err_probe(dev, rc, + "Add OPP %lu failed\n", freq_mhz); + } + } + + return devm_add_action_or_reset(dev, devm_hisi_uncore_remove_opp, + uncore); +} + +static int hisi_platform_gov_func(struct devfreq *df, unsigned long *freq) +{ + /* + * Platform-controlled mode doesn't care the frequency issued from + * devfreq, so just pick the max freq. + */ + *freq = DEVFREQ_MAX_FREQ; + + return 0; +} + +static int hisi_platform_gov_handler(struct devfreq *df, unsigned int event, + void *val) +{ + struct hisi_uncore_freq *uncore = dev_get_drvdata(df->dev.parent); + int rc = 0; + u32 data; + + if (WARN_ON(!uncore || !uncore->pchan)) + return -ENODEV; + + switch (event) { + case DEVFREQ_GOV_START: + data = HUCF_MODE_PLATFORM; + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); + if (rc) + dev_err(uncore->dev, "Failed to set platform mode (%d)\n", rc); + break; + case DEVFREQ_GOV_STOP: + data = HUCF_MODE_OS; + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); + if (rc) + dev_err(uncore->dev, "Failed to set os mode (%d)\n", rc); + break; + default: + break; + } + + return rc; +} + +/* + * In the platform-controlled mode, the platform decides the uncore frequency + * and ignores the frequency issued from the driver. + * Thus, create a pseudo 'hisi_platform' governor that stops devfreq monitor + * from working so as to save meaningless overhead. + */ +static struct devfreq_governor hisi_platform_governor = { + .name = "hisi_platform", + /* + * Set interrupt_driven to skip the devfreq monitor mechanism, though + * this governor is not interrupt-driven. + */ + .flags = DEVFREQ_GOV_FLAG_IRQ_DRIVEN, + .get_target_freq = hisi_platform_gov_func, + .event_handler = hisi_platform_gov_handler, +}; + +static void hisi_uncore_remove_platform_gov(struct hisi_uncore_freq *uncore) +{ + u32 data = HUCF_MODE_PLATFORM; + int rc; + + if (!(uncore->cap & HUCF_CAP_PLATFORM_CTRL)) + return; + + guard(mutex)(&hisi_platform_gov_usage_lock); + + if (--hisi_platform_gov_usage == 0) { + rc = devfreq_remove_governor(&hisi_platform_governor); + if (rc) + dev_err(uncore->dev, "Failed to remove hisi_platform gov (%d)\n", rc); + } + + /* + * Set to the platform-controlled mode on exit if supported, so as to + * have a certain behaviour when the driver is detached. + */ + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); + if (rc) + dev_err(uncore->dev, "Failed to set platform mode on exit (%d)\n", rc); +} + +static void devm_hisi_uncore_remove_platform_gov(void *data) +{ + hisi_uncore_remove_platform_gov(data); +} + +static int hisi_uncore_add_platform_gov(struct hisi_uncore_freq *uncore) +{ + if (!(uncore->cap & HUCF_CAP_PLATFORM_CTRL)) + return 0; + + guard(mutex)(&hisi_platform_gov_usage_lock); + + if (hisi_platform_gov_usage == 0) { + int rc = devfreq_add_governor(&hisi_platform_governor); + if (rc) + return rc; + } + hisi_platform_gov_usage++; + + return devm_add_action_or_reset(uncore->dev, + devm_hisi_uncore_remove_platform_gov, + uncore); +} + +/* + * Returns: + * 0 if success, uncore->related_cpus is set. + * -EINVAL if property not found, or property found but without elements in it, + * or invalid arguments received in any of the subroutine. + * Other error codes if it goes wrong. + */ +static int hisi_uncore_mark_related_cpus(struct hisi_uncore_freq *uncore, + char *property, int (*get_topo_id)(int cpu), + const struct cpumask *(*get_cpumask)(int cpu)) +{ + unsigned int i, cpu; + size_t len; + int rc; + + rc = device_property_count_u32(uncore->dev, property); + if (rc < 0) + return rc; + if (rc == 0) + return -EINVAL; + + len = rc; + u32 *num __free(kfree) = kcalloc(len, sizeof(*num), GFP_KERNEL); + if (!num) + return -ENOMEM; + + rc = device_property_read_u32_array(uncore->dev, property, num, len); + if (rc) + return rc; + + for (i = 0; i < len; i++) { + for_each_possible_cpu(cpu) { + if (get_topo_id(cpu) != num[i]) + continue; + + cpumask_or(&uncore->related_cpus, + &uncore->related_cpus, get_cpumask(cpu)); + break; + } + } + + return 0; +} + +static int get_package_id(int cpu) +{ + return topology_physical_package_id(cpu); +} + +static const struct cpumask *get_package_cpumask(int cpu) +{ + return topology_core_cpumask(cpu); +} + +static int get_cluster_id(int cpu) +{ + return topology_cluster_id(cpu); +} + +static const struct cpumask *get_cluster_cpumask(int cpu) +{ + return topology_cluster_cpumask(cpu); +} + +static int hisi_uncore_mark_related_cpus_wrap(struct hisi_uncore_freq *uncore) +{ + int rc; + + cpumask_clear(&uncore->related_cpus); + + rc = hisi_uncore_mark_related_cpus(uncore, "related-package", + get_package_id, + get_package_cpumask); + /* Success, or firmware probably broken */ + if (!rc || rc != -EINVAL) + return rc; + + /* Try another property name if rc == -EINVAL */ + return hisi_uncore_mark_related_cpus(uncore, "related-cluster", + get_cluster_id, + get_cluster_cpumask); +} + +static ssize_t related_cpus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev->parent); + + return cpumap_print_to_pagebuf(true, buf, &uncore->related_cpus); +} + +static DEVICE_ATTR_RO(related_cpus); + +static struct attribute *hisi_uncore_freq_attrs[] = { + &dev_attr_related_cpus.attr, + NULL +}; +ATTRIBUTE_GROUPS(hisi_uncore_freq); + +static int hisi_uncore_devfreq_register(struct hisi_uncore_freq *uncore) +{ + struct devfreq_dev_profile *profile; + struct device *dev = uncore->dev; + unsigned long freq; + u32 data; + int rc; + + rc = hisi_uncore_get_cur_freq(dev, &freq); + if (rc) + return dev_err_probe(dev, rc, "Failed to get plat init freq\n"); + + profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); + if (!profile) + return -ENOMEM; + + *profile = (struct devfreq_dev_profile) { + .initial_freq = freq, + .polling_ms = HUCF_DEFAULT_POLLING_MS, + .timer = DEVFREQ_TIMER_DELAYED, + .target = hisi_uncore_target, + .get_dev_status = hisi_uncore_get_dev_status, + .get_cur_freq = hisi_uncore_get_cur_freq, + .dev_groups = hisi_uncore_freq_groups, + }; + + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_MODE, &data); + if (rc) + return dev_err_probe(dev, rc, "Failed to get operate mode\n"); + + if (data == HUCF_MODE_PLATFORM) + uncore->devfreq = devm_devfreq_add_device(dev, profile, + hisi_platform_governor.name, NULL); + else + uncore->devfreq = devm_devfreq_add_device(dev, profile, + DEVFREQ_GOV_PERFORMANCE, NULL); + if (IS_ERR(uncore->devfreq)) + return dev_err_probe(dev, PTR_ERR(uncore->devfreq), + "Failed to add devfreq device\n"); + + return 0; +} + +static int hisi_uncore_freq_probe(struct platform_device *pdev) +{ + struct hisi_uncore_freq *uncore; + struct device *dev = &pdev->dev; + u32 cap; + int rc; + + uncore = devm_kzalloc(dev, sizeof(*uncore), GFP_KERNEL); + if (!uncore) + return -ENOMEM; + + uncore->dev = dev; + platform_set_drvdata(pdev, uncore); + + rc = hisi_uncore_init_pcc_chan(uncore); + if (rc) + return dev_err_probe(dev, rc, "Failed to init PCC channel\n"); + + rc = hisi_uncore_init_opp(uncore); + if (rc) + return dev_err_probe(dev, rc, "Failed to init OPP\n"); + + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_CAP, &cap); + if (rc) + return dev_err_probe(dev, rc, "Failed to get capability\n"); + + uncore->cap = cap; + + rc = hisi_uncore_add_platform_gov(uncore); + if (rc) + return dev_err_probe(dev, rc, "Failed to add hisi_platform governor\n"); + + rc = hisi_uncore_mark_related_cpus_wrap(uncore); + if (rc) + return dev_err_probe(dev, rc, "Failed to mark related cpus\n"); + + rc = hisi_uncore_devfreq_register(uncore); + if (rc) + return dev_err_probe(dev, rc, "Failed to register devfreq\n"); + + return 0; +} + +static const struct acpi_device_id hisi_uncore_freq_acpi_match[] = { + { "HISI04F1", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_uncore_freq_acpi_match); + +static struct platform_driver hisi_uncore_freq_drv = { + .probe = hisi_uncore_freq_probe, + .driver = { + .name = "hisi_uncore_freq", + .acpi_match_table = hisi_uncore_freq_acpi_match, + }, +}; +module_platform_driver(hisi_uncore_freq_drv); + +MODULE_DESCRIPTION("HiSilicon uncore frequency scaling driver"); +MODULE_AUTHOR("Jie Zhan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/sun8i-a33-mbus.c b/drivers/devfreq/sun8i-a33-mbus.c index 7c6ae91ede1f..4bd5657558d6 100644 --- a/drivers/devfreq/sun8i-a33-mbus.c +++ b/drivers/devfreq/sun8i-a33-mbus.c @@ -360,7 +360,7 @@ static int sun8i_a33_mbus_probe(struct platform_device *pdev) if (IS_ERR(priv->reg_mbus)) return PTR_ERR(priv->reg_mbus); - priv->clk_bus = devm_clk_get(dev, "bus"); + priv->clk_bus = devm_clk_get_enabled(dev, "bus"); if (IS_ERR(priv->clk_bus)) return dev_err_probe(dev, PTR_ERR(priv->clk_bus), "failed to get bus clock\n"); @@ -375,24 +375,15 @@ static int sun8i_a33_mbus_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->clk_mbus), "failed to get mbus clock\n"); - ret = clk_prepare_enable(priv->clk_bus); - if (ret) - return dev_err_probe(dev, ret, - "failed to enable bus clock\n"); - /* Lock the DRAM clock rate to keep priv->nominal_bw in sync. */ - ret = clk_rate_exclusive_get(priv->clk_dram); - if (ret) { - err = "failed to lock dram clock rate\n"; - goto err_disable_bus; - } + ret = devm_clk_rate_exclusive_get(dev, priv->clk_dram); + if (ret) + return dev_err_probe(dev, ret, "failed to lock dram clock rate\n"); /* Lock the MBUS clock rate to keep MBUS_TMR_PERIOD in sync. */ - ret = clk_rate_exclusive_get(priv->clk_mbus); - if (ret) { - err = "failed to lock mbus clock rate\n"; - goto err_unlock_dram; - } + ret = devm_clk_rate_exclusive_get(dev, priv->clk_mbus); + if (ret) + return dev_err_probe(dev, ret, "failed to lock mbus clock rate\n"); priv->gov_data.upthreshold = 10; priv->gov_data.downdifferential = 5; @@ -405,10 +396,8 @@ static int sun8i_a33_mbus_probe(struct platform_device *pdev) priv->profile.max_state = max_state; ret = devm_pm_opp_set_clkname(dev, "dram"); - if (ret) { - err = "failed to add OPP table\n"; - goto err_unlock_mbus; - } + if (ret) + return dev_err_probe(dev, ret, "failed to add OPP table\n"); base_freq = clk_get_rate(clk_get_parent(priv->clk_dram)); for (i = 0; i < max_state; ++i) { @@ -448,12 +437,6 @@ static int sun8i_a33_mbus_probe(struct platform_device *pdev) err_remove_opps: dev_pm_opp_remove_all_dynamic(dev); -err_unlock_mbus: - clk_rate_exclusive_put(priv->clk_mbus); -err_unlock_dram: - clk_rate_exclusive_put(priv->clk_dram); -err_disable_bus: - clk_disable_unprepare(priv->clk_bus); return dev_err_probe(dev, ret, err); } @@ -472,9 +455,6 @@ static void sun8i_a33_mbus_remove(struct platform_device *pdev) dev_warn(dev, "failed to restore DRAM frequency: %d\n", ret); dev_pm_opp_remove_all_dynamic(dev); - clk_rate_exclusive_put(priv->clk_mbus); - clk_rate_exclusive_put(priv->clk_dram); - clk_disable_unprepare(priv->clk_bus); } static const struct sun8i_a33_mbus_variant sun50i_a64_mbus = { diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index b1ef4546346d..bea3e9858aca 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -685,11 +685,13 @@ long dma_resv_wait_timeout(struct dma_resv *obj, enum dma_resv_usage usage, dma_resv_iter_begin(&cursor, obj, usage); dma_resv_for_each_fence_unlocked(&cursor, fence) { - ret = dma_fence_wait_timeout(fence, intr, ret); - if (ret <= 0) { - dma_resv_iter_end(&cursor); - return ret; - } + ret = dma_fence_wait_timeout(fence, intr, timeout); + if (ret <= 0) + break; + + /* Even for zero timeout the return value is 1 */ + if (timeout) + timeout = ret; } dma_resv_iter_end(&cursor); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index db87dd2a07f7..05c7c7d9e5a4 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -89,7 +89,6 @@ config APPLE_ADMAC tristate "Apple ADMAC support" depends on ARCH_APPLE || COMPILE_TEST select DMA_ENGINE - default ARCH_APPLE help Enable support for Audio DMA Controller found on Apple Silicon SoCs. @@ -111,7 +110,7 @@ config AT_HDMAC config AT_XDMAC tristate "Atmel XDMA support" - depends on ARCH_AT91 + depends on ARCH_MICROCHIP select DMA_ENGINE help Support the Atmel XDMA controller. @@ -572,6 +571,15 @@ config PLX_DMA These are exposed via extra functions on the switch's upstream port. Each function exposes one DMA channel. +config SOPHGO_CV1800B_DMAMUX + tristate "Sophgo CV1800/SG2000 series SoC DMA multiplexer support" + depends on MFD_SYSCON + depends on ARCH_SOPHGO || COMPILE_TEST + help + Support for the DMA multiplexer on Sophgo CV1800/SG2000 + series SoCs. + Say Y here if your board have this soc. + config STE_DMA40 bool "ST-Ericsson DMA40 support" depends on ARCH_U8500 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index ba9732644752..a54d7688392b 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_PXA_DMA) += pxa_dma.o obj-$(CONFIG_RENESAS_DMA) += sh/ 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_TXX9_DMAC) += txx9dmac.o diff --git a/drivers/dma/cv1800b-dmamux.c b/drivers/dma/cv1800b-dmamux.c new file mode 100644 index 000000000000..e900d6595617 --- /dev/null +++ b/drivers/dma/cv1800b-dmamux.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Inochi Amaoto + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_DMA_CHANNEL_REMAP0 0x154 +#define REG_DMA_CHANNEL_REMAP1 0x158 +#define REG_DMA_INT_MUX 0x298 + +#define DMAMUX_NCELLS 2 +#define MAX_DMA_MAPPING_ID 42 +#define MAX_DMA_CPU_ID 2 +#define MAX_DMA_CH_ID 7 + +#define DMAMUX_INTMUX_REGISTER_LEN 4 +#define DMAMUX_NR_CH_PER_REGISTER 4 +#define DMAMUX_BIT_PER_CH 8 +#define DMAMUX_CH_MASk GENMASK(5, 0) +#define DMAMUX_INT_BIT_PER_CPU 10 +#define DMAMUX_CH_UPDATE_BIT BIT(31) + +#define DMAMUX_CH_REGPOS(chid) \ + ((chid) / DMAMUX_NR_CH_PER_REGISTER) +#define DMAMUX_CH_REGOFF(chid) \ + ((chid) % DMAMUX_NR_CH_PER_REGISTER) +#define DMAMUX_CH_REG(chid) \ + ((DMAMUX_CH_REGPOS(chid) * sizeof(u32)) + \ + REG_DMA_CHANNEL_REMAP0) +#define DMAMUX_CH_SET(chid, val) \ + (((val) << (DMAMUX_CH_REGOFF(chid) * DMAMUX_BIT_PER_CH)) | \ + DMAMUX_CH_UPDATE_BIT) +#define DMAMUX_CH_MASK(chid) \ + DMAMUX_CH_SET(chid, DMAMUX_CH_MASk) + +#define DMAMUX_INT_BIT(chid, cpuid) \ + BIT((cpuid) * DMAMUX_INT_BIT_PER_CPU + (chid)) +#define DMAMUX_INTEN_BIT(cpuid) \ + DMAMUX_INT_BIT(8, cpuid) +#define DMAMUX_INT_CH_BIT(chid, cpuid) \ + (DMAMUX_INT_BIT(chid, cpuid) | DMAMUX_INTEN_BIT(cpuid)) +#define DMAMUX_INT_MASK(chid) \ + (DMAMUX_INT_BIT(chid, 0) | \ + DMAMUX_INT_BIT(chid, 1) | \ + DMAMUX_INT_BIT(chid, 2)) +#define DMAMUX_INT_CH_MASK(chid, cpuid) \ + (DMAMUX_INT_MASK(chid) | DMAMUX_INTEN_BIT(cpuid)) + +struct cv1800_dmamux_data { + struct dma_router dmarouter; + struct regmap *regmap; + spinlock_t lock; + struct llist_head free_maps; + struct llist_head reserve_maps; + DECLARE_BITMAP(mapped_peripherals, MAX_DMA_MAPPING_ID); +}; + +struct cv1800_dmamux_map { + struct llist_node node; + unsigned int channel; + unsigned int peripheral; + unsigned int cpu; +}; + +static void cv1800_dmamux_free(struct device *dev, void *route_data) +{ + struct cv1800_dmamux_data *dmamux = dev_get_drvdata(dev); + struct cv1800_dmamux_map *map = route_data; + + guard(spinlock_irqsave)(&dmamux->lock); + + regmap_update_bits(dmamux->regmap, + DMAMUX_CH_REG(map->channel), + DMAMUX_CH_MASK(map->channel), + DMAMUX_CH_UPDATE_BIT); + + regmap_update_bits(dmamux->regmap, REG_DMA_INT_MUX, + DMAMUX_INT_CH_MASK(map->channel, map->cpu), + DMAMUX_INTEN_BIT(map->cpu)); + + dev_dbg(dev, "free channel %u for req %u (cpu %u)\n", + map->channel, map->peripheral, map->cpu); +} + +static void *cv1800_dmamux_route_allocate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); + struct cv1800_dmamux_data *dmamux = platform_get_drvdata(pdev); + struct cv1800_dmamux_map *map; + struct llist_node *node; + unsigned long flags; + unsigned int chid, devid, cpuid; + int ret; + + if (dma_spec->args_count != DMAMUX_NCELLS) { + dev_err(&pdev->dev, "invalid number of dma mux args\n"); + return ERR_PTR(-EINVAL); + } + + devid = dma_spec->args[0]; + cpuid = dma_spec->args[1]; + dma_spec->args_count = 1; + + if (devid > MAX_DMA_MAPPING_ID) { + dev_err(&pdev->dev, "invalid device id: %u\n", devid); + return ERR_PTR(-EINVAL); + } + + if (cpuid > MAX_DMA_CPU_ID) { + dev_err(&pdev->dev, "invalid cpu id: %u\n", cpuid); + return ERR_PTR(-EINVAL); + } + + dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); + if (!dma_spec->np) { + dev_err(&pdev->dev, "can't get dma master\n"); + return ERR_PTR(-EINVAL); + } + + spin_lock_irqsave(&dmamux->lock, flags); + + if (test_bit(devid, dmamux->mapped_peripherals)) { + llist_for_each_entry(map, dmamux->reserve_maps.first, node) { + if (map->peripheral == devid && map->cpu == cpuid) + goto found; + } + + ret = -EINVAL; + goto failed; + } else { + node = llist_del_first(&dmamux->free_maps); + if (!node) { + ret = -ENODEV; + goto failed; + } + + map = llist_entry(node, struct cv1800_dmamux_map, node); + llist_add(&map->node, &dmamux->reserve_maps); + set_bit(devid, dmamux->mapped_peripherals); + } + +found: + chid = map->channel; + map->peripheral = devid; + map->cpu = cpuid; + + regmap_set_bits(dmamux->regmap, + DMAMUX_CH_REG(chid), + DMAMUX_CH_SET(chid, devid)); + + regmap_update_bits(dmamux->regmap, REG_DMA_INT_MUX, + DMAMUX_INT_CH_MASK(chid, cpuid), + DMAMUX_INT_CH_BIT(chid, cpuid)); + + spin_unlock_irqrestore(&dmamux->lock, flags); + + dma_spec->args[0] = chid; + + dev_dbg(&pdev->dev, "register channel %u for req %u (cpu %u)\n", + chid, devid, cpuid); + + return map; + +failed: + spin_unlock_irqrestore(&dmamux->lock, flags); + of_node_put(dma_spec->np); + dev_err(&pdev->dev, "errno %d\n", ret); + return ERR_PTR(ret); +} + +static int cv1800_dmamux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *mux_node = dev->of_node; + struct cv1800_dmamux_data *data; + struct cv1800_dmamux_map *tmp; + struct device *parent = dev->parent; + struct regmap *regmap = NULL; + unsigned int i; + + if (!parent) + return -ENODEV; + + regmap = device_node_to_regmap(parent->of_node); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->lock); + init_llist_head(&data->free_maps); + init_llist_head(&data->reserve_maps); + + for (i = 0; i <= MAX_DMA_CH_ID; i++) { + tmp = devm_kmalloc(dev, sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + /* It is OK for not allocating all channel */ + dev_warn(dev, "can not allocate channel %u\n", i); + continue; + } + + init_llist_node(&tmp->node); + tmp->channel = i; + llist_add(&tmp->node, &data->free_maps); + } + + /* if no channel is allocated, the probe must fail */ + if (llist_empty(&data->free_maps)) + return -ENOMEM; + + data->regmap = regmap; + data->dmarouter.dev = dev; + data->dmarouter.route_free = cv1800_dmamux_free; + + platform_set_drvdata(pdev, data); + + return of_dma_router_register(mux_node, + cv1800_dmamux_route_allocate, + &data->dmarouter); +} + +static void cv1800_dmamux_remove(struct platform_device *pdev) +{ + of_dma_controller_free(pdev->dev.of_node); +} + +static const struct of_device_id cv1800_dmamux_ids[] = { + { .compatible = "sophgo,cv1800b-dmamux", }, + { } +}; +MODULE_DEVICE_TABLE(of, cv1800_dmamux_ids); + +static struct platform_driver cv1800_dmamux_driver = { + .probe = cv1800_dmamux_probe, + .remove = cv1800_dmamux_remove, + .driver = { + .name = "cv1800-dmamux", + .of_match_table = cv1800_dmamux_ids, + }, +}; +module_platform_driver(cv1800_dmamux_driver); + +MODULE_AUTHOR("Inochi Amaoto "); +MODULE_DESCRIPTION("Sophgo CV1800/SG2000 Series SoC DMAMUX driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 36943b0c6d60..5b06b0dc67ee 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -6,6 +6,7 @@ * Author: Lars-Peter Clausen */ +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include #include diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 758fcd0546d8..ca13cd39330b 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -926,6 +926,36 @@ void dma_release_channel(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(dma_release_channel); +static void dmaenginem_release_channel(void *chan) +{ + dma_release_channel(chan); +} + +/** + * devm_dma_request_chan - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + * + * Returns pointer to appropriate DMA channel on success or an error pointer. + * + * The operation is managed and will be undone on driver detach. + */ + +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; + + if (!IS_ERR(chan)) + ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan); + + if (ret) + return ERR_PTR(ret); + + return chan; +} +EXPORT_SYMBOL_GPL(devm_dma_request_chan); + /** * dmaengine_get - register interest in dma_channels */ diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index c2b88cc99e5d..b43255f914f3 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -23,18 +23,6 @@ #include "../dmaengine.h" #include "../virt-dma.h" -static inline -struct device *dchan2dev(struct dma_chan *dchan) -{ - return &dchan->dev->device; -} - -static inline -struct device *chan2dev(struct dw_edma_chan *chan) -{ - return &chan->vc.chan.dev->device; -} - static inline struct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd) { diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 49f09998e5c0..3371e0a76d3c 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -161,12 +161,16 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { struct dw_edma_pcie_data *pdata = (void *)pid->driver_data; - struct dw_edma_pcie_data vsec_data; + struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL; struct device *dev = &pdev->dev; struct dw_edma_chip *chip; int err, nr_irqs; int i, mask; + vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL); + if (!vsec_data) + return -ENOMEM; + /* Enable PCI device */ err = pcim_enable_device(pdev); if (err) { @@ -174,23 +178,23 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return err; } - memcpy(&vsec_data, pdata, sizeof(struct dw_edma_pcie_data)); + memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data)); /* * 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_vsec_dma_data(pdev, vsec_data); /* Mapping PCI BAR regions */ - mask = BIT(vsec_data.rg.bar); - for (i = 0; i < vsec_data.wr_ch_cnt; i++) { - mask |= BIT(vsec_data.ll_wr[i].bar); - mask |= BIT(vsec_data.dt_wr[i].bar); + mask = BIT(vsec_data->rg.bar); + for (i = 0; i < vsec_data->wr_ch_cnt; i++) { + mask |= BIT(vsec_data->ll_wr[i].bar); + mask |= BIT(vsec_data->dt_wr[i].bar); } - for (i = 0; i < vsec_data.rd_ch_cnt; i++) { - mask |= BIT(vsec_data.ll_rd[i].bar); - mask |= BIT(vsec_data.dt_rd[i].bar); + for (i = 0; i < vsec_data->rd_ch_cnt; i++) { + mask |= BIT(vsec_data->ll_rd[i].bar); + mask |= BIT(vsec_data->dt_rd[i].bar); } err = pcim_iomap_regions(pdev, mask, pci_name(pdev)); if (err) { @@ -213,7 +217,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; /* IRQs allocation */ - nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data.irqs, + nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data->irqs, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (nr_irqs < 1) { pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n", @@ -224,22 +228,22 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, /* Data structure initialization */ chip->dev = dev; - chip->mf = vsec_data.mf; + chip->mf = vsec_data->mf; chip->nr_irqs = nr_irqs; chip->ops = &dw_edma_pcie_plat_ops; - chip->ll_wr_cnt = vsec_data.wr_ch_cnt; - chip->ll_rd_cnt = vsec_data.rd_ch_cnt; + chip->ll_wr_cnt = vsec_data->wr_ch_cnt; + chip->ll_rd_cnt = vsec_data->rd_ch_cnt; - chip->reg_base = pcim_iomap_table(pdev)[vsec_data.rg.bar]; + chip->reg_base = pcim_iomap_table(pdev)[vsec_data->rg.bar]; if (!chip->reg_base) return -ENOMEM; for (i = 0; i < chip->ll_wr_cnt; 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]; - struct dw_edma_block *dt_block = &vsec_data.dt_wr[i]; + struct dw_edma_block *ll_block = &vsec_data->ll_wr[i]; + struct dw_edma_block *dt_block = &vsec_data->dt_wr[i]; ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; if (!ll_region->vaddr.io) @@ -263,8 +267,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, for (i = 0; i < chip->ll_rd_cnt; 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]; - struct dw_edma_block *dt_block = &vsec_data.dt_rd[i]; + struct dw_edma_block *ll_block = &vsec_data->ll_rd[i]; + struct dw_edma_block *dt_block = &vsec_data->dt_rd[i]; ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; if (!ll_region->vaddr.io) @@ -298,31 +302,31 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf); pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n", - vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz, + vsec_data->rg.bar, vsec_data->rg.off, vsec_data->rg.sz, chip->reg_base); for (i = 0; i < chip->ll_wr_cnt; i++) { pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data.ll_wr[i].bar, - vsec_data.ll_wr[i].off, chip->ll_region_wr[i].sz, + i, vsec_data->ll_wr[i].bar, + vsec_data->ll_wr[i].off, chip->ll_region_wr[i].sz, chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr); pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data.dt_wr[i].bar, - vsec_data.dt_wr[i].off, chip->dt_region_wr[i].sz, + i, vsec_data->dt_wr[i].bar, + vsec_data->dt_wr[i].off, chip->dt_region_wr[i].sz, chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr); } for (i = 0; i < chip->ll_rd_cnt; i++) { pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data.ll_rd[i].bar, - vsec_data.ll_rd[i].off, chip->ll_region_rd[i].sz, + i, vsec_data->ll_rd[i].bar, + vsec_data->ll_rd[i].off, chip->ll_region_rd[i].sz, chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr); pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data.dt_rd[i].bar, - vsec_data.dt_rd[i].off, chip->dt_region_rd[i].sz, + i, vsec_data->dt_rd[i].bar, + vsec_data->dt_rd[i].off, chip->dt_region_rd[i].sz, chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr); } diff --git a/drivers/dma/dw/rzn1-dmamux.c b/drivers/dma/dw/rzn1-dmamux.c index 4fb8508419db..deadf135681b 100644 --- a/drivers/dma/dw/rzn1-dmamux.c +++ b/drivers/dma/dw/rzn1-dmamux.c @@ -48,12 +48,16 @@ static void *rzn1_dmamux_route_allocate(struct of_phandle_args *dma_spec, u32 mask; int ret; - if (dma_spec->args_count != RNZ1_DMAMUX_NCELLS) - return ERR_PTR(-EINVAL); + if (dma_spec->args_count != RNZ1_DMAMUX_NCELLS) { + ret = -EINVAL; + goto put_device; + } map = kzalloc(sizeof(*map), GFP_KERNEL); - if (!map) - return ERR_PTR(-ENOMEM); + if (!map) { + ret = -ENOMEM; + goto put_device; + } chan = dma_spec->args[0]; map->req_idx = dma_spec->args[4]; @@ -94,12 +98,15 @@ static void *rzn1_dmamux_route_allocate(struct of_phandle_args *dma_spec, if (ret) goto clear_bitmap; + put_device(&pdev->dev); return map; clear_bitmap: clear_bit(map->req_idx, dmamux->used_chans); free_map: kfree(map); +put_device: + put_device(&pdev->dev); return ERR_PTR(ret); } diff --git a/drivers/dma/fsl-dpaa2-qdma/dpdmai.c b/drivers/dma/fsl-dpaa2-qdma/dpdmai.c index b4323d243d6d..4be81db24a19 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpdmai.c +++ b/drivers/dma/fsl-dpaa2-qdma/dpdmai.c @@ -48,11 +48,6 @@ struct dpdmai_cmd_destroy { __le32 dpdmai_id; } __packed; -static inline u64 mc_enc(int lsoffset, int width, u64 val) -{ - return (val & MAKE_UMASK64(width)) << lsoffset; -} - /** * dpdmai_open() - Open a control session for the specified object * @mc_io: Pointer to MC portal's I/O object diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index 823f5c6bc2e1..21e13f1207cb 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -148,6 +148,9 @@ * @__reserved1: Reserved field. * @cfg8b_w1: Compound descriptor command queue origin produced * by qDMA and dynamic debug field. + * @__reserved2: Reserved field. + * @cmd: Command for QDMA (see FSL_QDMA_CMD_RWTTYPE and + * others). * @data: Pointer to the memory 40-bit address, describes DMA * source information and DMA destination information. */ diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index 80355d03004d..8c4725ad1f64 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -189,27 +189,30 @@ static int idxd_setup_wqs(struct idxd_device *idxd) idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev)); if (!idxd->wq_enable_map) { rc = -ENOMEM; - goto err_bitmap; + goto err_free_wqs; } for (i = 0; i < idxd->max_wqs; i++) { wq = kzalloc_node(sizeof(*wq), GFP_KERNEL, dev_to_node(dev)); if (!wq) { rc = -ENOMEM; - goto err; + goto err_unwind; } idxd_dev_set_type(&wq->idxd_dev, IDXD_DEV_WQ); conf_dev = wq_confdev(wq); wq->id = i; wq->idxd = idxd; - device_initialize(wq_confdev(wq)); + device_initialize(conf_dev); conf_dev->parent = idxd_confdev(idxd); conf_dev->bus = &dsa_bus_type; conf_dev->type = &idxd_wq_device_type; rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id); - if (rc < 0) - goto err; + if (rc < 0) { + put_device(conf_dev); + kfree(wq); + goto err_unwind; + } mutex_init(&wq->wq_lock); init_waitqueue_head(&wq->err_queue); @@ -220,15 +223,20 @@ static int idxd_setup_wqs(struct idxd_device *idxd) wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES; wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev)); if (!wq->wqcfg) { + put_device(conf_dev); + kfree(wq); rc = -ENOMEM; - goto err; + goto err_unwind; } if (idxd->hw.wq_cap.op_config) { wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL); if (!wq->opcap_bmap) { + kfree(wq->wqcfg); + put_device(conf_dev); + kfree(wq); rc = -ENOMEM; - goto err_opcap_bmap; + goto err_unwind; } bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS); } @@ -239,13 +247,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd) return 0; -err_opcap_bmap: - kfree(wq->wqcfg); - -err: - put_device(conf_dev); - kfree(wq); - +err_unwind: while (--i >= 0) { wq = idxd->wqs[i]; if (idxd->hw.wq_cap.op_config) @@ -254,11 +256,10 @@ static int idxd_setup_wqs(struct idxd_device *idxd) conf_dev = wq_confdev(wq); put_device(conf_dev); kfree(wq); - } bitmap_free(idxd->wq_enable_map); -err_bitmap: +err_free_wqs: kfree(idxd->wqs); return rc; @@ -1036,7 +1037,6 @@ static void idxd_reset_prepare(struct pci_dev *pdev) const char *idxd_name; int rc; - dev = &idxd->pdev->dev; idxd_name = dev_name(idxd_confdev(idxd)); struct idxd_saved_states *idxd_saved __free(kfree) = @@ -1292,10 +1292,12 @@ static void idxd_remove(struct pci_dev *pdev) device_unregister(idxd_confdev(idxd)); idxd_shutdown(pdev); idxd_device_remove_debugfs(idxd); - idxd_cleanup(idxd); + perfmon_pmu_remove(idxd); + idxd_cleanup_interrupts(idxd); + if (device_pasid_enabled(idxd)) + idxd_disable_system_pasid(idxd); pci_iounmap(pdev, idxd->reg_base); put_device(idxd_confdev(idxd)); - idxd_free(idxd); pci_disable_device(pdev); } diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index 006ba206ab1b..9c1c546fe443 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -45,7 +45,7 @@ union gen_cap_reg { u64 rsvd3:32; }; u64 bits; -} __packed; +}; #define IDXD_GENCAP_OFFSET 0x10 union wq_cap_reg { @@ -65,7 +65,7 @@ union wq_cap_reg { u64 rsvd4:8; }; u64 bits; -} __packed; +}; #define IDXD_WQCAP_OFFSET 0x20 #define IDXD_WQCFG_MIN 5 @@ -79,7 +79,7 @@ union group_cap_reg { u64 rsvd:45; }; u64 bits; -} __packed; +}; #define IDXD_GRPCAP_OFFSET 0x30 union engine_cap_reg { @@ -88,7 +88,7 @@ union engine_cap_reg { u64 rsvd:56; }; u64 bits; -} __packed; +}; #define IDXD_ENGCAP_OFFSET 0x38 @@ -114,7 +114,7 @@ union offsets_reg { u64 rsvd:48; }; u64 bits[2]; -} __packed; +}; #define IDXD_TABLE_MULT 0x100 @@ -128,7 +128,7 @@ union gencfg_reg { u32 rsvd2:18; }; u32 bits; -} __packed; +}; #define IDXD_GENCTRL_OFFSET 0x88 union genctrl_reg { @@ -139,7 +139,7 @@ union genctrl_reg { u32 rsvd:29; }; u32 bits; -} __packed; +}; #define IDXD_GENSTATS_OFFSET 0x90 union gensts_reg { @@ -149,7 +149,7 @@ union gensts_reg { u32 rsvd:28; }; u32 bits; -} __packed; +}; enum idxd_device_status_state { IDXD_DEVICE_STATE_DISABLED = 0, @@ -183,7 +183,7 @@ union idxd_command_reg { u32 int_req:1; }; u32 bits; -} __packed; +}; enum idxd_cmd { IDXD_CMD_ENABLE_DEVICE = 1, @@ -213,7 +213,7 @@ union cmdsts_reg { u8 active:1; }; u32 bits; -} __packed; +}; #define IDXD_CMDSTS_ACTIVE 0x80000000 #define IDXD_CMDSTS_ERR_MASK 0xff #define IDXD_CMDSTS_RES_SHIFT 8 @@ -284,7 +284,7 @@ union sw_err_reg { u64 rsvd5; }; u64 bits[4]; -} __packed; +}; union iaa_cap_reg { struct { @@ -303,7 +303,7 @@ union iaa_cap_reg { u64 rsvd:52; }; u64 bits; -} __packed; +}; #define IDXD_IAACAP_OFFSET 0x180 @@ -320,7 +320,7 @@ union evlcfg_reg { u64 rsvd2:28; }; u64 bits[2]; -} __packed; +}; #define IDXD_EVL_SIZE_MIN 0x0040 #define IDXD_EVL_SIZE_MAX 0xffff @@ -334,7 +334,7 @@ union msix_perm { u32 pasid:20; }; u32 bits; -} __packed; +}; union group_flags { struct { @@ -352,13 +352,13 @@ union group_flags { u64 rsvd5:26; }; u64 bits; -} __packed; +}; struct grpcfg { u64 wqs[4]; u64 engines; union group_flags flags; -} __packed; +}; union wqcfg { struct { @@ -410,7 +410,7 @@ union wqcfg { u64 op_config[4]; }; u32 bits[16]; -} __packed; +}; #define WQCFG_PASID_IDX 2 #define WQCFG_PRIVL_IDX 2 @@ -474,7 +474,7 @@ union idxd_perfcap { u64 rsvd3:8; }; u64 bits; -} __packed; +}; #define IDXD_EVNTCAP_OFFSET 0x80 union idxd_evntcap { @@ -483,7 +483,7 @@ union idxd_evntcap { u64 rsvd:36; }; u64 bits; -} __packed; +}; struct idxd_event { union { @@ -493,7 +493,7 @@ struct idxd_event { }; u32 val; }; -} __packed; +}; #define IDXD_CNTRCAP_OFFSET 0x800 struct idxd_cntrcap { @@ -506,7 +506,7 @@ struct idxd_cntrcap { u32 val; }; struct idxd_event events[]; -} __packed; +}; #define IDXD_PERFRST_OFFSET 0x10 union idxd_perfrst { @@ -516,7 +516,7 @@ union idxd_perfrst { u32 rsvd:30; }; u32 val; -} __packed; +}; #define IDXD_OVFSTATUS_OFFSET 0x30 #define IDXD_PERFFRZ_OFFSET 0x20 @@ -533,7 +533,7 @@ union idxd_cntrcfg { u64 rsvd3:4; }; u64 val; -} __packed; +}; #define IDXD_FLTCFG_OFFSET 0x300 @@ -543,7 +543,7 @@ union idxd_cntrdata { u64 event_count_value; }; u64 val; -} __packed; +}; union event_cfg { struct { @@ -551,7 +551,7 @@ union event_cfg { u64 event_enc:28; }; u64 val; -} __packed; +}; union filter_cfg { struct { @@ -562,7 +562,7 @@ union filter_cfg { u64 eng:8; }; u64 val; -} __packed; +}; #define IDXD_EVLSTATUS_OFFSET 0xf0 @@ -580,7 +580,7 @@ union evl_status_reg { u32 bits_upper32; }; u64 bits; -} __packed; +}; #define IDXD_MAX_BATCH_IDENT 256 @@ -620,17 +620,17 @@ struct __evl_entry { }; u64 fault_addr; u64 rsvd5; -} __packed; +}; struct dsa_evl_entry { struct __evl_entry e; struct dsa_completion_record cr; -} __packed; +}; struct iax_evl_entry { struct __evl_entry e; u64 rsvd[4]; struct iax_completion_record cr; -} __packed; +}; #endif diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c index 47c8adfdc155..9f0c41ca7770 100644 --- a/drivers/dma/mediatek/mtk-cqdma.c +++ b/drivers/dma/mediatek/mtk-cqdma.c @@ -449,9 +449,9 @@ static enum dma_status mtk_cqdma_tx_status(struct dma_chan *c, return ret; spin_lock_irqsave(&cvc->pc->lock, flags); - spin_lock_irqsave(&cvc->vc.lock, flags); + spin_lock(&cvc->vc.lock); vd = mtk_cqdma_find_active_desc(c, cookie); - spin_unlock_irqrestore(&cvc->vc.lock, flags); + spin_unlock(&cvc->vc.lock); spin_unlock_irqrestore(&cvc->pc->lock, flags); if (vd) { diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index c8dc504510f1..b7fb843c67a6 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -641,7 +641,7 @@ static int mmp_tdma_probe(struct platform_device *pdev) int chan_num = TDMA_CHANNEL_NUM; struct gen_pool *pool = NULL; - type = (enum mmp_tdma_type)device_get_match_data(&pdev->dev); + type = (kernel_ulong_t)device_get_match_data(&pdev->dev); /* always have couple channels */ tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index fa6e4646fdc2..1fdcb0f5c9e7 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1061,8 +1061,16 @@ mv_xor_channel_add(struct mv_xor_device *xordev, */ mv_chan->dummy_src_addr = dma_map_single(dma_dev->dev, mv_chan->dummy_src, MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dev->dev, mv_chan->dummy_src_addr)) + return ERR_PTR(-ENOMEM); + mv_chan->dummy_dst_addr = dma_map_single(dma_dev->dev, mv_chan->dummy_dst, MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE); + if (dma_mapping_error(dma_dev->dev, mv_chan->dummy_dst_addr)) { + ret = -ENOMEM; + goto err_unmap_src; + } + /* allocate coherent memory for hardware descriptors * note: writecombine gives slightly better performance, but @@ -1071,8 +1079,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev, mv_chan->dma_desc_pool_virt = dma_alloc_wc(&pdev->dev, MV_XOR_POOL_SIZE, &mv_chan->dma_desc_pool, GFP_KERNEL); - if (!mv_chan->dma_desc_pool_virt) - return ERR_PTR(-ENOMEM); + if (!mv_chan->dma_desc_pool_virt) { + ret = -ENOMEM; + goto err_unmap_dst; + } /* discover transaction capabilities from the platform data */ dma_dev->cap_mask = cap_mask; @@ -1155,6 +1165,13 @@ mv_xor_channel_add(struct mv_xor_device *xordev, err_free_dma: dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE, mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool); +err_unmap_dst: + dma_unmap_single(dma_dev->dev, mv_chan->dummy_dst_addr, + MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE); +err_unmap_src: + dma_unmap_single(dma_dev->dev, mv_chan->dummy_src_addr, + MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE); + return ERR_PTR(ret); } diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 0d6324c4e2be..765462303de0 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -711,6 +711,9 @@ static int nbpf_desc_page_alloc(struct nbpf_channel *chan) list_add_tail(&ldesc->node, &lhead); ldesc->hwdesc_dma_addr = dma_map_single(dchan->device->dev, hwdesc, sizeof(*hwdesc), DMA_TO_DEVICE); + if (dma_mapping_error(dchan->device->dev, + ldesc->hwdesc_dma_addr)) + goto unmap_error; dev_dbg(dev, "%s(): mapped 0x%p to %pad\n", __func__, hwdesc, &ldesc->hwdesc_dma_addr); @@ -737,6 +740,16 @@ static int nbpf_desc_page_alloc(struct nbpf_channel *chan) spin_unlock_irq(&chan->lock); return ARRAY_SIZE(dpage->desc); + +unmap_error: + while (i--) { + ldesc--; hwdesc--; + + dma_unmap_single(dchan->device->dev, ldesc->hwdesc_dma_addr, + sizeof(hwdesc), DMA_TO_DEVICE); + } + + return -ENOMEM; } static void nbpf_desc_put(struct nbpf_desc *desc) @@ -1351,7 +1364,7 @@ static int nbpf_probe(struct platform_device *pdev) if (irqs == 1) { eirq = irqbuf[0]; - for (i = 0; i <= num_channels; i++) + for (i = 0; i < num_channels; i++) nbpf->chan[i].irq = irqbuf[0]; } else { eirq = platform_get_irq_byname(pdev, "error"); @@ -1361,16 +1374,15 @@ static int nbpf_probe(struct platform_device *pdev) if (irqs == num_channels + 1) { struct nbpf_channel *chan; - for (i = 0, chan = nbpf->chan; i <= num_channels; + for (i = 0, chan = nbpf->chan; i < num_channels; i++, chan++) { /* Skip the error IRQ */ if (irqbuf[i] == eirq) i++; + if (i >= ARRAY_SIZE(irqbuf)) + return -EINVAL; chan->irq = irqbuf[i]; } - - if (chan != nbpf->chan + num_channels) - return -EINVAL; } else { /* 2 IRQs and more than one channel */ if (irqbuf[0] == eirq) @@ -1378,7 +1390,7 @@ static int nbpf_probe(struct platform_device *pdev) else irq = irqbuf[0]; - for (i = 0; i <= num_channels; i++) + for (i = 0; i < num_channels; i++) nbpf->chan[i].irq = irq; } } diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index bbc3276992bb..2cf060174795 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -1283,13 +1283,17 @@ static int bam_dma_probe(struct platform_device *pdev) if (!bdev->bamclk) { ret = of_property_read_u32(pdev->dev.of_node, "num-channels", &bdev->num_channels); - if (ret) + if (ret) { dev_err(bdev->dev, "num-channels unspecified in dt\n"); + return ret; + } ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-ees", &bdev->num_ees); - if (ret) + if (ret) { dev_err(bdev->dev, "num-ees unspecified in dt\n"); + return ret; + } } ret = clk_prepare_enable(bdev->bamclk); diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index b1f0001cc99c..8e87738086b2 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -569,17 +569,6 @@ static inline void gpi_write_reg(struct gpii *gpii, void __iomem *addr, u32 val) writel_relaxed(val, addr); } -/* gpi_write_reg_field - write to specific bit field */ -static inline void gpi_write_reg_field(struct gpii *gpii, void __iomem *addr, - u32 mask, u32 shift, u32 val) -{ - u32 tmp = gpi_read_reg(gpii, addr); - - tmp &= ~mask; - val = tmp | ((val << shift) & mask); - gpi_write_reg(gpii, addr, val); -} - static __always_inline void gpi_update_reg(struct gpii *gpii, u32 offset, u32 mask, u32 val) { diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 6ea5a880b433..8184d475a49a 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -16,7 +16,7 @@ config SH_DMAE_BASE depends on SUPERH || COMPILE_TEST depends on !SUPERH || SH_DMA depends on !SH_DMA_API - default y + default SUPERH || SH_DMA select RENESAS_DMA help Enable support for the Renesas SuperH DMA controllers. diff --git a/drivers/dma/stm32/stm32-dma.c b/drivers/dma/stm32/stm32-dma.c index 917f8e922373..04389936c8a6 100644 --- a/drivers/dma/stm32/stm32-dma.c +++ b/drivers/dma/stm32/stm32-dma.c @@ -613,7 +613,7 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan) reg->dma_scr |= STM32_DMA_SCR_EN; stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr); - dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: started\n", &chan->vchan); } static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan) @@ -676,7 +676,7 @@ static void stm32_dma_handle_chan_paused(struct stm32_dma_chan *chan) chan->status = DMA_PAUSED; - dev_dbg(chan2dev(chan), "vchan %pK: paused\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: paused\n", &chan->vchan); } static void stm32_dma_post_resume_reconfigure(struct stm32_dma_chan *chan) @@ -728,7 +728,7 @@ static void stm32_dma_post_resume_reconfigure(struct stm32_dma_chan *chan) dma_scr |= STM32_DMA_SCR_EN; stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), dma_scr); - dev_dbg(chan2dev(chan), "vchan %pK: reconfigured after pause/resume\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: reconfigured after pause/resume\n", &chan->vchan); } static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan, u32 scr) @@ -744,7 +744,7 @@ static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan, u32 scr) /* cyclic while CIRC/DBM disable => post resume reconfiguration needed */ if (!(scr & (STM32_DMA_SCR_CIRC | STM32_DMA_SCR_DBM))) stm32_dma_post_resume_reconfigure(chan); - else if (scr & STM32_DMA_SCR_DBM) + else if (scr & STM32_DMA_SCR_DBM && chan->desc->num_sgs > 2) stm32_dma_configure_next_sg(chan); } else { chan->busy = false; @@ -820,7 +820,7 @@ static void stm32_dma_issue_pending(struct dma_chan *c) spin_lock_irqsave(&chan->vchan.lock, flags); if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) { - dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan); stm32_dma_start_transfer(chan); } @@ -922,7 +922,7 @@ static int stm32_dma_resume(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); - dev_dbg(chan2dev(chan), "vchan %pK: resumed\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: resumed\n", &chan->vchan); return 0; } diff --git a/drivers/dma/stm32/stm32-dma3.c b/drivers/dma/stm32/stm32-dma3.c index 0c6c4258b195..50e7106c5cb7 100644 --- a/drivers/dma/stm32/stm32-dma3.c +++ b/drivers/dma/stm32/stm32-dma3.c @@ -801,7 +801,7 @@ static void stm32_dma3_chan_start(struct stm32_dma3_chan *chan) chan->dma_status = DMA_IN_PROGRESS; - dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: started\n", &chan->vchan); } static int stm32_dma3_chan_suspend(struct stm32_dma3_chan *chan, bool susp) @@ -1452,7 +1452,7 @@ static int stm32_dma3_pause(struct dma_chan *c) chan->dma_status = DMA_PAUSED; - dev_dbg(chan2dev(chan), "vchan %pK: paused\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: paused\n", &chan->vchan); return 0; } @@ -1465,7 +1465,7 @@ static int stm32_dma3_resume(struct dma_chan *c) chan->dma_status = DMA_IN_PROGRESS; - dev_dbg(chan2dev(chan), "vchan %pK: resumed\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: resumed\n", &chan->vchan); return 0; } @@ -1490,7 +1490,7 @@ static int stm32_dma3_terminate_all(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); vchan_dma_desc_free_list(&chan->vchan, &head); - dev_dbg(chan2dev(chan), "vchan %pK: terminated\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: terminated\n", &chan->vchan); return 0; } @@ -1543,7 +1543,7 @@ static void stm32_dma3_issue_pending(struct dma_chan *c) spin_lock_irqsave(&chan->vchan.lock, flags); if (vchan_issue_pending(&chan->vchan) && !chan->swdesc) { - dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan); stm32_dma3_chan_start(chan); } diff --git a/drivers/dma/stm32/stm32-mdma.c b/drivers/dma/stm32/stm32-mdma.c index e6d525901de7..080c1c725216 100644 --- a/drivers/dma/stm32/stm32-mdma.c +++ b/drivers/dma/stm32/stm32-mdma.c @@ -1187,7 +1187,7 @@ static void stm32_mdma_start_transfer(struct stm32_mdma_chan *chan) chan->busy = true; - dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: started\n", &chan->vchan); } static void stm32_mdma_issue_pending(struct dma_chan *c) @@ -1200,7 +1200,7 @@ static void stm32_mdma_issue_pending(struct dma_chan *c) if (!vchan_issue_pending(&chan->vchan)) goto end; - dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan); if (!chan->desc && !chan->busy) stm32_mdma_start_transfer(chan); @@ -1220,7 +1220,7 @@ static int stm32_mdma_pause(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); if (!ret) - dev_dbg(chan2dev(chan), "vchan %pK: pause\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: pause\n", &chan->vchan); return ret; } @@ -1261,7 +1261,7 @@ static int stm32_mdma_resume(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); - dev_dbg(chan2dev(chan), "vchan %pK: resume\n", &chan->vchan); + dev_dbg(chan2dev(chan), "vchan %p: resume\n", &chan->vchan); return 0; } diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c index 24796aaaddfa..00d2fd38d17f 100644 --- a/drivers/dma/sun4i-dma.c +++ b/drivers/dma/sun4i-dma.c @@ -1249,11 +1249,10 @@ static int sun4i_dma_probe(struct platform_device *pdev) if (priv->irq < 0) return priv->irq; - priv->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "No clock specified\n"); - return PTR_ERR(priv->clk); - } + priv->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "Couldn't start the clock\n"); if (priv->cfg->has_reset) { priv->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); @@ -1328,12 +1327,6 @@ static int sun4i_dma_probe(struct platform_device *pdev) vchan_init(&vchan->vc, &priv->slave); } - ret = clk_prepare_enable(priv->clk); - if (ret) { - dev_err(&pdev->dev, "Couldn't enable the clock\n"); - return ret; - } - /* * Make sure the IRQs are all disabled and accounted for. The bootloader * likes to leave these dirty @@ -1343,33 +1336,23 @@ static int sun4i_dma_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, priv->irq, sun4i_dma_interrupt, 0, dev_name(&pdev->dev), priv); - if (ret) { - dev_err(&pdev->dev, "Cannot request IRQ\n"); - goto err_clk_disable; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Cannot request IRQ\n"); - ret = dma_async_device_register(&priv->slave); - if (ret) { - dev_warn(&pdev->dev, "Failed to register DMA engine device\n"); - goto err_clk_disable; - } + ret = dmaenginem_async_device_register(&priv->slave); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register DMA engine device\n"); ret = of_dma_controller_register(pdev->dev.of_node, sun4i_dma_of_xlate, priv); - if (ret) { - dev_err(&pdev->dev, "of_dma_controller_register failed\n"); - goto err_dma_unregister; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register translation function\n"); dev_dbg(&pdev->dev, "Successfully probed SUN4I_DMA\n"); return 0; - -err_dma_unregister: - dma_async_device_unregister(&priv->slave); -err_clk_disable: - clk_disable_unprepare(priv->clk); - return ret; } static void sun4i_dma_remove(struct platform_device *pdev) @@ -1380,9 +1363,6 @@ static void sun4i_dma_remove(struct platform_device *pdev) disable_irq(priv->irq); of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&priv->slave); - - clk_disable_unprepare(priv->clk); } static struct sun4i_dma_config sun4i_a10_dma_cfg = { diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig index 2adc2cca10e9..dbf168146d35 100644 --- a/drivers/dma/ti/Kconfig +++ b/drivers/dma/ti/Kconfig @@ -17,7 +17,7 @@ config TI_EDMA select DMA_ENGINE select DMA_VIRTUAL_CHANNELS select TI_DMA_CROSSBAR if (ARCH_OMAP || COMPILE_TEST) - default y + default ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE help Enable support for the TI EDMA (Enhanced DMA) controller. This DMA engine is found on TI DaVinci, AM33xx, AM43xx, DRA7xx and Keystone 2 @@ -29,7 +29,7 @@ config DMA_OMAP select DMA_ENGINE select DMA_VIRTUAL_CHANNELS select TI_DMA_CROSSBAR if (SOC_DRA7XX || COMPILE_TEST) - default y + default ARCH_OMAP help Enable support for the TI sDMA (System DMA or DMA4) controller. This DMA engine is found on OMAP and DRA7xx parts. diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 3ed406f08c44..552be71db6c4 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -2064,8 +2064,8 @@ static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, * priority. So Q0 is the highest priority queue and the last queue has * the lowest priority. */ - queue_priority_map = devm_kcalloc(dev, ecc->num_tc + 1, sizeof(s8), - GFP_KERNEL); + queue_priority_map = devm_kcalloc(dev, ecc->num_tc + 1, + sizeof(*queue_priority_map), GFP_KERNEL); if (!queue_priority_map) return -ENOMEM; diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig index 20607ed54243..ade872c915ac 100644 --- a/drivers/dpll/Kconfig +++ b/drivers/dpll/Kconfig @@ -3,5 +3,11 @@ # Generic DPLL drivers configuration # +menu "DPLL device support" + config DPLL bool + +source "drivers/dpll/zl3073x/Kconfig" + +endmenu diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile index 2e5b27850110..9e7a3a3e592e 100644 --- a/drivers/dpll/Makefile +++ b/drivers/dpll/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_DPLL) += dpll.o dpll-y += dpll_core.o dpll-y += dpll_netlink.o dpll-y += dpll_nl.o + +obj-$(CONFIG_ZL3073X) += zl3073x/ diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 20bdc52f63a5..a461095efd8a 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -506,6 +506,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, refcount_set(&pin->refcount, 1); xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC); xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC); + xa_init_flags(&pin->ref_sync_pins, XA_FLAGS_ALLOC); ret = xa_alloc_cyclic(&dpll_pin_xa, &pin->id, pin, xa_limit_32b, &dpll_pin_xa_id, GFP_KERNEL); if (ret < 0) @@ -514,6 +515,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, err_xa_alloc: xa_destroy(&pin->dpll_refs); xa_destroy(&pin->parent_refs); + xa_destroy(&pin->ref_sync_pins); dpll_pin_prop_free(&pin->prop); err_pin_prop: kfree(pin); @@ -595,6 +597,7 @@ void dpll_pin_put(struct dpll_pin *pin) xa_erase(&dpll_pin_xa, pin->id); xa_destroy(&pin->dpll_refs); xa_destroy(&pin->parent_refs); + xa_destroy(&pin->ref_sync_pins); dpll_pin_prop_free(&pin->prop); kfree_rcu(pin, rcu); } @@ -659,11 +662,26 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, } EXPORT_SYMBOL_GPL(dpll_pin_register); +static void dpll_pin_ref_sync_pair_del(u32 ref_sync_pin_id) +{ + struct dpll_pin *pin, *ref_sync_pin; + unsigned long i; + + xa_for_each(&dpll_pin_xa, i, pin) { + ref_sync_pin = xa_load(&pin->ref_sync_pins, ref_sync_pin_id); + if (ref_sync_pin) { + xa_erase(&pin->ref_sync_pins, ref_sync_pin_id); + __dpll_pin_change_ntf(pin); + } + } +} + static void __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); + dpll_pin_ref_sync_pair_del(pin->id); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); if (xa_empty(&pin->dpll_refs)) @@ -783,6 +801,33 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); +/** + * dpll_pin_ref_sync_pair_add - create a reference sync signal pin pair + * @pin: pin which produces the base frequency + * @ref_sync_pin: pin which produces the sync signal + * + * Once pins are paired, the user-space configuration of reference sync pair + * is possible. + * Context: Acquires a lock (dpll_lock) + * Return: + * * 0 on success + * * negative - error value + */ +int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin, + struct dpll_pin *ref_sync_pin) +{ + int ret; + + mutex_lock(&dpll_lock); + ret = xa_insert(&pin->ref_sync_pins, ref_sync_pin->id, + ref_sync_pin, GFP_KERNEL); + __dpll_pin_change_ntf(pin); + mutex_unlock(&dpll_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dpll_pin_ref_sync_pair_add); + static struct dpll_device_registration * dpll_device_registration_first(struct dpll_device *dpll) { diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 2b6d8ef1cdf3..8ce969bbeb64 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -44,8 +44,8 @@ struct dpll_device { * @module: module of creator * @dpll_refs: hold referencees to dplls pin was registered with * @parent_refs: hold references to parent pins pin was registered with + * @ref_sync_pins: hold references to pins for Reference SYNC feature * @prop: pin properties copied from the registerer - * @rclk_dev_name: holds name of device when pin can recover clock from it * @refcount: refcount * @rcu: rcu_head for kfree_rcu() **/ @@ -56,6 +56,7 @@ struct dpll_pin { struct module *module; struct xarray dpll_refs; struct xarray parent_refs; + struct xarray ref_sync_pins; struct dpll_pin_properties prop; refcount_t refcount; struct rcu_head rcu; diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index c130f87147fa..036f21cac0a9 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -48,6 +48,24 @@ dpll_msg_add_dev_parent_handle(struct sk_buff *msg, u32 id) return 0; } +static bool dpll_pin_available(struct dpll_pin *pin) +{ + struct dpll_pin_ref *par_ref; + unsigned long i; + + if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)) + return false; + xa_for_each(&pin->parent_refs, i, par_ref) + if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id, + DPLL_REGISTERED)) + return true; + xa_for_each(&pin->dpll_refs, i, par_ref) + if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id, + DPLL_REGISTERED)) + return true; + return false; +} + /** * dpll_msg_add_pin_handle - attach pin handle attribute to a given message * @msg: pointer to sk_buff message to attach a pin handle @@ -126,6 +144,26 @@ dpll_msg_add_mode_supported(struct sk_buff *msg, struct dpll_device *dpll, return 0; } +static int +dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll, + struct netlink_ext_ack *extack) +{ + const struct dpll_device_ops *ops = dpll_device_ops(dpll); + enum dpll_feature_state state; + int ret; + + if (ops->phase_offset_monitor_set && ops->phase_offset_monitor_get) { + ret = ops->phase_offset_monitor_get(dpll, dpll_priv(dpll), + &state, extack); + if (ret) + return ret; + if (nla_put_u32(msg, DPLL_A_PHASE_OFFSET_MONITOR, state)) + return -EMSGSIZE; + } + + return 0; +} + static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll, struct netlink_ext_ack *extack) @@ -408,6 +446,47 @@ dpll_msg_add_pin_esync(struct sk_buff *msg, struct dpll_pin *pin, return -EMSGSIZE; } +static int +dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin, + struct dpll_pin_ref *ref, + struct netlink_ext_ack *extack) +{ + const struct dpll_pin_ops *ops = dpll_pin_ops(ref); + struct dpll_device *dpll = ref->dpll; + void *pin_priv, *ref_sync_pin_priv; + struct dpll_pin *ref_sync_pin; + enum dpll_pin_state state; + struct nlattr *nest; + unsigned long index; + int ret; + + pin_priv = dpll_pin_on_dpll_priv(dpll, pin); + xa_for_each(&pin->ref_sync_pins, index, ref_sync_pin) { + if (!dpll_pin_available(ref_sync_pin)) + continue; + ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin); + if (WARN_ON(!ops->ref_sync_get)) + return -EOPNOTSUPP; + ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin, + ref_sync_pin_priv, &state, extack); + if (ret) + return ret; + nest = nla_nest_start(msg, DPLL_A_PIN_REFERENCE_SYNC); + if (!nest) + return -EMSGSIZE; + if (nla_put_s32(msg, DPLL_A_PIN_ID, ref_sync_pin->id)) + goto nest_cancel; + if (nla_put_s32(msg, DPLL_A_PIN_STATE, state)) + goto nest_cancel; + nla_nest_end(msg, nest); + } + return 0; + +nest_cancel: + nla_nest_cancel(msg, nest); + return -EMSGSIZE; +} + static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq) { int fs; @@ -550,6 +629,10 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, if (ret) return ret; ret = dpll_msg_add_pin_esync(msg, pin, ref, extack); + if (ret) + return ret; + if (!xa_empty(&pin->ref_sync_pins)) + ret = dpll_msg_add_pin_ref_sync(msg, pin, ref, extack); if (ret) return ret; if (xa_empty(&pin->parent_refs)) @@ -591,6 +674,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg, return ret; if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type)) return -EMSGSIZE; + ret = dpll_msg_add_phase_offset_monitor(msg, dpll, extack); + if (ret) + return ret; return 0; } @@ -642,24 +728,6 @@ __dpll_device_change_ntf(struct dpll_device *dpll) return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll); } -static bool dpll_pin_available(struct dpll_pin *pin) -{ - struct dpll_pin_ref *par_ref; - unsigned long i; - - if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)) - return false; - xa_for_each(&pin->parent_refs, i, par_ref) - if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id, - DPLL_REGISTERED)) - return true; - xa_for_each(&pin->dpll_refs, i, par_ref) - if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id, - DPLL_REGISTERED)) - return true; - return false; -} - /** * dpll_device_change_ntf - notify that the dpll device has been changed * @dpll: registered dpll pointer @@ -722,7 +790,7 @@ int dpll_pin_delete_ntf(struct dpll_pin *pin) return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin); } -static int __dpll_pin_change_ntf(struct dpll_pin *pin) +int __dpll_pin_change_ntf(struct dpll_pin *pin) { return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin); } @@ -746,6 +814,31 @@ int dpll_pin_change_ntf(struct dpll_pin *pin) } EXPORT_SYMBOL_GPL(dpll_pin_change_ntf); +static int +dpll_phase_offset_monitor_set(struct dpll_device *dpll, struct nlattr *a, + struct netlink_ext_ack *extack) +{ + const struct dpll_device_ops *ops = dpll_device_ops(dpll); + enum dpll_feature_state state = nla_get_u32(a), old_state; + int ret; + + if (!(ops->phase_offset_monitor_set && ops->phase_offset_monitor_get)) { + NL_SET_ERR_MSG_ATTR(extack, a, "dpll device not capable of phase offset monitor"); + return -EOPNOTSUPP; + } + ret = ops->phase_offset_monitor_get(dpll, dpll_priv(dpll), &old_state, + extack); + if (ret) { + NL_SET_ERR_MSG(extack, "unable to get current state of phase offset monitor"); + return ret; + } + if (state == old_state) + return 0; + + return ops->phase_offset_monitor_set(dpll, dpll_priv(dpll), state, + extack); +} + static int dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a, struct netlink_ext_ack *extack) @@ -887,6 +980,108 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a, return ret; } +static int +dpll_pin_ref_sync_state_set(struct dpll_pin *pin, + unsigned long ref_sync_pin_idx, + const enum dpll_pin_state state, + struct netlink_ext_ack *extack) + +{ + struct dpll_pin_ref *ref, *failed; + const struct dpll_pin_ops *ops; + enum dpll_pin_state old_state; + struct dpll_pin *ref_sync_pin; + struct dpll_device *dpll; + unsigned long i; + int ret; + + ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx, + ULONG_MAX, XA_PRESENT); + if (!ref_sync_pin) { + NL_SET_ERR_MSG(extack, "reference sync pin not found"); + return -EINVAL; + } + if (!dpll_pin_available(ref_sync_pin)) { + NL_SET_ERR_MSG(extack, "reference sync pin not available"); + return -EINVAL; + } + ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ASSERT_NOT_NULL(ref); + ops = dpll_pin_ops(ref); + if (!ops->ref_sync_set || !ops->ref_sync_get) { + NL_SET_ERR_MSG(extack, "reference sync not supported by this pin"); + return -EOPNOTSUPP; + } + dpll = ref->dpll; + ret = ops->ref_sync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), + ref_sync_pin, + dpll_pin_on_dpll_priv(dpll, ref_sync_pin), + &old_state, extack); + if (ret) { + NL_SET_ERR_MSG(extack, "unable to get old reference sync state"); + return ret; + } + if (state == old_state) + return 0; + xa_for_each(&pin->dpll_refs, i, ref) { + ops = dpll_pin_ops(ref); + dpll = ref->dpll; + ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), + ref_sync_pin, + dpll_pin_on_dpll_priv(dpll, + ref_sync_pin), + state, extack); + if (ret) { + failed = ref; + NL_SET_ERR_MSG_FMT(extack, "reference sync set failed for dpll_id:%u", + dpll->id); + goto rollback; + } + } + __dpll_pin_change_ntf(pin); + + return 0; + +rollback: + xa_for_each(&pin->dpll_refs, i, ref) { + if (ref == failed) + break; + ops = dpll_pin_ops(ref); + dpll = ref->dpll; + if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), + ref_sync_pin, + dpll_pin_on_dpll_priv(dpll, ref_sync_pin), + old_state, extack)) + NL_SET_ERR_MSG(extack, "set reference sync rollback failed"); + } + return ret; +} + +static int +dpll_pin_ref_sync_set(struct dpll_pin *pin, struct nlattr *nest, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[DPLL_A_PIN_MAX + 1]; + enum dpll_pin_state state; + u32 sync_pin_id; + + nla_parse_nested(tb, DPLL_A_PIN_MAX, nest, + dpll_reference_sync_nl_policy, extack); + if (!tb[DPLL_A_PIN_ID]) { + NL_SET_ERR_MSG(extack, "sync pin id expected"); + return -EINVAL; + } + sync_pin_id = nla_get_u32(tb[DPLL_A_PIN_ID]); + + if (!tb[DPLL_A_PIN_STATE]) { + NL_SET_ERR_MSG(extack, "sync pin state expected"); + return -EINVAL; + } + state = nla_get_u32(tb[DPLL_A_PIN_STATE]); + + return dpll_pin_ref_sync_state_set(pin, sync_pin_id, state, extack); +} + static int dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx, enum dpll_pin_state state, @@ -1193,6 +1388,11 @@ dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info) if (ret) return ret; break; + case DPLL_A_PIN_REFERENCE_SYNC: + ret = dpll_pin_ref_sync_set(pin, a, info->extack); + if (ret) + return ret; + break; } } @@ -1533,10 +1733,27 @@ int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info) return genlmsg_reply(msg, info); } +static int +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info) +{ + int ret; + + if (info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]) { + struct nlattr *a = info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]; + + ret = dpll_phase_offset_monitor_set(dpll, a, info->extack); + if (ret) + return ret; + } + + return 0; +} + int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info) { - /* placeholder for set command */ - return 0; + struct dpll_device *dpll = info->user_ptr[0]; + + return dpll_set_from_nlattr(dpll, info); } int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h index a9cfd55f57fc..dd28b56d27c5 100644 --- a/drivers/dpll/dpll_netlink.h +++ b/drivers/dpll/dpll_netlink.h @@ -11,3 +11,5 @@ 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/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c index fe9b6893d261..9f2efaf25268 100644 --- a/drivers/dpll/dpll_nl.c +++ b/drivers/dpll/dpll_nl.c @@ -24,6 +24,11 @@ const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = { [DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3), }; +const struct nla_policy dpll_reference_sync_nl_policy[DPLL_A_PIN_STATE + 1] = { + [DPLL_A_PIN_ID] = { .type = NLA_U32, }, + [DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3), +}; + /* DPLL_CMD_DEVICE_ID_GET - do */ static const struct nla_policy dpll_device_id_get_nl_policy[DPLL_A_TYPE + 1] = { [DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, }, @@ -37,8 +42,9 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = { }; /* DPLL_CMD_DEVICE_SET - do */ -static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_ID + 1] = { +static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_MONITOR + 1] = { [DPLL_A_ID] = { .type = NLA_U32, }, + [DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1), }; /* DPLL_CMD_PIN_ID_GET - do */ @@ -62,7 +68,7 @@ static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_PIN_ID + 1] = }; /* DPLL_CMD_PIN_SET - do */ -static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_ESYNC_FREQUENCY + 1] = { +static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_REFERENCE_SYNC + 1] = { [DPLL_A_PIN_ID] = { .type = NLA_U32, }, [DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, }, [DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2), @@ -72,6 +78,7 @@ static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_ESYNC_FREQUENCY [DPLL_A_PIN_PARENT_PIN] = NLA_POLICY_NESTED(dpll_pin_parent_pin_nl_policy), [DPLL_A_PIN_PHASE_ADJUST] = { .type = NLA_S32, }, [DPLL_A_PIN_ESYNC_FREQUENCY] = { .type = NLA_U64, }, + [DPLL_A_PIN_REFERENCE_SYNC] = NLA_POLICY_NESTED(dpll_reference_sync_nl_policy), }; /* Ops table for dpll */ @@ -105,7 +112,7 @@ static const struct genl_split_ops dpll_nl_ops[] = { .doit = dpll_nl_device_set_doit, .post_doit = dpll_post_doit, .policy = dpll_device_set_nl_policy, - .maxattr = DPLL_A_ID, + .maxattr = DPLL_A_PHASE_OFFSET_MONITOR, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -139,7 +146,7 @@ static const struct genl_split_ops dpll_nl_ops[] = { .doit = dpll_nl_pin_set_doit, .post_doit = dpll_pin_post_doit, .policy = dpll_pin_set_nl_policy, - .maxattr = DPLL_A_PIN_ESYNC_FREQUENCY, + .maxattr = DPLL_A_PIN_REFERENCE_SYNC, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; diff --git a/drivers/dpll/dpll_nl.h b/drivers/dpll/dpll_nl.h index f491262bee4f..3da10cfe9a6e 100644 --- a/drivers/dpll/dpll_nl.h +++ b/drivers/dpll/dpll_nl.h @@ -14,6 +14,7 @@ /* Common nested types */ extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1]; extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1]; +extern const struct nla_policy dpll_reference_sync_nl_policy[DPLL_A_PIN_STATE + 1]; int dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); diff --git a/drivers/dpll/zl3073x/Kconfig b/drivers/dpll/zl3073x/Kconfig new file mode 100644 index 000000000000..5bbca1400581 --- /dev/null +++ b/drivers/dpll/zl3073x/Kconfig @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config ZL3073X + tristate "Microchip Azurite DPLL/PTP/SyncE devices" if COMPILE_TEST + depends on NET + select DPLL + select NET_DEVLINK + select REGMAP + help + This driver supports Microchip Azurite family DPLL/PTP/SyncE + devices that support up to 5 independent DPLL channels, + 10 input pins and up to 20 output pins. + + To compile this driver as a module, choose M here. The module + will be called zl3073x. + +config ZL3073X_I2C + tristate "I2C bus implementation for Microchip Azurite devices" + depends on I2C && NET + select REGMAP_I2C + select ZL3073X + help + This is I2C bus implementation for Microchip Azurite DPLL/PTP/SyncE + devices. + + To compile this driver as a module, choose M here: the module will + be called zl3073x_i2c. + +config ZL3073X_SPI + tristate "SPI bus implementation for Microchip Azurite devices" + depends on NET && SPI + select REGMAP_SPI + select ZL3073X + help + This is SPI bus implementation for Microchip Azurite DPLL/PTP/SyncE + devices. + + To compile this driver as a module, choose M here: the module will + be called zl3073x_spi. diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile new file mode 100644 index 000000000000..c3e2f02f319d --- /dev/null +++ b/drivers/dpll/zl3073x/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ZL3073X) += zl3073x.o +zl3073x-objs := core.o devlink.o dpll.o prop.o + +obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o +zl3073x_i2c-objs := i2c.o + +obj-$(CONFIG_ZL3073X_SPI) += zl3073x_spi.o +zl3073x_spi-objs := spi.o diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c new file mode 100644 index 000000000000..7ebcfc5ec1f0 --- /dev/null +++ b/drivers/dpll/zl3073x/core.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "devlink.h" +#include "dpll.h" +#include "regs.h" + +/* Chip IDs for zl30731 */ +static const u16 zl30731_ids[] = { + 0x0E93, + 0x1E93, + 0x2E93, +}; + +const struct zl3073x_chip_info zl30731_chip_info = { + .ids = zl30731_ids, + .num_ids = ARRAY_SIZE(zl30731_ids), + .num_channels = 1, +}; +EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, "ZL3073X"); + +/* Chip IDs for zl30732 */ +static const u16 zl30732_ids[] = { + 0x0E30, + 0x0E94, + 0x1E94, + 0x1F60, + 0x2E94, + 0x3FC4, +}; + +const struct zl3073x_chip_info zl30732_chip_info = { + .ids = zl30732_ids, + .num_ids = ARRAY_SIZE(zl30732_ids), + .num_channels = 2, +}; +EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, "ZL3073X"); + +/* Chip IDs for zl30733 */ +static const u16 zl30733_ids[] = { + 0x0E95, + 0x1E95, + 0x2E95, +}; + +const struct zl3073x_chip_info zl30733_chip_info = { + .ids = zl30733_ids, + .num_ids = ARRAY_SIZE(zl30733_ids), + .num_channels = 3, +}; +EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, "ZL3073X"); + +/* Chip IDs for zl30734 */ +static const u16 zl30734_ids[] = { + 0x0E96, + 0x1E96, + 0x2E96, +}; + +const struct zl3073x_chip_info zl30734_chip_info = { + .ids = zl30734_ids, + .num_ids = ARRAY_SIZE(zl30734_ids), + .num_channels = 4, +}; +EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, "ZL3073X"); + +/* Chip IDs for zl30735 */ +static const u16 zl30735_ids[] = { + 0x0E97, + 0x1E97, + 0x2E97, +}; + +const struct zl3073x_chip_info zl30735_chip_info = { + .ids = zl30735_ids, + .num_ids = ARRAY_SIZE(zl30735_ids), + .num_channels = 5, +}; +EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, "ZL3073X"); + +#define ZL_RANGE_OFFSET 0x80 +#define ZL_PAGE_SIZE 0x80 +#define ZL_NUM_PAGES 15 +#define ZL_PAGE_SEL 0x7F +#define ZL_PAGE_SEL_MASK GENMASK(3, 0) +#define ZL_NUM_REGS (ZL_NUM_PAGES * ZL_PAGE_SIZE) + +/* Regmap range configuration */ +static const struct regmap_range_cfg zl3073x_regmap_range = { + .range_min = ZL_RANGE_OFFSET, + .range_max = ZL_RANGE_OFFSET + ZL_NUM_REGS - 1, + .selector_reg = ZL_PAGE_SEL, + .selector_mask = ZL_PAGE_SEL_MASK, + .selector_shift = 0, + .window_start = 0, + .window_len = ZL_PAGE_SIZE, +}; + +static bool +zl3073x_is_volatile_reg(struct device *dev __maybe_unused, unsigned int reg) +{ + /* Only page selector is non-volatile */ + return reg != ZL_PAGE_SEL; +} + +const struct regmap_config zl3073x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ZL_RANGE_OFFSET + ZL_NUM_REGS - 1, + .ranges = &zl3073x_regmap_range, + .num_ranges = 1, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = zl3073x_is_volatile_reg, +}; +EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X"); + +/** + * zl3073x_ref_freq_factorize - factorize given frequency + * @freq: input frequency + * @base: base frequency + * @mult: multiplier + * + * Checks if the given frequency can be factorized using one of the + * supported base frequencies. If so the base frequency and multiplier + * are stored into appropriate parameters if they are not NULL. + * + * Return: 0 on success, -EINVAL if the frequency cannot be factorized + */ +int +zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult) +{ + static const u16 base_freqs[] = { + 1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125, + 128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000, + 1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250, + 6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250, + 32000, 40000, 50000, 62500, + }; + u32 div; + int i; + + for (i = 0; i < ARRAY_SIZE(base_freqs); i++) { + div = freq / base_freqs[i]; + + if (div <= U16_MAX && (freq % base_freqs[i]) == 0) { + if (base) + *base = base_freqs[i]; + if (mult) + *mult = div; + + return 0; + } + } + + return -EINVAL; +} + +static bool +zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size) +{ + /* Check that multiop lock is held when accessing registers + * from page 10 and above. + */ + if (ZL_REG_PAGE(reg) >= 10) + lockdep_assert_held(&zldev->multiop_lock); + + /* Check the index is in valid range for indexed register */ + if (ZL_REG_OFFSET(reg) > ZL_REG_MAX_OFFSET(reg)) { + dev_err(zldev->dev, "Index out of range for reg 0x%04lx\n", + ZL_REG_ADDR(reg)); + return false; + } + /* Check the requested size corresponds to register size */ + if (ZL_REG_SIZE(reg) != size) { + dev_err(zldev->dev, "Invalid size %zu for reg 0x%04lx\n", + size, ZL_REG_ADDR(reg)); + return false; + } + + return true; +} + +static int +zl3073x_read_reg(struct zl3073x_dev *zldev, unsigned int reg, void *val, + size_t size) +{ + int rc; + + if (!zl3073x_check_reg(zldev, reg, size)) + return -EINVAL; + + /* Map the register address to virtual range */ + reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + rc = regmap_bulk_read(zldev->regmap, reg, val, size); + if (rc) { + dev_err(zldev->dev, "Failed to read reg 0x%04x: %pe\n", reg, + ERR_PTR(rc)); + return rc; + } + + return 0; +} + +static int +zl3073x_write_reg(struct zl3073x_dev *zldev, unsigned int reg, const void *val, + size_t size) +{ + int rc; + + if (!zl3073x_check_reg(zldev, reg, size)) + return -EINVAL; + + /* Map the register address to virtual range */ + reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + rc = regmap_bulk_write(zldev->regmap, reg, val, size); + if (rc) { + dev_err(zldev->dev, "Failed to write reg 0x%04x: %pe\n", reg, + ERR_PTR(rc)); + return rc; + } + + return 0; +} + +/** + * zl3073x_read_u8 - read value from 8bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 8bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val) +{ + return zl3073x_read_reg(zldev, reg, val, sizeof(*val)); +} + +/** + * zl3073x_write_u8 - write value to 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 8bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val) +{ + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u16 - read value from 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 16bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val) +{ + int rc; + + rc = zl3073x_read_reg(zldev, reg, val, sizeof(*val)); + if (!rc) + be16_to_cpus(val); + + return rc; +} + +/** + * zl3073x_write_u16 - write value to 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 16bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val) +{ + cpu_to_be16s(&val); + + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u32 - read value from 32bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 32bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val) +{ + int rc; + + rc = zl3073x_read_reg(zldev, reg, val, sizeof(*val)); + if (!rc) + be32_to_cpus(val); + + return rc; +} + +/** + * zl3073x_write_u32 - write value to 32bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 32bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val) +{ + cpu_to_be32s(&val); + + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u48 - read value from 48bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 48bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val) +{ + u8 buf[6]; + int rc; + + rc = zl3073x_read_reg(zldev, reg, buf, sizeof(buf)); + if (!rc) + *val = get_unaligned_be48(buf); + + return rc; +} + +/** + * zl3073x_write_u48 - write value to 48bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 48bit register. + * The value must be from the interval -S48_MIN to U48_MAX. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val) +{ + u8 buf[6]; + + /* Check the value belongs to + * Any value >= S48_MIN has bits 47..63 set. + */ + if (val > GENMASK_ULL(47, 0) && val < GENMASK_ULL(63, 47)) { + dev_err(zldev->dev, "Value 0x%0llx out of range\n", val); + return -EINVAL; + } + + put_unaligned_be48(val, buf); + + return zl3073x_write_reg(zldev, reg, buf, sizeof(buf)); +} + +/** + * zl3073x_poll_zero_u8 - wait for register to be cleared by device + * @zldev: zl3073x device pointer + * @reg: register to poll (has to be 8bit register) + * @mask: bit mask for polling + * + * Waits for bits specified by @mask in register @reg value to be cleared + * by the device. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask) +{ + /* Register polling sleep & timeout */ +#define ZL_POLL_SLEEP_US 10 +#define ZL_POLL_TIMEOUT_US 2000000 + unsigned int val; + + /* Check the register is 8bit */ + if (ZL_REG_SIZE(reg) != 1) { + dev_err(zldev->dev, "Invalid reg 0x%04lx size for polling\n", + ZL_REG_ADDR(reg)); + return -EINVAL; + } + + /* Map the register address to virtual range */ + reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask), + ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); +} + +int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val, + unsigned int mask_reg, u16 mask_val) +{ + int rc; + + /* Set mask for the operation */ + rc = zl3073x_write_u16(zldev, mask_reg, mask_val); + if (rc) + return rc; + + /* Trigger the operation */ + rc = zl3073x_write_u8(zldev, op_reg, op_val); + if (rc) + return rc; + + /* Wait for the operation to actually finish */ + return zl3073x_poll_zero_u8(zldev, op_reg, op_val); +} + +/** + * zl3073x_ref_state_fetch - get input reference state + * @zldev: pointer to zl3073x_dev structure + * @index: input reference index to fetch state for + * + * Function fetches information for the given input reference that are + * invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_ref *input = &zldev->ref[index]; + u8 ref_config; + int rc; + + /* If the input is differential then the configuration for N-pin + * reference is ignored and P-pin config is used for both. + */ + if (zl3073x_is_n_pin(index) && + zl3073x_ref_is_diff(zldev, index - 1)) { + input->enabled = zl3073x_ref_is_enabled(zldev, index - 1); + input->diff = true; + + return 0; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* Read ref_config register */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config); + if (rc) + return rc; + + input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config); + input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config); + + dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index, + str_enabled_disabled(input->enabled), + input->diff ? "differential" : "single-ended"); + + return rc; +} + +/** + * zl3073x_out_state_fetch - get output state + * @zldev: pointer to zl3073x_dev structure + * @index: output index to fetch state for + * + * Function fetches information for the given output (not output pin) + * that are invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_out *out = &zldev->out[index]; + u8 output_ctrl, output_mode; + int rc; + + /* Read output configuration */ + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl); + if (rc) + return rc; + + /* Store info about output enablement and synthesizer the output + * is connected to. + */ + out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl); + out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl); + + dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index, + str_enabled_disabled(out->enabled), out->synth); + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* Read output_mode */ + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Extract and store output signal format */ + out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT, + output_mode); + + dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index, + out->signal_format); + + return rc; +} + +/** + * zl3073x_synth_state_fetch - get synth state + * @zldev: pointer to zl3073x_dev structure + * @index: synth index to fetch state for + * + * Function fetches information for the given synthesizer that are + * invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_synth *synth = &zldev->synth[index]; + u16 base, m, n; + u8 synth_ctrl; + u32 mult; + int rc; + + /* Read synth control register */ + rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl); + if (rc) + return rc; + + /* Store info about synth enablement and DPLL channel the synth is + * driven by. + */ + synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl); + synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl); + + dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index, + str_enabled_disabled(synth->enabled), synth->dpll); + + guard(mutex)(&zldev->multiop_lock); + + /* Read synth configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD, + ZL_REG_SYNTH_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* The output frequency is determined by the following formula: + * base * multiplier * numerator / denominator + * + * Read registers with these values + */ + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base); + if (rc) + return rc; + + rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult); + if (rc) + return rc; + + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m); + if (rc) + return rc; + + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n); + if (rc) + return rc; + + /* Check denominator for zero to avoid div by 0 */ + if (!n) { + dev_err(zldev->dev, + "Zero divisor for SYNTH%u retrieved from device\n", + index); + return -EINVAL; + } + + /* Compute and store synth frequency */ + zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n); + + dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index, + zldev->synth[index].freq); + + return rc; +} + +static int +zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) +{ + int rc; + u8 i; + + for (i = 0; i < ZL3073X_NUM_REFS; i++) { + rc = zl3073x_ref_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch input state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + for (i = 0; i < ZL3073X_NUM_SYNTHS; i++) { + rc = zl3073x_synth_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch synth state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + for (i = 0; i < ZL3073X_NUM_OUTS; i++) { + rc = zl3073x_out_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch output state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + return rc; +} + +/** + * zl3073x_ref_phase_offsets_update - update reference phase offsets + * @zldev: pointer to zl3073x_dev structure + * @channel: DPLL channel number or -1 + * + * The function asks device to update phase offsets latch registers with + * the latest measured values. There are 2 sets of latch registers: + * + * 1) Up to 5 DPLL-to-connected-ref registers that contain phase offset + * values between particular DPLL channel and its *connected* input + * reference. + * + * 2) 10 selected-DPLL-to-all-ref registers that contain phase offset values + * between selected DPLL channel and all input references. + * + * If the caller is interested in 2) then it has to pass DPLL channel number + * in @channel parameter. If it is interested only in 1) then it should pass + * @channel parameter with value of -1. + * + * Return: 0 on success, <0 on error + */ +int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel) +{ + int rc; + + /* Per datasheet we have to wait for 'dpll_ref_phase_err_rqst_rd' + * to be zero to ensure that the measured data are coherent. + */ + rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); + if (rc) + return rc; + + /* Select DPLL channel if it is specified */ + if (channel != -1) { + rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_IDX, channel); + if (rc) + return rc; + } + + /* Request to update phase offsets measurement values */ + rc = zl3073x_write_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); + if (rc) + return rc; + + /* Wait for finish */ + return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); +} + +/** + * zl3073x_ref_ffo_update - update reference fractional frequency offsets + * @zldev: pointer to zl3073x_dev structure + * + * The function asks device to update fractional frequency offsets latch + * registers the latest measured values, reads and stores them into + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_ref_ffo_update(struct zl3073x_dev *zldev) +{ + int i, rc; + + /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero + * to ensure that the measured data are coherent. + */ + rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL); + if (rc) + return rc; + + /* Select all references for measurement */ + rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_3_0, + GENMASK(7, 0)); /* REF0P..REF3N */ + if (rc) + return rc; + rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_4, + GENMASK(1, 0)); /* REF4P..REF4N */ + if (rc) + return rc; + + /* Request frequency offset measurement */ + rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF); + if (rc) + return rc; + + /* Wait for finish */ + rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL); + if (rc) + return rc; + + /* Read DPLL-to-REFx frequency offset measurements */ + for (i = 0; i < ZL3073X_NUM_REFS; i++) { + s32 value; + + /* Read value stored in units of 2^-32 signed */ + rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value); + if (rc) + return rc; + + /* Convert to ppm -> ffo = (10^6 * value) / 2^32 */ + zldev->ref[i].ffo = mul_s64_u64_shr(value, 1000000, 32); + } + + return 0; +} + +static void +zl3073x_dev_periodic_work(struct kthread_work *work) +{ + struct zl3073x_dev *zldev = container_of(work, struct zl3073x_dev, + work.work); + struct zl3073x_dpll *zldpll; + int rc; + + /* Update DPLL-to-connected-ref phase offsets registers */ + rc = zl3073x_ref_phase_offsets_update(zldev, -1); + if (rc) + dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", + ERR_PTR(rc)); + + /* Update references' fractional frequency offsets */ + rc = zl3073x_ref_ffo_update(zldev); + if (rc) + dev_warn(zldev->dev, + "Failed to update fractional frequency offsets: %pe\n", + ERR_PTR(rc)); + + list_for_each_entry(zldpll, &zldev->dplls, list) + zl3073x_dpll_changes_check(zldpll); + + /* Run twice a second */ + kthread_queue_delayed_work(zldev->kworker, &zldev->work, + msecs_to_jiffies(500)); +} + +static void zl3073x_dev_dpll_fini(void *ptr) +{ + struct zl3073x_dpll *zldpll, *next; + struct zl3073x_dev *zldev = ptr; + + /* Stop monitoring thread */ + if (zldev->kworker) { + kthread_cancel_delayed_work_sync(&zldev->work); + kthread_destroy_worker(zldev->kworker); + zldev->kworker = NULL; + } + + /* Release DPLLs */ + list_for_each_entry_safe(zldpll, next, &zldev->dplls, list) { + zl3073x_dpll_unregister(zldpll); + list_del(&zldpll->list); + zl3073x_dpll_free(zldpll); + } +} + +static int +zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls) +{ + struct kthread_worker *kworker; + struct zl3073x_dpll *zldpll; + unsigned int i; + int rc; + + INIT_LIST_HEAD(&zldev->dplls); + + /* Initialize all DPLLs */ + for (i = 0; i < num_dplls; i++) { + zldpll = zl3073x_dpll_alloc(zldev, i); + if (IS_ERR(zldpll)) { + dev_err_probe(zldev->dev, PTR_ERR(zldpll), + "Failed to alloc DPLL%u\n", i); + rc = PTR_ERR(zldpll); + goto error; + } + + rc = zl3073x_dpll_register(zldpll); + if (rc) { + dev_err_probe(zldev->dev, rc, + "Failed to register DPLL%u\n", i); + zl3073x_dpll_free(zldpll); + goto error; + } + + list_add_tail(&zldpll->list, &zldev->dplls); + } + + /* Perform initial firmware fine phase correction */ + rc = zl3073x_dpll_init_fine_phase_adjust(zldev); + if (rc) { + dev_err_probe(zldev->dev, rc, + "Failed to init fine phase correction\n"); + goto error; + } + + /* Initialize monitoring thread */ + kthread_init_delayed_work(&zldev->work, zl3073x_dev_periodic_work); + kworker = kthread_run_worker(0, "zl3073x-%s", dev_name(zldev->dev)); + if (IS_ERR(kworker)) { + rc = PTR_ERR(kworker); + goto error; + } + + zldev->kworker = kworker; + kthread_queue_delayed_work(zldev->kworker, &zldev->work, 0); + + /* Add devres action to release DPLL related resources */ + rc = devm_add_action_or_reset(zldev->dev, zl3073x_dev_dpll_fini, zldev); + if (rc) + goto error; + + return 0; + +error: + zl3073x_dev_dpll_fini(zldev); + + return rc; +} + +/** + * zl3073x_dev_phase_meas_setup - setup phase offset measurement + * @zldev: pointer to zl3073x_dev structure + * @num_channels: number of DPLL channels + * + * Enable phase offset measurement block, set measurement averaging factor + * and enable DPLL-to-its-ref phase measurement for all DPLLs. + * + * Returns: 0 on success, <0 on error + */ +static int +zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev, int num_channels) +{ + u8 dpll_meas_ctrl, mask; + int i, rc; + + /* Read DPLL phase measurement control register */ + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl); + if (rc) + return rc; + + /* Setup phase measurement averaging factor */ + dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR; + dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3); + + /* Enable DPLL measurement block */ + dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN; + + /* Update phase measurement control register */ + rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl); + if (rc) + return rc; + + /* Enable DPLL-to-connected-ref measurement for each channel */ + for (i = 0, mask = 0; i < num_channels; i++) + mask |= BIT(i); + + return zl3073x_write_u8(zldev, ZL_REG_DPLL_PHASE_ERR_READ_MASK, mask); +} + +/** + * zl3073x_dev_probe - initialize zl3073x device + * @zldev: pointer to zl3073x device + * @chip_info: chip info based on compatible + * + * Common initialization of zl3073x device structure. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_dev_probe(struct zl3073x_dev *zldev, + const struct zl3073x_chip_info *chip_info) +{ + u16 id, revision, fw_ver; + unsigned int i; + u32 cfg_ver; + int rc; + + /* Read chip ID */ + rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id); + if (rc) + return rc; + + /* Check it matches */ + for (i = 0; i < chip_info->num_ids; i++) { + if (id == chip_info->ids[i]) + break; + } + + if (i == chip_info->num_ids) { + return dev_err_probe(zldev->dev, -ENODEV, + "Unknown or non-match chip ID: 0x%0x\n", + id); + } + + /* Read revision, firmware version and custom config version */ + rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision); + if (rc) + return rc; + rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver); + if (rc) + return rc; + rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver); + if (rc) + return rc; + + dev_dbg(zldev->dev, "ChipID(%X), ChipRev(%X), FwVer(%u)\n", id, + revision, fw_ver); + dev_dbg(zldev->dev, "Custom config version: %lu.%lu.%lu.%lu\n", + FIELD_GET(GENMASK(31, 24), cfg_ver), + FIELD_GET(GENMASK(23, 16), cfg_ver), + FIELD_GET(GENMASK(15, 8), cfg_ver), + FIELD_GET(GENMASK(7, 0), cfg_ver)); + + /* Generate random clock ID as the device has not such property that + * could be used for this purpose. A user can later change this value + * using devlink. + */ + zldev->clock_id = get_random_u64(); + + /* Initialize mutex for operations where multiple reads, writes + * and/or polls are required to be done atomically. + */ + rc = devm_mutex_init(zldev->dev, &zldev->multiop_lock); + if (rc) + return dev_err_probe(zldev->dev, rc, + "Failed to initialize mutex\n"); + + /* Fetch device state */ + rc = zl3073x_dev_state_fetch(zldev); + if (rc) + return rc; + + /* Setup phase offset measurement block */ + rc = zl3073x_dev_phase_meas_setup(zldev, chip_info->num_channels); + if (rc) + return dev_err_probe(zldev->dev, rc, + "Failed to setup phase measurement\n"); + + /* Register DPLL channels */ + rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels); + if (rc) + return rc; + + /* Register the devlink instance and parameters */ + rc = zl3073x_devlink_register(zldev); + if (rc) + return dev_err_probe(zldev->dev, rc, + "Failed to register devlink instance\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, "ZL3073X"); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h new file mode 100644 index 000000000000..71af2c800110 --- /dev/null +++ b/drivers/dpll/zl3073x/core.h @@ -0,0 +1,383 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_CORE_H +#define _ZL3073X_CORE_H + +#include +#include +#include +#include + +#include "regs.h" + +struct device; +struct regmap; +struct zl3073x_dpll; + +/* + * Hardware limits for ZL3073x chip family + */ +#define ZL3073X_MAX_CHANNELS 5 +#define ZL3073X_NUM_REFS 10 +#define ZL3073X_NUM_OUTS 10 +#define ZL3073X_NUM_SYNTHS 5 +#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS +#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2) +#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \ + ZL3073X_NUM_OUTPUT_PINS) + +/** + * struct zl3073x_ref - input reference invariant info + * @enabled: input reference is enabled or disabled + * @diff: true if input reference is differential + * @ffo: current fractional frequency offset + */ +struct zl3073x_ref { + bool enabled; + bool diff; + s64 ffo; +}; + +/** + * struct zl3073x_out - output invariant info + * @enabled: out is enabled or disabled + * @synth: synthesizer the out is connected to + * @signal_format: out signal format + */ +struct zl3073x_out { + bool enabled; + u8 synth; + u8 signal_format; +}; + +/** + * struct zl3073x_synth - synthesizer invariant info + * @freq: synthesizer frequency + * @dpll: ID of DPLL the synthesizer is driven by + * @enabled: synth is enabled or disabled + */ +struct zl3073x_synth { + u32 freq; + u8 dpll; + bool enabled; +}; + +/** + * struct zl3073x_dev - zl3073x device + * @dev: pointer to device + * @regmap: regmap to access device registers + * @multiop_lock: to serialize multiple register operations + * @clock_id: clock id of the device + * @ref: array of input references' invariants + * @out: array of outs' invariants + * @synth: array of synths' invariants + * @dplls: list of DPLLs + * @kworker: thread for periodic work + * @work: periodic work + */ +struct zl3073x_dev { + struct device *dev; + struct regmap *regmap; + struct mutex multiop_lock; + u64 clock_id; + + /* Invariants */ + struct zl3073x_ref ref[ZL3073X_NUM_REFS]; + struct zl3073x_out out[ZL3073X_NUM_OUTS]; + struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS]; + + /* DPLL channels */ + struct list_head dplls; + + /* Monitor */ + struct kthread_worker *kworker; + struct kthread_delayed_work work; +}; + +struct zl3073x_chip_info { + const u16 *ids; + size_t num_ids; + int num_channels; +}; + +extern const struct zl3073x_chip_info zl30731_chip_info; +extern const struct zl3073x_chip_info zl30732_chip_info; +extern const struct zl3073x_chip_info zl30733_chip_info; +extern const struct zl3073x_chip_info zl30734_chip_info; +extern const struct zl3073x_chip_info zl30735_chip_info; +extern const struct regmap_config zl3073x_regmap_config; + +struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev); +int zl3073x_dev_probe(struct zl3073x_dev *zldev, + const struct zl3073x_chip_info *chip_info); + +/********************** + * Registers operations + **********************/ + +int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val, + unsigned int mask_reg, u16 mask_val); +int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask); +int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val); +int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val); +int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val); +int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val); +int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val); +int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val); +int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val); +int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val); + +/***************** + * Misc operations + *****************/ + +int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult); +int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel); + +static inline bool +zl3073x_is_n_pin(u8 id) +{ + /* P-pins ids are even while N-pins are odd */ + return id & 1; +} + +static inline bool +zl3073x_is_p_pin(u8 id) +{ + return !zl3073x_is_n_pin(id); +} + +/** + * zl3073x_input_pin_ref_get - get reference for given input pin + * @id: input pin id + * + * Return: reference id for the given input pin + */ +static inline u8 +zl3073x_input_pin_ref_get(u8 id) +{ + return id; +} + +/** + * zl3073x_output_pin_out_get - get output for the given output pin + * @id: output pin id + * + * Return: output id for the given output pin + */ +static inline u8 +zl3073x_output_pin_out_get(u8 id) +{ + /* Output pin pair shares the single output */ + return id / 2; +} + +/** + * zl3073x_ref_ffo_get - get current fractional frequency offset + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: the latest measured fractional frequency offset + */ +static inline s64 +zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].ffo; +} + +/** + * zl3073x_ref_is_diff - check if the given input reference is differential + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: true if reference is differential, false if reference is single-ended + */ +static inline bool +zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].diff; +} + +/** + * zl3073x_ref_is_enabled - check if the given input reference is enabled + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: true if input refernce is enabled, false otherwise + */ +static inline bool +zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].enabled; +} + +/** + * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: ID of DPLL the given synthetizer is driven by + */ +static inline u8 +zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].dpll; +} + +/** + * zl3073x_synth_freq_get - get synth current freq + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: frequency of given synthetizer + */ +static inline u32 +zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].freq; +} + +/** + * zl3073x_synth_is_enabled - check if the given synth is enabled + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: true if synth is enabled, false otherwise + */ +static inline bool +zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].enabled; +} + +/** + * zl3073x_out_synth_get - get synth connected to given output + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: index of synth connected to given output. + */ +static inline u8 +zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->out[index].synth; +} + +/** + * zl3073x_out_is_enabled - check if the given output is enabled + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: true if the output is enabled, false otherwise + */ +static inline bool +zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + u8 synth; + + /* Output is enabled only if associated synth is enabled */ + synth = zl3073x_out_synth_get(zldev, index); + if (zl3073x_synth_is_enabled(zldev, synth)) + return zldev->out[index].enabled; + + return false; +} + +/** + * zl3073x_out_signal_format_get - get output signal format + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: signal format of given output + */ +static inline u8 +zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->out[index].signal_format; +} + +/** + * zl3073x_out_dpll_get - get DPLL ID the output is driven by + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: ID of DPLL the given output is driven by + */ +static inline +u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index) +{ + u8 synth; + + /* Get synthesizer connected to given output */ + synth = zl3073x_out_synth_get(zldev, index); + + /* Return DPLL that drives the synth */ + return zl3073x_synth_dpll_get(zldev, synth); +} + +/** + * zl3073x_out_is_diff - check if the given output is differential + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: true if output is differential, false if output is single-ended + */ +static inline bool +zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index) +{ + switch (zl3073x_out_signal_format_get(zldev, index)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM: + return true; + default: + break; + } + + return false; +} + +/** + * zl3073x_output_pin_is_enabled - check if the given output pin is enabled + * @zldev: pointer to zl3073x device + * @id: output pin id + * + * Checks if the output of the given output pin is enabled and also that + * its signal format also enables the given pin. + * + * Return: true if output pin is enabled, false if output pin is disabled + */ +static inline bool +zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id) +{ + u8 output = zl3073x_output_pin_out_get(id); + + /* Check if the whole output is enabled */ + if (!zl3073x_out_is_enabled(zldev, output)) + return false; + + /* Check signal format */ + switch (zl3073x_out_signal_format_get(zldev, output)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED: + /* Both output pins are disabled by signal format */ + return false; + + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P: + /* Output is one single ended P-pin output */ + if (zl3073x_is_n_pin(id)) + return false; + break; + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N: + /* Output is one single ended N-pin output */ + if (zl3073x_is_p_pin(id)) + return false; + break; + default: + /* For other format both pins are enabled */ + break; + } + + return true; +} + +#endif /* _ZL3073X_CORE_H */ diff --git a/drivers/dpll/zl3073x/devlink.c b/drivers/dpll/zl3073x/devlink.c new file mode 100644 index 000000000000..7e7fe726ee37 --- /dev/null +++ b/drivers/dpll/zl3073x/devlink.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "core.h" +#include "devlink.h" +#include "dpll.h" +#include "regs.h" + +/** + * zl3073x_devlink_info_get - Devlink device info callback + * @devlink: devlink structure pointer + * @req: devlink request pointer to store information + * @extack: netlink extack pointer to report errors + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dev *zldev = devlink_priv(devlink); + u16 id, revision, fw_ver; + char buf[16]; + u32 cfg_ver; + int rc; + + rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%X", id); + rc = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + buf); + if (rc) + return rc; + + rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%X", revision); + rc = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, + buf); + if (rc) + return rc; + + rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%u", fw_ver); + rc = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (rc) + return rc; + + rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver); + if (rc) + return rc; + + /* No custom config version */ + if (cfg_ver == U32_MAX) + return 0; + + snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu", + FIELD_GET(GENMASK(31, 24), cfg_ver), + FIELD_GET(GENMASK(23, 16), cfg_ver), + FIELD_GET(GENMASK(15, 8), cfg_ver), + FIELD_GET(GENMASK(7, 0), cfg_ver)); + + return devlink_info_version_running_put(req, "custom_cfg", buf); +} + +static int +zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change, + enum devlink_reload_action action, + enum devlink_reload_limit limit, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dev *zldev = devlink_priv(devlink); + struct zl3073x_dpll *zldpll; + + if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) + return -EOPNOTSUPP; + + /* Unregister all DPLLs */ + list_for_each_entry(zldpll, &zldev->dplls, list) + zl3073x_dpll_unregister(zldpll); + + return 0; +} + +static int +zl3073x_devlink_reload_up(struct devlink *devlink, + enum devlink_reload_action action, + enum devlink_reload_limit limit, + u32 *actions_performed, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dev *zldev = devlink_priv(devlink); + union devlink_param_value val; + struct zl3073x_dpll *zldpll; + int rc; + + if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) + return -EOPNOTSUPP; + + rc = devl_param_driverinit_value_get(devlink, + DEVLINK_PARAM_GENERIC_ID_CLOCK_ID, + &val); + if (rc) + return rc; + + if (zldev->clock_id != val.vu64) { + dev_dbg(zldev->dev, + "'clock_id' changed to %016llx\n", val.vu64); + zldev->clock_id = val.vu64; + } + + /* Re-register all DPLLs */ + list_for_each_entry(zldpll, &zldev->dplls, list) { + rc = zl3073x_dpll_register(zldpll); + if (rc) + dev_warn(zldev->dev, + "Failed to re-register DPLL%u\n", zldpll->id); + } + + *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); + + return 0; +} + +static const struct devlink_ops zl3073x_devlink_ops = { + .info_get = zl3073x_devlink_info_get, + .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT), + .reload_down = zl3073x_devlink_reload_down, + .reload_up = zl3073x_devlink_reload_up, +}; + +static void +zl3073x_devlink_free(void *ptr) +{ + devlink_free(ptr); +} + +/** + * zl3073x_devm_alloc - allocates zl3073x device structure + * @dev: pointer to device structure + * + * Allocates zl3073x device structure as device resource. + * + * Return: pointer to zl3073x device on success, error pointer on error + */ +struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev) +{ + struct zl3073x_dev *zldev; + struct devlink *devlink; + int rc; + + devlink = devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev); + if (!devlink) + return ERR_PTR(-ENOMEM); + + /* Add devres action to free devlink device */ + rc = devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink); + if (rc) + return ERR_PTR(rc); + + zldev = devlink_priv(devlink); + zldev->dev = dev; + dev_set_drvdata(zldev->dev, zldev); + + return zldev; +} +EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X"); + +static int +zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + if (!val.vu64) { + NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero"); + return -EINVAL; + } + + return 0; +} + +static const struct devlink_param zl3073x_devlink_params[] = { + DEVLINK_PARAM_GENERIC(CLOCK_ID, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), + NULL, NULL, + zl3073x_devlink_param_clock_id_validate), +}; + +static void +zl3073x_devlink_unregister(void *ptr) +{ + struct devlink *devlink = priv_to_devlink(ptr); + + devl_lock(devlink); + + /* Unregister devlink params */ + devl_params_unregister(devlink, zl3073x_devlink_params, + ARRAY_SIZE(zl3073x_devlink_params)); + + /* Unregister devlink instance */ + devl_unregister(devlink); + + devl_unlock(devlink); +} + +/** + * zl3073x_devlink_register - register devlink instance and params + * @zldev: zl3073x device to register the devlink for + * + * Register the devlink instance and parameters associated with the device. + * + * Return: 0 on success, <0 on error + */ +int zl3073x_devlink_register(struct zl3073x_dev *zldev) +{ + struct devlink *devlink = priv_to_devlink(zldev); + union devlink_param_value value; + int rc; + + devl_lock(devlink); + + /* Register devlink params */ + rc = devl_params_register(devlink, zl3073x_devlink_params, + ARRAY_SIZE(zl3073x_devlink_params)); + if (rc) { + devl_unlock(devlink); + + return rc; + } + + value.vu64 = zldev->clock_id; + devl_param_driverinit_value_set(devlink, + DEVLINK_PARAM_GENERIC_ID_CLOCK_ID, + value); + + /* Register devlink instance */ + devl_register(devlink); + + devl_unlock(devlink); + + /* Add devres action to unregister devlink device */ + return devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister, + zldev); +} diff --git a/drivers/dpll/zl3073x/devlink.h b/drivers/dpll/zl3073x/devlink.h new file mode 100644 index 000000000000..037720db204f --- /dev/null +++ b/drivers/dpll/zl3073x/devlink.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_DEVLINK_H +#define _ZL3073X_DEVLINK_H + +struct zl3073x_dev; + +struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev); + +int zl3073x_devlink_register(struct zl3073x_dev *zldev); + +#endif /* _ZL3073X_DEVLINK_H */ diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c new file mode 100644 index 000000000000..3e42e9e7fd27 --- /dev/null +++ b/drivers/dpll/zl3073x/dpll.c @@ -0,0 +1,2318 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "dpll.h" +#include "prop.h" +#include "regs.h" + +#define ZL3073X_DPLL_REF_NONE ZL3073X_NUM_REFS +#define ZL3073X_DPLL_REF_IS_VALID(_ref) ((_ref) != ZL3073X_DPLL_REF_NONE) + +/** + * struct zl3073x_dpll_pin - DPLL pin + * @list: this DPLL pin list entry + * @dpll: DPLL the pin is registered to + * @dpll_pin: pointer to registered dpll_pin + * @label: package label + * @dir: pin direction + * @id: pin id + * @prio: pin priority <0, 14> + * @selectable: pin is selectable in automatic mode + * @esync_control: embedded sync is controllable + * @pin_state: last saved pin state + * @phase_offset: last saved pin phase offset + * @freq_offset: last saved fractional frequency offset + */ +struct zl3073x_dpll_pin { + struct list_head list; + struct zl3073x_dpll *dpll; + struct dpll_pin *dpll_pin; + char label[8]; + enum dpll_pin_direction dir; + u8 id; + u8 prio; + bool selectable; + bool esync_control; + enum dpll_pin_state pin_state; + s64 phase_offset; + s64 freq_offset; +}; + +/* + * Supported esync ranges for input and for output per output pair type + */ +static const struct dpll_pin_frequency esync_freq_ranges[] = { + DPLL_PIN_FREQUENCY_RANGE(0, 1), +}; + +/** + * zl3073x_dpll_is_input_pin - check if the pin is input one + * @pin: pin to check + * + * Return: true if pin is input, false if pin is output. + */ +static bool +zl3073x_dpll_is_input_pin(struct zl3073x_dpll_pin *pin) +{ + return pin->dir == DPLL_PIN_DIRECTION_INPUT; +} + +/** + * zl3073x_dpll_is_p_pin - check if the pin is P-pin + * @pin: pin to check + * + * Return: true if the pin is P-pin, false if it is N-pin + */ +static bool +zl3073x_dpll_is_p_pin(struct zl3073x_dpll_pin *pin) +{ + return zl3073x_is_p_pin(pin->id); +} + +static int +zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + enum dpll_pin_direction *direction, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin = pin_priv; + + *direction = pin->dir; + + return 0; +} + +/** + * zl3073x_dpll_input_ref_frequency_get - get input reference frequency + * @zldpll: pointer to zl3073x_dpll + * @ref_id: reference id + * @frequency: pointer to variable to store frequency + * + * Reads frequency of given input reference. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dpll *zldpll, u8 ref_id, + u32 *frequency) +{ + struct zl3073x_dev *zldev = zldpll->dev; + u16 base, mult, num, denom; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref_id)); + if (rc) + return rc; + + /* Read registers to compute resulting frequency */ + rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &base); + if (rc) + return rc; + rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &mult); + if (rc) + return rc; + rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &num); + if (rc) + return rc; + rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &denom); + if (rc) + return rc; + + /* Sanity check that HW has not returned zero denominator */ + if (!denom) { + dev_err(zldev->dev, + "Zero divisor for ref %u frequency got from device\n", + ref_id); + return -EINVAL; + } + + /* Compute the frequency */ + *frequency = mul_u64_u32_div(base * mult, num, denom); + + return rc; +} + +static int +zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + struct dpll_pin_esync *esync, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u8 ref, ref_sync_ctrl, sync_mode; + u32 esync_div, ref_freq; + int rc; + + /* Get reference frequency */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_dpll_input_ref_frequency_get(zldpll, pin->id, &ref_freq); + if (rc) + return rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration into mailbox */ + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Get ref sync mode */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl); + if (rc) + return rc; + + /* Get esync divisor */ + rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &esync_div); + if (rc) + return rc; + + sync_mode = FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref_sync_ctrl); + + switch (sync_mode) { + case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75: + esync->freq = (esync_div == ZL_REF_ESYNC_DIV_1HZ) ? 1 : 0; + esync->pulse = 25; + break; + default: + esync->freq = 0; + esync->pulse = 0; + break; + } + + /* If the pin supports esync control expose its range but only + * if the current reference frequency is > 1 Hz. + */ + if (pin->esync_control && ref_freq > 1) { + esync->range = esync_freq_ranges; + esync->range_num = ARRAY_SIZE(esync_freq_ranges); + } else { + esync->range = NULL; + esync->range_num = 0; + } + + return rc; +} + +static int +zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 freq, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u8 ref, ref_sync_ctrl, sync_mode; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration into mailbox */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Get ref sync mode */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl); + if (rc) + return rc; + + /* Use freq == 0 to disable esync */ + if (!freq) + sync_mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF; + else + sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75; + + ref_sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE; + ref_sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode); + + /* Update ref sync control register */ + rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL, ref_sync_ctrl); + if (rc) + return rc; + + if (freq) { + /* 1 Hz is only supported frequnecy currently */ + rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV, + ZL_REF_ESYNC_DIV_1HZ); + if (rc) + return rc; + } + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + +static int +zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + s64 *ffo, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin = pin_priv; + + *ffo = pin->freq_offset; + + return 0; +} + +static int +zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 *frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dpll_pin *pin = pin_priv; + u32 ref_freq; + u8 ref; + int rc; + + /* Read and return ref frequency */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref, &ref_freq); + if (!rc) + *frequency = ref_freq; + + return rc; +} + +static int +zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u16 base, mult; + u8 ref; + int rc; + + /* Get base frequency and multiplier for the requested frequency */ + rc = zl3073x_ref_freq_factorize(frequency, &base, &mult); + if (rc) + return rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Load reference configuration */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + + /* Update base frequency, multiplier, numerator & denominator */ + rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE, base); + if (rc) + return rc; + rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT, mult); + if (rc) + return rc; + rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M, 1); + if (rc) + return rc; + rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N, 1); + if (rc) + return rc; + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + +/** + * zl3073x_dpll_selected_ref_get - get currently selected reference + * @zldpll: pointer to zl3073x_dpll + * @ref: place to store selected reference + * + * Check for currently selected reference the DPLL should be locked to + * and stores its index to given @ref. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref) +{ + struct zl3073x_dev *zldev = zldpll->dev; + u8 state, value; + int rc; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + /* For automatic mode read refsel_status register */ + rc = zl3073x_read_u8(zldev, + ZL_REG_DPLL_REFSEL_STATUS(zldpll->id), + &value); + if (rc) + return rc; + + /* Extract reference state */ + state = FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value); + + /* Return the reference only if the DPLL is locked to it */ + if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK) + *ref = FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value); + else + *ref = ZL3073X_DPLL_REF_NONE; + break; + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* For manual mode return stored value */ + *ref = zldpll->forced_ref; + break; + default: + /* For other modes like NCO, freerun... there is no input ref */ + *ref = ZL3073X_DPLL_REF_NONE; + break; + } + + return 0; +} + +/** + * zl3073x_dpll_selected_ref_set - select reference in manual mode + * @zldpll: pointer to zl3073x_dpll + * @ref: input reference to be selected + * + * Selects the given reference for the DPLL channel it should be + * locked to. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref) +{ + struct zl3073x_dev *zldev = zldpll->dev; + u8 mode, mode_refsel; + int rc; + + mode = zldpll->refsel_mode; + + switch (mode) { + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* Manual mode with ref selected */ + if (ref == ZL3073X_DPLL_REF_NONE) { + switch (zldpll->lock_status) { + case DPLL_LOCK_STATUS_LOCKED_HO_ACQ: + case DPLL_LOCK_STATUS_HOLDOVER: + /* Switch to forced holdover */ + mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER; + break; + default: + /* Switch to freerun */ + mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN; + break; + } + /* Keep selected reference */ + ref = zldpll->forced_ref; + } else if (ref == zldpll->forced_ref) { + /* No register update - same mode and same ref */ + return 0; + } + break; + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + /* Manual mode without no ref */ + if (ref == ZL3073X_DPLL_REF_NONE) + /* No register update - keep current mode */ + return 0; + + /* Switch to reflock mode and update ref selection */ + mode = ZL_DPLL_MODE_REFSEL_MODE_REFLOCK; + break; + default: + /* For other modes like automatic or NCO ref cannot be selected + * manually + */ + return -EOPNOTSUPP; + } + + /* Build mode_refsel value */ + mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode) | + FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref); + + /* Update dpll_mode_refsel register */ + rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id), + mode_refsel); + if (rc) + return rc; + + /* Store new mode and forced reference */ + zldpll->refsel_mode = mode; + zldpll->forced_ref = ref; + + return rc; +} + +/** + * zl3073x_dpll_connected_ref_get - get currently connected reference + * @zldpll: pointer to zl3073x_dpll + * @ref: place to store selected reference + * + * Looks for currently connected the DPLL is locked to and stores its index + * to given @ref. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref) +{ + struct zl3073x_dev *zldev = zldpll->dev; + int rc; + + /* Get currently selected input reference */ + rc = zl3073x_dpll_selected_ref_get(zldpll, ref); + if (rc) + return rc; + + if (ZL3073X_DPLL_REF_IS_VALID(*ref)) { + u8 ref_status; + + /* Read the reference monitor status */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(*ref), + &ref_status); + if (rc) + return rc; + + /* If the monitor indicates an error nothing is connected */ + if (ref_status != ZL_REF_MON_STATUS_OK) + *ref = ZL3073X_DPLL_REF_NONE; + } + + return 0; +} + +static int +zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, s64 *phase_offset, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u8 conn_ref, ref, ref_status; + s64 ref_phase; + int rc; + + /* Get currently connected reference */ + rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_ref); + if (rc) + return rc; + + /* Report phase offset only for currently connected pin if the phase + * monitor feature is disabled. + */ + ref = zl3073x_input_pin_ref_get(pin->id); + if (!zldpll->phase_monitor && ref != conn_ref) { + *phase_offset = 0; + + return 0; + } + + /* Get this pin monitor status */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &ref_status); + if (rc) + return rc; + + /* Report phase offset only if the input pin signal is present */ + if (ref_status != ZL_REF_MON_STATUS_OK) { + *phase_offset = 0; + + return 0; + } + + ref_phase = pin->phase_offset; + + /* The DPLL being locked to a higher freq than the current ref + * the phase offset is modded to the period of the signal + * the dpll is locked to. + */ + if (ZL3073X_DPLL_REF_IS_VALID(conn_ref) && conn_ref != ref) { + u32 conn_freq, ref_freq; + + /* Get frequency of connected ref */ + rc = zl3073x_dpll_input_ref_frequency_get(zldpll, conn_ref, + &conn_freq); + if (rc) + return rc; + + /* Get frequency of given ref */ + rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref, + &ref_freq); + if (rc) + return rc; + + if (conn_freq > ref_freq) { + s64 conn_period, div_factor; + + conn_period = div_s64(PSEC_PER_SEC, conn_freq); + div_factor = div64_s64(ref_phase, conn_period); + ref_phase -= conn_period * div_factor; + } + } + + *phase_offset = ref_phase * DPLL_PHASE_OFFSET_DIVIDER; + + return rc; +} + +static int +zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 *phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + s64 phase_comp; + u8 ref; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Read current phase offset compensation */ + rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, &phase_comp); + if (rc) + return rc; + + /* Perform sign extension for 48bit signed value */ + phase_comp = sign_extend64(phase_comp, 47); + + /* Reverse two's complement negation applied during set and convert + * to 32bit signed int + */ + *phase_adjust = (s32)-phase_comp; + + return rc; +} + +static int +zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + s64 phase_comp; + u8 ref; + int rc; + + /* The value in the register is stored as two's complement negation + * of requested value. + */ + phase_comp = -phase_adjust; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Write the requested value into the compensation register */ + rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, phase_comp); + if (rc) + return rc; + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + +/** + * zl3073x_dpll_ref_prio_get - get priority for given input pin + * @pin: pointer to pin + * @prio: place to store priority + * + * Reads current priority for the given input pin and stores the value + * to @prio. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_dev *zldev = zldpll->dev; + u8 ref, ref_prio; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read DPLL configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); + if (rc) + return rc; + + /* Read reference priority - one value for P&N pins (4 bits/pin) */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), + &ref_prio); + if (rc) + return rc; + + /* Select nibble according pin type */ + if (zl3073x_dpll_is_p_pin(pin)) + *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, ref_prio); + else + *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, ref_prio); + + return rc; +} + +/** + * zl3073x_dpll_ref_prio_set - set priority for given input pin + * @pin: pointer to pin + * @prio: place to store priority + * + * Sets priority for the given input pin. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_prio_set(struct zl3073x_dpll_pin *pin, u8 prio) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_dev *zldev = zldpll->dev; + u8 ref, ref_prio; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read DPLL configuration into mailbox */ + rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); + if (rc) + return rc; + + /* Read reference priority - one value shared between P&N pins */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), &ref_prio); + if (rc) + return rc; + + /* Update nibble according pin type */ + if (zl3073x_dpll_is_p_pin(pin)) { + ref_prio &= ~ZL_DPLL_REF_PRIO_REF_P; + ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio); + } else { + ref_prio &= ~ZL_DPLL_REF_PRIO_REF_N; + ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio); + } + + /* Update reference priority */ + rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), ref_prio); + if (rc) + return rc; + + /* Commit configuration */ + return zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); +} + +/** + * zl3073x_dpll_ref_state_get - get status for given input pin + * @pin: pointer to pin + * @state: place to store status + * + * Checks current status for the given input pin and stores the value + * to @state. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin, + enum dpll_pin_state *state) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_dev *zldev = zldpll->dev; + u8 ref, ref_conn, status; + int rc; + + ref = zl3073x_input_pin_ref_get(pin->id); + + /* Get currently connected reference */ + rc = zl3073x_dpll_connected_ref_get(zldpll, &ref_conn); + if (rc) + return rc; + + if (ref == ref_conn) { + *state = DPLL_PIN_STATE_CONNECTED; + return 0; + } + + /* If the DPLL is running in automatic mode and the reference is + * selectable and its monitor does not report any error then report + * pin as selectable. + */ + if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO && + pin->selectable) { + /* Read reference monitor status */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), + &status); + if (rc) + return rc; + + /* If the monitor indicates errors report the reference + * as disconnected + */ + if (status == ZL_REF_MON_STATUS_OK) { + *state = DPLL_PIN_STATE_SELECTABLE; + return 0; + } + } + + /* Otherwise report the pin as disconnected */ + *state = DPLL_PIN_STATE_DISCONNECTED; + + return 0; +} + +static int +zl3073x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state *state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin = pin_priv; + + return zl3073x_dpll_ref_state_get(pin, state); +} + +static int +zl3073x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dpll_pin *pin = pin_priv; + u8 new_ref; + int rc; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + if (state == DPLL_PIN_STATE_CONNECTED) { + /* Choose the pin as new selected reference */ + new_ref = zl3073x_input_pin_ref_get(pin->id); + } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + /* No reference */ + new_ref = ZL3073X_DPLL_REF_NONE; + } else { + NL_SET_ERR_MSG_MOD(extack, + "Invalid pin state for manual mode"); + return -EINVAL; + } + + rc = zl3073x_dpll_selected_ref_set(zldpll, new_ref); + break; + + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + if (state == DPLL_PIN_STATE_SELECTABLE) { + if (pin->selectable) + return 0; /* Pin is already selectable */ + + /* Restore pin priority in HW */ + rc = zl3073x_dpll_ref_prio_set(pin, pin->prio); + if (rc) + return rc; + + /* Mark pin as selectable */ + pin->selectable = true; + } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + if (!pin->selectable) + return 0; /* Pin is already disconnected */ + + /* Set pin priority to none in HW */ + rc = zl3073x_dpll_ref_prio_set(pin, + ZL_DPLL_REF_PRIO_NONE); + if (rc) + return rc; + + /* Mark pin as non-selectable */ + pin->selectable = false; + } else { + NL_SET_ERR_MSG(extack, + "Invalid pin state for automatic mode"); + return -EINVAL; + } + break; + + default: + /* In other modes we cannot change input reference */ + NL_SET_ERR_MSG(extack, + "Pin state cannot be changed in current mode"); + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +static int +zl3073x_dpll_input_pin_prio_get(const struct dpll_pin *dpll_pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + u32 *prio, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin = pin_priv; + + *prio = pin->prio; + + return 0; +} + +static int +zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + u32 prio, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin = pin_priv; + int rc; + + if (prio > ZL_DPLL_REF_PRIO_MAX) + return -EINVAL; + + /* If the pin is selectable then update HW registers */ + if (pin->selectable) { + rc = zl3073x_dpll_ref_prio_set(pin, prio); + if (rc) + return rc; + } + + /* Save priority */ + pin->prio = prio; + + return 0; +} + +static int +zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + struct dpll_pin_esync *esync, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + struct device *dev = zldev->dev; + u32 esync_period, esync_width; + u8 clock_type, synth; + u8 out, output_mode; + u32 output_div; + u32 synth_freq; + int rc; + + out = zl3073x_output_pin_out_get(pin->id); + + /* If N-division is enabled, esync is not supported. The register used + * for N-division is also used for the esync divider so both cannot + * be used. + */ + switch (zl3073x_out_signal_format_get(zldev, out)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + return -EOPNOTSUPP; + default: + break; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read output mode */ + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Read output divisor */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!output_div) { + dev_err(dev, "Zero divisor for OUTPUT%u got from device\n", + out); + return -EINVAL; + } + + /* Get synth attached to output pin */ + synth = zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode); + if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) { + /* No need to read esync data if it is not enabled */ + esync->freq = 0; + esync->pulse = 0; + + goto finish; + } + + /* Read esync period */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &esync_period); + if (rc) + return rc; + + /* Check esync divisor for zero */ + if (!esync_period) { + dev_err(dev, "Zero esync divisor for OUTPUT%u got from device\n", + out); + return -EINVAL; + } + + /* Get esync pulse width in units of half synth cycles */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width); + if (rc) + return rc; + + /* Compute esync frequency */ + esync->freq = synth_freq / output_div / esync_period; + + /* By comparing the esync_pulse_width to the half of the pulse width + * the esync pulse percentage can be determined. + * Note that half pulse width is in units of half synth cycles, which + * is why it reduces down to be output_div. + */ + esync->pulse = (50 * esync_width) / output_div; + +finish: + /* Set supported esync ranges if the pin supports esync control and + * if the output frequency is > 1 Hz. + */ + if (pin->esync_control && (synth_freq / output_div) > 1) { + esync->range = esync_freq_ranges; + esync->range_num = ARRAY_SIZE(esync_freq_ranges); + } else { + esync->range = NULL; + esync->range_num = 0; + } + + return 0; +} + +static int +zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 freq, + struct netlink_ext_ack *extack) +{ + u32 esync_period, esync_width, output_div; + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u8 clock_type, out, output_mode, synth; + u32 synth_freq; + int rc; + + out = zl3073x_output_pin_out_get(pin->id); + + /* If N-division is enabled, esync is not supported. The register used + * for N-division is also used for the esync divider so both cannot + * be used. + */ + switch (zl3073x_out_signal_format_get(zldev, out)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + return -EOPNOTSUPP; + default: + break; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read output mode */ + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Select clock type */ + if (freq) + clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC; + else + clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL; + + /* Update clock type in output mode */ + output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE; + output_mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type); + rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, output_mode); + if (rc) + return rc; + + /* If esync is being disabled just write mailbox and finish */ + if (!freq) + goto write_mailbox; + + /* Get synth attached to output pin */ + synth = zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!output_div) { + dev_err(zldev->dev, + "Zero divisor for OUTPUT%u got from device\n", out); + return -EINVAL; + } + + /* Compute and update esync period */ + esync_period = synth_freq / (u32)freq / output_div; + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, esync_period); + if (rc) + return rc; + + /* Half of the period in units of 1/2 synth cycle can be represented by + * the output_div. To get the supported esync pulse width of 25% of the + * period the output_div can just be divided by 2. Note that this + * assumes that output_div is even, otherwise some resolution will be + * lost. + */ + esync_width = output_div / 2; + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, esync_width); + if (rc) + return rc; + +write_mailbox: + /* Commit output configuration */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + +static int +zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 *frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + struct device *dev = zldev->dev; + u8 out, signal_format, synth; + u32 output_div, synth_freq; + int rc; + + out = zl3073x_output_pin_out_get(pin->id); + synth = zl3073x_out_synth_get(zldev, out); + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!output_div) { + dev_err(dev, "Zero divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Read used signal format for the given output */ + signal_format = zl3073x_out_signal_format_get(zldev, out); + + switch (signal_format) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + /* In case of divided format we have to distiguish between + * given output pin type. + */ + if (zl3073x_dpll_is_p_pin(pin)) { + /* For P-pin the resulting frequency is computed as + * simple division of synth frequency and output + * divisor. + */ + *frequency = synth_freq / output_div; + } else { + /* For N-pin we have to divide additionally by + * divisor stored in esync_period output mailbox + * register that is used as N-pin divisor for these + * modes. + */ + u32 ndiv; + + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, + &ndiv); + if (rc) + return rc; + + /* Check N-pin divisor for zero */ + if (!ndiv) { + dev_err(dev, + "Zero N-pin divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Compute final divisor for N-pin */ + *frequency = synth_freq / output_div / ndiv; + } + break; + default: + /* In other modes the resulting frequency is computed as + * division of synth frequency and output divisor. + */ + *frequency = synth_freq / output_div; + break; + } + + return rc; +} + +static int +zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + struct device *dev = zldev->dev; + u32 output_n_freq, output_p_freq; + u8 out, signal_format, synth; + u32 cur_div, new_div, ndiv; + u32 synth_freq; + int rc; + + out = zl3073x_output_pin_out_get(pin->id); + synth = zl3073x_out_synth_get(zldev, out); + synth_freq = zl3073x_synth_freq_get(zldev, synth); + new_div = synth_freq / (u32)frequency; + + /* Get used signal format for the given output */ + signal_format = zl3073x_out_signal_format_get(zldev, out); + + guard(mutex)(&zldev->multiop_lock); + + /* Load output configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Check signal format */ + if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV && + signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) { + /* For non N-divided signal formats the frequency is computed + * as division of synth frequency and output divisor. + */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div); + if (rc) + return rc; + + /* Commit output configuration */ + return zl3073x_mb_op(zldev, + ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + } + + /* For N-divided signal format get current divisor */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!cur_div) { + dev_err(dev, "Zero divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Get N-pin divisor (shares the same register with esync */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv); + if (rc) + return rc; + + /* Check N-pin divisor for zero */ + if (!ndiv) { + dev_err(dev, + "Zero N-pin divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Compute current output frequency for P-pin */ + output_p_freq = synth_freq / cur_div; + + /* Compute current N-pin frequency */ + output_n_freq = output_p_freq / ndiv; + + if (zl3073x_dpll_is_p_pin(pin)) { + /* We are going to change output frequency for P-pin but + * if the requested frequency is less than current N-pin + * frequency then indicate a failure as we are not able + * to compute N-pin divisor to keep its frequency unchanged. + */ + if (frequency <= output_n_freq) + return -EINVAL; + + /* Update the output divisor */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div); + if (rc) + return rc; + + /* Compute new divisor for N-pin */ + ndiv = (u32)frequency / output_n_freq; + } else { + /* We are going to change frequency of N-pin but if + * the requested freq is greater or equal than freq of P-pin + * in the output pair we cannot compute divisor for the N-pin. + * In this case indicate a failure. + */ + if (output_p_freq <= frequency) + return -EINVAL; + + /* Compute new divisor for N-pin */ + ndiv = output_p_freq / (u32)frequency; + } + + /* Update divisor for the N-pin */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv); + if (rc) + return rc; + + /* Commit output configuration */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + +static int +zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 *phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + u32 synth_freq; + s32 phase_comp; + u8 out, synth; + int rc; + + out = zl3073x_output_pin_out_get(pin->id); + synth = zl3073x_out_synth_get(zldev, out); + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + /* Check synth freq for zero */ + if (!synth_freq) { + dev_err(zldev->dev, "Got zero synth frequency for output %u\n", + out); + return -EINVAL; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read current output phase compensation */ + rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, &phase_comp); + if (rc) + return rc; + + /* Value in register is expressed in half synth clock cycles */ + phase_comp *= (int)div_u64(PSEC_PER_SEC, 2 * synth_freq); + + /* Reverse two's complement negation applied during 'set' */ + *phase_adjust = -phase_comp; + + return rc; +} + +static int +zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + struct zl3073x_dpll_pin *pin = pin_priv; + int half_synth_cycle; + u32 synth_freq; + u8 out, synth; + int rc; + + /* Get attached synth */ + out = zl3073x_output_pin_out_get(pin->id); + synth = zl3073x_out_synth_get(zldev, out); + + /* Get synth's frequency */ + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + /* Value in register is expressed in half synth clock cycles so + * the given phase adjustment a multiple of half synth clock. + */ + half_synth_cycle = (int)div_u64(PSEC_PER_SEC, 2 * synth_freq); + + if ((phase_adjust % half_synth_cycle) != 0) { + NL_SET_ERR_MSG_FMT(extack, + "Phase adjustment value has to be multiple of %d", + half_synth_cycle); + return -EINVAL; + } + phase_adjust /= half_synth_cycle; + + /* The value in the register is stored as two's complement negation + * of requested value. + */ + phase_adjust = -phase_adjust; + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Write the requested value into the compensation register */ + rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, phase_adjust); + if (rc) + return rc; + + /* Update output configuration from mailbox */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + +static int +zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state *state, + struct netlink_ext_ack *extack) +{ + /* If the output pin is registered then it is always connected */ + *state = DPLL_PIN_STATE_CONNECTED; + + return 0; +} + +static int +zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv, + enum dpll_lock_status *status, + enum dpll_lock_status_error *status_error, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dev *zldev = zldpll->dev; + u8 mon_status, state; + int rc; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_NCO: + /* In FREERUN and NCO modes the DPLL is always unlocked */ + *status = DPLL_LOCK_STATUS_UNLOCKED; + + return 0; + default: + break; + } + + /* Read DPLL monitor status */ + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id), + &mon_status); + if (rc) + return rc; + state = FIELD_GET(ZL_DPLL_MON_STATUS_STATE, mon_status); + + switch (state) { + case ZL_DPLL_MON_STATUS_STATE_LOCK: + if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status)) + *status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ; + else + *status = DPLL_LOCK_STATUS_LOCKED; + break; + case ZL_DPLL_MON_STATUS_STATE_HOLDOVER: + case ZL_DPLL_MON_STATUS_STATE_ACQUIRING: + *status = DPLL_LOCK_STATUS_HOLDOVER; + break; + default: + dev_warn(zldev->dev, "Unknown DPLL monitor status: 0x%02x\n", + mon_status); + *status = DPLL_LOCK_STATUS_UNLOCKED; + break; + } + + return 0; +} + +static int +zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv, + enum dpll_mode *mode, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + case ZL_DPLL_MODE_REFSEL_MODE_NCO: + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* Use MANUAL for device FREERUN, HOLDOVER, NCO and + * REFLOCK modes + */ + *mode = DPLL_MODE_MANUAL; + break; + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + /* Use AUTO for device AUTO mode */ + *mode = DPLL_MODE_AUTOMATIC; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_feature_state *state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + + if (zldpll->phase_monitor) + *state = DPLL_FEATURE_STATE_ENABLE; + else + *state = DPLL_FEATURE_STATE_DISABLE; + + return 0; +} + +static int +zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_feature_state state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + + zldpll->phase_monitor = (state == DPLL_FEATURE_STATE_ENABLE); + + return 0; +} + +static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = { + .direction_get = zl3073x_dpll_pin_direction_get, + .esync_get = zl3073x_dpll_input_pin_esync_get, + .esync_set = zl3073x_dpll_input_pin_esync_set, + .ffo_get = zl3073x_dpll_input_pin_ffo_get, + .frequency_get = zl3073x_dpll_input_pin_frequency_get, + .frequency_set = zl3073x_dpll_input_pin_frequency_set, + .phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get, + .phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get, + .phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set, + .prio_get = zl3073x_dpll_input_pin_prio_get, + .prio_set = zl3073x_dpll_input_pin_prio_set, + .state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get, + .state_on_dpll_set = zl3073x_dpll_input_pin_state_on_dpll_set, +}; + +static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = { + .direction_get = zl3073x_dpll_pin_direction_get, + .esync_get = zl3073x_dpll_output_pin_esync_get, + .esync_set = zl3073x_dpll_output_pin_esync_set, + .frequency_get = zl3073x_dpll_output_pin_frequency_get, + .frequency_set = zl3073x_dpll_output_pin_frequency_set, + .phase_adjust_get = zl3073x_dpll_output_pin_phase_adjust_get, + .phase_adjust_set = zl3073x_dpll_output_pin_phase_adjust_set, + .state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get, +}; + +static const struct dpll_device_ops zl3073x_dpll_device_ops = { + .lock_status_get = zl3073x_dpll_lock_status_get, + .mode_get = zl3073x_dpll_mode_get, + .phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get, + .phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set, +}; + +/** + * zl3073x_dpll_pin_alloc - allocate DPLL pin + * @zldpll: pointer to zl3073x_dpll + * @dir: pin direction + * @id: pin id + * + * Allocates and initializes zl3073x_dpll_pin structure for given + * pin id and direction. + * + * Return: pointer to allocated structure on success, error pointer on error + */ +static struct zl3073x_dpll_pin * +zl3073x_dpll_pin_alloc(struct zl3073x_dpll *zldpll, enum dpll_pin_direction dir, + u8 id) +{ + struct zl3073x_dpll_pin *pin; + + pin = kzalloc(sizeof(*pin), GFP_KERNEL); + if (!pin) + return ERR_PTR(-ENOMEM); + + pin->dpll = zldpll; + pin->dir = dir; + pin->id = id; + + return pin; +} + +/** + * zl3073x_dpll_pin_free - deallocate DPLL pin + * @pin: pin to free + * + * Deallocates DPLL pin previously allocated by @zl3073x_dpll_pin_alloc. + */ +static void +zl3073x_dpll_pin_free(struct zl3073x_dpll_pin *pin) +{ + WARN(pin->dpll_pin, "DPLL pin is still registered\n"); + + kfree(pin); +} + +/** + * zl3073x_dpll_pin_register - register DPLL pin + * @pin: pointer to DPLL pin + * @index: absolute pin index for registration + * + * Registers given DPLL pin into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_pin_props *props; + const struct dpll_pin_ops *ops; + int rc; + + /* Get pin properties */ + props = zl3073x_pin_props_get(zldpll->dev, pin->dir, pin->id); + if (IS_ERR(props)) + return PTR_ERR(props); + + /* Save package label & esync capability */ + strscpy(pin->label, props->package_label); + pin->esync_control = props->esync_control; + + if (zl3073x_dpll_is_input_pin(pin)) { + rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio); + if (rc) + goto err_prio_get; + + if (pin->prio == ZL_DPLL_REF_PRIO_NONE) { + /* Clamp prio to max value & mark pin non-selectable */ + pin->prio = ZL_DPLL_REF_PRIO_MAX; + pin->selectable = false; + } else { + /* Mark pin as selectable */ + pin->selectable = true; + } + } + + /* Create or get existing DPLL pin */ + pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE, + &props->dpll_props); + if (IS_ERR(pin->dpll_pin)) { + rc = PTR_ERR(pin->dpll_pin); + goto err_pin_get; + } + + if (zl3073x_dpll_is_input_pin(pin)) + ops = &zl3073x_dpll_input_pin_ops; + else + ops = &zl3073x_dpll_output_pin_ops; + + /* Register the pin */ + rc = dpll_pin_register(zldpll->dpll_dev, pin->dpll_pin, ops, pin); + if (rc) + goto err_register; + + /* Free pin properties */ + zl3073x_pin_props_put(props); + + return 0; + +err_register: + dpll_pin_put(pin->dpll_pin); +err_prio_get: + pin->dpll_pin = NULL; +err_pin_get: + zl3073x_pin_props_put(props); + + return rc; +} + +/** + * zl3073x_dpll_pin_unregister - unregister DPLL pin + * @pin: pointer to DPLL pin + * + * Unregisters pin previously registered by @zl3073x_dpll_pin_register. + */ +static void +zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + const struct dpll_pin_ops *ops; + + WARN(!pin->dpll_pin, "DPLL pin is not registered\n"); + + if (zl3073x_dpll_is_input_pin(pin)) + ops = &zl3073x_dpll_input_pin_ops; + else + ops = &zl3073x_dpll_output_pin_ops; + + /* Unregister the pin */ + dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin); + + dpll_pin_put(pin->dpll_pin); + pin->dpll_pin = NULL; +} + +/** + * zl3073x_dpll_pins_unregister - unregister all registered DPLL pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Enumerates all DPLL pins registered to given DPLL device and + * unregisters them. + */ +static void +zl3073x_dpll_pins_unregister(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin, *next; + + list_for_each_entry_safe(pin, next, &zldpll->pins, list) { + zl3073x_dpll_pin_unregister(pin); + list_del(&pin->list); + zl3073x_dpll_pin_free(pin); + } +} + +/** + * zl3073x_dpll_pin_is_registrable - check if the pin is registrable + * @zldpll: pointer to zl3073x_dpll structure + * @dir: pin direction + * @index: pin index + * + * Checks if the given pin can be registered to given DPLL. For both + * directions the pin can be registered if it is enabled. In case of + * differential signal type only P-pin is reported as registrable. + * And additionally for the output pin, the pin can be registered only + * if it is connected to synthesizer that is driven by given DPLL. + * + * Return: true if the pin is registrable, false if not + */ +static bool +zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll, + enum dpll_pin_direction dir, u8 index) +{ + struct zl3073x_dev *zldev = zldpll->dev; + bool is_diff, is_enabled; + const char *name; + + if (dir == DPLL_PIN_DIRECTION_INPUT) { + u8 ref = zl3073x_input_pin_ref_get(index); + + name = "REF"; + + /* Skip the pin if the DPLL is running in NCO mode */ + if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_NCO) + return false; + + is_diff = zl3073x_ref_is_diff(zldev, ref); + is_enabled = zl3073x_ref_is_enabled(zldev, ref); + } else { + /* Output P&N pair shares single HW output */ + u8 out = zl3073x_output_pin_out_get(index); + + name = "OUT"; + + /* Skip the pin if it is connected to different DPLL channel */ + if (zl3073x_out_dpll_get(zldev, out) != zldpll->id) { + dev_dbg(zldev->dev, + "%s%u is driven by different DPLL\n", name, + out); + + return false; + } + + is_diff = zl3073x_out_is_diff(zldev, out); + is_enabled = zl3073x_out_is_enabled(zldev, out); + } + + /* Skip N-pin if the corresponding input/output is differential */ + if (is_diff && zl3073x_is_n_pin(index)) { + dev_dbg(zldev->dev, "%s%u is differential, skipping N-pin\n", + name, index / 2); + + return false; + } + + /* Skip the pin if it is disabled */ + if (!is_enabled) { + dev_dbg(zldev->dev, "%s%u%c is disabled\n", name, index / 2, + zl3073x_is_p_pin(index) ? 'P' : 'N'); + + return false; + } + + return true; +} + +/** + * zl3073x_dpll_pins_register - register all registerable DPLL pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Enumerates all possible input/output pins and registers all of them + * that are registrable. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin; + enum dpll_pin_direction dir; + u8 id, index; + int rc; + + /* Process input pins */ + for (index = 0; index < ZL3073X_NUM_PINS; index++) { + /* First input pins and then output pins */ + if (index < ZL3073X_NUM_INPUT_PINS) { + id = index; + dir = DPLL_PIN_DIRECTION_INPUT; + } else { + id = index - ZL3073X_NUM_INPUT_PINS; + dir = DPLL_PIN_DIRECTION_OUTPUT; + } + + /* Check if the pin registrable to this DPLL */ + if (!zl3073x_dpll_pin_is_registrable(zldpll, dir, id)) + continue; + + pin = zl3073x_dpll_pin_alloc(zldpll, dir, id); + if (IS_ERR(pin)) { + rc = PTR_ERR(pin); + goto error; + } + + rc = zl3073x_dpll_pin_register(pin, index); + if (rc) + goto error; + + list_add(&pin->list, &zldpll->pins); + } + + return 0; + +error: + zl3073x_dpll_pins_unregister(zldpll); + + return rc; +} + +/** + * zl3073x_dpll_device_register - register DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Registers given DPLL device into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dev *zldev = zldpll->dev; + u8 dpll_mode_refsel; + int rc; + + /* Read DPLL mode and forcibly selected reference */ + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id), + &dpll_mode_refsel); + if (rc) + return rc; + + /* Extract mode and selected input reference */ + zldpll->refsel_mode = FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, + dpll_mode_refsel); + zldpll->forced_ref = FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, + dpll_mode_refsel); + + zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id, + THIS_MODULE); + if (IS_ERR(zldpll->dpll_dev)) { + rc = PTR_ERR(zldpll->dpll_dev); + zldpll->dpll_dev = NULL; + + return rc; + } + + rc = dpll_device_register(zldpll->dpll_dev, + zl3073x_prop_dpll_type_get(zldev, zldpll->id), + &zl3073x_dpll_device_ops, zldpll); + if (rc) { + dpll_device_put(zldpll->dpll_dev); + zldpll->dpll_dev = NULL; + } + + return rc; +} + +/** + * zl3073x_dpll_device_unregister - unregister DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Unregisters given DPLL device from DPLL sub-system previously registered + * by @zl3073x_dpll_device_register. + */ +static void +zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll) +{ + WARN(!zldpll->dpll_dev, "DPLL device is not registered\n"); + + dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops, + zldpll); + dpll_device_put(zldpll->dpll_dev); + zldpll->dpll_dev = NULL; +} + +/** + * zl3073x_dpll_pin_phase_offset_check - check for pin phase offset change + * @pin: pin to check + * + * Check for the change of DPLL to connected pin phase offset change. + * + * Return: true on phase offset change, false otherwise + */ +static bool +zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_dev *zldev = zldpll->dev; + unsigned int reg; + s64 phase_offset; + u8 ref; + int rc; + + ref = zl3073x_input_pin_ref_get(pin->id); + + /* Select register to read phase offset value depending on pin and + * phase monitor state: + * 1) For connected pin use dpll_phase_err_data register + * 2) For other pins use appropriate ref_phase register if the phase + * monitor feature is enabled and reference monitor does not + * report signal errors for given input pin + */ + if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) { + reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id); + } else if (zldpll->phase_monitor) { + u8 status; + + /* Get reference monitor status */ + rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), + &status); + if (rc) { + dev_err(zldev->dev, + "Failed to read %s refmon status: %pe\n", + pin->label, ERR_PTR(rc)); + + return false; + } + + if (status != ZL_REF_MON_STATUS_OK) + return false; + + reg = ZL_REG_REF_PHASE(ref); + } else { + /* The pin is not connected or phase monitor disabled */ + return false; + } + + /* Read measured phase offset value */ + rc = zl3073x_read_u48(zldev, reg, &phase_offset); + if (rc) { + dev_err(zldev->dev, "Failed to read ref phase offset: %pe\n", + ERR_PTR(rc)); + + return false; + } + + /* Convert to ps */ + phase_offset = div_s64(sign_extend64(phase_offset, 47), 100); + + /* Compare with previous value */ + if (phase_offset != pin->phase_offset) { + dev_dbg(zldev->dev, "%s phase offset changed: %lld -> %lld\n", + pin->label, pin->phase_offset, phase_offset); + pin->phase_offset = phase_offset; + + return true; + } + + return false; +} + +/** + * zl3073x_dpll_pin_ffo_check - check for pin fractional frequency offset change + * @pin: pin to check + * + * Check for the given pin's fractional frequency change. + * + * Return: true on fractional frequency offset change, false otherwise + */ +static bool +zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll = pin->dpll; + struct zl3073x_dev *zldev = zldpll->dev; + u8 ref, status; + s64 ffo; + int rc; + + /* Get reference monitor status */ + ref = zl3073x_input_pin_ref_get(pin->id); + rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status); + if (rc) { + dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n", + pin->label, ERR_PTR(rc)); + + return false; + } + + /* Do not report ffo changes if the reference monitor report errors */ + if (status != ZL_REF_MON_STATUS_OK) + return false; + + /* Get the latest measured ref's ffo */ + ffo = zl3073x_ref_ffo_get(zldev, ref); + + /* Compare with previous value */ + if (pin->freq_offset != ffo) { + dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n", + pin->label, pin->freq_offset, ffo); + pin->freq_offset = ffo; + + return true; + } + + return false; +} + +/** + * zl3073x_dpll_changes_check - check for changes and send notifications + * @zldpll: pointer to zl3073x_dpll structure + * + * Checks for changes on given DPLL device and its registered DPLL pins + * and sends notifications about them. + * + * This function is periodically called from @zl3073x_dev_periodic_work. + */ +void +zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dev *zldev = zldpll->dev; + enum dpll_lock_status lock_status; + struct device *dev = zldev->dev; + struct zl3073x_dpll_pin *pin; + int rc; + + zldpll->check_count++; + + /* Get current lock status for the DPLL */ + rc = zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll, + &lock_status, NULL, NULL); + if (rc) { + dev_err(dev, "Failed to get DPLL%u lock status: %pe\n", + zldpll->id, ERR_PTR(rc)); + return; + } + + /* If lock status was changed then notify DPLL core */ + if (zldpll->lock_status != lock_status) { + zldpll->lock_status = lock_status; + dpll_device_change_ntf(zldpll->dpll_dev); + } + + /* Input pin monitoring does make sense only in automatic + * or forced reference modes. + */ + if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO && + zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) + return; + + /* Update phase offset latch registers for this DPLL if the phase + * offset monitor feature is enabled. + */ + if (zldpll->phase_monitor) { + rc = zl3073x_ref_phase_offsets_update(zldev, zldpll->id); + if (rc) { + dev_err(zldev->dev, + "Failed to update phase offsets: %pe\n", + ERR_PTR(rc)); + return; + } + } + + list_for_each_entry(pin, &zldpll->pins, list) { + enum dpll_pin_state state; + bool pin_changed = false; + + /* Output pins change checks are not necessary because output + * states are constant. + */ + if (!zl3073x_dpll_is_input_pin(pin)) + continue; + + rc = zl3073x_dpll_ref_state_get(pin, &state); + if (rc) { + dev_err(dev, + "Failed to get %s on DPLL%u state: %pe\n", + pin->label, zldpll->id, ERR_PTR(rc)); + return; + } + + if (state != pin->pin_state) { + dev_dbg(dev, "%s state changed: %u->%u\n", pin->label, + pin->pin_state, state); + pin->pin_state = state; + pin_changed = true; + } + + /* Check for phase offset and ffo change once per second */ + if (zldpll->check_count % 2 == 0) { + if (zl3073x_dpll_pin_phase_offset_check(pin)) + pin_changed = true; + + if (zl3073x_dpll_pin_ffo_check(pin)) + pin_changed = true; + } + + if (pin_changed) + dpll_pin_change_ntf(pin->dpll_pin); + } +} + +/** + * zl3073x_dpll_init_fine_phase_adjust - do initial fine phase adjustments + * @zldev: pointer to zl3073x device + * + * Performs initial fine phase adjustments needed per datasheet. + * + * Return: 0 on success, <0 on error + */ +int +zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev) +{ + int rc; + + rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_MASK, 0x1f); + if (rc) + return rc; + + rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_INTVL, 0x01); + if (rc) + return rc; + + rc = zl3073x_write_u16(zldev, ZL_REG_SYNTH_PHASE_SHIFT_DATA, 0xffff); + if (rc) + return rc; + + rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_CTRL, 0x01); + if (rc) + return rc; + + return rc; +} + +/** + * zl3073x_dpll_alloc - allocate DPLL device + * @zldev: pointer to zl3073x device + * @ch: DPLL channel number + * + * Allocates DPLL device structure for given DPLL channel. + * + * Return: pointer to DPLL device on success, error pointer on error + */ +struct zl3073x_dpll * +zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch) +{ + struct zl3073x_dpll *zldpll; + + zldpll = kzalloc(sizeof(*zldpll), GFP_KERNEL); + if (!zldpll) + return ERR_PTR(-ENOMEM); + + zldpll->dev = zldev; + zldpll->id = ch; + INIT_LIST_HEAD(&zldpll->pins); + + return zldpll; +} + +/** + * zl3073x_dpll_free - free DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Deallocates given DPLL device previously allocated by @zl3073x_dpll_alloc. + */ +void +zl3073x_dpll_free(struct zl3073x_dpll *zldpll) +{ + WARN(zldpll->dpll_dev, "DPLL device is still registered\n"); + + kfree(zldpll); +} + +/** + * zl3073x_dpll_register - register DPLL device and all its pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Registers given DPLL device and all its pins into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +int +zl3073x_dpll_register(struct zl3073x_dpll *zldpll) +{ + int rc; + + rc = zl3073x_dpll_device_register(zldpll); + if (rc) + return rc; + + rc = zl3073x_dpll_pins_register(zldpll); + if (rc) { + zl3073x_dpll_device_unregister(zldpll); + return rc; + } + + return 0; +} + +/** + * zl3073x_dpll_unregister - unregister DPLL device and its pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Unregisters given DPLL device and all its pins from DPLL sub-system + * previously registered by @zl3073x_dpll_register. + */ +void +zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll) +{ + /* Unregister all pins and dpll */ + zl3073x_dpll_pins_unregister(zldpll); + zl3073x_dpll_device_unregister(zldpll); +} diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h new file mode 100644 index 000000000000..304910ffc9c0 --- /dev/null +++ b/drivers/dpll/zl3073x/dpll.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_DPLL_H +#define _ZL3073X_DPLL_H + +#include +#include + +#include "core.h" + +/** + * struct zl3073x_dpll - ZL3073x DPLL sub-device structure + * @list: this DPLL list entry + * @dev: pointer to multi-function parent device + * @id: DPLL index + * @refsel_mode: reference selection mode + * @forced_ref: selected reference in forced reference lock mode + * @check_count: periodic check counter + * @phase_monitor: is phase offset monitor enabled + * @dpll_dev: pointer to registered DPLL device + * @lock_status: last saved DPLL lock status + * @pins: list of pins + */ +struct zl3073x_dpll { + struct list_head list; + struct zl3073x_dev *dev; + u8 id; + u8 refsel_mode; + u8 forced_ref; + u8 check_count; + bool phase_monitor; + struct dpll_device *dpll_dev; + enum dpll_lock_status lock_status; + struct list_head pins; +}; + +struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch); +void zl3073x_dpll_free(struct zl3073x_dpll *zldpll); + +int zl3073x_dpll_register(struct zl3073x_dpll *zldpll); +void zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll); + +int zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev); +void zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll); + +#endif /* _ZL3073X_DPLL_H */ diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c new file mode 100644 index 000000000000..7bbfdd4ed867 --- /dev/null +++ b/drivers/dpll/zl3073x/i2c.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "core.h" + +static int zl3073x_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct zl3073x_dev *zldev; + + zldev = zl3073x_devm_alloc(dev); + if (IS_ERR(zldev)) + return PTR_ERR(zldev); + + zldev->regmap = devm_regmap_init_i2c(client, &zl3073x_regmap_config); + if (IS_ERR(zldev->regmap)) + return dev_err_probe(dev, PTR_ERR(zldev->regmap), + "Failed to initialize regmap\n"); + + return zl3073x_dev_probe(zldev, i2c_get_match_data(client)); +} + +static const struct i2c_device_id zl3073x_i2c_id[] = { + { + .name = "zl30731", + .driver_data = (kernel_ulong_t)&zl30731_chip_info, + }, + { + .name = "zl30732", + .driver_data = (kernel_ulong_t)&zl30732_chip_info, + }, + { + .name = "zl30733", + .driver_data = (kernel_ulong_t)&zl30733_chip_info, + }, + { + .name = "zl30734", + .driver_data = (kernel_ulong_t)&zl30734_chip_info, + }, + { + .name = "zl30735", + .driver_data = (kernel_ulong_t)&zl30735_chip_info, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id); + +static const struct of_device_id zl3073x_i2c_of_match[] = { + { .compatible = "microchip,zl30731", .data = &zl30731_chip_info }, + { .compatible = "microchip,zl30732", .data = &zl30732_chip_info }, + { .compatible = "microchip,zl30733", .data = &zl30733_chip_info }, + { .compatible = "microchip,zl30734", .data = &zl30734_chip_info }, + { .compatible = "microchip,zl30735", .data = &zl30735_chip_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match); + +static struct i2c_driver zl3073x_i2c_driver = { + .driver = { + .name = "zl3073x-i2c", + .of_match_table = zl3073x_i2c_of_match, + }, + .probe = zl3073x_i2c_probe, + .id_table = zl3073x_i2c_id, +}; +module_i2c_driver(zl3073x_i2c_driver); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x I2C driver"); +MODULE_IMPORT_NS("ZL3073X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c new file mode 100644 index 000000000000..4cf7e8aefcb3 --- /dev/null +++ b/drivers/dpll/zl3073x/prop.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "prop.h" + +/** + * zl3073x_pin_check_freq - verify frequency for given pin + * @zldev: pointer to zl3073x device + * @dir: pin direction + * @id: pin index + * @freq: frequency to check + * + * The function checks the given frequency is valid for the device. For input + * pins it checks that the frequency can be factorized using supported base + * frequencies. For output pins it checks that the frequency divides connected + * synth frequency without remainder. + * + * Return: true if the frequency is valid, false if not. + */ +static bool +zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir, + u8 id, u64 freq) +{ + if (freq > U32_MAX) + goto err_inv_freq; + + if (dir == DPLL_PIN_DIRECTION_INPUT) { + int rc; + + /* Check if the frequency can be factorized */ + rc = zl3073x_ref_freq_factorize(freq, NULL, NULL); + if (rc) + goto err_inv_freq; + } else { + u32 synth_freq; + u8 out, synth; + + /* Get output pin synthesizer */ + out = zl3073x_output_pin_out_get(id); + synth = zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq = zl3073x_synth_freq_get(zldev, synth); + + /* Check the frequency divides synth frequency */ + if (synth_freq % (u32)freq) + goto err_inv_freq; + } + + return true; + +err_inv_freq: + dev_warn(zldev->dev, + "Unsupported frequency %llu Hz in firmware node\n", freq); + + return false; +} + +/** + * zl3073x_prop_pin_package_label_set - get package label for the pin + * @zldev: pointer to zl3073x device + * @props: pointer to pin properties + * @dir: pin direction + * @id: pin index + * + * Generates package label string and stores it into pin properties structure. + * + * Possible formats: + * REF - differential input reference + * REFP & REFN - single-ended input reference (P or N pin) + * OUT - differential output + * OUTP & OUTN - single-ended output (P or N pin) + */ +static void +zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev, + struct zl3073x_pin_props *props, + enum dpll_pin_direction dir, u8 id) +{ + const char *prefix, *suffix; + bool is_diff; + + if (dir == DPLL_PIN_DIRECTION_INPUT) { + u8 ref; + + prefix = "REF"; + ref = zl3073x_input_pin_ref_get(id); + is_diff = zl3073x_ref_is_diff(zldev, ref); + } else { + u8 out; + + prefix = "OUT"; + out = zl3073x_output_pin_out_get(id); + is_diff = zl3073x_out_is_diff(zldev, out); + } + + if (!is_diff) + suffix = zl3073x_is_p_pin(id) ? "P" : "N"; + else + suffix = ""; /* No suffix for differential one */ + + snprintf(props->package_label, sizeof(props->package_label), "%s%u%s", + prefix, id / 2, suffix); + + /* Set package_label pointer in DPLL core properties to generated + * string. + */ + props->dpll_props.package_label = props->package_label; +} + +/** + * zl3073x_prop_pin_fwnode_get - get fwnode for given pin + * @zldev: pointer to zl3073x device + * @props: pointer to pin properties + * @dir: pin direction + * @id: pin index + * + * Return: 0 on success, -ENOENT if the firmware node does not exist + */ +static int +zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev, + struct zl3073x_pin_props *props, + enum dpll_pin_direction dir, u8 id) +{ + struct fwnode_handle *pins_node, *pin_node; + const char *node_name; + + if (dir == DPLL_PIN_DIRECTION_INPUT) + node_name = "input-pins"; + else + node_name = "output-pins"; + + /* Get node containing input or output pins */ + pins_node = device_get_named_child_node(zldev->dev, node_name); + if (!pins_node) { + dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name); + return -ENOENT; + } + + /* Enumerate child pin nodes and find the requested one */ + fwnode_for_each_child_node(pins_node, pin_node) { + u32 reg; + + if (fwnode_property_read_u32(pin_node, "reg", ®)) + continue; + + if (id == reg) + break; + } + + /* Release pin parent node */ + fwnode_handle_put(pins_node); + + /* Save found node */ + props->fwnode = pin_node; + + dev_dbg(zldev->dev, "Firmware node for %s %sfound\n", + props->package_label, pin_node ? "" : "NOT "); + + return pin_node ? 0 : -ENOENT; +} + +/** + * zl3073x_pin_props_get - get pin properties + * @zldev: pointer to zl3073x device + * @dir: pin direction + * @index: pin index + * + * The function looks for firmware node for the given pin if it is provided + * by the system firmware (DT or ACPI), allocates pin properties structure, + * generates package label string according pin type and optionally fetches + * board label, connection type, supported frequencies and esync capability + * from the firmware node if it does exist. + * + * Pointer that is returned by this function should be freed using + * @zl3073x_pin_props_put(). + * + * Return: + * * pointer to allocated pin properties structure on success + * * error pointer in case of error + */ +struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev, + enum dpll_pin_direction dir, + u8 index) +{ + struct dpll_pin_frequency *ranges; + struct zl3073x_pin_props *props; + int i, j, num_freqs, rc; + const char *type; + u64 *freqs; + + props = kzalloc(sizeof(*props), GFP_KERNEL); + if (!props) + return ERR_PTR(-ENOMEM); + + /* Set default pin type and capabilities */ + if (dir == DPLL_PIN_DIRECTION_INPUT) { + props->dpll_props.type = DPLL_PIN_TYPE_EXT; + props->dpll_props.capabilities = + DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE | + DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + } else { + props->dpll_props.type = DPLL_PIN_TYPE_GNSS; + } + + props->dpll_props.phase_range.min = S32_MIN; + props->dpll_props.phase_range.max = S32_MAX; + + zl3073x_prop_pin_package_label_set(zldev, props, dir, index); + + /* Get firmware node for the given pin */ + rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index); + if (rc) + return props; /* Return if it does not exist */ + + /* Look for label property and store the value as board label */ + fwnode_property_read_string(props->fwnode, "label", + &props->dpll_props.board_label); + + /* Look for pin type property and translate its value to DPLL + * pin type enum if it is present. + */ + if (!fwnode_property_read_string(props->fwnode, "connection-type", + &type)) { + if (!strcmp(type, "ext")) + props->dpll_props.type = DPLL_PIN_TYPE_EXT; + else if (!strcmp(type, "gnss")) + props->dpll_props.type = DPLL_PIN_TYPE_GNSS; + else if (!strcmp(type, "int")) + props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR; + else if (!strcmp(type, "synce")) + props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT; + else + dev_warn(zldev->dev, + "Unknown or unsupported pin type '%s'\n", + type); + } + + /* Check if the pin supports embedded sync control */ + props->esync_control = fwnode_property_read_bool(props->fwnode, + "esync-control"); + + /* Read supported frequencies property if it is specified */ + num_freqs = fwnode_property_count_u64(props->fwnode, + "supported-frequencies-hz"); + if (num_freqs <= 0) + /* Return if the property does not exist or number is 0 */ + return props; + + /* The firmware node specifies list of supported frequencies while + * DPLL core pin properties requires list of frequency ranges. + * So read the frequency list into temporary array. + */ + freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL); + if (!freqs) { + rc = -ENOMEM; + goto err_alloc_freqs; + } + + /* Read frequencies list from firmware node */ + fwnode_property_read_u64_array(props->fwnode, + "supported-frequencies-hz", freqs, + num_freqs); + + /* Allocate frequency ranges list and fill it */ + ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL); + if (!ranges) { + rc = -ENOMEM; + goto err_alloc_ranges; + } + + /* Convert list of frequencies to list of frequency ranges but + * filter-out frequencies that are not representable by device + */ + for (i = 0, j = 0; i < num_freqs; i++) { + struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]); + + if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) { + ranges[j] = freq; + j++; + } + } + + /* Save number of freq ranges and pointer to them into pin properties */ + props->dpll_props.freq_supported = ranges; + props->dpll_props.freq_supported_num = j; + + /* Free temporary array */ + kfree(freqs); + + return props; + +err_alloc_ranges: + kfree(freqs); +err_alloc_freqs: + fwnode_handle_put(props->fwnode); + kfree(props); + + return ERR_PTR(rc); +} + +/** + * zl3073x_pin_props_put - release pin properties + * @props: pin properties to free + * + * The function deallocates given pin properties structure. + */ +void zl3073x_pin_props_put(struct zl3073x_pin_props *props) +{ + /* Free supported frequency ranges list if it is present */ + kfree(props->dpll_props.freq_supported); + + /* Put firmware handle if it is present */ + if (props->fwnode) + fwnode_handle_put(props->fwnode); + + kfree(props); +} + +/** + * zl3073x_prop_dpll_type_get - get DPLL channel type + * @zldev: pointer to zl3073x device + * @index: DPLL channel index + * + * Return: DPLL type for given DPLL channel + */ +enum dpll_type +zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index) +{ + const char *types[ZL3073X_MAX_CHANNELS]; + int count; + + /* Read dpll types property from firmware */ + count = device_property_read_string_array(zldev->dev, "dpll-types", + types, ARRAY_SIZE(types)); + + /* Return default if property or entry for given channel is missing */ + if (index >= count) + return DPLL_TYPE_PPS; + + if (!strcmp(types[index], "pps")) + return DPLL_TYPE_PPS; + else if (!strcmp(types[index], "eec")) + return DPLL_TYPE_EEC; + + dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n", + types[index]); + + return DPLL_TYPE_PPS; /* Default */ +} diff --git a/drivers/dpll/zl3073x/prop.h b/drivers/dpll/zl3073x/prop.h new file mode 100644 index 000000000000..721a18f05938 --- /dev/null +++ b/drivers/dpll/zl3073x/prop.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_PROP_H +#define _ZL3073X_PROP_H + +#include + +#include "core.h" + +struct fwnode_handle; + +/** + * struct zl3073x_pin_props - pin properties + * @fwnode: pin firmware node + * @dpll_props: DPLL core pin properties + * @package_label: pin package label + * @esync_control: embedded sync support + */ +struct zl3073x_pin_props { + struct fwnode_handle *fwnode; + struct dpll_pin_properties dpll_props; + char package_label[8]; + bool esync_control; +}; + +enum dpll_type zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index); + +struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev, + enum dpll_pin_direction dir, + u8 index); + +void zl3073x_pin_props_put(struct zl3073x_pin_props *props); + +#endif /* _ZL3073X_PROP_H */ diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h new file mode 100644 index 000000000000..614e33128a5c --- /dev/null +++ b/drivers/dpll/zl3073x/regs.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_REGS_H +#define _ZL3073X_REGS_H + +#include +#include + +/* + * Register address structure: + * =========================== + * 25 19 18 16 15 7 6 0 + * +------------------------------------------+ + * | max_offset | size | page | page_offset | + * +------------------------------------------+ + * + * page_offset ... <0x00..0x7F> + * page .......... HW page number + * size .......... register byte size (1, 2, 4 or 6) + * max_offset .... maximal offset for indexed registers + * (for non-indexed regs max_offset == page_offset) + */ + +#define ZL_REG_OFFSET_MASK GENMASK(6, 0) +#define ZL_REG_PAGE_MASK GENMASK(15, 7) +#define ZL_REG_SIZE_MASK GENMASK(18, 16) +#define ZL_REG_MAX_OFFSET_MASK GENMASK(25, 19) +#define ZL_REG_ADDR_MASK GENMASK(15, 0) + +#define ZL_REG_OFFSET(_reg) FIELD_GET(ZL_REG_OFFSET_MASK, _reg) +#define ZL_REG_PAGE(_reg) FIELD_GET(ZL_REG_PAGE_MASK, _reg) +#define ZL_REG_MAX_OFFSET(_reg) FIELD_GET(ZL_REG_MAX_OFFSET_MASK, _reg) +#define ZL_REG_SIZE(_reg) FIELD_GET(ZL_REG_SIZE_MASK, _reg) +#define ZL_REG_ADDR(_reg) FIELD_GET(ZL_REG_ADDR_MASK, _reg) + +/** + * ZL_REG_IDX - define indexed register + * @_idx: index of register to access + * @_page: register page + * @_offset: register offset in page + * @_size: register byte size (1, 2, 4 or 6) + * @_items: number of register indices + * @_stride: stride between items in bytes + * + * All parameters except @_idx should be constant. + */ +#define ZL_REG_IDX(_idx, _page, _offset, _size, _items, _stride) \ + (FIELD_PREP(ZL_REG_OFFSET_MASK, \ + (_offset) + (_idx) * (_stride)) | \ + FIELD_PREP_CONST(ZL_REG_PAGE_MASK, _page) | \ + FIELD_PREP_CONST(ZL_REG_SIZE_MASK, _size) | \ + FIELD_PREP_CONST(ZL_REG_MAX_OFFSET_MASK, \ + (_offset) + ((_items) - 1) * (_stride))) + +/** + * ZL_REG - define simple (non-indexed) register + * @_page: register page + * @_offset: register offset in page + * @_size: register byte size (1, 2, 4 or 6) + * + * All parameters should be constant. + */ +#define ZL_REG(_page, _offset, _size) \ + ZL_REG_IDX(0, _page, _offset, _size, 1, 0) + +/************************** + * Register Page 0, General + **************************/ + +#define ZL_REG_ID ZL_REG(0, 0x01, 2) +#define ZL_REG_REVISION ZL_REG(0, 0x03, 2) +#define ZL_REG_FW_VER ZL_REG(0, 0x05, 2) +#define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4) + +/************************* + * Register Page 2, Status + *************************/ + +#define ZL_REG_REF_MON_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1) +#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */ + +#define ZL_REG_DPLL_MON_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1) +#define ZL_DPLL_MON_STATUS_STATE GENMASK(1, 0) +#define ZL_DPLL_MON_STATUS_STATE_ACQUIRING 0 +#define ZL_DPLL_MON_STATUS_STATE_LOCK 1 +#define ZL_DPLL_MON_STATUS_STATE_HOLDOVER 2 +#define ZL_DPLL_MON_STATUS_HO_READY BIT(2) + +#define ZL_REG_DPLL_REFSEL_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x30, 1, ZL3073X_MAX_CHANNELS, 1) +#define ZL_DPLL_REFSEL_STATUS_REFSEL GENMASK(3, 0) +#define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4) +#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4 + +#define ZL_REG_REF_FREQ(_idx) \ + ZL_REG_IDX(_idx, 2, 0x44, 4, ZL3073X_NUM_REFS, 4) + +/********************** + * Register Page 4, Ref + **********************/ + +#define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1) +#define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0) + +#define ZL_REG_REF_FREQ_MEAS_CTRL ZL_REG(4, 0x1c, 1) +#define ZL_REF_FREQ_MEAS_CTRL GENMASK(1, 0) +#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ 1 +#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF 2 +#define ZL_REF_FREQ_MEAS_CTRL_DPLL_FREQ_OFF 3 + +#define ZL_REG_REF_FREQ_MEAS_MASK_3_0 ZL_REG(4, 0x1d, 1) +#define ZL_REF_FREQ_MEAS_MASK_3_0(_ref) BIT(_ref) + +#define ZL_REG_REF_FREQ_MEAS_MASK_4 ZL_REG(4, 0x1e, 1) +#define ZL_REF_FREQ_MEAS_MASK_4(_ref) BIT((_ref) - 8) + +#define ZL_REG_DPLL_MEAS_REF_FREQ_CTRL ZL_REG(4, 0x1f, 1) +#define ZL_DPLL_MEAS_REF_FREQ_CTRL_EN BIT(0) +#define ZL_DPLL_MEAS_REF_FREQ_CTRL_IDX GENMASK(6, 4) + +#define ZL_REG_REF_PHASE(_idx) \ + ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6) + +/*********************** + * Register Page 5, DPLL + ***********************/ + +#define ZL_REG_DPLL_MODE_REFSEL(_idx) \ + ZL_REG_IDX(_idx, 5, 0x04, 1, ZL3073X_MAX_CHANNELS, 4) +#define ZL_DPLL_MODE_REFSEL_MODE GENMASK(2, 0) +#define ZL_DPLL_MODE_REFSEL_MODE_FREERUN 0 +#define ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER 1 +#define ZL_DPLL_MODE_REFSEL_MODE_REFLOCK 2 +#define ZL_DPLL_MODE_REFSEL_MODE_AUTO 3 +#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4 +#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4) + +#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1) +#define ZL_DPLL_MEAS_CTRL_EN BIT(0) +#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4) + +#define ZL_REG_DPLL_MEAS_IDX ZL_REG(5, 0x51, 1) +#define ZL_DPLL_MEAS_IDX GENMASK(2, 0) + +#define ZL_REG_DPLL_PHASE_ERR_READ_MASK ZL_REG(5, 0x54, 1) + +#define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \ + ZL_REG_IDX(_idx, 5, 0x55, 6, ZL3073X_MAX_CHANNELS, 6) + +/*********************************** + * Register Page 9, Synth and Output + ***********************************/ + +#define ZL_REG_SYNTH_CTRL(_idx) \ + ZL_REG_IDX(_idx, 9, 0x00, 1, ZL3073X_NUM_SYNTHS, 1) +#define ZL_SYNTH_CTRL_EN BIT(0) +#define ZL_SYNTH_CTRL_DPLL_SEL GENMASK(6, 4) + +#define ZL_REG_SYNTH_PHASE_SHIFT_CTRL ZL_REG(9, 0x1e, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_MASK ZL_REG(9, 0x1f, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_INTVL ZL_REG(9, 0x20, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_DATA ZL_REG(9, 0x21, 2) + +#define ZL_REG_OUTPUT_CTRL(_idx) \ + ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1) +#define ZL_OUTPUT_CTRL_EN BIT(0) +#define ZL_OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4) + +/******************************* + * Register Page 10, Ref Mailbox + *******************************/ + +#define ZL_REG_REF_MB_MASK ZL_REG(10, 0x02, 2) + +#define ZL_REG_REF_MB_SEM ZL_REG(10, 0x04, 1) +#define ZL_REF_MB_SEM_WR BIT(0) +#define ZL_REF_MB_SEM_RD BIT(1) + +#define ZL_REG_REF_FREQ_BASE ZL_REG(10, 0x05, 2) +#define ZL_REG_REF_FREQ_MULT ZL_REG(10, 0x07, 2) +#define ZL_REG_REF_RATIO_M ZL_REG(10, 0x09, 2) +#define ZL_REG_REF_RATIO_N ZL_REG(10, 0x0b, 2) + +#define ZL_REG_REF_CONFIG ZL_REG(10, 0x0d, 1) +#define ZL_REF_CONFIG_ENABLE BIT(0) +#define ZL_REF_CONFIG_DIFF_EN BIT(2) + +#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6) + +#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1) +#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0) +#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0 +#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2 + +#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4) +#define ZL_REF_ESYNC_DIV_1HZ 0 + +/******************************** + * Register Page 12, DPLL Mailbox + ********************************/ + +#define ZL_REG_DPLL_MB_MASK ZL_REG(12, 0x02, 2) + +#define ZL_REG_DPLL_MB_SEM ZL_REG(12, 0x04, 1) +#define ZL_DPLL_MB_SEM_WR BIT(0) +#define ZL_DPLL_MB_SEM_RD BIT(1) + +#define ZL_REG_DPLL_REF_PRIO(_idx) \ + ZL_REG_IDX(_idx, 12, 0x52, 1, ZL3073X_NUM_REFS / 2, 1) +#define ZL_DPLL_REF_PRIO_REF_P GENMASK(3, 0) +#define ZL_DPLL_REF_PRIO_REF_N GENMASK(7, 4) +#define ZL_DPLL_REF_PRIO_MAX 14 +#define ZL_DPLL_REF_PRIO_NONE 15 + +/********************************* + * Register Page 13, Synth Mailbox + *********************************/ + +#define ZL_REG_SYNTH_MB_MASK ZL_REG(13, 0x02, 2) + +#define ZL_REG_SYNTH_MB_SEM ZL_REG(13, 0x04, 1) +#define ZL_SYNTH_MB_SEM_WR BIT(0) +#define ZL_SYNTH_MB_SEM_RD BIT(1) + +#define ZL_REG_SYNTH_FREQ_BASE ZL_REG(13, 0x06, 2) +#define ZL_REG_SYNTH_FREQ_MULT ZL_REG(13, 0x08, 4) +#define ZL_REG_SYNTH_FREQ_M ZL_REG(13, 0x0c, 2) +#define ZL_REG_SYNTH_FREQ_N ZL_REG(13, 0x0e, 2) + +/********************************** + * Register Page 14, Output Mailbox + **********************************/ +#define ZL_REG_OUTPUT_MB_MASK ZL_REG(14, 0x02, 2) + +#define ZL_REG_OUTPUT_MB_SEM ZL_REG(14, 0x04, 1) +#define ZL_OUTPUT_MB_SEM_WR BIT(0) +#define ZL_OUTPUT_MB_SEM_RD BIT(1) + +#define ZL_REG_OUTPUT_MODE ZL_REG(14, 0x05, 1) +#define ZL_OUTPUT_MODE_CLOCK_TYPE GENMASK(2, 0) +#define ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL 0 +#define ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC 1 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT GENMASK(7, 4) +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED 0 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS 1 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF 2 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM 3 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2 4 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P 5 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N 6 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_INV 7 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV 12 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV 15 + +#define ZL_REG_OUTPUT_DIV ZL_REG(14, 0x0c, 4) +#define ZL_REG_OUTPUT_WIDTH ZL_REG(14, 0x10, 4) +#define ZL_REG_OUTPUT_ESYNC_PERIOD ZL_REG(14, 0x14, 4) +#define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4) +#define ZL_REG_OUTPUT_PHASE_COMP ZL_REG(14, 0x20, 4) + +#endif /* _ZL3073X_REGS_H */ diff --git a/drivers/dpll/zl3073x/spi.c b/drivers/dpll/zl3073x/spi.c new file mode 100644 index 000000000000..af901b4d6dda --- /dev/null +++ b/drivers/dpll/zl3073x/spi.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "core.h" + +static int zl3073x_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct zl3073x_dev *zldev; + + zldev = zl3073x_devm_alloc(dev); + if (IS_ERR(zldev)) + return PTR_ERR(zldev); + + zldev->regmap = devm_regmap_init_spi(spi, &zl3073x_regmap_config); + if (IS_ERR(zldev->regmap)) + return dev_err_probe(dev, PTR_ERR(zldev->regmap), + "Failed to initialize regmap\n"); + + return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi)); +} + +static const struct spi_device_id zl3073x_spi_id[] = { + { + .name = "zl30731", + .driver_data = (kernel_ulong_t)&zl30731_chip_info + }, + { + .name = "zl30732", + .driver_data = (kernel_ulong_t)&zl30732_chip_info, + }, + { + .name = "zl30733", + .driver_data = (kernel_ulong_t)&zl30733_chip_info, + }, + { + .name = "zl30734", + .driver_data = (kernel_ulong_t)&zl30734_chip_info, + }, + { + .name = "zl30735", + .driver_data = (kernel_ulong_t)&zl30735_chip_info, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, zl3073x_spi_id); + +static const struct of_device_id zl3073x_spi_of_match[] = { + { .compatible = "microchip,zl30731", .data = &zl30731_chip_info }, + { .compatible = "microchip,zl30732", .data = &zl30732_chip_info }, + { .compatible = "microchip,zl30733", .data = &zl30733_chip_info }, + { .compatible = "microchip,zl30734", .data = &zl30734_chip_info }, + { .compatible = "microchip,zl30735", .data = &zl30735_chip_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match); + +static struct spi_driver zl3073x_spi_driver = { + .driver = { + .name = "zl3073x-spi", + .of_match_table = zl3073x_spi_of_match, + }, + .probe = zl3073x_spi_probe, + .id_table = zl3073x_spi_id, +}; +module_spi_driver(zl3073x_spi_driver); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x SPI driver"); +MODULE_IMPORT_NS("ZL3073X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index cae52c654a15..7685a8550d4b 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -128,7 +128,6 @@ static ssize_t altr_sdr_mc_err_inject_write(struct file *file, ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL); if (!ptemp) { - dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); edac_printk(KERN_ERR, EDAC_MC, "Inject: Buffer Allocation error\n"); return -ENOMEM; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 58b1482a0fbb..07f1e9dc1ca7 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1209,7 +1209,9 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) cs_mode |= CS_ODD_PRIMARY; - /* Asymmetric dual-rank DIMM support. */ + if (csrow_sec_enabled(2 * dimm, ctrl, pvt)) + cs_mode |= CS_EVEN_SECONDARY; + if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) cs_mode |= CS_ODD_SECONDARY; @@ -1230,12 +1232,13 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) return cs_mode; } -static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode, - int csrow_nr, int dimm) +static int calculate_cs_size(u32 mask, unsigned int cs_mode) { - u32 msb, weight, num_zero_bits; - u32 addr_mask_deinterleaved; - int size = 0; + int msb, weight, num_zero_bits; + u32 deinterleaved_mask; + + if (!mask) + return 0; /* * The number of zero bits in the mask is equal to the number of bits @@ -1248,19 +1251,30 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode, * without swapping with the most significant bit. This can be handled * by keeping the MSB where it is and ignoring the single zero bit. */ - msb = fls(addr_mask_orig) - 1; - weight = hweight_long(addr_mask_orig); + msb = fls(mask) - 1; + weight = hweight_long(mask); num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE); /* Take the number of zero bits off from the top of the mask. */ - addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); + deinterleaved_mask = GENMASK(msb - num_zero_bits, 1); + edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", deinterleaved_mask); + + return (deinterleaved_mask >> 2) + 1; +} + +static int __addr_mask_to_cs_size(u32 addr_mask, u32 addr_mask_sec, + unsigned int cs_mode, int csrow_nr, int dimm) +{ + int size; edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); - edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); - edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved); + edac_dbg(1, " Primary AddrMask: 0x%x\n", addr_mask); /* Register [31:1] = Address [39:9]. Size is in kBs here. */ - size = (addr_mask_deinterleaved >> 2) + 1; + size = calculate_cs_size(addr_mask, cs_mode); + + edac_dbg(1, " Secondary AddrMask: 0x%x\n", addr_mask_sec); + size += calculate_cs_size(addr_mask_sec, cs_mode); /* Return size in MBs. */ return size >> 10; @@ -1269,8 +1283,8 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode, static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, unsigned int cs_mode, int csrow_nr) { + u32 addr_mask = 0, addr_mask_sec = 0; int cs_mask_nr = csrow_nr; - u32 addr_mask_orig; int dimm, size = 0; /* No Chip Selects are enabled. */ @@ -1308,13 +1322,13 @@ static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, if (!pvt->flags.zn_regs_v2) cs_mask_nr >>= 1; - /* Asymmetric dual-rank DIMM support. */ - if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) - addr_mask_orig = pvt->csels[umc].csmasks_sec[cs_mask_nr]; - else - addr_mask_orig = pvt->csels[umc].csmasks[cs_mask_nr]; + if (cs_mode & (CS_EVEN_PRIMARY | CS_ODD_PRIMARY)) + addr_mask = pvt->csels[umc].csmasks[cs_mask_nr]; - return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, dimm); + if (cs_mode & (CS_EVEN_SECONDARY | CS_ODD_SECONDARY)) + addr_mask_sec = pvt->csels[umc].csmasks_sec[cs_mask_nr]; + + return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, dimm); } static void umc_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) @@ -3512,9 +3526,10 @@ static void gpu_get_err_info(struct mce *m, struct err_info *err) static int gpu_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, unsigned int cs_mode, int csrow_nr) { - u32 addr_mask_orig = pvt->csels[umc].csmasks[csrow_nr]; + u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr]; + u32 addr_mask_sec = pvt->csels[umc].csmasks_sec[csrow_nr]; - return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, csrow_nr >> 1); + return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, csrow_nr >> 1); } static void gpu_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) @@ -3879,6 +3894,7 @@ static int per_family_init(struct amd64_pvt *pvt) break; case 0x70 ... 0x7f: pvt->ctl_name = "F19h_M70h"; + pvt->max_mcs = 4; pvt->flags.zn_regs_v2 = 1; break; case 0x90 ... 0x9f: diff --git a/drivers/edac/ecs.c b/drivers/edac/ecs.c index 1d51838a60c1..51c451c7f0f0 100755 --- a/drivers/edac/ecs.c +++ b/drivers/edac/ecs.c @@ -170,8 +170,10 @@ static int ecs_create_desc(struct device *ecs_dev, const struct attribute_group fru_ctx->dev_attr[ECS_RESET] = EDAC_ECS_ATTR_WO(reset, fru); fru_ctx->dev_attr[ECS_THRESHOLD] = EDAC_ECS_ATTR_RW(threshold, fru); - for (i = 0; i < ECS_MAX_ATTRS; i++) + for (i = 0; i < ECS_MAX_ATTRS; i++) { + sysfs_attr_init(&fru_ctx->dev_attr[i].dev_attr.attr); fru_ctx->ecs_attrs[i] = &fru_ctx->dev_attr[i].dev_attr.attr; + } sprintf(fru_ctx->name, "%s%d", EDAC_ECS_FRU_NAME, fru); group->name = fru_ctx->name; diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index a3fca2567752..bf4171ac191d 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -62,6 +62,7 @@ ((GET_BITFIELD(reg, 0, 10) << 12) + 0x140000) #define I10NM_GNR_IMC_MMIO_OFFSET 0x24c000 +#define I10NM_GNR_D_IMC_MMIO_OFFSET 0x206000 #define I10NM_GNR_IMC_MMIO_SIZE 0x4000 #define I10NM_HBM_IMC_MMIO_SIZE 0x9000 #define I10NM_DDR_IMC_CH_CNT(reg) GET_BITFIELD(reg, 21, 24) @@ -343,7 +344,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask; - n = snprintf(msg, len, " retry_rd_err_log["); + n = scnprintf(msg, len, " retry_rd_err_log["); for (i = 0; i < rrl->set_num; i++) { scrub = (rrl->modes[i] == FRE_SCRUB || rrl->modes[i] == LRE_SCRUB); if (scrub_err != scrub) @@ -355,9 +356,9 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, log = read_imc_reg(imc, ch, offset, width); if (width == 4) - n += snprintf(msg + n, len - n, "%.8llx ", log); + n += scnprintf(msg + n, len - n, "%.8llx ", log); else - n += snprintf(msg + n, len - n, "%.16llx ", log); + n += scnprintf(msg + n, len - n, "%.16llx ", log); /* Clear RRL status if RRL in Linux control mode. */ if (retry_rd_err_log == 2 && !j && (log & status_mask)) @@ -367,10 +368,10 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, /* Move back one space. */ n--; - n += snprintf(msg + n, len - n, "]"); + n += scnprintf(msg + n, len - n, "]"); if (len - n > 0) { - n += snprintf(msg + n, len - n, " correrrcnt["); + n += scnprintf(msg + n, len - n, " correrrcnt["); for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) { offset = rrl->cecnt_offsets[i]; width = rrl->cecnt_widths[i]; @@ -378,20 +379,20 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, /* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */ if (res_cfg->type <= SPR) { - n += snprintf(msg + n, len - n, "%.4llx %.4llx ", + n += scnprintf(msg + n, len - n, "%.4llx %.4llx ", corr & 0xffff, corr >> 16); } else { /* CPUs {GNR} encode one counter per CORRERRCNT register. */ if (width == 4) - n += snprintf(msg + n, len - n, "%.8llx ", corr); + n += scnprintf(msg + n, len - n, "%.8llx ", corr); else - n += snprintf(msg + n, len - n, "%.16llx ", corr); + n += scnprintf(msg + n, len - n, "%.16llx ", corr); } } /* Move back one space. */ n--; - n += snprintf(msg + n, len - n, "]"); + n += scnprintf(msg + n, len - n, "]"); } } @@ -687,6 +688,14 @@ static struct pci_dev *get_gnr_mdev(struct skx_dev *d, int logical_idx, int *phy return NULL; } +static u32 get_gnr_imc_mmio_offset(void) +{ + if (boot_cpu_data.x86_vfm == INTEL_GRANITERAPIDS_D) + return I10NM_GNR_D_IMC_MMIO_OFFSET; + + return I10NM_GNR_IMC_MMIO_OFFSET; +} + /** * get_ddr_munit() - Get the resource of the i-th DDR memory controller. * @@ -715,7 +724,7 @@ static struct pci_dev *get_ddr_munit(struct skx_dev *d, int i, u32 *offset, unsi return NULL; *offset = I10NM_GET_IMC_MMIO_OFFSET(reg) + - I10NM_GNR_IMC_MMIO_OFFSET + + get_gnr_imc_mmio_offset() + physical_idx * I10NM_GNR_IMC_MMIO_SIZE; *size = I10NM_GNR_IMC_MMIO_SIZE; @@ -1030,6 +1039,7 @@ static const struct x86_cpu_id i10nm_cpuids[] = { X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, &spr_cfg), X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &spr_cfg), X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, &gnr_cfg), + X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, &gnr_cfg), X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &gnr_cfg), X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &gnr_cfg), X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &gnr_cfg), diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c index a53612be4b2f..5c1fa1c0d12e 100644 --- a/drivers/edac/ie31200_edac.c +++ b/drivers/edac/ie31200_edac.c @@ -87,14 +87,31 @@ #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10 0x3eca /* Raptor Lake-S */ -#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1 0xa703 -#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2 0x4640 -#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3 0x4630 -#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4 0xa700 +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1 0xa703 /* 8P+8E, e.g. i7-13700 */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2 0x4640 /* 6P+8E, e.g. i5-13500, i5-13600, i5-14500 */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3 0x4630 /* 4P+0E, e.g. i3-13100E */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4 0xa700 /* 8P+16E, e.g. i9-13900, i9-14900 */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_5 0xa740 /* 8P+12E, e.g. i7-14700 */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_6 0xa704 /* 6P+8E, e.g. i5-14600 */ + +/* Raptor Lake-HX */ +#define PCI_DEVICE_ID_INTEL_IE31200_RPL_HX_1 0xa702 /* 8P+16E, e.g. i9-13950HX */ /* Alder Lake-S */ #define PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1 0x4660 +/* Bartlett Lake-S */ +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_1 0x4639 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_2 0x463c +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_3 0x4642 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_4 0x4643 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_5 0xa731 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_6 0xa732 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_7 0xa733 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_8 0xa741 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_9 0xa744 +#define PCI_DEVICE_ID_INTEL_IE31200_BTL_S_10 0xa745 + #define IE31200_RANKS_PER_CHANNEL 8 #define IE31200_DIMMS_PER_CHANNEL 2 #define IE31200_CHANNELS 2 @@ -740,7 +757,20 @@ static const struct pci_device_id ie31200_pci_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2), (kernel_ulong_t)&rpl_s_cfg}, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3), (kernel_ulong_t)&rpl_s_cfg}, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_5), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_6), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_HX_1), (kernel_ulong_t)&rpl_s_cfg}, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_1), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_2), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_3), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_4), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_5), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_6), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_7), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_8), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_9), (kernel_ulong_t)&rpl_s_cfg}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_10), (kernel_ulong_t)&rpl_s_cfg}, { 0, } /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index 1930dc00c791..2fc59f9eed69 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -125,7 +125,7 @@ #define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) #define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) -static const struct res_config { +static struct res_config { bool machine_check; /* The number of present memory controllers. */ int num_imc; @@ -275,6 +275,9 @@ static struct work_struct ecclog_work; #define DID_PTL_H_SKU2 0xb001 #define DID_PTL_H_SKU3 0xb002 +/* Compute die IDs for Wildcat Lake with IBECC */ +#define DID_WCL_SKU1 0xfd00 + static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) { union { @@ -479,7 +482,7 @@ static u64 rpl_p_err_addr(u64 ecclog) return ECC_ERROR_LOG_ADDR45(ecclog); } -static const struct res_config ehl_cfg = { +static struct res_config ehl_cfg = { .num_imc = 1, .imc_base = 0x5000, .ibecc_base = 0xdc00, @@ -489,7 +492,7 @@ static const struct res_config ehl_cfg = { .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, }; -static const struct res_config icl_cfg = { +static struct res_config icl_cfg = { .num_imc = 1, .imc_base = 0x5000, .ibecc_base = 0xd800, @@ -499,7 +502,7 @@ static const struct res_config icl_cfg = { .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, }; -static const struct res_config tgl_cfg = { +static struct res_config tgl_cfg = { .machine_check = true, .num_imc = 2, .imc_base = 0x5000, @@ -513,7 +516,7 @@ static const struct res_config tgl_cfg = { .err_addr_to_imc_addr = tgl_err_addr_to_imc_addr, }; -static const struct res_config adl_cfg = { +static struct res_config adl_cfg = { .machine_check = true, .num_imc = 2, .imc_base = 0xd800, @@ -524,7 +527,7 @@ static const struct res_config adl_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static const struct res_config adl_n_cfg = { +static struct res_config adl_n_cfg = { .machine_check = true, .num_imc = 1, .imc_base = 0xd800, @@ -535,7 +538,7 @@ static const struct res_config adl_n_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static const struct res_config rpl_p_cfg = { +static struct res_config rpl_p_cfg = { .machine_check = true, .num_imc = 2, .imc_base = 0xd800, @@ -547,7 +550,7 @@ static const struct res_config rpl_p_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static const struct res_config mtl_ps_cfg = { +static struct res_config mtl_ps_cfg = { .machine_check = true, .num_imc = 2, .imc_base = 0xd800, @@ -558,7 +561,7 @@ static const struct res_config mtl_ps_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static const struct res_config mtl_p_cfg = { +static struct res_config mtl_p_cfg = { .machine_check = true, .num_imc = 2, .imc_base = 0xd800, @@ -569,7 +572,18 @@ static const struct res_config mtl_p_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static const struct pci_device_id igen6_pci_tbl[] = { +static struct res_config wcl_cfg = { + .machine_check = true, + .num_imc = 1, + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, + .ibecc_available = mtl_p_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, +}; + +static struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_EHL_SKU5), (kernel_ulong_t)&ehl_cfg }, { PCI_VDEVICE(INTEL, DID_EHL_SKU6), (kernel_ulong_t)&ehl_cfg }, { PCI_VDEVICE(INTEL, DID_EHL_SKU7), (kernel_ulong_t)&ehl_cfg }, @@ -622,6 +636,7 @@ static const struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, { }, }; MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); @@ -1350,9 +1365,11 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar) return -ENODEV; } - if (lmc < res_cfg->num_imc) - igen6_printk(KERN_WARNING, "Expected %d mcs, but only %d detected.", + if (lmc < res_cfg->num_imc) { + igen6_printk(KERN_DEBUG, "Expected %d mcs, but only %d detected.", res_cfg->num_imc, lmc); + res_cfg->num_imc = lmc; + } return 0; diff --git a/drivers/edac/mem_repair.c b/drivers/edac/mem_repair.c index d1a8caa85369..108d69209146 100755 --- a/drivers/edac/mem_repair.c +++ b/drivers/edac/mem_repair.c @@ -286,17 +286,26 @@ static umode_t mem_repair_attr_visible(struct kobject *kobj, struct attribute *a return 0; } -#define MR_ATTR_RO(_name, _instance) \ - ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_RO(_name), \ - .instance = _instance }) - -#define MR_ATTR_WO(_name, _instance) \ - ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_WO(_name), \ - .instance = _instance }) - -#define MR_ATTR_RW(_name, _instance) \ - ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_RW(_name), \ - .instance = _instance }) +static const struct device_attribute mem_repair_dev_attr[] = { + [MR_TYPE] = __ATTR_RO(repair_type), + [MR_PERSIST_MODE] = __ATTR_RW(persist_mode), + [MR_SAFE_IN_USE] = __ATTR_RO(repair_safe_when_in_use), + [MR_HPA] = __ATTR_RW(hpa), + [MR_MIN_HPA] = __ATTR_RO(min_hpa), + [MR_MAX_HPA] = __ATTR_RO(max_hpa), + [MR_DPA] = __ATTR_RW(dpa), + [MR_MIN_DPA] = __ATTR_RO(min_dpa), + [MR_MAX_DPA] = __ATTR_RO(max_dpa), + [MR_NIBBLE_MASK] = __ATTR_RW(nibble_mask), + [MR_BANK_GROUP] = __ATTR_RW(bank_group), + [MR_BANK] = __ATTR_RW(bank), + [MR_RANK] = __ATTR_RW(rank), + [MR_ROW] = __ATTR_RW(row), + [MR_COLUMN] = __ATTR_RW(column), + [MR_CHANNEL] = __ATTR_RW(channel), + [MR_SUB_CHANNEL] = __ATTR_RW(sub_channel), + [MEM_DO_REPAIR] = __ATTR_WO(repair) +}; static int mem_repair_create_desc(struct device *dev, const struct attribute_group **attr_groups, @@ -305,34 +314,14 @@ static int mem_repair_create_desc(struct device *dev, struct edac_mem_repair_context *ctx; struct attribute_group *group; int i; - struct edac_mem_repair_dev_attr dev_attr[] = { - [MR_TYPE] = MR_ATTR_RO(repair_type, instance), - [MR_PERSIST_MODE] = MR_ATTR_RW(persist_mode, instance), - [MR_SAFE_IN_USE] = MR_ATTR_RO(repair_safe_when_in_use, instance), - [MR_HPA] = MR_ATTR_RW(hpa, instance), - [MR_MIN_HPA] = MR_ATTR_RO(min_hpa, instance), - [MR_MAX_HPA] = MR_ATTR_RO(max_hpa, instance), - [MR_DPA] = MR_ATTR_RW(dpa, instance), - [MR_MIN_DPA] = MR_ATTR_RO(min_dpa, instance), - [MR_MAX_DPA] = MR_ATTR_RO(max_dpa, instance), - [MR_NIBBLE_MASK] = MR_ATTR_RW(nibble_mask, instance), - [MR_BANK_GROUP] = MR_ATTR_RW(bank_group, instance), - [MR_BANK] = MR_ATTR_RW(bank, instance), - [MR_RANK] = MR_ATTR_RW(rank, instance), - [MR_ROW] = MR_ATTR_RW(row, instance), - [MR_COLUMN] = MR_ATTR_RW(column, instance), - [MR_CHANNEL] = MR_ATTR_RW(channel, instance), - [MR_SUB_CHANNEL] = MR_ATTR_RW(sub_channel, instance), - [MEM_DO_REPAIR] = MR_ATTR_WO(repair, instance) - }; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; for (i = 0; i < MR_MAX_ATTRS; i++) { - memcpy(&ctx->mem_repair_dev_attr[i], - &dev_attr[i], sizeof(dev_attr[i])); + ctx->mem_repair_dev_attr[i].dev_attr = mem_repair_dev_attr[i]; + ctx->mem_repair_dev_attr[i].instance = instance; + sysfs_attr_init(&ctx->mem_repair_dev_attr[i].dev_attr.attr); ctx->mem_repair_attrs[i] = &ctx->mem_repair_dev_attr[i].dev_attr.attr; } diff --git a/drivers/edac/scrub.c b/drivers/edac/scrub.c index e421d3ebd959..f9d02af2fc3a 100755 --- a/drivers/edac/scrub.c +++ b/drivers/edac/scrub.c @@ -176,6 +176,7 @@ static int scrub_create_desc(struct device *scrub_dev, group = &scrub_ctx->group; for (i = 0; i < SCRUB_MAX_ATTRS; i++) { memcpy(&scrub_ctx->scrub_dev_attr[i], &dev_attr[i], sizeof(dev_attr[i])); + sysfs_attr_init(&scrub_ctx->scrub_dev_attr[i].dev_attr.attr); scrub_ctx->scrub_attrs[i] = &scrub_ctx->scrub_dev_attr[i].dev_attr.attr; } sprintf(scrub_ctx->name, "%s%d", "scrub", instance); diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index c9ade45c1a99..39c733dbc5b9 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -670,12 +670,12 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, } if (res->decoded_by_adxl) { - len = snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", + len = scnprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", overflow ? " OVERFLOW" : "", (uncorrected_error && recoverable) ? " recoverable" : "", mscod, errcode, adxl_msg); } else { - len = snprintf(skx_msg, MSG_SIZE, + len = scnprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x ProcessorSocketId:0x%x MemoryControllerId:0x%x PhysicalRankId:0x%x Row:0x%x Column:0x%x Bank:0x%x BankGroup:0x%x", overflow ? " OVERFLOW" : "", (uncorrected_error && recoverable) ? " recoverable" : "", diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 5ed32a3299c4..51143b3257de 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -332,20 +332,26 @@ struct synps_edac_priv { #endif }; +enum synps_platform_type { + ZYNQ, + ZYNQMP, + SYNPS, +}; + /** * struct synps_platform_data - synps platform data structure. + * @platform: Identifies the target hardware platform * @get_error_info: Get EDAC error info. * @get_mtype: Get mtype. * @get_dtype: Get dtype. - * @get_ecc_state: Get ECC state. * @get_mem_info: Get EDAC memory info * @quirks: To differentiate IPs. */ struct synps_platform_data { + enum synps_platform_type platform; int (*get_error_info)(struct synps_edac_priv *priv); enum mem_type (*get_mtype)(const void __iomem *base); enum dev_type (*get_dtype)(const void __iomem *base); - bool (*get_ecc_state)(void __iomem *base); #ifdef CONFIG_EDAC_DEBUG u64 (*get_mem_info)(struct synps_edac_priv *priv); #endif @@ -720,51 +726,38 @@ static enum dev_type zynqmp_get_dtype(const void __iomem *base) return dt; } -/** - * zynq_get_ecc_state - Return the controller ECC enable/disable status. - * @base: DDR memory controller base address. - * - * Get the ECC enable/disable status of the controller. - * - * Return: true if enabled, otherwise false. - */ -static bool zynq_get_ecc_state(void __iomem *base) +static bool get_ecc_state(struct synps_edac_priv *priv) { + u32 ecctype, clearval; enum dev_type dt; - u32 ecctype; - dt = zynq_get_dtype(base); - if (dt == DEV_UNKNOWN) - return false; + if (priv->p_data->platform == ZYNQ) { + dt = zynq_get_dtype(priv->baseaddr); + if (dt == DEV_UNKNOWN) + return false; - ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; - if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) - return true; + ecctype = readl(priv->baseaddr + SCRUB_OFST) & SCRUB_MODE_MASK; + if (ecctype == SCRUB_MODE_SECDED && dt == DEV_X2) { + clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_UE_ERR; + writel(clearval, priv->baseaddr + ECC_CTRL_OFST); + writel(0x0, priv->baseaddr + ECC_CTRL_OFST); + return true; + } + } else { + dt = zynqmp_get_dtype(priv->baseaddr); + if (dt == DEV_UNKNOWN) + return false; - return false; -} - -/** - * zynqmp_get_ecc_state - Return the controller ECC enable/disable status. - * @base: DDR memory controller base address. - * - * Get the ECC enable/disable status for the controller. - * - * Return: a ECC status boolean i.e true/false - enabled/disabled. - */ -static bool zynqmp_get_ecc_state(void __iomem *base) -{ - enum dev_type dt; - u32 ecctype; - - dt = zynqmp_get_dtype(base); - if (dt == DEV_UNKNOWN) - return false; - - ecctype = readl(base + ECC_CFG0_OFST) & SCRUB_MODE_MASK; - if ((ecctype == SCRUB_MODE_SECDED) && - ((dt == DEV_X2) || (dt == DEV_X4) || (dt == DEV_X8))) - return true; + ecctype = readl(priv->baseaddr + ECC_CFG0_OFST) & SCRUB_MODE_MASK; + if (ecctype == SCRUB_MODE_SECDED && + (dt == DEV_X2 || dt == DEV_X4 || dt == DEV_X8)) { + clearval = readl(priv->baseaddr + ECC_CLR_OFST) | + ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT | + ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; + writel(clearval, priv->baseaddr + ECC_CLR_OFST); + return true; + } + } return false; } @@ -934,18 +927,18 @@ static int setup_irq(struct mem_ctl_info *mci, } static const struct synps_platform_data zynq_edac_def = { + .platform = ZYNQ, .get_error_info = zynq_get_error_info, .get_mtype = zynq_get_mtype, .get_dtype = zynq_get_dtype, - .get_ecc_state = zynq_get_ecc_state, .quirks = 0, }; static const struct synps_platform_data zynqmp_edac_def = { + .platform = ZYNQMP, .get_error_info = zynqmp_get_error_info, .get_mtype = zynqmp_get_mtype, .get_dtype = zynqmp_get_dtype, - .get_ecc_state = zynqmp_get_ecc_state, #ifdef CONFIG_EDAC_DEBUG .get_mem_info = zynqmp_get_mem_info, #endif @@ -957,10 +950,10 @@ static const struct synps_platform_data zynqmp_edac_def = { }; static const struct synps_platform_data synopsys_edac_def = { + .platform = SYNPS, .get_error_info = zynqmp_get_error_info, .get_mtype = zynqmp_get_mtype, .get_dtype = zynqmp_get_dtype, - .get_ecc_state = zynqmp_get_ecc_state, .quirks = (DDR_ECC_INTR_SUPPORT | DDR_ECC_INTR_SELF_CLEAR #ifdef CONFIG_EDAC_DEBUG | DDR_ECC_DATA_POISON_SUPPORT @@ -1390,10 +1383,6 @@ static int mc_probe(struct platform_device *pdev) if (!p_data) return -ENODEV; - if (!p_data->get_ecc_state(baseaddr)) { - edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); - return -ENXIO; - } layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; layers[0].size = SYNPS_EDAC_NR_CSROWS; @@ -1413,6 +1402,12 @@ static int mc_probe(struct platform_device *pdev) priv = mci->pvt_info; priv->baseaddr = baseaddr; priv->p_data = p_data; + if (!get_ecc_state(priv)) { + edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); + rc = -ENODEV; + goto free_edac_mc; + } + spin_lock_init(&priv->reglock); mc_init(mci, pdev); diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 01354b9de8b2..aae774e7a5c3 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -237,7 +237,7 @@ EXPORT_SYMBOL(fw_schedule_bus_reset); static void br_work(struct work_struct *work) { - struct fw_card *card = container_of(work, struct fw_card, br_work.work); + struct fw_card *card = from_work(card, work, br_work.work); /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */ if (card->reset_jiffies != 0 && @@ -273,10 +273,6 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) fw_device_set_broadcast_channel); } -static const char gap_count_table[] = { - 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 -}; - void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) { fw_card_get(card); @@ -286,7 +282,10 @@ void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) static void bm_work(struct work_struct *work) { - struct fw_card *card = container_of(work, struct fw_card, bm_work.work); + static const char gap_count_table[] = { + 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 + }; + struct fw_card *card = from_work(card, work, bm_work.work); struct fw_device *root_device, *irm_device; struct fw_node *root_node; int root_id, new_root_id, irm_id, bm_id, local_id; @@ -574,7 +573,6 @@ EXPORT_SYMBOL(fw_card_initialize); int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid, unsigned int supported_isoc_contexts) { - struct workqueue_struct *isoc_wq; int ret; // This workqueue should be: @@ -589,29 +587,48 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid, // * == WQ_SYSFS Parameters are available via sysfs. // * max_active == n_it + n_ir A hardIRQ could notify events for multiple isochronous // contexts if they are scheduled to the same cycle. - isoc_wq = alloc_workqueue("firewire-isoc-card%u", - WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS, - supported_isoc_contexts, card->index); - if (!isoc_wq) + card->isoc_wq = alloc_workqueue("firewire-isoc-card%u", + WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS, + supported_isoc_contexts, card->index); + if (!card->isoc_wq) return -ENOMEM; + // This workqueue should be: + // * != WQ_BH Sleepable. + // * == WQ_UNBOUND Any core can process data for asynchronous context. + // * == WQ_MEM_RECLAIM Used for any backend of block device. + // * == WQ_FREEZABLE The target device would not be available when being freezed. + // * == WQ_HIGHPRI High priority to process semi-realtime timestamped data. + // * == WQ_SYSFS Parameters are available via sysfs. + // * max_active == 4 A hardIRQ could notify events for a pair of requests and + // response AR/AT contexts. + card->async_wq = alloc_workqueue("firewire-async-card%u", + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS, + 4, card->index); + if (!card->async_wq) { + ret = -ENOMEM; + goto err_isoc; + } + card->max_receive = max_receive; card->link_speed = link_speed; card->guid = guid; - guard(mutex)(&card_mutex); + scoped_guard(mutex, &card_mutex) { + generate_config_rom(card, tmp_config_rom); + ret = card->driver->enable(card, tmp_config_rom, config_rom_length); + if (ret < 0) + goto err_async; - generate_config_rom(card, tmp_config_rom); - ret = card->driver->enable(card, tmp_config_rom, config_rom_length); - if (ret < 0) { - destroy_workqueue(isoc_wq); - return ret; + list_add_tail(&card->link, &card_list); } - card->isoc_wq = isoc_wq; - list_add_tail(&card->link, &card_list); - return 0; +err_async: + destroy_workqueue(card->async_wq); +err_isoc: + destroy_workqueue(card->isoc_wq); + return ret; } EXPORT_SYMBOL(fw_card_add); @@ -744,6 +761,7 @@ void fw_core_remove_card(struct fw_card *card) dummy_driver.stop_iso = card->driver->stop_iso; card->driver = &dummy_driver; drain_workqueue(card->isoc_wq); + drain_workqueue(card->async_wq); scoped_guard(spinlock_irqsave, &card->lock) fw_destroy_nodes(card); @@ -753,6 +771,7 @@ void fw_core_remove_card(struct fw_card *card) wait_for_completion(&card->done); destroy_workqueue(card->isoc_wq); + destroy_workqueue(card->async_wq); WARN_ON(!list_empty(&card->transaction_list)); } diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index bd04980009a4..78b10c6ef7fe 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1313,8 +1313,7 @@ static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) static void iso_resource_work(struct work_struct *work) { struct iso_resource_event *e; - struct iso_resource *r = - container_of(work, struct iso_resource, work.work); + struct iso_resource *r = from_work(r, work, work.work); struct client *client = r->client; unsigned long index = r->resource.handle; int generation, channel, bandwidth, todo; diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index ec3e21ad2025..aeacd4cfd694 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -853,8 +853,7 @@ static void fw_schedule_device_work(struct fw_device *device, static void fw_device_shutdown(struct work_struct *work) { - struct fw_device *device = - container_of(work, struct fw_device, work.work); + struct fw_device *device = from_work(device, work, work.work); if (time_before64(get_jiffies_64(), device->card->reset_jiffies + SHUTDOWN_DELAY) @@ -921,8 +920,7 @@ static int update_unit(struct device *dev, void *data) static void fw_device_update(struct work_struct *work) { - struct fw_device *device = - container_of(work, struct fw_device, work.work); + struct fw_device *device = from_work(device, work, work.work); fw_device_cdev_update(device); device_for_each_child(&device->device, NULL, update_unit); @@ -1002,8 +1000,7 @@ static int compare_configuration_rom(struct device *dev, const void *data) static void fw_device_init(struct work_struct *work) { - struct fw_device *device = - container_of(work, struct fw_device, work.work); + struct fw_device *device = from_work(device, work, work.work); struct fw_card *card = device->card; struct device *found; u32 minor; @@ -1184,8 +1181,7 @@ static int reread_config_rom(struct fw_device *device, int generation, static void fw_device_refresh(struct work_struct *work) { - struct fw_device *device = - container_of(work, struct fw_device, work.work); + struct fw_device *device = from_work(device, work, work.work); struct fw_card *card = device->card; int ret, node_id = device->node_id; bool changed; @@ -1251,8 +1247,7 @@ static void fw_device_refresh(struct work_struct *work) static void fw_device_workfn(struct work_struct *work) { - struct fw_device *device = container_of(to_delayed_work(work), - struct fw_device, work); + struct fw_device *device = from_work(device, to_delayed_work(work), work); device->workfn(work); } diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 2bd5deb9054e..1d1c2d8f85ae 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -550,6 +550,23 @@ const struct fw_address_region fw_unit_space_region = { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; #endif /* 0 */ +static void complete_address_handler(struct kref *kref) +{ + struct fw_address_handler *handler = container_of(kref, struct fw_address_handler, kref); + + complete(&handler->done); +} + +static void get_address_handler(struct fw_address_handler *handler) +{ + kref_get(&handler->kref); +} + +static int put_address_handler(struct fw_address_handler *handler) +{ + return kref_put(&handler->kref, complete_address_handler); +} + /** * fw_core_add_address_handler() - register for incoming requests * @handler: callback @@ -557,9 +574,10 @@ const struct fw_address_region fw_unit_space_region = * * region->start, ->end, and handler->length have to be quadlet-aligned. * - * When a request is received that falls within the specified address range, - * the specified callback is invoked. The parameters passed to the callback - * give the details of the particular request. + * When a request is received that falls within the specified address range, the specified callback + * is invoked. The parameters passed to the callback give the details of the particular request. + * The callback is invoked in the workqueue context in most cases. However, if the request is + * initiated by the local node, the callback is invoked in the initiator's context. * * To be called in process context. * Return value: 0 on success, non-zero otherwise. @@ -595,6 +613,8 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, if (other != NULL) { handler->offset += other->length; } else { + init_completion(&handler->done); + kref_init(&handler->kref); list_add_tail_rcu(&handler->link, &address_handler_list); ret = 0; break; @@ -620,6 +640,9 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler) list_del_rcu(&handler->link); synchronize_rcu(); + + if (!put_address_handler(handler)) + wait_for_completion(&handler->done); } EXPORT_SYMBOL(fw_core_remove_address_handler); @@ -913,22 +936,31 @@ static void handle_exclusive_region_request(struct fw_card *card, handler = lookup_enclosing_address_handler(&address_handler_list, offset, request->length); if (handler) - handler->address_callback(card, request, tcode, destination, source, - p->generation, offset, request->data, - request->length, handler->callback_data); + get_address_handler(handler); } - if (!handler) + if (!handler) { fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + // Outside the RCU read-side critical section. Without spinlock. With reference count. + handler->address_callback(card, request, tcode, destination, source, p->generation, offset, + request->data, request->length, handler->callback_data); + put_address_handler(handler); } +// To use kmalloc allocator efficiently, this should be power of two. +#define BUFFER_ON_KERNEL_STACK_SIZE 4 + static void handle_fcp_region_request(struct fw_card *card, struct fw_packet *p, struct fw_request *request, unsigned long long offset) { - struct fw_address_handler *handler; - int tcode, destination, source; + struct fw_address_handler *buffer_on_kernel_stack[BUFFER_ON_KERNEL_STACK_SIZE]; + struct fw_address_handler *handler, **handlers; + int tcode, destination, source, i, count, buffer_size; if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) || @@ -949,15 +981,55 @@ static void handle_fcp_region_request(struct fw_card *card, return; } + count = 0; + handlers = buffer_on_kernel_stack; + buffer_size = ARRAY_SIZE(buffer_on_kernel_stack); scoped_guard(rcu) { list_for_each_entry_rcu(handler, &address_handler_list, link) { - if (is_enclosing_handler(handler, offset, request->length)) - handler->address_callback(card, request, tcode, destination, source, - p->generation, offset, request->data, - request->length, handler->callback_data); + if (is_enclosing_handler(handler, offset, request->length)) { + if (count >= buffer_size) { + int next_size = buffer_size * 2; + struct fw_address_handler **buffer_on_kernel_heap; + + if (handlers == buffer_on_kernel_stack) + buffer_on_kernel_heap = NULL; + else + buffer_on_kernel_heap = handlers; + + buffer_on_kernel_heap = + krealloc_array(buffer_on_kernel_heap, next_size, + sizeof(*buffer_on_kernel_heap), GFP_ATOMIC); + // FCP is used for purposes unrelated to significant system + // resources (e.g. storage or networking), so allocation + // failures are not considered so critical. + if (!buffer_on_kernel_heap) + break; + + if (handlers == buffer_on_kernel_stack) { + memcpy(buffer_on_kernel_heap, buffer_on_kernel_stack, + sizeof(buffer_on_kernel_stack)); + } + + handlers = buffer_on_kernel_heap; + buffer_size = next_size; + } + get_address_handler(handler); + handlers[count++] = handler; + } } } + for (i = 0; i < count; ++i) { + handler = handlers[i]; + handler->address_callback(card, request, tcode, destination, source, + p->generation, offset, request->data, + request->length, handler->callback_data); + put_address_handler(handler); + } + + if (handlers != buffer_on_kernel_stack) + kfree(handlers); + fw_send_response(card, request, RCODE_COMPLETE); } diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 1bf0e15c1540..6d6446713539 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1007,7 +1007,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask) spin_lock_irqsave(&dev->lock, flags); - /* If the AT tasklet already ran, we may be last user. */ + /* If the AT work item already ran, we may be last user. */ free = (ptask->outstanding_pkts == 0 && !ptask->enqueued); if (!free) ptask->enqueued = true; @@ -1026,7 +1026,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask) spin_lock_irqsave(&dev->lock, flags); - /* If the AT tasklet already ran, we may be last user. */ + /* If the AT work item already ran, we may be last user. */ free = (ptask->outstanding_pkts == 0 && !ptask->enqueued); if (!free) ptask->enqueued = true; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index edaedd156a6d..5d8301b0f3aa 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -101,7 +101,7 @@ struct ar_context { void *pointer; unsigned int last_buffer_index; u32 regs; - struct tasklet_struct tasklet; + struct work_struct work; }; struct context; @@ -128,7 +128,6 @@ struct context { int total_allocation; u32 current_bus; bool running; - bool flushing; /* * List of page-sized buffers for storing DMA descriptors. @@ -157,8 +156,12 @@ struct context { int prev_z; descriptor_callback_t callback; +}; - struct tasklet_struct tasklet; +struct at_context { + struct context context; + struct work_struct work; + bool flushing; }; struct iso_context { @@ -204,8 +207,8 @@ struct fw_ohci { struct ar_context ar_request_ctx; struct ar_context ar_response_ctx; - struct context at_request_ctx; - struct context at_response_ctx; + struct at_context at_request_ctx; + struct at_context at_response_ctx; u32 it_context_support; u32 it_context_mask; /* unoccupied IT contexts */ @@ -1016,9 +1019,9 @@ static void ar_recycle_buffers(struct ar_context *ctx, unsigned int end_buffer) } } -static void ar_context_tasklet(unsigned long data) +static void ohci_ar_context_work(struct work_struct *work) { - struct ar_context *ctx = (struct ar_context *)data; + struct ar_context *ctx = from_work(ctx, work, work); unsigned int end_buffer_index, end_buffer_offset; void *p, *end; @@ -1026,23 +1029,19 @@ static void ar_context_tasklet(unsigned long data) if (!p) return; - end_buffer_index = ar_search_last_active_buffer(ctx, - &end_buffer_offset); + end_buffer_index = ar_search_last_active_buffer(ctx, &end_buffer_offset); ar_sync_buffers_for_cpu(ctx, end_buffer_index, end_buffer_offset); end = ctx->buffer + end_buffer_index * PAGE_SIZE + end_buffer_offset; if (end_buffer_index < ar_first_buffer_index(ctx)) { - /* - * The filled part of the overall buffer wraps around; handle - * all packets up to the buffer end here. If the last packet - * wraps around, its tail will be visible after the buffer end - * because the buffer start pages are mapped there again. - */ + // The filled part of the overall buffer wraps around; handle all packets up to the + // buffer end here. If the last packet wraps around, its tail will be visible after + // the buffer end because the buffer start pages are mapped there again. void *buffer_end = ctx->buffer + AR_BUFFERS * PAGE_SIZE; p = handle_ar_packets(ctx, p, buffer_end); if (p < buffer_end) goto error; - /* adjust p to point back into the actual buffer */ + // adjust p to point back into the actual buffer p -= AR_BUFFERS * PAGE_SIZE; } @@ -1057,7 +1056,6 @@ static void ar_context_tasklet(unsigned long data) ar_recycle_buffers(ctx, end_buffer_index); return; - error: ctx->pointer = NULL; } @@ -1073,7 +1071,7 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, ctx->regs = regs; ctx->ohci = ohci; - tasklet_init(&ctx->tasklet, ar_context_tasklet, (unsigned long)ctx); + INIT_WORK(&ctx->work, ohci_ar_context_work); for (i = 0; i < AR_BUFFERS; i++) { ctx->pages[i] = dma_alloc_pages(dev, PAGE_SIZE, &dma_addr, @@ -1181,16 +1179,16 @@ static void context_retire_descriptors(struct context *ctx) } } -static void context_tasklet(unsigned long data) +static void ohci_at_context_work(struct work_struct *work) { - struct context *ctx = (struct context *) data; + struct at_context *ctx = from_work(ctx, work, work); - context_retire_descriptors(ctx); + context_retire_descriptors(&ctx->context); } static void ohci_isoc_context_work(struct work_struct *work) { - struct fw_iso_context *base = container_of(work, struct fw_iso_context, work); + struct fw_iso_context *base = from_work(base, work, work); struct iso_context *isoc_ctx = container_of(base, struct iso_context, base); context_retire_descriptors(&isoc_ctx->context); @@ -1248,7 +1246,6 @@ static int context_init(struct context *ctx, struct fw_ohci *ohci, ctx->buffer_tail = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list); - tasklet_init(&ctx->tasklet, context_tasklet, (unsigned long)ctx); ctx->callback = callback; /* @@ -1388,17 +1385,17 @@ struct driver_data { * Must always be called with the ochi->lock held to ensure proper * generation handling and locking around packet queue manipulation. */ -static int at_context_queue_packet(struct context *ctx, - struct fw_packet *packet) +static int at_context_queue_packet(struct at_context *ctx, struct fw_packet *packet) { - struct fw_ohci *ohci = ctx->ohci; + struct context *context = &ctx->context; + struct fw_ohci *ohci = context->ohci; dma_addr_t d_bus, payload_bus; struct driver_data *driver_data; struct descriptor *d, *last; __le32 *header; int z, tcode; - d = context_get_descriptors(ctx, 4, &d_bus); + d = context_get_descriptors(context, 4, &d_bus); if (d == NULL) { packet->ack = RCODE_SEND_ERROR; return -1; @@ -1428,7 +1425,7 @@ static int at_context_queue_packet(struct context *ctx, ohci1394_at_data_set_destination_id(header, async_header_get_destination(packet->header)); - if (ctx == &ctx->ohci->at_response_ctx) { + if (ctx == &ohci->at_response_ctx) { ohci1394_at_data_set_rcode(header, async_header_get_rcode(packet->header)); } else { ohci1394_at_data_set_destination_offset(header, @@ -1517,37 +1514,42 @@ static int at_context_queue_packet(struct context *ctx, return -1; } - context_append(ctx, d, z, 4 - z); + context_append(context, d, z, 4 - z); - if (ctx->running) - reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); + if (context->running) + reg_write(ohci, CONTROL_SET(context->regs), CONTEXT_WAKE); else - context_run(ctx, 0); + context_run(context, 0); return 0; } -static void at_context_flush(struct context *ctx) +static void at_context_flush(struct at_context *ctx) { - tasklet_disable(&ctx->tasklet); + // Avoid dead lock due to programming mistake. + if (WARN_ON_ONCE(current_work() == &ctx->work)) + return; - ctx->flushing = true; - context_tasklet((unsigned long)ctx); - ctx->flushing = false; + disable_work_sync(&ctx->work); - tasklet_enable(&ctx->tasklet); + WRITE_ONCE(ctx->flushing, true); + ohci_at_context_work(&ctx->work); + WRITE_ONCE(ctx->flushing, false); + + enable_work(&ctx->work); } static int handle_at_packet(struct context *context, struct descriptor *d, struct descriptor *last) { + struct at_context *ctx = container_of(context, struct at_context, context); + struct fw_ohci *ohci = ctx->context.ohci; struct driver_data *driver_data; struct fw_packet *packet; - struct fw_ohci *ohci = context->ohci; int evt; - if (last->transfer_status == 0 && !context->flushing) + if (last->transfer_status == 0 && !READ_ONCE(ctx->flushing)) /* This descriptor isn't done yet, stop iteration. */ return 0; @@ -1581,7 +1583,7 @@ static int handle_at_packet(struct context *context, break; case OHCI1394_evt_missing_ack: - if (context->flushing) + if (READ_ONCE(ctx->flushing)) packet->ack = RCODE_GENERATION; else { /* @@ -1603,7 +1605,7 @@ static int handle_at_packet(struct context *context, break; case OHCI1394_evt_no_status: - if (context->flushing) { + if (READ_ONCE(ctx->flushing)) { packet->ack = RCODE_GENERATION; break; } @@ -1700,13 +1702,14 @@ static void handle_local_lock(struct fw_ohci *ohci, fw_core_handle_response(&ohci->card, &response); } -static void handle_local_request(struct context *ctx, struct fw_packet *packet) +static void handle_local_request(struct at_context *ctx, struct fw_packet *packet) { + struct fw_ohci *ohci = ctx->context.ohci; u64 offset, csr; - if (ctx == &ctx->ohci->at_request_ctx) { + if (ctx == &ohci->at_request_ctx) { packet->ack = ACK_PENDING; - packet->callback(packet, &ctx->ohci->card, packet->ack); + packet->callback(packet, &ohci->card, packet->ack); } offset = async_header_get_offset(packet->header); @@ -1714,54 +1717,55 @@ static void handle_local_request(struct context *ctx, struct fw_packet *packet) /* Handle config rom reads. */ if (csr >= CSR_CONFIG_ROM && csr < CSR_CONFIG_ROM_END) - handle_local_rom(ctx->ohci, packet, csr); + handle_local_rom(ohci, packet, csr); else switch (csr) { case CSR_BUS_MANAGER_ID: case CSR_BANDWIDTH_AVAILABLE: case CSR_CHANNELS_AVAILABLE_HI: case CSR_CHANNELS_AVAILABLE_LO: - handle_local_lock(ctx->ohci, packet, csr); + handle_local_lock(ohci, packet, csr); break; default: - if (ctx == &ctx->ohci->at_request_ctx) - fw_core_handle_request(&ctx->ohci->card, packet); + if (ctx == &ohci->at_request_ctx) + fw_core_handle_request(&ohci->card, packet); else - fw_core_handle_response(&ctx->ohci->card, packet); + fw_core_handle_response(&ohci->card, packet); break; } - if (ctx == &ctx->ohci->at_response_ctx) { + if (ctx == &ohci->at_response_ctx) { packet->ack = ACK_COMPLETE; - packet->callback(packet, &ctx->ohci->card, packet->ack); + packet->callback(packet, &ohci->card, packet->ack); } } -static void at_context_transmit(struct context *ctx, struct fw_packet *packet) +static void at_context_transmit(struct at_context *ctx, struct fw_packet *packet) { + struct fw_ohci *ohci = ctx->context.ohci; unsigned long flags; int ret; - spin_lock_irqsave(&ctx->ohci->lock, flags); + spin_lock_irqsave(&ohci->lock, flags); - if (async_header_get_destination(packet->header) == ctx->ohci->node_id && - ctx->ohci->generation == packet->generation) { - spin_unlock_irqrestore(&ctx->ohci->lock, flags); + if (async_header_get_destination(packet->header) == ohci->node_id && + ohci->generation == packet->generation) { + spin_unlock_irqrestore(&ohci->lock, flags); // Timestamping on behalf of the hardware. - packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci)); handle_local_request(ctx, packet); return; } ret = at_context_queue_packet(ctx, packet); - spin_unlock_irqrestore(&ctx->ohci->lock, flags); + spin_unlock_irqrestore(&ohci->lock, flags); if (ret < 0) { // Timestamping on behalf of the hardware. - packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci)); - packet->callback(packet, &ctx->ohci->card, packet->ack); + packet->callback(packet, &ohci->card, packet->ack); } } @@ -2028,8 +2032,7 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) static void bus_reset_work(struct work_struct *work) { - struct fw_ohci *ohci = - container_of(work, struct fw_ohci, bus_reset_work); + struct fw_ohci *ohci = from_work(ohci, work, bus_reset_work); int self_id_count, generation, new_generation, i, j; u32 reg, quadlet; void *free_rom = NULL; @@ -2141,8 +2144,8 @@ static void bus_reset_work(struct work_struct *work) // FIXME: Document how the locking works. scoped_guard(spinlock_irq, &ohci->lock) { ohci->generation = -1; // prevent AT packet queueing - context_stop(&ohci->at_request_ctx); - context_stop(&ohci->at_response_ctx); + context_stop(&ohci->at_request_ctx.context); + context_stop(&ohci->at_response_ctx.context); } /* @@ -2239,16 +2242,16 @@ static irqreturn_t irq_handler(int irq, void *data) } if (event & OHCI1394_RQPkt) - tasklet_schedule(&ohci->ar_request_ctx.tasklet); + queue_work(ohci->card.async_wq, &ohci->ar_request_ctx.work); if (event & OHCI1394_RSPkt) - tasklet_schedule(&ohci->ar_response_ctx.tasklet); + queue_work(ohci->card.async_wq, &ohci->ar_response_ctx.work); if (event & OHCI1394_reqTxComplete) - tasklet_schedule(&ohci->at_request_ctx.tasklet); + queue_work(ohci->card.async_wq, &ohci->at_request_ctx.work); if (event & OHCI1394_respTxComplete) - tasklet_schedule(&ohci->at_response_ctx.tasklet); + queue_work(ohci->card.async_wq, &ohci->at_response_ctx.work); if (event & OHCI1394_isochRx) { iso_event = reg_read(ohci, OHCI1394_IsoRecvIntEventClear); @@ -2528,7 +2531,7 @@ static int ohci_enable(struct fw_card *card, * They shouldn't do that in this initial case where the link * isn't enabled. This means we have to use the same * workaround here, setting the bus header to 0 and then write - * the right values in the bus reset tasklet. + * the right values in the bus reset work item. */ if (config_rom) { @@ -2617,7 +2620,7 @@ static int ohci_set_config_rom(struct fw_card *card, * during the atomic update, even on little endian * architectures. The workaround we use is to put a 0 in the * header quadlet; 0 is endian agnostic and means that the - * config rom isn't ready yet. In the bus reset tasklet we + * config rom isn't ready yet. In the bus reset work item we * then set up the real values for the two registers. * * We use ohci->lock to avoid racing with the code that sets @@ -2659,7 +2662,7 @@ static int ohci_set_config_rom(struct fw_card *card, /* * Now initiate a bus reset to have the changes take * effect. We clean up the old config rom memory and DMA - * mappings in the bus reset tasklet, since the OHCI + * mappings in the bus reset work item, since the OHCI * controller could need to access it before the bus reset * takes effect. */ @@ -2686,11 +2689,14 @@ static void ohci_send_response(struct fw_card *card, struct fw_packet *packet) static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) { struct fw_ohci *ohci = fw_ohci(card); - struct context *ctx = &ohci->at_request_ctx; + struct at_context *ctx = &ohci->at_request_ctx; struct driver_data *driver_data = packet->driver_data; int ret = -ENOENT; - tasklet_disable_in_atomic(&ctx->tasklet); + // Avoid dead lock due to programming mistake. + if (WARN_ON_ONCE(current_work() == &ctx->work)) + return 0; + disable_work_sync(&ctx->work); if (packet->ack != 0) goto out; @@ -2709,7 +2715,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) packet->callback(packet, &ohci->card, packet->ack); ret = 0; out: - tasklet_enable(&ctx->tasklet); + enable_work(&ctx->work); return ret; } @@ -3767,15 +3773,17 @@ static int pci_probe(struct pci_dev *dev, if (err < 0) return err; - err = context_init(&ohci->at_request_ctx, ohci, + err = context_init(&ohci->at_request_ctx.context, ohci, OHCI1394_AsReqTrContextControlSet, handle_at_packet); if (err < 0) return err; + INIT_WORK(&ohci->at_request_ctx.work, ohci_at_context_work); - err = context_init(&ohci->at_response_ctx, ohci, + err = context_init(&ohci->at_response_ctx.context, ohci, OHCI1394_AsRspTrContextControlSet, handle_at_packet); if (err < 0) return err; + INIT_WORK(&ohci->at_response_ctx.work, ohci_at_context_work); reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); ohci->ir_context_channels = ~0ULL; diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index fe55613a8ea9..65bf1685350a 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -110,7 +110,7 @@ struct ffa_drv_info { struct work_struct sched_recv_irq_work; struct xarray partition_info; DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS)); - struct mutex notify_lock; /* lock to protect notifier hashtable */ + rwlock_t notify_lock; /* lock to protect notifier hashtable */ }; static struct ffa_drv_info *drv_info; @@ -1250,13 +1250,12 @@ notifier_hnode_get_by_type(u16 notify_id, enum notify_type type) return NULL; } -static int -update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb, - void *cb_data, bool is_registration, bool is_framework) +static int update_notifier_cb(struct ffa_device *dev, int notify_id, + struct notifier_cb_info *cb, bool is_framework) { struct notifier_cb_info *cb_info = NULL; enum notify_type type = ffa_notify_type_get(dev->vm_id); - bool cb_found; + bool cb_found, is_registration = !!cb; if (is_framework) cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, dev->vm_id, @@ -1270,20 +1269,10 @@ update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb, return -EINVAL; if (is_registration) { - cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL); - if (!cb_info) - return -ENOMEM; - - cb_info->dev = dev; - cb_info->cb_data = cb_data; - if (is_framework) - cb_info->fwk_cb = cb; - else - cb_info->cb = cb; - - hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id); + hash_add(drv_info->notifier_hash, &cb->hnode, notify_id); } else { hash_del(&cb_info->hnode); + kfree(cb_info); } return 0; @@ -1300,20 +1289,19 @@ static int __ffa_notify_relinquish(struct ffa_device *dev, int notify_id, if (notify_id >= FFA_MAX_NOTIFICATIONS) return -EINVAL; - mutex_lock(&drv_info->notify_lock); + write_lock(&drv_info->notify_lock); - rc = update_notifier_cb(dev, notify_id, NULL, NULL, false, - is_framework); + rc = update_notifier_cb(dev, notify_id, NULL, is_framework); if (rc) { pr_err("Could not unregister notification callback\n"); - mutex_unlock(&drv_info->notify_lock); + write_unlock(&drv_info->notify_lock); return rc; } if (!is_framework) rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id)); - mutex_unlock(&drv_info->notify_lock); + write_unlock(&drv_info->notify_lock); return rc; } @@ -1334,6 +1322,7 @@ static int __ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, { int rc; u32 flags = 0; + struct notifier_cb_info *cb_info = NULL; if (ffa_notifications_disabled()) return -EOPNOTSUPP; @@ -1341,28 +1330,40 @@ static int __ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, if (notify_id >= FFA_MAX_NOTIFICATIONS) return -EINVAL; - mutex_lock(&drv_info->notify_lock); + cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL); + if (!cb_info) + return -ENOMEM; + + cb_info->dev = dev; + cb_info->cb_data = cb_data; + if (is_framework) + cb_info->fwk_cb = cb; + else + cb_info->cb = cb; + + write_lock(&drv_info->notify_lock); if (!is_framework) { if (is_per_vcpu) flags = PER_VCPU_NOTIFICATION_FLAG; rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags); - if (rc) { - mutex_unlock(&drv_info->notify_lock); - return rc; - } + if (rc) + goto out_unlock_free; } - rc = update_notifier_cb(dev, notify_id, cb, cb_data, true, - is_framework); + rc = update_notifier_cb(dev, notify_id, cb_info, is_framework); if (rc) { pr_err("Failed to register callback for %d - %d\n", notify_id, rc); if (!is_framework) ffa_notification_unbind(dev->vm_id, BIT(notify_id)); } - mutex_unlock(&drv_info->notify_lock); + +out_unlock_free: + write_unlock(&drv_info->notify_lock); + if (rc) + kfree(cb_info); return rc; } @@ -1406,9 +1407,9 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type) if (!(bitmap & 1)) continue; - mutex_lock(&drv_info->notify_lock); + read_lock(&drv_info->notify_lock); cb_info = notifier_hnode_get_by_type(notify_id, type); - mutex_unlock(&drv_info->notify_lock); + read_unlock(&drv_info->notify_lock); if (cb_info && cb_info->cb) cb_info->cb(notify_id, cb_info->cb_data); @@ -1446,9 +1447,9 @@ static void handle_fwk_notif_callbacks(u32 bitmap) ffa_rx_release(); - mutex_lock(&drv_info->notify_lock); + read_lock(&drv_info->notify_lock); cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); - mutex_unlock(&drv_info->notify_lock); + read_unlock(&drv_info->notify_lock); if (cb_info && cb_info->fwk_cb) cb_info->fwk_cb(notify_id, cb_info->cb_data, buf); @@ -1973,7 +1974,7 @@ static void ffa_notifications_setup(void) goto cleanup; hash_init(drv_info->notifier_hash); - mutex_init(&drv_info->notify_lock); + rwlock_init(&drv_info->notify_lock); drv_info->notif_enabled = true; return; @@ -2058,7 +2059,7 @@ static int __init ffa_init(void) kfree(drv_info); return ret; } -module_init(ffa_init); +rootfs_initcall(ffa_init); static void __exit ffa_exit(void) { diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 1adef0389475..24e59ddf85e7 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -323,6 +323,31 @@ static struct attribute *scmi_device_attributes_attrs[] = { }; ATTRIBUTE_GROUPS(scmi_device_attributes); +static int scmi_pm_suspend(struct device *dev) +{ + const struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->suspend) + return drv->pm->suspend(dev); + + return 0; +} + +static int scmi_pm_resume(struct device *dev) +{ + const struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->resume) + return drv->pm->resume(dev); + + return 0; +} + +static const struct dev_pm_ops scmi_dev_pm_ops = { + .suspend = pm_sleep_ptr(scmi_pm_suspend), + .resume = pm_sleep_ptr(scmi_pm_resume), +}; + const struct bus_type scmi_bus_type = { .name = "scmi_protocol", .match = scmi_dev_match, @@ -330,6 +355,7 @@ const struct bus_type scmi_bus_type = { .remove = scmi_dev_remove, .uevent = scmi_device_uevent, .dev_groups = scmi_device_attributes_groups, + .pm = &scmi_dev_pm_ops, }; EXPORT_SYMBOL_GPL(scmi_bus_type); diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index dab758c5fdea..07b9e629276d 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -305,6 +305,7 @@ enum debug_counters { ERR_MSG_INVALID, ERR_MSG_NOMEM, ERR_PROTOCOL, + XFERS_INFLIGHT, SCMI_DEBUG_COUNTERS_LAST }; @@ -314,6 +315,12 @@ static inline void scmi_inc_count(atomic_t *arr, int stat) atomic_inc(&arr[stat]); } +static inline void scmi_dec_count(atomic_t *arr, int stat) +{ + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) + atomic_dec(&arr[stat]); +} + enum scmi_bad_msg { MSG_UNEXPECTED = -1, MSG_INVALID = -2, @@ -498,4 +505,5 @@ static struct platform_driver __drv = { \ void scmi_notification_instance_data_set(const struct scmi_handle *handle, void *priv); void *scmi_notification_instance_data_get(const struct scmi_handle *handle); +int scmi_inflight_count(const struct scmi_handle *handle); #endif /* _SCMI_COMMON_H */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 395fe9289035..bd56a877fdfc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -190,6 +190,7 @@ struct scmi_info { }; #define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle) +#define tx_minfo_to_scmi_info(h) container_of(h, struct scmi_info, tx_minfo) #define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb) #define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb) @@ -603,9 +604,14 @@ static inline void scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer, struct scmi_xfers_info *minfo) { + /* In this context minfo will be tx_minfo due to the xfer pending */ + struct scmi_info *info = tx_minfo_to_scmi_info(minfo); + /* Set in-flight */ set_bit(xfer->hdr.seq, minfo->xfer_alloc_table); hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq); + scmi_inc_count(info->dbg->counters, XFERS_INFLIGHT); + xfer->pending = true; } @@ -807,9 +813,13 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) spin_lock_irqsave(&minfo->xfer_lock, flags); if (refcount_dec_and_test(&xfer->users)) { if (xfer->pending) { + struct scmi_info *info = tx_minfo_to_scmi_info(minfo); + scmi_xfer_token_clear(minfo, xfer); hash_del(&xfer->node); xfer->pending = false; + + scmi_dec_count(info->dbg->counters, XFERS_INFLIGHT); } hlist_add_head(&xfer->node, &minfo->free_xfers); } @@ -1433,7 +1443,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph, trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id, xfer->hdr.protocol_id, xfer->hdr.seq, - xfer->hdr.poll_completion); + xfer->hdr.poll_completion, + scmi_inflight_count(&info->handle)); /* Clear any stale status */ xfer->hdr.status = SCMI_SUCCESS; @@ -1469,7 +1480,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph, info->desc->ops->mark_txdone(cinfo, ret, xfer); trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, - xfer->hdr.protocol_id, xfer->hdr.seq, ret); + xfer->hdr.protocol_id, xfer->hdr.seq, ret, + scmi_inflight_count(&info->handle)); return ret; } @@ -2912,6 +2924,7 @@ static const char * const dbg_counter_strs[] = { "err_msg_invalid", "err_msg_nomem", "err_protocol", + "xfers_inflight", }; static ssize_t reset_all_on_write(struct file *filp, const char __user *buf, @@ -3405,6 +3418,17 @@ static struct dentry *scmi_debugfs_init(void) return d; } +int scmi_inflight_count(const struct scmi_handle *handle) +{ + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) { + struct scmi_info *info = handle_to_scmi_info(handle); + + return atomic_read(&info->dbg->counters[XFERS_INFLIGHT]); + } else { + return 0; + } +} + static int __init scmi_driver_init(void) { scmi_quirks_initialize(); diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index e160ecb22948..dee9f238f6fd 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -318,6 +318,9 @@ struct scmi_registered_events_desc { * customized event report * @num_sources: The number of possible sources for this event as stated at * events' registration time + * @not_supported_by_platform: A flag to indicate that not even one source was + * found to be supported by the platform for this + * event * @sources: A reference to a dynamically allocated array used to refcount the * events' enable requests for all the existing sources * @sources_mtx: A mutex to serialize the access to @sources @@ -334,6 +337,7 @@ struct scmi_registered_event { const struct scmi_event *evt; void *report; u32 num_sources; + bool not_supported_by_platform; refcount_t *sources; /* locking to serialize the access to sources */ struct mutex sources_mtx; @@ -811,10 +815,19 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, if (!r_evt->report) return -ENOMEM; - for (id = 0; id < r_evt->num_sources; id++) - if (ee->ops->is_notify_supported && - !ee->ops->is_notify_supported(ph, r_evt->evt->id, id)) - refcount_set(&r_evt->sources[id], NOTIF_UNSUPP); + if (ee->ops->is_notify_supported) { + int supported = 0; + + for (id = 0; id < r_evt->num_sources; id++) { + if (!ee->ops->is_notify_supported(ph, r_evt->evt->id, id)) + refcount_set(&r_evt->sources[id], NOTIF_UNSUPP); + else + supported++; + } + + /* Not even one source has been found to be supported */ + r_evt->not_supported_by_platform = !supported; + } pd->registered_events[i] = r_evt; /* Ensure events are updated */ @@ -936,6 +949,11 @@ static inline int scmi_bind_event_handler(struct scmi_notify_instance *ni, * of protocol instance. */ hash_del(&hndl->hash); + + /* Bailout if event is not supported at all */ + if (r_evt->not_supported_by_platform) + return -EOPNOTSUPP; + /* * Acquire protocols only for NON pending handlers, so as NOT to trigger * protocol initialization when a notifier is registered against a still @@ -1060,6 +1078,9 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni, r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key), KEY_XTRACT_EVT_ID(evt_key)); + if (r_evt && r_evt->not_supported_by_platform) + return ERR_PTR(-EOPNOTSUPP); + mutex_lock(&ni->pending_mtx); /* Search registered events at first ... if possible at all */ if (r_evt) { @@ -1087,7 +1108,7 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni, hndl->key); /* this hndl can be only a pending one */ scmi_put_handler_unlocked(ni, hndl); - hndl = NULL; + hndl = ERR_PTR(-EINVAL); } } mutex_unlock(&ni->pending_mtx); @@ -1370,8 +1391,8 @@ static int scmi_notifier_register(const struct scmi_handle *handle, evt_key = MAKE_HASH_KEY(proto_id, evt_id, src_id ? *src_id : SRC_ID_MASK); hndl = scmi_get_or_create_handler(ni, evt_key); - if (!hndl) - return -EINVAL; + if (IS_ERR(hndl)) + return PTR_ERR(hndl); blocking_notifier_chain_register(&hndl->chain, nb); @@ -1416,8 +1437,8 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle, evt_key = MAKE_HASH_KEY(proto_id, evt_id, src_id ? *src_id : SRC_ID_MASK); hndl = scmi_get_handler(ni, evt_key); - if (!hndl) - return -EINVAL; + if (IS_ERR(hndl)) + return PTR_ERR(hndl); /* * Note that this chain unregistration call is safe on its own diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index c7e5a34b254b..683fd9b85c5c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -892,7 +892,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, freq = dom->opp[idx].indicative_freq * dom->mult_factor; /* All OPPs above the sustained frequency are treated as turbo */ - data.turbo = freq > dom->sustained_freq_khz * 1000; + data.turbo = freq > dom->sustained_freq_khz * 1000UL; data.level = dom->opp[idx].perf; data.freq = freq; diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c index 3d543b1d8947..73db5492ab44 100644 --- a/drivers/firmware/arm_scmi/raw_mode.c +++ b/drivers/firmware/arm_scmi/raw_mode.c @@ -475,7 +475,8 @@ static void scmi_xfer_raw_worker(struct work_struct *work) raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer); trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, - xfer->hdr.protocol_id, xfer->hdr.seq, ret); + xfer->hdr.protocol_id, xfer->hdr.seq, + ret, scmi_inflight_count(raw->handle)); /* Wait also for an async delayed response if needed */ if (!ret && xfer->async_done) { @@ -642,7 +643,8 @@ static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw, trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id, xfer->hdr.protocol_id, xfer->hdr.seq, - xfer->hdr.poll_completion); + xfer->hdr.poll_completion, + scmi_inflight_count(raw->handle)); ret = raw->desc->ops->send_message(rw->cinfo, xfer); if (ret) { diff --git a/drivers/firmware/arm_scmi/scmi_power_control.c b/drivers/firmware/arm_scmi/scmi_power_control.c index 21f467a92942..955736336061 100644 --- a/drivers/firmware/arm_scmi/scmi_power_control.c +++ b/drivers/firmware/arm_scmi/scmi_power_control.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -324,12 +325,7 @@ static int scmi_userspace_notifier(struct notifier_block *nb, static void scmi_suspend_work_func(struct work_struct *work) { - struct scmi_syspower_conf *sc = - container_of(work, struct scmi_syspower_conf, suspend_work); - pm_suspend(PM_SUSPEND_MEM); - - sc->state = SCMI_SYSPOWER_IDLE; } static int scmi_syspower_probe(struct scmi_device *sdev) @@ -354,6 +350,7 @@ static int scmi_syspower_probe(struct scmi_device *sdev) sc->required_transition = SCMI_SYSTEM_MAX; sc->userspace_nb.notifier_call = &scmi_userspace_notifier; sc->dev = &sdev->dev; + dev_set_drvdata(&sdev->dev, sc); INIT_WORK(&sc->suspend_work, scmi_suspend_work_func); @@ -363,6 +360,18 @@ static int scmi_syspower_probe(struct scmi_device *sdev) NULL, &sc->userspace_nb); } +static int scmi_system_power_resume(struct device *dev) +{ + struct scmi_syspower_conf *sc = dev_get_drvdata(dev); + + sc->state = SCMI_SYSPOWER_IDLE; + return 0; +} + +static const struct dev_pm_ops scmi_system_power_pmops = { + SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume) +}; + static const struct scmi_device_id scmi_id_table[] = { { SCMI_PROTOCOL_SYSTEM, "syspower" }, { }, @@ -370,6 +379,9 @@ static const struct scmi_device_id scmi_id_table[] = { MODULE_DEVICE_TABLE(scmi, scmi_id_table); static struct scmi_driver scmi_system_power_driver = { + .driver = { + .pm = pm_sleep_ptr(&scmi_system_power_pmops), + }, .name = "scmi-system-power", .probe = scmi_syspower_probe, .id_table = scmi_id_table, diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 560724ce21aa..f51047d8ea64 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -311,6 +311,11 @@ static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; static const struct cs_dsp_ops cs_dsp_halo_ops; static const struct cs_dsp_ops cs_dsp_halo_ao_ops; +struct cs_dsp_alg_region_list_item { + struct list_head list; + struct cs_dsp_alg_region alg_region; +}; + struct cs_dsp_buf { struct list_head list; void *buf; @@ -1752,13 +1757,13 @@ static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, int type, unsigned int id) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; lockdep_assert_held(&dsp->pwr_lock); - list_for_each_entry(alg_region, &dsp->alg_regions, list) { - if (id == alg_region->alg && type == alg_region->type) - return alg_region; + list_for_each_entry(item, &dsp->alg_regions, list) { + if (id == item->alg_region.alg && type == item->alg_region.type) + return &item->alg_region; } return NULL; @@ -1769,35 +1774,35 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, int type, __be32 id, __be32 ver, __be32 base) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; - alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); - if (!alg_region) + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) return ERR_PTR(-ENOMEM); - alg_region->type = type; - alg_region->alg = be32_to_cpu(id); - alg_region->ver = be32_to_cpu(ver); - alg_region->base = be32_to_cpu(base); + item->alg_region.type = type; + item->alg_region.alg = be32_to_cpu(id); + item->alg_region.ver = be32_to_cpu(ver); + item->alg_region.base = be32_to_cpu(base); - list_add_tail(&alg_region->list, &dsp->alg_regions); + list_add_tail(&item->list, &dsp->alg_regions); if (dsp->wmfw_ver > 0) - cs_dsp_ctl_fixup_base(dsp, alg_region); + cs_dsp_ctl_fixup_base(dsp, &item->alg_region); - return alg_region; + return &item->alg_region; } static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct cs_dsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); + item = list_first_entry(&dsp->alg_regions, + struct cs_dsp_alg_region_list_item, + list); + list_del(&item->list); + kfree(item); } } diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index db8c5c03d3a2..d528c94c5859 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -263,6 +263,14 @@ config EFI_COCO_SECRET virt/coco/efi_secret module to access the secrets, which in turn allows userspace programs to access the injected secrets. +config OVMF_DEBUG_LOG + bool "Expose OVMF firmware debug log via sysfs" + depends on EFI + help + Recent OVMF versions (edk2-stable202508 + newer) can write + their debug log to a memory buffer. This driver exposes the + log content via sysfs (/sys/firmware/efi/ovmf_debug_log). + config UNACCEPTED_MEMORY bool depends on EFI_STUB @@ -286,7 +294,7 @@ config EFI_SBAT config EFI_SBAT_FILE string "Embedded SBAT section file path" - depends on EFI_ZBOOT + depends on EFI_ZBOOT || (EFI_STUB && X86) help SBAT section provides a way to improve SecureBoot revocations of UEFI binaries by introducing a generation-based mechanism. With SBAT, older diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index a2d0009560d0..8efbcf699e4f 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o obj-$(CONFIG_LOAD_UEFI_KEYS) += mokvar-table.o +obj-$(CONFIG_OVMF_DEBUG_LOG) += ovmf-debug-log.o obj-$(CONFIG_SYSFB) += sysfb_efi.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index e57bff702b5f..1ce428e2ac8a 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -45,6 +45,7 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, + .ovmf_debug_log = EFI_INVALID_TABLE_ADDR, #ifdef CONFIG_LOAD_UEFI_KEYS .mokvar_table = EFI_INVALID_TABLE_ADDR, #endif @@ -473,6 +474,10 @@ static int __init efisubsys_init(void) platform_device_register_simple("efi_secret", 0, NULL, 0); #endif + if (IS_ENABLED(CONFIG_OVMF_DEBUG_LOG) && + efi.ovmf_debug_log != EFI_INVALID_TABLE_ADDR) + ovmf_log_probe(efi.ovmf_debug_log); + return 0; err_remove_group: @@ -617,6 +622,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, +#ifdef CONFIG_OVMF_DEBUG_LOG + {OVMF_MEMORY_LOG_TABLE_GUID, &efi.ovmf_debug_log, "OvmfDebugLog" }, +#endif #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, #endif diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 939a4955e00b..94b05e4451dd 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -22,16 +22,16 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -std=gnu11 \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ +cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_KSTACK_ERASE) \ -fno-unwind-tables -fno-asynchronous-unwind-tables cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ -DEFI_HAVE_STRCMP -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE -mno-relax \ - $(DISABLE_STACKLEAK_PLUGIN) -cflags-$(CONFIG_LOONGARCH) += -fpie $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) +cflags-$(CONFIG_LOONGARCH) += -fpie $(DISABLE_KSTACK_ERASE) cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 92e3c73502ba..832deee36e48 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -36,7 +36,7 @@ aflags-zboot-header-$(EFI_ZBOOT_FORWARD_CFI) := \ -DPE_DLL_CHAR_EX=IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE) \ - -DZBOOT_EFI_PATH="\"$(realpath $(obj)/vmlinuz.efi.elf)\"" \ + -DZBOOT_EFI_PATH="\"$(abspath $(obj)/vmlinuz.efi.elf)\"" \ -DZBOOT_SIZE_LEN=$(zboot-size-len-y) \ -DCOMP_TYPE="\"$(comp-type-y)\"" \ $(aflags-zboot-header-y) diff --git a/drivers/firmware/efi/libstub/printk.c b/drivers/firmware/efi/libstub/printk.c index 3a67a2cea7bd..bc599212c05d 100644 --- a/drivers/firmware/efi/libstub/printk.c +++ b/drivers/firmware/efi/libstub/printk.c @@ -5,13 +5,13 @@ #include #include #include -#include /* For CONSOLE_LOGLEVEL_* */ +#include #include #include #include "efistub.h" -int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; +int efi_loglevel = LOGLEVEL_NOTICE; /** * efi_char16_puts() - Write a UCS-2 encoded string to the console diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index c3a166675450..367907eb7d86 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -29,14 +29,12 @@ SECTIONS . = _etext; } -#ifdef CONFIG_EFI_SBAT .sbat : ALIGN(4096) { _sbat = .; *(.sbat) _esbat = ALIGN(4096); . = _esbat; } -#endif .data : ALIGN(4096) { _data = .; @@ -60,6 +58,6 @@ SECTIONS PROVIDE(__efistub__gzdata_size = ABSOLUTE(__efistub__gzdata_end - __efistub__gzdata_start)); -PROVIDE(__data_rawsize = ABSOLUTE(_edata - _etext)); -PROVIDE(__data_size = ABSOLUTE(_end - _etext)); +PROVIDE(__data_rawsize = ABSOLUTE(_edata - _data)); +PROVIDE(__data_size = ABSOLUTE(_end - _data)); PROVIDE(__sbat_size = ABSOLUTE(_esbat - _sbat)); diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c index 0a856c3f69a3..aedbbd627706 100644 --- a/drivers/firmware/efi/mokvar-table.c +++ b/drivers/firmware/efi/mokvar-table.c @@ -340,7 +340,7 @@ static int __init efi_mokvar_sysfs_init(void) mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name; mokvar_sysfs->bin_attr.attr.mode = 0400; mokvar_sysfs->bin_attr.size = mokvar_entry->data_size; - mokvar_sysfs->bin_attr.read_new = efi_mokvar_sysfs_read; + mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read; err = sysfs_create_bin_file(mokvar_kobj, &mokvar_sysfs->bin_attr); diff --git a/drivers/firmware/efi/ovmf-debug-log.c b/drivers/firmware/efi/ovmf-debug-log.c new file mode 100644 index 000000000000..5b2471ffaeed --- /dev/null +++ b/drivers/firmware/efi/ovmf-debug-log.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OVMF_DEBUG_LOG_MAGIC1 0x3167646d666d766f // "ovmfmdg1" +#define OVMF_DEBUG_LOG_MAGIC2 0x3267646d666d766f // "ovmfmdg2" + +struct ovmf_debug_log_header { + u64 magic1; + u64 magic2; + u64 hdr_size; + u64 log_size; + u64 lock; // edk2 spinlock + u64 head_off; + u64 tail_off; + u64 truncated; + u8 fw_version[128]; +}; + +static struct ovmf_debug_log_header *hdr; +static u8 *logbuf; +static u64 logbufsize; + +static ssize_t ovmf_log_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, + loff_t offset, size_t count) +{ + u64 start, end; + + start = hdr->head_off + offset; + if (hdr->head_off > hdr->tail_off && start >= hdr->log_size) + start -= hdr->log_size; + + end = start + count; + if (start > hdr->tail_off) { + if (end > hdr->log_size) + end = hdr->log_size; + } else { + if (end > hdr->tail_off) + end = hdr->tail_off; + } + + if (start > logbufsize || end > logbufsize) + return 0; + if (start >= end) + return 0; + + memcpy(buf, logbuf + start, end - start); + return end - start; +} + +static struct bin_attribute ovmf_log_bin_attr = { + .attr = { + .name = "ovmf_debug_log", + .mode = 0444, + }, + .read = ovmf_log_read, +}; + +int __init ovmf_log_probe(unsigned long ovmf_debug_log_table) +{ + int ret = -EINVAL; + u64 size; + + /* map + verify header */ + hdr = memremap(ovmf_debug_log_table, sizeof(*hdr), MEMREMAP_WB); + if (!hdr) { + pr_err("OVMF debug log: header map failed\n"); + return -EINVAL; + } + + if (hdr->magic1 != OVMF_DEBUG_LOG_MAGIC1 || + hdr->magic2 != OVMF_DEBUG_LOG_MAGIC2) { + printk(KERN_ERR "OVMF debug log: magic mismatch\n"); + goto err_unmap; + } + + size = hdr->hdr_size + hdr->log_size; + pr_info("OVMF debug log: firmware version: \"%s\"\n", hdr->fw_version); + pr_info("OVMF debug log: buffer size: %lluk\n", size / 1024); + + /* map complete log buffer */ + memunmap(hdr); + hdr = memremap(ovmf_debug_log_table, size, MEMREMAP_WB); + if (!hdr) { + pr_err("OVMF debug log: buffer map failed\n"); + return -EINVAL; + } + logbuf = (void *)hdr + hdr->hdr_size; + logbufsize = hdr->log_size; + + ovmf_log_bin_attr.size = size; + ret = sysfs_create_bin_file(efi_kobj, &ovmf_log_bin_attr); + if (ret != 0) { + pr_err("OVMF debug log: sysfs register failed\n"); + goto err_unmap; + } + + return 0; + +err_unmap: + memunmap(hdr); + return ret; +} diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c index f741ca279052..65c0fe1ba275 100644 --- a/drivers/firmware/efi/stmm/tee_stmm_efi.c +++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c @@ -143,6 +143,10 @@ static efi_status_t mm_communicate(u8 *comm_buf, size_t payload_size) return var_hdr->ret_status; } +#define COMM_BUF_SIZE(__payload_size) (MM_COMMUNICATE_HEADER_SIZE + \ + MM_VARIABLE_COMMUNICATE_SIZE + \ + (__payload_size)) + /** * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the * header data. @@ -150,11 +154,9 @@ static efi_status_t mm_communicate(u8 *comm_buf, size_t payload_size) * @dptr: pointer address to store allocated buffer * @payload_size: payload size * @func: standAloneMM function number - * @ret: EFI return code * Return: pointer to corresponding StandAloneMM function buffer or NULL */ -static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func, - efi_status_t *ret) +static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func) { const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID; struct efi_mm_communicate_header *mm_hdr; @@ -169,17 +171,13 @@ static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func, if (max_buffer_size && max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) { - *ret = EFI_INVALID_PARAMETER; return NULL; } - comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE + - MM_VARIABLE_COMMUNICATE_SIZE + payload_size, - GFP_KERNEL); - if (!comm_buf) { - *ret = EFI_OUT_OF_RESOURCES; + comm_buf = alloc_pages_exact(COMM_BUF_SIZE(payload_size), + GFP_KERNEL | __GFP_ZERO); + if (!comm_buf) return NULL; - } mm_hdr = (struct efi_mm_communicate_header *)comm_buf; memcpy(&mm_hdr->header_guid, &mm_var_guid, sizeof(mm_hdr->header_guid)); @@ -187,9 +185,7 @@ static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func, var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; var_hdr->function = func; - if (dptr) - *dptr = comm_buf; - *ret = EFI_SUCCESS; + *dptr = comm_buf; return var_hdr->data; } @@ -212,10 +208,9 @@ static efi_status_t get_max_payload(size_t *size) payload_size = sizeof(*var_payload); var_payload = setup_mm_hdr(&comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, - &ret); + SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE); if (!var_payload) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; ret = mm_communicate(comm_buf, payload_size); if (ret != EFI_SUCCESS) @@ -239,7 +234,7 @@ static efi_status_t get_max_payload(size_t *size) */ *size -= 2; out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } @@ -259,9 +254,9 @@ static efi_status_t get_property_int(u16 *name, size_t name_size, smm_property = setup_mm_hdr( &comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret); + SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET); if (!smm_property) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid)); smm_property->name_size = name_size; @@ -282,7 +277,7 @@ static efi_status_t get_property_int(u16 *name, size_t name_size, memcpy(var_property, &smm_property->property, sizeof(*var_property)); out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } @@ -315,9 +310,9 @@ static efi_status_t tee_get_variable(u16 *name, efi_guid_t *vendor, payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize; var_acc = setup_mm_hdr(&comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret); + SMM_VARIABLE_FUNCTION_GET_VARIABLE); if (!var_acc) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; /* Fill in contents */ memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid)); @@ -347,7 +342,7 @@ static efi_status_t tee_get_variable(u16 *name, efi_guid_t *vendor, memcpy(data, (u8 *)var_acc->name + var_acc->name_size, var_acc->data_size); out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } @@ -380,10 +375,9 @@ static efi_status_t tee_get_next_variable(unsigned long *name_size, payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; var_getnext = setup_mm_hdr(&comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, - &ret); + SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); if (!var_getnext) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; /* Fill in contents */ memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid)); @@ -404,7 +398,7 @@ static efi_status_t tee_get_next_variable(unsigned long *name_size, memcpy(name, var_getnext->name, var_getnext->name_size); out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } @@ -437,9 +431,9 @@ static efi_status_t tee_set_variable(efi_char16_t *name, efi_guid_t *vendor, * the properties, if the allocation fails */ var_acc = setup_mm_hdr(&comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret); + SMM_VARIABLE_FUNCTION_SET_VARIABLE); if (!var_acc) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; /* * The API has the ability to override RO flags. If no RO check was @@ -467,7 +461,7 @@ static efi_status_t tee_set_variable(efi_char16_t *name, efi_guid_t *vendor, ret = mm_communicate(comm_buf, payload_size); dev_dbg(pvt_data.dev, "Set Variable %s %d %lx\n", __FILE__, __LINE__, ret); out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } @@ -492,10 +486,9 @@ static efi_status_t tee_query_variable_info(u32 attributes, payload_size = sizeof(*mm_query_info); mm_query_info = setup_mm_hdr(&comm_buf, payload_size, - SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, - &ret); + SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); if (!mm_query_info) - return EFI_OUT_OF_RESOURCES; + return EFI_DEVICE_ERROR; mm_query_info->attr = attributes; ret = mm_communicate(comm_buf, payload_size); @@ -507,7 +500,7 @@ static efi_status_t tee_query_variable_info(u32 attributes, *max_variable_size = mm_query_info->max_variable_size; out: - kfree(comm_buf); + free_pages_exact(comm_buf, COMM_BUF_SIZE(payload_size)); return ret; } diff --git a/drivers/firmware/google/cbmem.c b/drivers/firmware/google/cbmem.c index 773d05078e0a..54c3b8b05e5d 100644 --- a/drivers/firmware/google/cbmem.c +++ b/drivers/firmware/google/cbmem.c @@ -86,7 +86,7 @@ static const struct bin_attribute *const bin_attrs[] = { static const struct attribute_group cbmem_entry_group = { .attrs = attrs, - .bin_attrs_new = bin_attrs, + .bin_attrs = bin_attrs, }; static const struct attribute_group *dev_groups[] = { diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index e8fb00dcaf65..0ceccde5a302 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -530,7 +530,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, static const struct bin_attribute eventlog_bin_attr = { .attr = {.name = "append_to_eventlog", .mode = 0200}, - .write_new = eventlog_write, + .write = eventlog_write, }; static ssize_t gsmi_clear_eventlog_store(struct kobject *kobj, diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c index d957af6f9349..6138a1653ec5 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/google/memconsole.c @@ -28,7 +28,7 @@ static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, static struct bin_attribute memconsole_bin_attr = { .attr = {.name = "log", .mode = 0444}, - .read_new = memconsole_read, + .read = memconsole_read, }; void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t)) diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index 254ac6545d68..339a3f74b247 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -121,7 +121,7 @@ static int vpd_section_attrib_add(const u8 *key, u32 key_len, info->bin_attr.attr.name = info->key; info->bin_attr.attr.mode = 0444; info->bin_attr.size = value_len; - info->bin_attr.read_new = vpd_attrib_read; + info->bin_attr.read = vpd_attrib_read; info->bin_attr.private = info; info->value = value; @@ -201,7 +201,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, sec->bin_attr.attr.name = sec->raw_name; sec->bin_attr.attr.mode = 0444; sec->bin_attr.size = size; - sec->bin_attr.read_new = vpd_section_read; + sec->bin_attr.read = vpd_section_read; sec->bin_attr.private = sec; err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index f63b716be5b0..26cd0458aacd 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1603,7 +1603,13 @@ bool qcom_scm_lmh_dcvsh_available(void) } EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); -int qcom_scm_shm_bridge_enable(void) +/* + * This is only supposed to be called once by the TZMem module. It takes the + * SCM struct device as argument and uses it to pass the call as at the time + * the SHM Bridge is enabled, the SCM is not yet fully set up and doesn't + * accept global user calls. Don't try to use the __scm pointer here. + */ +int qcom_scm_shm_bridge_enable(struct device *scm_dev) { int ret; @@ -1615,11 +1621,11 @@ int qcom_scm_shm_bridge_enable(void) struct qcom_scm_res res; - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, + if (!__qcom_scm_is_call_available(scm_dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_SHM_BRIDGE_ENABLE)) return -EOPNOTSUPP; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(scm_dev, &desc, &res); if (ret) return ret; @@ -1631,7 +1637,7 @@ int qcom_scm_shm_bridge_enable(void) } EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_enable); -int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, +int qcom_scm_shm_bridge_create(u64 pfn_and_ns_perm_flags, u64 ipfn_and_s_perm_flags, u64 size_and_flags, u64 ns_vmids, u64 *handle) { @@ -1659,7 +1665,7 @@ int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, } EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_create); -int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle) +int qcom_scm_shm_bridge_delete(u64 handle) { struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_MP, @@ -2250,24 +2256,47 @@ static int qcom_scm_probe(struct platform_device *pdev) if (ret) return ret; - /* Paired with smp_load_acquire() in qcom_scm_is_available(). */ - smp_store_release(&__scm, scm); + ret = of_reserved_mem_device_init(scm->dev); + if (ret && ret != -ENODEV) + return dev_err_probe(scm->dev, ret, + "Failed to setup the reserved memory region for TZ mem\n"); + + ret = qcom_tzmem_enable(scm->dev); + if (ret) + return dev_err_probe(scm->dev, ret, + "Failed to enable the TrustZone memory allocator\n"); + + memset(&pool_config, 0, sizeof(pool_config)); + pool_config.initial_size = 0; + pool_config.policy = QCOM_TZMEM_POLICY_ON_DEMAND; + pool_config.max_size = SZ_256K; + + scm->mempool = devm_qcom_tzmem_pool_new(scm->dev, &pool_config); + if (IS_ERR(scm->mempool)) + return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), + "Failed to create the SCM memory pool\n"); irq = platform_get_irq_optional(pdev, 0); if (irq < 0) { - if (irq != -ENXIO) { - ret = irq; - goto err; - } + if (irq != -ENXIO) + return irq; } else { - ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, - IRQF_ONESHOT, "qcom-scm", __scm); - if (ret < 0) { - dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); - goto err; - } + ret = devm_request_threaded_irq(scm->dev, irq, NULL, qcom_scm_irq_handler, + IRQF_ONESHOT, "qcom-scm", scm); + if (ret < 0) + return dev_err_probe(scm->dev, ret, + "Failed to request qcom-scm irq\n"); } + /* + * Paired with smp_load_acquire() in qcom_scm_is_available(). + * + * This marks the SCM API as ready to accept user calls and can only + * be called after the TrustZone memory pool is initialized and the + * waitqueue interrupt requested. + */ + smp_store_release(&__scm, scm); + __get_convention(); /* @@ -2283,32 +2312,6 @@ static int qcom_scm_probe(struct platform_device *pdev) if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled") || !download_mode) qcom_scm_disable_sdi(); - ret = of_reserved_mem_device_init(__scm->dev); - if (ret && ret != -ENODEV) { - dev_err_probe(__scm->dev, ret, - "Failed to setup the reserved memory region for TZ mem\n"); - goto err; - } - - ret = qcom_tzmem_enable(__scm->dev); - if (ret) { - dev_err_probe(__scm->dev, ret, - "Failed to enable the TrustZone memory allocator\n"); - goto err; - } - - memset(&pool_config, 0, sizeof(pool_config)); - pool_config.initial_size = 0; - pool_config.policy = QCOM_TZMEM_POLICY_ON_DEMAND; - pool_config.max_size = SZ_256K; - - __scm->mempool = devm_qcom_tzmem_pool_new(__scm->dev, &pool_config); - if (IS_ERR(__scm->mempool)) { - ret = dev_err_probe(__scm->dev, PTR_ERR(__scm->mempool), - "Failed to create the SCM memory pool\n"); - goto err; - } - /* * Initialize the QSEECOM interface. * @@ -2323,12 +2326,6 @@ static int qcom_scm_probe(struct platform_device *pdev) WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); return 0; - -err: - /* Paired with smp_load_acquire() in qcom_scm_is_available(). */ - smp_store_release(&__scm, NULL); - - return ret; } static void qcom_scm_shutdown(struct platform_device *pdev) diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index 3133d826f5fa..0e8dd838099e 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -83,6 +83,7 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, struct qcom_scm_res *res); struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void); +int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01 diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 94196ad87105..ea0a35355657 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -20,6 +20,7 @@ #include #include +#include "qcom_scm.h" #include "qcom_tzmem.h" struct qcom_tzmem_area { @@ -94,7 +95,7 @@ static int qcom_tzmem_init(void) goto notsupp; } - ret = qcom_scm_shm_bridge_enable(); + ret = qcom_scm_shm_bridge_enable(qcom_tzmem_dev); if (ret == -EOPNOTSUPP) goto notsupp; @@ -124,9 +125,9 @@ static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) if (!handle) return -ENOMEM; - ret = qcom_scm_shm_bridge_create(qcom_tzmem_dev, pfn_and_ns_perm, - ipfn_and_s_perm, size_and_flags, - QCOM_SCM_VMID_HLOS, handle); + ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, + size_and_flags, QCOM_SCM_VMID_HLOS, + handle); if (ret) return ret; @@ -142,7 +143,7 @@ static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area) if (!qcom_tzmem_using_shm_bridge) return; - qcom_scm_shm_bridge_delete(qcom_tzmem_dev, *handle); + qcom_scm_shm_bridge_delete(*handle); kfree(handle); } diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 2615fb780e3c..0eebd572f9a5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -476,7 +476,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, static const struct bin_attribute fw_cfg_sysfs_attr_raw = { .attr = { .name = "raw", .mode = S_IRUSR }, - .read_new = fw_cfg_sysfs_read_raw, + .read = fw_cfg_sysfs_read_raw, }; /* diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index e02f14f4bd7c..3a69fe3234c7 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -430,6 +430,9 @@ int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer) return -EOPNOTSUPP; } + msg.chan_id = xfer->acpm_chan_id; + msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL; + scoped_guard(mutex, &achan->tx_lock) { tx_front = readl(achan->tx.front); idx = (tx_front + 1) % achan->qlen; @@ -446,25 +449,15 @@ int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer) /* Advance TX front. */ writel(idx, achan->tx.front); + + ret = mbox_send_message(achan->chan, (void *)&msg); + if (ret < 0) + return ret; + + mbox_client_txdone(achan->chan, 0); } - msg.chan_id = xfer->acpm_chan_id; - msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL; - ret = mbox_send_message(achan->chan, (void *)&msg); - if (ret < 0) - return ret; - - ret = acpm_wait_for_message_response(achan, xfer); - - /* - * NOTE: we might prefer not to need the mailbox ticker to manage the - * transfer queueing since the protocol layer queues things by itself. - * Unfortunately, we have to kick the mailbox framework after we have - * received our message. - */ - mbox_client_txdone(achan->chan, ret); - - return ret; + return acpm_wait_for_message_response(achan, xfer); } /** diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c index cd65b434dc6e..bdee057db2fd 100644 --- a/drivers/firmware/smccc/smccc.c +++ b/drivers/firmware/smccc/smccc.c @@ -72,10 +72,7 @@ bool arm_smccc_hypervisor_has_uuid(const uuid_t *hyp_uuid) struct arm_smccc_res res = {}; uuid_t uuid; - if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_HVC) - return false; - - arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res); + arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res); if (res.a0 == SMCCC_RET_NOT_SUPPORTED) return false; diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index cde1ab8bd9d1..91f2320c0d0f 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig @@ -2,7 +2,7 @@ menu "Tegra firmware driver" config TEGRA_IVC - bool "Tegra IVC protocol" + bool "Tegra IVC protocol" if COMPILE_TEST depends on ARCH_TEGRA help IVC (Inter-VM Communication) protocol is part of the IPC @@ -13,8 +13,9 @@ config TEGRA_IVC config TEGRA_BPMP bool "Tegra BPMP driver" - depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC + depends on ARCH_TEGRA && TEGRA_HSP_MBOX depends on !CPU_BIG_ENDIAN + select TEGRA_IVC help BPMP (Boot and Power Management Processor) is designed to off-loading the PM functions which include clock/DVFS/thermal/power from the CPU. diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 620cf3fdd607..41e2e4dc31d6 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -4,6 +4,7 @@ tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_234_SOC) += bpmp-tegra186.o +tegra-bpmp-$(CONFIG_ARCH_TEGRA_264_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o obj-$(CONFIG_TEGRA_IVC) += ivc.o diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h index 182bfe396516..07c3d46abb87 100644 --- a/drivers/firmware/tegra/bpmp-private.h +++ b/drivers/firmware/tegra/bpmp-private.h @@ -23,13 +23,7 @@ struct tegra_bpmp_ops { int (*resume)(struct tegra_bpmp *bpmp); }; -#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) extern const struct tegra_bpmp_ops tegra186_bpmp_ops; -#endif -#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) extern const struct tegra_bpmp_ops tegra210_bpmp_ops; -#endif #endif diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c index 6f0d0511b486..7cfc5fdfa49d 100644 --- a/drivers/firmware/tegra/bpmp-tegra186.c +++ b/drivers/firmware/tegra/bpmp-tegra186.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -192,16 +192,11 @@ static void tegra186_bpmp_teardown_channels(struct tegra_bpmp *bpmp) static int tegra186_bpmp_dram_init(struct tegra_bpmp *bpmp) { struct tegra186_bpmp *priv = bpmp->priv; - struct device_node *np; struct resource res; size_t size; int err; - np = of_parse_phandle(bpmp->dev->of_node, "memory-region", 0); - if (!np) - return -ENODEV; - - err = of_address_to_resource(np, 0, &res); + err = of_reserved_mem_region_to_resource(bpmp->dev->of_node, 0, &res); if (err < 0) { dev_warn(bpmp->dev, "failed to parse memory region: %d\n", err); return err; diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index c3a1dc344961..e74bba7ccc44 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -836,7 +836,8 @@ static const struct dev_pm_ops tegra_bpmp_pm_ops = { #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC) static const struct tegra_bpmp_soc tegra186_soc = { .channels = { .cpu_tx = { @@ -884,7 +885,8 @@ static const struct tegra_bpmp_soc tegra210_soc = { static const struct of_device_id tegra_bpmp_match[] = { #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC) { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, #endif #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 7356e860e65c..02da3e48bc8f 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1299,11 +1300,10 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); * This API function is to be used for notify the power management controller * about the completed power management initialization. */ -int zynqmp_pm_init_finalize(void) +static int zynqmp_pm_init_finalize(void) { return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0); } -EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize); /** * zynqmp_pm_set_suspend_mode() - Set system suspend mode @@ -2100,6 +2100,19 @@ static void zynqmp_firmware_remove(struct platform_device *pdev) platform_device_unregister(em_dev); } +static void zynqmp_firmware_sync_state(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!of_device_is_compatible(np, "xlnx,zynqmp-firmware")) + return; + + of_genpd_sync_state(np); + + if (zynqmp_pm_init_finalize()) + dev_warn(dev, "failed to release power management to firmware\n"); +} + static const struct of_device_id zynqmp_firmware_of_match[] = { {.compatible = "xlnx,zynqmp-firmware"}, {.compatible = "xlnx,versal-firmware"}, @@ -2112,6 +2125,7 @@ static struct platform_driver zynqmp_firmware_driver = { .name = "zynqmp_firmware", .of_match_table = zynqmp_firmware_of_match, .dev_groups = zynqmp_firmware_groups, + .sync_state = zynqmp_firmware_sync_state, }, .probe = zynqmp_firmware_probe, .remove = zynqmp_firmware_remove, diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index f7e08f7ea9ef..b7629a0e4813 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -405,12 +405,12 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) } } - priv->dma_nelms = - dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); - if (priv->dma_nelms == 0) { + err = dma_map_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0); + if (err) { dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n"); - return -ENOMEM; + return err; } + priv->dma_nelms = sgt->nents; /* enable clock */ err = clk_enable(priv->clk); @@ -478,7 +478,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) clk_disable(priv->clk); out_free: - dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + dma_unmap_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0); return err; } diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 50e8736039fe..c6c115993ebc 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -613,8 +613,8 @@ static const struct bin_attribute fsi_slave_raw_attr = { .mode = 0600, }, .size = 0, - .read_new = fsi_slave_sysfs_raw_read, - .write_new = fsi_slave_sysfs_raw_write, + .read = fsi_slave_sysfs_raw_read, + .write = fsi_slave_sysfs_raw_write, }; static void fsi_slave_release(struct device *dev) @@ -1404,7 +1404,7 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv) } EXPORT_SYMBOL_GPL(fsi_driver_unregister); -struct bus_type fsi_bus_type = { +const struct bus_type fsi_bus_type = { .name = "fsi", .match = fsi_bus_match, }; diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c index 9f2fd444ceb6..e67d7cd30fca 100644 --- a/drivers/fsi/fsi-master-ast-cf.c +++ b/drivers/fsi/fsi-master-ast-cf.c @@ -13,13 +13,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include "fsi-master.h" @@ -1285,14 +1285,7 @@ static int fsi_master_acf_probe(struct platform_device *pdev) master->gpio_mux = gpio; /* Grab the reserved memory region (use DMA API instead ?) */ - np = of_parse_phandle(mnode, "memory-region", 0); - if (!np) { - dev_err(&pdev->dev, "Didn't find reserved memory\n"); - rc = -EINVAL; - goto err_free; - } - rc = of_address_to_resource(np, 0, &res); - of_node_put(np); + rc = of_reserved_mem_region_to_resource(mnode, 0, &res); if (rc) { dev_err(&pdev->dev, "Couldn't address to resource for reserved memory\n"); rc = -ENOMEM; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 44f922e10db2..d8ac40d0eb6f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -3,6 +3,9 @@ # GPIO infrastructure and drivers # +config GPIOLIB_LEGACY + def_bool y + menuconfig GPIOLIB bool "GPIO Support" help @@ -69,6 +72,14 @@ config GPIO_SYSFS use the character device /dev/gpiochipN with the appropriate ioctl() operations instead. +config GPIO_SYSFS_LEGACY + bool "Enable legacy functionalities of the sysfs interface" + depends on GPIO_SYSFS + default y if GPIO_SYSFS + help + Say Y here if you want to enable the legacy, global GPIO + numberspace-based functionalities of the sysfs interface. + config GPIO_CDEV bool "Character device (/dev/gpiochipN) support" if EXPERT default y @@ -1263,6 +1274,7 @@ config GPIO_ADP5520 config GPIO_ADP5585 tristate "GPIO Support for ADP5585" depends on MFD_ADP5585 + select GPIOLIB_IRQCHIP help This option enables support for the GPIO function found in the Analog Devices ADP5585. @@ -1464,6 +1476,16 @@ config GPIO_LP87565 This driver can also be built as a module. If so, the module will be called gpio-lp87565. +config GPIO_MACSMC + tristate "Apple Mac SMC GPIO" + depends on MFD_MACSMC + help + Support for GPIOs controlled by the SMC microcontroller on Apple Mac + systems. + + This driver can also be built as a module. If so, the module will be + called gpio-macsmc. + config GPIO_MADERA tristate "Cirrus Logic Madera class codecs" depends on PINCTRL_MADERA @@ -1501,7 +1523,7 @@ config GPIO_MAX77759 called gpio-max77759. config GPIO_PALMAS - bool "TI PALMAS series PMICs GPIO" + tristate "TI PALMAS series PMICs GPIO" depends on MFD_PALMAS help Select this option to enable GPIO driver for the TI PALMAS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 88dedd298256..379f55e9ed1e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,7 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o -obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o +obj-$(CONFIG_GPIOLIB_LEGACY) += gpiolib-legacy.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o @@ -99,6 +99,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 4a8b349f2483..7a09a4f58551 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -131,6 +131,11 @@ Work items: helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use this with dry-coding and sending to maintainers to test +- Move the MMIO GPIO specific fields out of struct gpio_chip into a + dedicated structure. Currently every GPIO chip has them if gpio-mmio is + enabled in Kconfig even if it itself doesn't register with the helper + library. + ------------------------------------------------------------------------------- Generic regmap GPIO @@ -183,16 +188,12 @@ remove the old ones and finally rename the new ones back to the old names. ------------------------------------------------------------------------------- -Extend the sysfs ABI to allow exporting lines by their HW offsets +Remove legacy sysfs features -The need to support the sysfs GPIO class is one of the main obstacles to -removing the global GPIO numberspace from the kernel. In order to wean users -off using global numbers from user-space, extend the existing interface with -new per-gpiochip export/unexport attributes that allow to refer to GPIOs using -their hardware offsets within the chip. - -Encourage users to switch to using them and eventually remove the existing -global export/unexport attribues. +We have two parallel per-chip class devices and per-exported-line attribute +groups in sysfs. One is using the obsolete global GPIO numberspace and the +second relies on hardware offsets of pins within the chip. Remove the former +once user-space has switched to using the latter. ------------------------------------------------------------------------------- diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 4dd5c2c330bb..c226524efeba 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -141,8 +141,8 @@ static int gen_74x164_probe(struct spi_device *spi) chip->gpio_chip.label = spi->modalias; chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; - chip->gpio_chip.set_rv = gen_74x164_set_value; - chip->gpio_chip.set_multiple_rv = gen_74x164_set_multiple; + chip->gpio_chip.set = gen_74x164_set_value; + chip->gpio_chip.set_multiple = gen_74x164_set_multiple; chip->gpio_chip.base = -1; chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; chip->gpio_chip.can_sleep = true; diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index c7ac5a9ffb1f..bd2cc5f4f851 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,8 +19,8 @@ #define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { - struct gpio_chip gc; - unsigned flags; + struct gpio_generic_chip gen_gc; + unsigned int flags; }; static const struct of_device_id mmio_74xx_gpio_ids[] = { @@ -99,16 +100,15 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - if (priv->flags & MMIO_74XX_DIR_OUT) { - gc->set(gc, gpio, val); - return 0; - } + if (priv->flags & MMIO_74XX_DIR_OUT) + return gpio_generic_chip_set(&priv->gen_gc, gpio, val); return -ENOTSUPP; } static int mmio_74xx_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct mmio_74xx_gpio_priv *priv; void __iomem *dat; int err; @@ -123,19 +123,21 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) if (IS_ERR(dat)) return PTR_ERR(dat); - err = bgpio_init(&priv->gc, &pdev->dev, - DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8), - dat, NULL, NULL, NULL, NULL, 0); + config.dev = &pdev->dev; + config.sz = DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8); + config.dat = dat; + + err = gpio_generic_chip_init(&priv->gen_gc, &config); if (err) return err; - priv->gc.direction_input = mmio_74xx_dir_in; - priv->gc.direction_output = mmio_74xx_dir_out; - priv->gc.get_direction = mmio_74xx_get_direction; - priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); - priv->gc.owner = THIS_MODULE; + priv->gen_gc.gc.direction_input = mmio_74xx_dir_in; + priv->gen_gc.gc.direction_output = mmio_74xx_dir_out; + priv->gen_gc.gc.get_direction = mmio_74xx_get_direction; + priv->gen_gc.gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); + priv->gen_gc.gc.owner = THIS_MODULE; - return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); + return devm_gpiochip_add_data(&pdev->dev, &priv->gen_gc.gc, priv); } static struct platform_driver mmio_74xx_gpio_driver = { diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index dc2b941c3726..e5ac2d211013 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -430,7 +430,7 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, chip->direction_input = adnp_gpio_direction_input; chip->direction_output = adnp_gpio_direction_output; chip->get = adnp_gpio_get; - chip->set_rv = adnp_gpio_set; + chip->set = adnp_gpio_set; chip->can_sleep = true; if (IS_ENABLED(CONFIG_DEBUG_FS)) diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index 57d12c10cbda..6305c8b7dc05 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -122,7 +122,7 @@ static int adp5520_gpio_probe(struct platform_device *pdev) gc->direction_input = adp5520_gpio_direction_input; gc->direction_output = adp5520_gpio_direction_output; gc->get = adp5520_gpio_get_value; - gc->set_rv = adp5520_gpio_set_value; + gc->set = adp5520_gpio_set_value; gc->can_sleep = true; gc->base = pdata->gpio_start; diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index d5c0f1b267c8..0fd3cc26d017 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -4,67 +4,131 @@ * * Copyright 2022 NXP * Copyright 2024 Ideas on Board Oy + * Copyright 2025 Analog Devices, Inc. */ +#include +#include +#include #include #include #include #include +#include +#include #include #include #include -#define ADP5585_GPIO_MAX 11 +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the + * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to + * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver + * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is + * marked as reserved in the device tree for variants that don't support it. + */ +#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0) +#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n)) + +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 8/R7", numbered 0 to 7 by the + * driver, bank 1 covers pins "GPIO 9/C0" to "GPIO 16/C7", numbered 8 to + * 15 and bank 3 covers pins "GPIO 17/C8" to "GPIO 19/C10", numbered 16 to 18. + */ +#define ADP5589_BANK(n) ((n) >> 3) +#define ADP5589_BIT(n) BIT((n) & 0x7) + +struct adp5585_gpio_chip { + int (*bank)(unsigned int off); + int (*bit)(unsigned int off); + unsigned int debounce_dis_a; + unsigned int rpull_cfg_a; + unsigned int gpo_data_a; + unsigned int gpo_out_a; + unsigned int gpio_dir_a; + unsigned int gpi_stat_a; + unsigned int gpi_int_lvl_a; + unsigned int gpi_ev_a; + unsigned int gpi_ev_min; + unsigned int gpi_ev_max; + bool has_bias_hole; +}; struct adp5585_gpio_dev { struct gpio_chip gpio_chip; + struct notifier_block nb; + const struct adp5585_gpio_chip *info; struct regmap *regmap; + unsigned long irq_mask; + unsigned long irq_en; + unsigned long irq_active_high; + /* used for irqchip bus locking */ + struct mutex bus_lock; }; +static int adp5585_gpio_bank(unsigned int off) +{ + return ADP5585_BANK(off); +} + +static int adp5585_gpio_bit(unsigned int off) +{ + return ADP5585_BIT(off); +} + +static int adp5589_gpio_bank(unsigned int off) +{ + return ADP5589_BANK(off); +} + +static int adp5589_gpio_bit(unsigned int off) +{ + return ADP5589_BIT(off); +} + static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; unsigned int val; - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val); + regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), &val); - return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + return val & info->bit(off) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; - return regmap_clear_bits(adp5585_gpio->regmap, - ADP5585_GPIO_DIRECTION_A + bank, bit); + return regmap_clear_bits(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), + info->bit(off)); } static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bank = info->bank(off); + unsigned int bit = info->bit(off); int ret; - ret = regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_DATA_OUT_A + bank, bit, - val ? bit : 0); + ret = regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + bank, + bit, val ? bit : 0); if (ret) return ret; - return regmap_set_bits(adp5585_gpio->regmap, - ADP5585_GPIO_DIRECTION_A + bank, bit); + return regmap_set_bits(adp5585_gpio->regmap, info->gpio_dir_a + bank, + bit); } static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bank = info->bank(off); + unsigned int bit = info->bit(off); unsigned int reg; unsigned int val; @@ -79,8 +143,8 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) * .direction_input(), .direction_output() or .set() operations racing * with this. */ - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val); - reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A; + regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + bank, &val); + reg = val & bit ? info->gpo_data_a : info->gpi_stat_a; regmap_read(adp5585_gpio->regmap, reg + bank, &val); return !!(val & bit); @@ -90,17 +154,17 @@ static int adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); - return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_DATA_OUT_A + bank, + return regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + info->bank(off), bit, val ? bit : 0); } static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, unsigned int bias) { + const struct adp5585_gpio_chip *info = adp5585_gpio->info; unsigned int bit, reg, mask, val; /* @@ -108,8 +172,10 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, * consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits * after R5. */ - bit = off * 2 + (off > 5 ? 4 : 0); - reg = ADP5585_RPULL_CONFIG_A + bit / 8; + bit = off * 2; + if (info->has_bias_hole) + bit += (off > 5 ? 4 : 0); + reg = info->rpull_cfg_a + bit / 8; mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8); val = bias << (bit % 8); @@ -119,22 +185,22 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, enum pin_config_param drive) { - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_OUT_MODE_A + bank, bit, + info->gpo_out_a + info->bank(off), bit, drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0); } static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, unsigned int debounce) { - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_DEBOUNCE_DIS_A + bank, bit, + info->debounce_dis_a + info->bank(off), bit, debounce ? 0 : bit); } @@ -172,11 +238,175 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off, }; } +static int adp5585_gpio_request(struct gpio_chip *chip, unsigned int off) +{ + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + struct device *dev = chip->parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + const struct adp5585_regs *regs = adp5585->regs; + int ret; + + ret = test_and_set_bit(off, adp5585->pin_usage); + if (ret) + return -EBUSY; + + /* make sure it's configured for GPIO */ + return regmap_clear_bits(adp5585_gpio->regmap, + regs->pin_cfg_a + info->bank(off), + info->bit(off)); +} + +static void adp5585_gpio_free(struct gpio_chip *chip, unsigned int off) +{ + struct device *dev = chip->parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + + clear_bit(off, adp5585->pin_usage); +} + +static int adp5585_gpio_key_event(struct notifier_block *nb, unsigned long key, + void *data) +{ + struct adp5585_gpio_dev *adp5585_gpio = container_of(nb, struct adp5585_gpio_dev, nb); + struct device *dev = adp5585_gpio->gpio_chip.parent; + unsigned long key_press = (unsigned long)data; + unsigned int irq, irq_type; + struct irq_data *irqd; + bool active_high; + unsigned int off; + + /* make sure the event is for me */ + if (key < adp5585_gpio->info->gpi_ev_min || key > adp5585_gpio->info->gpi_ev_max) + return NOTIFY_DONE; + + off = key - adp5585_gpio->info->gpi_ev_min; + active_high = test_bit(off, &adp5585_gpio->irq_active_high); + + irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off); + if (!irq) + return NOTIFY_BAD; + + irqd = irq_get_irq_data(irq); + if (!irqd) { + dev_err(dev, "Could not get irq(%u) data\n", irq); + return NOTIFY_BAD; + } + + dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%lu, a_high=%u\n", + off, key_press, active_high); + + if (!active_high) + key_press = !key_press; + + irq_type = irqd_get_trigger_type(irqd); + + if ((irq_type & IRQ_TYPE_EDGE_RISING && key_press) || + (irq_type & IRQ_TYPE_EDGE_FALLING && !key_press)) + handle_nested_irq(irq); + + return NOTIFY_STOP; +} + +static void adp5585_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + + mutex_lock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + bool active_high = test_bit(hwirq, &adp5585_gpio->irq_active_high); + bool enabled = test_bit(hwirq, &adp5585_gpio->irq_en); + bool masked = test_bit(hwirq, &adp5585_gpio->irq_mask); + unsigned int bank = adp5585_gpio->info->bank(hwirq); + unsigned int bit = adp5585_gpio->info->bit(hwirq); + + if (masked && !enabled) + goto out_unlock; + if (!masked && enabled) + goto out_unlock; + + regmap_update_bits(adp5585_gpio->regmap, info->gpi_int_lvl_a + bank, bit, + active_high ? bit : 0); + regmap_update_bits(adp5585_gpio->regmap, info->gpi_ev_a + bank, bit, + masked ? 0 : bit); + assign_bit(hwirq, &adp5585_gpio->irq_en, !masked); + +out_unlock: + mutex_unlock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + __set_bit(hwirq, &adp5585_gpio->irq_mask); + gpiochip_disable_irq(gc, hwirq); +} + +static void adp5585_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + __clear_bit(hwirq, &adp5585_gpio->irq_mask); +} + +static int adp5585_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + if (!(type & IRQ_TYPE_EDGE_BOTH)) + return -EINVAL; + + assign_bit(hwirq, &adp5585_gpio->irq_active_high, + type == IRQ_TYPE_EDGE_RISING); + + irq_set_handler_locked(d, handle_edge_irq); + return 0; +} + +static const struct irq_chip adp5585_irq_chip = { + .name = "adp5585", + .irq_mask = adp5585_irq_mask, + .irq_unmask = adp5585_irq_unmask, + .irq_bus_lock = adp5585_irq_bus_lock, + .irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock, + .irq_set_type = adp5585_irq_set_type, + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void adp5585_gpio_unreg_notifier(void *data) +{ + struct adp5585_gpio_dev *adp5585_gpio = data; + struct device *dev = adp5585_gpio->gpio_chip.parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + + blocking_notifier_chain_unregister(&adp5585->event_notifier, + &adp5585_gpio->nb); +} + static int adp5585_gpio_probe(struct platform_device *pdev) { struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); + const struct platform_device_id *id = platform_get_device_id(pdev); struct adp5585_gpio_dev *adp5585_gpio; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; struct gpio_chip *gc; int ret; @@ -186,6 +416,10 @@ static int adp5585_gpio_probe(struct platform_device *pdev) adp5585_gpio->regmap = adp5585->regmap; + adp5585_gpio->info = (const struct adp5585_gpio_chip *)id->driver_data; + if (!adp5585_gpio->info) + return -ENODEV; + device_set_of_node_from_dev(dev, dev->parent); gc = &adp5585_gpio->gpio_chip; @@ -194,15 +428,45 @@ static int adp5585_gpio_probe(struct platform_device *pdev) gc->direction_input = adp5585_gpio_direction_input; gc->direction_output = adp5585_gpio_direction_output; gc->get = adp5585_gpio_get_value; - gc->set_rv = adp5585_gpio_set_value; + gc->set = adp5585_gpio_set_value; gc->set_config = adp5585_gpio_set_config; + gc->request = adp5585_gpio_request; + gc->free = adp5585_gpio_free; gc->can_sleep = true; gc->base = -1; - gc->ngpio = ADP5585_GPIO_MAX; + gc->ngpio = adp5585->n_pins; gc->label = pdev->name; gc->owner = THIS_MODULE; + if (device_property_present(dev->parent, "interrupt-controller")) { + if (!adp5585->irq) + return dev_err_probe(dev, -EINVAL, + "Unable to serve as interrupt controller without IRQ\n"); + + girq = &adp5585_gpio->gpio_chip.irq; + gpio_irq_chip_set_chip(girq, &adp5585_irq_chip); + girq->handler = handle_bad_irq; + girq->threaded = true; + + adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event; + ret = blocking_notifier_chain_register(&adp5585->event_notifier, + &adp5585_gpio->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier, + adp5585_gpio); + if (ret) + return ret; + } + + /* everything masked by default */ + adp5585_gpio->irq_mask = ~0UL; + + ret = devm_mutex_init(dev, &adp5585_gpio->bus_lock); + if (ret) + return ret; ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip, adp5585_gpio); if (ret) @@ -211,8 +475,40 @@ static int adp5585_gpio_probe(struct platform_device *pdev) return 0; } +static const struct adp5585_gpio_chip adp5585_gpio_chip_info = { + .bank = adp5585_gpio_bank, + .bit = adp5585_gpio_bit, + .debounce_dis_a = ADP5585_DEBOUNCE_DIS_A, + .rpull_cfg_a = ADP5585_RPULL_CONFIG_A, + .gpo_data_a = ADP5585_GPO_DATA_OUT_A, + .gpo_out_a = ADP5585_GPO_OUT_MODE_A, + .gpio_dir_a = ADP5585_GPIO_DIRECTION_A, + .gpi_stat_a = ADP5585_GPI_STATUS_A, + .has_bias_hole = true, + .gpi_ev_min = ADP5585_GPI_EVENT_START, + .gpi_ev_max = ADP5585_GPI_EVENT_END, + .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A, + .gpi_ev_a = ADP5585_GPI_EVENT_EN_A, +}; + +static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { + .bank = adp5589_gpio_bank, + .bit = adp5589_gpio_bit, + .debounce_dis_a = ADP5589_DEBOUNCE_DIS_A, + .rpull_cfg_a = ADP5589_RPULL_CONFIG_A, + .gpo_data_a = ADP5589_GPO_DATA_OUT_A, + .gpo_out_a = ADP5589_GPO_OUT_MODE_A, + .gpio_dir_a = ADP5589_GPIO_DIRECTION_A, + .gpi_stat_a = ADP5589_GPI_STATUS_A, + .gpi_ev_min = ADP5589_GPI_EVENT_START, + .gpi_ev_max = ADP5589_GPI_EVENT_END, + .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A, + .gpi_ev_a = ADP5589_GPI_EVENT_EN_A, +}; + static const struct platform_device_id adp5585_gpio_id_table[] = { - { "adp5585-gpio" }, + { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, + { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 6f941db02c04..af9d8b3a711d 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -534,8 +534,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, chip->direction_output = gpio_fwd_direction_output; chip->get = gpio_fwd_get; chip->get_multiple = gpio_fwd_get_multiple_locked; - chip->set_rv = gpio_fwd_set; - chip->set_multiple_rv = gpio_fwd_set_multiple_locked; + chip->set = gpio_fwd_set; + chip->set_multiple = gpio_fwd_set_multiple_locked; chip->to_irq = gpio_fwd_to_irq; chip->base = -1; chip->ngpio = ngpios; diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 77a674cf99e4..4524c18a87e7 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -69,7 +69,7 @@ static const struct gpio_chip altr_a10sr_gc = { .label = "altr_a10sr_gpio", .owner = THIS_MODULE, .get = altr_a10sr_gpio_get, - .set_rv = altr_a10sr_gpio_set, + .set = altr_a10sr_gpio_set, .direction_input = altr_a10sr_gpio_direction_input, .direction_output = altr_a10sr_gpio_direction_output, .can_sleep = true, diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 1b28525726d7..9508d764cce4 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -259,7 +259,7 @@ static int altera_gpio_probe(struct platform_device *pdev) altera_gc->gc.direction_input = altera_gpio_direction_input; altera_gc->gc.direction_output = altera_gpio_direction_output; altera_gc->gc.get = altera_gpio_get; - altera_gc->gc.set_rv = altera_gpio_set; + altera_gc->gc.set = altera_gpio_set; altera_gc->gc.owner = THIS_MODULE; altera_gc->gc.parent = &pdev->dev; altera_gc->gc.base = -1; diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c index f8d0cea46049..e6c6c3ec7656 100644 --- a/drivers/gpio/gpio-amd-fch.c +++ b/drivers/gpio/gpio-amd-fch.c @@ -165,7 +165,7 @@ static int amd_fch_gpio_probe(struct platform_device *pdev) priv->gc.direction_output = amd_fch_gpio_direction_output; priv->gc.get_direction = amd_fch_gpio_get_direction; priv->gc.get = amd_fch_gpio_get; - priv->gc.set_rv = amd_fch_gpio_set; + priv->gc.set = amd_fch_gpio_set; spin_lock_init(&priv->lock); diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 425d8472f744..15fd5e210d74 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -165,7 +165,7 @@ static struct amd_gpio gp = { .ngpio = 32, .request = amd_gpio_request, .free = amd_gpio_free, - .set_rv = amd_gpio_set, + .set = amd_gpio_set, .get = amd_gpio_get, .direction_output = amd_gpio_dirout, .direction_input = amd_gpio_dirin, diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index e530c94dcce8..a7e98d395d8e 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -39,7 +39,6 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return ret; if (change && persistent) { - pm_runtime_mark_last_busy(chip->parent); pm_runtime_put_autosuspend(chip->parent); } @@ -82,7 +81,6 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) return ret; } - pm_runtime_mark_last_busy(chip->parent); pm_runtime_put_autosuspend(chip->parent); } @@ -140,7 +138,7 @@ static const struct gpio_chip template_chip = { .direction_input = arizona_gpio_direction_in, .get = arizona_gpio_get, .direction_output = arizona_gpio_direction_out, - .set_rv = arizona_gpio_set, + .set = arizona_gpio_set, .can_sleep = true, }; diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 00b31497ecff..7622f9e9f54a 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -596,7 +596,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) gpio->chip.request = NULL; gpio->chip.free = NULL; gpio->chip.get = aspeed_sgpio_get; - gpio->chip.set_rv = aspeed_sgpio_set; + gpio->chip.set = aspeed_sgpio_set; gpio->chip.set_config = aspeed_sgpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 2d340a343a17..7953a9c4e36d 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -1352,7 +1352,7 @@ static int aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.request = aspeed_gpio_request; gpio->chip.free = aspeed_gpio_free; gpio->chip.get = aspeed_gpio_get; - gpio->chip.set_rv = aspeed_gpio_set; + gpio->chip.set = aspeed_gpio_set; gpio->chip.set_config = aspeed_gpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 8f22cb36004d..208b71c59d58 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -339,7 +339,7 @@ static const struct gpio_chip template_chip = { .direction_input = bcm_kona_gpio_direction_input, .get = bcm_kona_gpio_get, .direction_output = bcm_kona_gpio_direction_output, - .set_rv = bcm_kona_gpio_set, + .set = bcm_kona_gpio_set, .set_config = bcm_kona_gpio_set_config, .to_irq = bcm_kona_gpio_to_irq, .base = 0, diff --git a/drivers/gpio/gpio-bd71815.c b/drivers/gpio/gpio-bd71815.c index 36701500925e..afb18a5a9d79 100644 --- a/drivers/gpio/gpio-bd71815.c +++ b/drivers/gpio/gpio-bd71815.c @@ -85,7 +85,7 @@ static const struct gpio_chip bd71815gpo_chip = { .owner = THIS_MODULE, .get = bd71815gpo_get, .get_direction = bd71815gpo_direction_get, - .set_rv = bd71815gpo_set, + .set = bd71815gpo_set, .set_config = bd71815_gpio_set_config, .can_sleep = true, }; diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c index 4ba151e5cf25..e439dbfffc62 100644 --- a/drivers/gpio/gpio-bd71828.c +++ b/drivers/gpio/gpio-bd71828.c @@ -109,7 +109,7 @@ static int bd71828_probe(struct platform_device *pdev) bdgpio->gpio.set_config = bd71828_gpio_set_config; bdgpio->gpio.can_sleep = true; bdgpio->gpio.get = bd71828_gpio_get; - bdgpio->gpio.set_rv = bd71828_gpio_set; + bdgpio->gpio.set = bd71828_gpio_set; bdgpio->gpio.base = -1; /* diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 8df1361e3e84..7c95bb36511e 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -88,7 +88,7 @@ static const struct gpio_chip template_chip = { .direction_input = bd9571mwv_gpio_direction_input, .direction_output = bd9571mwv_gpio_direction_output, .get = bd9571mwv_gpio_get, - .set_rv = bd9571mwv_gpio_set, + .set = bd9571mwv_gpio_set, .base = -1, .ngpio = 2, .can_sleep = true, diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index e7671bcd5c07..e29a9589b3cc 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -436,10 +436,8 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, struct device_node *np = dev->of_node; int err; - priv->irq_domain = - irq_domain_create_linear(of_fwnode_handle(np), priv->num_gpios, - &brcmstb_gpio_irq_domain_ops, - priv); + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), priv->num_gpios, + &brcmstb_gpio_irq_domain_ops, priv); if (!priv->irq_domain) { dev_err(dev, "Couldn't allocate IRQ domain\n"); return -ENXIO; diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7c9e81fea37a..05401da03ca3 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -145,7 +145,7 @@ static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) c->direction_input = bt8xxgpio_gpio_direction_input; c->get = bt8xxgpio_gpio_get; c->direction_output = bt8xxgpio_gpio_direction_output; - c->set_rv = bt8xxgpio_gpio_set; + c->set = bt8xxgpio_gpio_set; c->dbg_show = NULL; c->base = modparam_gpiobase; c->ngpio = BT8XXGPIO_NR_GPIOS; diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index e9dd2564c54f..c647953521c7 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -8,9 +8,11 @@ * Boris Brezillon */ -#include +#include #include +#include #include +#include #include #include #include @@ -30,7 +32,7 @@ #define CDNS_GPIO_IRQ_ANY_EDGE 0x2c struct cdns_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; void __iomem *regs; u32 bypass_orig; }; @@ -38,29 +40,24 @@ struct cdns_gpio_chip { static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(gpio_generic_lock)(&cgpio->gen_gc); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset), cgpio->regs + CDNS_GPIO_BYPASS_MODE); - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); return 0; } static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(gpio_generic_lock)(&cgpio->gen_gc); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) | (BIT(offset) & cgpio->bypass_orig), cgpio->regs + CDNS_GPIO_BYPASS_MODE); - - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); } static void cdns_gpio_irq_mask(struct irq_data *d) @@ -85,13 +82,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; u32 int_value; u32 int_type; u32 mask = BIT(d->hwirq); int ret = 0; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(gpio_generic_lock)(&cgpio->gen_gc); int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask; int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask; @@ -108,15 +104,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) } else if (type == IRQ_TYPE_LEVEL_LOW) { int_type |= mask; } else { - ret = -EINVAL; - goto err_irq_type; + return -EINVAL; } iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE); iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE); -err_irq_type: - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); return ret; } @@ -150,6 +143,7 @@ static const struct irq_chip cdns_gpio_irqchip = { static int cdns_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct cdns_gpio_chip *cgpio; int ret, irq; u32 dir_prev; @@ -176,32 +170,33 @@ static int cdns_gpio_probe(struct platform_device *pdev) * gpiochip_lock_as_irq: * tried to flag a GPIO set as output for IRQ * Generic GPIO driver stores the direction value internally, - * so it needs to be changed before bgpio_init() is called. + * so it needs to be changed before gpio_generic_chip_init() is called. */ dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE); iowrite32(GENMASK(num_gpios - 1, 0), cgpio->regs + CDNS_GPIO_DIRECTION_MODE); - ret = bgpio_init(&cgpio->gc, &pdev->dev, 4, - cgpio->regs + CDNS_GPIO_INPUT_VALUE, - cgpio->regs + CDNS_GPIO_OUTPUT_VALUE, - NULL, - NULL, - cgpio->regs + CDNS_GPIO_DIRECTION_MODE, - BGPIOF_READ_OUTPUT_REG_SET); + config.dev = &pdev->dev; + config.sz = 4; + config.dat = cgpio->regs + CDNS_GPIO_INPUT_VALUE; + config.set = cgpio->regs + CDNS_GPIO_OUTPUT_VALUE; + config.dirin = cgpio->regs + CDNS_GPIO_DIRECTION_MODE; + config.flags = BGPIOF_READ_OUTPUT_REG_SET; + + ret = gpio_generic_chip_init(&cgpio->gen_gc, &config); if (ret) { dev_err(&pdev->dev, "Failed to register generic gpio, %d\n", ret); goto err_revert_dir; } - cgpio->gc.label = dev_name(&pdev->dev); - cgpio->gc.ngpio = num_gpios; - cgpio->gc.parent = &pdev->dev; - cgpio->gc.base = -1; - cgpio->gc.owner = THIS_MODULE; - cgpio->gc.request = cdns_gpio_request; - cgpio->gc.free = cdns_gpio_free; + cgpio->gen_gc.gc.label = dev_name(&pdev->dev); + cgpio->gen_gc.gc.ngpio = num_gpios; + cgpio->gen_gc.gc.parent = &pdev->dev; + cgpio->gen_gc.gc.base = -1; + cgpio->gen_gc.gc.owner = THIS_MODULE; + cgpio->gen_gc.gc.request = cdns_gpio_request; + cgpio->gen_gc.gc.free = cdns_gpio_free; clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { @@ -218,7 +213,7 @@ static int cdns_gpio_probe(struct platform_device *pdev) if (irq >= 0) { struct gpio_irq_chip *girq; - girq = &cgpio->gc.irq; + girq = &cgpio->gen_gc.gc.irq; gpio_irq_chip_set_chip(girq, &cdns_gpio_irqchip); girq->parent_handler = cdns_gpio_irq_handler; girq->num_parents = 1; @@ -234,7 +229,7 @@ static int cdns_gpio_probe(struct platform_device *pdev) girq->handler = handle_level_irq; } - ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio); + ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gen_gc.gc, cgpio); if (ret < 0) { dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); goto err_revert_dir; diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c index 1495bec62456..0efa1b61001a 100644 --- a/drivers/gpio/gpio-cgbc.c +++ b/drivers/gpio/gpio-cgbc.c @@ -171,7 +171,7 @@ static int cgbc_gpio_probe(struct platform_device *pdev) chip->direction_output = cgbc_gpio_direction_output; chip->get_direction = cgbc_gpio_get_direction; chip->get = cgbc_gpio_get; - chip->set_rv = cgbc_gpio_set; + chip->set = cgbc_gpio_set; chip->ngpio = CGBC_GPIO_NGPIO; ret = devm_mutex_init(dev, &gpio->lock); diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index d69a24dd4828..24ff2347d599 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -8,13 +8,15 @@ #include #include #include +#include #include static int clps711x_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device_node *np = pdev->dev.of_node; + struct gpio_generic_chip *gen_gc; void __iomem *dat, *dir; - struct gpio_chip *gc; int err, id; if (!np) @@ -24,8 +26,8 @@ static int clps711x_gpio_probe(struct platform_device *pdev) if ((id < 0) || (id > 4)) return -ENODEV; - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); - if (!gc) + gen_gc = devm_kzalloc(&pdev->dev, sizeof(*gen_gc), GFP_KERNEL); + if (!gen_gc) return -ENOMEM; dat = devm_platform_ioremap_resource(pdev, 0); @@ -36,35 +38,37 @@ static int clps711x_gpio_probe(struct platform_device *pdev) if (IS_ERR(dir)) return PTR_ERR(dir); + config.dev = &pdev->dev; + config.sz = 1; + config.dat = dat; + switch (id) { case 3: /* PORTD is inverted logic for direction register */ - err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, - NULL, dir, 0); + config.dirin = dir; break; default: - err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, - dir, NULL, 0); + config.dirout = dir; break; } + err = gpio_generic_chip_init(gen_gc, &config); if (err) return err; switch (id) { case 4: /* PORTE is 3 lines only */ - gc->ngpio = 3; + gen_gc->gc.ngpio = 3; break; default: break; } - gc->base = -1; - gc->owner = THIS_MODULE; - platform_set_drvdata(pdev, gc); + gen_gc->gc.base = -1; + gen_gc->gc.owner = THIS_MODULE; - return devm_gpiochip_add_data(&pdev->dev, gc, NULL); + return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL); } static const struct of_device_id clps711x_gpio_ids[] = { diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 8b49f02c7896..f8ea961fa1de 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -167,7 +167,7 @@ static int creg_gpio_probe(struct platform_device *pdev) hcg->gc.label = dev_name(dev); hcg->gc.base = -1; hcg->gc.ngpio = ngpios; - hcg->gc.set_rv = creg_gpio_set; + hcg->gc.set = creg_gpio_set; hcg->gc.direction_output = creg_gpio_dir_out; ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg); diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 53cd5ff6247b..435483826c6e 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -188,7 +188,7 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) gc->can_sleep = true; gc->label = dev_name(dev); gc->base = -1; - gc->set_rv = cros_ec_gpio_set; + gc->set = cros_ec_gpio_set; gc->get = cros_ec_gpio_get; gc->get_direction = cros_ec_gpio_get_direction; diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 8db7cca3a060..0fb5c06d0886 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -349,7 +349,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) cg->chip.direction_input = crystalcove_gpio_dir_in; cg->chip.direction_output = crystalcove_gpio_dir_out; cg->chip.get = crystalcove_gpio_get; - cg->chip.set_rv = crystalcove_gpio_set; + cg->chip.set = crystalcove_gpio_set; cg->chip.base = -1; cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM; cg->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 143d1f4173a6..8affe4e9f90e 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -296,7 +296,7 @@ static struct cs5535_gpio_chip cs5535_gpio_chip = { .request = chip_gpio_request, .get = chip_gpio_get, - .set_rv = chip_gpio_set, + .set = chip_gpio_set, .direction_input = chip_direction_input, .direction_output = chip_direction_output, diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index 6482c5b267db..495f0ee58505 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -172,7 +172,7 @@ static const struct gpio_chip reference_gp = { .label = "da9052-gpio", .owner = THIS_MODULE, .get = da9052_gpio_get, - .set_rv = da9052_gpio_set, + .set = da9052_gpio_set, .direction_input = da9052_gpio_direction_input, .direction_output = da9052_gpio_direction_output, .to_irq = da9052_gpio_to_irq, diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 3d9d0c700100..a09bd6eb93cf 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -116,7 +116,7 @@ static const struct gpio_chip reference_gp = { .label = "da9055-gpio", .owner = THIS_MODULE, .get = da9055_gpio_get, - .set_rv = da9055_gpio_set, + .set = da9055_gpio_set, .direction_input = da9055_gpio_direction_input, .direction_output = da9055_gpio_direction_output, .to_irq = da9055_gpio_to_irq, diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 80a82492171e..538f27209ce7 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -202,7 +202,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) chips->chip.direction_input = davinci_direction_in; chips->chip.get = davinci_gpio_get; chips->chip.direction_output = davinci_direction_out; - chips->chip.set_rv = davinci_gpio_set; + chips->chip.set = davinci_gpio_set; chips->chip.ngpio = ngpio; chips->chip.base = -1; @@ -478,7 +478,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) return irq; } - irq_domain = irq_domain_create_legacy(of_fwnode_handle(dev->of_node), ngpio, irq, 0, + irq_domain = irq_domain_create_legacy(dev_fwnode(dev), ngpio, irq, 0, &davinci_gpio_irq_ops, chips); if (!irq_domain) { dev_err(dev, "Couldn't register an IRQ domain\n"); diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 4bd3c47eaf93..4670ffd7ea7f 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -469,7 +469,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) dln2->gpio.base = -1; dln2->gpio.ngpio = pins; dln2->gpio.can_sleep = true; - dln2->gpio.set_rv = dln2_gpio_set; + dln2->gpio.set = dln2_gpio_set; dln2->gpio.get = dln2_gpio_get; dln2->gpio.request = dln2_gpio_request; dln2->gpio.free = dln2_gpio_free; diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index f2973d0b7138..50fafeda8d7e 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -663,7 +663,7 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->chip.request = sprd_eic_request; sprd_eic->chip.free = sprd_eic_free; sprd_eic->chip.set_config = sprd_eic_set_config; - sprd_eic->chip.set_rv = sprd_eic_set; + sprd_eic->chip.set = sprd_eic_set; fallthrough; case SPRD_EIC_ASYNC: case SPRD_EIC_SYNC: diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index a5e6e446f39c..a214b0672726 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -306,7 +306,7 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->direction_input = em_gio_direction_input; gpio_chip->get = em_gio_get; gpio_chip->direction_output = em_gio_direction_output; - gpio_chip->set_rv = em_gio_set; + gpio_chip->set = em_gio_set; gpio_chip->to_irq = em_gio_to_irq; gpio_chip->request = pinctrl_gpio_request; gpio_chip->free = em_gio_free; @@ -325,8 +325,7 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_release_resources = em_gio_irq_relres; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_create_simple(of_fwnode_handle(dev->of_node), - ngpios, 0, + p->irq_domain = irq_domain_create_simple(dev_fwnode(dev), ngpios, 0, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { dev_err(dev, "cannot initialize irq domain\n"); diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index 69834db2c1cf..cf47afc578a9 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,28 +14,23 @@ /** * struct airoha_gpio_ctrl - Airoha GPIO driver data - * @gc: Associated gpio_chip instance. + * @gen_gc: Associated gpio_generic_chip instance. * @data: The data register. * @dir: [0] The direction register for the lower 16 pins. * [1]: The direction register for the higher 16 pins. * @output: The output enable register. */ struct airoha_gpio_ctrl { - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; void __iomem *data; void __iomem *dir[2]; void __iomem *output; }; -static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc) -{ - return container_of(gc, struct airoha_gpio_ctrl, gc); -} - static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, int val, int out) { - struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc); u32 dir = ioread32(ctrl->dir[gpio / 16]); u32 output = ioread32(ctrl->output); u32 mask = BIT((gpio % 16) * 2); @@ -50,7 +46,7 @@ static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, iowrite32(dir, ctrl->dir[gpio / 16]); if (out) - gc->set(gc, gpio, val); + gpio_generic_chip_set(&ctrl->gen_gc, gpio, val); iowrite32(output, ctrl->output); @@ -70,7 +66,7 @@ static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio) static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) { - struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc); u32 dir = ioread32(ctrl->dir[gpio / 16]); u32 mask = BIT((gpio % 16) * 2); @@ -79,6 +75,7 @@ static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) static int airoha_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device *dev = &pdev->dev; struct airoha_gpio_ctrl *ctrl; int err; @@ -103,18 +100,21 @@ static int airoha_gpio_probe(struct platform_device *pdev) if (IS_ERR(ctrl->output)) return PTR_ERR(ctrl->output); - err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL, - NULL, NULL, NULL, 0); + config.dev = dev; + config.sz = 4; + config.dat = ctrl->data; + + err = gpio_generic_chip_init(&ctrl->gen_gc, &config); if (err) return dev_err_probe(dev, err, "unable to init generic GPIO"); - ctrl->gc.ngpio = AIROHA_GPIO_MAX; - ctrl->gc.owner = THIS_MODULE; - ctrl->gc.direction_output = airoha_dir_out; - ctrl->gc.direction_input = airoha_dir_in; - ctrl->gc.get_direction = airoha_get_dir; + ctrl->gen_gc.gc.ngpio = AIROHA_GPIO_MAX; + ctrl->gen_gc.gc.owner = THIS_MODULE; + ctrl->gen_gc.gc.direction_output = airoha_dir_out; + ctrl->gen_gc.gc.direction_input = airoha_dir_in; + ctrl->gen_gc.gc.get_direction = airoha_get_dir; - return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); + return devm_gpiochip_add_data(dev, &ctrl->gen_gc.gc, ctrl); } static const struct of_device_id airoha_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index beb98286d13e..9053662f1817 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -211,7 +211,7 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; exar_gpio->gpio_chip.get = exar_get_value; - exar_gpio->gpio_chip.set_rv = exar_set_value; + exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base = -1; exar_gpio->gpio_chip.ngpio = ngpios; exar_gpio->index = index; diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index dfcd3634f279..4d5b927ad70f 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -173,7 +173,7 @@ static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, .direction_input = f7188x_gpio_direction_in, \ .get = f7188x_gpio_get, \ .direction_output = f7188x_gpio_direction_out, \ - .set_rv = f7188x_gpio_set, \ + .set = f7188x_gpio_set, \ .set_config = f7188x_gpio_set_config, \ .base = -1, \ .ngpio = _ngpio, \ diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c index f25283e5239d..121bf29a27f5 100644 --- a/drivers/gpio/gpio-graniterapids.c +++ b/drivers/gpio/gpio-graniterapids.c @@ -159,7 +159,7 @@ static const struct gpio_chip gnr_gpio_chip = { .owner = THIS_MODULE, .request = gnr_gpio_request, .get = gnr_gpio_get, - .set_rv = gnr_gpio_set, + .set = gnr_gpio_set, .get_direction = gnr_gpio_get_direction, .direction_input = gnr_gpio_direction_input, .direction_output = gnr_gpio_direction_output, diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index d38a2d9854ca..f3f8bab62f94 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -402,9 +402,8 @@ static int grgpio_probe(struct platform_device *ofdev) return -EINVAL; } - priv->domain = irq_domain_create_linear(of_fwnode_handle(np), gc->ngpio, - &grgpio_irq_domain_ops, - priv); + priv->domain = irq_domain_create_linear(dev_fwnode(&ofdev->dev), gc->ngpio, + &grgpio_irq_domain_ops, priv); if (!priv->domain) { dev_err(dev, "Could not add irq domain\n"); return -EINVAL; diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index a40ba99a3aea..2e5d97b7363f 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -86,7 +86,7 @@ static int gw_pld_probe(struct i2c_client *client) gw->chip.direction_input = gw_pld_input8; gw->chip.get = gw_pld_get8; gw->chip.direction_output = gw_pld_output8; - gw->chip.set_rv = gw_pld_set8; + gw->chip.set = gw_pld_set8; gw->client = client; /* diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index b1844a676c7c..2eaed83214d8 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -324,7 +324,7 @@ static int __init egpio_probe(struct platform_device *pdev) chip->parent = &pdev->dev; chip->owner = THIS_MODULE; chip->get = egpio_get; - chip->set_rv = egpio_set; + chip->set = egpio_set; chip->direction_input = egpio_direction_input; chip->direction_output = egpio_direction_output; chip->get_direction = egpio_get_direction; diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 67089b2423d8..1802c9116ffe 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -273,7 +273,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip) chip->get = ichx_priv.desc->get ? ichx_priv.desc->get : ichx_gpio_get; - chip->set_rv = ichx_gpio_set; + chip->set = ichx_gpio_set; chip->get_direction = ichx_gpio_get_direction; chip->direction_input = ichx_gpio_direction_input; chip->direction_output = ichx_gpio_direction_output; diff --git a/drivers/gpio/gpio-imx-scu.c b/drivers/gpio/gpio-imx-scu.c index 1693dbf1b777..0a75afecf9f8 100644 --- a/drivers/gpio/gpio-imx-scu.c +++ b/drivers/gpio/gpio-imx-scu.c @@ -102,7 +102,7 @@ static int imx_scu_gpio_probe(struct platform_device *pdev) gc->ngpio = ARRAY_SIZE(scu_rsrc_arr); gc->label = dev_name(dev); gc->get = imx_scu_gpio_get; - gc->set_rv = imx_scu_gpio_set; + gc->set = imx_scu_gpio_set; gc->get_direction = imx_scu_gpio_get_direction; platform_set_drvdata(pdev, priv); diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index d8184b527bac..5d677bcfccf2 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -267,7 +267,7 @@ static const struct gpio_chip it87_template_chip = { .request = it87_gpio_request, .get = it87_gpio_get, .direction_input = it87_gpio_direction_in, - .set_rv = it87_gpio_set, + .set = it87_gpio_set, .direction_output = it87_gpio_direction_out, .base = -1 }; diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c index 9f548eda3888..b0c4a3346e7d 100644 --- a/drivers/gpio/gpio-janz-ttl.c +++ b/drivers/gpio/gpio-janz-ttl.c @@ -171,7 +171,7 @@ static int ttl_probe(struct platform_device *pdev) gpio->parent = &pdev->dev; gpio->label = pdev->name; gpio->get = ttl_get_value; - gpio->set_rv = ttl_set_value; + gpio->set = ttl_set_value; gpio->owner = THIS_MODULE; /* request dynamic allocation */ diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index e38e604baa22..923aad3ab4d4 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -169,7 +169,7 @@ static int kempld_gpio_probe(struct platform_device *pdev) chip->direction_output = kempld_gpio_direction_output; chip->get_direction = kempld_gpio_get_direction; chip->get = kempld_gpio_get; - chip->set_rv = kempld_gpio_set; + chip->set = kempld_gpio_set; chip->ngpio = kempld_gpio_pincount(pld); if (chip->ngpio == 0) { dev_err(dev, "No GPIO pins detected\n"); diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index 3d0ff09284fb..c64aaa896766 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -166,11 +166,11 @@ static int gpio_latch_probe(struct platform_device *pdev) if (gpio_latch_can_sleep(priv, n_latches)) { priv->gc.can_sleep = true; - priv->gc.set_rv = gpio_latch_set_can_sleep; + priv->gc.set = gpio_latch_set_can_sleep; mutex_init(&priv->mutex); } else { priv->gc.can_sleep = false; - priv->gc.set_rv = gpio_latch_set; + priv->gc.set = gpio_latch_set; spin_lock_init(&priv->spinlock); } diff --git a/drivers/gpio/gpio-ljca.c b/drivers/gpio/gpio-ljca.c index 61524a9ba765..3b4f8830c741 100644 --- a/drivers/gpio/gpio-ljca.c +++ b/drivers/gpio/gpio-ljca.c @@ -437,7 +437,7 @@ static int ljca_gpio_probe(struct auxiliary_device *auxdev, ljca_gpio->gc.direction_output = ljca_gpio_direction_output; ljca_gpio->gc.get_direction = ljca_gpio_get_direction; ljca_gpio->gc.get = ljca_gpio_get_value; - ljca_gpio->gc.set_rv = ljca_gpio_set_value; + ljca_gpio->gc.set = ljca_gpio_set_value; ljca_gpio->gc.set_config = ljca_gpio_set_config; ljca_gpio->gc.init_valid_mask = ljca_gpio_init_valid_mask; ljca_gpio->gc.can_sleep = true; diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c index 19cd2847467c..cb9dbcc290ad 100644 --- a/drivers/gpio/gpio-logicvc.c +++ b/drivers/gpio/gpio-logicvc.c @@ -134,7 +134,7 @@ static int logicvc_gpio_probe(struct platform_device *pdev) logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + LOGICVC_POWER_CTRL_GPIO_BITS; logicvc->chip.get = logicvc_gpio_get; - logicvc->chip.set_rv = logicvc_gpio_set; + logicvc->chip.set = logicvc_gpio_set; logicvc->chip.direction_output = logicvc_gpio_direction_output; return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 26227669f026..818c606fbc51 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -157,7 +157,7 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp lgpio->chip.get = loongson_gpio_get; lgpio->chip.get_direction = loongson_gpio_get_direction; lgpio->chip.direction_output = loongson_gpio_direction_output; - lgpio->chip.set_rv = loongson_gpio_set; + lgpio->chip.set = loongson_gpio_set; lgpio->chip.parent = dev; spin_lock_init(&lgpio->lock); } @@ -222,6 +222,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { @@ -230,6 +231,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { .conf_offset = 0x0, .in_offset = 0x20, .out_offset = 0x10, + .inten_offset = 0x30, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { @@ -246,6 +248,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { @@ -254,6 +257,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { .conf_offset = 0x800, .in_offset = 0xa00, .out_offset = 0x900, + .inten_offset = 0xb00, }; /* LS7A2000 chipset GPIO */ @@ -263,12 +267,13 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data0 = { .conf_offset = 0x800, .in_offset = 0xa00, .out_offset = 0x900, + .inten_offset = 0xb00, }; /* LS7A2000 ACPI GPIO */ static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data1 = { .label = "ls7a2000_gpio", - .mode = BYTE_CTRL_MODE, + .mode = BIT_CTRL_MODE, .conf_offset = 0x4, .in_offset = 0x8, .out_offset = 0x0, @@ -281,6 +286,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a6000_data = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct of_device_id loongson_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index 8f3668169ebf..f3e0559f969d 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -106,7 +106,7 @@ static int loongson_gpio_probe(struct platform_device *pdev) gc->base = 0; gc->ngpio = LOONGSON_N_GPIO; gc->get = loongson_gpio_get_value; - gc->set_rv = loongson_gpio_set_value; + gc->set = loongson_gpio_set_value; gc->direction_input = loongson_gpio_direction_input; gc->direction_output = loongson_gpio_direction_output; diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 52ab3ac4844c..e8e00daff7df 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -184,7 +184,7 @@ static const struct gpio_chip lp3943_gpio_chip = { .direction_input = lp3943_gpio_direction_input, .get = lp3943_gpio_get, .direction_output = lp3943_gpio_direction_output, - .set_rv = lp3943_gpio_set, + .set = lp3943_gpio_set, .base = -1, .ngpio = LP3943_MAX_GPIO, .can_sleep = 1, diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 1908ed302e92..5376708a81bf 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -124,7 +124,7 @@ static const struct gpio_chip template_chip = { .direction_input = lp873x_gpio_direction_input, .direction_output = lp873x_gpio_direction_output, .get = lp873x_gpio_get, - .set_rv = lp873x_gpio_set, + .set = lp873x_gpio_set, .set_config = lp873x_gpio_set_config, .base = -1, .ngpio = 2, diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index 8ea687d5d028..0f337c1283b2 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -139,7 +139,7 @@ static const struct gpio_chip template_chip = { .direction_input = lp87565_gpio_direction_input, .direction_output = lp87565_gpio_direction_output, .get = lp87565_gpio_get, - .set_rv = lp87565_gpio_set, + .set = lp87565_gpio_set, .set_config = lp87565_gpio_set_config, .base = -1, .ngpio = 3, diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index b0a8da5c058d..37a2342eb2e6 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -249,8 +249,8 @@ static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc) raw_spin_lock_init(&ic->lock); ic->domain = irq_domain_create_hierarchy(parent_domain, 0, NR_LPC18XX_GPIO_PIN_IC_IRQS, - of_fwnode_handle(dev->of_node), - &lpc18xx_gpio_pin_ic_domain_ops, ic); + dev_fwnode(dev), &lpc18xx_gpio_pin_ic_domain_ops, + ic); if (!ic->domain) { pr_err("unable to add irq domain\n"); ret = -ENODEV; @@ -327,7 +327,7 @@ static const struct gpio_chip lpc18xx_chip = { .free = gpiochip_generic_free, .direction_input = lpc18xx_gpio_direction_input, .direction_output = lpc18xx_gpio_direction_output, - .set_rv = lpc18xx_gpio_set, + .set = lpc18xx_gpio_set, .get = lpc18xx_gpio_get, .ngpio = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT, .owner = THIS_MODULE, diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 6668b8bd9f1e..37fc54fc7385 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -407,7 +407,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set_rv = lpc32xx_gpio_set_value_p012, + .set = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_p01, .base = LPC32XX_GPIO_P0_GRP, @@ -423,7 +423,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set_rv = lpc32xx_gpio_set_value_p012, + .set = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_p01, .base = LPC32XX_GPIO_P1_GRP, @@ -439,7 +439,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set_rv = lpc32xx_gpio_set_value_p012, + .set = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .base = LPC32XX_GPIO_P2_GRP, .ngpio = LPC32XX_GPIO_P2_MAX, @@ -454,7 +454,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p3, .get = lpc32xx_gpio_get_value_p3, .direction_output = lpc32xx_gpio_dir_output_p3, - .set_rv = lpc32xx_gpio_set_value_p3, + .set = lpc32xx_gpio_set_value_p3, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_gpio_p3, .base = LPC32XX_GPIO_P3_GRP, @@ -482,7 +482,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .chip = { .label = "gpo_p3", .direction_output = lpc32xx_gpio_dir_out_always, - .set_rv = lpc32xx_gpo_set_value, + .set = lpc32xx_gpo_set_value, .get = lpc32xx_gpo_get_value, .request = lpc32xx_gpio_request, .base = LPC32XX_GPO_P3_GRP, diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c new file mode 100644 index 000000000000..30ef258e7655 --- /dev/null +++ b/drivers/gpio/gpio-macsmc.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC GPIO driver + * Copyright The Asahi Linux Contributors + * + * This driver implements basic SMC PMU GPIO support that can read inputs + * and write outputs. Mode changes and IRQ config are not yet implemented. + */ + +#include +#include +#include +#include +#include + +#define MAX_GPIO 64 + +/* + * Commands 0-6 are, presumably, the intended API. + * Command 0xff lets you get/set the pin configuration in detail directly, + * but the bit meanings seem not to be stable between devices/PMU hardware + * versions. + * + * We're going to try to make do with the low commands for now. + * We don't implement pin mode changes at this time. + */ + +#define CMD_ACTION (0 << 24) +#define CMD_OUTPUT (1 << 24) +#define CMD_INPUT (2 << 24) +#define CMD_PINMODE (3 << 24) +#define CMD_IRQ_ENABLE (4 << 24) +#define CMD_IRQ_ACK (5 << 24) +#define CMD_IRQ_MODE (6 << 24) +#define CMD_CONFIG (0xff << 24) + +#define MODE_INPUT 0 +#define MODE_OUTPUT 1 +#define MODE_VALUE_0 0 +#define MODE_VALUE_1 2 + +#define IRQ_MODE_HIGH 0 +#define IRQ_MODE_LOW 1 +#define IRQ_MODE_RISING 2 +#define IRQ_MODE_FALLING 3 +#define IRQ_MODE_BOTH 4 + +#define CONFIG_MASK GENMASK(23, 16) +#define CONFIG_VAL GENMASK(7, 0) + +#define CONFIG_OUTMODE GENMASK(7, 6) +#define CONFIG_IRQMODE GENMASK(5, 3) +#define CONFIG_PULLDOWN BIT(2) +#define CONFIG_PULLUP BIT(1) +#define CONFIG_OUTVAL BIT(0) + +/* + * Output modes seem to differ depending on the PMU in use... ? + * j274 / M1 (Sera PMU): + * 0 = input + * 1 = output + * 2 = open drain + * 3 = disable + * j314 / M1Pro (Maverick PMU): + * 0 = input + * 1 = open drain + * 2 = output + * 3 = ? + */ + +struct macsmc_gpio { + struct device *dev; + struct apple_smc *smc; + struct gpio_chip gc; + + int first_index; +}; + +static int macsmc_gpio_nr(smc_key key) +{ + int low = hex_to_bin(key & 0xff); + int high = hex_to_bin((key >> 8) & 0xff); + + if (low < 0 || high < 0) + return -1; + + return low | (high << 4); +} + +static int macsmc_gpio_key(unsigned int offset) +{ + return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset); +} + +static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp) +{ + struct apple_smc *smc = smcgp->smc; + smc_key key = macsmc_gpio_key(0); + smc_key first_key, last_key; + int start, count, ret; + + /* Return early if the key is out of bounds */ + ret = apple_smc_get_key_by_index(smc, 0, &first_key); + if (ret) + return ret; + if (key <= first_key) + return -ENODEV; + + ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key); + if (ret) + return ret; + if (key > last_key) + return -ENODEV; + + /* Binary search to find index of first SMC key bigger or equal to key */ + start = 0; + count = smc->key_count; + while (count > 1) { + smc_key pkey; + int pivot = start + ((count - 1) >> 1); + + ret = apple_smc_get_key_by_index(smc, pivot, &pkey); + if (ret < 0) + return ret; + + if (pkey == key) + return pivot; + + pivot++; + + if (pkey < key) { + count -= pivot - start; + start = pivot; + } else { + count = pivot - start; + } + } + + return start; +} + +static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 val; + int ret; + + /* First try reading the explicit pin mode register */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val); + if (!ret) + return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + + /* + * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode. + * Fall back to reading IRQ mode, which will only succeed for inputs. + */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val); + return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 cmd, val; + int ret; + + ret = macsmc_gpio_get_direction(gc, offset); + if (ret < 0) + return ret; + + if (ret == GPIO_LINE_DIRECTION_OUT) + cmd = CMD_OUTPUT; + else + cmd = CMD_INPUT; + + ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val); + if (ret < 0) + return ret; + + return val ? 1 : 0; +} + +static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + int ret; + + value |= CMD_OUTPUT; + ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value); + if (ret < 0) + dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n", + &key, value); + + return ret; +} + +static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, unsigned int ngpios) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + int count; + int i; + + count = min(smcgp->smc->key_count, MAX_GPIO); + + bitmap_zero(valid_mask, ngpios); + + for (i = 0; i < count; i++) { + int ret, gpio_nr; + smc_key key; + + ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key); + if (ret < 0) + return ret; + + if (key > SMC_KEY(gPff)) + break; + + gpio_nr = macsmc_gpio_nr(key); + if (gpio_nr < 0 || gpio_nr > MAX_GPIO) { + dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key); + continue; + } + + set_bit(gpio_nr, valid_mask); + } + + return 0; +} + +static int macsmc_gpio_probe(struct platform_device *pdev) +{ + struct macsmc_gpio *smcgp; + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + smc_key key; + int ret; + + smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL); + if (!smcgp) + return -ENOMEM; + + smcgp->dev = &pdev->dev; + smcgp->smc = smc; + + smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp); + if (smcgp->first_index < 0) + return smcgp->first_index; + + ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key); + if (ret < 0) + return ret; + + if (key > macsmc_gpio_key(MAX_GPIO - 1)) + return -ENODEV; + + dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key); + + smcgp->gc.label = "macsmc-pmu-gpio"; + smcgp->gc.owner = THIS_MODULE; + smcgp->gc.get = macsmc_gpio_get; + smcgp->gc.set = macsmc_gpio_set; + smcgp->gc.get_direction = macsmc_gpio_get_direction; + smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask; + smcgp->gc.can_sleep = true; + smcgp->gc.ngpio = MAX_GPIO; + smcgp->gc.base = -1; + smcgp->gc.parent = &pdev->dev; + + return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp); +} + +static const struct of_device_id macsmc_gpio_of_table[] = { + { .compatible = "apple,smc-gpio", }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table); + +static struct platform_driver macsmc_gpio_driver = { + .driver = { + .name = "macsmc-gpio", + .of_match_table = macsmc_gpio_of_table, + }, + .probe = macsmc_gpio_probe, +}; +module_platform_driver(macsmc_gpio_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC GPIO driver"); diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index e73e72d62bc8..551faf9655b2 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -109,7 +109,7 @@ static const struct gpio_chip madera_gpio_chip = { .direction_input = madera_gpio_direction_in, .get = madera_gpio_get, .direction_output = madera_gpio_direction_out, - .set_rv = madera_gpio_set, + .set = madera_gpio_set, .set_config = gpiochip_generic_config, .can_sleep = true, }; diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index 75d414d8c992..84c7c2dca822 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -188,7 +188,7 @@ int __max730x_probe(struct max7301 *ts) ts->chip.direction_input = max7301_direction_input; ts->chip.get = max7301_get; ts->chip.direction_output = max7301_direction_output; - ts->chip.set_rv = max7301_set; + ts->chip.set = max7301_set; ts->chip.ngpio = PIN_NUMBER; ts->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index d5ffedb086af..a61d670ceeda 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -585,8 +585,8 @@ static int max732x_setup_gpio(struct max732x_chip *chip, gc->direction_input = max732x_gpio_direction_input; if (chip->dir_output) { gc->direction_output = max732x_gpio_direction_output; - gc->set_rv = max732x_gpio_set_value; - gc->set_multiple_rv = max732x_gpio_set_multiple; + gc->set = max732x_gpio_set_value; + gc->set_multiple = max732x_gpio_set_multiple; } gc->get = max732x_gpio_get_value; gc->can_sleep = true; diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index af7af8e40afe..02eca400b307 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -311,7 +311,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; mgpio->gpio_chip.get = max77620_gpio_get; mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; - mgpio->gpio_chip.set_rv = max77620_gpio_set; + mgpio->gpio_chip.set = max77620_gpio_set; mgpio->gpio_chip.set_config = max77620_gpio_set_config; mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c index a553e141059f..4540da4c1418 100644 --- a/drivers/gpio/gpio-max77650.c +++ b/drivers/gpio/gpio-max77650.c @@ -166,7 +166,7 @@ static int max77650_gpio_probe(struct platform_device *pdev) chip->gc.direction_input = max77650_gpio_direction_input; chip->gc.direction_output = max77650_gpio_direction_output; - chip->gc.set_rv = max77650_gpio_set_value; + chip->gc.set = max77650_gpio_set_value; chip->gc.get = max77650_gpio_get_value; chip->gc.get_direction = max77650_gpio_get_direction; chip->gc.set_config = max77650_gpio_set_config; diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c index 7fe8e6f697d0..5e48eb03e7b3 100644 --- a/drivers/gpio/gpio-max77759.c +++ b/drivers/gpio/gpio-max77759.c @@ -469,7 +469,7 @@ static int max77759_gpio_probe(struct platform_device *pdev) chip->gc.direction_input = max77759_gpio_direction_input; chip->gc.direction_output = max77759_gpio_direction_output; chip->gc.get = max77759_gpio_get_value; - chip->gc.set_rv = max77759_gpio_set_value; + chip->gc.set = max77759_gpio_set_value; girq = &chip->gc.irq; gpio_irq_chip_set_chip(girq, &max77759_gpio_irq_chip); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 5ee2991ecdfd..581a71872eab 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -180,7 +180,7 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) gchip->gc.request = mb86s70_gpio_request; gchip->gc.free = mb86s70_gpio_free; gchip->gc.get = mb86s70_gpio_get; - gchip->gc.set_rv = mb86s70_gpio_set; + gchip->gc.set = mb86s70_gpio_set; gchip->gc.to_irq = mb86s70_gpio_to_irq; gchip->gc.label = dev_name(&pdev->dev); gchip->gc.ngpio = 32; diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index e68956104161..9a40e9579e95 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -103,7 +103,7 @@ static int mc33880_probe(struct spi_device *spi) mc->spi = spi; mc->chip.label = DRIVER_NAME; - mc->chip.set_rv = mc33880_set; + mc->chip.set = mc33880_set; mc->chip.base = pdata->base; mc->chip.ngpio = PIN_NUMBER; mc->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 12cf36f9ca63..f6af81bf2b13 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -224,7 +224,7 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) gpio->direction_input = ioh_gpio_direction_input; gpio->get = ioh_gpio_get; gpio->direction_output = ioh_gpio_direction_output; - gpio->set_rv = ioh_gpio_set; + gpio->set = ioh_gpio_set; gpio->dbg_show = NULL; gpio->base = -1; gpio->ngpio = num_port; diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 6f3dda6b635f..390f2e74a9d8 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -397,7 +397,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev) gc->ngpio = npins; gc->owner = THIS_MODULE; - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { girq = &gs->gc.irq; gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip); diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 10ea71273c89..ed29b07d16c1 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -227,7 +227,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) gc->owner = THIS_MODULE; gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges; - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { girq = &gs->gc.irq; gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 14ae25783438..8f1405733d98 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -55,9 +55,9 @@ static void ltq_mm_apply(struct ltq_mm *chip) * @gpio: GPIO signal number. * @val: Value to be written to specified signal. * - * Set the shadow value and call ltq_mm_apply. + * Set the shadow value and call ltq_mm_apply. Always returns 0. */ -static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) +static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value) { struct ltq_mm *chip = gpiochip_get_data(gc); @@ -66,6 +66,8 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) else chip->shadow &= ~(1 << offset); ltq_mm_apply(chip); + + return 0; } /** @@ -78,9 +80,7 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) */ static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value) { - ltq_mm_set(gc, offset, value); - - return 0; + return ltq_mm_set(gc, offset, value); } /** diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4841e4ebe7a6..021ad62778c2 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -211,11 +211,12 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, return 0; } -static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) { + return 0; } -static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; @@ -230,10 +231,12 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) gc->write_reg(gc->reg_dat, gc->bgpio_data); raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; } -static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, - int val) +static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) { unsigned long mask = bgpio_line2mask(gc, gpio); @@ -241,9 +244,11 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, gc->write_reg(gc->reg_set, mask); else gc->write_reg(gc->reg_clr, mask); + + return 0; } -static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; @@ -258,6 +263,8 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) gc->write_reg(gc->reg_set, gc->bgpio_data); raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; } static void bgpio_multiple_get_masks(struct gpio_chip *gc, @@ -298,21 +305,25 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc, raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); } -static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, +static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_dat); + + return 0; } -static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_set); + + return 0; } -static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, - unsigned long *mask, - unsigned long *bits) +static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits) { unsigned long set_mask, clear_mask; @@ -322,6 +333,8 @@ static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, gc->write_reg(gc->reg_set, set_mask); if (clear_mask) gc->write_reg(gc->reg_clr, clear_mask); + + return 0; } static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) @@ -335,6 +348,11 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou return pinctrl_gpio_direction_input(gc, gpio); } +static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) +{ + return -EINVAL; +} + static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) { return bgpio_dir_return(gc, gpio, false); @@ -566,7 +584,11 @@ static int bgpio_setup_direction(struct gpio_chip *gc, gc->direction_output = bgpio_dir_out_err; else gc->direction_output = bgpio_simple_dir_out; - gc->direction_input = bgpio_simple_dir_in; + + if (flags & BGPIOF_NO_INPUT) + gc->direction_input = bgpio_dir_in_err; + else + gc->direction_input = bgpio_simple_dir_in; } return 0; @@ -712,28 +734,6 @@ static const struct of_device_id bgpio_of_match[] = { }; MODULE_DEVICE_TABLE(of, bgpio_of_match); -static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags) -{ - struct bgpio_pdata *pdata; - - if (!dev_fwnode(dev)) - return NULL; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->base = -1; - - if (device_is_big_endian(dev)) - *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; - - if (device_property_read_bool(dev, "no-output")) - *flags |= BGPIOF_NO_OUTPUT; - - return pdata; -} - static int bgpio_pdev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -745,18 +745,10 @@ static int bgpio_pdev_probe(struct platform_device *pdev) void __iomem *dirin; unsigned long sz; unsigned long flags = 0; + unsigned int base; int err; struct gpio_chip *gc; - struct bgpio_pdata *pdata; - - pdata = bgpio_parse_fw(dev, &flags); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - if (!pdata) { - pdata = dev_get_platdata(dev); - flags = pdev->id_entry->driver_data; - } + const char *label; r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (!r) @@ -788,17 +780,27 @@ static int bgpio_pdev_probe(struct platform_device *pdev) if (!gc) return -ENOMEM; + if (device_is_big_endian(dev)) + flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + + if (device_property_read_bool(dev, "no-output")) + flags |= BGPIOF_NO_OUTPUT; + err = bgpio_init(gc, dev, sz, dat, set, clr, dirout, dirin, flags); if (err) return err; - if (pdata) { - if (pdata->label) - gc->label = pdata->label; - gc->base = pdata->base; - if (pdata->ngpio > 0) - gc->ngpio = pdata->ngpio; - } + err = device_property_read_string(dev, "label", &label); + if (!err) + gc->label = label; + + /* + * This property *must not* be used in device-tree sources, it's only + * meant to be passed to the driver from board files and MFD core. + */ + err = device_property_read_u32(dev, "gpio-mmio,base", &base); + if (!err && base <= INT_MAX) + gc->base = base; platform_set_drvdata(pdev, gc); @@ -809,9 +811,6 @@ static const struct platform_device_id bgpio_id_table[] = { { .name = "basic-mmio-gpio", .driver_data = 0, - }, { - .name = "basic-mmio-gpio-be", - .driver_data = BGPIOF_BIG_ENDIAN, }, { } }; diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 266c0953d914..a7d69f3835c1 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -449,9 +449,9 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->owner = THIS_MODULE; gc->parent = dev; gc->get = gpio_mockup_get; - gc->set_rv = gpio_mockup_set; + gc->set = gpio_mockup_set; gc->get_multiple = gpio_mockup_get_multiple; - gc->set_multiple_rv = gpio_mockup_set_multiple; + gc->set_multiple = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c index 61f9efd6c64f..4eb9f1a2779b 100644 --- a/drivers/gpio/gpio-moxtet.c +++ b/drivers/gpio/gpio-moxtet.c @@ -52,15 +52,15 @@ static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset) return !!(ret & BIT(offset)); } -static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, - int val) +static int moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) { struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); int state; state = moxtet_device_written(chip->dev); if (state < 0) - return; + return state; offset -= MOXTET_GPIO_INPUTS; @@ -69,7 +69,7 @@ static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, else state &= ~BIT(offset); - moxtet_device_write(chip->dev, state); + return moxtet_device_write(chip->dev, state); } static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -104,13 +104,11 @@ static int moxtet_gpio_direction_output(struct gpio_chip *gc, struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); if (chip->desc->out_mask & BIT(offset)) - moxtet_gpio_set_value(gc, offset, val); + return moxtet_gpio_set_value(gc, offset, val); else if (chip->desc->in_mask & BIT(offset)) return -ENOTSUPP; - else - return -EINVAL; - return 0; + return -EINVAL; } static int moxtet_gpio_probe(struct device *dev) diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 091d96f2d682..dad0eca1ca2e 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -69,7 +69,7 @@ __mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) out_8(®s->wkup_dvo, chip->shadow_dvo); } -static void +static int mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; @@ -81,6 +81,8 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; } static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -228,7 +230,7 @@ __mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) out_be32(®s->simple_dvo, chip->shadow_dvo); } -static void +static int mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; @@ -240,6 +242,8 @@ mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; } static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c index 561a961c97a6..82d557a7e5d8 100644 --- a/drivers/gpio/gpio-mpfs.c +++ b/drivers/gpio/gpio-mpfs.c @@ -99,16 +99,19 @@ static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index) return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->inp, BIT(gpio_index)); } -static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) +static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) { struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int ret; mpfs_gpio_get(gc, gpio_index); - regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index), - value << gpio_index); + ret = regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, + BIT(gpio_index), value << gpio_index); mpfs_gpio_get(gc, gpio_index); + + return ret; } static int mpfs_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 3ea32c5e33d1..9f42bb30b4ec 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -160,8 +160,8 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank) return buf; } -static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { unsigned long i, bank, bank_mask, bank_bits; int ret; @@ -180,11 +180,11 @@ static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, ret = gpio_mpsse_set_bank(priv, bank); if (ret) - dev_err(&priv->intf->dev, - "Couldn't set values for bank %ld!", - bank); + return ret; } } + + return 0; } static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, @@ -227,7 +227,7 @@ static int gpio_mpsse_gpio_get(struct gpio_chip *chip, unsigned int offset) return 0; } -static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { unsigned long mask = 0, bits = 0; @@ -236,7 +236,7 @@ static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, if (value) __set_bit(offset, &bits); - gpio_mpsse_set_multiple(chip, &mask, &bits); + return gpio_mpsse_set_multiple(chip, &mask, &bits); } static int gpio_mpsse_direction_output(struct gpio_chip *chip, @@ -249,9 +249,7 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip, scoped_guard(mutex, &priv->io_mutex) priv->gpio_dir[bank] |= BIT(bank_offset); - gpio_mpsse_gpio_set(chip, offset, value); - - return 0; + return gpio_mpsse_gpio_set(chip, offset, value); } static int gpio_mpsse_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index 6db9e469e0dc..b0cccd856840 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -486,7 +486,7 @@ struct msc313_gpio { u8 *saved; }; -static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct msc313_gpio *gpio = gpiochip_get_data(chip); u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); @@ -497,6 +497,8 @@ static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int val gpioreg &= ~MSC313_GPIO_OUT; writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; } static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 57633a7b4270..5e3f54cb8bc4 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1168,7 +1168,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; - mvchip->chip.set_rv = mvebu_gpio_set; + mvchip->chip.set = mvebu_gpio_set; if (have_irqs) mvchip->chip.to_irq = mvebu_gpio_to_irq; mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; @@ -1236,8 +1236,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) if (!have_irqs) return 0; - mvchip->domain = - irq_domain_create_linear(of_fwnode_handle(np), ngpios, &irq_generic_chip_ops, NULL); + mvchip->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), ngpios, + &irq_generic_chip_ops, NULL); if (!mvchip->domain) { dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n", mvchip->chip.label); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index fae1a30f8ae6..433cbadc3a4c 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -7,6 +7,7 @@ // Authors: Daniel Mack, Juergen Beisert. // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include @@ -64,7 +66,7 @@ struct mxc_gpio_port { int irq_high; void (*mx_irq_handler)(struct irq_desc *desc); struct irq_domain *domain; - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; struct device *dev; u32 both_edges; struct mxc_gpio_reg_saved gpio_saved_reg; @@ -161,7 +163,6 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxc_gpio_port *port = gc->private; - unsigned long flags; u32 bit, val; u32 gpio_idx = d->hwirq; int edge; @@ -179,7 +180,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) if (GPIO_EDGE_SEL >= 0) { edge = GPIO_INT_BOTH_EDGES; } else { - val = port->gc.get(&port->gc, gpio_idx); + val = port->gen_gc.gc.get(&port->gen_gc.gc, gpio_idx); if (val) { edge = GPIO_INT_LOW_LEV; pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx); @@ -200,41 +201,38 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) return -EINVAL; } - raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); + scoped_guard(gpio_generic_lock_irqsave, &port->gen_gc) { + if (GPIO_EDGE_SEL >= 0) { + val = readl(port->base + GPIO_EDGE_SEL); + if (edge == GPIO_INT_BOTH_EDGES) + writel(val | (1 << gpio_idx), + port->base + GPIO_EDGE_SEL); + else + writel(val & ~(1 << gpio_idx), + port->base + GPIO_EDGE_SEL); + } - if (GPIO_EDGE_SEL >= 0) { - val = readl(port->base + GPIO_EDGE_SEL); - if (edge == GPIO_INT_BOTH_EDGES) - writel(val | (1 << gpio_idx), - port->base + GPIO_EDGE_SEL); - else - writel(val & ~(1 << gpio_idx), - port->base + GPIO_EDGE_SEL); + if (edge != GPIO_INT_BOTH_EDGES) { + reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ + bit = gpio_idx & 0xf; + val = readl(reg) & ~(0x3 << (bit << 1)); + writel(val | (edge << (bit << 1)), reg); + } + + writel(1 << gpio_idx, port->base + GPIO_ISR); + port->pad_type[gpio_idx] = type; } - if (edge != GPIO_INT_BOTH_EDGES) { - reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ - bit = gpio_idx & 0xf; - val = readl(reg) & ~(0x3 << (bit << 1)); - writel(val | (edge << (bit << 1)), reg); - } - - writel(1 << gpio_idx, port->base + GPIO_ISR); - port->pad_type[gpio_idx] = type; - - raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); - - return port->gc.direction_input(&port->gc, gpio_idx); + return port->gen_gc.gc.direction_input(&port->gen_gc.gc, gpio_idx); } static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) { void __iomem *reg = port->base; - unsigned long flags; u32 bit, val; int edge; - raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); + guard(gpio_generic_lock_irqsave)(&port->gen_gc); reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ bit = gpio & 0xf; @@ -250,12 +248,9 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) } else { pr_err("mxc: invalid configuration for GPIO %d: %x\n", gpio, edge); - goto unlock; + return; } writel(val | (edge << (bit << 1)), reg); - -unlock: - raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); } /* handle 32 interrupts in one status register */ @@ -420,6 +415,7 @@ static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enab static int mxc_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; int irq_count; @@ -479,27 +475,31 @@ static int mxc_gpio_probe(struct platform_device *pdev) port->mx_irq_handler = mx3_gpio_irq_handler; mxc_update_irq_chained_handler(port, true); - err = bgpio_init(&port->gc, &pdev->dev, 4, - port->base + GPIO_PSR, - port->base + GPIO_DR, NULL, - port->base + GPIO_GDIR, NULL, - BGPIOF_READ_OUTPUT_REG_SET); + + config.dev = &pdev->dev; + config.sz = 4; + config.dat = port->base + GPIO_PSR; + config.set = port->base + GPIO_DR; + config.dirout = port->base + GPIO_GDIR; + config.flags = BGPIOF_READ_OUTPUT_REG_SET; + + err = gpio_generic_chip_init(&port->gen_gc, &config); if (err) goto out_bgio; - port->gc.request = mxc_gpio_request; - port->gc.free = mxc_gpio_free; - port->gc.to_irq = mxc_gpio_to_irq; + port->gen_gc.gc.request = mxc_gpio_request; + port->gen_gc.gc.free = mxc_gpio_free; + port->gen_gc.gc.to_irq = mxc_gpio_to_irq; /* * Driver is DT-only, so a fixed base needs only be maintained for legacy * userspace with sysfs interface. */ if (IS_ENABLED(CONFIG_GPIO_SYSFS)) - port->gc.base = of_alias_get_id(np, "gpio") * 32; + port->gen_gc.gc.base = of_alias_get_id(np, "gpio") * 32; else /* silence boot time warning */ - port->gc.base = -1; + port->gen_gc.gc.base = -1; - err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); + err = devm_gpiochip_add_data(&pdev->dev, &port->gen_gc.gc, port); if (err) goto out_bgio; @@ -509,8 +509,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) goto out_bgio; } - port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, - &irq_domain_simple_ops, NULL); + port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, + &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; goto out_bgio; @@ -573,7 +573,8 @@ static bool mxc_gpio_generic_config(struct mxc_gpio_port *port, if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") || of_device_is_compatible(np, "fsl,imx8qxp-gpio") || of_device_is_compatible(np, "fsl,imx8qm-gpio")) - return (gpiochip_generic_config(&port->gc, offset, conf) == 0); + return (gpiochip_generic_config(&port->gen_gc.gc, + offset, conf) == 0); return false; } diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index b418fbccb26c..0ea46f3d04e1 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -303,7 +303,7 @@ static int mxs_gpio_probe(struct platform_device *pdev) goto out_iounmap; } - port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, + port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index fa19a44943fd..bcf4b07dd458 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -347,8 +347,8 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned int offset) return value; } -static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, - int val) +static int nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, + int val) { struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); @@ -357,6 +357,8 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, __nmk_gpio_set_output(nmk_chip, offset, val); clk_disable(nmk_chip->clk); + + return 0; } static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index 260570614543..83c77a2c0623 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -211,9 +211,7 @@ static int npcm_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) static int npcm_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { - gc->set(gc, offset, val); - - return 0; + return gc->set(gc, offset, val); } static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -226,7 +224,7 @@ static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) return GPIO_LINE_DIRECTION_IN; } -static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct npcm_sgpio *gpio = gpiochip_get_data(gc); const struct npcm_sgpio_bank *bank = offset_to_bank(offset); @@ -242,6 +240,8 @@ static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) reg &= ~BIT(GPIO_BIT(offset)); iowrite8(reg, addr); + + return 0; } static int npcm_sgpio_get(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-octeon.c b/drivers/gpio/gpio-octeon.c index afb0e8a791e5..777e20c608dc 100644 --- a/drivers/gpio/gpio-octeon.c +++ b/drivers/gpio/gpio-octeon.c @@ -47,12 +47,15 @@ static int octeon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) return 0; } -static void octeon_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int octeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct octeon_gpio *gpio = gpiochip_get_data(chip); u64 mask = 1ull << offset; u64 reg = gpio->register_base + (value ? TX_SET : TX_CLEAR); cvmx_write_csr(reg, mask); + + return 0; } static int octeon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 54c4bfdccf56..a268c76bdca6 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -953,7 +953,7 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset, return ret; } -static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int omap_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_bank *bank; unsigned long flags; @@ -962,10 +962,12 @@ static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) raw_spin_lock_irqsave(&bank->lock, flags); bank->set_dataout(bank, offset, value); raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; } -static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { struct gpio_bank *bank = gpiochip_get_data(chip); void __iomem *reg = bank->base + bank->regs->dataout; @@ -977,6 +979,8 @@ static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, writel_relaxed(l, reg); bank->context.dataout = l; raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; } /*---------------------------------------------------------------------*/ diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 28dba7048509..e377f6dd4ccf 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -54,12 +54,11 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & BIT(offset)); } -static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int palmas_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct palmas_gpio *pg = gpiochip_get_data(gc); struct palmas *palmas = pg->palmas; - int ret; unsigned int reg; int gpio16 = (offset/8); @@ -71,9 +70,7 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, reg = (value) ? PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; - ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); - if (ret < 0) - dev_err(gc->parent, "Reg 0x%02x write failed, %d\n", reg, ret); + return palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); } static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -89,7 +86,9 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; /* Set the initial value */ - palmas_gpio_set(gc, offset, value); + ret = palmas_gpio_set(gc, offset, value); + if (ret) + return ret; ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, BIT(offset), BIT(offset)); @@ -140,6 +139,7 @@ static const struct of_device_id of_palmas_gpio_match[] = { { .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,}, { }, }; +MODULE_DEVICE_TABLE(of, of_palmas_gpio_match); static int palmas_gpio_probe(struct platform_device *pdev) { @@ -197,3 +197,13 @@ static int __init palmas_gpio_init(void) return platform_driver_register(&palmas_gpio_driver); } subsys_initcall(palmas_gpio_init); + +static void __exit palmas_gpio_exit(void) +{ + platform_driver_unregister(&palmas_gpio_driver); +} +module_exit(palmas_gpio_exit); + +MODULE_DESCRIPTION("TI PALMAS series GPIO driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b852e4997629..b46927f55038 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -38,6 +38,10 @@ #define PCA953X_INVERT 0x02 #define PCA953X_DIRECTION 0x03 +#define TCA6418_INPUT 0x14 +#define TCA6418_OUTPUT 0x17 +#define TCA6418_DIRECTION 0x23 + #define REG_ADDR_MASK GENMASK(5, 0) #define REG_ADDR_EXT BIT(6) #define REG_ADDR_AI BIT(7) @@ -76,7 +80,8 @@ #define PCA953X_TYPE BIT(12) #define PCA957X_TYPE BIT(13) #define PCAL653X_TYPE BIT(14) -#define PCA_TYPE_MASK GENMASK(15, 12) +#define TCA6418_TYPE BIT(16) +#define PCA_TYPE_MASK GENMASK(16, 12) #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) @@ -115,6 +120,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, @@ -204,6 +210,13 @@ static const struct pca953x_reg_config pca957x_regs = { .invert = PCA957X_INVRT, }; +static const struct pca953x_reg_config tca6418_regs = { + .direction = TCA6418_DIRECTION, + .output = TCA6418_OUTPUT, + .input = TCA6418_INPUT, + .invert = 0xFF, /* Does not apply */ +}; + struct pca953x_chip { unsigned gpio_start; struct mutex i2c_lock; @@ -237,6 +250,22 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); } +/* + * Helper function to get the correct bit mask for a given offset and chip type. + * The TCA6418's input, output, and direction banks have a peculiar bit order: + * the first byte uses reversed bit order, while the second byte uses standard order. + */ +static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int offset) +{ + unsigned int bit_pos_in_bank = offset % BANK_SZ; + int msb = BANK_SZ - 1; + + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE && offset <= msb) + return BIT(msb - bit_pos_in_bank); + + return BIT(bit_pos_in_bank); +} + #define PCA953x_BANK_INPUT BIT(0) #define PCA953x_BANK_OUTPUT BIT(1) #define PCA953x_BANK_POLARITY BIT(2) @@ -353,18 +382,43 @@ static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg, return true; } +/* TCA6418 breaks the PCA953x register order rule */ +static bool tca6418_check_register(struct pca953x_chip *chip, unsigned int reg, + u32 access_type_mask) +{ + /* Valid Input Registers - BIT(0) for readable access */ + if (reg >= TCA6418_INPUT && reg < (TCA6418_INPUT + NBANK(chip))) + return (access_type_mask & BIT(0)); + + /* Valid Output Registers - BIT(1) for writeable access */ + if (reg >= TCA6418_OUTPUT && reg < (TCA6418_OUTPUT + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + /* Valid Direction Registers - BIT(2) for volatile access */ + if (reg >= TCA6418_DIRECTION && reg < (TCA6418_DIRECTION + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + return false; +} + static bool pca953x_readable_register(struct device *dev, unsigned int reg) { struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(0) to indicate read access */ + return tca6418_check_register(chip, reg, BIT(0)); + default: bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) { @@ -381,12 +435,18 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(1) for write access */ + return tca6418_check_register(chip, reg, BIT(1)); + default: bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) @@ -401,10 +461,17 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg) struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT; - else + break; + case TCA6418_TYPE: + /* BIT(2) for volatile access */ + return tca6418_check_register(chip, reg, BIT(2)); + default: bank = PCA953x_BANK_INPUT; + break; + } if (chip->driver_data & PCA_PCAL) bank |= PCAL9xxx_BANK_IRQ_STAT; @@ -489,6 +556,16 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off) return pinctrl + addr + (off / BANK_SZ); } +static u8 tca6418_recalc_addr(struct pca953x_chip *chip, int reg_base, int offset) +{ + /* + * reg_base will be TCA6418_INPUT, TCA6418_OUTPUT, or TCA6418_DIRECTION + * offset is the global GPIO line offset (0-17) + * BANK_SZ is 8 for TCA6418 (8 bits per register bank) + */ + return reg_base + (offset / BANK_SZ); +} + static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val) { u8 regaddr = chip->recalc_addr(chip, reg, 0); @@ -529,11 +606,14 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); - return regmap_write_bits(chip->regmap, dirreg, bit, bit); + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_update_bits(chip->regmap, dirreg, bit, 0); + + return regmap_update_bits(chip->regmap, dirreg, bit, bit); } static int pca953x_gpio_direction_output(struct gpio_chip *gc, @@ -542,25 +622,31 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); int ret; guard(mutex)(&chip->i2c_lock); /* set output level */ - ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + ret = regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); if (ret) return ret; - /* then direction */ - return regmap_write_bits(chip->regmap, dirreg, bit, 0); + /* + * then direction + * (in/out logic is inverted on TCA6418) + */ + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_update_bits(chip->regmap, dirreg, bit, bit); + + return regmap_update_bits(chip->regmap, dirreg, bit, 0); } static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 inreg = chip->recalc_addr(chip, chip->regs->input, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -577,18 +663,18 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); - return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + return regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); } static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -597,7 +683,14 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) if (ret < 0) return ret; - if (reg_val & bit) + /* (in/out logic is inverted on TCA6418) */ + if (reg_val & bit) { + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; + } + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -658,9 +751,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Configure pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_PULL_UP) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, bit); else if (param == PIN_CONFIG_BIAS_PULL_DOWN) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, 0); else ret = 0; if (ret) @@ -668,9 +761,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Disable/Enable pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_DISABLE) - return regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, 0); else - return regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, bit); } static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, @@ -696,10 +789,10 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->direction_input = pca953x_gpio_direction_input; gc->direction_output = pca953x_gpio_direction_output; gc->get = pca953x_gpio_get_value; - gc->set_rv = pca953x_gpio_set_value; + gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->get_multiple = pca953x_gpio_get_multiple; - gc->set_multiple_rv = pca953x_gpio_set_multiple; + gc->set_multiple = pca953x_gpio_set_multiple; gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; @@ -974,7 +1067,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), chip); if (ret) - return dev_err_probe(dev, client->irq, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); return 0; } @@ -1117,12 +1210,22 @@ static int pca953x_probe(struct i2c_client *client) regmap_config = &pca953x_i2c_regmap; } - if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCAL653X_TYPE: chip->recalc_addr = pcal6534_recalc_addr; chip->check_reg = pcal6534_check_register; - } else { + break; + case TCA6418_TYPE: + chip->recalc_addr = tca6418_recalc_addr; + /* + * We don't assign chip->check_reg = tca6418_check_register directly here. + * Instead, the wrappers handle the dispatch based on PCA_CHIP_TYPE. + */ + break; + default: chip->recalc_addr = pca953x_recalc_addr; chip->check_reg = pca953x_check_register; + break; } chip->regmap = devm_regmap_init_i2c(client, regmap_config); @@ -1151,15 +1254,22 @@ static int pca953x_probe(struct i2c_client *client) lockdep_set_subclass(&chip->i2c_lock, i2c_adapter_depth(client->adapter)); - /* initialize cached registers from their original values. + /* + * initialize cached registers from their original values. * we can't share this chip with another i2c master. */ - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: chip->regs = &pca957x_regs; ret = device_pca957x_init(chip); - } else { + break; + case TCA6418_TYPE: + chip->regs = &tca6418_regs; + break; + default: chip->regs = &pca953x_regs; ret = device_pca95xx_init(chip); + break; } if (ret) return ret; @@ -1325,6 +1435,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, + { .compatible = "ti,tca6418", .data = (void *)(18 | TCA6418_TYPE | PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, @@ -1355,7 +1466,9 @@ static int __init pca953x_init(void) { return i2c_add_driver(&pca953x_driver); } -/* register after i2c postcore initcall and before + +/* + * register after i2c postcore initcall and before * subsys initcalls that may rely on these GPIOs */ subsys_initcall(pca953x_init); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index d37ba4049368..c5a1287079a0 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -88,7 +88,7 @@ static int pca9570_get(struct gpio_chip *chip, unsigned offset) return !!(buffer & BIT(offset)); } -static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) +static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value) { struct pca9570 *gpio = gpiochip_get_data(chip); u8 buffer; @@ -110,6 +110,7 @@ static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) out: mutex_unlock(&gpio->lock); + return ret; } static int pca9570_probe(struct i2c_client *client) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 2e5f5d7f8865..3b9de8c3d924 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -171,21 +171,24 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value return status; } -static void pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value) +static int pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value) { - pcf857x_output(chip, offset, value); + return pcf857x_output(chip, offset, value); } -static void pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { struct pcf857x *gpio = gpiochip_get_data(chip); + int status; mutex_lock(&gpio->lock); gpio->out &= ~*mask; gpio->out |= *bits & *mask; - gpio->write(gpio->client, gpio->out); + status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); + + return status; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 63f25c72eac2..9925687e05fb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -99,7 +99,7 @@ struct pch_gpio { spinlock_t spinlock; }; -static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { u32 reg_val; struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -114,6 +114,8 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) iowrite32(reg_val, &chip->reg->po); spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; } static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index e3013e778e15..a69b74866a13 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -67,13 +67,6 @@ static int pisosr_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int pisosr_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - /* This device is input only */ - return -EINVAL; -} - static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset) { struct pisosr_gpio *gpio = gpiochip_get_data(chip); @@ -108,7 +101,6 @@ static const struct gpio_chip template_chip = { .owner = THIS_MODULE, .get_direction = pisosr_gpio_get_direction, .direction_input = pisosr_gpio_direction_input, - .direction_output = pisosr_gpio_direction_output, .get = pisosr_gpio_get, .get_multiple = pisosr_gpio_get_multiple, .base = -1, diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 1c273727ffa3..02e4ffcf5a6f 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -115,11 +115,13 @@ static int pl061_get_value(struct gpio_chip *gc, unsigned offset) return !!readb(pl061->base + (BIT(offset + 2))); } -static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) +static int pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value) { struct pl061 *pl061 = gpiochip_get_data(gc); writeb(!!value << offset, pl061->base + (BIT(offset + 2))); + + return 0; } static int pl061_irq_type(struct irq_data *d, unsigned trigger) diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index d9b228bea42e..cb015fb5c946 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -109,12 +109,6 @@ static int sprd_pmic_eic_direction_input(struct gpio_chip *chip, return 0; } -static void sprd_pmic_eic_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - /* EICs are always input, nothing need to do here. */ -} - static int sprd_pmic_eic_set_debounce(struct gpio_chip *chip, unsigned int offset, unsigned int debounce) @@ -351,7 +345,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) pmic_eic->chip.request = sprd_pmic_eic_request; pmic_eic->chip.free = sprd_pmic_eic_free; pmic_eic->chip.set_config = sprd_pmic_eic_set_config; - pmic_eic->chip.set = sprd_pmic_eic_set; pmic_eic->chip.get = sprd_pmic_eic_get; pmic_eic->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index aead35ea090e..fa22f3faa163 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -315,12 +315,14 @@ static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(gplr & GPIO_bit(offset)); } -static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int pxa_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { void __iomem *base = gpio_bank_base(chip, offset); writel_relaxed(GPIO_bit(offset), base + (value ? GPSR_OFFSET : GPCR_OFFSET)); + + return 0; } #ifdef CONFIG_OF_GPIO @@ -497,8 +499,6 @@ static void pxa_mask_muxed_gpio(struct irq_data *d) gfer = readl_relaxed(base + GFER_OFFSET) & ~GPIO_bit(gpio); writel_relaxed(grer, base + GRER_OFFSET); writel_relaxed(gfer, base + GFER_OFFSET); - - gpiochip_disable_irq(&pchip->chip, gpio); } static int pxa_gpio_set_wake(struct irq_data *d, unsigned int on) @@ -518,21 +518,17 @@ static void pxa_unmask_muxed_gpio(struct irq_data *d) unsigned int gpio = irqd_to_hwirq(d); struct pxa_gpio_bank *c = gpio_to_pxabank(&pchip->chip, gpio); - gpiochip_enable_irq(&pchip->chip, gpio); - c->irq_mask |= GPIO_bit(gpio); update_edge_detect(c); } -static const struct irq_chip pxa_muxed_gpio_chip = { +static struct irq_chip pxa_muxed_gpio_chip = { .name = "GPIO", .irq_ack = pxa_ack_muxed_gpio, .irq_mask = pxa_mask_muxed_gpio, .irq_unmask = pxa_unmask_muxed_gpio, .irq_set_type = pxa_gpio_irq_type, .irq_set_wake = pxa_gpio_set_wake, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int pxa_gpio_nums(struct platform_device *pdev) @@ -642,9 +638,8 @@ static int pxa_gpio_probe(struct platform_device *pdev) if (!pxa_last_gpio) return -EINVAL; - pchip->irqdomain = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), - pxa_last_gpio + 1, irq_base, 0, - &pxa_irq_domain_ops, pchip); + pchip->irqdomain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), pxa_last_gpio + 1, + irq_base, 0, &pxa_irq_domain_ops, pchip); if (!pchip->irqdomain) return -ENOMEM; diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index 9d1b95e429f1..40413e06b69c 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -175,7 +175,7 @@ static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off) return !!get.state; } -static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +static int rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) { struct rpi_exp_gpio *gpio; struct gpio_get_set_state set; @@ -188,10 +188,14 @@ static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE, &set, sizeof(set)); - if (ret || set.gpio != 0) + if (ret || set.gpio != 0) { dev_err(gc->parent, "Failed to set GPIO %u state (%d %x)\n", off, ret, set.gpio); + return ret ? ret : -EIO; + } + + return 0; } static int rpi_exp_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c index c34dcadaee36..5a69e4534591 100644 --- a/drivers/gpio/gpio-rc5t583.c +++ b/drivers/gpio/gpio-rc5t583.c @@ -35,14 +35,20 @@ static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset) return !!(val & BIT(offset)); } -static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct rc5t583_gpio *rc5t583_gpio = gpiochip_get_data(gc); struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + if (val) - rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, + BIT(offset)); else - rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, + BIT(offset)); + + return ret; } static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) @@ -66,7 +72,10 @@ static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset, struct device *parent = rc5t583_gpio->rc5t583->dev; int ret; - rc5t583_gpio_set(gc, offset, value); + ret = rc5t583_gpio_set(gc, offset, value); + if (ret) + return ret; + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); if (ret < 0) return ret; diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 18c965ee02c8..86777e097fd8 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -331,14 +331,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { + u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); struct gpio_rcar_priv *p = gpiochip_get_data(chip); - u32 bankmask, outputs, m, val = 0; + u32 outputs, m, val = 0; unsigned long flags; - bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (!bankmask) - return 0; - if (p->info.has_always_in) { bits[0] = gpio_rcar_read(p, INDT) & bankmask; return 0; @@ -359,7 +356,7 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, return 0; } -static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) +static int gpio_rcar_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_rcar_priv *p = gpiochip_get_data(chip); unsigned long flags; @@ -367,18 +364,17 @@ static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) raw_spin_lock_irqsave(&p->lock, flags); gpio_rcar_modify_bit(p, OUTDT, offset, value); raw_spin_unlock_irqrestore(&p->lock, flags); + + return 0; } -static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { + u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); struct gpio_rcar_priv *p = gpiochip_get_data(chip); unsigned long flags; - u32 val, bankmask; - - bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (!bankmask) - return; + u32 val; raw_spin_lock_irqsave(&p->lock, flags); val = gpio_rcar_read(p, OUTDT); @@ -386,6 +382,8 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, val |= (bankmask & bits[0]); gpio_rcar_write(p, OUTDT, val); raw_spin_unlock_irqrestore(&p->lock, flags); + + return 0; } static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, @@ -594,7 +592,6 @@ static void gpio_rcar_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int gpio_rcar_suspend(struct device *dev) { struct gpio_rcar_priv *p = dev_get_drvdata(dev); @@ -653,16 +650,16 @@ static int gpio_rcar_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP*/ -static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, + gpio_rcar_resume); static struct platform_driver gpio_rcar_device_driver = { .probe = gpio_rcar_probe, .remove = gpio_rcar_remove, .driver = { .name = "gpio_rcar", - .pm = &gpio_rcar_pm_ops, + .pm = pm_sleep_ptr(&gpio_rcar_pm_ops), .of_match_table = gpio_rcar_of_table, } }; diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index ec7fb9220a47..ba62b81aa8ae 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -64,8 +64,8 @@ static void rdc_gpio_set_value_impl(struct gpio_chip *chip, } /* set GPIO pin to value */ -static void rdc_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int rdc_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, + int value) { struct rdc321x_gpio *gpch; @@ -73,6 +73,8 @@ static void rdc_gpio_set_value(struct gpio_chip *chip, spin_lock(&gpch->lock); rdc_gpio_set_value_impl(chip, gpio, value); spin_unlock(&gpch->lock); + + return 0; } static int rdc_gpio_config(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c index 73c7260d89c0..f2238196faf1 100644 --- a/drivers/gpio/gpio-reg.c +++ b/drivers/gpio/gpio-reg.c @@ -57,7 +57,7 @@ static int gpio_reg_direction_input(struct gpio_chip *gc, unsigned offset) return r->direction & BIT(offset) ? 0 : -ENOTSUPP; } -static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) +static int gpio_reg_set(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; @@ -72,6 +72,8 @@ static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) r->out = val; writel_relaxed(val, r->reg); spin_unlock_irqrestore(&r->lock, flags); + + return 0; } static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) @@ -92,8 +94,8 @@ static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) return !!(val & mask); } -static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; @@ -102,6 +104,8 @@ static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, r->out = (r->out & ~*mask) | (*bits & *mask); writel_relaxed(r->out, r->reg); spin_unlock_irqrestore(&r->lock, flags); + + return 0; } static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 87c4225784cf..e8a32dfebdcb 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -260,9 +260,9 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->free = gpiochip_generic_free; chip->get = gpio_regmap_get; if (gpio->reg_set_base && gpio->reg_clr_base) - chip->set_rv = gpio_regmap_set_with_clear; + chip->set = gpio_regmap_set_with_clear; else if (gpio->reg_set_base) - chip->set_rv = gpio_regmap_set; + chip->set = gpio_regmap_set; chip->get_direction = gpio_regmap_get_direction; if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) { diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index c63352f2f1ec..bcfc323a8315 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -177,8 +177,8 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip, return 0; } -static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct rockchip_pin_bank *bank = gpiochip_get_data(gc); unsigned long flags; @@ -186,6 +186,8 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, raw_spin_lock_irqsave(&bank->slock, flags); rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr); raw_spin_unlock_irqrestore(&bank->slock, flags); + + return 0; } static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset) @@ -521,8 +523,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank) struct irq_chip_generic *gc; int ret; - bank->domain = irq_domain_create_linear(of_fwnode_handle(bank->of_node), 32, - &irq_generic_chip_ops, NULL); + bank->domain = irq_domain_create_linear(dev_fwnode(bank->dev), 32, &irq_generic_chip_ops, + NULL); if (!bank->domain) { dev_warn(bank->dev, "could not init irq domain for bank %s\n", bank->name); diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c index bf7f008f58d7..d46b40dd5283 100644 --- a/drivers/gpio/gpio-rtd.c +++ b/drivers/gpio/gpio-rtd.c @@ -275,7 +275,7 @@ static int rtd_gpio_set_config(struct gpio_chip *chip, unsigned int offset, } } -static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct rtd_gpio *data = gpiochip_get_data(chip); u32 mask = BIT(offset % 32); @@ -292,6 +292,8 @@ static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) else val &= ~mask; writel_relaxed(val, data->base + dato_reg_offset); + + return 0; } static int rtd_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 3f3ee36bc3cb..7f6a62f5d1ee 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -43,11 +43,14 @@ static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) BIT(offset); } -static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int sa1100_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { int reg = value ? R_GPSR : R_GPCR; writel_relaxed(BIT(offset), sa1100_gpio_chip(chip)->membase + reg); + + return 0; } static int sa1100_get_direction(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index d770a6f3d846..5005688f6e67 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -169,15 +169,15 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) /* * sama5d2_piobu_set() - gpiochip set */ -static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, - int value) +static int sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, + int value) { if (!value) value = PIOBU_LOW; else value = PIOBU_HIGH; - sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); + return sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); } static int sama5d2_piobu_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index ff0341b1222f..966d16a6d515 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -117,7 +117,7 @@ static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) return sch_gpio_reg_get(sch, gpio_num, GLV); } -static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) +static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; @@ -125,6 +125,8 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); spin_unlock_irqrestore(&sch->lock, flags); + + return 0; } static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, @@ -146,8 +148,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, * But we cannot prevent a short low pulse if direction is set to high * and an external pull-up is connected. */ - sch_gpio_set(gc, gpio_num, val); - return 0; + return sch_gpio_set(gc, gpio_num, val); } static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index ba4fccf3cc94..f95566998d30 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -178,14 +178,16 @@ static void __sch311x_gpio_set(struct sch311x_gpio_block *block, outb(data, block->runtime_reg + block->data_reg); } -static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) +static int sch311x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct sch311x_gpio_block *block = gpiochip_get_data(chip); spin_lock(&block->lock); __sch311x_gpio_set(block, offset, value); spin_unlock(&block->lock); + + return 0; } static int sch311x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index f638219a7c4f..050092583f79 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -39,7 +39,7 @@ #include "dev-sync-probe.h" #define GPIO_SIM_NGPIO_MAX 1024 -#define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ +#define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */ #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ static DEFINE_IDA(gpio_sim_ida); @@ -486,9 +486,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) gc->parent = dev; gc->fwnode = swnode; gc->get = gpio_sim_get; - gc->set_rv = gpio_sim_set; + gc->set = gpio_sim_set; gc->get_multiple = gpio_sim_get_multiple; - gc->set_multiple_rv = gpio_sim_set_multiple; + gc->set_multiple = gpio_sim_set_multiple; gc->direction_output = gpio_sim_direction_output; gc->direction_input = gpio_sim_direction_input; gc->get_direction = gpio_sim_get_direction; @@ -629,6 +629,7 @@ struct gpio_sim_line { unsigned int offset; char *name; + bool valid; /* There can only be one hog per line. */ struct gpio_sim_hog *hog; @@ -744,6 +745,36 @@ gpio_sim_set_line_names(struct gpio_sim_bank *bank, char **line_names) } } +static unsigned int gpio_sim_get_reserved_ranges_size(struct gpio_sim_bank *bank) +{ + struct gpio_sim_line *line; + unsigned int size = 0; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->valid) + continue; + + size += 2; + } + + return size; +} + +static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank, + u32 *ranges) +{ + struct gpio_sim_line *line; + int i = 0; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->valid) + continue; + + ranges[i++] = line->offset; + ranges[i++] = 1; + } +} + static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) { struct gpiod_hog *hog; @@ -844,9 +875,10 @@ static struct fwnode_handle * gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, struct fwnode_handle *parent) { + unsigned int prop_idx = 0, line_names_size, ranges_size; struct property_entry properties[GPIO_SIM_PROP_MAX]; - unsigned int prop_idx = 0, line_names_size; char **line_names __free(kfree) = NULL; + u32 *ranges __free(kfree) = NULL; memset(properties, 0, sizeof(properties)); @@ -870,6 +902,19 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, line_names, line_names_size); } + ranges_size = gpio_sim_get_reserved_ranges_size(bank); + if (ranges_size) { + ranges = kcalloc(ranges_size, sizeof(u32), GFP_KERNEL); + if (!ranges) + return ERR_PTR(-ENOMEM); + + gpio_sim_set_reserved_ranges(bank, ranges); + + properties[prop_idx++] = PROPERTY_ENTRY_U32_ARRAY_LEN( + "gpio-reserved-ranges", + ranges, ranges_size); + } + return fwnode_create_software_node(properties, parent); } @@ -1189,8 +1234,41 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_line_config_, name); +static ssize_t +gpio_sim_line_config_valid_show(struct config_item *item, char *page) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + + guard(mutex)(&dev->lock); + + return sprintf(page, "%c\n", line->valid ? '1' : '0'); +} + +static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + bool valid; + int ret; + + ret = kstrtobool(page, &valid); + if (ret) + return ret; + + guard(mutex)(&dev->lock); + + line->valid = valid; + + return count; +} + +CONFIGFS_ATTR(gpio_sim_line_config_, valid); + static struct configfs_attribute *gpio_sim_line_config_attrs[] = { &gpio_sim_line_config_attr_name, + &gpio_sim_line_config_attr_valid, NULL }; @@ -1399,6 +1477,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, line->parent = bank; line->offset = offset; + line->valid = true; list_add_tail(&line->siblings, &bank->line_list); return &line->group; diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c index 051bc99bdfb2..958034b9f3f3 100644 --- a/drivers/gpio/gpio-siox.c +++ b/drivers/gpio/gpio-siox.c @@ -160,8 +160,8 @@ static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset) return ret; } -static void gpio_siox_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int gpio_siox_set(struct gpio_chip *chip, + unsigned int offset, int value) { struct gpio_siox_ddata *ddata = gpiochip_get_data(chip); u8 mask = 1 << (19 - offset); @@ -174,6 +174,8 @@ static void gpio_siox_set(struct gpio_chip *chip, ddata->setdata[0] &= ~mask; mutex_unlock(&ddata->lock); + + return 0; } static int gpio_siox_direction_input(struct gpio_chip *chip, @@ -191,8 +193,7 @@ static int gpio_siox_direction_output(struct gpio_chip *chip, if (offset < 12) return -EINVAL; - gpio_siox_set(chip, offset, value); - return 0; + return gpio_siox_set(chip, offset, value); } static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 8cf3b171c599..969dddd3d6fa 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -306,7 +306,7 @@ static void gpio_la_poll_remove(struct platform_device *pdev) } static const struct of_device_id gpio_la_poll_of_match[] = { - { .compatible = GPIO_LA_NAME }, + { .compatible = "gpio-sloppy-logic-analyzer" }, { } }; MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index 6a3c4c625138..abd13c79ace0 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -169,8 +169,8 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); - sd->id = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), SDV_NUM_PUB_GPIOS, - sd->irq_base, 0, &irq_domain_sdv_ops, sd); + sd->id = irq_domain_create_legacy(dev_fwnode(&pdev->dev), SDV_NUM_PUB_GPIOS, sd->irq_base, + 0, &irq_domain_sdv_ops, sd); if (!sd->id) return -ENODEV; diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c index f027066365ff..3cc75c701ec4 100644 --- a/drivers/gpio/gpio-spacemit-k1.c +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -278,6 +278,7 @@ static const struct of_device_id spacemit_gpio_dt_ids[] = { { .compatible = "spacemit,k1-gpio" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); static struct platform_driver spacemit_gpio_driver = { .probe = spacemit_gpio_probe, diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 51539185400d..96a0e1211500 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -51,13 +51,8 @@ struct spear_spics { struct gpio_chip chip; }; -/* gpio framework specific routines */ -static int spics_get_value(struct gpio_chip *chip, unsigned offset) -{ - return -ENXIO; -} - -static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) +static int spics_set_value(struct gpio_chip *chip, unsigned int offset, + int value) { struct spear_spics *spics = gpiochip_get_data(chip); u32 tmp; @@ -74,18 +69,14 @@ static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) tmp &= ~(0x1 << spics->cs_value_bit); tmp |= value << spics->cs_value_bit; writel_relaxed(tmp, spics->base + spics->perip_cfg); -} -static int spics_direction_input(struct gpio_chip *chip, unsigned offset) -{ - return -ENXIO; + return 0; } static int spics_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - spics_set_value(chip, offset, value); - return 0; + return spics_set_value(chip, offset, value); } static int spics_request(struct gpio_chip *chip, unsigned offset) @@ -148,9 +139,7 @@ static int spics_gpio_probe(struct platform_device *pdev) spics->chip.base = -1; spics->chip.request = spics_request; spics->chip.free = spics_free; - spics->chip.direction_input = spics_direction_input; spics->chip.direction_output = spics_direction_output; - spics->chip.get = spics_get_value; spics->chip.set = spics_set_value; spics->chip.label = dev_name(&pdev->dev); spics->chip.parent = &pdev->dev; diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index c117c11bfb29..413bcd0a4240 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -108,10 +108,12 @@ static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset) return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA); } -static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value); + + return 0; } static void sprd_gpio_irq_mask(struct irq_data *data) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dce8ff322e47..5dd4c21a8e60 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -54,7 +54,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(ret & mask); } -static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int stmpe_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; @@ -67,9 +67,9 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) * For them we need to write 0 to clear and 1 to set. */ if (stmpe->regs[STMPE_IDX_GPSR_LSB] == stmpe->regs[STMPE_IDX_GPCR_LSB]) - stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); - else - stmpe_reg_write(stmpe, reg, mask); + return stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); + + return stmpe_reg_write(stmpe, reg, mask); } static int stmpe_gpio_get_direction(struct gpio_chip *chip, @@ -98,8 +98,11 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip, struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; u8 mask = BIT(offset % 8); + int ret; - stmpe_gpio_set(chip, offset, val); + ret = stmpe_gpio_set(chip, offset, val); + if (ret) + return ret; return stmpe_set_bits(stmpe, reg, mask, mask); } diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 5a6406d1f03a..493c027afdd6 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -113,7 +113,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio) * * Set the shadow value and call ltq_ebu_apply. */ -static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) +static int xway_stp_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct xway_stp *chip = gpiochip_get_data(gc); @@ -124,6 +124,8 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) xway_stp_w32(chip->virt, chip->shadow, XWAY_STP_CPU0); if (!chip->reserved) xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0); + + return 0; } /** @@ -136,9 +138,7 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) */ static int xway_stp_dir_out(struct gpio_chip *gc, unsigned gpio, int val) { - xway_stp_set(gc, gpio, val); - - return 0; + return xway_stp_set(gc, gpio, val); } /** diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 5ab394ec81e6..40064d4cf47f 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -40,8 +40,8 @@ struct syscon_gpio_data { unsigned int bit_count; unsigned int dat_bit_offset; unsigned int dir_bit_offset; - void (*set)(struct gpio_chip *chip, - unsigned offset, int value); + int (*set)(struct gpio_chip *chip, unsigned int offset, + int value); }; struct syscon_gpio_priv { @@ -68,17 +68,17 @@ static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offs % SYSCON_REG_BITS)); } -static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int syscon_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; - regmap_update_bits(priv->syscon, - (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, - BIT(offs % SYSCON_REG_BITS), - val ? BIT(offs % SYSCON_REG_BITS) : 0); + return regmap_update_bits(priv->syscon, + (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, + BIT(offs % SYSCON_REG_BITS), + val ? BIT(offs % SYSCON_REG_BITS) : 0); } static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) @@ -115,9 +115,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val) BIT(offs % SYSCON_REG_BITS)); } - chip->set(chip, offset, val); - - return 0; + return chip->set(chip, offset, val); } static const struct syscon_gpio_data clps711x_mctrl_gpio = { @@ -127,8 +125,8 @@ static const struct syscon_gpio_data clps711x_mctrl_gpio = { .dat_bit_offset = 0x40 * 8 + 8, }; -static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, - int val) +static int rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; @@ -144,6 +142,8 @@ static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, data); if (ret < 0) dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + + return ret; } static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = { @@ -156,7 +156,8 @@ static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = { #define KEYSTONE_LOCK_BIT BIT(0) -static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int keystone_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; @@ -165,7 +166,7 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; if (!val) - return; + return 0; ret = regmap_update_bits( priv->syscon, @@ -174,6 +175,8 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT); if (ret < 0) dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + + return ret; } static const struct syscon_gpio_data keystone_dsp_gpio = { diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index a415e6d36173..ba5a8ede8912 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -90,7 +90,7 @@ static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(readl(gplr) & BIT(shift)); } -static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tng_gpio *priv = gpiochip_get_data(chip); void __iomem *reg; @@ -101,6 +101,8 @@ static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) guard(raw_spinlock_irqsave)(&priv->lock); writel(BIT(shift), reg); + + return 0; } static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 8cf676fd0a0b..1869ee7f9423 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -183,9 +183,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev) if (ret != 0) return ret; - tb10x_gpio->domain = irq_domain_create_linear(of_fwnode_handle(np), - tb10x_gpio->gc.ngpio, - &irq_generic_chip_ops, NULL); + tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev), tb10x_gpio->gc.ngpio, + &irq_generic_chip_ops, NULL); if (!tb10x_gpio->domain) { return -ENOMEM; } diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index e62ee7e56908..90d048f9da08 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -49,7 +49,7 @@ static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(ret & mask); } -static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) +static int tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; @@ -57,7 +57,7 @@ static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int va unsigned int pos = offset % 8; u8 data[] = {val ? BIT(pos) : 0, BIT(pos)}; - tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); + return tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); } static int tc3589x_gpio_direction_output(struct gpio_chip *chip, @@ -67,8 +67,11 @@ static int tc3589x_gpio_direction_output(struct gpio_chip *chip, struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODIR0 + offset / 8; unsigned int pos = offset % 8; + int ret; - tc3589x_gpio_set(chip, offset, val); + ret = tc3589x_gpio_set(chip, offset, val); + if (ret) + return ret; return tc3589x_set_bits(tc3589x, reg, BIT(pos), BIT(pos)); } diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 9ad286adf263..15a5762a82c2 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -146,12 +146,14 @@ static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) tegra_gpio_disable(tgi, offset); } -static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); + + return 0; } static int tegra_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d27bfac6c9f5..5fd3ec3e2c53 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -202,6 +202,28 @@ static int tegra186_init_valid_mask(struct gpio_chip *chip, return 0; } +static int tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + int level) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + void __iomem *base; + u32 value; + + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return -ENODEV; + + value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); + if (level == 0) + value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + else + value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + + writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + + return 0; +} + static int tegra186_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -249,9 +271,12 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); void __iomem *base; u32 value; + int ret; /* configure output level first */ - chip->set(chip, offset, level); + ret = tegra186_gpio_set(chip, offset, level); + if (ret) + return ret; base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) @@ -359,26 +384,6 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) return value & BIT(0); } -static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, - int level) -{ - struct tegra_gpio *gpio = gpiochip_get_data(chip); - void __iomem *base; - u32 value; - - base = tegra186_gpio_get_base(gpio, offset); - if (WARN_ON(base == NULL)) - return; - - value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); - if (level == 0) - value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - else - value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - - writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -} - static int tegra186_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 5b851e904c11..be96853063ba 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -116,8 +116,8 @@ static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line) return 0; } -static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, - int value) +static int thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, + int value) { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); int bank = line / 64; @@ -127,6 +127,8 @@ static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, (bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR); writeq(BIT_ULL(bank_bit), reg); + + return 0; } static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, @@ -269,9 +271,9 @@ static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line) return masked_bits != 0; } -static void thunderx_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, - unsigned long *bits) +static int thunderx_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) { int bank; u64 set_bits, clear_bits; @@ -283,6 +285,8 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET); writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR); } + + return 0; } static void thunderx_gpio_irq_ack(struct irq_data *d) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index cb303a26f4d3..f488939dd00a 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -80,10 +80,9 @@ static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, return timbgpio_update_bit(gpio, nr, TGPIODIR, false); } -static void timbgpio_gpio_set(struct gpio_chip *gpio, - unsigned nr, int val) +static int timbgpio_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { - timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); + return timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); } static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) @@ -138,7 +137,7 @@ static int timbgpio_irq_type(struct irq_data *d, unsigned trigger) u32 ver; int ret = 0; - if (offset < 0 || offset > tgpio->gpio.ngpio) + if (offset < 0 || offset >= tgpio->gpio.ngpio) return -EINVAL; ver = ioread32(tgpio->membase + TGPIO_VER); diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index effb7b8ff81f..866ff2d436d5 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -25,7 +25,7 @@ struct tpic2810 { struct mutex lock; }; -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value); static int tpic2810_get_direction(struct gpio_chip *chip, unsigned offset) @@ -34,19 +34,11 @@ static int tpic2810_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_OUT; } -static int tpic2810_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - /* This device is output only */ - return -EINVAL; -} - static int tpic2810_direction_output(struct gpio_chip *chip, unsigned offset, int value) { /* This device always output */ - tpic2810_set(chip, offset, value); - return 0; + return tpic2810_set(chip, offset, value); } static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) @@ -68,22 +60,25 @@ static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) mutex_unlock(&gpio->lock); } -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value) { tpic2810_set_mask_bits(chip, BIT(offset), value ? BIT(offset) : 0); + + return 0; } -static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { tpic2810_set_mask_bits(chip, *mask, *bits); + + return 0; } static const struct gpio_chip template_chip = { .label = "tpic2810", .owner = THIS_MODULE, .get_direction = tpic2810_get_direction, - .direction_input = tpic2810_direction_input, .direction_output = tpic2810_direction_output, .set = tpic2810_set, .set_multiple = tpic2810_set_multiple, diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 8f5827554e1e..84b17b83476f 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -37,10 +37,8 @@ static int tps65086_gpio_direction_output(struct gpio_chip *chip, struct tps65086_gpio *gpio = gpiochip_get_data(chip); /* Set the initial value */ - regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, - BIT(4 + offset), value ? BIT(4 + offset) : 0); - - return 0; + return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); } static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -55,13 +53,13 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) return val & BIT(4 + offset); } -static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) +static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tps65086_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, - BIT(4 + offset), value ? BIT(4 + offset) : 0); + return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); } static const struct gpio_chip template_chip = { diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index d7d9d50dcddf..3b4c41f5ef55 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -34,34 +34,28 @@ static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & (TPS65218_ENABLE2_GPIO1 << offset)); } -static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65218_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); struct tps65218 *tps65218 = tps65218_gpio->tps65218; if (value) - tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_PROTECT_L1); - else - tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_PROTECT_L1); + return tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); + + return tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); } static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { /* Only drives GPOs */ - tps65218_gpio_set(gc, offset, value); - return 0; -} - -static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) -{ - return -EPERM; + return tps65218_gpio_set(gc, offset, value); } static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) @@ -174,7 +168,6 @@ static const struct gpio_chip template_chip = { .owner = THIS_MODULE, .request = tps65218_gpio_request, .direction_output = tps65218_gpio_output, - .direction_input = tps65218_gpio_input, .get = tps65218_gpio_get, .set = tps65218_gpio_set, .set_config = tps65218_gpio_set_config, diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 526640c39a11..158f63bcf10c 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * GPIO driver for TI TPS65219 PMICs + * GPIO driver for TI TPS65214/TPS65215/TPS65219 PMICs * - * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2022, 2025 Texas Instruments Incorporated - http://www.ti.com/ */ #include @@ -13,20 +13,48 @@ #include #define TPS65219_GPIO0_DIR_MASK BIT(3) -#define TPS65219_GPIO0_OFFSET 2 -#define TPS65219_GPIO0_IDX 0 +#define TPS65214_GPIO0_DIR_MASK BIT(1) +#define TPS6521X_GPIO0_OFFSET 2 +#define TPS6521X_GPIO0_IDX 0 + +/* + * TPS65214 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin9 ) -> bit_offset 0 + * + * TPS65215 & TPS65219 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin8 ) -> bit_offset 0 + * Linux gpio offset 2 -> GPO2 (pin17) -> bit_offset 1 + */ struct tps65219_gpio { + int (*change_dir)(struct gpio_chip *gc, unsigned int offset, unsigned int dir); struct gpio_chip gpio_chip; struct tps65219 *tps; }; +static int tps65214_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + int ret, val; + + if (offset != TPS6521X_GPIO0_IDX) + return GPIO_LINE_DIRECTION_OUT; + + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, &val); + if (ret) + return ret; + + return !(val & TPS65214_GPIO0_DIR_MASK); +} + static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); int ret, val; - if (offset != TPS65219_GPIO0_IDX) + if (offset != TPS6521X_GPIO0_IDX) return GPIO_LINE_DIRECTION_OUT; ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); @@ -42,7 +70,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) struct device *dev = gpio->tps->dev; int ret, val; - if (offset != TPS65219_GPIO0_IDX) { + if (offset != TPS6521X_GPIO0_IDX) { dev_err(dev, "GPIO%d is output only, cannot get\n", offset); return -ENOTSUPP; } @@ -65,19 +93,18 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) return ret; } -static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); - struct device *dev = gpio->tps->dev; int v, mask, bit; - bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; + bit = (offset == TPS6521X_GPIO0_IDX) ? TPS6521X_GPIO0_OFFSET : offset - 1; mask = BIT(bit); v = value ? mask : 0; - if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v)) - dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value); + return regmap_update_bits(gpio->tps->regmap, + TPS65219_REG_GENERAL_CONFIG, mask, v); } static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, @@ -112,12 +139,39 @@ static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int off return -ENOTSUPP; } +static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, + unsigned int direction) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + int val, ret; + + /** + * Verified if GPIO or GPO in parent function + * Masked value: 0 = GPIO, 1 = VSEL + */ + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); + if (ret) + return ret; + + ret = !!(val & BIT(TPS65219_GPIO0_DIR_MASK)); + if (ret) + dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset); + + ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, + TPS65214_GPIO0_DIR_MASK, direction); + if (ret) + dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset); + + return ret; +} + static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); struct device *dev = gpio->tps->dev; - if (offset != TPS65219_GPIO0_IDX) { + if (offset != TPS6521X_GPIO0_IDX) { dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset); return -ENOTSUPP; } @@ -125,21 +179,36 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN) return 0; - return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); + return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN); } static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) { + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + tps65219_gpio_set(gc, offset, value); - if (offset != TPS65219_GPIO0_IDX) + if (offset != TPS6521X_GPIO0_IDX) return 0; if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return 0; - return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); + return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT); } +static const struct gpio_chip tps65214_template_chip = { + .label = "tps65214-gpio", + .owner = THIS_MODULE, + .get_direction = tps65214_gpio_get_direction, + .direction_input = tps65219_gpio_direction_input, + .direction_output = tps65219_gpio_direction_output, + .get = tps65219_gpio_get, + .set = tps65219_gpio_set, + .base = -1, + .ngpio = 2, + .can_sleep = true, +}; + static const struct gpio_chip tps65219_template_chip = { .label = "tps65219-gpio", .owner = THIS_MODULE, @@ -155,6 +224,7 @@ static const struct gpio_chip tps65219_template_chip = { static int tps65219_gpio_probe(struct platform_device *pdev) { + enum pmic_id chip = platform_get_device_id(pdev)->driver_data; struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); struct tps65219_gpio *gpio; @@ -162,22 +232,38 @@ static int tps65219_gpio_probe(struct platform_device *pdev) if (!gpio) return -ENOMEM; + if (chip == TPS65214) { + gpio->gpio_chip = tps65214_template_chip; + gpio->change_dir = tps65214_gpio_change_direction; + } else if (chip == TPS65219) { + gpio->gpio_chip = tps65219_template_chip; + gpio->change_dir = tps65219_gpio_change_direction; + } else { + return -ENODATA; + } + gpio->tps = tps; - gpio->gpio_chip = tps65219_template_chip; gpio->gpio_chip.parent = tps->dev; return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio); } +static const struct platform_device_id tps6521x_gpio_id_table[] = { + { "tps65214-gpio", TPS65214 }, + { "tps65219-gpio", TPS65219 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); + static struct platform_driver tps65219_gpio_driver = { .driver = { .name = "tps65219-gpio", }, .probe = tps65219_gpio_probe, + .id_table = tps6521x_gpio_id_table, }; module_platform_driver(tps65219_gpio_driver); -MODULE_ALIAS("platform:tps65219-gpio"); MODULE_AUTHOR("Jonathan Cormier "); -MODULE_DESCRIPTION("TPS65219 GPIO driver"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index d277aa951143..aaacbb54bf5d 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -40,13 +40,13 @@ static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & (1 << offset)); } -static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps6586x_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc); - tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, - value << offset, 1 << offset); + return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, + value << offset, 1 << offset); } static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -54,8 +54,11 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, { struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc); uint8_t val, mask; + int ret; - tps6586x_gpio_set(gc, offset, value); + ret = tps6586x_gpio_set(gc, offset, value); + if (ret) + return ret; val = 0x1 << (offset * 2); mask = 0x3 << (offset * 2); diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 187d21580573..25e9f41efe78 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -36,18 +36,18 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) return 0; } -static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65910_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); struct tps65910 *tps65910 = tps65910_gpio->tps65910; if (value) - regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); - else - regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); + return regmap_set_bits(tps65910->regmap, + TPS65910_GPIO0 + offset, GPIO_SET_MASK); + + return regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); } static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -55,9 +55,12 @@ static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, { struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); struct tps65910 *tps65910 = tps65910_gpio->tps65910; + int ret; /* Set the initial value */ - tps65910_gpio_set(gc, offset, value); + ret = tps65910_gpio_set(gc, offset, value); + if (ret) + return ret; return regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index fab771cb6a87..7a2c5685c2fd 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -49,10 +49,13 @@ static int tps65912_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value) { struct tps65912_gpio *gpio = gpiochip_get_data(gc); + int ret; /* Set the initial value */ - regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, - GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + ret = regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + if (ret) + return ret; return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, GPIO_CFG_MASK, GPIO_CFG_MASK); @@ -73,13 +76,13 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) return 0; } -static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65912_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65912_gpio *gpio = gpiochip_get_data(gc); - regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, - GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); } static const struct gpio_chip template_chip = { diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c index 532deaddfd4e..d4fbdf90e190 100644 --- a/drivers/gpio/gpio-tps68470.c +++ b/drivers/gpio/gpio-tps68470.c @@ -70,8 +70,8 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc, GPIO_LINE_DIRECTION_IN; } -static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); struct regmap *regmap = tps68470_gpio->tps68470_regmap; @@ -82,7 +82,8 @@ static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, offset -= TPS68470_N_REGULAR_GPIO; } - regmap_update_bits(regmap, reg, BIT(offset), value ? BIT(offset) : 0); + return regmap_update_bits(regmap, reg, BIT(offset), + value ? BIT(offset) : 0); } static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, @@ -90,9 +91,12 @@ static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, { struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); struct regmap *regmap = tps68470_gpio->tps68470_regmap; + int ret; /* Set the initial value */ - tps68470_gpio_set(gc, offset, value); + ret = tps68470_gpio_set(gc, offset, value); + if (ret) + return ret; /* rest are always outputs */ if (offset >= TPS68470_N_REGULAR_GPIO) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 18f523a15b3c..27dd09273292 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -93,14 +93,16 @@ static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset, tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); } -static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); guard(raw_spinlock_irqsave)(&gpio->spinlock); _tqmx86_gpio_set(gpio, offset, value); + + return 0; } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 5c806140fdf0..d9ee8fc77ccd 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -95,16 +95,16 @@ static int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(reg & priv->input_bit); } -static void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); if (value) - regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, - TS4900_GPIO_OUT); - else - regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); + return regmap_update_bits(priv->regmap, offset, + TS4900_GPIO_OUT, TS4900_GPIO_OUT); + + return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); } static const struct regmap_config ts4900_regmap_config = { diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 61cbec5c06a7..3c7f2efe10fd 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -244,7 +244,7 @@ static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val) return 0; } -static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { struct ts5500_priv *priv = gpiochip_get_data(chip); const struct ts5500_dio line = priv->pinout[offset]; @@ -256,6 +256,8 @@ static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) else ts5500_clear_mask(line.value_mask, line.value_addr); spin_unlock_irqrestore(&priv->lock, flags); + + return 0; } static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 0d17985a5fdc..a33dc7c7e7a0 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -120,7 +120,7 @@ static u8 cached_leden; * external pullup is needed. We could also expose the integrated PWM * as a LED brightness control; we initialize it as "always on". */ -static void twl4030_led_set_value(int led, int value) +static int twl4030_led_set_value(int led, int value) { u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; @@ -132,8 +132,8 @@ static void twl4030_led_set_value(int led, int value) else cached_leden |= mask; - WARN_ON_ONCE(twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, - TWL4030_LED_LEDEN_REG)); + return twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, + TWL4030_LED_LEDEN_REG); } static int twl4030_set_gpio_direction(int gpio, int is_input) @@ -278,7 +278,7 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) mutex_lock(&priv->mutex); if (offset >= TWL4030_GPIO_MAX) { - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); + WARN_ON_ONCE(twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1)); goto out; } @@ -334,15 +334,16 @@ static int twl_get(struct gpio_chip *chip, unsigned offset) return ret; } -static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); + int ret; mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) - twl4030_set_gpio_dataout(offset, value); + ret = twl4030_set_gpio_dataout(offset, value); else - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + ret = twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); if (value) priv->out_state |= BIT(offset); @@ -350,6 +351,8 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value) priv->out_state &= ~BIT(offset); mutex_unlock(&priv->mutex); + + return ret; } static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) @@ -373,9 +376,7 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) priv->direction |= BIT(offset); mutex_unlock(&priv->mutex); - twl_set(chip, offset, value); - - return ret; + return twl_set(chip, offset, value); } static int twl_get_direction(struct gpio_chip *chip, unsigned offset) @@ -523,7 +524,7 @@ static int gpio_twl4030_probe(struct platform_device *pdev) return irq_base; } - irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), TWL4030_GPIO_MAX, irq_base, 0, + irq_domain_create_legacy(dev_fwnode(&pdev->dev), TWL4030_GPIO_MAX, irq_base, 0, &irq_domain_simple_ops, NULL); ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base); diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index b9171bf66168..4ec9bcd40439 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -37,14 +37,8 @@ static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset) return GPIO_LINE_DIRECTION_OUT; } -static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, - int value) -{ - /* This only drives GPOs, and can't change direction */ - return 0; -} - -static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl6040gpo_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret; @@ -52,14 +46,21 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); if (ret < 0) - return; + return ret; if (value) gpoctl = ret | BIT(offset); else gpoctl = ret & ~BIT(offset); - twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); + return twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); +} + +static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned int offset, + int value) +{ + /* This only drives GPOs, and can't change direction */ + return twl6040gpo_set(chip, offset, value); } static struct gpio_chip twl6040gpo_chip = { diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index d738da8718f9..197bb1d22b3c 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -138,14 +138,16 @@ static int uniphier_gpio_get(struct gpio_chip *chip, unsigned int offset) return uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DATA); } -static void uniphier_gpio_set(struct gpio_chip *chip, - unsigned int offset, int val) +static int uniphier_gpio_set(struct gpio_chip *chip, + unsigned int offset, int val) { uniphier_gpio_offset_write(chip, offset, UNIPHIER_GPIO_PORT_DATA, val); + + return 0; } -static void uniphier_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int uniphier_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) { unsigned long i, bank, bank_mask, bank_bits; @@ -156,6 +158,8 @@ static void uniphier_gpio_set_multiple(struct gpio_chip *chip, uniphier_gpio_bank_write(chip, bank, UNIPHIER_GPIO_PORT_DATA, bank_mask, bank_bits); } + + return 0; } static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index e55d28a8a66f..15e495c109d2 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -128,45 +128,50 @@ static int vprbrd_gpioa_get(struct gpio_chip *chip, return answer; } -static void vprbrd_gpioa_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int vprbrd_gpioa_set(struct gpio_chip *chip, unsigned int offset, + int value) { - int ret; + int ret = 0; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); struct vprbrd *vb = gpio->vb; struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; - if (gpio->gpioa_out & (1 << offset)) { - if (value) - gpio->gpioa_val |= (1 << offset); - else - gpio->gpioa_val &= ~(1 << offset); + if (!(gpio->gpioa_out & (1 << offset))) + return 0; - mutex_lock(&vb->lock); + if (value) + gpio->gpioa_val |= (1 << offset); + else + gpio->gpioa_val &= ~(1 << offset); - gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; - gamsg->clk = 0x00; - gamsg->offset = offset; - gamsg->t1 = 0x00; - gamsg->t2 = 0x00; - gamsg->invert = 0x00; - gamsg->pwmlevel = 0x00; - gamsg->outval = value; - gamsg->risefall = 0x00; - gamsg->answer = 0x00; - gamsg->__fill = 0x00; + mutex_lock(&vb->lock); - ret = usb_control_msg(vb->usb_dev, - usb_sndctrlpipe(vb->usb_dev, 0), - VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, - 0x0000, 0x0000, gamsg, - sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; + gamsg->clk = 0x00; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = value; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; - mutex_unlock(&vb->lock); + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gamsg, + sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); - if (ret != sizeof(struct vprbrd_gpioa_msg)) - dev_err(chip->parent, "usb error setting pin value\n"); + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) { + dev_err(chip->parent, "usb error setting pin value\n"); + return -EREMOTEIO; } + + return 0; } static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, @@ -304,37 +309,42 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip, return (gpio->gpiob_val >> offset) & 0x1; } -static void vprbrd_gpiob_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int vprbrd_gpiob_set(struct gpio_chip *chip, unsigned int offset, + int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); struct vprbrd *vb = gpio->vb; struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; - if (gpio->gpiob_out & (1 << offset)) { - if (value) - gpio->gpiob_val |= (1 << offset); - else - gpio->gpiob_val &= ~(1 << offset); + if (!(gpio->gpiob_out & (1 << offset))) + return 0; - mutex_lock(&vb->lock); + if (value) + gpio->gpiob_val |= (1 << offset); + else + gpio->gpiob_val &= ~(1 << offset); - gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; - gbmsg->val = cpu_to_be16(value << offset); - gbmsg->mask = cpu_to_be16(0x0001 << offset); + mutex_lock(&vb->lock); - ret = usb_control_msg(vb->usb_dev, - usb_sndctrlpipe(vb->usb_dev, 0), - VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, - 0x0000, 0x0000, gbmsg, - sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; + gbmsg->val = cpu_to_be16(value << offset); + gbmsg->mask = cpu_to_be16(0x0001 << offset); - mutex_unlock(&vb->lock); + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gbmsg, + sizeof(struct vprbrd_gpiob_msg), + VPRBRD_USB_TIMEOUT_MS); - if (ret != sizeof(struct vprbrd_gpiob_msg)) - dev_err(chip->parent, "usb error setting pin value\n"); + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpiob_msg)) { + dev_err(chip->parent, "usb error setting pin value\n"); + return -EREMOTEIO; } + + return 0; } static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, @@ -368,16 +378,14 @@ static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, gpio->gpiob_out |= (1 << offset); mutex_lock(&vb->lock); - ret = vprbrd_gpiob_setdir(vb, offset, 1); - if (ret) - dev_err(chip->parent, "usb error setting pin to output\n"); - mutex_unlock(&vb->lock); + if (ret) { + dev_err(chip->parent, "usb error setting pin to output\n"); + return ret; + } - vprbrd_gpiob_set(chip, offset, value); - - return ret; + return vprbrd_gpiob_set(chip, offset, value); } /* ----- end of gpio b chip ---------------------------------------------- */ diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index ac39da17a29b..17e040991e46 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -194,11 +194,12 @@ static int virtio_gpio_get(struct gpio_chip *gc, unsigned int gpio) return ret ? ret : value; } -static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct virtio_gpio *vgpio = gpiochip_get_data(gc); - virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL); + return virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, + NULL); } /* Interrupt handling */ @@ -526,7 +527,6 @@ static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio, static int virtio_gpio_probe(struct virtio_device *vdev) { - struct virtio_gpio_config config; struct device *dev = &vdev->dev; struct virtio_gpio *vgpio; struct irq_chip *gpio_irq_chip; @@ -539,9 +539,11 @@ static int virtio_gpio_probe(struct virtio_device *vdev) return -ENOMEM; /* Read configuration */ - virtio_cread_bytes(vdev, 0, &config, sizeof(config)); - gpio_names_size = le32_to_cpu(config.gpio_names_size); - ngpio = le16_to_cpu(config.ngpio); + gpio_names_size = + virtio_cread32(vdev, offsetof(struct virtio_gpio_config, + gpio_names_size)); + ngpio = virtio_cread16(vdev, offsetof(struct virtio_gpio_config, + ngpio)); if (!ngpio) { dev_err(dev, "Number of GPIOs can't be zero\n"); return -EINVAL; diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index eab6726953b4..a10eab7d2617 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -215,9 +215,7 @@ static int gpio_virtuser_set_array_value(struct gpio_descs *descs, struct gpio_virtuser_irq_work_context ctx; if (!atomic) - return gpiod_set_array_value_cansleep(descs->ndescs, - descs->desc, - descs->info, values); + return gpiod_multi_set_value_cansleep(descs, values); gpio_virtuser_init_irq_work_context(&ctx); ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_set_value_array_atomic); diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 8fd6c3913d69..84b3a973a503 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -127,8 +127,7 @@ static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr) return ret; } -static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, - int val) +static int vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { struct vx855_gpio *vg = gpiochip_get_data(gpio); unsigned long flags; @@ -136,7 +135,7 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, /* True GPI cannot be switched to output mode */ if (nr < NR_VX855_GPI) - return; + return -EPERM; spin_lock_irqsave(&vg->lock, flags); reg_out = inl(vg->io_gpo); @@ -153,6 +152,8 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, } outl(reg_out, vg->io_gpo); spin_unlock_irqrestore(&vg->lock, flags); + + return 0; } static int vx855gpio_direction_output(struct gpio_chip *gpio, diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c index 2bba27b13947..4af504c23e6f 100644 --- a/drivers/gpio/gpio-wcd934x.c +++ b/drivers/gpio/gpio-wcd934x.c @@ -46,9 +46,12 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int val) { struct wcd_gpio_data *data = gpiochip_get_data(chip); + int ret; - regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, - WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + ret = regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + if (ret) + return ret; return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, WCD_PIN_MASK(pin), @@ -65,12 +68,13 @@ static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) return !!(value & WCD_PIN_MASK(pin)); } -static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) +static int wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) { struct wcd_gpio_data *data = gpiochip_get_data(chip); - regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, - WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0); + return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, + WCD_PIN_MASK(pin), + val ? WCD_PIN_MASK(pin) : 0); } static int wcd_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 1ec24f6f9300..4a5e20e936a9 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -200,18 +200,15 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) +static int wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); if (reg < 0) - return; + return 0; - if (value) - regmap_set_bits(wg->regmap, reg, 1); - else - regmap_clear_bits(wg->regmap, reg, 1); + return regmap_assign_bits(wg->regmap, reg, 1, value); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, diff --git a/drivers/gpio/gpio-winbond.c b/drivers/gpio/gpio-winbond.c index 4b61d975cc0e..dcfda738fd69 100644 --- a/drivers/gpio/gpio-winbond.c +++ b/drivers/gpio/gpio-winbond.c @@ -458,17 +458,19 @@ static int winbond_gpio_direction_out(struct gpio_chip *gc, return 0; } -static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, - int val) +static int winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) { unsigned long *base = gpiochip_get_data(gc); const struct winbond_gpio_info *info; + int ret; if (!winbond_gpio_get_info(&offset, &info)) - return; + return -EACCES; - if (winbond_sio_enter(*base) != 0) - return; + ret = winbond_sio_enter(*base); + if (ret) + return ret; winbond_sio_select_logical(*base, info->dev); @@ -481,6 +483,8 @@ static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, winbond_sio_reg_bclear(*base, info->datareg, offset); winbond_sio_leave(*base); + + return 0; } static struct gpio_chip winbond_gpio_chip = { diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 61bb83a1e8ae..f03c0e808fab 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -58,13 +58,14 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) return 0; } -static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm831x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; - wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, - value << offset); + return wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, + value << offset); } static int wm831x_gpio_direction_out(struct gpio_chip *chip, @@ -85,9 +86,7 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip, return ret; /* Can only set GPIO state once it's in output mode */ - wm831x_gpio_set(chip, offset, value); - - return 0; + return wm831x_gpio_set(chip, offset, value); } static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index 2421cf606ed6..46923b23a72e 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -48,15 +48,16 @@ static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset) return 0; } -static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8350_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); struct wm8350 *wm8350 = wm8350_gpio->wm8350; if (value) - wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); - else - wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + return wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + + return wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); } static int wm8350_gpio_direction_out(struct gpio_chip *chip, @@ -72,9 +73,7 @@ static int wm8350_gpio_direction_out(struct gpio_chip *chip, return ret; /* Don't have an atomic direction/value setup */ - wm8350_gpio_set(chip, offset, value); - - return 0; + return wm8350_gpio_set(chip, offset, value); } static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index bf05c9b5882b..df47a27f508d 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -89,7 +89,8 @@ static int wm8994_gpio_direction_out(struct gpio_chip *chip, WM8994_GPN_DIR | WM8994_GPN_LVL, value); } -static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8994_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); struct wm8994 *wm8994 = wm8994_gpio->wm8994; @@ -97,7 +98,8 @@ static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) if (value) value = WM8994_GPN_LVL; - wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); + return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, + value); } static int wm8994_gpio_set_config(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index fb4b0c67aeef..4f627de3f56c 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -62,7 +62,7 @@ static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) iowrite32(setval, chip->base + bank_offset); } -static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct xgene_gpio *chip = gpiochip_get_data(gc); unsigned long flags; @@ -70,6 +70,8 @@ static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) spin_lock_irqsave(&chip->lock, flags); __xgene_gpio_set(gc, offset, val); spin_unlock_irqrestore(&chip->lock, flags); + + return 0; } static int xgene_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index c58a7e1349b4..83675ac81077 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -148,7 +148,7 @@ static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) * This function writes the specified value in to the specified signal of the * GPIO device. */ -static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); @@ -162,6 +162,8 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state); raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; } /** @@ -173,8 +175,8 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) * This function writes the specified values into the specified signals of the * GPIO devices. */ -static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { DECLARE_BITMAP(hw_mask, 64); DECLARE_BITMAP(hw_bits, 64); @@ -194,6 +196,8 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, bitmap_copy(chip->state, state, 64); raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; } /** diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index b4b52213bcd9..aede6324387f 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -206,7 +206,6 @@ static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1); return 0; @@ -216,7 +215,6 @@ static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0); return 0; @@ -226,16 +224,16 @@ static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); return xlp_gpio_get_reg(priv->gpio_paddrv, gpio); } -static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) +static int xlp_gpio_set(struct gpio_chip *gc, unsigned int gpio, int state) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); + + return 0; } static int xlp_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 842cf875bb92..faadcb4b0b2d 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -102,16 +102,13 @@ static int xra1403_get(struct gpio_chip *chip, unsigned int offset) return !!(val & BIT(offset % 8)); } -static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) +static int xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) { - int ret; struct xra1403 *xra = gpiochip_get_data(chip); - ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), - BIT(offset % 8), value ? BIT(offset % 8) : 0); - if (ret) - dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n", - offset, ret); + return regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), + value ? BIT(offset % 8) : 0); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index c8af34a6368f..4418947a10e5 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -86,12 +86,6 @@ static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset) return !!(impwire & BIT(offset)); } -static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset, - int value) -{ - BUG(); /* output only; should never be called */ -} - static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset) { return GPIO_LINE_DIRECTION_OUT; /* output only */ @@ -109,7 +103,7 @@ static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset) return !!(expstate & BIT(offset)); } -static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset, +static int xtensa_expstate_set_value(struct gpio_chip *gc, unsigned int offset, int value) { unsigned long flags, saved_cpenable; @@ -120,6 +114,8 @@ static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset, __asm__ __volatile__("wrmsk_expstate %0, %1" :: "a" (val), "a" (mask)); disable_cp(flags, saved_cpenable); + + return 0; } static struct gpio_chip impwire_chip = { @@ -128,7 +124,6 @@ static struct gpio_chip impwire_chip = { .ngpio = 32, .get_direction = xtensa_impwire_get_direction, .get = xtensa_impwire_get_value, - .set = xtensa_impwire_set_value, }; static struct gpio_chip expstate_chip = { diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index d7230fd83f5d..29375bea2289 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -91,7 +91,7 @@ static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; } -static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +static int zevio_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct zevio_gpio *controller = gpiochip_get_data(chip); u32 val; @@ -105,6 +105,8 @@ static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); spin_unlock(&controller->lock); + + return 0; } static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 3dae63f3ea21..0ffd76e8951f 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -265,8 +265,8 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin) * upper 16 bits) based on the given pin number and sets the state of a * gpio pin to the specified value. The state is either 0 or non-zero. */ -static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, - int state) +static int zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) { unsigned int reg_offset, bank_num, bank_pin_num; struct zynq_gpio *gpio = gpiochip_get_data(chip); @@ -290,6 +290,8 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, ((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK); writel_relaxed(state, gpio->base_addr + reg_offset); + + return 0; } /** diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c index 2f3c9ebfa78d..5e651482e985 100644 --- a/drivers/gpio/gpio-zynqmp-modepin.c +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -57,8 +57,8 @@ static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin) * * Return: None. */ -static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, - int state) +static int modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) { u32 bootpin_val = 0; int ret; @@ -77,6 +77,8 @@ static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, ret = zynqmp_pm_bootmode_write(bootpin_val); if (ret) pr_err("modepin: set value error %d for pin %d\n", ret, pin); + + return ret; } /** @@ -102,7 +104,7 @@ static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, int state) { - return 0; + return modepin_gpio_set_value(chip, pin, state); } /** diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c index 219667315b2c..bfb04e67c4bc 100644 --- a/drivers/gpio/gpiolib-acpi-quirks.c +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -331,6 +331,33 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { .ignore_interrupt = "AMDI0030:00@11", }, }, + { + /* + * Wakeup only works when keyboard backlight is turned off + * https://gitlab.freedesktop.org/drm/amd/-/issues/4169 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 15"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@8", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 5.35 + * https://gitlab.freedesktop.org/drm/amd/-/issues/4482 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt PX13"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ASCP1A00:00@8", + }, + }, {} /* Terminating entry */ }; diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 4d5f83b17624..72422c5db364 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -319,7 +319,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_unhinge); */ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) { - devm_remove_action(dev, devm_gpiod_release_array, descs); + devm_release_action(dev, devm_gpiod_release_array, descs); } EXPORT_SYMBOL_GPL(devm_gpiod_put_array); diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index aeae6df8bec9..3bc93ccadb5b 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -85,44 +85,6 @@ static void devm_gpio_release(struct device *dev, void *res) gpio_free(*gpio); } -/** - * devm_gpio_request - request a GPIO for a managed device - * @dev: device to request the GPIO for - * @gpio: GPIO to allocate - * @label: the name of the requested GPIO - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_request(). - * GPIOs requested with this function will be automatically freed - * on driver detach. - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - * - * Returns: - * 0 on success, or negative errno on failure. - */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) -{ - unsigned *dr; - int rc; - - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - rc = gpio_request(gpio, label); - if (rc) { - devres_free(dr); - return rc; - } - - *dr = gpio; - devres_add(dev, dr); - - return 0; -} -EXPORT_SYMBOL_GPL(devm_gpio_request); - /** * devm_gpio_request_one - request a single GPIO with initial setup * @dev: device to request for diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 73ba73b31cb1..37ab78243fab 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -708,7 +708,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id, unsigned int idx, unsigned long *flags) { char propname[32]; /* 32 is max size of property name */ - enum of_gpio_flags of_flags; + enum of_gpio_flags of_flags = 0; const of_find_gpio_quirk *q; struct gpio_desc *desc; diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 3eebfac290c5..2257f7a498a1 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -8,7 +8,7 @@ #include -struct device; +struct device_node; struct fwnode_handle; struct gpio_chip; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 4a3aa09dad9d..b64106f1cb7b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -12,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +24,8 @@ #include "gpiolib.h" #include "gpiolib-sysfs.h" +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + struct kernfs_node; #define GPIO_IRQF_TRIGGER_NONE 0 @@ -34,15 +34,64 @@ struct kernfs_node; #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ GPIO_IRQF_TRIGGER_RISING) +enum { + GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION = 0, + GPIO_SYSFS_LINE_CLASS_ATTR_VALUE, + GPIO_SYSFS_LINE_CLASS_ATTR_EDGE, + GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW, + GPIO_SYSFS_LINE_CLASS_ATTR_SENTINEL, + GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, +}; + +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +enum { + GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION = 0, + GPIO_SYSFS_LINE_CHIP_ATTR_VALUE, + GPIO_SYSFS_LINE_CHIP_ATTR_SENTINEL, + GPIO_SYSFS_LINE_CHIP_ATTR_SIZE, +}; + struct gpiod_data { + struct list_head list; + struct gpio_desc *desc; + struct device *dev; struct mutex mutex; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct kernfs_node *value_kn; int irq; unsigned char irq_flags; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ bool direction_can_change; + + struct kobject *parent; + struct device_attribute dir_attr; + struct device_attribute val_attr; + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + struct device_attribute edge_attr; + struct device_attribute active_low_attr; + + struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE]; + struct attribute_group class_attr_group; + const struct attribute_group *class_attr_groups[2]; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + + struct attribute *chip_attrs[GPIO_SYSFS_LINE_CHIP_ATTR_SIZE]; + struct attribute_group chip_attr_group; + const struct attribute_group *chip_attr_groups[2]; +}; + +struct gpiodev_data { + struct list_head exported_lines; + struct gpio_device *gdev; + struct device *cdev_id; /* Class device by GPIO device ID */ +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + struct device *cdev_base; /* Class device by GPIO base */ +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ }; /* @@ -73,9 +122,10 @@ static DEFINE_MUTEX(sysfs_lock); */ static ssize_t direction_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; int value; @@ -88,11 +138,13 @@ static ssize_t direction_show(struct device *dev, } static ssize_t direction_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, const char *buf, + size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; - ssize_t status; + ssize_t status; guard(mutex)(&data->mutex); @@ -107,14 +159,14 @@ static ssize_t direction_store(struct device *dev, return status ? : size; } -static DEVICE_ATTR_RW(direction); -static ssize_t value_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t value_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; - ssize_t status; + ssize_t status; scoped_guard(mutex, &data->mutex) status = gpiod_get_value_cansleep(desc); @@ -125,10 +177,11 @@ static ssize_t value_show(struct device *dev, return sysfs_emit(buf, "%zd\n", status); } -static ssize_t value_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t value_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; ssize_t status; long value; @@ -145,8 +198,8 @@ static ssize_t value_store(struct device *dev, return size; } -static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { struct gpiod_data *data = priv; @@ -157,9 +210,8 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) } /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) +static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags) { - struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; unsigned long irq_flags; int ret; @@ -172,33 +224,29 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) if (data->irq < 0) return -EIO; - data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); - if (!data->value_kn) - return -ENODEV; - irq_flags = IRQF_SHARED; if (flags & GPIO_IRQF_TRIGGER_FALLING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; set_bit(FLAG_EDGE_FALLING, &desc->flags); } if (flags & GPIO_IRQF_TRIGGER_RISING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; set_bit(FLAG_EDGE_RISING, &desc->flags); } /* * FIXME: This should be done in the irq_request_resources callback - * when the irq is requested, but a few drivers currently fail - * to do so. + * when the irq is requested, but a few drivers currently fail to do + * so. * - * Remove this redundant call (along with the corresponding - * unlock) when those drivers have been fixed. + * Remove this redundant call (along with the corresponding unlock) + * when those drivers have been fixed. */ ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) - goto err_put_kn; + goto err_clr_bits; ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags, "gpiolib", data); @@ -211,10 +259,9 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) err_unlock: gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); -err_put_kn: +err_clr_bits: clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); - sysfs_put(data->value_kn); return ret; } @@ -223,9 +270,8 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) * Caller holds gpiod-data mutex (unless called after class-device * deregistration). */ -static void gpio_sysfs_free_irq(struct device *dev) +static void gpio_sysfs_free_irq(struct gpiod_data *data) { - struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; CLASS(gpio_chip_guard, guard)(desc); @@ -237,20 +283,20 @@ static void gpio_sysfs_free_irq(struct device *dev) gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); - sysfs_put(data->value_kn); } -static const char * const trigger_names[] = { +static const char *const trigger_names[] = { [GPIO_IRQF_TRIGGER_NONE] = "none", [GPIO_IRQF_TRIGGER_FALLING] = "falling", [GPIO_IRQF_TRIGGER_RISING] = "rising", [GPIO_IRQF_TRIGGER_BOTH] = "both", }; -static ssize_t edge_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t edge_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); int flags; scoped_guard(mutex, &data->mutex) @@ -262,10 +308,11 @@ static ssize_t edge_show(struct device *dev, return sysfs_emit(buf, "%s\n", trigger_names[flags]); } -static ssize_t edge_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t edge_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); ssize_t status = size; int flags; @@ -279,12 +326,12 @@ static ssize_t edge_store(struct device *dev, return size; if (data->irq_flags) - gpio_sysfs_free_irq(dev); + gpio_sysfs_free_irq(data); if (!flags) return size; - status = gpio_sysfs_request_irq(dev, flags); + status = gpio_sysfs_request_irq(data, flags); if (status) return status; @@ -292,17 +339,14 @@ static ssize_t edge_store(struct device *dev, return size; } -static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_set_active_low(struct device *dev, int value) +static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) { - struct gpiod_data *data = dev_get_drvdata(dev); unsigned int flags = data->irq_flags; struct gpio_desc *desc = data->desc; int status = 0; - if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; @@ -310,9 +354,9 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) /* reconfigure poll(2) support if enabled on one edge only */ if (flags == GPIO_IRQF_TRIGGER_FALLING || - flags == GPIO_IRQF_TRIGGER_RISING) { - gpio_sysfs_free_irq(dev); - status = gpio_sysfs_request_irq(dev, flags); + flags == GPIO_IRQF_TRIGGER_RISING) { + gpio_sysfs_free_irq(data); + status = gpio_sysfs_request_irq(data, flags); } gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); @@ -321,9 +365,10 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) } static ssize_t active_low_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); struct gpio_desc *desc = data->desc; int value; @@ -334,9 +379,11 @@ static ssize_t active_low_show(struct device *dev, } static ssize_t active_low_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, + const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); ssize_t status; long value; @@ -346,84 +393,189 @@ static ssize_t active_low_store(struct device *dev, guard(mutex)(&data->mutex); - return gpio_sysfs_set_active_low(dev, value) ?: size; + return gpio_sysfs_set_active_low(data, value) ?: size; } -static DEVICE_ATTR_RW(active_low); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = kobj_to_dev(kobj); - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; + struct device_attribute *dev_attr = container_of(attr, + struct device_attribute, attr); umode_t mode = attr->mode; - bool show_direction = data->direction_can_change; + struct gpiod_data *data; - if (attr == &dev_attr_direction.attr) { - if (!show_direction) + if (strcmp(attr->name, "direction") == 0) { + data = container_of(dev_attr, struct gpiod_data, dir_attr); + + if (!data->direction_can_change) mode = 0; - } else if (attr == &dev_attr_edge.attr) { - if (gpiod_to_irq(desc) < 0) +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + } else if (strcmp(attr->name, "edge") == 0) { + data = container_of(dev_attr, struct gpiod_data, edge_attr); + + if (gpiod_to_irq(data->desc) < 0) mode = 0; - if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + + if (!data->direction_can_change && + test_bit(FLAG_IS_OUT, &data->desc->flags)) mode = 0; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ } return mode; } -static struct attribute *gpio_attrs[] = { - &dev_attr_direction.attr, - &dev_attr_edge.attr, - &dev_attr_value.attr, - &dev_attr_active_low.attr, - NULL, -}; - -static const struct attribute_group gpio_group = { - .attrs = gpio_attrs, - .is_visible = gpio_is_visible, -}; - -static const struct attribute_group *gpio_groups[] = { - &gpio_group, - NULL -}; - /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) * /label ... matching gpio_chip.label * /ngpio ... matching gpio_chip.ngpio + * + * AND + * + * /sys/class/gpio/chipX/ + * /export ... export GPIO at given offset + * /unexport ... unexport GPIO at given offset + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio */ -static ssize_t base_show(struct device *dev, - struct device_attribute *attr, char *buf) +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +static ssize_t base_show(struct device *dev, struct device_attribute *attr, + char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", gdev->base); + return sysfs_emit(buf, "%u\n", data->gdev->base); } static DEVICE_ATTR_RO(base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ -static ssize_t label_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t label_show(struct device *dev, struct device_attribute *attr, + char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", gdev->label); + return sysfs_emit(buf, "%s\n", data->gdev->label); } static DEVICE_ATTR_RO(label); -static ssize_t ngpio_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, + char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", gdev->ngpio); + return sysfs_emit(buf, "%u\n", data->gdev->ngpio); } static DEVICE_ATTR_RO(ngpio); +static int export_gpio_desc(struct gpio_desc *desc) +{ + int offset, ret; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + offset = gpio_chip_hwgpio(desc); + if (!gpiochip_line_is_valid(guard.gc, offset)) { + pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, + gpio_chip_hwgpio(desc)); + return -EINVAL; + } + + /* + * No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + ret = gpiod_request_user(desc, "sysfs"); + if (ret) + return ret; + + ret = gpiod_set_transitory(desc, false); + if (ret) { + gpiod_free(desc); + return ret; + } + + ret = gpiod_export(desc, true); + if (ret < 0) { + gpiod_free(desc); + } else { + set_bit(FLAG_SYSFS, &desc->flags); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); + } + + return ret; +} + +static int unexport_gpio_desc(struct gpio_desc *desc) +{ + /* + * No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (!test_and_clear_bit(FLAG_SYSFS, &desc->flags)) + return -EINVAL; + + gpiod_unexport(desc); + gpiod_free(desc); + + return 0; +} + +static ssize_t do_chip_export_store(struct device *dev, + struct device_attribute *attr, + const char *buf, ssize_t size, + int (*handler)(struct gpio_desc *desc)) +{ + struct gpiodev_data *data = dev_get_drvdata(dev); + struct gpio_device *gdev = data->gdev; + struct gpio_desc *desc; + unsigned int gpio; + int ret; + + ret = kstrtouint(buf, 0, &gpio); + if (ret) + return ret; + + desc = gpio_device_get_desc(gdev, gpio); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + ret = handler(desc); + if (ret) + return ret; + + return size; +} + +static ssize_t chip_export_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return do_chip_export_store(dev, attr, buf, size, export_gpio_desc); +} + +static struct device_attribute dev_attr_export = __ATTR(export, 0200, NULL, + chip_export_store); + +static ssize_t chip_unexport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return do_chip_export_store(dev, attr, buf, size, unexport_gpio_desc); +} + +static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200, + NULL, + chip_unexport_store); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, @@ -431,7 +583,18 @@ static struct attribute *gpiochip_attrs[] = { NULL, }; ATTRIBUTE_GROUPS(gpiochip); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +static struct attribute *gpiochip_ext_attrs[] = { + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + &dev_attr_export.attr, + &dev_attr_unexport.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpiochip_ext); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) @@ -439,11 +602,11 @@ ATTRIBUTE_GROUPS(gpiochip); * integer N ... number of GPIO to unexport */ static ssize_t export_store(const struct class *class, - const struct class_attribute *attr, - const char *buf, size_t len) + const struct class_attribute *attr, + const char *buf, size_t len) { struct gpio_desc *desc; - int status, offset; + int status; long gpio; status = kstrtol(buf, 0, &gpio); @@ -457,40 +620,7 @@ static ssize_t export_store(const struct class *class, return -EINVAL; } - CLASS(gpio_chip_guard, guard)(desc); - if (!guard.gc) - return -ENODEV; - - offset = gpio_chip_hwgpio(desc); - if (!gpiochip_line_is_valid(guard.gc, offset)) { - pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio); - return -EINVAL; - } - - /* No extra locking here; FLAG_SYSFS just signifies that the - * request and export were done by on behalf of userspace, so - * they may be undone on its behalf too. - */ - - status = gpiod_request_user(desc, "sysfs"); - if (status) - goto done; - - status = gpiod_set_transitory(desc, false); - if (status) { - gpiod_free(desc); - goto done; - } - - status = gpiod_export(desc, true); - if (status < 0) { - gpiod_free(desc); - } else { - set_bit(FLAG_SYSFS, &desc->flags); - gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); - } - -done: + status = export_gpio_desc(desc); if (status) pr_debug("%s: status %d\n", __func__, status); return status ? : len; @@ -498,8 +628,8 @@ static ssize_t export_store(const struct class *class, static CLASS_ATTR_WO(export); static ssize_t unexport_store(const struct class *class, - const struct class_attribute *attr, - const char *buf, size_t len) + const struct class_attribute *attr, + const char *buf, size_t len) { struct gpio_desc *desc; int status; @@ -507,7 +637,7 @@ static ssize_t unexport_store(const struct class *class, status = kstrtol(buf, 0, &gpio); if (status < 0) - goto done; + return status; desc = gpio_to_desc(gpio); /* reject bogus commands (gpiod_unexport() ignores them) */ @@ -516,18 +646,7 @@ static ssize_t unexport_store(const struct class *class, return -EINVAL; } - status = -EINVAL; - - /* No extra locking here; FLAG_SYSFS just signifies that the - * request and export were done by on behalf of userspace, so - * they may be undone on its behalf too. - */ - if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { - gpiod_unexport(desc); - gpiod_free(desc); - status = 0; - } -done: + status = unexport_gpio_desc(desc); if (status) pr_debug("%s: status %d\n", __func__, status); return status ? : len; @@ -540,12 +659,55 @@ static struct attribute *gpio_class_attrs[] = { NULL, }; ATTRIBUTE_GROUPS(gpio_class); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static const struct class gpio_class = { .name = "gpio", +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) .class_groups = gpio_class_groups, +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ }; +static int match_gdev(struct device *dev, const void *desc) +{ + struct gpiodev_data *data = dev_get_drvdata(dev); + const struct gpio_device *gdev = desc; + + return data && data->gdev == gdev; +} + +static struct gpiodev_data * +gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) +{ + /* + * Find the first device in GPIO class that matches. Whether that's + * the one indexed by GPIO base or device ID doesn't matter, it has + * the same address set as driver data. + */ + struct device *cdev __free(put_device) = class_find_device(&gpio_class, + NULL, gdev, + match_gdev); + if (!cdev) + return NULL; + + return dev_get_drvdata(cdev); +}; + +static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = 0644; + dev_attr->show = show; + dev_attr->store = store; +} + /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -564,9 +726,11 @@ static const struct class gpio_class = { */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { + char *path __free(kfree) = NULL; + struct gpiodev_data *gdev_data; + struct gpiod_data *desc_data; struct gpio_device *gdev; - struct gpiod_data *data; - struct device *dev; + struct attribute **attrs; int status; /* can't export until sysfs is available ... */ @@ -591,43 +755,116 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) guard(mutex)(&sysfs_lock); - /* check if chip is being removed */ - if (!gdev->mockdev) { - status = -ENODEV; - goto err_clear_bit; - } - if (!test_bit(FLAG_REQUESTED, &desc->flags)) { gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__); status = -EPERM; goto err_clear_bit; } - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { + desc_data = kzalloc(sizeof(*desc_data), GFP_KERNEL); + if (!desc_data) { status = -ENOMEM; goto err_clear_bit; } - data->desc = desc; - mutex_init(&data->mutex); + desc_data->desc = desc; + mutex_init(&desc_data->mutex); if (guard.gc->direction_input && guard.gc->direction_output) - data->direction_can_change = direction_may_change; + desc_data->direction_can_change = direction_may_change; else - data->direction_can_change = false; + desc_data->direction_can_change = false; - dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), data, gpio_groups, - "gpio%u", desc_to_gpio(desc)); - if (IS_ERR(dev)) { - status = PTR_ERR(dev); + gpiod_attr_init(&desc_data->dir_attr, "direction", + direction_show, direction_store); + gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store); + gpiod_attr_init(&desc_data->active_low_attr, "active_low", + active_low_show, active_low_store); + + attrs = desc_data->class_attrs; + desc_data->class_attr_group.is_visible = gpio_is_visible; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION] = &desc_data->dir_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_VALUE] = &desc_data->val_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_EDGE] = &desc_data->edge_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW] = &desc_data->active_low_attr.attr; + + desc_data->class_attr_group.attrs = desc_data->class_attrs; + desc_data->class_attr_groups[0] = &desc_data->class_attr_group; + + /* + * Note: we need to continue passing desc_data here as there's still + * at least one known user of gpiod_export_link() in the tree. This + * function still uses class_find_device() internally. + */ + desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), desc_data, + desc_data->class_attr_groups, + "gpio%u", + desc_to_gpio(desc)); + if (IS_ERR(desc_data->dev)) { + status = PTR_ERR(desc_data->dev); goto err_free_data; } + desc_data->value_kn = sysfs_get_dirent(desc_data->dev->kobj.sd, + "value"); + if (!desc_data->value_kn) { + status = -ENODEV; + goto err_unregister_device; + } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + + gdev_data = gdev_get_data(gdev); + if (!gdev_data) { + status = -ENODEV; + goto err_put_dirent; + } + + desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u", + gpio_chip_hwgpio(desc)); + if (!desc_data->chip_attr_group.name) { + status = -ENOMEM; + goto err_put_dirent; + } + + attrs = desc_data->chip_attrs; + desc_data->chip_attr_group.is_visible = gpio_is_visible; + attrs[GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION] = &desc_data->dir_attr.attr; + attrs[GPIO_SYSFS_LINE_CHIP_ATTR_VALUE] = &desc_data->val_attr.attr; + + desc_data->chip_attr_group.attrs = attrs; + desc_data->chip_attr_groups[0] = &desc_data->chip_attr_group; + + desc_data->parent = &gdev_data->cdev_id->kobj; + status = sysfs_create_groups(desc_data->parent, + desc_data->chip_attr_groups); + if (status) + goto err_free_name; + + path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc)); + if (!path) { + status = -ENOMEM; + goto err_remove_groups; + } + + list_add(&desc_data->list, &gdev_data->exported_lines); + return 0; +err_remove_groups: + sysfs_remove_groups(desc_data->parent, desc_data->chip_attr_groups); +err_free_name: + kfree(desc_data->chip_attr_group.name); +err_put_dirent: +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + sysfs_put(desc_data->value_kn); +err_unregister_device: + device_unregister(desc_data->dev); err_free_data: - kfree(data); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + kfree(desc_data); err_clear_bit: clear_bit(FLAG_EXPORT, &desc->flags); gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -635,12 +872,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) } EXPORT_SYMBOL_GPL(gpiod_export); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static int match_export(struct device *dev, const void *desc) { struct gpiod_data *data = dev_get_drvdata(dev); - return data->desc == desc; + return gpiod_is_equal(data->desc, desc); } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ /** * gpiod_export_link - create a sysfs link to an exported GPIO node @@ -657,6 +896,7 @@ static int match_export(struct device *dev, const void *desc) int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc) { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct device *cdev; int ret; @@ -673,6 +913,9 @@ int gpiod_export_link(struct device *dev, const char *name, put_device(cdev); return ret; +#else + return -EOPNOTSUPP; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ } EXPORT_SYMBOL_GPL(gpiod_export_link); @@ -684,8 +927,9 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *data; - struct device *dev; + struct gpiod_data *tmp, *desc_data = NULL; + struct gpiodev_data *gdev_data; + struct gpio_device *gdev; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); @@ -696,32 +940,50 @@ void gpiod_unexport(struct gpio_desc *desc) if (!test_bit(FLAG_EXPORT, &desc->flags)) return; - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (!dev) + gdev = gpiod_to_gpio_device(desc); + gdev_data = gdev_get_data(gdev); + if (!gdev_data) return; - data = dev_get_drvdata(dev); + list_for_each_entry(tmp, &gdev_data->exported_lines, list) { + if (gpiod_is_equal(desc, tmp->desc)) { + desc_data = tmp; + break; + } + } + + if (!desc_data) + return; + + list_del(&desc_data->list); clear_bit(FLAG_EXPORT, &desc->flags); - device_unregister(dev); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + sysfs_put(desc_data->value_kn); + device_unregister(desc_data->dev); /* * Release irq after deregistration to prevent race with * edge_store. */ - if (data->irq_flags) - gpio_sysfs_free_irq(dev); + if (desc_data->irq_flags) + gpio_sysfs_free_irq(desc_data); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + + sysfs_remove_groups(desc_data->parent, + desc_data->chip_attr_groups); } - put_device(dev); - kfree(data); + mutex_destroy(&desc_data->mutex); + kfree(desc_data); } EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_sysfs_register(struct gpio_device *gdev) { + struct gpiodev_data *data; struct gpio_chip *chip; struct device *parent; - struct device *dev; + int err; /* * Many systems add gpio chips for SOC support very early, @@ -747,32 +1009,61 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) else parent = &gdev->dev; - /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev, - gpiochip_groups, GPIOCHIP_NAME "%d", - chip->base); - if (IS_ERR(dev)) - return PTR_ERR(dev); + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->gdev = gdev; + INIT_LIST_HEAD(&data->exported_lines); guard(mutex)(&sysfs_lock); - gdev->mockdev = dev; + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + /* use chip->base for the ID; it's already known to be unique */ + data->cdev_base = device_create_with_groups(&gpio_class, parent, + MKDEV(0, 0), data, + gpiochip_groups, + GPIOCHIP_NAME "%d", + chip->base); + if (IS_ERR(data->cdev_base)) { + err = PTR_ERR(data->cdev_base); + kfree(data); + return err; + } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + + data->cdev_id = device_create_with_groups(&gpio_class, parent, + MKDEV(0, 0), data, + gpiochip_ext_groups, + "chip%d", gdev->id); + if (IS_ERR(data->cdev_id)) { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + err = PTR_ERR(data->cdev_id); + kfree(data); + return err; + } return 0; } void gpiochip_sysfs_unregister(struct gpio_device *gdev) { + struct gpiodev_data *data; struct gpio_desc *desc; struct gpio_chip *chip; scoped_guard(mutex, &sysfs_lock) { - if (!gdev->mockdev) + data = gdev_get_data(gdev); + if (!data) return; - device_unregister(gdev->mockdev); - - /* prevent further gpiod exports */ - gdev->mockdev = NULL; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + device_unregister(data->cdev_id); + kfree(data); } guard(srcu)(&gdev->srcu); @@ -798,9 +1089,6 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) struct gpio_device *gdev = gc->gpiodev; int ret; - if (gdev->mockdev) - return 0; - ret = gpiochip_sysfs_register(gdev); if (ret) chip_err(gc, "failed to register the sysfs entry: %d\n", ret); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fdafa0df1b43..0d2b470a252e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -74,6 +74,19 @@ static const struct bus_type gpio_bus_type = { .match = gpio_bus_match, }; +/* + * At the end we want all GPIOs to be dynamically allocated from 0. + * However, some legacy drivers still perform fixed allocation. + * Until they are all fixed, leave 0-512 space for them. + */ +#define GPIO_DYNAMIC_BASE 512 +/* + * Define the maximum of the possible GPIO in the global numberspace. + * While the GPIO base and numbers are positive, we limit it with signed + * maximum as a lot of code is using negative values for special cases. + */ +#define GPIO_DYNAMIC_MAX INT_MAX + /* * Number of GPIOs to use for the fast path in set array */ @@ -265,20 +278,6 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); -/** - * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. - * @desc: Descriptor to compare. - * @other: The second descriptor to compare against. - * - * Returns: - * True if the descriptors refer to the same physical pin. False otherwise. - */ -bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) -{ - return desc == other; -} -EXPORT_SYMBOL_GPL(gpiod_is_equal); - /** * gpio_device_get_base() - Get the base GPIO number allocated by this device * @gdev: GPIO device @@ -387,6 +386,21 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) return; \ } while (0) +/** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) +{ + return validate_desc(desc, __func__) > 0 && + !IS_ERR_OR_NULL(other) && desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); + static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) { int ret; @@ -1023,11 +1037,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret; - /* Only allow one set() and one set_multiple(). */ - if ((gc->set && gc->set_rv) || - (gc->set_multiple && gc->set_multiple_rv)) - return -EINVAL; - /* * First: allocate and populate the internal stat container, and * set up the struct device. @@ -2877,19 +2886,14 @@ static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value) lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set && !gc->set_rv))) + if (WARN_ON(unlikely(!gc->set))) return -EOPNOTSUPP; - if (gc->set_rv) { - ret = gc->set_rv(gc, offset, value); - if (ret > 0) - ret = -EBADE; + ret = gc->set(gc, offset, value); + if (ret > 0) + ret = -EBADE; - return ret; - } - - gc->set(gc, offset, value); - return 0; + return ret; } static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) @@ -2905,7 +2909,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) * output-only, but if there is then not even a .set() operation it * is pretty tricky to drive the output line. */ - if (!guard.gc->set && !guard.gc->set_rv && !guard.gc->direction_output) { + if (!guard.gc->set && !guard.gc->direction_output) { gpiod_warn(desc, "%s: missing set() and direction_output() operations\n", __func__); @@ -3297,14 +3301,15 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { - int ret; - lockdep_assert_held(&gc->gpiodev->srcu); if (gc->get_multiple) { + int ret; + ret = gc->get_multiple(gc, mask, bits); if (ret > 0) return -EBADE; + return ret; } if (gc->get) { @@ -3650,19 +3655,14 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, lockdep_assert_held(&gc->gpiodev->srcu); - if (gc->set_multiple_rv) { - ret = gc->set_multiple_rv(gc, mask, bits); + if (gc->set_multiple) { + ret = gc->set_multiple(gc, mask, bits); if (ret > 0) ret = -EBADE; return ret; } - if (gc->set_multiple) { - gc->set_multiple(gc, mask, bits); - return 0; - } - /* set outputs if the corresponding mask bit is set */ for_each_set_bit(i, mask, gc->ngpio) { ret = gpiochip_set(gc, i, test_bit(i, bits)); @@ -5220,8 +5220,8 @@ core_initcall(gpiolib_dev_init); static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { bool active_low, is_irq, is_out; - unsigned int gpio = gdev->base; struct gpio_desc *desc; + unsigned int gpio = 0; struct gpio_chip *gc; unsigned long flags; int value; @@ -5325,8 +5325,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) return 0; } - seq_printf(s, "%s: GPIOs %u-%u", dev_name(&gdev->dev), gdev->base, - gdev->base + gdev->ngpio - 1); + seq_printf(s, "%s: %u GPIOs", dev_name(&gdev->dev), gdev->ngpio); parent = gc->parent; if (parent) seq_printf(s, ", parent: %s/%s", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 58f64056de77..9b74738a9ca5 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -27,8 +27,6 @@ * @dev: the GPIO device struct * @chrdev: character device for the GPIO device * @id: numerical ID number for the GPIO chip - * @mockdev: class device used by the deprecated sysfs interface (may be - * NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @chip: pointer to the corresponding gpiochip, holding static * data for this device @@ -65,7 +63,6 @@ struct gpio_device { struct device dev; struct cdev chrdev; int id; - struct device *mockdev; struct module *owner; struct gpio_chip __rcu *chip; struct gpio_desc *descs; diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 930de203d533..2d0fea87af79 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -84,7 +84,8 @@ amdgpu-y += \ vega20_reg_init.o nbio_v7_4.o nbio_v2_3.o nv.o arct_reg_init.o mxgpu_nv.o \ nbio_v7_2.o hdp_v4_0.o hdp_v5_0.o aldebaran_reg_init.o aldebaran.o soc21.o soc24.o \ sienna_cichlid.o smu_v13_0_10.o nbio_v4_3.o hdp_v6_0.o nbio_v7_7.o hdp_v5_2.o lsdma_v6_0.o \ - nbio_v7_9.o aqua_vanjaram.o nbio_v7_11.o lsdma_v7_0.o hdp_v7_0.o nbif_v6_3_1.o + nbio_v7_9.o aqua_vanjaram.o nbio_v7_11.o lsdma_v7_0.o hdp_v7_0.o nbif_v6_3_1.o \ + cyan_skillfish_reg_init.o # add DF block amdgpu-y += \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index ef3af170dda4..17848ce65d1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -819,6 +819,20 @@ struct amdgpu_ip_map_info { uint32_t mask); }; +enum amdgpu_uid_type { + AMDGPU_UID_TYPE_XCD, + AMDGPU_UID_TYPE_AID, + AMDGPU_UID_TYPE_SOC, + AMDGPU_UID_TYPE_MAX +}; + +#define AMDGPU_UID_INST_MAX 8 /* max number of instances for each UID type */ + +struct amdgpu_uid { + uint64_t uid[AMDGPU_UID_TYPE_MAX][AMDGPU_UID_INST_MAX]; + struct amdgpu_device *adev; +}; + struct amd_powerplay { void *pp_handle; const struct amd_pm_funcs *pp_funcs; @@ -896,6 +910,9 @@ struct amdgpu_pcie_reset_ctx { bool in_link_reset; bool occurs_dpc; bool audio_suspended; + struct pci_dev *swus; + struct pci_saved_state *swus_pcistate; + struct pci_saved_state *swds_pcistate; }; /* @@ -929,12 +946,6 @@ enum amdgpu_enforce_isolation_mode { AMDGPU_ENFORCE_ISOLATION_NO_CLEANER_SHADER = 3, }; - -/* - * Non-zero (true) if the GPU has VRAM. Zero (false) otherwise. - */ -#define AMDGPU_HAS_VRAM(_adev) ((_adev)->gmc.real_vram_size) - struct amdgpu_device { struct device *dev; struct pci_dev *pdev; @@ -1138,9 +1149,6 @@ struct amdgpu_device { /* for userq and VM fences */ struct amdgpu_seq64 seq64; - /* KFD */ - struct amdgpu_kfd_dev kfd; - /* UMC */ struct amdgpu_umc umc; @@ -1302,6 +1310,12 @@ struct amdgpu_device { struct list_head userq_mgr_list; struct mutex userq_mutex; bool userq_halt_for_enforce_isolation; + struct amdgpu_uid *uid_info; + + /* KFD + * Must be last --ends in a flexible-array member. + */ + struct amdgpu_kfd_dev kfd; }; static inline uint32_t amdgpu_ip_version(const struct amdgpu_device *adev, @@ -1785,4 +1799,9 @@ static inline int amdgpu_device_bus_status_check(struct amdgpu_device *adev) return 0; } +void amdgpu_device_set_uid(struct amdgpu_uid *uid_info, + enum amdgpu_uid_type type, uint8_t inst, + uint64_t uid); +uint64_t amdgpu_device_get_uid(struct amdgpu_uid *uid_info, + enum amdgpu_uid_type type, uint8_t inst); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c index cbc40cad581b..9b3180449150 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c @@ -76,6 +76,7 @@ static void aca_banks_release(struct aca_banks *banks) list_for_each_entry_safe(node, tmp, &banks->list, node) { list_del(&node->node); kvfree(node); + banks->nr_banks--; } } @@ -130,6 +131,27 @@ static void aca_smu_bank_dump(struct amdgpu_device *adev, int idx, int total, st RAS_EVENT_LOG(adev, event_id, HW_ERR "hardware error logged by the scrubber\n"); } +static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type) +{ + + struct aca_hwip *hwip; + int hwid, mcatype; + u64 ipid; + + if (!bank || type == ACA_HWIP_TYPE_UNKNOW) + return false; + + hwip = &aca_hwid_mcatypes[type]; + if (!hwip->hwid) + return false; + + ipid = bank->regs[ACA_REG_IDX_IPID]; + hwid = ACA_REG__IPID__HARDWAREID(ipid); + mcatype = ACA_REG__IPID__MCATYPE(ipid); + + return hwip->hwid == hwid && hwip->mcatype == mcatype; +} + static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_type type, int start, int count, struct aca_banks *banks, struct ras_query_context *qctx) @@ -168,6 +190,15 @@ static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_ bank.smu_err_type = type; + /* + * Poison being consumed when injecting a UE while running background workloads, + * which are unexpected. + */ + if (type == ACA_SMU_TYPE_UE && + ACA_REG__STATUS__POISON(bank.regs[ACA_REG_IDX_STATUS]) && + !aca_bank_hwip_is_matched(&bank, ACA_HWIP_TYPE_UMC)) + continue; + aca_smu_bank_dump(adev, i, count, &bank, qctx); ret = aca_banks_add_bank(banks, &bank); @@ -178,27 +209,6 @@ static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_ return 0; } -static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type) -{ - - struct aca_hwip *hwip; - int hwid, mcatype; - u64 ipid; - - if (!bank || type == ACA_HWIP_TYPE_UNKNOW) - return false; - - hwip = &aca_hwid_mcatypes[type]; - if (!hwip->hwid) - return false; - - ipid = bank->regs[ACA_REG_IDX_IPID]; - hwid = ACA_REG__IPID__HARDWAREID(ipid); - mcatype = ACA_REG__IPID__MCATYPE(ipid); - - return hwip->hwid == hwid && hwip->mcatype == mcatype; -} - static bool aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type) { const struct aca_bank_ops *bank_ops = handle->bank_ops; @@ -229,6 +239,7 @@ static struct aca_bank_error *new_bank_error(struct aca_error *aerr, struct aca_ mutex_lock(&aerr->lock); list_add_tail(&bank_error->node, &aerr->list); + aerr->nr_errors++; mutex_unlock(&aerr->lock); return bank_error; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 33eb4826b58b..127927b16ee2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -107,11 +107,13 @@ struct amdgpu_kfd_dev { bool init_complete; struct work_struct reset_work; - /* HMM page migration MEMORY_DEVICE_PRIVATE mapping */ - struct dev_pagemap pgmap; - /* Client for KFD BO GEM handle allocations */ struct drm_client_dev client; + + /* HMM page migration MEMORY_DEVICE_PRIVATE mapping + * Must be last --ends in a flexible-array member. + */ + struct dev_pagemap pgmap; }; enum kgd_engine_type { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 260165bbe373..c3b34a410375 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -213,19 +213,35 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev, spin_lock(&kfd_mem_limit.mem_limit_lock); if (kfd_mem_limit.system_mem_used + system_mem_needed > - kfd_mem_limit.max_system_mem_limit) + kfd_mem_limit.max_system_mem_limit) { pr_debug("Set no_system_mem_limit=1 if using shared memory\n"); + if (!no_system_mem_limit) { + ret = -ENOMEM; + goto release; + } + } - if ((kfd_mem_limit.system_mem_used + system_mem_needed > - kfd_mem_limit.max_system_mem_limit && !no_system_mem_limit) || - (kfd_mem_limit.ttm_mem_used + ttm_mem_needed > - kfd_mem_limit.max_ttm_mem_limit) || - (adev && xcp_id >= 0 && adev->kfd.vram_used[xcp_id] + vram_needed > - vram_size - reserved_for_pt - reserved_for_ras - atomic64_read(&adev->vram_pin_size))) { + if (kfd_mem_limit.ttm_mem_used + ttm_mem_needed > + kfd_mem_limit.max_ttm_mem_limit) { ret = -ENOMEM; goto release; } + /*if is_app_apu is false and apu_prefer_gtt is true, it is an APU with + * carve out < gtt. In that case, VRAM allocation will go to gtt domain, skip + * VRAM check since ttm_mem_limit check already cover this allocation + */ + + if (adev && xcp_id >= 0 && (!adev->apu_prefer_gtt || adev->gmc.is_app_apu)) { + uint64_t vram_available = + vram_size - reserved_for_pt - reserved_for_ras - + atomic64_read(&adev->vram_pin_size); + if (adev->kfd.vram_used[xcp_id] + vram_needed > vram_available) { + ret = -ENOMEM; + goto release; + } + } + /* Update memory accounting by decreasing available system * memory, TTM memory and GPU memory as computed above */ @@ -494,7 +510,8 @@ static int vm_update_pds(struct amdgpu_vm *vm, struct amdgpu_sync *sync) return amdgpu_sync_fence(sync, vm->last_update, GFP_KERNEL); } -static uint64_t get_pte_flags(struct amdgpu_device *adev, struct kgd_mem *mem) +static uint64_t get_pte_flags(struct amdgpu_device *adev, struct amdgpu_vm *vm, + struct kgd_mem *mem) { uint32_t mapping_flags = AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_MTYPE_DEFAULT; @@ -504,7 +521,7 @@ static uint64_t get_pte_flags(struct amdgpu_device *adev, struct kgd_mem *mem) if (mem->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE) mapping_flags |= AMDGPU_VM_PAGE_EXECUTABLE; - return amdgpu_gem_va_map_flags(adev, mapping_flags); + return mapping_flags; } /** @@ -961,7 +978,7 @@ static int kfd_mem_attach(struct amdgpu_device *adev, struct kgd_mem *mem, goto unwind; } attachment[i]->va = va; - attachment[i]->pte_flags = get_pte_flags(adev, mem); + attachment[i]->pte_flags = get_pte_flags(adev, vm, mem); attachment[i]->adev = adev; list_add(&attachment[i]->list, &mem->attachments); @@ -1626,11 +1643,15 @@ size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev, uint64_t vram_available, system_mem_available, ttm_mem_available; spin_lock(&kfd_mem_limit.mem_limit_lock); - vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id) - - adev->kfd.vram_used_aligned[xcp_id] - - atomic64_read(&adev->vram_pin_size) - - reserved_for_pt - - reserved_for_ras; + if (adev->apu_prefer_gtt && !adev->gmc.is_app_apu) + vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id) + - adev->kfd.vram_used_aligned[xcp_id]; + else + vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id) + - adev->kfd.vram_used_aligned[xcp_id] + - atomic64_read(&adev->vram_pin_size) + - reserved_for_pt + - reserved_for_ras; if (adev->apu_prefer_gtt) { system_mem_available = no_system_mem_limit ? @@ -2969,9 +2990,22 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * struct amdgpu_device *adev = amdgpu_ttm_adev( peer_vm->root.bo->tbo.bdev); + struct amdgpu_fpriv *fpriv = + container_of(peer_vm, struct amdgpu_fpriv, vm); + + ret = amdgpu_vm_bo_update(adev, fpriv->prt_va, false); + if (ret) { + dev_dbg(adev->dev, + "Memory eviction: handle PRT moved failed, pid %8d. Try again.\n", + pid_nr(process_info->pid)); + goto validate_map_fail; + } + ret = amdgpu_vm_handle_moved(adev, peer_vm, &exec.ticket); if (ret) { - pr_debug("Memory eviction: handle moved failed. Try again\n"); + dev_dbg(adev->dev, + "Memory eviction: handle moved failed, pid %8d. Try again.\n", + pid_nr(process_info->pid)); goto validate_map_fail; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index e476e45b996a..9dfdc08cc887 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -1816,16 +1816,43 @@ static ssize_t amdgpu_atombios_get_vbios_version(struct device *dev, return sysfs_emit(buf, "%s\n", ctx->vbios_pn); } +static ssize_t amdgpu_atombios_get_vbios_build(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + struct atom_context *ctx = adev->mode_info.atom_context; + + return sysfs_emit(buf, "%s\n", ctx->build_num); +} + static DEVICE_ATTR(vbios_version, 0444, amdgpu_atombios_get_vbios_version, NULL); +static DEVICE_ATTR(vbios_build, 0444, amdgpu_atombios_get_vbios_build, NULL); static struct attribute *amdgpu_vbios_version_attrs[] = { - &dev_attr_vbios_version.attr, - NULL + &dev_attr_vbios_version.attr, &dev_attr_vbios_build.attr, NULL }; +static umode_t amdgpu_vbios_version_attrs_is_visible(struct kobject *kobj, + struct attribute *attr, + int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + struct atom_context *ctx = adev->mode_info.atom_context; + + if (attr == &dev_attr_vbios_build.attr && !strlen(ctx->build_num)) + return 0; + + return attr->mode; +} + const struct attribute_group amdgpu_vbios_version_attr_group = { - .attrs = amdgpu_vbios_version_attrs + .attrs = amdgpu_vbios_version_attrs, + .is_visible = amdgpu_vbios_version_attrs_is_visible, }; int amdgpu_atombios_sysfs_init(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index 5e375e9c4f5d..bf38fc69c1cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -1195,29 +1195,69 @@ static void amdgpu_connector_dvi_force(struct drm_connector *connector) amdgpu_connector->use_digital = true; } +/** + * amdgpu_max_hdmi_pixel_clock - Return max supported HDMI (TMDS) pixel clock + * @adev: pointer to amdgpu_device + * + * Return: maximum supported HDMI (TMDS) pixel clock in KHz. + */ +static int amdgpu_max_hdmi_pixel_clock(const struct amdgpu_device *adev) +{ + if (adev->asic_type >= CHIP_POLARIS10) + return 600000; + else if (adev->asic_type >= CHIP_TONGA) + return 300000; + else + return 297000; +} + +/** + * amdgpu_connector_dvi_mode_valid - Validate a mode on DVI/HDMI connectors + * @connector: DRM connector to validate the mode on + * @mode: display mode to validate + * + * Validate the given display mode on DVI and HDMI connectors, including + * analog signals on DVI-I. + * + * Return: drm_mode_status indicating whether the mode is valid. + */ static enum drm_mode_status amdgpu_connector_dvi_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode) { struct drm_device *dev = connector->dev; struct amdgpu_device *adev = drm_to_adev(dev); struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); + const int max_hdmi_pixel_clock = amdgpu_max_hdmi_pixel_clock(adev); + const int max_dvi_single_link_pixel_clock = 165000; + int max_digital_pixel_clock_khz; /* XXX check mode bandwidth */ - if (amdgpu_connector->use_digital && (mode->clock > 165000)) { - if ((amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) || - (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || - (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) { - return MODE_OK; - } else if (connector->display_info.is_hdmi) { - /* HDMI 1.3+ supports max clock of 340 Mhz */ - if (mode->clock > 340000) - return MODE_CLOCK_HIGH; - else - return MODE_OK; - } else { - return MODE_CLOCK_HIGH; + if (amdgpu_connector->use_digital) { + switch (amdgpu_connector->connector_object_id) { + case CONNECTOR_OBJECT_ID_HDMI_TYPE_A: + max_digital_pixel_clock_khz = max_hdmi_pixel_clock; + break; + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I: + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D: + max_digital_pixel_clock_khz = max_dvi_single_link_pixel_clock; + break; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I: + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D: + case CONNECTOR_OBJECT_ID_HDMI_TYPE_B: + max_digital_pixel_clock_khz = max_dvi_single_link_pixel_clock * 2; + break; } + + /* When the display EDID claims that it's an HDMI display, + * we use the HDMI encoder mode of the display HW, + * so we should verify against the max HDMI clock here. + */ + if (connector->display_info.is_hdmi) + max_digital_pixel_clock_khz = max_hdmi_pixel_clock; + + if (mode->clock > max_digital_pixel_clock_khz) + return MODE_CLOCK_HIGH; } /* check against the max pixel clock */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index 25252231a68a..ef996493115f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -68,7 +68,6 @@ void amdgpu_cper_entry_fill_hdr(struct amdgpu_device *adev, hdr->error_severity = sev; hdr->valid_bits.platform_id = 1; - hdr->valid_bits.partition_id = 1; hdr->valid_bits.timestamp = 1; amdgpu_cper_get_timestamp(&hdr->timestamp); @@ -174,7 +173,7 @@ int amdgpu_cper_entry_fill_runtime_section(struct amdgpu_device *adev, struct cper_sec_nonstd_err *section; bool poison; - poison = (sev == CPER_SEV_NON_FATAL_CORRECTED) ? false : true; + poison = sev != CPER_SEV_NON_FATAL_CORRECTED; section_desc = (struct cper_sec_desc *)((uint8_t *)hdr + SEC_DESC_OFFSET(idx)); section = (struct cper_sec_nonstd_err *)((uint8_t *)hdr + NONSTD_SEC_OFFSET(hdr->sec_cnt, idx)); @@ -206,6 +205,7 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev { struct cper_sec_desc *section_desc; struct cper_sec_nonstd_err *section; + uint32_t socket_id; section_desc = (struct cper_sec_desc *)((uint8_t *)hdr + SEC_DESC_OFFSET(idx)); section = (struct cper_sec_nonstd_err *)((uint8_t *)hdr + @@ -219,11 +219,17 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev section->hdr.valid_bits.err_context_cnt = 1; section->info.error_type = RUNTIME; + section->info.valid_bits.ms_chk = 1; section->info.ms_chk_bits.err_type_valid = 1; + section->info.ms_chk_bits.err_type = 1; + section->info.ms_chk_bits.pcc = 1; section->ctx.reg_ctx_type = CPER_CTX_TYPE_CRASH; section->ctx.reg_arr_size = sizeof(section->ctx.reg_dump); /* Hardcoded Reg dump for bad page threshold CPER */ + socket_id = (adev->smuio.funcs && adev->smuio.funcs->get_socket_id) ? + adev->smuio.funcs->get_socket_id(adev) : + 0; section->ctx.reg_dump[CPER_ACA_REG_CTL_LO] = 0x1; section->ctx.reg_dump[CPER_ACA_REG_CTL_HI] = 0x0; section->ctx.reg_dump[CPER_ACA_REG_STATUS_LO] = 0x137; @@ -234,8 +240,8 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev section->ctx.reg_dump[CPER_ACA_REG_MISC0_HI] = 0x0; section->ctx.reg_dump[CPER_ACA_REG_CONFIG_LO] = 0x2; section->ctx.reg_dump[CPER_ACA_REG_CONFIG_HI] = 0x1ff; - section->ctx.reg_dump[CPER_ACA_REG_IPID_LO] = 0x0; - section->ctx.reg_dump[CPER_ACA_REG_IPID_HI] = 0x96; + section->ctx.reg_dump[CPER_ACA_REG_IPID_LO] = (socket_id / 4) & 0x01; + section->ctx.reg_dump[CPER_ACA_REG_IPID_HI] = 0x096 | (((socket_id % 4) & 0x3) << 12); section->ctx.reg_dump[CPER_ACA_REG_SYND_LO] = 0x0; section->ctx.reg_dump[CPER_ACA_REG_SYND_HI] = 0x0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index a2adaacf6adb..2ac9729e4c86 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -396,7 +396,7 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, chunk_ib->ib_bytes : 0, AMDGPU_IB_POOL_DELAYED, ib); if (r) { - DRM_ERROR("Failed to get ib !\n"); + drm_err(adev_to_drm(p->adev), "Failed to get ib !\n"); return r; } @@ -468,7 +468,7 @@ static int amdgpu_syncobj_lookup_and_add(struct amdgpu_cs_parser *p, r = drm_syncobj_find_fence(p->filp, handle, point, flags, &fence); if (r) { - DRM_ERROR("syncobj %u failed to find fence @ %llu (%d)!\n", + drm_err(adev_to_drm(p->adev), "syncobj %u failed to find fence @ %llu (%d)!\n", handle, point, r); return r; } @@ -902,7 +902,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, sizeof(struct page *), GFP_KERNEL); if (!e->user_pages) { - DRM_ERROR("kvmalloc_array failure\n"); + drm_err(adev_to_drm(p->adev), "kvmalloc_array failure\n"); r = -ENOMEM; goto out_free_user_pages; } @@ -983,7 +983,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, r = amdgpu_vm_validate(p->adev, &fpriv->vm, NULL, amdgpu_cs_bo_validate, p); if (r) { - DRM_ERROR("amdgpu_vm_validate() failed.\n"); + drm_err(adev_to_drm(p->adev), "amdgpu_vm_validate() failed.\n"); goto out_free_user_pages; } @@ -1061,13 +1061,13 @@ static int amdgpu_cs_patch_ibs(struct amdgpu_cs_parser *p, va_start = ib->gpu_addr & AMDGPU_GMC_HOLE_MASK; r = amdgpu_cs_find_mapping(p, va_start, &aobj, &m); if (r) { - DRM_ERROR("IB va_start is invalid\n"); + drm_err(adev_to_drm(p->adev), "IB va_start is invalid\n"); return r; } if ((va_start + ib->length_dw * 4) > (m->last + 1) * AMDGPU_GPU_PAGE_SIZE) { - DRM_ERROR("IB va_start+ib_bytes is invalid\n"); + drm_err(adev_to_drm(p->adev), "IB va_start+ib_bytes is invalid\n"); return -EINVAL; } @@ -1139,6 +1139,9 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) } } + if (!amdgpu_vm_ready(vm)) + return -EINVAL; + r = amdgpu_vm_clear_freed(adev, vm, NULL); if (r) return r; @@ -1235,7 +1238,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entities[p->gang_leader_idx]); if (r) { if (r != -ERESTARTSYS) - DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n"); + drm_err(adev_to_drm(p->adev), "amdgpu_ctx_wait_prev_fence failed.\n"); return r; } @@ -1448,7 +1451,7 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = amdgpu_cs_parser_init(&parser, adev, filp, data); if (r) { - DRM_ERROR_RATELIMITED("Failed to initialize parser %d!\n", r); + drm_err_ratelimited(dev, "Failed to initialize parser %d!\n", r); return r; } @@ -1463,9 +1466,9 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = amdgpu_cs_parser_bos(&parser, data); if (r) { if (r == -ENOMEM) - DRM_ERROR("Not enough memory for command submission!\n"); + drm_err(dev, "Not enough memory for command submission!\n"); else if (r != -ERESTARTSYS && r != -EAGAIN) - DRM_DEBUG("Failed to process the buffer list %d!\n", r); + drm_dbg(dev, "Failed to process the buffer list %d!\n", r); goto error_fini; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 0e6e2e2acf5b..a70651050acf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -2136,12 +2136,14 @@ static int amdgpu_pt_info_read(struct seq_file *m, void *unused) struct drm_file *file; struct amdgpu_fpriv *fpriv; struct amdgpu_bo *root_bo; + struct amdgpu_device *adev; int r; file = m->private; if (!file) return -EINVAL; + adev = drm_to_adev(file->minor->dev); fpriv = file->driver_priv; if (!fpriv || !fpriv->vm.root.bo) return -ENODEV; @@ -2153,7 +2155,11 @@ static int amdgpu_pt_info_read(struct seq_file *m, void *unused) return -EINVAL; } - seq_printf(m, "gpu_address: 0x%llx\n", amdgpu_bo_gpu_offset(fpriv->vm.root.bo)); + seq_printf(m, "pd_address: 0x%llx\n", amdgpu_gmc_pd_addr(fpriv->vm.root.bo)); + seq_printf(m, "max_pfn: 0x%llx\n", adev->vm_manager.max_pfn); + seq_printf(m, "num_level: 0x%x\n", adev->vm_manager.num_level); + seq_printf(m, "block_size: 0x%x\n", adev->vm_manager.block_size); + seq_printf(m, "fragment_size: 0x%x\n", adev->vm_manager.fragment_size); amdgpu_bo_unreserve(root_bo); amdgpu_bo_unref(&root_bo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index a43ce3404849..bdfb80377e6a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -95,6 +95,7 @@ MODULE_FIRMWARE("amdgpu/picasso_gpu_info.bin"); MODULE_FIRMWARE("amdgpu/raven2_gpu_info.bin"); MODULE_FIRMWARE("amdgpu/arcturus_gpu_info.bin"); MODULE_FIRMWARE("amdgpu/navi12_gpu_info.bin"); +MODULE_FIRMWARE("amdgpu/cyan_skillfish_gpu_info.bin"); #define AMDGPU_RESUME_MS 2000 #define AMDGPU_MAX_RETRY_LIMIT 2 @@ -178,6 +179,8 @@ struct amdgpu_init_level amdgpu_init_minimal_xgmi = { BIT(AMD_IP_BLOCK_TYPE_PSP) }; +static void amdgpu_device_load_switch_state(struct amdgpu_device *adev); + static inline bool amdgpu_ip_member_of_hwini(struct amdgpu_device *adev, enum amd_ip_block_type block) { @@ -2445,6 +2448,33 @@ int amdgpu_device_ip_block_version_cmp(struct amdgpu_device *adev, return 1; } +static const char *ip_block_names[] = { + [AMD_IP_BLOCK_TYPE_COMMON] = "common", + [AMD_IP_BLOCK_TYPE_GMC] = "gmc", + [AMD_IP_BLOCK_TYPE_IH] = "ih", + [AMD_IP_BLOCK_TYPE_SMC] = "smu", + [AMD_IP_BLOCK_TYPE_PSP] = "psp", + [AMD_IP_BLOCK_TYPE_DCE] = "dce", + [AMD_IP_BLOCK_TYPE_GFX] = "gfx", + [AMD_IP_BLOCK_TYPE_SDMA] = "sdma", + [AMD_IP_BLOCK_TYPE_UVD] = "uvd", + [AMD_IP_BLOCK_TYPE_VCE] = "vce", + [AMD_IP_BLOCK_TYPE_ACP] = "acp", + [AMD_IP_BLOCK_TYPE_VCN] = "vcn", + [AMD_IP_BLOCK_TYPE_MES] = "mes", + [AMD_IP_BLOCK_TYPE_JPEG] = "jpeg", + [AMD_IP_BLOCK_TYPE_VPE] = "vpe", + [AMD_IP_BLOCK_TYPE_UMSCH_MM] = "umsch_mm", + [AMD_IP_BLOCK_TYPE_ISP] = "isp", +}; + +static const char *ip_block_name(struct amdgpu_device *adev, enum amd_ip_block_type type) +{ + int idx = (int)type; + + return idx < ARRAY_SIZE(ip_block_names) ? ip_block_names[idx] : "unknown"; +} + /** * amdgpu_device_ip_block_add * @@ -2473,8 +2503,13 @@ int amdgpu_device_ip_block_add(struct amdgpu_device *adev, break; } - dev_info(adev->dev, "detected ip block number %d <%s>\n", - adev->num_ip_blocks, ip_block_version->funcs->name); + dev_info(adev->dev, "detected ip block number %d <%s_v%d_%d_%d> (%s)\n", + adev->num_ip_blocks, + ip_block_name(adev, ip_block_version->type), + ip_block_version->major, + ip_block_version->minor, + ip_block_version->rev, + ip_block_version->funcs->name); adev->ip_blocks[adev->num_ip_blocks].adev = adev; @@ -2570,9 +2605,6 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev) adev->firmware.gpu_info_fw = NULL; - if (adev->mman.discovery_bin) - return 0; - switch (adev->asic_type) { default: return 0; @@ -2594,8 +2626,13 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev) chip_name = "arcturus"; break; case CHIP_NAVI12: + if (adev->mman.discovery_bin) + return 0; chip_name = "navi12"; break; + case CHIP_CYAN_SKILLFISH: + chip_name = "cyan_skillfish"; + break; } err = amdgpu_ucode_request(adev, &adev->firmware.gpu_info_fw, @@ -2675,6 +2712,24 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev) return err; } +static void amdgpu_uid_init(struct amdgpu_device *adev) +{ + /* Initialize the UID for the device */ + adev->uid_info = kzalloc(sizeof(struct amdgpu_uid), GFP_KERNEL); + if (!adev->uid_info) { + dev_warn(adev->dev, "Failed to allocate memory for UID\n"); + return; + } + adev->uid_info->adev = adev; +} + +static void amdgpu_uid_fini(struct amdgpu_device *adev) +{ + /* Free the UID memory */ + kfree(adev->uid_info); + adev->uid_info = NULL; +} + /** * amdgpu_device_ip_early_init - run early init for hardware IPs * @@ -2858,6 +2913,8 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) if (adev->gmc.xgmi.supported) amdgpu_xgmi_early_init(adev); + if (amdgpu_is_multi_aid(adev)) + amdgpu_uid_init(adev); ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GFX); if (ip_block->status.valid != false) amdgpu_amdkfd_device_probe(adev); @@ -3271,6 +3328,7 @@ static bool amdgpu_device_check_vram_lost(struct amdgpu_device *adev) * always assumed to be lost. */ switch (amdgpu_asic_reset_method(adev)) { + case AMD_RESET_METHOD_LEGACY: case AMD_RESET_METHOD_LINK: case AMD_RESET_METHOD_BACO: case AMD_RESET_METHOD_MODE1: @@ -3389,7 +3447,7 @@ static int amdgpu_device_enable_mgpu_fan_boost(void) for (i = 0; i < mgpu_info.num_dgpu; i++) { gpu_ins = &(mgpu_info.gpu_ins[i]); adev = gpu_ins->adev; - if (!(adev->flags & AMD_IS_APU) && + if (!(adev->flags & AMD_IS_APU || amdgpu_sriov_multi_vf_mode(adev)) && !gpu_ins->mgpu_fan_enabled) { ret = amdgpu_dpm_enable_mgpu_fan_boost(adev); if (ret) @@ -3648,6 +3706,7 @@ static int amdgpu_device_ip_fini(struct amdgpu_device *adev) } amdgpu_ras_fini(adev); + amdgpu_uid_fini(adev); return 0; } @@ -4992,7 +5051,8 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) adev->reset_domain = NULL; kfree(adev->pci_state); - + kfree(adev->pcie_reset_ctx.swds_pcistate); + kfree(adev->pcie_reset_ctx.swus_pcistate); } /** @@ -5311,6 +5371,8 @@ int amdgpu_device_resume(struct drm_device *dev, bool notify_clients) dev->dev->power.disable_depth--; #endif } + + amdgpu_vram_mgr_clear_reset_blocks(adev); adev->in_suspend = false; if (amdgpu_acpi_smart_shift_update(adev, AMDGPU_SS_DEV_D0)) @@ -5699,7 +5761,7 @@ int amdgpu_device_link_reset(struct amdgpu_device *adev) dev_info(adev->dev, "GPU link reset\n"); - if (!adev->pcie_reset_ctx.occurs_dpc) + if (!amdgpu_reset_in_dpc(adev)) ret = amdgpu_dpm_link_reset(adev); if (ret) @@ -5828,6 +5890,7 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) amdgpu_set_init_level(tmp_adev, init_level); if (full_reset) { /* post card */ + amdgpu_reset_set_dpc_status(tmp_adev, false); amdgpu_ras_clear_err_state(tmp_adev); r = amdgpu_device_asic_init(tmp_adev); if (r) { @@ -6134,12 +6197,11 @@ static int amdgpu_device_health_check(struct list_head *device_list_handle) return ret; } -static int amdgpu_device_recovery_prepare(struct amdgpu_device *adev, +static void amdgpu_device_recovery_prepare(struct amdgpu_device *adev, struct list_head *device_list, struct amdgpu_hive_info *hive) { struct amdgpu_device *tmp_adev = NULL; - int r; /* * Build list of devices to reset. @@ -6151,7 +6213,7 @@ static int 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 (adev->pcie_reset_ctx.occurs_dpc) + if (amdgpu_reset_in_dpc(adev)) tmp_adev->pcie_reset_ctx.in_link_reset = true; } if (!list_is_first(&adev->reset_list, device_list)) @@ -6159,14 +6221,6 @@ static int amdgpu_device_recovery_prepare(struct amdgpu_device *adev, } else { list_add_tail(&adev->reset_list, device_list); } - - if (!amdgpu_sriov_vf(adev) && (!adev->pcie_reset_ctx.occurs_dpc)) { - r = amdgpu_device_health_check(device_list); - if (r) - return r; - } - - return 0; } static void amdgpu_device_recovery_get_reset_lock(struct amdgpu_device *adev, @@ -6235,9 +6289,8 @@ static void amdgpu_device_halt_activities(struct amdgpu_device *adev, drm_client_dev_suspend(adev_to_drm(tmp_adev), false); /* disable ras on ALL IPs */ - if (!need_emergency_restart && - (!adev->pcie_reset_ctx.occurs_dpc) && - amdgpu_device_ip_need_full_reset(tmp_adev)) + if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) && + amdgpu_device_ip_need_full_reset(tmp_adev)) amdgpu_ras_suspend(tmp_adev); for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { @@ -6265,11 +6318,7 @@ static int amdgpu_device_asic_reset(struct amdgpu_device *adev, retry: /* Rest of adevs pre asic reset from XGMI hive. */ list_for_each_entry(tmp_adev, device_list, reset_list) { - if (adev->pcie_reset_ctx.occurs_dpc) - tmp_adev->no_hw_access = true; r = amdgpu_device_pre_asic_reset(tmp_adev, reset_context); - if (adev->pcie_reset_ctx.occurs_dpc) - tmp_adev->no_hw_access = false; /*TODO Should we stop ?*/ if (r) { dev_err(tmp_adev->dev, "GPU pre asic reset failed with err, %d for drm dev, %s ", @@ -6447,8 +6496,9 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, emergency_restart(); } - dev_info(adev->dev, "GPU %s begin!\n", - need_emergency_restart ? "jobs stop":"reset"); + dev_info(adev->dev, "GPU %s begin!. Source: %d\n", + need_emergency_restart ? "jobs stop" : "reset", + reset_context->src); if (!amdgpu_sriov_vf(adev)) hive = amdgpu_get_xgmi_hive(adev); @@ -6459,8 +6509,13 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, reset_context->hive = hive; INIT_LIST_HEAD(&device_list); - if (amdgpu_device_recovery_prepare(adev, &device_list, hive)) - goto end_reset; + amdgpu_device_recovery_prepare(adev, &device_list, hive); + + if (!amdgpu_sriov_vf(adev)) { + r = amdgpu_device_health_check(&device_list); + if (r) + goto end_reset; + } /* We need to lock reset domain only once both for XGMI and single device */ amdgpu_device_recovery_get_reset_lock(adev, &device_list); @@ -6888,11 +6943,6 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta dev_info(adev->dev, "PCI error: detected callback!!\n"); - if (!amdgpu_dpm_is_link_reset_supported(adev)) { - dev_warn(adev->dev, "No support for XGMI hive yet...\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - adev->pci_channel_state = state; switch (state) { @@ -6902,10 +6952,23 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta case pci_channel_io_frozen: /* Fatal error, prepare for slot reset */ dev_info(adev->dev, "pci_channel_io_frozen: state(%d)!!\n", state); + if (hive) { + /* Hive devices should be able to support FW based + * link reset on other devices, if not return. + */ + if (!amdgpu_dpm_is_link_reset_supported(adev)) { + dev_warn(adev->dev, + "No support for XGMI hive yet...\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + /* Set dpc status only if device is part of hive + * Non-hive devices should be able to recover after + * link reset. + */ + amdgpu_reset_set_dpc_status(adev, true); - if (hive) mutex_lock(&hive->hive_lock); - adev->pcie_reset_ctx.occurs_dpc = true; + } memset(&reset_context, 0, sizeof(reset_context)); INIT_LIST_HEAD(&device_list); @@ -6964,22 +7027,34 @@ pci_ers_result_t amdgpu_pci_slot_reset(struct pci_dev *pdev) struct amdgpu_device *tmp_adev; struct amdgpu_hive_info *hive; struct list_head device_list; - int r = 0, i; + struct pci_dev *link_dev; + int r = 0, i, timeout; u32 memsize; - - /* PCI error slot reset should be skipped During RAS recovery */ - if ((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_ras_in_recovery(adev)) - return PCI_ERS_RESULT_RECOVERED; + u16 status; dev_info(adev->dev, "PCI error: slot reset callback!!\n"); memset(&reset_context, 0, sizeof(reset_context)); - /* wait for asic to come out of reset */ - msleep(700); + if (adev->pcie_reset_ctx.swus) + link_dev = adev->pcie_reset_ctx.swus; + else + link_dev = adev->pdev; + /* wait for asic to come out of reset, timeout = 10s */ + timeout = 10000; + do { + usleep_range(10000, 10500); + r = pci_read_config_word(link_dev, PCI_VENDOR_ID, &status); + timeout -= 10; + } while (timeout > 0 && (status != PCI_VENDOR_ID_ATI) && + (status != PCI_VENDOR_ID_AMD)); + if ((status != PCI_VENDOR_ID_ATI) && (status != PCI_VENDOR_ID_AMD)) { + r = -ETIME; + goto out; + } + + amdgpu_device_load_switch_state(adev); /* Restore PCI confspace */ amdgpu_device_load_pci_state(pdev); @@ -7074,7 +7149,6 @@ void amdgpu_pci_resume(struct pci_dev *pdev) 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); - adev->pcie_reset_ctx.occurs_dpc = false; if (hive) { mutex_unlock(&hive->hive_lock); @@ -7082,6 +7156,58 @@ void amdgpu_pci_resume(struct pci_dev *pdev) } } +static void amdgpu_device_cache_switch_state(struct amdgpu_device *adev) +{ + struct pci_dev *parent = pci_upstream_bridge(adev->pdev); + int r; + + if (parent->vendor != PCI_VENDOR_ID_ATI) + return; + + /* If already saved, return */ + if (adev->pcie_reset_ctx.swus) + return; + /* Upstream bridge is ATI, assume it's SWUS/DS architecture */ + r = pci_save_state(parent); + if (r) + return; + adev->pcie_reset_ctx.swds_pcistate = pci_store_saved_state(parent); + + parent = pci_upstream_bridge(parent); + r = pci_save_state(parent); + if (r) + return; + adev->pcie_reset_ctx.swus_pcistate = pci_store_saved_state(parent); + + adev->pcie_reset_ctx.swus = parent; +} + +static void amdgpu_device_load_switch_state(struct amdgpu_device *adev) +{ + struct pci_dev *pdev; + int r; + + if (!adev->pcie_reset_ctx.swds_pcistate || + !adev->pcie_reset_ctx.swus_pcistate) + return; + + pdev = adev->pcie_reset_ctx.swus; + r = pci_load_saved_state(pdev, adev->pcie_reset_ctx.swus_pcistate); + if (!r) { + pci_restore_state(pdev); + } else { + dev_warn(adev->dev, "Failed to load SWUS state, err:%d\n", r); + return; + } + + pdev = pci_upstream_bridge(adev->pdev); + r = pci_load_saved_state(pdev, adev->pcie_reset_ctx.swds_pcistate); + if (!r) + pci_restore_state(pdev); + else + dev_warn(adev->dev, "Failed to load SWDS state, err:%d\n", r); +} + bool amdgpu_device_cache_pci_state(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -7106,6 +7232,8 @@ bool amdgpu_device_cache_pci_state(struct pci_dev *pdev) return false; } + amdgpu_device_cache_switch_state(adev); + return true; } @@ -7492,3 +7620,53 @@ ssize_t amdgpu_show_reset_mask(char *buf, uint32_t supported_reset) size += sysfs_emit_at(buf, size, "\n"); return size; } + +void amdgpu_device_set_uid(struct amdgpu_uid *uid_info, + enum amdgpu_uid_type type, uint8_t inst, + uint64_t uid) +{ + if (!uid_info) + return; + + if (type >= AMDGPU_UID_TYPE_MAX) { + dev_err_once(uid_info->adev->dev, "Invalid UID type %d\n", + type); + return; + } + + if (inst >= AMDGPU_UID_INST_MAX) { + dev_err_once(uid_info->adev->dev, "Invalid UID instance %d\n", + inst); + return; + } + + if (uid_info->uid[type][inst] != 0) { + dev_warn_once( + uid_info->adev->dev, + "Overwriting existing UID %llu for type %d instance %d\n", + uid_info->uid[type][inst], type, inst); + } + + uid_info->uid[type][inst] = uid; +} + +u64 amdgpu_device_get_uid(struct amdgpu_uid *uid_info, + enum amdgpu_uid_type type, uint8_t inst) +{ + if (!uid_info) + return 0; + + if (type >= AMDGPU_UID_TYPE_MAX) { + dev_err_once(uid_info->adev->dev, "Invalid UID type %d\n", + type); + return 0; + } + + if (inst >= AMDGPU_UID_INST_MAX) { + dev_err_once(uid_info->adev->dev, "Invalid UID instance %d\n", + inst); + return 0; + } + + return uid_info->uid[type][inst]; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 81b3443c8d7f..73401f0aeb34 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -276,7 +276,7 @@ static int amdgpu_discovery_read_binary_from_mem(struct amdgpu_device *adev, u32 msg; if (!amdgpu_sriov_vf(adev)) { - /* It can take up to a second for IFWI init to complete on some dGPUs, + /* It can take up to two second for IFWI init to complete on some dGPUs, * but generally it should be in the 60-100ms range. Normally this starts * as soon as the device gets power so by the time the OS loads this has long * completed. However, when a card is hotplugged via e.g., USB4, we need to @@ -284,7 +284,7 @@ static int amdgpu_discovery_read_binary_from_mem(struct amdgpu_device *adev, * continue. */ - for (i = 0; i < 1000; i++) { + for (i = 0; i < 2000; i++) { msg = RREG32(mmMP0_SMN_C2PMSG_33); if (msg & 0x80000000) break; @@ -2124,7 +2124,6 @@ static int amdgpu_discovery_set_smu_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 0, 5): case IP_VERSION(11, 0, 9): case IP_VERSION(11, 0, 7): - case IP_VERSION(11, 0, 8): case IP_VERSION(11, 0, 11): case IP_VERSION(11, 0, 12): case IP_VERSION(11, 0, 13): @@ -2132,6 +2131,10 @@ static int amdgpu_discovery_set_smu_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 2): amdgpu_device_ip_block_add(adev, &smu_v11_0_ip_block); break; + case IP_VERSION(11, 0, 8): + if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2) + amdgpu_device_ip_block_add(adev, &smu_v11_0_ip_block); + break; case IP_VERSION(12, 0, 0): case IP_VERSION(12, 0, 1): amdgpu_device_ip_block_add(adev, &smu_v12_0_ip_block); @@ -2555,40 +2558,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) switch (adev->asic_type) { case CHIP_VEGA10: - case CHIP_VEGA12: - case CHIP_RAVEN: - case CHIP_VEGA20: - case CHIP_ARCTURUS: - case CHIP_ALDEBARAN: - /* this is not fatal. We have a fallback below - * if the new firmwares are not present. some of - * this will be overridden below to keep things - * consistent with the current behavior. + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. */ - r = amdgpu_discovery_reg_base_init(adev); - if (!r) { - amdgpu_discovery_harvest_ip(adev); - amdgpu_discovery_get_gfx_info(adev); - amdgpu_discovery_get_mall_info(adev); - amdgpu_discovery_get_vcn_info(adev); - } - break; - default: - r = amdgpu_discovery_reg_base_init(adev); - if (r) { - drm_err(&adev->ddev, "discovery failed: %d\n", r); - return r; - } - - amdgpu_discovery_harvest_ip(adev); - amdgpu_discovery_get_gfx_info(adev); - amdgpu_discovery_get_mall_info(adev); - amdgpu_discovery_get_vcn_info(adev); - break; - } - - switch (adev->asic_type) { - case CHIP_VEGA10: + amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 2; adev->gmc.num_umc = 4; @@ -2611,6 +2585,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[DCI_HWIP][0] = IP_VERSION(12, 0, 0); break; case CHIP_VEGA12: + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. + */ + amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 2; adev->gmc.num_umc = 4; @@ -2633,6 +2612,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[DCI_HWIP][0] = IP_VERSION(12, 0, 1); break; case CHIP_RAVEN: + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. + */ + amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 1; adev->vcn.num_vcn_inst = 1; @@ -2674,6 +2658,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) } break; case CHIP_VEGA20: + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. + */ + amdgpu_discovery_init(adev); vega20_reg_base_init(adev); adev->sdma.num_instances = 2; adev->gmc.num_umc = 8; @@ -2697,6 +2686,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[DCI_HWIP][0] = IP_VERSION(12, 1, 0); break; case CHIP_ARCTURUS: + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. + */ + amdgpu_discovery_init(adev); arct_reg_base_init(adev); adev->sdma.num_instances = 8; adev->vcn.num_vcn_inst = 2; @@ -2725,6 +2719,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[UVD_HWIP][1] = IP_VERSION(2, 5, 0); break; case CHIP_ALDEBARAN: + /* This is not fatal. We only need the discovery + * binary for sysfs. We don't need it for a + * functional system. + */ + amdgpu_discovery_init(adev); aldebaran_reg_base_init(adev); adev->sdma.num_instances = 5; adev->vcn.num_vcn_inst = 2; @@ -2750,7 +2749,47 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[UVD_HWIP][1] = IP_VERSION(2, 6, 0); adev->ip_versions[XGMI_HWIP][0] = IP_VERSION(6, 1, 0); break; + case CHIP_CYAN_SKILLFISH: + if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2) { + r = amdgpu_discovery_reg_base_init(adev); + if (r) + return -EINVAL; + + amdgpu_discovery_harvest_ip(adev); + amdgpu_discovery_get_gfx_info(adev); + amdgpu_discovery_get_mall_info(adev); + amdgpu_discovery_get_vcn_info(adev); + } else { + cyan_skillfish_reg_base_init(adev); + adev->sdma.num_instances = 2; + adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(2, 0, 3); + adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(2, 0, 3); + adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[HDP_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[SDMA0_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[SDMA1_HWIP][1] = IP_VERSION(5, 0, 1); + adev->ip_versions[DF_HWIP][0] = IP_VERSION(3, 5, 0); + adev->ip_versions[NBIO_HWIP][0] = IP_VERSION(2, 1, 1); + adev->ip_versions[UMC_HWIP][0] = IP_VERSION(8, 1, 1); + adev->ip_versions[MP0_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[MP1_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[THM_HWIP][0] = IP_VERSION(11, 0, 1); + adev->ip_versions[SMUIO_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 1, 3); + adev->ip_versions[UVD_HWIP][0] = IP_VERSION(2, 0, 3); + } + break; default: + r = amdgpu_discovery_reg_base_init(adev); + if (r) { + drm_err(&adev->ddev, "discovery failed: %d\n", r); + return r; + } + + amdgpu_discovery_harvest_ip(adev); + amdgpu_discovery_get_gfx_info(adev); + amdgpu_discovery_get_mall_info(adev); + amdgpu_discovery_get_vcn_info(adev); break; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index ff98c87b2e0b..8561ad7f6180 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -285,6 +285,36 @@ static int amdgpu_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, return ret; } +static int amdgpu_dma_buf_vmap(struct dma_buf *dma_buf, struct iosys_map *map) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); + int ret; + + /* + * Pin to keep buffer in place while it's vmap'ed. The actual + * domain is not that important as long as it's mapable. Using + * GTT and VRAM should be compatible with most use cases. + */ + ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM); + if (ret) + return ret; + ret = drm_gem_dmabuf_vmap(dma_buf, map); + if (ret) + amdgpu_bo_unpin(bo); + + return ret; +} + +static void amdgpu_dma_buf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); + + drm_gem_dmabuf_vunmap(dma_buf, map); + amdgpu_bo_unpin(bo); +} + const struct dma_buf_ops amdgpu_dmabuf_ops = { .attach = amdgpu_dma_buf_attach, .pin = amdgpu_dma_buf_pin, @@ -294,8 +324,8 @@ const struct dma_buf_ops amdgpu_dmabuf_ops = { .release = drm_gem_dmabuf_release, .begin_cpu_access = amdgpu_dma_buf_begin_cpu_access, .mmap = drm_gem_dmabuf_mmap, - .vmap = drm_gem_dmabuf_vmap, - .vunmap = drm_gem_dmabuf_vunmap, + .vmap = amdgpu_dma_buf_vmap, + .vunmap = amdgpu_dma_buf_vunmap, }; /** @@ -313,11 +343,23 @@ struct dma_buf *amdgpu_gem_prime_export(struct drm_gem_object *gobj, { struct amdgpu_bo *bo = gem_to_amdgpu_bo(gobj); struct dma_buf *buf; + struct ttm_operation_ctx ctx = { + .interruptible = true, + .no_wait_gpu = true, + /* We opt to avoid OOM on system pages allocations */ + .gfp_retry_mayfail = true, + .allow_res_evict = false, + }; + int ret; if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) || bo->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) return ERR_PTR(-EPERM); + ret = ttm_bo_setup_export(&bo->tbo, &ctx); + if (ret) + return ERR_PTR(ret); + buf = drm_gem_prime_export(gobj, flags); if (!IS_ERR(buf)) buf->ops = &amdgpu_dmabuf_ops; @@ -514,7 +556,7 @@ bool amdgpu_dmabuf_is_xgmi_accessible(struct amdgpu_device *adev, return false; if (drm_gem_is_imported(obj)) { - struct dma_buf *dma_buf = obj->dma_buf; + struct dma_buf *dma_buf = obj->import_attach->dmabuf; if (dma_buf->ops != &amdgpu_dmabuf_ops) /* No XGMI with non AMD GPUs */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 395c6be901ce..ece251cbe8c3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -886,7 +886,7 @@ module_param_named(dcfeaturemask, amdgpu_dc_feature_mask, uint, 0444); /** * DOC: dcdebugmask (uint) - * Override display features enabled. See enum DC_DEBUG_MASK in drivers/gpu/drm/amd/include/amd_shared.h. + * Display debug options. See enum DC_DEBUG_MASK in drivers/gpu/drm/amd/include/amd_shared.h. */ MODULE_PARM_DESC(dcdebugmask, "all debug options disabled (default))"); module_param_named(dcdebugmask, amdgpu_dc_debug_mask, uint, 0444); @@ -2172,6 +2172,11 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN}, /* CYAN_SKILLFISH */ + {0x1002, 0x13DB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, + {0x1002, 0x13F9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, + {0x1002, 0x13FA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, + {0x1002, 0x13FB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, + {0x1002, 0x13FC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, {0x1002, 0x13FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, {0x1002, 0x143F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, @@ -2597,6 +2602,7 @@ static int amdgpu_pmops_suspend(struct device *dev) else if (amdgpu_acpi_is_s3_active(adev)) adev->in_s3 = true; if (!adev->in_s0ix && !adev->in_s3) { +#if IS_ENABLED(CONFIG_SUSPEND) /* don't allow going deep first time followed by s2idle the next time */ if (adev->last_suspend_state != PM_SUSPEND_ON && adev->last_suspend_state != pm_suspend_target_state) { @@ -2604,11 +2610,14 @@ static int amdgpu_pmops_suspend(struct device *dev) pm_suspend_target_state); return -EINVAL; } +#endif return 0; } +#if IS_ENABLED(CONFIG_SUSPEND) /* cache the state last used for suspend */ adev->last_suspend_state = pm_suspend_target_state; +#endif return amdgpu_device_suspend(drm_dev, true); } @@ -2933,11 +2942,14 @@ static int amdgpu_drm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; struct amdgpu_fpriv *fpriv = file_priv->driver_priv; + struct drm_device *dev = file_priv->minor->dev; + int idx; - if (fpriv) { + if (fpriv && drm_dev_enter(dev, &idx)) { fpriv->evf_mgr.fd_closing = true; amdgpu_eviction_fence_destroy(&fpriv->evf_mgr); amdgpu_userq_mgr_fini(&fpriv->userq_mgr); + drm_dev_exit(idx); } return drm_release(inode, filp); @@ -2964,15 +2976,15 @@ long amdgpu_drm_ioctl(struct file *filp, } static const struct dev_pm_ops amdgpu_pm_ops = { - .prepare = amdgpu_pmops_prepare, - .complete = amdgpu_pmops_complete, - .suspend = amdgpu_pmops_suspend, - .suspend_noirq = amdgpu_pmops_suspend_noirq, - .resume = amdgpu_pmops_resume, - .freeze = amdgpu_pmops_freeze, - .thaw = amdgpu_pmops_thaw, - .poweroff = amdgpu_pmops_poweroff, - .restore = amdgpu_pmops_restore, + .prepare = pm_sleep_ptr(amdgpu_pmops_prepare), + .complete = pm_sleep_ptr(amdgpu_pmops_complete), + .suspend = pm_sleep_ptr(amdgpu_pmops_suspend), + .suspend_noirq = pm_sleep_ptr(amdgpu_pmops_suspend_noirq), + .resume = pm_sleep_ptr(amdgpu_pmops_resume), + .freeze = pm_sleep_ptr(amdgpu_pmops_freeze), + .thaw = pm_sleep_ptr(amdgpu_pmops_thaw), + .poweroff = pm_sleep_ptr(amdgpu_pmops_poweroff), + .restore = pm_sleep_ptr(amdgpu_pmops_restore), .runtime_suspend = amdgpu_pmops_runtime_suspend, .runtime_resume = amdgpu_pmops_runtime_resume, .runtime_idle = amdgpu_pmops_runtime_idle, @@ -3044,6 +3056,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_USERQ, amdgpu_userq_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_SIGNAL, amdgpu_userq_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct drm_driver amdgpu_kms_driver = { @@ -3117,7 +3130,7 @@ static struct pci_driver amdgpu_kms_pci_driver = { .probe = amdgpu_pci_probe, .remove = amdgpu_pci_remove, .shutdown = amdgpu_pci_shutdown, - .driver.pm = &amdgpu_pm_ops, + .driver.pm = pm_ptr(&amdgpu_pm_ops), .err_handler = &amdgpu_pci_err_handler, .dev_groups = amdgpu_sysfs_groups, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index 9e7506965cab..fd8cca241da6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -120,7 +120,6 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, struct dma_fence **f, am_fence = kzalloc(sizeof(*am_fence), GFP_KERNEL); if (!am_fence) return -ENOMEM; - am_fence->context = 0; } else { am_fence = af; } @@ -738,7 +737,7 @@ void amdgpu_fence_driver_force_completion(struct amdgpu_ring *ring) } -/** +/* * Kernel queue reset handling * * The driver can reset individual queues for most engines, but those queues diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 6626a6e64ff5..c049848a56b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -317,7 +317,8 @@ static int amdgpu_gem_object_open(struct drm_gem_object *obj, */ if (!vm->is_compute_context || !vm->process_info) return 0; - if (!drm_gem_is_imported(obj) || !dma_buf_is_dynamic(obj->dma_buf)) + if (!drm_gem_is_imported(obj) || + !dma_buf_is_dynamic(obj->import_attach->dmabuf)) return 0; mutex_lock_nested(&vm->process_info->lock, 1); if (!WARN_ON(!vm->process_info->eviction_fence)) { @@ -442,15 +443,7 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, int r; /* reject invalid gem flags */ - if (flags & ~(AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | - AMDGPU_GEM_CREATE_NO_CPU_ACCESS | - AMDGPU_GEM_CREATE_CPU_GTT_USWC | - AMDGPU_GEM_CREATE_VRAM_CLEARED | - AMDGPU_GEM_CREATE_VM_ALWAYS_VALID | - AMDGPU_GEM_CREATE_EXPLICIT_SYNC | - AMDGPU_GEM_CREATE_ENCRYPTED | - AMDGPU_GEM_CREATE_GFX12_DCC | - AMDGPU_GEM_CREATE_DISCARDABLE)) + if (flags & ~AMDGPU_GEM_CREATE_SETTABLE_MASK) return -EINVAL; /* reject invalid gem domains */ @@ -790,36 +783,6 @@ amdgpu_gem_va_update_vm(struct amdgpu_device *adev, return fence; } -/** - * amdgpu_gem_va_map_flags - map GEM UAPI flags into hardware flags - * - * @adev: amdgpu_device pointer - * @flags: GEM UAPI flags - * - * Returns the GEM UAPI flags mapped into hardware for the ASIC. - */ -uint64_t amdgpu_gem_va_map_flags(struct amdgpu_device *adev, uint32_t flags) -{ - uint64_t pte_flag = 0; - - if (flags & AMDGPU_VM_PAGE_EXECUTABLE) - pte_flag |= AMDGPU_PTE_EXECUTABLE; - if (flags & AMDGPU_VM_PAGE_READABLE) - pte_flag |= AMDGPU_PTE_READABLE; - if (flags & AMDGPU_VM_PAGE_WRITEABLE) - pte_flag |= AMDGPU_PTE_WRITEABLE; - if (flags & AMDGPU_VM_PAGE_PRT) - pte_flag |= AMDGPU_PTE_PRT_FLAG(adev); - if (flags & AMDGPU_VM_PAGE_NOALLOC) - pte_flag |= AMDGPU_PTE_NOALLOC; - - if (adev->gmc.gmc_funcs->map_mtype) - pte_flag |= amdgpu_gmc_map_mtype(adev, - flags & AMDGPU_VM_MTYPE_MASK); - - return pte_flag; -} - int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { @@ -840,7 +803,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct dma_fence_chain *timeline_chain = NULL; struct dma_fence *fence; struct drm_exec exec; - uint64_t va_flags; uint64_t vm_size; int r = 0; @@ -944,10 +906,9 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, switch (args->operation) { case AMDGPU_VA_OP_MAP: - va_flags = amdgpu_gem_va_map_flags(adev, args->flags); r = amdgpu_vm_bo_map(adev, bo_va, args->va_address, args->offset_in_bo, args->map_size, - va_flags); + args->flags); break; case AMDGPU_VA_OP_UNMAP: r = amdgpu_vm_bo_unmap(adev, bo_va, args->va_address); @@ -959,10 +920,9 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, args->map_size); break; case AMDGPU_VA_OP_REPLACE: - va_flags = amdgpu_gem_va_map_flags(adev, args->flags); r = amdgpu_vm_bo_replace_map(adev, bo_va, args->va_address, args->offset_in_bo, args->map_size, - va_flags); + args->flags); break; default: break; @@ -996,17 +956,34 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *gobj; struct amdgpu_vm_bo_base *base; struct amdgpu_bo *robj; + struct drm_exec exec; + struct amdgpu_fpriv *fpriv = filp->driver_priv; int r; + if (args->padding) + return -EINVAL; + gobj = drm_gem_object_lookup(filp, args->handle); if (!gobj) return -ENOENT; robj = gem_to_amdgpu_bo(gobj); - r = amdgpu_bo_reserve(robj, false); - if (unlikely(r)) - goto out; + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | + DRM_EXEC_IGNORE_DUPLICATES, 0); + drm_exec_until_all_locked(&exec) { + r = drm_exec_lock_obj(&exec, gobj); + drm_exec_retry_on_contention(&exec); + if (r) + goto out_exec; + + if (args->op == AMDGPU_GEM_OP_GET_MAPPING_INFO) { + r = amdgpu_vm_lock_pd(&fpriv->vm, &exec, 0); + drm_exec_retry_on_contention(&exec); + if (r) + goto out_exec; + } + } switch (args->op) { case AMDGPU_GEM_OP_GET_GEM_CREATE_INFO: { @@ -1017,7 +994,7 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, info.alignment = robj->tbo.page_alignment << PAGE_SHIFT; info.domains = robj->preferred_domains; info.domain_flags = robj->flags; - amdgpu_bo_unreserve(robj); + drm_exec_fini(&exec); if (copy_to_user(out, &info, sizeof(info))) r = -EFAULT; break; @@ -1026,20 +1003,17 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, if (drm_gem_is_imported(&robj->tbo.base) && args->value & AMDGPU_GEM_DOMAIN_VRAM) { r = -EINVAL; - amdgpu_bo_unreserve(robj); - break; + goto out_exec; } if (amdgpu_ttm_tt_get_usermm(robj->tbo.ttm)) { r = -EPERM; - amdgpu_bo_unreserve(robj); - break; + goto out_exec; } for (base = robj->vm_bo; base; base = base->next) if (amdgpu_xgmi_same_hive(amdgpu_ttm_adev(robj->tbo.bdev), amdgpu_ttm_adev(base->vm->root.bo->tbo.bdev))) { r = -EINVAL; - amdgpu_bo_unreserve(robj); - goto out; + goto out_exec; } @@ -1052,17 +1026,146 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, if (robj->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) amdgpu_vm_bo_invalidate(robj, true); - - amdgpu_bo_unreserve(robj); + drm_exec_fini(&exec); break; + case AMDGPU_GEM_OP_GET_MAPPING_INFO: { + struct amdgpu_bo_va *bo_va = amdgpu_vm_bo_find(&fpriv->vm, robj); + struct drm_amdgpu_gem_vm_entry *vm_entries; + struct amdgpu_bo_va_mapping *mapping; + int num_mappings = 0; + /* + * num_entries is set as an input to the size of the user-allocated array of + * drm_amdgpu_gem_vm_entry stored at args->value. + * num_entries is sent back as output as the number of mappings the bo has. + * If that number is larger than the size of the array, the ioctl must + * be retried. + */ + vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); + if (!vm_entries) + return -ENOMEM; + + amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) { + if (num_mappings < args->num_entries) { + vm_entries[num_mappings].addr = mapping->start * AMDGPU_GPU_PAGE_SIZE; + vm_entries[num_mappings].size = (mapping->last - mapping->start + 1) * AMDGPU_GPU_PAGE_SIZE; + vm_entries[num_mappings].offset = mapping->offset; + vm_entries[num_mappings].flags = mapping->flags; + } + num_mappings += 1; + } + + amdgpu_vm_bo_va_for_each_invalid_mapping(bo_va, mapping) { + if (num_mappings < args->num_entries) { + vm_entries[num_mappings].addr = mapping->start * AMDGPU_GPU_PAGE_SIZE; + vm_entries[num_mappings].size = (mapping->last - mapping->start + 1) * AMDGPU_GPU_PAGE_SIZE; + vm_entries[num_mappings].offset = mapping->offset; + vm_entries[num_mappings].flags = mapping->flags; + } + num_mappings += 1; + } + + drm_exec_fini(&exec); + + if (num_mappings > 0 && num_mappings <= args->num_entries) + if (copy_to_user(u64_to_user_ptr(args->value), vm_entries, num_mappings * sizeof(*vm_entries))) + r = -EFAULT; + + args->num_entries = num_mappings; + + kvfree(vm_entries); + break; + } default: - amdgpu_bo_unreserve(robj); + drm_exec_fini(&exec); r = -EINVAL; } -out: drm_gem_object_put(gobj); return r; +out_exec: + drm_exec_fini(&exec); + drm_gem_object_put(gobj); + return r; +} + +/** + * amdgpu_gem_list_handles_ioctl - get information about a process' buffer objects + * + * @dev: drm device pointer + * @data: drm_amdgpu_gem_list_handles + * @filp: drm file pointer + * + * num_entries is set as an input to the size of the entries array. + * num_entries is sent back as output as the number of bos in the process. + * If that number is larger than the size of the array, the ioctl must + * be retried. + * + * Returns: + * 0 for success, -errno for errors. + */ +int amdgpu_gem_list_handles_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_amdgpu_gem_list_handles *args = data; + struct drm_amdgpu_gem_list_handles_entry *bo_entries; + struct drm_gem_object *gobj; + int id, ret = 0; + int bo_index = 0; + int num_bos = 0; + + spin_lock(&filp->table_lock); + idr_for_each_entry(&filp->object_idr, gobj, id) + num_bos += 1; + spin_unlock(&filp->table_lock); + + if (args->num_entries < num_bos) { + args->num_entries = num_bos; + return 0; + } + + if (num_bos == 0) { + args->num_entries = 0; + return 0; + } + + bo_entries = kvcalloc(num_bos, sizeof(*bo_entries), GFP_KERNEL); + if (!bo_entries) + return -ENOMEM; + + spin_lock(&filp->table_lock); + idr_for_each_entry(&filp->object_idr, gobj, id) { + struct amdgpu_bo *bo = gem_to_amdgpu_bo(gobj); + struct drm_amdgpu_gem_list_handles_entry *bo_entry; + + if (bo_index >= num_bos) { + ret = -EAGAIN; + break; + } + + bo_entry = &bo_entries[bo_index]; + + bo_entry->size = amdgpu_bo_size(bo); + bo_entry->alloc_flags = bo->flags & AMDGPU_GEM_CREATE_SETTABLE_MASK; + bo_entry->preferred_domains = bo->preferred_domains; + bo_entry->gem_handle = id; + bo_entry->alignment = bo->tbo.page_alignment; + + if (bo->tbo.base.import_attach) + bo_entry->flags |= AMDGPU_GEM_LIST_HANDLES_FLAG_IS_IMPORT; + + bo_index += 1; + } + spin_unlock(&filp->table_lock); + + args->num_entries = bo_index; + + if (!ret) + if (copy_to_user(u64_to_user_ptr(args->entries), bo_entries, num_bos * sizeof(*bo_entries))) + ret = -EFAULT; + + kvfree(bo_entries); + + return ret; } static int amdgpu_gem_align_pitch(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h index 3a8f57900a3a..b558336bc4c6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h @@ -63,13 +63,28 @@ int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); -uint64_t amdgpu_gem_va_map_flags(struct amdgpu_device *adev, uint32_t flags); int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int amdgpu_gem_list_handles_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +#define AMDGPU_GEM_CREATE_SETTABLE_MASK (AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \ + AMDGPU_GEM_CREATE_NO_CPU_ACCESS | \ + AMDGPU_GEM_CREATE_CPU_GTT_USWC | \ + AMDGPU_GEM_CREATE_VRAM_CLEARED | \ + AMDGPU_GEM_CREATE_VM_ALWAYS_VALID | \ + AMDGPU_GEM_CREATE_EXPLICIT_SYNC | \ + AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE | \ + AMDGPU_GEM_CREATE_ENCRYPTED | \ + AMDGPU_GEM_CREATE_GFX12_DCC | \ + AMDGPU_GEM_CREATE_DISCARDABLE | \ + AMDGPU_GEM_CREATE_COHERENT | \ + AMDGPU_GEM_CREATE_UNCACHED | \ + AMDGPU_GEM_CREATE_EXT_COHERENT) + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index c80c8f543532..98aa99b314c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1474,7 +1474,8 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) owner = (void *)(unsigned long)atomic_inc_return(&counter); r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner, - 64, 0, &job); + 64, 0, &job, + AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER); if (r) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 97b562a79ea8..9dcf51991b5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -690,7 +690,7 @@ void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, r = amdgpu_job_alloc_with_ib(ring->adev, &adev->mman.high_pr, AMDGPU_FENCE_OWNER_UNDEFINED, 16 * 4, AMDGPU_IB_POOL_IMMEDIATE, - &job); + &job, AMDGPU_KERNEL_JOB_ID_FLUSH_GPU_TLB); if (r) goto error_alloc; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index 397c6ccdb903..55097ca10738 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -154,15 +154,15 @@ struct amdgpu_gmc_funcs { unsigned pasid); /* enable/disable PRT support */ void (*set_prt)(struct amdgpu_device *adev, bool enable); - /* map mtype to hardware flags */ - uint64_t (*map_mtype)(struct amdgpu_device *adev, uint32_t flags); /* get the pde for a given mc addr */ void (*get_vm_pde)(struct amdgpu_device *adev, int level, u64 *dst, u64 *flags); - /* get the pte flags to use for a BO VA mapping */ + /* get the pte flags to use for PTEs */ void (*get_vm_pte)(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t *flags); + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, + uint64_t *pte_flags); /* override per-page pte flags */ void (*override_vm_pte_flags)(struct amdgpu_device *dev, struct amdgpu_vm *vm, @@ -356,9 +356,10 @@ struct amdgpu_gmc { #define amdgpu_gmc_emit_flush_gpu_tlb(r, vmid, addr) (r)->adev->gmc.gmc_funcs->emit_flush_gpu_tlb((r), (vmid), (addr)) #define amdgpu_gmc_emit_pasid_mapping(r, vmid, pasid) (r)->adev->gmc.gmc_funcs->emit_pasid_mapping((r), (vmid), (pasid)) -#define amdgpu_gmc_map_mtype(adev, flags) (adev)->gmc.gmc_funcs->map_mtype((adev),(flags)) #define amdgpu_gmc_get_vm_pde(adev, level, dst, flags) (adev)->gmc.gmc_funcs->get_vm_pde((adev), (level), (dst), (flags)) -#define amdgpu_gmc_get_vm_pte(adev, mapping, flags) (adev)->gmc.gmc_funcs->get_vm_pte((adev), (mapping), (flags)) +#define amdgpu_gmc_get_vm_pte(adev, vm, bo, vm_flags, pte_flags) \ + ((adev)->gmc.gmc_funcs->get_vm_pte((adev), (vm), (bo), (vm_flags), \ + (pte_flags))) #define amdgpu_gmc_override_vm_pte_flags(adev, vm, addr, pte_flags) \ (adev)->gmc.gmc_funcs->override_vm_pte_flags \ ((adev), (vm), (addr), (pte_flags)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index e6061d45f142..d020a890a0ea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -209,11 +209,12 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, struct drm_sched_entity *entity, void *owner, size_t size, enum amdgpu_ib_pool_type pool_type, - struct amdgpu_job **job) + struct amdgpu_job **job, u64 k_job_id) { int r; - r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, job, 0); + r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, job, + k_job_id); if (r) return r; @@ -365,13 +366,6 @@ amdgpu_job_prepare_job(struct drm_sched_job *sched_job, dev_err(ring->adev->dev, "Error getting VM ID (%d)\n", r); goto error; } - /* - * The VM structure might be released after the VMID is - * assigned, we had multiple problems with people trying to use - * the VM pointer so better set it to NULL. - */ - if (!fence) - job->vm = NULL; return fence; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index 2f302266662b..4a6487eb6cb5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -44,6 +44,22 @@ struct amdgpu_fence; enum amdgpu_ib_pool_type; +/* Internal kernel job ids. (decreasing values, starting from U64_MAX). */ +#define AMDGPU_KERNEL_JOB_ID_VM_UPDATE (18446744073709551615ULL) +#define AMDGPU_KERNEL_JOB_ID_VM_UPDATE_PDES (18446744073709551614ULL) +#define AMDGPU_KERNEL_JOB_ID_VM_UPDATE_RANGE (18446744073709551613ULL) +#define AMDGPU_KERNEL_JOB_ID_VM_PT_CLEAR (18446744073709551612ULL) +#define AMDGPU_KERNEL_JOB_ID_TTM_MAP_BUFFER (18446744073709551611ULL) +#define AMDGPU_KERNEL_JOB_ID_TTM_ACCESS_MEMORY_SDMA (18446744073709551610ULL) +#define AMDGPU_KERNEL_JOB_ID_TTM_COPY_BUFFER (18446744073709551609ULL) +#define AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE (18446744073709551608ULL) +#define AMDGPU_KERNEL_JOB_ID_MOVE_BLIT (18446744073709551607ULL) +#define AMDGPU_KERNEL_JOB_ID_TTM_CLEAR_BUFFER (18446744073709551606ULL) +#define AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER (18446744073709551605ULL) +#define AMDGPU_KERNEL_JOB_ID_FLUSH_GPU_TLB (18446744073709551604ULL) +#define AMDGPU_KERNEL_JOB_ID_KFD_GART_MAP (18446744073709551603ULL) +#define AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST (18446744073709551602ULL) + struct amdgpu_job { struct drm_sched_job base; struct amdgpu_vm *vm; @@ -96,7 +112,8 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, struct drm_sched_entity *entity, void *owner, size_t size, enum amdgpu_ib_pool_type pool_type, - struct amdgpu_job **job); + struct amdgpu_job **job, + u64 k_job_id); void amdgpu_job_set_resources(struct amdgpu_job *job, struct amdgpu_bo *gds, struct amdgpu_bo *gws, struct amdgpu_bo *oa); void amdgpu_job_free_resources(struct amdgpu_job *job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 82d58ac7afb0..22da65f45226 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -121,10 +121,12 @@ static void amdgpu_jpeg_idle_work_handler(struct work_struct *work) fences += amdgpu_fence_count_emitted(&adev->jpeg.inst[i].ring_dec[j]); } - if (!fences && !atomic_read(&adev->jpeg.total_submission_cnt)) + if (!fences && !atomic_read(&adev->jpeg.total_submission_cnt)) { + mutex_lock(&adev->jpeg.jpeg_pg_lock); amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG, AMD_PG_STATE_GATE); - else + mutex_unlock(&adev->jpeg.jpeg_pg_lock); + } else schedule_delayed_work(&adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT); } @@ -194,7 +196,8 @@ static int amdgpu_jpeg_dec_set_reg(struct amdgpu_ring *ring, uint32_t handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job); + AMDGPU_IB_POOL_DIRECT, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -368,7 +371,7 @@ static int amdgpu_debugfs_jpeg_sched_mask_set(void *data, u64 val) for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) { ring = &adev->jpeg.inst[i].ring_dec[j]; - if (val & (1 << ((i * adev->jpeg.num_jpeg_rings) + j))) + if (val & (BIT_ULL(1) << ((i * adev->jpeg.num_jpeg_rings) + j))) ring->sched.ready = true; else ring->sched.ready = false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index 135598502c8d..5bf9be073cdd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -191,6 +191,20 @@ int amdgpu_mes_init(struct amdgpu_device *adev) if (r) goto error_doorbell; + if (adev->mes.hung_queue_db_array_size) { + r = amdgpu_bo_create_kernel(adev, + adev->mes.hung_queue_db_array_size * sizeof(u32), + PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, + &adev->mes.hung_queue_db_array_gpu_obj, + &adev->mes.hung_queue_db_array_gpu_addr, + &adev->mes.hung_queue_db_array_cpu_addr); + if (r) { + dev_warn(adev->dev, "failed to create MES hung db array buffer (%d)", r); + goto error_doorbell; + } + } + return 0; error_doorbell: @@ -216,6 +230,10 @@ void amdgpu_mes_fini(struct amdgpu_device *adev) { int i; + amdgpu_bo_free_kernel(&adev->mes.hung_queue_db_array_gpu_obj, + &adev->mes.hung_queue_db_array_gpu_addr, + &adev->mes.hung_queue_db_array_cpu_addr); + amdgpu_bo_free_kernel(&adev->mes.event_log_gpu_obj, &adev->mes.event_log_gpu_addr, &adev->mes.event_log_cpu_addr); @@ -366,6 +384,53 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev, return r; } +int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev) +{ + return adev->mes.hung_queue_db_array_size; +} + +int amdgpu_mes_detect_and_reset_hung_queues(struct amdgpu_device *adev, + int queue_type, + bool detect_only, + unsigned int *hung_db_num, + u32 *hung_db_array) + +{ + struct mes_detect_and_reset_queue_input input; + u32 *db_array = adev->mes.hung_queue_db_array_cpu_addr; + int r, i; + + if (!hung_db_num || !hung_db_array) + return -EINVAL; + + if ((queue_type != AMDGPU_RING_TYPE_GFX) && + (queue_type != AMDGPU_RING_TYPE_COMPUTE) && + (queue_type != AMDGPU_RING_TYPE_SDMA)) + return -EINVAL; + + /* Clear the doorbell array before detection */ + memset(adev->mes.hung_queue_db_array_cpu_addr, 0, + adev->mes.hung_queue_db_array_size * sizeof(u32)); + input.queue_type = queue_type; + input.detect_only = detect_only; + + r = adev->mes.funcs->detect_and_reset_hung_queues(&adev->mes, + &input); + if (r) { + dev_err(adev->dev, "failed to detect and reset\n"); + } else { + *hung_db_num = 0; + for (i = 0; i < adev->mes.hung_queue_db_array_size; i++) { + if (db_array[i] != AMDGPU_MES_INVALID_DB_OFFSET) { + hung_db_array[i] = db_array[i]; + *hung_db_num += 1; + } + } + } + + return r; +} + uint32_t amdgpu_mes_rreg(struct amdgpu_device *adev, uint32_t reg) { struct mes_misc_op_input op_input; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h index c0d2c195fe2e..6b506fc72f58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h @@ -41,6 +41,7 @@ #define AMDGPU_MES_API_VERSION_MASK 0x00fff000 #define AMDGPU_MES_FEAT_VERSION_MASK 0xff000000 #define AMDGPU_MES_MSCRATCH_SIZE 0x40000 +#define AMDGPU_MES_INVALID_DB_OFFSET 0xffffffff enum amdgpu_mes_priority_level { AMDGPU_MES_PRIORITY_LEVEL_LOW = 0, @@ -147,6 +148,10 @@ struct amdgpu_mes { uint64_t resource_1_gpu_addr[AMDGPU_MAX_MES_PIPES]; void *resource_1_addr[AMDGPU_MAX_MES_PIPES]; + int hung_queue_db_array_size; + struct amdgpu_bo *hung_queue_db_array_gpu_obj; + uint64_t hung_queue_db_array_gpu_addr; + void *hung_queue_db_array_cpu_addr; }; struct amdgpu_mes_gang { @@ -280,6 +285,18 @@ struct mes_reset_queue_input { bool is_kq; }; +struct mes_detect_and_reset_queue_input { + uint32_t queue_type; + bool detect_only; +}; + +struct mes_inv_tlbs_pasid_input { + uint32_t xcc_id; + uint16_t pasid; + uint8_t hub_id; + uint8_t flush_type; +}; + enum mes_misc_opcode { MES_MISC_OP_WRITE_REG, MES_MISC_OP_READ_REG, @@ -367,6 +384,13 @@ struct amdgpu_mes_funcs { int (*reset_hw_queue)(struct amdgpu_mes *mes, struct mes_reset_queue_input *input); + + int (*detect_and_reset_hung_queues)(struct amdgpu_mes *mes, + struct mes_detect_and_reset_queue_input *input); + + + int (*invalidate_tlbs_pasid)(struct amdgpu_mes *mes, + struct mes_inv_tlbs_pasid_input *input); }; #define amdgpu_mes_kiq_hw_init(adev) (adev)->mes.kiq_hw_init((adev)) @@ -390,6 +414,13 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev, unsigned int vmid, bool use_mmio); +int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev); +int amdgpu_mes_detect_and_reset_hung_queues(struct amdgpu_device *adev, + int queue_type, + bool detect_only, + unsigned int *hung_db_num, + u32 *hung_db_array); + uint32_t amdgpu_mes_rreg(struct amdgpu_device *adev, uint32_t reg); int amdgpu_mes_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t val); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 6da4f946cac0..20460cfd09bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -496,8 +496,6 @@ struct amdgpu_crtc { struct drm_connector *connector; /* for dpm */ u32 line_time; - u32 wm_low; - u32 wm_high; u32 lb_vblank_lead_lines; struct drm_display_mode hw_mode; /* for virtual dce */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.c index e56ba93a8df6..a974265837f0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.c @@ -55,7 +55,8 @@ u64 amdgpu_nbio_get_pcie_replay_count(struct amdgpu_device *adev) bool amdgpu_nbio_is_replay_cnt_supported(struct amdgpu_device *adev) { - if (amdgpu_sriov_vf(adev) || !adev->asic_funcs->get_pcie_replay_count || + if (amdgpu_sriov_vf(adev) || !adev->asic_funcs || + !adev->asic_funcs->get_pcie_replay_count || (!adev->nbio.funcs || !adev->nbio.funcs->get_pcie_replay_count)) return false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 122a88294883..d18bade9c98f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1313,7 +1313,8 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) if (r) goto out; - r = amdgpu_fill_buffer(abo, 0, &bo->base._resv, &fence, true); + r = amdgpu_fill_buffer(abo, 0, &bo->base._resv, &fence, true, + AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE); if (WARN_ON(r)) goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index c316920f3450..87523fcd4386 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -69,7 +69,7 @@ struct amdgpu_bo_va_mapping { uint64_t last; uint64_t __subtree_last; uint64_t offset; - uint64_t flags; + uint32_t flags; }; /* User space allocated BO in a VM */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 75911600d504..3696f48c233b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -448,7 +448,7 @@ static int psp_sw_init(struct amdgpu_ip_block *ip_block) psp->cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL); if (!psp->cmd) { dev_err(adev->dev, "Failed to allocate memory to command buffer!\n"); - ret = -ENOMEM; + return -ENOMEM; } adev->psp.xgmi_context.supports_extended_data = @@ -506,8 +506,7 @@ static int psp_sw_init(struct amdgpu_ip_block *ip_block) } ret = amdgpu_bo_create_kernel(adev, PSP_1_MEG, PSP_1_MEG, - (amdgpu_sriov_vf(adev) || adev->debug_use_vram_fw_buf) ? - AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, + AMDGPU_GEM_DOMAIN_VRAM, &psp->fw_pri_bo, &psp->fw_pri_mc_addr, &psp->fw_pri_buf); @@ -666,6 +665,10 @@ static const char *psp_gfx_cmd_name(enum psp_gfx_cmd_id cmd_id) return "FB_FW_RESERV_ADDR"; case GFX_CMD_ID_FB_FW_RESERV_EXT_ADDR: return "FB_FW_RESERV_EXT_ADDR"; + case GFX_CMD_ID_SRIOV_SPATIAL_PART: + return "SPATIAL_PARTITION"; + case GFX_CMD_ID_FB_NPS_MODE: + return "NPS_MODE_CHANGE"; default: return "UNKNOWN CMD"; } @@ -877,9 +880,7 @@ static int psp_tmr_init(struct psp_context *psp) pptr = amdgpu_sriov_vf(psp->adev) ? &tmr_buf : NULL; ret = amdgpu_bo_create_kernel(psp->adev, tmr_size, PSP_TMR_ALIGNMENT, - AMDGPU_HAS_VRAM(psp->adev) ? - AMDGPU_GEM_DOMAIN_VRAM : - AMDGPU_GEM_DOMAIN_GTT, + AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM, &psp->tmr_bo, &psp->tmr_mc_addr, pptr); } @@ -1039,15 +1040,28 @@ int psp_update_fw_reservation(struct psp_context *psp) { int ret; uint64_t reserv_addr, reserv_addr_ext; - uint32_t reserv_size, reserv_size_ext; + uint32_t reserv_size, reserv_size_ext, mp0_ip_ver; struct amdgpu_device *adev = psp->adev; + mp0_ip_ver = amdgpu_ip_version(adev, MP0_HWIP, 0); + if (amdgpu_sriov_vf(psp->adev)) return 0; - if ((amdgpu_ip_version(adev, MP0_HWIP, 0) != IP_VERSION(14, 0, 2)) && - (amdgpu_ip_version(adev, MP0_HWIP, 0) != IP_VERSION(14, 0, 3))) + switch (mp0_ip_ver) { + case IP_VERSION(14, 0, 2): + if (adev->psp.sos.fw_version < 0x3b0e0d) + return 0; + break; + + case IP_VERSION(14, 0, 3): + if (adev->psp.sos.fw_version < 0x3a0e14) + return 0; + break; + + default: return 0; + } ret = psp_get_fw_reservation_info(psp, GFX_CMD_ID_FB_FW_RESERV_ADDR, &reserv_addr, &reserv_size); if (ret) @@ -4255,8 +4269,8 @@ static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj, static const struct bin_attribute psp_vbflash_bin_attr = { .attr = {.name = "psp_vbflash", .mode = 0660}, .size = 0, - .write_new = amdgpu_psp_vbflash_write, - .read_new = amdgpu_psp_vbflash_read, + .write = amdgpu_psp_vbflash_write, + .read = amdgpu_psp_vbflash_read, }; /** @@ -4319,7 +4333,7 @@ static umode_t amdgpu_bin_flash_attr_is_visible(struct kobject *kobj, const struct attribute_group amdgpu_flash_attr_group = { .attrs = flash_attrs, - .bin_attrs_new = bin_flash_attrs, + .bin_attrs = bin_flash_attrs, .is_bin_visible = amdgpu_bin_flash_attr_is_visible, .is_visible = amdgpu_flash_attr_is_visible, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index a0a14370745e..7fe5b1940df8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -122,12 +122,15 @@ const char *get_ras_block_str(struct ras_common_if *ras_block) /* typical ECC bad page rate is 1 bad page per 100MB VRAM */ #define RAS_BAD_PAGE_COVER (100 * 1024 * 1024ULL) -#define MAX_UMC_POISON_POLLING_TIME_ASYNC 300 //ms +#define MAX_UMC_POISON_POLLING_TIME_ASYNC 10 #define AMDGPU_RAS_RETIRE_PAGE_INTERVAL 100 //ms #define MAX_FLUSH_RETIRE_DWORK_TIMES 100 +#define BYPASS_ALLOCATED_ADDRESS 0x0 +#define BYPASS_INITIALIZATION_ADDRESS 0x1 + enum amdgpu_ras_retire_page_reservation { AMDGPU_RAS_RETIRE_PAGE_RESERVED, AMDGPU_RAS_RETIRE_PAGE_PENDING, @@ -136,10 +139,14 @@ enum amdgpu_ras_retire_page_reservation { atomic_t amdgpu_ras_in_intr = ATOMIC_INIT(0); -static bool amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, +static int amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, uint64_t addr); -static bool amdgpu_ras_check_bad_page(struct amdgpu_device *adev, +static int amdgpu_ras_check_bad_page(struct amdgpu_device *adev, uint64_t addr); + +static void amdgpu_ras_critical_region_init(struct amdgpu_device *adev); +static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev); + #ifdef CONFIG_X86_MCE_AMD static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev); struct mce_notifier_adev_list { @@ -169,18 +176,16 @@ static int amdgpu_reserve_page_direct(struct amdgpu_device *adev, uint64_t addre struct eeprom_table_record err_rec; int ret; - if ((address >= adev->gmc.mc_vram_size) || - (address >= RAS_UMC_INJECT_ADDR_LIMIT)) { + ret = amdgpu_ras_check_bad_page(adev, address); + if (ret == -EINVAL) { dev_warn(adev->dev, - "RAS WARN: input address 0x%llx is invalid.\n", - address); + "RAS WARN: input address 0x%llx is invalid.\n", + address); return -EINVAL; - } - - if (amdgpu_ras_check_bad_page(adev, address)) { + } else if (ret == 1) { dev_warn(adev->dev, - "RAS WARN: 0x%llx has already been marked as bad page!\n", - address); + "RAS WARN: 0x%llx has already been marked as bad page!\n", + address); return 0; } @@ -207,6 +212,49 @@ static int amdgpu_reserve_page_direct(struct amdgpu_device *adev, uint64_t addre return 0; } +static int amdgpu_check_address_validity(struct amdgpu_device *adev, + uint64_t address, uint64_t flags) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + struct amdgpu_vram_block_info blk_info; + uint64_t page_pfns[32] = {0}; + int i, ret, count; + + if (amdgpu_ip_version(adev, UMC_HWIP, 0) < IP_VERSION(12, 0, 0)) + return 0; + + if ((address >= adev->gmc.mc_vram_size) || + (address >= RAS_UMC_INJECT_ADDR_LIMIT)) + return -EFAULT; + + count = amdgpu_umc_lookup_bad_pages_in_a_row(adev, + address, page_pfns, ARRAY_SIZE(page_pfns)); + if (count <= 0) + return -EPERM; + + for (i = 0; i < count; i++) { + memset(&blk_info, 0, sizeof(blk_info)); + ret = amdgpu_vram_mgr_query_address_block_info(&adev->mman.vram_mgr, + page_pfns[i] << AMDGPU_GPU_PAGE_SHIFT, &blk_info); + if (!ret) { + /* The input address that needs to be checked is allocated by + * current calling process, so it is necessary to exclude + * the calling process. + */ + if ((flags == BYPASS_ALLOCATED_ADDRESS) && + ((blk_info.task.pid != task_pid_nr(current)) || + strncmp(blk_info.task.comm, current->comm, TASK_COMM_LEN))) + return -EACCES; + else if ((flags == BYPASS_INITIALIZATION_ADDRESS) && + (blk_info.task.pid == con->init_task_pid) && + !strncmp(blk_info.task.comm, con->init_task_comm, TASK_COMM_LEN)) + return -EACCES; + } + } + + return 0; +} + static ssize_t amdgpu_ras_debugfs_read(struct file *f, char __user *buf, size_t size, loff_t *pos) { @@ -297,6 +345,8 @@ static int amdgpu_ras_debugfs_ctrl_parse_data(struct file *f, op = 2; else if (strstr(str, "retire_page") != NULL) op = 3; + else if (strstr(str, "check_address") != NULL) + op = 4; else if (str[0] && str[1] && str[2] && str[3]) /* ascii string, but commands are not matched. */ return -EINVAL; @@ -310,6 +360,15 @@ static int amdgpu_ras_debugfs_ctrl_parse_data(struct file *f, data->op = op; data->inject.address = address; + return 0; + } else if (op == 4) { + if (sscanf(str, "%*s 0x%llx 0x%llx", &address, &value) != 2 && + sscanf(str, "%*s %llu %llu", &address, &value) != 2) + return -EINVAL; + + data->op = op; + data->inject.address = address; + data->inject.value = value; return 0; } @@ -500,6 +559,9 @@ static ssize_t amdgpu_ras_debugfs_ctrl_write(struct file *f, return size; else return ret; + } else if (data.op == 4) { + ret = amdgpu_check_address_validity(adev, data.inject.address, data.inject.value); + return ret ? ret : size; } if (!amdgpu_ras_is_supported(adev, data.head.block)) @@ -513,22 +575,16 @@ static ssize_t amdgpu_ras_debugfs_ctrl_write(struct file *f, ret = amdgpu_ras_feature_enable(adev, &data.head, 1); break; case 2: - if ((data.inject.address >= adev->gmc.mc_vram_size && - adev->gmc.mc_vram_size) || - (data.inject.address >= RAS_UMC_INJECT_ADDR_LIMIT)) { - dev_warn(adev->dev, "RAS WARN: input address " - "0x%llx is invalid.", - data.inject.address); - ret = -EINVAL; - break; - } - /* umc ce/ue error injection for a bad page is not allowed */ - if ((data.head.block == AMDGPU_RAS_BLOCK__UMC) && - amdgpu_ras_check_bad_page(adev, data.inject.address)) { - dev_warn(adev->dev, "RAS WARN: inject: 0x%llx has " - "already been marked as bad!\n", - data.inject.address); + if (data.head.block == AMDGPU_RAS_BLOCK__UMC) + ret = amdgpu_ras_check_bad_page(adev, data.inject.address); + if (ret == -EINVAL) { + dev_warn(adev->dev, "RAS WARN: input address 0x%llx is invalid.", + data.inject.address); + break; + } else if (ret == 1) { + dev_warn(adev->dev, "RAS WARN: inject: 0x%llx has already been marked as bad!\n", + data.inject.address); break; } @@ -2127,7 +2183,7 @@ static int amdgpu_ras_fs_init(struct amdgpu_device *adev) con->badpages_attr = bin_attr_gpu_vram_bad_pages; sysfs_bin_attr_init(&con->badpages_attr); bin_attrs[0] = &con->badpages_attr; - group.bin_attrs_new = bin_attrs; + group.bin_attrs = bin_attrs; } r = sysfs_create_group(&adev->dev->kobj, &group); @@ -2566,18 +2622,26 @@ static int amdgpu_ras_badpages_read(struct amdgpu_device *adev, goto out; } - *bps = kmalloc(sizeof(struct ras_badpage) * data->count, GFP_KERNEL); + *bps = kmalloc_array(data->count, sizeof(struct ras_badpage), GFP_KERNEL); if (!*bps) { ret = -ENOMEM; goto out; } for (; i < data->count; i++) { + if (!data->bps[i].ts) + continue; + (*bps)[i] = (struct ras_badpage){ .bp = data->bps[i].retired_page, .size = AMDGPU_GPU_PAGE_SIZE, .flags = AMDGPU_RAS_RETIRE_PAGE_RESERVED, }; + + if (amdgpu_ras_check_critical_address(adev, + data->bps[i].retired_page << AMDGPU_GPU_PAGE_SHIFT)) + continue; + status = amdgpu_vram_mgr_query_page_status(&adev->mman.vram_mgr, data->bps[i].retired_page << AMDGPU_GPU_PAGE_SHIFT); if (status == -EBUSY) @@ -2586,7 +2650,7 @@ static int amdgpu_ras_badpages_read(struct amdgpu_device *adev, (*bps)[i].flags = AMDGPU_RAS_RETIRE_PAGE_FAULT; } - *count = data->count; + *count = con->bad_page_num; out: mutex_unlock(&con->recovery_lock); return ret; @@ -2722,7 +2786,7 @@ static int amdgpu_ras_realloc_eh_data_space(struct amdgpu_device *adev, unsigned int old_space = data->count + data->space_left; unsigned int new_space = old_space + pages; unsigned int align_space = ALIGN(new_space, 512); - void *bps = kmalloc(align_space * sizeof(*data->bps), GFP_KERNEL); + void *bps = kmalloc_array(align_space, sizeof(*data->bps), GFP_KERNEL); if (!bps) { return -ENOMEM; @@ -2814,8 +2878,11 @@ static int __amdgpu_ras_restore_bad_pages(struct amdgpu_device *adev, for (j = 0; j < count; j++) { if (amdgpu_ras_check_bad_page_unlock(con, - bps[j].retired_page << AMDGPU_GPU_PAGE_SHIFT)) + bps[j].retired_page << AMDGPU_GPU_PAGE_SHIFT)) { + data->count++; + data->space_left--; continue; + } if (!data->space_left && amdgpu_ras_realloc_eh_data_space(adev, data, 256)) { @@ -2828,6 +2895,7 @@ static int __amdgpu_ras_restore_bad_pages(struct amdgpu_device *adev, sizeof(struct eeprom_table_record)); data->count++; data->space_left--; + con->bad_page_num++; } return 0; @@ -2974,7 +3042,7 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev, ret = __amdgpu_ras_convert_rec_array_from_rom(adev, &bps[i], &err_data, nps); if (ret) - control->ras_num_bad_pages -= adev->umc.retire_unit; + con->bad_page_num -= adev->umc.retire_unit; i += (adev->umc.retire_unit - 1); } else { break; @@ -2988,8 +3056,10 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev, ret = __amdgpu_ras_convert_rec_from_rom(adev, &bps[i], &err_data, nps); if (ret) - control->ras_num_bad_pages -= adev->umc.retire_unit; + con->bad_page_num -= adev->umc.retire_unit; } + + con->eh_data->count_saved = con->eh_data->count; } else { ret = __amdgpu_ras_restore_bad_pages(adev, bps, pages); } @@ -3012,7 +3082,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_err_handler_data *data; struct amdgpu_ras_eeprom_control *control; - int save_count, unit_num, bad_page_num, i; + int save_count, unit_num, i; if (!con || !con->eh_data) { if (new_cnt) @@ -3033,27 +3103,26 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, mutex_lock(&con->recovery_lock); control = &con->eeprom_control; data = con->eh_data; - bad_page_num = control->ras_num_bad_pages; - save_count = data->count - bad_page_num; + unit_num = data->count / adev->umc.retire_unit - control->ras_num_recs; + save_count = con->bad_page_num - control->ras_num_bad_pages; mutex_unlock(&con->recovery_lock); - unit_num = save_count / adev->umc.retire_unit; if (new_cnt) *new_cnt = unit_num; /* only new entries are saved */ - if (save_count > 0) { + if (unit_num > 0) { /*old asics only save pa to eeprom like before*/ if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) { if (amdgpu_ras_eeprom_append(control, - &data->bps[bad_page_num], save_count)) { + &data->bps[data->count_saved], unit_num)) { dev_err(adev->dev, "Failed to save EEPROM table data!"); return -EIO; } } else { for (i = 0; i < unit_num; i++) { if (amdgpu_ras_eeprom_append(control, - &data->bps[bad_page_num + + &data->bps[data->count_saved + i * adev->umc.retire_unit], 1)) { dev_err(adev->dev, "Failed to save EEPROM table data!"); return -EIO; @@ -3062,6 +3131,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, } dev_info(adev->dev, "Saved %d pages to EEPROM table.\n", save_count); + data->count_saved = data->count; } return 0; @@ -3116,17 +3186,17 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev) } } + ret = amdgpu_ras_add_bad_pages(adev, bps, control->ras_num_recs, true); + if (ret) + goto out; + ret = amdgpu_ras_eeprom_check(control); if (ret) goto out; /* HW not usable */ - if (amdgpu_ras_is_rma(adev)) { + if (amdgpu_ras_is_rma(adev)) ret = -EHWPOISON; - goto out; - } - - ret = amdgpu_ras_add_bad_pages(adev, bps, control->ras_num_recs, true); } out: @@ -3134,18 +3204,24 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev) return ret; } -static bool amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, +static int amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, uint64_t addr) { struct ras_err_handler_data *data = con->eh_data; + struct amdgpu_device *adev = con->adev; int i; + if ((addr >= adev->gmc.mc_vram_size && + adev->gmc.mc_vram_size) || + (addr >= RAS_UMC_INJECT_ADDR_LIMIT)) + return -EINVAL; + addr >>= AMDGPU_GPU_PAGE_SHIFT; for (i = 0; i < data->count; i++) if (addr == data->bps[i].retired_page) - return true; + return 1; - return false; + return 0; } /* @@ -3153,11 +3229,11 @@ static bool amdgpu_ras_check_bad_page_unlock(struct amdgpu_ras *con, * * Note: this check is only for umc block */ -static bool amdgpu_ras_check_bad_page(struct amdgpu_device *adev, +static int amdgpu_ras_check_bad_page(struct amdgpu_device *adev, uint64_t addr) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - bool ret = false; + int ret = 0; if (!con || !con->eh_data) return ret; @@ -3241,7 +3317,7 @@ static void amdgpu_ras_ecc_log_init(struct ras_ecc_log_info *ecc_log) INIT_RADIX_TREE(&ecc_log->de_page_tree, GFP_KERNEL); ecc_log->de_queried_count = 0; - ecc_log->prev_de_queried_count = 0; + ecc_log->consumption_q_count = 0; } static void amdgpu_ras_ecc_log_fini(struct ras_ecc_log_info *ecc_log) @@ -3261,7 +3337,7 @@ static void amdgpu_ras_ecc_log_fini(struct ras_ecc_log_info *ecc_log) mutex_destroy(&ecc_log->lock); ecc_log->de_queried_count = 0; - ecc_log->prev_de_queried_count = 0; + ecc_log->consumption_q_count = 0; } static bool amdgpu_ras_schedule_retirement_dwork(struct amdgpu_ras *con, @@ -3287,7 +3363,6 @@ static void amdgpu_ras_do_page_retirement(struct work_struct *work) page_retirement_dwork.work); struct amdgpu_device *adev = con->adev; struct ras_err_data err_data; - unsigned long err_cnt; /* If gpu reset is ongoing, delay retiring the bad pages */ if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) { @@ -3299,13 +3374,9 @@ static void amdgpu_ras_do_page_retirement(struct work_struct *work) amdgpu_ras_error_data_init(&err_data); amdgpu_umc_handle_bad_pages(adev, &err_data); - err_cnt = err_data.err_addr_cnt; amdgpu_ras_error_data_fini(&err_data); - if (err_cnt && amdgpu_ras_is_rma(adev)) - amdgpu_ras_reset_gpu(adev); - amdgpu_ras_schedule_retirement_dwork(con, AMDGPU_RAS_RETIRE_PAGE_INTERVAL); } @@ -3316,49 +3387,39 @@ static int amdgpu_ras_poison_creation_handler(struct amdgpu_device *adev, int ret = 0; struct ras_ecc_log_info *ecc_log; struct ras_query_if info; - uint32_t timeout = 0; + u32 timeout = MAX_UMC_POISON_POLLING_TIME_ASYNC; struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); - uint64_t de_queried_count; - uint32_t new_detect_count, total_detect_count; - uint32_t need_query_count = poison_creation_count; + u64 de_queried_count; + u64 consumption_q_count; enum ras_event_type type = RAS_EVENT_TYPE_POISON_CREATION; memset(&info, 0, sizeof(info)); info.head.block = AMDGPU_RAS_BLOCK__UMC; ecc_log = &ras->umc_ecc_log; - total_detect_count = 0; + ecc_log->de_queried_count = 0; + ecc_log->consumption_q_count = 0; + do { ret = amdgpu_ras_query_error_status_with_event(adev, &info, type); if (ret) return ret; de_queried_count = ecc_log->de_queried_count; - if (de_queried_count > ecc_log->prev_de_queried_count) { - new_detect_count = de_queried_count - ecc_log->prev_de_queried_count; - ecc_log->prev_de_queried_count = de_queried_count; - timeout = 0; - } else { - new_detect_count = 0; - } + consumption_q_count = ecc_log->consumption_q_count; - if (new_detect_count) { - total_detect_count += new_detect_count; - } else { - if (!timeout && need_query_count) - timeout = MAX_UMC_POISON_POLLING_TIME_ASYNC; + if (de_queried_count && consumption_q_count) + break; - if (timeout) { - if (!--timeout) - break; - msleep(1); - } - } - } while (total_detect_count < need_query_count); + msleep(100); + } while (--timeout); - if (total_detect_count) + if (de_queried_count) schedule_delayed_work(&ras->page_retirement_dwork, 0); + if (amdgpu_ras_is_rma(adev) && atomic_cmpxchg(&ras->rma_in_recovery, 0, 1) == 0) + amdgpu_ras_reset_gpu(adev); + return 0; } @@ -3394,6 +3455,12 @@ static int amdgpu_ras_poison_consumption_handler(struct amdgpu_device *adev, reset_flags |= msg.reset; } + /* + * Try to ensure poison creation handler is completed first + * to set rma if bad page exceed threshold. + */ + flush_delayed_work(&con->page_retirement_dwork); + /* for RMA, amdgpu_ras_poison_creation_handler will trigger gpu reset */ if (reset_flags && !amdgpu_ras_is_rma(adev)) { if (reset_flags & AMDGPU_RAS_GPU_RESET_MODE1_RESET) @@ -3403,8 +3470,6 @@ static int amdgpu_ras_poison_consumption_handler(struct amdgpu_device *adev, else reset = reset_flags; - flush_delayed_work(&con->page_retirement_dwork); - con->gpu_reset_flags |= reset; amdgpu_ras_reset_gpu(adev); @@ -3434,6 +3499,7 @@ static int amdgpu_ras_page_retirement_thread(void *param) if (kthread_should_stop()) break; + mutex_lock(&con->poison_lock); gpu_reset = 0; do { @@ -3446,7 +3512,8 @@ static int amdgpu_ras_page_retirement_thread(void *param) atomic_sub(poison_creation_count, &con->poison_creation_count); atomic_sub(poison_creation_count, &con->page_retirement_req_cnt); } - } while (atomic_read(&con->poison_creation_count)); + } while (atomic_read(&con->poison_creation_count) && + !atomic_read(&con->poison_consumption_count)); if (ret != -EIO) { msg_count = kfifo_len(&con->poison_fifo); @@ -3463,6 +3530,7 @@ static int amdgpu_ras_page_retirement_thread(void *param) /* gpu mode-1 reset is ongoing or just completed ras mode-1 reset */ /* Clear poison creation request */ atomic_set(&con->poison_creation_count, 0); + atomic_set(&con->poison_consumption_count, 0); /* Clear poison fifo */ amdgpu_ras_clear_poison_fifo(adev); @@ -3487,9 +3555,12 @@ static int amdgpu_ras_page_retirement_thread(void *param) atomic_sub(msg_count, &con->page_retirement_req_cnt); } + atomic_set(&con->poison_consumption_count, 0); + /* Wake up work to save bad pages to eeprom */ schedule_delayed_work(&con->page_retirement_dwork, 0); } + mutex_unlock(&con->poison_lock); } return 0; @@ -3570,8 +3641,10 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info) } mutex_init(&con->recovery_lock); + mutex_init(&con->poison_lock); INIT_WORK(&con->recovery_work, amdgpu_ras_do_recovery); atomic_set(&con->in_recovery, 0); + atomic_set(&con->rma_in_recovery, 0); con->eeprom_control.bad_channel_bitmap = 0; max_eeprom_records_count = amdgpu_ras_eeprom_max_record_count(&con->eeprom_control); @@ -3589,6 +3662,7 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info) init_waitqueue_head(&con->page_retirement_wq); atomic_set(&con->page_retirement_req_cnt, 0); atomic_set(&con->poison_creation_count, 0); + atomic_set(&con->poison_consumption_count, 0); con->page_retirement_thread = kthread_run(amdgpu_ras_page_retirement_thread, adev, "umc_page_retirement"); if (IS_ERR(con->page_retirement_thread)) { @@ -3661,6 +3735,8 @@ static int amdgpu_ras_recovery_fini(struct amdgpu_device *adev) kfree(data); mutex_unlock(&con->recovery_lock); + amdgpu_ras_critical_region_init(adev); + return 0; } /* recovery end */ @@ -4087,6 +4163,12 @@ int amdgpu_ras_init(struct amdgpu_device *adev) goto release_con; } + con->init_task_pid = task_pid_nr(current); + get_task_comm(con->init_task_comm, current); + + mutex_init(&con->critical_region_lock); + INIT_LIST_HEAD(&con->critical_region_head); + dev_info(adev->dev, "RAS INFO: ras initialized successfully, " "hardware ability[%x] ras_mask[%x]\n", adev->ras_hw_enabled, adev->ras_enabled); @@ -4366,6 +4448,9 @@ int amdgpu_ras_fini(struct amdgpu_device *adev) if (!adev->ras_enabled || !con) return 0; + amdgpu_ras_critical_region_fini(adev); + mutex_destroy(&con->critical_region_lock); + list_for_each_entry_safe(ras_node, tmp, &adev->ras_list, node) { if (ras_node->ras_obj) { obj = ras_node->ras_obj; @@ -5274,6 +5359,9 @@ int amdgpu_ras_reserve_page(struct amdgpu_device *adev, uint64_t pfn) uint64_t start = pfn << AMDGPU_GPU_PAGE_SHIFT; int ret = 0; + if (amdgpu_ras_check_critical_address(adev, start)) + return 0; + mutex_lock(&con->page_rsv_lock); ret = amdgpu_vram_mgr_query_page_status(mgr, start); if (ret == -ENOENT) @@ -5310,3 +5398,80 @@ bool amdgpu_ras_is_rma(struct amdgpu_device *adev) return con->is_rma; } + +int amdgpu_ras_add_critical_region(struct amdgpu_device *adev, + struct amdgpu_bo *bo) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + struct amdgpu_vram_mgr_resource *vres; + struct ras_critical_region *region; + struct drm_buddy_block *block; + int ret = 0; + + if (!bo || !bo->tbo.resource) + return -EINVAL; + + vres = to_amdgpu_vram_mgr_resource(bo->tbo.resource); + + mutex_lock(&con->critical_region_lock); + + /* Check if the bo had been recorded */ + list_for_each_entry(region, &con->critical_region_head, node) + if (region->bo == bo) + goto out; + + /* Record new critical amdgpu bo */ + list_for_each_entry(block, &vres->blocks, link) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) { + ret = -ENOMEM; + goto out; + } + region->bo = bo; + region->start = amdgpu_vram_mgr_block_start(block); + region->size = amdgpu_vram_mgr_block_size(block); + list_add_tail(®ion->node, &con->critical_region_head); + } + +out: + mutex_unlock(&con->critical_region_lock); + + return ret; +} + +static void amdgpu_ras_critical_region_init(struct amdgpu_device *adev) +{ + amdgpu_ras_add_critical_region(adev, adev->mman.fw_reserved_memory); +} + +static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + struct ras_critical_region *region, *tmp; + + mutex_lock(&con->critical_region_lock); + list_for_each_entry_safe(region, tmp, &con->critical_region_head, node) { + list_del(®ion->node); + kfree(region); + } + mutex_unlock(&con->critical_region_lock); +} + +bool amdgpu_ras_check_critical_address(struct amdgpu_device *adev, uint64_t addr) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + struct ras_critical_region *region; + bool ret = false; + + mutex_lock(&con->critical_region_lock); + list_for_each_entry(region, &con->critical_region_head, node) { + if ((region->start <= addr) && + (addr < (region->start + region->size))) { + ret = true; + break; + } + } + mutex_unlock(&con->critical_region_lock); + + return ret; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h index 927d6bff734a..6cf0dfd38be8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h @@ -492,8 +492,15 @@ struct ras_ecc_err { struct ras_ecc_log_info { struct mutex lock; struct radix_tree_root de_page_tree; - uint64_t de_queried_count; - uint64_t prev_de_queried_count; + uint64_t de_queried_count; + uint64_t consumption_q_count; +}; + +struct ras_critical_region { + struct list_head node; + struct amdgpu_bo *bo; + uint64_t start; + uint64_t size; }; struct amdgpu_ras { @@ -515,6 +522,7 @@ struct amdgpu_ras { /* gpu recovery */ struct work_struct recovery_work; atomic_t in_recovery; + atomic_t rma_in_recovery; struct amdgpu_device *adev; /* error handler data */ struct ras_err_handler_data *eh_data; @@ -557,6 +565,7 @@ struct amdgpu_ras { struct mutex page_retirement_lock; atomic_t page_retirement_req_cnt; atomic_t poison_creation_count; + atomic_t poison_consumption_count; struct mutex page_rsv_lock; DECLARE_KFIFO(poison_fifo, struct ras_poison_msg, 128); struct ras_ecc_log_info umc_ecc_log; @@ -570,6 +579,17 @@ struct amdgpu_ras { struct ras_event_manager *event_mgr; uint64_t reserved_pages_in_bytes; + + pid_t init_task_pid; + char init_task_comm[TASK_COMM_LEN]; + + int bad_page_num; + + struct list_head critical_region_head; + struct mutex critical_region_lock; + + /* Protect poison injection */ + struct mutex poison_lock; }; struct ras_fs_data { @@ -608,6 +628,7 @@ struct ras_err_handler_data { struct eeprom_table_record *bps; /* the count of entries */ int count; + int count_saved; /* the space can place new entries */ int space_left; }; @@ -973,6 +994,9 @@ int amdgpu_ras_mark_ras_event_caller(struct amdgpu_device *adev, enum ras_event_ int amdgpu_ras_reserve_page(struct amdgpu_device *adev, uint64_t pfn); +int amdgpu_ras_add_critical_region(struct amdgpu_device *adev, struct amdgpu_bo *bo); +bool amdgpu_ras_check_critical_address(struct amdgpu_device *adev, uint64_t addr); + int amdgpu_ras_put_poison_req(struct amdgpu_device *adev, enum amdgpu_ras_block block, uint16_t pasid, pasid_notify pasid_fn, void *data, uint32_t reset); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index 9bda9ad13f88..3eb3fb55ccb0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -743,8 +743,7 @@ amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control, else control->ras_num_mca_recs += num; - control->ras_num_bad_pages = control->ras_num_pa_recs + - control->ras_num_mca_recs * adev->umc.retire_unit; + control->ras_num_bad_pages = con->bad_page_num; Out: kfree(buf); return res; @@ -766,6 +765,10 @@ amdgpu_ras_eeprom_update_header(struct amdgpu_ras_eeprom_control *control) dev_warn(adev->dev, "Saved bad pages %d reaches threshold value %d\n", control->ras_num_bad_pages, ras->bad_page_cnt_threshold); + + if (adev->cper.enabled && amdgpu_cper_generate_bp_threshold_record(adev)) + dev_warn(adev->dev, "fail to generate bad page threshold cper records\n"); + if ((amdgpu_bad_page_threshold != -1) && (amdgpu_bad_page_threshold != -2)) { control->tbl_hdr.header = RAS_TABLE_HDR_BAD; @@ -774,9 +777,10 @@ amdgpu_ras_eeprom_update_header(struct amdgpu_ras_eeprom_control *control) control->tbl_rai.health_percent = 0; } ras->is_rma = true; - /* ignore the -ENOTSUPP return value */ - amdgpu_dpm_send_rma_reason(adev); } + + /* ignore the -ENOTSUPP return value */ + amdgpu_dpm_send_rma_reason(adev); } if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1) @@ -1457,8 +1461,7 @@ int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control) if (!__get_eeprom_i2c_addr(adev, control)) return -EINVAL; - control->ras_num_bad_pages = control->ras_num_pa_recs + - control->ras_num_mca_recs * adev->umc.retire_unit; + control->ras_num_bad_pages = ras->bad_page_num; if (hdr->header == RAS_TABLE_HDR_VAL) { dev_dbg(adev->dev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c index dabfbdf6f1ce..28c4ad62f50e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c @@ -340,6 +340,9 @@ void amdgpu_reset_get_desc(struct amdgpu_reset_context *rst_ctxt, char *buf, case AMDGPU_RESET_SRC_USER: strscpy(buf, "user trigger", len); break; + case AMDGPU_RESET_SRC_USERQ: + strscpy(buf, "user queue trigger", len); + break; default: strscpy(buf, "unknown", len); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h index 4d9b9701139b..07b4d37f1db6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h @@ -43,6 +43,7 @@ enum AMDGPU_RESET_SRCS { AMDGPU_RESET_SRC_MES, AMDGPU_RESET_SRC_HWS, AMDGPU_RESET_SRC_USER, + AMDGPU_RESET_SRC_USERQ, }; struct amdgpu_reset_context { @@ -160,4 +161,16 @@ int amdgpu_reset_do_xgmi_reset_on_init( bool amdgpu_reset_in_recovery(struct amdgpu_device *adev); +static inline void amdgpu_reset_set_dpc_status(struct amdgpu_device *adev, + bool status) +{ + adev->pcie_reset_ctx.occurs_dpc = status; + adev->no_hw_access = status; +} + +static inline bool amdgpu_reset_in_dpc(struct amdgpu_device *adev) +{ + return adev->pcie_reset_ctx.occurs_dpc; +} + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 6379bb25bf5c..486c3646710c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -421,8 +421,6 @@ void amdgpu_ring_fini(struct amdgpu_ring *ring) dma_fence_put(ring->vmid_wait); ring->vmid_wait = NULL; ring->me = 0; - - ring->adev->rings[ring->idx] = NULL; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c index d45ebfb642ca..a0b479d5fff1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c @@ -67,9 +67,9 @@ static inline u64 amdgpu_seq64_get_va_base(struct amdgpu_device *adev) int amdgpu_seq64_map(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_bo_va **bo_va) { - u64 seq64_addr, va_flags; struct amdgpu_bo *bo; struct drm_exec exec; + u64 seq64_addr; int r; bo = adev->seq64.sbo; @@ -94,9 +94,9 @@ int amdgpu_seq64_map(struct amdgpu_device *adev, struct amdgpu_vm *vm, seq64_addr = amdgpu_seq64_get_va_base(adev) & AMDGPU_GMC_HOLE_MASK; - va_flags = amdgpu_gem_va_map_flags(adev, AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_MTYPE_UC); - r = amdgpu_vm_bo_map(adev, *bo_va, seq64_addr, 0, AMDGPU_VA_RESERVED_SEQ64_SIZE, - va_flags); + r = amdgpu_vm_bo_map(adev, *bo_va, seq64_addr, 0, + AMDGPU_VA_RESERVED_SEQ64_SIZE, + AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_MTYPE_UC); if (r) { DRM_ERROR("failed to do bo_map on userq sem, err=%d\n", r); amdgpu_vm_bo_del(adev, *bo_va); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 27ab4e754b2a..428265046815 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -226,7 +226,8 @@ static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo, r = amdgpu_job_alloc_with_ib(adev, &adev->mman.high_pr, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4 + num_bytes, - AMDGPU_IB_POOL_DELAYED, &job); + AMDGPU_IB_POOL_DELAYED, &job, + AMDGPU_KERNEL_JOB_ID_TTM_MAP_BUFFER); if (r) return r; @@ -406,7 +407,7 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, struct dma_fence *wipe_fence = NULL; r = amdgpu_fill_buffer(abo, 0, NULL, &wipe_fence, - false); + false, AMDGPU_KERNEL_JOB_ID_MOVE_BLIT); if (r) { goto error; } else if (wipe_fence) { @@ -1510,7 +1511,8 @@ static int amdgpu_ttm_access_memory_sdma(struct ttm_buffer_object *bo, r = amdgpu_job_alloc_with_ib(adev, &adev->mman.high_pr, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4, AMDGPU_IB_POOL_DELAYED, - &job); + &job, + AMDGPU_KERNEL_JOB_ID_TTM_ACCESS_MEMORY_SDMA); if (r) goto out; @@ -2167,7 +2169,7 @@ static int amdgpu_ttm_prepare_job(struct amdgpu_device *adev, struct dma_resv *resv, bool vm_needs_flush, struct amdgpu_job **job, - bool delayed) + bool delayed, u64 k_job_id) { enum amdgpu_ib_pool_type pool = direct_submit ? AMDGPU_IB_POOL_DIRECT : @@ -2177,7 +2179,7 @@ static int amdgpu_ttm_prepare_job(struct amdgpu_device *adev, &adev->mman.high_pr; r = amdgpu_job_alloc_with_ib(adev, entity, AMDGPU_FENCE_OWNER_UNDEFINED, - num_dw * 4, pool, job); + num_dw * 4, pool, job, k_job_id); if (r) return r; @@ -2217,7 +2219,8 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring, uint64_t src_offset, num_loops = DIV_ROUND_UP(byte_count, max_bytes); num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->copy_num_dw, 8); r = amdgpu_ttm_prepare_job(adev, direct_submit, num_dw, - resv, vm_needs_flush, &job, false); + resv, vm_needs_flush, &job, false, + AMDGPU_KERNEL_JOB_ID_TTM_COPY_BUFFER); if (r) return r; @@ -2252,7 +2255,8 @@ static int amdgpu_ttm_fill_mem(struct amdgpu_ring *ring, uint32_t src_data, uint64_t dst_addr, uint32_t byte_count, struct dma_resv *resv, struct dma_fence **fence, - bool vm_needs_flush, bool delayed) + bool vm_needs_flush, bool delayed, + u64 k_job_id) { struct amdgpu_device *adev = ring->adev; unsigned int num_loops, num_dw; @@ -2265,7 +2269,7 @@ static int amdgpu_ttm_fill_mem(struct amdgpu_ring *ring, uint32_t src_data, num_loops = DIV_ROUND_UP_ULL(byte_count, max_bytes); num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->fill_num_dw, 8); r = amdgpu_ttm_prepare_job(adev, false, num_dw, resv, vm_needs_flush, - &job, delayed); + &job, delayed, k_job_id); if (r) return r; @@ -2335,7 +2339,8 @@ int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo, goto err; r = amdgpu_ttm_fill_mem(ring, 0, addr, size, resv, - &next, true, true); + &next, true, true, + AMDGPU_KERNEL_JOB_ID_TTM_CLEAR_BUFFER); if (r) goto err; @@ -2354,7 +2359,8 @@ int amdgpu_fill_buffer(struct amdgpu_bo *bo, uint32_t src_data, struct dma_resv *resv, struct dma_fence **f, - bool delayed) + bool delayed, + u64 k_job_id) { struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring; @@ -2384,7 +2390,7 @@ int amdgpu_fill_buffer(struct amdgpu_bo *bo, goto error; r = amdgpu_ttm_fill_mem(ring, src_data, to, cur_size, resv, - &next, true, delayed); + &next, true, delayed, k_job_id); if (r) goto error; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 215c198e4aff..d82d107fdcc6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -155,6 +155,7 @@ int amdgpu_vram_mgr_reserve_range(struct amdgpu_vram_mgr *mgr, uint64_t start, uint64_t size); int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr, uint64_t start); +void amdgpu_vram_mgr_clear_reset_blocks(struct amdgpu_device *adev); bool amdgpu_res_cpu_visible(struct amdgpu_device *adev, struct ttm_resource *res); @@ -181,7 +182,8 @@ int amdgpu_fill_buffer(struct amdgpu_bo *bo, uint32_t src_data, struct dma_resv *resv, struct dma_fence **fence, - bool delayed); + bool delayed, + u64 k_job_id); int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo); void amdgpu_ttm_recover_gart(struct ttm_buffer_object *tbo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c index c92b8794aa73..2e039fb778ea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c @@ -252,6 +252,7 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev, block, pasid, pasid_fn, data, reset); if (!ret) { atomic_inc(&con->page_retirement_req_cnt); + atomic_inc(&con->poison_consumption_count); wake_up(&con->page_retirement_wq); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index c3ace8030530..467e8fa6cb8b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -364,7 +364,7 @@ static int amdgpu_mqd_info_read(struct seq_file *m, void *unused) return -EINVAL; } - seq_printf(m, "queue_type %d\n", queue->queue_type); + seq_printf(m, "queue_type: %d\n", queue->queue_type); seq_printf(m, "mqd_gpu_address: 0x%llx\n", amdgpu_bo_gpu_offset(queue->mqd.obj)); amdgpu_bo_unreserve(bo); @@ -471,6 +471,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (index == (uint64_t)-EINVAL) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); kfree(queue); + r = -EINVAL; goto unlock; } @@ -567,7 +568,6 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data, args->in.queue_size || args->in.rptr_va || args->in.wptr_va || - args->in.wptr_va || args->in.mqd || args->in.mqd_size) return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index b1ca91b7cda4..1bd84f4cce78 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -78,6 +78,12 @@ struct amdgpu_userq_funcs { struct amdgpu_usermode_queue *queue); int (*map)(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue); + int (*preempt)(struct amdgpu_userq_mgr *uq_mgr, + struct amdgpu_usermode_queue *queue); + int (*restore)(struct amdgpu_userq_mgr *uq_mgr, + struct amdgpu_usermode_queue *queue); + int (*detect_and_reset)(struct amdgpu_device *adev, + int queue_type); }; /* Usermode queues for gfx */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index c2a983ff23c9..95e91d1dc58a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -67,6 +67,14 @@ static u64 amdgpu_userq_fence_read(struct amdgpu_userq_fence_driver *fence_drv) return le64_to_cpu(*fence_drv->cpu_addr); } +static void +amdgpu_userq_fence_write(struct amdgpu_userq_fence_driver *fence_drv, + u64 seq) +{ + if (fence_drv->cpu_addr) + *fence_drv->cpu_addr = cpu_to_le64(seq); +} + int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev, struct amdgpu_usermode_queue *userq) { @@ -408,6 +416,40 @@ 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) +{ + struct amdgpu_userq_fence_driver *fence_drv = fence->fence_drv; + unsigned long flags; + struct dma_fence *f; + + spin_lock_irqsave(&fence_drv->fence_list_lock, flags); + + f = rcu_dereference_protected(&fence->base, + lockdep_is_held(&fence_drv->fence_list_lock)); + if (f && !dma_fence_is_signaled_locked(f)) + dma_fence_set_error(f, error); + spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); +} + +void +amdgpu_userq_fence_driver_force_completion(struct amdgpu_usermode_queue *userq) +{ + struct dma_fence *f = userq->last_fence; + + if (f) { + struct amdgpu_userq_fence *fence = to_amdgpu_userq_fence(f); + struct amdgpu_userq_fence_driver *fence_drv = fence->fence_drv; + u64 wptr = fence->base.seqno; + + amdgpu_userq_fence_driver_set_error(fence, -ECANCELED); + amdgpu_userq_fence_write(fence_drv, wptr); + amdgpu_userq_fence_driver_process(fence_drv); + + } +} + int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h index 97a125ab8a78..d76add2afc77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h @@ -67,6 +67,7 @@ int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev, struct amdgpu_usermode_queue *userq); void amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq); void 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, struct drm_file *filp); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 74758b5ffc6c..5c38f0d30c87 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -1136,7 +1136,8 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo, r = amdgpu_job_alloc_with_ib(ring->adev, &adev->uvd.entity, AMDGPU_FENCE_OWNER_UNDEFINED, 64, direct ? AMDGPU_IB_POOL_DIRECT : - AMDGPU_IB_POOL_DELAYED, &job); + AMDGPU_IB_POOL_DELAYED, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index b9060bcd4806..ce318f5de047 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -449,7 +449,7 @@ static int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, r = amdgpu_job_alloc_with_ib(ring->adev, &ring->adev->vce.entity, AMDGPU_FENCE_OWNER_UNDEFINED, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job); + &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -540,7 +540,8 @@ static int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, AMDGPU_FENCE_OWNER_UNDEFINED, ib_size_dw * 4, direct ? AMDGPU_IB_POOL_DIRECT : - AMDGPU_IB_POOL_DELAYED, &job); + AMDGPU_IB_POOL_DELAYED, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index f1f67521c29c..595f0df17bcc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -92,6 +92,7 @@ MODULE_FIRMWARE(FIRMWARE_VCN5_0_0); MODULE_FIRMWARE(FIRMWARE_VCN5_0_1); static void amdgpu_vcn_idle_work_handler(struct work_struct *work); +static void amdgpu_vcn_reg_dump_fini(struct amdgpu_device *adev); int amdgpu_vcn_early_init(struct amdgpu_device *adev, int i) { @@ -184,16 +185,16 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev, int i) dec_ver = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xf; vep = (le32_to_cpu(hdr->ucode_version) >> 28) & 0xf; dev_info(adev->dev, - "Found VCN firmware Version ENC: %u.%u DEC: %u VEP: %u Revision: %u\n", - enc_major, enc_minor, dec_ver, vep, fw_rev); + "[VCN instance %d] Found VCN firmware Version ENC: %u.%u DEC: %u VEP: %u Revision: %u\n", + i, enc_major, enc_minor, dec_ver, vep, fw_rev); } else { unsigned int version_major, version_minor, family_id; family_id = le32_to_cpu(hdr->ucode_version) & 0xff; version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; - dev_info(adev->dev, "Found VCN firmware Version: %u.%u Family ID: %u\n", - version_major, version_minor, family_id); + dev_info(adev->dev, "[VCN instance %d] Found VCN firmware Version: %u.%u Family ID: %u\n", + i, version_major, version_minor, family_id); } bo_size = AMDGPU_VCN_STACK_SIZE + AMDGPU_VCN_CONTEXT_SIZE; @@ -285,6 +286,10 @@ int amdgpu_vcn_sw_fini(struct amdgpu_device *adev, int i) amdgpu_ucode_release(&adev->vcn.inst[0].fw); adev->vcn.inst[i].fw = NULL; } + + if (adev->vcn.reg_list) + amdgpu_vcn_reg_dump_fini(adev); + mutex_destroy(&adev->vcn.inst[i].vcn_pg_lock); mutex_destroy(&adev->vcn.inst[i].vcn1_jpeg1_workaround); @@ -352,8 +357,6 @@ int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i) if (adev->vcn.harvest_config & (1 << i)) return 0; - cancel_delayed_work_sync(&adev->vcn.inst[i].idle_work); - /* 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) @@ -405,6 +408,54 @@ int amdgpu_vcn_resume(struct amdgpu_device *adev, int i) return 0; } +void amdgpu_vcn_get_profile(struct amdgpu_device *adev) +{ + int r; + + mutex_lock(&adev->vcn.workload_profile_mutex); + + if (adev->vcn.workload_profile_active) { + mutex_unlock(&adev->vcn.workload_profile_mutex); + return; + } + r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, + true); + if (r) + dev_warn(adev->dev, + "(%d) failed to enable video power profile mode\n", r); + else + adev->vcn.workload_profile_active = true; + mutex_unlock(&adev->vcn.workload_profile_mutex); +} + +void amdgpu_vcn_put_profile(struct amdgpu_device *adev) +{ + bool pg = true; + int r, i; + + mutex_lock(&adev->vcn.workload_profile_mutex); + for (i = 0; i < adev->vcn.num_vcn_inst; i++) { + if (adev->vcn.inst[i].cur_state != AMD_PG_STATE_GATE) { + pg = false; + break; + } + } + + if (pg) { + r = amdgpu_dpm_switch_power_profile( + adev, PP_SMC_POWER_PROFILE_VIDEO, false); + if (r) + dev_warn( + adev->dev, + "(%d) failed to disable video power profile mode\n", + r); + else + adev->vcn.workload_profile_active = false; + } + + mutex_unlock(&adev->vcn.workload_profile_mutex); +} + static void amdgpu_vcn_idle_work_handler(struct work_struct *work) { struct amdgpu_vcn_inst *vcn_inst = @@ -412,7 +463,6 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work) struct amdgpu_device *adev = vcn_inst->adev; unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0}; unsigned int i = vcn_inst->inst, j; - int r = 0; if (adev->vcn.harvest_config & (1 << i)) return; @@ -438,16 +488,11 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work) fences += fence[i]; if (!fences && !atomic_read(&vcn_inst->total_submission_cnt)) { + mutex_lock(&vcn_inst->vcn_pg_lock); vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_GATE); - mutex_lock(&adev->vcn.workload_profile_mutex); - if (adev->vcn.workload_profile_active) { - r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, - false); - if (r) - dev_warn(adev->dev, "(%d) failed to disable video power profile mode\n", r); - adev->vcn.workload_profile_active = false; - } - mutex_unlock(&adev->vcn.workload_profile_mutex); + mutex_unlock(&vcn_inst->vcn_pg_lock); + amdgpu_vcn_put_profile(adev); + } else { schedule_delayed_work(&vcn_inst->idle_work, VCN_IDLE_TIMEOUT); } @@ -457,30 +502,11 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct amdgpu_vcn_inst *vcn_inst = &adev->vcn.inst[ring->me]; - int r = 0; atomic_inc(&vcn_inst->total_submission_cnt); cancel_delayed_work_sync(&vcn_inst->idle_work); - /* We can safely return early here because we've cancelled the - * the delayed work so there is no one else to set it to false - * and we don't care if someone else sets it to true. - */ - if (adev->vcn.workload_profile_active) - goto pg_lock; - - mutex_lock(&adev->vcn.workload_profile_mutex); - if (!adev->vcn.workload_profile_active) { - r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, - true); - if (r) - dev_warn(adev->dev, "(%d) failed to switch to video power profile mode\n", r); - adev->vcn.workload_profile_active = true; - } - mutex_unlock(&adev->vcn.workload_profile_mutex); - -pg_lock: mutex_lock(&vcn_inst->vcn_pg_lock); vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_UNGATE); @@ -508,6 +534,7 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring) vcn_inst->pause_dpg_mode(vcn_inst, &new_state); } mutex_unlock(&vcn_inst->vcn_pg_lock); + amdgpu_vcn_get_profile(adev); } void amdgpu_vcn_ring_end_use(struct amdgpu_ring *ring) @@ -601,7 +628,7 @@ static int amdgpu_vcn_dec_send_msg(struct amdgpu_ring *ring, r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, 64, AMDGPU_IB_POOL_DIRECT, - &job); + &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) goto err; @@ -781,7 +808,7 @@ static int amdgpu_vcn_dec_sw_send_msg(struct amdgpu_ring *ring, r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job); + &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) goto err; @@ -911,7 +938,7 @@ static int amdgpu_vcn_enc_get_create_msg(struct amdgpu_ring *ring, uint32_t hand r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job); + &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -978,7 +1005,7 @@ static int amdgpu_vcn_enc_get_destroy_msg(struct amdgpu_ring *ring, uint32_t han r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job); + &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -1527,3 +1554,86 @@ int amdgpu_vcn_ring_reset(struct amdgpu_ring *ring, return amdgpu_vcn_reset_engine(adev, ring->me); } + +int amdgpu_vcn_reg_dump_init(struct amdgpu_device *adev, + const struct amdgpu_hwip_reg_entry *reg, u32 count) +{ + adev->vcn.ip_dump = kcalloc(adev->vcn.num_vcn_inst * count, + sizeof(uint32_t), GFP_KERNEL); + if (!adev->vcn.ip_dump) + return -ENOMEM; + adev->vcn.reg_list = reg; + adev->vcn.reg_count = count; + + return 0; +} + +static void amdgpu_vcn_reg_dump_fini(struct amdgpu_device *adev) +{ + kfree(adev->vcn.ip_dump); + adev->vcn.ip_dump = NULL; + adev->vcn.reg_list = NULL; + adev->vcn.reg_count = 0; +} + +void amdgpu_vcn_dump_ip_state(struct amdgpu_ip_block *ip_block) +{ + struct amdgpu_device *adev = ip_block->adev; + int i, j; + bool is_powered; + u32 inst_off; + + if (!adev->vcn.ip_dump) + return; + + for (i = 0; i < adev->vcn.num_vcn_inst; i++) { + if (adev->vcn.harvest_config & (1 << i)) + continue; + + inst_off = i * adev->vcn.reg_count; + /* mmUVD_POWER_STATUS is always readable and is the first in reg_list */ + adev->vcn.ip_dump[inst_off] = + RREG32(SOC15_REG_ENTRY_OFFSET_INST(adev->vcn.reg_list[0], i)); + is_powered = (adev->vcn.ip_dump[inst_off] & + UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF) != + UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF; + + if (is_powered) + for (j = 1; j < adev->vcn.reg_count; j++) + adev->vcn.ip_dump[inst_off + j] = + RREG32(SOC15_REG_ENTRY_OFFSET_INST(adev->vcn.reg_list[j], i)); + } +} + +void amdgpu_vcn_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) +{ + struct amdgpu_device *adev = ip_block->adev; + int i, j; + bool is_powered; + u32 inst_off; + + if (!adev->vcn.ip_dump) + return; + + drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); + for (i = 0; i < adev->vcn.num_vcn_inst; i++) { + if (adev->vcn.harvest_config & (1 << i)) { + drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); + continue; + } + + inst_off = i * adev->vcn.reg_count; + is_powered = (adev->vcn.ip_dump[inst_off] & + UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF) != + UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF; + + if (is_powered) { + drm_printf(p, "\nActive Instance:VCN%d\n", i); + for (j = 0; j < adev->vcn.reg_count; j++) + drm_printf(p, "%-50s \t 0x%08x\n", adev->vcn.reg_list[j].reg_name, + adev->vcn.ip_dump[inst_off + j]); + } else { + drm_printf(p, "\nInactive Instance:VCN%d\n", i); + } + } +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h index 0bc0a94d7cf0..6d9acd36041d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h @@ -237,6 +237,8 @@ #define AMDGPU_DRM_KEY_INJECT_WORKAROUND_VCNFW_ASD_HANDSHAKING 2 +struct amdgpu_hwip_reg_entry; + enum amdgpu_vcn_caps { AMDGPU_VCN_RRMT_ENABLED, }; @@ -362,6 +364,8 @@ struct amdgpu_vcn { bool workload_profile_active; struct mutex workload_profile_mutex; + u32 reg_count; + const struct amdgpu_hwip_reg_entry *reg_list; }; struct amdgpu_fw_shared_rb_ptrs_struct { @@ -557,4 +561,11 @@ int vcn_set_powergating_state(struct amdgpu_ip_block *ip_block, int amdgpu_vcn_ring_reset(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *guilty_fence); +int amdgpu_vcn_reg_dump_init(struct amdgpu_device *adev, + const struct amdgpu_hwip_reg_entry *reg, u32 count); +void amdgpu_vcn_dump_ip_state(struct amdgpu_ip_block *ip_block); +void amdgpu_vcn_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p); +void amdgpu_vcn_get_profile(struct amdgpu_device *adev); +void amdgpu_vcn_put_profile(struct amdgpu_device *adev); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h index 3da3ebb1d9a1..58accf2259b3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h @@ -267,7 +267,8 @@ struct amdgpu_virt { struct amdgpu_irq_src rcv_irq; struct work_struct flr_work; - struct work_struct bad_pages_work; + struct work_struct req_bad_pages_work; + struct work_struct handle_bad_pages_work; struct amdgpu_mm_table mm_table; const struct amdgpu_virt_ops *ops; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 5cacf5717016..dbda3a38a2b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -654,11 +654,10 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, * Check if all VM PDs/PTs are ready for updates * * Returns: - * True if VM is not evicting. + * True if VM is not evicting and all VM entities are not stopped */ bool amdgpu_vm_ready(struct amdgpu_vm *vm) { - bool empty; bool ret; amdgpu_vm_eviction_lock(vm); @@ -666,10 +665,18 @@ bool amdgpu_vm_ready(struct amdgpu_vm *vm) amdgpu_vm_eviction_unlock(vm); spin_lock(&vm->status_lock); - empty = list_empty(&vm->evicted); + ret &= list_empty(&vm->evicted); spin_unlock(&vm->status_lock); - return ret && empty; + spin_lock(&vm->immediate.lock); + ret &= !vm->immediate.stopped; + spin_unlock(&vm->immediate.lock); + + spin_lock(&vm->delayed.lock); + ret &= !vm->delayed.stopped; + spin_unlock(&vm->delayed.lock); + + return ret; } /** @@ -970,7 +977,8 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, params.vm = vm; params.immediate = immediate; - r = vm->update_funcs->prepare(¶ms, NULL); + r = vm->update_funcs->prepare(¶ms, NULL, + AMDGPU_KERNEL_JOB_ID_VM_UPDATE_PDES); if (r) goto error; @@ -1139,7 +1147,8 @@ int amdgpu_vm_update_range(struct amdgpu_device *adev, struct amdgpu_vm *vm, dma_fence_put(tmp); } - r = vm->update_funcs->prepare(¶ms, sync); + r = vm->update_funcs->prepare(¶ms, sync, + AMDGPU_KERNEL_JOB_ID_VM_UPDATE_RANGE); if (r) goto error_free; @@ -1276,7 +1285,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, struct drm_gem_object *obj = &bo->tbo.base; if (drm_gem_is_imported(obj) && bo_va->is_xgmi) { - struct dma_buf *dma_buf = obj->dma_buf; + struct dma_buf *dma_buf = obj->import_attach->dmabuf; struct drm_gem_object *gobj = dma_buf->priv; struct amdgpu_bo *abo = gem_to_amdgpu_bo(gobj); @@ -1332,13 +1341,14 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, /* normally,bo_va->flags only contians READABLE and WIRTEABLE bit go here * but in case of something, we filter the flags in first place */ - if (!(mapping->flags & AMDGPU_PTE_READABLE)) + if (!(mapping->flags & AMDGPU_VM_PAGE_READABLE)) update_flags &= ~AMDGPU_PTE_READABLE; - if (!(mapping->flags & AMDGPU_PTE_WRITEABLE)) + if (!(mapping->flags & AMDGPU_VM_PAGE_WRITEABLE)) update_flags &= ~AMDGPU_PTE_WRITEABLE; /* Apply ASIC specific mapping flags */ - amdgpu_gmc_get_vm_pte(adev, mapping, &update_flags); + amdgpu_gmc_get_vm_pte(adev, vm, bo, mapping->flags, + &update_flags); trace_amdgpu_vm_bo_update(mapping); @@ -1479,7 +1489,7 @@ static void amdgpu_vm_free_mapping(struct amdgpu_device *adev, struct amdgpu_bo_va_mapping *mapping, struct dma_fence *fence) { - if (mapping->flags & AMDGPU_PTE_PRT_FLAG(adev)) + if (mapping->flags & AMDGPU_VM_PAGE_PRT) amdgpu_vm_add_prt_cb(adev, fence); kfree(mapping); } @@ -1758,7 +1768,7 @@ static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev, list_add(&mapping->list, &bo_va->invalids); amdgpu_vm_it_insert(mapping, &vm->va); - if (mapping->flags & AMDGPU_PTE_PRT_FLAG(adev)) + if (mapping->flags & AMDGPU_VM_PAGE_PRT) amdgpu_vm_prt_get(adev); if (amdgpu_vm_is_bo_always_valid(vm, bo) && !bo_va->base.moved) @@ -1818,7 +1828,7 @@ static int amdgpu_vm_verify_parameters(struct amdgpu_device *adev, int amdgpu_vm_bo_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t saddr, uint64_t offset, - uint64_t size, uint64_t flags) + uint64_t size, uint32_t flags) { struct amdgpu_bo_va_mapping *mapping, *tmp; struct amdgpu_bo *bo = bo_va->base.bo; @@ -1877,7 +1887,7 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev, int amdgpu_vm_bo_replace_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t saddr, uint64_t offset, - uint64_t size, uint64_t flags) + uint64_t size, uint32_t flags) { struct amdgpu_bo_va_mapping *mapping; struct amdgpu_bo *bo = bo_va->base.bo; @@ -2734,7 +2744,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) dma_fence_put(vm->last_tlb_flush); list_for_each_entry_safe(mapping, tmp, &vm->freed, list) { - if (mapping->flags & AMDGPU_PTE_PRT_FLAG(adev) && prt_fini_needed) { + if (mapping->flags & AMDGPU_VM_PAGE_PRT && prt_fini_needed) { amdgpu_vm_prt_fini(adev, vm); prt_fini_needed = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index fd086efd8457..67eaf5402e7e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -308,7 +308,7 @@ struct amdgpu_vm_update_params { struct amdgpu_vm_update_funcs { int (*map_table)(struct amdgpu_bo_vm *bo); int (*prepare)(struct amdgpu_vm_update_params *p, - struct amdgpu_sync *sync); + struct amdgpu_sync *sync, u64 k_job_id); int (*update)(struct amdgpu_vm_update_params *p, struct amdgpu_bo_vm *bo, uint64_t pe, uint64_t addr, unsigned count, uint32_t incr, uint64_t flags); @@ -538,11 +538,11 @@ struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, int amdgpu_vm_bo_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t addr, uint64_t offset, - uint64_t size, uint64_t flags); + uint64_t size, uint32_t flags); int amdgpu_vm_bo_replace_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t addr, uint64_t offset, - uint64_t size, uint64_t flags); + uint64_t size, uint32_t flags); int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t addr); @@ -670,4 +670,9 @@ void amdgpu_vm_tlb_fence_create(struct amdgpu_device *adev, void amdgpu_vm_print_task_info(struct amdgpu_device *adev, struct amdgpu_task_info *task_info); +#define amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) \ + list_for_each_entry(mapping, &(bo_va)->valids, list) +#define amdgpu_vm_bo_va_for_each_invalid_mapping(bo_va, mapping) \ + list_for_each_entry(mapping, &(bo_va)->invalids, list) + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c index 0c1ef5850a5e..22e2e5b47341 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c @@ -40,12 +40,14 @@ static int amdgpu_vm_cpu_map_table(struct amdgpu_bo_vm *table) * * @p: see amdgpu_vm_update_params definition * @sync: sync obj with fences to wait on + * @k_job_id: the id for tracing/debug purposes * * Returns: * Negativ errno, 0 for success. */ static int amdgpu_vm_cpu_prepare(struct amdgpu_vm_update_params *p, - struct amdgpu_sync *sync) + struct amdgpu_sync *sync, + u64 k_job_id) { if (!sync) 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 30022123b0bf..f794fb1cc06e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c @@ -26,6 +26,7 @@ #include "amdgpu.h" #include "amdgpu_trace.h" #include "amdgpu_vm.h" +#include "amdgpu_job.h" /* * amdgpu_vm_pt_cursor - state for for_each_amdgpu_vm_pt @@ -395,7 +396,8 @@ int amdgpu_vm_pt_clear(struct amdgpu_device *adev, struct amdgpu_vm *vm, params.vm = vm; params.immediate = immediate; - r = vm->update_funcs->prepare(¶ms, NULL); + r = vm->update_funcs->prepare(¶ms, NULL, + AMDGPU_KERNEL_JOB_ID_VM_PT_CLEAR); if (r) goto exit; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index 46d9fb433ab2..36805dcfa159 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -40,7 +40,7 @@ static int amdgpu_vm_sdma_map_table(struct amdgpu_bo_vm *table) /* Allocate a new job for @count PTE updates */ static int amdgpu_vm_sdma_alloc_job(struct amdgpu_vm_update_params *p, - unsigned int count) + unsigned int count, u64 k_job_id) { enum amdgpu_ib_pool_type pool = p->immediate ? AMDGPU_IB_POOL_IMMEDIATE : AMDGPU_IB_POOL_DELAYED; @@ -56,7 +56,7 @@ static int amdgpu_vm_sdma_alloc_job(struct amdgpu_vm_update_params *p, ndw = min(ndw, AMDGPU_VM_SDMA_MAX_NUM_DW); r = amdgpu_job_alloc_with_ib(p->adev, entity, AMDGPU_FENCE_OWNER_VM, - ndw * 4, pool, &p->job); + ndw * 4, pool, &p->job, k_job_id); if (r) return r; @@ -69,16 +69,17 @@ static int amdgpu_vm_sdma_alloc_job(struct amdgpu_vm_update_params *p, * * @p: see amdgpu_vm_update_params definition * @sync: amdgpu_sync object with fences to wait for + * @k_job_id: identifier of the job, for tracing purpose * * Returns: * Negativ errno, 0 for success. */ static int amdgpu_vm_sdma_prepare(struct amdgpu_vm_update_params *p, - struct amdgpu_sync *sync) + struct amdgpu_sync *sync, u64 k_job_id) { int r; - r = amdgpu_vm_sdma_alloc_job(p, 0); + r = amdgpu_vm_sdma_alloc_job(p, 0, k_job_id); if (r) return r; @@ -249,7 +250,8 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p, if (r) return r; - r = amdgpu_vm_sdma_alloc_job(p, count); + r = amdgpu_vm_sdma_alloc_job(p, count, + AMDGPU_KERNEL_JOB_ID_VM_UPDATE); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c index 121ee17b522b..474bfe36c0c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c @@ -379,9 +379,10 @@ static int vpe_sw_init(struct amdgpu_ip_block *ip_block) if (ret) goto out; - /* TODO: Add queue reset mask when FW fully supports it */ adev->vpe.supported_reset = amdgpu_get_soft_full_reset_mask(&adev->vpe.ring); + if (!amdgpu_sriov_vf(adev)) + adev->vpe.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE; ret = amdgpu_vpe_sysfs_reset_mask_init(adev); if (ret) goto out; @@ -435,6 +436,8 @@ static int vpe_hw_fini(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; struct amdgpu_vpe *vpe = &adev->vpe; + cancel_delayed_work_sync(&adev->vpe.idle_work); + vpe_ring_stop(vpe); /* Power off VPE */ @@ -445,10 +448,6 @@ static int vpe_hw_fini(struct amdgpu_ip_block *ip_block) static int vpe_suspend(struct amdgpu_ip_block *ip_block) { - struct amdgpu_device *adev = ip_block->adev; - - cancel_delayed_work_sync(&adev->vpe.idle_work); - return vpe_hw_fini(ip_block); } @@ -874,6 +873,27 @@ static void vpe_ring_end_use(struct amdgpu_ring *ring) schedule_delayed_work(&adev->vpe.idle_work, VPE_IDLE_TIMEOUT); } +static int vpe_ring_reset(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence) +{ + struct amdgpu_device *adev = ring->adev; + int r; + + amdgpu_ring_reset_helper_begin(ring, timedout_fence); + + r = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VPE, + AMD_PG_STATE_GATE); + if (r) + return r; + r = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VPE, + AMD_PG_STATE_UNGATE); + if (r) + return r; + + return amdgpu_ring_reset_helper_end(ring, timedout_fence); +} + static ssize_t amdgpu_get_vpe_reset_mask(struct device *dev, struct device_attribute *attr, char *buf) @@ -942,6 +962,7 @@ static const struct amdgpu_ring_funcs vpe_ring_funcs = { .preempt_ib = vpe_ring_preempt_ib, .begin_use = vpe_ring_begin_use, .end_use = vpe_ring_end_use, + .reset = vpe_ring_reset, }; static void vpe_set_ring_funcs(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index abdc52b0895a..e69db0a93378 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -396,6 +396,35 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr, return ret; } +int amdgpu_vram_mgr_query_address_block_info(struct amdgpu_vram_mgr *mgr, + uint64_t address, struct amdgpu_vram_block_info *info) +{ + struct amdgpu_vram_mgr_resource *vres; + struct drm_buddy_block *block; + u64 start, size; + int ret = -ENOENT; + + mutex_lock(&mgr->lock); + list_for_each_entry(vres, &mgr->allocated_vres_list, vres_node) { + list_for_each_entry(block, &vres->blocks, link) { + start = amdgpu_vram_mgr_block_start(block); + size = amdgpu_vram_mgr_block_size(block); + if ((start <= address) && (address < (start + size))) { + info->start = start; + info->size = size; + memcpy(&info->task, &vres->task, sizeof(vres->task)); + ret = 0; + goto out; + } + } + } + +out: + mutex_unlock(&mgr->lock); + + return ret; +} + static void amdgpu_dummy_vram_mgr_debug(struct ttm_resource_manager *man, struct drm_printer *printer) { @@ -568,6 +597,10 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, remaining_size -= size; } + vres->task.pid = task_pid_nr(current); + get_task_comm(vres->task.comm, current); + list_add_tail(&vres->vres_node, &mgr->allocated_vres_list); + if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS && adjust_dcc_size) { struct drm_buddy_block *dcc_block; unsigned long dcc_start; @@ -645,12 +678,15 @@ static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man, uint64_t vis_usage = 0; mutex_lock(&mgr->lock); + + list_del(&vres->vres_node); + memset(&vres->task, 0, sizeof(vres->task)); + list_for_each_entry(block, &vres->blocks, link) vis_usage += amdgpu_vram_mgr_vis_size(adev, block); - amdgpu_vram_mgr_do_reserve(man); - drm_buddy_free_list(mm, &vres->blocks, vres->flags); + amdgpu_vram_mgr_do_reserve(man); mutex_unlock(&mgr->lock); atomic64_sub(vis_usage, &mgr->vis_usage); @@ -782,6 +818,23 @@ uint64_t amdgpu_vram_mgr_vis_usage(struct amdgpu_vram_mgr *mgr) return atomic64_read(&mgr->vis_usage); } +/** + * amdgpu_vram_mgr_clear_reset_blocks - reset clear blocks + * + * @adev: amdgpu device pointer + * + * Reset the cleared drm buddy blocks. + */ +void amdgpu_vram_mgr_clear_reset_blocks(struct amdgpu_device *adev) +{ + struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr; + struct drm_buddy *mm = &mgr->mm; + + mutex_lock(&mgr->lock); + drm_buddy_reset_clear(mm, false); + mutex_unlock(&mgr->lock); +} + /** * amdgpu_vram_mgr_intersects - test each drm buddy block for intersection * @@ -917,6 +970,7 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) mutex_init(&mgr->lock); INIT_LIST_HEAD(&mgr->reservations_pending); INIT_LIST_HEAD(&mgr->reserved_pages); + INIT_LIST_HEAD(&mgr->allocated_vres_list); mgr->default_page_size = PAGE_SIZE; if (!adev->gmc.is_app_apu) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 2c88d5fd87da..5f5fd9a911c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -35,12 +35,26 @@ struct amdgpu_vram_mgr { struct list_head reserved_pages; atomic64_t vis_usage; u64 default_page_size; + struct list_head allocated_vres_list; +}; + +struct amdgpu_vres_task { + pid_t pid; + char comm[TASK_COMM_LEN]; +}; + +struct amdgpu_vram_block_info { + u64 start; + u64 size; + struct amdgpu_vres_task task; }; struct amdgpu_vram_mgr_resource { struct ttm_resource base; struct list_head blocks; unsigned long flags; + struct list_head vres_node; + struct amdgpu_vres_task task; }; static inline u64 amdgpu_vram_mgr_block_start(struct drm_buddy_block *block) @@ -72,4 +86,7 @@ static inline void amdgpu_vram_mgr_set_cleared(struct ttm_resource *res) ares->flags |= DRM_BUDDY_CLEARED; } +int amdgpu_vram_mgr_query_address_block_info(struct amdgpu_vram_mgr *mgr, + uint64_t address, struct amdgpu_vram_block_info *info); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index c417f8689220..1083db8cea2e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -120,6 +120,25 @@ static void __amdgpu_xcp_add_block(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id, xcp->valid = true; } +static void __amdgpu_xcp_set_unique_id(struct amdgpu_xcp_mgr *xcp_mgr, + int xcp_id) +{ + struct amdgpu_xcp *xcp = &xcp_mgr->xcp[xcp_id]; + struct amdgpu_device *adev = xcp_mgr->adev; + uint32_t inst_mask; + uint64_t uid; + int i; + + if (!amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &inst_mask) && + inst_mask) { + i = GET_INST(GC, (ffs(inst_mask) - 1)); + uid = amdgpu_device_get_uid(xcp_mgr->adev->uid_info, + AMDGPU_UID_TYPE_XCD, i); + if (uid) + xcp->unique_id = uid; + } +} + int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode) { struct amdgpu_device *adev = xcp_mgr->adev; @@ -158,6 +177,7 @@ int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode) else xcp_mgr->xcp[i].mem_id = mem_id; } + __amdgpu_xcp_set_unique_id(xcp_mgr, i); } xcp_mgr->num_xcps = num_xcps; @@ -406,6 +426,7 @@ void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev) p_ddev->primary->dev = adev->xcp_mgr->xcp[i].pdev; p_ddev->driver = adev->xcp_mgr->xcp[i].driver; p_ddev->vma_offset_manager = adev->xcp_mgr->xcp[i].vma_offset_manager; + amdgpu_xcp_drm_dev_free(p_ddev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h index 70a0f8400b57..1928d9e224fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h @@ -112,6 +112,7 @@ struct amdgpu_xcp { struct amdgpu_sched gpu_sched[AMDGPU_HW_IP_NUM][AMDGPU_RING_PRIO_MAX]; struct amdgpu_xcp_mgr *xcp_mgr; struct kobject kobj; + uint64_t unique_id; }; struct amdgpu_xcp_mgr { diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c index 914cf4bfb033..811124ff88a8 100644 --- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c +++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c @@ -227,6 +227,7 @@ static int __aqua_vanjaram_get_px_mode_info(struct amdgpu_xcp_mgr *xcp_mgr, uint16_t *nps_modes) { struct amdgpu_device *adev = xcp_mgr->adev; + uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0); if (!num_xcp || !nps_modes || !(xcp_mgr->supp_xcp_modes & BIT(px_mode))) return -EINVAL; @@ -250,12 +251,14 @@ static int __aqua_vanjaram_get_px_mode_info(struct amdgpu_xcp_mgr *xcp_mgr, *num_xcp = 4; *nps_modes = BIT(AMDGPU_NPS1_PARTITION_MODE) | BIT(AMDGPU_NPS4_PARTITION_MODE); + if (gc_ver == IP_VERSION(9, 5, 0)) + *nps_modes |= BIT(AMDGPU_NPS2_PARTITION_MODE); break; case AMDGPU_CPX_PARTITION_MODE: *num_xcp = NUM_XCC(adev->gfx.xcc_mask); *nps_modes = BIT(AMDGPU_NPS1_PARTITION_MODE) | BIT(AMDGPU_NPS4_PARTITION_MODE); - if (amdgpu_sriov_vf(adev)) + if (gc_ver == IP_VERSION(9, 5, 0)) *nps_modes |= BIT(AMDGPU_NPS2_PARTITION_MODE); break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c index 427b073de2fc..1c994d0cc50b 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.c +++ b/drivers/gpu/drm/amd/amdgpu/atom.c @@ -1494,6 +1494,27 @@ static void atom_get_vbios_version(struct atom_context *ctx) } } +static void atom_get_vbios_build(struct atom_context *ctx) +{ + unsigned char *atom_rom_hdr; + unsigned char *str; + uint16_t base; + + base = CU16(ATOM_ROM_TABLE_PTR); + atom_rom_hdr = CSTR(base); + + str = CSTR(CU16(base + ATOM_ROM_CFG_PTR)); + /* Skip config string */ + while (str < atom_rom_hdr && *str++) + ; + /* Skip change list string */ + while (str < atom_rom_hdr && *str++) + ; + + if ((str + STRLEN_NORMAL) < atom_rom_hdr) + strscpy(ctx->build_num, str, STRLEN_NORMAL); +} + struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios) { int base; @@ -1554,6 +1575,7 @@ struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios) atom_get_vbios_pn(ctx); atom_get_vbios_date(ctx); atom_get_vbios_version(ctx); + atom_get_vbios_build(ctx); return ctx; } diff --git a/drivers/gpu/drm/amd/amdgpu/atom.h b/drivers/gpu/drm/amd/amdgpu/atom.h index b807f6639a4c..825ff28731f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.h +++ b/drivers/gpu/drm/amd/amdgpu/atom.h @@ -37,6 +37,7 @@ struct drm_device; #define ATOM_ROM_MAGIC "ATOM" #define ATOM_ROM_MAGIC_PTR 4 +#define ATOM_ROM_CFG_PTR 0xC #define ATOM_ROM_MSG_PTR 0x10 #define ATOM_ROM_CMD_PTR 0x1E #define ATOM_ROM_DATA_PTR 0x20 @@ -151,6 +152,7 @@ struct atom_context { uint32_t version; uint8_t vbios_ver_str[STRLEN_NORMAL]; uint8_t date[STRLEN_NORMAL]; + uint8_t build_num[STRLEN_NORMAL]; }; extern int amdgpu_atom_debug; diff --git a/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c new file mode 100644 index 000000000000..96616a865aac --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "amdgpu.h" +#include "nv.h" + +#include "soc15_common.h" +#include "soc15_hw_ip.h" +#include "cyan_skillfish_ip_offset.h" + +int cyan_skillfish_reg_base_init(struct amdgpu_device *adev) +{ + /* HW has more IP blocks, only initialized the blocke needed by driver */ + uint32_t i; + + adev->gfx.xcc_mask = 1; + for (i = 0 ; i < MAX_INSTANCE ; ++i) { + adev->reg_offset[GC_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[HDP_HWIP][i] = (uint32_t *)(&(HDP_BASE.instance[i])); + adev->reg_offset[MMHUB_HWIP][i] = (uint32_t *)(&(MMHUB_BASE.instance[i])); + adev->reg_offset[ATHUB_HWIP][i] = (uint32_t *)(&(ATHUB_BASE.instance[i])); + adev->reg_offset[NBIO_HWIP][i] = (uint32_t *)(&(NBIO_BASE.instance[i])); + adev->reg_offset[MP0_HWIP][i] = (uint32_t *)(&(MP0_BASE.instance[i])); + adev->reg_offset[MP1_HWIP][i] = (uint32_t *)(&(MP1_BASE.instance[i])); + adev->reg_offset[VCN_HWIP][i] = (uint32_t *)(&(UVD0_BASE.instance[i])); + adev->reg_offset[DF_HWIP][i] = (uint32_t *)(&(DF_BASE.instance[i])); + adev->reg_offset[DCE_HWIP][i] = (uint32_t *)(&(DMU_BASE.instance[i])); + adev->reg_offset[OSSSYS_HWIP][i] = (uint32_t *)(&(OSSSYS_BASE.instance[i])); + adev->reg_offset[SDMA0_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[SDMA1_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[SMUIO_HWIP][i] = (uint32_t *)(&(SMUIO_BASE.instance[i])); + adev->reg_offset[THM_HWIP][i] = (uint32_t *)(&(THM_BASE.instance[i])); + adev->reg_offset[CLK_HWIP][i] = (uint32_t *)(&(CLK_BASE.instance[i])); + } + return 0; +} diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index bf7c22f81cda..72ca6538b2e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -1141,8 +1141,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, /* save values for DPM */ amdgpu_crtc->line_time = line_time; - amdgpu_crtc->wm_high = latency_watermark_a; - amdgpu_crtc->wm_low = latency_watermark_b; + /* Save number of lines the linebuffer leads before the scanout */ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines; } @@ -1462,17 +1461,12 @@ static int dce_v10_0_audio_init(struct amdgpu_device *adev) static void dce_v10_0_audio_fini(struct amdgpu_device *adev) { - int i; - if (!amdgpu_audio) return; if (!adev->mode_info.audio.enabled) return; - for (i = 0; i < adev->mode_info.audio.num_pins; i++) - dce_v10_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); - adev->mode_info.audio.enabled = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 47e05783c4a0..e84608891300 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -1173,8 +1173,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, /* save values for DPM */ amdgpu_crtc->line_time = line_time; - amdgpu_crtc->wm_high = latency_watermark_a; - amdgpu_crtc->wm_low = latency_watermark_b; + /* Save number of lines the linebuffer leads before the scanout */ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines; } @@ -1511,17 +1510,12 @@ static int dce_v11_0_audio_init(struct amdgpu_device *adev) static void dce_v11_0_audio_fini(struct amdgpu_device *adev) { - int i; - if (!amdgpu_audio) return; if (!adev->mode_info.audio.enabled) return; - for (i = 0; i < adev->mode_info.audio.num_pins; i++) - dce_v11_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); - adev->mode_info.audio.enabled = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 276c025c4c03..acc887a58518 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -1034,7 +1034,6 @@ static void dce_v6_0_program_watermarks(struct amdgpu_device *adev, /* save values for DPM */ amdgpu_crtc->line_time = line_time; - amdgpu_crtc->wm_high = latency_watermark_a; /* Save number of lines the linebuffer leads before the scanout */ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines; @@ -1451,17 +1450,12 @@ static int dce_v6_0_audio_init(struct amdgpu_device *adev) static void dce_v6_0_audio_fini(struct amdgpu_device *adev) { - int i; - if (!amdgpu_audio) return; if (!adev->mode_info.audio.enabled) return; - for (i = 0; i < adev->mode_info.audio.num_pins; i++) - dce_v6_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); - adev->mode_info.audio.enabled = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index e62ccf9eb73d..2ccd6aad8dd6 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -1096,8 +1096,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, /* save values for DPM */ amdgpu_crtc->line_time = line_time; - amdgpu_crtc->wm_high = latency_watermark_a; - amdgpu_crtc->wm_low = latency_watermark_b; + /* Save number of lines the linebuffer leads before the scanout */ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines; } @@ -1443,17 +1442,12 @@ static int dce_v8_0_audio_init(struct amdgpu_device *adev) static void dce_v8_0_audio_fini(struct amdgpu_device *adev) { - int i; - if (!amdgpu_audio) return; if (!adev->mode_info.audio.enabled) return; - for (i = 0; i < adev->mode_info.audio.num_pins; i++) - dce_v8_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); - adev->mode_info.audio.enabled = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 7bd506f06eb1..264183ab24ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -7668,19 +7668,17 @@ static int gfx_v10_0_soft_reset(struct amdgpu_ip_block *ip_block) /* Disable MEC parsing/prefetching */ gfx_v10_0_cp_compute_enable(adev, false); - if (grbm_soft_reset) { - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - tmp |= grbm_soft_reset; - dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); + tmp |= grbm_soft_reset; + dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - udelay(50); + udelay(50); - tmp &= ~grbm_soft_reset; - WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - } + tmp &= ~grbm_soft_reset; + WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); /* Wait a little for things to settle down */ udelay(50); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index c01c241a1b06..3d9c045a8a64 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -1612,9 +1612,9 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 0, 2): case IP_VERSION(11, 0, 3): if (!adev->gfx.disable_uq && - adev->gfx.me_fw_version >= 2390 && - adev->gfx.pfp_fw_version >= 2530 && - adev->gfx.mec_fw_version >= 2600 && + adev->gfx.me_fw_version >= 2420 && + adev->gfx.pfp_fw_version >= 2580 && + adev->gfx.mec_fw_version >= 2650 && adev->mes.fw_version[0] >= 120) { adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs; adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs; @@ -4129,6 +4129,8 @@ static int gfx_v11_0_gfx_mqd_init(struct amdgpu_device *adev, void *m, #endif if (prop->tmz_queue) tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, TMZ_MATCH, 1); + if (!prop->kernel_queue) + tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_NON_PRIV, 1); mqd->cp_gfx_hqd_cntl = tmp; /* set up cp_doorbell_control */ @@ -4281,8 +4283,10 @@ static int gfx_v11_0_compute_mqd_init(struct amdgpu_device *adev, void *m, tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 1); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TUNNEL_DISPATCH, prop->allow_tunneling); - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); + if (prop->kernel_queue) { + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); + } if (prop->tmz_queue) tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TMZ, 1); mqd->cp_hqd_pq_control = tmp; @@ -4639,8 +4643,7 @@ static int gfx_v11_0_gfxhub_enable(struct amdgpu_device *adev) amdgpu_device_flush_hdp(adev, NULL); - value = (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_ALWAYS) ? - false : true; + value = amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS; adev->gfxhub.funcs->set_fault_enable_default(adev, value); /* TODO investigate why this and the hdp flush above is needed, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 3e138527d534..5dbc5dbc694a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -3026,6 +3026,8 @@ static int gfx_v12_0_gfx_mqd_init(struct amdgpu_device *adev, void *m, #endif if (prop->tmz_queue) tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, TMZ_MATCH, 1); + if (!prop->kernel_queue) + tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_NON_PRIV, 1); mqd->cp_gfx_hqd_cntl = tmp; /* set up cp_doorbell_control */ @@ -3175,8 +3177,10 @@ static int gfx_v12_0_compute_mqd_init(struct amdgpu_device *adev, void *m, (order_base_2(AMDGPU_GPU_PAGE_SIZE / 4) - 1)); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 1); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TUNNEL_DISPATCH, 0); - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); + if (prop->kernel_queue) { + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); + } if (prop->tmz_queue) tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TMZ, 1); mqd->cp_hqd_pq_control = tmp; @@ -3520,8 +3524,7 @@ static int gfx_v12_0_gfxhub_enable(struct amdgpu_device *adev) amdgpu_device_flush_hdp(adev, NULL); - value = (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_ALWAYS) ? - false : true; + value = amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS; adev->gfxhub.funcs->set_fault_enable_default(adev, value); /* TODO investigate why this and the hdp flush above is needed, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 20b30f4b3c7d..a6ff9a137a83 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -2650,6 +2650,9 @@ static void gfx_v9_0_init_sq_config(struct amdgpu_device *adev) !READ_ONCE(adev->barrier_has_auto_waitcnt)); WREG32_SOC15(GC, 0, mmSQ_CONFIG, tmp); break; + case IP_VERSION(9, 4, 2): + gfx_v9_4_2_init_sq(adev); + break; default: break; } @@ -4172,19 +4175,17 @@ static int gfx_v9_0_soft_reset(struct amdgpu_ip_block *ip_block) /* Disable MEC parsing/prefetching */ gfx_v9_0_cp_compute_enable(adev, false); - if (grbm_soft_reset) { - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - tmp |= grbm_soft_reset; - dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); + tmp |= grbm_soft_reset; + dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - udelay(50); + udelay(50); - tmp &= ~grbm_soft_reset; - WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); - } + tmp &= ~grbm_soft_reset; + WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET); /* Wait a little for things to settle down */ udelay(50); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c index c48cd47b531f..8058ea91ecaf 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c @@ -748,6 +748,18 @@ void gfx_v9_4_2_init_golden_registers(struct amdgpu_device *adev, } } +void gfx_v9_4_2_init_sq(struct amdgpu_device *adev) +{ + uint32_t data; + + if (adev->gfx.mec_fw_version >= 98) { + adev->gmc.xnack_flags |= AMDGPU_GMC_XNACK_FLAG_CHAIN; + data = RREG32_SOC15(GC, 0, regSQ_CONFIG1); + data = REG_SET_FIELD(data, SQ_CONFIG1, DISABLE_XNACK_CHECK_IN_RETRY_DISABLE, 1); + WREG32_SOC15(GC, 0, regSQ_CONFIG1, data); + } +} + void gfx_v9_4_2_debug_trap_config_init(struct amdgpu_device *adev, uint32_t first_vmid, uint32_t last_vmid) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h index 7584624b641c..a603724c1dfc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h @@ -28,6 +28,7 @@ void gfx_v9_4_2_debug_trap_config_init(struct amdgpu_device *adev, uint32_t first_vmid, uint32_t last_vmid); void gfx_v9_4_2_init_golden_registers(struct amdgpu_device *adev, uint32_t die_id); +void gfx_v9_4_2_init_sq(struct amdgpu_device *adev); void gfx_v9_4_2_set_power_brake_sequence(struct amdgpu_device *adev); int gfx_v9_4_2_do_edc_gpr_workarounds(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 51babf5c78c8..8ba66d4dfe86 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -2461,19 +2461,17 @@ static int gfx_v9_4_3_soft_reset(struct amdgpu_ip_block *ip_block) /* Disable MEC parsing/prefetching */ gfx_v9_4_3_xcc_cp_compute_enable(adev, false, 0); - if (grbm_soft_reset) { - tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); - tmp |= grbm_soft_reset; - dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); + tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); + tmp |= grbm_soft_reset; + dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); - udelay(50); + udelay(50); - tmp &= ~grbm_soft_reset; - WREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET, tmp); - tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); - } + tmp &= ~grbm_soft_reset; + WREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET, tmp); + tmp = RREG32_SOC15(GC, GET_INST(GC, 0), regGRBM_SOFT_RESET); /* Wait a little for things to settle down */ udelay(50); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index 7923f491cf73..d7499be8c4bf 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -466,24 +466,6 @@ static void gmc_v10_0_emit_pasid_mapping(struct amdgpu_ring *ring, unsigned int * 0 valid */ -static uint64_t gmc_v10_0_map_mtype(struct amdgpu_device *adev, uint32_t flags) -{ - switch (flags) { - case AMDGPU_VM_MTYPE_DEFAULT: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_NC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_WC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_WC); - case AMDGPU_VM_MTYPE_CC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_CC); - case AMDGPU_VM_MTYPE_UC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_UC); - default: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - } -} - static void gmc_v10_0_get_vm_pde(struct amdgpu_device *adev, int level, uint64_t *addr, uint64_t *flags) { @@ -508,21 +490,39 @@ static void gmc_v10_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v10_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { - struct amdgpu_bo *bo = mapping->bo_va->base.bo; + if (vm_flags & AMDGPU_VM_PAGE_EXECUTABLE) + *flags |= AMDGPU_PTE_EXECUTABLE; + else + *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; + switch (vm_flags & AMDGPU_VM_MTYPE_MASK) { + case AMDGPU_VM_MTYPE_DEFAULT: + case AMDGPU_VM_MTYPE_NC: + default: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_NC); + break; + case AMDGPU_VM_MTYPE_WC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_WC); + break; + case AMDGPU_VM_MTYPE_CC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_CC); + break; + case AMDGPU_VM_MTYPE_UC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_UC); + break; + } - *flags &= ~AMDGPU_PTE_MTYPE_NV10_MASK; - *flags |= (mapping->flags & AMDGPU_PTE_MTYPE_NV10_MASK); + if (vm_flags & AMDGPU_VM_PAGE_NOALLOC) + *flags |= AMDGPU_PTE_NOALLOC; + else + *flags &= ~AMDGPU_PTE_NOALLOC; - *flags &= ~AMDGPU_PTE_NOALLOC; - *flags |= (mapping->flags & AMDGPU_PTE_NOALLOC); - - if (mapping->flags & AMDGPU_PTE_PRT) { + if (vm_flags & AMDGPU_VM_PAGE_PRT) { *flags |= AMDGPU_PTE_PRT; *flags |= AMDGPU_PTE_SNOOPED; *flags |= AMDGPU_PTE_LOG; @@ -563,7 +563,6 @@ static const struct amdgpu_gmc_funcs gmc_v10_0_gmc_funcs = { .flush_gpu_tlb_pasid = gmc_v10_0_flush_gpu_tlb_pasid, .emit_flush_gpu_tlb = gmc_v10_0_emit_flush_gpu_tlb, .emit_pasid_mapping = gmc_v10_0_emit_pasid_mapping, - .map_mtype = gmc_v10_0_map_mtype, .get_vm_pde = gmc_v10_0_get_vm_pde, .get_vm_pte = gmc_v10_0_get_vm_pte, .get_vbios_fb_size = gmc_v10_0_get_vbios_fb_size, @@ -964,8 +963,7 @@ static int gmc_v10_0_gart_enable(struct amdgpu_device *adev) /* Flush HDP after it is initialized */ amdgpu_device_flush_hdp(adev, NULL); - value = (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_ALWAYS) ? - false : true; + value = amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS; if (!adev->in_s0ix) adev->gfxhub.funcs->set_fault_enable_default(adev, value); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index f15d691e9a20..7bc389d9f5c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -430,24 +430,6 @@ static void gmc_v11_0_emit_pasid_mapping(struct amdgpu_ring *ring, unsigned int * 0 valid */ -static uint64_t gmc_v11_0_map_mtype(struct amdgpu_device *adev, uint32_t flags) -{ - switch (flags) { - case AMDGPU_VM_MTYPE_DEFAULT: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_NC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_WC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_WC); - case AMDGPU_VM_MTYPE_CC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_CC); - case AMDGPU_VM_MTYPE_UC: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_UC); - default: - return AMDGPU_PTE_MTYPE_NV10(0ULL, MTYPE_NC); - } -} - static void gmc_v11_0_get_vm_pde(struct amdgpu_device *adev, int level, uint64_t *addr, uint64_t *flags) { @@ -472,21 +454,39 @@ static void gmc_v11_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v11_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { - struct amdgpu_bo *bo = mapping->bo_va->base.bo; + if (vm_flags & AMDGPU_VM_PAGE_EXECUTABLE) + *flags |= AMDGPU_PTE_EXECUTABLE; + else + *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; + switch (vm_flags & AMDGPU_VM_MTYPE_MASK) { + case AMDGPU_VM_MTYPE_DEFAULT: + case AMDGPU_VM_MTYPE_NC: + default: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_NC); + break; + case AMDGPU_VM_MTYPE_WC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_WC); + break; + case AMDGPU_VM_MTYPE_CC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_CC); + break; + case AMDGPU_VM_MTYPE_UC: + *flags = AMDGPU_PTE_MTYPE_NV10(*flags, MTYPE_UC); + break; + } - *flags &= ~AMDGPU_PTE_MTYPE_NV10_MASK; - *flags |= (mapping->flags & AMDGPU_PTE_MTYPE_NV10_MASK); + if (vm_flags & AMDGPU_VM_PAGE_NOALLOC) + *flags |= AMDGPU_PTE_NOALLOC; + else + *flags &= ~AMDGPU_PTE_NOALLOC; - *flags &= ~AMDGPU_PTE_NOALLOC; - *flags |= (mapping->flags & AMDGPU_PTE_NOALLOC); - - if (mapping->flags & AMDGPU_PTE_PRT) { + if (vm_flags & AMDGPU_VM_PAGE_PRT) { *flags |= AMDGPU_PTE_PRT; *flags |= AMDGPU_PTE_SNOOPED; *flags |= AMDGPU_PTE_LOG; @@ -527,7 +527,6 @@ static const struct amdgpu_gmc_funcs gmc_v11_0_gmc_funcs = { .flush_gpu_tlb_pasid = gmc_v11_0_flush_gpu_tlb_pasid, .emit_flush_gpu_tlb = gmc_v11_0_emit_flush_gpu_tlb, .emit_pasid_mapping = gmc_v11_0_emit_pasid_mapping, - .map_mtype = gmc_v11_0_map_mtype, .get_vm_pde = gmc_v11_0_get_vm_pde, .get_vm_pte = gmc_v11_0_get_vm_pte, .get_vbios_fb_size = gmc_v11_0_get_vbios_fb_size, @@ -906,8 +905,7 @@ static int gmc_v11_0_gart_enable(struct amdgpu_device *adev) /* Flush HDP after it is initialized */ amdgpu_device_flush_hdp(adev, NULL); - value = (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_ALWAYS) ? - false : true; + value = amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS; adev->mmhub.funcs->set_fault_enable_default(adev, value); gmc_v11_0_flush_gpu_tlb(adev, 0, AMDGPU_MMHUB0(0), 0); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index de763105fdfd..76d3c40735b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -336,6 +336,22 @@ static void gmc_v12_0_flush_gpu_tlb_pasid(struct amdgpu_device *adev, uint16_t queried; int vmid, i; + if (adev->enable_uni_mes && adev->mes.ring[AMDGPU_MES_SCHED_PIPE].sched.ready && + (adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x81) { + struct mes_inv_tlbs_pasid_input input = {0}; + input.pasid = pasid; + input.flush_type = flush_type; + input.hub_id = AMDGPU_GFXHUB(0); + /* MES will invalidate all gc_hub for the device from master */ + adev->mes.funcs->invalidate_tlbs_pasid(&adev->mes, &input); + if (all_hub) { + /* Only need to invalidate mm_hub now, gfx12 only support one mmhub */ + input.hub_id = AMDGPU_MMHUB0(0); + adev->mes.funcs->invalidate_tlbs_pasid(&adev->mes, &input); + } + return; + } + for (vmid = 1; vmid < 16; vmid++) { bool valid; @@ -453,20 +469,6 @@ static void gmc_v12_0_emit_pasid_mapping(struct amdgpu_ring *ring, unsigned vmid * 0 valid */ -static uint64_t gmc_v12_0_map_mtype(struct amdgpu_device *adev, uint32_t flags) -{ - switch (flags) { - case AMDGPU_VM_MTYPE_DEFAULT: - return AMDGPU_PTE_MTYPE_GFX12(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_NC: - return AMDGPU_PTE_MTYPE_GFX12(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_UC: - return AMDGPU_PTE_MTYPE_GFX12(0ULL, MTYPE_UC); - default: - return AMDGPU_PTE_MTYPE_GFX12(0ULL, MTYPE_NC); - } -} - static void gmc_v12_0_get_vm_pde(struct amdgpu_device *adev, int level, uint64_t *addr, uint64_t *flags) { @@ -490,19 +492,35 @@ static void gmc_v12_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v12_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { - struct amdgpu_bo *bo = mapping->bo_va->base.bo; + if (vm_flags & AMDGPU_VM_PAGE_EXECUTABLE) + *flags |= AMDGPU_PTE_EXECUTABLE; + else + *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; + switch (vm_flags & AMDGPU_VM_MTYPE_MASK) { + case AMDGPU_VM_MTYPE_DEFAULT: + *flags = AMDGPU_PTE_MTYPE_GFX12(*flags, MTYPE_NC); + break; + case AMDGPU_VM_MTYPE_NC: + default: + *flags = AMDGPU_PTE_MTYPE_GFX12(*flags, MTYPE_NC); + break; + case AMDGPU_VM_MTYPE_UC: + *flags = AMDGPU_PTE_MTYPE_GFX12(*flags, MTYPE_UC); + break; + } - *flags &= ~AMDGPU_PTE_MTYPE_GFX12_MASK; - *flags |= (mapping->flags & AMDGPU_PTE_MTYPE_GFX12_MASK); + if (vm_flags & AMDGPU_VM_PAGE_NOALLOC) + *flags |= AMDGPU_PTE_NOALLOC; + else + *flags &= ~AMDGPU_PTE_NOALLOC; - if (mapping->flags & AMDGPU_PTE_PRT_GFX12) { - *flags |= AMDGPU_PTE_PRT_GFX12; + if (vm_flags & AMDGPU_VM_PAGE_PRT) { *flags |= AMDGPU_PTE_SNOOPED; *flags |= AMDGPU_PTE_SYSTEM; *flags |= AMDGPU_PTE_IS_PTE; @@ -543,7 +561,6 @@ static const struct amdgpu_gmc_funcs gmc_v12_0_gmc_funcs = { .flush_gpu_tlb_pasid = gmc_v12_0_flush_gpu_tlb_pasid, .emit_flush_gpu_tlb = gmc_v12_0_emit_flush_gpu_tlb, .emit_pasid_mapping = gmc_v12_0_emit_pasid_mapping, - .map_mtype = gmc_v12_0_map_mtype, .get_vm_pde = gmc_v12_0_get_vm_pde, .get_vm_pte = gmc_v12_0_get_vm_pte, .get_vbios_fb_size = gmc_v12_0_get_vbios_fb_size, @@ -876,8 +893,7 @@ static int gmc_v12_0_gart_enable(struct amdgpu_device *adev) /* Flush HDP after it is initialized */ amdgpu_device_flush_hdp(adev, NULL); - value = (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_ALWAYS) ? - false : true; + value = amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS; adev->mmhub.funcs->set_fault_enable_default(adev, value); gmc_v12_0_flush_gpu_tlb(adev, 0, AMDGPU_MMHUB0(0), 0); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index 8030fcd64210..f6ad7911f1e6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -382,7 +382,9 @@ static void gmc_v6_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v6_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { *flags &= ~AMDGPU_PTE_EXECUTABLE; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index a8d5795084fc..93d7ccb7d013 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -504,7 +504,9 @@ static void gmc_v7_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v7_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { *flags &= ~AMDGPU_PTE_EXECUTABLE; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index b45fa0cea9d2..c5e2a2c41e06 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -716,11 +716,15 @@ static void gmc_v8_0_get_vm_pde(struct amdgpu_device *adev, int level, } static void gmc_v8_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { - *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; + if (vm_flags & AMDGPU_VM_PAGE_EXECUTABLE) + *flags |= AMDGPU_PTE_EXECUTABLE; + else + *flags &= ~AMDGPU_PTE_EXECUTABLE; *flags &= ~AMDGPU_PTE_PRT; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index c4d69cf4e06c..8404695eb13f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1073,27 +1073,6 @@ static void gmc_v9_0_emit_pasid_mapping(struct amdgpu_ring *ring, unsigned int v * 0 valid */ -static uint64_t gmc_v9_0_map_mtype(struct amdgpu_device *adev, uint32_t flags) - -{ - switch (flags) { - case AMDGPU_VM_MTYPE_DEFAULT: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_NC: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_NC); - case AMDGPU_VM_MTYPE_WC: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_WC); - case AMDGPU_VM_MTYPE_RW: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_RW); - case AMDGPU_VM_MTYPE_CC: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_CC); - case AMDGPU_VM_MTYPE_UC: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_UC); - default: - return AMDGPU_PTE_MTYPE_VG10(0ULL, MTYPE_NC); - } -} - static void gmc_v9_0_get_vm_pde(struct amdgpu_device *adev, int level, uint64_t *addr, uint64_t *flags) { @@ -1123,6 +1102,7 @@ static void gmc_v9_0_get_vm_pde(struct amdgpu_device *adev, int level, static void gmc_v9_0_get_coherence_flags(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { struct amdgpu_device *bo_adev = amdgpu_ttm_adev(bo->tbo.bdev); @@ -1236,25 +1216,43 @@ static void gmc_v9_0_get_coherence_flags(struct amdgpu_device *adev, } static void gmc_v9_0_get_vm_pte(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, + struct amdgpu_vm *vm, + struct amdgpu_bo *bo, + uint32_t vm_flags, uint64_t *flags) { - struct amdgpu_bo *bo = mapping->bo_va->base.bo; + if (vm_flags & AMDGPU_VM_PAGE_EXECUTABLE) + *flags |= AMDGPU_PTE_EXECUTABLE; + else + *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags &= ~AMDGPU_PTE_EXECUTABLE; - *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; + switch (vm_flags & AMDGPU_VM_MTYPE_MASK) { + case AMDGPU_VM_MTYPE_DEFAULT: + case AMDGPU_VM_MTYPE_NC: + default: + *flags = AMDGPU_PTE_MTYPE_VG10(*flags, MTYPE_NC); + break; + case AMDGPU_VM_MTYPE_WC: + *flags |= AMDGPU_PTE_MTYPE_VG10(*flags, MTYPE_WC); + break; + case AMDGPU_VM_MTYPE_RW: + *flags |= AMDGPU_PTE_MTYPE_VG10(*flags, MTYPE_RW); + break; + case AMDGPU_VM_MTYPE_CC: + *flags |= AMDGPU_PTE_MTYPE_VG10(*flags, MTYPE_CC); + break; + case AMDGPU_VM_MTYPE_UC: + *flags |= AMDGPU_PTE_MTYPE_VG10(*flags, MTYPE_UC); + break; + } - *flags &= ~AMDGPU_PTE_MTYPE_VG10_MASK; - *flags |= mapping->flags & AMDGPU_PTE_MTYPE_VG10_MASK; - - if (mapping->flags & AMDGPU_PTE_PRT) { + if (vm_flags & AMDGPU_VM_PAGE_PRT) { *flags |= AMDGPU_PTE_PRT; *flags &= ~AMDGPU_PTE_VALID; } if ((*flags & AMDGPU_PTE_VALID) && bo) - gmc_v9_0_get_coherence_flags(adev, mapping->bo_va->base.vm, bo, - flags); + gmc_v9_0_get_coherence_flags(adev, vm, bo, vm_flags, flags); } static void gmc_v9_0_override_vm_pte_flags(struct amdgpu_device *adev, @@ -1391,7 +1389,6 @@ static const struct amdgpu_gmc_funcs gmc_v9_0_gmc_funcs = { .flush_gpu_tlb_pasid = gmc_v9_0_flush_gpu_tlb_pasid, .emit_flush_gpu_tlb = gmc_v9_0_emit_flush_gpu_tlb, .emit_pasid_mapping = gmc_v9_0_emit_pasid_mapping, - .map_mtype = gmc_v9_0_map_mtype, .get_vm_pde = gmc_v9_0_get_vm_pde, .get_vm_pte = gmc_v9_0_get_vm_pte, .override_vm_pte_flags = gmc_v9_0_override_vm_pte_flags, diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c index 5900b560b7de..333e9c30c091 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c @@ -587,8 +587,7 @@ static int ih_v6_0_sw_init(struct amdgpu_ip_block *ip_block) /* use gpu virtual address for ih ring * until ih_checken is programmed to allow * use bus address for ih ring by psp bl */ - use_bus_addr = - (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) ? false : true; + use_bus_addr = adev->firmware.load_type != AMDGPU_FW_LOAD_PSP; r = amdgpu_ih_ring_init(adev, &adev->irq.ih, IH_RING_SIZE, use_bus_addr); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c index 068ed849dbad..95b3f4e55ec3 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c @@ -562,8 +562,7 @@ static int ih_v6_1_sw_init(struct amdgpu_ip_block *ip_block) /* use gpu virtual address for ih ring * until ih_checken is programmed to allow * use bus address for ih ring by psp bl */ - use_bus_addr = - (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) ? false : true; + use_bus_addr = adev->firmware.load_type != AMDGPU_FW_LOAD_PSP; r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 256 * 1024, use_bus_addr); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v7_0.c b/drivers/gpu/drm/amd/amdgpu/ih_v7_0.c index 40a3530e0453..b32ea4129c61 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v7_0.c @@ -552,8 +552,7 @@ static int ih_v7_0_sw_init(struct amdgpu_ip_block *ip_block) /* use gpu virtual address for ih ring * until ih_checken is programmed to allow * use bus address for ih ring by psp bl */ - use_bus_addr = - (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) ? false : true; + use_bus_addr = adev->firmware.load_type != AMDGPU_FW_LOAD_PSP; r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 256 * 1024, use_bus_addr); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.c b/drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.c index a887df520414..4258d3e0b706 100644 --- a/drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.c +++ b/drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.c @@ -29,6 +29,8 @@ #include "amdgpu.h" #include "isp_v4_1_1.h" +MODULE_FIRMWARE("amdgpu/isp_4_1_1.bin"); + #define ISP_PERFORMANCE_STATE_LOW 0 #define ISP_PERFORMANCE_STATE_HIGH 1 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 b86288a69e7b..a78144773fab 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -444,7 +444,7 @@ static int jpeg_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block) ret = jpeg_v4_0_3_set_powergating_state(ip_block, AMD_PG_STATE_GATE); } - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__JPEG)) + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__JPEG) && !amdgpu_sriov_vf(adev)) amdgpu_irq_put(adev, &adev->jpeg.inst->ras_poison_irq, 0); return ret; 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 481d1a2dbe5a..5d86e1d846eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -686,7 +686,7 @@ static int jpeg_v4_0_5_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { struct amdgpu_device *adev = ip_block->adev; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; + bool enable = state == AMD_CG_STATE_GATE; int i; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { 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 e0a71909252b..34c70270ea1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c @@ -584,7 +584,7 @@ static int jpeg_v5_0_0_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { struct amdgpu_device *adev = ip_block->adev; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; + bool enable = state == AMD_CG_STATE_GATE; if (enable) { if (!jpeg_v5_0_0_is_idle(ip_block)) 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 54523dc1f702..baf097d2e1ac 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -196,6 +196,14 @@ static int jpeg_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block) } } + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__JPEG)) { + r = amdgpu_jpeg_ras_sw_init(adev); + if (r) { + dev_err(adev->dev, "Failed to initialize jpeg ras block!\n"); + return r; + } + } + r = amdgpu_jpeg_reg_dump_init(adev, jpeg_reg_list_5_0_1, ARRAY_SIZE(jpeg_reg_list_5_0_1)); if (r) return r; @@ -307,7 +315,7 @@ static int jpeg_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block) ret = jpeg_v5_0_1_set_powergating_state(ip_block, AMD_PG_STATE_GATE); } - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__JPEG)) + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__JPEG) && !amdgpu_sriov_vf(adev)) amdgpu_irq_put(adev, &adev->jpeg.inst->ras_poison_irq, 0); return ret; @@ -689,7 +697,7 @@ static int jpeg_v5_0_1_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { struct amdgpu_device *adev = ip_block->adev; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; + bool enable = state == AMD_CG_STATE_GATE; int i; @@ -1016,8 +1024,9 @@ static int jpeg_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_ban /* reference to smu driver if header file */ static int jpeg_v5_0_1_err_codes[] = { - 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-7][S|D] */ - 24, 25, 26, 27, 28, 29, 30, 31 + 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-9][S|D] */ + 24, 25, 26, 27, 28, 29, 30, 31, + 48, 49, 50, 51, }; static bool jpeg_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, @@ -1058,6 +1067,11 @@ static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_comm if (r) return r; + r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG, + &jpeg_v5_0_1_aca_info, NULL); + if (r) + goto late_fini; + if (amdgpu_ras_is_supported(adev, ras_block->block) && adev->jpeg.inst->ras_poison_irq.funcs) { r = amdgpu_irq_get(adev, &adev->jpeg.inst->ras_poison_irq, 0); @@ -1065,11 +1079,6 @@ static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_comm goto late_fini; } - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG, - &jpeg_v5_0_1_aca_info, NULL); - if (r) - goto late_fini; - return 0; late_fini: diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index d6f50b13e2ba..aee26f80bd53 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -21,6 +21,7 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include #include "amdgpu.h" #include "amdgpu_gfx.h" #include "mes_userqueue.h" @@ -198,6 +199,53 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, return 0; } +static int mes_userq_detect_and_reset(struct amdgpu_device *adev, + int queue_type) +{ + int db_array_size = amdgpu_mes_get_hung_queue_db_array_size(adev); + struct mes_detect_and_reset_queue_input input; + struct amdgpu_usermode_queue *queue; + struct amdgpu_userq_mgr *uqm, *tmp; + unsigned int hung_db_num = 0; + int queue_id, r, i; + u32 db_array[4]; + + if (db_array_size > 4) { + dev_err(adev->dev, "DB array size (%d vs 4) too small\n", + db_array_size); + return -EINVAL; + } + + memset(&input, 0x0, sizeof(struct mes_detect_and_reset_queue_input)); + + input.queue_type = queue_type; + + amdgpu_mes_lock(&adev->mes); + r = amdgpu_mes_detect_and_reset_hung_queues(adev, queue_type, false, + &hung_db_num, db_array); + amdgpu_mes_unlock(&adev->mes); + if (r) { + dev_err(adev->dev, "Failed to detect and reset queues, err (%d)\n", r); + } else if (hung_db_num) { + list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) { + idr_for_each_entry(&uqm->userq_idr, queue, queue_id) { + if (queue->queue_type == queue_type) { + for (i = 0; i < hung_db_num; i++) { + if (queue->doorbell_index == db_array[i]) { + queue->state = AMDGPU_USERQ_STATE_HUNG; + atomic_inc(&adev->gpu_reset_counter); + amdgpu_userq_fence_driver_force_completion(queue); + drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL); + } + } + } + } + } + } + + return r; +} + static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr, struct drm_amdgpu_userq_in *args_in, struct amdgpu_usermode_queue *queue) @@ -352,4 +400,5 @@ const struct amdgpu_userq_funcs userq_mes_funcs = { .mqd_destroy = mes_userq_mqd_destroy, .unmap = mes_userq_unmap, .map = mes_userq_map, + .detect_and_reset = mes_userq_detect_and_reset, }; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 28eb846280dd..3b91ea601add 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -66,6 +66,8 @@ static int mes_v11_0_kiq_hw_fini(struct amdgpu_device *adev); #define GFX_MES_DRAM_SIZE 0x80000 #define MES11_HW_RESOURCE_1_SIZE (128 * AMDGPU_GPU_PAGE_SIZE) +#define MES11_HUNG_DB_OFFSET_ARRAY_SIZE 4 + static void mes_v11_0_ring_set_wptr(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; @@ -641,8 +643,9 @@ static int mes_v11_0_misc_op(struct amdgpu_mes *mes, break; case MES_MISC_OP_CHANGE_CONFIG: if ((mes->adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) < 0x63) { - dev_err(mes->adev->dev, "MES FW version must be larger than 0x63 to support limit single process feature.\n"); - return -EINVAL; + dev_warn_once(mes->adev->dev, + "MES FW version must be larger than 0x63 to support limit single process feature.\n"); + return 0; } misc_pkt.opcode = MESAPI_MISC__CHANGE_CONFIG; misc_pkt.change_config.opcode = @@ -783,6 +786,32 @@ static int mes_v11_0_reset_hw_queue(struct amdgpu_mes *mes, offsetof(union MESAPI__RESET, api_status)); } +static int mes_v11_0_detect_and_reset_hung_queues(struct amdgpu_mes *mes, + struct mes_detect_and_reset_queue_input *input) +{ + union MESAPI__RESET mes_reset_queue_pkt; + + memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt)); + + mes_reset_queue_pkt.header.type = MES_API_TYPE_SCHEDULER; + mes_reset_queue_pkt.header.opcode = MES_SCH_API_RESET; + mes_reset_queue_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS; + + mes_reset_queue_pkt.queue_type = + convert_to_mes_queue_type(input->queue_type); + mes_reset_queue_pkt.doorbell_offset_addr = + mes->hung_queue_db_array_gpu_addr; + + if (input->detect_only) + mes_reset_queue_pkt.hang_detect_only = 1; + else + mes_reset_queue_pkt.hang_detect_then_reset = 1; + + return mes_v11_0_submit_pkt_and_poll_completion(mes, + &mes_reset_queue_pkt, sizeof(mes_reset_queue_pkt), + offsetof(union MESAPI__RESET, api_status)); +} + static const struct amdgpu_mes_funcs mes_v11_0_funcs = { .add_hw_queue = mes_v11_0_add_hw_queue, .remove_hw_queue = mes_v11_0_remove_hw_queue, @@ -792,6 +821,7 @@ static const struct amdgpu_mes_funcs mes_v11_0_funcs = { .resume_gang = mes_v11_0_resume_gang, .misc_op = mes_v11_0_misc_op, .reset_hw_queue = mes_v11_0_reset_hw_queue, + .detect_and_reset_hung_queues = mes_v11_0_detect_and_reset_hung_queues, }; static int mes_v11_0_allocate_ucode_buffer(struct amdgpu_device *adev, @@ -1684,6 +1714,8 @@ static int mes_v11_0_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int pipe, r; + adev->mes.hung_queue_db_array_size = + MES11_HUNG_DB_OFFSET_ARRAY_SIZE; for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) { if (!adev->enable_mes_kiq && pipe == AMDGPU_MES_KIQ_PIPE) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c index 6b222630f3fa..998893dff08e 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c @@ -47,6 +47,8 @@ static int mes_v12_0_kiq_hw_fini(struct amdgpu_device *adev); #define MES_EOP_SIZE 2048 +#define MES12_HUNG_DB_OFFSET_ARRAY_SIZE 4 + static void mes_v12_0_ring_set_wptr(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; @@ -108,6 +110,7 @@ static const char *mes_v12_0_opcodes[] = { "SET_SE_MODE", "SET_GANG_SUBMIT", "SET_HW_RSRC_1", + "INVALIDATE_TLBS", }; static const char *mes_v12_0_misc_opcodes[] = { @@ -567,13 +570,41 @@ static int mes_v12_0_unmap_legacy_queue(struct amdgpu_mes *mes, static int mes_v12_0_suspend_gang(struct amdgpu_mes *mes, struct mes_suspend_gang_input *input) { - return 0; + union MESAPI__SUSPEND mes_suspend_gang_pkt; + + memset(&mes_suspend_gang_pkt, 0, sizeof(mes_suspend_gang_pkt)); + + mes_suspend_gang_pkt.header.type = MES_API_TYPE_SCHEDULER; + mes_suspend_gang_pkt.header.opcode = MES_SCH_API_SUSPEND; + mes_suspend_gang_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS; + + mes_suspend_gang_pkt.suspend_all_gangs = input->suspend_all_gangs; + mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; + mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + + return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, + &mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt), + offsetof(union MESAPI__SUSPEND, api_status)); } static int mes_v12_0_resume_gang(struct amdgpu_mes *mes, struct mes_resume_gang_input *input) { - return 0; + union MESAPI__RESUME mes_resume_gang_pkt; + + memset(&mes_resume_gang_pkt, 0, sizeof(mes_resume_gang_pkt)); + + mes_resume_gang_pkt.header.type = MES_API_TYPE_SCHEDULER; + mes_resume_gang_pkt.header.opcode = MES_SCH_API_RESUME; + mes_resume_gang_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS; + + mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; + mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + + return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, + &mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt), + offsetof(union MESAPI__RESUME, api_status)); } static int mes_v12_0_query_sched_status(struct amdgpu_mes *mes, int pipe) @@ -879,6 +910,74 @@ static int mes_v12_0_reset_hw_queue(struct amdgpu_mes *mes, offsetof(union MESAPI__RESET, api_status)); } +static int mes_v12_0_detect_and_reset_hung_queues(struct amdgpu_mes *mes, + struct mes_detect_and_reset_queue_input *input) +{ + union MESAPI__RESET mes_reset_queue_pkt; + + memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt)); + + mes_reset_queue_pkt.header.type = MES_API_TYPE_SCHEDULER; + mes_reset_queue_pkt.header.opcode = MES_SCH_API_RESET; + mes_reset_queue_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS; + + mes_reset_queue_pkt.queue_type = + convert_to_mes_queue_type(input->queue_type); + mes_reset_queue_pkt.doorbell_offset_addr = + mes->hung_queue_db_array_gpu_addr; + + if (input->detect_only) + mes_reset_queue_pkt.hang_detect_only = 1; + else + mes_reset_queue_pkt.hang_detect_then_reset = 1; + + return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, + &mes_reset_queue_pkt, sizeof(mes_reset_queue_pkt), + offsetof(union MESAPI__RESET, api_status)); +} + +static int mes_v12_inv_tlb_convert_hub_id(uint8_t id) +{ + /* + * MES doesn't support invalidate gc_hub on slave xcc individually + * master xcc will invalidate all gc_hub for the partition + */ + if (AMDGPU_IS_GFXHUB(id)) + return 0; + else if (AMDGPU_IS_MMHUB0(id)) + return 1; + else + return -EINVAL; + +} + +static int mes_v12_0_inv_tlbs_pasid(struct amdgpu_mes *mes, + struct mes_inv_tlbs_pasid_input *input) +{ + union MESAPI__INV_TLBS mes_inv_tlbs; + int ret; + + memset(&mes_inv_tlbs, 0, sizeof(mes_inv_tlbs)); + + mes_inv_tlbs.header.type = MES_API_TYPE_SCHEDULER; + mes_inv_tlbs.header.opcode = MES_SCH_API_INV_TLBS; + mes_inv_tlbs.header.dwsize = API_FRAME_SIZE_IN_DWORDS; + + mes_inv_tlbs.invalidate_tlbs.inv_sel = 0; + mes_inv_tlbs.invalidate_tlbs.flush_type = input->flush_type; + mes_inv_tlbs.invalidate_tlbs.inv_sel_id = input->pasid; + + /*convert amdgpu_mes_hub_id to mes expected hub_id */ + ret = mes_v12_inv_tlb_convert_hub_id(input->hub_id); + if (ret < 0) + return -EINVAL; + mes_inv_tlbs.invalidate_tlbs.hub_id = ret; + return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_KIQ_PIPE, + &mes_inv_tlbs, sizeof(mes_inv_tlbs), + offsetof(union MESAPI__INV_TLBS, api_status)); + +} + static const struct amdgpu_mes_funcs mes_v12_0_funcs = { .add_hw_queue = mes_v12_0_add_hw_queue, .remove_hw_queue = mes_v12_0_remove_hw_queue, @@ -888,6 +987,8 @@ static const struct amdgpu_mes_funcs mes_v12_0_funcs = { .resume_gang = mes_v12_0_resume_gang, .misc_op = mes_v12_0_misc_op, .reset_hw_queue = mes_v12_0_reset_hw_queue, + .invalidate_tlbs_pasid = mes_v12_0_inv_tlbs_pasid, + .detect_and_reset_hung_queues = mes_v12_0_detect_and_reset_hung_queues, }; static int mes_v12_0_allocate_ucode_buffer(struct amdgpu_device *adev, @@ -1793,6 +1894,8 @@ static int mes_v12_0_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int pipe, r; + adev->mes.hung_queue_db_array_size = + MES12_HUNG_DB_OFFSET_ARRAY_SIZE; for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) { r = amdgpu_mes_init_microcode(adev, pipe); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_1.c b/drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_1.c index 134c4ec10887..910337dc28d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_1.c @@ -36,40 +36,47 @@ static const char *mmhub_client_ids_v3_0_1[][2] = { [0][0] = "VMC", + [1][0] = "ISPXT", + [2][0] = "ISPIXT", [4][0] = "DCEDMC", [5][0] = "DCEVGA", [6][0] = "MP0", [7][0] = "MP1", - [8][0] = "MPIO", - [16][0] = "HDP", - [17][0] = "LSDMA", - [18][0] = "JPEG", - [19][0] = "VCNU0", - [21][0] = "VSCH", - [22][0] = "VCNU1", - [23][0] = "VCN1", - [32+20][0] = "VCN0", - [2][1] = "DBGUNBIO", + [8][0] = "MPM", + [12][0] = "ISPTNR", + [14][0] = "ISPCRD0", + [15][0] = "ISPCRD1", + [16][0] = "ISPCRD2", + [22][0] = "HDP", + [23][0] = "LSDMA", + [24][0] = "JPEG", + [27][0] = "VSCH", + [28][0] = "VCNU", + [29][0] = "VCN", + [1][1] = "ISPXT", + [2][1] = "ISPIXT", [3][1] = "DCEDWB", [4][1] = "DCEDMC", [5][1] = "DCEVGA", [6][1] = "MP0", [7][1] = "MP1", - [8][1] = "MPIO", - [10][1] = "DBGU0", - [11][1] = "DBGU1", - [12][1] = "DBGU2", - [13][1] = "DBGU3", - [14][1] = "XDP", - [15][1] = "OSSSYS", - [16][1] = "HDP", - [17][1] = "LSDMA", - [18][1] = "JPEG", - [19][1] = "VCNU0", - [20][1] = "VCN0", - [21][1] = "VSCH", - [22][1] = "VCNU1", - [23][1] = "VCN1", + [8][1] = "MPM", + [10][1] = "ISPMWR0", + [11][1] = "ISPMWR1", + [12][1] = "ISPTNR", + [13][1] = "ISPSWR", + [14][1] = "ISPCWR0", + [15][1] = "ISPCWR1", + [16][1] = "ISPCWR2", + [17][1] = "ISPCWR3", + [18][1] = "XDP", + [21][1] = "OSSSYS", + [22][1] = "HDP", + [23][1] = "LSDMA", + [24][1] = "JPEG", + [27][1] = "VSCH", + [28][1] = "VCNU", + [29][1] = "VCN", }; static uint32_t mmhub_v3_0_1_get_invalidate_req(unsigned int vmid, diff --git a/drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.c b/drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.c index bc3d6c2fc87a..f6fc9778bc30 100644 --- a/drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.c +++ b/drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.c @@ -39,6 +39,64 @@ #define regDAGB1_L1TLB_REG_RW_3_3_BASE_IDX 1 static const char *mmhub_client_ids_v3_3[][2] = { + [0][0] = "VMC", + [1][0] = "ISPXT", + [2][0] = "ISPIXT", + [4][0] = "DCEDMC", + [6][0] = "MP0", + [7][0] = "MP1", + [8][0] = "MPM", + [9][0] = "ISPPDPRD", + [10][0] = "ISPCSTATRD", + [11][0] = "ISPBYRPRD", + [12][0] = "ISPRGBPRD", + [13][0] = "ISPMCFPRD", + [14][0] = "ISPMCFPRD1", + [15][0] = "ISPYUVPRD", + [16][0] = "ISPMCSCRD", + [17][0] = "ISPGDCRD", + [18][0] = "ISPLMERD", + [22][0] = "ISPXT1", + [23][0] = "ISPIXT1", + [24][0] = "HDP", + [25][0] = "LSDMA", + [26][0] = "JPEG", + [27][0] = "VPE", + [28][0] = "VSCH", + [29][0] = "VCNU", + [30][0] = "VCN", + [1][1] = "ISPXT", + [2][1] = "ISPIXT", + [3][1] = "DCEDWB", + [4][1] = "DCEDMC", + [5][1] = "ISPCSISWR", + [6][1] = "MP0", + [7][1] = "MP1", + [8][1] = "MPM", + [9][1] = "ISPPDPWR", + [10][1] = "ISPCSTATWR", + [11][1] = "ISPBYRPWR", + [12][1] = "ISPRGBPWR", + [13][1] = "ISPMCFPWR", + [14][1] = "ISPMWR0", + [15][1] = "ISPYUVPWR", + [16][1] = "ISPMCSCWR", + [17][1] = "ISPGDCWR", + [18][1] = "ISPLMEWR", + [20][1] = "ISPMWR2", + [21][1] = "OSSSYS", + [22][1] = "ISPXT1", + [23][1] = "ISPIXT1", + [24][1] = "HDP", + [25][1] = "LSDMA", + [26][1] = "JPEG", + [27][1] = "VPE", + [28][1] = "VSCH", + [29][1] = "VCNU", + [30][1] = "VCN", +}; + +static const char *mmhub_client_ids_v3_3_1[][2] = { [0][0] = "VMC", [4][0] = "DCEDMC", [6][0] = "MP0", @@ -46,10 +104,29 @@ static const char *mmhub_client_ids_v3_3[][2] = { [8][0] = "MPM", [24][0] = "HDP", [25][0] = "LSDMA", - [26][0] = "JPEG", - [27][0] = "VPE", - [29][0] = "VCNU", - [30][0] = "VCN", + [26][0] = "JPEG0", + [27][0] = "VPE0", + [28][0] = "VSCH", + [29][0] = "VCNU0", + [30][0] = "VCN0", + [32+1][0] = "ISPXT", + [32+2][0] = "ISPIXT", + [32+9][0] = "ISPPDPRD", + [32+10][0] = "ISPCSTATRD", + [32+11][0] = "ISPBYRPRD", + [32+12][0] = "ISPRGBPRD", + [32+13][0] = "ISPMCFPRD", + [32+14][0] = "ISPMCFPRD1", + [32+15][0] = "ISPYUVPRD", + [32+16][0] = "ISPMCSCRD", + [32+17][0] = "ISPGDCRD", + [32+18][0] = "ISPLMERD", + [32+22][0] = "ISPXT1", + [32+23][0] = "ISPIXT1", + [32+26][0] = "JPEG1", + [32+27][0] = "VPE1", + [32+29][0] = "VCNU1", + [32+30][0] = "VCN1", [3][1] = "DCEDWB", [4][1] = "DCEDMC", [6][1] = "MP0", @@ -58,10 +135,32 @@ static const char *mmhub_client_ids_v3_3[][2] = { [21][1] = "OSSSYS", [24][1] = "HDP", [25][1] = "LSDMA", - [26][1] = "JPEG", - [27][1] = "VPE", - [29][1] = "VCNU", - [30][1] = "VCN", + [26][1] = "JPEG0", + [27][1] = "VPE0", + [28][1] = "VSCH", + [29][1] = "VCNU0", + [30][1] = "VCN0", + [32+1][1] = "ISPXT", + [32+2][1] = "ISPIXT", + [32+5][1] = "ISPCSISWR", + [32+9][1] = "ISPPDPWR", + [32+10][1] = "ISPCSTATWR", + [32+11][1] = "ISPBYRPWR", + [32+12][1] = "ISPRGBPWR", + [32+13][1] = "ISPMCFPWR", + [32+14][1] = "ISPMWR0", + [32+15][1] = "ISPYUVPWR", + [32+16][1] = "ISPMCSCWR", + [32+17][1] = "ISPGDCWR", + [32+18][1] = "ISPLMEWR", + [32+19][1] = "ISPMWR1", + [32+20][1] = "ISPMWR2", + [32+22][1] = "ISPXT1", + [32+23][1] = "ISPIXT1", + [32+26][1] = "JPEG1", + [32+27][1] = "VPE1", + [32+29][1] = "VCNU1", + [32+30][1] = "VCN1", }; static uint32_t mmhub_v3_3_get_invalidate_req(unsigned int vmid, @@ -102,12 +201,16 @@ mmhub_v3_3_print_l2_protection_fault_status(struct amdgpu_device *adev, switch (amdgpu_ip_version(adev, MMHUB_HWIP, 0)) { case IP_VERSION(3, 3, 0): - case IP_VERSION(3, 3, 1): case IP_VERSION(3, 3, 2): mmhub_cid = cid < ARRAY_SIZE(mmhub_client_ids_v3_3) ? mmhub_client_ids_v3_3[cid][rw] : cid == 0x140 ? "UMSCH" : NULL; break; + case IP_VERSION(3, 3, 1): + mmhub_cid = cid < ARRAY_SIZE(mmhub_client_ids_v3_3_1) ? + mmhub_client_ids_v3_3_1[cid][rw] : + cid == 0x140 ? "UMSCH" : NULL; + break; default: mmhub_cid = NULL; break; diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c index 48101a34e049..9a40107a0869 100644 --- a/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c @@ -292,14 +292,32 @@ static void xgpu_ai_mailbox_flr_work(struct work_struct *work) } } -static void xgpu_ai_mailbox_bad_pages_work(struct work_struct *work) +static void xgpu_ai_mailbox_req_bad_pages_work(struct work_struct *work) { - struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, bad_pages_work); + struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, req_bad_pages_work); struct amdgpu_device *adev = container_of(virt, struct amdgpu_device, virt); if (down_read_trylock(&adev->reset_domain->sem)) { amdgpu_virt_fini_data_exchange(adev); amdgpu_virt_request_bad_pages(adev); + up_read(&adev->reset_domain->sem); + } +} + +/** + * xgpu_ai_mailbox_handle_bad_pages_work - Reinitialize the data exchange region to get fresh bad page information + * @work: pointer to the work_struct + * + * This work handler is triggered when bad pages are ready, and it reinitializes + * the data exchange region to retrieve updated bad page information from the host. + */ +static void xgpu_ai_mailbox_handle_bad_pages_work(struct work_struct *work) +{ + struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, handle_bad_pages_work); + struct amdgpu_device *adev = container_of(virt, struct amdgpu_device, virt); + + if (down_read_trylock(&adev->reset_domain->sem)) { + amdgpu_virt_fini_data_exchange(adev); amdgpu_virt_init_data_exchange(adev); up_read(&adev->reset_domain->sem); } @@ -327,10 +345,15 @@ static int xgpu_ai_mailbox_rcv_irq(struct amdgpu_device *adev, struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); switch (event) { + case IDH_RAS_BAD_PAGES_READY: + xgpu_ai_mailbox_send_ack(adev); + if (amdgpu_sriov_runtime(adev)) + schedule_work(&adev->virt.handle_bad_pages_work); + break; case IDH_RAS_BAD_PAGES_NOTIFICATION: xgpu_ai_mailbox_send_ack(adev); if (amdgpu_sriov_runtime(adev)) - schedule_work(&adev->virt.bad_pages_work); + schedule_work(&adev->virt.req_bad_pages_work); break; case IDH_UNRECOV_ERR_NOTIFICATION: xgpu_ai_mailbox_send_ack(adev); @@ -415,7 +438,8 @@ int xgpu_ai_mailbox_get_irq(struct amdgpu_device *adev) } INIT_WORK(&adev->virt.flr_work, xgpu_ai_mailbox_flr_work); - INIT_WORK(&adev->virt.bad_pages_work, xgpu_ai_mailbox_bad_pages_work); + INIT_WORK(&adev->virt.req_bad_pages_work, xgpu_ai_mailbox_req_bad_pages_work); + INIT_WORK(&adev->virt.handle_bad_pages_work, xgpu_ai_mailbox_handle_bad_pages_work); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c index f6d8597452ed..457972aa5632 100644 --- a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c @@ -202,9 +202,6 @@ static int xgpu_nv_send_access_requests_with_param(struct amdgpu_device *adev, case IDH_REQ_RAS_CPER_DUMP: event = IDH_RAS_CPER_DUMP_READY; break; - case IDH_REQ_RAS_BAD_PAGES: - event = IDH_RAS_BAD_PAGES_READY; - break; default: break; } @@ -359,14 +356,32 @@ static void xgpu_nv_mailbox_flr_work(struct work_struct *work) } } -static void xgpu_nv_mailbox_bad_pages_work(struct work_struct *work) +static void xgpu_nv_mailbox_req_bad_pages_work(struct work_struct *work) { - struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, bad_pages_work); + struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, req_bad_pages_work); struct amdgpu_device *adev = container_of(virt, struct amdgpu_device, virt); if (down_read_trylock(&adev->reset_domain->sem)) { amdgpu_virt_fini_data_exchange(adev); amdgpu_virt_request_bad_pages(adev); + up_read(&adev->reset_domain->sem); + } +} + +/** + * xgpu_nv_mailbox_handle_bad_pages_work - Reinitialize the data exchange region to get fresh bad page information + * @work: pointer to the work_struct + * + * This work handler is triggered when bad pages are ready, and it reinitializes + * the data exchange region to retrieve updated bad page information from the host. + */ +static void xgpu_nv_mailbox_handle_bad_pages_work(struct work_struct *work) +{ + struct amdgpu_virt *virt = container_of(work, struct amdgpu_virt, handle_bad_pages_work); + struct amdgpu_device *adev = container_of(virt, struct amdgpu_device, virt); + + if (down_read_trylock(&adev->reset_domain->sem)) { + amdgpu_virt_fini_data_exchange(adev); amdgpu_virt_init_data_exchange(adev); up_read(&adev->reset_domain->sem); } @@ -397,10 +412,15 @@ static int xgpu_nv_mailbox_rcv_irq(struct amdgpu_device *adev, struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); switch (event) { + case IDH_RAS_BAD_PAGES_READY: + xgpu_nv_mailbox_send_ack(adev); + if (amdgpu_sriov_runtime(adev)) + schedule_work(&adev->virt.handle_bad_pages_work); + break; case IDH_RAS_BAD_PAGES_NOTIFICATION: xgpu_nv_mailbox_send_ack(adev); if (amdgpu_sriov_runtime(adev)) - schedule_work(&adev->virt.bad_pages_work); + schedule_work(&adev->virt.req_bad_pages_work); break; case IDH_UNRECOV_ERR_NOTIFICATION: xgpu_nv_mailbox_send_ack(adev); @@ -485,7 +505,8 @@ int xgpu_nv_mailbox_get_irq(struct amdgpu_device *adev) } INIT_WORK(&adev->virt.flr_work, xgpu_nv_mailbox_flr_work); - INIT_WORK(&adev->virt.bad_pages_work, xgpu_nv_mailbox_bad_pages_work); + INIT_WORK(&adev->virt.req_bad_pages_work, xgpu_nv_mailbox_req_bad_pages_work); + INIT_WORK(&adev->virt.handle_bad_pages_work, xgpu_nv_mailbox_handle_bad_pages_work); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c index d5002ff931d8..860bc5cb03c8 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c @@ -151,9 +151,9 @@ static void nbio_v7_4_sdma_doorbell_range(struct amdgpu_device *adev, int instan * BIF_SDMA0_DOORBELL_RANGE: 0x3bc0 * BIF_SDMA1_DOORBELL_RANGE: 0x3bc4 * BIF_SDMA2_DOORBELL_RANGE: 0x3bd8 -+ * BIF_SDMA4_DOORBELL_RANGE: -+ * ARCTURUS: 0x3be0 -+ * ALDEBARAN: 0x3be4 + * BIF_SDMA4_DOORBELL_RANGE: + * ARCTURUS: 0x3be0 + * ALDEBARAN: 0x3be4 */ if (adev->asic_type == CHIP_ALDEBARAN && instance == 4) reg = instance + 0x4 + 0x1 + diff --git a/drivers/gpu/drm/amd/amdgpu/nv.h b/drivers/gpu/drm/amd/amdgpu/nv.h index 83e9782aef39..8f4817404f10 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.h +++ b/drivers/gpu/drm/amd/amdgpu/nv.h @@ -31,5 +31,6 @@ extern const struct amdgpu_ip_block_version nv_common_ip_block; void nv_grbm_select(struct amdgpu_device *adev, u32 me, u32 pipe, u32 queue, u32 vmid); void nv_set_virt_ops(struct amdgpu_device *adev); +int cyan_skillfish_reg_base_init(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 6cc05d36e359..64b240b51f1a 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -149,12 +149,12 @@ static int psp_v11_0_wait_for_bootloader(struct psp_context *psp) int ret; int retry_loop; - for (retry_loop = 0; retry_loop < 10; retry_loop++) { + for (retry_loop = 0; retry_loop < 20; retry_loop++) { /* Wait for bootloader to signify that is ready having bit 31 of C2PMSG_35 set to 1 */ ret = psp_wait_for( psp, SOC15_REG_OFFSET(MP0, 0, mmMP0_SMN_C2PMSG_35), - 0x80000000, 0x80000000, PSP_WAITREG_NOVERBOSE); + 0x80000000, 0x8000FFFF, PSP_WAITREG_NOVERBOSE); if (ret == 0) return 0; @@ -397,18 +397,6 @@ static int psp_v11_0_mode1_reset(struct psp_context *psp) msleep(500); - offset = SOC15_REG_OFFSET(MP0, 0, mmMP0_SMN_C2PMSG_33); - - ret = psp_wait_for(psp, offset, MBOX_TOS_RESP_FLAG, MBOX_TOS_RESP_MASK, - 0); - - if (ret) { - DRM_INFO("psp mode 1 reset failed!\n"); - return -EINVAL; - } - - DRM_INFO("psp mode1 reset succeed \n"); - return 0; } @@ -665,7 +653,8 @@ static const struct psp_funcs psp_v11_0_funcs = { .ring_get_wptr = psp_v11_0_ring_get_wptr, .ring_set_wptr = psp_v11_0_ring_set_wptr, .load_usbc_pd_fw = psp_v11_0_load_usbc_pd_fw, - .read_usbc_pd_fw = psp_v11_0_read_usbc_pd_fw + .read_usbc_pd_fw = psp_v11_0_read_usbc_pd_fw, + .wait_for_bootloader = psp_v11_0_wait_for_bootloader }; void psp_v11_0_set_psp_funcs(struct psp_context *psp) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index e6d8eddda2bf..db6e41967f12 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -1377,7 +1377,7 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block) switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) { case IP_VERSION(6, 0, 0): - if ((adev->sdma.instance[0].fw_version >= 24) && !adev->sdma.disable_uq) + if ((adev->sdma.instance[0].fw_version >= 27) && !adev->sdma.disable_uq) adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; break; case IP_VERSION(6, 0, 1): @@ -1385,11 +1385,11 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block) adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; break; case IP_VERSION(6, 0, 2): - if ((adev->sdma.instance[0].fw_version >= 21) && !adev->sdma.disable_uq) + if ((adev->sdma.instance[0].fw_version >= 23) && !adev->sdma.disable_uq) adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; break; case IP_VERSION(6, 0, 3): - if ((adev->sdma.instance[0].fw_version >= 25) && !adev->sdma.disable_uq) + if ((adev->sdma.instance[0].fw_version >= 27) && !adev->sdma.disable_uq) adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; break; case IP_VERSION(6, 1, 0): diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index b8b06d4c5882..326ecc8d37d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -1353,7 +1353,7 @@ static int sdma_v7_0_sw_init(struct amdgpu_ip_block *ip_block) switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) { case IP_VERSION(7, 0, 0): case IP_VERSION(7, 0, 1): - if ((adev->sdma.instance[0].fw_version >= 7836028) && !adev->sdma.disable_uq) + if ((adev->sdma.instance[0].fw_version >= 7966358) && !adev->sdma.disable_uq) adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index c457be3a3c56..9785fada4fa7 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -741,7 +741,6 @@ static void soc15_reg_base_init(struct amdgpu_device *adev) void soc15_set_virt_ops(struct amdgpu_device *adev) { adev->virt.ops = &xgpu_ai_virt_ops; - /* init soc15 reg base early enough so we can * request request full access for sriov before * set_ip_blocks. */ @@ -1218,6 +1217,8 @@ static int soc15_common_early_init(struct amdgpu_ip_block *ip_block) AMD_PG_SUPPORT_JPEG; /*TODO: need a new external_rev_id for GC 9.4.4? */ adev->external_rev_id = adev->rev_id + 0x46; + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0)) + adev->external_rev_id = adev->rev_id + 0x50; break; default: /* FIXME: not supported yet */ diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c index e590cbdd8de9..8dc32787d625 100644 --- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c @@ -536,8 +536,11 @@ static int umc_v12_0_update_ecc_status(struct amdgpu_device *adev, hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID); mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType); - if ((hwid != MCA_UMC_HWID_V12_0) || (mcatype != MCA_UMC_MCATYPE_V12_0)) + /* The IP block decode of consumption is SMU */ + if (hwid != MCA_UMC_HWID_V12_0 || mcatype != MCA_UMC_MCATYPE_V12_0) { + con->umc_ecc_log.consumption_q_count++; return 0; + } if (!status) return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c index 5dbaebb592b3..2e79a3afc774 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c @@ -623,7 +623,22 @@ static void uvd_v3_1_enable_mgcg(struct amdgpu_device *adev, * * @ip_block: Pointer to the amdgpu_ip_block for this hw instance. * - * Initialize the hardware, boot up the VCPU and do some testing + * Initialize the hardware, boot up the VCPU and do some testing. + * + * On SI, the UVD is meant to be used in a specific power state, + * or alternatively the driver can manually enable its clock. + * In amdgpu we use the dedicated UVD power state when DPM is enabled. + * Calling amdgpu_dpm_enable_uvd makes DPM select the UVD power state + * for the SMU and afterwards enables the UVD clock. + * This is automatically done by amdgpu_uvd_ring_begin_use when work + * is submitted to the UVD ring. Here, we have to call it manually + * in order to power up UVD before firmware validation. + * + * Note that we must not disable the UVD clock here, as that would + * cause the ring test to fail. However, UVD is powered off + * automatically after the ring test: amdgpu_uvd_ring_end_use calls + * the UVD idle work handler which will disable the UVD clock when + * all fences are signalled. */ static int uvd_v3_1_hw_init(struct amdgpu_ip_block *ip_block) { @@ -633,6 +648,15 @@ static int uvd_v3_1_hw_init(struct amdgpu_ip_block *ip_block) int r; uvd_v3_1_mc_resume(adev); + uvd_v3_1_enable_mgcg(adev, true); + + /* Make sure UVD is powered during FW validation. + * It's going to be automatically powered off after the ring test. + */ + if (adev->pm.dpm_enabled) + amdgpu_dpm_enable_uvd(adev, true); + else + amdgpu_asic_set_uvd_clocks(adev, 53300, 40000); r = uvd_v3_1_fw_validate(adev); if (r) { @@ -640,9 +664,6 @@ static int uvd_v3_1_hw_init(struct amdgpu_ip_block *ip_block) return r; } - uvd_v3_1_enable_mgcg(adev, true); - amdgpu_asic_set_uvd_clocks(adev, 53300, 40000); - uvd_v3_1_start(adev); r = amdgpu_ring_test_helper(ring); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index 1c07b701d0e4..ceb94bbb03a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -217,7 +217,8 @@ static int uvd_v6_0_enc_get_create_msg(struct amdgpu_ring *ring, uint32_t handle int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job); + AMDGPU_IB_POOL_DIRECT, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -281,7 +282,8 @@ static int uvd_v6_0_enc_get_destroy_msg(struct amdgpu_ring *ring, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job); + AMDGPU_IB_POOL_DIRECT, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c index 9d237b5937fb..1f8866f3f63c 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c @@ -225,7 +225,8 @@ static int uvd_v7_0_enc_get_create_msg(struct amdgpu_ring *ring, u32 handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job); + AMDGPU_IB_POOL_DIRECT, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; @@ -288,7 +289,8 @@ static int uvd_v7_0_enc_get_destroy_msg(struct amdgpu_ring *ring, u32 handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job); + AMDGPU_IB_POOL_DIRECT, &job, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c index c74947705d77..1e89ba153d9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c @@ -1338,7 +1338,6 @@ static int vcn_v1_0_pause_dpg_mode(struct amdgpu_vcn_inst *vinst, WREG32_SOC15(UVD, 0, mmUVD_RB_RPTR2, lower_32_bits(ring->wptr)); WREG32_SOC15(UVD, 0, mmUVD_RB_WPTR2, lower_32_bits(ring->wptr)); - ring = &adev->vcn.inst->ring_dec; WREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR, RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2) & 0x7FFFFFFF); SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, @@ -1399,7 +1398,6 @@ static int vcn_v1_0_pause_dpg_mode(struct amdgpu_vcn_inst *vinst, WREG32_SOC15(UVD, 0, mmUVD_JRBC_RB_CNTL, UVD_JRBC_RB_CNTL__RB_RPTR_WR_EN_MASK); - ring = &adev->vcn.inst->ring_dec; WREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR, RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2) & 0x7FFFFFFF); SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c index 68b4371df0f1..b115137ab2d6 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c @@ -136,8 +136,6 @@ static int vcn_v2_0_sw_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_ring *ring; int i, r; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_0); - uint32_t *ptr; struct amdgpu_device *adev = ip_block->adev; volatile struct amdgpu_fw_shared *fw_shared; @@ -232,14 +230,9 @@ static int vcn_v2_0_sw_init(struct amdgpu_ip_block *ip_block) if (amdgpu_vcnfw_log) amdgpu_vcn_fwlog_init(adev->vcn.inst); - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_2_0, ARRAY_SIZE(vcn_reg_list_2_0)); + if (r) + return r; r = amdgpu_vcn_sysfs_reset_mask_init(adev); if (r) @@ -276,8 +269,6 @@ static int vcn_v2_0_sw_fini(struct amdgpu_ip_block *ip_block) r = amdgpu_vcn_sw_fini(adev, 0); - kfree(adev->vcn.ip_dump); - return r; } @@ -865,6 +856,7 @@ static int vcn_v2_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) volatile struct amdgpu_fw_shared *fw_shared = adev->vcn.inst->fw_shared.cpu_addr; struct amdgpu_ring *ring = &adev->vcn.inst->ring_dec; uint32_t rb_bufsz, tmp; + int ret; vcn_v2_0_enable_static_power_gating(vinst); @@ -948,8 +940,13 @@ static int vcn_v2_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) UVD, 0, mmUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, 0, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, 0, 0); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } /* force RBC into idle state */ rb_bufsz = order_base_2(ring->ring_size); @@ -2095,66 +2092,6 @@ static int vcn_v2_0_start_sriov(struct amdgpu_device *adev) return vcn_v2_0_start_mmsch(adev, &adev->virt.mm_table); } -static void vcn_v2_0_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_0); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_2_0[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v2_0_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_0); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, mmUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_2_0[j], i)); - } -} - static const struct amd_ip_funcs vcn_v2_0_ip_funcs = { .name = "vcn_v2_0", .early_init = vcn_v2_0_early_init, @@ -2168,8 +2105,8 @@ static const struct amd_ip_funcs vcn_v2_0_ip_funcs = { .wait_for_idle = vcn_v2_0_wait_for_idle, .set_clockgating_state = vcn_v2_0_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v2_0_dump_ip_state, - .print_ip_state = vcn_v2_0_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; static const struct amdgpu_ring_funcs vcn_v2_0_dec_ring_vm_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c index bc30a5326866..904b94bc8693 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c @@ -116,7 +116,6 @@ static void vcn_v2_5_idle_work_handler(struct work_struct *work) struct amdgpu_device *adev = vcn_inst->adev; unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0}; unsigned int i, j; - int r = 0; for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { struct amdgpu_vcn_inst *v = &adev->vcn.inst[i]; @@ -149,15 +148,7 @@ static void vcn_v2_5_idle_work_handler(struct work_struct *work) if (!fences && !atomic_read(&adev->vcn.inst[0].total_submission_cnt)) { amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCN, AMD_PG_STATE_GATE); - mutex_lock(&adev->vcn.workload_profile_mutex); - if (adev->vcn.workload_profile_active) { - r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, - false); - if (r) - dev_warn(adev->dev, "(%d) failed to disable video power profile mode\n", r); - adev->vcn.workload_profile_active = false; - } - mutex_unlock(&adev->vcn.workload_profile_mutex); + amdgpu_vcn_put_profile(adev); } else { schedule_delayed_work(&adev->vcn.inst[0].idle_work, VCN_IDLE_TIMEOUT); } @@ -167,7 +158,6 @@ static void vcn_v2_5_ring_begin_use(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct amdgpu_vcn_inst *v = &adev->vcn.inst[ring->me]; - int r = 0; atomic_inc(&adev->vcn.inst[0].total_submission_cnt); @@ -177,20 +167,6 @@ static void vcn_v2_5_ring_begin_use(struct amdgpu_ring *ring) * the delayed work so there is no one else to set it to false * and we don't care if someone else sets it to true. */ - if (adev->vcn.workload_profile_active) - goto pg_lock; - - mutex_lock(&adev->vcn.workload_profile_mutex); - if (!adev->vcn.workload_profile_active) { - r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, - true); - if (r) - dev_warn(adev->dev, "(%d) failed to switch to video power profile mode\n", r); - adev->vcn.workload_profile_active = true; - } - mutex_unlock(&adev->vcn.workload_profile_mutex); - -pg_lock: mutex_lock(&adev->vcn.inst[0].vcn_pg_lock); amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCN, AMD_PG_STATE_UNGATE); @@ -218,6 +194,7 @@ static void vcn_v2_5_ring_begin_use(struct amdgpu_ring *ring) v->pause_dpg_mode(v, &new_state); } mutex_unlock(&adev->vcn.inst[0].vcn_pg_lock); + amdgpu_vcn_get_profile(adev); } static void vcn_v2_5_ring_end_use(struct amdgpu_ring *ring) @@ -297,8 +274,6 @@ static int vcn_v2_5_sw_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_ring *ring; int i, j, r; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_5); - uint32_t *ptr; struct amdgpu_device *adev = ip_block->adev; for (j = 0; j < adev->vcn.num_vcn_inst; j++) { @@ -423,14 +398,9 @@ static int vcn_v2_5_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_2_5, ARRAY_SIZE(vcn_reg_list_2_5)); + if (r) + return r; r = amdgpu_vcn_sysfs_reset_mask_init(adev); if (r) @@ -477,8 +447,6 @@ static int vcn_v2_5_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); - return 0; } @@ -1035,6 +1003,7 @@ static int vcn_v2_5_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) volatile struct amdgpu_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; uint32_t rb_bufsz, tmp; + int ret; /* disable register anti-hang mechanism */ WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), 1, @@ -1125,8 +1094,13 @@ static int vcn_v2_5_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) VCN, 0, mmUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_dec; /* force RBC into idle state */ @@ -2127,66 +2101,6 @@ static void vcn_v2_5_set_irq_funcs(struct amdgpu_device *adev) } } -static void vcn_v2_5_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_5); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_2_5[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v2_5_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_2_5); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, mmUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_2_5[j], i)); - } -} - static const struct amd_ip_funcs vcn_v2_5_ip_funcs = { .name = "vcn_v2_5", .early_init = vcn_v2_5_early_init, @@ -2200,8 +2114,8 @@ static const struct amd_ip_funcs vcn_v2_5_ip_funcs = { .wait_for_idle = vcn_v2_5_wait_for_idle, .set_clockgating_state = vcn_v2_5_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v2_5_dump_ip_state, - .print_ip_state = vcn_v2_5_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; static const struct amd_ip_funcs vcn_v2_6_ip_funcs = { @@ -2217,8 +2131,8 @@ static const struct amd_ip_funcs vcn_v2_6_ip_funcs = { .wait_for_idle = vcn_v2_5_wait_for_idle, .set_clockgating_state = vcn_v2_5_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v2_5_dump_ip_state, - .print_ip_state = vcn_v2_5_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v2_5_ip_block = diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index 4b8f4407047f..f3085137ba08 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -175,8 +175,6 @@ static int vcn_v3_0_sw_init(struct amdgpu_ip_block *ip_block) struct amdgpu_ring *ring; int i, j, r; int vcn_doorbell_index = 0; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_3_0); - uint32_t *ptr; struct amdgpu_device *adev = ip_block->adev; /* @@ -304,14 +302,9 @@ static int vcn_v3_0_sw_init(struct amdgpu_ip_block *ip_block) return r; } - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (ptr == NULL) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_3_0, ARRAY_SIZE(vcn_reg_list_3_0)); + if (r) + return r; r = amdgpu_vcn_sysfs_reset_mask_init(adev); if (r) @@ -361,7 +354,6 @@ static int vcn_v3_0_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); return 0; } @@ -1042,6 +1034,7 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) volatile struct amdgpu_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; uint32_t rb_bufsz, tmp; + int ret; /* disable register anti-hang mechanism */ WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, mmUVD_POWER_STATUS), 1, @@ -1134,8 +1127,13 @@ static int vcn_v3_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET( VCN, inst_idx, mmUVD_VCPU_CNTL), tmp, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_dec; /* force RBC into idle state */ @@ -1888,15 +1886,19 @@ static int vcn_v3_0_limit_sched(struct amdgpu_cs_parser *p, struct amdgpu_job *job) { struct drm_gpu_scheduler **scheds; - - /* The create msg must be in the first IB submitted */ - if (atomic_read(&job->base.entity->fence_seq)) - return -EINVAL; + struct dma_fence *fence; /* if VCN0 is harvested, we can't support AV1 */ if (p->adev->vcn.harvest_config & AMDGPU_VCN_HARVEST_VCN0) return -EINVAL; + /* wait for all jobs to finish before switching to instance 0 */ + fence = amdgpu_ctx_get_fence(p->ctx, job->base.entity, ~0ull); + if (fence) { + dma_fence_wait(fence, false); + dma_fence_put(fence); + } + scheds = p->adev->gpu_sched[AMDGPU_HW_IP_VCN_DEC] [AMDGPU_RING_PRIO_DEFAULT].sched; drm_sched_entity_modify_sched(job->base.entity, scheds, 1); @@ -2342,67 +2344,6 @@ static void vcn_v3_0_set_irq_funcs(struct amdgpu_device *adev) } } -static void vcn_v3_0_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_3_0); - uint32_t inst_off; - bool is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_3_0[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v3_0_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_3_0); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, mmUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_3_0[j], i)); - } -} - static const struct amd_ip_funcs vcn_v3_0_ip_funcs = { .name = "vcn_v3_0", .early_init = vcn_v3_0_early_init, @@ -2416,8 +2357,8 @@ static const struct amd_ip_funcs vcn_v3_0_ip_funcs = { .wait_for_idle = vcn_v3_0_wait_for_idle, .set_clockgating_state = vcn_v3_0_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v3_0_dump_ip_state, - .print_ip_state = vcn_v3_0_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v3_0_ip_block = { diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 1924e075b66f..bc9dfe5ffea7 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -183,8 +183,6 @@ static int vcn_v4_0_sw_init(struct amdgpu_ip_block *ip_block) struct amdgpu_ring *ring; struct amdgpu_device *adev = ip_block->adev; int i, r; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0); - uint32_t *ptr; for (i = 0; i < adev->vcn.num_vcn_inst; i++) { if (adev->vcn.harvest_config & (1 << i)) @@ -255,14 +253,9 @@ static int vcn_v4_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_4_0, ARRAY_SIZE(vcn_reg_list_4_0)); + if (r) + return r; r = amdgpu_vcn_sysfs_reset_mask_init(adev); if (r) @@ -315,8 +308,6 @@ static int vcn_v4_0_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); - return 0; } @@ -1012,6 +1003,7 @@ static int vcn_v4_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) volatile struct amdgpu_vcn4_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; uint32_t tmp; + int ret; /* disable register anti-hang mechanism */ WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, regUVD_POWER_STATUS), 1, @@ -1094,8 +1086,13 @@ static int vcn_v4_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, bool indirect) UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_enc[0]; @@ -1624,7 +1621,6 @@ static int vcn_v4_0_stop(struct amdgpu_vcn_inst *vinst) if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) { vcn_v4_0_stop_dpg_mode(vinst); - r = 0; goto done; } @@ -1808,15 +1804,19 @@ static int vcn_v4_0_limit_sched(struct amdgpu_cs_parser *p, struct amdgpu_job *job) { struct drm_gpu_scheduler **scheds; - - /* The create msg must be in the first IB submitted */ - if (atomic_read(&job->base.entity->fence_seq)) - return -EINVAL; + struct dma_fence *fence; /* if VCN0 is harvested, we can't support AV1 */ if (p->adev->vcn.harvest_config & AMDGPU_VCN_HARVEST_VCN0) return -EINVAL; + /* wait for all jobs to finish before switching to instance 0 */ + fence = amdgpu_ctx_get_fence(p->ctx, job->base.entity, ~0ull); + if (fence) { + dma_fence_wait(fence, false); + dma_fence_put(fence); + } + scheds = p->adev->gpu_sched[AMDGPU_HW_IP_VCN_ENC] [AMDGPU_RING_PRIO_0].sched; drm_sched_entity_modify_sched(job->base.entity, scheds, 1); @@ -1907,22 +1907,16 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, #define RADEON_VCN_ENGINE_TYPE_ENCODE (0x00000002) #define RADEON_VCN_ENGINE_TYPE_DECODE (0x00000003) - #define RADEON_VCN_ENGINE_INFO (0x30000001) -#define RADEON_VCN_ENGINE_INFO_MAX_OFFSET 16 - #define RENCODE_ENCODE_STANDARD_AV1 2 #define RENCODE_IB_PARAM_SESSION_INIT 0x00000003 -#define RENCODE_IB_PARAM_SESSION_INIT_MAX_OFFSET 64 -/* return the offset in ib if id is found, -1 otherwise - * to speed up the searching we only search upto max_offset - */ -static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int max_offset) +/* return the offset in ib if id is found, -1 otherwise */ +static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start) { int i; - for (i = 0; i < ib->length_dw && i < max_offset && ib->ptr[i] >= 8; i += ib->ptr[i]/4) { + for (i = start; i < ib->length_dw && ib->ptr[i] >= 8; i += ib->ptr[i] / 4) { if (ib->ptr[i + 1] == id) return i; } @@ -1937,33 +1931,29 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, struct amdgpu_vcn_decode_buffer *decode_buffer; uint64_t addr; uint32_t val; - int idx; + int idx = 0, sidx; /* The first instance can decode anything */ if (!ring->me) return 0; - /* RADEON_VCN_ENGINE_INFO is at the top of ib block */ - idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, - RADEON_VCN_ENGINE_INFO_MAX_OFFSET); - if (idx < 0) /* engine info is missing */ - return 0; + 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]; - 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]; + if (!(decode_buffer->valid_buf_flag & 0x1)) + return 0; - if (!(decode_buffer->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); - } else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) { - idx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, - RENCODE_IB_PARAM_SESSION_INIT_MAX_OFFSET); - if (idx >= 0 && ib->ptr[idx + 2] == RENCODE_ENCODE_STANDARD_AV1) - return vcn_v4_0_limit_sched(p, job); + addr = ((u64)decode_buffer->msg_buffer_address_hi) << 32 | + decode_buffer->msg_buffer_address_lo; + return vcn_v4_0_dec_msg(p, job, 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) + return vcn_v4_0_limit_sched(p, job); + } + idx += ib->ptr[idx] / 4; } return 0; } @@ -2246,67 +2236,6 @@ static void vcn_v4_0_set_irq_funcs(struct amdgpu_device *adev) } } -static void vcn_v4_0_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v4_0_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, regUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0[j], - i)); - } -} - static const struct amd_ip_funcs vcn_v4_0_ip_funcs = { .name = "vcn_v4_0", .early_init = vcn_v4_0_early_init, @@ -2320,8 +2249,8 @@ static const struct amd_ip_funcs vcn_v4_0_ip_funcs = { .wait_for_idle = vcn_v4_0_wait_for_idle, .set_clockgating_state = vcn_v4_0_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v4_0_dump_ip_state, - .print_ip_state = vcn_v4_0_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v4_0_ip_block = { 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 2a3663b551af..7b93a275ec4f 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c @@ -134,6 +134,19 @@ static int vcn_v4_0_3_early_init(struct amdgpu_ip_block *ip_block) return 0; } +static int vcn_v4_0_3_late_init(struct amdgpu_ip_block *ip_block) +{ + struct amdgpu_device *adev = ip_block->adev; + + adev->vcn.supported_reset = + amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]); + + if (amdgpu_dpm_reset_vcn_is_supported(adev)) + adev->vcn.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE; + + return 0; +} + static int vcn_v4_0_3_fw_shared_init(struct amdgpu_device *adev, int inst_idx) { struct amdgpu_vcn4_fw_shared *fw_shared; @@ -160,8 +173,6 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; struct amdgpu_ring *ring; int i, r, vcn_inst; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3); - uint32_t *ptr; /* VCN DEC TRAP */ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_VCN, @@ -213,10 +224,6 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) adev->vcn.inst[i].pause_dpg_mode = vcn_v4_0_3_pause_dpg_mode; } - /* TODO: Add queue reset mask when FW fully supports it */ - adev->vcn.supported_reset = - amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]); - if (amdgpu_sriov_vf(adev)) { r = amdgpu_virt_alloc_mm_table(adev); if (r) @@ -231,20 +238,11 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) } } - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } - - r = amdgpu_vcn_sysfs_reset_mask_init(adev); + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_4_0_3, ARRAY_SIZE(vcn_reg_list_4_0_3)); if (r) return r; - return 0; + return amdgpu_vcn_sysfs_reset_mask_init(adev); } /** @@ -287,8 +285,6 @@ static int vcn_v4_0_3_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); - return 0; } @@ -391,7 +387,7 @@ static int vcn_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block) vinst->set_pg_state(vinst, AMD_PG_STATE_GATE); } - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN) && !amdgpu_sriov_vf(adev)) amdgpu_irq_put(adev, &adev->vcn.inst->ras_poison_irq, 0); return 0; @@ -851,7 +847,7 @@ static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_vcn_inst *vinst, volatile struct amdgpu_vcn4_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; - int vcn_inst; + int vcn_inst, ret; uint32_t tmp; vcn_inst = GET_INST(VCN, inst_idx); @@ -944,8 +940,13 @@ static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_vcn_inst *vinst, VCN, 0, regUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_enc[0]; @@ -1872,71 +1873,10 @@ static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev) adev->vcn.inst->ras_poison_irq.funcs = &vcn_v4_0_3_ras_irq_funcs; } -static void vcn_v4_0_3_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_3[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v4_0_3_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off, inst_id; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_id = GET_INST(VCN, i); - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, inst_id, regUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_3[j], - inst_id)); - } -} - static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = { .name = "vcn_v4_0_3", .early_init = vcn_v4_0_3_early_init, + .late_init = vcn_v4_0_3_late_init, .sw_init = vcn_v4_0_3_sw_init, .sw_fini = vcn_v4_0_3_sw_fini, .hw_init = vcn_v4_0_3_hw_init, @@ -1947,8 +1887,8 @@ static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = { .wait_for_idle = vcn_v4_0_3_wait_for_idle, .set_clockgating_state = vcn_v4_0_3_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v4_0_3_dump_ip_state, - .print_ip_state = vcn_v4_0_3_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v4_0_3_ip_block = { 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 caf2d95a85d4..6dbf33b26ee2 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c @@ -147,9 +147,6 @@ static int vcn_v4_0_5_sw_init(struct amdgpu_ip_block *ip_block) struct amdgpu_ring *ring; struct amdgpu_device *adev = ip_block->adev; int i, r; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_5); - uint32_t *ptr; - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { volatile struct amdgpu_vcn4_fw_shared *fw_shared; @@ -233,15 +230,9 @@ static int vcn_v4_0_5_sw_init(struct amdgpu_ip_block *ip_block) return r; } - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } - return 0; + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_4_0_5, ARRAY_SIZE(vcn_reg_list_4_0_5)); + + return r; } /** @@ -284,8 +275,6 @@ static int vcn_v4_0_5_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); - return 0; } @@ -926,6 +915,7 @@ static int vcn_v4_0_5_start_dpg_mode(struct amdgpu_vcn_inst *vinst, volatile struct amdgpu_vcn4_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; uint32_t tmp; + int ret; /* disable register anti-hang mechanism */ WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, regUVD_POWER_STATUS), 1, @@ -1006,8 +996,13 @@ static int vcn_v4_0_5_start_dpg_mode(struct amdgpu_vcn_inst *vinst, VCN, inst_idx, regUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_enc[0]; @@ -1596,7 +1591,7 @@ static int vcn_v4_0_5_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { struct amdgpu_device *adev = ip_block->adev; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; + bool enable = state == AMD_CG_STATE_GATE; int i; for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { @@ -1704,67 +1699,6 @@ static void vcn_v4_0_5_set_irq_funcs(struct amdgpu_device *adev) } } -static void vcn_v4_0_5_print_ip_state(struct amdgpu_ip_block *ip_block, struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_5); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_5[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -static void vcn_v4_0_5_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_5); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, regUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_5[j], - i)); - } -} - static const struct amd_ip_funcs vcn_v4_0_5_ip_funcs = { .name = "vcn_v4_0_5", .early_init = vcn_v4_0_5_early_init, @@ -1778,8 +1712,8 @@ static const struct amd_ip_funcs vcn_v4_0_5_ip_funcs = { .wait_for_idle = vcn_v4_0_5_wait_for_idle, .set_clockgating_state = vcn_v4_0_5_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v4_0_5_dump_ip_state, - .print_ip_state = vcn_v4_0_5_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v4_0_5_ip_block = { 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 07a6e9582880..536f06b81706 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -115,21 +115,6 @@ static int vcn_v5_0_0_early_init(struct amdgpu_ip_block *ip_block) return 0; } -void vcn_v5_0_0_alloc_ip_dump(struct amdgpu_device *adev) -{ - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_5_0); - uint32_t *ptr; - - /* Allocate memory for VCN IP Dump buffer */ - ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL); - if (!ptr) { - DRM_ERROR("Failed to allocate memory for VCN IP Dump\n"); - adev->vcn.ip_dump = NULL; - } else { - adev->vcn.ip_dump = ptr; - } -} - /** * vcn_v5_0_0_sw_init - sw init for VCN block * @@ -201,7 +186,9 @@ static int vcn_v5_0_0_sw_init(struct amdgpu_ip_block *ip_block) if (!amdgpu_sriov_vf(adev)) adev->vcn.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE; - vcn_v5_0_0_alloc_ip_dump(adev); + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_5_0, ARRAY_SIZE(vcn_reg_list_5_0)); + if (r) + return r; r = amdgpu_vcn_sysfs_reset_mask_init(adev); if (r) @@ -251,8 +238,6 @@ static int vcn_v5_0_0_sw_fini(struct amdgpu_ip_block *ip_block) return r; } - kfree(adev->vcn.ip_dump); - return 0; } @@ -713,6 +698,7 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, volatile struct amdgpu_vcn5_fw_shared *fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; uint32_t tmp; + int ret; /* disable register anti-hang mechanism */ WREG32_P(SOC15_REG_OFFSET(VCN, inst_idx, regUVD_POWER_STATUS), 1, @@ -766,8 +752,13 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, VCN, inst_idx, regUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, 0); + if (ret) { + dev_err(adev->dev, "%s: vcn sram load failed %d\n", __func__, ret); + return ret; + } + } ring = &adev->vcn.inst[inst_idx].ring_enc[0]; @@ -1320,7 +1311,7 @@ static int vcn_v5_0_0_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { struct amdgpu_device *adev = ip_block->adev; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; + bool enable = state == AMD_CG_STATE_GATE; int i; for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { @@ -1428,67 +1419,6 @@ static void vcn_v5_0_0_set_irq_funcs(struct amdgpu_device *adev) } } -void vcn_v5_0_0_print_ip_state(struct amdgpu_ip_block *ip_block, - struct drm_printer *p) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_5_0); - uint32_t inst_off, is_powered; - - if (!adev->vcn.ip_dump) - return; - - drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst); - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) { - drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i); - continue; - } - - inst_off = i * reg_count; - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) { - drm_printf(p, "\nActive Instance:VCN%d\n", i); - for (j = 0; j < reg_count; j++) - drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_5_0[j].reg_name, - adev->vcn.ip_dump[inst_off + j]); - } else { - drm_printf(p, "\nInactive Instance:VCN%d\n", i); - } - } -} - -void vcn_v5_0_0_dump_ip_state(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - int i, j; - bool is_powered; - uint32_t inst_off; - uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_5_0); - - if (!adev->vcn.ip_dump) - return; - - for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) - continue; - - inst_off = i * reg_count; - /* mmUVD_POWER_STATUS is always readable and is first element of the array */ - adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, i, regUVD_POWER_STATUS); - is_powered = (adev->vcn.ip_dump[inst_off] & - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1; - - if (is_powered) - for (j = 1; j < reg_count; j++) - adev->vcn.ip_dump[inst_off + j] = - RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_5_0[j], i)); - } -} - static const struct amd_ip_funcs vcn_v5_0_0_ip_funcs = { .name = "vcn_v5_0_0", .early_init = vcn_v5_0_0_early_init, @@ -1502,8 +1432,8 @@ static const struct amd_ip_funcs vcn_v5_0_0_ip_funcs = { .wait_for_idle = vcn_v5_0_0_wait_for_idle, .set_clockgating_state = vcn_v5_0_0_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v5_0_0_dump_ip_state, - .print_ip_state = vcn_v5_0_0_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v5_0_0_ip_block = { diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.h b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.h index b8927652bc50..51bbccd4360f 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.h +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.h @@ -32,11 +32,6 @@ #define VCN_VID_IP_ADDRESS 0x0 #define VCN_AON_IP_ADDRESS 0x30000 -void vcn_v5_0_0_alloc_ip_dump(struct amdgpu_device *adev); -void vcn_v5_0_0_print_ip_state(struct amdgpu_ip_block *ip_block, - struct drm_printer *p); -void vcn_v5_0_0_dump_ip_state(struct amdgpu_ip_block *ip_block); - extern const struct amdgpu_ip_block_version vcn_v5_0_0_ip_block; #endif /* __VCN_V5_0_0_H__ */ 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 cdefd7fcb0da..4b01e35ad7ef 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c @@ -40,6 +40,40 @@ #include +static const struct amdgpu_hwip_reg_entry vcn_reg_list_5_0_1[] = { + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_POWER_STATUS), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_STATUS), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA0), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA1), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_CMD), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI3), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO3), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI4), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO4), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR3), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR3), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR4), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR4), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE2), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE3), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE4), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_CTL), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_DATA), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_MASK), + SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_PAUSE) +}; + static int vcn_v5_0_1_start_sriov(struct amdgpu_device *adev); static void vcn_v5_0_1_set_unified_ring_funcs(struct amdgpu_device *adev); static void vcn_v5_0_1_set_irq_funcs(struct amdgpu_device *adev); @@ -163,7 +197,17 @@ static int vcn_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block) return r; } - vcn_v5_0_0_alloc_ip_dump(adev); + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) { + r = amdgpu_vcn_ras_sw_init(adev); + if (r) { + dev_err(adev->dev, "Failed to initialize vcn ras block!\n"); + return r; + } + } + + r = amdgpu_vcn_reg_dump_init(adev, vcn_reg_list_5_0_1, ARRAY_SIZE(vcn_reg_list_5_0_1)); + if (r) + return r; return amdgpu_vcn_sysfs_reset_mask_init(adev); } @@ -209,8 +253,6 @@ static int vcn_v5_0_1_sw_fini(struct amdgpu_ip_block *ip_block) amdgpu_vcn_sysfs_reset_mask_fini(adev); - kfree(adev->vcn.ip_dump); - return 0; } @@ -284,7 +326,7 @@ static int vcn_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block) vinst->set_pg_state(vinst, AMD_PG_STATE_GATE); } - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) + if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN) && !amdgpu_sriov_vf(adev)) amdgpu_irq_put(adev, &adev->vcn.inst->ras_poison_irq, 0); return 0; @@ -605,7 +647,7 @@ static int vcn_v5_0_1_start_dpg_mode(struct amdgpu_vcn_inst *vinst, adev->vcn.inst[inst_idx].fw_shared.cpu_addr; struct amdgpu_ring *ring; struct dpg_pause_state state = {.fw_based = VCN_DPG_STATE__PAUSE}; - int vcn_inst; + int vcn_inst, ret; uint32_t tmp; vcn_inst = GET_INST(VCN, inst_idx); @@ -666,8 +708,13 @@ static int vcn_v5_0_1_start_dpg_mode(struct amdgpu_vcn_inst *vinst, VCN, 0, regUVD_MASTINT_EN), UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect); - if (indirect) - amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM); + if (indirect) { + ret = amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM); + if (ret) { + dev_err(adev->dev, "vcn sram load failed %d\n", ret); + return ret; + } + } /* resetting ring, fw should not check RB ring */ fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET; @@ -1475,8 +1522,8 @@ static const struct amd_ip_funcs vcn_v5_0_1_ip_funcs = { .post_soft_reset = NULL, .set_clockgating_state = vcn_v5_0_1_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, - .dump_ip_state = vcn_v5_0_0_dump_ip_state, - .print_ip_state = vcn_v5_0_0_print_ip_state, + .dump_ip_state = amdgpu_vcn_dump_ip_state, + .print_ip_state = amdgpu_vcn_print_ip_state, }; const struct amdgpu_ip_block_version vcn_v5_0_1_ip_block = { @@ -1557,7 +1604,7 @@ static int vcn_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_bank /* reference to smu driver if header file */ static int vcn_v5_0_1_err_codes[] = { - 14, 15, /* VCN */ + 14, 15, 47, /* VCN [D|V|S] */ }; static bool vcn_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, @@ -1603,6 +1650,13 @@ static int vcn_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_commo if (r) goto late_fini; + if (amdgpu_ras_is_supported(adev, ras_block->block) && + adev->vcn.inst->ras_poison_irq.funcs) { + r = amdgpu_irq_get(adev, &adev->vcn.inst->ras_poison_irq, 0); + if (r) + goto late_fini; + } + return 0; late_fini: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 828a9ceef1e7..8535a52a62ca 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1070,7 +1070,12 @@ static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep, svm_range_list_lock_and_flush_work(&p->svms, current->mm); mutex_lock(&p->svms.lock); mmap_write_unlock(current->mm); - if (interval_tree_iter_first(&p->svms.objects, + + /* Skip a special case that allocates VRAM without VA, + * VA will be invalid of 0. + */ + if (!(!args->va_addr && (flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM)) && + interval_tree_iter_first(&p->svms.objects, args->va_addr >> PAGE_SHIFT, (args->va_addr + args->size - 1) >> PAGE_SHIFT)) { pr_err("Address: 0x%llx already allocated by SVM\n", @@ -2566,8 +2571,8 @@ static int criu_restore(struct file *filep, pr_debug("CRIU restore (num_devices:%u num_bos:%u num_objects:%u priv_data_size:%llu)\n", args->num_devices, args->num_bos, args->num_objects, args->priv_data_size); - if (!args->bos || !args->devices || !args->priv_data || !args->priv_data_size || - !args->num_devices || !args->num_bos) + if ((args->num_bos > 0 && !args->bos) || !args->devices || !args->priv_data || + !args->priv_data_size || !args->num_devices) return -EINVAL; mutex_lock(&p->mutex); @@ -3252,8 +3257,10 @@ static long kfd_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) int retcode = -EINVAL; bool ptrace_attached = false; - if (nr >= AMDKFD_CORE_IOCTL_COUNT) + if (nr >= AMDKFD_CORE_IOCTL_COUNT) { + retcode = -ENOTTY; goto err_i1; + } if ((nr >= AMDKFD_COMMAND_START) && (nr < AMDKFD_COMMAND_END)) { u32 amdkfd_size; @@ -3266,8 +3273,10 @@ static long kfd_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) asize = amdkfd_size; cmd = ioctl->cmd; - } else + } else { + retcode = -ENOTTY; goto err_i1; + } dev_dbg(kfd_device, "ioctl cmd 0x%x (#0x%x), arg 0x%lx\n", cmd, nr, arg); 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 2d91027e2a74..6c5c7c1bf5ed 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2725,7 +2725,7 @@ static void get_queue_checkpoint_info(struct device_queue_manager *dqm, dqm_lock(dqm); mqd_mgr = dqm->mqd_mgrs[mqd_type]; - *mqd_size = mqd_mgr->mqd_size; + *mqd_size = mqd_mgr->mqd_size * NUM_XCC(mqd_mgr->dev->xcc_mask); *ctl_stack_size = 0; if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE && mqd_mgr->get_checkpoint_info) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c index 2b0a830f5b29..fb3129883a4c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c @@ -46,11 +46,7 @@ static bool kq_initialize(struct kernel_queue *kq, struct kfd_node *dev, int retval; union PM4_MES_TYPE_3_HEADER nop; - if (WARN_ON(type != KFD_QUEUE_TYPE_DIQ && type != KFD_QUEUE_TYPE_HIQ)) - return false; - - pr_debug("Initializing queue type %d size %d\n", KFD_QUEUE_TYPE_HIQ, - queue_size); + pr_debug("Initializing queue type %d size %d\n", type, queue_size); memset(&prop, 0, sizeof(prop)); memset(&nop, 0, sizeof(nop)); @@ -69,6 +65,7 @@ static bool kq_initialize(struct kernel_queue *kq, struct kfd_node *dev, kq->mqd_mgr = dev->dqm->mqd_mgrs[KFD_MQD_TYPE_HIQ]; break; default: + WARN(1, "Invalid queue type %d\n", type); dev_err(dev->adev->dev, "Invalid queue type %d\n", type); return false; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 79251f22b702..86315ecb6f1d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -68,7 +68,8 @@ svm_migrate_gart_map(struct amdgpu_ring *ring, uint64_t npages, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4 + num_bytes, AMDGPU_IB_POOL_DELAYED, - &job); + &job, + AMDGPU_KERNEL_JOB_ID_KFD_GART_MAP); if (r) return r; @@ -260,20 +261,7 @@ static void svm_migrate_put_sys_page(unsigned long addr) put_page(page); } -static unsigned long svm_migrate_unsuccessful_pages(struct migrate_vma *migrate) -{ - unsigned long upages = 0; - unsigned long i; - - for (i = 0; i < migrate->npages; i++) { - if (migrate->src[i] & MIGRATE_PFN_VALID && - !(migrate->src[i] & MIGRATE_PFN_MIGRATE)) - upages++; - } - return upages; -} - -static int +static long svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, struct migrate_vma *migrate, struct dma_fence **mfence, dma_addr_t *scratch, uint64_t ttm_res_offset) @@ -282,7 +270,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, struct amdgpu_device *adev = node->adev; struct device *dev = adev->dev; struct amdgpu_res_cursor cursor; - uint64_t mpages = 0; + long mpages; dma_addr_t *src; uint64_t *dst; uint64_t i, j; @@ -296,6 +284,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, amdgpu_res_first(prange->ttm_res, ttm_res_offset, npages << PAGE_SHIFT, &cursor); + mpages = 0; for (i = j = 0; (i < npages) && (mpages < migrate->cpages); i++) { struct page *spage; @@ -356,13 +345,14 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, out_free_vram_pages: if (r) { pr_debug("failed %d to copy memory to vram\n", r); - for (i = 0; i < npages && mpages; i++) { + while (i-- && mpages) { if (!dst[i]) continue; svm_migrate_put_vram_page(adev, dst[i]); migrate->dst[i] = 0; mpages--; } + mpages = r; } #ifdef DEBUG_FORCE_MIXED_DOMAINS @@ -380,7 +370,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, } #endif - return r; + return mpages; } static long @@ -395,7 +385,7 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, struct dma_fence *mfence = NULL; struct migrate_vma migrate = { 0 }; unsigned long cpages = 0; - unsigned long mpages = 0; + long mpages = 0; dma_addr_t *scratch; void *buf; int r = -ENOMEM; @@ -441,15 +431,17 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, else pr_debug("0x%lx pages collected\n", cpages); - r = svm_migrate_copy_to_vram(node, prange, &migrate, &mfence, scratch, ttm_res_offset); + mpages = svm_migrate_copy_to_vram(node, prange, &migrate, &mfence, scratch, ttm_res_offset); migrate_vma_pages(&migrate); svm_migrate_copy_done(adev, mfence); migrate_vma_finalize(&migrate); - mpages = cpages - svm_migrate_unsuccessful_pages(&migrate); - pr_debug("successful/cpages/npages 0x%lx/0x%lx/0x%lx\n", + if (mpages >= 0) + pr_debug("migrated/collected/requested 0x%lx/0x%lx/0x%lx\n", mpages, cpages, migrate.npages); + else + r = mpages; svm_range_dma_unmap_dev(adev->dev, scratch, 0, npages); @@ -459,14 +451,13 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, start >> PAGE_SHIFT, end >> PAGE_SHIFT, 0, node->id, trigger, r); out: - if (!r && mpages) { + if (!r && mpages > 0) { pdd = svm_range_get_pdd_by_node(prange, node); if (pdd) WRITE_ONCE(pdd->page_in, pdd->page_in + mpages); - - return mpages; } - return r; + + return r ? r : mpages; } /** @@ -577,7 +568,7 @@ static void svm_migrate_page_free(struct page *page) } } -static int +static long svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, struct migrate_vma *migrate, struct dma_fence **mfence, dma_addr_t *scratch, uint64_t npages) @@ -586,6 +577,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, uint64_t *src; dma_addr_t *dst; struct page *dpage; + long mpages; uint64_t i = 0, j; uint64_t addr; int r = 0; @@ -598,6 +590,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, src = (uint64_t *)(scratch + npages); dst = scratch; + mpages = 0; for (i = 0, j = 0; i < npages; i++, addr += PAGE_SIZE) { struct page *spage; @@ -646,6 +639,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, dst[i] >> PAGE_SHIFT, page_to_pfn(dpage)); migrate->dst[i] = migrate_pfn(page_to_pfn(dpage)); + mpages++; j++; } @@ -655,13 +649,17 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, out_oom: if (r) { pr_debug("failed %d copy to ram\n", r); - while (i--) { + while (i-- && mpages) { + if (!migrate->dst[i]) + continue; svm_migrate_put_sys_page(dst[i]); migrate->dst[i] = 0; + mpages--; } + mpages = r; } - return r; + return mpages; } /** @@ -688,9 +686,8 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, { struct kfd_process *p = container_of(prange->svms, struct kfd_process, svms); uint64_t npages = (end - start) >> PAGE_SHIFT; - unsigned long upages = npages; unsigned long cpages = 0; - unsigned long mpages = 0; + long mpages = 0; struct amdgpu_device *adev = node->adev; struct kfd_process_device *pdd; struct dma_fence *mfence = NULL; @@ -736,7 +733,6 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, if (!cpages) { pr_debug("failed collect migrate device pages [0x%lx 0x%lx]\n", prange->start, prange->last); - upages = svm_migrate_unsuccessful_pages(&migrate); goto out_free; } if (cpages != npages) @@ -745,13 +741,15 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, else pr_debug("0x%lx pages collected\n", cpages); - r = svm_migrate_copy_to_ram(adev, prange, &migrate, &mfence, + mpages = svm_migrate_copy_to_ram(adev, prange, &migrate, &mfence, scratch, npages); migrate_vma_pages(&migrate); - upages = svm_migrate_unsuccessful_pages(&migrate); - pr_debug("unsuccessful/cpages/npages 0x%lx/0x%lx/0x%lx\n", - upages, cpages, migrate.npages); + if (mpages >= 0) + pr_debug("migrated/collected/requested 0x%lx/0x%lx/0x%lx\n", + mpages, cpages, migrate.npages); + else + r = mpages; svm_migrate_copy_done(adev, mfence); migrate_vma_finalize(&migrate); @@ -764,8 +762,7 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, start >> PAGE_SHIFT, end >> PAGE_SHIFT, node->id, 0, trigger, r); out: - if (!r && cpages) { - mpages = cpages - upages; + if (!r && mpages > 0) { pdd = svm_range_get_pdd_by_node(prange, node); if (pdd) WRITE_ONCE(pdd->page_out, pdd->page_out + mpages); @@ -848,6 +845,9 @@ int svm_migrate_vram_to_ram(struct svm_range *prange, struct mm_struct *mm, } if (r >= 0) { + WARN_ONCE(prange->vram_pages < mpages, + "Recorded vram pages(0x%llx) should not be less than migration pages(0x%lx).", + prange->vram_pages, mpages); prange->vram_pages -= mpages; /* prange does not have vram page set its actual_loc to system diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c index aee2212e52f6..33aa23450b3f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c @@ -78,8 +78,8 @@ static int kfd_init(void) static void kfd_exit(void) { kfd_cleanup_processes(); - kfd_debugfs_fini(); kfd_process_destroy_wq(); + kfd_debugfs_fini(); kfd_procfs_shutdown(); kfd_topology_shutdown(); kfd_chardev_exit(); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index 97933d2a3803..f2dee320fada 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -373,7 +373,7 @@ static void get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stac { struct v9_mqd *m = get_mqd(mqd); - *ctl_stack_size = m->cp_hqd_cntl_stack_size; + *ctl_stack_size = m->cp_hqd_cntl_stack_size * NUM_XCC(mm->dev->xcc_mask); } static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) @@ -388,6 +388,24 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi memcpy(ctl_stack_dst, ctl_stack, m->cp_hqd_cntl_stack_size); } +static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm, + void *mqd, + void *mqd_dst, + void *ctl_stack_dst) +{ + struct v9_mqd *m; + int xcc; + uint64_t size = get_mqd(mqd)->cp_mqd_stride_size; + + for (xcc = 0; xcc < NUM_XCC(mm->dev->xcc_mask); xcc++) { + m = get_mqd(mqd + size * xcc); + + checkpoint_mqd(mm, m, + (uint8_t *)mqd_dst + sizeof(*m) * xcc, + (uint8_t *)ctl_stack_dst + m->cp_hqd_cntl_stack_size * xcc); + } +} + static void restore_mqd(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, struct queue_properties *qp, @@ -764,13 +782,35 @@ static void restore_mqd_v9_4_3(struct mqd_manager *mm, void **mqd, const void *mqd_src, const void *ctl_stack_src, u32 ctl_stack_size) { - restore_mqd(mm, mqd, mqd_mem_obj, gart_addr, qp, mqd_src, ctl_stack_src, ctl_stack_size); - if (amdgpu_sriov_multi_vf_mode(mm->dev->adev)) { - struct v9_mqd *m; + struct kfd_mem_obj xcc_mqd_mem_obj; + u32 mqd_ctl_stack_size; + struct v9_mqd *m; + u32 num_xcc; + int xcc; - m = (struct v9_mqd *) mqd_mem_obj->cpu_ptr; - m->cp_hqd_pq_doorbell_control |= 1 << - CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_MODE__SHIFT; + uint64_t offset = mm->mqd_stride(mm, qp); + + mm->dev->dqm->current_logical_xcc_start++; + + num_xcc = NUM_XCC(mm->dev->xcc_mask); + mqd_ctl_stack_size = ctl_stack_size / num_xcc; + + memset(&xcc_mqd_mem_obj, 0x0, sizeof(struct kfd_mem_obj)); + + /* Set the MQD pointer and gart address to XCC0 MQD */ + *mqd = mqd_mem_obj->cpu_ptr; + if (gart_addr) + *gart_addr = mqd_mem_obj->gpu_addr; + + for (xcc = 0; xcc < num_xcc; xcc++) { + get_xcc_mqd(mqd_mem_obj, &xcc_mqd_mem_obj, offset * xcc); + restore_mqd(mm, (void **)&m, + &xcc_mqd_mem_obj, + NULL, + qp, + (uint8_t *)mqd_src + xcc * sizeof(*m), + (uint8_t *)ctl_stack_src + xcc * mqd_ctl_stack_size, + mqd_ctl_stack_size); } } static int destroy_mqd_v9_4_3(struct mqd_manager *mm, void *mqd, @@ -906,7 +946,6 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type, mqd->free_mqd = kfd_free_mqd_cp; mqd->is_occupied = kfd_is_occupied_cp; mqd->get_checkpoint_info = get_checkpoint_info; - mqd->checkpoint_mqd = checkpoint_mqd; mqd->mqd_size = sizeof(struct v9_mqd); mqd->mqd_stride = mqd_stride_v9; #if defined(CONFIG_DEBUG_FS) @@ -918,16 +957,18 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type, mqd->init_mqd = init_mqd_v9_4_3; mqd->load_mqd = load_mqd_v9_4_3; mqd->update_mqd = update_mqd_v9_4_3; - mqd->restore_mqd = restore_mqd_v9_4_3; mqd->destroy_mqd = destroy_mqd_v9_4_3; mqd->get_wave_state = get_wave_state_v9_4_3; + mqd->checkpoint_mqd = checkpoint_mqd_v9_4_3; + mqd->restore_mqd = restore_mqd_v9_4_3; } else { mqd->init_mqd = init_mqd; mqd->load_mqd = load_mqd; mqd->update_mqd = update_mqd; - mqd->restore_mqd = restore_mqd; mqd->destroy_mqd = kfd_destroy_mqd_cp; mqd->get_wave_state = get_wave_state; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; } break; case KFD_MQD_TYPE_HIQ: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 67694bcd9464..d01ef5ac0766 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -111,7 +111,14 @@ #define KFD_KERNEL_QUEUE_SIZE 2048 -#define KFD_UNMAP_LATENCY_MS (4000) +/* KFD_UNMAP_LATENCY_MS is the timeout CP waiting for SDMA preemption. One XCC + * can be associated to 2 SDMA engines. queue_preemption_timeout_ms is the time + * driver waiting for CP returning the UNMAP_QUEUE fence. Thus the math is + * queue_preemption_timeout_ms = sdma_preemption_time * 2 + cp workload + * The format here makes CP workload 10% of total timeout + */ +#define KFD_UNMAP_LATENCY_MS \ + ((queue_preemption_timeout_ms - queue_preemption_timeout_ms / 10) >> 1) #define KFD_MAX_SDMA_QUEUES 128 diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index c643e0ccec52..7fbb5c274ccc 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -914,7 +914,10 @@ static int criu_checkpoint_queues_device(struct kfd_process_device *pdd, q_data = (struct kfd_criu_queue_priv_data *)q_private_data; - /* data stored in this order: priv_data, mqd, ctl_stack */ + /* + * data stored in this order: + * priv_data, mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]... + */ q_data->mqd_size = mqd_size; q_data->ctl_stack_size = ctl_stack_size; @@ -963,7 +966,7 @@ int kfd_criu_checkpoint_queues(struct kfd_process *p, } static void set_queue_properties_from_criu(struct queue_properties *qp, - struct kfd_criu_queue_priv_data *q_data) + struct kfd_criu_queue_priv_data *q_data, uint32_t num_xcc) { qp->is_interop = false; qp->queue_percent = q_data->q_percent; @@ -976,7 +979,11 @@ static void set_queue_properties_from_criu(struct queue_properties *qp, qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size; qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address; qp->ctx_save_restore_area_size = q_data->ctx_save_restore_area_size; - qp->ctl_stack_size = q_data->ctl_stack_size; + if (q_data->type == KFD_QUEUE_TYPE_COMPUTE) + qp->ctl_stack_size = q_data->ctl_stack_size / num_xcc; + else + qp->ctl_stack_size = q_data->ctl_stack_size; + qp->type = q_data->type; qp->format = q_data->format; } @@ -1036,12 +1043,15 @@ int kfd_criu_restore_queue(struct kfd_process *p, goto exit; } - /* data stored in this order: mqd, ctl_stack */ + /* + * data stored in this order: + * mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]... + */ mqd = q_extra_data; ctl_stack = mqd + q_data->mqd_size; memset(&qp, 0, sizeof(qp)); - set_queue_properties_from_criu(&qp, q_data); + set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask)); print_queue_properties(&qp); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index a0f22ea6d15a..68ba239b2e5d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1189,7 +1189,7 @@ svm_nodes_in_same_hive(struct kfd_node *node_a, struct kfd_node *node_b) } static uint64_t -svm_range_get_pte_flags(struct kfd_node *node, +svm_range_get_pte_flags(struct kfd_node *node, struct amdgpu_vm *vm, struct svm_range *prange, int domain) { struct kfd_node *bo_node; @@ -1292,10 +1292,6 @@ svm_range_get_pte_flags(struct kfd_node *node, AMDGPU_VM_MTYPE_UC : AMDGPU_VM_MTYPE_NC; } - mapping_flags |= AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE; - - if (flags & KFD_IOCTL_SVM_FLAG_GPU_RO) - mapping_flags &= ~AMDGPU_VM_PAGE_WRITEABLE; if (flags & KFD_IOCTL_SVM_FLAG_GPU_EXEC) mapping_flags |= AMDGPU_VM_PAGE_EXECUTABLE; @@ -1305,7 +1301,10 @@ svm_range_get_pte_flags(struct kfd_node *node, if (gc_ip_version >= IP_VERSION(12, 0, 0)) pte_flags |= AMDGPU_PTE_IS_PTE; - pte_flags |= amdgpu_gem_va_map_flags(node->adev, mapping_flags); + amdgpu_gmc_get_vm_pte(node->adev, vm, NULL, mapping_flags, &pte_flags); + pte_flags |= AMDGPU_PTE_READABLE; + if (!(flags & KFD_IOCTL_SVM_FLAG_GPU_RO)) + pte_flags |= AMDGPU_PTE_WRITEABLE; return pte_flags; } @@ -1412,7 +1411,7 @@ svm_range_map_to_gpu(struct kfd_process_device *pdd, struct svm_range *prange, pr_debug("Mapping range [0x%lx 0x%llx] on domain: %s\n", last_start, prange->start + i, last_domain ? "GPU" : "CPU"); - pte_flags = svm_range_get_pte_flags(pdd->dev, prange, last_domain); + pte_flags = svm_range_get_pte_flags(pdd->dev, vm, prange, last_domain); if (readonly) pte_flags &= ~AMDGPU_PTE_WRITEABLE; @@ -1714,6 +1713,29 @@ static int svm_range_validate_and_map(struct mm_struct *mm, next = min(vma->vm_end, end); npages = (next - addr) >> PAGE_SHIFT; + /* HMM requires at least READ permissions. If provided with PROT_NONE, + * unmap the memory. If it's not already mapped, this is a no-op + * If PROT_WRITE is provided without READ, warn first then unmap + */ + if (!(vma->vm_flags & VM_READ)) { + unsigned long e, s; + + svm_range_lock(prange); + if (vma->vm_flags & VM_WRITE) + pr_debug("VM_WRITE without VM_READ is not supported"); + s = max(start, prange->start); + e = min(end, prange->last); + if (e >= s) + r = svm_range_unmap_from_gpus(prange, s, e, + KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU); + svm_range_unlock(prange); + /* If unmap returns non-zero, we'll bail on the next for loop + * iteration, so just leave r and continue + */ + addr = next; + continue; + } + WRITE_ONCE(p->svms.faulting_task, current); r = amdgpu_hmm_range_get_pages(&prange->notifier, addr, npages, readonly, owner, NULL, @@ -4239,7 +4261,7 @@ svm_ioctl(struct kfd_process *p, enum kfd_ioctl_svm_op op, uint64_t start, r = svm_range_get_attr(p, mm, start, size, nattrs, attrs); break; default: - r = EINVAL; + r = -EINVAL; break; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 4ec73f33535e..5c98746eb72d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -530,6 +530,8 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, sysfs_show_32bit_prop(buffer, offs, "sdma_fw_version", dev->gpu->kfd->sdma_fw_version); sysfs_show_64bit_prop(buffer, offs, "unique_id", + dev->gpu->xcp ? + dev->gpu->xcp->unique_id : dev->gpu->adev->unique_id); sysfs_show_32bit_prop(buffer, offs, "num_xcc", NUM_XCC(dev->gpu->xcc_mask)); @@ -1587,7 +1589,8 @@ static int kfd_dev_create_p2p_links(void) break; if (!dev->gpu || !dev->gpu->adev || (dev->gpu->kfd->hive_id && - dev->gpu->kfd->hive_id == new_dev->gpu->kfd->hive_id)) + dev->gpu->kfd->hive_id == new_dev->gpu->kfd->hive_id && + amdgpu_xgmi_get_is_sharing_enabled(dev->gpu->adev, new_dev->gpu->adev))) goto next; /* check if node(s) is/are peer accessible in one direction or bi-direction */ diff --git a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c index 8bc36f04b1b7..44009aa8216e 100644 --- a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c +++ b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c @@ -46,18 +46,29 @@ static const struct drm_driver amdgpu_xcp_driver = { static int8_t pdev_num; static struct xcp_device *xcp_dev[MAX_XCP_PLATFORM_DEVICE]; +static DEFINE_MUTEX(xcp_mutex); int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev) { struct platform_device *pdev; struct xcp_device *pxcp_dev; char dev_name[20]; - int ret; + int ret, i; + + guard(mutex)(&xcp_mutex); if (pdev_num >= MAX_XCP_PLATFORM_DEVICE) return -ENODEV; - snprintf(dev_name, sizeof(dev_name), "amdgpu_xcp_%d", pdev_num); + for (i = 0; i < MAX_XCP_PLATFORM_DEVICE; i++) { + if (!xcp_dev[i]) + break; + } + + if (i >= MAX_XCP_PLATFORM_DEVICE) + return -ENODEV; + + snprintf(dev_name, sizeof(dev_name), "amdgpu_xcp_%d", i); pdev = platform_device_register_simple(dev_name, -1, NULL, 0); if (IS_ERR(pdev)) return PTR_ERR(pdev); @@ -73,8 +84,8 @@ int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev) goto out_devres; } - xcp_dev[pdev_num] = pxcp_dev; - xcp_dev[pdev_num]->pdev = pdev; + xcp_dev[i] = pxcp_dev; + xcp_dev[i]->pdev = pdev; *ddev = &pxcp_dev->drm; pdev_num++; @@ -89,16 +100,43 @@ int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev) } EXPORT_SYMBOL(amdgpu_xcp_drm_dev_alloc); -void amdgpu_xcp_drv_release(void) +static void free_xcp_dev(int8_t index) { - for (--pdev_num; pdev_num >= 0; --pdev_num) { - struct platform_device *pdev = xcp_dev[pdev_num]->pdev; + if ((index < MAX_XCP_PLATFORM_DEVICE) && (xcp_dev[index])) { + struct platform_device *pdev = xcp_dev[index]->pdev; devres_release_group(&pdev->dev, NULL); platform_device_unregister(pdev); - xcp_dev[pdev_num] = NULL; + + xcp_dev[index] = NULL; + pdev_num--; + } +} + +void amdgpu_xcp_drm_dev_free(struct drm_device *ddev) +{ + int8_t i; + + guard(mutex)(&xcp_mutex); + + for (i = 0; i < MAX_XCP_PLATFORM_DEVICE; i++) { + if ((xcp_dev[i]) && (&xcp_dev[i]->drm == ddev)) { + free_xcp_dev(i); + break; + } + } +} +EXPORT_SYMBOL(amdgpu_xcp_drm_dev_free); + +void amdgpu_xcp_drv_release(void) +{ + int8_t i; + + guard(mutex)(&xcp_mutex); + + for (i = 0; pdev_num && i < MAX_XCP_PLATFORM_DEVICE; i++) { + free_xcp_dev(i); } - pdev_num = 0; } EXPORT_SYMBOL(amdgpu_xcp_drv_release); diff --git a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.h b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.h index c1c4b679bf95..580a1602c8e3 100644 --- a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.h +++ b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.h @@ -25,5 +25,6 @@ #define _AMDGPU_XCP_DRV_H_ int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev); +void amdgpu_xcp_drm_dev_free(struct drm_device *ddev); void amdgpu_xcp_drv_release(void); #endif /* _AMDGPU_XCP_DRV_H_ */ diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile index 89d605de0595..0084a8d55254 100644 --- a/drivers/gpu/drm/amd/display/Makefile +++ b/drivers/gpu/drm/amd/display/Makefile @@ -44,6 +44,7 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/mmhubbub subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/mpc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/opp subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/pg +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/soc_and_ip_translator subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/inc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/freesync subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/color 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 2a175fc0399c..62defeccbb5c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2015 Advanced Micro Devices, Inc. * @@ -39,13 +40,11 @@ #include "dc/dc_stat.h" #include "dc/dc_state.h" #include "amdgpu_dm_trace.h" -#include "dpcd_defs.h" #include "link/protocols/link_dpcd.h" #include "link_service_types.h" #include "link/protocols/link_dp_capability.h" #include "link/protocols/link_ddc.h" -#include "vid.h" #include "amdgpu.h" #include "amdgpu_display.h" #include "amdgpu_ucode.h" @@ -56,7 +55,6 @@ #include "amdgpu_dm_hdcp.h" #include #include "amdgpu_dm_wb.h" -#include "amdgpu_pm.h" #include "amdgpu_atombios.h" #include "amd_shared.h" @@ -82,6 +80,7 @@ #include #include +#include #include #include #include @@ -102,15 +101,6 @@ #include "ivsrcid/dcn/irqsrcs_dcn_1_0.h" -#include "dcn/dcn_1_0_offset.h" -#include "dcn/dcn_1_0_sh_mask.h" -#include "soc15_hw_ip.h" -#include "soc15_common.h" -#include "vega10_ip_offset.h" - -#include "gc/gc_11_0_0_offset.h" -#include "gc/gc_11_0_0_sh_mask.h" - #include "modules/inc/mod_freesync.h" #include "modules/power/power_helpers.h" @@ -541,6 +531,50 @@ static void dm_pflip_high_irq(void *interrupt_params) amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e); } +static void dm_handle_vmin_vmax_update(struct work_struct *offload_work) +{ + struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work); + struct amdgpu_device *adev = work->adev; + struct dc_stream_state *stream = work->stream; + struct dc_crtc_timing_adjust *adjust = work->adjust; + + mutex_lock(&adev->dm.dc_lock); + dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust); + mutex_unlock(&adev->dm.dc_lock); + + dc_stream_release(stream); + kfree(work->adjust); + kfree(work); +} + +static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, + struct dc_stream_state *stream, + struct dc_crtc_timing_adjust *adjust) +{ + struct vupdate_offload_work *offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL); + if (!offload_work) { + drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n"); + return; + } + + struct dc_crtc_timing_adjust *adjust_copy = kzalloc(sizeof(*adjust_copy), GFP_KERNEL); + if (!adjust_copy) { + drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n"); + kfree(offload_work); + return; + } + + dc_stream_retain(stream); + memcpy(adjust_copy, adjust, sizeof(*adjust_copy)); + + INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update); + offload_work->adev = adev; + offload_work->stream = stream; + offload_work->adjust = adjust_copy; + + queue_work(system_wq, &offload_work->work); +} + static void dm_vupdate_high_irq(void *interrupt_params) { struct common_irq_params *irq_params = interrupt_params; @@ -578,22 +612,27 @@ static void dm_vupdate_high_irq(void *interrupt_params) * page-flip completion events that have been queued to us * if a pageflip happened inside front-porch. */ - if (vrr_active) { + if (vrr_active && acrtc->dm_irq_params.stream) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state + == VRR_STATE_ACTIVE_VARIABLE; + amdgpu_dm_crtc_handle_vblank(acrtc); /* BTR processing for pre-DCE12 ASICs */ - if (acrtc->dm_irq_params.stream && - adev->family < AMDGPU_FAMILY_AI) { + if (adev->family < AMDGPU_FAMILY_AI) { spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); mod_freesync_handle_v_update( adev->dm.freesync_module, acrtc->dm_irq_params.stream, &acrtc->dm_irq_params.vrr_params); - dc_stream_adjust_vmin_vmax( - adev->dm.dc, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); } } @@ -676,15 +715,20 @@ static void dm_crtc_high_irq(void *interrupt_params) spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); if (acrtc->dm_irq_params.stream && - acrtc->dm_irq_params.vrr_params.supported && - acrtc->dm_irq_params.freesync_config.state == - VRR_STATE_ACTIVE_VARIABLE) { + acrtc->dm_irq_params.vrr_params.supported) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE; + mod_freesync_handle_v_update(adev->dm.freesync_module, acrtc->dm_irq_params.stream, &acrtc->dm_irq_params.vrr_params); - dc_stream_adjust_vmin_vmax(adev->dm.dc, acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); + /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */ + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } } /* @@ -2142,7 +2186,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) if (drm_vblank_init(adev_to_drm(adev), adev->dm.display_indexes_num)) { drm_err(adev_to_drm(adev), - "failed to initialize sw for display support.\n"); + "failed to initialize vblank for display support.\n"); goto error; } @@ -2913,6 +2957,17 @@ static int dm_oem_i2c_hw_init(struct amdgpu_device *adev) return 0; } +static void dm_oem_i2c_hw_fini(struct amdgpu_device *adev) +{ + struct amdgpu_display_manager *dm = &adev->dm; + + if (dm->oem_i2c) { + i2c_del_adapter(&dm->oem_i2c->base); + kfree(dm->oem_i2c); + dm->oem_i2c = NULL; + } +} + /** * dm_hw_init() - Initialize DC device * @ip_block: Pointer to the amdgpu_ip_block for this hw instance. @@ -2963,7 +3018,7 @@ static int dm_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - kfree(adev->dm.oem_i2c); + dm_oem_i2c_hw_fini(adev); amdgpu_dm_hpd_fini(adev); @@ -3127,25 +3182,6 @@ static void dm_destroy_cached_state(struct amdgpu_device *adev) dm->cached_state = NULL; } -static void dm_complete(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - dm_destroy_cached_state(adev); -} - -static int dm_prepare_suspend(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (amdgpu_in_reset(adev)) - return 0; - - WARN_ON(adev->dm.cached_state); - - return dm_cache_state(adev); -} - static int dm_suspend(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -3571,10 +3607,8 @@ static const struct amd_ip_funcs amdgpu_dm_funcs = { .early_fini = amdgpu_dm_early_fini, .hw_init = dm_hw_init, .hw_fini = dm_hw_fini, - .prepare_suspend = dm_prepare_suspend, .suspend = dm_suspend, .resume = dm_resume, - .complete = dm_complete, .is_idle = dm_is_idle, .wait_for_idle = dm_wait_for_idle, .check_soft_reset = dm_check_soft_reset, @@ -3612,18 +3646,20 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = { static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) { + const struct drm_panel_backlight_quirk *panel_backlight_quirk; struct amdgpu_dm_backlight_caps *caps; struct drm_connector *conn_base; struct amdgpu_device *adev; struct drm_luminance_range_info *luminance_range; - int min_input_signal_override; + struct drm_device *drm; if (aconnector->bl_idx == -1 || aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP) return; conn_base = &aconnector->base; - adev = drm_to_adev(conn_base->dev); + drm = conn_base->dev; + adev = drm_to_adev(drm); caps = &adev->dm.backlight_caps[aconnector->bl_idx]; caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; @@ -3656,9 +3692,24 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else caps->aux_min_input_signal = 1; - min_input_signal_override = drm_get_panel_min_brightness_quirk(aconnector->drm_edid); - if (min_input_signal_override >= 0) - caps->min_input_signal = min_input_signal_override; + panel_backlight_quirk = + drm_get_panel_backlight_quirk(aconnector->drm_edid); + if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { + if (panel_backlight_quirk->min_brightness) { + caps->min_input_signal = + panel_backlight_quirk->min_brightness - 1; + drm_info(drm, + "Applying panel backlight quirk, min_brightness: %d\n", + caps->min_input_signal); + } + if (panel_backlight_quirk->brightness_mask) { + drm_info(drm, + "Applying panel backlight quirk, brightness_mask: 0x%X\n", + panel_backlight_quirk->brightness_mask); + caps->brightness_mask = + panel_backlight_quirk->brightness_mask; + } + } } DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T)) @@ -4756,16 +4807,16 @@ static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, return 1; } -/* Rescale from [min..max] to [0..MAX_BACKLIGHT_LEVEL] */ +/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */ static inline u32 scale_input_to_fw(int min, int max, u64 input) { - return DIV_ROUND_CLOSEST_ULL(input * MAX_BACKLIGHT_LEVEL, max - min); + return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min); } -/* Rescale from [0..MAX_BACKLIGHT_LEVEL] to [min..max] */ +/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */ static inline u32 scale_fw_to_input(int min, int max, u64 input) { - return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), MAX_BACKLIGHT_LEVEL); + return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL); } static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, @@ -4773,8 +4824,8 @@ static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *cap uint32_t *user_brightness) { u32 brightness = scale_input_to_fw(min, max, *user_brightness); - u8 prev_signal = 0, prev_lum = 0; - int i = 0; + u8 lower_signal, upper_signal, upper_lum, lower_lum, lum; + int left, right; if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) return; @@ -4782,32 +4833,44 @@ static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *cap if (!caps->data_points) return; - /* choose start to run less interpolation steps */ - if (caps->luminance_data[caps->data_points/2].input_signal > brightness) - i = caps->data_points/2; - do { - u8 signal = caps->luminance_data[i].input_signal; - u8 lum = caps->luminance_data[i].luminance; + left = 0; + right = caps->data_points - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + u8 signal = caps->luminance_data[mid].input_signal; - /* - * brightness == signal: luminance is percent numerator - * brightness < signal: interpolate between previous and current luminance numerator - * brightness > signal: find next data point - */ - if (brightness > signal) { - prev_signal = signal; - prev_lum = lum; - i++; - continue; + /* Exact match found */ + if (signal == brightness) { + lum = caps->luminance_data[mid].luminance; + goto scale; } - if (brightness < signal) - lum = prev_lum + DIV_ROUND_CLOSEST((lum - prev_lum) * - (brightness - prev_signal), - signal - prev_signal); - *user_brightness = scale_fw_to_input(min, max, - DIV_ROUND_CLOSEST(lum * brightness, 101)); - return; - } while (i < caps->data_points); + + if (signal < brightness) + left = mid + 1; + else + right = mid - 1; + } + + /* verify bound */ + if (left >= caps->data_points) + left = caps->data_points - 1; + + /* At this point, left > right */ + lower_signal = caps->luminance_data[right].input_signal; + upper_signal = caps->luminance_data[left].input_signal; + lower_lum = caps->luminance_data[right].luminance; + upper_lum = caps->luminance_data[left].luminance; + + /* interpolate */ + if (right == left || !lower_lum) + lum = upper_lum; + else + lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * + (brightness - lower_signal), + upper_signal - lower_signal); +scale: + *user_brightness = scale_fw_to_input(min, max, + DIV_ROUND_CLOSEST(lum * brightness, 101)); } static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps, @@ -4858,6 +4921,10 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); link = (struct dc_link *)dm->backlight_link[bl_idx]; + /* Apply brightness quirk */ + if (caps->brightness_mask) + brightness |= caps->brightness_mask; + /* Change brightness based on AUX property */ mutex_lock(&dm->dc_lock); if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { @@ -4926,10 +4993,8 @@ static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, if (caps.aux_support) { u32 avg, peak; - bool rc; - rc = dc_link_get_backlight_level_nits(link, &avg, &peak); - if (!rc) + if (!dc_link_get_backlight_level_nits(link, &avg, &peak)) return dm->brightness[bl_idx]; return convert_brightness_to_user(&caps, avg); } @@ -4995,8 +5060,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) } else props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL; - if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) + if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) { drm_info(drm, "Using custom brightness curve\n"); + props.scale = BACKLIGHT_SCALE_NON_LINEAR; + } else + props.scale = BACKLIGHT_SCALE_LINEAR; props.type = BACKLIGHT_RAW; snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", @@ -6390,13 +6458,15 @@ static void fill_stream_properties_from_drm_display_mode( (struct drm_connector *)connector, mode_in); if (err < 0) - drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd \n", connector->name, err); + drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n", + connector->name, err); timing_out->vic = avi_frame.video_code; err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame, (struct drm_connector *)connector, mode_in); if (err < 0) - drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd \n", connector->name, err); + drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n", + connector->name, err); timing_out->hdmi_vic = hv_frame.vic; } @@ -7792,6 +7862,9 @@ amdgpu_dm_connector_atomic_check(struct drm_connector *conn, struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn); int ret; + if (WARN_ON(unlikely(!old_con_state || !new_con_state))) + return -EINVAL; + trace_amdgpu_dm_connector_atomic_check(new_con_state); if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { @@ -7803,6 +7876,14 @@ amdgpu_dm_connector_atomic_check(struct drm_connector *conn, if (!crtc) return 0; + if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) { + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = true; + } + if (new_con_state->colorspace != old_con_state->colorspace) { new_crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(new_crtc_state)) @@ -7934,7 +8015,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, if (IS_ERR(mst_state)) return PTR_ERR(mst_state); - mst_state->pbn_div.full = dfixed_const(dm_mst_get_pbn_divider(aconnector->mst_root->dc_link)); + mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link); if (!state->duplicated) { int max_bpc = conn_state->max_requested_bpc; @@ -8378,8 +8459,7 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) drm_add_modes_noedid(connector, 1920, 1080); } else { amdgpu_dm_connector_ddc_get_modes(connector, drm_edid); - if (encoder && (connector->connector_type != DRM_MODE_CONNECTOR_eDP) && - (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)) + if (encoder) amdgpu_dm_connector_add_common_modes(encoder, connector); amdgpu_dm_connector_add_freesync_modes(connector, drm_edid); } @@ -8498,6 +8578,18 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, if (adev->dm.hdcp_workqueue) drm_connector_attach_content_protection_property(&aconnector->base, true); } + + if (connector_type == DRM_MODE_CONNECTOR_eDP) { + struct drm_privacy_screen *privacy_screen; + + privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL); + if (!IS_ERR(privacy_screen)) { + drm_connector_attach_privacy_screen_provider(&aconnector->base, + privacy_screen); + } else if (PTR_ERR(privacy_screen) != -ENODEV) { + drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n"); + } + } } static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, @@ -10086,69 +10178,40 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm, drm_writeback_queue_job(wb_conn, new_con_state); } -/** - * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation. - * @state: The atomic state to commit - * - * This will tell DC to commit the constructed DC state from atomic_check, - * programming the hardware. Any failures here implies a hardware failure, since - * atomic check should have filtered anything non-kosher. - */ -static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) +static void amdgpu_dm_update_hdcp(struct drm_atomic_state *state) { - struct drm_device *dev = state->dev; - struct amdgpu_device *adev = drm_to_adev(dev); - struct amdgpu_display_manager *dm = &adev->dm; - struct dm_atomic_state *dm_state; - struct dc_state *dc_state = NULL; - u32 i, j; - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state, *new_crtc_state; - unsigned long flags; - bool wait_for_vblank = true; - struct drm_connector *connector; struct drm_connector_state *old_con_state, *new_con_state; - struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; - int crtc_disable_count = 0; + struct drm_device *dev = state->dev; + struct drm_connector *connector; + struct amdgpu_device *adev = drm_to_adev(dev); + int i; - trace_amdgpu_dm_atomic_commit_tail_begin(state); - - drm_atomic_helper_update_legacy_modeset_state(dev, state); - drm_dp_mst_atomic_wait_for_dependencies(state); - - dm_state = dm_atomic_get_new_state(state); - if (dm_state && dm_state->context) { - dc_state = dm_state->context; - amdgpu_dm_commit_streams(state, dc_state); - } + if (!adev->dm.hdcp_workqueue) + return; for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct dm_crtc_state *dm_new_crtc_state; struct amdgpu_dm_connector *aconnector; - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + if (!connector || connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) continue; aconnector = to_amdgpu_dm_connector(connector); - if (!adev->dm.hdcp_workqueue) - continue; + drm_dbg(dev, "[HDCP_DM] -------------- i : %x ----------\n", i); - pr_debug("[HDCP_DM] -------------- i : %x ----------\n", i); - - if (!connector) - continue; - - pr_debug("[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n", + drm_dbg(dev, "[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n", connector->index, connector->status, connector->dpms); - pr_debug("[HDCP_DM] state protection old: %x new: %x\n", + drm_dbg(dev, "[HDCP_DM] state protection old: %x new: %x\n", old_con_state->content_protection, new_con_state->content_protection); if (aconnector->dc_sink) { if (aconnector->dc_sink->sink_signal != SIGNAL_TYPE_VIRTUAL && aconnector->dc_sink->sink_signal != SIGNAL_TYPE_NONE) { - pr_debug("[HDCP_DM] pipe_ctx dispname=%s\n", + drm_dbg(dev, "[HDCP_DM] pipe_ctx dispname=%s\n", aconnector->dc_sink->edid_caps.display_name); } } @@ -10162,7 +10225,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) } if (old_crtc_state) - pr_debug("old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n", + drm_dbg(dev, "old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n", old_crtc_state->enable, old_crtc_state->active, old_crtc_state->mode_changed, @@ -10170,29 +10233,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) old_crtc_state->connectors_changed); if (new_crtc_state) - pr_debug("NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n", + drm_dbg(dev, "NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n", new_crtc_state->enable, new_crtc_state->active, new_crtc_state->mode_changed, new_crtc_state->active_changed, new_crtc_state->connectors_changed); - } - for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { - struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); - struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - - if (!adev->dm.hdcp_workqueue) - continue; - - new_crtc_state = NULL; - old_crtc_state = NULL; - - if (acrtc) { - new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); - old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base); - } dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); @@ -10236,7 +10283,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) new_con_state->content_protection >= DRM_MODE_CONTENT_PROTECTION_DESIRED) enable_encryption = true; - drm_info(adev_to_drm(adev), "[HDCP_DM] hdcp_update_display enable_encryption = %x\n", enable_encryption); + drm_info(dev, "[HDCP_DM] hdcp_update_display enable_encryption = %x\n", enable_encryption); if (aconnector->dc_link) hdcp_update_display( @@ -10244,6 +10291,45 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) new_con_state->hdcp_content_type, enable_encryption); } } +} + +/** + * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation. + * @state: The atomic state to commit + * + * This will tell DC to commit the constructed DC state from atomic_check, + * programming the hardware. Any failures here implies a hardware failure, since + * atomic check should have filtered anything non-kosher. + */ +static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_display_manager *dm = &adev->dm; + struct dm_atomic_state *dm_state; + struct dc_state *dc_state = NULL; + u32 i, j; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + unsigned long flags; + bool wait_for_vblank = true; + struct drm_connector *connector; + struct drm_connector_state *old_con_state = NULL, *new_con_state = NULL; + struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + int crtc_disable_count = 0; + + trace_amdgpu_dm_atomic_commit_tail_begin(state); + + drm_atomic_helper_update_legacy_modeset_state(dev, state); + drm_dp_mst_atomic_wait_for_dependencies(state); + + dm_state = dm_atomic_get_new_state(state); + if (dm_state && dm_state->context) { + dc_state = dm_state->context; + amdgpu_dm_commit_streams(state, dc_state); + } + + amdgpu_dm_update_hdcp(state); /* Handle connector state changes */ for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { @@ -10346,6 +10432,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) &stream_update); mutex_unlock(&dm->dc_lock); kfree(dummy_updates); + + drm_connector_update_privacy_screen(new_con_state); } /** @@ -10397,6 +10485,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) if (amdgpu_dm_crc_window_is_activated(crtc)) { uint8_t cnt; + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); for (cnt = 0; cnt < MAX_CRC_WINDOW_NUM; cnt++) { if (acrtc->dm_irq_params.window_param[cnt].enable) { 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 b937da0a4e4a..159f8ded0439 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright (C) 2015-2020 Advanced Micro Devices, Inc. All rights reserved. * @@ -152,6 +153,20 @@ struct idle_workqueue { bool running; }; +/** + * struct vupdate_offload_work - Work data for offloading task from vupdate handler + * @work: Kernel work data for the work event + * @adev: amdgpu_device back pointer + * @stream: DC stream associated with the crtc + * @adjust: DC CRTC timing adjust to be applied to the crtc + */ +struct vupdate_offload_work { + struct work_struct work; + struct amdgpu_device *adev; + struct dc_stream_state *stream; + struct dc_crtc_timing_adjust *adjust; +}; + #define MAX_LUMINANCE_DATA_POINTS 99 /** @@ -200,6 +215,11 @@ struct amdgpu_dm_backlight_caps { * @aux_support: Describes if the display supports AUX backlight. */ bool aux_support; + /** + * @brightness_mask: After deriving brightness, OR it with this mask. + * Workaround for panels with issues with certain brightness values. + */ + u32 brightness_mask; /** * @ac_level: the default brightness if booted on AC */ @@ -753,6 +773,9 @@ struct amdgpu_dm_connector { uint16_t vc_full_pbn; struct mutex handle_mst_msg_ready; + /* branch device specific data */ + uint32_t branch_ieee_oui; + /* TODO see if we can merge with ddc_bus or make a dm_connector */ struct amdgpu_i2c_adapter *i2c; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index ebabfe3a512f..c7387af725d6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2018 Advanced Micro Devices, Inc. * @@ -27,7 +28,6 @@ #include "amdgpu_dm.h" #include "dc.h" #include "modules/color/color_gamma.h" -#include "basics/conversion.h" /** * DOC: overview diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c index 033bd817d871..e20aa7438066 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2015 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h index 3da056c8d20b..95bdb8699d7f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2019 Advanced Micro Devices, Inc. * 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 2551823382f8..45feb404b097 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 @@ -299,6 +299,25 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); if (enable) { + struct dc *dc = adev->dm.dc; + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct psr_settings *psr = &acrtc_state->stream->link->psr_settings; + struct replay_settings *pr = &acrtc_state->stream->link->replay_settings; + bool sr_supported = (psr->psr_version != DC_PSR_VERSION_UNSUPPORTED) || + pr->config.replay_supported; + + /* + * IPS & self-refresh feature can cause vblank counter resets between + * vblank disable and enable. + * It may cause system stuck due to waiting for the vblank counter. + * Call this function to estimate missed vblanks by using timestamps and + * update the vblank counter in DRM. + */ + if (dc->caps.ips_support && + dc->config.disable_ips != DMUB_IPS_DISABLE_ALL && + sr_supported && vblank->config.disable_immediate) + drm_crtc_vblank_restore(crtc); + /* vblank irq on -> Only need vupdate irq in vrr mode */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); @@ -661,6 +680,15 @@ static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc, return -EINVAL; } + if (!state->legacy_cursor_update && amdgpu_dm_crtc_vrr_active(dm_crtc_state)) { + struct drm_plane_state *primary_state; + + /* Pull in primary plane for correct VRR handling */ + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) + return PTR_ERR(primary_state); + } + /* In some use cases, like reset, no stream is attached */ if (!dm_crtc_state->stream) return 0; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index b726bcd18e29..f263e1a4537e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2018 Advanced Micro Devices, Inc. * @@ -3105,6 +3106,35 @@ static int replay_get_state(void *data, u64 *val) return 0; } +/* + * Start / Stop capture Replay residency + */ +static int replay_set_residency(void *data, u64 val) +{ + struct amdgpu_dm_connector *connector = data; + struct dc_link *link = connector->dc_link; + bool is_start = (val != 0); + u32 residency = 0; + + link->dc->link_srv->edp_replay_residency(link, &residency, is_start, PR_RESIDENCY_MODE_PHY); + return 0; +} + +/* + * Read Replay residency + */ +static int replay_get_residency(void *data, u64 *val) +{ + struct amdgpu_dm_connector *connector = data; + struct dc_link *link = connector->dc_link; + u32 residency = 0; + + link->dc->link_srv->edp_replay_residency(link, &residency, false, PR_RESIDENCY_MODE_PHY); + *val = (u64)residency; + + return 0; +} + /* * Read PSR state */ @@ -3324,7 +3354,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(dmcub_trace_event_state_fops, dmcub_trace_event_state_g dmcub_trace_event_state_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(replay_state_fops, replay_get_state, NULL, "%llu\n"); - +DEFINE_DEBUGFS_ATTRIBUTE(replay_residency_fops, replay_get_residency, replay_set_residency, + "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(psr_fops, psr_get, NULL, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(psr_residency_fops, psr_read_residency, NULL, "%llu\n"); @@ -3502,6 +3533,8 @@ void connector_debugfs_init(struct amdgpu_dm_connector *connector) debugfs_create_file("replay_capability", 0444, dir, connector, &replay_capability_fops); debugfs_create_file("replay_state", 0444, dir, connector, &replay_state_fops); + debugfs_create_file_unsafe("replay_residency", 0444, dir, + connector, &replay_residency_fops); debugfs_create_file_unsafe("psr_capability", 0444, dir, connector, &psr_capability_fops); debugfs_create_file_unsafe("psr_state", 0444, dir, connector, &psr_fops); debugfs_create_file_unsafe("psr_residency", 0444, dir, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h index 071200473c27..122cdc124b3b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2018 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index c16962256514..58e084f52526 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2019 Advanced Micro Devices, Inc. * @@ -222,6 +223,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; link_adjust.auth_delay = 2; + link_adjust.retry_limit = MAX_NUM_OF_ATTEMPTS; if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; @@ -571,6 +573,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) link->dp.usb4_enabled = config->usb4_enabled; display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; link->adjust.auth_delay = 2; + link->adjust.retry_limit = MAX_NUM_OF_ATTEMPTS; link->adjust.hdcp1.disable = 0; hdcp_w->encryption_status[display->index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; @@ -723,8 +726,8 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, static const struct bin_attribute data_attr = { .attr = {.name = "hdcp_srm", .mode = 0664}, .size = PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, /* Limit SRM size */ - .write_new = srm_data_write, - .read_new = srm_data_read, + .write = srm_data_write, + .read = srm_data_read, }; struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h index 69b445b011c8..4faa344f196e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2019 Advanced Micro Devices, Inc. * 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 9e3e51a2dc49..fe100e4c9801 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 @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2015 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index b61e210f6246..a1c722112c22 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2015 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h index ba17c23b2706..4f6b58f4f90d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2015 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h index 6c9de834455b..3c9995275cbd 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2020 Advanced Micro Devices, Inc. * 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 7187d5aedf0a..5e92eaa67aa3 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 @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2012-15 Advanced Micro Devices, Inc. * @@ -329,6 +330,34 @@ static bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnect return true; } +static bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_dp_mst_port *port = aconnector->mst_output_port; + struct drm_dp_mst_port *port_parent; + struct drm_dp_aux *immediate_upstream_aux; + struct drm_dp_desc branch_desc; + + if (!port->parent) + return false; + + port_parent = port->parent->port_parent; + + immediate_upstream_aux = port_parent ? &port_parent->aux : port->mgr->aux; + + if (drm_dp_read_desc(immediate_upstream_aux, &branch_desc, true)) + return false; + + aconnector->branch_ieee_oui = (branch_desc.ident.oui[0] << 16) + + (branch_desc.ident.oui[1] << 8) + + (branch_desc.ident.oui[2]); + + drm_dbg_dp(port->aux.drm_dev, "MST branch oui 0x%x detected at %s\n", + aconnector->branch_ieee_oui, connector->name); + + return true; +} + static int dm_dp_mst_get_modes(struct drm_connector *connector) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); @@ -668,6 +697,9 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, drm_connector_set_path_property(connector, pathprop); + if (!retrieve_branch_specific_data(aconnector)) + aconnector->branch_ieee_oui = 0; + /* * Initialize connector state before adding the connectror to drm and * framebuffer lists @@ -809,6 +841,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, drm_dp_aux_init(&aconnector->dm_dp_aux.aux); drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux, &aconnector->base); + drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false); if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_eDP) return; @@ -821,13 +854,20 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, drm_connector_attach_dp_subconnector_property(&aconnector->base); } -int dm_mst_get_pbn_divider(struct dc_link *link) +uint32_t dm_mst_get_pbn_divider(struct dc_link *link) { + uint32_t pbn_div_x100; + uint64_t dividend, divisor; + if (!link) return 0; - return dc_link_bandwidth_kbps(link, - dc_link_get_link_cap(link)) / (8 * 1000 * 54); + dividend = (uint64_t)dc_link_bandwidth_kbps(link, dc_link_get_link_cap(link)) * 100; + divisor = 8 * 1000 * 54; + + pbn_div_x100 = div64_u64(dividend, divisor); + + return dfixed_const(pbn_div_x100) / 100; } struct dsc_mst_fairness_params { @@ -1762,14 +1802,20 @@ static bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_lin union lane_count_set lane_count; u8 dp_link_encoding; u8 link_bw_set = 0; + u8 data[16] = {0}; *cur_link_bw = 0; - if (drm_dp_dpcd_read(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, &dp_link_encoding, 1) != 1 || - drm_dp_dpcd_read(aux, DP_LANE_COUNT_SET, &lane_count.raw, 1) != 1 || - drm_dp_dpcd_read(aux, DP_LINK_BW_SET, &link_bw_set, 1) != 1) + if (drm_dp_dpcd_read(aux, DP_LINK_BW_SET, data, 16) != 16) return false; + dp_link_encoding = data[DP_MAIN_LINK_CHANNEL_CODING_SET - DP_LINK_BW_SET]; + link_bw_set = data[DP_LINK_BW_SET - DP_LINK_BW_SET]; + lane_count.raw = data[DP_LANE_COUNT_SET - DP_LINK_BW_SET]; + + drm_dbg_dp(aux->drm_dev, "MST_DSC downlink setting: %d, 0x%x x %d\n", + dp_link_encoding, link_bw_set, lane_count.bits.LANE_COUNT_SET); + switch (dp_link_encoding) { case DP_8b_10b_ENCODING: link_rate = link_bw_set; @@ -1866,8 +1912,10 @@ enum dc_status dm_dp_mst_is_port_support_mode( end_link_bw = aconnector->mst_local_bw; } - if (end_link_bw > 0 && stream_kbps > end_link_bw) { - DRM_DEBUG_DRIVER("MST_DSC dsc decode at last link." + if (end_link_bw > 0 && + stream_kbps > end_link_bw && + aconnector->branch_ieee_oui != DP_BRANCH_DEVICE_ID_90CC24) { + DRM_DEBUG_DRIVER("MST_DSC dsc decode at last link. " "Mode required bw can't fit into last link\n"); return DC_FAIL_BANDWIDTH_VALIDATE; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 600d6e221011..6f7ea684b555 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2012-15 Advanced Micro Devices, Inc. * @@ -59,7 +60,7 @@ enum mst_msg_ready_type { struct amdgpu_display_manager; struct amdgpu_dm_connector; -int dm_mst_get_pbn_divider(struct dc_link *link); +uint32_t dm_mst_get_pbn_divider(struct dc_link *link); void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c index 848c5b4bb301..e5771f490f2e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2018 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c index ff7b867ae98b..fd491b7a3cd7 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2021 Advanced Micro Devices, Inc. * @@ -26,7 +27,6 @@ #include "amdgpu_dm_psr.h" #include "dc_dmub_srv.h" #include "dc.h" -#include "dm_helpers.h" #include "amdgpu_dm.h" #include "modules/power/power_helpers.h" diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h index e2366321a3c1..4fb8626913cf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2021 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c index 41f07f13a7b5..82ea3fe5e764 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* * Copyright 2023 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.h index 8126bdb1eb6b..73b6c67ae5e7 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 2021 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c index 0005f5f8f34f..132de4071efd 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -1,3 +1,4 @@ +//SPDX-License-Identifier: MIT /* * Copyright 2015 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h index 95f890fda8aa..aa56fd6d56c3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h @@ -1,3 +1,4 @@ +//SPDX-License-Identifier: MIT /* * Copyright 2018 Advanced Micro Devices, Inc. * diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile index 3c9ecea7eebc..dc943abd6dba 100644 --- a/drivers/gpu/drm/amd/display/dc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -37,6 +37,7 @@ DC_LIBS += dcn301 DC_LIBS += dcn31 DC_LIBS += dml DC_LIBS += dml2 +DC_LIBS += soc_and_ip_translator endif DC_LIBS += dce120 diff --git a/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c b/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c index d897f8a30ede..4da5adab799c 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c @@ -1136,7 +1136,7 @@ static void calculate_bandwidth( } } } - data->total_dmifmc_urgent_trips = bw_ceil2(bw_div(data->total_requests_for_adjusted_dmif_size, (bw_add(dceip->dmif_request_buffer_size, bw_int_to_fixed(vbios->number_of_request_slots_gmc_reserves_for_dmif_per_channel * data->number_of_dram_channels)))), bw_int_to_fixed(1)); + data->total_dmifmc_urgent_trips = bw_ceil2(bw_div(data->total_requests_for_adjusted_dmif_size, (bw_add(dceip->dmif_request_buffer_size, bw_int_to_fixed((uint64_t)vbios->number_of_request_slots_gmc_reserves_for_dmif_per_channel * data->number_of_dram_channels)))), bw_int_to_fixed(1)); data->total_dmifmc_urgent_latency = bw_mul(vbios->dmifmc_urgent_latency, data->total_dmifmc_urgent_trips); data->total_display_reads_required_data = bw_int_to_fixed(0); data->total_display_reads_required_dram_access_data = bw_int_to_fixed(0); diff --git a/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c index 452206b5095e..6073cadde76c 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c +++ b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c @@ -284,7 +284,7 @@ struct fixed31_32 dc_fixpt_cos(struct fixed31_32 arg) dc_fixpt_mul( square, res), - n * (n - 1))); + (long long)n * (n - 1))); n -= 2; } while (n != 0); diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index 6d2924114a3e..b413a672c2c0 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -170,7 +170,7 @@ bool dal_vector_remove_at_index( memmove( vector->container + (index * vector->struct_size), vector->container + ((index + 1) * vector->struct_size), - (vector->count - index - 1) * vector->struct_size); + (size_t)(vector->count - index - 1) * vector->struct_size); vector->count -= 1; return true; @@ -219,7 +219,7 @@ bool dal_vector_insert_at( memmove( insert_address + vector->struct_size, insert_address, - vector->struct_size * (vector->count - position)); + (size_t)vector->struct_size * (vector->count - position)); memmove( insert_address, @@ -271,7 +271,7 @@ struct vector *dal_vector_clone( /* copy vector's data */ memmove(vec_cloned->container, vector->container, - vec_cloned->struct_size * vec_cloned->capacity); + (size_t)vec_cloned->struct_size * vec_cloned->capacity); return vec_cloned; } 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 67f08495b7e6..154fd2c18e88 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -174,11 +174,8 @@ static struct graphics_object_id bios_parser_get_connector_id( return object_id; } - if (tbl->ucNumberOfObjects <= i) { - dm_error("Can't find connector id %d in connector table of size %d.\n", - i, tbl->ucNumberOfObjects); + if (tbl->ucNumberOfObjects <= i) return object_id; - } id = le16_to_cpu(tbl->asObjects[i].usObjectID); object_id = object_id_from_bios_object_id(id); diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.c b/drivers/gpu/drm/amd/display/dc/bios/command_table.c index 2bcae0643e61..58e88778da7f 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/command_table.c +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.c @@ -993,7 +993,7 @@ static enum bp_result set_pixel_clock_v3( allocation.sPCLKInput.usFbDiv = cpu_to_le16((uint16_t)bp_params->feedback_divider); allocation.sPCLKInput.ucFracFbDiv = - (uint8_t)bp_params->fractional_feedback_divider; + (uint8_t)(bp_params->fractional_feedback_divider / 100000); allocation.sPCLKInput.ucPostDiv = (uint8_t)bp_params->pixel_clock_post_divider; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c index 33b9d36619ff..4071851f9e86 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c @@ -158,7 +158,6 @@ struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *p return NULL; } dce60_clk_mgr_construct(ctx, clk_mgr); - dce_clk_mgr_construct(ctx, clk_mgr); return &clk_mgr->base; } #endif diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c index 26feefbb8990..dbd6ef1b60a0 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c @@ -72,9 +72,9 @@ static const struct state_dependent_clocks dce80_max_clks_by_state[] = { /* ClocksStateLow */ { .display_clk_khz = 352000, .pixel_clk_khz = 330000}, /* ClocksStateNominal */ -{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, +{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 }, /* ClocksStatePerformance */ -{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; +{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 } }; int dentist_get_divider_from_did(int did) { @@ -245,6 +245,11 @@ int dce_set_clock( pxl_clk_params.target_pixel_clock_100hz = requested_clk_khz * 10; pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + /* DCE 6.0, DCE 6.4: engine clock is the same as PLL0 */ + if (clk_mgr_base->ctx->dce_version == DCE_VERSION_6_0 || + clk_mgr_base->ctx->dce_version == DCE_VERSION_6_4) + pxl_clk_params.pll_id = CLOCK_SOURCE_ID_PLL0; + if (clk_mgr_dce->dfs_bypass_active) pxl_clk_params.flags.SET_DISPCLK_DFS_BYPASS = true; @@ -386,8 +391,6 @@ static void dce_pplib_apply_display_requirements( { struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; - pp_display_cfg->avail_mclk_switch_time_us = dce110_get_min_vblank_time_us(context); - dce110_fill_display_configs(context, pp_display_cfg); if (memcmp(&dc->current_state->pp_display_cfg, pp_display_cfg, sizeof(*pp_display_cfg)) != 0) @@ -400,11 +403,9 @@ static void dce_update_clocks(struct clk_mgr *clk_mgr_base, { struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); struct dm_pp_power_level_change_request level_change_req; - int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; - - /*TODO: W/A for dal3 linux, investigate why this works */ - if (!clk_mgr_dce->dfs_bypass_active) - patched_disp_clk = patched_disp_clk * 115 / 100; + const int max_disp_clk = + clk_mgr_dce->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; + int patched_disp_clk = MIN(max_disp_clk, context->bw_ctx.bw.dce.dispclk_khz); level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); /* get max clock state from PPLIB */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c index f8409453434c..13cf415e38e5 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c @@ -120,9 +120,15 @@ void dce110_fill_display_configs( const struct dc_state *context, struct dm_pp_display_configuration *pp_display_cfg) { + struct dc *dc = context->clk_mgr->ctx->dc; int j; int num_cfgs = 0; + pp_display_cfg->avail_mclk_switch_time_us = dce110_get_min_vblank_time_us(context); + pp_display_cfg->disp_clk_khz = dc->clk_mgr->clks.dispclk_khz; + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us = 0; + pp_display_cfg->crtc_index = dc->res_pool->res_cap->num_timing_generator; + for (j = 0; j < context->stream_count; j++) { int k; @@ -164,6 +170,23 @@ void dce110_fill_display_configs( cfg->v_refresh /= stream->timing.h_total; cfg->v_refresh = (cfg->v_refresh + stream->timing.v_total / 2) / stream->timing.v_total; + + /* Find first CRTC index and calculate its line time. + * This is necessary for DPM on SI GPUs. + */ + if (cfg->pipe_idx < pp_display_cfg->crtc_index) { + const struct dc_crtc_timing *timing = + &context->streams[0]->timing; + + pp_display_cfg->crtc_index = cfg->pipe_idx; + pp_display_cfg->line_time_in_us = + timing->h_total * 10000 / timing->pix_clk_100hz; + } + } + + if (!num_cfgs) { + pp_display_cfg->crtc_index = 0; + pp_display_cfg->line_time_in_us = 0; } pp_display_cfg->display_count = num_cfgs; @@ -223,25 +246,8 @@ void dce11_pplib_apply_display_requirements( pp_display_cfg->min_engine_clock_deep_sleep_khz = context->bw_ctx.bw.dce.sclk_deep_sleep_khz; - pp_display_cfg->avail_mclk_switch_time_us = - dce110_get_min_vblank_time_us(context); - /* TODO: dce11.2*/ - pp_display_cfg->avail_mclk_switch_time_in_disp_active_us = 0; - - pp_display_cfg->disp_clk_khz = dc->clk_mgr->clks.dispclk_khz; - dce110_fill_display_configs(context, pp_display_cfg); - /* TODO: is this still applicable?*/ - if (pp_display_cfg->display_count == 1) { - const struct dc_crtc_timing *timing = - &context->streams[0]->timing; - - pp_display_cfg->crtc_index = - pp_display_cfg->disp_configs[0].pipe_idx; - pp_display_cfg->line_time_in_us = timing->h_total * 10000 / timing->pix_clk_100hz; - } - if (memcmp(&dc->current_state->pp_display_cfg, pp_display_cfg, sizeof(*pp_display_cfg)) != 0) dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c index 0267644717b2..a39641a0ff09 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c @@ -83,22 +83,13 @@ static const struct state_dependent_clocks dce60_max_clks_by_state[] = { static int dce60_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr_base) { struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); - int dprefclk_wdivider; - int dp_ref_clk_khz; - int target_div; + struct dc_context *ctx = clk_mgr_base->ctx; + int dp_ref_clk_khz = 0; - /* DCE6 has no DPREFCLK_CNTL to read DP Reference Clock source */ - - /* Read the mmDENTIST_DISPCLK_CNTL to get the currently - * programmed DID DENTIST_DPREFCLK_WDIVIDER*/ - REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider); - - /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ - target_div = dentist_get_divider_from_did(dprefclk_wdivider); - - /* Calculate the current DFS clock, in kHz.*/ - dp_ref_clk_khz = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR - * clk_mgr->base.dentist_vco_freq_khz) / target_div; + if (ASIC_REV_IS_TAHITI_P(ctx->asic_id.hw_internal_rev)) + dp_ref_clk_khz = ctx->dc_bios->fw_info.default_display_engine_pll_frequency; + else + dp_ref_clk_khz = clk_mgr_base->clks.dispclk_khz; return dce_adjust_dp_ref_freq_for_ss(clk_mgr, dp_ref_clk_khz); } @@ -109,8 +100,6 @@ static void dce60_pplib_apply_display_requirements( { struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; - pp_display_cfg->avail_mclk_switch_time_us = dce110_get_min_vblank_time_us(context); - dce110_fill_display_configs(context, pp_display_cfg); if (memcmp(&dc->current_state->pp_display_cfg, pp_display_cfg, sizeof(*pp_display_cfg)) != 0) @@ -123,11 +112,9 @@ static void dce60_update_clocks(struct clk_mgr *clk_mgr_base, { struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); struct dm_pp_power_level_change_request level_change_req; - int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; - - /*TODO: W/A for dal3 linux, investigate why this works */ - if (!clk_mgr_dce->dfs_bypass_active) - patched_disp_clk = patched_disp_clk * 115 / 100; + const int max_disp_clk = + clk_mgr_dce->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; + int patched_disp_clk = MIN(max_disp_clk, context->bw_ctx.bw.dce.dispclk_khz); level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); /* get max clock state from PPLIB */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c index 9e2ef0e724fc..7aee02d56292 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c @@ -563,6 +563,7 @@ static void vg_clk_mgr_helper_populate_bw_params( { int i, j; struct clk_bw_params *bw_params = clk_mgr->base.bw_params; + uint32_t max_dispclk = 0, max_dppclk = 0; j = -1; @@ -584,6 +585,15 @@ static void vg_clk_mgr_helper_populate_bw_params( return; } + /* dispclk and dppclk can be max at any voltage, same number of levels for both */ + if (clock_table->NumDispClkLevelsEnabled <= VG_NUM_DISPCLK_DPM_LEVELS && + clock_table->NumDispClkLevelsEnabled <= VG_NUM_DPPCLK_DPM_LEVELS) { + max_dispclk = find_max_clk_value(clock_table->DispClocks, clock_table->NumDispClkLevelsEnabled); + max_dppclk = find_max_clk_value(clock_table->DppClocks, clock_table->NumDispClkLevelsEnabled); + } else { + ASSERT(0); + } + bw_params->clk_table.num_entries = j + 1; for (i = 0; i < bw_params->clk_table.num_entries - 1; i++, j--) { @@ -591,11 +601,17 @@ static void vg_clk_mgr_helper_populate_bw_params( bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[j].memclk; bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[j].voltage; bw_params->clk_table.entries[i].dcfclk_mhz = find_dcfclk_for_voltage(clock_table, clock_table->DfPstateTable[j].voltage); + + /* Now update clocks we do read */ + bw_params->clk_table.entries[i].dispclk_mhz = max_dispclk; + bw_params->clk_table.entries[i].dppclk_mhz = max_dppclk; } bw_params->clk_table.entries[i].fclk_mhz = clock_table->DfPstateTable[j].fclk; bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[j].memclk; bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[j].voltage; bw_params->clk_table.entries[i].dcfclk_mhz = find_max_clk_value(clock_table->DcfClocks, VG_NUM_DCFCLK_DPM_LEVELS); + bw_params->clk_table.entries[i].dispclk_mhz = find_max_clk_value(clock_table->DispClocks, VG_NUM_DISPCLK_DPM_LEVELS); + bw_params->clk_table.entries[i].dppclk_mhz = find_max_clk_value(clock_table->DppClocks, VG_NUM_DPPCLK_DPM_LEVELS); bw_params->vram_type = bios_info->memory_type; bw_params->num_channels = bios_info->ma_channel_number; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 084994c650c4..8376e2b0e73d 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -1047,11 +1047,8 @@ static void dcn32_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) &num_entries_per_clk->num_fclk_levels); clk_mgr_base->bw_params->dc_mode_limit.fclk_mhz = dcn30_smu_get_dc_mode_max_dpm_freq(clk_mgr, PPCLK_FCLK); - if (num_entries_per_clk->num_memclk_levels >= num_entries_per_clk->num_fclk_levels) { - num_levels = num_entries_per_clk->num_memclk_levels; - } else { - num_levels = num_entries_per_clk->num_fclk_levels; - } + num_levels = max(num_entries_per_clk->num_memclk_levels, num_entries_per_clk->num_fclk_levels); + clk_mgr_base->bw_params->max_memclk_mhz = clk_mgr_base->bw_params->clk_table.entries[num_entries_per_clk->num_memclk_levels - 1].memclk_mhz; clk_mgr_base->bw_params->clk_table.num_entries = num_levels ? num_levels : 1; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c index b59703467128..47ff4c965d76 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c @@ -1404,11 +1404,7 @@ static void dcn401_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) clk_mgr_base->bw_params->clk_table.entries[num_entries_per_clk->num_fclk_levels - 1].fclk_mhz) clk_mgr_base->bw_params->dc_mode_limit.fclk_mhz = 0; - if (num_entries_per_clk->num_memclk_levels >= num_entries_per_clk->num_fclk_levels) { - num_levels = num_entries_per_clk->num_memclk_levels; - } else { - num_levels = num_entries_per_clk->num_fclk_levels; - } + num_levels = max(num_entries_per_clk->num_memclk_levels, num_entries_per_clk->num_fclk_levels); clk_mgr_base->bw_params->clk_table.num_entries = num_levels ? num_levels : 1; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 28aca7017f0f..5963019d1e74 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -84,6 +84,7 @@ #if defined(CONFIG_DRM_AMD_DC_FP) #include "dml2/dml2_internal_types.h" +#include "soc_and_ip_translator.h" #endif #include "dce/dmub_outbox.h" @@ -217,11 +218,24 @@ static bool create_links( connectors_num, num_virtual_links); - // condition loop on link_count to allow skipping invalid indices + /* When getting the number of connectors, the VBIOS reports the number of valid indices, + * but it doesn't say which indices are valid, and not every index has an actual connector. + * So, if we don't find a connector on an index, that is not an error. + * + * - There is no guarantee that the first N indices will be valid + * - VBIOS may report a higher amount of valid indices than there are actual connectors + * - Some VBIOS have valid configurations for more connectors than there actually are + * on the card. This may be because the manufacturer used the same VBIOS for different + * variants of the same card. + */ for (i = 0; dc->link_count < connectors_num && i < MAX_LINKS; i++) { + struct graphics_object_id connector_id = bios->funcs->get_connector_id(bios, i); struct link_init_data link_init_params = {0}; struct dc_link *link; + if (connector_id.id == CONNECTOR_ID_UNKNOWN) + continue; + DC_LOG_DC("BIOS object table - printing link object info for connector number: %d, link_index: %d", i, dc->link_count); link_init_params.ctx = dc->ctx; @@ -446,7 +460,9 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, * avoid conflicting with firmware updates. */ if (dc->ctx->dce_version > DCE_VERSION_MAX) { - if (dc->optimized_required || dc->wm_optimized_required) { + if ((dc->optimized_required || dc->wm_optimized_required) && + (stream->adjust.v_total_max != adjust->v_total_max || + stream->adjust.v_total_min != adjust->v_total_min)) { stream->adjust.timing_adjust_pending = true; return false; } @@ -934,21 +950,24 @@ static void dc_destruct(struct dc *dc) } dc_destroy_resource_pool(dc); - +#ifdef CONFIG_DRM_AMD_DC_FP + dc_destroy_soc_and_ip_translator(&dc->soc_and_ip_translator); +#endif if (dc->link_srv) link_destroy_link_service(&dc->link_srv); - if (dc->ctx->gpio_service) - dal_gpio_service_destroy(&dc->ctx->gpio_service); + if (dc->ctx) { + if (dc->ctx->gpio_service) + dal_gpio_service_destroy(&dc->ctx->gpio_service); - if (dc->ctx->created_bios) - dal_bios_parser_destroy(&dc->ctx->dc_bios); + if (dc->ctx->created_bios) + dal_bios_parser_destroy(&dc->ctx->dc_bios); + kfree(dc->ctx->logger); + dc_perf_trace_destroy(&dc->ctx->perf_trace); - kfree(dc->ctx->logger); - dc_perf_trace_destroy(&dc->ctx->perf_trace); - - kfree(dc->ctx); - dc->ctx = NULL; + kfree(dc->ctx); + dc->ctx = NULL; + } kfree(dc->bw_vbios); dc->bw_vbios = NULL; @@ -1137,6 +1156,9 @@ static bool dc_construct(struct dc *dc, dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); DC_FP_END(); } + dc->soc_and_ip_translator = dc_create_soc_and_ip_translator(dc_ctx->dce_version); + if (!dc->soc_and_ip_translator) + goto fail; #endif if (!create_links(dc, init_params->num_virtual_links)) @@ -2397,6 +2419,18 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params goto fail; } + /* + * If not already seamless, make transition seamless by inserting intermediate minimal transition + */ + if (dc->hwss.is_pipe_topology_transition_seamless && + !dc->hwss.is_pipe_topology_transition_seamless(dc, dc->current_state, context)) { + res = commit_minimal_transition_state(dc, context); + if (res != DC_OK) { + BREAK_TO_DEBUGGER(); + goto fail; + } + } + res = dc_commit_state_no_check(dc, context); for (i = 0; i < params->stream_count; i++) { @@ -3376,7 +3410,7 @@ static void update_seamless_boot_flags(struct dc *dc, int surface_count, struct dc_stream_state *stream) { - if (get_seamless_boot_stream_count(context) > 0 && surface_count > 0) { + if (get_seamless_boot_stream_count(context) > 0 && (surface_count > 0 || stream->dpms_off)) { /* Optimize seamless boot flag keeps clocks and watermarks high until * first flip. After first flip, optimization is required to lower * bandwidth. Important to note that it is expected UEFI will @@ -4135,7 +4169,7 @@ static void commit_planes_for_stream(struct dc *dc, } if (dc->hwseq->funcs.wait_for_pipe_update_if_needed) - dc->hwseq->funcs.wait_for_pipe_update_if_needed(dc, top_pipe_to_program, update_type == UPDATE_TYPE_FAST); + dc->hwseq->funcs.wait_for_pipe_update_if_needed(dc, top_pipe_to_program, update_type < UPDATE_TYPE_FULL); if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) { if (dc->hwss.subvp_pipe_control_lock) @@ -5088,129 +5122,6 @@ static bool fast_update_only(struct dc *dc, && !full_update_required(dc, srf_updates, surface_count, stream_update, stream); } -static bool update_planes_and_stream_v1(struct dc *dc, - struct dc_surface_update *srf_updates, int surface_count, - struct dc_stream_state *stream, - struct dc_stream_update *stream_update, - struct dc_state *state) -{ - const struct dc_stream_status *stream_status; - enum surface_update_type update_type; - struct dc_state *context; - struct dc_context *dc_ctx = dc->ctx; - int i, j; - struct dc_fast_update fast_update[MAX_SURFACES] = {0}; - - dc_exit_ips_for_hw_access(dc); - - populate_fast_updates(fast_update, srf_updates, surface_count, stream_update); - stream_status = dc_stream_get_status(stream); - context = dc->current_state; - - update_type = dc_check_update_surfaces_for_stream( - dc, srf_updates, surface_count, stream_update, stream_status); - /* It is possible to receive a flip for one plane while there are multiple flip_immediate planes in the same stream. - * E.g. Desktop and MPO plane are flip_immediate but only the MPO plane received a flip - * Force the other flip_immediate planes to flip so GSL doesn't wait for a flip that won't come. - */ - force_immediate_gsl_plane_flip(dc, srf_updates, surface_count); - - if (update_type >= UPDATE_TYPE_FULL) { - - /* initialize scratch memory for building context */ - context = dc_state_create_copy(state); - if (context == NULL) { - DC_ERROR("Failed to allocate new validate context!\n"); - return false; - } - - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *new_pipe = &context->res_ctx.pipe_ctx[i]; - struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; - - if (new_pipe->plane_state && new_pipe->plane_state != old_pipe->plane_state) - new_pipe->plane_state->force_full_update = true; - } - } else if (update_type == UPDATE_TYPE_FAST) { - /* - * Previous frame finished and HW is ready for optimization. - */ - dc_post_update_surfaces_to_stream(dc); - } - - for (i = 0; i < surface_count; i++) { - struct dc_plane_state *surface = srf_updates[i].surface; - - copy_surface_update_to_plane(surface, &srf_updates[i]); - - if (update_type >= UPDATE_TYPE_MED) { - for (j = 0; j < dc->res_pool->pipe_count; j++) { - struct pipe_ctx *pipe_ctx = - &context->res_ctx.pipe_ctx[j]; - - if (pipe_ctx->plane_state != surface) - continue; - - resource_build_scaling_params(pipe_ctx); - } - } - } - - copy_stream_update_to_stream(dc, context, stream, stream_update); - - if (update_type >= UPDATE_TYPE_FULL) { - if (dc->res_pool->funcs->validate_bandwidth(dc, context, DC_VALIDATE_MODE_AND_PROGRAMMING) != DC_OK) { - DC_ERROR("Mode validation failed for stream update!\n"); - dc_state_release(context); - return false; - } - } - - TRACE_DC_PIPE_STATE(pipe_ctx, i, MAX_PIPES); - - if (fast_update_only(dc, fast_update, srf_updates, surface_count, stream_update, stream) && - !dc->debug.enable_legacy_fast_update) { - commit_planes_for_stream_fast(dc, - srf_updates, - surface_count, - stream, - stream_update, - update_type, - context); - } else { - commit_planes_for_stream( - dc, - srf_updates, - surface_count, - stream, - stream_update, - update_type, - context); - } - /*update current_State*/ - if (dc->current_state != context) { - - struct dc_state *old = dc->current_state; - - dc->current_state = context; - dc_state_release(old); - - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; - - if (pipe_ctx->plane_state && pipe_ctx->stream == stream) - pipe_ctx->plane_state->force_full_update = false; - } - } - - /* Legacy optimization path for DCE. */ - if (update_type >= UPDATE_TYPE_FULL && dc_ctx->dce_version < DCE_VERSION_MAX) { - dc_post_update_surfaces_to_stream(dc); - TRACE_DCE_CLOCK_STATE(&context->bw_ctx.bw.dce); - } - return true; -} - static bool update_planes_and_stream_v2(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, @@ -5468,12 +5379,10 @@ void dc_commit_updates_for_stream(struct dc *dc, if (dc->ctx->dce_version >= DCN_VERSION_4_01) { ret = update_planes_and_stream_v3(dc, srf_updates, surface_count, stream, stream_update); - } else if (dc->ctx->dce_version >= DCN_VERSION_3_2) { + } else { ret = update_planes_and_stream_v2(dc, srf_updates, surface_count, stream, stream_update); - } else - ret = update_planes_and_stream_v1(dc, srf_updates, surface_count, stream, - stream_update, state); + } if (ret && dc->ctx->dce_version >= DCN_VERSION_3_2) clear_update_flags(srf_updates, surface_count, stream); @@ -6449,3 +6358,21 @@ bool dc_can_clear_cursor_limit(struct dc *dc) return false; } + +void dc_get_underflow_debug_data_for_otg(struct dc *dc, int primary_otg_inst, + struct dc_underflow_debug_data *out_data) +{ + struct timing_generator *tg = NULL; + + for (int i = 0; i < MAX_PIPES; i++) { + if (dc->res_pool->timing_generators[i] && + dc->res_pool->timing_generators[i]->inst == primary_otg_inst) { + tg = dc->res_pool->timing_generators[i]; + break; + } + } + + dc_exit_ips_for_hw_access(dc); + if (dc->hwss.get_underflow_debug_data) + dc->hwss.get_underflow_debug_data(dc, tg, out_data); +} 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 ec4e80e5b6eb..d82b1cb467f4 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 @@ -1177,6 +1177,8 @@ void hwss_wait_for_odm_update_pending_complete(struct dc *dc, struct dc_state *c tg = otg_master->stream_res.tg; if (tg->funcs->wait_odm_doublebuffer_pending_clear) tg->funcs->wait_odm_doublebuffer_pending_clear(tg); + if (tg->funcs->wait_otg_disable) + tg->funcs->wait_otg_disable(tg); } /* ODM update may require to reprogram blank pattern for each OPP */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c index 130455f2802a..b7a5de4ecb61 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c @@ -520,3 +520,10 @@ enum dc_status dc_link_validate_dp_tunneling_bandwidth(const struct dc *dc, cons return dc->link_srv->validate_dp_tunnel_bandwidth(dc, new_ctx); } +void dc_link_get_alpm_support(struct dc_link *link, + bool *auxless_support, + bool *auxwake_support) +{ + link->dc->link_srv->edp_get_alpm_support(link, auxless_support, auxwake_support); +} + 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 4d6181e7c612..d712548b1927 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -165,7 +165,13 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) case FAMILY_NV: dc_version = DCN_VERSION_2_0; - if (asic_id.chip_id == DEVICE_ID_NV_13FE || asic_id.chip_id == DEVICE_ID_NV_143F) { + if (asic_id.chip_id == DEVICE_ID_NV_13FE || + asic_id.chip_id == DEVICE_ID_NV_143F || + asic_id.chip_id == DEVICE_ID_NV_13F9 || + asic_id.chip_id == DEVICE_ID_NV_13FA || + asic_id.chip_id == DEVICE_ID_NV_13FB || + asic_id.chip_id == DEVICE_ID_NV_13FC || + asic_id.chip_id == DEVICE_ID_NV_13DB) { dc_version = DCN_VERSION_2_01; break; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stat.c b/drivers/gpu/drm/amd/display/dc/core/dc_stat.c index fe9f99f1bdf9..f976ffd6d466 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stat.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stat.c @@ -65,7 +65,7 @@ void dc_stat_get_dmub_notification(const struct dc *dc, struct dmub_notification notify->type == DMUB_NOTIFICATION_DPIA_NOTIFICATION || notify->type == DMUB_NOTIFICATION_SET_CONFIG_REPLY) { notify->link_index = - get_link_index_from_dpia_port_index(dc, notify->link_index); + get_link_index_from_dpia_port_index(dc, notify->instance); } } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index 4d6bc9fd4faa..9ac2d41f8fca 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -316,6 +316,9 @@ bool dc_stream_set_cursor_attributes( { bool result = false; + if (!stream) + return false; + if (dc_stream_check_cursor_attributes(stream, stream->ctx->dc->current_state, attributes)) { stream->cursor_attributes = *attributes; result = true; @@ -331,7 +334,10 @@ bool dc_stream_program_cursor_attributes( struct dc *dc; bool reset_idle_optimizations = false; - dc = stream ? stream->ctx->dc : NULL; + if (!stream) + return false; + + dc = stream->ctx->dc; if (dc_stream_set_cursor_attributes(stream, attributes)) { dc_z10_restore(dc); diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 59c07756130d..954a2786fbe2 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -55,7 +55,7 @@ struct aux_payload; struct set_config_cmd_payload; struct dmub_notification; -#define DC_VER "3.2.340" +#define DC_VER "3.2.349" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC @@ -234,6 +234,7 @@ struct lut3d_caps { * @ogam_ram: programmable out gamma LUT * @ocsc: output color space conversion matrix * @num_3dluts: MPC 3D LUT; always assumes a preceding shaper LUT + * @num_rmcm_3dluts: number of RMCM 3D LUTS; always assumes a preceding shaper LUT * @shared_3d_lut: shared 3D LUT flag. Can be either DPP or MPC, but single * instance * @ogam_rom_caps: pre-definied curve caps for regamma 1D LUT @@ -694,6 +695,15 @@ struct dc_clocks { int idle_fclk_khz; int subvp_prefetch_dramclk_khz; int subvp_prefetch_fclk_khz; + + /* Stutter efficiency is technically not clock values + * but stored here so the values are part of the update_clocks call similar to num_ways + * Efficiencies are stored as percentage (0-100) + */ + struct { + uint8_t base_efficiency; //LP1 + uint8_t low_power_efficiency; //LP2 + } stutter_efficiency; }; struct dc_bw_validation_profile { @@ -839,7 +849,8 @@ union dpia_debug_options { uint32_t enable_force_tbt3_work_around:1; /* bit 4 */ uint32_t disable_usb4_pm_support:1; /* bit 5 */ uint32_t enable_usb4_bw_zero_alloc_patch:1; /* bit 6 */ - uint32_t reserved:25; + uint32_t enable_bw_allocation_mode:1; /* bit 7 */ + uint32_t reserved:24; } bits; uint32_t raw; }; @@ -1072,6 +1083,7 @@ struct dc_debug_options { unsigned int force_mall_ss_num_ways; bool alloc_extra_way_for_cursor; uint32_t subvp_extra_lines; + bool disable_force_pstate_allow_on_hw_release; bool force_usr_allow; /* uses value at boot and disables switch */ bool disable_dtb_ref_clk_switch; @@ -1145,6 +1157,12 @@ struct dc_debug_options { bool enable_hblank_borrow; bool force_subvp_df_throttle; uint32_t acpi_transition_bitmasks[MAX_PIPES]; + bool enable_pg_cntl_debug_logs; + unsigned int auxless_alpm_lfps_setup_ns; + unsigned int auxless_alpm_lfps_period_ns; + unsigned int auxless_alpm_lfps_silence_ns; + unsigned int auxless_alpm_lfps_t1t2_us; + short auxless_alpm_lfps_t1t2_offset_us; }; @@ -1305,6 +1323,32 @@ union dc_3dlut_state { }; +#define MATRIX_9C__DIM_128_ALIGNED_LEN 16 // 9+8 : 9 * 8 + 7 * 8 = 72 + 56 = 128 % 128 = 0 +#define MATRIX_17C__DIM_128_ALIGNED_LEN 32 //17+15: 17 * 8 + 15 * 8 = 136 + 120 = 256 % 128 = 0 +#define MATRIX_33C__DIM_128_ALIGNED_LEN 64 //17+47: 17 * 8 + 47 * 8 = 136 + 376 = 512 % 128 = 0 + +struct lut_rgb { + uint16_t b; + uint16_t g; + uint16_t r; + uint16_t padding; +}; + +//this structure maps directly to how the lut will read it from memory +struct lut_mem_mapping { + union { + //NATIVE MODE 1, 2 + //RGB layout [b][g][r] //red is 128 byte aligned + //BGR layout [r][g][b] //blue is 128 byte aligned + struct lut_rgb rgb_17c[17][17][MATRIX_17C__DIM_128_ALIGNED_LEN]; + struct lut_rgb rgb_33c[33][33][MATRIX_33C__DIM_128_ALIGNED_LEN]; + + //TRANSFORMED + uint16_t linear_rgb[(33*33*33*4/128+1)*128]; + }; + uint16_t size; +}; + struct dc_rmcm_3dlut { bool isInUse; const struct dc_stream_state *stream; @@ -1733,7 +1777,7 @@ struct dc { struct dml2_configuration_options dml2_options; struct dml2_configuration_options dml2_dc_power_options; enum dc_acpi_cm_power_state power_state; - + struct soc_and_ip_translator *soc_and_ip_translator; }; struct dc_scaling_info { @@ -1786,6 +1830,23 @@ struct dc_surface_update { struct dc_bias_and_scale bias_and_scale; }; +struct dc_underflow_debug_data { + uint32_t otg_inst; + uint32_t otg_underflow; + uint32_t h_position; + uint32_t v_position; + uint32_t otg_frame_count; + struct dc_underflow_per_hubp_debug_data { + uint32_t hubp_underflow; + uint32_t hubp_in_blank; + uint32_t hubp_readline; + uint32_t det_config_error; + } hubps[MAX_PIPES]; + uint32_t curr_det_sizes[MAX_PIPES]; + uint32_t target_det_sizes[MAX_PIPES]; + uint32_t compbuf_config_error; +}; + /* * Create a new surface with default parameters; */ @@ -1804,8 +1865,6 @@ void dc_3dlut_func_retain(struct dc_3dlut *lut); void dc_post_update_surfaces_to_stream( struct dc *dc); -#include "dc_stream.h" - /** * struct dc_validation_set - Struct to store surface/stream associations for validation */ @@ -2447,6 +2506,12 @@ void dc_link_dp_dpia_handle_usb4_bandwidth_allocation_for_link( */ enum dc_status dc_link_validate_dp_tunneling_bandwidth(const struct dc *dc, const struct dc_state *new_ctx); +/* + * Get if ALPM is supported by the link + */ +void dc_link_get_alpm_support(struct dc_link *link, bool *auxless_support, + bool *auxwake_support); + /* Sink Interfaces - A sink corresponds to a display output device */ struct dc_container_id { @@ -2674,4 +2739,17 @@ bool dc_is_timing_changed(struct dc_stream_state *cur_stream, bool dc_is_cursor_limit_pending(struct dc *dc); bool dc_can_clear_cursor_limit(struct dc *dc); +/** + * dc_get_underflow_debug_data_for_otg() - Retrieve underflow debug data. + * + * @dc: Pointer to the display core context. + * @primary_otg_inst: Instance index of the primary OTG that underflowed. + * @out_data: Pointer to a dc_underflow_debug_data struct to be filled with debug information. + * + * This function collects and logs underflow-related HW states when underflow happens, + * including OTG underflow status, current read positions, frame count, and per-HUBP debug data. + * The results are stored in the provided out_data structure for further analysis or logging. + */ +void dc_get_underflow_debug_data_for_otg(struct dc *dc, int primary_otg_inst, struct dc_underflow_debug_data *out_data); + #endif /* DC_INTERFACE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index f5ef1a07078e..53a088ebddef 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -2010,11 +2010,12 @@ bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv) return result; } -bool dmub_lsdma_send_linear_copy_packet( +bool dmub_lsdma_send_linear_copy_command( struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t dst_addr, - uint32_t count) + uint32_t count +) { struct dc_context *dc_ctx = dc_dmub_srv->ctx; union dmub_rb_cmd cmd; @@ -2042,9 +2043,54 @@ bool dmub_lsdma_send_linear_copy_packet( return result; } +bool dmub_lsdma_send_linear_sub_window_copy_command( + struct dc_dmub_srv *dc_dmub_srv, + struct lsdma_linear_sub_window_copy_params copy_data +) +{ + struct dc_context *dc_ctx = dc_dmub_srv->ctx; + union dmub_rb_cmd cmd; + enum dm_dmub_wait_type wait_type; + struct dmub_cmd_lsdma_data *lsdma_data = &cmd.lsdma.lsdma_data; + bool result; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_common.header.type = DMUB_CMD__LSDMA; + cmd.cmd_common.header.sub_type = DMUB_CMD__LSDMA_LINEAR_SUB_WINDOW_COPY; + wait_type = DM_DMUB_WAIT_TYPE_NO_WAIT; + + lsdma_data->u.linear_sub_window_copy_data.tmz = copy_data.tmz; + lsdma_data->u.linear_sub_window_copy_data.element_size = copy_data.element_size; + lsdma_data->u.linear_sub_window_copy_data.src_lo = copy_data.src_lo; + lsdma_data->u.linear_sub_window_copy_data.src_hi = copy_data.src_hi; + lsdma_data->u.linear_sub_window_copy_data.src_x = copy_data.src_x; + lsdma_data->u.linear_sub_window_copy_data.src_y = copy_data.src_y; + lsdma_data->u.linear_sub_window_copy_data.src_pitch = copy_data.src_pitch; + lsdma_data->u.linear_sub_window_copy_data.src_slice_pitch = copy_data.src_slice_pitch; + lsdma_data->u.linear_sub_window_copy_data.dst_lo = copy_data.dst_lo; + lsdma_data->u.linear_sub_window_copy_data.dst_hi = copy_data.dst_hi; + lsdma_data->u.linear_sub_window_copy_data.dst_x = copy_data.dst_x; + lsdma_data->u.linear_sub_window_copy_data.dst_y = copy_data.dst_y; + lsdma_data->u.linear_sub_window_copy_data.dst_pitch = copy_data.dst_pitch; + lsdma_data->u.linear_sub_window_copy_data.dst_slice_pitch = copy_data.dst_slice_pitch; + lsdma_data->u.linear_sub_window_copy_data.rect_x = copy_data.rect_x; + lsdma_data->u.linear_sub_window_copy_data.rect_y = copy_data.rect_y; + lsdma_data->u.linear_sub_window_copy_data.src_cache_policy = copy_data.src_cache_policy; + lsdma_data->u.linear_sub_window_copy_data.dst_cache_policy = copy_data.dst_cache_policy; + + result = dc_wake_and_execute_dmub_cmd(dc_ctx, &cmd, wait_type); + + if (!result) + DC_ERROR("LSDMA Linear Sub Window Copy failed in DMUB"); + + return result; +} + bool dmub_lsdma_send_tiled_to_tiled_copy_command( struct dc_dmub_srv *dc_dmub_srv, - struct lsdma_send_tiled_to_tiled_copy_command_params params) + struct lsdma_send_tiled_to_tiled_copy_command_params params +) { struct dc_context *dc_ctx = dc_dmub_srv->ctx; union dmub_rb_cmd cmd; @@ -2066,8 +2112,8 @@ bool dmub_lsdma_send_tiled_to_tiled_copy_command( lsdma_data->u.tiled_copy_data.src_y = params.src_y; lsdma_data->u.tiled_copy_data.dst_x = params.dst_x; lsdma_data->u.tiled_copy_data.dst_y = params.dst_y; - lsdma_data->u.tiled_copy_data.src_width = params.src_width - 1; // LSDMA controller expects width -1 - lsdma_data->u.tiled_copy_data.dst_width = params.dst_width - 1; // LSDMA controller expects width -1 + lsdma_data->u.tiled_copy_data.src_width = params.src_width; + lsdma_data->u.tiled_copy_data.dst_width = params.dst_width; lsdma_data->u.tiled_copy_data.src_swizzle_mode = params.swizzle_mode; lsdma_data->u.tiled_copy_data.dst_swizzle_mode = params.swizzle_mode; lsdma_data->u.tiled_copy_data.src_element_size = params.element_size; @@ -2078,8 +2124,8 @@ bool dmub_lsdma_send_tiled_to_tiled_copy_command( lsdma_data->u.tiled_copy_data.tmz = params.tmz; lsdma_data->u.tiled_copy_data.read_compress = params.read_compress; lsdma_data->u.tiled_copy_data.write_compress = params.write_compress; - lsdma_data->u.tiled_copy_data.src_height = params.src_height - 1; // LSDMA controller expects height -1 - lsdma_data->u.tiled_copy_data.dst_height = params.dst_height - 1; // LSDMA controller expects height -1 + lsdma_data->u.tiled_copy_data.src_height = params.src_height; + lsdma_data->u.tiled_copy_data.dst_height = params.dst_height; lsdma_data->u.tiled_copy_data.data_format = params.data_format; lsdma_data->u.tiled_copy_data.max_com = params.max_com; lsdma_data->u.tiled_copy_data.max_uncom = params.max_uncom; @@ -2097,7 +2143,8 @@ bool dmub_lsdma_send_pio_copy_command( uint64_t src_addr, uint64_t dst_addr, uint32_t byte_count, - uint32_t overlap_disable) + uint32_t overlap_disable +) { struct dc_context *dc_ctx = dc_dmub_srv->ctx; union dmub_rb_cmd cmd; @@ -2130,7 +2177,8 @@ bool dmub_lsdma_send_pio_constfill_command( struct dc_dmub_srv *dc_dmub_srv, uint64_t dst_addr, uint32_t byte_count, - uint32_t data) + uint32_t data +) { struct dc_context *dc_ctx = dc_dmub_srv->ctx; union dmub_rb_cmd cmd; diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h index 8ea320f21269..7ef93444ef3c 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h @@ -211,11 +211,45 @@ void dc_dmub_srv_fams2_passthrough_flip( int surface_count); bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv); -bool dmub_lsdma_send_linear_copy_packet( +bool dmub_lsdma_send_linear_copy_command( struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t dst_addr, uint32_t count); + +struct lsdma_linear_sub_window_copy_params { + uint32_t src_lo; + uint32_t src_hi; + + uint32_t dst_lo; + uint32_t dst_hi; + + uint32_t src_x : 16; + uint32_t src_y : 16; + + uint32_t dst_x : 16; + uint32_t dst_y : 16; + + uint32_t rect_x : 16; + uint32_t rect_y : 16; + + uint32_t src_pitch : 16; + uint32_t dst_pitch : 16; + + uint32_t src_slice_pitch; + uint32_t dst_slice_pitch; + + uint32_t tmz : 1; + uint32_t element_size : 3; + uint32_t src_cache_policy : 3; + uint32_t dst_cache_policy : 3; + uint32_t padding : 22; +}; + +bool dmub_lsdma_send_linear_sub_window_copy_command( + struct dc_dmub_srv *dc_dmub_srv, + struct lsdma_linear_sub_window_copy_params copy_data +); bool dmub_lsdma_send_pio_copy_command( struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index 5ce1be362534..db669ccb1d58 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -1021,7 +1021,8 @@ union dp_128b_132b_supported_lttpr_link_rates { union dp_alpm_lttpr_cap { struct { uint8_t AUX_LESS_ALPM_SUPPORTED :1; - uint8_t RESERVED :7; + uint8_t ASSR_SUPPORTED :1; + uint8_t RESERVED :6; } bits; uint8_t raw; }; @@ -1119,10 +1120,11 @@ union dp_128b_132b_training_aux_rd_interval { union edp_alpm_caps { struct { - uint8_t AUX_WAKE_ALPM_CAP :1; - uint8_t PM_STATE_2A_SUPPORT :1; - uint8_t AUX_LESS_ALPM_CAP :1; - uint8_t RESERVED :5; + uint8_t AUX_WAKE_ALPM_CAP :1; + uint8_t PM_STATE_2A_SUPPORT :1; + uint8_t AUX_LESS_ALPM_CAP :1; + uint8_t AUX_LESS_ALPM_ML_PHY_SLEEP_STATUS_SUPPORTED :1; + uint8_t RESERVED :4; } bits; uint8_t raw; }; @@ -1282,6 +1284,7 @@ struct dpcd_caps { union dp_receive_port0_cap receive_port0_cap; /* Indicates the number of SST links supported by MSO (Multi-Stream Output) */ uint8_t mso_cap_sst_links_supported; + uint8_t dp_edp_general_cap_2; }; union dpcd_sink_ext_caps { @@ -1347,7 +1350,9 @@ union dpcd_alpm_configuration { struct { unsigned char ENABLE : 1; unsigned char IRQ_HPD_ENABLE : 1; - unsigned char RESERVED : 6; + unsigned char ALPM_MODE_SEL : 1; + unsigned char ACDS_PERIOD_DURATION : 1; + unsigned char RESERVED : 4; } bits; unsigned char raw; }; diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c index 7217de258851..51e41aed7316 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -732,7 +732,7 @@ char *dce_version_to_string(const int version) case DCN_VERSION_3_03: return "DCN 3.0.3"; case DCN_VERSION_3_1: - return "DCN 3.1"; + return "DCN 3.1.2"; case DCN_VERSION_3_14: return "DCN 3.1.4"; case DCN_VERSION_3_15: diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 375ca2f13b7a..619834a328a3 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -563,6 +563,12 @@ struct dc_info_packet_128 { uint8_t sb[128]; }; +struct dc_edid_read_policy { + uint32_t max_retry_count; + uint32_t delay_time_ms; + uint32_t ignore_checksum; +}; + #define DC_PLANE_UPDATE_TIMES_MAX 10 struct dc_plane_flip_time { @@ -571,6 +577,12 @@ struct dc_plane_flip_time { unsigned int prev_update_time_in_us; }; +enum dc_alpm_mode { + DC_ALPM_AUXWAKE = 0, + DC_ALPM_AUXLESS = 1, + DC_ALPM_UNSUPPORTED = 0xF, +}; + enum dc_psr_state { PSR_STATE0 = 0x0, PSR_STATE1, @@ -616,6 +628,7 @@ struct psr_config { unsigned int line_time_in_us; uint8_t rate_control_caps; uint16_t dsc_slice_height; + bool os_request_force_ffu; }; union dmcu_psr_level { @@ -728,6 +741,7 @@ struct psr_context { unsigned int line_time_in_us; uint8_t rate_control_caps; uint16_t dsc_slice_height; + bool os_request_force_ffu; }; struct colorspace_transform { @@ -1137,6 +1151,10 @@ struct replay_config { bool low_rr_supported; /* Replay Video Conferencing Optimization Enabled */ bool replay_video_conferencing_optimization_enabled; + /* Replay alpm mode */ + enum dc_alpm_mode alpm_mode; + /* Replay full screen only */ + bool os_request_force_ffu; }; /* Replay feature flags*/ diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c index 58c84f555c0f..0ce9489ac6b7 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c @@ -133,30 +133,34 @@ enum dsc_clk_source { }; -static void dccg35_set_dsc_clk_rcg(struct dccg *dccg, int inst, bool enable) +static void dccg35_set_dsc_clk_rcg(struct dccg *dccg, int inst, bool allow_rcg) { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); - if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dsc && enable) + if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dsc && allow_rcg) return; switch (inst) { case 0: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 1: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 2: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 3: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; default: BREAK_TO_DEBUGGER(); return; } + + /* Wait for clock to ramp */ + if (!allow_rcg) + udelay(10); } static void dccg35_set_symclk32_se_rcg( @@ -385,35 +389,34 @@ static void dccg35_set_dtbclk_p_rcg(struct dccg *dccg, int inst, bool enable) } } -static void dccg35_set_dppclk_rcg(struct dccg *dccg, - int inst, bool enable) +static void dccg35_set_dppclk_rcg(struct dccg *dccg, int inst, bool allow_rcg) { - struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); - - if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dpp && enable) + if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dpp && allow_rcg) return; switch (inst) { case 0: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 1: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 2: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; case 3: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, allow_rcg ? 0 : 1); break; default: BREAK_TO_DEBUGGER(); break; } - //DC_LOG_DEBUG("%s: inst(%d) DPPCLK rcg_disable: %d\n", __func__, inst, enable ? 0 : 1); + /* Wait for clock to ramp */ + if (!allow_rcg) + udelay(10); } static void dccg35_set_dpstreamclk_rcg( @@ -1177,32 +1180,34 @@ static void dccg35_update_dpp_dto(struct dccg *dccg, int dpp_inst, } static void dccg35_set_dppclk_root_clock_gating(struct dccg *dccg, - uint32_t dpp_inst, uint32_t enable) + uint32_t dpp_inst, uint32_t disallow_rcg) { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); - if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dpp) + if (!dccg->ctx->dc->debug.root_clock_optimization.bits.dpp && !disallow_rcg) return; switch (dpp_inst) { case 0: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, enable); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, disallow_rcg); break; case 1: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, enable); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, disallow_rcg); break; case 2: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, enable); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, disallow_rcg); break; case 3: - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, enable); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, disallow_rcg); break; default: break; } - //DC_LOG_DEBUG("%s: dpp_inst(%d) rcg: %d\n", __func__, dpp_inst, enable); + /* Wait for clock to ramp */ + if (disallow_rcg) + udelay(10); } static void dccg35_get_pixel_rate_div( @@ -1782,8 +1787,7 @@ static void dccg35_enable_dscclk(struct dccg *dccg, int inst) //Disable DTO switch (inst) { case 0: - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dsc) - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, 1); REG_UPDATE_2(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, 0, @@ -1791,8 +1795,7 @@ static void dccg35_enable_dscclk(struct dccg *dccg, int inst) REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK0_EN, 1); break; case 1: - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dsc) - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, 1); REG_UPDATE_2(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, 0, @@ -1800,8 +1803,7 @@ static void dccg35_enable_dscclk(struct dccg *dccg, int inst) REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK1_EN, 1); break; case 2: - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dsc) - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, 1); REG_UPDATE_2(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_PHASE, 0, @@ -1809,8 +1811,7 @@ static void dccg35_enable_dscclk(struct dccg *dccg, int inst) REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK2_EN, 1); break; case 3: - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dsc) - REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, 1); REG_UPDATE_2(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, 0, @@ -1821,6 +1822,9 @@ static void dccg35_enable_dscclk(struct dccg *dccg, int inst) BREAK_TO_DEBUGGER(); return; } + + /* Wait for clock to ramp */ + udelay(10); } static void dccg35_disable_dscclk(struct dccg *dccg, @@ -1864,6 +1868,9 @@ static void dccg35_disable_dscclk(struct dccg *dccg, default: return; } + + /* Wait for clock ramp */ + udelay(10); } static void dccg35_enable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint32_t link_enc_inst) @@ -2349,10 +2356,7 @@ static void dccg35_disable_symclk_se_cb( void dccg35_root_gate_disable_control(struct dccg *dccg, uint32_t pipe_idx, uint32_t disable_clock_gating) { - - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpp) { - dccg35_set_dppclk_root_clock_gating(dccg, pipe_idx, disable_clock_gating); - } + dccg35_set_dppclk_root_clock_gating(dccg, pipe_idx, disable_clock_gating); } static const struct dccg_funcs dccg35_funcs_new = { diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c index 668ee2d405fd..0b8ed9b94d3c 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c @@ -619,7 +619,7 @@ void dccg401_set_dp_dto( dto_integer = div_u64(params->pixclk_hz, dto_modulo_hz); dto_phase_hz = params->pixclk_hz - dto_integer * dto_modulo_hz; - if (dto_phase_hz <= 0) { + if (dto_phase_hz <= 0 && dto_integer <= 0) { /* negative pixel rate should never happen */ BREAK_TO_DEBUGGER(); return; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c index bb4ac5042c80..673bb87d2c17 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -725,14 +725,18 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, for (i = 0; i < AUX_MAX_RETRIES; i++) { DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, LOG_FLAG_I2cAux_DceAux, - "dce_aux_transfer_with_retries: link_index=%u: START: retry %d of %d: address=0x%04x length=%u write=%d mot=%d", + "dce_aux_transfer_with_retries: link_index=%u: START: retry %d of %d: " + "address=0x%04x length=%u write=%d mot=%d is_i2c=%d is_dpia=%d ddc_hw_inst=%d", ddc && ddc->link ? ddc->link->link_index : UINT_MAX, i + 1, (int)AUX_MAX_RETRIES, payload->address, payload->length, (unsigned int) payload->write, - (unsigned int) payload->mot); + (unsigned int) payload->mot, + payload->i2c_over_aux, + (ddc->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? true : false, + ddc->link->ddc_hw_inst); if (payload->write) dce_aux_log_payload(" write", payload->data, payload->length, 16); @@ -746,7 +750,9 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, LOG_FLAG_I2cAux_DceAux, - "dce_aux_transfer_with_retries: link_index=%u: END: retry %d of %d: address=0x%04x length=%u write=%d mot=%d: ret=%d operation_result=%d payload->reply=%u", + "dce_aux_transfer_with_retries: link_index=%u: END: retry %d of %d: " + "address=0x%04x length=%u write=%d mot=%d: ret=%d operation_result=%d " + "payload->reply=%u is_i2c=%d is_dpia=%d ddc_hw_inst=%d", ddc && ddc->link ? ddc->link->link_index : UINT_MAX, i + 1, (int)AUX_MAX_RETRIES, @@ -756,7 +762,10 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, (unsigned int) payload->mot, ret, (int)operation_result, - (unsigned int) *payload->reply); + (unsigned int) *payload->reply, + payload->i2c_over_aux, + (ddc->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? true : false, + ddc->link->ddc_hw_inst); if (!payload->write) dce_aux_log_payload(" read", payload->data, ret > 0 ? ret : 0, 16); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c index 0421b267a0b5..365dd2e37aea 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c @@ -591,7 +591,7 @@ static bool dce_i2c_hw_engine_submit_payload(struct dce_i2c_hw *dce_i2c_hw, DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; - request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.address = (uint8_t) ((payload->address << 1) | (payload->write ? 0 : 1)); request.length = payload->length; request.data = payload->data; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c index e188447c8156..2d73b94c515c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c @@ -451,7 +451,7 @@ static bool dce_i2c_sw_engine_submit_payload(struct dce_i2c_sw *engine, DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT : DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; - request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.address = (uint8_t) ((payload->address << 1) | (payload->write ? 0 : 1)); request.length = payload->length; request.data = payload->data; 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 4a9d07c31bc5..0c50fe266c8a 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 @@ -896,13 +896,13 @@ void dce110_link_encoder_construct( enc110->base.id, &bp_cap_info); /* Override features with DCE-specific values */ - if (BP_RESULT_OK == result) { + if (result == BP_RESULT_OK) { enc110->base.features.flags.bits.IS_HBR2_CAPABLE = bp_cap_info.DP_HBR2_EN; enc110->base.features.flags.bits.IS_HBR3_CAPABLE = bp_cap_info.DP_HBR3_EN; enc110->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; - } else { + } else if (result != BP_RESULT_NORECORD) { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, result); @@ -1798,13 +1798,13 @@ void dce60_link_encoder_construct( enc110->base.id, &bp_cap_info); /* Override features with DCE-specific values */ - if (BP_RESULT_OK == result) { + if (result == BP_RESULT_OK) { enc110->base.features.flags.bits.IS_HBR2_CAPABLE = bp_cap_info.DP_HBR2_EN; enc110->base.features.flags.bits.IS_HBR3_CAPABLE = bp_cap_info.DP_HBR3_EN; enc110->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; - } else { + } else if (result != BP_RESULT_NORECORD) { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, result); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c index ff3b8244ba3d..87af4fdc04a6 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c @@ -391,7 +391,7 @@ static bool dmub_psr_copy_settings(struct dmub_psr *dmub, sizeof(DP_SINK_DEVICE_STR_ID_1))) link->psr_settings.force_ffu_mode = 1; - copy_settings_data->force_ffu_mode = link->psr_settings.force_ffu_mode; + copy_settings_data->force_ffu_mode = link->psr_settings.force_ffu_mode || psr_context->os_request_force_ffu; if (((link->dpcd_caps.fec_cap.bits.FEC_CAPABLE && !link->dc->debug.disable_fec) && diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c index e7a318e26d38..65b979617b0c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c @@ -3,8 +3,8 @@ // Copyright 2024 Advanced Micro Devices, Inc. #include "dc.h" +#include "link.h" #include "dc_dmub_srv.h" -#include "dc_dp_types.h" #include "dmub/dmub_srv.h" #include "core_types.h" #include "dmub_replay.h" @@ -44,45 +44,21 @@ static void dmub_replay_get_state(struct dmub_replay *dmub, enum replay_state *s /* * Enable/Disable Replay. */ -static void dmub_replay_enable(struct dmub_replay *dmub, bool enable, bool wait, uint8_t panel_inst, - struct dc_link *link) +static void dmub_replay_enable(struct dmub_replay *dmub, bool enable, bool wait, uint8_t panel_inst) { union dmub_rb_cmd cmd; struct dc_context *dc = dmub->ctx; uint32_t retry_count; enum replay_state state = REPLAY_STATE_0; - struct pipe_ctx *pipe_ctx = NULL; - struct resource_context *res_ctx = &link->ctx->dc->current_state->res_ctx; - uint8_t i; memset(&cmd, 0, sizeof(cmd)); cmd.replay_enable.header.type = DMUB_CMD__REPLAY; cmd.replay_enable.data.panel_inst = panel_inst; cmd.replay_enable.header.sub_type = DMUB_CMD__REPLAY_ENABLE; - if (enable) { + if (enable) cmd.replay_enable.data.enable = REPLAY_ENABLE; - // hpo stream/link encoder assignments are not static, need to update everytime we try to enable replay - if (link->cur_link_settings.link_rate >= LINK_RATE_UHBR10) { - for (i = 0; i < MAX_PIPES; i++) { - if (res_ctx && - res_ctx->pipe_ctx[i].stream && - res_ctx->pipe_ctx[i].stream->link && - res_ctx->pipe_ctx[i].stream->link == link && - res_ctx->pipe_ctx[i].stream->link->connector_signal == SIGNAL_TYPE_EDP) { - pipe_ctx = &res_ctx->pipe_ctx[i]; - //TODO: refactor for multi edp support - break; - } - } - - if (!pipe_ctx) - return; - - cmd.replay_enable.data.hpo_stream_enc_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; - cmd.replay_enable.data.hpo_link_enc_inst = pipe_ctx->link_res.hpo_dp_link_enc->inst; - } - } else + else cmd.replay_enable.data.enable = REPLAY_DISABLE; cmd.replay_enable.header.payload_bytes = sizeof(struct dmub_rb_cmd_replay_enable_data); @@ -174,17 +150,6 @@ static bool dmub_replay_copy_settings(struct dmub_replay *dmub, copy_settings_data->digbe_inst = replay_context->digbe_inst; copy_settings_data->digfe_inst = replay_context->digfe_inst; - if (link->cur_link_settings.link_rate >= LINK_RATE_UHBR10) { - if (pipe_ctx->stream_res.hpo_dp_stream_enc) - copy_settings_data->hpo_stream_enc_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; - else - copy_settings_data->hpo_stream_enc_inst = 0; - if (pipe_ctx->link_res.hpo_dp_link_enc) - copy_settings_data->hpo_link_enc_inst = pipe_ctx->link_res.hpo_dp_link_enc->inst; - else - copy_settings_data->hpo_link_enc_inst = 0; - } - if (pipe_ctx->plane_res.dpp) copy_settings_data->dpp_inst = pipe_ctx->plane_res.dpp->inst; else @@ -225,6 +190,18 @@ static bool dmub_replay_copy_settings(struct dmub_replay *dmub, else copy_settings_data->flags.bitfields.force_wakeup_by_tps3 = 0; + copy_settings_data->flags.bitfields.alpm_mode = (enum dmub_alpm_mode)link->replay_settings.config.alpm_mode; + if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) { + copy_settings_data->auxless_alpm_data.lfps_setup_ns = dc->dc->debug.auxless_alpm_lfps_setup_ns; + copy_settings_data->auxless_alpm_data.lfps_period_ns = dc->dc->debug.auxless_alpm_lfps_period_ns; + copy_settings_data->auxless_alpm_data.lfps_silence_ns = dc->dc->debug.auxless_alpm_lfps_silence_ns; + copy_settings_data->auxless_alpm_data.lfps_t1_t2_override_us = + dc->dc->debug.auxless_alpm_lfps_t1t2_us; + copy_settings_data->auxless_alpm_data.lfps_t1_t2_offset_us = + dc->dc->debug.auxless_alpm_lfps_t1t2_offset_us; + copy_settings_data->auxless_alpm_data.lttpr_count = link->dc->link_srv->dp_get_lttpr_count(link); + } + dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT); return true; @@ -247,7 +224,6 @@ static void dmub_replay_set_coasting_vtotal(struct dmub_replay *dmub, pCmd->header.type = DMUB_CMD__REPLAY; pCmd->header.sub_type = DMUB_CMD__REPLAY_SET_COASTING_VTOTAL; pCmd->header.payload_bytes = sizeof(struct dmub_cmd_replay_set_coasting_vtotal_data); - pCmd->replay_set_coasting_vtotal_data.panel_inst = panel_inst; pCmd->replay_set_coasting_vtotal_data.coasting_vtotal = (coasting_vtotal & 0xFFFF); pCmd->replay_set_coasting_vtotal_data.coasting_vtotal_high = (coasting_vtotal & 0xFFFF0000) >> 16; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h index ccbe385e132c..e6346c0ffc0e 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h @@ -19,7 +19,7 @@ struct dmub_replay_funcs { void (*replay_get_state)(struct dmub_replay *dmub, enum replay_state *state, uint8_t panel_inst); void (*replay_enable)(struct dmub_replay *dmub, bool enable, bool wait, - uint8_t panel_inst, struct dc_link *link); + uint8_t panel_inst); bool (*replay_copy_settings)(struct dmub_replay *dmub, struct dc_link *link, struct replay_context *replay_context, uint8_t panel_inst); void (*replay_set_power_opt)(struct dmub_replay *dmub, unsigned int power_opt, diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c index e0558a78b11c..1c1228116487 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c @@ -812,7 +812,7 @@ bool dcn10_link_encoder_validate_output_with_stream( enc10, &stream->timing); break; case SIGNAL_TYPE_EDP: - is_valid = (stream->timing.pixel_encoding == PIXEL_ENCODING_RGB) ? true : false; + is_valid = stream->timing.pixel_encoding == PIXEL_ENCODING_RGB; break; case SIGNAL_TYPE_VIRTUAL: is_valid = true; diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c index 6ab2a218b769..6f30b6cc3c76 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c @@ -397,7 +397,7 @@ static bool enc35_is_fifo_enabled(struct stream_encoder *enc) uint32_t reset_val; REG_GET(DIG_FIFO_CTRL0, DIG_FIFO_ENABLE, &reset_val); - return (reset_val == 0) ? false : true; + return reset_val != 0; } void enc35_disable_fifo(struct stream_encoder *enc) { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c index 390c1a77fda6..9c58ff1069d6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c @@ -646,7 +646,7 @@ static void get_meta_and_pte_attr(struct display_mode_lib *mode_lib, // the dpte_group_bytes is reduced for the specific case of vertical // access of a tile surface that has dpte request of 8x1 ptes. - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else //full size diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c index 843d6004258c..570e6e39eb45 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c @@ -646,7 +646,7 @@ static void get_meta_and_pte_attr(struct display_mode_lib *mode_lib, // the dpte_group_bytes is reduced for the specific case of vertical // access of a tile surface that has dpte request of 8x1 ptes. - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else //full size diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c index 5718000627b0..f549da082c01 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c @@ -652,7 +652,7 @@ static void get_meta_and_pte_attr( if (hostvm_enable) rq_sizing_param->dpte_group_bytes = 512; else { - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else //full size diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c index 8d4873f80df0..4fb37df54d59 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c @@ -620,7 +620,7 @@ static void get_meta_and_pte_attr(struct display_mode_lib *mode_lib, if (hostvm_enable) rq_sizing_param->dpte_group_bytes = 512; else { - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else rq_sizing_param->dpte_group_bytes = 2048; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.c index 0c0b2d67c9cd..1aaa77265eed 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.c @@ -326,7 +326,7 @@ void dcn301_fpu_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_p struct dcn301_resource_pool *pool = TO_DCN301_RES_POOL(dc->res_pool); struct clk_limit_table *clk_table = &bw_params->clk_table; unsigned int i, closest_clk_lvl; - int j; + int j = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0; dc_assert_fp_enabled(); @@ -338,6 +338,15 @@ void dcn301_fpu_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_p dcn3_01_soc.num_chans = bw_params->num_channels; ASSERT(clk_table->num_entries); + + /* Prepass to find max clocks independent of voltage level. */ + for (i = 0; i < clk_table->num_entries; ++i) { + if (clk_table->entries[i].dispclk_mhz > max_dispclk_mhz) + max_dispclk_mhz = clk_table->entries[i].dispclk_mhz; + if (clk_table->entries[i].dppclk_mhz > max_dppclk_mhz) + max_dppclk_mhz = clk_table->entries[i].dppclk_mhz; + } + for (i = 0; i < clk_table->num_entries; i++) { /* loop backwards*/ for (closest_clk_lvl = 0, j = dcn3_01_soc.num_states - 1; j >= 0; j--) { @@ -353,8 +362,13 @@ void dcn301_fpu_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_p s[i].socclk_mhz = clk_table->entries[i].socclk_mhz; s[i].dram_speed_mts = clk_table->entries[i].memclk_mhz * 2; - s[i].dispclk_mhz = dcn3_01_soc.clock_limits[closest_clk_lvl].dispclk_mhz; - s[i].dppclk_mhz = dcn3_01_soc.clock_limits[closest_clk_lvl].dppclk_mhz; + /* Clocks independent of voltage level. */ + s[i].dispclk_mhz = max_dispclk_mhz ? max_dispclk_mhz : + dcn3_01_soc.clock_limits[closest_clk_lvl].dispclk_mhz; + + s[i].dppclk_mhz = max_dppclk_mhz ? max_dppclk_mhz : + dcn3_01_soc.clock_limits[closest_clk_lvl].dppclk_mhz; + s[i].dram_bw_per_chan_gbps = dcn3_01_soc.clock_limits[closest_clk_lvl].dram_bw_per_chan_gbps; s[i].dscclk_mhz = dcn3_01_soc.clock_limits[closest_clk_lvl].dscclk_mhz; @@ -435,12 +449,12 @@ void dcn301_fpu_calculate_wm_and_dlg(struct dc *dc, &context->bw_ctx.dml, pipes, pipe_cnt); /* WM Set C */ table_entry = &bw_params->wm_table.entries[WM_C]; - vlevel = min(max(vlevel_req, 2), vlevel_max); + vlevel = clamp(vlevel_req, 2, vlevel_max); calculate_wm_set_for_vlevel(vlevel, table_entry, &context->bw_ctx.bw.dcn.watermarks.c, &context->bw_ctx.dml, pipes, pipe_cnt); /* WM Set B */ table_entry = &bw_params->wm_table.entries[WM_B]; - vlevel = min(max(vlevel_req, 1), vlevel_max); + vlevel = clamp(vlevel_req, 1, vlevel_max); calculate_wm_set_for_vlevel(vlevel, table_entry, &context->bw_ctx.bw.dcn.watermarks.b, &context->bw_ctx.dml, pipes, pipe_cnt); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c index 8da97a96b1ce..8d7c59ec701d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c @@ -280,7 +280,7 @@ void dcn302_fpu_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_p j = 0; /* create the final dcfclk and uclk table */ while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { - if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { + if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; } else { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c index e968870a4b81..b5d3fd4c3694 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c @@ -285,7 +285,7 @@ void dcn303_fpu_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_p j = 0; /* create the final dcfclk and uclk table */ while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { - if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { + if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; } else { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c index c46bda2141ac..bfeb01477f0c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c @@ -615,7 +615,7 @@ static void get_meta_and_pte_attr( if (hostvm_enable) rq_sizing_param->dpte_group_bytes = 512; else { - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else rq_sizing_param->dpte_group_bytes = 2048; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c index b7d2a0caec11..04df263ff65e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c @@ -703,7 +703,7 @@ static void get_meta_and_pte_attr( if (hostvm_enable) rq_sizing_param->dpte_group_bytes = 512; else { - if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + if (!surf_linear && (log2_dpte_req_height_ptes == 0) && surf_vert) //reduced, in this case, will have page fault within a group rq_sizing_param->dpte_group_bytes = 512; else rq_sizing_param->dpte_group_bytes = 2048; 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 6160952245b4..18388fb00be8 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 @@ -3229,7 +3229,7 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa j = 0; // create the final dcfclk and uclk table while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { - if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { + if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; } else { @@ -3401,7 +3401,7 @@ bool dcn32_allow_subvp_with_active_margin(struct pipe_ctx *pipe) uint32_t height = subvp_active_margin_list.res[i].height; refresh_rate = (pipe->stream->timing.pix_clk_100hz * (uint64_t)100 + - pipe->stream->timing.v_total * pipe->stream->timing.h_total - (uint64_t)1); + (uint64_t)pipe->stream->timing.v_total * pipe->stream->timing.h_total - (uint64_t)1); refresh_rate = div_u64(refresh_rate, pipe->stream->timing.v_total); refresh_rate = div_u64(refresh_rate, pipe->stream->timing.h_total); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c index 9ba6cb67655f..6c75aa82327a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c @@ -139,7 +139,6 @@ void dml32_rq_dlg_get_rq_reg(display_rq_regs_st *rq_regs, if (dual_plane) { unsigned int p1_pte_row_height_linear = get_dpte_row_height_linear_c(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); - ; if (src->sw_mode == dm_sw_linear) ASSERT(p1_pte_row_height_linear >= 8); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c index 8839faf42207..e0a1dc89ce43 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c @@ -779,7 +779,7 @@ void dcn321_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_p j = 0; // create the final dcfclk and uclk table while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { - if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { + if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; } else { diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c index a06217a9eef6..f6879e622271 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c @@ -8,7 +8,7 @@ #include "dml2_internal_types.h" #include "dml21_utils.h" #include "dml21_translation_helper.h" -#include "bounding_boxes/dcn4_soc_bb.h" +#include "soc_and_ip_translator.h" static void dml21_populate_pmo_options(struct dml2_pmo_options *pmo_options, const struct dc *in_dc, @@ -38,375 +38,37 @@ static void dml21_populate_pmo_options(struct dml2_pmo_options *pmo_options, pmo_options->disable_drr_clamped_when_var_active = in_dc->debug.disable_fams_gaming == INGAME_FAMS_DISABLE; } -/* - * Populate dml_init based on default static values in soc bb. The default - * values are for reference and support at least minimal operation of current - * SoC and DCN hardware. The values could be modifed by subsequent override - * functions to reflect our true hardware capability. - */ -static void populate_default_dml_init_params(struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) +static enum dml2_project_id dml21_dcn_revision_to_dml2_project_id(enum dce_version dcn_version) { - switch (in_dc->ctx->dce_version) { + enum dml2_project_id project_id; + switch (dcn_version) { case DCN_VERSION_4_01: - dml_init->options.project_id = dml2_project_dcn4x_stage2_auto_drr_svp; - dml21_populate_pmo_options(&dml_init->options.pmo_options, in_dc, config); - dml_init->soc_bb = dml2_socbb_dcn401; - dml_init->soc_bb.qos_parameters = dml_dcn4_variant_a_soc_qos_params; - dml_init->ip_caps = dml2_dcn401_max_ip_caps; + project_id = dml2_project_dcn4x_stage2_auto_drr_svp; break; default: - memset(dml_init, 0, sizeof(*dml_init)); + project_id = dml2_project_invalid; DC_ERR("unsupported dcn version for DML21!"); - return; - } -} - -static void override_dml_init_with_values_from_hardware_default(struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) -{ - dml_init->soc_bb.dchub_refclk_mhz = in_dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; - dml_init->soc_bb.dprefclk_mhz = in_dc->clk_mgr->dprefclk_khz / 1000; - dml_init->soc_bb.dispclk_dppclk_vco_speed_mhz = in_dc->clk_mgr->dentist_vco_freq_khz / 1000.0; -} - -/* - * SMU stands for System Management Unit. It is a power management processor. - * It owns the initialization of dc's clock table and programming of clock values - * based on dc's requests. - * Our clock values in base soc bb is a dummy placeholder. The real clock values - * are retrieved from SMU firmware to dc clock table at runtime. - * This function overrides our dummy placeholder values with real values in dc - * clock table. - */ -static void override_dml_init_with_values_from_smu( - struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) -{ - int i; - const struct clk_bw_params *dc_bw_params = in_dc->clk_mgr->bw_params; - const struct clk_limit_table *dc_clk_table = &dc_bw_params->clk_table; - struct dml2_soc_state_table *dml_clk_table = &dml_init->soc_bb.clk_table; - - if (!in_dc->clk_mgr->funcs->is_smu_present || - !in_dc->clk_mgr->funcs->is_smu_present(in_dc->clk_mgr)) - /* skip if smu is not present */ - return; - - /* dcfclk */ - if (dc_clk_table->num_entries_per_clk.num_dcfclk_levels) { - dml_clk_table->dcfclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dcfclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->dcfclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.dcfclk_mhz && - dc_clk_table->entries[i].dcfclk_mhz > dc_bw_params->dc_mode_limit.dcfclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].dcfclk_mhz < dc_bw_params->dc_mode_limit.dcfclk_mhz) { - dml_clk_table->dcfclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dcfclk_mhz * 1000; - dml_clk_table->dcfclk.num_clk_values = i + 1; - } else { - dml_clk_table->dcfclk.clk_values_khz[i] = 0; - dml_clk_table->dcfclk.num_clk_values = i; - } - } else { - dml_clk_table->dcfclk.clk_values_khz[i] = dc_clk_table->entries[i].dcfclk_mhz * 1000; - } - } else { - dml_clk_table->dcfclk.clk_values_khz[i] = 0; - } - } + break; } - /* fclk */ - if (dc_clk_table->num_entries_per_clk.num_fclk_levels) { - dml_clk_table->fclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_fclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->fclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.fclk_mhz && - dc_clk_table->entries[i].fclk_mhz > dc_bw_params->dc_mode_limit.fclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].fclk_mhz < dc_bw_params->dc_mode_limit.fclk_mhz) { - dml_clk_table->fclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.fclk_mhz * 1000; - dml_clk_table->fclk.num_clk_values = i + 1; - } else { - dml_clk_table->fclk.clk_values_khz[i] = 0; - dml_clk_table->fclk.num_clk_values = i; - } - } else { - dml_clk_table->fclk.clk_values_khz[i] = dc_clk_table->entries[i].fclk_mhz * 1000; - } - } else { - dml_clk_table->fclk.clk_values_khz[i] = 0; - } - } - } - - /* uclk */ - if (dc_clk_table->num_entries_per_clk.num_memclk_levels) { - dml_clk_table->uclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_memclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->uclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.memclk_mhz && - dc_clk_table->entries[i].memclk_mhz > dc_bw_params->dc_mode_limit.memclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].memclk_mhz < dc_bw_params->dc_mode_limit.memclk_mhz) { - dml_clk_table->uclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.memclk_mhz * 1000; - dml_clk_table->uclk.num_clk_values = i + 1; - } else { - dml_clk_table->uclk.clk_values_khz[i] = 0; - dml_clk_table->uclk.num_clk_values = i; - } - } else { - dml_clk_table->uclk.clk_values_khz[i] = dc_clk_table->entries[i].memclk_mhz * 1000; - } - } else { - dml_clk_table->uclk.clk_values_khz[i] = 0; - } - } - } - - /* dispclk */ - if (dc_clk_table->num_entries_per_clk.num_dispclk_levels) { - dml_clk_table->dispclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dispclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->dispclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.dispclk_mhz && - dc_clk_table->entries[i].dispclk_mhz > dc_bw_params->dc_mode_limit.dispclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].dispclk_mhz < dc_bw_params->dc_mode_limit.dispclk_mhz) { - dml_clk_table->dispclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dispclk_mhz * 1000; - dml_clk_table->dispclk.num_clk_values = i + 1; - } else { - dml_clk_table->dispclk.clk_values_khz[i] = 0; - dml_clk_table->dispclk.num_clk_values = i; - } - } else { - dml_clk_table->dispclk.clk_values_khz[i] = dc_clk_table->entries[i].dispclk_mhz * 1000; - } - } else { - dml_clk_table->dispclk.clk_values_khz[i] = 0; - } - } - } - - /* dppclk */ - if (dc_clk_table->num_entries_per_clk.num_dppclk_levels) { - dml_clk_table->dppclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dppclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->dppclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.dppclk_mhz && - dc_clk_table->entries[i].dppclk_mhz > dc_bw_params->dc_mode_limit.dppclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].dppclk_mhz < dc_bw_params->dc_mode_limit.dppclk_mhz) { - dml_clk_table->dppclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dppclk_mhz * 1000; - dml_clk_table->dppclk.num_clk_values = i + 1; - } else { - dml_clk_table->dppclk.clk_values_khz[i] = 0; - dml_clk_table->dppclk.num_clk_values = i; - } - } else { - dml_clk_table->dppclk.clk_values_khz[i] = dc_clk_table->entries[i].dppclk_mhz * 1000; - } - } else { - dml_clk_table->dppclk.clk_values_khz[i] = 0; - } - } - } - - /* dtbclk */ - if (dc_clk_table->num_entries_per_clk.num_dtbclk_levels) { - dml_clk_table->dtbclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dtbclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->dtbclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.dtbclk_mhz && - dc_clk_table->entries[i].dtbclk_mhz > dc_bw_params->dc_mode_limit.dtbclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].dtbclk_mhz < dc_bw_params->dc_mode_limit.dtbclk_mhz) { - dml_clk_table->dtbclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dtbclk_mhz * 1000; - dml_clk_table->dtbclk.num_clk_values = i + 1; - } else { - dml_clk_table->dtbclk.clk_values_khz[i] = 0; - dml_clk_table->dtbclk.num_clk_values = i; - } - } else { - dml_clk_table->dtbclk.clk_values_khz[i] = dc_clk_table->entries[i].dtbclk_mhz * 1000; - } - } else { - dml_clk_table->dtbclk.clk_values_khz[i] = 0; - } - } - } - - /* socclk */ - if (dc_clk_table->num_entries_per_clk.num_socclk_levels) { - dml_clk_table->socclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_socclk_levels; - for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { - if (i < dml_clk_table->socclk.num_clk_values) { - if (config->use_clock_dc_limits && dc_bw_params->dc_mode_limit.socclk_mhz && - dc_clk_table->entries[i].socclk_mhz > dc_bw_params->dc_mode_limit.socclk_mhz) { - if (i == 0 || dc_clk_table->entries[i-1].socclk_mhz < dc_bw_params->dc_mode_limit.socclk_mhz) { - dml_clk_table->socclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.socclk_mhz * 1000; - dml_clk_table->socclk.num_clk_values = i + 1; - } else { - dml_clk_table->socclk.clk_values_khz[i] = 0; - dml_clk_table->socclk.num_clk_values = i; - } - } else { - dml_clk_table->socclk.clk_values_khz[i] = dc_clk_table->entries[i].socclk_mhz * 1000; - } - } else { - dml_clk_table->socclk.clk_values_khz[i] = 0; - } - } - } -} - -static void override_dml_init_with_values_from_vbios( - struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) -{ - const struct clk_bw_params *dc_bw_params = in_dc->clk_mgr->bw_params; - struct dml2_soc_bb *dml_soc_bb = &dml_init->soc_bb; - struct dml2_soc_state_table *dml_clk_table = &dml_init->soc_bb.clk_table; - - if (in_dc->ctx->dc_bios->bb_info.dram_clock_change_latency_100ns > 0) - dml_soc_bb->power_management_parameters.dram_clk_change_blackout_us = - (in_dc->ctx->dc_bios->bb_info.dram_clock_change_latency_100ns + 9) / 10; - - if (in_dc->ctx->dc_bios->bb_info.dram_sr_enter_exit_latency_100ns > 0) - dml_soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us = - (in_dc->ctx->dc_bios->bb_info.dram_sr_enter_exit_latency_100ns + 9) / 10; - - if (in_dc->ctx->dc_bios->bb_info.dram_sr_exit_latency_100ns > 0) - dml_soc_bb->power_management_parameters.stutter_exit_latency_us = - (in_dc->ctx->dc_bios->bb_info.dram_sr_exit_latency_100ns + 9) / 10; - - if (dc_bw_params->num_channels) { - dml_clk_table->dram_config.channel_count = dc_bw_params->num_channels; - dml_soc_bb->mall_allocated_for_dcn_mbytes = in_dc->caps.mall_size_total / 1048576; - } else if (in_dc->ctx->dc_bios->vram_info.num_chans) { - dml_clk_table->dram_config.channel_count = in_dc->ctx->dc_bios->vram_info.num_chans; - dml_soc_bb->mall_allocated_for_dcn_mbytes = in_dc->caps.mall_size_total / 1048576; - } - - if (dc_bw_params->dram_channel_width_bytes) { - dml_clk_table->dram_config.channel_width_bytes = dc_bw_params->dram_channel_width_bytes; - } else if (in_dc->ctx->dc_bios->vram_info.dram_channel_width_bytes) { - dml_clk_table->dram_config.channel_width_bytes = in_dc->ctx->dc_bios->vram_info.dram_channel_width_bytes; - } - - dml_init->soc_bb.xtalclk_mhz = in_dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency / 1000; -} - - -static void override_dml_init_with_values_from_dmub(struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) -{ - /* - * TODO - There seems to be overlaps between the values overriden from - * dmub and vbios. Investigate and identify the values that DMUB needs - * to own. - */ -// const struct dmub_soc_bb_params *dmub_bb_params = -// (const struct dmub_soc_bb_params *)config->bb_from_dmub; - -// if (dmub_bb_params == NULL) -// return; - -// if (dmub_bb_params->dram_clk_change_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.dram_clk_change_blackout_us = -// (double) dmub_bb_params->dram_clk_change_blackout_ns / 1000.0; -// if (dmub_bb_params->dram_clk_change_read_only_ns > 0) -// dml_init->soc_bb.power_management_parameters.dram_clk_change_read_only_us = -// (double) dmub_bb_params->dram_clk_change_read_only_ns / 1000.0; -// if (dmub_bb_params->dram_clk_change_write_only_ns > 0) -// dml_init->soc_bb.power_management_parameters.dram_clk_change_write_only_us = -// (double) dmub_bb_params->dram_clk_change_write_only_ns / 1000.0; -// if (dmub_bb_params->fclk_change_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.fclk_change_blackout_us = -// (double) dmub_bb_params->fclk_change_blackout_ns / 1000.0; -// if (dmub_bb_params->g7_ppt_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.g7_ppt_blackout_us = -// (double) dmub_bb_params->g7_ppt_blackout_ns / 1000.0; -// if (dmub_bb_params->stutter_enter_plus_exit_latency_ns > 0) -// dml_init->soc_bb.power_management_parameters.stutter_enter_plus_exit_latency_us = -// (double) dmub_bb_params->stutter_enter_plus_exit_latency_ns / 1000.0; -// if (dmub_bb_params->stutter_exit_latency_ns > 0) -// dml_init->soc_bb.power_management_parameters.stutter_exit_latency_us = -// (double) dmub_bb_params->stutter_exit_latency_ns / 1000.0; -// if (dmub_bb_params->z8_stutter_enter_plus_exit_latency_ns > 0) -// dml_init->soc_bb.power_management_parameters.z8_stutter_enter_plus_exit_latency_us = -// (double) dmub_bb_params->z8_stutter_enter_plus_exit_latency_ns / 1000.0; -// if (dmub_bb_params->z8_stutter_exit_latency_ns > 0) -// dml_init->soc_bb.power_management_parameters.z8_stutter_exit_latency_us = -// (double) dmub_bb_params->z8_stutter_exit_latency_ns / 1000.0; -// if (dmub_bb_params->z8_min_idle_time_ns > 0) -// dml_init->soc_bb.power_management_parameters.z8_min_idle_time = -// (double) dmub_bb_params->z8_min_idle_time_ns / 1000.0; -// #ifndef TRIM_DML2_DCN6B_IP_SENSITIVE -// if (dmub_bb_params->type_b_dram_clk_change_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.lpddr5_dram_clk_change_blackout_us = -// (double) dmub_bb_params->type_b_dram_clk_change_blackout_ns / 1000.0; -// if (dmub_bb_params->type_b_ppt_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.lpddr5_ppt_blackout_us = -// (double) dmub_bb_params->type_b_ppt_blackout_ns / 1000.0; -// #else -// if (dmub_bb_params->type_b_dram_clk_change_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.type_b_dram_clk_change_blackout_us = -// (double) dmub_bb_params->type_b_dram_clk_change_blackout_ns / 1000.0; -// if (dmub_bb_params->type_b_ppt_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.type_b_ppt_blackout_us = -// (double) dmub_bb_params->type_b_ppt_blackout_ns / 1000.0; -// #endif -// if (dmub_bb_params->vmin_limit_dispclk_khz > 0) -// dml_init->soc_bb.vmin_limit.dispclk_khz = dmub_bb_params->vmin_limit_dispclk_khz; -// if (dmub_bb_params->vmin_limit_dcfclk_khz > 0) -// dml_init->soc_bb.vmin_limit.dcfclk_khz = dmub_bb_params->vmin_limit_dcfclk_khz; -// if (dmub_bb_params->g7_temperature_read_blackout_ns > 0) -// dml_init->soc_bb.power_management_parameters.g7_temperature_read_blackout_us = -// (double) dmub_bb_params->g7_temperature_read_blackout_ns / 1000.0; -} - -static void override_dml_init_with_values_from_software_policy(struct dml2_initialize_instance_in_out *dml_init, - const struct dml2_configuration_options *config, - const struct dc *in_dc) -{ - if (!config->use_native_soc_bb_construction) { - dml_init->soc_bb = config->external_socbb_ip_params->soc_bb; - dml_init->ip_caps = config->external_socbb_ip_params->ip_params; - } - - if (in_dc->bb_overrides.sr_exit_time_ns) - dml_init->soc_bb.power_management_parameters.stutter_exit_latency_us = - in_dc->bb_overrides.sr_exit_time_ns / 1000.0; - - if (in_dc->bb_overrides.sr_enter_plus_exit_time_ns) - dml_init->soc_bb.power_management_parameters.stutter_enter_plus_exit_latency_us = - in_dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; - - if (in_dc->bb_overrides.dram_clock_change_latency_ns) - dml_init->soc_bb.power_management_parameters.dram_clk_change_blackout_us = - in_dc->bb_overrides.dram_clock_change_latency_ns / 1000.0; - - if (in_dc->bb_overrides.fclk_clock_change_latency_ns) - dml_init->soc_bb.power_management_parameters.fclk_change_blackout_us = - in_dc->bb_overrides.fclk_clock_change_latency_ns / 1000.0; + return project_id; } void dml21_populate_dml_init_params(struct dml2_initialize_instance_in_out *dml_init, const struct dml2_configuration_options *config, const struct dc *in_dc) { - populate_default_dml_init_params(dml_init, config, in_dc); + dml_init->options.project_id = dml21_dcn_revision_to_dml2_project_id(in_dc->ctx->dce_version); - override_dml_init_with_values_from_hardware_default(dml_init, config, in_dc); + if (config->use_native_soc_bb_construction) { + in_dc->soc_and_ip_translator->translator_funcs->get_soc_bb(&dml_init->soc_bb, in_dc, config); + in_dc->soc_and_ip_translator->translator_funcs->get_ip_caps(&dml_init->ip_caps); + } else { + dml_init->soc_bb = config->external_socbb_ip_params->soc_bb; + dml_init->ip_caps = config->external_socbb_ip_params->ip_params; + } - override_dml_init_with_values_from_smu(dml_init, config, in_dc); - - override_dml_init_with_values_from_vbios(dml_init, config, in_dc); - - override_dml_init_with_values_from_dmub(dml_init, config, in_dc); - - override_dml_init_with_values_from_software_policy(dml_init, config, in_dc); + dml21_populate_pmo_options(&dml_init->options.pmo_options, in_dc, config); } static unsigned int calc_max_hardware_v_total(const struct dc_stream_state *stream) @@ -463,11 +125,7 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf (stream->timing.h_total * (long long)calc_max_hardware_v_total(stream))); } - if (stream->timing.min_refresh_in_uhz > min_hardware_refresh_in_uhz) { - timing->drr_config.min_refresh_uhz = stream->timing.min_refresh_in_uhz; - } else { - timing->drr_config.min_refresh_uhz = min_hardware_refresh_in_uhz; - } + timing->drr_config.min_refresh_uhz = max(stream->timing.min_refresh_in_uhz, min_hardware_refresh_in_uhz); if (dml_ctx->config.callbacks.get_max_flickerless_instant_vtotal_increase && stream->ctx->dc->config.enable_fpo_flicker_detection == 1) @@ -1165,6 +823,8 @@ void dml21_copy_clocks_to_dc_state(struct dml2_context *in_ctx, struct dc_state context->bw_ctx.bw.dcn.clk.socclk_khz = in_ctx->v21.mode_programming.programming->min_clocks.dcn4x.socclk_khz; context->bw_ctx.bw.dcn.clk.subvp_prefetch_dramclk_khz = in_ctx->v21.mode_programming.programming->min_clocks.dcn4x.svp_prefetch_no_throttle.uclk_khz; context->bw_ctx.bw.dcn.clk.subvp_prefetch_fclk_khz = in_ctx->v21.mode_programming.programming->min_clocks.dcn4x.svp_prefetch_no_throttle.fclk_khz; + context->bw_ctx.bw.dcn.clk.stutter_efficiency.base_efficiency = in_ctx->v21.mode_programming.programming->stutter.base_percent_efficiency; + context->bw_ctx.bw.dcn.clk.stutter_efficiency.low_power_efficiency = in_ctx->v21.mode_programming.programming->stutter.low_power_percent_efficiency; } static struct dml2_dchub_watermark_regs *wm_set_index_to_dc_wm_set(union dcn_watermark_set *watermarks, const enum dml2_dchub_watermark_reg_set_index wm_index) diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c index 03de3cf06ae5..798abb2b2e67 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c @@ -60,7 +60,7 @@ static void dml21_init(const struct dc *in_dc, struct dml2_context *dml_ctx, con DC_FP_START(); - dml21_populate_dml_init_params(&dml_ctx->v21.dml_init, config, in_dc); + dml21_populate_dml_init_params(&dml_ctx->v21.dml_init, &dml_ctx->config, in_dc); dml2_initialize_instance(&dml_ctx->v21.dml_init); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h index b05030926ce8..91955bbe24b8 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h @@ -159,6 +159,8 @@ struct dml2_dchub_watermark_regs { uint32_t sr_exit; uint32_t sr_enter_z8; uint32_t sr_exit_z8; + uint32_t sr_enter_low_power; + uint32_t sr_exit_low_power; uint32_t uclk_pstate; uint32_t fclk_pstate; uint32_t temp_read_or_ppt; diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h index 8c9f414aa6bf..176f55947664 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h @@ -96,6 +96,8 @@ struct dml2_soc_power_management_parameters { double g7_temperature_read_blackout_us; double stutter_enter_plus_exit_latency_us; double stutter_exit_latency_us; + double low_power_stutter_enter_plus_exit_latency_us; + double low_power_stutter_exit_latency_us; double z8_stutter_enter_plus_exit_latency_us; double z8_stutter_exit_latency_us; double z8_min_idle_time; diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h index 98c0234e2f47..7de10a95cfdb 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h @@ -417,6 +417,8 @@ struct dml2_display_cfg_programming { struct { bool supported_in_blank; // Changing to configurations where this is false requires stutter to be disabled during the transition + uint8_t base_percent_efficiency; //LP1 + uint8_t low_power_percent_efficiency; //LP2 } stutter; struct { diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index b9cff2198511..bf62d42b3f78 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -1238,18 +1238,27 @@ static void CalculateDETBufferSize( static double CalculateRequiredDispclk( enum dml2_odm_mode ODMMode, - double PixelClock) + double PixelClock, + bool isTMDS420) { + double DispClk; if (ODMMode == dml2_odm_mode_combine_4to1) { - return PixelClock / 4.0; + DispClk = PixelClock / 4.0; } else if (ODMMode == dml2_odm_mode_combine_3to1) { - return PixelClock / 3.0; + DispClk = PixelClock / 3.0; } else if (ODMMode == dml2_odm_mode_combine_2to1) { - return PixelClock / 2.0; + DispClk = PixelClock / 2.0; } else { - return PixelClock; + DispClk = PixelClock; } + + if (isTMDS420) { + double TMDS420MinPixClock = PixelClock / 2.0; + DispClk = math_max2(DispClk, TMDS420MinPixClock); + } + + return DispClk; } static double TruncToValidBPP( @@ -4122,11 +4131,12 @@ static noinline_for_stack void CalculateODMMode( bool success; bool UseDSC = DSCEnable && (NumberOfDSCSlices > 0); enum dml2_odm_mode DecidedODMMode; + bool isTMDS420 = (OutFormat == dml2_420 && Output == dml2_hdmi); - SurfaceRequiredDISPCLKWithoutODMCombine = CalculateRequiredDispclk(dml2_odm_mode_bypass, PixelClock); - SurfaceRequiredDISPCLKWithODMCombineTwoToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_2to1, PixelClock); - SurfaceRequiredDISPCLKWithODMCombineThreeToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_3to1, PixelClock); - SurfaceRequiredDISPCLKWithODMCombineFourToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_4to1, PixelClock); + SurfaceRequiredDISPCLKWithoutODMCombine = CalculateRequiredDispclk(dml2_odm_mode_bypass, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineTwoToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_2to1, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineThreeToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_3to1, PixelClock, isTMDS420); + SurfaceRequiredDISPCLKWithODMCombineFourToOne = CalculateRequiredDispclk(dml2_odm_mode_combine_4to1, PixelClock, isTMDS420); #ifdef __DML_VBA_DEBUG__ DML_LOG_VERBOSE("DML::%s: ODMUse = %d\n", __func__, ODMUse); DML_LOG_VERBOSE("DML::%s: Output = %d\n", __func__, Output); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c index 28394de02885..640087e862f8 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c @@ -10,7 +10,7 @@ bool dml2_core_create(enum dml2_project_id project_id, struct dml2_core_instance { bool result = false; - if (out == 0) + if (!out) return false; memset(out, 0, sizeof(struct dml2_core_instance)); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h index 28687565ac22..ffb8c09f37a5 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h @@ -201,6 +201,8 @@ struct dml2_core_internal_watermarks { double WritebackFCLKChangeWatermark; double StutterExitWatermark; double StutterEnterPlusExitWatermark; + double LowPowerStutterExitWatermark; + double LowPowerStutterEnterPlusExitWatermark; double Z8StutterExitWatermark; double Z8StutterEnterPlusExitWatermark; double USRRetrainingWatermark; @@ -877,6 +879,9 @@ struct dml2_core_internal_mode_program { double Z8StutterEfficiency; unsigned int Z8NumberOfStutterBurstsPerFrame; double Z8StutterEfficiencyNotIncludingVBlank; + double LowPowerStutterEfficiency; + double LowPowerStutterEfficiencyNotIncludingVBlank; + unsigned int LowPowerNumberOfStutterBurstsPerFrame; double StutterPeriod; double Z8StutterEfficiencyBestCase; unsigned int Z8NumberOfStutterBurstsPerFrameBestCase; @@ -1016,6 +1021,8 @@ struct dml2_core_internal_SOCParametersList { double FCLKChangeLatency; double SRExitTime; double SREnterPlusExitTime; + double SRExitTimeLowPower; + double SREnterPlusExitTimeLowPower; double SRExitZ8Time; double SREnterPlusExitZ8Time; double USRRetrainingLatency; @@ -1851,9 +1858,11 @@ struct dml2_core_calcs_CalculateStutterEfficiency_params { unsigned int CompbufReservedSpaceZs; bool hw_debug5; double SRExitTime; + double SRExitTimeLowPower; double SRExitZ8Time; bool SynchronizeTimings; double StutterEnterPlusExitWatermark; + double LowPowerStutterEnterPlusExitWatermark; double Z8StutterEnterPlusExitWatermark; bool ProgressiveToInterlaceUnitInOPP; double *MinTTUVBlank; @@ -1879,7 +1888,10 @@ struct dml2_core_calcs_CalculateStutterEfficiency_params { // output double *StutterEfficiencyNotIncludingVBlank; double *StutterEfficiency; + double *LowPowerStutterEfficiencyNotIncludingVBlank; + double *LowPowerStutterEfficiency; unsigned int *NumberOfStutterBurstsPerFrame; + unsigned int *LowPowerNumberOfStutterBurstsPerFrame; double *Z8StutterEfficiencyNotIncludingVBlank; double *Z8StutterEfficiency; unsigned int *Z8NumberOfStutterBurstsPerFrame; diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c index 3861bc6c9621..dfd01440737d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c @@ -20,7 +20,7 @@ bool dml2_dpmm_create(enum dml2_project_id project_id, struct dml2_dpmm_instance { bool result = false; - if (out == 0) + if (!out) return false; memset(out, 0, sizeof(struct dml2_dpmm_instance)); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c index cd3fbc0591d8..c60b8fe90819 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c @@ -15,7 +15,7 @@ bool dml2_mcg_create(enum dml2_project_id project_id, struct dml2_mcg_instance * { bool result = false; - if (out == 0) + if (!out) return false; memset(out, 0, sizeof(struct dml2_mcg_instance)); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c index 7ed0242a4b33..55d2464365d0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c @@ -26,7 +26,7 @@ bool dml2_pmo_create(enum dml2_project_id project_id, struct dml2_pmo_instance * { bool result = false; - if (out == 0) + if (!out) return false; memset(out, 0, sizeof(struct dml2_pmo_instance)); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c index 5f1b49a50049..4cfe64aa8492 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c @@ -473,7 +473,6 @@ static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes) { bool sorted, swapped; unsigned int cur_index; - unsigned int temp; int odm_slice_index; for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) { @@ -489,9 +488,8 @@ static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes) swapped = false; while (!sorted) { if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) { - temp = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index]; - pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]; - pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1] = temp; + swap(pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1], + pipes->pipes_assigned_to_plane[odm_slice_index][cur_index]); swapped = true; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c index a56e75cdf712..c59f825cfae9 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c @@ -654,14 +654,14 @@ static void set_phantom_stream_timing(struct dml2_context *ctx, struct dc_state unsigned int svp_height, unsigned int svp_vstartup) { - unsigned int i, pipe_idx; + unsigned int i; double line_time, fp_and_sync_width_time; struct pipe_ctx *pipe; uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines; static const double cvt_rb_vblank_max = ((double) 460 / (1000 * 1000)); // Find DML pipe index (pipe_idx) using dc_pipe_idx - for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) { + for (i = 0; i < ctx->config.dcn_pipe_count; i++) { pipe = &state->res_ctx.pipe_ctx[i]; if (!pipe->stream) @@ -669,8 +669,6 @@ static void set_phantom_stream_timing(struct dml2_context *ctx, struct dc_state if (i == dc_pipe_idx) break; - - pipe_idx++; } // Calculate lines required for pstate allow width and FW processing delays diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c index 0318260370ed..9deb03a18ccc 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c @@ -535,7 +535,7 @@ static bool dml2_validate_only(struct dc_state *context, enum dc_validate_mode v if (result) result = does_configuration_meet_sw_policies(dml2, &dml2->v20.scratch.cur_display_config, &dml2->v20.scratch.mode_support_info); - return (result == 1) ? true : false; + return result == 1; } static void dml2_apply_debug_options(const struct dc *dc, struct dml2_context *dml2) diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c index 75fb77bca83b..01480a04f85e 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c @@ -520,6 +520,15 @@ void dpp1_dppclk_control( REG_UPDATE(DPP_CONTROL, DPP_CLOCK_ENABLE, 0); } +void dpp_force_disable_cursor(struct dpp *dpp_base) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + /* Force disable cursor */ + REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, 0); + dpp_base->pos.cur0_ctl.bits.cur0_enable = 0; +} + static const struct dpp_funcs dcn10_dpp_funcs = { .dpp_read_state = dpp_read_state, .dpp_reset = dpp_reset, diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h index c48139bed11f..f466182963f7 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h @@ -1525,4 +1525,6 @@ void dpp1_construct(struct dcn10_dpp *dpp1, void dpp1_cm_get_gamut_remap(struct dpp *dpp_base, struct dpp_grph_csc_adjustment *adjust); +void dpp_force_disable_cursor(struct dpp *dpp_base); + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c index 2d70586cef40..09be2a90cc79 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c @@ -1494,6 +1494,7 @@ static struct dpp_funcs dcn30_dpp_funcs = { .dpp_dppclk_control = dpp1_dppclk_control, .dpp_set_hdr_multiplier = dpp3_set_hdr_multiplier, .dpp_get_gamut_remap = dpp3_cm_get_gamut_remap, + .dpp_force_disable_cursor = dpp_force_disable_cursor, }; diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.h index 5a6a861402b3..5f6b431ec398 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.h +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.h @@ -673,6 +673,16 @@ struct dcn401_dpp { struct pwl_params pwl_data; }; +enum dcn401_dscl_mode_sel { + DCN401_DSCL_MODE_SCALING_444_BYPASS = 0, + DCN401_DSCL_MODE_SCALING_444_RGB_ENABLE = 1, + DCN401_DSCL_MODE_SCALING_444_YCBCR_ENABLE = 2, + DCN401_DSCL_MODE_SCALING_420_YCBCR_ENABLE = 3, + DCN401_DSCL_MODE_SCALING_420_LUMA_BYPASS = 4, + DCN401_DSCL_MODE_SCALING_420_CHROMA_BYPASS = 5, + DCN401_DSCL_MODE_DSCL_BYPASS = 6 +}; + bool dpp401_construct(struct dcn401_dpp *dpp401, struct dc_context *ctx, uint32_t inst, diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_dscl.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_dscl.c index 2f92e7d4981b..6df3419f825f 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_dscl.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_dscl.c @@ -78,16 +78,6 @@ enum dscl_autocal_mode { AUTOCAL_MODE_AUTOREPLICATE = 3 }; -enum dscl_mode_sel { - DSCL_MODE_SCALING_444_BYPASS = 0, - DSCL_MODE_SCALING_444_RGB_ENABLE = 1, - DSCL_MODE_SCALING_444_YCBCR_ENABLE = 2, - DSCL_MODE_SCALING_420_YCBCR_ENABLE = 3, - DSCL_MODE_SCALING_420_LUMA_BYPASS = 4, - DSCL_MODE_SCALING_420_CHROMA_BYPASS = 5, - DSCL_MODE_DSCL_BYPASS = 6 -}; - static int dpp401_dscl_get_pixel_depth_val(enum lb_pixel_depth depth) { if (depth == LB_PIXEL_DEPTH_30BPP) @@ -122,7 +112,7 @@ static bool dpp401_dscl_is_420_format(enum pixel_format format) return false; } -static enum dscl_mode_sel dpp401_dscl_get_dscl_mode( +static enum dcn401_dscl_mode_sel dpp401_dscl_get_dscl_mode( struct dpp *dpp_base, const struct scaler_data *data, bool dbg_always_scale) @@ -132,7 +122,7 @@ static enum dscl_mode_sel dpp401_dscl_get_dscl_mode( if (dpp_base->caps->dscl_data_proc_format == DSCL_DATA_PRCESSING_FIXED_FORMAT) { /* DSCL is processing data in fixed format */ if (data->format == PIXEL_FORMAT_FP16) - return DSCL_MODE_DSCL_BYPASS; + return DCN401_DSCL_MODE_DSCL_BYPASS; } if (data->ratios.horz.value == one @@ -140,20 +130,20 @@ static enum dscl_mode_sel dpp401_dscl_get_dscl_mode( && data->ratios.horz_c.value == one && data->ratios.vert_c.value == one && !dbg_always_scale) - return DSCL_MODE_SCALING_444_BYPASS; + return DCN401_DSCL_MODE_SCALING_444_BYPASS; if (!dpp401_dscl_is_420_format(data->format)) { if (dpp401_dscl_is_video_format(data->format)) - return DSCL_MODE_SCALING_444_YCBCR_ENABLE; + return DCN401_DSCL_MODE_SCALING_444_YCBCR_ENABLE; else - return DSCL_MODE_SCALING_444_RGB_ENABLE; + return DCN401_DSCL_MODE_SCALING_444_RGB_ENABLE; } if (data->ratios.horz.value == one && data->ratios.vert.value == one) - return DSCL_MODE_SCALING_420_LUMA_BYPASS; + return DCN401_DSCL_MODE_SCALING_420_LUMA_BYPASS; if (data->ratios.horz_c.value == one && data->ratios.vert_c.value == one) - return DSCL_MODE_SCALING_420_CHROMA_BYPASS; + return DCN401_DSCL_MODE_SCALING_420_CHROMA_BYPASS; - return DSCL_MODE_SCALING_420_YCBCR_ENABLE; + return DCN401_DSCL_MODE_SCALING_420_YCBCR_ENABLE; } static void dpp401_power_on_dscl( @@ -1071,7 +1061,7 @@ void dpp401_dscl_set_scaler_manual_scale(struct dpp *dpp_base, uint32_t v_num_taps_c = scl_data->taps.v_taps_c - 1; uint32_t h_num_taps = scl_data->taps.h_taps - 1; uint32_t h_num_taps_c = scl_data->taps.h_taps_c - 1; - enum dscl_mode_sel dscl_mode = dpp401_dscl_get_dscl_mode( + enum dcn401_dscl_mode_sel dscl_mode = dpp401_dscl_get_dscl_mode( dpp_base, scl_data, dpp_base->ctx->dc->debug.always_scale); bool ycbcr = scl_data->format >= PIXEL_FORMAT_VIDEO_BEGIN && scl_data->format <= PIXEL_FORMAT_VIDEO_END; @@ -1102,7 +1092,7 @@ void dpp401_dscl_set_scaler_manual_scale(struct dpp *dpp_base, dpp->scl_data = *scl_data; if ((dpp->base.ctx->dc->config.use_spl) && (!dpp->base.ctx->dc->debug.disable_spl)) { - dscl_mode = (enum dscl_mode_sel) scl_data->dscl_prog_data.dscl_mode; + dscl_mode = (enum dcn401_dscl_mode_sel) scl_data->dscl_prog_data.dscl_mode; rect = (struct rect *)&scl_data->dscl_prog_data.recout; mpc_width = scl_data->dscl_prog_data.mpc_size.width; mpc_height = scl_data->dscl_prog_data.mpc_size.height; @@ -1112,7 +1102,7 @@ void dpp401_dscl_set_scaler_manual_scale(struct dpp *dpp_base, h_num_taps_c = scl_data->dscl_prog_data.taps.h_taps_c; } if (dpp_base->ctx->dc->debug.enable_mem_low_power.bits.dscl) { - if (dscl_mode != DSCL_MODE_DSCL_BYPASS) + if (dscl_mode != DCN401_DSCL_MODE_DSCL_BYPASS) dpp401_power_on_dscl(dpp_base, true); } @@ -1139,7 +1129,7 @@ void dpp401_dscl_set_scaler_manual_scale(struct dpp *dpp_base, /* SCL mode */ REG_UPDATE(SCL_MODE, DSCL_MODE, dscl_mode); - if (dscl_mode == DSCL_MODE_DSCL_BYPASS) { + if (dscl_mode == DCN401_DSCL_MODE_DSCL_BYPASS) { if (dpp_base->ctx->dc->debug.enable_mem_low_power.bits.dscl) dpp401_power_on_dscl(dpp_base, false); return; @@ -1149,7 +1139,7 @@ void dpp401_dscl_set_scaler_manual_scale(struct dpp *dpp_base, lb_config = dpp401_dscl_find_lb_memory_config(dpp, scl_data); dpp401_dscl_set_lb(dpp, &scl_data->lb_params, lb_config); - if (dscl_mode == DSCL_MODE_SCALING_444_BYPASS) { + if (dscl_mode == DCN401_DSCL_MODE_SCALING_444_BYPASS) { if (dpp->base.ctx->dc->config.prefer_easf) dpp401_dscl_disable_easf(dpp_base, scl_data); dpp401_dscl_program_isharp(dpp_base, scl_data, program_isharp_1dlut, &bs_coeffs_updated); 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 1f53a9f0c0ac..e4144b244332 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -1157,6 +1157,11 @@ static bool setup_dsc_config( if (!is_dsc_possible) goto done; + /* increase miniumum slice count to meet sink slice width limitations */ + min_slices_h = dc_fixpt_ceil(dc_fixpt_max( + dc_fixpt_div_int(dc_fixpt_from_int(pic_width), dsc_common_caps.max_slice_width), // sink min + dc_fixpt_from_int(min_slices_h))); // source min + min_slices_h = fit_num_slices_up(dsc_common_caps.slice_caps, min_slices_h); /* increase minimum slice count to meet sink throughput limitations */ diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c index d347bb06577a..e7e5f6d4778e 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c @@ -440,6 +440,35 @@ void hubbub3_init_watermarks(struct hubbub *hubbub) REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, reg); } +void hubbub3_get_det_sizes(struct hubbub *hubbub, uint32_t *curr_det_sizes, uint32_t *target_det_sizes) +{ + struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub); + + REG_GET_2(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, &curr_det_sizes[0], + DET0_SIZE, &target_det_sizes[0]); + + REG_GET_2(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, &curr_det_sizes[1], + DET1_SIZE, &target_det_sizes[1]); + + REG_GET_2(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, &curr_det_sizes[2], + DET2_SIZE, &target_det_sizes[2]); + + REG_GET_2(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, &curr_det_sizes[3], + DET3_SIZE, &target_det_sizes[3]); + +} + +uint32_t hubbub3_compbuf_config_error(struct hubbub *hubbub) +{ + struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub); + uint32_t compbuf_config_error = 0; + + REG_GET(DCHUBBUB_COMPBUF_CTRL, CONFIG_ERROR, + &compbuf_config_error); + + return compbuf_config_error; +} + static const struct hubbub_funcs hubbub30_funcs = { .update_dchub = hubbub2_update_dchub, .init_dchub_sys_ctx = hubbub3_init_dchub_sys_ctx, @@ -457,6 +486,8 @@ static const struct hubbub_funcs hubbub30_funcs = { .force_pstate_change_control = hubbub3_force_pstate_change_control, .init_watermarks = hubbub3_init_watermarks, .hubbub_read_state = hubbub2_read_state, + .get_det_sizes = hubbub3_get_det_sizes, + .compbuf_config_error = hubbub3_compbuf_config_error, }; void hubbub3_construct(struct dcn20_hubbub *hubbub3, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h index ca6233e8f1f4..49a469969d36 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h @@ -133,4 +133,10 @@ void hubbub3_force_pstate_change_control(struct hubbub *hubbub, void hubbub3_init_watermarks(struct hubbub *hubbub); +void hubbub3_get_det_sizes(struct hubbub *hubbub, + uint32_t *curr_det_sizes, + uint32_t *target_det_sizes); + +uint32_t hubbub3_compbuf_config_error(struct hubbub *hubbub); + #endif diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c index b98505b240a7..cdb20251a154 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c @@ -1071,6 +1071,8 @@ static const struct hubbub_funcs hubbub31_funcs = { .program_compbuf_size = dcn31_program_compbuf_size, .init_crb = dcn31_init_crb, .hubbub_read_state = hubbub2_read_state, + .get_det_sizes = hubbub3_get_det_sizes, + .compbuf_config_error = hubbub3_compbuf_config_error, }; void hubbub31_construct(struct dcn20_hubbub *hubbub31, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c index 32a6be543105..92957398ac0a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c @@ -1009,6 +1009,8 @@ static const struct hubbub_funcs hubbub32_funcs = { .force_usr_retraining_allow = hubbub32_force_usr_retraining_allow, .set_request_limit = hubbub32_set_request_limit, .get_mall_en = hubbub32_get_mall_en, + .get_det_sizes = hubbub3_get_det_sizes, + .compbuf_config_error = hubbub3_compbuf_config_error, }; void hubbub32_construct(struct dcn20_hubbub *hubbub2, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c index 6d41953011f5..a443722a8632 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c @@ -589,6 +589,8 @@ static const struct hubbub_funcs hubbub35_funcs = { .hubbub_read_state = hubbub2_read_state, .force_usr_retraining_allow = hubbub32_force_usr_retraining_allow, .dchubbub_init = hubbub35_init, + .get_det_sizes = hubbub3_get_det_sizes, + .compbuf_config_error = hubbub3_compbuf_config_error, }; void hubbub35_construct(struct dcn20_hubbub *hubbub2, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c index 92fab471b183..a36273a52880 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c @@ -1247,6 +1247,8 @@ static const struct hubbub_funcs hubbub4_01_funcs = { .program_compbuf_segments = dcn401_program_compbuf_segments, .wait_for_det_update = dcn401_wait_for_det_update, .program_arbiter = dcn401_program_arbiter, + .get_det_sizes = hubbub3_get_det_sizes, + .compbuf_config_error = hubbub3_compbuf_config_error, }; void hubbub401_construct(struct dcn20_hubbub *hubbub2, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h index f8f991785d4f..cf2eb9793008 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h @@ -104,7 +104,8 @@ SRI(DCN_SURF1_TTU_CNTL1, HUBPREQ, id),\ SRI(DCN_CUR0_TTU_CNTL0, HUBPREQ, id),\ SRI(DCN_CUR0_TTU_CNTL1, HUBPREQ, id),\ - SRI(HUBP_CLK_CNTL, HUBP, id) + SRI(HUBP_CLK_CNTL, HUBP, id),\ + SRI(HUBPRET_READ_LINE_VALUE, HUBPRET, id) /* Register address initialization macro for ASICs with VM */ #define HUBP_REG_LIST_DCN_VM(id)\ @@ -249,7 +250,8 @@ uint32_t CURSOR_POSITION; \ uint32_t CURSOR_HOT_SPOT; \ uint32_t CURSOR_DST_OFFSET; \ - uint32_t HUBP_CLK_CNTL + uint32_t HUBP_CLK_CNTL; \ + uint32_t HUBPRET_READ_LINE_VALUE #define HUBP_SF(reg_name, field_name, post_fix)\ .field_name = reg_name ## __ ## field_name ## post_fix @@ -622,6 +624,8 @@ type DCN_VM_SYSTEM_APERTURE_DEFAULT_SYSTEM;\ type DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB;\ type DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB;\ + type PIPE_READ_LINE;\ + type HUBP_SEG_ALLOC_ERR_STATUS;\ /* todo: get these from GVM instead of reading registers ourselves */\ type PAGE_DIRECTORY_ENTRY_HI32;\ type PAGE_DIRECTORY_ENTRY_LO32;\ @@ -671,6 +675,7 @@ struct dcn_fl_regs_st { uint32_t lut_done; uint32_t lut_addr_mode; uint32_t lut_width; + uint32_t lut_mpc_width; uint32_t lut_tmz; uint32_t lut_crossbar_sel_r; uint32_t lut_crossbar_sel_g; diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h index 62369be070ea..f325db555102 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h @@ -264,6 +264,7 @@ type HUBP_3DLUT_DONE;\ type HUBP_3DLUT_ADDRESSING_MODE;\ type HUBP_3DLUT_WIDTH;\ + type HUBP_3DLUT_MPC_WIDTH;\ type HUBP_3DLUT_TMZ;\ type HUBP_3DLUT_CROSSBAR_SELECT_Y_G;\ type HUBP_3DLUT_CROSSBAR_SELECT_CB_B;\ diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c index 0da70b50e86d..556214b2227d 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c @@ -505,6 +505,30 @@ void hubp3_init(struct hubp *hubp) hubp_reset(hubp); } +uint32_t hubp3_get_current_read_line(struct hubp *hubp) +{ + uint32_t read_line = 0; + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_GET(HUBPRET_READ_LINE_VALUE, + PIPE_READ_LINE, + &read_line); + + return read_line; +} + +unsigned int hubp3_get_underflow_status(struct hubp *hubp) +{ + uint32_t hubp_underflow = 0; + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_GET(DCHUBP_CNTL, + HUBP_UNDERFLOW_STATUS, + &hubp_underflow); + + return hubp_underflow; +} + static struct hubp_funcs dcn30_hubp_funcs = { .hubp_enable_tripleBuffer = hubp2_enable_triplebuffer, .hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled, @@ -534,6 +558,8 @@ static struct hubp_funcs dcn30_hubp_funcs = { .hubp_soft_reset = hubp1_soft_reset, .hubp_set_flip_int = hubp1_set_flip_int, .hubp_clear_tiling = hubp3_clear_tiling, + .hubp_get_underflow_status = hubp3_get_underflow_status, + .hubp_get_current_read_line = hubp3_get_current_read_line, }; bool hubp3_construct( diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h index b7d7adf0b58c..842f4eb72cc8 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h @@ -243,7 +243,8 @@ HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_6, REFCYC_PER_META_CHUNK_FLIP_C, mask_sh),\ HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_5, REFCYC_PER_VM_GROUP_VBLANK, mask_sh),\ HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_6, REFCYC_PER_VM_REQ_VBLANK, mask_sh),\ - HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, VM_GROUP_SIZE, mask_sh) + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, VM_GROUP_SIZE, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_READ_LINE_VALUE, PIPE_READ_LINE, mask_sh) bool hubp3_construct( struct dcn20_hubp *hubp2, @@ -299,6 +300,11 @@ void hubp3_init(struct hubp *hubp); void hubp3_clear_tiling(struct hubp *hubp); +uint32_t hubp3_get_current_read_line(struct hubp *hubp); + +uint32_t hubp3_get_underflow_status(struct hubp *hubp); + + #endif /* __DC_HUBP_DCN30_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c index 7fd582a8a4ba..47101847c2b7 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c @@ -68,6 +68,18 @@ void hubp31_program_extended_blank_value( hubp31_program_extended_blank(hubp, min_dst_y_next_start_optimized); } +uint32_t hubp31_get_det_config_error(struct hubp *hubp) +{ + uint32_t config_error = 0; + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_GET(DCHUBP_CNTL, + HUBP_SEG_ALLOC_ERR_STATUS, + &config_error); + + return config_error; +} + static struct hubp_funcs dcn31_hubp_funcs = { .hubp_enable_tripleBuffer = hubp2_enable_triplebuffer, .hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled, @@ -98,6 +110,9 @@ static struct hubp_funcs dcn31_hubp_funcs = { .hubp_in_blank = hubp1_in_blank, .program_extended_blank = hubp31_program_extended_blank, .hubp_clear_tiling = hubp3_clear_tiling, + .hubp_get_underflow_status = hubp3_get_underflow_status, + .hubp_get_current_read_line = hubp3_get_current_read_line, + .hubp_get_det_config_error = hubp31_get_det_config_error, }; bool hubp31_construct( diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.h index d688db79b750..5952c4671507 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.h @@ -228,7 +228,9 @@ HUBP_SF(HUBPREQ0_FLIP_PARAMETERS_6, REFCYC_PER_META_CHUNK_FLIP_C, mask_sh),\ HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_5, REFCYC_PER_VM_GROUP_VBLANK, mask_sh),\ HUBP_SF(HUBPREQ0_VBLANK_PARAMETERS_6, REFCYC_PER_VM_REQ_VBLANK, mask_sh),\ - HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, VM_GROUP_SIZE, mask_sh) + HUBP_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, VM_GROUP_SIZE, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_READ_LINE_VALUE, PIPE_READ_LINE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_SEG_ALLOC_ERR_STATUS, mask_sh) bool hubp31_construct( @@ -246,4 +248,6 @@ void hubp31_set_unbounded_requesting(struct hubp *hubp, bool enable); void hubp31_program_extended_blank_value( struct hubp *hubp, unsigned int min_dst_y_next_start_optimized); +uint32_t hubp31_get_det_config_error(struct hubp *hubp); + #endif /* __DC_HUBP_DCN31_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c index f3a21c623f44..a5f23bb2a76a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c @@ -206,6 +206,9 @@ static struct hubp_funcs dcn32_hubp_funcs = { .hubp_update_mall_sel = hubp32_update_mall_sel, .hubp_prepare_subvp_buffering = hubp32_prepare_subvp_buffering, .hubp_clear_tiling = hubp3_clear_tiling, + .hubp_get_underflow_status = hubp3_get_underflow_status, + .hubp_get_current_read_line = hubp3_get_current_read_line, + .hubp_get_det_config_error = hubp31_get_det_config_error, }; bool hubp32_construct( diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c index 6d060ba12da8..b140808f21af 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c @@ -218,6 +218,9 @@ static struct hubp_funcs dcn35_hubp_funcs = { .hubp_in_blank = hubp1_in_blank, .program_extended_blank = hubp31_program_extended_blank_value, .hubp_clear_tiling = hubp3_clear_tiling, + .hubp_get_underflow_status = hubp3_get_underflow_status, + .hubp_get_current_read_line = hubp3_get_current_read_line, + .hubp_get_det_config_error = hubp31_get_det_config_error, }; bool hubp35_construct( diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index 705b98b1b6cc..0fcbc6a35be6 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -127,6 +127,43 @@ void hubp401_program_3dlut_fl_format(struct hubp *hubp, enum hubp_3dlut_fl_forma REG_UPDATE(_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_FORMAT, format); } +void hubp401_program_3dlut_fl_config( + struct hubp *hubp, + struct hubp_fl_3dlut_config *cfg) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + uint32_t mpc_width = {(cfg->width == 17) ? 0 : 1}; + uint32_t width = {cfg->width}; + + if (cfg->layout == DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR) + width = (cfg->width == 17) ? 4916 : 35940; + + REG_UPDATE_2(_3DLUT_FL_CONFIG, + HUBP0_3DLUT_FL_MODE, cfg->mode, + HUBP0_3DLUT_FL_FORMAT, cfg->format); + + REG_UPDATE_2(_3DLUT_FL_BIAS_SCALE, + HUBP0_3DLUT_FL_BIAS, cfg->bias, + HUBP0_3DLUT_FL_SCALE, cfg->scale); + + REG_UPDATE(HUBP_3DLUT_ADDRESS_HIGH, + HUBP_3DLUT_ADDRESS_HIGH, cfg->address.lut3d.addr.high_part); + REG_UPDATE(HUBP_3DLUT_ADDRESS_LOW, + HUBP_3DLUT_ADDRESS_LOW, cfg->address.lut3d.addr.low_part); + + //cross bar + REG_UPDATE_8(HUBP_3DLUT_CONTROL, + HUBP_3DLUT_MPC_WIDTH, mpc_width, + HUBP_3DLUT_WIDTH, width, + HUBP_3DLUT_CROSSBAR_SELECT_CR_R, cfg->crossbar_bit_slice_cr_r, + HUBP_3DLUT_CROSSBAR_SELECT_Y_G, cfg->crossbar_bit_slice_y_g, + HUBP_3DLUT_CROSSBAR_SELECT_CB_B, cfg->crossbar_bit_slice_cb_b, + HUBP_3DLUT_ADDRESSING_MODE, cfg->addr_mode, + HUBP_3DLUT_TMZ, cfg->protection_bits, + HUBP_3DLUT_ENABLE, cfg->enabled ? 1 : 0); +} + void hubp401_update_mall_sel(struct hubp *hubp, uint32_t mall_sel, bool c_cursor) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); @@ -1033,6 +1070,10 @@ static struct hubp_funcs dcn401_hubp_funcs = { .hubp_program_3dlut_fl_crossbar = hubp401_program_3dlut_fl_crossbar, .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, .hubp_clear_tiling = hubp401_clear_tiling, + .hubp_program_3dlut_fl_config = hubp401_program_3dlut_fl_config, + .hubp_get_underflow_status = hubp3_get_underflow_status, + .hubp_get_current_read_line = hubp3_get_current_read_line, + .hubp_get_det_config_error = hubp31_get_det_config_error, }; bool hubp401_construct( diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h index 608e6153fa68..fdabbeec8ffa 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h @@ -252,7 +252,9 @@ HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_1H_P0, mask_sh),\ HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_2H_P0, mask_sh),\ HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_1H_P1, mask_sh),\ - HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_2H_P1, mask_sh) + HUBP_SF(HUBP0_DCHUBP_MCACHEID_CONFIG, MCACHEID_MALL_PREF_2H_P1, mask_sh),\ + HUBP_SF(HUBPRET0_HUBPRET_READ_LINE_VALUE, PIPE_READ_LINE, mask_sh),\ + HUBP_SF(HUBP0_DCHUBP_CNTL, HUBP_SEG_ALLOC_ERR_STATUS, mask_sh) void hubp401_update_mall_sel(struct hubp *hubp, uint32_t mall_sel, bool c_cursor); @@ -349,6 +351,10 @@ void hubp401_program_3dlut_fl_format(struct hubp *hubp, enum hubp_3dlut_fl_forma void hubp401_program_3dlut_fl_mode(struct hubp *hubp, enum hubp_3dlut_fl_mode mode); +void hubp401_program_3dlut_fl_config( + struct hubp *hubp, + struct hubp_fl_3dlut_config *cfg); + void hubp401_clear_tiling(struct hubp *hubp); void hubp401_vready_at_or_After_vsync(struct hubp *hubp, 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 4ea13d0bf815..153d68375fa3 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 @@ -671,6 +671,7 @@ 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); @@ -1269,7 +1270,7 @@ void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); } -static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) +enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) { switch (crtc_id) { case CONTROLLER_ID_D0: @@ -1289,7 +1290,7 @@ static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) } } -static void populate_audio_dp_link_info( +void populate_audio_dp_link_info( const struct pipe_ctx *pipe_ctx, struct audio_dp_link_info *dp_link_info) { @@ -1600,19 +1601,17 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( } if (pipe_ctx->stream_res.audio != NULL) { - struct audio_output audio_output = {0}; + build_audio_output(context, pipe_ctx, &pipe_ctx->stream_res.audio_output); - build_audio_output(context, pipe_ctx, &audio_output); - - link_hwss->setup_audio_output(pipe_ctx, &audio_output, + link_hwss->setup_audio_output(pipe_ctx, &pipe_ctx->stream_res.audio_output, pipe_ctx->stream_res.audio->inst); pipe_ctx->stream_res.audio->funcs->az_configure( pipe_ctx->stream_res.audio, pipe_ctx->stream->signal, - &audio_output.crtc_info, + &pipe_ctx->stream_res.audio_output.crtc_info, &pipe_ctx->stream->audio_info, - &audio_output.dp_link_info); + &pipe_ctx->stream_res.audio_output.dp_link_info); if (dc->config.disable_hbr_audio_dp2) if (pipe_ctx->stream_res.audio->funcs->az_disable_hbr_audio && @@ -2254,7 +2253,7 @@ static bool should_enable_fbc(struct dc *dc, /* * Enable FBC */ -static void enable_fbc( +void enable_fbc( struct dc *dc, struct dc_state *context) { @@ -2386,9 +2385,7 @@ static void dce110_setup_audio_dto( if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) continue; if (pipe_ctx->stream_res.audio != NULL) { - struct audio_output audio_output; - - build_audio_output(context, pipe_ctx, &audio_output); + build_audio_output(context, pipe_ctx, &pipe_ctx->stream_res.audio_output); if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->set_audio_dtbclk_dto) { struct dtbclk_dto_params dto_params = {0}; @@ -2399,14 +2396,14 @@ static void dce110_setup_audio_dto( pipe_ctx->stream_res.audio->funcs->wall_dto_setup( pipe_ctx->stream_res.audio, pipe_ctx->stream->signal, - &audio_output.crtc_info, - &audio_output.pll_info); + &pipe_ctx->stream_res.audio_output.crtc_info, + &pipe_ctx->stream_res.audio_output.pll_info); } else pipe_ctx->stream_res.audio->funcs->wall_dto_setup( pipe_ctx->stream_res.audio, pipe_ctx->stream->signal, - &audio_output.crtc_info, - &audio_output.pll_info); + &pipe_ctx->stream_res.audio_output.crtc_info, + &pipe_ctx->stream_res.audio_output.pll_info); break; } } @@ -2426,15 +2423,15 @@ static void dce110_setup_audio_dto( continue; if (pipe_ctx->stream_res.audio != NULL) { - struct audio_output audio_output = {0}; - - build_audio_output(context, pipe_ctx, &audio_output); + build_audio_output(context, + pipe_ctx, + &pipe_ctx->stream_res.audio_output); pipe_ctx->stream_res.audio->funcs->wall_dto_setup( pipe_ctx->stream_res.audio, pipe_ctx->stream->signal, - &audio_output.crtc_info, - &audio_output.pll_info); + &pipe_ctx->stream_res.audio_output.crtc_info, + &pipe_ctx->stream_res.audio_output.pll_info); break; } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h index 7cd8c1576988..9c032e449481 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h @@ -114,5 +114,12 @@ void build_audio_output( struct dc_state *state, const struct pipe_ctx *pipe_ctx, struct audio_output *audio_output); +enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id); +void populate_audio_dp_link_info( + const struct pipe_ctx *pipe_ctx, + struct audio_dp_link_info *dp_link_info); +void enable_fbc( + struct dc *dc, + struct dc_state *context); #endif /* __DC_HWSS_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 39910f73ecd0..506c3bbbf221 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -328,19 +328,25 @@ static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) } DTN_INFO("\n=======HUBP FL======\n"); - DTN_INFO( - "HUBP FL: Enabled Done adr_mode width tmz xbar_sel_R xbar_sel_G xbar_sel_B adr_hi adr_low REFCYC Bias Scale Mode Format\n"); + static const char * const pLabels[] = { + "inst", "Enabled ", "Done ", "adr_mode ", "width ", "mpc_width ", + "tmz", "xbar_sel_R", "xbar_sel_G", "xbar_sel_B", "adr_hi ", + "adr_low", "REFCYC", "Bias", "Scale", "Mode", + "Format", "prefetch"}; + for (i = 0; i < pool->pipe_count; i++) { struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); struct dcn_fl_regs_st *fl_regs = &s->fl_regs; + struct _vcs_dpi_display_dlg_regs_st *dlg_regs = &s->dlg_attr; if (!s->blank_en) { - DTN_INFO("[%2d]: %5xh %6xh %5d %6d %8xh %2xh %6xh %6d %8d %8d %7d %8xh %5x %5x %5x", + uint32_t values[] = { pool->hubps[i]->inst, fl_regs->lut_enable, fl_regs->lut_done, fl_regs->lut_addr_mode, fl_regs->lut_width, + fl_regs->lut_mpc_width, fl_regs->lut_tmz, fl_regs->lut_crossbar_sel_r, fl_regs->lut_crossbar_sel_g, @@ -351,8 +357,13 @@ static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) fl_regs->lut_fl_bias, fl_regs->lut_fl_scale, fl_regs->lut_fl_mode, - fl_regs->lut_fl_format); - DTN_INFO("\n"); + fl_regs->lut_fl_format, + dlg_regs->dst_y_prefetch}; + + int num_elements = 18; + + for (int j = 0; j < num_elements; j++) + DTN_INFO("%s \t %8xh\n", pLabels[j], values[j]); } } @@ -541,19 +552,43 @@ static void dcn10_log_color_state(struct dc *dc, dc->caps.color.mpc.ogam_ram, dc->caps.color.mpc.ocsc); DTN_INFO("===== MPC RMCM 3DLUT =====\n"); - DTN_INFO("MPCC: SIZE MODE MODE_CUR RD_SEL 30BIT_EN WR_EN_MASK RAM_SEL OUT_NORM_FACTOR FL_SEL OUT_OFFSET OUT_SCALE FL_DONE SOFT_UNDERFLOW HARD_UNDERFLOW MEM_PWR_ST FORCE DIS MODE\n"); + static const char * const pLabels[] = { + "MPCC", "SIZE", "MODE", "MODE_CUR", "RD_SEL", + "30BIT_EN", "WR_EN_MASK", "RAM_SEL", "OUT_NORM_FACTOR", "FL_SEL", + "OUT_OFFSET", "OUT_SCALE", "FL_DONE", "SOFT_UNDERFLOW", "HARD_UNDERFLOW", + "MEM_PWR_ST", "FORCE", "DIS", "MODE"}; + for (i = 0; i < pool->mpcc_count; i++) { struct mpcc_state s = {0}; pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s); - if (s.opp_id != 0xf) - DTN_INFO("[%2d]: %4xh %4xh %6xh %4x %4x %4x %4x %4x %4xh %4xh %6xh %4x %4x %4x %4x %4x %4x %4x\n", - i, s.rmcm_regs.rmcm_3dlut_size, s.rmcm_regs.rmcm_3dlut_mode, s.rmcm_regs.rmcm_3dlut_mode_cur, - s.rmcm_regs.rmcm_3dlut_read_sel, s.rmcm_regs.rmcm_3dlut_30bit_en, s.rmcm_regs.rmcm_3dlut_wr_en_mask, - s.rmcm_regs.rmcm_3dlut_ram_sel, s.rmcm_regs.rmcm_3dlut_out_norm_factor, s.rmcm_regs.rmcm_3dlut_fl_sel, - s.rmcm_regs.rmcm_3dlut_out_offset_r, s.rmcm_regs.rmcm_3dlut_out_scale_r, s.rmcm_regs.rmcm_3dlut_fl_done, - s.rmcm_regs.rmcm_3dlut_fl_soft_underflow, s.rmcm_regs.rmcm_3dlut_fl_hard_underflow, s.rmcm_regs.rmcm_3dlut_mem_pwr_state, - s.rmcm_regs.rmcm_3dlut_mem_pwr_force, s.rmcm_regs.rmcm_3dlut_mem_pwr_dis, s.rmcm_regs.rmcm_3dlut_mem_pwr_mode); + if (s.opp_id != 0xf) { + uint32_t values[] = { + i, + s.rmcm_regs.rmcm_3dlut_size, + s.rmcm_regs.rmcm_3dlut_mode, + s.rmcm_regs.rmcm_3dlut_mode_cur, + s.rmcm_regs.rmcm_3dlut_read_sel, + s.rmcm_regs.rmcm_3dlut_30bit_en, + s.rmcm_regs.rmcm_3dlut_wr_en_mask, + s.rmcm_regs.rmcm_3dlut_ram_sel, + s.rmcm_regs.rmcm_3dlut_out_norm_factor, + s.rmcm_regs.rmcm_3dlut_fl_sel, + s.rmcm_regs.rmcm_3dlut_out_offset_r, + s.rmcm_regs.rmcm_3dlut_out_scale_r, + s.rmcm_regs.rmcm_3dlut_fl_done, + s.rmcm_regs.rmcm_3dlut_fl_soft_underflow, + s.rmcm_regs.rmcm_3dlut_fl_hard_underflow, + s.rmcm_regs.rmcm_3dlut_mem_pwr_state, + s.rmcm_regs.rmcm_3dlut_mem_pwr_force, + s.rmcm_regs.rmcm_3dlut_mem_pwr_dis, + s.rmcm_regs.rmcm_3dlut_mem_pwr_mode}; + + int num_elements = 19; + + for (int j = 0; j < num_elements; j++) + DTN_INFO("%s \t %8xh\n", pLabels[j], values[j]); + } } DTN_INFO("\n"); DTN_INFO("===== MPC RMCM Shaper =====\n"); @@ -3628,6 +3663,8 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) int y_plane = pipe_ctx->plane_state->dst_rect.y; int x_pos = pos_cpy.x; int y_pos = pos_cpy.y; + int clip_x = pipe_ctx->plane_state->clip_rect.x; + int clip_width = pipe_ctx->plane_state->clip_rect.width; if ((pipe_ctx->top_pipe != NULL) || (pipe_ctx->bottom_pipe != NULL)) { if ((pipe_ctx->plane_state->src_rect.width != pipe_ctx->plane_res.scl_data.viewport.width) || @@ -3646,7 +3683,7 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) */ /** - * Translate cursor from stream space to plane space. + * Translate cursor and clip offset from stream space to plane space. * * If the cursor is scaled then we need to scale the position * to be in the approximately correct place. We can't do anything @@ -3663,6 +3700,10 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) pipe_ctx->plane_state->dst_rect.width; y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.height / pipe_ctx->plane_state->dst_rect.height; + clip_x = (clip_x - x_plane) * pipe_ctx->plane_state->src_rect.width / + pipe_ctx->plane_state->dst_rect.width; + clip_width = clip_width * pipe_ctx->plane_state->src_rect.width / + pipe_ctx->plane_state->dst_rect.width; } /** @@ -3709,30 +3750,18 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) if (param.rotation == ROTATION_ANGLE_0) { - int viewport_width = - pipe_ctx->plane_res.scl_data.viewport.width; - int viewport_x = - pipe_ctx->plane_res.scl_data.viewport.x; if (param.mirror) { - if (pipe_split_on || odm_combine_on) { - if (pos_cpy.x >= viewport_width + viewport_x) { - pos_cpy.x = 2 * viewport_width - - pos_cpy.x + 2 * viewport_x; - } else { - uint32_t temp_x = pos_cpy.x; - - pos_cpy.x = 2 * viewport_x - pos_cpy.x; - if (temp_x >= viewport_x + - (int)hubp->curs_attr.width || pos_cpy.x - <= (int)hubp->curs_attr.width + - pipe_ctx->plane_state->src_rect.x) { - pos_cpy.x = 2 * viewport_width - temp_x; - } - } - } else { - pos_cpy.x = viewport_width - pos_cpy.x + 2 * viewport_x; - } + /* + * The plane is split into multiple viewports. + * The combination of all viewports span the + * entirety of the clip rect. + * + * For no pipe_split, viewport_width is represents + * the full width of the clip_rect, so we can just + * mirror it. + */ + pos_cpy.x = clip_width - pos_cpy.x + 2 * clip_x; } } // Swap axis and mirror horizontally @@ -3802,30 +3831,17 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) } // Mirror horizontally and vertically else if (param.rotation == ROTATION_ANGLE_180) { - int viewport_width = - pipe_ctx->plane_res.scl_data.viewport.width; - int viewport_x = - pipe_ctx->plane_res.scl_data.viewport.x; - if (!param.mirror) { - if (pipe_split_on || odm_combine_on) { - if (pos_cpy.x >= viewport_width + viewport_x) { - pos_cpy.x = 2 * viewport_width - - pos_cpy.x + 2 * viewport_x; - } else { - uint32_t temp_x = pos_cpy.x; - - pos_cpy.x = 2 * viewport_x - pos_cpy.x; - if (temp_x >= viewport_x + - (int)hubp->curs_attr.width || pos_cpy.x - <= (int)hubp->curs_attr.width + - pipe_ctx->plane_state->src_rect.x) { - pos_cpy.x = temp_x + viewport_width; - } - } - } else { - pos_cpy.x = viewport_width - pos_cpy.x + 2 * viewport_x; - } + /* + * The plane is split into multiple viewports. + * The combination of all viewports span the + * entirety of the clip rect. + * + * For no pipe_split, viewport_width is represents + * the full width of the clip_rect, so we can just + * mirror it. + */ + pos_cpy.x = clip_width - pos_cpy.x + 2 * clip_x; } /** diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 3207addbd4eb..cc377fcda6ff 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -955,7 +955,7 @@ enum dc_status dcn20_enable_stream_timing( return DC_ERROR_UNEXPECTED; } - fsleep(stream->timing.v_total * (stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz)); + udelay(stream->timing.v_total * (stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz)); params.vertical_total_min = stream->adjust.v_total_min; params.vertical_total_max = stream->adjust.v_total_max; @@ -3054,6 +3054,8 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) link_enc->transmitter - TRANSMITTER_UNIPHY_A); } + link_hwss->setup_stream_attribute(pipe_ctx); + if (dc->res_pool->dccg->funcs->set_pixel_rate_div) dc->res_pool->dccg->funcs->set_pixel_rate_div( dc->res_pool->dccg, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index 37a239219dfe..139a63101488 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -1228,3 +1228,51 @@ void dcn30_wait_for_all_pending_updates(const struct pipe_ctx *pipe_ctx) } } } + +void dcn30_get_underflow_debug_data(const struct dc *dc, + struct timing_generator *tg, + struct dc_underflow_debug_data *out_data) +{ + struct hubbub *hubbub = dc->res_pool->hubbub; + + if (tg) { + uint32_t v_blank_start = 0, v_blank_end = 0; + + out_data->otg_inst = tg->inst; + + tg->funcs->get_scanoutpos(tg, + &v_blank_start, + &v_blank_end, + &out_data->h_position, + &out_data->v_position); + + out_data->otg_frame_count = tg->funcs->get_frame_count(tg); + + out_data->otg_underflow = tg->funcs->is_optc_underflow_occurred(tg); + } + + for (int i = 0; i < MAX_PIPES; i++) { + struct hubp *hubp = dc->res_pool->hubps[i]; + + if (hubp) { + if (hubp->funcs->hubp_get_underflow_status) + out_data->hubps[i].hubp_underflow = hubp->funcs->hubp_get_underflow_status(hubp); + + if (hubp->funcs->hubp_in_blank) + out_data->hubps[i].hubp_in_blank = hubp->funcs->hubp_in_blank(hubp); + + if (hubp->funcs->hubp_get_current_read_line) + out_data->hubps[i].hubp_readline = hubp->funcs->hubp_get_current_read_line(hubp); + + if (hubp->funcs->hubp_get_det_config_error) + out_data->hubps[i].det_config_error = hubp->funcs->hubp_get_det_config_error(hubp); + } + } + + if (hubbub->funcs->get_det_sizes) + hubbub->funcs->get_det_sizes(hubbub, out_data->curr_det_sizes, out_data->target_det_sizes); + + if (hubbub->funcs->compbuf_config_error) + out_data->compbuf_config_error = hubbub->funcs->compbuf_config_error(hubbub); + +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h index 4b90b781c4f2..40afbbfb5b9c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h @@ -29,6 +29,7 @@ #include "hw_sequencer_private.h" struct dc; +struct dc_underflow_debug_data; void dcn30_init_hw(struct dc *dc); void dcn30_program_all_writeback_pipes_in_tree( @@ -98,4 +99,8 @@ void dcn30_prepare_bandwidth(struct dc *dc, void dcn30_wait_for_all_pending_updates(const struct pipe_ctx *pipe_ctx); +void dcn30_get_underflow_debug_data(const struct dc *dc, + struct timing_generator *tg, + struct dc_underflow_debug_data *out_data); + #endif /* __DC_HWSS_DCN30_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c index 2ac5d54d1626..d7ff55669bac 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c @@ -110,6 +110,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = { .update_visual_confirm_color = dcn10_update_visual_confirm_color, .is_abm_supported = dcn21_is_abm_supported, .wait_for_all_pending_updates = dcn30_wait_for_all_pending_updates, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn30_private_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c index 556f4fe57eda..5a6a459da224 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c @@ -112,6 +112,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = { .exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state, .update_visual_confirm_color = dcn10_update_visual_confirm_color, .setup_hpo_hw_control = dcn31_setup_hpo_hw_control, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn31_private_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c index e68f21fd5f0f..560984533950 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c @@ -528,3 +528,75 @@ void dcn314_disable_link_output(struct dc_link *link, apply_symclk_on_tx_off_wa(link); } + +/** + * dcn314_dpp_pg_control - DPP power gate control. + * + * @hws: dce_hwseq reference. + * @dpp_inst: DPP instance reference. + * @power_on: true if we want to enable power gate, false otherwise. + * + * Enable or disable power gate in the specific DPP instance. + * If power gating is disabled, will force disable cursor in the DPP instance. + */ +void dcn314_dpp_pg_control( + struct dce_hwseq *hws, + unsigned int dpp_inst, + bool power_on) +{ + uint32_t power_gate = power_on ? 0 : 1; + uint32_t pwr_status = power_on ? 0 : 2; + + + if (hws->ctx->dc->debug.disable_dpp_power_gate) { + /* Workaround for DCN314 with disabled power gating */ + if (!power_on) { + + /* Force disable cursor if power gating is disabled */ + struct dpp *dpp = hws->ctx->dc->res_pool->dpps[dpp_inst]; + if (dpp && dpp->funcs->dpp_force_disable_cursor) + dpp->funcs->dpp_force_disable_cursor(dpp); + } + return; + } + if (REG(DOMAIN1_PG_CONFIG) == 0) + return; + + switch (dpp_inst) { + case 0: /* DPP0 */ + REG_UPDATE(DOMAIN1_PG_CONFIG, + DOMAIN1_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN1_PG_STATUS, + DOMAIN1_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 1: /* DPP1 */ + REG_UPDATE(DOMAIN3_PG_CONFIG, + DOMAIN3_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN3_PG_STATUS, + DOMAIN3_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 2: /* DPP2 */ + REG_UPDATE(DOMAIN5_PG_CONFIG, + DOMAIN5_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN5_PG_STATUS, + DOMAIN5_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 3: /* DPP3 */ + REG_UPDATE(DOMAIN7_PG_CONFIG, + DOMAIN7_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN7_PG_STATUS, + DOMAIN7_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h index 2305ad282f21..6c072d0274ea 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h @@ -47,4 +47,6 @@ void dcn314_dpp_root_clock_control(struct dce_hwseq *hws, unsigned int dpp_inst, void dcn314_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal); +void dcn314_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on); + #endif /* __DC_HWSS_DCN314_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c index f5112742edf9..79faab1125d4 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c @@ -115,6 +115,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = { .update_visual_confirm_color = dcn10_update_visual_confirm_color, .calculate_pix_rate_divider = dcn314_calculate_pix_rate_divider, .setup_hpo_hw_control = dcn31_setup_hpo_hw_control, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn314_private_funcs = { @@ -141,6 +142,7 @@ static const struct hwseq_private_funcs dcn314_private_funcs = { .enable_power_gating_plane = dcn314_enable_power_gating_plane, .dpp_root_clock_control = dcn314_dpp_root_clock_control, .hubp_pg_control = dcn31_hubp_pg_control, + .dpp_pg_control = dcn314_dpp_pg_control, .program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree, .update_odm = dcn314_update_odm, .dsc_pg_control = dcn314_dsc_pg_control, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c index b971356d30b1..c19ef075c882 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c @@ -121,6 +121,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .calculate_pix_rate_divider = dcn32_calculate_pix_rate_divider, .program_outstanding_updates = dcn32_program_outstanding_updates, .wait_for_all_pending_updates = dcn30_wait_for_all_pending_updates, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn32_private_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index a267f574b619..764eff6a4ec6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -113,6 +113,14 @@ static void enable_memory_low_power(struct dc *dc) } #endif +static void print_pg_status(struct dc *dc, const char *debug_func, const char *debug_log) +{ + if (dc->debug.enable_pg_cntl_debug_logs && dc->res_pool->pg_cntl) { + if (dc->res_pool->pg_cntl->funcs->print_pg_status) + dc->res_pool->pg_cntl->funcs->print_pg_status(dc->res_pool->pg_cntl, debug_func, debug_log); + } +} + void dcn35_set_dmu_fgcg(struct dce_hwseq *hws, bool enable) { REG_UPDATE_3(DMU_CLK_CNTL, @@ -137,6 +145,8 @@ void dcn35_init_hw(struct dc *dc) uint32_t user_level = MAX_BACKLIGHT_LEVEL; int i; + print_pg_status(dc, __func__, ": start"); + if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks) dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); @@ -200,10 +210,7 @@ void dcn35_init_hw(struct dc *dc) /* we want to turn off all dp displays before doing detection */ dc->link_srv->blank_all_dp_displays(dc); -/* - if (hws->funcs.enable_power_gating_plane) - hws->funcs.enable_power_gating_plane(dc->hwseq, true); -*/ + if (res_pool->hubbub && res_pool->hubbub->funcs->dchubbub_init) res_pool->hubbub->funcs->dchubbub_init(dc->res_pool->hubbub); /* If taking control over from VBIOS, we may want to optimize our first @@ -236,6 +243,8 @@ void dcn35_init_hw(struct dc *dc) } hws->funcs.init_pipes(dc, dc->current_state); + print_pg_status(dc, __func__, ": after init_pipes"); + if (dc->res_pool->hubbub->funcs->allow_self_refresh_control && !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter) dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, @@ -312,6 +321,7 @@ void dcn35_init_hw(struct dc *dc) if (dc->res_pool->pg_cntl->funcs->init_pg_status) dc->res_pool->pg_cntl->funcs->init_pg_status(dc->res_pool->pg_cntl); } + print_pg_status(dc, __func__, ": after init_pg_status"); } static void update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) @@ -500,97 +510,6 @@ void dcn35_physymclk_root_clock_control(struct dce_hwseq *hws, unsigned int phy_ } } -void dcn35_dsc_pg_control( - struct dce_hwseq *hws, - unsigned int dsc_inst, - bool power_on) -{ - uint32_t power_gate = power_on ? 0 : 1; - uint32_t pwr_status = power_on ? 0 : 2; - uint32_t org_ip_request_cntl = 0; - - if (hws->ctx->dc->debug.disable_dsc_power_gate) - return; - if (hws->ctx->dc->debug.ignore_pg) - return; - REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); - if (org_ip_request_cntl == 0) - REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - - switch (dsc_inst) { - case 0: /* DSC0 */ - REG_UPDATE(DOMAIN16_PG_CONFIG, - DOMAIN_POWER_GATE, power_gate); - - REG_WAIT(DOMAIN16_PG_STATUS, - DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); - break; - case 1: /* DSC1 */ - REG_UPDATE(DOMAIN17_PG_CONFIG, - DOMAIN_POWER_GATE, power_gate); - - REG_WAIT(DOMAIN17_PG_STATUS, - DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); - break; - case 2: /* DSC2 */ - REG_UPDATE(DOMAIN18_PG_CONFIG, - DOMAIN_POWER_GATE, power_gate); - - REG_WAIT(DOMAIN18_PG_STATUS, - DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); - break; - case 3: /* DSC3 */ - REG_UPDATE(DOMAIN19_PG_CONFIG, - DOMAIN_POWER_GATE, power_gate); - - REG_WAIT(DOMAIN19_PG_STATUS, - DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); - break; - default: - BREAK_TO_DEBUGGER(); - break; - } - - if (org_ip_request_cntl == 0) - REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); -} - -void dcn35_enable_power_gating_plane(struct dce_hwseq *hws, bool enable) -{ - bool force_on = true; /* disable power gating */ - uint32_t org_ip_request_cntl = 0; - - if (hws->ctx->dc->debug.disable_hubp_power_gate) - return; - if (hws->ctx->dc->debug.ignore_pg) - return; - REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); - if (org_ip_request_cntl == 0) - REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - /* DCHUBP0/1/2/3/4/5 */ - REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - /* DPP0/1/2/3/4/5 */ - REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - - force_on = true; /* disable power gating */ - if (enable && !hws->ctx->dc->debug.disable_dsc_power_gate) - force_on = false; - - /* DCS0/1/2/3/4 */ - REG_UPDATE(DOMAIN16_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - REG_UPDATE(DOMAIN17_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - REG_UPDATE(DOMAIN18_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - REG_UPDATE(DOMAIN19_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on); - - -} - /* In headless boot cases, DIG may be turned * on which causes HW/SW discrepancies. * To avoid this, power down hardware on boot @@ -1453,6 +1372,8 @@ void dcn35_prepare_bandwidth( } dcn20_prepare_bandwidth(dc, context); + + print_pg_status(dc, __func__, ": after rcg and power up"); } void dcn35_optimize_bandwidth( @@ -1461,6 +1382,8 @@ void dcn35_optimize_bandwidth( { struct pg_block_update pg_update_state; + print_pg_status(dc, __func__, ": before rcg and power up"); + dcn20_optimize_bandwidth(dc, context); if (dc->hwss.calc_blocks_to_gate) { @@ -1472,6 +1395,8 @@ void dcn35_optimize_bandwidth( if (dc->hwss.root_clock_control) dc->hwss.root_clock_control(dc, &pg_update_state, false); } + + print_pg_status(dc, __func__, ": after rcg and power up"); } void dcn35_set_drr(struct pipe_ctx **pipe_ctx, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c index a3ccf805bd16..f2f16a0bdb4f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c @@ -115,7 +115,6 @@ static const struct hw_sequencer_funcs dcn35_funcs = { .exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state, .update_visual_confirm_color = dcn10_update_visual_confirm_color, .apply_idle_power_optimizations = dcn35_apply_idle_power_optimizations, - .update_dsc_pg = dcn32_update_dsc_pg, .calc_blocks_to_gate = dcn35_calc_blocks_to_gate, .calc_blocks_to_ungate = dcn35_calc_blocks_to_ungate, .hw_block_power_up = dcn35_hw_block_power_up, @@ -128,6 +127,7 @@ static const struct hw_sequencer_funcs dcn35_funcs = { .enable_plane = dcn20_enable_plane, .update_dchubp_dpp = dcn20_update_dchubp_dpp, .post_unlock_reset_opp = dcn20_post_unlock_reset_opp, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn35_private_funcs = { @@ -150,7 +150,6 @@ static const struct hwseq_private_funcs dcn35_private_funcs = { .plane_atomic_disable = dcn35_plane_atomic_disable, //.plane_atomic_disable = dcn20_plane_atomic_disable,/*todo*/ //.hubp_pg_control = dcn35_hubp_pg_control, - .enable_power_gating_plane = dcn35_enable_power_gating_plane, .dpp_root_clock_control = dcn35_dpp_root_clock_control, .dpstream_root_clock_control = dcn35_dpstream_root_clock_control, .physymclk_root_clock_control = dcn35_physymclk_root_clock_control, @@ -165,7 +164,6 @@ static const struct hwseq_private_funcs dcn35_private_funcs = { .calculate_dccg_k1_k2_values = dcn32_calculate_dccg_k1_k2_values, .resync_fifo_dccg_dio = dcn314_resync_fifo_dccg_dio, .is_dp_dig_pixel_rate_div_policy = dcn35_is_dp_dig_pixel_rate_div_policy, - .dsc_pg_control = dcn35_dsc_pg_control, .dsc_pg_status = dcn32_dsc_pg_status, .enable_plane = dcn35_enable_plane, .wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c index 58f2be2a326b..09e60158f0b5 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c @@ -114,7 +114,6 @@ static const struct hw_sequencer_funcs dcn351_funcs = { .exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state, .update_visual_confirm_color = dcn10_update_visual_confirm_color, .apply_idle_power_optimizations = dcn35_apply_idle_power_optimizations, - .update_dsc_pg = dcn32_update_dsc_pg, .calc_blocks_to_gate = dcn351_calc_blocks_to_gate, .calc_blocks_to_ungate = dcn351_calc_blocks_to_ungate, .hw_block_power_up = dcn351_hw_block_power_up, @@ -123,6 +122,7 @@ static const struct hw_sequencer_funcs dcn351_funcs = { .set_long_vtotal = dcn35_set_long_vblank, .calculate_pix_rate_divider = dcn32_calculate_pix_rate_divider, .setup_hpo_hw_control = dcn35_setup_hpo_hw_control, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn351_private_funcs = { @@ -145,7 +145,6 @@ static const struct hwseq_private_funcs dcn351_private_funcs = { .plane_atomic_disable = dcn35_plane_atomic_disable, //.plane_atomic_disable = dcn20_plane_atomic_disable,/*todo*/ //.hubp_pg_control = dcn35_hubp_pg_control, - .enable_power_gating_plane = dcn35_enable_power_gating_plane, .dpp_root_clock_control = dcn35_dpp_root_clock_control, .dpstream_root_clock_control = dcn35_dpstream_root_clock_control, .physymclk_root_clock_control = dcn35_physymclk_root_clock_control, @@ -159,7 +158,6 @@ static const struct hwseq_private_funcs dcn351_private_funcs = { .setup_hpo_hw_control = dcn35_setup_hpo_hw_control, .calculate_dccg_k1_k2_values = dcn32_calculate_dccg_k1_k2_values, .is_dp_dig_pixel_rate_div_policy = dcn35_is_dp_dig_pixel_rate_div_policy, - .dsc_pg_control = dcn35_dsc_pg_control, .dsc_pg_status = dcn32_dsc_pg_status, .enable_plane = dcn35_enable_plane, .wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index cc9f40d97af2..d5b5e2ce6ff6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -965,6 +965,8 @@ void dcn401_enable_stream(struct pipe_ctx *pipe_ctx) } } + link_hwss->setup_stream_attribute(pipe_ctx); + if (dc->res_pool->dccg->funcs->set_pixel_rate_div) { dc->res_pool->dccg->funcs->set_pixel_rate_div( dc->res_pool->dccg, @@ -1619,20 +1621,28 @@ void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx, void dcn401_hardware_release(struct dc *dc) { - dc_dmub_srv_fams2_update_config(dc, dc->current_state, false); + if (!dc->debug.disable_force_pstate_allow_on_hw_release) { + dc_dmub_srv_fams2_update_config(dc, dc->current_state, false); - /* If pstate unsupported, or still supported - * by firmware, force it supported by dcn - */ - if (dc->current_state) { - if ((!dc->clk_mgr->clks.p_state_change_support || - dc->current_state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable) && - dc->res_pool->hubbub->funcs->force_pstate_change_control) - dc->res_pool->hubbub->funcs->force_pstate_change_control( - dc->res_pool->hubbub, true, true); + /* If pstate unsupported, or still supported + * by firmware, force it supported by dcn + */ + if (dc->current_state) { + if ((!dc->clk_mgr->clks.p_state_change_support || + dc->current_state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable) && + dc->res_pool->hubbub->funcs->force_pstate_change_control) + dc->res_pool->hubbub->funcs->force_pstate_change_control( + dc->res_pool->hubbub, true, true); - dc->current_state->bw_ctx.bw.dcn.clk.p_state_change_support = true; - dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true); + dc->current_state->bw_ctx.bw.dcn.clk.p_state_change_support = true; + dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true); + } + } else { + if (dc->current_state) { + dc->clk_mgr->clks.p_state_change_support = false; + dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, dc->current_state, true); + } + dc_dmub_srv_fams2_update_config(dc, dc->current_state, false); } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c index fe7aceb2f510..d6e11b7e4fce 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c @@ -104,6 +104,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = { .enable_plane = dcn20_enable_plane, .update_dchubp_dpp = dcn20_update_dchubp_dpp, .post_unlock_reset_opp = dcn20_post_unlock_reset_opp, + .get_underflow_debug_data = dcn30_get_underflow_debug_data, }; static const struct hwseq_private_funcs dcn401_private_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 9df8030e37f7..1723bbcf2c46 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -47,6 +47,7 @@ struct link_resource; struct dc_dmub_cmd; struct pg_block_update; struct drr_params; +struct dc_underflow_debug_data; struct subvp_pipe_control_lock_fast_params { struct dc *dc; @@ -475,6 +476,9 @@ struct hw_sequencer_funcs { struct dc_state *context); void (*post_unlock_reset_opp)(struct dc *dc, struct pipe_ctx *opp_head); + void (*get_underflow_debug_data)(const struct dc *dc, + struct timing_generator *tg, + struct dc_underflow_debug_data *out_data); }; void color_space_to_black_color( diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index f0d7185153b2..d30f94c35f11 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -228,8 +228,7 @@ struct resource_funcs { enum dc_status (*update_dc_state_for_encoder_switch)(struct dc_link *link, struct dc_link_settings *link_setting, uint8_t pipe_count, - struct pipe_ctx *pipes, - struct audio_output *audio_output); + struct pipe_ctx *pipes); }; struct audio_support{ @@ -361,6 +360,8 @@ struct stream_resource { uint8_t gsl_group; struct test_pattern_params test_pattern_params; + + struct audio_output audio_output; }; struct plane_resource { @@ -433,7 +434,7 @@ enum p_state_switch_method { P_STATE_V_ACTIVE, P_STATE_SUB_VP, P_STATE_DRR_SUB_VP, - P_STATE_V_BLANK_SUB_VP + P_STATE_V_BLANK_SUB_VP, }; struct pipe_ctx { @@ -683,6 +684,7 @@ struct replay_context { /* Controller Id used for Dig Fe source select */ enum controller_id controllerId; unsigned int line_time_in_ns; + bool os_request_force_ffu; }; enum dc_replay_enable { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h index 52b745667ef7..843a18287c83 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h @@ -137,6 +137,19 @@ struct dcn_hubbub_state { uint32_t dram_state_cntl; }; +struct hubbub_system_latencies { + uint32_t max_latency_ns; + uint32_t avg_latency_ns; + uint32_t min_latency_ns; +}; + +struct hubbub_urgent_latency_params { + uint32_t refclk_mhz; + uint32_t t_win_ns; + uint32_t bandwidth_mbps; + uint32_t bw_factor_x1000; +}; + struct hubbub_funcs { void (*update_dchub)( struct hubbub *hubbub, @@ -229,6 +242,17 @@ struct hubbub_funcs { void (*program_compbuf_segments)(struct hubbub *hubbub, unsigned compbuf_size_seg, bool safe_to_increase); void (*wait_for_det_update)(struct hubbub *hubbub, int hubp_inst); bool (*program_arbiter)(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower); + void (*get_det_sizes)(struct hubbub *hubbub, uint32_t *curr_det_sizes, uint32_t *target_det_sizes); + uint32_t (*compbuf_config_error)(struct hubbub *hubbub); + struct hubbub_perfmon_funcs{ + void (*start_system_latency_measurement)(struct hubbub *hubbub); + void (*get_system_latency_result)(struct hubbub *hubbub, uint32_t refclk_mhz, struct hubbub_system_latencies *latencies); + void (*start_in_order_bandwidth_measurement)(struct hubbub *hubbub); + void (*get_in_order_bandwidth_result)(struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *bandwidth_mbps); + void (*start_urgent_ramp_latency_measurement)(struct hubbub *hubbub, const struct hubbub_urgent_latency_params *params); + void (*get_urgent_ramp_latency_result)(struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *latency_ns); + void (*reset)(struct hubbub *hubbub); + } perfmon; }; struct hubbub { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h index 0c5675d1c593..1b7c085dc2cc 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h @@ -349,6 +349,9 @@ struct dpp_funcs { struct dpp *dpp_base, enum dc_color_space color_space, struct dc_csc_transform cursor_csc_color_matrix); + + void (*dpp_force_disable_cursor)(struct dpp *dpp_base); + }; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index cee29e89ec5c..2b874d2cc61c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -89,7 +89,7 @@ enum hubp_3dlut_fl_addressing_mode { enum hubp_3dlut_fl_width { hubp_3dlut_fl_width_17 = 17, hubp_3dlut_fl_width_33 = 33, - hubp_3dlut_fl_width_transformed = 4916 + hubp_3dlut_fl_width_transformed = 4916, //mpc default }; enum hubp_3dlut_fl_crossbar_bit_slice { @@ -99,6 +99,22 @@ enum hubp_3dlut_fl_crossbar_bit_slice { hubp_3dlut_fl_crossbar_bit_slice_48_63 = 3 }; +struct hubp_fl_3dlut_config { + bool enabled; + enum hubp_3dlut_fl_width width; + enum hubp_3dlut_fl_mode mode; + enum hubp_3dlut_fl_format format; + uint16_t bias; + uint16_t scale; + struct dc_plane_address address; + enum hubp_3dlut_fl_addressing_mode addr_mode; + enum dc_cm2_gpu_mem_layout layout; + uint8_t protection_bits; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r; +}; + struct hubp { const struct hubp_funcs *funcs; struct dc_context *ctx; @@ -288,7 +304,10 @@ struct hubp_funcs { enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cb_b, enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cr_r); int (*hubp_get_3dlut_fl_done)(struct hubp *hubp); + void (*hubp_program_3dlut_fl_config)(struct hubp *hubp, struct hubp_fl_3dlut_config *cfg); void (*hubp_clear_tiling)(struct hubp *hubp); + uint32_t (*hubp_get_current_read_line)(struct hubp *hubp); + uint32_t (*hubp_get_det_config_error)(struct hubp *hubp); }; #endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h index 7641439f6ca0..22960ee03dee 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h @@ -115,6 +115,16 @@ enum MCM_LUT_ID { MCM_LUT_SHAPER }; +struct mpc_fl_3dlut_config { + bool enabled; + uint16_t width; + bool select_lut_bank_a; + uint16_t bit_depth; + int hubp_index; + uint16_t bias; + uint16_t scale; +}; + union mcm_lut_params { const struct pwl_params *pwl; const struct tetrahedral_params *lut3d; @@ -1059,21 +1069,6 @@ struct mpc_funcs { */ void (*program_lut_mode)(struct mpc *mpc, const enum MCM_LUT_ID id, const enum MCM_LUT_XABLE xable, bool lut_bank_a, int mpcc_id); - /** - * @program_3dlut_size: - * - * Program 3D LUT size. - * - * Parameters: - * - [in/out] mpc - MPC context. - * - [in] is_17x17x17 - is 3dlut 17x17x17 - * - [in] mpcc_id - * - * Return: - * - * void - */ - void (*program_3dlut_size)(struct mpc *mpc, bool is_17x17x17, int mpcc_id); /** * @mcm: @@ -1098,6 +1093,7 @@ struct mpc_funcs { * MPC RMCM new HW sequential programming functions */ struct { + void (*fl_3dlut_configure)(struct mpc *mpc, struct mpc_fl_3dlut_config *cfg, int mpcc_id); void (*enable_3dlut_fl)(struct mpc *mpc, bool enable, int mpcc_id); void (*update_3dlut_fast_load_select)(struct mpc *mpc, int mpcc_id, int hubp_idx); void (*program_lut_read_write_control)(struct mpc *mpc, const enum MCM_LUT_ID id, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/pg_cntl.h b/drivers/gpu/drm/amd/display/dc/inc/hw/pg_cntl.h index 44f86cc2d1d6..227e3f8d7e5f 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/pg_cntl.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/pg_cntl.h @@ -49,6 +49,7 @@ struct pg_cntl_funcs { void (*mem_pg_control)(struct pg_cntl *pg_cntl, bool power_on); void (*dio_pg_control)(struct pg_cntl *pg_cntl, bool power_on); void (*init_pg_status)(struct pg_cntl *pg_cntl); + void (*print_pg_status)(struct pg_cntl *pg_cntl, const char *debug_func, const char *debug_log); }; #endif //__DC_PG_CNTL_H__ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h index 267ace4eef8a..f2de2cf23859 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h @@ -374,6 +374,7 @@ struct timing_generator_funcs { void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg); void (*set_long_vtotal)(struct timing_generator *optc, const struct long_vtotal_params *params); void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg); + void (*wait_otg_disable)(struct timing_generator *optc); bool (*get_optc_double_buffer_pending)(struct timing_generator *tg); bool (*get_otg_double_buffer_pending)(struct timing_generator *tg); bool (*get_pipe_update_pending)(struct timing_generator *tg); diff --git a/drivers/gpu/drm/amd/display/dc/inc/link.h b/drivers/gpu/drm/amd/display/dc/inc/link.h index f2503402c10e..0cce49d95e26 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/link.h +++ b/drivers/gpu/drm/amd/display/dc/inc/link.h @@ -218,7 +218,10 @@ struct link_service { bool (*dp_overwrite_extended_receiver_cap)(struct dc_link *link); enum lttpr_mode (*dp_decide_lttpr_mode)(struct dc_link *link, struct dc_link_settings *link_setting); - + uint8_t (*dp_get_lttpr_count)(struct dc_link *link); + void (*edp_get_alpm_support)(struct dc_link *link, + bool *auxless_support, + bool *auxwake_support); /*************************** DP DPIA/PHY ******************************/ void (*dpia_handle_usb4_bandwidth_allocation_for_link)( diff --git a/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h new file mode 100644 index 000000000000..23daf98b8aa8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __SOC_AND_IP_TRANSLATOR_H__ +#define __SOC_AND_IP_TRANSLATOR_H__ + +#include "dc.h" +#include "dml_top_soc_parameter_types.h" + +struct soc_and_ip_translator_funcs { + void (*get_soc_bb)(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); + void (*get_ip_caps)(struct dml2_ip_capabilities *dml_ip_caps); +}; + +struct soc_and_ip_translator { + const struct soc_and_ip_translator_funcs *translator_funcs; +}; + +struct soc_and_ip_translator *dc_create_soc_and_ip_translator(enum dce_version dc_version); +void dc_destroy_soc_and_ip_translator(struct soc_and_ip_translator **soc_and_ip_translator); + + +#endif // __SOC_AND_IP_TRANSLATOR_H__ diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c index 2956c2b3ad1a..23f41c99fa38 100644 --- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c +++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c @@ -75,7 +75,9 @@ static void dp_retrain_link_dp_test(struct dc_link *link, bool is_hpo_acquired; uint8_t count; int i; - struct audio_output audio_output[MAX_PIPES]; + + struct dc_stream_state *streams_on_link[MAX_PIPES]; + int num_streams_on_link = 0; needs_divider_update = (link->dc->link_srv->dp_get_encoding_format(link_setting) != link->dc->link_srv->dp_get_encoding_format((const struct dc_link_settings *) &link->cur_link_settings)); @@ -99,7 +101,7 @@ static void dp_retrain_link_dp_test(struct dc_link *link, if (needs_divider_update && link->dc->res_pool->funcs->update_dc_state_for_encoder_switch) { link->dc->res_pool->funcs->update_dc_state_for_encoder_switch(link, link_setting, count, - *pipes, &audio_output[0]); + *pipes); for (i = 0; i < count; i++) { pipes[i]->clock_source->funcs->program_pix_clk( pipes[i]->clock_source, @@ -111,15 +113,16 @@ static void dp_retrain_link_dp_test(struct dc_link *link, const struct link_hwss *link_hwss = get_link_hwss( link, &pipes[i]->link_res); - link_hwss->setup_audio_output(pipes[i], &audio_output[i], - pipes[i]->stream_res.audio->inst); + link_hwss->setup_audio_output(pipes[i], + &pipes[i]->stream_res.audio_output, + pipes[i]->stream_res.audio->inst); pipes[i]->stream_res.audio->funcs->az_configure( pipes[i]->stream_res.audio, pipes[i]->stream->signal, - &audio_output[i].crtc_info, + &pipes[i]->stream_res.audio_output.crtc_info, &pipes[i]->stream->audio_info, - &audio_output[i].dp_link_info); + &pipes[i]->stream_res.audio_output.dp_link_info); if (link->dc->config.disable_hbr_audio_dp2 && pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio && @@ -138,12 +141,19 @@ static void dp_retrain_link_dp_test(struct dc_link *link, pipes[i]->stream_res.tg->funcs->enable_crtc(pipes[i]->stream_res.tg); // Set DPMS on with stream update - for (i = 0; i < state->stream_count; i++) - if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link) { - stream_update.stream = state->streams[i]; + // Cache all streams on current link since dc_update_planes_and_stream might kill current_state + for (i = 0; i < MAX_PIPES; i++) { + if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link) + streams_on_link[num_streams_on_link++] = state->streams[i]; + } + + for (i = 0; i < num_streams_on_link; i++) { + if (streams_on_link[i] && streams_on_link[i]->link && streams_on_link[i]->link == link) { + stream_update.stream = streams_on_link[i]; stream_update.dpms_off = &dpms_off; - dc_update_planes_and_stream(state->clk_mgr->ctx->dc, NULL, 0, state->streams[i], &stream_update); + dc_update_planes_and_stream(state->clk_mgr->ctx->dc, NULL, 0, streams_on_link[i], &stream_update); } + } } static void dp_test_send_link_training(struct dc_link *link) diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c index b68bcc9fca0a..892907991f91 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c @@ -138,8 +138,7 @@ void setup_dio_stream_attribute(struct pipe_ctx *pipe_ctx) stream_encoder->funcs->dvi_set_stream_attribute( stream_encoder, &stream->timing, - (stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? - true : false); + stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK); else if (dc_is_lvds_signal(stream->signal)) stream_encoder->funcs->lvds_set_stream_attribute( stream_encoder, 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 827b630daf49..b717e430051a 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -656,7 +656,7 @@ static bool wait_for_entering_dp_alt_mode(struct dc_link *link) return true; is_in_alt_mode = link->link_enc->funcs->is_in_alt_mode(link->link_enc); - DC_LOG_DC("DP Alt mode state on HPD: %d\n", is_in_alt_mode); + DC_LOG_DC("DP Alt mode state on HPD: %d Link=%d\n", is_in_alt_mode, link->link_index); if (is_in_alt_mode) return true; 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 8c8682f743d6..08ee8d2f777b 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -2358,9 +2358,9 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) if (pipe_ctx->stream->sink) { if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) { - DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x\n", __func__, + DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x link=%d\n", __func__, pipe_ctx->stream->sink->edid_caps.display_name, - pipe_ctx->stream->signal); + pipe_ctx->stream->signal, link->link_index); } } @@ -2458,7 +2458,6 @@ void link_set_dpms_on( struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO; struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg; - const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); bool apply_edp_fast_boot_optimization = pipe_ctx->stream->apply_edp_fast_boot_optimization; @@ -2474,9 +2473,10 @@ void link_set_dpms_on( if (pipe_ctx->stream->sink) { if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) { - DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x\n", __func__, + DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x link=%d\n", __func__, pipe_ctx->stream->sink->edid_caps.display_name, - pipe_ctx->stream->signal); + pipe_ctx->stream->signal, + link->link_index); } } @@ -2502,8 +2502,6 @@ void link_set_dpms_on( pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, otg_out_dest); } - link_hwss->setup_stream_attribute(pipe_ctx); - pipe_ctx->stream->apply_edp_fast_boot_optimization = false; // Enable VPG before building infoframe 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 de1143dbbd25..31a73867cd4c 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -165,6 +165,8 @@ static void construct_link_service_dp_capability(struct link_service *link_srv) link_srv->dp_overwrite_extended_receiver_cap = dp_overwrite_extended_receiver_cap; link_srv->dp_decide_lttpr_mode = dp_decide_lttpr_mode; + link_srv->dp_get_lttpr_count = dp_get_lttpr_count; + link_srv->edp_get_alpm_support = edp_get_alpm_support; } /* link dp phy/dpia implements basic dp phy/dpia functionality such as diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c index aecaf37eee35..acdc162de535 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c @@ -408,8 +408,10 @@ enum dc_status link_validate_dp_tunnel_bandwidth(const struct dc *dc, const stru link = stream->link; if (!(link && (stream->signal == SIGNAL_TYPE_DISPLAY_PORT - || stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) - && link->hpd_status)) + || stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST))) + continue; + + if ((link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) && (link->hpd_status == false)) continue; dp_tunnel_settings = get_dp_tunnel_settings(new_ctx, stream); 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 651926e547b9..b12c11bd6a14 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 @@ -1525,8 +1525,8 @@ bool read_is_mst_supported(struct dc_link *link) return false; } - rev.raw = 0; - cap.raw = 0; + rev.raw = 0; + cap.raw = 0; st = core_link_read_dpcd(link, DP_DPCD_REV, &rev.raw, sizeof(rev)); @@ -2125,13 +2125,13 @@ void detect_edp_sink_caps(struct dc_link *link) &backlight_adj_cap, sizeof(backlight_adj_cap)); link->dpcd_caps.dynamic_backlight_capable_edp = - (backlight_adj_cap & DP_EDP_DYNAMIC_BACKLIGHT_CAP) ? true:false; + (backlight_adj_cap & DP_EDP_DYNAMIC_BACKLIGHT_CAP) ? true : false; core_link_read_dpcd(link, DP_EDP_GENERAL_CAP_1, &general_edp_cap, sizeof(general_edp_cap)); link->dpcd_caps.set_power_state_capable_edp = - (general_edp_cap & DP_EDP_SET_POWER_CAP) ? true:false; + (general_edp_cap & DP_EDP_SET_POWER_CAP) ? true : false; set_default_brightness_aux(link); @@ -2195,6 +2195,12 @@ void detect_edp_sink_caps(struct dc_link *link) DP_EDP_MSO_LINK_CAPABILITIES, (uint8_t *)&link->dpcd_caps.mso_cap_sst_links_supported, sizeof(link->dpcd_caps.mso_cap_sst_links_supported)); + /* + * Read eDP general capability 2 + */ + core_link_read_dpcd(link, DP_EDP_GENERAL_CAP_2, + (uint8_t *)&link->dpcd_caps.dp_edp_general_cap_2, + sizeof(link->dpcd_caps.dp_edp_general_cap_2)); } bool dp_get_max_link_enc_cap(const struct dc_link *link, struct dc_link_settings *max_link_enc_cap) @@ -2506,3 +2512,40 @@ bool dp_is_sink_present(struct dc_link *link) return present; } + +uint8_t dp_get_lttpr_count(struct dc_link *link) +{ + if (dp_is_lttpr_present(link)) + return dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); + + return 0; +} + +void edp_get_alpm_support(struct dc_link *link, + bool *auxless_support, + bool *auxwake_support) +{ + bool lttpr_present = dp_is_lttpr_present(link); + + if (auxless_support == NULL || auxwake_support == NULL) + return; + + *auxless_support = false; + *auxwake_support = false; + + if (!dc_is_embedded_signal(link->connector_signal)) + return; + + if (link->dpcd_caps.alpm_caps.bits.AUX_LESS_ALPM_CAP) { + if (lttpr_present) { + if (link->dpcd_caps.lttpr_caps.alpm.bits.AUX_LESS_ALPM_SUPPORTED) + *auxless_support = true; + } else + *auxless_support = true; + } + + if (link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP) { + if (!lttpr_present) + *auxwake_support = true; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h index 940b147cc5d4..7170db5a1c13 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h @@ -108,4 +108,10 @@ uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw); bool dp_overwrite_extended_receiver_cap(struct dc_link *link); +uint8_t dp_get_lttpr_count(struct dc_link *link); + +void edp_get_alpm_support(struct dc_link *link, + bool *auxless_support, + bool *auxwake_support); + #endif /* __DC_LINK_DP_CAPABILITY_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c index 819bf2d8ba53..8a3c18ae97a7 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -48,8 +48,7 @@ */ static bool link_dp_is_bw_alloc_available(struct dc_link *link) { - return (link && link->hpd_status - && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling + return (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support); } @@ -226,36 +225,41 @@ bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link) bool ret = false; uint8_t val; - if (link->hpd_status) { - val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ; - - if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) { - DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index); - - retrieve_usb4_dp_bw_allocation_info(link); - - if (link->dpia_bw_alloc_config.nrd_max_link_rate && link->dpia_bw_alloc_config.nrd_max_lane_count) { - link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate; - link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count; - } - - link->dpia_bw_alloc_config.bw_alloc_enabled = true; - ret = true; - - if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) { - /* - * During DP tunnel creation, the CM preallocates BW - * and reduces the estimated BW of other DPIAs. - * The CM releases the preallocation only when the allocation is complete. - * Perform a zero allocation to make the CM release the preallocation - * and correctly update the estimated BW for all DPIAs per host router. - */ - link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0); - } - } else - DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index); + if (link->dc->debug.dpia_debug.bits.enable_bw_allocation_mode == false) { + DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode disabled", __func__, link->link_index); + return false; } + val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ; + + if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) { + DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index); + + retrieve_usb4_dp_bw_allocation_info(link); + + if ( + link->dpia_bw_alloc_config.nrd_max_link_rate + && link->dpia_bw_alloc_config.nrd_max_lane_count) { + link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate; + link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count; + } + + link->dpia_bw_alloc_config.bw_alloc_enabled = true; + ret = true; + + if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) { + /* + * During DP tunnel creation, the CM preallocates BW + * and reduces the estimated BW of other DPIAs. + * The CM releases the preallocation only when the allocation is complete. + * Perform a zero allocation to make the CM release the preallocation + * and correctly update the estimated BW for all DPIAs per host router. + */ + link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0); + } + } else + DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index); + return ret; } @@ -297,15 +301,12 @@ void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int pe { if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling && link->dpia_bw_alloc_config.bw_alloc_enabled) { - //1. Hot Plug - if (link->hpd_status && peak_bw > 0) { + if (peak_bw > 0) { // If DP over USB4 then we need to check BW allocation link->dpia_bw_alloc_config.link_max_bw = peak_bw; link_dpia_send_bw_alloc_request(link, peak_bw); - } - //2. Cold Unplug - else if (!link->hpd_status) + } else dpia_bw_alloc_unplug(link); } } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c index 2dc1a660e504..134093ce5a8e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c @@ -1018,7 +1018,12 @@ static enum link_training_result dpcd_exit_training_mode(struct dc_link *link, e { enum dc_status status; uint8_t sink_status = 0; - uint8_t i; + uint32_t i; + uint8_t lttpr_count = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); + uint32_t intra_hop_disable_time_ms = (lttpr_count > 0 ? lttpr_count * 300 : 10); + + // Each hop could theoretically take over 256ms (max 128b/132b AUX RD INTERVAL) + // To be safe, allow 300ms per LTTPR and 10ms for no LTTPR case /* clear training pattern set */ status = dpcd_set_training_pattern(link, DP_TRAINING_PATTERN_VIDEOIDLE); @@ -1028,7 +1033,7 @@ static enum link_training_result dpcd_exit_training_mode(struct dc_link *link, e if (encoding == DP_128b_132b_ENCODING) { /* poll for intra-hop disable */ - for (i = 0; i < 10; i++) { + for (i = 0; i < intra_hop_disable_time_ms; i++) { if ((core_link_read_dpcd(link, DP_SINK_STATUS, &sink_status, 1) == DC_OK) && (sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION) == 0) break; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index e7927b8f5ba3..8b7b87b21c2e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -870,6 +870,8 @@ bool edp_setup_psr(struct dc_link *link, psr_context->dsc_slice_height = psr_config->dsc_slice_height; + psr_context->os_request_force_ffu = psr_config->os_request_force_ffu; + if (psr) { link->psr_settings.psr_feature_enabled = psr->funcs->psr_copy_settings(psr, link, psr_context, panel_inst); @@ -944,7 +946,7 @@ bool edp_set_replay_allow_active(struct dc_link *link, const bool *allow_active, // TODO: Handle mux change case if force_static is set // If force_static is set, just change the replay_allow_active state directly if (replay != NULL && link->replay_settings.replay_feature_enabled) - replay->funcs->replay_enable(replay, *allow_active, wait, panel_inst, link); + replay->funcs->replay_enable(replay, *allow_active, wait, panel_inst); link->replay_settings.replay_allow_active = *allow_active; } @@ -1029,6 +1031,8 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream replay_context.line_time_in_ns = lineTimeInNs; + replay_context.os_request_force_ffu = link->replay_settings.config.os_request_force_ffu; + link->replay_settings.replay_feature_enabled = replay->funcs->replay_copy_settings(replay, link, &replay_context, panel_inst); if (link->replay_settings.replay_feature_enabled) { @@ -1042,7 +1046,13 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream (uint8_t *)&(replay_config.raw), sizeof(uint8_t)); memset(&alpm_config, 0, sizeof(alpm_config)); - alpm_config.bits.ENABLE = 1; + alpm_config.bits.ENABLE = link->replay_settings.config.alpm_mode != DC_ALPM_UNSUPPORTED ? 1 : 0; + + if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) { + alpm_config.bits.ALPM_MODE_SEL = 1; + alpm_config.bits.ACDS_PERIOD_DURATION = 0; + } + dm_helpers_dp_write_dpcd( link->ctx, link, diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.c b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.c index 259a98e4ee2c..2a422e223bf2 100644 --- a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.c +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.c @@ -284,7 +284,7 @@ void mcifwb2_dump_frame(struct mcif_wb *mcif_wb, REG_UPDATE(MCIF_WB_BUFMGR_SW_CONTROL, MCIF_WB_BUFMGR_SW_LOCK, 0xf); - memcpy(dest_luma_buffer, luma_buffer, mcif_params->luma_pitch * dest_height); + memcpy(dest_luma_buffer, luma_buffer, (size_t)mcif_params->luma_pitch * dest_height); memcpy(dest_chroma_buffer, chroma_buffer, mcif_params->chroma_pitch * dest_height / 2); REG_UPDATE(MCIF_WB_BUFMGR_SW_CONTROL, MCIF_WB_BUFMGR_SW_LOCK, 0x0); diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c index f3fb3fe13757..e1a0308dee57 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c @@ -287,13 +287,6 @@ void mpc401_program_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_I } } -void mpc401_program_3dlut_size(struct mpc *mpc, bool is_17x17x17, int mpcc_id) -{ - struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); - - REG_UPDATE(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, is_17x17x17 ? 0 : 1); -} - void mpc_program_gamut_remap( struct mpc *mpc, unsigned int mpcc_id, @@ -611,7 +604,6 @@ static const struct mpc_funcs dcn401_mpc_funcs = { .populate_lut = mpc401_populate_lut, .program_lut_read_write_control = mpc401_program_lut_read_write_control, .program_lut_mode = mpc401_program_lut_mode, - .program_3dlut_size = mpc401_program_3dlut_size, }; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h index eb0c68d0b0c7..fdc42f8ab3ff 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h @@ -221,11 +221,6 @@ void mpc401_program_lut_read_write_control( bool lut_bank_a, int mpcc_id); -void mpc401_program_3dlut_size( - struct mpc *mpc, - bool is_17x17x17, - int mpcc_id); - void mpc401_set_gamut_remap( struct mpc *mpc, int mpcc_id, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h index d159e3ed3bb3..ead92ad78a23 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h @@ -62,6 +62,7 @@ SF(OTG0_OTG_CONTROL, OTG_DISABLE_POINT_CNTL, mask_sh),\ SF(OTG0_OTG_CONTROL, OTG_FIELD_NUMBER_CNTL, mask_sh),\ SF(OTG0_OTG_CONTROL, OTG_OUT_MUX, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_CURRENT_MASTER_EN_STATE, mask_sh),\ SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EN, mask_sh),\ SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_LINE_NUM, mask_sh),\ SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_POLARITY, mask_sh),\ diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c index 72bff94cb57d..52d5ea98c86b 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c @@ -162,6 +162,8 @@ static bool optc35_disable_crtc(struct timing_generator *optc) REG_WAIT(OTG_CLOCK_CONTROL, OTG_BUSY, 0, 1, 100000); + REG_WAIT(OTG_CONTROL, OTG_CURRENT_MASTER_EN_STATE, 0, 1, 100000); + optc1_clear_optc_underflow(optc); return true; @@ -428,6 +430,21 @@ static void optc35_set_long_vtotal( } } +static void optc35_wait_otg_disable(struct timing_generator *optc) +{ + struct optc *optc1; + uint32_t is_master_en; + + if (!optc || !optc->ctx) + return; + + optc1 = DCN10TG_FROM_TG(optc); + + REG_GET(OTG_CONTROL, OTG_MASTER_EN, &is_master_en); + if (!is_master_en) + REG_WAIT(OTG_CLOCK_CONTROL, OTG_CURRENT_MASTER_EN_STATE, 0, 1, 100000); +} + static const struct timing_generator_funcs dcn35_tg_funcs = { .validate_timing = optc1_validate_timing, .program_timing = optc1_program_timing, @@ -479,6 +496,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs = { .set_odm_bypass = optc32_set_odm_bypass, .set_odm_combine = optc35_set_odm_combine, .get_optc_source = optc2_get_optc_source, + .wait_otg_disable = optc35_wait_otg_disable, .set_h_timing_div_manual_mode = optc32_set_h_timing_div_manual_mode, .set_out_mux = optc3_set_out_mux, .set_drr_trigger_window = optc3_set_drr_trigger_window, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c index ff79c38287df..5af13706e601 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c @@ -226,6 +226,11 @@ bool optc401_disable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 0); + // wait until CRTC_CURRENT_MASTER_EN_STATE == 0 + REG_WAIT(OTG_CONTROL, + OTG_CURRENT_MASTER_EN_STATE, + 0, 10, 15000); + /* CRTC disabled, so disable clock. */ REG_WAIT(OTG_CLOCK_CONTROL, OTG_BUSY, 0, diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.c b/drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.c index af21c0a27f86..72bd43f9bbe2 100644 --- a/drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.c +++ b/drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.c @@ -79,16 +79,12 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo uint32_t power_gate = power_on ? 0 : 1; uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl = 0; - bool block_enabled; + bool block_enabled = false; + bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || + pg_cntl->ctx->dc->debug.disable_dsc_power_gate || + pg_cntl->ctx->dc->idle_optimizations_allowed; - /*need to enable dscclk regardless DSC_PG*/ - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc && power_on) - pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc( - pg_cntl->ctx->dc->res_pool->dccg, dsc_inst); - - if (pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_dsc_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed) + if (skip_pg && !power_on) return; block_enabled = pg_cntl35_dsc_pg_status(pg_cntl, dsc_inst); @@ -111,7 +107,7 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo REG_WAIT(DOMAIN16_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); + 1, 10000); break; case 1: /* DSC1 */ REG_UPDATE(DOMAIN17_PG_CONFIG, @@ -119,7 +115,7 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo REG_WAIT(DOMAIN17_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); + 1, 10000); break; case 2: /* DSC2 */ REG_UPDATE(DOMAIN18_PG_CONFIG, @@ -127,7 +123,7 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo REG_WAIT(DOMAIN18_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); + 1, 10000); break; case 3: /* DSC3 */ REG_UPDATE(DOMAIN19_PG_CONFIG, @@ -135,7 +131,7 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo REG_WAIT(DOMAIN19_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, - 1, 1000); + 1, 10000); break; default: BREAK_TO_DEBUGGER(); @@ -144,12 +140,6 @@ void pg_cntl35_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo if (dsc_inst < MAX_PIPES) pg_cntl->pg_pipe_res_enable[PG_DSC][dsc_inst] = power_on; - - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc && !power_on) { - /*this is to disable dscclk*/ - pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc( - pg_cntl->ctx->dc->res_pool->dccg, dsc_inst); - } } static bool pg_cntl35_hubp_dpp_pg_status(struct pg_cntl *pg_cntl, unsigned int hubp_dpp_inst) @@ -189,11 +179,12 @@ void pg_cntl35_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; bool block_enabled; + bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || + pg_cntl->ctx->dc->debug.disable_hubp_power_gate || + pg_cntl->ctx->dc->debug.disable_dpp_power_gate || + pg_cntl->ctx->dc->idle_optimizations_allowed; - if (pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_hubp_power_gate || - pg_cntl->ctx->dc->debug.disable_dpp_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed) + if (skip_pg && !power_on) return; block_enabled = pg_cntl35_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst); @@ -213,22 +204,22 @@ void pg_cntl35_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp case 0: /* DPP0 & HUBP0 */ REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); - REG_WAIT(DOMAIN0_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); + REG_WAIT(DOMAIN0_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 10000); break; case 1: /* DPP1 & HUBP1 */ REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); - REG_WAIT(DOMAIN1_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); + REG_WAIT(DOMAIN1_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 10000); break; case 2: /* DPP2 & HUBP2 */ REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); - REG_WAIT(DOMAIN2_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); + REG_WAIT(DOMAIN2_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 10000); break; case 3: /* DPP3 & HUBP3 */ REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); - REG_WAIT(DOMAIN3_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); + REG_WAIT(DOMAIN3_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 10000); break; default: BREAK_TO_DEBUGGER(); @@ -501,6 +492,36 @@ void pg_cntl35_init_pg_status(struct pg_cntl *pg_cntl) pg_cntl->pg_res_enable[PG_DWB] = block_enabled; } +static void pg_cntl35_print_pg_status(struct pg_cntl *pg_cntl, const char *debug_func, const char *debug_log) +{ + int i = 0; + bool block_enabled = false; + + DC_LOG_DEBUG("%s: %s", debug_func, debug_log); + + DC_LOG_DEBUG("PG_CNTL status:\n"); + + block_enabled = pg_cntl35_io_clk_status(pg_cntl); + DC_LOG_DEBUG("ONO0=%d (DCCG, DIO, DCIO)\n", block_enabled ? 1 : 0); + + block_enabled = pg_cntl35_mem_status(pg_cntl); + DC_LOG_DEBUG("ONO1=%d (DCHUBBUB, DCHVM, DCHUBBUBMEM)\n", block_enabled ? 1 : 0); + + block_enabled = pg_cntl35_plane_otg_status(pg_cntl); + DC_LOG_DEBUG("ONO2=%d (MPC, OPP, OPTC, DWB)\n", block_enabled ? 1 : 0); + + block_enabled = pg_cntl35_hpo_pg_status(pg_cntl); + DC_LOG_DEBUG("ONO3=%d (HPO)\n", block_enabled ? 1 : 0); + + for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) { + block_enabled = pg_cntl35_hubp_dpp_pg_status(pg_cntl, i); + DC_LOG_DEBUG("ONO%d=%d (DCHUBP%d, DPP%d)\n", 4 + i * 2, block_enabled ? 1 : 0, i, i); + + block_enabled = pg_cntl35_dsc_pg_status(pg_cntl, i); + DC_LOG_DEBUG("ONO%d=%d (DSC%d)\n", 5 + i * 2, block_enabled ? 1 : 0, i); + } +} + static const struct pg_cntl_funcs pg_cntl35_funcs = { .init_pg_status = pg_cntl35_init_pg_status, .dsc_pg_control = pg_cntl35_dsc_pg_control, @@ -511,7 +532,8 @@ static const struct pg_cntl_funcs pg_cntl35_funcs = { .mpcc_pg_control = pg_cntl35_mpcc_pg_control, .opp_pg_control = pg_cntl35_opp_pg_control, .optc_pg_control = pg_cntl35_optc_pg_control, - .dwb_pg_control = pg_cntl35_dwb_pg_control + .dwb_pg_control = pg_cntl35_dwb_pg_control, + .print_pg_status = pg_cntl35_print_pg_status }; struct pg_cntl *pg_cntl35_create( diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c index 164ba796f64c..869a8e515fc0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c @@ -1111,12 +1111,12 @@ static void bw_calcs_data_update_from_pplib(struct dc *dc) &clks); dc->bw_vbios->low_yclk = bw_frc_to_fixed( - clks.clocks_in_khz[0] * memory_type_multiplier, 1000); + (int64_t)clks.clocks_in_khz[0] * memory_type_multiplier, 1000); dc->bw_vbios->mid_yclk = bw_frc_to_fixed( - clks.clocks_in_khz[clks.num_levels>>1] * memory_type_multiplier, + (int64_t)clks.clocks_in_khz[clks.num_levels>>1] * memory_type_multiplier, 1000); dc->bw_vbios->high_yclk = bw_frc_to_fixed( - clks.clocks_in_khz[clks.num_levels-1] * memory_type_multiplier, + (int64_t)clks.clocks_in_khz[clks.num_levels-1] * memory_type_multiplier, 1000); return; @@ -1152,12 +1152,12 @@ static void bw_calcs_data_update_from_pplib(struct dc *dc) * YCLK = UMACLK*m_memoryTypeMultiplier */ dc->bw_vbios->low_yclk = bw_frc_to_fixed( - mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); + (int64_t)mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); dc->bw_vbios->mid_yclk = bw_frc_to_fixed( - mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, + (int64_t)mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, 1000); dc->bw_vbios->high_yclk = bw_frc_to_fixed( - mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, + (int64_t)mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, 1000); /* Now notify PPLib/SMU about which Watermarks sets they should select diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c index eb1e158d3436..2f23cc6df571 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c @@ -990,12 +990,12 @@ static void bw_calcs_data_update_from_pplib(struct dc *dc) memory_type_multiplier = MEMORY_TYPE_HBM; dc->bw_vbios->low_yclk = bw_frc_to_fixed( - mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); + (int64_t)mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); dc->bw_vbios->mid_yclk = bw_frc_to_fixed( - mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, + (int64_t)mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, 1000); dc->bw_vbios->high_yclk = bw_frc_to_fixed( - mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, + (int64_t)mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, 1000); /* Now notify PPLib/SMU about which Watermarks sets they should select 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 58b59d52dc9d..53b60044653f 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 @@ -373,7 +373,7 @@ static const struct resource_caps res_cap = { .num_timing_generator = 6, .num_audio = 6, .num_stream_encoder = 6, - .num_pll = 2, + .num_pll = 3, .num_ddc = 6, }; @@ -389,7 +389,7 @@ static const struct resource_caps res_cap_64 = { .num_timing_generator = 2, .num_audio = 2, .num_stream_encoder = 2, - .num_pll = 2, + .num_pll = 3, .num_ddc = 2, }; @@ -973,21 +973,24 @@ static bool dce60_construct( if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { pool->base.dp_clock_source = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + /* DCE 6.0 and 6.4: PLL0 can only be used with DP. Don't initialize it here. */ pool->base.clock_sources[0] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); pool->base.clock_sources[1] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); pool->base.clk_src_count = 2; } else { pool->base.dp_clock_source = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); pool->base.clock_sources[0] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); - pool->base.clk_src_count = 1; + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; } if (pool->base.dp_clock_source == NULL) { @@ -1365,21 +1368,24 @@ static bool dce64_construct( if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { pool->base.dp_clock_source = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + /* DCE 6.0 and 6.4: PLL0 can only be used with DP. Don't initialize it here. */ pool->base.clock_sources[0] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], false); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); pool->base.clock_sources[1] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); pool->base.clk_src_count = 2; } else { pool->base.dp_clock_source = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], true); + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); pool->base.clock_sources[0] = - dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); - pool->base.clk_src_count = 1; + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; } if (pool->base.dp_clock_source == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 895349d9ca07..201ed863b69e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -2192,7 +2192,7 @@ void dcn30_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params j = 0; // create the final dcfclk and uclk table while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { - if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { + if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; } else { 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 3ed7f50554e2..ca17e5d8fdc2 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 @@ -2239,8 +2239,7 @@ struct resource_pool *dcn31_create_resource_pool( enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link, struct dc_link_settings *link_setting, uint8_t pipe_count, - struct pipe_ctx *pipes, - struct audio_output *audio_output) + struct pipe_ctx *pipes) { struct dc_state *state = link->dc->current_state; int i; @@ -2255,7 +2254,7 @@ enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link, // Setup audio if (pipes[i].stream_res.audio != NULL) - build_audio_output(state, &pipes[i], &audio_output[i]); + build_audio_output(state, &pipes[i], &pipes[i].stream_res.audio_output); } #else /* This DCN requires rate divider updates and audio reprogramming to allow DP1<-->DP2 link rate switching, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.h index c32c85ef0ba4..7e8fde65528f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.h @@ -69,8 +69,7 @@ unsigned int dcn31_get_det_buffer_size( enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link, struct dc_link_settings *link_setting, uint8_t pipe_count, - struct pipe_ctx *pipes, - struct audio_output *audio_output); + struct pipe_ctx *pipes); /*temp: B0 specific before switch to dcn313 headers*/ #ifndef regPHYPLLF_PIXCLK_RESYNC_CNTL diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h index 82f966cf4ed2..20d714596021 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h @@ -1141,7 +1141,8 @@ unsigned int dcn32_get_max_hw_cursor_size(const struct dc *dc, SRI_ARR(DCN_SURF1_TTU_CNTL1, HUBPREQ, id), \ SRI_ARR(DCN_CUR0_TTU_CNTL0, HUBPREQ, id), \ SRI_ARR(DCN_CUR0_TTU_CNTL1, HUBPREQ, id), \ - SRI_ARR(HUBP_CLK_CNTL, HUBP, id) + SRI_ARR(HUBP_CLK_CNTL, HUBP, id), \ + SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id) #define HUBP_REG_LIST_DCN2_COMMON_RI(id) \ HUBP_REG_LIST_DCN_RI(id), HUBP_REG_LIST_DCN_VM_RI(id), \ SRI_ARR(PREFETCH_SETTINGS, HUBPREQ, id), \ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 1d1a002f6d54..8475c6eec547 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -1900,6 +1900,9 @@ static bool dcn35_resource_construct( dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; + dc->caps.num_of_host_routers = 2; + dc->caps.num_of_dpias_per_host_router = 2; + /* max_disp_clock_khz_at_vmin is slightly lower than the STA value in order * to provide some margin. * It's expected for furture ASIC to have equal or higher value, in order to diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 47912e0861a2..0971c0f74186 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -1872,6 +1872,9 @@ static bool dcn351_resource_construct( dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; + dc->caps.num_of_host_routers = 2; + dc->caps.num_of_dpias_per_host_router = 2; + /* max_disp_clock_khz_at_vmin is slightly lower than the STA value in order * to provide some margin. * It's expected for furture ASIC to have equal or higher value, in order to diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index 9ba91e214dda..8bae7fcedc22 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -1873,6 +1873,9 @@ static bool dcn36_resource_construct( dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; + dc->caps.num_of_host_routers = 2; + dc->caps.num_of_dpias_per_host_router = 2; + /* max_disp_clock_khz_at_vmin is slightly lower than the STA value in order * to provide some margin. * It's expected for furture ASIC to have equal or higher value, in order to diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index b3988e38d0a6..068c123ea8a8 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -708,6 +708,7 @@ static const struct dc_debug_options debug_defaults_drv = { }, .use_max_lb = true, .force_disable_subvp = false, + .disable_force_pstate_allow_on_hw_release = false, .exit_idle_opt_for_cursor_updates = true, .using_dml2 = true, .using_dml21 = true, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h index 2ae6831c31ef..0fc66487d800 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h @@ -140,7 +140,8 @@ void dcn401_prepare_mcache_programming(struct dc *dc, struct dc_state *context); SRI_ARR(UCLK_PSTATE_FORCE, HUBPREQ, id), \ HUBP_3DLUT_FL_REG_LIST_DCN401(id), \ SRI_ARR(DCSURF_VIEWPORT_MCACHE_SPLIT_COORDINATE, HUBP, id), \ - SRI_ARR(DCHUBP_MCACHEID_CONFIG, HUBP, id) + SRI_ARR(DCHUBP_MCACHEID_CONFIG, HUBP, id), \ + SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id) /* ABM */ #define ABM_DCN401_REG_LIST_RI(id) \ diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile new file mode 100644 index 000000000000..bc93356a0b5b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: MIT +# +# Copyright 2025 Advanced Micro Devices, Inc. +# Makefile for bounding box component. +# Floating point required due to nature of bounding box values + +soc_and_ip_translator_ccflags := $(CC_FLAGS_FPU) +soc_and_ip_translator_rcflags := $(CC_FLAGS_NO_FPU) + +CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags) + +CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags) + +soc_and_ip_translator := soc_and_ip_translator.o +soc_and_ip_translator += dcn401/dcn401_soc_and_ip_translator.o + +AMD_DAL_soc_and_ip_translator := $(addprefix $(AMDDALPATH)/dc/soc_and_ip_translator/, $(soc_and_ip_translator)) + +AMD_DISPLAY_FILES += $(AMD_DAL_soc_and_ip_translator) diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c new file mode 100644 index 000000000000..3190c76eb482 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn401_soc_and_ip_translator.h" +#include "bounding_boxes/dcn4_soc_bb.h" + +/* soc_and_ip_translator component used to get up-to-date values for bounding box. + * Bounding box values are stored in several locations and locations can vary with DCN revision. + * This component provides an interface to get DCN-specific bounding box values. + */ + +static void get_default_soc_bb(struct dml2_soc_bb *soc_bb) +{ + memcpy(soc_bb, &dml2_socbb_dcn401, sizeof(struct dml2_soc_bb)); + memcpy(&soc_bb->qos_parameters, &dml_dcn4_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters)); +} + +/* + * DC clock table is obtained from SMU during runtime. + * SMU stands for System Management Unit. It is a power management processor. + * It owns the initialization of dc's clock table and programming of clock values + * based on dc's requests. + * Our clock values in base soc bb is a dummy placeholder. The real clock values + * are retrieved from SMU firmware to dc clock table at runtime. + * This function overrides our dummy placeholder values with real values in dc + * clock table. + */ +static void dcn401_convert_dc_clock_table_to_soc_bb_clock_table( + struct dml2_soc_state_table *dml_clk_table, + const struct clk_bw_params *dc_bw_params, + bool use_clock_dc_limits) +{ + int i; + const struct clk_limit_table *dc_clk_table; + + if (dc_bw_params == NULL) + /* skip if bw params could not be obtained from smu */ + return; + + dc_clk_table = &dc_bw_params->clk_table; + + /* dcfclk */ + if (dc_clk_table->num_entries_per_clk.num_dcfclk_levels) { + dml_clk_table->dcfclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dcfclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->dcfclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.dcfclk_mhz && + dc_clk_table->entries[i].dcfclk_mhz > dc_bw_params->dc_mode_limit.dcfclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].dcfclk_mhz < dc_bw_params->dc_mode_limit.dcfclk_mhz) { + dml_clk_table->dcfclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dcfclk_mhz * 1000; + dml_clk_table->dcfclk.num_clk_values = i + 1; + } else { + dml_clk_table->dcfclk.clk_values_khz[i] = 0; + dml_clk_table->dcfclk.num_clk_values = i; + } + } else { + dml_clk_table->dcfclk.clk_values_khz[i] = dc_clk_table->entries[i].dcfclk_mhz * 1000; + } + } else { + dml_clk_table->dcfclk.clk_values_khz[i] = 0; + } + } + } + + /* fclk */ + if (dc_clk_table->num_entries_per_clk.num_fclk_levels) { + dml_clk_table->fclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_fclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->fclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.fclk_mhz && + dc_clk_table->entries[i].fclk_mhz > dc_bw_params->dc_mode_limit.fclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].fclk_mhz < dc_bw_params->dc_mode_limit.fclk_mhz) { + dml_clk_table->fclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.fclk_mhz * 1000; + dml_clk_table->fclk.num_clk_values = i + 1; + } else { + dml_clk_table->fclk.clk_values_khz[i] = 0; + dml_clk_table->fclk.num_clk_values = i; + } + } else { + dml_clk_table->fclk.clk_values_khz[i] = dc_clk_table->entries[i].fclk_mhz * 1000; + } + } else { + dml_clk_table->fclk.clk_values_khz[i] = 0; + } + } + } + + /* uclk */ + if (dc_clk_table->num_entries_per_clk.num_memclk_levels) { + dml_clk_table->uclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_memclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->uclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.memclk_mhz && + dc_clk_table->entries[i].memclk_mhz > dc_bw_params->dc_mode_limit.memclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].memclk_mhz < dc_bw_params->dc_mode_limit.memclk_mhz) { + dml_clk_table->uclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.memclk_mhz * 1000; + dml_clk_table->uclk.num_clk_values = i + 1; + } else { + dml_clk_table->uclk.clk_values_khz[i] = 0; + dml_clk_table->uclk.num_clk_values = i; + } + } else { + dml_clk_table->uclk.clk_values_khz[i] = dc_clk_table->entries[i].memclk_mhz * 1000; + } + } else { + dml_clk_table->uclk.clk_values_khz[i] = 0; + } + } + } + + /* dispclk */ + if (dc_clk_table->num_entries_per_clk.num_dispclk_levels) { + dml_clk_table->dispclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dispclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->dispclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.dispclk_mhz && + dc_clk_table->entries[i].dispclk_mhz > dc_bw_params->dc_mode_limit.dispclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].dispclk_mhz < dc_bw_params->dc_mode_limit.dispclk_mhz) { + dml_clk_table->dispclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dispclk_mhz * 1000; + dml_clk_table->dispclk.num_clk_values = i + 1; + } else { + dml_clk_table->dispclk.clk_values_khz[i] = 0; + dml_clk_table->dispclk.num_clk_values = i; + } + } else { + dml_clk_table->dispclk.clk_values_khz[i] = dc_clk_table->entries[i].dispclk_mhz * 1000; + } + } else { + dml_clk_table->dispclk.clk_values_khz[i] = 0; + } + } + } + + /* dppclk */ + if (dc_clk_table->num_entries_per_clk.num_dppclk_levels) { + dml_clk_table->dppclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dppclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->dppclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.dppclk_mhz && + dc_clk_table->entries[i].dppclk_mhz > dc_bw_params->dc_mode_limit.dppclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].dppclk_mhz < dc_bw_params->dc_mode_limit.dppclk_mhz) { + dml_clk_table->dppclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dppclk_mhz * 1000; + dml_clk_table->dppclk.num_clk_values = i + 1; + } else { + dml_clk_table->dppclk.clk_values_khz[i] = 0; + dml_clk_table->dppclk.num_clk_values = i; + } + } else { + dml_clk_table->dppclk.clk_values_khz[i] = dc_clk_table->entries[i].dppclk_mhz * 1000; + } + } else { + dml_clk_table->dppclk.clk_values_khz[i] = 0; + } + } + } + + /* dtbclk */ + if (dc_clk_table->num_entries_per_clk.num_dtbclk_levels) { + dml_clk_table->dtbclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dtbclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->dtbclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.dtbclk_mhz && + dc_clk_table->entries[i].dtbclk_mhz > dc_bw_params->dc_mode_limit.dtbclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].dtbclk_mhz < dc_bw_params->dc_mode_limit.dtbclk_mhz) { + dml_clk_table->dtbclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.dtbclk_mhz * 1000; + dml_clk_table->dtbclk.num_clk_values = i + 1; + } else { + dml_clk_table->dtbclk.clk_values_khz[i] = 0; + dml_clk_table->dtbclk.num_clk_values = i; + } + } else { + dml_clk_table->dtbclk.clk_values_khz[i] = dc_clk_table->entries[i].dtbclk_mhz * 1000; + } + } else { + dml_clk_table->dtbclk.clk_values_khz[i] = 0; + } + } + } + + /* socclk */ + if (dc_clk_table->num_entries_per_clk.num_socclk_levels) { + dml_clk_table->socclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_socclk_levels; + for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) { + if (i < dml_clk_table->socclk.num_clk_values) { + if (use_clock_dc_limits && dc_bw_params->dc_mode_limit.socclk_mhz && + dc_clk_table->entries[i].socclk_mhz > dc_bw_params->dc_mode_limit.socclk_mhz) { + if (i == 0 || dc_clk_table->entries[i-1].socclk_mhz < dc_bw_params->dc_mode_limit.socclk_mhz) { + dml_clk_table->socclk.clk_values_khz[i] = dc_bw_params->dc_mode_limit.socclk_mhz * 1000; + dml_clk_table->socclk.num_clk_values = i + 1; + } else { + dml_clk_table->socclk.clk_values_khz[i] = 0; + dml_clk_table->socclk.num_clk_values = i; + } + } else { + dml_clk_table->socclk.clk_values_khz[i] = dc_clk_table->entries[i].socclk_mhz * 1000; + } + } else { + dml_clk_table->socclk.clk_values_khz[i] = 0; + } + } + } + + /* dram config */ + dml_clk_table->dram_config.channel_count = dc_bw_params->num_channels; + dml_clk_table->dram_config.channel_width_bytes = dc_bw_params->dram_channel_width_bytes; +} + +void dcn401_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + soc_bb->dprefclk_mhz = dc->clk_mgr->dprefclk_khz / 1000; + soc_bb->dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; + soc_bb->mall_allocated_for_dcn_mbytes = dc->caps.mall_size_total / (1024 * 1024); + + if (dc->clk_mgr->funcs->is_smu_present && + dc->clk_mgr->funcs->is_smu_present(dc->clk_mgr)) { + dcn401_convert_dc_clock_table_to_soc_bb_clock_table(&soc_bb->clk_table, + dc->clk_mgr->bw_params, + config->use_clock_dc_limits); + } +} + +void dcn401_update_soc_bb_with_values_from_vbios(struct dml2_soc_bb *soc_bb, const struct dc *dc) +{ + soc_bb->dchub_refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; + soc_bb->xtalclk_mhz = dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency / 1000; + + /* latencies in vbios are platform specific and should be used if provided */ + if (dc->ctx->dc_bios->bb_info.dram_clock_change_latency_100ns) + soc_bb->power_management_parameters.dram_clk_change_blackout_us = + dc->ctx->dc_bios->bb_info.dram_clock_change_latency_100ns / 10.0; + + if (dc->ctx->dc_bios->bb_info.dram_sr_enter_exit_latency_100ns) + soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us = + dc->ctx->dc_bios->bb_info.dram_sr_enter_exit_latency_100ns / 10.0; + + if (dc->ctx->dc_bios->bb_info.dram_sr_exit_latency_100ns) + soc_bb->power_management_parameters.stutter_exit_latency_us = + dc->ctx->dc_bios->bb_info.dram_sr_exit_latency_100ns / 10.0; +} + +void dcn401_update_soc_bb_with_values_from_software_policy(struct dml2_soc_bb *soc_bb, const struct dc *dc) +{ + /* set if the value is provided */ + if (dc->bb_overrides.sr_exit_time_ns) + soc_bb->power_management_parameters.stutter_exit_latency_us = + dc->bb_overrides.sr_exit_time_ns / 1000.0; + + if (dc->bb_overrides.sr_enter_plus_exit_time_ns) + soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us = + dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; + + if (dc->bb_overrides.dram_clock_change_latency_ns) + soc_bb->power_management_parameters.dram_clk_change_blackout_us = + dc->bb_overrides.dram_clock_change_latency_ns / 1000.0; + + if (dc->bb_overrides.fclk_clock_change_latency_ns) + soc_bb->power_management_parameters.fclk_change_blackout_us = + dc->bb_overrides.fclk_clock_change_latency_ns / 1000.0; + + //Z8 values not expected nor used on DCN401 but still added for completeness + if (dc->bb_overrides.sr_exit_z8_time_ns) + soc_bb->power_management_parameters.z8_stutter_exit_latency_us = + dc->bb_overrides.sr_exit_z8_time_ns / 1000.0; + + if (dc->bb_overrides.sr_enter_plus_exit_z8_time_ns) + soc_bb->power_management_parameters.z8_stutter_enter_plus_exit_latency_us = + dc->bb_overrides.sr_enter_plus_exit_z8_time_ns / 1000.0; +} + +static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + /* Individual modification can be overwritten even if it was obtained by a previous function. + * Modifications are acquired in order of priority (lowest to highest). + */ + dc_assert_fp_enabled(); + + dcn401_update_soc_bb_with_values_from_clk_mgr(soc_bb, dc, config); + dcn401_update_soc_bb_with_values_from_vbios(soc_bb, dc); + dcn401_update_soc_bb_with_values_from_software_policy(soc_bb, dc); +} + +void dcn401_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + //get default soc_bb with static values + get_default_soc_bb(soc_bb); + //update soc_bb values with more accurate values + apply_soc_bb_updates(soc_bb, dc, config); +} + +static void dcn401_get_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + *ip_caps = dml2_dcn401_max_ip_caps; +} + +static struct soc_and_ip_translator_funcs dcn401_translator_funcs = { + .get_soc_bb = dcn401_get_soc_bb, + .get_ip_caps = dcn401_get_ip_caps, +}; + +void dcn401_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator) +{ + soc_and_ip_translator->translator_funcs = &dcn401_translator_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h new file mode 100644 index 000000000000..21d842857601 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef _DCN401_SOC_AND_IP_TRANSLATOR_H_ +#define _DCN401_SOC_AND_IP_TRANSLATOR_H_ + +#include "core_types.h" +#include "dc.h" +#include "clk_mgr.h" +#include "soc_and_ip_translator.h" +#include "dml2/dml21/inc/dml_top_soc_parameter_types.h" + +void dcn401_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator); + +/* Functions that can be re-used by higher DCN revisions of this component */ +void dcn401_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); +void dcn401_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); +void dcn401_update_soc_bb_with_values_from_vbios(struct dml2_soc_bb *soc_bb, const struct dc *dc); +void dcn401_update_soc_bb_with_values_from_software_policy(struct dml2_soc_bb *soc_bb, const struct dc *dc); + +#endif /* _DCN401_SOC_AND_IP_TRANSLATOR_H_ */ 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 new file mode 100644 index 000000000000..c9e224d262c9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dcn42_soc_and_ip_translator.h" +#include "soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h" +#include "bounding_boxes/dcn42_soc_bb.h" + +/* soc_and_ip_translator component used to get up-to-date values for bounding box. + * Bounding box values are stored in several locations and locations can vary with DCN revision. + * This component provides an interface to get DCN-specific bounding box values. + */ + +static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + *ip_caps = dml2_dcn42_max_ip_caps; +} + +static struct soc_and_ip_translator_funcs dcn42_translator_funcs = { + .get_soc_bb = dcn401_get_soc_bb, + .get_ip_caps = dcn42_get_ip_caps, +}; + +void dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator) +{ + soc_and_ip_translator->translator_funcs = &dcn42_translator_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h new file mode 100644 index 000000000000..914dcbb369a7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef _DCN42_SOC_AND_IP_TRANSLATOR_H_ +#define _DCN42_SOC_AND_IP_TRANSLATOR_H_ + +#include "core_types.h" +#include "dc.h" +#include "clk_mgr.h" +#include "dml_top_soc_parameter_types.h" +#include "soc_and_ip_translator.h" + +void dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator); + +#endif /* _DCN42_SOC_AND_IP_TRANSLATOR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c new file mode 100644 index 000000000000..0fc0e5a6c171 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "soc_and_ip_translator.h" +#include "soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h" + +static void dc_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator, + enum dce_version dc_version) +{ + switch (dc_version) { + case DCN_VERSION_4_01: + dcn401_construct_soc_and_ip_translator(soc_and_ip_translator); + break; + default: + break; + } +} + +struct soc_and_ip_translator *dc_create_soc_and_ip_translator(enum dce_version dc_version) +{ + struct soc_and_ip_translator *soc_and_ip_translator; + + soc_and_ip_translator = kzalloc(sizeof(*soc_and_ip_translator), GFP_KERNEL); + if (!soc_and_ip_translator) + return NULL; + + dc_construct_soc_and_ip_translator(soc_and_ip_translator, dc_version); + + return soc_and_ip_translator; +} + +void dc_destroy_soc_and_ip_translator(struct soc_and_ip_translator **soc_and_ip_translator) +{ + kfree(*soc_and_ip_translator); + *soc_and_ip_translator = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c index ad088d70e189..6ffc74fc9dcd 100644 --- a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c @@ -44,6 +44,11 @@ static void virtual_stream_encoder_dvi_set_stream_attribute( struct dc_crtc_timing *crtc_timing, bool is_dual_link) {} +static void virtual_stream_encoder_lvds_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing) +{} + static void virtual_stream_encoder_set_throttled_vcp_size( struct stream_encoder *enc, struct fixed31_32 avg_time_slots_per_mtp) @@ -115,6 +120,8 @@ static const struct stream_encoder_funcs virtual_str_enc_funcs = { virtual_stream_encoder_hdmi_set_stream_attribute, .dvi_set_stream_attribute = virtual_stream_encoder_dvi_set_stream_attribute, + .lvds_set_stream_attribute = + virtual_stream_encoder_lvds_set_stream_attribute, .set_throttled_vcp_size = virtual_stream_encoder_set_throttled_vcp_size, .update_hdmi_info_packets = diff --git a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h index 0bafb6710761..338fdc651f2c 100644 --- a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h @@ -316,6 +316,7 @@ struct dmub_srv_hw_params { bool disable_sldo_opt; bool enable_non_transparent_setconfig; bool lower_hbr3_phy_ssc; + bool override_hbr3_pll_vco; }; /** @@ -567,6 +568,7 @@ struct dmub_srv { bool sw_init; bool hw_init; + bool dpia_supported; uint64_t fb_base; uint64_t fb_offset; @@ -597,6 +599,8 @@ struct dmub_notification { enum dmub_notification_type type; uint8_t link_index; uint8_t result; + /* notify instance from DMUB */ + uint8_t instance; bool pending_notification; union { struct aux_reply_data aux_reply; diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h index c587b3441e07..e65747f7f12f 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h @@ -843,7 +843,8 @@ union dmub_fw_boot_options { uint32_t ips_sequential_ono: 1; /**< 1 to enable sequential ONO IPS sequence */ uint32_t disable_sldo_opt: 1; /**< 1 to disable SLDO optimizations */ uint32_t lower_hbr3_phy_ssc: 1; /**< 1 to lower hbr3 phy ssc to 0.125 percent */ - uint32_t reserved : 6; /**< reserved */ + uint32_t override_hbr3_pll_vco: 1; /**< 1 to override the hbr3 pll vco to 0 */ + uint32_t reserved : 5; /**< reserved */ } bits; /**< boot bits */ uint32_t all; /**< 32-bit access to bits */ }; @@ -882,7 +883,7 @@ enum dmub_shared_state_feature_id { /** * struct dmub_shared_state_ips_fw - Firmware signals for IPS. */ - union dmub_shared_state_ips_fw_signals { +union dmub_shared_state_ips_fw_signals { struct { uint32_t ips1_commit : 1; /**< 1 if in IPS1 or IPS0 RCG */ uint32_t ips2_commit : 1; /**< 1 if in IPS2 */ @@ -897,7 +898,7 @@ enum dmub_shared_state_feature_id { /** * struct dmub_shared_state_ips_signals - Firmware signals for IPS. */ - union dmub_shared_state_ips_driver_signals { +union dmub_shared_state_ips_driver_signals { struct { uint32_t allow_pg : 1; /**< 1 if PG is allowed */ uint32_t allow_ips1 : 1; /**< 1 is IPS1 is allowed */ @@ -1990,18 +1991,19 @@ struct dmub_cmd_lsdma_data { struct lsdma_tiled_copy_data { uint32_t src_addr_lo; uint32_t src_addr_hi; + uint32_t dst_addr_lo; uint32_t dst_addr_hi; uint32_t src_x : 16; uint32_t src_y : 16; - uint32_t src_width : 16; - uint32_t src_height : 16; - uint32_t dst_x : 16; uint32_t dst_y : 16; + uint32_t src_width : 16; + uint32_t src_height : 16; + uint32_t dst_width : 16; uint32_t dst_height : 16; @@ -2034,41 +2036,58 @@ struct dmub_cmd_lsdma_data { uint32_t padding : 30; } tiled_copy_data; struct lsdma_linear_copy_data { + uint32_t src_lo; + uint32_t src_hi; + + uint32_t dst_lo; + uint32_t dst_hi; + uint32_t count : 30; uint32_t cache_policy_dst : 2; uint32_t tmz : 1; uint32_t cache_policy_src : 2; uint32_t padding : 29; - + } linear_copy_data; + struct lsdma_linear_sub_window_copy_data { uint32_t src_lo; uint32_t src_hi; + uint32_t dst_lo; uint32_t dst_hi; - } linear_copy_data; + + uint32_t src_x : 16; + uint32_t src_y : 16; + + uint32_t dst_x : 16; + uint32_t dst_y : 16; + + uint32_t rect_x : 16; + uint32_t rect_y : 16; + + uint32_t src_pitch : 16; + uint32_t dst_pitch : 16; + + uint32_t src_slice_pitch; + uint32_t dst_slice_pitch; + + uint32_t tmz : 1; + uint32_t element_size : 3; + uint32_t src_cache_policy : 3; + uint32_t dst_cache_policy : 3; + uint32_t reserved0 : 22; + } linear_sub_window_copy_data; struct lsdma_reg_write_data { uint32_t reg_addr; uint32_t reg_data; } reg_write_data; struct lsdma_pio_copy_data { - union { - struct { - uint32_t byte_count : 26; - uint32_t src_loc : 1; - uint32_t dst_loc : 1; - uint32_t src_addr_inc : 1; - uint32_t dst_addr_inc : 1; - uint32_t overlap_disable : 1; - uint32_t constant_fill : 1; - } fields; - uint32_t raw; - } packet; uint32_t src_lo; uint32_t src_hi; + uint32_t dst_lo; uint32_t dst_hi; - } pio_copy_data; - struct lsdma_pio_constfill_data { + union { struct { uint32_t byte_count : 26; @@ -2081,14 +2100,29 @@ struct dmub_cmd_lsdma_data { } fields; uint32_t raw; } packet; + } pio_copy_data; + struct lsdma_pio_constfill_data { uint32_t dst_lo; uint32_t dst_hi; + + union { + struct { + uint32_t byte_count : 26; + uint32_t src_loc : 1; + uint32_t dst_loc : 1; + uint32_t src_addr_inc : 1; + uint32_t dst_addr_inc : 1; + uint32_t overlap_disable : 1; + uint32_t constant_fill : 1; + } fields; + uint32_t raw; + } packet; + uint32_t data; } pio_constfill_data; uint32_t all[14]; } u; - }; struct dmub_rb_cmd_lsdma { @@ -2330,6 +2364,7 @@ struct dmub_cmd_fams2_global_config { union dmub_fams2_global_feature_config features; uint32_t recovery_timeout_us; uint32_t hwfq_flip_programming_delay_us; + uint32_t max_allow_to_target_delta_us; // how early DCN could assert P-State allow compared to the P-State target }; union dmub_cmd_fams2_config { @@ -3985,6 +4020,10 @@ enum dmub_cmd_replay_type { * Set adaptive sync sdp enabled */ DMUB_CMD__REPLAY_DISABLED_ADAPTIVE_SYNC_SDP = 8, + /** + * Set version + */ + DMUB_CMD__REPLAY_SET_VERSION = 9, /** * Set Replay General command. */ @@ -4015,6 +4054,10 @@ struct dmub_alpm_auxless_data { uint16_t lfps_t1_t2_override_us; short lfps_t1_t2_offset_us; uint8_t lttpr_count; + /* + * Padding to align structure to 4 byte boundary. + */ + uint8_t pad[1]; }; /** @@ -4047,14 +4090,6 @@ struct dmub_cmd_replay_copy_settings_data { * DIG BE HW instance. */ uint8_t digbe_inst; - /** - * @hpo_stream_enc_inst: HPO stream encoder instance - */ - uint8_t hpo_stream_enc_inst; - /** - * @hpo_link_enc_inst: HPO link encoder instance - */ - uint8_t hpo_link_enc_inst; /** * AUX HW instance. */ @@ -4099,13 +4134,77 @@ struct dmub_cmd_replay_copy_settings_data { * Use for AUX-less ALPM LFPS wake operation */ struct dmub_alpm_auxless_data auxless_alpm_data; - + /** + * @hpo_stream_enc_inst: HPO stream encoder instance + */ + uint8_t hpo_stream_enc_inst; + /** + * @hpo_link_enc_inst: HPO link encoder instance + */ + uint8_t hpo_link_enc_inst; /** * @pad: Align structure to 4 byte boundary. */ uint8_t pad[2]; }; + +/** + * Replay versions. + */ +enum replay_version { + /** + * FreeSync Replay + */ + REPLAY_VERSION_FREESYNC_REPLAY = 0, + /** + * Panel Replay + */ + REPLAY_VERSION_PANEL_REPLAY = 1, + /** + * Replay not supported. + */ + REPLAY_VERSION_UNSUPPORTED = 0xFF, +}; + +/** + * Data passed from driver to FW in a DMUB_CMD___SET_REPLAY_VERSION command. + */ +struct dmub_cmd_replay_set_version_data { + /** + * Panel Instance. + * Panel instance to identify which psr_state to use + * Currently the support is only for 0 or 1 + */ + uint8_t panel_inst; + /** + * PSR version that FW should implement. + */ + enum replay_version version; + /** + * PSR control version. + */ + uint8_t cmd_version; + /** + * Explicit padding to 4 byte boundary. + */ + uint8_t pad[2]; +}; + +/** + * Definition of a DMUB_CMD__REPLAY_SET_VERSION command. + */ +struct dmub_rb_cmd_replay_set_version { + /** + * Command header. + */ + struct dmub_cmd_header header; + /** + * Data passed from driver to FW in a DMUB_CMD__REPLAY_SET_VERSION command. + */ + struct dmub_cmd_replay_set_version_data replay_set_version_data; +}; + /** * Definition of a DMUB_CMD__REPLAY_COPY_SETTINGS command. */ @@ -4159,18 +4258,6 @@ struct dmub_rb_cmd_replay_enable_data { * This does not support HDMI/DP2 for now. */ uint8_t phy_rate; - /** - * @hpo_stream_enc_inst: HPO stream encoder instance - */ - uint8_t hpo_stream_enc_inst; - /** - * @hpo_link_enc_inst: HPO link encoder instance - */ - uint8_t hpo_link_enc_inst; - /** - * @pad: Align structure to 4 byte boundary. - */ - uint8_t pad[2]; }; /** @@ -4469,6 +4556,10 @@ union dmub_replay_cmd_set { * Definition of DMUB_CMD__REPLAY_DISABLED_ADAPTIVE_SYNC_SDP command data. */ struct dmub_cmd_replay_disabled_adaptive_sync_sdp_data disabled_adaptive_sync_sdp_data; + /** + * Definition of DMUB_CMD__REPLAY_SET_VERSION command data. + */ + struct dmub_cmd_replay_set_version_data version_data; /** * Definition of DMUB_CMD__REPLAY_SET_GENERAL_CMD command data. */ @@ -4684,22 +4775,26 @@ enum dmub_cmd_lsdma_type { * LSDMA copies data from source to destination linearly */ DMUB_CMD__LSDMA_LINEAR_COPY = 1, + /** + * LSDMA copies data from source to destination linearly in sub window + */ + DMUB_CMD__LSDMA_LINEAR_SUB_WINDOW_COPY = 2, /** * Send the tiled-to-tiled copy command */ - DMUB_CMD__LSDMA_TILED_TO_TILED_COPY = 2, + DMUB_CMD__LSDMA_TILED_TO_TILED_COPY = 3, /** * Send the poll reg write command */ - DMUB_CMD__LSDMA_POLL_REG_WRITE = 3, + DMUB_CMD__LSDMA_POLL_REG_WRITE = 4, /** * Send the pio copy command */ - DMUB_CMD__LSDMA_PIO_COPY = 4, + DMUB_CMD__LSDMA_PIO_COPY = 5, /** * Send the pio constfill command */ - DMUB_CMD__LSDMA_PIO_CONSTFILL = 5, + DMUB_CMD__LSDMA_PIO_CONSTFILL = 6, }; struct abm_ace_curve { @@ -5914,6 +6009,9 @@ enum ips_residency_mode { IPS_RESIDENCY__IPS2, IPS_RESIDENCY__IPS1_RCG, IPS_RESIDENCY__IPS1_ONO2_ON, + IPS_RESIDENCY__IPS1_Z8_RETENTION, + IPS_RESIDENCY__PG_ONO_LAST_SEEN_IN_IPS, + IPS_RESIDENCY__PG_ONO_CURRENT_STATE }; #define NUM_IPS_HISTOGRAM_BUCKETS 16 @@ -5927,6 +6025,8 @@ struct dmub_ips_residency_info { uint32_t histogram[NUM_IPS_HISTOGRAM_BUCKETS]; uint64_t total_time_us; uint64_t total_inactive_time_us; + uint32_t ono_pg_state_at_collection; + uint32_t ono_pg_state_last_seen_in_ips; }; /** @@ -6223,6 +6323,10 @@ union dmub_rb_cmd { * Definition of a DMUB_CMD__IDLE_OPT_SET_DC_POWER_STATE command. */ struct dmub_rb_cmd_idle_opt_set_dc_power_state idle_opt_set_dc_power_state; + /** + * Definition of a DMUB_CMD__REPLAY_SET_VERSION command. + */ + struct dmub_rb_cmd_replay_set_version replay_set_version; /* * Definition of a DMUB_CMD__REPLAY_COPY_SETTINGS command. */ @@ -6431,15 +6535,18 @@ static inline bool dmub_rb_full(struct dmub_rb *rb) static inline bool dmub_rb_push_front(struct dmub_rb *rb, const union dmub_rb_cmd *cmd) { - uint64_t volatile *dst = (uint64_t volatile *)((uint8_t *)(rb->base_address) + rb->wrpt); - const uint64_t *src = (const uint64_t *)cmd; + uint8_t *dst = (uint8_t *)(rb->base_address) + rb->wrpt; + const uint8_t *src = (const uint8_t *)cmd; uint8_t i; + if (rb->capacity == 0) + return false; + if (dmub_rb_full(rb)) return false; // copying data - for (i = 0; i < DMUB_RB_CMD_SIZE / sizeof(uint64_t); i++) + for (i = 0; i < DMUB_RB_CMD_SIZE; i++) *dst++ = *src++; rb->wrpt += DMUB_RB_CMD_SIZE; @@ -6464,6 +6571,9 @@ static inline bool dmub_rb_out_push_front(struct dmub_rb *rb, uint8_t *dst = (uint8_t *)(rb->base_address) + rb->wrpt; const uint8_t *src = (const uint8_t *)cmd; + if (rb->capacity == 0) + return false; + if (dmub_rb_full(rb)) return false; @@ -6509,6 +6619,9 @@ static inline void dmub_rb_get_rptr_with_offset(struct dmub_rb *rb, uint32_t num_cmds, uint32_t *next_rptr) { + if (rb->capacity == 0) + return; + *next_rptr = rb->rptr + DMUB_RB_CMD_SIZE * num_cmds; if (*next_rptr >= rb->capacity) @@ -6572,6 +6685,9 @@ static inline bool dmub_rb_out_front(struct dmub_rb *rb, */ static inline bool dmub_rb_pop_front(struct dmub_rb *rb) { + if (rb->capacity == 0) + return false; + if (dmub_rb_empty(rb)) return false; @@ -6596,6 +6712,9 @@ static inline void dmub_rb_flush_pending(const struct dmub_rb *rb) uint32_t rptr = rb->rptr; uint32_t wptr = rb->wrpt; + if (rb->capacity == 0) + return; + while (rptr != wptr) { uint64_t *data = (uint64_t *)((uint8_t *)(rb->base_address) + rptr); uint8_t i; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c index 3f38db752b84..4777c7203b2c 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c @@ -377,6 +377,7 @@ void dmub_dcn31_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmu boot_options.bits.dpia_hpd_int_enable_supported = params->dpia_hpd_int_enable_supported; boot_options.bits.power_optimization = params->power_optimization; boot_options.bits.lower_hbr3_phy_ssc = params->lower_hbr3_phy_ssc; + boot_options.bits.override_hbr3_pll_vco = params->override_hbr3_pll_vco; boot_options.bits.sel_mux_phy_c_d_phy_f_g = (dmub->asic == DMUB_ASIC_DCN31B) ? 1 : 0; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c index 2228d62adc7e..834e5434ccb8 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c @@ -400,13 +400,14 @@ union dmub_fw_boot_options dmub_dcn35_get_fw_boot_option(struct dmub_srv *dmub) void dmub_dcn35_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmub_srv_hw_params *params) { union dmub_fw_boot_options boot_options = {0}; - union dmub_fw_boot_options cur_boot_options = {0}; - cur_boot_options = dmub_dcn35_get_fw_boot_option(dmub); + if (!dmub->dpia_supported) { + dmub->dpia_supported = dmub_dcn35_get_fw_boot_option(dmub).bits.enable_dpia; + } boot_options.bits.z10_disable = params->disable_z10; boot_options.bits.dpia_supported = params->dpia_supported; - boot_options.bits.enable_dpia = cur_boot_options.bits.enable_dpia && !params->disable_dpia; + boot_options.bits.enable_dpia = dmub->dpia_supported && !params->disable_dpia; boot_options.bits.usb4_cm_version = params->usb4_cm_version; boot_options.bits.dpia_hpd_int_enable_supported = params->dpia_hpd_int_enable_supported; boot_options.bits.power_optimization = params->power_optimization; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv_stat.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv_stat.c index 567c5b1aeb7a..e7a58b140388 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv_stat.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv_stat.c @@ -71,7 +71,7 @@ enum dmub_status dmub_srv_stat_get_notification(struct dmub_srv *dmub, switch (cmd.cmd_common.header.type) { case DMUB_OUT_CMD__DP_AUX_REPLY: notify->type = DMUB_NOTIFICATION_AUX_REPLY; - notify->link_index = cmd.dp_aux_reply.control.instance; + notify->instance = cmd.dp_aux_reply.control.instance; notify->result = cmd.dp_aux_reply.control.result; dmub_memcpy((void *)¬ify->aux_reply, (void *)&cmd.dp_aux_reply.reply_data, sizeof(struct aux_reply_data)); @@ -84,17 +84,17 @@ enum dmub_status dmub_srv_stat_get_notification(struct dmub_srv *dmub, notify->type = DMUB_NOTIFICATION_HPD_IRQ; } - notify->link_index = cmd.dp_hpd_notify.hpd_data.instance; + notify->instance = cmd.dp_hpd_notify.hpd_data.instance; notify->result = AUX_RET_SUCCESS; break; case DMUB_OUT_CMD__SET_CONFIG_REPLY: notify->type = DMUB_NOTIFICATION_SET_CONFIG_REPLY; - notify->link_index = cmd.set_config_reply.set_config_reply_control.instance; + notify->instance = cmd.set_config_reply.set_config_reply_control.instance; notify->sc_status = cmd.set_config_reply.set_config_reply_control.status; break; case DMUB_OUT_CMD__DPIA_NOTIFICATION: notify->type = DMUB_NOTIFICATION_DPIA_NOTIFICATION; - notify->link_index = cmd.dpia_notification.payload.header.instance; + notify->instance = cmd.dpia_notification.payload.header.instance; break; case DMUB_OUT_CMD__HPD_SENSE_NOTIFY: notify->type = DMUB_NOTIFICATION_HPD_SENSE_NOTIFY; diff --git a/drivers/gpu/drm/amd/display/include/dal_asic_id.h b/drivers/gpu/drm/amd/display/include/dal_asic_id.h index 5fc29164e4b4..8aea50aa9533 100644 --- a/drivers/gpu/drm/amd/display/include/dal_asic_id.h +++ b/drivers/gpu/drm/amd/display/include/dal_asic_id.h @@ -213,6 +213,11 @@ enum { #endif #define DEVICE_ID_NV_13FE 0x13FE // CYAN_SKILLFISH #define DEVICE_ID_NV_143F 0x143F +#define DEVICE_ID_NV_13F9 0x13F9 +#define DEVICE_ID_NV_13FA 0x13FA +#define DEVICE_ID_NV_13FB 0x13FB +#define DEVICE_ID_NV_13FC 0x13FC +#define DEVICE_ID_NV_13DB 0x13DB #define FAMILY_VGH 144 #define DEVICE_ID_VGH_163F 0x163F #define DEVICE_ID_VGH_1435 0x1435 diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 71efd2770c99..ce421bcddcb0 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -226,8 +226,8 @@ static void update_v_total_for_static_ramp( unsigned int target_duration_in_us = calc_duration_in_us_from_refresh_in_uhz( in_out_vrr->fixed.target_refresh_in_uhz); - bool ramp_direction_is_up = (current_duration_in_us > - target_duration_in_us) ? true : false; + bool ramp_direction_is_up = current_duration_in_us > + target_duration_in_us; /* Calculate ratio between new and current frame duration with 3 digit */ unsigned int frame_duration_ratio = div64_u64(1000000, diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c index 5e01c6e24cbc..c760216a6240 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c @@ -29,6 +29,7 @@ static void push_error_status(struct mod_hdcp *hdcp, enum mod_hdcp_status status) { struct mod_hdcp_trace *trace = &hdcp->connection.trace; + const uint8_t retry_limit = hdcp->connection.link.adjust.retry_limit; if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) { trace->errors[trace->error_count].status = status; @@ -39,11 +40,11 @@ static void push_error_status(struct mod_hdcp *hdcp, if (is_hdcp1(hdcp)) { hdcp->connection.hdcp1_retry_count++; - if (hdcp->connection.hdcp1_retry_count == MAX_NUM_OF_ATTEMPTS) + if (hdcp->connection.hdcp1_retry_count == retry_limit) hdcp->connection.link.adjust.hdcp1.disable = 1; } else if (is_hdcp2(hdcp)) { hdcp->connection.hdcp2_retry_count++; - if (hdcp->connection.hdcp2_retry_count == MAX_NUM_OF_ATTEMPTS) + if (hdcp->connection.hdcp2_retry_count == retry_limit) hdcp->connection.link.adjust.hdcp2.disable = 1; } } diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c index e58e7b93810b..6b7db8ec9a53 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c @@ -260,6 +260,9 @@ enum mod_hdcp_status mod_hdcp_hdcp1_create_session(struct mod_hdcp *hdcp) return MOD_HDCP_STATUS_FAILURE; } + if (!display) + return MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.context.mem_context.shared_buf; mutex_lock(&psp->hdcp_context.mutex); diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h index c42468bb70ac..b51ddf2846df 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h @@ -220,6 +220,7 @@ struct mod_hdcp_link_adjustment_hdcp2 { struct mod_hdcp_link_adjustment { uint8_t auth_delay; + uint8_t retry_limit; struct mod_hdcp_link_adjustment_hdcp1 hdcp1; struct mod_hdcp_link_adjustment_hdcp2 hdcp2; }; diff --git a/drivers/gpu/drm/amd/include/atomfirmware.h b/drivers/gpu/drm/amd/include/atomfirmware.h index 5c86423c2e92..3d083010e734 100644 --- a/drivers/gpu/drm/amd/include/atomfirmware.h +++ b/drivers/gpu/drm/amd/include/atomfirmware.h @@ -211,7 +211,7 @@ atom_bios_string = "ATOM" }; */ -#pragma pack(1) /* BIOS data must use byte aligment*/ +#pragma pack(1) /* BIOS data must use byte alignment*/ enum atombios_image_offset{ OFFSET_TO_ATOM_ROM_HEADER_POINTER = 0x00000048, @@ -255,8 +255,8 @@ struct atom_rom_header_v2_2 uint16_t subsystem_vendor_id; uint16_t subsystem_id; uint16_t pci_info_offset; - uint16_t masterhwfunction_offset; //Offest for SW to get all command function offsets, Don't change the position - uint16_t masterdatatable_offset; //Offest for SW to get all data table offsets, Don't change the position + uint16_t masterhwfunction_offset; //Offset for SW to get all command function offsets, Don't change the position + uint16_t masterdatatable_offset; //Offset for SW to get all data table offsets, Don't change the position uint16_t reserved; uint32_t pspdirtableoffset; }; @@ -453,7 +453,7 @@ struct atom_dtd_format uint8_t refreshrate; }; -/* atom_dtd_format.modemiscinfo defintion */ +/* atom_dtd_format.modemiscinfo definition */ enum atom_dtd_format_modemiscinfo{ ATOM_HSYNC_POLARITY = 0x0002, ATOM_VSYNC_POLARITY = 0x0004, @@ -678,7 +678,7 @@ struct lcd_info_v2_1 uint32_t reserved1[8]; }; -/* lcd_info_v2_1.panel_misc defintion */ +/* lcd_info_v2_1.panel_misc definition */ enum atom_lcd_info_panel_misc{ ATOM_PANEL_MISC_FPDI =0x0002, }; @@ -716,7 +716,7 @@ enum atom_gpio_pin_assignment_gpio_id { /* gpio_id pre-define id for multiple usage */ /* GPIO use to control PCIE_VDDC in certain SLT board */ PCIE_VDDC_CONTROL_GPIO_PINID = 56, - /* if PP_AC_DC_SWITCH_GPIO_PINID in Gpio_Pin_LutTable, AC/DC swithing feature is enable */ + /* if PP_AC_DC_SWITCH_GPIO_PINID in Gpio_Pin_LutTable, AC/DC switching feature is enable */ PP_AC_DC_SWITCH_GPIO_PINID = 60, /* VDDC_REGULATOR_VRHOT_GPIO_PINID in Gpio_Pin_LutTable, VRHot feature is enable */ VDDC_VRHOT_GPIO_PINID = 61, @@ -734,7 +734,7 @@ enum atom_gpio_pin_assignment_gpio_id { struct atom_gpio_pin_lut_v2_1 { struct atom_common_table_header table_header; - /*the real number of this included in the structure is calcualted by using the (whole structure size - the header size)/size of atom_gpio_pin_lut */ + /*the real number of this included in the structure is calculated by using the (whole structure size - the header size)/size of atom_gpio_pin_lut */ struct atom_gpio_pin_assignment gpio_pin[]; }; @@ -997,7 +997,7 @@ enum atom_connector_layout_info_mini_type_def { enum atom_display_device_tag_def{ ATOM_DISPLAY_LCD1_SUPPORT = 0x0002, //an embedded display is either an LVDS or eDP signal type of display - ATOM_DISPLAY_LCD2_SUPPORT = 0x0020, //second edp device tag 0x0020 for backward compability + ATOM_DISPLAY_LCD2_SUPPORT = 0x0020, //second edp device tag 0x0020 for backward compatibility ATOM_DISPLAY_DFP1_SUPPORT = 0x0008, ATOM_DISPLAY_DFP2_SUPPORT = 0x0080, ATOM_DISPLAY_DFP3_SUPPORT = 0x0200, @@ -1011,7 +1011,7 @@ struct atom_display_object_path_v2 { uint16_t display_objid; //Connector Object ID or Misc Object ID uint16_t disp_recordoffset; - uint16_t encoderobjid; //first encoder closer to the connector, could be either an external or intenal encoder + uint16_t encoderobjid; //first encoder closer to the connector, could be either an external or internal encoder uint16_t extencoderobjid; //2nd encoder after the first encoder, from the connector point of view; uint16_t encoder_recordoffset; uint16_t extencoder_recordoffset; @@ -1023,7 +1023,7 @@ struct atom_display_object_path_v2 struct atom_display_object_path_v3 { uint16_t display_objid; //Connector Object ID or Misc Object ID uint16_t disp_recordoffset; - uint16_t encoderobjid; //first encoder closer to the connector, could be either an external or intenal encoder + uint16_t encoderobjid; //first encoder closer to the connector, could be either an external or internal encoder uint16_t reserved1; //only on USBC case, otherwise always = 0 uint16_t reserved2; //reserved and always = 0 uint16_t reserved3; //reserved and always = 0 @@ -3547,7 +3547,7 @@ struct atom_voltage_object_header_v4{ enum atom_voltage_object_mode { VOLTAGE_OBJ_GPIO_LUT = 0, //VOLTAGE and GPIO Lookup table ->atom_gpio_voltage_object_v4 - VOLTAGE_OBJ_VR_I2C_INIT_SEQ = 3, //VOLTAGE REGULATOR INIT sequece through I2C -> atom_i2c_voltage_object_v4 + VOLTAGE_OBJ_VR_I2C_INIT_SEQ = 3, //VOLTAGE REGULATOR INIT sequence through I2C -> atom_i2c_voltage_object_v4 VOLTAGE_OBJ_PHASE_LUT = 4, //Set Vregulator Phase lookup table ->atom_gpio_voltage_object_v4 VOLTAGE_OBJ_SVID2 = 7, //Indicate voltage control by SVID2 ->atom_svid2_voltage_object_v4 VOLTAGE_OBJ_EVV = 8, @@ -3585,7 +3585,7 @@ struct atom_gpio_voltage_object_v4 { struct atom_voltage_object_header_v4 header; // voltage mode = VOLTAGE_OBJ_GPIO_LUT or VOLTAGE_OBJ_PHASE_LUT uint8_t gpio_control_id; // default is 0 which indicate control through CG VID mode - uint8_t gpio_entry_num; // indiate the entry numbers of Votlage/Gpio value Look up table + uint8_t gpio_entry_num; // indicate the entry numbers of Votlage/Gpio value Look up table uint8_t phase_delay_us; // phase delay in unit of micro second uint8_t reserved; uint32_t gpio_mask_val; // GPIO Mask value @@ -4507,8 +4507,8 @@ struct amd_acpi_description_header{ struct uefi_acpi_vfct{ struct amd_acpi_description_header sheader; uint8_t tableUUID[16]; //0x24 - uint32_t vbiosimageoffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. - uint32_t lib1Imageoffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. + uint32_t vbiosimageoffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the structure. + uint32_t lib1Imageoffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the structure. uint32_t reserved[4]; //0x3C }; @@ -4540,7 +4540,7 @@ struct gop_lib1_content { /* *************************************************************************** Scratch Register definitions - Each number below indicates which scratch regiser request, Active and + Each number below indicates which scratch register request, Active and Connect all share the same definitions as display_device_tag defines *************************************************************************** */ diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index e2b1ea7467b0..2f7e4b5bebf3 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -30,6 +30,12 @@ extern const struct amdgpu_ip_block_version smu_v12_0_ip_block; extern const struct amdgpu_ip_block_version smu_v13_0_ip_block; extern const struct amdgpu_ip_block_version smu_v14_0_ip_block; +enum smu_temp_metric_type { + SMU_TEMP_METRIC_BASEBOARD, + SMU_TEMP_METRIC_GPUBOARD, + SMU_TEMP_METRIC_MAX, +}; + enum smu_event_type { SMU_EVENT_RESET_COMPLETE = 0, }; @@ -496,6 +502,8 @@ struct amd_pm_funcs { int (*set_df_cstate)(void *handle, enum pp_df_cstate state); int (*set_xgmi_pstate)(void *handle, uint32_t pstate); ssize_t (*get_gpu_metrics)(void *handle, void **table); + ssize_t (*get_temp_metrics)(void *handle, enum smu_temp_metric_type type, void *table); + bool (*temp_metrics_is_supported)(void *handle, enum smu_temp_metric_type type); ssize_t (*get_xcp_metrics)(void *handle, int xcp_id, void *table); ssize_t (*get_pm_metrics)(void *handle, void *pmmetrics, size_t size); int (*set_watermarks_for_clock_ranges)(void *handle, @@ -1595,6 +1603,79 @@ struct amdgpu_pm_metrics { uint8_t data[]; }; +enum amdgpu_vr_temp { + AMDGPU_VDDCR_VDD0_TEMP, + AMDGPU_VDDCR_VDD1_TEMP, + AMDGPU_VDDCR_VDD2_TEMP, + AMDGPU_VDDCR_VDD3_TEMP, + AMDGPU_VDDCR_SOC_A_TEMP, + AMDGPU_VDDCR_SOC_C_TEMP, + AMDGPU_VDDCR_SOCIO_A_TEMP, + AMDGPU_VDDCR_SOCIO_C_TEMP, + AMDGPU_VDD_085_HBM_TEMP, + AMDGPU_VDDCR_11_HBM_B_TEMP, + AMDGPU_VDDCR_11_HBM_D_TEMP, + AMDGPU_VDD_USR_TEMP, + AMDGPU_VDDIO_11_E32_TEMP, + AMDGPU_VR_MAX_TEMP_ENTRIES, +}; + +enum amdgpu_system_temp { + AMDGPU_UBB_FPGA_TEMP, + AMDGPU_UBB_FRONT_TEMP, + AMDGPU_UBB_BACK_TEMP, + AMDGPU_UBB_OAM7_TEMP, + AMDGPU_UBB_IBC_TEMP, + AMDGPU_UBB_UFPGA_TEMP, + AMDGPU_UBB_OAM1_TEMP, + AMDGPU_OAM_0_1_HSC_TEMP, + AMDGPU_OAM_2_3_HSC_TEMP, + AMDGPU_OAM_4_5_HSC_TEMP, + AMDGPU_OAM_6_7_HSC_TEMP, + AMDGPU_UBB_FPGA_0V72_VR_TEMP, + AMDGPU_UBB_FPGA_3V3_VR_TEMP, + AMDGPU_RETIMER_0_1_2_3_1V2_VR_TEMP, + AMDGPU_RETIMER_4_5_6_7_1V2_VR_TEMP, + AMDGPU_RETIMER_0_1_0V9_VR_TEMP, + AMDGPU_RETIMER_4_5_0V9_VR_TEMP, + AMDGPU_RETIMER_2_3_0V9_VR_TEMP, + AMDGPU_RETIMER_6_7_0V9_VR_TEMP, + AMDGPU_OAM_0_1_2_3_3V3_VR_TEMP, + AMDGPU_OAM_4_5_6_7_3V3_VR_TEMP, + AMDGPU_IBC_HSC_TEMP, + AMDGPU_IBC_TEMP, + AMDGPU_SYSTEM_MAX_TEMP_ENTRIES = 32, +}; + +enum amdgpu_node_temp { + AMDGPU_RETIMER_X_TEMP, + AMDGPU_OAM_X_IBC_TEMP, + AMDGPU_OAM_X_IBC_2_TEMP, + AMDGPU_OAM_X_VDD18_VR_TEMP, + AMDGPU_OAM_X_04_HBM_B_VR_TEMP, + AMDGPU_OAM_X_04_HBM_D_VR_TEMP, + AMDGPU_NODE_MAX_TEMP_ENTRIES = 12, +}; + +struct amdgpu_gpuboard_temp_metrics_v1_0 { + struct metrics_table_header common_header; + uint16_t label_version; + uint16_t node_id; + uint64_t accumulation_counter; + /* Encoded temperature in Celcius, 24:31 is sensor id 0:23 is temp value */ + uint32_t node_temp[AMDGPU_NODE_MAX_TEMP_ENTRIES]; + uint32_t vr_temp[AMDGPU_VR_MAX_TEMP_ENTRIES]; +}; + +struct amdgpu_baseboard_temp_metrics_v1_0 { + struct metrics_table_header common_header; + uint16_t label_version; + uint16_t node_id; + uint64_t accumulation_counter; + /* Encoded temperature in Celcius, 24:31 is sensor id 0:23 is temp value */ + uint32_t system_temp[AMDGPU_SYSTEM_MAX_TEMP_ENTRIES]; +}; + struct amdgpu_partition_metrics_v1_0 { struct metrics_table_header common_header; /* Current clocks (Mhz) */ diff --git a/drivers/gpu/drm/amd/include/mes_v12_api_def.h b/drivers/gpu/drm/amd/include/mes_v12_api_def.h index d85ffab2aff9..c04bd351b250 100644 --- a/drivers/gpu/drm/amd/include/mes_v12_api_def.h +++ b/drivers/gpu/drm/amd/include/mes_v12_api_def.h @@ -66,6 +66,7 @@ enum MES_SCH_API_OPCODE { MES_SCH_API_SET_SE_MODE = 17, MES_SCH_API_SET_GANG_SUBMIT = 18, MES_SCH_API_SET_HW_RSRC_1 = 19, + MES_SCH_API_INV_TLBS = 20, MES_SCH_API_MAX = 0xFF }; @@ -870,6 +871,35 @@ union MESAPI__SET_GANG_SUBMIT { uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; }; +/* + * @inv_sel 0-select pasid as input to do the invalidation , 1-select vmid + * @flush_type 0-old style, 1-light weight, 2-heavyweight, 3-heavyweight2 + * @inv_sel_id specific pasid when inv_sel is 0 and specific vmid if inv_sel is 1 + * @hub_id 0-gc_hub, 1-mm_hub + */ +struct INV_TLBS { + uint8_t inv_sel; + uint8_t flush_type; + uint16_t inv_sel_id; + uint32_t hub_id; + /* If following two inv_range setting are all 0 , whole VM will be invalidated, + * otherwise only required range be invalidated + */ + uint64_t inv_range_va_start; + uint64_t inv_range_size; + uint64_t reserved; +}; + +union MESAPI__INV_TLBS { + struct { + union MES_API_HEADER header; + struct MES_API_STATUS api_status; + struct INV_TLBS invalidate_tlbs; + }; + + uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; +}; + #pragma pack(pop) #endif diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 71d986dd7a6e..518d07afc7df 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -764,10 +764,6 @@ int amdgpu_dpm_send_rma_reason(struct amdgpu_device *adev) ret = smu_send_rma_reason(smu); mutex_unlock(&adev->pm.mutex); - if (adev->cper.enabled) - if (amdgpu_cper_generate_bp_threshold_record(adev)) - dev_warn(adev->dev, "fail to generate bad page threshold cper records\n"); - return ret; } @@ -824,6 +820,21 @@ int amdgpu_dpm_reset_vcn(struct amdgpu_device *adev, uint32_t inst_mask) return ret; } +bool amdgpu_dpm_reset_vcn_is_supported(struct amdgpu_device *adev) +{ + struct smu_context *smu = adev->powerplay.pp_handle; + bool ret; + + if (!is_support_sw_smu(adev)) + return false; + + mutex_lock(&adev->pm.mutex); + ret = smu_reset_vcn_is_supported(smu); + mutex_unlock(&adev->pm.mutex); + + return ret; +} + int amdgpu_dpm_get_dpm_freq_range(struct amdgpu_device *adev, enum pp_clock_type type, uint32_t *min, @@ -2037,6 +2048,66 @@ int amdgpu_dpm_get_dpm_clock_table(struct amdgpu_device *adev, return ret; } +/** + * amdgpu_dpm_get_temp_metrics - Retrieve metrics for a specific compute + * partition + * @adev: Pointer to the device. + * @type: Identifier for the temperature type metrics to be fetched. + * @table: Pointer to a buffer where the metrics will be stored. If NULL, the + * function returns the size of the metrics structure. + * + * This function retrieves metrics for a specific temperature type, If the + * table parameter is NULL, the function returns the size of the metrics + * structure without populating it. + * + * Return: Size of the metrics structure on success, or a negative error code on failure. + */ +ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, + enum smu_temp_metric_type type, void *table) +{ + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + int ret; + + if (!pp_funcs->get_temp_metrics || + !amdgpu_dpm_is_temp_metrics_supported(adev, type)) + return -EOPNOTSUPP; + + mutex_lock(&adev->pm.mutex); + ret = pp_funcs->get_temp_metrics(adev->powerplay.pp_handle, type, table); + mutex_unlock(&adev->pm.mutex); + + return ret; +} + +/** + * amdgpu_dpm_is_temp_metrics_supported - Return if specific temperature metrics support + * is available + * @adev: Pointer to the device. + * @type: Identifier for the temperature type metrics to be fetched. + * + * This function returns metrics if specific temperature metrics type is supported or not. + * + * Return: True in case of metrics type supported else false. + */ +bool amdgpu_dpm_is_temp_metrics_supported(struct amdgpu_device *adev, + enum smu_temp_metric_type type) +{ + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + bool support_temp_metrics = false; + + if (!pp_funcs->temp_metrics_is_supported) + return support_temp_metrics; + + if (is_support_sw_smu(adev)) { + mutex_lock(&adev->pm.mutex); + support_temp_metrics = + pp_funcs->temp_metrics_is_supported(adev->powerplay.pp_handle, type); + mutex_unlock(&adev->pm.mutex); + } + + return support_temp_metrics; +} + /** * amdgpu_dpm_get_xcp_metrics - Retrieve metrics for a specific compute * partition diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm_internal.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm_internal.c index 42efe838fa85..2d2d2d5e6763 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm_internal.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm_internal.c @@ -66,6 +66,13 @@ u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev) (amdgpu_crtc->v_border * 2)); vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock; + + /* we have issues with mclk switching with + * refresh rates over 120 hz on the non-DC code. + */ + if (drm_mode_vrefresh(&amdgpu_crtc->hw_mode) > 120) + vblank_time_us = 0; + break; } } diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 4b64851fdb42..96590c1da553 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -110,9 +110,10 @@ static int amdgpu_pm_dev_state_check(struct amdgpu_device *adev, bool runpm) bool runpm_check = runpm ? adev->in_runpm : false; if (amdgpu_in_reset(adev)) - return -EPERM; + return -EBUSY; + if (adev->in_suspend && !runpm_check) - return -EPERM; + return -EBUSY; return 0; } @@ -2073,6 +2074,134 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd return 0; } +/** + * DOC: board + * + * Certain SOCs can support various board attributes reporting. This is useful + * for user application to monitor various board reated attributes. + * + * The amdgpu driver provides a sysfs API for reporting board attributes. Presently, + * only two types of attributes are reported, baseboard temperature and + * gpu board temperature. Both of them are reported as binary files. + * + * * .. code-block:: console + * + * hexdump /sys/bus/pci/devices/.../board/baseboard_temp + * + * hexdump /sys/bus/pci/devices/.../board/gpuboard_temp + * + */ + +/** + * DOC: baseboard_temp + * + * The amdgpu driver provides a sysfs API for retrieving current baseboard + * temperature metrics data. The file baseboard_temp is used for this. + * Reading the file will dump all the current baseboard temperature metrics data. + */ +static ssize_t amdgpu_get_baseboard_temp_metrics(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + ssize_t size; + int ret; + + ret = amdgpu_pm_get_access_if_active(adev); + if (ret) + return ret; + + size = amdgpu_dpm_get_temp_metrics(adev, SMU_TEMP_METRIC_BASEBOARD, NULL); + if (size <= 0) + goto out; + if (size >= PAGE_SIZE) { + ret = -ENOSPC; + goto out; + } + + amdgpu_dpm_get_temp_metrics(adev, SMU_TEMP_METRIC_BASEBOARD, buf); + +out: + amdgpu_pm_put_access(adev); + + if (ret) + return ret; + + return size; +} + +/** + * DOC: gpuboard_temp + * + * The amdgpu driver provides a sysfs API for retrieving current gpuboard + * temperature metrics data. The file gpuboard_temp is used for this. + * Reading the file will dump all the current gpuboard temperature metrics data. + */ +static ssize_t amdgpu_get_gpuboard_temp_metrics(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + ssize_t size; + int ret; + + ret = amdgpu_pm_get_access_if_active(adev); + if (ret) + return ret; + + size = amdgpu_dpm_get_temp_metrics(adev, SMU_TEMP_METRIC_GPUBOARD, NULL); + if (size <= 0) + goto out; + if (size >= PAGE_SIZE) { + ret = -ENOSPC; + goto out; + } + + amdgpu_dpm_get_temp_metrics(adev, SMU_TEMP_METRIC_GPUBOARD, buf); + +out: + amdgpu_pm_put_access(adev); + + if (ret) + return ret; + + return size; +} + +static DEVICE_ATTR(baseboard_temp, 0444, amdgpu_get_baseboard_temp_metrics, NULL); +static DEVICE_ATTR(gpuboard_temp, 0444, amdgpu_get_gpuboard_temp_metrics, NULL); + +static struct attribute *board_attrs[] = { + &dev_attr_baseboard_temp.attr, + &dev_attr_gpuboard_temp.attr, + NULL +}; + +static umode_t amdgpu_board_attr_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + + if (attr == &dev_attr_baseboard_temp.attr) { + if (!amdgpu_dpm_is_temp_metrics_supported(adev, SMU_TEMP_METRIC_BASEBOARD)) + return 0; + } + + if (attr == &dev_attr_gpuboard_temp.attr) { + if (!amdgpu_dpm_is_temp_metrics_supported(adev, SMU_TEMP_METRIC_GPUBOARD)) + return 0; + } + + return attr->mode; +} + +const struct attribute_group amdgpu_board_attr_group = { + .name = "board", + .attrs = board_attrs, + .is_visible = amdgpu_board_attr_visible, +}; + /* pm policy attributes */ struct amdgpu_pm_policy_attr { struct device_attribute dev_attr; @@ -3458,14 +3587,16 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, effective_mode &= ~S_IWUSR; /* not implemented yet for APUs other than GC 10.3.1 (vangogh) and 9.4.3 */ - if (((adev->family == AMDGPU_FAMILY_SI) || - ((adev->flags & AMD_IS_APU) && (gc_ver != IP_VERSION(10, 3, 1)) && - (gc_ver != IP_VERSION(9, 4, 3) && gc_ver != IP_VERSION(9, 4, 4)))) && - (attr == &sensor_dev_attr_power1_cap_max.dev_attr.attr || - attr == &sensor_dev_attr_power1_cap_min.dev_attr.attr || - attr == &sensor_dev_attr_power1_cap.dev_attr.attr || - attr == &sensor_dev_attr_power1_cap_default.dev_attr.attr)) - return 0; + if (attr == &sensor_dev_attr_power1_cap_max.dev_attr.attr || + attr == &sensor_dev_attr_power1_cap_min.dev_attr.attr || + attr == &sensor_dev_attr_power1_cap.dev_attr.attr || + attr == &sensor_dev_attr_power1_cap_default.dev_attr.attr) { + if (adev->family == AMDGPU_FAMILY_SI || + ((adev->flags & AMD_IS_APU) && gc_ver != IP_VERSION(10, 3, 1) && + (gc_ver != IP_VERSION(9, 4, 3) && gc_ver != IP_VERSION(9, 4, 4))) || + (amdgpu_sriov_vf(adev) && gc_ver == IP_VERSION(11, 0, 3))) + return 0; + } /* not implemented yet for APUs having < GC 9.3.0 (Renoir) */ if (((adev->family == AMDGPU_FAMILY_SI) || @@ -4461,6 +4592,13 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) goto err_out0; } + if (amdgpu_dpm_is_temp_metrics_supported(adev, SMU_TEMP_METRIC_GPUBOARD)) { + ret = devm_device_add_group(adev->dev, + &amdgpu_board_attr_group); + if (ret) + goto err_out0; + } + adev->pm.sysfs_initialized = true; return 0; diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index 768317ee1486..9748744133d9 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -526,6 +526,8 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); +ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, + enum smu_temp_metric_type type, void *table); /** * @get_pm_metrics: Get one snapshot of power management metrics from PMFW. The @@ -613,5 +615,8 @@ ssize_t amdgpu_dpm_get_pm_policy_info(struct amdgpu_device *adev, int amdgpu_dpm_reset_sdma(struct amdgpu_device *adev, uint32_t inst_mask); bool amdgpu_dpm_reset_sdma_is_supported(struct amdgpu_device *adev); int amdgpu_dpm_reset_vcn(struct amdgpu_device *adev, uint32_t inst_mask); +bool amdgpu_dpm_reset_vcn_is_supported(struct amdgpu_device *adev); +bool amdgpu_dpm_is_temp_metrics_supported(struct amdgpu_device *adev, + enum smu_temp_metric_type type); #endif diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.c index ea3ace882a10..52dbf6d0469d 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.c @@ -771,8 +771,7 @@ static struct amdgpu_ps *amdgpu_dpm_pick_power_state(struct amdgpu_device *adev, int i; struct amdgpu_ps *ps; u32 ui_class; - bool single_display = (adev->pm.dpm.new_active_crtc_count < 2) ? - true : false; + bool single_display = adev->pm.dpm.new_active_crtc_count < 2; /* check if the vblank period is too short to adjust the mclk */ if (single_display && adev->powerplay.pp_funcs->vblank_too_short) { diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 52e732be59e3..6595a611ce6e 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3085,7 +3085,13 @@ static bool si_dpm_vblank_too_short(void *handle) /* we never hit the non-gddr5 limit so disable it */ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0; - if (vblank_time < switch_limit) + /* Consider zero vblank time too short and disable MCLK switching. + * Note that the vblank time is set to maximum when no displays are attached, + * so we'll still enable MCLK switching in that case. + */ + if (vblank_time == 0) + return true; + else if (vblank_time < switch_limit) return true; else return false; @@ -3443,12 +3449,14 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, { struct si_ps *ps = si_get_ps(rps); struct amdgpu_clock_and_voltage_limits *max_limits; + struct amdgpu_connector *conn; bool disable_mclk_switching = false; bool disable_sclk_switching = false; u32 mclk, sclk; u16 vddc, vddci, min_vce_voltage = 0; u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; u32 max_sclk = 0, max_mclk = 0; + u32 high_pixelclock_count = 0; int i; if (adev->asic_type == CHIP_HAINAN) { @@ -3476,6 +3484,35 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, } } + /* We define "high pixelclock" for SI as higher than necessary for 4K 30Hz. + * For example, 4K 60Hz and 1080p 144Hz fall into this category. + * Find number of such displays connected. + */ + for (i = 0; i < adev->mode_info.num_crtc; i++) { + if (!(adev->pm.dpm.new_active_crtcs & (1 << i)) || + !adev->mode_info.crtcs[i]->enabled) + continue; + + conn = to_amdgpu_connector(adev->mode_info.crtcs[i]->connector); + + if (conn->pixelclock_for_modeset > 297000) + high_pixelclock_count++; + } + + /* These are some ad-hoc fixes to some issues observed with SI GPUs. + * They are necessary because we don't have something like dce_calcs + * for these GPUs to calculate bandwidth requirements. + */ + if (high_pixelclock_count) { + /* On Oland, we observe some flickering when two 4K 60Hz + * displays are connected, possibly because voltage is too low. + * Raise the voltage by requiring a higher SCLK. + * (Voltage cannot be adjusted independently without also SCLK.) + */ + if (high_pixelclock_count > 1 && adev->asic_type == CHIP_OLAND) + disable_sclk_switching = true; + } + if (rps->vce_active) { rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk; rps->ecclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].ecclk; @@ -5637,14 +5674,10 @@ static int si_populate_smc_t(struct amdgpu_device *adev, static int si_disable_ulv(struct amdgpu_device *adev) { - struct si_power_info *si_pi = si_get_pi(adev); - struct si_ulv_param *ulv = &si_pi->ulv; + PPSMC_Result r; - if (ulv->supported) - return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ? - 0 : -EINVAL; - - return 0; + r = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableULV); + return (r == PPSMC_Result_OK) ? 0 : -EINVAL; } static bool si_is_state_ulv_compatible(struct amdgpu_device *adev, @@ -5817,9 +5850,9 @@ static int si_upload_smc_data(struct amdgpu_device *adev) { struct amdgpu_crtc *amdgpu_crtc = NULL; int i; - - if (adev->pm.dpm.new_active_crtc_count == 0) - return 0; + u32 crtc_index = 0; + u32 mclk_change_block_cp_min = 0; + u32 mclk_change_block_cp_max = 0; for (i = 0; i < adev->mode_info.num_crtc; i++) { if (adev->pm.dpm.new_active_crtcs & (1 << i)) { @@ -5828,26 +5861,31 @@ static int si_upload_smc_data(struct amdgpu_device *adev) } } - if (amdgpu_crtc == NULL) - return 0; + /* When a display is plugged in, program these so that the SMC + * performs MCLK switching when it doesn't cause flickering. + * When no display is plugged in, there is no need to restrict + * MCLK switching, so program them to zero. + */ + if (adev->pm.dpm.new_active_crtc_count && amdgpu_crtc) { + crtc_index = amdgpu_crtc->crtc_id; - if (amdgpu_crtc->line_time <= 0) - return 0; + if (amdgpu_crtc->line_time) { + mclk_change_block_cp_min = 200 / amdgpu_crtc->line_time; + mclk_change_block_cp_max = 100 / amdgpu_crtc->line_time; + } + } - if (si_write_smc_soft_register(adev, - SI_SMC_SOFT_REGISTER_crtc_index, - amdgpu_crtc->crtc_id) != PPSMC_Result_OK) - return 0; + si_write_smc_soft_register(adev, + SI_SMC_SOFT_REGISTER_crtc_index, + crtc_index); - if (si_write_smc_soft_register(adev, - SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min, - amdgpu_crtc->wm_high / amdgpu_crtc->line_time) != PPSMC_Result_OK) - return 0; + si_write_smc_soft_register(adev, + SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min, + mclk_change_block_cp_min); - if (si_write_smc_soft_register(adev, - SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max, - amdgpu_crtc->wm_low / amdgpu_crtc->line_time) != PPSMC_Result_OK) - return 0; + si_write_smc_soft_register(adev, + SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max, + mclk_change_block_cp_max); return 0; } @@ -7954,6 +7992,7 @@ static void si_dpm_print_power_state(void *handle, amdgpu_dpm_dbg_print_class_info(adev, rps->class, rps->class2); amdgpu_dpm_dbg_print_cap_info(adev, rps->caps); drm_dbg(adev_to_drm(adev), "\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + drm_dbg(adev_to_drm(adev), "\tvce evclk: %d ecclk: %d\n", rps->evclk, rps->ecclk); for (i = 0; i < ps->performance_level_count; i++) { pl = &ps->performance_levels[i]; drm_dbg(adev_to_drm(adev), "\t\tpower level %d sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n", diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_smc.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_smc.c index 4e65ab9e931c..281a5e377aee 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_smc.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_smc.c @@ -172,20 +172,42 @@ PPSMC_Result amdgpu_si_send_msg_to_smc(struct amdgpu_device *adev, { u32 tmp; int i; + int usec_timeout; + + /* SMC seems to process some messages exceptionally slowly. */ + switch (msg) { + case PPSMC_MSG_NoForcedLevel: + case PPSMC_MSG_SetEnabledLevels: + case PPSMC_MSG_SetForcedLevels: + case PPSMC_MSG_DisableULV: + case PPSMC_MSG_SwitchToSwState: + usec_timeout = 1000000; /* 1 sec */ + break; + default: + usec_timeout = 200000; /* 200 ms */ + break; + } if (!amdgpu_si_is_smc_running(adev)) return PPSMC_Result_Failed; WREG32(mmSMC_MESSAGE_0, msg); - for (i = 0; i < adev->usec_timeout; i++) { + for (i = 0; i < usec_timeout; i++) { tmp = RREG32(mmSMC_RESP_0); if (tmp != 0) break; udelay(1); } - return (PPSMC_Result)RREG32(mmSMC_RESP_0); + tmp = RREG32(mmSMC_RESP_0); + if (tmp == 0) { + drm_warn(adev_to_drm(adev), + "%s timeout on message: %x (SMC_SCRATCH0: %x)\n", + __func__, msg, RREG32(mmSMC_SCRATCH0)); + } + + return (PPSMC_Result)tmp; } PPSMC_Result amdgpu_si_wait_for_smc_inactive(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c index 8d40ed0f0e83..ce166a7f8e42 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c @@ -563,8 +563,8 @@ bool atomctrl_is_voltage_controlled_by_gpio_v3( PP_ASSERT_WITH_CODE((NULL != voltage_info), "Could not find Voltage Table in BIOS.", return false;); - ret = (NULL != atomctrl_lookup_voltage_type_v3 - (voltage_info, voltage_type, voltage_mode)) ? true : false; + ret = atomctrl_lookup_voltage_type_v3 + (voltage_info, voltage_type, voltage_mode) != NULL; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c index 9a821563bc8e..14ccd743ca1d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c @@ -1032,7 +1032,7 @@ static int smu10_print_clock_levels(struct pp_hwmgr *hwmgr, data->clock_vol_info.vdd_dep_on_fclk; uint32_t i, now, size = 0; uint32_t min_freq, max_freq = 0; - uint32_t ret = 0; + int ret = 0; switch (type) { case PP_SCLK: diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c index 5e43ad2b2956..d2dbd90bb427 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c @@ -2540,9 +2540,8 @@ static int fiji_initialize_mc_reg_table(struct pp_hwmgr *hwmgr) static bool fiji_is_dpm_running(struct pp_hwmgr *hwmgr) { - return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device, - CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON)) - ? true : false; + return PHM_READ_INDIRECT_FIELD(hwmgr->device, + CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON) == 1; } static int fiji_update_dpm_settings(struct pp_hwmgr *hwmgr, diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c index 17d2f5bff4a7..1f50f1e74c48 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c @@ -2655,9 +2655,8 @@ static int iceland_initialize_mc_reg_table(struct pp_hwmgr *hwmgr) static bool iceland_is_dpm_running(struct pp_hwmgr *hwmgr) { - return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device, - CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON)) - ? true : false; + return PHM_READ_INDIRECT_FIELD(hwmgr->device, + CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON) == 1; } const struct pp_smumgr_func iceland_smu_funcs = { diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c index ff6b563ecbf5..bf6d09572cfc 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c @@ -2578,9 +2578,8 @@ static int polaris10_initialize_mc_reg_table(struct pp_hwmgr *hwmgr) static bool polaris10_is_dpm_running(struct pp_hwmgr *hwmgr) { - return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device, - CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON)) - ? true : false; + return PHM_READ_INDIRECT_FIELD(hwmgr->device, + CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON) == 1; } static int polaris10_update_dpm_settings(struct pp_hwmgr *hwmgr, diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c index baf51cd82a35..0d4cbe4113a0 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c @@ -401,7 +401,7 @@ int smu7_request_smu_load_fw(struct pp_hwmgr *hwmgr) int smu7_check_fw_load_finish(struct pp_hwmgr *hwmgr, uint32_t fw_type) { struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend); - uint32_t ret; + int ret; ret = phm_wait_on_indirect_register(hwmgr, mmSMC_IND_INDEX_11, smu_data->soft_regs_start + smum_get_offsetof(hwmgr, diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c index 6fe6e6abb5d8..2e21f9d066cb 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c @@ -3139,9 +3139,8 @@ static int tonga_initialize_mc_reg_table(struct pp_hwmgr *hwmgr) static bool tonga_is_dpm_running(struct pp_hwmgr *hwmgr) { - return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device, - CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON)) - ? true : false; + return PHM_READ_INDIRECT_FIELD(hwmgr->device, + CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON) == 1; } static int tonga_update_dpm_settings(struct pp_hwmgr *hwmgr, diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 756afe78a6e5..c5965924e7c6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -77,6 +77,9 @@ static void smu_power_profile_mode_get(struct smu_context *smu, static void smu_power_profile_mode_put(struct smu_context *smu, enum PP_SMC_POWER_PROFILE profile_mode); static enum smu_clk_type smu_convert_to_smuclk(enum pp_clock_type type); +static int smu_od_edit_dpm_table(void *handle, + enum PP_OD_DPM_TABLE_COMMAND type, + long *input, uint32_t size); static int smu_sys_get_pp_feature_mask(void *handle, char *buf) @@ -763,6 +766,7 @@ static int smu_set_funcs(struct amdgpu_device *adev) case IP_VERSION(13, 0, 14): case IP_VERSION(13, 0, 12): smu_v13_0_6_set_ppt_funcs(smu); + smu_v13_0_6_set_temp_funcs(smu); /* Enable pp_od_clk_voltage node */ smu->od_enabled = true; break; @@ -2195,6 +2199,7 @@ static int smu_resume(struct amdgpu_ip_block *ip_block) int ret; struct amdgpu_device *adev = ip_block->adev; struct smu_context *smu = adev->powerplay.pp_handle; + struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm); if (amdgpu_sriov_multi_vf_mode(adev)) return 0; @@ -2226,6 +2231,18 @@ static int smu_resume(struct amdgpu_ip_block *ip_block) adev->pm.dpm_enabled = true; + if (smu->current_power_limit) { + ret = smu_set_power_limit(smu, smu->current_power_limit); + if (ret && ret != -EOPNOTSUPP) + return ret; + } + + if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL) { + ret = smu_od_edit_dpm_table(smu, PP_OD_COMMIT_DPM_TABLE, NULL, 0); + if (ret) + return ret; + } + dev_info(adev->dev, "SMU is resumed successfully!\n"); return 0; @@ -3815,6 +3832,51 @@ int smu_set_pm_policy(struct smu_context *smu, enum pp_pm_policy p_type, return ret; } +static ssize_t smu_sys_get_temp_metrics(void *handle, enum smu_temp_metric_type type, void *table) +{ + struct smu_context *smu = handle; + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + enum smu_table_id table_id; + + if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) + return -EOPNOTSUPP; + + if (!smu->smu_temp.temp_funcs || !smu->smu_temp.temp_funcs->get_temp_metrics) + return -EOPNOTSUPP; + + table_id = smu_metrics_get_temp_table_id(type); + + if (table_id == SMU_TABLE_COUNT) + return -EINVAL; + + /* If the request is to get size alone, return the cached table size */ + if (!table && tables[table_id].cache.size) + return tables[table_id].cache.size; + + if (smu_table_cache_is_valid(&tables[table_id])) { + memcpy(table, tables[table_id].cache.buffer, + tables[table_id].cache.size); + return tables[table_id].cache.size; + } + + return smu->smu_temp.temp_funcs->get_temp_metrics(smu, type, table); +} + +static bool smu_temp_metrics_is_supported(void *handle, enum smu_temp_metric_type type) +{ + struct smu_context *smu = handle; + bool ret = false; + + if (!smu->pm_enabled) + return false; + + if (smu->smu_temp.temp_funcs && smu->smu_temp.temp_funcs->temp_metrics_is_supported) + ret = smu->smu_temp.temp_funcs->temp_metrics_is_supported(smu, type); + + return ret; +} + static ssize_t smu_sys_get_xcp_metrics(void *handle, int xcp_id, void *table) { struct smu_context *smu = handle; @@ -3887,6 +3949,8 @@ static const struct amd_pm_funcs swsmu_pm_funcs = { .get_dpm_clock_table = smu_get_dpm_clock_table, .get_smu_prv_buf_details = smu_get_prv_buffer_details, .get_xcp_metrics = smu_sys_get_xcp_metrics, + .get_temp_metrics = smu_sys_get_temp_metrics, + .temp_metrics_is_supported = smu_temp_metrics_is_supported, }; int smu_wait_for_event(struct smu_context *smu, enum smu_event_type event, @@ -4060,6 +4124,16 @@ int smu_reset_sdma(struct smu_context *smu, uint32_t inst_mask) return ret; } +bool smu_reset_vcn_is_supported(struct smu_context *smu) +{ + bool ret = false; + + if (smu->ppt_funcs && smu->ppt_funcs->reset_vcn_is_supported) + ret = smu->ppt_funcs->reset_vcn_is_supported(smu); + + return ret; +} + int smu_reset_vcn(struct smu_context *smu, uint32_t inst_mask) { if (smu->ppt_funcs && smu->ppt_funcs->dpm_reset_vcn) 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 b52e194397e2..5976eda80035 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -249,6 +249,14 @@ struct smu_user_dpm_profile { tables[table_id].domain = d; \ } while (0) +struct smu_table_cache { + void *buffer; + size_t size; + /* interval in ms*/ + uint32_t interval; + unsigned long last_cache_time; +}; + struct smu_table { uint64_t size; uint32_t align; @@ -257,6 +265,7 @@ struct smu_table { void *cpu_addr; struct amdgpu_bo *bo; uint32_t version; + struct smu_table_cache cache; }; enum smu_perf_level_designation { @@ -322,6 +331,9 @@ enum smu_table_id { SMU_TABLE_ECCINFO, SMU_TABLE_COMBO_PPTABLE, SMU_TABLE_WIFIBAND, + SMU_TABLE_GPUBOARD_TEMP_METRICS, + SMU_TABLE_BASEBOARD_TEMP_METRICS, + SMU_TABLE_PMFW_SYSTEM_METRICS, SMU_TABLE_COUNT, }; @@ -396,6 +408,10 @@ struct smu_dpm_context { struct smu_dpm_policy_ctxt *dpm_policies; }; +struct smu_temp_context { + const struct smu_temp_funcs *temp_funcs; +}; + struct smu_power_gate { bool uvd_gated; bool vce_gated; @@ -529,6 +545,7 @@ struct smu_context { struct smu_table_context smu_table; struct smu_dpm_context smu_dpm; struct smu_power_context smu_power; + struct smu_temp_context smu_temp; struct smu_feature smu_feature; struct amd_pp_display_configuration *display_config; struct smu_baco_context smu_baco; @@ -623,6 +640,28 @@ struct smu_context { struct i2c_adapter; +/** + * struct smu_temp_funcs - Callbacks used to get temperature data. + */ +struct smu_temp_funcs { + /** + * @get_temp_metrics: Calibrate voltage/frequency curve to fit the system's + * power delivery and voltage margins. Required for adaptive + * @type Temperature metrics type(baseboard/gpuboard) + * Return: Size of &table + */ + ssize_t (*get_temp_metrics)(struct smu_context *smu, + enum smu_temp_metric_type type, void *table); + + /** + * @temp_metrics_is_support: Get if specific temperature metrics is supported + * @type Temperature metrics type(baseboard/gpuboard) + * Return: true if supported else false + */ + bool (*temp_metrics_is_supported)(struct smu_context *smu, enum smu_temp_metric_type type); + +}; + /** * struct pptable_funcs - Callbacks used to interact with the SMU. */ @@ -1397,6 +1436,10 @@ struct pptable_funcs { * @reset_vcn: message SMU to soft reset vcn instance. */ int (*dpm_reset_vcn)(struct smu_context *smu, uint32_t inst_mask); + /** + * @reset_vcn_is_supported: Check if support resets vcn. + */ + bool (*reset_vcn_is_supported)(struct smu_context *smu); /** * @get_ecc_table: message SMU to get ECC INFO table. @@ -1622,6 +1665,71 @@ typedef struct { struct smu_dpm_policy *smu_get_pm_policy(struct smu_context *smu, enum pp_pm_policy p_type); +static inline enum smu_table_id +smu_metrics_get_temp_table_id(enum smu_temp_metric_type type) +{ + switch (type) { + case SMU_TEMP_METRIC_BASEBOARD: + return SMU_TABLE_BASEBOARD_TEMP_METRICS; + case SMU_TEMP_METRIC_GPUBOARD: + return SMU_TABLE_GPUBOARD_TEMP_METRICS; + default: + return SMU_TABLE_COUNT; + } + + return SMU_TABLE_COUNT; +} + +static inline void smu_table_cache_update_time(struct smu_table *table, + unsigned long time) +{ + table->cache.last_cache_time = time; +} + +static inline bool smu_table_cache_is_valid(struct smu_table *table) +{ + if (!table->cache.buffer || !table->cache.last_cache_time || + !table->cache.interval || !table->cache.size || + time_after(jiffies, + table->cache.last_cache_time + + msecs_to_jiffies(table->cache.interval))) + return false; + + return true; +} + +static inline int smu_table_cache_init(struct smu_context *smu, + enum smu_table_id table_id, size_t size, + uint32_t cache_interval) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + + tables[table_id].cache.buffer = kzalloc(size, GFP_KERNEL); + if (!tables[table_id].cache.buffer) + return -ENOMEM; + + tables[table_id].cache.last_cache_time = 0; + tables[table_id].cache.interval = cache_interval; + tables[table_id].cache.size = size; + + return 0; +} + +static inline void smu_table_cache_fini(struct smu_context *smu, + enum smu_table_id table_id) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + + if (tables[table_id].cache.buffer) { + kfree(tables[table_id].cache.buffer); + tables[table_id].cache.buffer = NULL; + tables[table_id].cache.last_cache_time = 0; + tables[table_id].cache.interval = 0; + } +} + #if !defined(SWSMU_CODE_LAYER_L2) && !defined(SWSMU_CODE_LAYER_L3) && !defined(SWSMU_CODE_LAYER_L4) int smu_get_power_limit(void *handle, uint32_t *limit, @@ -1673,6 +1781,7 @@ int smu_send_rma_reason(struct smu_context *smu); int smu_reset_sdma(struct smu_context *smu, uint32_t inst_mask); bool smu_reset_sdma_is_supported(struct smu_context *smu); int smu_reset_vcn(struct smu_context *smu, uint32_t inst_mask); +bool smu_reset_vcn_is_supported(struct smu_context *smu); int smu_set_pm_policy(struct smu_context *smu, enum pp_pm_policy p_type, int level); ssize_t smu_get_pm_policy_info(struct smu_context *smu, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h index 0a2ca544f4e3..1c407a8e96ee 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h @@ -135,7 +135,63 @@ typedef enum { GFX_DVM_MARGIN_COUNT } GFX_DVM_MARGIN_e; -#define SMU_METRICS_TABLE_VERSION 0x13 +typedef enum{ + SYSTEM_TEMP_UBB_FPGA, + SYSTEM_TEMP_UBB_FRONT, + SYSTEM_TEMP_UBB_BACK, + SYSTEM_TEMP_UBB_OAM7, + SYSTEM_TEMP_UBB_IBC, + SYSTEM_TEMP_UBB_UFPGA, + SYSTEM_TEMP_UBB_OAM1, + SYSTEM_TEMP_OAM_0_1_HSC, + SYSTEM_TEMP_OAM_2_3_HSC, + SYSTEM_TEMP_OAM_4_5_HSC, + SYSTEM_TEMP_OAM_6_7_HSC, + SYSTEM_TEMP_UBB_FPGA_0V72_VR, + SYSTEM_TEMP_UBB_FPGA_3V3_VR, + SYSTEM_TEMP_RETIMER_0_1_2_3_1V2_VR, + SYSTEM_TEMP_RETIMER_4_5_6_7_1V2_VR, + SYSTEM_TEMP_RETIMER_0_1_0V9_VR, + SYSTEM_TEMP_RETIMER_4_5_0V9_VR, + SYSTEM_TEMP_RETIMER_2_3_0V9_VR, + SYSTEM_TEMP_RETIMER_6_7_0V9_VR, + SYSTEM_TEMP_OAM_0_1_2_3_3V3_VR, + SYSTEM_TEMP_OAM_4_5_6_7_3V3_VR, + SYSTEM_TEMP_IBC_HSC, + SYSTEM_TEMP_IBC, + SYSTEM_TEMP_MAX_ENTRIES = 32 +} SYSTEM_TEMP_e; + +typedef enum{ + NODE_TEMP_RETIMER, + NODE_TEMP_IBC_TEMP, + NODE_TEMP_IBC_2_TEMP, + NODE_TEMP_VDD18_VR_TEMP, + NODE_TEMP_04_HBM_B_VR_TEMP, + NODE_TEMP_04_HBM_D_VR_TEMP, + NODE_TEMP_MAX_TEMP_ENTRIES = 12 +} NODE_TEMP_e; + +typedef enum { + SVI_VDDCR_VDD0_TEMP, + SVI_VDDCR_VDD1_TEMP, + SVI_VDDCR_VDD2_TEMP, + SVI_VDDCR_VDD3_TEMP, + SVI_VDDCR_SOC_A_TEMP, + SVI_VDDCR_SOC_C_TEMP, + SVI_VDDCR_SOCIO_A_TEMP, + SVI_VDDCR_SOCIO_C_TEMP, + SVI_VDD_085_HBM_TEMP, + SVI_VDDCR_11_HBM_B_TEMP, + SVI_VDDCR_11_HBM_D_TEMP, + SVI_VDD_USR_TEMP, + SVI_VDDIO_11_E32_TEMP, + SVI_MAX_TEMP_ENTRIES, // 13 +} SVI_TEMP_e; + +#define SMU_METRICS_TABLE_VERSION 0x14 + +#define SMU_SYSTEM_METRICS_TABLE_VERSION 0x0 typedef struct __attribute__((packed, aligned(4))) { uint64_t AccumulationCounter; @@ -231,11 +287,27 @@ typedef struct __attribute__((packed, aligned(4))) { uint64_t GfxclkBelowHostLimitThmAcc[8]; uint64_t GfxclkBelowHostLimitTotalAcc[8]; uint64_t GfxclkLowUtilizationAcc[8]; + + uint32_t AidTemperature[4]; + uint32_t XcdTemperature[8]; + uint32_t HbmTemperature[8]; } MetricsTable_t; #define SMU_VF_METRICS_TABLE_MASK (1 << 31) #define SMU_VF_METRICS_TABLE_VERSION (0x6 | SMU_VF_METRICS_TABLE_MASK) +#pragma pack(push, 4) +typedef struct { + uint64_t AccumulationCounter; // Last update timestamp + uint16_t LabelVersion; // Defaults to 0. + uint16_t NodeIdentifier; // Unique identifier to each node on system. + int16_t SystemTemperatures[SYSTEM_TEMP_MAX_ENTRIES]; // Signed integer temperature value in Celsius, unused fields are set to 0xFFFF + int16_t NodeTemperatures[NODE_TEMP_MAX_TEMP_ENTRIES]; // Signed integer temperature value in Celsius, unused fields are set to 0xFFFF + int16_t VrTemperatures[SVI_MAX_TEMP_ENTRIES]; // Signed integer temperature value in Celsius + int16_t spare[3]; +} SystemMetricsTable_t; +#pragma pack(pop) + typedef struct __attribute__((packed, aligned(4))) { uint32_t AccumulationCounter; uint32_t InstGfxclk_TargFreq; diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h index e1f490b6ce64..aff2776a8b6f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h @@ -116,7 +116,11 @@ #define PPSMC_MSG_DumpErrorRecord 0x57 #define PPSMC_MSG_EraseRasTable 0x58 #define PPSMC_MSG_GetStaticMetricsTable 0x59 -#define PPSMC_Message_Count 0x5A +#define PPSMC_MSG_ResetVfArbitersByIndex 0x5A +#define PPSMC_MSG_GetBadPageSeverity 0x5B +#define PPSMC_MSG_GetSystemMetricsTable 0x5C +#define PPSMC_MSG_GetSystemMetricsVersion 0x5D +#define PPSMC_Message_Count 0x5E //PPSMC Reset Types for driver msg argument #define PPSMC_RESET_TYPE_DRIVER_MODE_1_RESET 0x1 diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h index 41f268313613..63a088ef7169 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h @@ -94,9 +94,9 @@ #define PPSMC_MSG_RmaDueToBadPageThreshold 0x43 #define PPSMC_MSG_SetThrottlingPolicy 0x44 #define PPSMC_MSG_ResetSDMA 0x4D -#define PPSMC_MSG_ResetVCN 0x4E #define PPSMC_MSG_GetStaticMetricsTable 0x59 -#define PPSMC_Message_Count 0x5A +#define PPSMC_MSG_ResetVCN 0x5B +#define PPSMC_Message_Count 0x5C //PPSMC Reset Types for driver msg argument #define PPSMC_RESET_TYPE_DRIVER_MODE_1_RESET 0x1 diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index d7a9e41820fa..2256c77da636 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -278,7 +278,8 @@ __SMU_DUMMY_MAP(MALLPowerState), \ __SMU_DUMMY_MAP(ResetSDMA), \ __SMU_DUMMY_MAP(ResetVCN), \ - __SMU_DUMMY_MAP(GetStaticMetricsTable), + __SMU_DUMMY_MAP(GetStaticMetricsTable), \ + __SMU_DUMMY_MAP(GetSystemMetricsTable), #undef __SMU_DUMMY_MAP #define __SMU_DUMMY_MAP(type) SMU_MSG_##type @@ -469,6 +470,7 @@ enum smu_feature_mask { /* Message category flags */ #define SMU_MSG_VF_FLAG (1U << 0) #define SMU_MSG_RAS_PRI (1U << 1) +#define SMU_MSG_NO_PRECHECK (1U << 2) /* Firmware capability flags */ #define SMU_FW_CAP_RAS_PRI (1U << 0) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 9ad46f545d15..599eddb5a67d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -1897,7 +1897,7 @@ static ssize_t arcturus_get_gpu_metrics(struct smu_context *smu, ret = smu_cmn_get_metrics_table(smu, &metrics, - true); + false); if (ret) return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index e97b0cf19197..3baf20f4c373 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -470,7 +470,7 @@ static int renoir_od_edit_dpm_table(struct smu_context *smu, static int renoir_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) { uint32_t min = 0, max = 0; - uint32_t ret = 0; + int ret = 0; ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetMinGfxclkFrequency, 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 c63d2e28954d..b067147b7c41 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -1781,7 +1781,7 @@ static ssize_t aldebaran_get_gpu_metrics(struct smu_context *smu, ret = smu_cmn_get_metrics_table(smu, &metrics, - true); + false); if (ret) return ret; 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 02a455a31c25..0bec12b348ce 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 @@ -83,7 +83,6 @@ const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[SMU_FEATURE_COUNT] = SMU_13_0_12_FEA_MAP(SMU_FEATURE_PIT_BIT, FEATURE_PIT), }; -// clang-format off const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1), @@ -106,7 +105,7 @@ const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1), MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0), MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 1), - MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDriverReset, SMU_MSG_RAS_PRI), + MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDriverReset, SMU_MSG_RAS_PRI | SMU_MSG_NO_PRECHECK), MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0), MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0), MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0), @@ -138,8 +137,55 @@ const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(SetThrottlingPolicy, PPSMC_MSG_SetThrottlingPolicy, 0), MSG_MAP(ResetSDMA, PPSMC_MSG_ResetSDMA, 0), MSG_MAP(GetStaticMetricsTable, PPSMC_MSG_GetStaticMetricsTable, 1), + MSG_MAP(GetSystemMetricsTable, PPSMC_MSG_GetSystemMetricsTable, 0), }; +int smu_v13_0_12_tables_init(struct smu_context *smu) +{ + struct amdgpu_baseboard_temp_metrics_v1_0 *baseboard_temp_metrics; + struct amdgpu_gpuboard_temp_metrics_v1_0 *gpuboard_temp_metrics; + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + struct smu_table_cache *cache; + int ret; + + ret = smu_table_cache_init(smu, SMU_TABLE_PMFW_SYSTEM_METRICS, + smu_v13_0_12_get_system_metrics_size(), 5); + + if (ret) + return ret; + + ret = smu_table_cache_init(smu, SMU_TABLE_BASEBOARD_TEMP_METRICS, + sizeof(*baseboard_temp_metrics), 50); + if (ret) + return ret; + /* Initialize base board temperature metrics */ + cache = &(tables[SMU_TABLE_BASEBOARD_TEMP_METRICS].cache); + baseboard_temp_metrics = + (struct amdgpu_baseboard_temp_metrics_v1_0 *) cache->buffer; + smu_cmn_init_baseboard_temp_metrics(baseboard_temp_metrics, 1, 0); + /* Initialize GPU board temperature metrics */ + ret = smu_table_cache_init(smu, SMU_TABLE_GPUBOARD_TEMP_METRICS, + sizeof(*gpuboard_temp_metrics), 50); + if (ret) { + smu_table_cache_fini(smu, SMU_TABLE_PMFW_SYSTEM_METRICS); + smu_table_cache_fini(smu, SMU_TABLE_BASEBOARD_TEMP_METRICS); + return ret; + } + cache = &(tables[SMU_TABLE_GPUBOARD_TEMP_METRICS].cache); + gpuboard_temp_metrics = (struct amdgpu_gpuboard_temp_metrics_v1_0 *)cache->buffer; + smu_cmn_init_gpuboard_temp_metrics(gpuboard_temp_metrics, 1, 0); + + return 0; +} + +void smu_v13_0_12_tables_fini(struct smu_context *smu) +{ + smu_table_cache_fini(smu, SMU_TABLE_BASEBOARD_TEMP_METRICS); + smu_table_cache_fini(smu, SMU_TABLE_GPUBOARD_TEMP_METRICS); + smu_table_cache_fini(smu, SMU_TABLE_PMFW_SYSTEM_METRICS); +} + static int smu_v13_0_12_get_enabled_mask(struct smu_context *smu, uint64_t *feature_mask) { @@ -187,6 +233,11 @@ int smu_v13_0_12_get_max_metrics_size(void) return max(sizeof(StaticMetricsTable_t), sizeof(MetricsTable_t)); } +size_t smu_v13_0_12_get_system_metrics_size(void) +{ + return sizeof(SystemMetricsTable_t); +} + static void smu_v13_0_12_init_xgmi_data(struct smu_context *smu, StaticMetricsTable_t *static_metrics) { @@ -220,7 +271,7 @@ int smu_v13_0_12_setup_driver_pptable(struct smu_context *smu) struct PPTable_t *pptable = (struct PPTable_t *)smu_table->driver_pptable; uint32_t table_version; - int ret, i; + int ret, i, n; if (!pptable->Init) { ret = smu_v13_0_6_get_static_metrics_table(smu); @@ -259,6 +310,22 @@ int smu_v13_0_12_setup_driver_pptable(struct smu_context *smu) /* use AID0 serial number by default */ pptable->PublicSerialNumber_AID = static_metrics->PublicSerialNumber_AID[0]; + + amdgpu_device_set_uid(smu->adev->uid_info, AMDGPU_UID_TYPE_SOC, + 0, pptable->PublicSerialNumber_AID); + n = ARRAY_SIZE(static_metrics->PublicSerialNumber_AID); + for (i = 0; i < n; i++) { + amdgpu_device_set_uid( + smu->adev->uid_info, AMDGPU_UID_TYPE_AID, i, + static_metrics->PublicSerialNumber_AID[i]); + } + n = ARRAY_SIZE(static_metrics->PublicSerialNumber_XCD); + for (i = 0; i < n; i++) { + amdgpu_device_set_uid( + smu->adev->uid_info, AMDGPU_UID_TYPE_XCD, i, + static_metrics->PublicSerialNumber_XCD[i]); + } + ret = smu_v13_0_12_fru_get_product_info(smu, static_metrics); if (ret) return ret; @@ -359,6 +426,248 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, return 0; } +static int smu_v13_0_12_get_system_metrics_table(struct smu_context *smu) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *table = &smu_table->driver_table; + struct smu_table *tables = smu_table->tables; + struct smu_table *sys_table; + int ret; + + sys_table = &tables[SMU_TABLE_PMFW_SYSTEM_METRICS]; + if (smu_table_cache_is_valid(sys_table)) + return 0; + + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetSystemMetricsTable, NULL); + if (ret) { + dev_info(smu->adev->dev, + "Failed to export system metrics table!\n"); + return ret; + } + + amdgpu_asic_invalidate_hdp(smu->adev, NULL); + 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; +} + +static enum amdgpu_node_temp smu_v13_0_12_get_node_sensor_type(NODE_TEMP_e type) +{ + switch (type) { + case NODE_TEMP_RETIMER: + return AMDGPU_RETIMER_X_TEMP; + case NODE_TEMP_IBC_TEMP: + return AMDGPU_OAM_X_IBC_TEMP; + case NODE_TEMP_IBC_2_TEMP: + return AMDGPU_OAM_X_IBC_2_TEMP; + case NODE_TEMP_VDD18_VR_TEMP: + return AMDGPU_OAM_X_VDD18_VR_TEMP; + case NODE_TEMP_04_HBM_B_VR_TEMP: + return AMDGPU_OAM_X_04_HBM_B_VR_TEMP; + case NODE_TEMP_04_HBM_D_VR_TEMP: + return AMDGPU_OAM_X_04_HBM_D_VR_TEMP; + default: + return -EINVAL; + } +} + +static enum amdgpu_vr_temp smu_v13_0_12_get_vr_sensor_type(SVI_TEMP_e type) +{ + switch (type) { + case SVI_VDDCR_VDD0_TEMP: + return AMDGPU_VDDCR_VDD0_TEMP; + case SVI_VDDCR_VDD1_TEMP: + return AMDGPU_VDDCR_VDD1_TEMP; + case SVI_VDDCR_VDD2_TEMP: + return AMDGPU_VDDCR_VDD2_TEMP; + case SVI_VDDCR_VDD3_TEMP: + return AMDGPU_VDDCR_VDD3_TEMP; + case SVI_VDDCR_SOC_A_TEMP: + return AMDGPU_VDDCR_SOC_A_TEMP; + case SVI_VDDCR_SOC_C_TEMP: + return AMDGPU_VDDCR_SOC_C_TEMP; + case SVI_VDDCR_SOCIO_A_TEMP: + return AMDGPU_VDDCR_SOCIO_A_TEMP; + case SVI_VDDCR_SOCIO_C_TEMP: + return AMDGPU_VDDCR_SOCIO_C_TEMP; + case SVI_VDD_085_HBM_TEMP: + return AMDGPU_VDD_085_HBM_TEMP; + case SVI_VDDCR_11_HBM_B_TEMP: + return AMDGPU_VDDCR_11_HBM_B_TEMP; + case SVI_VDDCR_11_HBM_D_TEMP: + return AMDGPU_VDDCR_11_HBM_D_TEMP; + case SVI_VDD_USR_TEMP: + return AMDGPU_VDD_USR_TEMP; + case SVI_VDDIO_11_E32_TEMP: + return AMDGPU_VDDIO_11_E32_TEMP; + default: + return -EINVAL; + } +} + +static enum amdgpu_system_temp smu_v13_0_12_get_system_sensor_type(SYSTEM_TEMP_e type) +{ + switch (type) { + case SYSTEM_TEMP_UBB_FPGA: + return AMDGPU_UBB_FPGA_TEMP; + case SYSTEM_TEMP_UBB_FRONT: + return AMDGPU_UBB_FRONT_TEMP; + case SYSTEM_TEMP_UBB_BACK: + return AMDGPU_UBB_BACK_TEMP; + case SYSTEM_TEMP_UBB_OAM7: + return AMDGPU_UBB_OAM7_TEMP; + case SYSTEM_TEMP_UBB_IBC: + return AMDGPU_UBB_IBC_TEMP; + case SYSTEM_TEMP_UBB_UFPGA: + return AMDGPU_UBB_UFPGA_TEMP; + case SYSTEM_TEMP_UBB_OAM1: + return AMDGPU_UBB_OAM1_TEMP; + case SYSTEM_TEMP_OAM_0_1_HSC: + return AMDGPU_OAM_0_1_HSC_TEMP; + case SYSTEM_TEMP_OAM_2_3_HSC: + return AMDGPU_OAM_2_3_HSC_TEMP; + case SYSTEM_TEMP_OAM_4_5_HSC: + return AMDGPU_OAM_4_5_HSC_TEMP; + case SYSTEM_TEMP_OAM_6_7_HSC: + return AMDGPU_OAM_6_7_HSC_TEMP; + case SYSTEM_TEMP_UBB_FPGA_0V72_VR: + return AMDGPU_UBB_FPGA_0V72_VR_TEMP; + case SYSTEM_TEMP_UBB_FPGA_3V3_VR: + return AMDGPU_UBB_FPGA_3V3_VR_TEMP; + case SYSTEM_TEMP_RETIMER_0_1_2_3_1V2_VR: + return AMDGPU_RETIMER_0_1_2_3_1V2_VR_TEMP; + case SYSTEM_TEMP_RETIMER_4_5_6_7_1V2_VR: + return AMDGPU_RETIMER_4_5_6_7_1V2_VR_TEMP; + case SYSTEM_TEMP_RETIMER_0_1_0V9_VR: + return AMDGPU_RETIMER_0_1_0V9_VR_TEMP; + case SYSTEM_TEMP_RETIMER_4_5_0V9_VR: + return AMDGPU_RETIMER_4_5_0V9_VR_TEMP; + case SYSTEM_TEMP_RETIMER_2_3_0V9_VR: + return AMDGPU_RETIMER_2_3_0V9_VR_TEMP; + case SYSTEM_TEMP_RETIMER_6_7_0V9_VR: + return AMDGPU_RETIMER_6_7_0V9_VR_TEMP; + case SYSTEM_TEMP_OAM_0_1_2_3_3V3_VR: + return AMDGPU_OAM_0_1_2_3_3V3_VR_TEMP; + case SYSTEM_TEMP_OAM_4_5_6_7_3V3_VR: + return AMDGPU_OAM_4_5_6_7_3V3_VR_TEMP; + case SYSTEM_TEMP_IBC_HSC: + return AMDGPU_IBC_HSC_TEMP; + case SYSTEM_TEMP_IBC: + return AMDGPU_IBC_TEMP; + default: + return -EINVAL; + } +} + +static bool smu_v13_0_12_is_temp_metrics_supported(struct smu_context *smu, + enum smu_temp_metric_type type) +{ + switch (type) { + case SMU_TEMP_METRIC_BASEBOARD: + if (smu->adev->gmc.xgmi.physical_node_id == 0 && + smu->adev->gmc.xgmi.num_physical_nodes > 1 && + smu_v13_0_6_cap_supported(smu, SMU_CAP(TEMP_METRICS))) + return true; + break; + case SMU_TEMP_METRIC_GPUBOARD: + return smu_v13_0_6_cap_supported(smu, SMU_CAP(TEMP_METRICS)); + default: + break; + } + + return false; +} + +static ssize_t smu_v13_0_12_get_temp_metrics(struct smu_context *smu, + enum smu_temp_metric_type type, void *table) +{ + struct amdgpu_baseboard_temp_metrics_v1_0 *baseboard_temp_metrics; + struct amdgpu_gpuboard_temp_metrics_v1_0 *gpuboard_temp_metrics; + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + SystemMetricsTable_t *metrics; + struct smu_table *data_table; + struct smu_table *sys_table; + int ret, sensor_type; + u32 idx, sensors; + ssize_t size; + + if (type == SMU_TEMP_METRIC_BASEBOARD) { + /* Initialize base board temperature metrics */ + data_table = + &smu->smu_table.tables[SMU_TABLE_BASEBOARD_TEMP_METRICS]; + baseboard_temp_metrics = + (struct amdgpu_baseboard_temp_metrics_v1_0 *) + data_table->cache.buffer; + size = sizeof(*baseboard_temp_metrics); + } else { + data_table = + &smu->smu_table.tables[SMU_TABLE_GPUBOARD_TEMP_METRICS]; + gpuboard_temp_metrics = + (struct amdgpu_gpuboard_temp_metrics_v1_0 *) + data_table->cache.buffer; + size = sizeof(*baseboard_temp_metrics); + } + + ret = smu_v13_0_12_get_system_metrics_table(smu); + if (ret) + return ret; + + sys_table = &tables[SMU_TABLE_PMFW_SYSTEM_METRICS]; + metrics = (SystemMetricsTable_t *)sys_table->cache.buffer; + smu_table_cache_update_time(data_table, jiffies); + + if (type == SMU_TEMP_METRIC_GPUBOARD) { + gpuboard_temp_metrics->accumulation_counter = metrics->AccumulationCounter; + gpuboard_temp_metrics->label_version = metrics->LabelVersion; + gpuboard_temp_metrics->node_id = metrics->NodeIdentifier; + + idx = 0; + for (sensors = 0; sensors < NODE_TEMP_MAX_TEMP_ENTRIES; sensors++) { + if (metrics->NodeTemperatures[sensors] != -1) { + sensor_type = smu_v13_0_12_get_node_sensor_type(sensors); + gpuboard_temp_metrics->node_temp[idx] = + ((int)metrics->NodeTemperatures[sensors]) & 0xFFFFFF; + gpuboard_temp_metrics->node_temp[idx] |= (sensor_type << 24); + idx++; + } + } + + idx = 0; + + for (sensors = 0; sensors < SVI_MAX_TEMP_ENTRIES; sensors++) { + if (metrics->VrTemperatures[sensors] != -1) { + sensor_type = smu_v13_0_12_get_vr_sensor_type(sensors); + gpuboard_temp_metrics->vr_temp[idx] = + ((int)metrics->VrTemperatures[sensors]) & 0xFFFFFF; + gpuboard_temp_metrics->vr_temp[idx] |= (sensor_type << 24); + idx++; + } + } + } else if (type == SMU_TEMP_METRIC_BASEBOARD) { + baseboard_temp_metrics->accumulation_counter = metrics->AccumulationCounter; + baseboard_temp_metrics->label_version = metrics->LabelVersion; + baseboard_temp_metrics->node_id = metrics->NodeIdentifier; + + idx = 0; + for (sensors = 0; sensors < SYSTEM_TEMP_MAX_ENTRIES; sensors++) { + if (metrics->SystemTemperatures[sensors] != -1) { + sensor_type = smu_v13_0_12_get_system_sensor_type(sensors); + baseboard_temp_metrics->system_temp[idx] = + ((int)metrics->SystemTemperatures[sensors]) & 0xFFFFFF; + baseboard_temp_metrics->system_temp[idx] |= (sensor_type << 24); + idx++; + } + } + } + + memcpy(table, data_table->cache.buffer, size); + + return size; +} + ssize_t smu_v13_0_12_get_xcp_metrics(struct smu_context *smu, struct amdgpu_xcp *xcp, void *table, void *smu_metrics) { const u8 num_jpeg_rings = NUM_JPEG_RINGS_FW; @@ -572,3 +881,8 @@ ssize_t smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, void return sizeof(*gpu_metrics); } + +const struct smu_temp_funcs smu_v13_0_12_temp_funcs = { + .temp_metrics_is_supported = smu_v13_0_12_is_temp_metrics_supported, + .get_temp_metrics = smu_v13_0_12_get_temp_metrics, +}; 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 9cc294f4708b..ebee659f8a1c 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 @@ -145,7 +145,7 @@ static const struct cmn2asic_msg_mapping smu_v13_0_6_message_map[SMU_MSG_MAX_COU MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1), MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0), MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 1), - MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDriverReset, SMU_MSG_RAS_PRI), + MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDriverReset, SMU_MSG_RAS_PRI | SMU_MSG_NO_PRECHECK), MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0), MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0), MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0), @@ -177,7 +177,7 @@ static const struct cmn2asic_msg_mapping smu_v13_0_6_message_map[SMU_MSG_MAX_COU MSG_MAP(SetThrottlingPolicy, PPSMC_MSG_SetThrottlingPolicy, 0), MSG_MAP(ResetSDMA, PPSMC_MSG_ResetSDMA, 0), MSG_MAP(ResetVCN, PPSMC_MSG_ResetVCN, 0), - MSG_MAP(GetStaticMetricsTable, PPSMC_MSG_GetStaticMetricsTable, 0), + MSG_MAP(GetStaticMetricsTable, PPSMC_MSG_GetStaticMetricsTable, 1), }; // clang-format on @@ -312,6 +312,8 @@ static void smu_v13_0_14_init_caps(struct smu_context *smu) smu_v13_0_6_cap_set(smu, SMU_CAP(PER_INST_METRICS)); if (fw_ver >= 0x5551200) smu_v13_0_6_cap_set(smu, SMU_CAP(SDMA_RESET)); + if (fw_ver >= 0x5551800) + smu_v13_0_6_cap_set(smu, SMU_CAP(VCN_RESET)); if (fw_ver >= 0x5551600) { smu_v13_0_6_cap_set(smu, SMU_CAP(STATIC_METRICS)); smu_v13_0_6_cap_set(smu, SMU_CAP(BOARD_VOLTAGE)); @@ -350,6 +352,13 @@ static void smu_v13_0_12_init_caps(struct smu_context *smu) smu_v13_0_6_cap_set(smu, SMU_CAP(BOARD_VOLTAGE)); smu_v13_0_6_cap_set(smu, SMU_CAP(PLDM_VERSION)); } + + if (fw_ver >= 0x04560700) { + if (!amdgpu_sriov_vf(smu->adev)) + smu_v13_0_6_cap_set(smu, SMU_CAP(TEMP_METRICS)); + } else { + smu_v13_0_12_tables_fini(smu); + } } static void smu_v13_0_6_init_caps(struct smu_context *smu) @@ -402,19 +411,37 @@ static void smu_v13_0_6_init_caps(struct smu_context *smu) if ((pgm == 7 && fw_ver >= 0x7550E00) || (pgm == 0 && fw_ver >= 0x00557E00)) smu_v13_0_6_cap_set(smu, SMU_CAP(HST_LIMIT_METRICS)); - if ((pgm == 0 && fw_ver >= 0x00557F01) || - (pgm == 7 && fw_ver >= 0x7551000)) { - smu_v13_0_6_cap_set(smu, SMU_CAP(STATIC_METRICS)); - smu_v13_0_6_cap_set(smu, SMU_CAP(BOARD_VOLTAGE)); + + if (amdgpu_sriov_vf(adev)) { + if ((pgm == 0 && fw_ver >= 0x00558000) || + (pgm == 7 && fw_ver >= 0x7551000)) { + smu_v13_0_6_cap_set(smu, + SMU_CAP(STATIC_METRICS)); + smu_v13_0_6_cap_set(smu, + SMU_CAP(BOARD_VOLTAGE)); + smu_v13_0_6_cap_set(smu, SMU_CAP(PLDM_VERSION)); + } + } else { + if ((pgm == 0 && fw_ver >= 0x00557F01) || + (pgm == 7 && fw_ver >= 0x7551000)) { + smu_v13_0_6_cap_set(smu, + SMU_CAP(STATIC_METRICS)); + smu_v13_0_6_cap_set(smu, + SMU_CAP(BOARD_VOLTAGE)); + } + if ((pgm == 0 && fw_ver >= 0x00558000) || + (pgm == 7 && fw_ver >= 0x7551000)) + smu_v13_0_6_cap_set(smu, SMU_CAP(PLDM_VERSION)); } - if ((pgm == 0 && fw_ver >= 0x00558000) || - (pgm == 7 && fw_ver >= 0x7551000)) - smu_v13_0_6_cap_set(smu, SMU_CAP(PLDM_VERSION)); } if (((pgm == 7) && (fw_ver >= 0x7550700)) || ((pgm == 0) && (fw_ver >= 0x00557900)) || ((pgm == 4) && (fw_ver >= 0x4557000))) smu_v13_0_6_cap_set(smu, SMU_CAP(SDMA_RESET)); + + if (((pgm == 0) && (fw_ver >= 0x00558200)) || + ((pgm == 4) && (fw_ver >= 0x04557100))) + smu_v13_0_6_cap_set(smu, SMU_CAP(VCN_RESET)); } static void smu_v13_0_x_init_caps(struct smu_context *smu) @@ -511,8 +538,12 @@ static int smu_v13_0_6_tables_init(struct smu_context *smu) { struct smu_table_context *smu_table = &smu->smu_table; struct smu_table *tables = smu_table->tables; + void *gpu_metrics_table __free(kfree) = NULL; + void *driver_pptable __free(kfree) = NULL; + void *metrics_table __free(kfree) = NULL; struct amdgpu_device *adev = smu->adev; int gpu_metrcs_size = METRICS_TABLE_SIZE; + int ret; if (!(adev->flags & AMD_IS_APU)) SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU13_TOOL_SIZE, @@ -528,26 +559,35 @@ static int smu_v13_0_6_tables_init(struct smu_context *smu) PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT); - smu_table->metrics_table = kzalloc(METRICS_TABLE_SIZE, GFP_KERNEL); - if (!smu_table->metrics_table) + SMU_TABLE_INIT(tables, SMU_TABLE_PMFW_SYSTEM_METRICS, + smu_v13_0_12_get_system_metrics_size(), PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT); + + metrics_table = kzalloc(METRICS_TABLE_SIZE, GFP_KERNEL); + if (!metrics_table) return -ENOMEM; smu_table->metrics_time = 0; smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_8); - smu_table->gpu_metrics_table = + gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL); - if (!smu_table->gpu_metrics_table) { - kfree(smu_table->metrics_table); + if (!gpu_metrics_table) return -ENOMEM; + + driver_pptable = kzalloc(sizeof(struct PPTable_t), GFP_KERNEL); + if (!driver_pptable) + return -ENOMEM; + + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == + IP_VERSION(13, 0, 12)) { + ret = smu_v13_0_12_tables_init(smu); + if (ret) + return ret; } - smu_table->driver_pptable = - kzalloc(sizeof(struct PPTable_t), GFP_KERNEL); - if (!smu_table->driver_pptable) { - kfree(smu_table->metrics_table); - kfree(smu_table->gpu_metrics_table); - return -ENOMEM; - } + smu_table->gpu_metrics_table = no_free_ptr(gpu_metrics_table); + smu_table->metrics_table = no_free_ptr(metrics_table); + smu_table->driver_pptable = no_free_ptr(driver_pptable); return 0; } @@ -677,6 +717,13 @@ static int smu_v13_0_6_init_smc_tables(struct smu_context *smu) return ret; } +static int smu_v13_0_6_fini_smc_tables(struct smu_context *smu) +{ + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12)) + smu_v13_0_12_tables_fini(smu); + return smu_v13_0_fini_smc_tables(smu); +} + static int smu_v13_0_6_get_allowed_feature_mask(struct smu_context *smu, uint32_t *feature_mask, uint32_t num) @@ -803,7 +850,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) struct PPTable_t *pptable = (struct PPTable_t *)smu_table->driver_pptable; int version = smu_v13_0_6_get_metrics_version(smu); - int ret, i, retry = 100; + int ret, i, retry = 100, n; uint32_t table_version; uint16_t max_speed; uint8_t max_width; @@ -865,6 +912,23 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) pptable->PublicSerialNumber_AID = GET_METRIC_FIELD(PublicSerialNumber_AID, version)[0]; + amdgpu_device_set_uid(smu->adev->uid_info, AMDGPU_UID_TYPE_SOC, + 0, pptable->PublicSerialNumber_AID); + n = ARRAY_SIZE(metrics_v0->PublicSerialNumber_AID); + for (i = 0; i < n; i++) { + amdgpu_device_set_uid( + smu->adev->uid_info, AMDGPU_UID_TYPE_AID, i, + GET_METRIC_FIELD(PublicSerialNumber_AID, + version)[i]); + } + n = ARRAY_SIZE(metrics_v0->PublicSerialNumber_XCD); + for (i = 0; i < n; i++) { + amdgpu_device_set_uid( + smu->adev->uid_info, AMDGPU_UID_TYPE_XCD, i, + GET_METRIC_FIELD(PublicSerialNumber_XCD, + version)[i]); + } + pptable->Init = true; if (smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) { ret = smu_v13_0_6_get_static_metrics_table(smu); @@ -2560,9 +2624,9 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id, const u8 num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS_4_0_3; int version = smu_v13_0_6_get_metrics_version(smu); struct amdgpu_partition_metrics_v1_0 *xcp_metrics; + MetricsTableV0_t *metrics_v0 __free(kfree) = NULL; struct amdgpu_device *adev = smu->adev; int ret, inst, i, j, k, idx; - MetricsTableV0_t *metrics_v0; MetricsTableV1_t *metrics_v1; MetricsTableV2_t *metrics_v2; struct amdgpu_xcp *xcp; @@ -2587,17 +2651,14 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id, return -ENOMEM; ret = smu_v13_0_6_get_metrics_table(smu, metrics_v0, false); - if (ret) { - kfree(metrics_v0); + if (ret) return ret; - } if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12) && - smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) { - ret = smu_v13_0_12_get_xcp_metrics(smu, xcp, table, metrics_v0); - goto out; - } + smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) + return smu_v13_0_12_get_xcp_metrics(smu, xcp, table, + metrics_v0); metrics_v1 = (MetricsTableV1_t *)metrics_v0; metrics_v2 = (MetricsTableV2_t *)metrics_v0; @@ -2668,8 +2729,6 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id, idx++; } } -out: - kfree(metrics_v0); return sizeof(*xcp_metrics); } @@ -2680,31 +2739,26 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table struct gpu_metrics_v1_8 *gpu_metrics = (struct gpu_metrics_v1_8 *)smu_table->gpu_metrics_table; int version = smu_v13_0_6_get_metrics_version(smu); + MetricsTableV0_t *metrics_v0 __free(kfree) = NULL; int ret = 0, xcc_id, inst, i, j, k, idx; struct amdgpu_device *adev = smu->adev; - MetricsTableV0_t *metrics_v0; MetricsTableV1_t *metrics_v1; MetricsTableV2_t *metrics_v2; struct amdgpu_xcp *xcp; u16 link_width_level; - ssize_t num_bytes; u8 num_jpeg_rings; u32 inst_mask; bool per_inst; metrics_v0 = kzalloc(METRICS_TABLE_SIZE, GFP_KERNEL); ret = smu_v13_0_6_get_metrics_table(smu, metrics_v0, false); - if (ret) { - kfree(metrics_v0); + if (ret) return ret; - } - if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12) && - smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) { - num_bytes = smu_v13_0_12_get_gpu_metrics(smu, table, metrics_v0); - kfree(metrics_v0); - return num_bytes; - } + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == + IP_VERSION(13, 0, 12) && + smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) + return smu_v13_0_12_get_gpu_metrics(smu, table, metrics_v0); metrics_v1 = (MetricsTableV1_t *)metrics_v0; metrics_v2 = (MetricsTableV2_t *)metrics_v0; @@ -2890,7 +2944,6 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table gpu_metrics->firmware_timestamp = GET_METRIC_FIELD(Timestamp, version); *table = (void *)gpu_metrics; - kfree(metrics_v0); return sizeof(*gpu_metrics); } @@ -3076,7 +3129,7 @@ static inline bool smu_v13_0_6_is_link_reset_supported(struct smu_context *smu) struct amdgpu_device *adev = smu->adev; int var = (adev->pdev->device & 0xF); - if (var == 0x1) + if (var == 0x0 || var == 0x1 || var == 0x3) return true; return false; @@ -3152,6 +3205,11 @@ static int smu_v13_0_6_reset_sdma(struct smu_context *smu, uint32_t inst_mask) return ret; } +static bool smu_v13_0_6_reset_vcn_is_supported(struct smu_context *smu) +{ + return smu_v13_0_6_cap_supported(smu, SMU_CAP(VCN_RESET)); +} + static int smu_v13_0_6_reset_vcn(struct smu_context *smu, uint32_t inst_mask) { int ret = 0; @@ -3797,7 +3855,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .init_microcode = smu_v13_0_6_init_microcode, .fini_microcode = smu_v13_0_fini_microcode, .init_smc_tables = smu_v13_0_6_init_smc_tables, - .fini_smc_tables = smu_v13_0_fini_smc_tables, + .fini_smc_tables = smu_v13_0_6_fini_smc_tables, .init_power = smu_v13_0_init_power, .fini_power = smu_v13_0_fini_power, .check_fw_status = smu_v13_0_6_check_fw_status, @@ -3840,6 +3898,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .reset_sdma = smu_v13_0_6_reset_sdma, .reset_sdma_is_supported = smu_v13_0_6_reset_sdma_is_supported, .dpm_reset_vcn = smu_v13_0_6_reset_vcn, + .reset_vcn_is_supported = smu_v13_0_6_reset_vcn_is_supported, }; void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu) @@ -3857,3 +3916,9 @@ void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu) amdgpu_mca_smu_init_funcs(smu->adev, &smu_v13_0_6_mca_smu_funcs); amdgpu_aca_set_smu_funcs(smu->adev, &smu_v13_0_6_aca_smu_funcs); } + +void smu_v13_0_6_set_temp_funcs(struct smu_context *smu) +{ + smu->smu_temp.temp_funcs = (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) + == IP_VERSION(13, 0, 12)) ? &smu_v13_0_12_temp_funcs : NULL; +} diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h index 67b30674fd31..aae9a546a67e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h @@ -64,14 +64,17 @@ enum smu_v13_0_6_caps { SMU_CAP(RMA_MSG), SMU_CAP(ACA_SYND), SMU_CAP(SDMA_RESET), + SMU_CAP(VCN_RESET), SMU_CAP(STATIC_METRICS), SMU_CAP(HST_LIMIT_METRICS), SMU_CAP(BOARD_VOLTAGE), SMU_CAP(PLDM_VERSION), + SMU_CAP(TEMP_METRICS), SMU_CAP(ALL), }; extern void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu); +extern void smu_v13_0_6_set_temp_funcs(struct smu_context *smu); bool smu_v13_0_6_cap_supported(struct smu_context *smu, enum smu_v13_0_6_caps cap); int smu_v13_0_6_get_static_metrics_table(struct smu_context *smu); int smu_v13_0_6_get_metrics_table(struct smu_context *smu, void *metrics_table, @@ -79,6 +82,7 @@ int smu_v13_0_6_get_metrics_table(struct smu_context *smu, void *metrics_table, bool smu_v13_0_12_is_dpm_running(struct smu_context *smu); int smu_v13_0_12_get_max_metrics_size(void); +size_t smu_v13_0_12_get_system_metrics_size(void); int smu_v13_0_12_setup_driver_pptable(struct smu_context *smu); int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, MetricsMember_t member, uint32_t *value); @@ -86,6 +90,9 @@ ssize_t smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, void ssize_t smu_v13_0_12_get_xcp_metrics(struct smu_context *smu, struct amdgpu_xcp *xcp, void *table, void *smu_metrics); +int smu_v13_0_12_tables_init(struct smu_context *smu); +void smu_v13_0_12_tables_fini(struct smu_context *smu); extern const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[]; extern const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[]; +extern const struct smu_temp_funcs smu_v13_0_12_temp_funcs; #endif 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 3aea32baea3d..f32474af90b3 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 @@ -1697,9 +1697,11 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; + struct smu_14_0_2_powerplay_table *powerplay_table = + table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; CustomSkuTable_t *skutable = &pptable->CustomSkuTable; - uint32_t power_limit; + uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; if (smu_v14_0_get_current_power_limit(smu, &power_limit)) @@ -1712,11 +1714,29 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, if (default_power_limit) *default_power_limit = power_limit; - if (max_power_limit) - *max_power_limit = msg_limit; + if (powerplay_table) { + if (smu->od_enabled && + smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { + od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt; + od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt; + } else if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { + od_percent_upper = 0; + od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt; + } + } - if (min_power_limit) - *min_power_limit = 0; + dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", + od_percent_upper, od_percent_lower, power_limit); + + if (max_power_limit) { + *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit /= 100; + } + + if (min_power_limit) { + *min_power_limit = power_limit * (100 + od_percent_lower); + *min_power_limit /= 100; + } return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 59f9abd0f7b8..f532f7c69259 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -256,11 +256,12 @@ static int __smu_cmn_ras_filter_msg(struct smu_context *smu, { struct amdgpu_device *adev = smu->adev; uint32_t flags, resp; - bool fed_status; + bool fed_status, pri; flags = __smu_cmn_get_msg_flags(smu, msg); *poll = true; + pri = !!(flags & SMU_MSG_NO_PRECHECK); /* When there is RAS fatal error, FW won't process non-RAS priority * messages. Don't allow any messages other than RAS priority messages. */ @@ -272,15 +273,18 @@ static int __smu_cmn_ras_filter_msg(struct smu_context *smu, smu_get_message_name(smu, msg)); return -EACCES; } + } + if (pri || fed_status) { /* FW will ignore non-priority messages when a RAS fatal error - * is detected. Hence it is possible that a previous message - * wouldn't have got response. Allow to continue without polling - * for response status for priority messages. + * or reset condition is detected. Hence it is possible that a + * previous message wouldn't have got response. Allow to + * continue without polling for response status for priority + * messages. */ resp = RREG32(smu->resp_reg); dev_dbg(adev->dev, - "Sending RAS priority message %s response status: %x", + "Sending priority message %s response status: %x", smu_get_message_name(smu, msg), resp); if (resp == 0) *poll = false; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h index a608cdbdada4..d588f74b98de 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h @@ -65,6 +65,32 @@ header->structure_size = sizeof(*tmp); \ } while (0) +#define smu_cmn_init_baseboard_temp_metrics(ptr, fr, cr) \ + do { \ + typecheck(struct amdgpu_baseboard_temp_metrics_v##fr##_##cr *, \ + (ptr)); \ + struct amdgpu_baseboard_temp_metrics_v##fr##_##cr *tmp = (ptr); \ + struct metrics_table_header *header = \ + (struct metrics_table_header *)tmp; \ + memset(header, 0xFF, sizeof(*tmp)); \ + header->format_revision = fr; \ + header->content_revision = cr; \ + header->structure_size = sizeof(*tmp); \ + } while (0) + +#define smu_cmn_init_gpuboard_temp_metrics(ptr, fr, cr) \ + do { \ + typecheck(struct amdgpu_gpuboard_temp_metrics_v##fr##_##cr *, \ + (ptr)); \ + struct amdgpu_gpuboard_temp_metrics_v##fr##_##cr *tmp = (ptr); \ + struct metrics_table_header *header = \ + (struct metrics_table_header *)tmp; \ + memset(header, 0xFF, sizeof(*tmp)); \ + header->format_revision = fr; \ + header->content_revision = cr; \ + header->structure_size = sizeof(*tmp); \ + } while (0) + extern const int link_speed[]; /* Helper to Convert from PCIE Gen 1/2/3/4/5/6 to 0.1 GT/s speed units */ diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 34547edf1ee3..87f2e5ee8790 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -159,7 +159,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, } if (!fourcc_mod_is_vendor(modifier, ARM)) { - DRM_ERROR("Unknown modifier (not Arm)\n"); + DRM_DEBUG_KMS("Unknown modifier (not Arm)\n"); return false; } diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c index 477ee15eff5d..829e3b8b0d19 100644 --- a/drivers/gpu/drm/ast/ast_2100.c +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -31,6 +31,39 @@ #include "ast_drv.h" #include "ast_post.h" +/* + * DRAM type + */ + +static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast) +{ + u32 mcr_cfg; + enum ast_dram_layout dram_layout; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + mcr_cfg = ast_read32(ast, 0x10004); + + switch (mcr_cfg & 0x0c) { + case 0: + case 4: + default: + dram_layout = AST_DRAM_512Mx16; + break; + case 8: + if (mcr_cfg & 0x40) + dram_layout = AST_DRAM_1Gx16; + else + dram_layout = AST_DRAM_512Mx32; + break; + case 0xc: + dram_layout = AST_DRAM_1Gx32; + break; + } + + return dram_layout; +} + /* * POST */ @@ -266,6 +299,7 @@ static void ast_post_chip_2100(struct ast_device *ast) u8 j; u32 data, temp, i; const struct ast_dramstruct *dram_reg_info; + enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast); j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); @@ -292,11 +326,17 @@ static void ast_post_chip_2100(struct ast_device *ast) for (i = 0; i < 15; i++) udelay(dram_reg_info->data); } else if (AST_DRAMSTRUCT_IS(dram_reg_info, DRAM_TYPE)) { - data = dram_reg_info->data; - if (ast->dram_type == AST_DRAM_1Gx16) + switch (dram_layout) { + case AST_DRAM_1Gx16: data = 0x00000d89; - else if (ast->dram_type == AST_DRAM_1Gx32) + break; + case AST_DRAM_1Gx32: data = 0x00000c8d; + break; + default: + data = dram_reg_info->data; + break; + } temp = ast_read32(ast, 0x12070); temp &= 0xc; diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index e37a55295ed7..c15aef014f69 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -98,13 +98,15 @@ enum ast_config_mode { ast_use_defaults }; -#define AST_DRAM_512Mx16 0 -#define AST_DRAM_1Gx16 1 -#define AST_DRAM_512Mx32 2 -#define AST_DRAM_1Gx32 3 -#define AST_DRAM_2Gx16 6 -#define AST_DRAM_4Gx16 7 -#define AST_DRAM_8Gx16 8 +enum ast_dram_layout { + AST_DRAM_512Mx16 = 0, + AST_DRAM_1Gx16 = 1, + AST_DRAM_512Mx32 = 2, + AST_DRAM_1Gx32 = 3, + AST_DRAM_2Gx16 = 6, + AST_DRAM_4Gx16 = 7, + AST_DRAM_8Gx16 = 8, +}; /* * Hardware cursor @@ -172,10 +174,6 @@ struct ast_device { enum ast_config_mode config_mode; enum ast_chip chip; - uint32_t dram_bus_width; - uint32_t dram_type; - uint32_t mclk; - void __iomem *vram; unsigned long vram_base; unsigned long vram_size; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 44b9b5f659fc..3eea6a6cdacd 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -210,126 +210,6 @@ static void ast_detect_tx_chip(struct ast_device *ast, bool need_post) drm_info(dev, "Using %s\n", info_str[ast->tx_chip]); } -static int ast_get_dram_info(struct ast_device *ast) -{ - struct drm_device *dev = &ast->base; - struct device_node *np = dev->dev->of_node; - uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap; - uint32_t denum, num, div, ref_pll, dsel; - - switch (ast->config_mode) { - case ast_use_dt: - /* - * If some properties are missing, use reasonable - * defaults for GEN5 - */ - if (of_property_read_u32(np, "aspeed,mcr-configuration", - &mcr_cfg)) - mcr_cfg = 0x00000577; - if (of_property_read_u32(np, "aspeed,mcr-scu-mpll", - &mcr_scu_mpll)) - mcr_scu_mpll = 0x000050C0; - if (of_property_read_u32(np, "aspeed,mcr-scu-strap", - &mcr_scu_strap)) - mcr_scu_strap = 0; - break; - case ast_use_p2a: - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - mcr_cfg = ast_read32(ast, 0x10004); - mcr_scu_mpll = ast_read32(ast, 0x10120); - mcr_scu_strap = ast_read32(ast, 0x10170); - break; - case ast_use_defaults: - default: - ast->dram_bus_width = 16; - ast->dram_type = AST_DRAM_1Gx16; - if (IS_AST_GEN6(ast)) - ast->mclk = 800; - else - ast->mclk = 396; - return 0; - } - - if (mcr_cfg & 0x40) - ast->dram_bus_width = 16; - else - ast->dram_bus_width = 32; - - if (IS_AST_GEN6(ast)) { - switch (mcr_cfg & 0x03) { - case 0: - ast->dram_type = AST_DRAM_1Gx16; - break; - default: - case 1: - ast->dram_type = AST_DRAM_2Gx16; - break; - case 2: - ast->dram_type = AST_DRAM_4Gx16; - break; - case 3: - ast->dram_type = AST_DRAM_8Gx16; - break; - } - } else if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { - switch (mcr_cfg & 0x03) { - case 0: - ast->dram_type = AST_DRAM_512Mx16; - break; - default: - case 1: - ast->dram_type = AST_DRAM_1Gx16; - break; - case 2: - ast->dram_type = AST_DRAM_2Gx16; - break; - case 3: - ast->dram_type = AST_DRAM_4Gx16; - break; - } - } else { - switch (mcr_cfg & 0x0c) { - case 0: - case 4: - ast->dram_type = AST_DRAM_512Mx16; - break; - case 8: - if (mcr_cfg & 0x40) - ast->dram_type = AST_DRAM_1Gx16; - else - ast->dram_type = AST_DRAM_512Mx32; - break; - case 0xc: - ast->dram_type = AST_DRAM_1Gx32; - break; - } - } - - if (mcr_scu_strap & 0x2000) - ref_pll = 14318; - else - ref_pll = 12000; - - denum = mcr_scu_mpll & 0x1f; - num = (mcr_scu_mpll & 0x3fe0) >> 5; - dsel = (mcr_scu_mpll & 0xc000) >> 14; - switch (dsel) { - case 3: - div = 0x4; - break; - case 2: - case 1: - div = 0x2; - break; - default: - div = 0x1; - break; - } - ast->mclk = ref_pll * (num + 2) / ((denum + 2) * (div * 1000)); - return 0; -} - struct drm_device *ast_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -352,12 +232,6 @@ struct drm_device *ast_device_create(struct pci_dev *pdev, ast->regs = regs; ast->ioregs = ioregs; - ret = ast_get_dram_info(ast); - if (ret) - return ERR_PTR(ret); - drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n", - ast->mclk, ast->dram_type, ast->dram_bus_width); - ast_detect_tx_chip(ast, need_post); switch (ast->tx_chip) { case AST_TX_ASTDP: diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 7908087bcb5a..b4e8edc7c767 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -29,7 +29,6 @@ */ #include -#include #include #include diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b9e0ca85226a..a250afd8d662 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -120,8 +120,8 @@ config DRM_ITE_IT6505 select DRM_DISPLAY_DP_AUX_BUS select DRM_KMS_HELPER select EXTCON - select CRYPTO - select CRYPTO_HASH + select CRYPTO_LIB_SHA1 + select REGMAP_I2C help ITE IT6505 DisplayPort bridge chip driver. @@ -316,6 +316,19 @@ config DRM_SIMPLE_BRIDGE Support for non-programmable DRM bridges, such as ADI ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs. +config DRM_SOLOMON_SSD2825 + tristate "SSD2825 RGB/DSI bridge" + depends on SPI_MASTER && OF + select DRM_MIPI_DSI + select DRM_KMS_HELPER + select DRM_PANEL + help + Say Y here if you want support for the Solomon SSD2825 RGB/DSI + SPI bridge driver. + + Say M here if you want to support this hardware as a module. + The module will be named "ssd2825". + config DRM_THINE_THC63LVD1024 tristate "Thine THC63LVD1024 LVDS decoder bridge" depends on OF @@ -438,6 +451,18 @@ config DRM_TI_TPD12S015 Texas Instruments TPD12S015 HDMI level shifter and ESD protection driver. +config DRM_WAVESHARE_BRIDGE + tristate "Waveshare DSI bridge" + depends on OF + depends on BACKLIGHT_CLASS_DEVICE + select DRM_PANEL_BRIDGE + select DRM_KMS_HELPER + select DRM_MIPI_DSI + select REGMAP_I2C + help + Driver for waveshare DSI to DPI bridge board. + Please say Y if you have such hardware + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 245e8a27e3fc..c7dc03182e59 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o +obj-$(CONFIG_DRM_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o @@ -40,6 +41,7 @@ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TDP158) += ti-tdp158.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o +obj-$(CONFIG_DRM_WAVESHARE_BRIDGE) += waveshare-dsi.o obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 85ebead9809c..8be7266fd4f4 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -195,13 +195,14 @@ #define ADV7511_I2S_IEC958_DIRECT 3 #define ADV7511_PACKET(p, x) ((p) * 0x20 + (x)) -#define ADV7511_PACKET_SDP(x) ADV7511_PACKET(0, x) +#define ADV7511_PACKET_SPD(x) ADV7511_PACKET(0, x) #define ADV7511_PACKET_MPEG(x) ADV7511_PACKET(1, x) #define ADV7511_PACKET_ACP(x) ADV7511_PACKET(2, x) #define ADV7511_PACKET_ISRC1(x) ADV7511_PACKET(3, x) #define ADV7511_PACKET_ISRC2(x) ADV7511_PACKET(4, x) #define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x) -#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x) +#define ADV7511_PACKET_SPARE1(x) ADV7511_PACKET(6, x) +#define ADV7511_PACKET_SPARE2(x) ADV7511_PACKET(7, x) #define ADV7511_REG_CEC_TX_FRAME_HDR 0x00 #define ADV7511_REG_CEC_TX_FRAME_DATA0 0x01 @@ -348,6 +349,7 @@ struct adv7511 { struct i2c_client *i2c_cec; struct regmap *regmap; + struct regmap *regmap_packet; struct regmap *regmap_cec; enum drm_connector_status status; bool powered; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c index 766b1c96bc88..87e7e820810a 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c @@ -12,6 +12,8 @@ #include #include +#include + #include "adv7511.h" static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs, @@ -155,17 +157,8 @@ int adv7511_hdmi_audio_prepare(struct drm_bridge *bridge, regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4); - /* send current Audio infoframe values while updating */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, - BIT(5), BIT(5)); - - regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(0), 0x1); - - /* use Audio infoframe updated info */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, - BIT(5), 0); - - return 0; + return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, + &hparms->cea); } int adv7511_hdmi_audio_startup(struct drm_bridge *bridge, @@ -188,15 +181,9 @@ int adv7511_hdmi_audio_startup(struct drm_bridge *bridge, /* not copyrighted */ regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1, BIT(5), BIT(5)); - /* enable audio infoframes */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, - BIT(3), BIT(3)); /* AV mute disable */ regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0), BIT(7) | BIT(6), BIT(7)); - /* use Audio infoframe updated info */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, - BIT(5), 0); /* enable SPDIF receiver */ if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) @@ -214,4 +201,6 @@ void adv7511_hdmi_audio_shutdown(struct drm_bridge *bridge, if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(7), 0); + + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector); } diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 00d6417c177b..b9be86541307 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -132,6 +132,13 @@ static const struct regmap_config adv7511_regmap_config = { .volatile_reg = adv7511_register_volatile, }; +static const struct regmap_config adv7511_packet_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, +}; + /* ----------------------------------------------------------------------------- * Hardware configuration */ @@ -886,9 +893,18 @@ static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge, struct adv7511 *adv7511 = bridge_to_adv7511(bridge); switch (type) { + case HDMI_INFOFRAME_TYPE_AUDIO: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME); + break; case HDMI_INFOFRAME_TYPE_AVI: adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); break; + case HDMI_INFOFRAME_TYPE_SPD: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1); + break; default: drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); break; @@ -903,16 +919,52 @@ static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge, { struct adv7511 *adv7511 = bridge_to_adv7511(bridge); - adv7511_bridge_hdmi_clear_infoframe(bridge, type); - switch (type) { + case HDMI_INFOFRAME_TYPE_AUDIO: + /* send current Audio infoframe values while updating */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, + BIT(5), BIT(5)); + + /* The Audio infoframe id is not configurable */ + regmap_bulk_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_VERSION, + buffer + 1, len - 1); + + /* use Audio infoframe updated info */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, + BIT(5), 0); + + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME); + break; case HDMI_INFOFRAME_TYPE_AVI: + /* send current AVI infoframe values while updating */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, + BIT(6), BIT(6)); + /* The AVI infoframe id is not configurable */ regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, buffer + 1, len - 1); + regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_LENGTH, 0x2); + regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(1), 0x1); + + /* use AVI infoframe updated info */ + regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, + BIT(6), 0); + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); break; + case HDMI_INFOFRAME_TYPE_SPD: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD); + regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0), + buffer, len); + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1); + regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0), + buffer, len); + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1); + break; default: drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); break; @@ -1242,6 +1294,13 @@ static int adv7511_probe(struct i2c_client *i2c) goto err_i2c_unregister_edid; } + adv7511->regmap_packet = devm_regmap_init_i2c(adv7511->i2c_packet, + &adv7511_packet_config); + if (IS_ERR(adv7511->regmap_packet)) { + ret = PTR_ERR(adv7511->regmap_packet); + goto err_i2c_unregister_packet; + } + regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, adv7511->i2c_packet->addr << 1); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index ed35e567d117..efe534977d12 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1474,8 +1474,8 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) dp = devm_drm_bridge_alloc(dev, struct analogix_dp_device, bridge, &analogix_dp_bridge_funcs); - if (!dp) - return ERR_PTR(-ENOMEM); + if (IS_ERR(dp)) + return ERR_CAST(dp); dp->dev = &pdev->dev; dp->dpms_mode = DRM_MODE_DPMS_OFF; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index c0ad8f59e483..609cdb9d371e 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2604,6 +2604,7 @@ static int anx7625_link_bridge(struct drm_dp_aux *aux) platform->bridge.type = platform->pdata.panel_bridge ? DRM_MODE_CONNECTOR_eDP : DRM_MODE_CONNECTOR_DisplayPort; + platform->bridge.support_hdcp = true; drm_bridge_add(&platform->bridge); diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c index e579f947e15b..2e9c702c7087 100644 --- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c +++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c @@ -65,10 +65,11 @@ struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent, str adev->id = ret; adev->name = "dp_hpd_bridge"; adev->dev.parent = parent; - adev->dev.of_node = of_node_get(parent->of_node); adev->dev.release = drm_aux_hpd_bridge_release; adev->dev.platform_data = of_node_get(np); + device_set_of_node_from_dev(&adev->dev, parent); + ret = auxiliary_device_init(adev); if (ret) { of_node_put(adev->dev.platform_data); diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index cced81633ddc..f1d8a8a151d8 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -6,6 +6,7 @@ config DRM_CDNS_DSI select DRM_PANEL_BRIDGE select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY + select VIDEOMODE_HELPERS depends on OF help Support Cadence DPI to DSI bridge. This is an internal diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index a57ca8c3bdae..09b289f0fcbf 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -9,6 +9,7 @@ #include #include #include

), ptr.cast()) + }) +} + +/// Encapsulate `data` in a [`KBox`] and [`Drop::drop`] `data` once `dev` is unbound. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::{device::{Bound, Device}, devres}; +/// +/// /// Registration of e.g. a class device, IRQ, etc. +/// struct Registration; +/// +/// impl Registration { +/// fn new() -> Self { +/// // register +/// +/// Self +/// } +/// } +/// +/// impl Drop for Registration { +/// fn drop(&mut self) { +/// // unregister +/// } +/// } +/// +/// fn from_bound_context(dev: &Device) -> Result { +/// devres::register(dev, Registration::new(), GFP_KERNEL) +/// } +/// ``` +pub fn register(dev: &Device, data: impl PinInit, flags: Flags) -> Result +where + T: Send + 'static, + Error: From, +{ + let data = KBox::pin_init(data, flags)?; + + register_foreign(dev, data) } diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 25dfa0e6cc3c..2bc8ab51ec28 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -5,14 +5,156 @@ //! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h) use crate::{ - bindings, build_assert, - device::{Bound, Device}, - error::code::*, - error::Result, + bindings, build_assert, device, + device::{Bound, Core}, + error::{to_result, Result}, + prelude::*, transmute::{AsBytes, FromBytes}, types::ARef, }; +/// Trait to be implemented by DMA capable bus devices. +/// +/// The [`dma::Device`](Device) trait should be implemented by bus specific device representations, +/// where the underlying bus is DMA capable, such as [`pci::Device`](::kernel::pci::Device) or +/// [`platform::Device`](::kernel::platform::Device). +pub trait Device: AsRef> { + /// Set up the device's DMA streaming addressing capabilities. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_mask(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { bindings::dma_set_mask(self.as_ref().as_raw(), mask.value()) }) + } + + /// Set up the device's DMA coherent addressing capabilities. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_coherent_mask(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { bindings::dma_set_coherent_mask(self.as_ref().as_raw(), mask.value()) }) + } + + /// Set up the device's DMA addressing capabilities. + /// + /// This is a combination of [`Device::dma_set_mask`] and [`Device::dma_set_coherent_mask`]. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_mask_and_coherent(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { + bindings::dma_set_mask_and_coherent(self.as_ref().as_raw(), mask.value()) + }) + } +} + +/// A DMA mask that holds a bitmask with the lowest `n` bits set. +/// +/// Use [`DmaMask::new`] or [`DmaMask::try_new`] to construct a value. Values +/// are guaranteed to never exceed the bit width of `u64`. +/// +/// This is the Rust equivalent of the C macro `DMA_BIT_MASK()`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DmaMask(u64); + +impl DmaMask { + /// Constructs a `DmaMask` with the lowest `n` bits set to `1`. + /// + /// For `n <= 64`, sets exactly the lowest `n` bits. + /// For `n > 64`, results in a build error. + /// + /// # Examples + /// + /// ``` + /// use kernel::dma::DmaMask; + /// + /// let mask0 = DmaMask::new::<0>(); + /// assert_eq!(mask0.value(), 0); + /// + /// let mask1 = DmaMask::new::<1>(); + /// assert_eq!(mask1.value(), 0b1); + /// + /// let mask64 = DmaMask::new::<64>(); + /// assert_eq!(mask64.value(), u64::MAX); + /// + /// // Build failure. + /// // let mask_overflow = DmaMask::new::<100>(); + /// ``` + #[inline] + pub const fn new() -> Self { + let Ok(mask) = Self::try_new(N) else { + build_error!("Invalid DMA Mask."); + }; + + mask + } + + /// Constructs a `DmaMask` with the lowest `n` bits set to `1`. + /// + /// For `n <= 64`, sets exactly the lowest `n` bits. + /// For `n > 64`, returns [`EINVAL`]. + /// + /// # Examples + /// + /// ``` + /// use kernel::dma::DmaMask; + /// + /// let mask0 = DmaMask::try_new(0)?; + /// assert_eq!(mask0.value(), 0); + /// + /// let mask1 = DmaMask::try_new(1)?; + /// assert_eq!(mask1.value(), 0b1); + /// + /// let mask64 = DmaMask::try_new(64)?; + /// assert_eq!(mask64.value(), u64::MAX); + /// + /// let mask_overflow = DmaMask::try_new(100); + /// assert!(mask_overflow.is_err()); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub const fn try_new(n: u32) -> Result { + Ok(Self(match n { + 0 => 0, + 1..=64 => u64::MAX >> (64 - n), + _ => return Err(EINVAL), + })) + } + + /// Returns the underlying `u64` bitmask value. + #[inline] + pub const fn value(&self) -> u64 { + self.0 + } +} + /// Possible attributes associated with a DMA mapping. /// /// They can be combined with the operators `|`, `&`, and `!`. @@ -38,7 +180,7 @@ impl Attrs { /// Get the raw representation of this attribute. pub(crate) fn as_raw(self) -> crate::ffi::c_ulong { - self.0 as _ + self.0 as crate::ffi::c_ulong } /// Check whether `flags` is contained in `self`. @@ -132,7 +274,7 @@ pub mod attrs { // Hence, find a way to revoke the device resources of a `CoherentAllocation`, but not the // entire `CoherentAllocation` including the allocated memory itself. pub struct CoherentAllocation { - dev: ARef, + dev: ARef, dma_handle: bindings::dma_addr_t, count: usize, cpu_addr: *mut T, @@ -154,7 +296,7 @@ impl CoherentAllocation { /// # Ok::<(), Error>(()) } /// ``` pub fn alloc_attrs( - dev: &Device, + dev: &device::Device, count: usize, gfp_flags: kernel::alloc::Flags, dma_attrs: Attrs, @@ -191,7 +333,7 @@ pub fn alloc_attrs( dev: dev.into(), dma_handle, count, - cpu_addr: ret as *mut T, + cpu_addr: ret.cast::(), dma_attrs, }) } @@ -199,7 +341,7 @@ pub fn alloc_attrs( /// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the /// `dma_attrs` is 0 by default. pub fn alloc_coherent( - dev: &Device, + dev: &device::Device, count: usize, gfp_flags: kernel::alloc::Flags, ) -> Result> { @@ -294,7 +436,7 @@ pub unsafe fn as_slice(&self, offset: usize, count: usize) -> Result<&[T]> { /// slice is live. /// * Callers must ensure that this call does not race with a read or write to the same region /// while the returned slice is live. - pub unsafe fn as_slice_mut(&self, offset: usize, count: usize) -> Result<&mut [T]> { + pub unsafe fn as_slice_mut(&mut self, offset: usize, count: usize) -> Result<&mut [T]> { self.validate_range(offset, count)?; // SAFETY: // - The pointer is valid due to type invariant on `CoherentAllocation`, @@ -326,7 +468,7 @@ pub unsafe fn as_slice_mut(&self, offset: usize, count: usize) -> Result<&mut [T /// unsafe { alloc.write(buf, 0)?; } /// # Ok::<(), Error>(()) } /// ``` - pub unsafe fn write(&self, src: &[T], offset: usize) -> Result { + pub unsafe fn write(&mut self, src: &[T], offset: usize) -> Result { self.validate_range(offset, src.len())?; // SAFETY: // - The pointer is valid due to type invariant on `CoherentAllocation` @@ -414,7 +556,7 @@ fn drop(&mut self) { bindings::dma_free_attrs( self.dev.as_raw(), size, - self.cpu_addr as _, + self.cpu_addr.cast(), self.dma_handle, self.dma_attrs.as_raw(), ) diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index ec9166cedfa7..279e3af20682 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -2,11 +2,96 @@ //! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.). //! -//! Each bus / subsystem is expected to implement [`RegistrationOps`], which allows drivers to -//! register using the [`Registration`] class. +//! This documentation describes how to implement a bus specific driver API and how to align it with +//! the design of (bus specific) devices. +//! +//! Note: Readers are expected to know the content of the documentation of [`Device`] and +//! [`DeviceContext`]. +//! +//! # Driver Trait +//! +//! The main driver interface is defined by a bus specific driver trait. For instance: +//! +//! ```ignore +//! pub trait Driver: Send { +//! /// The type holding information about each device ID supported by the driver. +//! type IdInfo: 'static; +//! +//! /// The table of OF device ids supported by the driver. +//! const OF_ID_TABLE: Option> = None; +//! +//! /// The table of ACPI device ids supported by the driver. +//! const ACPI_ID_TABLE: Option> = None; +//! +//! /// Driver probe. +//! fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; +//! +//! /// Driver unbind (optional). +//! fn unbind(dev: &Device, this: Pin<&Self>) { +//! let _ = (dev, this); +//! } +//! } +//! ``` +//! +//! For specific examples see [`auxiliary::Driver`], [`pci::Driver`] and [`platform::Driver`]. +//! +//! The `probe()` callback should return a `Result>>`, i.e. the driver's private +//! data. The bus abstraction should store the pointer in the corresponding bus device. The generic +//! [`Device`] infrastructure provides common helpers for this purpose on its +//! [`Device`] implementation. +//! +//! All driver callbacks should provide a reference to the driver's private data. Once the driver +//! is unbound from the device, the bus abstraction should take back the ownership of the driver's +//! private data from the corresponding [`Device`] and [`drop`] it. +//! +//! All driver callbacks should provide a [`Device`] reference (see also [`device::Core`]). +//! +//! # Adapter +//! +//! The adapter implementation of a bus represents the abstraction layer between the C bus +//! callbacks and the Rust bus callbacks. It therefore has to be generic over an implementation of +//! the [driver trait](#driver-trait). +//! +//! ```ignore +//! pub struct Adapter; +//! ``` +//! +//! There's a common [`Adapter`] trait that can be implemented to inherit common driver +//! infrastructure, such as finding the ID info from an [`of::IdTable`] or [`acpi::IdTable`]. +//! +//! # Driver Registration +//! +//! In order to register C driver types (such as `struct platform_driver`) the [adapter](#adapter) +//! should implement the [`RegistrationOps`] trait. +//! +//! This trait implementation can be used to create the actual registration with the common +//! [`Registration`] type. +//! +//! Typically, bus abstractions want to provide a bus specific `module_bus_driver!` macro, which +//! creates a kernel module with exactly one [`Registration`] for the bus specific adapter. +//! +//! The generic driver infrastructure provides a helper for this with the [`module_driver`] macro. +//! +//! # Device IDs +//! +//! Besides the common device ID types, such as [`of::DeviceId`] and [`acpi::DeviceId`], most buses +//! may need to implement their own device ID types. +//! +//! For this purpose the generic infrastructure in [`device_id`] should be used. +//! +//! [`auxiliary::Driver`]: kernel::auxiliary::Driver +//! [`Core`]: device::Core +//! [`Device`]: device::Device +//! [`Device`]: device::Device +//! [`Device`]: device::Device +//! [`DeviceContext`]: device::DeviceContext +//! [`device_id`]: kernel::device_id +//! [`module_driver`]: kernel::module_driver +//! [`pci::Driver`]: kernel::pci::Driver +//! [`platform::Driver`]: kernel::platform::Driver use crate::error::{Error, Result}; -use crate::{device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; +use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; use core::pin::Pin; use pin_init::{pin_data, pinned_drop, PinInit}; @@ -141,36 +226,76 @@ pub trait Adapter { /// The type holding driver private data about each device id supported by the driver. type IdInfo: 'static; + /// The [`acpi::IdTable`] of the corresponding driver + fn acpi_id_table() -> Option>; + + /// Returns the driver's private data from the matching entry in the [`acpi::IdTable`], if any. + /// + /// If this returns `None`, it means there is no match with an entry in the [`acpi::IdTable`]. + fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + #[cfg(not(CONFIG_ACPI))] + { + let _ = dev; + None + } + + #[cfg(CONFIG_ACPI)] + { + let table = Self::acpi_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. + let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct acpi_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + Some(table.info(::index(id))) + } + } + } + /// The [`of::IdTable`] of the corresponding driver. fn of_id_table() -> Option>; /// Returns the driver's private data from the matching entry in the [`of::IdTable`], if any. /// /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`]. - #[cfg(CONFIG_OF)] fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { - let table = Self::of_id_table()?; - - // SAFETY: - // - `table` has static lifetime, hence it's valid for read, - // - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. - let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; - - if raw_id.is_null() { + #[cfg(not(CONFIG_OF))] + { + let _ = dev; None - } else { - // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and - // does not add additional invariants, so it's safe to transmute. - let id = unsafe { &*raw_id.cast::() }; - - Some(table.info(::index(id))) } - } - #[cfg(not(CONFIG_OF))] - #[allow(missing_docs)] - fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { - None + #[cfg(CONFIG_OF)] + { + let table = Self::of_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + Some( + table.info(::index( + id, + )), + ) + } + } } /// Returns the driver's private data from the matching entry of any of the ID tables, if any. @@ -178,6 +303,11 @@ fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { /// If this returns `None`, it means that there is no match in any of the ID tables directly /// associated with a [`device::Device`]. fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + let id = Self::acpi_id_info(dev); + if id.is_some() { + return id; + } + let id = Self::of_id_info(dev); if id.is_some() { return id; diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 98418f4ce625..bb8e43c77dc9 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -5,14 +5,16 @@ //! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h) use crate::{ + alloc::allocator::Kmalloc, bindings, device, drm, drm::driver::AllocImpl, error::from_err_ptr, error::Result, prelude::*, - types::{ARef, AlwaysRefCounted, Opaque}, + sync::aref::{ARef, AlwaysRefCounted}, + types::Opaque, }; -use core::{mem, ops::Deref, ptr, ptr::NonNull}; +use core::{alloc::Layout, mem, ops::Deref, ptr, ptr::NonNull}; #[cfg(CONFIG_DRM_LEGACY)] macro_rules! drm_legacy_fields { @@ -53,10 +55,8 @@ macro_rules! drm_legacy_fields { /// /// `self.dev` is a valid instance of a `struct device`. #[repr(C)] -#[pin_data] pub struct Device { dev: Opaque, - #[pin] data: T::Data, } @@ -66,7 +66,7 @@ impl Device { open: Some(drm::File::::open_callback), postclose: Some(drm::File::::postclose_callback), unload: None, - release: None, + release: Some(Self::release), master_set: None, master_drop: None, debugfs_init: None, @@ -83,19 +83,23 @@ impl Device { major: T::INFO.major, minor: T::INFO.minor, patchlevel: T::INFO.patchlevel, - name: T::INFO.name.as_char_ptr() as *mut _, - desc: T::INFO.desc.as_char_ptr() as *mut _, + name: T::INFO.name.as_char_ptr().cast_mut(), + desc: T::INFO.desc.as_char_ptr().cast_mut(), driver_features: drm::driver::FEAT_GEM, ioctls: T::IOCTLS.as_ptr(), num_ioctls: T::IOCTLS.len() as i32, - fops: &Self::GEM_FOPS as _, + fops: &Self::GEM_FOPS, }; const GEM_FOPS: bindings::file_operations = drm::gem::create_fops(); /// Create a new `drm::Device` for a `drm::Driver`. pub fn new(dev: &device::Device, data: impl PinInit) -> Result> { + // `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence ensure a `kmalloc()` + // compatible `Layout`. + let layout = Kmalloc::aligned_layout(Layout::new::()); + // SAFETY: // - `VTABLE`, as a `const` is pinned to the read-only section of the compilation, // - `dev` is valid by its type invarants, @@ -103,7 +107,7 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result(), + layout.size(), mem::offset_of!(Self, dev), ) } @@ -117,9 +121,13 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result *mut bindings::drm_device { /// /// `ptr` must be a valid pointer to a `struct device` embedded in `Self`. unsafe fn from_drm_device(ptr: *const bindings::drm_device) -> *mut Self { - let ptr: *const Opaque = ptr.cast(); - // SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a // `struct drm_device` embedded in `Self`. - unsafe { crate::container_of!(ptr, Self, dev) }.cast_mut() + unsafe { crate::container_of!(Opaque::cast_from(ptr), Self, dev) }.cast_mut() + } + + /// # Safety + /// + /// `ptr` must be a valid pointer to `Self`. + unsafe fn into_drm_device(ptr: NonNull) -> *mut bindings::drm_device { + // SAFETY: By the safety requirements of this function, `ptr` is a valid pointer to `Self`. + unsafe { &raw mut (*ptr.as_ptr()).dev }.cast() } /// Not intended to be called externally, except via declare_drm_ioctls!() @@ -162,6 +176,16 @@ pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_device) -> &'a Self { // SAFETY: `ptr` is valid by the safety requirements of this function. unsafe { &*ptr.cast() } } + + extern "C" fn release(ptr: *mut bindings::drm_device) { + // SAFETY: `ptr` is a valid pointer to a `struct drm_device` and embedded in `Self`. + let this = unsafe { Self::from_drm_device(ptr) }; + + // SAFETY: + // - When `release` runs it is guaranteed that there is no further access to `this`. + // - `this` is valid for dropping. + unsafe { core::ptr::drop_in_place(this) }; + } } impl Deref for Device { @@ -181,8 +205,11 @@ fn inc_ref(&self) { } unsafe fn dec_ref(obj: NonNull) { + // SAFETY: `obj` is a valid pointer to `Self`. + let drm_dev = unsafe { Self::into_drm_device(obj) }; + // SAFETY: The safety requirements guarantee that the refcount is non-zero. - unsafe { bindings::drm_dev_put(obj.cast().as_ptr()) }; + unsafe { bindings::drm_dev_put(drm_dev) }; } } @@ -190,7 +217,7 @@ impl AsRef for Device { fn as_ref(&self) -> &device::Device { // SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid, // which is guaranteed by the type invariant. - unsafe { device::Device::as_ref((*self.as_raw()).dev) } + unsafe { device::Device::from_raw((*self.as_raw()).dev) } } } diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index af93d46d03d3..8fefae41bcc6 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -5,12 +5,10 @@ //! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h) use crate::{ - bindings, device, - devres::Devres, - drm, + bindings, device, devres, drm, error::{to_result, Result}, prelude::*, - types::ARef, + sync::aref::ARef, }; use macros::vtable; @@ -129,18 +127,22 @@ fn new(drm: &drm::Device, flags: usize) -> Result { } /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to - /// [`Devres`]. + /// [`devres::register`]. pub fn new_foreign_owned( drm: &drm::Device, dev: &device::Device, flags: usize, - ) -> Result { + ) -> Result + where + T: 'static, + { if drm.as_ref().as_raw() != dev.as_raw() { return Err(EINVAL); } let reg = Registration::::new(drm, flags)?; - Devres::new_foreign_owned(dev, reg, GFP_KERNEL) + + devres::register(dev, reg, GFP_KERNEL) } /// Returns a reference to the `Device` instance for this registration. diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index a24c9a2fc201..a822aedee949 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -10,7 +10,8 @@ drm::driver::{AllocImpl, AllocOps}, error::{to_result, Result}, prelude::*, - types::{ARef, AlwaysRefCounted, Opaque}, + sync::aref::{ARef, AlwaysRefCounted}, + types::Opaque, }; use core::{mem, ops::Deref, ptr::NonNull}; @@ -125,11 +126,9 @@ fn as_raw(&self) -> *mut bindings::drm_gem_object { } unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self { - let self_ptr: *mut Opaque = self_ptr.cast(); - // SAFETY: `obj` is guaranteed to be in an `Object` via the safety contract of this // function - unsafe { &*crate::container_of!(self_ptr, Object, obj) } + unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Object, obj) } } } diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs index fdec01c37168..af1bb29cf06d 100644 --- a/rust/kernel/drm/ioctl.rs +++ b/rust/kernel/drm/ioctl.rs @@ -83,7 +83,7 @@ pub mod internal { /// /// ```ignore /// fn foo(device: &kernel::drm::Device, -/// data: &Opaque, +/// data: &mut uapi::argument_type, /// file: &kernel::drm::File, /// ) -> Result /// ``` @@ -138,9 +138,12 @@ macro_rules! declare_drm_ioctls { // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we // asserted above matches the size of this type, and all bit patterns of // UAPI structs must be valid. - let data = unsafe { - &*(raw_data as *const $crate::types::Opaque<$crate::uapi::$struct>) - }; + // The `ioctl` argument is exclusively owned by the handler + // and guaranteed by the C implementation (`drm_ioctl()`) to remain + // valid for the entire lifetime of the reference taken here. + // There is no concurrent access or aliasing; no other references + // to this object exist during this call. + let data = unsafe { &mut *(raw_data.cast::<$crate::uapi::$struct>()) }; // SAFETY: This is just the DRM file structure let file = unsafe { $crate::drm::File::from_raw(raw_file) }; diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 083c7b068cf4..a41de293dcd1 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -6,10 +6,10 @@ use crate::{ alloc::{layout::LayoutError, AllocError}, + fmt, str::CStr, }; -use core::fmt; use core::num::NonZeroI32; use core::num::TryFromIntError; use core::str::Utf8Error; @@ -154,7 +154,7 @@ pub(crate) fn to_blk_status(self) -> bindings::blk_status_t { /// Returns the error encoded as a pointer. pub fn to_ptr(self) -> *mut T { // SAFETY: `self.0` is a valid error due to its invariant. - unsafe { bindings::ERR_PTR(self.0.get() as _) as *mut _ } + unsafe { bindings::ERR_PTR(self.0.get() as crate::ffi::c_long).cast() } } /// Returns a string representing the error, if one exists. @@ -189,7 +189,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Some(name) => f .debug_tuple( // SAFETY: These strings are ASCII-only. - unsafe { core::str::from_utf8_unchecked(name) }, + unsafe { core::str::from_utf8_unchecked(name.to_bytes()) }, ) .finish(), } @@ -220,8 +220,8 @@ fn from(_: LayoutError) -> Error { } } -impl From for Error { - fn from(_: core::fmt::Error) -> Error { +impl From for Error { + fn from(_: fmt::Error) -> Error { code::EINVAL } } diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs index 8a50fcd4c9bb..7fe2dd197e37 100644 --- a/rust/kernel/faux.rs +++ b/rust/kernel/faux.rs @@ -4,7 +4,7 @@ //! //! This module provides bindings for working with faux devices in kernel modules. //! -//! C header: [`include/linux/device/faux.h`] +//! C header: [`include/linux/device/faux.h`](srctree/include/linux/device/faux.h) use crate::{bindings, device, error::code::*, prelude::*}; use core::ptr::{addr_of_mut, null, null_mut, NonNull}; @@ -54,7 +54,7 @@ impl AsRef for Registration { fn as_ref(&self) -> &device::Device { // SAFETY: The underlying `device` in `faux_device` is guaranteed by the C API to be // a valid initialized `device`. - unsafe { device::Device::as_ref(addr_of_mut!((*self.as_raw()).dev)) } + unsafe { device::Device::from_raw(addr_of_mut!((*self.as_raw()).dev)) } } } diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs index 2494c96e105f..1abab5b2f052 100644 --- a/rust/kernel/firmware.rs +++ b/rust/kernel/firmware.rs @@ -62,10 +62,11 @@ impl Firmware { fn request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result { let mut fw: *mut bindings::firmware = core::ptr::null_mut(); let pfw: *mut *mut bindings::firmware = &mut fw; + let pfw: *mut *const bindings::firmware = pfw.cast(); // SAFETY: `pfw` is a valid pointer to a NULL initialized `bindings::firmware` pointer. // `name` and `dev` are valid as by their type invariants. - let ret = unsafe { func.0(pfw as _, name.as_char_ptr(), dev.as_raw()) }; + let ret = unsafe { func.0(pfw, name.as_char_ptr(), dev.as_raw()) }; if ret != 0 { return Err(Error::from_errno(ret)); } @@ -139,7 +140,7 @@ unsafe impl Sync for Firmware {} /// Typically, such contracts would be enforced by a trait, however traits do not (yet) support /// const functions. /// -/// # Example +/// # Examples /// /// ``` /// # mod module_firmware_test { @@ -181,7 +182,7 @@ unsafe impl Sync for Firmware {} /// module! { /// type: MyModule, /// name: "module_firmware_test", -/// author: "Rust for Linux", +/// authors: ["Rust for Linux"], /// description: "module_firmware! test module", /// license: "GPL", /// } @@ -202,7 +203,7 @@ macro_rules! module_firmware { }; #[link_section = ".modinfo"] - #[used] + #[used(compiler)] static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX) .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build(); }; @@ -261,7 +262,7 @@ const fn push_internal(mut self, bytes: &[u8]) -> Self { /// Append path components to the [`ModInfoBuilder`] instance. Paths need to be separated /// with [`ModInfoBuilder::new_entry`]. /// - /// # Example + /// # Examples /// /// ``` /// use kernel::firmware::ModInfoBuilder; diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs new file mode 100644 index 000000000000..0306e8388968 --- /dev/null +++ b/rust/kernel/fmt.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Formatting utilities. +//! +//! This module is intended to be used in place of `core::fmt` in kernel code. + +pub use core::fmt::{Arguments, Debug, Display, Error, Formatter, Result, Write}; diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 72d84fb0e266..35fd5db35c46 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -366,7 +366,7 @@ fn deref(&self) -> &LocalFile { // // By the type invariants, there are no `fdget_pos` calls that did not take the // `f_pos_lock` mutex. - unsafe { LocalFile::from_raw_file(self as *const File as *const bindings::file) } + unsafe { LocalFile::from_raw_file(core::ptr::from_ref(self).cast()) } } } diff --git a/rust/kernel/generated_arch_reachable_asm.rs.S b/rust/kernel/generated_arch_reachable_asm.rs.S new file mode 100644 index 000000000000..3886a9ad3a99 --- /dev/null +++ b/rust/kernel/generated_arch_reachable_asm.rs.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include + +// Cut here. + +::kernel::concat_literals!(ARCH_WARN_REACHABLE) diff --git a/rust/kernel/generated_arch_warn_asm.rs.S b/rust/kernel/generated_arch_warn_asm.rs.S new file mode 100644 index 000000000000..409eb4c2d3a1 --- /dev/null +++ b/rust/kernel/generated_arch_warn_asm.rs.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include + +// Cut here. + +::kernel::concat_literals!(ARCH_WARN_ASM("{file}", "{line}", "{flags}", "{size}")) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 8d228c237954..4949047af8d7 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -29,15 +29,15 @@ //! //! ## General Examples //! -//! ```rust,ignore -//! # #![allow(clippy::disallowed_names)] +//! ```rust +//! # #![expect(clippy::disallowed_names, clippy::undocumented_unsafe_blocks)] //! use kernel::types::Opaque; //! use pin_init::pin_init_from_closure; //! //! // assume we have some `raw_foo` type in C: //! #[repr(C)] //! struct RawFoo([u8; 16]); -//! extern { +//! extern "C" { //! fn init_foo(_: *mut RawFoo); //! } //! @@ -66,25 +66,17 @@ //! }); //! ``` //! -//! ```rust,ignore -//! # #![allow(unreachable_pub, clippy::disallowed_names)] +//! ```rust +//! # #![expect(unreachable_pub, clippy::disallowed_names)] //! use kernel::{prelude::*, types::Opaque}; //! use core::{ptr::addr_of_mut, marker::PhantomPinned, pin::Pin}; //! # mod bindings { -//! # #![allow(non_camel_case_types)] +//! # #![expect(non_camel_case_types, clippy::missing_safety_doc)] //! # pub struct foo; //! # pub unsafe fn init_foo(_ptr: *mut foo) {} //! # pub unsafe fn destroy_foo(_ptr: *mut foo) {} //! # pub unsafe fn enable_foo(_ptr: *mut foo, _flags: u32) -> i32 { 0 } //! # } -//! # // `Error::from_errno` is `pub(crate)` in the `kernel` crate, thus provide a workaround. -//! # trait FromErrno { -//! # fn from_errno(errno: core::ffi::c_int) -> Error { -//! # // Dummy error that can be constructed outside the `kernel` crate. -//! # Error::from(core::fmt::Error) -//! # } -//! # } -//! # impl FromErrno for Error {} //! /// # Invariants //! /// //! /// `foo` is always initialized @@ -108,13 +100,13 @@ //! let foo = addr_of_mut!((*slot).foo); //! //! // Initialize the `foo` -//! bindings::init_foo(Opaque::raw_get(foo)); +//! bindings::init_foo(Opaque::cast_into(foo)); //! //! // Try to enable it. -//! let err = bindings::enable_foo(Opaque::raw_get(foo), flags); +//! let err = bindings::enable_foo(Opaque::cast_into(foo), flags); //! if err != 0 { //! // Enabling has failed, first clean up the foo and then return the error. -//! bindings::destroy_foo(Opaque::raw_get(foo)); +//! bindings::destroy_foo(Opaque::cast_into(foo)); //! return Err(Error::from_errno(err)); //! } //! @@ -206,7 +198,7 @@ fn init(init: impl Init, flags: Flags) -> error::Result /// /// ```rust /// use kernel::error::Error; -/// use pin_init::zeroed; +/// use pin_init::init_zeroed; /// struct BigBuf { /// big: KBox<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], @@ -215,7 +207,7 @@ fn init(init: impl Init, flags: Flags) -> error::Result /// impl BigBuf { /// fn new() -> impl Init { /// try_init!(Self { -/// big: KBox::init(zeroed(), GFP_KERNEL)?, +/// big: KBox::init(init_zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// }? Error) /// } @@ -231,14 +223,14 @@ macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),* $(,)?>)? { + ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),* $(,)?>)? { + ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; @@ -264,7 +256,7 @@ macro_rules! try_init { /// ```rust /// # #![feature(new_uninit)] /// use kernel::error::Error; -/// use pin_init::zeroed; +/// use pin_init::init_zeroed; /// #[pin_data] /// struct BigBuf { /// big: KBox<[u8; 1024 * 1024 * 1024]>, @@ -275,7 +267,7 @@ macro_rules! try_init { /// impl BigBuf { /// fn new() -> impl PinInit { /// try_pin_init!(Self { -/// big: KBox::init(zeroed(), GFP_KERNEL)?, +/// big: KBox::init(init_zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// ptr: core::ptr::null_mut(), /// }? Error) @@ -291,14 +283,14 @@ macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),* $(,)?>)? { + ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),* $(,)?>)? { + ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 72d80a6f131e..03b467722b86 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -5,7 +5,12 @@ //! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h) use crate::error::{code::EINVAL, Result}; -use crate::{bindings, build_assert}; +use crate::{bindings, build_assert, ffi::c_void}; + +pub mod mem; +pub mod resource; + +pub use resource::Resource; /// Raw representation of an MMIO region. /// @@ -43,7 +48,7 @@ pub fn maxsize(&self) -> usize { } } -/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes. +/// IO-mapped memory region. /// /// The creator (usually a subsystem / bus such as PCI) is responsible for creating the /// mapping, performing an additional region request etc. @@ -56,7 +61,7 @@ pub fn maxsize(&self) -> usize { /// # Examples /// /// ```no_run -/// # use kernel::{bindings, io::{Io, IoRaw}}; +/// # use kernel::{bindings, ffi::c_void, io::{Io, IoRaw}}; /// # use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. @@ -70,19 +75,19 @@ pub fn maxsize(&self) -> usize { /// unsafe fn new(paddr: usize) -> Result{ /// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is /// // valid for `ioremap`. -/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) }; +/// let addr = unsafe { bindings::ioremap(paddr as bindings::phys_addr_t, SIZE) }; /// if addr.is_null() { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?)) +/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) /// } /// } /// /// impl Drop for IoMem { /// fn drop(&mut self) { /// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`. -/// unsafe { bindings::iounmap(self.0.addr() as _); }; +/// unsafe { bindings::iounmap(self.0.addr() as *mut c_void); }; /// } /// } /// @@ -119,7 +124,7 @@ pub fn $name(&self, offset: usize) -> $type_name { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(addr as _) } + unsafe { bindings::$c_fn(addr as *const c_void) } } /// Read IO data from a given offset. @@ -131,7 +136,7 @@ pub fn $try_name(&self, offset: usize) -> Result<$type_name> { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - Ok(unsafe { bindings::$c_fn(addr as _) }) + Ok(unsafe { bindings::$c_fn(addr as *const c_void) }) } }; } @@ -148,7 +153,7 @@ pub fn $name(&self, value: $type_name, offset: usize) { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(value, addr as _, ) } + unsafe { bindings::$c_fn(value, addr as *mut c_void) } } /// Write IO data from a given offset. @@ -160,7 +165,7 @@ pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(value, addr as _) } + unsafe { bindings::$c_fn(value, addr as *mut c_void) } Ok(()) } }; diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs new file mode 100644 index 000000000000..6f99510bfc3a --- /dev/null +++ b/rust/kernel/io/mem.rs @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic memory-mapped IO. + +use core::ops::Deref; + +use crate::c_str; +use crate::device::Bound; +use crate::device::Device; +use crate::devres::Devres; +use crate::io; +use crate::io::resource::Region; +use crate::io::resource::Resource; +use crate::io::Io; +use crate::io::IoRaw; +use crate::prelude::*; + +/// An IO request for a specific device and resource. +pub struct IoRequest<'a> { + device: &'a Device, + resource: &'a Resource, +} + +impl<'a> IoRequest<'a> { + /// Creates a new [`IoRequest`] instance. + /// + /// # Safety + /// + /// Callers must ensure that `resource` is valid for `device` during the + /// lifetime `'a`. + pub(crate) unsafe fn new(device: &'a Device, resource: &'a Resource) -> Self { + IoRequest { device, resource } + } + + /// Maps an [`IoRequest`] where the size is known at compile time. + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`kernel::platform::Device`] for + /// illustration purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo = (); + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset = 0; // Some offset. + /// + /// // If the size is known at compile time, use [`Self::iomap_sized`]. + /// // + /// // No runtime checks will apply when reading and writing. + /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; + /// let iomem = request.iomap_sized::<42>(); + /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io = iomem.access(pdev.as_ref())?; + /// + /// // Read and write a 32-bit value at `offset`. + /// let data = io.read32_relaxed(offset); + /// + /// io.write32_relaxed(data, offset); + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap_sized(self) -> impl PinInit>, Error> + 'a { + IoMem::new(self) + } + + /// Same as [`Self::iomap_sized`] but with exclusive access to the + /// underlying region. + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + pub fn iomap_exclusive_sized( + self, + ) -> impl PinInit>, Error> + 'a { + ExclusiveIoMem::new(self) + } + + /// Maps an [`IoRequest`] where the size is not known at compile time, + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`kernel::platform::Device`] for + /// illustration purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo = (); + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset = 0; // Some offset. + /// + /// // Unlike [`Self::iomap_sized`], here the size of the memory region + /// // is not known at compile time, so only the `try_read*` and `try_write*` + /// // family of functions should be used, leading to runtime checks on every + /// // access. + /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; + /// let iomem = request.iomap(); + /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io = iomem.access(pdev.as_ref())?; + /// + /// let data = io.try_read32_relaxed(offset)?; + /// + /// io.try_write32_relaxed(data, offset)?; + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap(self) -> impl PinInit>, Error> + 'a { + Self::iomap_sized::<0>(self) + } + + /// Same as [`Self::iomap`] but with exclusive access to the underlying + /// region. + pub fn iomap_exclusive(self) -> impl PinInit>, Error> + 'a { + Self::iomap_exclusive_sized::<0>(self) + } +} + +/// An exclusive memory-mapped IO region. +/// +/// # Invariants +/// +/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. +pub struct ExclusiveIoMem { + /// The underlying `IoMem` instance. + iomem: IoMem, + + /// The region abstraction. This represents exclusive access to the + /// range represented by the underlying `iomem`. + /// + /// This field is needed for ownership of the region. + _region: Region, +} + +impl ExclusiveIoMem { + /// Creates a new `ExclusiveIoMem` instance. + fn ioremap(resource: &Resource) -> Result { + let start = resource.start(); + let size = resource.size(); + let name = resource.name().unwrap_or(c_str!("")); + + let region = resource + .request_region( + start, + size, + name.to_cstring()?, + io::resource::Flags::IORESOURCE_MEM, + ) + .ok_or(EBUSY)?; + + let iomem = IoMem::ioremap(resource)?; + + let iomem = ExclusiveIoMem { + iomem, + _region: region, + }; + + Ok(iomem) + } + + /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. + pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { + let dev = io_request.device; + let res = io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Deref for ExclusiveIoMem { + type Target = Io; + + fn deref(&self) -> &Self::Target { + &self.iomem + } +} + +/// A generic memory-mapped IO region. +/// +/// Accesses to the underlying region is checked either at compile time, if the +/// region's size is known at that point, or at runtime otherwise. +/// +/// # Invariants +/// +/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the +/// start of the I/O memory mapped region. +pub struct IoMem { + io: IoRaw, +} + +impl IoMem { + fn ioremap(resource: &Resource) -> Result { + // Note: Some ioremap() implementations use types that depend on the CPU + // word width rather than the bus address width. + // + // TODO: Properly address this in the C code to avoid this `try_into`. + let size = resource.size().try_into()?; + if size == 0 { + return Err(EINVAL); + } + + let res_start = resource.start(); + + let addr = if resource + .flags() + .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) + { + // SAFETY: + // - `res_start` and `size` are read from a presumably valid `struct resource`. + // - `size` is known not to be zero at this point. + unsafe { bindings::ioremap_np(res_start, size) } + } else { + // SAFETY: + // - `res_start` and `size` are read from a presumably valid `struct resource`. + // - `size` is known not to be zero at this point. + unsafe { bindings::ioremap(res_start, size) } + }; + + if addr.is_null() { + return Err(ENOMEM); + } + + let io = IoRaw::new(addr as usize, size)?; + let io = IoMem { io }; + + Ok(io) + } + + /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. + pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { + let dev = io_request.device; + let res = io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Drop for IoMem { + fn drop(&mut self) { + // SAFETY: Safe as by the invariant of `Io`. + unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } + } +} + +impl Deref for IoMem { + type Target = Io; + + fn deref(&self) -> &Self::Target { + // SAFETY: Safe as by the invariant of `IoMem`. + unsafe { Io::from_raw(&self.io) } + } +} diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs new file mode 100644 index 000000000000..bea3ee0ed87b --- /dev/null +++ b/rust/kernel/io/resource.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for [system +//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management). +//! +//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h) + +use core::ops::Deref; +use core::ptr::NonNull; + +use crate::prelude::*; +use crate::str::{CStr, CString}; +use crate::types::Opaque; + +/// Resource Size type. +/// +/// This is a type alias to either `u32` or `u64` depending on the config option +/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures. +pub type ResourceSize = bindings::phys_addr_t; + +/// A region allocated from a parent [`Resource`]. +/// +/// # Invariants +/// +/// - `self.0` points to a valid `bindings::resource` that was obtained through +/// `bindings::__request_region`. +pub struct Region { + /// The resource returned when the region was requested. + resource: NonNull, + /// The name that was passed in when the region was requested. We need to + /// store it for ownership reasons. + _name: CString, +} + +impl Deref for Region { + type Target = Resource; + + fn deref(&self) -> &Self::Target { + // SAFETY: Safe as per the invariant of `Region`. + unsafe { Resource::from_raw(self.resource.as_ptr()) } + } +} + +impl Drop for Region { + fn drop(&mut self) { + let (flags, start, size) = { + let res = &**self; + (res.flags(), res.start(), res.size()) + }; + + let release_fn = if flags.contains(Flags::IORESOURCE_MEM) { + bindings::release_mem_region + } else { + bindings::release_region + }; + + // SAFETY: Safe as per the invariant of `Region`. + unsafe { release_fn(start, size) }; + } +} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from +// any thread. +unsafe impl Send for Region {} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are +// safe to be used from any thread. +unsafe impl Sync for Region {} + +/// A resource abstraction. +/// +/// # Invariants +/// +/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`. +#[repr(transparent)] +pub struct Resource(Opaque); + +impl Resource { + /// Creates a reference to a [`Resource`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that for the duration of 'a, the pointer will + /// point at a valid `bindings::resource`. + /// + /// The caller must also ensure that the [`Resource`] is only accessed via the + /// returned reference for the duration of 'a. + pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self { + // SAFETY: Self is a transparent wrapper around `Opaque`. + unsafe { &*ptr.cast() } + } + + /// Requests a resource region. + /// + /// Exclusive access will be given and the region will be marked as busy. + /// Further calls to [`Self::request_region`] will return [`None`] if + /// the region, or a part of it, is already in use. + pub fn request_region( + &self, + start: ResourceSize, + size: ResourceSize, + name: CString, + flags: Flags, + ) -> Option { + // SAFETY: + // - Safe as per the invariant of `Resource`. + // - `__request_region` will store a reference to the name, but that is + // safe as we own it and it will not be dropped until the `Region` is + // dropped. + let region = unsafe { + bindings::__request_region( + self.0.get(), + start, + size, + name.as_char_ptr(), + flags.0 as c_int, + ) + }; + + Some(Region { + resource: NonNull::new(region)?, + _name: name, + }) + } + + /// Returns the size of the resource. + pub fn size(&self) -> ResourceSize { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + unsafe { bindings::resource_size(inner) } + } + + /// Returns the start address of the resource. + pub fn start(&self) -> ResourceSize { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + unsafe { (*inner).start } + } + + /// Returns the name of the resource. + pub fn name(&self) -> Option<&CStr> { + let inner = self.0.get(); + + // SAFETY: Safe as per the invariants of `Resource`. + let name = unsafe { (*inner).name }; + + if name.is_null() { + return None; + } + + // SAFETY: In the C code, `resource::name` either contains a null + // pointer or points to a valid NUL-terminated C string, and at this + // point we know it is not null, so we can safely convert it to a + // `CStr`. + Some(unsafe { CStr::from_char_ptr(name) }) + } + + /// Returns the flags associated with the resource. + pub fn flags(&self) -> Flags { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + let flags = unsafe { (*inner).flags }; + + Flags(flags) + } +} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is +// safe to be used from any thread. +unsafe impl Send for Resource {} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references +// to which are safe to be used from any thread. +unsafe impl Sync for Resource {} + +/// Resource flags as stored in the C `struct resource::flags` field. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the associated constants such as +/// [`Flags::IORESOURCE_IO`]. +#[derive(Clone, Copy, PartialEq)] +pub struct Flags(c_ulong); + +impl Flags { + /// Check whether `flags` is contained in `self`. + pub fn contains(self, flags: Flags) -> bool { + (self & flags) == flags + } +} + +impl core::ops::BitOr for Flags { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for Flags { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for Flags { + type Output = Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Flags { + /// PCI/ISA I/O ports. + pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO); + + /// Resource is software muxed. + pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED); + + /// Resource represents a memory region. + pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM); + + /// Resource represents a memory region that must be ioremaped using `ioremap_np`. + pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); + + const fn new(value: u32) -> Self { + crate::build_assert!(value as u64 <= c_ulong::MAX as u64); + Flags(value as c_ulong) + } +} diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 4b8cdcb21e77..41efd87595d6 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -7,7 +7,10 @@ //! Reference: use crate::prelude::*; -use core::{ffi::c_void, fmt}; +use core::fmt; + +#[cfg(CONFIG_PRINTK)] +use crate::c_str; /// Prints a KUnit error-level message. /// @@ -19,8 +22,8 @@ pub fn err(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c"\x013%pA".as_ptr() as _, - &args as *const _ as *const c_void, + c_str!("\x013%pA").as_char_ptr(), + core::ptr::from_ref(&args).cast::(), ); } } @@ -35,8 +38,8 @@ pub fn info(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c"\x016%pA".as_ptr() as _, - &args as *const _ as *const c_void, + c_str!("\x016%pA").as_char_ptr(), + core::ptr::from_ref(&args).cast::(), ); } } @@ -302,7 +305,7 @@ macro_rules! kunit_unsafe_test_suite { is_init: false, }; - #[used] + #[used(compiler)] #[allow(unused_unsafe)] #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")] static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite = diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c3..fef97f2a5098 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,12 +34,19 @@ // Expected to become stable. #![feature(arbitrary_self_types)] // +// To be determined. +#![feature(used_with_arg)] +// // `feature(derive_coerce_pointee)` is expected to become stable. Before Rust // 1.84.0, it did not exist, so enable the predecessor features. #![cfg_attr(CONFIG_RUSTC_HAS_COERCE_POINTEE, feature(derive_coerce_pointee))] #![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(coerce_unsized))] #![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(dispatch_from_dyn))] #![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(unsize))] +// +// `feature(file_with_nul)` is expected to become stable. Before Rust 1.89.0, it did not exist, so +// enable it conditionally. +#![cfg_attr(CONFIG_RUSTC_HAS_FILE_WITH_NUL, feature(file_with_nul))] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. @@ -51,11 +58,14 @@ pub use ffi; +pub mod acpi; pub mod alloc; #[cfg(CONFIG_AUXILIARY_BUS)] pub mod auxiliary; +pub mod bits; #[cfg(CONFIG_BLOCK)] pub mod block; +pub mod bug; #[doc(hidden)] pub mod build_assert; pub mod clk; @@ -77,6 +87,7 @@ pub mod faux; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; +pub mod fmt; pub mod fs; pub mod init; pub mod io; @@ -100,6 +111,7 @@ pub mod prelude; pub mod print; pub mod rbtree; +pub mod regulator; pub mod revocable; pub mod security; pub mod seq_file; @@ -204,6 +216,13 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! { /// Produces a pointer to an object from a pointer to one of its fields. /// +/// If you encounter a type mismatch due to the [`Opaque`] type, then use [`Opaque::cast_into`] or +/// [`Opaque::cast_from`] to resolve the mismatch. +/// +/// [`Opaque`]: crate::types::Opaque +/// [`Opaque::cast_into`]: crate::types::Opaque::cast_into +/// [`Opaque::cast_from`]: crate::types::Opaque::cast_from +/// /// # Safety /// /// The pointer passed to this macro, and the pointer returned by this macro, must both be in @@ -274,3 +293,52 @@ macro_rules! asm { ::core::arch::asm!( $($asm)*, $($rest)* ) }; } + +/// Gets the C string file name of a [`Location`]. +/// +/// If `Location::file_as_c_str()` is not available, returns a string that warns about it. +/// +/// [`Location`]: core::panic::Location +/// +/// # Examples +/// +/// ``` +/// # use kernel::file_from_location; +/// +/// #[track_caller] +/// fn foo() { +/// let caller = core::panic::Location::caller(); +/// +/// // Output: +/// // - A path like "rust/kernel/example.rs" if `file_as_c_str()` is available. +/// // - "" otherwise. +/// let caller_file = file_from_location(caller); +/// +/// // Prints out the message with caller's file name. +/// pr_info!("foo() called in file {caller_file:?}\n"); +/// +/// # if cfg!(CONFIG_RUSTC_HAS_FILE_WITH_NUL) { +/// # assert_eq!(Ok(caller.file()), caller_file.to_str()); +/// # } +/// } +/// +/// # foo(); +/// ``` +#[inline] +pub fn file_from_location<'a>(loc: &'a core::panic::Location<'a>) -> &'a core::ffi::CStr { + #[cfg(CONFIG_RUSTC_HAS_FILE_AS_C_STR)] + { + loc.file_as_c_str() + } + + #[cfg(all(CONFIG_RUSTC_HAS_FILE_WITH_NUL, not(CONFIG_RUSTC_HAS_FILE_AS_C_STR)))] + { + loc.file_with_nul() + } + + #[cfg(not(CONFIG_RUSTC_HAS_FILE_WITH_NUL))] + { + let _ = loc; + c"" + } +} diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs index c391c30b80f8..44e5219cfcbc 100644 --- a/rust/kernel/list.rs +++ b/rust/kernel/list.rs @@ -57,14 +57,11 @@ /// } /// } /// -/// impl_has_list_links! { -/// impl HasListLinks<0> for BasicItem { self.links } -/// } /// impl_list_arc_safe! { /// impl ListArcSafe<0> for BasicItem { untracked; } /// } /// impl_list_item! { -/// impl ListItem<0> for BasicItem { using ListLinks; } +/// impl ListItem<0> for BasicItem { using ListLinks { self.links }; } /// } /// /// // Create a new empty list. @@ -82,9 +79,9 @@ /// // [15, 10, 30] /// { /// let mut iter = list.iter(); -/// assert_eq!(iter.next().unwrap().value, 15); -/// assert_eq!(iter.next().unwrap().value, 10); -/// assert_eq!(iter.next().unwrap().value, 30); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 15); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 10); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 30); /// assert!(iter.next().is_none()); /// /// // Verify the length of the list. @@ -93,9 +90,9 @@ /// /// // Pop the items from the list using `pop_back()` and verify the content. /// { -/// assert_eq!(list.pop_back().unwrap().value, 30); -/// assert_eq!(list.pop_back().unwrap().value, 10); -/// assert_eq!(list.pop_back().unwrap().value, 15); +/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value, 30); +/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value, 10); +/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value, 15); /// } /// /// // Insert 3 elements using `push_front()`. @@ -107,9 +104,9 @@ /// // [30, 10, 15] /// { /// let mut iter = list.iter(); -/// assert_eq!(iter.next().unwrap().value, 30); -/// assert_eq!(iter.next().unwrap().value, 10); -/// assert_eq!(iter.next().unwrap().value, 15); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 30); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 10); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 15); /// assert!(iter.next().is_none()); /// /// // Verify the length of the list. @@ -118,8 +115,8 @@ /// /// // Pop the items from the list using `pop_front()` and verify the content. /// { -/// assert_eq!(list.pop_front().unwrap().value, 30); -/// assert_eq!(list.pop_front().unwrap().value, 10); +/// assert_eq!(list.pop_front().ok_or(EINVAL)?.value, 30); +/// assert_eq!(list.pop_front().ok_or(EINVAL)?.value, 10); /// } /// /// // Push `list2` to `list` through `push_all_back()`. @@ -135,9 +132,9 @@ /// // list: [15, 25, 35] /// // list2: [] /// let mut iter = list.iter(); -/// assert_eq!(iter.next().unwrap().value, 15); -/// assert_eq!(iter.next().unwrap().value, 25); -/// assert_eq!(iter.next().unwrap().value, 35); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 15); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 25); +/// assert_eq!(iter.next().ok_or(EINVAL)?.value, 35); /// assert!(iter.next().is_none()); /// assert!(list2.is_empty()); /// } @@ -284,7 +281,7 @@ pub fn new() -> impl PinInit { #[inline] unsafe fn fields(me: *mut Self) -> *mut ListLinksFields { // SAFETY: The caller promises that the pointer is valid. - unsafe { Opaque::raw_get(ptr::addr_of!((*me).inner)) } + unsafe { Opaque::cast_into(ptr::addr_of!((*me).inner)) } } /// # Safety @@ -320,9 +317,6 @@ unsafe impl Send for ListLinksSelfPtr {} unsafe impl Sync for ListLinksSelfPtr {} impl ListLinksSelfPtr { - /// The offset from the [`ListLinks`] to the self pointer field. - pub const LIST_LINKS_SELF_PTR_OFFSET: usize = core::mem::offset_of!(Self, self_ptr); - /// Creates a new initializer for this type. pub fn new() -> impl PinInit { // INVARIANT: Pin-init initializers can't be used on an existing `Arc`, so this value will @@ -337,6 +331,16 @@ pub fn new() -> impl PinInit { self_ptr: Opaque::uninit(), } } + + /// Returns a pointer to the self pointer. + /// + /// # Safety + /// + /// The provided pointer must point at a valid struct of type `Self`. + pub unsafe fn raw_get_self_ptr(me: *const Self) -> *const Opaque<*const T> { + // SAFETY: The caller promises that the pointer is valid. + unsafe { ptr::addr_of!((*me).self_ptr) } + } } impl, const ID: u64> List { @@ -711,14 +715,11 @@ fn next(&mut self) -> Option> { /// } /// } /// -/// kernel::list::impl_has_list_links! { -/// impl HasListLinks<0> for ListItem { self.links } -/// } /// kernel::list::impl_list_arc_safe! { /// impl ListArcSafe<0> for ListItem { untracked; } /// } /// kernel::list::impl_list_item! { -/// impl ListItem<0> for ListItem { using ListLinks; } +/// impl ListItem<0> for ListItem { using ListLinks { self.links }; } /// } /// /// // Use a cursor to remove the first element with the given value. @@ -809,11 +810,11 @@ fn next(&mut self) -> Option> { /// merge_sorted(&mut list, list2); /// /// let mut items = list.into_iter(); -/// assert_eq!(items.next().unwrap().value, 10); -/// assert_eq!(items.next().unwrap().value, 11); -/// assert_eq!(items.next().unwrap().value, 12); -/// assert_eq!(items.next().unwrap().value, 13); -/// assert_eq!(items.next().unwrap().value, 14); +/// assert_eq!(items.next().ok_or(EINVAL)?.value, 10); +/// assert_eq!(items.next().ok_or(EINVAL)?.value, 11); +/// assert_eq!(items.next().ok_or(EINVAL)?.value, 12); +/// assert_eq!(items.next().ok_or(EINVAL)?.value, 13); +/// assert_eq!(items.next().ok_or(EINVAL)?.value, 14); /// assert!(items.next().is_none()); /// # Result::<(), Error>::Ok(()) /// ``` diff --git a/rust/kernel/list/impl_list_item_mod.rs b/rust/kernel/list/impl_list_item_mod.rs index a0438537cee1..202bc6f97c13 100644 --- a/rust/kernel/list/impl_list_item_mod.rs +++ b/rust/kernel/list/impl_list_item_mod.rs @@ -4,60 +4,48 @@ //! Helpers for implementing list traits safely. -use crate::list::ListLinks; - -/// Declares that this type has a `ListLinks` field at a fixed offset. +/// Declares that this type has a [`ListLinks`] field. /// -/// This trait is only used to help implement `ListItem` safely. If `ListItem` is implemented +/// This trait is only used to help implement [`ListItem`] safely. If [`ListItem`] is implemented /// manually, then this trait is not needed. Use the [`impl_has_list_links!`] macro to implement /// this trait. /// /// # Safety /// -/// All values of this type must have a `ListLinks` field at the given offset. +/// The methods on this trait must have exactly the behavior that the definitions given below have. /// -/// The behavior of `raw_get_list_links` must not be changed. +/// [`ListLinks`]: crate::list::ListLinks +/// [`ListItem`]: crate::list::ListItem pub unsafe trait HasListLinks { - /// The offset of the `ListLinks` field. - const OFFSET: usize; - - /// Returns a pointer to the [`ListLinks`] field. + /// Returns a pointer to the [`ListLinks`] field. /// /// # Safety /// /// The provided pointer must point at a valid struct of type `Self`. /// - /// [`ListLinks`]: ListLinks - // We don't really need this method, but it's necessary for the implementation of - // `impl_has_list_links!` to be correct. - #[inline] - unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut ListLinks { - // SAFETY: The caller promises that the pointer is valid. The implementer promises that the - // `OFFSET` constant is correct. - unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut ListLinks } - } + /// [`ListLinks`]: crate::list::ListLinks + unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut crate::list::ListLinks; } /// Implements the [`HasListLinks`] trait for the given type. #[macro_export] macro_rules! impl_has_list_links { - ($(impl$(<$($implarg:ident),*>)? + ($(impl$({$($generics:tt)*})? HasListLinks$(<$id:tt>)? - for $self:ident $(<$($selfarg:ty),*>)? + for $self:ty { self$(.$field:ident)* } )*) => {$( // SAFETY: The implementation of `raw_get_list_links` only compiles if the field has the // right type. - // - // The behavior of `raw_get_list_links` is not changed since the `addr_of_mut!` macro is - // equivalent to the pointer offset operation in the trait definition. - unsafe impl$(<$($implarg),*>)? $crate::list::HasListLinks$(<$id>)? for - $self $(<$($selfarg),*>)? - { - const OFFSET: usize = ::core::mem::offset_of!(Self, $($field).*) as usize; - + unsafe impl$(<$($generics)*>)? $crate::list::HasListLinks$(<$id>)? for $self { #[inline] unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? { + // Statically ensure that `$(.field)*` doesn't follow any pointers. + // + // Cannot be `const` because `$self` may contain generics and E0401 says constants + // "can't use {`Self`,generic parameters} from outer item". + if false { let _: usize = ::core::mem::offset_of!(Self, $($field).*); } + // SAFETY: The caller promises that the pointer is not dangling. We know that this // expression doesn't follow any pointers, as the `offset_of!` invocation above // would otherwise not compile. @@ -68,12 +56,16 @@ unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$ } pub use impl_has_list_links; -/// Declares that the `ListLinks` field in this struct is inside a `ListLinksSelfPtr`. +/// Declares that the [`ListLinks`] field in this struct is inside a +/// [`ListLinksSelfPtr`]. /// /// # Safety /// -/// The `ListLinks` field of this struct at the offset `HasListLinks::OFFSET` must be -/// inside a `ListLinksSelfPtr`. +/// The [`ListLinks`] field of this struct at [`HasListLinks::raw_get_list_links`] must be +/// inside a [`ListLinksSelfPtr`]. +/// +/// [`ListLinks`]: crate::list::ListLinks +/// [`ListLinksSelfPtr`]: crate::list::ListLinksSelfPtr pub unsafe trait HasSelfPtr where Self: HasListLinks, @@ -83,27 +75,21 @@ pub unsafe trait HasSelfPtr /// Implements the [`HasListLinks`] and [`HasSelfPtr`] traits for the given type. #[macro_export] macro_rules! impl_has_list_links_self_ptr { - ($(impl$({$($implarg:tt)*})? + ($(impl$({$($generics:tt)*})? HasSelfPtr<$item_type:ty $(, $id:tt)?> - for $self:ident $(<$($selfarg:ty),*>)? - { self.$field:ident } + for $self:ty + { self$(.$field:ident)* } )*) => {$( // SAFETY: The implementation of `raw_get_list_links` only compiles if the field has the // right type. - unsafe impl$(<$($implarg)*>)? $crate::list::HasSelfPtr<$item_type $(, $id)?> for - $self $(<$($selfarg),*>)? - {} - - unsafe impl$(<$($implarg)*>)? $crate::list::HasListLinks$(<$id>)? for - $self $(<$($selfarg),*>)? - { - const OFFSET: usize = ::core::mem::offset_of!(Self, $field) as usize; + unsafe impl$(<$($generics)*>)? $crate::list::HasSelfPtr<$item_type $(, $id)?> for $self {} + unsafe impl$(<$($generics)*>)? $crate::list::HasListLinks$(<$id>)? for $self { #[inline] unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? { // SAFETY: The caller promises that the pointer is not dangling. let ptr: *mut $crate::list::ListLinksSelfPtr<$item_type $(, $id)?> = - unsafe { ::core::ptr::addr_of_mut!((*ptr).$field) }; + unsafe { ::core::ptr::addr_of_mut!((*ptr)$(.$field)*) }; ptr.cast() } } @@ -117,15 +103,95 @@ unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$ /// implement that trait. /// /// [`ListItem`]: crate::list::ListItem +/// +/// # Examples +/// +/// ``` +/// #[pin_data] +/// struct SimpleListItem { +/// value: u32, +/// #[pin] +/// links: kernel::list::ListLinks, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl ListArcSafe<0> for SimpleListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl ListItem<0> for SimpleListItem { using ListLinks { self.links }; } +/// } +/// +/// struct ListLinksHolder { +/// inner: kernel::list::ListLinks, +/// } +/// +/// #[pin_data] +/// struct ComplexListItem { +/// value: Result, +/// #[pin] +/// links: ListLinksHolder, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl{T, U} ListArcSafe<0> for ComplexListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl{T, U} ListItem<0> for ComplexListItem { using ListLinks { self.links.inner }; } +/// } +/// ``` +/// +/// ``` +/// #[pin_data] +/// struct SimpleListItem { +/// value: u32, +/// #[pin] +/// links: kernel::list::ListLinksSelfPtr, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl ListArcSafe<0> for SimpleListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl ListItem<0> for SimpleListItem { using ListLinksSelfPtr { self.links }; } +/// } +/// +/// struct ListLinksSelfPtrHolder { +/// inner: kernel::list::ListLinksSelfPtr>, +/// } +/// +/// #[pin_data] +/// struct ComplexListItem { +/// value: Result, +/// #[pin] +/// links: ListLinksSelfPtrHolder, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl{T, U} ListArcSafe<0> for ComplexListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl{T, U} ListItem<0> for ComplexListItem { +/// using ListLinksSelfPtr { self.links.inner }; +/// } +/// } +/// ``` #[macro_export] macro_rules! impl_list_item { ( - $(impl$({$($generics:tt)*})? ListItem<$num:tt> for $t:ty { - using ListLinks; + $(impl$({$($generics:tt)*})? ListItem<$num:tt> for $self:ty { + using ListLinks { self$(.$field:ident)* }; })* ) => {$( + $crate::list::impl_has_list_links! { + impl$({$($generics)*})? HasListLinks<$num> for $self { self$(.$field)* } + } + // SAFETY: See GUARANTEES comment on each method. - unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $t { + unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $self { // GUARANTEES: // * This returns the same pointer as `prepare_to_insert` because `prepare_to_insert` // is implemented in terms of `view_links`. @@ -139,20 +205,19 @@ unsafe fn view_links(me: *const Self) -> *mut $crate::list::ListLinks<$num> { } // GUARANTEES: - // * `me` originates from the most recent call to `prepare_to_insert`, which just added - // `offset` to the pointer passed to `prepare_to_insert`. This method subtracts - // `offset` from `me` so it returns the pointer originally passed to - // `prepare_to_insert`. + // * `me` originates from the most recent call to `prepare_to_insert`, which calls + // `raw_get_list_link`, which is implemented using `addr_of_mut!((*self)$(.$field)*)`. + // This method uses `container_of` to perform the inverse operation, so it returns the + // pointer originally passed to `prepare_to_insert`. // * The pointer remains valid until the next call to `post_remove` because the caller // of the most recent call to `prepare_to_insert` promised to retain ownership of the // `ListArc` containing `Self` until the next call to `post_remove`. The value cannot // be destroyed while a `ListArc` reference exists. unsafe fn view_value(me: *mut $crate::list::ListLinks<$num>) -> *const Self { - let offset = >::OFFSET; // SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it - // points at the field at offset `offset` in a value of type `Self`. Thus, - // subtracting `offset` from `me` is still in-bounds of the allocation. - unsafe { (me as *const u8).sub(offset) as *const Self } + // points at the field `$field` in a value of type `Self`. Thus, reversing that + // operation is still in-bounds of the allocation. + $crate::container_of!(me, Self, $($field).*) } // GUARANTEES: @@ -169,27 +234,30 @@ unsafe fn prepare_to_insert(me: *const Self) -> *mut $crate::list::ListLinks<$nu } // GUARANTEES: - // * `me` originates from the most recent call to `prepare_to_insert`, which just added - // `offset` to the pointer passed to `prepare_to_insert`. This method subtracts - // `offset` from `me` so it returns the pointer originally passed to - // `prepare_to_insert`. + // * `me` originates from the most recent call to `prepare_to_insert`, which calls + // `raw_get_list_link`, which is implemented using `addr_of_mut!((*self)$(.$field)*)`. + // This method uses `container_of` to perform the inverse operation, so it returns the + // pointer originally passed to `prepare_to_insert`. unsafe fn post_remove(me: *mut $crate::list::ListLinks<$num>) -> *const Self { - let offset = >::OFFSET; // SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it - // points at the field at offset `offset` in a value of type `Self`. Thus, - // subtracting `offset` from `me` is still in-bounds of the allocation. - unsafe { (me as *const u8).sub(offset) as *const Self } + // points at the field `$field` in a value of type `Self`. Thus, reversing that + // operation is still in-bounds of the allocation. + $crate::container_of!(me, Self, $($field).*) } } )*}; ( - $(impl$({$($generics:tt)*})? ListItem<$num:tt> for $t:ty { - using ListLinksSelfPtr; + $(impl$({$($generics:tt)*})? ListItem<$num:tt> for $self:ty { + using ListLinksSelfPtr { self$(.$field:ident)* }; })* ) => {$( + $crate::list::impl_has_list_links_self_ptr! { + impl$({$($generics)*})? HasSelfPtr<$self> for $self { self$(.$field)* } + } + // SAFETY: See GUARANTEES comment on each method. - unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $t { + unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $self { // GUARANTEES: // This implementation of `ListItem` will not give out exclusive access to the same // `ListLinks` several times because calls to `prepare_to_insert` and `post_remove` @@ -202,14 +270,16 @@ unsafe fn prepare_to_insert(me: *const Self) -> *mut $crate::list::ListLinks<$nu // SAFETY: The caller promises that `me` points at a valid value of type `Self`. let links_field = unsafe { >::view_links(me) }; - let spoff = $crate::list::ListLinksSelfPtr::::LIST_LINKS_SELF_PTR_OFFSET; - // Goes via the offset as the field is private. - // - // SAFETY: The constant is equal to `offset_of!(ListLinksSelfPtr, self_ptr)`, so - // the pointer stays in bounds of the allocation. - let self_ptr = unsafe { (links_field as *const u8).add(spoff) } - as *const $crate::types::Opaque<*const Self>; - let cell_inner = $crate::types::Opaque::raw_get(self_ptr); + let container = $crate::container_of!( + links_field, $crate::list::ListLinksSelfPtr, inner + ); + + // SAFETY: By the same reasoning above, `links_field` is a valid pointer. + let self_ptr = unsafe { + $crate::list::ListLinksSelfPtr::raw_get_self_ptr(container) + }; + + let cell_inner = $crate::types::Opaque::cast_into(self_ptr); // SAFETY: This value is not accessed in any other places than `prepare_to_insert`, // `post_remove`, or `view_value`. By the safety requirements of those methods, @@ -228,7 +298,9 @@ unsafe fn prepare_to_insert(me: *const Self) -> *mut $crate::list::ListLinks<$nu // this value is not in a list. unsafe fn view_links(me: *const Self) -> *mut $crate::list::ListLinks<$num> { // SAFETY: The caller promises that `me` points at a valid value of type `Self`. - unsafe { >::raw_get_list_links(me.cast_mut()) } + unsafe { + >::raw_get_list_links(me.cast_mut()) + } } // This function is also used as the implementation of `post_remove`, so the caller @@ -247,12 +319,17 @@ unsafe fn view_links(me: *const Self) -> *mut $crate::list::ListLinks<$num> { // `ListArc` containing `Self` until the next call to `post_remove`. The value cannot // be destroyed while a `ListArc` reference exists. unsafe fn view_value(links_field: *mut $crate::list::ListLinks<$num>) -> *const Self { - let spoff = $crate::list::ListLinksSelfPtr::::LIST_LINKS_SELF_PTR_OFFSET; - // SAFETY: The constant is equal to `offset_of!(ListLinksSelfPtr, self_ptr)`, so - // the pointer stays in bounds of the allocation. - let self_ptr = unsafe { (links_field as *const u8).add(spoff) } - as *const ::core::cell::UnsafeCell<*const Self>; - let cell_inner = ::core::cell::UnsafeCell::raw_get(self_ptr); + let container = $crate::container_of!( + links_field, $crate::list::ListLinksSelfPtr, inner + ); + + // SAFETY: By the same reasoning above, `links_field` is a valid pointer. + let self_ptr = unsafe { + $crate::list::ListLinksSelfPtr::raw_get_self_ptr(container) + }; + + let cell_inner = $crate::types::Opaque::cast_into(self_ptr); + // SAFETY: This is not a data race, because the only function that writes to this // value is `prepare_to_insert`, but by the safety requirements the // `prepare_to_insert` method may not be called in parallel with `view_value` or diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 939278bc7b03..6373fe183b27 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -17,7 +17,6 @@ mm::virt::VmaNew, prelude::*, seq_file::SeqFile, - str::CStr, types::{ForeignOwnable, Opaque}, }; use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; @@ -34,7 +33,7 @@ impl MiscDeviceOptions { pub const fn into_raw(self) -> bindings::miscdevice { // SAFETY: All zeros is valid for this C type. let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; - result.minor = bindings::MISC_DYNAMIC_MINOR as _; + result.minor = bindings::MISC_DYNAMIC_MINOR as ffi::c_int; result.name = self.name.as_char_ptr(); result.fops = MiscdeviceVTable::::build(); result @@ -45,7 +44,13 @@ pub const fn into_raw(self) -> bindings::miscdevice { /// /// # Invariants /// -/// `inner` is a registered misc device. +/// - `inner` contains a `struct miscdevice` that is registered using +/// `misc_register()`. +/// - This registration remains valid for the entire lifetime of the +/// [`MiscDeviceRegistration`] instance. +/// - Deregistration occurs exactly once in [`Drop`] via `misc_deregister()`. +/// - `inner` wraps a valid, pinned `miscdevice` created using +/// [`MiscDeviceOptions::into_raw`]. #[repr(transparent)] #[pin_data(PinnedDrop)] pub struct MiscDeviceRegistration { @@ -92,7 +97,7 @@ pub fn device(&self) -> &Device { // function tells the borrow-checker that the `&Device` reference must not outlive the // `&MiscDeviceRegistration` used to obtain it, so the last use of the reference must be // before the underlying `struct miscdevice` is destroyed. - unsafe { Device::as_ref((*self.as_raw()).this_device) } + unsafe { Device::from_raw((*self.as_raw()).this_device) } } } @@ -217,7 +222,7 @@ impl MiscdeviceVTable { // type. // // SAFETY: The open call of a file can access the private data. - unsafe { (*raw_file).private_data = ptr.into_foreign().cast() }; + unsafe { (*raw_file).private_data = ptr.into_foreign() }; 0 } @@ -228,7 +233,7 @@ impl MiscdeviceVTable { /// must be associated with a `MiscDeviceRegistration`. unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int { // SAFETY: The release call of a file owns the private data. - let private = unsafe { (*file).private_data }.cast(); + let private = unsafe { (*file).private_data }; // SAFETY: The release call of a file owns the private data. let ptr = unsafe { ::from_foreign(private) }; @@ -272,7 +277,7 @@ impl MiscdeviceVTable { /// `file` must be a valid file that is associated with a `MiscDeviceRegistration`. unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long { // SAFETY: The ioctl call of a file can access the private data. - let private = unsafe { (*file).private_data }.cast(); + let private = unsafe { (*file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. let device = unsafe { ::borrow(private) }; @@ -297,7 +302,7 @@ impl MiscdeviceVTable { arg: c_ulong, ) -> c_long { // SAFETY: The compat ioctl call of a file can access the private data. - let private = unsafe { (*file).private_data }.cast(); + let private = unsafe { (*file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. let device = unsafe { ::borrow(private) }; @@ -318,7 +323,7 @@ impl MiscdeviceVTable { /// - `seq_file` must be a valid `struct seq_file` that we can write to. unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) { // SAFETY: The release call of a file owns the private data. - let private = unsafe { (*file).private_data }.cast(); + let private = unsafe { (*file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. let device = unsafe { ::borrow(private) }; // SAFETY: diff --git a/rust/kernel/mm/virt.rs b/rust/kernel/mm/virt.rs index 31803674aecc..a1bfa4e19293 100644 --- a/rust/kernel/mm/virt.rs +++ b/rust/kernel/mm/virt.rs @@ -209,6 +209,7 @@ pub fn vm_insert_page(&self, address: usize, page: &Page) -> Result { /// /// For the duration of 'a, the referenced vma must be undergoing initialization in an /// `f_ops->mmap()` hook. +#[repr(transparent)] pub struct VmaNew { vma: VmaRef, } @@ -392,80 +393,80 @@ pub mod flags { use crate::bindings; /// No flags are set. - pub const NONE: vm_flags_t = bindings::VM_NONE as _; + pub const NONE: vm_flags_t = bindings::VM_NONE as vm_flags_t; /// Mapping allows reads. - pub const READ: vm_flags_t = bindings::VM_READ as _; + pub const READ: vm_flags_t = bindings::VM_READ as vm_flags_t; /// Mapping allows writes. - pub const WRITE: vm_flags_t = bindings::VM_WRITE as _; + pub const WRITE: vm_flags_t = bindings::VM_WRITE as vm_flags_t; /// Mapping allows execution. - pub const EXEC: vm_flags_t = bindings::VM_EXEC as _; + pub const EXEC: vm_flags_t = bindings::VM_EXEC as vm_flags_t; /// Mapping is shared. - pub const SHARED: vm_flags_t = bindings::VM_SHARED as _; + pub const SHARED: vm_flags_t = bindings::VM_SHARED as vm_flags_t; /// Mapping may be updated to allow reads. - pub const MAYREAD: vm_flags_t = bindings::VM_MAYREAD as _; + pub const MAYREAD: vm_flags_t = bindings::VM_MAYREAD as vm_flags_t; /// Mapping may be updated to allow writes. - pub const MAYWRITE: vm_flags_t = bindings::VM_MAYWRITE as _; + pub const MAYWRITE: vm_flags_t = bindings::VM_MAYWRITE as vm_flags_t; /// Mapping may be updated to allow execution. - pub const MAYEXEC: vm_flags_t = bindings::VM_MAYEXEC as _; + pub const MAYEXEC: vm_flags_t = bindings::VM_MAYEXEC as vm_flags_t; /// Mapping may be updated to be shared. - pub const MAYSHARE: vm_flags_t = bindings::VM_MAYSHARE as _; + pub const MAYSHARE: vm_flags_t = bindings::VM_MAYSHARE as vm_flags_t; /// Page-ranges managed without `struct page`, just pure PFN. - pub const PFNMAP: vm_flags_t = bindings::VM_PFNMAP as _; + pub const PFNMAP: vm_flags_t = bindings::VM_PFNMAP as vm_flags_t; /// Memory mapped I/O or similar. - pub const IO: vm_flags_t = bindings::VM_IO as _; + pub const IO: vm_flags_t = bindings::VM_IO as vm_flags_t; /// Do not copy this vma on fork. - pub const DONTCOPY: vm_flags_t = bindings::VM_DONTCOPY as _; + pub const DONTCOPY: vm_flags_t = bindings::VM_DONTCOPY as vm_flags_t; /// Cannot expand with mremap(). - pub const DONTEXPAND: vm_flags_t = bindings::VM_DONTEXPAND as _; + pub const DONTEXPAND: vm_flags_t = bindings::VM_DONTEXPAND as vm_flags_t; /// Lock the pages covered when they are faulted in. - pub const LOCKONFAULT: vm_flags_t = bindings::VM_LOCKONFAULT as _; + pub const LOCKONFAULT: vm_flags_t = bindings::VM_LOCKONFAULT as vm_flags_t; /// Is a VM accounted object. - pub const ACCOUNT: vm_flags_t = bindings::VM_ACCOUNT as _; + pub const ACCOUNT: vm_flags_t = bindings::VM_ACCOUNT as vm_flags_t; /// Should the VM suppress accounting. - pub const NORESERVE: vm_flags_t = bindings::VM_NORESERVE as _; + pub const NORESERVE: vm_flags_t = bindings::VM_NORESERVE as vm_flags_t; /// Huge TLB Page VM. - pub const HUGETLB: vm_flags_t = bindings::VM_HUGETLB as _; + pub const HUGETLB: vm_flags_t = bindings::VM_HUGETLB as vm_flags_t; /// Synchronous page faults. (DAX-specific) - pub const SYNC: vm_flags_t = bindings::VM_SYNC as _; + pub const SYNC: vm_flags_t = bindings::VM_SYNC as vm_flags_t; /// Architecture-specific flag. - pub const ARCH_1: vm_flags_t = bindings::VM_ARCH_1 as _; + pub const ARCH_1: vm_flags_t = bindings::VM_ARCH_1 as vm_flags_t; /// Wipe VMA contents in child on fork. - pub const WIPEONFORK: vm_flags_t = bindings::VM_WIPEONFORK as _; + pub const WIPEONFORK: vm_flags_t = bindings::VM_WIPEONFORK as vm_flags_t; /// Do not include in the core dump. - pub const DONTDUMP: vm_flags_t = bindings::VM_DONTDUMP as _; + pub const DONTDUMP: vm_flags_t = bindings::VM_DONTDUMP as vm_flags_t; /// Not soft dirty clean area. - pub const SOFTDIRTY: vm_flags_t = bindings::VM_SOFTDIRTY as _; + pub const SOFTDIRTY: vm_flags_t = bindings::VM_SOFTDIRTY as vm_flags_t; /// Can contain `struct page` and pure PFN pages. - pub const MIXEDMAP: vm_flags_t = bindings::VM_MIXEDMAP as _; + pub const MIXEDMAP: vm_flags_t = bindings::VM_MIXEDMAP as vm_flags_t; /// MADV_HUGEPAGE marked this vma. - pub const HUGEPAGE: vm_flags_t = bindings::VM_HUGEPAGE as _; + pub const HUGEPAGE: vm_flags_t = bindings::VM_HUGEPAGE as vm_flags_t; /// MADV_NOHUGEPAGE marked this vma. - pub const NOHUGEPAGE: vm_flags_t = bindings::VM_NOHUGEPAGE as _; + pub const NOHUGEPAGE: vm_flags_t = bindings::VM_NOHUGEPAGE as vm_flags_t; /// KSM may merge identical pages. - pub const MERGEABLE: vm_flags_t = bindings::VM_MERGEABLE as _; + pub const MERGEABLE: vm_flags_t = bindings::VM_MERGEABLE as vm_flags_t; } diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 32ea43ece646..7de5cc7a0eee 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -6,7 +6,7 @@ //! //! C headers: [`include/linux/phy.h`](srctree/include/linux/phy.h). -use crate::{error::*, prelude::*, types::Opaque}; +use crate::{device_id::RawDeviceId, error::*, prelude::*, types::Opaque}; use core::{marker::PhantomData, ptr::addr_of_mut}; pub mod reg; @@ -142,7 +142,7 @@ pub fn is_autoneg_enabled(&self) -> bool { // SAFETY: The struct invariant ensures that we may access // this field without additional synchronization. let bit_field = unsafe { &(*self.0.get())._bitfield_1 }; - bit_field.get(13, 1) == bindings::AUTONEG_ENABLE as u64 + bit_field.get(13, 1) == u64::from(bindings::AUTONEG_ENABLE) } /// Gets the current auto-negotiation state. @@ -163,20 +163,20 @@ pub fn set_speed(&mut self, speed: u32) { let phydev = self.0.get(); // SAFETY: The struct invariant ensures that we may access // this field without additional synchronization. - unsafe { (*phydev).speed = speed as i32 }; + unsafe { (*phydev).speed = speed as c_int }; } /// Sets duplex mode. pub fn set_duplex(&mut self, mode: DuplexMode) { let phydev = self.0.get(); let v = match mode { - DuplexMode::Full => bindings::DUPLEX_FULL as i32, - DuplexMode::Half => bindings::DUPLEX_HALF as i32, - DuplexMode::Unknown => bindings::DUPLEX_UNKNOWN as i32, + DuplexMode::Full => bindings::DUPLEX_FULL, + DuplexMode::Half => bindings::DUPLEX_HALF, + DuplexMode::Unknown => bindings::DUPLEX_UNKNOWN, }; // SAFETY: The struct invariant ensures that we may access // this field without additional synchronization. - unsafe { (*phydev).duplex = v }; + unsafe { (*phydev).duplex = v as c_int }; } /// Reads a PHY register. @@ -285,7 +285,7 @@ impl AsRef for Device { fn as_ref(&self) -> &kernel::device::Device { let phydev = self.0.get(); // SAFETY: The struct invariant ensures that `mdio.dev` is valid. - unsafe { kernel::device::Device::as_ref(addr_of_mut!((*phydev).mdio.dev)) } + unsafe { kernel::device::Device::from_raw(addr_of_mut!((*phydev).mdio.dev)) } } } @@ -312,9 +312,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn soft_reset_callback( - phydev: *mut bindings::phy_device, - ) -> crate::ffi::c_int { + unsafe extern "C" fn soft_reset_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -328,7 +326,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn probe_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { + unsafe extern "C" fn probe_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we can exclusively access `phy_device` because @@ -343,9 +341,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn get_features_callback( - phydev: *mut bindings::phy_device, - ) -> crate::ffi::c_int { + unsafe extern "C" fn get_features_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -359,7 +355,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { + unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: The C core code ensures that the accessors on // `Device` are okay to call even though `phy_device->lock` @@ -373,7 +369,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { + unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: The C core code ensures that the accessors on // `Device` are okay to call even though `phy_device->lock` @@ -387,9 +383,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn config_aneg_callback( - phydev: *mut bindings::phy_device, - ) -> crate::ffi::c_int { + unsafe extern "C" fn config_aneg_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -403,9 +397,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn read_status_callback( - phydev: *mut bindings::phy_device, - ) -> crate::ffi::c_int { + unsafe extern "C" fn read_status_callback(phydev: *mut bindings::phy_device) -> c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -422,12 +414,12 @@ impl Adapter { unsafe extern "C" fn match_phy_device_callback( phydev: *mut bindings::phy_device, _phydrv: *const bindings::phy_driver, - ) -> crate::ffi::c_int { + ) -> c_int { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on // `Device` are okay to call. let dev = unsafe { Device::from_raw(phydev) }; - T::match_phy_device(dev) as i32 + T::match_phy_device(dev).into() } /// # Safety @@ -507,7 +499,7 @@ pub const fn create_phy_driver() -> DriverVTable { DriverVTable(Opaque::new(bindings::phy_driver { name: T::NAME.as_char_ptr().cast_mut(), flags: T::FLAGS, - phy_id: T::PHY_DEVICE_ID.id, + phy_id: T::PHY_DEVICE_ID.id(), phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(), soft_reset: if T::HAS_SOFT_RESET { Some(Adapter::::soft_reset_callback) @@ -691,42 +683,41 @@ fn drop(&mut self) { /// /// Represents the kernel's `struct mdio_device_id`. This is used to find an appropriate /// PHY driver. -pub struct DeviceId { - id: u32, - mask: DeviceMask, -} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::mdio_device_id); impl DeviceId { /// Creates a new instance with the exact match mask. pub const fn new_with_exact_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Exact, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Exact.as_int(), + }) } /// Creates a new instance with the model match mask. pub const fn new_with_model_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Model, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Model.as_int(), + }) } /// Creates a new instance with the vendor match mask. pub const fn new_with_vendor_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Vendor, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Vendor.as_int(), + }) } /// Creates a new instance with a custom match mask. pub const fn new_with_custom_mask(id: u32, mask: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Custom(mask), - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Custom(mask).as_int(), + }) } /// Creates a new instance from [`Driver`]. @@ -734,21 +725,29 @@ pub const fn new_with_driver() -> Self { T::PHY_DEVICE_ID } - /// Get a `mask` as u32. + /// Get the MDIO device's PHY ID. + pub const fn id(&self) -> u32 { + self.0.phy_id + } + + /// Get the MDIO device's match mask. pub const fn mask_as_int(&self) -> u32 { - self.mask.as_int() + self.0.phy_id_mask } // macro use only #[doc(hidden)] pub const fn mdio_device_id(&self) -> bindings::mdio_device_id { - bindings::mdio_device_id { - phy_id: self.id, - phy_id_mask: self.mask.as_int(), - } + self.0 } } +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct mdio_device_id` +// and does not add additional invariants, so it's safe to transmute to `RawType`. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::mdio_device_id; +} + enum DeviceMask { Exact, Model, @@ -849,19 +848,18 @@ const fn as_int(&self) -> u32 { /// } /// }; /// -/// const _DEVICE_TABLE: [::kernel::bindings::mdio_device_id; 2] = [ -/// ::kernel::bindings::mdio_device_id { -/// phy_id: 0x00000001, -/// phy_id_mask: 0xffffffff, -/// }, -/// ::kernel::bindings::mdio_device_id { -/// phy_id: 0, -/// phy_id_mask: 0, -/// }, -/// ]; -/// #[cfg(MODULE)] -/// #[no_mangle] -/// static __mod_device_table__mdio__phydev: [::kernel::bindings::mdio_device_id; 2] = _DEVICE_TABLE; +/// const N: usize = 1; +/// +/// const TABLE: ::kernel::device_id::IdArray<::kernel::net::phy::DeviceId, (), N> = +/// ::kernel::device_id::IdArray::new_without_index([ +/// ::kernel::net::phy::DeviceId( +/// ::kernel::bindings::mdio_device_id { +/// phy_id: 0x00000001, +/// phy_id_mask: 0xffffffff, +/// }), +/// ]); +/// +/// ::kernel::module_device_table!("mdio", phydev, TABLE); /// ``` #[macro_export] macro_rules! module_phy_driver { @@ -872,20 +870,12 @@ macro_rules! module_phy_driver { }; (@device_table [$($dev:expr),+]) => { - // SAFETY: C will not read off the end of this constant since the last element is zero. - const _DEVICE_TABLE: [$crate::bindings::mdio_device_id; - $crate::module_phy_driver!(@count_devices $($dev),+) + 1] = [ - $($dev.mdio_device_id()),+, - $crate::bindings::mdio_device_id { - phy_id: 0, - phy_id_mask: 0 - } - ]; + const N: usize = $crate::module_phy_driver!(@count_devices $($dev),+); - #[cfg(MODULE)] - #[no_mangle] - static __mod_device_table__mdio__phydev: [$crate::bindings::mdio_device_id; - $crate::module_phy_driver!(@count_devices $($dev),+) + 1] = _DEVICE_TABLE; + const TABLE: $crate::device_id::IdArray<$crate::net::phy::DeviceId, (), N> = + $crate::device_id::IdArray::new_without_index([ $(($dev,())),+, ]); + + $crate::module_device_table!("mdio", phydev, TABLE); }; (drivers: [$($driver:ident),+ $(,)?], device_table: [$($dev:expr),+ $(,)?], $($f:tt)*) => { diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs index 04f2d8ef29cb..b76b35265df2 100644 --- a/rust/kernel/of.rs +++ b/rust/kernel/of.rs @@ -2,7 +2,11 @@ //! Device Tree / Open Firmware abstractions. -use crate::{bindings, device_id::RawDeviceId, prelude::*}; +use crate::{ + bindings, + device_id::{RawDeviceId, RawDeviceIdIndex}, + prelude::*, +}; /// IdTable type for OF drivers. pub type IdTable = &'static dyn kernel::device_id::IdTable; @@ -12,17 +16,18 @@ #[derive(Clone, Copy)] pub struct DeviceId(bindings::of_device_id); -// SAFETY: -// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add -// additional invariants, so it's safe to transmute to `RawType`. -// * `DRIVER_DATA_OFFSET` is the offset to the `data` field. +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` and +// does not add additional invariants, so it's safe to transmute to `RawType`. unsafe impl RawDeviceId for DeviceId { type RawType = bindings::of_device_id; +} +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `data` field. +unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data); fn index(&self) -> usize { - self.0.data as _ + self.0.data as usize } } @@ -34,10 +39,10 @@ pub const fn new(compatible: &'static CStr) -> Self { // SAFETY: FFI type is valid to be zero-initialized. let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() }; - // TODO: Use `clone_from_slice` once the corresponding types do match. + // TODO: Use `copy_from_slice` once stabilized for `const`. let mut i = 0; while i < src.len() { - of.compatible[i] = src[i] as _; + of.compatible[i] = src[i]; i += 1; } diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index a566fc3e7dcb..08126035d2c6 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -92,7 +92,7 @@ fn to_c_str_array(names: &[CString]) -> Result> { let mut list = KVec::with_capacity(names.len() + 1, GFP_KERNEL)?; for name in names.iter() { - list.push(name.as_ptr() as _, GFP_KERNEL)?; + list.push(name.as_ptr().cast(), GFP_KERNEL)?; } list.push(ptr::null(), GFP_KERNEL)?; @@ -103,7 +103,7 @@ fn to_c_str_array(names: &[CString]) -> Result> { /// /// Represents voltage in microvolts, wrapping a [`c_ulong`] value. /// -/// ## Examples +/// # Examples /// /// ``` /// use kernel::opp::MicroVolt; @@ -128,7 +128,7 @@ fn from(volt: MicroVolt) -> Self { /// /// Represents power in microwatts, wrapping a [`c_ulong`] value. /// -/// ## Examples +/// # Examples /// /// ``` /// use kernel::opp::MicroWatt; @@ -153,7 +153,7 @@ fn from(power: MicroWatt) -> Self { /// /// The associated [`OPP`] is automatically removed when the [`Token`] is dropped. /// -/// ## Examples +/// # Examples /// /// The following example demonstrates how to create an [`OPP`] dynamically. /// @@ -202,7 +202,7 @@ fn drop(&mut self) { /// Rust abstraction for the C `struct dev_pm_opp_data`, used to define operating performance /// points (OPPs) dynamically. /// -/// ## Examples +/// # Examples /// /// The following example demonstrates how to create an [`OPP`] with [`Data`]. /// @@ -254,7 +254,7 @@ fn freq(&self) -> Hertz { /// [`OPP`] search options. /// -/// ## Examples +/// # Examples /// /// Defines how to search for an [`OPP`] in a [`Table`] relative to a frequency. /// @@ -326,7 +326,7 @@ fn drop(&mut self) { /// /// Rust abstraction for the C `struct dev_pm_opp_config`. /// -/// ## Examples +/// # Examples /// /// The following example demonstrates how to set OPP property-name configuration for a [`Device`]. /// @@ -345,7 +345,7 @@ fn drop(&mut self) { /// impl ConfigOps for Driver {} /// /// fn configure(dev: &ARef) -> Result { -/// let name = CString::try_from_fmt(fmt!("{}", "slow"))?; +/// let name = CString::try_from_fmt(fmt!("slow"))?; /// /// // The OPP configuration is cleared once the [`ConfigToken`] goes out of scope. /// Config::::new() @@ -514,9 +514,9 @@ extern "C" fn config_clks( dev: *mut bindings::device, opp_table: *mut bindings::opp_table, opp: *mut bindings::dev_pm_opp, - _data: *mut kernel::ffi::c_void, + _data: *mut c_void, scaling_down: bool, - ) -> kernel::ffi::c_int { + ) -> c_int { from_result(|| { // SAFETY: 'dev' is guaranteed by the C code to be valid. let dev = unsafe { Device::get_device(dev) }; @@ -540,8 +540,8 @@ extern "C" fn config_regulators( old_opp: *mut bindings::dev_pm_opp, new_opp: *mut bindings::dev_pm_opp, regulators: *mut *mut bindings::regulator, - count: kernel::ffi::c_uint, - ) -> kernel::ffi::c_int { + count: c_uint, + ) -> c_int { from_result(|| { // SAFETY: 'dev' is guaranteed by the C code to be valid. let dev = unsafe { Device::get_device(dev) }; @@ -569,7 +569,7 @@ extern "C" fn config_regulators( /// /// Instances of this type are reference-counted. /// -/// ## Examples +/// # Examples /// /// The following example demonstrates how to get OPP [`Table`] for a [`Cpumask`] and set its /// frequency. @@ -1011,7 +1011,7 @@ fn drop(&mut self) { /// /// A reference to the [`OPP`], &[`OPP`], isn't refcounted by the Rust code. /// -/// ## Examples +/// # Examples /// /// The following example demonstrates how to get [`OPP`] corresponding to a frequency value and /// configure the device with it. diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 8435f8132e38..887ee611b553 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,16 +5,15 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) use crate::{ - alloc::flags::*, bindings, container_of, device, - device_id::RawDeviceId, + device_id::{RawDeviceId, RawDeviceIdIndex}, devres::Devres, driver, - error::{to_result, Result}, + error::{from_result, to_result, Result}, io::Io, io::IoRaw, str::CStr, - types::{ARef, ForeignOwnable, Opaque}, + types::{ARef, Opaque}, ThisModule, }; use core::{ @@ -66,41 +65,40 @@ extern "C" fn probe_callback( // `struct pci_dev`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>() }; - // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct pci_device_id` and // does not add additional invariants, so it's safe to transmute. let id = unsafe { &*id.cast::() }; let info = T::ID_TABLE.info(id.index()); - match T::probe(pdev, info) { - Ok(data) => { - // Let the `struct pci_dev` own a reference of the driver's private data. - // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a - // `struct pci_dev`. - unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; - } - Err(err) => return Error::to_errno(err), - } + from_result(|| { + let data = T::probe(pdev, info)?; - 0 + pdev.as_ref().set_drvdata(data); + Ok(0) + }) } extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { // SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a // `struct pci_dev`. - let ptr = unsafe { bindings::pci_get_drvdata(pdev) }.cast(); + // + // INVARIANT: `pdev` is valid for the duration of `remove_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized - // `KBox` pointer created through `KBox::into_foreign`. - let _ = unsafe { KBox::::from_foreign(ptr) }; + // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_obtain::>>() }; + + T::unbind(pdev, data.as_ref()); } } /// Declares a kernel module that exposes a single PCI driver. /// -/// # Example +/// # Examples /// ///```ignore /// kernel::module_pci_driver! { @@ -161,17 +159,18 @@ pub const fn from_class(class: u32, class_mask: u32) -> Self { } } -// SAFETY: -// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_device_id` and does not add -// additional invariants, so it's safe to transmute to `RawType`. -// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `pci_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. unsafe impl RawDeviceId for DeviceId { type RawType = bindings::pci_device_id; +} +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data); fn index(&self) -> usize { - self.0.driver_data as _ + self.0.driver_data } } @@ -194,7 +193,7 @@ macro_rules! pci_device_table { /// The PCI driver trait. /// -/// # Example +/// # Examples /// ///``` /// # use kernel::{bindings, device::Core, pci}; @@ -206,7 +205,10 @@ macro_rules! pci_device_table { /// MODULE_PCI_TABLE, /// ::IdInfo, /// [ -/// (pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as _), ()) +/// ( +/// pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as u32), +/// (), +/// ) /// ] /// ); /// @@ -241,6 +243,20 @@ pub trait Driver: Send { /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; + + /// Platform driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown operations that require a + /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O + /// operations to gracefully tear down the device. + /// + /// Otherwise, release operations for driver resources should be performed in `Self::drop`. + fn unbind(dev: &Device, this: Pin<&Self>) { + let _ = (dev, this); + } } /// The PCI device representation. @@ -251,7 +267,8 @@ pub trait Driver: Send { /// /// # Invariants /// -/// A [`Device`] instance represents a valid `struct device` created by the C portion of the kernel. +/// A [`Device`] instance represents a valid `struct pci_dev` created by the C portion of the +/// kernel. #[repr(transparent)] pub struct Device( Opaque, @@ -330,7 +347,7 @@ unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { // `ioptr` is valid by the safety requirements. // `num` is valid by the safety requirements. unsafe { - bindings::pci_iounmap(pdev.as_raw(), ioptr as _); + bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut kernel::ffi::c_void); bindings::pci_release_region(pdev.as_raw(), num); } } @@ -398,19 +415,20 @@ pub fn resource_len(&self, bar: u32) -> Result { impl Device { /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks /// can be performed on compile time for offsets (plus the requested type size) < SIZE. - pub fn iomap_region_sized( - &self, + pub fn iomap_region_sized<'a, const SIZE: usize>( + &'a self, bar: u32, - name: &CStr, - ) -> Result>> { - let bar = Bar::::new(self, bar, name)?; - let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; - - Ok(devres) + name: &'a CStr, + ) -> impl PinInit>, Error> + 'a { + Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } /// Mapps an entire PCI-BAR after performing a region-request on it. - pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { + pub fn iomap_region<'a>( + &'a self, + bar: u32, + name: &'a CStr, + ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } } @@ -434,6 +452,8 @@ pub fn set_master(&self) { kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); +impl crate::dma::Device for Device {} + // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::types::AlwaysRefCounted for Device { fn inc_ref(&self) { @@ -454,7 +474,7 @@ fn as_ref(&self) -> &device::Device { let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::as_ref(dev) } + unsafe { device::Device::from_raw(dev) } } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 5b21fa517e55..8f028c76f9fa 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,12 +5,14 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) use crate::{ - bindings, container_of, device, driver, - error::{to_result, Result}, + acpi, bindings, container_of, + device::{self, Bound}, + driver, + error::{from_result, to_result, Result}, + io::{mem::IoRequest, Resource}, of, prelude::*, - str::CStr, - types::{ForeignOwnable, Opaque}, + types::Opaque, ThisModule, }; @@ -37,12 +39,18 @@ unsafe fn register( None => core::ptr::null(), }; + let acpi_table = match T::ACPI_ID_TABLE { + Some(table) => table.as_ptr(), + None => core::ptr::null(), + }; + // SAFETY: It's safe to set the fields of `struct platform_driver` on initialization. unsafe { (*pdrv.get()).driver.name = name.as_char_ptr(); (*pdrv.get()).probe = Some(Self::probe_callback); (*pdrv.get()).remove = Some(Self::remove_callback); (*pdrv.get()).driver.of_match_table = of_table; + (*pdrv.get()).driver.acpi_match_table = acpi_table; } // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. @@ -61,30 +69,30 @@ extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ff // `struct platform_device`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; - + let pdev = unsafe { &*pdev.cast::>() }; let info = ::id_info(pdev.as_ref()); - match T::probe(pdev, info) { - Ok(data) => { - // Let the `struct platform_device` own a reference of the driver's private data. - // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a - // `struct platform_device`. - unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; - } - Err(err) => return Error::to_errno(err), - } - 0 + from_result(|| { + let data = T::probe(pdev, info)?; + + pdev.as_ref().set_drvdata(data); + Ok(0) + }) } extern "C" fn remove_callback(pdev: *mut bindings::platform_device) { - // SAFETY: `pdev` is a valid pointer to a `struct platform_device`. - let ptr = unsafe { bindings::platform_get_drvdata(pdev) }.cast(); + // SAFETY: The platform bus only ever calls the remove callback with a valid pointer to a + // `struct platform_device`. + // + // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized - // `KBox` pointer created through `KBox::into_foreign`. - let _ = unsafe { KBox::::from_foreign(ptr) }; + // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_obtain::>>() }; + + T::unbind(pdev, data.as_ref()); } } @@ -94,6 +102,10 @@ impl driver::Adapter for Adapter { fn of_id_table() -> Option> { T::OF_ID_TABLE } + + fn acpi_id_table() -> Option> { + T::ACPI_ID_TABLE + } } /// Declares a kernel module that exposes a single platform driver. @@ -120,10 +132,10 @@ macro_rules! module_platform_driver { /// /// Drivers must implement this trait in order to get a platform driver registered. /// -/// # Example +/// # Examples /// ///``` -/// # use kernel::{bindings, c_str, device::Core, of, platform}; +/// # use kernel::{acpi, bindings, c_str, device::Core, of, platform}; /// /// struct MyDriver; /// @@ -136,9 +148,19 @@ macro_rules! module_platform_driver { /// ] /// ); /// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// ::IdInfo, +/// [ +/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// ] +/// ); +/// /// impl platform::Driver for MyDriver { /// type IdInfo = (); /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); +/// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// /// fn probe( /// _pdev: &platform::Device, @@ -158,7 +180,10 @@ pub trait Driver: Send { type IdInfo: 'static; /// The table of OF device ids supported by the driver. - const OF_ID_TABLE: Option>; + const OF_ID_TABLE: Option> = None; + + /// The table of ACPI device ids supported by the driver. + const ACPI_ID_TABLE: Option> = None; /// Platform driver probe. /// @@ -166,6 +191,20 @@ pub trait Driver: Send { /// Implementers should attempt to initialize the device here. fn probe(dev: &Device, id_info: Option<&Self::IdInfo>) -> Result>>; + + /// Platform driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown operations that require a + /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O + /// operations to gracefully tear down the device. + /// + /// Otherwise, release operations for driver resources should be performed in `Self::drop`. + fn unbind(dev: &Device, this: Pin<&Self>) { + let _ = (dev, this); + } } /// The platform device representation. @@ -188,6 +227,61 @@ impl Device { fn as_raw(&self) -> *mut bindings::platform_device { self.0.get() } + + /// Returns the resource at `index`, if any. + pub fn resource_by_index(&self, index: u32) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct platform_device`. + let resource = unsafe { + bindings::platform_get_resource(self.as_raw(), bindings::IORESOURCE_MEM, index) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } + + /// Returns the resource with a given `name`, if any. + pub fn resource_by_name(&self, name: &CStr) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct + // platform_device` and `name` points to a valid C string. + let resource = unsafe { + bindings::platform_get_resource_byname( + self.as_raw(), + bindings::IORESOURCE_MEM, + name.as_char_ptr(), + ) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } +} + +impl Device { + /// Returns an `IoRequest` for the resource at `index`, if any. + pub fn io_request_by_index(&self, index: u32) -> Option> { + self.resource_by_index(index) + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) }) + } + + /// Returns an `IoRequest` for the resource with a given `name`, if any. + pub fn io_request_by_name(&self, name: &CStr) -> Option> { + self.resource_by_name(name) + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) }) + } } // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic @@ -195,6 +289,8 @@ fn as_raw(&self) -> *mut bindings::platform_device { kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); +impl crate::dma::Device for Device {} + // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::types::AlwaysRefCounted for Device { fn inc_ref(&self) { @@ -215,7 +311,7 @@ fn as_ref(&self) -> &device::Device { let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::as_ref(dev) } + unsafe { device::Device::from_raw(dev) } } } diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 2f30a398dddd..25fe97aafd02 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -31,9 +31,9 @@ // `super::std_vendor` is hidden, which makes the macro inline for some reason. #[doc(no_inline)] pub use super::dbg; -pub use super::fmt; pub use super::{dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn}; pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; +pub use core::format_args as fmt; pub use super::{try_init, try_pin_init}; @@ -46,3 +46,5 @@ pub use super::init::InPlaceInit; pub use super::current; + +pub use super::uaccess::UserPtr; diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 9783d960a97a..2d743d78d220 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -8,10 +8,10 @@ use crate::{ ffi::{c_char, c_void}, + fmt, prelude::*, str::RawFormatter, }; -use core::fmt; // Called from `vsprintf` with format specifier `%pA`. #[expect(clippy::missing_safety_doc)] @@ -25,7 +25,7 @@ // SAFETY: The C contract guarantees that `buf` is valid if it's less than `end`. let mut w = unsafe { RawFormatter::from_ptrs(buf.cast(), end.cast()) }; // SAFETY: TODO. - let _ = w.write_fmt(unsafe { *(ptr as *const fmt::Arguments<'_>) }); + let _ = w.write_fmt(unsafe { *ptr.cast::>() }); w.pos().cast() } @@ -109,7 +109,7 @@ pub unsafe fn call_printk( bindings::_printk( format_string.as_ptr(), module_name.as_ptr(), - &args as *const _ as *const c_void, + core::ptr::from_ref(&args).cast::(), ); } } @@ -129,7 +129,7 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) { unsafe { bindings::_printk( format_strings::CONT.as_ptr(), - &args as *const _ as *const c_void, + core::ptr::from_ref(&args).cast::(), ); } } @@ -149,7 +149,7 @@ macro_rules! print_macro ( // takes borrows on the arguments, but does not extend the scope of temporaries. // Therefore, a `match` expression is used to keep them around, since // the scrutinee is kept until the end of the `match`. - match format_args!($($arg)+) { + match $crate::prelude::fmt!($($arg)+) { // SAFETY: This hidden macro should only be called by the documented // printing macros which ensure the format string is one of the fixed // ones. All `__LOG_PREFIX`s are null-terminated as they are generated @@ -168,7 +168,7 @@ macro_rules! print_macro ( // The `CONT` case. ($format_string:path, true, $($arg:tt)+) => ( $crate::print::call_printk_cont( - format_args!($($arg)+), + $crate::prelude::fmt!($($arg)+), ); ); ); diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 8d978c896747..b8fe6be6fcc4 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -191,6 +191,12 @@ pub fn new() -> Self { } } + /// Returns true if this tree is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.root.rb_node.is_null() + } + /// Returns an iterator over the tree nodes, sorted by key. pub fn iter(&self) -> Iter<'_, K, V> { Iter { @@ -769,23 +775,14 @@ pub fn remove_current(self) -> (Option, RBTreeNode) { // the tree cannot change. By the tree invariant, all nodes are valid. unsafe { bindings::rb_erase(&mut (*this).links, addr_of_mut!(self.tree.root)) }; - let current = match (prev, next) { - (_, Some(next)) => next, - (Some(prev), None) => prev, - (None, None) => { - return (None, node); - } - }; + // INVARIANT: + // - `current` is a valid node in the [`RBTree`] pointed to by `self.tree`. + let cursor = next.or(prev).map(|current| Self { + current, + tree: self.tree, + }); - ( - // INVARIANT: - // - `current` is a valid node in the [`RBTree`] pointed to by `self.tree`. - Some(Self { - current, - tree: self.tree, - }), - node, - ) + (cursor, node) } /// Remove the previous node, returning it if it exists. diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs new file mode 100644 index 000000000000..65f3a125348f --- /dev/null +++ b/rust/kernel/regulator.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Regulator abstractions, providing a standard kernel interface to control +//! voltage and current regulators. +//! +//! The intention is to allow systems to dynamically control regulator power +//! output in order to save power and prolong battery life. This applies to both +//! voltage regulators (where voltage output is controllable) and current sinks +//! (where current limit is controllable). +//! +//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h) +//! +//! Regulators are modeled in Rust with a collection of states. Each state may +//! enforce a given invariant, and they may convert between each other where applicable. +//! +//! See [Voltage and current regulator API](https://docs.kernel.org/driver-api/regulator.html) +//! for more information. + +use crate::{ + bindings, + device::Device, + error::{from_err_ptr, to_result, Result}, + prelude::*, +}; + +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +mod private { + pub trait Sealed {} + + impl Sealed for super::Enabled {} + impl Sealed for super::Disabled {} + impl Sealed for super::Dynamic {} +} + +/// A trait representing the different states a [`Regulator`] can be in. +pub trait RegulatorState: private::Sealed + 'static { + /// Whether the regulator should be disabled when dropped. + const DISABLE_ON_DROP: bool; +} + +/// A state where the [`Regulator`] is known to be enabled. +/// +/// The `enable` reference count held by this state is decremented when it is +/// dropped. +pub struct Enabled; + +/// A state where this [`Regulator`] handle has not specifically asked for the +/// underlying regulator to be enabled. This means that this reference does not +/// own an `enable` reference count, but the regulator may still be on. +pub struct Disabled; + +/// A state that models the C API. The [`Regulator`] can be either enabled or +/// disabled, and the user is in control of the reference count. This is also +/// the default state. +/// +/// Use [`Regulator::is_enabled`] to check the regulator's current state. +pub struct Dynamic; + +impl RegulatorState for Enabled { + const DISABLE_ON_DROP: bool = true; +} + +impl RegulatorState for Disabled { + const DISABLE_ON_DROP: bool = false; +} + +impl RegulatorState for Dynamic { + const DISABLE_ON_DROP: bool = false; +} + +/// A trait that abstracts the ability to check if a [`Regulator`] is enabled. +pub trait IsEnabled: RegulatorState {} +impl IsEnabled for Disabled {} +impl IsEnabled for Dynamic {} + +/// An error that can occur when trying to convert a [`Regulator`] between states. +pub struct Error { + /// The error that occurred. + pub error: kernel::error::Error, + + /// The regulator that caused the error, so that the operation may be retried. + pub regulator: Regulator, +} + +/// A `struct regulator` abstraction. +/// +/// # Examples +/// +/// ## Enabling a regulator +/// +/// This example uses [`Regulator`], which is suitable for drivers that +/// enable a regulator at probe time and leave them on until the device is +/// removed or otherwise shutdown. +/// +/// These users can store [`Regulator`] directly in their driver's +/// private data struct. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled}; +/// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // The voltage can be set before enabling the regulator if needed, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Enables the regulator, consuming the previous value. +/// // +/// // From now on, the regulator is known to be enabled because of the type +/// // `Enabled`. +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_enabled().map_err(|error| error.error)?; +/// +/// // The voltage can also be set after enabling the regulator, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// A more concise shortcut is available for enabling a regulator. This is +/// equivalent to `regulator_get_enable()`: +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Enabled}; +/// fn enable(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator and enable it. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Disabling a regulator +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Enabled, Disabled}; +/// fn disable(dev: &Device, regulator: Regulator) -> Result { +/// // We can also disable an enabled regulator without reliquinshing our +/// // refcount: +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_disabled().map_err(|error| error.error)?; +/// +/// // The refcount will be decremented when `regulator` is dropped. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Using [`Regulator`] +/// +/// This example mimics the behavior of the C API, where the user is in +/// control of the enabled reference count. This is useful for drivers that +/// might call enable and disable to manage the `enable` reference count at +/// runtime, perhaps as a result of `open()` and `close()` calls or whatever +/// other driver-specific or subsystem-specific hooks. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Dynamic}; +/// struct PrivateData { +/// regulator: Regulator, +/// } +/// +/// // A fictictious probe function that obtains a regulator and sets it up. +/// fn probe(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let mut regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// Ok(PrivateData { regulator }) +/// } +/// +/// // A fictictious function that indicates that the device is going to be used. +/// fn open(dev: &Device, data: &mut PrivateData) -> Result { +/// // Increase the `enabled` reference count. +/// data.regulator.enable()?; +/// +/// Ok(()) +/// } +/// +/// fn close(dev: &Device, data: &mut PrivateData) -> Result { +/// // Decrease the `enabled` reference count. +/// data.regulator.disable()?; +/// +/// Ok(()) +/// } +/// +/// fn remove(dev: &Device, data: PrivateData) -> Result { +/// // `PrivateData` is dropped here, which will drop the +/// // `Regulator` in turn. +/// // +/// // The reference that was obtained by `regulator_get()` will be +/// // released, but it is up to the user to make sure that the number of calls +/// // to `enable()` and `disabled()` are balanced before this point. +/// Ok(()) +/// } +/// ``` +/// +/// # Invariants +/// +/// - `inner` is a non-null wrapper over a pointer to a `struct +/// regulator` obtained from [`regulator_get()`]. +/// +/// [`regulator_get()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_get +pub struct Regulator +where + State: RegulatorState, +{ + inner: NonNull, + _phantom: PhantomData, +} + +impl Regulator { + /// Sets the voltage for the regulator. + /// + /// This can be used to ensure that the device powers up cleanly. + pub fn set_voltage(&self, min_voltage: Voltage, max_voltage: Voltage) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { + bindings::regulator_set_voltage( + self.inner.as_ptr(), + min_voltage.as_microvolts(), + max_voltage.as_microvolts(), + ) + }) + } + + /// Gets the current voltage of the regulator. + pub fn get_voltage(&self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + let voltage = unsafe { bindings::regulator_get_voltage(self.inner.as_ptr()) }; + if voltage < 0 { + Err(kernel::error::Error::from_errno(voltage)) + } else { + Ok(Voltage::from_microvolts(voltage)) + } + } + + fn get_internal(dev: &Device, name: &CStr) -> Result> { + // SAFETY: It is safe to call `regulator_get()`, on a device pointer + // received from the C code. + let inner = from_err_ptr(unsafe { bindings::regulator_get(dev.as_raw(), name.as_ptr()) })?; + + // SAFETY: We can safely trust `inner` to be a pointer to a valid + // regulator if `ERR_PTR` was not returned. + let inner = unsafe { NonNull::new_unchecked(inner) }; + + Ok(Self { + inner, + _phantom: PhantomData, + }) + } + + fn enable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) }) + } + + fn disable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Attempts to convert the regulator to an enabled state. + pub fn try_into_enabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count to + // `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .enable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system and enables it. + /// + /// This is equivalent to calling `regulator_get_enable()` in the C API. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::::get_internal(dev, name)? + .try_into_enabled() + .map_err(|error| error.error) + } + + /// Attempts to convert the regulator to a disabled state. + pub fn try_into_disabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count + // to `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .disable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. The current state of + /// the regulator is unknown and it is up to the user to manage the enabled + /// reference count. + /// + /// This closely mimics the behavior of the C API and can be used to + /// dynamically manage the enabled reference count at runtime. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Increases the `enabled` reference count. + pub fn enable(&mut self) -> Result { + self.enable_internal() + } + + /// Decreases the `enabled` reference count. + pub fn disable(&mut self) -> Result { + self.disable_internal() + } +} + +impl Regulator { + /// Checks if the regulator is enabled. + pub fn is_enabled(&self) -> bool { + // SAFETY: Safe as per the type invariants of `Regulator`. + unsafe { bindings::regulator_is_enabled(self.inner.as_ptr()) != 0 } + } +} + +impl Drop for Regulator { + fn drop(&mut self) { + if T::DISABLE_ON_DROP { + // SAFETY: By the type invariants, we know that `self` owns a + // reference on the enabled refcount, so it is safe to relinquish it + // now. + unsafe { bindings::regulator_disable(self.inner.as_ptr()) }; + } + // SAFETY: By the type invariants, we know that `self` owns a reference, + // so it is safe to relinquish it now. + unsafe { bindings::regulator_put(self.inner.as_ptr()) }; + } +} + +/// A voltage. +/// +/// This type represents a voltage value in microvolts. +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Voltage(i32); + +impl Voltage { + /// Creates a new `Voltage` from a value in microvolts. + pub fn from_microvolts(uv: i32) -> Self { + Self(uv) + } + + /// Returns the value of the voltage in microvolts as an [`i32`]. + pub fn as_microvolts(self) -> i32 { + self.0 + } +} diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs index db4aa46bb121..0f4ae673256d 100644 --- a/rust/kernel/revocable.rs +++ b/rust/kernel/revocable.rs @@ -5,6 +5,8 @@ //! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence //! of a [`RevocableGuard`] ensures that objects remain valid. +use pin_init::Wrapper; + use crate::{bindings, prelude::*, sync::rcu, types::Opaque}; use core::{ marker::PhantomData, @@ -80,11 +82,11 @@ unsafe impl Sync for Revocable {} impl Revocable { /// Creates a new revocable instance of the given data. - pub fn new(data: impl PinInit) -> impl PinInit { - pin_init!(Self { + pub fn new(data: impl PinInit) -> impl PinInit { + try_pin_init!(Self { is_available: AtomicBool::new(true), data <- Opaque::pin_init(data), - }) + }? E) } /// Tries to access the revocable wrapped object. @@ -154,8 +156,10 @@ pub unsafe fn access(&self) -> &T { /// # Safety /// /// Callers must ensure that there are no more concurrent users of the revocable object. - unsafe fn revoke_internal(&self) { - if self.is_available.swap(false, Ordering::Relaxed) { + unsafe fn revoke_internal(&self) -> bool { + let revoke = self.is_available.swap(false, Ordering::Relaxed); + + if revoke { if SYNC { // SAFETY: Just an FFI call, there are no further requirements. unsafe { bindings::synchronize_rcu() }; @@ -165,6 +169,8 @@ unsafe fn revoke_internal(&self) { // `compare_exchange` above that takes `is_available` from `true` to `false`. unsafe { drop_in_place(self.data.get()) }; } + + revoke } /// Revokes access to and drops the wrapped object. @@ -172,10 +178,13 @@ unsafe fn revoke_internal(&self) { /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`], /// expecting that there are no concurrent users of the object. /// + /// Returns `true` if `&self` has been revoked with this call, `false` if it was revoked + /// already. + /// /// # Safety /// /// Callers must ensure that there are no more concurrent users of the revocable object. - pub unsafe fn revoke_nosync(&self) { + pub unsafe fn revoke_nosync(&self) -> bool { // SAFETY: By the safety requirement of this function, the caller ensures that nobody is // accessing the data anymore and hence we don't have to wait for the grace period to // finish. @@ -189,7 +198,10 @@ pub unsafe fn revoke_nosync(&self) { /// If there are concurrent users of the object (i.e., ones that called /// [`Revocable::try_access`] beforehand and still haven't dropped the returned guard), this /// function waits for the concurrent access to complete before dropping the wrapped object. - pub fn revoke(&self) { + /// + /// Returns `true` if `&self` has been revoked with this call, `false` if it was revoked + /// already. + pub fn revoke(&self) -> bool { // SAFETY: By passing `true` we ask `revoke_internal` to wait for the grace period to // finish. unsafe { self.revoke_internal::() } @@ -221,6 +233,10 @@ fn drop(self: Pin<&mut Self>) { /// /// The RCU read-side lock is held while the guard is alive. pub struct RevocableGuard<'a, T> { + // This can't use the `&'a T` type because references that appear in function arguments must + // not become dangling during the execution of the function, which can happen if the + // `RevocableGuard` is passed as a function argument and then dropped during execution of the + // function. data_ref: *const T, _rcu_guard: rcu::Guard, _p: PhantomData<&'a ()>, diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index 7a9403eb6e5b..8f199b1a3bb1 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -37,7 +37,7 @@ pub fn call_printf(&self, args: core::fmt::Arguments<'_>) { bindings::seq_printf( self.inner.get(), c_str!("%pA").as_char_ptr(), - &args as *const _ as *const crate::ffi::c_void, + core::ptr::from_ref(&args).cast::(), ); } } diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index a927db8e079c..6c892550c0ba 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -3,7 +3,7 @@ //! String representations. use crate::alloc::{flags::*, AllocError, KVec}; -use core::fmt::{self, Write}; +use crate::fmt::{self, Write}; use core::ops::{self, Deref, DerefMut, Index}; use crate::prelude::*; @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool { #[inline] pub const fn from_bytes(bytes: &[u8]) -> &Self { // SAFETY: `BStr` is transparent to `[u8]`. - unsafe { &*(bytes as *const [u8] as *const BStr) } + unsafe { &*(core::ptr::from_ref(bytes) as *const BStr) } } /// Strip a prefix from `self`. Delegates to [`slice::strip_prefix`]. @@ -54,14 +54,14 @@ impl fmt::Display for BStr { /// Formats printable ASCII characters, escaping the rest. /// /// ``` - /// # use kernel::{fmt, b_str, str::{BStr, CString}}; + /// # use kernel::{prelude::fmt, b_str, str::{BStr, CString}}; /// let ascii = b_str!("Hello, BStr!"); - /// let s = CString::try_from_fmt(fmt!("{}", ascii))?; - /// assert_eq!(s.as_bytes(), "Hello, BStr!".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{ascii}"))?; + /// assert_eq!(s.to_bytes(), "Hello, BStr!".as_bytes()); /// /// let non_ascii = b_str!("🦀"); - /// let s = CString::try_from_fmt(fmt!("{}", non_ascii))?; - /// assert_eq!(s.as_bytes(), "\\xf0\\x9f\\xa6\\x80".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{non_ascii}"))?; + /// assert_eq!(s.to_bytes(), "\\xf0\\x9f\\xa6\\x80".as_bytes()); /// # Ok::<(), kernel::error::Error>(()) /// ``` fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -85,15 +85,15 @@ impl fmt::Debug for BStr { /// escaping the rest. /// /// ``` - /// # use kernel::{fmt, b_str, str::{BStr, CString}}; + /// # use kernel::{prelude::fmt, b_str, str::{BStr, CString}}; /// // Embedded double quotes are escaped. /// let ascii = b_str!("Hello, \"BStr\"!"); - /// let s = CString::try_from_fmt(fmt!("{:?}", ascii))?; - /// assert_eq!(s.as_bytes(), "\"Hello, \\\"BStr\\\"!\"".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{ascii:?}"))?; + /// assert_eq!(s.to_bytes(), "\"Hello, \\\"BStr\\\"!\"".as_bytes()); /// /// let non_ascii = b_str!("😺"); - /// let s = CString::try_from_fmt(fmt!("{:?}", non_ascii))?; - /// assert_eq!(s.as_bytes(), "\"\\xf0\\x9f\\x98\\xba\"".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{non_ascii:?}"))?; + /// assert_eq!(s.to_bytes(), "\"\\xf0\\x9f\\x98\\xba\"".as_bytes()); /// # Ok::<(), kernel::error::Error>(()) /// ``` fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -175,6 +175,15 @@ macro_rules! b_str { }}; } +/// Returns a C pointer to the string. +// It is a free function rather than a method on an extension trait because: +// +// - error[E0379]: functions in trait impls cannot be declared const +#[inline] +pub const fn as_char_ptr_in_const_context(c_str: &CStr) -> *const c_char { + c_str.0.as_ptr() +} + /// Possible errors when using conversion functions in [`CStr`]. #[derive(Debug, Clone, Copy)] pub enum CStrConvertError { @@ -232,12 +241,12 @@ pub const fn is_empty(&self) -> bool { /// last at least `'a`. When `CStr` is alive, the memory pointed by `ptr` /// must not be mutated. #[inline] - pub unsafe fn from_char_ptr<'a>(ptr: *const crate::ffi::c_char) -> &'a Self { + pub unsafe fn from_char_ptr<'a>(ptr: *const c_char) -> &'a Self { // SAFETY: The safety precondition guarantees `ptr` is a valid pointer // to a `NUL`-terminated C string. let len = unsafe { bindings::strlen(ptr) } + 1; // SAFETY: Lifetime guaranteed by the safety precondition. - let bytes = unsafe { core::slice::from_raw_parts(ptr as _, len) }; + let bytes = unsafe { core::slice::from_raw_parts(ptr.cast(), len) }; // SAFETY: As `len` is returned by `strlen`, `bytes` does not contain interior `NUL`. // As we have added 1 to `len`, the last byte is known to be `NUL`. unsafe { Self::from_bytes_with_nul_unchecked(bytes) } @@ -290,27 +299,49 @@ pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError #[inline] pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr { // SAFETY: Properties of `bytes` guaranteed by the safety precondition. - unsafe { &mut *(bytes as *mut [u8] as *mut CStr) } + unsafe { &mut *(core::ptr::from_mut(bytes) as *mut CStr) } } /// Returns a C pointer to the string. + /// + /// Using this function in a const context is deprecated in favor of + /// [`as_char_ptr_in_const_context`] in preparation for replacing `CStr` with `core::ffi::CStr` + /// which does not have this method. #[inline] - pub const fn as_char_ptr(&self) -> *const crate::ffi::c_char { - self.0.as_ptr() + pub const fn as_char_ptr(&self) -> *const c_char { + as_char_ptr_in_const_context(self) } /// Convert the string to a byte slice without the trailing `NUL` byte. #[inline] - pub fn as_bytes(&self) -> &[u8] { + pub fn to_bytes(&self) -> &[u8] { &self.0[..self.len()] } + /// Convert the string to a byte slice without the trailing `NUL` byte. + /// + /// This function is deprecated in favor of [`Self::to_bytes`] in preparation for replacing + /// `CStr` with `core::ffi::CStr` which does not have this method. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.to_bytes() + } + /// Convert the string to a byte slice containing the trailing `NUL` byte. #[inline] - pub const fn as_bytes_with_nul(&self) -> &[u8] { + pub const fn to_bytes_with_nul(&self) -> &[u8] { &self.0 } + /// Convert the string to a byte slice containing the trailing `NUL` byte. + /// + /// This function is deprecated in favor of [`Self::to_bytes_with_nul`] in preparation for + /// replacing `CStr` with `core::ffi::CStr` which does not have this method. + #[inline] + pub const fn as_bytes_with_nul(&self) -> &[u8] { + self.to_bytes_with_nul() + } + /// Yields a [`&str`] slice if the [`CStr`] contains valid UTF-8. /// /// If the contents of the [`CStr`] are valid UTF-8 data, this @@ -429,20 +460,20 @@ impl fmt::Display for CStr { /// /// ``` /// # use kernel::c_str; - /// # use kernel::fmt; + /// # use kernel::prelude::fmt; /// # use kernel::str::CStr; /// # use kernel::str::CString; /// let penguin = c_str!("🐧"); - /// let s = CString::try_from_fmt(fmt!("{}", penguin))?; - /// assert_eq!(s.as_bytes_with_nul(), "\\xf0\\x9f\\x90\\xa7\0".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{penguin}"))?; + /// assert_eq!(s.to_bytes_with_nul(), "\\xf0\\x9f\\x90\\xa7\0".as_bytes()); /// /// let ascii = c_str!("so \"cool\""); - /// let s = CString::try_from_fmt(fmt!("{}", ascii))?; - /// assert_eq!(s.as_bytes_with_nul(), "so \"cool\"\0".as_bytes()); + /// let s = CString::try_from_fmt(fmt!("{ascii}"))?; + /// assert_eq!(s.to_bytes_with_nul(), "so \"cool\"\0".as_bytes()); /// # Ok::<(), kernel::error::Error>(()) /// ``` fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for &c in self.as_bytes() { + for &c in self.to_bytes() { if (0x20..0x7f).contains(&c) { // Printable character. f.write_char(c as char)?; @@ -459,16 +490,16 @@ impl fmt::Debug for CStr { /// /// ``` /// # use kernel::c_str; - /// # use kernel::fmt; + /// # use kernel::prelude::fmt; /// # use kernel::str::CStr; /// # use kernel::str::CString; /// let penguin = c_str!("🐧"); - /// let s = CString::try_from_fmt(fmt!("{:?}", penguin))?; + /// let s = CString::try_from_fmt(fmt!("{penguin:?}"))?; /// assert_eq!(s.as_bytes_with_nul(), "\"\\xf0\\x9f\\x90\\xa7\"\0".as_bytes()); /// /// // Embedded double quotes are escaped. /// let ascii = c_str!("so \"cool\""); - /// let s = CString::try_from_fmt(fmt!("{:?}", ascii))?; + /// let s = CString::try_from_fmt(fmt!("{ascii:?}"))?; /// assert_eq!(s.as_bytes_with_nul(), "\"so \\\"cool\\\"\"\0".as_bytes()); /// # Ok::<(), kernel::error::Error>(()) /// ``` @@ -578,7 +609,7 @@ mod tests { macro_rules! format { ($($f:tt)*) => ({ - CString::try_from_fmt(::kernel::fmt!($($f)*))?.to_str()? + CString::try_from_fmt(fmt!($($f)*))?.to_str()? }) } @@ -728,9 +759,9 @@ fn new() -> Self { pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self { // INVARIANT: The safety requirements guarantee the type invariants. Self { - beg: pos as _, - pos: pos as _, - end: end as _, + beg: pos as usize, + pos: pos as usize, + end: end as usize, } } @@ -755,7 +786,7 @@ pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self { /// /// N.B. It may point to invalid memory. pub(crate) fn pos(&self) -> *mut u8 { - self.pos as _ + self.pos as *mut u8 } /// Returns the number of bytes written to the formatter. @@ -840,14 +871,14 @@ fn write_str(&mut self, s: &str) -> fmt::Result { /// # Examples /// /// ``` -/// use kernel::{str::CString, fmt}; +/// use kernel::{str::CString, prelude::fmt}; /// /// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20))?; -/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes()); +/// assert_eq!(s.to_bytes_with_nul(), "abc1020\0".as_bytes()); /// /// let tmp = "testing"; /// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123))?; -/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes()); +/// assert_eq!(s.to_bytes_with_nul(), "testing123\0".as_bytes()); /// /// // This fails because it has an embedded `NUL` byte. /// let s = CString::try_from_fmt(fmt!("a\0b{}", 123)); @@ -917,7 +948,7 @@ impl<'a> TryFrom<&'a CStr> for CString { fn try_from(cstr: &'a CStr) -> Result { let mut buf = KVec::new(); - buf.extend_from_slice(cstr.as_bytes_with_nul(), GFP_KERNEL)?; + buf.extend_from_slice(cstr.to_bytes_with_nul(), GFP_KERNEL)?; // INVARIANT: The `CStr` and `CString` types have the same invariants for // the string data, and we copied it over without changes. @@ -930,9 +961,3 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } - -/// A convenience alias for [`core::format_args`]. -#[macro_export] -macro_rules! fmt { - ($($f:tt)*) => ( ::core::format_args!($($f)*) ) -} diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 36a719015583..00f9b558a3ad 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -10,6 +10,8 @@ use pin_init; mod arc; +pub mod aref; +pub mod completion; mod condvar; pub mod lock; mod locked_by; @@ -17,6 +19,7 @@ pub mod rcu; pub use arc::{Arc, ArcBorrow, UniqueArc}; +pub use completion::Completion; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy}; pub use lock::mutex::{new_mutex, Mutex, MutexGuard}; @@ -39,7 +42,7 @@ impl LockClassKey { /// Initializes a dynamically allocated lock class key. In the common case of using a /// statically allocated lock class key, the static_lock_class! macro should be used instead. /// - /// # Example + /// # Examples /// ``` /// # use kernel::c_str; /// # use kernel::alloc::KBox; @@ -93,8 +96,11 @@ fn drop(self: Pin<&mut Self>) { macro_rules! static_lock_class { () => {{ static CLASS: $crate::sync::LockClassKey = - // SAFETY: lockdep expects uninitialized memory when it's handed a statically allocated - // lock_class_key + // Lockdep expects uninitialized memory when it's handed a statically allocated `struct + // lock_class_key`. + // + // SAFETY: `LockClassKey` transparently wraps `Opaque` which permits uninitialized + // memory. unsafe { ::core::mem::MaybeUninit::uninit().assume_init() }; $crate::prelude::Pin::static_ref(&CLASS) }}; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index c7af0aa48a0a..63a66761d0c7 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -19,12 +19,14 @@ use crate::{ alloc::{AllocError, Flags, KBox}, bindings, + ffi::c_void, init::InPlaceInit, try_init, types::{ForeignOwnable, Opaque}, }; use core::{ alloc::Layout, + borrow::{Borrow, BorrowMut}, fmt, marker::PhantomData, mem::{ManuallyDrop, MaybeUninit}, @@ -140,10 +142,9 @@ pub struct Arc { _p: PhantomData>, } -#[doc(hidden)] #[pin_data] #[repr(C)] -pub struct ArcInner { +struct ArcInner { refcount: Opaque, data: T, } @@ -372,20 +373,22 @@ pub fn into_unique_or_drop(self) -> Option>> { } } -// SAFETY: The `into_foreign` function returns a pointer that is well-aligned. +// SAFETY: The pointer returned by `into_foreign` comes from a well aligned +// pointer to `ArcInner`. unsafe impl ForeignOwnable for Arc { - type PointedTo = ArcInner; + const FOREIGN_ALIGN: usize = core::mem::align_of::>(); + type Borrowed<'a> = ArcBorrow<'a, T>; type BorrowedMut<'a> = Self::Borrowed<'a>; - fn into_foreign(self) -> *mut Self::PointedTo { - ManuallyDrop::new(self).ptr.as_ptr() + fn into_foreign(self) -> *mut c_void { + ManuallyDrop::new(self).ptr.as_ptr().cast() } - unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self { + unsafe fn from_foreign(ptr: *mut c_void) -> Self { // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous // call to `Self::into_foreign`. - let inner = unsafe { NonNull::new_unchecked(ptr) }; + let inner = unsafe { NonNull::new_unchecked(ptr.cast::>()) }; // SAFETY: By the safety requirement of this function, we know that `ptr` came from // a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and @@ -393,20 +396,20 @@ unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self { unsafe { Self::from_inner(inner) } } - unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> ArcBorrow<'a, T> { + unsafe fn borrow<'a>(ptr: *mut c_void) -> ArcBorrow<'a, T> { // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous // call to `Self::into_foreign`. - let inner = unsafe { NonNull::new_unchecked(ptr) }; + let inner = unsafe { NonNull::new_unchecked(ptr.cast::>()) }; // SAFETY: The safety requirements of `from_foreign` ensure that the object remains alive // for the lifetime of the returned value. unsafe { ArcBorrow::new(inner) } } - unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> ArcBorrow<'a, T> { + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> ArcBorrow<'a, T> { // SAFETY: The safety requirements for `borrow_mut` are a superset of the safety // requirements for `borrow`. - unsafe { Self::borrow(ptr) } + unsafe { ::borrow(ptr) } } } @@ -426,6 +429,31 @@ fn as_ref(&self) -> &T { } } +/// # Examples +/// +/// ``` +/// # use core::borrow::Borrow; +/// # use kernel::sync::Arc; +/// struct Foo>(B); +/// +/// // Owned instance. +/// let owned = Foo(1); +/// +/// // Shared instance. +/// let arc = Arc::new(1, GFP_KERNEL)?; +/// let shared = Foo(arc.clone()); +/// +/// let i = 1; +/// // Borrowed from `i`. +/// let borrowed = Foo(&i); +/// # Ok::<(), Error>(()) +/// ``` +impl Borrow for Arc { + fn borrow(&self) -> &T { + self.deref() + } +} + impl Clone for Arc { fn clone(&self) -> Self { // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is @@ -834,6 +862,56 @@ fn deref_mut(&mut self) -> &mut Self::Target { } } +/// # Examples +/// +/// ``` +/// # use core::borrow::Borrow; +/// # use kernel::sync::UniqueArc; +/// struct Foo>(B); +/// +/// // Owned instance. +/// let owned = Foo(1); +/// +/// // Owned instance using `UniqueArc`. +/// let arc = UniqueArc::new(1, GFP_KERNEL)?; +/// let shared = Foo(arc); +/// +/// let i = 1; +/// // Borrowed from `i`. +/// let borrowed = Foo(&i); +/// # Ok::<(), Error>(()) +/// ``` +impl Borrow for UniqueArc { + fn borrow(&self) -> &T { + self.deref() + } +} + +/// # Examples +/// +/// ``` +/// # use core::borrow::BorrowMut; +/// # use kernel::sync::UniqueArc; +/// struct Foo>(B); +/// +/// // Owned instance. +/// let owned = Foo(1); +/// +/// // Owned instance using `UniqueArc`. +/// let arc = UniqueArc::new(1, GFP_KERNEL)?; +/// let shared = Foo(arc); +/// +/// let mut i = 1; +/// // Borrowed from `i`. +/// let borrowed = Foo(&mut i); +/// # Ok::<(), Error>(()) +/// ``` +impl BorrowMut for UniqueArc { + fn borrow_mut(&mut self) -> &mut T { + self.deref_mut() + } +} + impl fmt::Display for UniqueArc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.deref(), f) diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs new file mode 100644 index 000000000000..dbd77bb68617 --- /dev/null +++ b/rust/kernel/sync/aref.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Internal reference counting support. + +use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull}; + +/// Types that are _always_ reference counted. +/// +/// It allows such types to define their own custom ref increment and decrement functions. +/// Additionally, it allows users to convert from a shared reference `&T` to an owned reference +/// [`ARef`]. +/// +/// This is usually implemented by wrappers to existing structures on the C side of the code. For +/// Rust code, the recommendation is to use [`Arc`](crate::sync::Arc) to create reference-counted +/// instances of a type. +/// +/// # Safety +/// +/// Implementers must ensure that increments to the reference count keep the object alive in memory +/// at least until matching decrements are performed. +/// +/// Implementers must also ensure that all instances are reference-counted. (Otherwise they +/// won't be able to honour the requirement that [`AlwaysRefCounted::inc_ref`] keep the object +/// alive.) +pub unsafe trait AlwaysRefCounted { + /// Increments the reference count on the object. + fn inc_ref(&self); + + /// Decrements the reference count on the object. + /// + /// Frees the object when the count reaches zero. + /// + /// # Safety + /// + /// Callers must ensure that there was a previous matching increment to the reference count, + /// and that the object is no longer used after its reference count is decremented (as it may + /// result in the object being freed), unless the caller owns another increment on the refcount + /// (e.g., it calls [`AlwaysRefCounted::inc_ref`] twice, then calls + /// [`AlwaysRefCounted::dec_ref`] once). + unsafe fn dec_ref(obj: NonNull); +} + +/// An owned reference to an always-reference-counted object. +/// +/// The object's reference count is automatically decremented when an instance of [`ARef`] is +/// dropped. It is also automatically incremented when a new instance is created via +/// [`ARef::clone`]. +/// +/// # Invariants +/// +/// The pointer stored in `ptr` is non-null and valid for the lifetime of the [`ARef`] instance. In +/// particular, the [`ARef`] instance owns an increment on the underlying object's reference count. +pub struct ARef { + ptr: NonNull, + _p: PhantomData, +} + +// SAFETY: It is safe to send `ARef` to another thread when the underlying `T` is `Sync` because +// it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, it needs +// `T` to be `Send` because any thread that has an `ARef` may ultimately access `T` using a +// mutable reference, for example, when the reference count reaches zero and `T` is dropped. +unsafe impl Send for ARef {} + +// SAFETY: It is safe to send `&ARef` to another thread when the underlying `T` is `Sync` +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, +// it needs `T` to be `Send` because any thread that has a `&ARef` may clone it and get an +// `ARef` on that thread, so the thread may ultimately access `T` using a mutable reference, for +// example, when the reference count reaches zero and `T` is dropped. +unsafe impl Sync for ARef {} + +impl ARef { + /// Creates a new instance of [`ARef`]. + /// + /// It takes over an increment of the reference count on the underlying object. + /// + /// # Safety + /// + /// Callers must ensure that the reference count was incremented at least once, and that they + /// are properly relinquishing one increment. That is, if there is only one increment, callers + /// must not use the underlying object anymore -- it is only safe to do so via the newly + /// created [`ARef`]. + pub unsafe fn from_raw(ptr: NonNull) -> Self { + // INVARIANT: The safety requirements guarantee that the new instance now owns the + // increment on the refcount. + Self { + ptr, + _p: PhantomData, + } + } + + /// Consumes the `ARef`, returning a raw pointer. + /// + /// This function does not change the refcount. After calling this function, the caller is + /// responsible for the refcount previously managed by the `ARef`. + /// + /// # Examples + /// + /// ``` + /// use core::ptr::NonNull; + /// use kernel::types::{ARef, AlwaysRefCounted}; + /// + /// struct Empty {} + /// + /// # // SAFETY: TODO. + /// unsafe impl AlwaysRefCounted for Empty { + /// fn inc_ref(&self) {} + /// unsafe fn dec_ref(_obj: NonNull) {} + /// } + /// + /// let mut data = Empty {}; + /// let ptr = NonNull::::new(&mut data).unwrap(); + /// # // SAFETY: TODO. + /// let data_ref: ARef = unsafe { ARef::from_raw(ptr) }; + /// let raw_ptr: NonNull = ARef::into_raw(data_ref); + /// + /// assert_eq!(ptr, raw_ptr); + /// ``` + pub fn into_raw(me: Self) -> NonNull { + ManuallyDrop::new(me).ptr + } +} + +impl Clone for ARef { + fn clone(&self) -> Self { + self.inc_ref(); + // SAFETY: We just incremented the refcount above. + unsafe { Self::from_raw(self.ptr) } + } +} + +impl Deref for ARef { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: The type invariants guarantee that the object is valid. + unsafe { self.ptr.as_ref() } + } +} + +impl From<&T> for ARef { + fn from(b: &T) -> Self { + b.inc_ref(); + // SAFETY: We just incremented the refcount above. + unsafe { Self::from_raw(NonNull::from(b)) } + } +} + +impl Drop for ARef { + fn drop(&mut self) { + // SAFETY: The type invariants guarantee that the `ARef` owns the reference we're about to + // decrement. + unsafe { T::dec_ref(self.ptr) }; + } +} diff --git a/rust/kernel/sync/completion.rs b/rust/kernel/sync/completion.rs new file mode 100644 index 000000000000..c50012a940a3 --- /dev/null +++ b/rust/kernel/sync/completion.rs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Completion support. +//! +//! Reference: +//! +//! C header: [`include/linux/completion.h`](srctree/include/linux/completion.h) + +use crate::{bindings, prelude::*, types::Opaque}; + +/// Synchronization primitive to signal when a certain task has been completed. +/// +/// The [`Completion`] synchronization primitive signals when a certain task has been completed by +/// waking up other tasks that have been queued up to wait for the [`Completion`] to be completed. +/// +/// # Examples +/// +/// ``` +/// use kernel::sync::{Arc, Completion}; +/// use kernel::workqueue::{self, impl_has_work, new_work, Work, WorkItem}; +/// +/// #[pin_data] +/// struct MyTask { +/// #[pin] +/// work: Work, +/// #[pin] +/// done: Completion, +/// } +/// +/// impl_has_work! { +/// impl HasWork for MyTask { self.work } +/// } +/// +/// impl MyTask { +/// fn new() -> Result> { +/// let this = Arc::pin_init(pin_init!(MyTask { +/// work <- new_work!("MyTask::work"), +/// done <- Completion::new(), +/// }), GFP_KERNEL)?; +/// +/// let _ = workqueue::system().enqueue(this.clone()); +/// +/// Ok(this) +/// } +/// +/// fn wait_for_completion(&self) { +/// self.done.wait_for_completion(); +/// +/// pr_info!("Completion: task complete\n"); +/// } +/// } +/// +/// impl WorkItem for MyTask { +/// type Pointer = Arc; +/// +/// fn run(this: Arc) { +/// // process this task +/// this.done.complete_all(); +/// } +/// } +/// +/// let task = MyTask::new()?; +/// task.wait_for_completion(); +/// # Ok::<(), Error>(()) +/// ``` +#[pin_data] +pub struct Completion { + #[pin] + inner: Opaque, +} + +// SAFETY: `Completion` is safe to be send to any task. +unsafe impl Send for Completion {} + +// SAFETY: `Completion` is safe to be accessed concurrently. +unsafe impl Sync for Completion {} + +impl Completion { + /// Create an initializer for a new [`Completion`]. + pub fn new() -> impl PinInit { + pin_init!(Self { + inner <- Opaque::ffi_init(|slot: *mut bindings::completion| { + // SAFETY: `slot` is a valid pointer to an uninitialized `struct completion`. + unsafe { bindings::init_completion(slot) }; + }), + }) + } + + fn as_raw(&self) -> *mut bindings::completion { + self.inner.get() + } + + /// Signal all tasks waiting on this completion. + /// + /// This method wakes up all tasks waiting on this completion; after this operation the + /// completion is permanently done, i.e. signals all current and future waiters. + pub fn complete_all(&self) { + // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`. + unsafe { bindings::complete_all(self.as_raw()) }; + } + + /// Wait for completion of a task. + /// + /// This method waits for the completion of a task; it is not interruptible and there is no + /// timeout. + /// + /// See also [`Completion::complete_all`]. + pub fn wait_for_completion(&self) { + // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`. + unsafe { bindings::wait_for_completion(self.as_raw()) }; + } +} diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs index caebf03f553b..c6ec64295c9f 100644 --- a/rust/kernel/sync/condvar.rs +++ b/rust/kernel/sync/condvar.rs @@ -216,6 +216,7 @@ fn notify(&self, count: c_int) { /// This method behaves like `notify_one`, except that it hints to the scheduler that the /// current thread is about to go to sleep, so it should schedule the target thread on the same /// CPU. + #[inline] pub fn notify_sync(&self) { // SAFETY: `wait_queue_head` points to valid memory. unsafe { bindings::__wake_up_sync(self.wait_queue_head.get(), TASK_NORMAL) }; @@ -225,6 +226,7 @@ pub fn notify_sync(&self) { /// /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost /// completely (as opposed to automatically waking up the next waiter). + #[inline] pub fn notify_one(&self) { self.notify(1); } @@ -233,6 +235,7 @@ pub fn notify_one(&self) { /// /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost /// completely (as opposed to automatically waking up the next waiter). + #[inline] pub fn notify_all(&self) { self.notify(0); } diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index e82fa5be289c..27202beef90c 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -175,6 +175,8 @@ pub fn lock(&self) -> Guard<'_, T, B> { /// Tries to acquire the lock. /// /// Returns a guard that can be used to access the data protected by the lock if successful. + // `Option` is not `#[must_use]` even if `T` is, thus the attribute is needed here. + #[must_use = "if unused, the lock will be immediately unlocked"] pub fn try_lock(&self) -> Option> { // SAFETY: The constructor of the type calls `init`, so the existence of the object proves // that `init` was called. diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs index d7e6e59e124b..0ec985d560c8 100644 --- a/rust/kernel/sync/poll.rs +++ b/rust/kernel/sync/poll.rs @@ -9,9 +9,8 @@ fs::File, prelude::*, sync::{CondVar, LockClassKey}, - types::Opaque, }; -use core::ops::Deref; +use core::{marker::PhantomData, ops::Deref}; /// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -23,58 +22,43 @@ macro_rules! new_poll_condvar { }; } -/// Wraps the kernel's `struct poll_table`. +/// Wraps the kernel's `poll_table`. /// /// # Invariants /// -/// This struct contains a valid `struct poll_table`. -/// -/// For a `struct poll_table` to be valid, its `_qproc` function must follow the safety -/// requirements of `_qproc` functions: -/// -/// * The `_qproc` function is given permission to enqueue a waiter to the provided `poll_table` -/// during the call. Once the waiter is removed and an rcu grace period has passed, it must no -/// longer access the `wait_queue_head`. +/// The pointer must be null or reference a valid `poll_table`. #[repr(transparent)] -pub struct PollTable(Opaque); +pub struct PollTable<'a> { + table: *mut bindings::poll_table, + _lifetime: PhantomData<&'a bindings::poll_table>, +} -impl PollTable { - /// Creates a reference to a [`PollTable`] from a valid pointer. +impl<'a> PollTable<'a> { + /// Creates a [`PollTable`] from a valid pointer. /// /// # Safety /// - /// The caller must ensure that for the duration of `'a`, the pointer will point at a valid poll - /// table (as defined in the type invariants). - /// - /// The caller must also ensure that the `poll_table` is only accessed via the returned - /// reference for the duration of `'a`. - pub unsafe fn from_ptr<'a>(ptr: *mut bindings::poll_table) -> &'a mut PollTable { - // SAFETY: The safety requirements guarantee the validity of the dereference, while the - // `PollTable` type being transparent makes the cast ok. - unsafe { &mut *ptr.cast() } - } - - fn get_qproc(&self) -> bindings::poll_queue_proc { - let ptr = self.0.get(); - // SAFETY: The `ptr` is valid because it originates from a reference, and the `_qproc` - // field is not modified concurrently with this call since we have an immutable reference. - unsafe { (*ptr)._qproc } + /// The pointer must be null or reference a valid `poll_table` for the duration of `'a`. + pub unsafe fn from_raw(table: *mut bindings::poll_table) -> Self { + // INVARIANTS: The safety requirements are the same as the struct invariants. + PollTable { + table, + _lifetime: PhantomData, + } } /// Register this [`PollTable`] with the provided [`PollCondVar`], so that it can be notified /// using the condition variable. - pub fn register_wait(&mut self, file: &File, cv: &PollCondVar) { - if let Some(qproc) = self.get_qproc() { - // SAFETY: The pointers to `file` and `self` need to be valid for the duration of this - // call to `qproc`, which they are because they are references. - // - // The `cv.wait_queue_head` pointer must be valid until an rcu grace period after the - // waiter is removed. The `PollCondVar` is pinned, so before `cv.wait_queue_head` can - // be destroyed, the destructor must run. That destructor first removes all waiters, - // and then waits for an rcu grace period. Therefore, `cv.wait_queue_head` is valid for - // long enough. - unsafe { qproc(file.as_ptr() as _, cv.wait_queue_head.get(), self.0.get()) }; - } + pub fn register_wait(&self, file: &File, cv: &PollCondVar) { + // SAFETY: + // * `file.as_ptr()` references a valid file for the duration of this call. + // * `self.table` is null or references a valid poll_table for the duration of this call. + // * Since `PollCondVar` is pinned, its destructor is guaranteed to run before the memory + // containing `cv.wait_queue_head` is invalidated. Since the destructor clears all + // waiters and then waits for an rcu grace period, it's guaranteed that + // `cv.wait_queue_head` remains valid for at least an rcu grace period after the removal + // of the last waiter. + unsafe { bindings::poll_wait(file.as_ptr(), cv.wait_queue_head.get(), self.table) } } } @@ -107,6 +91,7 @@ fn deref(&self) -> &CondVar { #[pinned_drop] impl PinnedDrop for PollCondVar { + #[inline] fn drop(self: Pin<&mut Self>) { // Clear anything registered using `register_wait`. // diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 927413d85484..7d0935bc325c 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -173,6 +173,7 @@ pub fn current_raw() -> *mut bindings::task_struct { /// Callers must ensure that the returned object is only used to access a [`CurrentTask`] /// within the task context that was active when this function was called. For more details, /// see the invariants section for [`CurrentTask`]. + #[inline] pub unsafe fn current() -> impl Deref { struct TaskRef { task: *const CurrentTask, @@ -222,24 +223,28 @@ pub fn pid(&self) -> Pid { } /// Returns the UID of the given task. + #[inline] pub fn uid(&self) -> Kuid { // SAFETY: It's always safe to call `task_uid` on a valid task. Kuid::from_raw(unsafe { bindings::task_uid(self.as_ptr()) }) } /// Returns the effective UID of the given task. + #[inline] pub fn euid(&self) -> Kuid { // SAFETY: It's always safe to call `task_euid` on a valid task. Kuid::from_raw(unsafe { bindings::task_euid(self.as_ptr()) }) } /// Determines whether the given task has pending signals. + #[inline] pub fn signal_pending(&self) -> bool { // SAFETY: It's always safe to call `signal_pending` on a valid task. unsafe { bindings::signal_pending(self.as_ptr()) != 0 } } /// Returns task's pid namespace with elevated reference count + #[inline] pub fn get_pid_ns(&self) -> Option> { // SAFETY: By the type invariant, we know that `self.0` is valid. let ptr = unsafe { bindings::task_get_pid_ns(self.as_ptr()) }; @@ -255,6 +260,7 @@ pub fn get_pid_ns(&self) -> Option> { /// Returns the given task's pid in the provided pid namespace. #[doc(alias = "task_tgid_nr_ns")] + #[inline] pub fn tgid_nr_ns(&self, pidns: Option<&PidNamespace>) -> Pid { let pidns = match pidns { Some(pidns) => pidns.as_ptr(), @@ -268,6 +274,7 @@ pub fn tgid_nr_ns(&self, pidns: Option<&PidNamespace>) -> Pid { } /// Wakes up the task. + #[inline] pub fn wake_up(&self) { // SAFETY: It's always safe to call `wake_up_process` on a valid task, even if the task // running. @@ -341,11 +348,13 @@ pub fn active_pid_ns(&self) -> Option<&PidNamespace> { // SAFETY: The type invariants guarantee that `Task` is always refcounted. unsafe impl crate::types::AlwaysRefCounted for Task { + #[inline] fn inc_ref(&self) { // SAFETY: The existence of a shared reference means that the refcount is nonzero. unsafe { bindings::get_task_struct(self.as_ptr()) }; } + #[inline] unsafe fn dec_ref(obj: ptr::NonNull) { // SAFETY: The safety requirements guarantee that the refcount is nonzero. unsafe { bindings::put_task_struct(obj.cast().as_ptr()) } @@ -391,3 +400,27 @@ fn eq(&self, other: &Kuid) -> bool { } impl Eq for Kuid {} + +/// Annotation for functions that can sleep. +/// +/// Equivalent to the C side [`might_sleep()`], this function serves as +/// a debugging aid and a potential scheduling point. +/// +/// This function can only be used in a nonatomic context. +/// +/// [`might_sleep()`]: https://docs.kernel.org/driver-api/basics.html#c.might_sleep +#[track_caller] +#[inline] +pub fn might_sleep() { + #[cfg(CONFIG_DEBUG_ATOMIC_SLEEP)] + { + let loc = core::panic::Location::caller(); + let file = kernel::file_from_location(loc); + + // SAFETY: `file.as_ptr()` is valid for reading and guaranteed to be nul-terminated. + unsafe { crate::bindings::__might_sleep(file.as_ptr().cast(), loc.line() as i32) } + } + + // SAFETY: Always safe to call. + unsafe { crate::bindings::might_resched() } +} diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index a8089a98da9e..64c8dcf548d6 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -24,6 +24,9 @@ //! C header: [`include/linux/jiffies.h`](srctree/include/linux/jiffies.h). //! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h). +use core::marker::PhantomData; + +pub mod delay; pub mod hrtimer; /// The number of nanoseconds per microsecond. @@ -49,26 +52,141 @@ pub fn msecs_to_jiffies(msecs: Msecs) -> Jiffies { unsafe { bindings::__msecs_to_jiffies(msecs) } } +/// Trait for clock sources. +/// +/// Selection of the clock source depends on the use case. In some cases the usage of a +/// particular clock is mandatory, e.g. in network protocols, filesystems. In other +/// cases the user of the clock has to decide which clock is best suited for the +/// purpose. In most scenarios clock [`Monotonic`] is the best choice as it +/// provides a accurate monotonic notion of time (leap second smearing ignored). +pub trait ClockSource { + /// The kernel clock ID associated with this clock source. + /// + /// This constant corresponds to the C side `clockid_t` value. + const ID: bindings::clockid_t; + + /// Get the current time from the clock source. + /// + /// The function must return a value in the range from 0 to `KTIME_MAX`. + fn ktime_get() -> bindings::ktime_t; +} + +/// A monotonically increasing clock. +/// +/// A nonsettable system-wide clock that represents monotonic time since as +/// described by POSIX, "some unspecified point in the past". On Linux, that +/// point corresponds to the number of seconds that the system has been +/// running since it was booted. +/// +/// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the +/// CLOCK_REAL (e.g., if the system administrator manually changes the +/// clock), but is affected by frequency adjustments. This clock does not +/// count time that the system is suspended. +pub struct Monotonic; + +impl ClockSource for Monotonic { + const ID: bindings::clockid_t = bindings::CLOCK_MONOTONIC as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get()` outside of NMI context. + unsafe { bindings::ktime_get() } + } +} + +/// A settable system-wide clock that measures real (i.e., wall-clock) time. +/// +/// Setting this clock requires appropriate privileges. This clock is +/// affected by discontinuous jumps in the system time (e.g., if the system +/// administrator manually changes the clock), and by frequency adjustments +/// performed by NTP and similar applications via adjtime(3), adjtimex(2), +/// clock_adjtime(2), and ntp_adjtime(3). This clock normally counts the +/// number of seconds since 1970-01-01 00:00:00 Coordinated Universal Time +/// (UTC) except that it ignores leap seconds; near a leap second it may be +/// adjusted by leap second smearing to stay roughly in sync with UTC. Leap +/// second smearing applies frequency adjustments to the clock to speed up +/// or slow down the clock to account for the leap second without +/// discontinuities in the clock. If leap second smearing is not applied, +/// the clock will experience discontinuity around leap second adjustment. +pub struct RealTime; + +impl ClockSource for RealTime { + const ID: bindings::clockid_t = bindings::CLOCK_REALTIME as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_real()` outside of NMI context. + unsafe { bindings::ktime_get_real() } + } +} + +/// A monotonic that ticks while system is suspended. +/// +/// A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC, +/// except that it also includes any time that the system is suspended. This +/// allows applications to get a suspend-aware monotonic clock without +/// having to deal with the complications of CLOCK_REALTIME, which may have +/// discontinuities if the time is changed using settimeofday(2) or similar. +pub struct BootTime; + +impl ClockSource for BootTime { + const ID: bindings::clockid_t = bindings::CLOCK_BOOTTIME as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_boottime()` outside of NMI context. + unsafe { bindings::ktime_get_boottime() } + } +} + +/// International Atomic Time. +/// +/// A system-wide clock derived from wall-clock time but counting leap seconds. +/// +/// This clock is coupled to CLOCK_REALTIME and will be set when CLOCK_REALTIME is +/// set, or when the offset to CLOCK_REALTIME is changed via adjtimex(2). This +/// usually happens during boot and **should** not happen during normal operations. +/// However, if NTP or another application adjusts CLOCK_REALTIME by leap second +/// smearing, this clock will not be precise during leap second smearing. +/// +/// The acronym TAI refers to International Atomic Time. +pub struct Tai; + +impl ClockSource for Tai { + const ID: bindings::clockid_t = bindings::CLOCK_TAI as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_tai()` outside of NMI context. + unsafe { bindings::ktime_get_clocktai() } + } +} + /// A specific point in time. /// /// # Invariants /// /// The `inner` value is in the range from 0 to `KTIME_MAX`. #[repr(transparent)] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Instant { +#[derive(PartialEq, PartialOrd, Eq, Ord)] +pub struct Instant { inner: bindings::ktime_t, + _c: PhantomData, } -impl Instant { - /// Get the current time using `CLOCK_MONOTONIC`. +impl Clone for Instant { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Instant {} + +impl Instant { + /// Get the current time from the clock source. #[inline] pub fn now() -> Self { - // INVARIANT: The `ktime_get()` function returns a value in the range + // INVARIANT: The `ClockSource::ktime_get()` function returns a value in the range // from 0 to `KTIME_MAX`. Self { - // SAFETY: It is always safe to call `ktime_get()` outside of NMI context. - inner: unsafe { bindings::ktime_get() }, + inner: C::ktime_get(), + _c: PhantomData, } } @@ -77,86 +195,25 @@ pub fn now() -> Self { pub fn elapsed(&self) -> Delta { Self::now() - *self } + + #[inline] + pub(crate) fn as_nanos(&self) -> i64 { + self.inner + } } -impl core::ops::Sub for Instant { +impl core::ops::Sub for Instant { type Output = Delta; // By the type invariant, it never overflows. #[inline] - fn sub(self, other: Instant) -> Delta { + fn sub(self, other: Instant) -> Delta { Delta { nanos: self.inner - other.inner, } } } -/// An identifier for a clock. Used when specifying clock sources. -/// -/// -/// Selection of the clock depends on the use case. In some cases the usage of a -/// particular clock is mandatory, e.g. in network protocols, filesystems.In other -/// cases the user of the clock has to decide which clock is best suited for the -/// purpose. In most scenarios clock [`ClockId::Monotonic`] is the best choice as it -/// provides a accurate monotonic notion of time (leap second smearing ignored). -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[repr(u32)] -pub enum ClockId { - /// A settable system-wide clock that measures real (i.e., wall-clock) time. - /// - /// Setting this clock requires appropriate privileges. This clock is - /// affected by discontinuous jumps in the system time (e.g., if the system - /// administrator manually changes the clock), and by frequency adjustments - /// performed by NTP and similar applications via adjtime(3), adjtimex(2), - /// clock_adjtime(2), and ntp_adjtime(3). This clock normally counts the - /// number of seconds since 1970-01-01 00:00:00 Coordinated Universal Time - /// (UTC) except that it ignores leap seconds; near a leap second it may be - /// adjusted by leap second smearing to stay roughly in sync with UTC. Leap - /// second smearing applies frequency adjustments to the clock to speed up - /// or slow down the clock to account for the leap second without - /// discontinuities in the clock. If leap second smearing is not applied, - /// the clock will experience discontinuity around leap second adjustment. - RealTime = bindings::CLOCK_REALTIME, - /// A monotonically increasing clock. - /// - /// A nonsettable system-wide clock that represents monotonic time since—as - /// described by POSIX—"some unspecified point in the past". On Linux, that - /// point corresponds to the number of seconds that the system has been - /// running since it was booted. - /// - /// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the - /// CLOCK_REAL (e.g., if the system administrator manually changes the - /// clock), but is affected by frequency adjustments. This clock does not - /// count time that the system is suspended. - Monotonic = bindings::CLOCK_MONOTONIC, - /// A monotonic that ticks while system is suspended. - /// - /// A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC, - /// except that it also includes any time that the system is suspended. This - /// allows applications to get a suspend-aware monotonic clock without - /// having to deal with the complications of CLOCK_REALTIME, which may have - /// discontinuities if the time is changed using settimeofday(2) or similar. - BootTime = bindings::CLOCK_BOOTTIME, - /// International Atomic Time. - /// - /// A system-wide clock derived from wall-clock time but counting leap seconds. - /// - /// This clock is coupled to CLOCK_REALTIME and will be set when CLOCK_REALTIME is - /// set, or when the offset to CLOCK_REALTIME is changed via adjtimex(2). This - /// usually happens during boot and **should** not happen during normal operations. - /// However, if NTP or another application adjusts CLOCK_REALTIME by leap second - /// smearing, this clock will not be precise during leap second smearing. - /// - /// The acronym TAI refers to International Atomic Time. - TAI = bindings::CLOCK_TAI, -} - -impl ClockId { - fn into_c(self) -> bindings::clockid_t { - self as bindings::clockid_t - } -} - /// A span of time. /// /// This struct represents a span of time, with its value stored as nanoseconds. @@ -228,13 +285,31 @@ pub const fn as_nanos(self) -> i64 { /// Return the smallest number of microseconds greater than or equal /// to the value in the [`Delta`]. #[inline] - pub const fn as_micros_ceil(self) -> i64 { - self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + pub fn as_micros_ceil(self) -> i64 { + #[cfg(CONFIG_64BIT)] + { + self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + } + + #[cfg(not(CONFIG_64BIT))] + // SAFETY: It is always safe to call `ktime_to_us()` with any value. + unsafe { + bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1)) + } } /// Return the number of milliseconds in the [`Delta`]. #[inline] - pub const fn as_millis(self) -> i64 { - self.as_nanos() / NSEC_PER_MSEC + pub fn as_millis(self) -> i64 { + #[cfg(CONFIG_64BIT)] + { + self.as_nanos() / NSEC_PER_MSEC + } + + #[cfg(not(CONFIG_64BIT))] + // SAFETY: It is always safe to call `ktime_to_ms()` with any value. + unsafe { + bindings::ktime_to_ms(self.as_nanos()) + } } } diff --git a/rust/kernel/time/delay.rs b/rust/kernel/time/delay.rs new file mode 100644 index 000000000000..eb8838da62bc --- /dev/null +++ b/rust/kernel/time/delay.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Delay and sleep primitives. +//! +//! This module contains the kernel APIs related to delay and sleep that +//! have been ported or wrapped for usage by Rust code in the kernel. +//! +//! C header: [`include/linux/delay.h`](srctree/include/linux/delay.h). + +use super::Delta; +use crate::prelude::*; + +/// Sleeps for a given duration at least. +/// +/// Equivalent to the C side [`fsleep()`], flexible sleep function, +/// which automatically chooses the best sleep method based on a duration. +/// +/// `delta` must be within `[0, i32::MAX]` microseconds; +/// otherwise, it is erroneous behavior. That is, it is considered a bug +/// to call this function with an out-of-range value, in which case the function +/// will sleep for at least the maximum value in the range and may warn +/// in the future. +/// +/// The behavior above differs from the C side [`fsleep()`] for which out-of-range +/// values mean "infinite timeout" instead. +/// +/// This function can only be used in a nonatomic context. +/// +/// [`fsleep()`]: https://docs.kernel.org/timers/delay_sleep_functions.html#c.fsleep +pub fn fsleep(delta: Delta) { + // The maximum value is set to `i32::MAX` microseconds to prevent integer + // overflow inside fsleep, which could lead to unintentional infinite sleep. + const MAX_DELTA: Delta = Delta::from_micros(i32::MAX as i64); + + let delta = if (Delta::ZERO..=MAX_DELTA).contains(&delta) { + delta + } else { + // TODO: Add WARN_ONCE() when it's supported. + MAX_DELTA + }; + + // SAFETY: It is always safe to call `fsleep()` with any duration. + unsafe { + // Convert the duration to microseconds and round up to preserve + // the guarantee; `fsleep()` sleeps for at least the provided duration, + // but that it may sleep for longer under some circumstances. + bindings::fsleep(delta.as_micros_ceil() as c_ulong) + } +} diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 36e1290cd079..144e3b57cc78 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -67,27 +67,11 @@ //! A `restart` operation on a timer in the **stopped** state is equivalent to a //! `start` operation. -use super::ClockId; +use super::{ClockSource, Delta, Instant}; use crate::{prelude::*, types::Opaque}; use core::marker::PhantomData; use pin_init::PinInit; -/// A Rust wrapper around a `ktime_t`. -// NOTE: Ktime is going to be removed when hrtimer is converted to Instant/Delta. -#[repr(transparent)] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Ktime { - inner: bindings::ktime_t, -} - -impl Ktime { - /// Returns the number of nanoseconds. - #[inline] - pub fn to_ns(self) -> i64 { - self.inner - } -} - /// A timer backed by a C `struct hrtimer`. /// /// # Invariants @@ -98,7 +82,6 @@ pub fn to_ns(self) -> i64 { pub struct HrTimer { #[pin] timer: Opaque, - mode: HrTimerMode, _t: PhantomData, } @@ -112,9 +95,10 @@ unsafe impl Sync for HrTimer {} impl HrTimer { /// Return an initializer for a new timer instance. - pub fn new(mode: HrTimerMode, clock: ClockId) -> impl PinInit + pub fn new() -> impl PinInit where T: HrTimerCallback, + T: HasHrTimer, { pin_init!(Self { // INVARIANT: We initialize `timer` with `hrtimer_setup` below. @@ -126,12 +110,11 @@ pub fn new(mode: HrTimerMode, clock: ClockId) -> impl PinInit bindings::hrtimer_setup( place, Some(T::Pointer::run), - clock.into_c(), - mode.into_c(), + <>::TimerMode as HrTimerMode>::Clock::ID, + >::TimerMode::C_MODE, ); } }), - mode: mode, _t: PhantomData, }) } @@ -148,7 +131,7 @@ unsafe fn raw_get(this: *const Self) -> *mut bindings::hrtimer { // SAFETY: The field projection to `timer` does not go out of bounds, // because the caller of this function promises that `this` points to an // allocation of at least the size of `Self`. - unsafe { Opaque::raw_get(core::ptr::addr_of!((*this).timer)) } + unsafe { Opaque::cast_into(core::ptr::addr_of!((*this).timer)) } } /// Cancel an initialized and potentially running timer. @@ -193,6 +176,11 @@ pub(crate) unsafe fn raw_cancel(this: *const Self) -> bool { /// exist. A timer can be manipulated through any of the handles, and a handle /// may represent a cancelled timer. pub trait HrTimerPointer: Sync + Sized { + /// The operational mode associated with this timer. + /// + /// This defines how the expiration value is interpreted. + type TimerMode: HrTimerMode; + /// A handle representing a started or restarted timer. /// /// If the timer is running or if the timer callback is executing when the @@ -205,7 +193,7 @@ pub trait HrTimerPointer: Sync + Sized { /// Start the timer with expiry after `expires` time units. If the timer was /// already running, it is restarted with the new expiry time. - fn start(self, expires: Ktime) -> Self::TimerHandle; + fn start(self, expires: ::Expires) -> Self::TimerHandle; } /// Unsafe version of [`HrTimerPointer`] for situations where leaking the @@ -220,6 +208,11 @@ pub trait HrTimerPointer: Sync + Sized { /// [`UnsafeHrTimerPointer`] outlives any associated [`HrTimerPointer::TimerHandle`] /// instances. pub unsafe trait UnsafeHrTimerPointer: Sync + Sized { + /// The operational mode associated with this timer. + /// + /// This defines how the expiration value is interpreted. + type TimerMode: HrTimerMode; + /// A handle representing a running timer. /// /// # Safety @@ -236,7 +229,7 @@ pub unsafe trait UnsafeHrTimerPointer: Sync + Sized { /// /// Caller promises keep the timer structure alive until the timer is dead. /// Caller can ensure this by not leaking the returned [`Self::TimerHandle`]. - unsafe fn start(self, expires: Ktime) -> Self::TimerHandle; + unsafe fn start(self, expires: ::Expires) -> Self::TimerHandle; } /// A trait for stack allocated timers. @@ -246,9 +239,14 @@ pub unsafe trait UnsafeHrTimerPointer: Sync + Sized { /// Implementers must ensure that `start_scoped` does not return until the /// timer is dead and the timer handler is not running. pub unsafe trait ScopedHrTimerPointer { + /// The operational mode associated with this timer. + /// + /// This defines how the expiration value is interpreted. + type TimerMode: HrTimerMode; + /// Start the timer to run after `expires` time units and immediately /// after call `f`. When `f` returns, the timer is cancelled. - fn start_scoped(self, expires: Ktime, f: F) -> T + fn start_scoped(self, expires: ::Expires, f: F) -> T where F: FnOnce() -> T; } @@ -260,7 +258,13 @@ unsafe impl ScopedHrTimerPointer for T where T: UnsafeHrTimerPointer, { - fn start_scoped(self, expires: Ktime, f: F) -> U + type TimerMode = T::TimerMode; + + fn start_scoped( + self, + expires: <::TimerMode as HrTimerMode>::Expires, + f: F, + ) -> U where F: FnOnce() -> U, { @@ -335,6 +339,11 @@ pub unsafe trait HrTimerHandle { /// their documentation. All the methods of this trait must operate on the same /// field. pub unsafe trait HasHrTimer { + /// The operational mode associated with this timer. + /// + /// This defines how the expiration value is interpreted. + type TimerMode: HrTimerMode; + /// Return a pointer to the [`HrTimer`] within `Self`. /// /// This function is useful to get access to the value without creating @@ -382,14 +391,14 @@ unsafe fn c_timer_ptr(this: *const Self) -> *const bindings::hrtimer { /// - `this` must point to a valid `Self`. /// - Caller must ensure that the pointee of `this` lives until the timer /// fires or is canceled. - unsafe fn start(this: *const Self, expires: Ktime) { + unsafe fn start(this: *const Self, expires: ::Expires) { // SAFETY: By function safety requirement, `this` is a valid `Self`. unsafe { bindings::hrtimer_start_range_ns( Self::c_timer_ptr(this).cast_mut(), - expires.to_ns(), + expires.as_nanos(), 0, - (*Self::raw_get_timer(this)).mode.into_c(), + ::C_MODE, ); } } @@ -411,80 +420,171 @@ fn into_c(self) -> bindings::hrtimer_restart { } } -/// Operational mode of [`HrTimer`]. -// NOTE: Some of these have the same encoding on the C side, so we keep -// `repr(Rust)` and convert elsewhere. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum HrTimerMode { - /// Timer expires at the given expiration time. - Absolute, - /// Timer expires after the given expiration time interpreted as a duration from now. - Relative, - /// Timer does not move between CPU cores. - Pinned, - /// Timer handler is executed in soft irq context. - Soft, - /// Timer handler is executed in hard irq context. - Hard, - /// Timer expires at the given expiration time. - /// Timer does not move between CPU cores. - AbsolutePinned, - /// Timer expires after the given expiration time interpreted as a duration from now. - /// Timer does not move between CPU cores. - RelativePinned, - /// Timer expires at the given expiration time. - /// Timer handler is executed in soft irq context. - AbsoluteSoft, - /// Timer expires after the given expiration time interpreted as a duration from now. - /// Timer handler is executed in soft irq context. - RelativeSoft, - /// Timer expires at the given expiration time. - /// Timer does not move between CPU cores. - /// Timer handler is executed in soft irq context. - AbsolutePinnedSoft, - /// Timer expires after the given expiration time interpreted as a duration from now. - /// Timer does not move between CPU cores. - /// Timer handler is executed in soft irq context. - RelativePinnedSoft, - /// Timer expires at the given expiration time. - /// Timer handler is executed in hard irq context. - AbsoluteHard, - /// Timer expires after the given expiration time interpreted as a duration from now. - /// Timer handler is executed in hard irq context. - RelativeHard, - /// Timer expires at the given expiration time. - /// Timer does not move between CPU cores. - /// Timer handler is executed in hard irq context. - AbsolutePinnedHard, - /// Timer expires after the given expiration time interpreted as a duration from now. - /// Timer does not move between CPU cores. - /// Timer handler is executed in hard irq context. - RelativePinnedHard, +/// Time representations that can be used as expiration values in [`HrTimer`]. +pub trait HrTimerExpires { + /// Converts the expiration time into a nanosecond representation. + /// + /// This value corresponds to a raw ktime_t value, suitable for passing to kernel + /// timer functions. The interpretation (absolute vs relative) depends on the + /// associated [HrTimerMode] in use. + fn as_nanos(&self) -> i64; } -impl HrTimerMode { - fn into_c(self) -> bindings::hrtimer_mode { - use bindings::*; - match self { - HrTimerMode::Absolute => hrtimer_mode_HRTIMER_MODE_ABS, - HrTimerMode::Relative => hrtimer_mode_HRTIMER_MODE_REL, - HrTimerMode::Pinned => hrtimer_mode_HRTIMER_MODE_PINNED, - HrTimerMode::Soft => hrtimer_mode_HRTIMER_MODE_SOFT, - HrTimerMode::Hard => hrtimer_mode_HRTIMER_MODE_HARD, - HrTimerMode::AbsolutePinned => hrtimer_mode_HRTIMER_MODE_ABS_PINNED, - HrTimerMode::RelativePinned => hrtimer_mode_HRTIMER_MODE_REL_PINNED, - HrTimerMode::AbsoluteSoft => hrtimer_mode_HRTIMER_MODE_ABS_SOFT, - HrTimerMode::RelativeSoft => hrtimer_mode_HRTIMER_MODE_REL_SOFT, - HrTimerMode::AbsolutePinnedSoft => hrtimer_mode_HRTIMER_MODE_ABS_PINNED_SOFT, - HrTimerMode::RelativePinnedSoft => hrtimer_mode_HRTIMER_MODE_REL_PINNED_SOFT, - HrTimerMode::AbsoluteHard => hrtimer_mode_HRTIMER_MODE_ABS_HARD, - HrTimerMode::RelativeHard => hrtimer_mode_HRTIMER_MODE_REL_HARD, - HrTimerMode::AbsolutePinnedHard => hrtimer_mode_HRTIMER_MODE_ABS_PINNED_HARD, - HrTimerMode::RelativePinnedHard => hrtimer_mode_HRTIMER_MODE_REL_PINNED_HARD, - } +impl HrTimerExpires for Instant { + #[inline] + fn as_nanos(&self) -> i64 { + Instant::::as_nanos(self) } } +impl HrTimerExpires for Delta { + #[inline] + fn as_nanos(&self) -> i64 { + Delta::as_nanos(*self) + } +} + +mod private { + use crate::time::ClockSource; + + pub trait Sealed {} + + impl Sealed for super::AbsoluteMode {} + impl Sealed for super::RelativeMode {} + impl Sealed for super::AbsolutePinnedMode {} + impl Sealed for super::RelativePinnedMode {} + impl Sealed for super::AbsoluteSoftMode {} + impl Sealed for super::RelativeSoftMode {} + impl Sealed for super::AbsolutePinnedSoftMode {} + impl Sealed for super::RelativePinnedSoftMode {} + impl Sealed for super::AbsoluteHardMode {} + impl Sealed for super::RelativeHardMode {} + impl Sealed for super::AbsolutePinnedHardMode {} + impl Sealed for super::RelativePinnedHardMode {} +} + +/// Operational mode of [`HrTimer`]. +pub trait HrTimerMode: private::Sealed { + /// The C representation of hrtimer mode. + const C_MODE: bindings::hrtimer_mode; + + /// Type representing the clock source. + type Clock: ClockSource; + + /// Type representing the expiration specification (absolute or relative time). + type Expires: HrTimerExpires; +} + +/// Timer that expires at a fixed point in time. +pub struct AbsoluteMode(PhantomData); + +impl HrTimerMode for AbsoluteMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS; + + type Clock = C; + type Expires = Instant; +} + +/// Timer that expires after a delay from now. +pub struct RelativeMode(PhantomData); + +impl HrTimerMode for RelativeMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL; + + type Clock = C; + type Expires = Delta; +} + +/// Timer with absolute expiration time, pinned to its current CPU. +pub struct AbsolutePinnedMode(PhantomData); +impl HrTimerMode for AbsolutePinnedMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED; + + type Clock = C; + type Expires = Instant; +} + +/// Timer with relative expiration time, pinned to its current CPU. +pub struct RelativePinnedMode(PhantomData); +impl HrTimerMode for RelativePinnedMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED; + + type Clock = C; + type Expires = Delta; +} + +/// Timer with absolute expiration, handled in soft irq context. +pub struct AbsoluteSoftMode(PhantomData); +impl HrTimerMode for AbsoluteSoftMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_SOFT; + + type Clock = C; + type Expires = Instant; +} + +/// Timer with relative expiration, handled in soft irq context. +pub struct RelativeSoftMode(PhantomData); +impl HrTimerMode for RelativeSoftMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_SOFT; + + type Clock = C; + type Expires = Delta; +} + +/// Timer with absolute expiration, pinned to CPU and handled in soft irq context. +pub struct AbsolutePinnedSoftMode(PhantomData); +impl HrTimerMode for AbsolutePinnedSoftMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED_SOFT; + + type Clock = C; + type Expires = Instant; +} + +/// Timer with absolute expiration, pinned to CPU and handled in soft irq context. +pub struct RelativePinnedSoftMode(PhantomData); +impl HrTimerMode for RelativePinnedSoftMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED_SOFT; + + type Clock = C; + type Expires = Delta; +} + +/// Timer with absolute expiration, handled in hard irq context. +pub struct AbsoluteHardMode(PhantomData); +impl HrTimerMode for AbsoluteHardMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_HARD; + + type Clock = C; + type Expires = Instant; +} + +/// Timer with relative expiration, handled in hard irq context. +pub struct RelativeHardMode(PhantomData); +impl HrTimerMode for RelativeHardMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_HARD; + + type Clock = C; + type Expires = Delta; +} + +/// Timer with absolute expiration, pinned to CPU and handled in hard irq context. +pub struct AbsolutePinnedHardMode(PhantomData); +impl HrTimerMode for AbsolutePinnedHardMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED_HARD; + + type Clock = C; + type Expires = Instant; +} + +/// Timer with relative expiration, pinned to CPU and handled in hard irq context. +pub struct RelativePinnedHardMode(PhantomData); +impl HrTimerMode for RelativePinnedHardMode { + const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED_HARD; + + type Clock = C; + type Expires = Delta; +} + /// Use to implement the [`HasHrTimer`] trait. /// /// See [`module`] documentation for an example. @@ -496,12 +596,16 @@ macro_rules! impl_has_hr_timer { impl$({$($generics:tt)*})? HasHrTimer<$timer_type:ty> for $self:ty - { self.$field:ident } + { + mode : $mode:ty, + field : self.$field:ident $(,)? + } $($rest:tt)* ) => { // SAFETY: This implementation of `raw_get_timer` only compiles if the // field has the right type. unsafe impl$(<$($generics)*>)? $crate::time::hrtimer::HasHrTimer<$timer_type> for $self { + type TimerMode = $mode; #[inline] unsafe fn raw_get_timer( diff --git a/rust/kernel/time/hrtimer/arc.rs b/rust/kernel/time/hrtimer/arc.rs index ccf1e66e5b2d..ed490a7a8950 100644 --- a/rust/kernel/time/hrtimer/arc.rs +++ b/rust/kernel/time/hrtimer/arc.rs @@ -4,8 +4,8 @@ use super::HrTimer; use super::HrTimerCallback; use super::HrTimerHandle; +use super::HrTimerMode; use super::HrTimerPointer; -use super::Ktime; use super::RawHrTimerCallback; use crate::sync::Arc; use crate::sync::ArcBorrow; @@ -54,9 +54,13 @@ impl HrTimerPointer for Arc T: HasHrTimer, T: for<'a> HrTimerCallback = Self>, { + type TimerMode = >::TimerMode; type TimerHandle = ArcHrTimerHandle; - fn start(self, expires: Ktime) -> ArcHrTimerHandle { + fn start( + self, + expires: <>::TimerMode as HrTimerMode>::Expires, + ) -> ArcHrTimerHandle { // SAFETY: // - We keep `self` alive by wrapping it in a handle below. // - Since we generate the pointer passed to `start` from a valid diff --git a/rust/kernel/time/hrtimer/pin.rs b/rust/kernel/time/hrtimer/pin.rs index 293ca9cf058c..aef16d9ee2f0 100644 --- a/rust/kernel/time/hrtimer/pin.rs +++ b/rust/kernel/time/hrtimer/pin.rs @@ -4,7 +4,7 @@ use super::HrTimer; use super::HrTimerCallback; use super::HrTimerHandle; -use super::Ktime; +use super::HrTimerMode; use super::RawHrTimerCallback; use super::UnsafeHrTimerPointer; use core::pin::Pin; @@ -54,9 +54,13 @@ unsafe impl<'a, T> UnsafeHrTimerPointer for Pin<&'a T> T: HasHrTimer, T: HrTimerCallback = Self>, { + type TimerMode = >::TimerMode; type TimerHandle = PinHrTimerHandle<'a, T>; - unsafe fn start(self, expires: Ktime) -> Self::TimerHandle { + unsafe fn start( + self, + expires: <>::TimerMode as HrTimerMode>::Expires, + ) -> Self::TimerHandle { // Cast to pointer let self_ptr: *const T = self.get_ref(); @@ -79,7 +83,7 @@ impl<'a, T> RawHrTimerCallback for Pin<&'a T> unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart { // `HrTimer` is `repr(C)` - let timer_ptr = ptr as *mut HrTimer; + let timer_ptr = ptr.cast::>(); // SAFETY: By the safety requirement of this function, `timer_ptr` // points to a `HrTimer` contained in an `T`. diff --git a/rust/kernel/time/hrtimer/pin_mut.rs b/rust/kernel/time/hrtimer/pin_mut.rs index 6033572d35ad..767d0a4e8a2c 100644 --- a/rust/kernel/time/hrtimer/pin_mut.rs +++ b/rust/kernel/time/hrtimer/pin_mut.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 use super::{ - HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, Ktime, RawHrTimerCallback, + HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, RawHrTimerCallback, UnsafeHrTimerPointer, }; use core::{marker::PhantomData, pin::Pin, ptr::NonNull}; @@ -52,9 +52,13 @@ unsafe impl<'a, T> UnsafeHrTimerPointer for Pin<&'a mut T> T: HasHrTimer, T: HrTimerCallback = Self>, { + type TimerMode = >::TimerMode; type TimerHandle = PinMutHrTimerHandle<'a, T>; - unsafe fn start(mut self, expires: Ktime) -> Self::TimerHandle { + unsafe fn start( + mut self, + expires: <>::TimerMode as HrTimerMode>::Expires, + ) -> Self::TimerHandle { // SAFETY: // - We promise not to move out of `self`. We only pass `self` // back to the caller as a `Pin<&mut self>`. @@ -83,7 +87,7 @@ impl<'a, T> RawHrTimerCallback for Pin<&'a mut T> unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart { // `HrTimer` is `repr(C)` - let timer_ptr = ptr as *mut HrTimer; + let timer_ptr = ptr.cast::>(); // SAFETY: By the safety requirement of this function, `timer_ptr` // points to a `HrTimer` contained in an `T`. diff --git a/rust/kernel/time/hrtimer/tbox.rs b/rust/kernel/time/hrtimer/tbox.rs index 29526a5da203..ec08303315f2 100644 --- a/rust/kernel/time/hrtimer/tbox.rs +++ b/rust/kernel/time/hrtimer/tbox.rs @@ -4,8 +4,8 @@ use super::HrTimer; use super::HrTimerCallback; use super::HrTimerHandle; +use super::HrTimerMode; use super::HrTimerPointer; -use super::Ktime; use super::RawHrTimerCallback; use crate::prelude::*; use core::ptr::NonNull; @@ -64,9 +64,13 @@ impl HrTimerPointer for Pin> T: for<'a> HrTimerCallback = Pin>>, A: crate::alloc::Allocator, { + type TimerMode = >::TimerMode; type TimerHandle = BoxHrTimerHandle; - fn start(self, expires: Ktime) -> Self::TimerHandle { + fn start( + self, + expires: <>::TimerMode as HrTimerMode>::Expires, + ) -> Self::TimerHandle { // SAFETY: // - We will not move out of this box during timer callback (we pass an // immutable reference to the callback). diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 22985b6f6982..dc0a02f5c3cf 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -2,14 +2,16 @@ //! Kernel types. +use crate::ffi::c_void; use core::{ cell::UnsafeCell, marker::{PhantomData, PhantomPinned}, - mem::{ManuallyDrop, MaybeUninit}, + mem::MaybeUninit, ops::{Deref, DerefMut}, - ptr::NonNull, }; -use pin_init::{PinInit, Zeroable}; +use pin_init::{PinInit, Wrapper, Zeroable}; + +pub use crate::sync::aref::{ARef, AlwaysRefCounted}; /// Used to transfer ownership to and from foreign (non-Rust) languages. /// @@ -21,15 +23,10 @@ /// /// # Safety /// -/// Implementers must ensure that [`into_foreign`] returns a pointer which meets the alignment -/// requirements of [`PointedTo`]. -/// -/// [`into_foreign`]: Self::into_foreign -/// [`PointedTo`]: Self::PointedTo +/// - Implementations must satisfy the guarantees of [`Self::into_foreign`]. pub unsafe trait ForeignOwnable: Sized { - /// Type used when the value is foreign-owned. In practical terms only defines the alignment of - /// the pointer. - type PointedTo; + /// The alignment of pointers returned by `into_foreign`. + const FOREIGN_ALIGN: usize; /// Type used to immutably borrow a value that is currently foreign-owned. type Borrowed<'a>; @@ -39,18 +36,21 @@ pub unsafe trait ForeignOwnable: Sized { /// Converts a Rust-owned object to a foreign-owned one. /// + /// The foreign representation is a pointer to void. Aside from the guarantees listed below, + /// there are no other guarantees for this pointer. For example, it might be invalid, dangling + /// or pointing to uninitialized memory. Using it in any way except for [`from_foreign`], + /// [`try_from_foreign`], [`borrow`], or [`borrow_mut`] can result in undefined behavior. + /// /// # Guarantees /// - /// The return value is guaranteed to be well-aligned, but there are no other guarantees for - /// this pointer. For example, it might be null, dangling, or point to uninitialized memory. - /// Using it in any way except for [`ForeignOwnable::from_foreign`], [`ForeignOwnable::borrow`], - /// [`ForeignOwnable::try_from_foreign`] can result in undefined behavior. + /// - Minimum alignment of returned pointer is [`Self::FOREIGN_ALIGN`]. + /// - The returned pointer is not null. /// /// [`from_foreign`]: Self::from_foreign /// [`try_from_foreign`]: Self::try_from_foreign /// [`borrow`]: Self::borrow /// [`borrow_mut`]: Self::borrow_mut - fn into_foreign(self) -> *mut Self::PointedTo; + fn into_foreign(self) -> *mut c_void; /// Converts a foreign-owned object back to a Rust-owned one. /// @@ -60,7 +60,7 @@ pub unsafe trait ForeignOwnable: Sized { /// must not be passed to `from_foreign` more than once. /// /// [`into_foreign`]: Self::into_foreign - unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self; + unsafe fn from_foreign(ptr: *mut c_void) -> Self; /// Tries to convert a foreign-owned object back to a Rust-owned one. /// @@ -72,7 +72,7 @@ pub unsafe trait ForeignOwnable: Sized { /// `ptr` must either be null or satisfy the safety requirements for [`from_foreign`]. /// /// [`from_foreign`]: Self::from_foreign - unsafe fn try_from_foreign(ptr: *mut Self::PointedTo) -> Option { + unsafe fn try_from_foreign(ptr: *mut c_void) -> Option { if ptr.is_null() { None } else { @@ -95,7 +95,7 @@ unsafe fn try_from_foreign(ptr: *mut Self::PointedTo) -> Option { /// /// [`into_foreign`]: Self::into_foreign /// [`from_foreign`]: Self::from_foreign - unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> Self::Borrowed<'a>; + unsafe fn borrow<'a>(ptr: *mut c_void) -> Self::Borrowed<'a>; /// Borrows a foreign-owned object mutably. /// @@ -123,23 +123,24 @@ unsafe fn try_from_foreign(ptr: *mut Self::PointedTo) -> Option { /// [`from_foreign`]: Self::from_foreign /// [`borrow`]: Self::borrow /// [`Arc`]: crate::sync::Arc - unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> Self::BorrowedMut<'a>; + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> Self::BorrowedMut<'a>; } -// SAFETY: The `into_foreign` function returns a pointer that is dangling, but well-aligned. +// SAFETY: The pointer returned by `into_foreign` comes from a well aligned +// pointer to `()`. unsafe impl ForeignOwnable for () { - type PointedTo = (); + const FOREIGN_ALIGN: usize = core::mem::align_of::<()>(); type Borrowed<'a> = (); type BorrowedMut<'a> = (); - fn into_foreign(self) -> *mut Self::PointedTo { + fn into_foreign(self) -> *mut c_void { core::ptr::NonNull::dangling().as_ptr() } - unsafe fn from_foreign(_: *mut Self::PointedTo) -> Self {} + unsafe fn from_foreign(_: *mut c_void) -> Self {} - unsafe fn borrow<'a>(_: *mut Self::PointedTo) -> Self::Borrowed<'a> {} - unsafe fn borrow_mut<'a>(_: *mut Self::PointedTo) -> Self::BorrowedMut<'a> {} + unsafe fn borrow<'a>(_: *mut c_void) -> Self::Borrowed<'a> {} + unsafe fn borrow_mut<'a>(_: *mut c_void) -> Self::BorrowedMut<'a> {} } /// Runs a cleanup function/closure when dropped. @@ -353,17 +354,6 @@ pub const fn zeroed() -> Self { } } - /// Create an opaque pin-initializer from the given pin-initializer. - pub fn pin_init(slot: impl PinInit) -> impl PinInit { - Self::ffi_init(|ptr: *mut T| { - // SAFETY: - // - `ptr` is a valid pointer to uninitialized memory, - // - `slot` is not accessed on error; the call is infallible, - // - `slot` is pinned in memory. - let _ = unsafe { PinInit::::__pinned_init(slot, ptr) }; - }) - } - /// Creates a pin-initializer from the given initializer closure. /// /// The returned initializer calls the given closure with the pointer to the inner `T` of this @@ -377,7 +367,7 @@ pub fn ffi_init(init_func: impl FnOnce(*mut T)) -> impl PinInit { // initialize the `T`. unsafe { pin_init::pin_init_from_closure::<_, ::core::convert::Infallible>(move |slot| { - init_func(Self::raw_get(slot)); + init_func(Self::cast_into(slot)); Ok(()) }) } @@ -397,7 +387,7 @@ pub fn try_ffi_init( // SAFETY: We contain a `MaybeUninit`, so it is OK for the `init_func` to not fully // initialize the `T`. unsafe { - pin_init::pin_init_from_closure::<_, E>(move |slot| init_func(Self::raw_get(slot))) + pin_init::pin_init_from_closure::<_, E>(move |slot| init_func(Self::cast_into(slot))) } } @@ -410,178 +400,29 @@ pub const fn get(&self) -> *mut T { /// /// This function is useful to get access to the value without creating intermediate /// references. - pub const fn raw_get(this: *const Self) -> *mut T { + pub const fn cast_into(this: *const Self) -> *mut T { UnsafeCell::raw_get(this.cast::>>()).cast::() } -} -/// Types that are _always_ reference counted. -/// -/// It allows such types to define their own custom ref increment and decrement functions. -/// Additionally, it allows users to convert from a shared reference `&T` to an owned reference -/// [`ARef`]. -/// -/// This is usually implemented by wrappers to existing structures on the C side of the code. For -/// Rust code, the recommendation is to use [`Arc`](crate::sync::Arc) to create reference-counted -/// instances of a type. -/// -/// # Safety -/// -/// Implementers must ensure that increments to the reference count keep the object alive in memory -/// at least until matching decrements are performed. -/// -/// Implementers must also ensure that all instances are reference-counted. (Otherwise they -/// won't be able to honour the requirement that [`AlwaysRefCounted::inc_ref`] keep the object -/// alive.) -pub unsafe trait AlwaysRefCounted { - /// Increments the reference count on the object. - fn inc_ref(&self); - - /// Decrements the reference count on the object. - /// - /// Frees the object when the count reaches zero. - /// - /// # Safety - /// - /// Callers must ensure that there was a previous matching increment to the reference count, - /// and that the object is no longer used after its reference count is decremented (as it may - /// result in the object being freed), unless the caller owns another increment on the refcount - /// (e.g., it calls [`AlwaysRefCounted::inc_ref`] twice, then calls - /// [`AlwaysRefCounted::dec_ref`] once). - unsafe fn dec_ref(obj: NonNull); -} - -/// An owned reference to an always-reference-counted object. -/// -/// The object's reference count is automatically decremented when an instance of [`ARef`] is -/// dropped. It is also automatically incremented when a new instance is created via -/// [`ARef::clone`]. -/// -/// # Invariants -/// -/// The pointer stored in `ptr` is non-null and valid for the lifetime of the [`ARef`] instance. In -/// particular, the [`ARef`] instance owns an increment on the underlying object's reference count. -pub struct ARef { - ptr: NonNull, - _p: PhantomData, -} - -// SAFETY: It is safe to send `ARef` to another thread when the underlying `T` is `Sync` because -// it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, it needs -// `T` to be `Send` because any thread that has an `ARef` may ultimately access `T` using a -// mutable reference, for example, when the reference count reaches zero and `T` is dropped. -unsafe impl Send for ARef {} - -// SAFETY: It is safe to send `&ARef` to another thread when the underlying `T` is `Sync` -// because it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, -// it needs `T` to be `Send` because any thread that has a `&ARef` may clone it and get an -// `ARef` on that thread, so the thread may ultimately access `T` using a mutable reference, for -// example, when the reference count reaches zero and `T` is dropped. -unsafe impl Sync for ARef {} - -impl ARef { - /// Creates a new instance of [`ARef`]. - /// - /// It takes over an increment of the reference count on the underlying object. - /// - /// # Safety - /// - /// Callers must ensure that the reference count was incremented at least once, and that they - /// are properly relinquishing one increment. That is, if there is only one increment, callers - /// must not use the underlying object anymore -- it is only safe to do so via the newly - /// created [`ARef`]. - pub unsafe fn from_raw(ptr: NonNull) -> Self { - // INVARIANT: The safety requirements guarantee that the new instance now owns the - // increment on the refcount. - Self { - ptr, - _p: PhantomData, - } - } - - /// Consumes the `ARef`, returning a raw pointer. - /// - /// This function does not change the refcount. After calling this function, the caller is - /// responsible for the refcount previously managed by the `ARef`. - /// - /// # Examples - /// - /// ``` - /// use core::ptr::NonNull; - /// use kernel::types::{ARef, AlwaysRefCounted}; - /// - /// struct Empty {} - /// - /// # // SAFETY: TODO. - /// unsafe impl AlwaysRefCounted for Empty { - /// fn inc_ref(&self) {} - /// unsafe fn dec_ref(_obj: NonNull) {} - /// } - /// - /// let mut data = Empty {}; - /// let ptr = NonNull::::new(&mut data).unwrap(); - /// # // SAFETY: TODO. - /// let data_ref: ARef = unsafe { ARef::from_raw(ptr) }; - /// let raw_ptr: NonNull = ARef::into_raw(data_ref); - /// - /// assert_eq!(ptr, raw_ptr); - /// ``` - pub fn into_raw(me: Self) -> NonNull { - ManuallyDrop::new(me).ptr + /// The opposite operation of [`Opaque::cast_into`]. + pub const fn cast_from(this: *const T) -> *const Self { + this.cast() } } -impl Clone for ARef { - fn clone(&self) -> Self { - self.inc_ref(); - // SAFETY: We just incremented the refcount above. - unsafe { Self::from_raw(self.ptr) } +impl Wrapper for Opaque { + /// Create an opaque pin-initializer from the given pin-initializer. + fn pin_init(slot: impl PinInit) -> impl PinInit { + Self::try_ffi_init(|ptr: *mut T| { + // SAFETY: + // - `ptr` is a valid pointer to uninitialized memory, + // - `slot` is not accessed on error, + // - `slot` is pinned in memory. + unsafe { PinInit::::__pinned_init(slot, ptr) } + }) } } -impl Deref for ARef { - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: The type invariants guarantee that the object is valid. - unsafe { self.ptr.as_ref() } - } -} - -impl From<&T> for ARef { - fn from(b: &T) -> Self { - b.inc_ref(); - // SAFETY: We just incremented the refcount above. - unsafe { Self::from_raw(NonNull::from(b)) } - } -} - -impl Drop for ARef { - fn drop(&mut self) { - // SAFETY: The type invariants guarantee that the `ARef` owns the reference we're about to - // decrement. - unsafe { T::dec_ref(self.ptr) }; - } -} - -/// A sum type that always holds either a value of type `L` or `R`. -/// -/// # Examples -/// -/// ``` -/// use kernel::types::Either; -/// -/// let left_value: Either = Either::Left(7); -/// let right_value: Either = Either::Right("right value"); -/// ``` -pub enum Either { - /// Constructs an instance of [`Either`] containing a value of type `L`. - Left(L), - - /// Constructs an instance of [`Either`] containing a value of type `R`. - Right(R), -} - /// Zero-sized type to mark types not [`Send`]. /// /// Add this type as a field to your struct if your type should not be sent to a different task. diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index 6d70edd8086a..a8fb4764185a 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -8,14 +8,57 @@ alloc::{Allocator, Flags}, bindings, error::Result, - ffi::c_void, + ffi::{c_char, c_void}, prelude::*, transmute::{AsBytes, FromBytes}, }; use core::mem::{size_of, MaybeUninit}; -/// The type used for userspace addresses. -pub type UserPtr = usize; +/// A pointer into userspace. +/// +/// This is the Rust equivalent to C pointers tagged with `__user`. +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct UserPtr(*mut c_void); + +impl UserPtr { + /// Create a `UserPtr` from an integer representing the userspace address. + #[inline] + pub fn from_addr(addr: usize) -> Self { + Self(addr as *mut c_void) + } + + /// Create a `UserPtr` from a pointer representing the userspace address. + #[inline] + pub fn from_ptr(addr: *mut c_void) -> Self { + Self(addr) + } + + /// Cast this userspace pointer to a raw const void pointer. + /// + /// It is up to the caller to use the returned pointer correctly. + #[inline] + pub fn as_const_ptr(self) -> *const c_void { + self.0 + } + + /// Cast this userspace pointer to a raw mutable void pointer. + /// + /// It is up to the caller to use the returned pointer correctly. + #[inline] + pub fn as_mut_ptr(self) -> *mut c_void { + self.0 + } + + /// Increment this user pointer by `add` bytes. + /// + /// This addition is wrapping, so wrapping around the address space does not result in a panic + /// even if `CONFIG_RUST_OVERFLOW_CHECKS` is enabled. + #[inline] + pub fn wrapping_byte_add(self, add: usize) -> UserPtr { + UserPtr(self.0.wrapping_byte_add(add)) + } +} /// A pointer to an area in userspace memory, which can be either read-only or read-write. /// @@ -177,7 +220,7 @@ impl UserSliceReader { pub fn skip(&mut self, num_skip: usize) -> Result { // Update `self.length` first since that's the fallible part of this operation. self.length = self.length.checked_sub(num_skip).ok_or(EFAULT)?; - self.ptr = self.ptr.wrapping_add(num_skip); + self.ptr = self.ptr.wrapping_byte_add(num_skip); Ok(()) } @@ -224,11 +267,11 @@ pub fn read_raw(&mut self, out: &mut [MaybeUninit]) -> Result { } // SAFETY: `out_ptr` points into a mutable slice of length `len`, so we may write // that many bytes to it. - let res = unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len) }; + let res = unsafe { bindings::copy_from_user(out_ptr, self.ptr.as_const_ptr(), len) }; if res != 0 { return Err(EFAULT); } - self.ptr = self.ptr.wrapping_add(len); + self.ptr = self.ptr.wrapping_byte_add(len); self.length -= len; Ok(()) } @@ -240,7 +283,7 @@ pub fn read_raw(&mut self, out: &mut [MaybeUninit]) -> Result { pub fn read_slice(&mut self, out: &mut [u8]) -> Result { // SAFETY: The types are compatible and `read_raw` doesn't write uninitialized bytes to // `out`. - let out = unsafe { &mut *(out as *mut [u8] as *mut [MaybeUninit]) }; + let out = unsafe { &mut *(core::ptr::from_mut(out) as *mut [MaybeUninit]) }; self.read_raw(out) } @@ -262,14 +305,14 @@ pub fn read(&mut self) -> Result { let res = unsafe { bindings::_copy_from_user( out.as_mut_ptr().cast::(), - self.ptr as *const c_void, + self.ptr.as_const_ptr(), len, ) }; if res != 0 { return Err(EFAULT); } - self.ptr = self.ptr.wrapping_add(len); + self.ptr = self.ptr.wrapping_byte_add(len); self.length -= len; // SAFETY: The read above has initialized all bytes in `out`, and since `T` implements // `FromBytes`, any bit-pattern is a valid value for this type. @@ -291,6 +334,65 @@ pub fn read_all(mut self, buf: &mut Vec, flags: Flags) -> R unsafe { buf.inc_len(len) }; Ok(()) } + + /// Read a NUL-terminated string from userspace and return it. + /// + /// The string is read into `buf` and a NUL-terminator is added if the end of `buf` is reached. + /// Since there must be space to add a NUL-terminator, the buffer must not be empty. The + /// returned `&CStr` points into `buf`. + /// + /// Fails with [`EFAULT`] if the read happens on a bad address (some data may have been + /// copied). + #[doc(alias = "strncpy_from_user")] + pub fn strcpy_into_buf<'buf>(self, buf: &'buf mut [u8]) -> Result<&'buf CStr> { + if buf.is_empty() { + return Err(EINVAL); + } + + // SAFETY: The types are compatible and `strncpy_from_user` doesn't write uninitialized + // bytes to `buf`. + let mut dst = unsafe { &mut *(core::ptr::from_mut(buf) as *mut [MaybeUninit]) }; + + // We never read more than `self.length` bytes. + if dst.len() > self.length { + dst = &mut dst[..self.length]; + } + + let mut len = raw_strncpy_from_user(dst, self.ptr)?; + if len < dst.len() { + // Add one to include the NUL-terminator. + len += 1; + } else if len < buf.len() { + // This implies that `len == dst.len() < buf.len()`. + // + // This means that we could not fill the entire buffer, but we had to stop reading + // because we hit the `self.length` limit of this `UserSliceReader`. Since we did not + // fill the buffer, we treat this case as if we tried to read past the `self.length` + // limit and received a page fault, which is consistent with other `UserSliceReader` + // methods that also return page faults when you exceed `self.length`. + return Err(EFAULT); + } else { + // This implies that `len == buf.len()`. + // + // This means that we filled the buffer exactly. In this case, we add a NUL-terminator + // and return it. Unlike the `len < dst.len()` branch, don't modify `len` because it + // already represents the length including the NUL-terminator. + // + // SAFETY: Due to the check at the beginning, the buffer is not empty. + unsafe { *buf.last_mut().unwrap_unchecked() = 0 }; + } + + // This method consumes `self`, so it can only be called once, thus we do not need to + // update `self.length`. This sidesteps concerns such as whether `self.length` should be + // incremented by `len` or `len-1` in the `len == buf.len()` case. + + // SAFETY: There are two cases: + // * If we hit the `len < dst.len()` case, then `raw_strncpy_from_user` guarantees that + // this slice contains exactly one NUL byte at the end of the string. + // * Otherwise, `raw_strncpy_from_user` guarantees that the string contained no NUL bytes, + // and we have since added a NUL byte at the end. + Ok(unsafe { CStr::from_bytes_with_nul_unchecked(&buf[..len]) }) + } } /// A writer for [`UserSlice`]. @@ -327,11 +429,11 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result { } // SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read // that many bytes from it. - let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len) }; + let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), data_ptr, len) }; if res != 0 { return Err(EFAULT); } - self.ptr = self.ptr.wrapping_add(len); + self.ptr = self.ptr.wrapping_byte_add(len); self.length -= len; Ok(()) } @@ -354,16 +456,53 @@ pub fn write(&mut self, value: &T) -> Result { // is a compile-time constant. let res = unsafe { bindings::_copy_to_user( - self.ptr as *mut c_void, - (value as *const T).cast::(), + self.ptr.as_mut_ptr(), + core::ptr::from_ref(value).cast::(), len, ) }; if res != 0 { return Err(EFAULT); } - self.ptr = self.ptr.wrapping_add(len); + self.ptr = self.ptr.wrapping_byte_add(len); self.length -= len; Ok(()) } } + +/// Reads a nul-terminated string into `dst` and returns the length. +/// +/// This reads from userspace until a NUL byte is encountered, or until `dst.len()` bytes have been +/// read. Fails with [`EFAULT`] if a read happens on a bad address (some data may have been +/// copied). When the end of the buffer is encountered, no NUL byte is added, so the string is +/// *not* guaranteed to be NUL-terminated when `Ok(dst.len())` is returned. +/// +/// # Guarantees +/// +/// When this function returns `Ok(len)`, it is guaranteed that the first `len` bytes of `dst` are +/// initialized and non-zero. Furthermore, if `len < dst.len()`, then `dst[len]` is a NUL byte. +#[inline] +fn raw_strncpy_from_user(dst: &mut [MaybeUninit], src: UserPtr) -> Result { + // CAST: Slice lengths are guaranteed to be `<= isize::MAX`. + let len = dst.len() as isize; + + // SAFETY: `dst` is valid for writing `dst.len()` bytes. + let res = unsafe { + bindings::strncpy_from_user( + dst.as_mut_ptr().cast::(), + src.as_const_ptr().cast::(), + len, + ) + }; + + if res < 0 { + return Err(Error::from_errno(res as i32)); + } + + #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)] + assert!(res <= len); + + // GUARANTEES: `strncpy_from_user` was successful, so `dst` has contents in accordance with the + // guarantees of this function. + Ok(res as usize) +} diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index d092112d843f..b9343d5bc00f 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -26,7 +26,7 @@ //! * The [`WorkItemPointer`] trait is implemented for the pointer type that points at a something //! that implements [`WorkItem`]. //! -//! ## Example +//! ## Examples //! //! This example defines a struct that holds an integer and can be scheduled on the workqueue. When //! the struct is executed, it will print the integer. Since there is only one `work_struct` field, @@ -131,10 +131,69 @@ //! # print_2_later(MyStruct::new(41, 42).unwrap()); //! ``` //! +//! This example shows how you can schedule delayed work items: +//! +//! ``` +//! use kernel::sync::Arc; +//! use kernel::workqueue::{self, impl_has_delayed_work, new_delayed_work, DelayedWork, WorkItem}; +//! +//! #[pin_data] +//! struct MyStruct { +//! value: i32, +//! #[pin] +//! work: DelayedWork, +//! } +//! +//! impl_has_delayed_work! { +//! impl HasDelayedWork for MyStruct { self.work } +//! } +//! +//! impl MyStruct { +//! fn new(value: i32) -> Result> { +//! Arc::pin_init( +//! pin_init!(MyStruct { +//! value, +//! work <- new_delayed_work!("MyStruct::work"), +//! }), +//! GFP_KERNEL, +//! ) +//! } +//! } +//! +//! impl WorkItem for MyStruct { +//! type Pointer = Arc; +//! +//! fn run(this: Arc) { +//! pr_info!("The value is: {}\n", this.value); +//! } +//! } +//! +//! /// This method will enqueue the struct for execution on the system workqueue, where its value +//! /// will be printed 12 jiffies later. +//! fn print_later(val: Arc) { +//! let _ = workqueue::system().enqueue_delayed(val, 12); +//! } +//! +//! /// It is also possible to use the ordinary `enqueue` method together with `DelayedWork`. This +//! /// is equivalent to calling `enqueue_delayed` with a delay of zero. +//! fn print_now(val: Arc) { +//! let _ = workqueue::system().enqueue(val); +//! } +//! # print_later(MyStruct::new(42).unwrap()); +//! # print_now(MyStruct::new(42).unwrap()); +//! ``` +//! //! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h) -use crate::alloc::{AllocError, Flags}; -use crate::{prelude::*, sync::Arc, sync::LockClassKey, types::Opaque}; +use crate::{ + alloc::{AllocError, Flags}, + container_of, + prelude::*, + sync::Arc, + sync::LockClassKey, + time::Jiffies, + types::Opaque, +}; use core::marker::PhantomData; /// Creates a [`Work`] initialiser with the given name and a newly-created lock class. @@ -146,6 +205,33 @@ macro_rules! new_work { } pub use new_work; +/// Creates a [`DelayedWork`] initialiser with the given name and a newly-created lock class. +#[macro_export] +macro_rules! new_delayed_work { + () => { + $crate::workqueue::DelayedWork::new( + $crate::optional_name!(), + $crate::static_lock_class!(), + $crate::c_str!(::core::concat!( + ::core::file!(), + ":", + ::core::line!(), + "_timer" + )), + $crate::static_lock_class!(), + ) + }; + ($name:literal) => { + $crate::workqueue::DelayedWork::new( + $crate::c_str!($name), + $crate::static_lock_class!(), + $crate::c_str!(::core::concat!($name, "_timer")), + $crate::static_lock_class!(), + ) + }; +} +pub use new_delayed_work; + /// A kernel work queue. /// /// Wraps the kernel's C `struct workqueue_struct`. @@ -170,7 +256,7 @@ impl Queue { pub unsafe fn from_raw<'a>(ptr: *const bindings::workqueue_struct) -> &'a Queue { // SAFETY: The `Queue` type is `#[repr(transparent)]`, so the pointer cast is valid. The // caller promises that the pointer is not dangling. - unsafe { &*(ptr as *const Queue) } + unsafe { &*ptr.cast::() } } /// Enqueues a work item. @@ -198,7 +284,7 @@ pub fn enqueue(&self, w: W) -> W::EnqueueOutput unsafe { w.__enqueue(move |work_ptr| { bindings::queue_work_on( - bindings::wq_misc_consts_WORK_CPU_UNBOUND as _, + bindings::wq_misc_consts_WORK_CPU_UNBOUND as ffi::c_int, queue_ptr, work_ptr, ) @@ -206,6 +292,42 @@ pub fn enqueue(&self, w: W) -> W::EnqueueOutput } } + /// Enqueues a delayed work item. + /// + /// This may fail if the work item is already enqueued in a workqueue. + /// + /// The work item will be submitted using `WORK_CPU_UNBOUND`. + pub fn enqueue_delayed(&self, w: W, delay: Jiffies) -> W::EnqueueOutput + where + W: RawDelayedWorkItem + Send + 'static, + { + let queue_ptr = self.0.get(); + + // SAFETY: We only return `false` if the `work_struct` is already in a workqueue. The other + // `__enqueue` requirements are not relevant since `W` is `Send` and static. + // + // The call to `bindings::queue_delayed_work_on` will dereference the provided raw pointer, + // which is ok because `__enqueue` guarantees that the pointer is valid for the duration of + // this closure, and the safety requirements of `RawDelayedWorkItem` expands this + // requirement to apply to the entire `delayed_work`. + // + // Furthermore, if the C workqueue code accesses the pointer after this call to + // `__enqueue`, then the work item was successfully enqueued, and + // `bindings::queue_delayed_work_on` will have returned true. In this case, `__enqueue` + // promises that the raw pointer will stay valid until we call the function pointer in the + // `work_struct`, so the access is ok. + unsafe { + w.__enqueue(move |work_ptr| { + bindings::queue_delayed_work_on( + bindings::wq_misc_consts_WORK_CPU_UNBOUND as ffi::c_int, + queue_ptr, + container_of!(work_ptr, bindings::delayed_work, work), + delay, + ) + }) + } + } + /// Tries to spawn the given function or closure as a work item. /// /// This method can fail because it allocates memory to store the work item. @@ -298,6 +420,16 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput F: FnOnce(*mut bindings::work_struct) -> bool; } +/// A raw delayed work item. +/// +/// # Safety +/// +/// If the `__enqueue` method in the `RawWorkItem` implementation calls the closure, then the +/// provided pointer must point at the `work` field of a valid `delayed_work`, and the guarantees +/// that `__enqueue` provides about accessing the `work_struct` must also apply to the rest of the +/// `delayed_work` struct. +pub unsafe trait RawDelayedWorkItem: RawWorkItem {} + /// Defines the method that should be called directly when a work item is executed. /// /// This trait is implemented by `Pin>` and [`Arc`], and is mainly intended to be @@ -403,11 +535,11 @@ pub unsafe fn raw_get(ptr: *const Self) -> *mut bindings::work_struct { // // A pointer cast would also be ok due to `#[repr(transparent)]`. We use `addr_of!` so that // the compiler does not complain that the `work` field is unused. - unsafe { Opaque::raw_get(core::ptr::addr_of!((*ptr).work)) } + unsafe { Opaque::cast_into(core::ptr::addr_of!((*ptr).work)) } } } -/// Declares that a type has a [`Work`] field. +/// Declares that a type contains a [`Work`]. /// /// The intended way of using this trait is via the [`impl_has_work!`] macro. You can use the macro /// like this: @@ -506,6 +638,178 @@ unsafe fn work_container_of( impl{T} HasWork for ClosureWork { self.work } } +/// Links for a delayed work item. +/// +/// This struct contains a function pointer to the [`run`] function from the [`WorkItemPointer`] +/// trait, and defines the linked list pointers necessary to enqueue a work item in a workqueue in +/// a delayed manner. +/// +/// Wraps the kernel's C `struct delayed_work`. +/// +/// This is a helper type used to associate a `delayed_work` with the [`WorkItem`] that uses it. +/// +/// [`run`]: WorkItemPointer::run +#[pin_data] +#[repr(transparent)] +pub struct DelayedWork { + #[pin] + dwork: Opaque, + _inner: PhantomData, +} + +// SAFETY: Kernel work items are usable from any thread. +// +// We do not need to constrain `T` since the work item does not actually contain a `T`. +unsafe impl Send for DelayedWork {} +// SAFETY: Kernel work items are usable from any thread. +// +// We do not need to constrain `T` since the work item does not actually contain a `T`. +unsafe impl Sync for DelayedWork {} + +impl DelayedWork { + /// Creates a new instance of [`DelayedWork`]. + #[inline] + pub fn new( + work_name: &'static CStr, + work_key: Pin<&'static LockClassKey>, + timer_name: &'static CStr, + timer_key: Pin<&'static LockClassKey>, + ) -> impl PinInit + where + T: WorkItem, + { + pin_init!(Self { + dwork <- Opaque::ffi_init(|slot: *mut bindings::delayed_work| { + // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as + // the work item function. + unsafe { + bindings::init_work_with_key( + core::ptr::addr_of_mut!((*slot).work), + Some(T::Pointer::run), + false, + work_name.as_char_ptr(), + work_key.as_ptr(), + ) + } + + // SAFETY: The `delayed_work_timer_fn` function pointer can be used here because + // the timer is embedded in a `struct delayed_work`, and only ever scheduled via + // the core workqueue code, and configured to run in irqsafe context. + unsafe { + bindings::timer_init_key( + core::ptr::addr_of_mut!((*slot).timer), + Some(bindings::delayed_work_timer_fn), + bindings::TIMER_IRQSAFE, + timer_name.as_char_ptr(), + timer_key.as_ptr(), + ) + } + }), + _inner: PhantomData, + }) + } + + /// Get a pointer to the inner `delayed_work`. + /// + /// # Safety + /// + /// The provided pointer must not be dangling and must be properly aligned. (But the memory + /// need not be initialized.) + #[inline] + pub unsafe fn raw_as_work(ptr: *const Self) -> *mut Work { + // SAFETY: The caller promises that the pointer is aligned and not dangling. + let dw: *mut bindings::delayed_work = + unsafe { Opaque::cast_into(core::ptr::addr_of!((*ptr).dwork)) }; + // SAFETY: The caller promises that the pointer is aligned and not dangling. + let wrk: *mut bindings::work_struct = unsafe { core::ptr::addr_of_mut!((*dw).work) }; + // CAST: Work and work_struct have compatible layouts. + wrk.cast() + } +} + +/// Declares that a type contains a [`DelayedWork`]. +/// +/// # Safety +/// +/// The `HasWork` implementation must return a `work_struct` that is stored in the `work` +/// field of a `delayed_work` with the same access rules as the `work_struct`. +pub unsafe trait HasDelayedWork: HasWork {} + +/// Used to safely implement the [`HasDelayedWork`] trait. +/// +/// This macro also implements the [`HasWork`] trait, so you do not need to use [`impl_has_work!`] +/// when using this macro. +/// +/// # Examples +/// +/// ``` +/// use kernel::sync::Arc; +/// use kernel::workqueue::{self, impl_has_delayed_work, DelayedWork}; +/// +/// struct MyStruct<'a, T, const N: usize> { +/// work_field: DelayedWork, 17>, +/// f: fn(&'a [T; N]), +/// } +/// +/// impl_has_delayed_work! { +/// impl{'a, T, const N: usize} HasDelayedWork, 17> +/// for MyStruct<'a, T, N> { self.work_field } +/// } +/// ``` +#[macro_export] +macro_rules! impl_has_delayed_work { + ($(impl$({$($generics:tt)*})? + HasDelayedWork<$work_type:ty $(, $id:tt)?> + for $self:ty + { self.$field:ident } + )*) => {$( + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right + // type. + unsafe impl$(<$($generics)+>)? + $crate::workqueue::HasDelayedWork<$work_type $(, $id)?> for $self {} + + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right + // type. + unsafe impl$(<$($generics)+>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self { + #[inline] + unsafe fn raw_get_work( + ptr: *mut Self + ) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> { + // SAFETY: The caller promises that the pointer is not dangling. + let ptr: *mut $crate::workqueue::DelayedWork<$work_type $(, $id)?> = unsafe { + ::core::ptr::addr_of_mut!((*ptr).$field) + }; + + // SAFETY: The caller promises that the pointer is not dangling. + unsafe { $crate::workqueue::DelayedWork::raw_as_work(ptr) } + } + + #[inline] + unsafe fn work_container_of( + ptr: *mut $crate::workqueue::Work<$work_type $(, $id)?>, + ) -> *mut Self { + // SAFETY: The caller promises that the pointer points at a field of the right type + // in the right kind of struct. + let ptr = unsafe { $crate::workqueue::Work::raw_get(ptr) }; + + // SAFETY: The caller promises that the pointer points at a field of the right type + // in the right kind of struct. + let delayed_work = unsafe { + $crate::container_of!(ptr, $crate::bindings::delayed_work, work) + }; + + let delayed_work: *mut $crate::workqueue::DelayedWork<$work_type $(, $id)?> = + delayed_work.cast(); + + // SAFETY: The caller promises that the pointer points at a field of the right type + // in the right kind of struct. + unsafe { $crate::container_of!(delayed_work, Self, $field) } + } + } + )*}; +} +pub use impl_has_delayed_work; + // SAFETY: The `__enqueue` implementation in RawWorkItem uses a `work_struct` initialized with the // `run` method of this trait as the function pointer because: // - `__enqueue` gets the `work_struct` from the `Work` field, using `T::raw_get_work`. @@ -522,7 +826,7 @@ unsafe impl WorkItemPointer for Arc { unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { // The `__enqueue` method always uses a `work_struct` stored in a `Work`. - let ptr = ptr as *mut Work; + let ptr = ptr.cast::>(); // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`. let ptr = unsafe { T::work_container_of(ptr) }; // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership. @@ -567,6 +871,16 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput } } +// SAFETY: By the safety requirements of `HasDelayedWork`, the `work_struct` returned by methods in +// `HasWork` provides a `work_struct` that is the `work` field of a `delayed_work`, and the rest of +// the `delayed_work` has the same access rules as its `work` field. +unsafe impl RawDelayedWorkItem for Arc +where + T: WorkItem, + T: HasDelayedWork, +{ +} + // SAFETY: TODO. unsafe impl WorkItemPointer for Pin> where @@ -575,7 +889,7 @@ unsafe impl WorkItemPointer for Pin> { unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { // The `__enqueue` method always uses a `work_struct` stored in a `Work`. - let ptr = ptr as *mut Work; + let ptr = ptr.cast::>(); // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`. let ptr = unsafe { T::work_container_of(ptr) }; // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership. @@ -617,6 +931,16 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput } } +// SAFETY: By the safety requirements of `HasDelayedWork`, the `work_struct` returned by methods in +// `HasWork` provides a `work_struct` that is the `work` field of a `delayed_work`, and the rest of +// the `delayed_work` has the same access rules as its `work` field. +unsafe impl RawDelayedWorkItem for Pin> +where + T: WorkItem, + T: HasDelayedWork, +{ +} + /// Returns the system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU multi-threaded. There are diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 75719e7bb491..a49d6db28845 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -7,9 +7,10 @@ use crate::{ alloc, bindings, build_assert, error::{Error, Result}, + ffi::c_void, types::{ForeignOwnable, NotThreadSafe, Opaque}, }; -use core::{iter, marker::PhantomData, mem, pin::Pin, ptr::NonNull}; +use core::{iter, marker::PhantomData, pin::Pin, ptr::NonNull}; use pin_init::{pin_data, pin_init, pinned_drop, PinInit}; /// An array which efficiently maps sparse integer indices to owned objects. @@ -101,7 +102,7 @@ pub fn new(kind: AllocKind) -> impl PinInit { }) } - fn iter(&self) -> impl Iterator> + '_ { + fn iter(&self) -> impl Iterator> + '_ { let mut index = 0; // SAFETY: `self.xa` is always valid by the type invariant. @@ -179,7 +180,7 @@ fn from(value: StoreError) -> Self { impl<'a, T: ForeignOwnable> Guard<'a, T> { fn load(&self, index: usize, f: F) -> Option where - F: FnOnce(NonNull) -> U, + F: FnOnce(NonNull) -> U, { // SAFETY: `self.xa.xa` is always valid by the type invariant. let ptr = unsafe { bindings::xa_load(self.xa.xa.get(), index) }; @@ -230,7 +231,7 @@ pub fn store( gfp: alloc::Flags, ) -> Result, StoreError> { build_assert!( - mem::align_of::() >= 4, + T::FOREIGN_ALIGN >= 4, "pointers stored in XArray must be 4-byte aligned" ); let new = value.into_foreign(); diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 2ddd2eeb2852..5ee54a00c0b6 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -57,7 +57,7 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { {cfg} #[doc(hidden)] #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")] - #[used] + #[used(compiler)] pub static __{module}_{counter}: [u8; {length}] = *{string}; ", cfg = if builtin { @@ -94,7 +94,6 @@ struct ModuleInfo { type_: String, license: String, name: String, - author: Option, authors: Option>, description: Option, alias: Option>, @@ -108,7 +107,6 @@ fn parse(it: &mut token_stream::IntoIter) -> Self { const EXPECTED_KEYS: &[&str] = &[ "type", "name", - "author", "authors", "description", "license", @@ -134,7 +132,6 @@ fn parse(it: &mut token_stream::IntoIter) -> Self { match key.as_str() { "type" => info.type_ = expect_ident(it), "name" => info.name = expect_string_ascii(it), - "author" => info.author = Some(expect_string(it)), "authors" => info.authors = Some(expect_string_array(it)), "description" => info.description = Some(expect_string(it)), "license" => info.license = expect_string_ascii(it), @@ -179,9 +176,6 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { // Rust does not allow hyphens in identifiers, use underscore instead. let ident = info.name.replace('-', "_"); let mut modinfo = ModInfoBuilder::new(ident.as_ref()); - if let Some(author) = info.author { - modinfo.emit("author", &author); - } if let Some(authors) = info.authors { for author in authors { modinfo.emit("author", &author); @@ -249,7 +243,7 @@ mod __module_init {{ // key or a new section. For the moment, keep it simple. #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] static __IS_RUST_MODULE: () = (); static mut __MOD: ::core::mem::MaybeUninit<{type_}> = @@ -273,7 +267,7 @@ mod __module_init {{ #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] #[link_section = \".init.data\"] static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module; @@ -293,7 +287,7 @@ mod __module_init {{ #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] #[link_section = \".exit.data\"] static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module; @@ -303,7 +297,7 @@ mod __module_init {{ #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] #[doc(hidden)] #[link_section = \"{initcall_section}\"] - #[used] + #[used(compiler)] pub static __{ident}_initcall: extern \"C\" fn() -> ::kernel::ffi::c_int = __{ident}_init; diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 2d0cda961d45..a4c01a8d78b2 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -125,7 +125,7 @@ impl DriverData { fn new() -> impl PinInit { try_pin_init!(Self { status <- CMutex::new(0), - buffer: Box::init(pin_init::zeroed())?, + buffer: Box::init(pin_init::init_zeroed())?, }? Error) } } diff --git a/rust/pin-init/examples/big_struct_in_place.rs b/rust/pin-init/examples/big_struct_in_place.rs index 30d44a334ffd..c05139927486 100644 --- a/rust/pin-init/examples/big_struct_in_place.rs +++ b/rust/pin-init/examples/big_struct_in_place.rs @@ -4,6 +4,7 @@ // Struct with size over 1GiB #[derive(Debug)] +#[allow(dead_code)] pub struct BigStruct { buf: [u8; 1024 * 1024 * 1024], a: u64, @@ -20,20 +21,23 @@ pub struct ManagedBuf { impl ManagedBuf { pub fn new() -> impl Init { - init!(ManagedBuf { buf <- zeroed() }) + init!(ManagedBuf { buf <- init_zeroed() }) } } fn main() { - // we want to initialize the struct in-place, otherwise we would get a stackoverflow - let buf: Box = Box::init(init!(BigStruct { - buf <- zeroed(), - a: 7, - b: 186, - c: 7789, - d: 34, - managed_buf <- ManagedBuf::new(), - })) - .unwrap(); - println!("{}", core::mem::size_of_val(&*buf)); + #[cfg(any(feature = "std", feature = "alloc"))] + { + // we want to initialize the struct in-place, otherwise we would get a stackoverflow + let buf: Box = Box::init(init!(BigStruct { + buf <- init_zeroed(), + a: 7, + b: 186, + c: 7789, + d: 34, + managed_buf <- ManagedBuf::new(), + })) + .unwrap(); + println!("{}", core::mem::size_of_val(&*buf)); + } } diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs index 0bbc7b8d83a1..f9e117c7dfe0 100644 --- a/rust/pin-init/examples/linked_list.rs +++ b/rust/pin-init/examples/linked_list.rs @@ -14,8 +14,9 @@ use pin_init::*; -#[expect(unused_attributes)] +#[allow(unused_attributes)] mod error; +#[allow(unused_imports)] use error::Error; #[pin_data(PinnedDrop)] @@ -39,6 +40,7 @@ pub fn new() -> impl PinInit { } #[inline] + #[allow(dead_code)] pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { try_pin_init!(&this in Self { prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}), @@ -112,6 +114,7 @@ fn next(&self) -> &Link { } #[inline] + #[allow(dead_code)] fn prev(&self) -> &Link { unsafe { &(*self.0.get().as_ptr()).prev } } @@ -137,8 +140,13 @@ fn set(&self, val: &Link) { } } +#[allow(dead_code)] +#[cfg(not(any(feature = "std", feature = "alloc")))] +fn main() {} + #[allow(dead_code)] #[cfg_attr(test, test)] +#[cfg(any(feature = "std", feature = "alloc"))] fn main() -> Result<(), Error> { let a = Box::pin_init(ListHead::new())?; stack_pin_init!(let b = ListHead::insert_next(&a)); diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 3e3630780c96..9f295226cd64 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -12,14 +12,15 @@ pin::Pin, sync::atomic::{AtomicBool, Ordering}, }; +#[cfg(feature = "std")] use std::{ sync::Arc, - thread::{self, park, sleep, Builder, Thread}, + thread::{self, sleep, Builder, Thread}, time::Duration, }; use pin_init::*; -#[expect(unused_attributes)] +#[allow(unused_attributes)] #[path = "./linked_list.rs"] pub mod linked_list; use linked_list::*; @@ -36,6 +37,7 @@ pub fn acquire(&self) -> SpinLockGuard<'_> { .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) .is_err() { + #[cfg(feature = "std")] while self.inner.load(Ordering::Relaxed) { thread::yield_now(); } @@ -94,7 +96,8 @@ pub fn lock(&self) -> Pin> { // println!("wait list length: {}", self.wait_list.size()); while self.locked.get() { drop(sguard); - park(); + #[cfg(feature = "std")] + thread::park(); sguard = self.spin_lock.acquire(); } // This does have an effect, as the ListHead inside wait_entry implements Drop! @@ -131,8 +134,11 @@ fn drop(&mut self) { let sguard = self.mtx.spin_lock.acquire(); self.mtx.locked.set(false); if let Some(list_field) = self.mtx.wait_list.next() { - let wait_entry = list_field.as_ptr().cast::(); - unsafe { (*wait_entry).thread.unpark() }; + let _wait_entry = list_field.as_ptr().cast::(); + #[cfg(feature = "std")] + unsafe { + (*_wait_entry).thread.unpark() + }; } drop(sguard); } @@ -159,52 +165,61 @@ fn deref_mut(&mut self) -> &mut Self::Target { struct WaitEntry { #[pin] wait_list: ListHead, + #[cfg(feature = "std")] thread: Thread, } impl WaitEntry { #[inline] fn insert_new(list: &ListHead) -> impl PinInit + '_ { - pin_init!(Self { - thread: thread::current(), - wait_list <- ListHead::insert_prev(list), - }) + #[cfg(feature = "std")] + { + pin_init!(Self { + thread: thread::current(), + wait_list <- ListHead::insert_prev(list), + }) + } + #[cfg(not(feature = "std"))] + { + pin_init!(Self { + wait_list <- ListHead::insert_prev(list), + }) + } } } -#[cfg(not(any(feature = "std", feature = "alloc")))] -fn main() {} - -#[allow(dead_code)] #[cfg_attr(test, test)] -#[cfg(any(feature = "std", feature = "alloc"))] +#[allow(dead_code)] fn main() { - let mtx: Pin>> = Arc::pin_init(CMutex::new(0)).unwrap(); - let mut handles = vec![]; - let thread_count = 20; - let workload = if cfg!(miri) { 100 } else { 1_000 }; - for i in 0..thread_count { - let mtx = mtx.clone(); - handles.push( - Builder::new() - .name(format!("worker #{i}")) - .spawn(move || { - for _ in 0..workload { - *mtx.lock() += 1; - } - println!("{i} halfway"); - sleep(Duration::from_millis((i as u64) * 10)); - for _ in 0..workload { - *mtx.lock() += 1; - } - println!("{i} finished"); - }) - .expect("should not fail"), - ); + #[cfg(feature = "std")] + { + let mtx: Pin>> = Arc::pin_init(CMutex::new(0)).unwrap(); + let mut handles = vec![]; + let thread_count = 20; + let workload = if cfg!(miri) { 100 } else { 1_000 }; + for i in 0..thread_count { + let mtx = mtx.clone(); + handles.push( + Builder::new() + .name(format!("worker #{i}")) + .spawn(move || { + for _ in 0..workload { + *mtx.lock() += 1; + } + println!("{i} halfway"); + sleep(Duration::from_millis((i as u64) * 10)); + for _ in 0..workload { + *mtx.lock() += 1; + } + println!("{i} finished"); + }) + .expect("should not fail"), + ); + } + for h in handles { + h.join().expect("thread panicked"); + } + println!("{:?}", &*mtx.lock()); + assert_eq!(*mtx.lock(), workload * thread_count * 2); } - for h in handles { - h.join().expect("thread panicked"); - } - println!("{:?}", &*mtx.lock()); - assert_eq!(*mtx.lock(), workload * thread_count * 2); } diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index 5acc5108b954..49b004c8c137 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -44,6 +44,7 @@ fn drop(self: Pin<&mut Self>) { pub enum Error { #[allow(dead_code)] IO(std::io::Error), + #[allow(dead_code)] Alloc, } @@ -61,6 +62,7 @@ fn from(_: AllocError) -> Self { } impl PThreadMutex { + #[allow(dead_code)] pub fn new(data: T) -> impl PinInit { fn init_raw() -> impl PinInit, Error> { let init = |slot: *mut UnsafeCell| { @@ -103,6 +105,7 @@ fn init_raw() -> impl PinInit, Error> { }? Error) } + #[allow(dead_code)] pub fn lock(&self) -> PThreadMutexGuard<'_, T> { // SAFETY: raw is always initialized unsafe { libc::pthread_mutex_lock(self.raw.get()) }; @@ -137,6 +140,7 @@ fn deref_mut(&mut self) -> &mut Self::Target { } #[cfg_attr(test, test)] +#[cfg_attr(all(test, miri), ignore)] fn main() { #[cfg(all(any(feature = "std", feature = "alloc"), not(windows)))] { diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 48531413ab94..0e165daa9798 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -3,6 +3,7 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] +#![allow(unused_imports)] use core::{ cell::{Cell, UnsafeCell}, @@ -12,12 +13,13 @@ time::Duration, }; use pin_init::*; +#[cfg(feature = "std")] use std::{ sync::Arc, thread::{sleep, Builder}, }; -#[expect(unused_attributes)] +#[allow(unused_attributes)] mod mutex; use mutex::*; @@ -82,42 +84,41 @@ unsafe fn __pinned_init( pub static COUNT: StaticInit, CountInit> = StaticInit::new(CountInit); -#[cfg(not(any(feature = "std", feature = "alloc")))] -fn main() {} - -#[cfg(any(feature = "std", feature = "alloc"))] fn main() { - let mtx: Pin>> = Arc::pin_init(CMutex::new(0)).unwrap(); - let mut handles = vec![]; - let thread_count = 20; - let workload = 1_000; - for i in 0..thread_count { - let mtx = mtx.clone(); - handles.push( - Builder::new() - .name(format!("worker #{i}")) - .spawn(move || { - for _ in 0..workload { - *COUNT.lock() += 1; - std::thread::sleep(std::time::Duration::from_millis(10)); - *mtx.lock() += 1; - std::thread::sleep(std::time::Duration::from_millis(10)); - *COUNT.lock() += 1; - } - println!("{i} halfway"); - sleep(Duration::from_millis((i as u64) * 10)); - for _ in 0..workload { - std::thread::sleep(std::time::Duration::from_millis(10)); - *mtx.lock() += 1; - } - println!("{i} finished"); - }) - .expect("should not fail"), - ); + #[cfg(feature = "std")] + { + let mtx: Pin>> = Arc::pin_init(CMutex::new(0)).unwrap(); + let mut handles = vec![]; + let thread_count = 20; + let workload = 1_000; + for i in 0..thread_count { + let mtx = mtx.clone(); + handles.push( + Builder::new() + .name(format!("worker #{i}")) + .spawn(move || { + for _ in 0..workload { + *COUNT.lock() += 1; + std::thread::sleep(std::time::Duration::from_millis(10)); + *mtx.lock() += 1; + std::thread::sleep(std::time::Duration::from_millis(10)); + *COUNT.lock() += 1; + } + println!("{i} halfway"); + sleep(Duration::from_millis((i as u64) * 10)); + for _ in 0..workload { + std::thread::sleep(std::time::Duration::from_millis(10)); + *mtx.lock() += 1; + } + println!("{i} finished"); + }) + .expect("should not fail"), + ); + } + for h in handles { + h.join().expect("thread panicked"); + } + println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock()); + assert_eq!(*mtx.lock(), workload * thread_count * 2); } - for h in handles { - h.join().expect("thread panicked"); - } - println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock()); - assert_eq!(*mtx.lock(), workload * thread_count * 2); } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 557b5948cddc..90f18e9a2912 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -188,6 +188,7 @@ pub fn init(self: Pin<&mut Self>, init: impl PinInit) -> Result impl PinInit { //! try_pin_init!(Self { //! status <- CMutex::new(0), -//! buffer: Box::init(pin_init::zeroed())?, +//! buffer: Box::init(pin_init::init_zeroed())?, //! }? Error) //! } //! } @@ -742,7 +742,7 @@ macro_rules! stack_try_pin_init { /// - Fields that you want to initialize in-place have to use `<-` instead of `:`. /// - In front of the initializer you can write `&this in` to have access to a [`NonNull`] /// pointer named `this` inside of the initializer. -/// - Using struct update syntax one can place `..Zeroable::zeroed()` at the very end of the +/// - Using struct update syntax one can place `..Zeroable::init_zeroed()` at the very end of the /// struct, this initializes every field with 0 and then runs all initializers specified in the /// body. This can only be done if [`Zeroable`] is implemented for the struct. /// @@ -769,7 +769,7 @@ macro_rules! stack_try_pin_init { /// }); /// let init = pin_init!(Buf { /// buf: [1; 64], -/// ..Zeroable::zeroed() +/// ..Zeroable::init_zeroed() /// }); /// ``` /// @@ -805,7 +805,7 @@ macro_rules! pin_init { /// ```rust /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; -/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, zeroed}; +/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed}; /// /// #[pin_data] /// struct BigBuf { @@ -817,7 +817,7 @@ macro_rules! pin_init { /// impl BigBuf { /// fn new() -> impl PinInit { /// try_pin_init!(Self { -/// big: Box::init(zeroed())?, +/// big: Box::init(init_zeroed())?, /// small: [0; 1024 * 1024], /// ptr: core::ptr::null_mut(), /// }? Error) @@ -866,7 +866,7 @@ macro_rules! try_pin_init { /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::InPlaceInit; -/// use pin_init::{init, Init, zeroed}; +/// use pin_init::{init, Init, init_zeroed}; /// /// struct BigBuf { /// small: [u8; 1024 * 1024], @@ -875,7 +875,7 @@ macro_rules! try_pin_init { /// impl BigBuf { /// fn new() -> impl Init { /// init!(Self { -/// small <- zeroed(), +/// small <- init_zeroed(), /// }) /// } /// } @@ -913,7 +913,7 @@ macro_rules! init { /// # #![feature(allocator_api)] /// # use core::alloc::AllocError; /// # use pin_init::InPlaceInit; -/// use pin_init::{try_init, Init, zeroed}; +/// use pin_init::{try_init, Init, init_zeroed}; /// /// struct BigBuf { /// big: Box<[u8; 1024 * 1024 * 1024]>, @@ -923,7 +923,7 @@ macro_rules! init { /// impl BigBuf { /// fn new() -> impl Init { /// try_init!(Self { -/// big: Box::init(zeroed())?, +/// big: Box::init(init_zeroed())?, /// small: [0; 1024 * 1024], /// }? AllocError) /// } @@ -953,7 +953,7 @@ macro_rules! try_init { /// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is /// structurally pinned. /// -/// # Example +/// # Examples /// /// This will succeed: /// ``` @@ -1170,7 +1170,7 @@ pub unsafe trait Init: PinInit { /// /// ```rust /// # #![expect(clippy::disallowed_names)] - /// use pin_init::{init, zeroed, Init}; + /// use pin_init::{init, init_zeroed, Init}; /// /// struct Foo { /// buf: [u8; 1_000_000], @@ -1183,7 +1183,7 @@ pub unsafe trait Init: PinInit { /// } /// /// let foo = init!(Foo { - /// buf <- zeroed() + /// buf <- init_zeroed() /// }).chain(|foo| { /// foo.setup(); /// Ok(()) @@ -1390,20 +1390,44 @@ pub fn pin_init_array_from_fn( unsafe { pin_init_from_closure(init) } } -// SAFETY: Every type can be initialized by-value. -unsafe impl Init for T { - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: TODO. +// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`. +unsafe impl Init for T { + unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; Ok(()) } } -// SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`. -unsafe impl PinInit for T { +// SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of +// `slot`. Additionally, all pinning invariants of `T` are upheld. +unsafe impl PinInit for T { + unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self) }; + Ok(()) + } +} + +// SAFETY: when the `__init` function returns with +// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. +// - `Err(err)`, slot was not written to. +unsafe impl Init for Result { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self?) }; + Ok(()) + } +} + +// SAFETY: when the `__pinned_init` function returns with +// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. +// - `Err(err)`, slot was not written to. +unsafe impl PinInit for Result { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: TODO. - unsafe { self.__init(slot) } + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self?) }; + Ok(()) } } @@ -1471,7 +1495,45 @@ pub unsafe trait PinnedDrop: __internal::HasPinData { /// ```rust,ignore /// let val: Self = unsafe { core::mem::zeroed() }; /// ``` -pub unsafe trait Zeroable {} +pub unsafe trait Zeroable { + /// Create a new zeroed `Self`. + /// + /// The returned initializer will write `0x00` to every byte of the given `slot`. + #[inline] + fn init_zeroed() -> impl Init + where + Self: Sized, + { + init_zeroed() + } + + /// Create a `Self` consisting of all zeroes. + /// + /// Whenever a type implements [`Zeroable`], this function should be preferred over + /// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. + /// + /// # Examples + /// + /// ``` + /// use pin_init::{Zeroable, zeroed}; + /// + /// #[derive(Zeroable)] + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// + /// let point: Point = zeroed(); + /// assert_eq!(point.x, 0); + /// assert_eq!(point.y, 0); + /// ``` + fn zeroed() -> Self + where + Self: Sized, + { + zeroed() + } +} /// Marker trait for types that allow `Option` to be set to all zeroes in order to write /// `None` to that location. @@ -1484,11 +1546,21 @@ pub unsafe trait ZeroableOption {} // SAFETY: by the safety requirement of `ZeroableOption`, this is valid. unsafe impl Zeroable for Option {} -/// Create a new zeroed T. +// SAFETY: `Option<&T>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for &T {} +// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for &mut T {} +// SAFETY: `Option>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for NonNull {} + +/// Create an initializer for a zeroed `T`. /// /// The returned initializer will write `0x00` to every byte of the given `slot`. #[inline] -pub fn zeroed() -> impl Init { +pub fn init_zeroed() -> impl Init { // SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T` // and because we write all zeroes, the memory is initialized. unsafe { @@ -1499,6 +1571,31 @@ pub fn zeroed() -> impl Init { } } +/// Create a `T` consisting of all zeroes. +/// +/// Whenever a type implements [`Zeroable`], this function should be preferred over +/// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. +/// +/// # Examples +/// +/// ``` +/// use pin_init::{Zeroable, zeroed}; +/// +/// #[derive(Zeroable)] +/// struct Point { +/// x: u32, +/// y: u32, +/// } +/// +/// let point: Point = zeroed(); +/// assert_eq!(point.x, 0); +/// assert_eq!(point.y, 0); +/// ``` +pub const fn zeroed() -> T { + // SAFETY:By the type invariants of `Zeroable`, all zeroes is a valid bit pattern for `T`. + unsafe { core::mem::zeroed() } +} + macro_rules! impl_zeroable { ($($({$($generics:tt)*})? $t:ty, )*) => { // SAFETY: Safety comments written in the macro invocation. @@ -1536,7 +1633,6 @@ macro_rules! impl_zeroable { Option, Option, Option, Option, Option, Option, Option, Option, - {} Option>, // SAFETY: `null` pointer is valid. // @@ -1566,6 +1662,22 @@ unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J); +macro_rules! impl_fn_zeroable_option { + ([$($abi:literal),* $(,)?] $args:tt) => { + $(impl_fn_zeroable_option!({extern $abi} $args);)* + $(impl_fn_zeroable_option!({unsafe extern $abi} $args);)* + }; + ({$($prefix:tt)*} {$(,)?}) => {}; + ({$($prefix:tt)*} {$ret:ident, $($rest:ident),* $(,)?}) => { + // SAFETY: function pointers are part of the option layout optimization: + // . + unsafe impl<$ret, $($rest),*> ZeroableOption for $($prefix)* fn($($rest),*) -> $ret {} + impl_fn_zeroable_option!({$($prefix)*} {$($rest),*,}); + }; +} + +impl_fn_zeroable_option!(["Rust", "C"] { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U }); + /// This trait allows creating an instance of `Self` which contains exactly one /// [structurally pinned value](https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning). /// diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index 935d77745d1d..9ced630737b8 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1030,7 +1030,7 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> /// /// This macro has multiple internal call configurations, these are always the very first ident: /// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros. -/// - `with_update_parsed`: when the `..Zeroable::zeroed()` syntax has been handled. +/// - `with_update_parsed`: when the `..Zeroable::init_zeroed()` syntax has been handled. /// - `init_slot`: recursively creates the code that initializes all fields in `slot`. /// - `make_initializer`: recursively create the struct initializer that guarantees that every /// field has been initialized exactly once. @@ -1059,7 +1059,7 @@ macro_rules! __init_internal { @data($data, $($use_data)?), @has_data($has_data, $get_data), @construct_closure($construct_closure), - @zeroed(), // Nothing means default behavior. + @init_zeroed(), // Nothing means default behavior. ) }; ( @@ -1074,7 +1074,7 @@ macro_rules! __init_internal { @has_data($has_data:ident, $get_data:ident), // `pin_init_from_closure` or `init_from_closure`. @construct_closure($construct_closure:ident), - @munch_fields(..Zeroable::zeroed()), + @munch_fields(..Zeroable::init_zeroed()), ) => { $crate::__init_internal!(with_update_parsed: @this($($this)?), @@ -1084,7 +1084,7 @@ macro_rules! __init_internal { @data($data, $($use_data)?), @has_data($has_data, $get_data), @construct_closure($construct_closure), - @zeroed(()), // `()` means zero all fields not mentioned. + @init_zeroed(()), // `()` means zero all fields not mentioned. ) }; ( @@ -1124,7 +1124,7 @@ macro_rules! __init_internal { @has_data($has_data:ident, $get_data:ident), // `pin_init_from_closure` or `init_from_closure`. @construct_closure($construct_closure:ident), - @zeroed($($init_zeroed:expr)?), + @init_zeroed($($init_zeroed:expr)?), ) => {{ // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return // type and shadow it later when we insert the arbitrary user code. That way there will be @@ -1196,7 +1196,7 @@ fn assert_zeroable(_: *mut T) {} @data($data:ident), @slot($slot:ident), @guards($($guards:ident,)*), - @munch_fields($(..Zeroable::zeroed())? $(,)?), + @munch_fields($(..Zeroable::init_zeroed())? $(,)?), ) => { // Endpoint of munching, no fields are left. If execution reaches this point, all fields // have been initialized. Therefore we can now dismiss the guards by forgetting them. @@ -1300,11 +1300,11 @@ fn assert_zeroable(_: *mut T) {} (make_initializer: @slot($slot:ident), @type_name($t:path), - @munch_fields(..Zeroable::zeroed() $(,)?), + @munch_fields(..Zeroable::init_zeroed() $(,)?), @acc($($acc:tt)*), ) => { // Endpoint, nothing more to munch, create the initializer. Since the users specified - // `..Zeroable::zeroed()`, the slot will already have been zeroed and all field that have + // `..Zeroable::init_zeroed()`, the slot will already have been zeroed and all field that have // not been overwritten are thus zero and initialized. We still check that all fields are // actually accessible by using the struct update syntax ourselves. // We are inside of a closure that is never executed and thus we can abuse `slot` to diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs index c98d7a8cde77..31c2f713313f 100644 --- a/rust/uapi/lib.rs +++ b/rust/uapi/lib.rs @@ -14,6 +14,9 @@ #![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))] #![allow( clippy::all, + clippy::cast_lossless, + clippy::ptr_as_ptr, + clippy::ref_as_ptr, clippy::undocumented_unsafe_blocks, dead_code, missing_docs, diff --git a/samples/Kconfig b/samples/Kconfig index ffef99950206..6e072a5f1ed8 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -54,7 +54,7 @@ config SAMPLE_FTRACE_OPS measures the time taken to invoke one function a number of times. config SAMPLE_TRACE_ARRAY - tristate "Build sample module for kernel access to Ftrace instancess" + tristate "Build sample module for kernel access to Ftrace instances" depends on EVENT_TRACING && m help This builds a module that demonstrates the use of various APIs to @@ -316,10 +316,9 @@ config SAMPLE_HUNG_TASK depends on DETECT_HUNG_TASK && DEBUG_FS help Build a module that provides debugfs files (e.g., mutex, semaphore, - etc.) under /hung_task. If user reads one of these files, - it will sleep long time (256 seconds) with holding a lock. Thus, - if 2 or more processes read the same file concurrently, it will - be detected by the hung_task watchdog. + rw_semaphore_read, rw_semaphore_write) under /hung_task. + Reading these files with multiple processes triggers hung task + detection by holding locks for a long time (256 seconds). source "samples/rust/Kconfig" diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index 36d2cd933f5a..7ebd352138e4 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -12,6 +12,11 @@ #include #include +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "damon_sample_mtier." + static unsigned long node0_start_addr __read_mostly; module_param(node0_start_addr, ulong, 0600); @@ -24,20 +29,47 @@ module_param(node1_start_addr, ulong, 0600); static unsigned long node1_end_addr __read_mostly; module_param(node1_end_addr, ulong, 0600); +static unsigned long node0_mem_used_bp __read_mostly = 9970; +module_param(node0_mem_used_bp, ulong, 0600); + +static unsigned long node0_mem_free_bp __read_mostly = 50; +module_param(node0_mem_free_bp, ulong, 0600); + static int damon_sample_mtier_enable_store( const char *val, const struct kernel_param *kp); -static const struct kernel_param_ops enable_param_ops = { +static const struct kernel_param_ops enabled_param_ops = { .set = damon_sample_mtier_enable_store, .get = param_get_bool, }; -static bool enable __read_mostly; -module_param_cb(enable, &enable_param_ops, &enable, 0600); -MODULE_PARM_DESC(enable, "Enable of disable DAMON_SAMPLE_MTIER"); +static bool enabled __read_mostly; +module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); +MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_MTIER"); + +static bool detect_node_addresses __read_mostly; +module_param(detect_node_addresses, bool, 0600); static struct damon_ctx *ctxs[2]; +struct region_range { + phys_addr_t start; + phys_addr_t end; +}; + +static int nid_to_phys(int target_node, struct region_range *range) +{ + if (!node_online(target_node)) { + pr_err("NUMA node %d is not online\n", target_node); + return -EINVAL; + } + + range->start = PFN_PHYS(node_start_pfn(target_node)); + range->end = PFN_PHYS(node_end_pfn(target_node)); + + return 0; +} + static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) { struct damon_ctx *ctx; @@ -47,6 +79,8 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) struct damos *scheme; struct damos_quota_goal *quota_goal; struct damos_filter *filter; + struct region_range addr; + int ret; ctx = damon_new_ctx(); if (!ctx) @@ -76,9 +110,17 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) if (!target) goto free_out; damon_add_target(ctx, target); - region = damon_new_region( - promote ? node1_start_addr : node0_start_addr, - promote ? node1_end_addr : node0_end_addr); + + if (detect_node_addresses) { + ret = promote ? nid_to_phys(1, &addr) : nid_to_phys(0, &addr); + if (ret) + goto free_out; + } else { + addr.start = promote ? node1_start_addr : node0_start_addr; + addr.end = promote ? node1_end_addr : node0_end_addr; + } + + region = damon_new_region(addr.start, addr.end); if (!region) goto free_out; damon_add_region(region, target); @@ -112,7 +154,7 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) quota_goal = damos_new_quota_goal( promote ? DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP, - promote ? 9970 : 50); + promote ? node0_mem_used_bp : node0_mem_free_bp); if (!quota_goal) goto free_out; quota_goal->nid = 0; @@ -151,27 +193,41 @@ static void damon_sample_mtier_stop(void) damon_destroy_ctx(ctxs[1]); } +static bool init_called; + static int damon_sample_mtier_enable_store( const char *val, const struct kernel_param *kp) { - bool enabled = enable; + bool is_enabled = enabled; int err; - err = kstrtobool(val, &enable); + err = kstrtobool(val, &enabled); if (err) return err; - if (enable == enabled) + if (enabled == is_enabled) return 0; - if (enable) - return damon_sample_mtier_start(); + if (enabled) { + err = damon_sample_mtier_start(); + if (err) + enabled = false; + return err; + } damon_sample_mtier_stop(); return 0; } static int __init damon_sample_mtier_init(void) { + int err = 0; + + init_called = true; + if (enabled) { + err = damon_sample_mtier_start(); + if (err) + enabled = false; + } return 0; } diff --git a/samples/damon/prcl.c b/samples/damon/prcl.c index 056b1b21a0fe..1b839c06a612 100644 --- a/samples/damon/prcl.c +++ b/samples/damon/prcl.c @@ -11,26 +11,32 @@ #include #include +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "damon_sample_prcl." + static int target_pid __read_mostly; module_param(target_pid, int, 0600); static int damon_sample_prcl_enable_store( const char *val, const struct kernel_param *kp); -static const struct kernel_param_ops enable_param_ops = { +static const struct kernel_param_ops enabled_param_ops = { .set = damon_sample_prcl_enable_store, .get = param_get_bool, }; -static bool enable __read_mostly; -module_param_cb(enable, &enable_param_ops, &enable, 0600); -MODULE_PARM_DESC(enable, "Enable of disable DAMON_SAMPLE_WSSE"); +static bool enabled __read_mostly; +module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); +MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_PRCL"); static struct damon_ctx *ctx; static struct pid *target_pidp; -static int damon_sample_prcl_after_aggregate(struct damon_ctx *c) +static int damon_sample_prcl_repeat_call_fn(void *data) { + struct damon_ctx *c = data; struct damon_target *t; damon_for_each_target(t, c) { @@ -46,10 +52,16 @@ static int damon_sample_prcl_after_aggregate(struct damon_ctx *c) return 0; } +static struct damon_call_control repeat_call_control = { + .fn = damon_sample_prcl_repeat_call_fn, + .repeat = true, +}; + static int damon_sample_prcl_start(void) { struct damon_target *target; struct damos *scheme; + int err; pr_info("start\n"); @@ -74,8 +86,6 @@ static int damon_sample_prcl_start(void) } target->pid = target_pidp; - ctx->callback.after_aggregation = damon_sample_prcl_after_aggregate; - scheme = damon_new_scheme( &(struct damos_access_pattern) { .min_sz_region = PAGE_SIZE, @@ -95,7 +105,12 @@ static int damon_sample_prcl_start(void) } damon_set_schemes(ctx, &scheme, 1); - return damon_start(&ctx, 1, true); + err = damon_start(&ctx, 1, true); + if (err) + return err; + + repeat_call_control.data = ctx; + return damon_call(ctx, &repeat_call_control); } static void damon_sample_prcl_stop(void) @@ -105,31 +120,43 @@ static void damon_sample_prcl_stop(void) damon_stop(&ctx, 1); damon_destroy_ctx(ctx); } - if (target_pidp) - put_pid(target_pidp); } +static bool init_called; + static int damon_sample_prcl_enable_store( const char *val, const struct kernel_param *kp) { - bool enabled = enable; + bool is_enabled = enabled; int err; - err = kstrtobool(val, &enable); + err = kstrtobool(val, &enabled); if (err) return err; - if (enable == enabled) + if (enabled == is_enabled) return 0; - if (enable) - return damon_sample_prcl_start(); + if (enabled) { + err = damon_sample_prcl_start(); + if (err) + enabled = false; + return err; + } damon_sample_prcl_stop(); return 0; } static int __init damon_sample_prcl_init(void) { + int err = 0; + + init_called = true; + if (enabled) { + err = damon_sample_prcl_start(); + if (err) + enabled = false; + } return 0; } diff --git a/samples/damon/wsse.c b/samples/damon/wsse.c index 11be25803274..da052023b099 100644 --- a/samples/damon/wsse.c +++ b/samples/damon/wsse.c @@ -12,26 +12,32 @@ #include #include +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "damon_sample_wsse." + static int target_pid __read_mostly; module_param(target_pid, int, 0600); static int damon_sample_wsse_enable_store( const char *val, const struct kernel_param *kp); -static const struct kernel_param_ops enable_param_ops = { +static const struct kernel_param_ops enabled_param_ops = { .set = damon_sample_wsse_enable_store, .get = param_get_bool, }; -static bool enable __read_mostly; -module_param_cb(enable, &enable_param_ops, &enable, 0600); -MODULE_PARM_DESC(enable, "Enable or disable DAMON_SAMPLE_WSSE"); +static bool enabled __read_mostly; +module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); +MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_WSSE"); static struct damon_ctx *ctx; static struct pid *target_pidp; -static int damon_sample_wsse_after_aggregate(struct damon_ctx *c) +static int damon_sample_wsse_repeat_call_fn(void *data) { + struct damon_ctx *c = data; struct damon_target *t; damon_for_each_target(t, c) { @@ -47,9 +53,15 @@ static int damon_sample_wsse_after_aggregate(struct damon_ctx *c) return 0; } +static struct damon_call_control repeat_call_control = { + .fn = damon_sample_wsse_repeat_call_fn, + .repeat = true, +}; + static int damon_sample_wsse_start(void) { struct damon_target *target; + int err; pr_info("start\n"); @@ -74,8 +86,11 @@ static int damon_sample_wsse_start(void) } target->pid = target_pidp; - ctx->callback.after_aggregation = damon_sample_wsse_after_aggregate; - return damon_start(&ctx, 1, true); + err = damon_start(&ctx, 1, true); + if (err) + return err; + repeat_call_control.data = ctx; + return damon_call(ctx, &repeat_call_control); } static void damon_sample_wsse_stop(void) @@ -85,32 +100,44 @@ static void damon_sample_wsse_stop(void) damon_stop(&ctx, 1); damon_destroy_ctx(ctx); } - if (target_pidp) - put_pid(target_pidp); } +static bool init_called; + static int damon_sample_wsse_enable_store( const char *val, const struct kernel_param *kp) { - bool enabled = enable; + bool is_enabled = enabled; int err; - err = kstrtobool(val, &enable); + err = kstrtobool(val, &enabled); if (err) return err; - if (enable == enabled) + if (enabled == is_enabled) return 0; - if (enable) - return damon_sample_wsse_start(); + if (enabled) { + err = damon_sample_wsse_start(); + if (err) + enabled = false; + return err; + } damon_sample_wsse_stop(); return 0; } static int __init damon_sample_wsse_init(void) { - return 0; + int err = 0; + + init_called = true; + if (enabled) { + err = damon_sample_wsse_start(); + if (err) + enabled = false; + } + return err; } module_init(damon_sample_wsse_init); diff --git a/samples/fanotify/fs-monitor.c b/samples/fanotify/fs-monitor.c index 608db24c471e..28c0a652ffeb 100644 --- a/samples/fanotify/fs-monitor.c +++ b/samples/fanotify/fs-monitor.c @@ -12,6 +12,9 @@ #include #include #include +#ifndef __GLIBC__ +#include +#endif #ifndef FAN_FS_ERROR #define FAN_FS_ERROR 0x00008000 @@ -95,7 +98,11 @@ static void handle_notifications(char *buffer, int len) fid = (struct fanotify_event_info_fid *) info; printf("\tfsid: %x%x\n", +#if defined(__GLIBC__) fid->fsid.val[0], fid->fsid.val[1]); +#else + fid->fsid.__val[0], fid->fsid.__val[1]); +#endif print_fh((struct file_handle *) &fid->handle); break; diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c index cfea7a38befb..da3a9f2091f5 100644 --- a/samples/ftrace/ftrace-direct-modify.c +++ b/samples/ftrace/ftrace-direct-modify.c @@ -75,8 +75,8 @@ asm ( CALL_DEPTH_ACCOUNT " call my_direct_func1\n" " leave\n" -" .size my_tramp1, .-my_tramp1\n" ASM_RET +" .size my_tramp1, .-my_tramp1\n" " .type my_tramp2, @function\n" " .globl my_tramp2\n" diff --git a/samples/hung_task/hung_task_tests.c b/samples/hung_task/hung_task_tests.c index a5c09bd3a47d..0360ec916890 100644 --- a/samples/hung_task/hung_task_tests.c +++ b/samples/hung_task/hung_task_tests.c @@ -4,11 +4,12 @@ * semaphore, etc. * * Usage: Load this module and read `/hung_task/mutex`, - * `/hung_task/semaphore`, etc., with 2 or more processes. + * `/hung_task/semaphore`, `/hung_task/rw_semaphore_read`, + * `/hung_task/rw_semaphore_write`, etc., with 2 or more processes. * * This is for testing kernel hung_task error messages with various locking - * mechanisms (e.g., mutex, semaphore, etc.). Note that this may freeze - * your system or cause a panic. Use only for testing purposes. + * mechanisms (e.g., mutex, semaphore, rw_semaphore_read, rw_semaphore_write, etc.). + * Note that this may freeze your system or cause a panic. Use only for testing purposes. */ #include @@ -17,21 +18,29 @@ #include #include #include +#include -#define HUNG_TASK_DIR "hung_task" -#define HUNG_TASK_MUTEX_FILE "mutex" -#define HUNG_TASK_SEM_FILE "semaphore" -#define SLEEP_SECOND 256 +#define HUNG_TASK_DIR "hung_task" +#define HUNG_TASK_MUTEX_FILE "mutex" +#define HUNG_TASK_SEM_FILE "semaphore" +#define HUNG_TASK_RWSEM_READ_FILE "rw_semaphore_read" +#define HUNG_TASK_RWSEM_WRITE_FILE "rw_semaphore_write" +#define SLEEP_SECOND 256 static const char dummy_string[] = "This is a dummy string."; static DEFINE_MUTEX(dummy_mutex); static DEFINE_SEMAPHORE(dummy_sem, 1); +static DECLARE_RWSEM(dummy_rwsem); static struct dentry *hung_task_dir; /* Mutex-based read function */ static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { + /* Check if data is already read */ + if (*ppos >= sizeof(dummy_string)) + return 0; + /* Second task waits on mutex, entering uninterruptible sleep */ guard(mutex)(&dummy_mutex); @@ -46,6 +55,10 @@ static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf, static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { + /* Check if data is already read */ + if (*ppos >= sizeof(dummy_string)) + return 0; + /* Second task waits on semaphore, entering uninterruptible sleep */ down(&dummy_sem); @@ -58,6 +71,46 @@ static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf, sizeof(dummy_string)); } +/* Read-write semaphore read function */ +static ssize_t read_dummy_rwsem_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + /* Check if data is already read */ + if (*ppos >= sizeof(dummy_string)) + return 0; + + /* Acquires read lock, allowing concurrent readers but blocks if write lock is held */ + down_read(&dummy_rwsem); + + /* Sleeps here, potentially triggering hung task detection if lock is held too long */ + msleep_interruptible(SLEEP_SECOND * 1000); + + up_read(&dummy_rwsem); + + return simple_read_from_buffer(user_buf, count, ppos, dummy_string, + sizeof(dummy_string)); +} + +/* Read-write semaphore write function */ +static ssize_t read_dummy_rwsem_write(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + /* Check if data is already read */ + if (*ppos >= sizeof(dummy_string)) + return 0; + + /* Acquires exclusive write lock, blocking all other readers and writers */ + down_write(&dummy_rwsem); + + /* Sleeps here, potentially triggering hung task detection if lock is held too long */ + msleep_interruptible(SLEEP_SECOND * 1000); + + up_write(&dummy_rwsem); + + return simple_read_from_buffer(user_buf, count, ppos, dummy_string, + sizeof(dummy_string)); +} + /* File operations for mutex */ static const struct file_operations hung_task_mutex_fops = { .read = read_dummy_mutex, @@ -68,6 +121,16 @@ static const struct file_operations hung_task_sem_fops = { .read = read_dummy_semaphore, }; +/* File operations for rw_semaphore read */ +static const struct file_operations hung_task_rwsem_read_fops = { + .read = read_dummy_rwsem_read, +}; + +/* File operations for rw_semaphore write */ +static const struct file_operations hung_task_rwsem_write_fops = { + .read = read_dummy_rwsem_write, +}; + static int __init hung_task_tests_init(void) { hung_task_dir = debugfs_create_dir(HUNG_TASK_DIR, NULL); @@ -79,6 +142,10 @@ static int __init hung_task_tests_init(void) &hung_task_mutex_fops); debugfs_create_file(HUNG_TASK_SEM_FILE, 0400, hung_task_dir, NULL, &hung_task_sem_fops); + debugfs_create_file(HUNG_TASK_RWSEM_READ_FILE, 0400, hung_task_dir, NULL, + &hung_task_rwsem_read_fops); + debugfs_create_file(HUNG_TASK_RWSEM_WRITE_FILE, 0400, hung_task_dir, NULL, + &hung_task_rwsem_write_fops); return 0; } diff --git a/samples/kobject/kobject-example.c b/samples/kobject/kobject-example.c index c9c3db19799a..36d87ca0bee2 100644 --- a/samples/kobject/kobject-example.c +++ b/samples/kobject/kobject-example.c @@ -13,7 +13,7 @@ /* * This module shows how to create a simple subdirectory in sysfs called - * /sys/kernel/kobject-example In that directory, 3 files are created: + * /sys/kernel/kobject_example In that directory, 3 files are created: * "foo", "baz", and "bar". If an integer is written to these files, it can be * later read out of it. */ @@ -102,7 +102,7 @@ static struct attribute *attrs[] = { * created for the attributes with the directory being the name of the * attribute group. */ -static struct attribute_group attr_group = { +static const struct attribute_group attr_group = { .attrs = attrs, }; diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index 552d7e363539..579ce150217c 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -14,8 +14,8 @@ /* * This module shows how to create a kset in sysfs called - * /sys/kernel/kset-example - * Then tree kobjects are created and assigned to this kset, "foo", "baz", + * /sys/kernel/kset_example + * Then three kobjects are created and assigned to this kset, "foo", "baz", * and "bar". In those kobjects, attributes of the same name are also * created and if an integer is written to these files, it can be later * read out of it. diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index 4e2854c6f9a3..e7af02f98208 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,10 @@ #include #include +#if defined(__GLIBC__) +#include +#endif + #ifndef landlock_create_ruleset static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, diff --git a/samples/mei/mei-amt-version.c b/samples/mei/mei-amt-version.c index 867debd3b912..1d7254bcb44c 100644 --- a/samples/mei/mei-amt-version.c +++ b/samples/mei/mei-amt-version.c @@ -69,11 +69,11 @@ #include #include #include +#include #include #include #include #include -#include #include /***************************************************************************** diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs index 60ddbe62cda3..af04bfa35cb2 100644 --- a/samples/rust/rust_configfs.rs +++ b/samples/rust/rust_configfs.rs @@ -14,7 +14,7 @@ module! { type: RustConfigfs, name: "rust_configfs", - author: "Rust for Linux Contributors", + authors: ["Rust for Linux Contributors"], description: "Rust configfs sample", license: "GPL", } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 9e05d5c0cdae..c5e7cce68654 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -4,7 +4,14 @@ //! //! To make this driver probe, QEMU must be run with `-device pci-testdev`. -use kernel::{bindings, device::Core, dma::CoherentAllocation, pci, prelude::*, types::ARef}; +use kernel::{ + bindings, + device::Core, + dma::{CoherentAllocation, Device, DmaMask}, + pci, + prelude::*, + types::ARef, +}; struct DmaSampleDriver { pdev: ARef, @@ -51,6 +58,11 @@ impl pci::Driver for DmaSampleDriver { fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + let mask = DmaMask::new::<64>(); + + // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. + unsafe { pdev.dma_set_mask_and_coherent(mask)? }; + let ca: CoherentAllocation = CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 3e15e6d002bb..f2a820683fc3 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -5,8 +5,7 @@ //! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{ - auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr, - InPlaceModule, + auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule, }; use pin_init::PinInit; @@ -114,7 +113,7 @@ fn init(module: &'static kernel::ThisModule) -> impl PinInit { module! { type: SampleModule, name: "rust_driver_auxiliary", - author: "Danilo Krummrich", + authors: ["Danilo Krummrich"], description: "Rust auxiliary driver", license: "GPL v2", } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 15147e4401b2..606946ff4d7f 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -18,16 +18,19 @@ impl Regs { type Bar0 = pci::Bar<{ Regs::END }>; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] struct TestIndex(u8); impl TestIndex { const NO_EVENTFD: Self = Self(0); } +#[pin_data(PinnedDrop)] struct SampleDriver { pdev: ARef, + #[pin] bar: Devres, + index: TestIndex, } kernel::pci_device_table!( @@ -73,13 +76,12 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> Result pdev.enable_device_mem()?; pdev.set_master(); - let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?; - - let drvdata = KBox::new( - Self { + let drvdata = KBox::pin_init( + try_pin_init!(Self { pdev: pdev.into(), - bar, - }, + bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")), + index: *info, + }), GFP_KERNEL, )?; @@ -90,12 +92,20 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> Result Self::testdev(info, bar)? ); - Ok(drvdata.into()) + Ok(drvdata) + } + + fn unbind(pdev: &pci::Device, this: Pin<&Self>) { + if let Ok(bar) = this.bar.access(pdev.as_ref()) { + // Reset pci-testdev by writing a new test index. + bar.write8(this.index.0, Regs::TEST); + } } } -impl Drop for SampleDriver { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for SampleDriver { + fn drop(self: Pin<&mut Self>) { dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n"); } } diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 8b42b3cfb363..69ed55b7b0fa 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -2,7 +2,78 @@ //! Rust Platform driver sample. -use kernel::{c_str, device::Core, of, platform, prelude::*, types::ARef}; +//! ACPI match table test +//! +//! This demonstrates how to test an ACPI-based Rust platform driver using QEMU +//! with a custom SSDT. +//! +//! Steps: +//! +//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content: +//! +//! ```asl +//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001) +//! { +//! Scope (\_SB) +//! { +//! Device (T432) +//! { +//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match +//! Name (_UID, 1) +//! Name (_STA, 0x0F) // Device present, enabled +//! Name (_CRS, ResourceTemplate () +//! { +//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000) +//! }) +//! } +//! } +//! } +//! ``` +//! +//! 2. **Compile the table**: +//! +//! ```sh +//! iasl -tc ssdt.dsl +//! ``` +//! +//! This generates `ssdt.aml` +//! +//! 3. **Run QEMU** with the compiled AML file: +//! +//! ```sh +//! qemu-system-x86_64 -m 512M \ +//! -enable-kvm \ +//! -kernel path/to/bzImage \ +//! -append "root=/dev/sda console=ttyS0" \ +//! -hda rootfs.img \ +//! -serial stdio \ +//! -acpitable file=ssdt.aml +//! ``` +//! +//! Requirements: +//! - The `rust_driver_platform` must be present either: +//! - built directly into the kernel (`bzImage`), or +//! - available as a `.ko` file and loadable from `rootfs.img` +//! +//! 4. **Verify it worked** by checking `dmesg`: +//! +//! ``` +//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'. +//! ``` +//! + +use kernel::{ + acpi, c_str, + device::{ + self, + property::{FwNodeReferenceArgs, NArgs}, + Core, + }, + of, platform, + prelude::*, + str::CString, + types::ARef, +}; struct SampleDriver { pdev: ARef, @@ -17,18 +88,32 @@ struct SampleDriver { [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))] ); +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::IdInfo, + [(acpi::DeviceId::new(c_str!("LNUXBEEF")), Info(0))] +); + impl platform::Driver for SampleDriver { type IdInfo = Info; const OF_ID_TABLE: Option> = Some(&OF_TABLE); + const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( pdev: &platform::Device, info: Option<&Self::IdInfo>, ) -> Result>> { - dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); + let dev = pdev.as_ref(); + + dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); if let Some(info) = info { - dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); + dev_info!(dev, "Probed with info: '{}'.\n", info.0); + } + + if dev.fwnode().is_some_and(|node| node.is_of_node()) { + Self::properties_parse(dev)?; } let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?; @@ -37,6 +122,62 @@ fn probe( } } +impl SampleDriver { + fn properties_parse(dev: &device::Device) -> Result { + let fwnode = dev.fwnode().ok_or(ENOENT)?; + + if let Ok(idx) = + fwnode.property_match_string(c_str!("compatible"), c_str!("test,rust-device")) + { + dev_info!(dev, "matched compatible string idx = {}\n", idx); + } + + let name = c_str!("compatible"); + let prop = fwnode.property_read::(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + + let name = c_str!("test,bool-prop"); + let prop = fwnode.property_read_bool(c_str!("test,bool-prop")); + dev_info!(dev, "'{name}'='{prop}'\n"); + + if fwnode.property_present(c_str!("test,u32-prop")) { + dev_info!(dev, "'test,u32-prop' is present\n"); + } + + let name = c_str!("test,u32-optional-prop"); + let prop = fwnode.property_read::(name).or(0x12); + dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n",); + + // A missing required property will print an error. Discard the error to + // prevent properties_parse from failing in that case. + let name = c_str!("test,u32-required-prop"); + let _ = fwnode.property_read::(name).required_by(dev); + + let name = c_str!("test,u32-prop"); + let prop: u32 = fwnode.property_read(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:#x}'\n"); + + let name = c_str!("test,i16-array"); + let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + let len = fwnode.property_count_elem::(name)?; + dev_info!(dev, "'{name}' length is {len}\n",); + + let name = c_str!("test,i16-array"); + let prop: KVec = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n"); + + for child in fwnode.children() { + let name = c_str!("test,ref-arg"); + let nargs = NArgs::N(2); + let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + } + + Ok(()) + } +} + impl Drop for SampleDriver { fn drop(&mut self) { dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n"); diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index c881fd6dbd08..e7ab77448f75 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -176,6 +176,8 @@ fn open(_file: &File, misc: &MiscDeviceRegistration) -> Result, _file: &File, cmd: u32, arg: usize) -> Result { dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n"); + // Treat the ioctl argument as a user pointer. + let arg = UserPtr::from_addr(arg); let size = _IOC_SIZE(cmd); match cmd { diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs index 8ea95e8c2f36..4095c72afeab 100644 --- a/samples/rust/rust_print_main.rs +++ b/samples/rust/rust_print_main.rs @@ -40,7 +40,7 @@ fn arc_print() -> Result { // behaviour, contract or protocol on both `i32` and `&str` into a single `Arc` of // type `Arc`. - use core::fmt::Display; + use kernel::fmt::Display; fn arc_dyn_print(arc: &Arc) { pr_info!("Arc says {arc}"); } diff --git a/scripts/Makefile.build b/scripts/Makefile.build index a6461ea411f7..d0ee33a487be 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -309,13 +309,15 @@ $(obj)/%.lst: $(obj)/%.c FORCE # The features in this list are the ones allowed for non-`rust/` code. # # - Stable since Rust 1.81.0: `feature(lint_reasons)`. -# - Stable since Rust 1.82.0: `feature(asm_const)`, `feature(raw_ref_op)`. +# - Stable since Rust 1.82.0: `feature(asm_const)`, +# `feature(offset_of_nested)`, `feature(raw_ref_op)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`. # - Expected to become stable: `feature(arbitrary_self_types)`. +# - To be determined: `feature(used_with_arg)`. # # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on # the unstable features in use. -rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 435ab3f0ec44..b0e1423b09c2 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins @@ -8,20 +8,6 @@ ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY endif export DISABLE_LATENT_ENTROPY_PLUGIN -gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -DSTACKLEAK_PLUGIN -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE) -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH) -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) \ - += -fplugin-arg-stackleak_plugin-verbose -ifdef CONFIG_GCC_PLUGIN_STACKLEAK - DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable -endif -export DISABLE_STACKLEAK_PLUGIN - # All the plugin CFLAGS are collected here in case a build target needs to # filter them out of the KBUILD_CFLAGS. GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) -DGCC_PLUGINS @@ -34,6 +20,8 @@ KBUILD_CFLAGS += $(GCC_PLUGINS_CFLAGS) # be included in GCC_PLUGIN so they can get built. gcc-plugin-external-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \ += randomize_layout_plugin.so +gcc-plugin-external-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ + += stackleak_plugin.so # All enabled GCC plugins are collected here for building in # scripts/gcc-scripts/Makefile. diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan index 693dbbebebba..0ba2aac3b8dc 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan @@ -86,10 +86,14 @@ kasan_params += hwasan-instrument-stack=$(stack_enable) \ hwasan-use-short-granules=0 \ hwasan-inline-all-checks=0 -# Instrument memcpy/memset/memmove calls by using instrumented __hwasan_mem*(). -ifeq ($(call clang-min-version, 150000)$(call gcc-min-version, 130000),y) - kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 -endif +# Instrument memcpy/memset/memmove calls by using instrumented __(hw)asan_mem*(). +ifdef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX + ifdef CONFIG_CC_IS_GCC + kasan_params += asan-kernel-mem-intrinsic-prefix=1 + else + kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 + endif +endif # CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX endif # CONFIG_KASAN_SW_TAGS diff --git a/scripts/Makefile.kstack_erase b/scripts/Makefile.kstack_erase new file mode 100644 index 000000000000..ee7e4ef7b892 --- /dev/null +++ b/scripts/Makefile.kstack_erase @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifdef CONFIG_GCC_PLUGIN_STACKLEAK +kstack-erase-cflags-y += -fplugin=$(objtree)/scripts/gcc-plugins/stackleak_plugin.so +kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) +kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH) +kstack-erase-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) += -fplugin-arg-stackleak_plugin-verbose +DISABLE_KSTACK_ERASE := -fplugin-arg-stackleak_plugin-disable +endif + +ifdef CONFIG_CC_IS_CLANG +kstack-erase-cflags-y += -fsanitize-coverage=stack-depth +kstack-erase-cflags-y += -fsanitize-coverage-stack-depth-callback-min=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) +DISABLE_KSTACK_ERASE := -fno-sanitize-coverage=stack-depth +endif + +KSTACK_ERASE_CFLAGS := $(kstack-erase-cflags-y) + +export KSTACK_ERASE_CFLAGS DISABLE_KSTACK_ERASE + +KBUILD_CFLAGS += $(KSTACK_ERASE_CFLAGS) diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index 20274c63e745..910fd8a9a268 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -1,4 +1,4 @@ -#!/usr/bin/gawk -f +#!/usr/bin/env -S gawk -f # SPDX-License-Identifier: GPL-2.0 # Script to check sysctl documentation against source files @@ -13,10 +13,22 @@ # Specify -vdebug=1 to see debugging information BEGIN { - if (!table) { + if (!table) { print "Please specify the table to look for using the table variable" > "/dev/stderr" exit 1 - } + } + + # Documentation title skiplist + skiplist[0] = "^Documentation for" + skiplist[1] = "Network core options$" + skiplist[2] = "POSIX message queues filesystem$" + skiplist[3] = "Configuration options" + skiplist[4] = ". /proc/sys/fs" + skiplist[5] = "^Introduction$" + skiplist[6] = "^seccomp$" + skiplist[7] = "^pty$" + skiplist[8] = "^firmware_config$" + skiplist[9] = "^random$" } # The following globals are used: @@ -31,124 +43,132 @@ BEGIN { # Remove punctuation from the given value function trimpunct(value) { - while (value ~ /^["&]/) { - value = substr(value, 2) - } - while (value ~ /[]["&,}]$/) { - value = substr(value, 1, length(value) - 1) - } - return value + while (value ~ /^["&]/) { + value = substr(value, 2) + } + while (value ~ /[]["&,}]$/) { + value = substr(value, 1, length(value) - 1) + } + return value } # Print the information for the given entry function printentry(entry) { - seen[entry]++ - printf "* %s from %s", entry, file[entry] - if (documented[entry]) { - printf " (documented)" - } - print "" + seen[entry]++ + printf "* %s from %s", entry, file[entry] + if (documented[entry]) { + printf " (documented)" + } + print "" } # Stage 1: build the list of documented entries FNR == NR && /^=+$/ { - if (prevline ~ /Documentation for/) { - # This is the main title - next - } + for (i in skiplist) { + if (prevline ~ skiplist[i]) { + next + } + } - # The previous line is a section title, parse it - $0 = prevline - if (debug) print "Parsing " $0 - inbrackets = 0 - for (i = 1; i <= NF; i++) { - if (length($i) == 0) { - continue + # The previous line is a section title, parse it + $0 = prevline + if (debug) print "Parsing " $0 + inbrackets = 0 + for (i = 1; i <= NF; i++) { + if (length($i) == 0) { + continue + } + if (!inbrackets && substr($i, 1, 1) == "(") { + inbrackets = 1 + } + if (!inbrackets) { + token = trimpunct($i) + if (length(token) > 0 && token != "and") { + if (debug) print trimpunct($i) + documented[trimpunct($i)]++ + } + } + if (inbrackets && substr($i, length($i), 1) == ")") { + inbrackets = 0 + } } - if (!inbrackets && substr($i, 1, 1) == "(") { - inbrackets = 1 - } - if (!inbrackets) { - token = trimpunct($i) - if (length(token) > 0 && token != "and") { - if (debug) print trimpunct($i) - documented[trimpunct($i)]++ - } - } - if (inbrackets && substr($i, length($i), 1) == ")") { - inbrackets = 0 - } - } } FNR == NR { - prevline = $0 - next + prevline = $0 + next } # Stage 2: process each file and find all sysctl tables BEGINFILE { - delete entries - curtable = "" - curentry = "" - delete vars - if (debug) print "Processing file " FILENAME + delete entries + curtable = "" + curentry = "" + delete vars + if (debug) print "Processing file " FILENAME } /^static( const)? struct ctl_table/ { - match($0, /static( const)? struct ctl_table ([^][]+)/, tables) - curtable = tables[2] - if (debug) print "Processing table " curtable + match($0, /static( const)? struct ctl_table ([^][]+)/, tables) + curtable = tables[2] + if (debug) print "Processing table " curtable } /^};$/ { - curtable = "" - curentry = "" - delete vars + curtable = "" + curentry = "" + delete vars } curtable && /\.procname[\t ]*=[\t ]*".+"/ { - match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) - curentry = names[1] - if (debug) print "Adding entry " curentry " to table " curtable - entries[curtable][curentry]++ - file[curentry] = FILENAME + match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME +} + +curtable && /UCOUNT_ENTRY.*/ { + match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME } /register_sysctl.*/ { - match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) - if (debug) print "Registering table " tables[3] " at " tables[2] - if (tables[2] == table) { - for (entry in entries[tables[3]]) { - printentry(entry) - } - } + match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) + if (debug) print "Registering table " tables[3] " at " tables[2] + if (tables[2] == table) { + for (entry in entries[tables[3]]) { + printentry(entry) + } + } } /kmemdup.*/ { - match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) - if (debug) print "Found variable " names[1] " for table " names[2] - if (names[2] in entries) { - vars[names[1]] = names[2] - } + match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) + if (debug) print "Found variable " names[1] " for table " names[2] + if (names[2] in entries) { + vars[names[1]] = names[2] + } } /__register_sysctl_table.*/ { - match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) - if (debug) print "Registering variable table " tables[2] " at " tables[1] - if (tables[1] == table && tables[2] in vars) { - for (entry in entries[vars[tables[2]]]) { - printentry(entry) - } - } + match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) + if (debug) print "Registering variable table " tables[2] " at " tables[1] + if (tables[1] == table && tables[2] in vars) { + for (entry in entries[vars[tables[2]]]) { + printentry(entry) + } + } } END { - for (entry in documented) { - if (!seen[entry]) { - print "No implementation for " entry + for (entry in documented) { + if (!seen[entry]) + print "No implementation for " entry } - } } diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 664f7b7a622c..e722dd6fa8ef 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -685,6 +685,9 @@ our $tracing_logging_tags = qr{(?xi: [\.\!:\s]* )}; +# Device ID types like found in include/linux/mod_devicetable.h. +our $dev_id_types = qr{\b[a-z]\w*_device_id\b}; + sub edit_distance_min { my (@arr) = @_; my $len = scalar @arr; @@ -857,8 +860,6 @@ our %deprecated_apis = ( "kunmap" => "kunmap_local", "kmap_atomic" => "kmap_local_page", "kunmap_atomic" => "kunmap_local", - "srcu_read_lock_lite" => "srcu_read_lock_fast", - "srcu_read_unlock_lite" => "srcu_read_unlock_fast", ); #Create a search pattern for all these strings to speed up a loop below @@ -3502,9 +3503,10 @@ sub process { # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { + my $rawline_utf8 = decode("utf8", $rawline); + while ($rawline_utf8 =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { my $typo = $1; - my $blank = copy_spacing($rawline); + my $blank = copy_spacing($rawline_utf8); my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); my $hereptr = "$hereline$ptr\n"; my $typo_fix = $spelling_fix{lc($typo)}; @@ -3741,6 +3743,18 @@ sub process { } } +# Check for RGMII phy-mode with delay on PCB + if ($realfile =~ /\.(dts|dtsi|dtso)$/ && + $line =~ /^\+\s*(phy-mode|phy-connection-type)\s*=\s*"/ && + !ctx_has_comment($first_line, $linenr)) { + my $prop = $1; + my $mode = get_quoted_string($line, $rawline); + if ($mode =~ /^"rgmii(?:|-rxid|-txid)"$/) { + WARN("UNCOMMENTED_RGMII_MODE", + "$prop $mode without comment -- delays on the PCB should be described, otherwise use \"rgmii-id\"\n" . $herecurr); + } + } + # check for using SPDX license tag at beginning of files if ($realline == $checklicenseline) { if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { @@ -7678,6 +7692,31 @@ sub process { WARN("DUPLICATED_SYSCTL_CONST", "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); } + +# Check that *_device_id tables have sentinel entries. + if (defined $stat && $line =~ /struct\s+$dev_id_types\s+\w+\s*\[\s*\]\s*=\s*\{/) { + my $stripped = $stat; + + # Strip diff line prefixes. + $stripped =~ s/(^|\n)./$1/g; + # Line continuations. + $stripped =~ s/\\\n/\n/g; + # Strip whitespace, empty strings, zeroes, and commas. + $stripped =~ s/""//g; + $stripped =~ s/0x0//g; + $stripped =~ s/[\s$;,0]//g; + # Strip field assignments. + $stripped =~ s/\.$Ident=//g; + + if (!(substr($stripped, -4) eq "{}};" || + substr($stripped, -6) eq "{{}}};" || + $stripped =~ /ISAPNP_DEVICE_SINGLE_END}};$/ || + $stripped =~ /ISAPNP_CARD_END}};$/ || + $stripped =~ /NULL};$/ || + $stripped =~ /PCMCIA_DEVICE_NULL};$/)) { + ERROR("MISSING_SENTINEL", "missing sentinel in ID array\n" . "$here\n$stat\n"); + } + } } # If we have no input at all, then there is nothing to report on diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py index 578c3fecfdfd..e39529e46c3d 100755 --- a/scripts/checktransupdate.py +++ b/scripts/checktransupdate.py @@ -24,6 +24,7 @@ commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs") """ import os +import re import time import logging from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction @@ -69,6 +70,38 @@ def get_origin_from_trans(origin_path, t_from_head): return o_from_t +def get_origin_from_trans_smartly(origin_path, t_from_head): + """Get the latest origin commit from the formatted translation commit: + (1) update to commit HASH (TITLE) + (2) Update the translation through commit HASH (TITLE) + """ + # catch flag for 12-bit commit hash + HASH = r'([0-9a-f]{12})' + # pattern 1: contains "update to commit HASH" + pat_update_to = re.compile(rf'update to commit {HASH}') + # pattern 2: contains "Update the translation through commit HASH" + pat_update_translation = re.compile(rf'Update the translation through commit {HASH}') + + origin_commit_hash = None + for line in t_from_head["message"]: + # check if the line matches the first pattern + match = pat_update_to.search(line) + if match: + origin_commit_hash = match.group(1) + break + # check if the line matches the second pattern + match = pat_update_translation.search(line) + if match: + origin_commit_hash = match.group(1) + break + if origin_commit_hash is None: + return None + o_from_t = get_latest_commit_from(origin_path, origin_commit_hash) + if o_from_t is not None: + logging.debug("tracked origin commit id: %s", o_from_t["hash"]) + return o_from_t + + def get_commits_count_between(opath, commit1, commit2): """Get the commits count between two commits for the specified file""" command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}" @@ -108,7 +141,10 @@ def check_per_file(file_path): logging.error("Cannot find the latest commit for %s", file_path) return - o_from_t = get_origin_from_trans(opath, t_from_head) + o_from_t = get_origin_from_trans_smartly(opath, t_from_head) + # notice, o_from_t from get_*_smartly() is always more accurate than from get_*() + if o_from_t is None: + o_from_t = get_origin_from_trans(opath, t_from_head) if o_from_t is None: logging.error("Error: Cannot find the latest origin commit for %s", file_path) diff --git a/scripts/coccinelle/misc/secs_to_jiffies.cocci b/scripts/coccinelle/misc/secs_to_jiffies.cocci index 416f348174ca..f3241ce75a7b 100644 --- a/scripts/coccinelle/misc/secs_to_jiffies.cocci +++ b/scripts/coccinelle/misc/secs_to_jiffies.cocci @@ -7,26 +7,65 @@ // Confidence: High // Copyright: (C) 2024 Easwar Hariharan, Microsoft // Keywords: secs, seconds, jiffies -// +// Options: --include-headers virtual patch +virtual report +virtual context -@depends on patch@ constant C; @@ +@pconst depends on patch@ constant C; @@ - msecs_to_jiffies(C * 1000) + secs_to_jiffies(C) -@depends on patch@ constant C; @@ +@pconstms depends on patch@ constant C; @@ - msecs_to_jiffies(C * MSEC_PER_SEC) + secs_to_jiffies(C) -@depends on patch@ expression E; @@ +@pexpr depends on patch@ expression E; @@ - msecs_to_jiffies(E * 1000) + secs_to_jiffies(E) -@depends on patch@ expression E; @@ +@pexprms depends on patch@ expression E; @@ - msecs_to_jiffies(E * MSEC_PER_SEC) + secs_to_jiffies(E) + +@r depends on report && !patch@ +constant C; +expression E; +position p; +@@ + +( + msecs_to_jiffies(C@p * 1000) +| + msecs_to_jiffies(C@p * MSEC_PER_SEC) +| + msecs_to_jiffies(E@p * 1000) +| + msecs_to_jiffies(E@p * MSEC_PER_SEC) +) + +@c depends on context && !patch@ +constant C; +expression E; +@@ + +( +* msecs_to_jiffies(C * 1000) +| +* msecs_to_jiffies(C * MSEC_PER_SEC) +| +* msecs_to_jiffies(E * 1000) +| +* msecs_to_jiffies(E * MSEC_PER_SEC) +) + +@script:python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0], "WARNING opportunity for secs_to_jiffies()") diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index e8609a03c3d8..6eb94fddc338 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -1,6 +1,7 @@ acpi_dock_ops address_space_operations backlight_ops +bin_attribute block_device_operations bus_type clk_ops diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py new file mode 100755 index 000000000000..4ac927d40cf5 --- /dev/null +++ b/scripts/crypto/gen-hash-testvecs.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script that generates test vectors for the given cryptographic hash function. +# +# Copyright 2025 Google LLC + +import hashlib +import hmac +import sys + +DATA_LENS = [0, 1, 2, 3, 16, 32, 48, 49, 63, 64, 65, 127, 128, 129, 256, 511, + 513, 1000, 3333, 4096, 4128, 4160, 4224, 16384] + +# Generate the given number of random bytes, using the length itself as the seed +# for a simple linear congruential generator (LCG). The C test code uses the +# same LCG with the same seeding strategy to reconstruct the data, ensuring +# reproducibility without explicitly storing the data in the test vectors. +def rand_bytes(length): + seed = length + out = [] + for _ in range(length): + seed = (seed * 25214903917 + 11) % 2**48 + out.append((seed >> 16) % 256) + return bytes(out) + +POLY1305_KEY_SIZE = 32 + +# A straightforward, unoptimized implementation of Poly1305. +# Reference: https://cr.yp.to/mac/poly1305-20050329.pdf +class Poly1305: + def __init__(self, key): + assert len(key) == POLY1305_KEY_SIZE + self.h = 0 + rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff + self.r = int.from_bytes(key[:16], byteorder='little') & rclamp + self.s = int.from_bytes(key[16:], byteorder='little') + + # Note: this supports partial blocks only at the end. + def update(self, data): + for i in range(0, len(data), 16): + chunk = data[i:i+16] + c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk)) + self.h = ((self.h + c) * self.r) % (2**130 - 5) + return self + + # Note: gen_additional_poly1305_testvecs() relies on this being + # nondestructive, i.e. not changing any field of self. + def digest(self): + m = (self.h + self.s) % 2**128 + return m.to_bytes(16, byteorder='little') + +def hash_init(alg): + if alg == 'poly1305': + # Use a fixed random key here, to present Poly1305 as an unkeyed hash. + # This allows all the test cases for unkeyed hashes to work on Poly1305. + return Poly1305(rand_bytes(POLY1305_KEY_SIZE)) + return hashlib.new(alg) + +def hash_update(ctx, data): + ctx.update(data) + +def hash_final(ctx): + return ctx.digest() + +def compute_hash(alg, data): + ctx = hash_init(alg) + hash_update(ctx, data) + return hash_final(ctx) + +def print_bytes(prefix, value, bytes_per_line): + for i in range(0, len(value), bytes_per_line): + line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line]) + print(f'{line.rstrip()}') + +def print_static_u8_array_definition(name, value): + print('') + print(f'static const u8 {name} = {{') + print_bytes('\t', value, 8) + print('};') + +def print_c_struct_u8_array_field(name, value): + print(f'\t\t.{name} = {{') + print_bytes('\t\t\t', value, 8) + print('\t\t},') + +def gen_unkeyed_testvecs(alg): + print('') + print('static const struct {') + print('\tsize_t data_len;') + print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];') + print('} hash_testvecs[] = {') + for data_len in DATA_LENS: + data = rand_bytes(data_len) + print('\t{') + print(f'\t\t.data_len = {data_len},') + print_c_struct_u8_array_field('digest', compute_hash(alg, data)) + print('\t},') + print('};') + + data = rand_bytes(4096) + ctx = hash_init(alg) + for data_len in range(len(data) + 1): + hash_update(ctx, compute_hash(alg, data[:data_len])) + print_static_u8_array_definition( + f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', + hash_final(ctx)) + +def gen_hmac_testvecs(alg): + ctx = hmac.new(rand_bytes(32), digestmod=alg) + data = rand_bytes(4096) + for data_len in range(len(data) + 1): + ctx.update(data[:data_len]) + key_len = data_len % 293 + key = rand_bytes(key_len) + mac = hmac.digest(key, data[:data_len], alg) + ctx.update(mac) + print_static_u8_array_definition( + f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', + ctx.digest()) + +def gen_additional_poly1305_testvecs(): + key = b'\xff' * POLY1305_KEY_SIZE + data = b'' + ctx = Poly1305(key) + for _ in range(32): + for j in range(0, 4097, 16): + ctx.update(b'\xff' * j) + data += ctx.digest() + print_static_u8_array_definition( + 'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]', + Poly1305(key).update(data).digest()) + +if len(sys.argv) != 2: + sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') + sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n') + sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') + sys.exit(1) + +alg = sys.argv[1] +print('/* SPDX-License-Identifier: GPL-2.0-or-later */') +print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') +gen_unkeyed_testvecs(alg) +if alg == 'poly1305': + gen_additional_poly1305_testvecs() +else: + gen_hmac_testvecs(alg) diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux index 8995cd304e6e..189956b5a5c8 100755 --- a/scripts/extract-vmlinux +++ b/scripts/extract-vmlinux @@ -12,13 +12,12 @@ check_vmlinux() { - # Use readelf to check if it's a valid ELF - # TODO: find a better to way to check that it's really vmlinux - # and not just an elf - readelf -h $1 > /dev/null 2>&1 || return 1 - - cat $1 - exit 0 + if file "$1" | grep -q 'Linux kernel.*boot executable' || + readelf -h "$1" > /dev/null 2>&1 + then + cat "$1" + exit 0 + fi } try_decompress() diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c index d20c47d21ad8..e486488c867d 100644 --- a/scripts/gcc-plugins/stackleak_plugin.c +++ b/scripts/gcc-plugins/stackleak_plugin.c @@ -9,7 +9,7 @@ * any of the gcc libraries * * This gcc plugin is needed for tracking the lowest border of the kernel stack. - * It instruments the kernel code inserting stackleak_track_stack() calls: + * It instruments the kernel code inserting __sanitizer_cov_stack_depth() calls: * - after alloca(); * - for the functions with a stack frame size greater than or equal * to the "track-min-size" plugin parameter. @@ -33,7 +33,7 @@ __visible int plugin_is_GPL_compatible; static int track_frame_size = -1; static bool build_for_x86 = false; -static const char track_function[] = "stackleak_track_stack"; +static const char track_function[] = "__sanitizer_cov_stack_depth"; static bool disable = false; static bool verbose = false; @@ -58,7 +58,7 @@ static void add_stack_tracking_gcall(gimple_stmt_iterator *gsi, bool after) cgraph_node_ptr node; basic_block bb; - /* Insert calling stackleak_track_stack() */ + /* Insert calling __sanitizer_cov_stack_depth() */ stmt = gimple_build_call(track_function_decl, 0); gimple_call = as_a_gcall(stmt); if (after) @@ -120,12 +120,12 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after) gcc_assert(build_for_x86); /* - * Insert calling stackleak_track_stack() in asm: - * asm volatile("call stackleak_track_stack" + * Insert calling __sanitizer_cov_stack_depth() in asm: + * asm volatile("call __sanitizer_cov_stack_depth" * :: "r" (current_stack_pointer)) * Use ASM_CALL_CONSTRAINT trick from arch/x86/include/asm/asm.h. * This constraint is taken into account during gcc shrink-wrapping - * optimization. It is needed to be sure that stackleak_track_stack() + * optimization. It is needed to be sure that __sanitizer_cov_stack_depth() * call is inserted after the prologue of the containing function, * when the stack frame is prepared. */ @@ -137,7 +137,7 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after) input = build_tree_list(NULL_TREE, build_const_char_string(2, "r")); input = chainon(NULL_TREE, build_tree_list(input, sp_decl)); vec_safe_push(inputs, input); - asm_call = gimple_build_asm_vec("call stackleak_track_stack", + asm_call = gimple_build_asm_vec("call __sanitizer_cov_stack_depth", inputs, NULL, NULL, NULL); gimple_asm_set_volatile(asm_call, true); if (after) @@ -151,11 +151,11 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after) { /* * The 'no_caller_saved_registers' attribute is used for - * stackleak_track_stack(). If the compiler supports this attribute for - * the target arch, we can add calling stackleak_track_stack() in asm. + * __sanitizer_cov_stack_depth(). If the compiler supports this attribute for + * the target arch, we can add calling __sanitizer_cov_stack_depth() in asm. * That improves performance: we avoid useless operations with the * caller-saved registers in the functions from which we will remove - * stackleak_track_stack() call during the stackleak_cleanup pass. + * __sanitizer_cov_stack_depth() call during the stackleak_cleanup pass. */ if (lookup_attribute_spec(get_identifier("no_caller_saved_registers"))) add_stack_tracking_gasm(gsi, after); @@ -165,7 +165,7 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after) /* * Work with the GIMPLE representation of the code. Insert the - * stackleak_track_stack() call after alloca() and into the beginning + * __sanitizer_cov_stack_depth() call after alloca() and into the beginning * of the function if it is not instrumented. */ static unsigned int stackleak_instrument_execute(void) @@ -205,7 +205,7 @@ static unsigned int stackleak_instrument_execute(void) DECL_NAME_POINTER(current_function_decl)); } - /* Insert stackleak_track_stack() call after alloca() */ + /* Insert __sanitizer_cov_stack_depth() call after alloca() */ add_stack_tracking(&gsi, true); if (bb == entry_bb) prologue_instrumented = true; @@ -241,7 +241,7 @@ static unsigned int stackleak_instrument_execute(void) return 0; } - /* Insert stackleak_track_stack() call at the function beginning */ + /* Insert __sanitizer_cov_stack_depth() call at the function beginning */ bb = entry_bb; if (!single_pred_p(bb)) { /* gcc_assert(bb_loop_depth(bb) || @@ -270,15 +270,15 @@ static void remove_stack_tracking_gcall(void) rtx_insn *insn, *next; /* - * Find stackleak_track_stack() calls. Loop through the chain of insns, + * Find __sanitizer_cov_stack_depth() calls. Loop through the chain of insns, * which is an RTL representation of the code for a function. * * The example of a matching insn: - * (call_insn 8 4 10 2 (call (mem (symbol_ref ("stackleak_track_stack") - * [flags 0x41] ) - * [0 stackleak_track_stack S1 A8]) (0)) 675 {*call} (expr_list - * (symbol_ref ("stackleak_track_stack") [flags 0x41] ) (expr_list (0) (nil))) (nil)) + * (call_insn 8 4 10 2 (call (mem (symbol_ref ("__sanitizer_cov_stack_depth") + * [flags 0x41] ) + * [0 __sanitizer_cov_stack_depth S1 A8]) (0)) 675 {*call} (expr_list + * (symbol_ref ("__sanitizer_cov_stack_depth") [flags 0x41] ) (expr_list (0) (nil))) (nil)) */ for (insn = get_insns(); insn; insn = next) { rtx body; @@ -318,7 +318,7 @@ static void remove_stack_tracking_gcall(void) if (SYMBOL_REF_DECL(body) != track_function_decl) continue; - /* Delete the stackleak_track_stack() call */ + /* Delete the __sanitizer_cov_stack_depth() call */ delete_insn_and_edges(insn); #if BUILDING_GCC_VERSION < 8000 if (GET_CODE(next) == NOTE && @@ -340,12 +340,12 @@ static bool remove_stack_tracking_gasm(void) gcc_assert(build_for_x86); /* - * Find stackleak_track_stack() asm calls. Loop through the chain of + * Find __sanitizer_cov_stack_depth() asm calls. Loop through the chain of * insns, which is an RTL representation of the code for a function. * * The example of a matching insn: * (insn 11 5 12 2 (parallel [ (asm_operands/v - * ("call stackleak_track_stack") ("") 0 + * ("call __sanitizer_cov_stack_depth") ("") 0 * [ (reg/v:DI 7 sp [ current_stack_pointer ]) ] * [ (asm_input:DI ("r")) ] []) * (clobber (reg:CC 17 flags)) ]) -1 (nil)) @@ -375,7 +375,7 @@ static bool remove_stack_tracking_gasm(void) continue; if (strcmp(ASM_OPERANDS_TEMPLATE(body), - "call stackleak_track_stack")) { + "call __sanitizer_cov_stack_depth")) { continue; } @@ -389,7 +389,7 @@ static bool remove_stack_tracking_gasm(void) /* * Work with the RTL representation of the code. - * Remove the unneeded stackleak_track_stack() calls from the functions + * Remove the unneeded __sanitizer_cov_stack_depth() calls from the functions * which don't call alloca() and don't have a large enough stack frame size. */ static unsigned int stackleak_cleanup_execute(void) @@ -474,13 +474,13 @@ static bool stackleak_gate(void) return track_frame_size >= 0; } -/* Build the function declaration for stackleak_track_stack() */ +/* Build the function declaration for __sanitizer_cov_stack_depth() */ static void stackleak_start_unit(void *gcc_data __unused, void *user_data __unused) { tree fntype; - /* void stackleak_track_stack(void) */ + /* void __sanitizer_cov_stack_depth(void) */ fntype = build_function_type_list(void_type_node, NULL_TREE); track_function_decl = build_fn_decl(track_function, fntype); DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */ diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in index fd6bd69c5096..c3886739a028 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -73,12 +74,12 @@ if IS_BUILTIN(CONFIG_MODULES): LX_GDBPARSED(MOD_RO_AFTER_INIT) /* linux/mount.h */ -LX_VALUE(MNT_NOSUID) -LX_VALUE(MNT_NODEV) -LX_VALUE(MNT_NOEXEC) -LX_VALUE(MNT_NOATIME) -LX_VALUE(MNT_NODIRATIME) -LX_VALUE(MNT_RELATIME) +LX_GDBPARSED(MNT_NOSUID) +LX_GDBPARSED(MNT_NODEV) +LX_GDBPARSED(MNT_NOEXEC) +LX_GDBPARSED(MNT_NOATIME) +LX_GDBPARSED(MNT_NODIRATIME) +LX_GDBPARSED(MNT_RELATIME) /* linux/threads.h */ LX_VALUE(NR_CPUS) @@ -93,6 +94,12 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE) LX_GDBPARSED(RADIX_TREE_MAP_SHIFT) LX_GDBPARSED(RADIX_TREE_MAP_MASK) +/* linux/maple_tree.h */ +LX_VALUE(MAPLE_NODE_SLOTS) +LX_VALUE(MAPLE_RANGE64_SLOTS) +LX_VALUE(MAPLE_ARANGE64_SLOTS) +LX_GDBPARSED(MAPLE_NODE_MASK) + /* linux/vmalloc.h */ LX_VALUE(VM_IOREMAP) LX_VALUE(VM_ALLOC) diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py index 616a5f26377a..f4f715a8f0e3 100644 --- a/scripts/gdb/linux/interrupts.py +++ b/scripts/gdb/linux/interrupts.py @@ -7,7 +7,7 @@ import gdb from linux import constants from linux import cpus from linux import utils -from linux import radixtree +from linux import mapletree irq_desc_type = utils.CachedType("struct irq_desc") @@ -23,12 +23,12 @@ def irqd_is_level(desc): def show_irq_desc(prec, irq): text = "" - desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq) + desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq) if desc is None: return text - desc = desc.cast(irq_desc_type.get_type()) - if desc is None: + desc = desc.cast(irq_desc_type.get_type().pointer()) + if desc == 0: return text if irq_settings_is_hidden(desc): @@ -110,7 +110,7 @@ def x86_show_mce(prec, var, pfx, desc): pvar = gdb.parse_and_eval(var) text = "%*s: " % (prec, pfx) for cpu in cpus.each_online_cpu(): - text += "%10u " % (cpus.per_cpu(pvar, cpu)) + text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference()) text += " %s\n" % (desc) return text @@ -142,7 +142,7 @@ def x86_show_interupts(prec): if constants.LX_CONFIG_X86_MCE: text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions") - text == x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") + text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") text += show_irq_err_count(prec) @@ -221,8 +221,8 @@ class LxInterruptList(gdb.Command): gdb.write("CPU%-8d" % cpu) gdb.write("\n") - if utils.gdb_eval_or_none("&irq_desc_tree") is None: - return + if utils.gdb_eval_or_none("&sparse_irqs") is None: + raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?") for irq in range(nr_irqs): gdb.write(show_irq_desc(prec, irq)) diff --git a/scripts/gdb/linux/mapletree.py b/scripts/gdb/linux/mapletree.py new file mode 100644 index 000000000000..d52d51c0a03f --- /dev/null +++ b/scripts/gdb/linux/mapletree.py @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Maple tree helpers +# +# Copyright (c) 2025 Broadcom +# +# Authors: +# Florian Fainelli + +import gdb + +from linux import utils +from linux import constants +from linux import xarray + +maple_tree_root_type = utils.CachedType("struct maple_tree") +maple_node_type = utils.CachedType("struct maple_node") +maple_enode_type = utils.CachedType("void") + +maple_dense = 0 +maple_leaf_64 = 1 +maple_range_64 = 2 +maple_arange_64 = 3 + +class Mas(object): + ma_active = 0 + ma_start = 1 + ma_root = 2 + ma_none = 3 + ma_pause = 4 + ma_overflow = 5 + ma_underflow = 6 + ma_error = 7 + + def __init__(self, mt, first, end): + if mt.type == maple_tree_root_type.get_type().pointer(): + self.tree = mt.dereference() + elif mt.type != maple_tree_root_type.get_type(): + raise gdb.GdbError("must be {} not {}" + .format(maple_tree_root_type.get_type().pointer(), mt.type)) + self.tree = mt + self.index = first + self.last = end + self.node = None + self.status = self.ma_start + self.min = 0 + self.max = -1 + + def is_start(self): + # mas_is_start() + return self.status == self.ma_start + + def is_ptr(self): + # mas_is_ptr() + return self.status == self.ma_root + + def is_none(self): + # mas_is_none() + return self.status == self.ma_none + + def root(self): + # mas_root() + return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer()) + + def start(self): + # mas_start() + if self.is_start() is False: + return None + + self.min = 0 + self.max = ~0 + + while True: + self.depth = 0 + root = self.root() + if xarray.xa_is_node(root): + self.depth = 0 + self.status = self.ma_active + self.node = mte_safe_root(root) + self.offset = 0 + if mte_dead_node(self.node) is True: + continue + + return None + + self.node = None + # Empty tree + if root is None: + self.status = self.ma_none + self.offset = constants.LX_MAPLE_NODE_SLOTS + return None + + # Single entry tree + self.status = self.ma_root + self.offset = constants.LX_MAPLE_NODE_SLOTS + + if self.index != 0: + return None + + return root + + return None + + def reset(self): + # mas_reset() + self.status = self.ma_start + self.node = None + +def mte_safe_root(node): + if node.type != maple_enode_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type)) + ulong_type = utils.get_ulong_type() + indirect_ptr = node.cast(ulong_type) & ~0x2 + val = indirect_ptr.cast(maple_enode_type.get_type().pointer()) + return val + +def mte_node_type(entry): + ulong_type = utils.get_ulong_type() + val = None + if entry.type == maple_enode_type.get_type().pointer(): + val = entry.cast(ulong_type) + elif entry.type == ulong_type: + val = entry + else: + raise gdb.GdbError("{} must be {} not {}" + .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type)) + return (val >> 0x3) & 0xf + +def ma_dead_node(node): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type)) + ulong_type = utils.get_ulong_type() + parent = node['parent'] + indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK + return indirect_ptr == node + +def mte_to_node(enode): + ulong_type = utils.get_ulong_type() + if enode.type == maple_enode_type.get_type().pointer(): + indirect_ptr = enode.cast(ulong_type) + elif enode.type == ulong_type: + indirect_ptr = enode + else: + raise gdb.GdbError("{} must be {} not {}" + .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) + indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK + return indirect_ptr.cast(maple_node_type.get_type().pointer()) + +def mte_dead_node(enode): + if enode.type != maple_enode_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) + node = mte_to_node(enode) + return ma_dead_node(node) + +def ma_is_leaf(tp): + result = tp < maple_range_64 + return tp < maple_range_64 + +def mt_pivots(t): + if t == maple_dense: + return 0 + elif t == maple_leaf_64 or t == maple_range_64: + return constants.LX_MAPLE_RANGE64_SLOTS - 1 + elif t == maple_arange_64: + return constants.LX_MAPLE_ARANGE64_SLOTS - 1 + +def ma_pivots(node, t): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{}: must be {} not {}" + .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type)) + if t == maple_arange_64: + return node['ma64']['pivot'] + elif t == maple_leaf_64 or t == maple_range_64: + return node['mr64']['pivot'] + else: + return None + +def ma_slots(node, tp): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{}: must be {} not {}" + .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type)) + if tp == maple_arange_64: + return node['ma64']['slot'] + elif tp == maple_range_64 or tp == maple_leaf_64: + return node['mr64']['slot'] + elif tp == maple_dense: + return node['slot'] + else: + return None + +def mt_slot(mt, slots, offset): + ulong_type = utils.get_ulong_type() + return slots[offset].cast(ulong_type) + +def mtree_lookup_walk(mas): + ulong_type = utils.get_ulong_type() + n = mas.node + + while True: + node = mte_to_node(n) + tp = mte_node_type(n) + pivots = ma_pivots(node, tp) + end = mt_pivots(tp) + offset = 0 + while True: + if pivots[offset] >= mas.index: + break + if offset >= end: + break + offset += 1 + + slots = ma_slots(node, tp) + n = mt_slot(mas.tree, slots, offset) + if ma_dead_node(node) is True: + mas.reset() + return None + break + + if ma_is_leaf(tp) is True: + break + + return n + +def mtree_load(mt, index): + ulong_type = utils.get_ulong_type() + # MT_STATE(...) + mas = Mas(mt, index, index) + entry = None + + while True: + entry = mas.start() + if mas.is_none(): + return None + + if mas.is_ptr(): + if index != 0: + entry = None + return entry + + entry = mtree_lookup_walk(mas) + if entry is None and mas.is_start(): + continue + else: + break + + if xarray.xa_is_zero(entry): + return None + + return entry diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 2332bd8eddf1..6edb99221675 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -84,6 +84,30 @@ def get_kerneloffset(): return None +def is_in_s390_decompressor(): + # DAT is always off in decompressor. Use this as an indicator. + # Note that in the kernel, DAT can be off during kexec() or restart. + # Accept this imprecision in order to avoid complicating things. + # It is unlikely that someone will run lx-symbols at these points. + pswm = int(gdb.parse_and_eval("$pswm")) + return (pswm & 0x0400000000000000) == 0 + + +def skip_decompressor(): + if utils.is_target_arch("s390"): + if is_in_s390_decompressor(): + # The address of the jump_to_kernel function is statically placed + # into svc_old_psw.addr (see ipl_data.c); read it from there. DAT + # is off, so we do not need to care about lowcore relocation. + svc_old_pswa = 0x148 + jump_to_kernel = int(gdb.parse_and_eval("*(unsigned long long *)" + + hex(svc_old_pswa))) + gdb.execute("tbreak *" + hex(jump_to_kernel)) + gdb.execute("continue") + while is_in_s390_decompressor(): + gdb.execute("stepi") + + class LxSymbols(gdb.Command): """(Re-)load symbols of Linux kernel and currently loaded modules. @@ -204,6 +228,8 @@ lx-symbols command.""" saved_state['breakpoint'].enabled = saved_state['enabled'] def invoke(self, arg, from_tty): + skip_decompressor() + self.module_paths = [os.path.abspath(os.path.expanduser(p)) for p in arg.split()] self.module_paths.append(os.getcwd()) diff --git a/scripts/gdb/linux/vfs.py b/scripts/gdb/linux/vfs.py index c77b9ce75f6d..9e921b645a68 100644 --- a/scripts/gdb/linux/vfs.py +++ b/scripts/gdb/linux/vfs.py @@ -22,7 +22,7 @@ def dentry_name(d): if parent == d or parent == 0: return "" p = dentry_name(d['d_parent']) + "/" - return p + d['d_iname'].string() + return p + d['d_name']['name'].string() class DentryName(gdb.Function): """Return string of the full path of a dentry. diff --git a/scripts/gdb/linux/xarray.py b/scripts/gdb/linux/xarray.py new file mode 100644 index 000000000000..f4477b5def75 --- /dev/null +++ b/scripts/gdb/linux/xarray.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Xarray helpers +# +# Copyright (c) 2025 Broadcom +# +# Authors: +# Florian Fainelli + +import gdb + +from linux import utils +from linux import constants + +def xa_is_internal(entry): + ulong_type = utils.get_ulong_type() + return ((entry.cast(ulong_type) & 3) == 2) + +def xa_mk_internal(v): + return ((v << 2) | 2) + +def xa_is_zero(entry): + ulong_type = utils.get_ulong_type() + return entry.cast(ulong_type) == xa_mk_internal(257) + +def xa_is_node(entry): + ulong_type = utils.get_ulong_type() + return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096) diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c index c9c19b86a686..1c640db93db3 100644 --- a/scripts/gendwarfksyms/cache.c +++ b/scripts/gendwarfksyms/cache.c @@ -15,7 +15,7 @@ void cache_set(struct cache *cache, unsigned long key, int value) { struct cache_item *ci; - ci = xmalloc(sizeof(struct cache_item)); + ci = xmalloc(sizeof(*ci)); ci->key = key; ci->value = value; hash_add(cache->cache, &ci->hash, hash_32(key)); diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c index 6183bbbe7b54..052f7a3f975a 100644 --- a/scripts/gendwarfksyms/die.c +++ b/scripts/gendwarfksyms/die.c @@ -33,7 +33,7 @@ static struct die *create_die(Dwarf_Die *die, enum die_state state) { struct die *cd; - cd = xmalloc(sizeof(struct die)); + cd = xmalloc(sizeof(*cd)); init_die(cd); cd->addr = (uintptr_t)die->addr; @@ -123,7 +123,7 @@ static struct die_fragment *append_item(struct die *cd) { struct die_fragment *df; - df = xmalloc(sizeof(struct die_fragment)); + df = xmalloc(sizeof(*df)); df->type = FRAGMENT_EMPTY; list_add_tail(&df->list, &cd->fragments); return df; diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c index 13ea7bf1ae7d..3538a7d9cb07 100644 --- a/scripts/gendwarfksyms/dwarf.c +++ b/scripts/gendwarfksyms/dwarf.c @@ -634,7 +634,7 @@ static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, * Note that the user of this feature is responsible for ensuring * that the structure actually remains ABI compatible. */ - memset(&state.kabi, 0, sizeof(struct kabi_state)); + memset(&state.kabi, 0, sizeof(state.kabi)); res = checkp(process_die_container(&state, NULL, die, check_union_member_kabi_status, diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index b3ade713778f..e3c2a3ccf51a 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -228,7 +228,7 @@ void kabi_read_rules(int fd) if (type == KABI_RULE_TYPE_UNKNOWN) error("unsupported kABI rule type: '%s'", field); - rule = xmalloc(sizeof(struct rule)); + rule = xmalloc(sizeof(*rule)); rule->type = type; rule->target = xstrdup(get_rule_field(&rule_str, &left)); diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c index 327f87389c34..35ed594f0749 100644 --- a/scripts/gendwarfksyms/symbols.c +++ b/scripts/gendwarfksyms/symbols.c @@ -146,7 +146,7 @@ void symbol_read_exports(FILE *file) continue; } - sym = xcalloc(1, sizeof(struct symbol)); + sym = xcalloc(1, sizeof(*sym)); sym->name = name; sym->addr.section = SHN_UNDEF; sym->state = SYMBOL_UNPROCESSED; diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 7bd459ea6c59..9c3b053bf061 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -6,6 +6,8 @@ #define _GNU_SOURCE #include #include +#include +#include #include #include "gendwarfksyms.h" @@ -43,7 +45,7 @@ static int type_list_append(struct list_head *list, const char *s, void *owned) if (!s) return 0; - entry = xmalloc(sizeof(struct type_list_entry)); + entry = xmalloc(sizeof(*entry)); entry->str = s; entry->owned = owned; list_add_tail(&entry->list, list); @@ -120,7 +122,7 @@ static struct type_expansion *type_map_add(const char *name, struct type_expansion *e; if (__type_map_get(name, &e)) { - e = xmalloc(sizeof(struct type_expansion)); + e = xmalloc(sizeof(*e)); type_expansion_init(e); e->name = xstrdup(name); @@ -179,20 +181,41 @@ static int type_map_get(const char *name, struct type_expansion **res) return -1; } +static int cmp_expansion_name(const void *p1, const void *p2) +{ + struct type_expansion *const *e1 = p1; + struct type_expansion *const *e2 = p2; + + return strcmp((*e1)->name, (*e2)->name); +} + static void type_map_write(FILE *file) { struct type_expansion *e; struct hlist_node *tmp; + struct type_expansion **es; + size_t count = 0; + size_t i = 0; if (!file) return; - hash_for_each_safe(type_map, e, tmp, hash) { - checkp(fputs(e->name, file)); + hash_for_each_safe(type_map, e, tmp, hash) + ++count; + es = xmalloc(count * sizeof(*es)); + hash_for_each_safe(type_map, e, tmp, hash) + es[i++] = e; + + qsort(es, count, sizeof(*es), cmp_expansion_name); + + for (i = 0; i < count; ++i) { + checkp(fputs(es[i]->name, file)); checkp(fputs(" ", file)); - type_list_write(&e->expanded, file); + type_list_write(&es[i]->expanded, file); checkp(fputs("\n", file)); } + + free(es); } static void type_map_free(void) diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 39c82908ff3a..38b3416bb979 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -225,7 +225,11 @@ fn main() { ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); ts.push("supported-sanitizers", ["kcfi", "kernel-address"]); - ts.push("target-pointer-width", "64"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 64); + } else { + ts.push("target-pointer-width", "64"); + } } else if cfg.has("X86_32") { // This only works on UML, as i386 otherwise needs regparm support in rustc if !cfg.has("UML") { @@ -245,7 +249,11 @@ fn main() { } ts.push("features", features); ts.push("llvm-target", "i386-unknown-linux-gnu"); - ts.push("target-pointer-width", "32"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 32); + } else { + ts.push("target-pointer-width", "32"); + } } else if cfg.has("LOONGARCH") { panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target"); } else { diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8abe57041955..a7b44cd8ae14 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -594,7 +594,7 @@ static void check_conf(struct menu *menu) default: if (!conf_cnt++) printf("*\n* Restart config...\n*\n"); - rootEntry = menu_get_parent_menu(menu); + rootEntry = menu_get_menu_or_parent_menu(menu); conf(rootEntry); break; } diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index ac95661a1c9d..9599a0408862 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -77,7 +77,7 @@ static bool is_same(const char *file1, const char *file2) if (map2 == MAP_FAILED) goto close2; - if (bcmp(map1, map2, st1.st_size)) + if (memcmp(map1, map2, st1.st_size)) goto close2; ret = true; diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh index fc954c0538fa..856c692f480c 100755 --- a/scripts/kconfig/gconf-cfg.sh +++ b/scripts/kconfig/gconf-cfg.sh @@ -6,7 +6,7 @@ set -eu cflags=$1 libs=$2 -PKG="gtk+-2.0 gmodule-2.0 libglade-2.0" +PKG=gtk+-3.0 if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then echo >&2 "*" @@ -18,18 +18,11 @@ fi if ! ${HOSTPKG_CONFIG} --exists $PKG; then echo >&2 "*" echo >&2 "* Unable to find the GTK+ installation. Please make sure that" - echo >&2 "* the GTK+ 2.0 development package is correctly installed." + echo >&2 "* the GTK 3 development package is correctly installed." echo >&2 "* You need $PKG" echo >&2 "*" exit 1 fi -if ! ${HOSTPKG_CONFIG} --atleast-version=2.0.0 gtk+-2.0; then - echo >&2 "*" - echo >&2 "* GTK+ is present but version >= 2.0.0 is required." - echo >&2 "*" - exit 1 -fi - ${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags} ${HOSTPKG_CONFIG} --libs ${PKG} > ${libs} diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c0f46f189060..8b164ccfa008 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -7,10 +7,7 @@ #include "lkc.h" #include "images.h" -#include #include -#include -#include #include #include @@ -18,7 +15,7 @@ #include #include -enum { +enum view_mode { SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW }; @@ -30,29 +27,24 @@ static gint view_mode = FULL_VIEW; static gboolean show_name = TRUE; static gboolean show_range = TRUE; static gboolean show_value = TRUE; -static gboolean resizeable = FALSE; static int opt_mode = OPT_NORMAL; -GtkWidget *main_wnd = NULL; -GtkWidget *tree1_w = NULL; // left frame -GtkWidget *tree2_w = NULL; // right frame -GtkWidget *text_w = NULL; -GtkWidget *hpaned = NULL; -GtkWidget *vpaned = NULL; -GtkWidget *back_btn = NULL; -GtkWidget *save_btn = NULL; -GtkWidget *save_menu_item = NULL; +static GtkWidget *main_wnd; +static GtkWidget *tree1_w; // left frame +static GtkWidget *tree2_w; // right frame +static GtkWidget *text_w; +static GtkWidget *hpaned; +static GtkWidget *vpaned; +static GtkWidget *back_btn, *save_btn, *single_btn, *split_btn, *full_btn; +static GtkWidget *save_menu_item; -GtkTextTag *tag1, *tag2; -GdkColor color; +static GtkTextTag *tag1, *tag2; -GtkTreeStore *tree1, *tree2, *tree; -GtkTreeModel *model1, *model2; -static GtkTreeIter *parents[256]; -static gint indent; +static GtkTreeStore *tree1, *tree2; +static GdkPixbuf *pix_menu; -static struct menu *current; // current node for SINGLE view -static struct menu *browsed; // browsed node for SPLIT view +static struct menu *browsed; // browsed menu for SINGLE/SPLIT view +static struct menu *selected; // selected entry enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, @@ -61,28 +53,8 @@ enum { COL_NUMBER }; -static void display_list(void); -static void display_tree(struct menu *menu); -static void display_tree_part(void); -static void update_tree(struct menu *src, GtkTreeIter * dst); - -static void replace_button_icon(GladeXML *xml, GdkDrawable *window, - GtkStyle *style, gchar *btn_name, gchar **xpm) -{ - GdkPixmap *pixmap; - GdkBitmap *mask; - GtkToolButton *button; - GtkWidget *image; - - pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, - &style->bg[GTK_STATE_NORMAL], - xpm); - - button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - image = gtk_image_new_from_pixmap(pixmap, mask); - gtk_widget_show(image); - gtk_tool_button_set_icon_widget(button, image); -} +static void display_tree(GtkTreeStore *store, struct menu *menu); +static void recreate_tree(void); static void conf_changed(bool dirty) { @@ -90,273 +62,12 @@ static void conf_changed(bool dirty) gtk_widget_set_sensitive(save_menu_item, dirty); } -/* Main Window Initialization */ -static void init_main_window(const gchar *glade_file) -{ - GladeXML *xml; - GtkWidget *widget; - GtkTextBuffer *txtbuf; - GtkStyle *style; - - xml = glade_xml_new(glade_file, "window1", NULL); - if (!xml) - g_error("GUI loading failed !\n"); - glade_xml_signal_autoconnect(xml); - - main_wnd = glade_xml_get_widget(xml, "window1"); - hpaned = glade_xml_get_widget(xml, "hpaned1"); - vpaned = glade_xml_get_widget(xml, "vpaned1"); - tree1_w = glade_xml_get_widget(xml, "treeview1"); - tree2_w = glade_xml_get_widget(xml, "treeview2"); - text_w = glade_xml_get_widget(xml, "textview3"); - - back_btn = glade_xml_get_widget(xml, "button1"); - gtk_widget_set_sensitive(back_btn, FALSE); - - widget = glade_xml_get_widget(xml, "show_name1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_name); - - widget = glade_xml_get_widget(xml, "show_range1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_range); - - widget = glade_xml_get_widget(xml, "show_data1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_value); - - save_btn = glade_xml_get_widget(xml, "button3"); - save_menu_item = glade_xml_get_widget(xml, "save1"); - conf_set_changed_callback(conf_changed); - - style = gtk_widget_get_style(main_wnd); - widget = glade_xml_get_widget(xml, "toolbar1"); - - replace_button_icon(xml, main_wnd->window, style, - "button4", (gchar **) xpm_single_view); - replace_button_icon(xml, main_wnd->window, style, - "button5", (gchar **) xpm_split_view); - replace_button_icon(xml, main_wnd->window, style, - "button6", (gchar **) xpm_tree_view); - - txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", - "foreground", "red", - "weight", PANGO_WEIGHT_BOLD, - NULL); - tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", - /*"style", PANGO_STYLE_OBLIQUE, */ - NULL); - - gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); - - gtk_widget_show(main_wnd); -} - -static void init_tree_model(void) -{ - gint i; - - tree = tree2 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model2 = GTK_TREE_MODEL(tree2); - - for (parents[0] = NULL, i = 1; i < 256; i++) - parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); - - tree1 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model1 = GTK_TREE_MODEL(tree1); -} - -static void init_left_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree1_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - - gtk_tree_view_set_model(view, model1); - gtk_tree_view_set_headers_visible(view, TRUE); - gtk_tree_view_set_rules_hint(view, TRUE); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); - gtk_widget_realize(tree1_w); -} - -static void renderer_edited(GtkCellRendererText * cell, - const gchar * path_string, - const gchar * new_text, gpointer user_data); - -static void init_right_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree2_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - gint i; - - gtk_tree_view_set_model(view, model2); - gtk_tree_view_set_headers_visible(view, TRUE); - gtk_tree_view_set_rules_hint(view, TRUE); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "pixbuf", COL_PIXBUF, - "visible", COL_PIXVIS, NULL); - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Name", renderer, - "text", COL_NAME, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "N", renderer, - "text", COL_NO, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "M", renderer, - "text", COL_MOD, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Y", renderer, - "text", COL_YES, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Value", renderer, - "text", COL_VALUE, - "editable", - COL_EDIT, - "foreground-gdk", - COL_COLOR, NULL); - g_signal_connect(G_OBJECT(renderer), "edited", - G_CALLBACK(renderer_edited), NULL); - - column = gtk_tree_view_get_column(view, COL_NAME); - gtk_tree_view_column_set_visible(column, show_name); - column = gtk_tree_view_get_column(view, COL_NO); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_MOD); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_YES); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_VALUE); - gtk_tree_view_column_set_visible(column, show_value); - - if (resizeable) { - for (i = 0; i < COL_VALUE; i++) { - column = gtk_tree_view_get_column(view, i); - gtk_tree_view_column_set_resizable(column, TRUE); - } - } - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); -} - - /* Utility Functions */ - -static void text_insert_help(struct menu *menu) +static void text_insert_msg(const char *title, const char *msg) { GtkTextBuffer *buffer; GtkTextIter start, end; - const char *prompt = menu_get_prompt(menu); - struct gstr help = str_new(); - - menu_get_ext_help(menu, &help); - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - gtk_text_buffer_get_bounds(buffer, &start, &end); - gtk_text_buffer_delete(buffer, &start, &end); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); - - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, - NULL); - gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2, - NULL); - str_free(&help); -} - - -static void text_insert_msg(const char *title, const char *message) -{ - GtkTextBuffer *buffer; - GtkTextIter start, end; - const char *msg = message; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); @@ -372,183 +83,352 @@ static void text_insert_msg(const char *title, const char *message) NULL); } - -/* Main Windows Callbacks */ - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); -gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, - gpointer user_data) +static void text_insert_help(struct menu *menu) { - GtkWidget *dialog, *label; - gint result; + struct gstr help = str_new(); - if (!conf_get_changed()) - return FALSE; + menu_get_ext_help(menu, &help); + text_insert_msg(menu_get_prompt(menu), str_get(&help)); + str_free(&help); +} - dialog = gtk_dialog_new_with_buttons("Warning !", - GTK_WINDOW(main_wnd), - (GtkDialogFlags) - (GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_OK, - GTK_RESPONSE_YES, - GTK_STOCK_NO, - GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), - GTK_RESPONSE_CANCEL); +static void _select_menu(GtkTreeView *view, GtkTreeModel *model, + GtkTreeIter *parent, struct menu *match) +{ + GtkTreeIter iter; + gboolean valid; - label = gtk_label_new("\nSave configuration ?\n"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); - gtk_widget_show(label); + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; - result = gtk_dialog_run(GTK_DIALOG(dialog)); - switch (result) { - case GTK_RESPONSE_YES: - on_save_activate(NULL, NULL); - return FALSE; - case GTK_RESPONSE_NO: - return FALSE; - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_DELETE_EVENT: - default: - gtk_widget_destroy(dialog); - return TRUE; + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu == match) { + GtkTreeSelection *selection; + GtkTreePath *path; + + /* + * Expand parents to reflect the selection, and + * scroll down to it. + */ + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_to_path(view, path); + gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE, + 0.5, 0.0); + gtk_tree_path_free(path); + + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_select_iter(selection, &iter); + + text_insert_help(menu); + } + + _select_menu(view, model, &iter, match); + + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +static void select_menu(GtkTreeView *view, struct menu *match) +{ + _select_menu(view, gtk_tree_view_get_model(view), NULL, match); +} + +static void _update_row_visibility(GtkTreeView *view) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(view)); + + gtk_tree_model_filter_refilter(filter); +} + +static void update_row_visibility(void) +{ + if (view_mode == SPLIT_VIEW) + _update_row_visibility(GTK_TREE_VIEW(tree1_w)); + _update_row_visibility(GTK_TREE_VIEW(tree2_w)); +} + +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate val; + gchar *option; + const gchar *_no = ""; + const gchar *_mod = ""; + const gchar *_yes = ""; + const gchar *value = ""; + GdkRGBA color; + gboolean editable = FALSE; + gboolean btnvis = FALSE; + + option = g_strdup_printf("%s %s %s %s", + menu->type == M_COMMENT ? "***" : "", + menu_get_prompt(menu), + menu->type == M_COMMENT ? "***" : "", + sym && !sym_has_value(sym) ? "(NEW)" : ""); + + gdk_rgba_parse(&color, menu_is_visible(menu) ? "Black" : "DarkGray"); + + if (!sym) + goto set; + + sym_calc_value(sym); + + if (menu->type == M_CHOICE) { // parse children to get a final value + struct symbol *def_sym = sym_calc_choice(menu); + struct menu *def_menu = NULL; + + for (struct menu *child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + value = menu_get_prompt(def_menu); + + goto set; } - return FALSE; + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + + btnvis = TRUE; + + val = sym_get_tristate_value(sym); + switch (val) { + case no: + _no = "N"; + value = "N"; + break; + case mod: + _mod = "M"; + value = "M"; + break; + case yes: + _yes = "Y"; + value = "Y"; + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + _no = "_"; + if (val != mod && sym_tristate_within_range(sym, mod)) + _mod = "_"; + if (val != yes && sym_tristate_within_range(sym, yes)) + _yes = "_"; + break; + default: + value = sym_get_string_value(sym); + editable = TRUE; + break; + } + +set: + gtk_tree_store_set(tree, node, + COL_OPTION, option, + COL_NAME, sym ? sym->name : "", + COL_NO, _no, + COL_MOD, _mod, + COL_YES, _yes, + COL_VALUE, value, + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, editable, + COL_PIXBUF, pix_menu, + COL_PIXVIS, view_mode == SINGLE_VIEW && menu->type == M_MENU, + COL_BTNVIS, btnvis, + COL_BTNACT, _yes[0] == 'Y', + COL_BTNINC, _mod[0] == 'M', + COL_BTNRAD, sym && sym_is_choice_value(sym), + -1); + + g_free(option); } - -void on_window1_destroy(GtkObject * object, gpointer user_data) +static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) { - gtk_main_quit(); + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu) + set_node(store, &iter, menu); + + _update_tree(store, &iter); + + valid = gtk_tree_model_iter_next(model, &iter); + } } - -void -on_window1_size_request(GtkWidget * widget, - GtkRequisition * requisition, gpointer user_data) +static void update_tree(GtkTreeStore *store) { - static gint old_h; - gint w, h; - - if (widget->window == NULL) - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - else - gdk_window_get_size(widget->window, &w, &h); - - if (h == old_h) - return; - old_h = h; - - gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); + _update_tree(store, NULL); + update_row_visibility(); } +static void update_trees(void) +{ + if (view_mode == SPLIT_VIEW) + update_tree(tree1); + update_tree(tree2); +} + +static void set_view_mode(enum view_mode mode) +{ + view_mode = mode; + + if (mode == SPLIT_VIEW) { // two panes + gint w; + + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + } else { + gtk_widget_hide(tree1_w); + gtk_paned_set_position(GTK_PANED(hpaned), 0); + } + + gtk_widget_set_sensitive(single_btn, TRUE); + gtk_widget_set_sensitive(split_btn, TRUE); + gtk_widget_set_sensitive(full_btn, TRUE); + + switch (mode) { + case SINGLE_VIEW: + if (selected) + browsed = menu_get_parent_menu(selected) ?: &rootmenu; + else + browsed = &rootmenu; + recreate_tree(); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); + gtk_widget_set_sensitive(single_btn, FALSE); + break; + case SPLIT_VIEW: + browsed = selected; + while (browsed && !(browsed->flags & MENU_ROOT)) + browsed = browsed->parent; + gtk_tree_store_clear(tree1); + display_tree(tree1, &rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); + gtk_tree_store_clear(tree2); + if (browsed) + display_tree(tree2, browsed); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree1_w), browsed); + select_menu(GTK_TREE_VIEW(tree2_w), selected); + gtk_widget_set_sensitive(split_btn, FALSE); + break; + case FULL_VIEW: + gtk_tree_store_clear(tree2); + display_tree(tree2, &rootmenu); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); + gtk_widget_set_sensitive(full_btn, FALSE); + break; + } + + gtk_widget_set_sensitive(back_btn, + mode == SINGLE_VIEW && browsed != &rootmenu); +} /* Menu & Toolbar Callbacks */ - -static void -load_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Load file...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_read(fn)) - text_insert_msg("Error", "Unable to load configuration !"); - else - display_tree_part(); + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); + + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_read(filename)) + text_insert_msg("Error", + "Unable to load configuration!"); + else + update_trees(); + + g_free(filename); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); } -void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) -{ - GtkWidget *fs; - - fs = gtk_file_selection_new("Load file..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(load_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); -} - - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) { if (conf_write(NULL)) text_insert_msg("Error", "Unable to save configuration !"); conf_write_autoconf(0); } - -static void -store_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Save file as...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_write(fn)) - text_insert_msg("Error", "Unable to save configuration !"); + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); - gtk_widget_destroy(GTK_WIDGET(user_data)); + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_write(filename)) + text_insert_msg("Error", + "Unable to save configuration !"); + + g_free(filename); + } + + gtk_widget_destroy(dialog); } -void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) -{ - GtkWidget *fs; - - fs = gtk_file_selection_new("Save file as..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(store_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); -} - - -void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) -{ - if (!on_window1_delete_event(NULL, NULL, NULL)) - gtk_widget_destroy(GTK_WIDGET(main_wnd)); -} - - -void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_name = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); if (col) gtk_tree_view_column_set_visible(col, show_name); } - -void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_range = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); if (col) gtk_tree_view_column_set_visible(col, show_range); @@ -561,46 +441,38 @@ void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) } - -void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); if (col) gtk_tree_view_column_set_visible(col, show_value); } - -void -on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode1_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_NORMAL; - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } - -void -on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode2_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_ALL; - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } - -void -on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode3_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_PROMPT; - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } - -void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *intro_text = @@ -621,14 +493,11 @@ void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", intro_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } - -void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *about_text = @@ -638,15 +507,16 @@ void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, "%s", about_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + GTK_BUTTONS_CLOSE, "%s\nGTK version: %d.%d.%d", + about_text, + gtk_get_major_version(), + gtk_get_minor_version(), + gtk_get_micro_version()); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } - -void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *license_text = @@ -658,81 +528,127 @@ void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", license_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } - -void on_back_clicked(GtkButton * button, gpointer user_data) +/* toolbar handlers */ +static void on_back_clicked(GtkButton *button, gpointer user_data) { - enum prop_type ptype; + browsed = menu_get_parent_menu(browsed) ?: &rootmenu; - current = current->parent; - ptype = current->prompt ? current->prompt->type : P_UNKNOWN; - if (ptype != P_MENU) - current = current->parent; - display_tree_part(); + recreate_tree(); - if (current == &rootmenu) + if (browsed == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); } - -void on_load_clicked(GtkButton * button, gpointer user_data) +static void on_load_clicked(GtkButton *button, gpointer user_data) { on_load1_activate(NULL, user_data); } - -void on_single_clicked(GtkButton * button, gpointer user_data) +static void on_save_clicked(GtkButton *button, gpointer user_data) { - view_mode = SINGLE_VIEW; - gtk_widget_hide(tree1_w); - current = &rootmenu; - display_tree_part(); + on_save_activate(NULL, user_data); } - -void on_split_clicked(GtkButton * button, gpointer user_data) +static void on_single_clicked(GtkButton *button, gpointer user_data) { - gint w, h; - view_mode = SPLIT_VIEW; - gtk_widget_show(tree1_w); - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - gtk_paned_set_position(GTK_PANED(hpaned), w / 2); - if (tree2) - gtk_tree_store_clear(tree2); - display_list(); - - /* Disable back btn, like in full mode. */ - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(SINGLE_VIEW); } - -void on_full_clicked(GtkButton * button, gpointer user_data) +static void on_split_clicked(GtkButton *button, gpointer user_data) { - view_mode = FULL_VIEW; - gtk_widget_hide(tree1_w); - if (tree2) - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(SPLIT_VIEW); } +static void on_full_clicked(GtkButton *button, gpointer user_data) +{ + set_view_mode(FULL_VIEW); +} -void on_collapse_clicked(GtkButton * button, gpointer user_data) +static void on_collapse_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); } - -void on_expand_clicked(GtkButton * button, gpointer user_data) +static void on_expand_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } +/* Main Windows Callbacks */ + +static void on_window1_destroy(GtkWidget *widget, gpointer user_data) +{ + gtk_main_quit(); +} + +static gboolean on_window1_configure(GtkWidget *self, + GdkEventConfigure *event, + gpointer user_data) +{ + gtk_paned_set_position(GTK_PANED(vpaned), 2 * event->height / 3); + return FALSE; +} + +static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ + GtkWidget *dialog, *label, *content_area; + gint result; + gint ret = FALSE; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons("Warning !", + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + "_OK", + GTK_RESPONSE_YES, + "_No", + GTK_RESPONSE_NO, + "_Cancel", + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new("\nSave configuration ?\n"); + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(content_area), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + break; + case GTK_RESPONSE_NO: + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + ret = TRUE; + break; + } + + gtk_widget_destroy(dialog); + + if (!ret) + g_object_unref(pix_menu); + + return ret; +} + +static void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} /* CTree Callbacks */ @@ -741,25 +657,28 @@ static void renderer_edited(GtkCellRendererText * cell, const gchar * path_string, const gchar * new_text, gpointer user_data) { + GtkTreeView *view = GTK_TREE_VIEW(user_data); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path = gtk_tree_path_new_from_string(path_string); GtkTreeIter iter; const char *old_def, *new_def; struct menu *menu; struct symbol *sym; - if (!gtk_tree_model_get_iter(model2, &iter, path)) - return; + if (!gtk_tree_model_get_iter(model, &iter, path)) + goto free; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); sym = menu->sym; - gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + gtk_tree_model_get(model, &iter, COL_VALUE, &old_def, -1); new_def = new_text; sym_set_string_value(sym, new_def); - update_tree(&rootmenu, NULL); + update_trees(); +free: gtk_tree_path_free(path); } @@ -787,14 +706,7 @@ static void change_sym_value(struct menu *menu, gint col) if (!sym_tristate_within_range(sym, newval)) newval = yes; sym_set_tristate_value(sym, newval); - if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); - else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); break; case S_INT: case S_HEX: @@ -810,14 +722,7 @@ static void toggle_sym_value(struct menu *menu) return; sym_toggle_tristate_value(menu->sym); - if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); - else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); } static gint column2index(GtkTreeViewColumn * column) @@ -837,43 +742,39 @@ static gint column2index(GtkTreeViewColumn * column) /* User click: update choice (full) or goes down (single) */ -gboolean -on_treeview2_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview2_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; - -#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); -#else - gtk_tree_view_get_cursor(view, &path, &column); -#endif + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, NULL, NULL); if (path == NULL) return FALSE; - if (!gtk_tree_model_get_iter(model2, &iter, path)) + if (!gtk_tree_model_get_iter(model, &iter, path)) return FALSE; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + selected = menu; col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { enum prop_type ptype; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) { // goes down into menu - current = menu; - display_tree_part(); + browsed = menu; + recreate_tree(); gtk_widget_set_sensitive(back_btn, TRUE); } else if (col == COL_OPTION) { toggle_sym_value(menu); @@ -894,35 +795,31 @@ on_treeview2_button_press_event(GtkWidget * widget, } /* Key pressed: update choice */ -gboolean -on_treeview2_key_press_event(GtkWidget * widget, - GdkEventKey * event, gpointer user_data) +static gboolean on_treeview2_key_press_event(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; - gtk_tree_view_get_cursor(view, &path, &column); + gtk_tree_view_get_cursor(view, &path, NULL); if (path == NULL) return FALSE; - if (event->keyval == GDK_space) { + if (event->keyval == GDK_KEY_space) { if (gtk_tree_view_row_expanded(view, path)) gtk_tree_view_collapse_row(view, path); else gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } - if (event->keyval == GDK_KP_Enter) { - } - if (widget == tree1_w) - return FALSE; - gtk_tree_model_get_iter(model2, &iter, path); - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (!strcasecmp(event->string, "n")) col = COL_NO; @@ -939,369 +836,74 @@ on_treeview2_key_press_event(GtkWidget * widget, /* Row selection changed: update help */ -void -on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +static void on_treeview2_cursor_changed(GtkTreeView *treeview, + gpointer user_data) { + GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeSelection *selection; GtkTreeIter iter; struct menu *menu; selection = gtk_tree_view_get_selection(treeview); - if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) { + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); text_insert_help(menu); } } /* User click: display sub-tree in the right frame. */ -gboolean -on_treeview1_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview1_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; - gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, NULL, NULL, NULL); if (path == NULL) return FALSE; - gtk_tree_model_get_iter(model1, &iter, path); - gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); - if (event->type == GDK_2BUTTON_PRESS) { + if (event->type == GDK_2BUTTON_PRESS) toggle_sym_value(menu); - current = menu; - display_tree_part(); - } else { + + selected = menu; + + if (menu->type == M_MENU) { browsed = menu; - display_tree_part(); + recreate_tree(); } - gtk_widget_realize(tree2_w); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); return FALSE; } - -/* Fill a row of strings */ -static gchar **fill_row(struct menu *menu) -{ - static gchar *row[COL_NUMBER]; - struct symbol *sym = menu->sym; - const char *def; - int stype; - tristate val; - enum prop_type ptype; - int i; - - for (i = COL_OPTION; i <= COL_COLOR; i++) - g_free(row[i]); - bzero(row, sizeof(row)); - - ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - - row[COL_OPTION] = - g_strdup_printf("%s %s %s %s", - ptype == P_COMMENT ? "***" : "", - menu_get_prompt(menu), - ptype == P_COMMENT ? "***" : "", - sym && !sym_has_value(sym) ? "(NEW)" : ""); - - if (opt_mode == OPT_ALL && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else if (opt_mode == OPT_PROMPT && - menu_has_prompt(menu) && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else - row[COL_COLOR] = g_strdup("Black"); - - switch (ptype) { - case P_MENU: - row[COL_PIXBUF] = (gchar *) xpm_menu; - if (view_mode == SINGLE_VIEW) - row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - case P_COMMENT: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - default: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); - break; - } - - if (!sym) - return row; - row[COL_NAME] = g_strdup(sym->name); - - sym_calc_value(sym); - menu->flags &= ~MENU_CHANGED; - - if (sym_is_choice(sym)) { // parse childs for getting final value - struct menu *child; - struct symbol *def_sym = sym_calc_choice(menu); - struct menu *def_menu = NULL; - - for (child = menu->list; child; child = child->next) { - if (menu_is_visible(child) - && child->sym == def_sym) - def_menu = child; - } - - if (def_menu) - row[COL_VALUE] = - g_strdup(menu_get_prompt(def_menu)); - - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - return row; - } - if (sym_is_choice_value(sym)) - row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); - - stype = sym_get_type(sym); - switch (stype) { - case S_BOOLEAN: - case S_TRISTATE: - val = sym_get_tristate_value(sym); - switch (val) { - case no: - row[COL_NO] = g_strdup("N"); - row[COL_VALUE] = g_strdup("N"); - row[COL_BTNACT] = GINT_TO_POINTER(FALSE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - case mod: - row[COL_MOD] = g_strdup("M"); - row[COL_VALUE] = g_strdup("M"); - row[COL_BTNINC] = GINT_TO_POINTER(TRUE); - break; - case yes: - row[COL_YES] = g_strdup("Y"); - row[COL_VALUE] = g_strdup("Y"); - row[COL_BTNACT] = GINT_TO_POINTER(TRUE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - } - - if (val != no && sym_tristate_within_range(sym, no)) - row[COL_NO] = g_strdup("_"); - if (val != mod && sym_tristate_within_range(sym, mod)) - row[COL_MOD] = g_strdup("_"); - if (val != yes && sym_tristate_within_range(sym, yes)) - row[COL_YES] = g_strdup("_"); - break; - case S_INT: - case S_HEX: - case S_STRING: - def = sym_get_string_value(sym); - row[COL_VALUE] = g_strdup(def); - row[COL_EDIT] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - } - - return row; -} - - -/* Set the node content with a row of strings */ -static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) -{ - GdkColor color; - gboolean success; - GdkPixbuf *pix; - - pix = gdk_pixbuf_new_from_xpm_data((const char **) - row[COL_PIXBUF]); - - gdk_color_parse(row[COL_COLOR], &color); - gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, - FALSE, FALSE, &success); - - gtk_tree_store_set(tree, node, - COL_OPTION, row[COL_OPTION], - COL_NAME, row[COL_NAME], - COL_NO, row[COL_NO], - COL_MOD, row[COL_MOD], - COL_YES, row[COL_YES], - COL_VALUE, row[COL_VALUE], - COL_MENU, (gpointer) menu, - COL_COLOR, &color, - COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), - COL_PIXBUF, pix, - COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), - COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), - COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), - COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), - COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), - -1); - - g_object_unref(pix); -} - - -/* Add a node to the tree */ -static void place_node(struct menu *menu, char **row) -{ - GtkTreeIter *parent = parents[indent - 1]; - GtkTreeIter *node = parents[indent]; - - gtk_tree_store_append(tree, node, parent); - set_node(node, menu, row); -} - - -/* Find a node in the GTK+ tree */ -static GtkTreeIter found; - -/* - * Find a menu in the GtkTree starting at parent. - */ -static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent, - struct menu *tofind) -{ - GtkTreeIter iter; - GtkTreeIter *child = &iter; - gboolean valid; - GtkTreeIter *ret; - - valid = gtk_tree_model_iter_children(model2, child, parent); - while (valid) { - struct menu *menu; - - gtk_tree_model_get(model2, child, 6, &menu, -1); - - if (menu == tofind) { - memcpy(&found, child, sizeof(GtkTreeIter)); - return &found; - } - - ret = gtktree_iter_find_node(child, tofind); - if (ret) - return ret; - - valid = gtk_tree_model_iter_next(model2, child); - } - - return NULL; -} - - -/* - * Update the tree by adding/removing entries - * Does not change other nodes - */ -static void update_tree(struct menu *src, GtkTreeIter * dst) -{ - struct menu *child1; - GtkTreeIter iter, tmp; - GtkTreeIter *child2 = &iter; - gboolean valid; - GtkTreeIter *sibling; - struct symbol *sym; - struct menu *menu1, *menu2; - - if (src == &rootmenu) - indent = 1; - - valid = gtk_tree_model_iter_children(model2, child2, dst); - for (child1 = src->list; child1; child1 = child1->next) { - - sym = child1->sym; - - reparse: - menu1 = child1; - if (valid) - gtk_tree_model_get(model2, child2, COL_MENU, - &menu2, -1); - else - menu2 = NULL; // force adding of a first child - - if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) || - (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) || - (opt_mode == OPT_ALL && !menu_get_prompt(child1))) { - - /* remove node */ - if (gtktree_iter_find_node(dst, menu1) != NULL) { - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; /* next parent */ - else - goto reparse; /* next child */ - } else - continue; - } - - if (menu1 != menu2) { - if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node - if (!valid && !menu2) - sibling = NULL; - else - sibling = child2; - gtk_tree_store_insert_before(tree2, - child2, - dst, sibling); - set_node(child2, menu1, fill_row(menu1)); - if (menu2 == NULL) - valid = TRUE; - } else { // remove node - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; // next parent - else - goto reparse; // next child - } - } else if (sym && (child1->flags & MENU_CHANGED)) { - set_node(child2, menu1, fill_row(menu1)); - } - - indent++; - update_tree(child1, child2); - indent--; - - valid = gtk_tree_model_iter_next(model2, child2); - } -} - - /* Display the whole tree (single/split/full view) */ -static void display_tree(struct menu *menu) +static void _display_tree(GtkTreeStore *tree, struct menu *menu, + GtkTreeIter *parent) { - struct property *prop; struct menu *child; - enum prop_type ptype; - - if (menu == &rootmenu) { - indent = 1; - current = &rootmenu; - } + GtkTreeIter iter; for (child = menu->list; child; child = child->next) { - prop = child->prompt; - ptype = prop ? prop->type : P_UNKNOWN; - - menu->flags &= ~MENU_CHANGED; + /* + * REVISIT: + * menu_finalize() creates empty "if" entries. + * Do not confuse gtk_tree_model_get(), which would otherwise + * return "if" menu entry. + */ + if (child->type == M_IF) + continue; if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) @@ -1311,58 +913,25 @@ static void display_tree(struct menu *menu) && (tree == tree2)) continue; - if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || - (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || - (opt_mode == OPT_ALL && menu_get_prompt(child))) - place_node(child, fill_row(child)); + gtk_tree_store_append(tree, &iter, parent); + set_node(tree, &iter, child); - if ((view_mode != FULL_VIEW) && (ptype == P_MENU) - && (tree == tree2)) - continue; -/* - if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW))*/ - - /* Change paned position if the view is not in 'split mode' */ - if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) { - gtk_paned_set_position(GTK_PANED(hpaned), 0); - } - - if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW)) { - indent++; - display_tree(child); - indent--; - } + if (view_mode != SINGLE_VIEW || child->type != M_MENU) + _display_tree(tree, child, &iter); } } -/* Display a part of the tree starting at current node (single/split view) */ -static void display_tree_part(void) +static void display_tree(GtkTreeStore *store, struct menu *menu) { - if (tree2) - gtk_tree_store_clear(tree2); - if (view_mode == SINGLE_VIEW) - display_tree(current); - else if (view_mode == SPLIT_VIEW) - display_tree(browsed); - else if (view_mode == FULL_VIEW) - display_tree(&rootmenu); - gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); + _display_tree(store, menu, NULL); } -/* Display the list in the left frame (split view) */ -static void display_list(void) +/* Recreate the tree store starting at 'browsed' node */ +static void recreate_tree(void) { - if (tree1) - gtk_tree_store_clear(tree1); - - tree = tree1; - display_tree(&rootmenu); - gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); - tree = tree2; + gtk_tree_store_clear(tree2); + display_tree(tree2, browsed); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } static void fixup_rootmenu(struct menu *menu) @@ -1381,6 +950,335 @@ static void fixup_rootmenu(struct menu *menu) } } +/* Main Window Initialization */ +static void replace_button_icon(GtkWidget *widget, const char * const xpm[]) +{ + GdkPixbuf *pixbuf; + GtkWidget *image; + + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(widget), image); +} + +static void init_main_window(const gchar *glade_file) +{ + GtkBuilder *builder; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + + builder = gtk_builder_new_from_file(glade_file); + if (!builder) + g_error("GUI loading failed !\n"); + + main_wnd = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); + g_signal_connect(main_wnd, "destroy", + G_CALLBACK(on_window1_destroy), NULL); + g_signal_connect(main_wnd, "configure-event", + G_CALLBACK(on_window1_configure), NULL); + g_signal_connect(main_wnd, "delete-event", + G_CALLBACK(on_window1_delete_event), NULL); + + hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1")); + vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1")); + tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1")); + g_signal_connect(tree1_w, "cursor-changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree1_w, "button-press-event", + G_CALLBACK(on_treeview1_button_press_event), NULL); + g_signal_connect(tree1_w, "key-press-event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + + tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2")); + g_signal_connect(tree2_w, "cursor-changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree2_w, "button-press-event", + G_CALLBACK(on_treeview2_button_press_event), NULL); + g_signal_connect(tree2_w, "key-press-event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + + text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3")); + + /* menubar */ + widget = GTK_WIDGET(gtk_builder_get_object(builder, "load1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_load1_activate), NULL); + + save_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "save1")); + g_signal_connect(save_menu_item, "activate", + G_CALLBACK(on_save_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "save_as1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_save_as1_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "quit1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_quit1_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_name1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_name1_activate), NULL); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_range1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_range1_activate), NULL); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_data1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_data1_activate), NULL); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode1_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode2")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode2_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode3")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode3_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "introduction1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_introduction1_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "about1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_about1_activate), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "license1")); + g_signal_connect(widget, "activate", + G_CALLBACK(on_license1_activate), NULL); + + /* toolbar */ + back_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button1")); + g_signal_connect(back_btn, "clicked", + G_CALLBACK(on_back_clicked), NULL); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button2")); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_load_clicked), NULL); + + save_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button3")); + g_signal_connect(save_btn, "clicked", + G_CALLBACK(on_save_clicked), NULL); + + single_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button4")); + g_signal_connect(single_btn, "clicked", + G_CALLBACK(on_single_clicked), NULL); + replace_button_icon(single_btn, xpm_single_view); + + split_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button5")); + g_signal_connect(split_btn, "clicked", + G_CALLBACK(on_split_clicked), NULL); + replace_button_icon(split_btn, xpm_split_view); + + full_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button6")); + g_signal_connect(full_btn, "clicked", + G_CALLBACK(on_full_clicked), NULL); + replace_button_icon(full_btn, xpm_tree_view); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button7")); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_collapse_clicked), NULL); + + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button8")); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_expand_clicked), NULL); + + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); + + gtk_widget_show_all(main_wnd); + + g_object_unref(builder); + + conf_set_changed_callback(conf_changed); +} + +static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter, + gpointer data) +{ + struct menu *menu; + + gtk_tree_model_get(model, iter, COL_MENU, &menu, -1); + + if (!menu) + return FALSE; + + return menu_is_visible(menu) || opt_mode == OPT_ALL || + (opt_mode == OPT_PROMPT && menu_has_prompt(menu)); +} + +static void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + GtkTreeModel *filter; + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_RGBA, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-rgba", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + +static void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + GtkTreeModel *filter; + gint i; + + tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_RGBA, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-rgba", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Name", renderer, + "text", COL_NAME, + "foreground-rgba", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-rgba", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-rgba", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-rgba", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Value", renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-rgba", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), tree2_w); + + pix_menu = gdk_pixbuf_new_from_xpm_data((const char **)xpm_menu); + + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} /* Main */ int main(int ac, char *av[]) @@ -1390,18 +1288,16 @@ int main(int ac, char *av[]) gchar *glade_file; /* GTK stuffs */ - gtk_set_locale(); gtk_init(&ac, &av); - glade_init(); /* Determine GUI path */ env = getenv(SRCTREE); if (env) - glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); + glade_file = g_strconcat(env, "/scripts/kconfig/gconf.ui", NULL); else if (av[0][0] == '/') - glade_file = g_strconcat(av[0], ".glade", NULL); + glade_file = g_strconcat(av[0], ".ui", NULL); else - glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".ui", NULL); /* Conf stuffs */ if (ac > 1 && av[1][0] == '-') { @@ -1426,23 +1322,12 @@ int main(int ac, char *av[]) /* Load the interface and connect signals */ init_main_window(glade_file); - init_tree_model(); init_left_tree(); init_right_tree(); conf_read(NULL); - switch (view_mode) { - case SINGLE_VIEW: - display_tree_part(); - break; - case SPLIT_VIEW: - display_list(); - break; - case FULL_VIEW: - display_tree(&rootmenu); - break; - } + set_view_mode(view_mode); gtk_main(); diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.ui similarity index 57% rename from scripts/kconfig/gconf.glade rename to scripts/kconfig/gconf.ui index aa483cb32755..ab4431255fa7 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.ui @@ -1,8 +1,8 @@ - + - + True Gtk Kernel Configurator GTK_WINDOW_TOPLEVEL @@ -17,295 +17,196 @@ False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST - - - - + + vertical True False 0 - + True - + True _File True - - + + - + True - Load a config file + Load a config file _Load True - - - - - True - gtk-open - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + True - Save the config in .config + Save the config in .config _Save True - - - - - True - gtk-save - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + True - Save the config in a file + Save the config in a file Save _as True - - - - - True - gtk-save-as - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + True - + - + True _Quit True - - - - - True - gtk-quit - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + - + - + True _Options True - - + + - + True - Show name + Show name Show _name True False - - + - + True - Show range (Y/M/N) + Show range (Y/M/N) Show _range True False - - + - + True - Show value of the option + Show value of the option Show _data True False - - + - + True - + - + True - Show normal options + Show normal options Show normal options True True - - + - + True - Show all options + Show all options Show all _options True False set_option_mode1 - - + - + True - Show all options with prompts + Show all options with prompts Show all prompt options True False set_option_mode1 - - + - + - + - + True _Help True - - + + - + True _Introduction True - - - - - True - gtk-dialog-question - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + True _About True - - - - - True - gtk-properties - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + True _License True - - - - - True - gtk-justify-fill - 1 - 0.5 - 0.5 - 0 - 0 - - - + - + - + - + 0 False @@ -314,32 +215,23 @@ - - True - GTK_SHADOW_OUT - GTK_POS_LEFT - GTK_POS_TOP - - - + True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH - True True - + True - Goes up of one level (single view) + Goes up one level (single view) Back True gtk-undo True True False - - + False True @@ -347,18 +239,18 @@ - + True True True False - + True - + - + False False @@ -366,17 +258,16 @@ - + True - Load a config file + Load a config file Load True gtk-open True True False - - + False True @@ -384,17 +275,16 @@ - + True - Save a config file + Save a config file Save True gtk-save True True False - - + False True @@ -402,18 +292,18 @@ - + True True True False - + True - + - + False False @@ -421,17 +311,16 @@ - + True - Single view + Single view Single True gtk-missing-image True True False - - + False True @@ -439,17 +328,16 @@ - + True - Split view + Split view Split True gtk-missing-image True True False - - + False True @@ -457,17 +345,16 @@ - + True - Full view + Full view Full True gtk-missing-image True True False - - + False True @@ -475,18 +362,18 @@ - + True True True False - + True - + - + False False @@ -494,17 +381,16 @@ - + True - Collapse the whole tree in the right frame + Collapse the whole tree in the right frame Collapse True gtk-remove True True False - - + False True @@ -512,25 +398,22 @@ - + True - Expand the whole tree in the right frame + Expand the whole tree in the right frame Expand True gtk-add True True False - - + False True - - - + 0 False @@ -539,14 +422,13 @@ - + 1 True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -554,19 +436,16 @@ GTK_CORNER_TOP_LEFT - + True True True False False False - - - - + - + True False @@ -574,13 +453,13 @@ - + + vertical True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -588,7 +467,7 @@ GTK_CORNER_TOP_LEFT - + True True True @@ -596,12 +475,9 @@ False False False - - - - + - + True False @@ -609,7 +485,7 @@ - + True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC @@ -617,7 +493,7 @@ GTK_CORNER_TOP_LEFT - + True True False @@ -632,30 +508,29 @@ 0 0 0 - Sorry, no help available for this option yet. - + - + True True - + True True - + 0 True True - + - + - + diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index fbc907f75eac..56548efc14d7 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -98,9 +98,11 @@ bool menu_is_visible(struct menu *menu); bool menu_has_prompt(const struct menu *menu); const char *menu_get_prompt(const struct menu *menu); struct menu *menu_get_parent_menu(struct menu *menu); +struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); +void menu_dump(void); /* symbol.c */ void sym_clear_all_valid(void); diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c index 3c6e24b20f5b..5e4a131724f2 100644 --- a/scripts/kconfig/lxdialog/inputbox.c +++ b/scripts/kconfig/lxdialog/inputbox.c @@ -39,8 +39,10 @@ int dialog_inputbox(const char *title, const char *prompt, int height, int width if (!init) instr[0] = '\0'; - else - strcpy(instr, init); + else { + strncpy(instr, init, sizeof(dialog_input_result) - 1); + instr[sizeof(dialog_input_result) - 1] = '\0'; + } do_resize: if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGHT_MIN)) diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c index 6e6244df0c56..d4c19b7beebb 100644 --- a/scripts/kconfig/lxdialog/menubox.c +++ b/scripts/kconfig/lxdialog/menubox.c @@ -264,7 +264,7 @@ int dialog_menu(const char *title, const char *prompt, if (key < 256 && isalpha(key)) key = tolower(key); - if (strchr("ynmh", key)) + if (strchr("ynmh ", key)) i = max_choice; else { for (i = choice + 1; i < max_choice; i++) { diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c index 964139c87fcb..b34000beb294 100644 --- a/scripts/kconfig/lxdialog/util.c +++ b/scripts/kconfig/lxdialog/util.c @@ -345,8 +345,7 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) int prompt_len, room, wlen; char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0; - strcpy(tempstr, prompt); - + snprintf(tempstr, sizeof(tempstr), "%s", prompt); prompt_len = strlen(tempstr); if (prompt_len <= width - x * 2) { /* If prompt is short */ diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 7d48a692bd27..0f1a6513987c 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -575,7 +575,27 @@ const char *menu_get_prompt(const struct menu *menu) return NULL; } +/** + * menu_get_parent_menu - return the parent menu or NULL + * @menu: pointer to the menu + * return: the parent menu, or NULL if there is no parent. + */ struct menu *menu_get_parent_menu(struct menu *menu) +{ + for (menu = menu->parent; menu; menu = menu->parent) + if (menu->type == M_MENU) + return menu; + + return NULL; +} + +/** + * menu_get_menu_or_parent_menu - return the parent menu or the menu itself + * @menu: pointer to the menu + * return: the parent menu. If the given argument is already a menu, return + * itself. + */ +struct menu *menu_get_menu_or_parent_menu(struct menu *menu) { enum prop_type type; @@ -768,3 +788,77 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help) if (sym) get_symbol_str(help, sym, NULL); } + +/** + * menu_dump - dump all menu entries in a tree-like format + */ +void menu_dump(void) +{ + struct menu *menu = &rootmenu; + unsigned long long bits = 0; + int indent = 0; + + while (menu) { + + for (int i = indent - 1; i >= 0; i--) { + if (bits & (1ULL << i)) { + if (i > 0) + printf("| "); + else + printf("|-- "); + } else { + if (i > 0) + printf(" "); + else + printf("`-- "); + } + } + + switch (menu->type) { + case M_CHOICE: + printf("choice \"%s\"\n", menu->prompt->text); + break; + case M_COMMENT: + printf("comment \"%s\"\n", menu->prompt->text); + break; + case M_IF: + printf("if\n"); + break; + case M_MENU: + printf("menu \"%s\"", menu->prompt->text); + if (!menu->sym) { + printf("\n"); + break; + } + printf(" + "); + /* fallthrough */ + case M_NORMAL: + printf("symbol %s\n", menu->sym->name); + break; + } + if (menu->list) { + bits <<= 1; + menu = menu->list; + if (menu->next) + bits |= 1; + else + bits &= ~1; + indent++; + continue; + } + + while (menu && !menu->next) { + menu = menu->parent; + bits >>= 1; + indent--; + } + + if (menu) { + menu = menu->next; + if (menu->next) + bits |= 1; + else + bits &= ~1; + } + } +} diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index c0b2dabf6c89..ae1fe5f60327 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -593,6 +593,8 @@ static void item_add_str(const char *fmt, ...) tmp_str, sizeof(k_menu_items[index].str)); + k_menu_items[index].str[sizeof(k_menu_items[index].str) - 1] = '\0'; + free_item(curses_menu_items[index]); curses_menu_items[index] = new_item( k_menu_items[index].str, diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 4bfdf8ac2a9a..7206437e784a 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -359,6 +359,7 @@ int dialog_inputbox(WINDOW *main_window, x = (columns-win_cols)/2; strncpy(result, init, *result_len); + result[*result_len - 1] = '\0'; /* create the windows */ win = newwin(win_lines, win_cols, y, x); diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index eaa465b0ccf9..f8992db1870a 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -37,6 +37,12 @@ QAction *ConfigMainWindow::saveAction; ConfigSettings::ConfigSettings() : QSettings("kernel.org", "qconf") { + beginGroup("/kconfig/qconf"); +} + +ConfigSettings::~ConfigSettings() +{ + endGroup(); } /** @@ -92,7 +98,6 @@ void ConfigItem::updateMenu(void) { ConfigList* list; struct symbol* sym; - struct property *prop; QString prompt; int type; tristate expr; @@ -105,11 +110,10 @@ void ConfigItem::updateMenu(void) } sym = menu->sym; - prop = menu->prompt; prompt = menu_get_prompt(menu); - if (prop) switch (prop->type) { - case P_MENU: + switch (menu->type) { + case M_MENU: if (list->mode == singleMode) { /* a menuconfig entry is displayed differently * depending whether it's at the view root or a child. @@ -123,10 +127,16 @@ void ConfigItem::updateMenu(void) setIcon(promptColIdx, QIcon()); } goto set_prompt; - case P_COMMENT: + case M_COMMENT: setIcon(promptColIdx, QIcon()); prompt = "*** " + prompt + " ***"; goto set_prompt; + case M_CHOICE: + setIcon(promptColIdx, QIcon()); + sym = sym_calc_choice(menu); + if (sym) + setText(dataColIdx, sym->name); + goto set_prompt; default: ; } @@ -188,7 +198,11 @@ void ConfigItem::testUpdateMenu(void) if (!menu) return; - sym_calc_value(menu->sym); + if (menu->type == M_CHOICE) + sym_calc_choice(menu); + else + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { /* the menu entry changed, so update all list items */ menu->flags &= ~MENU_CHANGED; @@ -478,7 +492,7 @@ void ConfigList::updateListAllForAll() while (it.hasNext()) { ConfigList *list = it.next(); - list->updateList(); + list->updateListAll(); } } @@ -569,7 +583,7 @@ void ConfigList::setParentMenu(void) oldroot = rootEntry; if (rootEntry == &rootmenu) return; - setRootMenu(menu_get_parent_menu(rootEntry->parent)); + setRootMenu(menu_get_menu_or_parent_menu(rootEntry->parent)); QTreeWidgetItemIterator it(this); while (*it) { @@ -1532,7 +1546,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) switch (configList->mode) { case singleMode: list = configList; - parent = menu_get_parent_menu(menu); + parent = menu_get_menu_or_parent_menu(menu); if (!parent) return; list->setRootMenu(parent); @@ -1543,7 +1557,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) configList->clearSelection(); list = configList; } else { - parent = menu_get_parent_menu(menu->parent); + parent = menu_get_menu_or_parent_menu(menu->parent); if (!parent) return; @@ -1821,7 +1835,6 @@ int main(int ac, char** av) configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); - configSettings->beginGroup("/kconfig/qconf"); v = new ConfigMainWindow(); //zconfdump(stdout); @@ -1829,7 +1842,6 @@ int main(int ac, char** av) v->show(); configApp->exec(); - configSettings->endGroup(); delete configSettings; delete v; delete configApp; diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 62ab3286d04f..ab4e51f12914 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -24,6 +24,7 @@ class ConfigMainWindow; class ConfigSettings : public QSettings { public: ConfigSettings(); + ~ConfigSettings(void); QList readSizes(const QString& key, bool *ok); bool writeSizes(const QString& key, const QList& value); }; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index d57f8cbba291..26ab10c0fd76 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -195,6 +195,10 @@ static void sym_set_changed(struct symbol *sym) list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; + + menu = sym_get_choice_menu(sym); + if (menu) + menu->flags |= MENU_CHANGED; } static void sym_set_all_changed(void) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 12ae66f40bd7..fc3d46ef519f 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -271,6 +271,16 @@ def main(): logger.addHandler(handler) + python_ver = sys.version_info[:2] + if python_ver < (3,6): + logger.warning("Python 3.6 or later is required by kernel-doc") + + # Return 0 here to avoid breaking compilation + sys.exit(0) + + if python_ver < (3,7): + logger.warning("Python 3.7 or later is required for correct results") + if args.man: out_style = ManFormat(modulename=args.modulename) elif args.none: diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 9be4a64df71d..9e09b45b02fa 100644 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -275,8 +275,8 @@ class KernelFiles(): self.config.log.warning("No kernel-doc for file %s", fname) continue - for name, arg in self.results[fname]: - m = self.out_msg(fname, name, arg) + for arg in self.results[fname]: + m = self.out_msg(fname, arg.name, arg) if m is None: ln = arg.get("ln", 0) diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py new file mode 100644 index 000000000000..b3b225764550 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_item.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# A class that will, eventually, encapsulate all of the parsed data that we +# then pass into the output modules. +# + +class KdocItem: + def __init__(self, name, type, start_line, **other_stuff): + self.name = name + self.type = type + self.declaration_start_line = start_line + self.sections = {} + self.sections_start_lines = {} + self.parameterlist = [] + self.parameterdesc_start_lines = [] + self.parameterdescs = {} + self.parametertypes = {} + # + # Just save everything else into our own dict so that the output + # side can grab it directly as before. As we move things into more + # structured data, this will, hopefully, fade away. + # + self.other_stuff = other_stuff + + def get(self, key, default = None): + return self.other_stuff.get(key, default) + + def __getitem__(self, key): + return self.get(key) + + # + # Tracking of section and parameter information. + # + def set_sections(self, sections, start_lines): + self.sections = sections + self.section_start_lines = start_lines + + def set_params(self, names, descs, types, starts): + self.parameterlist = names + self.parameterdescs = descs + self.parametertypes = types + self.parameterdesc_start_lines = starts diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index 86102e628d91..ea8914537ba0 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -124,9 +124,7 @@ class OutputFormat: Output warnings for identifiers that will be displayed. """ - warnings = args.get('warnings', []) - - for log_msg in warnings: + for log_msg in args.warnings: self.config.warning(log_msg) def check_doc(self, name, args): @@ -184,7 +182,7 @@ class OutputFormat: self.data = "" - dtype = args.get('type', "") + dtype = args.type if dtype == "doc": self.out_doc(fname, name, args) @@ -338,12 +336,7 @@ class RestFormat(OutputFormat): starts by putting out the name of the doc section itself, but that tends to duplicate a header already in the template file. """ - - sectionlist = args.get('sectionlist', []) - sections = args.get('sections', {}) - section_start_lines = args.get('section_start_lines', {}) - - for section in sectionlist: + for section, text in args.sections.items(): # Skip sections that are in the nosymbol_table if section in self.nosymbol: continue @@ -355,8 +348,8 @@ class RestFormat(OutputFormat): else: self.data += f'{self.lineprefix}**{section}**\n\n' - self.print_lineno(section_start_lines.get(section, 0)) - self.output_highlight(sections[section]) + self.print_lineno(args.section_start_lines.get(section, 0)) + self.output_highlight(text) self.data += "\n" self.data += "\n" @@ -372,24 +365,19 @@ class RestFormat(OutputFormat): func_macro = args.get('func_macro', False) if func_macro: - signature = args['function'] + signature = name else: if args.get('functiontype'): signature = args['functiontype'] + " " - signature += args['function'] + " (" - - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) - - ln = args.get('declaration_start_line', 0) + signature += name + " (" + ln = args.declaration_start_line count = 0 - for parameter in parameterlist: + for parameter in args.parameterlist: if count != 0: signature += ", " count += 1 - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if function_pointer.search(dtype): signature += function_pointer.group(1) + parameter + function_pointer.group(3) @@ -401,7 +389,7 @@ class RestFormat(OutputFormat): self.print_lineno(ln) if args.get('typedef') or not args.get('functiontype'): - self.data += f".. c:macro:: {args['function']}\n\n" + self.data += f".. c:macro:: {name}\n\n" if args.get('typedef'): self.data += " **Typedef**: " @@ -424,26 +412,26 @@ class RestFormat(OutputFormat): # function prototypes apart self.lineprefix = " " - if parameterlist: + if args.parameterlist: self.data += ".. container:: kernelindent\n\n" self.data += f"{self.lineprefix}**Parameters**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = KernRe(r'\[.*').sub('', parameter) - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if dtype: self.data += f"{self.lineprefix}``{dtype}``\n" else: self.data += f"{self.lineprefix}``{parameter}``\n" - self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.lineprefix = " " - if parameter_name in parameterdescs and \ - parameterdescs[parameter_name] != KernelDoc.undescribed: + if parameter_name in args.parameterdescs and \ + args.parameterdescs[parameter_name] != KernelDoc.undescribed: - self.output_highlight(parameterdescs[parameter_name]) + self.output_highlight(args.parameterdescs[parameter_name]) self.data += "\n" else: self.data += f"{self.lineprefix}*undescribed*\n\n" @@ -455,10 +443,7 @@ class RestFormat(OutputFormat): def out_enum(self, fname, name, args): oldprefix = self.lineprefix - name = args.get('enum', '') - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - ln = args.get('declaration_start_line', 0) + ln = args.declaration_start_line self.data += f"\n\n.. c:enum:: {name}\n\n" @@ -472,11 +457,11 @@ class RestFormat(OutputFormat): self.lineprefix = outer + " " self.data += f"{outer}**Constants**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: self.data += f"{outer}``{parameter}``\n" - if parameterdescs.get(parameter, '') != KernelDoc.undescribed: - self.output_highlight(parameterdescs[parameter]) + if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: + self.output_highlight(args.parameterdescs[parameter]) else: self.data += f"{self.lineprefix}*undescribed*\n\n" self.data += "\n" @@ -487,8 +472,7 @@ class RestFormat(OutputFormat): def out_typedef(self, fname, name, args): oldprefix = self.lineprefix - name = args.get('typedef', '') - ln = args.get('declaration_start_line', 0) + ln = args.declaration_start_line self.data += f"\n\n.. c:type:: {name}\n\n" @@ -504,15 +488,10 @@ class RestFormat(OutputFormat): def out_struct(self, fname, name, args): - name = args.get('struct', "") purpose = args.get('purpose', "") declaration = args.get('definition', "") - dtype = args.get('type', "struct") - ln = args.get('declaration_start_line', 0) - - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) + dtype = args.type + ln = args.declaration_start_line self.data += f"\n\n.. c:{dtype}:: {name}\n\n" @@ -536,21 +515,21 @@ class RestFormat(OutputFormat): self.lineprefix = " " self.data += f"{self.lineprefix}**Members**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: if not parameter or parameter.startswith("#"): continue parameter_name = parameter.split("[", maxsplit=1)[0] - if parameterdescs.get(parameter_name) == KernelDoc.undescribed: + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue - self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.data += f"{self.lineprefix}``{parameter}``\n" self.lineprefix = " " - self.output_highlight(parameterdescs[parameter_name]) + self.output_highlight(args.parameterdescs[parameter_name]) self.lineprefix = " " self.data += "\n" @@ -636,46 +615,38 @@ class ManFormat(OutputFormat): self.data += line + "\n" def out_doc(self, fname, name, args): - sectionlist = args.get('sectionlist', []) - sections = args.get('sections', {}) - if not self.check_doc(name, args): return self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" - for section in sectionlist: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) def out_function(self, fname, name, args): """output function in man""" - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - sectionlist = args.get('sectionlist', []) - sections = args.get('sections', {}) - - self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"{args['function']} \\- {args['purpose']}\n" + self.data += f"{name} \\- {args['purpose']}\n" self.data += ".SH SYNOPSIS\n" if args.get('functiontype', ''): - self.data += f'.B "{args["functiontype"]}" {args["function"]}' + "\n" + self.data += f'.B "{args["functiontype"]}" {name}' + "\n" else: - self.data += f'.B "{args["function"]}' + "\n" + self.data += f'.B "{name}' + "\n" count = 0 parenth = "(" post = "," - for parameter in parameterlist: - if count == len(parameterlist) - 1: + for parameter in args.parameterlist: + if count == len(args.parameterlist) - 1: post = ");" - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if function_pointer.match(dtype): # Pointer-to-function self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" @@ -686,38 +657,32 @@ class ManFormat(OutputFormat): count += 1 parenth = "" - if parameterlist: + if args.parameterlist: self.data += ".SH ARGUMENTS\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = re.sub(r'\[.*', '', parameter) self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(parameterdescs.get(parameter_name, "")) + self.output_highlight(args.parameterdescs.get(parameter_name, "")) - for section in sectionlist: + for section, text in args.sections.items(): self.data += f'.SH "{section.upper()}"' + "\n" - self.output_highlight(sections[section]) + self.output_highlight(text) def out_enum(self, fname, name, args): - - name = args.get('enum', '') - parameterlist = args.get('parameterlist', []) - sectionlist = args.get('sectionlist', []) - sections = args.get('sections', {}) - - self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"enum {args['enum']} \\- {args['purpose']}\n" + self.data += f"enum {name} \\- {args['purpose']}\n" self.data += ".SH SYNOPSIS\n" - self.data += f"enum {args['enum']}" + " {\n" + self.data += f"enum {name}" + " {\n" count = 0 - for parameter in parameterlist: + for parameter in args.parameterlist: self.data += f'.br\n.BI " {parameter}"' + "\n" - if count == len(parameterlist) - 1: + if count == len(args.parameterlist) - 1: self.data += "\n};\n" else: self.data += ", \n.br\n" @@ -726,68 +691,59 @@ class ManFormat(OutputFormat): self.data += ".SH Constants\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = KernRe(r'\[.*').sub('', parameter) self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(args['parameterdescs'].get(parameter_name, "")) + self.output_highlight(args.parameterdescs.get(parameter_name, "")) - for section in sectionlist: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections[section]) + self.output_highlight(text) def out_typedef(self, fname, name, args): module = self.modulename - typedef = args.get('typedef') purpose = args.get('purpose') - sectionlist = args.get('sectionlist', []) - sections = args.get('sections', {}) - self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"typedef {typedef} \\- {purpose}\n" + self.data += f"typedef {name} \\- {purpose}\n" - for section in sectionlist: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) def out_struct(self, fname, name, args): module = self.modulename - struct_type = args.get('type') - struct_name = args.get('struct') purpose = args.get('purpose') definition = args.get('definition') - sectionlist = args.get('sectionlist', []) - parameterlist = args.get('parameterlist', []) - sections = args.get('sections', {}) - parameterdescs = args.get('parameterdescs', {}) - self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"{struct_type} {struct_name} \\- {purpose}\n" + self.data += f"{args.type} {name} \\- {purpose}\n" # Replace tabs with two spaces and handle newlines declaration = definition.replace("\t", " ") declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) self.data += ".SH SYNOPSIS\n" - self.data += f"{struct_type} {struct_name} " + "{" + "\n.br\n" + self.data += f"{args.type} {name} " + "{" + "\n.br\n" self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" self.data += ".SH Members\n" - for parameter in parameterlist: + for parameter in args.parameterlist: if parameter.startswith("#"): continue parameter_name = re.sub(r"\[.*", "", parameter) - if parameterdescs.get(parameter_name) == KernelDoc.undescribed: + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(parameterdescs.get(parameter_name)) + self.output_highlight(args.parameterdescs.get(parameter_name)) - for section in sectionlist: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 062453eefc7a..fe730099eca8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -12,11 +12,12 @@ Read a C language source or header FILE and extract embedded documentation comments """ +import sys import re from pprint import pformat from kdoc_re import NestedMatch, KernRe - +from kdoc_item import KdocItem # # Regular expressions used to parse kernel-doc markups at KernelDoc class. @@ -42,12 +43,13 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False) # @{section-name}: # while trying to not match literal block starts like "example::" # +known_section_names = 'description|context|returns?|notes?|examples?' +known_sections = KernRe(known_section_names, flags = re.I) doc_sect = doc_com + \ - KernRe(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$', - flags=re.I, cache=False) + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + flags=re.I, cache=False) doc_content = doc_com_body + KernRe(r'(.*)', cache=False) -doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) @@ -60,6 +62,25 @@ export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+" type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) +# +# Tests for the beginning of a kerneldoc block in its various forms. +# +doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) +doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False) +doc_begin_func = KernRe(str(doc_com) + # initial " * ' + r"(?:\w+\s*\*\s*)?" + # type (not captured) + r'(?:define\s+)?' + # possible "define" (not captured) + r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)" + r'(?:[-:].*)?$', # description (not captured) + cache = False) + +# +# A little helper to get rid of excess white space +# +multi_space = KernRe(r'\s\s+') +def trim_whitespace(s): + return multi_space.sub(' ', s.strip()) + class state: """ State machine enums @@ -68,40 +89,26 @@ class state: # Parser states NORMAL = 0 # normal code NAME = 1 # looking for function name - BODY_MAYBE = 2 # body - or maybe more description + DECLARATION = 2 # We have seen a declaration which might not be done BODY = 3 # the body of the comment - BODY_WITH_BLANK_LINE = 4 # the body which has a blank line + SPECIAL_SECTION = 4 # doc section ending with a blank line PROTO = 5 # scanning prototype DOCBLOCK = 6 # documentation block - INLINE = 7 # gathering doc outside main block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs name = [ "NORMAL", "NAME", - "BODY_MAYBE", + "DECLARATION", "BODY", - "BODY_WITH_BLANK_LINE", + "SPECIAL_SECTION", "PROTO", "DOCBLOCK", - "INLINE", + "INLINE_NAME", + "INLINE_TEXT", ] - # Inline documentation state - INLINE_NA = 0 # not applicable ($state != INLINE) - INLINE_NAME = 1 # looking for member name (@foo:) - INLINE_TEXT = 2 # looking for member documentation - INLINE_END = 3 # done - INLINE_ERROR = 4 # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. - - inline_name = [ - "", - "_NAME", - "_TEXT", - "_END", - "_ERROR", - ] SECTION_DEFAULT = "Description" # default section @@ -110,10 +117,7 @@ class KernelEntry: def __init__(self, config, ln): self.config = config - self.contents = "" - self.function = "" - self.sectcheck = "" - self.struct_actual = "" + self._contents = [] self.prototype = "" self.warnings = [] @@ -124,7 +128,6 @@ class KernelEntry: self.parameterdesc_start_lines = {} self.section_start_lines = {} - self.sectionlist = [] self.sections = {} self.anon_struct_union = False @@ -133,10 +136,17 @@ class KernelEntry: # State flags self.brcount = 0 - - self.in_doc_sect = False self.declaration_start_line = ln + 1 + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + # TODO: rename to emit_message after removal of kernel-doc.pl def emit_msg(self, log_msg, warning=True): """Emit a message""" @@ -151,13 +161,27 @@ class KernelEntry: self.warnings.append(log_msg) return + # + # Begin a new section. + # + def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False): + if dump: + self.dump_section(start_new = True) + self.section = title + self.new_start_line = line_no + def dump_section(self, start_new=True): """ Dumps section contents to arrays/hashes intended for that purpose. """ - + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return name = self.section - contents = self.contents + contents = self.contents() if type_param.match(name): name = type_param.group(1) @@ -165,14 +189,6 @@ class KernelEntry: self.parameterdescs[name] = contents self.parameterdesc_start_lines[name] = self.new_start_line - self.sectcheck += name + " " - self.new_start_line = 0 - - elif name == "@...": - name = "..." - self.parameterdescs[name] = contents - self.sectcheck += name + " " - self.parameterdesc_start_lines[name] = self.new_start_line self.new_start_line = 0 else: @@ -181,10 +197,10 @@ class KernelEntry: if name != SECTION_DEFAULT: self.emit_msg(self.new_start_line, f"duplicate section name '{name}'\n") - self.sections[name] += contents + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents else: self.sections[name] = contents - self.sectionlist.append(name) self.section_start_lines[name] = self.new_start_line self.new_start_line = 0 @@ -192,7 +208,7 @@ class KernelEntry: if start_new: self.section = SECTION_DEFAULT - self.contents = "" + self._contents = [] class KernelDoc: @@ -203,7 +219,6 @@ class KernelDoc: # Section names - section_intro = "Introduction" section_context = "Context" section_return = "Return" @@ -217,7 +232,6 @@ class KernelDoc: # Initial state for the state machines self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA # Store entry currently being processed self.entry = None @@ -225,6 +239,14 @@ class KernelDoc: # Place all potential outputs into an array self.entries = [] + # + # We need Python 3.7 for its "dicts remember the insertion + # order" guarantee + # + if sys.version_info.major == 3 and sys.version_info.minor < 7: + self.emit_msg(0, + 'Python 3.7 or later is required for correct results') + def emit_msg(self, ln, msg, warning=True): """Emit a message""" @@ -255,32 +277,20 @@ class KernelDoc: The actual output and output filters will be handled elsewhere """ - # The implementation here is different than the original kernel-doc: - # instead of checking for output filters or actually output anything, - # it just stores the declaration content at self.entries, as the - # output will happen on a separate class. - # - # For now, we're keeping the same name of the function just to make - # easier to compare the source code of both scripts - - args["declaration_start_line"] = self.entry.declaration_start_line - args["type"] = dtype - args["warnings"] = self.entry.warnings - - # TODO: use colletions.OrderedDict to remove sectionlist - - sections = args.get('sections', {}) - sectionlist = args.get('sectionlist', []) + item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) + item.warnings = self.entry.warnings # Drop empty sections # TODO: improve empty sections logic to emit warnings + sections = self.entry.sections for section in ["Description", "Return"]: - if section in sectionlist: - if not sections[section].rstrip(): - del sections[section] - sectionlist.remove(section) - - self.entries.append((name, args)) + if section in sections and not sections[section].rstrip(): + del sections[section] + item.set_sections(sections, self.entry.section_start_lines) + item.set_params(self.entry.parameterlist, self.entry.parameterdescs, + self.entry.parametertypes, + self.entry.parameterdesc_start_lines) + self.entries.append(item) self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) @@ -294,7 +304,6 @@ class KernelDoc: # State flags self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA def push_parameter(self, ln, decl_type, param, dtype, org_arg, declaration_name): @@ -367,15 +376,6 @@ class KernelDoc: org_arg = KernRe(r'\s\s+').sub(' ', org_arg) self.entry.parametertypes[param] = org_arg - def save_struct_actual(self, actual): - """ - Strip all spaces from the actual param so that it looks like - one string item. - """ - - actual = KernRe(r'\s*').sub("", actual, count=1) - - self.entry.struct_actual += actual + " " def create_parameter_list(self, ln, decl_type, args, splitter, declaration_name): @@ -421,7 +421,6 @@ class KernelDoc: param = arg dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) @@ -438,7 +437,6 @@ class KernelDoc: dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) @@ -471,7 +469,6 @@ class KernelDoc: param = r.group(1) - self.save_struct_actual(r.group(2)) self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) @@ -483,52 +480,27 @@ class KernelDoc: continue if dtype != "": # Skip unnamed bit-fields - self.save_struct_actual(r.group(1)) self.push_parameter(ln, decl_type, r.group(1), f"{dtype}:{r.group(2)}", arg, declaration_name) else: - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) - def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck): + def check_sections(self, ln, decl_name, decl_type): """ Check for errors inside sections, emitting warnings if not found parameters are described. """ - - sects = sectcheck.split() - prms = prmscheck.split() - err = False - - for sx in range(len(sects)): # pylint: disable=C0200 - err = True - for px in range(len(prms)): # pylint: disable=C0200 - prm_clean = prms[px] - prm_clean = KernRe(r'\[.*\]').sub('', prm_clean) - prm_clean = attribute.sub('', prm_clean) - - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - prm_clean = KernRe(r'\[.*').sub('', prm_clean) - - if prm_clean == sects[sx]: - err = False - break - - if err: + for section in self.entry.sections: + if section not in self.entry.parameterlist and \ + not known_sections.search(section): if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" - self.emit_msg(ln, - f"Excess {dname} '{sects[sx]}' description in '{decl_name}'") + f"Excess {dname} '{section}' description in '{decl_name}'") def check_return_section(self, ln, declaration_name, return_type): """ @@ -666,6 +638,7 @@ class KernelDoc: (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), + (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), ] # Regexes here are guaranteed to have the end limiter matching @@ -782,8 +755,7 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, members, ';', declaration_name) - self.check_sections(ln, declaration_name, decl_type, - self.entry.sectcheck, self.entry.struct_actual) + self.check_sections(ln, declaration_name, decl_type) # Adjust declaration for better display declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) @@ -819,15 +791,7 @@ class KernelDoc: level += 1 self.output_declaration(decl_type, declaration_name, - struct=declaration_name, definition=declaration, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto): @@ -845,39 +809,48 @@ class KernelDoc: # Strip #define macros inside enums proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) - members = None - declaration_name = None - + # + # Parse out the name and members of the enum. Typedef form first. + # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # else: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) members = r.group(2).rstrip() - - if not members: - self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") - return - + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # if self.entry.identifier != declaration_name: if self.entry.identifier == "": self.emit_msg(ln, f"{proto}: wrong kernel-doc identifier on prototype") else: self.emit_msg(ln, - f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead") + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") return if not declaration_name: declaration_name = "(anonymous)" - + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # member_set = set() - - members = KernRe(r'\([^;]*?[\)]').sub('', members) - + members = KernRe(r'\([^;)]*\)').sub('', members) for arg in members.split(','): if not arg: continue @@ -888,20 +861,15 @@ class KernelDoc: self.emit_msg(ln, f"Enum value '{arg}' not described in enum '{declaration_name}'") member_set.add(arg) - + # + # Ensure that every described member actually exists in the enum. + # for k in self.entry.parameterdescs: if k not in member_set: self.emit_msg(ln, f"Excess enum value '%{k}' description in '{declaration_name}'") self.output_declaration('enum', declaration_name, - enum=declaration_name, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype): @@ -911,18 +879,13 @@ class KernelDoc: if self.entry.decl_type == "enum": self.dump_enum(ln, prototype) - return - - if self.entry.decl_type == "typedef": + elif self.entry.decl_type == "typedef": self.dump_typedef(ln, prototype) - return - - if self.entry.decl_type in ["union", "struct"]: + elif self.entry.decl_type in ["union", "struct"]: self.dump_struct(ln, prototype) - return - - self.output_declaration(self.entry.decl_type, prototype, - entry=self.entry) + else: + # This would be a bug + self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') def dump_function(self, ln, prototype): """ @@ -1056,38 +1019,20 @@ class KernelDoc: f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") return - prms = " ".join(self.entry.parameterlist) - self.check_sections(ln, declaration_name, "function", - self.entry.sectcheck, prms) + self.check_sections(ln, declaration_name, "function") self.check_return_section(ln, declaration_name, return_type) if 'typedef' in return_type: self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=True, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) else: self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=False, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) @@ -1124,16 +1069,8 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, args, ',', declaration_name) self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=True, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) return @@ -1153,10 +1090,6 @@ class KernelDoc: return self.output_declaration('typedef', declaration_name, - typedef=declaration_name, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) return @@ -1171,17 +1104,28 @@ class KernelDoc: with a staticmethod decorator. """ + # We support documenting some exported symbols with different + # names. A horrible hack. + suffixes = [ '_noprof' ] + # Note: it accepts only one EXPORT_SYMBOL* per line, as having # multiple export lines would violate Kernel coding style. if export_symbol.search(line): symbol = export_symbol.group(2) - function_set.add(symbol) - return - - if export_symbol_ns.search(line): + elif export_symbol_ns.search(line): symbol = export_symbol_ns.group(2) - function_set.add(symbol) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + # Be backward compatible with Python < 3.9 + if symbol.endswith(suffix): + symbol = symbol[:-len(suffix)] + function_set.add(symbol) + return True def process_normal(self, ln, line): """ @@ -1193,7 +1137,6 @@ class KernelDoc: # start a new entry self.reset_state(ln) - self.entry.in_doc_sect = False # next line is always the function name self.state = state.NAME @@ -1202,80 +1145,60 @@ class KernelDoc: """ STATE_NAME: Looking for the "name - description" line """ - + # + # Check for a DOC: block and handle them specially. + # if doc_block.search(line): - self.entry.new_start_line = ln if not doc_block.group(1): - self.entry.section = self.section_intro + self.entry.begin_section(ln, "Introduction") else: - self.entry.section = doc_block.group(1) + self.entry.begin_section(ln, doc_block.group(1)) self.entry.identifier = self.entry.section self.state = state.DOCBLOCK - return - - if doc_decl.search(line): + # + # Otherwise we're looking for a normal kerneldoc declaration line. + # + elif doc_decl.search(line): self.entry.identifier = doc_decl.group(1) - self.entry.is_kernel_comment = False - - decl_start = str(doc_com) # comment block asterisk - fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions) - parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function - decl_end = r"(?:[-:].*)" # end of the name part - - # test for pointer declaration type, foo * bar() - desc - r = KernRe(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$") - if r.search(line): - self.entry.identifier = r.group(1) # Test for data declaration - r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") - if r.search(line): - self.entry.decl_type = r.group(1) - self.entry.identifier = r.group(2) - self.entry.is_kernel_comment = True + if doc_begin_data.search(line): + self.entry.decl_type = doc_begin_data.group(1) + self.entry.identifier = doc_begin_data.group(2) + # + # Look for a function description + # + elif doc_begin_func.search(line): + self.entry.identifier = doc_begin_func.group(1) + self.entry.decl_type = "function" + # + # We struck out. + # else: - # Look for foo() or static void foo() - description; - # or misspelt identifier - - r1 = KernRe(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$") - r2 = KernRe(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$") - - for r in [r1, r2]: - if r.search(line): - self.entry.identifier = r.group(1) - self.entry.decl_type = "function" - - r = KernRe(r"define\s+") - self.entry.identifier = r.sub("", self.entry.identifier) - self.entry.is_kernel_comment = True - break - - self.entry.identifier = self.entry.identifier.strip(" ") - - self.state = state.BODY - - # if there's no @param blocks need to set up default section here - self.entry.section = SECTION_DEFAULT - self.entry.new_start_line = ln + 1 - - r = KernRe("[-:](.*)") - if r.search(line): - # strip leading/trailing/multiple spaces - self.entry.descr = r.group(1).strip(" ") - - r = KernRe(r"\s+") - self.entry.descr = r.sub(" ", self.entry.descr) - self.entry.declaration_purpose = self.entry.descr - self.state = state.BODY_MAYBE - else: - self.entry.declaration_purpose = "" - - if not self.entry.is_kernel_comment: self.emit_msg(ln, f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") self.state = state.NORMAL + return + # + # OK, set up for a new kerneldoc entry. + # + self.state = state.BODY + self.entry.identifier = self.entry.identifier.strip(" ") + # if there's no @param blocks need to set up default section here + self.entry.begin_section(ln + 1) + # + # Find the description portion, which *should* be there but + # isn't always. + # (We should be able to capture this from the previous parsing - someday) + # + r = KernRe("[-:](.*)") + if r.search(line): + self.entry.declaration_purpose = trim_whitespace(r.group(1)) + self.state = state.DECLARATION + else: + self.entry.declaration_purpose = "" if not self.entry.declaration_purpose and self.config.wshort_desc: self.emit_msg(ln, @@ -1290,60 +1213,51 @@ class KernelDoc: self.emit_msg(ln, f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", warning=False) - - return - + # # Failed to find an identifier. Emit a warning - self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") - - def process_body(self, ln, line): - """ - STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. - """ - - if self.state == state.BODY_WITH_BLANK_LINE: - r = KernRe(r"\s*\*\s?\S") - if r.match(line): - self.dump_section() - self.entry.section = SECTION_DEFAULT - self.entry.new_start_line = ln - self.entry.contents = "" + # + else: + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + # + # Helper function to determine if a new section is being started. + # + def is_new_section(self, ln, line): if doc_sect.search(line): - self.entry.in_doc_sect = True + self.state = state.BODY + # + # Pick out the name of our new section, tweaking it if need be. + # newsection = doc_sect.group(1) - - if newsection.lower() in ["description", "context"]: - newsection = newsection.title() - - # Special case: @return is a section, not a param description - if newsection.lower() in ["@return", "@returns", - "return", "returns"]: + if newsection.lower() == 'description': + newsection = 'Description' + elif newsection.lower() == 'context': + newsection = 'Context' + self.state = state.SPECIAL_SECTION + elif newsection.lower() in ["@return", "@returns", + "return", "returns"]: newsection = "Return" - - # Perl kernel-doc has a check here for contents before sections. - # the logic there is always false, as in_doc_sect variable is - # always true. So, just don't implement Wcontents_before_sections - - # .title() + self.state = state.SPECIAL_SECTION + elif newsection[0] == '@': + self.state = state.SPECIAL_SECTION + # + # Initialize the contents, and get the new section going. + # newcontents = doc_sect.group(2) if not newcontents: newcontents = "" - - if self.entry.contents.strip("\n"): - self.dump_section() - - self.entry.new_start_line = ln - self.entry.section = newsection + self.dump_section() + self.entry.begin_section(ln, newsection) self.entry.leading_space = None - self.entry.contents = newcontents.lstrip() - if self.entry.contents: - self.entry.contents += "\n" - - self.state = state.BODY - return + self.entry.add_text(newcontents.lstrip()) + return True + return False + # + # Helper function to detect (and effect) the end of a kerneldoc comment. + # + def is_comment_end(self, ln, line): if doc_end.search(line): self.dump_section() @@ -1356,100 +1270,128 @@ class KernelDoc: self.entry.new_start_line = ln + 1 self.state = state.PROTO + return True + return False + + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # Look for anything with the " * " line beginning. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # A blank line means that we have moved out of the declaration + # part of the comment (without any "special section" parameter + # descriptions). + # + if cont == "": + self.state = state.BODY + # + # Otherwise we have more of the declaration section to soak up. + # + else: + self.entry.declaration_purpose = \ + trim_whitespace(self.entry.declaration_purpose + ' ' + cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + + def process_special(self, ln, line): + """ + STATE_SPECIAL_SECTION: a section ending with a blank line + """ + # + # If we have hit a blank line (only the " * " marker), then this + # section is done. + # + if KernRe(r"\s*\*\s*$").match(line): + self.entry.begin_section(ln, dump = True) + self.state = state.BODY + return + # + # Not a blank line, look for the other ways to end the section. + # + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # OK, we should have a continuation of the text for this section. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # If the lines of text after the first in a special section have + # leading white space, we need to trim it out or Sphinx will get + # confused. For the second line (the None case), see what we + # find there and remember it. + # + if self.entry.leading_space is None: + r = KernRe(r'^(\s+)') + if r.match(cont): + self.entry.leading_space = len(r.group(1)) + else: + self.entry.leading_space = 0 + # + # Otherwise, before trimming any leading chars, be *sure* + # that they are white space. We should maybe warn if this + # isn't the case. + # + for i in range(0, self.entry.leading_space): + if cont[i] != " ": + self.entry.leading_space = i + break + # + # Add the trimmed result to the section and we're done. + # + self.entry.add_text(cont[self.entry.leading_space:]) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_body(self, ln, line): + """ + STATE_BODY: the bulk of a kerneldoc comment. + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): return if doc_content.search(line): cont = doc_content.group(1) + self.entry.add_text(cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") - if cont == "": - if self.entry.section == self.section_context: - self.dump_section() + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" - self.entry.new_start_line = ln - self.state = state.BODY - else: - if self.entry.section != SECTION_DEFAULT: - self.state = state.BODY_WITH_BLANK_LINE - else: - self.state = state.BODY + if doc_inline_sect.search(line): + self.entry.begin_section(ln, doc_inline_sect.group(1)) + self.entry.add_text(doc_inline_sect.group(2).lstrip()) + self.state = state.INLINE_TEXT + elif doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? - self.entry.contents += "\n" - - elif self.state == state.BODY_MAYBE: - - # Continued declaration purpose - self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() - self.entry.declaration_purpose += " " + cont - - r = KernRe(r"\s+") - self.entry.declaration_purpose = r.sub(' ', - self.entry.declaration_purpose) - - else: - if self.entry.section.startswith('@') or \ - self.entry.section == self.section_context: - if self.entry.leading_space is None: - r = KernRe(r'^(\s+)') - if r.match(cont): - self.entry.leading_space = len(r.group(1)) - else: - self.entry.leading_space = 0 - - # Double-check if leading space are realy spaces - pos = 0 - for i in range(0, self.entry.leading_space): - if cont[i] != " ": - break - pos += 1 - - cont = cont[pos:] - - # NEW LOGIC: - # In case it is different, update it - if self.entry.leading_space != pos: - self.entry.leading_space = pos - - self.entry.contents += cont + "\n" - return - - # Unknown line, ignore - self.emit_msg(ln, f"bad line: {line}") - - def process_inline(self, ln, line): - """STATE_INLINE: docbook comments within a prototype.""" - - if self.inline_doc_state == state.INLINE_NAME and \ - doc_inline_sect.search(line): - self.entry.section = doc_inline_sect.group(1) - self.entry.new_start_line = ln - - self.entry.contents = doc_inline_sect.group(2).lstrip() - if self.entry.contents != "": - self.entry.contents += "\n" - - self.inline_doc_state = state.INLINE_TEXT - # Documentation block end */ - return + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" if doc_inline_end.search(line): - if self.entry.contents not in ["", "\n"]: - self.dump_section() - + self.dump_section() self.state = state.PROTO - self.inline_doc_state = state.INLINE_NA - return - - if doc_content.search(line): - if self.inline_doc_state == state.INLINE_TEXT: - self.entry.contents += doc_content.group(1) + "\n" - if not self.entry.contents.strip(" ").rstrip("\n"): - self.entry.contents = "" - - elif self.inline_doc_state == state.INLINE_NAME: - self.emit_msg(ln, - f"Incorrect use of kernel-doc format: {line}") - - self.inline_doc_state = state.INLINE_ERROR + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? def syscall_munge(self, ln, proto): # pylint: disable=W0613 """ @@ -1531,105 +1473,94 @@ class KernelDoc: """Ancillary routine to process a function prototype""" # strip C99-style comments to end of line - r = KernRe(r"\/\/.*$", re.S) - line = r.sub('', line) - + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # if KernRe(r'\s*#\s*define').match(line): self.entry.prototype = line - elif line.startswith('#'): - # Strip other macros like #ifdef/#ifndef/#endif/... - pass - else: + elif not line.startswith('#'): # skip other preprocessor stuff r = KernRe(r'([^\{]*)') if r.match(line): self.entry.prototype += r.group(1) + " " - + # + # If we now have the whole prototype, clean it up and declare victory. + # if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): - # strip comments - r = KernRe(r'/\*.*?\*/') - self.entry.prototype = r.sub('', self.entry.prototype) - - # strip newlines/cr's - r = KernRe(r'[\r\n]+') - self.entry.prototype = r.sub(' ', self.entry.prototype) - - # strip leading spaces - r = KernRe(r'^\s+') - self.entry.prototype = r.sub('', self.entry.prototype) - + # strip comments and surrounding spaces + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # # Handle self.entry.prototypes for function pointers like: # int (*pcs_config)(struct foo) - + # by turning it into + # int pcs_config(struct foo) + # r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) - + # + # Handle special declaration syntaxes + # if 'SYSCALL_DEFINE' in self.entry.prototype: self.entry.prototype = self.syscall_munge(ln, self.entry.prototype) - - r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') - if r.search(self.entry.prototype): - self.entry.prototype = self.tracepoint_munge(ln, - self.entry.prototype) - + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # self.dump_function(ln, self.entry.prototype) self.reset_state(ln) def process_proto_type(self, ln, line): """Ancillary routine to process a type""" - # Strip newlines/cr's. - line = KernRe(r'[\r\n]+', re.S).sub(' ', line) - - # Strip leading spaces - line = KernRe(r'^\s+', re.S).sub('', line) - - # Strip trailing spaces - line = KernRe(r'\s+$', re.S).sub('', line) - - # Strip C99-style comments to the end of the line - line = KernRe(r"\/\/.*$", re.S).sub('', line) + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here # To distinguish preprocessor directive from regular declaration later. if line.startswith('#'): line += ";" - - r = KernRe(r'([^\{\};]*)([\{\};])(.*)') - while True: - if r.search(line): - if self.entry.prototype: - self.entry.prototype += " " - self.entry.prototype += r.group(1) + r.group(2) - - self.entry.brcount += r.group(2).count('{') - self.entry.brcount -= r.group(2).count('}') - - self.entry.brcount = max(self.entry.brcount, 0) - - if r.group(2) == ';' and self.entry.brcount == 0: + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: self.dump_declaration(ln, self.entry.prototype) self.reset_state(ln) - break - - line = r.group(3) - else: - self.entry.prototype += line - break + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' def process_proto(self, ln, line): """STATE_PROTO: reading a function/whatever prototype.""" if doc_inline_oneline.search(line): - self.entry.section = doc_inline_oneline.group(1) - self.entry.contents = doc_inline_oneline.group(2) - - if self.entry.contents != "": - self.entry.contents += "\n" - self.dump_section(start_new=False) + self.entry.begin_section(ln, doc_inline_oneline.group(1)) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() elif doc_inline_start.search(line): - self.state = state.INLINE - self.inline_doc_state = state.INLINE_NAME + self.state = state.INLINE_NAME elif self.entry.decl_type == 'function': self.process_proto_function(ln, line) @@ -1642,14 +1573,11 @@ class KernelDoc: if doc_end.search(line): self.dump_section() - self.output_declaration("doc", self.entry.identifier, - sectionlist=self.entry.sectionlist, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines) + self.output_declaration("doc", self.entry.identifier) self.reset_state(ln) elif doc_content.search(line): - self.entry.contents += doc_content.group(1) + "\n" + self.entry.add_text(doc_content.group(1)) def parse_export(self): """ @@ -1670,6 +1598,22 @@ class KernelDoc: return export_table + # + # The state/action table telling us which function to invoke in + # each state. + # + state_actions = { + state.NORMAL: process_normal, + state.NAME: process_name, + state.BODY: process_body, + state.DECLARATION: process_decl, + state.SPECIAL_SECTION: process_special, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, + state.PROTO: process_proto, + state.DOCBLOCK: process_docblock, + } + def parse_kdoc(self): """ Open and process each line of a C source file. @@ -1680,7 +1624,6 @@ class KernelDoc: Besides parsing kernel-doc tags, it also parses export symbols. """ - cont = False prev = "" prev_ln = None export_table = set() @@ -1696,23 +1639,18 @@ class KernelDoc: if self.state == state.PROTO: if line.endswith("\\"): prev += line.rstrip("\\") - cont = True - if not prev_ln: prev_ln = ln - continue - if cont: + if prev: ln = prev_ln line = prev + line prev = "" - cont = False prev_ln = None - self.config.log.debug("%d %s%s: %s", + self.config.log.debug("%d %s: %s", ln, state.name[self.state], - state.inline_name[self.inline_doc_state], line) # This is an optimization over the original script. @@ -1720,25 +1658,11 @@ class KernelDoc: # it was read twice. Here, we use the already-existing # loop to parse exported symbols as well. # - # TODO: It should be noticed that not all states are - # needed here. On a future cleanup, process export only - # at the states that aren't handling comment markups. - self.process_export(export_table, line) + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) - # Hand this line to the appropriate state handler - if self.state == state.NORMAL: - self.process_normal(ln, line) - elif self.state == state.NAME: - self.process_name(ln, line) - elif self.state in [state.BODY, state.BODY_MAYBE, - state.BODY_WITH_BLANK_LINE]: - self.process_body(ln, line) - elif self.state == state.INLINE: # scanning for inline parameters - self.process_inline(ln, line) - elif self.state == state.PROTO: - self.process_proto(ln, line) - elif self.state == state.DOCBLOCK: - self.process_docblock(ln, line) except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py index e81695b273bf..612223e1e723 100644 --- a/scripts/lib/kdoc/kdoc_re.py +++ b/scripts/lib/kdoc/kdoc_re.py @@ -29,12 +29,9 @@ class KernRe: """ Adds a new regex or re-use it from the cache. """ - - if string in re_cache: - self.regex = re_cache[string] - else: + self.regex = re_cache.get(string, None) + if not self.regex: self.regex = re.compile(string, flags=flags) - if self.cache: re_cache[string] = self.regex diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 450f1088d5fd..ee79c41059f3 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -52,17 +52,12 @@ SECTIONS { .data : { *(.data .data.[0-9a-zA-Z_]*) *(.data..L*) - MOD_CODETAG_SECTIONS() } .rodata : { *(.rodata .rodata.[0-9a-zA-Z_]*) *(.rodata..L*) } -#else - .data : { - MOD_CODETAG_SECTIONS() - } #endif MOD_SEPARATE_CODETAG_SECTIONS() } diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 0871b2e92584..861b56dda64e 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -359,7 +359,7 @@ if ($arch eq "x86_64") { $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_CKCORE_PCREL_JSR_IMM26BY2\\s+_mcount\$"; $alignment = 2; } else { - die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD"; + die "Arch $arch is not supported with CONFIG_DYNAMIC_FTRACE"; } my $text_found = 0; diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index 1ca253594d38..abb34ada2508 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -85,24 +85,25 @@ fn find_candidates( } } - assert!( - valid_paths.len() > 0, - "No path candidates found for `{file}`. This is likely a bug in the build system, or some \ - files went away while compiling." - ); + match valid_paths.as_slice() { + [] => panic!( + "No path candidates found for `{file}`. This is likely a bug in the build system, or \ + some files went away while compiling." + ), + [valid_path] => valid_path.to_str().unwrap(), + valid_paths => { + use std::fmt::Write; - if valid_paths.len() > 1 { - eprintln!("Several path candidates found:"); - for path in valid_paths { - eprintln!(" {path:?}"); + let mut candidates = String::new(); + for path in valid_paths { + writeln!(&mut candidates, " {path:?}").unwrap(); + } + panic!( + "Several path candidates found for `{file}`, please resolve the ambiguity by \ + renaming a file or folder. Candidates:\n{candidates}", + ); } - panic!( - "Several path candidates found for `{file}`, please resolve the ambiguity by renaming \ - a file or folder." - ); } - - valid_paths[0].to_str().unwrap() } fn main() { diff --git a/scripts/spelling.txt b/scripts/spelling.txt index ac94fa1c2415..1e89b92c2f9a 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -1099,6 +1099,7 @@ notication||notification notications||notifications notifcations||notifications notifed||notified +notifer||notifier notity||notify notfify||notify nubmer||number diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index ad9945ccb0cf..3f8d6925e896 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -245,6 +245,10 @@ sub check_missing_tex($) sub get_sphinx_fname() { + if ($ENV{'SPHINXBUILD'}) { + return $ENV{'SPHINXBUILD'}; + } + my $fname = "sphinx-build"; return $fname if findprog($fname); @@ -409,7 +413,7 @@ sub give_redhat_hints() my $old = 0; my $rel; my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; - $rel = $1 if ($system_release =~ /release\s+(\d+)/); + $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/); if (!($system_release =~ /Fedora/)) { $map{"virtualenv"} = "python-virtualenv"; diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl index 580b4e246aec..d1ae5e92c615 100644 --- a/scripts/syscall.tbl +++ b/scripts/syscall.tbl @@ -408,3 +408,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py new file mode 100755 index 000000000000..47b4606569f9 --- /dev/null +++ b/scripts/test_doc_build.py @@ -0,0 +1,513 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab +# +# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301 + +""" +Install minimal supported requirements for different Sphinx versions +and optionally test the build. +""" + +import argparse +import asyncio +import os.path +import shutil +import sys +import time +import subprocess + +# Minimal python version supported by the building system. + +PYTHON = os.path.basename(sys.executable) + +min_python_bin = None + +for i in range(9, 13): + p = f"python3.{i}" + if shutil.which(p): + min_python_bin = p + break + +if not min_python_bin: + min_python_bin = PYTHON + +# Starting from 8.0, Python 3.9 is not supported anymore. +PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON} + +DEFAULT_VERSIONS_TO_TEST = [ + (3, 4, 3), # Minimal supported version + (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9 + (6, 1, 1), # Debian 12 + (7, 2, 1), # openSUSE Leap 15.6 + (7, 2, 6), # Ubuntu 24.04 LTS + (7, 4, 7), # Ubuntu 24.10 + (7, 3, 0), # openSUSE Tumbleweed + (8, 1, 3), # Fedora 42 + (8, 2, 3) # Latest version - covers rolling distros +] + +# Sphinx versions to be installed and their incremental requirements +SPHINX_REQUIREMENTS = { + # Oldest versions we support for each package required by Sphinx 3.4.3 + (3, 4, 3): { + "docutils": "0.16", + "alabaster": "0.7.12", + "babel": "2.8.0", + "certifi": "2020.6.20", + "docutils": "0.16", + "idna": "2.10", + "imagesize": "1.2.0", + "Jinja2": "2.11.2", + "MarkupSafe": "1.1.1", + "packaging": "20.4", + "Pygments": "2.6.1", + "PyYAML": "5.1", + "requests": "2.24.0", + "snowballstemmer": "2.0.0", + "sphinxcontrib-applehelp": "1.0.2", + "sphinxcontrib-devhelp": "1.0.2", + "sphinxcontrib-htmlhelp": "1.0.3", + "sphinxcontrib-jsmath": "1.0.1", + "sphinxcontrib-qthelp": "1.0.3", + "sphinxcontrib-serializinghtml": "1.1.4", + "urllib3": "1.25.9", + }, + + # Update package dependencies to a more modern base. The goal here + # is to avoid to many incremental changes for the next entries + (3, 5, 0): { + "alabaster": "0.7.13", + "babel": "2.17.0", + "certifi": "2025.6.15", + "idna": "3.10", + "imagesize": "1.4.1", + "packaging": "25.0", + "Pygments": "2.8.1", + "requests": "2.32.4", + "snowballstemmer": "3.0.1", + "sphinxcontrib-applehelp": "1.0.4", + "sphinxcontrib-htmlhelp": "2.0.1", + "sphinxcontrib-serializinghtml": "1.1.5", + "urllib3": "2.0.0", + }, + + # Starting from here, ensure all docutils versions are covered with + # supported Sphinx versions. Other packages are upgraded only when + # required by pip + (4, 0, 0): { + "PyYAML": "5.1", + }, + (4, 1, 0): { + "docutils": "0.17", + "Pygments": "2.19.1", + "Jinja2": "3.0.3", + "MarkupSafe": "2.0", + }, + (4, 3, 0): {}, + (4, 4, 0): {}, + (4, 5, 0): { + "docutils": "0.17.1", + }, + (5, 0, 0): {}, + (5, 1, 0): {}, + (5, 2, 0): { + "docutils": "0.18", + "Jinja2": "3.1.2", + "MarkupSafe": "2.0", + "PyYAML": "5.3.1", + }, + (5, 3, 0): { + "docutils": "0.18.1", + }, + (6, 0, 0): {}, + (6, 1, 0): {}, + (6, 2, 0): { + "PyYAML": "5.4.1", + }, + (7, 0, 0): {}, + (7, 1, 0): {}, + (7, 2, 0): { + "docutils": "0.19", + "PyYAML": "6.0.1", + "sphinxcontrib-serializinghtml": "1.1.9", + }, + (7, 2, 6): { + "docutils": "0.20", + }, + (7, 3, 0): { + "alabaster": "0.7.14", + "PyYAML": "6.0.1", + "tomli": "2.0.1", + }, + (7, 4, 0): { + "docutils": "0.20.1", + "PyYAML": "6.0.1", + }, + (8, 0, 0): { + "docutils": "0.21", + }, + (8, 1, 0): { + "docutils": "0.21.1", + "PyYAML": "6.0.1", + "sphinxcontrib-applehelp": "1.0.7", + "sphinxcontrib-devhelp": "1.0.6", + "sphinxcontrib-htmlhelp": "2.0.6", + "sphinxcontrib-qthelp": "1.0.6", + }, + (8, 2, 0): { + "docutils": "0.21.2", + "PyYAML": "6.0.1", + "sphinxcontrib-serializinghtml": "1.1.9", + }, +} + + +class AsyncCommands: + """Excecute command synchronously""" + + def __init__(self, fp=None): + + self.stdout = None + self.stderr = None + self.output = None + self.fp = fp + + def log(self, out, verbose, is_info=True): + out = out.removesuffix('\n') + + if verbose: + if is_info: + print(out) + else: + print(out, file=sys.stderr) + + if self.fp: + self.fp.write(out + "\n") + + async def _read(self, stream, verbose, is_info): + """Ancillary routine to capture while displaying""" + + while stream is not None: + line = await stream.readline() + if line: + out = line.decode("utf-8", errors="backslashreplace") + self.log(out, verbose, is_info) + if is_info: + self.stdout += out + else: + self.stderr += out + else: + break + + async def run(self, cmd, capture_output=False, check=False, + env=None, verbose=True): + + """ + Execute an arbitrary command, handling errors. + + Please notice that this class is not thread safe + """ + + self.stdout = "" + self.stderr = "" + + self.log("$ " + " ".join(cmd), verbose) + + proc = await asyncio.create_subprocess_exec(cmd[0], + *cmd[1:], + env=env, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + + # Handle input and output in realtime + await asyncio.gather( + self._read(proc.stdout, verbose, True), + self._read(proc.stderr, verbose, False), + ) + + await proc.wait() + + if check and proc.returncode > 0: + raise subprocess.CalledProcessError(returncode=proc.returncode, + cmd=" ".join(cmd), + output=self.stdout, + stderr=self.stderr) + + if capture_output: + if proc.returncode > 0: + self.log(f"Error {proc.returncode}", verbose=True, is_info=False) + return "" + + return self.output + + ret = subprocess.CompletedProcess(args=cmd, + returncode=proc.returncode, + stdout=self.stdout, + stderr=self.stderr) + + return ret + + +class SphinxVenv: + """ + Installs Sphinx on one virtual env per Sphinx version with a minimal + set of dependencies, adjusting them to each specific version. + """ + + def __init__(self): + """Initialize instance variables""" + + self.built_time = {} + self.first_run = True + + async def _handle_version(self, args, fp, + cur_ver, cur_requirements, python_bin): + """Handle a single Sphinx version""" + + cmd = AsyncCommands(fp) + + ver = ".".join(map(str, cur_ver)) + + if not self.first_run and args.wait_input and args.build: + ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() + if ret == "a": + print("Aborted.") + sys.exit() + else: + self.first_run = False + + venv_dir = f"Sphinx_{ver}" + req_file = f"requirements_{ver}.txt" + + cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True) + + # Create venv + await cmd.run([python_bin, "-m", "venv", venv_dir], + verbose=args.verbose, check=True) + pip = os.path.join(venv_dir, "bin/pip") + + # Create install list + reqs = [] + for pkg, verstr in cur_requirements.items(): + reqs.append(f"{pkg}=={verstr}") + + reqs.append(f"Sphinx=={ver}") + + await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose) + + # Freeze environment + result = await cmd.run([pip, "freeze"], verbose=False, check=True) + + # Pip install succeeded. Write requirements file + if args.req_file: + with open(req_file, "w", encoding="utf-8") as fp: + fp.write(result.stdout) + + if args.build: + start_time = time.time() + + # Prepare a venv environment + env = os.environ.copy() + bin_dir = os.path.join(venv_dir, "bin") + env["PATH"] = bin_dir + ":" + env["PATH"] + env["VIRTUAL_ENV"] = venv_dir + if "PYTHONHOME" in env: + del env["PYTHONHOME"] + + # Test doc build + await cmd.run(["make", "cleandocs"], env=env, check=True) + make = ["make"] + + if args.output: + sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build") + make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"] + + if args.make_args: + make += args.make_args + + make += args.targets + + if args.verbose: + cmd.log(f". {bin_dir}/activate", verbose=True) + await cmd.run(make, env=env, check=True, verbose=True) + if args.verbose: + cmd.log("deactivate", verbose=True) + + end_time = time.time() + elapsed_time = end_time - start_time + hours, minutes = divmod(elapsed_time, 3600) + minutes, seconds = divmod(minutes, 60) + + hours = int(hours) + minutes = int(minutes) + seconds = int(seconds) + + self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + + cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True) + + async def run(self, args): + """ + Navigate though multiple Sphinx versions, handling each of them + on a loop. + """ + + if args.log: + fp = open(args.log, "w", encoding="utf-8") + if not args.verbose: + args.verbose = False + else: + fp = None + if not args.verbose: + args.verbose = True + + cur_requirements = {} + python_bin = min_python_bin + + vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions) + + for cur_ver in sorted(vers): + if cur_ver in SPHINX_REQUIREMENTS: + new_reqs = SPHINX_REQUIREMENTS[cur_ver] + cur_requirements.update(new_reqs) + + if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715 + python_bin = PYTHON_VER_CHANGES[cur_ver] + + if cur_ver not in args.versions: + continue + + if args.min_version: + if cur_ver < args.min_version: + continue + + if args.max_version: + if cur_ver > args.max_version: + break + + await self._handle_version(args, fp, cur_ver, cur_requirements, + python_bin) + + if args.build: + cmd = AsyncCommands(fp) + cmd.log("\nSummary:", verbose=True) + for ver, elapsed_time in sorted(self.built_time.items()): + cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}", + verbose=True) + + if fp: + fp.close() + +def parse_version(ver_str): + """Convert a version string into a tuple.""" + + return tuple(map(int, ver_str.split("."))) + + +DEFAULT_VERS = " - " +DEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}", + DEFAULT_VERSIONS_TO_TEST)) + +SCRIPT = os.path.relpath(__file__) + +DESCRIPTION = f""" +This tool allows creating Python virtual environments for different +Sphinx versions that are supported by the Linux Kernel build system. + +Besides creating the virtual environment, it can also test building +the documentation using "make htmldocs" (and/or other doc targets). + +If called without "--versions" argument, it covers the versions shipped +on major distros, plus the lowest supported version: + +{DEFAULT_VERS} + +A typical usage is to run: + + {SCRIPT} -m -l sphinx_builds.log + +This will create one virtual env for the default version set and run +"make htmldocs" for each version, creating a log file with the +excecuted commands on it. + +NOTE: The build time can be very long, specially on old versions. Also, there +is a known bug with Sphinx version 6.0.x: each subprocess uses a lot of +memory. That, together with "-jauto" may cause OOM killer to cause +failures at the doc generation. To minimize the risk, you may use the +"-a" command line parameter to constrain the built directories and/or +reduce the number of threads from "-jauto" to, for instance, "-j4": + + {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'" + +""" + +MAKE_TARGETS = [ + "htmldocs", + "texinfodocs", + "infodocs", + "latexdocs", + "pdfdocs", + "epubdocs", + "xmldocs", +] + +async def main(): + """Main program""" + + parser = argparse.ArgumentParser(description=DESCRIPTION, + formatter_class=argparse.RawDescriptionHelpFormatter) + + ver_group = parser.add_argument_group("Version range options") + + ver_group.add_argument('-V', '--versions', nargs="*", + default=DEFAULT_VERSIONS_TO_TEST,type=parse_version, + help='Sphinx versions to test') + ver_group.add_argument('--min-version', "--min", type=parse_version, + help='Sphinx minimal version') + ver_group.add_argument('--max-version', "--max", type=parse_version, + help='Sphinx maximum version') + ver_group.add_argument('-f', '--full', action='store_true', + help='Add all Sphinx (major,minor) supported versions to the version range') + + build_group = parser.add_argument_group("Build options") + + build_group.add_argument('-b', '--build', action='store_true', + help='Build documentation') + build_group.add_argument('-a', '--make-args', nargs="*", + help='extra arguments for make, like SPHINXDIRS=netlink/specs', + ) + build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS, + default=[MAKE_TARGETS[0]], + help="make build targets. Default: htmldocs.") + build_group.add_argument("-o", '--output', + help="output directory for the make O=OUTPUT") + + other_group = parser.add_argument_group("Other options") + + other_group.add_argument('-r', '--req-file', action='store_true', + help='write a requirements.txt file') + other_group.add_argument('-l', '--log', + help='Log command output on a file') + other_group.add_argument('-v', '--verbose', action='store_true', + help='Verbose all commands') + other_group.add_argument('-i', '--wait-input', action='store_true', + help='Wait for an enter before going to the next version') + + args = parser.parse_args() + + if not args.make_args: + args.make_args = [] + + sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys())) + + if args.full: + args.versions += list(SPHINX_REQUIREMENTS.keys()) + + venv = SphinxVenv() + await venv.run(args) + + +# Call main method +if __name__ == "__main__": + asyncio.run(main()) diff --git a/scripts/ver_linux b/scripts/ver_linux index 1a8ee4ff0e32..d6f2362d3792 100755 --- a/scripts/ver_linux +++ b/scripts/ver_linux @@ -25,8 +25,6 @@ BEGIN { printversion("Module-init-tools", version("depmod -V")) printversion("E2fsprogs", version("tune2fs")) printversion("Jfsutils", version("fsck.jfs -V")) - printversion("Reiserfsprogs", version("reiserfsck -V")) - printversion("Reiser4fsprogs", version("fsck.reiser4 -V")) printversion("Xfsprogs", version("xfs_db -V")) printversion("Pcmciautils", version("pccardctl -V")) printversion("Pcmcia-cs", version("cardmgr -V")) diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index fd1238753cad..b9a5bc3430aa 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -82,10 +82,13 @@ choice endchoice -config GCC_PLUGIN_STACKLEAK +config CC_HAS_SANCOV_STACK_DEPTH_CALLBACK + def_bool $(cc-option,-fsanitize-coverage-stack-depth-callback-min=1) + +config KSTACK_ERASE bool "Poison kernel stack before returning from syscalls" - depends on GCC_PLUGINS - depends on HAVE_ARCH_STACKLEAK + depends on HAVE_ARCH_KSTACK_ERASE + depends on GCC_PLUGINS || CC_HAS_SANCOV_STACK_DEPTH_CALLBACK help This option makes the kernel erase the kernel stack before returning from system calls. This has the effect of leaving @@ -103,6 +106,10 @@ config GCC_PLUGIN_STACKLEAK are advised to test this feature on your expected workload before deploying it. +config GCC_PLUGIN_STACKLEAK + def_bool KSTACK_ERASE + depends on GCC_PLUGINS + help This plugin was ported from grsecurity/PaX. More information at: * https://grsecurity.net/ * https://pax.grsecurity.net/ @@ -117,37 +124,37 @@ config GCC_PLUGIN_STACKLEAK_VERBOSE instrumented. This is useful for comparing coverage between builds. -config STACKLEAK_TRACK_MIN_SIZE - int "Minimum stack frame size of functions tracked by STACKLEAK" +config KSTACK_ERASE_TRACK_MIN_SIZE + int "Minimum stack frame size of functions tracked by KSTACK_ERASE" default 100 range 0 4096 - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help - The STACKLEAK gcc plugin instruments the kernel code for tracking + The KSTACK_ERASE option instruments the kernel code for tracking the lowest border of the kernel stack (and for some other purposes). - It inserts the stackleak_track_stack() call for the functions with - a stack frame size greater than or equal to this parameter. + It inserts the __sanitizer_cov_stack_depth() call for the functions + with a stack frame size greater than or equal to this parameter. If unsure, leave the default value 100. -config STACKLEAK_METRICS - bool "Show STACKLEAK metrics in the /proc file system" - depends on GCC_PLUGIN_STACKLEAK +config KSTACK_ERASE_METRICS + bool "Show KSTACK_ERASE metrics in the /proc file system" + depends on KSTACK_ERASE depends on PROC_FS help - If this is set, STACKLEAK metrics for every task are available in - the /proc file system. In particular, /proc//stack_depth + If this is set, KSTACK_ERASE metrics for every task are available + in the /proc file system. In particular, /proc//stack_depth shows the maximum kernel stack consumption for the current and previous syscalls. Although this information is not precise, it - can be useful for estimating the STACKLEAK performance impact for - your workloads. + can be useful for estimating the KSTACK_ERASE performance impact + for your workloads. -config STACKLEAK_RUNTIME_DISABLE +config KSTACK_ERASE_RUNTIME_DISABLE bool "Allow runtime disabling of kernel stack erasing" - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help This option provides 'stack_erasing' sysctl, which can be used in runtime to control kernel stack erasing for kernels built with - CONFIG_GCC_PLUGIN_STACKLEAK. + CONFIG_KSTACK_ERASE. config INIT_ON_ALLOC_DEFAULT_ON bool "Enable heap memory zeroing on allocation by default" diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 64cc3044a42c..1e3bd44643da 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -59,8 +59,7 @@ config SECURITY_APPARMOR_INTROSPECT_POLICY config SECURITY_APPARMOR_HASH bool "Enable introspection of sha256 hashes for loaded profiles" depends on SECURITY_APPARMOR_INTROSPECT_POLICY - select CRYPTO - select CRYPTO_SHA256 + select CRYPTO_LIB_SHA256 default y help This option selects whether introspection of loaded policy diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index b9c5879dd599..12fb419714c0 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ - policy_compat.o + policy_compat.o af_unix.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o @@ -28,7 +28,7 @@ clean-files := capability_names.h rlim_names.h net_names.h # to # #define AA_SFS_AF_MASK "local inet" quiet_cmd_make-af = GEN $@ -cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ +cmd_make-af = echo "static const char *const address_family_names[] = {" > $@ ;\ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ echo "};" >> $@ ;\ @@ -43,7 +43,7 @@ cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ # to # [1] = "stream", quiet_cmd_make-sock = GEN $@ -cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ +cmd_make-sock = echo "static const char *const sock_type_names[] = {" >> $@ ;\ sed $^ >>$@ -r -n \ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ echo "};" >> $@ diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000000..9129766d1e9c --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2023 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include + +#include "include/audit.h" +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/file.h" +#include "include/label.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/cred.h" + + +static inline struct sock *aa_unix_sk(struct unix_sock *u) +{ + return &u->sk; +} + +static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred, + struct aa_label *label, struct path *path) +{ + AA_BUG(!label); + AA_BUG(!path); + + if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + /* if !u->path.dentry socket is being shutdown - implicit delegation + * until obj delegation is supported + */ + if (path->dentry) { + /* the sunpath may not be valid for this ns so use the path */ + struct inode *inode = path->dentry->d_inode; + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(path->mnt), inode); + struct path_cond cond = { + .uid = vfsuid_into_kuid(vfsuid), + .mode = inode->i_mode, + }; + + return aa_path_perm(op, subj_cred, label, path, + PATH_SOCK_COND, mask, &cond); + } /* else implicitly delegated */ + + return 0; +} + +/* match_addr special constants */ +#define ABSTRACT_ADDR "\x00" /* abstract socket addr */ +#define ANONYMOUS_ADDR "\x01" /* anonymous endpoint, no addr */ +#define DISCONNECTED_ADDR "\x02" /* addr is another namespace */ +#define SHUTDOWN_ADDR "\x03" /* path addr is shutdown and cleared */ +#define FS_ADDR "/" /* path addr in fs */ + +static aa_state_t match_addr(struct aa_dfa *dfa, aa_state_t state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(dfa, state, addr->sun_path, + unix_addr_len(addrlen)); + else + state = aa_dfa_match_len(dfa, state, ANONYMOUS_ADDR, 1); + /* todo: could change to out of band for cleaner separation */ + state = aa_dfa_null_transition(dfa, state); + + return state; +} + +static aa_state_t match_to_local(struct aa_policydb *policy, + aa_state_t state, u32 request, + int type, int protocol, + struct sockaddr_un *addr, int addrlen, + struct aa_perms **p, + const char **info) +{ + state = aa_match_to_prot(policy, state, request, PF_UNIX, type, + protocol, NULL, info); + if (state) { + state = match_addr(policy->dfa, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(policy->dfa, state); + if (!state) + *info = "failed local label match"; + } else { + *info = "failed local address match"; + } + } + + return state; +} + +struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen) +{ + struct unix_address *addr; + + /* memory barrier is sufficient see note in net/unix/af_unix.c */ + addr = smp_load_acquire(&u->addr); + if (addr) { + *addrlen = addr->len; + return addr->name; + } + *addrlen = 0; + return NULL; +} + +static aa_state_t match_to_sk(struct aa_policydb *policy, + aa_state_t state, u32 request, + struct unix_sock *u, struct aa_perms **p, + const char **info) +{ + int addrlen; + struct sockaddr_un *addr = aa_sunaddr(u, &addrlen); + + return match_to_local(policy, state, request, u->sk.sk_type, + u->sk.sk_protocol, addr, addrlen, p, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static aa_state_t match_to_cmd(struct aa_policydb *policy, aa_state_t state, + u32 request, struct unix_sock *u, + char cmd, struct aa_perms **p, + const char **info) +{ + AA_BUG(!p); + + state = match_to_sk(policy, state, request, u, p, info); + if (state && !*p) { + state = aa_dfa_match_len(policy->dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static aa_state_t match_to_peer(struct aa_policydb *policy, aa_state_t state, + u32 request, struct unix_sock *u, + struct sockaddr_un *peer_addr, int peer_addrlen, + struct aa_perms **p, const char **info) +{ + AA_BUG(!p); + + state = match_to_cmd(policy, state, request, u, CMD_ADDR, p, info); + if (state && !*p) { + state = match_addr(policy->dfa, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + + return state; +} + +static aa_state_t match_label(struct aa_profile *profile, + struct aa_ruleset *rule, aa_state_t state, + u32 request, struct aa_profile *peer, + struct aa_perms *p, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + ad->peer = &peer->label; + + if (state && !p) { + state = aa_dfa_match(rule->policy->dfa, state, + peer->base.hname); + if (!state) + ad->info = "failed peer label match"; + + } + + return aa_do_perms(profile, rule->policy, state, request, p, ad); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + * v8 - same as v7 + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + state = aa_match_to_prot(rules->policy, state, AA_MAY_CREATE, + PF_UNIX, type, protocol, NULL, + &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_CREATE, + NULL, ad); + } + + return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type, + protocol); +} + +static int profile_sk_perm(struct aa_profile *profile, + struct apparmor_audit_data *ad, + u32 request, struct sock *sk, struct path *path) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, + &unix_sk(sk)->path); + + state = match_to_sk(rules->policy, state, request, unix_sk(sk), + &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, request, p, + ad); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + if (is_unix_addr_fs(ad->net.addr, ad->net.addrlen)) + /* under v7-9 fs hook handles bind */ + return 0; + /* bind for abstract socket */ + state = match_to_local(rules->policy, state, AA_MAY_BIND, + sk->sk_type, sk->sk_protocol, + unix_addr(ad->net.addr), + ad->net.addrlen, + &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_BIND, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk); +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog, struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + __be16 b = cpu_to_be16(backlog); + + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, AA_MAY_LISTEN, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_cmd(rules->policy, state, AA_MAY_LISTEN, + unix_sk(sk), CMD_LISTEN, &p, &ad->info); + if (state && !p) { + state = aa_dfa_match_len(rules->policy->dfa, state, + (char *) &b, 2); + if (!state) + ad->info = "failed listen backlog match"; + } + return aa_do_perms(profile, rules->policy, state, AA_MAY_LISTEN, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk); +} + +static int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, AA_MAY_ACCEPT, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_sk(rules->policy, state, AA_MAY_ACCEPT, + unix_sk(sk), &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_ACCEPT, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk); +} + +static int profile_opt_perm(struct aa_profile *profile, u32 request, + struct sock *sk, int optname, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + __be16 b = cpu_to_be16(optname); + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, request, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_cmd(rules->policy, state, request, unix_sk(sk), + CMD_OPT, &p, &ad->info); + if (state && !p) { + state = aa_dfa_match_len(rules->policy->dfa, state, + (char *) &b, 2); + if (!state) + ad->info = "failed sockopt match"; + } + return aa_do_perms(profile, rules->policy, state, request, p, + ad); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, u32 request, + struct sock *sk, struct path *path, + struct sockaddr_un *peer_addr, + int peer_addrlen, struct path *peer_path, + struct aa_label *peer_label, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_label); + AA_BUG(!ad); + + state = RULE_MEDIATES_v9NET(rules); + if (state) { + struct aa_profile *peerp; + + if (peer_path) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, peer_path); + else if (path) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, path); + state = match_to_peer(rules->policy, state, request, + unix_sk(sk), + peer_addr, peer_addrlen, &p, &ad->info); + + return fn_for_each_in_ns(peer_label, peerp, + match_label(profile, rules, state, request, + peerp, p, ad)); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* -------------------------------- */ + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + if (!unconfined(label)) { + struct aa_profile *profile; + DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family, + type, protocol); + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, + protocol, &ad)); + } + + return 0; +} + +static int aa_unix_label_sk_perm(const struct cred *subj_cred, + struct aa_label *label, + const char *op, u32 request, struct sock *sk, + struct path *path) +{ + if (!unconfined(label)) { + struct aa_profile *profile; + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, &ad, request, sk, + path)); + } + return 0; +} + +/* revalidation, get/set attr, shutdown */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + int error; + + label = begin_current_label_crit_section(); + error = aa_unix_label_sk_perm(current_cred(), label, op, + request, sock->sk, + is_unix_fs(sock->sk) ? &unix_sk(sock->sk)->path : NULL); + end_current_label_crit_section(label); + + return error; +} + +static int valid_addr(struct sockaddr *addr, int addr_len) +{ + struct sockaddr_un *sunaddr = unix_addr(addr); + + /* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */ + if (addr_len < offsetof(struct sockaddr_un, sun_path) || + addr_len > sizeof(*sunaddr)) + return -EINVAL; + return 0; +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + error = valid_addr(addr, addrlen); + if (error) + return error; + + label = begin_current_label_crit_section(); + /* fs bind is handled by mknod */ + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_BIND, current_cred(), sock->sk); + + ad.net.addr = unix_addr(addr); + ad.net.addrlen = addrlen; + + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, &ad)); + } + end_current_label_crit_section(label); + + return error; +} + +/* + * unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + * This is just here to document this is not needed for af_unix + * +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 0; +} +*/ + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_LISTEN, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog, &ad)); + } + end_current_label_crit_section(label); + + return error; +} + + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_ACCEPT, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, &ad)); + } + end_current_label_crit_section(label); + + return error; +} + + +/* + * dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here, but connect + socket transfer is + * sufficient. This is just here to document this is not needed for af_unix + * + * sendmsg, recvmsg +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} +*/ + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, op, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, request, sock->sk, + optname, &ad)); + } + end_current_label_crit_section(label); + + return error; +} + +static int unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct path *path, + struct sockaddr_un *peer_addr, int peer_addrlen, + struct path *peer_path, struct aa_label *peer_label) +{ + struct aa_profile *profile; + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); + + ad.net.peer.addr = peer_addr; + ad.net.peer.addrlen = peer_addrlen; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, request, sk, path, + peer_addr, peer_addrlen, peer_path, + peer_label, &ad)); +} + +/** + * + * Requires: lock held on both @sk and @peer_sk + * called by unix_stream_connect, unix_may_send + */ +int aa_unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + int plen; + struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), &plen); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(!peer_label); + + return unix_peer_perm(subj_cred, label, op, request, sk, + is_unix_fs(sk) ? &u->path : NULL, + paddr, plen, + is_unix_fs(peer_sk) ? &peeru->path : NULL, + peer_label); +} + +/* sk_plabel for comparison only */ +static void update_sk_ctx(struct sock *sk, struct aa_label *label, + struct aa_label *plabel) +{ + struct aa_label *l, *old; + struct aa_sk_ctx *ctx = aa_sock(sk); + bool update_sk; + + rcu_read_lock(); + update_sk = (plabel && + (plabel != rcu_access_pointer(ctx->peer_lastupdate) || + !aa_label_is_subset(plabel, rcu_dereference(ctx->peer)))) || + !__aa_subj_label_is_cached(label, rcu_dereference(ctx->label)); + rcu_read_unlock(); + if (!update_sk) + return; + + spin_lock(&unix_sk(sk)->lock); + old = rcu_dereference_protected(ctx->label, + lockdep_is_held(&unix_sk(sk)->lock)); + l = aa_label_merge(old, label, GFP_ATOMIC); + if (l) { + if (l != old) { + rcu_assign_pointer(ctx->label, l); + aa_put_label(old); + } else + aa_put_label(l); + } + if (plabel && rcu_access_pointer(ctx->peer_lastupdate) != plabel) { + old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock)); + + if (old == plabel) { + rcu_assign_pointer(ctx->peer_lastupdate, plabel); + } else if (aa_label_is_subset(plabel, old)) { + rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer, aa_get_label(plabel)); + aa_put_label(old); + } /* else race or a subset - don't update */ + } + spin_unlock(&unix_sk(sk)->lock); +} + +static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx, + struct aa_label *label) +{ + struct aa_label *l, *old; + + spin_lock(&unix_sk(sk)->lock); + old = rcu_dereference_protected(ctx->peer, + lockdep_is_held(&unix_sk(sk)->lock)); + l = aa_label_merge(old, label, GFP_ATOMIC); + if (l) { + if (l != old) { + rcu_assign_pointer(ctx->peer, l); + aa_put_label(old); + } else + aa_put_label(l); + } + spin_unlock(&unix_sk(sk)->lock); +} + +/* This fn is only checked if something has changed in the security + * boundaries. Otherwise cached info off file is sufficient + */ +int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, struct file *file) +{ + struct socket *sock = (struct socket *) file->private_data; + struct sockaddr_un *addr, *peer_addr; + int addrlen, peer_addrlen; + struct aa_label *plabel = NULL; + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + struct path path; + bool is_sk_fs; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != PF_UNIX); + + /* investigate only using lock via unix_peer_get() + * addr only needs the memory barrier, but need to investigate + * path + */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + + is_sk_fs = is_unix_fs(sock->sk); + addr = aa_sunaddr(unix_sk(sock->sk), &addrlen); + path = unix_sk(sock->sk)->path; + unix_state_unlock(sock->sk); + + if (is_sk_fs && peer_sk) + sk_req = request; + if (sk_req) { + error = aa_unix_label_sk_perm(subj_cred, label, op, + sk_req, sock->sk, + is_sk_fs ? &path : NULL); + } + if (!peer_sk) + goto out; + + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + + struct path peer_path; + + peer_path = unix_sk(peer_sk)->path; + if (!is_sk_fs && is_unix_fs(peer_sk)) { + last_error(error, + unix_fs_perm(op, request, subj_cred, label, + is_unix_fs(peer_sk) ? &peer_path : NULL)); + } else if (!is_sk_fs) { + struct aa_label *plabel; + struct aa_sk_ctx *pctx = aa_sock(peer_sk); + + rcu_read_lock(); + plabel = aa_get_label_rcu(&pctx->label); + rcu_read_unlock(); + /* no fs check of aa_unix_peer_perm because conditions above + * ensure they will never be done + */ + last_error(error, + xcheck(unix_peer_perm(subj_cred, label, op, + MAY_READ | MAY_WRITE, sock->sk, + is_sk_fs ? &path : NULL, + peer_addr, peer_addrlen, + is_unix_fs(peer_sk) ? + &peer_path : NULL, + plabel), + unix_peer_perm(file->f_cred, plabel, op, + MAY_READ | MAY_WRITE, peer_sk, + is_unix_fs(peer_sk) ? + &peer_path : NULL, + addr, addrlen, + is_sk_fs ? &path : NULL, + label))); + if (!error && !__aa_subj_label_is_cached(plabel, label)) + update_peer_ctx(peer_sk, pctx, label); + } + sock_put(peer_sk); + +out: + + /* update peer cache to latest successful perm check */ + if (error == 0) + update_sk_ctx(sock->sk, label, plabel); + aa_put_label(plabel); + + return error; +} + diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0aef34b9609b..391a586d0557 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -43,7 +43,7 @@ * The interface is split into two main components based on their function * a securityfs component: * used for static files that are always available, and which allows - * userspace to specificy the location of the security filesystem. + * userspace to specify the location of the security filesystem. * * fns and data are prefixed with * aa_sfs_ @@ -204,7 +204,7 @@ static struct file_system_type aafs_ops = { /** * __aafs_setup_d_inode - basic inode setup for apparmorfs * @dir: parent directory for the dentry - * @dentry: dentry we are seting the inode up for + * @dentry: dentry we are setting the inode up for * @mode: permissions the file should have * @data: data to store on inode.i_private, available in open() * @link: if symlink, symlink target string @@ -612,8 +612,7 @@ static const struct file_operations aa_fs_ns_revision_fops = { static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms, const char *match_str, size_t match_len) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_perms tmp = { }; aa_state_t state = DFA_NOMATCH; @@ -626,11 +625,20 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms, if (state) { struct path_cond cond = { }; - tmp = *(aa_lookup_fperms(rules->file, state, &cond)); + tmp = *(aa_lookup_condperms(current_fsuid(), + rules->file, state, &cond)); } } else if (rules->policy->dfa) { if (!RULE_MEDIATES(rules, *match_str)) return; /* no change to current perms */ + /* old user space does not correctly detect dbus mediation + * support so we may get dbus policy and requests when + * the abi doesn't support it. This can cause mediation + * regressions, so explicitly test for this situation. + */ + if (*match_str == AA_CLASS_DBUS && + !RULE_MEDIATES_v9NET(rules)) + return; /* no change to current perms */ state = aa_dfa_match_len(rules->policy->dfa, rules->policy->start[0], match_str, match_len); @@ -997,7 +1005,7 @@ static int aa_sfs_seq_show(struct seq_file *seq, void *v) switch (fs_file->v_type) { case AA_SFS_TYPE_BOOLEAN: - seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no"); + seq_printf(seq, "%s\n", str_yes_no(fs_file->v.boolean)); break; case AA_SFS_TYPE_STRING: seq_printf(seq, "%s\n", fs_file->v.string); @@ -1006,7 +1014,7 @@ static int aa_sfs_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%#08lx\n", fs_file->v.u64); break; default: - /* Ignore unpritable entry types. */ + /* Ignore unprintable entry types. */ break; } @@ -1152,7 +1160,7 @@ static int seq_ns_stacked_show(struct seq_file *seq, void *v) struct aa_label *label; label = begin_current_label_crit_section(); - seq_printf(seq, "%s\n", label->size > 1 ? "yes" : "no"); + seq_printf(seq, "%s\n", str_yes_no(label->size > 1)); end_current_label_crit_section(label); return 0; @@ -1175,7 +1183,7 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v) } } - seq_printf(seq, "%s\n", count > 1 ? "yes" : "no"); + seq_printf(seq, "%s\n", str_yes_no(count > 1)); end_current_label_crit_section(label); return 0; @@ -2244,7 +2252,7 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos) /** * p_stop - stop depth first traversal * @f: seq_file we are filling - * @p: the last profile writen + * @p: the last profile written * * Release all locking done by p_start/p_next on namespace tree */ @@ -2332,6 +2340,7 @@ static struct aa_sfs_entry aa_sfs_entry_attach[] = { static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("change_hat", 1), AA_SFS_FILE_BOOLEAN("change_hatv", 1), + AA_SFS_FILE_BOOLEAN("unconfined_allowed_children", 1), AA_SFS_FILE_BOOLEAN("change_onexec", 1), AA_SFS_FILE_BOOLEAN("change_profile", 1), AA_SFS_FILE_BOOLEAN("stack", 1), @@ -2340,6 +2349,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("computed_longest_left", 1), AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach), AA_SFS_FILE_BOOLEAN("disconnected.path", 1), + AA_SFS_FILE_BOOLEAN("kill.signal", 1), AA_SFS_FILE_STRING("version", "1.2"), { } }; @@ -2364,7 +2374,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { AA_SFS_FILE_BOOLEAN("set_load", 1), /* number of out of band transitions supported */ AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), - AA_SFS_FILE_U64("permstable32_version", 1), + AA_SFS_FILE_U64("permstable32_version", 3), AA_SFS_FILE_STRING("permstable32", PERMS32STR), AA_SFS_FILE_U64("state32", 1), AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined), @@ -2384,6 +2394,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { + AA_SFS_FILE_STRING("mask", "acquire send receive"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_BOOLEAN("data", 1), @@ -2406,6 +2421,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), AA_SFS_DIR("network_v8", aa_sfs_entry_network), + AA_SFS_DIR("network_v9", aa_sfs_entry_networkv9), AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), @@ -2413,6 +2429,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), AA_SFS_DIR("query", aa_sfs_entry_query), AA_SFS_DIR("io_uring", aa_sfs_entry_io_uring), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 73087d76f649..ac89602aa2d9 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -192,7 +192,7 @@ int aa_audit(int type, struct aa_profile *profile, aa_audit_msg(type, ad, cb); if (ad->type == AUDIT_APPARMOR_KILL) - (void)send_sig_info(SIGKILL, NULL, + (void)send_sig_info(profile->signal, NULL, ad->common.type == LSM_AUDIT_DATA_TASK && ad->common.u.tsk ? ad->common.u.tsk : current); diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 7ca489ee1054..b9ea6bc45c1a 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -27,6 +27,7 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = { AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK), + AA_SFS_FILE_BOOLEAN("extended", 1), { } }; @@ -68,8 +69,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile { const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */ - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; @@ -121,10 +121,32 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile static int profile_capable(struct aa_profile *profile, int cap, unsigned int opts, struct apparmor_audit_data *ad) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; + aa_state_t state; int error; + state = RULE_MEDIATES(rules, ad->class); + if (state) { + struct aa_perms perms = { }; + u32 request; + + /* caps broken into 256 x 32 bit permission chunks */ + state = aa_dfa_next(rules->policy->dfa, state, cap >> 5); + request = 1 << (cap & 0x1f); + perms = *aa_lookup_perms(rules->policy, state); + aa_apply_modes_to_perms(profile, &perms); + + if (opts & CAP_OPT_NOAUDIT) { + if (perms.complain & request) + ad->info = "optional: no audit"; + else + ad = NULL; + } + return aa_check_perms(profile, &perms, request, ad, + audit_cb); + } + + /* fallback to old caps mediation that doesn't support conditionals */ if (cap_raised(rules->caps.allow, cap) && !cap_raised(rules->caps.denied, cap)) error = 0; @@ -168,3 +190,34 @@ int aa_capable(const struct cred *subj_cred, struct aa_label *label, return error; } + +kernel_cap_t aa_profile_capget(struct aa_profile *profile) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + aa_state_t state; + + state = RULE_MEDIATES(rules, AA_CLASS_CAP); + if (state) { + kernel_cap_t caps = CAP_EMPTY_SET; + int i; + + /* caps broken into up to 256, 32 bit permission chunks */ + for (i = 0; i < (CAP_LAST_CAP >> 5); i++) { + struct aa_perms perms = { }; + aa_state_t tmp; + + tmp = aa_dfa_next(rules->policy->dfa, state, i); + perms = *aa_lookup_perms(rules->policy, tmp); + aa_apply_modes_to_perms(profile, &perms); + caps.val |= ((u64)(perms.allow)) << (i * 5); + caps.val |= ((u64)(perms.complain)) << (i * 5); + } + return caps; + } + + /* fallback to old caps */ + if (COMPLAIN_MODE(profile)) + return CAP_FULL_SET; + + return rules->caps.allow; +} diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index aad486b2fca6..227d47c14907 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -11,113 +11,52 @@ * it should be. */ -#include +#include #include "include/apparmor.h" #include "include/crypto.h" -static unsigned int apparmor_hash_size; - -static struct crypto_shash *apparmor_tfm; - unsigned int aa_hash_size(void) { - return apparmor_hash_size; + return SHA256_DIGEST_SIZE; } char *aa_calc_hash(void *data, size_t len) { - SHASH_DESC_ON_STACK(desc, apparmor_tfm); char *hash; - int error; - if (!apparmor_tfm) - return NULL; - - hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + hash = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); if (!hash) return ERR_PTR(-ENOMEM); - desc->tfm = apparmor_tfm; - - error = crypto_shash_init(desc); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) data, len); - if (error) - goto fail; - error = crypto_shash_final(desc, hash); - if (error) - goto fail; - + sha256(data, len, hash); return hash; - -fail: - kfree(hash); - - return ERR_PTR(error); } int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { - SHASH_DESC_ON_STACK(desc, apparmor_tfm); - int error; + struct sha256_ctx sctx; __le32 le32_version = cpu_to_le32(version); if (!aa_g_hash_policy) return 0; - if (!apparmor_tfm) - return 0; - - profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + profile->hash = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); if (!profile->hash) return -ENOMEM; - desc->tfm = apparmor_tfm; - - error = crypto_shash_init(desc); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) &le32_version, 4); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) start, len); - if (error) - goto fail; - error = crypto_shash_final(desc, profile->hash); - if (error) - goto fail; - + sha256_init(&sctx); + sha256_update(&sctx, (u8 *)&le32_version, 4); + sha256_update(&sctx, (u8 *)start, len); + sha256_final(&sctx, profile->hash); return 0; - -fail: - kfree(profile->hash); - profile->hash = NULL; - - return error; } static int __init init_profile_hash(void) { - struct crypto_shash *tfm; - - if (!apparmor_initialized) - return 0; - - tfm = crypto_alloc_shash("sha256", 0, 0); - if (IS_ERR(tfm)) { - int error = PTR_ERR(tfm); - AA_ERROR("failed to setup profile sha256 hashing: %d\n", error); - return error; - } - apparmor_tfm = tfm; - apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm); - - aa_info_message("AppArmor sha256 policy hashing enabled"); - + if (apparmor_initialized) + aa_info_message("AppArmor sha256 policy hashing enabled"); return 0; } - late_initcall(init_profile_hash); diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 5939bd9a9b9b..267da82afb14 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -28,6 +28,12 @@ #include "include/policy.h" #include "include/policy_ns.h" +static const char * const CONFLICTING_ATTACH_STR = "conflicting profile attachments"; +static const char * const CONFLICTING_ATTACH_STR_IX = + "conflicting profile attachments - ix fallback"; +static const char * const CONFLICTING_ATTACH_STR_UX = + "conflicting profile attachments - ux fallback"; + /** * may_change_ptraced_domain - check if can change profile on ptraced task * @to_cred: cred of task changing domain @@ -87,8 +93,7 @@ static inline aa_state_t match_component(struct aa_profile *profile, struct aa_profile *tp, bool stack, aa_state_t state) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; const char *ns_name; if (stack) @@ -125,8 +130,7 @@ static int label_compound_match(struct aa_profile *profile, aa_state_t state, bool subns, u32 request, struct aa_perms *perms) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_profile *tp; struct label_it i; struct path_cond cond = { }; @@ -154,7 +158,8 @@ static int label_compound_match(struct aa_profile *profile, if (!state) goto fail; } - *perms = *(aa_lookup_fperms(rules->file, state, &cond)); + *perms = *(aa_lookup_condperms(current_fsuid(), rules->file, state, + &cond)); aa_apply_modes_to_perms(profile, perms); if ((perms->allow & request) != request) return -EACCES; @@ -187,8 +192,7 @@ static int label_components_match(struct aa_profile *profile, aa_state_t start, bool subns, u32 request, struct aa_perms *perms) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_profile *tp; struct label_it i; struct aa_perms tmp; @@ -209,7 +213,8 @@ static int label_components_match(struct aa_profile *profile, return 0; next: - tmp = *(aa_lookup_fperms(rules->file, state, &cond)); + tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, + &cond)); aa_apply_modes_to_perms(profile, &tmp); aa_perms_accum(perms, &tmp); label_for_each_cont(i, label, tp) { @@ -218,7 +223,8 @@ static int label_components_match(struct aa_profile *profile, state = match_component(profile, tp, stack, start); if (!state) goto fail; - tmp = *(aa_lookup_fperms(rules->file, state, &cond)); + tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, + &cond)); aa_apply_modes_to_perms(profile, &tmp); aa_perms_accum(perms, &tmp); } @@ -323,7 +329,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i], &value, value_size, GFP_KERNEL); if (size >= 0) { - u32 index, perm; + struct aa_perms *perms; /* * Check the xattr presence before value. This ensure @@ -335,9 +341,8 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, /* Check xattr value */ state = aa_dfa_match_len(attach->xmatch->dfa, state, value, size); - index = ACCEPT_TABLE(attach->xmatch->dfa)[state]; - perm = attach->xmatch->perms[index].allow; - if (!(perm & MAY_EXEC)) { + perms = aa_lookup_perms(attach->xmatch, state); + if (!(perms->allow & MAY_EXEC)) { ret = -EINVAL; goto out; } @@ -415,15 +420,14 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, if (attach->xmatch->dfa) { unsigned int count; aa_state_t state; - u32 index, perm; + struct aa_perms *perms; state = aa_dfa_leftmatch(attach->xmatch->dfa, attach->xmatch->start[AA_CLASS_XMATCH], name, &count); - index = ACCEPT_TABLE(attach->xmatch->dfa)[state]; - perm = attach->xmatch->perms[index].allow; + perms = aa_lookup_perms(attach->xmatch, state); /* any accepting state means a valid match. */ - if (perm & MAY_EXEC) { + if (perms->allow & MAY_EXEC) { int ret = 0; if (count < candidate_len) @@ -484,7 +488,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, if (!candidate || conflict) { if (conflict) - *info = "conflicting profile attachments"; + *info = CONFLICTING_ATTACH_STR; rcu_read_unlock(); return NULL; } @@ -508,15 +512,16 @@ static const char *next_name(int xtype, const char *name) * @name: returns: name tested to find label (NOT NULL) * * Returns: refcounted label, or NULL on failure (MAYBE NULL) + * @name will always be set with the last name tried */ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, const char **name) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_label *label = NULL; u32 xtype = xindex & AA_X_TYPE_MASK; int index = xindex & AA_X_INDEX_MASK; + const char *next; AA_BUG(!name); @@ -524,25 +529,27 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, /* TODO: move lookup parsing to unpack time so this is a straight * index into the resultant label */ - for (*name = rules->file->trans.table[index]; !label && *name; - *name = next_name(xtype, *name)) { + for (next = rules->file->trans.table[index]; next; + next = next_name(xtype, next)) { + const char *lookup = (*next == '&') ? next + 1 : next; + *name = next; if (xindex & AA_X_CHILD) { - struct aa_profile *new_profile; - /* release by caller */ - new_profile = aa_find_child(profile, *name); - if (new_profile) - label = &new_profile->label; + /* TODO: switich to parse to get stack of child */ + struct aa_profile *new = aa_find_child(profile, lookup); + + if (new) + /* release by caller */ + return &new->label; continue; } - label = aa_label_parse(&profile->label, *name, GFP_KERNEL, + label = aa_label_parse(&profile->label, lookup, GFP_KERNEL, true, false); - if (IS_ERR(label)) - label = NULL; + if (!IS_ERR_OR_NULL(label)) + /* release by caller */ + return label; } - /* released by caller */ - - return label; + return NULL; } /** @@ -564,12 +571,12 @@ static struct aa_label *x_to_label(struct aa_profile *profile, const char **lookupname, const char **info) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); struct aa_label *new = NULL; + struct aa_label *stack = NULL; struct aa_ns *ns = profile->ns; u32 xtype = xindex & AA_X_TYPE_MASK; - const char *stack = NULL; + /* Used for info checks during fallback handling */ + const char *old_info = NULL; switch (xtype) { case AA_X_NONE: @@ -578,13 +585,14 @@ static struct aa_label *x_to_label(struct aa_profile *profile, break; case AA_X_TABLE: /* TODO: fix when perm mapping done at unload */ - stack = rules->file->trans.table[xindex & AA_X_INDEX_MASK]; - if (*stack != '&') { - /* released by caller */ - new = x_table_lookup(profile, xindex, lookupname); - stack = NULL; + /* released by caller + * if null for both stack and direct want to try fallback + */ + new = x_table_lookup(profile, xindex, lookupname); + if (!new || **lookupname != '&') break; - } + stack = new; + new = NULL; fallthrough; /* to X_NAME */ case AA_X_NAME: if (xindex & AA_X_CHILD) @@ -599,17 +607,38 @@ static struct aa_label *x_to_label(struct aa_profile *profile, break; } + /* fallback transition check */ if (!new) { if (xindex & AA_X_INHERIT) { /* (p|c|n)ix - don't change profile but do * use the newest version */ - *info = "ix fallback"; + if (*info == CONFLICTING_ATTACH_STR) { + *info = CONFLICTING_ATTACH_STR_IX; + } else { + old_info = *info; + *info = "ix fallback"; + } /* no profile && no error */ new = aa_get_newest_label(&profile->label); } else if (xindex & AA_X_UNCONFINED) { new = aa_get_newest_label(ns_unconfined(profile->ns)); - *info = "ux fallback"; + if (*info == CONFLICTING_ATTACH_STR) { + *info = CONFLICTING_ATTACH_STR_UX; + } else { + old_info = *info; + *info = "ux fallback"; + } + } + /* We set old_info on the code paths above where overwriting + * could have happened, so now check if info was set by + * find_attach as well (i.e. whether we actually overwrote) + * and warn accordingly. + */ + if (old_info && old_info != CONFLICTING_ATTACH_STR) { + pr_warn_ratelimited( + "AppArmor: find_attach (from profile %s) audit info \"%s\" dropped", + profile->base.hname, old_info); } } @@ -617,12 +646,12 @@ static struct aa_label *x_to_label(struct aa_profile *profile, /* base the stack on post domain transition */ struct aa_label *base = new; - new = aa_label_parse(base, stack, GFP_KERNEL, true, false); - if (IS_ERR(new)) - new = NULL; + new = aa_label_merge(base, stack, GFP_KERNEL); + /* null on error */ aa_put_label(base); } + aa_put_label(stack); /* released by caller */ return new; } @@ -633,8 +662,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, char *buffer, struct path_cond *cond, bool *secure_exec) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_label *new = NULL; struct aa_profile *new_profile = NULL; const char *info = NULL, *name = NULL, *target = NULL; @@ -652,7 +680,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, if (error) { if (profile_unconfined(profile) || (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) { - AA_DEBUG("name lookup ix on error"); + AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error"); error = 0; new = aa_get_newest_label(&profile->label); } @@ -663,11 +691,27 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, if (profile_unconfined(profile)) { new = find_attach(bprm, profile->ns, &profile->ns->base.profiles, name, &info); + /* info set -> something unusual that we should report + * Currently this is only conflicting attachments, but other + * infos added in the future should also be logged by default + * and only excluded on a case-by-case basis + */ + if (info) { + /* Because perms is never used again after this audit + * we don't need to care about clobbering it + */ + perms.audit |= MAY_EXEC; + perms.allow |= MAY_EXEC; + /* Don't cause error if auditing fails */ + (void) aa_audit_file(subj_cred, profile, &perms, + OP_EXEC, MAY_EXEC, name, target, new, cond->uid, + info, error); + } if (new) { - AA_DEBUG("unconfined attached to new label"); + AA_DEBUG(DEBUG_DOMAIN, "unconfined attached to new label"); return new; } - AA_DEBUG("unconfined exec no attachment"); + AA_DEBUG(DEBUG_DOMAIN, "unconfined exec no attachment"); return aa_get_newest_label(&profile->label); } @@ -678,9 +722,21 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, new = x_to_label(profile, bprm, name, perms.xindex, &target, &info); if (new && new->proxy == profile->label.proxy && info) { + /* Force audit on conflicting attachment fallback + * Because perms is never used again after this audit + * we don't need to care about clobbering it + */ + if (info == CONFLICTING_ATTACH_STR_IX + || info == CONFLICTING_ATTACH_STR_UX) + perms.audit |= MAY_EXEC; /* hack ix fallback - improve how this is detected */ goto audit; } else if (!new) { + if (info) { + pr_warn_ratelimited( + "AppArmor: %s (from profile %s) audit info \"%s\" dropped on missing transition", + __func__, profile->base.hname, info); + } info = "profile transition not found"; /* remove MAY_EXEC to audit as failure or complaint */ perms.allow &= ~MAY_EXEC; @@ -739,8 +795,7 @@ static int profile_onexec(const struct cred *subj_cred, char *buffer, struct path_cond *cond, bool *secure_exec) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; aa_state_t state = rules->file->start[AA_CLASS_FILE]; struct aa_perms perms = {}; const char *xname = NULL, *info = "change_profile onexec"; @@ -755,7 +810,7 @@ static int profile_onexec(const struct cred *subj_cred, /* change_profile on exec already granted */ /* * NOTE: Domain transitions from unconfined are allowed - * even when no_new_privs is set because this aways results + * even when no_new_privs is set because this always results * in a further reduction of permissions. */ return 0; @@ -766,7 +821,7 @@ static int profile_onexec(const struct cred *subj_cred, if (error) { if (profile_unconfined(profile) || (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) { - AA_DEBUG("name lookup ix on error"); + AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error"); error = 0; } xname = bprm->filename; @@ -926,7 +981,7 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) * * NOTE: Domain transitions from unconfined and to stacked * subsets are allowed even when no_new_privs is set because this - * aways results in a further reduction of permissions. + * always results in a further reduction of permissions. */ if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) && @@ -1188,10 +1243,24 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) ctx->nnp = aa_get_label(label); + /* return -EPERM when unconfined doesn't have children to avoid + * changing the traditional error code for unconfined. + */ if (unconfined(label)) { - info = "unconfined can not change_hat"; - error = -EPERM; - goto fail; + struct label_it i; + bool empty = true; + + rcu_read_lock(); + label_for_each_in_ns(i, labels_ns(label), label, profile) { + empty &= list_empty(&profile->base.profiles); + } + rcu_read_unlock(); + + if (empty) { + info = "unconfined can not change_hat"; + error = -EPERM; + goto fail; + } } if (count) { @@ -1216,7 +1285,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) if (task_no_new_privs(current) && !unconfined(label) && !aa_label_is_unconfined_subset(new, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ - AA_DEBUG("no_new_privs - change_hat denied"); + AA_DEBUG(DEBUG_DOMAIN, + "no_new_privs - change_hat denied"); error = -EPERM; goto out; } @@ -1237,7 +1307,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) if (task_no_new_privs(current) && !unconfined(label) && !aa_label_is_unconfined_subset(previous, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ - AA_DEBUG("no_new_privs - change_hat denied"); + AA_DEBUG(DEBUG_DOMAIN, + "no_new_privs - change_hat denied"); error = -EPERM; goto out; } @@ -1282,8 +1353,7 @@ static int change_profile_perms_wrapper(const char *op, const char *name, struct aa_label *target, bool stack, u32 request, struct aa_perms *perms) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; const char *info = NULL; int error = 0; @@ -1343,7 +1413,7 @@ int aa_change_profile(const char *fqname, int flags) if (!fqname || !*fqname) { aa_put_label(label); - AA_DEBUG("no profile name"); + AA_DEBUG(DEBUG_DOMAIN, "no profile name"); return -EINVAL; } @@ -1462,7 +1532,8 @@ int aa_change_profile(const char *fqname, int flags) if (task_no_new_privs(current) && !unconfined(label) && !aa_label_is_unconfined_subset(new, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ - AA_DEBUG("no_new_privs - change_hat denied"); + AA_DEBUG(DEBUG_DOMAIN, + "no_new_privs - change_hat denied"); error = -EPERM; goto out; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index d52a5b14dad4..c75820402878 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -14,6 +14,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/cred.h" @@ -168,8 +169,9 @@ static int path_name(const char *op, const struct cred *subj_cred, struct aa_perms default_perms = {}; /** - * aa_lookup_fperms - convert dfa compressed perms to internal perms - * @file_rules: the aa_policydb to lookup perms for (NOT NULL) + * aa_lookup_condperms - convert dfa compressed perms to internal perms + * @subj_uid: uid to use for subject owner test + * @rules: the aa_policydb to lookup perms for (NOT NULL) * @state: state in dfa * @cond: conditions to consider (NOT NULL) * @@ -177,18 +179,21 @@ struct aa_perms default_perms = {}; * * Returns: a pointer to a file permission set */ -struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, - aa_state_t state, struct path_cond *cond) +struct aa_perms *aa_lookup_condperms(kuid_t subj_uid, struct aa_policydb *rules, + aa_state_t state, struct path_cond *cond) { - unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state]; + unsigned int index = ACCEPT_TABLE(rules->dfa)[state]; - if (!(file_rules->perms)) + if (!(rules->perms)) return &default_perms; - if (uid_eq(current_fsuid(), cond->uid)) - return &(file_rules->perms[index]); + if ((ACCEPT_TABLE2(rules->dfa)[state] & ACCEPT_FLAG_OWNER)) { + if (uid_eq(subj_uid, cond->uid)) + return &(rules->perms[index]); + return &(rules->perms[index + 1]); + } - return &(file_rules->perms[index + 1]); + return &(rules->perms[index]); } /** @@ -207,21 +212,22 @@ aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start, { aa_state_t state; state = aa_dfa_match(file_rules->dfa, start, name); - *perms = *(aa_lookup_fperms(file_rules, state, cond)); + *perms = *(aa_lookup_condperms(current_fsuid(), file_rules, state, + cond)); return state; } -static int __aa_path_perm(const char *op, const struct cred *subj_cred, - struct aa_profile *profile, const char *name, - u32 request, struct path_cond *cond, int flags, - struct aa_perms *perms) +int __aa_path_perm(const char *op, const struct cred *subj_cred, + struct aa_profile *profile, const char *name, + u32 request, struct path_cond *cond, int flags, + struct aa_perms *perms) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; int e = 0; - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + ((flags & PATH_SOCK_COND) && !RULE_MEDIATES_v9NET(rules))) return 0; aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE], name, cond, perms); @@ -316,8 +322,7 @@ static int profile_path_link(const struct cred *subj_cred, const struct path *target, char *buffer2, struct path_cond *cond) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; const char *lname, *tname = NULL; struct aa_perms lperms = {}, perms; const char *info = NULL; @@ -423,9 +428,11 @@ int aa_path_link(const struct cred *subj_cred, { struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; + struct inode *inode = d_backing_inode(old_dentry); + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(target.mnt), inode); struct path_cond cond = { - d_backing_inode(old_dentry)->i_uid, - d_backing_inode(old_dentry)->i_mode + .uid = vfsuid_into_kuid(vfsuid), + .mode = inode->i_mode, }; char *buffer = NULL, *buffer2 = NULL; struct aa_profile *profile; @@ -534,22 +541,19 @@ static int __file_sock_perm(const char *op, const struct cred *subj_cred, struct aa_label *flabel, struct file *file, u32 request, u32 denied) { - struct socket *sock = (struct socket *) file->private_data; int error; - AA_BUG(!sock); - /* revalidation due to label out of date. No revocation at this time */ if (!denied && aa_label_is_subset(flabel, label)) return 0; /* TODO: improve to skip profiles cached in flabel */ - error = aa_sock_file_perm(subj_cred, label, op, request, sock); + error = aa_sock_file_perm(subj_cred, label, op, request, file); if (denied) { /* TODO: improve to skip profiles checked above */ /* check every profile in file label to is cached */ last_error(error, aa_sock_file_perm(subj_cred, flabel, op, - request, sock)); + request, file)); } if (!error) update_file_ctx(file_ctx(file), label, request); @@ -557,6 +561,35 @@ static int __file_sock_perm(const char *op, const struct cred *subj_cred, return error; } +/* for now separate fn to indicate semantics of the check */ +static bool __file_is_delegated(struct aa_label *obj_label) +{ + return unconfined(obj_label); +} + +static bool __unix_needs_revalidation(struct file *file, struct aa_label *label, + u32 request) +{ + struct socket *sock = (struct socket *) file->private_data; + + lockdep_assert_in_rcu_read_lock(); + + if (!S_ISSOCK(file_inode(file)->i_mode)) + return false; + if (request & NET_PEER_MASK) + return false; + if (sock->sk->sk_family == PF_UNIX) { + struct aa_sk_ctx *ctx = aa_sock(sock->sk); + + if (rcu_access_pointer(ctx->peer) != + rcu_access_pointer(ctx->peer_lastupdate)) + return true; + return !__aa_subj_label_is_cached(rcu_dereference(ctx->label), + label); + } + return false; +} + /** * aa_file_perm - do permission revalidation check & audit for @file * @op: operation being checked @@ -594,17 +627,18 @@ int aa_file_perm(const char *op, const struct cred *subj_cred, * delegation from unconfined tasks */ denied = request & ~fctx->allow; - if (unconfined(label) || unconfined(flabel) || - (!denied && aa_label_is_subset(flabel, label))) { + if (unconfined(label) || __file_is_delegated(flabel) || + __unix_needs_revalidation(file, label, request) || + (!denied && __aa_subj_label_is_cached(label, flabel))) { rcu_read_unlock(); goto done; } + /* slow path - revalidate access */ flabel = aa_get_newest_label(flabel); rcu_read_unlock(); - /* TODO: label cross check */ - if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) + if (path_mediated_fs(file->f_path.dentry)) error = __file_path_perm(op, subj_cred, label, flabel, file, request, denied, in_atomic); diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000000..4a62e600d82b --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2023 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ +#ifndef __AA_AF_UNIX_H + +#include + +#include "label.h" + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define is_unix_addr_abstract_name(B) ((B)[0] == 0) +#define is_unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define is_unix_addr_fs(A, L) (!is_unix_addr_anon(A, L) && \ + !is_unix_addr_abstract_name(unix_addr(A)->sun_path)) + +#define is_unix_anonymous(U) (!unix_sk(U)->addr) +#define is_unix_fs(U) (!is_unix_anonymous(U) && \ + unix_sk(U)->addr->name->sun_path[0]) +#define is_unix_connected(S) ((S)->state == SS_CONNECTED) + + +struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen); +int aa_unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, struct file *file); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index f83934913b0f..cc6e3df1bc62 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -28,6 +28,7 @@ #define AA_CLASS_SIGNAL 10 #define AA_CLASS_XMATCH 11 #define AA_CLASS_NET 14 +#define AA_CLASS_NETV9 15 #define AA_CLASS_LABEL 16 #define AA_CLASS_POSIX_MQUEUE 17 #define AA_CLASS_MODULE 19 @@ -38,12 +39,13 @@ #define AA_CLASS_X 31 #define AA_CLASS_DBUS 32 +/* NOTE: if AA_CLASS_LAST > 63 need to update label->mediates */ #define AA_CLASS_LAST AA_CLASS_DBUS /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; -extern bool aa_g_debug; +extern int aa_g_debug; extern bool aa_g_hash_policy; extern bool aa_g_export_binary; extern int aa_g_rawdata_compression_level; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index e27229349abb..1a71a94ea19c 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -138,9 +138,12 @@ struct apparmor_audit_data { }; struct { int type, protocol; - struct sock *peer_sk; void *addr; int addrlen; + struct { + void *addr; + int addrlen; + } peer; } net; }; }; diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index d6dcc604ec0c..1ddcec2d1160 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -36,6 +36,7 @@ struct aa_caps { extern struct aa_sfs_entry aa_sfs_entry_caps[]; +kernel_cap_t aa_profile_capget(struct aa_profile *profile); int aa_capable(const struct cred *subj_cred, struct aa_label *label, int cap, unsigned int opts); diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h index 7265d2f81dd5..b028e4c13b6f 100644 --- a/security/apparmor/include/cred.h +++ b/security/apparmor/include/cred.h @@ -114,10 +114,22 @@ static inline struct aa_label *aa_get_current_label(void) return aa_get_label(l); } -#define __end_current_label_crit_section(X) end_current_label_crit_section(X) +/** + * __end_current_label_crit_section - end crit section begun with __begin_... + * @label: label obtained from __begin_current_label_crit_section + * @needput: output: bool set by __begin_current_label_crit_section + * + * Returns: label to use for this crit section + */ +static inline void __end_current_label_crit_section(struct aa_label *label, + bool needput) +{ + if (unlikely(needput)) + aa_put_label(label); +} /** - * end_label_crit_section - put a reference found with begin_current_label.. + * end_current_label_crit_section - put a reference found with begin_current_label.. * @label: label reference to put * * Should only be used with a reference obtained with @@ -132,6 +144,7 @@ static inline void end_current_label_crit_section(struct aa_label *label) /** * __begin_current_label_crit_section - current's confining label + * @needput: store whether the label needs to be put when ending crit section * * Returns: up to date confining label or the ns unconfined label (NOT NULL) * @@ -142,13 +155,16 @@ static inline void end_current_label_crit_section(struct aa_label *label) * critical section between __begin_current_label_crit_section() .. * __end_current_label_crit_section() */ -static inline struct aa_label *__begin_current_label_crit_section(void) +static inline struct aa_label *__begin_current_label_crit_section(bool *needput) { struct aa_label *label = aa_current_raw_label(); - if (label_is_stale(label)) - label = aa_get_newest_label(label); + if (label_is_stale(label)) { + *needput = true; + return aa_get_newest_label(label); + } + *needput = false; return label; } @@ -184,10 +200,11 @@ static inline struct aa_ns *aa_get_current_ns(void) { struct aa_label *label; struct aa_ns *ns; + bool needput; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); ns = aa_get_ns(labels_ns(label)); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return ns; } diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 6e8f2aa66cd6..ef60f99bc5ae 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -77,12 +77,17 @@ int aa_audit_file(const struct cred *cred, const char *target, struct aa_label *tlabel, kuid_t ouid, const char *info, int error); -struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, - aa_state_t state, struct path_cond *cond); +struct aa_perms *aa_lookup_condperms(kuid_t subj_uid, + struct aa_policydb *file_rules, + aa_state_t state, struct path_cond *cond); aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start, const char *name, struct path_cond *cond, struct aa_perms *perms); +int __aa_path_perm(const char *op, const struct cred *subj_cred, + struct aa_profile *profile, const char *name, + u32 request, struct path_cond *cond, int flags, + struct aa_perms *perms); int aa_path_perm(const char *op, const struct cred *subj_cred, struct aa_label *label, const struct path *path, int flags, u32 request, struct path_cond *cond); @@ -99,7 +104,7 @@ void aa_inherit_files(const struct cred *cred, struct files_struct *files); /** - * aa_map_file_perms - map file flags to AppArmor permissions + * aa_map_file_to_perms - map file flags to AppArmor permissions * @file: open file to map flags to AppArmor permissions * * Returns: apparmor permission set for the file diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 74d17052f76b..323dd071afe9 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -13,6 +13,9 @@ #include +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 + int aa_may_signal(const struct cred *subj_cred, struct aa_label *sender, const struct cred *target_cred, struct aa_label *target, int sig); diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 93290ae300bb..c0812dbc1b5b 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -19,6 +19,7 @@ #include "lib.h" struct aa_ns; +struct aa_ruleset; #define LOCAL_VEC_ENTRIES 8 #define DEFINE_VEC(T, V) \ @@ -109,7 +110,7 @@ struct label_it { int i, j; }; -/* struct aa_label - lazy labeling struct +/* struct aa_label_base - base info of label * @count: ref count of active users * @node: rbtree position * @rcu: rcu callback struct @@ -118,7 +119,10 @@ struct label_it { * @flags: stale and other flags - values may change under label set lock * @secid: secid that references this label * @size: number of entries in @ent[] - * @ent: set of profiles for label, actual size determined by @size + * @mediates: bitmask for label_mediates + * profile: label vec when embedded in a profile FLAG_PROFILE is set + * rules: variable length rules in a profile FLAG_PROFILE is set + * vec: vector of profiles comprising the compound label */ struct aa_label { struct kref count; @@ -129,7 +133,18 @@ struct aa_label { long flags; u32 secid; int size; - struct aa_profile *vec[]; + u64 mediates; + union { + struct { + /* only used is the label is a profile, size of + * rules[] is determined by the profile + * profile[1] is poison or null as guard + */ + struct aa_profile *profile[2]; + DECLARE_FLEX_ARRAY(struct aa_ruleset *, rules); + }; + DECLARE_FLEX_ARRAY(struct aa_profile *, vec); + }; }; #define last_error(E, FN) \ @@ -231,20 +246,17 @@ int aa_label_next_confined(struct aa_label *l, int i); #define fn_for_each_not_in_set(L1, L2, P, FN) \ fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set) -#define LABEL_MEDIATES(L, C) \ -({ \ - struct aa_profile *profile; \ - struct label_it i; \ - int ret = 0; \ - label_for_each(i, (L), profile) { \ - if (RULE_MEDIATES(&profile->rules, (C))) { \ - ret = 1; \ - break; \ - } \ - } \ - ret; \ -}) +static inline bool label_mediates(struct aa_label *L, unsigned char C) +{ + return (L)->mediates & (((u64) 1) << (C)); +} +static inline bool label_mediates_safe(struct aa_label *L, unsigned char C) +{ + if (C > AA_CLASS_LAST) + return false; + return label_mediates(L, C); +} void aa_labelset_destroy(struct aa_labelset *ls); void aa_labelset_init(struct aa_labelset *ls); @@ -417,6 +429,13 @@ static inline void aa_put_label(struct aa_label *l) kref_put(&l->count, aa_label_kref); } +/* wrapper fn to indicate semantics of the check */ +static inline bool __aa_subj_label_is_cached(struct aa_label *subj_label, + struct aa_label *obj_label) +{ + return aa_label_is_subset(obj_label, subj_label); +} + struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp); void aa_proxy_kref(struct kref *kref); diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index f11a0db7f51d..444197075fd6 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -18,23 +18,35 @@ extern struct aa_dfa *stacksplitdfa; -/* - * DEBUG remains global (no per profile flag) since it is mostly used in sysctl - * which is not related to profile accesses. - */ - -#define DEBUG_ON (aa_g_debug) /* * split individual debug cases out in preparation for finer grained * debug controls in the future. */ -#define AA_DEBUG_LABEL DEBUG_ON #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) -#define AA_DEBUG(fmt, args...) \ + +#define DEBUG_NONE 0 +#define DEBUG_LABEL_ABS_ROOT 1 +#define DEBUG_LABEL 2 +#define DEBUG_DOMAIN 4 +#define DEBUG_POLICY 8 +#define DEBUG_INTERFACE 0x10 + +#define DEBUG_ALL 0x1f /* update if new DEBUG_X added */ +#define DEBUG_PARSE_ERROR (-1) + +#define DEBUG_ON (aa_g_debug != DEBUG_NONE) +#define DEBUG_ABS_ROOT (aa_g_debug & DEBUG_LABEL_ABS_ROOT) + +#define AA_DEBUG(opt, fmt, args...) \ do { \ - if (DEBUG_ON) \ - pr_debug_ratelimited("AppArmor: " fmt, ##args); \ + if (aa_g_debug & opt) \ + pr_warn_ratelimited("%s: " fmt, __func__, ##args); \ } while (0) +#define AA_DEBUG_LABEL(LAB, X, fmt, args...) \ +do { \ + if ((LAB)->flags & FLAG_DEBUG1) \ + AA_DEBUG(X, fmt, args); \ +} while (0) #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) @@ -48,9 +60,16 @@ extern struct aa_dfa *stacksplitdfa; #define AA_BUG_FMT(X, fmt, args...) \ WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) #else -#define AA_BUG_FMT(X, fmt, args...) no_printk(fmt, ##args) +#define AA_BUG_FMT(X, fmt, args...) \ + do { \ + BUILD_BUG_ON_INVALID(X); \ + no_printk(fmt, ##args); \ + } while (0) #endif +int aa_parse_debug_params(const char *str); +int aa_print_debug_params(char *buffer); + #define AA_ERROR(fmt, args...) \ pr_err_ratelimited("AppArmor: " fmt, ##args) @@ -106,6 +125,7 @@ struct aa_str_table { }; void aa_free_str_table(struct aa_str_table *table); +bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp); struct counted_str { struct kref count; @@ -151,7 +171,7 @@ struct aa_policy { /** * basename - find the last component of an hname - * @name: hname to find the base profile name component of (NOT NULL) + * @hname: hname to find the base profile name component of (NOT NULL) * * Returns: the tail (base profile name) name component of an hname */ @@ -281,7 +301,7 @@ __do_cleanup: \ } \ __done: \ if (!__new_) \ - AA_DEBUG("label build failed\n"); \ + AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \ (__new_); \ }) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 536ce3abd598..1fbe82f5021b 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -17,7 +17,7 @@ #define DFA_START 1 -/** +/* * The format used for transition tables is based on the GNU flex table * file format (--tables-file option; see Table File Format in the flex * info pages and the flex sources for documentation). The magic number @@ -137,17 +137,15 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start, void aa_dfa_free_kref(struct kref *kref); -#define WB_HISTORY_SIZE 24 +/* This needs to be a power of 2 */ +#define WB_HISTORY_SIZE 32 struct match_workbuf { - unsigned int count; unsigned int pos; unsigned int len; - unsigned int size; /* power of 2, same as history size */ - unsigned int history[WB_HISTORY_SIZE]; + aa_state_t history[WB_HISTORY_SIZE]; }; #define DEFINE_MATCH_WB(N) \ struct match_workbuf N = { \ - .count = 0, \ .pos = 0, \ .len = 0, \ } diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index c42ed8a73f1c..0d0b0ce42723 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -47,8 +47,9 @@ #define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ AA_MAY_ACCEPT) struct aa_sk_ctx { - struct aa_label *label; - struct aa_label *peer; + struct aa_label __rcu *label; + struct aa_label __rcu *peer; + struct aa_label __rcu *peer_lastupdate; /* ptr cmp only, no deref */ }; static inline struct aa_sk_ctx *aa_sock(const struct sock *sk) @@ -56,7 +57,7 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk) return sk->sk_security + apparmor_blob_sizes.lbs_sock; } -#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ +#define DEFINE_AUDIT_NET(NAME, OP, CRED, SK, F, T, P) \ struct lsm_network_audit NAME ## _net = { .sk = (SK), \ .family = (F)}; \ DEFINE_AUDIT_DATA(NAME, \ @@ -65,24 +66,15 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk) AA_CLASS_NET, \ OP); \ NAME.common.u.net = &(NAME ## _net); \ + NAME.subj_cred = (CRED); \ NAME.net.type = (T); \ NAME.net.protocol = (P) -#define DEFINE_AUDIT_SK(NAME, OP, SK) \ - DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ +#define DEFINE_AUDIT_SK(NAME, OP, CRED, SK) \ + DEFINE_AUDIT_NET(NAME, OP, CRED, SK, (SK)->sk_family, (SK)->sk_type, \ (SK)->sk_protocol) -#define af_select(FAMILY, FN, DEF_FN) \ -({ \ - int __e; \ - switch ((FAMILY)) { \ - default: \ - __e = DEF_FN; \ - } \ - __e; \ -}) - struct aa_secmark { u8 audit; u8 deny; @@ -91,11 +83,19 @@ struct aa_secmark { }; extern struct aa_sfs_entry aa_sfs_entry_network[]; +extern struct aa_sfs_entry aa_sfs_entry_networkv9[]; +int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy, + aa_state_t state, u32 request, struct aa_perms *p, + struct apparmor_audit_data *ad); +/* passing in state returned by XXX_mediates_AF() */ +aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state, + u32 request, u16 af, int type, int protocol, + struct aa_perms **p, const char **info); void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, struct apparmor_audit_data *ad, - u32 request, u16 family, int type); + u32 request, u16 family, int type, int protocol); int aa_af_perm(const struct cred *subj_cred, struct aa_label *label, const char *op, u32 request, u16 family, int type, int protocol); @@ -105,13 +105,13 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, struct sock *sk) { return aa_profile_af_perm(profile, ad, request, sk->sk_family, - sk->sk_type); + sk->sk_type, sk->sk_protocol); } int aa_sk_perm(const char *op, u32 request, struct sock *sk); int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label, const char *op, u32 request, - struct socket *sock); + struct file *file); int apparmor_secmark_check(struct aa_label *label, char *op, u32 request, u32 secid, const struct sock *sk); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 343189903dba..8bb915d48dc7 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -13,6 +13,7 @@ enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index bbaa7d39a39a..37a3781b99a0 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -101,8 +101,8 @@ extern struct aa_perms allperms; /** * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms - * @accum - perms struct to accumulate into - * @addend - perms struct to add to @accum + * @accum: perms struct to accumulate into + * @addend: perms struct to add to @accum */ static inline void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend) @@ -128,8 +128,8 @@ static inline void aa_perms_accum_raw(struct aa_perms *accum, /** * aa_perms_accum - accumulate perms, masking off overlapping perms - * @accum - perms struct to accumulate into - * @addend - perms struct to add to @accum + * @accum: perms struct to accumulate into + * @addend: perms struct to add to @accum */ static inline void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 757e3c232c57..4c50875c9d13 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -59,6 +59,11 @@ extern const char *const aa_profile_mode_names[]; #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) +/* flags in the dfa accept2 table */ +enum dfa_accept_flags { + ACCEPT_FLAG_OWNER = 1, +}; + /* * FIXME: currently need a clean way to replace and remove profiles as a * set. It should be done at the namespace level. @@ -124,6 +129,7 @@ static inline void aa_put_pdb(struct aa_policydb *pdb) kref_put(&pdb->count, aa_pdb_free_kref); } +/* lookup perm that doesn't have and object conditional */ static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy, aa_state_t state) { @@ -135,7 +141,6 @@ static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy, return &(policy->perms[index]); } - /* struct aa_data - generic data structure * key: name for retrieving this data * size: size of data in bytes @@ -160,8 +165,6 @@ struct aa_data { * @secmark: secmark label match info */ struct aa_ruleset { - struct list_head list; - int size; /* TODO: merge policy and file */ @@ -175,6 +178,7 @@ struct aa_ruleset { struct aa_secmark *secmark; }; + /* struct aa_attachment - data and rules for a profiles attachment * @list: * @xmatch_str: human readable attachment string @@ -193,7 +197,6 @@ struct aa_attachment { /* struct aa_profile - basic confinement data * @base - base components of the profile (name, refcount, lists, lock ...) - * @label - label this profile is an extension of * @parent: parent of profile * @ns: namespace the profile is in * @rename: optional profile name that this profile renamed @@ -201,13 +204,20 @@ struct aa_attachment { * @audit: the auditing mode of the profile * @mode: the enforcement mode of the profile * @path_flags: flags controlling path generation behavior + * @signal: the signal that should be used when kill is used * @disconnected: what to prepend if attach_disconnected is specified * @attach: attachment rules for the profile * @rules: rules to be enforced * + * learning_cache: the accesses learned in complain mode + * raw_data: rawdata of the loaded profile policy + * hash: cryptographic hash of the profile * @dents: dentries for the profiles file entries in apparmorfs * @dirname: name of the profile dir in apparmorfs + * @dents: set of dentries associated with the profile * @data: hashtable for free-form policy aa_data + * @label - label this profile is an extension of + * @rules - label with the rule vec on its end * * The AppArmor profile contains the basic confinement data. Each profile * has a name, and exists in a namespace. The @name and @exec_match are @@ -231,16 +241,19 @@ struct aa_profile { enum audit_mode audit; long mode; u32 path_flags; + int signal; const char *disconnected; struct aa_attachment attach; - struct list_head rules; struct aa_loaddata *rawdata; unsigned char *hash; char *dirname; struct dentry *dents[AAFS_PROF_SIZEOF]; struct rhashtable *data; + + int n_rules; + /* special - variable length must be last entry in profile */ struct aa_label label; }; @@ -298,24 +311,38 @@ static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules, rules->policy->start[0], &class, 1); } -static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF) +static inline aa_state_t RULE_MEDIATES_v9NET(struct aa_ruleset *rules) { - aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET); - __be16 be_af = cpu_to_be16(AF); - - if (!state) - return DFA_NOMATCH; - return aa_dfa_match_len(rules->policy->dfa, state, (char *) &be_af, 2); + return RULE_MEDIATES(rules, AA_CLASS_NETV9); } -static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head, - unsigned char class) +static inline aa_state_t RULE_MEDIATES_NET(struct aa_ruleset *rules) { - struct aa_ruleset *rule; + /* can not use RULE_MEDIATE_v9AF here, because AF match fail + * can not be distiguished from class match fail, and we only + * fallback to checking older class on class match failure + */ + aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NETV9); - /* TODO: change to list walk */ - rule = list_first_entry(head, typeof(*rule), list); - return RULE_MEDIATES(rule, class); + /* fallback and check v7/8 if v9 is NOT mediated */ + if (!state) + state = RULE_MEDIATES(rules, AA_CLASS_NET); + + return state; +} + + +void aa_compute_profile_mediates(struct aa_profile *profile); +static inline bool profile_mediates(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates(&profile->label, class); +} + +static inline bool profile_mediates_safe(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates_safe(&profile->label, class); } /** diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h index cbf7a997ed84..c772668cdc62 100644 --- a/security/apparmor/include/sig_names.h +++ b/security/apparmor/include/sig_names.h @@ -1,9 +1,5 @@ #include - -#define SIGUNKNOWN 0 -#define MAXMAPPED_SIG 35 -#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) -#define SIGRT_BASE 128 +#include "signal.h" /* provide a mapping of arch signal to internal signal # for mediation * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO diff --git a/security/apparmor/include/signal.h b/security/apparmor/include/signal.h new file mode 100644 index 000000000000..729763fa7ce6 --- /dev/null +++ b/security/apparmor/include/signal.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor ipc mediation function definitions. + * + * Copyright 2023 Canonical Ltd. + */ + +#ifndef __AA_SIGNAL_H +#define __AA_SIGNAL_H + +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 + +#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) +#define SIGRT_BASE 128 + +#endif /* __AA_SIGNAL_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 0cdf4340b02d..df5712cea685 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -80,21 +80,20 @@ static int profile_signal_perm(const struct cred *cred, struct aa_label *peer, u32 request, struct apparmor_audit_data *ad) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_perms perms; aa_state_t state; - if (profile_unconfined(profile) || - !ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_SIGNAL)) + if (profile_unconfined(profile)) return 0; ad->subj_cred = cred; ad->peer = peer; /* TODO: secondary cache check */ - state = aa_dfa_next(rules->policy->dfa, - rules->policy->start[AA_CLASS_SIGNAL], - ad->signal); + state = RULE_MEDIATES(rules, AA_CLASS_SIGNAL); + if (!state) + return 0; + state = aa_dfa_next(rules->policy->dfa, state, ad->signal); aa_label_match(profile, rules, peer, state, false, request, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, ad, audit_signal_cb); diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 91483ecacc16..913678f199c3 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -198,21 +198,25 @@ static bool vec_is_stale(struct aa_profile **vec, int n) return false; } -static long accum_vec_flags(struct aa_profile **vec, int n) +static void accum_label_info(struct aa_label *new) { long u = FLAG_UNCONFINED; int i; - AA_BUG(!vec); + AA_BUG(!new); - for (i = 0; i < n; i++) { - u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 | - FLAG_STALE); - if (!(u & vec[i]->label.flags & FLAG_UNCONFINED)) + /* size == 1 is a profile and flags must be set as part of creation */ + if (new->size == 1) + return; + + for (i = 0; i < new->size; i++) { + u |= new->vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 | + FLAG_STALE); + if (!(u & new->vec[i]->label.flags & FLAG_UNCONFINED)) u &= ~FLAG_UNCONFINED; + new->mediates |= new->vec[i]->label.mediates; } - - return u; + new->flags |= u; } static int sort_cmp(const void *a, const void *b) @@ -431,7 +435,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) /* + 1 for null terminator entry on vec */ new = kzalloc(struct_size(new, vec, size + 1), gfp); - AA_DEBUG("%s (%p)\n", __func__, new); + AA_DEBUG(DEBUG_LABEL, "%s (%p)\n", __func__, new); if (!new) goto fail; @@ -645,6 +649,7 @@ static bool __label_replace(struct aa_label *old, struct aa_label *new) rb_replace_node(&old->node, &new->node, &ls->root); old->flags &= ~FLAG_IN_TREE; new->flags |= FLAG_IN_TREE; + accum_label_info(new); return true; } @@ -705,6 +710,7 @@ static struct aa_label *__label_insert(struct aa_labelset *ls, rb_link_node(&label->node, parent, new); rb_insert_color(&label->node, &ls->root); label->flags |= FLAG_IN_TREE; + accum_label_info(label); return aa_get_label(label); } @@ -1085,7 +1091,6 @@ static struct aa_label *label_merge_insert(struct aa_label *new, else if (k == b->size) return aa_get_label(b); } - new->flags |= accum_vec_flags(new->vec, new->size); ls = labels_set(new); write_lock_irqsave(&ls->lock, flags); label = __label_insert(labels_set(new), new, false); @@ -1456,7 +1461,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp) /* * cached label name is present and visible - * @label->hname only exists if label is namespace hierachical + * @label->hname only exists if label is namespace hierarchical */ static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, int flags) @@ -1617,7 +1622,7 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, AA_BUG(!str && size != 0); AA_BUG(!label); - if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) { + if (DEBUG_ABS_ROOT && (flags & FLAG_ABS_ROOT)) { ns = root_ns; len = snprintf(str, size, "_"); update_for_len(total, len, size, str); @@ -1731,7 +1736,7 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, display_mode(ns, label, flags)) { len = aa_label_asxprint(&name, ns, label, flags, gfp); if (len < 0) { - AA_DEBUG("label print error"); + AA_DEBUG(DEBUG_LABEL, "label print error"); return; } str = name; @@ -1759,7 +1764,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, len = aa_label_asxprint(&str, ns, label, flags, gfp); if (len < 0) { - AA_DEBUG("label print error"); + AA_DEBUG(DEBUG_LABEL, "label print error"); return; } seq_puts(f, str); @@ -1782,7 +1787,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, len = aa_label_asxprint(&str, ns, label, flags, gfp); if (len < 0) { - AA_DEBUG("label print error"); + AA_DEBUG(DEBUG_LABEL, "label print error"); return; } pr_info("%s", str); @@ -1865,7 +1870,7 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, AA_BUG(!str); str = skipn_spaces(str, n); - if (str == NULL || (AA_DEBUG_LABEL && *str == '_' && + if (str == NULL || (DEBUG_ABS_ROOT && *str == '_' && base != &root_ns->unconfined->label)) return ERR_PTR(-EINVAL); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 7db62213e352..82dbb97ad406 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -25,6 +25,120 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK, .quiet = ALL_PERMS_MASK, .hide = ALL_PERMS_MASK }; +struct val_table_ent { + const char *str; + int value; +}; + +static struct val_table_ent debug_values_table[] = { + { "N", DEBUG_NONE }, + { "none", DEBUG_NONE }, + { "n", DEBUG_NONE }, + { "0", DEBUG_NONE }, + { "all", DEBUG_ALL }, + { "Y", DEBUG_ALL }, + { "y", DEBUG_ALL }, + { "1", DEBUG_ALL }, + { "abs_root", DEBUG_LABEL_ABS_ROOT }, + { "label", DEBUG_LABEL }, + { "domain", DEBUG_DOMAIN }, + { "policy", DEBUG_POLICY }, + { "interface", DEBUG_INTERFACE }, + { NULL, 0 } +}; + +static struct val_table_ent *val_table_find_ent(struct val_table_ent *table, + const char *name, size_t len) +{ + struct val_table_ent *entry; + + for (entry = table; entry->str != NULL; entry++) { + if (strncmp(entry->str, name, len) == 0 && + strlen(entry->str) == len) + return entry; + } + return NULL; +} + +int aa_parse_debug_params(const char *str) +{ + struct val_table_ent *ent; + const char *next; + int val = 0; + + do { + size_t n = strcspn(str, "\r\n,"); + + next = str + n; + ent = val_table_find_ent(debug_values_table, str, next - str); + if (ent) + val |= ent->value; + else + AA_DEBUG(DEBUG_INTERFACE, "unknown debug type '%.*s'", + (int)(next - str), str); + str = next + 1; + } while (*next != 0); + return val; +} + +/** + * val_mask_to_str - convert a perm mask to its short string + * @str: character buffer to store string in (at least 10 characters) + * @size: size of the @str buffer + * @table: NUL-terminated character buffer of permission characters (NOT NULL) + * @mask: permission mask to convert + */ +static int val_mask_to_str(char *str, size_t size, + const struct val_table_ent *table, u32 mask) +{ + const struct val_table_ent *ent; + int total = 0; + + for (ent = table; ent->str; ent++) { + if (ent->value && (ent->value & mask) == ent->value) { + int len = scnprintf(str, size, "%s%s", total ? "," : "", + ent->str); + size -= len; + str += len; + total += len; + mask &= ~ent->value; + } + } + + return total; +} + +int aa_print_debug_params(char *buffer) +{ + if (!aa_g_debug) + return sprintf(buffer, "N"); + return val_mask_to_str(buffer, PAGE_SIZE, debug_values_table, + aa_g_debug); +} + +bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp) +{ + char **n; + int i; + + if (t->size == newsize) + return true; + n = kcalloc(newsize, sizeof(*n), gfp); + if (!n) + return false; + for (i = 0; i < min(t->size, newsize); i++) + n[i] = t->table[i]; + for (; i < t->size; i++) + kfree_sensitive(t->table[i]); + if (newsize > t->size) + memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n)); + kfree_sensitive(t->table); + t->table = n; + t->size = newsize; + + return true; +} + /** * aa_free_str_table - free entries str table * @t: the string table to free (MAYBE NULL) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 9b6c2f157f83..8e1cc229b41b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -26,6 +26,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" @@ -126,14 +127,15 @@ static int apparmor_ptrace_access_check(struct task_struct *child, struct aa_label *tracer, *tracee; const struct cred *cred; int error; + bool needput; cred = get_task_cred(child); tracee = cred_label(cred); /* ref count on cred */ - tracer = __begin_current_label_crit_section(); + tracer = __begin_current_label_crit_section(&needput); error = aa_may_ptrace(current_cred(), tracer, cred, tracee, (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ : AA_PTRACE_TRACE); - __end_current_label_crit_section(tracer); + __end_current_label_crit_section(tracer, needput); put_cred(cred); return error; @@ -144,14 +146,15 @@ static int apparmor_ptrace_traceme(struct task_struct *parent) struct aa_label *tracer, *tracee; const struct cred *cred; int error; + bool needput; - tracee = __begin_current_label_crit_section(); + tracee = __begin_current_label_crit_section(&needput); cred = get_task_cred(parent); tracer = cred_label(cred); /* ref count on cred */ error = aa_may_ptrace(cred, tracer, current_cred(), tracee, AA_PTRACE_TRACE); put_cred(cred); - __end_current_label_crit_section(tracee); + __end_current_label_crit_section(tracee, needput); return error; } @@ -176,15 +179,11 @@ static int apparmor_capget(const struct task_struct *target, kernel_cap_t *effec struct label_it i; label_for_each_confined(i, label, profile) { - struct aa_ruleset *rules; - if (COMPLAIN_MODE(profile)) - continue; - rules = list_first_entry(&profile->rules, - typeof(*rules), list); - *effective = cap_intersect(*effective, - rules->caps.allow); - *permitted = cap_intersect(*permitted, - rules->caps.allow); + kernel_cap_t allowed; + + allowed = aa_profile_capget(profile); + *effective = cap_intersect(*effective, allowed); + *permitted = cap_intersect(*permitted, allowed); } } rcu_read_unlock(); @@ -221,12 +220,13 @@ static int common_perm(const char *op, const struct path *path, u32 mask, { struct aa_label *label; int error = 0; + bool needput; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); if (!unconfined(label)) error = aa_path_perm(op, current_cred(), label, path, 0, mask, cond); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -524,14 +524,15 @@ static int common_file_perm(const char *op, struct file *file, u32 mask, { struct aa_label *label; int error = 0; + bool needput; /* don't reaudit files closed during inheritance */ - if (file->f_path.dentry == aa_null.dentry) + if (unlikely(file->f_path.dentry == aa_null.dentry)) return -EACCES; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); error = aa_file_perm(op, current_cred(), label, file, mask, in_atomic); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -633,7 +634,7 @@ static int profile_uring(struct aa_profile *profile, u32 request, AA_BUG(!profile); - rules = list_first_entry(&profile->rules, typeof(*rules), list); + rules = profile->label.rules[0]; state = RULE_MEDIATES(rules, AA_CLASS_IO_URING); if (state) { struct aa_perms perms = { }; @@ -664,15 +665,16 @@ static int apparmor_uring_override_creds(const struct cred *new) struct aa_profile *profile; struct aa_label *label; int error; + bool needput; DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING, OP_URING_OVERRIDE); ad.uring.target = cred_label(new); - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); error = fn_for_each(label, profile, profile_uring(profile, AA_MAY_OVERRIDE_CRED, cred_label(new), CAP_SYS_ADMIN, &ad)); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -688,14 +690,15 @@ static int apparmor_uring_sqpoll(void) struct aa_profile *profile; struct aa_label *label; int error; + bool needput; DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING, OP_URING_SQPOLL); - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); error = fn_for_each(label, profile, profile_uring(profile, AA_MAY_CREATE_SQPOLL, NULL, CAP_SYS_ADMIN, &ad)); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -706,6 +709,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path, { struct aa_label *label; int error = 0; + bool needput; /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) @@ -713,7 +717,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path, flags &= ~AA_MS_IGNORE_MASK; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); if (!unconfined(label)) { if (flags & MS_REMOUNT) error = aa_remount(current_cred(), label, path, flags, @@ -732,7 +736,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path, error = aa_new_mount(current_cred(), label, dev_name, path, type, flags, data); } - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -742,12 +746,13 @@ static int apparmor_move_mount(const struct path *from_path, { struct aa_label *label; int error = 0; + bool needput; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); if (!unconfined(label)) error = aa_move_mount(current_cred(), label, from_path, to_path); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -756,11 +761,12 @@ static int apparmor_sb_umount(struct vfsmount *mnt, int flags) { struct aa_label *label; int error = 0; + bool needput; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); if (!unconfined(label)) error = aa_umount(current_cred(), label, mnt, flags); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -984,10 +990,12 @@ static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm) static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop) { - struct aa_label *label = __begin_current_label_crit_section(); + struct aa_label *label; + bool needput; + label = __begin_current_label_crit_section(&needput); prop->apparmor.label = label; - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); } static void apparmor_task_getlsmprop_obj(struct task_struct *p, @@ -1002,13 +1010,16 @@ static void apparmor_task_getlsmprop_obj(struct task_struct *p, static int apparmor_task_setrlimit(struct task_struct *task, unsigned int resource, struct rlimit *new_rlim) { - struct aa_label *label = __begin_current_label_crit_section(); + struct aa_label *label; int error = 0; + bool needput; + + label = __begin_current_label_crit_section(&needput); if (!unconfined(label)) error = aa_task_setrlimit(current_cred(), label, task, resource, new_rlim); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return error; } @@ -1019,6 +1030,7 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo const struct cred *tc; struct aa_label *cl, *tl; int error; + bool needput; tc = get_task_cred(target); tl = aa_get_newest_cred_label(tc); @@ -1030,9 +1042,9 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo error = aa_may_signal(cred, cl, tc, tl, sig); aa_put_label(cl); } else { - cl = __begin_current_label_crit_section(); + cl = __begin_current_label_crit_section(&needput); error = aa_may_signal(current_cred(), cl, tc, tl, sig); - __end_current_label_crit_section(cl); + __end_current_label_crit_section(cl, needput); } aa_put_label(tl); put_cred(tc); @@ -1061,12 +1073,29 @@ static int apparmor_userns_create(const struct cred *cred) return error; } +static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t gfp) +{ + struct aa_sk_ctx *ctx = aa_sock(sk); + struct aa_label *label; + bool needput; + + label = __begin_current_label_crit_section(&needput); + //spin_lock_init(&ctx->lock); + rcu_assign_pointer(ctx->label, aa_get_label(label)); + rcu_assign_pointer(ctx->peer, NULL); + rcu_assign_pointer(ctx->peer_lastupdate, NULL); + __end_current_label_crit_section(label, needput); + return 0; +} + static void apparmor_sk_free_security(struct sock *sk) { struct aa_sk_ctx *ctx = aa_sock(sk); - aa_put_label(ctx->label); - aa_put_label(ctx->peer); + /* dead these won't be updated any more */ + aa_put_label(rcu_dereference_protected(ctx->label, true)); + aa_put_label(rcu_dereference_protected(ctx->peer, true)); + aa_put_label(rcu_dereference_protected(ctx->peer_lastupdate, true)); } /** @@ -1080,13 +1109,153 @@ static void apparmor_sk_clone_security(const struct sock *sk, struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *new = aa_sock(newsk); - if (new->label) - aa_put_label(new->label); - new->label = aa_get_label(ctx->label); + /* not actually in use yet */ + if (rcu_access_pointer(ctx->label) != rcu_access_pointer(new->label)) { + aa_put_label(rcu_dereference_protected(new->label, true)); + rcu_assign_pointer(new->label, aa_get_label_rcu(&ctx->label)); + } - if (new->peer) - aa_put_label(new->peer); - new->peer = aa_get_label(ctx->peer); + if (rcu_access_pointer(ctx->peer) != rcu_access_pointer(new->peer)) { + aa_put_label(rcu_dereference_protected(new->peer, true)); + rcu_assign_pointer(new->peer, aa_get_label_rcu(&ctx->peer)); + } + + if (rcu_access_pointer(ctx->peer_lastupdate) != rcu_access_pointer(new->peer_lastupdate)) { + aa_put_label(rcu_dereference_protected(new->peer_lastupdate, true)); + rcu_assign_pointer(new->peer_lastupdate, + aa_get_label_rcu(&ctx->peer_lastupdate)); + } +} + +static int unix_connect_perm(const struct cred *cred, struct aa_label *label, + struct sock *sk, struct sock *peer_sk) +{ + struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk); + int error; + + error = aa_unix_peer_perm(cred, label, OP_CONNECT, + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), + sk, peer_sk, + rcu_dereference_protected(peer_ctx->label, + lockdep_is_held(&unix_sk(peer_sk)->lock))); + if (!is_unix_fs(peer_sk)) { + last_error(error, + aa_unix_peer_perm(cred, + rcu_dereference_protected(peer_ctx->label, + lockdep_is_held(&unix_sk(peer_sk)->lock)), + OP_CONNECT, + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), + peer_sk, sk, label)); + } + + return error; +} + +/* lockdep check in unix_connect_perm - push sks here to check */ +static void unix_connect_peers(struct aa_sk_ctx *sk_ctx, + struct aa_sk_ctx *peer_ctx) +{ + /* Cross reference the peer labels for SO_PEERSEC */ + struct aa_label *label = rcu_dereference_protected(sk_ctx->label, true); + + aa_get_label(label); + aa_put_label(rcu_dereference_protected(peer_ctx->peer, + true)); + rcu_assign_pointer(peer_ctx->peer, label); /* transfer cnt */ + + label = aa_get_label(rcu_dereference_protected(peer_ctx->label, + true)); + //spin_unlock(&peer_ctx->lock); + + //spin_lock(&sk_ctx->lock); + aa_put_label(rcu_dereference_protected(sk_ctx->peer, + true)); + aa_put_label(rcu_dereference_protected(sk_ctx->peer_lastupdate, + true)); + + rcu_assign_pointer(sk_ctx->peer, aa_get_label(label)); + rcu_assign_pointer(sk_ctx->peer_lastupdate, label); /* transfer cnt */ + //spin_unlock(&sk_ctx->lock); +} + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * @sk: sk attempting to connect + * @peer_sk: sk that is accepting the connection + * @newsk: new sk created for this connection + * peer is locked when this hook is called + * + * Return: + * 0 if connection is permitted + * error code on denial or failure + */ +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, + struct sock *newsk) +{ + struct aa_sk_ctx *sk_ctx = aa_sock(sk); + struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk); + struct aa_sk_ctx *new_ctx = aa_sock(newsk); + struct aa_label *label; + int error; + bool needput; + + label = __begin_current_label_crit_section(&needput); + error = unix_connect_perm(current_cred(), label, sk, peer_sk); + __end_current_label_crit_section(label, needput); + + if (error) + return error; + + /* newsk doesn't go through post_create, but does go through + * security_sk_alloc() + */ + rcu_assign_pointer(new_ctx->label, + aa_get_label(rcu_dereference_protected(peer_ctx->label, + true))); + + /* Cross reference the peer labels for SO_PEERSEC */ + unix_connect_peers(sk_ctx, new_ctx); + + return 0; +} + +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * @sock: socket sending the message + * @peer: socket message is being send to + * + * Performs bidirectional permission checks for Unix domain socket communication: + * 1. Verifies sender has AA_MAY_SEND to target socket + * 2. Verifies receiver has AA_MAY_RECEIVE from source socket + * + * sock and peer are locked when this hook is called + * called by: dgram_connect peer setup but path not copied to newsk + * + * Return: + * 0 if transmission is permitted + * error code on denial or failure + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) +{ + struct aa_sk_ctx *peer_ctx = aa_sock(peer->sk); + struct aa_label *label; + int error; + bool needput; + + label = __begin_current_label_crit_section(&needput); + error = xcheck(aa_unix_peer_perm(current_cred(), + label, OP_SENDMSG, AA_MAY_SEND, + sock->sk, peer->sk, + rcu_dereference_protected(peer_ctx->label, + true)), + aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL, + rcu_dereference_protected(peer_ctx->label, + true), + OP_SENDMSG, AA_MAY_RECEIVE, peer->sk, + sock->sk, label)); + __end_current_label_crit_section(label, needput); + + return error; } static int apparmor_socket_create(int family, int type, int protocol, int kern) @@ -1096,13 +1265,19 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern) AA_BUG(in_interrupt()); + if (kern) + return 0; + label = begin_current_label_crit_section(); - if (!(kern || unconfined(label))) - error = af_select(family, - create_perm(label, family, type, protocol), - aa_af_perm(current_cred(), label, - OP_CREATE, AA_MAY_CREATE, - family, type, protocol)); + if (!unconfined(label)) { + if (family == PF_UNIX) + error = aa_unix_create_perm(label, family, type, + protocol); + else + error = aa_af_perm(current_cred(), label, OP_CREATE, + AA_MAY_CREATE, family, type, + protocol); + } end_current_label_crit_section(label); return error; @@ -1135,14 +1310,58 @@ static int apparmor_socket_post_create(struct socket *sock, int family, if (sock->sk) { struct aa_sk_ctx *ctx = aa_sock(sock->sk); - aa_put_label(ctx->label); - ctx->label = aa_get_label(label); + /* still not live */ + aa_put_label(rcu_dereference_protected(ctx->label, true)); + rcu_assign_pointer(ctx->label, aa_get_label(label)); } aa_put_label(label); return 0; } +static int apparmor_socket_socketpair(struct socket *socka, + struct socket *sockb) +{ + struct aa_sk_ctx *a_ctx = aa_sock(socka->sk); + struct aa_sk_ctx *b_ctx = aa_sock(sockb->sk); + struct aa_label *label; + + /* socks not live yet - initial values set in sk_alloc */ + label = begin_current_label_crit_section(); + if (rcu_access_pointer(a_ctx->label) != label) { + AA_BUG("a_ctx != label"); + aa_put_label(rcu_dereference_protected(a_ctx->label, true)); + rcu_assign_pointer(a_ctx->label, aa_get_label(label)); + } + if (rcu_access_pointer(b_ctx->label) != label) { + AA_BUG("b_ctx != label"); + aa_put_label(rcu_dereference_protected(b_ctx->label, true)); + rcu_assign_pointer(b_ctx->label, aa_get_label(label)); + } + + if (socka->sk->sk_family == PF_UNIX) { + /* unix socket pairs by-pass unix_stream_connect */ + unix_connect_peers(a_ctx, b_ctx); + } + end_current_label_crit_section(label); + + return 0; +} + +/** + * apparmor_socket_bind - check perms before bind addr to socket + * @sock: socket to bind the address to (must be non-NULL) + * @address: address that is being bound (must be non-NULL) + * @addrlen: length of @address + * + * Performs security checks before allowing a socket to bind to an address. + * Handles Unix domain sockets specially through aa_unix_bind_perm(). + * For other socket families, uses generic permission check via aa_sk_perm(). + * + * Return: + * 0 if binding is permitted + * error code on denial or invalid parameters + */ static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { @@ -1151,9 +1370,9 @@ static int apparmor_socket_bind(struct socket *sock, AA_BUG(!address); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - bind_perm(sock, address, addrlen), - aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_bind_perm(sock, address, addrlen); + return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); } static int apparmor_socket_connect(struct socket *sock, @@ -1164,9 +1383,10 @@ static int apparmor_socket_connect(struct socket *sock, AA_BUG(!address); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - connect_perm(sock, address, addrlen), - aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); + /* PF_UNIX goes through unix_stream_connect && unix_may_send */ + if (sock->sk->sk_family == PF_UNIX) + return 0; + return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); } static int apparmor_socket_listen(struct socket *sock, int backlog) @@ -1175,9 +1395,9 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) AA_BUG(!sock->sk); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - listen_perm(sock, backlog), - aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_listen_perm(sock, backlog); + return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); } /* @@ -1191,9 +1411,9 @@ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) AA_BUG(!newsock); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - accept_perm(sock, newsock), - aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_accept_perm(sock, newsock); + return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); } static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, @@ -1204,9 +1424,10 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, AA_BUG(!msg); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - msg_perm(op, request, sock, msg, size), - aa_sk_perm(op, request, sock->sk)); + /* PF_UNIX goes through unix_may_send */ + if (sock->sk->sk_family == PF_UNIX) + return 0; + return aa_sk_perm(op, request, sock->sk); } static int apparmor_socket_sendmsg(struct socket *sock, @@ -1228,9 +1449,9 @@ static int aa_sock_perm(const char *op, u32 request, struct socket *sock) AA_BUG(!sock->sk); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - sock_perm(op, request, sock), - aa_sk_perm(op, request, sock->sk)); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_sock_perm(op, request, sock); + return aa_sk_perm(op, request, sock->sk); } static int apparmor_socket_getsockname(struct socket *sock) @@ -1251,9 +1472,9 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, AA_BUG(!sock->sk); AA_BUG(in_interrupt()); - return af_select(sock->sk->sk_family, - opt_perm(op, request, sock, level, optname), - aa_sk_perm(op, request, sock->sk)); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_opt_perm(op, request, sock, level, optname); + return aa_sk_perm(op, request, sock->sk); } static int apparmor_socket_getsockopt(struct socket *sock, int level, @@ -1289,6 +1510,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct aa_sk_ctx *ctx = aa_sock(sk); + int error; if (!skb->secmark) return 0; @@ -1297,23 +1519,31 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * If reach here before socket_post_create hook is called, in which * case label is null, drop the packet. */ - if (!ctx->label) + if (!rcu_access_pointer(ctx->label)) return -EACCES; - return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, - skb->secmark, sk); + rcu_read_lock(); + error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG, + AA_MAY_RECEIVE, skb->secmark, sk); + rcu_read_unlock(); + + return error; } #endif -static struct aa_label *sk_peer_label(struct sock *sk) +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 (ctx->peer) - return ctx->peer; + if (rcu_access_pointer(ctx->peer)) + return aa_get_label_rcu(&ctx->peer); - return ERR_PTR(-ENOPROTOOPT); + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); + + return label; } /** @@ -1335,19 +1565,19 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock, struct aa_label *label; struct aa_label *peer; - label = begin_current_label_crit_section(); - peer = sk_peer_label(sock->sk); + peer = sk_peer_get_label(sock->sk); if (IS_ERR(peer)) { error = PTR_ERR(peer); goto done; } + label = begin_current_label_crit_section(); slen = aa_label_asxprint(&name, labels_ns(label), peer, FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); /* don't include terminating \0 in slen, it breaks some apps */ if (slen < 0) { error = -ENOMEM; - goto done; + goto done_put; } if (slen > len) { error = -ERANGE; @@ -1359,8 +1589,11 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock, done_len: if (copy_to_sockptr(optlen, &slen, sizeof(slen))) error = -EFAULT; -done: + +done_put: end_current_label_crit_section(label); + aa_put_label(peer); +done: kfree(name); return error; } @@ -1396,8 +1629,9 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent) { struct aa_sk_ctx *ctx = aa_sock(sk); - if (!ctx->label) - ctx->label = aa_get_current_label(); + /* setup - not live */ + if (!rcu_access_pointer(ctx->label)) + rcu_assign_pointer(ctx->label, aa_get_current_label()); } #ifdef CONFIG_NETWORK_SECMARK @@ -1405,12 +1639,17 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb struct request_sock *req) { struct aa_sk_ctx *ctx = aa_sock(sk); + int error; if (!skb->secmark) return 0; - return apparmor_secmark_check(ctx->label, OP_CONNECT, AA_MAY_CONNECT, - skb->secmark, sk); + rcu_read_lock(); + error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_CONNECT, + AA_MAY_CONNECT, skb->secmark, sk); + rcu_read_unlock(); + + return error; } #endif @@ -1467,11 +1706,16 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_socketpair, apparmor_socket_socketpair), LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), @@ -1571,6 +1815,9 @@ static const struct kernel_param_ops param_ops_aalockpolicy = { .get = param_get_aalockpolicy }; +static int param_set_debug(const char *val, const struct kernel_param *kp); +static int param_get_debug(char *buffer, const struct kernel_param *kp); + static int param_set_audit(const char *val, const struct kernel_param *kp); static int param_get_audit(char *buffer, const struct kernel_param *kp); @@ -1604,8 +1851,9 @@ module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, aacompressionlevel, 0400); /* Debug mode */ -bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES); -module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); +int aa_g_debug; +module_param_call(debug, param_set_debug, param_get_debug, + &aa_g_debug, 0600); /* Audit mode */ enum audit_mode aa_g_audit; @@ -1798,6 +2046,34 @@ static int param_get_aacompressionlevel(char *buffer, return param_get_int(buffer, kp); } +static int param_get_debug(char *buffer, const struct kernel_param *kp) +{ + if (!apparmor_enabled) + return -EINVAL; + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) + return -EPERM; + return aa_print_debug_params(buffer); +} + +static int param_set_debug(const char *val, const struct kernel_param *kp) +{ + int i; + + if (!apparmor_enabled) + return -EINVAL; + if (!val) + return -EINVAL; + if (apparmor_initialized && !aa_current_policy_admin_capable(NULL)) + return -EPERM; + + i = aa_parse_debug_params(val); + if (i == DEBUG_PARSE_ERROR) + return -EINVAL; + + aa_g_debug = i; + return 0; +} + static int param_get_audit(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) @@ -2006,7 +2282,7 @@ static int __init alloc_buffers(void) * two should be enough, with more CPUs it is possible that more * buffers will be used simultaneously. The preallocated pool may grow. * This preallocation has also the side-effect that AppArmor will be - * disabled early at boot if aa_g_path_max is extremly high. + * disabled early at boot if aa_g_path_max is extremely high. */ if (num_online_cpus() > 1) num = 4 + RESERVE_COUNT; @@ -2082,6 +2358,7 @@ static unsigned int apparmor_ip_postroute(void *priv, { struct aa_sk_ctx *ctx; struct sock *sk; + int error; if (!skb->secmark) return NF_ACCEPT; @@ -2091,8 +2368,11 @@ static unsigned int apparmor_ip_postroute(void *priv, return NF_ACCEPT; ctx = aa_sock(sk); - if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND, - skb->secmark, sk)) + rcu_read_lock(); + error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG, + AA_MAY_SEND, skb->secmark, sk); + rcu_read_unlock(); + if (!error) return NF_ACCEPT; return NF_DROP_ERR(-ECONNREFUSED); @@ -2149,12 +2429,12 @@ static int __init apparmor_nf_ip_init(void) __initcall(apparmor_nf_ip_init); #endif -static char nulldfa_src[] = { +static char nulldfa_src[] __aligned(8) = { #include "nulldfa.in" }; static struct aa_dfa *nulldfa; -static char stacksplitdfa_src[] = { +static char stacksplitdfa_src[] __aligned(8) = { #include "stacksplitdfa.in" }; struct aa_dfa *stacksplitdfa; diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f2d9c57f8794..c5a91600842a 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -679,34 +679,35 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start, return state; } -#define inc_wb_pos(wb) \ -do { \ +#define inc_wb_pos(wb) \ +do { \ + BUILD_BUG_ON_NOT_POWER_OF_2(WB_HISTORY_SIZE); \ wb->pos = (wb->pos + 1) & (WB_HISTORY_SIZE - 1); \ - wb->len = (wb->len + 1) & (WB_HISTORY_SIZE - 1); \ + wb->len = (wb->len + 1) > WB_HISTORY_SIZE ? WB_HISTORY_SIZE : \ + wb->len + 1; \ } while (0) /* For DFAs that don't support extended tagging of states */ +/* adjust is only set if is_loop returns true */ static bool is_loop(struct match_workbuf *wb, aa_state_t state, unsigned int *adjust) { - aa_state_t pos = wb->pos; - aa_state_t i; + int pos = wb->pos; + int i; if (wb->history[pos] < state) return false; - for (i = 0; i <= wb->len; i++) { + for (i = 0; i < wb->len; i++) { if (wb->history[pos] == state) { *adjust = i; return true; } - if (pos == 0) - pos = WB_HISTORY_SIZE; - pos--; + /* -1 wraps to WB_HISTORY_SIZE - 1 */ + pos = (pos - 1) & (WB_HISTORY_SIZE - 1); } - *adjust = i; - return true; + return false; } static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index bf8863253e07..523570aa1a5a 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -311,8 +311,7 @@ static int match_mnt_path_str(const struct cred *subj_cred, { struct aa_perms perms = { }; const char *mntpnt = NULL, *info = NULL; - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; int pos, error; AA_BUG(!profile); @@ -371,8 +370,7 @@ static int match_mnt(const struct cred *subj_cred, bool binary) { const char *devname = NULL, *info = NULL; - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; int error = -EACCES; AA_BUG(!profile); @@ -604,8 +602,7 @@ static int profile_umount(const struct cred *subj_cred, struct aa_profile *profile, const struct path *path, char *buffer) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_perms perms = { }; const char *name = NULL, *info = NULL; aa_state_t state; @@ -668,8 +665,7 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred, const struct path *old_path, char *old_buffer) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; const char *old_name, *new_name = NULL, *info = NULL; const char *trans_name = NULL; struct aa_perms perms = { }; diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 77413a519117..45cf25605c34 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -8,6 +8,7 @@ * Copyright 2009-2017 Canonical Ltd. */ +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/cred.h" @@ -24,6 +25,12 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { { } }; +struct aa_sfs_entry aa_sfs_entry_networkv9[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("af_unix", 1), + { } +}; + static const char * const net_mask_names[] = { "unknown", "send", @@ -66,6 +73,42 @@ static const char * const net_mask_names[] = { "unknown", }; +static void audit_unix_addr(struct audit_buffer *ab, const char *str, + struct sockaddr_un *addr, int addrlen) +{ + int len = unix_addr_len(addrlen); + + if (!addr || len <= 0) { + audit_log_format(ab, " %s=none", str); + } else if (addr->sun_path[0]) { + audit_log_format(ab, " %s=", str); + audit_log_untrustedstring(ab, addr->sun_path); + } else { + audit_log_format(ab, " %s=\"@", str); + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); + else + audit_log_format(ab, "%.*s", len - 1, + &addr->sun_path[1]); + audit_log_format(ab, "\""); + } +} + +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, + const struct sock *sk) +{ + const struct unix_sock *u = unix_sk(sk); + + if (u && u->addr) { + int addrlen; + struct sockaddr_un *addr = aa_sunaddr(u, &addrlen); + + audit_unix_addr(ab, str, addr, addrlen); + } else { + audit_unix_addr(ab, str, NULL, 0); + + } +} /* audit callback for net specific fields */ void audit_net_cb(struct audit_buffer *ab, void *va) @@ -73,12 +116,12 @@ void audit_net_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; struct apparmor_audit_data *ad = aad(sa); - if (address_family_names[sa->u.net->family]) + if (address_family_names[ad->common.u.net->family]) audit_log_format(ab, " family=\"%s\"", - address_family_names[sa->u.net->family]); + address_family_names[ad->common.u.net->family]); else audit_log_format(ab, " family=\"unknown(%d)\"", - sa->u.net->family); + ad->common.u.net->family); if (sock_type_names[ad->net.type]) audit_log_format(ab, " sock_type=\"%s\"", sock_type_names[ad->net.type]); @@ -98,6 +141,19 @@ void audit_net_cb(struct audit_buffer *ab, void *va) net_mask_names, NET_PERMS_MASK); } } + if (ad->common.u.net->family == PF_UNIX) { + if (ad->net.addr || !ad->common.u.net->sk) + audit_unix_addr(ab, "addr", + unix_addr(ad->net.addr), + ad->net.addrlen); + else + audit_unix_sk_addr(ab, "addr", ad->common.u.net->sk); + if (ad->request & NET_PEER_MASK) { + audit_unix_addr(ab, "peer_addr", + unix_addr(ad->net.peer.addr), + ad->net.peer.addrlen); + } + } if (ad->peer) { audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, @@ -105,45 +161,123 @@ void audit_net_cb(struct audit_buffer *ab, void *va) } } +/* standard permission lookup pattern - supports early bailout */ +int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy, + aa_state_t state, u32 request, + struct aa_perms *p, struct apparmor_audit_data *ad) +{ + struct aa_perms perms; + + AA_BUG(!profile); + AA_BUG(!policy); + + + if (state || !p) + p = aa_lookup_perms(policy, state); + perms = *p; + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, ad, + audit_net_cb); +} + +/* only continue match if + * insufficient current perms at current state + * indicates there are more perms in later state + * Returns: perms struct if early match + */ +static struct aa_perms *early_match(struct aa_policydb *policy, + aa_state_t state, u32 request) +{ + struct aa_perms *p; + + p = aa_lookup_perms(policy, state); + if (((p->allow & request) != request) && (p->allow & AA_CONT_MATCH)) + return NULL; + return p; +} + +static aa_state_t aa_dfa_match_be16(struct aa_dfa *dfa, aa_state_t state, + u16 data) +{ + __be16 buffer = cpu_to_be16(data); + + return aa_dfa_match_len(dfa, state, (char *) &buffer, 2); +} + +/** + * aa_match_to_prot - match the af, type, protocol triplet + * @policy: policy being matched + * @state: state to start in + * @request: permissions being requested, ignored if @p == NULL + * @af: socket address family + * @type: socket type + * @protocol: socket protocol + * @p: output - pointer to permission associated with match + * @info: output - pointer to string describing failure + * + * RETURNS: state match stopped in. + * + * If @(p) is assigned a value the returned state will be the + * corresponding state. Will not set @p on failure or if match completes + * only if an early match occurs + */ +aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state, + u32 request, u16 af, int type, int protocol, + struct aa_perms **p, const char **info) +{ + state = aa_dfa_match_be16(policy->dfa, state, (u16)af); + if (!state) { + *info = "failed af match"; + return state; + } + state = aa_dfa_match_be16(policy->dfa, state, (u16)type); + if (state) { + if (p) + *p = early_match(policy, state, request); + if (!p || !*p) { + state = aa_dfa_match_be16(policy->dfa, state, (u16)protocol); + if (!state) + *info = "failed protocol match"; + } + } else { + *info = "failed type match"; + } + + return state; +} + /* Generic af perm */ int aa_profile_af_perm(struct aa_profile *profile, struct apparmor_audit_data *ad, u32 request, u16 family, - int type) + int type, int protocol) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); - struct aa_perms perms = { }; + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; aa_state_t state; - __be16 buffer[2]; AA_BUG(family >= AF_MAX); AA_BUG(type < 0 || type >= SOCK_MAX); + AA_BUG(profile_unconfined(profile)); if (profile_unconfined(profile)) return 0; - state = RULE_MEDIATES(rules, AA_CLASS_NET); + state = RULE_MEDIATES_NET(rules); if (!state) return 0; - - buffer[0] = cpu_to_be16(family); - buffer[1] = cpu_to_be16((u16) type); - state = aa_dfa_match_len(rules->policy->dfa, state, (char *) &buffer, - 4); - perms = *aa_lookup_perms(rules->policy, state); - aa_apply_modes_to_perms(profile, &perms); - - return aa_check_perms(profile, &perms, request, ad, audit_net_cb); + state = aa_match_to_prot(rules->policy, state, request, family, type, + protocol, &p, &ad->info); + return aa_do_perms(profile, rules->policy, state, request, p, ad); } int aa_af_perm(const struct cred *subj_cred, struct aa_label *label, const char *op, u32 request, u16 family, int type, int protocol) { struct aa_profile *profile; - DEFINE_AUDIT_NET(ad, op, NULL, family, type, protocol); + DEFINE_AUDIT_NET(ad, op, subj_cred, NULL, family, type, protocol); return fn_for_each_confined(label, profile, aa_profile_af_perm(profile, &ad, request, family, - type)); + type, protocol)); } static int aa_label_sk_perm(const struct cred *subj_cred, @@ -157,9 +291,9 @@ static int aa_label_sk_perm(const struct cred *subj_cred, AA_BUG(!label); AA_BUG(!sk); - if (ctx->label != kernel_t && !unconfined(label)) { + if (rcu_access_pointer(ctx->label) != kernel_t && !unconfined(label)) { struct aa_profile *profile; - DEFINE_AUDIT_SK(ad, op, sk); + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); ad.subj_cred = subj_cred; error = fn_for_each_confined(label, profile, @@ -187,12 +321,16 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label, - const char *op, u32 request, struct socket *sock) + const char *op, u32 request, struct file *file) { + struct socket *sock = (struct socket *) file->private_data; + AA_BUG(!label); AA_BUG(!sock); AA_BUG(!sock->sk); + if (sock->sk->sk_family == PF_UNIX) + return aa_unix_file_perm(subj_cred, label, op, request, file); return aa_label_sk_perm(subj_cred, label, op, request, sock->sk); } @@ -223,8 +361,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid, { int i, ret; struct aa_perms perms = { }; - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; if (rules->secmark_count == 0) return 0; @@ -257,7 +394,7 @@ int apparmor_secmark_check(struct aa_label *label, char *op, u32 request, u32 secid, const struct sock *sk) { struct aa_profile *profile; - DEFINE_AUDIT_SK(ad, op, sk); + DEFINE_AUDIT_SK(ad, op, NULL, sk); return fn_for_each_confined(label, profile, aa_secmark_perm(profile, request, secid, diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index d0244fab0653..50d5345ff5cb 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -243,6 +243,9 @@ static void free_ruleset(struct aa_ruleset *rules) { int i; + if (!rules) + return; + aa_put_pdb(rules->file); aa_put_pdb(rules->policy); aa_free_cap_rules(&rules->caps); @@ -259,8 +262,6 @@ struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp) struct aa_ruleset *rules; rules = kzalloc(sizeof(*rules), gfp); - if (rules) - INIT_LIST_HEAD(&rules->list); return rules; } @@ -277,10 +278,9 @@ struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp) */ void aa_free_profile(struct aa_profile *profile) { - struct aa_ruleset *rule, *tmp; struct rhashtable *rht; - AA_DEBUG("%s(%p)\n", __func__, profile); + AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, profile); if (!profile) return; @@ -299,10 +299,9 @@ void aa_free_profile(struct aa_profile *profile) * at this point there are no tasks that can have a reference * to rules */ - list_for_each_entry_safe(rule, tmp, &profile->rules, list) { - list_del_init(&rule->list); - free_ruleset(rule); - } + for (int i = 0; i < profile->n_rules; i++) + free_ruleset(profile->label.rules[i]); + kfree_sensitive(profile->dirname); if (profile->data) { @@ -331,10 +330,12 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, gfp_t gfp) { struct aa_profile *profile; - struct aa_ruleset *rules; - /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(struct_size(profile, label.vec, 2), gfp); + /* freed by free_profile - usually through aa_put_profile + * this adds space for a single ruleset in the rules section of the + * label + */ + profile = kzalloc(struct_size(profile, label.rules, 1), gfp); if (!profile) return NULL; @@ -343,13 +344,11 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, if (!aa_label_init(&profile->label, 1, gfp)) goto fail; - INIT_LIST_HEAD(&profile->rules); - /* allocate the first ruleset, but leave it empty */ - rules = aa_alloc_ruleset(gfp); - if (!rules) + profile->label.rules[0] = aa_alloc_ruleset(gfp); + if (!profile->label.rules[0]) goto fail; - list_add(&rules->list, &profile->rules); + profile->n_rules = 1; /* update being set needed by fs interface */ if (!proxy) { @@ -364,6 +363,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, profile->label.flags |= FLAG_PROFILE; profile->label.vec[0] = profile; + profile->signal = SIGKILL; /* refcount released by caller */ return profile; @@ -373,6 +373,41 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, return NULL; } +static inline bool ANY_RULE_MEDIATES(struct aa_profile *profile, + unsigned char class) +{ + int i; + + for (i = 0; i < profile->n_rules; i++) { + if (RULE_MEDIATES(profile->label.rules[i], class)) + return true; + } + return false; +} + +/* set of rules that are mediated by unconfined */ +static int unconfined_mediates[] = { AA_CLASS_NS, AA_CLASS_IO_URING, 0 }; + +/* must be called after profile rulesets and start information is setup */ +void aa_compute_profile_mediates(struct aa_profile *profile) +{ + int c; + + if (profile_unconfined(profile)) { + int *pos; + + for (pos = unconfined_mediates; *pos; pos++) { + if (ANY_RULE_MEDIATES(profile, *pos)) + profile->label.mediates |= ((u64) 1) << AA_CLASS_NS; + } + return; + } + for (c = 0; c <= AA_CLASS_LAST; c++) { + if (ANY_RULE_MEDIATES(profile, c)) + profile->label.mediates |= ((u64) 1) << c; + } +} + /* TODO: profile accounting - setup in remove */ /** @@ -463,7 +498,7 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, } /** - * __create_missing_ancestors - create place holders for missing ancestores + * __create_missing_ancestors - create place holders for missing ancestors * @ns: namespace to lookup profile in (NOT NULL) * @hname: hierarchical profile name to find parent of (NOT NULL) * @gfp: type of allocation. @@ -621,13 +656,15 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, /* TODO: ideally we should inherit abi from parent */ profile->label.flags |= FLAG_NULL; profile->attach.xmatch = aa_get_pdb(nullpdb); - rules = list_first_entry(&profile->rules, typeof(*rules), list); + rules = profile->label.rules[0]; rules->file = aa_get_pdb(nullpdb); rules->policy = aa_get_pdb(nullpdb); + aa_compute_profile_mediates(profile); if (parent) { profile->path_flags = parent->path_flags; - + /* override/inherit what is mediated from parent */ + profile->label.mediates = parent->label.mediates; /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); profile->ns = aa_get_ns(parent->ns); @@ -833,8 +870,8 @@ bool aa_policy_admin_capable(const struct cred *subj_cred, bool capable = policy_ns_capable(subj_cred, label, user_ns, CAP_MAC_ADMIN) == 0; - AA_DEBUG("cap_mac_admin? %d\n", capable); - AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); + AA_DEBUG(DEBUG_POLICY, "cap_mac_admin? %d\n", capable); + AA_DEBUG(DEBUG_POLICY, "policy locked? %d\n", aa_g_lock_policy); return aa_policy_view_capable(subj_cred, label, ns) && capable && !aa_g_lock_policy; @@ -843,11 +880,11 @@ bool aa_policy_admin_capable(const struct cred *subj_cred, bool aa_current_policy_view_capable(struct aa_ns *ns) { struct aa_label *label; - bool res; + bool needput, res; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); res = aa_policy_view_capable(current_cred(), label, ns); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return res; } @@ -855,11 +892,11 @@ bool aa_current_policy_view_capable(struct aa_ns *ns) bool aa_current_policy_admin_capable(struct aa_ns *ns) { struct aa_label *label; - bool res; + bool needput, res; - label = __begin_current_label_crit_section(); + label = __begin_current_label_crit_section(&needput); res = aa_policy_admin_capable(current_cred(), label, ns); - __end_current_label_crit_section(label); + __end_current_label_crit_section(label, needput); return res; } @@ -1068,7 +1105,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, goto out; /* ensure that profiles are all for the same ns - * TODO: update locking to remove this constaint. All profiles in + * TODO: update locking to remove this constraint. All profiles in * the load set must succeed as a set or the load will * fail. Sort ent list and take ns locks in hierarchy order */ diff --git a/security/apparmor/policy_compat.c b/security/apparmor/policy_compat.c index 423227670e68..cfc2207e5a12 100644 --- a/security/apparmor/policy_compat.c +++ b/security/apparmor/policy_compat.c @@ -286,10 +286,10 @@ static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor) AA_BUG(!dfa); - for (state = 0; state < state_count; state++) + for (state = 0; state < state_count; state++) { ACCEPT_TABLE(dfa)[state] = state * factor; - kvfree(dfa->tables[YYTD_ID_ACCEPT2]); - dfa->tables[YYTD_ID_ACCEPT2] = NULL; + ACCEPT_TABLE2(dfa)[state] = factor > 1 ? ACCEPT_FLAG_OWNER : 0; + } } /* TODO: merge different dfa mappings into single map_policy fn */ diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 1f02cfe1d974..64783ca3b0f2 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -107,7 +107,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) struct aa_ns *ns; ns = kzalloc(sizeof(*ns), GFP_KERNEL); - AA_DEBUG("%s(%p)\n", __func__, ns); + AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, ns); if (!ns) return NULL; if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL)) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 992b74c50d64..7523971e37d9 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -29,6 +29,7 @@ #include "include/policy.h" #include "include/policy_unpack.h" #include "include/policy_compat.h" +#include "include/signal.h" /* audit callback for unpack fields */ static void audit_cb(struct audit_buffer *ab, void *va) @@ -598,8 +599,8 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules) fail: if (rules->secmark) { for (i = 0; i < size; i++) - kfree(rules->secmark[i].label); - kfree(rules->secmark); + kfree_sensitive(rules->secmark[i].label); + kfree_sensitive(rules->secmark); rules->secmark_count = 0; rules->secmark = NULL; } @@ -716,6 +717,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, void *pos = e->pos; int i, flags, error = -EPROTO; ssize_t size; + u32 version = 0; pdb = aa_alloc_pdb(GFP_KERNEL); if (!pdb) @@ -733,6 +735,9 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, if (pdb->perms) { /* perms table present accept is index */ flags = TO_ACCEPT1_FLAG(YYTD_DATA32); + if (aa_unpack_u32(e, &version, "permsv") && version > 2) + /* accept2 used for dfa flags */ + flags |= TO_ACCEPT2_FLAG(YYTD_DATA32); } else { /* packed perms in accept1 and accept2 */ flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | @@ -770,6 +775,21 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, } } + /* accept2 is in some cases being allocated, even with perms */ + if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + /* add dfa flags table missing in v2 */ + u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen; + u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags; + size_t tsize = table_size(noents, tdflags); + + pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL); + if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + *info = "failed to alloc dfa flags table"; + goto out; + } + pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents; + pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags; + } /* * Unfortunately due to a bug in earlier userspaces, a * transition table may be present even when the dfa is @@ -783,9 +803,13 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, if (!pdb->dfa && pdb->trans.table) aa_free_str_table(&pdb->trans); - /* TODO: move compat mapping here, requires dfa merging first */ - /* TODO: move verify here, it has to be done after compat mappings */ - + /* TODO: + * - move compat mapping here, requires dfa merging first + * - move verify here, it has to be done after compat mappings + * - move free of unneeded trans table here, has to be done + * after perm mapping. + */ +out: *policy = pdb; return 0; @@ -862,7 +886,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) error = -ENOMEM; goto fail; } - rules = list_first_entry(&profile->rules, typeof(*rules), list); + rules = profile->label.rules[0]; /* profile renaming is optional */ (void) aa_unpack_str(e, &profile->rename, "rename"); @@ -898,6 +922,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) (void) aa_unpack_strdup(e, &disconnected, "disconnected"); profile->disconnected = disconnected; + /* optional */ + (void) aa_unpack_u32(e, &profile->signal, "kill"); + if (profile->signal < 1 || profile->signal > MAXMAPPED_SIG) { + info = "profile kill.signal invalid value"; + goto fail; + } /* per profile debug flags (complain, audit) */ if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) { info = "profile missing flags"; @@ -1101,6 +1131,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + aa_compute_profile_mediates(profile); + return profile; fail: @@ -1215,21 +1247,32 @@ static bool verify_perm(struct aa_perms *perm) static bool verify_perms(struct aa_policydb *pdb) { int i; + int xidx, xmax = -1; for (i = 0; i < pdb->size; i++) { if (!verify_perm(&pdb->perms[i])) return false; /* verify indexes into str table */ - if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE && - (pdb->perms[i].xindex & AA_X_INDEX_MASK) >= pdb->trans.size) - return false; + if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + xidx = pdb->perms[i].xindex & AA_X_INDEX_MASK; + if (xidx >= pdb->trans.size) + return false; + if (xmax < xidx) + xmax = xidx; + } if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) return false; if (pdb->perms[i].label && pdb->perms[i].label >= pdb->trans.size) return false; } - + /* deal with incorrectly constructed string tables */ + if (xmax == -1) { + aa_free_str_table(&pdb->trans); + } else if (pdb->trans.size > xmax + 1) { + if (!aa_resize_str_table(&pdb->trans, xmax + 1, GFP_KERNEL)) + return false; + } return true; } @@ -1243,8 +1286,8 @@ static bool verify_perms(struct aa_policydb *pdb) */ static int verify_profile(struct aa_profile *profile) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; + if (!rules) return 0; diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index 5b2ba88ae9e2..cf18744dafe2 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -9,6 +9,8 @@ #include "include/policy.h" #include "include/policy_unpack.h" +#include + #define TEST_STRING_NAME "TEST_STRING" #define TEST_STRING_DATA "testing" #define TEST_STRING_BUF_OFFSET \ @@ -80,7 +82,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, *(buf + 1) = strlen(TEST_U32_NAME) + 1; strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32; - *((__le32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = cpu_to_le32(TEST_U32_DATA); + put_unaligned_le32(TEST_U32_DATA, buf + 3 + strlen(TEST_U32_NAME) + 2); buf = e->start + TEST_NAMED_U64_BUF_OFFSET; *buf = AA_NAME; @@ -103,7 +105,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, *(buf + 1) = strlen(TEST_ARRAY_NAME) + 1; strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY; - *((__le16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = cpu_to_le16(TEST_ARRAY_SIZE); + put_unaligned_le16(TEST_ARRAY_SIZE, buf + 3 + strlen(TEST_ARRAY_NAME) + 2); return e; } diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index e3857e3d7c6c..ce40f15d4952 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -125,12 +125,14 @@ int aa_setprocattr_changehat(char *args, size_t size, int flags) for (count = 0; (hat < end) && count < 16; ++count) { char *next = hat + strlen(hat) + 1; hats[count] = hat; - AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" + AA_DEBUG(DEBUG_DOMAIN, + "%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" , __func__, current->pid, token, count, hat); hat = next; } } else - AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", + AA_DEBUG(DEBUG_DOMAIN, + "%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", __func__, current->pid, token, count, ""); return aa_change_hat(hats, count, token, flags); diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index dcc94c3153d5..8e80db3ae21c 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -89,8 +89,7 @@ static int profile_setrlimit(const struct cred *subj_cred, struct aa_profile *profile, unsigned int resource, struct rlimit *new_rlim) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; int e = 0; if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max > @@ -165,9 +164,7 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l) * to the lesser of the tasks hard limit and the init tasks soft limit */ label_for_each_confined(i, old_l, old) { - struct aa_ruleset *rules = list_first_entry(&old->rules, - typeof(*rules), - list); + struct aa_ruleset *rules = old->label.rules[0]; if (rules->rlimits.mask) { int j; @@ -185,9 +182,7 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l) /* set any new hard limits as dictated by the new profile */ label_for_each_confined(i, new_l, new) { - struct aa_ruleset *rules = list_first_entry(&new->rules, - typeof(*rules), - list); + struct aa_ruleset *rules = new->label.rules[0]; int j; if (!rules->rlimits.mask) diff --git a/security/apparmor/task.c b/security/apparmor/task.c index c87fb9f4ac18..c9bc9cc69475 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -228,8 +228,7 @@ static int profile_ptrace_perm(const struct cred *cred, struct aa_label *peer, u32 request, struct apparmor_audit_data *ad) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; struct aa_perms perms = { }; ad->subj_cred = cred; @@ -246,7 +245,7 @@ static int profile_tracee_perm(const struct cred *cred, struct apparmor_audit_data *ad) { if (profile_unconfined(tracee) || unconfined(tracer) || - !ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE)) + !label_mediates(&tracee->label, AA_CLASS_PTRACE)) return 0; return profile_ptrace_perm(cred, tracee, tracer, request, ad); @@ -260,7 +259,7 @@ static int profile_tracer_perm(const struct cred *cred, if (profile_unconfined(tracer)) return 0; - if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE)) + if (label_mediates(&tracer->label, AA_CLASS_PTRACE)) return profile_ptrace_perm(cred, tracer, tracee, request, ad); /* profile uses the old style capability check for ptrace */ @@ -324,9 +323,7 @@ int aa_profile_ns_perm(struct aa_profile *profile, ad->request = request; if (!profile_unconfined(profile)) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), - list); + struct aa_ruleset *rules = profile->label.rules[0]; aa_state_t state; state = RULE_MEDIATES(rules, ad->class); diff --git a/security/commoncap.c b/security/commoncap.c index 28d4248bf001..6bd4adeb4795 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -856,12 +856,6 @@ static void handle_privileged_root(struct linux_binprm *bprm, bool has_fcap, #define __cap_full(field, cred) \ cap_issubset(CAP_FULL_SET, cred->cap_##field) -static inline bool __is_setuid(struct cred *new, const struct cred *old) -{ return !uid_eq(new->euid, old->uid); } - -static inline bool __is_setgid(struct cred *new, const struct cred *old) -{ return !gid_eq(new->egid, old->gid); } - /* * 1) Audit candidate if current->cap_effective is set * @@ -891,7 +885,7 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old, (root_privileged() && __is_suid(root, new) && !__cap_full(effective, new)) || - (!__is_setuid(new, old) && + (uid_eq(new->euid, old->euid) && ((has_fcap && __cap_gained(permitted, new, old)) || __cap_gained(ambient, new, old)))) @@ -917,7 +911,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file) /* Process setpcap binaries and capabilities for uid 0 */ const struct cred *old = current_cred(); struct cred *new = bprm->cred; - bool effective = false, has_fcap = false, is_setid; + bool effective = false, has_fcap = false, id_changed; int ret; kuid_t root_uid; @@ -941,9 +935,9 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file) * * In addition, if NO_NEW_PRIVS, then ensure we get no new privs. */ - is_setid = __is_setuid(new, old) || __is_setgid(new, old); + id_changed = !uid_eq(new->euid, old->euid) || !in_group_p(new->egid); - if ((is_setid || __cap_gained(permitted, new, old)) && + if ((id_changed || __cap_gained(permitted, new, old)) && ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) || !ptracer_capable(current, new->user_ns))) { /* downgrade; they get no more than they had, and maybe less */ @@ -960,7 +954,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file) new->sgid = new->fsgid = new->egid; /* File caps or setid cancels ambient. */ - if (has_fcap || is_setid) + if (has_fcap || id_changed) cap_clear(new->cap_ambient); /* @@ -993,7 +987,9 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file) return -EPERM; /* Check for privilege-elevated exec. */ - if (is_setid || + if (id_changed || + !uid_eq(new->euid, old->uid) || + !gid_eq(new->egid, old->gid) || (!__is_real(root_uid, new) && (effective || __cap_grew(permitted, ambient, new)))) diff --git a/security/inode.c b/security/inode.c index 3913501621fa..43382ef8896e 100644 --- a/security/inode.c +++ b/security/inode.c @@ -112,18 +112,20 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, struct dentry *dentry; struct inode *dir, *inode; int error; + bool pinned = false; if (!(mode & S_IFMT)) mode = (mode & S_IALLUGO) | S_IFREG; pr_debug("securityfs: creating file '%s'\n",name); - error = simple_pin_fs(&fs_type, &mount, &mount_count); - if (error) - return ERR_PTR(error); - - if (!parent) + if (!parent) { + error = simple_pin_fs(&fs_type, &mount, &mount_count); + if (error) + return ERR_PTR(error); + pinned = true; parent = mount->mnt_root; + } dir = d_inode(parent); @@ -159,7 +161,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, inode->i_fop = fops; } d_instantiate(dentry, inode); - dget(dentry); inode_unlock(dir); return dentry; @@ -168,7 +169,8 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, dentry = ERR_PTR(error); out: inode_unlock(dir); - simple_release_fs(&mount, &mount_count); + if (pinned) + simple_release_fs(&mount, &mount_count); return dentry; } @@ -279,6 +281,12 @@ struct dentry *securityfs_create_symlink(const char *name, } EXPORT_SYMBOL_GPL(securityfs_create_symlink); +static void remove_one(struct dentry *victim) +{ + if (victim->d_parent == victim->d_sb->s_root) + simple_release_fs(&mount, &mount_count); +} + /** * securityfs_remove - removes a file or directory from the securityfs filesystem * @@ -291,43 +299,11 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink); * This function is required to be called in order for the file to be * removed. No automatic cleanup of files will happen when a module is * removed; you are responsible here. + * + * AV: when applied to directory it will take all children out; no need to call + * it for descendents if ancestor is getting killed. */ void securityfs_remove(struct dentry *dentry) -{ - struct inode *dir; - - if (IS_ERR_OR_NULL(dentry)) - return; - - dir = d_inode(dentry->d_parent); - inode_lock(dir); - if (simple_positive(dentry)) { - if (d_is_dir(dentry)) - simple_rmdir(dir, dentry); - else - simple_unlink(dir, dentry); - dput(dentry); - } - inode_unlock(dir); - simple_release_fs(&mount, &mount_count); -} -EXPORT_SYMBOL_GPL(securityfs_remove); - -static void remove_one(struct dentry *victim) -{ - simple_release_fs(&mount, &mount_count); -} - -/** - * securityfs_recursive_remove - recursively removes a file or directory - * - * @dentry: a pointer to a the dentry of the file or directory to be removed. - * - * This function recursively removes a file or directory in securityfs that was - * previously created with a call to another securityfs function (like - * securityfs_create_file() or variants thereof.) - */ -void securityfs_recursive_remove(struct dentry *dentry) { if (IS_ERR_OR_NULL(dentry)) return; @@ -336,7 +312,7 @@ void securityfs_recursive_remove(struct dentry *dentry) simple_recursive_removal(dentry, remove_one); simple_release_fs(&mount, &mount_count); } -EXPORT_SYMBOL_GPL(securityfs_recursive_remove); +EXPORT_SYMBOL_GPL(securityfs_remove); #ifdef CONFIG_SECURITY static struct dentry *lsm_dentry; diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 9b907c2fee60..b0d2aad27850 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -17,7 +17,6 @@ #include "evm.h" static struct dentry *evm_dir; -static struct dentry *evm_init_tpm; static struct dentry *evm_symlink; #ifdef CONFIG_EVM_ADD_XATTRS @@ -286,7 +285,7 @@ static int evm_init_xattrs(void) { evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL, &evm_xattr_ops); - if (!evm_xattrs || IS_ERR(evm_xattrs)) + if (IS_ERR(evm_xattrs)) return -EFAULT; return 0; @@ -301,21 +300,22 @@ static int evm_init_xattrs(void) int __init evm_init_secfs(void) { int error = 0; + struct dentry *dentry; evm_dir = securityfs_create_dir("evm", integrity_dir); - if (!evm_dir || IS_ERR(evm_dir)) + if (IS_ERR(evm_dir)) return -EFAULT; - evm_init_tpm = securityfs_create_file("evm", 0660, - evm_dir, NULL, &evm_key_ops); - if (!evm_init_tpm || IS_ERR(evm_init_tpm)) { + dentry = securityfs_create_file("evm", 0660, + evm_dir, NULL, &evm_key_ops); + if (IS_ERR(dentry)) { error = -EFAULT; goto out; } evm_symlink = securityfs_create_symlink("evm", NULL, "integrity/evm/evm", NULL); - if (!evm_symlink || IS_ERR(evm_symlink)) { + if (IS_ERR(evm_symlink)) { error = -EFAULT; goto out; } @@ -328,7 +328,6 @@ int __init evm_init_secfs(void) return 0; out: securityfs_remove(evm_symlink); - securityfs_remove(evm_init_tpm); securityfs_remove(evm_dir); return error; } diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index e4a79a9b2d58..87045b09f120 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -116,28 +116,6 @@ void ima_putc(struct seq_file *m, void *data, int datalen) seq_putc(m, *(char *)data++); } -static struct dentry **ascii_securityfs_measurement_lists __ro_after_init; -static struct dentry **binary_securityfs_measurement_lists __ro_after_init; -static int securityfs_measurement_list_count __ro_after_init; - -static void lookup_template_data_hash_algo(int *algo_idx, enum hash_algo *algo, - struct seq_file *m, - struct dentry **lists) -{ - struct dentry *dentry; - int i; - - dentry = file_dentry(m->file); - - for (i = 0; i < securityfs_measurement_list_count; i++) { - if (dentry == lists[i]) { - *algo_idx = i; - *algo = ima_algo_array[i].algo; - break; - } - } -} - /* print format: * 32bit-le=pcr# * char[n]=template digest @@ -160,9 +138,10 @@ int ima_measurements_show(struct seq_file *m, void *v) algo_idx = ima_sha1_idx; algo = HASH_ALGO_SHA1; - if (m->file != NULL) - lookup_template_data_hash_algo(&algo_idx, &algo, m, - binary_securityfs_measurement_lists); + 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; @@ -256,9 +235,10 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) algo_idx = ima_sha1_idx; algo = HASH_ALGO_SHA1; - if (m->file != NULL) - lookup_template_data_hash_algo(&algo_idx, &algo, m, - ascii_securityfs_measurement_lists); + 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; @@ -396,11 +376,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, static struct dentry *ima_dir; static struct dentry *ima_symlink; -static struct dentry *binary_runtime_measurements; -static struct dentry *ascii_runtime_measurements; -static struct dentry *runtime_measurements_count; -static struct dentry *violations; -static struct dentry *ima_policy; enum ima_fs_flags { IMA_FS_BUSY, @@ -417,64 +392,33 @@ static const struct seq_operations ima_policy_seqops = { }; #endif -static void __init remove_securityfs_measurement_lists(struct dentry **lists) -{ - int i; - - if (lists) { - for (i = 0; i < securityfs_measurement_list_count; i++) - securityfs_remove(lists[i]); - - kfree(lists); - } -} - static int __init create_securityfs_measurement_lists(void) { - char file_name[NAME_MAX + 1]; - struct dentry *dentry; - u16 algo; - int i; - - securityfs_measurement_list_count = NR_BANKS(ima_tpm_chip); + int count = NR_BANKS(ima_tpm_chip); if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) - securityfs_measurement_list_count++; + count++; - ascii_securityfs_measurement_lists = - kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), - GFP_KERNEL); - if (!ascii_securityfs_measurement_lists) - return -ENOMEM; - - binary_securityfs_measurement_lists = - kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), - GFP_KERNEL); - if (!binary_securityfs_measurement_lists) - return -ENOMEM; - - for (i = 0; i < securityfs_measurement_list_count; i++) { - algo = ima_algo_array[i].algo; + for (int i = 0; i < count; i++) { + u16 algo = ima_algo_array[i].algo; + char file_name[NAME_MAX + 1]; + struct dentry *dentry; sprintf(file_name, "ascii_runtime_measurements_%s", hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, - ima_dir, NULL, + ima_dir, (void *)(uintptr_t)i, &ima_ascii_measurements_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); - ascii_securityfs_measurement_lists[i] = dentry; - sprintf(file_name, "binary_runtime_measurements_%s", hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, - ima_dir, NULL, + ima_dir, (void *)(uintptr_t)i, &ima_measurements_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); - - binary_securityfs_measurement_lists[i] = dentry; } return 0; @@ -533,8 +477,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) ima_update_policy(); #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) - securityfs_remove(ima_policy); - ima_policy = NULL; + securityfs_remove(file->f_path.dentry); #elif defined(CONFIG_IMA_WRITE_POLICY) clear_bit(IMA_FS_BUSY, &ima_fs_flags); #elif defined(CONFIG_IMA_READ_POLICY) @@ -553,11 +496,9 @@ static const struct file_operations ima_measure_policy_ops = { int __init ima_fs_init(void) { + struct dentry *dentry; int ret; - ascii_securityfs_measurement_lists = NULL; - binary_securityfs_measurement_lists = NULL; - ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) return PTR_ERR(ima_dir); @@ -573,57 +514,45 @@ int __init ima_fs_init(void) if (ret != 0) goto out; - binary_runtime_measurements = - securityfs_create_symlink("binary_runtime_measurements", ima_dir, + dentry = securityfs_create_symlink("binary_runtime_measurements", ima_dir, "binary_runtime_measurements_sha1", NULL); - if (IS_ERR(binary_runtime_measurements)) { - ret = PTR_ERR(binary_runtime_measurements); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - ascii_runtime_measurements = - securityfs_create_symlink("ascii_runtime_measurements", ima_dir, + dentry = securityfs_create_symlink("ascii_runtime_measurements", ima_dir, "ascii_runtime_measurements_sha1", NULL); - if (IS_ERR(ascii_runtime_measurements)) { - ret = PTR_ERR(ascii_runtime_measurements); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - runtime_measurements_count = - securityfs_create_file("runtime_measurements_count", + dentry = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_measurements_count_ops); - if (IS_ERR(runtime_measurements_count)) { - ret = PTR_ERR(runtime_measurements_count); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - violations = - securityfs_create_file("violations", S_IRUSR | S_IRGRP, + dentry = securityfs_create_file("violations", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_htable_violations_ops); - if (IS_ERR(violations)) { - ret = PTR_ERR(violations); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, + dentry = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, &ima_measure_policy_ops); - if (IS_ERR(ima_policy)) { - ret = PTR_ERR(ima_policy); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } return 0; out: - securityfs_remove(ima_policy); - securityfs_remove(violations); - securityfs_remove(runtime_measurements_count); - securityfs_remove(ascii_runtime_measurements); - securityfs_remove(binary_runtime_measurements); - remove_securityfs_measurement_lists(ascii_securityfs_measurement_lists); - remove_securityfs_measurement_lists(binary_securityfs_measurement_lists); - securityfs_measurement_list_count = 0; securityfs_remove(ima_symlink); securityfs_remove(ima_dir); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index f99ab1a3b0f0..cdd225f65a62 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ima.h" @@ -38,11 +39,30 @@ int ima_appraise; int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1; static int hash_setup_done; +static int ima_disabled __ro_after_init; static struct notifier_block ima_lsm_policy_notifier = { .notifier_call = ima_lsm_policy_change, }; +static int __init ima_setup(char *str) +{ + if (!is_kdump_kernel()) { + pr_info("Warning: ima setup option only permitted in kdump"); + return 1; + } + + if (strncmp(str, "off", 3) == 0) + ima_disabled = 1; + else if (strncmp(str, "on", 2) == 0) + ima_disabled = 0; + else + pr_err("Invalid ima setup option: \"%s\" , please specify ima=on|off.", str); + + return 1; +} +__setup("ima=", ima_setup); + static int __init hash_setup(char *str) { struct ima_template_desc *template_desc = ima_template_desc_current(); @@ -1186,6 +1206,12 @@ static int __init init_ima(void) { int error; + /*Note that turning IMA off is intentionally limited to kdump kernel.*/ + if (ima_disabled && is_kdump_kernel()) { + pr_info("IMA functionality is disabled"); + return 0; + } + ima_appraise_parse_cmdline(); ima_init_template_list(); hash_setup(CONFIG_IMA_DEFAULT_HASH); diff --git a/security/integrity/platform_certs/load_powerpc.c b/security/integrity/platform_certs/load_powerpc.c index c85febca3343..714c961a00f5 100644 --- a/security/integrity/platform_certs/load_powerpc.c +++ b/security/integrity/platform_certs/load_powerpc.c @@ -75,12 +75,13 @@ static int __init load_powerpc_certs(void) return -ENODEV; // Check for known secure boot implementations from OPAL or PLPKS - if (strcmp("ibm,edk2-compat-v1", buf) && strcmp("ibm,plpks-sb-v1", buf)) { + if (strcmp("ibm,edk2-compat-v1", buf) && strcmp("ibm,plpks-sb-v1", buf) && + strcmp("ibm,plpks-sb-v0", buf)) { pr_err("Unsupported secvar implementation \"%s\", not loading certs\n", buf); return -ENODEV; } - if (strcmp("ibm,plpks-sb-v1", buf) == 0) + if (strcmp("ibm,plpks-sb-v1", buf) == 0 || strcmp("ibm,plpks-sb-v0", buf) == 0) /* PLPKS authenticated variables ESL data is prefixed with 8 bytes of timestamp */ offset = 8; diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig index 3c75bf267da4..a110a6cd848b 100644 --- a/security/ipe/Kconfig +++ b/security/ipe/Kconfig @@ -6,6 +6,7 @@ menuconfig SECURITY_IPE bool "Integrity Policy Enforcement (IPE)" depends on SECURITY && SECURITYFS && AUDIT && AUDITSYSCALL + select CRYPTO_LIB_SHA256 select PKCS7_MESSAGE_PARSER select SYSTEM_DATA_VERIFICATION select IPE_PROP_DM_VERITY if DM_VERITY diff --git a/security/ipe/audit.c b/security/ipe/audit.c index 9668ecc5acd5..de5fed62592e 100644 --- a/security/ipe/audit.c +++ b/security/ipe/audit.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "ipe.h" #include "eval.h" @@ -17,7 +17,7 @@ #define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY") -#define IPE_AUDIT_HASH_ALG "sha256" +#define IPE_AUDIT_HASH_ALG "sha256" /* keep in sync with audit_policy() */ #define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\ "policy_digest=" IPE_AUDIT_HASH_ALG ":" @@ -182,37 +182,14 @@ static void audit_policy(struct audit_buffer *ab, const char *audit_format, const struct ipe_policy *const p) { - SHASH_DESC_ON_STACK(desc, tfm); - struct crypto_shash *tfm; - u8 *digest = NULL; + u8 digest[SHA256_DIGEST_SIZE]; - tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, 0, 0); - if (IS_ERR(tfm)) - return; - - desc->tfm = tfm; - - digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL); - if (!digest) - goto out; - - if (crypto_shash_init(desc)) - goto out; - - if (crypto_shash_update(desc, p->pkcs7, p->pkcs7len)) - goto out; - - if (crypto_shash_final(desc, digest)) - goto out; + sha256(p->pkcs7, p->pkcs7len, digest); audit_log_format(ab, audit_format, p->parsed->name, p->parsed->version.major, p->parsed->version.minor, p->parsed->version.rev); - audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm)); - -out: - kfree(digest); - crypto_free_shash(tfm); + audit_log_n_hex(ab, digest, sizeof(digest)); } /** diff --git a/security/ipe/fs.c b/security/ipe/fs.c index f40e50bfd2e7..0bb9468b8026 100644 --- a/security/ipe/fs.c +++ b/security/ipe/fs.c @@ -12,11 +12,8 @@ #include "policy.h" #include "audit.h" -static struct dentry *np __ro_after_init; static struct dentry *root __ro_after_init; struct dentry *policy_root __ro_after_init; -static struct dentry *audit_node __ro_after_init; -static struct dentry *enforce_node __ro_after_init; /** * setaudit() - Write handler for the securityfs node, "ipe/success_audit" @@ -200,27 +197,26 @@ static int __init ipe_init_securityfs(void) { int rc = 0; struct ipe_policy *ap; + struct dentry *dentry; if (!ipe_enabled) return -EOPNOTSUPP; root = securityfs_create_dir("ipe", NULL); - if (IS_ERR(root)) { - rc = PTR_ERR(root); - goto err; - } + if (IS_ERR(root)) + return PTR_ERR(root); - audit_node = securityfs_create_file("success_audit", 0600, root, + dentry = securityfs_create_file("success_audit", 0600, root, NULL, &audit_fops); - if (IS_ERR(audit_node)) { - rc = PTR_ERR(audit_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } - enforce_node = securityfs_create_file("enforce", 0600, root, NULL, + dentry = securityfs_create_file("enforce", 0600, root, NULL, &enforce_fops); - if (IS_ERR(enforce_node)) { - rc = PTR_ERR(enforce_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } @@ -237,18 +233,14 @@ static int __init ipe_init_securityfs(void) goto err; } - np = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); - if (IS_ERR(np)) { - rc = PTR_ERR(np); + dentry = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } return 0; err: - securityfs_remove(np); - securityfs_remove(policy_root); - securityfs_remove(enforce_node); - securityfs_remove(audit_node); securityfs_remove(root); return rc; } diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c index db26032ccbe1..9d92d8a14b13 100644 --- a/security/ipe/policy_fs.c +++ b/security/ipe/policy_fs.c @@ -438,7 +438,7 @@ static const struct ipefs_file policy_subdir[] = { */ void ipe_del_policyfs_node(struct ipe_policy *p) { - securityfs_recursive_remove(p->policyfs); + securityfs_remove(p->policyfs); p->policyfs = NULL; } @@ -485,6 +485,6 @@ int ipe_new_policyfs_node(struct ipe_policy *p) return 0; err: - securityfs_recursive_remove(policyfs); + securityfs_remove(policyfs); return rc; } diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 6fee7c20f64d..c04f8879ad03 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -895,6 +895,7 @@ static bool is_access_to_paths_allowed( /* Stops when a rule from each layer grants access. */ if (allowed_parent1 && allowed_parent2) break; + jump_up: if (walker_path.dentry == walker_path.mnt->mnt_root) { if (follow_up(&walker_path)) { diff --git a/security/landlock/id.c b/security/landlock/id.c index 56f7cc0fc744..838c3ed7bb82 100644 --- a/security/landlock/id.c +++ b/security/landlock/id.c @@ -119,6 +119,12 @@ static u64 get_id_range(size_t number_of_ids, atomic64_t *const counter, #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST +static u8 get_random_u8_positive(void) +{ + /* max() evaluates its arguments once. */ + return max(1, get_random_u8()); +} + static void test_range1_rand0(struct kunit *const test) { atomic64_t counter; @@ -127,9 +133,10 @@ static void test_range1_rand0(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 0), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 1); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 1); } static void test_range1_rand1(struct kunit *const test) @@ -140,9 +147,10 @@ static void test_range1_rand1(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 1), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } static void test_range1_rand15(struct kunit *const test) @@ -153,9 +161,10 @@ static void test_range1_rand15(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 15), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 16); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 16); } static void test_range1_rand16(struct kunit *const test) @@ -166,9 +175,10 @@ static void test_range1_rand16(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 16), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 1); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 1); } static void test_range2_rand0(struct kunit *const test) @@ -179,9 +189,10 @@ static void test_range2_rand0(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 0), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } static void test_range2_rand1(struct kunit *const test) @@ -192,9 +203,10 @@ static void test_range2_rand1(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 1), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 3); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 3); } static void test_range2_rand2(struct kunit *const test) @@ -205,9 +217,10 @@ static void test_range2_rand2(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 2), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 4); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 4); } static void test_range2_rand15(struct kunit *const test) @@ -218,9 +231,10 @@ static void test_range2_rand15(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 15), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 17); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 17); } static void test_range2_rand16(struct kunit *const test) @@ -231,9 +245,10 @@ static void test_range2_rand16(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 16), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 33eafb71e4f3..0116e9f93ffe 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -303,7 +303,6 @@ static int get_path_from_fd(const s32 fd, struct path *const path) if ((fd_file(f)->f_op == &ruleset_fops) || (fd_file(f)->f_path.mnt->mnt_flags & MNT_INTERNAL) || (fd_file(f)->f_path.dentry->d_sb->s_flags & SB_NOUSER) || - d_is_negative(fd_file(f)->f_path.dentry) || IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry))) return -EBADFD; diff --git a/security/security.c b/security/security.c index 596d41818577..ad163f06bf7a 100644 --- a/security/security.c +++ b/security/security.c @@ -2181,7 +2181,7 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry, } /** - * security_inode_mkdir() - Check if creation a new director is allowed + * security_inode_mkdir() - Check if creating a new directory is allowed * @dir: parent directory * @dentry: new directory * @mode: new directory mode @@ -2622,6 +2622,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name) call_void_hook(inode_post_removexattr, dentry, name); } +/** + * security_inode_file_setattr() - check if setting fsxattr is allowed + * @dentry: file to set filesystem extended attributes on + * @fa: extended attributes to set on the inode + * + * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on + * inode + * + * Return: Returns 0 if permission is granted. + */ +int security_inode_file_setattr(struct dentry *dentry, struct file_kattr *fa) +{ + return call_int_hook(inode_file_setattr, dentry, fa); +} + +/** + * security_inode_file_getattr() - check if retrieving fsxattr is allowed + * @dentry: file to retrieve filesystem extended attributes from + * @fa: extended attributes to get + * + * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on + * inode + * + * Return: Returns 0 if permission is granted. + */ +int security_inode_file_getattr(struct dentry *dentry, struct file_kattr *fa) +{ + return call_int_hook(inode_file_getattr, dentry, fa); +} + /** * security_inode_need_killpriv() - Check if security_inode_killpriv() required * @dentry: associated dentry diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 595ceb314aeb..c95a5874bf7d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3181,6 +3181,8 @@ static inline void task_avdcache_update(struct task_security_struct *tsec, 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.permissive_neveraudit = + (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)); } /** @@ -3207,10 +3209,13 @@ static int selinux_inode_permission(struct inode *inode, int requested) if (!mask) return 0; + tsec = selinux_cred(current_cred()); + if (task_avdcache_permnoaudit(tsec)) + return 0; + isec = inode_security_rcu(inode, requested & MAY_NOT_BLOCK); if (IS_ERR(isec)) return PTR_ERR(isec); - tsec = selinux_cred(current_cred()); perms = file_mask_to_av(inode->i_mode, mask); rc = task_avdcache_search(tsec, isec, &avdc); @@ -3274,6 +3279,13 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, static int selinux_inode_getattr(const struct path *path) { + struct task_security_struct *tsec; + + tsec = selinux_cred(current_cred()); + + if (task_avdcache_permnoaudit(tsec)) + return 0; + return path_has_perm(current_cred(), path, FILE__GETATTR); } @@ -3480,6 +3492,18 @@ static int selinux_inode_removexattr(struct mnt_idmap *idmap, return -EACCES; } +static int selinux_inode_file_setattr(struct dentry *dentry, + struct file_kattr *fa) +{ + return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); +} + +static int selinux_inode_file_getattr(struct dentry *dentry, + struct file_kattr *fa) +{ + return dentry_has_perm(current_cred(), dentry, FILE__GETATTR); +} + static int selinux_path_notify(const struct path *path, u64 mask, unsigned int obj_type) { @@ -7350,6 +7374,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr), LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr), LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr), + LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr), + LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr), LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl), LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl), LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl), diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 281f40103663..01b5167fee1a 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -65,6 +65,10 @@ static inline u32 avc_audit_required(u32 requested, struct av_decision *avd, int result, u32 auditdeny, u32 *deniedp) { u32 denied, audited; + + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + return 0; + denied = requested & ~avd->allowed; if (unlikely(denied)) { audited = denied & avd->auditdeny; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 6ee7dc4dfd6e..1d7ac59015a1 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -49,9 +49,17 @@ struct task_security_struct { u32 seqno; /* AVC sequence number */ unsigned int dir_spot; /* dir cache index to check first */ struct avdc_entry dir[TSEC_AVDC_DIR_SIZE]; /* dir entries */ + bool permissive_neveraudit; /* permissive and neveraudit */ } avdcache; } __randomize_layout; +static inline bool task_avdcache_permnoaudit(struct task_security_struct *tsec) +{ + return (tsec->avdcache.permissive_neveraudit && + tsec->sid == tsec->avdcache.sid && + tsec->avdcache.seqno == avc_policy_seqno()); +} + enum label_initialized { LABEL_INVALID, /* invalid or not initialized */ LABEL_INITIALIZED, /* initialized */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 278c144c22d6..8201e6a3ac0f 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -47,10 +47,11 @@ #define POLICYDB_VERSION_GLBLUB 32 #define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */ #define POLICYDB_VERSION_COND_XPERMS 34 /* extended permissions in conditional policies */ +#define POLICYDB_VERSION_NEVERAUDIT 35 /* neveraudit types */ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COND_XPERMS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEVERAUDIT /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f @@ -260,6 +261,7 @@ struct extended_perms { /* definitions of av_decision.flags */ #define AVD_FLAGS_PERMISSIVE 0x0001 +#define AVD_FLAGS_NEVERAUDIT 0x0002 void security_compute_av(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index e67a8ce4b64c..9aa1d03ab612 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1072,6 +1072,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) 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, @@ -2097,8 +2098,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) pr_err("SELinux: %s: failed while creating inodes\n", __func__); - selinux_fs_info_free(sb); - return ret; } diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 383fd2d70878..1382eb3bfde1 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -40,7 +40,8 @@ int hashtab_init(struct hashtab *h, u32 nel_hint) h->htable = NULL; if (size) { - h->htable = kcalloc(size, sizeof(*h->htable), GFP_KERNEL); + h->htable = kcalloc(size, sizeof(*h->htable), + GFP_KERNEL | __GFP_NOWARN); if (!h->htable) return -ENOMEM; h->size = size; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9ea971943713..91df3db6a88c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -160,6 +160,11 @@ static const struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_NEVERAUDIT, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static const struct policydb_compat_info * @@ -531,6 +536,7 @@ static void policydb_init(struct policydb *p) ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); + ebitmap_init(&p->neveraudit_map); } /* @@ -852,6 +858,7 @@ void policydb_destroy(struct policydb *p) ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); + ebitmap_destroy(&p->neveraudit_map); } /* @@ -2538,6 +2545,12 @@ int policydb_read(struct policydb *p, struct policy_file *fp) goto bad; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_read(&p->neveraudit_map, fp); + if (rc) + goto bad; + } + rc = -EINVAL; info = policydb_lookup_compat(p->policyvers); if (!info) { @@ -3723,6 +3736,12 @@ int policydb_write(struct policydb *p, struct policy_file *fp) return rc; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_write(&p->neveraudit_map, fp); + if (rc) + return rc; + } + num_syms = info->sym_num; for (i = 0; i < num_syms; i++) { struct policy_data pd; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 25650224b6e7..89a180b1742f 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -300,6 +300,8 @@ struct policydb { struct ebitmap permissive_map; + struct ebitmap neveraudit_map; + /* length of this policy when it was loaded */ size_t len; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 7becf3808818..713130bd43c4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1153,6 +1153,14 @@ void security_compute_av(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1172,6 +1180,8 @@ void security_compute_av(u32 ssid, policydb->allow_unknown); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; @@ -1208,6 +1218,14 @@ void security_compute_av_user(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1225,6 +1243,8 @@ void security_compute_av_user(u32 ssid, NULL); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; @@ -1909,11 +1929,17 @@ static int security_compute_sid(u32 ssid, goto out_unlock; } /* Obtain the sid for the context. */ - rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); - if (rc == -ESTALE) { - rcu_read_unlock(); - context_destroy(&newcontext); - goto retry; + if (context_equal(scontext, &newcontext)) + *out_sid = ssid; + else if (context_equal(tcontext, &newcontext)) + *out_sid = tsid; + else { + rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + context_destroy(&newcontext); + goto retry; + } } out_unlock: rcu_read_unlock(); diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 90ec4ef1b082..61d56b0c2be1 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -94,7 +94,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; - ctx->ctx_len = str_len; + ctx->ctx_len = str_len + 1; memcpy(ctx->ctx_str, &uctx[1], str_len); ctx->ctx_str[str_len] = '\0'; rc = security_context_to_sid(ctx->ctx_str, str_len, diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 840bb9cfe789..a66f258cafaa 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1269,62 +1269,62 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) stream = &data->stream; guard(mutex)(&stream->device->lock); - switch (_IOC_NR(cmd)) { - case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): + switch (cmd) { + case SNDRV_COMPRESS_IOCTL_VERSION: return put_user(SNDRV_COMPRESS_VERSION, (int __user *)arg) ? -EFAULT : 0; - case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): + case SNDRV_COMPRESS_GET_CAPS: return snd_compr_get_caps(stream, arg); #ifndef COMPR_CODEC_CAPS_OVERFLOW - case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): + case SNDRV_COMPRESS_GET_CODEC_CAPS: return snd_compr_get_codec_caps(stream, arg); #endif - case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): + case SNDRV_COMPRESS_SET_PARAMS: return snd_compr_set_params(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): + case SNDRV_COMPRESS_GET_PARAMS: return snd_compr_get_params(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): + case SNDRV_COMPRESS_SET_METADATA: return snd_compr_set_metadata(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): + case SNDRV_COMPRESS_GET_METADATA: return snd_compr_get_metadata(stream, arg); } if (stream->direction == SND_COMPRESS_ACCEL) { #if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) - switch (_IOC_NR(cmd)) { - case _IOC_NR(SNDRV_COMPRESS_TASK_CREATE): + switch (cmd) { + case SNDRV_COMPRESS_TASK_CREATE: return snd_compr_task_create(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_TASK_FREE): + case SNDRV_COMPRESS_TASK_FREE: return snd_compr_task_seq(stream, arg, snd_compr_task_free_one); - case _IOC_NR(SNDRV_COMPRESS_TASK_START): + case SNDRV_COMPRESS_TASK_START: return snd_compr_task_start_ioctl(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_TASK_STOP): + case SNDRV_COMPRESS_TASK_STOP: return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one); - case _IOC_NR(SNDRV_COMPRESS_TASK_STATUS): + case SNDRV_COMPRESS_TASK_STATUS: return snd_compr_task_status_ioctl(stream, arg); } #endif return -ENOTTY; } - switch (_IOC_NR(cmd)) { - case _IOC_NR(SNDRV_COMPRESS_TSTAMP): + switch (cmd) { + case SNDRV_COMPRESS_TSTAMP: return snd_compr_tstamp(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_AVAIL): + case SNDRV_COMPRESS_AVAIL: return snd_compr_ioctl_avail(stream, arg); - case _IOC_NR(SNDRV_COMPRESS_PAUSE): + case SNDRV_COMPRESS_PAUSE: return snd_compr_pause(stream); - case _IOC_NR(SNDRV_COMPRESS_RESUME): + case SNDRV_COMPRESS_RESUME: return snd_compr_resume(stream); - case _IOC_NR(SNDRV_COMPRESS_START): + case SNDRV_COMPRESS_START: return snd_compr_start(stream); - case _IOC_NR(SNDRV_COMPRESS_STOP): + case SNDRV_COMPRESS_STOP: return snd_compr_stop(stream); - case _IOC_NR(SNDRV_COMPRESS_DRAIN): + case SNDRV_COMPRESS_DRAIN: return snd_compr_drain(stream); - case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): + case SNDRV_COMPRESS_PARTIAL_DRAIN: return snd_compr_partial_drain(stream); - case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): + case SNDRV_COMPRESS_NEXT_TRACK: return snd_compr_next_track(stream); } diff --git a/sound/core/control.c b/sound/core/control.c index 11f660fc6f2b..9c3fd5113a61 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1435,7 +1435,7 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, names = ue->priv_data; for (; item > 0; --item) names += strlen(names) + 1; - strcpy(uinfo->value.enumerated.name, names); + strscpy(uinfo->value.enumerated.name, names); return 0; } diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index e9c60dce59fb..c364bd126ac8 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -138,7 +139,7 @@ static int __init snd_hrtimer_init(void) return err; timer->module = THIS_MODULE; - strcpy(timer->name, "HR timer"); + strscpy(timer->name, "HR timer"); timer->hw = hrtimer_hw; timer->hw.resolution = resolution; timer->hw.ticks = NANO_SEC / resolution; diff --git a/sound/core/init.c b/sound/core/init.c index 114fb87de990..c372b3228785 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -723,27 +723,25 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, * ("card" conflicts with proc directories) */ if (!*id || !strncmp(id, "card", 4)) { - strcpy(id, "Default"); + strscpy(card->id, "Default"); is_default = true; } len = strlen(id); for (loops = 0; loops < SNDRV_CARDS; loops++) { - char *spos; char sfxstr[5]; /* "_012" */ - int sfxlen; + int sfxlen, slen; if (card_id_ok(card, id)) return; /* OK */ /* Add _XYZ suffix */ - sprintf(sfxstr, "_%X", loops + 1); - sfxlen = strlen(sfxstr); + sfxlen = scnprintf(sfxstr, sizeof(sfxstr), "_%X", loops + 1); if (len + sfxlen >= sizeof(card->id)) - spos = id + sizeof(card->id) - sfxlen - 1; + slen = sizeof(card->id) - sfxlen - 1; else - spos = id + len; - strcpy(spos, sfxstr); + slen = len; + strscpy(id + slen, sfxstr, sizeof(card->id) - slen); } /* fallback to the default id */ if (!is_default) { @@ -801,7 +799,7 @@ static ssize_t id_store(struct device *dev, struct device_attribute *attr, guard(mutex)(&snd_card_mutex); if (!card_id_ok(NULL, buf1)) return -EEXIST; - strcpy(card->id, buf1); + strscpy(card->id, buf1); snd_info_card_id_change(card); return count; diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 05fc8911479c..e839a4bb93f8 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -991,7 +991,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct slot *pslot; struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *rslot; - char str[64]; + const char *str; /* check if already assigned */ if (mixer->slots[ptr->oss_id].get_volume && ! replace_old) @@ -1014,11 +1014,11 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, if (kctl->info(kctl, uinfo)) return 0; - strcpy(str, ptr->name); + str = ptr->name; if (!strcmp(str, "Master")) - strcpy(str, "Mix"); - if (!strcmp(str, "Master Mono")) - strcpy(str, "Mix Mono"); + str = "Mix"; + else if (!strcmp(str, "Master Mono")) + str = "Mix Mono"; slot.capture_item = 0; if (!strcmp(uinfo->value.enumerated.name, str)) { slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index a42ec7f5a1da..54eb9bd8eb21 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -377,12 +377,10 @@ struct snd_pcm_mmap_status_x32 { s32 pad1; u32 hw_ptr; u32 pad2; /* alignment */ - s64 tstamp_sec; - s64 tstamp_nsec; + struct __snd_timespec64 tstamp; snd_pcm_state_t suspended_state; s32 pad3; - s64 audio_tstamp_sec; - s64 audio_tstamp_nsec; + struct __snd_timespec64 audio_tstamp; } __packed; struct snd_pcm_mmap_control_x32 { @@ -418,9 +416,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, if (snd_BUG_ON(!runtime)) return -EINVAL; - if (get_user(sflags, &src->flags) || - get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - get_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src)) return -EFAULT; if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); @@ -450,15 +446,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (put_user(sstatus.state, &src->s.status.state) || - put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) || - put_user(sstatus.suspended_state, &src->s.status.suspended_state) || - put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) || - put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) || - put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - put_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src)) return -EFAULT; return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 853ac5bb33ff..1eab940fa2e5 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "pcm_local.h" @@ -3051,49 +3052,87 @@ static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream) return snd_pcm_delay(substream, NULL); } +#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \ + __label__ failed, failed_begin; \ + int __err = -EFAULT; \ + typeof(*(__ptr)) __user *__src = (__ptr); \ + \ + if (!user_read_access_begin(__src, sizeof(*__src))) \ + goto failed_begin; \ + unsafe_get_user(__f, &__src->flags, failed); \ + unsafe_get_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \ + unsafe_get_user(__c.avail_min, &__src->c.control.avail_min, failed); \ + __err = 0; \ +failed: \ + user_read_access_end(); \ +failed_begin: \ + __err; \ +}) + +#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \ + __label__ failed, failed_begin; \ + int __err = -EFAULT; \ + typeof(*(__ptr)) __user *__src = (__ptr); \ + \ + if (!user_write_access_begin(__src, sizeof(*__src))) \ + goto failed_begin; \ + unsafe_put_user(__s.state, &__src->s.status.state, failed); \ + unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \ + unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp.tv_sec, failed); \ + unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp.tv_nsec, failed); \ + unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp.tv_sec, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp.tv_nsec, failed);\ + unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \ + unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \ + __err = 0; \ +failed: \ + user_write_access_end(); \ +failed_begin: \ + __err; \ +}) + static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr __user *_sync_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_sync_ptr sync_ptr; volatile struct snd_pcm_mmap_status *status; volatile struct snd_pcm_mmap_control *control; + u32 sflags; + struct snd_pcm_mmap_control scontrol; + struct snd_pcm_mmap_status sstatus; int err; - memset(&sync_ptr, 0, sizeof(sync_ptr)); - if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags))) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, _sync_ptr)) return -EFAULT; - if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control))) - return -EFAULT; status = runtime->status; control = runtime->control; - if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) { + if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); if (err < 0) return err; } scoped_guard(pcm_stream_lock_irq, substream) { - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = pcm_lib_apply_appl_ptr(substream, - sync_ptr.c.control.appl_ptr); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr); if (err < 0) return err; } else { - sync_ptr.c.control.appl_ptr = control->appl_ptr; + scontrol.appl_ptr = control->appl_ptr; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = sync_ptr.c.control.avail_min; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; else - sync_ptr.c.control.avail_min = control->avail_min; - sync_ptr.s.status.state = status->state; - sync_ptr.s.status.hw_ptr = status->hw_ptr; - sync_ptr.s.status.tstamp = status->tstamp; - sync_ptr.s.status.suspended_state = status->suspended_state; - sync_ptr.s.status.audio_tstamp = status->audio_tstamp; + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, _sync_ptr)) return -EFAULT; return 0; } @@ -3102,11 +3141,9 @@ struct snd_pcm_mmap_status32 { snd_pcm_state_t state; s32 pad1; u32 hw_ptr; - s32 tstamp_sec; - s32 tstamp_nsec; + struct __snd_timespec tstamp; snd_pcm_state_t suspended_state; - s32 audio_tstamp_sec; - s32 audio_tstamp_nsec; + struct __snd_timespec audio_tstamp; } __packed; struct snd_pcm_mmap_control32 { @@ -3130,13 +3167,23 @@ struct snd_pcm_sync_ptr32 { static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t boundary; + snd_pcm_uframes_t border; + int order; if (! runtime->buffer_size) return 0; - boundary = runtime->buffer_size; - while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) - boundary *= 2; - return boundary; + + border = 0x7fffffffUL - runtime->buffer_size; + if (runtime->buffer_size > border) + return runtime->buffer_size; + + order = __fls(border) - __fls(runtime->buffer_size); + boundary = runtime->buffer_size << order; + + if (boundary <= border) + return boundary; + else + return boundary / 2; } static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, @@ -3154,9 +3201,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, if (snd_BUG_ON(!runtime)) return -EINVAL; - if (get_user(sflags, &src->flags) || - get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - get_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src)) return -EFAULT; if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); @@ -3189,15 +3234,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (put_user(sstatus.state, &src->s.status.state) || - put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) || - put_user(sstatus.suspended_state, &src->s.status.suspended_state) || - put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) || - put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) || - put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - put_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src)) return -EFAULT; return 0; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 70a958ac1112..20d36a346cca 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -631,9 +631,9 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->flags = rmidi->info_flags; if (substream->inactive) info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE; - strcpy(info->id, rmidi->id); - strcpy(info->name, rmidi->name); - strcpy(info->subname, substream->name); + strscpy(info->id, rmidi->id); + strscpy(info->name, rmidi->name); + strscpy(info->subname, substream->name); info->subdevices_count = substream->pstr->substream_count; info->subdevices_avail = (substream->pstr->substream_count - substream->pstr->substream_opened); diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index e6d7d83ed0e7..973f057eb731 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -79,7 +79,7 @@ snd_seq_oss_create_client(void) system_client = rc; /* create announcement receiver port */ - strcpy(port->name, "Receiver"); + strscpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ port->type = 0; @@ -347,7 +347,7 @@ alloc_seq_queue(struct seq_oss_devinfo *dp) memset(&qinfo, 0, sizeof(qinfo)); qinfo.owner = system_client; qinfo.locked = 1; - strcpy(qinfo.name, "OSS Sequencer Emulation"); + strscpy(qinfo.name, "OSS Sequencer Emulation"); rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo); if (rc < 0) return rc; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 880240924bfd..aa9c956d2581 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1256,7 +1256,7 @@ static void get_client_info(struct snd_seq_client *cptr, /* fill the info fields */ info->type = cptr->type; - strcpy(info->name, cptr->name); + strscpy(info->name, cptr->name); info->filter = cptr->filter; info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index ba52a77eda38..581e138a3115 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -344,7 +344,7 @@ snd_seq_midisynth_probe(struct device *_dev) info->stream = SNDRV_RAWMIDI_STREAM_INPUT; info->subdevice = p; if (snd_rawmidi_info_select(card, info) >= 0) - strcpy(port->name, info->subname); + strscpy(port->name, info->subname); if (! port->name[0]) { if (info->name[0]) { if (ports > 1) diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 853920f79016..5b5603e5970b 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -146,7 +146,7 @@ int __init snd_seq_system_client_init(void) } /* register timer */ - strcpy(port->name, "Timer"); + strscpy(port->name, "Timer"); port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ port->kernel = &pcallbacks; @@ -160,7 +160,7 @@ int __init snd_seq_system_client_init(void) goto error_port; /* register announcement port */ - strcpy(port->name, "Announce"); + strscpy(port->name, "Announce"); port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ pcallbacks.event_input = NULL; pcallbacks.subscribe = sys_announce_subscribe; diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 1255351b59ce..27247babb16d 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -310,7 +310,7 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) SNDRV_SEQ_PORT_TYPE_HARDWARE | SNDRV_SEQ_PORT_TYPE_PORT; port->midi_channels = 16; - strcpy(port->name, "MIDI 2.0"); + strscpy(port->name, "MIDI 2.0"); memset(&pcallbacks, 0, sizeof(pcallbacks)); pcallbacks.owner = THIS_MODULE; pcallbacks.private_data = client; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index b4672613c261..9e7fd4993a10 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -497,7 +497,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi &rmidi); if (err < 0) return err; - strcpy(rmidi->name, rmidi->id); + strscpy(rmidi->name, rmidi->id); rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (rdev == NULL) { snd_device_free(card, rmidi); diff --git a/sound/core/timer.c b/sound/core/timer.c index 8072183c33d3..d9fff5c87613 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1195,7 +1195,7 @@ static int snd_timer_register_system(void) err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); if (err < 0) return err; - strcpy(timer->name, "system timer"); + strscpy(timer->name, "system timer"); timer->hw = snd_timer_system; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) { @@ -2139,14 +2139,14 @@ static int snd_utimer_create(struct snd_timer_uinfo *utimer_info, goto err_take_id; } + utimer->id = utimer_id; + utimer->name = kasprintf(GFP_KERNEL, "snd-utimer%d", utimer_id); if (!utimer->name) { err = -ENOMEM; goto err_get_name; } - utimer->id = utimer_id; - tid.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; tid.card = -1; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 6c318a5903ab..a8902dc45dc1 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1368,7 +1369,7 @@ static int loopback_pcm_new(struct loopback *loopback, pcm->private_data = loopback; pcm->info_flags = 0; - strcpy(pcm->name, "Loopback PCM"); + strscpy(pcm->name, "Loopback PCM"); loopback->pcm[device] = pcm; return 0; @@ -1631,7 +1632,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify) struct loopback_setup *setup; int err, dev, substr, substr_count, idx; - strcpy(card->mixername, "Loopback Mixer"); + strscpy(card->mixername, "Loopback Mixer"); for (dev = 0; dev < 2; dev++) { pcm = loopback->pcm[dev]; substr_count = @@ -1824,8 +1825,8 @@ static int loopback_probe(struct platform_device *devptr) loopback_cable_proc_new(loopback, 0); loopback_cable_proc_new(loopback, 1); loopback_timer_source_proc_new(loopback); - strcpy(card->driver, "Loopback"); - strcpy(card->shortname, "Loopback"); + strscpy(card->driver, "Loopback"); + strscpy(card->shortname, "Loopback"); sprintf(card->longname, "Loopback %i", dev + 1); err = snd_card_register(card); if (err < 0) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 783fe3a22bc9..6dac0b2523c1 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -684,7 +685,7 @@ static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); pcm->private_data = dummy; pcm->info_flags = 0; - strcpy(pcm->name, "Dummy PCM"); + strscpy(pcm->name, "Dummy PCM"); if (!fake_buffer) { snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, @@ -875,7 +876,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy) int err; spin_lock_init(&dummy->mixer_lock); - strcpy(card->mixername, "Dummy Mixer"); + strscpy(card->mixername, "Dummy Mixer"); dummy->iobox = 1; for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { @@ -1083,8 +1084,8 @@ static int snd_dummy_probe(struct platform_device *devptr) err = snd_card_dummy_new_mixer(dummy); if (err < 0) return err; - strcpy(card->driver, "Dummy"); - strcpy(card->shortname, "Dummy"); + strscpy(card->driver, "Dummy"); + strscpy(card->shortname, "Dummy"); sprintf(card->longname, "Dummy %i", dev + 1); dummy_proc_init(dummy); diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index cd01af5fa4ed..d3f9424088d4 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -63,8 +63,8 @@ static int snd_mpu401_create(struct device *devptr, int dev, 0, &card); if (err < 0) return err; - strcpy(card->driver, "MPU-401 UART"); - strcpy(card->shortname, card->driver); + strscpy(card->driver, "MPU-401 UART"); + strscpy(card->shortname, card->driver); sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); if (irq[dev] >= 0) { sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 851f34e2cdd0..91828f496738 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -46,6 +46,7 @@ #include #include #include +#include /* * globals @@ -605,11 +606,11 @@ static void snd_mtpav_set_name(struct mtpav *chip, else if (substream->number >= 8 && substream->number < chip->num_ports * 2) sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); else if (substream->number == chip->num_ports * 2) - strcpy(substream->name, "MTP computer"); + strscpy(substream->name, "MTP computer"); else if (substream->number == chip->num_ports * 2 + 1) - strcpy(substream->name, "MTP ADAT"); + strscpy(substream->name, "MTP ADAT"); else - strcpy(substream->name, "MTP broadcast"); + strscpy(substream->name, "MTP broadcast"); } static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard) @@ -697,8 +698,8 @@ static int snd_mtpav_probe(struct platform_device *dev) if (err < 0) return err; - strcpy(card->driver, "MTPAV"); - strcpy(card->shortname, "MTPAV on parallel port"); + strscpy(card->driver, "MTPAV"); + strscpy(card->shortname, "MTPAV on parallel port"); snprintf(card->longname, sizeof(card->longname), "MTPAV on parallel port at 0x%lx", port); diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 17f215bad0ec..bbeebbe467ea 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -763,7 +764,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card) return err; rmidi->private_data = mts; - strcpy(rmidi->name, CARD_NAME); + strscpy(rmidi->name, CARD_NAME); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -791,7 +792,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card) mts->midi_input_substream[substream->number] = substream; switch(substream->number) { case MTS64_SMPTE_SUBSTREAM: - strcpy(substream->name, "Miditerminal SMPTE"); + strscpy(substream->name, "Miditerminal SMPTE"); break; default: sprintf(substream->name, @@ -929,8 +930,8 @@ static int snd_mts64_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Cannot create card\n"); return err; } - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "ESI " CARD_NAME); + strscpy(card->driver, DRIVER_NAME); + strscpy(card->shortname, "ESI " CARD_NAME); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index 4e57e3b2f118..cd9642a6689b 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -245,7 +245,7 @@ static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no) tid.subdevice = 0; err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "AdLib timer #1"); + strscpy(timer->name, "AdLib timer #1"); timer->private_data = opl3; timer->hw = snd_opl3_timer1; } @@ -266,7 +266,7 @@ static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no) tid.subdevice = 0; err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "AdLib timer #2"); + strscpy(timer->name, "AdLib timer #2"); timer->private_data = opl3; timer->hw = snd_opl3_timer2; } @@ -497,18 +497,18 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, if (device == 0) hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; #endif - strcpy(hw->name, hw->id); + strscpy(hw->name, hw->id); switch (opl3->hardware & OPL3_HW_MASK) { case OPL3_HW_OPL2: - strcpy(hw->name, "OPL2 FM"); + strscpy(hw->name, "OPL2 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL2; break; case OPL3_HW_OPL3: - strcpy(hw->name, "OPL3 FM"); + strscpy(hw->name, "OPL3 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL3; break; case OPL3_HW_OPL4: - strcpy(hw->name, "OPL4 FM"); + strscpy(hw->name, "OPL4 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL4; break; } @@ -524,7 +524,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, #if IS_ENABLED(CONFIG_SND_SEQUENCER) if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { - strcpy(opl3->seq_dev->name, hw->name); + strscpy(opl3->seq_dev->name, hw->name); *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; } #endif diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 8fa61596875a..ef6b2d533958 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -152,7 +152,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device) opl4->seq_dev_num = seq_device; if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4, sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) { - strcpy(opl4->seq_dev->name, "OPL4 Wavetable"); + strscpy(opl4->seq_dev->name, "OPL4 Wavetable"); *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4; opl4->seq_dev->private_data = opl4; opl4->seq_dev->private_free = snd_opl4_seq_dev_free; diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 39f1e1fe4c44..19b3f306c564 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -555,7 +556,7 @@ static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst) if (err < 0) return err; pcm->private_data = pcmtst; - strcpy(pcm->name, "PCMTest"); + strscpy(pcm->name, "PCMTest"); pcmtst->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcmtst_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcmtst_capture_ops); @@ -613,9 +614,9 @@ static int pcmtst_probe(struct platform_device *pdev) if (err < 0) return err; - strcpy(card->driver, "PCM-TEST Driver"); - strcpy(card->shortname, "PCM-Test"); - strcpy(card->longname, "PCM-Test virtual driver"); + strscpy(card->driver, "PCM-TEST Driver"); + strscpy(card->shortname, "PCM-Test"); + strscpy(card->longname, "PCM-Test virtual driver"); err = snd_card_register(card); if (err < 0) diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index e8482c2290c3..ff6bb375c900 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -122,8 +122,8 @@ static int snd_card_pcsp_probe(int devnum, struct device *dev) if (err < 0) return err; - strcpy(card->driver, "PC-Speaker"); - strcpy(card->shortname, "pcsp"); + strscpy(card->driver, "PC-Speaker"); + strscpy(card->shortname, "pcsp"); sprintf(card->longname, "Internal PC-Speaker at port 0x%x", pcsp_chip.port); diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index d9bc1ea1b53c..80b313f4fcd3 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -340,7 +340,7 @@ int snd_pcsp_new_pcm(struct snd_pcsp *chip) chip->pcm->private_data = chip; chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - strcpy(chip->pcm->name, "pcsp"); + strscpy(chip->pcm->name, "pcsp"); snd_pcm_set_managed_buffer_all(chip->pcm, SNDRV_DMA_TYPE_CONTINUOUS, diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c index c0ae942358b9..27d6150329a8 100644 --- a/sound/drivers/pcsp/pcsp_mixer.c +++ b/sound/drivers/pcsp/pcsp_mixer.c @@ -158,7 +158,7 @@ int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm) if (err < 0) return err; - strcpy(card->mixername, "PC-Speaker"); + strscpy(card->mixername, "PC-Speaker"); return 0; } diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 5e4ef25a83a4..b4fa6625a3d6 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -549,7 +549,7 @@ static int snd_portman_rawmidi_create(struct snd_card *card) return err; rmidi->private_data = pm; - strcpy(rmidi->name, CARD_NAME); + strscpy(rmidi->name, CARD_NAME); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -714,8 +714,8 @@ static int snd_portman_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Cannot create card\n"); return err; } - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, CARD_NAME); + strscpy(card->driver, DRIVER_NAME); + strscpy(card->shortname, CARD_NAME); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index 36409a56c675..21ae053c0576 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -300,7 +300,7 @@ static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata, &snd_serial_generic_input); snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_serial_generic_output); - strcpy(rrawmidi->name, drvdata->card->shortname); + strscpy(rrawmidi->name, drvdata->card->shortname); snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], drvdata->serdev->ctrl->nr); @@ -329,7 +329,7 @@ static int snd_serial_generic_probe(struct serdev_device *serdev) if (err < 0) return err; - strcpy(card->driver, "SerialMIDI"); + strscpy(card->driver, "SerialMIDI"); sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr); sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr); diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 6d0656fcd574..52772ccfc377 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -845,7 +845,7 @@ static int snd_uart16550_rmidi(struct snd_uart16550 *uart, int device, &snd_uart16550_input); snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); - strcpy(rrawmidi->name, "Serial MIDI"); + strscpy(rrawmidi->name, "Serial MIDI"); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | @@ -905,8 +905,8 @@ static int snd_serial_probe(struct platform_device *devptr) if (err < 0) return err; - strcpy(card->driver, "Serial"); - strcpy(card->shortname, "Serial MIDI (UART16550A)"); + strscpy(card->driver, "Serial"); + strscpy(card->shortname, "Serial MIDI (UART16550A)"); err = snd_uart16550_create(card, port[dev], irq[dev], speed[dev], base[dev], adaptor[dev], droponfull[dev], diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 5f7b65ad63e3..a204f42d1026 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -95,11 +95,11 @@ static int snd_virmidi_probe(struct platform_device *devptr) if (err < 0) return err; vmidi->midi[idx] = rmidi; - strcpy(rmidi->name, "Virtual Raw MIDI"); + strscpy(rmidi->name, "Virtual Raw MIDI"); } - strcpy(card->driver, "VirMIDI"); - strcpy(card->shortname, "VirMIDI"); + strscpy(card->driver, "VirMIDI"); + strscpy(card->shortname, "VirMIDI"); sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); err = snd_card_register(card); diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index e2def8719ed2..7f25fa7c0404 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -806,7 +806,7 @@ struct vx_core *snd_vx_create(struct snd_card *card, chip->card = card; card->private_data = chip; - strcpy(card->driver, hw->name); + strscpy(card->driver, hw->name); sprintf(card->shortname, "Digigram %s", hw->name); vx_proc_init(chip); diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 53d78eb13c53..0a51ecdc084a 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -903,7 +903,7 @@ int snd_vx_mixer_new(struct vx_core *chip) struct snd_card *card = chip->card; char name[32]; - strcpy(card->mixername, card->driver); + strscpy(card->mixername, card->driver); /* output level controls */ for (i = 0; i < chip->hw->num_outs; i++) { diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index cbc77ca1ebdd..defc489494af 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1226,7 +1226,7 @@ int snd_vx_pcm_new(struct vx_core *chip) pcm->private_free = snd_vx_pcm_free; pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[i] = pcm; } diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 2ba5962beb30..4ebaeff16455 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -105,9 +105,9 @@ name_device(struct snd_bebob *bebob) if (err < 0) goto end; - strcpy(bebob->card->driver, "BeBoB"); - strcpy(bebob->card->shortname, model); - strcpy(bebob->card->mixername, model); + strscpy(bebob->card->driver, "BeBoB"); + strscpy(bebob->card->shortname, model); + strscpy(bebob->card->mixername, model); snprintf(bebob->card->longname, sizeof(bebob->card->longname), "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d", vendor, model, hw_id, revision, diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c index 6f9331655d43..5779e99a6bb2 100644 --- a/sound/firewire/bebob/bebob_hwdep.c +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -183,7 +183,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "BeBoB"); + strscpy(hwdep->name, "BeBoB"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; hwdep->ops = ops; hwdep->private_data = bebob; diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c index ffc0b97782d6..d165dd427bd3 100644 --- a/sound/firewire/dice/dice-hwdep.c +++ b/sound/firewire/dice/dice-hwdep.c @@ -179,7 +179,7 @@ int snd_dice_create_hwdep(struct snd_dice *dice) err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); if (err < 0) return err; - strcpy(hwdep->name, "DICE"); + strscpy(hwdep->name, "DICE"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; hwdep->ops = ops; hwdep->private_data = dice; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 2cf2adb48f2a..cfc19bd0d5dd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -442,7 +442,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) return err; pcm->private_data = dice; pcm->nonatomic = true; - strcpy(pcm->name, dice->card->shortname); + strscpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index d362e4251c68..9675ec14271d 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -102,9 +102,9 @@ static void dice_card_strings(struct snd_dice *dice) unsigned int i; int err; - strcpy(card->driver, "DICE"); + strscpy(card->driver, "DICE"); - strcpy(card->shortname, "DICE"); + strscpy(card->shortname, "DICE"); BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME, card->shortname, @@ -117,16 +117,16 @@ static void dice_card_strings(struct snd_dice *dice) card->shortname[sizeof(card->shortname) - 1] = '\0'; } - strcpy(vendor, "?"); + strscpy(vendor, "?"); fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); - strcpy(model, "?"); + strscpy(model, "?"); fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); snprintf(card->longname, sizeof(card->longname), "%s %s (serial %u) at %s, S%d", vendor, model, dev->config_rom[4] & 0x3fffff, dev_name(&dice->unit->device), 100 << dev->max_speed); - strcpy(card->mixername, "DICE"); + strscpy(card->mixername, "DICE"); } static void dice_card_free(struct snd_card *card) diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c index aadf7d724856..b150607c0a0d 100644 --- a/sound/firewire/digi00x/digi00x-hwdep.c +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -188,7 +188,7 @@ int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) if (err < 0) return err; - strcpy(hwdep->name, "Digi00x"); + strscpy(hwdep->name, "Digi00x"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; hwdep->ops = ops; hwdep->private_data = dg00x; diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 704ae2a5316b..cebc35dcf8cd 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -30,9 +30,9 @@ static int name_card(struct snd_dg00x *dg00x) model = skip_spaces(name); - strcpy(dg00x->card->driver, "Digi00x"); - strcpy(dg00x->card->shortname, model); - strcpy(dg00x->card->mixername, model); + strscpy(dg00x->card->driver, "Digi00x"); + strscpy(dg00x->card->shortname, model); + strscpy(dg00x->card->mixername, model); snprintf(dg00x->card->longname, sizeof(dg00x->card->longname), "Digidesign %s, GUID %08x%08x at %s, S%d", model, fw_dev->config_rom[3], fw_dev->config_rom[4], diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index 8a741b3b0436..ca5c5dee71f2 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -197,7 +197,7 @@ int snd_ff_create_hwdep_devices(struct snd_ff *ff) if (err < 0) return err; - strcpy(hwdep->name, ff->card->driver); + strscpy(hwdep->name, ff->card->driver); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE; hwdep->ops = hwdep_ops; hwdep->private_data = ff; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 6e84e4787259..5d2c4fbf4434 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -27,9 +27,9 @@ static void name_card(struct snd_ff *ff) name = names[ff->unit_version]; - strcpy(ff->card->driver, "Fireface"); - strcpy(ff->card->shortname, name); - strcpy(ff->card->mixername, name); + strscpy(ff->card->driver, "Fireface"); + strscpy(ff->card->shortname, name); + strscpy(ff->card->mixername, name); snprintf(ff->card->longname, sizeof(ff->card->longname), "RME %s, GUID %08x%08x at %s, S%d", name, fw_dev->config_rom[3], fw_dev->config_rom[4], diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index e3ed4e094ccd..69f722244362 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -90,9 +90,9 @@ get_hardware_info(struct snd_efw *efw) (hwinfo->arm_version >> 16) & 0xff); efw->firmware_version = hwinfo->arm_version; - strcpy(efw->card->driver, "Fireworks"); - strcpy(efw->card->shortname, hwinfo->model_name); - strcpy(efw->card->mixername, hwinfo->model_name); + strscpy(efw->card->driver, "Fireworks"); + strscpy(efw->card->shortname, hwinfo->model_name); + strscpy(efw->card->mixername, hwinfo->model_name); scnprintf(efw->card->longname, sizeof(efw->card->longname), "%s %s v%s, GUID %08x%08x at %s, S%d", hwinfo->vendor_name, hwinfo->model_name, version, diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c index 3a53914277d3..037833cd066e 100644 --- a/sound/firewire/fireworks/fireworks_hwdep.c +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -319,7 +319,7 @@ int snd_efw_create_hwdep_device(struct snd_efw *efw) err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "Fireworks"); + strscpy(hwdep->name, "Fireworks"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; hwdep->ops = ops; hwdep->private_data = efw; diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index b1e059f0d473..ee574b5d7406 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -455,7 +455,7 @@ static int isight_create_pcm(struct isight *isight) return err; pcm->private_data = isight; pcm->nonatomic = true; - strcpy(pcm->name, "iSight"); + strscpy(pcm->name, "iSight"); isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; isight->pcm->ops = &ops; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); @@ -638,13 +638,13 @@ static int isight_probe(struct fw_unit *unit, card->private_free = isight_card_free; - strcpy(card->driver, "iSight"); - strcpy(card->shortname, "Apple iSight"); + strscpy(card->driver, "iSight"); + strscpy(card->shortname, "Apple iSight"); snprintf(card->longname, sizeof(card->longname), "Apple iSight (GUID %08x%08x) at %s, S%d", fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&unit->device), 100 << fw_dev->max_speed); - strcpy(card->mixername, "iSight"); + strscpy(card->mixername, "iSight"); err = isight_create_pcm(isight); if (err < 0) diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 88d1f4b56e4b..fa2685665db3 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -111,7 +111,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, events = 0; spin_unlock_irq(&motu->lock); - return events | EPOLLOUT; + return events; } static int hwdep_get_info(struct snd_motu *motu, void __user *arg) @@ -290,7 +290,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu) if (err < 0) return err; - strcpy(hwdep->name, "MOTU"); + strscpy(hwdep->name, "MOTU"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU; hwdep->ops = ops; hwdep->private_data = motu; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index f3b48495acae..7b4d476af348 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -361,7 +361,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) return err; pcm->private_data = motu; pcm->nonatomic = true; - strcpy(pcm->name, motu->card->shortname); + strscpy(pcm->name, motu->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index d14ab5dd5bea..fd2a9dddbfa6 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -41,9 +41,9 @@ static void name_card(struct snd_motu *motu) } } - strcpy(motu->card->driver, "FW-MOTU"); - strcpy(motu->card->shortname, motu->spec->name); - strcpy(motu->card->mixername, motu->spec->name); + strscpy(motu->card->driver, "FW-MOTU"); + strscpy(motu->card->shortname, motu->spec->name); + strscpy(motu->card->mixername, motu->spec->name); snprintf(motu->card->longname, sizeof(motu->card->longname), "MOTU %s (version:%06x), GUID %08x%08x at %s, S%d", motu->spec->name, version, diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c index a0fe99618554..3be214d8a922 100644 --- a/sound/firewire/oxfw/oxfw-hwdep.c +++ b/sound/firewire/oxfw/oxfw-hwdep.c @@ -177,7 +177,7 @@ int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw) err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, oxfw->card->driver); + strscpy(hwdep->name, oxfw->card->driver); hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW; hwdep->ops = hwdep_ops; hwdep->private_data = oxfw; diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 8ca9dde54ec6..e13dc817fc28 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -441,7 +441,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) pcm->private_data = oxfw; pcm->nonatomic = true; - strcpy(pcm->name, oxfw->card->shortname); + strscpy(pcm->name, oxfw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); if (cap > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 98ae0e8cba87..7a985f3cb8f6 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -105,9 +105,9 @@ static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *ent m = model; } - strcpy(oxfw->card->driver, d); - strcpy(oxfw->card->mixername, m); - strcpy(oxfw->card->shortname, m); + strscpy(oxfw->card->driver, d); + strscpy(oxfw->card->mixername, m); + strscpy(oxfw->card->shortname, m); scnprintf(oxfw->card->longname, sizeof(oxfw->card->longname), "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 74eed9505665..8fc30cba29d5 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -265,7 +265,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) if (err < 0) return err; - strcpy(hwdep->name, "Tascam"); + strscpy(hwdep->name, "Tascam"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM; hwdep->ops = ops; hwdep->private_data = tscm; diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index 86880089de28..4f68bb4c58bc 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -73,9 +73,9 @@ static int identify_model(struct snd_tscm *tscm) if (tscm->spec == NULL) return -ENODEV; - strcpy(tscm->card->driver, "FW-TASCAM"); - strcpy(tscm->card->shortname, model); - strcpy(tscm->card->mixername, model); + strscpy(tscm->card->driver, "FW-TASCAM"); + strscpy(tscm->card->shortname, model); + strscpy(tscm->card->mixername, model); snprintf(tscm->card->longname, sizeof(tscm->card->longname), "TASCAM %s, GUID %08x%08x at %s, S%d", model, fw_dev->config_rom[3], fw_dev->config_rom[4], diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index eb488a522572..7797f44b3d0c 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,67 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_HDA_CORE - tristate - select REGMAP +menu "HD-Audio" -config SND_HDA_DSP_LOADER - bool +source "sound/hda/common/Kconfig" +source "sound/hda/controllers/Kconfig" +source "sound/hda/codecs/Kconfig" +source "sound/hda/core/Kconfig" -config SND_HDA_ALIGNED_MMIO - bool - -config SND_HDA_COMPONENT - bool - -config SND_HDA_I915 - bool - select SND_HDA_COMPONENT - -config SND_HDA_EXT_CORE - tristate - select SND_HDA_CORE - -config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" - range 0 32768 - default 0 if SND_DMA_SGBUF - default 64 if !SND_DMA_SGBUF - help - Specifies the default pre-allocated buffer-size in kB for the - HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems using PulseAudio. The default 64 is chosen just - for compatibility reasons. - On x86 systems, the default is zero as S/G allocation works - and no preallocation is needed in most cases. - - Note that the pre-allocation size can be changed dynamically - via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. - -config SND_INTEL_NHLT - bool - # this config should be selected only for Intel ACPI platforms. - # A fallback is provided so that the code compiles in all cases. - -config SND_INTEL_DSP_CONFIG - tristate - select ACPI_NHLT if ACPI - select SND_INTEL_NHLT if ACPI - select SND_INTEL_SOUNDWIRE_ACPI if ACPI - # this config should be selected only for Intel DSP platforms. - # A fallback is provided so that the code compiles in all cases. - -config SND_INTEL_SOUNDWIRE_ACPI - tristate - -config SND_INTEL_BYT_PREFER_SOF - bool "Prefer SOF driver over SST on BY/CHT platforms" - depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL - default n - help - The kernel has 2 drivers for the Low Power Engine audio-block on - Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF - driver. If both drivers are enabled then the kernel will default - to using the old SST driver, unless told otherwise through the - snd_intel_dspcfg.dsp_driver module-parameter. - - Set this option to Y to make the kernel default to the new SOF - driver instead. +endmenu diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 83cceafe0d4c..d9a6def582ef 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,22 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -snd-hda-core-y := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o - -snd-hda-core-y += trace.o -CFLAGS_trace.o := -I$(src) - -# for sync with i915 gfx driver -snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o -snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o - -obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o - -#extended hda -obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ - -snd-intel-dspcfg-y := intel-dsp-config.o -snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o -obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o - -snd-intel-sdw-acpi-y := intel-sdw-acpi.o -obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o +obj-y += core/ +obj-$(CONFIG_SND_HDA) += common/ +obj-$(CONFIG_SND_HDA) += codecs/ +# this must be the last entry after codec drivers; +# otherwise the codec drivers won't be hooked before the PCI probe +# when built in kernel +obj-$(CONFIG_SND_HDA) += controllers/ diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig new file mode 100644 index 000000000000..addbc9424336 --- /dev/null +++ b/sound/hda/codecs/Kconfig @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-only +if SND_HDA + +config SND_HDA_GENERIC_LEDS + bool + +config SND_HDA_CODEC_ANALOG + tristate "Build Analog Devices HD-audio codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include Analog Devices HD-audio codec support in + snd-hda-intel driver, such as AD1986A. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m + +config SND_HDA_CODEC_SIGMATEL + tristate "Build IDT/Sigmatel HD-audio codec support" + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + help + Say Y or M here to include IDT (Sigmatel) HD-audio codec support in + snd-hda-intel driver, such as STAC9200. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m + +config SND_HDA_CODEC_VIA + tristate "Build VIA HD-audio codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include VIA HD-audio codec support in + snd-hda-intel driver, such as VT1708. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_VIA=m + +config SND_HDA_CODEC_CONEXANT + tristate "Build Conexant HD-audio codec support" + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + help + Say Y or M here to include Conexant HD-audio codec support in + snd-hda-intel driver, such as CX20549. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m + +config SND_HDA_CODEC_SENARYTECH + tristate "Build Senarytech HD-audio codec support" + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + help + Say Y or M here to include Senarytech HD-audio codec support in + snd-hda-intel driver, such as SN6186. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m + +config SND_HDA_CODEC_CA0110 + tristate "Build Creative CA0110-IBG codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include Creative CA0110-IBG codec support in + snd-hda-intel driver, found on some Creative X-Fi cards. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m + +config SND_HDA_CODEC_CA0132 + tristate "Build Creative CA0132 codec support" + help + Say Y or M here to include Creative CA0132 codec support in + snd-hda-intel driver. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m + +config SND_HDA_CODEC_CA0132_DSP + bool "Support new DSP code for CA0132 codec" + depends on SND_HDA_CODEC_CA0132 + default y + select SND_HDA_DSP_LOADER + select FW_LOADER + help + Say Y here to enable the DSP for Creative CA0132 for extended + features like equalizer or echo cancellation. + + Note that this option requires the external firmware file + (ctefx.bin). + +config SND_HDA_CODEC_CMEDIA + tristate "Build C-Media HD-audio codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include C-Media HD-audio codec support in + snd-hda-intel driver, such as CMI9880. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m + +config SND_HDA_CODEC_CM9825 + tristate "Build C-Media CM9825 HD-audio codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include C-Media CM9825 HD-audio codec support in + snd-hda-intel driver + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CM9825=m + +config SND_HDA_CODEC_SI3054 + tristate "Build Silicon Labs 3054 HD-modem codec support" + help + Say Y or M here to include Silicon Labs 3054 HD-modem codec + (and compatibles) support in snd-hda-intel driver. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m + +config SND_HDA_GENERIC + tristate "Enable generic HD-audio codec parser" + select SND_CTL_LED if SND_HDA_GENERIC_LEDS + select LEDS_CLASS if SND_HDA_GENERIC_LEDS + help + Say Y or M here to enable the generic HD-audio codec parser + in snd-hda-intel driver. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_GENERIC=m + +source "sound/hda/codecs/realtek/Kconfig" +source "sound/hda/codecs/cirrus/Kconfig" +source "sound/hda/codecs/hdmi/Kconfig" +source "sound/hda/codecs/side-codecs/Kconfig" + +endif # SND_HDA diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile new file mode 100644 index 000000000000..e7f03e281999 --- /dev/null +++ b/sound/hda/codecs/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../common + +snd-hda-codec-generic-y := generic.o +snd-hda-codec-cmedia-y := cmedia.o +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 +snd-hda-codec-si3054-y := si3054.o +snd-hda-codec-via-y := via.o + +obj-y += cirrus/ +obj-y += hdmi/ +obj-y += realtek/ +obj-y += side-codecs/ + +# codec drivers +obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o +obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o +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 +obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o +obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o diff --git a/sound/pci/hda/patch_analog.c b/sound/hda/codecs/analog.c similarity index 90% rename from sound/pci/hda/patch_analog.c rename to sound/hda/codecs/analog.c index 56354fe060a1..33aaeb44c4dc 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/hda/codecs/analog.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, + * HD audio codec driver for AD1882, AD1884, AD1981HD, AD1983, AD1984, * AD1986A, AD1988 * * Copyright (c) 2005-2007 Takashi Iwai @@ -16,11 +16,20 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" +enum { + MODEL_AD1882, + MODEL_AD1884, + MODEL_AD1981, + MODEL_AD1983, + MODEL_AD1986A, + MODEL_AD1988, +}; struct ad198x_spec { struct hda_gen_spec gen; + int model; /* for auto parser */ int smux_paths[4]; @@ -111,7 +120,7 @@ static void ad198x_power_eapd(struct hda_codec *codec) } } -static int ad198x_suspend(struct hda_codec *codec) +static int ad_codec_suspend(struct hda_codec *codec) { snd_hda_shutup_pins(codec); ad198x_power_eapd(codec); @@ -137,7 +146,7 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled) * Automatic parse of I/O pins from the BIOS configuration */ -static int ad198x_auto_build_controls(struct hda_codec *codec) +static int ad_codec_build_controls(struct hda_codec *codec) { int err; @@ -150,17 +159,6 @@ static int ad198x_auto_build_controls(struct hda_codec *codec) return 0; } -static const struct hda_codec_ops ad198x_auto_patch_ops = { - .build_controls = ad198x_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, - .suspend = ad198x_suspend, -}; - - static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) { struct ad198x_spec *spec = codec->spec; @@ -198,7 +196,6 @@ static int alloc_ad_spec(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; snd_hda_gen_spec_init(&spec->gen); - codec->patch_ops = ad198x_auto_patch_ops; return 0; } @@ -375,10 +372,10 @@ static const struct hda_model_fixup ad1986a_fixup_models[] = { /* */ -static int patch_ad1986a(struct hda_codec *codec) +static int ad1986a_probe(struct hda_codec *codec) { int err; - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; static const hda_nid_t preferred_pairs[] = { 0x1a, 0x03, 0x1b, 0x03, @@ -388,11 +385,6 @@ static int patch_ad1986a(struct hda_codec *codec) 0 }; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - /* AD1986A has the inverted EAPD implementation */ codec->inv_eapd = 1; @@ -418,10 +410,8 @@ static int patch_ad1986a(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = ad198x_parse_auto_config(codec, false); - if (err < 0) { - snd_hda_gen_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -507,18 +497,13 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) return 0; } -static int patch_ad1983(struct hda_codec *codec) +static int ad1983_probe(struct hda_codec *codec) { static const hda_nid_t conn_0c[] = { 0x08 }; static const hda_nid_t conn_0d[] = { 0x09 }; - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x0e; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); @@ -529,15 +514,11 @@ static int patch_ad1983(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, false); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -597,16 +578,11 @@ static const struct hda_quirk ad1981_fixup_tbl[] = { {} }; -static int patch_ad1981(struct hda_codec *codec) +static int ad1981_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return -ENOMEM; - spec = codec->spec; - spec->gen.mixer_nid = 0x0e; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); @@ -616,18 +592,14 @@ static int patch_ad1981(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, false); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -776,7 +748,7 @@ static const struct snd_kcontrol_new ad1988_auto_smux_mixer = { .put = ad1988_auto_smux_enum_put, }; -static int ad1988_auto_init(struct hda_codec *codec) +static int ad_codec_init(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; int i, err; @@ -784,6 +756,8 @@ static int ad1988_auto_init(struct hda_codec *codec) err = snd_hda_gen_init(codec); if (err < 0) return err; + if (spec->model != MODEL_AD1988) + return 0; if (!spec->gen.autocfg.dig_outs) return 0; @@ -854,8 +828,6 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) return -ENOMEM; - codec->patch_ops.init = ad1988_auto_init; - return 0; } @@ -889,16 +861,11 @@ static const struct hda_model_fixup ad1988_fixup_models[] = { {} }; -static int patch_ad1988(struct hda_codec *codec) +static int ad1988_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; @@ -909,18 +876,14 @@ static int patch_ad1988(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -1069,16 +1032,11 @@ static const struct hda_quirk ad1884_fixup_tbl[] = { }; -static int patch_ad1884(struct hda_codec *codec) +static int ad1884_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; @@ -1089,18 +1047,14 @@ static int patch_ad1884(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } /* @@ -1115,53 +1069,99 @@ static int patch_ad1884(struct hda_codec *codec) * port-G - rear clfe-out (6stack) */ -static int patch_ad1882(struct hda_codec *codec) +static int ad1882_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; return 0; - - error: - snd_hda_gen_free(codec); - return err; } - /* - * patch entries + * driver entries */ +static int ad_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + struct ad198x_spec *spec; + int err; + + err = alloc_ad_spec(codec); + if (err < 0) + return -ENOMEM; + spec = codec->spec; + spec->model = id->driver_data; + + switch (spec->model) { + case MODEL_AD1882: + err = ad1882_probe(codec); + break; + case MODEL_AD1884: + err = ad1884_probe(codec); + break; + case MODEL_AD1981: + err = ad1981_probe(codec); + break; + case MODEL_AD1983: + err = ad1983_probe(codec); + break; + case MODEL_AD1986A: + err = ad1986a_probe(codec); + break; + case MODEL_AD1988: + err = ad1988_probe(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_gen_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops ad_codec_ops = { + .probe = ad_codec_probe, + .remove = snd_hda_gen_remove, + .build_controls = ad_codec_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = ad_codec_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = ad_codec_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_analog[] = { - HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882), - HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981), - HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983), - HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a), - HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882), - HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988), + HDA_CODEC_ID_MODEL(0x11d4184a, "AD1884A", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41882, "AD1882", MODEL_AD1882), + HDA_CODEC_ID_MODEL(0x11d41883, "AD1883", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41884, "AD1884", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d4194a, "AD1984A", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d4194b, "AD1984B", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41981, "AD1981", MODEL_AD1981), + HDA_CODEC_ID_MODEL(0x11d41983, "AD1983", MODEL_AD1983), + HDA_CODEC_ID_MODEL(0x11d41984, "AD1984", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41986, "AD1986A", MODEL_AD1986A), + HDA_CODEC_ID_MODEL(0x11d41988, "AD1988", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4198b, "AD1988B", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4882a, "AD1882A", MODEL_AD1882), + HDA_CODEC_ID_MODEL(0x11d4989a, "AD1989A", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4989b, "AD1989B", MODEL_AD1988), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); @@ -1171,6 +1171,7 @@ MODULE_DESCRIPTION("Analog Devices HD-audio codec"); static struct hda_codec_driver analog_driver = { .id = snd_hda_id_analog, + .ops = &ad_codec_ops, }; module_hda_codec_driver(analog_driver); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/hda/codecs/ca0110.c similarity index 75% rename from sound/pci/hda/patch_ca0110.c rename to sound/hda/codecs/ca0110.c index 1818ce67f761..c75a9ff9460d 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/hda/codecs/ca0110.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Creative X-Fi CA0110-IBG chip + * HD audio codec driver for Creative X-Fi CA0110-IBG chip * * Copyright (c) 2008 Takashi Iwai */ @@ -13,16 +13,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" - - -static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; +#include "generic.h" static int ca0110_parse_auto_config(struct hda_codec *codec) { @@ -39,8 +30,7 @@ static int ca0110_parse_auto_config(struct hda_codec *codec) return 0; } - -static int patch_ca0110(struct hda_codec *codec) +static int ca0110_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct hda_gen_spec *spec; int err; @@ -50,7 +40,6 @@ static int patch_ca0110(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(spec); codec->spec = spec; - codec->patch_ops = ca0110_patch_ops; spec->multi_cap_vol = 1; codec->bus->core.needs_damn_long_delay = 1; @@ -62,18 +51,27 @@ static int patch_ca0110(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops ca0110_codec_ops = { + .probe = ca0110_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_ca0110[] = { - HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110), + HDA_CODEC_ID(0x1102000a, "CA0110-IBG"), + HDA_CODEC_ID(0x1102000b, "CA0110-IBG"), + HDA_CODEC_ID(0x1102000d, "SB0880 X-Fi"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110); @@ -83,6 +81,7 @@ MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); static struct hda_codec_driver ca0110_driver = { .id = snd_hda_id_ca0110, + .ops = &ca0110_codec_ops, }; module_hda_codec_driver(ca0110_driver); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/hda/codecs/ca0132.c similarity index 99% rename from sound/pci/hda/patch_ca0132.c rename to sound/hda/codecs/ca0132.c index d40197fb5fbd..b7d456e16c93 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Creative CA0132 chip + * HD audio codec driver for Creative CA0132 chip * * Copyright (c) 2011, Creative Technology Ltd. * - * Based on patch_ca0110.c + * Based on ca0110.c * Copyright (c) 2008 Takashi Iwai */ @@ -28,7 +28,7 @@ #include "ca0132_regs.h" /* Enable this to see controls for tuning purpose. */ -/*#define ENABLE_TUNING_CONTROLS*/ +#define ENABLE_TUNING_CONTROLS #ifdef ENABLE_TUNING_CONTROLS #include @@ -174,7 +174,7 @@ static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50}; #define DSP_SPEAKER_OUT_LATENCY 7 struct ct_effect { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *name; hda_nid_t nid; int mid; /*effect module ID*/ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/ @@ -305,7 +305,7 @@ enum { }; struct ct_tuning_ctl { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *name; hda_nid_t parent_nid; hda_nid_t nid; int mid; /*effect module ID*/ @@ -418,14 +418,14 @@ static const struct ct_tuning_ctl ca0132_tuning_ctls[] = { #define VOICEFX_MAX_PARAM_COUNT 9 struct ct_voicefx { - char *name; + const char *name; hda_nid_t nid; int mid; int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/ }; struct ct_voicefx_preset { - char *name; /*preset name*/ + const char *name; /*preset name*/ unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; }; @@ -514,14 +514,14 @@ static const struct ct_voicefx_preset ca0132_voicefx_presets[] = { #define EQ_PRESET_MAX_PARAM_COUNT 11 struct ct_eq { - char *name; + const char *name; hda_nid_t nid; int mid; int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/ }; struct ct_eq_preset { - char *name; /*preset name*/ + const char *name; /*preset name*/ unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT]; }; @@ -679,7 +679,7 @@ enum { }; struct ca0132_alt_speaker_channel_cfg { - char *name; + const char *name; unsigned int val; }; @@ -755,7 +755,7 @@ static const struct ae_ca0113_output_set ae7_ca0113_output_presets = { /* ae5 ca0113 command sequences to set headphone gain levels. */ #define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 struct ae5_headphone_gain_set { - char *name; + const char *name; unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; }; @@ -772,7 +772,7 @@ static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { }; struct ae5_filter_set { - char *name; + const char *name; unsigned int val; }; @@ -4385,8 +4385,8 @@ static int add_tuning_control(struct hda_codec *codec, knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; - knew.tlv.c = 0; - knew.tlv.p = 0; + knew.tlv.c = NULL; + knew.tlv.p = NULL; switch (pnid) { case VOICE_FOCUS: knew.info = voice_focus_ctl_info; @@ -4410,7 +4410,7 @@ static int add_tuning_control(struct hda_codec *codec, } knew.private_value = HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); - sprintf(namestr, "%s %s Volume", name, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "%s %s Volume", name, dirstr[dir]); return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } @@ -4802,7 +4802,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) if (err < 0) goto exit; - if (ca0132_alt_select_out_quirk_set(codec) < 0) + err = ca0132_alt_select_out_quirk_set(codec); + if (err < 0) goto exit; switch (spec->cur_out_type) { @@ -4892,6 +4893,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) spec->bass_redirection_val); else err = ca0132_alt_surround_set_bass_redirection(codec, 0); + if (err < 0) + goto exit; /* Unmute DSP now that we're done with output selection. */ err = dspio_set_uint_param(codec, 0x96, @@ -5787,7 +5790,7 @@ static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol, static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - char *sfx = "dB"; + const char *sfx = "dB"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -5796,7 +5799,7 @@ static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5839,7 +5842,7 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - char *sfx = " Ohms)"; + const char *sfx = " Ohms)"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -5850,7 +5853,7 @@ static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, sprintf(namestr, "%s %s", ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, sfx); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5903,7 +5906,7 @@ static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; sprintf(namestr, "%s", ae5_filter_presets[uinfo->value.enumerated.item].name); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5952,7 +5955,7 @@ static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS; if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, in_src_str[uinfo->value.enumerated.item]); return 0; } @@ -6004,7 +6007,7 @@ static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = NUM_OF_OUTPUTS; if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, out_type_str[uinfo->value.enumerated.item]); return 0; } @@ -6055,7 +6058,7 @@ static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, speaker_channel_cfgs[uinfo->value.enumerated.item].name); return 0; } @@ -6108,7 +6111,7 @@ static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS; if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS) uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, out_svm_set_enum_str[uinfo->value.enumerated.item]); return 0; } @@ -6172,7 +6175,7 @@ static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ca0132_alt_eq_presets[uinfo->value.enumerated.item].name); return 0; } @@ -6229,7 +6232,7 @@ static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ca0132_voicefx_presets[uinfo->value.enumerated.item].name); return 0; } @@ -9118,7 +9121,7 @@ static void sbz_dsp_startup_check(struct hda_codec *codec) codec_info(codec, "Reloading... Tries left: %d", reload); sbz_exit_chip(codec); spec->dsp_state = DSP_DOWNLOAD_INIT; - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); failure = 0; for (i = 0; i < 4; i++) { chipio_read(codec, cur_address, &dsp_data_check[i]); @@ -9694,30 +9697,6 @@ static void dbpro_free(struct hda_codec *codec) kfree(codec->spec); } -static int ca0132_suspend(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - return 0; -} - -static const struct hda_codec_ops ca0132_patch_ops = { - .build_controls = ca0132_build_controls, - .build_pcms = ca0132_build_pcms, - .init = ca0132_init, - .free = ca0132_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = ca0132_suspend, -}; - -static const struct hda_codec_ops dbpro_patch_ops = { - .build_controls = dbpro_build_controls, - .build_pcms = dbpro_build_pcms, - .init = dbpro_init, - .free = dbpro_free, -}; - static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -9982,12 +9961,23 @@ static void sbz_detect_quirk(struct hda_codec *codec) } } -static int patch_ca0132(struct hda_codec *codec) +static void ca0132_codec_remove(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_free(codec); + else + return ca0132_free(codec); +} + +static int ca0132_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct ca0132_spec *spec; int err; - codec_dbg(codec, "patch_ca0132\n"); + codec_dbg(codec, "%s\n", __func__); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -10000,11 +9990,6 @@ static int patch_ca0132(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_detect_quirk(codec); - if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) - codec->patch_ops = dbpro_patch_ops; - else - codec->patch_ops = ca0132_patch_ops; - codec->pcm_format_first = 1; codec->no_sticky_stream = 1; @@ -10100,15 +10085,63 @@ static int patch_ca0132(struct hda_codec *codec) return 0; error: - ca0132_free(codec); + ca0132_codec_remove(codec); return err; } +static int ca0132_codec_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_build_controls(codec); + else + return ca0132_build_controls(codec); +} + +static int ca0132_codec_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_build_pcms(codec); + else + return ca0132_build_pcms(codec); +} + +static int ca0132_codec_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_init(codec); + else + return ca0132_init(codec); +} + +static int ca0132_codec_suspend(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + return 0; +} + +static const struct hda_codec_ops ca0132_codec_ops = { + .probe = ca0132_codec_probe, + .remove = ca0132_codec_remove, + .build_controls = ca0132_codec_build_controls, + .build_pcms = ca0132_codec_build_pcms, + .init = ca0132_codec_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = ca0132_codec_suspend, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_ca0132[] = { - HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132), + HDA_CODEC_ID(0x11020011, "CA0132"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132); @@ -10118,6 +10151,7 @@ MODULE_DESCRIPTION("Creative Sound Core3D codec"); static struct hda_codec_driver ca0132_driver = { .id = snd_hda_id_ca0132, + .ops = &ca0132_codec_ops, }; module_hda_codec_driver(ca0132_driver); diff --git a/sound/pci/hda/ca0132_regs.h b/sound/hda/codecs/ca0132_regs.h similarity index 99% rename from sound/pci/hda/ca0132_regs.h rename to sound/hda/codecs/ca0132_regs.h index 0ead571fb447..dc0153df3d5c 100644 --- a/sound/pci/hda/ca0132_regs.h +++ b/sound/hda/codecs/ca0132_regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * HD audio interface patch for Creative CA0132 chip. + * HD audio codec driver for Creative CA0132 chip. * CA0132 registers defines. * * Copyright (c) 2011, Creative Technology Ltd. diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig new file mode 100644 index 000000000000..ec6cbcaf64f0 --- /dev/null +++ b/sound/hda/codecs/cirrus/Kconfig @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig SND_HDA_CODEC_CIRRUS + tristate "Cirrus Logic HD-audio codec support" + help + Say Y or M here to include Cirrus Logic HD-audio codec support. + + This will enable both CS420x and CS421x HD-audio codec drivers + as default, but you can enable/disable each codec driver + individually, too (only when CONFIG_EXPERT is set). + +if SND_HDA_CODEC_CIRRUS + +config SND_HDA_CODEC_CS420X + tristate "Build Cirrus Logic CS420x codec support" if EXPERT + select SND_HDA_GENERIC + default y + help + Say Y or M here to include Cirrus Logic CS420x codec support + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CS420X=m + +config SND_HDA_CODEC_CS421X + tristate "Build Cirrus Logic CS421x codec support" if EXPERT + select SND_HDA_GENERIC + default y + help + Say Y or M here to include Cirrus Logic CS421x codec support + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CS421X=m + +config SND_HDA_CODEC_CS8409 + tristate "Build Cirrus Logic HDA bridge support" + select SND_HDA_GENERIC + help + Say Y or M here to include Cirrus Logic HDA bridge support + such as CS8409. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m + +endif diff --git a/sound/hda/codecs/cirrus/Makefile b/sound/hda/codecs/cirrus/Makefile new file mode 100644 index 000000000000..dda1873ebcf5 --- /dev/null +++ b/sound/hda/codecs/cirrus/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-cs420x-y := cs420x.o +snd-hda-codec-cs421x-y := cs421x.o +snd-hda-codec-cs8409-y := cs8409.o cs8409-tables.o + +obj-$(CONFIG_SND_HDA_CODEC_CS420X) += snd-hda-codec-cs420x.o +obj-$(CONFIG_SND_HDA_CODEC_CS421X) += snd-hda-codec-cs421x.o +obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o diff --git a/sound/pci/hda/patch_cirrus.c b/sound/hda/codecs/cirrus/cs420x.c similarity index 61% rename from sound/pci/hda/patch_cirrus.c rename to sound/hda/codecs/cirrus/cs420x.c index 06e046214a41..823220d5cada 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Cirrus Logic CS420x chip + * Cirrus Logic CS420x HD-audio codec * * Copyright (c) 2009 Takashi Iwai */ @@ -15,10 +15,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" - -/* - */ +#include "../generic.h" struct cs_spec { struct hda_gen_spec gen; @@ -29,10 +26,6 @@ struct cs_spec { unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ - /* CS421x */ - unsigned int spdif_detect:1; - unsigned int spdif_present:1; - unsigned int sense_b:1; hda_nid_t vendor_nid; /* for MBP SPDIF control */ @@ -56,13 +49,6 @@ enum { CS420X_APPLE = CS420X_GPIO_13, }; -/* CS421x boards */ -enum { - CS421X_CDB4210, - CS421X_SENSE_B, - CS421X_STUMPY, -}; - /* Vendor-specific processing widget */ #define CS420X_VENDOR_NID 0x11 #define CS_DIG_OUT1_PIN_NID 0x10 @@ -105,28 +91,6 @@ enum { /* Cirrus Logic CS4208 */ #define CS4208_VENDOR_NID 0x24 -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ -#define CS4210_DAC_NID 0x02 -#define CS4210_ADC_NID 0x03 -#define CS4210_VENDOR_NID 0x0B -#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ -#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ - -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 - -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 - - static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) { struct cs_spec *spec = codec->spec; @@ -158,9 +122,6 @@ static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - /* mute HPs if spdif jack (SENSE_B) is present */ - spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); - snd_hda_gen_update_outputs(codec); if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { @@ -331,16 +292,6 @@ static int cs_build_controls(struct hda_codec *codec) return 0; } -#define cs_free snd_hda_gen_free - -static const struct hda_codec_ops cs_patch_ops = { - .build_controls = cs_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - static int cs_parse_auto_config(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; @@ -584,17 +535,10 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) return spec; } -static int patch_cs420x(struct hda_codec *codec) +static int cs420x_probe(struct hda_codec *codec) { - struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; codec->single_adc_amp = 1; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, @@ -603,15 +547,11 @@ static int patch_cs420x(struct hda_codec *codec) err = cs_parse_auto_config(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - cs_free(codec); - return err; } /* @@ -766,17 +706,11 @@ static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc) snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps); } -static int patch_cs4208(struct hda_codec *codec) +static int cs4208_probe(struct hda_codec *codec) { - struct cs_spec *spec; + struct cs_spec *spec = codec->spec; int err; - spec = cs_alloc_spec(codec, CS4208_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; /* exclude NID 0x10 (HP) from output volumes due to different steps */ spec->gen.out_vol_mask = 1ULL << 0x10; @@ -792,452 +726,60 @@ static int patch_cs4208(struct hda_codec *codec) err = cs_parse_auto_config(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - cs_free(codec); - return err; } -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ - -/* CS4210 board names */ -static const struct hda_model_fixup cs421x_models[] = { - { .id = CS421X_CDB4210, .name = "cdb4210" }, - { .id = CS421X_STUMPY, .name = "stumpy" }, - {} -}; - -static const struct hda_quirk cs421x_fixup_tbl[] = { - /* Test Intel board + CDB2410 */ - SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), - {} /* terminator */ -}; - -/* CS4210 board pinconfigs */ -/* Default CS4210 (CDB4210)*/ -static const struct hda_pintbl cdb4210_pincfgs[] = { - { 0x05, 0x0321401f }, - { 0x06, 0x90170010 }, - { 0x07, 0x03813031 }, - { 0x08, 0xb7a70037 }, - { 0x09, 0xb7a6003e }, - { 0x0a, 0x034510f0 }, - {} /* terminator */ -}; - -/* Stumpy ChromeBox */ -static const struct hda_pintbl stumpy_pincfgs[] = { - { 0x05, 0x022120f0 }, - { 0x06, 0x901700f0 }, - { 0x07, 0x02a120f0 }, - { 0x08, 0x77a70037 }, - { 0x09, 0x77a6003e }, - { 0x0a, 0x434510f0 }, - {} /* terminator */ -}; - -/* Setup GPIO/SENSE for each board (if used) */ -static void cs421x_fixup_sense_b(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct cs_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->sense_b = 1; -} - -static const struct hda_fixup cs421x_fixups[] = { - [CS421X_CDB4210] = { - .type = HDA_FIXUP_PINS, - .v.pins = cdb4210_pincfgs, - .chained = true, - .chain_id = CS421X_SENSE_B, - }, - [CS421X_SENSE_B] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs421x_fixup_sense_b, - }, - [CS421X_STUMPY] = { - .type = HDA_FIXUP_PINS, - .v.pins = stumpy_pincfgs, - }, -}; - -static const struct hda_verb cs421x_coef_init_verbs[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 1}, - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, - /* - * Disable Coefficient Index Auto-Increment(DAI)=1, - * PDREF=0 - */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, - /* ADC SZCMode = Digital Soft Ramp */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, - {0x0B, AC_VERB_SET_PROC_COEF, - (0x0002 /* DAC SZCMode = Digital Soft Ramp */ - | 0x0004 /* Mute DAC on FIFO error */ - | 0x0008 /* Enable DAC High Pass Filter */ - )}, - {} /* terminator */ -}; - -/* Errata: CS4210 rev A1 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ - * - * Description: - * 1. Performance degredation is present in the ADC. - * 2. Speaker output is not completely muted upon HP detect. - * 3. Noise is present when clipping occurs on the amplified - * speaker outputs. - * - * Workaround: - * The following verb sequence written to the registers during - * initialization will correct the issues listed above. - */ - -static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, - {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, - {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ - - {} /* terminator */ -}; - -/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ -static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); - -static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - return 0; -} - -static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; - return 0; -} - -static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - unsigned int vol = ucontrol->value.integer.value[0]; - unsigned int coef = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); - unsigned int original_coef = coef; - - coef &= ~0x0003; - coef |= (vol & 0x0003); - if (original_coef != coef) { - cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); - return 1; - } - - return 0; -} - -static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { - - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .name = "Speaker Boost Playback Volume", - .info = cs421x_boost_vol_info, - .get = cs421x_boost_vol_get, - .put = cs421x_boost_vol_put, - .tlv = { .p = cs421x_speaker_boost_db_scale }, -}; - -static void cs4210_pinmux_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int def_conf, coef; - - /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - - if (spec->gpio_mask) - coef |= 0x0008; /* B1,B2 are GPIOs */ - else - coef &= ~0x0008; - - if (spec->sense_b) - coef |= 0x0010; /* B2 is SENSE_B, not inverted */ - else - coef &= ~0x0010; - - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - - if ((spec->gpio_mask || spec->sense_b) && - is_active_pin(codec, CS421X_DMIC_PIN_NID)) { - - /* - * GPIO or SENSE_B forced - disconnect the DMIC pin. - */ - def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); - def_conf &= ~AC_DEFCFG_PORT_CONN; - def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); - snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); - } -} - -static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_callback *tbl) -{ - struct cs_spec *spec = codec->spec; - bool spdif_present = false; - hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; - - /* detect on spdif is specific to CS4210 */ - if (!spec->spdif_detect || - spec->vendor_nid != CS4210_VENDOR_NID) - return; - - spdif_present = snd_hda_jack_detect(codec, spdif_pin); - if (spdif_present == spec->spdif_present) - return; - - spec->spdif_present = spdif_present; - /* SPDIF TX on/off */ - snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0); - - cs_automute(codec); -} - -static void parse_cs421x_digital(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int i; - - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t nid = cfg->dig_out_pins[i]; - - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - spec->spdif_detect = 1; - snd_hda_jack_detect_enable_callback(codec, nid, - cs4210_spdif_automute); - } - } -} - -static int cs421x_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - snd_hda_sequence_write(codec, cs421x_coef_init_verbs); - snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); - cs4210_pinmux_init(codec); - } - - snd_hda_gen_init(codec); - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - init_input_coef(codec); - - cs4210_spdif_automute(codec, NULL); - - return 0; -} - -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) -{ - unsigned int caps; - - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); -} - -static int cs421x_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - hda_nid_t dac = CS4210_DAC_NID; - int err; - - fix_volume_caps(codec, dac); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - parse_cs421x_digital(codec); - - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &cs421x_speaker_boost_ctl)) - return -ENOMEM; - } - - return 0; -} - -/* - * Manage PDREF, when transitioning to D3hot - * (DAC,ADC) -> D3, PDREF=1, AFG->D3 - */ -static int cs421x_suspend(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int coef; - - snd_hda_shutup_pins(codec); - - snd_hda_codec_write(codec, CS4210_DAC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, CS4210_ADC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - coef |= 0x0004; /* PDREF */ - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - } - - return 0; -} - -static const struct hda_codec_ops cs421x_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs421x_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cs421x_suspend, -}; - -static int patch_cs4210(struct hda_codec *codec) +static int cs_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); + spec = cs_alloc_spec(codec, id->driver_data); if (!spec) return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; spec->gen.automute_hook = cs_automute; - snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, - cs421x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* - * Update the GPIO/DMIC/SENSE_B pinmux before the configuration - * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input - * is disabled. - */ - cs4210_pinmux_init(codec); - - err = cs421x_parse_auto_config(codec); + if (spec->vendor_nid == CS4208_VENDOR_NID) + err = cs4208_probe(codec); + else + err = cs420x_probe(codec); if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); + snd_hda_gen_remove(codec); return err; } -static int patch_cs4213(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - cs_free(codec); - return err; -} +static const struct hda_codec_ops cs_codec_ops = { + .probe = cs_codec_probe, + .remove = snd_hda_gen_remove, + .build_controls = cs_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs_init, + .unsol_event = snd_hda_jack_unsol_event, + .stream_pm = snd_hda_gen_stream_pm, +}; /* - * patch entries + * driver entries */ -static const struct hda_device_id snd_hda_id_cirrus[] = { - HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x), - HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x), - HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208), - HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210), - HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213), +static const struct hda_device_id snd_hda_id_cs420x[] = { + HDA_CODEC_ID_MODEL(0x10134206, "CS4206", CS420X_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134207, "CS4207", CS420X_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134208, "CS4208", CS4208_VENDOR_NID), {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs420x); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); +MODULE_DESCRIPTION("Cirrus Logic CS420x HD-audio codec"); -static struct hda_codec_driver cirrus_driver = { - .id = snd_hda_id_cirrus, +static struct hda_codec_driver cs420x_driver = { + .id = snd_hda_id_cs420x, + .ops = &cs_codec_ops, }; -module_hda_codec_driver(cirrus_driver); +module_hda_codec_driver(cs420x_driver); diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c new file mode 100644 index 000000000000..a93e2e0bb391 --- /dev/null +++ b/sound/hda/codecs/cirrus/cs421x.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cirrus Logic CS421x HD-audio codec + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "../generic.h" + +struct cs_spec { + struct hda_gen_spec gen; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ + unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +}; + +/* CS421x boards */ +enum { + CS421X_CDB4210, + CS421X_SENSE_B, + CS421X_STUMPY, +}; + +/* Vendor-specific processing widget */ +#define CS_DIG_OUT1_PIN_NID 0x10 +#define CS_DIG_OUT2_PIN_NID 0x15 +#define CS_DMIC1_PIN_NID 0x0e +#define CS_DMIC2_PIN_NID 0x12 + +/* coef indices */ +#define IDX_SPDIF_STAT 0x0000 +#define IDX_SPDIF_CTL 0x0001 +#define IDX_ADC_CFG 0x0002 +/* SZC bitmask, 4 modes below: + * 0 = immediate, + * 1 = digital immediate, analog zero-cross + * 2 = digtail & analog soft-ramp + * 3 = digital soft-ramp, analog zero-cross + */ +#define CS_COEF_ADC_SZC_MASK (3 << 0) +#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ +#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ +/* PGA mode: 0 = differential, 1 = signle-ended */ +#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ +#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ +#define IDX_DAC_CFG 0x0003 +/* SZC bitmask, 4 modes below: + * 0 = Immediate + * 1 = zero-cross + * 2 = soft-ramp + * 3 = soft-ramp on zero-cross + */ +#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ +#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ +#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ + +#define IDX_BEEP_CFG 0x0004 +/* 0x0008 - test reg key */ +/* 0x0009 - 0x0014 -> 12 test regs */ +/* 0x0015 - visibility reg */ + +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Transmitter(sense) + */ +#define CS4210_DAC_NID 0x02 +#define CS4210_ADC_NID 0x03 +#define CS4210_VENDOR_NID 0x0B +#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ +#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ + +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 + +/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ +#define CS4213_VENDOR_NID 0x09 + + +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, spec->vendor_nid, 0, + AC_VERB_GET_PROC_COEF, 0); +} + +static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_PROC_COEF, coef); +} + +/* + * auto-mute and auto-mic switching + * CS421x auto-output redirecting + * HP/SPK/SPDIF + */ + +static void cs_automute(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + /* mute HPs if spdif jack (SENSE_B) is present */ + spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); + + snd_hda_gen_update_outputs(codec); + + if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { + if (spec->gen.automute_speaker) + spec->gpio_data = spec->gen.hp_jack_present ? + spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + else + spec->gpio_data = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, spec->gpio_data); + } +} + +static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); +} + +static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) +{ + struct cs_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + codec->spec = spec; + spec->vendor_nid = vendor_nid; + codec->power_save_node = 1; + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Transmitter(sense) + */ + +/* CS4210 board names */ +static const struct hda_model_fixup cs421x_models[] = { + { .id = CS421X_CDB4210, .name = "cdb4210" }, + { .id = CS421X_STUMPY, .name = "stumpy" }, + {} +}; + +static const struct hda_quirk cs421x_fixup_tbl[] = { + /* Test Intel board + CDB2410 */ + SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), + {} /* terminator */ +}; + +/* CS4210 board pinconfigs */ +/* Default CS4210 (CDB4210)*/ +static const struct hda_pintbl cdb4210_pincfgs[] = { + { 0x05, 0x0321401f }, + { 0x06, 0x90170010 }, + { 0x07, 0x03813031 }, + { 0x08, 0xb7a70037 }, + { 0x09, 0xb7a6003e }, + { 0x0a, 0x034510f0 }, + {} /* terminator */ +}; + +/* Stumpy ChromeBox */ +static const struct hda_pintbl stumpy_pincfgs[] = { + { 0x05, 0x022120f0 }, + { 0x06, 0x901700f0 }, + { 0x07, 0x02a120f0 }, + { 0x08, 0x77a70037 }, + { 0x09, 0x77a6003e }, + { 0x0a, 0x434510f0 }, + {} /* terminator */ +}; + +/* Setup GPIO/SENSE for each board (if used) */ +static void cs421x_fixup_sense_b(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct cs_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->sense_b = 1; +} + +static const struct hda_fixup cs421x_fixups[] = { + [CS421X_CDB4210] = { + .type = HDA_FIXUP_PINS, + .v.pins = cdb4210_pincfgs, + .chained = true, + .chain_id = CS421X_SENSE_B, + }, + [CS421X_SENSE_B] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs421x_fixup_sense_b, + }, + [CS421X_STUMPY] = { + .type = HDA_FIXUP_PINS, + .v.pins = stumpy_pincfgs, + }, +}; + +static const struct hda_verb cs421x_coef_init_verbs[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 1}, + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, + /* + * Disable Coefficient Index Auto-Increment(DAI)=1, + * PDREF=0 + */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, + /* ADC SZCMode = Digital Soft Ramp */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, + {0x0B, AC_VERB_SET_PROC_COEF, + (0x0002 /* DAC SZCMode = Digital Soft Ramp */ + | 0x0004 /* Mute DAC on FIFO error */ + | 0x0008 /* Enable DAC High Pass Filter */ + )}, + {} /* terminator */ +}; + +/* Errata: CS4210 rev A1 Silicon + * + * http://www.cirrus.com/en/pubs/errata/ + * + * Description: + * 1. Performance degredation is present in the ADC. + * 2. Speaker output is not completely muted upon HP detect. + * 3. Noise is present when clipping occurs on the amplified + * speaker outputs. + * + * Workaround: + * The following verb sequence written to the registers during + * initialization will correct the issues listed above. + */ + +static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, + {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, + {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ + + {} /* terminator */ +}; + +/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ +static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); + +static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; + return 0; +} + +static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + unsigned int vol = ucontrol->value.integer.value[0]; + unsigned int coef = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); + unsigned int original_coef = coef; + + coef &= ~0x0003; + coef |= (vol & 0x0003); + if (original_coef != coef) { + cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Speaker Boost Playback Volume", + .info = cs421x_boost_vol_info, + .get = cs421x_boost_vol_get, + .put = cs421x_boost_vol_put, + .tlv = { .p = cs421x_speaker_boost_db_scale }, +}; + +static void cs4210_pinmux_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int def_conf, coef; + + /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + + if (spec->gpio_mask) + coef |= 0x0008; /* B1,B2 are GPIOs */ + else + coef &= ~0x0008; + + if (spec->sense_b) + coef |= 0x0010; /* B2 is SENSE_B, not inverted */ + else + coef &= ~0x0010; + + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + if ((spec->gpio_mask || spec->sense_b) && + is_active_pin(codec, CS421X_DMIC_PIN_NID)) { + + /* + * GPIO or SENSE_B forced - disconnect the DMIC pin. + */ + def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); + def_conf &= ~AC_DEFCFG_PORT_CONN; + def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); + snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); + } +} + +static void cs4210_spdif_automute(struct hda_codec *codec, + struct hda_jack_callback *tbl) +{ + struct cs_spec *spec = codec->spec; + bool spdif_present = false; + hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; + + /* detect on spdif is specific to CS4210 */ + if (!spec->spdif_detect || + spec->vendor_nid != CS4210_VENDOR_NID) + return; + + spdif_present = snd_hda_jack_detect(codec, spdif_pin); + if (spdif_present == spec->spdif_present) + return; + + spec->spdif_present = spdif_present; + /* SPDIF TX on/off */ + snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0); + + cs_automute(codec); +} + +static void parse_cs421x_digital(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int i; + + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t nid = cfg->dig_out_pins[i]; + + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + spec->spdif_detect = 1; + snd_hda_jack_detect_enable_callback(codec, nid, + cs4210_spdif_automute); + } + } +} + +static int cs421x_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + snd_hda_sequence_write(codec, cs421x_coef_init_verbs); + snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); + cs4210_pinmux_init(codec); + } + + snd_hda_gen_init(codec); + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + cs4210_spdif_automute(codec, NULL); + + return 0; +} + +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +{ + unsigned int caps; + + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); +} + +static int cs421x_parse_auto_config(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + hda_nid_t dac = CS4210_DAC_NID; + int err; + + fix_volume_caps(codec, dac); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + parse_cs421x_digital(codec); + + if (spec->gen.autocfg.speaker_outs && + spec->vendor_nid == CS4210_VENDOR_NID) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &cs421x_speaker_boost_ctl)) + return -ENOMEM; + } + + return 0; +} + +/* + * Manage PDREF, when transitioning to D3hot + * (DAC,ADC) -> D3, PDREF=1, AFG->D3 + */ +static int cs421x_suspend(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int coef; + + snd_hda_shutup_pins(codec); + + snd_hda_codec_write(codec, CS4210_DAC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, CS4210_ADC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + coef |= 0x0004; /* PDREF */ + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + } + + return 0; +} + +static int cs421x_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct cs_spec *spec; + int err; + + spec = cs_alloc_spec(codec, id->driver_data); + if (!spec) + return -ENOMEM; + + spec->gen.automute_hook = cs_automute; + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, + cs421x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* + * Update the GPIO/DMIC/SENSE_B pinmux before the configuration + * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input + * is disabled. + */ + cs4210_pinmux_init(codec); + } + + err = cs421x_parse_auto_config(codec); + if (err < 0) + goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops cs421x_codec_ops = { + .probe = cs421x_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs421x_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cs421x_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_cs421x[] = { + HDA_CODEC_ID_MODEL(0x10134210, "CS4210", CS4210_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134213, "CS4213", CS4213_VENDOR_NID), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs421x); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cirrus Logic CS421x HD-audio codec"); + +static struct hda_codec_driver cs421x_driver = { + .id = snd_hda_id_cs421x, + .ops = &cs421x_codec_ops, +}; + +module_hda_codec_driver(cs421x_driver); diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c similarity index 99% rename from sound/pci/hda/patch_cs8409-tables.c rename to sound/hda/codecs/cirrus/cs8409-tables.c index 09240138e087..8c703b714a71 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/hda/codecs/cirrus/cs8409-tables.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * cs8409-tables.c -- HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -8,7 +8,7 @@ * Author: Lucas Tanure */ -#include "patch_cs8409.h" +#include "cs8409.h" /****************************************************************************** * CS42L42 Specific Data diff --git a/sound/pci/hda/patch_cs8409.c b/sound/hda/codecs/cirrus/cs8409.c similarity index 98% rename from sound/pci/hda/patch_cs8409.c rename to sound/hda/codecs/cirrus/cs8409.c index e50006757a2c..e32b462cdc5e 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -13,7 +13,7 @@ #include #include -#include "patch_cs8409.h" +#include "cs8409.h" /****************************************************************************** * CS8409 Specific Functions @@ -954,7 +954,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); } -static void cs8409_free(struct hda_codec *codec) +static void cs8409_remove(struct hda_codec *codec) { struct cs8409_spec *spec = codec->spec; @@ -962,7 +962,7 @@ static void cs8409_free(struct hda_codec *codec) cancel_delayed_work_sync(&spec->i2c_clk_work); cs8409_disable_i2c_clock(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } /****************************************************************************** @@ -1007,6 +1007,16 @@ static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned in } } +static void cs8409_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_spec *spec = codec->spec; + + if (spec->unsol_event) + spec->unsol_event(codec, res); + else + cs8409_cs42l42_jack_unsol_event(codec, res); +} + /* Manage PDREF, when transition to D3hot */ static int cs8409_cs42l42_suspend(struct hda_codec *codec) { @@ -1076,15 +1086,6 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec) cs8409_enable_ur(codec, 1); } -static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = cs8409_cs42l42_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res) { @@ -1134,7 +1135,6 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; spec->num_scodecs = 1; spec->scodecs[CS8409_CODEC0]->codec = codec; - codec->patch_ops = cs8409_cs42l42_patch_ops; spec->gen.suppress_auto_mute = 1; spec->gen.no_primary_hp = 1; @@ -1304,15 +1304,6 @@ static void dolphin_hw_init(struct hda_codec *codec) cs8409_enable_ur(codec, 1); } -static const struct hda_codec_ops cs8409_dolphin_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = dolphin_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res) { @@ -1371,7 +1362,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac spec->num_scodecs = 2; spec->gen.suppress_vmaster = 1; - codec->patch_ops = cs8409_dolphin_patch_ops; + spec->unsol_event = dolphin_jack_unsol_event; /* GPIO 1,5 out, 0,4 in */ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | @@ -1444,7 +1435,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac } } -static int patch_cs8409(struct hda_codec *codec) +static int cs8409_probe(struct hda_codec *codec, const struct hda_device_id *id) { int err; @@ -1461,7 +1452,7 @@ static int patch_cs8409(struct hda_codec *codec) err = cs8409_parse_auto_config(codec); if (err < 0) { - cs8409_free(codec); + cs8409_remove(codec); return err; } @@ -1469,14 +1460,26 @@ static int patch_cs8409(struct hda_codec *codec) return 0; } +static const struct hda_codec_ops cs8409_codec_ops = { + .probe = cs8409_probe, + .remove = cs8409_remove, + .build_controls = cs8409_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs8409_init, + .unsol_event = cs8409_unsol_event, + .suspend = cs8409_cs42l42_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_cs8409[] = { - HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), + HDA_CODEC_ID(0x10138409, "CS8409"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); static struct hda_codec_driver cs8409_driver = { .id = snd_hda_id_cs8409, + .ops = &cs8409_codec_ops, }; module_hda_codec_driver(cs8409_driver); diff --git a/sound/pci/hda/patch_cs8409.h b/sound/hda/codecs/cirrus/cs8409.h similarity index 98% rename from sound/pci/hda/patch_cs8409.h rename to sound/hda/codecs/cirrus/cs8409.h index e4bd2e12110b..7fe56f4a73bc 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/hda/codecs/cirrus/cs8409.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -17,7 +17,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" /* CS8409 Specific Definitions */ @@ -345,6 +345,8 @@ struct cs8409_spec { /* verb exec op override */ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res); + /* unsol_event op override */ + void (*unsol_event)(struct hda_codec *codec, unsigned int res); }; extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/hda/codecs/cm9825.c similarity index 73% rename from sound/pci/hda/patch_cmedia.c rename to sound/hda/codecs/cm9825.c index fe946d407830..5c474ce44348 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/hda/codecs/cm9825.c @@ -1,10 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for C-Media CMI9880 - * - * Copyright (c) 2004 Takashi Iwai + * CM9825 HD-audio codec */ #include @@ -15,7 +11,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" /* CM9825 Offset Definitions */ @@ -183,12 +179,12 @@ static int cm9825_init(struct hda_codec *codec) return 0; } -static void cm9825_free(struct hda_codec *codec) +static void cm9825_remove(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; cancel_delayed_work_sync(&spec->unsol_hp_work); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } static int cm9825_suspend(struct hda_codec *codec) @@ -217,7 +213,7 @@ static int cm9825_resume(struct hda_codec *codec) msleep(150); /* for depop noise */ - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); hp_pin = spec->gen.autocfg.hp_pins[0]; hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); @@ -242,18 +238,7 @@ static int cm9825_resume(struct hda_codec *codec) return 0; } -/* - * stuff for auto-parser - */ -static const struct hda_codec_ops cmi_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int patch_cm9825(struct hda_codec *codec) +static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct cmi_spec *spec; struct auto_pin_cfg *cfg; @@ -266,12 +251,6 @@ static int patch_cm9825(struct hda_codec *codec) INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); codec->spec = spec; spec->codec = codec; - codec->patch_ops = cmi_auto_patch_ops; - codec->patch_ops.init = cm9825_init; - codec->patch_ops.suspend = cm9825_suspend; - codec->patch_ops.resume = cm9825_resume; - codec->patch_ops.free = cm9825_free; - codec->patch_ops.check_power_status = snd_hda_gen_check_power_status; cfg = &spec->gen.autocfg; snd_hda_gen_spec_init(&spec->gen); spec->chip_d0_verbs = cm9825_std_d0_verbs; @@ -293,104 +272,41 @@ static int patch_cm9825(struct hda_codec *codec) return 0; error: - cm9825_free(codec); + cm9825_remove(codec); codec_info(codec, "Enter err %d\n", err); return err; } -static int patch_cmi9880(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - -static int patch_cmi8888(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - - /* mask NID 0x10 from the playback volume selection; - * it's a headphone boost volume handled manually below - */ - spec->gen.out_vol_mask = (1ULL << 0x10); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == - AC_JACK_HP_OUT) { - static const struct snd_kcontrol_new amp_kctl = - HDA_CODEC_VOLUME("Headphone Amp Playback Volume", - 0x10, 0, HDA_OUTPUT); - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &_kctl)) { - err = -ENOMEM; - goto error; - } - } - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} +static const struct hda_codec_ops cm9825_codec_ops = { + .probe = cm9825_probe, + .remove = cm9825_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cm9825_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cm9825_suspend, + .resume = cm9825_resume, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; /* - * patch entries + * driver entries */ -static const struct hda_device_id snd_hda_id_cmedia[] = { - HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888), - HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x13f69825, "CM9825", patch_cm9825), +static const struct hda_device_id snd_hda_id_cm9825[] = { + HDA_CODEC_ID(0x13f69825, "CM9825"), {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cm9825); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("C-Media HD-audio codec"); +MODULE_DESCRIPTION("CM9825 HD-audio codec"); -static struct hda_codec_driver cmedia_driver = { - .id = snd_hda_id_cmedia, +static struct hda_codec_driver cm9825_driver = { + .id = snd_hda_id_cm9825, + .ops = &cm9825_codec_ops, }; -module_hda_codec_driver(cmedia_driver); +module_hda_codec_driver(cm9825_driver); diff --git a/sound/hda/codecs/cmedia.c b/sound/hda/codecs/cmedia.c new file mode 100644 index 000000000000..15e5a1118a6e --- /dev/null +++ b/sound/hda/codecs/cmedia.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Universal codec driver for Intel High Definition Audio Codec + * + * HD audio codec driver for C-Media CMI9880 + * + * Copyright (c) 2004 Takashi Iwai + */ + +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "generic.h" + +static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct hda_gen_spec *spec; + struct auto_pin_cfg *cfg; + bool is_cmi8888 = id->vendor_id == 0x13f68888; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + cfg = &spec->autocfg; + snd_hda_gen_spec_init(spec); + + if (is_cmi8888) { + /* mask NID 0x10 from the playback volume selection; + * it's a headphone boost volume handled manually below + */ + spec->out_vol_mask = (1ULL << 0x10); + } + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + goto error; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + goto error; + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + goto error; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + goto error; + + if (is_cmi8888) { + if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == + AC_JACK_HP_OUT) { + static const struct snd_kcontrol_new amp_kctl = + HDA_CODEC_VOLUME("Headphone Amp Playback Volume", + 0x10, 0, HDA_OUTPUT); + if (!snd_hda_gen_add_kctl(spec, NULL, &_kctl)) { + err = -ENOMEM; + goto error; + } + } + } + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops cmedia_codec_ops = { + .probe = cmedia_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_cmedia[] = { + HDA_CODEC_ID(0x13f68888, "CMI8888"), + HDA_CODEC_ID(0x13f69880, "CMI9880"), + HDA_CODEC_ID(0x434d4980, "CMI9880"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("C-Media HD-audio codec"); + +static struct hda_codec_driver cmedia_driver = { + .id = snd_hda_id_cmedia, + .ops = &cmedia_codec_ops, +}; + +module_hda_codec_driver(cmedia_driver); diff --git a/sound/pci/hda/patch_conexant.c b/sound/hda/codecs/conexant.c similarity index 92% rename from sound/pci/hda/patch_conexant.c rename to sound/hda/codecs/conexant.c index 34874039ad45..c881bf213ebe 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Conexant HDA audio codec + * HD audio codec driver for Conexant HDA audio codec * * Copyright (c) 2006 Pototskiy Akex * Takashi Iwai @@ -19,7 +19,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" struct conexant_spec { struct hda_gen_spec gen; @@ -42,7 +42,7 @@ struct conexant_spec { unsigned int gpio_led; unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; - bool is_cx8070_sn6140; + bool is_cx11880_sn6140; }; @@ -185,7 +185,7 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); } -static int cx_auto_init(struct hda_codec *codec) +static int cx_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; snd_hda_gen_init(codec); @@ -195,7 +195,7 @@ static int cx_auto_init(struct hda_codec *codec) cxt_init_gpio_led(codec); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - if (spec->is_cx8070_sn6140) + if (spec->is_cx11880_sn6140) cx_fixup_headset_recog(codec); return 0; @@ -210,10 +210,10 @@ static void cx_auto_shutdown(struct hda_codec *codec) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); } -static void cx_auto_free(struct hda_codec *codec) +static void cx_remove(struct hda_codec *codec) { cx_auto_shutdown(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } static void cx_process_headset_plugin(struct hda_codec *codec) @@ -247,7 +247,7 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ { unsigned int mic_present; - /* In cx8070 and sn6140, the node 16 can only be configured to headphone or disabled, + /* In cx11880 and sn6140, the node 16 can only be configured to headphone or disabled, * the node 19 can only be configured to microphone or disabled. * Check hp&mic tag to process headset plugin & plugout. */ @@ -258,22 +258,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ cx_process_headset_plugin(codec); } -static int cx_auto_suspend(struct hda_codec *codec) +static int cx_suspend(struct hda_codec *codec) { cx_auto_shutdown(codec); return 0; } -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cx_auto_init, - .free = cx_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cx_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - /* * pin fix-up */ @@ -312,10 +302,10 @@ enum { }; /* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" +#include "helpers/thinkpad.c" /* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" +#include "helpers/ideapad_hotkey_led.c" static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -1178,7 +1168,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec) spec->gen.dac_min_mute = true; } -static int patch_conexant_auto(struct hda_codec *codec) +static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct conexant_spec *spec; int err; @@ -1190,13 +1180,12 @@ static int patch_conexant_auto(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - codec->patch_ops = cx_auto_patch_ops; - /* init cx8070/sn6140 flag and reset headset_present_flag */ + /* init cx11880/sn6140 flag and reset headset_present_flag */ switch (codec->core.vendor_id) { case 0x14f11f86: case 0x14f11f87: - spec->is_cx8070_sn6140 = true; + spec->is_cx11880_sn6140 = true; snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref); break; } @@ -1276,47 +1265,59 @@ static int patch_conexant_auto(struct hda_codec *codec) return 0; error: - cx_auto_free(codec); + cx_remove(codec); return err; } +static const struct hda_codec_ops cx_codec_ops = { + .probe = cx_probe, + .remove = cx_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cx_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cx_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* */ static const struct hda_device_id snd_hda_id_conexant[] = { - HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto), + HDA_CODEC_ID(0x14f11f86, "CX11880"), + HDA_CODEC_ID(0x14f11f87, "SN6140"), + HDA_CODEC_ID(0x14f12008, "CX8200"), + HDA_CODEC_ID(0x14f120d0, "CX11970"), + HDA_CODEC_ID(0x14f120d1, "SN6180"), + HDA_CODEC_ID(0x14f15045, "CX20549 (Venice)"), + HDA_CODEC_ID(0x14f15047, "CX20551 (Waikiki)"), + HDA_CODEC_ID(0x14f15051, "CX20561 (Hermosa)"), + HDA_CODEC_ID(0x14f15066, "CX20582 (Pebble)"), + HDA_CODEC_ID(0x14f15067, "CX20583 (Pebble HSF)"), + HDA_CODEC_ID(0x14f15068, "CX20584"), + HDA_CODEC_ID(0x14f15069, "CX20585"), + HDA_CODEC_ID(0x14f1506c, "CX20588"), + HDA_CODEC_ID(0x14f1506e, "CX20590"), + HDA_CODEC_ID(0x14f15097, "CX20631"), + HDA_CODEC_ID(0x14f15098, "CX20632"), + HDA_CODEC_ID(0x14f150a1, "CX20641"), + HDA_CODEC_ID(0x14f150a2, "CX20642"), + HDA_CODEC_ID(0x14f150ab, "CX20651"), + HDA_CODEC_ID(0x14f150ac, "CX20652"), + HDA_CODEC_ID(0x14f150b8, "CX20664"), + HDA_CODEC_ID(0x14f150b9, "CX20665"), + HDA_CODEC_ID(0x14f150f1, "CX21722"), + HDA_CODEC_ID(0x14f150f2, "CX20722"), + HDA_CODEC_ID(0x14f150f3, "CX21724"), + HDA_CODEC_ID(0x14f150f4, "CX20724"), + HDA_CODEC_ID(0x14f1510f, "CX20751/2"), + HDA_CODEC_ID(0x14f15110, "CX20751/2"), + HDA_CODEC_ID(0x14f15111, "CX20753/4"), + HDA_CODEC_ID(0x14f15113, "CX20755"), + HDA_CODEC_ID(0x14f15114, "CX20756"), + HDA_CODEC_ID(0x14f15115, "CX20757"), + HDA_CODEC_ID(0x14f151d7, "CX20952"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant); @@ -1326,6 +1327,7 @@ MODULE_DESCRIPTION("Conexant HD-audio codec"); static struct hda_codec_driver conexant_driver = { .id = snd_hda_id_conexant, + .ops = &cx_codec_ops, }; module_hda_codec_driver(conexant_driver); diff --git a/sound/pci/hda/hda_generic.c b/sound/hda/codecs/generic.c similarity index 99% rename from sound/pci/hda/hda_generic.c rename to sound/hda/codecs/generic.c index b34d84fedcc8..a44beefe3e97 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/hda/codecs/generic.c @@ -25,7 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_beep.h" -#include "hda_generic.h" +#include "generic.h" /** @@ -2828,7 +2828,7 @@ static int in_jack_mode_info(struct snd_kcontrol *kcontrol, snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), vref_texts); /* set the right text */ - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); return 0; } @@ -2941,7 +2941,7 @@ static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, text = "Mic In"; } - strcpy(uinfo->value.enumerated.name, text); + strscpy(uinfo->value.enumerated.name, text); return 0; } @@ -4946,7 +4946,7 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) * @nid: audio widget * @on: power on/off flag * - * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. + * Set this in hda_codec_ops.stream_pm. Only valid with power_save_node flag. */ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) { @@ -5190,8 +5190,6 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (spec->power_down_unused || codec->power_save_node) { if (!codec->power_filter) codec->power_filter = snd_hda_gen_path_power_filter; - if (!codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; } if (!spec->no_analog && spec->beep_nid) { @@ -5230,7 +5228,7 @@ static const char * const follower_pfxs[] = { * snd_hda_gen_build_controls - Build controls from the parsed results * @codec: the HDA codec * - * Pass this to build_controls patch_ops. + * Pass this to build_controls hda_codec_ops. */ int snd_hda_gen_build_controls(struct hda_codec *codec) { @@ -5743,7 +5741,7 @@ static void setup_pcm_stream(struct hda_pcm_stream *str, * snd_hda_gen_build_pcms - build PCM streams based on the parsed results * @codec: the HDA codec * - * Pass this to build_pcms patch_ops. + * Pass this to build_pcms hda_codec_ops. */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { @@ -6032,7 +6030,7 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) * snd_hda_gen_init - initialize the generic spec * @codec: the HDA codec * - * This can be put as patch_ops init function. + * This can be put as hda_codec_ops init function. */ int snd_hda_gen_init(struct hda_codec *codec) { @@ -6070,26 +6068,26 @@ int snd_hda_gen_init(struct hda_codec *codec) EXPORT_SYMBOL_GPL(snd_hda_gen_init); /** - * snd_hda_gen_free - free the generic spec + * snd_hda_gen_remove - free the generic spec * @codec: the HDA codec * - * This can be put as patch_ops free function. + * This can be put as hda_codec_ops remove function. */ -void snd_hda_gen_free(struct hda_codec *codec) +void snd_hda_gen_remove(struct hda_codec *codec) { snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); snd_hda_gen_spec_free(codec->spec); kfree(codec->spec); codec->spec = NULL; } -EXPORT_SYMBOL_GPL(snd_hda_gen_free); +EXPORT_SYMBOL_GPL(snd_hda_gen_remove); /** * snd_hda_gen_check_power_status - check the loopback power save state * @codec: the HDA codec * @nid: NID to inspect * - * This can be put as patch_ops check_power_status function. + * This can be put as hda_codec_ops check_power_status function. */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -6103,20 +6101,8 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); * the generic codec support */ -static const struct hda_codec_ops generic_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, -}; - -/* - * snd_hda_parse_generic_codec - Generic codec parser - * @codec: the HDA codec - */ -static int snd_hda_parse_generic_codec(struct hda_codec *codec) +static int snd_hda_gen_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct hda_gen_spec *spec; int err; @@ -6135,22 +6121,34 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = generic_patch_ops; return 0; error: - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops generic_codec_ops = { + .probe = snd_hda_gen_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_generic[] = { - HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), + HDA_CODEC_ID(0x1af40021, "Generic"), /* QEMU */ + HDA_CODEC_ID(HDA_CODEC_ID_GENERIC, "Generic"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); static struct hda_codec_driver generic_driver = { .id = snd_hda_id_generic, + .ops = &generic_codec_ops, }; module_hda_codec_driver(generic_driver); diff --git a/sound/pci/hda/hda_generic.h b/sound/hda/codecs/generic.h similarity index 99% rename from sound/pci/hda/hda_generic.h rename to sound/hda/codecs/generic.h index 9612afaa61c2..524591821f8c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/hda/codecs/generic.h @@ -311,7 +311,7 @@ enum { int snd_hda_gen_spec_init(struct hda_gen_spec *spec); int snd_hda_gen_init(struct hda_codec *codec); -void snd_hda_gen_free(struct hda_codec *codec); +void snd_hda_gen_remove(struct hda_codec *codec); int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); diff --git a/sound/hda/codecs/hdmi/Kconfig b/sound/hda/codecs/hdmi/Kconfig new file mode 100644 index 000000000000..6ea3553ba9f8 --- /dev/null +++ b/sound/hda/codecs/hdmi/Kconfig @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig SND_HDA_CODEC_HDMI + tristate "HD-audio HDMI codec support" + help + Say Y or M here to include HD-audio HDMI/DislayPort codec support. + + This will enable all HDMI/DP codec drivers as default, but you can + enable/disable each codec driver individually, too (only when + CONFIG_EXPERT is set). + +if SND_HDA_CODEC_HDMI + +config SND_HDA_CODEC_HDMI_GENERIC + tristate "Generic HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_DYNAMIC_MINORS + select SND_PCM_ELD + default y + help + Say Y or M here to include Generic HDMI and DisplayPort HD-audio + codec support. + + Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS + to assure the multiple streams for DP-MST support. + +config SND_HDA_CODEC_HDMI_SIMPLE + tristate "Simple HDMI/DisplayPort HD-audio codec support" if EXPERT + default y + help + Say Y or M here to include Simple HDMI and DisplayPort HD-audio + codec support for VIA and other codecs. + +config SND_HDA_CODEC_HDMI_INTEL + tristate "Intel HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_HDA_CODEC_HDMI_GENERIC + default y + help + Say Y or M here to include Intel graphics HDMI and DisplayPort + HD-audio codec support. + +config SND_HDA_INTEL_HDMI_SILENT_STREAM + bool "Enable Silent Stream always for HDMI" + depends on SND_HDA_CODEC_HDMI_INTEL + help + Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream + for HDMI on hardware that supports the feature. + + When enabled, the HDMI/DisplayPort codec will continue to provide + a continuous clock and a valid but silent data stream to + any connected external receiver. This allows to avoid gaps + at start of playback. Many receivers require multiple seconds + to start playing audio after the clock has been stopped. + This feature can impact power consumption as resources + are kept reserved both at transmitter and receiver. + +config SND_HDA_CODEC_HDMI_ATI + tristate "AMD/ATI HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_HDA_CODEC_HDMI_GENERIC + default y + help + Say Y or M here to include AMD/ATI graphics HDMI and DisplayPort + HD-audio codec support. + +config SND_HDA_CODEC_HDMI_NVIDIA + tristate "Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_HDA_CODEC_HDMI_GENERIC + default y + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for the recent Nvidia graphics cards. + +config SND_HDA_CODEC_HDMI_NVIDIA_MCP + tristate "Legacy Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_HDA_CODEC_HDMI_SIMPLE + default y + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for the legacy Nvidia graphics like MCP73, MCP67, MCP77/78. + +config SND_HDA_CODEC_HDMI_TEGRA + tristate "Nvidia Tegra HDMI/DisplayPort HD-audio codec support" if EXPERT + select SND_HDA_CODEC_HDMI_GENERIC + default y + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for Nvidia Tegra. + +endif diff --git a/sound/hda/codecs/hdmi/Makefile b/sound/hda/codecs/hdmi/Makefile new file mode 100644 index 000000000000..0e49a9421e3b --- /dev/null +++ b/sound/hda/codecs/hdmi/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-hdmi-y := hdmi.o eld.o +snd-hda-codec-simplehdmi-y := simplehdmi.o +snd-hda-codec-intelhdmi-y := intelhdmi.o +snd-hda-codec-atihdmi-y := atihdmi.o +snd-hda-codec-nvhdmi-y := nvhdmi.o +snd-hda-codec-nvhdmi-mcp-y := nvhdmi-mcp.o +snd-hda-codec-tegrahdmi-y := tegrahdmi.o + +obj-$(CONFIG_SND_HDA_CODEC_HDMI_GENERIC) += snd-hda-codec-hdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_SIMPLE) += snd-hda-codec-simplehdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_INTEL) += snd-hda-codec-intelhdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_ATI) += snd-hda-codec-atihdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA) += snd-hda-codec-nvhdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA_MCP) += snd-hda-codec-nvhdmi-mcp.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_TEGRA) += snd-hda-codec-tegrahdmi.o diff --git a/sound/hda/codecs/hdmi/atihdmi.c b/sound/hda/codecs/hdmi/atihdmi.c new file mode 100644 index 000000000000..44366f75de33 --- /dev/null +++ b/sound/hda/codecs/hdmi/atihdmi.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ATI/AMD codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +#define is_amdhdmi_rev3_or_later(codec) \ + ((codec)->core.vendor_id == 0x1002aa01 && \ + ((codec)->core.revision_id & 0xff00) >= 0x0300) +#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) + +/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ +#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 +#define ATI_VERB_SET_DOWNMIX_INFO 0x772 +#define ATI_VERB_SET_MULTICHANNEL_01 0x777 +#define ATI_VERB_SET_MULTICHANNEL_23 0x778 +#define ATI_VERB_SET_MULTICHANNEL_45 0x779 +#define ATI_VERB_SET_MULTICHANNEL_67 0x77a +#define ATI_VERB_SET_HBR_CONTROL 0x77c +#define ATI_VERB_SET_MULTICHANNEL_1 0x785 +#define ATI_VERB_SET_MULTICHANNEL_3 0x786 +#define ATI_VERB_SET_MULTICHANNEL_5 0x787 +#define ATI_VERB_SET_MULTICHANNEL_7 0x788 +#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 +#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 +#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 +#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 +#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 +#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 +#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a +#define ATI_VERB_GET_HBR_CONTROL 0xf7c +#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 +#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 +#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 +#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 +#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 + +/* AMD specific HDA cvt verbs */ +#define ATI_VERB_SET_RAMP_RATE 0x770 +#define ATI_VERB_GET_RAMP_RATE 0xf70 + +#define ATI_OUT_ENABLE 0x1 + +#define ATI_MULTICHANNEL_MODE_PAIRED 0 +#define ATI_MULTICHANNEL_MODE_SINGLE 1 + +#define ATI_HBR_CAPABLE 0x01 +#define ATI_HBR_ENABLE 0x10 + +/* ATI/AMD specific ELD emulation */ + +#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 +#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 +#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 +#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 +#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b +#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 +#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 + +#define ATI_SPKALLOC_SPKALLOC 0x007f +#define ATI_SPKALLOC_TYPE_HDMI 0x0100 +#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 + +/* first three bytes are just standard SAD */ +#define ATI_AUDIODESC_CHANNELS 0x00000007 +#define ATI_AUDIODESC_RATES 0x0000ff00 +#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 + +/* in standard HDMI VSDB format */ +#define ATI_DELAY_VIDEO_LATENCY 0x000000ff +#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 + +enum ati_sink_info_idx { + ATI_INFO_IDX_MANUFACTURER_ID = 0, + ATI_INFO_IDX_PRODUCT_ID = 1, + ATI_INFO_IDX_SINK_DESC_LEN = 2, + ATI_INFO_IDX_PORT_ID_LOW = 3, + ATI_INFO_IDX_PORT_ID_HIGH = 4, + ATI_INFO_IDX_SINK_DESC_FIRST = 5, + ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ +}; + +static int get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, bool rev3_or_later) +{ + int spkalloc, ati_sad, aud_synch; + int sink_desc_len = 0; + int pos, i; + + /* ATI/AMD does not have ELD, emulate it */ + + spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); + + if (spkalloc <= 0) { + codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n"); + return -EINVAL; + } + + memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); + + /* version */ + buf[0] = ELD_VER_CEA_861D << 3; + + /* speaker allocation from EDID */ + buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; + + /* is DisplayPort? */ + if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) + buf[5] |= 0x04; + + pos = ELD_FIXED_BYTES; + + if (rev3_or_later) { + int sink_info; + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 8); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 12); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 16); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 18); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); + sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + + if (sink_desc_len > ELD_MAX_MNL) { + codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", + sink_desc_len); + sink_desc_len = ELD_MAX_MNL; + } + + buf[4] |= sink_desc_len; + + for (i = 0; i < sink_desc_len; i++) { + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); + buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + } + } + + for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { + if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) + continue; /* not handled by ATI/AMD */ + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); + ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); + + if (ati_sad <= 0) + continue; + + if (ati_sad & ATI_AUDIODESC_RATES) { + /* format is supported, copy SAD as-is */ + buf[pos++] = (ati_sad & 0x0000ff) >> 0; + buf[pos++] = (ati_sad & 0x00ff00) >> 8; + buf[pos++] = (ati_sad & 0xff0000) >> 16; + } + + if (i == AUDIO_CODING_TYPE_LPCM + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { + /* for PCM there is a separate stereo rate mask */ + buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; + /* rates from the extra byte */ + buf[pos++] = (ati_sad & 0xff000000) >> 24; + buf[pos++] = (ati_sad & 0x00ff0000) >> 16; + } + } + + if (pos == ELD_FIXED_BYTES + sink_desc_len) { + codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); + return -EINVAL; + } + + /* + * HDMI VSDB latency format: + * separately for both audio and video: + * 0 field not valid or unknown latency + * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) + * 255 audio/video not supported + * + * HDA latency format: + * single value indicating video latency relative to audio: + * 0 unknown or 0ms + * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) + * [251..255] reserved + */ + aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); + if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { + int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); + int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8; + + if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb && + video_latency_hdmi > audio_latency_hdmi) + buf[6] = video_latency_hdmi - audio_latency_hdmi; + /* else unknown/invalid or 0ms or video ahead of audio, so use zero */ + } + + /* SAD count */ + buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; + + /* Baseline ELD block length is 4-byte aligned */ + pos = round_up(pos, 4); + + /* Baseline ELD length (4-byte header is not counted in) */ + buf[2] = (pos - 4) / 4; + + *eld_size = pos; + + return 0; +} + +static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, + int dev_id, unsigned char *buf, int *eld_size) +{ + WARN_ON(dev_id != 0); + /* call hda_eld.c ATI/AMD-specific function */ + return get_eld_ati(codec, nid, buf, eld_size, + is_amdhdmi_rev3_or_later(codec)); +} + +static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id, int ca, + int active_channels, int conn_type) +{ + WARN_ON(dev_id != 0); + snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); +} + +static int atihdmi_paired_swap_fc_lfe(int pos) +{ + /* + * ATI/AMD have automatic FC/LFE swap built-in + * when in pairwise mapping mode. + */ + + switch (pos) { + /* see channel_allocations[].speakers[] */ + case 2: return 3; + case 3: return 2; + default: return pos; + } +} + +static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + struct hdac_cea_channel_speaker_allocation *cap; + int i, j; + + /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ + + cap = snd_hdac_get_ch_alloc_from_ca(ca); + for (i = 0; i < chs; ++i) { + int mask = snd_hdac_chmap_to_spk_mask(map[i]); + bool ok = false; + bool companion_ok = false; + + if (!mask) + continue; + + for (j = 0 + i % 2; j < 8; j += 2) { + int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); + + if (cap->speakers[chan_idx] == mask) { + /* channel is in a supported position */ + ok = true; + + if (i % 2 == 0 && i + 1 < chs) { + /* even channel, check the odd companion */ + int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); + int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); + int comp_mask_act = cap->speakers[comp_chan_idx]; + + if (comp_mask_req == comp_mask_act) + companion_ok = true; + else + return -EINVAL; + } + break; + } + } + + if (!ok) + return -EINVAL; + + if (companion_ok) + i++; /* companion channel already checked */ + } + + return 0; +} + +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + int verb; + int ati_channel_setup = 0; + + if (hdmi_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); + + /* In case this is an odd slot but without stream channel, do not + * disable the slot since the corresponding even slot could have a + * channel. In case neither have a channel, the slot pair will be + * disabled when this function is called for the even slot. + */ + if (hdmi_slot % 2 != 0 && stream_channel == 0xf) + return 0; + + hdmi_slot -= hdmi_slot % 2; + + if (stream_channel != 0xf) + stream_channel -= stream_channel % 2; + } + + verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; + + /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ + + if (stream_channel != 0xf) + ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; + + return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); +} + +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + bool was_odd = false; + int ati_asp_slot = asp_slot; + int verb; + int ati_channel_setup; + + if (asp_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); + if (ati_asp_slot % 2 != 0) { + ati_asp_slot -= 1; + was_odd = true; + } + } + + verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; + + ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); + + if (!(ati_channel_setup & ATI_OUT_ENABLE)) + return 0xf; + + return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; +} + +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, + int channels) +{ + int c; + + /* + * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so + * we need to take that into account (a single channel may take 2 + * channel slots if we need to carry a silent channel next to it). + * On Rev3+ AMD codecs this function is not used. + */ + int chanpairs = 0; + + /* We only produce even-numbered channel count TLVs */ + if ((channels % 2) != 0) + return -1; + + for (c = 0; c < 7; c += 2) { + if (cap->speakers[c] || cap->speakers[c+1]) + chanpairs++; + } + + if (chanpairs * 2 != channels) + return -1; + + return SNDRV_CTL_TLVT_CHMAP_PAIRED; +} + +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + /* produce paired maps for pre-rev3 ATI/AMD codecs */ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); + int spk = cap->speakers[chan]; + + if (!spk) { + /* add N/A channel if the companion channel is occupied */ + if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) + chmap[count++] = SNDRV_CHMAP_NA; + + continue; + } + + chmap[count++] = snd_hdac_spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, bool hbr) +{ + int hbr_ctl, hbr_ctl_new; + + WARN_ON(dev_id != 0); + + hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); + if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) { + if (hbr) + hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; + else + hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; + + codec_dbg(codec, + "%s: NID=0x%x, %shbr-ctl=0x%x\n", + __func__, + pin_nid, + hbr_ctl == hbr_ctl_new ? "" : "new-", + hbr_ctl_new); + + if (hbr_ctl != hbr_ctl_new) + snd_hda_codec_write(codec, pin_nid, 0, + ATI_VERB_SET_HBR_CONTROL, + hbr_ctl_new); + + } else if (hbr) + return -EINVAL; + + return 0; +} + +static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, + u32 stream_tag, int format) +{ + if (is_amdhdmi_rev3_or_later(codec)) { + int ramp_rate = 180; /* default as per AMD spec */ + /* disable ramp-up/down for non-pcm as per AMD spec */ + if (format & AC_FMT_TYPE_NON_PCM) + ramp_rate = 0; + + snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); + } + + return snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); +} + + +static int atihdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, err; + + err = snd_hda_hdmi_generic_init(codec); + + if (err) + return err; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* make sure downmix information in infoframe is zero */ + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); + + /* enable channel-wise remap mode if supported */ + if (has_amd_full_remap_support(codec)) + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + ATI_VERB_SET_MULTICHANNEL_MODE, + ATI_MULTICHANNEL_MODE_SINGLE); + } + codec->auto_runtime_pm = 1; + + return 0; +} + +/* map from pin NID to port; port is 0-based */ +/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ +static int atihdmi_pin2port(void *audio_ptr, int pin_nid) +{ + return pin_nid / 2 - 1; +} + +/* reverse-map from port to pin NID: see above */ +static int atihdmi_port2pin(struct hda_codec *codec, int port) +{ + return port * 2 + 3; +} + +static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { + .pin2port = atihdmi_pin2port, + .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify, + .master_bind = snd_hda_hdmi_acomp_master_bind, + .master_unbind = snd_hda_hdmi_acomp_master_unbind, +}; + +static int atihdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct hdmi_spec *spec; + struct hdmi_spec_per_cvt *per_cvt; + int err, cvt_idx; + + err = snd_hda_hdmi_generic_probe(codec); + if (err) + return err; + + spec = codec->spec; + + spec->static_pcm_mapping = true; + + spec->ops.pin_get_eld = atihdmi_pin_get_eld; + spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; + spec->ops.setup_stream = atihdmi_setup_stream; + + spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; + + if (!has_amd_full_remap_support(codec)) { + /* override to ATI/AMD-specific versions with pairwise mapping */ + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + atihdmi_paired_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + } + + /* ATI/AMD converters do not advertise all of their capabilities */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->channels_max = max(per_cvt->channels_max, 8u); + per_cvt->rates |= SUPPORTED_RATES; + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } + + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); + + /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing + * the link-down as is. Tell the core to allow it. + */ + codec->link_down_at_suspend = 1; + + snd_hda_hdmi_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin); + + return 0; +} + +static const struct hda_codec_ops atihdmi_codec_ops = { + .probe = atihdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = atihdmi_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_atihdmi[] = { + HDA_CODEC_ID(0x1002793c, "RS600 HDMI"), + HDA_CODEC_ID(0x10027919, "RS600 HDMI"), + HDA_CODEC_ID(0x1002791a, "RS690/780 HDMI"), + HDA_CODEC_ID(0x1002aa01, "R6xx HDMI"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_atihdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD/ATI HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver atihdmi_driver = { + .id = snd_hda_id_atihdmi, + .ops = &atihdmi_codec_ops, +}; + +module_hda_codec_driver(atihdmi_driver); diff --git a/sound/pci/hda/hda_eld.c b/sound/hda/codecs/hdmi/eld.c similarity index 51% rename from sound/pci/hda/hda_eld.c rename to sound/hda/codecs/hdmi/eld.c index d3e87b9c1a4f..1464fd1c675b 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/hda/codecs/hdmi/eld.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "hda_local.h" @@ -229,174 +228,3 @@ void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, hinfo->maxbps = min(hinfo->maxbps, maxbps); hinfo->channels_max = min(hinfo->channels_max, channels_max); } - - -/* ATI/AMD specific stuff (ELD emulation) */ - -#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 -#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 -#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 -#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 -#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b -#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 -#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 - -#define ATI_SPKALLOC_SPKALLOC 0x007f -#define ATI_SPKALLOC_TYPE_HDMI 0x0100 -#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 - -/* first three bytes are just standard SAD */ -#define ATI_AUDIODESC_CHANNELS 0x00000007 -#define ATI_AUDIODESC_RATES 0x0000ff00 -#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 - -/* in standard HDMI VSDB format */ -#define ATI_DELAY_VIDEO_LATENCY 0x000000ff -#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 - -enum ati_sink_info_idx { - ATI_INFO_IDX_MANUFACTURER_ID = 0, - ATI_INFO_IDX_PRODUCT_ID = 1, - ATI_INFO_IDX_SINK_DESC_LEN = 2, - ATI_INFO_IDX_PORT_ID_LOW = 3, - ATI_INFO_IDX_PORT_ID_HIGH = 4, - ATI_INFO_IDX_SINK_DESC_FIRST = 5, - ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ -}; - -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, bool rev3_or_later) -{ - int spkalloc, ati_sad, aud_synch; - int sink_desc_len = 0; - int pos, i; - - /* ATI/AMD does not have ELD, emulate it */ - - spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); - - if (spkalloc <= 0) { - codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n"); - return -EINVAL; - } - - memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); - - /* version */ - buf[0] = ELD_VER_CEA_861D << 3; - - /* speaker allocation from EDID */ - buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; - - /* is DisplayPort? */ - if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) - buf[5] |= 0x04; - - pos = ELD_FIXED_BYTES; - - if (rev3_or_later) { - int sink_info; - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 8); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 12); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 16); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 18); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); - sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - - if (sink_desc_len > ELD_MAX_MNL) { - codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", - sink_desc_len); - sink_desc_len = ELD_MAX_MNL; - } - - buf[4] |= sink_desc_len; - - for (i = 0; i < sink_desc_len; i++) { - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); - buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - } - } - - for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { - if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) - continue; /* not handled by ATI/AMD */ - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); - ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); - - if (ati_sad <= 0) - continue; - - if (ati_sad & ATI_AUDIODESC_RATES) { - /* format is supported, copy SAD as-is */ - buf[pos++] = (ati_sad & 0x0000ff) >> 0; - buf[pos++] = (ati_sad & 0x00ff00) >> 8; - buf[pos++] = (ati_sad & 0xff0000) >> 16; - } - - if (i == AUDIO_CODING_TYPE_LPCM - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { - /* for PCM there is a separate stereo rate mask */ - buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; - /* rates from the extra byte */ - buf[pos++] = (ati_sad & 0xff000000) >> 24; - buf[pos++] = (ati_sad & 0x00ff0000) >> 16; - } - } - - if (pos == ELD_FIXED_BYTES + sink_desc_len) { - codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); - return -EINVAL; - } - - /* - * HDMI VSDB latency format: - * separately for both audio and video: - * 0 field not valid or unknown latency - * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) - * 255 audio/video not supported - * - * HDA latency format: - * single value indicating video latency relative to audio: - * 0 unknown or 0ms - * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) - * [251..255] reserved - */ - aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); - if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { - int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); - int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8; - - if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb && - video_latency_hdmi > audio_latency_hdmi) - buf[6] = video_latency_hdmi - audio_latency_hdmi; - /* else unknown/invalid or 0ms or video ahead of audio, so use zero */ - } - - /* SAD count */ - buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; - - /* Baseline ELD block length is 4-byte aligned */ - pos = round_up(pos, 4); - - /* Baseline ELD length (4-byte header is not counted in) */ - buf[2] = (pos - 4) / 4; - - *eld_size = pos; - - return 0; -} diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c new file mode 100644 index 000000000000..44576b30f699 --- /dev/null +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -0,0 +1,2417 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * hdmi.c - routines for HDMI/DisplayPort codecs + * + * Copyright(c) 2008-2010 Intel Corporation + * Copyright (c) 2006 ATI Technologies Inc. + * Copyright (c) 2008 NVIDIA Corp. All rights reserved. + * Copyright (c) 2008 Wei Ni + * Copyright (c) 2013 Anssi Hannula + * + * Authors: + * Wu Fengguang + * + * Maintained by: + * Wu Fengguang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_jack.h" +#include "hda_controller.h" +#include "hdmi_local.h" + +static bool static_hdmi_pcm; +module_param(static_hdmi_pcm, bool, 0644); +MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); + +static bool enable_acomp = true; +module_param(enable_acomp, bool, 0444); +MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); + +static bool enable_all_pins; +module_param(enable_all_pins, bool, 0444); +MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins"); + +int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + + /* + * (dev_id == -1) means it is NON-MST pin + * return the first virtual pin on this port + */ + if (dev_id == -1) + dev_id = 0; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + per_pin = get_pin(spec, pin_idx); + if ((per_pin->pin_nid == pin_nid) && + (per_pin->dev_id == dev_id)) + return pin_idx; + } + + codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_pin_id_to_pin_index, "SND_HDA_CODEC_HDMI"); + +static int hinfo_to_pcm_index(struct hda_codec *codec, + struct hda_pcm_stream *hinfo) +{ + struct hdmi_spec *spec = codec->spec; + int pcm_idx; + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) + if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) + return pcm_idx; + + codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo); + return -EINVAL; +} + +static int hinfo_to_pin_index(struct hda_codec *codec, + struct hda_pcm_stream *hinfo) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + per_pin = get_pin(spec, pin_idx); + if (per_pin->pcm && + per_pin->pcm->pcm->stream == hinfo) + return pin_idx; + } + + codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo, + hinfo_to_pcm_index(codec, hinfo)); + return -EINVAL; +} + +static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec, + int pcm_idx) +{ + int i; + struct hdmi_spec_per_pin *per_pin; + + for (i = 0; i < spec->num_pins; i++) { + per_pin = get_pin(spec, i); + if (per_pin->pcm_idx == pcm_idx) + return per_pin; + } + return NULL; +} + +static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) +{ + struct hdmi_spec *spec = codec->spec; + int cvt_idx; + + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) + if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid) + return cvt_idx; + + codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid); + return -EINVAL; +} + +static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; + int pcm_idx; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + uinfo->count = 0; + goto unlock; + } + eld = &per_pin->sink_eld; + uinfo->count = eld->eld_valid ? eld->eld_size : 0; + + unlock: + mutex_unlock(&spec->pcm_lock); + return 0; +} + +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; + int pcm_idx; + int err = 0; + + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + goto unlock; + } + + eld = &per_pin->sink_eld; + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { + snd_BUG(); + err = -EINVAL; + goto unlock; + } + + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + if (eld->eld_valid) + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); + + unlock: + mutex_unlock(&spec->pcm_lock); + return err; +} + +static const struct snd_kcontrol_new eld_bytes_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, +}; + +static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx, + int device) +{ + struct snd_kcontrol *kctl; + struct hdmi_spec *spec = codec->spec; + int err; + + kctl = snd_ctl_new1(&eld_bytes_ctl, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = pcm_idx; + kctl->id.device = device; + + /* no pin nid is associated with the kctl now + * tbd: associate pin nid to eld ctl later + */ + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + + get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl; + return 0; +} + +#ifdef BE_PARANOID +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int *packet_index, int *byte_index) +{ + int val; + + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); + + *packet_index = val >> 5; + *byte_index = val & 0x1f; +} +#endif + +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int packet_index, int byte_index) +{ + int val; + + val = (packet_index << 5) | (byte_index & 0x1f); + + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); +} + +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, + unsigned char val) +{ + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); +} + +static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec = codec->spec; + int pin_out; + + /* Unmute */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + if (spec->dyn_pin_out) + /* Disable pin out until stream is active */ + pin_out = 0; + else + /* Enable pin out: some machines with GM965 gets broken output + * when the pin is disabled or changed while using with HDMI + */ + pin_out = PIN_OUT; + + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); +} + +/* + * ELD proc files + */ + +#ifdef CONFIG_SND_PROC_FS +static void print_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer, per_pin->pin_nid, + per_pin->dev_id, per_pin->cvt_nid); + mutex_unlock(&per_pin->lock); +} + +static void write_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer); + mutex_unlock(&per_pin->lock); +} + +static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) +{ + char name[32]; + struct hda_codec *codec = per_pin->codec; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); + err = snd_card_proc_new(codec->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, per_pin, print_eld_info); + entry->c.text.write = write_eld_info; + entry->mode |= 0200; + per_pin->proc_entry = entry; + + return 0; +} + +static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ + if (!per_pin->codec->bus->shutdown) { + snd_info_free_entry(per_pin->proc_entry); + per_pin->proc_entry = NULL; + } +} +#else +static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin, + int index) +{ + return 0; +} +static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ +} +#endif + +/* + * Audio InfoFrame routines + */ + +/* + * Enable Audio InfoFrame Transmission + */ +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_BEST); +} + +/* + * Disable Audio InfoFrame Transmission + */ +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_DISABLE); +} + +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int size; + + size = snd_hdmi_get_eld_size(codec, pin_nid); + codec_dbg(codec, "HDMI: ELD buf size is %d\n", size); + + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size); + } +#endif +} + +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef BE_PARANOID + int i, j; + int size; + int pi, bi; + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + if (size == 0) + continue; + + hdmi_set_dip_index(codec, pin_nid, i, 0x0); + for (j = 1; j < 1000; j++) { + hdmi_write_dip_byte(codec, pin_nid, 0x0); + hdmi_get_dip_index(codec, pin_nid, &pi, &bi); + if (pi != i) + codec_dbg(codec, "dip index %d: %d != %d\n", + bi, pi, i); + if (bi == 0) /* byte index wrapped around */ + break; + } + codec_dbg(codec, + "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", + i, size, j); + } +#endif +} + +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) +{ + u8 *bytes = (u8 *)hdmi_ai; + u8 sum = 0; + int i; + + hdmi_ai->checksum = 0; + + for (i = 0; i < sizeof(*hdmi_ai); i++) + sum += bytes[i]; + + hdmi_ai->checksum = -sum; +} + +static void hdmi_fill_audio_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, + u8 *dip, int size) +{ + int i; + + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < size; i++) + hdmi_write_dip_byte(codec, pin_nid, dip[i]); +} + +static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + u8 *dip, int size) +{ + u8 val; + int i; + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) + != AC_DIPXMIT_BEST) + return false; + + for (i = 0; i < size; i++) { + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_DATA, 0); + if (val != dip[i]) + return false; + } + + return true; +} + +static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, + int dev_id, unsigned char *buf, int *eld_size) +{ + snd_hda_set_dev_select(codec, nid, dev_id); + + return snd_hdmi_get_eld(codec, nid, buf, eld_size); +} + +static void hdmi_pin_setup_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id, + int ca, int active_channels, + int conn_type) +{ + struct hdmi_spec *spec = codec->spec; + union audio_infoframe ai; + + memset(&ai, 0, sizeof(ai)); + if ((conn_type == 0) || /* HDMI */ + /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */ + (conn_type == 1 && spec->nv_dp_workaround)) { + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + + if (conn_type == 0) { /* HDMI */ + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + } else {/* Nvidia DP */ + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x1b; + hdmi_ai->len = 0x11 << 2; + } + hdmi_ai->CC02_CT47 = active_channels - 1; + hdmi_ai->CA = ca; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai = &ai.dp; + + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = active_channels - 1; + dp_ai->CA = ca; + } else { + codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid); + return; + } + + snd_hda_set_dev_select(codec, pin_nid, dev_id); + + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { + codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n", + __func__, pin_nid, active_channels, ca); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai.bytes, sizeof(ai)); + hdmi_start_infoframe_trans(codec, pin_nid); + } +} + +void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm) +{ + struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; + hda_nid_t pin_nid = per_pin->pin_nid; + int dev_id = per_pin->dev_id; + int channels = per_pin->channels; + int active_channels; + struct hdmi_eld *eld; + int ca; + + if (!channels) + return; + + snd_hda_set_dev_select(codec, pin_nid, dev_id); + + /* some HW (e.g. HSW+) needs reprogramming the amp at each time */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + + eld = &per_pin->sink_eld; + + ca = snd_hdac_channel_allocation(&codec->core, + eld->info.spk_alloc, channels, + per_pin->chmap_set, non_pcm, per_pin->chmap); + + active_channels = snd_hdac_get_active_channels(ca); + + chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, + active_channels); + + /* + * always configure channel mapping, it may have been changed by the + * user in the meantime + */ + snd_hdac_setup_channel_mapping(&spec->chmap, + pin_nid, non_pcm, ca, channels, + per_pin->chmap, per_pin->chmap_set); + + spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id, + ca, active_channels, eld->info.conn_type); + + per_pin->non_pcm = non_pcm; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_audio_infoframe, "SND_HDA_CODEC_HDMI"); + +/* + * Unsolicited events + */ + +static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); + +void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec, + hda_nid_t nid, int dev_id) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx = pin_id_to_pin_index(codec, nid, dev_id); + + if (pin_idx < 0) + return; + mutex_lock(&spec->pcm_lock); + hdmi_present_sense(get_pin(spec, pin_idx), 1); + mutex_unlock(&spec->pcm_lock); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_check_presence_and_report, + "SND_HDA_CODEC_HDMI"); + +static void jack_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + /* stop polling when notification is enabled */ + if (codec_has_acomp(codec)) + return; + + snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id); +} + +static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, + struct hda_jack_tbl *jack) +{ + jack->jack_dirty = 1; + + codec_dbg(codec, + "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), + !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); + + snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id); +} + +static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); + int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); + + codec_info(codec, + "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + codec->addr, + tag, + subtag, + cp_state, + cp_ready); + + /* TODO */ + if (cp_state) { + ; + } + if (cp_ready) { + ; + } +} + +void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res) +{ + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + struct hda_jack_tbl *jack; + + if (codec_has_acomp(codec)) + return; + + if (codec->dp_mst) { + int dev_entry = + (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; + + jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); + } else { + jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); + } + + if (!jack) { + codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag); + return; + } + + if (subtag == 0) + hdmi_intrinsic_event(codec, res, jack); + else + hdmi_non_intrinsic_event(codec, res); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_unsol_event, "SND_HDA_CODEC_HDMI"); + +/* + * Callbacks + */ + +/* HBR should be Non-PCM, 8 channels */ +#define is_hbr_format(format) \ + ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) + +static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, bool hbr) +{ + int pinctl, new_pinctl; + + if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { + snd_hda_set_dev_select(codec, pin_nid, dev_id); + pinctl = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + if (pinctl < 0) + return hbr ? -EINVAL : 0; + + new_pinctl = pinctl & ~AC_PINCTL_EPT; + if (hbr) + new_pinctl |= AC_PINCTL_EPT_HBR; + else + new_pinctl |= AC_PINCTL_EPT_NATIVE; + + codec_dbg(codec, + "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n", + pin_nid, + pinctl == new_pinctl ? "" : "new-", + new_pinctl); + + if (pinctl != new_pinctl) + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + new_pinctl); + } else if (hbr) + return -EINVAL; + + return 0; +} + +int snd_hda_hdmi_setup_stream(struct hda_codec *codec, + hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, + u32 stream_tag, int format) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int param; + int err; + + err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id, + is_hbr_format(format)); + + if (err) { + codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n"); + return err; + } + + if (spec->intel_hsw_fixup) { + + /* + * on recent platforms IEC Coding Type is required for HBR + * support, read current Digital Converter settings and set + * ICT bitfield if needed. + */ + param = snd_hda_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + + param = (param >> 16) & ~(AC_DIG3_ICT); + + /* on recent platforms ICT mode is required for HBR support */ + if (is_hbr_format(format)) + param |= 0x1; + + snd_hda_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_DIGI_CONVERT_3, param); + } + + snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_stream, "SND_HDA_CODEC_HDMI"); + +/* Try to find an available converter + * If pin_idx is less then zero, just try to find an available converter. + * Otherwise, try to find an available converter and get the cvt mux index + * of the pin. + */ +static int hdmi_choose_cvt(struct hda_codec *codec, + int pin_idx, int *cvt_id, + bool silent) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int cvt_idx, mux_idx = 0; + + /* pin_idx < 0 means no pin will be bound to the converter */ + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); + + if (per_pin && per_pin->silent_stream) { + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + per_cvt = get_cvt(spec, cvt_idx); + if (per_cvt->assigned && !silent) + return -EBUSY; + if (cvt_id) + *cvt_id = cvt_idx; + return 0; + } + + /* Dynamically assign converter to stream */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + + /* Must not already be assigned */ + if (per_cvt->assigned || per_cvt->silent_stream) + continue; + if (per_pin == NULL) + break; + /* Must be in pin's mux's list of converters */ + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) + break; + /* Not in mux list */ + if (mux_idx == per_pin->num_mux_nids) + continue; + break; + } + + /* No free converters */ + if (cvt_idx == spec->num_cvts) + return -EBUSY; + + if (per_pin != NULL) + per_pin->mux_idx = mux_idx; + + if (cvt_id) + *cvt_id = cvt_idx; + + return 0; +} + +/* skeleton caller of pin_cvt_fixup ops */ +static void pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->ops.pin_cvt_fixup) + spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid); +} + +/* called in hdmi_pcm_open when no pin is assigned to the PCM */ +static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int cvt_idx, pcm_idx; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int err; + + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) + return -EINVAL; + + err = hdmi_choose_cvt(codec, -1, &cvt_idx, false); + if (err) + return err; + + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = true; + hinfo->nid = per_cvt->cvt_nid; + + pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid); + + set_bit(pcm_idx, &spec->pcm_in_use); + /* todo: setup spdif ctls assign */ + + /* Initially set the converter's capabilities */ + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; + + /* Store the updated parameters */ + runtime->hw.channels_min = hinfo->channels_min; + runtime->hw.channels_max = hinfo->channels_max; + runtime->hw.formats = hinfo->formats; + runtime->hw.rates = hinfo->rates; + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + return 0; +} + +/* + * HDA PCM callbacks + */ +static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int pin_idx, cvt_idx, pcm_idx; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int err; + + /* Validate hinfo */ + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) + return -EINVAL; + + mutex_lock(&spec->pcm_lock); + pin_idx = hinfo_to_pin_index(codec, hinfo); + /* no pin is assigned to the PCM + * PA need pcm open successfully when probe + */ + if (pin_idx < 0) { + err = hdmi_pcm_open_no_pin(hinfo, codec, substream); + goto unlock; + } + + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false); + if (err < 0) + goto unlock; + + per_cvt = get_cvt(spec, cvt_idx); + /* Claim converter */ + per_cvt->assigned = true; + + set_bit(pcm_idx, &spec->pcm_in_use); + per_pin = get_pin(spec, pin_idx); + per_pin->cvt_nid = per_cvt->cvt_nid; + hinfo->nid = per_cvt->cvt_nid; + + /* flip stripe flag for the assigned stream if supported */ + if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE) + azx_stream(get_azx_dev(substream))->stripe = 1; + + snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + per_pin->mux_idx); + + /* configure unused pins to choose other converters */ + pin_cvt_fixup(codec, per_pin, 0); + + snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); + + /* Initially set the converter's capabilities */ + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; + + eld = &per_pin->sink_eld; + /* Restrict capabilities by ELD if this isn't disabled */ + if (!static_hdmi_pcm && eld->eld_valid) { + snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); + if (hinfo->channels_min > hinfo->channels_max || + !hinfo->rates || !hinfo->formats) { + per_cvt->assigned = false; + hinfo->nid = 0; + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + err = -ENODEV; + goto unlock; + } + } + + /* Store the updated parameters */ + runtime->hw.channels_min = hinfo->channels_min; + runtime->hw.channels_max = hinfo->channels_max; + runtime->hw.formats = hinfo->formats; + runtime->hw.rates = hinfo->rates; + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + unlock: + mutex_unlock(&spec->pcm_lock); + return err; +} + +/* + * HDA/HDMI auto parsing + */ +static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + hda_nid_t pin_nid = per_pin->pin_nid; + int dev_id = per_pin->dev_id; + int conns; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + codec_warn(codec, + "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + snd_hda_set_dev_select(codec, pin_nid, dev_id); + + if (spec->intel_hsw_fixup) { + conns = spec->num_cvts; + memcpy(per_pin->mux_nids, spec->cvt_nids, + sizeof(hda_nid_t) * conns); + } else { + conns = snd_hda_get_raw_connections(codec, pin_nid, + per_pin->mux_nids, + HDA_MAX_CONNECTIONS); + } + + /* all the device entries on the same pin have the same conn list */ + per_pin->num_mux_nids = conns; + + return 0; +} + +static int hdmi_find_pcm_slot(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int i; + + for (i = 0; i < spec->pcm_used; i++) { + if (!test_bit(i, &spec->pcm_bitmap)) + return i; + } + return -EBUSY; +} + +static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be attached to the pin */ + if (per_pin->pcm) + return; + /* try the previously used slot at first */ + idx = per_pin->prev_pcm_idx; + if (idx >= 0) { + if (!test_bit(idx, &spec->pcm_bitmap)) + goto found; + per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */ + } + idx = hdmi_find_pcm_slot(spec, per_pin); + if (idx == -EBUSY) + return; + found: + per_pin->pcm_idx = idx; + per_pin->pcm = get_hdmi_pcm(spec, idx); + set_bit(idx, &spec->pcm_bitmap); +} + +static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be detached from the pin */ + if (!per_pin->pcm) + return; + idx = per_pin->pcm_idx; + per_pin->pcm_idx = -1; + per_pin->prev_pcm_idx = idx; /* remember the previous index */ + per_pin->pcm = NULL; + if (idx >= 0 && idx < spec->pcm_used) + clear_bit(idx, &spec->pcm_bitmap); +} + +static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid) +{ + int mux_idx; + + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == cvt_nid) + break; + return mux_idx; +} + +static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid); + +static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hda_codec *codec = per_pin->codec; + struct hda_pcm *pcm; + struct hda_pcm_stream *hinfo; + struct snd_pcm_substream *substream; + int mux_idx; + bool non_pcm; + + if (per_pin->pcm_idx < 0 || per_pin->pcm_idx >= spec->pcm_used) + return; + pcm = get_pcm_rec(spec, per_pin->pcm_idx); + if (!pcm->pcm) + return; + if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) + return; + + /* hdmi audio only uses playback and one substream */ + hinfo = pcm->stream; + substream = pcm->pcm->streams[0].substream; + + per_pin->cvt_nid = hinfo->nid; + + mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid); + if (mux_idx < per_pin->num_mux_nids) { + snd_hda_set_dev_select(codec, per_pin->pin_nid, + per_pin->dev_id); + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid); + + non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid); + if (substream->runtime) + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; + per_pin->mux_idx = mux_idx; + + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); +} + +static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) + snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx); + + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); + + per_pin->setup = false; + per_pin->channels = 0; +} + +static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + + if (per_pin->pcm_idx >= 0) + return spec->pcm_rec[per_pin->pcm_idx].jack; + else + return NULL; +} + +/* update per_pin ELD from the given new ELD; + * setup info frame and notification accordingly + * also notify ELD kctl and report jack status changes + */ +static void update_eld(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + struct hdmi_eld *eld, + int repoll) +{ + struct hdmi_eld *pin_eld = &per_pin->sink_eld; + struct hdmi_spec *spec = codec->spec; + struct snd_jack *pcm_jack; + bool old_eld_valid = pin_eld->eld_valid; + bool eld_changed; + int pcm_idx; + + if (eld->eld_valid) { + if (eld->eld_size <= 0 || + snd_parse_eld(hda_codec_dev(codec), &eld->info, + eld->eld_buffer, eld->eld_size) < 0) { + eld->eld_valid = false; + if (repoll) { + schedule_delayed_work(&per_pin->work, + msecs_to_jiffies(300)); + return; + } + } + } + + if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { + eld->eld_valid = false; + eld->eld_size = 0; + } + + /* for monitor disconnection, save pcm_idx firstly */ + pcm_idx = per_pin->pcm_idx; + + /* + * pcm_idx >=0 before update_eld() means it is in monitor + * disconnected event. Jack must be fetched before update_eld(). + */ + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); + + if (!spec->static_pcm_mapping) { + if (eld->eld_valid) { + hdmi_attach_hda_pcm(spec, per_pin); + hdmi_pcm_setup_pin(spec, per_pin); + } else { + hdmi_pcm_reset_pin(spec, per_pin); + hdmi_detach_hda_pcm(spec, per_pin); + } + } + + /* if pcm_idx == -1, it means this is in monitor connection event + * we can get the correct pcm_idx now. + */ + if (pcm_idx == -1) + pcm_idx = per_pin->pcm_idx; + if (!pcm_jack) + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); + + if (eld->eld_valid) + snd_show_eld(hda_codec_dev(codec), &eld->info); + + eld_changed = (pin_eld->eld_valid != eld->eld_valid); + eld_changed |= (pin_eld->monitor_present != eld->monitor_present); + if (!eld_changed && eld->eld_valid && pin_eld->eld_valid) + if (pin_eld->eld_size != eld->eld_size || + memcmp(pin_eld->eld_buffer, eld->eld_buffer, + eld->eld_size) != 0) + eld_changed = true; + + if (eld_changed) { + pin_eld->monitor_present = eld->monitor_present; + pin_eld->eld_valid = eld->eld_valid; + pin_eld->eld_size = eld->eld_size; + if (eld->eld_valid) + memcpy(pin_eld->eld_buffer, eld->eld_buffer, + eld->eld_size); + pin_eld->info = eld->info; + } + + /* + * Re-setup pin and infoframe. This is needed e.g. when + * - sink is first plugged-in + * - transcoder can change during stream playback on Haswell + * and this can make HW reset converter selection on a pin. + */ + if (eld->eld_valid && !old_eld_valid && per_pin->setup) { + pin_cvt_fixup(codec, per_pin, 0); + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + } + + if (eld_changed && pcm_idx >= 0) + snd_ctl_notify(codec->card, + SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); + + if (eld_changed && pcm_jack) + snd_jack_report(pcm_jack, + (eld->monitor_present && eld->eld_valid) ? + SND_JACK_AVOUT : 0); +} + +/* update ELD and jack state via HD-audio verbs */ +static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, + int repoll) +{ + struct hda_codec *codec = per_pin->codec; + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld = &spec->temp_eld; + struct device *dev = hda_codec_dev(codec); + hda_nid_t pin_nid = per_pin->pin_nid; + int dev_id = per_pin->dev_id; + /* + * Always execute a GetPinSense verb here, even when called from + * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited + * response's PD bit is not the real PD value, but indicates that + * the real PD value changed. An older version of the HD-audio + * specification worked this way. Hence, we just ignore the data in + * the unsolicited response to avoid custom WARs. + */ + int present; + int ret; + +#ifdef CONFIG_PM + if (dev->power.runtime_status == RPM_SUSPENDING) + return; +#endif + + ret = snd_hda_power_up_pm(codec); + if (ret < 0 && pm_runtime_suspended(dev)) + goto out; + + present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id); + + mutex_lock(&per_pin->lock); + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + if (eld->monitor_present) + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + else + eld->eld_valid = false; + + codec_dbg(codec, + "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); + + if (eld->eld_valid) { + if (spec->ops.pin_get_eld(codec, pin_nid, dev_id, + eld->eld_buffer, &eld->eld_size) < 0) + eld->eld_valid = false; + } + + update_eld(codec, per_pin, eld, repoll); + mutex_unlock(&per_pin->lock); + out: + snd_hda_power_down_pm(codec); +} + +static void silent_stream_enable(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx, pin_idx, err; + + /* + * Power-up will call hdmi_present_sense, so the PM calls + * have to be done without mutex held. + */ + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream enable ret=[%d]\n", err); + snd_hda_power_down_pm(codec); + return; + } + + mutex_lock(&per_pin->lock); + + if (per_pin->setup) { + codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); + err = -EBUSY; + goto unlock_out; + } + + pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id); + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, true); + if (err) { + codec_err(codec, "hdmi: no free converter to enable silent mode\n"); + goto unlock_out; + } + + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->silent_stream = true; + per_pin->cvt_nid = per_cvt->cvt_nid; + per_pin->silent_stream = true; + + codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_cvt->cvt_nid); + + snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + per_pin->mux_idx); + + /* configure unused pins to choose other converters */ + pin_cvt_fixup(codec, per_pin, 0); + + spec->ops.silent_stream(codec, per_pin, true); + + unlock_out: + mutex_unlock(&per_pin->lock); + + snd_hda_power_down_pm(codec); +} + +static void silent_stream_disable(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx, err; + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream disable ret=[%d]\n", + err); + snd_hda_power_down_pm(codec); + return; + } + + mutex_lock(&per_pin->lock); + if (!per_pin->silent_stream) + goto unlock_out; + + codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_pin->cvt_nid); + + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->silent_stream = false; + } + + spec->ops.silent_stream(codec, per_pin, false); + + per_pin->cvt_nid = 0; + per_pin->silent_stream = false; + + unlock_out: + mutex_unlock(&per_pin->lock); + + snd_hda_power_down_pm(codec); +} + +/* update ELD and jack state via audio component */ +static void sync_eld_via_acomp(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld = &spec->temp_eld; + bool monitor_prev, monitor_next; + + mutex_lock(&per_pin->lock); + eld->monitor_present = false; + monitor_prev = per_pin->sink_eld.monitor_present; + eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, + per_pin->dev_id, &eld->monitor_present, + eld->eld_buffer, ELD_MAX_SIZE); + eld->eld_valid = (eld->eld_size > 0); + update_eld(codec, per_pin, eld, 0); + monitor_next = per_pin->sink_eld.monitor_present; + mutex_unlock(&per_pin->lock); + + if (spec->silent_stream_type) { + if (!monitor_prev && monitor_next) + silent_stream_enable(codec, per_pin); + else if (monitor_prev && !monitor_next) + silent_stream_disable(codec, per_pin); + } +} + +static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) +{ + struct hda_codec *codec = per_pin->codec; + + if (!codec_has_acomp(codec)) + hdmi_present_sense_via_verbs(per_pin, repoll); + else + sync_eld_via_acomp(codec, per_pin); +} + +static void hdmi_repoll_eld(struct work_struct *work) +{ + struct hdmi_spec_per_pin *per_pin = + container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); + struct hda_codec *codec = per_pin->codec; + struct hdmi_spec *spec = codec->spec; + struct hda_jack_tbl *jack; + + jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, + per_pin->dev_id); + if (jack) + jack->jack_dirty = 1; + + if (per_pin->repoll_count++ > 6) + per_pin->repoll_count = 0; + + mutex_lock(&spec->pcm_lock); + hdmi_present_sense(per_pin, per_pin->repoll_count); + mutex_unlock(&spec->pcm_lock); +} + +static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int caps, config; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + int err; + int dev_num, i; + + caps = snd_hda_query_pin_caps(codec, pin_nid); + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) + return 0; + + /* + * For DP MST audio, Configuration Default is the same for + * all device entries on the same pin + */ + config = snd_hda_codec_get_pincfg(codec, pin_nid); + if (get_defcfg_connect(config) == AC_JACK_PORT_NONE && + !spec->force_connect) + return 0; + + /* + * To simplify the implementation, malloc all + * the virtual pins in the initialization statically + */ + if (spec->intel_hsw_fixup) { + /* + * On Intel platforms, device entries count returned + * by AC_PAR_DEVLIST_LEN is dynamic, and depends on + * the type of receiver that is connected. Allocate pin + * structures based on worst case. + */ + dev_num = spec->dev_num; + } else if (codec->dp_mst) { + dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1; + /* + * spec->dev_num is the maxinum number of device entries + * among all the pins + */ + spec->dev_num = (spec->dev_num > dev_num) ? + spec->dev_num : dev_num; + } else { + /* + * If the platform doesn't support DP MST, + * manually set dev_num to 1. This means + * the pin has only one device entry. + */ + dev_num = 1; + spec->dev_num = 1; + } + + for (i = 0; i < dev_num; i++) { + pin_idx = spec->num_pins; + per_pin = snd_array_new(&spec->pins); + + if (!per_pin) + return -ENOMEM; + + per_pin->pcm = NULL; + per_pin->pcm_idx = -1; + per_pin->prev_pcm_idx = -1; + per_pin->pin_nid = pin_nid; + per_pin->pin_nid_idx = spec->num_nids; + per_pin->dev_id = i; + per_pin->non_pcm = false; + snd_hda_set_dev_select(codec, pin_nid, i); + err = hdmi_read_pin_conn(codec, pin_idx); + if (err < 0) + return err; + if (!is_jack_detectable(codec, pin_nid)) + codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid); + spec->num_pins++; + } + spec->num_nids++; + + return 0; +} + +static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + unsigned int chans; + int err; + + chans = get_wcaps(codec, cvt_nid); + chans = get_wcaps_channels(chans); + + per_cvt = snd_array_new(&spec->cvts); + if (!per_cvt) + return -ENOMEM; + + per_cvt->cvt_nid = cvt_nid; + per_cvt->channels_min = 2; + if (chans <= 16) { + per_cvt->channels_max = chans; + if (chans > spec->chmap.channels_max) + spec->chmap.channels_max = chans; + } + + err = snd_hda_query_supported_pcm(codec, cvt_nid, + &per_cvt->rates, + &per_cvt->formats, + NULL, + &per_cvt->maxbps); + if (err < 0) + return err; + + if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids)) + spec->cvt_nids[spec->num_cvts] = cvt_nid; + spec->num_cvts++; + + return 0; +} + +static const struct snd_pci_quirk force_connect_list[] = { + SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), + SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), + SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1), + SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), + SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), + SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1), + SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1), + SND_PCI_QUIRK(0x1043, 0x86ae, "ASUS", 1), /* Z170 PRO */ + SND_PCI_QUIRK(0x1043, 0x86c7, "ASUS", 1), /* Z170M PLUS */ + SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1), + SND_PCI_QUIRK(0x8086, 0x2060, "Intel NUC5CPYB", 1), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1), + {} +}; + +int snd_hda_hdmi_parse_codec(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t start_nid; + unsigned int caps; + int i, nodes; + const struct snd_pci_quirk *q; + + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid); + if (!start_nid || nodes < 0) { + codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + if (enable_all_pins) + spec->force_connect = true; + + q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list); + + if (q && q->value) + spec->force_connect = true; + + /* + * hdmi_add_pin() assumes total amount of converters to + * be known, so first discover all converters + */ + for (i = 0; i < nodes; i++) { + hda_nid_t nid = start_nid + i; + + caps = get_wcaps(codec, nid); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + if (get_wcaps_type(caps) == AC_WID_AUD_OUT) + hdmi_add_cvt(codec, nid); + } + + /* discover audio pins */ + for (i = 0; i < nodes; i++) { + hda_nid_t nid = start_nid + i; + + caps = get_wcaps(codec, nid); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + if (get_wcaps_type(caps) == AC_WID_PIN) + hdmi_add_pin(codec, nid); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_parse_codec, "SND_HDA_CODEC_HDMI"); + +/* + */ +static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) +{ + struct hda_spdif_out *spdif; + bool non_pcm; + + mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid); + /* Add sanity check to pass klockwork check. + * This should never happen. + */ + if (WARN_ON(spdif == NULL)) { + mutex_unlock(&codec->spdif_mutex); + return true; + } + non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); + mutex_unlock(&codec->spdif_mutex); + return non_pcm; +} + +/* + * HDMI callbacks + */ + +int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + hda_nid_t cvt_nid = hinfo->nid; + struct hdmi_spec *spec = codec->spec; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + struct snd_pcm_runtime *runtime = substream->runtime; + bool non_pcm; + int pinctl, stripe; + int err = 0; + + mutex_lock(&spec->pcm_lock); + pin_idx = hinfo_to_pin_index(codec, hinfo); + if (pin_idx < 0) { + /* when pcm is not bound to a pin skip pin setup and return 0 + * to make audio playback be ongoing + */ + pin_cvt_fixup(codec, NULL, cvt_nid); + snd_hda_codec_setup_stream(codec, cvt_nid, + stream_tag, 0, format); + goto unlock; + } + + per_pin = get_pin(spec, pin_idx); + + /* Verify pin:cvt selections to avoid silent audio after S3. + * After S3, the audio driver restores pin:cvt selections + * but this can happen before gfx is ready and such selection + * is overlooked by HW. Thus multiple pins can share a same + * default convertor and mute control will affect each other, + * which can cause a resumed audio playback become silent + * after S3. + */ + pin_cvt_fixup(codec, per_pin, 0); + + /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ + /* Todo: add DP1.2 MST audio support later */ + if (codec_has_acomp(codec)) + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, runtime->rate); + + non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); + mutex_lock(&per_pin->lock); + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; + + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { + stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, + substream); + snd_hda_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_STRIPE_CONTROL, + stripe); + } + + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + mutex_unlock(&per_pin->lock); + if (spec->dyn_pin_out) { + snd_hda_set_dev_select(codec, per_pin->pin_nid, + per_pin->dev_id); + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl | PIN_OUT); + } + + /* snd_hda_set_dev_select() has been called before */ + err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid, + per_pin->dev_id, stream_tag, format); + unlock: + mutex_unlock(&spec->pcm_lock); + return err; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_prepare, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_cleanup, "SND_HDA_CODEC_HDMI"); + +static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int cvt_idx, pin_idx, pcm_idx; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + int pinctl; + int err = 0; + + mutex_lock(&spec->pcm_lock); + if (hinfo->nid) { + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (snd_BUG_ON(pcm_idx < 0)) { + err = -EINVAL; + goto unlock; + } + cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); + if (snd_BUG_ON(cvt_idx < 0)) { + err = -EINVAL; + goto unlock; + } + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = false; + hinfo->nid = 0; + + azx_stream(get_azx_dev(substream))->stripe = 0; + + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + clear_bit(pcm_idx, &spec->pcm_in_use); + pin_idx = hinfo_to_pin_index(codec, hinfo); + /* + * In such a case, return 0 to match the behavior in + * hdmi_pcm_open() + */ + if (pin_idx < 0) + goto unlock; + + per_pin = get_pin(spec, pin_idx); + + if (spec->dyn_pin_out) { + snd_hda_set_dev_select(codec, per_pin->pin_nid, + per_pin->dev_id); + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl & ~PIN_OUT); + } + + mutex_lock(&per_pin->lock); + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); + + per_pin->setup = false; + per_pin->channels = 0; + mutex_unlock(&per_pin->lock); + } + +unlock: + mutex_unlock(&spec->pcm_lock); + + return err; +} + +static const struct hda_pcm_ops generic_ops = { + .open = hdmi_pcm_open, + .close = hdmi_pcm_close, + .prepare = snd_hda_hdmi_generic_pcm_prepare, + .cleanup = snd_hda_hdmi_generic_pcm_cleanup, +}; + +static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + if (!per_pin) + return 0; + + return per_pin->sink_eld.info.spk_alloc; +} + +static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + /* chmap is already set to 0 in caller */ + if (!per_pin) + return; + + memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); +} + +static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + if (!per_pin) + return; + mutex_lock(&per_pin->lock); + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); + if (prepared) + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); +} + +static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + return per_pin ? true:false; +} + +int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int idx, pcm_num; + + /* limit the PCM devices to the codec converters or available PINs */ + pcm_num = min(spec->num_cvts, spec->num_pins); + codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num); + + for (idx = 0; idx < pcm_num; idx++) { + struct hdmi_spec_per_cvt *per_cvt; + struct hda_pcm *info; + struct hda_pcm_stream *pstr; + + info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx); + if (!info) + return -ENOMEM; + + spec->pcm_rec[idx].pcm = info; + spec->pcm_used++; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->own_chmap = true; + + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + pstr->substreams = 1; + pstr->ops = generic_ops; + + per_cvt = get_cvt(spec, 0); + pstr->channels_min = per_cvt->channels_min; + pstr->channels_max = per_cvt->channels_max; + + /* pcm number is less than pcm_rec array size */ + if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec)) + break; + /* other pstr fields are set in open */ + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_pcms, "SND_HDA_CODEC_HDMI"); + +static void free_hdmi_jack_priv(struct snd_jack *jack) +{ + struct hdmi_pcm *pcm = jack->private_data; + + pcm->jack = NULL; +} + +static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) +{ + char hdmi_str[32] = "HDMI/DP"; + struct hdmi_spec *spec = codec->spec; + struct snd_jack *jack; + int pcmdev = get_pcm_rec(spec, pcm_idx)->device; + int err; + + if (pcmdev > 0) + sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); + + err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, + true, false); + if (err < 0) + return err; + + spec->pcm_rec[pcm_idx].jack = jack; + jack->private_data = &spec->pcm_rec[pcm_idx]; + jack->private_free = free_hdmi_jack_priv; + return 0; +} + +int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int dev, err; + int pin_idx, pcm_idx; + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + if (!get_pcm_rec(spec, pcm_idx)->pcm) { + /* no PCM: mark this for skipping permanently */ + set_bit(pcm_idx, &spec->pcm_bitmap); + continue; + } + + err = generic_hdmi_build_jack(codec, pcm_idx); + if (err < 0) + return err; + + /* create the spdif for each pcm + * pin will be bound when monitor is connected + */ + err = snd_hda_create_dig_out_ctls(codec, + 0, spec->cvt_nids[0], + HDA_PCM_TYPE_HDMI); + if (err < 0) + return err; + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + + dev = get_pcm_rec(spec, pcm_idx)->device; + if (dev != SNDRV_PCM_INVALID_DEVICE) { + /* add control for ELD Bytes */ + err = hdmi_create_eld_ctl(codec, pcm_idx, dev); + if (err < 0) + return err; + } + } + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + struct hdmi_eld *pin_eld = &per_pin->sink_eld; + + if (spec->static_pcm_mapping) { + hdmi_attach_hda_pcm(spec, per_pin); + hdmi_pcm_setup_pin(spec, per_pin); + } + + pin_eld->eld_valid = false; + hdmi_present_sense(per_pin, 0); + } + + /* add channel maps */ + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + struct hda_pcm *pcm; + + pcm = get_pcm_rec(spec, pcm_idx); + if (!pcm || !pcm->pcm) + break; + err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_controls, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + per_pin->codec = codec; + mutex_init(&per_pin->lock); + INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); + eld_proc_new(per_pin, pin_idx); + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init_per_pins, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_generic_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx; + + mutex_lock(&spec->bind_lock); + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + hda_nid_t pin_nid = per_pin->pin_nid; + int dev_id = per_pin->dev_id; + + snd_hda_set_dev_select(codec, pin_nid, dev_id); + hdmi_init_pin(codec, pin_nid); + if (codec_has_acomp(codec)) + continue; + snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id, + jack_callback); + } + mutex_unlock(&spec->bind_lock); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init, "SND_HDA_CODEC_HDMI"); + +static void hdmi_array_init(struct hdmi_spec *spec, int nums) +{ + snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); + snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); +} + +static void hdmi_array_free(struct hdmi_spec *spec) +{ + snd_array_free(&spec->pins); + snd_array_free(&spec->cvts); +} + +void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec) { + hdmi_array_free(spec); + kfree(spec); + codec->spec = NULL; + } + codec->dp_mst = false; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_spec_free, "SND_HDA_CODEC_HDMI"); + +void snd_hda_hdmi_generic_remove(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, pcm_idx; + + if (spec->acomp_registered) { + snd_hdac_acomp_exit(&codec->bus->core); + } else if (codec_has_acomp(codec)) { + snd_hdac_acomp_register_notifier(&codec->bus->core, NULL); + } + codec->relaxed_resume = 0; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + cancel_delayed_work_sync(&per_pin->work); + eld_proc_free(per_pin); + } + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + if (spec->pcm_rec[pcm_idx].jack == NULL) + continue; + snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack); + } + + snd_hda_hdmi_generic_spec_free(codec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_remove, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_generic_suspend(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + cancel_delayed_work_sync(&per_pin->work); + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_suspend, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_generic_resume(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx; + + snd_hda_codec_init(codec); + snd_hda_regmap_sync(codec); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + hdmi_present_sense(per_pin, 1); + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_resume, "SND_HDA_CODEC_HDMI"); + +static const struct hdmi_ops generic_standard_hdmi_ops = { + .pin_get_eld = hdmi_pin_get_eld, + .pin_setup_infoframe = hdmi_pin_setup_infoframe, + .pin_hbr_setup = hdmi_pin_hbr_setup, + .setup_stream = snd_hda_hdmi_setup_stream, +}; + +/* allocate codec->spec and assign/initialize generic parser ops */ +int snd_hda_hdmi_generic_alloc(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + spec->codec = codec; + spec->ops = generic_standard_hdmi_ops; + spec->dev_num = 1; /* initialize to 1 */ + mutex_init(&spec->pcm_lock); + mutex_init(&spec->bind_lock); + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc; + + codec->spec = spec; + hdmi_array_init(spec, 4); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_alloc, "SND_HDA_CODEC_HDMI"); + +/* generic HDMI parser */ +int snd_hda_hdmi_generic_probe(struct hda_codec *codec) +{ + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + + err = snd_hda_hdmi_parse_codec(codec); + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + snd_hda_hdmi_generic_init_per_pins(codec); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_probe, "SND_HDA_CODEC_HDMI"); + +/* + * generic audio component binding + */ + +/* turn on / off the unsol event jack detection dynamically */ +static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid, + int dev_id, bool use_acomp) +{ + struct hda_jack_tbl *tbl; + + tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id); + if (tbl) { + /* clear unsol even if component notifier is used, or re-enable + * if notifier is cleared + */ + unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, val); + } +} + +/* set up / clear component notifier dynamically */ +static void generic_acomp_notifier_set(struct drm_audio_component *acomp, + bool use_acomp) +{ + struct hdmi_spec *spec; + int i; + + spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops); + mutex_lock(&spec->bind_lock); + spec->use_acomp_notifier = use_acomp; + spec->codec->relaxed_resume = use_acomp; + spec->codec->bus->keep_power = 0; + /* reprogram each jack detection logic depending on the notifier */ + for (i = 0; i < spec->num_pins; i++) + reprogram_jack_detect(spec->codec, + get_pin(spec, i)->pin_nid, + get_pin(spec, i)->dev_id, + use_acomp); + mutex_unlock(&spec->bind_lock); +} + +/* enable / disable the notifier via master bind / unbind */ +int snd_hda_hdmi_acomp_master_bind(struct device *dev, + struct drm_audio_component *acomp) +{ + generic_acomp_notifier_set(acomp, true); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_bind, "SND_HDA_CODEC_HDMI"); + +void snd_hda_hdmi_acomp_master_unbind(struct device *dev, + struct drm_audio_component *acomp) +{ + generic_acomp_notifier_set(acomp, false); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_unbind, "SND_HDA_CODEC_HDMI"); + +/* check whether both HD-audio and DRM PCI devices belong to the same bus */ +static int match_bound_vga(struct device *dev, int subtype, void *data) +{ + struct hdac_bus *bus = data; + struct pci_dev *pci, *master; + + if (!dev_is_pci(dev) || !dev_is_pci(bus->dev)) + return 0; + master = to_pci_dev(bus->dev); + pci = to_pci_dev(dev); + return master->bus == pci->bus; +} + +/* audio component notifier for AMD/Nvidia HDMI codecs */ +void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) +{ + struct hda_codec *codec = audio_ptr; + struct hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid = spec->port2pin(codec, port); + + if (!pin_nid) + return; + if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN) + return; + /* skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume + */ + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + return; + + snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_pin_eld_notify, "SND_HDA_CODEC_HDMI"); + +/* set up the private drm_audio_ops from the template */ +void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops) +{ + struct hdmi_spec *spec = codec->spec; + + spec->drm_audio_ops.audio_ptr = codec; + /* intel_audio_codec_enable() or intel_audio_codec_disable() + * will call pin_eld_notify with using audio_ptr pointer + * We need make sure audio_ptr is really setup + */ + wmb(); + spec->drm_audio_ops.pin2port = ops->pin2port; + spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify; + spec->drm_audio_ops.master_bind = ops->master_bind; + spec->drm_audio_ops.master_unbind = ops->master_unbind; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_drm_audio_ops, "SND_HDA_CODEC_HDMI"); + +/* initialize the generic HDMI audio component */ +void snd_hda_hdmi_acomp_init(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops, + int (*port2pin)(struct hda_codec *, int)) +{ + struct hdmi_spec *spec = codec->spec; + + if (!enable_acomp) { + codec_info(codec, "audio component disabled by module option\n"); + return; + } + + spec->port2pin = port2pin; + snd_hda_hdmi_setup_drm_audio_ops(codec, ops); + if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, + match_bound_vga, 0)) { + spec->acomp_registered = true; + } +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI"); + +/* + */ + +enum { + MODEL_GENERIC, + MODEL_GF, +}; + +static int generichdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + int err; + + err = snd_hda_hdmi_generic_probe(codec); + if (err < 0) + return err; + /* + * Glenfly GPUs have two codecs, stream switches from one codec to + * another, need to do actual clean-ups in codec_cleanup_stream + */ + if (id->driver_data == MODEL_GF) + codec->no_sticky_stream = 1; + + return 0; +} + +static const struct hda_codec_ops generichdmi_codec_ops = { + .probe = generichdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + +/* + */ +static const struct hda_device_id snd_hda_id_generichdmi[] = { + HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x11069f85, "VX11 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x17e80047, "Chrontel HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x1d179f86, "ZX-100S HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f87, "ZX-100S HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f88, "KX-5000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f89, "KX-5000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8a, "KX-6000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8b, "KX-6000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8c, "KX-6000G HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8d, "KX-6000G HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8e, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8f, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f90, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d82, "Arise 82 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d83, "Arise 83 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d84, "Arise 84 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d85, "Arise 85 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d86, "Arise 86 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d87, "Arise 87 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x80862801, "Bearlake HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862802, "Cantiga HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862803, "Eaglelake HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862880, "CedarTrail HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x808629fb, "Crestline HDMI", MODEL_GENERIC), + /* special ID for generic HDMI */ + HDA_CODEC_ID_MODEL(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", MODEL_GENERIC), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generichdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic HDMI HD-audio codec"); + +static struct hda_codec_driver generichdmi_driver = { + .id = snd_hda_id_generichdmi, + .ops = &generichdmi_codec_ops, +}; + +module_hda_codec_driver(generichdmi_driver); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h new file mode 100644 index 000000000000..548241ad3fa9 --- /dev/null +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD-audio HDMI codec driver + */ + +#ifndef __HDA_HDMI_LOCAL_H +#define __HDA_HDMI_LOCAL_H + +#include +#include +#include +#include +#include +#include +#include "hda_local.h" + +struct hdmi_spec_per_cvt { + hda_nid_t cvt_nid; + bool assigned; /* the stream has been assigned */ + bool silent_stream; /* silent stream activated */ + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + +struct hdmi_spec_per_pin { + hda_nid_t pin_nid; + int dev_id; + /* pin idx, different device entries on the same pin use the same idx */ + int pin_nid_idx; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + int mux_idx; + hda_nid_t cvt_nid; + + struct hda_codec *codec; + struct hdmi_eld sink_eld; + struct mutex lock; + struct delayed_work work; + struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ + int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ + int prev_pcm_idx; /* previously assigned pcm index */ + int repoll_count; + bool setup; /* the stream has been set up by prepare callback */ + bool silent_stream; + int channels; /* current number of channels */ + bool non_pcm; + bool chmap_set; /* channel-map override by ALSA API? */ + unsigned char chmap[8]; /* ALSA API channel-map */ +#ifdef CONFIG_SND_PROC_FS + struct snd_info_entry *proc_entry; +#endif +}; + +/* operations used by generic code that can be overridden by codec drivers */ +struct hdmi_ops { + int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, unsigned char *buf, int *eld_size); + + void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, + int ca, int active_channels, int conn_type); + + /* enable/disable HBR (HD passthrough) */ + int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, bool hbr); + + int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, u32 stream_tag, + int format); + + void (*pin_cvt_fixup)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid); + + void (*silent_stream)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable); +}; + +struct hdmi_pcm { + struct hda_pcm *pcm; + struct snd_jack *jack; + struct snd_kcontrol *eld_ctl; +}; + +enum { + SILENT_STREAM_OFF = 0, + SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ + SILENT_STREAM_I915, /* Intel i915 extension */ +}; + +struct hdmi_spec { + struct hda_codec *codec; + int num_cvts; + struct snd_array cvts; /* struct hdmi_spec_per_cvt */ + hda_nid_t cvt_nids[4]; /* only for haswell fix */ + + /* + * num_pins is the number of virtual pins + * for example, there are 3 pins, and each pin + * has 4 device entries, then the num_pins is 12 + */ + int num_pins; + /* + * num_nids is the number of real pins + * In the above example, num_nids is 3 + */ + int num_nids; + /* + * dev_num is the number of device entries + * on each pin. + * In the above example, dev_num is 4 + */ + int dev_num; + struct snd_array pins; /* struct hdmi_spec_per_pin */ + struct hdmi_pcm pcm_rec[8]; + struct mutex pcm_lock; + struct mutex bind_lock; /* for audio component binding */ + /* pcm_bitmap means which pcms have been assigned to pins*/ + unsigned long pcm_bitmap; + int pcm_used; /* counter of pcm_rec[] */ + /* bitmap shows whether the pcm is opened in user space + * bit 0 means the first playback PCM (PCM3); + * bit 1 means the second playback PCM, and so on. + */ + unsigned long pcm_in_use; + + struct hdmi_eld temp_eld; + struct hdmi_ops ops; + + bool dyn_pin_out; + bool static_pcm_mapping; + /* hdmi interrupt trigger control flag for Nvidia codec */ + bool hdmi_intr_trig_ctrl; + bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */ + + bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ + /* + * Non-generic VIA/NVIDIA specific + */ + struct hda_multi_out multiout; + struct hda_pcm_stream pcm_playback; + + bool use_acomp_notifier; /* use eld_notify callback for hotplug */ + bool acomp_registered; /* audio component registered in this driver */ + bool force_connect; /* force connectivity */ + struct drm_audio_component_audio_ops drm_audio_ops; + int (*port2pin)(struct hda_codec *codec, int port); /* reverse port/pin mapping */ + + struct hdac_chmap chmap; + hda_nid_t vendor_nid; + const int *port_map; + int port_num; + int silent_stream_type; + + const struct snd_pcm_hw_constraint_list *hw_constraints_channels; +}; + +#ifdef CONFIG_SND_HDA_COMPONENT +static inline bool codec_has_acomp(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + return spec->use_acomp_notifier; +} +#else +#define codec_has_acomp(codec) false +#endif + +struct hdmi_audio_infoframe { + u8 type; /* 0x84 */ + u8 ver; /* 0x01 */ + u8 len; /* 0x0a */ + + u8 checksum; + + u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +union audio_infoframe { + struct hdmi_audio_infoframe hdmi; + struct dp_audio_infoframe dp; + DECLARE_FLEX_ARRAY(u8, bytes); +}; + +#ifdef LIMITED_RATE_FMT_SUPPORT +/* support only the safe format and rate */ +#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 +#define SUPPORTED_MAXBPS 16 +#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#else +/* support all rates and formats */ +#define SUPPORTED_RATES \ + (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define SUPPORTED_MAXBPS 24 +#define SUPPORTED_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#endif + +/* + * HDMI routines + */ + +#define get_pin(spec, idx) \ + ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) +#define get_cvt(spec, idx) \ + ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) +/* obtain hdmi_pcm object assigned to idx */ +#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) +/* obtain hda_pcm object assigned to idx */ +#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) + +/* Generic HDMI codec support */ +int snd_hda_hdmi_generic_alloc(struct hda_codec *codec); +int snd_hda_hdmi_parse_codec(struct hda_codec *codec); +int snd_hda_hdmi_generic_probe(struct hda_codec *codec); +void snd_hda_hdmi_generic_remove(struct hda_codec *codec); + +int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec); +int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec); +int snd_hda_hdmi_generic_init(struct hda_codec *codec); +int snd_hda_hdmi_generic_suspend(struct hda_codec *codec); +int snd_hda_hdmi_generic_resume(struct hda_codec *codec); +void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res); + +int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id); +#define pin_id_to_pin_index(codec, pin, dev) \ + snd_hda_hdmi_pin_id_to_pin_index(codec, pin, dev) +int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec); +void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec); +int snd_hda_hdmi_setup_stream(struct hda_codec *codec, + hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, + u32 stream_tag, int format); + +int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream); +int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream); + +void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec, + hda_nid_t nid, int dev_id); +void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm); + +/* Audio component support */ +void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops); +void snd_hda_hdmi_acomp_init(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops, + int (*port2pin)(struct hda_codec *, int)); +void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id); +int snd_hda_hdmi_acomp_master_bind(struct device *dev, + struct drm_audio_component *acomp); +void snd_hda_hdmi_acomp_master_unbind(struct device *dev, + struct drm_audio_component *acomp); + +/* Simple / legacy HDMI codec support */ +int snd_hda_hdmi_simple_probe(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid); +void snd_hda_hdmi_simple_remove(struct hda_codec *codec); + +int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec); +int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec); +int snd_hda_hdmi_simple_init(struct hda_codec *codec); +void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec, + unsigned int res); +int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream); + +#endif /* __HDA_HDMI_LOCAL_H */ diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c new file mode 100644 index 000000000000..23237d527430 --- /dev/null +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -0,0 +1,811 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Intel HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +static bool enable_silent_stream = +IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); +module_param(enable_silent_stream, bool, 0644); +MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); + +enum { + MODEL_HSW, + MODEL_GLK, + MODEL_ICL, + MODEL_TGL, + MODEL_ADLP, + MODEL_BYT, + MODEL_CPT, +}; + +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void intel_haswell_enable_all_pins(struct hda_codec *codec, + bool update_tree) +{ + unsigned int vendor_param; + struct hdmi_spec *spec = codec->spec; + + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + + if (update_tree) + snd_hda_codec_update_widgets(codec); +} + +static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) +{ + unsigned int vendor_param; + struct hdmi_spec *spec = codec->spec; + + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); + snd_hda_codec_write_cache(codec, spec->vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); +} + +/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. + * Otherwise you may get severe h/w communication errors. + */ +static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + /* check codec->spec: it can be called before the probe gets called */ + if (codec->spec) { + if (power_state == AC_PWRST_D0) { + intel_haswell_enable_all_pins(codec, false); + intel_haswell_fixup_enable_dp12(codec); + } + } + + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state); +} + +/* There is a fixed mapping between audio pin node and display port. + * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: + * Pin Widget 5 - PORT B (port = 1 in i915 driver) + * Pin Widget 6 - PORT C (port = 2 in i915 driver) + * Pin Widget 7 - PORT D (port = 3 in i915 driver) + * + * on VLV, ILK: + * Pin Widget 4 - PORT B (port = 1 in i915 driver) + * Pin Widget 5 - PORT C (port = 2 in i915 driver) + * Pin Widget 6 - PORT D (port = 3 in i915 driver) + */ +static int intel_base_nid(struct hda_codec *codec) +{ + switch (codec->core.vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + return 4; + default: + return 5; + } +} + +static int intel_pin2port(void *audio_ptr, int pin_nid) +{ + struct hda_codec *codec = audio_ptr; + struct hdmi_spec *spec = codec->spec; + int base_nid, i; + + if (!spec->port_num) { + base_nid = intel_base_nid(codec); + if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) + return -1; + return pin_nid - base_nid + 1; + } + + /* + * looking for the pin number in the mapping table and return + * the index which indicate the port number + */ + for (i = 0; i < spec->port_num; i++) { + if (pin_nid == spec->port_map[i]) + return i; + } + + codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); + return -1; +} + +static int intel_port2pin(struct hda_codec *codec, int port) +{ + struct hdmi_spec *spec = codec->spec; + + if (!spec->port_num) { + /* we assume only from port-B to port-D */ + if (port < 1 || port > 3) + return 0; + return port + intel_base_nid(codec) - 1; + } + + if (port < 0 || port >= spec->port_num) + return 0; + return spec->port_map[port]; +} + +static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) +{ + struct hda_codec *codec = audio_ptr; + int pin_nid; + int dev_id = pipe; + + pin_nid = intel_port2pin(codec, port); + if (!pin_nid) + return; + /* skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume + */ + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + return; + + snd_hdac_i915_set_bclk(&codec->bus->core); + snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); +} + +static const struct drm_audio_component_audio_ops intel_audio_ops = { + .pin2port = intel_pin2port, + .pin_eld_notify = intel_pin_eld_notify, +}; + +/* register i915 component pin_eld_notify callback */ +static void register_i915_notifier(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + spec->use_acomp_notifier = true; + spec->port2pin = intel_port2pin; + snd_hda_hdmi_setup_drm_audio_ops(codec, &intel_audio_ops); + snd_hdac_acomp_register_notifier(&codec->bus->core, + &spec->drm_audio_ops); + /* no need for forcible resume for jack check thanks to notifier */ + codec->relaxed_resume = 1; +} + +#define I915_SILENT_RATE 48000 +#define I915_SILENT_CHANNELS 2 +#define I915_SILENT_FORMAT_BITS 16 +#define I915_SILENT_FMT_MASK 0xf + +static void silent_stream_enable_i915(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + unsigned int format; + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, + I915_SILENT_RATE); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); +} + +static void silent_stream_set_kae(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + unsigned int param; + + codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + param = (param >> 16) & 0xff; + + if (enable) + param |= AC_DIG3_KAE; + else + param &= ~AC_DIG3_KAE; + + snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); +} + +static void i915_set_silent_stream(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + struct hdmi_spec *spec = codec->spec; + + switch (spec->silent_stream_type) { + case SILENT_STREAM_KAE: + if (enable) { + silent_stream_enable_i915(codec, per_pin); + silent_stream_set_kae(codec, per_pin, true); + } else { + silent_stream_set_kae(codec, per_pin, false); + } + break; + case SILENT_STREAM_I915: + if (enable) { + silent_stream_enable_i915(codec, per_pin); + snd_hda_power_up_pm(codec); + } else { + /* release ref taken in silent_stream_enable() */ + snd_hda_power_down_pm(codec); + } + break; + default: + break; + } +} + +static void haswell_verify_D0(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t nid) +{ + int pwr; + + /* For Haswell, the converter 1/2 may keep in D3 state after bootup, + * thus pins could only choose converter 0 for use. Make sure the + * converters are in correct power state + */ + if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0)) + snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + msleep(40); + pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; + codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); + } +} + +/* Assure the pin select the right convetor */ +static void intel_verify_pin_cvt_connect(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + hda_nid_t pin_nid = per_pin->pin_nid; + int mux_idx, curr; + + mux_idx = per_pin->mux_idx; + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (curr != mux_idx) + snd_hda_codec_write_cache(codec, pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); +} + +/* get the mux index for the converter of the pins + * converter's mux index is the same for all pins on Intel platform + */ +static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, + hda_nid_t cvt_nid) +{ + int i; + + for (i = 0; i < spec->num_cvts; i++) + if (spec->cvt_nids[i] == cvt_nid) + return i; + return -EINVAL; +} + +/* Intel HDMI workaround to fix audio routing issue: + * For some Intel display codecs, pins share the same connection list. + * So a conveter can be selected by multiple pins and playback on any of these + * pins will generate sound on the external display, because audio flows from + * the same converter to the display pipeline. Also muting one pin may make + * other pins have no sound output. + * So this function assures that an assigned converter for a pin is not selected + * by any other pins. + */ +static void intel_not_share_assigned_cvt(struct hda_codec *codec, + hda_nid_t pin_nid, + int dev_id, int mux_idx) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t nid; + int cvt_idx, curr; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + int pin_idx; + + /* configure the pins connections */ + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + int dev_id_saved; + int dev_num; + + per_pin = get_pin(spec, pin_idx); + /* + * pin not connected to monitor + * no need to operate on it + */ + if (!per_pin->pcm) + continue; + + if ((per_pin->pin_nid == pin_nid) && + (per_pin->dev_id == dev_id)) + continue; + + /* + * if per_pin->dev_id >= dev_num, + * snd_hda_get_dev_select() will fail, + * and the following operation is unpredictable. + * So skip this situation. + */ + dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; + if (per_pin->dev_id >= dev_num) + continue; + + nid = per_pin->pin_nid; + + /* + * Calling this function should not impact + * on the device entry selection + * So let's save the dev id for each pin, + * and restore it when return + */ + dev_id_saved = snd_hda_get_dev_select(codec, nid); + snd_hda_set_dev_select(codec, nid, per_pin->dev_id); + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (curr != mux_idx) { + snd_hda_set_dev_select(codec, nid, dev_id_saved); + continue; + } + + + /* choose an unassigned converter. The conveters in the + * connection list are in the same order as in the codec. + */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + if (!per_cvt->assigned) { + codec_dbg(codec, + "choose cvt %d for pin NID 0x%x\n", + cvt_idx, nid); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + cvt_idx); + break; + } + } + snd_hda_set_dev_select(codec, nid, dev_id_saved); + } +} + +/* A wrapper of intel_not_share_asigned_cvt() */ +static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) +{ + int mux_idx; + struct hdmi_spec *spec = codec->spec; + + /* On Intel platform, the mapping of converter nid to + * mux index of the pins are always the same. + * The pin nid may be 0, this means all pins will not + * share the converter. + */ + mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); + if (mux_idx >= 0) + intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); +} + +/* setup_stream ops override for HSW+ */ +static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, u32 stream_tag, + int format) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); + struct hdmi_spec_per_pin *per_pin; + int res; + + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); + + haswell_verify_D0(codec, cvt_nid, pin_nid); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } + + res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + usleep_range(100, 200); + silent_stream_set_kae(codec, per_pin, true); + } + + return res; +} + +/* pin_cvt_fixup ops override for HSW+ and VLV+ */ +static void i915_pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + if (per_pin) { + haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid); + snd_hda_set_dev_select(codec, per_pin->pin_nid, + per_pin->dev_id); + intel_verify_pin_cvt_connect(codec, per_pin); + intel_not_share_assigned_cvt(codec, per_pin->pin_nid, + per_pin->dev_id, per_pin->mux_idx); + } else { + intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid); + } +} + +static int i915_hdmi_suspend(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + bool silent_streams = false; + int pin_idx, res; + + res = snd_hda_hdmi_generic_suspend(codec); + if (spec->silent_stream_type != SILENT_STREAM_KAE) + return res; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + if (per_pin->silent_stream) { + silent_streams = true; + break; + } + } + + if (silent_streams) { + /* + * stream-id should remain programmed when codec goes + * to runtime suspend + */ + codec->no_stream_clean_at_suspend = 1; + + /* + * the system might go to S3, in which case keep-alive + * must be reprogrammed upon resume + */ + codec->forced_resume = 1; + + codec_dbg(codec, "HDMI: KAE active at suspend\n"); + } else { + codec->no_stream_clean_at_suspend = 0; + codec->forced_resume = 0; + } + + return res; +} + +static int i915_hdmi_resume(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, res; + + res = snd_hda_hdmi_generic_resume(codec); + if (spec->silent_stream_type != SILENT_STREAM_KAE) + return res; + + /* KAE not programmed at suspend, nothing to do here */ + if (!codec->no_stream_clean_at_suspend) + return res; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* + * If system was in suspend with monitor connected, + * the codec setting may have been lost. Re-enable + * keep-alive. + */ + if (per_pin->silent_stream) { + unsigned int param; + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_CONV, 0); + if (!param) { + codec_dbg(codec, "HDMI: KAE: restore stream id\n"); + silent_stream_enable_i915(codec, per_pin); + } + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + if (!(param & (AC_DIG3_KAE << 16))) { + codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); + silent_stream_set_kae(codec, per_pin, true); + } + } + } + + return res; +} + +/* precondition and allocation for Intel codecs */ +static int alloc_intel_hdmi(struct hda_codec *codec) +{ + /* requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + /* set probe_id here to prevent generic fallback binding */ + codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; + return -ENODEV; + } + + return snd_hda_hdmi_generic_alloc(codec); +} + +/* parse and post-process for Intel codecs */ +static int parse_intel_hdmi(struct hda_codec *codec) +{ + int err, retries = 3; + + do { + err = snd_hda_hdmi_parse_codec(codec); + } while (err < 0 && retries--); + + if (err < 0) + return err; + + snd_hda_hdmi_generic_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel Haswell and onwards; audio component with eld notifier */ +static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, + const int *port_map, int port_num, int dev_num, + bool send_silent_stream) +{ + struct hdmi_spec *spec; + + spec = codec->spec; + codec->dp_mst = true; + spec->vendor_nid = vendor_nid; + spec->port_map = port_map; + spec->port_num = port_num; + spec->intel_hsw_fixup = true; + spec->dev_num = dev_num; + + intel_haswell_enable_all_pins(codec, true); + intel_haswell_fixup_enable_dp12(codec); + + codec->display_power_control = 1; + + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + spec->ops.setup_stream = i915_hsw_setup_stream; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + spec->ops.silent_stream = i915_set_silent_stream; + + /* + * Enable silent stream feature, if it is enabled via + * module param or Kconfig option + */ + if (send_silent_stream) + spec->silent_stream_type = SILENT_STREAM_I915; + + return parse_intel_hdmi(codec); +} + +static int probe_i915_hsw_hdmi(struct hda_codec *codec) +{ + return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, + enable_silent_stream); +} + +static int probe_i915_glk_hdmi(struct hda_codec *codec) +{ + /* + * Silent stream calls audio component .get_power() from + * .pin_eld_notify(). On GLK this will deadlock in i915 due + * to the audio vs. CDCLK workaround. + */ + return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); +} + +static int probe_i915_icl_hdmi(struct hda_codec *codec) +{ + /* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number. + */ + static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; + + return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, + enable_silent_stream); +} + +static int probe_i915_tgl_hdmi(struct hda_codec *codec) +{ + /* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number. + */ + static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + + return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, + enable_silent_stream); +} + +static int probe_i915_adlp_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int res; + + res = probe_i915_tgl_hdmi(codec); + if (!res) { + spec = codec->spec; + + if (spec->silent_stream_type) + spec->silent_stream_type = SILENT_STREAM_KAE; + } + + return res; +} + +/* Intel Baytrail and Braswell; with eld notifier */ +static int probe_i915_byt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = codec->spec; + + /* For Valleyview/Cherryview, only the display codec is in the display + * power well and can use link_power ops to request/release the power. + */ + codec->display_power_control = 1; + + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + + return parse_intel_hdmi(codec); +} + +/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ +static int probe_i915_cpt_hdmi(struct hda_codec *codec) +{ + return parse_intel_hdmi(codec); +} + +/* + * common driver probe + */ +static int intelhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + int err; + + err = alloc_intel_hdmi(codec); + if (err < 0) + return err; + + switch (id->driver_data) { + case MODEL_HSW: + err = probe_i915_hsw_hdmi(codec); + break; + case MODEL_GLK: + err = probe_i915_glk_hdmi(codec); + break; + case MODEL_ICL: + err = probe_i915_icl_hdmi(codec); + break; + case MODEL_TGL: + err = probe_i915_tgl_hdmi(codec); + break; + case MODEL_ADLP: + err = probe_i915_adlp_hdmi(codec); + break; + case MODEL_BYT: + err = probe_i915_byt_hdmi(codec); + break; + case MODEL_CPT: + err = probe_i915_cpt_hdmi(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops intelhdmi_codec_ops = { + .probe = intelhdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = i915_hdmi_suspend, + .resume = i915_hdmi_resume, + .set_power_state = haswell_set_power_state, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_intelhdmi[] = { + HDA_CODEC_ID_MODEL(0x80860054, "IbexPeak HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862800, "Geminilake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x80862804, "IbexPeak HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862805, "CougarPoint HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862806, "PantherPoint HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862807, "Haswell HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x80862808, "Broadwell HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x80862809, "Skylake HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280a, "Broxton HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280b, "Kabylake HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280c, "Cannonlake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x8086280d, "Geminilake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x8086280f, "Icelake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x80862812, "Tigerlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862814, "DG1 HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862815, "Alderlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862816, "Rocketlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862818, "Raptorlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862819, "DG2 HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x8086281a, "Jasperlake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x8086281b, "Elkhartlake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x8086281c, "Alderlake-P HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281d, "Meteor Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281e, "Battlemage HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281f, "Raptor Lake P HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x80862820, "Lunar Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x80862822, "Panther Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x80862823, "Wildcat Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x80862882, "Valleyview2 HDMI", MODEL_BYT), + HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_intelhdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver intelhdmi_driver = { + .id = snd_hda_id_intelhdmi, + .ops = &intelhdmi_codec_ops, +}; + +module_hda_codec_driver(intelhdmi_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c new file mode 100644 index 000000000000..fbcea6d1850e --- /dev/null +++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Legacy Nvidia HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +enum { MODEL_2CH, MODEL_8CH }; + +#define Nv_VERB_SET_Channel_Allocation 0xF79 +#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A +#define Nv_VERB_SET_Audio_Protection_On 0xF98 +#define Nv_VERB_SET_Audio_Protection_Off 0xF99 + +#define nvhdmi_master_con_nid_7x 0x04 +#define nvhdmi_master_pin_nid_7x 0x05 + +static const hda_nid_t nvhdmi_con_nids_7x[4] = { + /*front, rear, clfe, rear_surr */ + 0x6, 0x8, 0xa, 0xc, +}; + +static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, + /* enable digital output on pin widget */ + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + {} /* terminator */ +}; + +static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, + /* enable digital output on pin widget */ + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + {} /* terminator */ +}; + +static int nvhdmi_mcp_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->multiout.max_channels == 2) + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); + else + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); + return 0; +} + +static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, + int channels) +{ + unsigned int chanmask; + int chan = channels ? (channels - 1) : 1; + + switch (channels) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + + /* Set the audio infoframe channel allocation and checksum fields. The + * channel count is computed implicitly by the hardware. + */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); +} + +static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int i; + + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, + 0, AC_VERB_SET_CHANNEL_STREAMID, 0); + for (i = 0; i < 4; i++) { + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_STREAM_FORMAT, 0); + } + + /* The audio hardware sends a channel count of 0x7 (8ch) when all the + * streams are disabled. + */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int chs; + unsigned int dataDCC2, channel_id; + int i; + struct hdmi_spec *spec = codec->spec; + struct hda_spdif_out *spdif; + struct hdmi_spec_per_cvt *per_cvt; + + mutex_lock(&codec->spdif_mutex); + per_cvt = get_cvt(spec, 0); + spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); + + chs = substream->runtime->channels; + + dataDCC2 = 0x2; + + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); + + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); + + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_STREAM_FORMAT, format); + + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + + for (i = 0; i < 4; i++) { + if (chs == 2) + channel_id = 0; + else + channel_id = i * 2; + + /* turn off SPDIF once; + *otherwise the IEC958 bits won't be updated + */ + if (codec->spdif_status_reset && + (spdif->ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); + /* set the stream id */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_CHANNEL_STREAMID, + (stream_tag << 4) | channel_id); + /* set the stream format */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_STREAM_FORMAT, + format); + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && + (spdif->ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + } + + nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); + + mutex_unlock(&codec->spdif_mutex); + return 0; +} + +static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = nvhdmi_master_con_nid_7x, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, + .ops = { + .open = snd_hda_hdmi_simple_pcm_open, + .close = nvhdmi_8ch_7x_pcm_close, + .prepare = nvhdmi_8ch_7x_pcm_prepare + }, +}; + +static int nvhdmi_mcp_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err; + + err = snd_hda_hdmi_simple_build_pcms(codec); + if (!err && spec->multiout.max_channels == 8) { + struct hda_pcm *info = get_pcm_rec(spec, 0); + + info->own_chmap = true; + } + return err; +} + +static int nvhdmi_mcp_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info; + struct snd_pcm_chmap *chmap; + int err; + + err = snd_hda_hdmi_simple_build_controls(codec); + if (err < 0) + return err; + + if (spec->multiout.max_channels != 8) + return 0; + + /* add channel maps */ + info = get_pcm_rec(spec, 0); + err = snd_pcm_add_chmap_ctls(info->pcm, + SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 8, 0, &chmap); + if (err < 0) + return err; + switch (codec->preset->vendor_id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + chmap->channel_mask = (1U << 2) | (1U << 8); + break; + case 0x10de0007: + chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); + } + return 0; +} + +static const unsigned int channels_2_6_8[] = { + 2, 6, 8 +}; + +static const unsigned int channels_2_8[] = { + 2, 8 +}; + +static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { + .count = ARRAY_SIZE(channels_2_6_8), + .list = channels_2_6_8, + .mask = 0, +}; + +static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { + .count = ARRAY_SIZE(channels_2_8), + .list = channels_2_8, + .mask = 0, +}; + +static int nvhdmi_mcp_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x, + nvhdmi_master_pin_nid_7x); + if (err < 0) + return err; + + /* override the PCM rates, etc, as the codec doesn't give full list */ + spec = codec->spec; + spec->pcm_playback.rates = SUPPORTED_RATES; + spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; + spec->pcm_playback.formats = SUPPORTED_FORMATS; + spec->nv_dp_workaround = true; + + if (id->driver_data == MODEL_2CH) + return 0; + + spec->multiout.max_channels = 8; + spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; + + switch (codec->preset->vendor_id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + spec->hw_constraints_channels = &hw_constraints_2_8_channels; + break; + case 0x10de0007: + spec->hw_constraints_channels = &hw_constraints_2_6_8_channels; + break; + default: + break; + } + + /* Initialize the audio infoframe channel mask and checksum to something + * valid + */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + + return 0; +} + +static const struct hda_codec_ops nvhdmi_mcp_codec_ops = { + .probe = nvhdmi_mcp_probe, + .remove = snd_hda_hdmi_simple_remove, + .build_controls = nvhdmi_mcp_build_pcms, + .build_pcms = nvhdmi_mcp_build_controls, + .init = nvhdmi_mcp_init, + .unsol_event = snd_hda_hdmi_simple_unsol_event, +}; + +static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = { + HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver nvhdmi_mcp_driver = { + .id = snd_hda_id_nvhdmi_mcp, + .ops = &nvhdmi_mcp_codec_ops, +}; + +module_hda_codec_driver(nvhdmi_mcp_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi.c b/sound/hda/codecs/hdmi/nvhdmi.c new file mode 100644 index 000000000000..94671ad24b5e --- /dev/null +++ b/sound/hda/codecs/hdmi/nvhdmi.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nvidia HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +enum { + MODEL_GENERIC, + MODEL_LEGACY, +}; + +/* + * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: + * - 0x10de0015 + * - 0x10de0040 + */ +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + if (cap->ca_index == 0x00 && channels == 2) + return SNDRV_CTL_TLVT_CHMAP_FIXED; + + /* If the speaker allocation matches the channel count, it is OK. */ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + return 0; +} + +/* map from pin NID to port; port is 0-based */ +/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */ +static int nvhdmi_pin2port(void *audio_ptr, int pin_nid) +{ + return pin_nid - 4; +} + +/* reverse-map from port to pin NID: see above */ +static int nvhdmi_port2pin(struct hda_codec *codec, int port) +{ + return port + 4; +} + +static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { + .pin2port = nvhdmi_pin2port, + .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify, + .master_bind = snd_hda_hdmi_acomp_master_bind, + .master_unbind = snd_hda_hdmi_acomp_master_unbind, +}; + +static int probe_generic(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + codec->dp_mst = true; + + spec = codec->spec; + + err = snd_hda_hdmi_parse_codec(codec); + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + snd_hda_hdmi_generic_init_per_pins(codec); + + spec->dyn_pin_out = true; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + codec->link_down_at_suspend = 1; + + snd_hda_hdmi_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin); + + return 0; +} + +static int probe_legacy(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_generic_probe(codec); + if (err) + return err; + + spec = codec->spec; + spec->dyn_pin_out = true; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + codec->link_down_at_suspend = 1; + + return 0; +} + +static int nvhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + if (id->driver_data == MODEL_LEGACY) + return probe_legacy(codec); + else + return probe_generic(codec); +} + +static const struct hda_codec_ops nvhdmi_codec_ops = { + .probe = nvhdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + +static const struct hda_device_id snd_hda_id_nvhdmi[] = { + HDA_CODEC_ID_MODEL(0x10de0008, "GPU 08 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0009, "GPU 09 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000a, "GPU 0a HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000b, "GPU 0b HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000c, "MCP89 HDMI", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000d, "GPU 0d HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0010, "GPU 10 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0011, "GPU 11 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0012, "GPU 12 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0013, "GPU 13 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0014, "GPU 14 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0015, "GPU 15 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0016, "GPU 16 HDMI/DP", MODEL_LEGACY), + /* 17 is known to be absent */ + HDA_CODEC_ID_MODEL(0x10de0018, "GPU 18 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0019, "GPU 19 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001a, "GPU 1a HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001b, "GPU 1b HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001c, "GPU 1c HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0040, "GPU 40 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0041, "GPU 41 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0042, "GPU 42 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0043, "GPU 43 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0044, "GPU 44 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0045, "GPU 45 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0050, "GPU 50 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0051, "GPU 51 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0052, "GPU 52 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0060, "GPU 60 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0061, "GPU 61 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0062, "GPU 62 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0070, "GPU 70 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0071, "GPU 71 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0072, "GPU 72 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0073, "GPU 73 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0074, "GPU 74 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0076, "GPU 76 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007b, "GPU 7b HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007c, "GPU 7c HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007d, "GPU 7d HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007e, "GPU 7e HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0080, "GPU 80 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0081, "GPU 81 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0082, "GPU 82 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0083, "GPU 83 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0084, "GPU 84 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0090, "GPU 90 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0091, "GPU 91 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0092, "GPU 92 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0093, "GPU 93 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0094, "GPU 94 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0095, "GPU 95 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0097, "GPU 97 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0098, "GPU 98 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0099, "GPU 99 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009a, "GPU 9a HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009b, "GPU 9b HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009c, "GPU 9c HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009d, "GPU 9d HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009e, "GPU 9e HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009f, "GPU 9f HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a0, "GPU a0 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a1, "GPU a1 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a3, "GPU a3 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a4, "GPU a4 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a5, "GPU a5 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a6, "GPU a6 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a7, "GPU a7 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a8, "GPU a8 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a9, "GPU a9 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00aa, "GPU aa HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00ab, "GPU ab HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00ad, "GPU ad HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00ae, "GPU ae HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00af, "GPU af HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00b0, "GPU b0 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00b1, "GPU b1 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00c0, "GPU c0 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00c1, "GPU c1 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00c3, "GPU c3 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00c4, "GPU c4 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00c5, "GPU c5 HDMI/DP", MODEL_GENERIC), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver nvhdmi_driver = { + .id = snd_hda_id_nvhdmi, + .ops = &nvhdmi_codec_ops, +}; + +module_hda_codec_driver(nvhdmi_driver); diff --git a/sound/hda/codecs/hdmi/simplehdmi.c b/sound/hda/codecs/hdmi/simplehdmi.c new file mode 100644 index 000000000000..193c8dc882af --- /dev/null +++ b/sound/hda/codecs/hdmi/simplehdmi.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Non-generic simple HDMI codec support + */ + +#include +#include +#include "hdmi_local.h" +#include "hda_jack.h" + +int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info; + unsigned int chans; + struct hda_pcm_stream *pstr; + struct hdmi_spec_per_cvt *per_cvt; + + per_cvt = get_cvt(spec, 0); + chans = get_wcaps(codec, per_cvt->cvt_nid); + chans = get_wcaps_channels(chans); + + info = snd_hda_codec_pcm_new(codec, "HDMI 0"); + if (!info) + return -ENOMEM; + spec->pcm_rec[0].pcm = info; + info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + *pstr = spec->pcm_playback; + pstr->nid = per_cvt->cvt_nid; + if (pstr->channels_max <= 2 && chans && chans <= 16) + pstr->channels_max = chans; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_pcms, "SND_HDA_CODEC_HDMI"); + +/* unsolicited event for jack sensing */ +void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_report_sync(codec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_unsol_event, "SND_HDA_CODEC_HDMI"); + +static void free_hdmi_jack_priv(struct snd_jack *jack) +{ + struct hdmi_pcm *pcm = jack->private_data; + + pcm->jack = NULL; +} + +static int simple_hdmi_build_jack(struct hda_codec *codec) +{ + char hdmi_str[32] = "HDMI/DP"; + struct hdmi_spec *spec = codec->spec; + struct snd_jack *jack; + struct hdmi_pcm *pcmp = get_hdmi_pcm(spec, 0); + int pcmdev = pcmp->pcm->device; + int err; + + if (pcmdev > 0) + sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); + + err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, + true, false); + if (err < 0) + return err; + + pcmp->jack = jack; + jack->private_data = pcmp; + jack->private_free = free_hdmi_jack_priv; + return 0; +} + +int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int err; + + per_cvt = get_cvt(spec, 0); + err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid, + per_cvt->cvt_nid, + HDA_PCM_TYPE_HDMI); + if (err < 0) + return err; + return simple_hdmi_build_jack(codec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_controls, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_simple_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); + hda_nid_t pin = per_pin->pin_nid; + + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* some codecs require to unmute the pin */ + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_init, "SND_HDA_CODEC_HDMI"); + +void snd_hda_hdmi_simple_remove(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + snd_array_free(&spec->pins); + snd_array_free(&spec->cvts); + kfree(spec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_remove, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->hw_constraints_channels) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + spec->hw_constraints_channels); + } else { + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + } + + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_pcm_open, "SND_HDA_CODEC_HDMI"); + +static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static const struct hda_pcm_stream simple_pcm_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = snd_hda_hdmi_simple_pcm_open, + .close = simple_playback_pcm_close, + .prepare = simple_playback_pcm_prepare + }, +}; + +int snd_hda_hdmi_simple_probe(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + spec->codec = codec; + codec->spec = spec; + snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), 1); + snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), 1); + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = cvt_nid; + spec->num_cvts = 1; + spec->num_pins = 1; + per_pin = snd_array_new(&spec->pins); + per_cvt = snd_array_new(&spec->cvts); + if (!per_pin || !per_cvt) { + snd_hda_hdmi_simple_remove(codec); + return -ENOMEM; + } + per_cvt->cvt_nid = cvt_nid; + per_pin->pin_nid = pin_nid; + spec->pcm_playback = simple_pcm_playback; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_probe, "SND_HDA_CODEC_HDMI"); + +/* + * driver entries + */ + +enum { MODEL_VIA }; + +/* VIA HDMI Implementation */ +#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ +#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ + +static int simplehdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + switch (id->driver_data) { + case MODEL_VIA: + return snd_hda_hdmi_simple_probe(codec, VIAHDMI_CVT_NID, + VIAHDMI_PIN_NID); + default: + return -EINVAL; + } +} + +static const struct hda_codec_ops simplehdmi_codec_ops = { + .probe = simplehdmi_probe, + .remove = snd_hda_hdmi_simple_remove, + .build_controls = snd_hda_hdmi_simple_build_controls, + .build_pcms = snd_hda_hdmi_simple_build_pcms, + .init = snd_hda_hdmi_simple_init, + .unsol_event = snd_hda_hdmi_simple_unsol_event, +}; + +static const struct hda_device_id snd_hda_id_simplehdmi[] = { + HDA_CODEC_ID_MODEL(0x11069f80, "VX900 HDMI/DP", MODEL_VIA), + HDA_CODEC_ID_MODEL(0x11069f81, "VX900 HDMI/DP", MODEL_VIA), + {} /* terminator */ +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple HDMI HD-audio codec support"); + +static struct hda_codec_driver simplehdmi_driver = { + .id = snd_hda_id_simplehdmi, + .ops = &simplehdmi_codec_ops, +}; + +module_hda_codec_driver(simplehdmi_driver); diff --git a/sound/hda/codecs/hdmi/tegrahdmi.c b/sound/hda/codecs/hdmi/tegrahdmi.c new file mode 100644 index 000000000000..5f6fe31aa202 --- /dev/null +++ b/sound/hda/codecs/hdmi/tegrahdmi.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nvidia Tegra HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +enum { + MODEL_TEGRA, + MODEL_TEGRA234, +}; + +/* + * The HDA codec on NVIDIA Tegra contains two scratch registers that are + * accessed using vendor-defined verbs. These registers can be used for + * interoperability between the HDA and HDMI drivers. + */ + +/* Audio Function Group node */ +#define NVIDIA_AFG_NID 0x01 + +/* + * The SCRATCH0 register is used to notify the HDMI codec of changes in audio + * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to + * be raised in the HDMI codec. The remainder of the bits is arbitrary. This + * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an + * additional bit (at position 30) to signal the validity of the format. + * + * | 31 | 30 | 29 16 | 15 0 | + * +---------+-------+--------+--------+ + * | TRIGGER | VALID | UNUSED | FORMAT | + * +-----------------------------------| + * + * Note that for the trigger bit to take effect it needs to change value + * (i.e. it needs to be toggled). The trigger bit is not applicable from + * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt + * trigger to hdmi. + */ +#define NVIDIA_SET_HOST_INTR 0xf80 +#define NVIDIA_GET_SCRATCH0 0xfa6 +#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 +#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 +#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 +#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa +#define NVIDIA_SCRATCH_TRIGGER (1 << 7) +#define NVIDIA_SCRATCH_VALID (1 << 6) + +#define NVIDIA_GET_SCRATCH1 0xfab +#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac +#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad +#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae +#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf + +/* + * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, + * the format is invalidated so that the HDMI codec can be disabled. + */ +static void tegra_hdmi_set_format(struct hda_codec *codec, + hda_nid_t cvt_nid, + unsigned int format) +{ + unsigned int value; + unsigned int nid = NVIDIA_AFG_NID; + struct hdmi_spec *spec = codec->spec; + + /* + * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. + * This resulted in moving scratch registers from audio function + * group to converter widget context. So CVT NID should be used for + * scratch register read/write for DP MST supported Tegra HDA codec. + */ + if (codec->dp_mst) + nid = cvt_nid; + + /* bits [31:30] contain the trigger and valid bits */ + value = snd_hda_codec_read(codec, nid, 0, + NVIDIA_GET_SCRATCH0, 0); + value = (value >> 24) & 0xff; + + /* bits [15:0] are used to store the HDA format */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE0, + (format >> 0) & 0xff); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE1, + (format >> 8) & 0xff); + + /* bits [16:24] are unused */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE2, 0); + + /* + * Bit 30 signals that the data is valid and hence that HDMI audio can + * be enabled. + */ + if (format == 0) + value &= ~NVIDIA_SCRATCH_VALID; + else + value |= NVIDIA_SCRATCH_VALID; + + if (spec->hdmi_intr_trig_ctrl) { + /* + * For Tegra HDA Codec design from TEGRA234 onwards, the + * Interrupt to hdmi driver is triggered by writing + * non-zero values to verb 0xF80 instead of 31st bit of + * scratch register. + */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_HOST_INTR, 0x1); + } else { + /* + * Whenever the 31st trigger bit is toggled, an interrupt is raised + * in the HDMI codec. The HDMI driver will use that as trigger + * to update its configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER; + + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + } +} + +static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int err; + + err = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag, + format, substream); + if (err < 0) + return err; + + /* notify the HDMI codec of the format change */ + tegra_hdmi_set_format(codec, hinfo->nid, format); + + return 0; +} + +static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + /* invalidate the format in the HDMI codec */ + tegra_hdmi_set_format(codec, hinfo->nid, 0); + + return snd_hda_hdmi_generic_pcm_cleanup(hinfo, codec, substream); +} + +static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < spec->num_pins; i++) { + struct hda_pcm *pcm = get_pcm_rec(spec, i); + + if (pcm->pcm_type == type) + return pcm; + } + + return NULL; +} + +static int tegra_hdmi_build_pcms(struct hda_codec *codec) +{ + struct hda_pcm_stream *stream; + struct hda_pcm *pcm; + int err; + + err = snd_hda_hdmi_generic_build_pcms(codec); + if (err < 0) + return err; + + pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); + if (!pcm) + return -ENODEV; + + /* + * Override ->prepare() and ->cleanup() operations to notify the HDMI + * codec about format changes. + */ + stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + stream->ops.prepare = tegra_hdmi_pcm_prepare; + stream->ops.cleanup = tegra_hdmi_pcm_cleanup; + + return 0; +} + +/* + * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: + * - 0x10de0015 + * - 0x10de0040 + */ +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + if (cap->ca_index == 0x00 && channels == 2) + return SNDRV_CTL_TLVT_CHMAP_FIXED; + + /* If the speaker allocation matches the channel count, it is OK. */ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + return 0; +} + +static int tegra_hdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int i, err; + + err = snd_hda_hdmi_parse_codec(codec); + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + for (i = 0; i < spec->num_cvts; i++) + snd_hda_codec_write(codec, spec->cvt_nids[i], 0, + AC_VERB_SET_DIGI_CONVERT_1, + AC_DIG1_ENABLE); + + snd_hda_hdmi_generic_init_per_pins(codec); + + codec->depop_delay = 10; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + return 0; +} + +static int tegrahdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + + if (id->driver_data == MODEL_TEGRA234) { + codec->dp_mst = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->hdmi_intr_trig_ctrl = true; + } + + return tegra_hdmi_init(codec); +} + +static const struct hda_codec_ops tegrahdmi_codec_ops = { + .probe = tegrahdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = tegra_hdmi_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + +static const struct hda_device_id snd_hda_id_tegrahdmi[] = { + HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP", MODEL_TEGRA234), + HDA_CODEC_ID_MODEL(0x10de0033, "SoC 33 HDMI/DP", MODEL_TEGRA234), + HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP", MODEL_TEGRA234), + HDA_CODEC_ID_MODEL(0x10de0035, "SoC 35 HDMI/DP", MODEL_TEGRA234), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver tegrahdmi_driver = { + .id = snd_hda_id_tegrahdmi, + .ops = &tegrahdmi_codec_ops, +}; + +module_hda_codec_driver(tegrahdmi_driver); diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/hda/codecs/helpers/hp_x360.c similarity index 100% rename from sound/pci/hda/hp_x360_helper.c rename to sound/hda/codecs/helpers/hp_x360.c diff --git a/sound/pci/hda/ideapad_hotkey_led_helper.c b/sound/hda/codecs/helpers/ideapad_hotkey_led.c similarity index 100% rename from sound/pci/hda/ideapad_hotkey_led_helper.c rename to sound/hda/codecs/helpers/ideapad_hotkey_led.c diff --git a/sound/pci/hda/ideapad_s740_helper.c b/sound/hda/codecs/helpers/ideapad_s740.c similarity index 100% rename from sound/pci/hda/ideapad_s740_helper.c rename to sound/hda/codecs/helpers/ideapad_s740.c diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/hda/codecs/helpers/thinkpad.c similarity index 100% rename from sound/pci/hda/thinkpad_helper.c rename to sound/hda/codecs/helpers/thinkpad.c diff --git a/sound/hda/codecs/realtek/Kconfig b/sound/hda/codecs/realtek/Kconfig new file mode 100644 index 000000000000..cdc6d9509a01 --- /dev/null +++ b/sound/hda/codecs/realtek/Kconfig @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig SND_HDA_CODEC_REALTEK + tristate "Realtek HD-audio codec support" + help + Say Y or M here to include Realtek HD-audio codec support. + + This will enable all Realtek HD-audio codec drivers as default, + but you can enable/disable each codec driver individually, too + (only when CONFIG_EXPERT is set). + +if SND_HDA_CODEC_REALTEK + +config SND_HDA_CODEC_REALTEK_LIB + tristate + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + select SND_HDA_SCODEC_COMPONENT + +config SND_HDA_CODEC_ALC260 + tristate "Build Realtek ALC260 HD-audio codec support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC260 HD-audio codec support + +config SND_HDA_CODEC_ALC262 + tristate "Build Realtek ALC262 HD-audio codec support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC262 HD-audio codec support + +config SND_HDA_CODEC_ALC268 + tristate "Build Realtek ALC268 HD-audio codec support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC268 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC269 + tristate "Build Realtek ALC269 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC269 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC662 + tristate "Build Realtek ALC662 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC662 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC680 + tristate "Build Realtek ALC680 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC680 HD-audio codec support + +config SND_HDA_CODEC_ALC861 + tristate "Build Realtek ALC861 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC861 HD-audio codec support + +config SND_HDA_CODEC_ALC861VD + tristate "Build Realtek ALC861-VD HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC861-VD HD-audio codec support + +config SND_HDA_CODEC_ALC880 + tristate "Build Realtek ALC880 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC880 HD-audio codec support + +config SND_HDA_CODEC_ALC882 + tristate "Build Realtek ALC882 HD-audio codecs support" if EXPERT + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + default y + help + Say Y or M here to include Realtek ALC882 and compatible HD-audio + codec support + +endif diff --git a/sound/hda/codecs/realtek/Makefile b/sound/hda/codecs/realtek/Makefile new file mode 100644 index 000000000000..c6ee4e526a40 --- /dev/null +++ b/sound/hda/codecs/realtek/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-realtek-lib-y := realtek.o +snd-hda-codec-alc260-y := alc260.o +snd-hda-codec-alc262-y := alc262.o +snd-hda-codec-alc268-y := alc268.o +snd-hda-codec-alc269-y := alc269.o +snd-hda-codec-alc662-y := alc662.o +snd-hda-codec-alc680-y := alc680.o +snd-hda-codec-alc861-y := alc861.o +snd-hda-codec-alc861vd-y := alc861vd.o +snd-hda-codec-alc880-y := alc880.o +snd-hda-codec-alc882-y := alc882.o + +obj-$(CONFIG_SND_HDA_CODEC_REALTEK_LIB) += snd-hda-codec-realtek-lib.o +obj-$(CONFIG_SND_HDA_CODEC_ALC260) += snd-hda-codec-alc260.o +obj-$(CONFIG_SND_HDA_CODEC_ALC262) += snd-hda-codec-alc262.o +obj-$(CONFIG_SND_HDA_CODEC_ALC268) += snd-hda-codec-alc268.o +obj-$(CONFIG_SND_HDA_CODEC_ALC269) += snd-hda-codec-alc269.o +obj-$(CONFIG_SND_HDA_CODEC_ALC662) += snd-hda-codec-alc662.o +obj-$(CONFIG_SND_HDA_CODEC_ALC680) += snd-hda-codec-alc680.o +obj-$(CONFIG_SND_HDA_CODEC_ALC861) += snd-hda-codec-alc861.o +obj-$(CONFIG_SND_HDA_CODEC_ALC861VD) += snd-hda-codec-alc861vd.o +obj-$(CONFIG_SND_HDA_CODEC_ALC880) += snd-hda-codec-alc880.o +obj-$(CONFIG_SND_HDA_CODEC_ALC882) += snd-hda-codec-alc882.o diff --git a/sound/hda/codecs/realtek/alc260.c b/sound/hda/codecs/realtek/alc260.c new file mode 100644 index 000000000000..8bd47079dccb --- /dev/null +++ b/sound/hda/codecs/realtek/alc260.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC260 codec +// + +#include +#include +#include "realtek.h" + +static int alc260_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; + static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; + return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); +} + +/* + * Pin config fixes + */ +enum { + ALC260_FIXUP_HP_DC5750, + ALC260_FIXUP_HP_PIN_0F, + ALC260_FIXUP_COEF, + ALC260_FIXUP_GPIO1, + ALC260_FIXUP_GPIO1_TOGGLE, + ALC260_FIXUP_REPLACER, + ALC260_FIXUP_HP_B1900, + ALC260_FIXUP_KN1, + ALC260_FIXUP_FSC_S7020, + ALC260_FIXUP_FSC_S7020_JWSE, + ALC260_FIXUP_VAIO_PINS, +}; + +static void alc260_gpio1_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); +} + +static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PROBE) { + /* although the machine has only one output pin, we need to + * toggle GPIO1 according to the jack state + */ + spec->gen.automute_hook = alc260_gpio1_automute; + spec->gen.detect_hp = 1; + spec->gen.automute_speaker = 1; + spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable_callback(codec, 0x0f, + snd_hda_gen_hp_automute); + alc_setup_gpio(codec, 0x01); + } +} + +static void alc260_fixup_kn1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x0f, 0x02214000 }, /* HP/speaker */ + { 0x12, 0x90a60160 }, /* int mic */ + { 0x13, 0x02a19000 }, /* ext mic */ + { 0x18, 0x01446000 }, /* SPDIF out */ + /* disable bogus I/O pins */ + { 0x10, 0x411111f0 }, + { 0x11, 0x411111f0 }, + { 0x14, 0x411111f0 }, + { 0x15, 0x411111f0 }, + { 0x16, 0x411111f0 }, + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + spec->init_amp = ALC_INIT_NONE; + break; + } +} + +static void alc260_fixup_fsc_s7020(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->init_amp = ALC_INIT_NONE; +} + +static void alc260_fixup_fsc_s7020_jwse(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.add_jack_modes = 1; + spec->gen.hp_mic = 1; + } +} + +static const struct hda_fixup alc260_fixups[] = { + [ALC260_FIXUP_HP_DC5750] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x11, 0x90130110 }, /* speaker */ + { } + } + }, + [ALC260_FIXUP_HP_PIN_0F] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0f, 0x01214000 }, /* HP */ + { } + } + }, + [ALC260_FIXUP_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, + { } + }, + }, + [ALC260_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC260_FIXUP_GPIO1_TOGGLE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, + [ALC260_FIXUP_REPLACER] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, + }, + [ALC260_FIXUP_HP_B1900] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_COEF, + }, + [ALC260_FIXUP_KN1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_kn1, + }, + [ALC260_FIXUP_FSC_S7020] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020, + }, + [ALC260_FIXUP_FSC_S7020_JWSE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020_jwse, + .chained = true, + .chain_id = ALC260_FIXUP_FSC_S7020, + }, + [ALC260_FIXUP_VAIO_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* Pin configs are missing completely on some VAIOs */ + { 0x0f, 0x01211020 }, + { 0x10, 0x0001003f }, + { 0x11, 0x411111f0 }, + { 0x12, 0x01a15930 }, + { 0x13, 0x411111f0 }, + { 0x14, 0x411111f0 }, + { 0x15, 0x411111f0 }, + { 0x16, 0x411111f0 }, + { 0x17, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { } + } + }, +}; + +static const struct hda_quirk alc260_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), + SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), + SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), + SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), + SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), + {} +}; + +static const struct hda_model_fixup alc260_fixup_models[] = { + {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC260_FIXUP_COEF, .name = "coef"}, + {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, + {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, + {} +}; + +/* + */ +static int alc260_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x07); + if (err < 0) + return err; + + spec = codec->spec; + /* as quite a few machines require HP amp for speaker outputs, + * it's easier to enable it unconditionally; even if it's unneeded, + * it's almost harmless. + */ + spec->gen.prefer_hp_amp = 1; + spec->gen.beep_nid = 0x01; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, + alc260_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc260_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc260_codec_ops = { + .probe = alc260_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc260[] = { + HDA_CODEC_ID(0x10ec0260, "ALC260"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc260_driver = { + .id = snd_hda_id_alc260, + .ops = &alc260_codec_ops, +}; + +module_hda_codec_driver(alc260_driver); diff --git a/sound/hda/codecs/realtek/alc262.c b/sound/hda/codecs/realtek/alc262.c new file mode 100644 index 000000000000..3ec06cf5d2a6 --- /dev/null +++ b/sound/hda/codecs/realtek/alc262.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC262 codec +// + +#include +#include +#include "realtek.h" + +static int alc262_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); +} + +/* + * Pin config fixes + */ +enum { + ALC262_FIXUP_FSC_H270, + ALC262_FIXUP_FSC_S7110, + ALC262_FIXUP_HP_Z200, + ALC262_FIXUP_TYAN, + ALC262_FIXUP_LENOVO_3000, + ALC262_FIXUP_BENQ, + ALC262_FIXUP_BENQ_T31, + ALC262_FIXUP_INV_DMIC, + ALC262_FIXUP_INTEL_BAYLEYBAY, +}; + +static const struct hda_fixup alc262_fixups[] = { + [ALC262_FIXUP_FSC_H270] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0221142f }, /* front HP */ + { 0x1b, 0x0121141f }, /* rear HP */ + { } + } + }, + [ALC262_FIXUP_FSC_S7110] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x90170110 }, /* speaker */ + { } + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, + [ALC262_FIXUP_HP_Z200] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130120 }, /* internal speaker */ + { } + } + }, + [ALC262_FIXUP_TYAN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x1993e1f0 }, /* int AUX */ + { } + } + }, + [ALC262_FIXUP_LENOVO_3000] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, + {} + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, + [ALC262_FIXUP_BENQ] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + } + }, + [ALC262_FIXUP_BENQ_T31] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + {} + } + }, + [ALC262_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC262_FIXUP_INTEL_BAYLEYBAY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_depop_delay, + }, +}; + +static const struct hda_quirk alc262_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), + SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), + SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270), + SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), + SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY), + {} +}; + +static const struct hda_model_fixup alc262_fixup_models[] = { + {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, + {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, + {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, + {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, + {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, + {.id = ALC262_FIXUP_BENQ, .name = "benq"}, + {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, + {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, + {} +}; + +/* + */ +static int alc262_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + spec->gen.shared_mic_vref_pin = 0x18; + + spec->shutup = alc_eapd_shutup; + +#if 0 + /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is + * under-run + */ + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); +#endif + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + alc262_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + /* automatic parse from the BIOS config */ + err = alc262_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc262_codec_ops = { + .probe = alc262_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc262[] = { + HDA_CODEC_ID(0x10ec0262, "ALC262"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc262); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC262 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc262_driver = { + .id = snd_hda_id_alc262, + .ops = &alc262_codec_ops, +}; + +module_hda_codec_driver(alc262_driver); diff --git a/sound/hda/codecs/realtek/alc268.c b/sound/hda/codecs/realtek/alc268.c new file mode 100644 index 000000000000..e489cdc98eb8 --- /dev/null +++ b/sound/hda/codecs/realtek/alc268.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "realtek.h" + +/* bind Beep switches of both NID 0x0f and 0x10 */ +static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = (pval & ~0xff) | 0x0f; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err >= 0) { + kcontrol->private_value = (pval & ~0xff) | 0x10; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} + +static const struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_beep_switch_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) + }, +}; + +/* set PCBEEP vol = 0, mute connections */ +static const struct hda_verb alc268_beep_init_verbs[] = { + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } +}; + +enum { + ALC268_FIXUP_INV_DMIC, + ALC268_FIXUP_HP_EAPD, + ALC268_FIXUP_SPDIF, +}; + +static const struct hda_fixup alc268_fixups[] = { + [ALC268_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC268_FIXUP_HP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC268_FIXUP_SPDIF] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x014b1180 }, /* enable SPDIF out */ + {} + } + }, +}; + +static const struct hda_model_fixup alc268_fixup_models[] = { + {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, + {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, + {} +}; + +static const struct hda_quirk alc268_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), + SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), + /* below is codec SSID since multiple Toshiba laptops have the + * same PCI SSID 1179:ff00 + */ + SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), + {} +}; + +/* + * BIOS auto configuration + */ +static int alc268_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, NULL, alc268_ssids); +} + +/* + */ +static int alc268_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int i, err; + + /* ALC268 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc268_parse_auto_config(codec); + if (err < 0) + goto error; + + if (err > 0 && !spec->gen.no_analog && + spec->gen.autocfg.speaker_pins[0] != 0x1d) { + for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc268_beep_mixer[i])) { + err = -ENOMEM; + goto error; + } + } + snd_hda_add_verbs(codec, alc268_beep_init_verbs); + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc268_codec_ops = { + .probe = alc268_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc268[] = { + HDA_CODEC_ID(0x10ec0267, "ALC267"), + HDA_CODEC_ID(0x10ec0268, "ALC268"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC267/268 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc268_driver = { + .id = snd_hda_id_alc268, + .ops = &alc268_codec_ops, +}; + +module_hda_codec_driver(alc268_driver); diff --git a/sound/pci/hda/patch_realtek.c b/sound/hda/codecs/realtek/alc269.c similarity index 63% rename from sound/pci/hda/patch_realtek.c rename to sound/hda/codecs/realtek/alc269.c index cd0d7ba7320e..85bb8c4d3b17 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1,3158 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Realtek ALC codecs - * - * Copyright (c) 2004 Kailang Yang - * PeiSen Hou - * Takashi Iwai - * Jonathan Woithe - */ +// +// Realtek ALC269 and compatible codecs +// -#include -#include #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" -#include "hda_component.h" +#include "realtek.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 -/* extra amp-initialization sequence types */ -enum { - ALC_INIT_UNDEFINED, - ALC_INIT_NONE, - ALC_INIT_DEFAULT, -}; - -enum { - ALC_HEADSET_MODE_UNKNOWN, - ALC_HEADSET_MODE_UNPLUGGED, - ALC_HEADSET_MODE_HEADSET, - ALC_HEADSET_MODE_MIC, - ALC_HEADSET_MODE_HEADPHONE, -}; - -enum { - ALC_HEADSET_TYPE_UNKNOWN, - ALC_HEADSET_TYPE_CTIA, - ALC_HEADSET_TYPE_OMTP, -}; - -enum { - ALC_KEY_MICMUTE_INDEX, -}; - -struct alc_customize_define { - unsigned int sku_cfg; - unsigned char port_connectivity; - unsigned char check_sum; - unsigned char customization; - unsigned char external_amp; - unsigned int enable_pcbeep:1; - unsigned int platform_type:1; - unsigned int swap:1; - unsigned int override:1; - unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ -}; - -struct alc_coef_led { - unsigned int idx; - unsigned int mask; - unsigned int on; - unsigned int off; -}; - -struct alc_spec { - struct hda_gen_spec gen; /* must be at head */ - - /* codec parameterization */ - struct alc_customize_define cdefine; - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - /* GPIO bits */ - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - bool gpio_write_delay; /* add a delay before writing gpio_data */ - - /* mute LED for HP laptops, see vref_mute_led_set() */ - int mute_led_polarity; - int micmute_led_polarity; - hda_nid_t mute_led_nid; - hda_nid_t cap_mute_led_nid; - - unsigned int gpio_mute_led_mask; - unsigned int gpio_mic_led_mask; - struct alc_coef_led mute_led_coef; - struct alc_coef_led mic_led_coef; - struct mutex coef_mutex; - - hda_nid_t headset_mic_pin; - hda_nid_t headphone_mic_pin; - int current_headset_mode; - int current_headset_type; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*power_hook)(struct hda_codec *codec); - void (*shutup)(struct hda_codec *codec); - - int init_amp; - int codec_variant; /* flag for other variants */ - unsigned int has_alc5505_dsp:1; - unsigned int no_depop_delay:1; - unsigned int done_hp_init:1; - unsigned int no_shutup_pins:1; - unsigned int ultra_low_power:1; - unsigned int has_hs_key:1; - unsigned int no_internal_mic_pin:1; - unsigned int en_3kpull_low:1; - int num_speaker_amps; - - /* for PLL fix */ - hda_nid_t pll_nid; - unsigned int pll_coef_idx, pll_coef_bit; - unsigned int coef0; - struct input_dev *kb_dev; - u8 alc_mute_keycode_map[1]; - - /* component binding */ - struct hda_component_parent comps; -}; - -/* - * COEF access helper functions - */ - -static void coef_mutex_lock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_power_up_pm(codec); - mutex_lock(&spec->coef_mutex); -} - -static void coef_mutex_unlock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - mutex_unlock(&spec->coef_mutex); - snd_hda_power_down_pm(codec); -} - -static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - return val; -} - -static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - coef_mutex_lock(codec); - val = __alc_read_coefex_idx(codec, nid, coef_idx); - coef_mutex_unlock(codec); - return val; -} - -#define alc_read_coef_idx(codec, coef_idx) \ - alc_read_coefex_idx(codec, 0x20, coef_idx) - -static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); -} - -static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - coef_mutex_lock(codec); - __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); - coef_mutex_unlock(codec); -} - -#define alc_write_coef_idx(codec, coef_idx, coef_val) \ - alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) - -static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); - - if (val != -1) - __alc_write_coefex_idx(codec, nid, coef_idx, - (val & ~mask) | bits_set); -} - -static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - coef_mutex_lock(codec); - __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); - coef_mutex_unlock(codec); -} - -#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ - alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) - -/* a special bypass for COEF 0; read the cached value at the second time */ -static unsigned int alc_get_coef0(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - return spec->coef0; -} - -/* coef writes/updates batch */ -struct coef_fw { - unsigned char nid; - unsigned char idx; - unsigned short mask; - unsigned short val; -}; - -#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ - { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } -#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) -#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) -#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) - -static void alc_process_coef_fw(struct hda_codec *codec, - const struct coef_fw *fw) -{ - coef_mutex_lock(codec); - for (; fw->nid; fw++) { - if (fw->mask == (unsigned short)-1) - __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); - else - __alc_update_coefex_idx(codec, fw->nid, fw->idx, - fw->mask, fw->val); - } - coef_mutex_unlock(codec); -} - -/* - * GPIO setup tables, used in initialization - */ - -/* Enable GPIO mask and set output */ -static void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) -{ - struct alc_spec *spec = codec->spec; - - spec->gpio_mask |= mask; - spec->gpio_dir |= mask; - spec->gpio_data |= mask; -} - -static void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} - -static void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, - bool on) -{ - struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_data; - - if (on) - spec->gpio_data |= mask; - else - spec->gpio_data &= ~mask; - if (oldval != spec->gpio_data) - alc_write_gpio_data(codec); -} - -static void alc_write_gpio(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->gpio_mask) - return; - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); - if (spec->gpio_write_delay) - msleep(1); - alc_write_gpio_data(codec); -} - -static void alc_fixup_gpio(struct hda_codec *codec, int action, - unsigned int mask) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - alc_setup_gpio(codec, mask); -} - -static void alc_fixup_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x01); -} - -static void alc_fixup_gpio2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x02); -} - -static void alc_fixup_gpio3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x03); -} - -static void alc_fixup_gpio4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x04); -} - -static void alc_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_gen_add_micmute_led_cdev(codec, NULL); -} - -/* - * Fix hardware PLL issue - * On some codecs, the analog PLL gating control must be off while - * the default value is 1. - */ -static void alc_fix_pll(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->pll_nid) - alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, - 1 << spec->pll_coef_bit, 0); -} - -static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_bit) -{ - struct alc_spec *spec = codec->spec; - spec->pll_nid = nid; - spec->pll_coef_idx = coef_idx; - spec->pll_coef_bit = coef_bit; - alc_fix_pll(codec); -} - -/* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - unsigned int val; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl; - - kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); - if (!kctl) - return; - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return; - val = snd_hda_codec_read(codec, jack->nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - val &= HDA_AMP_VOLMASK; - uctl->value.integer.value[0] = val; - uctl->value.integer.value[1] = val; - kctl->put(kctl, uctl); - kfree(uctl); -} - -static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) -{ - /* For some reason, the res given from ALC880 is broken. - Here we adjust it properly. */ - snd_hda_jack_unsol_event(codec, res >> 2); -} - -/* Change EAPD to verb control */ -static void alc_fill_eapd_coef(struct hda_codec *codec) -{ - int coef; - - coef = alc_get_coef0(codec); - - switch (codec->core.vendor_id) { - case 0x10ec0262: - alc_update_coef_idx(codec, 0x7, 0, 1<<5); - break; - case 0x10ec0267: - case 0x10ec0268: - alc_update_coef_idx(codec, 0x7, 0, 1<<13); - break; - case 0x10ec0269: - if ((coef & 0x00f0) == 0x0010) - alc_update_coef_idx(codec, 0xd, 0, 1<<14); - if ((coef & 0x00f0) == 0x0020) - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0280: - case 0x10ec0284: - case 0x10ec0290: - case 0x10ec0292: - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - break; - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - fallthrough; - case 0x10ec0215: - case 0x10ec0236: - case 0x10ec0245: - case 0x10ec0256: - case 0x10ec0257: - case 0x10ec0285: - case 0x10ec0289: - alc_update_coef_idx(codec, 0x36, 1<<13, 0); - fallthrough; - case 0x10ec0230: - case 0x10ec0233: - case 0x10ec0235: - case 0x10ec0255: - case 0x19e58326: - case 0x10ec0282: - case 0x10ec0283: - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - case 0x10ec0300: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0275: - alc_update_coef_idx(codec, 0xe, 0, 1<<0); - break; - case 0x10ec0287: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - alc_write_coef_idx(codec, 0x8, 0x4ab7); - break; - case 0x10ec0293: - alc_update_coef_idx(codec, 0xa, 1<<13, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - alc_write_coef_idx(codec, 0x6e, 0x0c25); - fallthrough; - case 0x10ec0294: - case 0x10ec0700: - case 0x10ec0701: - case 0x10ec0703: - case 0x10ec0711: - alc_update_coef_idx(codec, 0x10, 1<<15, 0); - break; - case 0x10ec0662: - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ - break; - case 0x10ec0272: - case 0x10ec0273: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0670: - case 0x10ec0671: - case 0x10ec0672: - alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ - break; - case 0x10ec0222: - case 0x10ec0623: - alc_update_coef_idx(codec, 0x19, 1<<13, 0); - break; - case 0x10ec0668: - alc_update_coef_idx(codec, 0x7, 3<<13, 0); - break; - case 0x10ec0867: - alc_update_coef_idx(codec, 0x4, 1<<10, 0); - break; - case 0x10ec0888: - if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0892: - case 0x10ec0897: - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0899: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1168: - case 0x10ec1220: - alc_update_coef_idx(codec, 0x7, 1<<1, 0); - break; - } -} - -/* additional initialization for ALC888 variants */ -static void alc888_coef_init(struct hda_codec *codec) -{ - switch (alc_get_coef0(codec) & 0x00f0) { - /* alc888-VA */ - case 0x00: - /* alc888-VB */ - case 0x10: - alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */ - break; - } -} - -/* turn on/off EAPD control (only if available) */ -static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) -{ - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return; - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - on ? 2 : 0); -} - -/* turn on/off EAPD controls of the codec */ -static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) -{ - /* We currently only handle front, HP */ - static const hda_nid_t pins[] = { - 0x0f, 0x10, 0x14, 0x15, 0x17, 0 - }; - const hda_nid_t *p; - for (p = pins; *p; p++) - set_eapd(codec, *p, on); -} - -static int find_ext_mic_pin(struct hda_codec *codec); - -static void alc_headset_mic_no_shutup(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int mic_pin = find_ext_mic_pin(codec); - int i; - - /* don't shut up pins when unloading the driver; otherwise it breaks - * the default pin setup at the next load of the driver - */ - if (codec->bus->shutdown) - return; - - snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - - codec->pins_shutup = 1; -} - -static void alc_shutup_pins(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->no_shutup_pins) - return; - - switch (codec->core.vendor_id) { - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - case 0x10ec0283: - case 0x10ec0285: - case 0x10ec0286: - case 0x10ec0287: - case 0x10ec0288: - case 0x10ec0295: - case 0x10ec0298: - alc_headset_mic_no_shutup(codec); - break; - default: - snd_hda_shutup_pins(codec); - break; - } -} - -/* generic shutup callback; - * just turning off EAPD and a little pause for avoiding pop-noise - */ -static void alc_eapd_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_auto_setup_eapd(codec, false); - if (!spec->no_depop_delay) - msleep(200); - alc_shutup_pins(codec); -} - -/* generic EAPD initialization */ -static void alc_auto_init_amp(struct hda_codec *codec, int type) -{ - alc_auto_setup_eapd(codec, true); - alc_write_gpio(codec); - switch (type) { - case ALC_INIT_DEFAULT: - switch (codec->core.vendor_id) { - case 0x10ec0260: - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); - break; - case 0x10ec0880: - case 0x10ec0882: - case 0x10ec0883: - case 0x10ec0885: - alc_update_coef_idx(codec, 7, 0, 0x2030); - break; - case 0x10ec0888: - alc888_coef_init(codec); - break; - } - break; - } -} - -/* get a primary headphone pin if available */ -static hda_nid_t alc_get_hp_pin(struct alc_spec *spec) -{ - if (spec->gen.autocfg.hp_pins[0]) - return spec->gen.autocfg.hp_pins[0]; - if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) - return spec->gen.autocfg.line_out_pins[0]; - return 0; -} - -/* - * Realtek SSID verification - */ - -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) - -static void alc_fixup_sku_ignore(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->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; - } -} - -static void alc_fixup_no_depop_delay(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - spec->no_depop_delay = 1; - codec->depop_delay = 0; - } -} - -static int alc_auto_parse_customize_define(struct hda_codec *codec) -{ - unsigned int ass, tmp, i; - unsigned nid = 0; - struct alc_spec *spec = codec->spec; - - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } - - if (!codec->bus->pci) - return -1; - ass = codec->core.subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->core.chip_name, ass); - return -1; - } - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; - - spec->cdefine.port_connectivity = ass >> 30; - spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; - spec->cdefine.check_sum = (ass >> 16) & 0xf; - spec->cdefine.customization = ass >> 8; -do_sku: - spec->cdefine.sku_cfg = ass; - spec->cdefine.external_amp = (ass & 0x38) >> 3; - spec->cdefine.platform_type = (ass & 0x4) >> 2; - spec->cdefine.swap = (ass & 0x2) >> 1; - spec->cdefine.override = ass & 0x1; - - codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n", - nid, spec->cdefine.sku_cfg); - codec_dbg(codec, "SKU: port_connectivity=0x%x\n", - spec->cdefine.port_connectivity); - codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); - codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); - codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization); - codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp); - codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type); - codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap); - codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override); - - return 0; -} - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} -/* return true if the given NID is found in the list */ -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - return find_idx_in_nid_list(nid, list, nums) >= 0; -} - -/* check subsystem ID and set up device-specific initialization; - * return 1 if initialized, 0 if invalid SSID - */ -/* 32-bit subsystem ID for BIOS loading in HD Audio codec. - * 31 ~ 16 : Manufacture ID - * 15 ~ 8 : SKU ID - * 7 ~ 0 : Assembly ID - * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 - */ -static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) -{ - unsigned int ass, tmp, i; - unsigned nid; - struct alc_spec *spec = codec->spec; - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return 0; - goto do_sku; - } - - ass = codec->core.subsystem_id & 0xffff; - if (codec->bus->pci && - ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - /* invalid SSID, check the special NID pin defcfg instead */ - /* - * 31~30 : port connectivity - * 29~21 : reserve - * 20 : PCBEEP input - * 19~16 : Check sum (15:1) - * 15~1 : Custom - * 0 : override - */ - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - codec_dbg(codec, - "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n", - ass, nid); - if (!(ass & 1)) - return 0; - if ((ass >> 30) != 1) /* no physical connection */ - return 0; - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return 0; -do_sku: - codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->core.vendor_id); - /* - * 0 : override - * 1 : Swap Jack - * 2 : 0 --> Desktop, 1 --> Laptop - * 3~5 : External Amplifier control - * 7~6 : Reserved - */ - tmp = (ass & 0x38) >> 3; /* external Amp control */ - if (spec->init_amp == ALC_INIT_UNDEFINED) { - switch (tmp) { - case 1: - alc_setup_gpio(codec, 0x01); - break; - case 3: - alc_setup_gpio(codec, 0x02); - break; - case 7: - alc_setup_gpio(codec, 0x04); - break; - case 5: - default: - spec->init_amp = ALC_INIT_DEFAULT; - break; - } - } - - /* is laptop or Desktop and enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!(ass & 0x8000)) - return 1; - /* - * 10~8 : Jack location - * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered - * 14~13: Resvered - * 15 : 1 --> enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!alc_get_hp_pin(spec)) { - hda_nid_t nid; - tmp = (ass >> 11) & 0x3; /* HP to chassis */ - nid = ports[tmp]; - if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, - spec->gen.autocfg.line_outs)) - return 1; - spec->gen.autocfg.hp_pins[0] = nid; - } - return 1; -} - -/* Check the validity of ALC subsystem-id - * ports contains an array of 4 pin NIDs for port-A, E, D and I */ -static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) -{ - if (!alc_subsystem_id(codec, ports)) { - struct alc_spec *spec = codec->spec; - if (spec->init_amp == ALC_INIT_UNDEFINED) { - codec_dbg(codec, - "realtek: Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; - } - } -} - -/* inverted digital-mic */ -static void alc_fixup_inv_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - spec->gen.inv_dmic_split = 1; -} - - -static int alc_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); - return 0; -} - - -/* - * Common callbacks - */ - -static void alc_pre_init(struct hda_codec *codec) -{ - alc_fill_eapd_coef(codec); -} - -#define is_s3_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) -#define is_s4_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) -#define is_s4_suspend(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - /* hibernation resume needs the full chip initialization */ - if (is_s4_resume(codec)) - alc_pre_init(codec); - - if (spec->init_hook) - spec->init_hook(codec); - - spec->gen.skip_verbs = 1; /* applied in below */ - snd_hda_gen_init(codec); - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - snd_hda_apply_verbs(codec); /* apply verbs here after own init */ - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return 0; -} - -/* forward declaration */ -static const struct component_master_ops comp_master_ops; - -static void alc_free(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec) - hda_component_manager_free(&spec->comps, &comp_master_ops); - - snd_hda_gen_free(codec); -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!snd_hda_get_bool_hint(codec, "shutup")) - return; /* disabled explicitly by hints */ - - if (spec && spec->shutup) - spec->shutup(codec); - else - alc_shutup_pins(codec); -} - -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} - -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; -} - -static int alc_resume(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->no_depop_delay) - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} - -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, - .resume = alc_resume, - .suspend = alc_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - - -#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) - -/* - * Rename codecs appropriately from COEF value or subvendor id - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -struct alc_codec_rename_pci_table { - unsigned int codec_vendor_id; - unsigned short pci_subvendor; - unsigned short pci_subdevice; - const char *name; -}; - -static const struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { - { 0x10ec0280, 0x1028, 0, "ALC3220" }, - { 0x10ec0282, 0x1028, 0, "ALC3221" }, - { 0x10ec0283, 0x1028, 0, "ALC3223" }, - { 0x10ec0288, 0x1028, 0, "ALC3263" }, - { 0x10ec0292, 0x1028, 0, "ALC3226" }, - { 0x10ec0293, 0x1028, 0, "ALC3235" }, - { 0x10ec0255, 0x1028, 0, "ALC3234" }, - { 0x10ec0668, 0x1028, 0, "ALC3661" }, - { 0x10ec0275, 0x1028, 0, "ALC3260" }, - { 0x10ec0899, 0x1028, 0, "ALC3861" }, - { 0x10ec0298, 0x1028, 0, "ALC3266" }, - { 0x10ec0236, 0x1028, 0, "ALC3204" }, - { 0x10ec0256, 0x1028, 0, "ALC3246" }, - { 0x10ec0225, 0x1028, 0, "ALC3253" }, - { 0x10ec0295, 0x1028, 0, "ALC3254" }, - { 0x10ec0299, 0x1028, 0, "ALC3271" }, - { 0x10ec0670, 0x1025, 0, "ALC669X" }, - { 0x10ec0676, 0x1025, 0, "ALC679X" }, - { 0x10ec0282, 0x1043, 0, "ALC3229" }, - { 0x10ec0233, 0x1043, 0, "ALC3236" }, - { 0x10ec0280, 0x103c, 0, "ALC3228" }, - { 0x10ec0282, 0x103c, 0, "ALC3227" }, - { 0x10ec0286, 0x103c, 0, "ALC3242" }, - { 0x10ec0290, 0x103c, 0, "ALC3241" }, - { 0x10ec0668, 0x103c, 0, "ALC3662" }, - { 0x10ec0283, 0x17aa, 0, "ALC3239" }, - { 0x10ec0292, 0x17aa, 0, "ALC3232" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - const struct alc_codec_rename_pci_table *q; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->core.vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - - if (!codec->bus->pci) - return 0; - for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->core.vendor_id) - continue; - if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) - continue; - if (!q->pci_subdevice || - q->pci_subdevice == codec->bus->pci->subsystem_device) - return alc_codec_rename(codec, q->name); - } - - return 0; -} - - -/* - * Digital-beep handlers - */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - -/* additional beep mixers; private_value will be overwritten */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), -}; - -/* set up and create beep controls */ -static int set_beep_amp(struct alc_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - int i; - - for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; - } - return 0; -} - -static const struct snd_pci_quirk beep_allow_list[] = { - SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), - SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), - SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), - /* denylist -- no beep available */ - SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0), - SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0), - {} -}; - -static inline int has_cdefine_beep(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list); - if (q) - return q->value; - return spec->cdefine.enable_pcbeep; -} -#else -#define set_beep_amp(spec, nid, idx, dir) 0 -#define has_cdefine_beep(codec) 0 -#endif - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -static int alc_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids, - const hda_nid_t *ssid_nids) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; - - if (ssid_nids) - alc_ssid_check(codec, ssid_nids); - - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - return 1; -} - -/* common preparation job for alc_spec */ -static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) -{ - struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - int err; - - if (!spec) - return -ENOMEM; - codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - spec->gen.mixer_nid = mixer_nid; - spec->gen.own_eapd_ctl = 1; - codec->single_adc_amp = 1; - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - codec->forced_resume = 1; - codec->patch_ops = alc_patch_ops; - mutex_init(&spec->coef_mutex); - - err = alc_codec_rename_from_preset(codec); - if (err < 0) { - kfree(spec); - return err; - } - return 0; -} - -static int alc880_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); -} - -/* - * ALC880 fix-ups - */ -enum { - ALC880_FIXUP_GPIO1, - ALC880_FIXUP_GPIO2, - ALC880_FIXUP_MEDION_RIM, - ALC880_FIXUP_LG, - ALC880_FIXUP_LG_LW25, - ALC880_FIXUP_W810, - ALC880_FIXUP_EAPD_COEF, - ALC880_FIXUP_TCL_S700, - ALC880_FIXUP_VOL_KNOB, - ALC880_FIXUP_FUJITSU, - ALC880_FIXUP_F1734, - ALC880_FIXUP_UNIWILL, - ALC880_FIXUP_UNIWILL_DIG, - ALC880_FIXUP_Z71V, - ALC880_FIXUP_ASUS_W5A, - ALC880_FIXUP_3ST_BASE, - ALC880_FIXUP_3ST, - ALC880_FIXUP_3ST_DIG, - ALC880_FIXUP_5ST_BASE, - ALC880_FIXUP_5ST, - ALC880_FIXUP_5ST_DIG, - ALC880_FIXUP_6ST_BASE, - ALC880_FIXUP_6ST, - ALC880_FIXUP_6ST_DIG, - ALC880_FIXUP_6ST_AUTOMUTE, -}; - -/* enable the volume-knob widget support on NID 0x21 */ -static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc_update_knob_master); -} - -static const struct hda_fixup alc880_fixups[] = { - [ALC880_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC880_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC880_FIXUP_MEDION_RIM] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_LG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x16, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_LG_LW25] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x0181344f }, /* line-in */ - { 0x1b, 0x0321403f }, /* headphone */ - { } - } - }, - [ALC880_FIXUP_W810] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_EAPD_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - {} - }, - }, - [ALC880_FIXUP_TCL_S700] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_VOL_KNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc880_fixup_vol_knob, - }, - [ALC880_FIXUP_FUJITSU] = { - /* override all pins as BIOS on old Amilo is broken */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x01454140 }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_F1734] = { - /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_UNIWILL] = { - /* need to fix HP and speaker pins to be parsed correctly */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { } - }, - }, - [ALC880_FIXUP_UNIWILL_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1f, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_Z71V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x99030120 }, /* speaker */ - { 0x15, 0x0121411f }, /* HP */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19950 }, /* mic-in */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - } - }, - [ALC880_FIXUP_ASUS_W5A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x90a60160 }, /* mic */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0xb743111e }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO1, - }, - [ALC880_FIXUP_3ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* line-out */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_3ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_5ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01016412 }, /* surr */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_5ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_6ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x01016412 }, /* surr */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01012414 }, /* side */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x02a19c40 }, /* front-mic */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x0121411f }, /* HP */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_6ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_AUTOMUTE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0121401f }, /* HP with jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, -}; - -static const struct hda_quirk alc880_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), - SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), - - /* Below is the copied entries from alc880_quirks.c. - * It's not quite sure whether BIOS sets the correct pin-config table - * on these machines, thus they are kept to be compatible with - * the old static quirks. Once when it's confirmed to work without - * these overrides, it'd be better to remove. - */ - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), - {} -}; - -static const struct hda_model_fixup alc880_fixup_models[] = { - {.id = ALC880_FIXUP_3ST, .name = "3stack"}, - {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, - {.id = ALC880_FIXUP_5ST, .name = "5stack"}, - {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, - {.id = ALC880_FIXUP_6ST, .name = "6stack"}, - {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, - {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, - {} -}; - - -/* - * OK, here we have finally the patch for ALC880 - */ -static int patch_alc880(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.need_dac_fix = 1; - spec->gen.beep_nid = 0x01; - - codec->patch_ops.unsol_event = alc880_unsol_event; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, - alc880_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC260 support - */ -static int alc260_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; - return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC260_FIXUP_HP_DC5750, - ALC260_FIXUP_HP_PIN_0F, - ALC260_FIXUP_COEF, - ALC260_FIXUP_GPIO1, - ALC260_FIXUP_GPIO1_TOGGLE, - ALC260_FIXUP_REPLACER, - ALC260_FIXUP_HP_B1900, - ALC260_FIXUP_KN1, - ALC260_FIXUP_FSC_S7020, - ALC260_FIXUP_FSC_S7020_JWSE, - ALC260_FIXUP_VAIO_PINS, -}; - -static void alc260_gpio1_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); -} - -static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { - /* although the machine has only one output pin, we need to - * toggle GPIO1 according to the jack state - */ - spec->gen.automute_hook = alc260_gpio1_automute; - spec->gen.detect_hp = 1; - spec->gen.automute_speaker = 1; - spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, - snd_hda_gen_hp_automute); - alc_setup_gpio(codec, 0x01); - } -} - -static void alc260_fixup_kn1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x0f, 0x02214000 }, /* HP/speaker */ - { 0x12, 0x90a60160 }, /* int mic */ - { 0x13, 0x02a19000 }, /* ext mic */ - { 0x18, 0x01446000 }, /* SPDIF out */ - /* disable bogus I/O pins */ - { 0x10, 0x411111f0 }, - { 0x11, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - spec->init_amp = ALC_INIT_NONE; - break; - } -} - -static void alc260_fixup_fsc_s7020(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->init_amp = ALC_INIT_NONE; -} - -static void alc260_fixup_fsc_s7020_jwse(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.add_jack_modes = 1; - spec->gen.hp_mic = 1; - } -} - -static const struct hda_fixup alc260_fixups[] = { - [ALC260_FIXUP_HP_DC5750] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x11, 0x90130110 }, /* speaker */ - { } - } - }, - [ALC260_FIXUP_HP_PIN_0F] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0f, 0x01214000 }, /* HP */ - { } - } - }, - [ALC260_FIXUP_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, - { } - }, - }, - [ALC260_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, - }, - [ALC260_FIXUP_REPLACER] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, - }, - [ALC260_FIXUP_HP_B1900] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_COEF, - }, - [ALC260_FIXUP_KN1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_kn1, - }, - [ALC260_FIXUP_FSC_S7020] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020, - }, - [ALC260_FIXUP_FSC_S7020_JWSE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020_jwse, - .chained = true, - .chain_id = ALC260_FIXUP_FSC_S7020, - }, - [ALC260_FIXUP_VAIO_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* Pin configs are missing completely on some VAIOs */ - { 0x0f, 0x01211020 }, - { 0x10, 0x0001003f }, - { 0x11, 0x411111f0 }, - { 0x12, 0x01a15930 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - } - }, -}; - -static const struct hda_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), - SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), - SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), - SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), - SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc260_fixup_models[] = { - {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC260_FIXUP_COEF, .name = "coef"}, - {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, - {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, - {} -}; - -/* - */ -static int patch_alc260(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x07); - if (err < 0) - return err; - - spec = codec->spec; - /* as quite a few machines require HP amp for speaker outputs, - * it's easier to enable it unconditionally; even if it's unneeded, - * it's almost harmless. - */ - spec->gen.prefer_hp_amp = 1; - spec->gen.beep_nid = 0x01; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, - alc260_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC882/883/885/888/889 support - * - * ALC882 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * Pin config fixes - */ -enum { - ALC882_FIXUP_ABIT_AW9D_MAX, - ALC882_FIXUP_LENOVO_Y530, - ALC882_FIXUP_PB_M5210, - ALC882_FIXUP_ACER_ASPIRE_7736, - ALC882_FIXUP_ASUS_W90V, - ALC889_FIXUP_CD, - ALC889_FIXUP_FRONT_HP_NO_PRESENCE, - ALC889_FIXUP_VAIO_TT, - ALC888_FIXUP_EEE1601, - ALC886_FIXUP_EAPD, - ALC882_FIXUP_EAPD, - ALC883_FIXUP_EAPD, - ALC883_FIXUP_ACER_EAPD, - ALC882_FIXUP_GPIO1, - ALC882_FIXUP_GPIO2, - ALC882_FIXUP_GPIO3, - ALC889_FIXUP_COEF, - ALC882_FIXUP_ASUS_W2JC, - ALC882_FIXUP_ACER_ASPIRE_4930G, - ALC882_FIXUP_ACER_ASPIRE_8930G, - ALC882_FIXUP_ASPIRE_8930G_VERBS, - ALC885_FIXUP_MACPRO_GPIO, - ALC889_FIXUP_DAC_ROUTE, - ALC889_FIXUP_MBP_VREF, - ALC889_FIXUP_IMAC91_VREF, - ALC889_FIXUP_MBA11_VREF, - ALC889_FIXUP_MBA21_VREF, - ALC889_FIXUP_MP11_VREF, - ALC889_FIXUP_MP41_VREF, - ALC882_FIXUP_INV_DMIC, - ALC882_FIXUP_NO_PRIMARY_HP, - ALC887_FIXUP_ASUS_BASS, - ALC887_FIXUP_BASS_CHMAP, - ALC1220_FIXUP_GB_DUAL_CODECS, - ALC1220_FIXUP_GB_X570, - ALC1220_FIXUP_CLEVO_P950, - ALC1220_FIXUP_CLEVO_PB51ED, - ALC1220_FIXUP_CLEVO_PB51ED_PINS, - ALC887_FIXUP_ASUS_AUDIO, - ALC887_FIXUP_ASUS_HMIC, - ALCS1200A_FIXUP_MIC_VREF, - ALC888VD_FIXUP_MIC_100VREF, -}; - -static void alc889_fixup_coef(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_INIT) - return; - alc_update_coef_idx(codec, 7, 0, 0x2030); -} - -/* set up GPIO at initialization */ -static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - spec->gpio_write_delay = true; - alc_fixup_gpio3(codec, fix, action); -} - -/* Fix the connection of some pins for ALC889: - * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't - * work correctly (bko#42740) - */ -static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* fake the connections during parsing the tree */ - static const hda_nid_t conn1[] = { 0x0c, 0x0d }; - static const hda_nid_t conn2[] = { 0x0e, 0x0f }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2); - } else if (action == HDA_FIXUP_ACT_PROBE) { - /* restore the connections */ - static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn); - } -} - -/* Set VREF on HP pin */ -static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 }; - struct alc_spec *spec = codec->spec; - int i; - - if (action != HDA_FIXUP_ACT_INIT) - return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { - unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); - if (get_defcfg_device(val) != AC_JACK_HP_OUT) - continue; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_80; - snd_hda_set_pin_ctl(codec, nids[i], val); - spec->gen.keep_vref_in_automute = 1; - break; - } -} - -static void alc889_fixup_mac_pins(struct hda_codec *codec, - const hda_nid_t *nids, int num_nids) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_nids; i++) { - unsigned int val; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, nids[i], val); - } - spec->gen.keep_vref_in_automute = 1; -} - -/* Set VREF on speaker pins on imac91 */ -static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18, 0x1a }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Set VREF on speaker pins on mba11 */ -static void alc889_fixup_mba11_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18 }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Set VREF on speaker pins on mba21 */ -static void alc889_fixup_mba21_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18, 0x19 }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Don't take HP output as primary - * Strangely, the speaker output doesn't work on Vaio Z and some Vaio - * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 - */ -static void alc882_fixup_no_primary_hp(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.no_primary_hp = 1; - spec->gen.no_multi_io = 1; - } -} - -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action); - -/* For dual-codec configuration, we need to disable some features to avoid - * conflicts of kctls and PCM streams - */ -static void alc_fixup_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - /* disable vmaster */ - spec->gen.suppress_vmaster = 1; - /* auto-mute and auto-mic switch don't work with multiple codecs */ - spec->gen.suppress_auto_mute = 1; - spec->gen.suppress_auto_mic = 1; - /* disable aamix as well */ - spec->gen.mixer_nid = 0; - /* add location prefix to avoid conflicts */ - codec->force_pin_prefix = 1; -} - -static void rename_ctl(struct hda_codec *codec, const char *oldname, - const char *newname) -{ - struct snd_kcontrol *kctl; - - kctl = snd_hda_find_mixer_ctl(codec, oldname); - if (kctl) - snd_ctl_rename(codec->card, kctl, newname); -} - -static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } -} - -static void alc1220_fixup_gb_x570(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const hda_nid_t conn1[] = { 0x0c }; - static const struct coef_fw gb_x570_coefs[] = { - WRITE_COEF(0x07, 0x03c0), - WRITE_COEF(0x1a, 0x01c1), - WRITE_COEF(0x1b, 0x0202), - WRITE_COEF(0x43, 0x3005), - {} - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, gb_x570_coefs); - break; - } -} - -static void alc1220_fixup_clevo_p950(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const hda_nid_t conn1[] = { 0x0c }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - alc_update_coef_idx(codec, 0x7, 0, 0x3c3); - /* We therefore want to make sure 0x14 (front headphone) and - * 0x1b (speakers) use the stereo DAC 0x02 - */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); -} - -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action); - -static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc1220_fixup_clevo_p950(codec, fix, action); - alc_fixup_headset_mode_no_hp_mic(codec, fix, action); -} - -static void alc887_asus_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - unsigned int vref; - - snd_hda_gen_hp_automute(codec, jack); - - if (spec->gen.hp_jack_present) - vref = AC_PINCTL_VREF_80; - else - vref = AC_PINCTL_VREF_HIZ; - snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); -} - -static void alc887_fixup_asus_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) - return; - snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); - spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; -} - -static const struct hda_fixup alc882_fixups[] = { - [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } - } - }, - [ALC882_FIXUP_LENOVO_Y530] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* rear int speakers */ - { 0x16, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - } - }, - [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC882_FIXUP_ASUS_W90V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130110 }, /* fix sequence for CLFE */ - { } - } - }, - [ALC889_FIXUP_CD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1c, 0x993301f0 }, /* CD */ - { } - } - }, - [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */ - { } - }, - .chained = true, - .chain_id = ALC889_FIXUP_CD, - }, - [ALC889_FIXUP_VAIO_TT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170111 }, /* hidden surround speaker */ - { } - } - }, - [ALC888_FIXUP_EEE1601] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, - { } - } - }, - [ALC886_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 }, - { } - } - }, - [ALC882_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - } - }, - [ALC883_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - { } - } - }, - [ALC883_FIXUP_ACER_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* eanable EAPD on Acer laptops */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - } - }, - [ALC882_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC882_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC882_FIXUP_GPIO3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio3, - }, - [ALC882_FIXUP_ASUS_W2JC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - .chained = true, - .chain_id = ALC882_FIXUP_EAPD, - }, - [ALC889_FIXUP_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_coef, - }, - [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x17, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x1b, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, - }, - [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { - /* additional init verbs for Acer Aspire 8930G */ - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enable all DACs */ - /* DAC DISABLE/MUTE 1? */ - /* setting bits 1-5 disables DAC nids 0x02-0x06 - * apparently. Init=0x38 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DAC DISABLE/MUTE 2? */ - /* some bit here disables the other DACs. - * Init=0x4900 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DMIC fix - * This laptop has a stereo digital microphone. - * The mics are only 1cm apart which makes the stereo - * useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal - * instead of standard stereo, which is annoying. - * So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, - * turning it into a normal mono mic. - */ - /* DMIC_CONTROL? Init value = 0x0001 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC885_FIXUP_MACPRO_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc885_fixup_macpro_gpio, - }, - [ALC889_FIXUP_DAC_ROUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_dac_route, - }, - [ALC889_FIXUP_MBP_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_IMAC91_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_imac91_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_MBA11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MBA21_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba21_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MP11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC889_FIXUP_MP41_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC882_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc882_fixup_no_primary_hp, - }, - [ALC887_FIXUP_ASUS_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x99130130}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC887_FIXUP_BASS_CHMAP, - }, - [ALC887_FIXUP_BASS_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - }, - [ALC1220_FIXUP_GB_DUAL_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_dual_codecs, - }, - [ALC1220_FIXUP_GB_X570] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_x570, - }, - [ALC1220_FIXUP_CLEVO_P950] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_p950, - }, - [ALC1220_FIXUP_CLEVO_PB51ED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_pb51ed, - }, - [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - {} - }, - .chained = true, - .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, - }, - [ALC887_FIXUP_ASUS_AUDIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ - { 0x19, 0x22219420 }, - {} - }, - }, - [ALC887_FIXUP_ASUS_HMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc887_fixup_asus_jack, - .chained = true, - .chain_id = ALC887_FIXUP_ASUS_AUDIO, - }, - [ALCS1200A_FIXUP_MIC_VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF50 }, /* rear mic */ - { 0x19, PIN_VREF50 }, /* front mic */ - {} - } - }, - [ALC888VD_FIXUP_MIC_100VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF100 }, /* headset mic */ - {} - } - }, -}; - -static const struct hda_quirk alc882_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), - SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), - SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), - SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), - SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF), - SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), - SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP), - - /* All Apple entries are in codec SSIDs */ - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF), - SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF), - - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF), - SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), - SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), - SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), - SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED), - SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc882_fixup_models[] = { - {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, - {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, - {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, - {.id = ALC889_FIXUP_CD, .name = "cd"}, - {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, - {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, - {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, - {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, - {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, - {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, - {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, - {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, - {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, - {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, - {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, - {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, - {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, - {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, - {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, - {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, - {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, - {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, - {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, - {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, - {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, - {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"}, - {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, - {} -}; - -static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01014010}, - {0x15, 0x01011012}, - {0x16, 0x01016011}, - {0x18, 0x01a19040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181304f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01456130}), - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01015010}, - {0x15, 0x01011012}, - {0x16, 0x01011011}, - {0x18, 0x01a11040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181104f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01451130}), - {} -}; - -/* - * BIOS auto configuration - */ -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); -} - -/* - */ -static int patch_alc882(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - - switch (codec->core.vendor_id) { - case 0x10ec0882: - case 0x10ec0885: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1220: - break; - default: - /* ALC883 and variants */ - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - break; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, - alc882_fixups); - snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC262 support - */ -static int alc262_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC262_FIXUP_FSC_H270, - ALC262_FIXUP_FSC_S7110, - ALC262_FIXUP_HP_Z200, - ALC262_FIXUP_TYAN, - ALC262_FIXUP_LENOVO_3000, - ALC262_FIXUP_BENQ, - ALC262_FIXUP_BENQ_T31, - ALC262_FIXUP_INV_DMIC, - ALC262_FIXUP_INTEL_BAYLEYBAY, -}; - -static const struct hda_fixup alc262_fixups[] = { - [ALC262_FIXUP_FSC_H270] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0221142f }, /* front HP */ - { 0x1b, 0x0121141f }, /* rear HP */ - { } - } - }, - [ALC262_FIXUP_FSC_S7110] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x90170110 }, /* speaker */ - { } - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_HP_Z200] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130120 }, /* internal speaker */ - { } - } - }, - [ALC262_FIXUP_TYAN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x1993e1f0 }, /* int AUX */ - { } - } - }, - [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_BENQ] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - } - }, - [ALC262_FIXUP_BENQ_T31] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - {} - } - }, - [ALC262_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC262_FIXUP_INTEL_BAYLEYBAY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_depop_delay, - }, -}; - -static const struct hda_quirk alc262_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), - SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), - SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY), - {} -}; - -static const struct hda_model_fixup alc262_fixup_models[] = { - {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, - {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, - {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, - {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, - {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, - {.id = ALC262_FIXUP_BENQ, .name = "benq"}, - {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, - {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, - {} -}; - -/* - */ -static int patch_alc262(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; - - spec->shutup = alc_eapd_shutup; - -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); -#endif - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, - alc262_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc262_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC268 - */ -/* bind Beep switches of both NID 0x0f and 0x10 */ -static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = (pval & ~0xff) | 0x0f; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err >= 0) { - kcontrol->private_value = (pval & ~0xff) | 0x10; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; -} - -static const struct snd_kcontrol_new alc268_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Beep Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_beep_switch_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) - }, -}; - -/* set PCBEEP vol = 0, mute connections */ -static const struct hda_verb alc268_beep_init_verbs[] = { - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -enum { - ALC268_FIXUP_INV_DMIC, - ALC268_FIXUP_HP_EAPD, - ALC268_FIXUP_SPDIF, -}; - -static const struct hda_fixup alc268_fixups[] = { - [ALC268_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC268_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC268_FIXUP_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x014b1180 }, /* enable SPDIF out */ - {} - } - }, -}; - -static const struct hda_model_fixup alc268_fixup_models[] = { - {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, - {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, - {} -}; - -static const struct hda_quirk alc268_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), - SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), - /* below is codec SSID since multiple Toshiba laptops have the - * same PCI SSID 1179:ff00 - */ - SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), - {} -}; - -/* - * BIOS auto configuration - */ -static int alc268_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, NULL, alc268_ssids); -} - -/* - */ -static int patch_alc268(struct hda_codec *codec) -{ - struct alc_spec *spec; - int i, err; - - /* ALC268 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc268_parse_auto_config(codec); - if (err < 0) - goto error; - - if (err > 0 && !spec->gen.no_analog && - spec->gen.autocfg.speaker_pins[0] != 0x1d) { - for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc268_beep_mixer[i])) { - err = -ENOMEM; - goto error; - } - } - snd_hda_add_verbs(codec, alc268_beep_init_verbs); - if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) - /* override the amp caps for beep generator */ - snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, - (0x0c << AC_AMPCAP_OFFSET_SHIFT) | - (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC269 - */ - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ }; @@ -3653,6 +510,15 @@ static void alc256_shutup(struct hda_codec *codec) hp_pin = 0x21; alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + + /* 3k pull low control for Headset jack. */ + /* NOTE: call this before clearing the pin, otherwise codec stalls */ + /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly + * when booting with headset plugged. So skip setting it for the codec alc257 + */ + if (spec->en_3kpull_low) + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); if (hp_pin_sense) { @@ -3663,14 +529,6 @@ static void alc256_shutup(struct hda_codec *codec) msleep(75); - /* 3k pull low control for Headset jack. */ - /* NOTE: call this before clearing the pin, otherwise codec stalls */ - /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly - * when booting with headset plugged. So skip setting it for the codec alc257 - */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - if (!spec->no_shutup_pins) snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); @@ -4135,7 +993,7 @@ static int alc269_resume(struct hda_codec *codec) msleep(150); } - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); if (spec->codec_variant == ALC269_TYPE_ALC269VB) alc269vb_toggle_power_output(codec, 1); @@ -4189,15 +1047,6 @@ static void alc269_fixup_hweq(struct hda_codec *codec, alc_update_coef_idx(codec, 0x1e, 0, 0x80); } -static void alc269_fixup_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; -} - static void alc271_fixup_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4468,61 +1317,6 @@ static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b); } -/* update LED status via GPIO */ -static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, - int polarity, bool enabled) -{ - if (polarity) - enabled = !enabled; - alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ -} - -/* turn on/off mute LED via GPIO per vmaster hook */ -static int gpio_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mute_led_mask, - spec->mute_led_polarity, !brightness); - return 0; -} - -/* turn on/off mic-mute LED via GPIO per capture hook */ -static int micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mic_led_mask, - spec->micmute_led_polarity, !brightness); - return 0; -} - -/* setup mute and mic-mute GPIO bits, add hooks appropriately */ -static void alc_fixup_hp_gpio_led(struct hda_codec *codec, - int action, - unsigned int mute_mask, - unsigned int micmute_mask) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_gpio(codec, action, mute_mask | micmute_mask); - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - if (mute_mask) { - spec->gpio_mute_led_mask = mute_mask; - snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); - } - if (micmute_mask) { - spec->gpio_mic_led_mask = micmute_mask; - snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); - } -} - static void alc236_fixup_hp_gpio_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4752,7 +1546,7 @@ static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_polarity = 0; spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 1 << 3; + spec->mute_led_coef.mask = 3 << 2; spec->mute_led_coef.on = 1 << 3; spec->mute_led_coef.off = 0; snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); @@ -5165,985 +1959,6 @@ static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec, alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); } -static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(delay); -} - -static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - msleep(delay); -} - -static const struct coef_fw alc225_pre_hsmode[] = { - UPDATE_COEF(0x4a, 1<<8, 0), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), - UPDATE_COEF(0x63, 3<<14, 3<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x4a, 3<<10, 3<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), - UPDATE_COEF(0x4a, 3<<10, 0), - {} -}; - -static void alc_headset_mode_unplugged(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x1b, 0x0c0b), - WRITE_COEF(0x45, 0xc429), - UPDATE_COEF(0x35, 0x4000, 0), - WRITE_COEF(0x06, 0x2104), - WRITE_COEF(0x1a, 0x0001), - WRITE_COEF(0x26, 0x0004), - WRITE_COEF(0x32, 0x42a3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x19, 0x1300, 0x0300), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x18, 0x7308), - WRITE_COEF(0x6b, 0xc429), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ - UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ - UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ - WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - {} - }; - static const struct coef_fw coef0668[] = { - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0100, 0), - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), - UPDATE_COEF(0x6b, 0xf000, 0x5000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x0c00, 0x0c00), - WRITE_COEF(0x45, 0x5289), - UPDATE_COEF(0x4a, 0x0c00, 0), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, coef0256); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0298: - alc_process_coef_fw(codec, coef0298); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0668); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to unplugged mode.\n"); -} - - -static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, - hda_nid_t mic_pin) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEFEX(0x57, 0x03, 0x8aa6), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0256[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEFEX(0x57, 0x03, 0x09a3), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0233[] = { - UPDATE_COEF(0x35, 0, 1<<14), - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x1a, 0x0021), - WRITE_COEF(0x26, 0x008c), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0x00c0, 0), - UPDATE_COEF(0x50, 0x2000, 0), - UPDATE_COEF(0x56, 0x0006, 0), - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x66, 0x0008, 0x0008), - UPDATE_COEF(0x67, 0x2000, 0x2000), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x19, 0xa208), - WRITE_COEF(0x2e, 0xacf0), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0xb5, 0x1040), - UPDATE_COEF(0xc3, 0, 1<<12), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x6b, 0xf000, 0), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0255); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0256); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0x4689); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0274); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0233); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0288); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0292: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - /* Set to TRS mode */ - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0293); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); - fallthrough; - case 0x10ec0221: - case 0x10ec0662: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0688); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0225); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - } - codec_dbg(codec, "Headset jack set to mic-in mode.\n"); -} - -static void alc_headset_mode_default(struct hda_codec *codec) -{ - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10), - UPDATE_COEF(0x49, 3<<8, 0<<8), - UPDATE_COEF(0x4a, 3<<4, 3<<4), - UPDATE_COEF(0x63, 3<<14, 0), - UPDATE_COEF(0x67, 0xf000, 0x3000), - {} - }; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xc089), - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - WRITE_COEF(0x49, 0x0049), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x0da3), - WRITE_COEF(0x49, 0x0049), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6100), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x6b, 0xc429), - WRITE_COEF(0x18, 0x7308), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0041), - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0274[] = { - WRITE_COEF(0x45, 0x4289), - UPDATE_COEF(0x4a, 0x0010, 0x0010), - UPDATE_COEF(0x6b, 0x0f00, 0), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x45, 0xc089); - msleep(50); - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); -} - -/* Iphone type */ -static void alc_headset_mode_ctia(struct hda_codec *codec) -{ - int val; - - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xd429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xd429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225_1[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - static const struct coef_fw coef0225_2[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 1<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xd689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) - alc_process_coef_fw(codec, coef0225_2); - else - alc_process_coef_fw(codec, coef0225_1); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); -} - -/* Nokia type */ -static void alc_headset_mode_omtp(struct hda_codec *codec) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xe429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xe429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d50), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xe689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - } - codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); -} - -static void alc_determine_headset_type(struct hda_codec *codec) -{ - int val; - bool is_ctia = false; - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ - WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref - conteol) */ - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - UPDATE_COEF(0x19, 0x1300, 0x1300), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0c00), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x8000, 0), - WRITE_COEF(0x45, 0xd289), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x06, 0x6104); - alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); - - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - if (!is_ctia) { - alc_write_coef_idx(codec, 0x45, 0xe089); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x0070) == 0x0070) - is_ctia = false; - else - is_ctia = true; - } - alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - msleep(850); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xd029); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0298: - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(100); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(200); - - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } - alc_process_coef_fw(codec, coef0298); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - msleep(75); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0292: - alc_write_coef_idx(codec, 0x6b, 0xd429); - msleep(300); - val = alc_read_coef_idx(codec, 0x6c); - is_ctia = (val & 0x001c) == 0x001c; - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - msleep(300); - val = alc_read_coef_idx(codec, 0xbe); - is_ctia = (val & 0x1c02) == 0x1c02; - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } else { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } - if (!is_ctia) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x00f0) == 0x00f0) - is_ctia = false; - else - is_ctia = true; - } - alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); - alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - break; - case 0x10ec0867: - is_ctia = true; - break; - } - - codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", - str_yes_no(is_ctia)); - spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; -} - -static void alc_update_headset_mode(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - - int new_headset_mode; - - if (!snd_hda_jack_detect(codec, hp_pin)) - new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; - else if (mux_pin == spec->headset_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_HEADSET; - else if (mux_pin == spec->headphone_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_MIC; - else - new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; - - if (new_headset_mode == spec->current_headset_mode) { - snd_hda_gen_update_outputs(codec); - return; - } - - switch (new_headset_mode) { - case ALC_HEADSET_MODE_UNPLUGGED: - alc_headset_mode_unplugged(codec); - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADSET: - if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) - alc_determine_headset_type(codec); - if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) - alc_headset_mode_ctia(codec); - else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) - alc_headset_mode_omtp(codec); - spec->gen.hp_jack_present = true; - break; - case ALC_HEADSET_MODE_MIC: - alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADPHONE: - alc_headset_mode_default(codec); - spec->gen.hp_jack_present = true; - break; - } - if (new_headset_mode != ALC_HEADSET_MODE_MIC) { - snd_hda_set_pin_ctl_cache(codec, hp_pin, - AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin) - snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, - PIN_VREFHIZ); - } - spec->current_headset_mode = new_headset_mode; - - snd_hda_gen_update_outputs(codec); -} - -static void alc_update_headset_mode_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - alc_update_headset_mode(codec); -} - -static void alc_update_headset_jack_cb(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - snd_hda_gen_hp_automute(codec, jack); - alc_update_headset_mode(codec); -} - -static void alc_probe_headset_mode(struct hda_codec *codec) -{ - int i; - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - - /* Find mic pins */ - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) - spec->headset_mic_pin = cfg->inputs[i].pin; - if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) - spec->headphone_mic_pin = cfg->inputs[i].pin; - } - - WARN_ON(spec->gen.cap_sync_hook); - spec->gen.cap_sync_hook = alc_update_headset_mode_hook; - spec->gen.automute_hook = alc_update_headset_mode; - spec->gen.hp_automute_hook = alc_update_headset_jack_cb; -} - -static void alc_fixup_headset_mode(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; - break; - case HDA_FIXUP_ACT_PROBE: - alc_probe_headset_mode(codec); - break; - case HDA_FIXUP_ACT_INIT: - if (is_s3_resume(codec) || is_s4_resume(codec)) { - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - } - alc_update_headset_mode(codec); - break; - } -} - -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - } - else - alc_fixup_headset_mode(codec, fix, action); -} - static void alc255_set_default_jack_type(struct hda_codec *codec) { /* Set to iphone type */ @@ -6221,15 +2036,6 @@ static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, } } -static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->gen.auto_mute_via_amp = 1; - } -} - static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -6239,16 +2045,6 @@ static void alc_fixup_no_shutup(struct hda_codec *codec, } } -static void alc_fixup_disable_aamix(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - /* Disable AA-loopback as it causes white noise */ - spec->gen.mixer_nid = 0; - } -} - /* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ static void alc_fixup_tpt440_dock(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -6319,94 +2115,6 @@ static void alc295_fixup_asus_dacs(struct hda_codec *codec, spec->gen.preferred_dacs = preferred_pairs; } -static void alc_shutup_dell_xps13(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int hp_pin = alc_get_hp_pin(spec); - - /* Prevent pop noises when headphones are plugged in */ - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(20); -} - -static void alc_fixup_dell_xps13(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - int i; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise - * it causes a click noise at start up - */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); - spec->shutup = alc_shutup_dell_xps13; - break; - case HDA_FIXUP_ACT_PROBE: - /* Make the internal mic the default input source. */ - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == 0x12) { - spec->gen.cur_mux[0] = i; - break; - } - } - break; - } -} - -static void alc_fixup_headset_mode_alc662(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */ - - /* Disable boost for mic-in permanently. (This code is only called - from quirks that guarantee that the headphone is at NID 0x1b.) */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000); - snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP); - } else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - alc_write_coef_idx(codec, 0xc4, 0x8000); - alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } - alc_fixup_headset_mode(codec, fix, action); -} - -/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ -static int find_ext_mic_pin(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t nid; - unsigned int defcfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - nid = cfg->inputs[i].pin; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) - continue; - return nid; - } - - return 0; -} - static void alc271_hp_gate_mic_jack(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -6414,7 +2122,7 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PROBE) { - int mic_pin = find_ext_mic_pin(codec); + int mic_pin = alc_find_ext_mic_pin(codec); int hp_pin = alc_get_hp_pin(spec); if (snd_BUG_ON(!mic_pin || !hp_pin)) @@ -6609,6 +2317,7 @@ static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { static const hda_nid_t conn[] = { 0x02, 0x03 }; snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + snd_hda_gen_add_micmute_led_cdev(codec, NULL); } } @@ -6691,30 +2400,6 @@ static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec, } } -static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } -} - static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -7117,7 +2802,7 @@ static void alc285_fixup_hp_beep(struct hda_codec *codec, } /* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" +#include "../helpers/thinkpad.c" static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -7127,7 +2812,7 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, } /* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" +#include "../helpers/ideapad_hotkey_led.c" static void alc_fixup_ideapad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -7296,6 +2981,11 @@ static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixu comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } +static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1); +} + static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); @@ -7323,7 +3013,7 @@ static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fix alc_fixup_inv_dmic(cdc, fix, action); } -static void tas2781_fixup_i2c(struct hda_codec *cdc, +static void tas2781_fixup_tias_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); @@ -7334,6 +3024,12 @@ static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); } +static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1); +} + static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { @@ -7348,10 +3044,10 @@ static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, /* for alc295_fixup_hp_top_speakers */ -#include "hp_x360_helper.c" +#include "../helpers/hp_x360.c" /* for alc285_fixup_ideapad_s740_coef() */ -#include "ideapad_s740_helper.c" +#include "../helpers/ideapad_s740.c" static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), @@ -7495,6 +3191,9 @@ static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, }; struct alc_spec *spec = codec->spec; + /* Support Audio mute LED and Mic mute LED on keyboard */ + hda_fixup_ideapad_acpi(codec, fix, action); + switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_apply_pincfgs(codec, pincfgs); @@ -7553,9 +3252,10 @@ static void alc287_fixup_bind_dacs(struct hda_codec *codec, 0x0); /* Make sure 0x14 was disable */ } } + /* Fix none verb table of Headset Mic pin */ -static void alc_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc2xx_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static const struct hda_pintbl pincfgs[] = { @@ -7880,6 +3580,7 @@ enum { ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, ALC294_FIXUP_ASUS_MIC, ALC294_FIXUP_ASUS_HEADSET_MIC, + ALC294_FIXUP_ASUS_I2C_HEADSET_MIC, ALC294_FIXUP_ASUS_SPK, ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE, ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, @@ -7986,6 +3687,7 @@ enum { ALC287_FIXUP_CS35L41_I2C_2, ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, ALC287_FIXUP_CS35L41_I2C_4, + ALC245_FIXUP_CS35L41_SPI_1, ALC245_FIXUP_CS35L41_SPI_2, ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED, ALC245_FIXUP_CS35L41_SPI_4, @@ -8001,6 +3703,7 @@ enum { ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, ALC245_FIXUP_TAS2781_SPI_2, + ALC287_FIXUP_TXNW2781_I2C, ALC287_FIXUP_YOGA7_14ARB7_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, @@ -8030,6 +3733,9 @@ enum { ALC294_FIXUP_ASUS_CS35L41_SPI_2, ALC274_FIXUP_HP_AIO_BIND_DACS, ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, + ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, + ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, + ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -8145,7 +3851,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC269_FIXUP_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, + .v.func = alc_fixup_headset_mic, }, [ALC269_FIXUP_QUANTA_MUTE] = { .type = HDA_FIXUP_FUNC, @@ -9185,6 +4891,15 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC294_FIXUP_ASUS_I2C_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a19020 }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2 + }, [ALC294_FIXUP_ASUS_SPK] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -10112,6 +5827,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_spi_two, }, + [ALC245_FIXUP_CS35L41_SPI_1] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_one, + }, [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_spi_two, @@ -10251,7 +5970,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC287_FIXUP_TAS2781_I2C] = { .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_i2c, + .v.func = tas2781_fixup_tias_i2c, .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, @@ -10261,6 +5980,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, + [ALC287_FIXUP_TXNW2781_I2C] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_txnw_i2c, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, + }, [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { .type = HDA_FIXUP_FUNC, .v.func = yoga7_14arb7_fixup_i2c, @@ -10295,7 +6020,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC2XX_FIXUP_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mic, + .v.func = alc2xx_fixup_headset_mic, }, [ALC289_FIXUP_DELL_CS35L41_SPI_2] = { .type = HDA_FIXUP_FUNC, @@ -10414,6 +6139,26 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc274_fixup_hp_aio_bind_dacs, }, + [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 + }, + [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + }, + [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -10509,6 +6254,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), @@ -10633,6 +6379,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), 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, 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, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), @@ -10665,6 +6413,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87cc, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), @@ -10713,6 +6462,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), + SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), @@ -10733,6 +6483,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a26, "HP Victus 16-d1xxx (MB 8A26)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), @@ -10742,6 +6493,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), @@ -10787,7 +6539,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), @@ -10840,6 +6595,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c99, "HP Victus 16-r1xxx (MB 8C99)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -10852,6 +6609,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d07, "HP Victus 15-fb2xxx (MB 8D07)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), @@ -10881,7 +6639,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfb, "HP EliteBook 6 G1a 14", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), @@ -10904,6 +6664,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), @@ -10932,9 +6694,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1454, "ASUS PM3406CKA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -10971,12 +6735,13 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2), @@ -10997,6 +6762,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), @@ -11005,6 +6771,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), @@ -11026,6 +6793,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), @@ -11049,6 +6817,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), + SND_PCI_QUIRK(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), @@ -11106,6 +6875,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x35a1, "Clevo V3[56]0EN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x35b1, "Clevo V3[57]0WN[MNP]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -11133,6 +6904,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -11172,6 +6944,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC), + SND_PCI_QUIRK(0x1558, 0xa743, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -11318,8 +7091,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { 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), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), @@ -11350,6 +7123,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL), SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), @@ -11373,17 +7147,22 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), 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, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), 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), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), + SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), @@ -11395,6 +7174,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), 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), #if 0 @@ -12091,9 +7871,19 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4, 0, 1<<11); } +static void alc269_remove(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec) + hda_component_manager_free(&spec->comps, &comp_master_ops); + + snd_hda_gen_remove(codec); +} + /* */ -static int patch_alc269(struct hda_codec *codec) +static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -12107,8 +7897,6 @@ static int patch_alc269(struct hda_codec *codec) codec->power_save_node = 0; spec->en_3kpull_low = true; - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; spec->shutup = alc_default_shutup; spec->init_hook = alc_default_init; @@ -12306,1421 +8094,81 @@ static int patch_alc269(struct hda_codec *codec) return 0; error: - alc_free(codec); + alc269_remove(codec); return err; } -/* - * ALC861 - */ - -static int alc861_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; - return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); -} - -/* Pin config fixes */ -enum { - ALC861_FIXUP_FSC_AMILO_PI1505, - ALC861_FIXUP_AMP_VREF_0F, - ALC861_FIXUP_NO_JACK_DETECT, - ALC861_FIXUP_ASUS_A6RP, - ALC660_FIXUP_ASUS_W7J, -}; - -/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ -static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (action != HDA_FIXUP_ACT_INIT) - return; - val = snd_hda_codec_get_pin_target(codec, 0x0f); - if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) - val |= AC_PINCTL_IN_EN; - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->gen.keep_vref_in_automute = 1; -} - -/* suppress the jack-detection */ -static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - -static const struct hda_fixup alc861_fixups[] = { - [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ - { } - } - }, - [ALC861_FIXUP_AMP_VREF_0F] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - }, - [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC861_FIXUP_ASUS_A6RP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - .chained = true, - .chain_id = ALC861_FIXUP_NO_JACK_DETECT, - }, - [ALC660_FIXUP_ASUS_W7J] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* ASUS W7J needs a magic pin setup on unused NID 0x10 - * for enabling outputs - */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - { } - }, - } -}; - -static const struct hda_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), - {} +static const struct hda_codec_ops alc269_codec_ops = { + .probe = alc269_probe, + .remove = alc269_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = alc269_suspend, + .resume = alc269_resume, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, }; /* + * driver entries */ -static int patch_alc861(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x15); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->power_hook = alc_power_eapd; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861-VD support - * - * Based on ALC882 - * - * In addition, an independent DAC - */ -static int alc861vd_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); -} - -enum { - ALC660VD_FIX_ASUS_GPIO1, - ALC861VD_FIX_DALLAS, -}; - -/* exclude VREF80 */ -static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_override_pin_caps(codec, 0x18, 0x00000734); - snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); - } -} - -/* reset GPIO1 */ -static void alc660vd_fixup_asus_gpio1(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->gpio_mask |= 0x02; - alc_fixup_gpio(codec, action, 0x01); -} - -static const struct hda_fixup alc861vd_fixups[] = { - [ALC660VD_FIX_ASUS_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc660vd_fixup_asus_gpio1, - }, - [ALC861VD_FIX_DALLAS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861vd_fixup_dallas, - }, -}; - -static const struct hda_quirk alc861vd_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), - {} -}; - -/* - */ -static int patch_alc861vd(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861vd_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC662 support - * - * ALC662 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * BIOS auto configuration - */ - -static int alc662_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - const hda_nid_t *ssids; - - if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || - codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || - codec->core.vendor_id == 0x10ec0671) - ssids = alc663_ssids; - else - ssids = alc662_ssids; - return alc_parse_auto_config(codec, alc662_ignore, ssids); -} - -static void alc272_fixup_mario(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, - (0x3b << AC_AMPCAP_OFFSET_SHIFT) | - (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT))) - codec_warn(codec, "failed to override amp caps for NID 0x2\n"); -} - -static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { - { .channels = 2, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, - { .channels = 4, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ - { } -}; - -/* override the 2.1 chmap */ -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_BUILD) { - struct alc_spec *spec = codec->spec; - spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; - } -} - -/* avoid D3 for keeping GPIO up */ -static unsigned int gpio_led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - struct alc_spec *spec = codec->spec; - if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) - return AC_PWRST_D0; - return power_state; -} - -static void alc662_fixup_led_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x01, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 1; - codec->power_filter = gpio_led_power_filter; - } -} - -static void alc662_usi_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - msleep(100); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -static void alc662_fixup_usi_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc662_usi_automute_hook; - } -} - -static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* surround speakers at 0x1b already get muted automatically when - * headphones are plugged in, but we have to mute/unmute the remaining - * channels manually: - * 0x15 - front left/front right - * 0x18 - front center/ LFE - */ - if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { - snd_hda_set_pin_ctl_cache(codec, 0x15, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } else { - snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); - snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); - } -} - -static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Pin 0x1b: shared headphones jack and surround speakers */ - if (!is_jack_detectable(codec, 0x1b)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x1b, - alc662_aspire_ethos_mute_speakers); - /* subwoofer needs an extra GPIO setting to become audible */ - alc_setup_gpio(codec, 0x02); - break; - case HDA_FIXUP_ACT_INIT: - /* Make sure to start in a correct state, i.e. if - * headphones have been plugged in before powering up the system - */ - alc662_aspire_ethos_mute_speakers(codec, NULL); - break; - } -} - -static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - static const struct hda_pintbl pincfgs[] = { - { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */ - { 0x1b, 0x0181304f }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.mixer_nid = 0; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - snd_hda_apply_pincfgs(codec, pincfgs); - break; - case HDA_FIXUP_ACT_INIT: - alc_write_coef_idx(codec, 0x19, 0xa054); - break; - } -} - -static void alc897_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - - snd_hda_gen_hp_automute(codec, jack); - vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP; - snd_hda_set_pin_ctl(codec, 0x1b, vref); -} - -static void alc897_fixup_lenovo_headset_mic(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.hp_automute_hook = alc897_hp_automute_hook; - spec->no_shutup_pins = 1; - } - if (action == HDA_FIXUP_ACT_PROBE) { - snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100); - } -} - -static void alc897_fixup_lenovo_headset_mode(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc897_hp_automute_hook; - } -} - -static const struct coef_fw alc668_coefs[] = { - WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), - WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), - WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0), - WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f), - WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940), - WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0), - WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418), - WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468), - WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418), - WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00), - WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000), - WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0), - WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480), - WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0), - WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040), - WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697), - WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab), - WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02), - WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6), - {} -}; - -static void alc668_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc668_coefs); -} - -enum { - ALC662_FIXUP_ASPIRE, - ALC662_FIXUP_LED_GPIO1, - ALC662_FIXUP_IDEAPAD, - ALC272_FIXUP_MARIO, - ALC662_FIXUP_CZC_ET26, - ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_SKU_IGNORE, - ALC662_FIXUP_HP_RP5800, - ALC662_FIXUP_ASUS_MODE1, - ALC662_FIXUP_ASUS_MODE2, - ALC662_FIXUP_ASUS_MODE3, - ALC662_FIXUP_ASUS_MODE4, - ALC662_FIXUP_ASUS_MODE5, - ALC662_FIXUP_ASUS_MODE6, - ALC662_FIXUP_ASUS_MODE7, - ALC662_FIXUP_ASUS_MODE8, - ALC662_FIXUP_NO_JACK_DETECT, - ALC662_FIXUP_ZOTAC_Z68, - ALC662_FIXUP_INV_DMIC, - ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - ALC668_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_HEADSET_MODE, - ALC668_FIXUP_HEADSET_MODE, - ALC662_FIXUP_BASS_MODE4_CHMAP, - ALC662_FIXUP_BASS_16, - ALC662_FIXUP_BASS_1A, - ALC662_FIXUP_BASS_CHMAP, - ALC668_FIXUP_AUTO_MUTE, - ALC668_FIXUP_DELL_DISABLE_AAMIX, - ALC668_FIXUP_DELL_XPS13, - ALC662_FIXUP_ASUS_Nx50, - ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, - ALC668_FIXUP_ASUS_Nx51, - ALC668_FIXUP_MIC_COEF, - ALC668_FIXUP_ASUS_G751, - ALC891_FIXUP_HEADSET_MODE, - ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_ACER_VERITON, - ALC892_FIXUP_ASROCK_MOBO, - ALC662_FIXUP_USI_FUNC, - ALC662_FIXUP_USI_HEADSET_MODE, - ALC662_FIXUP_LENOVO_MULTI_CODECS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, - ALC671_FIXUP_HP_HEADSET_MIC2, - ALC662_FIXUP_ACER_X2660G_HEADSET_MODE, - ALC662_FIXUP_ACER_NITRO_HEADSET_MODE, - ALC668_FIXUP_ASUS_NO_HEADSET_MIC, - ALC668_FIXUP_HEADSET_MIC, - ALC668_FIXUP_MIC_DET_COEF, - ALC897_FIXUP_LENOVO_HEADSET_MIC, - ALC897_FIXUP_HEADSET_MIC_PIN, - ALC897_FIXUP_HP_HSMIC_VERB, - ALC897_FIXUP_LENOVO_HEADSET_MODE, - ALC897_FIXUP_HEADSET_MIC_PIN2, - ALC897_FIXUP_UNIS_H3C_X500S, - ALC897_FIXUP_HEADSET_MIC_PIN3, -}; - -static const struct hda_fixup alc662_fixups[] = { - [ALC662_FIXUP_ASPIRE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* subwoofer */ - { } - } - }, - [ALC662_FIXUP_LED_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_led_gpio1, - }, - [ALC662_FIXUP_IDEAPAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x99130112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_LED_GPIO1, - }, - [ALC272_FIXUP_MARIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc272_fixup_mario, - }, - [ALC662_FIXUP_CZC_ET26] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x12, 0x403cc000}, - {0x14, 0x90170110}, /* speaker */ - {0x15, 0x411111f0}, - {0x16, 0x411111f0}, - {0x18, 0x01a19030}, /* mic */ - {0x19, 0x90a7013f}, /* int-mic */ - {0x1a, 0x01014020}, - {0x1b, 0x0121401f}, - {0x1c, 0x411111f0}, - {0x1d, 0x411111f0}, - {0x1e, 0x40478e35}, - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_CZC_P10T] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC662_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC662_FIXUP_HP_RP5800] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0221201f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19820 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x1b, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x01211420 }, /* HP2 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE4] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE5] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE6] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x01211420 }, /* HP2 */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE7] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x01214020 }, /* HP */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE8] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x12, 0x99a30970 }, /* int-mic */ - { 0x15, 0x01214020 }, /* HP */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC662_FIXUP_ZOTAC_Z68] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214020 }, /* Front HP */ - { } - } - }, - [ALC662_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC668_FIXUP_DELL_XPS13] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX - }, - [ALC668_FIXUP_DELL_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE - }, - [ALC668_FIXUP_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE - }, - [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_HEADSET_MODE - }, - [ALC662_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc662, - }, - [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MODE - }, - [ALC668_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, - }, - [ALC662_FIXUP_BASS_MODE4_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - .chained = true, - .chain_id = ALC662_FIXUP_ASUS_MODE4 - }, - [ALC662_FIXUP_BASS_16] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x80106111}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, - }, - [ALC662_FIXUP_BASS_1A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x1a, 0x80106111}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, - }, - [ALC662_FIXUP_BASS_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - }, - [ALC662_FIXUP_ASUS_Nx50] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_1A - }, - [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, - .chain_id = ALC662_FIXUP_BASS_CHMAP - }, - [ALC668_FIXUP_ASUS_Nx51] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1a, 0x90170151 }, /* bass speaker */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - {} - }, - .chained = true, - .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, - }, - [ALC668_FIXUP_MIC_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, - {} - }, - }, - [ALC668_FIXUP_ASUS_G751] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x0421101f }, /* HP */ - {} - }, - .chained = true, - .chain_id = ALC668_FIXUP_MIC_COEF - }, - [ALC891_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - }, - [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC891_FIXUP_HEADSET_MODE - }, - [ALC662_FIXUP_ACER_VERITON] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x50170120 }, /* no internal speaker */ - { } - } - }, - [ALC892_FIXUP_ASROCK_MOBO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x40f000f0 }, /* disabled */ - { 0x16, 0x40f000f0 }, /* disabled */ - { } - } - }, - [ALC662_FIXUP_USI_FUNC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_usi_headset_mic, - }, - [ALC662_FIXUP_USI_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ - { 0x18, 0x01a1903d }, - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC662_FIXUP_LENOVO_MULTI_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_alc662_fixup_lenovo_dual_codecs, - }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_aspire_ethos_hp, - }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x92130110 }, /* front speakers */ - { 0x18, 0x99130111 }, /* center/subwoofer */ - { 0x1b, 0x11130012 }, /* surround plus jack for HP */ - { } - }, - .chained = true, - .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET - }, - [ALC671_FIXUP_HP_HEADSET_MIC2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc671_fixup_hp_headset_mic2, - }, - [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x0221144f }, - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x04a1112c }, - { } - }, - .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MIC - }, - [ALC668_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, - .chained = true, - .chain_id = ALC668_FIXUP_MIC_DET_COEF - }, - [ALC668_FIXUP_MIC_DET_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 }, - {} - }, - }, - [ALC897_FIXUP_LENOVO_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mic, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x03a11050 }, - { } - }, - .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC - }, - [ALC897_FIXUP_HP_HSMIC_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - }, - [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mode, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE - }, - [ALC897_FIXUP_UNIS_H3C_X500S] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 }, - {} - }, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* use as headset mic */ - { } - }, - }, -}; - -static const struct hda_quirk alc662_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), - SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS), - SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE), - SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE), - SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), - SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), - SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), - SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), - SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), - SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), - SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), - SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), - SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), - SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), - SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), -#endif - {} -}; - -static const struct hda_model_fixup alc662_fixup_models[] = { - {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, - {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, - {.id = ALC272_FIXUP_MARIO, .name = "mario"}, - {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, - {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, - {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, - {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, - {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, - {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, - {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, - {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, - {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, - {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, - {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, - {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, - {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, - {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, - {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, - {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, - {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, - {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, - {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, - {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, - {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, - {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, - {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, - {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, - {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, - {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, - {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, - {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, - {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"}, - {} -}; - -static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x17, 0x02211010}, - {0x18, 0x01a19030}, - {0x1a, 0x01813040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x16, 0x01813030}, - {0x17, 0x02211010}, - {0x18, 0x01a19040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - {0x14, 0x01014010}, - {0x18, 0x01a19020}, - {0x1a, 0x0181302f}, - {0x1b, 0x0221401f}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30140}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30150}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x17, 0x90170150}, - {0x19, 0x02a11060}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x18, 0x01a19040}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014020}, - {0x17, 0x90170110}, - {0x18, 0x01a19050}, - {0x1b, 0x01813040}, - {0x21, 0x02211030}), - {} -}; - -/* - */ -static int patch_alc662(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - - spec->shutup = alc_eapd_shutup; - - /* handle multiple HPs as is */ - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - - alc_fix_pll_init(codec, 0x20, 0x04, 15); - - switch (codec->core.vendor_id) { - case 0x10ec0668: - spec->init_hook = alc668_restore_default_value; - break; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc662_fixup_models, - alc662_fixup_tbl, alc662_fixups); - snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - if ((alc_get_coef0(codec) & (1 << 14)) && - codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) { - err = alc_codec_rename(codec, "ALC272X"); - if (err < 0) - goto error; - } - - /* automatic parse from the BIOS config */ - err = alc662_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->core.vendor_id) { - case 0x10ec0662: - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - break; - case 0x10ec0272: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0668: - err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - break; - case 0x10ec0273: - err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); - break; - } - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC680 support - */ - -static int alc680_parse_auto_config(struct hda_codec *codec) -{ - return alc_parse_auto_config(codec, NULL, NULL); -} - -/* - */ -static int patch_alc680(struct hda_codec *codec) -{ - int err; - - /* ALC680 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; - - /* automatic parse from the BIOS config */ - err = alc680_parse_auto_config(codec); - if (err < 0) { - alc_free(codec); - return err; - } - - return 0; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_realtek[] = { - HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0230, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), - HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), - HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269), - HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), - HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), - HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0711, "ALC711", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), - HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x19e58326, "HW8326", patch_alc269), +static const struct hda_device_id snd_hda_id_alc269[] = { + HDA_CODEC_ID(0x10ec0215, "ALC215"), + HDA_CODEC_ID(0x10ec0221, "ALC221"), + HDA_CODEC_ID(0x10ec0222, "ALC222"), + HDA_CODEC_ID(0x10ec0225, "ALC225"), + HDA_CODEC_ID(0x10ec0230, "ALC236"), + HDA_CODEC_ID(0x10ec0231, "ALC231"), + HDA_CODEC_ID(0x10ec0233, "ALC233"), + HDA_CODEC_ID(0x10ec0234, "ALC234"), + HDA_CODEC_ID(0x10ec0235, "ALC233"), + HDA_CODEC_ID(0x10ec0236, "ALC236"), + HDA_CODEC_ID(0x10ec0245, "ALC245"), + HDA_CODEC_ID(0x10ec0255, "ALC255"), + HDA_CODEC_ID(0x10ec0256, "ALC256"), + HDA_CODEC_ID(0x10ec0257, "ALC257"), + HDA_CODEC_ID(0x10ec0269, "ALC269"), + HDA_CODEC_ID(0x10ec0270, "ALC270"), + HDA_CODEC_ID(0x10ec0274, "ALC274"), + HDA_CODEC_ID(0x10ec0275, "ALC275"), + HDA_CODEC_ID(0x10ec0276, "ALC276"), + HDA_CODEC_ID(0x10ec0280, "ALC280"), + HDA_CODEC_ID(0x10ec0282, "ALC282"), + HDA_CODEC_ID(0x10ec0283, "ALC283"), + HDA_CODEC_ID(0x10ec0284, "ALC284"), + HDA_CODEC_ID(0x10ec0285, "ALC285"), + HDA_CODEC_ID(0x10ec0286, "ALC286"), + HDA_CODEC_ID(0x10ec0287, "ALC287"), + HDA_CODEC_ID(0x10ec0288, "ALC288"), + HDA_CODEC_ID(0x10ec0289, "ALC289"), + HDA_CODEC_ID(0x10ec0290, "ALC290"), + HDA_CODEC_ID(0x10ec0292, "ALC292"), + HDA_CODEC_ID(0x10ec0293, "ALC293"), + HDA_CODEC_ID(0x10ec0294, "ALC294"), + HDA_CODEC_ID(0x10ec0295, "ALC295"), + HDA_CODEC_ID(0x10ec0298, "ALC298"), + HDA_CODEC_ID(0x10ec0299, "ALC299"), + HDA_CODEC_ID(0x10ec0300, "ALC300"), + HDA_CODEC_ID(0x10ec0623, "ALC623"), + HDA_CODEC_ID(0x10ec0700, "ALC700"), + HDA_CODEC_ID(0x10ec0701, "ALC701"), + HDA_CODEC_ID(0x10ec0703, "ALC703"), + HDA_CODEC_ID(0x10ec0711, "ALC711"), + HDA_CODEC_ID(0x19e58326, "HW8326"), {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc269); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Realtek HD-audio codec"); +MODULE_DESCRIPTION("Realtek ALC269 and compatible HD-audio codecs"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); -static struct hda_codec_driver realtek_driver = { - .id = snd_hda_id_realtek, +static struct hda_codec_driver alc269_driver = { + .id = snd_hda_id_alc269, + .ops = &alc269_codec_ops, }; -module_hda_codec_driver(realtek_driver); +module_hda_codec_driver(alc269_driver); diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c new file mode 100644 index 000000000000..5073165d1f3c --- /dev/null +++ b/sound/hda/codecs/realtek/alc662.c @@ -0,0 +1,1116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC662 and compatible codecs +// + +#include +#include +#include "realtek.h" + +/* + * ALC662 support + * + * ALC662 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ + +/* + * BIOS auto configuration + */ + +static int alc662_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + const hda_nid_t *ssids; + + if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || + codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || + codec->core.vendor_id == 0x10ec0671) + ssids = alc663_ssids; + else + ssids = alc662_ssids; + return alc_parse_auto_config(codec, alc662_ignore, ssids); +} + +static void alc272_fixup_mario(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, + (0x3b << AC_AMPCAP_OFFSET_SHIFT) | + (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT))) + codec_warn(codec, "failed to override amp caps for NID 0x2\n"); +} + +/* avoid D3 for keeping GPIO up */ +static unsigned int gpio_led_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + struct alc_spec *spec = codec->spec; + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) + return AC_PWRST_D0; + return power_state; +} + +static void alc662_fixup_led_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + alc_fixup_hp_gpio_led(codec, action, 0x01, 0); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 1; + codec->power_filter = gpio_led_power_filter; + } +} + +static void alc662_usi_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + msleep(200); + snd_hda_gen_hp_automute(codec, jack); + + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + msleep(100); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} + +static void alc662_fixup_usi_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc662_usi_automute_hook; + } +} + +static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, + struct hda_jack_callback *cb) +{ + /* surround speakers at 0x1b already get muted automatically when + * headphones are plugged in, but we have to mute/unmute the remaining + * channels manually: + * 0x15 - front left/front right + * 0x18 - front center/ LFE + */ + if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { + snd_hda_set_pin_ctl_cache(codec, 0x15, 0); + snd_hda_set_pin_ctl_cache(codec, 0x18, 0); + } else { + snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); + snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); + } +} + +static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Pin 0x1b: shared headphones jack and surround speakers */ + if (!is_jack_detectable(codec, 0x1b)) + return; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x1b, + alc662_aspire_ethos_mute_speakers); + /* subwoofer needs an extra GPIO setting to become audible */ + alc_setup_gpio(codec, 0x02); + break; + case HDA_FIXUP_ACT_INIT: + /* Make sure to start in a correct state, i.e. if + * headphones have been plugged in before powering up the system + */ + alc662_aspire_ethos_mute_speakers(codec, NULL); + break; + } +} + +static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + static const struct hda_pintbl pincfgs[] = { + { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */ + { 0x1b, 0x0181304f }, + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.mixer_nid = 0; + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + snd_hda_apply_pincfgs(codec, pincfgs); + break; + case HDA_FIXUP_ACT_INIT: + alc_write_coef_idx(codec, 0x19, 0xa054); + break; + } +} + +static void alc897_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + + snd_hda_gen_hp_automute(codec, jack); + vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP; + snd_hda_set_pin_ctl(codec, 0x1b, vref); +} + +static void alc897_fixup_lenovo_headset_mic(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.hp_automute_hook = alc897_hp_automute_hook; + spec->no_shutup_pins = 1; + } + if (action == HDA_FIXUP_ACT_PROBE) { + snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100); + } +} + +static void alc897_fixup_lenovo_headset_mode(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc897_hp_automute_hook; + } +} + +static const struct coef_fw alc668_coefs[] = { + WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), + WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), + WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0), + WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f), + WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940), + WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0), + WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418), + WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468), + WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418), + WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00), + WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000), + WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0), + WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480), + WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0), + WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040), + WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697), + WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab), + WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02), + WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6), + {} +}; + +static void alc668_restore_default_value(struct hda_codec *codec) +{ + alc_process_coef_fw(codec, alc668_coefs); +} + +static void alc_fixup_headset_mode_alc662(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */ + + /* Disable boost for mic-in permanently. (This code is only called + from quirks that guarantee that the headphone is at NID 0x1b.) */ + snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000); + snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP); + } else + alc_fixup_headset_mode(codec, fix, action); +} + +static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + alc_write_coef_idx(codec, 0xc4, 0x8000); + alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); + snd_hda_set_pin_ctl_cache(codec, 0x18, 0); + } + alc_fixup_headset_mode(codec, fix, action); +} + +enum { + ALC662_FIXUP_ASPIRE, + ALC662_FIXUP_LED_GPIO1, + ALC662_FIXUP_IDEAPAD, + ALC272_FIXUP_MARIO, + ALC662_FIXUP_CZC_ET26, + ALC662_FIXUP_CZC_P10T, + ALC662_FIXUP_SKU_IGNORE, + ALC662_FIXUP_HP_RP5800, + ALC662_FIXUP_ASUS_MODE1, + ALC662_FIXUP_ASUS_MODE2, + ALC662_FIXUP_ASUS_MODE3, + ALC662_FIXUP_ASUS_MODE4, + ALC662_FIXUP_ASUS_MODE5, + ALC662_FIXUP_ASUS_MODE6, + ALC662_FIXUP_ASUS_MODE7, + ALC662_FIXUP_ASUS_MODE8, + ALC662_FIXUP_NO_JACK_DETECT, + ALC662_FIXUP_ZOTAC_Z68, + ALC662_FIXUP_INV_DMIC, + ALC662_FIXUP_DELL_MIC_NO_PRESENCE, + ALC668_FIXUP_DELL_MIC_NO_PRESENCE, + ALC662_FIXUP_HEADSET_MODE, + ALC668_FIXUP_HEADSET_MODE, + ALC662_FIXUP_BASS_MODE4_CHMAP, + ALC662_FIXUP_BASS_16, + ALC662_FIXUP_BASS_1A, + ALC662_FIXUP_BASS_CHMAP, + ALC668_FIXUP_AUTO_MUTE, + ALC668_FIXUP_DELL_DISABLE_AAMIX, + ALC668_FIXUP_DELL_XPS13, + ALC662_FIXUP_ASUS_Nx50, + ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, + ALC668_FIXUP_ASUS_Nx51, + ALC668_FIXUP_MIC_COEF, + ALC668_FIXUP_ASUS_G751, + ALC891_FIXUP_HEADSET_MODE, + ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + ALC662_FIXUP_ACER_VERITON, + ALC892_FIXUP_ASROCK_MOBO, + ALC662_FIXUP_USI_FUNC, + ALC662_FIXUP_USI_HEADSET_MODE, + ALC662_FIXUP_LENOVO_MULTI_CODECS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, + ALC671_FIXUP_HP_HEADSET_MIC2, + ALC662_FIXUP_ACER_X2660G_HEADSET_MODE, + ALC662_FIXUP_ACER_NITRO_HEADSET_MODE, + ALC668_FIXUP_ASUS_NO_HEADSET_MIC, + ALC668_FIXUP_HEADSET_MIC, + ALC668_FIXUP_MIC_DET_COEF, + ALC897_FIXUP_LENOVO_HEADSET_MIC, + ALC897_FIXUP_HEADSET_MIC_PIN, + ALC897_FIXUP_HP_HSMIC_VERB, + ALC897_FIXUP_LENOVO_HEADSET_MODE, + ALC897_FIXUP_HEADSET_MIC_PIN2, + ALC897_FIXUP_UNIS_H3C_X500S, + ALC897_FIXUP_HEADSET_MIC_PIN3, +}; + +static const struct hda_fixup alc662_fixups[] = { + [ALC662_FIXUP_ASPIRE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x99130112 }, /* subwoofer */ + { } + } + }, + [ALC662_FIXUP_LED_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_led_gpio1, + }, + [ALC662_FIXUP_IDEAPAD] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x99130112 }, /* subwoofer */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_LED_GPIO1, + }, + [ALC272_FIXUP_MARIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc272_fixup_mario, + }, + [ALC662_FIXUP_CZC_ET26] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x403cc000}, + {0x14, 0x90170110}, /* speaker */ + {0x15, 0x411111f0}, + {0x16, 0x411111f0}, + {0x18, 0x01a19030}, /* mic */ + {0x19, 0x90a7013f}, /* int-mic */ + {0x1a, 0x01014020}, + {0x1b, 0x0121401f}, + {0x1c, 0x411111f0}, + {0x1d, 0x411111f0}, + {0x1e, 0x40478e35}, + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_CZC_P10T] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC662_FIXUP_SKU_IGNORE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, + }, + [ALC662_FIXUP_HP_RP5800] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0221201f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE1] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19c20 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19820 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { 0x1b, 0x0121401f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE3] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121441f }, /* HP */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x21, 0x01211420 }, /* HP2 */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE4] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x16, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x21, 0x0121441f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE5] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121441f }, /* HP */ + { 0x16, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE6] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x01211420 }, /* HP2 */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x1b, 0x0121441f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE7] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x17, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x1b, 0x01214020 }, /* HP */ + { 0x21, 0x0121401f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE8] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x12, 0x99a30970 }, /* int-mic */ + { 0x15, 0x01214020 }, /* HP */ + { 0x17, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x21, 0x0121401f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_NO_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC662_FIXUP_ZOTAC_Z68] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x02214020 }, /* Front HP */ + { } + } + }, + [ALC662_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC668_FIXUP_DELL_XPS13] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX + }, + [ALC668_FIXUP_DELL_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + }, + [ALC668_FIXUP_AUTO_MUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + }, + [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_HEADSET_MODE + }, + [ALC662_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc662, + }, + [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC668_FIXUP_HEADSET_MODE + }, + [ALC668_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc668, + }, + [ALC662_FIXUP_BASS_MODE4_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + .chained = true, + .chain_id = ALC662_FIXUP_ASUS_MODE4 + }, + [ALC662_FIXUP_BASS_16] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x16, 0x80106111}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_CHMAP, + }, + [ALC662_FIXUP_BASS_1A] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x1a, 0x80106111}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_CHMAP, + }, + [ALC662_FIXUP_BASS_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + }, + [ALC662_FIXUP_ASUS_Nx50] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_1A + }, + [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc668, + .chain_id = ALC662_FIXUP_BASS_CHMAP + }, + [ALC668_FIXUP_ASUS_Nx51] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1a, 0x90170151 }, /* bass speaker */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + {} + }, + .chained = true, + .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, + }, + [ALC668_FIXUP_MIC_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, + {} + }, + }, + [ALC668_FIXUP_ASUS_G751] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x0421101f }, /* HP */ + {} + }, + .chained = true, + .chain_id = ALC668_FIXUP_MIC_COEF + }, + [ALC891_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode, + }, + [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC891_FIXUP_HEADSET_MODE + }, + [ALC662_FIXUP_ACER_VERITON] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x50170120 }, /* no internal speaker */ + { } + } + }, + [ALC892_FIXUP_ASROCK_MOBO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x40f000f0 }, /* disabled */ + { 0x16, 0x40f000f0 }, /* disabled */ + { } + } + }, + [ALC662_FIXUP_USI_FUNC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_usi_headset_mic, + }, + [ALC662_FIXUP_USI_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ + { 0x18, 0x01a1903d }, + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC662_FIXUP_LENOVO_MULTI_CODECS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_alc662_fixup_lenovo_dual_codecs, + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_aspire_ethos_hp, + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x92130110 }, /* front speakers */ + { 0x18, 0x99130111 }, /* center/subwoofer */ + { 0x1b, 0x11130012 }, /* surround plus jack for HP */ + { } + }, + .chained = true, + .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET + }, + [ALC671_FIXUP_HP_HEADSET_MIC2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc671_fixup_hp_headset_mic2, + }, + [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x0221144f }, + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x04a1112c }, + { } + }, + .chained = true, + .chain_id = ALC668_FIXUP_HEADSET_MIC + }, + [ALC668_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mic, + .chained = true, + .chain_id = ALC668_FIXUP_MIC_DET_COEF + }, + [ALC668_FIXUP_MIC_DET_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 }, + {} + }, + }, + [ALC897_FIXUP_LENOVO_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc897_fixup_lenovo_headset_mic, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x03a11050 }, + { } + }, + .chained = true, + .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC + }, + [ALC897_FIXUP_HP_HSMIC_VERB] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + }, + [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc897_fixup_lenovo_headset_mode, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE + }, + [ALC897_FIXUP_UNIS_H3C_X500S] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 }, + {} + }, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN3] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* use as headset mic */ + { } + }, + }, +}; + +static const struct hda_quirk alc662_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), + SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS), + SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE), + SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE), + SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), + SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), + SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), + SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), + SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), + SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), + SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), + SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), + SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), + SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), + SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), + SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), + SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), + SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), + SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), + SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), + SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), + SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + +#if 0 + /* Below is a quirk table taken from the old code. + * Basically the device should work as is without the fixup table. + * If BIOS doesn't give a proper info, enable the corresponding + * fixup entry. + */ + SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), + SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), +#endif + {} +}; + +static const struct hda_model_fixup alc662_fixup_models[] = { + {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, + {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, + {.id = ALC272_FIXUP_MARIO, .name = "mario"}, + {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, + {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, + {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, + {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, + {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, + {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, + {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, + {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, + {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, + {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, + {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, + {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, + {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, + {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, + {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, + {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, + {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, + {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, + {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, + {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, + {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, + {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, + {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, + {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, + {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, + {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, + {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, + {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, + {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"}, + {} +}; + +static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + {0x17, 0x02211010}, + {0x18, 0x01a19030}, + {0x1a, 0x01813040}, + {0x21, 0x01014020}), + SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + {0x16, 0x01813030}, + {0x17, 0x02211010}, + {0x18, 0x01a19040}, + {0x21, 0x01014020}), + SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, + {0x14, 0x01014010}, + {0x18, 0x01a19020}, + {0x1a, 0x0181302f}, + {0x1b, 0x0221401f}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30130}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30140}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30150}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014010}, + {0x17, 0x90170150}, + {0x19, 0x02a11060}, + {0x1b, 0x01813030}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014010}, + {0x18, 0x01a19040}, + {0x1b, 0x01813030}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014020}, + {0x17, 0x90170110}, + {0x18, 0x01a19050}, + {0x1b, 0x01813040}, + {0x21, 0x02211030}), + {} +}; + +/* + */ +static int alc662_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + + spec->shutup = alc_eapd_shutup; + + /* handle multiple HPs as is */ + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + + alc_fix_pll_init(codec, 0x20, 0x04, 15); + + switch (codec->core.vendor_id) { + case 0x10ec0668: + spec->init_hook = alc668_restore_default_value; + break; + } + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc662_fixup_models, + alc662_fixup_tbl, alc662_fixups); + snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + if ((alc_get_coef0(codec) & (1 << 14)) && + codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) { + err = alc_codec_rename(codec, "ALC272X"); + if (err < 0) + goto error; + } + + /* automatic parse from the BIOS config */ + err = alc662_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + switch (codec->core.vendor_id) { + case 0x10ec0662: + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + break; + case 0x10ec0272: + case 0x10ec0663: + case 0x10ec0665: + case 0x10ec0668: + err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + break; + case 0x10ec0273: + err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + break; + } + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc662_codec_ops = { + .probe = alc662_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc662[] = { + HDA_CODEC_ID(0x10ec0272, "ALC272"), + HDA_CODEC_ID_REV(0x10ec0662, 0x100101, "ALC662 rev1"), + HDA_CODEC_ID_REV(0x10ec0662, 0x100300, "ALC662 rev3"), + HDA_CODEC_ID(0x10ec0663, "ALC663"), + HDA_CODEC_ID(0x10ec0665, "ALC665"), + HDA_CODEC_ID(0x10ec0667, "ALC667"), + HDA_CODEC_ID(0x10ec0668, "ALC668"), + HDA_CODEC_ID(0x10ec0670, "ALC670"), + HDA_CODEC_ID(0x10ec0671, "ALC671"), + HDA_CODEC_ID(0x10ec0867, "ALC891"), + HDA_CODEC_ID(0x10ec0892, "ALC892"), + HDA_CODEC_ID(0x10ec0897, "ALC897"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc662); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC662 and compatible HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc662_driver = { + .id = snd_hda_id_alc662, + .ops = &alc662_codec_ops, +}; + +module_hda_codec_driver(alc662_driver); diff --git a/sound/hda/codecs/realtek/alc680.c b/sound/hda/codecs/realtek/alc680.c new file mode 100644 index 000000000000..8aab1026243c --- /dev/null +++ b/sound/hda/codecs/realtek/alc680.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC680 codec +// + +#include +#include +#include "realtek.h" + +static int alc680_parse_auto_config(struct hda_codec *codec) +{ + return alc_parse_auto_config(codec, NULL, NULL); +} + +/* + */ +static int alc680_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + int err; + + /* ALC680 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + /* automatic parse from the BIOS config */ + err = alc680_parse_auto_config(codec); + if (err < 0) { + snd_hda_gen_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops alc680_codec_ops = { + .probe = alc680_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc680[] = { + HDA_CODEC_ID(0x10ec0680, "ALC680"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc680); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC680 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc680_driver = { + .id = snd_hda_id_alc680, + .ops = &alc680_codec_ops, +}; + +module_hda_codec_driver(alc680_driver); diff --git a/sound/hda/codecs/realtek/alc861.c b/sound/hda/codecs/realtek/alc861.c new file mode 100644 index 000000000000..270037c6504a --- /dev/null +++ b/sound/hda/codecs/realtek/alc861.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC861 codec +// + +#include +#include +#include "realtek.h" + +static int alc861_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; + return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); +} + +/* Pin config fixes */ +enum { + ALC861_FIXUP_FSC_AMILO_PI1505, + ALC861_FIXUP_AMP_VREF_0F, + ALC861_FIXUP_NO_JACK_DETECT, + ALC861_FIXUP_ASUS_A6RP, + ALC660_FIXUP_ASUS_W7J, +}; + +/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ +static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + unsigned int val; + + if (action != HDA_FIXUP_ACT_INIT) + return; + val = snd_hda_codec_get_pin_target(codec, 0x0f); + if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) + val |= AC_PINCTL_IN_EN; + val |= AC_PINCTL_VREF_50; + snd_hda_set_pin_ctl(codec, 0x0f, val); + spec->gen.keep_vref_in_automute = 1; +} + +static const struct hda_fixup alc861_fixups[] = { + [ALC861_FIXUP_FSC_AMILO_PI1505] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } + } + }, + [ALC861_FIXUP_AMP_VREF_0F] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + }, + [ALC861_FIXUP_NO_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC861_FIXUP_ASUS_A6RP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + .chained = true, + .chain_id = ALC861_FIXUP_NO_JACK_DETECT, + }, + [ALC660_FIXUP_ASUS_W7J] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* ASUS W7J needs a magic pin setup on unused NID 0x10 + * for enabling outputs + */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + { } + }, + } +}; + +static const struct hda_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), + SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), + {} +}; + +/* + */ +static int alc861_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x15); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x23; + + spec->power_hook = alc_power_eapd; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc861_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc861_codec_ops = { + .probe = alc861_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc861[] = { + HDA_CODEC_ID_REV(0x10ec0861, 0x100340, "ALC660"), + HDA_CODEC_ID(0x10ec0861, "ALC861"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC861 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc861_driver = { + .id = snd_hda_id_alc861, + .ops = &alc861_codec_ops, +}; + +module_hda_codec_driver(alc861_driver); diff --git a/sound/hda/codecs/realtek/alc861vd.c b/sound/hda/codecs/realtek/alc861vd.c new file mode 100644 index 000000000000..44264e0d6e56 --- /dev/null +++ b/sound/hda/codecs/realtek/alc861vd.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC861-VD codec +// Based on ALC882 +// In addition, an independent DAC +// + +#include +#include +#include "realtek.h" + +static int alc861vd_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); +} + +enum { + ALC660VD_FIX_ASUS_GPIO1, + ALC861VD_FIX_DALLAS, +}; + +/* exclude VREF80 */ +static void alc861vd_fixup_dallas(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_override_pin_caps(codec, 0x18, 0x00000734); + snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); + } +} + +/* reset GPIO1 */ +static void alc660vd_fixup_asus_gpio1(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->gpio_mask |= 0x02; + alc_fixup_gpio(codec, action, 0x01); +} + +static const struct hda_fixup alc861vd_fixups[] = { + [ALC660VD_FIX_ASUS_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc660vd_fixup_asus_gpio1, + }, + [ALC861VD_FIX_DALLAS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861vd_fixup_dallas, + }, +}; + +static const struct hda_quirk alc861vd_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), + {} +}; + +/* + */ +static int alc861vd_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x23; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc861vd_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc861vd_codec_ops = { + .probe = alc861vd_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc861vd[] = { + HDA_CODEC_ID(0x10ec0660, "ALC660-VD"), + HDA_CODEC_ID(0x10ec0862, "ALC861-VD"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861vd); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC861-VD HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc861vd_driver = { + .id = snd_hda_id_alc861vd, + .ops = &alc861vd_codec_ops, +}; + +module_hda_codec_driver(alc861vd_driver); diff --git a/sound/hda/codecs/realtek/alc880.c b/sound/hda/codecs/realtek/alc880.c new file mode 100644 index 000000000000..bf1bdf11ec2d --- /dev/null +++ b/sound/hda/codecs/realtek/alc880.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC880 codec +// + +#include +#include +#include "realtek.h" + +static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) +{ + /* For some reason, the res given from ALC880 is broken. + Here we adjust it properly. */ + snd_hda_jack_unsol_event(codec, res >> 2); +} + +static int alc880_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); +} + +/* + * ALC880 fix-ups + */ +enum { + ALC880_FIXUP_GPIO1, + ALC880_FIXUP_GPIO2, + ALC880_FIXUP_MEDION_RIM, + ALC880_FIXUP_LG, + ALC880_FIXUP_LG_LW25, + ALC880_FIXUP_W810, + ALC880_FIXUP_EAPD_COEF, + ALC880_FIXUP_TCL_S700, + ALC880_FIXUP_VOL_KNOB, + ALC880_FIXUP_FUJITSU, + ALC880_FIXUP_F1734, + ALC880_FIXUP_UNIWILL, + ALC880_FIXUP_UNIWILL_DIG, + ALC880_FIXUP_Z71V, + ALC880_FIXUP_ASUS_W5A, + ALC880_FIXUP_3ST_BASE, + ALC880_FIXUP_3ST, + ALC880_FIXUP_3ST_DIG, + ALC880_FIXUP_5ST_BASE, + ALC880_FIXUP_5ST, + ALC880_FIXUP_5ST_DIG, + ALC880_FIXUP_6ST_BASE, + ALC880_FIXUP_6ST, + ALC880_FIXUP_6ST_DIG, + ALC880_FIXUP_6ST_AUTOMUTE, +}; + +/* enable the volume-knob widget support on NID 0x21 */ +static void alc880_fixup_vol_knob(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_jack_detect_enable_callback(codec, 0x21, + alc_update_knob_master); +} + +static const struct hda_fixup alc880_fixups[] = { + [ALC880_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC880_FIXUP_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, + }, + [ALC880_FIXUP_MEDION_RIM] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_LG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x16, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x1a, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_LG_LW25] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x0181344f }, /* line-in */ + { 0x1b, 0x0321403f }, /* headphone */ + { } + } + }, + [ALC880_FIXUP_W810] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_EAPD_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + {} + }, + }, + [ALC880_FIXUP_TCL_S700] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_VOL_KNOB] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc880_fixup_vol_knob, + }, + [ALC880_FIXUP_FUJITSU] = { + /* override all pins as BIOS on old Amilo is broken */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x01454140 }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_F1734] = { + /* almost compatible with FUJITSU, but no bass and SPDIF */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_UNIWILL] = { + /* need to fix HP and speaker pins to be parsed correctly */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { } + }, + }, + [ALC880_FIXUP_UNIWILL_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1f, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_Z71V] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x99030120 }, /* speaker */ + { 0x15, 0x0121411f }, /* HP */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19950 }, /* mic-in */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + } + }, + [ALC880_FIXUP_ASUS_W5A] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x90a60160 }, /* mic */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0xb743111e }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO1, + }, + [ALC880_FIXUP_3ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* line-out */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_3ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_3ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_5ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01016412 }, /* surr */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_5ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_5ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_6ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x01016412 }, /* surr */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01012414 }, /* side */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x02a19c40 }, /* front-mic */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x0121411f }, /* HP */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_6ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_AUTOMUTE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x0121401f }, /* HP with jack detect */ + { } + }, + .chained_before = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, +}; + +static const struct hda_quirk alc880_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), + SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), + SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), + + /* Below is the copied entries from alc880_quirks.c. + * It's not quite sure whether BIOS sets the correct pin-config table + * on these machines, thus they are kept to be compatible with + * the old static quirks. Once when it's confirmed to work without + * these overrides, it'd be better to remove. + */ + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), + /* default Intel */ + SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), + {} +}; + +static const struct hda_model_fixup alc880_fixup_models[] = { + {.id = ALC880_FIXUP_3ST, .name = "3stack"}, + {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, + {.id = ALC880_FIXUP_5ST, .name = "5stack"}, + {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, + {.id = ALC880_FIXUP_6ST, .name = "6stack"}, + {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, + {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, + {} +}; + + +/* + * OK, here we have finally the probe for ALC880 + */ +static int alc880_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + spec->gen.need_dac_fix = 1; + spec->gen.beep_nid = 0x01; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + alc880_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc880_codec_ops = { + .probe = alc880_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = alc880_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc880[] = { + HDA_CODEC_ID(0x10ec0880, "ALC880"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC880 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc880_driver = { + .id = snd_hda_id_alc880, + .ops = &alc880_codec_ops, +}; + +module_hda_codec_driver(alc880_driver); diff --git a/sound/hda/codecs/realtek/alc882.c b/sound/hda/codecs/realtek/alc882.c new file mode 100644 index 000000000000..529fecd5baa0 --- /dev/null +++ b/sound/hda/codecs/realtek/alc882.c @@ -0,0 +1,861 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC882/883/885/888/889 codec support +// +// ALC882 is almost identical with ALC880 but has cleaner and more flexible +// configuration. Each pin widget can choose any input DACs and a mixer. +// Each ADC is connected from a mixer of all inputs. This makes possible +// 6-channel independent captures. +// +// In addition, an independent DAC for the multi-playback (not used in this +// driver yet). +// + +#include +#include +#include "realtek.h" + +/* + * Pin config fixes + */ +enum { + ALC882_FIXUP_ABIT_AW9D_MAX, + ALC882_FIXUP_LENOVO_Y530, + ALC882_FIXUP_PB_M5210, + ALC882_FIXUP_ACER_ASPIRE_7736, + ALC882_FIXUP_ASUS_W90V, + ALC889_FIXUP_CD, + ALC889_FIXUP_FRONT_HP_NO_PRESENCE, + ALC889_FIXUP_VAIO_TT, + ALC888_FIXUP_EEE1601, + ALC886_FIXUP_EAPD, + ALC882_FIXUP_EAPD, + ALC883_FIXUP_EAPD, + ALC883_FIXUP_ACER_EAPD, + ALC882_FIXUP_GPIO1, + ALC882_FIXUP_GPIO2, + ALC882_FIXUP_GPIO3, + ALC889_FIXUP_COEF, + ALC882_FIXUP_ASUS_W2JC, + ALC882_FIXUP_ACER_ASPIRE_4930G, + ALC882_FIXUP_ACER_ASPIRE_8930G, + ALC882_FIXUP_ASPIRE_8930G_VERBS, + ALC885_FIXUP_MACPRO_GPIO, + ALC889_FIXUP_DAC_ROUTE, + ALC889_FIXUP_MBP_VREF, + ALC889_FIXUP_IMAC91_VREF, + ALC889_FIXUP_MBA11_VREF, + ALC889_FIXUP_MBA21_VREF, + ALC889_FIXUP_MP11_VREF, + ALC889_FIXUP_MP41_VREF, + ALC882_FIXUP_INV_DMIC, + ALC882_FIXUP_NO_PRIMARY_HP, + ALC887_FIXUP_ASUS_BASS, + ALC887_FIXUP_BASS_CHMAP, + ALC1220_FIXUP_GB_DUAL_CODECS, + ALC1220_FIXUP_GB_X570, + ALC1220_FIXUP_CLEVO_P950, + ALC1220_FIXUP_CLEVO_PB51ED, + ALC1220_FIXUP_CLEVO_PB51ED_PINS, + ALC887_FIXUP_ASUS_AUDIO, + ALC887_FIXUP_ASUS_HMIC, + ALCS1200A_FIXUP_MIC_VREF, + ALC888VD_FIXUP_MIC_100VREF, +}; + +static void alc889_fixup_coef(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_INIT) + return; + alc_update_coef_idx(codec, 7, 0, 0x2030); +} + +/* set up GPIO at initialization */ +static void alc885_fixup_macpro_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + spec->gpio_write_delay = true; + alc_fixup_gpio3(codec, fix, action); +} + +/* Fix the connection of some pins for ALC889: + * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't + * work correctly (bko#42740) + */ +static void alc889_fixup_dac_route(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* fake the connections during parsing the tree */ + static const hda_nid_t conn1[] = { 0x0c, 0x0d }; + static const hda_nid_t conn2[] = { 0x0e, 0x0f }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2); + snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2); + } else if (action == HDA_FIXUP_ACT_PROBE) { + /* restore the connections */ + static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn); + } +} + +/* Set VREF on HP pin */ +static void alc889_fixup_mbp_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 }; + struct alc_spec *spec = codec->spec; + int i; + + if (action != HDA_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); + if (get_defcfg_device(val) != AC_JACK_HP_OUT) + continue; + val = snd_hda_codec_get_pin_target(codec, nids[i]); + val |= AC_PINCTL_VREF_80; + snd_hda_set_pin_ctl(codec, nids[i], val); + spec->gen.keep_vref_in_automute = 1; + break; + } +} + +static void alc889_fixup_mac_pins(struct hda_codec *codec, + const hda_nid_t *nids, int num_nids) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < num_nids; i++) { + unsigned int val; + val = snd_hda_codec_get_pin_target(codec, nids[i]); + val |= AC_PINCTL_VREF_50; + snd_hda_set_pin_ctl(codec, nids[i], val); + } + spec->gen.keep_vref_in_automute = 1; +} + +/* Set VREF on speaker pins on imac91 */ +static void alc889_fixup_imac91_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18, 0x1a }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Set VREF on speaker pins on mba11 */ +static void alc889_fixup_mba11_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18 }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Set VREF on speaker pins on mba21 */ +static void alc889_fixup_mba21_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18, 0x19 }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Don't take HP output as primary + * Strangely, the speaker output doesn't work on Vaio Z and some Vaio + * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 + */ +static void alc882_fixup_no_primary_hp(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.no_primary_hp = 1; + spec->gen.no_multi_io = 1; + } +} + +static void alc1220_fixup_gb_x570(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + static const struct coef_fw gb_x570_coefs[] = { + WRITE_COEF(0x07, 0x03c0), + WRITE_COEF(0x1a, 0x01c1), + WRITE_COEF(0x1b, 0x0202), + WRITE_COEF(0x43, 0x3005), + {} + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, gb_x570_coefs); + break; + } +} + +static void alc1220_fixup_clevo_p950(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + alc_update_coef_idx(codec, 0x7, 0, 0x3c3); + /* We therefore want to make sure 0x14 (front headphone) and + * 0x1b (speakers) use the stereo DAC 0x02 + */ + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); +} + +static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc1220_fixup_clevo_p950(codec, fix, action); + alc_fixup_headset_mode_no_hp_mic(codec, fix, action); +} + +static void alc887_asus_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + unsigned int vref; + + snd_hda_gen_hp_automute(codec, jack); + + if (spec->gen.hp_jack_present) + vref = AC_PINCTL_VREF_80; + else + vref = AC_PINCTL_VREF_HIZ; + snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); +} + +static void alc887_fixup_asus_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action != HDA_FIXUP_ACT_PROBE) + return; + snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); + spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; +} + +static const struct hda_fixup alc882_fixups[] = { + [ALC882_FIXUP_ABIT_AW9D_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ + { } + } + }, + [ALC882_FIXUP_LENOVO_Y530] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ + { } + } + }, + [ALC882_FIXUP_PB_M5210] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, + {} + } + }, + [ALC882_FIXUP_ACER_ASPIRE_7736] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, + }, + [ALC882_FIXUP_ASUS_W90V] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130110 }, /* fix sequence for CLFE */ + { } + } + }, + [ALC889_FIXUP_CD] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1c, 0x993301f0 }, /* CD */ + { } + } + }, + [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */ + { } + }, + .chained = true, + .chain_id = ALC889_FIXUP_CD, + }, + [ALC889_FIXUP_VAIO_TT] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170111 }, /* hidden surround speaker */ + { } + } + }, + [ALC888_FIXUP_EEE1601] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, + { } + } + }, + [ALC886_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 }, + { } + } + }, + [ALC882_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + { } + } + }, + [ALC883_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + { } + } + }, + [ALC883_FIXUP_ACER_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* eanable EAPD on Acer laptops */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + } + }, + [ALC882_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC882_FIXUP_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, + }, + [ALC882_FIXUP_GPIO3] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio3, + }, + [ALC882_FIXUP_ASUS_W2JC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + .chained = true, + .chain_id = ALC882_FIXUP_EAPD, + }, + [ALC889_FIXUP_COEF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_coef, + }, + [ALC882_FIXUP_ACER_ASPIRE_4930G] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130111 }, /* CLFE speaker */ + { 0x17, 0x99130112 }, /* surround speaker */ + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC882_FIXUP_ACER_ASPIRE_8930G] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130111 }, /* CLFE speaker */ + { 0x1b, 0x99130112 }, /* surround speaker */ + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, + }, + [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { + /* additional init verbs for Acer Aspire 8930G */ + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enable all DACs */ + /* DAC DISABLE/MUTE 1? */ + /* setting bits 1-5 disables DAC nids 0x02-0x06 + * apparently. Init=0x38 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, + /* DAC DISABLE/MUTE 2? */ + /* some bit here disables the other DACs. + * Init=0x4900 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, + /* DMIC fix + * This laptop has a stereo digital microphone. + * The mics are only 1cm apart which makes the stereo + * useless. However, either the mic or the ALC889 + * makes the signal become a difference/sum signal + * instead of standard stereo, which is annoying. + * So instead we flip this bit which makes the + * codec replicate the sum signal to both channels, + * turning it into a normal mono mic. + */ + /* DMIC_CONTROL? Init value = 0x0001 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC885_FIXUP_MACPRO_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc885_fixup_macpro_gpio, + }, + [ALC889_FIXUP_DAC_ROUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_dac_route, + }, + [ALC889_FIXUP_MBP_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_IMAC91_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_imac91_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_MBA11_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba11_vref, + .chained = true, + .chain_id = ALC889_FIXUP_MBP_VREF, + }, + [ALC889_FIXUP_MBA21_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba21_vref, + .chained = true, + .chain_id = ALC889_FIXUP_MBP_VREF, + }, + [ALC889_FIXUP_MP11_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba11_vref, + .chained = true, + .chain_id = ALC885_FIXUP_MACPRO_GPIO, + }, + [ALC889_FIXUP_MP41_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC885_FIXUP_MACPRO_GPIO, + }, + [ALC882_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC882_FIXUP_NO_PRIMARY_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc882_fixup_no_primary_hp, + }, + [ALC887_FIXUP_ASUS_BASS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x16, 0x99130130}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC887_FIXUP_BASS_CHMAP, + }, + [ALC887_FIXUP_BASS_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + }, + [ALC1220_FIXUP_GB_DUAL_CODECS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_dual_codecs, + }, + [ALC1220_FIXUP_GB_X570] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_x570, + }, + [ALC1220_FIXUP_CLEVO_P950] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_clevo_p950, + }, + [ALC1220_FIXUP_CLEVO_PB51ED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_clevo_pb51ed, + }, + [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + {} + }, + .chained = true, + .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, + }, + [ALC887_FIXUP_ASUS_AUDIO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ + { 0x19, 0x22219420 }, + {} + }, + }, + [ALC887_FIXUP_ASUS_HMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc887_fixup_asus_jack, + .chained = true, + .chain_id = ALC887_FIXUP_ASUS_AUDIO, + }, + [ALCS1200A_FIXUP_MIC_VREF] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, PIN_VREF50 }, /* rear mic */ + { 0x19, PIN_VREF50 }, /* front mic */ + {} + } + }, + [ALC888VD_FIXUP_MIC_100VREF] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, PIN_VREF100 }, /* headset mic */ + {} + } + }, +}; + +static const struct hda_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", + ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", + ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), + SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), + SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), + SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), + SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), + SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), + SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), + SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), + SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF), + SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), + SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP), + + /* All Apple entries are in codec SSIDs */ + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF), + SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), + SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF), + SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF), + + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF), + SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), + SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), + SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), + SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), + SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x5802, "Clevo X58[05]WN[RST]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED), + SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), + SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), + {} +}; + +static const struct hda_model_fixup alc882_fixup_models[] = { + {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, + {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, + {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, + {.id = ALC889_FIXUP_CD, .name = "cd"}, + {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, + {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, + {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, + {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, + {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, + {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, + {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, + {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, + {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, + {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, + {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, + {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, + {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, + {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, + {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, + {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, + {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, + {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, + {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, + {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, + {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, + {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"}, + {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, + {} +}; + +static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950, + {0x14, 0x01014010}, + {0x15, 0x01011012}, + {0x16, 0x01016011}, + {0x18, 0x01a19040}, + {0x19, 0x02a19050}, + {0x1a, 0x0181304f}, + {0x1b, 0x0221401f}, + {0x1e, 0x01456130}), + SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950, + {0x14, 0x01015010}, + {0x15, 0x01011012}, + {0x16, 0x01011011}, + {0x18, 0x01a11040}, + {0x19, 0x02a19050}, + {0x1a, 0x0181104f}, + {0x1b, 0x0221401f}, + {0x1e, 0x01451130}), + {} +}; + +/* + * BIOS auto configuration + */ +/* almost identical with ALC880 parser... */ +static int alc882_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); +} + +/* + */ +static int alc882_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + + switch (codec->core.vendor_id) { + case 0x10ec0882: + case 0x10ec0885: + case 0x10ec0900: + case 0x10ec0b00: + case 0x10ec1220: + break; + default: + /* ALC883 and variants */ + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + break; + } + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + alc882_fixups); + snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + /* automatic parse from the BIOS config */ + err = alc882_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops alc882_codec_ops = { + .probe = alc882_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc882[] = { + HDA_CODEC_ID_REV(0x10ec0662, 0x100002, "ALC662 rev2"), + HDA_CODEC_ID(0x10ec0882, "ALC882"), + HDA_CODEC_ID(0x10ec0883, "ALC883"), + HDA_CODEC_ID_REV(0x10ec0885, 0x100101, "ALC889A"), + HDA_CODEC_ID_REV(0x10ec0885, 0x100103, "ALC889A"), + HDA_CODEC_ID(0x10ec0885, "ALC885"), + HDA_CODEC_ID(0x10ec0887, "ALC887"), + HDA_CODEC_ID_REV(0x10ec0888, 0x100101, "ALC1200"), + HDA_CODEC_ID(0x10ec0888, "ALC888"), + HDA_CODEC_ID(0x10ec0889, "ALC889"), + HDA_CODEC_ID(0x10ec0899, "ALC898"), + HDA_CODEC_ID(0x10ec0900, "ALC1150"), + HDA_CODEC_ID(0x10ec0b00, "ALCS1200A"), + HDA_CODEC_ID(0x10ec1168, "ALC1220"), + HDA_CODEC_ID(0x10ec1220, "ALC1220"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc882); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC882 and compatible HD-audio codecs"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc882_driver = { + .id = snd_hda_id_alc882, + .ops = &alc882_codec_ops, +}; + +module_hda_codec_driver(alc882_driver); diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c new file mode 100644 index 000000000000..b6feccfd45a9 --- /dev/null +++ b/sound/hda/codecs/realtek/realtek.c @@ -0,0 +1,2299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek HD-audio codec support code +// + +#include +#include +#include "realtek.h" + +/* + * COEF access helper functions + */ + +static void coef_mutex_lock(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_power_up_pm(codec); + mutex_lock(&spec->coef_mutex); +} + +static void coef_mutex_unlock(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + mutex_unlock(&spec->coef_mutex); + snd_hda_power_down_pm(codec); +} + +static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx) +{ + unsigned int val; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); + return val; +} + +int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx) +{ + unsigned int val; + + coef_mutex_lock(codec); + val = __alc_read_coefex_idx(codec, nid, coef_idx); + coef_mutex_unlock(codec); + return val; +} +EXPORT_SYMBOL_NS_GPL(alc_read_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); +} + +void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) +{ + coef_mutex_lock(codec); + __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_write_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set) +{ + unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); + + if (val != -1) + __alc_write_coefex_idx(codec, nid, coef_idx, + (val & ~mask) | bits_set); +} + +void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set) +{ + coef_mutex_lock(codec); + __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +/* a special bypass for COEF 0; read the cached value at the second time */ +unsigned int alc_get_coef0(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->coef0) + spec->coef0 = alc_read_coef_idx(codec, 0); + return spec->coef0; +} +EXPORT_SYMBOL_NS_GPL(alc_get_coef0, "SND_HDA_CODEC_REALTEK"); + +void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw) +{ + coef_mutex_lock(codec); + for (; fw->nid; fw++) { + if (fw->mask == (unsigned short)-1) + __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); + else + __alc_update_coefex_idx(codec, fw->nid, fw->idx, + fw->mask, fw->val); + } + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_process_coef_fw, "SND_HDA_CODEC_REALTEK"); + +/* + * GPIO setup tables, used in initialization + */ + +/* Enable GPIO mask and set output */ +void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) +{ + struct alc_spec *spec = codec->spec; + + spec->gpio_mask |= mask; + spec->gpio_dir |= mask; + spec->gpio_data |= mask; +} +EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_write_gpio_data(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); +} +EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK"); + +void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, + bool on) +{ + struct alc_spec *spec = codec->spec; + unsigned int oldval = spec->gpio_data; + + if (on) + spec->gpio_data |= mask; + else + spec->gpio_data &= ~mask; + if (oldval != spec->gpio_data) + alc_write_gpio_data(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK"); + +void alc_write_gpio(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->gpio_mask) + return; + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, spec->gpio_mask); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); + if (spec->gpio_write_delay) + msleep(1); + alc_write_gpio_data(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + alc_setup_gpio(codec, mask); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x01); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio1, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x02); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio2, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x03); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio3, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x04); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio4, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_gen_add_micmute_led_cdev(codec, NULL); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_micmute_led, "SND_HDA_CODEC_REALTEK"); + +/* + * Fix hardware PLL issue + * On some codecs, the analog PLL gating control must be off while + * the default value is 1. + */ +void alc_fix_pll(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->pll_nid) + alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, + 1 << spec->pll_coef_bit, 0); +} +EXPORT_SYMBOL_NS_GPL(alc_fix_pll, "SND_HDA_CODEC_REALTEK"); + +void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_bit) +{ + struct alc_spec *spec = codec->spec; + spec->pll_nid = nid; + spec->pll_coef_idx = coef_idx; + spec->pll_coef_bit = coef_bit; + alc_fix_pll(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_fix_pll_init, "SND_HDA_CODEC_REALTEK"); + +/* update the master volume per volume-knob's unsol event */ +void alc_update_knob_master(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + unsigned int val; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value *uctl; + + kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); + if (!kctl) + return; + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return; + val = snd_hda_codec_read(codec, jack->nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + val &= HDA_AMP_VOLMASK; + uctl->value.integer.value[0] = val; + uctl->value.integer.value[1] = val; + kctl->put(kctl, uctl); + kfree(uctl); +} +EXPORT_SYMBOL_NS_GPL(alc_update_knob_master, "SND_HDA_CODEC_REALTEK"); + +/* Change EAPD to verb control */ +void alc_fill_eapd_coef(struct hda_codec *codec) +{ + int coef; + + coef = alc_get_coef0(codec); + + switch (codec->core.vendor_id) { + case 0x10ec0262: + alc_update_coef_idx(codec, 0x7, 0, 1<<5); + break; + case 0x10ec0267: + case 0x10ec0268: + alc_update_coef_idx(codec, 0x7, 0, 1<<13); + break; + case 0x10ec0269: + if ((coef & 0x00f0) == 0x0010) + alc_update_coef_idx(codec, 0xd, 0, 1<<14); + if ((coef & 0x00f0) == 0x0020) + alc_update_coef_idx(codec, 0x4, 1<<15, 0); + if ((coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + break; + case 0x10ec0280: + case 0x10ec0284: + case 0x10ec0290: + case 0x10ec0292: + alc_update_coef_idx(codec, 0x4, 1<<15, 0); + break; + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + fallthrough; + case 0x10ec0215: + case 0x10ec0236: + case 0x10ec0245: + case 0x10ec0256: + case 0x10ec0257: + case 0x10ec0285: + case 0x10ec0289: + alc_update_coef_idx(codec, 0x36, 1<<13, 0); + fallthrough; + case 0x10ec0230: + case 0x10ec0233: + case 0x10ec0235: + case 0x10ec0255: + case 0x19e58326: + case 0x10ec0282: + case 0x10ec0283: + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + case 0x10ec0300: + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + break; + case 0x10ec0275: + alc_update_coef_idx(codec, 0xe, 0, 1<<0); + break; + case 0x10ec0287: + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + alc_write_coef_idx(codec, 0x8, 0x4ab7); + break; + case 0x10ec0293: + alc_update_coef_idx(codec, 0xa, 1<<13, 0); + break; + case 0x10ec0234: + case 0x10ec0274: + alc_write_coef_idx(codec, 0x6e, 0x0c25); + fallthrough; + case 0x10ec0294: + case 0x10ec0700: + case 0x10ec0701: + case 0x10ec0703: + case 0x10ec0711: + alc_update_coef_idx(codec, 0x10, 1<<15, 0); + break; + case 0x10ec0662: + if ((coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ + break; + case 0x10ec0272: + case 0x10ec0273: + case 0x10ec0663: + case 0x10ec0665: + case 0x10ec0670: + case 0x10ec0671: + case 0x10ec0672: + alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ + break; + case 0x10ec0222: + case 0x10ec0623: + alc_update_coef_idx(codec, 0x19, 1<<13, 0); + break; + case 0x10ec0668: + alc_update_coef_idx(codec, 0x7, 3<<13, 0); + break; + case 0x10ec0867: + alc_update_coef_idx(codec, 0x4, 1<<10, 0); + break; + case 0x10ec0888: + if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x7, 1<<5, 0); + break; + case 0x10ec0892: + case 0x10ec0897: + alc_update_coef_idx(codec, 0x7, 1<<5, 0); + break; + case 0x10ec0899: + case 0x10ec0900: + case 0x10ec0b00: + case 0x10ec1168: + case 0x10ec1220: + alc_update_coef_idx(codec, 0x7, 1<<1, 0); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fill_eapd_coef, "SND_HDA_CODEC_REALTEK"); + +/* turn on/off EAPD control (only if available) */ +static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return; + if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + on ? 2 : 0); +} + +/* turn on/off EAPD controls of the codec */ +void alc_auto_setup_eapd(struct hda_codec *codec, bool on) +{ + /* We currently only handle front, HP */ + static const hda_nid_t pins[] = { + 0x0f, 0x10, 0x14, 0x15, 0x17, 0 + }; + const hda_nid_t *p; + for (p = pins; *p; p++) + set_eapd(codec, *p, on); +} +EXPORT_SYMBOL_NS_GPL(alc_auto_setup_eapd, "SND_HDA_CODEC_REALTEK"); + +/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ +int alc_find_ext_mic_pin(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t nid; + unsigned int defcfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + nid = cfg->inputs[i].pin; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + continue; + return nid; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_find_ext_mic_pin, "SND_HDA_CODEC_REALTEK"); + +void alc_headset_mic_no_shutup(struct hda_codec *codec) +{ + const struct hda_pincfg *pin; + int mic_pin = alc_find_ext_mic_pin(codec); + int i; + + /* don't shut up pins when unloading the driver; otherwise it breaks + * the default pin setup at the next load of the driver + */ + if (codec->bus->shutdown) + return; + + snd_array_for_each(&codec->init_pins, i, pin) { + /* use read here for syncing after issuing each verb */ + if (pin->nid != mic_pin) + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + + codec->pins_shutup = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_headset_mic_no_shutup, "SND_HDA_CODEC_REALTEK"); + +void alc_shutup_pins(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->no_shutup_pins) + return; + + switch (codec->core.vendor_id) { + case 0x10ec0236: + case 0x10ec0256: + case 0x10ec0257: + case 0x19e58326: + case 0x10ec0283: + case 0x10ec0285: + case 0x10ec0286: + case 0x10ec0287: + case 0x10ec0288: + case 0x10ec0295: + case 0x10ec0298: + alc_headset_mic_no_shutup(codec); + break; + default: + snd_hda_shutup_pins(codec); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_shutup_pins, "SND_HDA_CODEC_REALTEK"); + +/* generic shutup callback; + * just turning off EAPD and a little pause for avoiding pop-noise + */ +void alc_eapd_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc_auto_setup_eapd(codec, false); + if (!spec->no_depop_delay) + msleep(200); + alc_shutup_pins(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_eapd_shutup, "SND_HDA_CODEC_REALTEK"); + +/* additional initialization for ALC888 variants */ +static void alc888_coef_init(struct hda_codec *codec) +{ + switch (alc_get_coef0(codec) & 0x00f0) { + /* alc888-VA */ + case 0x00: + /* alc888-VB */ + case 0x10: + alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */ + break; + } +} + +/* generic EAPD initialization */ +void alc_auto_init_amp(struct hda_codec *codec, int type) +{ + alc_auto_setup_eapd(codec, true); + alc_write_gpio(codec); + switch (type) { + case ALC_INIT_DEFAULT: + switch (codec->core.vendor_id) { + case 0x10ec0260: + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); + break; + case 0x10ec0880: + case 0x10ec0882: + case 0x10ec0883: + case 0x10ec0885: + alc_update_coef_idx(codec, 7, 0, 0x2030); + break; + case 0x10ec0888: + alc888_coef_init(codec); + break; + } + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_auto_init_amp, "SND_HDA_CODEC_REALTEK"); + +/* get a primary headphone pin if available */ +hda_nid_t alc_get_hp_pin(struct alc_spec *spec) +{ + if (spec->gen.autocfg.hp_pins[0]) + return spec->gen.autocfg.hp_pins[0]; + if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) + return spec->gen.autocfg.line_out_pins[0]; + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_get_hp_pin, "SND_HDA_CODEC_REALTEK"); + +/* + * Realtek SSID verification + */ + +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2) + +void alc_fixup_sku_ignore(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->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_sku_ignore, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_no_depop_delay(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PROBE) { + spec->no_depop_delay = 1; + codec->depop_delay = 0; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_no_depop_delay, "SND_HDA_CODEC_REALTEK"); + +int alc_auto_parse_customize_define(struct hda_codec *codec) +{ + unsigned int ass, tmp, i; + unsigned nid = 0; + struct alc_spec *spec = codec->spec; + + spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; + } + + if (!codec->bus->pci) + return -1; + ass = codec->core.subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + nid = 0x1d; + if (codec->core.vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + + if (!(ass & 1)) { + codec_info(codec, "%s: SKU not ready 0x%08x\n", + codec->core.chip_name, ass); + return -1; + } + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return -1; + + spec->cdefine.port_connectivity = ass >> 30; + spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; + spec->cdefine.check_sum = (ass >> 16) & 0xf; + spec->cdefine.customization = ass >> 8; +do_sku: + spec->cdefine.sku_cfg = ass; + spec->cdefine.external_amp = (ass & 0x38) >> 3; + spec->cdefine.platform_type = (ass & 0x4) >> 2; + spec->cdefine.swap = (ass & 0x2) >> 1; + spec->cdefine.override = ass & 0x1; + + codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n", + nid, spec->cdefine.sku_cfg); + codec_dbg(codec, "SKU: port_connectivity=0x%x\n", + spec->cdefine.port_connectivity); + codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); + codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); + codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization); + codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp); + codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type); + codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap); + codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_auto_parse_customize_define, "SND_HDA_CODEC_REALTEK"); + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} +/* return true if the given NID is found in the list */ +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + return find_idx_in_nid_list(nid, list, nums) >= 0; +} + +/* check subsystem ID and set up device-specific initialization; + * return 1 if initialized, 0 if invalid SSID + */ +/* 32-bit subsystem ID for BIOS loading in HD Audio codec. + * 31 ~ 16 : Manufacture ID + * 15 ~ 8 : SKU ID + * 7 ~ 0 : Assembly ID + * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 + */ +int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) +{ + unsigned int ass, tmp, i; + unsigned nid; + struct alc_spec *spec = codec->spec; + + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return 0; + goto do_sku; + } + + ass = codec->core.subsystem_id & 0xffff; + if (codec->bus->pci && + ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + /* invalid SSID, check the special NID pin defcfg instead */ + /* + * 31~30 : port connectivity + * 29~21 : reserve + * 20 : PCBEEP input + * 19~16 : Check sum (15:1) + * 15~1 : Custom + * 0 : override + */ + nid = 0x1d; + if (codec->core.vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + codec_dbg(codec, + "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n", + ass, nid); + if (!(ass & 1)) + return 0; + if ((ass >> 30) != 1) /* no physical connection */ + return 0; + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return 0; +do_sku: + codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", + ass & 0xffff, codec->core.vendor_id); + /* + * 0 : override + * 1 : Swap Jack + * 2 : 0 --> Desktop, 1 --> Laptop + * 3~5 : External Amplifier control + * 7~6 : Reserved + */ + tmp = (ass & 0x38) >> 3; /* external Amp control */ + if (spec->init_amp == ALC_INIT_UNDEFINED) { + switch (tmp) { + case 1: + alc_setup_gpio(codec, 0x01); + break; + case 3: + alc_setup_gpio(codec, 0x02); + break; + case 7: + alc_setup_gpio(codec, 0x04); + break; + case 5: + default: + spec->init_amp = ALC_INIT_DEFAULT; + break; + } + } + + /* is laptop or Desktop and enable the function "Mute internal speaker + * when the external headphone out jack is plugged" + */ + if (!(ass & 0x8000)) + return 1; + /* + * 10~8 : Jack location + * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered + * 14~13: Resvered + * 15 : 1 --> enable the function "Mute internal speaker + * when the external headphone out jack is plugged" + */ + if (!alc_get_hp_pin(spec)) { + hda_nid_t nid; + tmp = (ass >> 11) & 0x3; /* HP to chassis */ + nid = ports[tmp]; + if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, + spec->gen.autocfg.line_outs)) + return 1; + spec->gen.autocfg.hp_pins[0] = nid; + } + return 1; +} +EXPORT_SYMBOL_NS_GPL(alc_subsystem_id, "SND_HDA_CODEC_REALTEK"); + +/* Check the validity of ALC subsystem-id + * ports contains an array of 4 pin NIDs for port-A, E, D and I */ +void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) +{ + if (!alc_subsystem_id(codec, ports)) { + struct alc_spec *spec = codec->spec; + if (spec->init_amp == ALC_INIT_UNDEFINED) { + codec_dbg(codec, + "realtek: Enable default setup for auto mode as fallback\n"); + spec->init_amp = ALC_INIT_DEFAULT; + } + } +} +EXPORT_SYMBOL_NS_GPL(alc_ssid_check, "SND_HDA_CODEC_REALTEK"); + +/* inverted digital-mic */ +void alc_fixup_inv_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + spec->gen.inv_dmic_split = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_inv_dmic, "SND_HDA_CODEC_REALTEK"); + +int alc_build_controls(struct hda_codec *codec) +{ + int err; + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_build_controls, "SND_HDA_CODEC_REALTEK"); + +int alc_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + /* hibernation resume needs the full chip initialization */ + if (is_s4_resume(codec)) + alc_pre_init(codec); + + if (spec->init_hook) + spec->init_hook(codec); + + spec->gen.skip_verbs = 1; /* applied in below */ + snd_hda_gen_init(codec); + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp); + snd_hda_apply_verbs(codec); /* apply verbs here after own init */ + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_init, "SND_HDA_CODEC_REALTEK"); + +void alc_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!snd_hda_get_bool_hint(codec, "shutup")) + return; /* disabled explicitly by hints */ + + if (spec && spec->shutup) + spec->shutup(codec); + else + alc_shutup_pins(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_shutup, "SND_HDA_CODEC_REALTEK"); + +void alc_power_eapd(struct hda_codec *codec) +{ + alc_auto_setup_eapd(codec, false); +} +EXPORT_SYMBOL_NS_GPL(alc_power_eapd, "SND_HDA_CODEC_REALTEK"); + +int alc_suspend(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_suspend, "SND_HDA_CODEC_REALTEK"); + +int alc_resume(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->no_depop_delay) + msleep(150); /* to avoid pop noise */ + snd_hda_codec_init(codec); + snd_hda_regmap_sync(codec); + hda_call_check_power_status(codec, 0x01); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_resume, "SND_HDA_CODEC_REALTEK"); + +/* + * Rename codecs appropriately from COEF value or subvendor id + */ +struct alc_codec_rename_table { + unsigned int vendor_id; + unsigned short coef_mask; + unsigned short coef_bits; + const char *name; +}; + +struct alc_codec_rename_pci_table { + unsigned int codec_vendor_id; + unsigned short pci_subvendor; + unsigned short pci_subdevice; + const char *name; +}; + +static const struct alc_codec_rename_table rename_tbl[] = { + { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, + { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, + { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, + { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, + { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, + { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, + { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, + { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, + { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, + { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, + { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, + { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, + { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, + { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, + { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, + { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, + { } /* terminator */ +}; + +static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { + { 0x10ec0280, 0x1028, 0, "ALC3220" }, + { 0x10ec0282, 0x1028, 0, "ALC3221" }, + { 0x10ec0283, 0x1028, 0, "ALC3223" }, + { 0x10ec0288, 0x1028, 0, "ALC3263" }, + { 0x10ec0292, 0x1028, 0, "ALC3226" }, + { 0x10ec0293, 0x1028, 0, "ALC3235" }, + { 0x10ec0255, 0x1028, 0, "ALC3234" }, + { 0x10ec0668, 0x1028, 0, "ALC3661" }, + { 0x10ec0275, 0x1028, 0, "ALC3260" }, + { 0x10ec0899, 0x1028, 0, "ALC3861" }, + { 0x10ec0298, 0x1028, 0, "ALC3266" }, + { 0x10ec0236, 0x1028, 0, "ALC3204" }, + { 0x10ec0256, 0x1028, 0, "ALC3246" }, + { 0x10ec0225, 0x1028, 0, "ALC3253" }, + { 0x10ec0295, 0x1028, 0, "ALC3254" }, + { 0x10ec0299, 0x1028, 0, "ALC3271" }, + { 0x10ec0670, 0x1025, 0, "ALC669X" }, + { 0x10ec0676, 0x1025, 0, "ALC679X" }, + { 0x10ec0282, 0x1043, 0, "ALC3229" }, + { 0x10ec0233, 0x1043, 0, "ALC3236" }, + { 0x10ec0280, 0x103c, 0, "ALC3228" }, + { 0x10ec0282, 0x103c, 0, "ALC3227" }, + { 0x10ec0286, 0x103c, 0, "ALC3242" }, + { 0x10ec0290, 0x103c, 0, "ALC3241" }, + { 0x10ec0668, 0x103c, 0, "ALC3662" }, + { 0x10ec0283, 0x17aa, 0, "ALC3239" }, + { 0x10ec0292, 0x17aa, 0, "ALC3232" }, + { 0x10ec0257, 0x12f0, 0, "ALC3328" }, + { } /* terminator */ +}; + +static int alc_codec_rename_from_preset(struct hda_codec *codec) +{ + const struct alc_codec_rename_table *p; + const struct alc_codec_rename_pci_table *q; + + for (p = rename_tbl; p->vendor_id; p++) { + if (p->vendor_id != codec->core.vendor_id) + continue; + if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) + return alc_codec_rename(codec, p->name); + } + + if (!codec->bus->pci) + return 0; + for (q = rename_pci_tbl; q->codec_vendor_id; q++) { + if (q->codec_vendor_id != codec->core.vendor_id) + continue; + if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) + continue; + if (!q->pci_subdevice || + q->pci_subdevice == codec->bus->pci->subsystem_device) + return alc_codec_rename(codec, q->name); + } + + return 0; +} + +/* + * Digital-beep handlers + */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP + +/* additional beep mixers; private_value will be overwritten */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), +}; + +/* set up and create beep controls */ +int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir) +{ + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + int i; + + for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_set_beep_amp, "SND_HDA_CODEC_REALTEK"); + +static const struct snd_pci_quirk beep_allow_list[] = { + SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), + SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), + SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), + /* denylist -- no beep available */ + SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0), + SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0), + {} +}; + +int alc_has_cdefine_beep(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct snd_pci_quirk *q; + q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list); + if (q) + return q->value; + return spec->cdefine.enable_pcbeep; +} +EXPORT_SYMBOL_NS_GPL(alc_has_cdefine_beep, "SND_HDA_CODEC_REALTEK"); + +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ + +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int err; + + err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, + spec->parse_flags); + if (err < 0) + return err; + + if (ssid_nids) + alc_ssid_check(codec, ssid_nids); + + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; + + return 1; +} +EXPORT_SYMBOL_NS_GPL(alc_parse_auto_config, "SND_HDA_CODEC_REALTEK"); + +/* common preparation job for alc_spec */ +int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) +{ + struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); + int err; + + if (!spec) + return -ENOMEM; + codec->spec = spec; + snd_hda_gen_spec_init(&spec->gen); + spec->gen.mixer_nid = mixer_nid; + spec->gen.own_eapd_ctl = 1; + codec->single_adc_amp = 1; + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1; + codec->forced_resume = 1; + mutex_init(&spec->coef_mutex); + + err = alc_codec_rename_from_preset(codec); + if (err < 0) { + kfree(spec); + return err; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_alloc_spec, "SND_HDA_CODEC_REALTEK"); + +/* For dual-codec configuration, we need to disable some features to avoid + * conflicts of kctls and PCM streams + */ +void alc_fixup_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + /* disable vmaster */ + spec->gen.suppress_vmaster = 1; + /* auto-mute and auto-mic switch don't work with multiple codecs */ + spec->gen.suppress_auto_mute = 1; + spec->gen.suppress_auto_mic = 1; + /* disable aamix as well */ + spec->gen.mixer_nid = 0; + /* add location prefix to avoid conflicts */ + codec->force_pin_prefix = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ + { } +}; + +/* override the 2.1 chmap */ +void alc_fixup_bass_chmap(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_BUILD) { + struct alc_spec *spec = codec->spec; + spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_bass_chmap, "SND_HDA_CODEC_REALTEK"); + +/* exported as it's used by multiple codecs */ +void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc_fixup_dual_codecs(codec, fix, action); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* override card longname to provide a unique UCM profile */ + strscpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); + break; + case HDA_FIXUP_ACT_BUILD: + /* rename Capture controls depending on the codec */ + rename_ctl(codec, "Capture Volume", + codec->addr == 0 ? + "Rear-Panel Capture Volume" : + "Front-Panel Capture Volume"); + rename_ctl(codec, "Capture Switch", + codec->addr == 0 ? + "Rear-Panel Capture Switch" : + "Front-Panel Capture Switch"); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc1220_fixup_gb_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc_fixup_dual_codecs(codec, fix, action); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* override card longname to provide a unique UCM profile */ + strscpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); + break; + case HDA_FIXUP_ACT_BUILD: + /* rename Capture controls depending on the codec */ + rename_ctl(codec, "Capture Volume", + codec->addr == 0 ? + "Rear-Panel Capture Volume" : + "Front-Panel Capture Volume"); + rename_ctl(codec, "Capture Switch", + codec->addr == 0 ? + "Rear-Panel Capture Switch" : + "Front-Panel Capture Switch"); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc233_alc662_fixup_lenovo_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +static void alc_shutup_dell_xps13(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int hp_pin = alc_get_hp_pin(spec); + + /* Prevent pop noises when headphones are plugged in */ + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(20); +} + +void alc_fixup_dell_xps13(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->gen.input_mux; + int i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise + * it causes a click noise at start up + */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); + spec->shutup = alc_shutup_dell_xps13; + break; + case HDA_FIXUP_ACT_PROBE: + /* Make the internal mic the default input source. */ + for (i = 0; i < imux->num_items; i++) { + if (spec->gen.imux_pins[i] == 0x12) { + spec->gen.cur_mux[0] = i; + break; + } + } + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_dell_xps13, "SND_HDA_CODEC_REALTEK"); + +/* + * headset handling + */ + +static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(delay); +} + +static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(delay); +} + +static const struct coef_fw alc225_pre_hsmode[] = { + UPDATE_COEF(0x4a, 1<<8, 0), + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), + UPDATE_COEF(0x63, 3<<14, 3<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x4a, 3<<10, 3<<10), + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + UPDATE_COEF(0x4a, 3<<10, 0), + {} +}; + +static void alc_headset_mode_unplugged(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ + WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ + WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */ + WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ + WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ + WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */ + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x1b, 0x0c0b), + WRITE_COEF(0x45, 0xc429), + UPDATE_COEF(0x35, 0x4000, 0), + WRITE_COEF(0x06, 0x2104), + WRITE_COEF(0x1a, 0x0001), + WRITE_COEF(0x26, 0x0004), + WRITE_COEF(0x32, 0x42a3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0298[] = { + UPDATE_COEF(0x19, 0x1300, 0x0300), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x18, 0x7308), + WRITE_COEF(0x6b, 0xc429), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ + UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ + UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ + WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + {} + }; + static const struct coef_fw coef0668[] = { + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x63, 3<<14, 0), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0100, 0), + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), + UPDATE_COEF(0x6b, 0xf000, 0x5000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x0c00, 0x0c00), + WRITE_COEF(0x45, 0x5289), + UPDATE_COEF(0x4a, 0x0c00, 0), + {} + }; + + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_hp_mute_disable(codec, 75); + alc_process_coef_fw(codec, coef0256); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0298: + alc_process_coef_fw(codec, coef0298); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0668); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_hp_mute_disable(codec, 75); + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_process_coef_fw(codec, coef0225); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to unplugged mode.\n"); +} + + +static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, + hda_nid_t mic_pin) +{ + static const struct coef_fw coef0255[] = { + WRITE_COEFEX(0x57, 0x03, 0x8aa6), + WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ + {} + }; + static const struct coef_fw coef0256[] = { + UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEFEX(0x57, 0x03, 0x09a3), + WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ + {} + }; + static const struct coef_fw coef0233[] = { + UPDATE_COEF(0x35, 0, 1<<14), + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x1a, 0x0021), + WRITE_COEF(0x26, 0x008c), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0x00c0, 0), + UPDATE_COEF(0x50, 0x2000, 0), + UPDATE_COEF(0x56, 0x0006, 0), + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x66, 0x0008, 0x0008), + UPDATE_COEF(0x67, 0x2000, 0x2000), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x19, 0xa208), + WRITE_COEF(0x2e, 0xacf0), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0xb5, 0x1040), + UPDATE_COEF(0xc3, 0, 1<<12), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x63, 3<<14, 0), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x6b, 0xf000, 0), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0255); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0256); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0x4689); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0274); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_write_coef_idx(codec, 0x45, 0xc429); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0233); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0288); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0292: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + /* Set to TRS mode */ + alc_write_coef_idx(codec, 0x45, 0xc429); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0293); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); + fallthrough; + case 0x10ec0221: + case 0x10ec0662: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0668: + alc_write_coef_idx(codec, 0x11, 0x0001); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0688); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0225); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + } + codec_dbg(codec, "Headset jack set to mic-in mode.\n"); +} + +static void alc_headset_mode_default(struct hda_codec *codec) +{ + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10), + UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10), + UPDATE_COEF(0x49, 3<<8, 0<<8), + UPDATE_COEF(0x4a, 3<<4, 3<<4), + UPDATE_COEF(0x63, 3<<14, 0), + UPDATE_COEF(0x67, 0xf000, 0x3000), + {} + }; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xc089), + WRITE_COEF(0x45, 0xc489), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + WRITE_COEF(0x49, 0x0049), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xc489), + WRITE_COEFEX(0x57, 0x03, 0x0da3), + WRITE_COEF(0x49, 0x0049), + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEF(0x06, 0x6100), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x6b, 0xc429), + WRITE_COEF(0x18, 0x7308), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0041), + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; + static const struct coef_fw coef0274[] = { + WRITE_COEF(0x45, 0x4289), + UPDATE_COEF(0x4a, 0x0010, 0x0010), + UPDATE_COEF(0x6b, 0x0f00, 0), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x1b, 0x0e4b); + alc_write_coef_idx(codec, 0x45, 0xc089); + msleep(50); + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); +} + +/* Iphone type */ +static void alc_headset_mode_ctia(struct hda_codec *codec) +{ + int val; + + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ + WRITE_COEF(0x1b, 0x0e6b), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xd429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xd429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static const struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0000), + {} + }; + static const struct coef_fw coef0225_1[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), + UPDATE_COEF(0x63, 3<<14, 2<<14), + {} + }; + static const struct coef_fw coef0225_2[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), + UPDATE_COEF(0x63, 3<<14, 1<<14), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xd689); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0298: + val = alc_read_coef_idx(codec, 0x50); + if (val & (1 << 12)) { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + } else { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + } + break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + val = alc_read_coef_idx(codec, 0x45); + if (val & (1 << 9)) + alc_process_coef_fw(codec, coef0225_2); + else + alc_process_coef_fw(codec, coef0225_1); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); +} + +/* Nokia type */ +static void alc_headset_mode_omtp(struct hda_codec *codec) +{ + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ + WRITE_COEF(0x1b, 0x0e6b), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xe429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xe429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static const struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d50), + WRITE_COEF(0xc3, 0x0000), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), + UPDATE_COEF(0x63, 3<<14, 2<<14), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xe689); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0298: + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; + } + codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); +} + +static void alc_determine_headset_type(struct hda_codec *codec) +{ + int val; + bool is_ctia = false; + struct alc_spec *spec = codec->spec; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ + WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref + conteol) */ + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ + {} + }; + static const struct coef_fw coef0298[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + UPDATE_COEF(0x19, 0x1300, 0x1300), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0c00), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x8000, 0), + WRITE_COEF(0x45, 0xd289), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; + + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x1b, 0x0e4b); + alc_write_coef_idx(codec, 0x06, 0x6104); + alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); + + alc_process_coef_fw(codec, coef0255); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + if (!is_ctia) { + alc_write_coef_idx(codec, 0x45, 0xe089); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x0070) == 0x0070) + is_ctia = false; + else + is_ctia = true; + } + alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + msleep(850); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + break; + case 0x10ec0233: + case 0x10ec0283: + alc_write_coef_idx(codec, 0x45, 0xd029); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0298: + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(100); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(200); + + val = alc_read_coef_idx(codec, 0x50); + if (val & (1 << 12)) { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + } else { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + } + alc_process_coef_fw(codec, coef0298); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + msleep(75); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0292: + alc_write_coef_idx(codec, 0x6b, 0xd429); + msleep(300); + val = alc_read_coef_idx(codec, 0x6c); + is_ctia = (val & 0x001c) == 0x001c; + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + msleep(300); + val = alc_read_coef_idx(codec, 0xbe); + is_ctia = (val & 0x1c02) == 0x1c02; + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); + val = alc_read_coef_idx(codec, 0x45); + if (val & (1 << 9)) { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8); + msleep(800); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + } else { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); + msleep(800); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + } + if (!is_ctia) { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x00f0) == 0x00f0) + is_ctia = false; + else + is_ctia = true; + } + alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); + alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + break; + case 0x10ec0867: + is_ctia = true; + break; + } + + codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", + str_yes_no(is_ctia)); + spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; +} + +static void alc_update_headset_mode(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + + int new_headset_mode; + + if (!snd_hda_jack_detect(codec, hp_pin)) + new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; + else if (mux_pin == spec->headset_mic_pin) + new_headset_mode = ALC_HEADSET_MODE_HEADSET; + else if (mux_pin == spec->headphone_mic_pin) + new_headset_mode = ALC_HEADSET_MODE_MIC; + else + new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; + + if (new_headset_mode == spec->current_headset_mode) { + snd_hda_gen_update_outputs(codec); + return; + } + + switch (new_headset_mode) { + case ALC_HEADSET_MODE_UNPLUGGED: + alc_headset_mode_unplugged(codec); + spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; + spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; + spec->gen.hp_jack_present = false; + break; + case ALC_HEADSET_MODE_HEADSET: + if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) + alc_determine_headset_type(codec); + if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) + alc_headset_mode_ctia(codec); + else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) + alc_headset_mode_omtp(codec); + spec->gen.hp_jack_present = true; + break; + case ALC_HEADSET_MODE_MIC: + alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); + spec->gen.hp_jack_present = false; + break; + case ALC_HEADSET_MODE_HEADPHONE: + alc_headset_mode_default(codec); + spec->gen.hp_jack_present = true; + break; + } + if (new_headset_mode != ALC_HEADSET_MODE_MIC) { + snd_hda_set_pin_ctl_cache(codec, hp_pin, + AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin) + snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, + PIN_VREFHIZ); + } + spec->current_headset_mode = new_headset_mode; + + snd_hda_gen_update_outputs(codec); +} + +static void alc_update_headset_mode_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + alc_update_headset_mode(codec); +} + +void alc_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + snd_hda_gen_hp_automute(codec, jack); + alc_update_headset_mode(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_headset_jack_cb, "SND_HDA_CODEC_REALTEK"); + +static void alc_probe_headset_mode(struct hda_codec *codec) +{ + int i; + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + + /* Find mic pins */ + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) + spec->headset_mic_pin = cfg->inputs[i].pin; + if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) + spec->headphone_mic_pin = cfg->inputs[i].pin; + } + + WARN_ON(spec->gen.cap_sync_hook); + spec->gen.cap_sync_hook = alc_update_headset_mode_hook; + spec->gen.automute_hook = alc_update_headset_mode; + spec->gen.hp_automute_hook = alc_update_headset_jack_cb; +} + +void alc_fixup_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; + break; + case HDA_FIXUP_ACT_PROBE: + alc_probe_headset_mode(codec); + break; + case HDA_FIXUP_ACT_INIT: + if (is_s3_resume(codec) || is_s4_resume(codec)) { + spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; + spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; + } + alc_update_headset_mode(codec); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + } + else + alc_fixup_headset_mode(codec, fix, action); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mic, "SND_HDA_CODEC_REALTEK"); + +/* update LED status via GPIO */ +void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + int polarity, bool enabled) +{ + if (polarity) + enabled = !enabled; + alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ +} +EXPORT_SYMBOL_NS_GPL(alc_update_gpio_led, "SND_HDA_CODEC_REALTEK"); + +/* turn on/off mic-mute LED via GPIO per capture hook */ +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, !brightness); + return 0; +} + +/* turn on/off mute LED via GPIO per vmaster hook */ +static int gpio_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, + spec->mute_led_polarity, !brightness); + return 0; +} + +/* setup mute and mic-mute GPIO bits, add hooks appropriately */ +void alc_fixup_hp_gpio_led(struct hda_codec *codec, + int action, + unsigned int mute_mask, + unsigned int micmute_mask) +{ + struct alc_spec *spec = codec->spec; + + alc_fixup_gpio(codec, action, mute_mask | micmute_mask); + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + if (mute_mask) { + spec->gpio_mute_led_mask = mute_mask; + snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); + } + if (micmute_mask) { + spec->gpio_mic_led_mask = micmute_mask; + snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_hp_gpio_led, "SND_HDA_CODEC_REALTEK"); + +/* suppress the jack-detection */ +void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_no_jack_detect, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_disable_aamix(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + /* Disable AA-loopback as it causes white noise */ + spec->gen.mixer_nid = 0; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_disable_aamix, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->gen.auto_mute_via_amp = 1; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_auto_mute_via_amp, "SND_HDA_CODEC_REALTEK"); + +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek HD-audio codec helper"); diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h new file mode 100644 index 000000000000..ee893da0c486 --- /dev/null +++ b/sound/hda/codecs/realtek/realtek.h @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek HD-audio codec support code +// + +#ifndef __HDA_REALTEK_H +#define __HDA_REALTEK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_beep.h" +#include "hda_jack.h" +#include "../generic.h" +#include "../side-codecs/hda_component.h" + +/* extra amp-initialization sequence types */ +enum { + ALC_INIT_UNDEFINED, + ALC_INIT_NONE, + ALC_INIT_DEFAULT, +}; + +enum { + ALC_HEADSET_MODE_UNKNOWN, + ALC_HEADSET_MODE_UNPLUGGED, + ALC_HEADSET_MODE_HEADSET, + ALC_HEADSET_MODE_MIC, + ALC_HEADSET_MODE_HEADPHONE, +}; + +enum { + ALC_HEADSET_TYPE_UNKNOWN, + ALC_HEADSET_TYPE_CTIA, + ALC_HEADSET_TYPE_OMTP, +}; + +enum { + ALC_KEY_MICMUTE_INDEX, +}; + +struct alc_customize_define { + unsigned int sku_cfg; + unsigned char port_connectivity; + unsigned char check_sum; + unsigned char customization; + unsigned char external_amp; + unsigned int enable_pcbeep:1; + unsigned int platform_type:1; + unsigned int swap:1; + unsigned int override:1; + unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ +}; + +struct alc_coef_led { + unsigned int idx; + unsigned int mask; + unsigned int on; + unsigned int off; +}; + +struct alc_spec { + struct hda_gen_spec gen; /* must be at head */ + + /* codec parameterization */ + struct alc_customize_define cdefine; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + + /* GPIO bits */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + bool gpio_write_delay; /* add a delay before writing gpio_data */ + + /* mute LED for HP laptops, see vref_mute_led_set() */ + int mute_led_polarity; + int micmute_led_polarity; + hda_nid_t mute_led_nid; + hda_nid_t cap_mute_led_nid; + + unsigned int gpio_mute_led_mask; + unsigned int gpio_mic_led_mask; + struct alc_coef_led mute_led_coef; + struct alc_coef_led mic_led_coef; + struct mutex coef_mutex; + + hda_nid_t headset_mic_pin; + hda_nid_t headphone_mic_pin; + int current_headset_mode; + int current_headset_type; + + /* hooks */ + void (*init_hook)(struct hda_codec *codec); + void (*power_hook)(struct hda_codec *codec); + void (*shutup)(struct hda_codec *codec); + + int init_amp; + int codec_variant; /* flag for other variants */ + unsigned int has_alc5505_dsp:1; + unsigned int no_depop_delay:1; + unsigned int done_hp_init:1; + unsigned int no_shutup_pins:1; + unsigned int ultra_low_power:1; + unsigned int has_hs_key:1; + unsigned int no_internal_mic_pin:1; + unsigned int en_3kpull_low:1; + int num_speaker_amps; + + /* for PLL fix */ + hda_nid_t pll_nid; + unsigned int pll_coef_idx, pll_coef_bit; + unsigned int coef0; + struct input_dev *kb_dev; + u8 alc_mute_keycode_map[1]; + + /* component binding */ + struct hda_component_parent comps; +}; + +int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx); +void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val); +void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set); +#define alc_read_coef_idx(codec, coef_idx) \ + alc_read_coefex_idx(codec, 0x20, coef_idx) +#define alc_write_coef_idx(codec, coef_idx, coef_val) \ + alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) +#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ + alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) + +unsigned int alc_get_coef0(struct hda_codec *codec); + +/* coef writes/updates batch */ +struct coef_fw { + unsigned char nid; + unsigned char idx; + unsigned short mask; + unsigned short val; +}; + +#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ + { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } +#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) +#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) +#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) + +void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw); + +/* + * GPIO helpers + */ +void alc_setup_gpio(struct hda_codec *codec, unsigned int mask); +void alc_write_gpio_data(struct hda_codec *codec); +void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, + bool on); +void alc_write_gpio(struct hda_codec *codec); + +/* common GPIO fixups */ +void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask); +void alc_fixup_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio2(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio3(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +/* + * Common init code, callbacks and helpers + */ +void alc_fix_pll(struct hda_codec *codec); +void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_bit); +void alc_fill_eapd_coef(struct hda_codec *codec); +void alc_auto_setup_eapd(struct hda_codec *codec, bool on); + +int alc_find_ext_mic_pin(struct hda_codec *codec); +void alc_headset_mic_no_shutup(struct hda_codec *codec); +void alc_shutup_pins(struct hda_codec *codec); +void alc_eapd_shutup(struct hda_codec *codec); +void alc_auto_init_amp(struct hda_codec *codec, int type); +hda_nid_t alc_get_hp_pin(struct alc_spec *spec); +int alc_auto_parse_customize_define(struct hda_codec *codec); +int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports); +void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports); +int alc_build_controls(struct hda_codec *codec); +void alc_update_knob_master(struct hda_codec *codec, + struct hda_jack_callback *jack); + +static inline void alc_pre_init(struct hda_codec *codec) +{ + alc_fill_eapd_coef(codec); +} + +#define is_s3_resume(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) +#define is_s4_resume(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) +#define is_s4_suspend(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) + +int alc_init(struct hda_codec *codec); +void alc_shutup(struct hda_codec *codec); +void alc_power_eapd(struct hda_codec *codec); +int alc_suspend(struct hda_codec *codec); +int alc_resume(struct hda_codec *codec); + +int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids); +int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid); + +#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir); +int alc_has_cdefine_beep(struct hda_codec *codec); +#define set_beep_amp alc_set_beep_amp +#define has_cdefine_beep alc_has_cdefine_beep +#else +#define set_beep_amp(spec, nid, idx, dir) 0 +#define has_cdefine_beep(codec) 0 +#endif + +static inline void rename_ctl(struct hda_codec *codec, const char *oldname, + const char *newname) +{ + struct snd_kcontrol *kctl; + + kctl = snd_hda_find_mixer_ctl(codec, oldname); + if (kctl) + snd_ctl_rename(codec->card, kctl, newname); +} + +/* Common fixups */ +void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_no_depop_delay(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_inv_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_bass_chmap(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack); +void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + int polarity, bool enabled); +void alc_fixup_hp_gpio_led(struct hda_codec *codec, + int action, + unsigned int mute_mask, + unsigned int micmute_mask); +void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_disable_aamix(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +/* device-specific, but used by multiple codec drivers */ +void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); +void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); +void alc_fixup_dell_xps13(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +#endif /* __HDA_REALTEK_H */ diff --git a/sound/pci/hda/patch_senarytech.c b/sound/hda/codecs/senarytech.c similarity index 87% rename from sound/pci/hda/patch_senarytech.c rename to sound/hda/codecs/senarytech.c index 0691996fa971..9aa1e9bcd9ec 100644 --- a/sound/pci/hda/patch_senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Senary HDA audio codec + * HD audio codec driver for Senary HDA audio codec * - * Initially based on sound/pci/hda/patch_conexant.c + * Initially based on conexant.c */ #include @@ -17,7 +17,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" struct senary_spec { struct hda_gen_spec gen; @@ -129,7 +129,7 @@ static void senary_init_gpio_led(struct hda_codec *codec) } } -static int senary_auto_init(struct hda_codec *codec) +static int senary_init(struct hda_codec *codec) { snd_hda_gen_init(codec); senary_init_gpio_led(codec); @@ -138,7 +138,7 @@ static int senary_auto_init(struct hda_codec *codec) return 0; } -static void senary_auto_shutdown(struct hda_codec *codec) +static void senary_shutdown(struct hda_codec *codec) { struct senary_spec *spec = codec->spec; @@ -148,29 +148,19 @@ static void senary_auto_shutdown(struct hda_codec *codec) senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); } -static void senary_auto_free(struct hda_codec *codec) +static void senary_remove(struct hda_codec *codec) { - senary_auto_shutdown(codec); - snd_hda_gen_free(codec); + senary_shutdown(codec); + snd_hda_gen_remove(codec); } -static int senary_auto_suspend(struct hda_codec *codec) +static int senary_suspend(struct hda_codec *codec) { - senary_auto_shutdown(codec); + senary_shutdown(codec); return 0; } -static const struct hda_codec_ops senary_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = senary_auto_init, - .free = senary_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = senary_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - -static int patch_senary_auto(struct hda_codec *codec) +static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct senary_spec *spec; int err; @@ -182,7 +172,6 @@ static int patch_senary_auto(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - codec->patch_ops = senary_auto_patch_ops; senary_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; @@ -221,15 +210,27 @@ static int patch_senary_auto(struct hda_codec *codec) return 0; error: - senary_auto_free(codec); + senary_remove(codec); return err; } +static const struct hda_codec_ops senary_codec_ops = { + .probe = senary_probe, + .remove = senary_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = senary_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = senary_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* */ static const struct hda_device_id snd_hda_id_senary[] = { - HDA_CODEC_ENTRY(0x1fa86186, "SN6186", patch_senary_auto), + HDA_CODEC_ID(0x1fa86186, "SN6186"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary); @@ -239,6 +240,7 @@ MODULE_DESCRIPTION("Senarytech HD-audio codec"); static struct hda_codec_driver senary_driver = { .id = snd_hda_id_senary, + .ops = &senary_codec_ops, }; module_hda_codec_driver(senary_driver); diff --git a/sound/pci/hda/patch_si3054.c b/sound/hda/codecs/si3054.c similarity index 88% rename from sound/pci/hda/patch_si3054.c rename to sound/hda/codecs/si3054.c index 763eae80a148..87cf9da9f3bf 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/hda/codecs/si3054.c @@ -2,7 +2,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for Silicon Labs 3054/5 modem codec + * HD audio codec driver for Silicon Labs 3054/5 modem codec * * Copyright (c) 2005 Sasha Khapyorsky * Takashi Iwai @@ -246,50 +246,48 @@ static int si3054_init(struct hda_codec *codec) return 0; } -static void si3054_free(struct hda_codec *codec) +static void si3054_remove(struct hda_codec *codec) { kfree(codec->spec); } - /* */ -static const struct hda_codec_ops si3054_patch_ops = { - .build_controls = si3054_build_controls, - .build_pcms = si3054_build_pcms, - .init = si3054_init, - .free = si3054_free, -}; - -static int patch_si3054(struct hda_codec *codec) +static int si3054_probe(struct hda_codec *codec, const struct hda_device_id *id) { - struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) + codec->spec = kzalloc(sizeof(struct si3054_spec), GFP_KERNEL); + if (!codec->spec) return -ENOMEM; - codec->spec = spec; - codec->patch_ops = si3054_patch_ops; return 0; } +static const struct hda_codec_ops si3054_codec_ops = { + .probe = si3054_probe, + .remove = si3054_remove, + .build_controls = si3054_build_controls, + .build_pcms = si3054_build_pcms, + .init = si3054_init, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_si3054[] = { - HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054), + HDA_CODEC_ID(0x163c3055, "Si3054"), + HDA_CODEC_ID(0x163c3155, "Si3054"), + HDA_CODEC_ID(0x11c13026, "Si3054"), + HDA_CODEC_ID(0x11c13055, "Si3054"), + HDA_CODEC_ID(0x11c13155, "Si3054"), + HDA_CODEC_ID(0x10573055, "Si3054"), + HDA_CODEC_ID(0x10573057, "Si3054"), + HDA_CODEC_ID(0x10573155, "Si3054"), /* VIA HDA on Clevo m540 */ - HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054), + HDA_CODEC_ID(0x11063288, "Si3054"), /* Asus A8J Modem (SM56) */ - HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054), + HDA_CODEC_ID(0x15433155, "Si3054"), /* LG LW20 modem */ - HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054), + HDA_CODEC_ID(0x18540018, "Si3054"), {} }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054); @@ -299,6 +297,7 @@ MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); static struct hda_codec_driver si3054_driver = { .id = snd_hda_id_si3054, + .ops = &si3054_codec_ops, }; module_hda_codec_driver(si3054_driver); diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig new file mode 100644 index 000000000000..cbf1847896bc --- /dev/null +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -0,0 +1,128 @@ +config SND_HDA_CIRRUS_SCODEC + tristate + +config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST + tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS + depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT + default KUNIT_ALL_TESTS + help + This builds KUnit tests for the cirrus side-codec library. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". + +config SND_HDA_SCODEC_CS35L41 + tristate + select SND_HDA_GENERIC + select REGMAP_IRQ + select FW_CS_DSP + +config SND_HDA_SCODEC_COMPONENT + tristate + +config SND_HDA_SCODEC_CS35L41_I2C + tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L41 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m + +config SND_HDA_SCODEC_CS35L41_SPI + tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L41 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m + +config SND_HDA_SCODEC_CS35L56 + tristate + +config SND_HDA_SCODEC_CS35L56_I2C + tristate "Build CS35L56 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CIRRUS_SCODEC + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L56 amplifier support with + I2C control. + +config SND_HDA_SCODEC_CS35L56_SPI + tristate "Build CS35L56 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on SND_SOC + select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CIRRUS_SCODEC + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L56 amplifier support with + SPI control. + +config SND_HDA_SCODEC_TAS2781 + tristate + select SND_HDA_GENERIC + +config SND_HDA_SCODEC_TAS2781_I2C + tristate "Build TAS2781 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_HDA_SCODEC_TAS2781 + select SND_SOC_TAS2781_COMLIB_I2C + select SND_SOC_TAS2781_FMWLIB + select CRC32 + help + Say Y or M here to include TAS2781 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m + +config SND_HDA_SCODEC_TAS2781_SPI + tristate "Build TAS2781 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_HDA_SCODEC_TAS2781 + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + select CRC8 + select CRC32 + help + Say Y or M here to include TAS2781 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile new file mode 100644 index 000000000000..245e84f6a121 --- /dev/null +++ b/sound/hda/codecs/side-codecs/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-cirrus-scodec-y := cirrus_scodec.o +snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o +snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o +snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o +snd-hda-scodec-cs35l56-y := cs35l56_hda.o +snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o +snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o +snd-hda-scodec-component-y := hda_component.o +snd-hda-scodec-tas2781-y := tas2781_hda.o +snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o +snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o + +obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o +obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o diff --git a/sound/pci/hda/cirrus_scodec.c b/sound/hda/codecs/side-codecs/cirrus_scodec.c similarity index 100% rename from sound/pci/hda/cirrus_scodec.c rename to sound/hda/codecs/side-codecs/cirrus_scodec.c diff --git a/sound/pci/hda/cirrus_scodec.h b/sound/hda/codecs/side-codecs/cirrus_scodec.h similarity index 100% rename from sound/pci/hda/cirrus_scodec.h rename to sound/hda/codecs/side-codecs/cirrus_scodec.h diff --git a/sound/pci/hda/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c similarity index 99% rename from sound/pci/hda/cirrus_scodec_test.c rename to sound/hda/codecs/side-codecs/cirrus_scodec_test.c index 93b9cbf1f08a..9ba14c09c07f 100644 --- a/sound/pci/hda/cirrus_scodec_test.c +++ b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c @@ -86,7 +86,7 @@ static const struct gpio_chip cirrus_scodec_test_gpio_chip = { .direction_input = cirrus_scodec_test_gpio_direction_in, .get = cirrus_scodec_test_gpio_get, .direction_output = cirrus_scodec_test_gpio_direction_out, - .set_rv = cirrus_scodec_test_gpio_set, + .set = cirrus_scodec_test_gpio_set, .set_config = cirrus_scodec_test_gpio_set_config, .base = -1, .ngpio = 32, diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c similarity index 96% rename from sound/pci/hda/cs35l41_hda.c rename to sound/hda/codecs/side-codecs/cs35l41_hda.c index d5bc81099d0d..37f2cdc8ce82 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -17,7 +17,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "hda_component.h" #include "cs35l41_hda.h" #include "cs35l41_hda_property.h" @@ -93,47 +93,36 @@ module_param(firmware_autostart, bool, 0444); MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot" "(0=Disable, 1=Enable) (default=1); "); +static const char channel_name[3] = { 'L', 'R', 'C' }; + static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1 { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer - { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON - { CS35L41_ASP_TX3_SRC, 0x00000032 }, // ASPTX3 SRC = ERRVOL - { CS35L41_ASP_TX4_SRC, 0x00000033 }, // ASPTX4 SRC = CLASSH_TGT - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000009 }, // DSP1RX2 SRC = ASPRX2 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON +}; + +static const struct reg_sequence cs35l41_hda_config_no_dsp[] = { + { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 + { CS35L41_ASP_TX3_SRC, 0x00000000 }, // ASPTX3 SRC = ZERO FILL + { CS35L41_ASP_TX4_SRC, 0x00000000 }, // ASPTX4 SRC = ZERO FILL { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL + { CS35L41_DSP1_RX6_SRC, 0x00000021 }, // DSP1RX6 SRC = CLASSH_TGT }; static const struct reg_sequence cs35l41_hda_config_dsp[] = { - { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 - { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN - { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1 - { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz - { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled - { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 - { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON - { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; @@ -657,6 +646,41 @@ static void cs35l41_irq_release(struct cs35l41_hda *cs35l41) cs35l41->irq_errors = 0; } +static void cs35l41_update_mixer(struct cs35l41_hda *cs35l41) +{ + struct regmap *reg = cs35l41->regmap; + unsigned int asp_en = 0; + unsigned int dsp1rx2_src = 0; + + regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); + + if (cs35l41->cs_dsp.running) { + asp_en |= CS35L41_ASP_TX1_EN_MASK; // ASP_TX1_EN = 1 + regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, + ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_config_no_dsp, + ARRAY_SIZE(cs35l41_hda_config_no_dsp)); + } + + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) { + asp_en |= CS35L41_ASP_RX2_EN_MASK; // ASP_RX2_EN = 1 + dsp1rx2_src = 0x00000009; // DSP1RX2 SRC = ASPRX2 + } else { + dsp1rx2_src = 0x00000008; // DSP1RX2 SRC = ASPRX1 + } + + asp_en |= CS35L41_ASP_RX1_EN_MASK; // ASP_RX1_EN = 1 + + regmap_write(reg, CS35L41_SP_ENABLES, asp_en); + regmap_write(reg, CS35L41_DSP1_RX1_SRC, 0x00000008); // DSP1RX1 SRC = ASPRX1 + regmap_write(reg, CS35L41_DSP1_RX2_SRC, dsp1rx2_src); +} + static void cs35l41_hda_play_start(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); @@ -671,19 +695,13 @@ static void cs35l41_hda_play_start(struct device *dev) cs35l41->playback_started = true; + cs35l41_update_mixer(cs35l41); + if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, - ARRAY_SIZE(cs35l41_hda_config_dsp)); - if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); - else - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); } regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) @@ -818,7 +836,6 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action) * Playback must be finished for all amps before we start runtime suspend. * This ensures no amps are playing back when we start putting them to sleep. */ - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: @@ -841,22 +858,31 @@ static void cs35l41_hda_post_playback_hook(struct device *dev, int action) } } -static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) +static int cs35l41_hda_channel_map(struct cs35l41_hda *cs35l41) { - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - static const char * const channel_name[] = { "L", "R" }; + unsigned int tx_num = 0; + unsigned int *tx_slot = NULL; + unsigned int rx_num; + unsigned int *rx_slot; + unsigned int mono = 0; if (!cs35l41->amp_name) { - if (*rx_slot >= ARRAY_SIZE(channel_name)) + if (cs35l41->hw_cfg.spk_pos >= ARRAY_SIZE(channel_name)) return -EINVAL; - cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d", - channel_name[*rx_slot], cs35l41->channel_index); + cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%c%d", + channel_name[cs35l41->hw_cfg.spk_pos], + cs35l41->channel_index); if (!cs35l41->amp_name) return -ENOMEM; } + rx_num = 1; + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) + rx_slot = &mono; + else + rx_slot = &cs35l41->hw_cfg.spk_pos; + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, rx_slot); } @@ -1257,7 +1283,6 @@ static void cs35l41_fw_load_work(struct work_struct *work) cs35l41->fw_request_ongoing = false; mutex_unlock(&cs35l41->fw_mutex); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_put_autosuspend(cs35l41->dev); } @@ -1488,14 +1513,13 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas dev_warn(dev, "Unable to create device link\n"); unlock_system_sleep(sleep_flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); dev_info(cs35l41->dev, "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH, - cs35l41->hw_cfg.spk_pos ? 'R' : 'L', + channel_name[cs35l41->hw_cfg.spk_pos], cs35l41->cs_dsp.running, cs35l41->speaker_id); return ret; @@ -1709,7 +1733,7 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) if (using_irq) cs35l41_configure_interrupt(cs35l41, irq_pol); - return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); + return cs35l41_hda_channel_map(cs35l41); } int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id) @@ -2009,7 +2033,6 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); pm_runtime_use_autosuspend(cs35l41->dev); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_set_active(cs35l41->dev); pm_runtime_get_noresume(cs35l41->dev); pm_runtime_enable(cs35l41->dev); @@ -2085,3 +2108,5 @@ MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("FW_CS_DSP"); +MODULE_FIRMWARE("cirrus/cs35l41-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l41-*.bin"); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h similarity index 99% rename from sound/pci/hda/cs35l41_hda.h rename to sound/hda/codecs/side-codecs/cs35l41_hda.h index c730b3351589..7d003c598e93 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.h @@ -42,6 +42,7 @@ struct cs35l41_amp_efi_data { enum cs35l41_hda_spk_pos { CS35L41_LEFT, CS35L41_RIGHT, + CS35L41_CENTER, }; enum cs35l41_hda_gpio_function { diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_i2c.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_property.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_property.c diff --git a/sound/pci/hda/cs35l41_hda_property.h b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h similarity index 100% rename from sound/pci/hda/cs35l41_hda_property.h rename to sound/hda/codecs/side-codecs/cs35l41_hda_property.h diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_spi.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_spi.c diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c similarity index 93% rename from sound/pci/hda/cs35l56_hda.c rename to sound/hda/codecs/side-codecs/cs35l56_hda.c index 3f2fd32f4ad9..36fa62a41984 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -20,7 +20,7 @@ #include "cirrus_scodec.h" #include "cs35l56_hda.h" #include "hda_component.h" -#include "hda_generic.h" +#include "../generic.h" /* * The cs35l56_hda_dai_config[] reg sequence configures the device as @@ -89,7 +89,6 @@ static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56) BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) | BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT)); - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } @@ -873,6 +872,52 @@ static int cs35l56_hda_system_resume(struct device *dev) return 0; } +static int cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr) +{ + /* The cirrus,dev-index property has the wrong values */ + switch (*bus_addr) { + case 0x30: + cs35l56->index = 1; + return 0; + case 0x31: + cs35l56->index = 0; + return 0; + default: + /* There is a pseudo-address for broadcast to both amps - ignore it */ + dev_dbg(cs35l56->base.dev, "Ignoring I2C address %#x\n", *bus_addr); + return 0; + } +} + +static const struct { + const char *sub; + int (*fixup_fn)(struct cs35l56_hda *cs35l56, int *bus_addr); +} cs35l56_hda_fixups[] = { + { + .sub = "17AA390B", /* Lenovo Yoga Book 9i GenX */ + .fixup_fn = cs35l56_hda_fixup_yoga9, + }, +}; + +static int cs35l56_hda_apply_platform_fixups(struct cs35l56_hda *cs35l56, const char *sub, + int *bus_addr) +{ + int i; + + if (IS_ERR(sub)) + return 0; + + for (i = 0; i < ARRAY_SIZE(cs35l56_hda_fixups); i++) { + if (strcasecmp(cs35l56_hda_fixups[i].sub, sub) == 0) { + dev_dbg(cs35l56->base.dev, "Applying fixup for %s\n", + cs35l56_hda_fixups[i].sub); + return (cs35l56_hda_fixups[i].fixup_fn)(cs35l56, bus_addr); + } + } + + return 0; +} + static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) { u32 values[HDA_MAX_COMPONENTS]; @@ -897,39 +942,47 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) ACPI_COMPANION_SET(cs35l56->base.dev, adev); } - property = "cirrus,dev-index"; - ret = device_property_count_u32(cs35l56->base.dev, property); - if (ret <= 0) - goto err; - - if (ret > ARRAY_SIZE(values)) { - ret = -EINVAL; - goto err; - } - nval = ret; - - ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); - if (ret) - goto err; - + /* Initialize things that could be overwritten by a fixup */ cs35l56->index = -1; - for (i = 0; i < nval; i++) { - if (values[i] == id) { - cs35l56->index = i; - break; - } - } - /* - * It's not an error for the ID to be missing: for I2C there can be - * an alias address that is not a real device. So reject silently. - */ - if (cs35l56->index == -1) { - dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); - ret = -ENODEV; - goto err; - } sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); + ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id); + if (ret) + return ret; + + if (cs35l56->index == -1) { + property = "cirrus,dev-index"; + ret = device_property_count_u32(cs35l56->base.dev, property); + if (ret <= 0) + goto err; + + if (ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); + if (ret) + goto err; + + for (i = 0; i < nval; i++) { + if (values[i] == id) { + cs35l56->index = i; + break; + } + } + + /* + * It's not an error for the ID to be missing: for I2C there can be + * an alias address that is not a real device. So reject silently. + */ + if (cs35l56->index == -1) { + dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + } if (IS_ERR(sub)) { dev_info(cs35l56->base.dev, @@ -1122,3 +1175,7 @@ MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_AUTHOR("Simon Trimmer "); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("cirrus/cs35l54-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l54-*.bin"); +MODULE_FIRMWARE("cirrus/cs35l56-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l56-*.bin"); diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h similarity index 100% rename from sound/pci/hda/cs35l56_hda.h rename to sound/hda/codecs/side-codecs/cs35l56_hda.h diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c similarity index 100% rename from sound/pci/hda/cs35l56_hda_i2c.c rename to sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c similarity index 100% rename from sound/pci/hda/cs35l56_hda_spi.c rename to sound/hda/codecs/side-codecs/cs35l56_hda_spi.c diff --git a/sound/pci/hda/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c similarity index 100% rename from sound/pci/hda/hda_component.c rename to sound/hda/codecs/side-codecs/hda_component.c diff --git a/sound/pci/hda/hda_component.h b/sound/hda/codecs/side-codecs/hda_component.h similarity index 100% rename from sound/pci/hda/hda_component.h rename to sound/hda/codecs/side-codecs/hda_component.h diff --git a/sound/pci/hda/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c similarity index 89% rename from sound/pci/hda/tas2781_hda.c rename to sound/hda/codecs/side-codecs/tas2781_hda.c index 5f1d4b3e9688..f46d2e06c64f 100644 --- a/sound/pci/hda/tas2781_hda.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda.c @@ -18,6 +18,8 @@ #include "tas2781_hda.h" +#define CALIBRATION_DATA_AREA_NUM 2 + const efi_guid_t tasdev_fct_efi_guid[] = { /* DELL */ EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74, @@ -44,7 +46,7 @@ static void tas2781_apply_calib(struct tasdevice_priv *p) TASDEVICE_REG(0, 0x13, 0x70), TASDEVICE_REG(0, 0x18, 0x7c), }; - unsigned int crc, oft; + unsigned int crc, oft, node_num; unsigned char *buf; int i, j, k, l; @@ -80,8 +82,9 @@ static void tas2781_apply_calib(struct tasdevice_priv *p) dev_err(p->dev, "%s: CRC error\n", __func__); return; } + node_num = tmp_val[1]; - for (j = 0, k = 0; j < tmp_val[1]; j++) { + for (j = 0, k = 0; j < node_num; j++) { oft = j * 6 + 3; if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) { for (i = 0; i < TASDEV_CALIB_N; i++) { @@ -99,8 +102,9 @@ static void tas2781_apply_calib(struct tasdevice_priv *p) } data[l] = k; + oft++; for (i = 0; i < TASDEV_CALIB_N * 4; i++) - data[l + i] = data[4 * oft + i]; + data[l + i + 1] = data[4 * oft + i]; k++; } } @@ -158,36 +162,51 @@ int tas2781_save_calibration(struct tas2781_hda *hda) * manufactory. */ efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO]; - static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME; + /* + * Some devices save the calibrated data into L"CALI_DATA", + * and others into L"SmartAmpCalibrationData". + */ + static efi_char16_t *efi_name[CALIBRATION_DATA_AREA_NUM] = { + L"CALI_DATA", + L"SmartAmpCalibrationData", + }; struct tasdevice_priv *p = hda->priv; struct calidata *cali_data = &p->cali_data; unsigned long total_sz = 0; unsigned int attr, size; unsigned char *data; efi_status_t status; + int i; if (hda->catlog_id < LENOVO) efi_guid = tasdev_fct_efi_guid[hda->catlog_id]; cali_data->cali_dat_sz_per_dev = 20; size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); - /* Get real size of UEFI variable */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, NULL); - cali_data->total_sz = total_sz > size ? total_sz : size; - if (status == EFI_BUFFER_TOO_SMALL) { - /* Allocate data buffer of data_size bytes */ - data = p->cali_data.data = devm_kzalloc(p->dev, - p->cali_data.total_sz, GFP_KERNEL); - if (!data) { - p->cali_data.total_sz = 0; - return -ENOMEM; + for (i = 0; i < CALIBRATION_DATA_AREA_NUM; i++) { + /* Get real size of UEFI variable */ + status = efi.get_variable(efi_name[i], &efi_guid, &attr, + &total_sz, NULL); + cali_data->total_sz = total_sz > size ? total_sz : size; + if (status == EFI_BUFFER_TOO_SMALL) { + /* Allocate data buffer of data_size bytes */ + data = cali_data->data = devm_kzalloc(p->dev, + cali_data->total_sz, GFP_KERNEL); + if (!data) { + status = -ENOMEM; + continue; + } + /* Get variable contents into buffer */ + status = efi.get_variable(efi_name[i], &efi_guid, + &attr, &cali_data->total_sz, data); } - /* Get variable contents into buffer */ - status = efi.get_variable(efi_name, &efi_guid, &attr, - &p->cali_data.total_sz, data); + /* Check whether get the calibrated data */ + if (status == EFI_SUCCESS) + break; } + if (status != EFI_SUCCESS) { - p->cali_data.total_sz = 0; + cali_data->total_sz = 0; return status; } diff --git a/sound/pci/hda/tas2781_hda.h b/sound/hda/codecs/side-codecs/tas2781_hda.h similarity index 98% rename from sound/pci/hda/tas2781_hda.h rename to sound/hda/codecs/side-codecs/tas2781_hda.h index 575a701c8dfb..66188909a0bb 100644 --- a/sound/pci/hda/tas2781_hda.h +++ b/sound/hda/codecs/side-codecs/tas2781_hda.h @@ -11,7 +11,7 @@ /* Flag of calibration registers address. */ #define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7) -#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA" + #define TASDEV_CALIB_N 5 /* diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c similarity index 84% rename from sound/pci/hda/tas2781_hda_i2c.c rename to sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index d91eed9f7804..45a70fbf6205 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -24,13 +24,14 @@ #include #include #include +#include #include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_component.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "tas2781_hda.h" #define TAS2563_CAL_VAR_NAME_MAX 16 @@ -45,9 +46,18 @@ #define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14) #define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34) +enum device_chip_id { + HDA_TAS2563, + HDA_TAS2770, + HDA_TAS2781, + HDA_OTHERS +}; + struct tas2781_hda_i2c_priv { struct snd_kcontrol *snd_ctls[2]; int (*save_calibration)(struct tas2781_hda *h); + + int hda_chip_id; }; static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) @@ -157,7 +167,6 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) tas_hda->priv->playback_started = false; mutex_unlock(&tas_hda->priv->codec_lock); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: @@ -246,15 +255,24 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static const struct snd_kcontrol_new tas2770_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2770_AMP_LEVEL, + 0, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_amp_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2770_DVC_LEVEL, + 0, 0, 200, 1, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_dvc_tlv), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { - ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, - tas2781_amp_putvol, amp_vol_tlv), + tas2781_amp_putvol, tas2781_amp_tlv), ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, tas2781_force_fwload_get, tas2781_force_fwload_put), }; -static const struct snd_kcontrol_new tas2781_prof_ctrl = { +static const struct snd_kcontrol_new tasdevice_prof_ctrl = { .name = "Speaker Profile Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_profile, @@ -262,7 +280,7 @@ static const struct snd_kcontrol_new tas2781_prof_ctrl = { .put = tasdevice_set_profile_id, }; -static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_prog_ctrl = { .name = "Speaker Program Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_programs, @@ -270,7 +288,7 @@ static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { .put = tasdevice_program_put, }; -static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_conf_ctrl = { .name = "Speaker Config Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_config, @@ -282,16 +300,17 @@ static int tas2563_save_calibration(struct tas2781_hda *h) { efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO]; char *vars[TASDEV_CALIB_N] = { - "R0_%d", "InvR0_%d", "R0_Low_%d", "Power_%d", "TLim_%d" + "R0_%d", "R0_Low_%d", "InvR0_%d", "Power_%d", "TLim_%d" }; efi_char16_t efi_name[TAS2563_CAL_VAR_NAME_MAX]; unsigned long max_size = TAS2563_CAL_DATA_SIZE; unsigned char var8[TAS2563_CAL_VAR_NAME_MAX]; - struct tasdevice_priv *p = h->hda_priv; + struct tasdevice_priv *p = h->priv; struct calidata *cd = &p->cali_data; struct cali_reg *r = &cd->cali_reg_array; unsigned int offset = 0; unsigned char *data; + __be32 bedata; efi_status_t status; unsigned int attr; int ret, i, j, k; @@ -309,8 +328,8 @@ static int tas2563_save_calibration(struct tas2781_hda *h) data[offset] = i; offset++; for (j = 0; j < TASDEV_CALIB_N; ++j) { - ret = snprintf(var8, sizeof(var8), vars[j], i); - + /* EFI name for calibration started with 1, not 0 */ + ret = snprintf(var8, sizeof(var8), vars[j], i + 1); if (ret < 0 || ret >= sizeof(var8) - 1) { dev_err(p->dev, "%s: Read %s failed\n", __func__, var8); @@ -333,6 +352,8 @@ static int tas2563_save_calibration(struct tas2781_hda *h) i, j, status); return -EINVAL; } + bedata = cpu_to_be32(*(uint32_t *)&data[offset]); + memcpy(&data[offset], &bedata, sizeof(bedata)); offset += TAS2563_CAL_DATA_SIZE; } } @@ -379,44 +400,34 @@ static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) snd_ctl_remove(codec->card, tas_hda->prof_ctl); } -static void tasdev_fw_ready(const struct firmware *fmw, void *context) +static void tasdev_add_kcontrols(struct tasdevice_priv *tas_priv, + struct snd_kcontrol **ctls, struct hda_codec *codec, + const struct snd_kcontrol_new *tas_snd_ctrls, int num_ctls) +{ + int i, ret; + + for (i = 0; i < num_ctls; i++) { + ctls[i] = snd_ctl_new1( + &tas_snd_ctrls[i], tas_priv); + ret = snd_ctl_add(codec->card, ctls[i]); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas_snd_ctrls[i].name, ret); + break; + } + } +} + +static void tasdevice_dspfw_init(void *context) { struct tasdevice_priv *tas_priv = context; struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; struct hda_codec *codec = tas_priv->codec; - int i, ret, spk_id; - - pm_runtime_get_sync(tas_priv->dev); - mutex_lock(&tas_priv->codec_lock); - - ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) - goto out; - - tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl, tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_prof_ctrl.name, ret); - goto out; - } - - for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) { - hda_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i], - tas_priv); - ret = snd_ctl_add(codec->card, hda_priv->snd_ctls[i]); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_snd_controls[i].name, ret); - goto out; - } - } + int ret, spk_id; tasdevice_dsp_remove(tas_priv); - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; if (tas_priv->speaker_id != NULL) { // Speaker id need to be checked for ASUS only. @@ -442,28 +453,12 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; - goto out; - } - - tas_hda->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_prog_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_prog_ctrl.name, ret); - goto out; - } - - tas_hda->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_conf_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_conf_ctrl.name, ret); - goto out; + return; } + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_prog_ctl, codec, + &tasdevice_dsp_prog_ctrl, 1); + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_conf_ctl, codec, + &tasdevice_dsp_conf_ctrl, 1); tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; tasdevice_prmg_load(tas_priv, 0); @@ -476,14 +471,49 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) * calibrated data inside algo. */ hda_priv->save_calibration(tas_hda); +} - tasdevice_tuning_switch(tas_hda->priv, 0); - tas_hda->priv->playback_started = true; +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); + struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; + struct hda_codec *codec = tas_priv->codec; + int ret; + + pm_runtime_get_sync(tas_priv->dev); + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; + tasdev_add_kcontrols(tas_priv, &tas_hda->prof_ctl, codec, + &tasdevice_prof_ctrl, 1); + + switch (hda_priv->hda_chip_id) { + case HDA_TAS2770: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2770_snd_controls[0], + ARRAY_SIZE(tas2770_snd_controls)); + break; + case HDA_TAS2781: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2781_snd_controls[0], + ARRAY_SIZE(tas2781_snd_controls)); + tasdevice_dspfw_init(context); + break; + case HDA_TAS2563: + tasdevice_dspfw_init(context); + break; + default: + break; + } out: mutex_unlock(&tas_hda->priv->codec_lock); release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->dev); pm_runtime_put_autosuspend(tas_hda->dev); } @@ -526,7 +556,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -584,15 +613,38 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) return -ENOMEM; if (strstr(dev_name(&clt->dev), "TIAS2781")) { + /* + * TAS2781, integrated on-chip DSP with + * global I2C address supported. + */ device_name = "TIAS2781"; + hda_priv->hda_chip_id = HDA_TAS2781; + hda_priv->save_calibration = tas2781_save_calibration; + tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; + } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) { + /* + * TAS2770, has no on-chip DSP, so no calibration data + * required; has no global I2C address supported. + */ + device_name = "TXNW2770"; + hda_priv->hda_chip_id = HDA_TAS2770; + } else if (strstarts(dev_name(&clt->dev), + "i2c-TXNW2781:00-tas2781-hda.0")) { + device_name = "TXNW2781"; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; } else if (strstr(dev_name(&clt->dev), "INT8866")) { + /* + * TAS2563, integrated on-chip DSP with + * global I2C address supported. + */ device_name = "INT8866"; + hda_priv->hda_chip_id = HDA_TAS2563; hda_priv->save_calibration = tas2563_save_calibration; tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR; - } else + } else { return -ENODEV; + } tas_hda->priv->irq = clt->irq; ret = tas2781_read_acpi(tas_hda->priv, device_name); @@ -722,8 +774,10 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = { }; static const struct acpi_device_id tas2781_acpi_hda_match[] = { - {"TIAS2781", 0 }, {"INT8866", 0 }, + {"TIAS2781", 0 }, + {"TXNW2770", 0 }, + {"TXNW2781", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c similarity index 98% rename from sound/pci/hda/tas2781_hda_spi.c rename to sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 5c03e9d2283a..b9a55672bf15 100644 --- a/sound/pci/hda/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -35,7 +35,7 @@ #include "hda_auto_parser.h" #include "hda_component.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "tas2781_hda.h" #define TASDEVICE_RANGE_MAX_SIZE (256 * 128) @@ -400,7 +400,6 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) tasdevice_tuning_switch(tas_priv, 1); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } } @@ -495,9 +494,11 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new tas2781_snd_ctls[] = { ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_AMP_LEVEL, 1, 0, 20, 0, - tas2781_amp_getvol, tas2781_amp_putvol, amp_vol_tlv), + tas2781_amp_getvol, tas2781_amp_putvol, + tas2781_amp_tlv), ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_DVC_LVL, 0, 0, 200, 1, - tas2781_digital_getvol, tas2781_digital_putvol, dvc_tlv), + tas2781_digital_getvol, tas2781_digital_putvol, + tas2781_dvc_tlv), ACARD_SINGLE_BOOL_EXT(NULL, 0, tas2781_force_fwload_get, tas2781_force_fwload_put), }; @@ -698,7 +699,6 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tas2781_save_calibration(tas_hda); out: release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->priv->dev); pm_runtime_put_autosuspend(tas_hda->priv->dev); } @@ -731,7 +731,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -816,7 +815,6 @@ static int tas2781_hda_spi_probe(struct spi_device *spi) pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); pm_runtime_use_autosuspend(tas_priv->dev); - pm_runtime_mark_last_busy(tas_priv->dev); pm_runtime_set_active(tas_priv->dev); pm_runtime_get_noresume(tas_priv->dev); pm_runtime_enable(tas_priv->dev); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/hda/codecs/sigmatel.c similarity index 94% rename from sound/pci/hda/patch_sigmatel.c rename to sound/hda/codecs/sigmatel.c index bde6b7373858..ecbee408d771 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -2,12 +2,12 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for SigmaTel STAC92xx + * HD audio codec driver for SigmaTel STAC92xx * * Copyright (c) 2005 Embedded Alley Solutions, Inc. * Matt Porter * - * Based on patch_cmedia.c and patch_realtek.c + * Based on cmedia.c and realtek.c * Copyright (c) 2004 Takashi Iwai */ @@ -24,7 +24,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" enum { STAC_REF, @@ -4391,8 +4391,6 @@ static int stac_init(struct hda_codec *codec) return 0; } -#define stac_free snd_hda_gen_free - #ifdef CONFIG_SND_PROC_FS static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) @@ -4454,15 +4452,6 @@ static int stac_suspend(struct hda_codec *codec) return 0; } -static const struct hda_codec_ops stac_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = stac_init, - .free = stac_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = stac_suspend, -}; - static int alloc_stac_spec(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4474,19 +4463,14 @@ static int alloc_stac_spec(struct hda_codec *codec) codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ spec->gen.dac_min_mute = true; - codec->patch_ops = stac_patch_ops; return 0; } -static int patch_stac9200(struct hda_codec *codec) +static int probe_stac9200(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4500,25 +4484,19 @@ static int patch_stac9200(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -static int patch_stac925x(struct hda_codec *codec) +static int probe_stac925x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4530,26 +4508,20 @@ static int patch_stac925x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -static int patch_stac92hd73xx(struct hda_codec *codec) +static int probe_stac92hd73xx(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; int num_dacs; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; /* enable power_save_node only for new 92HD89xx chips, as it causes * click noises on old 92HD73xx chips. @@ -4604,10 +4576,8 @@ static int patch_stac92hd73xx(struct hda_codec *codec) snd_hda_add_verbs(codec, stac92hd73xx_core_init); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } /* Don't GPIO-mute speakers if there are no internal speakers, because * the GPIO might be necessary for Headphone @@ -4646,15 +4616,11 @@ static void stac_setup_gpio(struct hda_codec *codec) } } -static int patch_stac92hd83xxx(struct hda_codec *codec) +static int probe_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - /* longer delay needed for D3 */ codec->core.power_caps &= ~AC_PWRST_EPSS; @@ -4679,10 +4645,8 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd_proc_hook; @@ -4695,15 +4659,11 @@ static const hda_nid_t stac92hd95_pwr_nids[] = { 0x0a, 0x0b, 0x0c, 0x0d }; -static int patch_stac92hd95(struct hda_codec *codec) +static int probe_stac92hd95(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - /* longer delay needed for D3 */ codec->core.power_caps &= ~AC_PWRST_EPSS; @@ -4725,10 +4685,8 @@ static int patch_stac92hd95(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd_proc_hook; @@ -4737,16 +4695,12 @@ static int patch_stac92hd95(struct hda_codec *codec) return 0; } -static int patch_stac92hd71bxx(struct hda_codec *codec) +static int probe_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; /* disabled power_save_node since it causes noises on a Dell machine */ /* codec->power_save_node = 1; */ @@ -4809,10 +4763,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd7x_proc_hook; @@ -4821,15 +4773,11 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) return 0; } -static int patch_stac922x(struct hda_codec *codec) +static int probe_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4848,10 +4796,8 @@ static int patch_stac922x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -4863,15 +4809,11 @@ static const char * const stac927x_spdif_labels[] = { "Analog Mux 2", "Analog Mux 3", NULL }; -static int patch_stac927x(struct hda_codec *codec) +static int probe_stac927x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4897,10 +4839,8 @@ static int patch_stac927x(struct hda_codec *codec) snd_hda_add_verbs(codec, stac927x_core_init); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac927x_proc_hook; @@ -4921,15 +4861,11 @@ static int patch_stac927x(struct hda_codec *codec) return 0; } -static int patch_stac9205(struct hda_codec *codec) +static int probe_stac9205(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4955,10 +4891,8 @@ static int patch_stac9205(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac9205_proc_hook; @@ -5008,15 +4942,11 @@ static const struct hda_quirk stac9872_fixup_tbl[] = { {} /* terminator */ }; -static int patch_stac9872(struct hda_codec *codec) +static int probe_stac9872(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -5028,125 +4958,202 @@ static int patch_stac9872(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return -EINVAL; - } + if (err < 0) + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } +/* + * common driver probe + */ + +enum { + MODEL_STAC9200, + MODEL_STAC9205, + MODEL_STAC922X, + MODEL_STAC925X, + MODEL_STAC927X, + MODEL_STAC9872, + MODEL_STAC92HD71BXX, + MODEL_STAC92HD73XX, + MODEL_STAC92HD83XXX, + MODEL_STAC92HD95, +}; + +static int stac_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + int err; + + err = alloc_stac_spec(codec); + if (err < 0) + return err; + + switch (id->driver_data) { + case MODEL_STAC9200: + err = probe_stac9200(codec); + break; + case MODEL_STAC9205: + err = probe_stac9205(codec); + break; + case MODEL_STAC922X: + err = probe_stac922x(codec); + break; + case MODEL_STAC925X: + err = probe_stac925x(codec); + break; + case MODEL_STAC927X: + err = probe_stac927x(codec); + break; + case MODEL_STAC9872: + err = probe_stac9872(codec); + break; + case MODEL_STAC92HD71BXX: + err = probe_stac92hd71bxx(codec); + break; + case MODEL_STAC92HD73XX: + err = probe_stac92hd73xx(codec); + break; + case MODEL_STAC92HD83XXX: + err = probe_stac92hd83xxx(codec); + break; + case MODEL_STAC92HD95: + err = probe_stac92hd95(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_gen_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops stac_codec_ops = { + .probe = stac_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = stac_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = stac_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_sigmatel[] = { - HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200), - HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x), - HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x), - HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x), - HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x), - HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x), - HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x), + HDA_CODEC_ID_MODEL(0x83847690, "STAC9200", MODEL_STAC9200), + HDA_CODEC_ID_MODEL(0x83847882, "STAC9220 A1", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847680, "STAC9221 A1", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847880, "STAC9220 A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847681, "STAC9220D/9223D A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847682, "STAC9221 A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847683, "STAC9221D A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847618, "STAC9227", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847619, "STAC9227", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847638, "STAC92HD700", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847616, "STAC9228", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847617, "STAC9228", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847614, "STAC9229", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847615, "STAC9229", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847620, "STAC9274", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847621, "STAC9274D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847622, "STAC9273X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847623, "STAC9273D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847624, "STAC9272X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847625, "STAC9272D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847626, "STAC9271X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847627, "STAC9271D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847628, "STAC9274X5NH", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847629, "STAC9274D5NH", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847632, "STAC9202", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847633, "STAC9202D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847634, "STAC9250", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847635, "STAC9250D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847636, "STAC9251", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847637, "STAC9250D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847645, "92HD206X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847646, "92HD206D", MODEL_STAC927X), /* The following does not take into account .id=0x83847661 when subsys = * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are * currently not fully supported. */ - HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872), - HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872), - HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872), - HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205), - HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95), - HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx), + HDA_CODEC_ID_MODEL(0x83847661, "CXD9872RD/K", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847662, "STAC9872AK", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847664, "CXD9872AKD", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847698, "STAC9205", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a0, "STAC9205", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a1, "STAC9205D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a2, "STAC9204", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a3, "STAC9204D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a4, "STAC9255", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a5, "STAC9255D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a6, "STAC9254", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a7, "STAC9254D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x111d7603, "92HD75B3X5", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d7604, "92HD83C1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d4, "92HD83C1C5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7605, "92HD81B1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d5, "92HD81B1C5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d1, "92HD87B1/3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d9, "92HD87B2/4", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7666, "92HD88B3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7667, "92HD88B1", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7668, "92HD88B2", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7669, "92HD88B4", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7608, "92HD75B2X5", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d7674, "92HD73D1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7675, "92HD73C1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7676, "92HD73E1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7695, "92HD95", MODEL_STAC92HD95), + HDA_CODEC_ID_MODEL(0x111d76b0, "92HD71B8X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b1, "92HD71B8X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b2, "92HD71B7X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b3, "92HD71B7X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b4, "92HD71B6X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b5, "92HD71B6X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b6, "92HD71B5X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b7, "92HD71B5X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76c0, "92HD89C3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c1, "92HD89C2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c2, "92HD89C1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c3, "92HD89B3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c4, "92HD89B2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c5, "92HD89B1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c6, "92HD89E3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c7, "92HD89E2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c8, "92HD89E1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c9, "92HD89D3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76ca, "92HD89D2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cb, "92HD89D1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cc, "92HD89F3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cd, "92HD89F2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76ce, "92HD89F1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76df, "92HD93BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e0, "92HD91BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e3, "92HD98BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e5, "92HD99BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e7, "92HD90BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e8, "92HD66B1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e9, "92HD66B2X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ea, "92HD66B3X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76eb, "92HD66C1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ec, "92HD66C2X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ed, "92HD66C3X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ee, "92HD66B1X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ef, "92HD66B2X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f0, "92HD66B3X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f1, "92HD66C1X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f2, "92HD66C2X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f3, "92HD66C3/65", MODEL_STAC92HD83XXX), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel); @@ -5156,6 +5163,7 @@ MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); static struct hda_codec_driver sigmatel_driver = { .id = snd_hda_id_sigmatel, + .ops = &stac_codec_ops, }; module_hda_codec_driver(sigmatel_driver); diff --git a/sound/pci/hda/patch_via.c b/sound/hda/codecs/via.c similarity index 78% rename from sound/pci/hda/patch_via.c rename to sound/hda/codecs/via.c index d0893059b1b9..6becea9bb810 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/hda/codecs/via.c @@ -2,7 +2,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec + * HD audio codec driver for VIA VT17xx/VT18xx/VT20xx codec * * (C) 2006-2009 VIA Technology, Inc. * (C) 2006-2008 Takashi Iwai @@ -43,7 +43,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 @@ -52,8 +52,10 @@ enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, + VT1709, VT1709_10CH, VT1709_6CH, + VT1708B, VT1708B_8CH, VT1708B_4CH, VT1708S, @@ -66,6 +68,7 @@ enum VIA_HDA_CODEC { VT1802, VT1705CF, VT1808, + VT3476, CODEC_TYPES, }; @@ -95,8 +98,6 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action); -static const struct hda_codec_ops via_patch_ops; /* defined below */ - static struct via_spec *via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -118,7 +119,6 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; codec->power_save_node = 1; spec->gen.power_down_unused = 1; - codec->patch_ops = via_patch_ops; return spec; } @@ -373,10 +373,10 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, vt1708_update_hp_work(codec); } -static void via_free(struct hda_codec *codec) +static void via_remove(struct hda_codec *codec) { vt1708_stop_hp_work(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } static int via_suspend(struct hda_codec *codec) @@ -395,7 +395,7 @@ static int via_resume(struct hda_codec *codec) { /* some delay here to make jack detection working (bko#98921) */ msleep(10); - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); return 0; } @@ -411,20 +411,6 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* */ -static int via_init(struct hda_codec *codec); - -static const struct hda_codec_ops via_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = via_suspend, - .resume = via_resume, - .check_power_status = via_check_power_status, -}; - - static const struct hda_verb vt1708_init_verbs[] = { /* power down jack detect function */ {0x1, 0xf81, 0x1}, @@ -541,19 +527,21 @@ static int via_init(struct hda_codec *codec) return 0; } -static int vt1708_build_controls(struct hda_codec *codec) +static int via_build_controls(struct hda_codec *codec) { /* In order not to create "Phantom Jack" controls, temporary enable jackpoll */ int err; int old_interval = codec->jackpoll_interval; - codec->jackpoll_interval = msecs_to_jiffies(100); + if (old_interval) + codec->jackpoll_interval = msecs_to_jiffies(100); err = snd_hda_gen_build_controls(codec); - codec->jackpoll_interval = old_interval; + if (old_interval) + codec->jackpoll_interval = old_interval; return err; } -static int vt1708_build_pcms(struct hda_codec *codec) +static int via_build_pcms(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i, err; @@ -580,19 +568,11 @@ static int vt1708_build_pcms(struct hda_codec *codec) return 0; } -static int patch_vt1708(struct hda_codec *codec) +static int probe_vt1708(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* override some patch_ops */ - codec->patch_ops.build_controls = vt1708_build_controls; - codec->patch_ops.build_pcms = vt1708_build_pcms; spec->gen.mixer_nid = 0x17; /* set jackpoll_interval while parsing the codec */ @@ -611,81 +591,47 @@ static int patch_vt1708(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1708_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); if (err < 0) - goto error; + return err; /* add jack detect on/off control */ - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { - err = -ENOMEM; - goto error; - } + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) + return -ENOMEM; /* clear jackpoll_interval again; it's set dynamically */ codec->jackpoll_interval = 0; return 0; - - error: - via_free(codec); - return err; } -static int patch_vt1709(struct hda_codec *codec) +static int probe_vt1709(struct hda_codec *codec) { - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + struct via_spec *spec = codec->spec; spec->gen.mixer_nid = 0x18; - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B(struct hda_codec *codec) +static int probe_vt1708S(struct hda_codec *codec); +static int probe_vt1708B(struct hda_codec *codec) { - struct via_spec *spec; - int err; + struct via_spec *spec = codec->spec; if (get_codec_type(codec) == VT1708BCE) - return patch_vt1708S(codec); - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + return probe_vt1708S(codec); spec->gen.mixer_nid = 0x16; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1708S */ +/* Support for VT1708S */ static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, @@ -706,16 +652,11 @@ static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, (0 << AC_AMPCAP_MUTE_SHIFT)); } -static int patch_vt1708S(struct hda_codec *codec) +static int probe_vt1708S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -729,21 +670,12 @@ static int patch_vt1708S(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1708S_init_verbs); if (err < 0) - goto error; + return err; - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1702 */ +/* Support for VT1702 */ static const struct hda_verb vt1702_init_verbs[] = { /* mixer enable */ @@ -753,16 +685,11 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -static int patch_vt1702(struct hda_codec *codec) +static int probe_vt1702(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x1a; /* limit AA path volume to 0 dB */ @@ -774,21 +701,13 @@ static int patch_vt1702(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1702_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1718S */ +/* Support for VT1718S */ static const struct hda_verb vt1718S_init_verbs[] = { /* Enable MW0 adjust Gain 5 */ @@ -836,16 +755,11 @@ static int add_secret_dac_path(struct hda_codec *codec) } -static int patch_vt1718S(struct hda_codec *codec) +static int probe_vt1718S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -853,21 +767,13 @@ static int patch_vt1718S(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1718S_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1716S */ +/* Support for VT1716S */ static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -933,41 +839,30 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; -static int patch_vt1716S(struct hda_codec *codec) +static int probe_vt1716S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); err = snd_hda_add_verbs(codec, vt1716S_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); if (err < 0) - goto error; + return err; if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || - !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { - err = -ENOMEM; - goto error; - } + !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) + return -ENOMEM; return 0; - - error: - via_free(codec); - return err; } /* for vt2002P */ @@ -1055,17 +950,12 @@ static void fix_vt1802_connections(struct hda_codec *codec) snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); } -/* patch for vt2002P */ -static int patch_vt2002P(struct hda_codec *codec) +/* Support for vt2002P */ +static int probe_vt2002P(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -1081,18 +971,10 @@ static int patch_vt2002P(struct hda_codec *codec) else err = snd_hda_add_verbs(codec, vt2002P_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } /* for vt1812 */ @@ -1105,17 +987,11 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; -/* patch for vt1812 */ -static int patch_vt1812(struct hda_codec *codec) +static int probe_vt1812(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -1123,21 +999,13 @@ static int patch_vt1812(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1812_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* patch for vt3476 */ +/* Support for vt3476 */ static const struct hda_verb vt3476_init_verbs[] = { /* Enable DMic 8/16/32K */ @@ -1149,96 +1017,155 @@ static const struct hda_verb vt3476_init_verbs[] = { { } }; -static int patch_vt3476(struct hda_codec *codec) +static int probe_vt3476(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x3f; add_secret_dac_path(codec); err = snd_hda_add_verbs(codec, vt3476_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; + return via_parse_auto_config(codec); - return 0; - - error: - via_free(codec); - return err; } /* - * patch entries + * common driver probe + */ +static int via_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = via_new_spec(codec); + if (!spec) + return -ENOMEM; + + switch (id->driver_data) { + case VT1708: + err = probe_vt1708(codec); + break; + case VT1709: + err = probe_vt1709(codec); + break; + case VT1708B: + err = probe_vt1708B(codec); + break; + case VT1708S: + err = probe_vt1708S(codec); + break; + case VT1702: + err = probe_vt1702(codec); + break; + case VT1718S: + err = probe_vt1718S(codec); + break; + case VT1716S: + err = probe_vt1716S(codec); + break; + case VT2002P: + err = probe_vt2002P(codec); + break; + case VT1812: + err = probe_vt1812(codec); + break; + case VT3476: + err = probe_vt3476(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + via_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops via_codec_ops = { + .probe = via_probe, + .remove = via_remove, + .build_controls = via_build_controls, + .build_pcms = via_build_pcms, + .init = via_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = via_suspend, + .resume = via_resume, + .check_power_status = via_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries */ static const struct hda_device_id snd_hda_id_via[] = { - HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), - HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), - HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), - HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), - HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), + HDA_CODEC_ID_MODEL(0x11061708, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x11061709, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106170a, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106170b, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106e710, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e711, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e712, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e713, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e714, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e715, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e716, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e717, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e720, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e721, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e722, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e723, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e724, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e725, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e726, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e727, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x11060397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11061397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11062397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11063397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11064397, "VT1705", VT1708S), + HDA_CODEC_ID_MODEL(0x11065397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11066397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11067397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11060398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11061398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11062398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11063398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11064398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11065398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11066398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11067398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11060428, "VT1718S", VT1718S), + HDA_CODEC_ID_MODEL(0x11064428, "VT1718S", VT1718S), + HDA_CODEC_ID_MODEL(0x11060441, "VT2020", VT1718S), + HDA_CODEC_ID_MODEL(0x11064441, "VT1828S", VT1718S), + HDA_CODEC_ID_MODEL(0x11060433, "VT1716S", VT1716S), + HDA_CODEC_ID_MODEL(0x1106a721, "VT1716S", VT1716S), + HDA_CODEC_ID_MODEL(0x11060438, "VT2002P", VT2002P), + HDA_CODEC_ID_MODEL(0x11064438, "VT2002P", VT2002P), + HDA_CODEC_ID_MODEL(0x11060448, "VT1812", VT1812), + HDA_CODEC_ID_MODEL(0x11060440, "VT1818S", VT1708S), + HDA_CODEC_ID_MODEL(0x11060446, "VT1802", VT2002P), + HDA_CODEC_ID_MODEL(0x11068446, "VT1802", VT2002P), + HDA_CODEC_ID_MODEL(0x11064760, "VT1705CF", VT3476), + HDA_CODEC_ID_MODEL(0x11064761, "VT1708SCE", VT3476), + HDA_CODEC_ID_MODEL(0x11064762, "VT1808", VT3476), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); static struct hda_codec_driver via_driver = { .id = snd_hda_id_via, + .ops = &via_codec_ops, }; MODULE_LICENSE("GPL"); diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig new file mode 100644 index 000000000000..f38e1947fb3e --- /dev/null +++ b/sound/hda/common/Kconfig @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_HDA + tristate + select SND_PCM + select SND_VMASTER + select SND_JACK + select SND_HDA_CORE + +if SND_HDA + +config SND_HDA_HWDEP + bool "Build hwdep interface for HD-audio driver" + select SND_HWDEP + help + Say Y here to build a hwdep interface for HD-audio driver. + This interface can be used for out-of-band communication + with codecs for debugging purposes. + +config SND_HDA_RECONFIG + bool "Allow dynamic codec reconfiguration" + help + Say Y here to enable the HD-audio codec re-configuration feature. + It allows user to clear the whole codec configuration, change the + codec setup, add extra verbs, and re-configure the codec dynamically. + + Note that this item alone doesn't provide the sysfs interface, but + enables the feature just for the patch loader below. + If you need the traditional sysfs entries for the manual interaction, + turn on CONFIG_SND_HDA_HWDEP as well. + +config SND_HDA_INPUT_BEEP + bool "Support digital beep via input layer" + depends on INPUT=y || INPUT=SND_HDA + help + Say Y here to build a digital beep interface for HD-audio + driver. This interface is used to generate digital beeps. + +config SND_HDA_INPUT_BEEP_MODE + int "Digital beep registration mode (0=off, 1=on)" + depends on SND_HDA_INPUT_BEEP=y + default "1" + range 0 1 + help + Set 0 to disable the digital beep interface for HD-audio by default. + Set 1 to always enable the digital beep interface for HD-audio by + default. + +config SND_HDA_PATCH_LOADER + bool "Support initialization patch loading for HD-audio" + select FW_LOADER + select SND_HDA_RECONFIG + help + Say Y here to allow the HD-audio driver to load a pseudo + firmware file ("patch") for overriding the BIOS setup at + start up. The "patch" file can be specified via patch module + option, such as patch=hda-init. + +config SND_HDA_POWER_SAVE_DEFAULT + int "Default time-out for HD-audio power-save mode" + depends on PM + default 0 + help + The default time-out value in seconds for HD-audio automatic + power-save mode. 0 means to disable the power-save mode. + +config SND_HDA_CTL_DEV_ID + bool "Use the device identifier field for controls" + depends on SND_HDA_INTEL + help + Say Y to use the device identifier field for (mixer) + controls (old behaviour until this option is available). + + When enabled, the multiple HDA codecs may set the device + field in control (mixer) element identifiers. The use + of this field is not recommended and defined for mixer controls. + + The old behaviour (Y) is obsolete and will be removed. Consider + to not enable this option. + +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 0 if SND_DMA_SGBUF + default 64 if !SND_DMA_SGBUF + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. + On x86 systems, the default is zero as S/G allocation works + and no preallocation is needed in most cases. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. + +endif diff --git a/sound/hda/common/Makefile b/sound/hda/common/Makefile new file mode 100644 index 000000000000..3344fa0efe75 --- /dev/null +++ b/sound/hda/common/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-codec-y := bind.o codec.o jack.o auto_parser.o sysfs.o +snd-hda-codec-y += controller.o +snd-hda-codec-$(CONFIG_SND_PROC_FS) += proc.o + +snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hwdep.o +snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += beep.o + +# for trace-points +CFLAGS_controller.o := -I$(src) + +# common driver +obj-$(CONFIG_SND_HDA) := snd-hda-codec.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/hda/common/auto_parser.c similarity index 100% rename from sound/pci/hda/hda_auto_parser.c rename to sound/hda/common/auto_parser.c diff --git a/sound/pci/hda/hda_beep.c b/sound/hda/common/beep.c similarity index 100% rename from sound/pci/hda/hda_beep.c rename to sound/hda/common/beep.c diff --git a/sound/pci/hda/hda_bind.c b/sound/hda/common/bind.c similarity index 93% rename from sound/pci/hda/hda_bind.c rename to sound/hda/common/bind.c index df8f88beddd0..bb1090b65699 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/hda/common/bind.c @@ -42,6 +42,7 @@ static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *dr static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) { struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); /* ignore unsol events during shutdown */ if (codec->card->shutdown || codec->bus->shutdown) @@ -51,8 +52,8 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) if (codec->core.dev.power.power_state.event != PM_EVENT_ON) return; - if (codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, ev); + if (driver->ops->unsol_event) + driver->ops->unsol_event(codec, ev); } /** @@ -87,7 +88,7 @@ static int hda_codec_driver_probe(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); struct module *owner = dev->driver->owner; - hda_codec_patch_t patch; + struct hda_codec_driver *driver = hda_codec_to_driver(codec); int err; if (codec->bus->core.ext_ops) { @@ -111,13 +112,14 @@ static int hda_codec_driver_probe(struct device *dev) goto error; } - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (patch) { - err = patch(codec); - if (err < 0) - goto error_module_put; + if (WARN_ON(!(driver->ops && driver->ops->probe))) { + err = -EINVAL; + goto error_module_put; } + err = driver->ops->probe(codec, codec->preset); + if (err < 0) + goto error_module_put; err = snd_hda_codec_build_pcms(codec); if (err < 0) goto error_module; @@ -136,8 +138,8 @@ static int hda_codec_driver_probe(struct device *dev) return 0; error_module: - if (codec->patch_ops.free) - codec->patch_ops.free(codec); + if (driver->ops->remove) + driver->ops->remove(codec); error_module_put: module_put(owner); @@ -150,6 +152,7 @@ static int hda_codec_driver_probe(struct device *dev) static int hda_codec_driver_remove(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); if (codec->bus->core.ext_ops) { if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) @@ -163,8 +166,8 @@ static int hda_codec_driver_remove(struct device *dev) wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); snd_power_sync_ref(codec->bus->card); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); + if (driver->ops->remove) + driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); codec->preset = NULL; module_put(dev->driver->owner); @@ -305,7 +308,7 @@ static int codec_bind_generic(struct hda_codec *codec) * @codec: the HDA codec * * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. + * codec driver binding. * * Returns 0 if successful or a negative error code. */ diff --git a/sound/pci/hda/hda_codec.c b/sound/hda/common/codec.c similarity index 98% rename from sound/pci/hda/hda_codec.c rename to sound/hda/common/codec.c index c018beeecd3d..eb268d442201 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/hda/common/codec.c @@ -479,15 +479,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, { struct hda_pincfg *pin; - /* the check below may be invalid when pins are added by a fixup - * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled - * for now - */ - /* - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return -EINVAL; - */ - pin = look_up_pincfg(codec, list, nid); if (!pin) { pin = snd_array_new(list); @@ -639,24 +630,16 @@ static void hda_jackpoll_work(struct work_struct *work) struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - /* for non-polling trigger: we need nothing if already powered on */ - if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) - return; - - /* the power-up/down sequence triggers the runtime resume */ - snd_hda_power_up_pm(codec); - /* update jacks manually if polling is required, too */ - if (codec->jackpoll_interval) { - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_poll_all(codec); - } - snd_hda_power_down_pm(codec); - if (!codec->jackpoll_interval) return; - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); + /* the power-up/down sequence triggers the runtime resume */ + snd_hda_power_up(codec); + /* update jacks manually if polling is required, too */ + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_poll_all(codec); + schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); + snd_hda_power_down(codec); } /* release all pincfg lists */ @@ -783,7 +766,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) snd_hda_ctls_clear(codec); codec_release_pcms(codec); snd_hda_detach_beep_device(codec); - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; @@ -1131,6 +1113,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hda_codec *c; struct hda_cvt_setup *p; int type; @@ -1146,8 +1129,8 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (!p) return; - if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, true); + if (driver->ops->stream_pm) + driver->ops->stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); @@ -1207,7 +1190,9 @@ EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream); static void really_cleanup_stream(struct hda_codec *codec, struct hda_cvt_setup *q) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); hda_nid_t nid = q->nid; + if (q->stream_tag || q->channel_id) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); if (q->format_id) @@ -1215,8 +1200,8 @@ static void really_cleanup_stream(struct hda_codec *codec, ); memset(q, 0, sizeof(*q)); q->nid = nid; - if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, false); + if (driver->ops->stream_pm) + driver->ops->stream_pm(codec, nid, false); } /* clean up the all conflicting obsolete streams */ @@ -1653,7 +1638,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) id.index = idx; if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; - strcpy(id.name, name); + strscpy(id.name, name); return snd_ctl_find_id(codec->card, &id); } @@ -2407,7 +2392,7 @@ static const struct snd_kcontrol_new dig_mixes[] = { * @cvt_nid: converter NID * @type: HDA_PCM_TYPE_* * Creates controls related with the digital output. - * Called from each patch supporting the digital out. + * Called from each codec driver supporting the digital out. * * Returns 0 if successful, or a negative error code. */ @@ -2666,7 +2651,7 @@ static const struct snd_kcontrol_new dig_in_ctls[] = { * @nid: audio in widget NID * * Creates controls related with the SPDIF input. - * Called from each patch supporting the SPDIF in. + * Called from each codec driver supporting the SPDIF in. * * Returns 0 if successful, or a negative error code. */ @@ -2763,6 +2748,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; int count; unsigned int state; @@ -2779,9 +2765,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* repeat power states setting at most 10 times*/ for (count = 0; count < 10; count++) { - if (codec->patch_ops.set_power_state) - codec->patch_ops.set_power_state(codec, fg, - power_state); + /* might be called before binding to driver, too */ + if (driver && driver->ops && driver->ops->set_power_state) + driver->ops->set_power_state(codec, fg, power_state); else { state = power_state; if (codec->power_filter) @@ -2859,11 +2845,12 @@ void snd_hda_update_power_acct(struct hda_codec *codec) */ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); unsigned int state; snd_hdac_enter_pm(&codec->core); - if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec); + if (driver->ops->suspend) + driver->ops->suspend(codec); if (!codec->no_stream_clean_at_suspend) hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); @@ -2877,6 +2864,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + snd_hdac_enter_pm(&codec->core); if (codec->core.regmap) regcache_mark_dirty(codec->core.regmap); @@ -2887,20 +2876,19 @@ static void hda_call_codec_resume(struct hda_codec *codec) restore_shutup_pins(codec); hda_exec_init_verbs(codec); snd_hda_jack_set_dirty_all(codec); - if (codec->patch_ops.resume) - codec->patch_ops.resume(codec); + if (driver->ops->resume) + driver->ops->resume(codec); else { - if (codec->patch_ops.init) - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); } - if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); + snd_hda_jack_report_sync(codec); codec->core.dev.power.power_state = PMSG_ON; snd_hdac_leave_pm(&codec->core); + if (codec->jackpoll_interval) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } static int hda_codec_runtime_suspend(struct device *dev) @@ -2912,8 +2900,6 @@ static int hda_codec_runtime_suspend(struct device *dev) if (!codec->card) return 0; - cancel_delayed_work_sync(&codec->jackpoll_work); - state = hda_call_codec_suspend(codec); if (codec->link_down_at_suspend || (codec_has_clkstop(codec) && codec_has_epss(codec) && @@ -2921,10 +2907,6 @@ static int hda_codec_runtime_suspend(struct device *dev) snd_hdac_codec_link_down(&codec->core); snd_hda_codec_display_power(codec, false); - if (codec->bus->jackpoll_in_suspend && - (dev->power.power_state.event != PM_EVENT_SUSPEND)) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); return 0; } @@ -2943,6 +2925,15 @@ static int hda_codec_runtime_resume(struct device *dev) return 0; } +static int hda_codec_runtime_idle(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend) + return -EBUSY; + return 0; +} + static int hda_codec_pm_prepare(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); @@ -3008,7 +2999,8 @@ const struct dev_pm_ops hda_codec_driver_pm = { .thaw = pm_sleep_ptr(hda_codec_pm_thaw), .poweroff = pm_sleep_ptr(hda_codec_pm_suspend), .restore = pm_sleep_ptr(hda_codec_pm_restore), - RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) + RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, + hda_codec_runtime_idle) }; /* suspend the codec at shutdown; called from driver's shutdown callback */ @@ -3020,6 +3012,7 @@ void snd_hda_codec_shutdown(struct hda_codec *codec) if (!codec->core.registered) return; + codec->jackpoll_interval = 0; /* don't poll any longer */ cancel_delayed_work_sync(&codec->jackpoll_work); list_for_each_entry(cpcm, &codec->pcm_list_head, list) snd_pcm_suspend_all(cpcm->pcm); @@ -3071,25 +3064,31 @@ EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); int snd_hda_codec_build_controls(struct hda_codec *codec) { - int err = 0; + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + int err; + hda_exec_init_verbs(codec); /* continue to initialize... */ - if (codec->patch_ops.init) - err = codec->patch_ops.init(codec); - if (!err && codec->patch_ops.build_controls) - err = codec->patch_ops.build_controls(codec); + err = snd_hda_codec_init(codec); if (err < 0) return err; + if (driver->ops->build_controls) { + err = driver->ops->build_controls(codec); + if (err < 0) + return err; + } + /* we create chmaps here instead of build_pcms */ err = add_std_chmaps(codec); if (err < 0) return err; + snd_hda_jack_report_sync(codec); /* call at the last init point */ if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); /* call at the last init point */ + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + sync_power_up_states(codec); return 0; } @@ -3264,16 +3263,17 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) /* call build_pcms ops of the given codec and set up the default parameters */ int snd_hda_codec_parse_pcms(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hda_pcm *cpcm; int err; if (!list_empty(&codec->pcm_list_head)) return 0; /* already parsed */ - if (!codec->patch_ops.build_pcms) + if (!driver->ops->build_pcms) return 0; - err = codec->patch_ops.build_pcms(codec); + err = driver->ops->build_pcms(codec); if (err < 0) { codec_err(codec, "cannot build PCMs for #%d (error %d)\n", codec->core.addr, err); @@ -3508,7 +3508,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, index = uinfo->value.enumerated.item; if (index >= imux->num_items) index = imux->num_items - 1; - strcpy(uinfo->value.enumerated.name, imux->items[index].label); + strscpy(uinfo->value.enumerated.name, imux->items[index].label); return 0; } EXPORT_SYMBOL_GPL(snd_hda_input_mux_info); diff --git a/sound/pci/hda/hda_controller.c b/sound/hda/common/controller.c similarity index 99% rename from sound/pci/hda/hda_controller.c rename to sound/hda/common/controller.c index f3330b7e0fcf..84387ed761be 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/hda/common/controller.c @@ -29,7 +29,7 @@ #include "hda_local.h" #define CREATE_TRACE_POINTS -#include "hda_controller_trace.h" +#include "controller_trace.h" /* DSP lock helpers */ #define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/hda/common/controller_trace.h similarity index 97% rename from sound/pci/hda/hda_controller_trace.h rename to sound/hda/common/controller_trace.h index bf48304e230a..7f5841f8919e 100644 --- a/sound/pci/hda/hda_controller_trace.h +++ b/sound/hda/common/controller_trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM #define TRACE_SYSTEM hda_controller -#define TRACE_INCLUDE_FILE hda_controller_trace +#define TRACE_INCLUDE_FILE controller_trace #if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_HDA_CONTROLLER_H diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h similarity index 100% rename from sound/pci/hda/hda_auto_parser.h rename to sound/hda/common/hda_auto_parser.h diff --git a/sound/pci/hda/hda_beep.h b/sound/hda/common/hda_beep.h similarity index 100% rename from sound/pci/hda/hda_beep.h rename to sound/hda/common/hda_beep.h diff --git a/sound/pci/hda/hda_controller.h b/sound/hda/common/hda_controller.h similarity index 100% rename from sound/pci/hda/hda_controller.h rename to sound/hda/common/hda_controller.h diff --git a/sound/pci/hda/hda_jack.h b/sound/hda/common/hda_jack.h similarity index 100% rename from sound/pci/hda/hda_jack.h rename to sound/hda/common/hda_jack.h diff --git a/sound/pci/hda/hda_local.h b/sound/hda/common/hda_local.h similarity index 99% rename from sound/pci/hda/hda_local.h rename to sound/hda/common/hda_local.h index 68c31f5354b7..a7e53277a0fe 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -652,6 +652,15 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, void snd_hda_codec_shutdown(struct hda_codec *codec); +static inline int snd_hda_codec_init(struct hda_codec *codec) +{ + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + + if (driver->ops->init) + return driver->ops->init(codec); + return 0; +} + /* * AMP control callbacks */ @@ -689,10 +698,6 @@ int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, - bool rev3_or_later); - #ifdef CONFIG_SND_PROC_FS void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer, diff --git a/sound/pci/hda/hda_hwdep.c b/sound/hda/common/hwdep.c similarity index 100% rename from sound/pci/hda/hda_hwdep.c rename to sound/hda/common/hwdep.c diff --git a/sound/pci/hda/hda_jack.c b/sound/hda/common/jack.c similarity index 100% rename from sound/pci/hda/hda_jack.c rename to sound/hda/common/jack.c diff --git a/sound/pci/hda/hda_proc.c b/sound/hda/common/proc.c similarity index 100% rename from sound/pci/hda/hda_proc.c rename to sound/hda/common/proc.c diff --git a/sound/pci/hda/hda_sysfs.c b/sound/hda/common/sysfs.c similarity index 100% rename from sound/pci/hda/hda_sysfs.c rename to sound/hda/common/sysfs.c diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig new file mode 100644 index 000000000000..34721f50b055 --- /dev/null +++ b/sound/hda/controllers/Kconfig @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_HDA_INTEL + tristate "HD Audio PCI" + depends on SND_PCI + select SND_HDA + select SND_INTEL_DSP_CONFIG + help + Say Y here to include support for Intel "High Definition + Audio" (Azalia) and its compatible devices. + + This option enables the HD-audio controller. Don't forget + to choose the appropriate HD-audio codec options. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-intel. + +config SND_HDA_TEGRA + tristate "NVIDIA Tegra HD Audio" + depends on ARCH_TEGRA + select SND_HDA + select SND_HDA_ALIGNED_MMIO + help + Say Y here to support the HDA controller present in NVIDIA + Tegra SoCs + + This options enables support for the HD Audio controller + present in some NVIDIA Tegra SoCs, used to communicate audio + to the HDMI output. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-tegra. + +config SND_HDA_ACPI + tristate "HD Audio ACPI" + depends on ACPI + select SND_HDA + help + Say Y here to include support for Azalia-compatible HDA controllers + which are advertised via ACPI objects. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-acpi. diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile new file mode 100644 index 000000000000..a4bcd055e9ae --- /dev/null +++ b/sound/hda/controllers/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-intel-y := intel.o +snd-hda-tegra-y := tegra.o +snd-hda-acpi-y := acpi.o + +subdir-ccflags-y += -I$(src)/../common + +# for trace-points +CFLAGS_intel.o := -I$(src) + +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o +obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o +obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o diff --git a/sound/pci/hda/hda_acpi.c b/sound/hda/controllers/acpi.c similarity index 100% rename from sound/pci/hda/hda_acpi.c rename to sound/hda/controllers/acpi.c diff --git a/sound/pci/hda/hda_intel.c b/sound/hda/controllers/intel.c similarity index 99% rename from sound/pci/hda/hda_intel.c rename to sound/hda/controllers/intel.c index e5210ed48ddf..1bb3ff55b115 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/hda/controllers/intel.c @@ -54,11 +54,10 @@ #include #include #include -#include "hda_controller.h" -#include "hda_intel.h" +#include "intel.h" #define CREATE_TRACE_POINTS -#include "hda_intel_trace.h" +#include "intel_trace.h" /* position fix mode */ enum { @@ -1465,7 +1464,7 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci) * the dGPU is the one who is involved in * vgaswitcheroo. */ - if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) && + if (pci_is_display(p) && (atpx_present() || apple_gmux_detect(NULL, NULL))) return p; pci_dev_put(p); @@ -1477,7 +1476,7 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci) p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), pci->bus->number, 0); if (p) { - if ((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) + if (pci_is_display(p)) return p; pci_dev_put(p); } @@ -2025,7 +2024,7 @@ static int azx_first_init(struct azx *chip) if (azx_acquire_irq(chip, 0) < 0) return -EBUSY; - strcpy(card->driver, "HDA-Intel"); + strscpy(card->driver, "HDA-Intel"); strscpy(card->shortname, driver_short_names[chip->driver_type], sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), @@ -2078,7 +2077,6 @@ static const struct pci_device_id driver_denylist[] = { { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */ - { PCI_DEVICE_SUB(0x1022, 0x15e3, 0x1022, 0xd601) }, /* ASRock X670E Taichi */ {} }; @@ -2283,6 +2281,8 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0), /* Dell ALC3271 */ SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), + /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */ + SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), {} }; diff --git a/sound/pci/hda/hda_intel.h b/sound/hda/controllers/intel.h similarity index 100% rename from sound/pci/hda/hda_intel.h rename to sound/hda/controllers/intel.h diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/hda/controllers/intel_trace.h similarity index 95% rename from sound/pci/hda/hda_intel_trace.h rename to sound/hda/controllers/intel_trace.h index 2775fa81a500..fb10ab9e7e55 100644 --- a/sound/pci/hda/hda_intel_trace.h +++ b/sound/hda/controllers/intel_trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM #define TRACE_SYSTEM hda_intel -#define TRACE_INCLUDE_FILE hda_intel_trace +#define TRACE_INCLUDE_FILE intel_trace #if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_HDA_INTEL_H diff --git a/sound/pci/hda/hda_tegra.c b/sound/hda/controllers/tegra.c similarity index 100% rename from sound/pci/hda/hda_tegra.c rename to sound/hda/controllers/tegra.c diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig new file mode 100644 index 000000000000..bfdcf6384c52 --- /dev/null +++ b/sound/hda/core/Kconfig @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_HDA_CORE + tristate + select REGMAP + +config SND_HDA_DSP_LOADER + bool + +config SND_HDA_ALIGNED_MMIO + bool + +config SND_HDA_COMPONENT + bool + +config SND_HDA_I915 + bool + select SND_HDA_COMPONENT + +config SND_HDA_EXT_CORE + tristate + select SND_HDA_CORE + +config SND_INTEL_NHLT + bool + # this config should be selected only for Intel ACPI platforms. + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_DSP_CONFIG + tristate + select ACPI_NHLT if ACPI + select SND_INTEL_NHLT if ACPI + select SND_INTEL_SOUNDWIRE_ACPI if ACPI + # this config should be selected only for Intel DSP platforms. + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_SOUNDWIRE_ACPI + tristate + +config SND_INTEL_BYT_PREFER_SOF + bool "Prefer SOF driver over SST on BY/CHT platforms" + depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL + default n + help + The kernel has 2 drivers for the Low Power Engine audio-block on + Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF + driver. If both drivers are enabled then the kernel will default + to using the old SST driver, unless told otherwise through the + snd_intel_dspcfg.dsp_driver module-parameter. + + Set this option to Y to make the kernel default to the new SOF + driver instead. diff --git a/sound/hda/core/Makefile b/sound/hda/core/Makefile new file mode 100644 index 000000000000..89cb46143050 --- /dev/null +++ b/sound/hda/core/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-core-y := hda_bus_type.o bus.o device.o sysfs.o \ + regmap.o controller.o stream.o array.o hdmi_chmap.o + +snd-hda-core-y += trace.o +CFLAGS_trace.o := -I$(src) + +# for sync with i915 gfx driver +snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += component.o +snd-hda-core-$(CONFIG_SND_HDA_I915) += i915.o + +obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o + +#extended hda +obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ + +snd-intel-dspcfg-y := intel-dsp-config.o +snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o +obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o + +snd-intel-sdw-acpi-y := intel-sdw-acpi.o +obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o diff --git a/sound/hda/array.c b/sound/hda/core/array.c similarity index 100% rename from sound/hda/array.c rename to sound/hda/core/array.c diff --git a/sound/hda/hdac_bus.c b/sound/hda/core/bus.c similarity index 100% rename from sound/hda/hdac_bus.c rename to sound/hda/core/bus.c diff --git a/sound/hda/hdac_component.c b/sound/hda/core/component.c similarity index 100% rename from sound/hda/hdac_component.c rename to sound/hda/core/component.c diff --git a/sound/hda/hdac_controller.c b/sound/hda/core/controller.c similarity index 100% rename from sound/hda/hdac_controller.c rename to sound/hda/core/controller.c diff --git a/sound/hda/hdac_device.c b/sound/hda/core/device.c similarity index 99% rename from sound/hda/hdac_device.c rename to sound/hda/core/device.c index 0053831eed2d..018f9e176b1b 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/core/device.c @@ -436,11 +436,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); /* return CONNLIST_LEN parameter of the given widget */ static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) { - unsigned int wcaps = get_wcaps(codec, nid); + unsigned int wcaps = snd_hdac_get_wcaps(codec, nid); unsigned int parm; if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB) return 0; parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN); @@ -581,7 +581,6 @@ int snd_hdac_power_down(struct hdac_device *codec) { struct device *dev = &codec->dev; - pm_runtime_mark_last_busy(dev); return pm_runtime_put_autosuspend(dev); } EXPORT_SYMBOL_GPL(snd_hdac_power_down); @@ -855,7 +854,7 @@ static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) unsigned int val = 0; if (nid != codec->afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + (snd_hdac_get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); @@ -895,7 +894,7 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, { unsigned int i, val, wcaps; - wcaps = get_wcaps(codec, nid); + wcaps = snd_hdac_get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { diff --git a/sound/hda/ext/Makefile b/sound/hda/core/ext/Makefile similarity index 54% rename from sound/hda/ext/Makefile rename to sound/hda/core/ext/Makefile index 05883fb28d28..85190a7eb5de 100644 --- a/sound/hda/ext/Makefile +++ b/sound/hda/core/ext/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-hda-ext-core-y := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o +snd-hda-ext-core-y := bus.o controller.o stream.o obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/core/ext/bus.c similarity index 100% rename from sound/hda/ext/hdac_ext_bus.c rename to sound/hda/core/ext/bus.c diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/core/ext/controller.c similarity index 100% rename from sound/hda/ext/hdac_ext_controller.c rename to sound/hda/core/ext/controller.c diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/core/ext/stream.c similarity index 100% rename from sound/hda/ext/hdac_ext_stream.c rename to sound/hda/core/ext/stream.c diff --git a/sound/hda/hda_bus_type.c b/sound/hda/core/hda_bus_type.c similarity index 100% rename from sound/hda/hda_bus_type.c rename to sound/hda/core/hda_bus_type.c diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c similarity index 100% rename from sound/hda/hdmi_chmap.c rename to sound/hda/core/hdmi_chmap.c diff --git a/sound/hda/hdac_i915.c b/sound/hda/core/i915.c similarity index 98% rename from sound/hda/hdac_i915.c rename to sound/hda/core/i915.c index e9425213320e..44438c799f95 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/core/i915.c @@ -155,7 +155,7 @@ static int i915_gfx_present(struct pci_dev *hdac_pci) for_each_pci_dev(display_dev) { if (display_dev->vendor != PCI_VENDOR_ID_INTEL || - (display_dev->class >> 16) != PCI_BASE_CLASS_DISPLAY) + !pci_is_display(display_dev)) continue; if (pci_match_id(denylist, display_dev)) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c similarity index 96% rename from sound/hda/intel-dsp-config.c rename to sound/hda/core/intel-dsp-config.c index 3cb1e7fc3b3b..c15284742899 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/core/intel-dsp-config.c @@ -116,6 +116,13 @@ static const struct config_entry config_table[] = { .flags = FLAG_SST, .device = PCI_DEVICE_ID_INTEL_HDA_FCL, }, +#else /* AVS disabled; force to legacy as SOF doesn't work for SKL or KBL */ + { + .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP, + }, + { + .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP, + }, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) { @@ -167,9 +174,9 @@ static const struct config_entry config_table[] = { /* * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake, - * RaptorLake use legacy HDAudio driver except for Google Chromebooks - * and when DMICs are present. Two cases are required since Coreboot - * does not expose NHLT tables. + * RaptorLake, MeteorLake use legacy HDAudio driver except for Google + * Chromebooks and when DMICs are present. Two cases are required since + * Coreboot does not expose NHLT tables. * * When the Chromebook quirk is not present, it's based on information * that no such device exists. When the quirk is present, it could be @@ -516,6 +523,19 @@ static const struct config_entry config_table[] = { /* Meteor Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE) /* Meteorlake-P */ + { + .flags = FLAG_SOF, + .device = PCI_DEVICE_ID_INTEL_HDA_MTL, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = PCI_DEVICE_ID_INTEL_HDA_MTL, diff --git a/sound/hda/intel-nhlt.c b/sound/hda/core/intel-nhlt.c similarity index 100% rename from sound/hda/intel-nhlt.c rename to sound/hda/core/intel-nhlt.c diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/core/intel-sdw-acpi.c similarity index 100% rename from sound/hda/intel-sdw-acpi.c rename to sound/hda/core/intel-sdw-acpi.c diff --git a/sound/hda/local.h b/sound/hda/core/local.h similarity index 66% rename from sound/hda/local.h rename to sound/hda/core/local.h index 896ba142e8bc..5f03b203c416 100644 --- a/sound/hda/local.h +++ b/sound/hda/core/local.h @@ -6,27 +6,6 @@ #ifndef __HDAC_LOCAL_H #define __HDAC_LOCAL_H -#define get_wcaps(codec, nid) \ - snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) - -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -static inline unsigned int get_wcaps_channels(u32 wcaps) -{ - unsigned int chans; - - chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = (chans + 1) * 2; - - return chans; -} - extern const struct attribute_group *hdac_dev_attr_groups[]; int hda_widget_sysfs_init(struct hdac_device *codec); int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid, diff --git a/sound/hda/hdac_regmap.c b/sound/hda/core/regmap.c similarity index 100% rename from sound/hda/hdac_regmap.c rename to sound/hda/core/regmap.c diff --git a/sound/hda/hdac_stream.c b/sound/hda/core/stream.c similarity index 99% rename from sound/hda/hdac_stream.c rename to sound/hda/core/stream.c index e7f6208af5b0..4a87bef8834f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/core/stream.c @@ -634,7 +634,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, } EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params); -static u64 azx_cc_read(const struct cyclecounter *cc) +static u64 azx_cc_read(struct cyclecounter *cc) { struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc); diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/core/sysfs.c similarity index 95% rename from sound/hda/hdac_sysfs.c rename to sound/hda/core/sysfs.c index 60b0a70428d5..bffe52859dba 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/core/sysfs.c @@ -161,13 +161,13 @@ static const struct kobj_type widget_ktype = { static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - return sysfs_emit(buf, "0x%08x\n", get_wcaps(codec, nid)); + return sysfs_emit(buf, "0x%08x\n", snd_hdac_get_wcaps(codec, nid)); } static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); @@ -178,7 +178,7 @@ static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, { unsigned int val; - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN) return 0; if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) return 0; @@ -189,7 +189,7 @@ static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) { if (nid == codec->afg || nid == codec->mfg) return true; - switch (get_wcaps_type(get_wcaps(codec, nid))) { + switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) { case AC_WID_AUD_OUT: case AC_WID_AUD_IN: return true; @@ -219,7 +219,7 @@ static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); @@ -228,7 +228,7 @@ static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); @@ -237,7 +237,7 @@ static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); diff --git a/sound/hda/trace.c b/sound/hda/core/trace.c similarity index 100% rename from sound/hda/trace.c rename to sound/hda/core/trace.c diff --git a/sound/hda/trace.h b/sound/hda/core/trace.h similarity index 100% rename from sound/hda/trace.h rename to sound/hda/core/trace.h diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 99006dc4777e..8e84d4091f1e 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -98,7 +98,7 @@ static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card, pdev = pnp_request_card_device(card, id->devs[1].id, NULL); if (pdev == NULL) { mpu_port[dev] = -1; - dev_warn(&pdev->dev, "MPU401 device busy, skipping.\n"); + pr_warn("MPU401 device busy, skipping.\n"); return 0; } @@ -143,8 +143,8 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000) chip->clock_freq = clockfreq[dev]; - strcpy(card->driver, "AD1816A"); - strcpy(card->shortname, "ADI SoundPort AD1816A"); + strscpy(card->driver, "AD1816A"); + strscpy(card->shortname, "ADI SoundPort AD1816A"); sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 2b87036cb94f..400ae547bcba 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -631,7 +631,7 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device) pcm->private_data = chip; pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; - strcpy(pcm->name, snd_ad1816a_chip_id(chip)); + strscpy(pcm->name, snd_ad1816a_chip_id(chip)); snd_ad1816a_init(chip); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev, @@ -655,7 +655,7 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device) error = snd_timer_new(chip->card, "AD1816A", &tid, &timer); if (error < 0) return error; - strcpy(timer->name, snd_ad1816a_chip_id(chip)); + strscpy(timer->name, snd_ad1816a_chip_id(chip)); timer->private_data = chip; chip->timer = timer; timer->hw = snd_ad1816a_timer_table; @@ -912,7 +912,7 @@ int snd_ad1816a_mixer(struct snd_ad1816a *chip) card = chip->card; - strcpy(card->mixername, snd_ad1816a_chip_id(chip)); + strscpy(card->mixername, snd_ad1816a_chip_id(chip)); for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip)); diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c index f079ba4ef1a0..03fb2bce9255 100644 --- a/sound/isa/adlib.c +++ b/sound/isa/adlib.c @@ -61,8 +61,8 @@ static int snd_adlib_probe(struct device *dev, unsigned int n) return -EBUSY; } - strcpy(card->driver, DEV_NAME); - strcpy(card->shortname, CRD_NAME); + strscpy(card->driver, DEV_NAME); + strscpy(card->shortname, CRD_NAME); sprintf(card->longname, CRD_NAME " at %#lx", port[n]); error = snd_opl3_create(card, port[n], port[n] + 2, OPL3_HW_AUTO, 1, &opl3); diff --git a/sound/isa/als100.c b/sound/isa/als100.c index e70dbf0b099a..cfc241bd252e 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c @@ -192,14 +192,14 @@ static int snd_card_als100_probe(int dev, acard->chip = chip; if (pid->driver_data == SB_HW_DT019X) { - strcpy(card->driver, "DT-019X"); - strcpy(card->shortname, "Diamond Tech. DT-019X"); + strscpy(card->driver, "DT-019X"); + strscpy(card->shortname, "Diamond Tech. DT-019X"); snprintf(card->longname, sizeof(card->longname), "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d", chip->name, chip->port, irq[dev], dma8[dev]); } else { - strcpy(card->driver, "ALS100"); - strcpy(card->shortname, "Avance Logic ALS100"); + strscpy(card->driver, "ALS100"); + strscpy(card->shortname, "Avance Logic ALS100"); snprintf(card->longname, sizeof(card->longname), "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d", chip->name, chip->port, irq[dev], dma8[dev], diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index d30cce4cc7e3..4e6d823af103 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -155,34 +155,34 @@ static int snd_cmi8328_mixer(struct snd_wss *chip) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* rename AUX0 switch to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX0 volume to CD */ - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX1 switch to Synth */ - strcpy(id1.name, "Aux Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX1 volume to Synth */ - strcpy(id1.name, "Aux Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); id1.index = 1; - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); @@ -362,8 +362,8 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev) if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0) dev_warn(pdev, "error initializing OPL3 hwdep\n"); - strcpy(card->driver, "CMI8328"); - strcpy(card->shortname, "C-Media CMI8328"); + strscpy(card->driver, "CMI8328"); + strscpy(card->shortname, "C-Media CMI8328"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d", card->shortname, cmi->wss->port, irq[ndev], dma1[ndev], (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]); diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 25b4dc181089..e681c2c82474 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -297,7 +297,7 @@ static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard) unsigned int idx; int err; - strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); + strscpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { err = snd_ctl_add(card, @@ -437,7 +437,7 @@ static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip) err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm); if (err < 0) return err; - strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); + strscpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); pcm->private_data = chip; /* SB16 */ @@ -590,8 +590,8 @@ static int snd_cmi8330_probe(struct snd_card *card, int dev) mpuport[dev]); } - strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); - strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); + strscpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); + strscpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", card->shortname, acard->wss->port, diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index ad20bb2649bd..e36cc147651a 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -510,7 +510,7 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev, return -ENODEV; /* prepare second id */ - strcpy(cid, pdev->id[0].id); + strscpy(cid, pdev->id[0].id); cid[5] = '1'; cdev = NULL; list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) { diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index 1a03cff6915b..eaaf39aae526 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -1023,7 +1023,7 @@ int snd_cs4236_mixer(struct snd_wss *chip) if (snd_BUG_ON(!chip || !chip->card)) return -EINVAL; card = chip->card; - strcpy(card->mixername, snd_wss_chip_id(chip)); + strscpy(card->mixername, snd_wss_chip_id(chip)); if (chip->hardware == WSS_HW_CS4235 || chip->hardware == WSS_HW_CS4239) { diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index c0c230149a75..2ef183f197ad 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -706,7 +706,7 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - strcpy(pcm->name, snd_es1688_chip_id(chip)); + strscpy(pcm->name, snd_es1688_chip_id(chip)); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, @@ -971,7 +971,7 @@ int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip) if (snd_BUG_ON(!chip || !card)) return -EINVAL; - strcpy(card->mixername, snd_es1688_chip_id(chip)); + strscpy(card->mixername, snd_es1688_chip_id(chip)); for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip)); diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index e35c727a52fa..3e89a84c6244 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1771,7 +1771,7 @@ static int snd_es18xx_mixer(struct snd_card *card) int err; unsigned int idx; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { struct snd_kcontrol *kctl; diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c index 3164eb8510fa..b0f1562d0fc0 100644 --- a/sound/isa/galaxy/galaxy.c +++ b/sound/isa/galaxy/galaxy.c @@ -542,8 +542,8 @@ static int __snd_galaxy_probe(struct device *dev, unsigned int n) return err; } - strcpy(card->driver, DRV_NAME); - strcpy(card->shortname, DRV_NAME); + strscpy(card->driver, DRV_NAME); + strscpy(card->shortname, DRV_NAME); sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", card->shortname, port[n], wss_port[n], irq[n], dma1[n], dma2[n]); diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 7166869e423d..873ef4046cd6 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -348,8 +348,8 @@ static int snd_gus_check_version(struct snd_gus_card * gus) rev = inb(GUSP(gus, BOARDVERSION)); spin_unlock_irqrestore(&gus->reg_lock, flags); dev_dbg(card->dev, "GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); - strcpy(card->driver, "GUS"); - strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); + strscpy(card->driver, "GUS"); + strscpy(card->longname, "Gravis UltraSound Classic (2.4)"); if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { if (rev >= 5 && rev <= 9) { gus->ics_flag = 1; @@ -360,16 +360,16 @@ static int snd_gus_check_version(struct snd_gus_card * gus) } if (rev >= 10 && rev != 255) { if (rev >= 10 && rev <= 11) { - strcpy(card->driver, "GUS MAX"); - strcpy(card->longname, "Gravis UltraSound MAX"); + strscpy(card->driver, "GUS MAX"); + strscpy(card->longname, "Gravis UltraSound MAX"); gus->max_flag = 1; } else if (rev == 0x30) { - strcpy(card->driver, "GUS ACE"); - strcpy(card->longname, "Gravis UltraSound Ace"); + strscpy(card->driver, "GUS ACE"); + strscpy(card->longname, "Gravis UltraSound Ace"); gus->ace_flag = 1; } else if (rev == 0x50) { - strcpy(card->driver, "GUS Extreme"); - strcpy(card->longname, "Gravis UltraSound Extreme"); + strscpy(card->driver, "GUS Extreme"); + strscpy(card->longname, "Gravis UltraSound Extreme"); gus->ess_flag = 1; } else { dev_err(card->dev, diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c index 03f9cfcbf601..60c3a8219770 100644 --- a/sound/isa/gus/gus_mixer.c +++ b/sound/isa/gus/gus_mixer.c @@ -152,7 +152,7 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus) if (gus->ics_flag) snd_component_add(card, "ICS2101"); if (card->mixername[0] == '\0') { - strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); + strscpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); } else { if (gus->ics_flag) strcat(card->mixername, ",ICS2101"); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 16f9bbb43a54..8b9b7b8d92b2 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -851,7 +851,7 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) SNDRV_DMA_TYPE_DEV, card->dev, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } - strcpy(pcm->name, pcm->id); + strscpy(pcm->name, pcm->id); if (gus->interwave) { sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); } diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index 047ddbc6192f..7267fb5bf8e5 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -156,7 +156,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus) tid.subdevice = 0; if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { - strcpy(timer->name, "GF1 timer #1"); + strscpy(timer->name, "GF1 timer #1"); timer->private_data = gus; timer->private_free = snd_gf1_timer1_free; timer->hw = snd_gf1_timer1; @@ -166,7 +166,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus) tid.device++; if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { - strcpy(timer->name, "GF1 timer #2"); + strscpy(timer->name, "GF1 timer #2"); timer->private_data = gus; timer->private_free = snd_gf1_timer2_free; timer->hw = snd_gf1_timer2; diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 08276509447f..e207f274f240 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -236,7 +236,7 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); + strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index 6eab95bd49c1..28827a2e6cbd 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -204,15 +204,15 @@ static int snd_gusextreme_mixer(struct snd_card *card) id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); error = snd_ctl_rename_id(card, &id1, &id2); if (error < 0) return error; /* reassign Master Playback Switch to Synth Playback Switch */ - strcpy(id1.name, "Master Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Master Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); error = snd_ctl_rename_id(card, &id1, &id2); if (error < 0) return error; diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index 445fd2fb50f1..b572411c4422 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -134,24 +134,24 @@ static int snd_gusmax_mixer(struct snd_wss *chip) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUXA to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUXB to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 18a98123e286..0e0bcd85a648 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -271,7 +271,7 @@ static int snd_interwave_detect(struct snd_interwave *iwcard, dev_dbg(gus->card->dev, "[0x%lx] InterWave check - passed\n", gus->gf1.port); gus->interwave = 1; - strcpy(gus->card->shortname, "AMD InterWave"); + strscpy(gus->card->shortname, "AMD InterWave"); gus->revision = rev1 >> 4; #ifndef SNDRV_STB return 0; /* ok.. We have an InterWave board */ @@ -500,11 +500,11 @@ static int snd_interwave_mixer(struct snd_wss *chip) id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; #if 0 /* remove mono microphone controls */ - strcpy(id1.name, "Mic Playback Switch"); + strscpy(id1.name, "Mic Playback Switch"); err = snd_ctl_remove_id(card, &id1); if (err < 0) return err; - strcpy(id1.name, "Mic Playback Volume"); + strscpy(id1.name, "Mic Playback Volume"); err = snd_ctl_remove_id(card, &id1); if (err < 0) return err; @@ -520,24 +520,24 @@ static int snd_interwave_mixer(struct snd_wss *chip) snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); /* reassign AUXA to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUXB to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -713,14 +713,14 @@ static int snd_interwave_probe(struct snd_card *card, int dev, memset(&id1, 0, sizeof(id1)); memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id1.name, "Master Playback Switch"); - strcpy(id2.name, id1.name); + strscpy(id1.name, "Master Playback Switch"); + strscpy(id2.name, id1.name); id2.index = 1; err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Master Playback Volume"); - strcpy(id2.name, id1.name); + strscpy(id1.name, "Master Playback Volume"); + strscpy(id2.name, id1.name); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -742,8 +742,8 @@ static int snd_interwave_probe(struct snd_card *card, int dev, #else str = "InterWave STB"; #endif - strcpy(card->driver, str); - strcpy(card->shortname, str); + strscpy(card->driver, str); + strscpy(card->shortname, str); sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", str, gus->gf1.port, diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c index 69c515421dd8..8c1d2e27854b 100644 --- a/sound/isa/msnd/msnd.c +++ b/sound/isa/msnd/msnd.c @@ -673,7 +673,7 @@ int snd_msnd_pcm(struct snd_card *card, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, "Hurricane"); + strscpy(pcm->name, "Hurricane"); return 0; } diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 8caf431677e5..969bbb18657b 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -221,8 +221,8 @@ static int snd_msnd_probe(struct snd_card *card) } #ifdef MSND_CLASSIC - strcpy(card->shortname, "Classic/Tahiti/Monterey"); - strcpy(card->longname, "Turtle Beach Multisound"); + strscpy(card->shortname, "Classic/Tahiti/Monterey"); + strscpy(card->longname, "Turtle Beach Multisound"); dev_info(card->dev, LOGNAME ": %s, " "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n", card->shortname, @@ -251,38 +251,38 @@ static int snd_msnd_probe(struct snd_card *card) switch (info & 0x7) { case 0x0: rev = "I"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x1: rev = "F"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x2: rev = "G"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x3: rev = "H"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x4: rev = "E"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x5: rev = "C"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x6: rev = "D"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x7: rev = "A-B (Fiji) or A-E (Pinnacle)"; - strcpy(card->shortname, pinfiji); + strscpy(card->shortname, pinfiji); break; } - strcpy(card->longname, "Turtle Beach Multisound Pinnacle"); + strscpy(card->longname, "Turtle Beach Multisound Pinnacle"); dev_info(card->dev, LOGNAME ": %s revision %s, Xilinx version %s, " "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n", card->shortname, diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index 63633bd41e5b..2f1bb5a2b376 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -299,7 +299,7 @@ int snd_msndmix_new(struct snd_card *card) if (snd_BUG_ON(!chip)) return -EINVAL; spin_lock_init(&chip->mixer_lock); - strcpy(card->mixername, "MSND Pinnacle Mixer"); + strscpy(card->mixername, "MSND Pinnacle Mixer"); for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { err = snd_ctl_add(card, diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index a5ed5aa0606f..5e8e1326d5c0 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -488,30 +488,30 @@ static int snd_opl3sa2_mixer(struct snd_card *card) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } /* reassign AUX1 to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "FM Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); @@ -618,8 +618,8 @@ static int snd_opl3sa2_card_new(struct device *pdev, int dev, sizeof(struct snd_opl3sa2), &card); if (err < 0) return err; - strcpy(card->driver, "OPL3SA2"); - strcpy(card->shortname, "Yamaha OPL3-SA"); + strscpy(card->driver, "OPL3SA2"); + strscpy(card->shortname, "Yamaha OPL3-SA"); chip = card->private_data; spin_lock_init(&chip->reg_lock); chip->irq = -1; diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index 31d736d1dd10..ad7180d7c0c2 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -718,10 +718,10 @@ static int snd_miro_mixer(struct snd_card *card, switch (miro->hardware) { case OPTi9XX_HW_82C924: - strcpy(card->mixername, "ACI & OPTi924"); + strscpy(card->mixername, "ACI & OPTi924"); break; case OPTi9XX_HW_82C929: - strcpy(card->mixername, "ACI & OPTi929"); + strscpy(card->mixername, "ACI & OPTi929"); break; default: snd_BUG(); @@ -779,7 +779,7 @@ static int snd_miro_init(struct snd_miro *chip, static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; chip->hardware = hardware; - strcpy(chip->name, snd_opti9xx_names[hardware]); + strscpy(chip->name, snd_opti9xx_names[hardware]); chip->mc_base_size = opti9xx_mc_size[hardware]; @@ -1351,7 +1351,7 @@ static int snd_miro_probe(struct snd_card *card) sprintf(card->shortname, "unknown Cardinal Technologies"); } - strcpy(card->driver, "miro"); + strscpy(card->driver, "miro"); scnprintf(card->longname, sizeof(card->longname), "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", card->shortname, miro->name, codec->pcm->name, diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index a07a665d93dc..328d043a1619 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -171,7 +171,7 @@ static int snd_opti9xx_init(struct snd_opti9xx *chip, static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; chip->hardware = hardware; - strcpy(chip->name, snd_opti9xx_names[hardware]); + strscpy(chip->name, snd_opti9xx_names[hardware]); spin_lock_init(&chip->lock); @@ -594,35 +594,35 @@ static int snd_opti93x_mixer(struct snd_wss *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); memset(&id1, 0, sizeof(id1)); memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 switch to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opti93x control\n"); return err; } /* reassign AUX1 switch to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opti93x control\n"); return err; } /* remove AUX1 volume */ - strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; + strscpy(id1.name, "Aux Playback Volume"); id1.index = 1; snd_ctl_remove_id(card, &id1); /* Replace WSS volume controls with OPTi93x volume controls */ id1.index = 0; for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { - strcpy(id1.name, snd_opti93x_controls[idx].name); + strscpy(id1.name, snd_opti93x_controls[idx].name); snd_ctl_remove_id(card, &id1); err = snd_ctl_add(card, @@ -857,7 +857,7 @@ static int snd_opti9xx_probe(struct snd_card *card) #endif chip->irq = irq; card->sync_irq = chip->irq; - strcpy(card->driver, chip->name); + strscpy(card->driver, chip->name); sprintf(card->shortname, "OPTi %s", card->driver); #if defined(CS4231) || defined(OPTi93X) scnprintf(card->longname, sizeof(card->longname), diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 74db11525003..5a083eecaa6b 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -703,6 +703,9 @@ static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ct unsigned char nval, oval; int change; + if (chip->mode & (SB_MODE_PLAYBACK | SB_MODE_CAPTURE)) + return -EBUSY; + nval = ucontrol->value.enumerated.item[0]; if (nval > 2) return -EINVAL; @@ -711,6 +714,10 @@ static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ct change = nval != oval; snd_sb16_set_dma_mode(chip, nval); spin_unlock_irqrestore(&chip->reg_lock, flags); + if (change) { + snd_dma_disable(chip->dma8); + snd_dma_disable(chip->dma16); + } return change; } diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 3115c32b4061..6d618cc2ba45 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -469,24 +469,24 @@ static int snd_sc6000_mixer(struct snd_wss *chip) id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER; id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 to FM */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "FM Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUX1 to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -652,8 +652,8 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) mpu_port[dev]); } - strcpy(card->driver, DRV_NAME); - strcpy(card->shortname, "SC-6000"); + strscpy(card->driver, DRV_NAME); + strscpy(card->shortname, "SC-6000"); sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d", mss_port[dev], xirq, xdma); diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 09120e38f4c2..709a1659d66f 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -1047,8 +1047,8 @@ static int create_sscape(int dev, struct snd_card *card) wss_port[dev], irq[dev]); return err; } - strcpy(card->driver, "SoundScape"); - strcpy(card->shortname, name); + strscpy(card->driver, "SoundScape"); + strscpy(card->shortname, name); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n", name, sscape->chip->port, sscape->chip->irq, diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 621ab420a60f..07c68568091d 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -234,7 +234,7 @@ static struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card, if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) return NULL; - strcpy (wavefront_synth->name, + strscpy (wavefront_synth->name, "WaveFront (ICS2115) wavetable synthesizer"); wavefront_synth->ops.open = snd_wavefront_synth_open; wavefront_synth->ops.release = snd_wavefront_synth_release; @@ -292,10 +292,10 @@ static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card, return NULL; if (mpu == internal_mpu) { - strcpy(rmidi->name, "WaveFront MIDI (Internal)"); + strscpy(rmidi->name, "WaveFront MIDI (Internal)"); rmidi->private_data = &internal_id; } else { - strcpy(rmidi->name, "WaveFront MIDI (External)"); + strscpy(rmidi->name, "WaveFront MIDI (External)"); rmidi->private_data = &external_id; } @@ -407,7 +407,7 @@ snd_wavefront_probe (struct snd_card *card, int dev) return -ENOMEM; } - strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); + strscpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; hw_dev++; @@ -478,12 +478,12 @@ snd_wavefront_probe (struct snd_card *card, int dev) hw_dev++; - strcpy(card->driver, "Tropez+"); - strcpy(card->shortname, "Turtle Beach Tropez+"); + strscpy(card->driver, "Tropez+"); + strscpy(card->shortname, "Turtle Beach Tropez+"); } else { /* Need a way to distinguish between Maui and Tropez */ - strcpy(card->driver, "WaveFront"); - strcpy(card->shortname, "Turtle Beach WaveFront"); + strscpy(card->driver, "WaveFront"); + strscpy(card->shortname, "Turtle Beach WaveFront"); } /* ----- Register the card --------- */ diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 9c655789574d..1b6a80021d18 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -1865,7 +1865,7 @@ int snd_wss_pcm(struct snd_wss *chip, int device) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; if (chip->hardware != WSS_HW_INTERWAVE) pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, snd_wss_chip_id(chip)); + strscpy(pcm->name, snd_wss_chip_id(chip)); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); @@ -1896,7 +1896,7 @@ int snd_wss_timer(struct snd_wss *chip, int device) err = snd_timer_new(chip->card, "CS4231", &tid, &timer); if (err < 0) return err; - strcpy(timer->name, snd_wss_chip_id(chip)); + strscpy(timer->name, snd_wss_chip_id(chip)); timer->private_data = chip; timer->private_free = snd_wss_timer_free; timer->hw = snd_wss_timer_table; @@ -2176,7 +2176,7 @@ int snd_wss_mixer(struct snd_wss *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); /* Use only the first 11 entries on AD1848 */ if (chip->hardware & WSS_HW_AD1848_MASK) diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index 991793e6bda9..f88e6a6733a5 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -706,7 +706,7 @@ static int hal2_pcm_create(struct snd_hal2 *hal2) return err; pcm->private_data = hal2; - strcpy(pcm->name, "SGI HAL2"); + strscpy(pcm->name, "SGI HAL2"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -862,8 +862,8 @@ static int hal2_probe(struct platform_device *pdev) return err; } - strcpy(card->driver, "SGI HAL2 Audio"); - strcpy(card->shortname, "SGI HAL2 Audio"); + strscpy(card->driver, "SGI HAL2 Audio"); + strscpy(card->shortname, "SGI HAL2 Audio"); sprintf(card->longname, "%s irq %i", card->shortname, SGI_HPCDMA_IRQ); diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index 4e2ff954ff59..1af177f25c68 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -685,7 +686,7 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) return err; pcm->private_data = chip; - strcpy(pcm->name, "SGI O2 DAC1"); + strscpy(pcm->name, "SGI O2 DAC1"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -700,7 +701,7 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) return err; pcm->private_data = chip; - strcpy(pcm->name, "SGI O2 DAC2"); + strscpy(pcm->name, "SGI O2 DAC2"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -892,8 +893,8 @@ static int snd_sgio2audio_probe(struct platform_device *pdev) return err; } - strcpy(card->driver, "SGI O2 Audio"); - strcpy(card->shortname, "SGI O2 Audio"); + strscpy(card->driver, "SGI O2 Audio"); + strscpy(card->shortname, "SGI O2 Audio"); sprintf(card->longname, "%s irq %i-%i", card->shortname, MACEISA_AUDIO1_DMAT_IRQ, diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index db9c296dd688..76dd2210f9ea 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -601,7 +601,7 @@ snd_harmony_pcm_init(struct snd_harmony *h) pcm->private_data = h; pcm->info_flags = 0; - strcpy(pcm->name, "harmony"); + strscpy(pcm->name, "harmony"); h->pcm = pcm; h->psubs = NULL; @@ -823,7 +823,7 @@ snd_harmony_mixer_init(struct snd_harmony *h) if (snd_BUG_ON(!h)) return -EINVAL; card = h->card; - strcpy(card->mixername, "Harmony Gain control interface"); + strscpy(card->mixername, "Harmony Gain control interface"); for (idx = 0; idx < HARMONY_CONTROLS; idx++) { err = snd_ctl_add(card, @@ -937,8 +937,8 @@ snd_harmony_probe(struct parisc_device *padev) if (err < 0) goto free_and_ret; - strcpy(card->driver, "harmony"); - strcpy(card->shortname, "Harmony"); + strscpy(card->driver, "harmony"); + strscpy(card->shortname, "Harmony"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, h->hpa, h->irq); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 787868c9e91b..e0996a9d90b0 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -933,5 +933,3 @@ config SND_YMFPCI will be called snd-ymfpci. endif # SND_PCI - -source "sound/pci/hda/Kconfig" diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 18b673018dfd..9d5e8e12ae73 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_SND) += \ lx6464es/ \ echoaudio/ \ emu10k1/ \ - hda/ \ ice1712/ \ korg1212/ \ mixart/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 88ac37739b76..cd60c856a92e 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1840,7 +1840,8 @@ static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id return NULL; } -void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem) +void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, + size_t maxlen, int modem) { const struct ac97_codec_id *pid; @@ -1852,7 +1853,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m if (! pid) return; - strcpy(name, pid->name); + strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) @@ -1861,8 +1862,8 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strcat(name, " "); - strcat(name, pid->name); + strlcat(name, " ", maxlen); + strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { @@ -1870,8 +1871,10 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m (! modem && ! (pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } - } else - sprintf(name + strlen(name), " id %x", id & 0xff); + } else { + int l = strlen(name); + snprintf(name + l, maxlen - l, " id %x", id & 0xff); + } } /** @@ -2295,15 +2298,15 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, /* additional initializations */ if (bus->ops->init) bus->ops->init(ac97); - snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97)); - snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code + snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code if (! ac97->build_ops) ac97->build_ops = &null_build_ops; if (ac97_is_audio(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); @@ -2324,7 +2327,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, if (ac97_is_modem(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h index 8eeae2dec552..965284eb4b33 100644 --- a/sound/pci/ac97/ac97_local.h +++ b/sound/pci/ac97/ac97_local.h @@ -8,7 +8,7 @@ */ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, - int modem); + size_t maxlen, int modem); int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index cd83aa864ea3..3002be9d88f3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -298,7 +298,7 @@ static int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control - Wide"); + strscpy(kctl->id.name, "3D Control - Wide"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); err = snd_ctl_add(ac97->bus->card, @@ -891,7 +891,7 @@ static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97)); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; @@ -906,13 +906,13 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0); kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97); err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 518834964b7b..2df3ba9a08dc 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -98,7 +98,7 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" }; static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" }; - snd_ac97_get_name(NULL, ac97->id, name, 0); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), 0); snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); if ((ac97->scaps & AC97_SCAP_AUDIO) == 0) diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index ac27a93ce4ff..020cbb467e7e 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -605,7 +605,7 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->psubs = NULL; @@ -866,8 +866,8 @@ __snd_ad1889_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "AD1889"); - strcpy(card->shortname, "Analog Devices AD1889"); + strscpy(card->driver, "AD1889"); + strscpy(card->shortname, "Analog Devices AD1889"); /* (3) */ err = snd_ad1889_create(card, pci); diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c index 6af88e7b86f8..e54812bfb2c6 100644 --- a/sound/pci/ak4531_codec.c +++ b/sound/pci/ak4531_codec.c @@ -389,7 +389,7 @@ int snd_ak4531_mixer(struct snd_card *card, snd_ak4531_free(ak4531); return err; } - strcpy(card->mixername, "Asahi Kasei AK4531"); + strscpy(card->mixername, "Asahi Kasei AK4531"); ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ udelay(100); ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 69c02bdd38ce..a6e499e0ceda 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1645,7 +1645,7 @@ static int snd_ali_pcm(struct snd_ali *codec, int device, pcm->info_flags = 0; pcm->dev_class = desc->class; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, desc->name); + strscpy(pcm->name, desc->name); codec->pcm[0] = pcm; return 0; } @@ -2133,8 +2133,8 @@ static int __snd_ali_probe(struct pci_dev *pci, snd_ali_proc_init(codec); - strcpy(card->driver, "ALI5451"); - strcpy(card->shortname, "ALI 5451"); + strscpy(card->driver, "ALI5451"); + strscpy(card->shortname, "ALI 5451"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, codec->port, codec->irq); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 43f98719e61b..f9e8424dc77f 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -546,7 +546,7 @@ static int snd_als300_new_pcm(struct snd_als300 *chip) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, "ALS300"); + strscpy(pcm->name, "ALS300"); chip->pcm = pcm; /* set operators */ @@ -705,7 +705,7 @@ static int snd_als300_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "ALS300"); + strscpy(card->driver, "ALS300"); if (chip->chip_type == DEVICE_ALS300_PLUS) /* don't know much about ALS300+ yet * print revision number for now */ diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 3f4f3037f71f..eb159497c905 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -877,8 +877,8 @@ static int __snd_card_als4000_probe(struct pci_dev *pci, snd_als4000_configure(chip); - strcpy(card->driver, "ALS4000"); - strcpy(card->shortname, "Avance Logic ALS4000"); + strscpy(card->driver, "ALS4000"); + strscpy(card->shortname, "Avance Logic ALS4000"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->alt_port, chip->irq); diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index cbd964f87349..8419f2b6e589 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1257,7 +1257,7 @@ static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device) pcm->private_data = asihpi; pcm->info_flags = 0; - strcpy(pcm->name, "Asihpi PCM"); + strscpy(pcm->name, "Asihpi PCM"); /*? do we want to emulate MMAP for non-BBM cards? Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ @@ -2310,7 +2310,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, clkcache->s[uinfo->value.enumerated.item].name); return 0; } @@ -2530,7 +2530,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (snd_BUG_ON(!asihpi)) return -EINVAL; card = asihpi->card; - strcpy(card->mixername, "Asihpi Mixer"); + strscpy(card->mixername, "Asihpi Mixer"); err = hpi_mixer_open(asihpi->hpi->adapter->index, @@ -2741,7 +2741,7 @@ static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); if (err < 0) return err; - strcpy(hw->name, "asihpi (HPI)"); + strscpy(hw->name, "asihpi (HPI)"); hw->iface = SNDRV_HWDEP_IFACE_LAST; hw->ops.open = snd_asihpi_hpi_open; hw->ops.ioctl = snd_asihpi_hpi_ioctl; @@ -2889,7 +2889,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, by enable_hwdep module param*/ snd_asihpi_hpi_new(asihpi, 0); - strcpy(card->driver, "ASIHPI"); + strscpy(card->driver, "ASIHPI"); sprintf(card->shortname, "AudioScience ASI%4X", asihpi->hpi->adapter->type); diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 72aa135d69f8..b08578c93c6a 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -608,7 +608,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao, phr->u.ax.assert.p2 = 0; phr->u.ax.assert.count = 1; /* assert count */ phr->u.ax.assert.dsp_index = -1; /* "dsp index" */ - strcpy(phr->u.ax.assert.sz_message, "PCI2040 error"); + strscpy(phr->u.ax.assert.sz_message, "PCI2040 error"); phr->u.ax.assert.dsp_msg_addr = 0; gw_pci_read_asserts = 0; gw_pci_write_asserts = 0; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 427006be240b..4f544950ee7b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1271,7 +1271,7 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP AC97"); + strscpy(pcm->name, "ATI IXP AC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1301,9 +1301,9 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops); pcm->private_data = chip; if (chip->spdif_over_aclink) - strcpy(pcm->name, "ATI IXP IEC958 (AC97)"); + strscpy(pcm->name, "ATI IXP IEC958 (AC97)"); else - strcpy(pcm->name, "ATI IXP IEC958 (Direct)"); + strscpy(pcm->name, "ATI IXP IEC958 (Direct)"); chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1576,8 +1576,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); - strcpy(card->shortname, "ATI IXP"); + strscpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); + strscpy(card->shortname, "ATI IXP"); err = snd_atiixp_init(card, pci); if (err < 0) return err; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8d3083b9b024..f7417c2bb477 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -982,7 +982,7 @@ static int snd_atiixp_pcm_new(struct atiixp_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP MC97"); + strscpy(pcm->name, "ATI IXP MC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1206,8 +1206,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ATIIXP-MODEM"); - strcpy(card->shortname, "ATI IXP Modem"); + strscpy(card->driver, "ATIIXP-MODEM"); + strscpy(card->shortname, "ATI IXP Modem"); err = snd_atiixp_init(card, pci); if (err < 0) return err; diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index fd986247331a..de56e83d8e10 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -220,7 +220,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_workaround(pci, pcifix[dev]); // Card details needed in snd_vortex_midi - strcpy(card->driver, CARD_NAME_SHORT); + strscpy(card->driver, CARD_NAME_SHORT); sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->io, chip->irq); @@ -270,7 +270,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_synth_arg_t *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Aureal Synth"); + strscpy(wave->name, "Aureal Synth"); arg->hwptr = vortex; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index aeba684b8d18..00781a7fd28c 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -15,7 +15,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 7b4b8f785517..1d7aab14579e 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -281,8 +281,8 @@ static int snd_aw2_probe(struct pci_dev *pci, /* init spinlock */ spin_lock_init(&chip->reg_lock); /* (4) Define driver ID and name string */ - strcpy(card->driver, "aw2"); - strcpy(card->shortname, "Audiowerk2"); + strscpy(card->driver, "aw2"); + strscpy(card->shortname, "Audiowerk2"); sprintf(card->longname, "%s with SAA7146 irq %i", card->shortname, chip->irq); @@ -509,7 +509,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; /* Set PCM device name */ - strcpy(pcm_playback_ana->name, "Analog playback"); + strscpy(pcm_playback_ana->name, "Analog playback"); /* Associate private data to PCM device */ pcm_playback_ana->private_data = pcm_device; /* set operators of PCM device */ @@ -541,7 +541,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; /* Set PCM device name */ - strcpy(pcm_playback_num->name, "Digital playback"); + strscpy(pcm_playback_num->name, "Digital playback"); /* Associate private data to PCM device */ pcm_playback_num->private_data = pcm_device; /* set operators of PCM device */ @@ -574,7 +574,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; /* Set PCM device name */ - strcpy(pcm_capture->name, "Capture"); + strscpy(pcm_capture->name, "Capture"); /* Associate private data to PCM device */ pcm_capture->private_data = pcm_device; /* set operators of PCM device */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 053a18f434bf..b33344f65b8c 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -412,25 +412,25 @@ snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value) outl(value, chip->ctrl_io + reg); } -static inline void +static inline void __maybe_unused snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) { outb(value, chip->game_io + reg); } -static inline void +static inline void __maybe_unused snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value) { outw(value, chip->game_io + reg); } -static inline u8 +static inline u8 __maybe_unused snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg) { return inb(chip->game_io + reg); } -static inline u16 +static inline u16 __maybe_unused snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg) { return inw(chip->game_io + reg); @@ -1188,7 +1188,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) return err; } snd_component_add(card, "AZF3328 mixer"); - strcpy(card->mixername, "AZF3328 mixer"); + strscpy(card->mixername, "AZF3328 mixer"); return 0; } @@ -2095,7 +2095,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); /* same pcm object for playback/capture (see snd_pcm_new() above) */ chip->pcm[AZF_CODEC_PLAYBACK] = pcm; chip->pcm[AZF_CODEC_CAPTURE] = pcm; @@ -2112,7 +2112,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[AZF_CODEC_I2S_OUT] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -2217,7 +2217,7 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device) if (err < 0) goto out; - strcpy(timer->name, "AZF3328 timer"); + strscpy(timer->name, "AZF3328 timer"); timer->private_data = chip; timer->hw = snd_azf3328_timer_hw; @@ -2437,8 +2437,8 @@ __snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; chip = card->private_data; - strcpy(card->driver, "AZF3328"); - strcpy(card->shortname, "Aztech AZF3328 (PCI168)"); + strscpy(card->driver, "AZF3328"); + strscpy(card->shortname, "Aztech AZF3328 (PCI168)"); err = snd_azf3328_create(card, pci, pci_id->driver_data); if (err < 0) diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 91492dd2b38a..b70f6f4ffe67 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -672,7 +672,7 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, @@ -872,12 +872,12 @@ static int __snd_bt87x_probe(struct pci_dev *pci, chip->board.no_analog ? "no " : "", chip->board.no_digital ? "no " : "", chip->board.dig_rate); - strcpy(card->driver, "Bt87x"); + strscpy(card->driver, "Bt87x"); sprintf(card->shortname, "Brooktree Bt%x", pci->device); sprintf(card->longname, "%s at %#llx, irq %i", card->shortname, (unsigned long long)pci_resource_start(pci, 0), chip->irq); - strcpy(card->mixername, "Bt87x"); + strscpy(card->mixername, "Bt87x"); err = snd_card_register(card); if (err < 0) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 7c7119cad63c..242618793181 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1315,7 +1315,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device) } pcm->info_flags = 0; - strcpy(pcm->name, "CA0106"); + strscpy(pcm->name, "CA0106"); for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; @@ -1617,8 +1617,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card, pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model, pci->revision, chip->serial); - strcpy(card->driver, "CA0106"); - strcpy(card->shortname, "CA0106"); + strscpy(card->driver, "CA0106"); + strscpy(card->shortname, "CA0106"); for (c = ca0106_chip_details; c->serial; c++) { if (subsystem[dev]) { diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 1d5a899b2c24..f7b6b2db889b 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -701,7 +701,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -849,7 +849,7 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu) return err; } - strcpy(card->mixername, "CA0106"); + strscpy(card->mixername, "CA0106"); return 0; } diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index 957e60f64821..f9cec67f31ac 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -287,7 +287,7 @@ int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index b00df0a60d3f..c4ee550d7c96 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -1868,7 +1868,7 @@ static int snd_cmipci_pcm_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI DAC/ADC"); + strscpy(pcm->name, "C-Media PCI DAC/ADC"); cm->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1890,7 +1890,7 @@ static int snd_cmipci_pcm2_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI 2nd DAC"); + strscpy(pcm->name, "C-Media PCI 2nd DAC"); cm->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1913,7 +1913,7 @@ static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI IEC958"); + strscpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2633,7 +2633,7 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) card = cm->card; - strcpy(card->mixername, "CMedia PCI"); + strscpy(card->mixername, "CMedia PCI"); spin_lock_irq(&cm->reg_lock); snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ @@ -3008,11 +3008,12 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B) query_chip(cm); /* added -MCx suffix for chip supporting multi-channels */ - if (cm->can_multi_ch) - sprintf(cm->card->driver + strlen(cm->card->driver), - "-MC%d", cm->max_channels); - else if (cm->can_ac3_sw) - strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC"); + if (cm->can_multi_ch) { + int l = strlen(cm->card->driver); + scnprintf(cm->card->driver + l, sizeof(cm->card->driver) - l, + "-MC%d", cm->max_channels); + } else if (cm->can_ac3_sw) + strlcat(cm->card->driver, "-SWIEC", sizeof(cm->card->driver)); cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; @@ -3216,14 +3217,14 @@ static int snd_cmipci_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_CMEDIA_CM8738: case PCI_DEVICE_ID_CMEDIA_CM8738B: - strcpy(card->driver, "CMI8738"); + strscpy(card->driver, "CMI8738"); break; case PCI_DEVICE_ID_CMEDIA_CM8338A: case PCI_DEVICE_ID_CMEDIA_CM8338B: - strcpy(card->driver, "CMI8338"); + strscpy(card->driver, "CMI8338"); break; default: - strcpy(card->driver, "CMIPCI"); + strscpy(card->driver, "CMIPCI"); break; } diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 90958a422b75..31cb9cbe2f03 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -950,7 +950,7 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "CS4281"); + strscpy(pcm->name, "CS4281"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -1715,7 +1715,7 @@ static int snd_cs4281_midi(struct cs4281 *chip, int device) err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS4281"); + strscpy(rmidi->name, "CS4281"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -1870,8 +1870,8 @@ static int __snd_cs4281_probe(struct pci_dev *pci, if (err < 0) return err; snd_cs4281_create_gameport(chip); - strcpy(card->driver, "CS4281"); - strcpy(card->shortname, "Cirrus Logic CS4281"); + strscpy(card->driver, "CS4281"); + strscpy(card->shortname, "Cirrus Logic CS4281"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->ba0_addr, diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 8634004a606b..9c1995737eb7 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -107,8 +107,8 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, snd_cs46xx_gameport(chip); - strcpy(card->driver, "CS46xx"); - strcpy(card->shortname, "Sound Fusion CS46xx"); + strscpy(card->driver, "CS46xx"); + strscpy(card->shortname, "Sound Fusion CS46xx"); sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", card->shortname, chip->ba0_addr, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fb733633740b..85a7988cf822 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1760,7 +1760,7 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx"); + strscpy(pcm->name, "CS46xx"); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1787,7 +1787,7 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Rear"); + strscpy(pcm->name, "CS46xx - Rear"); chip->pcm_rear = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1812,7 +1812,7 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Center LFE"); + strscpy(pcm->name, "CS46xx - Center LFE"); chip->pcm_center_lfe = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1837,7 +1837,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - IEC958"); + strscpy(pcm->name, "CS46xx - IEC958"); chip->pcm_iec958 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2672,7 +2672,7 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS46XX"); + strscpy(rmidi->name, "CS46XX"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -3853,27 +3853,27 @@ int snd_cs46xx_create(struct snd_card *card, } region = &chip->region.name.ba0; - strcpy(region->name, "CS46xx_BA0"); + strscpy(region->name, "CS46xx_BA0"); region->base = chip->ba0_addr; region->size = CS46XX_BA0_SIZE; region = &chip->region.name.data0; - strcpy(region->name, "CS46xx_BA1_data0"); + strscpy(region->name, "CS46xx_BA1_data0"); region->base = chip->ba1_addr + BA1_SP_DMEM0; region->size = CS46XX_BA1_DATA0_SIZE; region = &chip->region.name.data1; - strcpy(region->name, "CS46xx_BA1_data1"); + strscpy(region->name, "CS46xx_BA1_data1"); region->base = chip->ba1_addr + BA1_SP_DMEM1; region->size = CS46XX_BA1_DATA1_SIZE; region = &chip->region.name.pmem; - strcpy(region->name, "CS46xx_BA1_pmem"); + strscpy(region->name, "CS46xx_BA1_pmem"); region->base = chip->ba1_addr + BA1_SP_PMEM; region->size = CS46XX_BA1_PRG_SIZE; region = &chip->region.name.reg; - strcpy(region->name, "CS46xx_BA1_reg"); + strscpy(region->name, "CS46xx_BA1_reg"); region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 1db6bc58d6a6..e07f85322f1c 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -203,7 +203,7 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type) index = find_free_symbol_index (ins); - strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); + strscpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); ins->symbol_table.symbols[index].address = address; ins->symbol_table.symbols[index].symbol_type = type; ins->symbol_table.symbols[index].module = NULL; @@ -923,7 +923,7 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam index = find_free_scb_index (ins); memset(&ins->scbs[index], 0, sizeof(ins->scbs[index])); - strcpy(ins->scbs[index].scb_name, name); + strscpy(ins->scbs[index].scb_name, name); ins->scbs[index].address = dest; ins->scbs[index].index = index; ins->scbs[index].ref_count = 1; @@ -953,9 +953,9 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) } if (name) - strcpy(ins->tasks[ins->ntask].task_name, name); + strscpy(ins->tasks[ins->ntask].task_name, name); else - strcpy(ins->tasks[ins->ntask].task_name, "(NULL)"); + strscpy(ins->tasks[ins->ntask].task_name, "(NULL)"); ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].size = size; diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 532891e67c34..292b65aa758a 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -207,8 +207,8 @@ static int snd_cs5530_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "CS5530"); - strcpy(card->shortname, "CS5530 Audio"); + strscpy(card->driver, "CS5530"); + strscpy(card->shortname, "CS5530 Audio"); sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); err = snd_card_register(card); diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 0f319013a2a2..76566e7baea0 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -315,9 +315,9 @@ static int __snd_cs5535audio_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "CS5535 Audio"); + strscpy(card->shortname, "CS5535 Audio"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, cs5535au->port, cs5535au->irq); diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 9c88e99e3750..f296b2c63026 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -423,7 +423,7 @@ int snd_cs5535audio_pcm(struct cs5535audio *cs5535au) pcm->private_data = cs5535au; pcm->info_flags = 0; - strcpy(pcm->name, "CS5535 Audio"); + strscpy(pcm->name, "CS5535 Audio"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &cs5535au->pci->dev, diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 6797fde3d788..496682613db5 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -1219,7 +1219,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, if (err) return err; - strcpy(atc->card->mixername, device_name); + strscpy(atc->card->mixername, device_name); return 0; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 713d36ea40cb..d8dd84d41c87 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -98,8 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) goto error; - strcpy(card->driver, "SB-XFi"); - strcpy(card->shortname, "Creative X-Fi"); + strscpy(card->driver, "SB-XFi"); + strscpy(card->shortname, "Creative X-Fi"); snprintf(card->longname, sizeof(card->longname), "%s %s %s", card->shortname, atc->chip_name, atc->model_name); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 80d8ce75fdbb..2b33ef588ac3 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -6,6 +6,7 @@ */ #include +#include MODULE_AUTHOR("Giuliano Pochini "); MODULE_LICENSE("GPL v2"); @@ -916,7 +917,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -929,7 +930,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); #endif /* ECHOCARD_HAS_DIGITAL_IO */ @@ -949,7 +950,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -963,7 +964,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -1985,8 +1986,8 @@ static int __snd_echo_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "Echo_" ECHOCARD_NAME); - strcpy(card->shortname, chip->card_name); + strscpy(card->driver, "Echo_" ECHOCARD_NAME); + strscpy(card->shortname, chip->card_name); dsp = "56301"; if (pci_id->device == 0x3410) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index c3f3c9129561..4ee230794b4e 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -311,7 +311,7 @@ static int snd_echo_midi_create(struct snd_card *card, if (err < 0) return err; - strcpy(chip->rmidi->name, card->shortname); + strscpy(chip->rmidi->name, card->shortname); chip->rmidi->private_data = chip; snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index dadeda7758ce..548e7d049901 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -154,7 +155,7 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, } else { struct snd_emu10k1_synth_arg *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Emu-10k1 Synth"); + strscpy(wave->name, "Emu-10k1 Synth"); arg->hwptr = emu; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 30ac37b5a214..8c18ad987223 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -840,15 +841,15 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) pcm->info_flags = 0; switch(device) { case 0: - strcpy(pcm->name, "EMU10K1X Front"); + strscpy(pcm->name, "EMU10K1X Front"); map = snd_pcm_std_chmaps; break; case 1: - strcpy(pcm->name, "EMU10K1X Rear"); + strscpy(pcm->name, "EMU10K1X Rear"); map = surround_map; break; case 2: - strcpy(pcm->name, "EMU10K1X Center/LFE"); + strscpy(pcm->name, "EMU10K1X Center/LFE"); map = clfe_map; break; } @@ -1461,7 +1462,7 @@ static int emu10k1x_midi_init(struct emu10k1x *emu, spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | @@ -1540,8 +1541,8 @@ static int __snd_emu10k1x_probe(struct pci_dev *pci, snd_emu10k1x_proc_init(chip); - strcpy(card->driver, "EMU10K1X"); - strcpy(card->shortname, "Dell Sound Blaster Live!"); + strscpy(card->driver, "EMU10K1X"); + strscpy(card->shortname, "Dell Sound Blaster Live!"); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->port, chip->irq); diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 03efc317e05f..7db0660e6b61 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1175,7 +1176,7 @@ snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; if (high_res_gpr_volume) { ctl->min = -1; @@ -1199,7 +1200,7 @@ snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; if (high_res_gpr_volume) { ctl->min = -1; @@ -1224,7 +1225,7 @@ snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->min = 0; @@ -1237,7 +1238,7 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; @@ -1325,7 +1326,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 256 / 8); - strcpy(icode->name, "Audigy DSP code for ALSA"); + strscpy(icode->name, "Audigy DSP code for ALSA"); ptr = 0; nctl = 0; gpr_map[bit_shifter16] = 0x00008000; @@ -1563,7 +1564,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[nctl + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1572,7 +1573,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[nctl + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1849,7 +1850,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 160 / 8); - strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); ptr = 0; i = 0; /* we have 12 inputs */ playback = SND_EMU10K1_INPUTS; @@ -2160,7 +2161,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[i + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2170,7 +2171,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2623,7 +2624,7 @@ int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device) err = snd_hwdep_new(emu->card, "FX8010", device, &hw); if (err < 0) return err; - strcpy(hw->name, "EMU10K1 (FX8010)"); + strscpy(hw->name, "EMU10K1 (FX8010)"); hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; hw->ops.open = snd_emu10k1_fx8010_open; hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 05b98d9b547b..d665d5d1ad7c 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -1983,7 +1984,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -2188,11 +2189,11 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, } else { no_ac97: if (emu->card_capabilities->ecard) - strcpy(emu->card->mixername, "EMU APS"); + strscpy(emu->card->mixername, "EMU APS"); else if (emu->audigy) - strcpy(emu->card->mixername, "SB Audigy"); + strscpy(emu->card->mixername, "SB Audigy"); else - strcpy(emu->card->mixername, "Emu10k1"); + strscpy(emu->card->mixername, "Emu10k1"); } if (emu->audigy) diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 747c34b3d566..efff19bbc0e9 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -320,7 +320,7 @@ static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *m spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1bf6e3d652f8..5414148057ea 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1425,7 +1425,7 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); + strscpy(pcm->name, "ADC Capture/Standard PCM Playback"); emu->pcm = pcm; /* playback substream can't use managed buffers due to alignment */ @@ -1457,7 +1457,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Multichannel Playback"); + strscpy(pcm->name, "Multichannel Playback"); emu->pcm_multi = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) @@ -1491,7 +1491,7 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); pcm->info_flags = 0; - strcpy(pcm->name, "Mic Capture"); + strscpy(pcm->name, "Mic Capture"); emu->pcm_mic = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, @@ -1818,9 +1818,9 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; if (emu->audigy) - strcpy(pcm->name, "Multichannel Capture"); + strscpy(pcm->name, "Multichannel Capture"); else - strcpy(pcm->name, "Multichannel Capture/PT Playback"); + strscpy(pcm->name, "Multichannel Capture/PT Playback"); emu->pcm_efx = pcm; if (!emu->card_capabilities->emu_model) { diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index a9a75891f1da..e774174d10de 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -573,7 +573,7 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "p16v"); + strscpy(pcm->name, "p16v"); emu->pcm_p16v = pcm; emu->p16v_interrupt = snd_p16v_interrupt; diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index bb2478319361..1231ae2bf931 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -80,7 +80,7 @@ int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device) tid.subdevice = 0; err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "EMU10K1 timer"); + strscpy(timer->name, "EMU10K1 timer"); timer->private_data = emu; timer->hw = snd_emu10k1_timer_hw; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1e6adf1ae304..82e10ecb9196 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1244,7 +1244,7 @@ static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device) pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC2/ADC"); + strscpy(pcm->name, CHIP_NAME " DAC2/ADC"); ensoniq->pcm1 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1276,7 +1276,7 @@ static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device) #endif pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC1"); + strscpy(pcm->name, CHIP_NAME " DAC1"); ensoniq->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2250,7 +2250,7 @@ static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device) err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, CHIP_NAME); + strscpy(rmidi->name, CHIP_NAME); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | @@ -2346,9 +2346,9 @@ static int __snd_audiopci_probe(struct pci_dev *pci, snd_ensoniq_create_gameport(ensoniq, dev); - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "Ensoniq AudioPCI"); + strscpy(card->shortname, "Ensoniq AudioPCI"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 27728bdfac57..0ce7076206f9 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -982,7 +982,7 @@ static int snd_es1938_new_pcm(struct es1938 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "ESS Solo-1"); + strscpy(pcm->name, "ESS Solo-1"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 64*1024, 64*1024); @@ -1658,7 +1658,7 @@ static int snd_es1938_mixer(struct es1938 *chip) card = chip->card; - strcpy(card->mixername, "ESS Solo-1"); + strscpy(card->mixername, "ESS Solo-1"); for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *kctl; @@ -1720,8 +1720,8 @@ static int __snd_es1938_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "ES1938"); - strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + strscpy(card->driver, "ES1938"); + strscpy(card->shortname, "ESS ES1938 (Solo-1)"); sprintf(card->longname, "%s rev %i, irq %i", card->shortname, chip->revision, diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 37bac890613e..624ba7d47566 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1812,7 +1812,7 @@ snd_es1968_pcm(struct es1968 *chip, int device) pcm->info_flags = 0; - strcpy(pcm->name, "ESS Maestro"); + strscpy(pcm->name, "ESS Maestro"); chip->pcm = pcm; @@ -2761,16 +2761,16 @@ static int __snd_es1968_probe(struct pci_dev *pci, switch (chip->type) { case TYPE_MAESTRO2E: - strcpy(card->driver, "ES1978"); - strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + strscpy(card->driver, "ES1978"); + strscpy(card->shortname, "ESS ES1978 (Maestro 2E)"); break; case TYPE_MAESTRO2: - strcpy(card->driver, "ES1968"); - strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + strscpy(card->driver, "ES1968"); + strscpy(card->shortname, "ESS ES1968 (Maestro 2)"); break; case TYPE_MAESTRO: - strcpy(card->driver, "ESM1"); - strcpy(card->shortname, "ESS Maestro 1"); + strscpy(card->driver, "ESM1"); + strscpy(card->shortname, "ESS Maestro 1"); break; } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index f283256eda0d..cf40bd06b734 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -726,7 +726,7 @@ static int snd_fm801_pcm(struct fm801 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "FM801"); + strscpy(pcm->name, "FM801"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev, @@ -1291,8 +1291,8 @@ static int __snd_card_fm801_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "FM801"); - strcpy(card->shortname, "ForteMedia FM801-"); + strscpy(card->driver, "FM801"); + strscpy(card->shortname, "ForteMedia FM801-"); strcat(card->shortname, chip->multichannel ? "AU" : "AS"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig deleted file mode 100644 index 745f120a5cee..000000000000 --- a/sound/pci/hda/Kconfig +++ /dev/null @@ -1,436 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -menu "HD-Audio" - -config SND_HDA - tristate - select SND_PCM - select SND_VMASTER - select SND_JACK - select SND_HDA_CORE - -config SND_HDA_GENERIC_LEDS - bool - -config SND_HDA_INTEL - tristate "HD Audio PCI" - depends on SND_PCI - select SND_HDA - select SND_INTEL_DSP_CONFIG - help - Say Y here to include support for Intel "High Definition - Audio" (Azalia) and its compatible devices. - - This option enables the HD-audio controller. Don't forget - to choose the appropriate codec options below. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-intel. - -config SND_HDA_TEGRA - tristate "NVIDIA Tegra HD Audio" - depends on ARCH_TEGRA - select SND_HDA - select SND_HDA_ALIGNED_MMIO - help - Say Y here to support the HDA controller present in NVIDIA - Tegra SoCs - - This options enables support for the HD Audio controller - present in some NVIDIA Tegra SoCs, used to communicate audio - to the HDMI output. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-tegra. - -config SND_HDA_ACPI - tristate "HD Audio ACPI" - depends on ACPI - select SND_HDA - help - Say Y here to include support for Azalia-compatible HDA controllers - which are advertised via ACPI objects. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-acpi. - -if SND_HDA - -config SND_HDA_HWDEP - bool "Build hwdep interface for HD-audio driver" - select SND_HWDEP - help - Say Y here to build a hwdep interface for HD-audio driver. - This interface can be used for out-of-band communication - with codecs for debugging purposes. - -config SND_HDA_RECONFIG - bool "Allow dynamic codec reconfiguration" - help - Say Y here to enable the HD-audio codec re-configuration feature. - It allows user to clear the whole codec configuration, change the - codec setup, add extra verbs, and re-configure the codec dynamically. - - Note that this item alone doesn't provide the sysfs interface, but - enables the feature just for the patch loader below. - If you need the traditional sysfs entries for the manual interaction, - turn on CONFIG_SND_HDA_HWDEP as well. - -config SND_HDA_INPUT_BEEP - bool "Support digital beep via input layer" - depends on INPUT=y || INPUT=SND_HDA - help - Say Y here to build a digital beep interface for HD-audio - driver. This interface is used to generate digital beeps. - -config SND_HDA_INPUT_BEEP_MODE - int "Digital beep registration mode (0=off, 1=on)" - depends on SND_HDA_INPUT_BEEP=y - default "1" - range 0 1 - help - Set 0 to disable the digital beep interface for HD-audio by default. - Set 1 to always enable the digital beep interface for HD-audio by - default. - -config SND_HDA_PATCH_LOADER - bool "Support initialization patch loading for HD-audio" - select FW_LOADER - select SND_HDA_RECONFIG - help - Say Y here to allow the HD-audio driver to load a pseudo - firmware file ("patch") for overriding the BIOS setup at - start up. The "patch" file can be specified via patch module - option, such as patch=hda-init. - -config SND_HDA_CIRRUS_SCODEC - tristate - -config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST - tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS - depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT - default KUNIT_ALL_TESTS - help - This builds KUnit tests for the cirrus side-codec library. - For more information on KUnit and unit tests in general, - please refer to the KUnit documentation in - Documentation/dev-tools/kunit/. - If in doubt, say "N". - -config SND_HDA_SCODEC_CS35L41 - tristate - select SND_HDA_GENERIC - select REGMAP_IRQ - select FW_CS_DSP - -config SND_HDA_SCODEC_COMPONENT - tristate - -config SND_HDA_SCODEC_CS35L41_I2C - tristate "Build CS35L41 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m - -config SND_HDA_SCODEC_CS35L41_SPI - tristate "Build CS35L41 HD-audio codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m - -config SND_HDA_SCODEC_CS35L56 - tristate - -config SND_HDA_SCODEC_CS35L56_I2C - tristate "Build CS35L56 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - I2C control. - -config SND_HDA_SCODEC_CS35L56_SPI - tristate "Build CS35L56 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - SPI control. - -config SND_HDA_SCODEC_TAS2781 - tristate - select SND_HDA_GENERIC - -config SND_HDA_SCODEC_TAS2781_I2C - tristate "Build TAS2781 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB_I2C - select SND_SOC_TAS2781_FMWLIB - select CRC32 - help - Say Y or M here to include TAS2781 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m - -config SND_HDA_SCODEC_TAS2781_SPI - tristate "Build TAS2781 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB - select SND_SOC_TAS2781_FMWLIB - select CRC8 - select CRC32 - help - Say Y or M here to include TAS2781 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m - -config SND_HDA_CODEC_REALTEK - tristate "Build Realtek HD-audio codec support" - depends on INPUT - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - select SND_HDA_SCODEC_COMPONENT - help - Say Y or M here to include Realtek HD-audio codec support in - snd-hda-intel driver, such as ALC880. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m - -config SND_HDA_CODEC_ANALOG - tristate "Build Analog Devices HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Analog Devices HD-audio codec support in - snd-hda-intel driver, such as AD1986A. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m - -config SND_HDA_CODEC_SIGMATEL - tristate "Build IDT/Sigmatel HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include IDT (Sigmatel) HD-audio codec support in - snd-hda-intel driver, such as STAC9200. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m - -config SND_HDA_CODEC_VIA - tristate "Build VIA HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include VIA HD-audio codec support in - snd-hda-intel driver, such as VT1708. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_VIA=m - -config SND_HDA_CODEC_HDMI - tristate "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS - select SND_PCM_ELD - help - Say Y or M here to include HDMI and DisplayPort HD-audio codec - support in snd-hda-intel driver. This includes all AMD/ATI, - Intel and Nvidia HDMI/DisplayPort codecs. - - Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS - to assure the multiple streams for DP-MST support. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m - -config SND_HDA_CODEC_CIRRUS - tristate "Build Cirrus Logic codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic codec support in - snd-hda-intel driver, such as CS4206. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m - -config SND_HDA_CODEC_CS8409 - tristate "Build Cirrus Logic HDA bridge support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic HDA bridge support in - snd-hda-intel driver, such as CS8409. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m - -config SND_HDA_CODEC_CONEXANT - tristate "Build Conexant HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include Conexant HD-audio codec support in - snd-hda-intel driver, such as CX20549. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m - -config SND_HDA_CODEC_SENARYTECH - tristate "Build Senarytech HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include Senarytech HD-audio codec support in - snd-hda-intel driver, such as SN6186. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m - -config SND_HDA_CODEC_CA0110 - tristate "Build Creative CA0110-IBG codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Creative CA0110-IBG codec support in - snd-hda-intel driver, found on some Creative X-Fi cards. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m - -config SND_HDA_CODEC_CA0132 - tristate "Build Creative CA0132 codec support" - help - Say Y or M here to include Creative CA0132 codec support in - snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m - -config SND_HDA_CODEC_CA0132_DSP - bool "Support new DSP code for CA0132 codec" - depends on SND_HDA_CODEC_CA0132 - default y - select SND_HDA_DSP_LOADER - select FW_LOADER - help - Say Y here to enable the DSP for Creative CA0132 for extended - features like equalizer or echo cancellation. - - Note that this option requires the external firmware file - (ctefx.bin). - -config SND_HDA_CODEC_CMEDIA - tristate "Build C-Media HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include C-Media HD-audio codec support in - snd-hda-intel driver, such as CMI9880. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m - -config SND_HDA_CODEC_SI3054 - tristate "Build Silicon Labs 3054 HD-modem codec support" - help - Say Y or M here to include Silicon Labs 3054 HD-modem codec - (and compatibles) support in snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m - -config SND_HDA_GENERIC - tristate "Enable generic HD-audio codec parser" - select SND_CTL_LED if SND_HDA_GENERIC_LEDS - select LEDS_CLASS if SND_HDA_GENERIC_LEDS - help - Say Y or M here to enable the generic HD-audio codec parser - in snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_GENERIC=m - -config SND_HDA_POWER_SAVE_DEFAULT - int "Default time-out for HD-audio power-save mode" - depends on PM - default 0 - help - The default time-out value in seconds for HD-audio automatic - power-save mode. 0 means to disable the power-save mode. - -config SND_HDA_INTEL_HDMI_SILENT_STREAM - bool "Enable Silent Stream always for HDMI" - depends on SND_HDA_INTEL - help - Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream - for HDMI on hardware that supports the feature. - - When enabled, the HDMI/DisplayPort codec will continue to provide - a continuous clock and a valid but silent data stream to - any connected external receiver. This allows to avoid gaps - at start of playback. Many receivers require multiple seconds - to start playing audio after the clock has been stopped. - This feature can impact power consumption as resources - are kept reserved both at transmitter and receiver. - -config SND_HDA_CTL_DEV_ID - bool "Use the device identifier field for controls" - depends on SND_HDA_INTEL - help - Say Y to use the device identifier field for (mixer) - controls (old behaviour until this option is available). - - When enabled, the multiple HDA codecs may set the device - field in control (mixer) element identifiers. The use - of this field is not recommended and defined for mixer controls. - - The old behaviour (Y) is obsolete and will be removed. Consider - to not enable this option. - -endif - -endmenu diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile deleted file mode 100644 index a5ab8ee2d7f9..000000000000 --- a/sound/pci/hda/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -snd-hda-intel-y := hda_intel.o -snd-hda-tegra-y := hda_tegra.o -snd-hda-acpi-y := hda_acpi.o - -snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o -snd-hda-codec-y += hda_controller.o -snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o - -snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o -snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o - -# for trace-points -CFLAGS_hda_controller.o := -I$(src) -CFLAGS_hda_intel.o := -I$(src) - -snd-hda-codec-generic-y := hda_generic.o -snd-hda-codec-realtek-y := patch_realtek.o -snd-hda-codec-cmedia-y := patch_cmedia.o -snd-hda-codec-analog-y := patch_analog.o -snd-hda-codec-idt-y := patch_sigmatel.o -snd-hda-codec-si3054-y := patch_si3054.o -snd-hda-codec-cirrus-y := patch_cirrus.o -snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o -snd-hda-codec-ca0110-y := patch_ca0110.o -snd-hda-codec-ca0132-y := patch_ca0132.o -snd-hda-codec-conexant-y := patch_conexant.o -snd-hda-codec-senarytech-y :=patch_senarytech.o -snd-hda-codec-via-y := patch_via.o -snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o - -# side codecs -snd-hda-cirrus-scodec-y := cirrus_scodec.o -snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o -snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o -snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o -snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o -snd-hda-scodec-cs35l56-y := cs35l56_hda.o -snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o -snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o -snd-hda-scodec-component-y := hda_component.o -snd-hda-scodec-tas2781-y := tas2781_hda.o -snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o -snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o - -# common driver -obj-$(CONFIG_SND_HDA) := snd-hda-codec.o - -# codec drivers -obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o -obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o -obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o -obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o -obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o -obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o -obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o -obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.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_CONEXANT) += snd-hda-codec-conexant.o -obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o -obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o -obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o - -# side codecs -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o - -# this must be the last entry after codec drivers; -# otherwise the codec patches won't be hooked before the PCI probe -# when built in kernel -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o -obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o -obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c deleted file mode 100644 index 08308231b4ed..000000000000 --- a/sound/pci/hda/patch_hdmi.c +++ /dev/null @@ -1,4676 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * patch_hdmi.c - routines for HDMI/DisplayPort codecs - * - * Copyright(c) 2008-2010 Intel Corporation - * Copyright (c) 2006 ATI Technologies Inc. - * Copyright (c) 2008 NVIDIA Corp. All rights reserved. - * Copyright (c) 2008 Wei Ni - * Copyright (c) 2013 Anssi Hannula - * - * Authors: - * Wu Fengguang - * - * Maintained by: - * Wu Fengguang - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hda_local.h" -#include "hda_jack.h" -#include "hda_controller.h" - -static bool static_hdmi_pcm; -module_param(static_hdmi_pcm, bool, 0644); -MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); - -static bool enable_acomp = true; -module_param(enable_acomp, bool, 0444); -MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); - -static bool enable_silent_stream = -IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); -module_param(enable_silent_stream, bool, 0644); -MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); - -static bool enable_all_pins; -module_param(enable_all_pins, bool, 0444); -MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins"); - -struct hdmi_spec_per_cvt { - hda_nid_t cvt_nid; - bool assigned; /* the stream has been assigned */ - bool silent_stream; /* silent stream activated */ - unsigned int channels_min; - unsigned int channels_max; - u32 rates; - u64 formats; - unsigned int maxbps; -}; - -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - -struct hdmi_spec_per_pin { - hda_nid_t pin_nid; - int dev_id; - /* pin idx, different device entries on the same pin use the same idx */ - int pin_nid_idx; - int num_mux_nids; - hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; - int mux_idx; - hda_nid_t cvt_nid; - - struct hda_codec *codec; - struct hdmi_eld sink_eld; - struct mutex lock; - struct delayed_work work; - struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ - int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ - int prev_pcm_idx; /* previously assigned pcm index */ - int repoll_count; - bool setup; /* the stream has been set up by prepare callback */ - bool silent_stream; - int channels; /* current number of channels */ - bool non_pcm; - bool chmap_set; /* channel-map override by ALSA API? */ - unsigned char chmap[8]; /* ALSA API channel-map */ -#ifdef CONFIG_SND_PROC_FS - struct snd_info_entry *proc_entry; -#endif -}; - -/* operations used by generic code that can be overridden by patches */ -struct hdmi_ops { - int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, unsigned char *buf, int *eld_size); - - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, - int ca, int active_channels, int conn_type); - - /* enable/disable HBR (HD passthrough) */ - int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr); - - int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format); - - void (*pin_cvt_fixup)(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid); -}; - -struct hdmi_pcm { - struct hda_pcm *pcm; - struct snd_jack *jack; - struct snd_kcontrol *eld_ctl; -}; - -enum { - SILENT_STREAM_OFF = 0, - SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ - SILENT_STREAM_I915, /* Intel i915 extension */ -}; - -struct hdmi_spec { - struct hda_codec *codec; - int num_cvts; - struct snd_array cvts; /* struct hdmi_spec_per_cvt */ - hda_nid_t cvt_nids[4]; /* only for haswell fix */ - - /* - * num_pins is the number of virtual pins - * for example, there are 3 pins, and each pin - * has 4 device entries, then the num_pins is 12 - */ - int num_pins; - /* - * num_nids is the number of real pins - * In the above example, num_nids is 3 - */ - int num_nids; - /* - * dev_num is the number of device entries - * on each pin. - * In the above example, dev_num is 4 - */ - int dev_num; - struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hdmi_pcm pcm_rec[8]; - struct mutex pcm_lock; - struct mutex bind_lock; /* for audio component binding */ - /* pcm_bitmap means which pcms have been assigned to pins*/ - unsigned long pcm_bitmap; - int pcm_used; /* counter of pcm_rec[] */ - /* bitmap shows whether the pcm is opened in user space - * bit 0 means the first playback PCM (PCM3); - * bit 1 means the second playback PCM, and so on. - */ - unsigned long pcm_in_use; - - struct hdmi_eld temp_eld; - struct hdmi_ops ops; - - bool dyn_pin_out; - bool static_pcm_mapping; - /* hdmi interrupt trigger control flag for Nvidia codec */ - bool hdmi_intr_trig_ctrl; - bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */ - - bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ - /* - * Non-generic VIA/NVIDIA specific - */ - struct hda_multi_out multiout; - struct hda_pcm_stream pcm_playback; - - bool use_acomp_notifier; /* use eld_notify callback for hotplug */ - bool acomp_registered; /* audio component registered in this driver */ - bool force_connect; /* force connectivity */ - struct drm_audio_component_audio_ops drm_audio_ops; - int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */ - - struct hdac_chmap chmap; - hda_nid_t vendor_nid; - const int *port_map; - int port_num; - int silent_stream_type; -}; - -#ifdef CONFIG_SND_HDA_COMPONENT -static inline bool codec_has_acomp(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - return spec->use_acomp_notifier; -} -#else -#define codec_has_acomp(codec) false -#endif - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; - - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -struct dp_audio_infoframe { - u8 type; /* 0x84 */ - u8 len; /* 0x1b */ - u8 ver; /* 0x11 << 2 */ - - u8 CC02_CT47; /* match with HDMI infoframe from this on */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -union audio_infoframe { - struct hdmi_audio_infoframe hdmi; - struct dp_audio_infoframe dp; - DECLARE_FLEX_ARRAY(u8, bytes); -}; - -/* - * HDMI routines - */ - -#define get_pin(spec, idx) \ - ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) -#define get_cvt(spec, idx) \ - ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -/* obtain hdmi_pcm object assigned to idx */ -#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) -/* obtain hda_pcm object assigned to idx */ -#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) - -static int pin_id_to_pin_index(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - - /* - * (dev_id == -1) means it is NON-MST pin - * return the first virtual pin on this port - */ - if (dev_id == -1) - dev_id = 0; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - per_pin = get_pin(spec, pin_idx); - if ((per_pin->pin_nid == pin_nid) && - (per_pin->dev_id == dev_id)) - return pin_idx; - } - - codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); - return -EINVAL; -} - -static int hinfo_to_pcm_index(struct hda_codec *codec, - struct hda_pcm_stream *hinfo) -{ - struct hdmi_spec *spec = codec->spec; - int pcm_idx; - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) - if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) - return pcm_idx; - - codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo); - return -EINVAL; -} - -static int hinfo_to_pin_index(struct hda_codec *codec, - struct hda_pcm_stream *hinfo) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - per_pin = get_pin(spec, pin_idx); - if (per_pin->pcm && - per_pin->pcm->pcm->stream == hinfo) - return pin_idx; - } - - codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo, - hinfo_to_pcm_index(codec, hinfo)); - return -EINVAL; -} - -static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec, - int pcm_idx) -{ - int i; - struct hdmi_spec_per_pin *per_pin; - - for (i = 0; i < spec->num_pins; i++) { - per_pin = get_pin(spec, i); - if (per_pin->pcm_idx == pcm_idx) - return per_pin; - } - return NULL; -} - -static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx; - - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) - if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid) - return cvt_idx; - - codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid); - return -EINVAL; -} - -static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - int pcm_idx; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - - pcm_idx = kcontrol->private_value; - mutex_lock(&spec->pcm_lock); - per_pin = pcm_idx_to_pin(spec, pcm_idx); - if (!per_pin) { - /* no pin is bound to the pcm */ - uinfo->count = 0; - goto unlock; - } - eld = &per_pin->sink_eld; - uinfo->count = eld->eld_valid ? eld->eld_size : 0; - - unlock: - mutex_unlock(&spec->pcm_lock); - return 0; -} - -static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - int pcm_idx; - int err = 0; - - pcm_idx = kcontrol->private_value; - mutex_lock(&spec->pcm_lock); - per_pin = pcm_idx_to_pin(spec, pcm_idx); - if (!per_pin) { - /* no pin is bound to the pcm */ - memset(ucontrol->value.bytes.data, 0, - ARRAY_SIZE(ucontrol->value.bytes.data)); - goto unlock; - } - - eld = &per_pin->sink_eld; - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || - eld->eld_size > ELD_MAX_SIZE) { - snd_BUG(); - err = -EINVAL; - goto unlock; - } - - memset(ucontrol->value.bytes.data, 0, - ARRAY_SIZE(ucontrol->value.bytes.data)); - if (eld->eld_valid) - memcpy(ucontrol->value.bytes.data, eld->eld_buffer, - eld->eld_size); - - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -static const struct snd_kcontrol_new eld_bytes_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE | - SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdmi_eld_ctl_info, - .get = hdmi_eld_ctl_get, -}; - -static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx, - int device) -{ - struct snd_kcontrol *kctl; - struct hdmi_spec *spec = codec->spec; - int err; - - kctl = snd_ctl_new1(&eld_bytes_ctl, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = pcm_idx; - kctl->id.device = device; - - /* no pin nid is associated with the kctl now - * tbd: associate pin nid to eld ctl later - */ - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - - get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl; - return 0; -} - -#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int *packet_index, int *byte_index) -{ - int val; - - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_INDEX, 0); - - *packet_index = val >> 5; - *byte_index = val & 0x1f; -} -#endif - -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int packet_index, int byte_index) -{ - int val; - - val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); -} - -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, - unsigned char val) -{ - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); -} - -static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec = codec->spec; - int pin_out; - - /* Unmute */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - if (spec->dyn_pin_out) - /* Disable pin out until stream is active */ - pin_out = 0; - else - /* Enable pin out: some machines with GM965 gets broken output - * when the pin is disabled or changed while using with HDMI - */ - pin_out = PIN_OUT; - - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); -} - -/* - * ELD proc files - */ - -#ifdef CONFIG_SND_PROC_FS -static void print_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hdmi_spec_per_pin *per_pin = entry->private_data; - - mutex_lock(&per_pin->lock); - snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer, per_pin->pin_nid, - per_pin->dev_id, per_pin->cvt_nid); - mutex_unlock(&per_pin->lock); -} - -static void write_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hdmi_spec_per_pin *per_pin = entry->private_data; - - mutex_lock(&per_pin->lock); - snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer); - mutex_unlock(&per_pin->lock); -} - -static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) -{ - char name[32]; - struct hda_codec *codec = per_pin->codec; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, per_pin, print_eld_info); - entry->c.text.write = write_eld_info; - entry->mode |= 0200; - per_pin->proc_entry = entry; - - return 0; -} - -static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) -{ - if (!per_pin->codec->bus->shutdown) { - snd_info_free_entry(per_pin->proc_entry); - per_pin->proc_entry = NULL; - } -} -#else -static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin, - int index) -{ - return 0; -} -static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) -{ -} -#endif - -/* - * Audio InfoFrame routines - */ - -/* - * Enable Audio InfoFrame Transmission - */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_BEST); -} - -/* - * Disable Audio InfoFrame Transmission - */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_DISABLE); -} - -static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int size; - - size = snd_hdmi_get_eld_size(codec, pin_nid); - codec_dbg(codec, "HDMI: ELD buf size is %d\n", size); - - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size); - } -#endif -} - -static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef BE_PARANOID - int i, j; - int size; - int pi, bi; - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - if (size == 0) - continue; - - hdmi_set_dip_index(codec, pin_nid, i, 0x0); - for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, pin_nid, 0x0); - hdmi_get_dip_index(codec, pin_nid, &pi, &bi); - if (pi != i) - codec_dbg(codec, "dip index %d: %d != %d\n", - bi, pi, i); - if (bi == 0) /* byte index wrapped around */ - break; - } - codec_dbg(codec, - "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", - i, size, j); - } -#endif -} - -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) -{ - u8 *bytes = (u8 *)hdmi_ai; - u8 sum = 0; - int i; - - hdmi_ai->checksum = 0; - - for (i = 0; i < sizeof(*hdmi_ai); i++) - sum += bytes[i]; - - hdmi_ai->checksum = -sum; -} - -static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, - u8 *dip, int size) -{ - int i; - - hdmi_debug_dip_size(codec, pin_nid); - hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < size; i++) - hdmi_write_dip_byte(codec, pin_nid, dip[i]); -} - -static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - u8 *dip, int size) -{ - u8 val; - int i; - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) - != AC_DIPXMIT_BEST) - return false; - - for (i = 0; i < size; i++) { - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != dip[i]) - return false; - } - - return true; -} - -static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, - int dev_id, unsigned char *buf, int *eld_size) -{ - snd_hda_set_dev_select(codec, nid, dev_id); - - return snd_hdmi_get_eld(codec, nid, buf, eld_size); -} - -static void hdmi_pin_setup_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, - int ca, int active_channels, - int conn_type) -{ - struct hdmi_spec *spec = codec->spec; - union audio_infoframe ai; - - memset(&ai, 0, sizeof(ai)); - if ((conn_type == 0) || /* HDMI */ - /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */ - (conn_type == 1 && spec->nv_dp_workaround)) { - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - if (conn_type == 0) { /* HDMI */ - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - } else {/* Nvidia DP */ - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x1b; - hdmi_ai->len = 0x11 << 2; - } - hdmi_ai->CC02_CT47 = active_channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = active_channels - 1; - dp_ai->CA = ca; - } else { - codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid); - return; - } - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n", - __func__, pin_nid, active_channels, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } -} - -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool non_pcm) -{ - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - int channels = per_pin->channels; - int active_channels; - struct hdmi_eld *eld; - int ca; - - if (!channels) - return; - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - /* some HW (e.g. HSW+) needs reprogramming the amp at each time */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - - eld = &per_pin->sink_eld; - - ca = snd_hdac_channel_allocation(&codec->core, - eld->info.spk_alloc, channels, - per_pin->chmap_set, non_pcm, per_pin->chmap); - - active_channels = snd_hdac_get_active_channels(ca); - - chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, - active_channels); - - /* - * always configure channel mapping, it may have been changed by the - * user in the meantime - */ - snd_hdac_setup_channel_mapping(&spec->chmap, - pin_nid, non_pcm, ca, channels, - per_pin->chmap, per_pin->chmap_set); - - spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id, - ca, active_channels, eld->info.conn_type); - - per_pin->non_pcm = non_pcm; -} - -/* - * Unsolicited events - */ - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); - -static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, - int dev_id) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_id_to_pin_index(codec, nid, dev_id); - - if (pin_idx < 0) - return; - mutex_lock(&spec->pcm_lock); - hdmi_present_sense(get_pin(spec, pin_idx), 1); - mutex_unlock(&spec->pcm_lock); -} - -static void jack_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - /* stop polling when notification is enabled */ - if (codec_has_acomp(codec)) - return; - - check_presence_and_report(codec, jack->nid, jack->dev_id); -} - -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, - struct hda_jack_tbl *jack) -{ - jack->jack_dirty = 1; - - codec_dbg(codec, - "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), - !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); - - check_presence_and_report(codec, jack->nid, jack->dev_id); -} - -static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); - int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); - - codec_info(codec, - "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", - codec->addr, - tag, - subtag, - cp_state, - cp_ready); - - /* TODO */ - if (cp_state) { - ; - } - if (cp_ready) { - ; - } -} - - -static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - struct hda_jack_tbl *jack; - - if (codec_has_acomp(codec)) - return; - - if (codec->dp_mst) { - int dev_entry = - (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; - - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); - } else { - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); - } - - if (!jack) { - codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag); - return; - } - - if (subtag == 0) - hdmi_intrinsic_event(codec, res, jack); - else - hdmi_non_intrinsic_event(codec, res); -} - -static void haswell_verify_D0(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t nid) -{ - int pwr; - - /* For Haswell, the converter 1/2 may keep in D3 state after bootup, - * thus pins could only choose converter 0 for use. Make sure the - * converters are in correct power state */ - if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - msleep(40); - pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); - pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; - codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); - } -} - -/* - * Callbacks - */ - -/* HBR should be Non-PCM, 8 channels */ -#define is_hbr_format(format) \ - ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) - -static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr) -{ - int pinctl, new_pinctl; - - if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { - snd_hda_set_dev_select(codec, pin_nid, dev_id); - pinctl = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - if (pinctl < 0) - return hbr ? -EINVAL : 0; - - new_pinctl = pinctl & ~AC_PINCTL_EPT; - if (hbr) - new_pinctl |= AC_PINCTL_EPT_HBR; - else - new_pinctl |= AC_PINCTL_EPT_NATIVE; - - codec_dbg(codec, - "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n", - pin_nid, - pinctl == new_pinctl ? "" : "new-", - new_pinctl); - - if (pinctl != new_pinctl) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - new_pinctl); - } else if (hbr) - return -EINVAL; - - return 0; -} - -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, - u32 stream_tag, int format) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int param; - int err; - - err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id, - is_hbr_format(format)); - - if (err) { - codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n"); - return err; - } - - if (spec->intel_hsw_fixup) { - - /* - * on recent platforms IEC Coding Type is required for HBR - * support, read current Digital Converter settings and set - * ICT bitfield if needed. - */ - param = snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - - param = (param >> 16) & ~(AC_DIG3_ICT); - - /* on recent platforms ICT mode is required for HBR support */ - if (is_hbr_format(format)) - param |= 0x1; - - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_DIGI_CONVERT_3, param); - } - - snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); - return 0; -} - -/* Try to find an available converter - * If pin_idx is less then zero, just try to find an available converter. - * Otherwise, try to find an available converter and get the cvt mux index - * of the pin. - */ -static int hdmi_choose_cvt(struct hda_codec *codec, - int pin_idx, int *cvt_id, - bool silent) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int cvt_idx, mux_idx = 0; - - /* pin_idx < 0 means no pin will be bound to the converter */ - if (pin_idx < 0) - per_pin = NULL; - else - per_pin = get_pin(spec, pin_idx); - - if (per_pin && per_pin->silent_stream) { - cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); - per_cvt = get_cvt(spec, cvt_idx); - if (per_cvt->assigned && !silent) - return -EBUSY; - if (cvt_id) - *cvt_id = cvt_idx; - return 0; - } - - /* Dynamically assign converter to stream */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - - /* Must not already be assigned */ - if (per_cvt->assigned || per_cvt->silent_stream) - continue; - if (per_pin == NULL) - break; - /* Must be in pin's mux's list of converters */ - for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) - if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) - break; - /* Not in mux list */ - if (mux_idx == per_pin->num_mux_nids) - continue; - break; - } - - /* No free converters */ - if (cvt_idx == spec->num_cvts) - return -EBUSY; - - if (per_pin != NULL) - per_pin->mux_idx = mux_idx; - - if (cvt_id) - *cvt_id = cvt_idx; - - return 0; -} - -/* Assure the pin select the right convetor */ -static void intel_verify_pin_cvt_connect(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - hda_nid_t pin_nid = per_pin->pin_nid; - int mux_idx, curr; - - mux_idx = per_pin->mux_idx; - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) - snd_hda_codec_write_cache(codec, pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); -} - -/* get the mux index for the converter of the pins - * converter's mux index is the same for all pins on Intel platform - */ -static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, - hda_nid_t cvt_nid) -{ - int i; - - for (i = 0; i < spec->num_cvts; i++) - if (spec->cvt_nids[i] == cvt_nid) - return i; - return -EINVAL; -} - -/* Intel HDMI workaround to fix audio routing issue: - * For some Intel display codecs, pins share the same connection list. - * So a conveter can be selected by multiple pins and playback on any of these - * pins will generate sound on the external display, because audio flows from - * the same converter to the display pipeline. Also muting one pin may make - * other pins have no sound output. - * So this function assures that an assigned converter for a pin is not selected - * by any other pins. - */ -static void intel_not_share_assigned_cvt(struct hda_codec *codec, - hda_nid_t pin_nid, - int dev_id, int mux_idx) -{ - struct hdmi_spec *spec = codec->spec; - hda_nid_t nid; - int cvt_idx, curr; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pin_idx; - - /* configure the pins connections */ - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - int dev_id_saved; - int dev_num; - - per_pin = get_pin(spec, pin_idx); - /* - * pin not connected to monitor - * no need to operate on it - */ - if (!per_pin->pcm) - continue; - - if ((per_pin->pin_nid == pin_nid) && - (per_pin->dev_id == dev_id)) - continue; - - /* - * if per_pin->dev_id >= dev_num, - * snd_hda_get_dev_select() will fail, - * and the following operation is unpredictable. - * So skip this situation. - */ - dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; - if (per_pin->dev_id >= dev_num) - continue; - - nid = per_pin->pin_nid; - - /* - * Calling this function should not impact - * on the device entry selection - * So let's save the dev id for each pin, - * and restore it when return - */ - dev_id_saved = snd_hda_get_dev_select(codec, nid); - snd_hda_set_dev_select(codec, nid, per_pin->dev_id); - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) { - snd_hda_set_dev_select(codec, nid, dev_id_saved); - continue; - } - - - /* choose an unassigned converter. The conveters in the - * connection list are in the same order as in the codec. - */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - if (!per_cvt->assigned) { - codec_dbg(codec, - "choose cvt %d for pin NID 0x%x\n", - cvt_idx, nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - cvt_idx); - break; - } - } - snd_hda_set_dev_select(codec, nid, dev_id_saved); - } -} - -/* A wrapper of intel_not_share_asigned_cvt() */ -static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) -{ - int mux_idx; - struct hdmi_spec *spec = codec->spec; - - /* On Intel platform, the mapping of converter nid to - * mux index of the pins are always the same. - * The pin nid may be 0, this means all pins will not - * share the converter. - */ - mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); - if (mux_idx >= 0) - intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); -} - -/* skeleton caller of pin_cvt_fixup ops */ -static void pin_cvt_fixup(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - - if (spec->ops.pin_cvt_fixup) - spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid); -} - -/* called in hdmi_pcm_open when no pin is assigned to the PCM */ -static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_runtime *runtime = substream->runtime; - int cvt_idx, pcm_idx; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int err; - - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (pcm_idx < 0) - return -EINVAL; - - err = hdmi_choose_cvt(codec, -1, &cvt_idx, false); - if (err) - return err; - - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->assigned = true; - hinfo->nid = per_cvt->cvt_nid; - - pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid); - - set_bit(pcm_idx, &spec->pcm_in_use); - /* todo: setup spdif ctls assign */ - - /* Initially set the converter's capabilities */ - hinfo->channels_min = per_cvt->channels_min; - hinfo->channels_max = per_cvt->channels_max; - hinfo->rates = per_cvt->rates; - hinfo->formats = per_cvt->formats; - hinfo->maxbps = per_cvt->maxbps; - - /* Store the updated parameters */ - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - return 0; -} - -/* - * HDA PCM callbacks - */ -static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, pcm_idx; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int err; - - /* Validate hinfo */ - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (pcm_idx < 0) - return -EINVAL; - - mutex_lock(&spec->pcm_lock); - pin_idx = hinfo_to_pin_index(codec, hinfo); - /* no pin is assigned to the PCM - * PA need pcm open successfully when probe - */ - if (pin_idx < 0) { - err = hdmi_pcm_open_no_pin(hinfo, codec, substream); - goto unlock; - } - - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false); - if (err < 0) - goto unlock; - - per_cvt = get_cvt(spec, cvt_idx); - /* Claim converter */ - per_cvt->assigned = true; - - set_bit(pcm_idx, &spec->pcm_in_use); - per_pin = get_pin(spec, pin_idx); - per_pin->cvt_nid = per_cvt->cvt_nid; - hinfo->nid = per_cvt->cvt_nid; - - /* flip stripe flag for the assigned stream if supported */ - if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE) - azx_stream(get_azx_dev(substream))->stripe = 1; - - snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - per_pin->mux_idx); - - /* configure unused pins to choose other converters */ - pin_cvt_fixup(codec, per_pin, 0); - - snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); - - /* Initially set the converter's capabilities */ - hinfo->channels_min = per_cvt->channels_min; - hinfo->channels_max = per_cvt->channels_max; - hinfo->rates = per_cvt->rates; - hinfo->formats = per_cvt->formats; - hinfo->maxbps = per_cvt->maxbps; - - eld = &per_pin->sink_eld; - /* Restrict capabilities by ELD if this isn't disabled */ - if (!static_hdmi_pcm && eld->eld_valid) { - snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); - if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) { - per_cvt->assigned = false; - hinfo->nid = 0; - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - err = -ENODEV; - goto unlock; - } - } - - /* Store the updated parameters */ - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -/* - * HDA/HDMI auto parsing - */ -static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - int conns; - - if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { - codec_warn(codec, - "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n", - pin_nid, get_wcaps(codec, pin_nid)); - return -EINVAL; - } - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - if (spec->intel_hsw_fixup) { - conns = spec->num_cvts; - memcpy(per_pin->mux_nids, spec->cvt_nids, - sizeof(hda_nid_t) * conns); - } else { - conns = snd_hda_get_raw_connections(codec, pin_nid, - per_pin->mux_nids, - HDA_MAX_CONNECTIONS); - } - - /* all the device entries on the same pin have the same conn list */ - per_pin->num_mux_nids = conns; - - return 0; -} - -static int hdmi_find_pcm_slot(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int i; - - for (i = 0; i < spec->pcm_used; i++) { - if (!test_bit(i, &spec->pcm_bitmap)) - return i; - } - return -EBUSY; -} - -static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int idx; - - /* pcm already be attached to the pin */ - if (per_pin->pcm) - return; - /* try the previously used slot at first */ - idx = per_pin->prev_pcm_idx; - if (idx >= 0) { - if (!test_bit(idx, &spec->pcm_bitmap)) - goto found; - per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */ - } - idx = hdmi_find_pcm_slot(spec, per_pin); - if (idx == -EBUSY) - return; - found: - per_pin->pcm_idx = idx; - per_pin->pcm = get_hdmi_pcm(spec, idx); - set_bit(idx, &spec->pcm_bitmap); -} - -static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int idx; - - /* pcm already be detached from the pin */ - if (!per_pin->pcm) - return; - idx = per_pin->pcm_idx; - per_pin->pcm_idx = -1; - per_pin->prev_pcm_idx = idx; /* remember the previous index */ - per_pin->pcm = NULL; - if (idx >= 0 && idx < spec->pcm_used) - clear_bit(idx, &spec->pcm_bitmap); -} - -static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid) -{ - int mux_idx; - - for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) - if (per_pin->mux_nids[mux_idx] == cvt_nid) - break; - return mux_idx; -} - -static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid); - -static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hda_codec *codec = per_pin->codec; - struct hda_pcm *pcm; - struct hda_pcm_stream *hinfo; - struct snd_pcm_substream *substream; - int mux_idx; - bool non_pcm; - - if (per_pin->pcm_idx < 0 || per_pin->pcm_idx >= spec->pcm_used) - return; - pcm = get_pcm_rec(spec, per_pin->pcm_idx); - if (!pcm->pcm) - return; - if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) - return; - - /* hdmi audio only uses playback and one substream */ - hinfo = pcm->stream; - substream = pcm->pcm->streams[0].substream; - - per_pin->cvt_nid = hinfo->nid; - - mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid); - if (mux_idx < per_pin->num_mux_nids) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); - } - snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid); - - non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid); - if (substream->runtime) - per_pin->channels = substream->runtime->channels; - per_pin->setup = true; - per_pin->mux_idx = mux_idx; - - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); -} - -static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) - snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx); - - per_pin->chmap_set = false; - memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); - - per_pin->setup = false; - per_pin->channels = 0; -} - -static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - - if (per_pin->pcm_idx >= 0) - return spec->pcm_rec[per_pin->pcm_idx].jack; - else - return NULL; -} - -/* update per_pin ELD from the given new ELD; - * setup info frame and notification accordingly - * also notify ELD kctl and report jack status changes - */ -static void update_eld(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - struct hdmi_eld *eld, - int repoll) -{ - struct hdmi_eld *pin_eld = &per_pin->sink_eld; - struct hdmi_spec *spec = codec->spec; - struct snd_jack *pcm_jack; - bool old_eld_valid = pin_eld->eld_valid; - bool eld_changed; - int pcm_idx; - - if (eld->eld_valid) { - if (eld->eld_size <= 0 || - snd_parse_eld(hda_codec_dev(codec), &eld->info, - eld->eld_buffer, eld->eld_size) < 0) { - eld->eld_valid = false; - if (repoll) { - schedule_delayed_work(&per_pin->work, - msecs_to_jiffies(300)); - return; - } - } - } - - if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { - eld->eld_valid = false; - eld->eld_size = 0; - } - - /* for monitor disconnection, save pcm_idx firstly */ - pcm_idx = per_pin->pcm_idx; - - /* - * pcm_idx >=0 before update_eld() means it is in monitor - * disconnected event. Jack must be fetched before update_eld(). - */ - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - - if (!spec->static_pcm_mapping) { - if (eld->eld_valid) { - hdmi_attach_hda_pcm(spec, per_pin); - hdmi_pcm_setup_pin(spec, per_pin); - } else { - hdmi_pcm_reset_pin(spec, per_pin); - hdmi_detach_hda_pcm(spec, per_pin); - } - } - - /* if pcm_idx == -1, it means this is in monitor connection event - * we can get the correct pcm_idx now. - */ - if (pcm_idx == -1) - pcm_idx = per_pin->pcm_idx; - if (!pcm_jack) - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - - if (eld->eld_valid) - snd_show_eld(hda_codec_dev(codec), &eld->info); - - eld_changed = (pin_eld->eld_valid != eld->eld_valid); - eld_changed |= (pin_eld->monitor_present != eld->monitor_present); - if (!eld_changed && eld->eld_valid && pin_eld->eld_valid) - if (pin_eld->eld_size != eld->eld_size || - memcmp(pin_eld->eld_buffer, eld->eld_buffer, - eld->eld_size) != 0) - eld_changed = true; - - if (eld_changed) { - pin_eld->monitor_present = eld->monitor_present; - pin_eld->eld_valid = eld->eld_valid; - pin_eld->eld_size = eld->eld_size; - if (eld->eld_valid) - memcpy(pin_eld->eld_buffer, eld->eld_buffer, - eld->eld_size); - pin_eld->info = eld->info; - } - - /* - * Re-setup pin and infoframe. This is needed e.g. when - * - sink is first plugged-in - * - transcoder can change during stream playback on Haswell - * and this can make HW reset converter selection on a pin. - */ - if (eld->eld_valid && !old_eld_valid && per_pin->setup) { - pin_cvt_fixup(codec, per_pin, 0); - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - } - - if (eld_changed && pcm_idx >= 0) - snd_ctl_notify(codec->card, - SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); - - if (eld_changed && pcm_jack) - snd_jack_report(pcm_jack, - (eld->monitor_present && eld->eld_valid) ? - SND_JACK_AVOUT : 0); -} - -/* update ELD and jack state via HD-audio verbs */ -static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, - int repoll) -{ - struct hda_codec *codec = per_pin->codec; - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->temp_eld; - struct device *dev = hda_codec_dev(codec); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - /* - * Always execute a GetPinSense verb here, even when called from - * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited - * response's PD bit is not the real PD value, but indicates that - * the real PD value changed. An older version of the HD-audio - * specification worked this way. Hence, we just ignore the data in - * the unsolicited response to avoid custom WARs. - */ - int present; - int ret; - -#ifdef CONFIG_PM - if (dev->power.runtime_status == RPM_SUSPENDING) - return; -#endif - - ret = snd_hda_power_up_pm(codec); - if (ret < 0 && pm_runtime_suspended(dev)) - goto out; - - present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id); - - mutex_lock(&per_pin->lock); - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - if (eld->monitor_present) - eld->eld_valid = !!(present & AC_PINSENSE_ELDV); - else - eld->eld_valid = false; - - codec_dbg(codec, - "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); - - if (eld->eld_valid) { - if (spec->ops.pin_get_eld(codec, pin_nid, dev_id, - eld->eld_buffer, &eld->eld_size) < 0) - eld->eld_valid = false; - } - - update_eld(codec, per_pin, eld, repoll); - mutex_unlock(&per_pin->lock); - out: - snd_hda_power_down_pm(codec); -} - -#define I915_SILENT_RATE 48000 -#define I915_SILENT_CHANNELS 2 -#define I915_SILENT_FORMAT_BITS 16 -#define I915_SILENT_FMT_MASK 0xf - -static void silent_stream_enable_i915(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - unsigned int format; - - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, I915_SILENT_RATE); - - /* trigger silent stream generation in hw */ - format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, - I915_SILENT_RATE); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, - I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); - usleep_range(100, 200); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); - - per_pin->channels = I915_SILENT_CHANNELS; - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); -} - -static void silent_stream_set_kae(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool enable) -{ - unsigned int param; - - codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); - param = (param >> 16) & 0xff; - - if (enable) - param |= AC_DIG3_KAE; - else - param &= ~AC_DIG3_KAE; - - snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); -} - -static void silent_stream_enable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx, pin_idx, err; - int keep_power = 0; - - /* - * Power-up will call hdmi_present_sense, so the PM calls - * have to be done without mutex held. - */ - - err = snd_hda_power_up_pm(codec); - if (err < 0 && err != -EACCES) { - codec_err(codec, - "Failed to power up codec for silent stream enable ret=[%d]\n", err); - snd_hda_power_down_pm(codec); - return; - } - - mutex_lock(&per_pin->lock); - - if (per_pin->setup) { - codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); - err = -EBUSY; - goto unlock_out; - } - - pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id); - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, true); - if (err) { - codec_err(codec, "hdmi: no free converter to enable silent mode\n"); - goto unlock_out; - } - - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->silent_stream = true; - per_pin->cvt_nid = per_cvt->cvt_nid; - per_pin->silent_stream = true; - - codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n", - per_pin->pin_nid, per_cvt->cvt_nid); - - snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - per_pin->mux_idx); - - /* configure unused pins to choose other converters */ - pin_cvt_fixup(codec, per_pin, 0); - - switch (spec->silent_stream_type) { - case SILENT_STREAM_KAE: - silent_stream_enable_i915(codec, per_pin); - silent_stream_set_kae(codec, per_pin, true); - break; - case SILENT_STREAM_I915: - silent_stream_enable_i915(codec, per_pin); - keep_power = 1; - break; - default: - break; - } - - unlock_out: - mutex_unlock(&per_pin->lock); - - if (err || !keep_power) - snd_hda_power_down_pm(codec); -} - -static void silent_stream_disable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx, err; - - err = snd_hda_power_up_pm(codec); - if (err < 0 && err != -EACCES) { - codec_err(codec, - "Failed to power up codec for silent stream disable ret=[%d]\n", - err); - snd_hda_power_down_pm(codec); - return; - } - - mutex_lock(&per_pin->lock); - if (!per_pin->silent_stream) - goto unlock_out; - - codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n", - per_pin->pin_nid, per_pin->cvt_nid); - - cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); - if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) { - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->silent_stream = false; - } - - if (spec->silent_stream_type == SILENT_STREAM_I915) { - /* release ref taken in silent_stream_enable() */ - snd_hda_power_down_pm(codec); - } else if (spec->silent_stream_type == SILENT_STREAM_KAE) { - silent_stream_set_kae(codec, per_pin, false); - } - - per_pin->cvt_nid = 0; - per_pin->silent_stream = false; - - unlock_out: - mutex_unlock(&per_pin->lock); - - snd_hda_power_down_pm(codec); -} - -/* update ELD and jack state via audio component */ -static void sync_eld_via_acomp(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->temp_eld; - bool monitor_prev, monitor_next; - - mutex_lock(&per_pin->lock); - eld->monitor_present = false; - monitor_prev = per_pin->sink_eld.monitor_present; - eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, - per_pin->dev_id, &eld->monitor_present, - eld->eld_buffer, ELD_MAX_SIZE); - eld->eld_valid = (eld->eld_size > 0); - update_eld(codec, per_pin, eld, 0); - monitor_next = per_pin->sink_eld.monitor_present; - mutex_unlock(&per_pin->lock); - - if (spec->silent_stream_type) { - if (!monitor_prev && monitor_next) - silent_stream_enable(codec, per_pin); - else if (monitor_prev && !monitor_next) - silent_stream_disable(codec, per_pin); - } -} - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) -{ - struct hda_codec *codec = per_pin->codec; - - if (!codec_has_acomp(codec)) - hdmi_present_sense_via_verbs(per_pin, repoll); - else - sync_eld_via_acomp(codec, per_pin); -} - -static void hdmi_repoll_eld(struct work_struct *work) -{ - struct hdmi_spec_per_pin *per_pin = - container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); - struct hda_codec *codec = per_pin->codec; - struct hdmi_spec *spec = codec->spec; - struct hda_jack_tbl *jack; - - jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, - per_pin->dev_id); - if (jack) - jack->jack_dirty = 1; - - if (per_pin->repoll_count++ > 6) - per_pin->repoll_count = 0; - - mutex_lock(&spec->pcm_lock); - hdmi_present_sense(per_pin, per_pin->repoll_count); - mutex_unlock(&spec->pcm_lock); -} - -static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int caps, config; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - int err; - int dev_num, i; - - caps = snd_hda_query_pin_caps(codec, pin_nid); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - return 0; - - /* - * For DP MST audio, Configuration Default is the same for - * all device entries on the same pin - */ - config = snd_hda_codec_get_pincfg(codec, pin_nid); - if (get_defcfg_connect(config) == AC_JACK_PORT_NONE && - !spec->force_connect) - return 0; - - /* - * To simplify the implementation, malloc all - * the virtual pins in the initialization statically - */ - if (spec->intel_hsw_fixup) { - /* - * On Intel platforms, device entries count returned - * by AC_PAR_DEVLIST_LEN is dynamic, and depends on - * the type of receiver that is connected. Allocate pin - * structures based on worst case. - */ - dev_num = spec->dev_num; - } else if (codec->dp_mst) { - dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1; - /* - * spec->dev_num is the maxinum number of device entries - * among all the pins - */ - spec->dev_num = (spec->dev_num > dev_num) ? - spec->dev_num : dev_num; - } else { - /* - * If the platform doesn't support DP MST, - * manually set dev_num to 1. This means - * the pin has only one device entry. - */ - dev_num = 1; - spec->dev_num = 1; - } - - for (i = 0; i < dev_num; i++) { - pin_idx = spec->num_pins; - per_pin = snd_array_new(&spec->pins); - - if (!per_pin) - return -ENOMEM; - - per_pin->pcm = NULL; - per_pin->pcm_idx = -1; - per_pin->prev_pcm_idx = -1; - per_pin->pin_nid = pin_nid; - per_pin->pin_nid_idx = spec->num_nids; - per_pin->dev_id = i; - per_pin->non_pcm = false; - snd_hda_set_dev_select(codec, pin_nid, i); - err = hdmi_read_pin_conn(codec, pin_idx); - if (err < 0) - return err; - if (!is_jack_detectable(codec, pin_nid)) - codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid); - spec->num_pins++; - } - spec->num_nids++; - - return 0; -} - -static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - unsigned int chans; - int err; - - chans = get_wcaps(codec, cvt_nid); - chans = get_wcaps_channels(chans); - - per_cvt = snd_array_new(&spec->cvts); - if (!per_cvt) - return -ENOMEM; - - per_cvt->cvt_nid = cvt_nid; - per_cvt->channels_min = 2; - if (chans <= 16) { - per_cvt->channels_max = chans; - if (chans > spec->chmap.channels_max) - spec->chmap.channels_max = chans; - } - - err = snd_hda_query_supported_pcm(codec, cvt_nid, - &per_cvt->rates, - &per_cvt->formats, - NULL, - &per_cvt->maxbps); - if (err < 0) - return err; - - if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids)) - spec->cvt_nids[spec->num_cvts] = cvt_nid; - spec->num_cvts++; - - return 0; -} - -static const struct snd_pci_quirk force_connect_list[] = { - SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), - SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), - SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1), - SND_PCI_QUIRK(0x1043, 0x86ae, "ASUS", 1), /* Z170 PRO */ - SND_PCI_QUIRK(0x1043, 0x86c7, "ASUS", 1), /* Z170M PLUS */ - SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1), - SND_PCI_QUIRK(0x8086, 0x2060, "Intel NUC5CPYB", 1), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1), - {} -}; - -static int hdmi_parse_codec(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - hda_nid_t start_nid; - unsigned int caps; - int i, nodes; - const struct snd_pci_quirk *q; - - nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid); - if (!start_nid || nodes < 0) { - codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); - return -EINVAL; - } - - if (enable_all_pins) - spec->force_connect = true; - - q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list); - - if (q && q->value) - spec->force_connect = true; - - /* - * hdmi_add_pin() assumes total amount of converters to - * be known, so first discover all converters - */ - for (i = 0; i < nodes; i++) { - hda_nid_t nid = start_nid + i; - - caps = get_wcaps(codec, nid); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - if (get_wcaps_type(caps) == AC_WID_AUD_OUT) - hdmi_add_cvt(codec, nid); - } - - /* discover audio pins */ - for (i = 0; i < nodes; i++) { - hda_nid_t nid = start_nid + i; - - caps = get_wcaps(codec, nid); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - if (get_wcaps_type(caps) == AC_WID_PIN) - hdmi_add_pin(codec, nid); - } - - return 0; -} - -/* - */ -static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hda_spdif_out *spdif; - bool non_pcm; - - mutex_lock(&codec->spdif_mutex); - spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid); - /* Add sanity check to pass klockwork check. - * This should never happen. - */ - if (WARN_ON(spdif == NULL)) { - mutex_unlock(&codec->spdif_mutex); - return true; - } - non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); - mutex_unlock(&codec->spdif_mutex); - return non_pcm; -} - -/* - * HDMI callbacks - */ - -static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - hda_nid_t cvt_nid = hinfo->nid; - struct hdmi_spec *spec = codec->spec; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - struct snd_pcm_runtime *runtime = substream->runtime; - bool non_pcm; - int pinctl, stripe; - int err = 0; - - mutex_lock(&spec->pcm_lock); - pin_idx = hinfo_to_pin_index(codec, hinfo); - if (pin_idx < 0) { - /* when pcm is not bound to a pin skip pin setup and return 0 - * to make audio playback be ongoing - */ - pin_cvt_fixup(codec, NULL, cvt_nid); - snd_hda_codec_setup_stream(codec, cvt_nid, - stream_tag, 0, format); - goto unlock; - } - - per_pin = get_pin(spec, pin_idx); - - /* Verify pin:cvt selections to avoid silent audio after S3. - * After S3, the audio driver restores pin:cvt selections - * but this can happen before gfx is ready and such selection - * is overlooked by HW. Thus multiple pins can share a same - * default convertor and mute control will affect each other, - * which can cause a resumed audio playback become silent - * after S3. - */ - pin_cvt_fixup(codec, per_pin, 0); - - /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ - /* Todo: add DP1.2 MST audio support later */ - if (codec_has_acomp(codec)) - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, runtime->rate); - - non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); - mutex_lock(&per_pin->lock); - per_pin->channels = substream->runtime->channels; - per_pin->setup = true; - - if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { - stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, - substream); - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_STRIPE_CONTROL, - stripe); - } - - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); - mutex_unlock(&per_pin->lock); - if (spec->dyn_pin_out) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl | PIN_OUT); - } - - /* snd_hda_set_dev_select() has been called before */ - err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid, - per_pin->dev_id, stream_tag, format); - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - return 0; -} - -static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx, pin_idx, pcm_idx; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pinctl; - int err = 0; - - mutex_lock(&spec->pcm_lock); - if (hinfo->nid) { - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (snd_BUG_ON(pcm_idx < 0)) { - err = -EINVAL; - goto unlock; - } - cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); - if (snd_BUG_ON(cvt_idx < 0)) { - err = -EINVAL; - goto unlock; - } - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->assigned = false; - hinfo->nid = 0; - - azx_stream(get_azx_dev(substream))->stripe = 0; - - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - clear_bit(pcm_idx, &spec->pcm_in_use); - pin_idx = hinfo_to_pin_index(codec, hinfo); - /* - * In such a case, return 0 to match the behavior in - * hdmi_pcm_open() - */ - if (pin_idx < 0) - goto unlock; - - per_pin = get_pin(spec, pin_idx); - - if (spec->dyn_pin_out) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl & ~PIN_OUT); - } - - mutex_lock(&per_pin->lock); - per_pin->chmap_set = false; - memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); - - per_pin->setup = false; - per_pin->channels = 0; - mutex_unlock(&per_pin->lock); - } - -unlock: - mutex_unlock(&spec->pcm_lock); - - return err; -} - -static const struct hda_pcm_ops generic_ops = { - .open = hdmi_pcm_open, - .close = hdmi_pcm_close, - .prepare = generic_hdmi_playback_pcm_prepare, - .cleanup = generic_hdmi_playback_pcm_cleanup, -}; - -static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - if (!per_pin) - return 0; - - return per_pin->sink_eld.info.spk_alloc; -} - -static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, - unsigned char *chmap) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - /* chmap is already set to 0 in caller */ - if (!per_pin) - return; - - memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); -} - -static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, - unsigned char *chmap, int prepared) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - if (!per_pin) - return; - mutex_lock(&per_pin->lock); - per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); - if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->lock); -} - -static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - return per_pin ? true:false; -} - -static int generic_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int idx, pcm_num; - - /* limit the PCM devices to the codec converters or available PINs */ - pcm_num = min(spec->num_cvts, spec->num_pins); - codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num); - - for (idx = 0; idx < pcm_num; idx++) { - struct hdmi_spec_per_cvt *per_cvt; - struct hda_pcm *info; - struct hda_pcm_stream *pstr; - - info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx); - if (!info) - return -ENOMEM; - - spec->pcm_rec[idx].pcm = info; - spec->pcm_used++; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->own_chmap = true; - - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - pstr->substreams = 1; - pstr->ops = generic_ops; - - per_cvt = get_cvt(spec, 0); - pstr->channels_min = per_cvt->channels_min; - pstr->channels_max = per_cvt->channels_max; - - /* pcm number is less than pcm_rec array size */ - if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec)) - break; - /* other pstr fields are set in open */ - } - - return 0; -} - -static void free_hdmi_jack_priv(struct snd_jack *jack) -{ - struct hdmi_pcm *pcm = jack->private_data; - - pcm->jack = NULL; -} - -static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) -{ - char hdmi_str[32] = "HDMI/DP"; - struct hdmi_spec *spec = codec->spec; - struct snd_jack *jack; - int pcmdev = get_pcm_rec(spec, pcm_idx)->device; - int err; - - if (pcmdev > 0) - sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - - err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, - true, false); - if (err < 0) - return err; - - spec->pcm_rec[pcm_idx].jack = jack; - jack->private_data = &spec->pcm_rec[pcm_idx]; - jack->private_free = free_hdmi_jack_priv; - return 0; -} - -static int generic_hdmi_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int dev, err; - int pin_idx, pcm_idx; - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - if (!get_pcm_rec(spec, pcm_idx)->pcm) { - /* no PCM: mark this for skipping permanently */ - set_bit(pcm_idx, &spec->pcm_bitmap); - continue; - } - - err = generic_hdmi_build_jack(codec, pcm_idx); - if (err < 0) - return err; - - /* create the spdif for each pcm - * pin will be bound when monitor is connected - */ - err = snd_hda_create_dig_out_ctls(codec, - 0, spec->cvt_nids[0], - HDA_PCM_TYPE_HDMI); - if (err < 0) - return err; - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - - dev = get_pcm_rec(spec, pcm_idx)->device; - if (dev != SNDRV_PCM_INVALID_DEVICE) { - /* add control for ELD Bytes */ - err = hdmi_create_eld_ctl(codec, pcm_idx, dev); - if (err < 0) - return err; - } - } - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *pin_eld = &per_pin->sink_eld; - - if (spec->static_pcm_mapping) { - hdmi_attach_hda_pcm(spec, per_pin); - hdmi_pcm_setup_pin(spec, per_pin); - } - - pin_eld->eld_valid = false; - hdmi_present_sense(per_pin, 0); - } - - /* add channel maps */ - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - struct hda_pcm *pcm; - - pcm = get_pcm_rec(spec, pcm_idx); - if (!pcm || !pcm->pcm) - break; - err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); - if (err < 0) - return err; - } - - return 0; -} - -static int generic_hdmi_init_per_pins(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - per_pin->codec = codec; - mutex_init(&per_pin->lock); - INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); - eld_proc_new(per_pin, pin_idx); - } - return 0; -} - -static int generic_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - mutex_lock(&spec->bind_lock); - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - hdmi_init_pin(codec, pin_nid); - if (codec_has_acomp(codec)) - continue; - snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id, - jack_callback); - } - mutex_unlock(&spec->bind_lock); - return 0; -} - -static void hdmi_array_init(struct hdmi_spec *spec, int nums) -{ - snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); - snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); -} - -static void hdmi_array_free(struct hdmi_spec *spec) -{ - snd_array_free(&spec->pins); - snd_array_free(&spec->cvts); -} - -static void generic_spec_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - if (spec) { - hdmi_array_free(spec); - kfree(spec); - codec->spec = NULL; - } - codec->dp_mst = false; -} - -static void generic_hdmi_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, pcm_idx; - - if (spec->acomp_registered) { - snd_hdac_acomp_exit(&codec->bus->core); - } else if (codec_has_acomp(codec)) { - snd_hdac_acomp_register_notifier(&codec->bus->core, NULL); - } - codec->relaxed_resume = 0; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); - eld_proc_free(per_pin); - } - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - if (spec->pcm_rec[pcm_idx].jack == NULL) - continue; - snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack); - } - - generic_spec_free(codec); -} - -static int generic_hdmi_suspend(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); - } - return 0; -} - -static int generic_hdmi_resume(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hdmi_present_sense(per_pin, 1); - } - return 0; -} - -static const struct hda_codec_ops generic_hdmi_patch_ops = { - .init = generic_hdmi_init, - .free = generic_hdmi_free, - .build_pcms = generic_hdmi_build_pcms, - .build_controls = generic_hdmi_build_controls, - .unsol_event = hdmi_unsol_event, - .suspend = generic_hdmi_suspend, - .resume = generic_hdmi_resume, -}; - -static const struct hdmi_ops generic_standard_hdmi_ops = { - .pin_get_eld = hdmi_pin_get_eld, - .pin_setup_infoframe = hdmi_pin_setup_infoframe, - .pin_hbr_setup = hdmi_pin_hbr_setup, - .setup_stream = hdmi_setup_stream, -}; - -/* allocate codec->spec and assign/initialize generic parser ops */ -static int alloc_generic_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - spec->codec = codec; - spec->ops = generic_standard_hdmi_ops; - spec->dev_num = 1; /* initialize to 1 */ - mutex_init(&spec->pcm_lock); - mutex_init(&spec->bind_lock); - snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); - - spec->chmap.ops.get_chmap = hdmi_get_chmap; - spec->chmap.ops.set_chmap = hdmi_set_chmap; - spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; - spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc; - - codec->spec = spec; - hdmi_array_init(spec, 4); - - codec->patch_ops = generic_hdmi_patch_ops; - - return 0; -} - -/* generic HDMI parser */ -static int patch_generic_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - return 0; -} - -/* - * generic audio component binding - */ - -/* turn on / off the unsol event jack detection dynamically */ -static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid, - int dev_id, bool use_acomp) -{ - struct hda_jack_tbl *tbl; - - tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id); - if (tbl) { - /* clear unsol even if component notifier is used, or re-enable - * if notifier is cleared - */ - unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, val); - } -} - -/* set up / clear component notifier dynamically */ -static void generic_acomp_notifier_set(struct drm_audio_component *acomp, - bool use_acomp) -{ - struct hdmi_spec *spec; - int i; - - spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops); - mutex_lock(&spec->bind_lock); - spec->use_acomp_notifier = use_acomp; - spec->codec->relaxed_resume = use_acomp; - spec->codec->bus->keep_power = 0; - /* reprogram each jack detection logic depending on the notifier */ - for (i = 0; i < spec->num_pins; i++) - reprogram_jack_detect(spec->codec, - get_pin(spec, i)->pin_nid, - get_pin(spec, i)->dev_id, - use_acomp); - mutex_unlock(&spec->bind_lock); -} - -/* enable / disable the notifier via master bind / unbind */ -static int generic_acomp_master_bind(struct device *dev, - struct drm_audio_component *acomp) -{ - generic_acomp_notifier_set(acomp, true); - return 0; -} - -static void generic_acomp_master_unbind(struct device *dev, - struct drm_audio_component *acomp) -{ - generic_acomp_notifier_set(acomp, false); -} - -/* check whether both HD-audio and DRM PCI devices belong to the same bus */ -static int match_bound_vga(struct device *dev, int subtype, void *data) -{ - struct hdac_bus *bus = data; - struct pci_dev *pci, *master; - - if (!dev_is_pci(dev) || !dev_is_pci(bus->dev)) - return 0; - master = to_pci_dev(bus->dev); - pci = to_pci_dev(dev); - return master->bus == pci->bus; -} - -/* audio component notifier for AMD/Nvidia HDMI codecs */ -static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) -{ - struct hda_codec *codec = audio_ptr; - struct hdmi_spec *spec = codec->spec; - hda_nid_t pin_nid = spec->port2pin(codec, port); - - if (!pin_nid) - return; - if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN) - return; - /* skip notification during system suspend (but not in runtime PM); - * the state will be updated at resume - */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) - return; - - check_presence_and_report(codec, pin_nid, dev_id); -} - -/* set up the private drm_audio_ops from the template */ -static void setup_drm_audio_ops(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops) -{ - struct hdmi_spec *spec = codec->spec; - - spec->drm_audio_ops.audio_ptr = codec; - /* intel_audio_codec_enable() or intel_audio_codec_disable() - * will call pin_eld_notify with using audio_ptr pointer - * We need make sure audio_ptr is really setup - */ - wmb(); - spec->drm_audio_ops.pin2port = ops->pin2port; - spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify; - spec->drm_audio_ops.master_bind = ops->master_bind; - spec->drm_audio_ops.master_unbind = ops->master_unbind; -} - -/* initialize the generic HDMI audio component */ -static void generic_acomp_init(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops, - int (*port2pin)(struct hda_codec *, int)) -{ - struct hdmi_spec *spec = codec->spec; - - if (!enable_acomp) { - codec_info(codec, "audio component disabled by module option\n"); - return; - } - - spec->port2pin = port2pin; - setup_drm_audio_ops(codec, ops); - if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, - match_bound_vga, 0)) { - spec->acomp_registered = true; - } -} - -/* - * Intel codec parsers and helpers - */ - -#define INTEL_GET_VENDOR_VERB 0xf81 -#define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ -#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ - -static void intel_haswell_enable_all_pins(struct hda_codec *codec, - bool update_tree) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) - return; - - vendor_param |= INTEL_EN_ALL_PIN_CVTS; - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); - if (vendor_param == -1) - return; - - if (update_tree) - snd_hda_codec_update_widgets(codec); -} - -static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) - return; - - /* enable DP1.2 mode */ - vendor_param |= INTEL_EN_DP12; - snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); - snd_hda_codec_write_cache(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); -} - -/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. - * Otherwise you may get severe h/w communication errors. - */ -static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D0) { - intel_haswell_enable_all_pins(codec, false); - intel_haswell_fixup_enable_dp12(codec); - } - - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); -} - -/* There is a fixed mapping between audio pin node and display port. - * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: - * Pin Widget 5 - PORT B (port = 1 in i915 driver) - * Pin Widget 6 - PORT C (port = 2 in i915 driver) - * Pin Widget 7 - PORT D (port = 3 in i915 driver) - * - * on VLV, ILK: - * Pin Widget 4 - PORT B (port = 1 in i915 driver) - * Pin Widget 5 - PORT C (port = 2 in i915 driver) - * Pin Widget 6 - PORT D (port = 3 in i915 driver) - */ -static int intel_base_nid(struct hda_codec *codec) -{ - switch (codec->core.vendor_id) { - case 0x80860054: /* ILK */ - case 0x80862804: /* ILK */ - case 0x80862882: /* VLV */ - return 4; - default: - return 5; - } -} - -static int intel_pin2port(void *audio_ptr, int pin_nid) -{ - struct hda_codec *codec = audio_ptr; - struct hdmi_spec *spec = codec->spec; - int base_nid, i; - - if (!spec->port_num) { - base_nid = intel_base_nid(codec); - if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) - return -1; - return pin_nid - base_nid + 1; - } - - /* - * looking for the pin number in the mapping table and return - * the index which indicate the port number - */ - for (i = 0; i < spec->port_num; i++) { - if (pin_nid == spec->port_map[i]) - return i; - } - - codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); - return -1; -} - -static int intel_port2pin(struct hda_codec *codec, int port) -{ - struct hdmi_spec *spec = codec->spec; - - if (!spec->port_num) { - /* we assume only from port-B to port-D */ - if (port < 1 || port > 3) - return 0; - return port + intel_base_nid(codec) - 1; - } - - if (port < 0 || port >= spec->port_num) - return 0; - return spec->port_map[port]; -} - -static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) -{ - struct hda_codec *codec = audio_ptr; - int pin_nid; - int dev_id = pipe; - - pin_nid = intel_port2pin(codec, port); - if (!pin_nid) - return; - /* skip notification during system suspend (but not in runtime PM); - * the state will be updated at resume - */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) - return; - - snd_hdac_i915_set_bclk(&codec->bus->core); - check_presence_and_report(codec, pin_nid, dev_id); -} - -static const struct drm_audio_component_audio_ops intel_audio_ops = { - .pin2port = intel_pin2port, - .pin_eld_notify = intel_pin_eld_notify, -}; - -/* register i915 component pin_eld_notify callback */ -static void register_i915_notifier(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - spec->use_acomp_notifier = true; - spec->port2pin = intel_port2pin; - setup_drm_audio_ops(codec, &intel_audio_ops); - snd_hdac_acomp_register_notifier(&codec->bus->core, - &spec->drm_audio_ops); - /* no need for forcible resume for jack check thanks to notifier */ - codec->relaxed_resume = 1; -} - -/* setup_stream ops override for HSW+ */ -static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); - struct hdmi_spec_per_pin *per_pin; - int res; - - if (pin_idx < 0) - per_pin = NULL; - else - per_pin = get_pin(spec, pin_idx); - - haswell_verify_D0(codec, cvt_nid, pin_nid); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - - res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - usleep_range(100, 200); - silent_stream_set_kae(codec, per_pin, true); - } - - return res; -} - -/* pin_cvt_fixup ops override for HSW+ and VLV+ */ -static void i915_pin_cvt_fixup(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid) -{ - if (per_pin) { - haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid); - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, - per_pin->dev_id, per_pin->mux_idx); - } else { - intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid); - } -} - -static int i915_adlp_hdmi_suspend(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - bool silent_streams = false; - int pin_idx, res; - - res = generic_hdmi_suspend(codec); - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - if (per_pin->silent_stream) { - silent_streams = true; - break; - } - } - - if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) { - /* - * stream-id should remain programmed when codec goes - * to runtime suspend - */ - codec->no_stream_clean_at_suspend = 1; - - /* - * the system might go to S3, in which case keep-alive - * must be reprogrammed upon resume - */ - codec->forced_resume = 1; - - codec_dbg(codec, "HDMI: KAE active at suspend\n"); - } else { - codec->no_stream_clean_at_suspend = 0; - codec->forced_resume = 0; - } - - return res; -} - -static int i915_adlp_hdmi_resume(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, res; - - res = generic_hdmi_resume(codec); - - /* KAE not programmed at suspend, nothing to do here */ - if (!codec->no_stream_clean_at_suspend) - return res; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* - * If system was in suspend with monitor connected, - * the codec setting may have been lost. Re-enable - * keep-alive. - */ - if (per_pin->silent_stream) { - unsigned int param; - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_CONV, 0); - if (!param) { - codec_dbg(codec, "HDMI: KAE: restore stream id\n"); - silent_stream_enable_i915(codec, per_pin); - } - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - if (!(param & (AC_DIG3_KAE << 16))) { - codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); - silent_stream_set_kae(codec, per_pin, true); - } - } - } - - return res; -} - -/* precondition and allocation for Intel codecs */ -static int alloc_intel_hdmi(struct hda_codec *codec) -{ - int err; - - /* requires i915 binding */ - if (!codec->bus->core.audio_component) { - codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); - /* set probe_id here to prevent generic fallback binding */ - codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; - return -ENODEV; - } - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - /* no need to handle unsol events */ - codec->patch_ops.unsol_event = NULL; - return 0; -} - -/* parse and post-process for Intel codecs */ -static int parse_intel_hdmi(struct hda_codec *codec) -{ - int err, retries = 3; - - do { - err = hdmi_parse_codec(codec); - } while (err < 0 && retries--); - - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; -} - -/* Intel Haswell and onwards; audio component with eld notifier */ -static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, - const int *port_map, int port_num, int dev_num, - bool send_silent_stream) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - codec->dp_mst = true; - spec->vendor_nid = vendor_nid; - spec->port_map = port_map; - spec->port_num = port_num; - spec->intel_hsw_fixup = true; - spec->dev_num = dev_num; - - intel_haswell_enable_all_pins(codec, true); - intel_haswell_fixup_enable_dp12(codec); - - codec->display_power_control = 1; - - codec->patch_ops.set_power_state = haswell_set_power_state; - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.setup_stream = i915_hsw_setup_stream; - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - /* - * Enable silent stream feature, if it is enabled via - * module param or Kconfig option - */ - if (send_silent_stream) - spec->silent_stream_type = SILENT_STREAM_I915; - - return parse_intel_hdmi(codec); -} - -static int patch_i915_hsw_hdmi(struct hda_codec *codec) -{ - return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, - enable_silent_stream); -} - -static int patch_i915_glk_hdmi(struct hda_codec *codec) -{ - /* - * Silent stream calls audio component .get_power() from - * .pin_eld_notify(). On GLK this will deadlock in i915 due - * to the audio vs. CDCLK workaround. - */ - return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); -} - -static int patch_i915_icl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, - enable_silent_stream); -} - -static int patch_i915_tgl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, - enable_silent_stream); -} - -static int patch_i915_adlp_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int res; - - res = patch_i915_tgl_hdmi(codec); - if (!res) { - spec = codec->spec; - - if (spec->silent_stream_type) { - spec->silent_stream_type = SILENT_STREAM_KAE; - - codec->patch_ops.resume = i915_adlp_hdmi_resume; - codec->patch_ops.suspend = i915_adlp_hdmi_suspend; - } - } - - return res; -} - -/* Intel Baytrail and Braswell; with eld notifier */ -static int patch_i915_byt_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - - /* For Valleyview/Cherryview, only the display codec is in the display - * power well and can use link_power ops to request/release the power. - */ - codec->display_power_control = 1; - - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - return parse_intel_hdmi(codec); -} - -/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ -static int patch_i915_cpt_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - return parse_intel_hdmi(codec); -} - -/* - * Shared non-generic implementations - */ - -static int simple_playback_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - unsigned int chans; - struct hda_pcm_stream *pstr; - struct hdmi_spec_per_cvt *per_cvt; - - per_cvt = get_cvt(spec, 0); - chans = get_wcaps(codec, per_cvt->cvt_nid); - chans = get_wcaps_channels(chans); - - info = snd_hda_codec_pcm_new(codec, "HDMI 0"); - if (!info) - return -ENOMEM; - spec->pcm_rec[0].pcm = info; - info->pcm_type = HDA_PCM_TYPE_HDMI; - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - *pstr = spec->pcm_playback; - pstr->nid = per_cvt->cvt_nid; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; - - return 0; -} - -/* unsolicited event for jack sensing */ -static void simple_hdmi_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_report_sync(codec); -} - -/* generic_hdmi_build_jack can be used for simple_hdmi, too, - * as long as spec->pins[] is set correctly - */ -#define simple_hdmi_build_jack generic_hdmi_build_jack - -static int simple_playback_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int err; - - per_cvt = get_cvt(spec, 0); - err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid, - per_cvt->cvt_nid, - HDA_PCM_TYPE_HDMI); - if (err < 0) - return err; - return simple_hdmi_build_jack(codec, 0); -} - -static int simple_playback_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); - hda_nid_t pin = per_pin->pin_nid; - - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* some codecs require to unmute the pin */ - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id); - return 0; -} - -static void simple_playback_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - hdmi_array_free(spec); - kfree(spec); -} - -/* - * Nvidia specific implementations - */ - -#define Nv_VERB_SET_Channel_Allocation 0xF79 -#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A -#define Nv_VERB_SET_Audio_Protection_On 0xF98 -#define Nv_VERB_SET_Audio_Protection_Off 0xF99 - -#define nvhdmi_master_con_nid_7x 0x04 -#define nvhdmi_master_pin_nid_7x 0x05 - -static const hda_nid_t nvhdmi_con_nids_7x[4] = { - /*front, rear, clfe, rear_surr */ - 0x6, 0x8, 0xa, 0xc, -}; - -static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -#ifdef LIMITED_RATE_FMT_SUPPORT -/* support only the safe format and rate */ -#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 -#define SUPPORTED_MAXBPS 16 -#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE -#else -/* support all rates and formats */ -#define SUPPORTED_RATES \ - (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ - SNDRV_PCM_RATE_192000) -#define SUPPORTED_MAXBPS 24 -#define SUPPORTED_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) -#endif - -static int nvhdmi_7x_init_2ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); - return 0; -} - -static int nvhdmi_7x_init_8ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); - return 0; -} - -static const unsigned int channels_2_6_8[] = { - 2, 6, 8 -}; - -static const unsigned int channels_2_8[] = { - 2, 8 -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { - .count = ARRAY_SIZE(channels_2_6_8), - .list = channels_2_6_8, - .mask = 0, -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { - .count = ARRAY_SIZE(channels_2_8), - .list = channels_2_8, - .mask = 0, -}; - -static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - const struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; - - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - hw_constraints_channels = &hw_constraints_2_8_channels; - break; - case 0x10de0007: - hw_constraints_channels = &hw_constraints_2_6_8_channels; - break; - default: - break; - } - - if (hw_constraints_channels != NULL) { - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - hw_constraints_channels); - } else { - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - } - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static const struct hda_pcm_stream simple_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = simple_playback_pcm_open, - .close = simple_playback_pcm_close, - .prepare = simple_playback_pcm_prepare - }, -}; - -static const struct hda_codec_ops simple_hdmi_patch_ops = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = simple_playback_init, - .free = simple_playback_free, - .unsol_event = simple_hdmi_unsol_event, -}; - -static int patch_simple_hdmi(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - spec->codec = codec; - codec->spec = spec; - hdmi_array_init(spec, 1); - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = cvt_nid; - spec->num_cvts = 1; - spec->num_pins = 1; - per_pin = snd_array_new(&spec->pins); - per_cvt = snd_array_new(&spec->cvts); - if (!per_pin || !per_cvt) { - simple_playback_free(codec); - return -ENOMEM; - } - per_cvt->cvt_nid = cvt_nid; - per_pin->pin_nid = pin_nid; - spec->pcm_playback = simple_pcm_playback; - - codec->patch_ops = simple_hdmi_patch_ops; - - return 0; -} - -static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, - int channels) -{ - unsigned int chanmask; - int chan = channels ? (channels - 1) : 1; - - switch (channels) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } - - /* Set the audio infoframe channel allocation and checksum fields. The - * channel count is computed implicitly by the hardware. */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); -} - -static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, - 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - for (i = 0; i < 4; i++) { - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_STREAM_FORMAT, 0); - } - - /* The audio hardware sends a channel count of 0x7 (8ch) when all the - * streams are disabled. */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int chs; - unsigned int dataDCC2, channel_id; - int i; - struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif; - struct hdmi_spec_per_cvt *per_cvt; - - mutex_lock(&codec->spdif_mutex); - per_cvt = get_cvt(spec, 0); - spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); - - chs = substream->runtime->channels; - - dataDCC2 = 0x2; - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); - - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_STREAM_FORMAT, format); - - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - - for (i = 0; i < 4; i++) { - if (chs == 2) - channel_id = 0; - else - channel_id = i * 2; - - /* turn off SPDIF once; - *otherwise the IEC958 bits won't be updated - */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - /* set the stream id */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_CHANNEL_STREAMID, - (stream_tag << 4) | channel_id); - /* set the stream format */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_STREAM_FORMAT, - format); - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - } - - nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); - - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = simple_playback_pcm_open, - .close = nvhdmi_8ch_7x_pcm_close, - .prepare = nvhdmi_8ch_7x_pcm_prepare - }, -}; - -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x, - nvhdmi_master_pin_nid_7x); - if (err < 0) - return err; - - codec->patch_ops.init = nvhdmi_7x_init_2ch; - /* override the PCM rates, etc, as the codec doesn't give full list */ - spec = codec->spec; - spec->pcm_playback.rates = SUPPORTED_RATES; - spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; - spec->pcm_playback.formats = SUPPORTED_FORMATS; - spec->nv_dp_workaround = true; - return 0; -} - -static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err = simple_playback_build_pcms(codec); - if (!err) { - struct hda_pcm *info = get_pcm_rec(spec, 0); - info->own_chmap = true; - } - return err; -} - -static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - struct snd_pcm_chmap *chmap; - int err; - - err = simple_playback_build_controls(codec); - if (err < 0) - return err; - - /* add channel maps */ - info = get_pcm_rec(spec, 0); - err = snd_pcm_add_chmap_ctls(info->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - snd_pcm_alt_chmaps, 8, 0, &chmap); - if (err < 0) - return err; - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - chmap->channel_mask = (1U << 2) | (1U << 8); - break; - case 0x10de0007: - chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); - } - return 0; -} - -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_nvhdmi_2ch(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->multiout.max_channels = 8; - spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; - codec->patch_ops.init = nvhdmi_7x_init_8ch; - codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; - codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; - - /* Initialize the audio infoframe channel mask and checksum to something - * valid */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return 0; -} - -/* - * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: - * - 0x10de0015 - * - 0x10de0040 - */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, int channels) -{ - if (cap->ca_index == 0x00 && channels == 2) - return SNDRV_CTL_TLVT_CHMAP_FIXED; - - /* If the speaker allocation matches the channel count, it is OK. */ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) - return -EINVAL; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */ -static int nvhdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid - 4; -} - -/* reverse-map from port to pin NID: see above */ -static int nvhdmi_port2pin(struct hda_codec *codec, int port) -{ - return port + 4; -} - -static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { - .pin2port = nvhdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_nvhdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - codec->dp_mst = true; - - spec = codec->spec; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin); - - return 0; -} - -static int patch_nvhdmi_legacy(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = patch_generic_hdmi(codec); - if (err) - return err; - - spec = codec->spec; - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - return 0; -} - -/* - * The HDA codec on NVIDIA Tegra contains two scratch registers that are - * accessed using vendor-defined verbs. These registers can be used for - * interoperability between the HDA and HDMI drivers. - */ - -/* Audio Function Group node */ -#define NVIDIA_AFG_NID 0x01 - -/* - * The SCRATCH0 register is used to notify the HDMI codec of changes in audio - * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to - * be raised in the HDMI codec. The remainder of the bits is arbitrary. This - * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an - * additional bit (at position 30) to signal the validity of the format. - * - * | 31 | 30 | 29 16 | 15 0 | - * +---------+-------+--------+--------+ - * | TRIGGER | VALID | UNUSED | FORMAT | - * +-----------------------------------| - * - * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). The trigger bit is not applicable from - * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt - * trigger to hdmi. - */ -#define NVIDIA_SET_HOST_INTR 0xf80 -#define NVIDIA_GET_SCRATCH0 0xfa6 -#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 -#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 -#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 -#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa -#define NVIDIA_SCRATCH_TRIGGER (1 << 7) -#define NVIDIA_SCRATCH_VALID (1 << 6) - -#define NVIDIA_GET_SCRATCH1 0xfab -#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac -#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad -#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae -#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf - -/* - * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, - * the format is invalidated so that the HDMI codec can be disabled. - */ -static void tegra_hdmi_set_format(struct hda_codec *codec, - hda_nid_t cvt_nid, - unsigned int format) -{ - unsigned int value; - unsigned int nid = NVIDIA_AFG_NID; - struct hdmi_spec *spec = codec->spec; - - /* - * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. - * This resulted in moving scratch registers from audio function - * group to converter widget context. So CVT NID should be used for - * scratch register read/write for DP MST supported Tegra HDA codec. - */ - if (codec->dp_mst) - nid = cvt_nid; - - /* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, nid, 0, - NVIDIA_GET_SCRATCH0, 0); - value = (value >> 24) & 0xff; - - /* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE0, - (format >> 0) & 0xff); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE1, - (format >> 8) & 0xff); - - /* bits [16:24] are unused */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE2, 0); - - /* - * Bit 30 signals that the data is valid and hence that HDMI audio can - * be enabled. - */ - if (format == 0) - value &= ~NVIDIA_SCRATCH_VALID; - else - value |= NVIDIA_SCRATCH_VALID; - - if (spec->hdmi_intr_trig_ctrl) { - /* - * For Tegra HDA Codec design from TEGRA234 onwards, the - * Interrupt to hdmi driver is triggered by writing - * non-zero values to verb 0xF80 instead of 31st bit of - * scratch register. - */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_HOST_INTR, 0x1); - } else { - /* - * Whenever the 31st trigger bit is toggled, an interrupt is raised - * in the HDMI codec. The HDMI driver will use that as trigger - * to update its configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; - - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - } -} - -static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int err; - - err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, - format, substream); - if (err < 0) - return err; - - /* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, hinfo->nid, format); - - return 0; -} - -static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, hinfo->nid, 0); - - return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); -} - -static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int i; - - for (i = 0; i < spec->num_pins; i++) { - struct hda_pcm *pcm = get_pcm_rec(spec, i); - - if (pcm->pcm_type == type) - return pcm; - } - - return NULL; -} - -static int tegra_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hda_pcm_stream *stream; - struct hda_pcm *pcm; - int err; - - err = generic_hdmi_build_pcms(codec); - if (err < 0) - return err; - - pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); - if (!pcm) - return -ENODEV; - - /* - * Override ->prepare() and ->cleanup() operations to notify the HDMI - * codec about format changes. - */ - stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; - stream->ops.prepare = tegra_hdmi_pcm_prepare; - stream->ops.cleanup = tegra_hdmi_pcm_cleanup; - - return 0; -} - -static int tegra_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i, err; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - for (i = 0; i < spec->num_cvts; i++) - snd_hda_codec_write(codec, spec->cvt_nids[i], 0, - AC_VERB_SET_DIGI_CONVERT_1, - AC_DIG1_ENABLE); - - generic_hdmi_init_per_pins(codec); - - codec->depop_delay = 10; - codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - return 0; -} - -static int patch_tegra_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - return tegra_hdmi_init(codec); -} - -static int patch_tegra234_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - codec->dp_mst = true; - spec = codec->spec; - spec->dyn_pin_out = true; - spec->hdmi_intr_trig_ctrl = true; - - return tegra_hdmi_init(codec); -} - -/* - * ATI/AMD-specific implementations - */ - -#define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->core.vendor_id == 0x1002aa01 && \ - ((codec)->core.revision_id & 0xff00) >= 0x0300) -#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) - -/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ -#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 -#define ATI_VERB_SET_DOWNMIX_INFO 0x772 -#define ATI_VERB_SET_MULTICHANNEL_01 0x777 -#define ATI_VERB_SET_MULTICHANNEL_23 0x778 -#define ATI_VERB_SET_MULTICHANNEL_45 0x779 -#define ATI_VERB_SET_MULTICHANNEL_67 0x77a -#define ATI_VERB_SET_HBR_CONTROL 0x77c -#define ATI_VERB_SET_MULTICHANNEL_1 0x785 -#define ATI_VERB_SET_MULTICHANNEL_3 0x786 -#define ATI_VERB_SET_MULTICHANNEL_5 0x787 -#define ATI_VERB_SET_MULTICHANNEL_7 0x788 -#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 -#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 -#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 -#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 -#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 -#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 -#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a -#define ATI_VERB_GET_HBR_CONTROL 0xf7c -#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 -#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 -#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 -#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 -#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 - -/* AMD specific HDA cvt verbs */ -#define ATI_VERB_SET_RAMP_RATE 0x770 -#define ATI_VERB_GET_RAMP_RATE 0xf70 - -#define ATI_OUT_ENABLE 0x1 - -#define ATI_MULTICHANNEL_MODE_PAIRED 0 -#define ATI_MULTICHANNEL_MODE_SINGLE 1 - -#define ATI_HBR_CAPABLE 0x01 -#define ATI_HBR_ENABLE 0x10 - -static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, - int dev_id, unsigned char *buf, int *eld_size) -{ - WARN_ON(dev_id != 0); - /* call hda_eld.c ATI/AMD-specific function */ - return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size, - is_amdhdmi_rev3_or_later(codec)); -} - -static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, int ca, - int active_channels, int conn_type) -{ - WARN_ON(dev_id != 0); - snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); -} - -static int atihdmi_paired_swap_fc_lfe(int pos) -{ - /* - * ATI/AMD have automatic FC/LFE swap built-in - * when in pairwise mapping mode. - */ - - switch (pos) { - /* see channel_allocations[].speakers[] */ - case 2: return 3; - case 3: return 2; - default: break; - } - - return pos; -} - -static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - struct hdac_cea_channel_speaker_allocation *cap; - int i, j; - - /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - - cap = snd_hdac_get_ch_alloc_from_ca(ca); - for (i = 0; i < chs; ++i) { - int mask = snd_hdac_chmap_to_spk_mask(map[i]); - bool ok = false; - bool companion_ok = false; - - if (!mask) - continue; - - for (j = 0 + i % 2; j < 8; j += 2) { - int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); - if (cap->speakers[chan_idx] == mask) { - /* channel is in a supported position */ - ok = true; - - if (i % 2 == 0 && i + 1 < chs) { - /* even channel, check the odd companion */ - int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); - int comp_mask_act = cap->speakers[comp_chan_idx]; - - if (comp_mask_req == comp_mask_act) - companion_ok = true; - else - return -EINVAL; - } - break; - } - } - - if (!ok) - return -EINVAL; - - if (companion_ok) - i++; /* companion channel already checked */ - } - - return 0; -} - -static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int hdmi_slot, int stream_channel) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - int verb; - int ati_channel_setup = 0; - - if (hdmi_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); - - /* In case this is an odd slot but without stream channel, do not - * disable the slot since the corresponding even slot could have a - * channel. In case neither have a channel, the slot pair will be - * disabled when this function is called for the even slot. */ - if (hdmi_slot % 2 != 0 && stream_channel == 0xf) - return 0; - - hdmi_slot -= hdmi_slot % 2; - - if (stream_channel != 0xf) - stream_channel -= stream_channel % 2; - } - - verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; - - /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ - - if (stream_channel != 0xf) - ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; - - return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); -} - -static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int asp_slot) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - bool was_odd = false; - int ati_asp_slot = asp_slot; - int verb; - int ati_channel_setup; - - if (asp_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); - if (ati_asp_slot % 2 != 0) { - ati_asp_slot -= 1; - was_odd = true; - } - } - - verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; - - ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); - - if (!(ati_channel_setup & ATI_OUT_ENABLE)) - return 0xf; - - return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; -} - -static int atihdmi_paired_chmap_cea_alloc_validate_get_type( - struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, - int channels) -{ - int c; - - /* - * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so - * we need to take that into account (a single channel may take 2 - * channel slots if we need to carry a silent channel next to it). - * On Rev3+ AMD codecs this function is not used. - */ - int chanpairs = 0; - - /* We only produce even-numbered channel count TLVs */ - if ((channels % 2) != 0) - return -1; - - for (c = 0; c < 7; c += 2) { - if (cap->speakers[c] || cap->speakers[c+1]) - chanpairs++; - } - - if (chanpairs * 2 != channels) - return -1; - - return SNDRV_CTL_TLVT_CHMAP_PAIRED; -} - -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, - struct hdac_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - /* produce paired maps for pre-rev3 ATI/AMD codecs */ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); - int spk = cap->speakers[chan]; - if (!spk) { - /* add N/A channel if the companion channel is occupied */ - if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) - chmap[count++] = SNDRV_CHMAP_NA; - - continue; - } - - chmap[count++] = snd_hdac_spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - -static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr) -{ - int hbr_ctl, hbr_ctl_new; - - WARN_ON(dev_id != 0); - - hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); - if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) { - if (hbr) - hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; - else - hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; - - codec_dbg(codec, - "atihdmi_pin_hbr_setup: NID=0x%x, %shbr-ctl=0x%x\n", - pin_nid, - hbr_ctl == hbr_ctl_new ? "" : "new-", - hbr_ctl_new); - - if (hbr_ctl != hbr_ctl_new) - snd_hda_codec_write(codec, pin_nid, 0, - ATI_VERB_SET_HBR_CONTROL, - hbr_ctl_new); - - } else if (hbr) - return -EINVAL; - - return 0; -} - -static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, - u32 stream_tag, int format) -{ - if (is_amdhdmi_rev3_or_later(codec)) { - int ramp_rate = 180; /* default as per AMD spec */ - /* disable ramp-up/down for non-pcm as per AMD spec */ - if (format & AC_FMT_TYPE_NON_PCM) - ramp_rate = 0; - - snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); - } - - return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); -} - - -static int atihdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, err; - - err = generic_hdmi_init(codec); - - if (err) - return err; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* make sure downmix information in infoframe is zero */ - snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); - - /* enable channel-wise remap mode if supported */ - if (has_amd_full_remap_support(codec)) - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - ATI_VERB_SET_MULTICHANNEL_MODE, - ATI_MULTICHANNEL_MODE_SINGLE); - } - codec->auto_runtime_pm = 1; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ -static int atihdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid / 2 - 1; -} - -/* reverse-map from port to pin NID: see above */ -static int atihdmi_port2pin(struct hda_codec *codec, int port) -{ - return port * 2 + 3; -} - -static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { - .pin2port = atihdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_atihdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - int err, cvt_idx; - - err = patch_generic_hdmi(codec); - - if (err) - return err; - - codec->patch_ops.init = atihdmi_init; - - spec = codec->spec; - - spec->static_pcm_mapping = true; - - spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; - spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; - spec->ops.setup_stream = atihdmi_setup_stream; - - spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; - - if (!has_amd_full_remap_support(codec)) { - /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = - atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; - } - - /* ATI/AMD converters do not advertise all of their capabilities */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->channels_max = max(per_cvt->channels_max, 8u); - per_cvt->rates |= SUPPORTED_RATES; - per_cvt->formats |= SUPPORTED_FORMATS; - per_cvt->maxbps = max(per_cvt->maxbps, 24u); - } - - spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); - - /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing - * the link-down as is. Tell the core to allow it. - */ - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin); - - return 0; -} - -/* VIA HDMI Implementation */ -#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ -#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ - -static int patch_via_hdmi(struct hda_codec *codec) -{ - return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); -} - -static int patch_gf_hdmi(struct hda_codec *codec) -{ - int err; - - err = patch_generic_hdmi(codec); - if (err) - return err; - - /* - * Glenfly GPUs have two codecs, stream switches from one codec to - * another, need to do actual clean-ups in codec_cleanup_stream - */ - codec->no_sticky_stream = 1; - return 0; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_hdmi[] = { -HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy), -/* 17 is known to be absent */ -HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0034, "Tegra264 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1d179f86, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f87, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f88, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f89, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8a, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8b, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8c, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8d, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8e, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8f, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f90, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862823, "Wildcat Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), -/* special ID for generic HDMI */ -HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), -{} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HDMI HD-audio codec"); -MODULE_ALIAS("snd-hda-codec-intelhdmi"); -MODULE_ALIAS("snd-hda-codec-nvhdmi"); -MODULE_ALIAS("snd-hda-codec-atihdmi"); - -static struct hda_codec_driver hdmi_driver = { - .id = snd_hda_id_hdmi, -}; - -module_hda_codec_driver(hdmi_driver); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index a8ac14887676..1aefd46ebf6b 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -850,7 +850,7 @@ static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer"); + strscpy(pcm->name, "ICE1712 consumer"); ice->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -875,7 +875,7 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer (DS)"); + strscpy(pcm->name, "ICE1712 consumer (DS)"); ice->pcm_ds = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1216,7 +1216,7 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 multi"); + strscpy(pcm->name, "ICE1712 multi"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -2559,8 +2559,8 @@ static int snd_ice1712_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1712"); - strcpy(card->shortname, "ICEnsemble ICE1712"); + strscpy(card->driver, "ICE1712"); + strscpy(card->shortname, "ICEnsemble ICE1712"); err = snd_ice1712_create(card, pci, model[dev], omni[dev], cs8427_timeout[dev], dxr_enable[dev]); @@ -2570,9 +2570,9 @@ static int snd_ice1712_probe(struct pci_dev *pci, for (tbl = card_tables; *tbl; tbl++) { for (c = *tbl; c->subvendor; c++) { if (c->subvendor == ice->eeprom.subvendor) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index be22b159e65a..0445d2e8e548 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1118,7 +1118,7 @@ static int snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724"); + strscpy(pcm->name, "ICE1724"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1313,7 +1313,7 @@ static int snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1425,7 +1425,7 @@ static int snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724 Surround PCM"); + strscpy(pcm->name, "ICE1724 Surround PCM"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1820,7 +1820,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; if (uinfo->value.enumerated.item >= hw_rates_count) /* ext_clock items */ - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ice->ext_clock_names[ uinfo->value.enumerated.item - hw_rates_count]); else @@ -2545,8 +2545,8 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1724"); - strcpy(card->shortname, "ICEnsemble ICE1724"); + strscpy(card->driver, "ICE1724"); + strscpy(card->shortname, "ICEnsemble ICE1724"); err = snd_vt1724_create(card, pci, model[dev]); if (err < 0) @@ -2557,9 +2557,9 @@ static int __snd_vt1724_probe(struct pci_dev *pci, c = ice->card_info; if (c) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) @@ -2637,7 +2637,7 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice->rmidi[0] = rmidi; rmidi->private_data = ice; - strcpy(rmidi->name, "ICE1724 MIDI"); + strscpy(rmidi->name, "ICE1724 MIDI"); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 51e7f1f1a48e..9e6a5065ffbf 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1436,7 +1436,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -1453,7 +1453,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip), @@ -2249,7 +2249,7 @@ static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT; for (i = 1; i < 4; i++) { if (pcm->r[0].codec[i]) { - tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT; + tmp |= chip->ac97_sdin[pcm->r[0].codec[i]->num] << ICH_DI2L_SHIFT; break; } } @@ -3118,21 +3118,21 @@ static int __snd_intel8x0_probe(struct pci_dev *pci, if (spdif_aclink < 0) spdif_aclink = check_default_spdif_aclink(pci); - strcpy(card->driver, "ICH"); + strscpy(card->driver, "ICH"); if (!spdif_aclink) { switch (pci_id->driver_data) { case DEVICE_NFORCE: - strcpy(card->driver, "NFORCE"); + strscpy(card->driver, "NFORCE"); break; case DEVICE_INTEL_ICH4: - strcpy(card->driver, "ICH4"); + strscpy(card->driver, "ICH4"); } } - strcpy(card->shortname, "Intel ICH"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 1ce775fe8a70..9e5988583abe 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -678,7 +678,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -696,7 +696,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1184,11 +1184,11 @@ static int __snd_intel8x0m_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ICH-MODEM"); - strcpy(card->shortname, "Intel ICH"); + strscpy(card->driver, "ICH-MODEM"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 56dea5b10828..0a66d5cfc090 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2249,7 +2249,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->pcm->private_data = korg1212; korg1212->pcm->private_free = snd_korg1212_free_pcm; - strcpy(korg1212->pcm->name, "korg1212"); + strscpy(korg1212->pcm->name, "korg1212"); snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); @@ -2298,8 +2298,8 @@ snd_korg1212_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "korg1212"); - strcpy(card->shortname, "korg1212"); + strscpy(card->driver, "korg1212"); + strscpy(card->shortname, "korg1212"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, korg1212->iomem, korg1212->irq); diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index fb8bd54e4c2d..8d927ecba165 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -630,12 +630,12 @@ static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev) if (err < 0) return err; - strcpy(card->driver, "Lola"); + strscpy(card->driver, "Lola"); strscpy(card->shortname, "Digigram Lola", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, chip->bar[0].addr, chip->irq); - strcpy(card->mixername, card->shortname); + strscpy(card->mixername, card->shortname); lola_irq_enable(chip); diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 63ebf9803ea8..9f12c936bb1f 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -814,7 +814,7 @@ static int lx_pcm_create(struct lx6464es *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, card_name); + strscpy(pcm->name, card_name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, size, size); @@ -1022,7 +1022,7 @@ static int snd_lx6464es_probe(struct pci_dev *pci, goto error; } - strcpy(card->driver, "LX6464ES"); + strscpy(card->driver, "LX6464ES"); sprintf(card->id, "LX6464ES_%02X%02X%02X", chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index e61e15774706..e092097599ff 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1846,7 +1846,7 @@ snd_m3_pcm(struct snd_m3 * chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->driver); + strscpy(pcm->name, chip->card->driver); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2648,14 +2648,14 @@ __snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: case PCI_DEVICE_ID_ESS_ALLEGRO_1: - strcpy(card->driver, "Allegro"); + strscpy(card->driver, "Allegro"); break; case PCI_DEVICE_ID_ESS_CANYON3D_2LE: case PCI_DEVICE_ID_ESS_CANYON3D_2: - strcpy(card->driver, "Canyon3D-2"); + strscpy(card->driver, "Canyon3D-2"); break; default: - strcpy(card->driver, "Maestro3"); + strscpy(card->driver, "Maestro3"); break; } diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7ceaf6a7a77e..cdc0ba5dd1ad 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -970,7 +970,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1004,7 +1004,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1330,7 +1330,7 @@ static int snd_mixart_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, CARD_NAME); + strscpy(card->driver, CARD_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram miXart [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index cd4dc43dbff1..39464d171f6b 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1595,13 +1595,13 @@ static int snd_nm256_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: - strcpy(card->driver, "NM256AV"); + strscpy(card->driver, "NM256AV"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: - strcpy(card->driver, "NM256ZX"); + strscpy(card->driver, "NM256ZX"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: - strcpy(card->driver, "NM256XL+"); + strscpy(card->driver, "NM256XL+"); break; default: dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 39b8ccf37cdd..9c7270e4c35e 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -655,11 +655,11 @@ static int __oxygen_pci_probe(struct pci_dev *pci, int index, char *id, chip->irq = pci->irq; card->sync_irq = chip->irq; - strcpy(card->driver, chip->model.chip); - strcpy(card->shortname, chip->model.shortname); + strscpy(card->driver, chip->model.chip); + strscpy(card->shortname, chip->model.shortname); sprintf(card->longname, "%s at %#lx, irq %i", chip->model.longname, chip->addr, chip->irq); - strcpy(card->mixername, chip->model.chip); + strscpy(card->mixername, chip->model.chip); snd_component_add(card, chip->model.chip); err = oxygen_pcm_init(chip); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b2a3fcfe31d4..643141f345bb 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -697,7 +697,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, "Multichannel"); + strscpy(pcm->name, "Multichannel"); if (outs) snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, SNDRV_DMA_TYPE_DEV, @@ -725,7 +725,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_c_ops); pcm->private_data = chip; - strcpy(pcm->name, "Digital"); + strscpy(pcm->name, "Digital"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -755,7 +755,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); + strscpy(pcm->name, outs ? "Front Panel" : "Analog 2"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -773,7 +773,7 @@ int oxygen_pcm_init(struct oxygen *chip) OXYGEN_REC_C_ROUTE_I2S_ADC_3, OXYGEN_REC_C_ROUTE_MASK); pcm->private_data = chip; - strcpy(pcm->name, "Analog 3"); + strscpy(pcm->name, "Analog 3"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 242bd7e04b3e..bfd84c50e981 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1149,7 +1149,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->mgr->pci->dev, @@ -1605,7 +1605,7 @@ static int pcxhr_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 578be0755b8a..e983cd657e28 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1688,7 +1688,7 @@ static int snd_riptide_pcm(struct snd_riptide *chip, int device) &snd_riptide_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "RIPTIDE"); + strscpy(pcm->name, "RIPTIDE"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, 64 * 1024, 128 * 1024); @@ -2098,8 +2098,8 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } #endif - strcpy(card->driver, "RIPTIDE"); - strcpy(card->shortname, "Riptide"); + strscpy(card->driver, "RIPTIDE"); + strscpy(card->shortname, "Riptide"); #ifdef SUPPORT_JOYSTICK scnprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x", diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 4bf122abea48..f07b6023473a 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1314,7 +1314,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->spdif_pcm->private_data = rme32; rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; - strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); + strscpy(rme32->spdif_pcm->name, "Digi32 IEC958"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_spdif_fd_ops); @@ -1344,7 +1344,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->adat_pcm->private_data = rme32; rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; - strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); + strscpy(rme32->adat_pcm->name, "Digi32 ADAT"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_adat_fd_ops); @@ -1879,16 +1879,16 @@ __snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) return err; - strcpy(card->driver, "Digi32"); + strscpy(card->driver, "Digi32"); switch (rme32->pci->device) { case PCI_DEVICE_ID_RME_DIGI32: - strcpy(card->shortname, "RME Digi32"); + strscpy(card->shortname, "RME Digi32"); break; case PCI_DEVICE_ID_RME_DIGI32_8: - strcpy(card->shortname, "RME Digi32/8"); + strscpy(card->shortname, "RME Digi32/8"); break; case PCI_DEVICE_ID_RME_DIGI32_PRO: - strcpy(card->shortname, "RME Digi32 PRO"); + strscpy(card->shortname, "RME Digi32 PRO"); break; } sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d", diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 01029843d7f3..5cdbbe9cf994 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1607,7 +1607,7 @@ snd_rme96_create(struct rme96 *rme96) rme96->spdif_pcm->private_data = rme96; rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; - strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + strscpy(rme96->spdif_pcm->name, "Digi96 IEC958"); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); @@ -1624,7 +1624,7 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->adat_pcm->private_data = rme96; rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; - strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + strscpy(rme96->adat_pcm->name, "Digi96 ADAT"); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); @@ -2434,23 +2434,23 @@ __snd_rme96_probe(struct pci_dev *pci, return -ENOMEM; } - strcpy(card->driver, "Digi96"); + strscpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: - strcpy(card->shortname, "RME Digi96"); + strscpy(card->shortname, "RME Digi96"); break; case PCI_DEVICE_ID_RME_DIGI96_8: - strcpy(card->shortname, "RME Digi96/8"); + strscpy(card->shortname, "RME Digi96/8"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PRO: - strcpy(card->shortname, "RME Digi96/8 PRO"); + strscpy(card->shortname, "RME Digi96/8 PRO"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST: pci_read_config_byte(rme96->pci, 8, &val); if (val < 5) { - strcpy(card->shortname, "RME Digi96/8 PAD"); + strscpy(card->shortname, "RME Digi96/8 PAD"); } else { - strcpy(card->shortname, "RME Digi96/8 PST"); + strscpy(card->shortname, "RME Digi96/8 PST"); } break; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 8df0f5bba0f6..7ce73746168a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -4944,7 +4944,7 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) hdsp->hwdep = hw; hw->private_data = hdsp; - strcpy(hw->name, "HDSP hwdep interface"); + strscpy(hw->name, "HDSP hwdep interface"); hw->ops.ioctl = snd_hdsp_hwdep_ioctl; hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl; @@ -4963,7 +4963,7 @@ static int snd_hdsp_create_pcm(struct snd_card *card, struct hdsp *hdsp) hdsp->pcm = pcm; pcm->private_data = hdsp; - strcpy(pcm->name, hdsp->card_name); + strscpy(pcm->name, hdsp->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops); @@ -5111,7 +5111,7 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp } if (!(hdsp->state & HDSP_InitializationComplete)) { - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); @@ -5255,8 +5255,8 @@ static int snd_hdsp_create(struct snd_card *card, */ pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF); - strcpy(card->driver, "H-DSP"); - strcpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "H-DSP"); + strscpy(card->mixername, "Xilinx FPGA"); if (hdsp->firmware_rev < 0xa) return -ENODEV; @@ -5412,7 +5412,7 @@ static int snd_hdsp_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); err = snd_card_register(card); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2041cf00cca0..a0976824beda 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6355,7 +6355,7 @@ static int snd_hdspm_create_hwdep(struct snd_card *card, hdspm->hwdep = hw; hw->private_data = hdspm; - strcpy(hw->name, "HDSPM hwdep interface"); + strscpy(hw->name, "HDSPM hwdep interface"); hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; @@ -6412,7 +6412,7 @@ static int snd_hdspm_create_pcm(struct snd_card *card, hdspm->pcm = pcm; pcm->private_data = hdspm; - strcpy(pcm->name, hdspm->card_name); + strscpy(pcm->name, hdspm->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdspm_ops); @@ -6512,8 +6512,8 @@ static int snd_hdspm_create(struct snd_card *card, pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); - strcpy(card->mixername, "Xilinx FPGA"); - strcpy(card->driver, "HDSPM"); + strscpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "HDSPM"); switch (hdspm->firmware_rev) { case HDSPM_RAYDAT_REV: diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 34d9c7995ddd..7dc8e3777c37 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2364,7 +2364,7 @@ static int snd_rme9652_create_pcm(struct snd_card *card, rme9652->pcm = pcm; pcm->private_data = rme9652; - strcpy(pcm->name, rme9652->card_name); + strscpy(pcm->name, rme9652->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); @@ -2447,7 +2447,7 @@ static int snd_rme9652_create(struct snd_card *card, switch (rev) { case 8: /* original eprom */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9636 (Rev 1.5)"; } else { @@ -2456,17 +2456,17 @@ static int snd_rme9652_create(struct snd_card *card, rme9652->ss_channels = RME9636_NCHANNELS; break; case 9: /* W36_G EPROM */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); rme9652->card_name = "RME Digi9636 (Rev G)"; rme9652->ss_channels = RME9636_NCHANNELS; break; case 4: /* W52_G EPROM */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); rme9652->card_name = "RME Digi9652 (Rev G)"; rme9652->ss_channels = RME9652_NCHANNELS; break; case 3: /* original eprom */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9652 (Rev 1.5)"; } else { @@ -2539,7 +2539,7 @@ static int snd_rme9652_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, rme9652->card_name); + strscpy(card->shortname, rme9652->card_name); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, rme9652->port, rme9652->irq); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 42b22f123fa7..3d7abcb31679 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -868,7 +868,7 @@ static int sis_pcm_create(struct sis7019 *sis) return rc; pcm->private_data = sis; - strcpy(pcm->name, "SiS7019"); + strscpy(pcm->name, "SiS7019"); sis->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops); @@ -1348,8 +1348,8 @@ static int __snd_sis7019_probe(struct pci_dev *pci, if (rc < 0) return rc; - strcpy(card->driver, "SiS7019"); - strcpy(card->shortname, "SiS7019"); + strscpy(card->driver, "SiS7019"); + strscpy(card->shortname, "SiS7019"); rc = sis_chip_create(card, pci); if (rc) return rc; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 808a793ff4da..f85a9556dacb 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -863,7 +863,7 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device) pcm->private_data = sonic; pcm->info_flags = 0; - strcpy(pcm->name, "S3 SonicVibes"); + strscpy(pcm->name, "S3 SonicVibes"); sonic->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1091,7 +1091,7 @@ static int snd_sonicvibes_mixer(struct sonicvibes *sonic) if (snd_BUG_ON(!sonic || !sonic->card)) return -EINVAL; card = sonic->card; - strcpy(card->mixername, "S3 SonicVibes"); + strscpy(card->mixername, "S3 SonicVibes"); for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) { kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic); @@ -1415,8 +1415,8 @@ static int __snd_sonic_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "SonicVibes"); - strcpy(card->shortname, "S3 SonicVibes"); + strscpy(card->driver, "SonicVibes"); + strscpy(card->shortname, "S3 SonicVibes"); sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", card->shortname, sonic->revision, diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 9922ab40798c..ddb6ccc72e44 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -88,11 +88,11 @@ static int snd_trident_probe(struct pci_dev *pci, default: str = "Unknown"; } - strcpy(card->driver, str); + strscpy(card->driver, str); if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - strcpy(card->shortname, "SiS "); + strscpy(card->shortname, "SiS "); } else { - strcpy(card->shortname, "Trident "); + strscpy(card->shortname, "Trident "); } strcat(card->shortname, str); sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 4e16b79d6584..39ed52bf8631 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2137,7 +2137,7 @@ int snd_trident_pcm(struct snd_trident *trident, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Trident 4DWave"); + strscpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; if (trident->tlb.entries) { @@ -2189,16 +2189,16 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, int device) else snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); foldback->info_flags = 0; - strcpy(foldback->name, "Trident 4DWave"); + strscpy(foldback->name, "Trident 4DWave"); substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - strcpy(substream->name, "Front Mixer"); + strscpy(substream->name, "Front Mixer"); substream = substream->next; - strcpy(substream->name, "Reverb Mixer"); + strscpy(substream->name, "Reverb Mixer"); substream = substream->next; - strcpy(substream->name, "Chorus Mixer"); + strscpy(substream->name, "Chorus Mixer"); if (num_chan == 4) { substream = substream->next; - strcpy(substream->name, "Second AC'97 ADC"); + strscpy(substream->name, "Second AC'97 ADC"); } trident->foldback = foldback; @@ -2241,7 +2241,7 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, int device) snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); } spdif->info_flags = 0; - strcpy(spdif->name, "Trident 4DWave IEC958"); + strscpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a04dbc0a420f..0753c0c73f51 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1436,7 +1436,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playbacks */ for (i = 0; i < 4; i++) @@ -1461,7 +1461,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1504,7 +1504,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1532,7 +1532,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->playback_devno, 0x30, 3, 0); @@ -1562,7 +1562,7 @@ static int snd_via686_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0); init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1); @@ -2461,7 +2461,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA686: - strcpy(card->driver, "VIA686A"); + strscpy(card->driver, "VIA686A"); sprintf(card->shortname, "VIA 82C686A/B rev%x", pci->revision); chip_type = TYPE_VIA686; break; @@ -2471,7 +2471,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) { if (pci->revision == via823x_cards[i].revision) { chip_type = via823x_cards[i].type; - strcpy(card->shortname, via823x_cards[i].name); + strscpy(card->shortname, via823x_cards[i].name); break; } } @@ -2487,11 +2487,11 @@ static int __snd_via82xx_probe(struct pci_dev *pci, chip_type = TYPE_VIA8233; } if (chip_type == TYPE_VIA8233A) - strcpy(card->driver, "VIA8233A"); + strscpy(card->driver, "VIA8233A"); else if (pci->revision >= VIA_REV_8237) - strcpy(card->driver, "VIA8237"); /* no slog assignment */ + strscpy(card->driver, "VIA8237"); /* no slog assignment */ else - strcpy(card->driver, "VIA8233"); + strscpy(card->driver, "VIA8233"); break; default: dev_err(card->dev, "invalid card type %d\n", card_type); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index eef0f9ddaae0..12a8c620724d 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -842,7 +842,7 @@ static int snd_via686_pcm_new(struct via82xx_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_MO_STATUS, 0); init_viadev(chip, 1, VIA_REG_MI_STATUS, 1); @@ -1116,7 +1116,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA82XX_MODEM: - strcpy(card->driver, "VIA82XX-MODEM"); + strscpy(card->driver, "VIA82XX-MODEM"); sprintf(card->shortname, "VIA 82XX modem"); break; default: diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 48444dda44de..764ca59e98d1 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -188,7 +188,7 @@ static int __snd_card_ymfpci_probe(struct pci_dev *pci, default: model = str = "???"; break; } - strcpy(card->driver, str); + strscpy(card->driver, str); sprintf(card->shortname, "Yamaha %s (%s)", model, str); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index d495f53a8324..75e013b66c5b 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1134,7 +1134,7 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI"); + strscpy(pcm->name, "YMFPCI"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1201,7 +1201,7 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - IEC958"); + strscpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1242,7 +1242,7 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - Rear PCM"); + strscpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1948,7 +1948,7 @@ int snd_ymfpci_timer(struct snd_ymfpci *chip, int device) tid.subdevice = 0; err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "YMFPCI timer"); + strscpy(timer->name, "YMFPCI timer"); timer->private_data = chip; timer->hw = snd_ymfpci_timer_hw; } diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 494460746614..13419837dfb7 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -160,7 +160,7 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq if (err < 0) return err; - strcpy(card->driver, "PDAudio-CF"); + strscpy(card->driver, "PDAudio-CF"); sprintf(card->shortname, "Core Sound %s", card->driver); sprintf(card->longname, "%s at 0x%x, irq %i", card->shortname, port, irq); diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c index aaa82ec36540..20aba745f1dc 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c @@ -263,7 +263,7 @@ int snd_pdacf_pcm_new(struct snd_pdacf *chip) pcm->private_data = chip; pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index d2d5f64d63b4..2e09f2a513a6 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -187,7 +187,7 @@ static int vxpocket_config(struct pcmcia_device *link) /* overwrite the hardware information */ chip->hw = &vxp440_hw; chip->type = vxp440_hw.type; - strcpy(chip->card->driver, vxp440_hw.name); + strscpy(chip->card->driver, vxp440_hw.name); } ret = pcmcia_request_io(link); diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 49399a4a290d..13a6f3af13ef 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -956,7 +956,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip) /* * build mixers */ - strcpy(chip->card->mixername, "PowerMac AWACS"); + strscpy(chip->card->mixername, "PowerMac AWACS"); err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), snd_pmac_awacs_mixers); diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 400a886562b1..ba15bc34c9ec 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -665,7 +665,7 @@ int snd_pmac_burgundy_init(struct snd_pmac *chip) /* * build burgundy mixers */ - strcpy(chip->card->mixername, "PowerMac Burgundy"); + strscpy(chip->card->mixername, "PowerMac Burgundy"); for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { err = snd_ctl_add(chip->card, diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index d5766952f3db..a74114225b67 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -261,7 +261,7 @@ int snd_pmac_daca_init(struct snd_pmac *chip) /* * build mixers */ - strcpy(chip->card->mixername, "PowerMac DACA"); + strscpy(chip->card->mixername, "PowerMac DACA"); for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) { err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip)); diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 76674c43fa7e..a3d346f1cc05 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -679,7 +679,7 @@ int snd_pmac_pcm_new(struct snd_pmac *chip) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index f1b0cf9ea555..e685d245883e 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -55,8 +55,8 @@ static int snd_pmac_probe(struct platform_device *devptr) switch (chip->model) { case PMAC_BURGUNDY: - strcpy(card->driver, "PMac Burgundy"); - strcpy(card->shortname, "PowerMac Burgundy"); + strscpy(card->driver, "PMac Burgundy"); + strscpy(card->shortname, "PowerMac Burgundy"); sprintf(card->longname, "%s (Dev %d) Sub-frame %d", card->shortname, chip->device_id, chip->subframe); err = snd_pmac_burgundy_init(chip); @@ -64,8 +64,8 @@ static int snd_pmac_probe(struct platform_device *devptr) goto __error; break; case PMAC_DACA: - strcpy(card->driver, "PMac DACA"); - strcpy(card->shortname, "PowerMac DACA"); + strscpy(card->driver, "PMac DACA"); + strscpy(card->shortname, "PowerMac DACA"); sprintf(card->longname, "%s (Dev %d) Sub-frame %d", card->shortname, chip->device_id, chip->subframe); err = snd_pmac_daca_init(chip); diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index a6cff2c46ac7..ce7ee2713f9d 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -951,9 +951,9 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) if (ret < 0) goto clean_irq; - strcpy(the_card.card->driver, "PS3"); - strcpy(the_card.card->shortname, "PS3"); - strcpy(the_card.card->longname, "PS3 sound"); + strscpy(the_card.card->driver, "PS3"); + strscpy(the_card.card->shortname, "PS3"); + strscpy(the_card.card->longname, "PS3 sound"); /* create control elements */ for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { @@ -975,7 +975,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) goto clean_card; the_card.pcm->private_data = &the_card; - strcpy(the_card.pcm->name, "SPDIF"); + strscpy(the_card.pcm->name, "SPDIF"); /* set pcm ops */ snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 40ea843113a7..fa81bfba59c1 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -424,7 +424,7 @@ static int __init snd_aicapcmchip(struct snd_card_aica if (unlikely(err < 0)) return err; pcm->private_data = dreamcastcard; - strcpy(pcm->name, "AICA PCM"); + strscpy(pcm->name, "AICA PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_aicapcm_playback_ops); /* Allocate the DMA buffers */ @@ -568,9 +568,9 @@ static int snd_aica_probe(struct platform_device *devptr) kfree(dreamcastcard); return err; } - strcpy(dreamcastcard->card->driver, "snd_aica"); - strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); - strcpy(dreamcastcard->card->longname, + strscpy(dreamcastcard->card->driver, "snd_aica"); + strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); + strscpy(dreamcastcard->card->longname, "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); /* Prepare to use the queue */ INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 84a4b17a0cc2..164f91240d02 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -224,7 +224,7 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device) return err; pcm->private_data = chip; - strcpy(pcm->name, "SH_DAC PCM"); + strscpy(pcm->name, "SH_DAC PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops); /* buffer size=48K */ @@ -358,8 +358,8 @@ static int snd_sh_dac_probe(struct platform_device *devptr) if (err < 0) goto probe_error; - strcpy(card->driver, "snd_sh_dac"); - strcpy(card->shortname, "SuperH DAC audio driver"); + strscpy(card->driver, "snd_sh_dac"); + strscpy(card->shortname, "SuperH DAC audio driver"); dev_info(&devptr->dev, "%s %s\n", card->longname, card->shortname); err = snd_card_register(card); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 1b983c7006f1..ce74818bd715 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -127,7 +127,6 @@ source "sound/soc/renesas/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sdca/Kconfig" -source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" source "sound/soc/starfive/Kconfig" @@ -141,6 +140,9 @@ source "sound/soc/ux500/Kconfig" source "sound/soc/xilinx/Kconfig" source "sound/soc/xtensa/Kconfig" +# SOF +source "sound/soc/sof/Kconfig" + # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig index 0236dc5b4e9f..d47dffbf40d0 100644 --- a/sound/soc/adi/Kconfig +++ b/sound/soc/adi/Kconfig @@ -1,12 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_ADI - tristate "Audio support for Analog Devices reference designs" - help - Audio support for various reference designs by Analog Devices. +menu "Analog Devices" config SND_SOC_ADI_AXI_I2S tristate "AXI-I2S support" - depends on SND_SOC_ADI select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help @@ -14,8 +10,9 @@ config SND_SOC_ADI_AXI_I2S config SND_SOC_ADI_AXI_SPDIF tristate "AXI-SPDIF support" - depends on SND_SOC_ADI select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral. + +endmenu diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index c7daae392d74..fd35a03aadcb 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "AMD" + config SND_SOC_AMD_ACP tristate "AMD Audio Coprocessor support" help @@ -185,3 +187,4 @@ config SND_SOC_AMD_PS_MACH If unsure select "N". endif +endmenu diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index b9432052c638..c2a60bc80ee6 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -148,6 +148,7 @@ config SND_SOC_AMD_SOF_SDW_MACH select SND_SOC_RT1316_SDW select SND_SOC_RT715_SDW select SND_SOC_RT715_SDCA_SDW + select SND_SOC_RT722_SDCA_SDW help This option enables SOF sound card support for SoundWire enabled AMD platforms along with ACP PDM controller. diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 70fa54d568ef..617690362ad7 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -58,6 +58,7 @@ static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id) case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div); val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div); break; @@ -134,6 +135,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: switch (slots) { case 1 ... 31: no_of_slots = slots; @@ -168,6 +170,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) chip->tdm_tx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 13) | (slot_len << 18); diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index ba8db0851daa..3078f459e005 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -372,6 +372,7 @@ static int acp_power_on(struct acp_chip_info *chip) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS; acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL; break; @@ -573,6 +574,7 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: pdm_addr = ACP70_PDM_ADDR; check_acp70_config(chip); break; diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index a0dab85088ec..c4bc8e849284 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -1772,6 +1772,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: links[i].platforms = platform_acp70_component; links[i].num_platforms = ARRAY_SIZE(platform_acp70_component); break; diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 0b2aa33cc426..f83708755ed1 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -137,26 +137,27 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id chip->name = "acp_asoc_renoir"; chip->rsrc = &rn_rsrc; chip->acp_hw_ops_init = acp31_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp_machines; + chip->machines = snd_soc_acpi_amd_acp_machines; break; case 0x6f: chip->name = "acp_asoc_rembrandt"; chip->rsrc = &rmb_rsrc; chip->acp_hw_ops_init = acp6x_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_rmb_acp_machines; + chip->machines = snd_soc_acpi_amd_rmb_acp_machines; break; case 0x63: chip->name = "acp_asoc_acp63"; chip->rsrc = &acp63_rsrc; chip->acp_hw_ops_init = acp63_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp63_acp_machines; + chip->machines = snd_soc_acpi_amd_acp63_acp_machines; break; case 0x70: case 0x71: + case 0x72: chip->name = "acp_asoc_acp70"; chip->rsrc = &acp70_rsrc; chip->acp_hw_ops_init = acp70_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp70_acp_machines; + chip->machines = snd_soc_acpi_amd_acp70_acp_machines; break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index b3eddf76aaa4..b25ac5612808 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -140,6 +140,7 @@ void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int s switch (chip->acp_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: switch (stream->dai_id) { case I2S_SP_INSTANCE: if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -205,6 +206,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = acp6x_pcm_hardware_playback; else diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 6c24f9d8694e..c2197b75a7dd 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -158,6 +158,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1), *be_id, &cpu_pin_id, dev); if (ret) @@ -264,6 +265,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card, case ACP63_PCI_REV: case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: sdw_platform_component->name = "amd_ps_sdw_dma.0"; break; default: @@ -311,6 +313,7 @@ static int create_dmic_dailinks(struct snd_soc_card *card, case ACP63_PCI_REV: case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: pdm_cpu->name = "acp_ps_pdm_dma.0"; pdm_platform->name = "acp_ps_pdm_dma.0"; break; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index 654fe78b2e2e..91d72d4bb9a2 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -130,6 +130,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1), *be_id, &cpu_pin_id, dev); if (ret) diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index b95e3949e70b..bca311c88139 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -136,6 +136,7 @@ static int acp_acp70_audio_probe(struct platform_device *pdev) switch (chip->acp_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: break; default: dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); diff --git a/sound/soc/amd/acp/acp_common.h b/sound/soc/amd/acp/acp_common.h index f1ae88013f62..984685602e3d 100644 --- a/sound/soc/amd/acp/acp_common.h +++ b/sound/soc/amd/acp/acp_common.h @@ -15,5 +15,6 @@ #define ACP63_PCI_ID 0x63 #define ACP70_PCI_ID 0x70 #define ACP71_PCI_ID 0x71 +#define ACP72_PCI_ID 0x72 #endif diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index e87ccfeee5bd..dcecac792e6d 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -155,6 +155,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines); +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = { + { + .link_mask = BIT(0), + .links = acp70_rt722_only, + .drv_name = "amd_sof_sdw", + .sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg", + .fw_filename = "sof-acp_7_0.ri", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines); + MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c index d95047d2ee94..27da2a862f1c 100644 --- a/sound/soc/amd/acp/amd-acpi-mach.c +++ b/sound/soc/amd/acp/amd-acpi-mach.c @@ -8,12 +8,12 @@ #include -struct snd_soc_acpi_codecs amp_rt1019 = { +static struct snd_soc_acpi_codecs amp_rt1019 = { .num_codecs = 1, .codecs = {"10EC1019"} }; -struct snd_soc_acpi_codecs amp_max = { +static struct snd_soc_acpi_codecs amp_max = { .num_codecs = 1, .codecs = {"MX98360A"} }; diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 863e74fcee43..cb8d97122f95 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -243,10 +243,10 @@ extern struct acp_resource rmb_rsrc; extern struct acp_resource acp63_rsrc; extern struct acp_resource acp70_rsrc; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[]; extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; extern const struct snd_soc_dai_ops acp_dmic_dai_ops; diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h index 1f24e0e06487..3930cc46fa58 100644 --- a/sound/soc/amd/acp/soc_amd_sdw_common.h +++ b/sound/soc/amd/acp/soc_amd_sdw_common.h @@ -21,6 +21,8 @@ #define ACP63_PCI_REV 0x63 #define ACP70_PCI_REV 0x70 #define ACP71_PCI_REV 0x71 +#define ACP72_PCI_REV 0x72 + #define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0)) #define ASOC_SDW_FOUR_SPK BIT(4) #define ASOC_SDW_ACP_DMIC BIT(5) diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index fdf016a64bbf..5b6362103ca0 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -27,6 +27,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; struct config_entry { u32 flags; diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 85feae45c44c..90fc016dac0b 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -14,6 +14,7 @@ #define ACP63_PCI_REV 0x63 #define ACP70_PCI_REV 0x70 #define ACP71_PCI_REV 0x71 +#define ACP72_PCI_REV 0x72 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP63_PGFSM_CNTL_POWER_ON_MASK 1 @@ -334,6 +335,8 @@ struct acp_hw_ops { * @addr: pci ioremap address * @reg_range: ACP reigister range * @acp_rev: ACP PCI revision id + * @acp_sw_pad_keeper_en: store acp SoundWire pad keeper enable register value + * @acp_pad_pulldown_ctrl: store acp pad pulldown control register value * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire * manager-SW0 instance * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire @@ -367,6 +370,8 @@ struct acp63_dev_data { u32 addr; u32 reg_range; u32 acp_rev; + u32 acp_sw_pad_keeper_en; + u32 acp_pad_pulldown_ctrl; u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS]; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 7936b3173632..c62299b29204 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -117,6 +117,7 @@ static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: adata->acp70_sdw0_dma_intr_stat[stream_id] = 1; break; } @@ -141,6 +142,7 @@ static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) { for (index = ACP70_P1_AUDIO2_RX_THRESHOLD; index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) { @@ -552,6 +554,7 @@ static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci) break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: acp70_hw_init_ops(adata->hw_ops); break; default: @@ -581,6 +584,7 @@ static int snd_acp63_probe(struct pci_dev *pci, case ACP63_PCI_REV: case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: break; default: dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n"); diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c index 1c89fb5fe1da..7b4966b75dc6 100644 --- a/sound/soc/amd/ps/ps-common.c +++ b/sound/soc/amd/ps/ps-common.c @@ -160,6 +160,8 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev) adata = dev_get_drvdata(dev); if (adata->is_sdw_dev) { + adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL); adata->sdw_en_stat = check_acp_sdw_enable_status(adata); if (adata->sdw_en_stat) { writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL); @@ -197,6 +199,7 @@ static int __maybe_unused snd_acp63_runtime_resume(struct device *dev) static int __maybe_unused snd_acp63_resume(struct device *dev) { struct acp63_dev_data *adata; + u32 acp_sw_pad_keeper_en; int ret; adata = dev_get_drvdata(dev); @@ -209,6 +212,12 @@ static int __maybe_unused snd_acp63_resume(struct device *dev) if (ret) dev_err(dev, "ACP init failed\n"); + acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en); + if (!acp_sw_pad_keeper_en) { + writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL); + } return ret; } @@ -408,6 +417,8 @@ static int __maybe_unused snd_acp70_suspend(struct device *dev) adata = dev_get_drvdata(dev); if (adata->is_sdw_dev) { + adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL); adata->sdw_en_stat = check_acp_sdw_enable_status(adata); if (adata->sdw_en_stat) { writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL); @@ -445,6 +456,7 @@ static int __maybe_unused snd_acp70_runtime_resume(struct device *dev) static int __maybe_unused snd_acp70_resume(struct device *dev) { struct acp63_dev_data *adata; + u32 acp_sw_pad_keeper_en; int ret; adata = dev_get_drvdata(dev); @@ -459,6 +471,12 @@ static int __maybe_unused snd_acp70_resume(struct device *dev) if (ret) dev_err(dev, "ACP init failed\n"); + acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en); + if (!acp_sw_pad_keeper_en) { + writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN); + writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL); + } return ret; } diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 1b933a017c06..5449323e2728 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -269,6 +269,7 @@ static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: switch (manager_instance) { case ACP_SDW0: reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size; @@ -382,6 +383,7 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: switch (stream->instance) { case ACP_SDW0: sdw_data->acp70_sdw0_dma_stream[stream_id] = substream; @@ -451,6 +453,7 @@ static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __io break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: switch (stream->instance) { case ACP_SDW0: pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg; @@ -529,6 +532,7 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: switch (stream->instance) { case ACP_SDW0: sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL; @@ -574,6 +578,7 @@ static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, break; case ACP70_PCI_REV: case ACP71_PCI_REV: + case ACP72_PCI_REV: switch (stream->instance) { case ACP_SDW0: sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id]; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 7e62445e02c1..f210a253da9f 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -353,6 +353,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "RB"), + DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -360,6 +367,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83J2"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83J3"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -402,6 +416,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "M6501RM"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -451,6 +472,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VEK"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -514,6 +542,20 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb1xxx"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb2xxx"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -556,6 +598,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8A7F"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8A81"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index 793f7782e0d7..d8dc2f1ccc83 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -1,8 +1,11 @@ +menu "Apple" + config SND_SOC_APPLE_MCA tristate "Apple Silicon MCA driver" depends on ARCH_APPLE || COMPILE_TEST select SND_DMAENGINE_PCM - default ARCH_APPLE help This option enables an ASoC platform driver for MCA peripherals found on Apple Silicon SoCs. + +endmenu diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 5d59e00be823..4f51612f3dd2 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -1,13 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_ATMEL_SOC - tristate "SoC Audio for the Atmel System-on-Chip" +menu "Atmel" depends on HAS_IOMEM - help - Say Y or M if you want to add support for codecs attached to - the ATMEL SSC interface. You will also need - to select the audio interfaces to support below. - -if SND_ATMEL_SOC config SND_ATMEL_SOC_PDC bool @@ -176,4 +169,4 @@ config SND_MCHP_SOC_PDMC 2 data lines. The signal path includes an audio grade programmable decimation filter and outputs 24-bit audio words. -endif +endmenu diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index fb820609c043..521bee4998f8 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -577,7 +577,6 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, sizeof(ch_stat->data)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -660,7 +659,6 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, sizeof(user_data->data)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -726,7 +724,6 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol, uvalue->value.integer.value[0] = ctrl->ulock; - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -762,7 +759,6 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol, ctrl->badf = 0; } - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -811,7 +807,6 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol, regmap_read(dev->regmap, SPDIFRX_RSR, &val); } - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: @@ -875,7 +870,6 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig index 8a78809e8754..a7630897bc0b 100644 --- a/sound/soc/au1x/Kconfig +++ b/sound/soc/au1x/Kconfig @@ -2,6 +2,8 @@ ## ## Au1200/Au1550/Au1300 PSC + DBDMA ## +menu "Au1x" + config SND_SOC_AU1XPSC tristate "SoC Audio for Au12xx/Au13xx/Au1550" depends on MIPS_ALCHEMY @@ -63,3 +65,5 @@ config SND_SOC_DB1200 Select this option to enable audio (AC97 and I2S) on the Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards. If you need Db1300 touchscreen support, you definitely want to say Y. + +endmenu diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index 4218057b0874..de4e8a0daf1c 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Broadcom" + config SND_BCM2835_SOC_I2S tristate "SoC Audio support for the Broadcom BCM2835 I2S module" depends on ARCH_BCM2835 || COMPILE_TEST @@ -26,3 +28,5 @@ config SND_BCM63XX_I2S_WHISTLER DSL/PON chips (bcm63158, bcm63178) If you don't know what to do here, say N + +endmenu diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 97def4e53fbc..31475e64e7dd 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Cirrus Logic" + config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" depends on ARCH_EP93XX || COMPILE_TEST @@ -31,3 +33,4 @@ config SND_EP93XX_SOC_I2S_WATCHDOG endif # if SND_EP93XX_SOC_I2S +endmenu diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 126f897312d4..6d7e4725d89c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 imply SND_SOC_RT9123 + imply SND_SOC_RTQ9124 imply SND_SOC_RTQ9128 imply SND_SOC_SDW_MOCKUP imply SND_SOC_SGTL5000 @@ -263,9 +264,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 - imply SND_SOC_TAS2781_COMLIB - imply SND_SOC_TAS2781_COMLIB_I2C - imply SND_SOC_TAS2781_FMWLIB imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 imply SND_SOC_TAS571X @@ -1858,6 +1856,14 @@ config SND_SOC_RT9123P Enable support for the HW control mode of Richtek RT9123P 3.2W mono Class-D audio amplifier. +config SND_SOC_RTQ9124 + tristate "Richtek RTQ9124 Mono Class-D Amplifier" + depends on I2C + select REGMAP + help + Enable support for Richtek RTQ9124 1x30W digital input automotive + audio amplifier with current sense and real-time load diagnostics. + config SND_SOC_RTQ9128 tristate "Richtek RTQ9128 45W Digital Input Amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6d7aa109ede7..a68c3d192a1b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -275,6 +275,7 @@ snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-y := rt9120.o snd-soc-rt9123-y := rt9123.o snd-soc-rt9123p-y := rt9123p.o +snd-soc-rtq9124-y := rtq9124.o snd-soc-rtq9128-y := rtq9128.o snd-soc-sdw-mockup-y := sdw-mockup.o snd-soc-sgtl5000-y := sgtl5000.o @@ -695,6 +696,7 @@ obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o +obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -853,4 +855,4 @@ obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o # Mux -obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o \ No newline at end of file +obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index 9c15ddba6008..22f9c431a0e5 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -319,7 +319,6 @@ static void arizona_stop_mic(struct arizona_priv *info) if (change) { regulator_disable(info->micvdd); - pm_runtime_mark_last_busy(arizona->dev); pm_runtime_put_autosuspend(arizona->dev); } } @@ -1127,7 +1126,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) mutex_unlock(&info->lock); - pm_runtime_mark_last_busy(arizona->dev); pm_runtime_put_autosuspend(arizona->dev); return IRQ_HANDLED; diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 110009616966..ef6f64856988 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -177,7 +177,7 @@ static int aw87390_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); - char *prof_name, *name; + char *prof_name; int count, ret; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -194,17 +194,15 @@ static int aw87390_profile_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; - name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; ret = aw87390_dev_get_prof_name(aw87390->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - strlen("null") + 1); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index 3dd8428f08cc..d61a7b8c5470 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -914,12 +914,11 @@ static int aw88081_profile_info(struct snd_kcontrol *kcontrol, ret = aw88081_dev_get_prof_name(aw88081->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(uinfo->value.enumerated.name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index 4f76ebe11cc7..28f62b991ef2 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -1478,7 +1478,7 @@ static int aw88166_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); - char *prof_name, *name; + char *prof_name; int count, ret; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -1495,17 +1495,15 @@ static int aw88166_profile_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; - name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; ret = aw88166_dev_get_prof_name(aw88166->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - strlen("null") + 1); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index fb99871578c5..de11ae8dd9d9 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -819,7 +819,7 @@ static int aw88261_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); - char *prof_name, *name; + char *prof_name; int count, ret; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -836,17 +836,15 @@ static int aw88261_profile_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; - name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - strlen("null") + 1); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index aea44a199b98..fb563b4c6971 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -175,7 +175,7 @@ static int aw88395_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); - char *prof_name, *name; + char *prof_name; int count, ret; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -192,17 +192,15 @@ static int aw88395_profile_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; - name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; ret = aw88395_dev_get_prof_name(aw88395->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - strlen("null") + 1); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h index 0f750f654f3e..6f8b30b475da 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.h +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -102,6 +102,11 @@ struct aw_profctrl_desc { unsigned int cur_mode; }; +enum { + CALI_RESULT_NORMAL, + CALI_RESULT_ERROR, +}; + struct aw_volume_desc { unsigned int init_volume; unsigned int mute_volume; @@ -124,9 +129,25 @@ struct aw_cali_delay_desc { unsigned int delay; }; +#define AW_CALI_CFG_NUM (4) +struct cali_cfg { + uint32_t data[AW_CALI_CFG_NUM]; +}; + +struct aw_cali_backup_desc { + unsigned int dsp_ng_cfg; + unsigned int dsp_lp_cfg; +}; + struct aw_cali_desc { u32 cali_re; u32 ra; + bool cali_switch; + bool cali_running; + uint16_t cali_result; + uint16_t store_vol; + struct cali_cfg cali_cfg; + struct aw_cali_backup_desc backup_info; }; struct aw_container { diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 4b90133e5ab4..58846feb013d 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "aw88399.h" #include "aw88395/aw88395_device.h" @@ -45,6 +46,67 @@ static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, return 0; } +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW88395_DSP_16_DATA_MASK; + ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data); + if (ret) + dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret); + + return ret; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + unsigned int reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + dsp_addr, dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed", + dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state */ + if (regmap_read(aw_dev->regmap, 0x00, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, unsigned short dsp_addr, unsigned int *dsp_data) { @@ -452,14 +514,14 @@ static int aw_dev_set_vcalb(struct aw88399 *aw88399) case AW88399_DEV_VDSEL_VSENSE: ret = aw88399_dev_get_vcalk(aw88399, &vcalk); vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR / + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * ical_k / vcal_k * aw88399->vcalb_init_val; break; case AW88399_DEV_VDSEL_DAC: ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / - AW88399_ISCAL_DAC_FACTOR / ical_k / + AW88399_ISCAL_DAC_FACTOR * ical_k / vcal_k * aw88399->vcalb_init_val; break; default: @@ -1356,6 +1418,329 @@ static struct snd_soc_dai_driver aw88399_dai[] = { }, }; +static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result) +{ + if (cali_result == CALI_RESULT_ERROR) + aw88399_dev_mute(aw_dev, true); + else if (cali_result == CALI_RESULT_NORMAL) + aw88399_dev_mute(aw_dev, false); +} + +static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev) +{ + struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH, + &cali_cfg->data[0], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH, + &cali_cfg->data[1], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN, + &cali_cfg->data[2], AW88399_DSP_16_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA, + &cali_cfg->data[3], AW88399_DSP_16_DATA); + + return ret; +} + +static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev, + struct cali_cfg cali_cfg) +{ + int ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH, + cali_cfg.data[0], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH, + cali_cfg.data[1], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN, + cali_cfg.data[2], AW88399_DSP_16_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA, + cali_cfg.data[3], AW88399_DSP_16_DATA); + + return ret; +} + +static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en) +{ + struct cali_cfg set_cfg; + int ret; + + aw_dev_dsp_enable(aw_dev, false); + if (cali_en) { + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE); + aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW88399_DSP_16_DATA); + + ret = aw_cali_svc_get_cali_cfg(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "get cali cfg failed\n"); + aw_dev_dsp_enable(aw_dev, true); + return ret; + } + set_cfg.data[0] = 0; + set_cfg.data[1] = 0; + set_cfg.data[2] = -1; + set_cfg.data[3] = 1; + + ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg); + if (ret) { + dev_err(aw_dev->dev, "set cali cfg failed\n"); + aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg); + aw_dev_dsp_enable(aw_dev, true); + return ret; + } + } else { + aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg); + } + + aw_dev_dsp_enable(aw_dev, true); + + return 0; +} + +static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable) +{ + unsigned int reg_val; + int ret; + + if (enable) { + ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG); + return ret; + } + + aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK); + ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG, + ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG, + ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol); + } + + return ret; +} + +static void aw_cali_svc_backup_info(struct aw_device *aw_dev) +{ + struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info; + unsigned int reg_val, dsp_val; + + regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, ®_val); + backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK); + + aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + &dsp_val, AW88399_DSP_16_DATA); + + backup_desc->dsp_lp_cfg = dsp_val; +} + +static void aw_cali_svc_recover_info(struct aw_device *aw_dev) +{ + struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info; + + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg); + + aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + backup_desc->dsp_lp_cfg, AW88399_DSP_16_DATA); +} + +static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start\n"); + return ret; + } + + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status error\n"); + return ret; + } + + aw_cali_svc_backup_info(aw_dev); + ret = aw_cali_svc_cali_en(aw_dev, true); + if (ret) { + dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n"); + return ret; + } + + ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true); + if (ret) { + aw_cali_svc_cali_en(aw_dev, false); + return ret; + } + + } else { + aw_cali_svc_cali_run_dsp_vol(aw_dev, false); + aw_cali_svc_recover_info(aw_dev); + aw_cali_svc_cali_en(aw_dev, false); + } + + return 0; +} + +static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re) +{ + uint32_t dsp_re, show_re; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW88399_DSP_16_DATA); + if (ret) + return ret; + + show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT); + + *re = (uint32_t)(show_re - aw_dev->cali_desc.ra); + + return 0; +} + +static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re) +{ + int sum = 0, i; + + for (i = 1; i < data_size - 1; i++) + sum += data[i]; + + *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM); +} + +static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev) +{ + unsigned int reg_data; + int ret, i; + + for (i = 0; i < AW_GET_IV_CNT_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, ®_data); + if (ret) { + dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG); + return ret; + } + + reg_data &= (~AW88399_REABS_MASK); + if (!reg_data) + return 0; + msleep(30); + } + + dev_err(aw_dev->dev, "IV data abnormal, please check\n"); + + return -EINVAL; +} + +static int compare_ints(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev) +{ + uint32_t re_temp[AW_CALI_READ_CNT_MAX]; + uint32_t dsp_re; + int ret, i; + + for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) { + ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]); + if (ret) + goto cali_re_fail; + msleep(30); + } + + sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL); + + aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re); + + ret = aw_cali_svc_get_iv_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "get iv data failed"); + goto cali_re_fail; + } + + if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) { + dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re); + aw_dev->cali_desc.cali_re = dsp_re; + aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR; + aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result); + + return 0; + } + + aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL; + + aw_dev->cali_desc.cali_re = dsp_re; + dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re); + + aw_dev_dsp_enable(aw_dev, false); + aw_dev_update_cali_re(&aw_dev->cali_desc); + aw_dev_dsp_enable(aw_dev, true); + + return 0; + +cali_re_fail: + aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR; + aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result); + return -EINVAL; +} + +static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_cali_desc *cali_desc = &aw_dev->cali_desc; + int ret; + + if (cali_desc->cali_running) { + dev_err(aw_dev->dev, "calibration in progress\n"); + return -EINVAL; + } + + cali_desc->cali_running = true; + aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL); + + ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true); + if (ret) { + dev_err(aw_dev->dev, "start cali re failed\n"); + goto re_mode_err; + } + + msleep(1000); + + ret = aw_cali_svc_get_smooth_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "get cali re failed\n"); + + aw_cali_svc_cali_re_mode_enable(aw_dev, false); + +re_mode_err: + cali_desc->cali_running = false; + + return ret; +} + static int aw88399_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1446,7 +1831,7 @@ static int aw88399_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); - char *prof_name, *name; + char *prof_name; int count, ret; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -1463,17 +1848,15 @@ static int aw88399_profile_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; - name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; ret = aw88399_dev_get_prof_name(aw88399->aw_pa, count, &prof_name); if (ret) { - strscpy(uinfo->value.enumerated.name, "null", - strlen("null") + 1); + strscpy(uinfo->value.enumerated.name, "null"); return 0; } - strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + strscpy(uinfo->value.enumerated.name, prof_name); return 0; } @@ -1616,6 +1999,53 @@ static int aw88399_re_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch; + + return 0; +} + +static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0]) + return 0; + + aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0]; + + return 1; +} + +static int aw88399_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* do nothing */ + return 0; +} + +static int aw88399_calib_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + if (aw_dev->status && aw_dev->cali_desc.cali_switch) + aw_cali_svc_dev_cali_re(aw88399); + + return 0; +} + static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) { struct aw_device *aw_dev = aw88399->aw_pa; @@ -1708,6 +2138,10 @@ static const struct snd_kcontrol_new aw88399_controls[] = { aw88399_get_fade_out_time, aw88399_set_fade_out_time), SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0, aw88399_re_get, aw88399_re_set), + SOC_SINGLE_BOOL_EXT("Calib Switch", 0, + aw88399_calib_switch_get, aw88399_calib_switch_set), + SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0, + aw88399_calib_get, aw88399_calib_set), AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info, aw88399_profile_get, aw88399_profile_set), }; @@ -1894,9 +2328,18 @@ static const struct i2c_device_id aw88399_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id aw88399_acpi_match[] = { + { "AWDZ8399", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, aw88399_acpi_match); +#endif + static struct i2c_driver aw88399_i2c_driver = { .driver = { .name = AW88399_I2C_NAME, + .acpi_match_table = ACPI_PTR(aw88399_acpi_match), }, .probe = aw88399_i2c_probe, .id_table = aw88399_i2c_id, diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index 5e9cdf725d3d..cacc03b1eefa 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -451,6 +451,24 @@ #define AW88399_WDT_CNT_MASK \ (~(((1< #include #include +#include #include #include #include @@ -961,7 +962,6 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev) struct ec_response_ec_codec_get_capabilities r; int ret; #ifdef CONFIG_OF - struct device_node *node; struct resource res; u64 ec_shm_size; const __be32 *regaddr_p; @@ -981,22 +981,18 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev) priv->ec_shm_addr, priv->ec_shm_len); } - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (node) { - ret = of_address_to_resource(node, 0, &res); - if (!ret) { - priv->ap_shm_phys_addr = res.start; - priv->ap_shm_len = resource_size(&res); - priv->ap_shm_addr = - (uint64_t)(uintptr_t)devm_ioremap_wc( - dev, priv->ap_shm_phys_addr, - priv->ap_shm_len); - priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); + if (!ret) { + priv->ap_shm_phys_addr = res.start; + priv->ap_shm_len = resource_size(&res); + priv->ap_shm_addr = + (uint64_t)(uintptr_t)devm_ioremap_wc( + dev, priv->ap_shm_phys_addr, + priv->ap_shm_len); + priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; - dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", - priv->ap_shm_phys_addr, priv->ap_shm_len); - } - of_node_put(node); + dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", + priv->ap_shm_phys_addr, priv->ap_shm_len); } #endif diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index b49c6905e872..b60697ff7a50 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -129,7 +129,7 @@ static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { {27000000, 0x3F, 0x0A}, }; -static struct reg_default cs35l36_reg[] = { +static const struct reg_default cs35l36_reg[] = { {CS35L36_TESTKEY_CTRL, 0x00000000}, {CS35L36_USERKEY_CTL, 0x00000000}, {CS35L36_OTP_CTRL1, 0x00002460}, diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index ff4134bee858..224d65987a8d 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -483,7 +483,6 @@ static irqreturn_t cs35l41_irq(int irq, void *data) } done: - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_put_autosuspend(cs35l41->dev); return ret; @@ -1328,7 +1327,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg * pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); pm_runtime_use_autosuspend(cs35l41->dev); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_set_active(cs35l41->dev); pm_runtime_get_noresume(cs35l41->dev); pm_runtime_enable(cs35l41->dev); diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index 432a19f4de2b..d4dcdf37bb70 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -1427,7 +1427,6 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); pm_runtime_use_autosuspend(cs35l45->dev); - pm_runtime_mark_last_busy(cs35l45->dev); pm_runtime_set_active(cs35l45->dev); pm_runtime_get_noresume(cs35l45->dev); pm_runtime_enable(cs35l45->dev); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 13f602f51bf3..3905c9cb188a 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -238,16 +238,15 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; -static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56) +static int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56) { int ret; - /* SoundWire UniqueId is used to index the calibration array */ ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0); if (ret < 0) return ret; - cs35l56->base.cal_index = ret & 0xf; + cs35l56->sdw_unique_id = ret & 0xf; return 0; } @@ -259,11 +258,13 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) pm_runtime_get_noresume(cs35l56->base.dev); - if (cs35l56->base.cal_index < 0) { - ret = cs35l56_sdw_set_cal_index(cs35l56); - if (ret < 0) - goto out; - } + ret = cs35l56_sdw_get_unique_id(cs35l56); + if (ret) + goto out; + + /* SoundWire UniqueId is used to index the calibration array */ + if (cs35l56->base.cal_index < 0) + cs35l56->base.cal_index = cs35l56->sdw_unique_id; ret = cs35l56_init(cs35l56); if (ret < 0) { @@ -282,7 +283,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) } out: - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } @@ -393,74 +393,6 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, return 0; } -static int cs35l63_sdw_kick_divider(struct cs35l56_private *cs35l56, - struct sdw_slave *peripheral) -{ - unsigned int curr_scale_reg, next_scale_reg; - int curr_scale, next_scale, ret; - - if (!cs35l56->base.init_done) - return 0; - - if (peripheral->bus->params.curr_bank) { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - } else { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - } - - /* - * Current clock scale value must be different to new value. - * Modify current to guarantee this. If next still has the dummy - * value we wrote when it was current, the core code has not set - * a new scale so restore its original good value - */ - curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); - if (curr_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); - return curr_scale; - } - - next_scale = sdw_read_no_pm(peripheral, next_scale_reg); - if (next_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); - return next_scale; - } - - if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { - next_scale = cs35l56->old_sdw_clock_scale; - ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", - ret); - return ret; - } - } - - cs35l56->old_sdw_clock_scale = curr_scale; - ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); - return ret; - } - - dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); - - return 0; -} - -static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, - struct sdw_bus_params *params) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - - if ((cs35l56->base.type == 0x63) && (cs35l56->base.rev < 0xa1)) - return cs35l63_sdw_kick_divider(cs35l56, peripheral); - - return 0; -} - static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -476,7 +408,6 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, - .bus_config = cs35l56_sdw_bus_config, #ifdef DEBUG .clk_stop = cs35l56_sdw_clk_stop, #endif @@ -587,6 +518,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi cs35l56->base.dev = dev; cs35l56->sdw_peripheral = peripheral; + cs35l56->sdw_link_num = peripheral->bus->link_id; INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); dev_set_drvdata(dev, cs35l56); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index d0831d609584..850fcf385996 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -838,6 +838,15 @@ const struct cirrus_amp_cal_controls cs35l56_calibration_controls = { }; EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, "SND_SOC_CS35L56_SHARED"); +static const struct cirrus_amp_cal_controls cs35l63_calibration_controls = { + .alg_id = 0xbf210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; + int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) { u64 silicon_uid = 0; @@ -912,19 +921,31 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED"); void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) { __be32 pid, sid, tid; + unsigned int alg_id; int ret; + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + alg_id = 0x9f212; + break; + default: + alg_id = 0xbf212; + break; + } + scoped_guard(mutex, &cs_dsp->pwr_lock) { ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID", - WMFW_ADSP2_XM, 0x9f212), + WMFW_ADSP2_XM, alg_id), 0, &pid, sizeof(pid)); if (!ret) ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID", - WMFW_ADSP2_XM, 0x9f212), + WMFW_ADSP2_XM, alg_id), 0, &sid, sizeof(sid)); if (!ret) ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID", - WMFW_ADSP2_XM, 0x9f212), + WMFW_ADSP2_XM, alg_id), 0, &tid, sizeof(tid)); } @@ -974,13 +995,15 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) case 0x35A54: case 0x35A56: case 0x35A57: + cs35l56_base->calibration_controls = &cs35l56_calibration_controls; break; case 0x35A630: + cs35l56_base->calibration_controls = &cs35l63_calibration_controls; devid = devid >> 4; break; default: dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); - return ret; + return -ENODEV; } cs35l56_base->type = devid & 0xFF; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index c78e4746e428..2c1edbd636ef 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -695,7 +695,7 @@ static int cs35l56_write_cal(struct cs35l56_private *cs35l56) return ret; ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp, - &cs35l56_calibration_controls, + cs35l56->base.calibration_controls, &cs35l56->base.cal_data); wm_adsp_stop(&cs35l56->dsp); @@ -706,16 +706,40 @@ static int cs35l56_write_cal(struct cs35l56_private *cs35l56) return ret; } +static int cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56, + bool load_firmware) +{ + int ret; + + /* + * Abort the first load if it didn't find the suffixed bins and + * we have an alternate fallback suffix. + */ + cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix); + + ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware); + if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) { + cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix; + cs35l56->fallback_fw_suffix = NULL; + cs35l56->dsp.bin_mandatory = false; + ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware); + } + + if (ret) { + dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret); + return ret; + } + + return 0; +} + static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56) { int ret; - /* Use wm_adsp to load and apply the firmware patch and coefficient files */ - ret = wm_adsp_power_up(&cs35l56->dsp, true); - if (ret) { - dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + ret = cs35l56_dsp_download_and_power_up(cs35l56, true); + if (ret) return; - } cs35l56_write_cal(cs35l56); @@ -750,11 +774,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing * but only if firmware is missing. If firmware is already patched just * power-up wm_adsp without downloading firmware. */ - ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing); - if (ret) { - dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + ret = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing); + if (ret) goto err; - } mutex_lock(&cs35l56->base.irq_lock); @@ -849,10 +871,37 @@ static void cs35l56_dsp_work(struct work_struct *work) cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp); err: - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } +static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) +{ + if (cs35l56->dsp.fwf_suffix) + return 0; + + if (!cs35l56->sdw_peripheral) + return 0; + + cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, + "l%uu%u", + cs35l56->sdw_link_num, + cs35l56->sdw_unique_id); + if (!cs35l56->dsp.fwf_suffix) + return -ENOMEM; + + /* + * There are published firmware files for L56 B0 silicon using + * the ALSA prefix as the filename suffix. Default to trying these + * first, with the new name as an alternate. + */ + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) { + cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; + cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix; + } + + return 0; +} + static int cs35l56_component_probe(struct snd_soc_component *component) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -892,6 +941,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENOMEM; cs35l56->component = component; + ret = cs35l56_set_fw_suffix(cs35l56); + if (ret) + return ret; + wm_adsp2_component_probe(&cs35l56->dsp, component); debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 200f695efca3..40a1800a4585 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -20,8 +20,6 @@ #define CS35L56_SDW_GEN_INT_MASK_1 0xc1 #define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0) -#define CS35L56_SDW_INVALID_BUS_SCALE 0xf - #define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ | SNDRV_PCM_FMTBIT_S32_LE) @@ -38,6 +36,7 @@ struct cs35l56_private { struct snd_soc_component *component; struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; struct sdw_slave *sdw_peripheral; + const char *fallback_fw_suffix; struct work_struct sdw_irq_work; bool sdw_irq_no_unmask; bool soft_resetting; @@ -51,7 +50,8 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; - u8 old_sdw_clock_scale; + u8 sdw_link_num; + u8 sdw_unique_id; }; extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 56668c392063..78bb093fa0cc 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1775,7 +1775,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) } mutex_unlock(&cs42l42->irq_lock); - pm_runtime_mark_last_busy(cs42l42->dev); pm_runtime_put_autosuspend(cs42l42->dev); return IRQ_HANDLED; diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 6165ac16c3a9..2a0a4986a9ce 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -242,7 +242,6 @@ int cs42l43_set_jack(struct snd_soc_component *component, error: mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -362,14 +361,15 @@ static void cs42l43_stop_button_detect(struct cs42l43_codec *priv) priv->button_detect_running = false; } +#define CS42L43_BUTTON_COMB_US 11000 #define CS42L43_BUTTON_COMB_MAX 512 #define CS42L43_BUTTON_ROUT 2210 -void cs42l43_button_press_work(struct work_struct *work) +irqreturn_t cs42l43_button_press(int irq, void *data) { - struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, - button_press_work.work); + struct cs42l43_codec *priv = data; struct cs42l43 *cs42l43 = priv->core; + irqreturn_t iret = IRQ_NONE; unsigned int buttons = 0; unsigned int val = 0; int i, ret; @@ -377,7 +377,7 @@ void cs42l43_button_press_work(struct work_struct *work) ret = pm_runtime_resume_and_get(priv->dev); if (ret) { dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); - return; + return iret; } mutex_lock(&priv->jack_lock); @@ -387,6 +387,9 @@ void cs42l43_button_press_work(struct work_struct *work) goto error; } + // Wait for 2 full cycles of comb filter to ensure good reading + usleep_range(2 * CS42L43_BUTTON_COMB_US, 2 * CS42L43_BUTTON_COMB_US + 50); + regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val); /* Bail if jack removed, the button is irrelevant and likely invalid */ @@ -420,34 +423,26 @@ void cs42l43_button_press_work(struct work_struct *work) snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + iret = IRQ_HANDLED; + error: mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); + + return iret; } -irqreturn_t cs42l43_button_press(int irq, void *data) +irqreturn_t cs42l43_button_release(int irq, void *data) { struct cs42l43_codec *priv = data; - - // Wait for 2 full cycles of comb filter to ensure good reading - queue_delayed_work(system_wq, &priv->button_press_work, - msecs_to_jiffies(20)); - - return IRQ_HANDLED; -} - -void cs42l43_button_release_work(struct work_struct *work) -{ - struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, - button_release_work); + irqreturn_t iret = IRQ_NONE; int ret; ret = pm_runtime_resume_and_get(priv->dev); if (ret) { dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); - return; + return iret; } mutex_lock(&priv->jack_lock); @@ -456,23 +451,17 @@ void cs42l43_button_release_work(struct work_struct *work) dev_dbg(priv->dev, "Button release IRQ\n"); snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + + iret = IRQ_HANDLED; } else { dev_dbg(priv->dev, "Spurious button release IRQ\n"); } mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); -} -irqreturn_t cs42l43_button_release(int irq, void *data) -{ - struct cs42l43_codec *priv = data; - - queue_work(system_wq, &priv->button_release_work); - - return IRQ_HANDLED; + return iret; } void cs42l43_bias_sense_timeout(struct work_struct *work) @@ -504,7 +493,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -776,7 +764,6 @@ void cs42l43_tip_sense_work(struct work_struct *work) priv->suspend_jack_debounce = false; - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -787,8 +774,6 @@ irqreturn_t cs42l43_tip_sense(int irq, void *data) cancel_delayed_work(&priv->bias_sense_timeout); cancel_delayed_work(&priv->tip_sense_work); - cancel_delayed_work(&priv->button_press_work); - cancel_work(&priv->button_release_work); // Ensure delay after suspend is long enough to avoid false detection if (priv->suspend_jack_debounce) diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index ea84ac64c775..b0c27d696c58 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -167,13 +167,14 @@ static void cs42l43_hp_ilimit_clear_work(struct work_struct *work) snd_soc_dapm_mutex_unlock(dapm); } -static void cs42l43_hp_ilimit_work(struct work_struct *work) +static irqreturn_t cs42l43_hp_ilimit(int irq, void *data) { - struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, - hp_ilimit_work); + struct cs42l43_codec *priv = data; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component); struct cs42l43 *cs42l43 = priv->core; + dev_dbg(priv->dev, "headphone ilimit IRQ\n"); + snd_soc_dapm_mutex_lock(dapm); if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) { @@ -183,7 +184,7 @@ static void cs42l43_hp_ilimit_work(struct work_struct *work) priv->hp_ilimit_count++; snd_soc_dapm_mutex_unlock(dapm); - return; + return IRQ_HANDLED; } dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n", @@ -218,15 +219,6 @@ static void cs42l43_hp_ilimit_work(struct work_struct *work) priv->hp_ilimited = false; snd_soc_dapm_mutex_unlock(dapm); -} - -static irqreturn_t cs42l43_hp_ilimit(int irq, void *data) -{ - struct cs42l43_codec *priv = data; - - dev_dbg(priv->dev, "headphone ilimit IRQ\n"); - - queue_work(system_long_wq, &priv->hp_ilimit_work); return IRQ_HANDLED; } @@ -1088,7 +1080,6 @@ static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) ret ? "open" : "closed"); error: - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -2159,10 +2150,7 @@ static void cs42l43_component_remove(struct snd_soc_component *component) cancel_delayed_work_sync(&priv->bias_sense_timeout); cancel_delayed_work_sync(&priv->tip_sense_work); - cancel_delayed_work_sync(&priv->button_press_work); - cancel_work_sync(&priv->button_release_work); - cancel_work_sync(&priv->hp_ilimit_work); cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); priv->component = NULL; @@ -2314,10 +2302,7 @@ static int cs42l43_codec_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); - INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work); - INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); - INIT_WORK(&priv->hp_ilimit_work, cs42l43_hp_ilimit_work); pm_runtime_set_autosuspend_delay(priv->dev, 100); pm_runtime_use_autosuspend(priv->dev); @@ -2370,7 +2355,6 @@ static int cs42l43_codec_probe(struct platform_device *pdev) goto err_clk; } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 1cd9d8a71c43..3ea36362b11a 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -88,8 +88,6 @@ struct cs42l43_codec { struct delayed_work tip_sense_work; struct delayed_work bias_sense_timeout; - struct delayed_work button_press_work; - struct work_struct button_release_work; struct completion type_detect; struct completion load_detect; @@ -99,7 +97,6 @@ struct cs42l43_codec { int jack_override; bool suspend_jack_debounce; - struct work_struct hp_ilimit_work; struct delayed_work hp_ilimit_clear_work; bool hp_ilimited; int hp_ilimit_count; @@ -134,8 +131,6 @@ int cs42l43_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *d); void cs42l43_bias_sense_timeout(struct work_struct *work); void cs42l43_tip_sense_work(struct work_struct *work); -void cs42l43_button_press_work(struct work_struct *work); -void cs42l43_button_release_work(struct work_struct *work); irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data); irqreturn_t cs42l43_button_press(int irq, void *data); irqreturn_t cs42l43_button_release(int irq, void *data); diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c index 90a795230d27..a306af4289ad 100644 --- a/sound/soc/codecs/cs48l32.c +++ b/sound/soc/codecs/cs48l32.c @@ -1385,7 +1385,6 @@ static irqreturn_t cs48l32_irq(int irq, void *data) result = IRQ_HANDLED; out: - pm_runtime_mark_last_busy(cs48l32_codec->core.dev); pm_runtime_put_autosuspend(cs48l32_codec->core.dev); return result; @@ -2162,6 +2161,10 @@ static int cs48l32_hw_params(struct snd_pcm_substream *substream, n_slots_multiple = 1; sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple); + if (sclk_target < 0) { + cs48l32_asp_err(dai, "Invalid parameters\n"); + return sclk_target; + } for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) { if ((cs48l32_sclk_rates[i].freq >= sclk_target) && diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 5f2f67e3bd29..a7539e1a1893 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3033,7 +3033,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7218 = { * Regmap configs */ -static struct reg_default da7218_reg_defaults[] = { +static const struct reg_default da7218_reg_defaults[] = { { DA7218_SYSTEM_ACTIVE, 0x00 }, { DA7218_CIF_CTRL, 0x00 }, { DA7218_SPARE1, 0x00 }, diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 3958e88a2445..1742f91c788c 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1982,8 +1982,8 @@ static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw, } } -static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int da7219_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, @@ -1992,28 +1992,30 @@ static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate, if (!da7219->master) return -EINVAL; - if (rate < 11025) - return 8000; - else if (rate < 12000) - return 11025; - else if (rate < 16000) - return 12000; - else if (rate < 22050) - return 16000; - else if (rate < 24000) - return 22050; - else if (rate < 32000) - return 24000; - else if (rate < 44100) - return 32000; - else if (rate < 48000) - return 44100; - else if (rate < 88200) - return 48000; - else if (rate < 96000) - return 88200; + if (req->rate < 11025) + req->rate = 8000; + else if (req->rate < 12000) + req->rate = 11025; + else if (req->rate < 16000) + req->rate = 12000; + else if (req->rate < 22050) + req->rate = 16000; + else if (req->rate < 24000) + req->rate = 22050; + else if (req->rate < 32000) + req->rate = 24000; + else if (req->rate < 44100) + req->rate = 32000; + else if (req->rate < 48000) + req->rate = 44100; + else if (req->rate < 88200) + req->rate = 48000; + else if (req->rate < 96000) + req->rate = 88200; else - return 96000; + req->rate = 96000; + + return 0; } static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2070,15 +2072,15 @@ static unsigned long da7219_bclk_get_factor(unsigned long rate, return 256; } -static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int da7219_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw[DA7219_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !da7219->master) + if (!req->best_parent_rate || !da7219->master) return -EINVAL; /* @@ -2088,9 +2090,11 @@ static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * parent WCLK rate set and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = da7219_bclk_get_factor(rate, *parent_rate); + factor = da7219_bclk_get_factor(req->rate, req->best_parent_rate); - return *parent_rate * factor; + req->rate = req->best_parent_rate * factor; + + return 0; } static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2116,12 +2120,12 @@ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = { .unprepare = da7219_wclk_unprepare, .is_prepared = da7219_wclk_is_prepared, .recalc_rate = da7219_wclk_recalc_rate, - .round_rate = da7219_wclk_round_rate, + .determine_rate = da7219_wclk_determine_rate, .set_rate = da7219_wclk_set_rate, }, [DA7219_DAI_BCLK_IDX] = { .recalc_rate = da7219_bclk_recalc_rate, - .round_rate = da7219_bclk_round_rate, + .determine_rate = da7219_bclk_determine_rate, .set_rate = da7219_bclk_set_rate, }, }; @@ -2312,7 +2316,7 @@ static void da7219_handle_pdata(struct snd_soc_component *component) * Regmap configs */ -static struct reg_default da7219_reg_defaults[] = { +static const struct reg_default da7219_reg_defaults[] = { { DA7219_MIC_1_SELECT, 0x00 }, { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, { DA7219_SR_24_48, 0x00 }, @@ -2443,7 +2447,7 @@ static const struct regmap_config da7219_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static struct reg_sequence da7219_rev_aa_patch[] = { +static const struct reg_sequence da7219_rev_aa_patch[] = { { DA7219_REFERENCES, 0x08 }, }; diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 066d92b54312..78c4e68f6002 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -1079,8 +1079,7 @@ static void es8326_init(struct snd_soc_component *component) regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); - regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, - (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index 009259632107..36b0ebdce514 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -620,7 +620,7 @@ static bool es8375_writeable_register(struct device *dev, unsigned int reg) } } -static struct regmap_config es8375_regmap_config = { +static const struct regmap_config es8375_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = ES8375_REG_MAX, diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index ba1763f36f17..6e4c75d288ef 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -636,7 +636,7 @@ static int es8389_set_bias_level(struct snd_soc_component *component, regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); - regmap_write(es8389->regmap, ES8389_RESET, 0x7E); + regmap_write(es8389->regmap, ES8389_RESET, 0x3E); regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); usleep_range(8000, 8500); regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index dc7794c9ac44..ede980cc6050 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -162,7 +162,6 @@ int hda_codec_probe_complete(struct hda_codec *codec) snd_hda_codec_register(codec); /* Complement pm_runtime_get_sync(bus) in probe */ - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); return ret; @@ -173,10 +172,10 @@ EXPORT_SYMBOL_GPL(hda_codec_probe_complete); static int hda_codec_probe(struct snd_soc_component *component) { struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_device *hdev = &codec->core; struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink; - hda_codec_patch_t patch; int ret; #ifdef CONFIG_PM @@ -214,14 +213,12 @@ static int hda_codec_probe(struct snd_soc_component *component) goto err; } - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (!patch) { - dev_err(&hdev->dev, "no patch specified\n"); + if (WARN_ON(!(driver->ops && driver->ops->probe))) { ret = -EINVAL; goto err; } - ret = patch(codec); + ret = driver->ops->probe(codec, codec->preset); if (ret < 0) { dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; @@ -252,8 +249,8 @@ static int hda_codec_probe(struct snd_soc_component *component) complete_err: hda_codec_unregister_dais(codec, component); parse_pcms_err: - if (codec->patch_ops.free) - codec->patch_ops.free(codec); + if (driver->ops->remove) + driver->ops->remove(codec); err: snd_hda_codec_cleanup_for_unbind(codec); device_new_err: @@ -262,7 +259,6 @@ static int hda_codec_probe(struct snd_soc_component *component) snd_hdac_ext_bus_link_put(bus, hlink); - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); return ret; } @@ -271,6 +267,7 @@ static int hda_codec_probe(struct snd_soc_component *component) static void hda_codec_remove(struct snd_soc_component *component) { struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_device *hdev = &codec->core; struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink; @@ -281,8 +278,8 @@ static void hda_codec_remove(struct snd_soc_component *component) hda_codec_unregister_dais(codec, component); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); + if (driver->ops->remove) + driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); pm_runtime_put_noidle(&hdev->dev); @@ -300,7 +297,6 @@ static void hda_codec_remove(struct snd_soc_component *component) * not be called due to early error, leaving bus uc unbalanced */ if (!was_registered) { - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); } diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 29c88de5508b..afd8edf10fdc 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -409,8 +409,8 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_soc_component_get_dapm(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *hcodec = hda_pvt->codec; + struct hda_codec_driver *driver = hda_codec_to_driver(hcodec); struct hdac_ext_link *hlink; - hda_codec_patch_t patch; int ret; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); @@ -484,15 +484,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) goto error_pm; } - patch = (hda_codec_patch_t)hcodec->preset->driver_data; - if (patch) { - ret = patch(hcodec); - if (ret < 0) { - dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret); - goto error_regmap; - } - } else { - dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__); + if (WARN_ON(!(driver->ops && driver->ops->probe))) { + ret = -EINVAL; + goto error_regmap; + } + + ret = driver->ops->probe(hcodec, hcodec->preset); + if (ret < 0) { + dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret); + goto error_regmap; } ret = snd_hda_codec_parse_pcms(hcodec); @@ -531,8 +531,8 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) return 0; error_patch: - if (hcodec->patch_ops.free) - hcodec->patch_ops.free(hcodec); + if (driver->ops->remove) + driver->ops->remove(hcodec); error_regmap: snd_hdac_regmap_exit(hdev); error_pm: @@ -548,6 +548,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) snd_soc_component_get_drvdata(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *codec = hda_pvt->codec; + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_ext_link *hlink = NULL; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); @@ -559,8 +560,8 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); + if (driver->ops->remove) + driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); } diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 1139a2754ca3..e50afd0bfcec 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -24,8 +24,6 @@ #include #include #include -#include "../../hda/local.h" -#include "hdac_hdmi.h" #define NAME_SIZE 32 @@ -221,8 +219,8 @@ static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) unsigned int caps; unsigned int type, param; - caps = get_wcaps(hdev, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; @@ -492,10 +490,10 @@ static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + if (!(snd_hdac_get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { dev_warn(&hdev->dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(hdev, pin->nid)); + pin->nid, snd_hdac_get_wcaps(hdev, pin->nid)); return -EINVAL; } @@ -660,8 +658,8 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int err; - chans = get_wcaps(hdev, cvt->nid); - chans = get_wcaps_channels(chans); + chans = snd_hdac_get_wcaps(hdev, cvt->nid); + chans = snd_hdac_get_wcaps_channels(chans); cvt->params.channels_min = 2; @@ -743,7 +741,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev, int count; unsigned int state; - if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_POWER) { if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { for (count = 0; count < 10; count++) { snd_hdac_codec_read(hdev, nid, 0, @@ -761,7 +759,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev, static void hdac_hdmi_set_amp(struct hdac_device *hdev, hda_nid_t nid, int val) { - if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) snd_hdac_codec_write(hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -1232,7 +1230,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev, >> DRM_ELD_VER_SHIFT; if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { - dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err_ratelimited(&hdev->dev, + "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1240,7 +1239,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev, DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; if (mnl > ELD_MAX_MNL) { - dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err_ratelimited(&hdev->dev, + "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1299,8 +1299,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", - __func__, pin->nid, port->id); + dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); /* * PCMs are not registered during device probe, so don't @@ -1431,122 +1431,6 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) } -static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_pcm *pcm; - struct hdac_hdmi_port *port; - struct hdac_hdmi_eld *eld; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = 0; - - pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); - if (!pcm) { - dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, - kcontrol->id.device); - return 0; - } - - if (list_empty(&pcm->port_list)) { - dev_dbg(component->dev, "%s: empty port list, device %d\n", - __func__, kcontrol->id.device); - return 0; - } - - mutex_lock(&hdmi->pin_mutex); - - list_for_each_entry(port, &pcm->port_list, head) { - eld = &port->eld; - - if (eld->eld_valid) { - uinfo->count = eld->eld_size; - break; - } - } - - mutex_unlock(&hdmi->pin_mutex); - - return 0; -} - -static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_pcm *pcm; - struct hdac_hdmi_port *port; - struct hdac_hdmi_eld *eld; - - memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); - - pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); - if (!pcm) { - dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, - kcontrol->id.device); - return 0; - } - - if (list_empty(&pcm->port_list)) { - dev_dbg(component->dev, "%s: empty port list, device %d\n", - __func__, kcontrol->id.device); - return 0; - } - - mutex_lock(&hdmi->pin_mutex); - - list_for_each_entry(port, &pcm->port_list, head) { - eld = &port->eld; - - if (!eld->eld_valid) - continue; - - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || - eld->eld_size > ELD_MAX_SIZE) { - mutex_unlock(&hdmi->pin_mutex); - - dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", - __func__, kcontrol->id.device, eld->eld_size); - snd_BUG(); - return -EINVAL; - } - - memcpy(ucontrol->value.bytes.data, eld->eld_buffer, - eld->eld_size); - break; - } - - mutex_unlock(&hdmi->pin_mutex); - - return 0; -} - -static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) -{ - struct snd_kcontrol *kctl; - struct snd_kcontrol_new hdmi_eld_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdac_hdmi_eld_ctl_info, - .get = hdac_hdmi_eld_ctl_get, - .device = pcm->pcm_id, - }; - - /* add ELD ctl with the device number corresponding to the PCM stream */ - kctl = snd_ctl_new1(&hdmi_eld_ctl, component); - if (!kctl) - return -ENOMEM; - - pcm->eld_ctl = kctl; - - return snd_ctl_add(component->card->snd_card, kctl); -} - static const struct snd_soc_dai_ops hdmi_dai_ops = { .startup = hdac_hdmi_pcm_open, .shutdown = hdac_hdmi_pcm_close, @@ -1648,8 +1532,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, unsigned int caps; unsigned int type; - caps = get_wcaps(hdev, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL)) continue; @@ -1754,186 +1638,6 @@ static struct drm_audio_component_audio_ops aops = { .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; -static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, - int device) -{ - struct snd_soc_pcm_runtime *rtd; - - for_each_card_rtds(card, rtd) { - if (rtd->pcm && (rtd->pcm->device == device)) - return rtd->pcm; - } - - return NULL; -} - -/* create jack pin kcontrols */ -static int create_fill_jack_kcontrols(struct snd_soc_card *card, - struct hdac_device *hdev) -{ - struct hdac_hdmi_pin *pin; - struct snd_kcontrol_new *kc; - char *name; - int i = 0, j; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct snd_soc_component *component = hdmi->component; - - kc = devm_kcalloc(component->dev, hdmi->num_ports, - sizeof(*kc), GFP_KERNEL); - - if (!kc) - return -ENOMEM; - - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - name = devm_kasprintf(component->dev, GFP_KERNEL, - "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - if (!name) - return -ENOMEM; - - kc[i].name = devm_kasprintf(component->dev, GFP_KERNEL, - "%s Switch", name); - if (!kc[i].name) - return -ENOMEM; - - kc[i].private_value = (unsigned long)name; - kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = 0; - kc[i].info = snd_soc_dapm_info_pin_switch; - kc[i].put = snd_soc_dapm_put_pin_switch; - kc[i].get = snd_soc_dapm_get_pin_switch; - i++; - } - } - - return snd_soc_add_card_controls(card, kc, i); -} - -int hdac_hdmi_jack_port_init(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm) -{ - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_device *hdev = hdmi->hdev; - struct hdac_hdmi_pin *pin; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *route; - char w_name[NAME_SIZE]; - int i = 0, j, ret; - - widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*widgets), GFP_KERNEL); - - if (!widgets) - return -ENOMEM; - - route = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*route), GFP_KERNEL); - if (!route) - return -ENOMEM; - - /* create Jack DAPM widget */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - - ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], - snd_soc_dapm_spk, NULL, - w_name, NULL, NULL, 0, NULL, 0); - if (ret < 0) - return ret; - - pin->ports[j].jack_pin = widgets[i].name; - pin->ports[j].dapm = dapm; - - /* add to route from Jack widget to output */ - hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, - NULL, pin->ports[j].output_pin, NULL); - - i++; - } - } - - /* Add Route from Jack widget to the output widget */ - ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_new_widgets(dapm->card); - if (ret < 0) - return ret; - - /* Add Jack Pin switch Kcontrol */ - ret = create_fill_jack_kcontrols(dapm->card, hdev); - - if (ret < 0) - return ret; - - /* default set the Jack Pin switch to OFF */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) - snd_soc_dapm_disable_pin(pin->ports[j].dapm, - pin->ports[j].jack_pin); - } - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); - -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, - struct snd_soc_jack *jack) -{ - struct snd_soc_component *component = dai->component; - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_device *hdev = hdmi->hdev; - struct hdac_hdmi_pcm *pcm; - struct snd_pcm *snd_pcm; - int err; - - /* - * this is a new PCM device, create new pcm and - * add to the pcm list - */ - pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); - if (!pcm) - return -ENOMEM; - pcm->pcm_id = device; - pcm->cvt = hdmi->dai_map[dai->id].cvt; - pcm->jack_event = 0; - pcm->jack = jack; - mutex_init(&pcm->lock); - INIT_LIST_HEAD(&pcm->port_list); - snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); - if (snd_pcm) { - err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); - if (err < 0) { - dev_err(&hdev->dev, - "chmap control add failed with err: %d for pcm: %d\n", - err, device); - return err; - } - } - - /* add control for ELD Bytes */ - err = hdac_hdmi_create_eld_ctl(component, pcm); - if (err < 0) { - dev_err(&hdev->dev, - "eld control add failed with err: %d for pcm: %d\n", - err, device); - return err; - } - - list_add_tail(&pcm->head, &hdmi->pcm_list); - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); - static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h deleted file mode 100644 index 493fa3b4ef75..000000000000 --- a/sound/soc/codecs/hdac_hdmi.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __HDAC_HDMI_H__ -#define __HDAC_HDMI_H__ - -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, - struct snd_soc_jack *jack); - -int hdac_hdmi_jack_port_init(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm); -#endif /* __HDAC_HDMI_H__ */ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 31121f9c18c9..e1933f733af1 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -943,7 +943,7 @@ static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, { if (jack_status != hcp->jack_status) { if (hcp->jack) - snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); + snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_AVOUT); hcp->jack_status = jack_status; } } @@ -964,7 +964,7 @@ static void plugged_cb(struct device *dev, bool plugged) else snd_show_eld(dev, &hcp->eld_parsed); } - hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); + hdmi_codec_jack_report(hcp, SND_JACK_AVOUT); } else { hdmi_codec_jack_report(hcp, 0); memset(hcp->eld, 0, sizeof(hcp->eld)); @@ -984,7 +984,7 @@ static int hdmi_codec_set_jack(struct snd_soc_component *component, * Report the initial jack status which may have been provided * by the parent hdmi driver while the hpd hook was registered. */ - snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT); + snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_AVOUT); return 0; } diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 55e90604bbaa..a03d4e5e7d14 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -1067,7 +1067,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); if (ret) { - dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + dev_err(&idt821034->spi->dev, "dir out gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); } @@ -1117,7 +1117,7 @@ static int idt821034_gpio_init(struct idt821034 *idt821034) idt821034->gpio_chip.direction_input = idt821034_chip_direction_input; idt821034->gpio_chip.direction_output = idt821034_chip_direction_output; idt821034->gpio_chip.get = idt821034_chip_gpio_get; - idt821034->gpio_chip.set_rv = idt821034_chip_gpio_set; + idt821034->gpio_chip.set = idt821034_chip_gpio_set; idt821034->gpio_chip.can_sleep = true; return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip, diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 45a6b83808b2..238dbdb46c18 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -619,7 +619,6 @@ static struct interp_sample_rate sr_val_tbl[] = { }; enum { - RX_MACRO_AIF_INVALID = 0, RX_MACRO_AIF1_PB, RX_MACRO_AIF2_PB, RX_MACRO_AIF3_PB, diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 27bae58f4072..1da34cb3505f 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -208,7 +208,6 @@ #define MCLK_FREQ 19200000 enum { - TX_MACRO_AIF_INVALID = 0, TX_MACRO_AIF1_CAP, TX_MACRO_AIF2_CAP, TX_MACRO_AIF3_CAP, @@ -2230,7 +2229,7 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx) } static const struct snd_soc_component_driver tx_macro_component_drv = { - .name = "RX-MACRO", + .name = "TX-MACRO", .probe = tx_macro_component_probe, .controls = tx_macro_snd_controls, .num_controls = ARRAY_SIZE(tx_macro_snd_controls), diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 74ada6e77526..a49551f3fb29 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -165,7 +165,6 @@ static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); enum { - VA_MACRO_AIF_INVALID = 0, VA_MACRO_AIF1_CAP, VA_MACRO_AIF2_CAP, VA_MACRO_AIF3_CAP, diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index c1fb71cfb5d0..da6adb3de21d 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -369,7 +369,6 @@ static struct interp_sample_rate int_mix_sample_rate_val[] = { }; enum { - WSA_MACRO_AIF_INVALID = 0, WSA_MACRO_AIF1_PB, WSA_MACRO_AIF_MIX1_PB, WSA_MACRO_AIF_VI, diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 950105e5bffd..25af78ab30d5 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -14,7 +14,7 @@ #include "max98363.h" -static struct reg_default max98363_reg[] = { +static const struct reg_default max98363_reg[] = { {MAX98363_R2021_ERR_MON_CTRL, 0x0}, {MAX98363_R2022_SPK_MON_THRESH, 0x0}, {MAX98363_R2023_SPK_MON_DURATION, 0x0}, @@ -188,7 +188,6 @@ static int max98363_io_init(struct sdw_slave *slave) max98363->hw_init = true; out: - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index 56c4ba1f3782..f58b8c8625a7 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -23,7 +23,7 @@ static const u32 max98373_i2c_cache_reg[] = { MAX98373_R20B6_BDE_CUR_STATE_READBACK, }; -static struct reg_default max98373_reg[] = { +static const struct reg_default max98373_reg[] = { {MAX98373_R2000_SW_RESET, 0x00}, {MAX98373_R2001_INT_RAW1, 0x00}, {MAX98373_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 6088278e6503..88ff215f52b3 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -26,7 +26,7 @@ static const u32 max98373_sdw_cache_reg[] = { MAX98373_R20B6_BDE_CUR_STATE_READBACK, }; -static struct reg_default max98373_reg[] = { +static const struct reg_default max98373_reg[] = { {MAX98373_R0040_SCP_INIT_STAT_1, 0x00}, {MAX98373_R0041_SCP_INIT_MASK_1, 0x00}, {MAX98373_R0042_SCP_INIT_STAT_2, 0x00}, @@ -458,7 +458,6 @@ static int max98373_io_init(struct sdw_slave *slave) max98373->first_hw_init = true; max98373->hw_init = true; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c index 99986090b4a6..076f15a9867e 100644 --- a/sound/soc/codecs/max98388.c +++ b/sound/soc/codecs/max98388.c @@ -18,7 +18,7 @@ #include #include "max98388.h" -static struct reg_default max98388_reg[] = { +static const struct reg_default max98388_reg[] = { {MAX98388_R2000_SW_RESET, 0x00}, {MAX98388_R2001_INT_RAW1, 0x00}, {MAX98388_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 76296176f948..a8a282ff9fc5 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -23,7 +23,7 @@ #include "max98390.h" -static struct reg_default max98390_reg_defaults[] = { +static const struct reg_default max98390_reg_defaults[] = { {MAX98390_INT_EN1, 0xf0}, {MAX98390_INT_EN2, 0x00}, {MAX98390_INT_EN3, 0x00}, diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index c1888cd83dbc..4b4e1fc98a6d 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -16,7 +16,7 @@ static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = { "dvddio", }; -static struct reg_default max98396_reg[] = { +static const struct reg_default max98396_reg[] = { {MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00}, {MAX98396_R2002_INT_RAW2, 0x00}, @@ -174,7 +174,7 @@ static struct reg_default max98396_reg[] = { {MAX98396_R21FF_REVISION_ID, 0x00}, }; -static struct reg_default max98397_reg[] = { +static const struct reg_default max98397_reg[] = { {MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00}, {MAX98396_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 6b6a7ece4cec..c94142768c81 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -35,7 +35,7 @@ struct max98504_priv { unsigned int brownout_release_rate; }; -static struct reg_default max98504_reg_defaults[] = { +static const struct reg_default max98504_reg_defaults[] = { { 0x01, 0}, { 0x02, 0}, { 0x03, 0}, diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index adf5a898c6df..2bf8976c1828 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -16,7 +16,7 @@ #include #include "max98520.h" -static struct reg_default max98520_reg[] = { +static const struct reg_default max98520_reg[] = { {MAX98520_R2000_SW_RESET, 0x00}, {MAX98520_R2001_STATUS_1, 0x00}, {MAX98520_R2002_STATUS_2, 0x00}, diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 55cc18451a2d..0e9b8970997c 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -19,7 +19,7 @@ #include #include "max98927.h" -static struct reg_default max98927_reg[] = { +static const struct reg_default max98927_reg[] = { {MAX98927_R0001_INT_RAW1, 0x00}, {MAX98927_R0002_INT_RAW2, 0x00}, {MAX98927_R0003_INT_RAW3, 0x00}, diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index b8905c03445e..c0c5b3c3e98b 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -1945,7 +1945,7 @@ static int peb2466_gpio_init(struct peb2466 *peb2466) peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input; peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output; peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get; - peb2466->gpio.gpio_chip.set_rv = peb2466_chip_gpio_set; + peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set; peb2466->gpio.gpio_chip.can_sleep = true; return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip, diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 88fc23a4999f..a9c000876be8 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -362,7 +362,6 @@ static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1017->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "hw_init complete\n"); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index ea708068f0e8..b6c224832a43 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -291,7 +291,6 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1308->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 960b6c4f5a66..01a977398864 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -302,7 +302,6 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1316->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 4eb636e0c9ed..70db5450d6d2 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -434,7 +434,6 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave) rt1318->first_hw_init = true; rt1318->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 015cc710e6dc..dcddc28e8856 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -109,6 +109,7 @@ static const struct reg_sequence rt1320_blind_write[] = { { 0x0000d540, 0x01 }, { 0xd172, 0x2a }, { 0xc5d6, 0x01 }, + { 0xd478, 0xff }, }; static const struct reg_sequence rt1320_vc_blind_write[] = { @@ -159,7 +160,7 @@ static const struct reg_sequence rt1320_vc_blind_write[] = { { 0xd471, 0x3a }, { 0xd474, 0x11 }, { 0xd475, 0x32 }, - { 0xd478, 0x64 }, + { 0xd478, 0xff }, { 0xd479, 0x20 }, { 0xd47a, 0x10 }, { 0xd47c, 0xff }, @@ -763,7 +764,6 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) rt1320->first_hw_init = true; rt1320->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 21a18012b4c0..f50e771db24b 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -193,7 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg) case RT5640_PRIV_DATA: case RT5640_PGM_REG_ARR1: case RT5640_PGM_REG_ARR3: - case RT5640_DUMMY2: + case RT5640_GCTL2: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: case RT5640_VENDOR_ID2: @@ -325,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg) case RT5640_HP_CALIB2: case RT5640_SV_ZCD1: case RT5640_SV_ZCD2: - case RT5640_DUMMY1: - case RT5640_DUMMY2: + case RT5640_GCTL1: + case RT5640_GCTL2: case RT5640_DUMMY3: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: @@ -423,7 +423,7 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 127, 0, adc_vol_tlv), - SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1, + SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1, RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1), SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, @@ -1969,7 +1969,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); - snd_soc_component_update_bits(component, RT5640_DUMMY1, + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0x1); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); @@ -1979,7 +1979,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_OFF: snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004); snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0); snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000); snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000); snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000); @@ -2328,12 +2328,12 @@ static void rt5640_jack_work(struct work_struct *work) jack_type |= SND_JACK_MICROPHONE; /* headphone jack */ - val = snd_soc_component_read(component, RT5640_DUMMY2); + val = snd_soc_component_read(component, RT5640_GCTL2); hda_hp_plugged = !(val & (0x1 << 11)); dev_dbg(component->dev, "headphone jack status %d\n", hda_hp_plugged); - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, (0x1 << 10), !hda_hp_plugged << 10); if (hda_hp_plugged) @@ -2504,7 +2504,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); - snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41); + snd_soc_component_write(component, RT5640_GCTL1, 0x3f41); rt5640_set_ovcd_params(component); @@ -2519,7 +2519,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | RT5640_JD2_EN); } else { @@ -2527,7 +2527,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR | RT5640_JD_P_INV); else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV | @@ -2596,7 +2596,7 @@ static void rt5640_enable_hda_jack_detect( snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); /* Select JD2 for Headphone */ - snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + snd_soc_component_update_bits(component, RT5640_GCTL2, 0x1100, 0x1100); /* Selecting GPIO01 as an interrupt */ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, @@ -2606,7 +2606,7 @@ static void rt5640_enable_hda_jack_detect( snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x400, 0x0); snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG, @@ -2668,7 +2668,7 @@ static int rt5640_probe(struct snd_soc_component *component) snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x0301, 0x0301); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00); @@ -2719,7 +2719,7 @@ static int rt5640_probe(struct snd_soc_component *component) RT5640_IN_DF2, RT5640_IN_DF2); if (device_property_read_bool(component->dev, "realtek,lout-differential")) - snd_soc_component_update_bits(component, RT5640_DUMMY1, + snd_soc_component_update_bits(component, RT5640_GCTL1, RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF); if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", @@ -2829,12 +2829,12 @@ static int rt5640_resume(struct snd_soc_component *component) if (rt5640->jack) { if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { snd_soc_component_update_bits(component, - RT5640_DUMMY2, 0x1100, 0x1100); + RT5640_GCTL2, 0x1100, 0x1100); } else { if (rt5640->jd_inverted) { if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) snd_soc_component_update_bits( - component, RT5640_DUMMY2, + component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | @@ -2843,7 +2843,7 @@ static int rt5640_resume(struct snd_soc_component *component) } else { if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) snd_soc_component_update_bits( - component, RT5640_DUMMY2, + component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK | RT5640_JD2_MASK, @@ -3013,6 +3013,11 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) } regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + if (val != RT5640_DEVICE_ID) { + usleep_range(60000, 100000); + regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + } + if (val != RT5640_DEVICE_ID) { dev_err(&i2c->dev, "Device with ID register %#x is not rt5640/39\n", val); @@ -3026,7 +3031,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); - regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, + regmap_update_bits(rt5640->regmap, RT5640_GCTL1, RT5640_MCLK_DET, RT5640_MCLK_DET); rt5640->hp_mute = true; diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 94b9a502f7f9..8a12cee76bdc 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -139,8 +139,8 @@ #define RT5640_SV_ZCD1 0xd9 #define RT5640_SV_ZCD2 0xda /* Dummy Register */ -#define RT5640_DUMMY1 0xfa -#define RT5640_DUMMY2 0xfb +#define RT5640_GCTL1 0xfa +#define RT5640_GCTL2 0xfb #define RT5640_DUMMY3 0xfc @@ -1986,7 +1986,7 @@ #define RT5640_M_MONO_ADC_R_SFT 12 #define RT5640_MCLK_DET (0x1 << 11) -/* General Control 1 (0xfb) */ +/* General Control 2 (0xfb) */ #define RT5640_IRQ_JD2_MASK (0x1 << 12) #define RT5640_IRQ_JD2_SFT 12 #define RT5640_IRQ_JD2_BP (0x0 << 12) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 08df87238eee..29a403526cd9 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -82,6 +82,7 @@ static const struct reg_sequence rt5650_init_list[] = { {0xf6, 0x0100}, {RT5645_PWR_ANLG1, 0x02}, {RT5645_IL_CMD3, 0x6728}, + {RT5645_PR_BASE + 0x3a, 0x0000}, }; static const struct reg_default rt5645_reg[] = { diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 82b92e83be4c..44c3a3b92f98 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1315,14 +1315,17 @@ static int rt5660_i2c_probe(struct i2c_client *i2c) regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1, RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL); - if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) + if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) { regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, RT5660_SEL_DMIC_DATA_MASK, RT5660_SEL_DMIC_DATA_GPIO2); - else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) + regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1, + RT5660_GP2_PIN_MASK, RT5660_GP2_PIN_DMIC1_SDA); + } else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) { regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, RT5660_SEL_DMIC_DATA_MASK, RT5660_SEL_DMIC_DATA_IN1P); + } } return devm_snd_soc_register_component(&i2c->dev, diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 69a0fb8d7f77..6b6c690a9e45 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4835,7 +4835,7 @@ static const struct gpio_chip rt5677_template_chip = { .label = RT5677_DRV_NAME, .owner = THIS_MODULE, .direction_output = rt5677_gpio_direction_out, - .set_rv = rt5677_gpio_set, + .set = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, .get = rt5677_gpio_get, .to_irq = rt5677_to_irq, diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index aa229894129b..055bea0a4a3b 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -474,7 +474,6 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) rt5682->first_hw_init = true; err_nodev: - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 7c88370e2dee..a0abd2ce0c1e 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2675,8 +2675,8 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, return rt5682->lrck[RT5682_AIF1]; } -static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, @@ -2689,13 +2689,13 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, * Only accept to set wclk rate to 44.1k or 48kHz. * It will force to 48kHz if not both. */ - if (rate != CLK_48 && rate != CLK_44) { + if (req->rate != CLK_48 && req->rate != CLK_44) { dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); - rate = CLK_48; + req->rate = CLK_48; } - return rate; + return 0; } static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2795,15 +2795,15 @@ static unsigned long rt5682_bclk_get_factor(unsigned long rate, return 256; } -static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !rt5682_clk_check(rt5682)) + if (!req->best_parent_rate || !rt5682_clk_check(rt5682)) return -EINVAL; /* @@ -2813,9 +2813,11 @@ static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = rt5682_bclk_get_factor(rate, *parent_rate); + factor = rt5682_bclk_get_factor(req->rate, req->best_parent_rate); - return *parent_rate * factor; + req->rate = req->best_parent_rate * factor; + + return 0; } static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2849,12 +2851,12 @@ static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { .prepare = rt5682_wclk_prepare, .unprepare = rt5682_wclk_unprepare, .recalc_rate = rt5682_wclk_recalc_rate, - .round_rate = rt5682_wclk_round_rate, + .determine_rate = rt5682_wclk_determine_rate, .set_rate = rt5682_wclk_set_rate, }, [RT5682_DAI_BCLK_IDX] = { .recalc_rate = rt5682_bclk_recalc_rate, - .round_rate = rt5682_bclk_round_rate, + .determine_rate = rt5682_bclk_determine_rate, .set_rate = rt5682_bclk_set_rate, }, }; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 73c4b3c31f8c..80b921695e7d 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -2610,8 +2610,8 @@ static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, return rt5682s->lrck[RT5682S_AIF1]; } -static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682s_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682s_priv *rt5682s = container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); @@ -2624,13 +2624,13 @@ static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate, * Only accept to set wclk rate to 44.1k or 48kHz. * It will force to 48kHz if not both. */ - if (rate != CLK_48 && rate != CLK_44) { + if (req->rate != CLK_48 && req->rate != CLK_44) { dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); - rate = CLK_48; + req->rate = CLK_48; } - return rate; + return 0; } static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2719,14 +2719,14 @@ static unsigned long rt5682s_bclk_get_factor(unsigned long rate, return 256; } -static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682s_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682s_priv *rt5682s = container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !rt5682s_clk_check(rt5682s)) + if (!req->best_parent_rate || !rt5682s_clk_check(rt5682s)) return -EINVAL; /* @@ -2736,9 +2736,11 @@ static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = rt5682s_bclk_get_factor(rate, *parent_rate); + factor = rt5682s_bclk_get_factor(req->rate, req->best_parent_rate); - return *parent_rate * factor; + req->rate = req->best_parent_rate * factor; + + return 0; } static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2769,12 +2771,12 @@ static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = { .prepare = rt5682s_wclk_prepare, .unprepare = rt5682s_wclk_unprepare, .recalc_rate = rt5682s_wclk_recalc_rate, - .round_rate = rt5682s_wclk_round_rate, + .determine_rate = rt5682s_wclk_determine_rate, .set_rate = rt5682s_wclk_set_rate, }, [RT5682S_DAI_BCLK_IDX] = { .recalc_rate = rt5682s_bclk_recalc_rate, - .round_rate = rt5682s_bclk_round_rate, + .determine_rate = rt5682s_bclk_determine_rate, .set_rate = rt5682s_bclk_set_rate, }, }; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 434b926f96c8..816117c13aea 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -338,7 +338,6 @@ static int rt700_set_jack_detect(struct snd_soc_component *component, rt700_jack_init(rt700); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -862,7 +861,7 @@ static int rt700_set_bias_level(struct snd_soc_component *component, default: break; } - dapm->bias_level = level; + return 0; } @@ -1230,7 +1229,6 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt700->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index dd6ccf17afd4..16c351779243 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -545,7 +545,6 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, rt711_sdca_jack_init(rt711); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1662,7 +1661,6 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt711->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 5446f9506a16..af3a49aee618 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -480,7 +480,6 @@ static int rt711_set_jack_detect(struct snd_soc_component *component, rt711_jack_init(rt711); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1331,7 +1330,6 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt711->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 4d044dfa3136..42f8f7b8bed0 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -236,7 +236,6 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt712->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 570c2af1245d..5b298db5f0f6 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -479,7 +479,6 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, rt712_sdca_jack_init(rt712); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1925,7 +1924,6 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); suspend: - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 7fb02654c16b..db7d43349d7d 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -1065,7 +1065,6 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt715->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 2cf461852091..7e1628a5c9d1 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -775,7 +775,7 @@ static int rt715_set_bias_level(struct snd_soc_component *component, default: break; } - dapm->bias_level = level; + return 0; } @@ -1129,7 +1129,6 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt715->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index 1c9f32e405cf..a4bd29d7220b 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -278,6 +278,8 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) RT721_ENT_FLOAT_CTL1, 0x4040); rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, RT721_ENT_FLOAT_CTL4, 0x1201); + rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL, + RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a); regmap_write(rt721->regmap, 0x2f58, 0x07); } @@ -327,7 +329,6 @@ static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, rt721_sdca_jack_init(rt721); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -430,6 +431,7 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol, unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; unsigned int adc_vol_flag = 0; const unsigned int interval_offset = 0xc0; + const unsigned int tendA = 0x200; const unsigned int tendB = 0xa00; if (strstr(ucontrol->id.name, "FU1E Capture Volume") || @@ -439,9 +441,16 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol, regmap_read(rt721->mbq_regmap, mc->reg, &read_l); regmap_read(rt721->mbq_regmap, mc->rreg, &read_r); - if (mc->shift == 8) /* boost gain */ + if (mc->shift == 8) { + /* boost gain */ ctl_l = read_l / tendB; - else { + } else if (mc->shift == 1) { + /* FU33 boost gain */ + if (read_l == 0x8000 || read_l == 0xfe00) + ctl_l = 0; + else + ctl_l = read_l / tendA + 1; + } else { if (adc_vol_flag) ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); else @@ -449,9 +458,16 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol, } if (read_l != read_r) { - if (mc->shift == 8) /* boost gain */ + if (mc->shift == 8) { + /* boost gain */ ctl_r = read_r / tendB; - else { /* ADC/DAC gain */ + } else if (mc->shift == 1) { + /* FU33 boost gain */ + if (read_r == 0x8000 || read_r == 0xfe00) + ctl_r = 0; + else + ctl_r = read_r / tendA + 1; + } else { /* ADC/DAC gain */ if (adc_vol_flag) ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); else @@ -1533,7 +1549,6 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt721->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h index 0a82c107b19a..71fac9cd8739 100644 --- a/sound/soc/codecs/rt721-sdca.h +++ b/sound/soc/codecs/rt721-sdca.h @@ -56,6 +56,7 @@ struct rt721_sdca_dmic_kctrl_priv { #define RT721_CBJ_CTRL 0x0a #define RT721_CAP_PORT_CTRL 0x0c #define RT721_CLASD_AMP_CTRL 0x0d +#define RT721_BOOST_CTRL 0x0f #define RT721_VENDOR_REG 0x20 #define RT721_RC_CALIB_CTRL 0x40 #define RT721_VENDOR_EQ_L 0x53 @@ -93,6 +94,9 @@ struct rt721_sdca_dmic_kctrl_priv { /* Index (NID:0dh) */ #define RT721_CLASD_AMP_2CH_CAL 0x14 +/* Index (NID:0fh) */ +#define RT721_BST_4CH_TOP_GATING_CTRL1 0x05 + /* Index (NID:20h) */ #define RT721_JD_PRODUCT_NUM 0x00 #define RT721_ANALOG_BIAS_CTL3 0x04 diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 609ca0d6c83a..70700bdb80a1 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -147,7 +147,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) } } -static struct regmap_sdw_mbq_cfg rt722_mbq_config = { +static const struct regmap_sdw_mbq_cfg rt722_mbq_config = { .mbq_size = rt722_sdca_mbq_size, }; diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index ac9588284a95..333611490ae3 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -339,7 +339,6 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, rt722_sdca_jack_init(rt722); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1559,7 +1558,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt722->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c index 242e8c975a62..b162824526d6 100644 --- a/sound/soc/codecs/rt9123.c +++ b/sound/soc/codecs/rt9123.c @@ -77,7 +77,6 @@ static int rt9123_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontro /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -140,7 +139,6 @@ static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele if (ret < 0) dev_err(dev, "Failed to get control (%d)\n", ret); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; } @@ -168,7 +166,6 @@ static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele if (ret < 0) dev_err(dev, "Failed to put control (%d)\n", ret); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; } diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c new file mode 100644 index 000000000000..186904b31434 --- /dev/null +++ b/sound/soc/codecs/rtq9124.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rtq9124.c -- RTQ9124 ALSA SoC Codec driver +// +// Author: ChiYuan Huang + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTQ9124_REG_SDI_SEL 0x00 +#define RTQ9124_REG_SDO_SEL 0x01 +#define RTQ9124_REG_I2S_OPT 0x02 +#define RTQ9124_REG_AMP_OPT 0x03 +#define RTQ9124_REG_STATE_CTRL 0x04 +#define RTQ9124_REG_PWM_PHASE 0x05 +#define RTQ9124_REG_SIL_CTRL 0x06 +#define RTQ9124_REG_PWM_SS_OPT 0x07 +#define RTQ9124_REG_ERR_INT_0 0x10 +#define RTQ9124_REG_ERR_MASK6 0x26 +#define RTQ9124_REG_TDM_TX_CH0 0x32 +#define RTQ9124_REG_TDM_RX_CH0 0x34 +#define RTQ9124_REG_VOL_OPT 0x38 +#define RTQ9124_REG_DCR_TH 0x4B +#define RTQ9124_REG_ERR_TH 0x4C +#define RTQ9124_REG_PROT_EN 0x5B +#define RTQ9124_REG_PRJ_CODE 0xF9 + +#define RTQ9124_MASK_CS_DATA_INV BIT(9) +#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8) +#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4) +#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0) +#define RTQ9124_MASK_CH_STATE GENMASK(1, 0) +#define RTQ9124_MASK_SF_RESET BIT(15) + +#define RTQ9124_FIXED_VENID 0x9124 + +struct rtq9124_priv { + struct gpio_desc *enable; + unsigned int dai_fmt; + int tdm_slots; + int tdm_slot_width; +}; + +static int rtq9124_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int i, chan_state; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Change state to normal */ + chan_state = 0; + break; + case SND_SOC_DAPM_POST_PMD: + /* Change state to HiZ */ + chan_state = 1; + break; + default: + return -EINVAL; + } + + /* Before amp turn on, clear old events first */ + for (i = 0; !chan_state && i < 8; i++) + snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff); + + snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE, + chan_state); + + return 0; +} + +static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = { + { "Amp Drv", NULL, "HiFi Playback" }, + { "SPK", NULL, "Amp Drv" }, +}; + +static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0); +static const DECLARE_TLV_DB_RANGE(ana_tlv, + 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0), + 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0)); +static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" }; +static const struct soc_enum rtq9124_i2sch_select_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text); +static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" }; +static const struct soc_enum rtq9124_sdo_vselect_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text); +static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" }; +static const struct soc_enum rtq9124_pwm_freq_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text); +static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" }; +static const struct soc_enum rtq9124_out_angle_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text); +static const char * const sdo_select_text[] = { + "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin", + "DCR", +}; +static const struct soc_enum rtq9124_sdo_select_enum = + SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text); +static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" }; +static const struct soc_enum rtq9124_ulqm_dcvt_select_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text); + +static const struct snd_kcontrol_new rtq9124_controls[] = { + SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv), + SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum), + SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum), + SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum), + SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum), + SOC_ENUM("SDO Select", rtq9124_sdo_select_enum), + SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum), + SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0), + SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0), +}; + +static int rtq9124_comp_probe(struct snd_soc_component *comp) +{ + /* CS Data INV */ + snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1); + + /* RTLD */ + snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30); + snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff); + snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc); + snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0); + + return 0; +} + +static const struct snd_soc_component_driver rtq9124_comp_driver = { + .probe = rtq9124_comp_probe, + .controls = rtq9124_controls, + .num_controls = ARRAY_SIZE(rtq9124_controls), + .dapm_widgets = rtq9124_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets), + .dapm_routes = rtq9124_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + + rtq9124->dai_fmt = fmt; + return 0; +} + +static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + struct device *dev = dai->dev; + unsigned int byte_loc, i; + + dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots, + slot_width, tx_mask, rx_mask); + + if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) { + dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width); + return -EINVAL; + } + + if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) { + dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots); + return -EINVAL; + } + + if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) { + dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots); + return -EINVAL; + } + + /* Configure tx channel data location */ + for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) { + byte_loc = (ffs(tx_mask) - 1) * slot_width / 8; + snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc); + } + + /* Configure rx channel data location */ + byte_loc = (ffs(rx_mask) - 1) * slot_width / 8; + snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc); + + rtq9124->tdm_slots = slots; + rtq9124->tdm_slot_width = slot_width; + + return 0; +} + +static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *param, struct snd_soc_dai *dai) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + unsigned int fmtval, width, slot_width, bitrate; + struct device *dev = dai->dev; + unsigned int audfmt, audbit; + + fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt); + if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && + fmtval != SND_SOC_DAIFMT_DSP_B) { + dev_err(dev, "TDM only can support DSP_A or DSP_B format\n"); + return -EINVAL; + } + + switch (fmtval) { + case SND_SOC_DAIFMT_I2S: + audfmt = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + audfmt = 1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audfmt = 2; + break; + case SND_SOC_DAIFMT_DSP_B: + audfmt = rtq9124->tdm_slots ? 7 : 3; + break; + case SND_SOC_DAIFMT_DSP_A: + audfmt = rtq9124->tdm_slots ? 15 : 11; + break; + default: + dev_err(dev, "Unsupported format %d\n", fmtval); + return -EINVAL; + } + + switch (width = params_width(param)) { + case 16: + audbit = 0; + break; + case 20: + audbit = 1; + break; + case 24: + case 32: + audbit = 3; + break; + default: + dev_err(dev, "Unsupported width %d\n", width); + return -EINVAL; + } + + if (rtq9124->tdm_slots) { + slot_width = params_physical_width(param); + if (slot_width > rtq9124->tdm_slot_width) { + dev_err(dev, "Slot width is larger than TDM slot width\n"); + return -EINVAL; + } + + bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param); + if (bitrate > 24576000) { + dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate); + return -EINVAL; + } + } + + snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt); + snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit); + + return 0; +} + +static const struct snd_soc_dai_ops rtq9124_dai_ops = { + .set_fmt = rtq9124_dai_set_format, + .set_tdm_slot = rtq9124_dai_set_tdm_slot, + .hw_params = rtq9124_dai_hw_params, +}; + +static struct snd_soc_dai_driver rtq9124_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rtq9124_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static bool rtq9124_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x17: + case 0x20 ... 0x27: + case 0x30 ... 0x3D: + case 0x40 ... 0x68: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x09: + case 0x0C ... 0x0E: + case 0x10 ... 0x17: + case 0x20 ... 0x27: + case 0x30: + case 0x32 ... 0x3D: + case 0x40 ... 0x4E: + case 0x50 ... 0x68: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0A ... 0x0B: + case 0x0F ... 0x17: + case 0x31: + case 0x4F: + case 0x51: + case 0x53 ... 0x57: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static inline u8 rtq9124_get_reg_len(unsigned int reg) +{ + return (reg >= 0x40 && reg <= 0x47) ? 4 : 2; +} + +static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf, + size_t val_size) +{ + struct i2c_client *i2c = context; + u8 reg = *(u8 *)reg_buf; + u8 size = rtq9124_get_reg_len(reg); + u32 *val = val_buf; + int ret; + + ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf); + if (ret < 0) + return ret; + else if (ret != size) + return -EIO; + + *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf); + + return 0; +} + +static int rtq9124_regmap_write(void *context, const void *data, size_t count) +{ + struct i2c_client *i2c = context; + u8 reg = *(u8 *)data, *vbuf; + u8 size = rtq9124_get_reg_len(reg); + __be16 val16 = cpu_to_be16p(data + 1); + __be32 val32 = cpu_to_be32p(data + 1); + + vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16; + return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf); +} + +static const struct regmap_config rtq9124_regmap_config = { + .name = "rtq9124", + .reg_bits = 8, + .val_bits = 32, + .read = rtq9124_regmap_read, + .write = rtq9124_regmap_write, + .readable_reg = rtq9124_readable_reg, + .writeable_reg = rtq9124_writeable_reg, + .volatile_reg = rtq9124_volatile_reg, + .cache_type = REGCACHE_MAPLE, + .num_reg_defaults_raw = 0xFD + 1, + .use_single_read = 1, + .use_single_write = 1, +}; + +static const struct reg_sequence rtq9124_init_regs[] = { + { 0xfb, 0x0065 }, + { 0x93, 0x2000 }, + { 0xfb, 0x0000 }, +}; + +static int rtq9124_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rtq9124_priv *rtq9124; + struct regmap *regmap; + int ret; + + rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL); + if (!rtq9124) + return -ENOMEM; + + rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(rtq9124->enable)) + return PTR_ERR(rtq9124->enable); + else if (rtq9124->enable) + usleep_range(6000, 7000); + else + dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n"); + + /* Check vendor id information */ + ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read project code\n"); + else if (ret != RTQ9124_FIXED_VENID) + return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret); + + /* Trigger RG reset before regmap init */ + ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET); + if (ret) + return dev_err_probe(dev, ret, "Failed to trigger RG reset\n"); + + /* Need to wait 10ms for the reset to complete */ + usleep_range(10000, 11000); + + regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs)); + if (ret) + return dev_err_probe(dev, ret, "Failed to register regmap patch\n"); + + i2c_set_clientdata(i2c, rtq9124); + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); + + return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1); +} + +#ifdef CONFIG_PM +static int rtq9124_runtime_suspend(struct device *dev) +{ + struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + + if (rtq9124->enable) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + gpiod_set_value(rtq9124->enable, 0); + } + + return 0; +} + +static int rtq9124_runtime_resume(struct device *dev) +{ + struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; + + if (rtq9124->enable) { + gpiod_set_value(rtq9124->enable, 1); + usleep_range(6000, 7000); + + regcache_cache_only(regmap, false); + ret = regcache_sync(regmap); + if (ret) + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops rtq9124_dev_pm_ops = { + SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id rtq9124_device_id[] = { + { .compatible = "richtek,rtq9124" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtq9124_device_id); +#endif + +static struct i2c_driver rtq9124_driver = { + .driver = { + .name = "rtq9124", + .of_match_table = of_match_ptr(rtq9124_device_id), + .pm = pm_ptr(&rtq9124_dev_pm_ops), + }, + .probe = rtq9124_probe, +}; +module_i2c_driver(rtq9124_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("ASoC RTQ9124 Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index b3d401ada176..6a601e7134ea 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1749,7 +1749,7 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil sma1307->set.header_size * sizeof(int)); if ((sma1307->set.checksum >> 8) != SMA1307_SETTING_CHECKSUM) { - dev_err(sma1307->dev, "%s: failed by dismatch \"%s\"\n", + dev_err(sma1307->dev, "%s: checksum failed \"%s\"\n", __func__, setting_file); sma1307->set.status = false; return; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index b56dd279d90a..43449d7c2584 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -724,7 +724,6 @@ static int tas2552_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_sync_autosuspend(&client->dev); dev_set_drvdata(&client->dev, data); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c40d8f754d89..0e09d794516f 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -14,9 +14,6 @@ // #include -#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C -#include -#endif #include #include #include @@ -911,12 +908,12 @@ static const struct snd_kcontrol_new tasdevice_cali_controls[] = { }; static const struct snd_kcontrol_new tas2781_snd_controls[] = { - SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, - tas2781_amp_putvol, amp_vol_tlv), - SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + tas2781_amp_putvol, tas2781_amp_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2781_DVC_LVL, 0, 0, 200, 1, tas2781_digital_getvol, - tas2781_digital_putvol, dvc_tlv), + tas2781_digital_putvol, tas2781_dvc_tlv), }; static const struct snd_kcontrol_new tas2781_cali_controls[] = { @@ -1483,7 +1480,7 @@ static ssize_t acoustic_ctl_write(struct file *file, return PTR_ERR(src); if (src[0] > max_pkg_len && src[0] != count) { - dev_err(priv->dev, "pkg(%u), max(%u), count(%u) dismatch.\n", + dev_err(priv->dev, "pkg(%u), max(%u), count(%u) mismatch.\n", src[0], max_pkg_len, (unsigned int)count); ret = 0; goto exit; diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 6bf37c77f0a7..41d73f470f8b 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -839,6 +839,56 @@ static const struct tas571x_chip tas5733_chip = { .vol_reg_size = 2, }; +static const struct reg_default tas5753_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x41}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0x03ff}, + {TAS571X_CH1_VOL_REG, 0x00c0}, + {TAS571X_CH2_VOL_REG, 0x00c0}, + {TAS571X_CH3_VOL_REG, 0x00c0}, + {TAS571X_VOL_CFG_REG, 0xf0}, + {TAS571X_MODULATION_LIMIT_REG, 0x01}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x57}, + {TAS571X_INPUT_MUX_REG, 0x00017772}, + {TAS571X_PWM_MUX_REG, 0x01021345}, + {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00}, + {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000}, + {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00}, + {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000}, +}; + +static const struct regmap_config tas5753_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5753_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5753_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +static const struct tas571x_chip tas5753_chip = { + .supply_names = tas5721_supply_names, + .num_supply_names = ARRAY_SIZE(tas5721_supply_names), + .controls = tas5733_controls, + .num_controls = ARRAY_SIZE(tas5733_controls), + .regmap_config = &tas5753_regmap_config, + .vol_reg_size = 2, +}; + static const struct tas571x_chip tas5721_chip = { .supply_names = tas5721_supply_names, .num_supply_names = ARRAY_SIZE(tas5721_supply_names), @@ -1007,6 +1057,7 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = { { .compatible = "ti,tas5719", .data = &tas5717_chip, }, { .compatible = "ti,tas5721", .data = &tas5721_chip, }, { .compatible = "ti,tas5733", .data = &tas5733_chip, }, + { .compatible = "ti,tas5753", .data = &tas5753_chip, }, { } }; MODULE_DEVICE_TABLE(of, tas571x_of_match); @@ -1018,6 +1069,7 @@ static const struct i2c_device_id tas571x_i2c_id[] = { { "tas5719", (kernel_ulong_t) &tas5717_chip }, { "tas5721", (kernel_ulong_t) &tas5721_chip }, { "tas5733", (kernel_ulong_t) &tas5733_chip }, + { "tas5753", (kernel_ulong_t) &tas5753_chip }, { } }; MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 1035ba17dc5d..258fbcaf345a 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1052,7 +1052,7 @@ static const struct gpio_chip adc3xxx_gpio_chip = { .owner = THIS_MODULE, .request = adc3xxx_gpio_request, .direction_output = adc3xxx_gpio_direction_out, - .set_rv = adc3xxx_gpio_set, + .set = adc3xxx_gpio_set, .get = adc3xxx_gpio_get, .can_sleep = 1, }; diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 54ea4bc58c27..7399080f8580 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -9,27 +9,26 @@ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. */ +#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 "tlv320aic32x4.h" @@ -38,7 +37,7 @@ struct aic32x4_priv { u32 power_cfg; u32 micpga_routing; bool swapdacs; - int rstn_gpio; + struct gpio_desc *rstn_gpio; const char *mclk_name; struct regulator *supply_ldo; @@ -1236,7 +1235,14 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; - aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ + aic32x4->rstn_gpio = devm_gpiod_get_optional(aic32x4->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(aic32x4->rstn_gpio)) { + return dev_err_probe(aic32x4->dev, PTR_ERR(aic32x4->rstn_gpio), + "Failed to get reset gpio\n"); + } else { + gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); + } if (of_property_read_u32_array(np, "aic32x4-gpio-func", aic32x4_setup->gpio_func, 5) >= 0) @@ -1346,7 +1352,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, enum aic32x4_type type) { struct aic32x4_priv *aic32x4; - struct aic32x4_pdata *pdata = dev->platform_data; struct device_node *np = dev->of_node; int ret; @@ -1363,13 +1368,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, aic32x4); - if (pdata) { - aic32x4->power_cfg = pdata->power_cfg; - aic32x4->swapdacs = pdata->swapdacs; - aic32x4->micpga_routing = pdata->micpga_routing; - aic32x4->rstn_gpio = pdata->rstn_gpio; - aic32x4->mclk_name = "mclk"; - } else if (np) { + if (np) { ret = aic32x4_parse_dt(aic32x4, np); if (ret) { dev_err(dev, "Failed to parse DT node\n"); @@ -1379,26 +1378,20 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, aic32x4->power_cfg = 0; aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; - aic32x4->rstn_gpio = -1; + aic32x4->rstn_gpio = NULL; aic32x4->mclk_name = "mclk"; } - if (gpio_is_valid(aic32x4->rstn_gpio)) { - ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, - GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); - if (ret != 0) - return ret; - } - ret = aic32x4_setup_regulators(dev, aic32x4); if (ret) { dev_err(dev, "Failed to setup regulators\n"); return ret; } - if (gpio_is_valid(aic32x4->rstn_gpio)) { + if (aic32x4->rstn_gpio) { ndelay(10); - gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); + /* deassert reset */ + gpiod_set_value_cansleep(aic32x4->rstn_gpio, 0); mdelay(1); } diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 4b7c3d6080a1..26ebcdadeb7d 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -825,7 +825,6 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_unlock(&mbhc->lock); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1319,7 +1318,6 @@ static void wcd_correct_swch_plug(struct work_struct *work) if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(component, true); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 5e19e813748d..1c050b8c19de 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -312,7 +312,6 @@ struct wcd9335_codec { u32 num_rx_port; u32 num_tx_port; - int sido_input_src; enum wcd9335_sido_voltage sido_voltage; struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; @@ -4725,8 +4724,6 @@ static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = { static void wcd9335_enable_sido_buck(struct snd_soc_component *component) { - struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); - snd_soc_component_update_bits(component, WCD9335_ANA_RCO, WCD9335_ANA_RCO_BG_EN_MASK, WCD9335_ANA_RCO_BG_ENABLE); @@ -4740,7 +4737,6 @@ static void wcd9335_enable_sido_buck(struct snd_soc_component *component) WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT); /* 100us sleep needed after VREF settings */ usleep_range(100, 110); - wcd->sido_input_src = SIDO_SOURCE_RCO_BG; } static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp) @@ -4871,7 +4867,6 @@ static int wcd9335_probe(struct wcd9335_codec *wcd) memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs)); memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs)); - wcd->sido_input_src = SIDO_SOURCE_INTERNAL; wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; return devm_snd_soc_register_component(dev, &wcd9335_component_drv, diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index fa69817c97ea..1bb7e1dc7e6b 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -537,8 +537,6 @@ struct wcd934x_codec { int rate; u32 version; u32 hph_mode; - int num_rx_port; - int num_tx_port; u32 tx_port_value[WCD934X_TX_MAX]; u32 rx_port_value[WCD934X_RX_MAX]; int sido_input_src; @@ -1928,13 +1926,11 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai, return -EINVAL; } - wcd->num_rx_port = rx_num; for (i = 0; i < rx_num; i++) { wcd->rx_chs[i].ch_num = rx_slot[i]; INIT_LIST_HEAD(&wcd->rx_chs[i].list); } - wcd->num_tx_port = tx_num; for (i = 0; i < tx_num; i++) { wcd->tx_chs[i].ch_num = tx_slot[i]; INIT_LIST_HEAD(&wcd->tx_chs[i].list); diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index b9df58b86ce9..3b0a8cc314e0 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -24,10 +24,8 @@ #include "wcd-mbhc-v2.h" #include "wcd937x.h" -enum { - CHIPID_WCD9370 = 0, - CHIPID_WCD9375 = 5, -}; +#define CHIPID_WCD9370 0x0 +#define CHIPID_WCD9375 0x5 /* Z value defined in milliohm */ #define WCD937X_ZDET_VAL_32 (32000) @@ -88,9 +86,7 @@ struct wcd937x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD937X_MAX_BULK_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD937X_MAX_MICBIAS]; @@ -113,6 +109,10 @@ struct wcd937x_priv { atomic_t ana_clk_count; }; +static const char * const wcd937x_supplies[] = { + "vdd-rxtx", "vdd-px", "vdd-mic-bias", "vdd-buck", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -2934,18 +2934,10 @@ static int wcd937x_probe(struct platform_device *pdev) cfg = &wcd937x->mbhc_cfg; cfg->swap_gnd_mic = wcd937x_swap_gnd_mic; - wcd937x->supplies[0].supply = "vdd-rxtx"; - wcd937x->supplies[1].supply = "vdd-px"; - wcd937x->supplies[2].supply = "vdd-mic-bias"; - wcd937x->supplies[3].supply = "vdd-buck"; - - ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd937x_supplies), + wcd937x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); - if (ret) - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd937x_dt_parse_micbias_info(dev, wcd937x); @@ -2962,13 +2954,13 @@ static int wcd937x_probe(struct platform_device *pdev) ret = wcd937x_add_slave_components(wcd937x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd937x_reset(wcd937x); ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -2978,25 +2970,17 @@ static int wcd937x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); - - return ret; } static void wcd937x_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); component_master_del(&pdev->dev, &wcd937x_comp_ops); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); - - regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); } #if defined(CONFIG_OF) diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 4ef57c496c37..3ab21bb5846e 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -487,7 +487,6 @@ #define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31) #define WCD937X_MAX_MICBIAS 3 -#define WCD937X_MAX_BULK_SUPPLY 4 #define WCD937X_MAX_SWR_CH_IDS 15 #define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index d9b61eab029a..711f373ece24 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -25,8 +25,10 @@ #include "wcd-mbhc-v2.h" #include "wcd938x.h" +#define CHIPID_WCD9380 0x0 +#define CHIPID_WCD9385 0x5 + #define WCD938X_MAX_MICBIAS (4) -#define WCD938X_MAX_SUPPLY (4) #define WCD938X_MBHC_MAX_BUTTONS (8) #define TX_ADC_MAX (4) @@ -73,11 +75,6 @@ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, snd_soc_get_volsw, \ wcd938x_ear_pa_put_gain, tlv_array) -enum { - WCD9380 = 0, - WCD9385 = 5, -}; - enum { /* INTR_CTRL_INT_MASK_0 */ WCD938X_IRQ_MBHC_BUTTON_PRESS_DET = 0, @@ -159,9 +156,7 @@ struct wcd938x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD938X_MAX_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD938X_MAX_MICBIAS]; @@ -170,7 +165,6 @@ struct wcd938x_priv { u32 tx_mode[TX_ADC_MAX]; int flyback_cur_det_disable; int ear_rx_path; - int variant; struct gpio_desc *reset_gpio; struct gpio_desc *us_euro_gpio; struct mux_control *us_euro_mux; @@ -188,6 +182,10 @@ struct wcd938x_priv { bool mux_setup_done; }; +static const char * const wcd938x_supplies[] = { + "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000); @@ -275,7 +273,7 @@ static const struct regmap_irq wcd938x_irqs[WCD938X_NUM_IRQS] = { REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), }; -static struct regmap_irq_chip wcd938x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd938x_regmap_irq_chip = { .name = "wcd938x", .irqs = wcd938x_irqs, .num_irqs = ARRAY_SIZE(wcd938x_irqs), @@ -3046,6 +3044,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev; struct device *dev = component->dev; unsigned long time_left; + unsigned int variant; int ret, i; time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, @@ -3061,9 +3060,9 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; - wcd938x->variant = snd_soc_component_read_field(component, - WCD938X_DIGITAL_EFUSE_REG_0, - WCD938X_ID_MASK); + variant = snd_soc_component_read_field(component, + WCD938X_DIGITAL_EFUSE_REG_0, + WCD938X_ID_MASK); wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X); if (IS_ERR(wcd938x->clsh_info)) { @@ -3117,24 +3116,24 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd938x->hphl_pdm_wd_int); disable_irq_nosync(wcd938x->aux_pdm_wd_int); - switch (wcd938x->variant) { - case WCD9380: + switch (variant) { + case CHIPID_WCD9380: ret = snd_soc_add_component_controls(component, wcd9380_snd_controls, ARRAY_SIZE(wcd9380_snd_controls)); if (ret < 0) { dev_err(component->dev, "%s: Failed to add snd ctrls for variant: %d\n", - __func__, wcd938x->variant); + __func__, variant); goto err_free_aux_pdm_wd_int; } break; - case WCD9385: + case CHIPID_WCD9385: ret = snd_soc_add_component_controls(component, wcd9385_snd_controls, ARRAY_SIZE(wcd9385_snd_controls)); if (ret < 0) { dev_err(component->dev, "%s: Failed to add snd ctrls for variant: %d\n", - __func__, wcd938x->variant); + __func__, variant); goto err_free_aux_pdm_wd_int; } break; @@ -3292,20 +3291,10 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; - wcd938x->supplies[0].supply = "vdd-rxtx"; - wcd938x->supplies[1].supply = "vdd-io"; - wcd938x->supplies[2].supply = "vdd-buck"; - wcd938x->supplies[3].supply = "vdd-mic-bias"; - - ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd938x_supplies), + wcd938x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - if (ret) { - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); - } + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd938x_dt_parse_micbias_info(dev, wcd938x); @@ -3569,13 +3558,13 @@ static int wcd938x_probe(struct platform_device *pdev) ret = wcd938x_add_slave_components(wcd938x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd938x_reset(wcd938x); ret = component_master_add_with_match(dev, &wcd938x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -3585,12 +3574,6 @@ static int wcd938x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); - - return ret; } static void wcd938x_remove(struct platform_device *pdev) @@ -3606,9 +3589,6 @@ static void wcd938x_remove(struct platform_device *pdev) if (wcd938x->us_euro_mux && wcd938x->mux_setup_done) mux_control_deselect(wcd938x->us_euro_mux); - - regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); } #if defined(CONFIG_OF) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 067d23c7ecf9..64f082e474c1 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -32,11 +32,18 @@ #include "wcd939x.h" #define WCD939X_MAX_MICBIAS (4) -#define WCD939X_MAX_SUPPLY (4) #define WCD939X_MBHC_MAX_BUTTONS (8) #define TX_ADC_MAX (4) #define WCD_MBHC_HS_V_MAX 1600 +#define CHIPID_WCD9390 0x0 +#define CHIPID_WCD9395 0x5 + +/* Version major: 1.x */ +#define CHIPID_WCD939X_VER_MAJOR_1 0x0 +/* Version minor: x.1 */ +#define CHIPID_WCD939X_VER_MINOR_1 0x3 + enum { WCD939X_VERSION_1_0 = 0, WCD939X_VERSION_1_1, @@ -86,11 +93,6 @@ enum { /* Z value compared in milliOhm */ #define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024) -enum { - WCD9390 = 0, - WCD9395 = 5, -}; - enum { /* INTR_CTRL_INT_MASK_0 */ WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0, @@ -190,9 +192,7 @@ struct wcd939x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD939X_MAX_MICBIAS]; @@ -213,6 +213,10 @@ struct wcd939x_priv { bool ldoh; }; +static const char * const wcd939x_supplies[] = { + "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", "vdd-px", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -1482,7 +1486,7 @@ static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, if (mode_val == wcd939x->hph_mode) return 0; - if (wcd939x->variant == WCD9390) { + if (wcd939x->variant == CHIPID_WCD9390) { switch (mode_val) { case CLS_H_NORMAL: case CLS_H_LP: @@ -3064,7 +3068,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd939x->ear_pdm_wd_int); switch (wcd939x->variant) { - case WCD9390: + case CHIPID_WCD9390: ret = snd_soc_add_component_controls(component, wcd9390_snd_controls, ARRAY_SIZE(wcd9390_snd_controls)); if (ret < 0) { @@ -3074,7 +3078,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component) goto err_free_ear_pdm_wd_int; } break; - case WCD9395: + case CHIPID_WCD9395: ret = snd_soc_add_component_controls(component, wcd9395_snd_controls, ARRAY_SIZE(wcd9395_snd_controls)); if (ret < 0) { @@ -3239,25 +3243,14 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device int ret; wcd939x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(wcd939x->reset_gpio)) { - ret = PTR_ERR(wcd939x->reset_gpio); - return dev_err_probe(dev, ret, "Failed to get reset gpio\n"); - } + if (IS_ERR(wcd939x->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd939x->reset_gpio), + "Failed to get reset gpio\n"); - wcd939x->supplies[0].supply = "vdd-rxtx"; - wcd939x->supplies[1].supply = "vdd-io"; - wcd939x->supplies[2].supply = "vdd-buck"; - wcd939x->supplies[3].supply = "vdd-mic-bias"; - - ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd939x_supplies), + wcd939x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - if (ret) { - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); - } + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd939x_dt_parse_micbias_info(dev, wcd939x); @@ -3461,8 +3454,8 @@ static int wcd939x_bind(struct device *dev) regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1); regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1); - if (id1 == 0) - version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); + if (id1 == CHIPID_WCD939X_VER_MAJOR_1) + version = ((status1 & CHIPID_WCD939X_VER_MINOR_1) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); else version = WCD939X_VERSION_2_0; @@ -3629,17 +3622,17 @@ static int wcd939x_probe(struct platform_device *pdev) ret = wcd939x_add_typec(wcd939x, dev); if (ret) - goto err_disable_regulators; + return ret; ret = wcd939x_add_slave_components(wcd939x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd939x_reset(wcd939x); ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -3649,27 +3642,17 @@ static int wcd939x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); - - return ret; } static void wcd939x_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); component_master_del(dev, &wcd939x_comp_ops); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); - - regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); } #if defined(CONFIG_OF) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index fb5ed4ba7f60..2d0a20f2fd8c 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2290,7 +2290,7 @@ static const struct gpio_chip wm5100_template_chip = { .label = "wm5100", .owner = THIS_MODULE, .direction_output = wm5100_gpio_direction_out, - .set_rv = wm5100_gpio_set, + .set = wm5100_gpio_set, .direction_input = wm5100_gpio_direction_in, .get = wm5100_gpio_get, .can_sleep = 1, diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c index 403e513f3fa8..6b1a7450b0ac 100644 --- a/sound/soc/codecs/wm8524.c +++ b/sound/soc/codecs/wm8524.c @@ -21,7 +21,7 @@ #include #include -#define WM8524_NUM_RATES 7 +#define WM8524_NUM_RATES 12 /* codec private data */ struct wm8524_priv { @@ -46,7 +46,7 @@ static const struct snd_soc_dapm_route wm8524_dapm_routes[] = { static const struct { int value; int ratio; -} lrclk_ratios[WM8524_NUM_RATES] = { +} lrclk_ratios[] = { { 1, 128 }, { 2, 192 }, { 3, 256 }, @@ -63,17 +63,12 @@ static int wm8524_startup(struct snd_pcm_substream *substream, struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); /* The set of sample rates that can be supported depends on the - * MCLK supplied to the CODEC - enforce this. + * MCLK supplied to the CODEC. */ - if (!wm8524->sysclk) { - dev_err(component->dev, - "No MCLK configured, call set_sysclk() on init\n"); - return -EINVAL; - } - - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &wm8524->rate_constraint); + if (wm8524->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &wm8524->rate_constraint); gpiod_set_value_cansleep(wm8524->mute, 1); @@ -97,9 +92,11 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, unsigned int val; int i, j = 0; - wm8524->sysclk = freq; - wm8524->rate_constraint.count = 0; + wm8524->sysclk = freq; + if (!wm8524->sysclk) + return 0; + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { val = freq / lrclk_ratios[i].ratio; /* Check that it's a standard rate since core can't @@ -108,9 +105,13 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, */ switch (val) { case 8000: + case 11025: + case 16000: + case 22050: case 32000: case 44100: case 48000: + case 64000: case 88200: case 96000: case 176400: @@ -157,6 +158,33 @@ static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; } +static int wm8524_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + int i; + + /* If sysclk is not configured, no need to check the rate */ + if (!wm8524->sysclk) + return 0; + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8524->rate_constraint.count; i++) { + if (wm8524->rate_constraint.list[i] == params_rate(params)) + break; + } + + if (i == wm8524->rate_constraint.count) { + dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8524->sysclk); + return -EINVAL; + } + + return 0; +} + #define WM8524_RATES SNDRV_PCM_RATE_8000_192000 #define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ @@ -169,6 +197,7 @@ static const struct snd_soc_dai_ops wm8524_dai_ops = { .set_sysclk = wm8524_set_dai_sysclk, .set_fmt = wm8524_set_fmt, .mute_stream = wm8524_mute_stream, + .hw_params = wm8524_hw_params, }; static struct snd_soc_dai_driver wm8524_dai = { diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 2ed9f493d507..f7d726e3052c 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1843,7 +1843,7 @@ static const struct gpio_chip wm8903_template_chip = { .direction_input = wm8903_gpio_direction_in, .get = wm8903_gpio_get, .direction_output = wm8903_gpio_direction_out, - .set_rv = wm8903_gpio_set, + .set = wm8903_gpio_set, .can_sleep = 1, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index d69aa8b15629..08c8ec3aeb44 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -82,6 +82,7 @@ struct wm8962_priv { #endif int irq; + bool master_flag; }; /* We can't use the same notifier block for more than one supply and @@ -2715,6 +2716,7 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); int aif0 = 0; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -2761,9 +2763,11 @@ static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + wm8962->master_flag = false; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBP_CFP: aif0 |= WM8962_MSTR; + wm8962->master_flag = true; break; case SND_SOC_DAIFMT_CBC_CFC: break; @@ -3442,7 +3446,7 @@ static const struct gpio_chip wm8962_template_chip = { .owner = THIS_MODULE, .request = wm8962_gpio_request, .direction_output = wm8962_gpio_direction_out, - .set_rv = wm8962_gpio_set, + .set = wm8962_gpio_set, .can_sleep = 1, }; @@ -3903,6 +3907,9 @@ static int wm8962_runtime_resume(struct device *dev) WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK, WM8962_BIAS_ENA | 0x180); + if (wm8962->master_flag) + regmap_update_bits(wm8962->regmap, WM8962_AUDIO_INTERFACE_0, + WM8962_MSTR, WM8962_MSTR); msleep(5); return 0; @@ -3916,6 +3923,10 @@ static int wm8962_runtime_suspend(struct device *dev) { struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + if (wm8962->master_flag) + regmap_update_bits(wm8962->regmap, WM8962_AUDIO_INTERFACE_0, + WM8962_MSTR, 0); + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0); diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index e364d0da9044..459b39998307 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2186,7 +2186,7 @@ static const struct gpio_chip wm8996_template_chip = { .label = "wm8996", .owner = THIS_MODULE, .direction_output = wm8996_gpio_direction_out, - .set_rv = wm8996_gpio_set, + .set = wm8996_gpio_set, .direction_input = wm8996_gpio_direction_in, .get = wm8996_gpio_get, .can_sleep = 1, diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 3c580faab3b7..8a1d5cc75d6c 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -783,16 +783,19 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, char **coeff_filename) { const char *system_name = dsp->system_name; - const char *asoc_component_prefix = dsp->component->name_prefix; + const char *suffix = dsp->component->name_prefix; int ret = 0; - if (system_name && asoc_component_prefix) { + if (dsp->fwf_suffix) + suffix = dsp->fwf_suffix; + + if (system_name && suffix) { if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, cirrus_dir, system_name, - asoc_component_prefix, "wmfw")) { + suffix, "wmfw")) { wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, cirrus_dir, system_name, - asoc_component_prefix, "bin"); + suffix, "bin"); return 0; } } @@ -801,10 +804,10 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, cirrus_dir, system_name, NULL, "wmfw")) { - if (asoc_component_prefix) + if (suffix) wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, cirrus_dir, system_name, - asoc_component_prefix, "bin"); + suffix, "bin"); if (!*coeff_firmware) wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, @@ -816,10 +819,10 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, /* Check system-specific bin without wmfw before falling back to generic */ if (dsp->wmfw_optional && system_name) { - if (asoc_component_prefix) + if (suffix) wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, cirrus_dir, system_name, - asoc_component_prefix, "bin"); + suffix, "bin"); if (!*coeff_firmware) wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, @@ -850,7 +853,7 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n", cirrus_dir, dsp->part, dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name, - wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix); + wm_adsp_fw[dsp->fw].file, system_name, suffix); return -ENOENT; } @@ -997,11 +1000,17 @@ int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) return ret; } + if (dsp->bin_mandatory && !coeff_firmware) { + ret = -ENOENT; + goto err; + } + ret = cs_dsp_power_up(&dsp->cs_dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, wm_adsp_fw_text[dsp->fw]); +err: wm_adsp_release_firmware_files(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index edc5b02ae765..25210d404bf1 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -29,12 +29,14 @@ struct wm_adsp { const char *part; const char *fwf_name; const char *system_name; + const char *fwf_suffix; struct snd_soc_component *component; unsigned int sys_config_size; int fw; bool wmfw_optional; + bool bin_mandatory; struct work_struct boot_work; int (*control_add)(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 6627d2da3722..636e59abc377 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -202,7 +202,7 @@ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ snd_soc_get_volsw, wsa881x_put_pa_gain, tlv_array) -static struct reg_default wsa881x_defaults[] = { +static const struct reg_default wsa881x_defaults[] = { { WSA881X_CHIP_ID0, 0x00 }, { WSA881X_CHIP_ID1, 0x00 }, { WSA881X_CHIP_ID2, 0x00 }, @@ -346,7 +346,7 @@ static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { }; /* Default register reset values for WSA881x rev 2.0 */ -static struct reg_sequence wsa881x_rev_2_0[] = { +static const struct reg_sequence wsa881x_rev_2_0[] = { { WSA881X_RESET_CTL, 0x00, 0x00 }, { WSA881X_TADC_VALUE_CTL, 0x01, 0x00 }, { WSA881X_INTR_MASK, 0x1B, 0x00 }, @@ -775,7 +775,6 @@ static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, usleep_range(1000, 1010); } - pm_runtime_mark_last_busy(comp->dev); pm_runtime_put_autosuspend(comp->dev); return 1; diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index f04d99c66f33..188363b03b93 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -572,7 +572,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { }, }; -static struct reg_default wsa883x_defaults[] = { +static const struct reg_default wsa883x_defaults[] = { { WSA883X_REF_CTRL, 0xD5 }, { WSA883X_TEST_CTL_0, 0x06 }, { WSA883X_BIAS_0, 0xD2 }, @@ -1491,7 +1491,6 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) ret = -EAGAIN; } out: - pm_runtime_mark_last_busy(wsa883x->dev); pm_runtime_put_autosuspend(wsa883x->dev); return ret; diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index fd6ebc25fe89..2484d4b8e2d9 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -899,7 +899,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { }, }; -static struct reg_default wsa884x_defaults[] = { +static const struct reg_default wsa884x_defaults[] = { { WSA884X_BG_CTRL, 0xa5 }, { WSA884X_ADC_CTRL, 0x00 }, { WSA884X_BOP1_PROG, 0x22 }, @@ -1941,7 +1941,6 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) } out: - pm_runtime_mark_last_busy(wsa884x->dev); pm_runtime_put_autosuspend(wsa884x->dev); return ret; diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index 180d45a349ac..7de4014e626d 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -440,7 +440,7 @@ static const struct gpio_chip template_chip = { .direction_input = chip_direction_input, .direction_output = chip_direction_output, .get = chip_gpio_get, - .set_rv = chip_gpio_set, + .set = chip_gpio_set, .can_sleep = true, }; diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index 71a58f7ac13a..6bb31b64210a 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "DesignWare" + config SND_DESIGNWARE_I2S tristate "Synopsys I2S Device Driver" depends on HAVE_CLK @@ -18,3 +20,4 @@ config SND_DESIGNWARE_PCM This functionality is specially suited for I2S devices that don't have DMA support. +endmenu diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index c4cf3cff58de..c4a00b22bc2a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "SoC Audio for Freescale CPUs" +menu "Freescale" comment "Common SoC Audio options for Freescale CPUs:" diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index ab583b432c60..71113886e494 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -63,6 +63,7 @@ struct codec_priv { * @sysclk_freq: SYSCLK rates for set_sysclk() * @sysclk_dir: SYSCLK directions for set_sysclk() * @sysclk_id: SYSCLK ids for set_sysclk() + * @sysclk_ratio: SYSCLK ratio on sample rate * @slot_width: Slot width of each frame * @slot_num: Number of slots of each frame * @@ -72,6 +73,7 @@ struct cpu_priv { unsigned long sysclk_freq[2]; u32 sysclk_dir[2]; u32 sysclk_id[2]; + u32 sysclk_ratio[2]; u32 slot_width; u32 slot_num; }; @@ -176,7 +178,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; struct cpu_priv *cpu_priv = &priv->cpu_priv; struct device *dev = rtd->card->dev; - unsigned int pll_out; + unsigned int pll_out, sysclk_freq; int codec_idx; int ret; @@ -187,9 +189,14 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, if (fsl_asoc_card_is_ac97(priv)) return 0; + if (!cpu_priv->sysclk_freq[tx] && cpu_priv->sysclk_ratio[tx]) + sysclk_freq = priv->sample_rate * cpu_priv->sysclk_ratio[tx]; + else + sysclk_freq = cpu_priv->sysclk_freq[tx]; + /* Specific configurations of DAIs starts from here */ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), cpu_priv->sysclk_id[tx], - cpu_priv->sysclk_freq[tx], + sysclk_freq, cpu_priv->sysclk_dir[tx]); if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk for cpu dai\n"); @@ -799,6 +806,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.slot_width = 32; priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_ratio[TX] = 256; } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { codec_dai_name[0] = "si476x-codec"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 677529916dc0..92fb16f7be45 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -517,7 +517,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index)); regmap_update_bits(asrc->regmap, REG_ASRCTR, - ASRCTR_USRi_MASK(index), 0); + ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index), + ASRCTR_USR(index)); /* Set the input and output clock sources */ regmap_update_bits(asrc->regmap, REG_ASRCSR, @@ -930,7 +931,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) } } -static struct reg_default fsl_asrc_reg[] = { +static const struct reg_default fsl_asrc_reg[] = { { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 }, { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 }, { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 }, diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index e34e5ea98de5..901f840df904 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -39,6 +39,7 @@ enum reg_type { * struct fsl_mqs_soc_data - soc specific data * * @type: control register space type + * @sm_index: index from definition in system manager * @ctrl_off: control register offset * @en_mask: enable bit mask * @en_shift: enable bit shift @@ -51,6 +52,7 @@ enum reg_type { */ struct fsl_mqs_soc_data { enum reg_type type; + int sm_index; int ctrl_off; int en_mask; int en_shift; @@ -82,7 +84,7 @@ static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val) if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) && mqs_priv->soc->ctrl_off == reg) - return scmi_imx_misc_ctrl_get(SCMI_IMX_CTRL_MQS1_SETTINGS, &num, val); + return scmi_imx_misc_ctrl_get(mqs_priv->soc->sm_index, &num, val); return -EINVAL; }; @@ -93,7 +95,7 @@ static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val) if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) && mqs_priv->soc->ctrl_off == reg) - return scmi_imx_misc_ctrl_set(SCMI_IMX_CTRL_MQS1_SETTINGS, val); + return scmi_imx_misc_ctrl_set(mqs_priv->soc->sm_index, val); return -EINVAL; }; @@ -386,6 +388,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = { .type = TYPE_REG_SM, + .sm_index = SCMI_IMX95_CTRL_MQS1_SETTINGS, .ctrl_off = 0x88, .en_mask = BIT(1), .en_shift = 1, @@ -412,6 +415,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = { static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = { .type = TYPE_REG_SM, + .sm_index = SCMI_IMX94_CTRL_MQS1_SETTINGS, .ctrl_off = 0x88, .en_mask = BIT(1), .en_shift = 1, @@ -424,7 +428,8 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = { - .type = TYPE_REG_GPR, + .type = TYPE_REG_SM, + .sm_index = SCMI_IMX94_CTRL_MQS2_SETTINGS, .ctrl_off = 0x10, .en_mask = BIT(1), .en_shift = 1, diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index af1a168d35e3..d0367b21f775 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -803,13 +803,15 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) * anymore. Add software reset to fix this issue. * This is a hardware bug, and will be fix in the * next sai version. + * + * In consumer mode, this can happen even after a + * single open/close, especially if both tx and rx + * are running concurrently. */ - if (!sai->is_consumer_mode[tx]) { - /* Software Reset */ - regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); - /* Clear SR bit to finish the reset */ - regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0); - } + /* Software Reset */ + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, 0); } static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -928,11 +930,11 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) unsigned int ofs = sai->soc_data->reg_offset; /* Software Reset for both Tx and Rx */ - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */ - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, 0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, 0); regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs), FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth), @@ -1059,7 +1061,7 @@ static const struct snd_soc_component_driver fsl_component = { .legacy_dai_naming = 1, }; -static struct reg_default fsl_sai_reg_defaults_ofs0[] = { +static const struct reg_default fsl_sai_reg_defaults_ofs0[] = { {FSL_SAI_TCR1(0), 0}, {FSL_SAI_TCR2(0), 0}, {FSL_SAI_TCR3(0), 0}, @@ -1082,7 +1084,7 @@ static struct reg_default fsl_sai_reg_defaults_ofs0[] = { {FSL_SAI_RMR, 0}, }; -static struct reg_default fsl_sai_reg_defaults_ofs8[] = { +static const struct reg_default fsl_sai_reg_defaults_ofs8[] = { {FSL_SAI_TCR1(8), 0}, {FSL_SAI_TCR2(8), 0}, {FSL_SAI_TCR3(8), 0}, @@ -1822,11 +1824,11 @@ static int fsl_sai_runtime_resume(struct device *dev) regcache_cache_only(sai->regmap, false); regcache_mark_dirty(sai->regmap); - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); usleep_range(1000, 2000); - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, 0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, 0); ret = regcache_sync(sai->regmap); if (ret) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index e3111dd80be4..5d804860f7d8 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -1395,7 +1395,7 @@ static irqreturn_t irq0_isr(int irq, void *devid) if (isr & FSL_XCVR_IRQ_NEW_CS) { dev_dbg(dev, "Received new CS block\n"); isr_clr |= FSL_XCVR_IRQ_NEW_CS; - if (!xcvr->soc_data->spdif_only) { + if (xcvr->soc_data->fw_name) { /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, FSL_XCVR_EXT_CTRL_PAGE_MASK, @@ -1423,6 +1423,26 @@ static irqreturn_t irq0_isr(int irq, void *devid) /* clear CS control register */ memset_io(reg_ctrl, 0, sizeof(val)); } + } else { + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_0, + (u32 *)&xcvr->rx_iec958.status[0]); + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_1, + (u32 *)&xcvr->rx_iec958.status[4]); + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_2, + (u32 *)&xcvr->rx_iec958.status[8]); + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_3, + (u32 *)&xcvr->rx_iec958.status[12]); + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_4, + (u32 *)&xcvr->rx_iec958.status[16]); + regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_5, + (u32 *)&xcvr->rx_iec958.status[20]); + for (i = 0; i < 6; i++) { + val = *(u32 *)(xcvr->rx_iec958.status + i * 4); + *(u32 *)(xcvr->rx_iec958.status + i * 4) = + bitrev32(val); + } + regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL, + FSL_XCVR_RX_DPTH_CTRL_CSA); } } if (isr & FSL_XCVR_IRQ_NEW_UD) { @@ -1497,6 +1517,7 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { }; static const struct fsl_xcvr_soc_data fsl_xcvr_imx95_data = { + .fw_name = "imx/xcvr/xcvr-imx95.bin", .spdif_only = true, .use_phy = true, .use_edma = true, @@ -1786,7 +1807,7 @@ static int fsl_xcvr_runtime_resume(struct device *dev) } } - if (xcvr->mode == FSL_XCVR_MODE_EARC) { + if (xcvr->soc_data->fw_name) { ret = fsl_xcvr_load_firmware(xcvr); if (ret) { dev_err(dev, "failed to load firmware.\n"); diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 9e668ae68039..28699d7b75ca 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -26,6 +26,7 @@ enum codec_type { CODEC_AK4497, CODEC_AK5552, CODEC_CS42888, + CODEC_WM8524, }; /* @@ -196,6 +197,13 @@ static struct imx_akcodec_tdm_fs_mul cs42888_tdm_fs_mul[] = { { .min = 256, .max = 256, .mul = 256 }, }; +static struct imx_akcodec_fs_mul wm8524_fs_mul[] = { + { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1152, }, + { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, }, + { .rmin = 88200, .rmax = 96000, .wmin = 128, .wmax = 384, }, + { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 192, }, +}; + static const u32 akcodec_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000, @@ -229,6 +237,10 @@ static const u32 cs42888_tdm_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8, }; +static const u32 wm8524_channels[] = { + 2, +}; + static bool format_is_dsd(struct snd_pcm_hw_params *params) { snd_pcm_format_t format = params_format(params); @@ -261,6 +273,7 @@ static bool codec_is_akcodec(unsigned int type) case CODEC_AK5558: case CODEC_AK5552: case CODEC_CS42888: + case CODEC_WM8524: return true; default: break; @@ -477,9 +490,24 @@ static int imx_aif_startup(struct snd_pcm_substream *substream) return ret; } +static void imx_aif_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + int i; + + for_each_rtd_cpu_dais(rtd, i, cpu_dai) + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + + for_each_rtd_codec_dais(rtd, i, codec_dai) + snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); +} + static const struct snd_soc_ops imx_aif_ops = { .hw_params = imx_aif_hw_params, .startup = imx_aif_startup, + .shutdown = imx_aif_shutdown, }; static const struct snd_soc_ops imx_aif_ops_be = { @@ -513,7 +541,6 @@ static int imx_card_parse_of(struct imx_card_data *data) struct device_node *platform = NULL; struct device_node *codec = NULL; struct device_node *cpu = NULL; - struct device_node *np; struct device *dev = card->dev; struct snd_soc_dai_link *link; struct dai_link_data *link_data; @@ -552,11 +579,10 @@ static int imx_card_parse_of(struct imx_card_data *data) link = card->dai_link; link_data = data->link_data; - for_each_child_of_node(dev->of_node, np) { + for_each_child_of_node_scoped(dev->of_node, np) { dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); if (!dlc) { - ret = -ENOMEM; - goto err_put_np; + return -ENOMEM; } link->cpus = &dlc[0]; @@ -567,8 +593,8 @@ static int imx_card_parse_of(struct imx_card_data *data) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); - goto err_put_np; + return dev_err_probe(card->dev, ret, + "error getting codec dai_link name\n"); } cpu = of_get_child_by_name(np, "cpu"); @@ -634,6 +660,8 @@ static int imx_card_parse_of(struct imx_card_data *data) plat_data->type = CODEC_AK5552; else if (!strcmp(link->codecs->dai_name, "cs42888")) plat_data->type = CODEC_CS42888; + else if (!strcmp(link->codecs->dai_name, "wm8524-hifi")) + plat_data->type = CODEC_WM8524; } else { link->codecs = &snd_soc_dummy_dlc; @@ -725,8 +753,7 @@ static int imx_card_parse_of(struct imx_card_data *data) of_node_put(cpu); of_node_put(codec); of_node_put(platform); -err_put_np: - of_node_put(np); + return ret; } @@ -808,6 +835,10 @@ static int imx_card_probe(struct platform_device *pdev) data->dapm_routes[1].sink = "CPU-Capture"; data->dapm_routes[1].source = "Capture"; break; + case CODEC_WM8524: + data->dapm_routes[0].sink = "Playback"; + data->dapm_routes[0].source = "CPU-Playback"; + break; default: break; } @@ -857,6 +888,12 @@ static int imx_card_probe(struct platform_device *pdev) plat_data->support_tdm_channels = cs42888_tdm_channels; plat_data->num_tdm_channels = ARRAY_SIZE(cs42888_tdm_channels); break; + case CODEC_WM8524: + plat_data->fs_mul = wm8524_fs_mul; + plat_data->num_fs_mul = ARRAY_SIZE(wm8524_fs_mul); + plat_data->support_channels = wm8524_channels; + plat_data->num_channels = ARRAY_SIZE(wm8524_channels); + break; default: break; } diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index b6df4e26bc4a..64b0817e2955 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Generic drivers" + config SND_SIMPLE_CARD_UTILS tristate @@ -37,3 +39,5 @@ config SND_TEST_COMPONENT depends on OF help This option enables test component sound driver support. + +endmenu diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig index 7603782fb060..6005653170a8 100644 --- a/sound/soc/google/Kconfig +++ b/sound/soc/google/Kconfig @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Google" config SND_SOC_CHV3_I2S tristate "Google Chameleon v3 I2S device" help Enable support for the Google Chameleon v3 I2S device. + +endmenu diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig index df8fbd8bb4b0..d95da932f352 100644 --- a/sound/soc/hisilicon/Kconfig +++ b/sound/soc/hisilicon/Kconfig @@ -1,6 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Hisilicon" + config SND_I2S_HI6210_I2S tristate "Hisilicon I2S controller" select SND_SOC_GENERIC_DMAENGINE_PCM help Hisilicon I2S + +endmenu diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index 568efa606ca4..22b75a8144a1 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -1,12 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_IMG - bool "Audio support for Imagination Technologies designs" - help - Audio support for Imagination Technologies audio hardware +menu "Imagination Technologies" + depends on MIPS || COMPILE_TEST config SND_SOC_IMG_I2S_IN tristate "Imagination I2S Input Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S in driver for @@ -14,7 +11,6 @@ config SND_SOC_IMG_I2S_IN config SND_SOC_IMG_I2S_OUT tristate "Imagination I2S Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S out driver for @@ -22,7 +18,6 @@ config SND_SOC_IMG_I2S_OUT config SND_SOC_IMG_PARALLEL_OUT tristate "Imagination Parallel Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for parallel out driver for @@ -30,7 +25,6 @@ config SND_SOC_IMG_PARALLEL_OUT config SND_SOC_IMG_SPDIF_IN tristate "Imagination SPDIF Input Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF input driver for @@ -38,7 +32,6 @@ config SND_SOC_IMG_SPDIF_IN config SND_SOC_IMG_SPDIF_OUT tristate "Imagination SPDIF Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF out driver for @@ -47,7 +40,8 @@ config SND_SOC_IMG_SPDIF_OUT config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC tristate "Support for Pistachio SoC Internal DAC Driver" - depends on SND_SOC_IMG help Say Y or M if you want to add support for Pistachio internal DAC driver for Imagination Technologies Pistachio internal DAC device. + +endmenu diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 2db494b0e3cf..412555e626b8 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Intel" + config SND_SOC_INTEL_SST_TOPLEVEL bool "Intel ASoC SST drivers" default y @@ -115,3 +117,5 @@ source "sound/soc/intel/avs/boards/Kconfig" # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" + +endmenu diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 5d08f7d803f9..c01b29616ebc 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -259,7 +259,6 @@ int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) { int ret; - pm_runtime_mark_last_busy(sst_drv->dev); ret = pm_runtime_put_autosuspend(sst_drv->dev); if (ret < 0) return ret; diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 673ccf162023..fb49167f5fc4 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -308,6 +308,33 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { }, .tplg_filename = "rt1308-tplg.bin", }, + { + .id = "10EC5640", + .uid = "1", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt5640-tplg.bin", + }, + { + .id = "10EC5640", + .uid = "3", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5640-tplg.bin", + }, + { + .id = "10EC5640", + .uid = "2", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(2), + }, + .tplg_filename = "rt5640-tplg.bin", + }, { .id = "ESSX8336", .drv_name = "avs_es8336", diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 8b654181004e..82f50207bb2f 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -153,6 +153,18 @@ config SND_SOC_INTEL_AVS_MACH_RT5514 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_RT5640 + tristate "rt5640 in I2S mode" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT5640 + help + This adds support for ASoC machine board connecting AVS with RT5640, + components representing Intel AudioDSP and Realtek 5640 codec respectively. + The codec chip is present on I2C bus and the streaming occurs over I2S + interface. + Say Y or m if you have such a device. + config SND_SOC_INTEL_AVS_MACH_RT5663 tristate "rt5663 in I2S mode" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index a95256b94dc8..46ef1babda34 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -15,6 +15,7 @@ snd-soc-avs-rt274-y := rt274.o snd-soc-avs-rt286-y := rt286.o snd-soc-avs-rt298-y := rt298.o snd-soc-avs-rt5514-y := rt5514.o +snd-soc-avs-rt5640-y := rt5640.o snd-soc-avs-rt5663-y := rt5663.o snd-soc-avs-rt5682-y := rt5682.o snd-soc-avs-ssm4567-y := ssm4567.o @@ -34,6 +35,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c new file mode 100644 index 000000000000..706b84ffe1ef --- /dev/null +++ b/sound/soc/intel/avs/boards/rt5640.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022-2025 Intel Corporation +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include +#include +#include +#include "../../../codecs/rt5640.h" +#include "../utils.h" + +#define AVS_RT5640_MCLK_HZ 19200000 +#define RT5640_CODEC_DAI "rt5640-aif1" + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + { "Headphone Jack", NULL, "HPOR" }, + { "Headphone Jack", NULL, "HPOL" }, + { "IN2P", NULL, "Mic Jack" }, + { "IN2P", NULL, "MICBIAS1" }, + { "Speaker", NULL, "SPOLP" }, + { "Speaker", NULL, "SPOLN" }, + { "Speaker", NULL, "SPORP" }, + { "Speaker", NULL, "SPORN" }, +}; + +static const struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + struct snd_soc_card *card = runtime->card; + struct snd_soc_jack_pin *pins; + struct snd_soc_jack *jack; + int num_pins, ret; + + jack = snd_soc_card_get_drvdata(card); + num_pins = ARRAY_SIZE(card_headset_pins); + + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins, + num_pins); + if (ret) + return ret; + + snd_soc_component_set_jack(codec_dai->component, jack, NULL); + card->dapm.idle_bias_off = true; + + return 0; +} + +static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + + snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */ + if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int avs_rt5640_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ, + params_rate(params) * 512); + if (ret < 0) { + dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret); + return ret; + } + + ret = rt5640_sel_asrc_clk_src(codec_dai->component, + RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER | + RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER | + RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER, + RT5640_CLK_SEL_ASRC); + if (ret) + dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops avs_rt5640_ops = { + .hw_params = avs_rt5640_hw_params, +}; + +static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, + struct snd_soc_acpi_mach *mach, + struct snd_soc_dai_link **dai_link) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + u32 uid = 0; + int ret; + + if (mach->uid) { + ret = kstrtou32(mach->uid, 0, &uid); + if (ret) + return ret; + uid--; /* 0-based indexing. */ + } + + dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + dl->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); + dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + if (!dl->name || !dl->cpus || !dl->codecs) + return -ENOMEM; + + dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot)); + dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid); + dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI); + if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) + return -ENOMEM; + + platform->name = dev_name(dev); + dl->num_cpus = 1; + dl->num_codecs = 1; + dl->platforms = platform; + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + dl->init = avs_rt5640_codec_init; + dl->exit = avs_rt5640_codec_exit; + dl->be_hw_params_fixup = avs_rt5640_be_fixup; + dl->ops = &avs_rt5640_ops; + dl->nonatomic = 1; + dl->no_pcm = 1; + + *dai_link = dl; + + return 0; +} + +static int avs_card_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); + + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_card_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); + struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); + + return snd_soc_component_set_jack(codec_dai->component, jack, NULL); +} + +static int avs_rt5640_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_link; + struct device *dev = &pdev->dev; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + struct snd_soc_jack *jack; + int ssp_port, tdm_slot, ret; + + mach = dev_get_platdata(dev); + + ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); + if (ret) + return ret; + + ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link); + if (ret) { + dev_err(dev, "Failed to create dai link: %d", ret); + return ret; + } + + jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!jack || !card) + return -ENOMEM; + + if (mach->uid) { + card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid); + if (!card->name) + return -ENOMEM; + } else { + card->name = "AVS I2S ALC5640"; + } + card->driver_name = "avs_rt5640"; + card->long_name = card->name; + card->dev = dev; + card->owner = THIS_MODULE; + card->suspend_pre = avs_card_suspend_pre; + card->resume_post = avs_card_resume_post; + card->dai_link = dai_link; + card->num_links = 1; + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + snd_soc_card_set_drvdata(card, jack); + + return devm_snd_soc_register_deferrable_card(dev, card); +} + +static const struct platform_device_id avs_rt5640_driver_ids[] = { + { + .name = "avs_rt5640", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); + +static struct platform_driver avs_rt5640_driver = { + .probe = avs_rt5640_probe, + .driver = { + .name = "avs_rt5640", + .pm = &snd_soc_pm_ops, + }, + .id_table = avs_rt5640_driver_ids, +}; + +module_platform_driver(avs_rt5640_driver); + +MODULE_DESCRIPTION("Intel rt5640 machine driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index ec1b3f55cb5c..5ebadba07ecc 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -231,7 +231,6 @@ static void avs_hda_probe_work(struct work_struct *work) /* configure PM */ pm_runtime_set_autosuspend_delay(bus->dev, 2000); pm_runtime_use_autosuspend(bus->dev); - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); pm_runtime_allow(bus->dev); } @@ -446,6 +445,8 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL); if (!adev) return -ENOMEM; + bus = &adev->base.core; + ret = avs_bus_init(adev, pci, id); if (ret < 0) { dev_err(dev, "failed to init avs bus: %d\n", ret); @@ -456,7 +457,6 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) if (ret < 0) return ret; - bus = &adev->base.core; bus->addr = pci_resource_start(pci, 0); bus->remap_addr = pci_ioremap_bar(pci, 0); if (!bus->remap_addr) { diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index c625cf879f17..f508f215ecd2 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -315,7 +315,6 @@ static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities) if (!adev->logged_resources) { avs_dsp_enable_d0ix(adev); err_d0ix: - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); } @@ -342,7 +341,6 @@ static int disable_logs(struct avs_dev *adev, u32 resource_mask) /* If that's the last resource, allow for D3. */ if (!adev->logged_resources) { avs_dsp_enable_d0ix(adev); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 0314f9d4ea5f..6bfb9d1a1ca8 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -141,7 +141,6 @@ static void avs_dsp_recovery(struct avs_dev *adev) if (ret < 0) dev_err(adev->dev, "dsp reboot failed: %d\n", ret); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_enable(adev->dev); pm_request_autosuspend(adev->dev); diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index ccf90428126d..67ce6675eea7 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -979,7 +979,6 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) if (!ret) ret = avs_module_info_init(adev, false); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); return ret; @@ -1570,11 +1569,13 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen { struct snd_soc_acpi_mach *mach; struct snd_soc_dai *dai, *save; + struct avs_mach_pdata *pdata; struct hda_codec *codec; char name[32]; mach = dev_get_platdata(component->card->dev); - codec = mach->pdata; + pdata = mach->pdata; + codec = pdata->codec; snprintf(name, sizeof(name), "%s-cpu", dev_name(&codec->core.dev)); for_each_component_dais_safe(component, dai, save) { diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 2df7afa2f469..c23fdb6aad4c 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -11,7 +11,7 @@ menuconfig SND_SOC_INTEL_MACH kernel: saying N will just cause the configurator to skip all the questions about Intel ASoC machine drivers. -if SND_SOC_INTEL_MACH +if SND_SOC_INTEL_MACH && (SND_SOC_SOF_INTEL_COMMON || !SND_SOC_SOF_INTEL_COMMON) config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES bool "Use more user friendly long card names" @@ -42,6 +42,7 @@ config SND_SOC_INTEL_SOF_NUVOTON_COMMON tristate config SND_SOC_INTEL_SOF_BOARD_HELPERS + select SND_SOC_ACPI_INTEL_MATCH tristate if SND_SOC_INTEL_CATPT diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index aaef212cf44e..54c1894ee96a 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -458,7 +459,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (!drv) return -ENOMEM; - strcpy(drv->codec_name, RT5672_I2C_DEFAULT); + strscpy(drv->codec_name, RT5672_I2C_DEFAULT, sizeof(drv->codec_name)); /* find index of codec dai */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 0554c7e2cb34..519218385fdf 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -85,6 +85,18 @@ skl_hda_get_board_quirk(struct snd_soc_acpi_mach_params *mach_params) return board_quirk; } +static int skl_hda_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + + /* Ignore the HDMI PCM link if iDisp is not present */ + if (strstr(link->stream_name, "HDMI") && !ctx->hdmi.idisp_codec) + link->ignore = true; + + return 0; +} + static int skl_hda_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; @@ -101,6 +113,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->fully_routed = true; card->late_probe = skl_hda_card_late_probe; + card->add_dai_link = skl_hda_add_dai_link; dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk); diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index a0b3679b17b4..1211a2b8a2a2 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -826,6 +826,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "ptl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index f5925bd0a3fc..4994aaccc583 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -892,6 +892,13 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_PORT_BT_OFFLOAD(2) | SOF_BT_OFFLOAD_PRESENT), }, + { + .name = "ptl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_SSP_PORT_CODEC(1) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_SSP_MASK_HDMI_CAPTURE(0x5)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 81a914bd7ec2..f997b2dc221b 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -741,6 +741,14 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOC_SDW_CODEC_SPKR), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CCC") + }, + .driver_data = (void *)(SOC_SDW_CODEC_SPKR), + }, /* Pantherlake devices*/ { .callback = sof_sdw_quirk_cb, @@ -783,6 +791,9 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x1043, 0x1f43, "ASUS Zenbook S16", SOC_SDW_CODEC_MIC), + SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC), + SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC), + SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC), {} }; @@ -1295,6 +1306,19 @@ static int sof_sdw_card_late_probe(struct snd_soc_card *card) return ret; } +static int sof_sdw_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private; + + /* Ignore the HDMI PCM link if iDisp is not present */ + if (strstr(link->stream_name, "HDMI") && !intel_ctx->hdmi.idisp_codec) + link->ignore = true; + + return 0; +} + static int mc_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); @@ -1321,6 +1345,7 @@ static int mc_probe(struct platform_device *pdev) card->name = "soundwire"; card->owner = THIS_MODULE; card->late_probe = sof_sdw_card_late_probe; + card->add_dai_link = sof_sdw_add_dai_link; snd_soc_card_set_drvdata(card, ctx); diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 81a2f0339e05..46acb7fdc547 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -673,7 +673,6 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, ret = catpt_ipc_set_device_format(cdev, &devfmt); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) @@ -871,7 +870,6 @@ static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); } - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return 0; @@ -892,7 +890,6 @@ static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol, ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id, ucontrol->value.integer.value); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return ret; @@ -927,7 +924,6 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); } - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return 0; @@ -958,7 +954,6 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id, ucontrol->value.integer.value); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) @@ -1035,7 +1030,6 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c index 936ac9d503ff..048253002ec8 100644 --- a/sound/soc/intel/catpt/sysfs.c +++ b/sound/soc/intel/catpt/sysfs.c @@ -21,7 +21,6 @@ static ssize_t fw_version_show(struct device *dev, ret = catpt_ipc_get_fw_version(cdev, &version); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 73e581e93755..6bf7a6250ddc 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -238,6 +238,15 @@ static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = { + { + .adr = 0x000330025D131601ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt1316-1" + } +}; + static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = { { .adr = 0x000230025D132001ull, @@ -368,6 +377,20 @@ static const struct snd_soc_acpi_link_adr arl_sdca_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1316_3_single_adr), + .adr_d = rt1316_3_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = { { .mask = BIT(0), @@ -467,6 +490,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", .get_function_tplg_files = sof_sdw_get_tplg_files, }, + { + .link_mask = BIT(2) | BIT(3), + .links = arl_cs42l43_l2_cs35l56_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, { .link_mask = BIT(2), .links = arl_cs42l43_l2, @@ -475,11 +505,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .get_function_tplg_files = sof_sdw_get_tplg_files, }, { - .link_mask = BIT(2) | BIT(3), - .links = arl_cs42l43_l2_cs35l56_l3, + .link_mask = BIT(0) | BIT(3), + .links = arl_rt711_l0_rt1316_l3, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", - .get_function_tplg_files = sof_sdw_get_tplg_files, + .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", }, { .link_mask = 0x1, /* link0 required */ diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 558dc4c91239..937a74a5d523 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -419,6 +419,15 @@ static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = { + { + .adr = 0x000231025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = { { .adr = 0x000130025D132001ull, @@ -609,6 +618,25 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt1318_l12_rt714_l0[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_sdw_rt1320_l12_rt714_l0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group2_adr), + .adr_d = rt1320_1_group2_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_group2_adr), + .adr_d = rt1320_2_group2_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = { { .mask = BIT(0), @@ -740,6 +768,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" }, + { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1320_l12_rt714_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" + }, { .link_mask = BIT(0) | BIT(1), .links = lnl_sdw_rt713_l0_rt1318_l1, diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index eae75f3f0fa4..e292701dfcfe 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -21,7 +21,24 @@ static const struct snd_soc_acpi_codecs ptl_rt5682_rt5682s_hp = { .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, }; +static const struct snd_soc_acpi_codecs ptl_essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + +static const struct snd_soc_acpi_codecs ptl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { + { + .comp_ids = &ptl_rt5682_rt5682s_hp, + .drv_name = "ptl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &ptl_lt6911_hdmi, + .sof_tplg_filename = "sof-ptl-rt5682-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &ptl_rt5682_rt5682s_hp, .drv_name = "ptl_rt5682_def", @@ -29,6 +46,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, }, + { + .comp_ids = &ptl_essx_83x6, + .drv_name = "ptl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &ptl_lt6911_hdmi, + .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &ptl_essx_83x6, + .drv_name = "sof-essx8336", + .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_machines); @@ -330,6 +362,15 @@ static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = { + { + .adr = 0x000030025d072101ull, + .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), + .endpoints = rt_mf_endpoints, + .name_prefix = "rt721" + } +}; + static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = { { .adr = 0x000330025d072101ull, @@ -448,6 +489,15 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt721_0_single_adr), + .adr_d = rt721_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = { { .mask = BIT(0), @@ -635,6 +685,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt711.tplg", }, + { + .link_mask = BIT(0), + .links = ptl_rt721_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt721.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, { .link_mask = BIT(0), .links = ptl_rt722_only, diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 90fe7aa3df1c..3cc81dcf047e 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -73,7 +73,8 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ break; default: dev_warn(card->dev, - "only -2ch and -4ch are supported for dmic\n"); + "unsupported number of dmics: %d\n", + mach_params.dmic_num); continue; } tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig index dd3b4507fbe6..f3ff3fb49239 100644 --- a/sound/soc/jz4740/Kconfig +++ b/sound/soc/jz4740/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "JZ4740" + config SND_JZ4740_SOC_I2S tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC" depends on MIPS || COMPILE_TEST @@ -8,3 +10,5 @@ config SND_JZ4740_SOC_I2S help Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740 based boards. + +endmenu diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 5d8a86b26fa2..924072e402c8 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Kirkwood" + config SND_KIRKWOOD_SOC tristate "SoC Audio for the Marvell Kirkwood and Dove chips" depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST @@ -16,3 +18,4 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB Say Y if you want to add support for SoC audio on the Armada 370 Development Board. +endmenu diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig index 1a3c28816e7a..2e06670e4d7e 100644 --- a/sound/soc/loongson/Kconfig +++ b/sound/soc/loongson/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -menu "SoC Audio for Loongson CPUs" +menu "Loongson" config SND_SOC_LOONGSON_CARD tristate "Loongson Sound Card Driver" diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c index e8852a30f213..e336656e13eb 100644 --- a/sound/soc/loongson/loongson_i2s.c +++ b/sound/soc/loongson/loongson_i2s.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 90e367586493..10ca8bccabdd 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Mediatek" + config SND_SOC_MEDIATEK tristate select REGMAP_MMIO @@ -319,3 +321,5 @@ config SND_SOC_MT8365_MT6357 boards with the MT6357 PMIC codec. Select Y if you have such device. If unsure select "N". + +endmenu diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 6b6330583941..70fd05d5ff48 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -120,7 +120,9 @@ int mtk_afe_pcm_new(struct snd_soc_component *component, struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); size = afe->mtk_afe_hardware->buffer_bytes_max; - snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev, 0, size); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev, + afe->preallocate_buffers ? size : 0, + size); return 0; } diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index f51578b6c50a..a406f2e3e7a8 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -117,6 +117,7 @@ struct mtk_base_afe { struct mtk_base_afe_irq *irqs; int irqs_size; int memif_32bit_supported; + bool preallocate_buffers; struct list_head sub_dais; struct snd_soc_dai_driver *dai_drivers; diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c index 713a368f79cf..95a083939f3e 100644 --- a/sound/soc/mediatek/common/mtk-soundcard-driver.c +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -262,9 +262,13 @@ int mtk_soundcard_common_probe(struct platform_device *pdev) soc_card_data->accdet = accdet_comp; else dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n"); + + put_device(&accdet_pdev->dev); } else { dev_err(&pdev->dev, "No device found from mediatek,accdet property\n"); } + + of_node_put(accdet_node); } platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 04ed0cfec174..c0fa623e0b17 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1053,22 +1054,28 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_base_afe *afe; struct mt8173_afe_private *afe_priv; struct snd_soc_component *comp_pcm, *comp_hdmi; + struct device *dev = &pdev->dev; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) return ret; - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; - afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), - GFP_KERNEL); + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL); afe_priv = afe->platform_priv; if (!afe_priv) return -ENOMEM; - afe->dev = &pdev->dev; + afe->dev = dev; + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } irq_id = platform_get_irq(pdev, 0); if (irq_id <= 0) @@ -1078,27 +1085,27 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); - afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, - &mt8173_afe_regmap_config); + afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr, + &mt8173_afe_regmap_config); if (IS_ERR(afe->regmap)) return PTR_ERR(afe->regmap); /* initial audio related clock */ ret = mt8173_afe_init_audio_clk(afe); if (ret) { - dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n"); + dev_err(dev, "mt8173_afe_init_audio_clk fail\n"); return ret; } /* memif % irq initialize*/ afe->memif_size = MT8173_AFE_MEMIF_NUM; - afe->memif = devm_kcalloc(afe->dev, afe->memif_size, + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), GFP_KERNEL); if (!afe->memif) return -ENOMEM; afe->irqs_size = MT8173_AFE_IRQ_NUM; - afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size, + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), GFP_KERNEL); if (!afe->irqs) return -ENOMEM; @@ -1117,9 +1124,9 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) platform_set_drvdata(pdev, afe); - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = mt8173_afe_runtime_resume(&pdev->dev); + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = mt8173_afe_runtime_resume(dev); if (ret) goto err_pm_disable; } @@ -1129,13 +1136,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->runtime_resume = mt8173_afe_runtime_resume; afe->runtime_suspend = mt8173_afe_runtime_suspend; - ret = devm_snd_soc_register_component(&pdev->dev, - &mtk_afe_pcm_platform, - NULL, 0); + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, + NULL, 0); if (ret) goto err_pm_disable; - comp_pcm = devm_kzalloc(&pdev->dev, sizeof(*comp_pcm), GFP_KERNEL); + comp_pcm = devm_kzalloc(dev, sizeof(*comp_pcm), GFP_KERNEL); if (!comp_pcm) { ret = -ENOMEM; goto err_pm_disable; @@ -1143,7 +1149,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ret = snd_soc_component_initialize(comp_pcm, &mt8173_afe_pcm_dai_component, - &pdev->dev); + dev); if (ret) goto err_pm_disable; @@ -1157,7 +1163,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_pm_disable; - comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL); + comp_hdmi = devm_kzalloc(dev, sizeof(*comp_hdmi), GFP_KERNEL); if (!comp_hdmi) { ret = -ENOMEM; goto err_cleanup_components; @@ -1165,7 +1171,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ret = snd_soc_component_initialize(comp_hdmi, &mt8173_afe_hdmi_dai_component, - &pdev->dev); + dev); if (ret) goto err_cleanup_components; @@ -1179,30 +1185,32 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_cleanup_components; - ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, + ret = devm_request_irq(dev, irq_id, mt8173_afe_irq_handler, 0, "Afe_ISR_Handle", (void *)afe); if (ret) { - dev_err(afe->dev, "could not request_irq\n"); + dev_err(dev, "could not request_irq\n"); goto err_cleanup_components; } - dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); + dev_info(dev, "MT8173 AFE driver initialized.\n"); return 0; err_cleanup_components: - snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_component(dev); err_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } static void mt8173_afe_pcm_dev_remove(struct platform_device *pdev) { - snd_soc_unregister_component(&pdev->dev); + struct device *dev = &pdev->dev; - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - mt8173_afe_runtime_suspend(&pdev->dev); + snd_soc_unregister_component(dev); + + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt8173_afe_runtime_suspend(dev); } static const struct of_device_id mt8173_afe_pcm_dt_match[] = { diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index e8884354995c..a7fef772760a 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -6,10 +6,12 @@ // Author: KaiChieh Chuang #include +#include #include #include #include #include +#include #include #include @@ -431,6 +433,9 @@ static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = { .reg_ofs_base = AFE_##_id##_BASE, \ .reg_ofs_cur = AFE_##_id##_CUR, \ .reg_ofs_end = AFE_##_id##_END, \ + .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \ + .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \ + .reg_ofs_end_msb = AFE_##_id##_END_MSB, \ .fs_reg = (_fs_reg), \ .fs_shift = _id##_MODE_SFT, \ .fs_maskbit = _id##_MODE_MASK, \ @@ -462,11 +467,17 @@ static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = { #define AFE_VUL12_BASE AFE_VUL_D2_BASE #define AFE_VUL12_CUR AFE_VUL_D2_CUR #define AFE_VUL12_END AFE_VUL_D2_END +#define AFE_VUL12_BASE_MSB AFE_VUL_D2_BASE_MSB +#define AFE_VUL12_CUR_MSB AFE_VUL_D2_CUR_MSB +#define AFE_VUL12_END_MSB AFE_VUL_D2_END_MSB #define AWB2_HD_ALIGN_SFT AWB2_ALIGN_SFT #define VUL12_DATA_SFT VUL12_MONO_SFT #define AFE_HDMI_BASE AFE_HDMI_OUT_BASE #define AFE_HDMI_CUR AFE_HDMI_OUT_CUR #define AFE_HDMI_END AFE_HDMI_OUT_END +#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB +#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB +#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = { MT8183_MEMIF(DL1, AFE_DAC_CON1, AFE_DAC_CON1), @@ -759,23 +770,31 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8183_afe_private *afe_priv; - struct device *dev; + struct device *dev = &pdev->dev; struct reset_control *rstc; int i, irq_id, ret; - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34)); + if (ret) + return ret; + + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; platform_set_drvdata(pdev, afe); - afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), - GFP_KERNEL); + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL); if (!afe->platform_priv) return -ENOMEM; afe_priv = afe->platform_priv; - afe->dev = &pdev->dev; - dev = afe->dev; + afe->dev = dev; + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } /* initial audio related clock */ ret = mt8183_init_clock(afe); @@ -814,7 +833,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_sync(dev); ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); if (ret) { @@ -822,7 +841,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) goto err_pm_disable; } - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; regcache_cache_only(afe->regmap, true); @@ -880,7 +899,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { ret = dai_register_cbs[i](afe); if (ret) { - dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + dev_warn(dev, "dai register i %d fail, ret %d\n", i, ret); goto err_pm_disable; } @@ -889,8 +908,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* init dai_driver and component_driver */ ret = mtk_afe_combine_sub_dai(afe); if (ret) { - dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", - ret); + dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n", ret); goto err_pm_disable; } @@ -902,16 +920,14 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) afe->runtime_suspend = mt8183_afe_runtime_suspend; /* register component */ - ret = devm_snd_soc_register_component(&pdev->dev, - &mtk_afe_pcm_platform, + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, NULL, 0); if (ret) { dev_warn(dev, "err_platform\n"); goto err_pm_disable; } - ret = devm_snd_soc_register_component(afe->dev, - &mt8183_afe_pcm_dai_component, + ret = devm_snd_soc_register_component(dev, &mt8183_afe_pcm_dai_component, afe->dai_drivers, afe->num_dai_drivers); if (ret) { @@ -922,15 +938,17 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) return ret; err_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } static void mt8183_afe_pcm_dev_remove(struct platform_device *pdev) { - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - mt8183_afe_runtime_suspend(&pdev->dev); + struct device *dev = &pdev->dev; + + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt8183_afe_runtime_suspend(dev); } static const struct of_device_id mt8183_afe_pcm_dt_match[] = { diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index db7c93401bee..c73b4664e53e 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -2835,6 +2836,12 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev) afe_priv = afe->platform_priv; afe->dev = &pdev->dev; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index a2a76b6df631..ea814a0f726d 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -408,7 +408,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) } if (pin_w) - dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU); + snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU); else dev_dbg(afe->dev, "%s(), no pinmux widget, please check if default on\n", __func__); @@ -510,7 +510,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i]; if (pin_w) - dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD); + snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD); dev_dbg(afe->dev, "%s(), end, calibration ok %d\n", __func__, param->mtkaif_calibration_ok); diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index fd6af74d7995..3d32fe46118e 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -2179,6 +2180,12 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = dev; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + /* init audio related clock */ ret = mt8192_init_clock(afe); if (ret) { diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c index cae51756cead..cb9beb172ed5 100644 --- a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c +++ b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c @@ -812,11 +812,10 @@ static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = { static int mt8365_dai_i2s_set_priv(struct mtk_base_afe *afe) { int i, ret; - struct mt8365_afe_private *afe_priv = afe->platform_priv; for (i = 0; i < DAI_I2S_NUM; i++) { ret = mt8365_dai_set_priv(afe, mt8365_i2s_priv[i].id, - sizeof(*afe_priv), + sizeof(mt8365_i2s_priv[i]), &mt8365_i2s_priv[i]); if (ret) return ret; diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 6458d5dc4902..d9a730994a2a 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "ASoC support for Amlogic platforms" +menu "Amlogic" depends on ARCH_MESON || (COMPILE_TEST && COMMON_CLK) config SND_MESON_AIU diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index a2dfccb7990f..b4dca80e15e4 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -222,7 +222,6 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, struct axg_dai_link_tdm_data *be) { struct axg_dai_link_tdm_mask *codec_mask; - struct device_node *np; codec_mask = devm_kcalloc(card->dev, link->num_codecs, sizeof(*codec_mask), GFP_KERNEL); @@ -231,7 +230,7 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, be->codec_masks = codec_mask; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", &codec_mask->rx); snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 68531183fb60..cdb759b466ad 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -137,7 +137,6 @@ int meson_card_set_be_link(struct snd_soc_card *card, struct device_node *node) { struct snd_soc_dai_link_component *codec; - struct device_node *np; int ret, num_codecs; num_codecs = of_get_child_count(node); @@ -154,19 +153,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, link->codecs = codec; link->num_codecs = num_codecs; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ret = meson_card_parse_dai(card, np, codec); - if (ret) { - of_node_put(np); + if (ret) return ret; - } codec++; } ret = meson_card_set_link_name(card, link, node, "be"); if (ret) - dev_err(card->dev, "error setting %pOFn link name\n", np); + dev_err(card->dev, "error setting %pOFn link name\n", node); return ret; } @@ -198,7 +195,6 @@ static int meson_card_add_links(struct snd_soc_card *card) { struct meson_card *priv = snd_soc_card_get_drvdata(card); struct device_node *node = card->dev->of_node; - struct device_node *np; int num, i, ret; num = of_get_child_count(node); @@ -212,12 +208,10 @@ static int meson_card_add_links(struct snd_soc_card *card) return ret; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ret = priv->match_data->add_link(card, np, &i); - if (ret) { - of_node_put(np); + if (ret) return ret; - } i++; } diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index 402ef1ee7a32..2fb1ca711f71 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_MXS_SOC - tristate "SoC Audio for Freescale MXS CPUs" + tristate "Freescale MXS" depends on ARCH_MXS || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e05d6ce4c8fa..e026f9912a6d 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "PXA" + config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA || COMPILE_TEST @@ -52,3 +54,5 @@ config SND_PXA910_SOC help Say Y if you want to add support for SoC audio on the Marvell PXA910 reference platform. + +endmenu diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index e86b4a03dd61..e6e24f3b9922 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_QCOM - tristate "ASoC support for QCOM platforms" + tristate "Qualcomm" depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices @@ -186,6 +186,7 @@ config SND_SOC_SM8250 tristate "SoC Machine driver for SM8250 boards" depends on QCOM_APR && SOUNDWIRE depends on COMMON_CLK + depends on SND_SOC_QCOM_OFFLOAD_UTILS || !SND_SOC_QCOM_OFFLOAD_UTILS select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_QCOM_SDW diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 9946f12254b3..b456e096f138 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -202,7 +202,6 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, struct regmap *map; unsigned int dai_id = cpu_dai->driver->id; - component->id = dai_id; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1190,13 +1189,14 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component) { struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct regmap *map; - unsigned int dai_id = component->id; - if (dai_id == LPASS_DP_RX) + if (drvdata->hdmi_port_enable) { map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; + regcache_cache_only(map, true); + regcache_mark_dirty(map); + } + map = drvdata->lpaif_map; regcache_cache_only(map, true); regcache_mark_dirty(map); @@ -1207,14 +1207,19 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component) { struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct regmap *map; - unsigned int dai_id = component->id; + int ret; - if (dai_id == LPASS_DP_RX) + if (drvdata->hdmi_port_enable) { map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; + regcache_cache_only(map, false); + ret = regcache_sync(map); + if (ret) + return ret; + } + map = drvdata->lpaif_map; regcache_cache_only(map, false); + return regcache_sync(map); } @@ -1224,7 +1229,9 @@ static int lpass_platform_copy(struct snd_soc_component *component, unsigned long bytes) { struct snd_pcm_runtime *rt = substream->runtime; - unsigned int dai_id = component->id; + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + unsigned int dai_id = cpu_dai->driver->id; int ret = 0; void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos + diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c index e758411603be..03838582aead 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c @@ -69,17 +69,17 @@ static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw, return clk->rate; } -static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_q6dsp_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - return rate; + return 0; } static const struct clk_ops clk_q6dsp_ops = { .prepare = clk_q6dsp_prepare, .unprepare = clk_q6dsp_unprepare, .set_rate = clk_q6dsp_set_rate, - .round_rate = clk_q6dsp_round_rate, + .determine_rate = clk_q6dsp_determine_rate, .recalc_rate = clk_q6dsp_recalc_rate, }; diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 99fd34728e38..73f9f82c4e25 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -186,6 +186,7 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) static const struct of_device_id snd_sc8280xp_dt_match[] = { {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, + {.compatible = "qcom,qcs8275-sndcard", "qcs8275"}, {.compatible = "qcom,qcs9075-sndcard", "qcs9075"}, {.compatible = "qcom,qcs9100-sndcard", "qcs9100"}, {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index b70b2a5031df..f5b75a06e5bd 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -16,7 +16,6 @@ #include "usb_offload_utils.h" #include "sdw.h" -#define DRIVER_NAME "sm8250" #define MI2S_BCLK_RATE 1536000 struct sm8250_snd_data { @@ -26,6 +25,7 @@ struct sm8250_snd_data { struct snd_soc_jack jack; struct snd_soc_jack usb_offload_jack; bool usb_offload_jack_setup; + struct snd_soc_jack dp_jack; bool jack_setup; }; @@ -33,14 +33,16 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - int ret; - if (cpu_dai->id == USB_RX) - ret = qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, - &data->usb_offload_jack_setup); - else - ret = qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); - return ret; + switch (cpu_dai->id) { + case DISPLAY_PORT_RX: + return qcom_snd_dp_jack_setup(rtd, &data->dp_jack, 0); + case USB_RX: + return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, + &data->usb_offload_jack_setup); + default: + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + } } static void sm8250_snd_exit(struct snd_soc_pcm_runtime *rtd) @@ -200,15 +202,17 @@ static int sm8250_platform_probe(struct platform_device *pdev) if (ret) return ret; - card->driver_name = DRIVER_NAME; + card->driver_name = of_device_get_match_data(dev); sm8250_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } static const struct of_device_id snd_sm8250_dt_match[] = { - {.compatible = "qcom,sm8250-sndcard"}, - {.compatible = "qcom,qrb4210-rb2-sndcard"}, - {.compatible = "qcom,qrb5165-rb5-sndcard"}, + { .compatible = "fairphone,fp4-sndcard", .data = "sm7225" }, + { .compatible = "fairphone,fp5-sndcard", .data = "qcm6490" }, + { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, + { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, + { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, {} }; diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig index dabf02a955ca..11c2027c88a7 100644 --- a/sound/soc/renesas/Kconfig +++ b/sound/soc/renesas/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -menu "SoC Audio support for Renesas SoCs" +menu "Renesas" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST config SND_SOC_PCM_SH7760 diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index a72f36d3ca2c..9f086906a2e5 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -597,7 +597,7 @@ int rsnd_dai_connect(struct rsnd_mod *mod, dev_dbg(dev, "%s is connected to io (%s)\n", rsnd_mod_name(mod), - snd_pcm_direction_name(io->substream->stream)); + rsnd_io_is_play(io) ? "Playback" : "Capture"); return 0; } @@ -1075,7 +1075,6 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); - struct device_node *np; int is_play = rsnd_io_is_play(io); int i; @@ -1094,7 +1093,7 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, if (!node) break; - for_each_child_of_node(ssiu_np, np) { + for_each_child_of_node_scoped(ssiu_np, np) { if (np == node) { rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); dev_dbg(dev, "%s is part of TDM Split\n", io->name); @@ -1154,21 +1153,18 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, { struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; int i; if (!node) return; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = mod_get(priv, i); @@ -1217,16 +1213,13 @@ int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *na int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name) { struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; int i; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); + if (i < 0) return 0; - } i++; } @@ -1250,7 +1243,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; - struct device_node *ports, *node; + struct device_node *node; int nr = 0; int i = 0; @@ -1270,7 +1263,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) of_node_put(node); - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (!of_node_name_eq(node, RSND_NODE_DAI)) continue; @@ -1279,7 +1272,6 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) i++; if (i >= RSND_MAX_COMPONENT) { dev_info(dev, "reach to max component\n"); - of_node_put(node); break; } } @@ -1290,7 +1282,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) /* * Audio-Graph-Card */ - for_each_child_of_node(np, ports) { + for_each_child_of_node_scoped(np, ports) { node = rsnd_pick_endpoint_node_for_ports(ports, np); if (!node) continue; @@ -1299,7 +1291,6 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) i++; if (i >= RSND_MAX_COMPONENT) { dev_info(dev, "reach to max component\n"); - of_node_put(ports); break; } } @@ -1505,10 +1496,9 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) dai_i = 0; if (is_graph) { struct device_node *dai_np_port; - struct device_node *ports; struct device_node *dai_np; - for_each_child_of_node(np, ports) { + for_each_child_of_node_scoped(np, ports) { dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np); if (!dai_np_port) continue; @@ -1525,14 +1515,11 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) } } } else { - struct device_node *node; - struct device_node *dai_np; - - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (!of_node_name_eq(node, RSND_NODE_DAI)) continue; - for_each_child_of_node(node, dai_np) { + for_each_child_of_node_scoped(node, dai_np) { __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { rdai = rsnd_rdai_get(priv, dai_i); diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c index a26ec7b780cd..bd4c61f9fb3c 100644 --- a/sound/soc/renesas/rcar/ctu.c +++ b/sound/soc/renesas/rcar/ctu.c @@ -316,7 +316,6 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) int rsnd_ctu_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ctu *ctu; struct clk *clk; @@ -344,7 +343,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ctu = rsnd_ctu_get(priv, i); /* @@ -357,16 +356,13 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_ctu_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, clk, RSND_MOD_CTU, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_ctu_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c index 2342bbb6fe92..2035ce06fe4c 100644 --- a/sound/soc/renesas/rcar/dma.c +++ b/sound/soc/renesas/rcar/dma.c @@ -194,14 +194,12 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *nam struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct dma_chan *chan = NULL; - struct device_node *np; int i = 0; - for_each_child_of_node(of_node, np) { + for_each_child_of_node_scoped(of_node, np) { i = rsnd_node_fixed_index(dev, np, name, i); if (i < 0) { chan = NULL; - of_node_put(np); break; } diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c index da91dd301aab..988cbddbc611 100644 --- a/sound/soc/renesas/rcar/dvc.c +++ b/sound/soc/renesas/rcar/dvc.c @@ -324,7 +324,6 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) int rsnd_dvc_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dvc *dvc; struct clk *clk; @@ -352,7 +351,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { dvc = rsnd_dvc_get(priv, i); snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", @@ -361,16 +360,13 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_dvc_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_dvc_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c index 024d91cc8748..aea74e703305 100644 --- a/sound/soc/renesas/rcar/mix.c +++ b/sound/soc/renesas/rcar/mix.c @@ -288,7 +288,6 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) int rsnd_mix_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mix *mix; struct clk *clk; @@ -316,7 +315,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { mix = rsnd_mix_get(priv, i); snprintf(name, MIX_NAME_SIZE, "%s.%d", @@ -325,16 +324,13 @@ int rsnd_mix_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_mix_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, clk, RSND_MOD_MIX, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_mix_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c index 75c9e91bada1..36d31ab8ac6a 100644 --- a/sound/soc/renesas/rcar/msiof.c +++ b/sound/soc/renesas/rcar/msiof.c @@ -30,56 +30,15 @@ #include #include #include +#include #include #include -/* register */ -#define SITMDR1 0x00 -#define SITMDR2 0x04 -#define SITMDR3 0x08 -#define SIRMDR1 0x10 -#define SIRMDR2 0x14 -#define SIRMDR3 0x18 -#define SICTR 0x28 -#define SISTR 0x40 -#define SIIER 0x44 -#define SITFDR 0x50 -#define SIRFDR 0x60 - -/* SITMDR1/ SIRMDR1 */ -#define PCON (1 << 30) /* Transfer Signal Connection */ -#define SYNCMD_LR (3 << 28) /* L/R mode */ -#define SYNCAC (1 << 25) /* Sync Polarity (Active-low) */ -#define DTDL_1 (1 << 20) /* 1-clock-cycle delay */ -#define TXSTP (1 << 0) /* Transmission/Reception Stop on FIFO */ - -/* SITMDR2 and SIRMDR2 */ -#define BITLEN1(x) (((x) - 1) << 24) /* Data Size (8-32 bits) */ -#define GRP (1 << 30) /* Group count */ - -/* SICTR */ -#define TEDG (1 << 27) /* Transmit Timing (1 = falling edge) */ -#define REDG (1 << 26) /* Receive Timing (1 = rising edge) */ -#define TXE (1 << 9) /* Transmit Enable */ -#define RXE (1 << 8) /* Receive Enable */ - /* SISTR */ -#define TFSERR (1 << 21) /* Transmit Frame Synchronization Error */ -#define TFOVF (1 << 20) /* Transmit FIFO Overflow */ -#define TFUDF (1 << 19) /* Transmit FIFO Underflow */ -#define RFSERR (1 << 5) /* Receive Frame Synchronization Error */ -#define RFUDF (1 << 4) /* Receive FIFO Underflow */ -#define RFOVF (1 << 3) /* Receive FIFO Overflow */ -#define SISTR_ERR_TX (TFSERR | TFOVF | TFUDF) -#define SISTR_ERR_RX (RFSERR | RFOVF | RFUDF) +#define SISTR_ERR_TX (SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF) +#define SISTR_ERR_RX (SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF) #define SISTR_ERR (SISTR_ERR_TX | SISTR_ERR_RX) -/* SIIER */ -#define TDMAE (1 << 31) /* Transmit Data DMA Transfer Req. Enable */ -#define TDREQE (1 << 28) /* Transmit Data Transfer Request Enable */ -#define RDMAE (1 << 15) /* Receive Data DMA Transfer Req. Enable */ -#define RDREQE (1 << 12) /* Receive Data Transfer Request Enable */ - /* * The data on memory in 24bit case is located at side * [ xxxxxx] @@ -174,42 +133,45 @@ static int msiof_hw_start(struct snd_soc_component *component, /* SITMDRx */ if (is_play) { - val = PCON | SYNCMD_LR | SYNCAC | TXSTP; + val = SITMDR1_PCON | + FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | + SIMDR1_SYNCAC | SIMDR1_XXSTP; if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) - val |= DTDL_1; + val |= FIELD_PREP(SIMDR1_DTDL, 1); msiof_write(priv, SITMDR1, val); - val = BITLEN1(width); - msiof_write(priv, SITMDR2, val | GRP); + val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); + msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SITMDR3, val); } /* SIRMDRx */ else { - val = SYNCMD_LR | SYNCAC; + val = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | + SIMDR1_SYNCAC; if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) - val |= DTDL_1; + val |= FIELD_PREP(SIMDR1_DTDL, 1); msiof_write(priv, SIRMDR1, val); - val = BITLEN1(width); - msiof_write(priv, SIRMDR2, val | GRP); + val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); + msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SIRMDR3, val); } /* SIIER */ if (is_play) - val = TDREQE | TDMAE | SISTR_ERR_TX; + val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX; else - val = RDREQE | RDMAE | SISTR_ERR_RX; + val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, val); /* SICTR */ if (is_play) - val = TXE | TEDG; + val = SICTR_TXE | SICTR_TEDG; else - val = RXE | REDG; + val = SICTR_RXE | SICTR_REDG; msiof_update_and_wait(priv, SICTR, val, val, val); msiof_status_clear(priv); @@ -230,9 +192,9 @@ static int msiof_hw_stop(struct snd_soc_component *component, /* SIIER */ if (is_play) - val = TDREQE | TDMAE | SISTR_ERR_TX; + val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX; else - val = RDREQE | RDMAE | SISTR_ERR_RX; + val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, 0); /* Stop DMAC */ @@ -240,9 +202,9 @@ static int msiof_hw_stop(struct snd_soc_component *component, /* SICTR */ if (is_play) - val = TXE; + val = SICTR_TXE; else - val = RXE; + val = SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, 0, 0); /* indicate error status if exist */ @@ -478,22 +440,22 @@ static irqreturn_t msiof_interrupt(int irq, void *data) substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK]; if (substream && (sistr & SISTR_ERR_TX)) { // snd_pcm_stop_xrun(substream); - if (sistr & TFSERR) + if (sistr & SISTR_TFSERR) priv->err_syc[SNDRV_PCM_STREAM_PLAYBACK]++; - if (sistr & TFOVF) + if (sistr & SISTR_TFOVF) priv->err_ovf[SNDRV_PCM_STREAM_PLAYBACK]++; - if (sistr & TFUDF) + if (sistr & SISTR_TFUDF) priv->err_udf[SNDRV_PCM_STREAM_PLAYBACK]++; } substream = priv->substream[SNDRV_PCM_STREAM_CAPTURE]; if (substream && (sistr & SISTR_ERR_RX)) { // snd_pcm_stop_xrun(substream); - if (sistr & RFSERR) + if (sistr & SISTR_RFSERR) priv->err_syc[SNDRV_PCM_STREAM_CAPTURE]++; - if (sistr & RFOVF) + if (sistr & SISTR_RFOVF) priv->err_ovf[SNDRV_PCM_STREAM_CAPTURE]++; - if (sistr & RFUDF) + if (sistr & SISTR_RFUDF) priv->err_udf[SNDRV_PCM_STREAM_CAPTURE]++; } diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index 7d73b183bda6..f47bf38c2f94 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -715,7 +715,6 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) int rsnd_src_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_src *src; struct clk *clk; @@ -742,14 +741,13 @@ int rsnd_src_probe(struct rsnd_priv *priv) priv->src = src; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (!of_device_is_available(np)) goto skip; i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); if (i < 0) { ret = -EINVAL; - of_node_put(np); goto rsnd_src_probe_done; } @@ -761,23 +759,19 @@ int rsnd_src_probe(struct rsnd_priv *priv) src->irq = irq_of_parse_and_map(np, 0); if (!src->irq) { ret = -EINVAL; - of_node_put(np); goto rsnd_src_probe_done; } clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_src_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(src), &rsnd_src_ops, clk, RSND_MOD_SRC, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_src_probe_done; - } skip: i++; diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index 0c6424a1fcac..d52056caa3ec 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -1115,7 +1115,6 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device *dev = rsnd_priv_to_dev(priv); struct device_node *node; - struct device_node *np; int i; node = rsnd_ssi_of_node(priv); @@ -1123,14 +1122,12 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, return; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = rsnd_ssi_mod_get(priv, i); @@ -1163,7 +1160,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; @@ -1191,14 +1187,13 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) priv->ssi_nr = nr; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (!of_device_is_available(np)) goto skip; i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); if (i < 0) { ret = -EINVAL; - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1210,7 +1205,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1223,7 +1217,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ssi->irq = irq_of_parse_and_map(np, 0); if (!ssi->irq) { ret = -EINVAL; - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1234,10 +1227,9 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, RSND_MOD_SSI, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_ssi_probe_done; - } + skip: i++; } diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c index 665e8b2db579..faf351126d57 100644 --- a/sound/soc/renesas/rcar/ssiu.c +++ b/sound/soc/renesas/rcar/ssiu.c @@ -478,17 +478,14 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, /* use rcar_sound,ssiu if exist */ if (node) { - struct device_node *np; int i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = rsnd_ssiu_mod_get(priv, i); diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index a08544827b2a..bd210fafe9fe 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -1,15 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_ROCKCHIP - tristate "ASoC support for Rockchip" +menu "Rockchip" depends on COMPILE_TEST || ARCH_ROCKCHIP - help - Say Y or M if you want to add support for codecs attached to - the Rockchip SoCs' Audio interfaces. You will also need to - select the audio interfaces to support below. + depends on HAVE_CLK config SND_SOC_ROCKCHIP_I2S tristate "Rockchip I2S Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for @@ -18,7 +13,6 @@ config SND_SOC_ROCKCHIP_I2S config SND_SOC_ROCKCHIP_I2S_TDM tristate "Rockchip I2S/TDM Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for the I2S/TDM driver for @@ -29,7 +23,6 @@ config SND_SOC_ROCKCHIP_I2S_TDM config SND_SOC_ROCKCHIP_PDM tristate "Rockchip PDM Controller Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM select RATIONAL help @@ -39,7 +32,6 @@ config SND_SOC_ROCKCHIP_PDM config SND_SOC_ROCKCHIP_SAI tristate "Rockchip SAI Controller Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for the Rockchip Serial Audio @@ -49,7 +41,6 @@ config SND_SOC_ROCKCHIP_SAI config SND_SOC_ROCKCHIP_SPDIF tristate "Rockchip SPDIF Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF driver for @@ -57,7 +48,7 @@ config SND_SOC_ROCKCHIP_SPDIF config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E @@ -68,7 +59,7 @@ config SND_SOC_ROCKCHIP_MAX98090 config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_RT5645 help @@ -77,7 +68,7 @@ config SND_SOC_ROCKCHIP_RT5645 config SND_SOC_RK3288_HDMI_ANALOG tristate "ASoC support multiple codecs for Rockchip RK3288 boards" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_HDMI_CODEC select SND_SOC_ES8328_I2C @@ -89,7 +80,7 @@ config SND_SOC_RK3288_HDMI_ANALOG config SND_SOC_RK3399_GRU_SOUND tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK && SPI + depends on I2C && GPIOLIB && SPI select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98357A select SND_SOC_RT5514 @@ -100,3 +91,5 @@ config SND_SOC_RK3399_GRU_SOUND help Say Y or M here if you want to add support multiple codecs for SoC audio on Rockchip RK3399 GRU boards. + +endmenu diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 602f1ddfad00..6695349ee561 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -378,19 +377,9 @@ static void rockchip_sai_xfer_start(struct rk_sai_dev *sai, int stream) static void rockchip_sai_xfer_stop(struct rk_sai_dev *sai, int stream) { unsigned int msk = 0, val = 0, clr = 0; - bool playback; - bool capture; - - if (stream < 0) { - playback = true; - capture = true; - } else if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - playback = true; - capture = false; - } else { - playback = true; - capture = false; - } + bool capture = stream == SNDRV_PCM_STREAM_CAPTURE || stream < 0; + bool playback = stream == SNDRV_PCM_STREAM_PLAYBACK || stream < 0; + /* could be <= 0 but we don't want to depend on enum values */ if (playback) { msk |= SAI_XFER_TXS_MASK; @@ -1437,43 +1426,32 @@ static int rockchip_sai_probe(struct platform_device *pdev) if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); - if (ret) { + if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to request irq %d\n", irq); - } } else { dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); } sai->mclk = devm_clk_get(&pdev->dev, "mclk"); - if (IS_ERR(sai->mclk)) { + if (IS_ERR(sai->mclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk), "Failed to get mclk\n"); - } - sai->hclk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(sai->hclk)) { + sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk"); + if (IS_ERR(sai->hclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk), "Failed to get hclk\n"); - } - - ret = clk_prepare_enable(sai->hclk); - if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to enable hclk\n"); regmap_read(sai->regmap, SAI_VERSION, &sai->version); ret = rockchip_sai_init_dai(sai, res, &dai); - if (ret) { - dev_err(&pdev->dev, "Failed to initialize DAI: %d\n", ret); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to initialize DAI\n"); ret = rockchip_sai_parse_paths(sai, node); - if (ret) { - dev_err(&pdev->dev, "Failed to parse paths: %d\n", ret); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); /* * From here on, all register accesses need to be wrapped in @@ -1484,10 +1462,8 @@ static int rockchip_sai_probe(struct platform_device *pdev) devm_pm_runtime_enable(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); ret = rockchip_sai_runtime_resume(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "Failed to resume device: %pe\n", ERR_PTR(ret)); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { @@ -1514,8 +1490,6 @@ static int rockchip_sai_probe(struct platform_device *pdev) /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */ if (pm_runtime_put(&pdev->dev)) rockchip_sai_runtime_suspend(&pdev->dev); -err_disable_hclk: - clk_disable_unprepare(sai->hclk); return ret; } diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 60b4b7b75215..ec7204f57fd4 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_SAMSUNG - tristate "ASoC support for Samsung" + tristate "Samsung" depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 8dc3b2da4c8f..61ed5e69391a 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -133,8 +133,6 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 5a02aac9b423..c8b06894ac88 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -95,8 +95,6 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 9262e5626584..f4cc5684ef0a 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -90,8 +90,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, break; } - card->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index d0f0c01365aa..1d0a782402f0 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -91,8 +91,6 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index ee20b9914aa1..6a3ba43f26bd 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "SoundWire (SDCA)" config SND_SOC_SDCA tristate @@ -7,5 +8,24 @@ config SND_SOC_SDCA This option enables support for the MIPI SoundWire Device Class for Audio (SDCA). +config SND_SOC_SDCA_HID + bool "SDCA HID support" + depends on SND_SOC_SDCA + depends on HID=y || HID=SND_SOC_SDCA + default y + help + This option enables support for audio jack button reporting using HID. + +config SND_SOC_SDCA_IRQ + bool "SDCA IRQ support" + select REGMAP + select REGMAP_IRQ + depends on SND_SOC_SDCA + default y + help + This option enables support for SDCA IRQs. + config SND_SOC_SDCA_OPTIONAL def_tristate SND_SOC_SDCA || !SND_SOC_SDCA + +endmenu diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 53344f108ca6..5e51760cb651 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o +snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o +snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o +snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o -obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 7bc8f6069f3d..c493ec530cc5 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -7,16 +7,22 @@ * https://www.mipi.org/mipi-sdca-v1-0-download */ +#include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include +#include +#include #include #include #include @@ -26,53 +32,6 @@ #include #include -static struct sdca_control *selector_find_control(struct device *dev, - struct sdca_entity *entity, - const int sel) -{ - int i; - - for (i = 0; i < entity->num_controls; i++) { - struct sdca_control *control = &entity->controls[i]; - - if (control->sel == sel) - return control; - } - - dev_err(dev, "%s: control %#x: missing\n", entity->label, sel); - return NULL; -} - -static struct sdca_control_range *control_find_range(struct device *dev, - struct sdca_entity *entity, - struct sdca_control *control, - int cols, int rows) -{ - struct sdca_control_range *range = &control->range; - - if ((cols && range->cols != cols) || (rows && range->rows != rows) || - !range->data) { - dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n", - entity->label, control->sel, range->cols, range->rows); - return NULL; - } - - return range; -} - -static struct sdca_control_range *selector_find_range(struct device *dev, - struct sdca_entity *entity, - int sel, int cols, int rows) -{ - struct sdca_control *control; - - control = selector_find_control(dev, entity, sel); - if (!control) - return NULL; - - return control_find_range(dev, entity, control, cols, rows); -} - static bool exported_control(struct sdca_entity *entity, struct sdca_control *control) { switch (SDCA_CTL_TYPE(entity->type, control->sel)) { @@ -93,6 +52,7 @@ static bool readonly_control(struct sdca_control *control) /** * sdca_asoc_count_component - count the various component parts + * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. * @num_widgets: Output integer pointer, will be filled with the * required number of DAPM widgets for the Function. @@ -212,7 +172,7 @@ static int entity_early_parse_ge(struct device *dev, const char **texts; int i; - control = selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE); if (!control) return -EINVAL; @@ -220,7 +180,7 @@ static int entity_early_parse_ge(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - range = control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0); + range = sdca_control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0); if (!range) return -EINVAL; @@ -229,11 +189,11 @@ static int entity_early_parse_ge(struct device *dev, if (!control_name) return -ENOMEM; - kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL); if (!soc_enum) return -ENOMEM; @@ -245,12 +205,12 @@ static int entity_early_parse_ge(struct device *dev, if (!values) return -ENOMEM; - texts[0] = "No Jack"; + texts[0] = "Jack Unplugged"; texts[1] = "Jack Unknown"; texts[2] = "Detection in Progress"; - values[0] = 0; - values[1] = 1; - values[2] = 2; + values[0] = SDCA_DETECTED_MODE_JACK_UNPLUGGED; + values[1] = SDCA_DETECTED_MODE_JACK_UNKNOWN; + values[2] = SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS; for (i = 0; i < range->rows; i++) { enum sdca_terminal_type type; @@ -397,6 +357,8 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, from = widget->off_val; to = widget->on_val; break; + default: + return 0; } for (i = 0; i < entity->pde.num_max_delay; i++) { @@ -440,7 +402,7 @@ static int entity_parse_pde(struct device *dev, unsigned int mask = 0; int i; - control = selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS); if (!control) return -EINVAL; @@ -449,7 +411,7 @@ static int entity_parse_pde(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - range = control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0); + range = sdca_control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0); if (!range) return -EINVAL; @@ -496,8 +458,8 @@ static int entity_parse_su_device(struct device *dev, return -EINVAL; } - range = selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE, - SDCA_SELECTED_MODE_NCOLS, 0); + range = sdca_selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE, + SDCA_SELECTED_MODE_NCOLS, 0); if (!range) return -EINVAL; @@ -558,11 +520,11 @@ static int entity_parse_su_class(struct device *dev, const char **texts; int i; - kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL); if (!soc_enum) return -ENOMEM; @@ -610,7 +572,7 @@ static int entity_parse_su(struct device *dev, return -EINVAL; } - control = selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR); if (!control) return -EINVAL; @@ -632,7 +594,6 @@ static int entity_parse_mu(struct device *dev, { struct sdca_control *control; struct snd_kcontrol_new *kctl; - int cn; int i; if (!entity->num_sources) { @@ -640,7 +601,7 @@ static int entity_parse_mu(struct device *dev, return -EINVAL; } - control = selector_find_control(dev, entity, SDCA_CTL_MU_MIXER); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_MU_MIXER); if (!control) return -EINVAL; @@ -649,18 +610,11 @@ static int entity_parse_mu(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - if (entity->num_sources != hweight64(control->cn_list)) { - dev_err(dev, "%s: mismatched control and sources\n", entity->label); - return -EINVAL; - } - kctl = devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - i = 0; - for_each_set_bit(cn, (unsigned long *)&control->cn_list, - BITS_PER_TYPE(control->cn_list)) { + for (i = 0; i < entity->num_sources; i++) { const char *control_name; struct soc_mixer_control *mc; @@ -669,7 +623,7 @@ static int entity_parse_mu(struct device *dev, if (!control_name) return -ENOMEM; - mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; @@ -685,7 +639,6 @@ static int entity_parse_mu(struct device *dev, kctl[i].info = snd_soc_info_volsw; kctl[i].get = snd_soc_dapm_get_volsw; kctl[i].put = snd_soc_dapm_put_volsw; - i++; } (*widget)->id = snd_soc_dapm_mixer; @@ -850,7 +803,7 @@ static int control_limit_kctl(struct device *dev, /* * FIXME: For now only handle the simple case of a single linear range */ - range = control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1); + range = sdca_control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1); if (!range) return -EINVAL; @@ -923,7 +876,7 @@ static int populate_control(struct device *dev, if (!control_name) return -ENOMEM; - mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; @@ -995,7 +948,7 @@ static int populate_pin_switch(struct device *dev, * sdca_asoc_populate_controls - fill in an array of ALSA controls for a Function * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. - * @route: Array of ALSA controls to be populated. + * @kctl: Array of ALSA controls to be populated. * * This function populates an array of ALSA controls from the DisCo * information for a particular SDCA Function. Typically, @@ -1137,9 +1090,9 @@ static int populate_rate_format(struct device *dev, } if (entity->iot.clock) { - range = selector_find_range(dev, entity->iot.clock, - SDCA_CTL_CS_SAMPLERATEINDEX, - SDCA_SAMPLERATEINDEX_NCOLS, 0); + range = sdca_selector_find_range(dev, entity->iot.clock, + SDCA_CTL_CS_SAMPLERATEINDEX, + SDCA_SAMPLERATEINDEX_NCOLS, 0); if (!range) return -EINVAL; @@ -1151,7 +1104,7 @@ static int populate_rate_format(struct device *dev, clock_rates = UINT_MAX; } - range = selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); + range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); if (!range) return -EINVAL; @@ -1242,7 +1195,11 @@ EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA"); * sdca_asoc_populate_component - fill in a component driver for a Function * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. - * @copmonent_drv: Pointer to the component driver to be populated. + * @component_drv: Pointer to the component driver to be populated. + * @dai_drv: Pointer to the DAI driver array to be allocated and populated. + * @num_dai_drv: Pointer to integer that will be populated with the number of + * DAI drivers. + * @ops: DAI ops pointer that will be used for each DAI driver. * * This function populates a snd_soc_component_driver structure based * on the DisCo information for a particular SDCA Function. It does @@ -1309,3 +1266,351 @@ int sdca_asoc_populate_component(struct device *dev, return 0; } EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA"); + +/** + * sdca_asoc_set_constraints - constrain channels available on a DAI + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @substream: Pointer to the PCM substream. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from startup(). + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + static const unsigned int channel_list[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + }; + struct sdca_entity *entity = &function->entities[dai->id]; + struct snd_pcm_hw_constraint_list *constraint; + struct sdca_control_range *range; + struct sdca_control *control; + unsigned int channel_mask = 0; + int i, ret; + + static_assert(ARRAY_SIZE(channel_list) == SDCA_MAX_CHANNEL_COUNT); + static_assert(sizeof(channel_mask) * BITS_PER_BYTE >= SDCA_MAX_CHANNEL_COUNT); + + if (entity->type != SDCA_ENTITY_TYPE_IT) + return 0; + + control = sdca_selector_find_control(dev, entity, SDCA_CTL_IT_CLUSTERINDEX); + if (!control) + return -EINVAL; + + range = sdca_control_find_range(dev, entity, control, SDCA_CLUSTER_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + int clusterid = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); + struct sdca_cluster *cluster; + + cluster = sdca_id_find_cluster(dev, function, clusterid); + if (!cluster) + return -ENODEV; + + channel_mask |= (1 << (cluster->num_channels - 1)); + } + + dev_dbg(dev, "%s: set channel constraint mask: %#x\n", + entity->label, channel_mask); + + constraint = kzalloc(sizeof(*constraint), GFP_KERNEL); + if (!constraint) + return -ENOMEM; + + constraint->count = ARRAY_SIZE(channel_list); + constraint->list = channel_list; + constraint->mask = channel_mask; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + constraint); + if (ret) { + dev_err(dev, "%s: failed to add constraint: %d\n", entity->label, ret); + kfree(constraint); + return ret; + } + + dai->priv = constraint; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA"); + +/** + * sdca_asoc_free_constraints - free constraint allocations + * @substream: Pointer to the PCM substream. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from shutdown(). + */ +void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_hw_constraint_list *constraint = dai->priv; + + kfree(constraint); +} +EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA"); + +/** + * sdca_asoc_get_port - return SoundWire port for a DAI + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from hw_params(). + * + * Return: Returns a positive port number on success, and a negative error + * code on failure. + */ +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_soc_dai *dai) +{ + struct sdca_entity *entity = &function->entities[dai->id]; + struct sdca_control_range *range; + unsigned int reg, val; + int sel = -EINVAL; + int i, ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + sel = SDCA_CTL_IT_DATAPORT_SELECTOR; + break; + case SDCA_ENTITY_TYPE_OT: + sel = SDCA_CTL_OT_DATAPORT_SELECTOR; + break; + default: + break; + } + + if (sel < 0 || !entity->iot.is_dataport) { + dev_err(dev, "%s: port number only available for dataports\n", + entity->label); + return -EINVAL; + } + + range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS, + SDCA_DATAPORT_SELECTOR_NROWS); + if (!range) + return -EINVAL; + + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0); + + ret = regmap_read(regmap, reg, &val); + if (ret) { + dev_err(dev, "%s: failed to read dataport selector: %d\n", + entity->label, ret); + return ret; + } + + for (i = 0; i < range->rows; i++) { + static const u8 port_mask = 0xF; + + sel = sdca_range(range, val & port_mask, i); + + /* + * FIXME: Currently only a single dataport is supported, so + * return the first one found, technically up to 4 dataports + * could be linked, but this is not yet supported. + */ + if (sel != 0xFF) + return sel; + + val >>= hweight8(port_mask); + } + + dev_err(dev, "%s: no dataport found\n", entity->label); + return -ENODEV; +} +EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA"); + +static int set_cluster(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, unsigned int channels) +{ + int sel = SDCA_CTL_IT_CLUSTERINDEX; + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_CLUSTER_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + int cluster_id = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); + struct sdca_cluster *cluster; + + cluster = sdca_id_find_cluster(dev, function, cluster_id); + if (!cluster) + return -ENODEV; + + if (cluster->num_channels == channels) { + int index = sdca_range(range, SDCA_CLUSTER_BYTEINDEX, i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, index); + if (ret) { + dev_err(dev, "%s: failed to write cluster index: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set cluster to %d (%d channels)\n", + entity->label, index, channels); + + return 0; + } + } + + dev_err(dev, "%s: no cluster for %d channels\n", entity->label, channels); + return -EINVAL; +} + +static int set_clock(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, int target_rate) +{ + int sel = SDCA_CTL_CS_SAMPLERATEINDEX; + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_SAMPLERATEINDEX_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + unsigned int rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i); + + if (rate == target_rate) { + unsigned int index = sdca_range(range, + SDCA_SAMPLERATEINDEX_INDEX, + i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, index); + if (ret) { + dev_err(dev, "%s: failed to write clock rate: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set clock rate to %d (%dHz)\n", + entity->label, index, rate); + + return 0; + } + } + + dev_err(dev, "%s: no clock rate for %dHz\n", entity->label, target_rate); + return -EINVAL; +} + +static int set_usage(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, int sel, + int target_rate, int target_width) +{ + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i); + unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i); + + if ((!rate || rate == target_rate) && width == target_width) { + unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, usage); + if (ret) { + dev_err(dev, "%s: failed to write usage: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set usage to %#x (%dHz, %d bits)\n", + entity->label, usage, target_rate, target_width); + + return 0; + } + } + + dev_err(dev, "%s: no usage for %dHz, %dbits\n", + entity->label, target_rate, target_width); + return -EINVAL; +} + +/** + * sdca_asoc_hw_params - set SDCA channels, sample rate and bit depth + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @substream: Pointer to the PCM substream. + * @params: Pointer to the hardware parameters. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from hw_params(). + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdca_entity *entity = &function->entities[dai->id]; + int channels = params_channels(params); + int width = params_width(params); + int rate = params_rate(params); + int usage_sel; + int ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + ret = set_cluster(dev, regmap, function, entity, channels); + if (ret) + return ret; + + usage_sel = SDCA_CTL_IT_USAGE; + break; + case SDCA_ENTITY_TYPE_OT: + usage_sel = SDCA_CTL_OT_USAGE; + break; + default: + dev_err(dev, "%s: hw_params on non-terminal entity\n", entity->label); + return -EINVAL; + } + + if (entity->iot.clock) { + ret = set_clock(dev, regmap, function, entity->iot.clock, rate); + if (ret) + return ret; + } + + ret = set_usage(dev, regmap, function, entity, usage_sel, rate, width); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_hw_params, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 64ac26443890..f26f597dca9e 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -19,6 +19,7 @@ #include #include #include +#include /* * Should be long enough to encompass all the MIPI DisCo properties. @@ -211,7 +212,7 @@ static int find_sdca_init_table(struct device *dev, } else if (num_init_writes % sizeof(*raw) != 0) { dev_err(dev, "%pfwP: init table size invalid\n", function_node); return -EINVAL; - } else if (num_init_writes > SDCA_MAX_INIT_COUNT) { + } else if ((num_init_writes / sizeof(*raw)) > SDCA_MAX_INIT_COUNT) { dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node); return -EINVAL; } @@ -813,6 +814,43 @@ static int find_sdca_control_range(struct device *dev, return 0; } +static int find_sdca_control_value(struct device *dev, struct sdca_entity *entity, + struct fwnode_handle *control_node, + struct sdca_control *control, + const char * const label) +{ + char property[SDCA_PROPERTY_LENGTH]; + bool global = true; + int ret, cn, i; + u32 tmp; + + snprintf(property, sizeof(property), "mipi-sdca-control-%s", label); + + ret = fwnode_property_read_u32(control_node, property, &tmp); + if (ret == -EINVAL) + global = false; + else if (ret) + return ret; + + i = 0; + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + if (!global) { + snprintf(property, sizeof(property), + "mipi-sdca-control-cn-%d-%s", cn, label); + + ret = fwnode_property_read_u32(control_node, property, &tmp); + if (ret) + return ret; + } + + control->values[i] = tmp; + i++; + } + + return 0; +} + /* * TODO: Add support for -cn- properties, allowing different channels to have * different defaults etc. @@ -842,45 +880,46 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti control->layers = tmp; + ret = fwnode_property_read_u64(control_node, "mipi-sdca-control-cn-list", + &control->cn_list); + if (ret == -EINVAL) { + /* Spec allows not specifying cn-list if only the first number is used */ + control->cn_list = 0x1; + } else if (ret || !control->cn_list) { + dev_err(dev, "%s: control %#x: cn list missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + control->values = devm_kzalloc(dev, hweight64(control->cn_list), GFP_KERNEL); + if (!control->values) + return -ENOMEM; + switch (control->mode) { case SDCA_ACCESS_MODE_DC: - ret = fwnode_property_read_u32(control_node, - "mipi-sdca-control-dc-value", - &tmp); + ret = find_sdca_control_value(dev, entity, control_node, control, + "dc-value"); if (ret) { dev_err(dev, "%s: control %#x: dc value missing: %d\n", entity->label, control->sel, ret); return ret; } - control->value = tmp; control->has_fixed = true; break; case SDCA_ACCESS_MODE_RW: case SDCA_ACCESS_MODE_DUAL: - ret = fwnode_property_read_u32(control_node, - "mipi-sdca-control-default-value", - &tmp); - if (!ret) { - control->value = tmp; + ret = find_sdca_control_value(dev, entity, control_node, control, + "default-value"); + if (!ret) control->has_default = true; - } - ret = fwnode_property_read_u32(control_node, - "mipi-sdca-control-fixed-value", - &tmp); - if (!ret) { - if (control->has_default && control->value != tmp) { - dev_err(dev, - "%s: control %#x: default and fixed value don't match\n", - entity->label, control->sel); - return -EINVAL; - } - - control->value = tmp; + ret = find_sdca_control_value(dev, entity, control_node, control, + "fixed-value"); + if (!ret) control->has_fixed = true; - } - + fallthrough; + case SDCA_ACCESS_MODE_RO: control->deferrable = fwnode_property_read_bool(control_node, "mipi-sdca-control-deferrable"); break; @@ -895,22 +934,13 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti return ret; } - ret = fwnode_property_read_u64(control_node, "mipi-sdca-control-cn-list", - &control->cn_list); - if (ret == -EINVAL) { - /* Spec allows not specifying cn-list if only the first number is used */ - control->cn_list = 0x1; - } else if (ret || !control->cn_list) { - dev_err(dev, "%s: control %#x: cn list missing: %d\n", - entity->label, control->sel, ret); - return ret; - } - ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-interrupt-position", &tmp); if (!ret) control->interrupt_position = tmp; + else + control->interrupt_position = SDCA_NO_INTERRUPT; control->label = find_sdca_control_label(dev, entity, control); if (!control->label) @@ -919,11 +949,10 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti control->type = find_sdca_control_datatype(entity, control); control->nbits = find_sdca_control_bits(entity, control); - dev_info(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d value %#x %s\n", + dev_info(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d %s\n", entity->label, control->label, control->sel, control->mode, control->layers, control->cn_list, - control->interrupt_position, control->value, - control->deferrable ? "deferrable" : ""); + control->interrupt_position, control->deferrable ? "deferrable" : ""); return 0; } @@ -1220,6 +1249,93 @@ static int find_sdca_entity_ge(struct device *dev, return -EINVAL; } +static int +find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, + struct fwnode_handle *entity_node, struct sdca_entity *entity) +{ + struct sdca_entity_hide *hide = &entity->hide; + unsigned int delay, *af_list = hide->af_number_list; + int nval, ret; + unsigned char *report_desc = NULL; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-RxUMP-ownership-transition-maxdelay", &delay); + if (!ret) + hide->max_delay = delay; + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); + if (nval > 0) { + hide->num_hidtx_ids = nval; + hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, + sizeof(*hide->hidtx_ids), GFP_KERNEL); + if (!hide->hidtx_ids) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDTx-supported-report-ids", + hide->hidtx_ids, + hide->num_hidtx_ids); + if (ret < 0) + return ret; + } + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); + if (nval > 0) { + hide->num_hidrx_ids = nval; + hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, + sizeof(*hide->hidrx_ids), GFP_KERNEL); + if (!hide->hidrx_ids) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDRx-supported-report-ids", + hide->hidrx_ids, + hide->num_hidrx_ids); + if (ret < 0) + return ret; + } + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); + if (nval <= 0) { + dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", + entity_node, nval); + return -EINVAL; + } else if (nval > SDCA_MAX_FUNCTION_COUNT) { + dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); + return -EINVAL; + } + + hide->hide_reside_function_num = nval; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-hide-related-audio-function-list", af_list, nval); + + nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (nval) + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&hide->hid_desc, nval); + + if (hide->hid_desc.bNumDescriptors) { + nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (nval) { + report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); + if (!report_desc) + return -ENOMEM; + hide->hid_report_desc = report_desc; + fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", + report_desc, nval); + + /* add HID device */ + ret = sdca_add_hid_device(dev, entity); + if (ret) { + dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); + return ret; + } + } + } + + return 0; +} + static int find_sdca_entity(struct device *dev, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, @@ -1261,6 +1377,9 @@ static int find_sdca_entity(struct device *dev, case SDCA_ENTITY_TYPE_GE: ret = find_sdca_entity_ge(dev, entity_node, entity); break; + case SDCA_ENTITY_TYPE_HIDE: + ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); + break; default: break; } @@ -1541,7 +1660,6 @@ static int find_sdca_entity_connection(struct device *dev, ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list); if (ret == -EINVAL) { /* Allow missing pin lists, assume no pins. */ - dev_warn(dev, "%s: missing pin list\n", entity->label); return 0; } else if (ret) { dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret); @@ -1848,5 +1966,73 @@ int sdca_parse_function(struct device *dev, } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); +struct sdca_control *sdca_selector_find_control(struct device *dev, + struct sdca_entity *entity, + const int sel) +{ + int i; + + for (i = 0; i < entity->num_controls; i++) { + struct sdca_control *control = &entity->controls[i]; + + if (control->sel == sel) + return control; + } + + dev_err(dev, "%s: control %#x: missing\n", entity->label, sel); + return NULL; +} +EXPORT_SYMBOL_NS(sdca_selector_find_control, "SND_SOC_SDCA"); + +struct sdca_control_range *sdca_control_find_range(struct device *dev, + struct sdca_entity *entity, + struct sdca_control *control, + int cols, int rows) +{ + struct sdca_control_range *range = &control->range; + + if ((cols && range->cols != cols) || (rows && range->rows != rows) || + !range->data) { + dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n", + entity->label, control->sel, range->cols, range->rows); + return NULL; + } + + return range; +} +EXPORT_SYMBOL_NS(sdca_control_find_range, "SND_SOC_SDCA"); + +struct sdca_control_range *sdca_selector_find_range(struct device *dev, + struct sdca_entity *entity, + int sel, int cols, int rows) +{ + struct sdca_control *control; + + control = sdca_selector_find_control(dev, entity, sel); + if (!control) + return NULL; + + return sdca_control_find_range(dev, entity, control, cols, rows); +} +EXPORT_SYMBOL_NS(sdca_selector_find_range, "SND_SOC_SDCA"); + +struct sdca_cluster *sdca_id_find_cluster(struct device *dev, + struct sdca_function_data *function, + const int id) +{ + int i; + + for (i = 0; i < function->num_clusters; i++) { + struct sdca_cluster *cluster = &function->clusters[i]; + + if (cluster->id == id) + return cluster; + } + + dev_err(dev, "%s: cluster %#x: missing\n", function->desc->name, id); + return NULL; +} +EXPORT_SYMBOL_NS(sdca_id_find_cluster, "SND_SOC_SDCA"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c new file mode 100644 index 000000000000..967f7ec6fb79 --- /dev/null +++ b/sound/soc/sdca/sdca_hid.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sdwhid_parse(struct hid_device *hid) +{ + struct sdca_entity *entity = hid->driver_data; + unsigned int rsize; + int ret; + + rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + + ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); + + if (!ret) + return 0; + + dev_err(&hid->dev, "parsing report descriptor failed\n"); + return ret; +} + +static int sdwhid_start(struct hid_device *hid) +{ + return 0; +} + +static void sdwhid_stop(struct hid_device *hid) +{ +} + +static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + /* not implemented yet */ + return 0; + case HID_REQ_SET_REPORT: + /* not implemented yet */ + return 0; + default: + return -EIO; + } +} + +static int sdwhid_open(struct hid_device *hid) +{ + return 0; +} + +static void sdwhid_close(struct hid_device *hid) +{ +} + +static const struct hid_ll_driver sdw_hid_driver = { + .parse = sdwhid_parse, + .start = sdwhid_start, + .stop = sdwhid_stop, + .open = sdwhid_open, + .close = sdwhid_close, + .raw_request = sdwhid_raw_request, +}; + +int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) +{ + struct sdw_bus *bus; + struct hid_device *hid; + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret; + + bus = slave->bus; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &sdw_hid_driver; + + hid->dev.parent = dev; + hid->bus = BUS_SDW; + hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); + + snprintf(hid->name, sizeof(hid->name), + "HID sdw:%01x:%01x:%04x:%04x:%02x", + bus->controller_id, bus->link_id, slave->id.mfg_id, + slave->id.part_id, slave->id.class_id); + + snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); + + hid->driver_data = entity; + + ret = hid_add_device(hid); + if (ret && ret != -ENODEV) { + dev_err(dev, "can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + entity->hide.hid = hid; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SDCA HID library"); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c new file mode 100644 index 000000000000..8018773ee426 --- /dev/null +++ b/sound/soc/sdca/sdca_interrupts.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \ + SDW_SCP_SDCA_INTMASK_SDCA_##number) + +static const struct regmap_irq regmap_irqs[SDCA_MAX_INTERRUPTS] = { + IRQ_SDCA(0), + IRQ_SDCA(1), + IRQ_SDCA(2), + IRQ_SDCA(3), + IRQ_SDCA(4), + IRQ_SDCA(5), + IRQ_SDCA(6), + IRQ_SDCA(7), + IRQ_SDCA(8), + IRQ_SDCA(9), + IRQ_SDCA(10), + IRQ_SDCA(11), + IRQ_SDCA(12), + IRQ_SDCA(13), + IRQ_SDCA(14), + IRQ_SDCA(15), + IRQ_SDCA(16), + IRQ_SDCA(17), + IRQ_SDCA(18), + IRQ_SDCA(19), + IRQ_SDCA(20), + IRQ_SDCA(21), + IRQ_SDCA(22), + IRQ_SDCA(23), + IRQ_SDCA(24), + IRQ_SDCA(25), + IRQ_SDCA(26), + IRQ_SDCA(27), + IRQ_SDCA(28), + IRQ_SDCA(29), + IRQ_SDCA(30), +}; + +static const struct regmap_irq_chip sdca_irq_chip = { + .name = "sdca_irq", + + .status_base = SDW_SCP_SDCA_INT1, + .unmask_base = SDW_SCP_SDCA_INTMASK1, + .ack_base = SDW_SCP_SDCA_INT1, + .num_regs = 4, + + .irqs = regmap_irqs, + .num_irqs = SDCA_MAX_INTERRUPTS, + + .runtime_pm = true, +}; + +static irqreturn_t base_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct device *dev = interrupt->component->dev; + + dev_info(dev, "%s irq without full handling\n", interrupt->name); + + return IRQ_HANDLED; +} + +static irqreturn_t function_status_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct device *dev = interrupt->component->dev; + unsigned int reg, val; + unsigned long status; + unsigned int mask; + int ret; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + interrupt->control->sel, 0); + + ret = regmap_read(interrupt->component->regmap, reg, &val); + if (ret < 0) { + dev_err(dev, "failed to read function status: %d\n", ret); + return IRQ_NONE; + } + + dev_dbg(dev, "function status: %#x\n", val); + + status = val; + for_each_set_bit(mask, &status, BITS_PER_BYTE) { + mask = 1 << mask; + + switch (mask) { + case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION: + //FIXME: Add init writes + break; + case SDCA_CTL_ENTITY_0_FUNCTION_FAULT: + dev_err(dev, "function fault\n"); + break; + case SDCA_CTL_ENTITY_0_UMP_SEQUENCE_FAULT: + dev_err(dev, "ump sequence fault\n"); + break; + case SDCA_CTL_ENTITY_0_FUNCTION_BUSY: + dev_info(dev, "unexpected function busy\n"); + break; + case SDCA_CTL_ENTITY_0_DEVICE_NEWLY_ATTACHED: + case SDCA_CTL_ENTITY_0_INTS_DISABLED_ABNORMALLY: + case SDCA_CTL_ENTITY_0_STREAMING_STOPPED_ABNORMALLY: + case SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET: + break; + } + } + + ret = regmap_write(interrupt->component->regmap, reg, val); + if (ret < 0) { + dev_err(dev, "failed to clear function status: %d\n", ret); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t detected_mode_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct snd_soc_component *component = interrupt->component; + struct device *dev = component->dev; + struct snd_soc_card *card = component->card; + struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; + struct snd_kcontrol *kctl = interrupt->priv; + struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; + struct soc_enum *soc_enum; + unsigned int reg, val; + int ret; + + if (!kctl) { + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", + interrupt->entity->label, + SDCA_CTL_SELECTED_MODE_NAME); + + if (!name) + return -ENOMEM; + + kctl = snd_soc_component_get_kcontrol(component, name); + if (!kctl) { + dev_dbg(dev, "control not found: %s\n", name); + return IRQ_NONE; + } + + interrupt->priv = kctl; + } + + soc_enum = (struct soc_enum *)kctl->private_value; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + interrupt->control->sel, 0); + + ret = regmap_read(component->regmap, reg, &val); + if (ret < 0) { + dev_err(dev, "failed to read detected mode: %d\n", ret); + return IRQ_NONE; + } + + switch (val) { + case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS: + case SDCA_DETECTED_MODE_JACK_UNKNOWN: + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, + interrupt->entity->id, + SDCA_CTL_GE_SELECTED_MODE, 0); + + /* + * Selected mode is not normally marked as volatile register + * (RW), but here force a read from the hardware. If the + * detected mode is unknown we need to see what the device + * selected as a "safe" option. + */ + regcache_drop_region(component->regmap, reg, reg); + + ret = regmap_read(component->regmap, reg, &val); + if (ret) { + dev_err(dev, "failed to re-check selected mode: %d\n", ret); + return IRQ_NONE; + } + break; + default: + break; + } + + dev_dbg(dev, "%s: %#x\n", interrupt->name, val); + + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return IRQ_NONE; + + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + + down_write(rwsem); + ret = kctl->put(kctl, ucontrol); + up_write(rwsem); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return IRQ_NONE; + } + + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + + return IRQ_HANDLED; +} + +static int sdca_irq_request_locked(struct device *dev, + struct sdca_interrupt_info *info, + int sdca_irq, const char *name, + irq_handler_t handler, void *data) +{ + int irq; + int ret; + + irq = regmap_irq_get_virq(info->irq_data, sdca_irq); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, handler, + IRQF_ONESHOT, name, data); + if (ret) + return ret; + + dev_dbg(dev, "requested irq %d for %s\n", irq, name); + + return 0; +} + +/** + * sdca_request_irq - request an individual SDCA interrupt + * @dev: Pointer to the struct device against which things should be allocated. + * @interrupt_info: Pointer to the interrupt information structure. + * @sdca_irq: SDCA interrupt position. + * @name: Name to be given to the IRQ. + * @handler: A callback thread function to be called for the IRQ. + * @data: Private data pointer that will be passed to the handler. + * + * Typically this is handled internally by sdca_irq_populate, however if + * a device requires custom IRQ handling this can be called manually before + * calling sdca_irq_populate, which will then skip that IRQ whilst processing. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, + int sdca_irq, const char *name, irq_handler_t handler, + void *data) +{ + int ret; + + if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS) { + dev_err(dev, "bad irq request: %d\n", sdca_irq); + return -EINVAL; + } + + guard(mutex)(&info->irq_lock); + + ret = sdca_irq_request_locked(dev, info, sdca_irq, name, handler, data); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", name, ret); + return ret; + } + + info->irqs[sdca_irq].externally_requested = true; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA"); + +/** + * sdca_irq_data_populate - Populate common interrupt data + * @component: Pointer to the ASoC component for the Function. + * @function: Pointer to the SDCA Function. + * @entity: Pointer to the SDCA Entity. + * @control: Pointer to the SDCA Control. + * @interrupt: Pointer to the SDCA interrupt for this IRQ. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_data_populate(struct snd_soc_component *component, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct sdca_control *control, + struct sdca_interrupt *interrupt) +{ + struct device *dev = component->dev; + const char *name; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name, + entity->label, control->label); + if (!name) + return -ENOMEM; + + interrupt->name = name; + interrupt->component = component; + interrupt->function = function; + interrupt->entity = entity; + interrupt->control = control; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA"); + +/** + * sdca_irq_populate - Request all the individual IRQs for an SDCA Function + * @function: Pointer to the SDCA Function. + * @component: Pointer to the ASoC component for the Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * Typically this would be called from the driver for a single SDCA Function. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_populate(struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info) +{ + struct device *dev = component->dev; + int i, j; + + guard(mutex)(&info->irq_lock); + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int irq = control->interrupt_position; + struct sdca_interrupt *interrupt; + irq_handler_t handler; + int ret; + + if (irq == SDCA_NO_INTERRUPT) { + continue; + } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) { + dev_err(dev, "bad irq position: %d\n", irq); + return -EINVAL; + } + + interrupt = &info->irqs[irq]; + + if (interrupt->externally_requested) { + dev_dbg(dev, + "skipping irq %d, externally requested\n", + irq); + continue; + } + + ret = sdca_irq_data_populate(component, function, entity, + control, interrupt); + if (ret) + return ret; + + handler = base_handler; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_ENTITY_0: + if (control->sel == SDCA_CTL_ENTITY_0_FUNCTION_STATUS) + handler = function_status_handler; + break; + case SDCA_ENTITY_TYPE_GE: + if (control->sel == SDCA_CTL_GE_DETECTED_MODE) + handler = detected_mode_handler; + break; + default: + break; + } + + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, + handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + interrupt->name, ret); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); + +/** + * sdca_irq_allocate - allocate an SDCA interrupt structure for a device + * @dev: Device pointer against which things should be allocated. + * @regmap: regmap to be used for accessing the SDCA IRQ registers. + * @irq: The interrupt number. + * + * Typically this would be called from the top level driver for the whole + * SDCA device, as only a single instance is required across all Functions + * on the device. + * + * Return: A pointer to the allocated sdca_interrupt_info struct, or an + * error code. + */ +struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, + struct regmap *regmap, int irq) +{ + struct sdca_interrupt_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + info->irq_chip = sdca_irq_chip; + + ret = devm_mutex_init(dev, &info->irq_lock); + if (ret) + return ERR_PTR(ret); + + ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, + &info->irq_chip, &info->irq_data); + if (ret) { + dev_err(dev, "failed to register irq chip: %d\n", ret); + return ERR_PTR(ret); + } + + dev_dbg(dev, "registered on irq %d\n", irq); + + return info; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SDCA IRQ library"); diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c index 66e7eee7d7f4..5cb3048ea8cf 100644 --- a/sound/soc/sdca/sdca_regmap.c +++ b/sound/soc/sdca/sdca_regmap.c @@ -72,12 +72,18 @@ bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg) if (!control) return false; + if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list)) + return false; + switch (control->mode) { case SDCA_ACCESS_MODE_RW: case SDCA_ACCESS_MODE_RO: - case SDCA_ACCESS_MODE_DUAL: case SDCA_ACCESS_MODE_RW1S: case SDCA_ACCESS_MODE_RW1C: + if (SDW_SDCA_NEXT_CTL(0) & reg) + return false; + fallthrough; + case SDCA_ACCESS_MODE_DUAL: /* No access to registers marked solely for device use */ return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; default: @@ -104,11 +110,17 @@ bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg if (!control) return false; + if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list)) + return false; + switch (control->mode) { case SDCA_ACCESS_MODE_RW: - case SDCA_ACCESS_MODE_DUAL: case SDCA_ACCESS_MODE_RW1S: case SDCA_ACCESS_MODE_RW1C: + if (SDW_SDCA_NEXT_CTL(0) & reg) + return false; + fallthrough; + case SDCA_ACCESS_MODE_DUAL: /* No access to registers marked solely for device use */ return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; default: @@ -241,7 +253,7 @@ int sdca_regmap_populate_constants(struct device *dev, struct sdca_function_data *function, struct reg_default *consts) { - int i, j, k; + int i, j, k, l; for (i = 0, k = 0; i < function->num_entities; i++) { struct sdca_entity *entity = &function->entities[i]; @@ -253,13 +265,15 @@ int sdca_regmap_populate_constants(struct device *dev, if (control->mode != SDCA_ACCESS_MODE_DC) continue; + l = 0; for_each_set_bit(cn, (unsigned long *)&control->cn_list, BITS_PER_TYPE(control->cn_list)) { consts[k].reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn); - consts[k].def = control->value; + consts[k].def = control->values[l]; k++; + l++; } } } @@ -283,7 +297,7 @@ EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA"); int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, struct sdca_function_data *function) { - int i, j; + int i, j, k; int ret; for (i = 0; i < function->num_entities; i++) { @@ -299,6 +313,7 @@ int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, if (!control->has_default && !control->has_fixed) continue; + k = 0; for_each_set_bit(cn, (unsigned long *)&control->cn_list, BITS_PER_TYPE(control->cn_list)) { unsigned int reg; @@ -306,9 +321,11 @@ int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn); - ret = regmap_write(regmap, reg, control->value); + ret = regmap_write(regmap, reg, control->values[k]); if (ret) return ret; + + k++; } } } diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c index 35b550bcd4de..520ea36c63ee 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c @@ -14,7 +14,6 @@ #include #include -#define CODEC_NAME_SIZE 8 #define CS_AMP_CHANNELS_PER_AMP 4 #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ @@ -38,21 +37,12 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs35l56_volume_limit, "SND_SOC_SDW_UTILS"); int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { - const char *dai_name = rtd->dai_link->codecs->dai_name; struct snd_soc_card *card = rtd->card; - char codec_name[CODEC_NAME_SIZE]; char widget_name[16]; struct snd_soc_dapm_route route = { "Speaker", NULL, widget_name }; struct snd_soc_dai *codec_dai; int i, ret; - snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - for_each_rtd_codec_dais(rtd, i, codec_dai) { if (!strstr(codec_dai->name, "cs35l56")) continue; diff --git a/sound/soc/sdw_utils/soc_sdw_maxim.c b/sound/soc/sdw_utils/soc_sdw_maxim.c index 5df8d9cae60c..8f9d1ed0725b 100644 --- a/sound/soc/sdw_utils/soc_sdw_maxim.c +++ b/sound/soc/sdw_utils/soc_sdw_maxim.c @@ -28,15 +28,6 @@ int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_ struct snd_soc_card *card = rtd->card; int ret; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:mx%04x", - card->components, maxim_part_id); - if (!card->components) - return -ENOMEM; - - dev_dbg(card->dev, "soundwire maxim card components assigned : %s\n", - card->components); - ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, 2); if (ret) dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); diff --git a/sound/soc/sdw_utils/soc_sdw_rt_amp.c b/sound/soc/sdw_utils/soc_sdw_rt_amp.c index 83c2368170cb..76ee24b8eee4 100644 --- a/sound/soc/sdw_utils/soc_sdw_rt_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_rt_amp.c @@ -195,12 +195,6 @@ int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc rt_amp_map = get_codec_name_and_route(dai, codec_name); - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - for_each_rtd_codec_dais(rtd, i, codec_dai) { if (strstr(codec_dai->component->name_prefix, "-1")) ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); diff --git a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c index 0161b14297d5..224b58de9084 100644 --- a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c +++ b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c @@ -73,13 +73,6 @@ int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd return -EINVAL; } - /* Update card components */ - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - /* Add routes */ ret = snd_soc_dapm_add_routes(&card->dapm, route_map->route_map, route_map->route_size); if (ret) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index b7060b746356..1580331cd34c 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -135,6 +135,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt712-sdca-aif2", + .component_name = "rt712", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -217,6 +218,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt1308-aif", + .component_name = "rt1308", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -237,6 +239,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "rt1316-aif", + .component_name = "rt1316", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -256,6 +259,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "rt1318-aif", + .component_name = "rt1318", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -275,6 +279,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt1320-aif1", + .component_name = "rt1320", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -368,6 +373,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt721-sdca-aif2", + .component_name = "rt721", .dai_type = SOC_SDW_DAI_TYPE_AMP, /* No feedback capability is provided by rt721-sdca codec driver*/ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -409,6 +415,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt722-sdca-aif2", + .component_name = "rt722", .dai_type = SOC_SDW_DAI_TYPE_AMP, /* No feedback capability is provided by rt722-sdca codec driver*/ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -438,6 +445,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "max98373-aif1", + .component_name = "mx8373", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_maxim_init, @@ -456,6 +464,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "max98363-aif1", + .component_name = "mx8363", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_maxim_init, @@ -491,6 +500,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs_amp_init, @@ -516,6 +526,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs_amp_init, @@ -589,6 +600,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs42l43-dp6", + .component_name = "cs42l43", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs42l43_spk_init, @@ -733,6 +745,7 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; struct asoc_sdw_codec_info *codec_info; struct snd_soc_dai *dai; + const char *spk_components=""; int dai_index; int ret; int i; @@ -785,7 +798,32 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; } + + /* 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) { + if (strlen (spk_components) == 0) + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, "%s", + codec_info->dais[dai_index].component_name); + 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); + } + codec_info->dais[dai_index].rtd_init_done = true; + + } + + if (strlen (spk_components) > 0) { + /* Update card components for speaker components */ + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s", + card->components, spk_components); + if (!card->components) + return -ENOMEM; } return 0; @@ -1205,6 +1243,8 @@ static int is_sdca_endpoint_present(struct device *dev, int i; dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); + if (!dlc) + return -ENOMEM; adr_end = &adr_dev->endpoints[end_index]; dai_info = &codec_info->dais[adr_end->num]; diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 29790807d785..37486d6a438e 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -125,7 +125,7 @@ static const struct gpio_chip snd_soc_ac97_gpio_chip = { .direction_input = snd_soc_ac97_gpio_direction_in, .get = snd_soc_ac97_gpio_get, .direction_output = snd_soc_ac97_gpio_direction_out, - .set_rv = snd_soc_ac97_gpio_set, + .set = snd_soc_ac97_gpio_set, .can_sleep = 1, }; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 25f5e543ae8d..65c495094024 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1278,7 +1278,6 @@ void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, if (rollback && !soc_component_mark_match(component, stream, pm)) continue; - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); /* remove marked stream */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 67bebc339148..cc9125ffe92a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -112,7 +112,7 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj, } static const struct attribute_group soc_dapm_dev_group = { - .attrs = soc_dapm_dev_attrs, + .attrs = snd_soc_dapm_dev_attrs, .is_visible = soc_dev_attr_is_visible, }; @@ -369,20 +369,25 @@ struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name) { struct snd_soc_component *component; - struct snd_soc_component *found_component; - found_component = NULL; for_each_component(component) { - if ((dev == component->dev) && - (!driver_name || - (driver_name == component->driver->name) || - (strcmp(component->driver->name, driver_name) == 0))) { - found_component = component; - break; - } + if (dev != component->dev) + continue; + + if (!driver_name) + return component; + + if (!component->driver->name) + continue; + + if (component->driver->name == driver_name) + return component; + + if (strcmp(component->driver->name, driver_name) == 0) + return component; } - return found_component; + return NULL; } EXPORT_SYMBOL_GPL(snd_soc_lookup_component_nolocked); @@ -681,7 +686,7 @@ int snd_soc_suspend(struct device *dev) soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_SUSPEND); /* Recheck all endpoints too, their state is affected by suspend */ - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* suspend all COMPONENTs */ @@ -778,7 +783,7 @@ static void soc_resume_deferred(struct work_struct *work) dev_dbg(card->dev, "ASoC: resume work completed\n"); /* Recheck all endpoints too, their state is affected by suspend */ - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* userspace can access us now we are back as we were before */ @@ -1139,6 +1144,9 @@ static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card, void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd) { + if (!rtd) + return; + lockdep_assert_held(&client_mutex); /* @@ -2286,7 +2294,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) } card->instantiated = 1; - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* deactivate pins to sleep state */ @@ -2604,6 +2612,7 @@ static char *fmt_single_name(struct device *dev, int *id) const char *devname = dev_name(dev); char *found, *name; unsigned int id1, id2; + int __id; if (devname == NULL) return NULL; @@ -2616,10 +2625,10 @@ static char *fmt_single_name(struct device *dev, int *id) found = strstr(name, dev->driver->name); if (found) { /* get ID */ - if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { + if (sscanf(&found[strlen(dev->driver->name)], ".%d", &__id) == 1) { /* discard ID from name if ID == -1 */ - if (*id == -1) + if (__id == -1) found[strlen(dev->driver->name)] = '\0'; } @@ -2627,16 +2636,19 @@ static char *fmt_single_name(struct device *dev, int *id) } else if (sscanf(name, "%x-%x", &id1, &id2) == 2) { /* create unique ID number from I2C addr and bus */ - *id = ((id1 & 0xffff) << 16) + id2; + __id = ((id1 & 0xffff) << 16) + id2; devm_kfree(dev, name); /* sanitize component name for DAI link creation */ name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname); } else { - *id = 0; + __id = 0; } + if (id) + *id = __id; + return name; } @@ -2831,7 +2843,7 @@ int snd_soc_component_initialize(struct snd_soc_component *component, mutex_init(&component->io_mutex); if (!component->name) { - component->name = fmt_single_name(dev, &component->id); + component->name = fmt_single_name(dev, NULL); if (!component->name) { dev_err(dev, "ASoC: Failed to allocate name\n"); return -ENOMEM; diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index a210089747d0..32f46a38682b 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -259,13 +259,15 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, &rx_mask, }; - if (dai->driver->ops && - dai->driver->ops->xlate_tdm_slot_mask) - ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - else - ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - if (ret) - goto err; + if (slots) { + if (dai->driver->ops && + dai->driver->ops->xlate_tdm_slot_mask) + ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + else + ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + if (ret) + goto err; + } for_each_pcm_streams(stream) snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f26f9e9d7ce7..a37d44cd04c6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -49,22 +49,6 @@ for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ (dir)++) -static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, - const char *control, - int (*connected)(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink)); - -struct snd_soc_dapm_widget * -snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); - -struct snd_soc_dapm_widget * -snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); - -static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg); - /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 1, @@ -222,7 +206,7 @@ static __always_inline void dapm_widget_invalidate_paths( list_for_each_entry(w, &list, work_list) { snd_soc_dapm_widget_for_each_path(w, dir, p) { - if (p->is_supply || p->weak || !p->connect) + if (p->is_supply || !p->connect) continue; node = p->node[rdir]; if (node->endpoints[dir] != -1) { @@ -284,7 +268,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) * Weak paths or supply paths do not influence the number of input or * output paths of their neighbors. */ - if (p->weak || p->is_supply) + if (p->is_supply) return; /* @@ -299,7 +283,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) dapm_widget_invalidate_output_paths(p->source); } -void dapm_mark_endpoints_dirty(struct snd_soc_card *card) +void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -354,6 +338,320 @@ struct dapm_kcontrol_data { struct snd_soc_dapm_widget_list *wlist; }; +static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) +{ + if (!dapm->component) + return -EIO; + return snd_soc_component_read(dapm->component, reg); +} + +/* set up initial codec paths */ +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, + int nth_path) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *) + p->sink->kcontrol_news[i].private_value; + unsigned int reg = mc->reg; + unsigned int invert = mc->invert; + + if (reg != SND_SOC_NOPM) { + unsigned int shift = mc->shift; + unsigned int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int val = soc_dapm_read(p->sink->dapm, reg); + + /* + * The nth_path argument allows this function to know + * which path of a kcontrol it is setting the initial + * status for. Ideally this would support any number + * of paths and channels. But since kcontrols only come + * in mono and stereo variants, we are limited to 2 + * channels. + * + * The following code assumes for stereo controls the + * first path is the left channel, and all remaining + * paths are the right channel. + */ + if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { + if (reg != mc->rreg) + val = soc_dapm_read(p->sink->dapm, mc->rreg); + val = (val >> mc->rshift) & mask; + } else { + val = (val >> shift) & mask; + } + if (invert) + val = max - val; + p->connect = !!val; + } else { + /* since a virtual mixer has no backing registers to + * decide which path to connect, it will try to match + * with initial state. This is to ensure + * that the default mixer choice will be + * correctly powered up during initialization. + */ + p->connect = invert; + } +} + +/* connect mux widget to its interconnecting audio paths */ +static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_path *path, const char *control_name, + struct snd_soc_dapm_widget *w) +{ + const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int item; + int i; + + if (e->reg != SND_SOC_NOPM) { + unsigned int val; + + val = soc_dapm_read(dapm, e->reg); + val = (val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + } else { + /* since a virtual mux has no backing registers to + * decide which path to connect, it will try to match + * with the first enumeration. This is to ensure + * that the default mux choice (the first) will be + * correctly powered up during initialization. + */ + item = 0; + } + + i = match_string(e->texts, e->items, control_name); + if (i < 0) + return -ENODEV; + + path->name = e->texts[i]; + path->connect = (i == item); + return 0; + +} + +/* connect mixer widget to its interconnecting audio paths */ +static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_path *path, const char *control_name) +{ + int i, nth_path = 0; + + /* search for mixer kcontrol */ + for (i = 0; i < path->sink->num_kcontrols; i++) { + if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { + path->name = path->sink->kcontrol_news[i].name; + dapm_set_mixer_path_status(path, i, nth_path++); + return 0; + } + } + return -ENODEV; +} + +/* + * dapm_update_widget_flags() - Re-compute widget sink and source flags + * @w: The widget for which to update the flags + * + * Some widgets have a dynamic category which depends on which neighbors they + * are connected to. This function update the category for these widgets. + * + * This function must be called whenever a path is added or removed to a widget. + */ +static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) +{ + enum snd_soc_dapm_direction dir; + struct snd_soc_dapm_path *p; + unsigned int ep; + + switch (w->id) { + case snd_soc_dapm_input: + /* On a fully routed card an input is never a source */ + if (w->dapm->card->fully_routed) + return; + ep = SND_SOC_DAPM_EP_SOURCE; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->source->id == snd_soc_dapm_micbias || + p->source->id == snd_soc_dapm_mic || + p->source->id == snd_soc_dapm_line || + p->source->id == snd_soc_dapm_output) { + ep = 0; + break; + } + } + break; + case snd_soc_dapm_output: + /* On a fully routed card a output is never a sink */ + if (w->dapm->card->fully_routed) + return; + ep = SND_SOC_DAPM_EP_SINK; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->sink->id == snd_soc_dapm_spk || + p->sink->id == snd_soc_dapm_hp || + p->sink->id == snd_soc_dapm_line || + p->sink->id == snd_soc_dapm_input) { + ep = 0; + break; + } + } + break; + case snd_soc_dapm_line: + ep = 0; + snd_soc_dapm_for_each_direction(dir) { + if (!list_empty(&w->edges[dir])) + ep |= SND_SOC_DAPM_DIR_TO_EP(dir); + } + break; + default: + return; + } + + w->is_ep = ep; +} + +static int snd_soc_dapm_check_dynamic_path( + struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, + const char *control) +{ + bool dynamic_source = false; + bool dynamic_sink = false; + + if (!control) + return 0; + + switch (source->id) { + case snd_soc_dapm_demux: + dynamic_source = true; + break; + default: + break; + } + + switch (sink->id) { + case snd_soc_dapm_mux: + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + dynamic_sink = true; + break; + default: + break; + } + + if (dynamic_source && dynamic_sink) { + dev_err(dapm->dev, + "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } else if (!dynamic_source && !dynamic_sink) { + dev_err(dapm->dev, + "Control not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } + + return 0; +} + +static int snd_soc_dapm_add_path( + struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)) +{ + enum snd_soc_dapm_direction dir; + struct snd_soc_dapm_path *path; + int ret; + + if (wsink->is_supply && !wsource->is_supply) { + dev_err(dapm->dev, + "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (connected && !wsource->is_supply) { + dev_err(dapm->dev, + "connected() callback only supported for supply widgets (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (wsource->is_supply && control) { + dev_err(dapm->dev, + "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", + wsource->name, control, wsink->name); + return -EINVAL; + } + + ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); + if (ret) + return ret; + + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); + if (!path) + return -ENOMEM; + + path->node[SND_SOC_DAPM_DIR_IN] = wsource; + path->node[SND_SOC_DAPM_DIR_OUT] = wsink; + + path->connected = connected; + INIT_LIST_HEAD(&path->list); + INIT_LIST_HEAD(&path->list_kcontrol); + + if (wsource->is_supply || wsink->is_supply) + path->is_supply = 1; + + /* connect static paths */ + if (control == NULL) { + path->connect = 1; + } else { + switch (wsource->id) { + case snd_soc_dapm_demux: + ret = dapm_connect_mux(dapm, path, control, wsource); + if (ret) + goto err; + break; + default: + break; + } + + switch (wsink->id) { + case snd_soc_dapm_mux: + ret = dapm_connect_mux(dapm, path, control, wsink); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + ret = dapm_connect_mixer(dapm, path, control); + if (ret != 0) + goto err; + break; + default: + break; + } + } + + list_add(&path->list, &dapm->card->paths); + + snd_soc_dapm_for_each_direction(dir) + list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); + + snd_soc_dapm_for_each_direction(dir) { + dapm_update_widget_flags(path->node[dir]); + dapm_mark_dirty(path->node[dir], "Route added"); + } + + if (snd_soc_card_is_instantiated(dapm->card) && path->connect) + dapm_path_invalidate(path); + + return 0; +err: + kfree(path); + return ret; +} + static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, const char *ctrl_name) { @@ -626,13 +924,6 @@ static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) return dapm->component->name_prefix; } -static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) -{ - if (!dapm->component) - return -EIO; - return snd_soc_component_read(dapm->component, reg); -} - static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, int reg, unsigned int mask, unsigned int value) { @@ -739,109 +1030,13 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, out: trace_snd_soc_bias_level_done(dapm, level); + /* success */ + if (ret == 0) + snd_soc_dapm_init_bias_level(dapm, level); + return ret; } -/* connect mux widget to its interconnecting audio paths */ -static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_path *path, const char *control_name, - struct snd_soc_dapm_widget *w) -{ - const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int item; - int i; - - if (e->reg != SND_SOC_NOPM) { - unsigned int val; - val = soc_dapm_read(dapm, e->reg); - val = (val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - } else { - /* since a virtual mux has no backing registers to - * decide which path to connect, it will try to match - * with the first enumeration. This is to ensure - * that the default mux choice (the first) will be - * correctly powered up during initialization. - */ - item = 0; - } - - i = match_string(e->texts, e->items, control_name); - if (i < 0) - return -ENODEV; - - path->name = e->texts[i]; - path->connect = (i == item); - return 0; - -} - -/* set up initial codec paths */ -static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, - int nth_path) -{ - struct soc_mixer_control *mc = (struct soc_mixer_control *) - p->sink->kcontrol_news[i].private_value; - unsigned int reg = mc->reg; - unsigned int invert = mc->invert; - - if (reg != SND_SOC_NOPM) { - unsigned int shift = mc->shift; - unsigned int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int val = soc_dapm_read(p->sink->dapm, reg); - - /* - * The nth_path argument allows this function to know - * which path of a kcontrol it is setting the initial - * status for. Ideally this would support any number - * of paths and channels. But since kcontrols only come - * in mono and stereo variants, we are limited to 2 - * channels. - * - * The following code assumes for stereo controls the - * first path is the left channel, and all remaining - * paths are the right channel. - */ - if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { - if (reg != mc->rreg) - val = soc_dapm_read(p->sink->dapm, mc->rreg); - val = (val >> mc->rshift) & mask; - } else { - val = (val >> shift) & mask; - } - if (invert) - val = max - val; - p->connect = !!val; - } else { - /* since a virtual mixer has no backing registers to - * decide which path to connect, it will try to match - * with initial state. This is to ensure - * that the default mixer choice will be - * correctly powered up during initialization. - */ - p->connect = invert; - } -} - -/* connect mixer widget to its interconnecting audio paths */ -static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_path *path, const char *control_name) -{ - int i, nth_path = 0; - - /* search for mixer kcontrol */ - for (i = 0; i < path->sink->num_kcontrols; i++) { - if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { - path->name = path->sink->kcontrol_news[i].name; - dapm_set_mixer_path_status(path, i, nth_path++); - return 0; - } - } - return -ENODEV; -} - static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *kcontrolw, const struct snd_kcontrol_new *kcontrol_new, @@ -1170,7 +1365,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, widget->endpoints[dir] = -1; snd_soc_dapm_widget_for_each_path(widget, rdir, path) { - if (path->weak || path->is_supply) + if (path->is_supply) continue; if (path->walking) @@ -1225,7 +1420,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, snd_soc_dapm_widget_for_each_path(widget, rdir, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak || path->is_supply) + if (path->is_supply) continue; if (path->walking) @@ -1346,8 +1541,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets); /* * Handler for regulator supply widget. */ -int dapm_regulator_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { int ret; @@ -1375,13 +1570,13 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_disable_deferred(w->regulator, w->shift); } } -EXPORT_SYMBOL_GPL(dapm_regulator_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_regulator_event); /* * Handler for pinctrl widget. */ -int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_dapm_pinctrl_priv *priv = w->priv; struct pinctrl *p = w->pinctrl; @@ -1400,13 +1595,13 @@ int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, return pinctrl_select_state(p, s); } -EXPORT_SYMBOL_GPL(dapm_pinctrl_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_pinctrl_event); /* * Handler for clock supply widget. */ -int dapm_clock_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { if (!w->clk) return -EIO; @@ -1422,7 +1617,7 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w, return 0; } -EXPORT_SYMBOL_GPL(dapm_clock_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_clock_event); static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) { @@ -1462,9 +1657,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) snd_soc_dapm_widget_for_each_sink_path(w, path) { DAPM_UPDATE_STAT(w, neighbour_checks); - if (path->weak) - continue; - if (path->connected && !path->connected(path->source, path->sink)) continue; @@ -1797,29 +1989,29 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up */ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) { - struct snd_soc_dapm_context *d = data; + struct snd_soc_dapm_context *dapm = data; int ret; /* If we're off and we're not supposed to go into STANDBY */ - if (d->bias_level == SND_SOC_BIAS_OFF && - d->target_bias_level != SND_SOC_BIAS_OFF) { - if (d->dev && cookie) - pm_runtime_get_sync(d->dev); + if (dapm->bias_level == SND_SOC_BIAS_OFF && + dapm->target_bias_level != SND_SOC_BIAS_OFF) { + if (dapm->dev && cookie) + pm_runtime_get_sync(dapm->dev); - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); if (ret != 0) - dev_err(d->dev, + dev_err(dapm->dev, "ASoC: Failed to turn on bias: %d\n", ret); } /* Prepare for a transition to ON or away from ON */ - if ((d->target_bias_level == SND_SOC_BIAS_ON && - d->bias_level != SND_SOC_BIAS_ON) || - (d->target_bias_level != SND_SOC_BIAS_ON && - d->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); + if ((dapm->target_bias_level == SND_SOC_BIAS_ON && + dapm->bias_level != SND_SOC_BIAS_ON) || + (dapm->target_bias_level != SND_SOC_BIAS_ON && + dapm->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); if (ret != 0) - dev_err(d->dev, + dev_err(dapm->dev, "ASoC: Failed to prepare bias: %d\n", ret); } } @@ -1829,37 +2021,37 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) */ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) { - struct snd_soc_dapm_context *d = data; + struct snd_soc_dapm_context *dapm = data; int ret; /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && - (d->target_bias_level == SND_SOC_BIAS_STANDBY || - d->target_bias_level == SND_SOC_BIAS_OFF)) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (dapm->bias_level == SND_SOC_BIAS_PREPARE && + (dapm->target_bias_level == SND_SOC_BIAS_STANDBY || + dapm->target_bias_level == SND_SOC_BIAS_OFF)) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to apply standby bias: %d\n", ret); } /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && - d->target_bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); + if (dapm->bias_level == SND_SOC_BIAS_STANDBY && + dapm->target_bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to turn off bias: %d\n", ret); - if (d->dev && cookie) - pm_runtime_put(d->dev); + if (dapm->dev && cookie) + pm_runtime_put(dapm->dev); } /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && - d->target_bias_level == SND_SOC_BIAS_ON) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); + if (dapm->bias_level == SND_SOC_BIAS_PREPARE && + dapm->target_bias_level == SND_SOC_BIAS_ON) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_ON); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to apply active bias: %d\n", ret); } } @@ -2440,10 +2632,10 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); -static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, +static ssize_t dapm_widget_show_component(struct snd_soc_component *component, char *buf, int count) { - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dapm_widget *w; char *state = "not set"; @@ -2451,10 +2643,10 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, * we're checking for that case specifically here but in future * we will ensure that the dummy component looks like others. */ - if (!cmpnt->card) + if (!component->card) return 0; - for_each_card_widgets(cmpnt->card, w) { + for_each_card_widgets(component->card, w) { if (w->dapm != dapm) continue; @@ -2515,9 +2707,9 @@ static ssize_t dapm_widget_show(struct device *dev, snd_soc_dapm_mutex_lock_root(rtd->card); for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_soc_component *cmpnt = codec_dai->component; + struct snd_soc_component *component = codec_dai->component; - count = dapm_widget_show_component(cmpnt, buf, count); + count = dapm_widget_show_component(component, buf, count); } snd_soc_dapm_mutex_unlock(rtd->card); @@ -2527,7 +2719,7 @@ static ssize_t dapm_widget_show(struct device *dev, static DEVICE_ATTR_RO(dapm_widget); -struct attribute *soc_dapm_dev_attrs[] = { +struct attribute *snd_soc_dapm_dev_attrs[] = { &dev_attr_dapm_widget.attr, NULL }; @@ -2794,210 +2986,6 @@ int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char } EXPORT_SYMBOL_GPL(snd_soc_dapm_widget_name_cmp); -/* - * dapm_update_widget_flags() - Re-compute widget sink and source flags - * @w: The widget for which to update the flags - * - * Some widgets have a dynamic category which depends on which neighbors they - * are connected to. This function update the category for these widgets. - * - * This function must be called whenever a path is added or removed to a widget. - */ -static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) -{ - enum snd_soc_dapm_direction dir; - struct snd_soc_dapm_path *p; - unsigned int ep; - - switch (w->id) { - case snd_soc_dapm_input: - /* On a fully routed card an input is never a source */ - if (w->dapm->card->fully_routed) - return; - ep = SND_SOC_DAPM_EP_SOURCE; - snd_soc_dapm_widget_for_each_source_path(w, p) { - if (p->source->id == snd_soc_dapm_micbias || - p->source->id == snd_soc_dapm_mic || - p->source->id == snd_soc_dapm_line || - p->source->id == snd_soc_dapm_output) { - ep = 0; - break; - } - } - break; - case snd_soc_dapm_output: - /* On a fully routed card a output is never a sink */ - if (w->dapm->card->fully_routed) - return; - ep = SND_SOC_DAPM_EP_SINK; - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if (p->sink->id == snd_soc_dapm_spk || - p->sink->id == snd_soc_dapm_hp || - p->sink->id == snd_soc_dapm_line || - p->sink->id == snd_soc_dapm_input) { - ep = 0; - break; - } - } - break; - case snd_soc_dapm_line: - ep = 0; - snd_soc_dapm_for_each_direction(dir) { - if (!list_empty(&w->edges[dir])) - ep |= SND_SOC_DAPM_DIR_TO_EP(dir); - } - break; - default: - return; - } - - w->is_ep = ep; -} - -static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, - const char *control) -{ - bool dynamic_source = false; - bool dynamic_sink = false; - - if (!control) - return 0; - - switch (source->id) { - case snd_soc_dapm_demux: - dynamic_source = true; - break; - default: - break; - } - - switch (sink->id) { - case snd_soc_dapm_mux: - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - dynamic_sink = true; - break; - default: - break; - } - - if (dynamic_source && dynamic_sink) { - dev_err(dapm->dev, - "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", - source->name, control, sink->name); - return -EINVAL; - } else if (!dynamic_source && !dynamic_sink) { - dev_err(dapm->dev, - "Control not supported for path %s -> [%s] -> %s\n", - source->name, control, sink->name); - return -EINVAL; - } - - return 0; -} - -static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, - const char *control, - int (*connected)(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink)) -{ - enum snd_soc_dapm_direction dir; - struct snd_soc_dapm_path *path; - int ret; - - if (wsink->is_supply && !wsource->is_supply) { - dev_err(dapm->dev, - "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", - wsource->name, wsink->name); - return -EINVAL; - } - - if (connected && !wsource->is_supply) { - dev_err(dapm->dev, - "connected() callback only supported for supply widgets (%s -> %s)\n", - wsource->name, wsink->name); - return -EINVAL; - } - - if (wsource->is_supply && control) { - dev_err(dapm->dev, - "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", - wsource->name, control, wsink->name); - return -EINVAL; - } - - ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); - if (ret) - return ret; - - path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); - if (!path) - return -ENOMEM; - - path->node[SND_SOC_DAPM_DIR_IN] = wsource; - path->node[SND_SOC_DAPM_DIR_OUT] = wsink; - - path->connected = connected; - INIT_LIST_HEAD(&path->list); - INIT_LIST_HEAD(&path->list_kcontrol); - - if (wsource->is_supply || wsink->is_supply) - path->is_supply = 1; - - /* connect static paths */ - if (control == NULL) { - path->connect = 1; - } else { - switch (wsource->id) { - case snd_soc_dapm_demux: - ret = dapm_connect_mux(dapm, path, control, wsource); - if (ret) - goto err; - break; - default: - break; - } - - switch (wsink->id) { - case snd_soc_dapm_mux: - ret = dapm_connect_mux(dapm, path, control, wsink); - if (ret != 0) - goto err; - break; - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - ret = dapm_connect_mixer(dapm, path, control); - if (ret != 0) - goto err; - break; - default: - break; - } - } - - list_add(&path->list, &dapm->card->paths); - - snd_soc_dapm_for_each_direction(dir) - list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); - - snd_soc_dapm_for_each_direction(dir) { - dapm_update_widget_flags(path->node[dir]); - dapm_mark_dirty(path->node[dir], "Route added"); - } - - if (snd_soc_card_is_instantiated(dapm->card) && path->connect) - dapm_path_invalidate(path); - - return 0; -err: - kfree(path); - return ret; -} - static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { @@ -3210,86 +3198,6 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); -static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) -{ - struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, - route->source, - true); - struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, - route->sink, - true); - struct snd_soc_dapm_path *path; - int count = 0; - - if (!source) { - dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n", - route->source); - return -ENODEV; - } - - if (!sink) { - dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n", - route->sink); - return -ENODEV; - } - - if (route->control || route->connected) - dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", - route->source, route->sink); - - snd_soc_dapm_widget_for_each_sink_path(source, path) { - if (path->sink == sink) { - path->weak = 1; - count++; - } - } - - if (count == 0) - dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n", - route->source, route->sink); - if (count > 1) - dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n", - count, route->source, route->sink); - - return 0; -} - -/** - * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak - * @dapm: DAPM context - * @route: audio routes - * @num: number of routes - * - * Mark existing routes matching those specified in the passed array - * as being weak, meaning that they are ignored for the purpose of - * power decisions. The main intended use case is for sidetone paths - * which couple audio between other independent paths if they are both - * active in order to make the combination work better at the user - * level but which aren't intended to be "used". - * - * Note that CODEC drivers should not use this as sidetone type paths - * can frequently also be used as bypass paths. - */ -int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route, int num) -{ - int i; - int ret = 0; - - snd_soc_dapm_mutex_lock_root(dapm); - for (i = 0; i < num; i++) { - int err = snd_soc_dapm_weak_route(dapm, route); - if (err) - ret = err; - route++; - } - snd_soc_dapm_mutex_unlock(dapm); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); - /** * snd_soc_dapm_new_widgets - add new dapm widgets * @card: card to be checked for new dapm widgets @@ -3764,7 +3672,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, } /* set to sleep_state when initializing */ - dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); + snd_soc_dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); break; case snd_soc_dapm_clock_supply: w->clk = devm_clk_get(dapm->dev, widget->name); @@ -4780,57 +4688,6 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); -/** - * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. - * @dapm: DAPM context - * @pin: pin name - * - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - * - * Requires external locking. - * - * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to - * do any widget power switching. - */ -int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin) -{ - return snd_soc_dapm_set_pin(dapm, pin, 0); -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); - -/** - * snd_soc_dapm_nc_pin - permanently disable pin. - * @dapm: DAPM context - * @pin: pin name - * - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - * - * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to - * do any widget power switching. - */ -int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) -{ - int ret; - - snd_soc_dapm_mutex_lock(dapm); - - ret = snd_soc_dapm_set_pin(dapm, pin, 0); - - snd_soc_dapm_mutex_unlock(dapm); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); - /** * snd_soc_dapm_get_pin_status - get audio pin status * @dapm: DAPM context @@ -4891,7 +4748,6 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) dapm_free_widgets(dapm); list_del(&dapm->list); } -EXPORT_SYMBOL_GPL(snd_soc_dapm_free); void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card, diff --git a/sound/soc/soc-ops-test.c b/sound/soc/soc-ops-test.c index dc1e482bba6a..1bafa5626d48 100644 --- a/sound/soc/soc-ops-test.c +++ b/sound/soc/soc-ops-test.c @@ -481,22 +481,27 @@ static void soc_ops_test_access(struct kunit *test) .private_data = &priv->component, .private_value = (unsigned long)¶m->mc, }; - struct snd_ctl_elem_value result; unsigned int val; int ret; + /* it is too large struct. use kzalloc() */ + struct snd_ctl_elem_value *result; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return; ret = regmap_write(priv->component.regmap, 0x0, param->init); KUNIT_ASSERT_FALSE(test, ret); ret = regmap_write(priv->component.regmap, 0x1, param->init); KUNIT_ASSERT_FALSE(test, ret); - result.value.integer.value[0] = param->lctl; - result.value.integer.value[1] = param->rctl; + result->value.integer.value[0] = param->lctl; + result->value.integer.value[1] = param->rctl; - ret = param->put(&kctl, &result); + ret = param->put(&kctl, result); KUNIT_ASSERT_EQ(test, ret, param->ret); if (ret < 0) - return; + goto end; ret = regmap_read(priv->component.regmap, 0x0, &val); KUNIT_ASSERT_FALSE(test, ret); @@ -506,17 +511,19 @@ static void soc_ops_test_access(struct kunit *test) KUNIT_ASSERT_FALSE(test, ret); KUNIT_EXPECT_EQ(test, val, (param->init & ~param->rmask) | param->rreg); - result.value.integer.value[0] = 0; - result.value.integer.value[1] = 0; + result->value.integer.value[0] = 0; + result->value.integer.value[1] = 0; - ret = param->get(&kctl, &result); + ret = param->get(&kctl, result); KUNIT_ASSERT_GE(test, ret, 0); - KUNIT_EXPECT_EQ(test, result.value.integer.value[0], param->lctl); + KUNIT_EXPECT_EQ(test, result->value.integer.value[0], param->lctl); if (param->layout != SOC_OPS_TEST_SINGLE) - KUNIT_EXPECT_EQ(test, result.value.integer.value[1], param->rctl); + KUNIT_EXPECT_EQ(test, result->value.integer.value[1], param->rctl); else - KUNIT_EXPECT_EQ(test, result.value.integer.value[1], 0); + KUNIT_EXPECT_EQ(test, result->value.integer.value[1], 0); +end: + kfree(result); } KUNIT_ARRAY_PARAM(all_info_tests, all_info_test_params, info_test_desc); diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 8d4dd11c9aef..a629e0eacb20 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -399,28 +399,32 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); static int snd_soc_clip_to_platform_max(struct snd_kcontrol *kctl) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; - struct snd_ctl_elem_value uctl; + struct snd_ctl_elem_value *uctl; int ret; if (!mc->platform_max) return 0; - ret = kctl->get(kctl, &uctl); - if (ret < 0) - return ret; + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; - if (uctl.value.integer.value[0] > mc->platform_max) - uctl.value.integer.value[0] = mc->platform_max; + ret = kctl->get(kctl, uctl); + if (ret < 0) + goto out; + + if (uctl->value.integer.value[0] > mc->platform_max) + uctl->value.integer.value[0] = mc->platform_max; if (snd_soc_volsw_is_stereo(mc) && - uctl.value.integer.value[1] > mc->platform_max) - uctl.value.integer.value[1] = mc->platform_max; + uctl->value.integer.value[1] > mc->platform_max) + uctl->value.integer.value[1] = mc->platform_max; - ret = kctl->put(kctl, &uctl); - if (ret < 0) - return ret; + ret = kctl->put(kctl, uctl); - return 0; +out: + kfree(uctl); + return ret; } /** diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 7b0b8531bb32..44b60324eaa2 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -429,8 +429,11 @@ static void soc_tplg_remove_link(struct snd_soc_component *comp, dobj->unload(comp, dobj); list_del(&dobj->list); - snd_soc_remove_pcm_runtime(comp->card, - snd_soc_get_pcm_runtime(comp->card, link)); + + /* Ignored links do not need to be removed, they are not added */ + if (!link->ignore) + snd_soc_remove_pcm_runtime(comp->card, + snd_soc_get_pcm_runtime(comp->card, link)); } /* unload dai link */ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 32ffd345e07f..a487ab0b51c7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_SOF_TOPLEVEL - bool "Sound Open Firmware Support" + bool "Sound Open Firmware (SOF) platforms" help This adds support for Sound Open Firmware (SOF). SOF is free and generic open source audio DSP firmware for multiple devices. diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index ea105227227d..98324bbade15 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -65,7 +65,7 @@ int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_t dma_size = page_count * ACP_PAGE_SIZE; adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size, &adata->sha_dma_addr, - GFP_ATOMIC); + GFP_KERNEL); if (!adata->bin_buf) return -ENOMEM; } @@ -77,7 +77,7 @@ int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_t adata->data_buf = dma_alloc_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, &adata->dma_addr, - GFP_ATOMIC); + GFP_KERNEL); if (!adata->data_buf) return -ENOMEM; } @@ -90,7 +90,7 @@ int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_t adata->sram_data_buf = dma_alloc_coherent(&pci->dev, ACP_DEFAULT_SRAM_LENGTH, &adata->sram_dma_addr, - GFP_ATOMIC); + GFP_KERNEL); if (!adata->sram_data_buf) return -ENOMEM; } diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 7132916aa253..71a18f156de2 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -59,6 +59,7 @@ static void init_dma_descriptor(struct acp_dev_data *adata) switch (acp_data->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; break; @@ -99,6 +100,7 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, switch (acp_data->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_dma_cntl_0 = ACP70_DMA_CNTL_0; acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; @@ -339,6 +341,7 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) switch (adata->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_dma_ch_sts = ACP70_DMA_CH_STS; break; default: @@ -522,6 +525,7 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) switch (adata->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: wake_irq_flag = amd_sof_check_and_handle_acp70_sdw_wake_irq(sdev); break; } @@ -559,6 +563,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; break; @@ -661,6 +666,7 @@ static int acp_init(struct snd_sof_dev *sdev) switch (acp_data->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: sdw0_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN); sdw1_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN); if (sdw0_wake_en || sdw1_wake_en) @@ -712,6 +718,7 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) switch (acp_data->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: enable = true; break; } @@ -738,6 +745,7 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev) switch (acp_data->pci_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); break; } diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index d3c5b2386cdf..2b7ea8c64106 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -75,6 +75,7 @@ #define ACP63_PCI_ID 0x63 #define ACP70_PCI_ID 0x70 #define ACP71_PCI_ID 0x71 +#define ACP72_PCI_ID 0x72 #define HOST_BRIDGE_CZN 0x1630 #define HOST_BRIDGE_VGH 0x1645 diff --git a/sound/soc/sof/amd/pci-acp70.c b/sound/soc/sof/amd/pci-acp70.c index c4db21668252..3523c9a92a94 100644 --- a/sound/soc/sof/amd/pci-acp70.c +++ b/sound/soc/sof/amd/pci-acp70.c @@ -48,6 +48,7 @@ static const struct sof_amd_acp_desc acp70_chip_info = { static const struct sof_dev_desc acp70_desc = { .machines = snd_soc_acpi_amd_acp70_sof_machines, + .alt_machines = snd_soc_acpi_amd_acp70_sof_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -76,6 +77,7 @@ static int acp70_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_ switch (pci->revision) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: break; default: return -ENODEV; diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 463d418e7200..a3fd1d523c09 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -196,7 +196,6 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get) ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); - pm_runtime_mark_last_busy(scomp->dev); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d0ffa1d71145..b24943a65c89 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -217,7 +217,6 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s } ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); - pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); if (ret < 0 || reply->rhdr.error < 0) { ret = min(ret, reply->rhdr.error); diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 62bf707aa909..f00b381cec3b 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -282,11 +282,8 @@ static int imx_region_name_to_blk_type(const char *region_name) static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) { const struct imx_chip_info *chip_info; - struct reserved_mem *reserved; struct platform_device *pdev; - struct device_node *res_np; - phys_addr_t base, size; - struct resource *res; + struct resource *res, _res; int i, blk_type, ret; pdev = to_platform_device(sdev->dev); @@ -307,37 +304,18 @@ static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) "failed to fetch %s resource\n", chip_info->memory[i].name); - base = res->start; - size = resource_size(res); } else { - ret = of_property_match_string(pdev->dev.of_node, - "memory-region-names", - chip_info->memory[i].name); + ret = of_reserved_mem_region_to_resource_byname(pdev->dev.of_node, + chip_info->memory[i].name, + &_res); if (ret < 0) return dev_err_probe(sdev->dev, ret, - "no valid index for %s\n", + "no valid entry for %s\n", chip_info->memory[i].name); - - res_np = of_parse_phandle(pdev->dev.of_node, - "memory-region", - ret); - if (!res_np) - return dev_err_probe(sdev->dev, -ENODEV, - "failed to parse phandle %s\n", - chip_info->memory[i].name); - - reserved = of_reserved_mem_lookup(res_np); - of_node_put(res_np); - if (!reserved) - return dev_err_probe(sdev->dev, -ENODEV, - "failed to get %s reserved\n", - chip_info->memory[i].name); - - base = reserved->base; - size = reserved->size; + res = &_res; } - sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size); + sdev->bar[blk_type] = devm_ioremap_resource(sdev->dev, res); if (!sdev->bar[blk_type]) return dev_err_probe(sdev->dev, -ENOMEM, diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index a40a8047873e..b73dd91bd529 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -40,6 +40,19 @@ struct imx8m_chip_data { struct reset_control *run_stall; }; +static int imx8_shutdown(struct snd_sof_dev *sdev) +{ + /* + * Force the DSP to stall. After the firmware image is loaded, + * the stall will be removed during run() by a matching + * imx_sc_pm_cpu_start() call. + */ + imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, false, + RESET_VECTOR_VADDR); + + return 0; +} + /* * DSP control. */ @@ -281,11 +294,13 @@ static int imx8_ops_init(struct snd_sof_dev *sdev) static const struct imx_chip_ops imx8_chip_ops = { .probe = imx8_probe, .core_kick = imx8_run, + .core_shutdown = imx8_shutdown, }; static const struct imx_chip_ops imx8x_chip_ops = { .probe = imx8_probe, .core_kick = imx8x_run, + .core_shutdown = imx8_shutdown, }; static const struct imx_chip_ops imx8m_chip_ops = { diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index dc1d21de4ab7..4f27f8c8debf 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -266,9 +266,10 @@ config SND_SOC_SOF_METEORLAKE config SND_SOC_SOF_INTEL_LNL tristate + select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 49085ca7b46b..2cc11d8b0f70 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -579,8 +579,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, struct sof_ipc4_msg msg = {}; int ret, ret1; - /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */ - if (reload && hda->booted_from_imr && ipc4_data->fw_context_save) + /* + * if IMR booting is enabled and libraries have been restored during fw + * boot, skip the loading + */ + if (reload && hda->booted_from_imr && ipc4_data->libraries_restored) return 0; /* the fw_lib has been verified during loading, we can trust the validity here */ diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index bdfe388da198..c387efec41e9 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -629,6 +629,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) goto skip_soundwire; + /* Skip SoundWire in nocodec mode */ + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + goto skip_soundwire; + /* scan SoundWire capabilities exposed by DSDT */ ret = hda_sdw_acpi_scan(sdev); if (ret < 0) { @@ -1257,11 +1262,11 @@ static int check_tplg_quirk_mask(struct snd_soc_acpi_mach *mach) return 0; } -static char *remove_file_ext(const char *tplg_filename) +static char *remove_file_ext(struct device *dev, const char *tplg_filename) { char *filename, *tmp; - filename = kstrdup(tplg_filename, GFP_KERNEL); + filename = devm_kstrdup(dev, tplg_filename, GFP_KERNEL); if (!filename) return NULL; @@ -1345,7 +1350,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) */ if (!sof_pdata->tplg_filename) { /* remove file extension if it exists */ - tplg_filename = remove_file_ext(mach->sof_tplg_filename); + tplg_filename = remove_file_ext(sdev->dev, mach->sof_tplg_filename); if (!tplg_filename) return NULL; diff --git a/sound/soc/sof/intel/ptl.c b/sound/soc/sof/intel/ptl.c index 875d18193b05..4633cd01e7dd 100644 --- a/sound/soc/sof/intel/ptl.c +++ b/sound/soc/sof/intel/ptl.c @@ -117,6 +117,7 @@ const struct sof_intel_dsp_desc ptl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_ext, .check_sdw_irq = lnl_dsp_check_sdw_irq, .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, + .sdw_process_wakeen = hda_sdw_process_wakeen_common, .check_ipc_irq = mtl_dsp_check_ipc_irq, .check_mic_privacy_irq = sof_ptl_check_mic_privacy_irq, .process_mic_privacy = sof_ptl_process_mic_privacy, @@ -142,6 +143,7 @@ const struct sof_intel_dsp_desc wcl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_ext, .check_sdw_irq = lnl_dsp_check_sdw_irq, .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, + .sdw_process_wakeen = hda_sdw_process_wakeen_common, .check_ipc_irq = mtl_dsp_check_ipc_irq, .cl_init = mtl_dsp_cl_init, .power_down_dsp = mtl_power_down_dsp, diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 744a91a150bc..e5c8fec173c4 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -172,7 +172,6 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, goto error; } ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size); - pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); error: diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index d2f534d65edf..b0d293f62d1c 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -236,7 +236,6 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id, ret = ipc4_data->load_library(sdev, fw_lib, false); - pm_runtime_mark_last_busy(sdev->dev); err = pm_runtime_put_autosuspend(sdev->dev); if (err < 0) dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n", @@ -494,6 +493,12 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) break; case SOF_IPC4_FW_CONTEXT_SAVE: ipc4_data->fw_context_save = *tuple->value; + /* + * Set the default libraries_restored value - if full + * context save is supported then it means that + * libraries are restored + */ + ipc4_data->libraries_restored = ipc4_data->fw_context_save; break; default: break; diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 8eee3e1aadf9..374dc10d10fd 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -56,17 +56,41 @@ sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps) return stream_priv->time_info; } +static +char *sof_ipc4_set_multi_pipeline_state_debug(struct snd_sof_dev *sdev, char *buf, size_t size, + struct ipc4_pipeline_set_state_data *trigger_list) +{ + int i, offset = 0; + + for (i = 0; i < trigger_list->count; i++) { + offset += snprintf(buf + offset, size - offset, " %d", + trigger_list->pipeline_instance_ids[i]); + + if (offset >= size - 1) { + buf[size - 1] = '\0'; + break; + } + } + return buf; +} + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { struct sof_ipc4_msg msg = {{ 0 }}; u32 primary, ipc_size; + char debug_buf[32]; /* trigger a single pipeline */ if (trigger_list->count == 1) return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0], state); + dev_dbg(sdev->dev, "Set pipelines %s to state %d%s", + sof_ipc4_set_multi_pipeline_state_debug(sdev, debug_buf, sizeof(debug_buf), + trigger_list), + state, sof_ipc4_pipeline_state_str(state)); + primary = state; primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); @@ -89,7 +113,8 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 s struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; - dev_dbg(sdev->dev, "ipc4 set pipeline instance %d state %d", instance_id, state); + dev_dbg(sdev->dev, "Set pipeline %d to state %d%s", instance_id, state, + sof_ipc4_pipeline_state_str(state)); primary = state; primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); @@ -409,9 +434,33 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * If use_chain_dma attribute is set we proceed to chained DMA * trigger function that handles the rest for the substream. */ - if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, - pipeline_list, state, cmd); + if (pipeline->use_chain_dma) { + struct sof_ipc4_timestamp_info *time_info; + + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); + + ret = sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, + pipeline_list, state, cmd); + if (ret || !time_info) + return ret; + + if (state == SOF_IPC4_PIPE_PAUSED) { + /* + * Record the DAI position for delay reporting + * To handle multiple pause/resume/xrun we need to add + * the positions to simulate how the firmware behaves + */ + u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component, + substream); + + time_info->stream_end_offset += pos; + } else if (state == SOF_IPC4_PIPE_RESET) { + /* Reset the end offset as the stream is stopped */ + time_info->stream_end_offset = 0; + } + + return 0; + } /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, @@ -557,12 +606,15 @@ static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0); } -static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, - struct snd_pcm_hw_params *params) +static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, + const char *link_name, + struct snd_pcm_hw_params *params) { struct snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; + int current_config = -1; + bool partial_match; int i; list_for_each_entry(slink, &sdev->dai_link_list, list) { @@ -573,19 +625,50 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const } if (!dai_link_found) - return; + return 0; + /* + * Find the first best matching hardware config: + * rate + format + channels are matching + * rate + channel are matching + * + * The copier cannot do rate and/or channel conversion. + */ for (i = 0; i < slink->num_hw_configs; i++) { struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i]; - if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) { - /* set current config for all DAI's with matching name */ - list_for_each_entry(dai, &sdev->dai_list, list) - if (!strcmp(slink->link->name, dai->name)) - dai->current_config = le32_to_cpu(hw_config->id); + if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate) && + params_width(params) == le32_to_cpu(hw_config->tdm_slot_width) && + params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) { + current_config = le32_to_cpu(hw_config->id); + partial_match = false; + /* best match found */ break; + } else if (current_config < 0 && + params_rate(params) == le32_to_cpu(hw_config->fsync_rate) && + params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) { + current_config = le32_to_cpu(hw_config->id); + partial_match = true; + /* keep looking for better match */ } } + + if (current_config < 0) { + dev_err(sdev->dev, + "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n", + __func__, slink->link->name, slink->num_hw_configs); + return -EINVAL; + } + + dev_dbg(sdev->dev, + "hw_config for %s: %d (num_hw_configs: %d) with %s match\n", + slink->link->name, current_config, slink->num_hw_configs, + partial_match ? "partial" : "full"); + list_for_each_entry(dai, &sdev->dai_list, list) + if (!strcmp(slink->link->name, dai->name)) + dai->current_config = current_config; + + return 0; } /* @@ -728,13 +811,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; } - switch (ipc4_copier->dai_type) { - case SOF_DAI_INTEL_SSP: - ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - break; - default: - break; - } + if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP) + return ipc4_ssp_dai_config_pcm_params_match(sdev, + (char *)rtd->dai_link->name, + params); return 0; } @@ -928,8 +1008,24 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, if (!host_copier || !dai_copier) return -EINVAL; - if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) + if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) { return -EINVAL; + } else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) { + /* + * While the firmware does not supports time_info reporting for + * streams using ChainDMA, it is granted that ChainDMA can only + * be used on Host+Link pairs where the link position is + * accessible from the host side. + * + * Enable delay calculation in case of ChainDMA via host + * accessible registers. + * + * The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts + * when 1ms data is available + */ + time_info->stream_start_offset = substream->runtime->rate / MSEC_PER_SEC; + goto out; + } node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id); offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg); @@ -947,6 +1043,7 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, time_info->stream_end_offset = ppl_reg.stream_end_offset; do_div(time_info->stream_end_offset, dai_sample_size); +out: /* * Calculate the wrap boundary need to be used for delay calculation * The host counter is in bytes, it will wrap earlier than the frames diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 58b032820683..a8cdf9bc750b 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -72,6 +72,8 @@ struct sof_ipc4_fw_library { * @max_num_pipelines: max number of pipelines * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware + * @fw_context_save: Firmware supports full context save and restore + * @libraries_restored: The libraries have been retained during firmware boot * * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion @@ -87,6 +89,7 @@ struct sof_ipc4_fw_data { int max_num_pipelines; u32 max_libs_count; bool fw_context_save; + bool libraries_restored; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); @@ -123,4 +126,7 @@ size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev, void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state); +enum sof_ipc4_pipeline_state; +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state); + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 540ba140e155..591ee30551ba 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -145,6 +145,14 @@ static const struct sof_topology_token src_tokens[] = { offsetof(struct sof_ipc4_src_data, sink_rate)}, }; +/* ASRC */ +static const struct sof_topology_token asrc_tokens[] = { + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_asrc_data, out_freq)}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_asrc_data, asrc_mode)}, +}; + static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, @@ -166,6 +174,7 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)}, [SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)}, [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, + [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, }; struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, @@ -1045,6 +1054,50 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) return ret; } +static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_pipeline *spipe = swidget->spipe; + struct sof_ipc4_asrc *asrc; + int ret; + + dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); + + asrc = kzalloc(sizeof(*asrc), GFP_KERNEL); + if (!asrc) + return -ENOMEM; + + swidget->private = asrc; + + ret = sof_ipc4_get_audio_fmt(scomp, swidget, &asrc->available_fmt, + &asrc->data.base_config); + if (ret) + goto err; + + ret = sof_update_ipc_object(scomp, &asrc->data, SOF_ASRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*asrc), 1); + if (ret) { + dev_err(scomp->dev, "Parsing ASRC tokens failed\n"); + goto err; + } + + spipe->core_mask |= BIT(swidget->core); + + dev_dbg(scomp->dev, "ASRC sink rate %d, mode 0x%08x\n", + asrc->data.out_freq, asrc->data.asrc_mode); + + ret = sof_ipc4_widget_setup_msg(swidget, &asrc->msg); + if (ret) + goto err; + + return 0; +err: + sof_ipc4_free_audio_fmt(&asrc->available_fmt); + kfree(asrc); + swidget->private = NULL; + return ret; +} + static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget) { struct sof_ipc4_src *src = swidget->private; @@ -1057,6 +1110,18 @@ static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget) swidget->private = NULL; } +static void sof_ipc4_widget_free_comp_asrc(struct snd_sof_widget *swidget) +{ + struct sof_ipc4_asrc *asrc = swidget->private; + + if (!asrc) + return; + + sof_ipc4_free_audio_fmt(&asrc->available_fmt); + kfree(swidget->private); + swidget->private = NULL; +} + static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget) { struct sof_ipc4_mixer *mixer = swidget->private; @@ -1873,10 +1938,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); /* - * Chain DMA does not support stream timestamping, set node_id to invalid - * to skip the code in sof_ipc4_get_stream_start_offset(). + * Chain DMA does not support stream timestamping, but it + * can use the host side registers for delay calculation. */ - copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; + copier_data->gtw_cfg.node_id = SOF_IPC4_CHAIN_DMA_NODE_ID; return 0; } @@ -2817,6 +2882,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg = &src->msg; break; } + case snd_soc_dapm_asrc: + { + struct sof_ipc4_asrc *asrc = swidget->private; + + ipc_size = sizeof(asrc->data); + ipc_data = &asrc->data; + + msg = &asrc->msg; + break; + } case snd_soc_dapm_effect: { struct sof_ipc4_process *process = swidget->private; @@ -3503,6 +3578,15 @@ static enum sof_tokens src_token_list[] = { SOF_COMP_EXT_TOKENS, }; +static enum sof_tokens asrc_token_list[] = { + SOF_COMP_TOKENS, + SOF_ASRC_TOKENS, + SOF_AUDIO_FMT_NUM_TOKENS, + SOF_IN_AUDIO_FORMAT_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, + SOF_COMP_EXT_TOKENS, +}; + static enum sof_tokens process_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, @@ -3548,6 +3632,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY src_token_list, ARRAY_SIZE(src_token_list), NULL, sof_ipc4_prepare_src_module, NULL}, + [snd_soc_dapm_asrc] = {sof_ipc4_widget_setup_comp_asrc, sof_ipc4_widget_free_comp_asrc, + asrc_token_list, ARRAY_SIZE(asrc_token_list), + NULL, sof_ipc4_prepare_src_module, /* Common prepare with SRC */ + NULL}, [snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process, sof_ipc4_widget_free_comp_process, process_token_list, ARRAY_SIZE(process_token_list), diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index f4dc499c0ffe..14ba58d2be03 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -58,6 +58,7 @@ #define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16 +#define SOF_IPC4_CHAIN_DMA_NODE_ID 0x7fffffff #define SOF_IPC4_INVALID_NODE_ID 0xffffffff /* FW requires minimum 2ms DMA buffer size */ @@ -435,6 +436,30 @@ struct sof_ipc4_src { struct sof_ipc4_msg msg; }; +/* + * struct sof_ipc4_asrc_data - IPC data for ASRC + * @base_config: IPC base config data + * @out_freq: Output rate for sink module, passed as such from topology to FW. + * @asrc_mode: Control for ASRC features with bit-fields, passed as such from topolgy to FW. + */ +struct sof_ipc4_asrc_data { + struct sof_ipc4_base_module_cfg base_config; + uint32_t out_freq; + uint32_t asrc_mode; +} __packed __aligned(4); + +/** + * struct sof_ipc4_asrc - ASRC config data + * @data: IPC base config data + * @available_fmt: Available audio format + * @msg: IPC4 message struct containing header and data info + */ +struct sof_ipc4_asrc { + struct sof_ipc4_asrc_data data; + struct sof_ipc4_available_audio_format available_fmt; + struct sof_ipc4_msg msg; +}; + /** * struct sof_ipc4_base_module_cfg_ext - base module config extension containing the pin format * information for the module. Both @num_input_pin_fmts and @num_output_pin_fmts cannot be 0 for a diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 37e837b22ac8..a4a090e6724a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -237,6 +237,26 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms msg->extension, str); } } + +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) +{ + switch (state) { + case SOF_IPC4_PIPE_INVALID_STATE: + return " (INVALID_STATE)"; + case SOF_IPC4_PIPE_UNINITIALIZED: + return " (UNINITIALIZED)"; + case SOF_IPC4_PIPE_RESET: + return " (RESET)"; + case SOF_IPC4_PIPE_PAUSED: + return " (PAUSED)"; + case SOF_IPC4_PIPE_RUNNING: + return " (RUNNING)"; + case SOF_IPC4_PIPE_EOS: + return " (EOS)"; + default: + return " ()"; + } +} #else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg, bool data_size_valid) @@ -254,6 +274,11 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms else dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension); } + +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) +{ + return ""; +} #endif static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, @@ -576,9 +601,19 @@ EXPORT_SYMBOL(sof_ipc4_find_debug_slot_offset_by_type); static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg) { - /* no need to re-check version/ABI for subsequent boots */ - if (!sdev->first_boot) + if (!sdev->first_boot) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + + /* + * After the initial boot only check if the libraries have been + * restored when full context save is not enabled + */ + if (!ipc4_data->fw_context_save) + ipc4_data->libraries_restored = !!(ipc4_msg->primary & + SOF_IPC4_FW_READY_LIB_RESTORED); + return 0; + } sof_ipc4_create_exception_debugfs_node(sdev); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 7ff080452cbe..c1bea967737d 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) { struct resource *mmio; struct resource res; - struct device_node *mem_region; struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; int ret; @@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) return ret; } - mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); - if (!mem_region) { - dev_err(dev, "no memory-region sysmem phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); + ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res); if (ret) { dev_err(dev, "of_address_to_resource sysmem failed\n"); return ret; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 3b3582d74510..4d6e9300a9c0 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) { struct resource *mmio; struct resource res; - struct device_node *mem_region; struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; int ret; @@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) return ret; } - mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); - if (!mem_region) { - dev_err(dev, "no memory-region sysmem phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); + ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res); if (ret) { dev_err(dev, "of_address_to_resource sysmem failed\n"); return ret; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d584a72e6f52..fe43de1fe96c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -20,24 +20,6 @@ #include "sof-utils.h" #include "ops.h" -/* Create DMA buffer page table for DSP */ -static int create_page_table(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - unsigned char *dma_area, size_t size) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_pcm *spcm; - struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); - int stream = substream->stream; - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; - - return snd_sof_create_page_table(component->dev, dmab, - spcm->stream[stream].page_table.area, size); -} - /* * sof pcm period elapse work */ @@ -144,7 +126,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { + if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) { ret = pcm_ops->hw_free(component, substream); if (ret < 0) return ret; @@ -168,9 +150,11 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { - ret = create_page_table(component, substream, runtime->dma_area, - runtime->dma_bytes); + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + ret = snd_sof_create_page_table(component->dev, dmab, + spcm->stream[substream->stream].page_table.area, + runtime->dma_bytes); if (ret < 0) return ret; } @@ -728,7 +712,6 @@ static int sof_pcm_probe(struct snd_soc_component *component) ret); pm_error: - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return ret; diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c index 11b6f7da2882..373f3a125372 100644 --- a/sound/soc/sof/sof-client-ipc-flood-test.c +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -223,7 +223,6 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, ipc_duration_ms, ipc_count); - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c index 8b28c3dc920c..249bd2d6c8d2 100644 --- a/sound/soc/sof/sof-client-ipc-kernel-injector.c +++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c @@ -65,7 +65,6 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); - pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); if (ret < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret); diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c index ba7ca1c5027f..9c8a0fbfb8df 100644 --- a/sound/soc/sof/sof-client-ipc-msg-injector.c +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -137,7 +137,6 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev) if (ret) dev_err(dev, "IPC message send failed: %d\n", ret); - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index aff9ce980429..663c0d3c314c 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -238,7 +238,6 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, kfree(desc); pm_error: - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); @@ -289,7 +288,6 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from, if (!ret) ret = count; - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); @@ -337,7 +335,6 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, if (!ret) ret = count; - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d612d693efc3..b6d5c8024f8c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2378,14 +2378,25 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw) { + struct snd_soc_tplg_private *priv = &tw->priv; + int ret; + + /* for snd_soc_dapm_widget.no_wname_in_kcontrol_name */ + ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, + ARRAY_SIZE(dapm_widget_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n", + w->name); + return ret; + } + if (WIDGET_IS_DAI(w->id)) { static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}}; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *priv = &tw->priv; struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; - int ret; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); if (!swidget) diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 1b053de1ae7c..84a52fd4c3ea 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "ST SPEAr" + config SND_SPEAR_SOC tristate select SND_SOC_GENERIC_DMAENGINE_PCM @@ -8,3 +10,5 @@ config SND_SPEAR_SPDIF_OUT config SND_SPEAR_SPDIF_IN tristate + +endmenu diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig index 5e0ac8278572..8ddfe55aa21f 100644 --- a/sound/soc/sprd/Kconfig +++ b/sound/soc/sprd/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Spreadtrum" + config SND_SOC_SPRD tristate "SoC Audio for the Spreadtrum SoC chips" depends on ARCH_SPRD || COMPILE_TEST @@ -14,3 +16,5 @@ config SND_SOC_SPRD_MCDT Say y here to enable multi-channel data transfer support. It is used for sound stream transmission between audio subsystem and other AP/CP subsystem. + +endmenu diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig index 279ac5c1d309..1e11aa74594d 100644 --- a/sound/soc/starfive/Kconfig +++ b/sound/soc/starfive/Kconfig @@ -1,15 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_STARFIVE - tristate "Audio support for StarFive SoC" +menu "StarFive" depends on COMPILE_TEST || ARCH_STARFIVE - help - Say Y or M if you want to add support for codecs attached to - the Starfive SoCs' Audio interfaces. You will also need to - select the audio interfaces to support below. + depends on HAVE_CLK config SND_SOC_JH7110_PWMDAC tristate "JH7110 PWM-DAC device driver" - depends on HAVE_CLK && SND_SOC_STARFIVE select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_SPDIF help @@ -18,7 +13,8 @@ config SND_SOC_JH7110_PWMDAC config SND_SOC_JH7110_TDM tristate "JH7110 TDM device driver" - depends on HAVE_CLK && SND_SOC_STARFIVE select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for StarFive TDM driver. + +endmenu diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig index f881da4b6aea..8e12356a4490 100644 --- a/sound/soc/sti/Kconfig +++ b/sound/soc/sti/Kconfig @@ -3,7 +3,7 @@ # STM SoC audio configuration # menuconfig SND_SOC_STI - tristate "SoC Audio support for STI System-On-Chip" + tristate "STI" depends on SND_SOC depends on ARCH_STI || COMPILE_TEST select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index da1f7a16605b..2753d6c2a826 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "STMicroelectronics STM32 SOC audio support" +menu "STMicroelectronics STM32" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6037b7a9c97b..6ca21780f21d 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -461,20 +461,22 @@ static int stm32_i2s_set_parent_rate(struct stm32_i2s_data *i2s, return -EINVAL; } -static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int stm32_i2smclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_i2smclk_data *mclk = to_mclk_data(hw); struct stm32_i2s_data *i2s = mclk->i2s_data; int ret; - ret = stm32_i2s_calc_clk_div(i2s, *prate, rate); + ret = stm32_i2s_calc_clk_div(i2s, req->best_parent_rate, req->rate); if (ret) return ret; - mclk->freq = *prate / i2s->divider; + mclk->freq = req->best_parent_rate / i2s->divider; - return mclk->freq; + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_i2smclk_recalc_rate(struct clk_hw *hw, @@ -530,7 +532,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_i2smclk_enable, .disable = stm32_i2smclk_disable, .recalc_rate = stm32_i2smclk_recalc_rate, - .round_rate = stm32_i2smclk_round_rate, + .determine_rate = stm32_i2smclk_determine_rate, .set_rate = stm32_i2smclk_set_rate, }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index bf5299ba11c3..463a2b7d023b 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -489,20 +489,22 @@ static int stm32_sai_set_parent_rate(struct stm32_sai_sub_data *sai, return -EINVAL; } -static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int stm32_sai_mclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); struct stm32_sai_sub_data *sai = mclk->sai_data; int div; - div = stm32_sai_get_clk_div(sai, *prate, rate); + div = stm32_sai_get_clk_div(sai, req->best_parent_rate, req->rate); if (div <= 0) return -EINVAL; - mclk->freq = *prate / div; + mclk->freq = req->best_parent_rate / div; - return mclk->freq; + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, @@ -558,7 +560,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_sai_mclk_enable, .disable = stm32_sai_mclk_disable, .recalc_rate = stm32_sai_mclk_recalc_rate, - .round_rate = stm32_sai_mclk_round_rate, + .determine_rate = stm32_sai_mclk_determine_rate, .set_rate = stm32_sai_mclk_set_rate, }; diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 1f18f016acbb..205f422d9ded 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "Allwinner SoC Audio support" +menu "Allwinner" depends on ARCH_SUNXI || COMPILE_TEST config SND_SUN4I_CODEC diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 2463c22e9cf6..71203a9197ed 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Tegra" + config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST @@ -293,3 +295,5 @@ config SND_SOC_TEGRA_SGTL5000 Colibri T30. endif + +endmenu diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 2376cc76e684..21aeaeba0b10 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -2066,7 +2066,7 @@ static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg) return true; default: break; - }; + } } return false; diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index e22e41af3226..3323cf96e309 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "Audio support for Texas Instruments SoCs" +menu "Texas Instruments" depends on DMA_OMAP || TI_EDMA || TI_K3_UDMA || COMPILE_TEST config SND_SOC_TI_EDMA_PCM diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index caf1887cc9d1..621a9d5f9377 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2218,7 +2218,7 @@ static const struct gpio_chip davinci_mcasp_template_chip = { .request = davinci_mcasp_gpio_request, .free = davinci_mcasp_gpio_free, .direction_output = davinci_mcasp_gpio_direction_out, - .set_rv = davinci_mcasp_gpio_set, + .set = davinci_mcasp_gpio_set, .direction_input = davinci_mcasp_gpio_direction_in, .get = davinci_mcasp_gpio_get, .get_direction = davinci_mcasp_gpio_get_direction, diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index ddfa6424c656..7c978639e0aa 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -1,18 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 -config SND_SOC_UNIPHIER - tristate "ASoC support for UniPhier" +menu "UniPhier" depends on (ARCH_UNIPHIER || COMPILE_TEST) - help - Say Y or M if you want to add support for the Socionext - UniPhier SoC audio interfaces. You will also need to select the - audio interfaces to support below. - If unsure select "N". config SND_SOC_UNIPHIER_AIO tristate "UniPhier AIO DAI Driver" select REGMAP_MMIO select SND_SOC_COMPRESS - depends on SND_SOC_UNIPHIER help This adds ASoC driver support for Socionext UniPhier 'AIO' Audio Input/Output subsystem. @@ -21,7 +14,6 @@ config SND_SOC_UNIPHIER_AIO config SND_SOC_UNIPHIER_LD11 tristate "UniPhier LD11/LD20 Device Driver" - depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO help This adds ASoC driver for Socionext UniPhier LD11/LD20 @@ -31,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11 config SND_SOC_UNIPHIER_PXS2 tristate "UniPhier PXs2 Device Driver" - depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO help This adds ASoC driver for Socionext UniPhier PXs2 @@ -41,10 +32,11 @@ config SND_SOC_UNIPHIER_PXS2 config SND_SOC_UNIPHIER_EVEA_CODEC tristate "UniPhier SoC internal audio codec" - depends on SND_SOC_UNIPHIER select REGMAP_MMIO help This adds Codec driver for Socionext UniPhier LD11/20 SoC internal DAC. This driver supports Line In / Out and HeadPhone. Select Y if you use such device. If unsure select "N". + +endmenu diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig index 34b2438b52d3..113058060350 100644 --- a/sound/soc/ux500/Kconfig +++ b/sound/soc/ux500/Kconfig @@ -3,7 +3,7 @@ # Ux500 SoC audio configuration # menuconfig SND_SOC_UX500 - tristate "SoC Audio support for Ux500 platform" + tristate "Ux500" depends on SND_SOC depends on MFD_DB8500_PRCMU help diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig index 5bd2730aab76..670e0f301097 100644 --- a/sound/soc/xilinx/Kconfig +++ b/sound/soc/xilinx/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Xilinx" + config SND_SOC_XILINX_I2S tristate "Audio support for the Xilinx I2S" help @@ -21,3 +23,5 @@ config SND_SOC_XILINX_SPDIF Select this option to enable Xilinx SPDIF Audio. This provides playback and capture of SPDIF audio in AES format. + +endmenu diff --git a/sound/soc/xtensa/Kconfig b/sound/soc/xtensa/Kconfig index 74b778f186ce..87457272c773 100644 --- a/sound/soc/xtensa/Kconfig +++ b/sound/soc/xtensa/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Xtensa" + config SND_SOC_XTFPGA_I2S tristate "XTFPGA I2S master" select REGMAP_MMIO @@ -6,3 +8,5 @@ config SND_SOC_XTFPGA_I2S Say Y or M if you want to add support for codecs attached to the I2S interface on XTFPGA daughter board. You will also need to select the drivers for the rest of XTFPGA audio subsystem. + +endmenu diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 1b44119edfbc..1a1fe3ceb76c 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1230,7 +1230,7 @@ static int snd_cs4231_pcm(struct snd_card *card) /* global setup */ pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, "CS4231"); + strscpy(pcm->name, "CS4231"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->op->dev, 64 * 1024, 128 * 1024); @@ -1256,7 +1256,7 @@ static int snd_cs4231_timer(struct snd_card *card) err = snd_timer_new(card, "CS4231", &tid, &timer); if (err < 0) return err; - strcpy(timer->name, "CS4231"); + strscpy(timer->name, "CS4231"); timer->private_data = chip; timer->hw = snd_cs4231_timer_table; chip->timer = timer; @@ -1530,7 +1530,7 @@ static int snd_cs4231_mixer(struct snd_card *card) if (snd_BUG_ON(!chip || !chip->pcm)) return -EINVAL; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { err = snd_ctl_add(card, @@ -1565,8 +1565,8 @@ static int cs4231_attach_begin(struct platform_device *op, if (err < 0) return err; - strcpy(card->driver, "CS4231"); - strcpy(card->shortname, "Sun CS4231"); + strscpy(card->driver, "CS4231"); + strscpy(card->shortname, "Sun CS4231"); chip = card->private_data; chip->card = card; @@ -1964,12 +1964,12 @@ static int snd_cs4231_ebus_create(struct snd_card *card, chip->op = op; memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); - strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); + strscpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback; chip->c_dma.ebus_info.client_cookie = chip; chip->c_dma.ebus_info.irq = op->archdata.irqs[0]; - strcpy(chip->p_dma.ebus_info.name, "cs4231(play)"); + strscpy(chip->p_dma.ebus_info.name, "cs4231(play)"); chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback; chip->p_dma.ebus_info.client_cookie = chip; diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 8f6929ced2c8..a0a7f90b6146 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -336,7 +336,7 @@ static int snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; - strcpy(pcm->name, "at73c213"); + strscpy(pcm->name, "at73c213"); chip->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops); @@ -713,7 +713,7 @@ static int snd_at73c213_mixer(struct snd_at73c213 *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { errval = snd_ctl_add(card, @@ -983,8 +983,8 @@ static int snd_at73c213_probe(struct spi_device *spi) if (retval) goto out_ssc; - strcpy(card->driver, "at73c213"); - strcpy(card->shortname, board->shortname); + strscpy(card->driver, "at73c213"); + strscpy(card->shortname, board->shortname); sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq); retval = snd_card_register(card); diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index 9e98c4c73fd1..14b990cf3b22 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -121,7 +121,7 @@ snd_emux_init_hwdep(struct snd_emux *emu) if (err < 0) return err; emu->hwdep = hw; - strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME); + strscpy(hw->name, SNDRV_EMUX_HWDEP_NAME); hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE; hw->ops.ioctl = snd_emux_hwdep_ioctl; /* The ioctl parameter types are compatible between 32- and diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index 5f98d5201ba2..1145c7956afd 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -60,7 +60,7 @@ snd_emux_init_seq_oss(struct snd_emux *emu) return; emu->oss_synth = dev; - strcpy(dev->name, emu->name); + strscpy(dev->name, emu->name); arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); arg->type = SYNTH_TYPE_SAMPLE; arg->subtype = SAMPLE_TYPE_AWE32; diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index d562a30b087f..9eb4bf9b138b 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -125,8 +125,8 @@ static int usb6fire_chip_probe(struct usb_interface *intf, dev_err(&intf->dev, "cannot create alsa card.\n"); return ret; } - strcpy(card->driver, "6FireUSB"); - strcpy(card->shortname, "TerraTec DMX6FireUSB"); + strscpy(card->driver, "6FireUSB"); + strscpy(card->shortname, "TerraTec DMX6FireUSB"); sprintf(card->longname, "%s at %d:%d", card->shortname, device->bus->busnum, device->devnum); diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index de2691d58de6..923f7767e62f 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -183,7 +183,7 @@ int usb6fire_midi_init(struct sfire_chip *chip) return ret; } rt->instance->private_data = rt; - strcpy(rt->instance->name, "DMX6FireUSB MIDI"); + strscpy(rt->instance->name, "DMX6FireUSB MIDI"); rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 32c39d8bd2e5..d53cad97889a 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -640,7 +640,7 @@ int usb6fire_pcm_init(struct sfire_chip *chip) } pcm->private_data = rt; - strcpy(pcm->name, "DMX 6Fire USB"); + strscpy(pcm->name, "DMX 6Fire USB"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); diff --git a/sound/usb/card.c b/sound/usb/card.c index 9fb8726a6c93..10d9b7285597 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -616,9 +616,10 @@ static void usb_audio_make_shortname(struct usb_device *dev, usb_string(dev, dev->descriptor.iProduct, card->shortname, sizeof(card->shortname)) <= 0) { /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); + scnprintf(card->shortname, sizeof(card->shortname), + "USB Device %#04x:%#04x", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); } strim(card->shortname); @@ -756,9 +757,9 @@ static int snd_usb_audio_create(struct usb_interface *intf, card->private_free = snd_usb_audio_free; - strcpy(card->driver, "USB-Audio"); - sprintf(component, "USB%04x:%04x", - USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); + strscpy(card->driver, "USB-Audio"); + scnprintf(component, sizeof(component), "USB%04x:%04x", + USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); usb_audio_make_shortname(dev, chip, quirk); diff --git a/sound/usb/format.c b/sound/usb/format.c index 8cd54f7bf33a..ec95a063beb1 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -310,16 +310,14 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int rate) { - struct usb_interface *iface; struct usb_host_interface *alts; unsigned char *fmt; unsigned int max_rate; - iface = usb_ifnum_to_if(chip->dev, fp->iface); - if (!iface) + alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting); + if (!alts) return true; - alts = &iface->altsetting[fp->altset_idx]; fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE); if (!fmt) @@ -328,20 +326,24 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip, if (fmt[0] == 10) { /* bLength */ max_rate = combine_quad(&fmt[6]); - /* Validate max rate */ - if (max_rate != 48000 && - max_rate != 96000 && - max_rate != 192000 && - max_rate != 384000) { - + switch (max_rate) { + case 192000: + if (rate == 176400 || rate == 192000) + return true; + fallthrough; + case 96000: + if (rate == 88200 || rate == 96000) + return true; + fallthrough; + case 48000: + return (rate == 44100 || rate == 48000); + default: usb_audio_info(chip, "%u:%d : unexpected max rate: %u\n", fp->iface, fp->altsetting, max_rate); return true; } - - return rate <= max_rate; } return true; diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index e9eb5c74d6c7..f2f9261489a2 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -678,7 +678,7 @@ static int line6_hwdep_init(struct usb_line6 *line6) err = snd_hwdep_new(line6->card, "config", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "config"); + strscpy(hwdep->name, "config"); hwdep->iface = SNDRV_HWDEP_IFACE_LINE6; hwdep->ops = hwdep_ops; hwdep->private_data = line6; @@ -770,9 +770,9 @@ int line6_probe(struct usb_interface *interface, line6->ifcdev = &interface->dev; INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work); - strcpy(card->id, properties->id); - strcpy(card->driver, driver_name); - strcpy(card->shortname, properties->name); + strscpy(card->id, properties->id); + strscpy(card->driver, driver_name); + strscpy(card->shortname, properties->name); sprintf(card->longname, "Line 6 %s at USB %s", properties->name, dev_name(line6->ifcdev)); card->private_free = line6_destruct; diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index 9b5176086280..1d77794b4490 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -228,8 +228,8 @@ static int snd_line6_new_midi(struct usb_line6 *line6, return err; rmidi = *rmidi_ret; - strcpy(rmidi->id, line6->properties->id); - strcpy(rmidi->name, line6->properties->name); + strscpy(rmidi->id, line6->properties->id); + strscpy(rmidi->name, line6->properties->name); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index d4dbbc432505..c1e2a8ab66fa 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -486,7 +486,7 @@ static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) if (err < 0) return err; pcm = *pcm_ret; - strcpy(pcm->name, line6->properties->name); + strscpy(pcm->name, line6->properties->name); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index c073b38cd673..68cda7bf330c 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -199,7 +199,7 @@ static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= size) uinfo->value.enumerated.item = size - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, toneport_source_info[uinfo->value.enumerated.item].name); return 0; diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 866e613fee4f..acb3bf92857c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2407,7 +2407,7 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi *umidi, out_ports, in_ports, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, umidi->card->shortname); + strscpy(rmidi->name, umidi->card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 692dfc3c182f..030569fda416 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -1058,7 +1058,8 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi) fill_ump_ep_name(ump, dev, dev->descriptor.iProduct); /* fill fallback name */ if (!*ump->info.name) - sprintf(ump->info.name, "USB MIDI %d", rmidi->index); + scnprintf(ump->info.name, sizeof(ump->info.name), + "USB MIDI %d", rmidi->index); /* copy as rawmidi name if not set */ if (!*ump->core.name) strscpy(ump->core.name, ump->info.name, diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 4f6b20ed29dd..e200e4af799c 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1246,8 +1246,8 @@ static int ua101_probe(struct usb_interface *interface, goto probe_error; name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101"; - strcpy(card->driver, "UA-101"); - strcpy(card->shortname, name); + strscpy(card->driver, "UA-101"); + strscpy(card->shortname, name); usb_make_path(ua->dev, usb_path, sizeof(usb_path)); snprintf(ua->card->longname, sizeof(ua->card->longname), "EDIROL %s (serial %s), %u Hz at %s, %s speed", name, @@ -1272,7 +1272,7 @@ static int ua101_probe(struct usb_interface *interface, if (err < 0) goto probe_error; ua->pcm->private_data = ua; - strcpy(ua->pcm->name, name); + strscpy(ua->pcm->name, name); snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops); snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops); snd_pcm_set_managed_buffer_all(ua->pcm, SNDRV_DMA_TYPE_VMALLOC, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 11be79af26db..63b300bc67ba 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -674,40 +674,40 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter return 0; switch (iterm->type >> 16) { case UAC3_SELECTOR_UNIT: - strcpy(name, "Selector"); + strscpy(name, "Selector", maxlen); return 8; case UAC3_PROCESSING_UNIT: - strcpy(name, "Process Unit"); + strscpy(name, "Process Unit", maxlen); return 12; case UAC3_EXTENSION_UNIT: - strcpy(name, "Ext Unit"); + strscpy(name, "Ext Unit", maxlen); return 8; case UAC3_MIXER_UNIT: - strcpy(name, "Mixer"); + strscpy(name, "Mixer", maxlen); return 5; default: - return sprintf(name, "Unit %d", iterm->id); + return scnprintf(name, maxlen, "Unit %d", iterm->id); } } switch (iterm->type & 0xff00) { case 0x0100: - strcpy(name, "PCM"); + strscpy(name, "PCM", maxlen); return 3; case 0x0200: - strcpy(name, "Mic"); + strscpy(name, "Mic", maxlen); return 3; case 0x0400: - strcpy(name, "Headset"); + strscpy(name, "Headset", maxlen); return 7; case 0x0500: - strcpy(name, "Phone"); + strscpy(name, "Phone", maxlen); return 5; } for (names = iterm_names; names->type; names++) { if (names->type == iterm->type) { - strcpy(name, names->name); + strscpy(name, names->name, maxlen); return strlen(names->name); } } @@ -2804,7 +2804,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, len = get_term_name(state->chip, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); if (! len) - sprintf(namelist[i], "Input %u", i); + scnprintf(namelist[i], MAX_ITEM_NAME_LEN, "Input %u", i); } kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); @@ -3569,7 +3569,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif) struct usb_mixer_interface *mixer; int err; - strcpy(chip->card->mixername, "USB Mixer"); + strscpy(chip->card->mixername, "USB Mixer"); mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); if (!mixer) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 0e9b5431a47f..faac7df1fbcf 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -383,6 +383,13 @@ static const struct usbmix_name_map ms_usb_link_map[] = { { 0 } /* terminator */ }; +/* KTMicro USB */ +static struct usbmix_name_map s31b2_0022_map[] = { + { 23, "Speaker Playback" }, + { 18, "Headphone Playback" }, + { 0 } +}; + /* ASUS ROG Zenith II with Realtek ALC1220-VB */ static const struct usbmix_name_map asus_zenith_ii_map[] = { { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ @@ -692,6 +699,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x045e, 0x083c), .map = ms_usb_link_map, }, + { + /* KTMicro USB */ + .id = USB_ID(0X31b2, 0x0022), + .map = s31b2_0022_map, + }, { 0 } /* terminator */ }; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index aad205df93b2..3df537fdb9f1 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -55,13 +56,13 @@ struct std_mono_table { * version, we keep it mono for simplicity. */ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, - unsigned int unitid, - unsigned int control, - unsigned int cmask, - int val_type, - unsigned int idx_off, - const char *name, - snd_kcontrol_tlv_rw_t *tlv_callback) + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + unsigned int idx_off, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) { struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; @@ -78,7 +79,8 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, cval->idx_off = idx_off; /* get_min_max() is called only for integer volumes later, - * so provide a short-cut for booleans */ + * so provide a short-cut for booleans + */ cval->min = 0; cval->max = 1; cval->res = 0; @@ -108,15 +110,16 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, } static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, - unsigned int unitid, - unsigned int control, - unsigned int cmask, - int val_type, - const char *name, - snd_kcontrol_tlv_rw_t *tlv_callback) + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) { return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask, - val_type, 0 /* Offset */, name, tlv_callback); + val_type, 0 /* Offset */, + name, tlv_callback); } /* @@ -127,9 +130,10 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, { int err; - while (t->name != NULL) { + while (t->name) { err = snd_create_std_mono_ctl(mixer, t->unitid, t->control, - t->cmask, t->val_type, t->name, t->tlv_callback); + t->cmask, t->val_type, t->name, + t->tlv_callback); if (err < 0) return err; t++; @@ -209,12 +213,11 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb) if (code == rc->mute_code) snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); mixer->rc_code = code; - wmb(); wake_up(&mixer->rc_waitq); } static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, - long count, loff_t *offset) + long count, loff_t *offset) { struct usb_mixer_interface *mixer = hw->private_data; int err; @@ -234,7 +237,7 @@ static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, } static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, - poll_table *wait) + poll_table *wait) { struct usb_mixer_interface *mixer = hw->private_data; @@ -285,7 +288,7 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) mixer->rc_setup_packet->wLength = cpu_to_le16(len); usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), - (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, + (u8 *)mixer->rc_setup_packet, mixer->rc_buffer, len, snd_usb_soundblaster_remote_complete, mixer); return 0; } @@ -310,20 +313,20 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, if (chip->usb_id == USB_ID(0x041e, 0x3042)) err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - !value, 0, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0); /* USB X-Fi S51 Pro */ if (chip->usb_id == USB_ID(0x041e, 0x30df)) err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - !value, 0, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0); else err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - value, index + 2, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + value, index + 2, NULL, 0); snd_usb_unlock_shutdown(chip); return err; } @@ -377,17 +380,17 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) struct snd_kcontrol_new knew; /* USB X-Fi S51 doesn't have a CMSS LED */ - if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042) && i == 0) continue; /* USB X-Fi S51 Pro doesn't have one either */ - if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0) + if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df) && i == 0) continue; if (i > 1 && /* Live24ext has 2 LEDs only */ (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) - break; + break; knew = snd_audigy2nx_control; knew.name = snd_audigy2nx_led_names[i]; @@ -481,9 +484,9 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, buf[0] = 0x01; buf[1] = value ? 0x02 : 0x01; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - 0x0400, 0x0e00, buf, 2); + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0x0400, 0x0e00, buf, 2); snd_usb_unlock_shutdown(chip); return err; } @@ -529,6 +532,265 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) &snd_emu0204_control, NULL); } +#if IS_REACHABLE(CONFIG_INPUT) +/* + * Sony DualSense controller (PS5) jack detection + * + * Since this is an UAC 1 device, it doesn't support jack detection. + * However, the controller hid-playstation driver reports HP & MIC + * insert events through a dedicated input device. + */ + +#define SND_DUALSENSE_JACK_OUT_TERM_ID 3 +#define SND_DUALSENSE_JACK_IN_TERM_ID 4 + +struct dualsense_mixer_elem_info { + struct usb_mixer_elem_info info; + struct input_handler ih; + struct input_device_id id_table[2]; + bool connected; +}; + +static void snd_dualsense_ih_event(struct input_handle *handle, + unsigned int type, unsigned int code, + int value) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_mixer_elem_list *me; + + if (type != EV_SW) + return; + + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); + me = &mei->info.head; + + if ((me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && code == SW_HEADPHONE_INSERT) || + (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && code == SW_MICROPHONE_INSERT)) { + mei->connected = !!value; + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &me->kctl->id); + } +} + +static bool snd_dualsense_ih_match(struct input_handler *handler, + struct input_dev *dev) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_device *snd_dev; + char *input_dev_path, *usb_dev_path; + size_t usb_dev_path_len; + bool match = false; + + mei = container_of(handler, struct dualsense_mixer_elem_info, ih); + snd_dev = mei->info.head.mixer->chip->dev; + + input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + if (!input_dev_path) { + dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); + return false; + } + + usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); + if (!usb_dev_path) { + dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); + goto free_paths; + } + + /* + * Ensure the VID:PID matched input device supposedly owned by the + * hid-playstation driver belongs to the actual hardware handled by + * the current USB audio device, which implies input_dev_path being + * a subpath of usb_dev_path. + * + * This verification is necessary when there is more than one identical + * controller attached to the host system. + */ + usb_dev_path_len = strlen(usb_dev_path); + if (usb_dev_path_len >= strlen(input_dev_path)) + goto free_paths; + + usb_dev_path[usb_dev_path_len] = '/'; + match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); + +free_paths: + kfree(input_dev_path); + kfree(usb_dev_path); + + return match; +} + +static int snd_dualsense_ih_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int err; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = handler->name; + + err = input_register_handle(handle); + if (err) + goto err_free; + + err = input_open_device(handle); + if (err) + goto err_unregister; + + return 0; + +err_unregister: + input_unregister_handle(handle); +err_free: + kfree(handle); + return err; +} + +static void snd_dualsense_ih_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static void snd_dualsense_ih_start(struct input_handle *handle) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_mixer_elem_list *me; + int status = -1; + + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); + me = &mei->info.head; + + if (me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && + test_bit(SW_HEADPHONE_INSERT, handle->dev->swbit)) + status = test_bit(SW_HEADPHONE_INSERT, handle->dev->sw); + else if (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && + test_bit(SW_MICROPHONE_INSERT, handle->dev->swbit)) + status = test_bit(SW_MICROPHONE_INSERT, handle->dev->sw); + + if (status >= 0) { + mei->connected = !!status; + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &me->kctl->id); + } +} + +static int snd_dualsense_jack_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); + + ucontrol->value.integer.value[0] = mei->connected; + + return 0; +} + +static const struct snd_kcontrol_new snd_dualsense_jack_control = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = snd_dualsense_jack_get, +}; + +static int snd_dualsense_resume_jack(struct usb_mixer_elem_list *list) +{ + snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &list->kctl->id); + return 0; +} + +static void snd_dualsense_mixer_elem_free(struct snd_kcontrol *kctl) +{ + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); + + if (mei->ih.event) + input_unregister_handler(&mei->ih); + + snd_usb_mixer_elem_free(kctl); +} + +static int snd_dualsense_jack_create(struct usb_mixer_interface *mixer, + const char *name, bool is_output) +{ + struct dualsense_mixer_elem_info *mei; + struct input_device_id *idev_id; + struct snd_kcontrol *kctl; + int err; + + mei = kzalloc(sizeof(*mei), GFP_KERNEL); + if (!mei) + return -ENOMEM; + + snd_usb_mixer_elem_init_std(&mei->info.head, mixer, + is_output ? SND_DUALSENSE_JACK_OUT_TERM_ID : + SND_DUALSENSE_JACK_IN_TERM_ID); + + mei->info.head.resume = snd_dualsense_resume_jack; + mei->info.val_type = USB_MIXER_BOOLEAN; + mei->info.channels = 1; + mei->info.min = 0; + mei->info.max = 1; + + kctl = snd_ctl_new1(&snd_dualsense_jack_control, mei); + if (!kctl) { + kfree(mei); + return -ENOMEM; + } + + strscpy(kctl->id.name, name, sizeof(kctl->id.name)); + kctl->private_free = snd_dualsense_mixer_elem_free; + + err = snd_usb_mixer_add_control(&mei->info.head, kctl); + if (err) + return err; + + idev_id = &mei->id_table[0]; + idev_id->flags = INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT | + INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT; + idev_id->vendor = USB_ID_VENDOR(mixer->chip->usb_id); + idev_id->product = USB_ID_PRODUCT(mixer->chip->usb_id); + idev_id->evbit[BIT_WORD(EV_SW)] = BIT_MASK(EV_SW); + if (is_output) + idev_id->swbit[BIT_WORD(SW_HEADPHONE_INSERT)] = BIT_MASK(SW_HEADPHONE_INSERT); + else + idev_id->swbit[BIT_WORD(SW_MICROPHONE_INSERT)] = BIT_MASK(SW_MICROPHONE_INSERT); + + mei->ih.event = snd_dualsense_ih_event; + mei->ih.match = snd_dualsense_ih_match; + mei->ih.connect = snd_dualsense_ih_connect; + mei->ih.disconnect = snd_dualsense_ih_disconnect; + mei->ih.start = snd_dualsense_ih_start; + mei->ih.name = name; + mei->ih.id_table = mei->id_table; + + err = input_register_handler(&mei->ih); + if (err) { + dev_warn(&mixer->chip->dev->dev, + "Could not register input handler: %d\n", err); + mei->ih.event = NULL; + } + + return 0; +} + +static int snd_dualsense_controls_create(struct usb_mixer_interface *mixer) +{ + int err; + + err = snd_dualsense_jack_create(mixer, "Headphone Jack", true); + if (err < 0) + return err; + + return snd_dualsense_jack_create(mixer, "Headset Mic Jack", false); +} +#endif /* IS_REACHABLE(CONFIG_INPUT) */ + /* ASUS Xonar U1 / U3 controls */ static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, @@ -856,6 +1118,7 @@ static const struct snd_kcontrol_new snd_mbox1_src_switch = { static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer) { int err; + err = add_single_ctl_with_resume(mixer, 0, snd_mbox1_clk_switch_resume, &snd_mbox1_clk_switch, NULL); @@ -869,7 +1132,7 @@ static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer) /* Native Instruments device quirks */ -#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) +#define _MAKE_NI_CONTROL(bRequest, wIndex) ((bRequest) << 16 | (wIndex)) static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl) @@ -1021,7 +1284,7 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ /* FTU Effect switch (also used by C400/C600) */ static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { static const char *const texts[8] = { "Room 1", "Room 2", "Room 3", "Hall 1", @@ -1055,7 +1318,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer, } static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.enumerated.item[0] = kctl->private_value >> 24; return 0; @@ -1086,7 +1349,7 @@ static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list) } static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); unsigned int pval = list->kctl->private_value; @@ -1104,7 +1367,7 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, - int validx, int bUnitID) + int validx, int bUnitID) { static struct snd_kcontrol_new template = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1143,22 +1406,22 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) for (in = 0; in < 8; in++) { cmask = BIT(in); snprintf(name, sizeof(name), - "AIn%d - Out%d Capture Volume", - in + 1, out + 1); + "AIn%d - Out%d Capture Volume", + in + 1, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } for (in = 8; in < 16; in++) { cmask = BIT(in); snprintf(name, sizeof(name), - "DIn%d - Out%d Playback Volume", - in - 7, out + 1); + "DIn%d - Out%d Playback Volume", + in - 7, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1219,10 +1482,10 @@ static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer) for (ch = 0; ch < 4; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Return %d Volume", ch + 1); + "Effect Return %d Volume", ch + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - snd_usb_mixer_vol_tlv); + cmask, val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1243,20 +1506,20 @@ static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer) for (ch = 0; ch < 8; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Send AIn%d Volume", ch + 1); + "Effect Send AIn%d Volume", ch + 1); err = snd_create_std_mono_ctl(mixer, id, control, cmask, - val_type, name, - snd_usb_mixer_vol_tlv); + val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } for (ch = 8; ch < 16; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Send DIn%d Volume", ch - 7); + "Effect Send DIn%d Volume", ch - 7); err = snd_create_std_mono_ctl(mixer, id, control, cmask, - val_type, name, - snd_usb_mixer_vol_tlv); + val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1346,19 +1609,19 @@ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) for (out = 0; out < num_outs; out++) { if (chan < num_outs) { snprintf(name, sizeof(name), - "PCM%d-Out%d Playback Volume", - chan + 1, out + 1); + "PCM%d-Out%d Playback Volume", + chan + 1, out + 1); } else { snprintf(name, sizeof(name), - "In%d-Out%d Playback Volume", - chan - num_outs + 1, out + 1); + "In%d-Out%d Playback Volume", + chan - num_outs + 1, out + 1); } cmask = (out == 0) ? 0 : BIT(out - 1); offset = chan * num_outs; err = snd_create_std_mono_ctl_offset(mixer, id, control, - cmask, val_type, offset, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1377,7 +1640,7 @@ static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer) const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, snd_usb_mixer_vol_tlv); + name, snd_usb_mixer_vol_tlv); } /* This control needs a volume quirk, see mixer.c */ @@ -1390,7 +1653,7 @@ static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, snd_usb_mixer_vol_tlv); + name, snd_usb_mixer_vol_tlv); } /* This control needs a volume quirk, see mixer.c */ @@ -1403,7 +1666,7 @@ static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, NULL); + name, NULL); } static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) @@ -1432,18 +1695,18 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) for (chan = 0; chan < num_outs + num_ins; chan++) { if (chan < num_outs) { snprintf(name, sizeof(name), - "Effect Send DOut%d", - chan + 1); + "Effect Send DOut%d", + chan + 1); } else { snprintf(name, sizeof(name), - "Effect Send AIn%d", - chan - num_outs + 1); + "Effect Send AIn%d", + chan - num_outs + 1); } cmask = (chan == 0) ? 0 : BIT(chan - 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1478,14 +1741,14 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer for (chan = 0; chan < num_outs; chan++) { snprintf(name, sizeof(name), - "Effect Return %d", - chan + 1); + "Effect Return %d", + chan + 1); cmask = (chan == 0) ? 0 : BIT(chan + (chan % 2) * num_outs - 1); err = snd_create_std_mono_ctl_offset(mixer, id, control, - cmask, val_type, offset, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1626,7 +1889,7 @@ static const struct std_mono_table ebox44_table[] = { * */ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; @@ -1634,7 +1897,7 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; @@ -1667,13 +1930,13 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC_GET_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, - ep, - data, - sizeof(data)); + usb_rcvctrlpipe(chip->dev, 0), + UAC_GET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, + ep, + data, + sizeof(data)); if (err < 0) goto end; @@ -1700,26 +1963,26 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 2, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 2, + NULL, + 0); if (err < 0) goto end; reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20; reg |= (pval >> 12) & 0x0f; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 3, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 3, + NULL, + 0); if (err < 0) goto end; @@ -1729,13 +1992,14 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) } static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); unsigned int pval, pval_old; int err; - pval = pval_old = kcontrol->private_value; + pval = kcontrol->private_value; + pval_old = pval; pval &= 0xfffff0f0; pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; pval |= (ucontrol->value.iec958.status[0] & 0x0f); @@ -1756,7 +2020,7 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.iec958.status[0] = 0x0f; ucontrol->value.iec958.status[1] = 0xff; @@ -1767,7 +2031,7 @@ static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02); @@ -1785,20 +2049,20 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) return err; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 9, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 9, + NULL, + 0); snd_usb_unlock_shutdown(chip); return err; } static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); u8 reg; @@ -1883,9 +2147,9 @@ static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer, if (err < 0) return err; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0x0202, 3, buff, 2); + usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x0202, 3, buff, 2); snd_usb_unlock_shutdown(chip); return err; } @@ -2153,15 +2417,15 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) #define SND_RME_CLK_FREQMUL_SHIFT 18 #define SND_RME_CLK_FREQMUL_MASK 0x7 #define SND_RME_CLK_SYSTEM(x) \ - ((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK) + (((x) >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK) #define SND_RME_CLK_AES(x) \ - ((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) + (((x) >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) #define SND_RME_CLK_SPDIF(x) \ - ((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) + (((x) >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) #define SND_RME_CLK_SYNC(x) \ - ((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK) + (((x) >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK) #define SND_RME_CLK_FREQMUL(x) \ - ((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK) + (((x) >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK) #define SND_RME_CLK_AES_LOCK 0x1 #define SND_RME_CLK_AES_SYNC 0x4 #define SND_RME_CLK_SPDIF_LOCK 0x2 @@ -2170,9 +2434,9 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) #define SND_RME_SPDIF_FORMAT_SHIFT 5 #define SND_RME_BINARY_MASK 0x1 #define SND_RME_SPDIF_IF(x) \ - ((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK) + (((x) >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK) #define SND_RME_SPDIF_FORMAT(x) \ - ((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK) + (((x) >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK) static const u32 snd_rme_rate_table[] = { 32000, 44100, 48000, 50000, @@ -2181,6 +2445,7 @@ static const u32 snd_rme_rate_table[] = { 256000, 352800, 384000, 400000, 512000, 705600, 768000, 800000 }; + /* maximum number of items for AES and S/PDIF rates for above table */ #define SND_RME_RATE_IDX_AES_SPDIF_NUM 12 @@ -3235,7 +3500,7 @@ static int snd_rme_digiface_enum_put(struct snd_kcontrol *kcontrol, } static int snd_rme_digiface_current_sync_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { int ret = snd_rme_digiface_enum_get(kcontrol, ucontrol); @@ -3269,7 +3534,6 @@ static int snd_rme_digiface_sync_state_get(struct snd_kcontrol *kcontrol, return 0; } - static int snd_rme_digiface_format_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3281,7 +3545,6 @@ static int snd_rme_digiface_format_info(struct snd_kcontrol *kcontrol, ARRAY_SIZE(format), format); } - static int snd_rme_digiface_sync_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3564,7 +3827,6 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_A9_IDX 0x6 #define SND_DJM_V10_IDX 0x7 - #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ .name = _name, \ .options = snd_djm_opts_##suffix, \ @@ -3576,7 +3838,6 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) .controls = snd_djm_ctls_##suffix, \ .ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) } - struct snd_djm_device { const char *name; const struct snd_djm_ctl *controls; @@ -3722,7 +3983,6 @@ static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = { SND_DJM_CTL("Output 3 Playback Switch", 250mk2_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-450 static const u16 snd_djm_opts_450_cap1[] = { 0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a }; @@ -3747,7 +4007,6 @@ static const struct snd_djm_ctl snd_djm_ctls_450[] = { SND_DJM_CTL("Output 3 Playback Switch", 450_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-750 static const u16 snd_djm_opts_750_cap1[] = { 0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f }; @@ -3766,7 +4025,6 @@ static const struct snd_djm_ctl snd_djm_ctls_750[] = { SND_DJM_CTL("Input 4 Capture Switch", 750_cap4, 0, SND_DJM_WINDEX_CAP) }; - // DJM-850 static const u16 snd_djm_opts_850_cap1[] = { 0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f }; @@ -3785,7 +4043,6 @@ static const struct snd_djm_ctl snd_djm_ctls_850[] = { SND_DJM_CTL("Input 4 Capture Switch", 850_cap4, 1, SND_DJM_WINDEX_CAP) }; - // DJM-900NXS2 static const u16 snd_djm_opts_900nxs2_cap1[] = { 0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a }; @@ -3823,7 +4080,6 @@ static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 }; static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 }; static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 }; - static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL), SND_DJM_CTL("Input 1 Capture Switch", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP), @@ -3836,7 +4092,6 @@ static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { SND_DJM_CTL("Output 3 Playback Switch", 750mk2_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-A9 static const u16 snd_djm_opts_a9_cap_level[] = { 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 }; @@ -3865,29 +4120,35 @@ static const struct snd_djm_ctl snd_djm_ctls_a9[] = { static const u16 snd_djm_opts_v10_cap_level[] = { 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 }; + static const u16 snd_djm_opts_v10_cap1[] = { 0x0103, 0x0100, 0x0102, 0x0106, 0x0110, 0x0107, 0x0108, 0x0109, 0x010a, 0x0121, 0x0122 }; + static const u16 snd_djm_opts_v10_cap2[] = { 0x0200, 0x0202, 0x0206, 0x0210, 0x0207, 0x0208, 0x0209, 0x020a, 0x0221, 0x0222 }; + static const u16 snd_djm_opts_v10_cap3[] = { 0x0303, 0x0300, 0x0302, 0x0306, 0x0310, 0x0307, 0x0308, 0x0309, 0x030a, 0x0321, 0x0322 }; + static const u16 snd_djm_opts_v10_cap4[] = { 0x0403, 0x0400, 0x0402, 0x0406, 0x0410, 0x0407, 0x0408, 0x0409, 0x040a, 0x0421, 0x0422 }; + static const u16 snd_djm_opts_v10_cap5[] = { 0x0500, 0x0502, 0x0506, 0x0510, 0x0507, 0x0508, 0x0509, 0x050a, 0x0521, 0x0522 }; + static const u16 snd_djm_opts_v10_cap6[] = { 0x0603, 0x0600, 0x0602, 0x0606, 0x0610, 0x0607, @@ -3916,9 +4177,8 @@ static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_V10_IDX] = SND_DJM_DEVICE(v10), }; - static int snd_djm_controls_info(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *info) + struct snd_ctl_elem_info *info) { unsigned long private_value = kctl->private_value; u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT; @@ -3937,8 +4197,8 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl, info->value.enumerated.item = noptions - 1; name = snd_djm_get_label(device_idx, - ctl->options[info->value.enumerated.item], - ctl->wIndex); + ctl->options[info->value.enumerated.item], + ctl->wIndex); if (!name) return -EINVAL; @@ -3950,25 +4210,25 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl, } static int snd_djm_controls_update(struct usb_mixer_interface *mixer, - u8 device_idx, u8 group, u16 value) + u8 device_idx, u8 group, u16 value) { int err; const struct snd_djm_device *device = &snd_djm_devices[device_idx]; - if ((group >= device->ncontrols) || value >= device->controls[group].noptions) + if (group >= device->ncontrols || value >= device->controls[group].noptions) return -EINVAL; err = snd_usb_lock_shutdown(mixer->chip); if (err) return err; - err = snd_usb_ctl_msg( - mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), - USB_REQ_SET_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - device->controls[group].options[value], - device->controls[group].wIndex, - NULL, 0); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + USB_REQ_SET_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + device->controls[group].options[value], + device->controls[group].wIndex, + NULL, 0); snd_usb_unlock_shutdown(mixer->chip); return err; @@ -4009,7 +4269,7 @@ static int snd_djm_controls_resume(struct usb_mixer_elem_list *list) } static int snd_djm_controls_create(struct usb_mixer_interface *mixer, - const u8 device_idx) + const u8 device_idx) { int err, i; u16 value; @@ -4028,10 +4288,10 @@ static int snd_djm_controls_create(struct usb_mixer_interface *mixer, for (i = 0; i < device->ncontrols; i++) { value = device->controls[i].default_value; knew.name = device->controls[i].name; - knew.private_value = ( + knew.private_value = ((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) | (i << SND_DJM_GROUP_SHIFT) | - value); + value; err = snd_djm_controls_update(mixer, device_idx, i, value); if (err) return err; @@ -4073,6 +4333,13 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_emu0204_controls_create(mixer); break; +#if IS_REACHABLE(CONFIG_INPUT) + case USB_ID(0x054c, 0x0ce6): /* Sony DualSense controller (PS5) */ + case USB_ID(0x054c, 0x0df2): /* Sony DualSense Edge controller (PS5) */ + err = snd_dualsense_controls_create(mixer); + break; +#endif /* IS_REACHABLE(CONFIG_INPUT) */ + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */ err = snd_c400_create_mixer(mixer); @@ -4098,13 +4365,15 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) break; case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ - err = snd_nativeinstruments_create_mixer(mixer, + err = snd_nativeinstruments_create_mixer(/* checkpatch hack */ + mixer, snd_nativeinstruments_ta6_mixers, ARRAY_SIZE(snd_nativeinstruments_ta6_mixers)); break; case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */ - err = snd_nativeinstruments_create_mixer(mixer, + err = snd_nativeinstruments_create_mixer(/* checkpatch hack */ + mixer, snd_nativeinstruments_ta10_mixers, ARRAY_SIZE(snd_nativeinstruments_ta10_mixers)); break; @@ -4254,7 +4523,8 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl) { /* Approximation using 10 ranges based on output measurement on hw v1.2. - * This seems close to the cubic mapping e.g. alsamixer uses. */ + * This seems close to the cubic mapping e.g. alsamixer uses. + */ static const DECLARE_TLV_DB_RANGE(scale, 0, 1, TLV_DB_MINMAX_ITEM(-5300, -4970), 2, 5, TLV_DB_MINMAX_ITEM(-4710, -4160), @@ -4338,18 +4608,15 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, if (unitid == 7 && cval->control == UAC_FU_VOLUME) snd_dragonfly_quirk_db_scale(mixer, cval, kctl); break; + } + /* lowest playback value is muted on some devices */ - case USB_ID(0x0d8c, 0x000c): /* C-Media */ - case USB_ID(0x0d8c, 0x0014): /* C-Media */ - case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */ + if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_MIN_MUTE) if (strstr(kctl->id.name, "Playback")) cval->min_mute = 1; - break; - } /* ALSA-ify some Plantronics headset control names */ if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f && (cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME)) snd_fix_plt_name(mixer->chip, &kctl->id); } - diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index ff548041679b..8babfa3f7c45 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -357,21 +357,21 @@ static int scarlett_ctl_put(struct snd_kcontrol *kctl, return changed; } -static void scarlett_generate_name(int i, char *dst, int offsets[]) +static void scarlett_generate_name(int i, char *dst, size_t size, int offsets[]) { if (i > offsets[SCARLETT_OFFSET_MIX]) - sprintf(dst, "Mix %c", - 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); + scnprintf(dst, size, "Mix %c", + 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); else if (i > offsets[SCARLETT_OFFSET_ADAT]) - sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); + scnprintf(dst, size, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); else if (i > offsets[SCARLETT_OFFSET_SPDIF]) - sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); + scnprintf(dst, size, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); else if (i > offsets[SCARLETT_OFFSET_ANALOG]) - sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); + scnprintf(dst, size, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); else if (i > offsets[SCARLETT_OFFSET_PCM]) - sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); + scnprintf(dst, size, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); else - sprintf(dst, "Off"); + scnprintf(dst, size, "Off"); } static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, @@ -391,6 +391,7 @@ static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, /* generate name dynamically based on item number and offset info */ scarlett_generate_name(uinfo->value.enumerated.item, uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), opt->offsets); return 0; @@ -876,7 +877,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_IMPEDANCE: - sprintf(mx, "Input %d Impedance Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Impedance Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x09, ctl->num, USB_MIXER_S16, 1, mx, @@ -885,7 +887,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_PAD: - sprintf(mx, "Input %d Pad Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Pad Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x0b, ctl->num, USB_MIXER_S16, 1, mx, @@ -894,7 +897,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_GAIN: - sprintf(mx, "Input %d Gain Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Gain Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x08, ctl->num, USB_MIXER_S16, 1, mx, @@ -960,8 +964,9 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) return err; for (o = 0; o < info->matrix_out; o++) { - sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, - o+'A'); + scnprintf(mx, sizeof(mx), + "Matrix %02d Mix %c Playback Volume", i+1, + o+'A'); err = add_new_ctl(mixer, &usb_scarlett_ctl, scarlett_ctl_resume, 0x3c, 0x00, (i << 3) + (o & 0x07), USB_MIXER_S16, diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 93589e86828a..1ec203cbbd94 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2351,6 +2351,8 @@ static int scarlett2_usb( struct scarlett2_usb_packet *req, *resp = NULL; size_t req_buf_size = struct_size(req, data, req_size); size_t resp_buf_size = struct_size(resp, data, resp_size); + int retries = 0; + const int max_retries = 5; int err; req = kmalloc(req_buf_size, GFP_KERNEL); @@ -2374,10 +2376,15 @@ static int scarlett2_usb( if (req_size) memcpy(req->data, req_data, req_size); +retry: err = scarlett2_usb_tx(dev, private->bInterfaceNumber, req, req_buf_size); if (err != req_buf_size) { + if (err == -EPROTO && ++retries <= max_retries) { + msleep(5 * (1 << (retries - 1))); + goto retry; + } usb_audio_err( mixer->chip, "%s USB request result cmd %x was %d\n", @@ -3971,8 +3978,13 @@ static int scarlett2_input_select_ctl_info( goto unlock; /* Loop through each input */ - for (i = 0; i < inputs; i++) + for (i = 0; i < inputs; i++) { values[i] = kasprintf(GFP_KERNEL, "Input %d", i + 1); + if (!values[i]) { + err = -ENOMEM; + goto unlock; + } + } err = snd_ctl_enum_info(uinfo, 1, i, (const char * const *)values); @@ -7396,13 +7408,15 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, if (port_type == SCARLETT2_PORT_TYPE_MIX && item >= private->num_mix_out) - sprintf(uinfo->value.enumerated.name, - port->dsp_src_descr, - item - private->num_mix_out + 1); + scnprintf(uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), + port->dsp_src_descr, + item - private->num_mix_out + 1); else - sprintf(uinfo->value.enumerated.name, - port->src_descr, - item + port->src_num_offset); + scnprintf(uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), + port->src_descr, + item + port->src_num_offset); return 0; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b24ee38fad72..bff92505e408 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1336,11 +1336,10 @@ static void retire_capture_urb(struct snd_usb_substream *subs, for (i = 0; i < urb->number_of_packets; i++) { cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj; - if (urb->iso_frame_desc[i].status && printk_ratelimit()) { - dev_dbg(&subs->dev->dev, "frame %d active: %d\n", - i, urb->iso_frame_desc[i].status); - // continue; - } + if (urb->iso_frame_desc[i].status) + dev_dbg_ratelimited(&subs->dev->dev, + "frame %d active: %d\n", i, + urb->iso_frame_desc[i].status); bytes = urb->iso_frame_desc[i].actual_length; if (subs->stream_offset_adj > 0) { unsigned int adj = min(subs->stream_offset_adj, bytes); diff --git a/sound/usb/proc.c b/sound/usb/proc.c index e9bbaea7b2fa..c8b967bd7065 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -231,7 +231,7 @@ void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream) char name[32]; struct snd_card *card = stream->chip->card; - sprintf(name, "stream%d", stream->pcm_index); + scnprintf(name, sizeof(name), "stream%d", stream->pcm_index); snd_card_ro_proc_new(card, name, stream, proc_pcm_format_read); } diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 5bc27c82e0af..a25c5a531690 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -759,7 +759,7 @@ static void qmi_stop_session(void) subs = find_substream(pcm_card_num, info->pcm_dev_num, info->direction); if (!subs || !chip || atomic_read(&chip->shutdown)) { - dev_err(&subs->dev->dev, + dev_err(&uadev[idx].udev->dev, "no sub for c#%u dev#%u dir%u\n", info->pcm_card_num, info->pcm_dev_num, @@ -825,8 +825,8 @@ static int uaudio_sideband_notifier(struct usb_interface *intf, } } - mutex_unlock(&qdev_mutex); mutex_unlock(&chip->mutex); + mutex_unlock(&qdev_mutex); return 0; } @@ -1360,20 +1360,21 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, if (!uadev[card_num].ctrl_intf) { dev_err(&subs->dev->dev, "audio ctrl intf info not cached\n"); - ret = -ENODEV; - goto err; + return -ENODEV; } ret = uaudio_populate_uac_desc(subs, resp); if (ret < 0) - goto err; + return ret; resp->slot_id = subs->dev->slot_id; resp->slot_id_valid = 1; data = snd_soc_usb_find_priv_data(uaudio_qdev->auxdev->dev.parent); - if (!data) - goto err; + if (!data) { + dev_err(&subs->dev->dev, "No private data found\n"); + return -ENODEV; + } uaudio_qdev->data = data; @@ -1382,7 +1383,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, &resp->xhci_mem_info.tr_data, &resp->std_as_data_ep_desc); if (ret < 0) - goto err; + return ret; resp->std_as_data_ep_desc_valid = 1; @@ -1500,7 +1501,6 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, xhci_sideband_remove_endpoint(uadev[card_num].sb, usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe)); -err: return ret; } @@ -1865,8 +1865,8 @@ static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip) /* Device has already been cleaned up, or never populated */ if (!dev->chip) { - mutex_unlock(&qdev_mutex); mutex_unlock(&chip->mutex); + mutex_unlock(&qdev_mutex); return; } @@ -1921,8 +1921,8 @@ static void qc_usb_audio_offload_suspend(struct usb_interface *intf, uaudio_send_disconnect_ind(chip); - mutex_unlock(&qdev_mutex); mutex_unlock(&chip->mutex); + mutex_unlock(&qdev_mutex); } static struct snd_usb_platform_ops offload_ops = { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index bd24f3a78ea9..766db7d00cbc 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2199,6 +2199,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_SET_IFACE_FIRST), DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0572, 0x1b08, /* Conexant Systems (Rockwell), Inc. */ + QUIRK_FLAG_MIXER_MIN_MUTE), + DEVICE_FLG(0x0572, 0x1b09, /* Conexant Systems (Rockwell), Inc. */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x05a3, 0x9420, /* ELP HD USB Camera */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x05a7, 0x1020, /* Bose Companion 5 */ @@ -2241,12 +2245,16 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */ QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0bda, 0x498a, /* Realtek Semiconductor Corp. */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x0c45, 0x6340, /* Sonix HD USB Camera */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x0c45, 0x636b, /* Microdia JP001 USB Camera */ QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x0d8c, 0x0014, /* USB Audio Device */ - QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0d8c, 0x000c, /* C-Media */ + QUIRK_FLAG_MIXER_MIN_MUTE), + DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ @@ -2255,6 +2263,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ @@ -2293,6 +2303,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x19f7, 0x0003, /* RODE NT-USB */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */ @@ -2343,6 +2355,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */ QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */ @@ -2353,10 +2367,14 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x2d95, 0x8021, /* VIVO USB-C-XE710 HEADSET */ QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x2fc6, 0xf0b7, /* iBasso DC07 Pro */ QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */ QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */ + QUIRK_FLAG_MIXER_MIN_MUTE), DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */ @@ -2408,6 +2426,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x2d87, /* Cayin device */ QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x2fc6, /* Comture-inc devices */ + QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x3336, /* HEM devices */ QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x3353, /* Khadas devices */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index c1ea8844a46f..5c235a5ba7e1 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -341,20 +341,28 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor len = le16_to_cpu(cluster->wLength); c = 0; - p += sizeof(struct uac3_cluster_header_descriptor); + p += sizeof(*cluster); + len -= sizeof(*cluster); - while (((p - (void *)cluster) < len) && (c < channels)) { + while (len > 0 && (c < channels)) { struct uac3_cluster_segment_descriptor *cs_desc = p; u16 cs_len; u8 cs_type; + if (len < sizeof(*cs_desc)) + break; cs_len = le16_to_cpu(cs_desc->wLength); + if (len < cs_len) + break; cs_type = cs_desc->bSegmentType; if (cs_type == UAC3_CHANNEL_INFORMATION) { struct uac3_cluster_information_segment_descriptor *is = p; unsigned char map; + if (cs_len < sizeof(*is)) + break; + /* * TODO: this conversion is not complete, update it * after adding UAC3 values to asound.h @@ -456,6 +464,7 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor chmap->map[c++] = map; } p += cs_len; + len -= cs_len; } if (channels < c) @@ -536,9 +545,10 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, pcm->private_free = snd_usb_audio_pcm_free; pcm->info_flags = 0; if (chip->pcm_devs > 0) - sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); + scnprintf(pcm->name, sizeof(pcm->name), "USB Audio #%d", + chip->pcm_devs); else - strcpy(pcm->name, "USB Audio"); + strscpy(pcm->name, "USB Audio"); snd_usb_init_substream(as, stream, fp, pd); @@ -880,7 +890,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, u64 badd_formats = 0; unsigned int num_channels; struct audioformat *fp; - u16 cluster_id, wLength; + u16 cluster_id, wLength, cluster_wLength; int clock = 0; int err; @@ -987,6 +997,8 @@ 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)) + return NULL; cluster = kzalloc(wLength, GFP_KERNEL); if (!cluster) return ERR_PTR(-ENOMEM); @@ -1008,6 +1020,16 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, return ERR_PTR(-EIO); } + cluster_wLength = le16_to_cpu(cluster->wLength); + if (cluster_wLength < sizeof(*cluster) || + cluster_wLength > wLength) { + dev_err(&dev->dev, + "%u:%d : invalid Cluster Descriptor size\n", + iface_no, altno); + kfree(cluster); + return ERR_PTR(-EIO); + } + num_channels = cluster->bNrChannels; chmap = convert_chmap_v3(cluster); kfree(cluster); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 158ec053dc44..1ef4d39978df 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -196,6 +196,9 @@ extern bool snd_usb_skip_validation; * for the given endpoint. * QUIRK_FLAG_MIC_RES_16 and QUIRK_FLAG_MIC_RES_384 * Set the fixed resolution for Mic Capture Volume (mostly for webcams) + * QUIRK_FLAG_MIXER_MIN_MUTE + * Set minimum volume control value as mute for devices where the lowest + * playback value represents muted state instead of minimum audible volume */ #define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0) @@ -222,5 +225,6 @@ extern bool snd_usb_skip_validation; #define QUIRK_FLAG_FIXED_RATE (1U << 21) #define QUIRK_FLAG_MIC_RES_16 (1U << 22) #define QUIRK_FLAG_MIC_RES_384 (1U << 23) +#define QUIRK_FLAG_MIXER_MIN_MUTE (1U << 24) #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 6bcf8b859ebb..2ace3ba46091 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -495,7 +495,7 @@ static int usx2y_create_card(struct usb_device *device, init_waitqueue_head(&US122L(card)->sk.sleep); US122L(card)->is_us144 = flags & US122L_FLAG_US144; INIT_LIST_HEAD(&US122L(card)->midi_list); - strcpy(card->driver, "USB "NAME_ALLCAPS""); + strscpy(card->driver, "USB "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", card->shortname, diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index 9fd6a86cc08e..4d7925184826 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -102,7 +102,7 @@ static int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw, } if (id < 0) return -ENODEV; - strcpy(info->id, type_ids[id]); + strscpy(info->id, type_ids[id]); info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code if (us428->chip_status & USX2Y_STAT_CHIP_INIT) info->chip_ready = 1; diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 5756ff3528a2..f34e78910200 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -382,7 +382,7 @@ static int usx2y_create_card(struct usb_device *device, init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head); mutex_init(&usx2y(card)->pcm_mutex); INIT_LIST_HEAD(&usx2y(card)->midi_list); - strcpy(card->driver, "USB "NAME_ALLCAPS""); + strscpy(card->driver, "USB "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", card->shortname, diff --git a/sound/usb/validate.c b/sound/usb/validate.c index 6fe206f6e911..a0d55b77c994 100644 --- a/sound/usb/validate.c +++ b/sound/usb/validate.c @@ -221,6 +221,17 @@ static bool validate_uac3_feature_unit(const void *p, return d->bLength >= sizeof(*d) + 4 + 2; } +static bool validate_uac3_power_domain_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac3_power_domain_descriptor *d = p; + + if (d->bLength < sizeof(*d)) + return false; + /* baEntities[] + wPDomainDescrStr */ + return d->bLength >= sizeof(*d) + d->bNrEntities + 2; +} + static bool validate_midi_out_jack(const void *p, const struct usb_desc_validator *v) { @@ -274,7 +285,7 @@ static const struct usb_desc_validator audio_validators[] = { /* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */ FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit), FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit), - FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit), + FUNC(UAC_VERSION_3, UAC3_FEATURE_UNIT, validate_uac3_feature_unit), /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */ FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit), FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit), @@ -285,6 +296,7 @@ static const struct usb_desc_validator audio_validators[] = { struct uac3_clock_multiplier_descriptor), /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */ /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */ + FUNC(UAC_VERSION_3, UAC3_POWER_DOMAIN, validate_uac3_power_domain_unit), { } /* terminator */ }; diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index fe5cb4139088..01f49555c5f6 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1102,7 +1102,6 @@ static int had_pcm_open(struct snd_pcm_substream *substream) return retval; error: - pm_runtime_mark_last_busy(intelhaddata->dev); pm_runtime_put_autosuspend(intelhaddata->dev); return retval; } @@ -1127,7 +1126,6 @@ static int had_pcm_close(struct snd_pcm_substream *substream) } spin_unlock_irq(&intelhaddata->had_spinlock); - pm_runtime_mark_last_busy(intelhaddata->dev); pm_runtime_put_autosuspend(intelhaddata->dev); return 0; } @@ -1589,7 +1587,6 @@ static void had_audio_wq(struct work_struct *work) } mutex_unlock(&ctx->mutex); - pm_runtime_mark_last_busy(ctx->dev); pm_runtime_put_autosuspend(ctx->dev); } @@ -1768,7 +1765,7 @@ static int __hdmi_lpe_audio_probe(struct platform_device *pdev) /* setup private data which can be retrieved when required */ pcm->private_data = ctx; pcm->info_flags = 0; - strscpy(pcm->name, card->shortname, strlen(card->shortname)); + strscpy(pcm->name, card->shortname, sizeof(pcm->name)); /* setup the ops for playback */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops); diff --git a/tools/accounting/Makefile b/tools/accounting/Makefile index 11def1ad046c..20bbd461515e 100644 --- a/tools/accounting/Makefile +++ b/tools/accounting/Makefile @@ -2,7 +2,7 @@ CC := $(CROSS_COMPILE)gcc CFLAGS := -I../../usr/include -PROGS := getdelays procacct +PROGS := getdelays procacct delaytop all: $(PROGS) diff --git a/tools/accounting/delaytop.c b/tools/accounting/delaytop.c new file mode 100644 index 000000000000..9afb1ffc00ba --- /dev/null +++ b/tools/accounting/delaytop.c @@ -0,0 +1,862 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * delaytop.c - system-wide delay monitoring tool. + * + * This tool provides real-time monitoring and statistics of + * system, container, and task-level delays, including CPU, + * memory, IO, and IRQ. It supports both interactive (top-like), + * and can output delay information for the whole system, specific + * containers (cgroups), or individual tasks (PIDs). + * + * Key features: + * - Collects per-task delay accounting statistics via taskstats. + * - Collects system-wide PSI information. + * - Supports sorting, filtering. + * - Supports both interactive (screen refresh). + * + * Copyright (C) Fan Yu, ZTE Corp. 2025 + * Copyright (C) Wang Yaxin, ZTE Corp. 2025 + * + * Compile with + * gcc -I/usr/src/linux/include delaytop.c -o delaytop + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PSI_CPU_SOME "/proc/pressure/cpu" +#define PSI_CPU_FULL "/proc/pressure/cpu" +#define PSI_MEMORY_SOME "/proc/pressure/memory" +#define PSI_MEMORY_FULL "/proc/pressure/memory" +#define PSI_IO_SOME "/proc/pressure/io" +#define PSI_IO_FULL "/proc/pressure/io" +#define PSI_IRQ_FULL "/proc/pressure/irq" + +#define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len))) +#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) +#define NLA_PAYLOAD(len) (len - NLA_HDRLEN) + +#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) +#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) + +#define TASK_COMM_LEN 16 +#define MAX_MSG_SIZE 1024 +#define MAX_TASKS 1000 +#define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field +#define BOOL_FPRINT(stream, fmt, ...) \ +({ \ + int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ + ret >= 0; \ +}) +#define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" + +/* Program settings structure */ +struct config { + int delay; /* Update interval in seconds */ + int iterations; /* Number of iterations, 0 == infinite */ + int max_processes; /* Maximum number of processes to show */ + char sort_field; /* Field to sort by */ + int output_one_time; /* Output once and exit */ + int monitor_pid; /* Monitor specific PID */ + char *container_path; /* Path to container cgroup */ +}; + +/* PSI statistics structure */ +struct psi_stats { + double cpu_some_avg10, cpu_some_avg60, cpu_some_avg300; + unsigned long long cpu_some_total; + double cpu_full_avg10, cpu_full_avg60, cpu_full_avg300; + unsigned long long cpu_full_total; + double memory_some_avg10, memory_some_avg60, memory_some_avg300; + unsigned long long memory_some_total; + double memory_full_avg10, memory_full_avg60, memory_full_avg300; + unsigned long long memory_full_total; + double io_some_avg10, io_some_avg60, io_some_avg300; + unsigned long long io_some_total; + double io_full_avg10, io_full_avg60, io_full_avg300; + unsigned long long io_full_total; + double irq_full_avg10, irq_full_avg60, irq_full_avg300; + unsigned long long irq_full_total; +}; + +/* Task delay information structure */ +struct task_info { + int pid; + int tgid; + char command[TASK_COMM_LEN]; + unsigned long long cpu_count; + unsigned long long cpu_delay_total; + unsigned long long blkio_count; + unsigned long long blkio_delay_total; + unsigned long long swapin_count; + unsigned long long swapin_delay_total; + unsigned long long freepages_count; + unsigned long long freepages_delay_total; + unsigned long long thrashing_count; + unsigned long long thrashing_delay_total; + unsigned long long compact_count; + unsigned long long compact_delay_total; + unsigned long long wpcopy_count; + unsigned long long wpcopy_delay_total; + unsigned long long irq_count; + unsigned long long irq_delay_total; +}; + +/* Container statistics structure */ +struct container_stats { + int nr_sleeping; /* Number of sleeping processes */ + int nr_running; /* Number of running processes */ + int nr_stopped; /* Number of stopped processes */ + int nr_uninterruptible; /* Number of uninterruptible processes */ + int nr_io_wait; /* Number of processes in IO wait */ +}; + +/* Global variables */ +static struct config cfg; +static struct psi_stats psi; +static struct task_info tasks[MAX_TASKS]; +static int task_count; +static int running = 1; +static struct container_stats container_stats; + +/* Netlink socket variables */ +static int nl_sd = -1; +static int family_id; + +/* Set terminal to non-canonical mode for q-to-quit */ +static struct termios orig_termios; +static void enable_raw_mode(void) +{ + struct termios raw; + + tcgetattr(STDIN_FILENO, &orig_termios); + raw = orig_termios; + raw.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); +} +static void disable_raw_mode(void) +{ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); +} + +/* Display usage information and command line options */ +static void usage(void) +{ + printf("Usage: delaytop [Options]\n" + "Options:\n" + " -h, --help Show this help message and exit\n" + " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" + " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" + " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" + " -o, --once Display once and exit\n" + " -p, --pid=PID Monitor only the specified PID\n" + " -C, --container=PATH Monitor the container at specified cgroup path\n"); + exit(0); +} + +/* Parse command line arguments and set configuration */ +static void parse_args(int argc, char **argv) +{ + int c; + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"delay", required_argument, 0, 'd'}, + {"iterations", required_argument, 0, 'n'}, + {"pid", required_argument, 0, 'p'}, + {"once", no_argument, 0, 'o'}, + {"processes", required_argument, 0, 'P'}, + {"container", required_argument, 0, 'C'}, + {0, 0, 0, 0} + }; + + /* Set defaults */ + cfg.delay = 2; + cfg.iterations = 0; + cfg.max_processes = 20; + cfg.sort_field = 'c'; /* Default sort by CPU delay */ + cfg.output_one_time = 0; + cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ + cfg.container_path = NULL; + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "hd:n:p:oP:C:", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(); + break; + case 'd': + cfg.delay = atoi(optarg); + if (cfg.delay < 1) { + fprintf(stderr, "Error: delay must be >= 1.\n"); + exit(1); + } + break; + case 'n': + cfg.iterations = atoi(optarg); + if (cfg.iterations < 0) { + fprintf(stderr, "Error: iterations must be >= 0.\n"); + exit(1); + } + break; + case 'p': + cfg.monitor_pid = atoi(optarg); + if (cfg.monitor_pid < 1) { + fprintf(stderr, "Error: pid must be >= 1.\n"); + exit(1); + } + break; + case 'o': + cfg.output_one_time = 1; + break; + case 'P': + cfg.max_processes = atoi(optarg); + if (cfg.max_processes < 1) { + fprintf(stderr, "Error: processes must be >= 1.\n"); + exit(1); + } + if (cfg.max_processes > MAX_TASKS) { + fprintf(stderr, "Warning: processes capped to %d.\n", + MAX_TASKS); + cfg.max_processes = MAX_TASKS; + } + break; + case 'C': + cfg.container_path = strdup(optarg); + break; + default: + fprintf(stderr, "Try 'delaytop --help' for more information.\n"); + exit(1); + } + } +} + +/* Create a raw netlink socket and bind */ +static int create_nl_socket(void) +{ + int fd; + struct sockaddr_nl local; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd < 0) + return -1; + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) { + fprintf(stderr, "Failed to bind socket when create nl_socket\n"); + close(fd); + return -1; + } + + return fd; +} + +/* Send a command via netlink */ +static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, + __u8 genl_cmd, __u16 nla_type, + void *nla_data, int nla_len) +{ + struct sockaddr_nl nladdr; + struct nlattr *na; + int r, buflen; + char *buf; + + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; + } msg; + + msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + msg.n.nlmsg_type = nlmsg_type; + msg.n.nlmsg_flags = NLM_F_REQUEST; + msg.n.nlmsg_seq = 0; + msg.n.nlmsg_pid = nlmsg_pid; + msg.g.cmd = genl_cmd; + msg.g.version = 0x1; + na = (struct nlattr *) GENLMSG_DATA(&msg); + na->nla_type = nla_type; + na->nla_len = nla_len + NLA_HDRLEN; + memcpy(NLA_DATA(na), nla_data, nla_len); + msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); + + buf = (char *) &msg; + buflen = msg.n.nlmsg_len; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, + sizeof(nladdr))) < buflen) { + if (r > 0) { + buf += r; + buflen -= r; + } else if (errno != EAGAIN) + return -1; + } + return 0; +} + +/* Get family ID for taskstats via netlink */ +static int get_family_id(int sd) +{ + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[256]; + } ans; + + int id = 0, rc; + struct nlattr *na; + int rep_len; + char name[100]; + + strncpy(name, TASKSTATS_GENL_NAME, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, + CTRL_ATTR_FAMILY_NAME, (void *)name, + strlen(TASKSTATS_GENL_NAME)+1); + if (rc < 0) { + fprintf(stderr, "Failed to send cmd for family id\n"); + return 0; + } + + rep_len = recv(sd, &ans, sizeof(ans), 0); + if (ans.n.nlmsg_type == NLMSG_ERROR || + (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) { + fprintf(stderr, "Failed to receive response for family id\n"); + return 0; + } + + na = (struct nlattr *) GENLMSG_DATA(&ans); + na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); + if (na->nla_type == CTRL_ATTR_FAMILY_ID) + id = *(__u16 *) NLA_DATA(na); + return id; +} + +static void read_psi_stats(void) +{ + FILE *fp; + char line[256]; + int ret = 0; + /* Zero all fields */ + memset(&psi, 0, sizeof(psi)); + /* CPU pressure */ + fp = fopen(PSI_CPU_SOME, "r"); + if (fp) { + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "some", 4) == 0) { + ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.cpu_some_avg10, &psi.cpu_some_avg60, + &psi.cpu_some_avg300, &psi.cpu_some_total); + if (ret != 4) + fprintf(stderr, "Failed to parse CPU some PSI data\n"); + } else if (strncmp(line, "full", 4) == 0) { + ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.cpu_full_avg10, &psi.cpu_full_avg60, + &psi.cpu_full_avg300, &psi.cpu_full_total); + if (ret != 4) + fprintf(stderr, "Failed to parse CPU full PSI data\n"); + } + } + fclose(fp); + } + /* Memory pressure */ + fp = fopen(PSI_MEMORY_SOME, "r"); + if (fp) { + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "some", 4) == 0) { + ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.memory_some_avg10, &psi.memory_some_avg60, + &psi.memory_some_avg300, &psi.memory_some_total); + if (ret != 4) + fprintf(stderr, "Failed to parse Memory some PSI data\n"); + } else if (strncmp(line, "full", 4) == 0) { + ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.memory_full_avg10, &psi.memory_full_avg60, + &psi.memory_full_avg300, &psi.memory_full_total); + } + if (ret != 4) + fprintf(stderr, "Failed to parse Memory full PSI data\n"); + } + fclose(fp); + } + /* IO pressure */ + fp = fopen(PSI_IO_SOME, "r"); + if (fp) { + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "some", 4) == 0) { + ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.io_some_avg10, &psi.io_some_avg60, + &psi.io_some_avg300, &psi.io_some_total); + if (ret != 4) + fprintf(stderr, "Failed to parse IO some PSI data\n"); + } else if (strncmp(line, "full", 4) == 0) { + ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.io_full_avg10, &psi.io_full_avg60, + &psi.io_full_avg300, &psi.io_full_total); + if (ret != 4) + fprintf(stderr, "Failed to parse IO full PSI data\n"); + } + } + fclose(fp); + } + /* IRQ pressure (only full) */ + fp = fopen(PSI_IRQ_FULL, "r"); + if (fp) { + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "full", 4) == 0) { + ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", + &psi.irq_full_avg10, &psi.irq_full_avg60, + &psi.irq_full_avg300, &psi.irq_full_total); + if (ret != 4) + fprintf(stderr, "Failed to parse IRQ full PSI data\n"); + } + } + fclose(fp); + } +} + +static int read_comm(int pid, char *comm_buf, size_t buf_size) +{ + char path[64]; + int ret = -1; + size_t len; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", pid); + fp = fopen(path, "r"); + if (!fp) { + fprintf(stderr, "Failed to open comm file /proc/%d/comm\n", pid); + return ret; + } + + if (fgets(comm_buf, buf_size, fp)) { + len = strlen(comm_buf); + if (len > 0 && comm_buf[len - 1] == '\n') + comm_buf[len - 1] = '\0'; + ret = 0; + } + + fclose(fp); + + return ret; +} + +static void fetch_and_fill_task_info(int pid, const char *comm) +{ + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; + } resp; + struct taskstats stats; + struct nlattr *nested; + struct nlattr *na; + int nested_len; + int nl_len; + int rc; + + /* Send request for task stats */ + if (send_cmd(nl_sd, family_id, getpid(), TASKSTATS_CMD_GET, + TASKSTATS_CMD_ATTR_PID, &pid, sizeof(pid)) < 0) { + fprintf(stderr, "Failed to send request for task stats\n"); + return; + } + + /* Receive response */ + rc = recv(nl_sd, &resp, sizeof(resp), 0); + if (rc < 0 || resp.n.nlmsg_type == NLMSG_ERROR) { + fprintf(stderr, "Failed to receive response for task stats\n"); + return; + } + + /* Parse response */ + nl_len = GENLMSG_PAYLOAD(&resp.n); + na = (struct nlattr *) GENLMSG_DATA(&resp); + while (nl_len > 0) { + if (na->nla_type == TASKSTATS_TYPE_AGGR_PID) { + nested = (struct nlattr *) NLA_DATA(na); + nested_len = NLA_PAYLOAD(na->nla_len); + while (nested_len > 0) { + if (nested->nla_type == TASKSTATS_TYPE_STATS) { + memcpy(&stats, NLA_DATA(nested), sizeof(stats)); + if (task_count < MAX_TASKS) { + tasks[task_count].pid = pid; + tasks[task_count].tgid = pid; + strncpy(tasks[task_count].command, comm, + TASK_COMM_LEN - 1); + tasks[task_count].command[TASK_COMM_LEN - 1] = '\0'; + SET_TASK_STAT(task_count, cpu_count); + SET_TASK_STAT(task_count, cpu_delay_total); + SET_TASK_STAT(task_count, blkio_count); + SET_TASK_STAT(task_count, blkio_delay_total); + SET_TASK_STAT(task_count, swapin_count); + SET_TASK_STAT(task_count, swapin_delay_total); + SET_TASK_STAT(task_count, freepages_count); + SET_TASK_STAT(task_count, freepages_delay_total); + SET_TASK_STAT(task_count, thrashing_count); + SET_TASK_STAT(task_count, thrashing_delay_total); + SET_TASK_STAT(task_count, compact_count); + SET_TASK_STAT(task_count, compact_delay_total); + SET_TASK_STAT(task_count, wpcopy_count); + SET_TASK_STAT(task_count, wpcopy_delay_total); + SET_TASK_STAT(task_count, irq_count); + SET_TASK_STAT(task_count, irq_delay_total); + task_count++; + } + break; + } + nested_len -= NLA_ALIGN(nested->nla_len); + nested = NLA_NEXT(nested); + } + } + nl_len -= NLA_ALIGN(na->nla_len); + na = NLA_NEXT(na); + } + return; +} + +static void get_task_delays(void) +{ + char comm[TASK_COMM_LEN]; + struct dirent *entry; + DIR *dir; + int pid; + + task_count = 0; + if (cfg.monitor_pid > 0) { + if (read_comm(cfg.monitor_pid, comm, sizeof(comm)) == 0) + fetch_and_fill_task_info(cfg.monitor_pid, comm); + return; + } + + dir = opendir("/proc"); + if (!dir) { + fprintf(stderr, "Error opening /proc directory\n"); + return; + } + + while ((entry = readdir(dir)) != NULL && task_count < MAX_TASKS) { + if (!isdigit(entry->d_name[0])) + continue; + pid = atoi(entry->d_name); + if (pid == 0) + continue; + if (read_comm(pid, comm, sizeof(comm)) != 0) + continue; + fetch_and_fill_task_info(pid, comm); + } + closedir(dir); +} + +/* Calculate average delay in milliseconds */ +static double average_ms(unsigned long long total, unsigned long long count) +{ + if (count == 0) + return 0; + return (double)total / 1000000.0 / count; +} + +/* Comparison function for sorting tasks */ +static int compare_tasks(const void *a, const void *b) +{ + const struct task_info *t1 = (const struct task_info *)a; + const struct task_info *t2 = (const struct task_info *)b; + double avg1, avg2; + + switch (cfg.sort_field) { + case 'c': /* CPU */ + avg1 = average_ms(t1->cpu_delay_total, t1->cpu_count); + avg2 = average_ms(t2->cpu_delay_total, t2->cpu_count); + if (avg1 != avg2) + return avg2 > avg1 ? 1 : -1; + return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; + + default: + return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; + } +} + +/* Sort tasks by selected field */ +static void sort_tasks(void) +{ + if (task_count > 0) + qsort(tasks, task_count, sizeof(struct task_info), compare_tasks); +} + +/* Get container statistics via cgroupstats */ +static void get_container_stats(void) +{ + int rc, cfd; + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; + } req, resp; + struct nlattr *na; + int nl_len; + struct cgroupstats stats; + + /* Check if container path is set */ + if (!cfg.container_path) + return; + + /* Open container cgroup */ + cfd = open(cfg.container_path, O_RDONLY); + if (cfd < 0) { + fprintf(stderr, "Error opening container path: %s\n", cfg.container_path); + return; + } + + /* Send request for container stats */ + if (send_cmd(nl_sd, family_id, getpid(), CGROUPSTATS_CMD_GET, + CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32)) < 0) { + fprintf(stderr, "Failed to send request for container stats\n"); + close(cfd); + return; + } + + /* Receive response */ + rc = recv(nl_sd, &resp, sizeof(resp), 0); + if (rc < 0 || resp.n.nlmsg_type == NLMSG_ERROR) { + fprintf(stderr, "Failed to receive response for container stats\n"); + close(cfd); + return; + } + + /* Parse response */ + nl_len = GENLMSG_PAYLOAD(&resp.n); + na = (struct nlattr *) GENLMSG_DATA(&resp); + while (nl_len > 0) { + if (na->nla_type == CGROUPSTATS_TYPE_CGROUP_STATS) { + /* Get the cgroupstats structure */ + memcpy(&stats, NLA_DATA(na), sizeof(stats)); + + /* Fill container stats */ + container_stats.nr_sleeping = stats.nr_sleeping; + container_stats.nr_running = stats.nr_running; + container_stats.nr_stopped = stats.nr_stopped; + container_stats.nr_uninterruptible = stats.nr_uninterruptible; + container_stats.nr_io_wait = stats.nr_io_wait; + break; + } + nl_len -= NLA_ALIGN(na->nla_len); + na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); + } + + close(cfd); +} + +/* Display results to stdout or log file */ +static void display_results(void) +{ + time_t now = time(NULL); + struct tm *tm_now = localtime(&now); + FILE *out = stdout; + char timestamp[32]; + bool suc = true; + int i, count; + + /* Clear terminal screen */ + suc &= BOOL_FPRINT(out, "\033[H\033[J"); + + /* PSI output (one-line, no cat style) */ + suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60/avg300/total)\n"); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "CPU some:", + psi.cpu_some_avg10, + psi.cpu_some_avg60, + psi.cpu_some_avg300, + psi.cpu_some_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "CPU full:", + psi.cpu_full_avg10, + psi.cpu_full_avg60, + psi.cpu_full_avg300, + psi.cpu_full_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "Memory full:", + psi.memory_full_avg10, + psi.memory_full_avg60, + psi.memory_full_avg300, + psi.memory_full_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "Memory some:", + psi.memory_some_avg10, + psi.memory_some_avg60, + psi.memory_some_avg300, + psi.memory_some_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "IO full:", + psi.io_full_avg10, + psi.io_full_avg60, + psi.io_full_avg300, + psi.io_full_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "IO some:", + psi.io_some_avg10, + psi.io_some_avg60, + psi.io_some_avg300, + psi.io_some_total / 1000); + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, + "IRQ full:", + psi.irq_full_avg10, + psi.irq_full_avg60, + psi.irq_full_avg300, + psi.irq_full_total / 1000); + + if (cfg.container_path) { + suc &= BOOL_FPRINT(out, "Container Information (%s):\n", cfg.container_path); + suc &= BOOL_FPRINT(out, "Processes: running=%d, sleeping=%d, ", + container_stats.nr_running, container_stats.nr_sleeping); + suc &= BOOL_FPRINT(out, "stopped=%d, uninterruptible=%d, io_wait=%d\n\n", + container_stats.nr_stopped, container_stats.nr_uninterruptible, + container_stats.nr_io_wait); + } + suc &= BOOL_FPRINT(out, "Top %d processes (sorted by CPU delay):\n", + cfg.max_processes); + suc &= BOOL_FPRINT(out, "%5s %5s %-17s", "PID", "TGID", "COMMAND"); + suc &= BOOL_FPRINT(out, "%7s %7s %7s %7s %7s %7s %7s %7s\n", + "CPU(ms)", "IO(ms)", "SWAP(ms)", "RCL(ms)", + "THR(ms)", "CMP(ms)", "WP(ms)", "IRQ(ms)"); + + suc &= BOOL_FPRINT(out, "-----------------------------------------------"); + suc &= BOOL_FPRINT(out, "----------------------------------------------\n"); + count = task_count < cfg.max_processes ? task_count : cfg.max_processes; + + for (i = 0; i < count; i++) { + suc &= BOOL_FPRINT(out, "%5d %5d %-15s", + tasks[i].pid, tasks[i].tgid, tasks[i].command); + suc &= BOOL_FPRINT(out, "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", + average_ms(tasks[i].cpu_delay_total, tasks[i].cpu_count), + average_ms(tasks[i].blkio_delay_total, tasks[i].blkio_count), + average_ms(tasks[i].swapin_delay_total, tasks[i].swapin_count), + average_ms(tasks[i].freepages_delay_total, tasks[i].freepages_count), + average_ms(tasks[i].thrashing_delay_total, tasks[i].thrashing_count), + average_ms(tasks[i].compact_delay_total, tasks[i].compact_count), + average_ms(tasks[i].wpcopy_delay_total, tasks[i].wpcopy_count), + average_ms(tasks[i].irq_delay_total, tasks[i].irq_count)); + } + + suc &= BOOL_FPRINT(out, "\n"); + + if (!suc) + perror("Error writing to output"); +} + +/* Main function */ +int main(int argc, char **argv) +{ + int iterations = 0; + int use_q_quit = 0; + + /* Parse command line arguments */ + parse_args(argc, argv); + + /* Setup netlink socket */ + nl_sd = create_nl_socket(); + if (nl_sd < 0) { + fprintf(stderr, "Error creating netlink socket\n"); + exit(1); + } + + /* Get family ID for taskstats via netlink */ + family_id = get_family_id(nl_sd); + if (!family_id) { + fprintf(stderr, "Error getting taskstats family ID\n"); + close(nl_sd); + exit(1); + } + + if (!cfg.output_one_time) { + use_q_quit = 1; + enable_raw_mode(); + printf("Press 'q' to quit.\n"); + fflush(stdout); + } + + /* Main loop */ + while (running) { + /* Read PSI statistics */ + read_psi_stats(); + + /* Get container stats if container path provided */ + if (cfg.container_path) + get_container_stats(); + + /* Get task delays */ + get_task_delays(); + + /* Sort tasks */ + sort_tasks(); + + /* Display results to stdout or log file */ + display_results(); + + /* Check for iterations */ + if (cfg.iterations > 0 && ++iterations >= cfg.iterations) + break; + + /* Exit if output_one_time is set */ + if (cfg.output_one_time) + break; + + /* Check for 'q' key to quit */ + if (use_q_quit) { + struct timeval tv = {cfg.delay, 0}; + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + int r = select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv); + + if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { + char ch = 0; + + read(STDIN_FILENO, &ch, 1); + if (ch == 'q' || ch == 'Q') { + running = 0; + break; + } + } + } else { + sleep(cfg.delay); + } + } + + /* Restore terminal mode */ + if (use_q_quit) + disable_raw_mode(); + + /* Cleanup */ + close(nl_sd); + if (cfg.container_path) + free(cfg.container_path); + + return 0; +} diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c index 3feac0482fe9..21cb3c3d1331 100644 --- a/tools/accounting/getdelays.c +++ b/tools/accounting/getdelays.c @@ -194,75 +194,108 @@ static int get_family_id(int sd) #define average_ms(t, c) (t / 1000000ULL / (c ? c : 1)) #define delay_ms(t) (t / 1000000ULL) +/* + * Version compatibility note: + * Field availability depends on taskstats version (t->version), + * corresponding to TASKSTATS_VERSION in kernel headers + * see include/uapi/linux/taskstats.h + * + * Version feature mapping: + * version >= 11 - supports COMPACT statistics + * version >= 13 - supports WPCOPY statistics + * version >= 14 - supports IRQ statistics + * version >= 16 - supports *_max and *_min delay statistics + * + * Always verify version before accessing version-dependent fields + * to maintain backward compatibility. + */ +#define PRINT_CPU_DELAY(version, t) \ + do { \ + if (version >= 16) { \ + printf("%-10s%15s%15s%15s%15s%15s%15s%15s\n", \ + "CPU", "count", "real total", "virtual total", \ + "delay total", "delay average", "delay max", "delay min"); \ + printf(" %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ + (unsigned long long)(t)->cpu_count, \ + (unsigned long long)(t)->cpu_run_real_total, \ + (unsigned long long)(t)->cpu_run_virtual_total, \ + (unsigned long long)(t)->cpu_delay_total, \ + average_ms((double)(t)->cpu_delay_total, (t)->cpu_count), \ + delay_ms((double)(t)->cpu_delay_max), \ + delay_ms((double)(t)->cpu_delay_min)); \ + } else { \ + printf("%-10s%15s%15s%15s%15s%15s\n", \ + "CPU", "count", "real total", "virtual total", \ + "delay total", "delay average"); \ + printf(" %15llu%15llu%15llu%15llu%15.3fms\n", \ + (unsigned long long)(t)->cpu_count, \ + (unsigned long long)(t)->cpu_run_real_total, \ + (unsigned long long)(t)->cpu_run_virtual_total, \ + (unsigned long long)(t)->cpu_delay_total, \ + average_ms((double)(t)->cpu_delay_total, (t)->cpu_count)); \ + } \ + } while (0) +#define PRINT_FILED_DELAY(name, version, t, count, total, max, min) \ + do { \ + if (version >= 16) { \ + printf("%-10s%15s%15s%15s%15s%15s\n", \ + name, "count", "delay total", "delay average", \ + "delay max", "delay min"); \ + printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ + (unsigned long long)(t)->count, \ + (unsigned long long)(t)->total, \ + average_ms((double)(t)->total, (t)->count), \ + delay_ms((double)(t)->max), \ + delay_ms((double)(t)->min)); \ + } else { \ + printf("%-10s%15s%15s%15s\n", \ + name, "count", "delay total", "delay average"); \ + printf(" %15llu%15llu%15.3fms\n", \ + (unsigned long long)(t)->count, \ + (unsigned long long)(t)->total, \ + average_ms((double)(t)->total, (t)->count)); \ + } \ + } while (0) + static void print_delayacct(struct taskstats *t) { - printf("\n\nCPU %15s%15s%15s%15s%15s%15s%15s\n" - " %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "IO %15s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "SWAP %15s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "RECLAIM %12s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "THRASHING%12s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "COMPACT %12s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "WPCOPY %12s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n" - "IRQ %15s%15s%15s%15s%15s\n" - " %15llu%15llu%15.3fms%13.6fms%13.6fms\n", - "count", "real total", "virtual total", - "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->cpu_count, - (unsigned long long)t->cpu_run_real_total, - (unsigned long long)t->cpu_run_virtual_total, - (unsigned long long)t->cpu_delay_total, - average_ms((double)t->cpu_delay_total, t->cpu_count), - delay_ms((double)t->cpu_delay_max), - delay_ms((double)t->cpu_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->blkio_count, - (unsigned long long)t->blkio_delay_total, - average_ms((double)t->blkio_delay_total, t->blkio_count), - delay_ms((double)t->blkio_delay_max), - delay_ms((double)t->blkio_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->swapin_count, - (unsigned long long)t->swapin_delay_total, - average_ms((double)t->swapin_delay_total, t->swapin_count), - delay_ms((double)t->swapin_delay_max), - delay_ms((double)t->swapin_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->freepages_count, - (unsigned long long)t->freepages_delay_total, - average_ms((double)t->freepages_delay_total, t->freepages_count), - delay_ms((double)t->freepages_delay_max), - delay_ms((double)t->freepages_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->thrashing_count, - (unsigned long long)t->thrashing_delay_total, - average_ms((double)t->thrashing_delay_total, t->thrashing_count), - delay_ms((double)t->thrashing_delay_max), - delay_ms((double)t->thrashing_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->compact_count, - (unsigned long long)t->compact_delay_total, - average_ms((double)t->compact_delay_total, t->compact_count), - delay_ms((double)t->compact_delay_max), - delay_ms((double)t->compact_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->wpcopy_count, - (unsigned long long)t->wpcopy_delay_total, - average_ms((double)t->wpcopy_delay_total, t->wpcopy_count), - delay_ms((double)t->wpcopy_delay_max), - delay_ms((double)t->wpcopy_delay_min), - "count", "delay total", "delay average", "delay max", "delay min", - (unsigned long long)t->irq_count, - (unsigned long long)t->irq_delay_total, - average_ms((double)t->irq_delay_total, t->irq_count), - delay_ms((double)t->irq_delay_max), - delay_ms((double)t->irq_delay_min)); + printf("\n\n"); + + PRINT_CPU_DELAY(t->version, t); + + PRINT_FILED_DELAY("IO", t->version, t, + blkio_count, blkio_delay_total, + blkio_delay_max, blkio_delay_min); + + PRINT_FILED_DELAY("SWAP", t->version, t, + swapin_count, swapin_delay_total, + swapin_delay_max, swapin_delay_min); + + PRINT_FILED_DELAY("RECLAIM", t->version, t, + freepages_count, freepages_delay_total, + freepages_delay_max, freepages_delay_min); + + PRINT_FILED_DELAY("THRASHING", t->version, t, + thrashing_count, thrashing_delay_total, + thrashing_delay_max, thrashing_delay_min); + + if (t->version >= 11) { + PRINT_FILED_DELAY("COMPACT", t->version, t, + compact_count, compact_delay_total, + compact_delay_max, compact_delay_min); + } + + if (t->version >= 13) { + PRINT_FILED_DELAY("WPCOPY", t->version, t, + wpcopy_count, wpcopy_delay_total, + wpcopy_delay_max, wpcopy_delay_min); + } + + if (t->version >= 14) { + PRINT_FILED_DELAY("IRQ", t->version, t, + irq_count, irq_delay_total, + irq_delay_max, irq_delay_min); + } } static void task_context_switch_counts(struct taskstats *t) diff --git a/tools/arch/arm64/include/asm/cputype.h b/tools/arch/arm64/include/asm/cputype.h index 9a5d85cfd1fb..139d5e87dc95 100644 --- a/tools/arch/arm64/include/asm/cputype.h +++ b/tools/arch/arm64/include/asm/cputype.h @@ -75,11 +75,13 @@ #define ARM_CPU_PART_CORTEX_A76 0xD0B #define ARM_CPU_PART_NEOVERSE_N1 0xD0C #define ARM_CPU_PART_CORTEX_A77 0xD0D +#define ARM_CPU_PART_CORTEX_A76AE 0xD0E #define ARM_CPU_PART_NEOVERSE_V1 0xD40 #define ARM_CPU_PART_CORTEX_A78 0xD41 #define ARM_CPU_PART_CORTEX_A78AE 0xD42 #define ARM_CPU_PART_CORTEX_X1 0xD44 #define ARM_CPU_PART_CORTEX_A510 0xD46 +#define ARM_CPU_PART_CORTEX_X1C 0xD4C #define ARM_CPU_PART_CORTEX_A520 0xD80 #define ARM_CPU_PART_CORTEX_A710 0xD47 #define ARM_CPU_PART_CORTEX_A715 0xD4D @@ -119,9 +121,11 @@ #define QCOM_CPU_PART_KRYO 0x200 #define QCOM_CPU_PART_KRYO_2XX_GOLD 0x800 #define QCOM_CPU_PART_KRYO_2XX_SILVER 0x801 +#define QCOM_CPU_PART_KRYO_3XX_GOLD 0x802 #define QCOM_CPU_PART_KRYO_3XX_SILVER 0x803 #define QCOM_CPU_PART_KRYO_4XX_GOLD 0x804 #define QCOM_CPU_PART_KRYO_4XX_SILVER 0x805 +#define QCOM_CPU_PART_ORYON_X1 0x001 #define NVIDIA_CPU_PART_DENVER 0x003 #define NVIDIA_CPU_PART_CARMEL 0x004 @@ -129,6 +133,7 @@ #define FUJITSU_CPU_PART_A64FX 0x001 #define HISI_CPU_PART_TSV110 0xD01 +#define HISI_CPU_PART_HIP09 0xD02 #define HISI_CPU_PART_HIP12 0xD06 #define APPLE_CPU_PART_M1_ICESTORM 0x022 @@ -159,11 +164,13 @@ #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) #define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) #define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) +#define MIDR_CORTEX_A76AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76AE) #define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) #define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) #define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE) #define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) #define MIDR_CORTEX_A510 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A510) +#define MIDR_CORTEX_X1C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1C) #define MIDR_CORTEX_A520 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A520) #define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) #define MIDR_CORTEX_A715 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A715) @@ -196,13 +203,26 @@ #define MIDR_QCOM_KRYO MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO) #define MIDR_QCOM_KRYO_2XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_2XX_GOLD) #define MIDR_QCOM_KRYO_2XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_2XX_SILVER) +#define MIDR_QCOM_KRYO_3XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_3XX_GOLD) #define MIDR_QCOM_KRYO_3XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_3XX_SILVER) #define MIDR_QCOM_KRYO_4XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_GOLD) #define MIDR_QCOM_KRYO_4XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_SILVER) +#define MIDR_QCOM_ORYON_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_ORYON_X1) + +/* + * NOTES: + * - Qualcomm Kryo 5XX Prime / Gold ID themselves as MIDR_CORTEX_A77 + * - Qualcomm Kryo 5XX Silver IDs itself as MIDR_QCOM_KRYO_4XX_SILVER + * - Qualcomm Kryo 6XX Prime IDs itself as MIDR_CORTEX_X1 + * - Qualcomm Kryo 6XX Gold IDs itself as ARM_CPU_PART_CORTEX_A78 + * - Qualcomm Kryo 6XX Silver IDs itself as MIDR_CORTEX_A55 + */ + #define MIDR_NVIDIA_DENVER MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_DENVER) #define MIDR_NVIDIA_CARMEL MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_CARMEL) #define MIDR_FUJITSU_A64FX MIDR_CPU_MODEL(ARM_CPU_IMP_FUJITSU, FUJITSU_CPU_PART_A64FX) #define MIDR_HISI_TSV110 MIDR_CPU_MODEL(ARM_CPU_IMP_HISI, HISI_CPU_PART_TSV110) +#define MIDR_HISI_HIP09 MIDR_CPU_MODEL(ARM_CPU_IMP_HISI, HISI_CPU_PART_HIP09) #define MIDR_HISI_HIP12 MIDR_CPU_MODEL(ARM_CPU_IMP_HISI, HISI_CPU_PART_HIP12) #define MIDR_APPLE_M1_ICESTORM MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM) #define MIDR_APPLE_M1_FIRESTORM MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM) @@ -291,6 +311,14 @@ static inline u32 __attribute_const__ read_cpuid_id(void) return read_cpuid(MIDR_EL1); } +struct target_impl_cpu { + u64 midr; + u64 revidr; + u64 aidr; +}; + +bool cpu_errata_set_target_impl(u64 num, void *impl_cpus); + static inline u64 __attribute_const__ read_cpuid_mpidr(void) { return read_cpuid(MPIDR_EL1); diff --git a/tools/arch/arm64/include/asm/sysreg.h b/tools/arch/arm64/include/asm/sysreg.h index 690b6ebd118f..65f2759ea27a 100644 --- a/tools/arch/arm64/include/asm/sysreg.h +++ b/tools/arch/arm64/include/asm/sysreg.h @@ -1080,9 +1080,6 @@ #define ARM64_FEATURE_FIELD_BITS 4 -/* Defined for compatibility only, do not add new users. */ -#define ARM64_FEATURE_MASK(x) (x##_MASK) - #ifdef __ASSEMBLY__ .macro mrs_s, rt, sreg diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index af9d9acaf997..ed5f3892674c 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -431,10 +431,11 @@ enum { /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 -#define KVM_ARM_VCPU_PMU_V3_IRQ 0 -#define KVM_ARM_VCPU_PMU_V3_INIT 1 -#define KVM_ARM_VCPU_PMU_V3_FILTER 2 -#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_IRQ 0 +#define KVM_ARM_VCPU_PMU_V3_INIT 1 +#define KVM_ARM_VCPU_PMU_V3_FILTER 2 +#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 diff --git a/tools/arch/loongarch/include/asm/orc_types.h b/tools/arch/loongarch/include/asm/orc_types.h index caf1f71a1057..d5fa98d1d177 100644 --- a/tools/arch/loongarch/include/asm/orc_types.h +++ b/tools/arch/loongarch/include/asm/orc_types.h @@ -34,7 +34,7 @@ #define ORC_TYPE_REGS 3 #define ORC_TYPE_REGS_PARTIAL 4 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* * This struct is more or less a vastly simplified version of the DWARF Call * Frame Information standard. It contains only the necessary parts of DWARF @@ -53,6 +53,6 @@ struct orc_entry { unsigned int type:3; unsigned int signal:1; }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* _ORC_TYPES_H */ diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h index eaeda001784e..077c5437f521 100644 --- a/tools/arch/powerpc/include/uapi/asm/kvm.h +++ b/tools/arch/powerpc/include/uapi/asm/kvm.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * 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. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2007 * * Authors: Hollis Blanchard diff --git a/tools/arch/x86/include/asm/amd/ibs.h b/tools/arch/x86/include/asm/amd/ibs.h index 300b6e0765b2..cbce54fec7b9 100644 --- a/tools/arch/x86/include/asm/amd/ibs.h +++ b/tools/arch/x86/include/asm/amd/ibs.h @@ -1,4 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_AMD_IBS_H +#define _ASM_X86_AMD_IBS_H + /* * From PPR Vol 1 for AMD Family 19h Model 01h B1 * 55898 Rev 0.35 - Feb 5, 2021 @@ -151,3 +154,5 @@ struct perf_ibs_data { }; u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX]; }; + +#endif /* _ASM_X86_AMD_IBS_H */ diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index e02be2962a01..06fc0479a23f 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -218,6 +218,7 @@ #define X86_FEATURE_FLEXPRIORITY ( 8*32+ 1) /* "flexpriority" Intel FlexPriority */ #define X86_FEATURE_EPT ( 8*32+ 2) /* "ept" Intel Extended Page Table */ #define X86_FEATURE_VPID ( 8*32+ 3) /* "vpid" Intel Virtual Processor ID */ +#define X86_FEATURE_COHERENCY_SFW_NO ( 8*32+ 4) /* SNP cache coherency software work around not needed */ #define X86_FEATURE_VMMCALL ( 8*32+15) /* "vmmcall" Prefer VMMCALL to VMCALL */ #define X86_FEATURE_XENPV ( 8*32+16) /* Xen paravirtual guest */ @@ -336,7 +337,7 @@ #define X86_FEATURE_AMD_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */ #define X86_FEATURE_AMD_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */ #define X86_FEATURE_AMD_STIBP_ALWAYS_ON (13*32+17) /* Single Thread Indirect Branch Predictors always-on preferred */ -#define X86_FEATURE_AMD_IBRS_SAME_MODE (13*32+19) /* Indirect Branch Restricted Speculation same mode protection*/ +#define X86_FEATURE_AMD_IBRS_SAME_MODE (13*32+19) /* Indirect Branch Restricted Speculation same mode protection*/ #define X86_FEATURE_AMD_PPIN (13*32+23) /* "amd_ppin" Protected Processor Inventory Number */ #define X86_FEATURE_AMD_SSBD (13*32+24) /* Speculative Store Bypass Disable */ #define X86_FEATURE_VIRT_SSBD (13*32+25) /* "virt_ssbd" Virtualized Speculative Store Bypass Disable */ @@ -379,6 +380,7 @@ #define X86_FEATURE_V_SPEC_CTRL (15*32+20) /* "v_spec_ctrl" Virtual SPEC_CTRL */ #define X86_FEATURE_VNMI (15*32+25) /* "vnmi" Virtual NMI */ #define X86_FEATURE_SVME_ADDR_CHK (15*32+28) /* SVME addr check */ +#define X86_FEATURE_BUS_LOCK_THRESHOLD (15*32+29) /* Bus lock threshold */ #define X86_FEATURE_IDLE_HLT (15*32+30) /* IDLE HLT intercept */ /* Intel-defined CPU features, CPUID level 0x00000007:0 (ECX), word 16 */ @@ -447,6 +449,7 @@ #define X86_FEATURE_DEBUG_SWAP (19*32+14) /* "debug_swap" SEV-ES full debug state swap support */ #define X86_FEATURE_RMPREAD (19*32+21) /* RMPREAD instruction */ #define X86_FEATURE_SEGMENTED_RMP (19*32+23) /* Segmented RMP support */ +#define X86_FEATURE_ALLOWED_SEV_FEATURES (19*32+27) /* Allowed SEV Features */ #define X86_FEATURE_SVSM (19*32+28) /* "svsm" SVSM present */ #define X86_FEATURE_HV_INUSE_WR_ALLOWED (19*32+30) /* Allow Write to in-use hypervisor-owned pages */ @@ -454,10 +457,15 @@ #define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */ #define X86_FEATURE_WRMSR_XX_BASE_NS (20*32+ 1) /* WRMSR to {FS,GS,KERNEL_GS}_BASE is non-serializing */ #define X86_FEATURE_LFENCE_RDTSC (20*32+ 2) /* LFENCE always serializing / synchronizes RDTSC */ +#define X86_FEATURE_VERW_CLEAR (20*32+ 5) /* The memory form of VERW mitigates TSA */ #define X86_FEATURE_NULL_SEL_CLR_BASE (20*32+ 6) /* Null Selector Clears Base */ + #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* Automatic IBRS */ #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* SMM_CTL MSR is not present */ +#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_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 */ #define X86_FEATURE_SRSO_NO (20*32+29) /* CPU is not affected by SRSO */ @@ -482,7 +490,11 @@ #define X86_FEATURE_AMD_HTR_CORES (21*32+ 6) /* Heterogeneous Core Topology */ #define X86_FEATURE_AMD_WORKLOAD_CLASS (21*32+ 7) /* Workload Classification */ #define X86_FEATURE_PREFER_YMM (21*32+ 8) /* Avoid ZMM registers due to downclocking */ -#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32+ 9) /* Use thunk for indirect branches in lower half of cacheline */ +#define X86_FEATURE_APX (21*32+ 9) /* Advanced Performance Extensions */ +#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32+10) /* Use thunk for indirect branches in lower half of cacheline */ +#define X86_FEATURE_TSA_SQ_NO (21*32+11) /* AMD CPU not vulnerable to TSA-SQ */ +#define X86_FEATURE_TSA_L1_NO (21*32+12) /* AMD CPU not vulnerable to TSA-L1 */ +#define X86_FEATURE_CLEAR_CPU_BUF_VM (21*32+13) /* Clear CPU buffers using VERW before VMRUN */ /* * BUG word(s) @@ -535,6 +547,8 @@ #define X86_BUG_BHI X86_BUG( 1*32+ 3) /* "bhi" CPU is affected by Branch History Injection */ #define X86_BUG_IBPB_NO_RET X86_BUG( 1*32+ 4) /* "ibpb_no_ret" IBPB omits return target predictions */ #define X86_BUG_SPECTRE_V2_USER X86_BUG( 1*32+ 5) /* "spectre_v2_user" CPU is affected by Spectre variant 2 attack between user processes */ -#define X86_BUG_ITS X86_BUG( 1*32+ 6) /* "its" CPU is affected by Indirect Target Selection */ -#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 7) /* "its_native_only" CPU is affected by ITS, VMX is not affected */ +#define X86_BUG_OLD_MICROCODE X86_BUG( 1*32+ 6) /* "old_microcode" CPU has old microcode, it is surely vulnerable to something */ +#define X86_BUG_ITS X86_BUG( 1*32+ 7) /* "its" CPU is affected by Indirect Target Selection */ +#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */ +#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index e7d2f460fcc6..b65c3ba5fa14 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -419,6 +419,7 @@ #define DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI (1UL << 12) #define DEBUGCTLMSR_FREEZE_IN_SMM_BIT 14 #define DEBUGCTLMSR_FREEZE_IN_SMM (1UL << DEBUGCTLMSR_FREEZE_IN_SMM_BIT) +#define DEBUGCTLMSR_RTM_DEBUG BIT(15) #define MSR_PEBS_FRONTEND 0x000003f7 @@ -533,7 +534,7 @@ #define MSR_HWP_CAPABILITIES 0x00000771 #define MSR_HWP_REQUEST_PKG 0x00000772 #define MSR_HWP_INTERRUPT 0x00000773 -#define MSR_HWP_REQUEST 0x00000774 +#define MSR_HWP_REQUEST 0x00000774 #define MSR_HWP_STATUS 0x00000777 /* CPUID.6.EAX */ @@ -550,16 +551,16 @@ #define HWP_LOWEST_PERF(x) (((x) >> 24) & 0xff) /* IA32_HWP_REQUEST */ -#define HWP_MIN_PERF(x) (x & 0xff) -#define HWP_MAX_PERF(x) ((x & 0xff) << 8) +#define HWP_MIN_PERF(x) (x & 0xff) +#define HWP_MAX_PERF(x) ((x & 0xff) << 8) #define HWP_DESIRED_PERF(x) ((x & 0xff) << 16) -#define HWP_ENERGY_PERF_PREFERENCE(x) (((unsigned long long) x & 0xff) << 24) +#define HWP_ENERGY_PERF_PREFERENCE(x) (((u64)x & 0xff) << 24) #define HWP_EPP_PERFORMANCE 0x00 #define HWP_EPP_BALANCE_PERFORMANCE 0x80 #define HWP_EPP_BALANCE_POWERSAVE 0xC0 #define HWP_EPP_POWERSAVE 0xFF -#define HWP_ACTIVITY_WINDOW(x) ((unsigned long long)(x & 0xff3) << 32) -#define HWP_PACKAGE_CONTROL(x) ((unsigned long long)(x & 0x1) << 42) +#define HWP_ACTIVITY_WINDOW(x) ((u64)(x & 0xff3) << 32) +#define HWP_PACKAGE_CONTROL(x) ((u64)(x & 0x1) << 42) /* IA32_HWP_STATUS */ #define HWP_GUARANTEED_CHANGE(x) (x & 0x1) @@ -602,7 +603,11 @@ /* V6 PMON MSR range */ #define MSR_IA32_PMC_V6_GP0_CTR 0x1900 #define MSR_IA32_PMC_V6_GP0_CFG_A 0x1901 +#define MSR_IA32_PMC_V6_GP0_CFG_B 0x1902 +#define MSR_IA32_PMC_V6_GP0_CFG_C 0x1903 #define MSR_IA32_PMC_V6_FX0_CTR 0x1980 +#define MSR_IA32_PMC_V6_FX0_CFG_B 0x1982 +#define MSR_IA32_PMC_V6_FX0_CFG_C 0x1983 #define MSR_IA32_PMC_V6_STEP 4 /* KeyID partitioning between MKTME and TDX */ @@ -624,6 +629,7 @@ #define MSR_AMD64_OSVW_STATUS 0xc0010141 #define MSR_AMD_PPIN_CTL 0xc00102f0 #define MSR_AMD_PPIN 0xc00102f1 +#define MSR_AMD64_CPUID_FN_7 0xc0011002 #define MSR_AMD64_CPUID_FN_1 0xc0011004 #define MSR_AMD64_LS_CFG 0xc0011020 #define MSR_AMD64_DC_CFG 0xc0011022 @@ -728,6 +734,11 @@ #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301 #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR 0xc0000302 +/* AMD Hardware Feedback Support MSRs */ +#define MSR_AMD_WORKLOAD_CLASS_CONFIG 0xc0000500 +#define MSR_AMD_WORKLOAD_CLASS_ID 0xc0000501 +#define MSR_AMD_WORKLOAD_HRST 0xc0000502 + /* AMD Last Branch Record MSRs */ #define MSR_AMD64_LBR_SELECT 0xc000010e @@ -826,6 +837,7 @@ #define MSR_K7_HWCR_SMMLOCK BIT_ULL(MSR_K7_HWCR_SMMLOCK_BIT) #define MSR_K7_HWCR_IRPERF_EN_BIT 30 #define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) +#define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35 #define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_STATUS 0xc0010042 #define MSR_K7_HWCR_CPB_DIS_BIT 25 diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index b663d916f162..0f15d683817d 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -441,6 +441,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) #define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7) #define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) +#define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 @@ -931,4 +932,80 @@ struct kvm_hyperv_eventfd { #define KVM_X86_SNP_VM 4 #define KVM_X86_TDX_VM 5 +/* Trust Domain eXtension sub-ioctl() commands. */ +enum kvm_tdx_cmd_id { + KVM_TDX_CAPABILITIES = 0, + KVM_TDX_INIT_VM, + KVM_TDX_INIT_VCPU, + KVM_TDX_INIT_MEM_REGION, + KVM_TDX_FINALIZE_VM, + KVM_TDX_GET_CPUID, + + KVM_TDX_CMD_NR_MAX, +}; + +struct kvm_tdx_cmd { + /* enum kvm_tdx_cmd_id */ + __u32 id; + /* flags for sub-commend. If sub-command doesn't use this, set zero. */ + __u32 flags; + /* + * data for each sub-command. An immediate or a pointer to the actual + * data in process virtual address. If sub-command doesn't use it, + * set zero. + */ + __u64 data; + /* + * Auxiliary error code. The sub-command may return TDX SEAMCALL + * status code in addition to -Exxx. + */ + __u64 hw_error; +}; + +struct kvm_tdx_capabilities { + __u64 supported_attrs; + __u64 supported_xfam; + + __u64 kernel_tdvmcallinfo_1_r11; + __u64 user_tdvmcallinfo_1_r11; + __u64 kernel_tdvmcallinfo_1_r12; + __u64 user_tdvmcallinfo_1_r12; + + __u64 reserved[250]; + + /* Configurable CPUID bits for userspace */ + struct kvm_cpuid2 cpuid; +}; + +struct kvm_tdx_init_vm { + __u64 attributes; + __u64 xfam; + __u64 mrconfigid[6]; /* sha384 digest */ + __u64 mrowner[6]; /* sha384 digest */ + __u64 mrownerconfig[6]; /* sha384 digest */ + + /* The total space for TD_PARAMS before the CPUIDs is 256 bytes */ + __u64 reserved[12]; + + /* + * Call KVM_TDX_INIT_VM before vcpu creation, thus before + * KVM_SET_CPUID2. + * This configuration supersedes KVM_SET_CPUID2s for VCPUs because the + * TDX module directly virtualizes those CPUIDs without VMM. The user + * space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with + * those values. If it doesn't, KVM may have wrong idea of vCPUIDs of + * the guest, and KVM may wrongly emulate CPUIDs or MSRs that the TDX + * module doesn't virtualize. + */ + struct kvm_cpuid2 cpuid; +}; + +#define KVM_TDX_MEASURE_MEMORY_REGION _BITULL(0) + +struct kvm_tdx_init_mem_region { + __u64 source_addr; + __u64 gpa; + __u64 nr_pages; +}; + #endif /* _ASM_X86_KVM_H */ diff --git a/tools/arch/x86/include/uapi/asm/svm.h b/tools/arch/x86/include/uapi/asm/svm.h index ec1321248dac..9c640a521a67 100644 --- a/tools/arch/x86/include/uapi/asm/svm.h +++ b/tools/arch/x86/include/uapi/asm/svm.h @@ -95,6 +95,7 @@ #define SVM_EXIT_CR14_WRITE_TRAP 0x09e #define SVM_EXIT_CR15_WRITE_TRAP 0x09f #define SVM_EXIT_INVPCID 0x0a2 +#define SVM_EXIT_BUS_LOCK 0x0a5 #define SVM_EXIT_IDLE_HLT 0x0a6 #define SVM_EXIT_NPF 0x400 #define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401 @@ -225,6 +226,7 @@ { SVM_EXIT_CR4_WRITE_TRAP, "write_cr4_trap" }, \ { SVM_EXIT_CR8_WRITE_TRAP, "write_cr8_trap" }, \ { SVM_EXIT_INVPCID, "invpcid" }, \ + { SVM_EXIT_BUS_LOCK, "buslock" }, \ { SVM_EXIT_IDLE_HLT, "idle-halt" }, \ { SVM_EXIT_NPF, "npf" }, \ { SVM_EXIT_AVIC_INCOMPLETE_IPI, "avic_incomplete_ipi" }, \ diff --git a/tools/arch/x86/include/uapi/asm/vmx.h b/tools/arch/x86/include/uapi/asm/vmx.h index a5faf6d88f1b..f0f4a4cf84a7 100644 --- a/tools/arch/x86/include/uapi/asm/vmx.h +++ b/tools/arch/x86/include/uapi/asm/vmx.h @@ -34,6 +34,7 @@ #define EXIT_REASON_TRIPLE_FAULT 2 #define EXIT_REASON_INIT_SIGNAL 3 #define EXIT_REASON_SIPI_SIGNAL 4 +#define EXIT_REASON_OTHER_SMI 6 #define EXIT_REASON_INTERRUPT_WINDOW 7 #define EXIT_REASON_NMI_WINDOW 8 @@ -92,6 +93,7 @@ #define EXIT_REASON_TPAUSE 68 #define EXIT_REASON_BUS_LOCK 74 #define EXIT_REASON_NOTIFY 75 +#define EXIT_REASON_TDCALL 77 #define VMX_EXIT_REASONS \ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \ @@ -155,7 +157,8 @@ { EXIT_REASON_UMWAIT, "UMWAIT" }, \ { EXIT_REASON_TPAUSE, "TPAUSE" }, \ { EXIT_REASON_BUS_LOCK, "BUS_LOCK" }, \ - { EXIT_REASON_NOTIFY, "NOTIFY" } + { EXIT_REASON_NOTIFY, "NOTIFY" }, \ + { EXIT_REASON_TDCALL, "TDCALL" } #define VMX_EXIT_REASON_FLAGS \ { VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" } diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S index 59cf6f9065aa..ccc3d923fc1e 100644 --- a/tools/arch/x86/lib/memcpy_64.S +++ b/tools/arch/x86/lib/memcpy_64.S @@ -40,6 +40,7 @@ SYM_FUNC_END(__memcpy) EXPORT_SYMBOL(__memcpy) SYM_FUNC_ALIAS_MEMFUNC(memcpy, __memcpy) +SYM_PIC_ALIAS(memcpy) EXPORT_SYMBOL(memcpy) SYM_FUNC_START_LOCAL(memcpy_orig) diff --git a/tools/arch/x86/lib/memset_64.S b/tools/arch/x86/lib/memset_64.S index d66b710d628f..fb5a03cf5ab7 100644 --- a/tools/arch/x86/lib/memset_64.S +++ b/tools/arch/x86/lib/memset_64.S @@ -42,6 +42,7 @@ SYM_FUNC_END(__memset) EXPORT_SYMBOL(__memset) SYM_FUNC_ALIAS_MEMFUNC(memset, __memset) +SYM_PIC_ALIAS(memset) EXPORT_SYMBOL(memset) SYM_FUNC_START_LOCAL(memset_orig) diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 8a48cc2536f5..55d59ed507d5 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -11,11 +11,16 @@ #include #include #include +#include #include #define pr_err(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +/* Bootconfig footer is [size][csum][BOOTCONFIG_MAGIC]. */ +#define BOOTCONFIG_FOOTER_SIZE \ + (sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN) + static int xbc_show_value(struct xbc_node *node, bool semicolon) { const char *val, *eol; @@ -185,10 +190,10 @@ static int load_xbc_from_initrd(int fd, char **buf) if (ret < 0) return -errno; - if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN) + if (stat.st_size < BOOTCONFIG_FOOTER_SIZE) return 0; - if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) + if (lseek(fd, -(off_t)BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) return pr_errno("Failed to lseek for magic", -errno); if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0) @@ -198,7 +203,7 @@ static int load_xbc_from_initrd(int fd, char **buf) if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0) return 0; - if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0) + if (lseek(fd, -(off_t)BOOTCONFIG_FOOTER_SIZE, SEEK_END) < 0) return pr_errno("Failed to lseek for size", -errno); if (read(fd, &size, sizeof(uint32_t)) < 0) @@ -210,12 +215,12 @@ static int load_xbc_from_initrd(int fd, char **buf) csum = le32toh(csum); /* Wrong size error */ - if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) { + if (stat.st_size < size + BOOTCONFIG_FOOTER_SIZE) { pr_err("bootconfig size is too big\n"); return -E2BIG; } - if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN), + if (lseek(fd, stat.st_size - (size + BOOTCONFIG_FOOTER_SIZE), SEEK_SET) < 0) return pr_errno("Failed to lseek", -errno); @@ -346,7 +351,7 @@ static int delete_xbc(const char *path) ret = fstat(fd, &stat); if (!ret) ret = ftruncate(fd, stat.st_size - - size - 8 - BOOTCONFIG_MAGIC_LEN); + - size - BOOTCONFIG_FOOTER_SIZE); if (ret) ret = -errno; } /* Ignore if there is no boot config in initrd */ @@ -359,7 +364,12 @@ static int delete_xbc(const char *path) static int apply_xbc(const char *path, const char *xbc_path) { - char *buf, *data, *p; + struct { + uint32_t size; + uint32_t csum; + char magic[BOOTCONFIG_MAGIC_LEN]; + } footer; + char *buf, *data; size_t total_size; struct stat stat; const char *msg; @@ -376,8 +386,7 @@ static int apply_xbc(const char *path, const char *xbc_path) csum = xbc_calc_checksum(buf, size); /* Backup the bootconfig data */ - data = calloc(size + BOOTCONFIG_ALIGN + - sizeof(uint32_t) + sizeof(uint32_t) + BOOTCONFIG_MAGIC_LEN, 1); + data = calloc(size + BOOTCONFIG_ALIGN + BOOTCONFIG_FOOTER_SIZE, 1); if (!data) return -ENOMEM; memcpy(data, buf, size); @@ -425,22 +434,18 @@ static int apply_xbc(const char *path, const char *xbc_path) } /* To align up the total size to BOOTCONFIG_ALIGN, get padding size */ - total_size = stat.st_size + size + sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN; + total_size = stat.st_size + size + BOOTCONFIG_FOOTER_SIZE; pad = ((total_size + BOOTCONFIG_ALIGN - 1) & (~BOOTCONFIG_ALIGN_MASK)) - total_size; size += pad; /* Add a footer */ - p = data + size; - *(uint32_t *)p = htole32(size); - p += sizeof(uint32_t); + footer.size = htole32(size); + footer.csum = htole32(csum); + memcpy(footer.magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN); + static_assert(sizeof(footer) == BOOTCONFIG_FOOTER_SIZE); + memcpy(data + size, &footer, BOOTCONFIG_FOOTER_SIZE); - *(uint32_t *)p = htole32(csum); - p += sizeof(uint32_t); - - memcpy(p, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN); - p += BOOTCONFIG_MAGIC_LEN; - - total_size = p - data; + total_size = size + BOOTCONFIG_FOOTER_SIZE; ret = write(fd, data, total_size); if (ret < total_size) { diff --git a/tools/bootconfig/scripts/ftrace.sh b/tools/bootconfig/scripts/ftrace.sh index 186eed923041..cc5250c64699 100644 --- a/tools/bootconfig/scripts/ftrace.sh +++ b/tools/bootconfig/scripts/ftrace.sh @@ -1,3 +1,4 @@ +#!/bin/sh # SPDX-License-Identifier: GPL-2.0-only clear_trace() { # reset trace output diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh index a2c484c243f5..7594659af1e1 100755 --- a/tools/bootconfig/test-bootconfig.sh +++ b/tools/bootconfig/test-bootconfig.sh @@ -27,16 +27,16 @@ NO=1 xpass() { # pass test command echo "test case $NO ($*)... " - if ! ($@ && echo "\t\t[OK]"); then - echo "\t\t[NG]"; NG=$((NG + 1)) + if ! ($@ && printf "\t\t[OK]\n"); then + printf "\t\t[NG]\n"; NG=$((NG + 1)) fi NO=$((NO + 1)) } xfail() { # fail test command echo "test case $NO ($*)... " - if ! (! $@ && echo "\t\t[OK]"); then - echo "\t\t[NG]"; NG=$((NG + 1)) + if ! (! $@ && printf "\t\t[OK]\n"); then + printf "\t\t[NG]\n"; NG=$((NG + 1)) fi NO=$((NO + 1)) } @@ -48,13 +48,13 @@ echo "Delete command should success without bootconfig" xpass $BOOTCONF -d $INITRD dd if=/dev/zero of=$INITRD bs=4096 count=1 -echo "key = value;" > $TEMPCONF -bconf_size=$(stat -c %s $TEMPCONF) -initrd_size=$(stat -c %s $INITRD) +printf "key = value;" > $TEMPCONF +bconf_size=$(wc -c < $TEMPCONF) +initrd_size=$(wc -c < $INITRD) echo "Apply command test" xpass $BOOTCONF -a $TEMPCONF $INITRD -new_size=$(stat -c %s $INITRD) +new_size=$(wc -c < $INITRD) echo "Show command test" xpass $BOOTCONF $INITRD @@ -69,13 +69,13 @@ echo "Apply command repeat test" xpass $BOOTCONF -a $TEMPCONF $INITRD echo "File size check" -xpass test $new_size -eq $(stat -c %s $INITRD) +xpass test $new_size -eq $(wc -c < $INITRD) echo "Delete command check" xpass $BOOTCONF -d $INITRD echo "File size check" -new_size=$(stat -c %s $INITRD) +new_size=$(wc -c < $INITRD) xpass test $new_size -eq $initrd_size echo "No error messge while applying" @@ -97,19 +97,20 @@ BEGIN { ' > $TEMPCONF xpass $BOOTCONF -a $TEMPCONF $INITRD -echo "badnode" >> $TEMPCONF +printf "badnode\n" >> $TEMPCONF xfail $BOOTCONF -a $TEMPCONF $INITRD echo "Max filesize check" # Max size is 32767 (including terminal byte) -echo -n "data = \"" > $TEMPCONF +printf "data = \"" > $TEMPCONF dd if=/dev/urandom bs=768 count=32 | base64 -w0 >> $TEMPCONF -echo "\"" >> $TEMPCONF +printf "\"\n" >> $TEMPCONF xfail $BOOTCONF -a $TEMPCONF $INITRD -truncate -s 32764 $TEMPCONF -echo "\"" >> $TEMPCONF # add 2 bytes + terminal ('\"\n\0') +dd if=$TEMPCONF of=$OUTFILE bs=1 count=32764 +cp $OUTFILE $TEMPCONF +printf "\"\n" >> $TEMPCONF # add 2 bytes + terminal ('\"\n\0') xpass $BOOTCONF -a $TEMPCONF $INITRD echo "Adding same-key values" @@ -139,7 +140,7 @@ xfail grep -q "baz" $OUTFILE xpass grep -q "qux" $OUTFILE echo "Double/single quotes test" -echo "key = '\"string\"';" > $TEMPCONF +printf "key = '\"string\"';" > $TEMPCONF $BOOTCONF -a $TEMPCONF $INITRD $BOOTCONF $INITRD > $TEMPCONF cat $TEMPCONF @@ -167,8 +168,8 @@ echo > $INITRD xpass $BOOTCONF -a $TEMPCONF $INITRD $BOOTCONF $INITRD > $OUTFILE -xfail grep -q val[[:space:]] $OUTFILE -xpass grep -q val2[[:space:]] $OUTFILE +xfail grep -q 'val[[:space:]]' $OUTFILE +xpass grep -q 'val2[[:space:]]' $OUTFILE echo "=== expected failure cases ===" for i in samples/bad-* ; do diff --git a/tools/bpf/bpf_jit_disasm.c b/tools/bpf/bpf_jit_disasm.c index 1baee9e2aba9..5ab8f80e2834 100644 --- a/tools/bpf/bpf_jit_disasm.c +++ b/tools/bpf/bpf_jit_disasm.c @@ -45,6 +45,8 @@ static void get_exec_path(char *tpath, size_t size) assert(path); len = readlink(path, tpath, size); + if (len < 0) + len = 0; tpath[len] = 0; free(path); diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index da3152c16228..f69fd92df8d8 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -35,6 +35,7 @@ PROG COMMANDS | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog tracelog** +| **bpftool** **prog tracelog** [ { **stdout** | **stderr** } *PROG* ] | **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] | **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs* | **bpftool** **prog help** @@ -179,6 +180,12 @@ bpftool prog tracelog purposes. For streaming data from BPF programs to user space, one can use perf events (see also **bpftool-map**\ (8)). +bpftool prog tracelog { stdout | stderr } *PROG* + Dump the BPF stream of the program. BPF programs can write to these streams + at runtime with the **bpf_stream_vprintk**\ () kfunc. The kernel may write + error messages to the standard error stream. This facility should be used + only for debugging purposes. + bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*] Run BPF program *PROG* in the kernel testing infrastructure for BPF, meaning that the program works on the data and context provided by the diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 27512feb5c70..a759ba24471d 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -518,7 +518,21 @@ _bpftool() esac ;; tracelog) - return 0 + case $prev in + $command) + COMPREPLY+=( $( compgen -W "stdout stderr" -- \ + "$cur" ) ) + return 0 + ;; + stdout|stderr) + COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ + "$cur" ) ) + return 0 + ;; + *) + return 0 + ;; + esac ;; profile) case $cword in diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index 6b14cbfa58aa..946612029dee 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -905,7 +905,8 @@ static int do_dump(int argc, char **argv) return -1; } - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, + BPF_F_RDONLY); if (fd < 0) return -1; @@ -1118,10 +1119,13 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, [BPF_OBJ_PROG] = "prog", [BPF_OBJ_MAP] = "map", }; + LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro); __u32 btf_id, id = 0; int err; int fd; + opts_ro.open_flags = BPF_F_RDONLY; + while (true) { switch (type) { case BPF_OBJ_PROG: @@ -1151,7 +1155,7 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, fd = bpf_prog_get_fd_by_id(id); break; case BPF_OBJ_MAP: - fd = bpf_map_get_fd_by_id(id); + fd = bpf_map_get_fd_by_id_opts(id, &opts_ro); break; default: err = -1; diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index ecfa790adc13..b07317d2842f 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -4,6 +4,7 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include #include #include #include @@ -193,7 +194,8 @@ int mount_tracefs(const char *target) return err; } -int open_obj_pinned(const char *path, bool quiet) +int open_obj_pinned(const char *path, bool quiet, + const struct bpf_obj_get_opts *opts) { char *pname; int fd = -1; @@ -205,7 +207,7 @@ int open_obj_pinned(const char *path, bool quiet) goto out_ret; } - fd = bpf_obj_get(pname); + fd = bpf_obj_get_opts(pname, opts); if (fd < 0) { if (!quiet) p_err("bpf obj get (%s): %s", pname, @@ -221,12 +223,13 @@ int open_obj_pinned(const char *path, bool quiet) return fd; } -int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type) +int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type, + const struct bpf_obj_get_opts *opts) { enum bpf_obj_type type; int fd; - fd = open_obj_pinned(path, false); + fd = open_obj_pinned(path, false, opts); if (fd < 0) return -1; @@ -555,7 +558,7 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb, if (typeflag != FTW_F) goto out_ret; - fd = open_obj_pinned(fpath, true); + fd = open_obj_pinned(fpath, true, NULL); if (fd < 0) goto out_ret; @@ -928,7 +931,7 @@ int prog_parse_fds(int *argc, char ***argv, int **fds) path = **argv; NEXT_ARGP(); - (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG); + (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG, NULL); if ((*fds)[0] < 0) return -1; return 1; @@ -965,7 +968,8 @@ int prog_parse_fd(int *argc, char ***argv) return fd; } -static int map_fd_by_name(char *name, int **fds) +static int map_fd_by_name(char *name, int **fds, + const struct bpf_get_fd_by_id_opts *opts) { unsigned int id = 0; int fd, nb_fds = 0; @@ -973,6 +977,7 @@ static int map_fd_by_name(char *name, int **fds) int err; while (true) { + LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro); struct bpf_map_info info = {}; __u32 len = sizeof(info); @@ -985,7 +990,9 @@ static int map_fd_by_name(char *name, int **fds) return nb_fds; } - fd = bpf_map_get_fd_by_id(id); + /* Request a read-only fd to query the map info */ + opts_ro.open_flags = BPF_F_RDONLY; + fd = bpf_map_get_fd_by_id_opts(id, &opts_ro); if (fd < 0) { p_err("can't get map by id (%u): %s", id, strerror(errno)); @@ -1004,6 +1011,19 @@ static int map_fd_by_name(char *name, int **fds) continue; } + /* Get an fd with the requested options, if they differ + * from the read-only options used to get the fd above. + */ + if (memcmp(opts, &opts_ro, sizeof(opts_ro))) { + close(fd); + fd = bpf_map_get_fd_by_id_opts(id, opts); + if (fd < 0) { + p_err("can't get map by id (%u): %s", id, + strerror(errno)); + goto err_close_fds; + } + } + if (nb_fds > 0) { tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); if (!tmp) { @@ -1023,8 +1043,13 @@ static int map_fd_by_name(char *name, int **fds) return -1; } -int map_parse_fds(int *argc, char ***argv, int **fds) +int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags) { + LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts); + + assert((open_flags & ~BPF_F_RDONLY) == 0); + opts.open_flags = open_flags; + if (is_prefix(**argv, "id")) { unsigned int id; char *endptr; @@ -1038,7 +1063,7 @@ int map_parse_fds(int *argc, char ***argv, int **fds) } NEXT_ARGP(); - (*fds)[0] = bpf_map_get_fd_by_id(id); + (*fds)[0] = bpf_map_get_fd_by_id_opts(id, &opts); if ((*fds)[0] < 0) { p_err("get map by id (%u): %s", id, strerror(errno)); return -1; @@ -1056,16 +1081,18 @@ int map_parse_fds(int *argc, char ***argv, int **fds) } NEXT_ARGP(); - return map_fd_by_name(name, fds); + return map_fd_by_name(name, fds, &opts); } else if (is_prefix(**argv, "pinned")) { char *path; + LIBBPF_OPTS(bpf_obj_get_opts, get_opts); + get_opts.file_flags = open_flags; NEXT_ARGP(); path = **argv; NEXT_ARGP(); - (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP); + (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP, &get_opts); if ((*fds)[0] < 0) return -1; return 1; @@ -1075,7 +1102,7 @@ int map_parse_fds(int *argc, char ***argv, int **fds) return -1; } -int map_parse_fd(int *argc, char ***argv) +int map_parse_fd(int *argc, char ***argv, __u32 open_flags) { int *fds = NULL; int nb_fds, fd; @@ -1085,7 +1112,7 @@ int map_parse_fd(int *argc, char ***argv) p_err("mem alloc failed"); return -1; } - nb_fds = map_parse_fds(argc, argv, &fds); + nb_fds = map_parse_fds(argc, argv, &fds, open_flags); if (nb_fds != 1) { if (nb_fds > 1) { p_err("several maps match this handle"); @@ -1103,12 +1130,12 @@ int map_parse_fd(int *argc, char ***argv) } int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info, - __u32 *info_len) + __u32 *info_len, __u32 open_flags) { int err; int fd; - fd = map_parse_fd(argc, argv); + fd = map_parse_fd(argc, argv, open_flags); if (fd < 0) return -1; diff --git a/tools/bpf/bpftool/iter.c b/tools/bpf/bpftool/iter.c index 5c39c2ed36a2..df5f0d1e07e8 100644 --- a/tools/bpf/bpftool/iter.c +++ b/tools/bpf/bpftool/iter.c @@ -37,7 +37,7 @@ static int do_pin(int argc, char **argv) return -1; } - map_fd = map_parse_fd(&argc, &argv); + map_fd = map_parse_fd(&argc, &argv, BPF_F_RDONLY); if (map_fd < 0) return -1; diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c index 3535afc80a49..a773e05d5ade 100644 --- a/tools/bpf/bpftool/link.c +++ b/tools/bpf/bpftool/link.c @@ -117,7 +117,7 @@ static int link_parse_fd(int *argc, char ***argv) path = **argv; NEXT_ARGP(); - return open_obj_pinned_any(path, BPF_OBJ_LINK); + return open_obj_pinned_any(path, BPF_OBJ_LINK, NULL); } p_err("expected 'id' or 'pinned', got: '%s'?", **argv); @@ -485,6 +485,7 @@ static int show_link_close_json(int fd, struct bpf_link_info *info) case BPF_LINK_TYPE_RAW_TRACEPOINT: jsonw_string_field(json_wtr, "tp_name", u64_to_ptr(info->raw_tracepoint.tp_name)); + jsonw_uint_field(json_wtr, "cookie", info->raw_tracepoint.cookie); break; case BPF_LINK_TYPE_TRACING: err = get_prog_info(info->prog_id, &prog_info); @@ -502,6 +503,7 @@ static int show_link_close_json(int fd, struct bpf_link_info *info) json_wtr); jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id); jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id); + jsonw_uint_field(json_wtr, "cookie", info->tracing.cookie); break; case BPF_LINK_TYPE_CGROUP: jsonw_lluint_field(json_wtr, "cgroup_id", @@ -879,6 +881,8 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info) case BPF_LINK_TYPE_RAW_TRACEPOINT: printf("\n\ttp '%s' ", (const char *)u64_to_ptr(info->raw_tracepoint.tp_name)); + if (info->raw_tracepoint.cookie) + printf("cookie %llu ", info->raw_tracepoint.cookie); break; case BPF_LINK_TYPE_TRACING: err = get_prog_info(info->prog_id, &prog_info); @@ -897,6 +901,8 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info) printf("\n\ttarget_obj_id %u target_btf_id %u ", info->tracing.target_obj_id, info->tracing.target_btf_id); + if (info->tracing.cookie) + printf("\n\tcookie %llu ", info->tracing.cookie); break; case BPF_LINK_TYPE_CGROUP: printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id); diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index cd5963cb6058..2b7f2bd3a7db 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -534,9 +534,9 @@ int main(int argc, char **argv) usage(); if (version_requested) - return do_version(argc, argv); - - ret = cmd_select(commands, argc, argv, do_help); + ret = do_version(argc, argv); + else + ret = cmd_select(commands, argc, argv, do_help); if (json_output) jsonw_destroy(&json_wtr); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 9eb764fe4cc8..6db704fda5c0 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -15,6 +15,7 @@ #include #include +#include #include "json_writer.h" @@ -140,8 +141,10 @@ void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd, int get_fd_type(int fd); const char *get_fd_type_name(enum bpf_obj_type type); char *get_fdinfo(int fd, const char *key); -int open_obj_pinned(const char *path, bool quiet); -int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type); +int open_obj_pinned(const char *path, bool quiet, + const struct bpf_obj_get_opts *opts); +int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type, + const struct bpf_obj_get_opts *opts); int mount_bpffs_for_file(const char *file_name); int create_and_mount_bpffs_dir(const char *dir_name); int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***)); @@ -167,10 +170,10 @@ int do_iter(int argc, char **argv) __weak; int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what); int prog_parse_fd(int *argc, char ***argv); int prog_parse_fds(int *argc, char ***argv, int **fds); -int map_parse_fd(int *argc, char ***argv); -int map_parse_fds(int *argc, char ***argv, int **fds); +int map_parse_fd(int *argc, char ***argv, __u32 open_flags); +int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags); int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info, - __u32 *info_len); + __u32 *info_len, __u32 open_flags); struct bpf_prog_linfo; #if defined(HAVE_LLVM_SUPPORT) || defined(HAVE_LIBBFD_SUPPORT) diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 81cc668b4b05..c9de44a45778 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -337,9 +337,9 @@ static void fill_per_cpu_value(struct bpf_map_info *info, void *value) memcpy(value + i * step, value, info->value_size); } -static int parse_elem(char **argv, struct bpf_map_info *info, - void *key, void *value, __u32 key_size, __u32 value_size, - __u32 *flags, __u32 **value_fd) +static int parse_elem(char **argv, struct bpf_map_info *info, void *key, + void *value, __u32 key_size, __u32 value_size, + __u32 *flags, __u32 **value_fd, __u32 open_flags) { if (!*argv) { if (!key && !value) @@ -362,7 +362,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, return -1; return parse_elem(argv, info, NULL, value, key_size, value_size, - flags, value_fd); + flags, value_fd, open_flags); } else if (is_prefix(*argv, "value")) { int fd; @@ -388,7 +388,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, return -1; } - fd = map_parse_fd(&argc, &argv); + fd = map_parse_fd(&argc, &argv, open_flags); if (fd < 0) return -1; @@ -424,7 +424,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, } return parse_elem(argv, info, key, NULL, key_size, value_size, - flags, NULL); + flags, NULL, open_flags); } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || is_prefix(*argv, "exist")) { if (!flags) { @@ -440,7 +440,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, *flags = BPF_EXIST; return parse_elem(argv + 1, info, key, value, key_size, - value_size, NULL, value_fd); + value_size, NULL, value_fd, open_flags); } p_err("expected key or value, got: %s", *argv); @@ -639,7 +639,7 @@ static int do_show_subset(int argc, char **argv) p_err("mem alloc failed"); return -1; } - nb_fds = map_parse_fds(&argc, &argv, &fds); + nb_fds = map_parse_fds(&argc, &argv, &fds, BPF_F_RDONLY); if (nb_fds < 1) goto exit_free; @@ -672,12 +672,15 @@ static int do_show_subset(int argc, char **argv) static int do_show(int argc, char **argv) { + LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts); struct bpf_map_info info = {}; __u32 len = sizeof(info); __u32 id = 0; int err; int fd; + opts.open_flags = BPF_F_RDONLY; + if (show_pinned) { map_table = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); @@ -707,7 +710,7 @@ static int do_show(int argc, char **argv) break; } - fd = bpf_map_get_fd_by_id(id); + fd = bpf_map_get_fd_by_id_opts(id, &opts); if (fd < 0) { if (errno == ENOENT) continue; @@ -909,7 +912,7 @@ static int do_dump(int argc, char **argv) p_err("mem alloc failed"); return -1; } - nb_fds = map_parse_fds(&argc, &argv, &fds); + nb_fds = map_parse_fds(&argc, &argv, &fds, BPF_F_RDONLY); if (nb_fds < 1) goto exit_free; @@ -997,7 +1000,7 @@ static int do_update(int argc, char **argv) if (argc < 2) usage(); - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0); if (fd < 0) return -1; @@ -1006,7 +1009,7 @@ static int do_update(int argc, char **argv) goto exit_free; err = parse_elem(argv, &info, key, value, info.key_size, - info.value_size, &flags, &value_fd); + info.value_size, &flags, &value_fd, 0); if (err) goto exit_free; @@ -1076,7 +1079,7 @@ static int do_lookup(int argc, char **argv) if (argc < 2) usage(); - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, BPF_F_RDONLY); if (fd < 0) return -1; @@ -1084,7 +1087,8 @@ static int do_lookup(int argc, char **argv) if (err) goto exit_free; - err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); + err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL, + BPF_F_RDONLY); if (err) goto exit_free; @@ -1127,7 +1131,7 @@ static int do_getnext(int argc, char **argv) if (argc < 2) usage(); - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, BPF_F_RDONLY); if (fd < 0) return -1; @@ -1140,8 +1144,8 @@ static int do_getnext(int argc, char **argv) } if (argc) { - err = parse_elem(argv, &info, key, NULL, info.key_size, 0, - NULL, NULL); + err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, + NULL, BPF_F_RDONLY); if (err) goto exit_free; } else { @@ -1198,7 +1202,7 @@ static int do_delete(int argc, char **argv) if (argc < 2) usage(); - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0); if (fd < 0) return -1; @@ -1209,7 +1213,8 @@ static int do_delete(int argc, char **argv) goto exit_free; } - err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); + err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL, + 0); if (err) goto exit_free; @@ -1226,11 +1231,16 @@ static int do_delete(int argc, char **argv) return err; } +static int map_parse_read_only_fd(int *argc, char ***argv) +{ + return map_parse_fd(argc, argv, BPF_F_RDONLY); +} + static int do_pin(int argc, char **argv) { int err; - err = do_pin_any(argc, argv, map_parse_fd); + err = do_pin_any(argc, argv, map_parse_read_only_fd); if (!err && json_output) jsonw_null(json_wtr); return err; @@ -1319,7 +1329,7 @@ static int do_create(int argc, char **argv) if (!REQ_ARGS(2)) usage(); inner_map_fd = map_parse_fd_and_info(&argc, &argv, - &info, &len); + &info, &len, BPF_F_RDONLY); if (inner_map_fd < 0) return -1; attr.inner_map_fd = inner_map_fd; @@ -1368,7 +1378,7 @@ static int do_pop_dequeue(int argc, char **argv) if (argc < 2) usage(); - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); + fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0); if (fd < 0) return -1; @@ -1407,7 +1417,7 @@ static int do_freeze(int argc, char **argv) if (!REQ_ARGS(2)) return -1; - fd = map_parse_fd(&argc, &argv); + fd = map_parse_fd(&argc, &argv, 0); if (fd < 0) return -1; diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c index 552b4ca40c27..bcb767e2d673 100644 --- a/tools/bpf/bpftool/map_perf_ring.c +++ b/tools/bpf/bpftool/map_perf_ring.c @@ -128,7 +128,8 @@ int do_event_pipe(int argc, char **argv) int err, map_fd; map_info_len = sizeof(map_info); - map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); + map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len, + 0); if (map_fd < 0) return -1; diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 64f958f437b0..cfc6f944f7c3 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -366,17 +366,18 @@ static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) { struct bpf_netdev_t *netinfo = cookie; struct ifinfomsg *ifinfo = msg; + struct ip_devname_ifindex *tmp; if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index) return 0; if (netinfo->used_len == netinfo->array_len) { - netinfo->devices = realloc(netinfo->devices, - (netinfo->array_len + 16) * - sizeof(struct ip_devname_ifindex)); - if (!netinfo->devices) + tmp = realloc(netinfo->devices, + (netinfo->array_len + 16) * sizeof(struct ip_devname_ifindex)); + if (!tmp) return -ENOMEM; + netinfo->devices = tmp; netinfo->array_len += 16; } netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index; @@ -395,6 +396,7 @@ static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) { struct bpf_tcinfo_t *tcinfo = cookie; struct tcmsg *info = msg; + struct tc_kind_handle *tmp; if (tcinfo->is_qdisc) { /* skip clsact qdisc */ @@ -406,11 +408,12 @@ static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) } if (tcinfo->used_len == tcinfo->array_len) { - tcinfo->handle_array = realloc(tcinfo->handle_array, + tmp = realloc(tcinfo->handle_array, (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); - if (!tcinfo->handle_array) + if (!tmp) return -ENOMEM; + tcinfo->handle_array = tmp; tcinfo->array_len += 16; } tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 96eea8a67225..9722d841abc0 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1062,7 +1062,7 @@ static int parse_attach_detach_args(int argc, char **argv, int *progfd, if (!REQ_ARGS(2)) return -EINVAL; - *mapfd = map_parse_fd(&argc, &argv); + *mapfd = map_parse_fd(&argc, &argv, 0); if (*mapfd < 0) return *mapfd; @@ -1113,6 +1113,52 @@ static int do_detach(int argc, char **argv) return 0; } +enum prog_tracelog_mode { + TRACE_STDOUT, + TRACE_STDERR, +}; + +static int +prog_tracelog_stream(int prog_fd, enum prog_tracelog_mode mode) +{ + FILE *file = mode == TRACE_STDOUT ? stdout : stderr; + int stream_id = mode == TRACE_STDOUT ? 1 : 2; + char buf[512]; + int ret; + + ret = 0; + do { + ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL); + if (ret > 0) + fwrite(buf, sizeof(buf[0]), ret, file); + } while (ret > 0); + + fflush(file); + return ret ? -1 : 0; +} + +static int do_tracelog_any(int argc, char **argv) +{ + enum prog_tracelog_mode mode; + int fd; + + if (argc == 0) + return do_tracelog(argc, argv); + if (!is_prefix(*argv, "stdout") && !is_prefix(*argv, "stderr")) + usage(); + mode = is_prefix(*argv, "stdout") ? TRACE_STDOUT : TRACE_STDERR; + NEXT_ARG(); + + if (!REQ_ARGS(2)) + return -1; + + fd = prog_parse_fd(&argc, &argv); + if (fd < 0) + return -1; + + return prog_tracelog_stream(fd, mode); +} + static int check_single_stdin(char *file_data_in, char *file_ctx_in) { if (file_data_in && file_ctx_in && @@ -1608,7 +1654,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) } NEXT_ARG(); - fd = map_parse_fd(&argc, &argv); + fd = map_parse_fd(&argc, &argv, 0); if (fd < 0) goto err_free_reuse_maps; @@ -2493,6 +2539,7 @@ static int do_help(int argc, char **argv) " [repeat N]\n" " %1$s %2$s profile PROG [duration DURATION] METRICs\n" " %1$s %2$s tracelog\n" + " %1$s %2$s tracelog { stdout | stderr } PROG\n" " %1$s %2$s help\n" "\n" " " HELP_SPEC_MAP "\n" @@ -2532,7 +2579,7 @@ static const struct cmd cmds[] = { { "loadall", do_loadall }, { "attach", do_attach }, { "detach", do_detach }, - { "tracelog", do_tracelog }, + { "tracelog", do_tracelog_any }, { "run", do_run }, { "profile", do_profile }, { 0 } diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 57bd995ce6af..649c5ab8e8f2 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -86,7 +86,6 @@ FEATURE_TESTS_BASIC := \ libtraceevent \ libtracefs \ libcpupower \ - libcrypto \ pthread-attr-setaffinity-np \ pthread-barrier \ reallocarray \ @@ -126,12 +125,7 @@ FEATURE_TESTS_EXTRA := \ llvm \ clang \ libbpf \ - libbpf-btf__load_from_kernel_by_id \ - libbpf-bpf_prog_load \ - libbpf-bpf_object__next_program \ - libbpf-bpf_object__next_map \ - libbpf-bpf_program__set_insns \ - libbpf-bpf_create_map \ + libbpf-strings \ libpfm4 \ libdebuginfod \ clang-bpf-co-re \ @@ -152,7 +146,6 @@ FEATURE_DISPLAY ?= \ numa_num_possible_cpus \ libperl \ libpython \ - libcrypto \ libcapstone \ llvm-perf \ zlib \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index b8b5fb183dd4..b41a42818d8a 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -38,7 +38,6 @@ FILES= \ test-libtraceevent.bin \ test-libcpupower.bin \ test-libtracefs.bin \ - test-libcrypto.bin \ test-libunwind.bin \ test-libunwind-debug-frame.bin \ test-libunwind-x86.bin \ @@ -59,6 +58,7 @@ FILES= \ test-lzma.bin \ test-bpf.bin \ test-libbpf.bin \ + test-libbpf-strings.bin \ test-get_cpuid.bin \ test-sdt.bin \ test-cxx.bin \ @@ -246,9 +246,6 @@ $(OUTPUT)test-libcpupower.bin: $(OUTPUT)test-libtracefs.bin: $(BUILD) $(shell $(PKG_CONFIG) --cflags libtracefs 2>/dev/null) -ltracefs -$(OUTPUT)test-libcrypto.bin: - $(BUILD) -lcrypto - $(OUTPUT)test-gtk2.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -Wno-deprecated-declarations @@ -339,26 +336,8 @@ $(OUTPUT)test-bpf.bin: $(OUTPUT)test-libbpf.bin: $(BUILD) -lbpf -$(OUTPUT)test-libbpf-btf__load_from_kernel_by_id.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-bpf_prog_load.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-bpf_map_create.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-bpf_object__next_program.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-bpf_object__next_map.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-bpf_program__set_insns.bin: - $(BUILD) -lbpf - -$(OUTPUT)test-libbpf-btf__raw_data.bin: - $(BUILD) -lbpf +$(OUTPUT)test-libbpf-strings.bin: + $(BUILD) $(OUTPUT)test-sdt.bin: $(BUILD) diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 03ddaac6f4c4..4419fb4710bd 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -66,14 +66,6 @@ # include "test-libslang.c" #undef main -#define main main_test_libbfd -# include "test-libbfd.c" -#undef main - -#define main main_test_libbfd_buildid -# include "test-libbfd-buildid.c" -#undef main - #define main main_test_backtrace # include "test-backtrace.c" #undef main @@ -138,10 +130,6 @@ # include "test-bpf.c" #undef main -#define main main_test_libcrypto -# include "test-libcrypto.c" -#undef main - #define main main_test_sdt # include "test-sdt.c" #undef main @@ -158,14 +146,6 @@ # include "test-reallocarray.c" #undef main -#define main main_test_disassembler_four_args -# include "test-disassembler-four-args.c" -#undef main - -#define main main_test_disassembler_init_styled -# include "test-disassembler-init-styled.c" -#undef main - #define main main_test_libzstd # include "test-libzstd.c" #undef main @@ -193,8 +173,6 @@ int main(int argc, char *argv[]) main_test_libelf_gelf_getnote(); main_test_libelf_getshdrstrndx(); main_test_libslang(); - main_test_libbfd(); - main_test_libbfd_buildid(); main_test_backtrace(); main_test_libnuma(); main_test_numa_num_possible_cpus(); @@ -206,14 +184,12 @@ int main(int argc, char *argv[]) main_test_lzma(); main_test_get_cpuid(); main_test_bpf(); - main_test_libcrypto(); main_test_scandirat(); main_test_sched_getcpu(); main_test_sdt(); main_test_setns(); main_test_libaio(); main_test_reallocarray(); - main_test_disassembler_four_args(); main_test_libzstd(); main_test_libtraceevent(); main_test_libtracefs(); diff --git a/tools/build/feature/test-libbpf-strings.c b/tools/build/feature/test-libbpf-strings.c new file mode 100644 index 000000000000..83e6c45f5c85 --- /dev/null +++ b/tools/build/feature/test-libbpf-strings.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +int main(void) +{ + struct btf_dump_type_data_opts opts; + + opts.emit_strings = 0; + return opts.emit_strings; +} diff --git a/tools/build/feature/test-libcrypto.c b/tools/build/feature/test-libcrypto.c deleted file mode 100644 index bc34a5bbb504..000000000000 --- a/tools/build/feature/test-libcrypto.c +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -int main(void) -{ - EVP_MD_CTX *mdctx; - unsigned char md[MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH]; - unsigned char dat[] = "12345"; - unsigned int digest_len; - - mdctx = EVP_MD_CTX_new(); - if (!mdctx) - return 0; - - EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); - EVP_DigestUpdate(mdctx, &dat[0], sizeof(dat)); - EVP_DigestFinal_ex(mdctx, &md[0], &digest_len); - EVP_MD_CTX_free(mdctx); - - SHA1(&dat[0], sizeof(dat), &md[0]); - - return 0; -} diff --git a/tools/cgroup/memcg_slabinfo.py b/tools/cgroup/memcg_slabinfo.py index 270c28a0d098..6bf4bde77903 100644 --- a/tools/cgroup/memcg_slabinfo.py +++ b/tools/cgroup/memcg_slabinfo.py @@ -146,11 +146,11 @@ def detect_kernel_config(): def for_each_slab(prog): - PGSlab = ~prog.constant('PG_slab') + slabtype = prog.constant('PGTY_slab') for page in for_each_page(prog): try: - if page.page_type.value_() == PGSlab: + if (page.page_type.value_() >> 24) == slabtype: yield cast('struct slab *', page) except FaultError: pass diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index ed565eb52275..342e056c8c66 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -77,7 +77,7 @@ $(OUTPUT)gpio-watch: $(GPIO_WATCH_IN) clean: rm -f $(ALL_PROGRAMS) - rm -f $(OUTPUT)include/linux/gpio.h + rm -rf $(OUTPUT)include find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.cmd' -delete install: $(ALL_PROGRAMS) diff --git a/tools/hv/hv_fcopy_uio_daemon.c b/tools/hv/hv_fcopy_uio_daemon.c index 0198321d14a2..92e8307b2a46 100644 --- a/tools/hv/hv_fcopy_uio_daemon.c +++ b/tools/hv/hv_fcopy_uio_daemon.c @@ -35,7 +35,10 @@ #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) -#define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio" +#define FCOPY_DEVICE_PATH(subdir) \ + "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/" #subdir +#define FCOPY_UIO_PATH FCOPY_DEVICE_PATH(uio) +#define FCOPY_CHANNELS_PATH FCOPY_DEVICE_PATH(channels) #define FCOPY_VER_COUNT 1 static const int fcopy_versions[] = { @@ -47,9 +50,62 @@ static const int fw_versions[] = { UTIL_FW_VERSION }; -#define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */ +static uint32_t get_ring_buffer_size(void) +{ + char ring_path[PATH_MAX]; + DIR *dir; + struct dirent *entry; + struct stat st; + uint32_t ring_size = 0; + int retry_count = 0; -static unsigned char desc[HV_RING_SIZE]; + /* Find the channel directory */ + dir = opendir(FCOPY_CHANNELS_PATH); + if (!dir) { + usleep(100 * 1000); /* Avoid race with kernel, wait 100ms and retry once */ + dir = opendir(FCOPY_CHANNELS_PATH); + if (!dir) { + syslog(LOG_ERR, "Failed to open channels directory: %s", strerror(errno)); + return 0; + } + } + +retry_once: + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && + strcmp(entry->d_name, "..") != 0) { + snprintf(ring_path, sizeof(ring_path), "%s/%s/ring", + FCOPY_CHANNELS_PATH, entry->d_name); + + if (stat(ring_path, &st) == 0) { + /* + * stat returns size of Tx, Rx rings combined, + * so take half of it for individual ring size. + */ + ring_size = (uint32_t)st.st_size / 2; + syslog(LOG_INFO, "Ring buffer size from %s: %u bytes", + ring_path, ring_size); + break; + } + } + } + + if (!ring_size && retry_count == 0) { + retry_count = 1; + rewinddir(dir); + usleep(100 * 1000); /* Wait 100ms and retry once */ + goto retry_once; + } + + closedir(dir); + + if (!ring_size) + syslog(LOG_ERR, "Could not determine ring size"); + + return ring_size; +} + +static unsigned char *desc; static int target_fd; static char target_fname[PATH_MAX]; @@ -62,8 +118,11 @@ static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags) filesize = 0; p = path_name; - snprintf(target_fname, sizeof(target_fname), "%s/%s", - path_name, file_name); + if (snprintf(target_fname, sizeof(target_fname), "%s/%s", + path_name, file_name) >= sizeof(target_fname)) { + syslog(LOG_ERR, "target file name is too long: %s/%s", path_name, file_name); + goto done; + } /* * Check to see if the path is already in place; if not, @@ -270,7 +329,7 @@ static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) { size_t len = 0; - while (len < dest_size) { + while (len < dest_size && *src) { if (src[len] < 0x80) dest[len++] = (char)(*src++); else @@ -282,27 +341,15 @@ static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) static int hv_fcopy_start(struct hv_start_fcopy *smsg_in) { + /* + * file_name and path_name should have same length with appropriate + * member of hv_start_fcopy. + */ + char file_name[W_MAX_PATH], path_name[W_MAX_PATH]; + setlocale(LC_ALL, "en_US.utf8"); - size_t file_size, path_size; - char *file_name, *path_name; - char *in_file_name = (char *)smsg_in->file_name; - char *in_path_name = (char *)smsg_in->path_name; - - file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1; - path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1; - - file_name = (char *)malloc(file_size * sizeof(char)); - path_name = (char *)malloc(path_size * sizeof(char)); - - if (!file_name || !path_name) { - free(file_name); - free(path_name); - syslog(LOG_ERR, "Can't allocate memory for file name and/or path name"); - return HV_E_FAIL; - } - - wcstoutf8(file_name, (__u16 *)in_file_name, file_size); - wcstoutf8(path_name, (__u16 *)in_path_name, path_size); + wcstoutf8(file_name, smsg_in->file_name, W_MAX_PATH - 1); + wcstoutf8(path_name, smsg_in->path_name, W_MAX_PATH - 1); return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags); } @@ -406,7 +453,7 @@ int main(int argc, char *argv[]) int daemonize = 1, long_index = 0, opt, ret = -EINVAL; struct vmbus_br txbr, rxbr; void *ring; - uint32_t len = HV_RING_SIZE; + uint32_t ring_size, len; char uio_name[NAME_MAX] = {0}; char uio_dev_path[PATH_MAX] = {0}; @@ -437,7 +484,20 @@ int main(int argc, char *argv[]) openlog("HV_UIO_FCOPY", 0, LOG_USER); syslog(LOG_INFO, "starting; pid is:%d", getpid()); - fcopy_get_first_folder(FCOPY_UIO, uio_name); + ring_size = get_ring_buffer_size(); + if (!ring_size) { + ret = -ENODEV; + goto exit; + } + + desc = malloc(ring_size * sizeof(unsigned char)); + if (!desc) { + syslog(LOG_ERR, "malloc failed for desc buffer"); + ret = -ENOMEM; + goto exit; + } + + fcopy_get_first_folder(FCOPY_UIO_PATH, uio_name); snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name); fcopy_fd = open(uio_dev_path, O_RDWR); @@ -445,17 +505,17 @@ int main(int argc, char *argv[]) syslog(LOG_ERR, "open %s failed; error: %d %s", uio_dev_path, errno, strerror(errno)); ret = fcopy_fd; - goto exit; + goto free_desc; } - ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE); + ring = vmbus_uio_map(&fcopy_fd, ring_size); if (!ring) { ret = errno; syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret)); goto close; } - vmbus_br_setup(&txbr, ring, HV_RING_SIZE); - vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE); + vmbus_br_setup(&txbr, ring, ring_size); + vmbus_br_setup(&rxbr, (char *)ring + ring_size, ring_size); rxbr.vbr->imask = 0; @@ -472,7 +532,7 @@ int main(int argc, char *argv[]) goto close; } - len = HV_RING_SIZE; + len = ring_size; ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len); if (unlikely(ret <= 0)) { /* This indicates a failure to communicate (or worse) */ @@ -492,6 +552,8 @@ int main(int argc, char *argv[]) } close: close(fcopy_fd); +free_desc: + free(desc); exit: return ret; } diff --git a/tools/include/linux/args.h b/tools/include/linux/args.h new file mode 100644 index 000000000000..2e8e65d975c7 --- /dev/null +++ b/tools/include/linux/args.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_ARGS_H +#define _LINUX_ARGS_H + +/* + * How do these macros work? + * + * In __COUNT_ARGS() _0 to _12 are just placeholders from the start + * in order to make sure _n is positioned over the correct number + * from 12 to 0 (depending on X, which is a variadic argument list). + * They serve no purpose other than occupying a position. Since each + * macro parameter must have a distinct identifier, those identifiers + * are as good as any. + * + * In COUNT_ARGS() we use actual integers, so __COUNT_ARGS() returns + * that as _n. + */ + +/* This counts to 15. Any more, it will return 16th argument. */ +#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _n, X...) _n +#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +/* Concatenate two parameters, but allow them to be expanded beforehand. */ +#define __CONCAT(a, b) a ## b +#define CONCATENATE(a, b) __CONCAT(a, b) + +#endif /* _LINUX_ARGS_H */ diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h index 14fd0ca9a6cd..a40cc861b3a7 100644 --- a/tools/include/linux/bits.h +++ b/tools/include/linux/bits.h @@ -2,16 +2,15 @@ #ifndef __LINUX_BITS_H #define __LINUX_BITS_H -#include #include #include -#include #define BIT_MASK(nr) (UL(1) << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BIT_ULL_MASK(nr) (ULL(1) << ((nr) % BITS_PER_LONG_LONG)) #define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) #define BITS_PER_BYTE 8 +#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) /* * Create a contiguous bitmask starting at bit position @l and ending at @@ -19,35 +18,72 @@ * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ #if !defined(__ASSEMBLY__) + +/* + * Missing asm support + * + * GENMASK_U*() and BIT_U*() depend on BITS_PER_TYPE() which relies on sizeof(), + * something not available in asm. Nevertheless, fixed width integers is a C + * concept. Assembly code can rely on the long and long long versions instead. + */ + #include #include +#include + #define GENMASK_INPUT_CHECK(h, l) BUILD_BUG_ON_ZERO(const_true((l) > (h))) -#else + +/* + * Generate a mask for the specified type @t. Additional checks are made to + * guarantee the value returned fits in that type, relying on + * -Wshift-count-overflow compiler check to detect incompatible arguments. + * For example, all these create build errors or warnings: + * + * - GENMASK(15, 20): wrong argument order + * - GENMASK(72, 15): doesn't fit unsigned long + * - GENMASK_U32(33, 15): doesn't fit in a u32 + */ +#define GENMASK_TYPE(t, h, l) \ + ((t)(GENMASK_INPUT_CHECK(h, l) + \ + (type_max(t) << (l) & \ + type_max(t) >> (BITS_PER_TYPE(t) - 1 - (h))))) + +#define GENMASK(h, l) GENMASK_TYPE(unsigned long, h, l) +#define GENMASK_ULL(h, l) GENMASK_TYPE(unsigned long long, h, l) + +#define GENMASK_U8(h, l) GENMASK_TYPE(u8, h, l) +#define GENMASK_U16(h, l) GENMASK_TYPE(u16, h, l) +#define GENMASK_U32(h, l) GENMASK_TYPE(u32, h, l) +#define GENMASK_U64(h, l) GENMASK_TYPE(u64, h, l) +#define GENMASK_U128(h, l) GENMASK_TYPE(u128, h, l) + +/* + * Fixed-type variants of BIT(), with additional checks like GENMASK_TYPE(). The + * following examples generate compiler warnings due to -Wshift-count-overflow: + * + * - BIT_U8(8) + * - BIT_U32(-1) + * - BIT_U32(40) + */ +#define BIT_INPUT_CHECK(type, nr) \ + BUILD_BUG_ON_ZERO(const_true((nr) >= BITS_PER_TYPE(type))) + +#define BIT_TYPE(type, nr) ((type)(BIT_INPUT_CHECK(type, nr) + BIT_ULL(nr))) + +#define BIT_U8(nr) BIT_TYPE(u8, nr) +#define BIT_U16(nr) BIT_TYPE(u16, nr) +#define BIT_U32(nr) BIT_TYPE(u32, nr) +#define BIT_U64(nr) BIT_TYPE(u64, nr) + +#else /* defined(__ASSEMBLY__) */ + /* * BUILD_BUG_ON_ZERO is not available in h files included from asm files, * disable the input check if that is the case. */ -#define GENMASK_INPUT_CHECK(h, l) 0 -#endif +#define GENMASK(h, l) __GENMASK(h, l) +#define GENMASK_ULL(h, l) __GENMASK_ULL(h, l) -#define GENMASK(h, l) \ - (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) -#define GENMASK_ULL(h, l) \ - (GENMASK_INPUT_CHECK(h, l) + __GENMASK_ULL(h, l)) - -#if !defined(__ASSEMBLY__) -/* - * Missing asm support - * - * __GENMASK_U128() depends on _BIT128() which would not work - * in the asm code, as it shifts an 'unsigned __int128' data - * type instead of direct representation of 128 bit constants - * such as long and unsigned long. The fundamental problem is - * that a 128 bit constant will get silently truncated by the - * gcc compiler. - */ -#define GENMASK_U128(h, l) \ - (GENMASK_INPUT_CHECK(h, l) + __GENMASK_U128(h, l)) -#endif +#endif /* !defined(__ASSEMBLY__) */ #endif /* __LINUX_BITS_H */ diff --git a/tools/include/linux/build_bug.h b/tools/include/linux/build_bug.h index b4898ff085de..ab2aa97bd8ce 100644 --- a/tools/include/linux/build_bug.h +++ b/tools/include/linux/build_bug.h @@ -4,17 +4,17 @@ #include -#ifdef __CHECKER__ -#define BUILD_BUG_ON_ZERO(e) (0) -#else /* __CHECKER__ */ /* * Force a compilation error if condition is true, but also produce a * result (of value 0 and type int), so the expression can be used * e.g. in a structure initializer (or where-ever else comma expressions * aren't permitted). + * + * Take an error message as an optional second argument. If omitted, + * default to the stringification of the tested expression. */ -#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); }))) -#endif /* __CHECKER__ */ +#define BUILD_BUG_ON_ZERO(e, ...) \ + __BUILD_BUG_ON_ZERO_MSG(e, ##__VA_ARGS__, #e " is true") /* Force a compilation error if a constant expression is not a power of 2 */ #define __BUILD_BUG_ON_NOT_POWER_OF_2(n) \ diff --git a/tools/include/linux/cfi_types.h b/tools/include/linux/cfi_types.h index 6b8713675765..685f7181780f 100644 --- a/tools/include/linux/cfi_types.h +++ b/tools/include/linux/cfi_types.h @@ -41,5 +41,28 @@ SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) #endif +#else /* __ASSEMBLY__ */ + +#ifdef CONFIG_CFI_CLANG +#define DEFINE_CFI_TYPE(name, func) \ + /* \ + * Force a reference to the function so the compiler generates \ + * __kcfi_typeid_. \ + */ \ + __ADDRESSABLE(func); \ + /* u32 name __ro_after_init = __kcfi_typeid_ */ \ + extern u32 name; \ + asm ( \ + " .pushsection .data..ro_after_init,\"aw\",\%progbits \n" \ + " .type " #name ",\%object \n" \ + " .globl " #name " \n" \ + " .p2align 2, 0x0 \n" \ + #name ": \n" \ + " .4byte __kcfi_typeid_" #func " \n" \ + " .size " #name ", 4 \n" \ + " .popsection \n" \ + ); +#endif + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_CFI_TYPES_H */ diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index d627e66a04a6..33411ca0cc90 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -244,6 +244,14 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s __asm__ ("" : "=r" (var) : "0" (var)) #endif +#ifndef __BUILD_BUG_ON_ZERO_MSG +#if defined(__clang__) +#define __BUILD_BUG_ON_ZERO_MSG(e, msg, ...) ((int)(sizeof(struct { int:(-!!(e)); }))) +#else +#define __BUILD_BUG_ON_ZERO_MSG(e, msg, ...) ((int)sizeof(struct {_Static_assert(!(e), msg);})) +#endif +#endif + #endif /* __ASSEMBLY__ */ #endif /* _TOOLS_LINUX_COMPILER_H */ diff --git a/tools/include/linux/kallsyms.h b/tools/include/linux/kallsyms.h index 5a37ccbec54f..f61a01dd7eb7 100644 --- a/tools/include/linux/kallsyms.h +++ b/tools/include/linux/kallsyms.h @@ -18,6 +18,7 @@ static inline const char *kallsyms_lookup(unsigned long addr, return NULL; } +#ifdef HAVE_BACKTRACE_SUPPORT #include #include static inline void print_ip_sym(const char *loglvl, unsigned long ip) @@ -30,5 +31,8 @@ static inline void print_ip_sym(const char *loglvl, unsigned long ip) free(name); } +#else +static inline void print_ip_sym(const char *loglvl, unsigned long ip) {} +#endif #endif diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index c335ce0bd195..143c2d2c2ba6 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -23,8 +23,7 @@ else Q=@ endif -nolibc_arch := $(patsubst arm64,aarch64,$(ARCH)) -arch_file := arch-$(nolibc_arch).h +arch_file := arch-$(ARCH).h all_files := \ compiler.h \ crt.h \ @@ -91,18 +90,12 @@ help: @echo " OUTPUT = $(OUTPUT)" @echo "" -# Note: when ARCH is "x86" we concatenate both x86_64 and i386 headers: $(Q)mkdir -p $(OUTPUT)sysroot $(Q)mkdir -p $(OUTPUT)sysroot/include $(Q)cp --parents $(all_files) $(OUTPUT)sysroot/include/ - $(Q)if [ "$(ARCH)" = "x86" ]; then \ - sed -e \ - 's,^#ifndef _NOLIBC_ARCH_X86_64_H,#if !defined(_NOLIBC_ARCH_X86_64_H) \&\& defined(__x86_64__),' \ - arch-x86_64.h; \ - sed -e \ - 's,^#ifndef _NOLIBC_ARCH_I386_H,#if !defined(_NOLIBC_ARCH_I386_H) \&\& !defined(__x86_64__),' \ - arch-i386.h; \ + $(Q)if [ "$(ARCH)" = "i386" -o "$(ARCH)" = "x86_64" ]; then \ + cat arch-x86.h; \ elif [ -e "$(arch_file)" ]; then \ cat $(arch_file); \ else \ @@ -114,11 +107,8 @@ headers_standalone: headers $(Q)$(MAKE) -C $(srctree) headers $(Q)$(MAKE) -C $(srctree) headers_install INSTALL_HDR_PATH=$(OUTPUT)sysroot -# GCC uses "s390", clang "systemz" -CLANG_CROSS_FLAGS := $(subst --target=s390-linux,--target=systemz-linux,$(CLANG_CROSS_FLAGS)) - headers_check: headers_standalone - for header in $(filter-out crt.h std.h,$(all_files)); do \ + $(Q)for header in $(filter-out crt.h std.h,$(all_files)); do \ $(CC) $(CLANG_CROSS_FLAGS) -Wall -Werror -nostdinc -fsyntax-only -x c /dev/null \ -I$(or $(objtree),$(srctree))/usr/include -include $$header -include $$header || exit 1; \ done diff --git a/tools/include/nolibc/arch-aarch64.h b/tools/include/nolibc/arch-arm64.h similarity index 97% rename from tools/include/nolibc/arch-aarch64.h rename to tools/include/nolibc/arch-arm64.h index 937a348da42e..02a3f74c8ec8 100644 --- a/tools/include/nolibc/arch-aarch64.h +++ b/tools/include/nolibc/arch-arm64.h @@ -1,16 +1,16 @@ /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ /* - * AARCH64 specific definitions for NOLIBC + * ARM64 specific definitions for NOLIBC * Copyright (C) 2017-2022 Willy Tarreau */ -#ifndef _NOLIBC_ARCH_AARCH64_H -#define _NOLIBC_ARCH_AARCH64_H +#ifndef _NOLIBC_ARCH_ARM64_H +#define _NOLIBC_ARCH_ARM64_H #include "compiler.h" #include "crt.h" -/* Syscalls for AARCH64 : +/* Syscalls for ARM64 : * - registers are 64-bit * - stack is 16-byte aligned * - syscall number is passed in x8 @@ -150,4 +150,4 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _s ); __nolibc_entrypoint_epilogue(); } -#endif /* _NOLIBC_ARCH_AARCH64_H */ +#endif /* _NOLIBC_ARCH_ARM64_H */ diff --git a/tools/include/nolibc/arch-i386.h b/tools/include/nolibc/arch-i386.h deleted file mode 100644 index 7c9b38e96418..000000000000 --- a/tools/include/nolibc/arch-i386.h +++ /dev/null @@ -1,178 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ -/* - * i386 specific definitions for NOLIBC - * Copyright (C) 2017-2022 Willy Tarreau - */ - -#ifndef _NOLIBC_ARCH_I386_H -#define _NOLIBC_ARCH_I386_H - -#include "compiler.h" -#include "crt.h" - -/* Syscalls for i386 : - * - mostly similar to x86_64 - * - registers are 32-bit - * - syscall number is passed in eax - * - arguments are in ebx, ecx, edx, esi, edi, ebp respectively - * - all registers are preserved (except eax of course) - * - the system call is performed by calling int $0x80 - * - syscall return comes in eax - * - the arguments are cast to long and assigned into the target registers - * which are then simply passed as registers to the asm code, so that we - * don't have to experience issues with register constraints. - * - the syscall number is always specified last in order to allow to force - * some registers before (gcc refuses a %-register at the last position). - * - * Also, i386 supports the old_select syscall if newselect is not available - */ -#define __ARCH_WANT_SYS_OLD_SELECT - -#define my_syscall0(num) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall1(num, arg1) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - register long _arg1 __asm__ ("ebx") = (long)(arg1); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "r"(_arg1), \ - "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall2(num, arg1, arg2) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - register long _arg1 __asm__ ("ebx") = (long)(arg1); \ - register long _arg2 __asm__ ("ecx") = (long)(arg2); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "r"(_arg1), "r"(_arg2), \ - "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall3(num, arg1, arg2, arg3) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - register long _arg1 __asm__ ("ebx") = (long)(arg1); \ - register long _arg2 __asm__ ("ecx") = (long)(arg2); \ - register long _arg3 __asm__ ("edx") = (long)(arg3); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ - "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall4(num, arg1, arg2, arg3, arg4) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - register long _arg1 __asm__ ("ebx") = (long)(arg1); \ - register long _arg2 __asm__ ("ecx") = (long)(arg2); \ - register long _arg3 __asm__ ("edx") = (long)(arg3); \ - register long _arg4 __asm__ ("esi") = (long)(arg4); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ - "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ -({ \ - long _ret; \ - register long _num __asm__ ("eax") = (num); \ - register long _arg1 __asm__ ("ebx") = (long)(arg1); \ - register long _arg2 __asm__ ("ecx") = (long)(arg2); \ - register long _arg3 __asm__ ("edx") = (long)(arg3); \ - register long _arg4 __asm__ ("esi") = (long)(arg4); \ - register long _arg5 __asm__ ("edi") = (long)(arg5); \ - \ - __asm__ volatile ( \ - "int $0x80\n" \ - : "=a" (_ret) \ - : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ - "0"(_num) \ - : "memory", "cc" \ - ); \ - _ret; \ -}) - -#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ -({ \ - long _eax = (long)(num); \ - long _arg6 = (long)(arg6); /* Always in memory */ \ - __asm__ volatile ( \ - "pushl %[_arg6]\n\t" \ - "pushl %%ebp\n\t" \ - "movl 4(%%esp),%%ebp\n\t" \ - "int $0x80\n\t" \ - "popl %%ebp\n\t" \ - "addl $4,%%esp\n\t" \ - : "+a"(_eax) /* %eax */ \ - : "b"(arg1), /* %ebx */ \ - "c"(arg2), /* %ecx */ \ - "d"(arg3), /* %edx */ \ - "S"(arg4), /* %esi */ \ - "D"(arg5), /* %edi */ \ - [_arg6]"m"(_arg6) /* memory */ \ - : "memory", "cc" \ - ); \ - _eax; \ -}) - -/* startup code */ -/* - * i386 System V ABI mandates: - * 1) last pushed argument must be 16-byte aligned. - * 2) The deepest stack frame should be set to zero - * - */ -void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void) -{ - __asm__ volatile ( - "xor %ebp, %ebp\n" /* zero the stack frame */ - "mov %esp, %eax\n" /* save stack pointer to %eax, as arg1 of _start_c */ - "sub $12, %esp\n" /* sub 12 to keep it aligned after the push %eax */ - "push %eax\n" /* push arg1 on stack to support plain stack modes too */ - "call _start_c\n" /* transfer to c runtime */ - "hlt\n" /* ensure it does not return */ - ); - __nolibc_entrypoint_epilogue(); -} - -#endif /* _NOLIBC_ARCH_I386_H */ diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index 753a8ed2cf69..0cbac63b249a 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -10,7 +10,7 @@ #include "compiler.h" #include "crt.h" -#if !defined(_ABIO32) +#if !defined(_ABIO32) && !defined(_ABIN32) && !defined(_ABI64) #error Unsupported MIPS ABI #endif @@ -32,11 +32,32 @@ * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. + * + * Syscalls for MIPS ABI N32, same as ABI O32 with the following differences : + * - arguments are in a0, a1, a2, a3, t0, t1, t2, t3. + * t0..t3 are also known as a4..a7. + * - stack is 16-byte aligned */ +#if defined(_ABIO32) + #define _NOLIBC_SYSCALL_CLOBBERLIST \ "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" +#define _NOLIBC_SYSCALL_STACK_RESERVE "addiu $sp, $sp, -32\n" +#define _NOLIBC_SYSCALL_STACK_UNRESERVE "addiu $sp, $sp, 32\n" + +#else /* _ABIN32 || _ABI64 */ + +/* binutils, GCC and clang disagree about register aliases, use numbers instead. */ +#define _NOLIBC_SYSCALL_CLOBBERLIST \ + "memory", "cc", "at", "v1", \ + "10", "11", "12", "13", "14", "15", "24", "25" + +#define _NOLIBC_SYSCALL_STACK_RESERVE +#define _NOLIBC_SYSCALL_STACK_UNRESERVE + +#endif /* _ABIO32 */ #define my_syscall0(num) \ ({ \ @@ -44,9 +65,9 @@ register long _arg4 __asm__ ("a3"); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r"(_num), "=r"(_arg4) \ : "r"(_num) \ : _NOLIBC_SYSCALL_CLOBBERLIST \ @@ -61,9 +82,9 @@ register long _arg4 __asm__ ("a3"); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1) \ @@ -80,9 +101,9 @@ register long _arg4 __asm__ ("a3"); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2) \ @@ -100,9 +121,9 @@ register long _arg4 __asm__ ("a3"); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3) \ @@ -120,9 +141,9 @@ register long _arg4 __asm__ ("a3") = (long)(arg4); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r" (_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \ @@ -131,6 +152,8 @@ _arg4 ? -_num : _num; \ }) +#if defined(_ABIO32) + #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ register long _num __asm__ ("v0") = (num); \ @@ -141,10 +164,10 @@ register long _arg5 = (long)(arg5); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "sw %7, 16($sp)\n" \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r" (_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \ @@ -164,11 +187,11 @@ register long _arg6 = (long)(arg6); \ \ __asm__ volatile ( \ - "addiu $sp, $sp, -32\n" \ + _NOLIBC_SYSCALL_STACK_RESERVE \ "sw %7, 16($sp)\n" \ "sw %8, 20($sp)\n" \ "syscall\n" \ - "addiu $sp, $sp, 32\n" \ + _NOLIBC_SYSCALL_STACK_UNRESERVE \ : "=r" (_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ @@ -178,28 +201,68 @@ _arg4 ? -_num : _num; \ }) +#else /* _ABIN32 || _ABI64 */ + +#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("v0") = (num); \ + register long _arg1 __asm__ ("$4") = (long)(arg1); \ + register long _arg2 __asm__ ("$5") = (long)(arg2); \ + register long _arg3 __asm__ ("$6") = (long)(arg3); \ + register long _arg4 __asm__ ("$7") = (long)(arg4); \ + register long _arg5 __asm__ ("$8") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "syscall\n" \ + : "=r" (_num), "=r"(_arg4) \ + : "0"(_num), \ + "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg4 ? -_num : _num; \ +}) + +#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("v0") = (num); \ + register long _arg1 __asm__ ("$4") = (long)(arg1); \ + register long _arg2 __asm__ ("$5") = (long)(arg2); \ + register long _arg3 __asm__ ("$6") = (long)(arg3); \ + register long _arg4 __asm__ ("$7") = (long)(arg4); \ + register long _arg5 __asm__ ("$8") = (long)(arg5); \ + register long _arg6 __asm__ ("$9") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "syscall\n" \ + : "=r" (_num), "=r"(_arg4) \ + : "0"(_num), \ + "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ + "r"(_arg6) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg4 ? -_num : _num; \ +}) + +#endif /* _ABIO32 */ + /* startup code, note that it's called __start on MIPS */ void __start(void); void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector __start(void) { __asm__ volatile ( - ".set push\n" - ".set noreorder\n" - "bal 1f\n" /* prime $ra for .cpload */ - "nop\n" - "1:\n" - ".cpload $ra\n" "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */ - "addiu $sp, $sp, -4\n" /* space for .cprestore to store $gp */ - ".cprestore 0\n" - "li $t0, -8\n" - "and $sp, $sp, $t0\n" /* $sp must be 8-byte aligned */ +#if defined(_ABIO32) "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */ +#endif /* _ABIO32 */ "lui $t9, %hi(_start_c)\n" /* ABI requires current function address in $t9 */ "ori $t9, %lo(_start_c)\n" +#if defined(_ABI64) + "lui $t0, %highest(_start_c)\n" + "ori $t0, %higher(_start_c)\n" + "dsll $t0, 0x20\n" + "or $t9, $t0\n" +#endif /* _ABI64 */ "jalr $t9\n" /* transfer to c runtime */ - " nop\n" /* delayed slot */ - ".set pop\n" ); __nolibc_entrypoint_epilogue(); } diff --git a/tools/include/nolibc/arch-sh.h b/tools/include/nolibc/arch-sh.h new file mode 100644 index 000000000000..a96b8914607e --- /dev/null +++ b/tools/include/nolibc/arch-sh.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * SuperH specific definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh + */ + +#ifndef _NOLIBC_ARCH_SH_H +#define _NOLIBC_ARCH_SH_H + +#include "compiler.h" +#include "crt.h" + +/* + * Syscalls for SuperH: + * - registers are 32bit wide + * - syscall number is passed in r3 + * - arguments are in r4, r5, r6, r7, r0, r1, r2 + * - the system call is performed by calling trapa #31 + * - syscall return value is in r0 + */ + +#define my_syscall0(num) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall1(num, arg1) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall2(num, arg1, arg2) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + register long _arg2 __asm__ ("r5") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1), "r"(_arg2) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall3(num, arg1, arg2, arg3) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + register long _arg2 __asm__ ("r5") = (long)(arg2); \ + register long _arg3 __asm__ ("r6") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + register long _arg2 __asm__ ("r5") = (long)(arg2); \ + register long _arg3 __asm__ ("r6") = (long)(arg3); \ + register long _arg4 __asm__ ("r7") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + register long _arg2 __asm__ ("r5") = (long)(arg2); \ + register long _arg3 __asm__ ("r6") = (long)(arg3); \ + register long _arg4 __asm__ ("r7") = (long)(arg4); \ + register long _arg5 __asm__ ("r0") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("r3") = (num); \ + register long _ret __asm__ ("r0"); \ + register long _arg1 __asm__ ("r4") = (long)(arg1); \ + register long _arg2 __asm__ ("r5") = (long)(arg2); \ + register long _arg3 __asm__ ("r6") = (long)(arg3); \ + register long _arg4 __asm__ ("r7") = (long)(arg4); \ + register long _arg5 __asm__ ("r0") = (long)(arg5); \ + register long _arg6 __asm__ ("r1") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "trapa #31" \ + : "=r"(_ret) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_arg6) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +/* startup code */ +void _start_wrapper(void); +void __attribute__((weak,noreturn)) __nolibc_entrypoint __no_stack_protector _start_wrapper(void) +{ + __asm__ volatile ( + ".global _start\n" /* The C function will have a prologue, */ + ".type _start, @function\n" /* corrupting "sp" */ + ".weak _start\n" + "_start:\n" + + "mov sp, r4\n" /* save argc pointer to r4, as arg1 of _start_c */ + "bsr _start_c\n" /* transfer to c runtime */ + "nop\n" /* delay slot */ + + ".size _start, .-_start\n" + ); + __nolibc_entrypoint_epilogue(); +} + +#endif /* _NOLIBC_ARCH_SH_H */ diff --git a/tools/include/nolibc/arch-sparc.h b/tools/include/nolibc/arch-sparc.h index 1435172f3dfe..ca420d843e25 100644 --- a/tools/include/nolibc/arch-sparc.h +++ b/tools/include/nolibc/arch-sparc.h @@ -188,4 +188,20 @@ pid_t sys_fork(void) } #define sys_fork sys_fork +static __attribute__((unused)) +pid_t sys_vfork(void) +{ + pid_t parent, ret; + + parent = getpid(); + ret = my_syscall0(__NR_vfork); + + /* The syscall returns the parent pid in the child instead of 0 */ + if (ret == parent) + return 0; + else + return ret; +} +#define sys_vfork sys_vfork + #endif /* _NOLIBC_ARCH_SPARC_H */ diff --git a/tools/include/nolibc/arch-x86_64.h b/tools/include/nolibc/arch-x86.h similarity index 53% rename from tools/include/nolibc/arch-x86_64.h rename to tools/include/nolibc/arch-x86.h index 67305e24dbef..d3efc0c3b8ad 100644 --- a/tools/include/nolibc/arch-x86_64.h +++ b/tools/include/nolibc/arch-x86.h @@ -1,15 +1,184 @@ /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ /* - * x86_64 specific definitions for NOLIBC - * Copyright (C) 2017-2022 Willy Tarreau + * x86 specific definitions for NOLIBC (both 32- and 64-bit) + * Copyright (C) 2017-2025 Willy Tarreau */ -#ifndef _NOLIBC_ARCH_X86_64_H -#define _NOLIBC_ARCH_X86_64_H +#ifndef _NOLIBC_ARCH_X86_H +#define _NOLIBC_ARCH_X86_H #include "compiler.h" #include "crt.h" +#if !defined(__x86_64__) + +/* Syscalls for i386 : + * - mostly similar to x86_64 + * - registers are 32-bit + * - syscall number is passed in eax + * - arguments are in ebx, ecx, edx, esi, edi, ebp respectively + * - all registers are preserved (except eax of course) + * - the system call is performed by calling int $0x80 + * - syscall return comes in eax + * - the arguments are cast to long and assigned into the target registers + * which are then simply passed as registers to the asm code, so that we + * don't have to experience issues with register constraints. + * - the syscall number is always specified last in order to allow to force + * some registers before (gcc refuses a %-register at the last position). + * + * Also, i386 supports the old_select syscall if newselect is not available + */ +#define __ARCH_WANT_SYS_OLD_SELECT + +#define my_syscall0(num) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall1(num, arg1) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + register long _arg1 __asm__ ("ebx") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "r"(_arg1), \ + "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall2(num, arg1, arg2) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + register long _arg1 __asm__ ("ebx") = (long)(arg1); \ + register long _arg2 __asm__ ("ecx") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "r"(_arg1), "r"(_arg2), \ + "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall3(num, arg1, arg2, arg3) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + register long _arg1 __asm__ ("ebx") = (long)(arg1); \ + register long _arg2 __asm__ ("ecx") = (long)(arg2); \ + register long _arg3 __asm__ ("edx") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ + "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + register long _arg1 __asm__ ("ebx") = (long)(arg1); \ + register long _arg2 __asm__ ("ecx") = (long)(arg2); \ + register long _arg3 __asm__ ("edx") = (long)(arg3); \ + register long _arg4 __asm__ ("esi") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + long _ret; \ + register long _num __asm__ ("eax") = (num); \ + register long _arg1 __asm__ ("ebx") = (long)(arg1); \ + register long _arg2 __asm__ ("ecx") = (long)(arg2); \ + register long _arg3 __asm__ ("edx") = (long)(arg3); \ + register long _arg4 __asm__ ("esi") = (long)(arg4); \ + register long _arg5 __asm__ ("edi") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "int $0x80\n" \ + : "=a" (_ret) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ + "0"(_num) \ + : "memory", "cc" \ + ); \ + _ret; \ +}) + +#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + long _eax = (long)(num); \ + long _arg6 = (long)(arg6); /* Always in memory */ \ + __asm__ volatile ( \ + "pushl %[_arg6]\n\t" \ + "pushl %%ebp\n\t" \ + "movl 4(%%esp),%%ebp\n\t" \ + "int $0x80\n\t" \ + "popl %%ebp\n\t" \ + "addl $4,%%esp\n\t" \ + : "+a"(_eax) /* %eax */ \ + : "b"(arg1), /* %ebx */ \ + "c"(arg2), /* %ecx */ \ + "d"(arg3), /* %edx */ \ + "S"(arg4), /* %esi */ \ + "D"(arg5), /* %edi */ \ + [_arg6]"m"(_arg6) /* memory */ \ + : "memory", "cc" \ + ); \ + _eax; \ +}) + +/* startup code */ +/* + * i386 System V ABI mandates: + * 1) last pushed argument must be 16-byte aligned. + * 2) The deepest stack frame should be set to zero + * + */ +void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void) +{ + __asm__ volatile ( + "xor %ebp, %ebp\n" /* zero the stack frame */ + "mov %esp, %eax\n" /* save stack pointer to %eax, as arg1 of _start_c */ + "sub $12, %esp\n" /* sub 12 to keep it aligned after the push %eax */ + "push %eax\n" /* push arg1 on stack to support plain stack modes too */ + "call _start_c\n" /* transfer to c runtime */ + "hlt\n" /* ensure it does not return */ + ); + __nolibc_entrypoint_epilogue(); +} + +#else /* !defined(__x86_64__) */ + /* Syscalls for x86_64 : * - registers are 64-bit * - syscall number is passed in rax @@ -214,4 +383,5 @@ __asm__ ( "retq\n" ); -#endif /* _NOLIBC_ARCH_X86_64_H */ +#endif /* !defined(__x86_64__) */ +#endif /* _NOLIBC_ARCH_X86_H */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index d20b2304aac2..426c89198135 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -15,14 +15,12 @@ #ifndef _NOLIBC_ARCH_H #define _NOLIBC_ARCH_H -#if defined(__x86_64__) -#include "arch-x86_64.h" -#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) -#include "arch-i386.h" +#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) +#include "arch-x86.h" #elif defined(__ARM_EABI__) #include "arch-arm.h" #elif defined(__aarch64__) -#include "arch-aarch64.h" +#include "arch-arm64.h" #elif defined(__mips__) #include "arch-mips.h" #elif defined(__powerpc__) @@ -37,6 +35,8 @@ #include "arch-sparc.h" #elif defined(__m68k__) #include "arch-m68k.h" +#elif defined(__sh__) +#include "arch-sh.h" #else #error Unsupported Architecture #endif diff --git a/tools/include/nolibc/std.h b/tools/include/nolibc/std.h index adda7333d12e..ba950f0e7338 100644 --- a/tools/include/nolibc/std.h +++ b/tools/include/nolibc/std.h @@ -16,6 +16,8 @@ #include "stdint.h" #include "stddef.h" +#include + /* those are commonly provided by sys/types.h */ typedef unsigned int dev_t; typedef unsigned long ino_t; @@ -27,6 +29,6 @@ typedef unsigned long nlink_t; typedef signed long off_t; typedef signed long blksize_t; typedef signed long blkcnt_t; -typedef signed long time_t; +typedef __kernel_old_time_t time_t; #endif /* _NOLIBC_STD_H */ diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index c470d334ef3f..7630234408c5 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -358,11 +358,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char n -= w; while (width-- > w) { if (cb(state, " ", 1) != 0) - break; + return -1; written += 1; } if (cb(state, outstr, w) != 0) - break; + return -1; } written += len; diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 9556c69a6ae1..295e71d34aba 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -22,6 +22,7 @@ #include #include #include /* for O_* and AT_* */ +#include /* for clone_args */ #include /* for statx() */ #include "errno.h" @@ -139,7 +140,7 @@ int chdir(const char *path) static __attribute__((unused)) int sys_chmod(const char *path, mode_t mode) { -#ifdef __NR_fchmodat +#if defined(__NR_fchmodat) return my_syscall4(__NR_fchmodat, AT_FDCWD, path, mode, 0); #elif defined(__NR_chmod) return my_syscall2(__NR_chmod, path, mode); @@ -162,7 +163,7 @@ int chmod(const char *path, mode_t mode) static __attribute__((unused)) int sys_chown(const char *path, uid_t owner, gid_t group) { -#ifdef __NR_fchownat +#if defined(__NR_fchownat) return my_syscall5(__NR_fchownat, AT_FDCWD, path, owner, group, 0); #elif defined(__NR_chown) return my_syscall3(__NR_chown, path, owner, group); @@ -236,7 +237,7 @@ int dup(int fd) static __attribute__((unused)) int sys_dup2(int old, int new) { -#ifdef __NR_dup3 +#if defined(__NR_dup3) return my_syscall3(__NR_dup3, old, new, 0); #elif defined(__NR_dup2) return my_syscall2(__NR_dup2, old, new); @@ -256,7 +257,7 @@ int dup2(int old, int new) * int dup3(int old, int new, int flags); */ -#ifdef __NR_dup3 +#if defined(__NR_dup3) static __attribute__((unused)) int sys_dup3(int old, int new, int flags) { @@ -320,7 +321,7 @@ void exit(int status) static __attribute__((unused)) pid_t sys_fork(void) { -#ifdef __NR_clone +#if defined(__NR_clone) /* note: some archs only have clone() and not fork(). Different archs * have a different API, but most archs have the flags on first arg and * will not use the rest with no other flag. @@ -340,6 +341,34 @@ pid_t fork(void) return __sysret(sys_fork()); } +#ifndef sys_vfork +static __attribute__((unused)) +pid_t sys_vfork(void) +{ +#if defined(__NR_vfork) + return my_syscall0(__NR_vfork); +#elif defined(__NR_clone3) + /* + * clone() could be used but has different argument orders per + * architecture. + */ + struct clone_args args = { + .flags = CLONE_VM | CLONE_VFORK, + .exit_signal = SIGCHLD, + }; + + return my_syscall2(__NR_clone3, &args, sizeof(args)); +#else + return __nolibc_enosys(__func__); +#endif +} +#endif + +static __attribute__((unused)) +pid_t vfork(void) +{ + return __sysret(sys_vfork()); +} /* * int fsync(int fd); @@ -382,7 +411,7 @@ int getdents64(int fd, struct linux_dirent64 *dirp, int count) static __attribute__((unused)) uid_t sys_geteuid(void) { -#ifdef __NR_geteuid32 +#if defined(__NR_geteuid32) return my_syscall0(__NR_geteuid32); #else return my_syscall0(__NR_geteuid); @@ -500,7 +529,7 @@ int getpagesize(void) static __attribute__((unused)) uid_t sys_getuid(void) { -#ifdef __NR_getuid32 +#if defined(__NR_getuid32) return my_syscall0(__NR_getuid32); #else return my_syscall0(__NR_getuid); @@ -538,7 +567,7 @@ int kill(pid_t pid, int signal) static __attribute__((unused)) int sys_link(const char *old, const char *new) { -#ifdef __NR_linkat +#if defined(__NR_linkat) return my_syscall5(__NR_linkat, AT_FDCWD, old, AT_FDCWD, new, 0); #elif defined(__NR_link) return my_syscall2(__NR_link, old, new); @@ -561,7 +590,7 @@ int link(const char *old, const char *new) static __attribute__((unused)) off_t sys_lseek(int fd, off_t offset, int whence) { -#ifdef __NR_lseek +#if defined(__NR_lseek) return my_syscall3(__NR_lseek, fd, offset, whence); #else return __nolibc_enosys(__func__, fd, offset, whence); @@ -572,7 +601,7 @@ static __attribute__((unused)) int sys_llseek(int fd, unsigned long offset_high, unsigned long offset_low, __kernel_loff_t *result, int whence) { -#ifdef __NR_llseek +#if defined(__NR_llseek) return my_syscall5(__NR_llseek, fd, offset_high, offset_low, result, whence); #else return __nolibc_enosys(__func__, fd, offset_high, offset_low, result, whence); @@ -609,7 +638,7 @@ off_t lseek(int fd, off_t offset, int whence) static __attribute__((unused)) int sys_mkdir(const char *path, mode_t mode) { -#ifdef __NR_mkdirat +#if defined(__NR_mkdirat) return my_syscall3(__NR_mkdirat, AT_FDCWD, path, mode); #elif defined(__NR_mkdir) return my_syscall2(__NR_mkdir, path, mode); @@ -631,7 +660,7 @@ int mkdir(const char *path, mode_t mode) static __attribute__((unused)) int sys_rmdir(const char *path) { -#ifdef __NR_rmdir +#if defined(__NR_rmdir) return my_syscall1(__NR_rmdir, path); #elif defined(__NR_unlinkat) return my_syscall3(__NR_unlinkat, AT_FDCWD, path, AT_REMOVEDIR); @@ -654,7 +683,7 @@ int rmdir(const char *path) static __attribute__((unused)) long sys_mknod(const char *path, mode_t mode, dev_t dev) { -#ifdef __NR_mknodat +#if defined(__NR_mknodat) return my_syscall4(__NR_mknodat, AT_FDCWD, path, mode, dev); #elif defined(__NR_mknod) return my_syscall3(__NR_mknod, path, mode, dev); @@ -843,7 +872,7 @@ pid_t setsid(void) static __attribute__((unused)) int sys_symlink(const char *old, const char *new) { -#ifdef __NR_symlinkat +#if defined(__NR_symlinkat) return my_syscall3(__NR_symlinkat, old, AT_FDCWD, new); #elif defined(__NR_symlink) return my_syscall2(__NR_symlink, old, new); @@ -900,7 +929,7 @@ int umount2(const char *path, int flags) static __attribute__((unused)) int sys_unlink(const char *path) { -#ifdef __NR_unlinkat +#if defined(__NR_unlinkat) return my_syscall3(__NR_unlinkat, AT_FDCWD, path, 0); #elif defined(__NR_unlink) return my_syscall1(__NR_unlink, path); diff --git a/tools/include/nolibc/sys/wait.h b/tools/include/nolibc/sys/wait.h index 4d44e3da0ba8..56ddb806da7f 100644 --- a/tools/include/nolibc/sys/wait.h +++ b/tools/include/nolibc/sys/wait.h @@ -78,7 +78,7 @@ pid_t waitpid(pid_t pid, int *status, int options) ret = waitid(idtype, id, &info, options); if (ret) - return ret; + return -1; switch (info.si_code) { case 0: diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index fc387940d51f..d02bc44d2643 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -36,6 +36,8 @@ void __nolibc_timespec_kernel_to_user(const struct __kernel_timespec *kts, struc * int clock_getres(clockid_t clockid, struct timespec *res); * int clock_gettime(clockid_t clockid, struct timespec *tp); * int clock_settime(clockid_t clockid, const struct timespec *tp); + * int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *rqtp, + * struct timespec *rmtp) */ static __attribute__((unused)) @@ -107,6 +109,32 @@ int clock_settime(clockid_t clockid, struct timespec *tp) return __sysret(sys_clock_settime(clockid, tp)); } +static __attribute__((unused)) +int sys_clock_nanosleep(clockid_t clockid, int flags, const struct timespec *rqtp, + struct timespec *rmtp) +{ +#if defined(__NR_clock_nanosleep) + return my_syscall4(__NR_clock_nanosleep, clockid, flags, rqtp, rmtp); +#elif defined(__NR_clock_nanosleep_time64) + struct __kernel_timespec krqtp, krmtp; + int ret; + + __nolibc_timespec_user_to_kernel(rqtp, &krqtp); + ret = my_syscall4(__NR_clock_nanosleep_time64, clockid, flags, &krqtp, &krmtp); + if (rmtp) + __nolibc_timespec_kernel_to_user(&krmtp, rmtp); + return ret; +#else + return __nolibc_enosys(__func__, clockid, flags, rqtp, rmtp); +#endif +} + +static __attribute__((unused)) +int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *rqtp, + struct timespec *rmtp) +{ + return __sysret(sys_clock_nanosleep(clockid, flags, rqtp, rmtp)); +} static __inline__ double difftime(time_t time1, time_t time2) @@ -114,6 +142,12 @@ double difftime(time_t time1, time_t time2) return time1 - time2; } +static __inline__ +int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + return clock_nanosleep(CLOCK_REALTIME, 0, rqtp, rmtp); +} + static __attribute__((unused)) time_t time(time_t *tptr) diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 30904be544ed..16c6e9ec9451 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -128,7 +128,7 @@ typedef struct { int __fd = (fd); \ if (__fd >= 0) \ __set->fds[__fd / FD_SETIDXMASK] &= \ - ~(1U << (__fd & FX_SETBITMASK)); \ + ~(1U << (__fd & FD_SETBITMASK)); \ } while (0) #define FD_SET(fd, set) do { \ @@ -145,7 +145,7 @@ typedef struct { int __r = 0; \ if (__fd >= 0) \ __r = !!(__set->fds[__fd / FD_SETIDXMASK] & \ -1U << (__fd & FD_SET_BITMASK)); \ +1U << (__fd & FD_SETBITMASK)); \ __r; \ }) diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h index 2892a45023af..04e0077fb4c9 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -852,8 +852,14 @@ __SYSCALL(__NR_removexattrat, sys_removexattrat) #define __NR_open_tree_attr 467 __SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) +/* fs/inode.c */ +#define __NR_file_getattr 468 +__SYSCALL(__NR_file_getattr, sys_file_getattr) +#define __NR_file_setattr 469 +__SYSCALL(__NR_file_setattr, sys_file_setattr) + #undef __NR_syscalls -#define __NR_syscalls 468 +#define __NR_syscalls 470 /* * 32 bit systems traditionally used different diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h index 7fba37b94401..e63a71d3c607 100644 --- a/tools/include/uapi/drm/drm.h +++ b/tools/include/uapi/drm/drm.h @@ -905,13 +905,17 @@ struct drm_syncobj_destroy { }; #define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE (1 << 1) #define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE (1 << 1) struct drm_syncobj_handle { __u32 handle; __u32 flags; __s32 fd; __u32 pad; + + __u64 point; }; struct drm_syncobj_transfer { diff --git a/tools/include/uapi/linux/bits.h b/tools/include/uapi/linux/bits.h index 682b406e1067..a04afef9efca 100644 --- a/tools/include/uapi/linux/bits.h +++ b/tools/include/uapi/linux/bits.h @@ -4,9 +4,9 @@ #ifndef _UAPI_LINUX_BITS_H #define _UAPI_LINUX_BITS_H -#define __GENMASK(h, l) (((~_UL(0)) << (l)) & (~_UL(0) >> (BITS_PER_LONG - 1 - (h)))) +#define __GENMASK(h, l) (((~_UL(0)) << (l)) & (~_UL(0) >> (__BITS_PER_LONG - 1 - (h)))) -#define __GENMASK_ULL(h, l) (((~_ULL(0)) << (l)) & (~_ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h)))) +#define __GENMASK_ULL(h, l) (((~_ULL(0)) << (l)) & (~_ULL(0) >> (__BITS_PER_LONG_LONG - 1 - (h)))) #define __GENMASK_U128(h, l) \ ((_BIT128((h)) << 1) - (_BIT128(l))) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0b4a2f124d11..233de8677382 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -450,6 +450,7 @@ union bpf_iter_link_info { * * **struct bpf_map_info** * * **struct bpf_btf_info** * * **struct bpf_link_info** + * * **struct bpf_token_info** * * Return * Returns zero on success. On error, -1 is returned and *errno* @@ -906,6 +907,17 @@ union bpf_iter_link_info { * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * + * BPF_PROG_STREAM_READ_BY_FD + * Description + * Read data of a program's BPF stream. The program is identified + * by *prog_fd*, and the stream is identified by the *stream_id*. + * The data is copied to a buffer pointed to by *stream_buf*, and + * filled less than or equal to *stream_buf_len* bytes. + * + * Return + * Number of bytes read from the stream on success, or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * * NOTES * eBPF objects (maps and programs) can be shared between processes. * @@ -961,6 +973,7 @@ enum bpf_cmd { BPF_LINK_DETACH, BPF_PROG_BIND_MAP, BPF_TOKEN_CREATE, + BPF_PROG_STREAM_READ_BY_FD, __MAX_BPF_CMD, }; @@ -1463,6 +1476,11 @@ struct bpf_stack_build_id { #define BPF_OBJ_NAME_LEN 16U +enum { + BPF_STREAM_STDOUT = 1, + BPF_STREAM_STDERR = 2, +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -1794,6 +1812,13 @@ union bpf_attr { }; __u64 expected_revision; } netkit; + struct { + union { + __u32 relative_fd; + __u32 relative_id; + }; + __u64 expected_revision; + } cgroup; }; } link_create; @@ -1842,6 +1867,13 @@ union bpf_attr { __u32 bpffs_fd; } token_create; + struct { + __aligned_u64 stream_buf; + __u32 stream_buf_len; + __u32 stream_id; + __u32 prog_fd; + } prog_stream_read; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF @@ -2403,7 +2435,7 @@ union bpf_attr { * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in - * *samples/bpf/trace_output_kern.c*). + * *samples/bpf/trace_output.bpf.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user @@ -6653,11 +6685,15 @@ struct bpf_link_info { struct { __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */ __u32 tp_name_len; /* in/out: tp_name buffer len */ + __u32 :32; + __u64 cookie; } raw_tracepoint; struct { __u32 attach_type; __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */ __u32 target_btf_id; /* BTF type id inside the object */ + __u32 :32; + __u64 cookie; } tracing; struct { __u64 cgroup_id; @@ -6768,6 +6804,13 @@ struct bpf_link_info { }; } __attribute__((aligned(8))); +struct bpf_token_info { + __u64 allowed_cmds; + __u64 allowed_maps; + __u64 allowed_progs; + __u64 allowed_attachs; +} __attribute__((aligned(8))); + /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed * by user and intended to be used by socket (e.g. to bind to, depends on * attach type). diff --git a/tools/include/uapi/linux/coredump.h b/tools/include/uapi/linux/coredump.h new file mode 100644 index 000000000000..dc3789b78af0 --- /dev/null +++ b/tools/include/uapi/linux/coredump.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef _UAPI_LINUX_COREDUMP_H +#define _UAPI_LINUX_COREDUMP_H + +#include + +/** + * coredump_{req,ack} flags + * @COREDUMP_KERNEL: kernel writes coredump + * @COREDUMP_USERSPACE: userspace writes coredump + * @COREDUMP_REJECT: don't generate coredump + * @COREDUMP_WAIT: wait for coredump server + */ +enum { + COREDUMP_KERNEL = (1ULL << 0), + COREDUMP_USERSPACE = (1ULL << 1), + COREDUMP_REJECT = (1ULL << 2), + COREDUMP_WAIT = (1ULL << 3), +}; + +/** + * struct coredump_req - message kernel sends to userspace + * @size: size of struct coredump_req + * @size_ack: known size of struct coredump_ack on this kernel + * @mask: supported features + * + * When a coredump happens the kernel will connect to the coredump + * socket and send a coredump request to the coredump server. The @size + * member is set to the size of struct coredump_req and provides a hint + * to userspace how much data can be read. Userspace may use MSG_PEEK to + * peek the size of struct coredump_req and then choose to consume it in + * one go. Userspace may also simply read a COREDUMP_ACK_SIZE_VER0 + * request. If the size the kernel sends is larger userspace simply + * discards any remaining data. + * + * The coredump_req->mask member is set to the currently know features. + * Userspace may only set coredump_ack->mask to the bits raised by the + * kernel in coredump_req->mask. + * + * The coredump_req->size_ack member is set by the kernel to the size of + * struct coredump_ack the kernel knows. Userspace may only send up to + * coredump_req->size_ack bytes to the kernel and must set + * coredump_ack->size accordingly. + */ +struct coredump_req { + __u32 size; + __u32 size_ack; + __u64 mask; +}; + +enum { + COREDUMP_REQ_SIZE_VER0 = 16U, /* size of first published struct */ +}; + +/** + * struct coredump_ack - message userspace sends to kernel + * @size: size of the struct + * @spare: unused + * @mask: features kernel is supposed to use + * + * The @size member must be set to the size of struct coredump_ack. It + * may never exceed what the kernel returned in coredump_req->size_ack + * but it may of course be smaller (>= COREDUMP_ACK_SIZE_VER0 and <= + * coredump_req->size_ack). + * + * The @mask member must be set to the features the coredump server + * wants the kernel to use. Only bits the kernel returned in + * coredump_req->mask may be set. + */ +struct coredump_ack { + __u32 size; + __u32 spare; + __u64 mask; +}; + +enum { + COREDUMP_ACK_SIZE_VER0 = 16U, /* size of first published struct */ +}; + +/** + * enum coredump_mark - Markers for the coredump socket + * + * The kernel will place a single byte on the coredump socket. The + * markers notify userspace whether the coredump ack succeeded or + * failed. + * + * @COREDUMP_MARK_MINSIZE: the provided coredump_ack size was too small + * @COREDUMP_MARK_MAXSIZE: the provided coredump_ack size was too big + * @COREDUMP_MARK_UNSUPPORTED: the provided coredump_ack mask was invalid + * @COREDUMP_MARK_CONFLICTING: the provided coredump_ack mask has conflicting options + * @COREDUMP_MARK_REQACK: the coredump request and ack was successful + * @__COREDUMP_MARK_MAX: the maximum coredump mark value + */ +enum coredump_mark { + COREDUMP_MARK_REQACK = 0U, + COREDUMP_MARK_MINSIZE = 1U, + COREDUMP_MARK_MAXSIZE = 2U, + COREDUMP_MARK_UNSUPPORTED = 3U, + COREDUMP_MARK_CONFLICTING = 4U, + __COREDUMP_MARK_MAX = (1U << 31), +}; + +#endif /* _UAPI_LINUX_COREDUMP_H */ diff --git a/tools/include/uapi/linux/fscrypt.h b/tools/include/uapi/linux/fscrypt.h index 7a8f4c290187..3aff99f2696a 100644 --- a/tools/include/uapi/linux/fscrypt.h +++ b/tools/include/uapi/linux/fscrypt.h @@ -119,7 +119,7 @@ struct fscrypt_key_specifier { */ struct fscrypt_provisioning_key_payload { __u32 type; - __u32 __reserved; + __u32 flags; __u8 raw[]; }; @@ -128,7 +128,9 @@ struct fscrypt_add_key_arg { struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; - __u32 __reserved[8]; +#define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001 + __u32 flags; + __u32 __reserved[7]; __u8 raw[]; }; diff --git a/tools/include/uapi/linux/if_xdp.h b/tools/include/uapi/linux/if_xdp.h index 44f2bb93e7e6..23a062781468 100644 --- a/tools/include/uapi/linux/if_xdp.h +++ b/tools/include/uapi/linux/if_xdp.h @@ -79,6 +79,7 @@ struct xdp_mmap_offsets { #define XDP_UMEM_COMPLETION_RING 6 #define XDP_STATISTICS 7 #define XDP_OPTIONS 8 +#define XDP_MAX_TX_SKB_BUDGET 9 struct xdp_umem_reg { __u64 addr; /* Start of packet data area */ diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index b6ae8ad8934b..f0f0d49d2544 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -178,6 +178,7 @@ struct kvm_xen_exit { #define KVM_EXIT_NOTIFY 37 #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_TDX 40 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -375,6 +376,7 @@ struct kvm_run { #define KVM_SYSTEM_EVENT_WAKEUP 4 #define KVM_SYSTEM_EVENT_SUSPEND 5 #define KVM_SYSTEM_EVENT_SEV_TERM 6 +#define KVM_SYSTEM_EVENT_TDX_FATAL 7 __u32 type; __u32 ndata; union { @@ -446,6 +448,31 @@ struct kvm_run { __u64 gpa; __u64 size; } memory_fault; + /* KVM_EXIT_TDX */ + struct { + __u64 flags; + __u64 nr; + union { + struct { + __u64 ret; + __u64 data[5]; + } unknown; + struct { + __u64 ret; + __u64 gpa; + __u64 size; + } get_quote; + struct { + __u64 ret; + __u64 leaf; + __u64 r11, r12, r13, r14; + } get_tdvmcall_info; + struct { + __u64 ret; + __u64 vector; + } setup_event_notify; + }; + } tdx; /* Fix the size of the union. */ char padding[256]; }; @@ -617,6 +644,7 @@ struct kvm_ioeventfd { #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) +#define KVM_X86_DISABLE_EXITS_APERFMPERF (1 << 4) /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { @@ -930,6 +958,10 @@ struct kvm_enable_cap { #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 #define KVM_CAP_X86_GUEST_MODE 238 #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 +#define KVM_CAP_ARM_EL2 240 +#define KVM_CAP_ARM_EL2_E2H0 241 +#define KVM_CAP_RISCV_MP_STATE_RESET 242 +#define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index 7eb9571786b8..48eb49aa03d4 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -77,6 +77,11 @@ enum netdev_qstats_scope { NETDEV_QSTATS_SCOPE_QUEUE = 1, }; +enum netdev_napi_threaded { + NETDEV_NAPI_THREADED_DISABLED, + NETDEV_NAPI_THREADED_ENABLED, +}; + enum { NETDEV_A_DEV_IFINDEX = 1, NETDEV_A_DEV_PAD, @@ -134,6 +139,7 @@ enum { NETDEV_A_NAPI_DEFER_HARD_IRQS, NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT, NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT, + NETDEV_A_NAPI_THREADED, __NETDEV_A_NAPI_MAX, NETDEV_A_NAPI_MAX = (__NETDEV_A_NAPI_MAX - 1) diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h index 43dec6eed559..475fc8ca4403 100644 --- a/tools/include/uapi/linux/prctl.h +++ b/tools/include/uapi/linux/prctl.h @@ -255,7 +255,12 @@ struct prctl_mm_map { /* Dispatch syscalls to a userspace handler */ #define PR_SET_SYSCALL_USER_DISPATCH 59 # define PR_SYS_DISPATCH_OFF 0 -# define PR_SYS_DISPATCH_ON 1 +/* Enable dispatch except for the specified range */ +# define PR_SYS_DISPATCH_EXCLUSIVE_ON 1 +/* Enable dispatch for the specified range */ +# define PR_SYS_DISPATCH_INCLUSIVE_ON 2 +/* Legacy name for backwards compatibility */ +# define PR_SYS_DISPATCH_ON PR_SYS_DISPATCH_EXCLUSIVE_ON /* The control values for the user space selector when dispatch is enabled */ # define SYSCALL_DISPATCH_FILTER_ALLOW 0 # define SYSCALL_DISPATCH_FILTER_BLOCK 1 @@ -367,8 +372,6 @@ struct prctl_mm_map { /* FUTEX hash management */ #define PR_FUTEX_HASH 78 # define PR_FUTEX_HASH_SET_SLOTS 1 -# define FH_FLAG_IMMUTABLE (1ULL << 0) # define PR_FUTEX_HASH_GET_SLOTS 2 -# define PR_FUTEX_HASH_GET_IMMUTABLE 3 #endif /* _LINUX_PRCTL_H */ diff --git a/tools/include/uapi/linux/stat.h b/tools/include/uapi/linux/stat.h index f78ee3670dd5..1686861aae20 100644 --- a/tools/include/uapi/linux/stat.h +++ b/tools/include/uapi/linux/stat.h @@ -182,8 +182,12 @@ struct statx { /* File offset alignment for direct I/O reads */ __u32 stx_dio_read_offset_align; - /* 0xb8 */ - __u64 __spare3[9]; /* Spare space for future expansion */ + /* Optimised max atomic write unit in bytes */ + __u32 stx_atomic_write_unit_max_opt; + __u32 __spare2[1]; + + /* 0xc0 */ + __u64 __spare3[8]; /* Spare space for future expansion */ /* 0x100 */ }; diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index a9c3e33d0f8a..ab40dbf9f020 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -837,6 +837,50 @@ int bpf_link_create(int prog_fd, int target_fd, if (!OPTS_ZEROED(opts, netkit)) return libbpf_err(-EINVAL); break; + case BPF_CGROUP_INET_INGRESS: + case BPF_CGROUP_INET_EGRESS: + case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_INET_SOCK_RELEASE: + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: + case BPF_CGROUP_UNIX_CONNECT: + case BPF_CGROUP_INET4_GETPEERNAME: + case BPF_CGROUP_INET6_GETPEERNAME: + case BPF_CGROUP_UNIX_GETPEERNAME: + case BPF_CGROUP_INET4_GETSOCKNAME: + case BPF_CGROUP_INET6_GETSOCKNAME: + case BPF_CGROUP_UNIX_GETSOCKNAME: + case BPF_CGROUP_UDP4_SENDMSG: + case BPF_CGROUP_UDP6_SENDMSG: + case BPF_CGROUP_UNIX_SENDMSG: + case BPF_CGROUP_UDP4_RECVMSG: + case BPF_CGROUP_UDP6_RECVMSG: + case BPF_CGROUP_UNIX_RECVMSG: + case BPF_CGROUP_SOCK_OPS: + case BPF_CGROUP_DEVICE: + case BPF_CGROUP_SYSCTL: + case BPF_CGROUP_GETSOCKOPT: + case BPF_CGROUP_SETSOCKOPT: + case BPF_LSM_CGROUP: + relative_fd = OPTS_GET(opts, cgroup.relative_fd, 0); + relative_id = OPTS_GET(opts, cgroup.relative_id, 0); + if (relative_fd && relative_id) + return libbpf_err(-EINVAL); + if (relative_id) { + attr.link_create.cgroup.relative_id = relative_id; + attr.link_create.flags |= BPF_F_ID; + } else { + attr.link_create.cgroup.relative_fd = relative_fd; + } + attr.link_create.cgroup.expected_revision = + OPTS_GET(opts, cgroup.expected_revision, 0); + if (!OPTS_ZEROED(opts, cgroup)) + return libbpf_err(-EINVAL); + break; default: if (!OPTS_ZEROED(opts, flags)) return libbpf_err(-EINVAL); @@ -1331,3 +1375,23 @@ int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts) fd = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz); return libbpf_err_errno(fd); } + +int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len, + struct bpf_prog_stream_read_opts *opts) +{ + const size_t attr_sz = offsetofend(union bpf_attr, prog_stream_read); + union bpf_attr attr; + int err; + + if (!OPTS_VALID(opts, bpf_prog_stream_read_opts)) + return libbpf_err(-EINVAL); + + memset(&attr, 0, attr_sz); + attr.prog_stream_read.stream_buf = ptr_to_u64(buf); + attr.prog_stream_read.stream_buf_len = buf_len; + attr.prog_stream_read.stream_id = stream_id; + attr.prog_stream_read.prog_fd = prog_fd; + + err = sys_bpf(BPF_PROG_STREAM_READ_BY_FD, &attr, attr_sz); + return libbpf_err_errno(err); +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 777627d33d25..7252150e7ad3 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -438,6 +438,11 @@ struct bpf_link_create_opts { __u32 relative_id; __u64 expected_revision; } netkit; + struct { + __u32 relative_fd; + __u32 relative_id; + __u64 expected_revision; + } cgroup; }; size_t :0; }; @@ -704,6 +709,27 @@ struct bpf_token_create_opts { LIBBPF_API int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts); +struct bpf_prog_stream_read_opts { + size_t sz; + size_t :0; +}; +#define bpf_prog_stream_read_opts__last_field sz +/** + * @brief **bpf_prog_stream_read** reads data from the BPF stream of a given BPF + * program. + * + * @param prog_fd FD for the BPF program whose BPF stream is to be read. + * @param stream_id ID of the BPF stream to be read. + * @param buf Buffer to read data into from the BPF stream. + * @param buf_len Maximum number of bytes to read from the BPF stream. + * @param opts optional options, can be NULL + * + * @return The number of bytes read, on success; negative error code, otherwise + * (errno is also set to the error code) + */ +LIBBPF_API int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len, + struct bpf_prog_stream_read_opts *opts); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index a50773d4616e..80c028540656 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -215,6 +215,7 @@ enum libbpf_tristate { #define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull"))) #define __arg_nullable __attribute((btf_decl_tag("arg:nullable"))) #define __arg_trusted __attribute((btf_decl_tag("arg:trusted"))) +#define __arg_untrusted __attribute((btf_decl_tag("arg:untrusted"))) #define __arg_arena __attribute((btf_decl_tag("arg:arena"))) #ifndef ___bpf_concat @@ -314,6 +315,22 @@ enum libbpf_tristate { ___param, sizeof(___param)); \ }) +extern int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args, + __u32 len__sz, void *aux__prog) __weak __ksym; + +#define bpf_stream_printk(stream_id, fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_stream_vprintk(stream_id, ___fmt, ___param, sizeof(___param), NULL);\ +}) + /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args * Otherwise use __bpf_vprintk */ diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 4392451d634b..ccfd905f03df 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -326,9 +326,10 @@ struct btf_dump_type_data_opts { bool compact; /* no newlines/indentation */ bool skip_names; /* skip member/type names */ bool emit_zeroes; /* show 0-valued fields */ + bool emit_strings; /* print char arrays as strings */ size_t :0; }; -#define btf_dump_type_data_opts__last_field emit_zeroes +#define btf_dump_type_data_opts__last_field emit_strings LIBBPF_API int btf_dump__dump_type_data(struct btf_dump *d, __u32 id, diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 460c3e57fadb..f09f25eccf3c 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -68,6 +68,7 @@ struct btf_dump_data { bool compact; bool skip_names; bool emit_zeroes; + bool emit_strings; __u8 indent_lvl; /* base indent level */ char indent_str[BTF_DATA_INDENT_STR_LEN]; /* below are used during iteration */ @@ -226,6 +227,9 @@ static void btf_dump_free_names(struct hashmap *map) size_t bkt; struct hashmap_entry *cur; + if (!map) + return; + hashmap__for_each_entry(map, cur, bkt) free((void *)cur->pkey); @@ -2028,6 +2032,52 @@ static int btf_dump_var_data(struct btf_dump *d, return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0); } +static int btf_dump_string_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + const void *data) +{ + const struct btf_array *array = btf_array(t); + const char *chars = data; + __u32 i; + + /* Make sure it is a NUL-terminated string. */ + for (i = 0; i < array->nelems; i++) { + if ((void *)(chars + i) >= d->typed_dump->data_end) + return -E2BIG; + if (chars[i] == '\0') + break; + } + if (i == array->nelems) { + /* The caller will print this as a regular array. */ + return -EINVAL; + } + + btf_dump_data_pfx(d); + btf_dump_printf(d, "\""); + + for (i = 0; i < array->nelems; i++) { + char c = chars[i]; + + if (c == '\0') { + /* + * When printing character arrays as strings, NUL bytes + * are always treated as string terminators; they are + * never printed. + */ + break; + } + if (isprint(c)) + btf_dump_printf(d, "%c", c); + else + btf_dump_printf(d, "\\x%02x", (__u8)c); + } + + btf_dump_printf(d, "\""); + + return 0; +} + static int btf_dump_array_data(struct btf_dump *d, const struct btf_type *t, __u32 id, @@ -2055,8 +2105,13 @@ static int btf_dump_array_data(struct btf_dump *d, * char arrays, so if size is 1 and element is * printable as a char, we'll do that. */ - if (elem_size == 1) + if (elem_size == 1) { + if (d->typed_dump->emit_strings && + btf_dump_string_data(d, t, id, data) == 0) { + return 0; + } d->typed_dump->is_array_char = true; + } } /* note that we increment depth before calling btf_dump_print() below; @@ -2544,6 +2599,7 @@ int btf_dump__dump_type_data(struct btf_dump *d, __u32 id, d->typed_dump->compact = OPTS_GET(opts, compact, false); d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false); d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false); + d->typed_dump->emit_strings = OPTS_GET(opts, emit_strings, false); ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0); diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e9c641a2fb20..8f5a81b672e1 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -597,7 +597,7 @@ struct extern_desc { int sym_idx; int btf_id; int sec_btf_id; - const char *name; + char *name; char *essent_name; bool is_set; bool is_weak; @@ -735,7 +735,7 @@ struct bpf_object { struct usdt_manager *usdt_man; - struct bpf_map *arena_map; + int arena_map_idx; void *arena_data; size_t arena_data_sz; @@ -1517,6 +1517,7 @@ static struct bpf_object *bpf_object__new(const char *path, obj->efile.obj_buf_sz = obj_buf_sz; obj->efile.btf_maps_shndx = -1; obj->kconfig_map_idx = -1; + obj->arena_map_idx = -1; obj->kern_version = get_kernel_version(); obj->state = OBJ_OPEN; @@ -2964,7 +2965,7 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map, const long page_sz = sysconf(_SC_PAGE_SIZE); size_t mmap_sz; - mmap_sz = bpf_map_mmap_sz(obj->arena_map); + mmap_sz = bpf_map_mmap_sz(map); if (roundup(data_sz, page_sz) > mmap_sz) { pr_warn("elf: sec '%s': declared ARENA map size (%zu) is too small to hold global __arena variables of size %zu\n", sec_name, mmap_sz, data_sz); @@ -3038,12 +3039,12 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, if (map->def.type != BPF_MAP_TYPE_ARENA) continue; - if (obj->arena_map) { + if (obj->arena_map_idx >= 0) { pr_warn("map '%s': only single ARENA map is supported (map '%s' is also ARENA)\n", - map->name, obj->arena_map->name); + map->name, obj->maps[obj->arena_map_idx].name); return -EINVAL; } - obj->arena_map = map; + obj->arena_map_idx = i; if (obj->efile.arena_data) { err = init_arena_map_data(obj, map, ARENA_SEC, obj->efile.arena_data_shndx, @@ -3053,7 +3054,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, return err; } } - if (obj->efile.arena_data && !obj->arena_map) { + if (obj->efile.arena_data && obj->arena_map_idx < 0) { pr_warn("elf: sec '%s': to use global __arena variables the ARENA map should be explicitly declared in SEC(\".maps\")\n", ARENA_SEC); return -ENOENT; @@ -4259,7 +4260,9 @@ static int bpf_object__collect_externs(struct bpf_object *obj) return ext->btf_id; } t = btf__type_by_id(obj->btf, ext->btf_id); - ext->name = btf__name_by_offset(obj->btf, t->name_off); + ext->name = strdup(btf__name_by_offset(obj->btf, t->name_off)); + if (!ext->name) + return -ENOMEM; ext->sym_idx = i; ext->is_weak = ELF64_ST_BIND(sym->st_info) == STB_WEAK; @@ -4579,10 +4582,20 @@ static int bpf_program__record_reloc(struct bpf_program *prog, /* arena data relocation */ if (shdr_idx == obj->efile.arena_data_shndx) { + if (obj->arena_map_idx < 0) { + pr_warn("prog '%s': bad arena data relocation at insn %u, no arena maps defined\n", + prog->name, insn_idx); + return -LIBBPF_ERRNO__RELOC; + } reloc_desc->type = RELO_DATA; reloc_desc->insn_idx = insn_idx; - reloc_desc->map_idx = obj->arena_map - obj->maps; + reloc_desc->map_idx = obj->arena_map_idx; reloc_desc->sym_off = sym->st_value; + + map = &obj->maps[obj->arena_map_idx]; + pr_debug("prog '%s': found arena map %d (%s, sec %d, off %zu) for insn %u\n", + prog->name, obj->arena_map_idx, map->name, map->sec_idx, + map->sec_offset, insn_idx); return 0; } @@ -9138,8 +9151,10 @@ void bpf_object__close(struct bpf_object *obj) zfree(&obj->btf_custom_path); zfree(&obj->kconfig); - for (i = 0; i < obj->nr_extern; i++) + for (i = 0; i < obj->nr_extern; i++) { + zfree(&obj->externs[i].name); zfree(&obj->externs[i].essent_name); + } zfree(&obj->externs); obj->nr_extern = 0; @@ -9206,7 +9221,7 @@ int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts) return libbpf_err(-EFAULT); if (!OPTS_VALID(opts, gen_loader_opts)) return libbpf_err(-EINVAL); - gen = calloc(sizeof(*gen), 1); + gen = calloc(1, sizeof(*gen)); if (!gen) return libbpf_err(-ENOMEM); gen->opts = opts; @@ -10081,7 +10096,7 @@ static int find_kernel_btf_id(struct bpf_object *obj, const char *attach_name, enum bpf_attach_type attach_type, int *btf_obj_fd, int *btf_type_id) { - int ret, i, mod_len; + int ret, i, mod_len = 0; const char *fn_name, *mod_name = NULL; fn_name = strchr(attach_name, ':'); @@ -10950,11 +10965,14 @@ struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *p } link->link.fd = pfd; } - if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { - err = -errno; - pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n", - prog->name, pfd, errstr(err)); - goto err_out; + + if (!OPTS_GET(opts, dont_enable, false)) { + if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { + err = -errno; + pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n", + prog->name, pfd, errstr(err)); + goto err_out; + } } return &link->link; @@ -12837,6 +12855,34 @@ struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifi return bpf_program_attach_fd(prog, ifindex, "xdp", NULL); } +struct bpf_link * +bpf_program__attach_cgroup_opts(const struct bpf_program *prog, int cgroup_fd, + const struct bpf_cgroup_opts *opts) +{ + LIBBPF_OPTS(bpf_link_create_opts, link_create_opts); + __u32 relative_id; + int relative_fd; + + if (!OPTS_VALID(opts, bpf_cgroup_opts)) + return libbpf_err_ptr(-EINVAL); + + relative_id = OPTS_GET(opts, relative_id, 0); + relative_fd = OPTS_GET(opts, relative_fd, 0); + + if (relative_fd && relative_id) { + pr_warn("prog '%s': relative_fd and relative_id cannot be set at the same time\n", + prog->name); + return libbpf_err_ptr(-EINVAL); + } + + link_create_opts.cgroup.expected_revision = OPTS_GET(opts, expected_revision, 0); + link_create_opts.cgroup.relative_fd = relative_fd; + link_create_opts.cgroup.relative_id = relative_id; + link_create_opts.flags = OPTS_GET(opts, flags, 0); + + return bpf_program_attach_fd(prog, cgroup_fd, "cgroup", &link_create_opts); +} + struct bpf_link * bpf_program__attach_tcx(const struct bpf_program *prog, int ifindex, const struct bpf_tcx_opts *opts) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 1137e7d2e1b5..455a957cb702 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -499,9 +499,11 @@ struct bpf_perf_event_opts { __u64 bpf_cookie; /* don't use BPF link when attach BPF program */ bool force_ioctl_attach; + /* don't automatically enable the event */ + bool dont_enable; size_t :0; }; -#define bpf_perf_event_opts__last_field force_ioctl_attach +#define bpf_perf_event_opts__last_field dont_enable LIBBPF_API struct bpf_link * bpf_program__attach_perf_event(const struct bpf_program *prog, int pfd); @@ -877,6 +879,21 @@ LIBBPF_API struct bpf_link * bpf_program__attach_netkit(const struct bpf_program *prog, int ifindex, const struct bpf_netkit_opts *opts); +struct bpf_cgroup_opts { + /* size of this struct, for forward/backward compatibility */ + size_t sz; + __u32 flags; + __u32 relative_fd; + __u32 relative_id; + __u64 expected_revision; + size_t :0; +}; +#define bpf_cgroup_opts__last_field expected_revision + +LIBBPF_API struct bpf_link * +bpf_program__attach_cgroup_opts(const struct bpf_program *prog, int cgroup_fd, + const struct bpf_cgroup_opts *opts); + struct bpf_map; LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 1205f9a4fe04..d7bd463e7017 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -437,6 +437,8 @@ LIBBPF_1.6.0 { bpf_linker__add_fd; bpf_linker__new_fd; bpf_object__prepare; + bpf_prog_stream_read; + bpf_program__attach_cgroup_opts; bpf_program__func_info; bpf_program__func_info_cnt; bpf_program__line_info; @@ -444,3 +446,6 @@ LIBBPF_1.6.0 { btf__add_decl_attr; btf__add_type_attr; } LIBBPF_1.5.0; + +LIBBPF_1.7.0 { +} LIBBPF_1.6.0; diff --git a/tools/lib/bpf/libbpf_version.h b/tools/lib/bpf/libbpf_version.h index 28c58fb17250..99331e317dee 100644 --- a/tools/lib/bpf/libbpf_version.h +++ b/tools/lib/bpf/libbpf_version.h @@ -4,6 +4,6 @@ #define __LIBBPF_VERSION_H #define LIBBPF_MAJOR_VERSION 1 -#define LIBBPF_MINOR_VERSION 6 +#define LIBBPF_MINOR_VERSION 7 #endif /* __LIBBPF_VERSION_H */ diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index 4e4a52742b01..3373b9d45ac4 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -59,7 +59,7 @@ * * STAP_PROBE3(my_usdt_provider, my_usdt_probe_name, 123, x, &y); * - * USDT is identified by it's : pair of names. Each + * USDT is identified by its : pair of names. Each * individual USDT has a fixed number of arguments (3 in the above example) * and specifies values of each argument as if it was a function call. * @@ -81,7 +81,7 @@ * NOP instruction that kernel can replace with an interrupt instruction to * trigger instrumentation code (BPF program for all that we care about). * - * Semaphore above is and optional feature. It records an address of a 2-byte + * Semaphore above is an optional feature. It records an address of a 2-byte * refcount variable (normally in '.probes' ELF section) used for signaling if * there is anything that is attached to USDT. This is useful for user * applications if, for example, they need to prepare some arguments that are @@ -121,7 +121,7 @@ * a uprobe BPF program (which for kernel, at least currently, is just a kprobe * program, so BPF_PROG_TYPE_KPROBE program type). With the only difference * that uprobe is usually attached at the function entry, while USDT will - * normally will be somewhere inside the function. But it should always be + * normally be somewhere inside the function. But it should always be * pointing to NOP instruction, which makes such uprobes the fastest uprobe * kind. * @@ -151,7 +151,7 @@ * libbpf sets to spec ID during attach time, or, if kernel is too old to * support BPF cookie, through IP-to-spec-ID map that libbpf maintains in such * case. The latter means that some modes of operation can't be supported - * without BPF cookie. Such mode is attaching to shared library "generically", + * without BPF cookie. Such a mode is attaching to shared library "generically", * without specifying target process. In such case, it's impossible to * calculate absolute IP addresses for IP-to-spec-ID map, and thus such mode * is not supported without BPF cookie support. @@ -185,7 +185,7 @@ * as even if USDT spec string is the same, USDT cookie value can be * different. It was deemed excessive to try to deduplicate across independent * USDT attachments by taking into account USDT spec string *and* USDT cookie - * value, which would complicated spec ID accounting significantly for little + * value, which would complicate spec ID accounting significantly for little * gain. */ diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c index b1f4c8176b32..3ed023f4b190 100644 --- a/tools/lib/perf/evlist.c +++ b/tools/lib/perf/evlist.c @@ -36,49 +36,88 @@ void perf_evlist__init(struct perf_evlist *evlist) static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, struct perf_evsel *evsel) { - if (evsel->system_wide) { - /* System wide: set the cpu map of the evsel to all online CPUs. */ - perf_cpu_map__put(evsel->cpus); - evsel->cpus = perf_cpu_map__new_online_cpus(); - } else if (evlist->has_user_cpus && evsel->is_pmu_core) { - /* - * User requested CPUs on a core PMU, ensure the requested CPUs - * are valid by intersecting with those of the PMU. - */ - perf_cpu_map__put(evsel->cpus); - evsel->cpus = perf_cpu_map__intersect(evlist->user_requested_cpus, evsel->own_cpus); - - /* - * Empty cpu lists would eventually get opened as "any" so remove - * genuinely empty ones before they're opened in the wrong place. - */ - if (perf_cpu_map__is_empty(evsel->cpus)) { - struct perf_evsel *next = perf_evlist__next(evlist, evsel); - - perf_evlist__remove(evlist, evsel); - /* Keep idx contiguous */ - if (next) - list_for_each_entry_from(next, &evlist->entries, node) - next->idx--; + if (perf_cpu_map__is_empty(evsel->cpus)) { + if (perf_cpu_map__is_empty(evsel->pmu_cpus)) { + /* + * Assume the unset PMU cpus were for a system-wide + * event, like a software or tracepoint. + */ + evsel->pmu_cpus = perf_cpu_map__new_online_cpus(); } - } else if (!evsel->own_cpus || evlist->has_user_cpus || - (!evsel->requires_cpu && perf_cpu_map__has_any_cpu(evlist->user_requested_cpus))) { - /* - * The PMU didn't specify a default cpu map, this isn't a core - * event and the user requested CPUs or the evlist user - * requested CPUs have the "any CPU" (aka dummy) CPU value. In - * which case use the user requested CPUs rather than the PMU - * ones. - */ + if (evlist->has_user_cpus && !evsel->system_wide) { + /* + * Use the user CPUs unless the evsel is set to be + * system wide, such as the dummy event. + */ + evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus); + } else { + /* + * System wide and other modes, assume the cpu map + * should be set to all PMU CPUs. + */ + evsel->cpus = perf_cpu_map__get(evsel->pmu_cpus); + } + } + /* + * Avoid "any CPU"(-1) for uncore and PMUs that require a CPU, even if + * requested. + */ + if (evsel->requires_cpu && perf_cpu_map__has_any_cpu(evsel->cpus)) { + perf_cpu_map__put(evsel->cpus); + evsel->cpus = perf_cpu_map__get(evsel->pmu_cpus); + } + + /* + * Globally requested CPUs replace user requested unless the evsel is + * set to be system wide. + */ + if (evlist->has_user_cpus && !evsel->system_wide) { + assert(!perf_cpu_map__has_any_cpu(evlist->user_requested_cpus)); + if (!perf_cpu_map__equal(evsel->cpus, evlist->user_requested_cpus)) { + perf_cpu_map__put(evsel->cpus); + evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus); + } + } + + /* Ensure cpus only references valid PMU CPUs. */ + if (!perf_cpu_map__has_any_cpu(evsel->cpus) && + !perf_cpu_map__is_subset(evsel->pmu_cpus, evsel->cpus)) { + struct perf_cpu_map *tmp = perf_cpu_map__intersect(evsel->pmu_cpus, evsel->cpus); + + perf_cpu_map__put(evsel->cpus); + evsel->cpus = tmp; + } + + /* + * Was event requested on all the PMU's CPUs but the user requested is + * any CPU (-1)? If so switch to using any CPU (-1) to reduce the number + * of events. + */ + if (!evsel->system_wide && + !evsel->requires_cpu && + perf_cpu_map__equal(evsel->cpus, evsel->pmu_cpus) && + perf_cpu_map__has_any_cpu(evlist->user_requested_cpus)) { perf_cpu_map__put(evsel->cpus); evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus); - } else if (evsel->cpus != evsel->own_cpus) { - /* - * No user requested cpu map but the PMU cpu map doesn't match - * the evsel's. Reset it back to the PMU cpu map. - */ - perf_cpu_map__put(evsel->cpus); - evsel->cpus = perf_cpu_map__get(evsel->own_cpus); + } + + /* Sanity check assert before the evsel is potentially removed. */ + assert(!evsel->requires_cpu || !perf_cpu_map__has_any_cpu(evsel->cpus)); + + /* + * Empty cpu lists would eventually get opened as "any" so remove + * genuinely empty ones before they're opened in the wrong place. + */ + if (perf_cpu_map__is_empty(evsel->cpus)) { + struct perf_evsel *next = perf_evlist__next(evlist, evsel); + + perf_evlist__remove(evlist, evsel); + /* Keep idx contiguous */ + if (next) + list_for_each_entry_from(next, &evlist->entries, node) + next->idx--; + + return; } if (evsel->system_wide) { @@ -98,6 +137,10 @@ static void perf_evlist__propagate_maps(struct perf_evlist *evlist) evlist->needs_map_propagation = true; + /* Clear the all_cpus set which will be merged into during propagation. */ + perf_cpu_map__put(evlist->all_cpus); + evlist->all_cpus = NULL; + list_for_each_entry_safe(evsel, n, &evlist->entries, node) __perf_evlist__propagate_maps(evlist, evsel); } diff --git a/tools/lib/perf/evsel.c b/tools/lib/perf/evsel.c index c475319e2e41..13a307fc75ae 100644 --- a/tools/lib/perf/evsel.c +++ b/tools/lib/perf/evsel.c @@ -40,8 +40,19 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) return evsel; } +void perf_evsel__exit(struct perf_evsel *evsel) +{ + assert(evsel->fd == NULL); /* If not fds were not closed. */ + assert(evsel->mmap == NULL); /* If not munmap wasn't called. */ + assert(evsel->sample_id == NULL); /* If not free_id wasn't called. */ + perf_cpu_map__put(evsel->cpus); + perf_cpu_map__put(evsel->pmu_cpus); + perf_thread_map__put(evsel->threads); +} + void perf_evsel__delete(struct perf_evsel *evsel) { + perf_evsel__exit(evsel); free(evsel); } diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h index ea78defa77d0..fefe64ba5e26 100644 --- a/tools/lib/perf/include/internal/evsel.h +++ b/tools/lib/perf/include/internal/evsel.h @@ -99,7 +99,7 @@ struct perf_evsel { * cpu map for opening the event on, for example, the first CPU on a * socket for an uncore event. */ - struct perf_cpu_map *own_cpus; + struct perf_cpu_map *pmu_cpus; struct perf_thread_map *threads; struct xyarray *fd; struct xyarray *mmap; @@ -133,6 +133,7 @@ struct perf_evsel { void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr, int idx); +void perf_evsel__exit(struct perf_evsel *evsel); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); void perf_evsel__close_fd(struct perf_evsel *evsel); void perf_evsel__free_fd(struct perf_evsel *evsel); diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index 09b7c643ddac..6608f1e3701b 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -467,6 +467,22 @@ struct perf_record_compressed2 { char data[]; }; +#define BPF_METADATA_KEY_LEN 64 +#define BPF_METADATA_VALUE_LEN 256 +#define BPF_PROG_NAME_LEN KSYM_NAME_LEN + +struct perf_record_bpf_metadata_entry { + char key[BPF_METADATA_KEY_LEN]; + char value[BPF_METADATA_VALUE_LEN]; +}; + +struct perf_record_bpf_metadata { + struct perf_event_header header; + char prog_name[BPF_PROG_NAME_LEN]; + __u64 nr_entries; + struct perf_record_bpf_metadata_entry entries[]; +}; + enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_USER_TYPE_START = 64, PERF_RECORD_HEADER_ATTR = 64, @@ -489,6 +505,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_COMPRESSED = 81, PERF_RECORD_FINISHED_INIT = 82, PERF_RECORD_COMPRESSED2 = 83, + PERF_RECORD_BPF_METADATA = 84, PERF_RECORD_HEADER_MAX }; @@ -530,6 +547,7 @@ union perf_event { struct perf_record_header_feature feat; struct perf_record_compressed pack; struct perf_record_compressed2 pack2; + struct perf_record_bpf_metadata bpf_metadata; }; #endif /* __LIBPERF_EVENT_H */ diff --git a/tools/lib/subcmd/help.c b/tools/lib/subcmd/help.c index 8561b0f01a24..9ef569492560 100644 --- a/tools/lib/subcmd/help.c +++ b/tools/lib/subcmd/help.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "subcmd-util.h" #include "help.h" #include "exec-cmd.h" @@ -82,10 +83,11 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) ci++; cj++; } else { - zfree(&cmds->names[cj]); - cmds->names[cj++] = cmds->names[ci++]; + cmds->names[cj++] = cmds->names[ci]; + cmds->names[ci++] = NULL; } } else if (cmp == 0) { + zfree(&cmds->names[ci]); ci++; ei++; } else if (cmp > 0) { @@ -94,12 +96,12 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) } if (ci != cj) { while (ci < cmds->cnt) { - zfree(&cmds->names[cj]); - cmds->names[cj++] = cmds->names[ci++]; + cmds->names[cj++] = cmds->names[ci]; + cmds->names[ci++] = NULL; } } for (ci = cj; ci < cmds->cnt; ci++) - zfree(&cmds->names[ci]); + assert(cmds->names[ci] == NULL); cmds->cnt = cj; } diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c index 0a764c25c384..b7510f83209a 100644 --- a/tools/lib/subcmd/run-command.c +++ b/tools/lib/subcmd/run-command.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -216,10 +217,20 @@ static int wait_or_whine(struct child_process *cmd, bool block) return result; } +/* + * Conservative estimate of number of characaters needed to hold an a decoded + * integer, assume each 3 bits needs a character byte and plus a possible sign + * character. + */ +#ifndef is_signed_type +#define is_signed_type(type) (((type)(-1)) < (type)1) +#endif +#define MAX_STRLEN_TYPE(type) (sizeof(type) * 8 / 3 + (is_signed_type(type) ? 1 : 0)) + int check_if_command_finished(struct child_process *cmd) { #ifdef __linux__ - char filename[FILENAME_MAX + 12]; + char filename[6 + MAX_STRLEN_TYPE(typeof(cmd->pid)) + 7 + 1]; char status_line[256]; FILE *status_file; @@ -227,7 +238,7 @@ int check_if_command_finished(struct child_process *cmd) * Check by reading /proc//status as calling waitpid causes * stdout/stderr to be closed and data lost. */ - sprintf(filename, "/proc/%d/status", cmd->pid); + sprintf(filename, "/proc/%u/status", cmd->pid); status_file = fopen(filename, "r"); if (status_file == NULL) { /* Open failed assume finish_command was called. */ diff --git a/tools/mm/show_page_info.py b/tools/mm/show_page_info.py new file mode 100644 index 000000000000..c46d8ea283d7 --- /dev/null +++ b/tools/mm/show_page_info.py @@ -0,0 +1,169 @@ +#!/usr/bin/env drgn +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2025 Ye Liu + +import argparse +import sys +from drgn import Object, FaultError, PlatformFlags, cast +from drgn.helpers.linux import find_task, follow_page, page_size +from drgn.helpers.linux.mm import ( + decode_page_flags, page_to_pfn, page_to_phys, page_to_virt, vma_find, + PageSlab, PageCompound, PageHead, PageTail, compound_head, compound_order, compound_nr +) +from drgn.helpers.linux.cgroup import cgroup_name, cgroup_path + +DESC = """ +This is a drgn script to show the page state. +For more info on drgn, visit https://github.com/osandov/drgn. +""" + +def format_page_data(page): + """ + Format raw page data into a readable hex dump with "RAW:" prefix. + + :param page: drgn.Object instance representing the page. + :return: Formatted string of memory contents. + """ + try: + address = page.value_() + size = prog.type("struct page").size + + if prog.platform.flags & PlatformFlags.IS_64_BIT: + word_size = 8 + else: + word_size = 4 + num_words = size // word_size + + values = [] + for i in range(num_words): + word_address = address + i * word_size + word = prog.read_word(word_address) + values.append(f"{word:0{word_size * 2}x}") + + lines = [f"RAW: {' '.join(values[i:i + 4])}" for i in range(0, len(values), 4)] + + return "\n".join(lines) + + except FaultError as e: + return f"Error reading memory: {e}" + except Exception as e: + return f"Unexpected error: {e}" + +def get_memcg_info(page): + """Retrieve memory cgroup information for a page.""" + try: + MEMCG_DATA_OBJEXTS = prog.constant("MEMCG_DATA_OBJEXTS").value_() + MEMCG_DATA_KMEM = prog.constant("MEMCG_DATA_KMEM").value_() + mask = prog.constant('__NR_MEMCG_DATA_FLAGS').value_() - 1 + memcg_data = page.memcg_data.read_() + if memcg_data & MEMCG_DATA_OBJEXTS: + slabobj_ext = cast("struct slabobj_ext *", memcg_data & ~mask) + memcg = slabobj_ext.objcg.memcg.value_() + elif memcg_data & MEMCG_DATA_KMEM: + objcg = cast("struct obj_cgroup *", memcg_data & ~mask) + memcg = objcg.memcg.value_() + else: + memcg = cast("struct mem_cgroup *", memcg_data & ~mask) + + if memcg.value_() == 0: + return "none", "/sys/fs/cgroup/memory/" + cgrp = memcg.css.cgroup + return cgroup_name(cgrp).decode(), f"/sys/fs/cgroup/memory{cgroup_path(cgrp).decode()}" + except FaultError as e: + return "unknown", f"Error retrieving memcg info: {e}" + except Exception as e: + return "unknown", f"Unexpected error: {e}" + +def show_page_state(page, addr, mm, pid, task): + """Display detailed information about a page.""" + try: + print(f'PID: {pid} Comm: {task.comm.string_().decode()} mm: {hex(mm)}') + try: + print(format_page_data(page)) + except FaultError as e: + print(f"Error reading page data: {e}") + fields = { + "Page Address": hex(page.value_()), + "Page Flags": decode_page_flags(page), + "Page Size": prog["PAGE_SIZE"].value_(), + "Page PFN": hex(page_to_pfn(page).value_()), + "Page Physical": hex(page_to_phys(page).value_()), + "Page Virtual": hex(page_to_virt(page).value_()), + "Page Refcount": page._refcount.counter.value_(), + "Page Mapcount": page._mapcount.counter.value_(), + "Page Index": hex(page.__folio_index.value_()), + "Page Memcg Data": hex(page.memcg_data.value_()), + } + + memcg_name, memcg_path = get_memcg_info(page) + fields["Memcg Name"] = memcg_name + fields["Memcg Path"] = memcg_path + fields["Page Mapping"] = hex(page.mapping.value_()) + fields["Page Anon/File"] = "Anon" if page.mapping.value_() & 0x1 else "File" + + try: + vma = vma_find(mm, addr) + fields["Page VMA"] = hex(vma.value_()) + fields["VMA Start"] = hex(vma.vm_start.value_()) + fields["VMA End"] = hex(vma.vm_end.value_()) + except FaultError as e: + fields["Page VMA"] = "Unavailable" + fields["VMA Start"] = "Unavailable" + fields["VMA End"] = "Unavailable" + print(f"Error retrieving VMA information: {e}") + + # Calculate the maximum field name length for alignment + max_field_len = max(len(field) for field in fields) + + # Print aligned fields + for field, value in fields.items(): + print(f"{field}:".ljust(max_field_len + 2) + f"{value}") + + # Additional information about the page + if PageSlab(page): + print("This page belongs to the slab allocator.") + + if PageCompound(page): + print("This page is part of a compound page.") + if PageHead(page): + print("This page is the head page of a compound page.") + if PageTail(page): + print("This page is the tail page of a compound page.") + print(f"{'Head Page:'.ljust(max_field_len + 2)}{hex(compound_head(page).value_())}") + print(f"{'Compound Order:'.ljust(max_field_len + 2)}{compound_order(page).value_()}") + print(f"{'Number of Pages:'.ljust(max_field_len + 2)}{compound_nr(page).value_()}") + else: + print("This page is not part of a compound page.") + except FaultError as e: + print(f"Error accessing page state: {e}") + except Exception as e: + print(f"Unexpected error: {e}") + +def main(): + """Main function to parse arguments and display page state.""" + parser = argparse.ArgumentParser(description=DESC, formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('pid', metavar='PID', type=int, help='Target process ID (PID)') + parser.add_argument('vaddr', metavar='VADDR', type=str, help='Target virtual address in hexadecimal format (e.g., 0x7fff1234abcd)') + args = parser.parse_args() + + try: + vaddr = int(args.vaddr, 16) + except ValueError: + sys.exit(f"Error: Invalid virtual address format: {args.vaddr}") + + try: + task = find_task(args.pid) + mm = task.mm + page = follow_page(mm, vaddr) + + if page: + show_page_state(page, vaddr, mm, args.pid, task) + else: + sys.exit(f"Address {hex(vaddr)} is not mapped.") + except FaultError as e: + sys.exit(f"Error accessing task or memory: {e}") + except Exception as e: + sys.exit(f"Unexpected error: {e}") + +if __name__ == "__main__": + main() diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py index 33ccc5c1843b..8c192e900bd3 100755 --- a/tools/net/ynl/pyynl/cli.py +++ b/tools/net/ynl/pyynl/cli.py @@ -113,6 +113,8 @@ def main(): spec = f"{spec_dir()}/{args.family}.yaml" if args.schema is None and spec.startswith(sys_schema_dir): args.schema = '' # disable schema validation when installed + if args.process_unknown is None: + args.process_unknown = True else: spec = args.spec if not os.path.isfile(spec): diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 55b59f6c79b8..8244a5f440b2 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -231,14 +231,7 @@ class NlMsg: self.extack['unknown'].append(extack) if attr_space: - # We don't have the ability to parse nests yet, so only do global - if 'miss-type' in self.extack and 'miss-nest' not in self.extack: - miss_type = self.extack['miss-type'] - if miss_type in attr_space.attrs_by_val: - spec = attr_space.attrs_by_val[miss_type] - self.extack['miss-type'] = spec['name'] - if 'doc' in spec: - self.extack['miss-type-doc'] = spec['doc'] + self.annotate_extack(attr_space) def _decode_policy(self, raw): policy = {} @@ -264,6 +257,18 @@ class NlMsg: policy['mask'] = attr.as_scalar('u64') return policy + def annotate_extack(self, attr_space): + """ Make extack more human friendly with attribute information """ + + # We don't have the ability to parse nests yet, so only do global + if 'miss-type' in self.extack and 'miss-nest' not in self.extack: + miss_type = self.extack['miss-type'] + if miss_type in attr_space.attrs_by_val: + spec = attr_space.attrs_by_val[miss_type] + self.extack['miss-type'] = spec['name'] + if 'doc' in spec: + self.extack['miss-type-doc'] = spec['doc'] + def cmd(self): return self.nl_type @@ -277,12 +282,12 @@ class NlMsg: class NlMsgs: - def __init__(self, data, attr_space=None): + def __init__(self, data): self.msgs = [] offset = 0 while offset < len(data): - msg = NlMsg(data, offset, attr_space=attr_space) + msg = NlMsg(data, offset) offset += msg.nl_len self.msgs.append(msg) @@ -570,7 +575,9 @@ class YnlFamily(SpecFamily): elif attr["type"] == 'string': attr_payload = str(value).encode('ascii') + b'\x00' elif attr["type"] == 'binary': - if isinstance(value, bytes): + if value is None: + attr_payload = b'' + elif isinstance(value, bytes): attr_payload = value elif isinstance(value, str): if attr.display_hint: @@ -579,6 +586,9 @@ class YnlFamily(SpecFamily): attr_payload = bytes.fromhex(value) elif isinstance(value, dict) and attr.struct_name: attr_payload = self._encode_struct(attr.struct_name, value) + elif isinstance(value, list) and attr.sub_type in NlAttr.type_formats: + format = NlAttr.get_format(attr.sub_type) + attr_payload = b''.join([format.pack(x) for x in value]) else: raise Exception(f'Unknown type for binary attribute, value: {value}') elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar: @@ -613,6 +623,16 @@ class YnlFamily(SpecFamily): pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4) return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad + def _get_enum_or_unknown(self, enum, raw): + try: + name = enum.entries_by_val[raw].name + except KeyError as error: + if self.process_unknown: + name = f"Unknown({raw})" + else: + raise error + return name + def _decode_enum(self, raw, attr_spec): enum = self.consts[attr_spec['enum']] if enum.type == 'flags' or attr_spec.get('enum-as-flags', False): @@ -620,11 +640,11 @@ class YnlFamily(SpecFamily): value = set() while raw: if raw & 1: - value.add(enum.entries_by_val[i].name) + value.add(self._get_enum_or_unknown(enum, i)) raw >>= 1 i += 1 else: - value = enum.entries_by_val[raw].name + value = self._get_enum_or_unknown(enum, raw) return value def _decode_binary(self, attr, attr_spec): @@ -757,6 +777,8 @@ class YnlFamily(SpecFamily): decoded = True elif attr_spec.is_auto_scalar: decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order) + if 'enum' in attr_spec: + decoded = self._decode_enum(decoded, attr_spec) elif attr_spec["type"] in NlAttr.type_formats: decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) if 'enum' in attr_spec: @@ -1034,12 +1056,13 @@ class YnlFamily(SpecFamily): op_rsp = [] while not done: reply = self.sock.recv(self._recv_size) - nms = NlMsgs(reply, attr_space=op.attr_set) + nms = NlMsgs(reply) self._recv_dbg_print(reply, nms) for nl_msg in nms: if nl_msg.nl_seq in reqs_by_seq: (op, vals, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq] if nl_msg.extack: + nl_msg.annotate_extack(op.attr_set) self._decode_extack(req_msg, op, nl_msg.extack, vals) else: op = None diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py index 76032e01c2e7..eb295756c3bf 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -275,9 +275,8 @@ class Type(SpecAttr): def _setter_lines(self, ri, member, presence): raise Exception(f"Setter not implemented for class type {self.type}") - def setter(self, ri, space, direction, deref=False, ref=None): + def setter(self, ri, space, direction, deref=False, ref=None, var="req"): ref = (ref if ref else []) + [self.c_name] - var = "req" member = f"{var}->{'.'.join(ref)}" local_vars = [] @@ -332,7 +331,7 @@ class TypeUnused(Type): def attr_get(self, ri, var, first): pass - def setter(self, ri, space, direction, deref=False, ref=None): + def setter(self, ri, space, direction, deref=False, ref=None, var=None): pass @@ -355,7 +354,7 @@ class TypePad(Type): def attr_policy(self, cw): pass - def setter(self, ri, space, direction, deref=False, ref=None): + def setter(self, ri, space, direction, deref=False, ref=None, var=None): pass @@ -695,13 +694,14 @@ class TypeNest(Type): f"parg.data = &{var}->{self.c_name};"] return get_lines, init_lines, None - def setter(self, ri, space, direction, deref=False, ref=None): + def setter(self, ri, space, direction, deref=False, ref=None, var="req"): ref = (ref if ref else []) + [self.c_name] for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list(): if attr.is_recursive(): continue - attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref) + attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref, + var=var) class TypeMultiAttr(Type): @@ -830,7 +830,7 @@ class TypeArrayNest(Type): 'ynl_attr_for_each_nested(attr2, attr) {', '\tif (ynl_attr_validate(yarg, attr2))', '\t\treturn YNL_PARSE_CB_ERROR;', - f'\t{var}->_count.{self.c_name}++;', + f'\tn_{self.c_name}++;', '}'] return get_lines, None, local_vars @@ -1879,7 +1879,9 @@ def rdir(direction): def op_prefix(ri, direction, deref=False): suffix = f"_{ri.type_name}" - if not ri.op_mode or ri.op_mode == 'do': + if not ri.op_mode: + pass + elif ri.op_mode == 'do': suffix += f"{direction_to_suffix[direction]}" else: if direction == 'request': @@ -2470,11 +2472,22 @@ def free_arg_name(direction): return 'obj' -def print_alloc_wrapper(ri, direction): +def print_alloc_wrapper(ri, direction, struct=None): name = op_prefix(ri, direction) - ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"]) + struct_name = name + if ri.type_name_conflict: + struct_name += '_' + + args = ["void"] + cnt = "1" + if struct and struct.in_multi_val: + args = ["unsigned int n"] + cnt = "n" + + ri.cw.write_func_prot(f'static inline struct {struct_name} *', + f"{name}_alloc", args) ri.cw.block_start() - ri.cw.p(f'return calloc(1, sizeof(struct {name}));') + ri.cw.p(f'return calloc({cnt}, sizeof(struct {struct_name}));') ri.cw.block_end() @@ -2544,6 +2557,19 @@ def print_type(ri, direction): def print_type_full(ri, struct): _print_type(ri, "", struct) + if struct.request and struct.in_multi_val: + print_alloc_wrapper(ri, "", struct) + ri.cw.nl() + free_rsp_nested_prototype(ri) + ri.cw.nl() + + # Name conflicts are too hard to deal with with the current code base, + # they are very rare so don't bother printing setters in that case. + if ri.ku_space == 'user' and not ri.type_name_conflict: + for _, attr in struct.member_list(): + attr.setter(ri, ri.attr_set, "", var="obj") + ri.cw.nl() + def print_type_helpers(ri, direction, deref=False): print_free_prototype(ri, direction) @@ -3515,9 +3541,6 @@ def main(): for attr_set, struct in parsed.pure_nested_structs.items(): ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set) print_type_full(ri, struct) - if struct.request and struct.in_multi_val: - free_rsp_nested_prototype(ri) - cw.nl() for op_name, op in parsed.ops.items(): cw.p(f"/* ============== {op.enum_name} ============== */") diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c index e39f86d97002..a80b75f7b061 100644 --- a/tools/objtool/arch/loongarch/special.c +++ b/tools/objtool/arch/loongarch/special.c @@ -27,6 +27,7 @@ static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, struct table_info *next_table; unsigned long tmp_insn_offset; unsigned long tmp_rodata_offset; + bool is_valid_list = false; rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); if (!rsec) @@ -35,6 +36,12 @@ static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, INIT_LIST_HEAD(&table_list); for_each_reloc(rsec, reloc) { + if (reloc->sym->sec->rodata) + continue; + + if (strcmp(insn->sec->name, reloc->sym->sec->name)) + continue; + orig_table = malloc(sizeof(struct table_info)); if (!orig_table) { WARN("malloc failed"); @@ -49,6 +56,22 @@ static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) break; + + if (strcmp(insn->sec->name, (reloc + 1)->sym->sec->name)) { + list_for_each_entry(orig_table, &table_list, jump_info) { + if (orig_table->insn_offset == insn->offset) { + is_valid_list = true; + break; + } + } + + if (!is_valid_list) { + list_del_init(&table_list); + continue; + } + + break; + } } list_for_each_entry(orig_table, &table_list, jump_info) { diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f23bdda737aa..d14f20ef1db1 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -224,6 +224,7 @@ static bool is_rust_noreturn(const struct symbol *func) str_ends_with(func->name, "_4core9panicking14panic_explicit") || str_ends_with(func->name, "_4core9panicking14panic_nounwind") || str_ends_with(func->name, "_4core9panicking18panic_bounds_check") || + str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || @@ -1192,8 +1193,8 @@ static const char *uaccess_safe_builtin[] = { "__ubsan_handle_type_mismatch_v1", "__ubsan_handle_shift_out_of_bounds", "__ubsan_handle_load_invalid_value", - /* STACKLEAK */ - "stackleak_track_stack", + /* KSTACK_ERASE */ + "__sanitizer_cov_stack_depth", /* TRACE_BRANCH_PROFILING */ "ftrace_likely_update", /* STACKPROTECTOR */ @@ -2318,6 +2319,7 @@ static int read_annotate(struct objtool_file *file, for_each_reloc(sec->rsec, reloc) { type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4); + type = bswap_if_needed(file->elf, type); offset = reloc->sym->offset + reloc_addend(reloc); insn = find_insn(file, reloc->sym->sec, offset); diff --git a/tools/objtool/noreturns.h b/tools/objtool/noreturns.h index eacfe3b0a8d1..6a922d046b8e 100644 --- a/tools/objtool/noreturns.h +++ b/tools/objtool/noreturns.h @@ -38,6 +38,7 @@ NORETURN(mpt_halt_firmware) NORETURN(mwait_play_dead) NORETURN(nmi_panic_self_stop) NORETURN(panic) +NORETURN(vpanic) NORETURN(panic_smp_self_stop) NORETURN(rest_init) NORETURN(rewind_stack_and_make_dead) diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 5aaf73df6700..b64302a76144 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -48,8 +48,6 @@ libbpf/ libperf/ libsubcmd/ libsymbol/ -libtraceevent/ -libtraceevent_plugins/ fixdep Documentation/doc.dep python_ext_build/ diff --git a/tools/perf/Build b/tools/perf/Build index 06107f1e1d42..b03cc59dabf8 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -73,7 +73,7 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -s bash -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/Documentation/perf-amd-ibs.txt b/tools/perf/Documentation/perf-amd-ibs.txt index 55f80beae037..548549935760 100644 --- a/tools/perf/Documentation/perf-amd-ibs.txt +++ b/tools/perf/Documentation/perf-amd-ibs.txt @@ -171,23 +171,48 @@ Below is a simple example of the perf mem tool. # perf mem report A normal perf mem report output will provide detailed memory access profile. -However, it can also be aggregated based on output fields. For example: +New output fields will show related access info together. For example: - # perf mem report -F mem,sample,snoop - Samples: 3M of event 'ibs_op//', Event count (approx.): 23524876 - Memory access Samples Snoop - N/A 1903343 N/A - L1 hit 1056754 N/A - L2 hit 75231 N/A - L3 hit 9496 HitM - L3 hit 2270 N/A - RAM hit 8710 N/A - Remote node, same socket RAM hit 3241 N/A - Remote core, same node Any cache hit 1572 HitM - Remote core, same node Any cache hit 514 N/A - Remote node, same socket Any cache hit 1216 HitM - Remote node, same socket Any cache hit 350 N/A - Uncached hit 18 N/A + # perf mem report -F overhead,cache,snoop,comm + ... + # Samples: 92K of event 'ibs_op//' + # Total weight : 531104 + # + # ---------- Cache ----------- --- Snoop ---- + # Overhead L1 L2 L1-buf Other HitM Other Command + # ........ ............................ .............. .......... + # + 76.07% 5.8% 35.7% 0.0% 34.6% 23.3% 52.8% cc1 + 5.79% 0.2% 0.0% 0.0% 5.6% 0.1% 5.7% make + 5.78% 0.1% 4.4% 0.0% 1.2% 0.5% 5.3% gcc + 5.33% 0.3% 3.9% 0.0% 1.1% 0.2% 5.2% as + 5.00% 0.1% 3.8% 0.0% 1.0% 0.3% 4.7% sh + 1.56% 0.1% 0.1% 0.0% 1.4% 0.6% 0.9% ld + 0.28% 0.1% 0.0% 0.0% 0.2% 0.1% 0.2% pkg-config + 0.09% 0.0% 0.0% 0.0% 0.1% 0.0% 0.1% git + 0.03% 0.0% 0.0% 0.0% 0.0% 0.0% 0.0% rm + ... + +Also, it can be aggregated based on various memory access info using the +sort keys. For example: + + # perf mem report -s mem,snoop + ... + # Samples: 92K of event 'ibs_op//' + # Total weight : 531104 + # Sort order : mem,snoop + # + # Overhead Samples Memory access Snoop + # ........ ............ ....................................... ............ + # + 47.99% 1509 L2 hit N/A + 25.08% 338 core, same node Any cache hit HitM + 10.24% 54374 N/A N/A + 6.77% 35938 L1 hit N/A + 6.39% 101 core, same node Any cache hit N/A + 3.50% 69 RAM hit N/A + 0.03% 158 LFB/MAB hit N/A + 0.00% 2 Uncached hit N/A Please refer to their man page for more detail. diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt index a764a4629220..ee92042082f7 100644 --- a/tools/perf/Documentation/perf-check.txt +++ b/tools/perf/Documentation/perf-check.txt @@ -52,8 +52,8 @@ feature:: dwarf-unwind / HAVE_DWARF_UNWIND_SUPPORT auxtrace / HAVE_AUXTRACE_SUPPORT libbfd / HAVE_LIBBFD_SUPPORT + libbpf-strings / HAVE_LIBBPF_STRINGS_SUPPORT libcapstone / HAVE_LIBCAPSTONE_SUPPORT - libcrypto / HAVE_LIBCRYPTO_SUPPORT libdw-dwarf-unwind / HAVE_LIBDW_SUPPORT libelf / HAVE_LIBELF_SUPPORT libnuma / HAVE_LIBNUMA_SUPPORT diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index b77f58c4d2fd..3f3808e513fe 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -123,6 +123,10 @@ OPTIONS for 'perf ftrace trace' --graph-opts:: List of options allowed to set: + - args - Show function arguments. + - retval - Show function return value. + - retval-hex - Show function return value in hexadecimal format. + - retaddr - Show function return address. - nosleep-time - Measure on-CPU time only for function_graph tracer. - noirqs - Ignore functions that happen inside interrupt. - verbose - Show process names, PIDs, timestamps, etc. @@ -139,6 +143,12 @@ OPTIONS for 'perf ftrace latency' Set the function name to get the histogram. Unlike perf ftrace trace, it only allows single function to calculate the histogram. +-e:: +--events=:: + Set the pair of events to get the histogram. The histogram is calculated + by the time difference between the two events from the same thread. This + requires -b/--use-bpf option. + -b:: --use-bpf:: Use BPF to measure function latency instead of using the ftrace (it diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index ce0735021473..28215306a78a 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -278,26 +278,33 @@ also be supplied. For example: perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ... -EVENT QUALIFIERS: +EVENT QUALIFIERS +---------------- It is also possible to add extra qualifiers to an event: percore: -Sums up the event counts for all hardware threads in a core, e.g.: - - - perf stat -e cpu/event=0,umask=0x3,percore=1/ + Sums up the event counts for all hardware threads in a core, e.g.: + perf stat -e cpu/event=0,umask=0x3,percore=1/ cpu: -Specifies the CPU to open the event upon. The value may be repeated to -specify opening the event on multiple CPUs: + Specifies a CPU or a range of CPUs to open the event upon. It may + also reference a PMU to copy the CPU mask from. The value may be + repeated to specify opening the event on multiple CPUs. + Example 1: to open the instructions event on CPUs 0 and 2, the + cycles event on CPUs 1 and 2: + perf stat -e instructions/cpu=0,cpu=2/,cycles/cpu=1-2/ -a sleep 1 - perf stat -e instructions/cpu=0,cpu=2/,cycles/cpu=1,cpu=2/ -a sleep 1 - perf stat -e data_read/cpu=0/,data_write/cpu=1/ -a sleep 1 + Example 2: to open the data_read uncore event on CPU 0 and the + data_write uncore event on CPU 1: + perf stat -e data_read/cpu=0/,data_write/cpu=1/ -a sleep 1 + Example 3: to open the software msr/tsc/ event only on the CPUs + matching those from the cpu_core PMU: + perf stat -e msr/tsc,cpu=cpu_core/ -a sleep 1 EVENT GROUPS ------------ diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 965e73d37772..4d164836d094 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt @@ -119,6 +119,22 @@ REPORT OPTIONS And the default sort keys are changed to local_weight, mem, sym, dso, symbol_daddr, dso_daddr, snoop, tlb, locked, blocked, local_ins_lat. +-F:: +--fields=:: + Specify output field - multiple keys can be specified in CSV format. + Please see linkperf:perf-report[1] for details. + + In addition to the default fields, 'perf mem report' will provide the + following fields to break down sample periods. + + - op: operation in the sample instruction (load, store, prefetch, ...) + - cache: location in CPU cache (L1, L2, ...) where the sample hit + - mem: location in memory or other places the sample hit + - dtlb: location in Data TLB (L1, L2) where the sample hit + - snoop: snoop result for the sampled data access + + Please take a look at the OUTPUT FIELD SELECTION section for caveats. + -T:: --type-profile:: Show data-type profile result instead of code symbols. This requires @@ -156,6 +172,40 @@ but one sample with weight 180 and the other with weight 20: 90% [k] memcpy 10% [.] strcmp +OUTPUT FIELD SELECTION +---------------------- +"perf mem report" adds a number of new output fields specific to data source +information in the sample. Some of them have the same name with the existing +sort keys ("mem" and "snoop"). So unlike other fields and sort keys, they'll +behave differently when it's used by -F/--fields or -s/--sort. + +Using those two as output fields will aggregate samples altogether and show +breakdown. + + $ perf mem report -F mem,snoop + ... + # ------ Memory ------- --- Snoop ---- + # RAM Uncach Other HitM Other + # ..................... .............. + # + 3.5% 0.0% 96.5% 25.1% 74.9% + +But using the same name for sort keys will aggregate samples for each type +separately. + + $ perf mem report -s mem,snoop + # Overhead Samples Memory access Snoop + # ........ ............ ....................................... ............ + # + 47.99% 1509 L2 hit N/A + 25.08% 338 core, same node Any cache hit HitM + 10.24% 54374 N/A N/A + 6.77% 35938 L1 hit N/A + 6.39% 101 core, same node Any cache hit N/A + 3.50% 69 RAM hit N/A + 0.03% 158 LFB/MAB hit N/A + 0.00% 2 Uncached hit N/A + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-arm-spe[1] diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 612612fa2d80..067891bd7da6 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -563,7 +563,9 @@ Specify vmlinux path which has debuginfo. Record build-id of all DSOs regardless whether it's actually hit or not. --buildid-mmap:: -Record build ids in mmap2 events, disables build id cache (implies --no-buildid). +Legacy record build-id in map events option which is now the +default. Behaves indentically to --no-buildid. Disable with +--no-buildid-mmap. --aio[=n]:: Use control blocks in asynchronous (Posix AIO) trace writing mode (default: 1, max: 4). diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 61d091670dee..1a766d4a2233 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -640,18 +640,20 @@ JSON FORMAT With -j, perf stat is able to print out a JSON format output that can be used for parsing. -- timestamp : optional usec time stamp in fractions of second (with -I) +- interval : optional timestamp in fractions of second (with -I) - optional aggregate options: - core : core identifier (with --per-core) - die : die identifier (with --per-die) - socket : socket identifier (with --per-socket) - node : node identifier (with --per-node) - thread : thread identifier (with --per-thread) +- counters : number of aggregated PMU counters - counter-value : counter value - unit : unit of the counter value or empty - event : event name - variance : optional variance if multiple values are collected (with -r) -- runtime : run time of counter +- event-runtime : run time of the event +- pcnt-running : percentage of time the event was running - metric-value : optional metric value - metric-unit : optional unit of metric diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index c1fb6056a0d3..973fede403a0 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -238,14 +238,6 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. the same beautifiers used in the strace-like enter+exit lines to augment the tracepoint arguments. ---map-dump:: - Dump BPF maps setup by events passed via -e, for instance the augmented_raw_syscalls - living in tools/perf/examples/bpf/augmented_raw_syscalls.c. For now this - dumps just boolean map values and integer keys, in time this will print in hex - by default and use BTF when available, as well as use functions to do pretty - printing using the existing 'perf trace' syscall arg beautifiers to map integer - arguments to strings (pid to comm, syscall id to syscall name, etc). - --force-btf:: Use btf_dump to pretty print syscall argument data, instead of using hand-crafted pretty printers. This option is intended for testing BTF integration in perf trace. btf_dump-based diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index d1ea7bf44964..5a5832ee7b53 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -19,6 +19,10 @@ detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected) CFLAGS := $(EXTRA_CFLAGS) $(filter-out -Wnested-externs,$(EXTRA_WARNINGS)) HOSTCFLAGS := $(filter-out -Wnested-externs,$(EXTRA_WARNINGS)) +# This is required because the kernel is built with this and some of the code +# borrowed from kernel headers depends on it, e.g. put_unaligned_*(). +CFLAGS += -fno-strict-aliasing + # Enabled Wthread-safety analysis for clang builds. ifeq ($(CC_NO_CLANG), 0) CFLAGS += -Wthread-safety @@ -130,8 +134,6 @@ ifndef NO_LIBUNWIND FEATURE_CHECK_LDFLAGS-libunwind-x86_64 += -lunwind -llzma -lunwind-x86_64 endif -FEATURE_CHECK_LDFLAGS-libcrypto = -lcrypto - ifdef CSINCLUDES LIBOPENCSD_CFLAGS := -I$(CSINCLUDES) endif @@ -595,8 +597,16 @@ ifndef NO_LIBELF LIBBPF_STATIC := 1 $(call detected,CONFIG_LIBBPF) CFLAGS += -DHAVE_LIBBPF_SUPPORT + LIBBPF_INCLUDE = $(LIBBPF_DIR)/.. endif endif + + FEATURE_CHECK_CFLAGS-libbpf-strings="-I$(LIBBPF_INCLUDE)" + $(call feature_check,libbpf-strings) + ifeq ($(feature-libbpf-strings), 1) + $(call detected,CONFIG_LIBBPF_STRINGS) + CFLAGS += -DHAVE_LIBBPF_STRINGS_SUPPORT + endif endif endif # NO_LIBBPF endif # NO_LIBELF @@ -772,17 +782,6 @@ ifneq ($(NO_LIBTRACEEVENT),1) $(call detected,CONFIG_TRACE) endif -ifndef NO_LIBCRYPTO - ifneq ($(feature-libcrypto), 1) - $(warning No libcrypto.h found, disables jitted code injection, please install openssl-devel or libssl-dev) - NO_LIBCRYPTO := 1 - else - CFLAGS += -DHAVE_LIBCRYPTO_SUPPORT - EXTLIBS += -lcrypto - $(call detected,CONFIG_CRYPTO) - endif -endif - ifndef NO_SLANG ifneq ($(feature-libslang), 1) ifneq ($(feature-libslang-include-subdir), 1) @@ -918,6 +917,8 @@ ifneq ($(NO_JEVENTS),1) endif ifdef BUILD_NONDISTRO + $(call feature_check,libbfd) + ifeq ($(feature-libbfd), 1) EXTLIBS += -lbfd -lopcodes else @@ -946,6 +947,9 @@ ifdef BUILD_NONDISTRO CFLAGS += -DHAVE_LIBBFD_SUPPORT CXXFLAGS += -DHAVE_LIBBFD_SUPPORT + + $(call feature_check,libbfd-buildid) + ifeq ($(feature-libbfd-buildid), 1) CFLAGS += -DHAVE_LIBBFD_BUILDID_SUPPORT else @@ -1144,7 +1148,7 @@ ifndef NO_JVMTI endif endif # NO_JVMTI_CMLR else - $(warning No openjdk development package found, please install JDK package, e.g. openjdk-8-jdk, java-1.8.0-openjdk-devel) + $(warning No openjdk development package found, please install JDK package, e.g. openjdk-8-jdk, java-latest-openjdk-devel) NO_JVMTI := 1 endif endif @@ -1157,7 +1161,7 @@ ifndef NO_LIBPFM4 ASCIIDOC_EXTRA = -aHAVE_LIBPFM=1 $(call detected,CONFIG_LIBPFM4) else - $(warning libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev) + $(warning libpfm4 not found, disables libpfm4 support. Please install libpfm-devel or libpfm4-dev) endif endif diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index d4c7031b01a7..e2150acc2c13 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -61,9 +61,6 @@ include ../scripts/utilities.mak # # Define NO_LIBBIONIC if you do not want bionic support # -# Define NO_LIBCRYPTO if you do not want libcrypto (openssl) support -# used for generating build-ids for ELFs generated by jitdump. -# # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support # for dwarf backtrace post unwind. # @@ -262,6 +259,8 @@ ifneq ($(SHELLCHECK),) ifeq ($(shell expr $(shell $(SHELLCHECK) --version | grep version: | \ sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \< 060), 1) SHELLCHECK := + else + SHELLCHECK := $(SHELLCHECK) -s bash -a -S warning endif endif @@ -1250,8 +1249,11 @@ else $(Q)cp "$(VMLINUX_H)" $@ endif -$(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) $(SKEL_OUT)/vmlinux.h | $(SKEL_TMP_OUT) - $(QUIET_CLANG)$(CLANG) -g -O2 --target=bpf $(CLANG_OPTIONS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \ +$(SKEL_TMP_OUT)/%.bpf.o: $(OUTPUT)PERF-VERSION-FILE util/bpf_skel/perf_version.h | $(SKEL_TMP_OUT) +$(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) $(SKEL_OUT)/vmlinux.h + $(QUIET_CLANG)$(CLANG) -g -O2 -fno-stack-protector --target=bpf \ + $(CLANG_OPTIONS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \ + -include $(OUTPUT)PERF-VERSION-FILE -include util/bpf_skel/perf_version.h \ -c $(filter util/bpf_skel/%.bpf.c,$^) -o $@ $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o | $(BPFTOOL) diff --git a/tools/perf/arch/arm/entry/syscalls/syscall.tbl b/tools/perf/arch/arm/entry/syscalls/syscall.tbl index 27c1d5ebcd91..b07e699aaa3c 100644 --- a/tools/perf/arch/arm/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/arm/entry/syscalls/syscall.tbl @@ -482,3 +482,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl index 1e8c44c7b614..7a7049c2c307 100644 --- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl +++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl @@ -382,3 +382,5 @@ 465 n64 listxattrat sys_listxattrat 466 n64 removexattrat sys_removexattrat 467 n64 open_tree_attr sys_open_tree_attr +468 n64 file_getattr sys_file_getattr +469 n64 file_setattr sys_file_setattr diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index 9a084bdb8926..b453e80dfc00 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -558,3 +558,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index ed82715080f9..fdd6a77a3432 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -5,7 +5,6 @@ perf-util-y += mem-events.o perf-util-y += pmu.o perf-util-y += sym-handling.o perf-util-y += evsel.o -perf-util-y += event.o perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o diff --git a/tools/perf/arch/powerpc/util/event.c b/tools/perf/arch/powerpc/util/event.c deleted file mode 100644 index 77d8cc2b5691..000000000000 --- a/tools/perf/arch/powerpc/util/event.c +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -#include "../../../util/event.h" -#include "../../../util/synthetic-events.h" -#include "../../../util/machine.h" -#include "../../../util/tool.h" -#include "../../../util/map.h" -#include "../../../util/debug.h" -#include "../../../util/sample.h" - -void arch_perf_parse_sample_weight(struct perf_sample *data, - const __u64 *array, u64 type) -{ - union perf_sample_weight weight; - - weight.full = *array; - if (type & PERF_SAMPLE_WEIGHT) - data->weight = weight.full; - else { - data->weight = weight.var1_dw; - data->ins_lat = weight.var2_w; - data->p_stage_cyc = weight.var3_w; - } -} - -void arch_perf_synthesize_sample_weight(const struct perf_sample *data, - __u64 *array, u64 type) -{ - *array = data->weight; - - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - *array &= 0xffffffff; - *array |= ((u64)data->ins_lat << 32); - } -} - -const char *arch_perf_header_entry(const char *se_header) -{ - if (!strcmp(se_header, "Local INSTR Latency")) - return "Finish Cyc"; - else if (!strcmp(se_header, "INSTR Latency")) - return "Global Finish_cyc"; - else if (!strcmp(se_header, "Local Pipeline Stage Cycle")) - return "Dispatch Cyc"; - else if (!strcmp(se_header, "Pipeline Stage Cycle")) - return "Global Dispatch_cyc"; - return se_header; -} - -int arch_support_sort_key(const char *sort_key) -{ - if (!strcmp(sort_key, "p_stage_cyc")) - return 1; - if (!strcmp(sort_key, "local_p_stage_cyc")) - return 1; - return 0; -} diff --git a/tools/perf/arch/riscv/util/kvm-stat.c b/tools/perf/arch/riscv/util/kvm-stat.c index 491aef449d1a..3ea7acb5e159 100644 --- a/tools/perf/arch/riscv/util/kvm-stat.c +++ b/tools/perf/arch/riscv/util/kvm-stat.c @@ -9,10 +9,10 @@ #include #include "../../../util/evsel.h" #include "../../../util/kvm-stat.h" -#include "riscv_exception_types.h" +#include "riscv_trap_types.h" #include "debug.h" -define_exit_reasons_table(riscv_exit_reasons, kvm_riscv_exception_class); +define_exit_reasons_table(riscv_exit_reasons, kvm_riscv_trap_class); const char *vcpu_id_str = "id"; const char *kvm_exit_reason = "scause"; @@ -30,7 +30,7 @@ static void event_get_key(struct evsel *evsel, struct event_key *key) { key->info = 0; - key->key = evsel__intval(evsel, sample, kvm_exit_reason); + key->key = evsel__intval(evsel, sample, kvm_exit_reason) & ~CAUSE_IRQ_FLAG; key->exit_reasons = riscv_exit_reasons; } diff --git a/tools/perf/arch/riscv/util/riscv_exception_types.h b/tools/perf/arch/riscv/util/riscv_exception_types.h deleted file mode 100644 index c49b8fa5e847..000000000000 --- a/tools/perf/arch/riscv/util/riscv_exception_types.h +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#ifndef ARCH_PERF_RISCV_EXCEPTION_TYPES_H -#define ARCH_PERF_RISCV_EXCEPTION_TYPES_H - -#define EXC_INST_MISALIGNED 0 -#define EXC_INST_ACCESS 1 -#define EXC_INST_ILLEGAL 2 -#define EXC_BREAKPOINT 3 -#define EXC_LOAD_MISALIGNED 4 -#define EXC_LOAD_ACCESS 5 -#define EXC_STORE_MISALIGNED 6 -#define EXC_STORE_ACCESS 7 -#define EXC_SYSCALL 8 -#define EXC_HYPERVISOR_SYSCALL 9 -#define EXC_SUPERVISOR_SYSCALL 10 -#define EXC_INST_PAGE_FAULT 12 -#define EXC_LOAD_PAGE_FAULT 13 -#define EXC_STORE_PAGE_FAULT 15 -#define EXC_INST_GUEST_PAGE_FAULT 20 -#define EXC_LOAD_GUEST_PAGE_FAULT 21 -#define EXC_VIRTUAL_INST_FAULT 22 -#define EXC_STORE_GUEST_PAGE_FAULT 23 - -#define EXC(x) {EXC_##x, #x } - -#define kvm_riscv_exception_class \ - EXC(INST_MISALIGNED), EXC(INST_ACCESS), EXC(INST_ILLEGAL), \ - EXC(BREAKPOINT), EXC(LOAD_MISALIGNED), EXC(LOAD_ACCESS), \ - EXC(STORE_MISALIGNED), EXC(STORE_ACCESS), EXC(SYSCALL), \ - EXC(HYPERVISOR_SYSCALL), EXC(SUPERVISOR_SYSCALL), \ - EXC(INST_PAGE_FAULT), EXC(LOAD_PAGE_FAULT), EXC(STORE_PAGE_FAULT), \ - EXC(INST_GUEST_PAGE_FAULT), EXC(LOAD_GUEST_PAGE_FAULT), \ - EXC(VIRTUAL_INST_FAULT), EXC(STORE_GUEST_PAGE_FAULT) - -#endif /* ARCH_PERF_RISCV_EXCEPTION_TYPES_H */ diff --git a/tools/perf/arch/riscv/util/riscv_trap_types.h b/tools/perf/arch/riscv/util/riscv_trap_types.h new file mode 100644 index 000000000000..6cc71eb01fca --- /dev/null +++ b/tools/perf/arch/riscv/util/riscv_trap_types.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef ARCH_PERF_RISCV_TRAP_TYPES_H +#define ARCH_PERF_RISCV_TRAP_TYPES_H + +/* Exception cause high bit - is an interrupt if set */ +#define CAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1)) + +/* Interrupt causes (minus the high bit) */ +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 + +/* Exception causes */ +#define EXC_INST_MISALIGNED 0 +#define EXC_INST_ACCESS 1 +#define EXC_INST_ILLEGAL 2 +#define EXC_BREAKPOINT 3 +#define EXC_LOAD_MISALIGNED 4 +#define EXC_LOAD_ACCESS 5 +#define EXC_STORE_MISALIGNED 6 +#define EXC_STORE_ACCESS 7 +#define EXC_SYSCALL 8 +#define EXC_HYPERVISOR_SYSCALL 9 +#define EXC_SUPERVISOR_SYSCALL 10 +#define EXC_INST_PAGE_FAULT 12 +#define EXC_LOAD_PAGE_FAULT 13 +#define EXC_STORE_PAGE_FAULT 15 +#define EXC_INST_GUEST_PAGE_FAULT 20 +#define EXC_LOAD_GUEST_PAGE_FAULT 21 +#define EXC_VIRTUAL_INST_FAULT 22 +#define EXC_STORE_GUEST_PAGE_FAULT 23 + +#define TRAP(x) { x, #x } + +#define kvm_riscv_trap_class \ + TRAP(IRQ_S_SOFT), TRAP(IRQ_VS_SOFT), TRAP(IRQ_M_SOFT), \ + TRAP(IRQ_S_TIMER), TRAP(IRQ_VS_TIMER), TRAP(IRQ_M_TIMER), \ + TRAP(IRQ_S_EXT), TRAP(IRQ_VS_EXT), TRAP(IRQ_M_EXT), \ + TRAP(IRQ_S_GEXT), TRAP(IRQ_PMU_OVF), \ + TRAP(EXC_INST_MISALIGNED), TRAP(EXC_INST_ACCESS), TRAP(EXC_INST_ILLEGAL), \ + TRAP(EXC_BREAKPOINT), TRAP(EXC_LOAD_MISALIGNED), TRAP(EXC_LOAD_ACCESS), \ + TRAP(EXC_STORE_MISALIGNED), TRAP(EXC_STORE_ACCESS), TRAP(EXC_SYSCALL), \ + TRAP(EXC_HYPERVISOR_SYSCALL), TRAP(EXC_SUPERVISOR_SYSCALL), \ + TRAP(EXC_INST_PAGE_FAULT), TRAP(EXC_LOAD_PAGE_FAULT), \ + TRAP(EXC_STORE_PAGE_FAULT), TRAP(EXC_INST_GUEST_PAGE_FAULT), \ + TRAP(EXC_LOAD_GUEST_PAGE_FAULT), TRAP(EXC_VIRTUAL_INST_FAULT), \ + TRAP(EXC_STORE_GUEST_PAGE_FAULT) + +#endif /* ARCH_PERF_RISCV_TRAP_TYPES_H */ diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index a4569b96ef06..8a6744d658db 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -470,3 +470,5 @@ 465 common listxattrat sys_listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr sys_file_setattr diff --git a/tools/perf/arch/sh/entry/syscalls/syscall.tbl b/tools/perf/arch/sh/entry/syscalls/syscall.tbl index 52a7652fcff6..5e9c9eff5539 100644 --- a/tools/perf/arch/sh/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/sh/entry/syscalls/syscall.tbl @@ -471,3 +471,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/perf/arch/sparc/entry/syscalls/syscall.tbl b/tools/perf/arch/sparc/entry/syscalls/syscall.tbl index 83e45eb6c095..ebb7d06d1044 100644 --- a/tools/perf/arch/sparc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/sparc/entry/syscalls/syscall.tbl @@ -513,3 +513,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/perf/arch/x86/Build b/tools/perf/arch/x86/Build index afae7b8f6bd6..d31a1168757c 100644 --- a/tools/perf/arch/x86/Build +++ b/tools/perf/arch/x86/Build @@ -10,6 +10,6 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-test-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl index ac007ea00979..4877e16da69a 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl @@ -473,3 +473,5 @@ 465 i386 listxattrat sys_listxattrat 466 i386 removexattrat sys_removexattrat 467 i386 open_tree_attr sys_open_tree_attr +468 i386 file_getattr sys_file_getattr +469 i386 file_setattr sys_file_setattr diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index cfb5ca41e30d..92cf0fe2291e 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -391,6 +391,8 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr # # Due to a historical design error, certain syscalls are numbered differently diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 4fd425157d7d..7d65b9e51840 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -2,6 +2,8 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H +#include "tests/tests.h" + struct test_suite; /* Tests */ @@ -12,11 +14,12 @@ int test__insn_x86(struct test_suite *test, int subtest); int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest); int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest); int test__bp_modify(struct test_suite *test, int subtest); -int test__x86_sample_parsing(struct test_suite *test, int subtest); int test__amd_ibs_via_core_pmu(struct test_suite *test, int subtest); int test__amd_ibs_period(struct test_suite *test, int subtest); int test__hybrid(struct test_suite *test, int subtest); +DECLARE_SUITE(x86_topdown); + extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 5e00cbfd2d56..7790b3e20f4e 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build @@ -2,7 +2,6 @@ perf-test-$(CONFIG_DWARF_UNWIND) += regs_load.o perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-test-y += arch-tests.o -perf-test-y += sample-parsing.o perf-test-y += hybrid.o perf-test-$(CONFIG_AUXTRACE) += intel-pt-test.o ifeq ($(CONFIG_EXTRA_TESTS),y) @@ -11,6 +10,7 @@ endif perf-test-$(CONFIG_X86_64) += bp-modify.o perf-test-y += amd-ibs-via-core-pmu.o perf-test-y += amd-ibs-period.o +perf-test-y += topdown.o ifdef SHELLCHECK SHELL_TESTS := gen-insn-x86-dat.sh @@ -22,6 +22,6 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-test-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index bfee2432515b..8f9cfeaa170f 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -23,7 +23,6 @@ struct test_suite suite__intel_pt = { #if defined(__x86_64__) DEFINE_SUITE("x86 bp modify", bp_modify); #endif -DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing); DEFINE_SUITE("AMD IBS via core pmu", amd_ibs_via_core_pmu); DEFINE_SUITE_EXCLUSIVE("AMD IBS sample period", amd_ibs_period); static struct test_case hybrid_tests[] = { @@ -49,9 +48,9 @@ struct test_suite *arch_tests[] = { #if defined(__x86_64__) &suite__bp_modify, #endif - &suite__x86_sample_parsing, &suite__amd_ibs_via_core_pmu, &suite__amd_ibs_period, &suite__hybrid, + &suite__x86_topdown, NULL, }; diff --git a/tools/perf/arch/x86/tests/sample-parsing.c b/tools/perf/arch/x86/tests/sample-parsing.c deleted file mode 100644 index a061e8619267..000000000000 --- a/tools/perf/arch/x86/tests/sample-parsing.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include -#include -#include -#include - -#include "event.h" -#include "evsel.h" -#include "debug.h" -#include "util/sample.h" -#include "util/synthetic-events.h" - -#include "tests/tests.h" -#include "arch-tests.h" - -#define COMP(m) do { \ - if (s1->m != s2->m) { \ - pr_debug("Samples differ at '"#m"'\n"); \ - return false; \ - } \ -} while (0) - -static bool samples_same(const struct perf_sample *s1, - const struct perf_sample *s2, - u64 type) -{ - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - COMP(ins_lat); - COMP(retire_lat); - } - - return true; -} - -static int do_test(u64 sample_type) -{ - struct evsel evsel = { - .needs_swap = false, - .core = { - . attr = { - .sample_type = sample_type, - .read_format = 0, - }, - }, - }; - union perf_event *event; - struct perf_sample sample = { - .weight = 101, - .ins_lat = 102, - .retire_lat = 103, - }; - struct perf_sample sample_out; - size_t i, sz, bufsz; - int err, ret = -1; - - sz = perf_event__sample_event_size(&sample, sample_type, 0); - bufsz = sz + 4096; /* Add a bit for overrun checking */ - event = malloc(bufsz); - if (!event) { - pr_debug("malloc failed\n"); - return -1; - } - - memset(event, 0xff, bufsz); - event->header.type = PERF_RECORD_SAMPLE; - event->header.misc = 0; - event->header.size = sz; - - err = perf_event__synthesize_sample(event, sample_type, 0, &sample); - if (err) { - pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", - "perf_event__synthesize_sample", sample_type, err); - goto out_free; - } - - /* The data does not contain 0xff so we use that to check the size */ - for (i = bufsz; i > 0; i--) { - if (*(i - 1 + (u8 *)event) != 0xff) - break; - } - if (i != sz) { - pr_debug("Event size mismatch: actual %zu vs expected %zu\n", - i, sz); - goto out_free; - } - - evsel.sample_size = __evsel__sample_size(sample_type); - - err = evsel__parse_sample(&evsel, event, &sample_out); - if (err) { - pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", - "evsel__parse_sample", sample_type, err); - goto out_free; - } - - if (!samples_same(&sample, &sample_out, sample_type)) { - pr_debug("parsing failed for sample_type %#"PRIx64"\n", - sample_type); - goto out_free; - } - - ret = 0; -out_free: - free(event); - - return ret; -} - -/** - * test__x86_sample_parsing - test X86 specific sample parsing - * - * This function implements a test that synthesizes a sample event, parses it - * and then checks that the parsed sample matches the original sample. If the - * test passes %0 is returned, otherwise %-1 is returned. - * - * For now, the PERF_SAMPLE_WEIGHT_STRUCT is the only X86 specific sample type. - * The test only checks the PERF_SAMPLE_WEIGHT_STRUCT type. - */ -int test__x86_sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - return do_test(PERF_SAMPLE_WEIGHT_STRUCT); -} diff --git a/tools/perf/arch/x86/tests/topdown.c b/tools/perf/arch/x86/tests/topdown.c new file mode 100644 index 000000000000..1eba3b4594ef --- /dev/null +++ b/tools/perf/arch/x86/tests/topdown.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "arch-tests.h" +#include "../util/topdown.h" +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "pmu.h" +#include "pmus.h" + +static int event_cb(void *state, struct pmu_event_info *info) +{ + char buf[256]; + struct parse_events_error parse_err; + int *ret = state, err; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + + if (!evlist) + return -ENOMEM; + + parse_events_error__init(&parse_err); + snprintf(buf, sizeof(buf), "%s/%s/", info->pmu->name, info->name); + err = parse_events(evlist, buf, &parse_err); + if (err) { + parse_events_error__print(&parse_err, buf); + *ret = TEST_FAIL; + } + parse_events_error__exit(&parse_err); + evlist__for_each_entry(evlist, evsel) { + bool fail = false; + bool p_core_pmu = evsel->pmu->type == PERF_TYPE_RAW; + const char *name = evsel__name(evsel); + + if (strcasestr(name, "uops_retired.slots") || + strcasestr(name, "topdown.backend_bound_slots") || + strcasestr(name, "topdown.br_mispredict_slots") || + strcasestr(name, "topdown.memory_bound_slots") || + strcasestr(name, "topdown.bad_spec_slots") || + strcasestr(name, "topdown.slots_p")) { + if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel)) + fail = true; + } else if (strcasestr(name, "slots")) { + if (arch_is_topdown_slots(evsel) != p_core_pmu || + arch_is_topdown_metrics(evsel)) + fail = true; + } else if (strcasestr(name, "topdown")) { + if (arch_is_topdown_slots(evsel) || + arch_is_topdown_metrics(evsel) != p_core_pmu) + fail = true; + } else if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel)) { + fail = true; + } + if (fail) { + pr_debug("Broken topdown information for '%s'\n", evsel__name(evsel)); + *ret = TEST_FAIL; + } + } + evlist__delete(evlist); + return 0; +} + +static int test__x86_topdown(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + int ret = TEST_OK; + struct perf_pmu *pmu = NULL; + + if (!topdown_sys_has_perf_metrics()) + return TEST_OK; + + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + if (perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/false, &ret, event_cb)) + break; + } + return ret; +} + +DEFINE_SUITE("x86 topdown", x86_topdown); diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c index a0400707180c..3cd384317739 100644 --- a/tools/perf/arch/x86/util/event.c +++ b/tools/perf/arch/x86/util/event.c @@ -91,49 +91,3 @@ int perf_event__synthesize_extra_kmaps(const struct perf_tool *tool, } #endif - -void arch_perf_parse_sample_weight(struct perf_sample *data, - const __u64 *array, u64 type) -{ - union perf_sample_weight weight; - - weight.full = *array; - if (type & PERF_SAMPLE_WEIGHT) - data->weight = weight.full; - else { - data->weight = weight.var1_dw; - data->ins_lat = weight.var2_w; - data->retire_lat = weight.var3_w; - } -} - -void arch_perf_synthesize_sample_weight(const struct perf_sample *data, - __u64 *array, u64 type) -{ - *array = data->weight; - - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - *array &= 0xffffffff; - *array |= ((u64)data->ins_lat << 32); - *array |= ((u64)data->retire_lat << 48); - } -} - -const char *arch_perf_header_entry(const char *se_header) -{ - if (!strcmp(se_header, "Local Pipeline Stage Cycle")) - return "Local Retire Latency"; - else if (!strcmp(se_header, "Pipeline Stage Cycle")) - return "Retire Latency"; - - return se_header; -} - -int arch_support_sort_key(const char *sort_key) -{ - if (!strcmp(sort_key, "p_stage_cyc")) - return 1; - if (!strcmp(sort_key, "local_p_stage_cyc")) - return 1; - return 0; -} diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 1969758cc8c1..75e9d00a1494 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -81,3 +81,27 @@ int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs) /* Default ordering by insertion index. */ return lhs->core.idx - rhs->core.idx; } + +int arch_evlist__add_required_events(struct list_head *list) +{ + struct evsel *pos, *metric_event = NULL; + int idx = 0; + + if (!topdown_sys_has_perf_metrics()) + return 0; + + list_for_each_entry(pos, list, core.node) { + if (arch_is_topdown_slots(pos)) { + /* Slots event already present, nothing to do. */ + return 0; + } + if (metric_event == NULL && arch_is_topdown_metrics(pos)) + metric_event = pos; + idx++; + } + if (metric_event == NULL) { + /* No topdown metric events, nothing to do. */ + return 0; + } + return topdown_insert_slots_event(list, idx + 1, metric_event); +} diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 3dd29ba2c23b..9bc80fff3aa0 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -23,47 +23,25 @@ void arch_evsel__set_sample_weight(struct evsel *evsel) bool evsel__sys_has_perf_metrics(const struct evsel *evsel) { struct perf_pmu *pmu; - u32 type = evsel->core.attr.type; + + if (!topdown_sys_has_perf_metrics()) + return false; /* - * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU - * on a non-hybrid machine, "cpu_core" PMU on a hybrid machine. - * The slots event is only available for the core PMU, which - * supports the perf metrics feature. - * Checking both the PERF_TYPE_RAW type and the slots event - * should be good enough to detect the perf metrics feature. + * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU on a + * non-hybrid machine, "cpu_core" PMU on a hybrid machine. The + * topdown_sys_has_perf_metrics checks the slots event is only available + * for the core PMU, which supports the perf metrics feature. Checking + * both the PERF_TYPE_RAW type and the slots event should be good enough + * to detect the perf metrics feature. */ -again: - switch (type) { - case PERF_TYPE_HARDWARE: - case PERF_TYPE_HW_CACHE: - type = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT; - if (type) - goto again; - break; - case PERF_TYPE_RAW: - break; - default: - return false; - } - - pmu = evsel->pmu; - if (pmu && perf_pmu__is_fake(pmu)) - pmu = NULL; - - if (!pmu) { - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - if (pmu->type == PERF_TYPE_RAW) - break; - } - } - return pmu && perf_pmu__have_event(pmu, "slots"); + pmu = evsel__find_pmu(evsel); + return pmu && pmu->type == PERF_TYPE_RAW; } bool arch_evsel__must_be_in_group(const struct evsel *evsel) { - if (!evsel__sys_has_perf_metrics(evsel) || !evsel->name || - strcasestr(evsel->name, "uops_retired.slots")) + if (!evsel__sys_has_perf_metrics(evsel)) return false; return arch_is_topdown_metrics(evsel) || arch_is_topdown_slots(evsel); diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c index d1c654839049..0d01b662627a 100644 --- a/tools/perf/arch/x86/util/topdown.c +++ b/tools/perf/arch/x86/util/topdown.c @@ -1,6 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include "api/fs/fs.h" -#include "util/evsel.h" #include "util/evlist.h" #include "util/pmu.h" #include "util/pmus.h" @@ -8,6 +6,9 @@ #include "topdown.h" #include "evsel.h" +// cmask=0, inv=0, pc=0, edge=0, umask=4, event=0 +#define TOPDOWN_SLOTS 0x0400 + /* Check whether there is a PMU which supports the perf metrics. */ bool topdown_sys_has_perf_metrics(void) { @@ -32,31 +33,19 @@ bool topdown_sys_has_perf_metrics(void) return has_perf_metrics; } -#define TOPDOWN_SLOTS 0x0400 bool arch_is_topdown_slots(const struct evsel *evsel) { - if (evsel->core.attr.config == TOPDOWN_SLOTS) - return true; - - return false; + return evsel->core.attr.type == PERF_TYPE_RAW && + evsel->core.attr.config == TOPDOWN_SLOTS && + evsel->core.attr.config1 == 0; } bool arch_is_topdown_metrics(const struct evsel *evsel) { - int config = evsel->core.attr.config; - const char *name_from_config; - struct perf_pmu *pmu; - - /* All topdown events have an event code of 0. */ - if ((config & 0xFF) != 0) - return false; - - pmu = evsel__find_pmu(evsel); - if (!pmu || !pmu->is_core) - return false; - - name_from_config = perf_pmu__name_from_config(pmu, config); - return name_from_config && strcasestr(name_from_config, "topdown"); + // cmask=0, inv=0, pc=0, edge=0, umask=0x80-0x87, event=0 + return evsel->core.attr.type == PERF_TYPE_RAW && + (evsel->core.attr.config & 0xFFFFF8FF) == 0x8000 && + evsel->core.attr.config1 == 0; } /* @@ -88,3 +77,31 @@ bool arch_topdown_sample_read(struct evsel *leader) return false; } + +/* + * Make a copy of the topdown metric event metric_event with the given index but + * change its configuration to be a topdown slots event. Copying from + * metric_event ensures modifiers are the same. + */ +int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event) +{ + struct evsel *evsel = evsel__new_idx(&metric_event->core.attr, idx); + + if (!evsel) + return -ENOMEM; + + evsel->core.attr.config = TOPDOWN_SLOTS; + evsel->core.cpus = perf_cpu_map__get(metric_event->core.cpus); + evsel->core.pmu_cpus = perf_cpu_map__get(metric_event->core.pmu_cpus); + evsel->core.is_pmu_core = true; + evsel->pmu = metric_event->pmu; + evsel->name = strdup("slots"); + evsel->precise_max = metric_event->precise_max; + evsel->sample_read = metric_event->sample_read; + evsel->weak_group = metric_event->weak_group; + evsel->bpf_counter = metric_event->bpf_counter; + evsel->retire_lat = metric_event->retire_lat; + evsel__set_leader(evsel, evsel__leader(metric_event)); + list_add_tail(&evsel->core.node, list); + return 0; +} diff --git a/tools/perf/arch/x86/util/topdown.h b/tools/perf/arch/x86/util/topdown.h index 1bae9b1822d7..69035565e649 100644 --- a/tools/perf/arch/x86/util/topdown.h +++ b/tools/perf/arch/x86/util/topdown.h @@ -2,8 +2,14 @@ #ifndef _TOPDOWN_H #define _TOPDOWN_H 1 +#include + +struct evsel; +struct list_head; + bool topdown_sys_has_perf_metrics(void); bool arch_is_topdown_slots(const struct evsel *evsel); bool arch_is_topdown_metrics(const struct evsel *evsel); +int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event); #endif diff --git a/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl b/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl index f657a77314f8..374e4cb788d8 100644 --- a/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl @@ -438,3 +438,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/perf/bench/evlist-open-close.c b/tools/perf/bench/evlist-open-close.c index 79cedcf94a39..bfaf50e4e519 100644 --- a/tools/perf/bench/evlist-open-close.c +++ b/tools/perf/bench/evlist-open-close.c @@ -57,7 +57,7 @@ static int evlist__count_evsel_fds(struct evlist *evlist) return cnt; } -static struct evlist *bench__create_evlist(char *evstr) +static struct evlist *bench__create_evlist(char *evstr, const char *uid_str) { struct parse_events_error err; struct evlist *evlist = evlist__new(); @@ -78,6 +78,18 @@ static struct evlist *bench__create_evlist(char *evstr) goto out_delete_evlist; } parse_events_error__exit(&err); + if (uid_str) { + uid_t uid = parse_uid(uid_str); + + if (uid == UINT_MAX) { + pr_err("Invalid User: %s", uid_str); + ret = -EINVAL; + goto out_delete_evlist; + } + ret = parse_uid_filter(evlist, uid); + if (ret) + goto out_delete_evlist; + } ret = evlist__create_maps(evlist, &opts.target); if (ret < 0) { pr_err("Not enough memory to create thread/cpu maps\n"); @@ -117,10 +129,10 @@ static int bench__do_evlist_open_close(struct evlist *evlist) return 0; } -static int bench_evlist_open_close__run(char *evstr) +static int bench_evlist_open_close__run(char *evstr, const char *uid_str) { // used to print statistics only - struct evlist *evlist = bench__create_evlist(evstr); + struct evlist *evlist = bench__create_evlist(evstr, uid_str); double time_average, time_stddev; struct timeval start, end, diff; struct stats time_stats; @@ -142,7 +154,7 @@ static int bench_evlist_open_close__run(char *evstr) for (i = 0; i < iterations; i++) { pr_debug("Started iteration %d\n", i); - evlist = bench__create_evlist(evstr); + evlist = bench__create_evlist(evstr, uid_str); if (!evlist) return -ENOMEM; @@ -206,6 +218,7 @@ static char *bench__repeat_event_string(const char *evstr, int n) int bench_evlist_open_close(int argc, const char **argv) { + const char *uid_str = NULL; const struct option options[] = { OPT_STRING('e', "event", &event_string, "event", "event selector. use 'perf list' to list available events"), @@ -221,7 +234,7 @@ int bench_evlist_open_close(int argc, const char **argv) "record events on existing process id"), OPT_STRING('t', "tid", &opts.target.tid, "tid", "record events on existing thread id"), - OPT_STRING('u', "uid", &opts.target.uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &uid_str, "user", "user to profile"), OPT_BOOLEAN(0, "per-thread", &opts.target.per_thread, "use per-thread mmaps"), OPT_END() }; @@ -245,15 +258,8 @@ int bench_evlist_open_close(int argc, const char **argv) goto out; } - err = target__parse_uid(&opts.target); - if (err) { - target__strerror(&opts.target, err, errbuf, sizeof(errbuf)); - pr_err("%s", errbuf); - goto out; - } - - /* Enable ignoring missing threads when -u/-p option is defined. */ - opts.ignore_missing_thread = opts.target.uid != UINT_MAX || opts.target.pid; + /* Enable ignoring missing threads when -p option is defined. */ + opts.ignore_missing_thread = opts.target.pid; evstr = bench__repeat_event_string(event_string, nr_events); if (!evstr) { @@ -261,7 +267,7 @@ int bench_evlist_open_close(int argc, const char **argv) goto out; } - err = bench_evlist_open_close__run(evstr); + err = bench_evlist_open_close__run(evstr, uid_str); free(evstr); out: diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index fdf133c9520f..7e29f04da744 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -57,7 +56,6 @@ static struct bench_futex_parameters params = { static const struct option options[] = { OPT_INTEGER( 'b', "buckets", ¶ms.nbuckets, "Specify amount of hash buckets"), - OPT_BOOLEAN( 'I', "immutable", ¶ms.buckets_immutable, "Make the hash buckets immutable"), OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), OPT_UINTEGER('r', "runtime", ¶ms.runtime, "Specify runtime (in seconds)"), OPT_UINTEGER('f', "futexes", ¶ms.nfutexes, "Specify amount of futexes per threads"), diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c index 5144a158512c..40640b674427 100644 --- a/tools/perf/bench/futex-lock-pi.c +++ b/tools/perf/bench/futex-lock-pi.c @@ -47,7 +47,6 @@ static struct bench_futex_parameters params = { static const struct option options[] = { OPT_INTEGER( 'b', "buckets", ¶ms.nbuckets, "Specify amount of hash buckets"), - OPT_BOOLEAN( 'I', "immutable", ¶ms.buckets_immutable, "Make the hash buckets immutable"), OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), OPT_UINTEGER('r', "runtime", ¶ms.runtime, "Specify runtime (in seconds)"), OPT_BOOLEAN( 'M', "multi", ¶ms.multi, "Use multiple futexes"), diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index a2f91ee1950b..0748b0fd689e 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -52,7 +52,6 @@ static struct bench_futex_parameters params = { static const struct option options[] = { OPT_INTEGER( 'b', "buckets", ¶ms.nbuckets, "Specify amount of hash buckets"), - OPT_BOOLEAN( 'I', "immutable", ¶ms.buckets_immutable, "Make the hash buckets immutable"), OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), OPT_UINTEGER('q', "nrequeue", ¶ms.nrequeue, "Specify amount of threads to requeue at once"), OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c index ee66482c29fd..6aede7c46b33 100644 --- a/tools/perf/bench/futex-wake-parallel.c +++ b/tools/perf/bench/futex-wake-parallel.c @@ -63,7 +63,6 @@ static struct bench_futex_parameters params = { static const struct option options[] = { OPT_INTEGER( 'b', "buckets", ¶ms.nbuckets, "Specify amount of hash buckets"), - OPT_BOOLEAN( 'I', "immutable", ¶ms.buckets_immutable, "Make the hash buckets immutable"), OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), OPT_UINTEGER('w', "nwakers", ¶ms.nwakes, "Specify amount of waking threads"), OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index 8d6107f7cd94..a31fc1563862 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -52,7 +52,6 @@ static struct bench_futex_parameters params = { static const struct option options[] = { OPT_INTEGER( 'b', "buckets", ¶ms.nbuckets, "Specify amount of hash buckets"), - OPT_BOOLEAN( 'I', "immutable", ¶ms.buckets_immutable, "Make the hash buckets immutable"), OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), OPT_UINTEGER('w', "nwakes", ¶ms.nwakes, "Specify amount of threads to wake at once"), OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), diff --git a/tools/perf/bench/futex.c b/tools/perf/bench/futex.c index 26382e4d8d4c..1481196a22f0 100644 --- a/tools/perf/bench/futex.c +++ b/tools/perf/bench/futex.c @@ -2,21 +2,24 @@ #include #include #include -#include #include #include "futex.h" +#ifndef PR_FUTEX_HASH +#define PR_FUTEX_HASH 78 +# define PR_FUTEX_HASH_SET_SLOTS 1 +# define PR_FUTEX_HASH_GET_SLOTS 2 +#endif // PR_FUTEX_HASH + void futex_set_nbuckets_param(struct bench_futex_parameters *params) { - unsigned long flags; int ret; if (params->nbuckets < 0) return; - flags = params->buckets_immutable ? FH_FLAG_IMMUTABLE : 0; - ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, params->nbuckets, flags); + ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, params->nbuckets, 0); if (ret) { printf("Requesting %d hash buckets failed: %d/%m\n", params->nbuckets, ret); @@ -40,18 +43,11 @@ void futex_print_nbuckets(struct bench_futex_parameters *params) printf("Requested: %d in usage: %d\n", params->nbuckets, ret); err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)"); } - if (params->nbuckets == 0) { + if (params->nbuckets == 0) ret = asprintf(&futex_hash_mode, "Futex hashing: global hash"); - } else { - ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_IMMUTABLE); - if (ret < 0) { - printf("Can't check if the hash is immutable: %m\n"); - err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)"); - } - ret = asprintf(&futex_hash_mode, "Futex hashing: %d hash buckets %s", - params->nbuckets, - ret == 1 ? "(immutable)" : ""); - } + else + ret = asprintf(&futex_hash_mode, "Futex hashing: %d hash buckets", + params->nbuckets); } else { if (ret <= 0) { ret = asprintf(&futex_hash_mode, "Futex hashing: global hash"); diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h index 9c9a73f9d865..dd295d27044a 100644 --- a/tools/perf/bench/futex.h +++ b/tools/perf/bench/futex.h @@ -26,7 +26,6 @@ struct bench_futex_parameters { unsigned int nwakes; unsigned int nrequeue; int nbuckets; - bool buckets_immutable; }; /** diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index f55c07e4be94..12387ea88b9a 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -80,12 +80,12 @@ static int add_dso(const char *fpath, const struct stat *sb __maybe_unused, int typeflag, struct FTW *ftwbuf __maybe_unused) { struct bench_dso *dso = &dsos[nr_dsos]; - struct build_id bid; + struct build_id bid = { .size = 0, }; if (typeflag == FTW_D || typeflag == FTW_SL) return 0; - if (filename__read_build_id(fpath, &bid) < 0) + if (filename__read_build_id(fpath, &bid, /*block=*/true) < 0) return 0; dso->name = realpath(fpath, NULL); diff --git a/tools/perf/bench/synthesize.c b/tools/perf/bench/synthesize.c index 9b333276cbdb..b3d493697675 100644 --- a/tools/perf/bench/synthesize.c +++ b/tools/perf/bench/synthesize.c @@ -114,12 +114,16 @@ static int run_single_threaded(void) .pid = "self", }; struct perf_thread_map *threads; + struct perf_env host_env; int err; perf_set_singlethreaded(); - session = perf_session__new(NULL, NULL); + perf_env__init(&host_env); + session = __perf_session__new(/*data=*/NULL, /*tool=*/NULL, + /*trace_event_repipe=*/false, &host_env); if (IS_ERR(session)) { pr_err("Session creation failed.\n"); + perf_env__exit(&host_env); return PTR_ERR(session); } threads = thread_map__new_by_pid(getpid()); @@ -144,6 +148,7 @@ static int run_single_threaded(void) perf_thread_map__put(threads); perf_session__delete(session); + perf_env__exit(&host_env); return err; } @@ -154,17 +159,21 @@ static int do_run_multi_threaded(struct target *target, u64 runtime_us; unsigned int i; double time_average, time_stddev, event_average, event_stddev; - int err; + int err = 0; struct stats time_stats, event_stats; struct perf_session *session; + struct perf_env host_env; + perf_env__init(&host_env); init_stats(&time_stats); init_stats(&event_stats); for (i = 0; i < multi_iterations; i++) { - session = perf_session__new(NULL, NULL); - if (IS_ERR(session)) - return PTR_ERR(session); - + session = __perf_session__new(/*data=*/NULL, /*tool=*/NULL, + /*trace_event_repipe=*/false, &host_env); + if (IS_ERR(session)) { + err = PTR_ERR(session); + goto err_out; + } atomic_set(&event_count, 0); gettimeofday(&start, NULL); err = __machine__synthesize_threads(&session->machines.host, @@ -175,7 +184,7 @@ static int do_run_multi_threaded(struct target *target, nr_threads_synthesize); if (err) { perf_session__delete(session); - return err; + goto err_out; } gettimeofday(&end, NULL); @@ -198,7 +207,9 @@ static int do_run_multi_threaded(struct target *target, printf(" Average time per event %.3f usec\n", time_average / event_average); - return 0; +err_out: + perf_env__exit(&host_env); + return err; } static int run_multi_threaded(void) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9833c2c82a2f..5d57d2913f3d 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -562,7 +562,7 @@ static int __cmd_annotate(struct perf_annotate *ann) } if (!annotate_opts.objdump_path) { - ret = perf_env__lookup_objdump(&session->header.env, + ret = perf_env__lookup_objdump(perf_session__env(session), &annotate_opts.objdump_path); if (ret) goto out; @@ -896,7 +896,7 @@ int cmd_annotate(int argc, const char **argv) symbol_conf.try_vmlinux_path = true; - ret = symbol__init(&annotate.session->header.env); + ret = symbol__init(perf_session__env(annotate.session)); if (ret < 0) goto out_delete; @@ -947,7 +947,7 @@ int cmd_annotate(int argc, const char **argv) annotate_opts.show_br_cntr = true; } - if (setup_sorting(NULL) < 0) + if (setup_sorting(/*evlist=*/NULL, perf_session__env(annotate.session)) < 0) usage_with_options(annotate_usage, options); ret = __cmd_annotate(&annotate); diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index b0511d16aeb6..2e0f2004696a 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -31,7 +31,7 @@ #include #include -static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) +static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid, size_t sbuildid_size) { char root_dir[PATH_MAX]; char *p; @@ -42,7 +42,7 @@ static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) if (!p) return -1; *p = '\0'; - return sysfs__sprintf_build_id(root_dir, sbuildid); + return sysfs__snprintf_build_id(root_dir, sbuildid, sbuildid_size); } static int build_id_cache__kcore_dir(char *dir, size_t sz) @@ -128,7 +128,7 @@ static int build_id_cache__add_kcore(const char *filename, bool force) return -1; *p = '\0'; - if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0) + if (build_id_cache__kcore_buildid(from_dir, sbuildid, sizeof(sbuildid)) < 0) return -1; scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s", @@ -175,19 +175,19 @@ static int build_id_cache__add_kcore(const char *filename, bool force) static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi) { char sbuild_id[SBUILD_ID_SIZE]; - struct build_id bid; + struct build_id bid = { .size = 0, }; int err; struct nscookie nsc; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid); + err = filename__read_build_id(filename, &bid, /*block=*/true); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); return -1; } - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); err = build_id_cache__add_s(sbuild_id, filename, nsi, false, false); pr_debug("Adding %s %s: %s\n", sbuild_id, filename, @@ -198,20 +198,20 @@ static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi) static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi) { char sbuild_id[SBUILD_ID_SIZE]; - struct build_id bid; + struct build_id bid = { .size = 0, }; struct nscookie nsc; int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid); + err = filename__read_build_id(filename, &bid, /*block=*/true); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); return -1; } - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); err = build_id_cache__remove_s(sbuild_id); pr_debug("Removing %s %s: %s\n", sbuild_id, filename, err ? "FAIL" : "Ok"); @@ -275,12 +275,12 @@ static int build_id_cache__purge_all(void) static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) { char filename[PATH_MAX]; - struct build_id bid; + struct build_id bid = { .size = 0, }; if (!dso__build_id_filename(dso, filename, sizeof(filename), false)) return true; - if (filename__read_build_id(filename, &bid) == -1) { + if (filename__read_build_id(filename, &bid, /*block=*/true) == -1) { if (errno == ENOENT) return false; @@ -303,13 +303,13 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi) { char sbuild_id[SBUILD_ID_SIZE]; - struct build_id bid; + struct build_id bid = { .size = 0, }; struct nscookie nsc; int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid); + err = filename__read_build_id(filename, &bid, /*block=*/true); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); @@ -317,7 +317,7 @@ static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi) } err = 0; - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); if (build_id_cache__cached(sbuild_id)) err = build_id_cache__remove_s(sbuild_id); @@ -453,7 +453,7 @@ int cmd_buildid_cache(int argc, const char **argv) return PTR_ERR(session); } - if (symbol__init(session ? &session->header.env : NULL) < 0) + if (symbol__init(session ? perf_session__env(session) : NULL) < 0) goto out; setup_pager(); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 52dfacaff8e3..a91bbb34ac94 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -31,7 +31,7 @@ static int buildid__map_cb(struct map *map, void *arg __maybe_unused) memset(bid_buf, 0, sizeof(bid_buf)); if (dso__has_build_id(dso)) - build_id__sprintf(dso__bid_const(dso), bid_buf); + build_id__snprintf(dso__bid(dso), bid_buf, sizeof(bid_buf)); printf("%s %16" PRIx64 " %16" PRIx64, bid_buf, map__start(map), map__end(map)); if (dso_long_name != NULL) printf(" %s", dso_long_name); @@ -45,11 +45,14 @@ static int buildid__map_cb(struct map *map, void *arg __maybe_unused) static void buildid__show_kernel_maps(void) { + struct perf_env host_env; struct machine *machine; - machine = machine__new_host(); + perf_env__init(&host_env); + machine = machine__new_host(&host_env); machine__for_each_kernel_map(machine, buildid__map_cb, NULL); machine__delete(machine); + perf_env__exit(&host_env); } static int sysfs__fprintf_build_id(FILE *fp) @@ -57,7 +60,7 @@ static int sysfs__fprintf_build_id(FILE *fp) char sbuild_id[SBUILD_ID_SIZE]; int ret; - ret = sysfs__sprintf_build_id("/", sbuild_id); + ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); if (ret != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; @@ -69,7 +72,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) char sbuild_id[SBUILD_ID_SIZE]; int ret; - ret = filename__sprintf_build_id(name, sbuild_id); + ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); if (ret != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e2e257bcc461..9e9ff471ddd1 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -195,12 +195,14 @@ static struct hist_entry_ops c2c_entry_ops = { static int c2c_hists__init(struct c2c_hists *hists, const char *sort, - int nr_header_lines); + int nr_header_lines, + struct perf_env *env); static struct c2c_hists* he__get_c2c_hists(struct hist_entry *he, const char *sort, - int nr_header_lines) + int nr_header_lines, + struct perf_env *env) { struct c2c_hist_entry *c2c_he; struct c2c_hists *hists; @@ -214,7 +216,7 @@ he__get_c2c_hists(struct hist_entry *he, if (!hists) return NULL; - ret = c2c_hists__init(hists, sort, nr_header_lines); + ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { free(hists); return NULL; @@ -350,7 +352,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, mi = mi_dup; - c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2); + c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2, machine->env); if (!c2c_hists) goto free_mi; @@ -1966,7 +1968,8 @@ static struct c2c_fmt *get_format(const char *name) return c2c_fmt; } -static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name) +static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name, + struct perf_env *env __maybe_unused) { struct c2c_fmt *c2c_fmt = get_format(name); int level = 0; @@ -1980,14 +1983,14 @@ static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name) return 0; } -static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name) +static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, struct perf_env *env) { struct c2c_fmt *c2c_fmt = get_format(name); struct c2c_dimension *dim; if (!c2c_fmt) { reset_dimensions(); - return sort_dimension__add(hpp_list, name, NULL, 0); + return sort_dimension__add(hpp_list, name, /*evlist=*/NULL, env, /*level=*/0); } dim = c2c_fmt->dim; @@ -2008,7 +2011,7 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name) \ for (tok = strtok_r((char *)_list, ", ", &tmp); \ tok; tok = strtok_r(NULL, ", ", &tmp)) { \ - ret = _fn(hpp_list, tok); \ + ret = _fn(hpp_list, tok, env); \ if (ret == -EINVAL) { \ pr_err("Invalid --fields key: `%s'", tok); \ break; \ @@ -2021,7 +2024,8 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name) static int hpp_list__parse(struct perf_hpp_list *hpp_list, const char *output_, - const char *sort_) + const char *sort_, + struct perf_env *env) { char *output = output_ ? strdup(output_) : NULL; char *sort = sort_ ? strdup(sort_) : NULL; @@ -2052,7 +2056,8 @@ static int hpp_list__parse(struct perf_hpp_list *hpp_list, static int c2c_hists__init(struct c2c_hists *hists, const char *sort, - int nr_header_lines) + int nr_header_lines, + struct perf_env *env) { __hists__init(&hists->hists, &hists->list); @@ -2066,15 +2071,16 @@ static int c2c_hists__init(struct c2c_hists *hists, /* Overload number of header lines.*/ hists->list.nr_header_lines = nr_header_lines; - return hpp_list__parse(&hists->list, NULL, sort); + return hpp_list__parse(&hists->list, /*output=*/NULL, sort, env); } static int c2c_hists__reinit(struct c2c_hists *c2c_hists, const char *output, - const char *sort) + const char *sort, + struct perf_env *env) { perf_hpp__reset_output_field(&c2c_hists->list); - return hpp_list__parse(&c2c_hists->list, output, sort); + return hpp_list__parse(&c2c_hists->list, output, sort, env); } #define DISPLAY_LINE_LIMIT 0.001 @@ -2207,8 +2213,9 @@ static int filter_cb(struct hist_entry *he, void *arg __maybe_unused) return 0; } -static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused) +static int resort_cl_cb(struct hist_entry *he, void *arg) { + struct perf_env *env = arg; struct c2c_hist_entry *c2c_he; struct c2c_hists *c2c_hists; bool display = he__display(he, &c2c.shared_clines_stats); @@ -2222,7 +2229,7 @@ static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused) c2c_he->cacheline_idx = idx++; calc_width(c2c_he); - c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort); + c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort, env); hists__collapse_resort(&c2c_hists->hists, NULL); hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb); @@ -2267,14 +2274,15 @@ static int setup_nodes(struct perf_session *session) int node, idx; struct perf_cpu cpu; int *cpu2node; + struct perf_env *env = perf_session__env(session); if (c2c.node_info > 2) c2c.node_info = 2; - c2c.nodes_cnt = session->header.env.nr_numa_nodes; - c2c.cpus_cnt = session->header.env.nr_cpus_avail; + c2c.nodes_cnt = env->nr_numa_nodes; + c2c.cpus_cnt = env->nr_cpus_avail; - n = session->header.env.numa_nodes; + n = env->numa_nodes; if (!n) return -EINVAL; @@ -2333,7 +2341,7 @@ static int resort_shared_cl_cb(struct hist_entry *he, void *arg __maybe_unused) return 0; } -static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb) +static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb, void *arg) { struct rb_node *next = rb_first_cached(&hists->entries); int ret = 0; @@ -2342,7 +2350,7 @@ static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb) struct hist_entry *he; he = rb_entry(next, struct hist_entry, rb_node); - ret = cb(he, NULL); + ret = cb(he, arg); if (ret) break; next = rb_next(&he->rb_node); @@ -2448,7 +2456,7 @@ static void print_cacheline(struct c2c_hists *c2c_hists, hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, false); } -static void print_pareto(FILE *out) +static void print_pareto(FILE *out, struct perf_env *env) { struct perf_hpp_list hpp_list; struct rb_node *nd; @@ -2473,7 +2481,7 @@ static void print_pareto(FILE *out) "dcacheline"; perf_hpp_list__init(&hpp_list); - ret = hpp_list__parse(&hpp_list, cl_output, NULL); + ret = hpp_list__parse(&hpp_list, cl_output, /*evlist=*/NULL, env); if (WARN_ONCE(ret, "failed to setup sort entries\n")) return; @@ -2538,7 +2546,7 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) fprintf(out, "=================================================\n"); fprintf(out, "#\n"); - print_pareto(out); + print_pareto(out, perf_session__env(session)); } #ifdef HAVE_SLANG_SUPPORT @@ -3030,6 +3038,7 @@ static int perf_c2c__report(int argc, const char **argv) }; int err = 0; const char *output_str, *sort_str = NULL; + struct perf_env *env; argc = parse_options(argc, argv, options, report_c2c_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -3072,14 +3081,14 @@ static int perf_c2c__report(int argc, const char **argv) pr_debug("Error creating perf session\n"); goto out; } - + env = perf_session__env(session); /* * Use the 'tot' as default display type if user doesn't specify it; * since Arm64 platform doesn't support HITMs flag, use 'peer' as the * default display type. */ if (!display) { - if (!strcmp(perf_env__arch(&session->header.env), "arm64")) + if (!strcmp(perf_env__arch(env), "arm64")) display = "peer"; else display = "tot"; @@ -3095,7 +3104,7 @@ static int perf_c2c__report(int argc, const char **argv) goto out_session; } - err = c2c_hists__init(&c2c.hists, "dcacheline", 2); + err = c2c_hists__init(&c2c.hists, "dcacheline", 2, perf_session__env(session)); if (err) { pr_debug("Failed to initialize hists\n"); goto out_session; @@ -3109,7 +3118,7 @@ static int perf_c2c__report(int argc, const char **argv) goto out_session; } - err = mem2node__init(&c2c.mem2node, &session->header.env); + err = mem2node__init(&c2c.mem2node, env); if (err) goto out_session; @@ -3117,7 +3126,7 @@ static int perf_c2c__report(int argc, const char **argv) if (err) goto out_mem2node; - if (symbol__init(&session->header.env) < 0) + if (symbol__init(env) < 0) goto out_mem2node; /* No pipe support at the moment. */ @@ -3179,13 +3188,13 @@ static int perf_c2c__report(int argc, const char **argv) else if (c2c.display == DISPLAY_SNP_PEER) sort_str = "tot_peer"; - c2c_hists__reinit(&c2c.hists, output_str, sort_str); + c2c_hists__reinit(&c2c.hists, output_str, sort_str, perf_session__env(session)); ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting..."); hists__collapse_resort(&c2c.hists.hists, NULL); hists__output_resort_cb(&c2c.hists.hists, &prog, resort_shared_cl_cb); - hists__iterate_cb(&c2c.hists.hists, resort_cl_cb); + hists__iterate_cb(&c2c.hists.hists, resort_cl_cb, perf_session__env(session)); ui_progress__finish(); diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 9a509cb3bb9a..b1e205871ab1 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -43,8 +43,8 @@ struct feature_status supported_features[] = { FEATURE_STATUS("dwarf-unwind", HAVE_DWARF_UNWIND_SUPPORT), FEATURE_STATUS("auxtrace", HAVE_AUXTRACE_SUPPORT), FEATURE_STATUS_TIP("libbfd", HAVE_LIBBFD_SUPPORT, "Deprecated, license incompatibility, use BUILD_NONDISTRO=1 and install binutils-dev[el]"), + FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT), FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT), - FEATURE_STATUS("libcrypto", HAVE_LIBCRYPTO_SUPPORT), FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT), FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT), FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT), diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index ae490d58af92..53d5ea4a6a4f 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -2003,7 +2003,7 @@ int cmd_diff(int argc, const char **argv) sort__mode = SORT_MODE__DIFF; } - if (setup_sorting(NULL) < 0) + if (setup_sorting(/*evlist=*/NULL, perf_session__env(data__files[0].session)) < 0) usage_with_options(diff_usage, options); setup_pager(); diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index bba36ebc2aa7..6b6eec65f93f 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -301,6 +301,10 @@ static void reset_tracing_options(struct perf_ftrace *ftrace __maybe_unused) write_tracing_option_file("funcgraph-proc", "0"); write_tracing_option_file("funcgraph-abstime", "0"); write_tracing_option_file("funcgraph-tail", "0"); + write_tracing_option_file("funcgraph-args", "0"); + write_tracing_option_file("funcgraph-retval", "0"); + write_tracing_option_file("funcgraph-retval-hex", "0"); + write_tracing_option_file("funcgraph-retaddr", "0"); write_tracing_option_file("latency-format", "0"); write_tracing_option_file("irq-info", "0"); } @@ -542,6 +546,41 @@ static int set_tracing_sleep_time(struct perf_ftrace *ftrace) return 0; } +static int set_tracing_funcgraph_args(struct perf_ftrace *ftrace) +{ + if (ftrace->graph_args) { + if (write_tracing_option_file("funcgraph-args", "1") < 0) + return -1; + } + + return 0; +} + +static int set_tracing_funcgraph_retval(struct perf_ftrace *ftrace) +{ + if (ftrace->graph_retval || ftrace->graph_retval_hex) { + if (write_tracing_option_file("funcgraph-retval", "1") < 0) + return -1; + } + + if (ftrace->graph_retval_hex) { + if (write_tracing_option_file("funcgraph-retval-hex", "1") < 0) + return -1; + } + + return 0; +} + +static int set_tracing_funcgraph_retaddr(struct perf_ftrace *ftrace) +{ + if (ftrace->graph_retaddr) { + if (write_tracing_option_file("funcgraph-retaddr", "1") < 0) + return -1; + } + + return 0; +} + static int set_tracing_funcgraph_irqs(struct perf_ftrace *ftrace) { if (!ftrace->graph_noirqs) @@ -642,6 +681,21 @@ static int set_tracing_options(struct perf_ftrace *ftrace) return -1; } + if (set_tracing_funcgraph_args(ftrace) < 0) { + pr_err("failed to set tracing option funcgraph-args\n"); + return -1; + } + + if (set_tracing_funcgraph_retval(ftrace) < 0) { + pr_err("failed to set tracing option funcgraph-retval\n"); + return -1; + } + + if (set_tracing_funcgraph_retaddr(ftrace) < 0) { + pr_err("failed to set tracing option funcgraph-retaddr\n"); + return -1; + } + if (set_tracing_funcgraph_irqs(ftrace) < 0) { pr_err("failed to set tracing option funcgraph-irqs\n"); return -1; @@ -1549,6 +1603,33 @@ static void delete_filter_func(struct list_head *head) } } +static int parse_filter_event(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + struct list_head *head = opt->value; + struct filter_entry *entry; + char *s, *p; + int ret = -ENOMEM; + + s = strdup(str); + if (s == NULL) + return -ENOMEM; + + while ((p = strsep(&s, ",")) != NULL) { + entry = malloc(sizeof(*entry) + strlen(p) + 1); + if (entry == NULL) + goto out; + + strcpy(entry->name, p); + list_add_tail(&entry->list, head); + } + ret = 0; + +out: + free(s); + return ret; +} + static int parse_buffer_size(const struct option *opt, const char *str, int unset) { @@ -1607,6 +1688,10 @@ static int parse_graph_tracer_opts(const struct option *opt, int ret; struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value; struct sublevel_option graph_tracer_opts[] = { + { .name = "args", .value_ptr = &ftrace->graph_args }, + { .name = "retval", .value_ptr = &ftrace->graph_retval }, + { .name = "retval-hex", .value_ptr = &ftrace->graph_retval_hex }, + { .name = "retaddr", .value_ptr = &ftrace->graph_retaddr }, { .name = "nosleep-time", .value_ptr = &ftrace->graph_nosleep_time }, { .name = "noirqs", .value_ptr = &ftrace->graph_noirqs }, { .name = "verbose", .value_ptr = &ftrace->graph_verbose }, @@ -1663,7 +1748,6 @@ int cmd_ftrace(int argc, const char **argv) int (*cmd_func)(struct perf_ftrace *) = NULL; struct perf_ftrace ftrace = { .tracer = DEFAULT_TRACER, - .target = { .uid = UINT_MAX, }, }; const struct option common_options[] = { OPT_STRING('p', "pid", &ftrace.target.pid, "pid", @@ -1699,7 +1783,7 @@ int cmd_ftrace(int argc, const char **argv) OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func", "Set nograph filter on given functions", parse_filter_func), OPT_CALLBACK(0, "graph-opts", &ftrace, "options", - "Graph tracer options, available options: nosleep-time,noirqs,verbose,thresh=,depth=", + "Graph tracer options, available options: args,retval,retval-hex,retaddr,nosleep-time,noirqs,verbose,thresh=,depth=", parse_graph_tracer_opts), OPT_CALLBACK('m', "buffer-size", &ftrace.percpu_buffer_size, "size", "Size of per cpu buffer, needs to use a B, K, M or G suffix.", parse_buffer_size), @@ -1712,6 +1796,8 @@ int cmd_ftrace(int argc, const char **argv) const struct option latency_options[] = { OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", "Show latency of given function", parse_filter_func), + OPT_CALLBACK('e', "events", &ftrace.event_pair, "event1,event2", + "Show latency between the two events", parse_filter_event), #ifdef HAVE_BPF_SKEL OPT_BOOLEAN('b', "use-bpf", &ftrace.target.use_bpf, "Use BPF to measure function latency"), @@ -1764,6 +1850,7 @@ int cmd_ftrace(int argc, const char **argv) INIT_LIST_HEAD(&ftrace.notrace); INIT_LIST_HEAD(&ftrace.graph_funcs); INIT_LIST_HEAD(&ftrace.nograph_funcs); + INIT_LIST_HEAD(&ftrace.event_pair); signal(SIGINT, sig_handler); signal(SIGUSR1, sig_handler); @@ -1818,9 +1905,24 @@ int cmd_ftrace(int argc, const char **argv) cmd_func = __cmd_ftrace; break; case PERF_FTRACE_LATENCY: - if (list_empty(&ftrace.filters)) { - pr_err("Should provide a function to measure\n"); + if (list_empty(&ftrace.filters) && list_empty(&ftrace.event_pair)) { + pr_err("Should provide a function or events to measure\n"); parse_options_usage(ftrace_usage, options, "T", 1); + parse_options_usage(NULL, options, "e", 1); + ret = -EINVAL; + goto out_delete_filters; + } + if (!list_empty(&ftrace.filters) && !list_empty(&ftrace.event_pair)) { + pr_err("Please specify either of function or events\n"); + parse_options_usage(ftrace_usage, options, "T", 1); + parse_options_usage(NULL, options, "e", 1); + ret = -EINVAL; + goto out_delete_filters; + } + if (!list_empty(&ftrace.event_pair) && !ftrace.target.use_bpf) { + pr_err("Event processing needs BPF\n"); + parse_options_usage(ftrace_usage, options, "b", 1); + parse_options_usage(NULL, options, "e", 1); ret = -EINVAL; goto out_delete_filters; } @@ -1911,6 +2013,7 @@ int cmd_ftrace(int argc, const char **argv) delete_filter_func(&ftrace.notrace); delete_filter_func(&ftrace.graph_funcs); delete_filter_func(&ftrace.nograph_funcs); + delete_filter_func(&ftrace.event_pair); return ret; } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 11e49cafa3af..a114b3fa1bea 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -587,15 +587,17 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct dso_id id; - struct dso_id *dso_id = NULL; + struct dso_id id = dso_id_empty; - if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { + if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { + build_id__init(&id.build_id, event->mmap2.build_id, event->mmap2.build_id_size); + } else { id.maj = event->mmap2.maj; id.min = event->mmap2.min; id.ino = event->mmap2.ino; id.ino_generation = event->mmap2.ino_generation; - dso_id = &id; + id.mmap2_valid = true; + id.mmap2_ino_generation_valid = true; } return perf_event__repipe_common_mmap( @@ -603,7 +605,7 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, event->mmap2.pid, event->mmap2.tid, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, event->mmap2.flags, event->mmap2.prot, - event->mmap2.filename, dso_id, + event->mmap2.filename, &id, perf_event__process_mmap2); } @@ -671,19 +673,20 @@ static int perf_event__repipe_tracing_data(struct perf_session *session, static int dso__read_build_id(struct dso *dso) { struct nscookie nsc; + struct build_id bid = { .size = 0, }; if (dso__has_build_id(dso)) return 0; mutex_lock(dso__lock(dso)); nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), dso__bid(dso)) > 0) - dso__set_has_build_id(dso); + if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) + dso__set_build_id(dso, &bid); else if (dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, dso__bid(dso)) > 0) - dso__set_has_build_id(dso); + if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) + dso__set_build_id(dso, &bid); free(new_name); } nsinfo__mountns_exit(&nsc); @@ -732,23 +735,26 @@ static bool perf_inject__lookup_known_build_id(struct perf_inject *inject, struct dso *dso) { struct str_node *pos; - int bid_len; strlist__for_each_entry(pos, inject->known_build_ids) { + struct build_id bid; const char *build_id, *dso_name; + size_t bid_len; build_id = skip_spaces(pos->s); dso_name = strchr(build_id, ' '); bid_len = dso_name - pos->s; + if (bid_len > sizeof(bid.data)) + bid_len = sizeof(bid.data); dso_name = skip_spaces(dso_name); if (strcmp(dso__long_name(dso), dso_name)) continue; - for (int ix = 0; 2 * ix + 1 < bid_len; ++ix) { - dso__bid(dso)->data[ix] = (hex(build_id[2 * ix]) << 4 | - hex(build_id[2 * ix + 1])); + for (size_t ix = 0; 2 * ix + 1 < bid_len; ++ix) { + bid.data[ix] = (hex(build_id[2 * ix]) << 4 | + hex(build_id[2 * ix + 1])); } - dso__bid(dso)->size = bid_len / 2; - dso__set_has_build_id(dso); + bid.size = bid_len / 2; + dso__set_build_id(dso, &bid); return true; } return false; @@ -2530,9 +2536,11 @@ int cmd_inject(int argc, const char **argv) inject.tool.finished_init = perf_event__repipe_op2_synth; inject.tool.compressed = perf_event__repipe_op4_synth; inject.tool.auxtrace = perf_event__repipe_auxtrace; + inject.tool.bpf_metadata = perf_event__repipe_op2_synth; inject.tool.dont_split_sample_group = true; inject.session = __perf_session__new(&data, &inject.tool, - /*trace_event_repipe=*/inject.output.is_pipe); + /*trace_event_repipe=*/inject.output.is_pipe, + /*host_env=*/NULL); if (IS_ERR(inject.session)) { ret = PTR_ERR(inject.session); @@ -2601,7 +2609,7 @@ int cmd_inject(int argc, const char **argv) inject.tool.finished_round = perf_event__drop_oe; } #endif - ret = symbol__init(&inject.session->header.env); + ret = symbol__init(perf_session__env(inject.session)); if (ret < 0) goto out_delete; diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c index a3c2ffdc1af8..3c4339982b16 100644 --- a/tools/perf/builtin-kallsyms.c +++ b/tools/perf/builtin-kallsyms.c @@ -12,18 +12,28 @@ #include #include "debug.h" #include "dso.h" +#include "env.h" #include "machine.h" #include "map.h" #include "symbol.h" static int __cmd_kallsyms(int argc, const char **argv) { - int i; - struct machine *machine = machine__new_kallsyms(); + int i, err; + struct perf_env host_env; + struct machine *machine = NULL; + + perf_env__init(&host_env); + err = perf_env__set_cmdline(&host_env, argc, argv); + if (err) + goto out; + + machine = machine__new_kallsyms(&host_env); if (machine == NULL) { pr_err("Couldn't read /proc/kallsyms\n"); - return -1; + err = -1; + goto out; } for (i = 0; i < argc; ++i) { @@ -42,9 +52,10 @@ static int __cmd_kallsyms(int argc, const char **argv) map__unmap_ip(map, symbol->start), map__unmap_ip(map, symbol->end), symbol->start, symbol->end); } - +out: machine__delete(machine); - return 0; + perf_env__exit(&host_env); + return err; } int cmd_kallsyms(int argc, const char **argv) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 67fb1946ef13..7929a5fa5f46 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -2024,7 +2024,7 @@ int cmd_kmem(int argc, const char **argv) symbol_conf.use_callchain = true; } - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); if (perf_time__parse_str(&ptime, time_str) != 0) { pr_err("Invalid time string\n"); diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 67fd2b006b0b..7b15b4a705e4 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1175,7 +1175,7 @@ static int cpu_isa_config(struct perf_kvm_stat *kvm) } cpuid = buf; } else - cpuid = kvm->session->header.env.cpuid; + cpuid = perf_session__env(kvm->session)->cpuid; if (!cpuid) { pr_err("Failed to look up CPU type\n"); @@ -1561,7 +1561,7 @@ static int read_events(struct perf_kvm_stat *kvm) return PTR_ERR(kvm->session); } - symbol__init(&kvm->session->header.env); + symbol__init(perf_session__env(kvm->session)); if (!perf_session__has_traces(kvm->session, "kvm record")) { ret = -EINVAL; @@ -1871,8 +1871,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->opts.user_interval = 1; kvm->opts.mmap_pages = 512; kvm->opts.target.uses_mmap = false; - kvm->opts.target.uid_str = NULL; - kvm->opts.target.uid = UINT_MAX; symbol__init(NULL); disable_buildid_cache(); diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index c41a68d073de..d2e08de5976d 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -1804,7 +1804,7 @@ static int perf_kwork__read_events(struct perf_kwork *kwork) return PTR_ERR(session); } - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); if (perf_kwork__check_config(kwork, session) != 0) goto out_delete; diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index e9b595d75df2..caf42276bd0f 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -58,6 +58,8 @@ struct print_state { bool metrics; /** @metricgroups: Controls printing of metric and metric groups. */ bool metricgroups; + /** @exclude_abi: Exclude PMUs with types less than PERF_TYPE_MAX except PERF_TYPE_RAW. */ + bool exclude_abi; /** @last_topic: The last printed event topic. */ char *last_topic; /** @last_metricgroups: The last printed metric group. */ @@ -113,7 +115,8 @@ static void wordwrap(FILE *fp, const char *s, int start, int max, int corr) } } -static void default_print_event(void *ps, const char *topic, const char *pmu_name, +static void default_print_event(void *ps, const char *topic, + const char *pmu_name, u32 pmu_type, const char *event_name, const char *event_alias, const char *scale_unit __maybe_unused, bool deprecated, const char *event_type_desc, @@ -130,6 +133,9 @@ static void default_print_event(void *ps, const char *topic, const char *pmu_nam if (print_state->pmu_glob && pmu_name && !strglobmatch(pmu_name, print_state->pmu_glob)) return; + if (print_state->exclude_abi && pmu_type < PERF_TYPE_MAX && pmu_type != PERF_TYPE_RAW) + return; + if (print_state->event_glob && (!event_name || !strglobmatch(event_name, print_state->event_glob)) && (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) && @@ -354,7 +360,8 @@ static void fix_escape_fprintf(FILE *fp, struct strbuf *buf, const char *fmt, .. fputs(buf->buf, fp); } -static void json_print_event(void *ps, const char *topic, const char *pmu_name, +static void json_print_event(void *ps, const char *topic, + const char *pmu_name, u32 pmu_type __maybe_unused, const char *event_name, const char *event_alias, const char *scale_unit, bool deprecated, const char *event_type_desc, @@ -614,19 +621,10 @@ int cmd_list(int argc, const char **argv) for (i = 0; i < argc; ++i) { char *sep, *s; - if (strcmp(argv[i], "tracepoint") == 0) - print_tracepoint_events(&print_cb, ps); - else if (strcmp(argv[i], "hw") == 0 || - strcmp(argv[i], "hardware") == 0) - print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE, - event_symbols_hw, PERF_COUNT_HW_MAX); - else if (strcmp(argv[i], "sw") == 0 || - strcmp(argv[i], "software") == 0) { + if (strcmp(argv[i], "tracepoint") == 0) { char *old_pmu_glob = default_ps.pmu_glob; - print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, - event_symbols_sw, PERF_COUNT_SW_MAX); - default_ps.pmu_glob = strdup("tool"); + default_ps.pmu_glob = strdup("tracepoint"); if (!default_ps.pmu_glob) { ret = -1; goto out; @@ -634,12 +632,33 @@ int cmd_list(int argc, const char **argv) perf_pmus__print_pmu_events(&print_cb, ps); zfree(&default_ps.pmu_glob); default_ps.pmu_glob = old_pmu_glob; + } else if (strcmp(argv[i], "hw") == 0 || + strcmp(argv[i], "hardware") == 0) + print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE, + event_symbols_hw, PERF_COUNT_HW_MAX); + else if (strcmp(argv[i], "sw") == 0 || + strcmp(argv[i], "software") == 0) { + char *old_pmu_glob = default_ps.pmu_glob; + static const char * const sw_globs[] = { "software", "tool" }; + + for (size_t j = 0; j < ARRAY_SIZE(sw_globs); j++) { + default_ps.pmu_glob = strdup(sw_globs[j]); + if (!default_ps.pmu_glob) { + ret = -1; + goto out; + } + perf_pmus__print_pmu_events(&print_cb, ps); + zfree(&default_ps.pmu_glob); + } + default_ps.pmu_glob = old_pmu_glob; } else if (strcmp(argv[i], "cache") == 0 || strcmp(argv[i], "hwcache") == 0) print_hwcache_events(&print_cb, ps); - else if (strcmp(argv[i], "pmu") == 0) + else if (strcmp(argv[i], "pmu") == 0) { + default_ps.exclude_abi = true; perf_pmus__print_pmu_events(&print_cb, ps); - else if (strcmp(argv[i], "sdt") == 0) + default_ps.exclude_abi = false; + } else if (strcmp(argv[i], "sdt") == 0) print_sdt_events(&print_cb, ps); else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) { default_ps.metricgroups = false; @@ -657,6 +676,7 @@ int cmd_list(int argc, const char **argv) #endif else if ((sep = strchr(argv[i], ':')) != NULL) { char *old_pmu_glob = default_ps.pmu_glob; + char *old_event_glob = default_ps.event_glob; default_ps.event_glob = strdup(argv[i]); if (!default_ps.event_glob) { @@ -664,13 +684,21 @@ int cmd_list(int argc, const char **argv) goto out; } - print_tracepoint_events(&print_cb, ps); + default_ps.pmu_glob = strdup("tracepoint"); + if (!default_ps.pmu_glob) { + zfree(&default_ps.event_glob); + ret = -1; + goto out; + } + perf_pmus__print_pmu_events(&print_cb, ps); + zfree(&default_ps.pmu_glob); + default_ps.pmu_glob = old_pmu_glob; print_sdt_events(&print_cb, ps); default_ps.metrics = true; default_ps.metricgroups = true; metricgroup__print(&print_cb, ps); zfree(&default_ps.event_glob); - default_ps.pmu_glob = old_pmu_glob; + default_ps.event_glob = old_event_glob; } else { if (asprintf(&s, "*%s*", argv[i]) < 0) { printf("Critical: Not enough memory! Trying to continue...\n"); @@ -679,11 +707,8 @@ int cmd_list(int argc, const char **argv) default_ps.event_glob = s; print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX); - print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, - event_symbols_sw, PERF_COUNT_SW_MAX); print_hwcache_events(&print_cb, ps); perf_pmus__print_pmu_events(&print_cb, ps); - print_tracepoint_events(&print_cb, ps); print_sdt_events(&print_cb, ps); default_ps.metrics = true; default_ps.metricgroups = true; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 3b3ade7a39ca..fd49703021fd 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -1876,7 +1876,7 @@ static int __cmd_report(bool display_info) } symbol_conf.allow_aliases = true; - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); if (!data.is_pipe) { if (!perf_session__has_traces(session, "lock record")) @@ -2042,7 +2042,7 @@ static int __cmd_contention(int argc, const char **argv) con.save_callstack = true; symbol_conf.allow_aliases = true; - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); if (use_bpf) { err = target__validate(&target); diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 5ec83cd85650..c6496adff3fe 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -304,7 +304,7 @@ static int report_raw_events(struct perf_mem *mem) goto out_delete; } - ret = symbol__init(&session->header.env); + ret = symbol__init(perf_session__env(session)); if (ret < 0) goto out_delete; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8059bce85a51..7ea3a11aca70 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -171,10 +171,12 @@ struct record { bool no_buildid_cache_set; bool buildid_all; bool buildid_mmap; + bool buildid_mmap_set; bool timestamp_filename; bool timestamp_boundary; bool off_cpu; const char *filter_action; + const char *uid_str; struct switch_output switch_output; unsigned long long samples; unsigned long output_max_size; /* = 0: unlimited */ @@ -773,7 +775,9 @@ static int record__auxtrace_mmap_read(struct record *rec, { int ret; - ret = auxtrace_mmap__read(map, rec->itr, &rec->tool, + ret = auxtrace_mmap__read(map, rec->itr, + perf_session__env(rec->session), + &rec->tool, record__process_auxtrace); if (ret < 0) return ret; @@ -789,7 +793,9 @@ static int record__auxtrace_mmap_read_snapshot(struct record *rec, { int ret; - ret = auxtrace_mmap__read_snapshot(map, rec->itr, &rec->tool, + ret = auxtrace_mmap__read_snapshot(map, rec->itr, + perf_session__env(rec->session), + &rec->tool, record__process_auxtrace, rec->opts.auxtrace_snapshot_size); if (ret < 0) @@ -1810,6 +1816,7 @@ record__finish_output(struct record *rec) data->dir.files[i].size = lseek(data->dir.files[i].fd, 0, SEEK_CUR); } + /* Buildid scanning disabled or build ID in kernel and synthesized map events. */ if (!rec->no_buildid) { process_buildids(rec); @@ -2161,6 +2168,14 @@ static int record__synthesize(struct record *rec, bool tail) return err; } +static void record__synthesize_final_bpf_metadata(struct record *rec __maybe_unused) +{ +#ifdef HAVE_LIBBPF_SUPPORT + perf_event__synthesize_final_bpf_metadata(rec->session, + process_synthesized_event); +#endif +} + static int record__process_signal_event(union perf_event *event __maybe_unused, void *data) { struct record *rec = data; @@ -2192,7 +2207,7 @@ static int record__setup_sb_evlist(struct record *rec) } } - if (evlist__add_bpf_sb_event(rec->sb_evlist, &rec->session->header.env)) { + if (evlist__add_bpf_sb_event(rec->sb_evlist, perf_session__env(rec->session))) { pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n."); return -1; } @@ -2211,15 +2226,16 @@ static int record__init_clock(struct record *rec) struct perf_session *session = rec->session; struct timespec ref_clockid; struct timeval ref_tod; + struct perf_env *env = perf_session__env(session); u64 ref; if (!rec->opts.use_clockid) return 0; if (rec->opts.use_clockid && rec->opts.clockid_res_ns) - session->header.env.clock.clockid_res_ns = rec->opts.clockid_res_ns; + env->clock.clockid_res_ns = rec->opts.clockid_res_ns; - session->header.env.clock.clockid = rec->opts.clockid; + env->clock.clockid = rec->opts.clockid; if (gettimeofday(&ref_tod, NULL) != 0) { pr_err("gettimeofday failed, cannot set reference time.\n"); @@ -2234,12 +2250,12 @@ static int record__init_clock(struct record *rec) ref = (u64) ref_tod.tv_sec * NSEC_PER_SEC + (u64) ref_tod.tv_usec * NSEC_PER_USEC; - session->header.env.clock.tod_ns = ref; + env->clock.tod_ns = ref; ref = (u64) ref_clockid.tv_sec * NSEC_PER_SEC + (u64) ref_clockid.tv_nsec; - session->header.env.clock.clockid_ns = ref; + env->clock.clockid_ns = ref; return 0; } @@ -2385,6 +2401,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) int fd; float ratio = 0; enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED; + struct perf_env *env; atexit(record__sig_exit); signal(SIGCHLD, sig_handler); @@ -2426,7 +2443,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) pr_err("Perf session creation failed.\n"); return PTR_ERR(session); } - + env = perf_session__env(session); if (record__threads_enabled(rec)) { if (perf_data__is_pipe(&rec->data)) { pr_err("Parallel trace streaming is not available in pipe mode.\n"); @@ -2460,8 +2477,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } #endif // HAVE_EVENTFD_SUPPORT - session->header.env.comp_type = PERF_COMP_ZSTD; - session->header.env.comp_level = rec->opts.comp_level; + env->comp_type = PERF_COMP_ZSTD; + env->comp_level = rec->opts.comp_level; if (rec->opts.kcore && !record__kcore_readable(&session->machines.host)) { @@ -2514,7 +2531,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } /* Debug message used by test scripts */ pr_debug3("perf record done opening and mmapping events\n"); - session->header.env.comp_mmap_len = session->evlist->core.mmap_len; + env->comp_mmap_len = session->evlist->core.mmap_len; if (rec->opts.kcore) { err = record__kcore_copy(&session->machines.host, data); @@ -2806,6 +2823,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) trigger_off(&auxtrace_snapshot_trigger); trigger_off(&switch_output_trigger); + record__synthesize_final_bpf_metadata(rec); + if (opts->auxtrace_snapshot_on_exit) record__auxtrace_snapshot_exit(rec); @@ -2842,7 +2861,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) if (rec->session->bytes_transferred && rec->session->bytes_compressed) { ratio = (float)rec->session->bytes_transferred/(float)rec->session->bytes_compressed; - session->header.env.comp_ratio = ratio + 0.5; + env->comp_ratio = ratio + 0.5; } if (forks) { @@ -2994,6 +3013,8 @@ static int perf_record_config(const char *var, const char *value, void *cb) rec->no_buildid = true; else if (!strcmp(value, "mmap")) rec->buildid_mmap = true; + else if (!strcmp(value, "no-mmap")) + rec->buildid_mmap = false; else return -1; return 0; @@ -3400,6 +3421,7 @@ static struct record record = { .synth = PERF_SYNTH_ALL, .off_cpu_thresh_ns = OFFCPU_THRESH, }, + .buildid_mmap = true, }; const char record_callchain_help[] = CALLCHAIN_RECORD_HELP @@ -3513,8 +3535,7 @@ static struct option __record_options[] = { "or ranges of time to enable events e.g. '-D 10-20,30-40'", record__parse_event_enable_time), OPT_BOOLEAN(0, "kcore", &record.opts.kcore, "copy /proc/kcore"), - OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", - "user to profile"), + OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, "branch any", "sample any taken branches", @@ -3567,8 +3588,8 @@ static struct option __record_options[] = { "file", "vmlinux pathname"), OPT_BOOLEAN(0, "buildid-all", &record.buildid_all, "Record build-id of all DSOs regardless of hits"), - OPT_BOOLEAN(0, "buildid-mmap", &record.buildid_mmap, - "Record build-id in map events"), + OPT_BOOLEAN_SET(0, "buildid-mmap", &record.buildid_mmap, &record.buildid_mmap_set, + "Record build-id in mmap events and skip build-id processing."), OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename, "append timestamp to output filename"), OPT_BOOLEAN(0, "timestamp-boundary", &record.timestamp_boundary, @@ -4098,19 +4119,24 @@ int cmd_record(int argc, const char **argv) record.opts.record_switch_events = true; } + if (!rec->buildid_mmap) { + pr_debug("Disabling build id in synthesized mmap2 events.\n"); + symbol_conf.no_buildid_mmap2 = true; + } else if (rec->buildid_mmap_set) { + /* + * Explicitly passing --buildid-mmap disables buildid processing + * and cache generation. + */ + rec->no_buildid = true; + } + if (rec->buildid_mmap && !perf_can_record_build_id()) { + pr_warning("Missing support for build id in kernel mmap events.\n" + "Disable this warning with --no-buildid-mmap\n"); + rec->buildid_mmap = false; + } if (rec->buildid_mmap) { - if (!perf_can_record_build_id()) { - pr_err("Failed: no support to record build id in mmap events, update your kernel.\n"); - err = -EINVAL; - goto out_opts; - } - pr_debug("Enabling build id in mmap2 events.\n"); - /* Enable mmap build id synthesizing. */ - symbol_conf.buildid_mmap2 = true; /* Enable perf_event_attr::build_id bit. */ rec->opts.build_id = true; - /* Disable build id cache. */ - rec->no_buildid = true; } if (rec->opts.record_cgroup && !perf_can_record_cgroup()) { @@ -4256,19 +4282,24 @@ int cmd_record(int argc, const char **argv) ui__warning("%s\n", errbuf); } - err = target__parse_uid(&rec->opts.target); - if (err) { - int saved_errno = errno; + if (rec->uid_str) { + uid_t uid = parse_uid(rec->uid_str); - target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); - ui__error("%s", errbuf); + if (uid == UINT_MAX) { + ui__error("Invalid User: %s", rec->uid_str); + err = -EINVAL; + goto out; + } + err = parse_uid_filter(rec->evlist, uid); + if (err) + goto out; - err = -saved_errno; - goto out; + /* User ID filtering implies system wide. */ + rec->opts.target.system_wide = true; } - /* Enable ignoring missing threads when -u/-p option is defined. */ - rec->opts.ignore_missing_thread = rec->opts.target.uid != UINT_MAX || rec->opts.target.pid; + /* Enable ignoring missing threads when -p option is defined. */ + rec->opts.ignore_missing_thread = rec->opts.target.pid; evlist__warn_user_requested_cpus(rec->evlist, rec->opts.target.cpu_list); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e662e1c3a7c6..35df04dad2fd 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -447,7 +447,7 @@ static int report__setup_sample_type(struct report *rep) } } - callchain_param_setup(sample_type, perf_env__arch(&rep->session->header.env)); + callchain_param_setup(sample_type, perf_env__arch(perf_session__env(rep->session))); if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" @@ -550,7 +550,7 @@ static int evlist__tui_block_hists_browse(struct evlist *evlist, struct report * evlist__for_each_entry(evlist, pos) { ret = report__browse_block_hists(&rep->block_reports[i++].hist, rep->min_percent, pos, - &rep->session->header.env); + perf_session__env(rep->session)); if (ret != 0) return ret; } @@ -685,7 +685,7 @@ static int report__browse_hists(struct report *rep) } ret = evlist__tui_browse_hists(evlist, help, NULL, rep->min_percent, - &session->header.env, true); + perf_session__env(session), true); /* * Usually "ret" is the last pressed key, and we only * care if the key notifies us to switch data file. @@ -861,17 +861,24 @@ static int maps__fprintf_task_cb(struct map *map, void *data) struct maps__fprintf_task_args *args = data; const struct dso *dso = map__dso(map); u32 prot = map__prot(map); + const struct dso_id *dso_id = dso__id_const(dso); int ret; + char buf[SBUILD_ID_SIZE]; + + if (dso_id->mmap2_valid) + snprintf(buf, sizeof(buf), "%" PRIu64, dso_id->ino); + else + build_id__snprintf(&dso_id->build_id, buf, sizeof(buf)); ret = fprintf(args->fp, - "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n", + "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %s %s\n", args->indent, "", map__start(map), map__end(map), prot & PROT_READ ? 'r' : '-', prot & PROT_WRITE ? 'w' : '-', prot & PROT_EXEC ? 'x' : '-', map__flags(map) ? 's' : 'p', map__pgoff(map), - dso__id_const(dso)->ino, dso__name(dso)); + buf, dso__name(dso)); if (ret < 0) return ret; @@ -1267,6 +1274,8 @@ static int process_attr(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) { + struct perf_session *session; + struct perf_env *env; u64 sample_type; int err; @@ -1279,7 +1288,9 @@ static int process_attr(const struct perf_tool *tool __maybe_unused, * on events sample_type. */ sample_type = evlist__combined_sample_type(*pevlist); - callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); + session = (*pevlist)->session; + env = perf_session__env(session); + callchain_param_setup(sample_type, perf_env__arch(env)); return 0; } @@ -1779,7 +1790,7 @@ int cmd_report(int argc, const char **argv) } if ((last_key != K_SWITCH_INPUT_DATA && last_key != K_RELOAD) && - (setup_sorting(session->evlist) < 0)) { + (setup_sorting(session->evlist, perf_session__env(session)) < 0)) { if (sort_order) parse_options_usage(report_usage, options, "s", 1); if (field_order) @@ -1835,7 +1846,7 @@ int cmd_report(int argc, const char **argv) annotation_config__init(); } - if (symbol__init(&session->header.env) < 0) + if (symbol__init(perf_session__env(session)) < 0) goto error; if (report.time_str) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 26ece6e9bfd1..f166d6cbc083 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -994,7 +994,7 @@ thread_atoms_search(struct rb_root_cached *root, struct thread *thread, else if (cmp < 0) node = node->rb_right; else { - BUG_ON(thread != atoms->thread); + BUG_ON(!RC_CHK_EQUAL(thread, atoms->thread)); return atoms; } } @@ -1111,6 +1111,21 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp) atoms->nb_atoms++; } +static void free_work_atoms(struct work_atoms *atoms) +{ + struct work_atom *atom, *tmp; + + if (atoms == NULL) + return; + + list_for_each_entry_safe(atom, tmp, &atoms->work_list, list) { + list_del(&atom->list); + free(atom); + } + thread__zput(atoms->thread); + free(atoms); +} + static int latency_switch_event(struct perf_sched *sched, struct evsel *evsel, struct perf_sample *sample, @@ -1634,6 +1649,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, const char *color = PERF_COLOR_NORMAL; char stimestamp[32]; const char *str; + int ret = -1; BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0); @@ -1664,17 +1680,20 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, sched_in = map__findnew_thread(sched, machine, -1, next_pid); sched_out = map__findnew_thread(sched, machine, -1, prev_pid); if (sched_in == NULL || sched_out == NULL) - return -1; + goto out; tr = thread__get_runtime(sched_in); - if (tr == NULL) { - thread__put(sched_in); - return -1; - } + if (tr == NULL) + goto out; + + thread__put(sched->curr_thread[this_cpu.cpu]); + thread__put(sched->curr_out_thread[this_cpu.cpu]); sched->curr_thread[this_cpu.cpu] = thread__get(sched_in); sched->curr_out_thread[this_cpu.cpu] = thread__get(sched_out); + ret = 0; + str = thread__comm_str(sched_in); new_shortname = 0; if (!tr->shortname[0]) { @@ -1769,12 +1788,10 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, color_fprintf(stdout, color, "\n"); out: - if (sched->map.task_name) - thread__put(sched_out); - + thread__put(sched_out); thread__put(sched_in); - return 0; + return ret; } static int process_sched_switch_event(const struct perf_tool *tool, @@ -1922,7 +1939,7 @@ static int perf_sched__read_events(struct perf_sched *sched) return PTR_ERR(session); } - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); /* prefer sched_waking if it is captured */ if (evlist__find_tracepoint_by_name(session->evlist, "sched:sched_waking")) @@ -2018,6 +2035,16 @@ static u64 evsel__get_time(struct evsel *evsel, u32 cpu) return r->last_time[cpu]; } +static void timehist__evsel_priv_destructor(void *priv) +{ + struct evsel_runtime *r = priv; + + if (r) { + free(r->last_time); + free(r); + } +} + static int comm_width = 30; static char *timehist_get_commstr(struct thread *thread) @@ -2174,6 +2201,11 @@ static void timehist_print_sample(struct perf_sched *sched, printf(" "); } + if (!thread__comm_set(thread)) { + const char *prev_comm = evsel__strval(evsel, sample, "prev_comm"); + thread__set_comm(thread, prev_comm, sample->time); + } + printf(" %-*s ", comm_width, timehist_get_commstr(thread)); if (sched->show_prio) @@ -2311,8 +2343,10 @@ static void save_task_callchain(struct perf_sched *sched, return; } - if (!sched->show_callchain || sample->callchain == NULL) + if (!sched->show_callchain || sample->callchain == NULL) { + thread__put(thread); return; + } cursor = get_tls_callchain_cursor(); @@ -2321,10 +2355,12 @@ static void save_task_callchain(struct perf_sched *sched, if (verbose > 0) pr_err("Failed to resolve callchain. Skipping\n"); + thread__put(thread); return; } callchain_cursor_commit(cursor); + thread__put(thread); while (true) { struct callchain_cursor_node *node; @@ -2401,8 +2437,17 @@ static void free_idle_threads(void) return; for (i = 0; i < idle_max_cpu; ++i) { - if ((idle_threads[i])) - thread__delete(idle_threads[i]); + struct thread *idle = idle_threads[i]; + + if (idle) { + struct idle_thread_runtime *itr; + + itr = thread__priv(idle); + if (itr) + thread__put(itr->last_thread); + + thread__delete(idle); + } } free(idle_threads); @@ -2439,7 +2484,7 @@ static struct thread *get_idle_thread(int cpu) } } - return idle_threads[cpu]; + return thread__get(idle_threads[cpu]); } static void save_idle_callchain(struct perf_sched *sched, @@ -2494,7 +2539,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, if (itr == NULL) return NULL; - itr->last_thread = thread; + thread__put(itr->last_thread); + itr->last_thread = thread__get(thread); /* copy task callchain when entering to idle */ if (evsel__intval(evsel, sample, "next_pid") == 0) @@ -2565,6 +2611,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, /* show wakeup unless both awakee and awaker are filtered */ if (timehist_skip_sample(sched, thread, evsel, sample) && timehist_skip_sample(sched, awakened, evsel, sample)) { + thread__put(thread); return; } @@ -2581,6 +2628,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, printf("awakened: %s", timehist_get_commstr(awakened)); printf("\n"); + + thread__put(thread); } static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused, @@ -2609,8 +2658,10 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, return -1; tr = thread__get_runtime(thread); - if (tr == NULL) + if (tr == NULL) { + thread__put(thread); return -1; + } if (tr->ready_to_run == 0) tr->ready_to_run = sample->time; @@ -2620,6 +2671,7 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, !perf_time__skip_sample(&sched->ptime, sample->time)) timehist_print_wakeup_event(sched, evsel, sample, machine, thread); + thread__put(thread); return 0; } @@ -2647,6 +2699,7 @@ static void timehist_print_migration_event(struct perf_sched *sched, if (timehist_skip_sample(sched, thread, evsel, sample) && timehist_skip_sample(sched, migrated, evsel, sample)) { + thread__put(thread); return; } @@ -2674,6 +2727,7 @@ static void timehist_print_migration_event(struct perf_sched *sched, printf(" cpu %d => %d", ocpu, dcpu); printf("\n"); + thread__put(thread); } static int timehist_migrate_task_event(const struct perf_tool *tool, @@ -2693,8 +2747,10 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, return -1; tr = thread__get_runtime(thread); - if (tr == NULL) + if (tr == NULL) { + thread__put(thread); return -1; + } tr->migrations++; tr->migrated = sample->time; @@ -2704,6 +2760,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, timehist_print_migration_event(sched, evsel, sample, machine, thread); } + thread__put(thread); return 0; } @@ -2726,10 +2783,10 @@ static void timehist_update_task_prio(struct evsel *evsel, return; tr = thread__get_runtime(thread); - if (tr == NULL) - return; + if (tr != NULL) + tr->prio = next_prio; - tr->prio = next_prio; + thread__put(thread); } static int timehist_sched_change_event(const struct perf_tool *tool, @@ -2741,7 +2798,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, struct perf_sched *sched = container_of(tool, struct perf_sched, tool); struct perf_time_interval *ptime = &sched->ptime; struct addr_location al; - struct thread *thread; + struct thread *thread = NULL; struct thread_runtime *tr = NULL; u64 tprev, t = sample->time; int rc = 0; @@ -2865,6 +2922,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, evsel__save_time(evsel, sample->time, sample->cpu); + thread__put(thread); addr_location__exit(&al); return rc; } @@ -3236,6 +3294,7 @@ static int perf_sched__timehist(struct perf_sched *sched) }; struct perf_session *session; + struct perf_env *env; struct evlist *evlist; int err = -1; @@ -3260,6 +3319,7 @@ static int perf_sched__timehist(struct perf_sched *sched) if (IS_ERR(session)) return PTR_ERR(session); + env = perf_session__env(session); if (cpu_list) { err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); if (err < 0) @@ -3268,7 +3328,7 @@ static int perf_sched__timehist(struct perf_sched *sched) evlist = session->evlist; - symbol__init(&session->header.env); + symbol__init(env); if (perf_time__parse_str(&sched->ptime, sched->time_str) != 0) { pr_err("Invalid time string\n"); @@ -3286,6 +3346,8 @@ static int perf_sched__timehist(struct perf_sched *sched) setup_pager(); + evsel__set_priv_destructor(timehist__evsel_priv_destructor); + /* prefer sched_waking if it is captured */ if (evlist__find_tracepoint_by_name(session->evlist, "sched:sched_waking")) handlers[1].handler = timehist_sched_wakeup_ignore; @@ -3305,7 +3367,7 @@ static int perf_sched__timehist(struct perf_sched *sched) goto out; /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu.cpu = session->header.env.nr_cpus_online; + sched->max_cpu.cpu = env->nr_cpus_online; if (sched->max_cpu.cpu == 0) sched->max_cpu.cpu = 4; if (init_idle_threads(sched->max_cpu.cpu)) @@ -3386,13 +3448,13 @@ static void __merge_work_atoms(struct rb_root_cached *root, struct work_atoms *d this->total_runtime += data->total_runtime; this->nb_atoms += data->nb_atoms; this->total_lat += data->total_lat; - list_splice(&data->work_list, &this->work_list); + list_splice_init(&data->work_list, &this->work_list); if (this->max_lat < data->max_lat) { this->max_lat = data->max_lat; this->max_lat_start = data->max_lat_start; this->max_lat_end = data->max_lat_end; } - zfree(&data); + free_work_atoms(data); return; } } @@ -3471,7 +3533,6 @@ static int perf_sched__lat(struct perf_sched *sched) work_list = rb_entry(next, struct work_atoms, node); output_lat_thread(sched, work_list); next = rb_next(next); - thread__zput(work_list->thread); } printf(" -----------------------------------------------------------------------------------------------------------------\n"); @@ -3485,6 +3546,13 @@ static int perf_sched__lat(struct perf_sched *sched) rc = 0; + while ((next = rb_first_cached(&sched->sorted_atom_root))) { + struct work_atoms *data; + + data = rb_entry(next, struct work_atoms, node); + rb_erase_cached(next, &sched->sorted_atom_root); + free_work_atoms(data); + } out_free_cpus_switch_event: free_cpus_switch_event(sched); return rc; @@ -3556,10 +3624,10 @@ static int perf_sched__map(struct perf_sched *sched) sched->curr_out_thread = calloc(MAX_CPUS, sizeof(*(sched->curr_out_thread))); if (!sched->curr_out_thread) - return rc; + goto out_free_curr_thread; if (setup_cpus_switch_event(sched)) - goto out_free_curr_thread; + goto out_free_curr_out_thread; if (setup_map_cpus(sched)) goto out_free_cpus_switch_event; @@ -3590,7 +3658,14 @@ static int perf_sched__map(struct perf_sched *sched) out_free_cpus_switch_event: free_cpus_switch_event(sched); +out_free_curr_out_thread: + for (int i = 0; i < MAX_CPUS; i++) + thread__put(sched->curr_out_thread[i]); + zfree(&sched->curr_out_thread); + out_free_curr_thread: + for (int i = 0; i < MAX_CPUS; i++) + thread__put(sched->curr_thread[i]); zfree(&sched->curr_thread); return rc; } @@ -3898,13 +3973,15 @@ int cmd_sched(int argc, const char **argv) if (!argc) usage_with_options(sched_usage, sched_options); + thread__set_priv_destructor(free); + /* * Aliased to 'perf script' for now: */ if (!strcmp(argv[0], "script")) { - return cmd_script(argc, argv); + ret = cmd_script(argc, argv); } else if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) { - return __cmd_record(argc, argv); + ret = __cmd_record(argc, argv); } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) { sched.tp_handler = &lat_ops; if (argc > 1) { @@ -3913,7 +3990,7 @@ int cmd_sched(int argc, const char **argv) usage_with_options(latency_usage, latency_options); } setup_sorting(&sched, latency_options, latency_usage); - return perf_sched__lat(&sched); + ret = perf_sched__lat(&sched); } else if (!strcmp(argv[0], "map")) { if (argc) { argc = parse_options(argc, argv, map_options, map_usage, 0); @@ -3924,13 +4001,14 @@ int cmd_sched(int argc, const char **argv) sched.map.task_names = strlist__new(sched.map.task_name, NULL); if (sched.map.task_names == NULL) { fprintf(stderr, "Failed to parse task names\n"); - return -1; + ret = -1; + goto out; } } } sched.tp_handler = &map_ops; setup_sorting(&sched, latency_options, latency_usage); - return perf_sched__map(&sched); + ret = perf_sched__map(&sched); } else if (strlen(argv[0]) > 2 && strstarts("replay", argv[0])) { sched.tp_handler = &replay_ops; if (argc) { @@ -3938,7 +4016,7 @@ int cmd_sched(int argc, const char **argv) if (argc) usage_with_options(replay_usage, replay_options); } - return perf_sched__replay(&sched); + ret = perf_sched__replay(&sched); } else if (!strcmp(argv[0], "timehist")) { if (argc) { argc = parse_options(argc, argv, timehist_options, @@ -3954,19 +4032,19 @@ int cmd_sched(int argc, const char **argv) parse_options_usage(NULL, timehist_options, "w", true); if (sched.show_next) parse_options_usage(NULL, timehist_options, "n", true); - return -EINVAL; + ret = -EINVAL; + goto out; } ret = symbol__validate_sym_arguments(); - if (ret) - return ret; - - return perf_sched__timehist(&sched); + if (!ret) + ret = perf_sched__timehist(&sched); } else { usage_with_options(sched_usage, sched_options); } +out: /* free usage string allocated by parse_options_subcommand */ free((void *)sched_usage[0]); - return 0; + return ret; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 6c3bf74dd78c..d9fbdcf72f25 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -38,6 +38,7 @@ #include "print_insn.h" #include "archinsn.h" #include +#include #include #include #include @@ -50,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -712,7 +714,7 @@ static int perf_session__check_output_opt(struct perf_session *session) } } - if (tod && !session->header.env.clock.enabled) { + if (tod && !perf_session__env(session)->clock.enabled) { pr_err("Can't provide 'tod' time, missing clock data. " "Please record with -k/--clockid option.\n"); return -1; @@ -757,7 +759,7 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen, if (buflen < 64 || !script) return buf; - env = &script->session->header.env; + env = perf_session__env(script->session); if (!env->clock.enabled) { scnprintf(buf, buflen, "disabled"); return buf; @@ -2134,8 +2136,7 @@ static void perf_sample__fprint_metric(struct perf_script *script, perf_stat__print_shadow_stats(&stat_config, ev2, evsel_script(ev2)->val, sample->cpu, - &ctx, - NULL); + &ctx); } evsel_script(leader)->gnum = 0; } @@ -2251,7 +2252,7 @@ static void process_event(struct perf_script *script, fprintf(fp, "%16" PRIu16, sample->ins_lat); if (PRINT_FIELD(RETIRE_LAT)) - fprintf(fp, "%16" PRIu16, sample->retire_lat); + fprintf(fp, "%16" PRIu16, sample->weight3); if (PRINT_FIELD(CGROUP)) { const char *cgrp_name; @@ -2533,7 +2534,7 @@ static int process_attr(const struct perf_tool *tool, union perf_event *event, * on events sample_type. */ sample_type = evlist__combined_sample_type(evlist); - callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); + callchain_param_setup(sample_type, perf_env__arch(perf_session__env(scr->session))); /* Enable fields for callchain entries */ if (symbol_conf.use_callchain && @@ -2755,6 +2756,14 @@ process_bpf_events(const struct perf_tool *tool __maybe_unused, sample->tid); } +static int +process_bpf_metadata_event(struct perf_session *session __maybe_unused, + union perf_event *event) +{ + perf_event__fprintf(event, NULL, stdout); + return 0; +} + static int process_text_poke_events(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -2877,8 +2886,9 @@ static int __cmd_script(struct perf_script *script) script->tool.finished_round = process_finished_round_event; } if (script->show_bpf_events) { - script->tool.ksymbol = process_bpf_events; - script->tool.bpf = process_bpf_events; + script->tool.ksymbol = process_bpf_events; + script->tool.bpf = process_bpf_events; + script->tool.bpf_metadata = process_bpf_metadata_event; } if (script->show_text_poke_events) { script->tool.ksymbol = process_bpf_events; @@ -3853,6 +3863,7 @@ int cmd_script(int argc, const char **argv) "perf script [] [script-args]", NULL }; + struct perf_env *env; perf_set_singlethreaded(); @@ -4099,6 +4110,7 @@ int cmd_script(int argc, const char **argv) if (IS_ERR(session)) return PTR_ERR(session); + env = perf_session__env(session); if (header || header_only) { script.tool.show_feat_hdr = SHOW_FEAT_HEADER; perf_session__fprintf_info(session, stdout, show_full_info); @@ -4108,17 +4120,17 @@ int cmd_script(int argc, const char **argv) if (show_full_info) script.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO; - if (symbol__init(&session->header.env) < 0) + if (symbol__init(env) < 0) goto out_delete; uname(&uts); if (data.is_pipe) { /* Assume pipe_mode indicates native_arch */ native_arch = true; - } else if (session->header.env.arch) { - if (!strcmp(uts.machine, session->header.env.arch)) + } else if (env->arch) { + if (!strcmp(uts.machine, env->arch)) native_arch = true; else if (!strcmp(uts.machine, "x86_64") && - !strcmp(session->header.env.arch, "i386")) + !strcmp(env->arch, "i386")) native_arch = true; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bf0e5e12d992..2c38dd98f6ca 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -108,9 +108,7 @@ static struct parse_events_option_args parse_events_option_args = { static bool all_counters_use_bpf = true; -static struct target target = { - .uid = UINT_MAX, -}; +static struct target target; static volatile sig_atomic_t child_pid = -1; static int detailed_run = 0; @@ -1367,7 +1365,7 @@ static struct aggr_cpu_id perf_stat__get_aggr(struct perf_stat_config *config, struct aggr_cpu_id id; /* per-process mode - should use global aggr mode */ - if (cpu.cpu == -1) + if (cpu.cpu == -1 || cpu.cpu >= config->cpus_aggr_map->nr) return get_id(config, cpu); if (aggr_cpu_id__is_empty(&config->cpus_aggr_map->map[cpu.cpu])) @@ -1515,11 +1513,8 @@ static int perf_stat_init_aggr_mode(void) * taking the highest cpu number to be the size of * the aggregation translate cpumap. */ - if (!perf_cpu_map__is_any_cpu_or_is_empty(evsel_list->core.user_requested_cpus)) - nr = perf_cpu_map__max(evsel_list->core.user_requested_cpus).cpu; - else - nr = 0; - stat_config.cpus_aggr_map = cpu_aggr_map__empty_new(nr + 1); + nr = perf_cpu_map__max(evsel_list->core.all_cpus).cpu + 1; + stat_config.cpus_aggr_map = cpu_aggr_map__empty_new(nr); return stat_config.cpus_aggr_map ? 0 : -ENOMEM; } @@ -1694,48 +1689,48 @@ static struct aggr_cpu_id perf_env__get_global_aggr_by_cpu(struct perf_cpu cpu _ static struct aggr_cpu_id perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_socket_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_socket_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_die_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_die_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_die_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_cluster_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_cluster_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_cluster_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_cache_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_cache_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_cache_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_core_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_core_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_cpu_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_cpu_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_cpu_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_node_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_node_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_node_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static struct aggr_cpu_id perf_stat__get_global_file(struct perf_stat_config *config __maybe_unused, struct perf_cpu cpu) { - return perf_env__get_global_aggr_by_cpu(cpu, &perf_stat.session->header.env); + return perf_env__get_global_aggr_by_cpu(cpu, perf_session__env(perf_stat.session)); } static aggr_cpu_id_get_t aggr_mode__get_aggr_file(enum aggr_mode aggr_mode) @@ -1794,7 +1789,7 @@ static aggr_get_id_t aggr_mode__get_id_file(enum aggr_mode aggr_mode) static int perf_stat_init_aggr_mode_file(struct perf_stat *st) { - struct perf_env *env = &st->session->header.env; + struct perf_env *env = perf_session__env(st->session); aggr_cpu_id_get_t get_id = aggr_mode__get_aggr_file(stat_config.aggr_mode); bool needs_sort = stat_config.aggr_mode != AGGR_NONE; @@ -1865,8 +1860,7 @@ static int add_default_events(void) stat_config.metric_no_threshold, stat_config.user_requested_cpu_list, stat_config.system_wide, - stat_config.hardware_aware_grouping, - &stat_config.metric_events); + stat_config.hardware_aware_grouping); goto out; } @@ -1903,8 +1897,7 @@ static int add_default_events(void) stat_config.metric_no_threshold, stat_config.user_requested_cpu_list, stat_config.system_wide, - stat_config.hardware_aware_grouping, - &stat_config.metric_events); + stat_config.hardware_aware_grouping); goto out; } @@ -1941,8 +1934,7 @@ static int add_default_events(void) /*metric_no_threshold=*/true, stat_config.user_requested_cpu_list, stat_config.system_wide, - stat_config.hardware_aware_grouping, - &stat_config.metric_events) < 0) { + stat_config.hardware_aware_grouping) < 0) { ret = -1; goto out; } @@ -1991,8 +1983,7 @@ static int add_default_events(void) /*metric_no_threshold=*/true, stat_config.user_requested_cpu_list, stat_config.system_wide, - stat_config.hardware_aware_grouping, - &stat_config.metric_events) < 0) { + stat_config.hardware_aware_grouping) < 0) { ret = -1; goto out; } @@ -2001,6 +1992,9 @@ static int add_default_events(void) evsel->default_metricgroup = true; evlist__splice_list_tail(evlist, &metric_evlist->core.entries); + metricgroup__copy_metric_events(evlist, /*cgrp=*/NULL, + &evlist->metric_events, + &metric_evlist->metric_events); evlist__delete(metric_evlist); } } @@ -2055,6 +2049,9 @@ static int add_default_events(void) } parse_events_error__exit(&err); evlist__splice_list_tail(evsel_list, &evlist->core.entries); + metricgroup__copy_metric_events(evsel_list, /*cgrp=*/NULL, + &evsel_list->metric_events, + &evlist->metric_events); evlist__delete(evlist); return ret; } @@ -2115,8 +2112,9 @@ static int process_stat_round_event(struct perf_session *session, { struct perf_record_stat_round *stat_round = &event->stat_round; struct timespec tsh, *ts = NULL; - const char **argv = session->header.env.cmdline_argv; - int argc = session->header.env.nr_cmdline; + struct perf_env *env = perf_session__env(session); + const char **argv = env->cmdline_argv; + int argc = env->nr_cmdline; process_counters(); @@ -2741,8 +2739,7 @@ int cmd_stat(int argc, const char **argv) stat_config.metric_no_threshold, stat_config.user_requested_cpu_list, stat_config.system_wide, - stat_config.hardware_aware_grouping, - &stat_config.metric_events); + stat_config.hardware_aware_grouping); zfree(&metrics); if (ret) { @@ -2762,8 +2759,7 @@ int cmd_stat(int argc, const char **argv) goto out; } - if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list, - &stat_config.metric_events, true) < 0) { + if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list, true) < 0) { parse_options_usage(stat_usage, stat_options, "for-each-cgroup", 0); goto out; @@ -2938,7 +2934,6 @@ int cmd_stat(int argc, const char **argv) evlist__delete(evsel_list); - metricgroup__rblist_exit(&stat_config.metric_events); evlist__close_control(stat_config.ctl_fd, stat_config.ctl_fd_ack, &stat_config.ctl_fd_close); return status; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 068d297aaf44..22050c640dfa 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -1618,7 +1618,7 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) if (IS_ERR(session)) return PTR_ERR(session); - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); (void)perf_header__process_sections(&session->header, perf_data__fd(session->data), diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7b6cde87d2af..a11f629c7d76 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -643,11 +643,12 @@ static void *display_thread_tui(void *arg) */ evlist__for_each_entry(top->evlist, pos) { struct hists *hists = evsel__hists(pos); - hists->uid_filter_str = top->record_opts.target.uid_str; + hists->uid_filter_str = top->uid_str; } ret = evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, - &top->session->header.env, !top->record_opts.overwrite); + perf_session__env(top->session), + !top->record_opts.overwrite); if (ret == K_RELOAD) { top->zero = true; goto repeat; @@ -1253,7 +1254,7 @@ static int __cmd_top(struct perf_top *top) int ret; if (!annotate_opts.objdump_path) { - ret = perf_env__lookup_objdump(&top->session->header.env, + ret = perf_env__lookup_objdump(perf_session__env(top->session), &annotate_opts.objdump_path); if (ret) return ret; @@ -1300,7 +1301,7 @@ static int __cmd_top(struct perf_top *top) perf_set_multithreaded(); if (perf_hpp_list.socket) { - ret = perf_env__read_cpu_topology_map(&perf_env); + ret = perf_env__read_cpu_topology_map(perf_session__env(top->session)); if (ret < 0) { char errbuf[BUFSIZ]; const char *err = str_error_r(-ret, errbuf, sizeof(errbuf)); @@ -1571,7 +1572,7 @@ int cmd_top(int argc, const char **argv) "Add prefix to source file path names in programs (with --prefix-strip)"), OPT_STRING(0, "prefix-strip", &annotate_opts.prefix_strip, "N", "Strip first N entries of source file path name in programs (with --prefix)"), - OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"), OPT_CALLBACK(0, "percent-limit", &top, "percent", "Don't show entries under that percent", parse_percent_limit), OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", @@ -1623,6 +1624,7 @@ int cmd_top(int argc, const char **argv) NULL }; int status = hists__init(); + struct perf_env host_env; if (status < 0) return status; @@ -1636,14 +1638,19 @@ int cmd_top(int argc, const char **argv) if (top.evlist == NULL) return -ENOMEM; + perf_env__init(&host_env); status = perf_config(perf_top_config, &top); if (status) - return status; + goto out_delete_evlist; /* * Since the per arch annotation init routine may need the cpuid, read * it here, since we are not getting this from the perf.data header. */ - status = perf_env__read_cpuid(&perf_env); + status = perf_env__set_cmdline(&host_env, argc, argv); + if (status) + goto out_delete_evlist; + + status = perf_env__read_cpuid(&host_env); if (status) { /* * Some arches do not provide a get_cpuid(), so just use pr_debug, otherwise @@ -1653,7 +1660,6 @@ int cmd_top(int argc, const char **argv) "Couldn't read the cpuid for this machine: %s\n", str_error_r(errno, errbuf, sizeof(errbuf))); } - top.evlist->env = &perf_env; argc = parse_options(argc, argv, options, top_usage, 0); if (argc) @@ -1661,18 +1667,24 @@ int cmd_top(int argc, const char **argv) if (disassembler_style) { annotate_opts.disassembler_style = strdup(disassembler_style); - if (!annotate_opts.disassembler_style) - return -ENOMEM; + if (!annotate_opts.disassembler_style) { + status = -ENOMEM; + goto out_delete_evlist; + } } if (objdump_path) { annotate_opts.objdump_path = strdup(objdump_path); - if (!annotate_opts.objdump_path) - return -ENOMEM; + if (!annotate_opts.objdump_path) { + status = -ENOMEM; + goto out_delete_evlist; + } } if (addr2line_path) { symbol_conf.addr2line_path = strdup(addr2line_path); - if (!symbol_conf.addr2line_path) - return -ENOMEM; + if (!symbol_conf.addr2line_path) { + status = -ENOMEM; + goto out_delete_evlist; + } } status = symbol__validate_sym_arguments(); @@ -1734,6 +1746,14 @@ int cmd_top(int argc, const char **argv) if (opts->branch_stack && callchain_param.enabled) symbol_conf.show_branchflag_count = true; + if (opts->branch_stack) { + status = perf_env__read_core_pmu_caps(&host_env); + if (status) { + pr_err("PMU capability data is not available\n"); + goto out_delete_evlist; + } + } + sort__mode = SORT_MODE__TOP; /* display thread wants entries to be collapsed in a different tree */ perf_hpp_list.need_collapse = 1; @@ -1747,7 +1767,17 @@ int cmd_top(int argc, const char **argv) setup_browser(false); - if (setup_sorting(top.evlist) < 0) { + top.session = __perf_session__new(/*data=*/NULL, /*tool=*/NULL, + /*trace_event_repipe=*/false, + &host_env); + if (IS_ERR(top.session)) { + status = PTR_ERR(top.session); + top.session = NULL; + goto out_delete_evlist; + } + top.evlist->session = top.session; + + if (setup_sorting(top.evlist, perf_session__env(top.session)) < 0) { if (sort_order) parse_options_usage(top_usage, options, "s", 1); if (field_order) @@ -1762,15 +1792,17 @@ int cmd_top(int argc, const char **argv) ui__warning("%s\n", errbuf); } - status = target__parse_uid(target); - if (status) { - int saved_errno = errno; + if (top.uid_str) { + uid_t uid = parse_uid(top.uid_str); - target__strerror(target, status, errbuf, BUFSIZ); - ui__error("%s\n", errbuf); - - status = -saved_errno; - goto out_delete_evlist; + if (uid == UINT_MAX) { + ui__error("Invalid User: %s", top.uid_str); + status = -EINVAL; + goto out_delete_evlist; + } + status = parse_uid_filter(top.evlist, uid); + if (status) + goto out_delete_evlist; } if (target__none(target)) @@ -1820,13 +1852,6 @@ int cmd_top(int argc, const char **argv) signal(SIGWINCH, winch_sig); } - top.session = perf_session__new(NULL, NULL); - if (IS_ERR(top.session)) { - status = PTR_ERR(top.session); - top.session = NULL; - goto out_delete_evlist; - } - if (!evlist__needs_bpf_sb_event(top.evlist)) top.record_opts.no_bpf_event = true; @@ -1840,7 +1865,7 @@ int cmd_top(int argc, const char **argv) goto out_delete_evlist; } - if (evlist__add_bpf_sb_event(top.sb_evlist, &perf_env)) { + if (evlist__add_bpf_sb_event(top.sb_evlist, &host_env)) { pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n."); status = -EINVAL; goto out_delete_evlist; @@ -1862,6 +1887,7 @@ int cmd_top(int argc, const char **argv) evlist__delete(top.evlist); perf_session__delete(top.session); annotation_options__exit(); + perf_env__exit(&host_env); return status; } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2ab1b8e05ad3..fe737b3ac6e6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -20,9 +20,6 @@ #include #include #include -#ifdef HAVE_BPF_SKEL -#include "bpf_skel/augmented_raw_syscalls.skel.h" -#endif #endif #include "util/bpf_map.h" #include "util/rlimit.h" @@ -143,6 +140,7 @@ struct syscall_fmt { }; struct trace { + struct perf_env host_env; struct perf_tool tool; struct { /** Sorted sycall numbers used by the trace. */ @@ -155,9 +153,6 @@ struct trace { *bpf_output; } events; } syscalls; -#ifdef HAVE_BPF_SKEL - struct augmented_raw_syscalls_bpf *skel; -#endif #ifdef HAVE_LIBBPF_SUPPORT struct btf *btf; #endif @@ -236,6 +231,7 @@ struct trace { struct ordered_events data; u64 last; } oe; + const char *uid_str; }; static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused) @@ -1123,12 +1119,14 @@ static bool syscall_arg__strtoul_btf_type(char *bf __maybe_unused, size_t size _ #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ .strtoul = STUL_STRARRAY, \ - .parm = &strarray__##array, } + .parm = &strarray__##array, \ + .show_zero = true, } #define STRARRAY_FLAGS(name, array) \ { .scnprintf = SCA_STRARRAY_FLAGS, \ .strtoul = STUL_STRARRAY_FLAGS, \ - .parm = &strarray__##array, } + .parm = &strarray__##array, \ + .show_zero = true, } #include "trace/beauty/eventfd.c" #include "trace/beauty/futex_op.c" @@ -1980,17 +1978,24 @@ static char *trace__machine__resolve_kernel_addr(void *vmachine, unsigned long l return machine__resolve_kernel_addr(vmachine, addrp, modp); } -static int trace__symbols_init(struct trace *trace, struct evlist *evlist) +static int trace__symbols_init(struct trace *trace, int argc, const char **argv, + struct evlist *evlist) { int err = symbol__init(NULL); if (err) return err; - trace->host = machine__new_host(); - if (trace->host == NULL) - return -ENOMEM; + perf_env__init(&trace->host_env); + err = perf_env__set_cmdline(&trace->host_env, argc, argv); + if (err) + goto out; + trace->host = machine__new_host(&trace->host_env); + if (trace->host == NULL) { + err = -ENOMEM; + goto out; + } thread__set_priv_destructor(thread_trace__delete); err = trace_event__register_resolver(trace->host, trace__machine__resolve_kernel_addr); @@ -2001,9 +2006,10 @@ static int trace__symbols_init(struct trace *trace, struct evlist *evlist) evlist->core.threads, trace__tool_process, true, false, 1); out: - if (err) + if (err) { + perf_env__exit(&trace->host_env); symbol__exit(); - + } return err; } @@ -2012,6 +2018,7 @@ static void trace__symbols__exit(struct trace *trace) machine__exit(trace->host); trace->host = NULL; + perf_env__exit(&trace->host_env); symbol__exit(); } @@ -2891,13 +2898,6 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam return sample__fprintf_callchain(sample, 38, print_opts, get_tls_callchain_cursor(), symbol_conf.bt_stop_list, trace->output); } -static const char *errno_to_name(struct evsel *evsel, int err) -{ - struct perf_env *env = evsel__env(evsel); - - return perf_env__arch_strerrno(env, err); -} - static int trace__sys_exit(struct trace *trace, struct evsel *evsel, union perf_event *event __maybe_unused, struct perf_sample *sample) @@ -2983,8 +2983,9 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, } else if (ret < 0) { errno_print: { char bf[STRERR_BUFSIZE]; - const char *emsg = str_error_r(-ret, bf, sizeof(bf)), - *e = errno_to_name(evsel, -ret); + struct perf_env *env = evsel__env(evsel) ?: &trace->host_env; + const char *emsg = str_error_r(-ret, bf, sizeof(bf)); + const char *e = perf_env__arch_strerrno(env, err); fprintf(trace->output, "-1 %s (%s)", e, emsg); } @@ -3700,7 +3701,10 @@ static int trace__set_ev_qualifier_tp_filter(struct trace *trace) goto out; } -#ifdef HAVE_BPF_SKEL +#ifdef HAVE_LIBBPF_SUPPORT + +static struct bpf_program *unaugmented_prog; + static int syscall_arg_fmt__cache_btf_struct(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type) { int id; @@ -3718,26 +3722,8 @@ static int syscall_arg_fmt__cache_btf_struct(struct syscall_arg_fmt *arg_fmt, st return 0; } -static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name) -{ - struct bpf_program *pos, *prog = NULL; - const char *sec_name; - - if (trace->skel->obj == NULL) - return NULL; - - bpf_object__for_each_program(pos, trace->skel->obj) { - sec_name = bpf_program__section_name(pos); - if (sec_name && !strcmp(sec_name, name)) { - prog = pos; - break; - } - } - - return prog; -} - -static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, struct syscall *sc, +static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace __maybe_unused, + struct syscall *sc, const char *prog_name, const char *type) { struct bpf_program *prog; @@ -3745,19 +3731,19 @@ static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, str if (prog_name == NULL) { char default_prog_name[256]; scnprintf(default_prog_name, sizeof(default_prog_name), "tp/syscalls/sys_%s_%s", type, sc->name); - prog = trace__find_bpf_program_by_title(trace, default_prog_name); + prog = augmented_syscalls__find_by_title(default_prog_name); if (prog != NULL) goto out_found; if (sc->fmt && sc->fmt->alias) { scnprintf(default_prog_name, sizeof(default_prog_name), "tp/syscalls/sys_%s_%s", type, sc->fmt->alias); - prog = trace__find_bpf_program_by_title(trace, default_prog_name); + prog = augmented_syscalls__find_by_title(default_prog_name); if (prog != NULL) goto out_found; } goto out_unaugmented; } - prog = trace__find_bpf_program_by_title(trace, prog_name); + prog = augmented_syscalls__find_by_title(prog_name); if (prog != NULL) { out_found: @@ -3767,7 +3753,7 @@ static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, str pr_debug("Couldn't find BPF prog \"%s\" to associate with syscalls:sys_%s_%s, not augmenting it\n", prog_name, type, sc->name); out_unaugmented: - return trace->skel->progs.syscall_unaugmented; + return unaugmented_prog; } static void trace__init_syscall_bpf_progs(struct trace *trace, int e_machine, int id) @@ -3784,13 +3770,13 @@ static void trace__init_syscall_bpf_progs(struct trace *trace, int e_machine, in static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int e_machine, int id) { struct syscall *sc = trace__syscall_info(trace, NULL, e_machine, id); - return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); + return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(unaugmented_prog); } static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int e_machine, int id) { struct syscall *sc = trace__syscall_info(trace, NULL, e_machine, id); - return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); + return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(unaugmented_prog); } static int trace__bpf_sys_enter_beauty_map(struct trace *trace, int e_machine, int key, unsigned int *beauty_array) @@ -3900,7 +3886,7 @@ static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace bool is_candidate = false; if (pair == NULL || pair->id == sc->id || - pair->bpf_prog.sys_enter == trace->skel->progs.syscall_unaugmented) + pair->bpf_prog.sys_enter == unaugmented_prog) continue; for (field = sc->args, candidate_field = pair->args; @@ -3966,7 +3952,7 @@ static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace */ if (pair_prog == NULL) { pair_prog = trace__find_syscall_bpf_prog(trace, pair, pair->fmt ? pair->fmt->bpf_prog_name.sys_enter : NULL, "enter"); - if (pair_prog == trace->skel->progs.syscall_unaugmented) + if (pair_prog == unaugmented_prog) goto next_candidate; } @@ -3982,12 +3968,17 @@ static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace, int e_machine) { - int map_enter_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_enter); - int map_exit_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_exit); - int beauty_map_fd = bpf_map__fd(trace->skel->maps.beauty_map_enter); + int map_enter_fd; + int map_exit_fd; + int beauty_map_fd; int err = 0; unsigned int beauty_array[6]; + if (augmented_syscalls__get_map_fds(&map_enter_fd, &map_exit_fd, &beauty_map_fd) < 0) + return -1; + + unaugmented_prog = augmented_syscalls__unaugmented(); + for (int i = 0, num_idx = syscalltbl__num_idx(e_machine); i < num_idx; ++i) { int prog_fd, key = syscalltbl__id_at_idx(e_machine, i); @@ -4057,7 +4048,7 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace, int e_m * For now we're just reusing the sys_enter prog, and if it * already has an augmenter, we don't need to find one. */ - if (sc->bpf_prog.sys_enter != trace->skel->progs.syscall_unaugmented) + if (sc->bpf_prog.sys_enter != unaugmented_prog) continue; /* @@ -4082,7 +4073,13 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace, int e_m return err; } -#endif // HAVE_BPF_SKEL +#else // !HAVE_LIBBPF_SUPPORT +static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace __maybe_unused, + int e_machine __maybe_unused) +{ + return -1; +} +#endif // HAVE_LIBBPF_SUPPORT static int trace__set_ev_qualifier_filter(struct trace *trace) { @@ -4091,24 +4088,6 @@ static int trace__set_ev_qualifier_filter(struct trace *trace) return 0; } -static int bpf_map__set_filter_pids(struct bpf_map *map __maybe_unused, - size_t npids __maybe_unused, pid_t *pids __maybe_unused) -{ - int err = 0; -#ifdef HAVE_LIBBPF_SUPPORT - bool value = true; - int map_fd = bpf_map__fd(map); - size_t i; - - for (i = 0; i < npids; ++i) { - err = bpf_map_update_elem(map_fd, &pids[i], &value, BPF_ANY); - if (err) - break; - } -#endif - return err; -} - static int trace__set_filter_loop_pids(struct trace *trace) { unsigned int nr = 1, err; @@ -4137,8 +4116,8 @@ static int trace__set_filter_loop_pids(struct trace *trace) thread__put(thread); err = evlist__append_tp_filter_pids(trace->evlist, nr, pids); - if (!err && trace->filter_pids.map) - err = bpf_map__set_filter_pids(trace->filter_pids.map, nr, pids); + if (!err) + err = augmented_syscalls__set_filter_pids(nr, pids); return err; } @@ -4155,8 +4134,8 @@ static int trace__set_filter_pids(struct trace *trace) if (trace->filter_pids.nr > 0) { err = evlist__append_tp_filter_pids(trace->evlist, trace->filter_pids.nr, trace->filter_pids.entries); - if (!err && trace->filter_pids.map) { - err = bpf_map__set_filter_pids(trace->filter_pids.map, trace->filter_pids.nr, + if (!err) { + err = augmented_syscalls__set_filter_pids(trace->filter_pids.nr, trace->filter_pids.entries); } } else if (perf_thread_map__pid(trace->evlist->core.threads, 0) == -1) { @@ -4412,8 +4391,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) evlist__add(evlist, pgfault_min); } - /* Enable ignoring missing threads when -u/-p option is defined. */ - trace->opts.ignore_missing_thread = trace->opts.target.uid != UINT_MAX || trace->opts.target.pid; + /* Enable ignoring missing threads when -p option is defined. */ + trace->opts.ignore_missing_thread = trace->opts.target.pid; if (trace->sched && evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime)) @@ -4453,7 +4432,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) goto out_delete_evlist; } - err = trace__symbols_init(trace, evlist); + err = trace__symbols_init(trace, argc, argv, evlist); if (err < 0) { fprintf(trace->output, "Problems initializing symbol libraries!\n"); goto out_delete_evlist; @@ -4479,41 +4458,18 @@ static int trace__run(struct trace *trace, int argc, const char **argv) err = evlist__open(evlist); if (err < 0) goto out_error_open; -#ifdef HAVE_BPF_SKEL - if (trace->syscalls.events.bpf_output) { - struct perf_cpu cpu; - /* - * Set up the __augmented_syscalls__ BPF map to hold for each - * CPU the bpf-output event's file descriptor. - */ - perf_cpu_map__for_each_cpu(cpu, i, trace->syscalls.events.bpf_output->core.cpus) { - int mycpu = cpu.cpu; + augmented_syscalls__setup_bpf_output(); - bpf_map__update_elem(trace->skel->maps.__augmented_syscalls__, - &mycpu, sizeof(mycpu), - xyarray__entry(trace->syscalls.events.bpf_output->core.fd, - mycpu, 0), - sizeof(__u32), BPF_ANY); - } - } - - if (trace->skel) - trace->filter_pids.map = trace->skel->maps.pids_filtered; -#endif err = trace__set_filter_pids(trace); if (err < 0) goto out_error_mem; -#ifdef HAVE_BPF_SKEL - if (trace->skel && trace->skel->progs.sys_enter) { - /* - * TODO: Initialize for all host binary machine types, not just - * those matching the perf binary. - */ - trace__init_syscalls_bpf_prog_array_maps(trace, EM_HOST); - } -#endif + /* + * TODO: Initialize for all host binary machine types, not just + * those matching the perf binary. + */ + trace__init_syscalls_bpf_prog_array_maps(trace, EM_HOST); if (trace->ev_qualifier_ids.nr > 0) { err = trace__set_ev_qualifier_filter(trace); @@ -4749,7 +4705,7 @@ static int trace__replay(struct trace *trace) if (trace->opts.target.tid) symbol_conf.tid_list_str = strdup(trace->opts.target.tid); - if (symbol__init(&session->header.env) < 0) + if (symbol__init(perf_session__env(session)) < 0) goto out; trace->host = &session->machines.host; @@ -5358,6 +5314,7 @@ static int trace__config(const char *var, const char *value, void *arg) static void trace__exit(struct trace *trace) { + thread__zput(trace->current); strlist__delete(trace->ev_qualifier); zfree(&trace->ev_qualifier_ids.entries); if (trace->syscalls.table) { @@ -5368,24 +5325,13 @@ static void trace__exit(struct trace *trace) zfree(&trace->perfconfig_events); evlist__delete(trace->evlist); trace->evlist = NULL; + ordered_events__free(&trace->oe.data); #ifdef HAVE_LIBBPF_SUPPORT btf__free(trace->btf); trace->btf = NULL; #endif } -#ifdef HAVE_BPF_SKEL -static int bpf__setup_bpf_output(struct evlist *evlist) -{ - int err = parse_event(evlist, "bpf-output/no-inherit=1,name=__augmented_syscalls__/"); - - if (err) - pr_debug("ERROR: failed to create the \"__augmented_syscalls__\" bpf-output event\n"); - - return err; -} -#endif - int cmd_trace(int argc, const char **argv) { const char *trace_usage[] = { @@ -5398,7 +5344,6 @@ int cmd_trace(int argc, const char **argv) struct trace trace = { .opts = { .target = { - .uid = UINT_MAX, .uses_mmap = true, }, .user_freq = UINT_MAX, @@ -5445,8 +5390,7 @@ int cmd_trace(int argc, const char **argv) "child tasks do not inherit counters"), OPT_CALLBACK('m', "mmap-pages", &trace.opts.mmap_pages, "pages", "number of mmap data pages", evlist__parse_mmap_pages), - OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", - "user to profile"), + OPT_STRING('u', "uid", &trace.uid_str, "user", "user to profile"), OPT_CALLBACK(0, "duration", &trace, "float", "show only events with duration > N.M ms", trace__set_duration), @@ -5519,6 +5463,9 @@ int cmd_trace(int argc, const char **argv) sigchld_act.sa_sigaction = sighandler_chld; sigaction(SIGCHLD, &sigchld_act, NULL); + ordered_events__init(&trace.oe.data, ordered_events__deliver_event, &trace); + ordered_events__set_copy_on_queue(&trace.oe.data, true); + trace.evlist = evlist__new(); if (trace.evlist == NULL) { @@ -5581,7 +5528,6 @@ int cmd_trace(int argc, const char **argv) "cgroup monitoring only available in system-wide mode"); } -#ifdef HAVE_BPF_SKEL if (!trace.trace_syscalls) goto skip_augmentation; @@ -5600,42 +5546,17 @@ int cmd_trace(int argc, const char **argv) goto skip_augmentation; } - trace.skel = augmented_raw_syscalls_bpf__open(); - if (!trace.skel) { - pr_debug("Failed to open augmented syscalls BPF skeleton"); - } else { - /* - * Disable attaching the BPF programs except for sys_enter and - * sys_exit that tail call into this as necessary. - */ - struct bpf_program *prog; + err = augmented_syscalls__prepare(); + if (err < 0) + goto skip_augmentation; - bpf_object__for_each_program(prog, trace.skel->obj) { - if (prog != trace.skel->progs.sys_enter && prog != trace.skel->progs.sys_exit) - bpf_program__set_autoattach(prog, /*autoattach=*/false); - } + trace__add_syscall_newtp(&trace); - err = augmented_raw_syscalls_bpf__load(trace.skel); + err = augmented_syscalls__create_bpf_output(trace.evlist); + if (err == 0) + trace.syscalls.events.bpf_output = evlist__last(trace.evlist); - if (err < 0) { - libbpf_strerror(err, bf, sizeof(bf)); - pr_debug("Failed to load augmented syscalls BPF skeleton: %s\n", bf); - } else { - augmented_raw_syscalls_bpf__attach(trace.skel); - trace__add_syscall_newtp(&trace); - } - } - - err = bpf__setup_bpf_output(trace.evlist); - if (err) { - libbpf_strerror(err, bf, sizeof(bf)); - pr_err("ERROR: Setup BPF output event failed: %s\n", bf); - goto out; - } - trace.syscalls.events.bpf_output = evlist__last(trace.evlist); - assert(evsel__name_is(trace.syscalls.events.bpf_output, "__augmented_syscalls__")); skip_augmentation: -#endif err = -1; if (trace.trace_pgfaults) { @@ -5677,11 +5598,6 @@ int cmd_trace(int argc, const char **argv) trace__load_vmlinux_btf(&trace); } - if (trace.sort_events) { - ordered_events__init(&trace.oe.data, ordered_events__deliver_event, &trace); - ordered_events__set_copy_on_queue(&trace.oe.data, true); - } - /* * If we are augmenting syscalls, then combine what we put in the * __augmented_syscalls__ BPF map with what is in the @@ -5804,11 +5720,19 @@ int cmd_trace(int argc, const char **argv) goto out_close; } - err = target__parse_uid(&trace.opts.target); - if (err) { - target__strerror(&trace.opts.target, err, bf, sizeof(bf)); - fprintf(trace.output, "%s", bf); - goto out_close; + if (trace.uid_str) { + uid_t uid = parse_uid(trace.uid_str); + + if (uid == UINT_MAX) { + ui__error("Invalid User: %s", trace.uid_str); + err = -EINVAL; + goto out_close; + } + err = parse_uid_filter(trace.evlist, uid); + if (err) + goto out_close; + + trace.opts.target.system_wide = true; } if (!argc && target__none(&trace.opts.target)) @@ -5824,8 +5748,6 @@ int cmd_trace(int argc, const char **argv) fclose(trace.output); out: trace__exit(&trace); -#ifdef HAVE_BPF_SKEL - augmented_raw_syscalls_bpf__destroy(trace.skel); -#endif + augmented_syscalls__cleanup(); return err; } diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index e9fab20e9330..be519c433ce4 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -4,8 +4,7 @@ YELLOW='\033[0;33m' NC='\033[0m' # No Color -declare -a FILES -FILES=( +declare -a FILES=( "include/uapi/linux/const.h" "include/uapi/drm/drm.h" "include/uapi/drm/i915_drm.h" @@ -73,8 +72,7 @@ FILES=( "scripts/syscall.tbl" ) -declare -a SYNC_CHECK_FILES -SYNC_CHECK_FILES=( +declare -a SYNC_CHECK_FILES=( "arch/x86/include/asm/inat.h" "arch/x86/include/asm/insn.h" "arch/x86/lib/inat.c" @@ -86,8 +84,7 @@ SYNC_CHECK_FILES=( # tables that then gets included in .c files for things like id->string syscall # tables (and the reverse lookup as well: string -> id) -declare -a BEAUTY_FILES -BEAUTY_FILES=( +declare -a BEAUTY_FILES=( "arch/x86/include/asm/irq_vectors.h" "arch/x86/include/uapi/asm/prctl.h" "include/linux/socket.h" @@ -186,7 +183,7 @@ done # diff with extra ignore lines check arch/x86/lib/memcpy_64.S '-I "^EXPORT_SYMBOL" -I "^#include " -I"^SYM_FUNC_START\(_LOCAL\)*(memcpy_\(erms\|orig\))" -I"^#include "' check arch/x86/lib/memset_64.S '-I "^EXPORT_SYMBOL" -I "^#include " -I"^SYM_FUNC_START\(_LOCAL\)*(memset_\(erms\|orig\))"' -check arch/x86/include/asm/amd/ibs.h '-I "^#include [<\"]\(asm/\)*msr-index.h"' +check arch/x86/include/asm/amd/ibs.h '-I "^#include .*/msr-index.h"' check arch/arm64/include/asm/cputype.h '-I "^#include [<\"]\(asm/\)*sysreg.h"' check include/linux/unaligned.h '-I "^#include " -I "^#include " -I "^#pragma GCC diagnostic"' check include/uapi/asm-generic/mman.h '-I "^#include <\(uapi/\)*asm-generic/mman-common\(-tools\)*.h>"' diff --git a/tools/perf/include/perf/perf_dlfilter.h b/tools/perf/include/perf/perf_dlfilter.h index 16fc4568ac53..2d3540ed3c58 100644 --- a/tools/perf/include/perf/perf_dlfilter.h +++ b/tools/perf/include/perf/perf_dlfilter.h @@ -87,7 +87,7 @@ struct perf_dlfilter_al { __u8 is_64_bit; /* Only valid if dso is not NULL */ __u8 is_kernel_ip; /* True if in kernel space */ __u32 buildid_size; - __u8 *buildid; + const __u8 *buildid; /* Below members are only populated by resolve_ip() */ __u8 filtered; /* True if this sample event will be filtered out */ const char *comm; diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c index fcca275e5bf9..82514e6532b8 100644 --- a/tools/perf/jvmti/libjvmti.c +++ b/tools/perf/jvmti/libjvmti.c @@ -141,11 +141,11 @@ copy_class_filename(const char * class_sign, const char * file_name, char * resu * Assume path name is class hierarchy, this is a common practice with Java programs */ if (*class_sign == 'L') { - int j, i = 0; + size_t j, i = 0; char *p = strrchr(class_sign, '/'); if (p) { /* drop the 'L' prefix and copy up to the final '/' */ - for (i = 0; i < (p - class_sign); i++) + for (i = 0; i < (size_t)(p - class_sign); i++) result[i] = class_sign[i+1]; } /* diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh index 6ed7e52ab881..7977e9b0a5ea 100755 --- a/tools/perf/perf-archive.sh +++ b/tools/perf/perf-archive.sh @@ -16,6 +16,13 @@ while [ $# -gt 0 ] ; do elif [ $1 == "--unpack" ]; then UNPACK=1 shift + elif [ $1 == "--exclude-buildids" ]; then + EXCLUDE_BUILDIDS="$2" + if [ ! -e "$EXCLUDE_BUILDIDS" ]; then + echo "Provided exclude-buildids file $EXCLUDE_BUILDIDS does not exist" + exit 1 + fi + shift 2 else PERF_DATA=$1 UNPACK_TAR=$1 @@ -86,11 +93,29 @@ fi BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) -perf buildid-list -i $PERF_DATA --with-hits | grep -v "^ " > $BUILDIDS -if [ ! -s $BUILDIDS ] ; then - echo "perf archive: no build-ids found" - rm $BUILDIDS || true - exit 1 +# +# EXCLUDE_BUILDIDS is an optional file that contains build-ids to be excluded from the +# archive. It is a list of build-ids, one per line, without any leading or trailing spaces. +# If the file is empty, all build-ids will be included in the archive. To create a exclude- +# buildids file, you can use the following command: +# perf buildid-list -i perf.data --with-hits | grep -v "^ " > exclude_buildids.txt +# You can edit the file to remove the lines that you want to keep in the archive, then: +# perf archive --exclude-buildids exclude_buildids.txt +# +if [ -s "$EXCLUDE_BUILDIDS" ]; then + perf buildid-list -i $PERF_DATA --with-hits | grep -v "^ " | grep -Fv -f $EXCLUDE_BUILDIDS > $BUILDIDS + if [ ! -s "$BUILDIDS" ] ; then + echo "perf archive: no build-ids found after applying exclude-buildids file" + rm $BUILDIDS || true + exit 1 + fi +else + perf buildid-list -i $PERF_DATA --with-hits | grep -v "^ " > $BUILDIDS + if [ ! -s "$BUILDIDS" ] ; then + echo "perf archive: no build-ids found" + rm $BUILDIDS || true + exit 1 + fi fi MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index f0617cc41f5f..88c60ecf3395 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -346,12 +346,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) use_pager = 1; commit_pager_choice(); - perf_env__init(&perf_env); - perf_env__set_cmdline(&perf_env, argc, argv); status = p->fn(argc, argv); perf_config__exit(); exit_browser(status); - perf_env__exit(&perf_env); if (status) return status & 0xff; 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 e40be37addf8..2416d9f8a83d 100644 --- a/tools/perf/pmu-events/arch/arm64/common-and-microarch.json +++ b/tools/perf/pmu-events/arch/arm64/common-and-microarch.json @@ -1833,5 +1833,75 @@ "EventCode": "0x8324", "EventName": "L1I_CACHE_REFILL_PERCYC", "BriefDescription": "Level 1 instruction or unified cache refills in progress." + }, + { + "EventCode": "0x8431", + "EventName": "ASE_FP_VREDUCE_SPEC", + "BriefDescription": "Floating-point operation_speculatively_executed, Advanced SIMD pairwise or reduction." + }, + { + "EventCode": "0x8432", + "EventName": "SVE_FP_PREDUCE_SPEC", + "BriefDescription": "Floating-point operation_speculatively_executed, Advanced SIMD pairwise add step or pairwise reduce step." + }, + { + "EventCode": "0x8443", + "EventName": "ASE_FP_BF16_MIN_SPEC", + "BriefDescription": "Advanced SIMD data processing operation speculatively_executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x8444", + "EventName": "ASE_FP_FP8_MIN_SPEC", + "BriefDescription": "Advanced SIMD data processing operation speculatively_executed, smallest type is 8-bit floating-point." + }, + { + "EventCode": "0x844B", + "EventName": "ASE_SVE_FP_BF16_MIN_SPEC", + "BriefDescription": "Advanced SIMD data processing or SVE data processing operation speculatively_executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x844C", + "EventName": "ASE_SVE_FP_FP8_MIN_SPEC", + "BriefDescription": "Advanced SIMD data processing or SVE data processing operation speculatively_executed, smallest type is 8-bit floating-point." + }, + { + "EventCode": "0x8463", + "EventName": "SVE_FP_BF16_MIN_SPEC", + "BriefDescription": "SVE data processing operation speculatively_executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x8464", + "EventName": "SVE_FP_FP8_MIN_SPEC", + "BriefDescription": "SVE data processing operation speculatively_executed, smallest type is 8-bit floating-point." + }, + { + "EventCode": "0x8473", + "EventName": "FP_BF16_MIN_SPEC", + "BriefDescription": "Floating-point operation speculatively_executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x8474", + "EventName": "FP_FP8_MIN_SPEC", + "BriefDescription": "Floating-point operation speculatively_executed, smallest type is 8-bit floating-point." + }, + { + "EventCode": "0x8483", + "EventName": "FP_BF16_FIXED_MIN_OPS_SPEC", + "BriefDescription": "Non-scalable element arithmetic operations speculatively executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x8484", + "EventName": "FP_FP8_FIXED_MIN_OPS_SPEC", + "BriefDescription": "Non-scalable element arithmetic operations speculatively executed, smallest type is 8-bit floating-point." + }, + { + "EventCode": "0x848B", + "EventName": "FP_BF16_SCALE_MIN_OPS_SPEC", + "BriefDescription": "Scalable element arithmetic operations speculatively executed, smallest type is BFloat16 floating-point." + }, + { + "EventCode": "0x848C", + "EventName": "FP_FP8_SCALE_MIN_OPS_SPEC", + "BriefDescription": "Scalable element arithmetic operations speculatively executed, smallest type is 8-bit floating-point." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/core-imp-def.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/core-imp-def.json index 52f5ca1482fe..57a854ff5033 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/core-imp-def.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/core-imp-def.json @@ -1,6 +1,6 @@ [ { "ArchStdEvent": "L1I_CACHE_PRF", - "BriefDescription": "This event counts fetch counted by either Level 1 instruction hardware prefetch or Level 1 instruction software prefetch." + "BriefDescription": "This event counts L1I_CACHE caused by hardware prefetch or software prefetch." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/cycle_accounting.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/cycle_accounting.json index 24ff5d8dbb98..84374adbb0f8 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/cycle_accounting.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/cycle_accounting.json @@ -12,12 +12,12 @@ { "EventCode": "0x0184", "EventName": "LD_COMP_WAIT", - "BriefDescription": "This event counts every cycle that no instruction was committed because the oldest and uncommitted load/store/prefetch operation waits for L1D cache, L2 cache and memory access." + "BriefDescription": "This event counts every cycle that no instruction was committed because the oldest and uncommitted load/store/prefetch operation waits for L1D cache, L2 cache, L3 cache and memory access." }, { "EventCode": "0x0185", "EventName": "LD_COMP_WAIT_EX", - "BriefDescription": "This event counts every cycle that no instruction was committed because the oldest and uncommitted integer load operation waits for L1D cache, L2 cache and memory access." + "BriefDescription": "This event counts every cycle that no instruction was committed because the oldest and uncommitted integer load operation waits for L1D cache, L2 cache, L3 cache and memory access." }, { "EventCode": "0x0186", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/exception.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/exception.json index f231712fe261..fba66bbcfeb5 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/exception.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/exception.json @@ -33,7 +33,7 @@ }, { "ArchStdEvent": "EXC_SMC", - "BriefDescription": "This event counts only Secure Monitor Call exceptions. The counter does not increment on SMC instructions trapped as a Hyp Trap exception." + "BriefDescription": "This event counts only Secure Monitor Call exceptions. This event does not increment on SMC instructions trapped as a Hyp Trap exception." }, { "ArchStdEvent": "EXC_HVC", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/fp_operation.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/fp_operation.json index a3c368959199..2ffdc16530dd 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/fp_operation.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/fp_operation.json @@ -2,7 +2,7 @@ { "EventCode": "0x0105", "EventName": "FP_MV_SPEC", - "BriefDescription": "This event counts architecturally executed floating-point move operations." + "BriefDescription": "This event counts architecturally executed floating-point move operation." }, { "EventCode": "0x0112", @@ -24,7 +24,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point operation." }, { "ArchStdEvent": "FP_HP_SPEC", @@ -40,7 +40,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_HP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE half-precision floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE half-precision floating-point operation." }, { "ArchStdEvent": "FP_SP_SPEC", @@ -56,7 +56,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_SP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE single-precision floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE single-precision floating-point operation." }, { "ArchStdEvent": "FP_DP_SPEC", @@ -72,7 +72,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_DP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE double-precision floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE double-precision floating-point operation." }, { "ArchStdEvent": "FP_DIV_SPEC", @@ -88,7 +88,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_DIV_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point divide operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point divide operation." }, { "ArchStdEvent": "FP_SQRT_SPEC", @@ -104,7 +104,7 @@ }, { "ArchStdEvent": "ASE_SVE_FP_SQRT_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point square root operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point square root operation." }, { "ArchStdEvent": "ASE_FP_FMA_SPEC", @@ -116,11 +116,11 @@ }, { "ArchStdEvent": "ASE_SVE_FP_FMA_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point FMA operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point FMA operation." }, { "ArchStdEvent": "FP_MUL_SPEC", - "BriefDescription": "This event counts architecturally executed floating-point multiply operations." + "BriefDescription": "This event counts architecturally executed floating-point multiply operation." }, { "ArchStdEvent": "ASE_FP_MUL_SPEC", @@ -132,11 +132,11 @@ }, { "ArchStdEvent": "ASE_SVE_FP_MUL_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point multiply operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point multiply operation." }, { "ArchStdEvent": "FP_ADDSUB_SPEC", - "BriefDescription": "This event counts architecturally executed floating-point add or subtract operations." + "BriefDescription": "This event counts architecturally executed floating-point add or subtract operation." }, { "ArchStdEvent": "ASE_FP_ADDSUB_SPEC", @@ -148,19 +148,19 @@ }, { "ArchStdEvent": "ASE_SVE_FP_ADDSUB_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point add or subtract operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point add or subtract operation." }, { "ArchStdEvent": "ASE_FP_RECPE_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD floating-point reciprocal estimate operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD floating-point reciprocal estimate operation." }, { "ArchStdEvent": "SVE_FP_RECPE_SPEC", - "BriefDescription": "This event counts architecturally executed SVE floating-point reciprocal estimate operations." + "BriefDescription": "This event counts architecturally executed SVE floating-point reciprocal estimate operation." }, { "ArchStdEvent": "ASE_SVE_FP_RECPE_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point reciprocal estimate operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point reciprocal estimate operation." }, { "ArchStdEvent": "ASE_FP_CVT_SPEC", @@ -172,15 +172,15 @@ }, { "ArchStdEvent": "ASE_SVE_FP_CVT_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point convert operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point convert operation." }, { "ArchStdEvent": "SVE_FP_AREDUCE_SPEC", - "BriefDescription": "This event counts architecturally executed SVE floating-point accumulating reduction operations." + "BriefDescription": "This event counts architecturally executed SVE floating-point accumulating reduction operation." }, { "ArchStdEvent": "ASE_FP_PREDUCE_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD floating-point pairwise add step operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD floating-point pairwise add step operation." }, { "ArchStdEvent": "SVE_FP_VREDUCE_SPEC", @@ -188,15 +188,15 @@ }, { "ArchStdEvent": "ASE_SVE_FP_VREDUCE_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE floating-point vector reduction operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE floating-point vector reduction operation." }, { "ArchStdEvent": "FP_SCALE_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed SVE arithmetic operations. See FP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by (128 / CSIZE) and by twice that amount for operations that would also be counted by SVE_FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed SVE arithmetic operation. See FP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by (128 / CSIZE) and by twice that amount for operations that would also be counted by SVE_FP_FMA_SPEC." }, { "ArchStdEvent": "FP_FIXED_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed v8SIMD&FP arithmetic operations. See FP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. The event counter is incremented by the specified number of elements for Advanced SIMD operations or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed v8SIMD&FP arithmetic operation. See FP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by the specified number of elements for Advanced SIMD operations or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." }, { "ArchStdEvent": "ASE_SVE_FP_DOT_SPEC", @@ -205,5 +205,61 @@ { "ArchStdEvent": "ASE_SVE_FP_MMLA_SPEC", "BriefDescription": "This event counts architecturally executed microarchitectural Advanced SIMD or SVE floating-point matrix multiply operation." + }, + { + "ArchStdEvent": "ASE_FP_VREDUCE_SPEC", + "BriefDescription": "This event counts architecturally executed Advanced SIMD floating-point vector reduction operation." + }, + { + "ArchStdEvent": "SVE_FP_PREDUCE_SPEC", + "BriefDescription": "This event counts architecturally executed SVE floating-point pairwise add step operation." + }, + { + "ArchStdEvent": "ASE_FP_BF16_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed Advanced SIMD data processing operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "ASE_FP_FP8_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed Advanced SIMD data processing operations, smallest type is 8-bit floating-point." + }, + { + "ArchStdEvent": "ASE_SVE_FP_BF16_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed Advanced SIMD data processing or SVE data processing operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "ASE_SVE_FP_FP8_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed Advanced SIMD data processing or SVE data processing operations, smallest type is 8-bit floating-point." + }, + { + "ArchStdEvent": "SVE_FP_BF16_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed SVE data processing operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "SVE_FP_FP8_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed SVE data processing operations, smallest type is 8-bit floating-point." + }, + { + "ArchStdEvent": "FP_BF16_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed data processing operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "FP_FP8_MIN_SPEC", + "BriefDescription": "This event counts architecturally executed data processing operations, smallest type is 8-bit floating-point." + }, + { + "ArchStdEvent": "FP_BF16_FIXED_MIN_OPS_SPEC", + "BriefDescription": "This event counts architecturally executed non-scalable element arithmetic operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "FP_FP8_FIXED_MIN_OPS_SPEC", + "BriefDescription": "This event counts architecturally executed non-scalable element arithmetic operations, smallest type is 8-bit floating-point." + }, + { + "ArchStdEvent": "FP_BF16_SCALE_MIN_OPS_SPEC", + "BriefDescription": "This event counts architecturally executed scalable element arithmetic operations, smallest type is BFloat16 floating-point." + }, + { + "ArchStdEvent": "FP_FP8_SCALE_MIN_OPS_SPEC", + "BriefDescription": "This event counts architecturally executed scalable element arithmetic operations, smallest type is 8-bit floating-point." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1d_cache.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1d_cache.json index b0818a2fedb0..a2ff3b49ac0d 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1d_cache.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1d_cache.json @@ -72,11 +72,11 @@ }, { "ArchStdEvent": "L1D_CACHE_HWPRF", - "BriefDescription": "This event counts access counted by L1D_CACHE that is due to a hardware prefetch." + "BriefDescription": "This event counts L1D_CACHE caused by hardware prefetch." }, { "ArchStdEvent": "L1D_CACHE_REFILL_HWPRF", - "BriefDescription": "This event counts hardware prefetch counted by L1D_CACHE_HWPRF that causes a refill of the Level 1 data cache from outside of the Level 1 data cache." + "BriefDescription": "This event counts L1D_CACHE_REFILL caused by hardware prefetch." }, { "ArchStdEvent": "L1D_CACHE_HIT_RD", @@ -100,14 +100,14 @@ }, { "ArchStdEvent": "L1D_CACHE_PRF", - "BriefDescription": "This event counts fetch counted by either Level 1 data hardware prefetch or Level 1 data software prefetch." + "BriefDescription": "This event counts L1D_CACHE caused by hardware prefetch or software prefetch." }, { "ArchStdEvent": "L1D_CACHE_REFILL_PRF", - "BriefDescription": "This event counts hardware prefetch counted by L1D_CACHE_PRF that causes a refill of the Level 1 data cache from outside of the Level 1 data cache." + "BriefDescription": "This event counts L1D_CACHE_REFILL caused by hardware prefetch or software prefetch." }, { "ArchStdEvent": "L1D_CACHE_REFILL_PERCYC", - "BriefDescription": "The counter counts by the number of cache refills counted by L1D_CACHE_REFILL in progress on each Processor cycle." + "BriefDescription": "This counter counts by the number of cache refills counted by L1D_CACHE_REFILL in progress on each Processor cycle." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1i_cache.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1i_cache.json index 8680d8ec461d..5250af8631c0 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1i_cache.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l1i_cache.json @@ -23,11 +23,11 @@ }, { "ArchStdEvent": "L1I_CACHE_HWPRF", - "BriefDescription": "This event counts access counted by L1I_CACHE that is due to a hardware prefetch." + "BriefDescription": "This event counts L1I_CACHE caused by hardware prefetch." }, { "ArchStdEvent": "L1I_CACHE_REFILL_HWPRF", - "BriefDescription": "This event counts hardware prefetch counted by L1I_CACHE_HWPRF that causes a refill of the Level 1 instruction cache from outside of the Level 1 instruction cache." + "BriefDescription": "This event counts L1I_CACHE_REFILL caused by hardware prefetch." }, { "ArchStdEvent": "L1I_CACHE_HIT_RD", @@ -43,10 +43,10 @@ }, { "ArchStdEvent": "L1I_CACHE_REFILL_PRF", - "BriefDescription": "This event counts hardware prefetch counted by L1I_CACHE_PRF that causes a refill of the Level 1 instruction cache from outside of the Level 1 instruction cache." + "BriefDescription": "This event counts L1I_CACHE_REFILL caused by hardware prefetch or software prefetch." }, { "ArchStdEvent": "L1I_CACHE_REFILL_PERCYC", - "BriefDescription": "The counter counts by the number of cache refills counted by L1I_CACHE_REFILL in progress on each Processor cycle." + "BriefDescription": "This counter counts by the number of cache refills counted by L1I_CACHE_REFILL in progress on each Processor cycle." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l2_cache.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l2_cache.json index 9e092752e6db..67f9151d7685 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l2_cache.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l2_cache.json @@ -21,19 +21,19 @@ }, { "ArchStdEvent": "L2D_CACHE_RD", - "BriefDescription": "This event counts L2D CACHE caused by read access." + "BriefDescription": "This event counts L2D_CACHE caused by read access." }, { "ArchStdEvent": "L2D_CACHE_WR", - "BriefDescription": "This event counts L2D CACHE caused by write access." + "BriefDescription": "This event counts L2D_CACHE caused by write access." }, { "ArchStdEvent": "L2D_CACHE_REFILL_RD", - "BriefDescription": "This event counts L2D CACHE_REFILL caused by read access." + "BriefDescription": "This event counts L2D_CACHE_REFILL caused by read access." }, { "ArchStdEvent": "L2D_CACHE_REFILL_WR", - "BriefDescription": "This event counts L2D CACHE_REFILL caused by write access." + "BriefDescription": "This event counts L2D_CACHE_REFILL caused by write access." }, { "ArchStdEvent": "L2D_CACHE_WB_VICTIM", @@ -57,7 +57,7 @@ { "EventCode": "0x0305", "EventName": "L2D_CACHE_HWPRF_ADJACENT", - "BriefDescription": "This event counts L2D_CACHE caused by hardware adjacent prefetch access." + "BriefDescription": "This event counts L2D_CACHE caused by hardware adjacent prefetch." }, { "EventCode": "0x0308", @@ -111,7 +111,7 @@ }, { "ArchStdEvent": "L2D_CACHE_LMISS_RD", - "BriefDescription": "This event counts operations that cause a refill of the L2D cache that incurs additional latency." + "BriefDescription": "This event counts operations that cause a refill of the L2 cache that incurs additional latency." }, { "ArchStdEvent": "L2D_CACHE_MISS", @@ -119,23 +119,23 @@ }, { "ArchStdEvent": "L2D_CACHE_HWPRF", - "BriefDescription": "This event counts access counted by L2D_CACHE that is due to a hardware prefetch." + "BriefDescription": "This event counts L2D_CACHE caused by hardware prefetch." }, { "ArchStdEvent": "L2D_CACHE_REFILL_HWPRF", - "BriefDescription": "This event counts hardware prefetch counted by L2D_CACHE_HWPRF that causes a refill of the Level 2 cache, or any Level 1 data and instruction cache of this PE, from outside of those caches." + "BriefDescription": "This event counts L2D_CACHE_REFILL caused by hardware prefetch." }, { "ArchStdEvent": "L2D_CACHE_HIT_RD", - "BriefDescription": "This event counts demand read counted by L2D_CACHE_RD that hits in the Level 2 data cache." + "BriefDescription": "This event counts demand read counted by L2D_CACHE_RD that hits in the Level 2 cache." }, { "ArchStdEvent": "L2D_CACHE_HIT_WR", - "BriefDescription": "This event counts demand write counted by L2D_CACHE_WR that hits in the Level 2 data cache." + "BriefDescription": "This event counts demand write counted by L2D_CACHE_WR that hits in the Level 2 cache." }, { "ArchStdEvent": "L2D_CACHE_HIT", - "BriefDescription": "This event counts access counted by L2D_CACHE that hits in the Level 2 data cache." + "BriefDescription": "This event counts access counted by L2D_CACHE that hits in the Level 2 cache." }, { "ArchStdEvent": "L2D_LFB_HIT_RD", @@ -147,14 +147,14 @@ }, { "ArchStdEvent": "L2D_CACHE_PRF", - "BriefDescription": "This event counts fetch counted by either Level 2 data hardware prefetch or Level 2 data software prefetch." + "BriefDescription": "This event counts L2D_CACHE caused by hardware prefetch or software prefetch." }, { "ArchStdEvent": "L2D_CACHE_REFILL_PRF", - "BriefDescription": "This event counts hardware prefetch counted by L2D_CACHE_PRF that causes a refill of the Level 2 data cache from outside of the Level 1 data cache." + "BriefDescription": "This event counts L2D_CACHE_REFILL caused by hardware prefetch or software prefetch." }, { "ArchStdEvent": "L2D_CACHE_REFILL_PERCYC", - "BriefDescription": "The counter counts by the number of cache refills counted by L2D_CACHE_REFILL in progress on each Processor cycle." + "BriefDescription": "This counter counts by the number of cache refills counted by L2D_CACHE_REFILL in progress on each Processor cycle." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l3_cache.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l3_cache.json index 3f3e0d22ac68..cf49c4d452b7 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l3_cache.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/l3_cache.json @@ -30,17 +30,17 @@ { "EventCode": "0x0394", "EventName": "L2D_CACHE_REFILL_L3D_CACHE_PRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_CACHE caused by prefetch access." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_CACHE caused by hardware prefetch or software prefetch." }, { "EventCode": "0x0395", "EventName": "L2D_CACHE_REFILL_L3D_CACHE_HWPRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_CACHE caused by hardware prefetch access." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_CACHE caused by hardware prefetch." }, { "EventCode": "0x0396", "EventName": "L2D_CACHE_REFILL_L3D_MISS", - "BriefDescription": "This event counts operations that cause a miss of the L3 cache." + "BriefDescription": "This event counts operations that cause a miss of the L3 cache. Note: This event may count inaccurately." }, { "EventCode": "0x0397", @@ -60,17 +60,17 @@ { "EventCode": "0x039A", "EventName": "L2D_CACHE_REFILL_L3D_MISS_PRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS caused by prefetch access." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS caused by hardware prefetch or software prefetch. Note: This event may count inaccurately." }, { "EventCode": "0x039B", "EventName": "L2D_CACHE_REFILL_L3D_MISS_HWPRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS caused by hardware prefetch access." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS caused by hardware prefetch. Note: This event may count inaccurately." }, { "EventCode": "0x039C", "EventName": "L2D_CACHE_REFILL_L3D_HIT", - "BriefDescription": "This event counts operations that cause a hit of the L3 cache." + "BriefDescription": "This event counts operations that cause a hit of the L3 cache. Note: This event may count inaccurately." }, { "EventCode": "0x039D", @@ -90,70 +90,65 @@ { "EventCode": "0x03A0", "EventName": "L2D_CACHE_REFILL_L3D_HIT_PRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_HIT caused by prefetch access." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_HIT caused by hardware prefetch or software prefetch. Note: This event may count inaccurately." }, { "EventCode": "0x03A1", "EventName": "L2D_CACHE_REFILL_L3D_HIT_HWPRF", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_HIT caused by hardware prefetch access." - }, - { - "EventCode": "0x03A2", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT", - "BriefDescription": "This event counts the number of L3 cache misses where the requests hit the PFTGT buffer." + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_HIT caused by hardware prefetch. Note: This event may count inaccurately." }, { "EventCode": "0x03A3", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT_DM", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT caused by demand access." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_PFTGT_HIT", + "BriefDescription": "This event counts the number of L3 cache misses caused by demand access where the requests hit the PFTGT buffer." }, { "EventCode": "0x03A4", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT_DM_RD", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT caused by demand read access." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_RD_PFTGT_HIT", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM_PFTGT_HIT caused by read access." }, { "EventCode": "0x03A5", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT_DM_WR", - "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_PFTGT_HIT caused by demand write access." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_WR_PFTGT_HIT", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM_PFTGT_HIT caused by write access." }, { "EventCode": "0x03A6", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_L_MEM", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access the memory in the same socket as the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_L_MEM", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access the memory in the same socket as the requests." }, { "EventCode": "0x03A7", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_FR_MEM", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access the memory in the different socket from the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_FR_MEM", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access the memory in the different socket from the requests." }, { "EventCode": "0x03A8", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_L_L2", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access the different L2 cache from the requests in the same Numa nodes as the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_L_L2", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access the different L2 cache from the requests in the same Numa nodes as the requests." }, { "EventCode": "0x03A9", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_NR_L2", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access L2 cache in the different Numa nodes from the requests in the same socket as the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_NR_L2", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access L2 cache in the different Numa nodes from the requests in the same socket as the requests." }, { "EventCode": "0x03AA", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_NR_L3", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access L3 cache in the different Numa nodes from the requests in the same socket as the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_NR_L3", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access L3 cache in the different Numa nodes from the requests in the same socket as the requests." }, { "EventCode": "0x03AB", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_FR_L2", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access L2 cache in the different socket from the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_FR_L2", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access L2 cache in the different socket from the requests." }, { "EventCode": "0x03AC", - "EventName": "L2D_CACHE_REFILL_L3D_MISS_FR_L3", - "BriefDescription": "This event counts the number of L3 cache misses where the requests access L3 cache in the different socket from the requests." + "EventName": "L2D_CACHE_REFILL_L3D_MISS_DM_FR_L3", + "BriefDescription": "This event counts L2D_CACHE_REFILL_L3D_MISS_DM where the requests access L3 cache in the different socket from the requests." }, { "ArchStdEvent": "L3D_CACHE_LMISS_RD", - "BriefDescription": "This event counts access counted by L3D_CACHE that is not completed by the L3D cache, and a Memory-read operation, as defined by the L2D_CACHE_REFILL_L3D_MISS events." + "BriefDescription": "This event counts access counted by L3D_CACHE that is not completed by the L3 cache, and a Memory-read operation, as defined by the L2D_CACHE_REFILL_L3D_MISS events. Note: This event may count inaccurately." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/ll_cache.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/ll_cache.json index a441b84729ab..d49d9f6df72c 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/ll_cache.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/ll_cache.json @@ -5,6 +5,6 @@ }, { "ArchStdEvent": "LL_CACHE_MISS_RD", - "BriefDescription": "This event counts access counted by L3D_CACHE that is not completed by the L3D cache, and a Memory-read operation, as defined by the L2D_CACHE_REFILL_L3D_MISS events." + "BriefDescription": "This event counts access counted by L3D_CACHE that is not completed by the L3 cache, and a Memory-read operation, as defined by the L2D_CACHE_REFILL_L3D_MISS events. Note: This event may count inaccurately." } ] diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/pipeline.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/pipeline.json index 3cc3105f4a5e..15cf54730b85 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/pipeline.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/pipeline.json @@ -147,17 +147,17 @@ { "EventCode": "0x02B0", "EventName": "L1_PIPE_COMP_GATHER_2FLOW", - "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 2 flows because 2 elements could not be combined." + "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 2-flows because 2 elements could not be combined." }, { "EventCode": "0x02B1", "EventName": "L1_PIPE_COMP_GATHER_1FLOW", - "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 1 flow because 2 elements could be combined." + "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 1-flow because 2 elements could be combined." }, { "EventCode": "0x02B2", "EventName": "L1_PIPE_COMP_GATHER_0FLOW", - "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 0 flow because both predicate values are 0." + "BriefDescription": "This event counts the number of times where 2 elements of the gather instructions became 0-flow because both predicate values are 0." }, { "EventCode": "0x02B3", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/spec_operation.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/spec_operation.json index 4841b43e2871..1caf3baeae4e 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/spec_operation.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/spec_operation.json @@ -81,7 +81,7 @@ }, { "ArchStdEvent": "CSDB_SPEC", - "BriefDescription": "This event counts speculatively executed control speculation barrier instructions." + "BriefDescription": "This event counts architecturally executed control speculation barrier instructions." }, { "EventCode": "0x0108", @@ -91,17 +91,17 @@ { "EventCode": "0x0109", "EventName": "IEL_SPEC", - "BriefDescription": "This event counts architecturally executed inter-element manipulation operations." + "BriefDescription": "This event counts architecturally executed inter-element manipulation operation." }, { "EventCode": "0x010A", "EventName": "IREG_SPEC", - "BriefDescription": "This event counts architecturally executed inter-register manipulation operations." + "BriefDescription": "This event counts architecturally executed inter-register manipulation operation." }, { "EventCode": "0x011A", "EventName": "BC_LD_SPEC", - "BriefDescription": "This event counts architecturally executed SIMD broadcast floating-point load operations." + "BriefDescription": "This event counts architecturally executed SIMD broadcast floating-point load operation." }, { "EventCode": "0x011B", @@ -130,7 +130,7 @@ }, { "ArchStdEvent": "ASE_INST_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD operation." }, { "ArchStdEvent": "INT_SPEC", @@ -158,7 +158,7 @@ }, { "ArchStdEvent": "NONFP_SPEC", - "BriefDescription": "This event counts architecturally executed non-floating-point operations." + "BriefDescription": "This event counts architecturally executed non-floating-point operation." }, { "ArchStdEvent": "INT_SCALE_OPS_SPEC", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/stall.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/stall.json index 5fb81e2a0a07..e1e16d513828 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/stall.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/stall.json @@ -5,7 +5,7 @@ }, { "ArchStdEvent": "STALL_BACKEND", - "BriefDescription": "This event counts every cycle counted by the CPU_CYCLES event on that no operation was issued because the backend is unable to accept any operations." + "BriefDescription": "This event counts every cycle counted by the CPU_CYCLES event on that no operation was issued because the backend is unable to accept any operation." }, { "ArchStdEvent": "STALL", @@ -69,7 +69,7 @@ }, { "ArchStdEvent": "STALL_BACKEND_L2D", - "BriefDescription": "This event counts every cycle counted by STALL_BACKEND_MEMBOUND when there is a demand data miss in L2D cache." + "BriefDescription": "This event counts every cycle counted by STALL_BACKEND_MEMBOUND when there is a demand data miss in L2 cache." }, { "ArchStdEvent": "STALL_BACKEND_CPUBOUND", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/sve.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/sve.json index e66b5af00f90..88cab0caf49e 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/sve.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/sve.json @@ -13,11 +13,11 @@ }, { "ArchStdEvent": "ASE_SVE_INST_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE operation." }, { "ArchStdEvent": "UOP_SPEC", - "BriefDescription": "This event counts all architecturally executed micro-operations." + "BriefDescription": "This event counts all architecturally executed micro-operation." }, { "ArchStdEvent": "SVE_MATH_SPEC", @@ -29,7 +29,7 @@ }, { "ArchStdEvent": "FP_FMA_SPEC", - "BriefDescription": "This event counts architecturally executed floating-point fused multiply-add and multiply-subtract operations." + "BriefDescription": "This event counts architecturally executed floating-point fused multiply-add and multiply-subtract operation." }, { "ArchStdEvent": "FP_RECPE_SPEC", @@ -41,15 +41,15 @@ }, { "ArchStdEvent": "ASE_INT_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD integer operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD integer operation." }, { "ArchStdEvent": "SVE_INT_SPEC", - "BriefDescription": "This event counts architecturally executed SVE integer operations." + "BriefDescription": "This event counts architecturally executed SVE integer operation." }, { "ArchStdEvent": "ASE_SVE_INT_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE integer operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE integer operation." }, { "ArchStdEvent": "SVE_INT_DIV_SPEC", @@ -69,7 +69,7 @@ }, { "ArchStdEvent": "ASE_SVE_INT_MUL_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE integer multiply operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE integer multiply operation." }, { "ArchStdEvent": "SVE_INT_MUL64_SPEC", @@ -77,19 +77,19 @@ }, { "ArchStdEvent": "SVE_INT_MULH64_SPEC", - "BriefDescription": "This event counts architecturally executed SVE integer 64-bit x 64-bit multiply returning high part operations." + "BriefDescription": "This event counts architecturally executed SVE integer 64-bit x 64-bit multiply returning high part operation." }, { "ArchStdEvent": "ASE_NONFP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD non-floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD non-floating-point operation." }, { "ArchStdEvent": "SVE_NONFP_SPEC", - "BriefDescription": "This event counts architecturally executed SVE non-floating-point operations." + "BriefDescription": "This event counts architecturally executed SVE non-floating-point operation." }, { "ArchStdEvent": "ASE_SVE_NONFP_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE non-floating-point operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE non-floating-point operation." }, { "ArchStdEvent": "ASE_INT_VREDUCE_SPEC", @@ -101,7 +101,7 @@ }, { "ArchStdEvent": "ASE_SVE_INT_VREDUCE_SPEC", - "BriefDescription": "This event counts architecturally executed Advanced SIMD and SVE integer reduction operations." + "BriefDescription": "This event counts architecturally executed Advanced SIMD or SVE integer reduction operation." }, { "ArchStdEvent": "SVE_PERM_SPEC", @@ -149,11 +149,11 @@ }, { "ArchStdEvent": "ASE_SVE_LD_SPEC", - "BriefDescription": "This event counts architecturally executed operations that read from memory due to SVE and Advanced SIMD load instructions." + "BriefDescription": "This event counts architecturally executed operations that read from memory due to Advanced SIMD or SVE load instructions." }, { "ArchStdEvent": "ASE_SVE_ST_SPEC", - "BriefDescription": "This event counts architecturally executed operations that write to memory due to SVE and Advanced SIMD store instructions." + "BriefDescription": "This event counts architecturally executed operations that write to memory due to Advanced SIMD or SVE store instructions." }, { "ArchStdEvent": "PRF_SPEC", @@ -197,11 +197,11 @@ }, { "ArchStdEvent": "ASE_SVE_LD_MULTI_SPEC", - "BriefDescription": "This event counts architecturally executed operations that read from memory due to SVE and Advanced SIMD multiple vector contiguous structure load instructions." + "BriefDescription": "This event counts architecturally executed operations that read from memory due to Advanced SIMD or SVE multiple vector contiguous structure load instructions." }, { "ArchStdEvent": "ASE_SVE_ST_MULTI_SPEC", - "BriefDescription": "This event counts architecturally executed operations that write to memory due to SVE and Advanced SIMD multiple vector contiguous structure store instructions." + "BriefDescription": "This event counts architecturally executed operations that write to memory due to Advanced SIMD or SVE multiple vector contiguous structure store instructions." }, { "ArchStdEvent": "SVE_LD_GATHER_SPEC", @@ -221,27 +221,27 @@ }, { "ArchStdEvent": "FP_HP_SCALE_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed SVE half-precision arithmetic operations. See FP_HP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 8, or by 16 for operations that would also be counted by SVE_FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed SVE half-precision arithmetic operation. See FP_HP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 8, or by 16 for operations that would also be counted by SVE_FP_FMA_SPEC." }, { "ArchStdEvent": "FP_HP_FIXED_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed v8SIMD&FP half-precision arithmetic operations. See FP_HP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by the number of 16-bit elements for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed v8SIMD&FP half-precision arithmetic operation. See FP_HP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by the number of 16-bit elements for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." }, { "ArchStdEvent": "FP_SP_SCALE_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed SVE single-precision arithmetic operations. See FP_SP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 4, or by 8 for operations that would also be counted by SVE_FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed SVE single-precision arithmetic operation. See FP_SP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 4, or by 8 for operations that would also be counted by SVE_FP_FMA_SPEC." }, { "ArchStdEvent": "FP_SP_FIXED_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed v8SIMD&FP single-precision arithmetic operations. See FP_SP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by the number of 32-bit elements for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed v8SIMD&FP single-precision arithmetic operation. See FP_SP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by the number of 32-bit elements for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." }, { "ArchStdEvent": "FP_DP_SCALE_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed SVE double-precision arithmetic operations. See FP_DP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 2, or by 4 for operations that would also be counted by SVE_FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed SVE double-precision arithmetic operation. See FP_DP_SCALE_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 2, or by 4 for operations that would also be counted by SVE_FP_FMA_SPEC." }, { "ArchStdEvent": "FP_DP_FIXED_OPS_SPEC", - "BriefDescription": "This event counts architecturally executed v8SIMD&FP double-precision arithmetic operations. See FP_DP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 2 for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." + "BriefDescription": "This event counts architecturally executed v8SIMD&FP double-precision arithmetic operation. See FP_DP_FIXED_OPS_SPEC of ARMv9 Reference Manual for more information. This event counter is incremented by 2 for Advanced SIMD operations, or by 1 for scalar operations, and by twice those amounts for operations that would also be counted by FP_FMA_SPEC." }, { "ArchStdEvent": "ASE_SVE_INT_DOT_SPEC", diff --git a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/tlb.json b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/tlb.json index edc7cb8696c8..f54029ba369a 100644 --- a/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/tlb.json +++ b/tools/perf/pmu-events/arch/arm64/fujitsu/monaka/tlb.json @@ -104,72 +104,72 @@ { "EventCode": "0x0C10", "EventName": "L1I_TLB_REFILL_4K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 4KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 4KB page." }, { "EventCode": "0x0C11", "EventName": "L1I_TLB_REFILL_64K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 64KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 64KB page." }, { "EventCode": "0x0C12", "EventName": "L1I_TLB_REFILL_2M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 2MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 2MB page." }, { "EventCode": "0x0C13", "EventName": "L1I_TLB_REFILL_32M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 32MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 32MB page." }, { "EventCode": "0x0C14", "EventName": "L1I_TLB_REFILL_512M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 512MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 512MB page." }, { "EventCode": "0x0C15", "EventName": "L1I_TLB_REFILL_1G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 1GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 1GB page." }, { "EventCode": "0x0C16", "EventName": "L1I_TLB_REFILL_16G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1I in 16GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1I in 16GB page." }, { "EventCode": "0x0C18", "EventName": "L1D_TLB_REFILL_4K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 4KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 4KB page." }, { "EventCode": "0x0C19", "EventName": "L1D_TLB_REFILL_64K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 64KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 64KB page." }, { "EventCode": "0x0C1A", "EventName": "L1D_TLB_REFILL_2M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 2MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 2MB page." }, { "EventCode": "0x0C1B", "EventName": "L1D_TLB_REFILL_32M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 32MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 32MB page." }, { "EventCode": "0x0C1C", "EventName": "L1D_TLB_REFILL_512M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 512MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 512MB page." }, { "EventCode": "0x0C1D", "EventName": "L1D_TLB_REFILL_1G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 1GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 1GB page." }, { "EventCode": "0x0C1E", "EventName": "L1D_TLB_REFILL_16G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L1D in 16GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L1D in 16GB page." }, { "EventCode": "0x0C20", @@ -244,72 +244,72 @@ { "EventCode": "0x0C30", "EventName": "L2I_TLB_REFILL_4K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2Iin 4KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 4KB page." }, { "EventCode": "0x0C31", "EventName": "L2I_TLB_REFILL_64K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 64KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 64KB page." }, { "EventCode": "0x0C32", "EventName": "L2I_TLB_REFILL_2M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 2MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 2MB page." }, { "EventCode": "0x0C33", "EventName": "L2I_TLB_REFILL_32M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 32MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 32MB page." }, { "EventCode": "0x0C34", "EventName": "L2I_TLB_REFILL_512M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 512MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 512MB page." }, { "EventCode": "0x0C35", "EventName": "L2I_TLB_REFILL_1G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 1GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 1GB page." }, { "EventCode": "0x0C36", "EventName": "L2I_TLB_REFILL_16G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2I in 16GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2I in 16GB page." }, { "EventCode": "0x0C38", "EventName": "L2D_TLB_REFILL_4K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 4KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 4KB page." }, { "EventCode": "0x0C39", "EventName": "L2D_TLB_REFILL_64K", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 64KB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 64KB page." }, { "EventCode": "0x0C3A", "EventName": "L2D_TLB_REFILL_2M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 2MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 2MB page." }, { "EventCode": "0x0C3B", "EventName": "L2D_TLB_REFILL_32M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 32MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 32MB page." }, { "EventCode": "0x0C3C", "EventName": "L2D_TLB_REFILL_512M", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 512MB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 512MB page." }, { "EventCode": "0x0C3D", "EventName": "L2D_TLB_REFILL_1G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 1GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 1GB page." }, { "EventCode": "0x0C3E", "EventName": "L2D_TLB_REFILL_16G", - "BriefDescription": "This event counts operations that cause a TLB refill to the L2D in 16GB page." + "BriefDescription": "This event counts operations that cause a TLB refill of the L2D in 16GB page." }, { "ArchStdEvent": "DTLB_WALK_PERCYC", diff --git a/tools/perf/pmu-events/arch/common/common/software.json b/tools/perf/pmu-events/arch/common/common/software.json new file mode 100644 index 000000000000..f2551f1107fd --- /dev/null +++ b/tools/perf/pmu-events/arch/common/common/software.json @@ -0,0 +1,92 @@ +[ + { + "Unit": "software", + "EventName": "cpu-clock", + "BriefDescription": "Per-CPU high-resolution timer based event", + "ConfigCode": "0" + }, + { + "Unit": "software", + "EventName": "task-clock", + "BriefDescription": "Per-task high-resolution timer based event", + "ConfigCode": "1" + }, + { + "Unit": "software", + "EventName": "faults", + "BriefDescription": "Number of page faults [This event is an alias of page-faults]", + "ConfigCode": "2" + }, + { + "Unit": "software", + "EventName": "page-faults", + "BriefDescription": "Number of page faults [This event is an alias of faults]", + "ConfigCode": "2" + }, + { + "Unit": "software", + "EventName": "context-switches", + "BriefDescription": "Number of context switches [This event is an alias of cs]", + "ConfigCode": "3" + }, + { + "Unit": "software", + "EventName": "cs", + "BriefDescription": "Number of context switches [This event is an alias of context-switches]", + "ConfigCode": "3" + }, + { + "Unit": "software", + "EventName": "cpu-migrations", + "BriefDescription": "Number of times a process has migrated to a new CPU [This event is an alias of migrations]", + "ConfigCode": "4" + }, + { + "Unit": "software", + "EventName": "migrations", + "BriefDescription": "Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]", + "ConfigCode": "4" + }, + { + "Unit": "software", + "EventName": "minor-faults", + "BriefDescription": "Number of minor page faults. Minor faults don't require I/O to handle", + "ConfigCode": "5" + }, + { + "Unit": "software", + "EventName": "major-faults", + "BriefDescription": "Number of major page faults. Major faults require I/O to handle", + "ConfigCode": "6" + }, + { + "Unit": "software", + "EventName": "alignment-faults", + "BriefDescription": "Number of kernel handled memory alignment faults", + "ConfigCode": "7" + }, + { + "Unit": "software", + "EventName": "emulation-faults", + "BriefDescription": "Number of kernel handled unimplemented instruction faults handled through emulation", + "ConfigCode": "8" + }, + { + "Unit": "software", + "EventName": "dummy", + "BriefDescription": "A placeholder event that doesn't count anything", + "ConfigCode": "9" + }, + { + "Unit": "software", + "EventName": "bpf-output", + "BriefDescription": "An event used by BPF programs to write to the perf ring buffer", + "ConfigCode": "10" + }, + { + "Unit": "software", + "EventName": "cgroup-switches", + "BriefDescription": "Number of context switches to a task in a different cgroup", + "ConfigCode": "11" + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z16/pai_crypto.json b/tools/perf/pmu-events/arch/s390/cf_z16/pai_crypto.json index cf8563d059b9..a82674f62409 100644 --- a/tools/perf/pmu-events/arch/s390/cf_z16/pai_crypto.json +++ b/tools/perf/pmu-events/arch/s390/cf_z16/pai_crypto.json @@ -753,14 +753,14 @@ "EventCode": "4203", "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_128", "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED TDEA 128", - "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA- 128 function ending with CC=0" + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-128 function ending with CC=0" }, { "Unit": "PAI-CRYPTO", "EventCode": "4204", "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_192", "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED TDEA 192", - "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA- 192 function ending with CC=0" + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-192 function ending with CC=0" }, { "Unit": "PAI-CRYPTO", @@ -788,21 +788,21 @@ "EventCode": "4208", "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_128", "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 128", - "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES- 128 function ending with CC=0" + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-128 function ending with CC=0" }, { "Unit": "PAI-CRYPTO", "EventCode": "4209", "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_192", "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 192", - "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES- 192 function ending with CC=0" + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-192 function ending with CC=0" }, { "Unit": "PAI-CRYPTO", "EventCode": "4210", - "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256A", - "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 256A", - "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES- 256A function ending with CC=0" + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 256", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-256 function ending with CC=0" }, { "Unit": "PAI-CRYPTO", diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/basic.json b/tools/perf/pmu-events/arch/s390/cf_z17/basic.json new file mode 100644 index 000000000000..1023d47028ce --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/basic.json @@ -0,0 +1,58 @@ +[ + { + "Unit": "CPU-M-CF", + "EventCode": "0", + "EventName": "CPU_CYCLES", + "BriefDescription": "Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles, excluding the number of cycles while the CPU is in the wait state." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "1", + "EventName": "INSTRUCTIONS", + "BriefDescription": "Instruction Count", + "PublicDescription": "This counter counts the total number of instructions executed by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "2", + "EventName": "L1I_DIR_WRITES", + "BriefDescription": "Level-1 I-Cache Directory Write Count", + "PublicDescription": "This counter counts the total number of level-1 instruction-cache or unified-cache directory writes." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "3", + "EventName": "L1I_PENALTY_CYCLES", + "BriefDescription": "Level-1 I-Cache Penalty Cycle Count", + "PublicDescription": "This counter counts the total number of cache penalty cycles for level-1 instruction cache or unified cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "4", + "EventName": "L1D_DIR_WRITES", + "BriefDescription": "Level-1 D-Cache Directory Write Count", + "PublicDescription": "This counter counts the total number of level-1 data-cache directory writes." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "5", + "EventName": "L1D_PENALTY_CYCLES", + "BriefDescription": "Level-1 D-Cache Penalty Cycle Count", + "PublicDescription": "This counter counts the total number of cache penalty cycles for level-1 data cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "32", + "EventName": "PROBLEM_STATE_CPU_CYCLES", + "BriefDescription": "Problem-State Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the CPU is in the problem state, excluding the number of cycles while the CPU is in the wait state." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "33", + "EventName": "PROBLEM_STATE_INSTRUCTIONS", + "BriefDescription": "Problem-State Instruction Count", + "PublicDescription": "This counter counts the total number of instructions executed by the CPU while in the problem state." + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/crypto6.json b/tools/perf/pmu-events/arch/s390/cf_z17/crypto6.json new file mode 100644 index 000000000000..8b4380b8e489 --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/crypto6.json @@ -0,0 +1,142 @@ +[ + { + "Unit": "CPU-M-CF", + "EventCode": "64", + "EventName": "PRNG_FUNCTIONS", + "BriefDescription": "PRNG Function Count", + "PublicDescription": "This counter counts the total number of the pseudorandom-number-generation functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "65", + "EventName": "PRNG_CYCLES", + "BriefDescription": "PRNG Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the DEA/AES/SHA coprocessor is busy performing the pseudorandom- number-generation functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "66", + "EventName": "PRNG_BLOCKED_FUNCTIONS", + "BriefDescription": "PRNG Blocked Function Count", + "PublicDescription": "This counter counts the total number of the pseudorandom-number-generation functions that are issued by the CPU and are blocked because the DEA/AES/SHA coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "67", + "EventName": "PRNG_BLOCKED_CYCLES", + "BriefDescription": "PRNG Blocked Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles blocked for the pseudorandom-number-generation functions issued by the CPU because the DEA/AES/SHA coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "68", + "EventName": "SHA_FUNCTIONS", + "BriefDescription": "SHA Function Count", + "PublicDescription": "This counter counts the total number of the SHA functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "69", + "EventName": "SHA_CYCLES", + "BriefDescription": "SHA Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the SHA coprocessor is busy performing the SHA functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "70", + "EventName": "SHA_BLOCKED_FUNCTIONS", + "BriefDescription": "SHA Blocked Function Count", + "PublicDescription": "This counter counts the total number of the SHA functions that are issued by the CPU and are blocked because the SHA coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "71", + "EventName": "SHA_BLOCKED_CYCLES", + "BriefDescription": "SHA Blocked Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles blocked for the SHA functions issued by the CPU because the SHA coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "72", + "EventName": "DEA_FUNCTIONS", + "BriefDescription": "DEA Function Count", + "PublicDescription": "This counter counts the total number of the DEA functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "73", + "EventName": "DEA_CYCLES", + "BriefDescription": "DEA Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the DEA/AES coprocessor is busy performing the DEA functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "74", + "EventName": "DEA_BLOCKED_FUNCTIONS", + "BriefDescription": "DEA Blocked Function Count", + "PublicDescription": "This counter counts the total number of the DEA functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "75", + "EventName": "DEA_BLOCKED_CYCLES", + "BriefDescription": "DEA Blocked Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles blocked for the DEA functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "76", + "EventName": "AES_FUNCTIONS", + "BriefDescription": "AES Function Count", + "PublicDescription": "This counter counts the total number of the AES functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "77", + "EventName": "AES_CYCLES", + "BriefDescription": "AES Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the DEA/AES coprocessor is busy performing the AES functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "78", + "EventName": "AES_BLOCKED_FUNCTIONS", + "BriefDescription": "AES Blocked Function Count", + "PublicDescription": "This counter counts the total number of the AES functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "79", + "EventName": "AES_BLOCKED_CYCLES", + "BriefDescription": "AES Blocked Cycle Count", + "PublicDescription": "This counter counts the total number of CPU cycles blocked for the AES functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "80", + "EventName": "ECC_FUNCTION_COUNT", + "BriefDescription": "ECC Function Count", + "PublicDescription": "This counter counts the total number of the elliptic-curve cryptography (ECC) functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "81", + "EventName": "ECC_CYCLES_COUNT", + "BriefDescription": "ECC Cycles Count", + "PublicDescription": "This counter counts the total number of CPU cycles when the ECC coprocessor is busy performing the elliptic-curve cryptography (ECC) functions issued by the CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "82", + "EventName": "ECC_BLOCKED_FUNCTION_COUNT", + "BriefDescription": "Ecc Blocked Function Count", + "PublicDescription": "This counter counts the total number of the elliptic-curve cryptography (ECC) functions that are issued by the CPU and are blocked because the ECC coprocessor is busy performing a function issued by another CPU." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "83", + "EventName": "ECC_BLOCKED_CYCLES_COUNT", + "BriefDescription": "ECC Blocked Cycles Count", + "PublicDescription": "This counter counts the total number of CPU cycles blocked for the elliptic-curve cryptography (ECC) functions issued by the CPU because the ECC coprocessor is busy performing a function issued by another CPU." + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/extended.json b/tools/perf/pmu-events/arch/s390/cf_z17/extended.json new file mode 100644 index 000000000000..e139482e217f --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/extended.json @@ -0,0 +1,541 @@ +[ + { + "Unit": "CPU-M-CF", + "EventCode": "128", + "EventName": "L1D_RO_EXCL_WRITES", + "BriefDescription": "L1D Read-only Exclusive Writes", + "PublicDescription": "A directory write to the Level-1 Data cache where the line was originally in a Read-Only state in the cache but has been updated to be in the Exclusive state that allows stores to the cache line." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "129", + "EventName": "DTLB2_WRITES", + "BriefDescription": "DTLB2 Writes", + "PublicDescription": "A translation has been written into The Translation Lookaside Buffer 2 (TLB2) and the request was made by the Level-1 Data cache. This is a replacement for what was provided for the DTLB on z13 and prior machines." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "130", + "EventName": "DTLB2_MISSES", + "BriefDescription": "DTLB2 Misses", + "PublicDescription": "A TLB2 miss is in progress for a request made by the Level-1 Data cache. Incremented by one for every TLB2 miss in progress for the Level-1 Data cache on this cycle. This is a replacement for what was provided for the DTLB on z13 and prior machines." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "131", + "EventName": "CRSTE_1MB_WRITES", + "BriefDescription": "One Megabyte CRSTE writes", + "PublicDescription": "A translation entry was written into the Combined Region and Segment Table Entry array in the Level-2 TLB for a one-megabyte page." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "132", + "EventName": "DTLB2_GPAGE_WRITES", + "BriefDescription": "DTLB2 Two-Gigabyte Page Writes", + "PublicDescription": "A translation entry for a two-gigabyte page was written into the Level-2 TLB." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "134", + "EventName": "ITLB2_WRITES", + "BriefDescription": "ITLB2 Writes", + "PublicDescription": "A translation entry has been written into the Translation Lookaside Buffer 2 (TLB2) and the request was made by the Level-1 Instruction cache. This is a replacement for what was provided for the ITLB on z13 and prior machines." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "135", + "EventName": "ITLB2_MISSES", + "BriefDescription": "ITLB2 Misses", + "PublicDescription": "A TLB2 miss is in progress for a request made by the Level-1 Instruction cache. Incremented by one for every TLB2 miss in progress for the Level-1 Instruction cache in a cycle. This is a replacement for what was provided for the ITLB on z13 and prior machines." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "137", + "EventName": "TLB2_PTE_WRITES", + "BriefDescription": "TLB2 Page Table Entry Writes", + "PublicDescription": "A translation entry was written into the Page Table Entry array in the Level-2 TLB." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "138", + "EventName": "TLB2_CRSTE_WRITES", + "BriefDescription": "TLB2 Combined Region and Segment Entry Writes", + "PublicDescription": "Translation entries were written into the Combined Region and Segment Table Entry array and the Page Table Entry array in the Level-2 TLB." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "139", + "EventName": "TLB2_ENGINES_BUSY", + "BriefDescription": "TLB2 Engines Busy", + "PublicDescription": "The number of Level-2 TLB translation engines busy in a cycle." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "140", + "EventName": "TX_C_TEND", + "BriefDescription": "Completed TEND instructions in constrained TX mode", + "PublicDescription": "A TEND instruction has completed in a constrained transactional-execution mode." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "141", + "EventName": "TX_NC_TEND", + "BriefDescription": "Completed TEND instructions in non-constrained TX mode", + "PublicDescription": "A TEND instruction has completed in a non-constrained transactional-execution mode." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "143", + "EventName": "L1C_TLB2_MISSES", + "BriefDescription": "L1C TLB2 Misses", + "PublicDescription": "Increments by one for any cycle where a Level-1 cache or Level-2 TLB miss is in progress." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "145", + "EventName": "DCW_REQ", + "BriefDescription": "Directory Write Level 1 Data Cache from L2-Cache", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from the requestors Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "146", + "EventName": "DCW_REQ_IV", + "BriefDescription": "Directory Write Level 1 Data Cache from L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from the requestors Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "147", + "EventName": "DCW_REQ_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Data Cache from L2-Cache with Chip HP Hit", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from the requestors Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "148", + "EventName": "DCW_REQ_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Data Cache from L2-Cache with Drawer HP Hit", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from the requestors Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "149", + "EventName": "DCW_ON_CHIP", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Chip L2-Cache", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "150", + "EventName": "DCW_ON_CHIP_IV", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Chip L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "151", + "EventName": "DCW_ON_CHIP_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Chip L2-Cache with Chip HP Hit", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "152", + "EventName": "DCW_ON_CHIP_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Chip L2-Cache with Drawer HP Hit", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "153", + "EventName": "DCW_ON_MODULE", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Module L2-Cache", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Module Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "154", + "EventName": "DCW_ON_DRAWER", + "BriefDescription": "Directory Write Level 1 Data Cache from On-Drawer L2-Cache", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Drawer Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "155", + "EventName": "DCW_OFF_DRAWER", + "BriefDescription": "Directory Write Level 1 Data Cache from Off-Drawer L2-Cache", + "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an Off-Drawer Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "156", + "EventName": "DCW_ON_CHIP_MEMORY", + "BriefDescription": "Directory Write Level 1 Cache from On-Chip Memory", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from On-Chip memory." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "157", + "EventName": "DCW_ON_MODULE_MEMORY", + "BriefDescription": "Directory Write Level 1 Cache from On-Module Memory", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from On-Module memory." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "158", + "EventName": "DCW_ON_DRAWER_MEMORY", + "BriefDescription": "Directory Write Level 1 Cache from On-Drawer Memory", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from On-Drawer memory." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "159", + "EventName": "DCW_OFF_DRAWER_MEMORY", + "BriefDescription": "Directory Write Level 1 Cache from Off-Drawer Memory", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from Off-Drawer memory." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "160", + "EventName": "IDCW_ON_MODULE_IV", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Module Memory L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from an On-Module Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "161", + "EventName": "IDCW_ON_MODULE_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Module Memory L2-Cache with Chip Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from an On-Module Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "162", + "EventName": "IDCW_ON_MODULE_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Module Memory L2-Cache with Drawer Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from an On-Module Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "163", + "EventName": "IDCW_ON_DRAWER_IV", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Drawer L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from an On-Drawer Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "164", + "EventName": "IDCW_ON_DRAWER_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Drawer L2-Cache with Chip Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 instruction cache directory where the returned cache line was sourced from an On-Drawer Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "165", + "EventName": "IDCW_ON_DRAWER_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from On-Drawer L2-Cache with Drawer Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 instruction cache directory where the returned cache line was sourced from an On-Drawer Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "166", + "EventName": "IDCW_OFF_DRAWER_IV", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from Off-Drawer L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "167", + "EventName": "IDCW_OFF_DRAWER_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from Off-Drawer L2-Cache with Chip Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "168", + "EventName": "IDCW_OFF_DRAWER_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Instruction and Data Cache from Off-Drawer L2-Cache with Drawer Hit", + "PublicDescription": "A directory write to the Level-1 Data or Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "169", + "EventName": "ICW_REQ", + "BriefDescription": "Directory Write Level 1 Instruction Cache from L2-Cache", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced the requestors Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "170", + "EventName": "ICW_REQ_IV", + "BriefDescription": "Directory Write Level 1 Instruction Cache from L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from the requestors Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "171", + "EventName": "ICW_REQ_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Instruction Cache from L2-Cache with Chip HP Hit", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from the requestors Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "172", + "EventName": "ICW_REQ_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Instruction Cache from L2-Cache with Drawer HP Hit", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from the requestors Level-2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "173", + "EventName": "ICW_ON_CHIP", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Chip L2-Cache", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Chip Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "174", + "EventName": "ICW_ON_CHIP_IV", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Chip L2-Cache with Intervention", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Chip Level-2 cache with intervention." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "175", + "EventName": "ICW_ON_CHIP_CHIP_HIT", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Chip L2-Cache with Chip HP Hit", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Chip Level-2 cache after using chip level horizontal persistence, Chip-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "176", + "EventName": "ICW_ON_CHIP_DRAWER_HIT", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Chip L2-Cache with Drawer HP Hit", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Chip level 2 cache after using drawer level horizontal persistence, Drawer-HP hit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "177", + "EventName": "ICW_ON_MODULE", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Module L2-Cache", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Module Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "178", + "EventName": "ICW_ON_DRAWER", + "BriefDescription": "Directory Write Level 1 Instruction Cache from On-Drawer L2-Cache", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Drawer Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "179", + "EventName": "ICW_OFF_DRAWER", + "BriefDescription": "Directory Write Level 1 Instruction Cache from Off-Drawer L2-Cache", + "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-2 cache." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "202", + "EventName": "CYCLES_SAMETHRD", + "BriefDescription": "CPU is not in wait state and CPU is running by itself", + "PublicDescription": "The number of cycles the CPU is not in wait state and the CPU is running by itself on the Core." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "203", + "EventName": "CYCLES_DIFFTHRD", + "BriefDescription": "CPU is not in wait state and CPU is running by another thread", + "PublicDescription": "The number of cycles the CPU is not in wait state and the CPU is running with another thread on the Core." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "204", + "EventName": "INST_SAMETHRD", + "BriefDescription": "Instructions executed on CPU by itself", + "PublicDescription": "The number of instructions executed on the CPU and the CPU is running by itself on the Core." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "205", + "EventName": "INST_DIFFTHRD", + "BriefDescription": "Instructions executed on CPU by another thread", + "PublicDescription": "The number of instructions executed on the CPU and the CPU is running with another thread on the Core." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "206", + "EventName": "WRONG_BRANCH_PREDICTION", + "BriefDescription": "Incorrect branch prediction on core", + "PublicDescription": "A count of the number of branches that were predicted incorrectly by the branch prediction logic in the Core. This includes incorrectly predicted branches that are executed in Firmware. Examples of instructions implemented in Firmware are complicated instructions like MVCL (Move Character Long) and PC (Program Call)." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "225", + "EventName": "VX_BCD_EXECUTION_SLOTS", + "BriefDescription": "Count finished vector arithmetic Binary Coded Decimal instructions", + "PublicDescription": "Count of floating point execution slots used for finished vector arithmetic Binary Coded Decimal instructions. Instructions: VAP, VSP, VMP, VMSP, VDP, VSDP, VRP, VLIP, VSRP, VPSOP, VCP, VTP, VPKZ, VUPKZ, VCVB, VCVBG, VCVD, VCVDG, VSCHP, VSCSHP, VCSPH, VCLZDP, VPKZR, VSRPR, VUPKZH, VUPKZL, VTZ, VUPH, VUPL, VCVBX, VCVDX." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "226", + "EventName": "DECIMAL_INSTRUCTIONS", + "BriefDescription": "Decimal instruction dispatched", + "PublicDescription": "Decimal instruction dispatched. Instructions: CVB, CVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP, TP." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "232", + "EventName": "LAST_HOST_TRANSLATIONS", + "BriefDescription": "Last host translation done", + "PublicDescription": "Last Host Translation done." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "244", + "EventName": "TX_NC_TABORT", + "BriefDescription": "Aborted transactions in unconstrained TX mode", + "PublicDescription": "A transaction abort has occurred in a non-constrained transactional-execution mode." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "245", + "EventName": "TX_C_TABORT_NO_SPECIAL", + "BriefDescription": "Aborted transactions in constrained TX mode", + "PublicDescription": "A transaction abort has occurred in a constrained transactional-execution mode and the CPU is not using any special logic to allow the transaction to complete." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "246", + "EventName": "TX_C_TABORT_SPECIAL", + "BriefDescription": "Aborted transactions in constrained TX mode using special completion logic", + "PublicDescription": "A transaction abort has occurred in a constrained transactional-execution mode and the CPU is using special logic to allow the transaction to complete." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "248", + "EventName": "DFLT_ACCESS", + "BriefDescription": "Cycles CPU spent obtaining access to Deflate unit", + "PublicDescription": "Cycles CPU spent obtaining access to Deflate unit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "253", + "EventName": "DFLT_CYCLES", + "BriefDescription": "Cycles CPU is using Deflate unit", + "PublicDescription": "Cycles CPU is using Deflate unit." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "256", + "EventName": "SORTL", + "BriefDescription": "Count SORTL instructions", + "PublicDescription": "Increments by one for every SORT LISTS (SORTL) instruction executed." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "265", + "EventName": "DFLT_CC", + "BriefDescription": "Increments DEFLATE CONVERSION CALL", + "PublicDescription": "Increments by one for every DEFLATE CONVERSION CALL (DFLTCC) instruction executed." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "266", + "EventName": "DFLT_CCFINISH", + "BriefDescription": "Increments completed DEFLATE CONVERSION CALL", + "PublicDescription": "Increments by one for every DEFLATE CONVERSION CALL (DFLTCC) instruction executed that ended in Condition Codes 0, 1 or 2." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "267", + "EventName": "NNPA_INVOCATIONS", + "BriefDescription": "NNPA Total invocations", + "PublicDescription": "Increments by one for every NEURAL NETWORK PROCESSING ASSIST (NNPA) instruction executed." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "268", + "EventName": "NNPA_COMPLETIONS", + "BriefDescription": "NNPA Total completions", + "PublicDescription": "Increments by one for every NEURAL NETWORK PROCESSING ASSIST (NNPA) instruction executed that ended in Condition Code 0." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "269", + "EventName": "NNPA_WAIT_LOCK", + "BriefDescription": "Cycles spent obtaining NNPA lock", + "PublicDescription": "Cycles CPU spent obtaining access to IBM Z Integrated Accelerator for AI." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "270", + "EventName": "NNPA_HOLD_LOCK", + "BriefDescription": "Cycles spent holding NNPA lock", + "PublicDescription": "Cycles CPU is using IBM Z Integrated Accelerator for AI." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "272", + "EventName": "NNPA_INST_ONCHIP", + "BriefDescription": "NNPA instructions used on-chip Integrated Accelerator", + "PublicDescription": "A NEURAL NETWORK PROCESSING ASSIST (NNPA) instruction has used the Local On-Chip IBM Z Integrated Accelerator for AI during its execution" + }, + { + "Unit": "CPU-M-CF", + "EventCode": "273", + "EventName": "NNPA_INST_OFFCHIP", + "BriefDescription": "NNPA instructions used off-chip Integrated Accelerator", + "PublicDescription": "A NEURAL NETWORK PROCESSING ASSIST (NNPA) instruction has used an Off-Chip IBM Z Integrated Accelerator for AI during its execution." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "274", + "EventName": "NNPA_INST_DIFF", + "BriefDescription": "NNPA instructions used different Integrated Accelerator", + "PublicDescription": "A NEURAL NETWORK PROCESSING ASSIST (NNPA) instruction has used a different IBM Z Integrated Accelerator for AI since it was last executed." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "276", + "EventName": "NNPA_4K_PREFETCH", + "BriefDescription": "Number of 4K prefetches for Integated Accelerator", + "PublicDescription": "Number of 4K prefetches done for a remote IBM Z Integated Accelerator for AI." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "277", + "EventName": "NNPA_COMPL_LOCK", + "BriefDescription": "A Perform Locked Operation has completed", + "PublicDescription": "A PERFORM LOCKED OPERATION (PLO) has completed." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "278", + "EventName": "NNPA_RETRY_LOCK", + "BriefDescription": "A Perform Locked Operation has been retried", + "PublicDescription": "A PERFORM LOCKED OPERATION (PLO) has been retried and the CPU did not use any special logic to allow the PLO to complete." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "279", + "EventName": "NNPA_RETRY_LOCK_WITH_PLO", + "BriefDescription": "A Perform Locked Operation has been retried using special logic", + "PublicDescription": "A PERFORM LOCKED OPERATION (PLO) has been retried and the CPU is using special logic to allow PLO to complete." + }, + { + "Unit": "CPU-M-CF", + "EventCode": "448", + "EventName": "MT_DIAG_CYCLES_ONE_THR_ACTIVE", + "BriefDescription": "Cycle count with one thread active", + "PublicDescription": "Cycle count with one thread active" + }, + { + "Unit": "CPU-M-CF", + "EventCode": "449", + "EventName": "MT_DIAG_CYCLES_TWO_THR_ACTIVE", + "BriefDescription": "Cycle count with two threads active", + "PublicDescription": "Cycle count with two threads active" + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/pai_crypto.json b/tools/perf/pmu-events/arch/s390/cf_z17/pai_crypto.json new file mode 100644 index 000000000000..fd2eb536ecc7 --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/pai_crypto.json @@ -0,0 +1,1213 @@ +[ + { + "Unit": "PAI-CRYPTO", + "EventCode": "4096", + "EventName": "CRYPTO_ALL", + "BriefDescription": "CRYPTO ALL", + "PublicDescription": "Sums of all non zero cryptography counters" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4097", + "EventName": "KM_DEA", + "BriefDescription": "KM DEA", + "PublicDescription": "KM-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4098", + "EventName": "KM_TDEA_128", + "BriefDescription": "KM TDEA 128", + "PublicDescription": "KM-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4099", + "EventName": "KM_TDEA_192", + "BriefDescription": "KM TDEA 192", + "PublicDescription": "KM-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4100", + "EventName": "KM_ENCRYPTED_DEA", + "BriefDescription": "KM ENCRYPTED DEA", + "PublicDescription": "KM-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4101", + "EventName": "KM_ENCRYPTED_TDEA_128", + "BriefDescription": "KM ENCRYPTED TDEA 128", + "PublicDescription": "KM-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4102", + "EventName": "KM_ENCRYPTED_TDEA_192", + "BriefDescription": "KM ENCRYPTED TDEA 192", + "PublicDescription": "KM-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4103", + "EventName": "KM_AES_128", + "BriefDescription": "KM AES 128", + "PublicDescription": "KM-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4104", + "EventName": "KM_AES_192", + "BriefDescription": "KM AES 192", + "PublicDescription": "KM-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4105", + "EventName": "KM_AES_256", + "BriefDescription": "KM AES 256", + "PublicDescription": "KM-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4106", + "EventName": "KM_ENCRYPTED_AES_128", + "BriefDescription": "KM ENCRYPTED AES 128", + "PublicDescription": "KM-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4107", + "EventName": "KM_ENCRYPTED_AES_192", + "BriefDescription": "KM ENCRYPTED AES 192", + "PublicDescription": "KM-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4108", + "EventName": "KM_ENCRYPTED_AES_256", + "BriefDescription": "KM ENCRYPTED AES 256", + "PublicDescription": "KM-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4109", + "EventName": "KM_XTS_AES_128", + "BriefDescription": "KM XTS AES 128", + "PublicDescription": "KM-XTS-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4110", + "EventName": "KM_XTS_AES_256", + "BriefDescription": "KM XTS AES 256", + "PublicDescription": "KM-XTS-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4111", + "EventName": "KM_XTS_ENCRYPTED_AES_128", + "BriefDescription": "KM XTS ENCRYPTED AES 128", + "PublicDescription": "KM-XTS-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4112", + "EventName": "KM_XTS_ENCRYPTED_AES_256", + "BriefDescription": "KM XTS ENCRYPTED AES 256", + "PublicDescription": "KM-XTS-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4113", + "EventName": "KMC_DEA", + "BriefDescription": "KMC DEA", + "PublicDescription": "KMC-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4114", + "EventName": "KMC_TDEA_128", + "BriefDescription": "KMC TDEA 128", + "PublicDescription": "KMC-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4115", + "EventName": "KMC_TDEA_192", + "BriefDescription": "KMC TDEA 192", + "PublicDescription": "KMC-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4116", + "EventName": "KMC_ENCRYPTED_DEA", + "BriefDescription": "KMC ENCRYPTED DEA", + "PublicDescription": "KMC-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4117", + "EventName": "KMC_ENCRYPTED_TDEA_128", + "BriefDescription": "KMC ENCRYPTED TDEA 128", + "PublicDescription": "KMC-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4118", + "EventName": "KMC_ENCRYPTED_TDEA_192", + "BriefDescription": "KMC ENCRYPTED TDEA 192", + "PublicDescription": "KMC-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4119", + "EventName": "KMC_AES_128", + "BriefDescription": "KMC AES 128", + "PublicDescription": "KMC-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4120", + "EventName": "KMC_AES_192", + "BriefDescription": "KMC AES 192", + "PublicDescription": "KMC-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4121", + "EventName": "KMC_AES_256", + "BriefDescription": "KMC AES 256", + "PublicDescription": "KMC-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4122", + "EventName": "KMC_ENCRYPTED_AES_128", + "BriefDescription": "KMC ENCRYPTED AES 128", + "PublicDescription": "KMC-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4123", + "EventName": "KMC_ENCRYPTED_AES_192", + "BriefDescription": "KMC ENCRYPTED AES 192", + "PublicDescription": "KMC-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4124", + "EventName": "KMC_ENCRYPTED_AES_256", + "BriefDescription": "KMC ENCRYPTED AES 256", + "PublicDescription": "KMC-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4125", + "EventName": "KMC_PRNG", + "BriefDescription": "KMC PRNG", + "PublicDescription": "KMC-PRNG function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4126", + "EventName": "KMA_GCM_AES_128", + "BriefDescription": "KMA GCM AES 128", + "PublicDescription": "KMA-GCM-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4127", + "EventName": "KMA_GCM_AES_192", + "BriefDescription": "KMA GCM AES 192", + "PublicDescription": "KMA-GCM-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4128", + "EventName": "KMA_GCM_AES_256", + "BriefDescription": "KMA GCM AES 256", + "PublicDescription": "KMA-GCM-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4129", + "EventName": "KMA_GCM_ENCRYPTED_AES_128", + "BriefDescription": "KMA GCM ENCRYPTED AES 128", + "PublicDescription": "KMA-GCM-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4130", + "EventName": "KMA_GCM_ENCRYPTED_AES_192", + "BriefDescription": "KMA GCM ENCRYPTED AES 192", + "PublicDescription": "KMA-GCM-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4131", + "EventName": "KMA_GCM_ENCRYPTED_AES_256", + "BriefDescription": "KMA GCM ENCRYPTED AES 256", + "PublicDescription": "KMA-GCM-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4132", + "EventName": "KMF_DEA", + "BriefDescription": "KMF DEA", + "PublicDescription": "KMF-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4133", + "EventName": "KMF_TDEA_128", + "BriefDescription": "KMF TDEA 128", + "PublicDescription": "KMF-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4134", + "EventName": "KMF_TDEA_192", + "BriefDescription": "KMF TDEA 192", + "PublicDescription": "KMF-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4135", + "EventName": "KMF_ENCRYPTED_DEA", + "BriefDescription": "KMF ENCRYPTED DEA", + "PublicDescription": "KMF-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4136", + "EventName": "KMF_ENCRYPTED_TDEA_128", + "BriefDescription": "KMF ENCRYPTED TDEA 128", + "PublicDescription": "KMF-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4137", + "EventName": "KMF_ENCRYPTED_TDEA_192", + "BriefDescription": "KMF ENCRYPTED TDEA 192", + "PublicDescription": "KMF-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4138", + "EventName": "KMF_AES_128", + "BriefDescription": "KMF AES 128", + "PublicDescription": "KMF-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4139", + "EventName": "KMF_AES_192", + "BriefDescription": "KMF AES 192", + "PublicDescription": "KMF-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4140", + "EventName": "KMF_AES_256", + "BriefDescription": "KMF AES 256", + "PublicDescription": "KMF-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4141", + "EventName": "KMF_ENCRYPTED_AES_128", + "BriefDescription": "KMF ENCRYPTED AES 128", + "PublicDescription": "KMF-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4142", + "EventName": "KMF_ENCRYPTED_AES_192", + "BriefDescription": "KMF ENCRYPTED AES 192", + "PublicDescription": "KMF-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4143", + "EventName": "KMF_ENCRYPTED_AES_256", + "BriefDescription": "KMF ENCRYPTED AES 256", + "PublicDescription": "KMF-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4144", + "EventName": "KMCTR_DEA", + "BriefDescription": "KMCTR DEA", + "PublicDescription": "KMCTR-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4145", + "EventName": "KMCTR_TDEA_128", + "BriefDescription": "KMCTR TDEA 128", + "PublicDescription": "KMCTR-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4146", + "EventName": "KMCTR_TDEA_192", + "BriefDescription": "KMCTR TDEA 192", + "PublicDescription": "KMCTR-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4147", + "EventName": "KMCTR_ENCRYPTED_DEA", + "BriefDescription": "KMCTR ENCRYPTED DEA", + "PublicDescription": "KMCTR-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4148", + "EventName": "KMCTR_ENCRYPTED_TDEA_128", + "BriefDescription": "KMCTR ENCRYPTED TDEA 128", + "PublicDescription": "KMCTR-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4149", + "EventName": "KMCTR_ENCRYPTED_TDEA_192", + "BriefDescription": "KMCTR ENCRYPTED TDEA 192", + "PublicDescription": "KMCTR-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4150", + "EventName": "KMCTR_AES_128", + "BriefDescription": "KMCTR AES 128", + "PublicDescription": "KMCTR-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4151", + "EventName": "KMCTR_AES_192", + "BriefDescription": "KMCTR AES 192", + "PublicDescription": "KMCTR-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4152", + "EventName": "KMCTR_AES_256", + "BriefDescription": "KMCTR AES 256", + "PublicDescription": "KMCTR-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4153", + "EventName": "KMCTR_ENCRYPTED_AES_128", + "BriefDescription": "KMCTR ENCRYPTED AES 128", + "PublicDescription": "KMCTR-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4154", + "EventName": "KMCTR_ENCRYPTED_AES_192", + "BriefDescription": "KMCTR ENCRYPTED AES 192", + "PublicDescription": "KMCTR-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4155", + "EventName": "KMCTR_ENCRYPTED_AES_256", + "BriefDescription": "KMCTR ENCRYPTED AES 256", + "PublicDescription": "KMCTR-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4156", + "EventName": "KMO_DEA", + "BriefDescription": "KMO DEA", + "PublicDescription": "KMO-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4157", + "EventName": "KMO_TDEA_128", + "BriefDescription": "KMO TDEA 128", + "PublicDescription": "KMO-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4158", + "EventName": "KMO_TDEA_192", + "BriefDescription": "KMO TDEA 192", + "PublicDescription": "KMO-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4159", + "EventName": "KMO_ENCRYPTED_DEA", + "BriefDescription": "KMO ENCRYPTED DEA", + "PublicDescription": "KMO-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4160", + "EventName": "KMO_ENCRYPTED_TDEA_128", + "BriefDescription": "KMO ENCRYPTED TDEA 128", + "PublicDescription": "KMO-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4161", + "EventName": "KMO_ENCRYPTED_TDEA_192", + "BriefDescription": "KMO ENCRYPTED TDEA 192", + "PublicDescription": "KMO-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4162", + "EventName": "KMO_AES_128", + "BriefDescription": "KMO AES 128", + "PublicDescription": "KMO-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4163", + "EventName": "KMO_AES_192", + "BriefDescription": "KMO AES 192", + "PublicDescription": "KMO-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4164", + "EventName": "KMO_AES_256", + "BriefDescription": "KMO AES 256", + "PublicDescription": "KMO-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4165", + "EventName": "KMO_ENCRYPTED_AES_128", + "BriefDescription": "KMO ENCRYPTED AES 128", + "PublicDescription": "KMO-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4166", + "EventName": "KMO_ENCRYPTED_AES_192", + "BriefDescription": "KMO ENCRYPTED AES 192", + "PublicDescription": "KMO-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4167", + "EventName": "KMO_ENCRYPTED_AES_256", + "BriefDescription": "KMO ENCRYPTED AES 256", + "PublicDescription": "KMO-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4168", + "EventName": "KIMD_SHA_1", + "BriefDescription": "KIMD SHA 1", + "PublicDescription": "KIMD-SHA-1 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4169", + "EventName": "KIMD_SHA_256", + "BriefDescription": "KIMD SHA 256", + "PublicDescription": "KIMD-SHA-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4170", + "EventName": "KIMD_SHA_512", + "BriefDescription": "KIMD SHA 512", + "PublicDescription": "KIMD-SHA-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4171", + "EventName": "KIMD_SHA3_224", + "BriefDescription": "KIMD SHA3 224", + "PublicDescription": "KIMD-SHA3-224 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4172", + "EventName": "KIMD_SHA3_256", + "BriefDescription": "KIMD SHA3 256", + "PublicDescription": "KIMD-SHA3-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4173", + "EventName": "KIMD_SHA3_384", + "BriefDescription": "KIMD SHA3 384", + "PublicDescription": "KIMD-SHA3-384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4174", + "EventName": "KIMD_SHA3_512", + "BriefDescription": "KIMD SHA3 512", + "PublicDescription": "KIMD-SHA3-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4175", + "EventName": "KIMD_SHAKE_128", + "BriefDescription": "KIMD SHAKE 128", + "PublicDescription": "KIMD-SHAKE-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4176", + "EventName": "KIMD_SHAKE_256", + "BriefDescription": "KIMD SHAKE 256", + "PublicDescription": "KIMD-SHAKE-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4177", + "EventName": "KIMD_GHASH", + "BriefDescription": "KIMD GHASH", + "PublicDescription": "KIMD-GHASH function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4178", + "EventName": "KLMD_SHA_1", + "BriefDescription": "KLMD SHA 1", + "PublicDescription": "KLMD-SHA-1 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4179", + "EventName": "KLMD_SHA_256", + "BriefDescription": "KLMD SHA 256", + "PublicDescription": "KLMD-SHA-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4180", + "EventName": "KLMD_SHA_512", + "BriefDescription": "KLMD SHA 512", + "PublicDescription": "KLMD-SHA-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4181", + "EventName": "KLMD_SHA3_224", + "BriefDescription": "KLMD SHA3 224", + "PublicDescription": "KLMD-SHA3-224 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4182", + "EventName": "KLMD_SHA3_256", + "BriefDescription": "KLMD SHA3 256", + "PublicDescription": "KLMD-SHA3-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4183", + "EventName": "KLMD_SHA3_384", + "BriefDescription": "KLMD SHA3 384", + "PublicDescription": "KLMD-SHA3-384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4184", + "EventName": "KLMD_SHA3_512", + "BriefDescription": "KLMD SHA3 512", + "PublicDescription": "KLMD-SHA3-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4185", + "EventName": "KLMD_SHAKE_128", + "BriefDescription": "KLMD SHAKE 128", + "PublicDescription": "KLMD-SHAKE-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4186", + "EventName": "KLMD_SHAKE_256", + "BriefDescription": "KLMD SHAKE 256", + "PublicDescription": "KLMD-SHAKE-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4187", + "EventName": "KMAC_DEA", + "BriefDescription": "KMAC DEA", + "PublicDescription": "KMAC-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4188", + "EventName": "KMAC_TDEA_128", + "BriefDescription": "KMAC TDEA 128", + "PublicDescription": "KMAC-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4189", + "EventName": "KMAC_TDEA_192", + "BriefDescription": "KMAC TDEA 192", + "PublicDescription": "KMAC-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4190", + "EventName": "KMAC_ENCRYPTED_DEA", + "BriefDescription": "KMAC ENCRYPTED DEA", + "PublicDescription": "KMAC-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4191", + "EventName": "KMAC_ENCRYPTED_TDEA_128", + "BriefDescription": "KMAC ENCRYPTED TDEA 128", + "PublicDescription": "KMAC-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4192", + "EventName": "KMAC_ENCRYPTED_TDEA_192", + "BriefDescription": "KMAC ENCRYPTED TDEA 192", + "PublicDescription": "KMAC-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4193", + "EventName": "KMAC_AES_128", + "BriefDescription": "KMAC AES 128", + "PublicDescription": "KMAC-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4194", + "EventName": "KMAC_AES_192", + "BriefDescription": "KMAC AES 192", + "PublicDescription": "KMAC-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4195", + "EventName": "KMAC_AES_256", + "BriefDescription": "KMAC AES 256", + "PublicDescription": "KMAC-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4196", + "EventName": "KMAC_ENCRYPTED_AES_128", + "BriefDescription": "KMAC ENCRYPTED AES 128", + "PublicDescription": "KMAC-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4197", + "EventName": "KMAC_ENCRYPTED_AES_192", + "BriefDescription": "KMAC ENCRYPTED AES 192", + "PublicDescription": "KMAC-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4198", + "EventName": "KMAC_ENCRYPTED_AES_256", + "BriefDescription": "KMAC ENCRYPTED AES 256", + "PublicDescription": "KMAC-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4199", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_DEA", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING DEA", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4200", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_128", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING TDEA 128", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4201", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_192", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING TDEA 192", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4202", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_DEA", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED DEA", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-DEA function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4203", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_128", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED TDEA 128", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4204", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_192", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED TDEA 192", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4205", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_128", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING AES 128", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4206", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_192", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING AES 192", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4207", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_256", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING AES 256", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4208", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_128", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 128", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4209", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_192", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 192", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-192 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4210", + "EventName": "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256", + "BriefDescription": "PCC COMPUTE LAST BLOCK CMAC USING ENCRYPTED AES 256", + "PublicDescription": "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4211", + "EventName": "PCC_COMPUTE_XTS_PARAMETER_USING_AES_128", + "BriefDescription": "PCC COMPUTE XTS PARAMETER USING AES 128", + "PublicDescription": "PCC-Compute-XTS-Parameter-Using-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4212", + "EventName": "PCC_COMPUTE_XTS_PARAMETER_USING_AES_256", + "BriefDescription": "PCC COMPUTE XTS PARAMETER USING AES 256", + "PublicDescription": "PCC-Compute-XTS-Parameter-Using-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4213", + "EventName": "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_128", + "BriefDescription": "PCC COMPUTE XTS PARAMETER USING ENCRYPTED AES 128", + "PublicDescription": "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4214", + "EventName": "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_256", + "BriefDescription": "PCC COMPUTE XTS PARAMETER USING ENCRYPTED AES 256", + "PublicDescription": "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4215", + "EventName": "PCC_SCALAR_MULTIPLY_P256", + "BriefDescription": "PCC SCALAR MULTIPLY P256", + "PublicDescription": "PCC-Scalar-Multiply-P256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4216", + "EventName": "PCC_SCALAR_MULTIPLY_P384", + "BriefDescription": "PCC SCALAR MULTIPLY P384", + "PublicDescription": "PCC-Scalar-Multiply-P384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4217", + "EventName": "PCC_SCALAR_MULTIPLY_P521", + "BriefDescription": "PCC SCALAR MULTIPLY P521", + "PublicDescription": "PCC-Scalar-Multiply-P521 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4218", + "EventName": "PCC_SCALAR_MULTIPLY_ED25519", + "BriefDescription": "PCC SCALAR MULTIPLY ED25519", + "PublicDescription": "PCC-Scalar-Multiply-Ed25519 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4219", + "EventName": "PCC_SCALAR_MULTIPLY_ED448", + "BriefDescription": "PCC SCALAR MULTIPLY ED448", + "PublicDescription": "PCC-Scalar-Multiply-Ed448 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4220", + "EventName": "PCC_SCALAR_MULTIPLY_X25519", + "BriefDescription": "PCC SCALAR MULTIPLY X25519", + "PublicDescription": "PCC-Scalar-Multiply-X25519 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4221", + "EventName": "PCC_SCALAR_MULTIPLY_X448", + "BriefDescription": "PCC SCALAR MULTIPLY X448", + "PublicDescription": "PCC-Scalar-Multiply-X448 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4222", + "EventName": "PRNO_SHA_512_DRNG", + "BriefDescription": "PRNO SHA 512 DRNG", + "PublicDescription": "PRNO-SHA-512-DRNG function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4223", + "EventName": "PRNO_TRNG_QUERY_RAW_TO_CONDITIONED_RATIO", + "BriefDescription": "PRNO TRNG QUERY RAW TO CONDITIONED RATIO", + "PublicDescription": "PRNO-TRNG-Query-Raw-to-Conditioned-Ratio function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4224", + "EventName": "PRNO_TRNG", + "BriefDescription": "PRNO TRNG", + "PublicDescription": "PRNO-TRNG function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4225", + "EventName": "KDSA_ECDSA_VERIFY_P256", + "BriefDescription": "KDSA ECDSA VERIFY P256", + "PublicDescription": "KDSA-ECDSA-Verify-P256 function ending with CC=0 or CC=2" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4226", + "EventName": "KDSA_ECDSA_VERIFY_P384", + "BriefDescription": "KDSA ECDSA VERIFY P384", + "PublicDescription": "KDSA-ECDSA-Verify-P384 function ending with CC=0 or CC=2" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4227", + "EventName": "KDSA_ECDSA_VERIFY_P521", + "BriefDescription": "KDSA ECDSA VERIFY P521", + "PublicDescription": "KDSA-ECDSA-Verify-P521 function ending with CC=0 or CC=2" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4228", + "EventName": "KDSA_ECDSA_SIGN_P256", + "BriefDescription": "KDSA ECDSA SIGN P256", + "PublicDescription": "KDSA-ECDSA-Sign-P256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4229", + "EventName": "KDSA_ECDSA_SIGN_P384", + "BriefDescription": "KDSA ECDSA SIGN P384", + "PublicDescription": "KDSA-ECDSA-Sign-P384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4230", + "EventName": "KDSA_ECDSA_SIGN_P521", + "BriefDescription": "KDSA ECDSA SIGN P521", + "PublicDescription": "KDSA-ECDSA-Sign-P521 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4231", + "EventName": "KDSA_ENCRYPTED_ECDSA_SIGN_P256", + "BriefDescription": "KDSA ENCRYPTED ECDSA SIGN P256", + "PublicDescription": "KDSA-Encrypted-ECDSA-Sign-P256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4232", + "EventName": "KDSA_ENCRYPTED_ECDSA_SIGN_P384", + "BriefDescription": "KDSA ENCRYPTED ECDSA SIGN P384", + "PublicDescription": "KDSA-Encrypted-ECDSA-Sign-P384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4233", + "EventName": "KDSA_ENCRYPTED_ECDSA_SIGN_P521", + "BriefDescription": "KDSA ENCRYPTED ECDSA SIGN P521", + "PublicDescription": "KDSA-Encrypted-ECDSA-Sign-P521 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4234", + "EventName": "KDSA_EDDSA_VERIFY_ED25519", + "BriefDescription": "KDSA EDDSA VERIFY ED25519", + "PublicDescription": "KDSA-EdDSA-Verify-Ed25519 function ending with CC=0 or CC=2" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4235", + "EventName": "KDSA_EDDSA_VERIFY_ED448", + "BriefDescription": "KDSA EDDSA VERIFY ED448", + "PublicDescription": "KDSA-EdDSA-Verify-Ed448 function ending with CC=0 or CC=2" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4236", + "EventName": "KDSA_EDDSA_SIGN_ED25519", + "BriefDescription": "KDSA EDDSA SIGN ED25519", + "PublicDescription": "KDSA-EdDSA-Sign-Ed25519 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4237", + "EventName": "KDSA_EDDSA_SIGN_ED448", + "BriefDescription": "KDSA EDDSA SIGN ED448", + "PublicDescription": "KDSA-EdDSA-Sign-Ed448 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4238", + "EventName": "KDSA_ENCRYPTED_EDDSA_SIGN_ED25519", + "BriefDescription": "KDSA ENCRYPTED EDDSA SIGN ED25519", + "PublicDescription": "KDSA-Encrypted-EdDSA-Sign-Ed25519 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4239", + "EventName": "KDSA_ENCRYPTED_EDDSA_SIGN_ED448", + "BriefDescription": "KDSA ENCRYPTED EDDSA SIGN ED448", + "PublicDescription": "KDSA-Encrypted-EdDSA-Sign-Ed448 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4240", + "EventName": "PCKMO_ENCRYPT_DEA_KEY", + "BriefDescription": "PCKMO ENCRYPT DEA KEY", + "PublicDescription": "PCKMO-Encrypt-DEA-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4241", + "EventName": "PCKMO_ENCRYPT_TDEA_128_KEY", + "BriefDescription": "PCKMO ENCRYPT TDEA 128 KEY", + "PublicDescription": "PCKMO-Encrypt-TDEA-128-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4242", + "EventName": "PCKMO_ENCRYPT_TDEA_192_KEY", + "BriefDescription": "PCKMO ENCRYPT TDEA 192 KEY", + "PublicDescription": "PCKMO-Encrypt-TDEA-192-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4243", + "EventName": "PCKMO_ENCRYPT_AES_128_KEY", + "BriefDescription": "PCKMO ENCRYPT AES 128 KEY", + "PublicDescription": "PCKMO-Encrypt-AES-128-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4244", + "EventName": "PCKMO_ENCRYPT_AES_192_KEY", + "BriefDescription": "PCKMO ENCRYPT AES 192 KEY", + "PublicDescription": "PCKMO-Encrypt-AES-192-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4245", + "EventName": "PCKMO_ENCRYPT_AES_256_KEY", + "BriefDescription": "PCKMO ENCRYPT AES 256 KEY", + "PublicDescription": "PCKMO-Encrypt-AES-256-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4246", + "EventName": "PCKMO_ENCRYPT_ECC_P256_KEY", + "BriefDescription": "PCKMO ENCRYPT ECC P256 KEY", + "PublicDescription": "PCKMO-Encrypt-ECC-P256-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4247", + "EventName": "PCKMO_ENCRYPT_ECC_P384_KEY", + "BriefDescription": "PCKMO ENCRYPT ECC P384 KEY", + "PublicDescription": "PCKMO-Encrypt-ECC-P384-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4248", + "EventName": "PCKMO_ENCRYPT_ECC_P521_KEY", + "BriefDescription": "PCKMO ENCRYPT ECC P521 KEY", + "PublicDescription": "PCKMO-Encrypt-ECC-P521-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4249", + "EventName": "PCKMO_ENCRYPT_ECC_ED25519_KEY", + "BriefDescription": "PCKMO ENCRYPT ECC ED25519 KEY", + "PublicDescription": "PCKMO-Encrypt-ECC-Ed25519-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4250", + "EventName": "PCKMO_ENCRYPT_ECC_ED448_KEY", + "BriefDescription": "PCKMO ENCRYPT ECC ED448 KEY", + "PublicDescription": "PCKMO-Encrypt-ECC-Ed448-key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4251", + "EventName": "IBM_RESERVED_155", + "BriefDescription": "IBM RESERVED_155", + "PublicDescription": "Reserved for IBM use" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4252", + "EventName": "IBM_RESERVED_156", + "BriefDescription": "IBM RESERVED_156", + "PublicDescription": "Reserved for IBM use" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4253", + "EventName": "KM_FULL_XTS_AES_128", + "BriefDescription": "KM FULL XTS AES 128", + "PublicDescription": "KM-Full-XTS-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4254", + "EventName": "KM_FULL_XTS_AES_256", + "BriefDescription": "KM FULL XTS AES 256", + "PublicDescription": "KM-Full-XTS-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4255", + "EventName": "KM_FULL_XTS_ENCRYPTED_AES_128", + "BriefDescription": "KM FULL XTS ENCRYPTED AES 128", + "PublicDescription": "KM-Full-XTS-Encrypted-AES-128 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4256", + "EventName": "KM_FULL_XTS_ENCRYPTED_AES_256", + "BriefDescription": "KM FULL XTS ENCRYPTED AES 256", + "PublicDescription": "KM-FULL-XTS-ENCRYPTED-AES-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4257", + "EventName": "KMAC_HMAC_SHA_224", + "BriefDescription": "KMAC HMAC SHA 224", + "PublicDescription": "KMAC-HMAC-SHA-224 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4258", + "EventName": "KMAC_HMAC_SHA_256", + "BriefDescription": "KMAC HMAC SHA 256", + "PublicDescription": "KMAC-HMAC-SHA-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4259", + "EventName": "KMAC_HMAC_SHA_384", + "BriefDescription": "KMAC HMAC SHA 384", + "PublicDescription": "KMAC-HMAC-SHA-384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4260", + "EventName": "KMAC_HMAC_SHA_512", + "BriefDescription": "KMAC HMAC SHA 512", + "PublicDescription": "KMAC-HMAC-SHA-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4261", + "EventName": "KMAC_HMAC_ENCRYPTED_SHA_224", + "BriefDescription": "KMAC HMAC ENCRYPTED SHA 224", + "PublicDescription": "KMAC-HMAC-Encrypted-SHA-224 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4262", + "EventName": "KMAC_HMAC_ENCRYPTED_SHA_256", + "BriefDescription": "KMAC HMAC ENCRYPTED SHA 256", + "PublicDescription": "KMAC-HMAC-Encrypted-SHA-256 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4263", + "EventName": "KMAC_HMAC_ENCRYPTED_SHA_384", + "BriefDescription": "KMAC HMAC ENCRYPTED SHA 384", + "PublicDescription": "KMAC-HMAC-Encrypted-SHA-384 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4264", + "EventName": "KMAC_HMAC_ENCRYPTED_SHA_512", + "BriefDescription": "KMAC HMAC ENCRYPTED SHA 512", + "PublicDescription": "KMAC-HMAC-Encrypted-SHA-512 function ending with CC=0" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4265", + "EventName": "PCKMO_ENCRYPT_HMAC_512_KEY", + "BriefDescription": "PCKMO ENCRYPT HMAC 512 KEY", + "PublicDescription": "PCKMO-Encrypt-HMAC-512-Key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4266", + "EventName": "PCKMO_ENCRYPT_HMAC_1024_KEY", + "BriefDescription": "PCKMO ENCRYPT HMAC 1024 KEY", + "PublicDescription": "PCKMO-Encrypt-HMAC-1024-Key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4267", + "EventName": "PCKMO_ENCRYPT_AES_XTS_128", + "BriefDescription": "PCKMO ENCRYPT AES XTS Double Key 128", + "PublicDescription": "PCKMO-ENCRYPT-AES-XTS-128 Double Key function" + }, + { + "Unit": "PAI-CRYPTO", + "EventCode": "4268", + "EventName": "PCKMO_ENCRYPT_AES_XTS_256", + "BriefDescription": "PCKMO ENCRYPT AES XTS Double Key 256", + "PublicDescription": "PCKMO-ENCRYPT-AES-XTS-256 Double Key function" + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/pai_ext.json b/tools/perf/pmu-events/arch/s390/cf_z17/pai_ext.json new file mode 100644 index 000000000000..935e9f5763b4 --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/pai_ext.json @@ -0,0 +1,261 @@ +[ + { + "Unit": "PAI-EXT", + "EventCode": "6144", + "EventName": "NNPA_ALL", + "BriefDescription": "NNPA ALL", + "PublicDescription": "Sums of all non zero NNPA counters" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6145", + "EventName": "NNPA_ADD", + "BriefDescription": "NNPA ADD function", + "PublicDescription": "NNPA-ADD function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6146", + "EventName": "NNPA_SUB", + "BriefDescription": "NNPA SUB function", + "PublicDescription": "NNPA-SUB function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6147", + "EventName": "NNPA_MUL", + "BriefDescription": "NNPA MUL function", + "PublicDescription": "NNPA-MUL function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6148", + "EventName": "NNPA_DIV", + "BriefDescription": "NNPA_DIV function", + "PublicDescription": "NNPA-DIV function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6149", + "EventName": "NNPA_MIN", + "BriefDescription": "NNPA MIN function", + "PublicDescription": "NNPA-MIN function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6150", + "EventName": "NNPA_MAX", + "BriefDescription": "NNPA MAX function", + "PublicDescription": "NNPA-MAX function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6151", + "EventName": "NNPA_LOG", + "BriefDescription": "NNPA LOG function", + "PublicDescription": "NNPA Log function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6152", + "EventName": "NNPA_EXP", + "BriefDescription": "NNPA EXP function", + "PublicDescription": "NNPA-EXP function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6153", + "EventName": "NNPA_IBM_RESERVED_9", + "BriefDescription": "Reserved for IBM use", + "PublicDescription": "Reserved for IBM use" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6154", + "EventName": "NNPA_RELU", + "BriefDescription": "NNPA RELU function", + "PublicDescription": "NNPA-RELU function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6155", + "EventName": "NNPA_TANH", + "BriefDescription": "NNPA TANH function", + "PublicDescription": "NNPA-TANH function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6156", + "EventName": "NNPA_SIGMOID", + "BriefDescription": "NNPA SIGMOID function", + "PublicDescription": "NNPA-SIGMOID function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6157", + "EventName": "NNPA_SOFTMAX", + "BriefDescription": "NNPA SOFTMAX function", + "PublicDescription": "NNPA-SOFTMAX function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6158", + "EventName": "NNPA_BATCHNORM", + "BriefDescription": "NNPA BATCHNORM function", + "PublicDescription": "NNPA-BATCHNORM function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6159", + "EventName": "NNPA_MAXPOOL2D", + "BriefDescription": "NNPA MAXPOOL2D function", + "PublicDescription": "NNPA-MAXPOOL2D function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6160", + "EventName": "NNPA_AVGPOOL2D", + "BriefDescription": "NNPA_AVGPOOL2D function", + "PublicDescription": "NNPA-AVGPOOL2D function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6161", + "EventName": "NNPA_LSTMACT", + "BriefDescription": "NNPA LSTMACT function", + "PublicDescription": "NNPA-LSTMACT function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6162", + "EventName": "NNPA_GRUACT", + "BriefDescription": "NNPA GRUACT function", + "PublicDescription": "NNPA-GRUACT function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6163", + "EventName": "NNPA_CONVOLUTION", + "BriefDescription": "NNPA CONVOLUTION function", + "PublicDescription": "NNPA-CONVOLUTION function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6164", + "EventName": "NNPA_MATMUL_OP", + "BriefDescription": "NNPA MATMUL OP function", + "PublicDescription": "NNPA-MATMUL-OP function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6165", + "EventName": "NNPA_MATMUL_OP_BCAST23", + "BriefDescription": "NNPA MATMUL OP BCAST23 function", + "PublicDescription": "NNPA-MATMUL-OP-BCAST23 function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6166", + "EventName": "NNPA_SMALLBATCH", + "BriefDescription": "NNPA Counter 22", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6167", + "EventName": "NNPA_LARGEDIM", + "BriefDescription": "NNPA Counter 23", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6168", + "EventName": "NNPA_SMALLTENSOR", + "BriefDescription": "NNPA Counter 24", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6169", + "EventName": "NNPA_1MFRAME", + "BriefDescription": "NNPA Counter 25", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6170", + "EventName": "NNPA_2GFRAME", + "BriefDescription": "NNPA Counter 26", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6171", + "EventName": "NNPA_ACCESSEXCEPT", + "BriefDescription": "NNPA Counter 27", + "PublicDescription": "NNPA function with conditions as described in Principles of Operation" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6172", + "EventName": "NNPA_TRANSFORM", + "BriefDescription": "NNPA-TRANSFORM function", + "PublicDescription": "NNPA-TRANSFORM function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6173", + "EventName": "NNPA_GELU", + "BriefDescription": "NNPA-GELU function", + "PublicDescription": "NNPA-GELU function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6174", + "EventName": "NNPA_MOMENTS", + "BriefDescription": "NNPA-MOMENTS function", + "PublicDescription": "NNPA-MOMENTS function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6175", + "EventName": "NNPA_LAYERNORM", + "BriefDescription": "NNPA-LAYERNORM function", + "PublicDescription": "NNPA-LAYERNORM function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6176", + "EventName": "NNPA_MATMUL_OP_BCAST1", + "BriefDescription": "NNPA-MATMUL_OP_BCAST1 function", + "PublicDescription": "NNPA-MATMUL-OP-BCAST1 function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6177", + "EventName": "NNPA_SQRT", + "BriefDescription": "NNPA-SQRT function", + "PublicDescription": "NNPA-SQRT function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6178", + "EventName": "NNPA_INVSQRT", + "BriefDescription": "NNPA-INVSQRT function", + "PublicDescription": "NNPA-INVSQRT function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6179", + "EventName": "NNPA_NORM", + "BriefDescription": "NNPA-NORM function", + "PublicDescription": "NNPA-NORM function ending with CC=0" + }, + { + "Unit": "PAI-EXT", + "EventCode": "6180", + "EventName": "NNPA_REDUCE", + "BriefDescription": "NNPA-REDUCE function", + "PublicDescription": "NNPA-REDUCE function ending with CC=0" + } +] diff --git a/tools/perf/pmu-events/arch/s390/cf_z17/transaction.json b/tools/perf/pmu-events/arch/s390/cf_z17/transaction.json new file mode 100644 index 000000000000..74df533c8b6f --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z17/transaction.json @@ -0,0 +1,72 @@ +[ + { + "BriefDescription": "Transaction count", + "MetricName": "transaction", + "MetricExpr": "TX_C_TEND + TX_NC_TEND + TX_NC_TABORT + TX_C_TABORT_SPECIAL + TX_C_TABORT_NO_SPECIAL if has_event(TX_C_TEND) else 0" + }, + { + "BriefDescription": "Cycles per Instruction", + "MetricName": "cpi", + "MetricExpr": "CPU_CYCLES / INSTRUCTIONS if has_event(INSTRUCTIONS) else 0" + }, + { + "BriefDescription": "Problem State Instruction Ratio", + "MetricName": "prbstate", + "MetricExpr": "(PROBLEM_STATE_INSTRUCTIONS / INSTRUCTIONS) * 100 if has_event(INSTRUCTIONS) else 0" + }, + { + "BriefDescription": "Level One Miss per 100 Instructions", + "MetricName": "l1mp", + "MetricExpr": "((L1I_DIR_WRITES + L1D_DIR_WRITES) / INSTRUCTIONS) * 100 if has_event(INSTRUCTIONS) else 0" + }, + { + "BriefDescription": "Percentage sourced from Level 2 cache", + "MetricName": "l2p", + "MetricExpr": "((DCW_REQ + DCW_REQ_IV + ICW_REQ + ICW_REQ_IV) / (L1I_DIR_WRITES + L1D_DIR_WRITES)) * 100 if has_event(DCW_REQ) else 0" + }, + { + "BriefDescription": "Percentage sourced from Level 3 on same chip cache", + "MetricName": "l3p", + "MetricExpr": "((DCW_REQ_CHIP_HIT + DCW_ON_CHIP + DCW_ON_CHIP_IV + DCW_ON_CHIP_CHIP_HIT + ICW_REQ_CHIP_HIT + ICW_ON_CHIP + ICW_ON_CHIP_IV + ICW_ON_CHIP_CHIP_HIT) / (L1I_DIR_WRITES + L1D_DIR_WRITES)) * 100 if has_event(DCW_REQ_CHIP_HIT) else 0" + }, + { + "BriefDescription": "Percentage sourced from Level 4 Local cache on same drawer", + "MetricName": "l4lp", + "MetricExpr": "((DCW_REQ_DRAWER_HIT + DCW_ON_CHIP_DRAWER_HIT + DCW_ON_MODULE + DCW_ON_DRAWER + IDCW_ON_MODULE_IV + IDCW_ON_MODULE_CHIP_HIT + IDCW_ON_MODULE_DRAWER_HIT + IDCW_ON_DRAWER_IV + IDCW_ON_DRAWER_CHIP_HIT + IDCW_ON_DRAWER_DRAWER_HIT + ICW_REQ_DRAWER_HIT + ICW_ON_CHIP_DRAWER_HIT + ICW_ON_MODULE + ICW_ON_DRAWER) / (L1I_DIR_WRITES + L1D_DIR_WRITES)) * 100 if has_event(DCW_REQ_DRAWER_HIT) else 0" + }, + { + "BriefDescription": "Percentage sourced from Level 4 Remote cache on different book", + "MetricName": "l4rp", + "MetricExpr": "((DCW_OFF_DRAWER + IDCW_OFF_DRAWER_IV + IDCW_OFF_DRAWER_CHIP_HIT + IDCW_OFF_DRAWER_DRAWER_HIT + ICW_OFF_DRAWER) / (L1I_DIR_WRITES + L1D_DIR_WRITES)) * 100 if has_event(DCW_OFF_DRAWER) else 0" + }, + { + "BriefDescription": "Percentage sourced from memory", + "MetricName": "memp", + "MetricExpr": "((DCW_ON_CHIP_MEMORY + DCW_ON_MODULE_MEMORY + DCW_ON_DRAWER_MEMORY + DCW_OFF_DRAWER_MEMORY) / (L1I_DIR_WRITES + L1D_DIR_WRITES)) * 100 if has_event(DCW_ON_CHIP_MEMORY) else 0" + }, + { + "BriefDescription": "Cycles per Instructions from Finite cache/memory", + "MetricName": "finite_cpi", + "MetricExpr": "L1C_TLB2_MISSES / INSTRUCTIONS if has_event(L1C_TLB2_MISSES) else 0" + }, + { + "BriefDescription": "Estimated Instruction Complexity CPI infinite Level 1", + "MetricName": "est_cpi", + "MetricExpr": "(CPU_CYCLES / INSTRUCTIONS) - (L1C_TLB2_MISSES / INSTRUCTIONS) if has_event(INSTRUCTIONS) else 0" + }, + { + "BriefDescription": "Estimated Sourcing Cycles per Level 1 Miss", + "MetricName": "scpl1m", + "MetricExpr": "L1C_TLB2_MISSES / (L1I_DIR_WRITES + L1D_DIR_WRITES) if has_event(L1C_TLB2_MISSES) else 0" + }, + { + "BriefDescription": "Estimated TLB CPU percentage of Total CPU", + "MetricName": "tlb_percent", + "MetricExpr": "((DTLB2_MISSES + ITLB2_MISSES) / CPU_CYCLES) * (L1C_TLB2_MISSES / (L1I_PENALTY_CYCLES + L1D_PENALTY_CYCLES)) * 100 if has_event(CPU_CYCLES) else 0" + }, + { + "BriefDescription": "Estimated Cycles per TLB Miss", + "MetricName": "tlb_miss", + "MetricExpr": "((DTLB2_MISSES + ITLB2_MISSES) / (DTLB2_WRITES + ITLB2_WRITES)) * (L1C_TLB2_MISSES / (L1I_PENALTY_CYCLES + L1D_PENALTY_CYCLES)) if has_event(DTLB2_MISSES) else 0" + } +] diff --git a/tools/perf/pmu-events/arch/s390/mapfile.csv b/tools/perf/pmu-events/arch/s390/mapfile.csv index b22648d12751..6fdede50e7b2 100644 --- a/tools/perf/pmu-events/arch/s390/mapfile.csv +++ b/tools/perf/pmu-events/arch/s390/mapfile.csv @@ -6,3 +6,4 @@ Family-model,Version,Filename,EventType ^IBM.390[67].*[13]\.[1-5].[[:xdigit:]]+$,3,cf_z14,core ^IBM.856[12].*3\.6.[[:xdigit:]]+$,3,cf_z15,core ^IBM.393[12].*$,3,cf_z16,core +^IBM.917[56].*$,3,cf_z17,core diff --git a/tools/perf/pmu-events/arch/x86/alderlake/cache.json b/tools/perf/pmu-events/arch/x86/alderlake/cache.json index c2802fbb853b..5461576dafc7 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/cache.json @@ -728,7 +728,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.DRAM_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in DRAM. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x80", "Unit": "cpu_atom" @@ -739,7 +738,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.HITM", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache, in which a snoop was required and modified data was forwarded from another core or module. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x20", "Unit": "cpu_atom" @@ -750,7 +748,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L1_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L1 data cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x1", "Unit": "cpu_atom" @@ -761,7 +758,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L1_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L1 data cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x8", "Unit": "cpu_atom" @@ -772,7 +768,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L2_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L2 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x2", "Unit": "cpu_atom" @@ -783,7 +778,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L2_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L2 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x10", "Unit": "cpu_atom" @@ -794,7 +788,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x4", "Unit": "cpu_atom" @@ -805,7 +798,6 @@ "Data_LA": "1", "EventCode": "0xd2", "EventName": "MEM_LOAD_UOPS_RETIRED_MISC.HIT_E_F", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache, in which a snoop was required, and non-modified data was forwarded. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x40", "Unit": "cpu_atom" @@ -816,7 +808,6 @@ "Data_LA": "1", "EventCode": "0xd2", "EventName": "MEM_LOAD_UOPS_RETIRED_MISC.L3_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L3 cache. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x20", "Unit": "cpu_atom" @@ -873,7 +864,7 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.ALL_LOADS", - "PublicDescription": "Counts the total number of load uops retired. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of load uops retired.", "SampleAfterValue": "200003", "UMask": "0x81", "Unit": "cpu_atom" @@ -884,111 +875,111 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.ALL_STORES", - "PublicDescription": "Counts the total number of store uops retired. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of store uops retired.", "SampleAfterValue": "200003", "UMask": "0x82", "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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" @@ -999,7 +990,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOCK_LOADS", - "PublicDescription": "Counts the number of load uops retired that performed one or more locks. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x21", "Unit": "cpu_atom" @@ -1010,7 +1000,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.SPLIT_LOADS", - "PublicDescription": "Counts the number of retired split load uops. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x41", "Unit": "cpu_atom" @@ -1021,7 +1010,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS", - "PublicDescription": "Counts the total number of load and store uops retired that missed in the second level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x13", "Unit": "cpu_atom" @@ -1032,7 +1020,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS_LOADS", - "PublicDescription": "Counts the number of load ops retired that miss in the second Level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x11", "Unit": "cpu_atom" @@ -1043,7 +1030,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS_STORES", - "PublicDescription": "Counts the number of store ops retired that miss in the second level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x12", "Unit": "cpu_atom" @@ -1054,7 +1040,7 @@ "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. Available PDIST counters: 0", + "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" @@ -1478,12 +1464,12 @@ "Unit": "cpu_core" }, { - "BriefDescription": "For every cycle where the core is waiting on at least 1 outstanding Demand RFO request, increments by 1.", + "BriefDescription": "Cycles with offcore outstanding demand rfo reads transactions in SuperQueue (SQ), queue to uncore.", "Counter": "0,1,2,3", "CounterMask": "1", "EventCode": "0x20", "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_RFO", - "PublicDescription": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_RFO Available PDIST counters: 0", + "PublicDescription": "Counts the number of offcore outstanding demand rfo Reads transactions in the super queue every cycle. The 'Offcore outstanding' state of the transaction lasts from the L2 miss until the sending transaction completion to requestor (SQ deallocation). See the corresponding Umask under OFFCORE_REQUESTS. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x4", "Unit": "cpu_core" diff --git a/tools/perf/pmu-events/arch/x86/alderlake/floating-point.json b/tools/perf/pmu-events/arch/x86/alderlake/floating-point.json index ce570b96360a..d01f1b163ed8 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/floating-point.json @@ -213,7 +213,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.FPDIV", - "PublicDescription": "Counts the number of floating point divide uops retired (x87 and SSE, including x87 sqrt). Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x8", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/alderlake/other.json b/tools/perf/pmu-events/arch/x86/alderlake/other.json index e4e75b088ccc..5f64138edfe4 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/other.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/other.json @@ -55,7 +55,6 @@ "Deprecated": "1", "EventCode": "0xe4", "EventName": "LBR_INSERTS.ANY", - "PublicDescription": "This event is deprecated. [This event is alias to MISC_RETIRED.LBR_INSERTS] Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json index 7e0e33792c45..48ef2a8cc49a 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json @@ -128,7 +128,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.ALL_BRANCHES", - "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for.", "SampleAfterValue": "200003", "Unit": "cpu_atom" }, @@ -147,7 +147,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf9", "Unit": "cpu_atom" @@ -157,7 +156,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.COND", - "PublicDescription": "Counts the number of retired JCC (Jump on Conditional Code) branch instructions retired, includes both taken and not taken branches. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e", "Unit": "cpu_atom" @@ -187,7 +185,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.COND_TAKEN", - "PublicDescription": "Counts the number of taken JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe", "Unit": "cpu_atom" @@ -207,7 +204,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.FAR_BRANCH", - "PublicDescription": "Counts the number of far branch instructions retired, includes far jump, far call and return, and interrupt call and return. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xbf", "Unit": "cpu_atom" @@ -227,7 +223,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT", - "PublicDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb", "Unit": "cpu_atom" @@ -247,7 +242,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT_CALL", - "PublicDescription": "Counts the number of near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb", "Unit": "cpu_atom" @@ -258,7 +252,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.IND_CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb", "Unit": "cpu_atom" @@ -269,7 +262,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e", "Unit": "cpu_atom" @@ -279,7 +271,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_CALL", - "PublicDescription": "Counts the number of near CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf9", "Unit": "cpu_atom" @@ -299,7 +290,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_RETURN", - "PublicDescription": "Counts the number of near RET branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7", "Unit": "cpu_atom" @@ -319,7 +309,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_TAKEN", - "PublicDescription": "Counts the number of near taken branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xc0", "Unit": "cpu_atom" @@ -340,7 +329,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NON_RETURN_IND", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb", "Unit": "cpu_atom" @@ -350,7 +338,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.REL_CALL", - "PublicDescription": "Counts the number of near relative CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfd", "Unit": "cpu_atom" @@ -361,7 +348,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.RETURN", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_RETURN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7", "Unit": "cpu_atom" @@ -372,7 +358,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.TAKEN_JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND_TAKEN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe", "Unit": "cpu_atom" @@ -382,7 +367,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.ALL_BRANCHES", - "PublicDescription": "Counts the total number of mispredicted branch instructions retired. All branch type instructions are accounted for. Prediction of the branch target address enables the processor to begin executing instructions before the non-speculative execution path is known. The branch prediction unit (BPU) predicts the target address based on the instruction pointer (IP) of the branch and on the execution path through which execution reached this IP. A branch misprediction occurs when the prediction is wrong, and results in discarding all instructions executed in the speculative path and re-fetching from the correct path. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of mispredicted branch instructions retired. All branch type instructions are accounted for. Prediction of the branch target address enables the processor to begin executing instructions before the non-speculative execution path is known. The branch prediction unit (BPU) predicts the target address based on the instruction pointer (IP) of the branch and on the execution path through which execution reached this IP. A branch misprediction occurs when the prediction is wrong, and results in discarding all instructions executed in the speculative path and re-fetching from the correct path.", "SampleAfterValue": "200003", "Unit": "cpu_atom" }, @@ -400,7 +385,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.COND", - "PublicDescription": "Counts the number of mispredicted JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e", "Unit": "cpu_atom" @@ -430,7 +414,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.COND_TAKEN", - "PublicDescription": "Counts the number of mispredicted taken JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe", "Unit": "cpu_atom" @@ -450,7 +433,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.INDIRECT", - "PublicDescription": "Counts the number of mispredicted near indirect JMP and near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb", "Unit": "cpu_atom" @@ -470,7 +452,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.INDIRECT_CALL", - "PublicDescription": "Counts the number of mispredicted near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb", "Unit": "cpu_atom" @@ -491,7 +472,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.IND_CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb", "Unit": "cpu_atom" @@ -502,7 +482,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.COND Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e", "Unit": "cpu_atom" @@ -512,7 +491,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.NEAR_TAKEN", - "PublicDescription": "Counts the number of mispredicted near taken branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x80", "Unit": "cpu_atom" @@ -533,7 +511,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.NON_RETURN_IND", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb", "Unit": "cpu_atom" @@ -553,7 +530,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.RETURN", - "PublicDescription": "Counts the number of mispredicted near RET branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7", "Unit": "cpu_atom" @@ -564,7 +540,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.TAKEN_JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.COND_TAKEN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe", "Unit": "cpu_atom" @@ -934,7 +909,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc0", "EventName": "INST_RETIRED.ANY_P", - "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 a programmable general purpose performance counter. Available PDIST counters: 0", + "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 a programmable general purpose performance counter.", "SampleAfterValue": "2000003", "Unit": "cpu_atom" }, @@ -1126,7 +1101,6 @@ "Deprecated": "1", "EventCode": "0x03", "EventName": "LD_BLOCKS.4K_ALIAS", - "PublicDescription": "This event is deprecated. Refer to new event LD_BLOCKS.ADDRESS_ALIAS Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x4", "Unit": "cpu_atom" @@ -1136,7 +1110,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0x03", "EventName": "LD_BLOCKS.ADDRESS_ALIAS", - "PublicDescription": "Counts the number of retired loads that are blocked because it initially appears to be store forward blocked, but subsequently is shown not to be blocked based on 4K alias check. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x4", "Unit": "cpu_atom" @@ -1156,7 +1129,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0x03", "EventName": "LD_BLOCKS.DATA_UNKNOWN", - "PublicDescription": "Counts the number of retired loads that are blocked because its address exactly matches an older store whose data is not ready. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_atom" @@ -1186,7 +1158,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1", "Unit": "cpu_core" @@ -1306,7 +1278,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xe4", "EventName": "MISC_RETIRED.LBR_INSERTS", - "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. This event is PDIR on GP0 and NPEBS on all other GPs [This event is alias to LBR_INSERTS.ANY] Available PDIST counters: 0", + "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. This event is PDIR on GP0 and NPEBS on all other GPs [This event is alias to LBR_INSERTS.ANY]", "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_atom" @@ -1681,7 +1653,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "TOPDOWN_RETIRING.ALL", - "PublicDescription": "Counts the total number of consumed retirement slots. Available PDIST counters: 0", "SampleAfterValue": "1000003", "Unit": "cpu_atom" }, @@ -1933,7 +1904,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.ALL", - "PublicDescription": "Counts the total number of uops retired. Available PDIST counters: 0", "SampleAfterValue": "2000003", "Unit": "cpu_atom" }, @@ -1963,7 +1933,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.IDIV", - "PublicDescription": "Counts the number of integer divide uops retired. Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x10", "Unit": "cpu_atom" @@ -1973,7 +1942,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.MS", - "PublicDescription": "Counts the number of uops that are from complex flows issued by the Microcode Sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows. Available PDIST counters: 0", + "PublicDescription": "Counts the number of uops that are from complex flows issued by the Microcode Sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows.", "SampleAfterValue": "2000003", "UMask": "0x1", "Unit": "cpu_atom" @@ -2030,7 +1999,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.X87", - "PublicDescription": "Counts the number of x87 uops retired, includes those in MS flows. Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x2", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json index 3d15275eca61..ffbbd08acc68 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json @@ -266,7 +266,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x13", "Unit": "cpu_atom" @@ -278,7 +277,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS_LOADS", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS_LOADS Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x11", "Unit": "cpu_atom" @@ -290,7 +288,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS_STORES", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS_STORES Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x12", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json index bf691aee1ef4..669f4979b651 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json @@ -118,7 +118,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.DRAM_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in DRAM. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x80" }, @@ -128,7 +127,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.HITM", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache, in which a snoop was required and modified data was forwarded from another core or module. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x20" }, @@ -138,7 +136,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L1_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L1 data cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x1" }, @@ -148,7 +145,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L1_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L1 data cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x8" }, @@ -158,7 +154,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L2_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L2 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x2" }, @@ -168,7 +163,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L2_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L2 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x10" }, @@ -178,7 +172,6 @@ "Data_LA": "1", "EventCode": "0xd1", "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x4" }, @@ -188,7 +181,6 @@ "Data_LA": "1", "EventCode": "0xd2", "EventName": "MEM_LOAD_UOPS_RETIRED_MISC.HIT_E_F", - "PublicDescription": "Counts the number of load uops retired that hit in the L3 cache, in which a snoop was required, and non-modified data was forwarded. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x40" }, @@ -198,7 +190,6 @@ "Data_LA": "1", "EventCode": "0xd2", "EventName": "MEM_LOAD_UOPS_RETIRED_MISC.L3_MISS", - "PublicDescription": "Counts the number of load uops retired that miss in the L3 cache. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x20" }, @@ -240,7 +231,7 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.ALL_LOADS", - "PublicDescription": "Counts the total number of load uops retired. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of load uops retired.", "SampleAfterValue": "200003", "UMask": "0x81" }, @@ -250,103 +241,103 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.ALL_STORES", - "PublicDescription": "Counts the total number of store uops retired. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of store uops retired.", "SampleAfterValue": "200003", "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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.", - "Counter": "0,1", + "Counter": "0,1,2,3,4,5", "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. Available PDIST counters: 0", + "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" }, @@ -356,7 +347,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOCK_LOADS", - "PublicDescription": "Counts the number of load uops retired that performed one or more locks. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x21" }, @@ -366,7 +356,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.SPLIT_LOADS", - "PublicDescription": "Counts the number of retired split load uops. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x41" }, @@ -376,7 +365,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS", - "PublicDescription": "Counts the total number of load and store uops retired that missed in the second level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x13" }, @@ -386,7 +374,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS_LOADS", - "PublicDescription": "Counts the number of load ops retired that miss in the second Level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x11" }, @@ -396,7 +383,6 @@ "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STLB_MISS_STORES", - "PublicDescription": "Counts the number of store ops retired that miss in the second level TLB. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x12" }, @@ -406,7 +392,7 @@ "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. Available PDIST counters: 0", + "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/floating-point.json b/tools/perf/pmu-events/arch/x86/alderlaken/floating-point.json index f44da31ff1f1..ed963fcb6485 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/floating-point.json @@ -29,7 +29,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.FPDIV", - "PublicDescription": "Counts the number of floating point divide uops retired (x87 and SSE, including x87 sqrt). Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x8" } diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/other.json b/tools/perf/pmu-events/arch/x86/alderlaken/other.json index 8c2b5a284f2a..144d7b06f240 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/other.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/other.json @@ -5,7 +5,6 @@ "Deprecated": "1", "EventCode": "0xe4", "EventName": "LBR_INSERTS.ANY", - "PublicDescription": "This event is deprecated. [This event is alias to MISC_RETIRED.LBR_INSERTS] Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json index 9616bf0e9f1f..1dd61baec1a9 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json @@ -54,7 +54,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.ALL_BRANCHES", - "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for.", "SampleAfterValue": "200003" }, { @@ -63,7 +63,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf9" }, @@ -72,7 +71,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.COND", - "PublicDescription": "Counts the number of retired JCC (Jump on Conditional Code) branch instructions retired, includes both taken and not taken branches. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e" }, @@ -81,7 +79,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.COND_TAKEN", - "PublicDescription": "Counts the number of taken JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe" }, @@ -90,7 +87,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.FAR_BRANCH", - "PublicDescription": "Counts the number of far branch instructions retired, includes far jump, far call and return, and interrupt call and return. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xbf" }, @@ -99,7 +95,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT", - "PublicDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb" }, @@ -108,7 +103,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT_CALL", - "PublicDescription": "Counts the number of near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb" }, @@ -118,7 +112,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.IND_CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb" }, @@ -128,7 +121,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e" }, @@ -137,7 +129,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_CALL", - "PublicDescription": "Counts the number of near CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf9" }, @@ -146,7 +137,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_RETURN", - "PublicDescription": "Counts the number of near RET branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7" }, @@ -155,7 +145,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_TAKEN", - "PublicDescription": "Counts the number of near taken branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xc0" }, @@ -165,7 +154,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NON_RETURN_IND", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb" }, @@ -174,7 +162,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.REL_CALL", - "PublicDescription": "Counts the number of near relative CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfd" }, @@ -184,7 +171,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.RETURN", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_RETURN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7" }, @@ -194,7 +180,6 @@ "Deprecated": "1", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.TAKEN_JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND_TAKEN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe" }, @@ -203,7 +188,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.ALL_BRANCHES", - "PublicDescription": "Counts the total number of mispredicted branch instructions retired. All branch type instructions are accounted for. Prediction of the branch target address enables the processor to begin executing instructions before the non-speculative execution path is known. The branch prediction unit (BPU) predicts the target address based on the instruction pointer (IP) of the branch and on the execution path through which execution reached this IP. A branch misprediction occurs when the prediction is wrong, and results in discarding all instructions executed in the speculative path and re-fetching from the correct path. Available PDIST counters: 0", + "PublicDescription": "Counts the total number of mispredicted branch instructions retired. All branch type instructions are accounted for. Prediction of the branch target address enables the processor to begin executing instructions before the non-speculative execution path is known. The branch prediction unit (BPU) predicts the target address based on the instruction pointer (IP) of the branch and on the execution path through which execution reached this IP. A branch misprediction occurs when the prediction is wrong, and results in discarding all instructions executed in the speculative path and re-fetching from the correct path.", "SampleAfterValue": "200003" }, { @@ -211,7 +196,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.COND", - "PublicDescription": "Counts the number of mispredicted JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e" }, @@ -220,7 +204,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.COND_TAKEN", - "PublicDescription": "Counts the number of mispredicted taken JCC (Jump on Conditional Code) branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe" }, @@ -229,7 +212,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.INDIRECT", - "PublicDescription": "Counts the number of mispredicted near indirect JMP and near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb" }, @@ -238,7 +220,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.INDIRECT_CALL", - "PublicDescription": "Counts the number of mispredicted near indirect CALL branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb" }, @@ -248,7 +229,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.IND_CALL", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT_CALL Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfb" }, @@ -258,7 +238,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.COND Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x7e" }, @@ -267,7 +246,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.NEAR_TAKEN", - "PublicDescription": "Counts the number of mispredicted near taken branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x80" }, @@ -277,7 +255,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.NON_RETURN_IND", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xeb" }, @@ -286,7 +263,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.RETURN", - "PublicDescription": "Counts the number of mispredicted near RET branch instructions retired. Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xf7" }, @@ -296,7 +272,6 @@ "Deprecated": "1", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.TAKEN_JCC", - "PublicDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.COND_TAKEN Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0xfe" }, @@ -371,7 +346,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc0", "EventName": "INST_RETIRED.ANY_P", - "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 a programmable general purpose performance counter. Available PDIST counters: 0", + "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 a programmable general purpose performance counter.", "SampleAfterValue": "2000003" }, { @@ -380,7 +355,6 @@ "Deprecated": "1", "EventCode": "0x03", "EventName": "LD_BLOCKS.4K_ALIAS", - "PublicDescription": "This event is deprecated. Refer to new event LD_BLOCKS.ADDRESS_ALIAS Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x4" }, @@ -389,7 +363,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0x03", "EventName": "LD_BLOCKS.ADDRESS_ALIAS", - "PublicDescription": "Counts the number of retired loads that are blocked because it initially appears to be store forward blocked, but subsequently is shown not to be blocked based on 4K alias check. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x4" }, @@ -398,7 +371,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0x03", "EventName": "LD_BLOCKS.DATA_UNKNOWN", - "PublicDescription": "Counts the number of retired loads that are blocked because its address exactly matches an older store whose data is not ready. Available PDIST counters: 0", "SampleAfterValue": "1000003", "UMask": "0x1" }, @@ -448,7 +420,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xe4", "EventName": "MISC_RETIRED.LBR_INSERTS", - "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. This event is PDIR on GP0 and NPEBS on all other GPs [This event is alias to LBR_INSERTS.ANY] Available PDIST counters: 0", + "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. This event is PDIR on GP0 and NPEBS on all other GPs [This event is alias to LBR_INSERTS.ANY]", "SampleAfterValue": "1000003", "UMask": "0x1" }, @@ -651,7 +623,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "TOPDOWN_RETIRING.ALL", - "PublicDescription": "Counts the total number of consumed retirement slots. Available PDIST counters: 0", "SampleAfterValue": "1000003" }, { @@ -667,7 +638,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.ALL", - "PublicDescription": "Counts the total number of uops retired. Available PDIST counters: 0", "SampleAfterValue": "2000003" }, { @@ -675,7 +645,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.IDIV", - "PublicDescription": "Counts the number of integer divide uops retired. Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x10" }, @@ -684,7 +653,7 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.MS", - "PublicDescription": "Counts the number of uops that are from complex flows issued by the Microcode Sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows. Available PDIST counters: 0", + "PublicDescription": "Counts the number of uops that are from complex flows issued by the Microcode Sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows.", "SampleAfterValue": "2000003", "UMask": "0x1" }, @@ -693,7 +662,6 @@ "Counter": "0,1,2,3,4,5", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.X87", - "PublicDescription": "Counts the number of x87 uops retired, includes those in MS flows. Available PDIST counters: 0", "SampleAfterValue": "2000003", "UMask": "0x2" } diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json b/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json index c348046696bf..d9c737a17df0 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json @@ -57,7 +57,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x13" }, @@ -68,7 +67,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS_LOADS", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS_LOADS Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x11" }, @@ -79,7 +77,6 @@ "Deprecated": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.DTLB_MISS_STORES", - "PublicDescription": "This event is deprecated. Refer to new event MEM_UOPS_RETIRED.STLB_MISS_STORES Available PDIST counters: 0", "SampleAfterValue": "200003", "UMask": "0x12" } diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json index 70175404540d..91929d8bcf47 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json @@ -237,7 +237,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of L2 Cache Accesses that miss the L2 and get BBL reject short and long rejects (includes those counted in L2_reject_XQ.any), per core event", + "BriefDescription": "Counts the number of L2 Cache Accesses that miss the L2 and get BBL reject short and long rejects, per core event", "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0x24", "EventName": "L2_REQUEST.REJECTS", @@ -728,6 +728,17 @@ "EventName": "MEM_LOAD_RETIRED.L1_HIT", "PublicDescription": "Counts retired load instructions with at least one uop that hit in the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source. Available PDIST counters: 0", "SampleAfterValue": "1000003", + "UMask": "0x101", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts retired load instructions with at least one uop that hit in the Level 0 of the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source.", + "Counter": "0,1,2,3", + "Data_LA": "1", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_RETIRED.L1_HIT_L0", + "PublicDescription": "Counts retired load instructions with at least one uop that hit in the Level 0 of the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source. Available PDIST counters: 0", + "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_core" }, diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json b/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json index 67cc83de18d3..56cf1ec63200 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json @@ -58,6 +58,22 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "SampleAfterValue": "1000003", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Retired ANT branches", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -82,6 +98,80 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_lowpower" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_lowpower" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_lowpower" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 4 uops", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Retired Instructions who experienced a critical DSB miss.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -103,6 +193,15 @@ "UMask": "0x20", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", "Counter": "0,1,2,3,4,5,6,7", @@ -301,6 +400,42 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_lowpower" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Retired Instructions who experienced STLB (2nd level TLB) true miss.", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/floating-point.json b/tools/perf/pmu-events/arch/x86/cascadelakex/floating-point.json index 1c709983b65f..3ef6f00f1135 100644 --- a/tools/perf/pmu-events/arch/x86/cascadelakex/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/cascadelakex/floating-point.json @@ -111,7 +111,7 @@ "Counter": "0,1,2,3", "EventCode": "0xCF", "EventName": "FP_ARITH_INST_RETIRED2.128BIT_PACKED_BF16", - "PublicDescription": "Counts once for each Intel AVX-512 computational 512-bit packed BFloat16 floating-point instruction retired. Applies to the ZMM based VDPBF16PS instruction. Each count represents 64 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", + "PublicDescription": "Counts once for each Intel AVX-512 computational 128-bit packed BFloat16 floating-point instruction retired. Applies to the XMM based VDPBF16PS instruction. Each count represents 16 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", "SampleAfterValue": "2000003", "UMask": "0x20" }, @@ -120,7 +120,7 @@ "Counter": "0,1,2,3", "EventCode": "0xCF", "EventName": "FP_ARITH_INST_RETIRED2.256BIT_PACKED_BF16", - "PublicDescription": "Counts once for each Intel AVX-512 computational 128-bit packed BFloat16 floating-point instruction retired. Applies to the XMM based VDPBF16PS instruction. Each count represents 16 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", + "PublicDescription": "Counts once for each Intel AVX-512 computational 256-bit packed BFloat16 floating-point instruction retired. Applies to the YMM based VDPBF16PS instruction. Each count represents 32 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", "SampleAfterValue": "2000003", "UMask": "0x40" }, @@ -129,7 +129,7 @@ "Counter": "0,1,2,3", "EventCode": "0xCF", "EventName": "FP_ARITH_INST_RETIRED2.512BIT_PACKED_BF16", - "PublicDescription": "Counts once for each Intel AVX-512 computational 256-bit packed BFloat16 floating-point instruction retired. Applies to the YMM based VDPBF16PS instruction. Each count represents 32 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", + "PublicDescription": "Counts once for each Intel AVX-512 computational 512-bit packed BFloat16 floating-point instruction retired. Applies to the ZMM based VDPBF16PS instruction. Each count represents 64 computation operations. This event is only supported on products formerly named Cooper Lake and is not supported on products formerly named Cascade Lake.", "SampleAfterValue": "2000003", "UMask": "0x80" }, diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/pipeline.json b/tools/perf/pmu-events/arch/x86/cascadelakex/pipeline.json index 3dd296ab4d78..9a1349527b66 100644 --- a/tools/perf/pmu-events/arch/x86/cascadelakex/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/cascadelakex/pipeline.json @@ -542,7 +542,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4C", "EventName": "LOAD_HIT_PRE.SW_PF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", "SampleAfterValue": "100003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/pipeline.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/pipeline.json index 00b05a77c289..48bec483b49a 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/pipeline.json @@ -684,7 +684,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1" }, 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 94340dee1c9c..d4cf2199d46b 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json @@ -1821,6 +1821,18 @@ "UMask": "0x4", "Unit": "IIO" }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "Experimental": "1", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x00FF", + "UMask": "0x4", + "Unit": "IIO" + }, { "BriefDescription": "ITC address map 1", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-memory.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-memory.json index aa06088dd26f..68be01dad7c9 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-memory.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-memory.json @@ -2145,6 +2145,16 @@ "UMask": "0x1", "Unit": "MCHBM" }, + { + "BriefDescription": "ECC Correctable Errors", + "Counter": "0,1,2,3", + "EventCode": "0x09", + "EventName": "UNC_MCHBM_ECC_CORRECTABLE_ERRORS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "ECC Correctable Errors. Counts the number of ECC errors detected and corrected by the iMC on this channel. This counter is only useful with ECC devices. This count will increment one time for each correction regardless of the number of bits corrected. The iMC can correct up to 4 bit errors in independent channel mode and 8 bit errors in lockstep mode.", + "Unit": "MCHBM" + }, { "BriefDescription": "HBM Precharge All Commands", "Counter": "0,1,2,3", @@ -2759,6 +2769,16 @@ "UMask": "0x3", "Unit": "iMC" }, + { + "BriefDescription": "ECC Correctable Errors", + "Counter": "0,1,2,3", + "EventCode": "0x09", + "EventName": "UNC_M_ECC_CORRECTABLE_ERRORS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "ECC Correctable Errors : Counts the number of ECC errors detected and corrected by the iMC on this channel. This counter is only useful with ECC DRAM devices. This count will increment one time for each correction regardless of the number of bits corrected. The iMC can correct up to 4 bit errors in independent channel mode and 8 bit errors in lockstep mode.", + "Unit": "iMC" + }, { "BriefDescription": "IMC Clockticks at HCLK frequency", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/grr-metrics.json b/tools/perf/pmu-events/arch/x86/grandridge/grr-metrics.json index 1c6dba7b2822..878b1caf12de 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/grr-metrics.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/grr-metrics.json @@ -106,6 +106,30 @@ "MetricName": "io_bandwidth_write", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "The percent of inbound full cache line writes initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM / UNC_CHA_TOR_INSERTS.IO_ITOM", + "MetricName": "io_full_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "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", + "MetricName": "io_msi", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "The percent of inbound partial writes initiated by IO that miss the L3 cache", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_MISS_RFO) / (UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_RFO)", + "MetricName": "io_partial_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "The percent of inbound reads initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR / UNC_CHA_TOR_INSERTS.IO_PCIRDCUR", + "MetricName": "io_read_l3_miss", + "ScaleUnit": "100%" + }, { "BriefDescription": "Ratio of number of completed page walks (for 2 megabyte and 4 megabyte page sizes) caused by a code fetch to the total number of completed instructions", "MetricExpr": "ITLB_MISSES.WALK_COMPLETED_2M_4M / INST_RETIRED.ANY", @@ -162,12 +186,6 @@ "MetricName": "llc_data_read_mpi_demand_plus_prefetch", "ScaleUnit": "1per_instr" }, - { - "BriefDescription": "Average latency of a last level cache (LLC) demand data read miss (read memory access) in nano seconds", - "MetricExpr": "1e9 * (UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT / UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT) / (UNC_CHA_CLOCKTICKS / (source_count(UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT) * #num_packages)) * duration_time", - "MetricName": "llc_demand_data_read_miss_latency", - "ScaleUnit": "1ns" - }, { "BriefDescription": "Load operations retired per instruction", "MetricExpr": "MEM_UOPS_RETIRED.ALL_LOADS / INST_RETIRED.ANY", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/grandridge/uncore-interconnect.json index 2c18767511f3..c7250332d8aa 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/uncore-interconnect.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/uncore-interconnect.json @@ -261,5 +261,15 @@ "PerPkg": "1", "UMask": "0x8", "Unit": "IRP" + }, + { + "BriefDescription": "Message Received : MSI", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.MSI_RCVD", + "PerPkg": "1", + "PublicDescription": "Message Received : MSI : Message Signaled Interrupts - interrupts sent by devices (including PCIe via IOxAPIC) (Socket Mode only)", + "UMask": "0x2", + "Unit": "UBOX" } ] diff --git a/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json b/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json index c5b05c71c56d..764cf2f0b4a8 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json @@ -907,6 +907,18 @@ "UMask": "0x4", "Unit": "IIO" }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x0FF", + "PublicDescription": "-", + "UMask": "0x4", + "Unit": "IIO" + }, { "BriefDescription": "All 9 bits of Page Walk Tracker Occupancy", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/cache.json b/tools/perf/pmu-events/arch/x86/graniterapids/cache.json index 32f99a8a3871..dbdeade6fe6f 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/cache.json @@ -977,6 +977,15 @@ "SampleAfterValue": "100003", "UMask": "0x4" }, + { + "BriefDescription": "Offcore Uncacheable memory data read transactions.", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "OFFCORE_REQUESTS.MEM_UC", + "PublicDescription": "This event counts noncacheable memory data read transactions. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x20" + }, { "BriefDescription": "Cycles when offcore outstanding cacheable Core Data Read transactions are present in SuperQueue (SQ), queue to uncore.", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/counter.json b/tools/perf/pmu-events/arch/x86/graniterapids/counter.json index 5d3b202eadd3..d97211a0227e 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/counter.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/counter.json @@ -59,6 +59,11 @@ "CountersNumFixed": "0", "CountersNumGeneric": 4 }, + { + "Unit": "UBOX", + "CountersNumFixed": "0", + "CountersNumGeneric": "2" + }, { "Unit": "PCU", "CountersNumFixed": "0", @@ -73,10 +78,5 @@ "Unit": "MDF", "CountersNumFixed": "0", "CountersNumGeneric": "4" - }, - { - "Unit": "UBOX", - "CountersNumFixed": "0", - "CountersNumGeneric": "2" } ] \ No newline at end of file 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 af527f7f9d0c..9a620e1b8de8 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json @@ -95,6 +95,12 @@ "MetricName": "io_bandwidth_read", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "Bandwidth of inbound IO reads that are initiated by end device controllers that are requesting memory from the CPU and miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read_l3_miss", + "ScaleUnit": "1MB/s" + }, { "BriefDescription": "Bandwidth of IO reads that are initiated by end device controllers that are requesting memory from the local CPU socket", "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_LOCAL * 64 / 1e6 / duration_time", @@ -113,6 +119,12 @@ "MetricName": "io_bandwidth_write", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "Bandwidth of inbound IO writes that are initiated by end device controllers that are writing memory to the CPU", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOM + UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write_l3_miss", + "ScaleUnit": "1MB/s" + }, { "BriefDescription": "Bandwidth of IO writes that are initiated by end device controllers that are writing memory to the local CPU socket", "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOM_LOCAL + UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_LOCAL) * 64 / 1e6 / duration_time", @@ -125,6 +137,30 @@ "MetricName": "io_bandwidth_write_remote", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "The percent of inbound full cache line writes initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM / UNC_CHA_TOR_INSERTS.IO_ITOM", + "MetricName": "io_full_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "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", + "MetricName": "io_msi", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "The percent of inbound partial writes initiated by IO that miss the L3 cache", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_MISS_RFO) / (UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_RFO)", + "MetricName": "io_partial_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "The percent of inbound reads initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR / UNC_CHA_TOR_INSERTS.IO_PCIRDCUR", + "MetricName": "io_read_l3_miss", + "ScaleUnit": "100%" + }, { "BriefDescription": "Ratio of number of completed page walks (for all page sizes) caused by a code fetch to the total number of completed instructions", "MetricExpr": "ITLB_MISSES.WALK_COMPLETED / INST_RETIRED.ANY", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/pipeline.json b/tools/perf/pmu-events/arch/x86/graniterapids/pipeline.json index 1edfdad1600d..27af3bd6bacf 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/pipeline.json @@ -738,7 +738,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/graniterapids/uncore-interconnect.json index e5bd11b27bcd..6667fbc50452 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/uncore-interconnect.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/uncore-interconnect.json @@ -1915,24 +1915,6 @@ "UMask": "0x4", "Unit": "UPI" }, - { - "BriefDescription": "Tx Flit Buffer Allocations : Number of allocations into the UPI Tx Flit Buffer. Generally, when data is transmitted across UPI, it will bypass the TxQ and pass directly to the link. However, the TxQ will be used with L0p and when LLR occurs, increasing latency to transfer out to the link. This event can be used in conjunction with the Flit Buffer Occupancy event in order to calculate the average flit buffer lifetime.", - "Counter": "0,1,2,3", - "EventCode": "0x40", - "EventName": "UNC_UPI_TxL_INSERTS", - "Experimental": "1", - "PerPkg": "1", - "Unit": "UPI" - }, - { - "BriefDescription": "Tx Flit Buffer Occupancy : Accumulates the number of flits in the TxQ. Generally, when data is transmitted across UPI, it will bypass the TxQ and pass directly to the link. However, the TxQ will be used with L0p and when LLR occurs, increasing latency to transfer out to the link. This can be used with the cycles not empty event to track average occupancy, or the allocations event to track average lifetime in the TxQ.", - "Counter": "0,1,2,3", - "EventCode": "0x42", - "EventName": "UNC_UPI_TxL_OCCUPANCY", - "Experimental": "1", - "PerPkg": "1", - "Unit": "UPI" - }, { "BriefDescription": "Message Received : Doorbell", "Counter": "0,1", @@ -1970,7 +1952,6 @@ "Counter": "0,1", "EventCode": "0x42", "EventName": "UNC_U_EVENT_MSG.MSI_RCVD", - "Experimental": "1", "PerPkg": "1", "PublicDescription": "Message Received : MSI : Message Signaled Interrupts - interrupts sent by devices (including PCIe via IOxAPIC) (Socket Mode only)", "UMask": "0x2", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/uncore-io.json b/tools/perf/pmu-events/arch/x86/graniterapids/uncore-io.json index 886b99a971be..f4f956966e16 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/uncore-io.json @@ -1121,8 +1121,9 @@ "Unit": "IIO" }, { - "BriefDescription": "Occupancy of outbound request queue : To device : Counts number of outbound requests/completions IIO is currently processing", + "BriefDescription": "This event is deprecated. [This event is alias to UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO]", "Counter": "2,3", + "Deprecated": "1", "EventCode": "0xc5", "EventName": "UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO", "Experimental": "1", @@ -1132,6 +1133,18 @@ "UMask": "0x8", "Unit": "IIO" }, + { + "BriefDescription": "Occupancy of outbound request queue : To device : Counts number of outbound requests/completions IIO is currently processing [This event is alias to UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO]", + "Counter": "2,3", + "EventCode": "0xc5", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, { "BriefDescription": "Passing data to be written", "Counter": "0,1,2,3", @@ -1300,6 +1313,18 @@ "UMask": "0x4", "Unit": "IIO" }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x0FF", + "PublicDescription": "-", + "UMask": "0x4", + "Unit": "IIO" + }, { "BriefDescription": "All 9 bits of Page Walk Tracker Occupancy", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/icelakex/pipeline.json b/tools/perf/pmu-events/arch/x86/icelakex/pipeline.json index f1446f1b67c6..f3a0d7f49af4 100644 --- a/tools/perf/pmu-events/arch/x86/icelakex/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/icelakex/pipeline.json @@ -477,7 +477,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", "SampleAfterValue": "100003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/arch/x86/icelakex/uncore-cache.json b/tools/perf/pmu-events/arch/x86/icelakex/uncore-cache.json index 8c73708befef..6f84ad47276d 100644 --- a/tools/perf/pmu-events/arch/x86/icelakex/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/icelakex/uncore-cache.json @@ -8193,7 +8193,6 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RFO", - "Experimental": "1", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by IO Devices that missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0xc803fe04", @@ -8234,7 +8233,6 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_RFO", - "Experimental": "1", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by IO Devices : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0xc803ff04", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json index b1a6bb867a1e..ff37d49611c3 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json @@ -790,6 +790,17 @@ "EventName": "MEM_LOAD_RETIRED.L1_HIT", "PublicDescription": "Counts retired load instructions with at least one uop that hit in the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source. Available PDIST counters: 0", "SampleAfterValue": "1000003", + "UMask": "0x101", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts retired load instructions with at least one uop that hit in the Level 0 of the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source.", + "Counter": "0,1,2,3", + "Data_LA": "1", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_RETIRED.L1_HIT_L0", + "PublicDescription": "Counts retired load instructions with at least one uop that hit in the Level 0 of the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source. Available PDIST counters: 0", + "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_core" }, diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json index 4875047fb65c..6ac410510628 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json @@ -1247,9 +1247,19 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of demand loads that match on a wcb (request buffer) allocated by an L1 hardware prefetch", + "BriefDescription": "Counts the number of demand loads that match on a wcb (request buffer) allocated by an L1 hardware prefetch [This event is alias to LOAD_HIT_PREFETCH.HW_PF]", "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0x4c", + "EventName": "LOAD_HIT_PREFETCH.HWPF", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to LOAD_HIT_PREFETCH.HWPF]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.HW_PF", "SampleAfterValue": "1000003", "UMask": "0x2", @@ -1664,7 +1674,7 @@ }, { "BriefDescription": "Fixed Counter: Counts the number of issue slots not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear.", - "Counter": "36", + "Counter": "Fixed counter 4", "EventName": "TOPDOWN_BAD_SPECULATION.ALL", "PublicDescription": "Fixed Counter: Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear. Counts all issue slots blocked during this recovery window including relevant microcode flows and while uops are not yet available in the IQ. Also, includes the issue slots that were consumed by the backend but were thrown away because they were younger than the mispredict or machine clear.", "SampleAfterValue": "1000003", @@ -1797,7 +1807,7 @@ }, { "BriefDescription": "Fixed Counter: Counts the number of retirement slots not consumed due to front end stalls.", - "Counter": "37", + "Counter": "Fixed counter 5", "EventName": "TOPDOWN_FE_BOUND.ALL", "SampleAfterValue": "1000003", "UMask": "0x6", @@ -1903,7 +1913,7 @@ }, { "BriefDescription": "Fixed Counter: Counts the number of consumed retirement slots.", - "Counter": "38", + "Counter": "Fixed counter 6", "EventName": "TOPDOWN_RETIRING.ALL", "SampleAfterValue": "1000003", "UMask": "0x7", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/lunarlake/virtual-memory.json index defa3a967754..e60a5e904da2 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/virtual-memory.json @@ -36,24 +36,6 @@ "UMask": "0x320", "Unit": "cpu_core" }, - { - "BriefDescription": "Counts the number of first level TLB misses but second level hits due to a demand load that did not start a page walk. Account for 4k page size only. Will result in a DTLB write from STLB.", - "Counter": "0,1,2,3,4,5,6,7", - "EventCode": "0x08", - "EventName": "DTLB_LOAD_MISSES.STLB_HIT_4K", - "SampleAfterValue": "200003", - "UMask": "0x20", - "Unit": "cpu_atom" - }, - { - "BriefDescription": "Counts the number of first level TLB misses but second level hits due to a demand load that did not start a page walk. Account for large page sizes only. Will result in a DTLB write from STLB.", - "Counter": "0,1,2,3,4,5,6,7", - "EventCode": "0x08", - "EventName": "DTLB_LOAD_MISSES.STLB_HIT_LGPG", - "SampleAfterValue": "200003", - "UMask": "0x40", - "Unit": "cpu_atom" - }, { "BriefDescription": "Cycles when at least one PMH is busy with a page walk for a demand load.", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index bde2f32423a1..354ce241500b 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -1,40 +1,41 @@ Family-model,Version,Filename,EventType -GenuineIntel-6-(97|9A|B7|BA|BF),v1.29,alderlake,core -GenuineIntel-6-BE,v1.29,alderlaken,core -GenuineIntel-6-C[56],v1.08,arrowlake,core +GenuineIntel-6-(97|9A|B7|BA|BF),v1.31,alderlake,core +GenuineIntel-6-BE,v1.31,alderlaken,core +GenuineIntel-6-C[56],v1.09,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 GenuineIntel-6-4F,v23,broadwellx,core -GenuineIntel-6-55-[56789ABCDEF],v1.23,cascadelakex,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.11,emeraldrapids,core +GenuineIntel-6-CF,v1.14,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core GenuineIntel-6-7A,v1.01,goldmontplus,core -GenuineIntel-6-B6,v1.07,grandridge,core -GenuineIntel-6-A[DE],v1.08,graniterapids,core +GenuineIntel-6-B6,v1.09,grandridge,core +GenuineIntel-6-A[DE],v1.10,graniterapids,core GenuineIntel-6-(3C|45|46),v36,haswell,core GenuineIntel-6-3F,v29,haswellx,core GenuineIntel-6-7[DE],v1.24,icelake,core -GenuineIntel-6-6[AC],v1.27,icelakex,core +GenuineIntel-6-6[AC],v1.28,icelakex,core 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.11,lunarlake,core -GenuineIntel-6-(AA|AC|B5),v1.13,meteorlake,core +GenuineIntel-6-BD,v1.14,lunarlake,core +GenuineIntel-6-(AA|AC|B5),v1.14,meteorlake,core GenuineIntel-6-1[AEF],v4,nehalemep,core GenuineIntel-6-2E,v4,nehalemex,core +GenuineIntel-6-CC,v1.00,pantherlake,core GenuineIntel-6-A7,v1.04,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core -GenuineIntel-6-8F,v1.25,sapphirerapids,core -GenuineIntel-6-AF,v1.09,sierraforest,core +GenuineIntel-6-8F,v1.28,sapphirerapids,core +GenuineIntel-6-AF,v1.11,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.36,skylakex,core +GenuineIntel-6-55-[01234],v1.37,skylakex,core GenuineIntel-6-86,v1.23,snowridgex,core -GenuineIntel-6-8[CD],v1.17,tigerlake,core +GenuineIntel-6-8[CD],v1.18,tigerlake,core GenuineIntel-6-2C,v5,westmereep-dp,core GenuineIntel-6-25,v4,westmereep-sp,core GenuineIntel-6-2F,v4,westmereex,core diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json index c980bbee6146..82b115183924 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json @@ -231,7 +231,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of L2 Cache Accesses that miss the L2 and get BBL reject short and long rejects (includes those counted in L2_reject_XQ.any), per core event", + "BriefDescription": "Counts the number of L2 Cache Accesses that miss the L2 and get BBL reject short and long rejects, per core event", "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0x24", "EventName": "L2_REQUEST.REJECTS", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json index 509ce68c2ea6..82727022efb6 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json @@ -49,6 +49,14 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Retired ANT branches", "Counter": "0,1,2,3,4,5,6,7", @@ -73,6 +81,43 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Retired Instructions who experienced a critical DSB miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -85,6 +130,15 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "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", "Counter": "0,1,2,3,4,5,6,7", @@ -286,6 +340,24 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Retired Instructions who experienced STLB (2nd level TLB) true miss.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json index a833d6f53d0e..22b25708e799 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json @@ -1076,7 +1076,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1", "Unit": "cpu_core" diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/cache.json b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json new file mode 100644 index 000000000000..c84f3d9fdb10 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json @@ -0,0 +1,278 @@ +[ + { + "BriefDescription": "Counts the number of L2 cache accesses from front door requests for Code Read, Data Read, RFO, ITOM, and L2 Prefetches. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x1ff", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "L2 code requests", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_RQSTS.ALL_CODE_RD", + "PublicDescription": "Counts the total number of L2 code requests.", + "SampleAfterValue": "200003", + "UMask": "0xe4", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Demand Data Read access L2 cache", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_RQSTS.ALL_DEMAND_DATA_RD", + "PublicDescription": "Counts Demand Data Read requests accessing the L2 cache. These requests may hit or miss L2 cache. True-miss exclude misses that were merged with ongoing L2 misses. An access is counted once.", + "SampleAfterValue": "200003", + "UMask": "0xe1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of cacheable memory requests that miss in the LLC. Counts on a per core basis.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x2e", + "EventName": "LONGEST_LAT_CACHE.MISS", + "PublicDescription": "Counts the number of cacheable memory requests that miss in the Last Level Cache (LLC). Requests include demand loads, reads for ownership (RFO), instruction fetches and L1 HW prefetches. If the core has access to an L3 cache, the LLC is the L3 cache, otherwise it is the L2 cache. Counts on a per core basis.", + "SampleAfterValue": "1000003", + "UMask": "0x41", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Core-originated cacheable requests that missed L3 (Except hardware prefetches to the L3)", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x2e", + "EventName": "LONGEST_LAT_CACHE.MISS", + "PublicDescription": "Counts core-originated cacheable requests that miss the L3 cache (Longest Latency cache). Requests include data and code reads, Reads-for-Ownership (RFOs), speculative accesses and hardware prefetches to the L1 and L2. It does not include hardware prefetches to the L3, and may not count other types of requests to the L3.", + "SampleAfterValue": "100003", + "UMask": "0x41", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of cacheable memory requests that access the LLC. Counts on a per core basis.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x2e", + "EventName": "LONGEST_LAT_CACHE.REFERENCE", + "PublicDescription": "Counts the number of cacheable memory requests that access the Last Level Cache (LLC). Requests include demand loads, reads for ownership (RFO), instruction fetches and L1 HW prefetches. If the core has access to an L3 cache, the LLC is the L3 cache, otherwise it is the L2 cache. Counts on a per core basis.", + "SampleAfterValue": "1000003", + "UMask": "0x4f", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Core-originated cacheable requests that refer to L3 (Except hardware prefetches to the L3)", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x2e", + "EventName": "LONGEST_LAT_CACHE.REFERENCE", + "PublicDescription": "Counts core-originated cacheable requests to the L3 cache (Longest Latency cache). Requests include data and code reads, Reads-for-Ownership (RFOs), speculative accesses and hardware prefetches to the L1 and L2. It does not include hardware prefetches to the L3, and may not count other types of requests to the L3.", + "SampleAfterValue": "100003", + "UMask": "0x4f", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts all retired load instructions.", + "Counter": "0,1,2,3", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_INST_RETIRED.ALL_LOADS", + "PublicDescription": "Counts Instructions with at least one architecturally visible load retired. Available PDIST counters: 0", + "SampleAfterValue": "1000003", + "UMask": "0x81", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Retired store instructions.", + "Counter": "0,1,2,3", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_INST_RETIRED.ALL_STORES", + "PublicDescription": "Counts all retired store instructions. Available PDIST counters: 0", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of load ops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL_LOADS", + "SampleAfterValue": "1000003", + "UMask": "0x81", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of store ops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL_STORES", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", + "MSRIndex": "0x3F6", + "MSRValue": "0x80", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", + "MSRIndex": "0x3F6", + "MSRValue": "0x10", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", + "MSRIndex": "0x3F6", + "MSRValue": "0x100", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", + "MSRIndex": "0x3F6", + "MSRValue": "0x20", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", + "MSRIndex": "0x3F6", + "MSRValue": "0x4", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", + "MSRIndex": "0x3F6", + "MSRValue": "0x200", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", + "MSRIndex": "0x3F6", + "MSRValue": "0x40", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "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.", + "Counter": "0,1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", + "MSRIndex": "0x3F6", + "MSRValue": "0x8", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", + "SampleAfterValue": "1000003", + "UMask": "0x6", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand data reads that have any type of response.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xB7", + "EventName": "OCR.DEMAND_DATA_RD.ANY_RESPONSE", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x10001", + "PublicDescription": "Counts demand data reads that have any type of response. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand data reads that have any type of response.", + "Counter": "0,1,2,3", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_DATA_RD.ANY_RESPONSE", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x10001", + "PublicDescription": "Counts demand data reads that have any type of response. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that have any type of response.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xB7", + "EventName": "OCR.DEMAND_RFO.ANY_RESPONSE", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x10002", + "PublicDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that have any type of response. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that have any type of response.", + "Counter": "0,1,2,3", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_RFO.ANY_RESPONSE", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x10002", + "PublicDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that have any type of response. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + } +] diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/counter.json b/tools/perf/pmu-events/arch/x86/pantherlake/counter.json new file mode 100644 index 000000000000..69f158a97707 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/counter.json @@ -0,0 +1,12 @@ +[ + { + "Unit": "cpu_atom", + "CountersNumFixed": "3", + "CountersNumGeneric": "39" + }, + { + "Unit": "cpu_core", + "CountersNumFixed": "4", + "CountersNumGeneric": "10" + } +] \ No newline at end of file diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json new file mode 100644 index 000000000000..aedf631e3c0f --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json @@ -0,0 +1,30 @@ +[ + { + "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x80", + "EventName": "ICACHE.ACCESSES", + "SampleAfterValue": "1000003", + "UMask": "0x3", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump and the instruction cache registers bytes are not present. -", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x80", + "EventName": "ICACHE.MISSES", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "This event counts a subset of the Topdown Slots event that when no operation was delivered to the back-end pipeline due to instruction fetch limitations when the back-end could have accepted more operations. Common examples include instruction cache misses or x86 instruction decode limitations.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x9c", + "EventName": "IDQ_BUBBLES.CORE", + "PublicDescription": "This event counts a subset of the Topdown Slots event that when no operation was delivered to the back-end pipeline due to instruction fetch limitations when the back-end could have accepted more operations. Common examples include instruction cache misses or x86 instruction decode limitations. Software can use this event as the numerator for the Frontend Bound metric (or top-level category) of the Top-down Microarchitecture Analysis method.", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_core" + } +] diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/memory.json b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json new file mode 100644 index 000000000000..47daee8cc00f --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json @@ -0,0 +1,215 @@ +[ + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 1024 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 1024 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "53", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 128 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_128", + "MSRIndex": "0x3F6", + "MSRValue": "0x80", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 128 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "1009", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 16 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_16", + "MSRIndex": "0x3F6", + "MSRValue": "0x10", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 16 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "20011", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 2048 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 2048 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "23", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 256 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_256", + "MSRIndex": "0x3F6", + "MSRValue": "0x100", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 256 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "503", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 32 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_32", + "MSRIndex": "0x3F6", + "MSRValue": "0x20", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 32 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "100007", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 4 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_4", + "MSRIndex": "0x3F6", + "MSRValue": "0x4", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 4 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 512 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_512", + "MSRIndex": "0x3F6", + "MSRValue": "0x200", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 512 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "101", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 64 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_64", + "MSRIndex": "0x3F6", + "MSRValue": "0x40", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 64 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "2003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 8 cycles.", + "Counter": "2,3,4,5,6,7,8,9", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_8", + "MSRIndex": "0x3F6", + "MSRValue": "0x8", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 8 cycles. Reported latency may be longer than just the memory latency. Available PDIST counters: 0", + "SampleAfterValue": "50021", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Retired memory store access operations. A PDist event for PEBS Store Latency Facility.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.STORE_SAMPLE", + "PublicDescription": "Counts Retired memory accesses with at least 1 store operation. This PEBS event is the precisely-distributed (PDist) trigger covering all stores uops for sampling by the PEBS Store Latency Facility. The facility is described in Intel SDM Volume 3 section 19.9.8 Available PDIST counters: 0", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts demand data reads that were supplied by DRAM.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xB7", + "EventName": "OCR.DEMAND_DATA_RD.DRAM", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x7BC000001", + "PublicDescription": "Counts demand data reads that were supplied by DRAM. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand data reads that were supplied by DRAM.", + "Counter": "0,1,2,3", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_DATA_RD.DRAM", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x1E780000001", + "PublicDescription": "Counts demand data reads that were supplied by DRAM. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts demand data reads that were not supplied by the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xB7", + "EventName": "OCR.DEMAND_DATA_RD.L3_MISS", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x13FBFC00001", + "PublicDescription": "Counts demand data reads that were not supplied by the L3 cache. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand data reads that were not supplied by the L3 cache.", + "Counter": "0,1,2,3", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_DATA_RD.L3_MISS", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x9E7FA000001", + "PublicDescription": "Counts demand data reads that were not supplied by the L3 cache. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that were not supplied by the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xB7", + "EventName": "OCR.DEMAND_RFO.L3_MISS", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x13FBFC00002", + "PublicDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that were not supplied by the L3 cache. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that were not supplied by the L3 cache.", + "Counter": "0,1,2,3", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_RFO.L3_MISS", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x9E7FA000002", + "PublicDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that were not supplied by the L3 cache. Available PDIST counters: 0", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + } +] diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json new file mode 100644 index 000000000000..2caf2f85327f --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json @@ -0,0 +1,325 @@ +[ + { + "BriefDescription": "Counts the total number of branch instructions retired for all branch types.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_BRANCHES", + "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for.", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "All branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_BRANCHES", + "PublicDescription": "Counts all branch instructions retired. Available PDIST counters: 0", + "SampleAfterValue": "400009", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.ALL_BRANCHES", + "PublicDescription": "Counts the total number of mispredicted branch instructions retired. All branch type instructions are accounted for. Prediction of the branch target address enables the processor to begin executing instructions before the non-speculative execution path is known. The branch prediction unit (BPU) predicts the target address based on the instruction pointer (IP) of the branch and on the execution path through which execution reached this IP. A branch misprediction occurs when the prediction is wrong, and results in discarding all instructions executed in the speculative path and re-fetching from the correct path.", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "All mispredicted branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.ALL_BRANCHES", + "PublicDescription": "Counts all the retired branch instructions that were mispredicted by the processor. A branch misprediction occurs when the processor incorrectly predicts the destination of the branch. When the misprediction is discovered at execution, all the instructions executed in the wrong (speculative) path must be discarded, and the processor must start fetching from the correct path. Available PDIST counters: 0", + "SampleAfterValue": "400009", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "Counter": "Fixed counter 1", + "EventName": "CPU_CLK_UNHALTED.CORE", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Core cycles when the core is not in a halt state.", + "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. This event is a component in many key event ratios. The core frequency may change from time to time due to transitions associated with Enhanced Intel SpeedStep Technology or TM2. For this reason this event may have a changing ratio with regards to time. When the core frequency is constant, this event can approximate elapsed time while the core was not in the halt state. It is counted on a dedicated fixed counter, leaving the programmable counters available for other events.", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "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,6,7", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.CORE_P", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Thread cycles when thread is not in halt state [This event is alias to CPU_CLK_UNHALTED.THREAD_P]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.CORE_P", + "PublicDescription": "This is an architectural event that counts the number of thread cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. The core frequency may change from time to time due to power or thermal throttling. For this reason, this event may have a changing ratio with regards to wall clock time. [This event is alias to CPU_CLK_UNHALTED.THREAD_P]", + "SampleAfterValue": "2000003", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", + "Counter": "Fixed counter 2", + "EventName": "CPU_CLK_UNHALTED.REF_TSC", + "SampleAfterValue": "1000003", + "UMask": "0x3", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Reference cycles when the core is not in halt state.", + "Counter": "Fixed counter 2", + "EventName": "CPU_CLK_UNHALTED.REF_TSC", + "PublicDescription": "Counts the number of reference cycles when the core is not in a halt state. The core enters the halt state when it is running the HLT instruction or the MWAIT instruction. This event is not affected by core frequency changes (for example, P states, TM2 transitions) but has the same incrementing frequency as the time stamp counter. This event can approximate elapsed time while the core was not in a halt state. Note: On all current platforms this event stops counting during 'throttling (TM)' states duty off periods the processor is 'halted'. The counter update is done at a lower clock rate then the core clock the overflow status bit for this counter may appear 'sticky'. After the counter has overflowed and software clears the overflow status bit and resets the counter to less than MAX. The reset value to the counter is not clocked immediately so the overflow status bit will flip 'high (1)' and generate another PMI (if enabled) after which the reset value gets clocked into the counter. Therefore, software will get the interrupt, read the overflow status bit '1 for bit 34 while the counter value is less than MAX. Software should ignore this case.", + "SampleAfterValue": "2000003", + "UMask": "0x3", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of unhalted reference clock cycles at TSC frequency.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.REF_TSC_P", + "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 a programmable general purpose performance counter.", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Reference cycles when the core is not in halt state.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.REF_TSC_P", + "PublicDescription": "Counts the number of reference cycles when the core is not in a halt state. The core enters the halt state when it is running the HLT instruction or the MWAIT instruction. This event is not affected by core frequency changes (for example, P states, TM2 transitions) but has the same incrementing frequency as the time stamp counter. This event can approximate elapsed time while the core was not in a halt state. Note: On all current platforms this event stops counting during 'throttling (TM)' states duty off periods the processor is 'halted'. The counter update is done at a lower clock rate then the core clock the overflow status bit for this counter may appear 'sticky'. After the counter has overflowed and software clears the overflow status bit and resets the counter to less than MAX. The reset value to the counter is not clocked immediately so the overflow status bit will flip 'high (1)' and generate another PMI (if enabled) after which the reset value gets clocked into the counter. Therefore, software will get the interrupt, read the overflow status bit '1 for bit 34 while the counter value is less than MAX. Software should ignore this case.", + "SampleAfterValue": "2000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "Counter": "Fixed counter 1", + "EventName": "CPU_CLK_UNHALTED.THREAD", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Core cycles when the thread is not in a halt state.", + "Counter": "Fixed counter 1", + "EventName": "CPU_CLK_UNHALTED.THREAD", + "PublicDescription": "Counts the number of core cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. This event is a component in many key event ratios. The core frequency may change from time to time due to transitions associated with Enhanced Intel SpeedStep Technology or TM2. For this reason this event may have a changing ratio with regards to time. When the core frequency is constant, this event can approximate elapsed time while the core was not in the halt state. It is counted on a dedicated fixed counter, leaving the programmable counters available for other events.", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "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,6,7", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.THREAD_P", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Thread cycles when thread is not in halt state [This event is alias to CPU_CLK_UNHALTED.CORE_P]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x3c", + "EventName": "CPU_CLK_UNHALTED.THREAD_P", + "PublicDescription": "This is an architectural event that counts the number of thread cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. The core frequency may change from time to time due to power or thermal throttling. For this reason, this event may have a changing ratio with regards to wall clock time. [This event is alias to CPU_CLK_UNHALTED.CORE_P]", + "SampleAfterValue": "2000003", + "Unit": "cpu_core" + }, + { + "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", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Number of instructions retired. Fixed Counter - architectural event", + "Counter": "Fixed counter 0", + "EventName": "INST_RETIRED.ANY", + "PublicDescription": "Counts the number of X86 instructions retired - an Architectural PerfMon event. Counting continues during hardware interrupts, traps, and inside interrupt handlers. Notes: INST_RETIRED.ANY is counted by a designated fixed counter freeing up programmable counters to count other events. INST_RETIRED.ANY_P is counted by a programmable counter. Available PDIST counters: 32", + "SampleAfterValue": "2000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc0", + "EventName": "INST_RETIRED.ANY_P", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Number of instructions retired. General Counter - architectural event", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xc0", + "EventName": "INST_RETIRED.ANY_P", + "PublicDescription": "Counts the number of X86 instructions retired - an Architectural PerfMon event. Counting continues during hardware interrupts, traps, and inside interrupt handlers. Notes: INST_RETIRED.ANY is counted by a designated fixed counter freeing up programmable counters to count other events. INST_RETIRED.ANY_P is counted by a programmable counter. Available PDIST counters: 0", + "SampleAfterValue": "2000003", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked because its address partially overlapped with an older store.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.STORE_FORWARD", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Loads blocked due to overlapping with a preceding store that cannot be forwarded.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.STORE_FORWARD", + "PublicDescription": "Counts the number of times where store forwarding was prevented for a load operation. The most common case is a load blocked due to the address of memory access (partially) overlapping with a preceding uncompleted store. Note: See the table of not supported store forwards in the Optimization Guide.", + "SampleAfterValue": "100003", + "UMask": "0x82", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe4", + "EventName": "MISC_RETIRED.LBR_INSERTS", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "LBR record is inserted", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xe4", + "EventName": "MISC_RETIRED.LBR_INSERTS", + "PublicDescription": "LBR record is inserted Available PDIST counters: 0", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This event counts a subset of the Topdown Slots event that were not consumed by the back-end pipeline due to lack of back-end resources, as a result of memory subsystem delays, execution units limitations, or other conditions.", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xa4", + "EventName": "TOPDOWN.BACKEND_BOUND_SLOTS", + "PublicDescription": "This event counts a subset of the Topdown Slots event that were not consumed by the back-end pipeline due to lack of back-end resources, as a result of memory subsystem delays, execution units limitations, or other conditions. Software can use this event as the numerator for the Backend Bound metric (or top-level category) of the Top-down Microarchitecture Analysis method.", + "SampleAfterValue": "10000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "TMA slots available for an unhalted logical processor. Fixed counter - architectural event", + "Counter": "Fixed counter 3", + "EventName": "TOPDOWN.SLOTS", + "PublicDescription": "Number of available slots for an unhalted logical processor. The event increments by machine-width of the narrowest pipeline as employed by the Top-down Microarchitecture Analysis method (TMA). Software can use this event as the denominator for the top-level metrics of the TMA method. This architectural event is counted on a designated fixed counter (Fixed Counter 3).", + "SampleAfterValue": "10000003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { + "BriefDescription": "TMA slots available for an unhalted logical processor. General counter - architectural event", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0xa4", + "EventName": "TOPDOWN.SLOTS_P", + "PublicDescription": "Counts the number of available slots for an unhalted logical processor. The event increments by machine-width of the narrowest pipeline as employed by the Top-down Microarchitecture Analysis method.", + "SampleAfterValue": "10000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear.", + "Counter": "36", + "EventName": "TOPDOWN_BAD_SPECULATION.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.ALL_P", + "PublicDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear. Only issue slots wasted due to fast nukes such as memory ordering nukes are counted. Other nukes are not accounted for. Counts all issue slots blocked during this recovery window, including relevant microcode flows, and while uops are not yet available in the instruction queue (IQ) or until an FE_BOUND event occurs besides OTHER and CISC. Also includes the issue slots that were consumed by the backend but were thrown away because they were younger than the mispredict or machine clear.", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of retirement slots not consumed due to backend stalls. [This event is alias to TOPDOWN_BE_BOUND.ALL_P]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xa4", + "EventName": "TOPDOWN_BE_BOUND.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of retirement slots not consumed due to backend stalls. [This event is alias to TOPDOWN_BE_BOUND.ALL]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xa4", + "EventName": "TOPDOWN_BE_BOUND.ALL_P", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of retirement slots not consumed due to front end stalls.", + "Counter": "37", + "EventName": "TOPDOWN_FE_BOUND.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x6", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of retirement slots not consumed due to front end stalls.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x9c", + "EventName": "TOPDOWN_FE_BOUND.ALL_P", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of consumed retirement slots.", + "Counter": "38", + "EventName": "TOPDOWN_RETIRING.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x7", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of consumed retirement slots.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "TOPDOWN_RETIRING.ALL_P", + "PublicDescription": "Counts the number of consumed retirement slots. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "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", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.SLOTS", + "PublicDescription": "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. Software can use this event as the numerator for the Retiring metric (or top-level category) of the Top-down Microarchitecture Analysis method.", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + } +] diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json new file mode 100644 index 000000000000..690c5dff9d9e --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json @@ -0,0 +1,62 @@ +[ + { + "BriefDescription": "Counts the number of page walks completed due to load DTLB misses to any page size.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x08", + "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED", + "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 any page size. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Load miss in all TLB levels causes a page walk that completes. (All page sizes)", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x12", + "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED", + "PublicDescription": "Counts completed page walks (all page sizes) caused by demand data loads. This implies it missed in the DTLB and further levels of TLB. The page walk can end with or without a fault.", + "SampleAfterValue": "100003", + "UMask": "0xe", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of page walks completed due to store DTLB misses to any page size.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x49", + "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED", + "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 any page size. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Store misses in all TLB levels causes a page walk that completes. (All page sizes)", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x13", + "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED", + "PublicDescription": "Counts completed page walks (all page sizes) caused by demand data stores. This implies it missed in the DTLB and further levels of TLB. The page walk can end with or without a fault.", + "SampleAfterValue": "100003", + "UMask": "0xe", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of page walks completed due to instruction fetch misses to any page size.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.WALK_COMPLETED", + "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 any page size. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Code miss in all TLB levels causes a page walk that completes. (All page sizes)", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x11", + "EventName": "ITLB_MISSES.WALK_COMPLETED", + "PublicDescription": "Counts completed page walks (all page sizes) caused by a code fetch. This implies it missed in the ITLB (Instruction TLB) and further levels of TLB. The page walk can end with or without a fault.", + "SampleAfterValue": "100003", + "UMask": "0xe", + "Unit": "cpu_core" + } +] diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/pipeline.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/pipeline.json index 00b05a77c289..48bec483b49a 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/pipeline.json @@ -684,7 +684,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1" }, 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 aab082ff9402..dac7e6c50f31 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json @@ -1901,6 +1901,18 @@ "UMask": "0x4", "Unit": "IIO" }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "Experimental": "1", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x00FF", + "UMask": "0x4", + "Unit": "IIO" + }, { "BriefDescription": "ITC address map 1", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-memory.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-memory.json index aa06088dd26f..68be01dad7c9 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-memory.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-memory.json @@ -2145,6 +2145,16 @@ "UMask": "0x1", "Unit": "MCHBM" }, + { + "BriefDescription": "ECC Correctable Errors", + "Counter": "0,1,2,3", + "EventCode": "0x09", + "EventName": "UNC_MCHBM_ECC_CORRECTABLE_ERRORS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "ECC Correctable Errors. Counts the number of ECC errors detected and corrected by the iMC on this channel. This counter is only useful with ECC devices. This count will increment one time for each correction regardless of the number of bits corrected. The iMC can correct up to 4 bit errors in independent channel mode and 8 bit errors in lockstep mode.", + "Unit": "MCHBM" + }, { "BriefDescription": "HBM Precharge All Commands", "Counter": "0,1,2,3", @@ -2759,6 +2769,16 @@ "UMask": "0x3", "Unit": "iMC" }, + { + "BriefDescription": "ECC Correctable Errors", + "Counter": "0,1,2,3", + "EventCode": "0x09", + "EventName": "UNC_M_ECC_CORRECTABLE_ERRORS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "ECC Correctable Errors : Counts the number of ECC errors detected and corrected by the iMC on this channel. This counter is only useful with ECC DRAM devices. This count will increment one time for each correction regardless of the number of bits corrected. The iMC can correct up to 4 bit errors in independent channel mode and 8 bit errors in lockstep mode.", + "Unit": "iMC" + }, { "BriefDescription": "IMC Clockticks at HCLK frequency", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/frontend.json b/tools/perf/pmu-events/arch/x86/sierraforest/frontend.json index fef5cba533bb..8a591e31d331 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/frontend.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/frontend.json @@ -8,6 +8,54 @@ "SampleAfterValue": "200003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", "Counter": "0,1,2,3,4,5,6,7", @@ -16,6 +64,22 @@ "SampleAfterValue": "1000003", "UMask": "0x10" }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json index f56d8d816e53..70af13143024 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json @@ -11,6 +11,7 @@ { "BriefDescription": "Counts the total number of branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF6, SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.ALL_BRANCHES", "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for.", @@ -19,6 +20,7 @@ { "BriefDescription": "Counts the number of retired JCC (Jump on Conditional Code) branch instructions retired, includes both taken and not taken branches.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.COND", "SampleAfterValue": "200003", @@ -35,6 +37,7 @@ { "BriefDescription": "Counts the number of far branch instructions retired, includes far jump, far call and return, and interrupt call and return.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.FAR_BRANCH", "SampleAfterValue": "200003", @@ -43,6 +46,7 @@ { "BriefDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT", "SampleAfterValue": "200003", @@ -51,6 +55,7 @@ { "BriefDescription": "Counts the number of near indirect CALL branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.INDIRECT_CALL", "SampleAfterValue": "200003", @@ -68,6 +73,7 @@ "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT_CALL", "Counter": "0,1,2,3,4,5,6,7", "Deprecated": "1", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.IND_CALL", "SampleAfterValue": "200003", @@ -76,6 +82,7 @@ { "BriefDescription": "Counts the number of near CALL branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF6, SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_CALL", "SampleAfterValue": "200003", @@ -92,6 +99,7 @@ { "BriefDescription": "Counts the number of near taken branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_TAKEN", "SampleAfterValue": "200003", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/srf-metrics.json b/tools/perf/pmu-events/arch/x86/sierraforest/srf-metrics.json index ef629e4e91ce..b9f3c611d87b 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/srf-metrics.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/srf-metrics.json @@ -61,6 +61,18 @@ "MetricName": "cpi", "ScaleUnit": "1per_instr" }, + { + "BriefDescription": "The average number of cores that are in cstate C0 as observed by the power control unit (PCU)", + "MetricExpr": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C0 / pcu_0@UNC_P_CLOCKTICKS@ * #num_packages", + "MetricGroup": "cpu_cstate", + "MetricName": "cpu_cstate_c0" + }, + { + "BriefDescription": "The average number of cores that are in cstate C6 as observed by the power control unit (PCU)", + "MetricExpr": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C6 / pcu_0@UNC_P_CLOCKTICKS@ * #num_packages", + "MetricGroup": "cpu_cstate", + "MetricName": "cpu_cstate_c6" + }, { "BriefDescription": "CPU operating frequency (in GHz)", "MetricExpr": "CPU_CLK_UNHALTED.THREAD / CPU_CLK_UNHALTED.REF_TSC * #SYSTEM_TSC_FREQ / 1e9", @@ -112,6 +124,12 @@ "MetricName": "io_bandwidth_read", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "Bandwidth of inbound IO reads that are initiated by end device controllers that are requesting memory from the CPU and miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read_l3_miss", + "ScaleUnit": "1MB/s" + }, { "BriefDescription": "Bandwidth of IO reads that are initiated by end device controllers that are requesting memory from the local CPU socket", "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_LOCAL * 64 / 1e6 / duration_time", @@ -130,6 +148,12 @@ "MetricName": "io_bandwidth_write", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "Bandwidth of inbound IO writes that are initiated by end device controllers that are writing memory to the CPU", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOM + UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write_l3_miss", + "ScaleUnit": "1MB/s" + }, { "BriefDescription": "Bandwidth of IO writes that are initiated by end device controllers that are writing memory to the local CPU socket", "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOM_LOCAL + UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_LOCAL) * 64 / 1e6 / duration_time", @@ -142,6 +166,30 @@ "MetricName": "io_bandwidth_write_remote", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "The percent of inbound full cache line writes initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM / UNC_CHA_TOR_INSERTS.IO_ITOM", + "MetricName": "io_full_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "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", + "MetricName": "io_msi", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "The percent of inbound partial writes initiated by IO that miss the L3 cache", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_MISS_RFO) / (UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_RFO)", + "MetricName": "io_partial_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "The percent of inbound reads initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR / UNC_CHA_TOR_INSERTS.IO_PCIRDCUR", + "MetricName": "io_read_l3_miss", + "ScaleUnit": "100%" + }, { "BriefDescription": "Ratio of number of completed page walks (for 2 megabyte and 4 megabyte page sizes) caused by a code fetch to the total number of completed instructions", "MetricExpr": "ITLB_MISSES.WALK_COMPLETED_2M_4M / INST_RETIRED.ANY", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-cache.json b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-cache.json index 7182ca00ef8d..3d1fb5f0417e 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-cache.json @@ -874,7 +874,7 @@ "Unit": "CHA" }, { - "BriefDescription": "Counts snoop filter capacity evictions for entries tracking exclusive lines in the cores? cache.? Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry.? Does not count clean evictions such as when a core?s cache replaces a tracked cacheline with a new cacheline.", + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking exclusive lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", "Counter": "0,1,2,3", "EventCode": "0x3d", "EventName": "UNC_CHA_SF_EVICTION.E_STATE", @@ -885,7 +885,7 @@ "Unit": "CHA" }, { - "BriefDescription": "Counts snoop filter capacity evictions for entries tracking modified lines in the cores? cache.? Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry.? Does not count clean evictions such as when a core?s cache replaces a tracked cacheline with a new cacheline.", + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking modified lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", "Counter": "0,1,2,3", "EventCode": "0x3d", "EventName": "UNC_CHA_SF_EVICTION.M_STATE", @@ -895,7 +895,7 @@ "Unit": "CHA" }, { - "BriefDescription": "Counts snoop filter capacity evictions for entries tracking shared lines in the cores? cache.? Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry.? Does not count clean evictions such as when a core?s cache replaces a tracked cacheline with a new cacheline.", + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking shared lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", "Counter": "0,1,2,3", "EventCode": "0x3d", "EventName": "UNC_CHA_SF_EVICTION.S_STATE", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-interconnect.json index 2ccbc8bca24e..952b6de3fefc 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-interconnect.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-interconnect.json @@ -1562,21 +1562,56 @@ "Unit": "UPI" }, { - "BriefDescription": "Tx Flit Buffer Allocations : Number of allocations into the UPI Tx Flit Buffer. Generally, when data is transmitted across UPI, it will bypass the TxQ and pass directly to the link. However, the TxQ will be used with L0p and when LLR occurs, increasing latency to transfer out to the link. This event can be used in conjunction with the Flit Buffer Occupancy event in order to calculate the average flit buffer lifetime.", - "Counter": "0,1,2,3", - "EventCode": "0x40", - "EventName": "UNC_UPI_TxL_INSERTS", + "BriefDescription": "Message Received : Doorbell", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.DOORBELL_RCVD", "Experimental": "1", "PerPkg": "1", - "Unit": "UPI" + "UMask": "0x8", + "Unit": "UBOX" }, { - "BriefDescription": "Tx Flit Buffer Occupancy : Accumulates the number of flits in the TxQ. Generally, when data is transmitted across UPI, it will bypass the TxQ and pass directly to the link. However, the TxQ will be used with L0p and when LLR occurs, increasing latency to transfer out to the link. This can be used with the cycles not empty event to track average occupancy, or the allocations event to track average lifetime in the TxQ.", - "Counter": "0,1,2,3", + "BriefDescription": "Message Received : Interrupt", + "Counter": "0,1", "EventCode": "0x42", - "EventName": "UNC_UPI_TxL_OCCUPANCY", + "EventName": "UNC_U_EVENT_MSG.INT_PRIO", "Experimental": "1", "PerPkg": "1", - "Unit": "UPI" + "PublicDescription": "Message Received : Interrupt : Interrupts", + "UMask": "0x10", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : IPI", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.IPI_RCVD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Message Received : IPI : Inter Processor Interrupts", + "UMask": "0x4", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : MSI", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.MSI_RCVD", + "PerPkg": "1", + "PublicDescription": "Message Received : MSI : Message Signaled Interrupts - interrupts sent by devices (including PCIe via IOxAPIC) (Socket Mode only)", + "UMask": "0x2", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : VLW", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.VLW_RCVD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Message Received : VLW : Virtual Logical Wire (legacy) message were received from Uncore.", + "UMask": "0x1", + "Unit": "UBOX" } ] diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-io.json b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-io.json index 886b99a971be..f4f956966e16 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/uncore-io.json @@ -1121,8 +1121,9 @@ "Unit": "IIO" }, { - "BriefDescription": "Occupancy of outbound request queue : To device : Counts number of outbound requests/completions IIO is currently processing", + "BriefDescription": "This event is deprecated. [This event is alias to UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO]", "Counter": "2,3", + "Deprecated": "1", "EventCode": "0xc5", "EventName": "UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO", "Experimental": "1", @@ -1132,6 +1133,18 @@ "UMask": "0x8", "Unit": "IIO" }, + { + "BriefDescription": "Occupancy of outbound request queue : To device : Counts number of outbound requests/completions IIO is currently processing [This event is alias to UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO]", + "Counter": "2,3", + "EventCode": "0xc5", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, { "BriefDescription": "Passing data to be written", "Counter": "0,1,2,3", @@ -1300,6 +1313,18 @@ "UMask": "0x4", "Unit": "IIO" }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x0FF", + "PublicDescription": "-", + "UMask": "0x4", + "Unit": "IIO" + }, { "BriefDescription": "All 9 bits of Page Walk Tracker Occupancy", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/skylakex/pipeline.json b/tools/perf/pmu-events/arch/x86/skylakex/pipeline.json index 3dd296ab4d78..9a1349527b66 100644 --- a/tools/perf/pmu-events/arch/x86/skylakex/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/skylakex/pipeline.json @@ -542,7 +542,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4C", "EventName": "LOAD_HIT_PRE.SW_PF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", "SampleAfterValue": "100003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/arch/x86/tigerlake/pipeline.json b/tools/perf/pmu-events/arch/x86/tigerlake/pipeline.json index 7ef1bac08463..b417e1db9e07 100644 --- a/tools/perf/pmu-events/arch/x86/tigerlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/tigerlake/pipeline.json @@ -497,7 +497,7 @@ "Counter": "0,1,2,3", "EventCode": "0x4c", "EventName": "LOAD_HIT_PREFETCH.SWPF", - "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", + "PublicDescription": "Counts all software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.", "SampleAfterValue": "100003", "UMask": "0x1" }, diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index d4017007a991..041c598b16d8 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -19,109 +19,147 @@ struct pmu_table_entry { }; static const char *const big_c_string = -/* offset=0 */ "tool\000" -/* offset=5 */ "duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000" -/* offset=81 */ "user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000" -/* offset=151 */ "system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000" -/* offset=219 */ "has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000" -/* offset=295 */ "num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000" -/* offset=440 */ "num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000" -/* offset=543 */ "num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000" -/* offset=660 */ "num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000" -/* offset=736 */ "num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000" -/* offset=822 */ "slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000" -/* offset=932 */ "smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000" -/* offset=1039 */ "system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000" -/* offset=1138 */ "default_core\000" -/* offset=1151 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000" -/* offset=1213 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000" -/* offset=1275 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000" -/* offset=1373 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000" -/* offset=1475 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000" -/* offset=1608 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000" -/* offset=1726 */ "hisi_sccl,ddrc\000" -/* offset=1741 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000DDRC write commands\000" -/* offset=1830 */ "uncore_cbox\000" -/* offset=1842 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000" -/* offset=2076 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000UNC_CBO_HYPHEN\000" -/* offset=2144 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000UNC_CBO_TWO_HYPH\000" -/* offset=2218 */ "hisi_sccl,l3c\000" -/* offset=2232 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000Total read hits\000" -/* offset=2315 */ "uncore_imc_free_running\000" -/* offset=2339 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000Total cache misses\000" -/* offset=2437 */ "uncore_imc\000" -/* offset=2448 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000Total cache hits\000" -/* offset=2529 */ "uncore_sys_ddr_pmu\000" -/* offset=2548 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000" -/* offset=2624 */ "uncore_sys_ccn_pmu\000" -/* offset=2643 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000" -/* offset=2720 */ "uncore_sys_cmn_pmu\000" -/* offset=2739 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000" -/* offset=2882 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000" -/* offset=2904 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000" -/* offset=2967 */ "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\00000" -/* offset=3133 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" -/* offset=3197 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" -/* offset=3264 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000" -/* offset=3335 */ "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\00000" -/* offset=3429 */ "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\00000" -/* offset=3563 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000" -/* offset=3627 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000" -/* offset=3695 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000" -/* offset=3765 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000" -/* offset=3787 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000" -/* offset=3809 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000" -/* offset=3829 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000" +/* offset=0 */ "software\000" +/* offset=9 */ "cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\000\00000\000\000\000\000\000" +/* offset=87 */ "task-clock\000software\000Per-task high-resolution timer based event\000config=1\000\00000\000\000\000\000\000" +/* offset=167 */ "faults\000software\000Number of page faults [This event is an alias of page-faults]\000config=2\000\00000\000\000\000\000\000" +/* offset=262 */ "page-faults\000software\000Number of page faults [This event is an alias of faults]\000config=2\000\00000\000\000\000\000\000" +/* offset=357 */ "context-switches\000software\000Number of context switches [This event is an alias of cs]\000config=3\000\00000\000\000\000\000\000" +/* offset=458 */ "cs\000software\000Number of context switches [This event is an alias of context-switches]\000config=3\000\00000\000\000\000\000\000" +/* offset=559 */ "cpu-migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of migrations]\000config=4\000\00000\000\000\000\000\000" +/* offset=691 */ "migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]\000config=4\000\00000\000\000\000\000\000" +/* offset=823 */ "minor-faults\000software\000Number of minor page faults. Minor faults don't require I/O to handle\000config=5\000\00000\000\000\000\000\000" +/* offset=932 */ "major-faults\000software\000Number of major page faults. Major faults require I/O to handle\000config=6\000\00000\000\000\000\000\000" +/* offset=1035 */ "alignment-faults\000software\000Number of kernel handled memory alignment faults\000config=7\000\00000\000\000\000\000\000" +/* offset=1127 */ "emulation-faults\000software\000Number of kernel handled unimplemented instruction faults handled through emulation\000config=8\000\00000\000\000\000\000\000" +/* offset=1254 */ "dummy\000software\000A placeholder event that doesn't count anything\000config=9\000\00000\000\000\000\000\000" +/* offset=1334 */ "bpf-output\000software\000An event used by BPF programs to write to the perf ring buffer\000config=0xa\000\00000\000\000\000\000\000" +/* offset=1436 */ "cgroup-switches\000software\000Number of context switches to a task in a different cgroup\000config=0xb\000\00000\000\000\000\000\000" +/* offset=1539 */ "tool\000" +/* offset=1544 */ "duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000" +/* offset=1620 */ "user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000" +/* offset=1690 */ "system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000" +/* offset=1758 */ "has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000" +/* offset=1834 */ "num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000" +/* offset=1979 */ "num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000" +/* offset=2082 */ "num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000" +/* offset=2199 */ "num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000" +/* offset=2275 */ "num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000" +/* offset=2361 */ "slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000" +/* offset=2471 */ "smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000" +/* offset=2578 */ "system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000" +/* offset=2677 */ "default_core\000" +/* offset=2690 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000" +/* offset=2752 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000" +/* offset=2814 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000" +/* offset=2912 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000" +/* offset=3014 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000" +/* offset=3147 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000" +/* offset=3265 */ "hisi_sccl,ddrc\000" +/* offset=3280 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000\000" +/* offset=3350 */ "uncore_cbox\000" +/* offset=3362 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000\000" +/* offset=3516 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000\000" +/* offset=3570 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000\000" +/* offset=3628 */ "hisi_sccl,l3c\000" +/* offset=3642 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000\000" +/* offset=3710 */ "uncore_imc_free_running\000" +/* offset=3734 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000\000" +/* offset=3814 */ "uncore_imc\000" +/* offset=3825 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000\000" +/* offset=3890 */ "uncore_sys_ddr_pmu\000" +/* offset=3909 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000" +/* offset=3985 */ "uncore_sys_ccn_pmu\000" +/* offset=4004 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000" +/* offset=4081 */ "uncore_sys_cmn_pmu\000" +/* offset=4100 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000" +/* offset=4243 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000" +/* offset=4265 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000" +/* offset=4328 */ "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\00000" +/* offset=4494 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=4558 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=4625 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000" +/* offset=4696 */ "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\00000" +/* offset=4790 */ "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\00000" +/* offset=4924 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000" +/* offset=4988 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=5056 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=5126 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000" +/* offset=5148 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000" +/* offset=5170 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000" +/* offset=5190 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000" ; +static const struct compact_pmu_event pmu_events__common_software[] = { +{ 1035 }, /* alignment-faults\000software\000Number of kernel handled memory alignment faults\000config=7\000\00000\000\000\000\000\000 */ +{ 1334 }, /* bpf-output\000software\000An event used by BPF programs to write to the perf ring buffer\000config=0xa\000\00000\000\000\000\000\000 */ +{ 1436 }, /* cgroup-switches\000software\000Number of context switches to a task in a different cgroup\000config=0xb\000\00000\000\000\000\000\000 */ +{ 357 }, /* context-switches\000software\000Number of context switches [This event is an alias of cs]\000config=3\000\00000\000\000\000\000\000 */ +{ 9 }, /* cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\000\00000\000\000\000\000\000 */ +{ 559 }, /* cpu-migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of migrations]\000config=4\000\00000\000\000\000\000\000 */ +{ 458 }, /* cs\000software\000Number of context switches [This event is an alias of context-switches]\000config=3\000\00000\000\000\000\000\000 */ +{ 1254 }, /* dummy\000software\000A placeholder event that doesn't count anything\000config=9\000\00000\000\000\000\000\000 */ +{ 1127 }, /* emulation-faults\000software\000Number of kernel handled unimplemented instruction faults handled through emulation\000config=8\000\00000\000\000\000\000\000 */ +{ 167 }, /* faults\000software\000Number of page faults [This event is an alias of page-faults]\000config=2\000\00000\000\000\000\000\000 */ +{ 932 }, /* major-faults\000software\000Number of major page faults. Major faults require I/O to handle\000config=6\000\00000\000\000\000\000\000 */ +{ 691 }, /* migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]\000config=4\000\00000\000\000\000\000\000 */ +{ 823 }, /* minor-faults\000software\000Number of minor page faults. Minor faults don't require I/O to handle\000config=5\000\00000\000\000\000\000\000 */ +{ 262 }, /* page-faults\000software\000Number of page faults [This event is an alias of faults]\000config=2\000\00000\000\000\000\000\000 */ +{ 87 }, /* task-clock\000software\000Per-task high-resolution timer based event\000config=1\000\00000\000\000\000\000\000 */ +}; static const struct compact_pmu_event pmu_events__common_tool[] = { -{ 5 }, /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000 */ -{ 219 }, /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000 */ -{ 295 }, /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000 */ -{ 440 }, /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000 */ -{ 543 }, /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000 */ -{ 660 }, /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000 */ -{ 736 }, /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000 */ -{ 822 }, /* slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000 */ -{ 932 }, /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000 */ -{ 151 }, /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000 */ -{ 1039 }, /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000 */ -{ 81 }, /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000 */ +{ 1544 }, /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000 */ +{ 1758 }, /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000 */ +{ 1834 }, /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000 */ +{ 1979 }, /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000 */ +{ 2082 }, /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000 */ +{ 2199 }, /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000 */ +{ 2275 }, /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000 */ +{ 2361 }, /* slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000 */ +{ 2471 }, /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000 */ +{ 1690 }, /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000 */ +{ 2578 }, /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000 */ +{ 1620 }, /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000 */ }; const struct pmu_table_entry pmu_events__common[] = { +{ + .entries = pmu_events__common_software, + .num_entries = ARRAY_SIZE(pmu_events__common_software), + .pmu_name = { 0 /* software\000 */ }, +}, { .entries = pmu_events__common_tool, .num_entries = ARRAY_SIZE(pmu_events__common_tool), - .pmu_name = { 0 /* tool\000 */ }, + .pmu_name = { 1539 /* tool\000 */ }, }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_default_core[] = { -{ 1151 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000 */ -{ 1213 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000 */ -{ 1475 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000 */ -{ 1608 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000 */ -{ 1275 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000 */ -{ 1373 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000 */ +{ 2690 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000 */ +{ 2752 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000 */ +{ 3014 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000 */ +{ 3147 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000 */ +{ 2814 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000 */ +{ 2912 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_ddrc[] = { -{ 1741 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000DDRC write commands\000 */ +{ 3280 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_l3c[] = { -{ 2232 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000Total read hits\000 */ +{ 3642 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_cbox[] = { -{ 2076 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000UNC_CBO_HYPHEN\000 */ -{ 2144 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000UNC_CBO_TWO_HYPH\000 */ -{ 1842 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */ +{ 3516 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000\000 */ +{ 3570 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000\000 */ +{ 3362 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc[] = { -{ 2448 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000Total cache hits\000 */ +{ 3825 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc_free_running[] = { -{ 2339 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000Total cache misses\000 */ +{ 3734 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000\000 */ }; @@ -129,51 +167,51 @@ const struct pmu_table_entry pmu_events__test_soc_cpu[] = { { .entries = pmu_events__test_soc_cpu_default_core, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core), - .pmu_name = { 1138 /* default_core\000 */ }, + .pmu_name = { 2677 /* default_core\000 */ }, }, { .entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc), - .pmu_name = { 1726 /* hisi_sccl,ddrc\000 */ }, + .pmu_name = { 3265 /* hisi_sccl,ddrc\000 */ }, }, { .entries = pmu_events__test_soc_cpu_hisi_sccl_l3c, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c), - .pmu_name = { 2218 /* hisi_sccl,l3c\000 */ }, + .pmu_name = { 3628 /* hisi_sccl,l3c\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_cbox, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox), - .pmu_name = { 1830 /* uncore_cbox\000 */ }, + .pmu_name = { 3350 /* uncore_cbox\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_imc, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc), - .pmu_name = { 2437 /* uncore_imc\000 */ }, + .pmu_name = { 3814 /* uncore_imc\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_imc_free_running, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running), - .pmu_name = { 2315 /* uncore_imc_free_running\000 */ }, + .pmu_name = { 3710 /* uncore_imc_free_running\000 */ }, }, }; static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = { -{ 2882 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */ -{ 3563 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */ -{ 3335 }, /* 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\00000 */ -{ 3429 }, /* 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\00000 */ -{ 3627 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ -{ 3695 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ -{ 2967 }, /* 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\00000 */ -{ 2904 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */ -{ 3829 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */ -{ 3765 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */ -{ 3787 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */ -{ 3809 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */ -{ 3264 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */ -{ 3133 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ -{ 3197 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ +{ 4243 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */ +{ 4924 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */ +{ 4696 }, /* 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\00000 */ +{ 4790 }, /* 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\00000 */ +{ 4988 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 5056 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 4328 }, /* 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\00000 */ +{ 4265 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */ +{ 5190 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */ +{ 5126 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */ +{ 5148 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */ +{ 5170 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */ +{ 4625 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */ +{ 4494 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ +{ 4558 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ }; @@ -181,18 +219,18 @@ const struct pmu_table_entry pmu_metrics__test_soc_cpu[] = { { .entries = pmu_metrics__test_soc_cpu_default_core, .num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core), - .pmu_name = { 1138 /* default_core\000 */ }, + .pmu_name = { 2677 /* default_core\000 */ }, }, }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ccn_pmu[] = { -{ 2643 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000 */ +{ 4004 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_cmn_pmu[] = { -{ 2739 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000 */ +{ 4100 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ddr_pmu[] = { -{ 2548 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000 */ +{ 3909 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000 */ }; @@ -200,17 +238,17 @@ const struct pmu_table_entry pmu_events__test_soc_sys[] = { { .entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu), - .pmu_name = { 2624 /* uncore_sys_ccn_pmu\000 */ }, + .pmu_name = { 3985 /* uncore_sys_ccn_pmu\000 */ }, }, { .entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu), - .pmu_name = { 2720 /* uncore_sys_cmn_pmu\000 */ }, + .pmu_name = { 4081 /* uncore_sys_cmn_pmu\000 */ }, }, { .entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu), - .pmu_name = { 2529 /* uncore_sys_ddr_pmu\000 */ }, + .pmu_name = { 3890 /* uncore_sys_ddr_pmu\000 */ }, }, }; @@ -632,8 +670,20 @@ static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) { struct perf_cpu cpu = {-1}; - if (pmu) + if (pmu) { + for (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { + const char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; + + if (!strcmp(pmu_name, pmu->name)) { + const struct pmu_events_map *map = &pmu_events_map[0]; + + while (strcmp("common", map->arch)) + map++; + return map; + } + } cpu = perf_cpu_map__min(pmu->cpus); + } return map_for_cpu(cpu); } diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index a1899f35ec74..168c044dd7cc 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -235,6 +235,7 @@ class JsonEvent: 'NO_GROUP_EVENTS_NMI': '2', 'NO_NMI_WATCHDOG': '2', 'NO_GROUP_EVENTS_SMT': '3', + 'NO_THRESHOLD_AND_NMI': '4', } return metric_constraint_to_enum[metric_constraint] @@ -295,6 +296,7 @@ class JsonEvent: 'cpu_atom': 'cpu_atom', 'ali_drw': 'ali_drw', 'arm_cmn': 'arm_cmn', + 'software': 'software', 'tool': 'tool', } return table[unit] if unit in table else f'uncore_{unit.lower()}' @@ -397,6 +399,9 @@ class JsonEvent: self.desc += extra_desc if self.long_desc and extra_desc: self.long_desc += extra_desc + if self.desc and self.long_desc and self.desc == self.long_desc: + # Avoid duplicated descriptions. + self.long_desc = None if arch_std: if arch_std.lower() in _arch_std_events: event = _arch_std_events[arch_std.lower()].event @@ -1155,8 +1160,20 @@ static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) { struct perf_cpu cpu = {-1}; - if (pmu) + if (pmu) { + for (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { + const char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; + + if (!strcmp(pmu_name, pmu->name)) { + const struct pmu_events_map *map = &pmu_events_map[0]; + + while (strcmp("common", map->arch)) + map++; + return map; + } + } cpu = perf_cpu_map__min(pmu->cpus); + } return map_for_cpu(cpu); } diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index a523936846e0..ea022ea55087 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -25,15 +25,21 @@ enum metric_event_groups { */ MetricNoGroupEvents = 1, /** - * @MetricNoGroupEventsNmi: Don't group events for the metric if the NMI - * watchdog is enabled. + * @MetricNoGroupEventsNmi: + * Don't group events for the metric if the NMI watchdog is enabled. */ MetricNoGroupEventsNmi = 2, /** - * @MetricNoGroupEventsSmt: Don't group events for the metric if SMT is - * enabled. + * @MetricNoGroupEventsSmt: + * Don't group events for the metric if SMT is enabled. */ MetricNoGroupEventsSmt = 3, + /** + * @MetricNoGroupEventsThresholdAndNmi: + * Don't group events for the metric thresholds and if the NMI watchdog + * is enabled. + */ + MetricNoGroupEventsThresholdAndNmi = 4, }; /* * Describe each PMU event. Each CPU has a table of PMU events. diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py index cf7ce8229a6c..ad735990c5be 100755 --- a/tools/perf/scripts/python/flamegraph.py +++ b/tools/perf/scripts/python/flamegraph.py @@ -18,7 +18,6 @@ # pylint: disable=missing-class-docstring # pylint: disable=missing-function-docstring -from __future__ import print_function import argparse import hashlib import io @@ -26,9 +25,10 @@ import json import os import subprocess import sys +from typing import Dict, Optional, Union import urllib.request -minimal_html = """ +MINIMAL_HTML = """ @@ -50,20 +50,20 @@ minimal_html = """ # pylint: disable=too-few-public-methods class Node: - def __init__(self, name, libtype): + def __init__(self, name: str, libtype: str): self.name = name # "root" | "kernel" | "" # "" indicates user space self.libtype = libtype - self.value = 0 - self.children = [] + self.value: int = 0 + self.children: list[Node] = [] - def to_json(self): + def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]: return { "n": self.name, "l": self.libtype, "v": self.value, - "c": self.children + "c": [x.to_json() for x in self.children] } @@ -73,7 +73,7 @@ class FlameGraphCLI: self.stack = Node("all", "root") @staticmethod - def get_libtype_from_dso(dso): + def get_libtype_from_dso(dso: Optional[str]) -> str: """ when kernel-debuginfo is installed, dso points to /usr/lib/debug/lib/modules/*/vmlinux @@ -84,7 +84,7 @@ class FlameGraphCLI: return "" @staticmethod - def find_or_create_node(node, name, libtype): + def find_or_create_node(node: Node, name: str, libtype: str) -> Node: for child in node.children: if child.name == name: return child @@ -93,7 +93,12 @@ class FlameGraphCLI: node.children.append(child) return child - def process_event(self, event): + def process_event(self, event) -> None: + # ignore events where the event name does not match + # the one specified by the user + if self.args.event_name and event.get("ev_name") != self.args.event_name: + return + pid = event.get("sample", {}).get("pid", 0) # event["dso"] sometimes contains /usr/lib/debug/lib/modules/*/vmlinux # for user-space processes; let's use pid for kernel or user-space distinction @@ -101,7 +106,7 @@ class FlameGraphCLI: comm = event["comm"] libtype = "kernel" else: - comm = "{} ({})".format(event["comm"], pid) + comm = f"{event['comm']} ({pid})" libtype = "" node = self.find_or_create_node(self.stack, comm, libtype) @@ -116,20 +121,30 @@ class FlameGraphCLI: node = self.find_or_create_node(node, name, libtype) node.value += 1 - def get_report_header(self): + def get_report_header(self) -> str: if self.args.input == "-": # when this script is invoked with "perf script flamegraph", # no perf.data is created and we cannot read the header of it return "" try: - output = subprocess.check_output(["perf", "report", "--header-only"]) - return output.decode("utf-8") + # if the file name other than perf.data is given, + # we read the header of that file + if self.args.input: + output = subprocess.check_output(["perf", "report", "--header-only", + "-i", self.args.input]) + else: + output = subprocess.check_output(["perf", "report", "--header-only"]) + + result = output.decode("utf-8") + if self.args.event_name: + result += "\nFocused event: " + self.args.event_name + return result except Exception as err: # pylint: disable=broad-except - print("Error reading report header: {}".format(err), file=sys.stderr) + print(f"Error reading report header: {err}", file=sys.stderr) return "" - def trace_end(self): + def trace_end(self) -> None: stacks_json = json.dumps(self.stack, default=lambda x: x.to_json()) if self.args.format == "html": @@ -153,7 +168,8 @@ graph template (--template PATH) or use another output format (--format FORMAT).""", file=sys.stderr) if self.args.input == "-": - print("""Not attempting to download Flame Graph template as script command line + print( +"""Not attempting to download Flame Graph template as script command line input is disabled due to using live mode. If you want to download the template retry without live mode. For example, use 'perf record -a -g -F 99 sleep 60' and 'perf script report flamegraph'. Alternatively, @@ -162,37 +178,40 @@ https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-b and place it at: /usr/share/d3-flame-graph/d3-flamegraph-base.html""", file=sys.stderr) - quit() + sys.exit(1) s = None - while s != "y" and s != "n": - s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower() + while s not in ["y", "n"]: + s = input("Do you wish to download a template from cdn.jsdelivr.net?" + + "(this warning can be suppressed with --allow-download) [yn] " + ).lower() if s == "n": - quit() - template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html" + sys.exit(1) + template = ("https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/" + "d3-flamegraph-base.html") template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36" try: - with urllib.request.urlopen(template) as template: + with urllib.request.urlopen(template) as url_template: output_str = "".join([ - l.decode("utf-8") for l in template.readlines() + l.decode("utf-8") for l in url_template.readlines() ]) except Exception as err: print(f"Error reading template {template}: {err}\n" "a minimal flame graph will be generated", file=sys.stderr) - output_str = minimal_html + output_str = MINIMAL_HTML template_md5sum = None if template_md5sum: download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest() if download_md5sum != template_md5sum: s = None - while s != "y" and s != "n": + while s not in ["y", "n"]: s = input(f"""Unexpected template md5sum. {download_md5sum} != {template_md5sum}, for: {output_str} continue?[yn] """).lower() if s == "n": - quit() + sys.exit(1) output_str = output_str.replace("/** @options_json **/", options_json) output_str = output_str.replace("/** @flamegraph_json **/", stacks_json) @@ -206,12 +225,12 @@ continue?[yn] """).lower() with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out: out.write(output_str) else: - print("dumping data to {}".format(output_fn)) + print(f"dumping data to {output_fn}") try: with io.open(output_fn, "w", encoding="utf-8") as out: out.write(output_str) except IOError as err: - print("Error writing output file: {}".format(err), file=sys.stderr) + print(f"Error writing output file: {err}", file=sys.stderr) sys.exit(1) @@ -235,6 +254,11 @@ if __name__ == "__main__": default=False, action="store_true", help="allow unprompted downloading of HTML template") + parser.add_argument("-e", "--event", + default="", + dest="event_name", + type=str, + help="specify the event to generate flamegraph for") cli_args = parser.parse_args() cli = FlameGraphCLI(cli_args) diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 2181f5a92148..3e8394be15ae 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -69,6 +69,7 @@ perf-test-y += symbols.o perf-test-y += util.o perf-test-y += hwmon_pmu.o perf-test-y += tool_pmu.o +perf-test-y += subcmd-help.o ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc)) perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o @@ -89,7 +90,7 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-test-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/backward-ring-buffer.c index 79a980b1e786..c5e7999f2817 100644 --- a/tools/perf/tests/backward-ring-buffer.c +++ b/tools/perf/tests/backward-ring-buffer.c @@ -91,7 +91,6 @@ static int test__backward_ring_buffer(struct test_suite *test __maybe_unused, in struct parse_events_error parse_error; struct record_opts opts = { .target = { - .uid = UINT_MAX, .uses_mmap = true, }, .freq = 0, diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 4cb7d486b5c1..047433c977bc 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -104,6 +104,7 @@ static int bp_accounting(int wp_cnt, int share) fd_wp = wp_event((void *)&the_var, &attr_new); TEST_ASSERT_VAL("failed to create max wp\n", fd_wp != -1); pr_debug("wp max created\n"); + close(fd_wp); } for (i = 0; i < wp_cnt; i++) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 45d3d8b3317a..85142dfb3e01 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -4,8 +4,12 @@ * * Builtin regression testing command: ever growing number of sanity tests */ +#include #include #include +#ifdef HAVE_BACKTRACE_SUPPORT +#include +#endif #include #include #include @@ -136,6 +140,7 @@ static struct test_suite *generic_tests[] = { &suite__event_groups, &suite__symbols, &suite__util, + &suite__subcmd_help, NULL, }; @@ -155,6 +160,71 @@ static struct test_workload *workloads[] = { #define test_suite__for_each_test_case(suite, idx) \ for (idx = 0; (suite)->test_cases && (suite)->test_cases[idx].name != NULL; idx++) +static void close_parent_fds(void) +{ + DIR *dir = opendir("/proc/self/fd"); + struct dirent *ent; + + while ((ent = readdir(dir))) { + char *end; + long fd; + + if (ent->d_type != DT_LNK) + continue; + + if (!isdigit(ent->d_name[0])) + continue; + + fd = strtol(ent->d_name, &end, 10); + if (*end) + continue; + + if (fd <= 3 || fd == dirfd(dir)) + continue; + + close(fd); + } + closedir(dir); +} + +static void check_leaks(void) +{ + DIR *dir = opendir("/proc/self/fd"); + struct dirent *ent; + int leaks = 0; + + while ((ent = readdir(dir))) { + char path[PATH_MAX]; + char *end; + long fd; + ssize_t len; + + if (ent->d_type != DT_LNK) + continue; + + if (!isdigit(ent->d_name[0])) + continue; + + fd = strtol(ent->d_name, &end, 10); + if (*end) + continue; + + if (fd <= 3 || fd == dirfd(dir)) + continue; + + leaks++; + len = readlinkat(dirfd(dir), ent->d_name, path, sizeof(path)); + if (len > 0 && (size_t)len < sizeof(path)) + path[len] = '\0'; + else + strncpy(path, ent->d_name, sizeof(path)); + pr_err("Leak of file descriptor %s that opened: '%s'\n", ent->d_name, path); + } + closedir(dir); + if (leaks) + abort(); +} + static int test_suite__num_test_cases(const struct test_suite *t) { int num; @@ -231,6 +301,16 @@ static jmp_buf run_test_jmp_buf; static void child_test_sig_handler(int sig) { +#ifdef HAVE_BACKTRACE_SUPPORT + void *stackdump[32]; + size_t stackdump_size; +#endif + + fprintf(stderr, "\n---- unexpected signal (%d) ----\n", sig); +#ifdef HAVE_BACKTRACE_SUPPORT + stackdump_size = backtrace(stackdump, ARRAY_SIZE(stackdump)); + __dump_stack(stderr, stackdump, stackdump_size); +#endif siglongjmp(run_test_jmp_buf, sig); } @@ -242,9 +322,11 @@ static int run_test_child(struct child_process *process) struct child_test *child = container_of(process, struct child_test, process); int err; + close_parent_fds(); + err = sigsetjmp(run_test_jmp_buf, 1); if (err) { - fprintf(stderr, "\n---- unexpected signal (%d) ----\n", err); + /* Received signal. */ err = err > 0 ? -err : -1; goto err_out; } @@ -257,6 +339,7 @@ static int run_test_child(struct child_process *process) err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num); pr_debug("---- end(%d) ----\n", err); + check_leaks(); err_out: fflush(NULL); for (size_t i = 0; i < ARRAY_SIZE(signals); i++) @@ -526,6 +609,7 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], for (struct test_suite **t = suites; *t; t++, curr_suite++) { int curr_test_case; + bool suite_matched = false; if (!perf_test__matches(test_description(*t, -1), curr_suite, argc, argv)) { /* @@ -543,6 +627,8 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], } if (skip) continue; + } else { + suite_matched = true; } if (intlist__find(skiplist, curr_suite + 1)) { @@ -554,10 +640,10 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], for (unsigned int run = 0; run < runs_per_test; run++) { test_suite__for_each_test_case(*t, curr_test_case) { - if (!perf_test__matches(test_description(*t, curr_test_case), + if (!suite_matched && + !perf_test__matches(test_description(*t, curr_test_case), curr_suite, argc, argv)) continue; - err = start_test(*t, curr_suite, curr_test_case, &child_tests[child_test_num++], width, pass); diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index cf6edbe697b2..9c2091310191 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -651,11 +651,12 @@ static int do_test_code_reading(bool try_kcore) struct dso *dso; const char *events[] = { "cycles", "cycles:u", "cpu-clock", "cpu-clock:u", NULL }; int evidx = 0; + struct perf_env host_env; pid = getpid(); - machine = machine__new_host(); - machine->env = &perf_env; + perf_env__init(&host_env); + machine = machine__new_host(&host_env); ret = machine__create_kernel_maps(machine); if (ret < 0) { @@ -749,13 +750,6 @@ static int do_test_code_reading(bool try_kcore) pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); } - /* - * Both cpus and threads are now owned by evlist - * and will be freed by following perf_evlist__set_maps - * call. Getting reference to keep them alive. - */ - perf_cpu_map__get(cpus); - perf_thread_map__get(threads); perf_evlist__set_maps(&evlist->core, NULL, NULL); evlist__delete(evlist); evlist = NULL; @@ -798,6 +792,7 @@ static int do_test_code_reading(bool try_kcore) perf_cpu_map__put(cpus); perf_thread_map__put(threads); machine__delete(machine); + perf_env__exit(&host_env); return err; } diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index 54f59d1246bc..80a1c941138d 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -319,11 +319,12 @@ static int run_perf_script(struct test_data *td) static int test__dlfilter_test(struct test_data *td) { + struct perf_env host_env; u64 sample_type = TEST_SAMPLE_TYPE; pid_t pid = 12345; pid_t tid = 12346; u64 id = 99; - int err; + int err = TEST_OK; if (get_dlfilters_path(td->name, td->dlfilters, PATH_MAX)) return test_result("dlfilters not found", TEST_SKIP); @@ -352,38 +353,42 @@ static int test__dlfilter_test(struct test_data *td) return test_result("Failed to find program symbols", TEST_FAIL); pr_debug("Creating new host machine structure\n"); - td->machine = machine__new_host(); - td->machine->env = &perf_env; + perf_env__init(&host_env); + td->machine = machine__new_host(&host_env); td->fd = creat(td->perf_data_file_name, 0644); if (td->fd < 0) return test_result("Failed to create test perf.data file", TEST_FAIL); err = perf_header__write_pipe(td->fd); - if (err < 0) - return test_result("perf_header__write_pipe() failed", TEST_FAIL); - + if (err < 0) { + err = test_result("perf_header__write_pipe() failed", TEST_FAIL); + goto out; + } err = write_attr(td, sample_type, &id); - if (err) - return test_result("perf_event__synthesize_attr() failed", TEST_FAIL); - - if (write_comm(td->fd, pid, tid, "test-prog")) - return TEST_FAIL; - - if (write_mmap(td->fd, pid, tid, MAP_START, 0x10000, 0, td->prog_file_name)) - return TEST_FAIL; - - if (write_sample(td, sample_type, id, pid, tid) != TEST_OK) - return TEST_FAIL; - + if (err) { + err = test_result("perf_event__synthesize_attr() failed", TEST_FAIL); + goto out; + } + if (write_comm(td->fd, pid, tid, "test-prog")) { + err = TEST_FAIL; + goto out; + } + if (write_mmap(td->fd, pid, tid, MAP_START, 0x10000, 0, td->prog_file_name)) { + err = TEST_FAIL; + goto out; + } + if (write_sample(td, sample_type, id, pid, tid) != TEST_OK) { + err = TEST_FAIL; + goto out; + } if (verbose > 1) system_cmd("%s script -i %s -D", td->perf, td->perf_data_file_name); - err = run_perf_script(td); - if (err) - return TEST_FAIL; - - return TEST_OK; + err = run_perf_script(td) ? TEST_FAIL : TEST_OK; +out: + perf_env__exit(&host_env); + return err; } static void unlink_path(const char *path) diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 525c46b7971a..9ed78d00fb87 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -7,6 +7,7 @@ #include #include "tests.h" #include "debug.h" +#include "env.h" #include "machine.h" #include "event.h" #include "../util/unwind.h" @@ -180,6 +181,7 @@ NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_1(struct thread *th noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { + struct perf_env host_env; struct machine *machine; struct thread *thread; int err = -1; @@ -188,15 +190,16 @@ noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused, callchain_param.record_mode = CALLCHAIN_DWARF; dwarf_callchain_users = true; - machine = machine__new_live(/*kernel_maps=*/true, pid); + perf_env__init(&host_env); + machine = machine__new_live(&host_env, /*kernel_maps=*/true, pid); if (!machine) { pr_err("Could not get machine\n"); - return -1; + goto out; } if (machine__create_kernel_maps(machine)) { pr_err("Failed to create kernel maps\n"); - return -1; + goto out; } if (verbose > 1) @@ -213,6 +216,7 @@ noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused, out: machine__delete(machine); + perf_env__exit(&host_env); return err; } diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index deefe5003bfc..ae3b98bb42cf 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -17,9 +17,7 @@ static int attach__enable_on_exec(struct evlist *evlist) { struct evsel *evsel = evlist__last(evlist); - struct target target = { - .uid = UINT_MAX, - }; + struct target target = {}; const char *argv[] = { "true", NULL, }; char sbuf[STRERR_BUFSIZE]; int err; @@ -64,7 +62,7 @@ static int attach__current_disabled(struct evlist *evlist) pr_debug("attaching to current thread as disabled\n"); - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); if (threads == NULL) { pr_debug("thread_map__new\n"); return -1; @@ -90,7 +88,7 @@ static int attach__current_enabled(struct evlist *evlist) pr_debug("attaching to current thread as enabled\n"); - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); if (threads == NULL) { pr_debug("failed to call thread_map__new\n"); return -1; diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index 9301fde11366..cb9e6de2e033 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -109,8 +109,8 @@ static int test__event_update(struct test_suite *test __maybe_unused, int subtes TEST_ASSERT_VAL("failed to synthesize attr update name", !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name)); - perf_cpu_map__put(evsel->core.own_cpus); - evsel->core.own_cpus = perf_cpu_map__new("1,2,3"); + perf_cpu_map__put(evsel->core.pmu_cpus); + evsel->core.pmu_cpus = perf_cpu_map__new("1,2,3"); TEST_ASSERT_VAL("failed to synthesize attr update cpus", !perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus)); diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c index 31966ff856f8..c7b32a220ca1 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -13,8 +13,7 @@ #include #include -static int test_expand_events(struct evlist *evlist, - struct rblist *metric_events) +static int test_expand_events(struct evlist *evlist) { int i, ret = TEST_FAIL; int nr_events; @@ -47,7 +46,7 @@ static int test_expand_events(struct evlist *evlist, was_group_event = evsel__is_group_event(evlist__first(evlist)); nr_members = evlist__first(evlist)->core.nr_members; - ret = evlist__expand_cgroup(evlist, cgrp_str, metric_events, false); + ret = evlist__expand_cgroup(evlist, cgrp_str, false); if (ret < 0) { pr_debug("failed to expand events for cgroups\n"); goto out; @@ -100,13 +99,11 @@ out: for (i = 0; i < nr_events; i++) static int expand_default_events(void) { int ret; - struct rblist metric_events; struct evlist *evlist = evlist__new_default(); TEST_ASSERT_VAL("failed to get evlist", evlist); - rblist__init(&metric_events); - ret = test_expand_events(evlist, &metric_events); + ret = test_expand_events(evlist); evlist__delete(evlist); return ret; } @@ -115,7 +112,6 @@ static int expand_group_events(void) { int ret; struct evlist *evlist; - struct rblist metric_events; struct parse_events_error err; const char event_str[] = "{cycles,instructions}"; @@ -132,8 +128,7 @@ static int expand_group_events(void) goto out; } - rblist__init(&metric_events); - ret = test_expand_events(evlist, &metric_events); + ret = test_expand_events(evlist); out: parse_events_error__exit(&err); evlist__delete(evlist); @@ -144,7 +139,6 @@ static int expand_libpfm_events(void) { int ret; struct evlist *evlist; - struct rblist metric_events; const char event_str[] = "CYCLES"; struct option opt = { .value = &evlist, @@ -166,8 +160,7 @@ static int expand_libpfm_events(void) goto out; } - rblist__init(&metric_events); - ret = test_expand_events(evlist, &metric_events); + ret = test_expand_events(evlist); out: evlist__delete(evlist); return ret; @@ -177,25 +170,22 @@ static int expand_metric_events(void) { int ret; struct evlist *evlist; - struct rblist metric_events; const char metric_str[] = "CPI"; const struct pmu_metrics_table *pme_test; evlist = evlist__new(); TEST_ASSERT_VAL("failed to get evlist", evlist); - rblist__init(&metric_events); pme_test = find_core_metrics_table("testarch", "testcpu"); - ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str, &metric_events); + ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str); if (ret < 0) { pr_debug("failed to parse '%s' metric\n", metric_str); goto out; } - ret = test_expand_events(evlist, &metric_events); + ret = test_expand_events(evlist); out: - metricgroup__rblist_exit(&metric_events); evlist__delete(evlist); return ret; } diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 1e0f5a310fd5..3eb9ef8d7ec6 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -295,7 +295,7 @@ static int test1(struct evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = false; evsel__reset_sample_bit(evsel, CALLCHAIN); - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -442,7 +442,7 @@ static int test2(struct evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = false; evsel__set_sample_bit(evsel, CALLCHAIN); - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -500,7 +500,7 @@ static int test3(struct evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = true; evsel__reset_sample_bit(evsel, CALLCHAIN); - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -684,7 +684,7 @@ static int test4(struct evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = true; evsel__set_sample_bit(evsel, CALLCHAIN); - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); callchain_param = callchain_param_default; callchain_register_param(&callchain_param); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 4b2e4f2fbe48..1cebd20cc91c 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -131,10 +131,6 @@ static int test__hists_filter(struct test_suite *test __maybe_unused, int subtes goto out; err = TEST_FAIL; - /* default sort order (comm,dso,sym) will be used */ - if (setup_sorting(NULL) < 0) - goto out; - machines__init(&machines); /* setup threads/dso/map/symbols also */ @@ -145,6 +141,10 @@ static int test__hists_filter(struct test_suite *test __maybe_unused, int subtes if (verbose > 1) machine__fprintf(machine, stderr); + /* default sort order (comm,dso,sym) will be used */ + if (setup_sorting(evlist, machine->env) < 0) + goto out; + /* process sample events */ err = add_hist_entries(evlist, machine); if (err < 0) diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 5b6f1e883466..996f5f0b3bd1 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -303,10 +303,6 @@ static int test__hists_link(struct test_suite *test __maybe_unused, int subtest goto out; err = TEST_FAIL; - /* default sort order (comm,dso,sym) will be used */ - if (setup_sorting(NULL) < 0) - goto out; - machines__init(&machines); /* setup threads/dso/map/symbols also */ @@ -317,6 +313,10 @@ static int test__hists_link(struct test_suite *test __maybe_unused, int subtest if (verbose > 1) machine__fprintf(machine, stderr); + /* default sort order (comm,dso,sym) will be used */ + if (setup_sorting(evlist, machine->env) < 0) + goto out; + /* process sample events */ err = add_hist_entries(evlist, machine); if (err < 0) diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index 33b5cc8352a7..ee5ec8bda60e 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -146,7 +146,7 @@ static int test1(struct evsel *evsel, struct machine *machine) field_order = NULL; sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */ - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); /* * expected output: @@ -248,7 +248,7 @@ static int test2(struct evsel *evsel, struct machine *machine) field_order = "overhead,cpu"; sort_order = "pid"; - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); /* * expected output: @@ -304,7 +304,7 @@ static int test3(struct evsel *evsel, struct machine *machine) field_order = "comm,overhead,dso"; sort_order = NULL; - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); /* * expected output: @@ -378,7 +378,7 @@ static int test4(struct evsel *evsel, struct machine *machine) field_order = "dso,sym,comm,overhead,dso"; sort_order = "sym"; - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); /* * expected output: @@ -480,7 +480,7 @@ static int test5(struct evsel *evsel, struct machine *machine) field_order = "cpu,pid,comm,dso,sym"; sort_order = "dso,pid"; - setup_sorting(NULL); + setup_sorting(/*evlist=*/NULL, machine->env); /* * expected output: diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index 0837aca1cdfa..151f02701c8c 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -93,9 +93,10 @@ static struct perf_pmu *test_pmu_get(char *dir, size_t sz) pr_err("Failed to mkdir hwmon directory\n"); goto err_out; } - hwmon_dirfd = openat(test_dirfd, "hwmon1234", O_DIRECTORY); + strncat(dir, "/hwmon1234", sz - strlen(dir)); + hwmon_dirfd = open(dir, O_PATH|O_DIRECTORY); if (hwmon_dirfd < 0) { - pr_err("Failed to open test hwmon directory \"%s/hwmon1234\"\n", dir); + pr_err("Failed to open test hwmon directory \"%s\"\n", dir); goto err_out; } file = openat(hwmon_dirfd, "name", O_WRONLY | O_CREAT, 0600); @@ -130,18 +131,18 @@ static struct perf_pmu *test_pmu_get(char *dir, size_t sz) } /* Make the PMU reading the files created above. */ - hwm = perf_pmus__add_test_hwmon_pmu(hwmon_dirfd, "hwmon1234", test_hwmon_name); + hwm = perf_pmus__add_test_hwmon_pmu(dir, "hwmon1234", test_hwmon_name); if (!hwm) pr_err("Test hwmon creation failed\n"); err_out: if (!hwm) { test_pmu_put(dir, hwm); - if (hwmon_dirfd >= 0) - close(hwmon_dirfd); } if (test_dirfd >= 0) close(test_dirfd); + if (hwmon_dirfd >= 0) + close(hwmon_dirfd); return hwm; } diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 5a3b2bed07f3..eafb49eb0b56 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -78,7 +78,7 @@ static int test__keep_tracking(struct test_suite *test __maybe_unused, int subte int found, err = -1; const char *comm; - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); CHECK_NOT_NULL__(threads); cpus = perf_cpu_map__new_online_cpus(); diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 0ee94caf9ec1..c574a678c28a 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -81,6 +81,7 @@ make_no_gtk2 := NO_GTK2=1 make_no_ui := NO_SLANG=1 NO_GTK2=1 make_no_demangle := NO_DEMANGLE=1 make_no_libelf := NO_LIBELF=1 +make_no_libdw := NO_LIBDW=1 make_libunwind := LIBUNWIND=1 make_no_libdw_dwarf_unwind := NO_LIBDW_DWARF_UNWIND=1 make_no_backtrace := NO_BACKTRACE=1 @@ -91,7 +92,6 @@ make_no_auxtrace := NO_AUXTRACE=1 make_no_libbpf := NO_LIBBPF=1 make_libbpf_dynamic := LIBBPF_DYNAMIC=1 make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1 -make_no_libcrypto := NO_LIBCRYPTO=1 make_no_libllvm := NO_LIBLLVM=1 make_with_babeltrace:= LIBBABELTRACE=1 make_with_coresight := CORESIGHT=1 @@ -120,9 +120,9 @@ make_static := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX3 # all the NO_* variable combined make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_GTK2=1 make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1 -make_minimal += NO_LIBNUMA=1 NO_LIBBIONIC=1 +make_minimal += NO_LIBNUMA=1 NO_LIBBIONIC=1 NO_LIBDW=1 make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 -make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1 +make_minimal += NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1 make_minimal += NO_LIBCAP=1 NO_CAPSTONE=1 # $(run) contains all available tests @@ -151,6 +151,7 @@ run += make_no_gtk2 run += make_no_ui run += make_no_demangle run += make_no_libelf +run += make_no_libdw run += make_libunwind run += make_no_libdw_dwarf_unwind run += make_no_backtrace @@ -160,7 +161,6 @@ run += make_no_libbionic run += make_no_auxtrace run += make_no_libbpf run += make_no_libbpf_DEBUG -run += make_no_libcrypto run += make_no_libllvm run += make_no_sdt run += make_no_syscall_tbl diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index bd2106628b34..3c89d3001887 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -1,15 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include +#include "cpumap.h" #include "debug.h" #include "event.h" #include "evlist.h" #include "evsel.h" #include "thread_map.h" #include "tests.h" +#include "util/affinity.h" #include "util/mmap.h" #include "util/sample.h" #include @@ -46,7 +49,7 @@ static int test__basic_mmap(struct test_suite *test __maybe_unused, int subtest char sbuf[STRERR_BUFSIZE]; struct mmap *md; - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); if (threads == NULL) { pr_debug("thread_map__new\n"); return -1; @@ -172,99 +175,199 @@ static int test__basic_mmap(struct test_suite *test __maybe_unused, int subtest return err; } -static int test_stat_user_read(int event) +enum user_read_state { + USER_READ_ENABLED, + USER_READ_DISABLED, + USER_READ_UNKNOWN, +}; + +static enum user_read_state set_user_read(struct perf_pmu *pmu, enum user_read_state enabled) { - struct perf_counts_values counts = { .val = 0 }; - struct perf_thread_map *threads; - struct perf_evsel *evsel; - struct perf_event_mmap_page *pc; - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = event, -#ifdef __aarch64__ - .config1 = 0x2, /* Request user access */ -#endif - }; - int err, i, ret = TEST_FAIL; - bool opened = false, mapped = false; + char buf[2] = {0, '\n'}; + ssize_t len; + int events_fd, rdpmc_fd; + enum user_read_state old_user_read = USER_READ_UNKNOWN; - threads = perf_thread_map__new_dummy(); - TEST_ASSERT_VAL("failed to create threads", threads); + if (enabled == USER_READ_UNKNOWN) + return USER_READ_UNKNOWN; + events_fd = perf_pmu__event_source_devices_fd(); + if (events_fd < 0) + return USER_READ_UNKNOWN; + + rdpmc_fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR); + if (rdpmc_fd < 0) { + close(events_fd); + return USER_READ_UNKNOWN; + } + + len = read(rdpmc_fd, buf, sizeof(buf)); + if (len != sizeof(buf)) + pr_debug("%s read failed\n", __func__); + + // Note, on Intel hybrid disabling on 1 PMU will implicitly disable on + // all the core PMUs. + old_user_read = (buf[0] == '1') ? USER_READ_ENABLED : USER_READ_DISABLED; + + if (enabled != old_user_read) { + buf[0] = (enabled == USER_READ_ENABLED) ? '1' : '0'; + len = write(rdpmc_fd, buf, sizeof(buf)); + if (len != sizeof(buf)) + pr_debug("%s write failed\n", __func__); + } + close(rdpmc_fd); + close(events_fd); + return old_user_read; +} + +static int test_stat_user_read(u64 event, enum user_read_state enabled) +{ + struct perf_pmu *pmu = NULL; + struct perf_thread_map *threads = perf_thread_map__new_dummy(); + int ret = TEST_OK; + + pr_err("User space counter reading %" PRIu64 "\n", event); + if (!threads) { + pr_err("User space counter reading [Failed to create threads]\n"); + return TEST_FAIL; + } perf_thread_map__set_pid(threads, 0, 0); - evsel = perf_evsel__new(&attr); - TEST_ASSERT_VAL("failed to create evsel", evsel); + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + enum user_read_state saved_user_read_state = set_user_read(pmu, enabled); + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = perf_pmus__supports_extended_type() + ? event | ((u64)pmu->type << PERF_PMU_TYPE_SHIFT) + : event, +#ifdef __aarch64__ + .config1 = 0x2, /* Request user access */ +#endif + }; + struct perf_evsel *evsel = NULL; + int err; + struct perf_event_mmap_page *pc; + bool mapped = false, opened = false, rdpmc_supported; + struct perf_counts_values counts = { .val = 0 }; - err = perf_evsel__open(evsel, NULL, threads); - if (err) { - pr_err("failed to open evsel: %s\n", strerror(-err)); - ret = TEST_SKIP; - goto out; - } - opened = true; - err = perf_evsel__mmap(evsel, 0); - if (err) { - pr_err("failed to mmap evsel: %s\n", strerror(-err)); - goto out; - } - mapped = true; + pr_debug("User space counter reading for PMU %s\n", pmu->name); + /* + * Restrict scheduling to only use the rdpmc on the CPUs the + * event can be on. If the test doesn't run on the CPU of the + * event then the event will be disabled and the pc->index test + * will fail. + */ + if (pmu->cpus != NULL) + cpu_map__set_affinity(pmu->cpus); - pc = perf_evsel__mmap_base(evsel, 0, 0); - if (!pc) { - pr_err("failed to get mmapped address\n"); - goto out; - } - - if (!pc->cap_user_rdpmc || !pc->index) { - pr_err("userspace counter access not %s\n", - !pc->cap_user_rdpmc ? "supported" : "enabled"); - ret = TEST_SKIP; - goto out; - } - if (pc->pmc_width < 32) { - pr_err("userspace counter width not set (%d)\n", pc->pmc_width); - goto out; - } - - perf_evsel__read(evsel, 0, 0, &counts); - if (counts.val == 0) { - pr_err("failed to read value for evsel\n"); - goto out; - } - - for (i = 0; i < 5; i++) { - volatile int count = 0x10000 << i; - __u64 start, end, last = 0; - - pr_debug("\tloop = %u, ", count); - - perf_evsel__read(evsel, 0, 0, &counts); - start = counts.val; - - while (count--) ; - - perf_evsel__read(evsel, 0, 0, &counts); - end = counts.val; - - if ((end - start) < last) { - pr_err("invalid counter data: end=%llu start=%llu last= %llu\n", - end, start, last); - goto out; + /* Make the evsel. */ + evsel = perf_evsel__new(&attr); + if (!evsel) { + pr_err("User space counter reading for PMU %s [Failed to allocate evsel]\n", + pmu->name); + ret = TEST_FAIL; + goto cleanup; } - last = end - start; - pr_debug("count = %llu\n", end - start); + + err = perf_evsel__open(evsel, NULL, threads); + if (err) { + pr_err("User space counter reading for PMU %s [Failed to open evsel]\n", + pmu->name); + ret = TEST_SKIP; + goto cleanup; + } + opened = true; + err = perf_evsel__mmap(evsel, 0); + if (err) { + pr_err("User space counter reading for PMU %s [Failed to mmap evsel]\n", + pmu->name); + ret = TEST_FAIL; + goto cleanup; + } + mapped = true; + + pc = perf_evsel__mmap_base(evsel, 0, 0); + if (!pc) { + pr_err("User space counter reading for PMU %s [Failed to get mmaped address]\n", + pmu->name); + ret = TEST_FAIL; + goto cleanup; + } + + if (saved_user_read_state == USER_READ_UNKNOWN) + rdpmc_supported = pc->cap_user_rdpmc && pc->index; + else + rdpmc_supported = (enabled == USER_READ_ENABLED); + + if (rdpmc_supported && (!pc->cap_user_rdpmc || !pc->index)) { + pr_err("User space counter reading for PMU %s [Failed unexpected supported counter access %d %d]\n", + pmu->name, pc->cap_user_rdpmc, pc->index); + ret = TEST_FAIL; + goto cleanup; + } + + if (!rdpmc_supported && pc->cap_user_rdpmc) { + pr_err("User space counter reading for PMU %s [Failed unexpected unsupported counter access %d]\n", + pmu->name, pc->cap_user_rdpmc); + ret = TEST_FAIL; + goto cleanup; + } + + if (rdpmc_supported && pc->pmc_width < 32) { + pr_err("User space counter reading for PMU %s [Failed width not set %d]\n", + pmu->name, pc->pmc_width); + ret = TEST_FAIL; + goto cleanup; + } + + perf_evsel__read(evsel, 0, 0, &counts); + if (counts.val == 0) { + pr_err("User space counter reading for PMU %s [Failed read]\n", pmu->name); + ret = TEST_FAIL; + goto cleanup; + } + + for (int i = 0; i < 5; i++) { + volatile int count = 0x10000 << i; + __u64 start, end, last = 0; + + pr_debug("\tloop = %u, ", count); + + perf_evsel__read(evsel, 0, 0, &counts); + start = counts.val; + + while (count--) ; + + perf_evsel__read(evsel, 0, 0, &counts); + end = counts.val; + + if ((end - start) < last) { + pr_err("User space counter reading for PMU %s [Failed invalid counter data: end=%llu start=%llu last= %llu]\n", + pmu->name, end, start, last); + ret = TEST_FAIL; + goto cleanup; + } + last = end - start; + pr_debug("count = %llu\n", last); + } + pr_debug("User space counter reading for PMU %s [Success]\n", pmu->name); +cleanup: + if (mapped) + perf_evsel__munmap(evsel); + if (opened) + perf_evsel__close(evsel); + perf_evsel__delete(evsel); + + /* If the affinity was changed, then put it back to all CPUs. */ + if (pmu->cpus != NULL) { + struct perf_cpu_map *cpus = cpu_map__online(); + + cpu_map__set_affinity(cpus); + perf_cpu_map__put(cpus); + } + set_user_read(pmu, saved_user_read_state); } - ret = TEST_OK; - -out: - if (mapped) - perf_evsel__munmap(evsel); - if (opened) - perf_evsel__close(evsel); - perf_evsel__delete(evsel); - perf_thread_map__put(threads); return ret; } @@ -272,20 +375,32 @@ static int test_stat_user_read(int event) static int test__mmap_user_read_instr(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS); + return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_ENABLED); } static int test__mmap_user_read_cycles(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES); + return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_ENABLED); +} + +static int test__mmap_user_read_instr_disabled(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_DISABLED); +} + +static int test__mmap_user_read_cycles_disabled(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_DISABLED); } static struct test_case tests__basic_mmap[] = { TEST_CASE_REASON("Read samples using the mmap interface", basic_mmap, "permissions"), - TEST_CASE_REASON("User space counter reading of instructions", + TEST_CASE_REASON_EXCLUSIVE("User space counter reading of instructions", mmap_user_read_instr, #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ (defined(__riscv) && __riscv_xlen == 64) @@ -294,13 +409,31 @@ static struct test_case tests__basic_mmap[] = { "unsupported" #endif ), - TEST_CASE_REASON("User space counter reading of cycles", + TEST_CASE_REASON_EXCLUSIVE("User space counter reading of cycles", mmap_user_read_cycles, #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ (defined(__riscv) && __riscv_xlen == 64) "permissions" #else "unsupported" +#endif + ), + TEST_CASE_REASON_EXCLUSIVE("User space counter disabling instructions", + mmap_user_read_instr_disabled, +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__riscv) && __riscv_xlen == 64) + "permissions" +#else + "unsupported" +#endif + ), + TEST_CASE_REASON_EXCLUSIVE("User space counter disabling cycles", + mmap_user_read_cycles_disabled, +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__riscv) && __riscv_xlen == 64) + "permissions" +#else + "unsupported" #endif ), { .name = NULL, } diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 446a3615d720..0c5619c6e6e9 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -8,6 +8,7 @@ #include #include #include "debug.h" +#include "env.h" #include "event.h" #include "tests.h" #include "machine.h" @@ -155,6 +156,7 @@ static int synth_process(struct machine *machine) static int mmap_events(synth_cb synth) { + struct perf_env host_env; struct machine *machine; int err, i; @@ -167,7 +169,8 @@ static int mmap_events(synth_cb synth) */ TEST_ASSERT_VAL("failed to create threads", !threads_create()); - machine = machine__new_host(); + perf_env__init(&host_env); + machine = machine__new_host(&host_env); dump_trace = verbose > 1 ? 1 : 0; @@ -209,6 +212,7 @@ static int mmap_events(synth_cb synth) } machine__delete(machine); + perf_env__exit(&host_env); return err; } diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c index fb114118c876..3644d6f52c07 100644 --- a/tools/perf/tests/openat-syscall-all-cpus.c +++ b/tools/perf/tests/openat-syscall-all-cpus.c @@ -28,7 +28,7 @@ static int test__openat_syscall_event_on_all_cpus(struct test_suite *test __mayb struct evsel *evsel; unsigned int nr_openat_calls = 111, i; cpu_set_t cpu_set; - struct perf_thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); + struct perf_thread_map *threads = thread_map__new_by_tid(getpid()); char sbuf[STRERR_BUFSIZE]; char errbuf[BUFSIZ]; diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 0ef4ba7c1571..2a139d2781a8 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -28,7 +28,6 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused { struct record_opts opts = { .target = { - .uid = UINT_MAX, .uses_mmap = true, }, .no_buffering = true, diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-syscall.c index 131b62271bfa..b54cbe5f1808 100644 --- a/tools/perf/tests/openat-syscall.c +++ b/tools/perf/tests/openat-syscall.c @@ -20,7 +20,7 @@ static int test__openat_syscall_event(struct test_suite *test __maybe_unused, int err = TEST_FAIL, fd; struct evsel *evsel; unsigned int nr_openat_calls = 111, i; - struct perf_thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); + struct perf_thread_map *threads = thread_map__new_by_tid(getpid()); char sbuf[STRERR_BUFSIZE]; char errbuf[BUFSIZ]; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 5ec2e5607987..bb8004397650 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -719,20 +719,20 @@ static int test__checkevent_pmu_partial_time_callgraph(struct evlist *evlist) static int test__checkevent_pmu_events(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct evsel *evsel; - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || - strcmp(evsel->pmu->name, "cpu")); - TEST_ASSERT_VAL("wrong exclude_user", - !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", - evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); - TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + TEST_ASSERT_VAL("wrong number of entries", 1 <= evlist->core.nr_entries); + evlist__for_each_entry(evlist, evsel) { + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || + strcmp(evsel->pmu->name, "cpu")); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); + TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + } return TEST_OK; } diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c index 2c28fb50dc24..66a5275917e2 100644 --- a/tools/perf/tests/parse-metric.c +++ b/tools/perf/tests/parse-metric.c @@ -45,15 +45,14 @@ static void load_runtime_stat(struct evlist *evlist, struct value *vals) } } -static double compute_single(struct rblist *metric_events, struct evlist *evlist, - const char *name) +static double compute_single(struct evlist *evlist, const char *name) { struct metric_expr *mexp; struct metric_event *me; struct evsel *evsel; evlist__for_each_entry(evlist, evsel) { - me = metricgroup__lookup(metric_events, evsel, false); + me = metricgroup__lookup(&evlist->metric_events, evsel, false); if (me != NULL) { list_for_each_entry (mexp, &me->head, nd) { if (strcmp(mexp->metric_name, name)) @@ -69,9 +68,6 @@ static int __compute_metric(const char *name, struct value *vals, const char *name1, double *ratio1, const char *name2, double *ratio2) { - struct rblist metric_events = { - .nr_entries = 0, - }; const struct pmu_metrics_table *pme_test; struct perf_cpu_map *cpus; struct evlist *evlist; @@ -95,8 +91,7 @@ static int __compute_metric(const char *name, struct value *vals, /* Parse the metric into metric_events list. */ pme_test = find_core_metrics_table("testarch", "testcpu"); - err = metricgroup__parse_groups_test(evlist, pme_test, name, - &metric_events); + err = metricgroup__parse_groups_test(evlist, pme_test, name); if (err) goto out; @@ -109,13 +104,12 @@ static int __compute_metric(const char *name, struct value *vals, /* And execute the metric */ if (name1 && ratio1) - *ratio1 = compute_single(&metric_events, evlist, name1); + *ratio1 = compute_single(evlist, name1); if (name2 && ratio2) - *ratio2 = compute_single(&metric_events, evlist, name2); + *ratio2 = compute_single(evlist, name2); out: /* ... cleanup. */ - metricgroup__rblist_exit(&metric_events); evlist__free_stats(evlist); perf_cpu_map__put(cpus); evlist__delete(evlist); diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c index fff58b220c07..8b31d1d05f90 100644 --- a/tools/perf/tests/pe-file-parsing.c +++ b/tools/perf/tests/pe-file-parsing.c @@ -24,7 +24,7 @@ static int run_dir(const char *d) { char filename[PATH_MAX]; char debugfile[PATH_MAX]; - struct build_id bid; + struct build_id bid = { .size = 0, }; char debuglink[PATH_MAX]; char expect_build_id[] = { 0x5a, 0x0f, 0xd8, 0x82, 0xb5, 0x30, 0x84, 0x22, @@ -37,7 +37,7 @@ static int run_dir(const char *d) size_t idx; scnprintf(filename, PATH_MAX, "%s/pe-file.exe", d); - ret = filename__read_build_id(filename, &bid); + ret = filename__read_build_id(filename, &bid, /*block=*/true); TEST_ASSERT_VAL("Failed to read build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, @@ -49,7 +49,7 @@ static int run_dir(const char *d) !strcmp(debuglink, expect_debuglink)); scnprintf(debugfile, PATH_MAX, "%s/%s", d, debuglink); - ret = filename__read_build_id(debugfile, &bid); + ret = filename__read_build_id(debugfile, &bid, /*block=*/true); TEST_ASSERT_VAL("Failed to read debug file build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 0958c7c8995f..0b3c37e66871 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -45,7 +45,6 @@ static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest { struct record_opts opts = { .target = { - .uid = UINT_MAX, .uses_mmap = true, }, .no_buffering = true, diff --git a/tools/perf/tests/perf-targz-src-pkg b/tools/perf/tests/perf-targz-src-pkg index b3075c168cb2..52a90e6bd8af 100755 --- a/tools/perf/tests/perf-targz-src-pkg +++ b/tools/perf/tests/perf-targz-src-pkg @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # Test one of the main kernel Makefile targets to generate a perf sources tarball # suitable for build outside the full kernel sources. diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index d3e40fa5482c..d4437410c99f 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -90,7 +90,7 @@ static int test__perf_time_to_tsc(struct test_suite *test __maybe_unused, int su struct mmap *md; - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); CHECK_NOT_NULL__(threads); cpus = perf_cpu_map__new_online_cpus(); diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 815b40097428..95fd9f671a22 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -53,7 +53,6 @@ static const struct perf_pmu_test_event bp_l1_btb_correct = { .topic = "branch", }, .alias_str = "event=0x8a", - .alias_long_desc = "L1 BTB Correction", }; static const struct perf_pmu_test_event bp_l2_btb_correct = { @@ -65,7 +64,6 @@ static const struct perf_pmu_test_event bp_l2_btb_correct = { .topic = "branch", }, .alias_str = "event=0x8b", - .alias_long_desc = "L2 BTB Correction", }; static const struct perf_pmu_test_event segment_reg_loads_any = { @@ -77,7 +75,6 @@ static const struct perf_pmu_test_event segment_reg_loads_any = { .topic = "other", }, .alias_str = "event=0x6,period=0x30d40,umask=0x80", - .alias_long_desc = "Number of segment register loads", }; static const struct perf_pmu_test_event dispatch_blocked_any = { @@ -89,7 +86,6 @@ static const struct perf_pmu_test_event dispatch_blocked_any = { .topic = "other", }, .alias_str = "event=0x9,period=0x30d40,umask=0x20", - .alias_long_desc = "Memory cluster signals to block micro-op dispatch for any reason", }; static const struct perf_pmu_test_event eist_trans = { @@ -101,7 +97,6 @@ static const struct perf_pmu_test_event eist_trans = { .topic = "other", }, .alias_str = "event=0x3a,period=0x30d40", - .alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", }; static const struct perf_pmu_test_event l3_cache_rd = { @@ -133,11 +128,9 @@ static const struct perf_pmu_test_event uncore_hisi_ddrc_flux_wcmd = { .event = "event=2", .desc = "DDRC write commands", .topic = "uncore", - .long_desc = "DDRC write commands", .pmu = "hisi_sccl,ddrc", }, .alias_str = "event=0x2", - .alias_long_desc = "DDRC write commands", .matching_pmu = "hisi_sccl1_ddrc2", }; @@ -147,11 +140,9 @@ static const struct perf_pmu_test_event unc_cbo_xsnp_response_miss_eviction = { .event = "event=0x22,umask=0x81", .desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", .topic = "uncore", - .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", .pmu = "uncore_cbox", }, .alias_str = "event=0x22,umask=0x81", - .alias_long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", .matching_pmu = "uncore_cbox_0", }; @@ -161,11 +152,9 @@ static const struct perf_pmu_test_event uncore_hyphen = { .event = "event=0xe0", .desc = "UNC_CBO_HYPHEN", .topic = "uncore", - .long_desc = "UNC_CBO_HYPHEN", .pmu = "uncore_cbox", }, .alias_str = "event=0xe0", - .alias_long_desc = "UNC_CBO_HYPHEN", .matching_pmu = "uncore_cbox_0", }; @@ -175,11 +164,9 @@ static const struct perf_pmu_test_event uncore_two_hyph = { .event = "event=0xc0", .desc = "UNC_CBO_TWO_HYPH", .topic = "uncore", - .long_desc = "UNC_CBO_TWO_HYPH", .pmu = "uncore_cbox", }, .alias_str = "event=0xc0", - .alias_long_desc = "UNC_CBO_TWO_HYPH", .matching_pmu = "uncore_cbox_0", }; @@ -189,11 +176,9 @@ static const struct perf_pmu_test_event uncore_hisi_l3c_rd_hit_cpipe = { .event = "event=7", .desc = "Total read hits", .topic = "uncore", - .long_desc = "Total read hits", .pmu = "hisi_sccl,l3c", }, .alias_str = "event=0x7", - .alias_long_desc = "Total read hits", .matching_pmu = "hisi_sccl3_l3c7", }; @@ -203,11 +188,9 @@ static const struct perf_pmu_test_event uncore_imc_free_running_cache_miss = { .event = "event=0x12", .desc = "Total cache misses", .topic = "uncore", - .long_desc = "Total cache misses", .pmu = "uncore_imc_free_running", }, .alias_str = "event=0x12", - .alias_long_desc = "Total cache misses", .matching_pmu = "uncore_imc_free_running_0", }; @@ -217,11 +200,9 @@ static const struct perf_pmu_test_event uncore_imc_cache_hits = { .event = "event=0x34", .desc = "Total cache hits", .topic = "uncore", - .long_desc = "Total cache hits", .pmu = "uncore_imc", }, .alias_str = "event=0x34", - .alias_long_desc = "Total cache hits", .matching_pmu = "uncore_imc_0", }; @@ -246,7 +227,6 @@ static const struct perf_pmu_test_event sys_ddr_pmu_write_cycles = { .compat = "v8", }, .alias_str = "event=0x2b", - .alias_long_desc = "ddr write-cycles event", .matching_pmu = "uncore_sys_ddr_pmu0", }; @@ -260,7 +240,6 @@ static const struct perf_pmu_test_event sys_ccn_pmu_read_cycles = { .compat = "0x01", }, .alias_str = "config=0x2c", - .alias_long_desc = "ccn read-cycles event", .matching_pmu = "uncore_sys_ccn_pmu4", }; @@ -274,7 +253,6 @@ static const struct perf_pmu_test_event sys_cmn_pmu_hnf_cache_miss = { .compat = "(434|436|43c|43a).*", }, .alias_str = "eventid=0x1,type=0x5", - .alias_long_desc = "Counts total cache misses in first lookup result (high priority)", .matching_pmu = "uncore_sys_cmn_pmu0", }; @@ -868,9 +846,6 @@ static int test__parsing_callback(const struct pmu_metric *pm, struct evlist *evlist; struct perf_cpu_map *cpus; struct evsel *evsel; - struct rblist metric_events = { - .nr_entries = 0, - }; int err = 0; if (!pm->metric_expr) @@ -895,7 +870,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, perf_evlist__set_maps(&evlist->core, cpus, NULL); - err = metricgroup__parse_groups_test(evlist, table, pm->metric_name, &metric_events); + err = metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || !strcmp(pm->metric_name, "M3")) { @@ -922,7 +897,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, k++; } evlist__for_each_entry(evlist, evsel) { - struct metric_event *me = metricgroup__lookup(&metric_events, evsel, false); + struct metric_event *me = metricgroup__lookup(&evlist->metric_events, evsel, false); if (me != NULL) { struct metric_expr *mexp; @@ -944,7 +919,6 @@ static int test__parsing_callback(const struct pmu_metric *pm, pr_debug("Broken metric %s\n", pm->metric_name); /* ... cleanup. */ - metricgroup__rblist_exit(&metric_events); evlist__free_stats(evlist); perf_cpu_map__put(cpus); evlist__delete(evlist); diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 72411580f869..a7327c942ca2 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -152,6 +152,12 @@ static bool samples_same(struct perf_sample *s1, if (type & PERF_SAMPLE_WEIGHT) COMP(weight); + if (type & PERF_SAMPLE_WEIGHT_STRUCT) { + COMP(weight); + COMP(ins_lat); + COMP(weight3); + } + if (type & PERF_SAMPLE_DATA_SRC) COMP(data_src); @@ -269,6 +275,8 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) .cgroup = 114, .data_page_size = 115, .code_page_size = 116, + .ins_lat = 117, + .weight3 = 118, .aux_sample = { .size = sizeof(aux_data), .data = (void *)aux_data, @@ -439,6 +447,12 @@ static int test__sample_parsing(struct test_suite *test __maybe_unused, int subt if (err) return err; } + sample_type = (PERF_SAMPLE_MAX - 1) & ~PERF_SAMPLE_WEIGHT_STRUCT; + for (i = 0; i < ARRAY_SIZE(rf); i++) { + err = do_test(sample_type, sample_regs, rf[i]); + if (err) + return err; + } return 0; } diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c index 919712899251..6132f1af3e22 100644 --- a/tools/perf/tests/sdt.c +++ b/tools/perf/tests/sdt.c @@ -28,16 +28,16 @@ static int target_function(void) static int build_id_cache__add_file(const char *filename) { char sbuild_id[SBUILD_ID_SIZE]; - struct build_id bid; + struct build_id bid = { .size = 0, }; int err; - err = filename__read_build_id(filename, &bid); + err = filename__read_build_id(filename, &bid, /*block=*/true); if (err < 0) { pr_debug("Failed to read build id of %s\n", filename); return err; } - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); err = build_id_cache__add_s(sbuild_id, filename, NULL, false, false); if (err < 0) pr_debug("Failed to add build id cache of %s\n", filename); diff --git a/tools/perf/tests/shell/amd-ibs-swfilt.sh b/tools/perf/tests/shell/amd-ibs-swfilt.sh index 83937aa687cc..7045ec72ba4c 100755 --- a/tools/perf/tests/shell/amd-ibs-swfilt.sh +++ b/tools/perf/tests/shell/amd-ibs-swfilt.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # AMD IBS software filtering echo "check availability of IBS swfilt" diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh index 16a1ccd06089..689de58e9238 100755 --- a/tools/perf/tests/shell/annotate.sh +++ b/tools/perf/tests/shell/annotate.sh @@ -53,21 +53,22 @@ test_basic() { # Generate the annotated output file if [ "x${mode}" == "xBasic" ] then - perf annotate --no-demangle -i "${perfdata}" --stdio 2> /dev/null > "${perfout}" + perf annotate --no-demangle -i "${perfdata}" --stdio --percent-limit 10 2> /dev/null > "${perfout}" else - perf annotate --no-demangle -i - --stdio 2> /dev/null < "${perfdata}" > "${perfout}" + perf annotate --no-demangle -i - --stdio 2> /dev/null --percent-limit 10 < "${perfdata}" > "${perfout}" fi # check if it has the target symbol - if ! head -250 "${perfout}" | grep -q "${testsym}" + if ! grep -q "${testsym}" "${perfout}" then echo "${mode} annotate [Failed: missing target symbol]" + cat "${perfout}" err=1 return fi # check if it has the disassembly lines - if ! head -250 "${perfout}" | grep -q "${disasm_regex}" + if ! grep -q "${disasm_regex}" "${perfout}" then echo "${mode} annotate [Failed: missing disasm output from default disassembler]" err=1 @@ -92,11 +93,11 @@ test_basic() { # check one more with external objdump tool (forced by --objdump option) if [ "x${mode}" == "xBasic" ] then - perf annotate --no-demangle -i "${perfdata}" --objdump=objdump 2> /dev/null > "${perfout}" + perf annotate --no-demangle -i "${perfdata}" --percent-limit 10 --objdump=objdump 2> /dev/null > "${perfout}" else - perf annotate --no-demangle -i - "${testsym}" 2> /dev/null < "${perfdata}" > "${perfout}" + perf annotate --no-demangle -i - "${testsym}" --percent-limit 10 --objdump=objdump 2> /dev/null < "${perfdata}" > "${perfout}" fi - if ! head -250 "${perfout}" | grep -q -m 3 "${disasm_regex}" + if ! grep -q -m 3 "${disasm_regex}" "${perfout}" then echo "${mode} annotate [Failed: missing disasm output from non default disassembler (using --objdump)]" err=1 diff --git a/tools/perf/tests/shell/buildid.sh b/tools/perf/tests/shell/buildid.sh index 3383ca3399d4..d2eb213da01d 100755 --- a/tools/perf/tests/shell/buildid.sh +++ b/tools/perf/tests/shell/buildid.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # build id cache operations # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop.sh b/tools/perf/tests/shell/coresight/asm_pure_loop.sh index c63bc8c73e26..0301904b9637 100755 --- a/tools/perf/tests/shell/coresight/asm_pure_loop.sh +++ b/tools/perf/tests/shell/coresight/asm_pure_loop.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # CoreSight / ASM Pure Loop (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh index 8e29630957c8..1f765d69acc3 100755 --- a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh +++ b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # CoreSight / Memcpy 16k 10 Threads (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh index 0c4c82a1c8e1..7f43a93a2ac2 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # CoreSight / Thread Loop 10 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh index d3aea9fc6ced..a94d2079ed06 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # CoreSight / Thread Loop 2 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh index 7429d3a2ae43..cb3e97a0a89f 100755 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh +++ b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # CoreSight / Unroll Loop Thread 10 (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/diff.sh b/tools/perf/tests/shell/diff.sh index e05a5dc49479..fe05fdebcab5 100755 --- a/tools/perf/tests/shell/diff.sh +++ b/tools/perf/tests/shell/diff.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf diff tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/drm_pmu.sh b/tools/perf/tests/shell/drm_pmu.sh new file mode 100755 index 000000000000..e629fe0e8463 --- /dev/null +++ b/tools/perf/tests/shell/drm_pmu.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# DRM PMU +# SPDX-License-Identifier: GPL-2.0 + +set -e + +output=$(mktemp /tmp/perf.drm_pmu.XXXXXX.txt) + +cleanup() { + rm -f "${output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# Array to store file descriptors and device names +declare -A device_fds + +# Open all devices and store file descriptors. Opening the device will create a +# /proc/$$/fdinfo file containing the DRM statistics. +fd_count=3 # Start with file descriptor 3 +for device in /dev/dri/* +do + if [[ ! -c "$device" ]] + then + continue + fi + major=$(stat -c "%Hr" "$device") + if [[ "$major" != 226 ]] + then + continue + fi + echo "Opening $device" + eval "exec $fd_count<\"$device\"" + echo "fdinfo for: $device (FD: $fd_count)" + cat "/proc/$$/fdinfo/$fd_count" + echo + device_fds["$device"]="$fd_count" + fd_count=$((fd_count + 1)) +done + +if [[ ${#device_fds[@]} -eq 0 ]] +then + echo "No DRM devices found [Skip]" + cleanup + exit 2 +fi + +# For each DRM event +err=0 +for p in $(perf list --raw-dump drm-) +do + echo -n "Testing perf stat of $p. " + perf stat -e "$p" --pid=$$ true > "$output" 2>&1 + if ! grep -q "$p" "$output" + then + echo "Missing DRM event in: [Failed]" + cat "$output" + err=1 + else + echo "[OK]" + fi +done + +# Close all file descriptors +for fd in "${device_fds[@]}"; do + eval "exec $fd<&-" +done + +# Finished +cleanup +exit $err diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh index c243731d2fbf..7f8aafcbb761 100755 --- a/tools/perf/tests/shell/ftrace.sh +++ b/tools/perf/tests/shell/ftrace.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf ftrace tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/header.sh b/tools/perf/tests/shell/header.sh new file mode 100755 index 000000000000..e1628ac0a614 --- /dev/null +++ b/tools/perf/tests/shell/header.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# perf header tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +err=0 +perfdata=$(mktemp /tmp/__perf_test_header.perf.data.XXXXX) +script_output=$(mktemp /tmp/__perf_test_header.perf.data.XXXXX.script) + +cleanup() { + rm -f "${perfdata}" + rm -f "${perfdata}".old + rm -f "${script_output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +check_header_output() { + declare -a fields=( + "captured" + "hostname" + "os release" + "arch" + "cpuid" + "nrcpus" + "event" + "cmdline" + "perf version" + "sibling (cores|dies|threads)" + "sibling threads" + "total memory" + ) + for i in "${fields[@]}" + do + if ! grep -q -E "$i" "${script_output}" + then + echo "Failed to find expected $i in output" + err=1 + fi + done +} + +test_file() { + echo "Test perf header file" + + perf record -o "${perfdata}" -- perf test -w noploop + perf report --header-only -I -i "${perfdata}" > "${script_output}" + check_header_output + + echo "Test perf header file [Done]" +} + +test_pipe() { + echo "Test perf header pipe" + + perf record -o - -- perf test -w noploop | perf report --header-only -I -i - > "${script_output}" + check_header_output + + echo "Test perf header pipe [Done]" +} + +test_file +test_pipe + +cleanup +exit $err diff --git a/tools/perf/tests/shell/lib/perf_has_symbol.sh b/tools/perf/tests/shell/lib/perf_has_symbol.sh index 561c93b75d77..0b35cce0b13d 100644 --- a/tools/perf/tests/shell/lib/perf_has_symbol.sh +++ b/tools/perf/tests/shell/lib/perf_has_symbol.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 perf_has_symbol() diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index 9e772a89ce38..c6750ef06c0f 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -45,7 +45,7 @@ def is_counter_value(num): def check_json_output(expected_items): checks = { - 'aggregate-number': lambda x: isfloat(x), + 'counters': lambda x: isfloat(x), 'core': lambda x: True, 'counter-value': lambda x: is_counter_value(x), 'cgroup': lambda x: True, @@ -75,7 +75,7 @@ def check_json_output(expected_items): if count not in expected_items and count >= 1 and count <= 7 and 'metric-value' in item: # Events that generate >1 metric may have isolated metric # values and possibly other prefixes like interval, core, - # aggregate-number, or event-runtime/pcnt-running from multiplexing. + # counters, or event-runtime/pcnt-running from multiplexing. pass elif count not in expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: pass diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index 58debce9ab42..88cd0e26d5f6 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Arnaldo Carvalho de Melo , 2017 perf probe -l 2>&1 | grep -q probe:vfs_getname diff --git a/tools/perf/tests/shell/lib/setup_python.sh b/tools/perf/tests/shell/lib/setup_python.sh index c2fce1793538..a58e5536f2ed 100644 --- a/tools/perf/tests/shell/lib/setup_python.sh +++ b/tools/perf/tests/shell/lib/setup_python.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 if [ "x$PYTHON" = "x" ] diff --git a/tools/perf/tests/shell/lib/waiting.sh b/tools/perf/tests/shell/lib/waiting.sh index bdd5a7c71591..3a152892e077 100644 --- a/tools/perf/tests/shell/lib/waiting.sh +++ b/tools/perf/tests/shell/lib/waiting.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 tenths=date\ +%s%1N diff --git a/tools/perf/tests/shell/list.sh b/tools/perf/tests/shell/list.sh index 76a9846cff22..0c04b3159cef 100755 --- a/tools/perf/tests/shell/list.sh +++ b/tools/perf/tests/shell/list.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf list tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index 30d195d4c62f..d33d9e4392b0 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # kernel lock contention analysis test # SPDX-License-Identifier: GPL-2.0 @@ -44,7 +44,7 @@ check() { test_record() { echo "Testing perf lock record and perf lock contention" - perf lock record -o ${perfdata} -- perf bench sched messaging > /dev/null 2>&1 + perf lock record -o ${perfdata} -- perf bench sched messaging -p > /dev/null 2>&1 # the output goes to the stderr and we expect only 1 output (-E 1) perf lock contention -i ${perfdata} -E 1 -q 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then @@ -64,7 +64,7 @@ test_bpf() fi # the perf lock contention output goes to the stderr - perf lock con -a -b -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 @@ -75,7 +75,7 @@ test_bpf() test_record_concurrent() { echo "Testing perf lock record and perf lock contention at the same time" - perf lock record -o- -- perf bench sched messaging 2> /dev/null | \ + perf lock record -o- -- perf bench sched messaging -p 2> /dev/null | \ perf lock contention -i- -E 1 -q 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)" @@ -99,7 +99,7 @@ test_aggr_task() fi # the perf lock contention output goes to the stderr - perf lock con -a -b -t -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -t -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 @@ -122,7 +122,7 @@ test_aggr_addr() fi # the perf lock contention output goes to the stderr - perf lock con -a -b -l -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -l -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 @@ -140,7 +140,7 @@ test_aggr_cgroup() fi # the perf lock contention output goes to the stderr - perf lock con -a -b -g -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -g -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 @@ -162,7 +162,7 @@ test_type_filter() return fi - perf lock con -a -b -Y spinlock -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -Y spinlock -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(grep -c -v spinlock "${result}")" != "0" ]; then echo "[Fail] BPF result should not have non-spinlocks:" "$(cat "${result}")" err=1 @@ -194,7 +194,7 @@ test_lock_filter() return fi - perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then echo "[Fail] BPF result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")" err=1 @@ -222,7 +222,7 @@ test_stack_filter() return fi - perf lock con -a -b -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -S unix_stream -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result should have a lock from unix_stream:" "$(cat "${result}")" err=1 @@ -250,7 +250,7 @@ test_aggr_task_stack_filter() return fi - perf lock con -a -b -t -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -t -S unix_stream -E 1 -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result should have a task from unix_stream:" "$(cat "${result}")" err=1 @@ -266,7 +266,7 @@ test_cgroup_filter() return fi - perf lock con -a -b -g -E 1 -F wait_total -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -g -E 1 -F wait_total -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result should have a cgroup result:" "$(cat "${result}")" err=1 @@ -274,7 +274,7 @@ test_cgroup_filter() fi cgroup=$(cat "${result}" | awk '{ print $3 }') - perf lock con -a -b -g -E 1 -G "${cgroup}" -q -- perf bench sched messaging > /dev/null 2> ${result} + perf lock con -a -b -g -E 1 -G "${cgroup}" -q -- perf bench sched messaging -p > /dev/null 2> ${result} if [ "$(cat "${result}" | wc -l)" != "1" ]; then echo "[Fail] BPF result should have a result with cgroup filter:" "$(cat "${cgroup}")" err=1 @@ -309,7 +309,7 @@ test_csv_output() fi # the perf lock contention output goes to the stderr - perf lock con -a -b -E 1 -x , --output ${result} -- perf bench sched messaging > /dev/null 2>&1 + perf lock con -a -b -E 1 -x , --output ${result} -- perf bench sched messaging -p > /dev/null 2>&1 output=$(grep -v "^#" ${result} | tr -d -c , | wc -c) if [ "${header}" != "${output}" ]; then echo "[Fail] BPF result does not match the number of commas: ${header} != ${output}" diff --git a/tools/perf/tests/shell/perf-report-hierarchy.sh b/tools/perf/tests/shell/perf-report-hierarchy.sh index 02e3b6aee4ed..e3c6f9a24f33 100755 --- a/tools/perf/tests/shell/perf-report-hierarchy.sh +++ b/tools/perf/tests/shell/perf-report-hierarchy.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf report --hierarchy # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh index 0f52654c914a..5fe5682c28ce 100755 --- a/tools/perf/tests/shell/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/probe_vfs_getname.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Add vfs_getname probe to get syscall args filenames (exclusive) # SPDX-License-Identifier: GPL-2.0 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 c4bab5b5cc59..ab99bef556bf 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # probe libc's inet_pton & backtrace it with ping (exclusive) # Installs a probe on libc's inet_pton function, that will use uprobes, @@ -18,12 +18,13 @@ libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g') nm -Dg $libc 2>/dev/null | grep -F -q inet_pton || exit 254 -event_pattern='probe_libc:inet_pton(\_[[:digit:]]+)?' +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 | \ - grep -P -o "$event_pattern(?=[[:space:]]\(on inet_pton in $libc\))") + awk -v ep="$event_pattern" -v l="$libc" '$0 ~ ep && $0 ~ \ + ("\\(on inet_pton in " l "\\)") {print $1}') if [ $? -ne 0 ] || [ -z "$event_name" ] ; then printf "FAIL: could not add event\n" diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh index 1ad252f0d36e..002f7037f182 100755 --- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh +++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Use vfs_getname probe to get syscall args filenames (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system diff --git a/tools/perf/tests/shell/record+zstd_comp_decomp.sh b/tools/perf/tests/shell/record+zstd_comp_decomp.sh index 8929046e9057..f6b82223834e 100755 --- a/tools/perf/tests/shell/record+zstd_comp_decomp.sh +++ b/tools/perf/tests/shell/record+zstd_comp_decomp.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Zstd perf.data compression/decompression # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 587f62e34414..b1ad24fb3b33 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -12,8 +12,10 @@ shelldir=$(dirname "$0") . "${shelldir}"/lib/perf_has_symbol.sh testsym="test_loop" +testsym2="brstack" skip_test_missing_symbol ${testsym} +skip_test_missing_symbol ${testsym2} err=0 perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) @@ -231,6 +233,31 @@ test_cgroup() { echo "Cgroup sampling test [Success]" } +test_uid() { + echo "Uid sampling test" + if ! perf record -aB --synth=no --uid "$(id -u)" -o "${perfdata}" ${testprog} \ + > "${script_output}" 2>&1 + then + if grep -q "libbpf.*EPERM" "${script_output}" + then + echo "Uid sampling [Skipped permissions]" + return + else + echo "Uid sampling [Failed to record]" + err=1 + # cat "${script_output}" + return + fi + fi + if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" + then + echo "Uid sampling [Failed missing output]" + err=1 + return + fi + echo "Uid sampling test [Success]" +} + test_leader_sampling() { echo "Basic leader sampling test" if ! perf record -o "${perfdata}" -e "{cycles,cycles}:Su" -- \ @@ -334,6 +361,33 @@ test_precise_max() { fi } +test_callgraph() { + echo "Callgraph test" + + case $(uname -m) + in s390x) + cmd_flags="--call-graph dwarf -e cpu-clock";; + *) + cmd_flags="-g";; + esac + + if ! perf record -o "${perfdata}" $cmd_flags perf test -w brstack + then + echo "Callgraph test [Failed missing output]" + err=1 + return + fi + + if ! perf report -i "${perfdata}" 2>&1 | grep "${testsym2}" + then + echo "Callgraph test [Failed missing symbol]" + err=1 + return + fi + + echo "Callgraph test [Success]" +} + # raise the limit of file descriptors to minimum if [[ $default_fd_limit -lt $min_fd_limit ]]; then ulimit -Sn $min_fd_limit @@ -345,9 +399,11 @@ test_system_wide test_workload test_branch_counter test_cgroup +test_uid test_leader_sampling test_topdown_leader_sampling test_precise_max +test_callgraph # restore the default value ulimit -Sn $default_fd_limit diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh index 4d6c3c1b7fb9..383574cb3bd3 100755 --- a/tools/perf/tests/shell/record_bpf_filter.sh +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf record sample filtering (by BPF) tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/record_offcpu.sh b/tools/perf/tests/shell/record_offcpu.sh index 21a22efe08f5..860a2d6f4b75 100755 --- a/tools/perf/tests/shell/record_offcpu.sh +++ b/tools/perf/tests/shell/record_offcpu.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf record offcpu profiling tests (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/record_sideband.sh b/tools/perf/tests/shell/record_sideband.sh index ac70ac27d590..2182551873be 100755 --- a/tools/perf/tests/shell/record_sideband.sh +++ b/tools/perf/tests/shell/record_sideband.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf record sideband tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/sched.sh b/tools/perf/tests/shell/sched.sh new file mode 100755 index 000000000000..b9b81eaf856e --- /dev/null +++ b/tools/perf/tests/shell/sched.sh @@ -0,0 +1,116 @@ +#!/bin/bash +# perf sched tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +if [ "$(id -u)" != 0 ]; then + echo "[Skip] No root permission" + exit 2 +fi + +err=0 +perfdata=$(mktemp /tmp/__perf_test_sched.perf.data.XXXXX) +PID1=0 +PID2=0 + +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 + +start_noploops() { + # Start two noploop workloads on CPU0 to trigger scheduling. + perf test -w noploop 10 & + PID1=$! + taskset -pc 0 $PID1 + perf test -w noploop 10 & + PID2=$! + taskset -pc 0 $PID2 + + if ! grep -q 'Cpus_allowed_list:\s*0$' "/proc/$PID1/status" + then + echo "Sched [Error taskset did not work for the 1st noploop ($PID1)]" + grep Cpus_allowed /proc/$PID1/status + err=1 + fi + + if ! grep -q 'Cpus_allowed_list:\s*0$' "/proc/$PID2/status" + then + echo "Sched [Error taskset did not work for the 2nd noploop ($PID2)]" + grep Cpus_allowed /proc/$PID2/status + err=1 + fi +} + +cleanup_noploops() { + kill "$PID1" "$PID2" +} + +test_sched_record() { + echo "Sched record" + + start_noploops + + perf sched record --no-inherit -o "${perfdata}" sleep 1 + + cleanup_noploops +} + +test_sched_latency() { + echo "Sched latency" + + if ! perf sched latency -i "${perfdata}" | grep -q perf-noploop + then + echo "Sched latency [Failed missing output]" + err=1 + fi +} + +test_sched_script() { + echo "Sched script" + + if ! perf sched script -i "${perfdata}" | grep -q perf-noploop + then + echo "Sched script [Failed missing output]" + err=1 + fi +} + +test_sched_map() { + echo "Sched map" + + if ! perf sched map -i "${perfdata}" | grep -q perf-noploop + then + echo "Sched map [Failed missing output]" + err=1 + fi +} + +test_sched_timehist() { + echo "Sched timehist" + + if ! perf sched timehist -i "${perfdata}" | grep -q perf-noploop + then + echo "Sched timehist [Failed missing output]" + err=1 + fi +} + +test_sched_record +test_sched_latency +test_sched_script +test_sched_map +test_sched_timehist + +cleanup +exit $err diff --git a/tools/perf/tests/shell/script.sh b/tools/perf/tests/shell/script.sh index d3e2958d2242..7007f1cdf761 100755 --- a/tools/perf/tests/shell/script.sh +++ b/tools/perf/tests/shell/script.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf script tests # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/stat+csv_summary.sh b/tools/perf/tests/shell/stat+csv_summary.sh index 323123ff4d19..9a4353db3825 100755 --- a/tools/perf/tests/shell/stat+csv_summary.sh +++ b/tools/perf/tests/shell/stat+csv_summary.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf stat csv summary test # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/stat+event_uniquifying.sh b/tools/perf/tests/shell/stat+event_uniquifying.sh index 5ec35c52b7d9..bf54bd6c3e2e 100755 --- a/tools/perf/tests/shell/stat+event_uniquifying.sh +++ b/tools/perf/tests/shell/stat+event_uniquifying.sh @@ -9,7 +9,8 @@ perf_tool=perf err=0 test_event_uniquifying() { - # We use `clockticks` to verify the uniquify behavior. + # We use `clockticks` in `uncore_imc` to verify the uniquify behavior. + pmu="uncore_imc" event="clockticks" # If the `-A` option is added, the event should be uniquified. @@ -43,11 +44,18 @@ test_event_uniquifying() { echo "stat event uniquifying test" uniquified_event_array=() + # Skip if the machine does not have `uncore_imc` device. + if ! ${perf_tool} list pmu | grep -q ${pmu}; then + echo "Target does not support PMU ${pmu} [Skipped]" + err=2 + return + fi + # Check how many uniquified events. while IFS= read -r line; do uniquified_event=$(echo "$line" | awk '{print $1}') uniquified_event_array+=("${uniquified_event}") - done < <(${perf_tool} list -v ${event} | grep "\[Kernel PMU event\]") + done < <(${perf_tool} list -v ${event} | grep ${pmu}) perf_command="${perf_tool} stat -e $event -A -o ${stat_output} -- true" $perf_command diff --git a/tools/perf/tests/shell/stat+shadow_stat.sh b/tools/perf/tests/shell/stat+shadow_stat.sh index 0c7d79a230ea..8824f445d343 100755 --- a/tools/perf/tests/shell/stat+shadow_stat.sh +++ b/tools/perf/tests/shell/stat+shadow_stat.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf stat metrics (shadow stat) test # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/stat_all_pfm.sh b/tools/perf/tests/shell/stat_all_pfm.sh index 4d004f777a6e..c08c186af2c4 100755 --- a/tools/perf/tests/shell/stat_all_pfm.sh +++ b/tools/perf/tests/shell/stat_all_pfm.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf all libpfm4 events test # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh index 95d2ad5d17c6..f43e28a136d3 100755 --- a/tools/perf/tests/shell/stat_bpf_counters.sh +++ b/tools/perf/tests/shell/stat_bpf_counters.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf stat --bpf-counters test (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh index 2ec69060c42f..ff2e06c408bc 100755 --- a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh +++ b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf stat --bpf-counters --for-each-cgroup test # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/test_arm_callgraph_fp.sh b/tools/perf/tests/shell/test_arm_callgraph_fp.sh index 9caa36130175..9172dd68a81d 100755 --- a/tools/perf/tests/shell/test_arm_callgraph_fp.sh +++ b/tools/perf/tests/shell/test_arm_callgraph_fp.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check Arm64 callgraphs are complete in fp mode # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index 573af9235b72..1c750b67d141 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check Arm CoreSight trace data recording and synthesized samples (exclusive) # Uses the 'perf record' to record trace data with Arm CoreSight sinks; diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh index be2d26303f94..0dfb4fadf531 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check Arm CoreSight disassembly script completes without errors (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/test_arm_spe.sh b/tools/perf/tests/shell/test_arm_spe.sh index a69aab70dd8a..bb76ea88aa14 100755 --- a/tools/perf/tests/shell/test_arm_spe.sh +++ b/tools/perf/tests/shell/test_arm_spe.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check Arm SPE trace data recording and synthesized samples (exclusive) # Uses the 'perf record' to record trace data of Arm SPE events; diff --git a/tools/perf/tests/shell/test_arm_spe_fork.sh b/tools/perf/tests/shell/test_arm_spe_fork.sh index 8efeef9fb956..5bcca51c03ac 100755 --- a/tools/perf/tests/shell/test_arm_spe_fork.sh +++ b/tools/perf/tests/shell/test_arm_spe_fork.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check Arm SPE doesn't hang when there are forks # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/test_bpf_metadata.sh b/tools/perf/tests/shell/test_bpf_metadata.sh new file mode 100755 index 000000000000..be67d56e0f09 --- /dev/null +++ b/tools/perf/tests/shell/test_bpf_metadata.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# BPF metadata collection test +# +# SPDX-License-Identifier: GPL-2.0 + +set -e + +err=0 +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + +cleanup() { + rm -f "${perfdata}" + rm -f "${perfdata}".old + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +test_bpf_metadata() { + echo "Checking BPF metadata collection" + + if ! perf check -q feature libbpf-strings ; then + echo "Basic BPF metadata test [skipping - not supported]" + err=0 + return + fi + + # This is a basic invocation of perf record + # that invokes the perf_sample_filter BPF program. + if ! perf record -e task-clock --filter 'ip > 0' \ + -o "${perfdata}" sleep 1 2> /dev/null + then + echo "Basic BPF metadata test [Failed record]" + err=1 + return + fi + + # The BPF programs that ship with "perf" all have the following + # variable defined at compile time: + # + # const char bpf_metadata_perf_version[] SEC(".rodata") = <...>; + # + # This invocation looks for a PERF_RECORD_BPF_METADATA event, + # and checks that its content contains the string given by + # "perf version". + VERS=$(perf version | awk '{print $NF}') + if ! perf script --show-bpf-events -i "${perfdata}" | awk ' + /PERF_RECORD_BPF_METADATA.*perf_sample_filter/ { + header = 1; + } + /^ *entry/ { + if (header) { header = 0; entry = 1; } + } + $0 !~ /^ *entry/ { + entry = 0; + } + /perf_version/ { + if (entry) print $NF; + } + ' | grep -qF "$VERS" + then + echo "Basic BPF metadata test [Failed invalid output]" + err=1 + return + fi + echo "Basic BPF metadata test [Success]" +} + +test_bpf_metadata + +cleanup +exit $err diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/shell/test_intel_pt.sh index 32a9b8dcb200..8ee761f03c38 100755 --- a/tools/perf/tests/shell/test_intel_pt.sh +++ b/tools/perf/tests/shell/test_intel_pt.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Miscellaneous Intel PT testing (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh index 5d5019988d61..7a0b1145d0cd 100755 --- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh +++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Check open filename arg using perf trace + vfs_getname (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh index f0b49f7fb57d..572001d75d78 100755 --- a/tools/perf/tests/shell/trace_btf_enum.sh +++ b/tools/perf/tests/shell/trace_btf_enum.sh @@ -1,12 +1,11 @@ -#!/bin/sh +#!/bin/bash # perf trace enum augmentation tests # SPDX-License-Identifier: GPL-2.0 err=0 -set -e syscall="landlock_add_rule" -non_syscall="timer:hrtimer_setup,timer:hrtimer_start" +non_syscall="timer:hrtimer_start" TESTPROG="perf test -w landlock" @@ -17,7 +16,7 @@ skip_if_no_perf_trace || exit 2 check_vmlinux() { echo "Checking if vmlinux exists" - if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1 + if [ ! -f /sys/kernel/btf/vmlinux ] then echo "trace+enum test [Skipped missing vmlinux BTF support]" err=2 @@ -34,22 +33,24 @@ trace_landlock() { return fi - if perf trace -e $syscall $TESTPROG 2>&1 | \ - grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -1.*" + output="$(perf trace -e $syscall $TESTPROG 2>&1)" + if echo "$output" | grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -1.*" then err=0 else + printf "[syscall failure] Failed to trace syscall $syscall, output:\n$output\n" err=1 fi } trace_non_syscall() { - echo "Tracing non-syscall tracepoint ${non-syscall}" - if perf trace -e $non_syscall --max-events=1 2>&1 | \ - grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$' + echo "Tracing non-syscall tracepoint ${non_syscall}" + output="$(perf trace -e $non_syscall --max-events=1 2>&1)" + if echo "$output" | grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$' then err=0 else + printf "[tracepoint failure] Failed to trace tracepoint $non_syscall, output:\n$output\n" err=1 fi } diff --git a/tools/perf/tests/shell/trace_btf_general.sh b/tools/perf/tests/shell/trace_btf_general.sh index a25d8744695e..ef2da806be6b 100755 --- a/tools/perf/tests/shell/trace_btf_general.sh +++ b/tools/perf/tests/shell/trace_btf_general.sh @@ -3,7 +3,6 @@ # SPDX-License-Identifier: GPL-2.0 err=0 -set -e # shellcheck source=lib/probe.sh . "$(dirname $0)"/lib/probe.sh @@ -28,10 +27,10 @@ check_vmlinux() { trace_test_string() { echo "Testing perf trace's string augmentation" - if ! perf trace -e renameat* --max-events=1 -- mv ${file1} ${file2} 2>&1 | \ - grep -q -E "^mv/[0-9]+ renameat(2)?\(.*, \"${file1}\", .*, \"${file2}\", .*\) += +[0-9]+$" + output="$(perf trace --sort-events -e renameat* --max-events=1 -- mv ${file1} ${file2} 2>&1)" + if ! echo "$output" | grep -q -E "^mv/[0-9]+ renameat(2)?\(.*, \"${file1}\", .*, \"${file2}\", .*\) += +[0-9]+$" then - echo "String augmentation test failed" + printf "String augmentation test failed, output:\n$output\n" err=1 fi } @@ -39,20 +38,20 @@ trace_test_string() { trace_test_buffer() { echo "Testing perf trace's buffer augmentation" # echo will insert a newline (\10) at the end of the buffer - if ! perf trace -e write --max-events=1 -- echo "${buffer}" 2>&1 | \ - grep -q -E "^echo/[0-9]+ write\([0-9]+, ${buffer}.*, [0-9]+\) += +[0-9]+$" + output="$(perf trace --sort-events -e write --max-events=1 -- echo "${buffer}" 2>&1)" + if ! echo "$output" | grep -q -E "^echo/[0-9]+ write\([0-9]+, ${buffer}.*, [0-9]+\) += +[0-9]+$" then - echo "Buffer augmentation test failed" + printf "Buffer augmentation test failed, output:\n$output\n" err=1 fi } trace_test_struct_btf() { echo "Testing perf trace's struct augmentation" - if ! perf trace -e clock_nanosleep --force-btf --max-events=1 -- sleep 1 2>&1 | \ - grep -q -E "^sleep/[0-9]+ clock_nanosleep\(0, 0, \{1,\}, 0x[0-9a-f]+\) += +[0-9]+$" + output="$(perf trace --sort-events -e clock_nanosleep --force-btf --max-events=1 -- sleep 1 2>&1)" + if ! echo "$output" | grep -q -E "^sleep/[0-9]+ clock_nanosleep\(0, 0, \{1,.*\}, 0x[0-9a-f]+\) += +[0-9]+$" then - echo "BTF struct augmentation test failed" + printf "BTF struct augmentation test failed, output:\n$output\n" err=1 fi } diff --git a/tools/perf/tests/shell/trace_exit_race.sh b/tools/perf/tests/shell/trace_exit_race.sh index 1e247693e756..db300cde94fb 100755 --- a/tools/perf/tests/shell/trace_exit_race.sh +++ b/tools/perf/tests/shell/trace_exit_race.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf trace exit race # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/trace_record_replay.sh b/tools/perf/tests/shell/trace_record_replay.sh index 6b4ed863c1ef..88d30a03dcec 100755 --- a/tools/perf/tests/shell/trace_record_replay.sh +++ b/tools/perf/tests/shell/trace_record_replay.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf trace record and replay # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/shell/trace_summary.sh b/tools/perf/tests/shell/trace_summary.sh index f9bb7f9388be..22e2651d5919 100755 --- a/tools/perf/tests/shell/trace_summary.sh +++ b/tools/perf/tests/shell/trace_summary.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf trace summary (exclusive) # SPDX-License-Identifier: GPL-2.0 diff --git a/tools/perf/tests/subcmd-help.c b/tools/perf/tests/subcmd-help.c new file mode 100644 index 000000000000..2280b4c0e5e7 --- /dev/null +++ b/tools/perf/tests/subcmd-help.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "tests.h" +#include +#include + +static int test__load_cmdnames(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + struct cmdnames cmds = {}; + + add_cmdname(&cmds, "aaa", 3); + add_cmdname(&cmds, "foo", 3); + add_cmdname(&cmds, "xyz", 3); + + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds, "aaa") == 1); + TEST_ASSERT_VAL("wrong cmd", is_in_cmdlist(&cmds, "bar") == 0); + TEST_ASSERT_VAL("case sensitive", is_in_cmdlist(&cmds, "XYZ") == 0); + + clean_cmdnames(&cmds); + return TEST_OK; +} + +static int test__uniq_cmdnames(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + struct cmdnames cmds = {}; + + /* uniq() assumes it's sorted */ + add_cmdname(&cmds, "aaa", 3); + add_cmdname(&cmds, "aaa", 3); + add_cmdname(&cmds, "bbb", 3); + + TEST_ASSERT_VAL("invalid original size", cmds.cnt == 3); + /* uniquify command names (to remove second 'aaa') */ + uniq(&cmds); + TEST_ASSERT_VAL("invalid final size", cmds.cnt == 2); + + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds, "aaa") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds, "bbb") == 1); + TEST_ASSERT_VAL("wrong cmd", is_in_cmdlist(&cmds, "ccc") == 0); + + clean_cmdnames(&cmds); + return TEST_OK; +} + +static int test__exclude_cmdnames(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + struct cmdnames cmds1 = {}; + struct cmdnames cmds2 = {}; + + add_cmdname(&cmds1, "aaa", 3); + add_cmdname(&cmds1, "bbb", 3); + add_cmdname(&cmds1, "ccc", 3); + add_cmdname(&cmds1, "ddd", 3); + add_cmdname(&cmds1, "eee", 3); + add_cmdname(&cmds1, "fff", 3); + add_cmdname(&cmds1, "ggg", 3); + add_cmdname(&cmds1, "hhh", 3); + add_cmdname(&cmds1, "iii", 3); + add_cmdname(&cmds1, "jjj", 3); + + add_cmdname(&cmds2, "bbb", 3); + add_cmdname(&cmds2, "eee", 3); + add_cmdname(&cmds2, "jjj", 3); + + TEST_ASSERT_VAL("invalid original size", cmds1.cnt == 10); + TEST_ASSERT_VAL("invalid original size", cmds2.cnt == 3); + + /* remove duplicate command names in cmds1 */ + exclude_cmds(&cmds1, &cmds2); + + TEST_ASSERT_VAL("invalid excluded size", cmds1.cnt == 7); + TEST_ASSERT_VAL("invalid excluded size", cmds2.cnt == 3); + + /* excluded commands should not belong to cmds1 */ + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "aaa") == 1); + TEST_ASSERT_VAL("wrong cmd", is_in_cmdlist(&cmds1, "bbb") == 0); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "ccc") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "ddd") == 1); + TEST_ASSERT_VAL("wrong cmd", is_in_cmdlist(&cmds1, "eee") == 0); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "fff") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "ggg") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "hhh") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds1, "iii") == 1); + TEST_ASSERT_VAL("wrong cmd", is_in_cmdlist(&cmds1, "jjj") == 0); + + /* they should be only in cmds2 */ + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds2, "bbb") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds2, "eee") == 1); + TEST_ASSERT_VAL("cannot find cmd", is_in_cmdlist(&cmds2, "jjj") == 1); + + clean_cmdnames(&cmds1); + clean_cmdnames(&cmds2); + return TEST_OK; +} + +static struct test_case tests__subcmd_help[] = { + TEST_CASE("Load subcmd names", load_cmdnames), + TEST_CASE("Uniquify subcmd names", uniq_cmdnames), + TEST_CASE("Exclude duplicate subcmd names", exclude_cmdnames), + { .name = NULL, } +}; + +struct test_suite suite__subcmd_help = { + .desc = "libsubcmd help tests", + .test_cases = tests__subcmd_help, +}; diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 6b3aac283c37..5be294014d3b 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -351,7 +351,7 @@ static int test__switch_tracking(struct test_suite *test __maybe_unused, int sub const char *comm; int err = -1; - threads = thread_map__new(-1, getpid(), UINT_MAX); + threads = thread_map__new_by_tid(getpid()); if (!threads) { pr_debug("thread_map__new failed!\n"); goto out_err; diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c index ee20a366f32f..f4ffe5804f40 100644 --- a/tools/perf/tests/symbols.c +++ b/tools/perf/tests/symbols.c @@ -5,6 +5,7 @@ #include #include "debug.h" #include "dso.h" +#include "env.h" #include "machine.h" #include "thread.h" #include "symbol.h" @@ -13,15 +14,18 @@ #include "tests.h" struct test_info { + struct perf_env host_env; struct machine *machine; struct thread *thread; }; static int init_test_info(struct test_info *ti) { - ti->machine = machine__new_host(); + perf_env__init(&ti->host_env); + ti->machine = machine__new_host(&ti->host_env); if (!ti->machine) { pr_debug("machine__new_host() failed!\n"); + perf_env__exit(&ti->host_env); return TEST_FAIL; } @@ -29,6 +33,7 @@ static int init_test_info(struct test_info *ti) ti->thread = machine__findnew_thread(ti->machine, 100, 100); if (!ti->thread) { pr_debug("machine__findnew_thread() failed!\n"); + perf_env__exit(&ti->host_env); return TEST_FAIL; } @@ -39,6 +44,7 @@ static void exit_test_info(struct test_info *ti) { thread__put(ti->thread); machine__delete(ti->machine); + perf_env__exit(&ti->host_env); } struct dso_map { @@ -96,8 +102,8 @@ static int create_map(struct test_info *ti, char *filename, struct map **map_p) dso__put(dso); /* Create a dummy map at 0x100000 */ - *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL, - PROT_EXEC, 0, NULL, filename, ti->thread); + *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, &dso_id_empty, + PROT_EXEC, /*flags=*/0, filename, ti->thread); if (!*map_p) { pr_debug("Failed to create map!"); return TEST_FAIL; diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 8e328bbd509d..4053ff2813bb 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -46,7 +46,6 @@ static int test__task_exit(struct test_suite *test __maybe_unused, int subtest _ struct evsel *evsel; struct evlist *evlist; struct target target = { - .uid = UINT_MAX, .uses_mmap = true, }; const char *argv[] = { "true", NULL }; diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c index 1d5759d08141..f18c4cd337c8 100644 --- a/tools/perf/tests/tests-scripts.c +++ b/tools/perf/tests/tests-scripts.c @@ -85,7 +85,7 @@ static char *shell_test__description(int dir_fd, const char *name) if (io.fd < 0) return NULL; - /* Skip first line - should be #!/bin/sh Shebang */ + /* Skip first line - should be #!/bin/bash Shebang */ if (io__get_char(&io) != '#') goto err_out; if (io__get_char(&io) != '!') @@ -260,6 +260,7 @@ static void append_scripts_in_dir(int dir_fd, continue; /* Skip scripts that have a separate driver. */ fd = openat(dir_fd, ent->d_name, O_PATH); append_scripts_in_dir(fd, result, result_sz); + close(fd); } for (i = 0; i < n_dirs; i++) /* Clean up */ zfree(&entlist[i]); diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index bb7951c61971..97e62db8764a 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -3,6 +3,7 @@ #define TESTS_H #include +#include "util/debug.h" enum { TEST_OK = 0, @@ -71,6 +72,15 @@ struct test_suite { .exclusive = true, \ } +#define TEST_CASE_REASON_EXCLUSIVE(description, _name, _reason) \ + { \ + .name = #_name, \ + .desc = description, \ + .run_case = test__##_name, \ + .skip_reason = _reason, \ + .exclusive = true, \ + } + #define DEFINE_SUITE(description, _name) \ struct test_case tests__##_name[] = { \ TEST_CASE(description, _name), \ @@ -168,6 +178,7 @@ DECLARE_SUITE(sigtrap); DECLARE_SUITE(event_groups); DECLARE_SUITE(symbols); DECLARE_SUITE(util); +DECLARE_SUITE(subcmd_help); /* * PowerPC and S390 do not support creation of instruction breakpoints using the diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 1fe521466bf4..54209592168d 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -115,7 +115,7 @@ static int test__thread_map_remove(struct test_suite *test __maybe_unused, int s TEST_ASSERT_VAL("failed to allocate map string", asprintf(&str, "%d,%d", getpid(), getppid()) >= 0); - threads = thread_map__new_str(str, NULL, 0, false); + threads = thread_map__new_str(str, /*tid=*/NULL, /*all_threads=*/false); free(str); TEST_ASSERT_VAL("failed to allocate thread_map", diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index a8cb5ba898ab..ec01150d208d 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -43,6 +43,7 @@ static int session_write_header(char *path) session->evlist = evlist__new_default(); TEST_ASSERT_VAL("can't get evlist", session->evlist); + session->evlist->session = session; perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); perf_header__set_feat(&session->header, HEADER_NRCPUS); @@ -69,9 +70,11 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) int i; struct aggr_cpu_id id; struct perf_cpu cpu; + struct perf_env *env; session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); + env = perf_session__env(session); cpu__setup_cpunode_map(); /* On platforms with large numbers of CPUs process_cpu_topology() @@ -95,9 +98,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) * condition is true (see do_core_id_test in header.c). So always * run this test on those platforms. */ - if (!session->header.env.cpu - && strncmp(session->header.env.arch, "s390", 4) - && strncmp(session->header.env.arch, "aarch64", 7)) + if (!env->cpu && strncmp(env->arch, "s390", 4) && strncmp(env->arch, "aarch64", 7)) return TEST_SKIP; /* @@ -106,20 +107,20 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) * physical_package_id will be set to -1. Hence skip this * test if physical_package_id returns -1 for cpu from perf_cpu_map. */ - if (!strncmp(session->header.env.arch, "ppc64le", 7)) { + if (!strncmp(env->arch, "ppc64le", 7)) { if (cpu__get_socket_id(perf_cpu_map__cpu(map, 0)) == -1) return TEST_SKIP; } - TEST_ASSERT_VAL("Session header CPU map not set", session->header.env.cpu); + TEST_ASSERT_VAL("Session header CPU map not set", env->cpu); - for (i = 0; i < session->header.env.nr_cpus_avail; i++) { + for (i = 0; i < env->nr_cpus_avail; i++) { cpu.cpu = i; if (!perf_cpu_map__has(map, cpu)) continue; pr_debug("CPU %d, core %d, socket %d\n", i, - session->header.env.cpu[i].core_id, - session->header.env.cpu[i].socket_id); + env->cpu[i].core_id, + env->cpu[i].socket_id); } // Test that CPU ID contains socket, die, core and CPU @@ -129,13 +130,12 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) cpu.cpu == id.cpu.cpu); TEST_ASSERT_VAL("Cpu map - Core ID doesn't match", - session->header.env.cpu[cpu.cpu].core_id == id.core); + env->cpu[cpu.cpu].core_id == id.core); TEST_ASSERT_VAL("Cpu map - Socket ID doesn't match", - session->header.env.cpu[cpu.cpu].socket_id == - id.socket); + env->cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Cpu map - Die ID doesn't match", - session->header.env.cpu[cpu.cpu].die_id == id.die); + env->cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Cpu map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Cpu map - Thread IDX is set", id.thread_idx == -1); } @@ -144,14 +144,13 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) perf_cpu_map__for_each_cpu(cpu, i, map) { id = aggr_cpu_id__core(cpu, NULL); TEST_ASSERT_VAL("Core map - Core ID doesn't match", - session->header.env.cpu[cpu.cpu].core_id == id.core); + env->cpu[cpu.cpu].core_id == id.core); TEST_ASSERT_VAL("Core map - Socket ID doesn't match", - session->header.env.cpu[cpu.cpu].socket_id == - id.socket); + env->cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Core map - Die ID doesn't match", - session->header.env.cpu[cpu.cpu].die_id == id.die); + env->cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Core map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Core map - Thread IDX is set", id.thread_idx == -1); } @@ -160,11 +159,10 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) perf_cpu_map__for_each_cpu(cpu, i, map) { id = aggr_cpu_id__die(cpu, NULL); TEST_ASSERT_VAL("Die map - Socket ID doesn't match", - session->header.env.cpu[cpu.cpu].socket_id == - id.socket); + env->cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Die map - Die ID doesn't match", - session->header.env.cpu[cpu.cpu].die_id == id.die); + env->cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Die map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Die map - Core is set", id.core == -1); @@ -176,8 +174,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) perf_cpu_map__for_each_cpu(cpu, i, map) { id = aggr_cpu_id__socket(cpu, NULL); TEST_ASSERT_VAL("Socket map - Socket ID doesn't match", - session->header.env.cpu[cpu.cpu].socket_id == - id.socket); + env->cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Socket map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Socket map - Die ID is set", id.die == -1); diff --git a/tools/perf/tests/util.c b/tools/perf/tests/util.c index 6366db5cbf8c..b273d287e164 100644 --- a/tools/perf/tests/util.c +++ b/tools/perf/tests/util.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "tests.h" #include "util/debug.h" +#include "util/sha1.h" #include #include @@ -16,6 +17,48 @@ static int test_strreplace(char needle, const char *haystack, return ret == 0; } +#define MAX_LEN 512 + +/* Test sha1() for all lengths from 0 to MAX_LEN inclusively. */ +static int test_sha1(void) +{ + u8 data[MAX_LEN]; + size_t digests_size = (MAX_LEN + 1) * SHA1_DIGEST_SIZE; + u8 *digests; + u8 digest_of_digests[SHA1_DIGEST_SIZE]; + /* + * The correctness of this value was verified by running this test with + * sha1() replaced by OpenSSL's SHA1(). + */ + static const u8 expected_digest_of_digests[SHA1_DIGEST_SIZE] = { + 0x74, 0xcd, 0x4c, 0xb9, 0xd8, 0xa6, 0xd5, 0x95, 0x22, 0x8b, + 0x7e, 0xd6, 0x8b, 0x7e, 0x46, 0x95, 0x31, 0x9b, 0xa2, 0x43, + }; + size_t i; + + digests = malloc(digests_size); + TEST_ASSERT_VAL("failed to allocate digests", digests != NULL); + + /* Generate MAX_LEN bytes of data. */ + for (i = 0; i < MAX_LEN; i++) + data[i] = i; + + /* Calculate a SHA-1 for each length 0 through MAX_LEN inclusively. */ + for (i = 0; i <= MAX_LEN; i++) + sha1(data, i, &digests[i * SHA1_DIGEST_SIZE]); + + /* Calculate digest of all digests calculated above. */ + sha1(digests, digests_size, digest_of_digests); + + free(digests); + + /* Check for the expected result. */ + TEST_ASSERT_VAL("wrong output from sha1()", + memcmp(digest_of_digests, expected_digest_of_digests, + SHA1_DIGEST_SIZE) == 0); + return 0; +} + static int test__util(struct test_suite *t __maybe_unused, int subtest __maybe_unused) { TEST_ASSERT_VAL("empty string", test_strreplace(' ', "", "123", "")); @@ -25,7 +68,7 @@ static int test__util(struct test_suite *t __maybe_unused, int subtest __maybe_u TEST_ASSERT_VAL("replace long", test_strreplace('a', "abcabc", "longlong", "longlongbclonglongbc")); - return 0; + return test_sha1(); } DEFINE_SUITE("util", util); diff --git a/tools/perf/tests/workloads/noploop.c b/tools/perf/tests/workloads/noploop.c index 940ea5910a84..656e472e6188 100644 --- a/tools/perf/tests/workloads/noploop.c +++ b/tools/perf/tests/workloads/noploop.c @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include #include #include @@ -16,6 +17,7 @@ static int noploop(int argc, const char **argv) { int sec = 1; + pthread_setname_np(pthread_self(), "perf-noploop"); if (argc > 0) sec = atoi(argv[0]); diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index f50ebdc445b8..561590ee8cda 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build @@ -31,6 +31,6 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -s bash -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h index c3322eb3d686..3b262487ec06 100644 --- a/tools/perf/trace/beauty/include/linux/socket.h +++ b/tools/perf/trace/beauty/include/linux/socket.h @@ -168,7 +168,7 @@ static inline struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr return __cmsg_nxthdr(__msg->msg_control, __msg->msg_controllen, __cmsg); } -static inline size_t msg_data_left(struct msghdr *msg) +static inline size_t msg_data_left(const struct msghdr *msg) { return iov_iter_count(&msg->msg_iter); } diff --git a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h index a15ac2fa4b20..f291ab4f94eb 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h @@ -90,10 +90,28 @@ #define DN_ATTRIB 0x00000020 /* File changed attibutes */ #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ +/* Reserved kernel ranges [-100], [-10000, -40000]. */ #define AT_FDCWD -100 /* Special value for dirfd used to indicate openat should use the current working directory. */ +/* + * The concept of process and threads in userland and the kernel is a confusing + * one - within the kernel every thread is a 'task' with its own individual PID, + * however from userland's point of view threads are grouped by a single PID, + * which is that of the 'thread group leader', typically the first thread + * spawned. + * + * To cut the Gideon knot, for internal kernel usage, we refer to + * PIDFD_SELF_THREAD to refer to the current thread (or task from a kernel + * perspective), and PIDFD_SELF_THREAD_GROUP to refer to the current thread + * group leader... + */ +#define PIDFD_SELF_THREAD -10000 /* Current thread. */ +#define PIDFD_SELF_THREAD_GROUP -10001 /* Current thread group leader. */ + +#define FD_PIDFS_ROOT -10002 /* Root of the pidfs filesystem */ +#define FD_INVALID -10009 /* Invalid file descriptor: -10000 - EBADF = -10009 */ /* Generic flags for the *at(2) family of syscalls. */ diff --git a/tools/perf/trace/beauty/include/uapi/linux/fs.h b/tools/perf/trace/beauty/include/uapi/linux/fs.h index e762e1af650c..0bd678a4a10e 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/fs.h +++ b/tools/perf/trace/beauty/include/uapi/linux/fs.h @@ -60,6 +60,17 @@ #define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */ #define RENAME_WHITEOUT (1 << 2) /* Whiteout source */ +/* + * The root inode of procfs is guaranteed to always have the same inode number. + * For programs that make heavy use of procfs, verifying that the root is a + * real procfs root and using openat2(RESOLVE_{NO_{XDEV,MAGICLINKS},BENEATH}) + * will allow you to make sure you are never tricked into operating on the + * wrong procfs file. + */ +enum procfs_ino { + PROCFS_ROOT_INO = 1, +}; + struct file_clone_range { __s64 src_fd; __u64 src_offset; @@ -91,6 +102,63 @@ struct fs_sysfs_path { __u8 name[128]; }; +/* Protection info capability flags */ +#define LBMD_PI_CAP_INTEGRITY (1 << 0) +#define LBMD_PI_CAP_REFTAG (1 << 1) + +/* Checksum types for Protection Information */ +#define LBMD_PI_CSUM_NONE 0 +#define LBMD_PI_CSUM_IP 1 +#define LBMD_PI_CSUM_CRC16_T10DIF 2 +#define LBMD_PI_CSUM_CRC64_NVME 4 + +/* sizeof first published struct */ +#define LBMD_SIZE_VER0 16 + +/* + * Logical block metadata capability descriptor + * If the device does not support metadata, all the fields will be zero. + * Applications must check lbmd_flags to determine whether metadata is + * supported or not. + */ +struct logical_block_metadata_cap { + /* Bitmask of logical block metadata capability flags */ + __u32 lbmd_flags; + /* + * The amount of data described by each unit of logical block + * metadata + */ + __u16 lbmd_interval; + /* + * Size in bytes of the logical block metadata associated with each + * interval + */ + __u8 lbmd_size; + /* + * Size in bytes of the opaque block tag associated with each + * interval + */ + __u8 lbmd_opaque_size; + /* + * Offset in bytes of the opaque block tag within the logical block + * metadata + */ + __u8 lbmd_opaque_offset; + /* Size in bytes of the T10 PI tuple associated with each interval */ + __u8 lbmd_pi_size; + /* Offset in bytes of T10 PI tuple within the logical block metadata */ + __u8 lbmd_pi_offset; + /* T10 PI guard tag type */ + __u8 lbmd_guard_tag_type; + /* Size in bytes of the T10 PI application tag */ + __u8 lbmd_app_tag_size; + /* Size in bytes of the T10 PI reference tag */ + __u8 lbmd_ref_tag_size; + /* Size in bytes of the T10 PI storage tag */ + __u8 lbmd_storage_tag_size; + __u8 pad; +}; + /* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */ #define FILE_DEDUPE_RANGE_SAME 0 #define FILE_DEDUPE_RANGE_DIFFERS 1 @@ -148,6 +216,24 @@ struct fsxattr { unsigned char fsx_pad[8]; }; +/* + * Variable size structure for file_[sg]et_attr(). + * + * Note. This is alternative to the structure 'struct file_kattr'/'struct fsxattr'. + * As this structure is passed to/from userspace with its size, this can + * be versioned based on the size. + */ +struct file_attr { + __u64 fa_xflags; /* xflags field value (get/set) */ + __u32 fa_extsize; /* extsize field value (get/set)*/ + __u32 fa_nextents; /* nextents field value (get) */ + __u32 fa_projid; /* project identifier (get/set) */ + __u32 fa_cowextsize; /* CoW extsize field value (get/set) */ +}; + +#define FILE_ATTR_SIZE_VER0 24 +#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0 + /* * Flags for the fsx_xflags field */ @@ -247,6 +333,8 @@ struct fsxattr { * also /sys/kernel/debug/ for filesystems with debugfs exports */ #define FS_IOC_GETFSSYSFSPATH _IOR(0x15, 1, struct fs_sysfs_path) +/* Get logical block metadata capability details */ +#define FS_IOC_GETLBMD_CAP _IOWR(0x15, 2, struct logical_block_metadata_cap) /* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) @@ -361,6 +449,7 @@ typedef int __bitwise __kernel_rwf_t; #define PAGE_IS_PFNZERO (1 << 5) #define PAGE_IS_HUGE (1 << 6) #define PAGE_IS_SOFT_DIRTY (1 << 7) +#define PAGE_IS_GUARD (1 << 8) /* * struct page_region - Page region with flags diff --git a/tools/perf/trace/beauty/include/uapi/linux/prctl.h b/tools/perf/trace/beauty/include/uapi/linux/prctl.h index 15c18ef4eb11..ed3aed264aeb 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/prctl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/prctl.h @@ -244,6 +244,8 @@ struct prctl_mm_map { # define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) /* Unused; kept only for source compatibility */ # define PR_MTE_TCF_SHIFT 1 +/* MTE tag check store only */ +# define PR_MTE_STORE_ONLY (1UL << 19) /* RISC-V pointer masking tag length */ # define PR_PMLEN_SHIFT 24 # define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT) @@ -255,7 +257,12 @@ struct prctl_mm_map { /* Dispatch syscalls to a userspace handler */ #define PR_SET_SYSCALL_USER_DISPATCH 59 # define PR_SYS_DISPATCH_OFF 0 -# define PR_SYS_DISPATCH_ON 1 +/* Enable dispatch except for the specified range */ +# define PR_SYS_DISPATCH_EXCLUSIVE_ON 1 +/* Enable dispatch for the specified range */ +# define PR_SYS_DISPATCH_INCLUSIVE_ON 2 +/* Legacy name for backwards compatibility */ +# define PR_SYS_DISPATCH_ON PR_SYS_DISPATCH_EXCLUSIVE_ON /* The control values for the user space selector when dispatch is enabled */ # define SYSCALL_DISPATCH_FILTER_ALLOW 0 # define SYSCALL_DISPATCH_FILTER_BLOCK 1 @@ -364,4 +371,9 @@ struct prctl_mm_map { # define PR_TIMER_CREATE_RESTORE_IDS_ON 1 # define PR_TIMER_CREATE_RESTORE_IDS_GET 2 +/* FUTEX hash management */ +#define PR_FUTEX_HASH 78 +# define PR_FUTEX_HASH_SET_SLOTS 1 +# define PR_FUTEX_HASH_GET_SLOTS 2 + #endif /* _LINUX_PRCTL_H */ diff --git a/tools/perf/trace/beauty/include/uapi/linux/stat.h b/tools/perf/trace/beauty/include/uapi/linux/stat.h index f78ee3670dd5..1686861aae20 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/stat.h +++ b/tools/perf/trace/beauty/include/uapi/linux/stat.h @@ -182,8 +182,12 @@ struct statx { /* File offset alignment for direct I/O reads */ __u32 stx_dio_read_offset_align; - /* 0xb8 */ - __u64 __spare3[9]; /* Spare space for future expansion */ + /* Optimised max atomic write unit in bytes */ + __u32 stx_atomic_write_unit_max_opt; + __u32 __spare2[1]; + + /* 0xc0 */ + __u64 __spare3[8]; /* Spare space for future expansion */ /* 0x100 */ }; diff --git a/tools/perf/trace/beauty/include/uapi/linux/vhost.h b/tools/perf/trace/beauty/include/uapi/linux/vhost.h index d4b3e2ae1314..c57674a6aa0d 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/vhost.h +++ b/tools/perf/trace/beauty/include/uapi/linux/vhost.h @@ -235,4 +235,39 @@ */ #define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ struct vhost_vring_state) + +/* Extended features manipulation */ +#define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) + +/* fork_owner values for vhost */ +#define VHOST_FORK_OWNER_KTHREAD 0 +#define VHOST_FORK_OWNER_TASK 1 + +/** + * VHOST_SET_FORK_FROM_OWNER - Set the fork_owner flag for the vhost device, + * This ioctl must called before VHOST_SET_OWNER. + * Only available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y + * + * @param fork_owner: An 8-bit value that determines the vhost thread mode + * + * When fork_owner is set to VHOST_FORK_OWNER_TASK(default value): + * - Vhost will create vhost worker as tasks forked from the owner, + * inheriting all of the owner's attributes. + * + * When fork_owner is set to VHOST_FORK_OWNER_KTHREAD: + * - Vhost will create vhost workers as kernel threads. + */ +#define VHOST_SET_FORK_FROM_OWNER _IOW(VHOST_VIRTIO, 0x84, __u8) + +/** + * VHOST_GET_FORK_OWNER - Get the current fork_owner flag for the vhost device. + * Only available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y + * + * @return: An 8-bit value indicating the current thread mode. + */ +#define VHOST_GET_FORK_FROM_OWNER _IOR(VHOST_VIRTIO, 0x85, __u8) + #endif diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index f59ad4f14d33..9d4404f9b87f 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -71,8 +71,8 @@ int ui_browser__help_window(struct ui_browser *browser, const char *text); bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); int ui_browser__input_window(const char *title, const char *text, char *input, const char *exit_msg, int delay_sec); -struct perf_env; -int tui__header_window(struct perf_env *env); +struct perf_session; +int tui__header_window(struct perf_session *session); void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); unsigned int ui_browser__argv_refresh(struct ui_browser *browser); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index ab776b1ed2d5..183902dac042 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -345,6 +345,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, browser->curr_hot = rb_last(&browser->entries); } +static struct annotation_line *annotate_browser__find_new_asm_line( + struct annotate_browser *browser, + int idx_asm) +{ + struct annotation_line *al; + struct list_head *head = browser->b.entries; + + /* find an annotation line in the new list with the same idx_asm */ + list_for_each_entry(al, head, node) { + if (al->idx_asm == idx_asm) + return al; + } + + /* There are no asm lines */ + return NULL; +} + static struct annotation_line *annotate_browser__find_next_asm_line( struct annotate_browser *browser, struct annotation_line *al) @@ -368,7 +385,31 @@ static struct annotation_line *annotate_browser__find_next_asm_line( return NULL; } -static bool annotate_browser__toggle_source(struct annotate_browser *browser) +static bool annotation__has_source(struct annotation *notes) +{ + struct annotation_line *al; + bool found_asm = false; + + /* Let's skip the first non-asm lines which present regardless of source. */ + list_for_each_entry(al, ¬es->src->source, node) { + if (al->offset >= 0) { + found_asm = true; + break; + } + } + + if (found_asm) { + /* After assembly lines, any line without offset means source. */ + list_for_each_entry_continue(al, ¬es->src->source, node) { + if (al->offset == -1) + return true; + } + } + return false; +} + +static bool annotate_browser__toggle_source(struct annotate_browser *browser, + struct evsel *evsel) { struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al; @@ -377,6 +418,39 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser) browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); + if (!annotate_opts.annotate_src) + annotate_opts.annotate_src = true; + + /* + * It's about to get source code annotation for the first time. + * Drop the existing annotation_lines and get the new one with source. + * And then move to the original line at the same asm index. + */ + if (annotate_opts.hide_src_code && !notes->src->tried_source) { + struct map_symbol *ms = browser->b.priv; + int orig_idx_asm = al->idx_asm; + + /* annotate again with source code info */ + annotate_opts.hide_src_code = false; + annotated_source__purge(notes->src); + symbol__annotate2(ms, evsel, &browser->arch); + annotate_opts.hide_src_code = true; + + /* should be after annotated_source__purge() */ + notes->src->tried_source = true; + + if (!annotation__has_source(notes)) + ui__warning("Annotation has no source code."); + + browser->b.entries = ¬es->src->source; + al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); + if (unlikely(al == NULL)) { + al = list_first_entry(¬es->src->source, + struct annotation_line, node); + } + browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); + } + if (annotate_opts.hide_src_code) { if (al->idx_asm < offset) offset = al->idx; @@ -833,7 +907,7 @@ static int annotate_browser__run(struct annotate_browser *browser, nd = browser->curr_hot; break; case 's': - if (annotate_browser__toggle_source(browser)) + if (annotate_browser__toggle_source(browser, evsel)) ui_helpline__puts(help); annotate__scnprintf_title(hists, title, sizeof(title)); annotate_browser__show(&browser->b, title, help); @@ -1011,6 +1085,12 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, ui__error("Couldn't annotate %s:\n%s", sym->name, msg); return -1; } + + if (!annotate_opts.hide_src_code) { + notes->src->tried_source = true; + if (!annotation__has_source(notes)) + ui__warning("Annotation has no source code."); + } } ui_helpline__push("Press ESC to exit"); @@ -1025,7 +1105,7 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, ret = annotate_browser__run(&browser, evsel, hbt); - if(not_annotated) + if (not_annotated && !notes->src->tried_source) annotated_source__purge(notes->src); return ret; diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c index 2213b4661600..5b5ca32e3eef 100644 --- a/tools/perf/ui/browsers/header.c +++ b/tools/perf/ui/browsers/header.c @@ -93,16 +93,14 @@ static int ui__list_menu(int argc, char * const argv[]) return list_menu__run(&menu); } -int tui__header_window(struct perf_env *env) +int tui__header_window(struct perf_session *session) { int i, argc = 0; char **argv; - struct perf_session *session; char *ptr, *pos; size_t size; FILE *fp = open_memstream(&ptr, &size); - session = container_of(env, struct perf_session, header.env); perf_header__fprintf_info(session, fp, true); fclose(fp); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index d26b925e3d7f..d9d3fb44477a 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -3233,7 +3233,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h case 'i': /* env->arch is NULL for live-mode (i.e. perf top) */ if (env->arch) - tui__header_window(env); + tui__header_window(evsel__session(evsel)); continue; case 'F': symbol_conf.filter_relative ^= 1; diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index 2d04ece833aa..1e8c2c2f952d 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -94,7 +94,7 @@ static int check_ev_match(int dir_fd, const char *scriptname, struct perf_sessio FILE *fp; { - char filename[FILENAME_MAX + 5]; + char filename[NAME_MAX + 5]; int fd; scnprintf(filename, sizeof(filename), "bin/%s-record", scriptname); diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 16c6eff4d241..022534eed68c 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -108,7 +108,7 @@ static void ui__signal_backtrace(int sig) printf("-------- backtrace --------\n"); size = backtrace(stackdump, ARRAY_SIZE(stackdump)); - backtrace_symbols_fd(stackdump, size, STDOUT_FILENO); + __dump_stack(stdout, stackdump, size); exit(0); } diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 7910d908c814..4959e7a990e4 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -41,6 +41,7 @@ perf-util-y += rbtree.o perf-util-y += libstring.o perf-util-y += bitmap.o perf-util-y += hweight.o +perf-util-y += sha1.o perf-util-y += smt.o perf-util-y += strbuf.o perf-util-y += string.o @@ -84,8 +85,10 @@ perf-util-y += pmu.o perf-util-y += pmus.o perf-util-y += pmu-flex.o perf-util-y += pmu-bison.o +perf-util-y += drm_pmu.o perf-util-y += hwmon_pmu.o perf-util-y += tool_pmu.o +perf-util-y += tp_pmu.o perf-util-y += svghelper.o perf-util-y += trace-event-info.o perf-util-y += trace-event-scripting.o @@ -175,6 +178,7 @@ perf-util-$(CONFIG_PERF_BPF_SKEL) += btf.o ifeq ($(CONFIG_TRACE),y) perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf-trace-summary.o + perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf_trace_augment.o endif ifeq ($(CONFIG_LIBTRACEEVENT),y) @@ -421,7 +425,7 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-util-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/util/affinity.c b/tools/perf/util/affinity.c index 38dc4524b7e8..4fe851334296 100644 --- a/tools/perf/util/affinity.c +++ b/tools/perf/util/affinity.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "perf.h" #include "cpumap.h" #include "affinity.h" @@ -83,3 +84,20 @@ void affinity__cleanup(struct affinity *a) if (a != NULL) __affinity__cleanup(a); } + +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; + + if (!cpuset) + return; + + perf_cpu_map__for_each_cpu_skip_any(cpu, idx, cpumap) + __set_bit(cpu.cpu, cpuset); + + sched_setaffinity(0, cpu_set_size, (cpu_set_t *)cpuset); + zfree(&cpuset); +} diff --git a/tools/perf/util/affinity.h b/tools/perf/util/affinity.h index 0ad6a18ef20c..7341194b2298 100644 --- a/tools/perf/util/affinity.h +++ b/tools/perf/util/affinity.h @@ -4,6 +4,7 @@ #include +struct perf_cpu_map; struct affinity { unsigned long *orig_cpus; unsigned long *sched_cpus; @@ -13,5 +14,6 @@ struct affinity { void affinity__cleanup(struct affinity *a); void affinity__set(struct affinity *a, int cpu); int affinity__setup(struct affinity *a); +void cpu_map__set_affinity(const struct perf_cpu_map *cpumap); #endif // PERF_AFFINITY_H diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index 4b540e6fb42d..b084dee76b1a 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -354,7 +354,7 @@ static void parse_cpuid(struct perf_env *env) */ bool evlist__has_amd_ibs(struct evlist *evlist) { - struct perf_env *env = evlist->env; + struct perf_env *env = perf_session__env(evlist->session); int ret, nr_pmu_mappings = perf_env__nr_pmu_mappings(env); const char *pmu_mapping = perf_env__pmu_mappings(env); char name[sizeof("ibs_fetch")]; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 264a212b47df..0dd475a744b6 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1451,6 +1451,7 @@ void annotated_source__purge(struct annotated_source *as) list_del_init(&al->node); disasm_line__free(disasm_line(al)); } + as->tried_source = false; } static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) @@ -2280,6 +2281,7 @@ void annotation_options__init(void) opt->annotate_src = true; opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS; opt->percent_type = PERCENT_PERIOD_LOCAL; + opt->hide_src_code = true; opt->hide_src_code_on_title = true; } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index bbb89b32f398..8b5131d257b0 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -294,6 +294,7 @@ struct annotated_source { int nr_entries; int nr_asm_entries; int max_jump_sources; + bool tried_source; u64 start; struct { u8 addr; diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index d46e0cccac99..8942fa598a84 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -856,7 +856,7 @@ static bool arm_spe__synth_ds(struct arm_spe_queue *speq, const char *cpuid; pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n"); - cpuid = perf_env__cpuid(spe->session->evlist->env); + cpuid = perf_env__cpuid(perf_session__env(spe->session)); midr = strtol(cpuid, NULL, 16); } else { /* CPU ID is -1 for per-thread mode */ diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 03211c2623de..ebd32f1b8f12 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1890,7 +1890,7 @@ int __weak compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) } static int __auxtrace_mmap__read(struct mmap *map, - struct auxtrace_record *itr, + struct auxtrace_record *itr, struct perf_env *env, const struct perf_tool *tool, process_auxtrace_t fn, bool snapshot, size_t snapshot_size) { @@ -1900,7 +1900,7 @@ static int __auxtrace_mmap__read(struct mmap *map, size_t size, head_off, old_off, len1, len2, padding; union perf_event ev; void *data1, *data2; - int kernel_is_64_bit = perf_env__kernel_is_64_bit(evsel__env(NULL)); + int kernel_is_64_bit = perf_env__kernel_is_64_bit(env); head = auxtrace_mmap__read_head(mm, kernel_is_64_bit); @@ -2002,17 +2002,18 @@ static int __auxtrace_mmap__read(struct mmap *map, } int auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, - const struct perf_tool *tool, process_auxtrace_t fn) + struct perf_env *env, const struct perf_tool *tool, + process_auxtrace_t fn) { - return __auxtrace_mmap__read(map, itr, tool, fn, false, 0); + return __auxtrace_mmap__read(map, itr, env, tool, fn, false, 0); } int auxtrace_mmap__read_snapshot(struct mmap *map, - struct auxtrace_record *itr, + struct auxtrace_record *itr, struct perf_env *env, const struct perf_tool *tool, process_auxtrace_t fn, size_t snapshot_size) { - return __auxtrace_mmap__read(map, itr, tool, fn, true, snapshot_size); + return __auxtrace_mmap__read(map, itr, env, tool, fn, true, snapshot_size); } /** diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index b0db84d27b25..f001cbb68f8e 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -23,6 +23,7 @@ union perf_event; struct perf_session; struct evlist; struct evsel; +struct perf_env; struct perf_tool; struct mmap; struct perf_sample; @@ -512,10 +513,11 @@ typedef int (*process_auxtrace_t)(const struct perf_tool *tool, size_t len1, void *data2, size_t len2); int auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, - const struct perf_tool *tool, process_auxtrace_t fn); + struct perf_env *env, const struct perf_tool *tool, + process_auxtrace_t fn); int auxtrace_mmap__read_snapshot(struct mmap *map, - struct auxtrace_record *itr, + struct auxtrace_record *itr, struct perf_env *env, const struct perf_tool *tool, process_auxtrace_t fn, size_t snapshot_size); diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index c81444059ad0..2298cd396c42 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -1,13 +1,21 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include +#include +#include #include +#include #include #include #include +#include #include #include +#include #include +#include #include +#include #include #include "bpf-event.h" #include "bpf-utils.h" @@ -151,6 +159,362 @@ static int synthesize_bpf_prog_name(char *buf, int size, return name_len; } +#ifdef HAVE_LIBBPF_STRINGS_SUPPORT + +#define BPF_METADATA_PREFIX "bpf_metadata_" +#define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1) + +static bool name_has_bpf_metadata_prefix(const char **s) +{ + if (strncmp(*s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) != 0) + return false; + *s += BPF_METADATA_PREFIX_LEN; + return true; +} + +struct bpf_metadata_map { + struct btf *btf; + const struct btf_type *datasec; + void *rodata; + size_t rodata_size; + unsigned int num_vars; +}; + +static int bpf_metadata_read_map_data(__u32 map_id, struct bpf_metadata_map *map) +{ + int map_fd; + struct bpf_map_info map_info; + __u32 map_info_len; + int key; + struct btf *btf; + const struct btf_type *datasec; + struct btf_var_secinfo *vsi; + unsigned int vlen, vars; + void *rodata; + + map_fd = bpf_map_get_fd_by_id(map_id); + if (map_fd < 0) + return -1; + + memset(&map_info, 0, sizeof(map_info)); + map_info_len = sizeof(map_info); + if (bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len) < 0) + goto out_close; + + /* If it's not an .rodata map, don't bother. */ + if (map_info.type != BPF_MAP_TYPE_ARRAY || + map_info.key_size != sizeof(int) || + map_info.max_entries != 1 || + !map_info.btf_value_type_id || + !strstr(map_info.name, ".rodata")) { + goto out_close; + } + + btf = btf__load_from_kernel_by_id(map_info.btf_id); + if (!btf) + goto out_close; + datasec = btf__type_by_id(btf, map_info.btf_value_type_id); + if (!btf_is_datasec(datasec)) + goto out_free_btf; + + /* + * If there aren't any variables with the "bpf_metadata_" prefix, + * don't bother. + */ + vlen = btf_vlen(datasec); + vsi = btf_var_secinfos(datasec); + vars = 0; + for (unsigned int i = 0; i < vlen; i++, vsi++) { + const struct btf_type *t_var = btf__type_by_id(btf, vsi->type); + const char *name = btf__name_by_offset(btf, t_var->name_off); + + if (name_has_bpf_metadata_prefix(&name)) + vars++; + } + if (vars == 0) + goto out_free_btf; + + rodata = zalloc(map_info.value_size); + if (!rodata) + goto out_free_btf; + key = 0; + if (bpf_map_lookup_elem(map_fd, &key, rodata)) { + free(rodata); + goto out_free_btf; + } + close(map_fd); + + map->btf = btf; + map->datasec = datasec; + map->rodata = rodata; + map->rodata_size = map_info.value_size; + map->num_vars = vars; + return 0; + +out_free_btf: + btf__free(btf); +out_close: + close(map_fd); + return -1; +} + +struct format_btf_ctx { + char *buf; + size_t buf_size; + size_t buf_idx; +}; + +static void format_btf_cb(void *arg, const char *fmt, va_list ap) +{ + int n; + struct format_btf_ctx *ctx = (struct format_btf_ctx *)arg; + + n = vsnprintf(ctx->buf + ctx->buf_idx, ctx->buf_size - ctx->buf_idx, + fmt, ap); + ctx->buf_idx += n; + if (ctx->buf_idx >= ctx->buf_size) + ctx->buf_idx = ctx->buf_size; +} + +static void format_btf_variable(struct btf *btf, char *buf, size_t buf_size, + const struct btf_type *t, const void *btf_data) +{ + struct format_btf_ctx ctx = { + .buf = buf, + .buf_idx = 0, + .buf_size = buf_size, + }; + const struct btf_dump_type_data_opts opts = { + .sz = sizeof(struct btf_dump_type_data_opts), + .skip_names = 1, + .compact = 1, + .emit_strings = 1, + }; + struct btf_dump *d; + size_t btf_size; + + d = btf_dump__new(btf, format_btf_cb, &ctx, NULL); + btf_size = btf__resolve_size(btf, t->type); + btf_dump__dump_type_data(d, t->type, btf_data, btf_size, &opts); + btf_dump__free(d); +} + +static void bpf_metadata_fill_event(struct bpf_metadata_map *map, + struct perf_record_bpf_metadata *bpf_metadata_event) +{ + struct btf_var_secinfo *vsi; + unsigned int i, vlen; + + memset(bpf_metadata_event->prog_name, 0, BPF_PROG_NAME_LEN); + vlen = btf_vlen(map->datasec); + vsi = btf_var_secinfos(map->datasec); + + for (i = 0; i < vlen; i++, vsi++) { + const struct btf_type *t_var = btf__type_by_id(map->btf, + vsi->type); + const char *name = btf__name_by_offset(map->btf, + t_var->name_off); + const __u64 nr_entries = bpf_metadata_event->nr_entries; + struct perf_record_bpf_metadata_entry *entry; + + if (!name_has_bpf_metadata_prefix(&name)) + continue; + + if (nr_entries >= (__u64)map->num_vars) + break; + + entry = &bpf_metadata_event->entries[nr_entries]; + memset(entry, 0, sizeof(*entry)); + snprintf(entry->key, BPF_METADATA_KEY_LEN, "%s", name); + format_btf_variable(map->btf, entry->value, + BPF_METADATA_VALUE_LEN, t_var, + map->rodata + vsi->offset); + bpf_metadata_event->nr_entries++; + } +} + +static void bpf_metadata_free_map_data(struct bpf_metadata_map *map) +{ + btf__free(map->btf); + free(map->rodata); +} + +static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, + __u32 nr_variables) +{ + struct bpf_metadata *metadata; + size_t event_size; + + metadata = zalloc(sizeof(struct bpf_metadata)); + if (!metadata) + return NULL; + + metadata->prog_names = zalloc(nr_prog_tags * sizeof(char *)); + if (!metadata->prog_names) { + bpf_metadata_free(metadata); + return NULL; + } + for (__u32 prog_index = 0; prog_index < nr_prog_tags; prog_index++) { + metadata->prog_names[prog_index] = zalloc(BPF_PROG_NAME_LEN); + if (!metadata->prog_names[prog_index]) { + bpf_metadata_free(metadata); + return NULL; + } + metadata->nr_prog_names++; + } + + event_size = sizeof(metadata->event->bpf_metadata) + + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + metadata->event = zalloc(event_size); + if (!metadata->event) { + bpf_metadata_free(metadata); + return NULL; + } + metadata->event->bpf_metadata = (struct perf_record_bpf_metadata) { + .header = { + .type = PERF_RECORD_BPF_METADATA, + .size = event_size, + }, + .nr_entries = 0, + }; + + return metadata; +} + +static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) +{ + struct bpf_metadata *metadata; + const __u32 *map_ids = (__u32 *)(uintptr_t)info->map_ids; + + for (__u32 map_index = 0; map_index < info->nr_map_ids; map_index++) { + struct bpf_metadata_map map; + + if (bpf_metadata_read_map_data(map_ids[map_index], &map) != 0) + continue; + + metadata = bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); + if (!metadata) + continue; + + bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); + + for (__u32 index = 0; index < info->nr_prog_tags; index++) { + synthesize_bpf_prog_name(metadata->prog_names[index], + BPF_PROG_NAME_LEN, info, + map.btf, index); + } + + bpf_metadata_free_map_data(&map); + + return metadata; + } + + return NULL; +} + +static int synthesize_perf_record_bpf_metadata(const struct bpf_metadata *metadata, + const struct perf_tool *tool, + perf_event__handler_t process, + struct machine *machine) +{ + const size_t event_size = metadata->event->header.size; + union perf_event *event; + int err = 0; + + event = zalloc(event_size + machine->id_hdr_size); + if (!event) + return -1; + memcpy(event, metadata->event, event_size); + memset((void *)event + event->header.size, 0, machine->id_hdr_size); + event->header.size += machine->id_hdr_size; + for (__u32 index = 0; index < metadata->nr_prog_names; index++) { + memcpy(event->bpf_metadata.prog_name, + metadata->prog_names[index], BPF_PROG_NAME_LEN); + err = perf_tool__process_synth_event(tool, event, machine, + process); + if (err != 0) + break; + } + + free(event); + return err; +} + +void bpf_metadata_free(struct bpf_metadata *metadata) +{ + if (metadata == NULL) + return; + for (__u32 index = 0; index < metadata->nr_prog_names; index++) + free(metadata->prog_names[index]); + free(metadata->prog_names); + free(metadata->event); + free(metadata); +} + +#else /* HAVE_LIBBPF_STRINGS_SUPPORT */ + +static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info __maybe_unused) +{ + return NULL; +} + +static int synthesize_perf_record_bpf_metadata(const struct bpf_metadata *metadata __maybe_unused, + const struct perf_tool *tool __maybe_unused, + perf_event__handler_t process __maybe_unused, + struct machine *machine __maybe_unused) +{ + return 0; +} + +void bpf_metadata_free(struct bpf_metadata *metadata __maybe_unused) +{ +} + +#endif /* HAVE_LIBBPF_STRINGS_SUPPORT */ + +struct bpf_metadata_final_ctx { + const struct perf_tool *tool; + perf_event__handler_t process; + struct machine *machine; +}; + +static void synthesize_final_bpf_metadata_cb(struct bpf_prog_info_node *node, + void *data) +{ + struct bpf_metadata_final_ctx *ctx = (struct bpf_metadata_final_ctx *)data; + struct bpf_metadata *metadata = node->metadata; + int err; + + if (metadata == NULL) + return; + err = synthesize_perf_record_bpf_metadata(metadata, ctx->tool, + ctx->process, ctx->machine); + if (err != 0) { + const char *prog_name = metadata->prog_names[0]; + + if (prog_name != NULL) + pr_warning("Couldn't synthesize final BPF metadata for %s.\n", prog_name); + else + pr_warning("Couldn't synthesize final BPF metadata.\n"); + } + bpf_metadata_free(metadata); + node->metadata = NULL; +} + +void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, + perf_event__handler_t process) +{ + struct perf_env *env = &session->header.env; + struct bpf_metadata_final_ctx ctx = { + .tool = session->tool, + .process = process, + .machine = &session->machines.host, + }; + + perf_env__iterate_bpf_prog_info(env, synthesize_final_bpf_metadata_cb, + &ctx); +} + /* * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf * program. One PERF_RECORD_BPF_EVENT is generated for the program. And @@ -173,6 +537,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, const struct perf_tool *tool = session->tool; struct bpf_prog_info_node *info_node; struct perf_bpil *info_linear; + struct bpf_metadata *metadata; struct bpf_prog_info *info; struct btf *btf = NULL; struct perf_env *env; @@ -184,7 +549,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, * for perf-record and perf-report use header.env; * otherwise, use global perf_env. */ - env = session->data ? &session->header.env : &perf_env; + env = perf_session__env(session); arrays = 1UL << PERF_BPIL_JITED_KSYMS; arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS; @@ -193,6 +558,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, arrays |= 1UL << PERF_BPIL_JITED_INSNS; arrays |= 1UL << PERF_BPIL_LINE_INFO; arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO; + arrays |= 1UL << PERF_BPIL_MAP_IDS; info_linear = get_bpf_prog_info_linear(fd, arrays); if (IS_ERR_OR_NULL(info_linear)) { @@ -289,10 +655,17 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, } info_node->info_linear = info_linear; + info_node->metadata = NULL; if (!perf_env__insert_bpf_prog_info(env, info_node)) { - free(info_linear); + /* + * Insert failed, likely because of a duplicate event + * made by the sideband thread. Ignore synthesizing the + * metadata. + */ free(info_node); + goto out; } + /* info_linear is now owned by info_node and shouldn't be freed below. */ info_linear = NULL; /* @@ -301,6 +674,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, */ err = perf_tool__process_synth_event(tool, event, machine, process); + + /* Synthesize PERF_RECORD_BPF_METADATA */ + metadata = bpf_metadata_create(info); + if (metadata != NULL) { + err = synthesize_perf_record_bpf_metadata(metadata, + tool, process, + machine); + bpf_metadata_free(metadata); + } } out: @@ -451,18 +833,18 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, return err; } -static void perf_env__add_bpf_info(struct perf_env *env, u32 id) +static int perf_env__add_bpf_info(struct perf_env *env, u32 id) { struct bpf_prog_info_node *info_node; struct perf_bpil *info_linear; struct btf *btf = NULL; u64 arrays; u32 btf_id; - int fd; + int fd, err = 0; fd = bpf_prog_get_fd_by_id(id); if (fd < 0) - return; + return -EINVAL; arrays = 1UL << PERF_BPIL_JITED_KSYMS; arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS; @@ -471,10 +853,12 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id) arrays |= 1UL << PERF_BPIL_JITED_INSNS; arrays |= 1UL << PERF_BPIL_LINE_INFO; arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO; + arrays |= 1UL << PERF_BPIL_MAP_IDS; info_linear = get_bpf_prog_info_linear(fd, arrays); if (IS_ERR_OR_NULL(info_linear)) { pr_debug("%s: failed to get BPF program info. aborting\n", __func__); + err = PTR_ERR(info_linear); goto out; } @@ -483,39 +867,48 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id) info_node = malloc(sizeof(struct bpf_prog_info_node)); if (info_node) { info_node->info_linear = info_linear; + info_node->metadata = bpf_metadata_create(&info_linear->info); if (!perf_env__insert_bpf_prog_info(env, info_node)) { + pr_debug("%s: duplicate add bpf info request for id %u\n", + __func__, btf_id); free(info_linear); free(info_node); + goto out; } - } else + } else { free(info_linear); + err = -ENOMEM; + goto out; + } if (btf_id == 0) goto out; btf = btf__load_from_kernel_by_id(btf_id); - if (libbpf_get_error(btf)) { - pr_debug("%s: failed to get BTF of id %u, aborting\n", - __func__, btf_id); - goto out; + if (!btf) { + err = -errno; + pr_debug("%s: failed to get BTF of id %u %d\n", __func__, btf_id, err); + } else { + perf_env__fetch_btf(env, btf_id, btf); } - perf_env__fetch_btf(env, btf_id, btf); out: btf__free(btf); close(fd); + return err; } static int bpf_event__sb_cb(union perf_event *event, void *data) { struct perf_env *env = data; + int ret = 0; if (event->header.type != PERF_RECORD_BPF_EVENT) return -1; switch (event->bpf.type) { case PERF_BPF_EVENT_PROG_LOAD: - perf_env__add_bpf_info(env, event->bpf.id); + ret = perf_env__add_bpf_info(env, event->bpf.id); case PERF_BPF_EVENT_PROG_UNLOAD: /* @@ -529,7 +922,7 @@ static int bpf_event__sb_cb(union perf_event *event, void *data) break; } - return 0; + return ret; } int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env) diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index e2f0420905f5..60d2c6637af5 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -17,8 +17,15 @@ struct record_opts; struct evlist; struct target; +struct bpf_metadata { + union perf_event *event; + char **prog_names; + __u64 nr_prog_names; +}; + struct bpf_prog_info_node { struct perf_bpil *info_linear; + struct bpf_metadata *metadata; struct rb_node rb_node; }; @@ -36,6 +43,7 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env); void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, struct perf_env *env, FILE *fp); +void bpf_metadata_free(struct bpf_metadata *metadata); #else static inline int machine__process_bpf(struct machine *machine __maybe_unused, union perf_event *event __maybe_unused, @@ -55,6 +63,11 @@ static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info _ FILE *fp __maybe_unused) { +} + +static inline void bpf_metadata_free(struct bpf_metadata *metadata __maybe_unused) +{ + } #endif // HAVE_LIBBPF_SUPPORT #endif diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index a4fdf6911ec1..a0b11f35395f 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -52,6 +52,7 @@ #include #include +#include "util/cap.h" #include "util/debug.h" #include "util/evsel.h" #include "util/target.h" @@ -449,7 +450,9 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) struct bpf_program *prog; struct bpf_link *link; struct perf_bpf_filter_entry *entry; - bool needs_idx_hash = !target__has_cpu(target) && !target->uid_str; + bool needs_idx_hash = !target__has_cpu(target); + DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts, + .dont_enable = true); entry = calloc(MAX_FILTERS, sizeof(*entry)); if (entry == NULL) @@ -521,7 +524,8 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) prog = skel->progs.perf_sample_filter; for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { - link = bpf_program__attach_perf_event(prog, FD(evsel, x, y)); + link = bpf_program__attach_perf_event_opts(prog, FD(evsel, x, y), + &pe_opts); if (IS_ERR(link)) { pr_err("Failed to attach perf sample-filter program\n"); ret = PTR_ERR(link); @@ -618,11 +622,38 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term return expr; } +static bool check_bpf_filter_capable(void) +{ + bool used_root; + + if (perf_cap__capable(CAP_BPF, &used_root)) + return true; + + if (!used_root) { + /* Check if root already pinned the filter programs and maps */ + int fd = get_pinned_fd("filters"); + + if (fd >= 0) { + close(fd); + return true; + } + } + + pr_err("Error: BPF filter only works for %s!\n" + "\tPlease run 'perf record --setup-filter pin' as root first.\n", + used_root ? "root" : "users with the CAP_BPF capability"); + + return false; +} + int perf_bpf_filter__parse(struct list_head *expr_head, const char *str) { YY_BUFFER_STATE buffer; int ret; + if (!check_bpf_filter_capable()) + return -EPERM; + buffer = perf_bpf_filter__scan_string(str); ret = perf_bpf_filter_parse(expr_head); diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h index 916ed7770b73..122477f2de44 100644 --- a/tools/perf/util/bpf-filter.h +++ b/tools/perf/util/bpf-filter.h @@ -5,6 +5,7 @@ #include #include "bpf_skel/sample-filter.h" +#include "util/debug.h" struct perf_bpf_filter_expr { struct list_head list; @@ -38,6 +39,8 @@ int perf_bpf_filter__unpin(void); static inline int perf_bpf_filter__parse(struct list_head *expr_head __maybe_unused, const char *str __maybe_unused) { + pr_err("Error: BPF filter is requested but perf is not built with BPF.\n" + "\tPlease make sure to build with libbpf and BPF skeleton.\n"); return -EOPNOTSUPP; } static inline int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused, diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index 80b1d2b3729b..5a66dc8594aa 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -20,7 +20,7 @@ struct bpil_array_desc { */ }; -static struct bpil_array_desc bpil_array_desc[] = { +static const struct bpil_array_desc bpil_array_desc[] = { [PERF_BPIL_JITED_INSNS] = { offsetof(struct bpf_prog_info, jited_prog_insns), offsetof(struct bpf_prog_info, jited_prog_len), @@ -115,7 +115,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) __u32 info_len = sizeof(info); __u32 data_len = 0; int i, err; - void *ptr; + __u8 *ptr; if (arrays >> PERF_BPIL_LAST_ARRAY) return ERR_PTR(-EINVAL); @@ -126,15 +126,15 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) pr_debug("can't get prog info: %s", strerror(errno)); return ERR_PTR(-EFAULT); } + if (info.type >= __MAX_BPF_PROG_TYPE) + pr_debug("%s:%d: unexpected program type %u\n", __func__, __LINE__, info.type); /* step 2: calculate total size of all arrays */ for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { + const struct bpil_array_desc *desc = &bpil_array_desc[i]; bool include_array = (arrays & (1UL << i)) > 0; - struct bpil_array_desc *desc; __u32 count, size; - desc = bpil_array_desc + i; - /* kernel is too old to support this field */ if (info_len < desc->array_offset + sizeof(__u32) || info_len < desc->count_offset + sizeof(__u32) || @@ -163,19 +163,20 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) ptr = info_linear->data; for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { - struct bpil_array_desc *desc; + const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u32 count, size; if ((arrays & (1UL << i)) == 0) continue; - desc = bpil_array_desc + i; count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); bpf_prog_info_set_offset_u32(&info_linear->info, desc->count_offset, count); bpf_prog_info_set_offset_u32(&info_linear->info, desc->size_offset, size); + assert(ptr >= info_linear->data); + assert(ptr < &info_linear->data[data_len]); bpf_prog_info_set_offset_u64(&info_linear->info, desc->array_offset, ptr_to_u64(ptr)); @@ -189,27 +190,45 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) free(info_linear); return ERR_PTR(-EFAULT); } + if (info_linear->info.type >= __MAX_BPF_PROG_TYPE) { + pr_debug("%s:%d: unexpected program type %u\n", + __func__, __LINE__, info_linear->info.type); + } /* step 6: verify the data */ + ptr = info_linear->data; for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { - struct bpil_array_desc *desc; - __u32 v1, v2; + const struct bpil_array_desc *desc = &bpil_array_desc[i]; + __u32 count1, count2, size1, size2; + __u64 ptr2; if ((arrays & (1UL << i)) == 0) continue; - desc = bpil_array_desc + i; - v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); - v2 = bpf_prog_info_read_offset_u32(&info_linear->info, + count1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); + count2 = bpf_prog_info_read_offset_u32(&info_linear->info, desc->count_offset); - if (v1 != v2) - pr_warning("%s: mismatch in element count\n", __func__); + if (count1 != count2) { + pr_warning("%s: mismatch in element count %u vs %u\n", __func__, count1, count2); + free(info_linear); + return ERR_PTR(-ERANGE); + } - v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); - v2 = bpf_prog_info_read_offset_u32(&info_linear->info, + size1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); + size2 = bpf_prog_info_read_offset_u32(&info_linear->info, desc->size_offset); - if (v1 != v2) - pr_warning("%s: mismatch in rec size\n", __func__); + if (size1 != size2) { + pr_warning("%s: mismatch in rec size %u vs %u\n", __func__, size1, size2); + free(info_linear); + return ERR_PTR(-ERANGE); + } + ptr2 = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); + if (ptr_to_u64(ptr) != ptr2) { + pr_warning("%s: mismatch in array %p vs %llx\n", __func__, ptr, ptr2); + free(info_linear); + return ERR_PTR(-ERANGE); + } + ptr += roundup(count1 * size1, sizeof(__u64)); } /* step 7: update info_len and data_len */ @@ -224,13 +243,12 @@ void bpil_addr_to_offs(struct perf_bpil *info_linear) int i; for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { - struct bpil_array_desc *desc; + const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; if ((info_linear->arrays & (1UL << i)) == 0) continue; - desc = bpil_array_desc + i; addr = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); offs = addr - ptr_to_u64(info_linear->data); @@ -244,13 +262,12 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) int i; for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { - struct bpil_array_desc *desc; + const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; if ((info_linear->arrays & (1UL << i)) == 0) continue; - desc = bpil_array_desc + i; offs = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); addr = offs + ptr_to_u64(info_linear->data); diff --git a/tools/perf/util/bpf_ftrace.c b/tools/perf/util/bpf_ftrace.c index 7324668cc83e..0cb02412043c 100644 --- a/tools/perf/util/bpf_ftrace.c +++ b/tools/perf/util/bpf_ftrace.c @@ -21,16 +21,27 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) { int fd, err; int i, ncpus = 1, ntasks = 1; - struct filter_entry *func; + struct filter_entry *func = NULL; - if (!list_is_singular(&ftrace->filters)) { - pr_err("ERROR: %s target function(s).\n", - list_empty(&ftrace->filters) ? "No" : "Too many"); - return -1; + if (!list_empty(&ftrace->filters)) { + if (!list_is_singular(&ftrace->filters)) { + pr_err("ERROR: Too many target functions.\n"); + return -1; + } + func = list_first_entry(&ftrace->filters, struct filter_entry, list); + } else { + int count = 0; + struct list_head *pos; + + list_for_each(pos, &ftrace->event_pair) + count++; + + if (count != 2) { + pr_err("ERROR: Needs two target events.\n"); + return -1; + } } - func = list_first_entry(&ftrace->filters, struct filter_entry, list); - skel = func_latency_bpf__open(); if (!skel) { pr_err("Failed to open func latency skeleton\n"); @@ -93,20 +104,44 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) skel->bss->min = INT64_MAX; - skel->links.func_begin = bpf_program__attach_kprobe(skel->progs.func_begin, - false, func->name); - if (IS_ERR(skel->links.func_begin)) { - pr_err("Failed to attach fentry program\n"); - err = PTR_ERR(skel->links.func_begin); - goto out; - } + if (func) { + skel->links.func_begin = bpf_program__attach_kprobe(skel->progs.func_begin, + false, func->name); + if (IS_ERR(skel->links.func_begin)) { + pr_err("Failed to attach fentry program\n"); + err = PTR_ERR(skel->links.func_begin); + goto out; + } - skel->links.func_end = bpf_program__attach_kprobe(skel->progs.func_end, - true, func->name); - if (IS_ERR(skel->links.func_end)) { - pr_err("Failed to attach fexit program\n"); - err = PTR_ERR(skel->links.func_end); - goto out; + skel->links.func_end = bpf_program__attach_kprobe(skel->progs.func_end, + true, func->name); + if (IS_ERR(skel->links.func_end)) { + pr_err("Failed to attach fexit program\n"); + err = PTR_ERR(skel->links.func_end); + goto out; + } + } else { + struct filter_entry *event; + + event = list_first_entry(&ftrace->event_pair, struct filter_entry, list); + + skel->links.event_begin = bpf_program__attach_raw_tracepoint(skel->progs.event_begin, + event->name); + if (IS_ERR(skel->links.event_begin)) { + pr_err("Failed to attach first tracepoint program\n"); + err = PTR_ERR(skel->links.event_begin); + goto out; + } + + event = list_next_entry(event, list); + + skel->links.event_end = bpf_program__attach_raw_tracepoint(skel->progs.event_end, + event->name); + if (IS_ERR(skel->links.event_end)) { + pr_err("Failed to attach second tracepoint program\n"); + err = PTR_ERR(skel->links.event_end); + goto out; + } } /* XXX: we don't actually use this fd - just for poll() */ diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index e4352881e3fa..cb86e261b4de 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -7,7 +7,6 @@ */ #include "vmlinux.h" -#include "../trace_augment.h" #include #include @@ -27,6 +26,8 @@ #define MAX_CPUS 4096 +#define TRACE_AUG_MAX_BUF 32 /* for buffer augmentation in perf trace */ + /* bpf-output associated map */ struct __augmented_syscalls__ { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); diff --git a/tools/perf/util/bpf_skel/func_latency.bpf.c b/tools/perf/util/bpf_skel/func_latency.bpf.c index e731a79a753a..621e2022c8bc 100644 --- a/tools/perf/util/bpf_skel/func_latency.bpf.c +++ b/tools/perf/util/bpf_skel/func_latency.bpf.c @@ -52,34 +52,89 @@ const volatile unsigned int min_latency; const volatile unsigned int max_latency; const volatile unsigned int bucket_num = NUM_BUCKET; -SEC("kprobe/func") -int BPF_PROG(func_begin) +static bool can_record(void) { - __u64 key, now; - - if (!enabled) - return 0; - - key = bpf_get_current_pid_tgid(); - if (has_cpu) { __u32 cpu = bpf_get_smp_processor_id(); __u8 *ok; ok = bpf_map_lookup_elem(&cpu_filter, &cpu); if (!ok) - return 0; + return false; } if (has_task) { - __u32 pid = key & 0xffffffff; + __u32 pid = bpf_get_current_pid_tgid(); __u8 *ok; ok = bpf_map_lookup_elem(&task_filter, &pid); if (!ok) - return 0; + return false; + } + return true; +} + +static void update_latency(__s64 delta) +{ + __u64 val = delta; + __u32 key = 0; + __u64 *hist; + __u64 cmp_base = use_nsec ? 1 : 1000; + + if (delta < 0) + return; + + if (bucket_range != 0) { + val = delta / cmp_base; + + if (min_latency > 0) { + if (val > min_latency) + val -= min_latency; + else + goto do_lookup; + } + + // Less than 1 unit (ms or ns), or, in the future, + // than the min latency desired. + if (val > 0) { // 1st entry: [ 1 unit .. bucket_range units ) + key = val / bucket_range + 1; + if (key >= bucket_num) + key = bucket_num - 1; + } + + goto do_lookup; + } + // calculate index using delta + for (key = 0; key < (bucket_num - 1); key++) { + if (delta < (cmp_base << key)) + break; } +do_lookup: + hist = bpf_map_lookup_elem(&latency, &key); + if (!hist) + return; + + __sync_fetch_and_add(hist, 1); + + __sync_fetch_and_add(&total, delta); // always in nsec + __sync_fetch_and_add(&count, 1); + + if (delta > max) + max = delta; + if (delta < min) + min = delta; +} + +SEC("kprobe/func") +int BPF_PROG(func_begin) +{ + __u64 key, now; + + if (!enabled || !can_record()) + return 0; + + key = bpf_get_current_pid_tgid(); now = bpf_ktime_get_ns(); // overwrite timestamp for nested functions @@ -92,7 +147,6 @@ int BPF_PROG(func_end) { __u64 tid; __u64 *start; - __u64 cmp_base = use_nsec ? 1 : 1000; if (!enabled) return 0; @@ -101,56 +155,44 @@ int BPF_PROG(func_end) start = bpf_map_lookup_elem(&functime, &tid); if (start) { - __s64 delta = bpf_ktime_get_ns() - *start; - __u64 val = delta; - __u32 key = 0; - __u64 *hist; - + update_latency(bpf_ktime_get_ns() - *start); + bpf_map_delete_elem(&functime, &tid); + } + + return 0; +} + +SEC("raw_tp") +int BPF_PROG(event_begin) +{ + __u64 key, now; + + if (!enabled || !can_record()) + return 0; + + key = bpf_get_current_pid_tgid(); + now = bpf_ktime_get_ns(); + + // overwrite timestamp for nested events + bpf_map_update_elem(&functime, &key, &now, BPF_ANY); + return 0; +} + +SEC("raw_tp") +int BPF_PROG(event_end) +{ + __u64 tid; + __u64 *start; + + if (!enabled) + return 0; + + tid = bpf_get_current_pid_tgid(); + + start = bpf_map_lookup_elem(&functime, &tid); + if (start) { + update_latency(bpf_ktime_get_ns() - *start); bpf_map_delete_elem(&functime, &tid); - - if (delta < 0) - return 0; - - if (bucket_range != 0) { - val = delta / cmp_base; - - if (min_latency > 0) { - if (val > min_latency) - val -= min_latency; - else - goto do_lookup; - } - - // Less than 1 unit (ms or ns), or, in the future, - // than the min latency desired. - if (val > 0) { // 1st entry: [ 1 unit .. bucket_range units ) - key = val / bucket_range + 1; - if (key >= bucket_num) - key = bucket_num - 1; - } - - goto do_lookup; - } - // calculate index using delta - for (key = 0; key < (bucket_num - 1); key++) { - if (delta < (cmp_base << key)) - break; - } - -do_lookup: - hist = bpf_map_lookup_elem(&latency, &key); - if (!hist) - return 0; - - __sync_fetch_and_add(hist, 1); - - __sync_fetch_and_add(&total, delta); // always in nsec - __sync_fetch_and_add(&count, 1); - - if (delta > max) - max = delta; - if (delta < min) - min = delta; } return 0; diff --git a/tools/perf/util/bpf_skel/perf_version.h b/tools/perf/util/bpf_skel/perf_version.h new file mode 100644 index 000000000000..1ed5b2e59bf5 --- /dev/null +++ b/tools/perf/util/bpf_skel/perf_version.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef __PERF_VERSION_H__ +#define __PERF_VERSION_H__ + +#include "vmlinux.h" +#include + +/* + * This is used by tests/shell/record_bpf_metadata.sh + * to verify that BPF metadata generation works. + * + * PERF_VERSION is defined by a build rule at compile time. + */ +const char bpf_metadata_perf_version[] SEC(".rodata") = PERF_VERSION; + +#endif /* __PERF_VERSION_H__ */ diff --git a/tools/perf/util/bpf_trace_augment.c b/tools/perf/util/bpf_trace_augment.c new file mode 100644 index 000000000000..56ed17534caa --- /dev/null +++ b/tools/perf/util/bpf_trace_augment.c @@ -0,0 +1,143 @@ +#include +#include + +#include "util/debug.h" +#include "util/evlist.h" +#include "util/trace_augment.h" + +#include "bpf_skel/augmented_raw_syscalls.skel.h" + +static struct augmented_raw_syscalls_bpf *skel; +static struct evsel *bpf_output; + +int augmented_syscalls__prepare(void) +{ + struct bpf_program *prog; + char buf[128]; + int err; + + skel = augmented_raw_syscalls_bpf__open(); + if (!skel) { + pr_debug("Failed to open augmented syscalls BPF skeleton\n"); + return -errno; + } + + /* + * Disable attaching the BPF programs except for sys_enter and + * sys_exit that tail call into this as necessary. + */ + bpf_object__for_each_program(prog, skel->obj) { + if (prog != skel->progs.sys_enter && prog != skel->progs.sys_exit) + bpf_program__set_autoattach(prog, /*autoattach=*/false); + } + + err = augmented_raw_syscalls_bpf__load(skel); + if (err < 0) { + libbpf_strerror(err, buf, sizeof(buf)); + pr_debug("Failed to load augmented syscalls BPF skeleton: %s\n", buf); + return err; + } + + augmented_raw_syscalls_bpf__attach(skel); + return 0; +} + +int augmented_syscalls__create_bpf_output(struct evlist *evlist) +{ + int err = parse_event(evlist, "bpf-output/no-inherit=1,name=__augmented_syscalls__/"); + + if (err) { + pr_err("ERROR: Setup BPF output event failed: %d\n", err); + return err; + } + + bpf_output = evlist__last(evlist); + assert(evsel__name_is(bpf_output, "__augmented_syscalls__")); + + return 0; +} + +void augmented_syscalls__setup_bpf_output(void) +{ + struct perf_cpu cpu; + int i; + + if (bpf_output == NULL) + return; + + /* + * Set up the __augmented_syscalls__ BPF map to hold for each + * CPU the bpf-output event's file descriptor. + */ + perf_cpu_map__for_each_cpu(cpu, i, bpf_output->core.cpus) { + int mycpu = cpu.cpu; + + bpf_map__update_elem(skel->maps.__augmented_syscalls__, + &mycpu, sizeof(mycpu), + xyarray__entry(bpf_output->core.fd, + mycpu, 0), + sizeof(__u32), BPF_ANY); + } +} + +int augmented_syscalls__set_filter_pids(unsigned int nr, pid_t *pids) +{ + bool value = true; + int err = 0; + + if (skel == NULL) + return 0; + + for (size_t i = 0; i < nr; ++i) { + err = bpf_map__update_elem(skel->maps.pids_filtered, &pids[i], + sizeof(*pids), &value, sizeof(value), + BPF_ANY); + if (err) + break; + } + return err; +} + +int augmented_syscalls__get_map_fds(int *enter_fd, int *exit_fd, int *beauty_fd) +{ + if (skel == NULL) + return -1; + + *enter_fd = bpf_map__fd(skel->maps.syscalls_sys_enter); + *exit_fd = bpf_map__fd(skel->maps.syscalls_sys_exit); + *beauty_fd = bpf_map__fd(skel->maps.beauty_map_enter); + + if (*enter_fd < 0 || *exit_fd < 0 || *beauty_fd < 0) { + pr_err("Error: failed to get syscall or beauty map fd\n"); + return -1; + } + + return 0; +} + +struct bpf_program *augmented_syscalls__unaugmented(void) +{ + return skel->progs.syscall_unaugmented; +} + +struct bpf_program *augmented_syscalls__find_by_title(const char *name) +{ + struct bpf_program *pos; + const char *sec_name; + + if (skel->obj == NULL) + return NULL; + + bpf_object__for_each_program(pos, skel->obj) { + sec_name = bpf_program__section_name(pos); + if (sec_name && !strcmp(sec_name, name)) + return pos; + } + + return NULL; +} + +void augmented_syscalls__cleanup(void) +{ + augmented_raw_syscalls_bpf__destroy(skel); +} diff --git a/tools/perf/util/branch.c b/tools/perf/util/branch.c index ab760e267d41..3712be067464 100644 --- a/tools/perf/util/branch.c +++ b/tools/perf/util/branch.c @@ -46,7 +46,7 @@ const char *branch_new_type_name(int new_type) "FAULT_DATA", "FAULT_INST", /* - * TODO: This switch should happen on 'session->header.env.arch' + * TODO: This switch should happen on 'perf_session__env(session)->arch' * instead, because an arm64 platform perf recording could be * opened for analysis on other platforms as well. */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index e763e8d99a43..bf7f3268b9a2 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -42,10 +42,20 @@ static bool no_buildid_cache; +static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data __maybe_unused) +{ + struct map *map = node->ms.map; + + if (map) + dso__set_hit(map__dso(map)); + + return 0; +} + int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel __maybe_unused, + struct evsel *evsel, struct machine *machine) { struct addr_location al; @@ -63,31 +73,29 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, dso__set_hit(map__dso(al.map)); addr_location__exit(&al); + + sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, + /*symbols=*/false, mark_dso_hit_callback, /*data=*/NULL); + + thread__put(thread); return 0; } -int build_id__sprintf(const struct build_id *build_id, char *bf) +int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size) { - char *bid = bf; - const u8 *raw = build_id->data; - size_t i; + size_t offs = 0; - bf[0] = 0x0; + for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) + offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); - for (i = 0; i < build_id->size; ++i) { - sprintf(bid, "%02x", *raw); - ++raw; - bid += 2; - } - - return (bid - bf) + 1; + return offs; } -int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) +int sysfs__snprintf_build_id(const char *root_dir, char *sbuild_id, size_t sbuild_id_size) { char notes[PATH_MAX]; - struct build_id bid; + struct build_id bid = { .size = 0, }; int ret; if (!root_dir) @@ -99,19 +107,19 @@ int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) if (ret < 0) return ret; - return build_id__sprintf(&bid, sbuild_id); + return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); } -int filename__sprintf_build_id(const char *pathname, char *sbuild_id) +int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sbuild_id_size) { - struct build_id bid; + struct build_id bid = { .size = 0, }; int ret; - ret = filename__read_build_id(pathname, &bid); + ret = filename__read_build_id(pathname, &bid, /*block=*/true); if (ret < 0) return ret; - return build_id__sprintf(&bid, sbuild_id); + return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); } /* asnprintf consolidates asprintf and snprintf */ @@ -212,9 +220,9 @@ static bool build_id_cache__valid_id(char *sbuild_id) return false; if (!strcmp(pathname, DSO__NAME_KALLSYMS)) - ret = sysfs__sprintf_build_id("/", real_sbuild_id); + ret = sysfs__snprintf_build_id("/", real_sbuild_id, sizeof(real_sbuild_id)); else if (pathname[0] == '/') - ret = filename__sprintf_build_id(pathname, real_sbuild_id); + ret = filename__snprintf_build_id(pathname, real_sbuild_id, sizeof(real_sbuild_id)); else ret = -EINVAL; /* Should we support other special DSO cache? */ if (ret >= 0) @@ -243,7 +251,7 @@ char *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, if (!dso__has_build_id(dso)) return NULL; - build_id__sprintf(dso__bid_const(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); linkname = build_id_cache__linkname(sbuild_id, NULL, 0); if (!linkname) return NULL; @@ -326,7 +334,7 @@ static int machine__write_buildid_table_cb(struct dso *dso, void *data) } in_kernel = dso__kernel(dso) || is_kernel_module(name, PERF_RECORD_MISC_CPUMODE_UNKNOWN); - return write_buildid(name, name_len, dso__bid(dso), args->machine->pid, + return write_buildid(name, name_len, &dso__id(dso)->build_id, args->machine->pid, in_kernel ? args->kmisc : args->umisc, args->fd); } @@ -769,7 +777,7 @@ static int build_id_cache__add_b(const struct build_id *bid, { char sbuild_id[SBUILD_ID_SIZE]; - build_id__sprintf(bid, sbuild_id); + build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id)); return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, is_vdso, proper_name, root_dir); @@ -833,7 +841,7 @@ static int filename__read_build_id_ns(const char *filename, int ret; nsinfo__mountns_enter(nsi, &nsc); - ret = filename__read_build_id(filename, bid); + ret = filename__read_build_id(filename, bid, /*block=*/true); nsinfo__mountns_exit(&nsc); return ret; @@ -841,7 +849,7 @@ static int filename__read_build_id_ns(const char *filename, static bool dso__build_id_mismatch(struct dso *dso, const char *name) { - struct build_id bid; + struct build_id bid = { .size = 0, }; bool ret = false; mutex_lock(dso__lock(dso)); @@ -864,7 +872,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine, char *allocated_name = NULL; int ret = 0; - if (!dso__has_build_id(dso)) + if (!dso__has_build_id(dso) || !dso__hit(dso)) return 0; if (dso__is_kcore(dso)) { @@ -951,7 +959,10 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) void build_id__init(struct build_id *bid, const u8 *data, size_t size) { - WARN_ON(size > BUILD_ID_SIZE); + if (size > BUILD_ID_SIZE) { + pr_debug("Truncating build_id size from %zd\n", size); + size = BUILD_ID_SIZE; + } memcpy(bid->data, data, size); bid->size = size; } diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a212497bfdb0..47e621cebe1b 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -13,7 +13,7 @@ struct build_id { u8 data[BUILD_ID_SIZE]; - size_t size; + u8 size; }; struct dso; @@ -21,10 +21,10 @@ struct feat_fd; struct nsinfo; void build_id__init(struct build_id *bid, const u8 *data, size_t size); -int build_id__sprintf(const struct build_id *build_id, char *bf); +int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size); bool build_id__is_defined(const struct build_id *bid); -int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id); -int filename__sprintf_build_id(const char *pathname, char *sbuild_id); +int sysfs__snprintf_build_id(const char *root_dir, char *sbuild_id, size_t sbuild_id_size); +int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sbuild_id_size); char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, size_t size); diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c index 69d9a2bcd40b..24a0ea7e6d97 100644 --- a/tools/perf/util/cap.c +++ b/tools/perf/util/cap.c @@ -7,7 +7,6 @@ #include "debug.h" #include #include -#include #include #include diff --git a/tools/perf/util/cap.h b/tools/perf/util/cap.h index 0c6a1ff55f07..c1b8ac033ccc 100644 --- a/tools/perf/util/cap.h +++ b/tools/perf/util/cap.h @@ -3,6 +3,7 @@ #define __PERF_CAP_H #include +#include /* For older systems */ #ifndef CAP_SYSLOG @@ -13,6 +14,10 @@ #define CAP_PERFMON 38 #endif +#ifndef CAP_BPF +#define CAP_BPF 39 +#endif + /* Query if a capability is supported, used_root is set if the fallback root check was used. */ bool perf_cap__capable(int cap, bool *used_root); diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index fbcc0626f9ce..25e2769b5e74 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -413,8 +413,7 @@ static bool has_pattern_string(const char *str) return !!strpbrk(str, "{}[]()|*+?^$"); } -int evlist__expand_cgroup(struct evlist *evlist, const char *str, - struct rblist *metric_events, bool open_cgroup) +int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgroup) { struct evlist *orig_list, *tmp_list; struct evsel *pos, *evsel, *leader; @@ -440,12 +439,8 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, evlist__splice_list_tail(orig_list, &evlist->core.entries); evlist->core.nr_entries = 0; - if (metric_events) { - orig_metric_events = *metric_events; - rblist__init(metric_events); - } else { - rblist__init(&orig_metric_events); - } + orig_metric_events = evlist->metric_events; + metricgroup__rblist_init(&evlist->metric_events); if (has_pattern_string(str)) prefix_len = match_cgroups(str); @@ -490,12 +485,10 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, cgroup__put(cgrp); nr_cgroups++; - if (metric_events) { - if (metricgroup__copy_metric_events(tmp_list, cgrp, - metric_events, - &orig_metric_events) < 0) - goto out_err; - } + if (metricgroup__copy_metric_events(tmp_list, cgrp, + &evlist->metric_events, + &orig_metric_events) < 0) + goto out_err; evlist__splice_list_tail(evlist, &tmp_list->core.entries); tmp_list->core.nr_entries = 0; @@ -512,7 +505,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, out_err: evlist__delete(orig_list); evlist__delete(tmp_list); - rblist__exit(&orig_metric_events); + metricgroup__rblist_exit(&orig_metric_events); release_cgroup_list(); return ret; diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h index de8882d6e8d3..7b1bda22878c 100644 --- a/tools/perf/util/cgroup.h +++ b/tools/perf/util/cgroup.h @@ -28,8 +28,7 @@ struct rblist; struct cgroup *cgroup__new(const char *name, bool do_open); struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name); -int evlist__expand_cgroup(struct evlist *evlist, const char *cgroups, - struct rblist *metric_events, bool open_cgroup); +int evlist__expand_cgroup(struct evlist *evlist, const char *cgroups, bool open_cgroup); void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index 8aa456d7c2cd..9880247a2c33 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -24,6 +24,7 @@ static struct comm_strs { static void comm_strs__remove_if_last(struct comm_str *cs); static void comm_strs__init(void) + NO_THREAD_SAFETY_ANALYSIS /* Inherently single threaded due to pthread_once. */ { init_rwsem(&_comm_strs.lock); _comm_strs.capacity = 16; @@ -119,6 +120,7 @@ static void comm_strs__remove_if_last(struct comm_str *cs) } static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str) + SHARED_LOCKS_REQUIRED(comm_strs->lock) { struct comm_str **result; diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 5e7ff09fbc95..3d2e437e1354 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1338,14 +1338,14 @@ static void cleanup_events(struct perf_session *session) static int setup_streams(struct ctf_writer *cw, struct perf_session *session) { struct ctf_stream **stream; - struct perf_header *ph = &session->header; + struct perf_env *env = perf_session__env(session); int ncpus; /* * Try to get the number of cpus used in the data file, * if not present fallback to the MAX_CPUS. */ - ncpus = ph->env.nr_cpus_avail ?: MAX_CPUS; + ncpus = env->nr_cpus_avail ?: MAX_CPUS; stream = zalloc(sizeof(*stream) * ncpus); if (!stream) { @@ -1371,7 +1371,7 @@ static void free_streams(struct ctf_writer *cw) static int ctf_writer__setup_env(struct ctf_writer *cw, struct perf_session *session) { - struct perf_header *header = &session->header; + struct perf_env *env = perf_session__env(session); struct bt_ctf_writer *writer = cw->writer; #define ADD(__n, __v) \ @@ -1380,11 +1380,11 @@ do { \ return -1; \ } while (0) - ADD("host", header->env.hostname); + ADD("host", env->hostname); ADD("sysname", "Linux"); - ADD("release", header->env.os_release); - ADD("version", header->env.version); - ADD("machine", header->env.arch); + ADD("release", env->os_release); + ADD("version", env->version); + ADD("machine", env->arch); ADD("domain", "kernel"); ADD("tracer_name", "perf"); @@ -1401,7 +1401,7 @@ static int ctf_writer__setup_clock(struct ctf_writer *cw, int64_t offset = 0; if (tod) { - struct perf_env *env = &session->header.env; + struct perf_env *env = perf_session__env(session); if (!env->clock.enabled) { pr_err("Can't provide --tod time, missing clock data. " diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index d9f805bf6fb0..9dc1e184cf3c 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -257,7 +257,8 @@ static int process_sample_event(const struct perf_tool *tool, static void output_headers(struct perf_session *session, struct convert_json *c) { struct stat st; - struct perf_header *header = &session->header; + const struct perf_header *header = &session->header; + const struct perf_env *env = perf_session__env(session); int ret; int fd = perf_data__fd(session->data); int i; @@ -280,32 +281,32 @@ static void output_headers(struct perf_session *session, struct convert_json *c) output_json_key_format(out, true, 2, "data-size", "%" PRIu64, header->data_size); output_json_key_format(out, true, 2, "feat-offset", "%" PRIu64, header->feat_offset); - output_json_key_string(out, true, 2, "hostname", header->env.hostname); - output_json_key_string(out, true, 2, "os-release", header->env.os_release); - output_json_key_string(out, true, 2, "arch", header->env.arch); + output_json_key_string(out, true, 2, "hostname", env->hostname); + output_json_key_string(out, true, 2, "os-release", env->os_release); + output_json_key_string(out, true, 2, "arch", env->arch); - if (header->env.cpu_desc) - output_json_key_string(out, true, 2, "cpu-desc", header->env.cpu_desc); + if (env->cpu_desc) + output_json_key_string(out, true, 2, "cpu-desc", env->cpu_desc); - output_json_key_string(out, true, 2, "cpuid", header->env.cpuid); - output_json_key_format(out, true, 2, "nrcpus-online", "%u", header->env.nr_cpus_online); - output_json_key_format(out, true, 2, "nrcpus-avail", "%u", header->env.nr_cpus_avail); + output_json_key_string(out, true, 2, "cpuid", env->cpuid); + output_json_key_format(out, true, 2, "nrcpus-online", "%u", env->nr_cpus_online); + output_json_key_format(out, true, 2, "nrcpus-avail", "%u", env->nr_cpus_avail); - if (header->env.clock.enabled) { + if (env->clock.enabled) { output_json_key_format(out, true, 2, "clockid", - "%u", header->env.clock.clockid); + "%u", env->clock.clockid); output_json_key_format(out, true, 2, "clock-time", - "%" PRIu64, header->env.clock.clockid_ns); + "%" PRIu64, env->clock.clockid_ns); output_json_key_format(out, true, 2, "real-time", - "%" PRIu64, header->env.clock.tod_ns); + "%" PRIu64, env->clock.tod_ns); } - output_json_key_string(out, true, 2, "perf-version", header->env.version); + output_json_key_string(out, true, 2, "perf-version", env->version); output_json_key_format(out, true, 2, "cmdline", "["); - for (i = 0; i < header->env.nr_cmdline; i++) { + for (i = 0; i < env->nr_cmdline; i++) { output_json_delimiters(out, i != 0, 3); - output_json_string(c->out, header->env.cmdline_argv[i]); + output_json_string(c->out, env->cmdline_argv[i]); } output_json_format(out, false, 2, "]"); } @@ -376,8 +377,7 @@ int bt_convert__perf2json(const char *input_name, const char *output_name, fprintf(stderr, "Error creating perf session!\n"); goto err_fclose; } - - if (symbol__init(&session->header.env) < 0) { + if (symbol__init(perf_session__env(session)) < 0) { fprintf(stderr, "Symbol init error!\n"); goto err_session_delete; } diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 50f916374d87..8f52e8cefcf3 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -181,7 +181,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, if (al->map) { struct dso *dso = map__dso(al->map); - err = db_export__dso(dbe, dso, maps__machine(al->maps)); + err = db_export__dso(dbe, dso, maps__machine(thread__maps(al->thread))); if (err) return err; *dso_db_id = dso__db_id(dso); @@ -256,6 +256,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, al.map = map__get(node->ms.map); al.maps = maps__get(thread__maps(thread)); al.addr = node->ip; + al.thread = thread__get(thread); if (al.map && !al.sym) al.sym = dso__find_symbol(map__dso(al.map), al.addr); @@ -358,14 +359,18 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, }; struct thread *main_thread; struct comm *comm = NULL; - struct machine *machine; + struct machine *machine = NULL; int err; + if (thread__maps(thread)) + machine = maps__machine(thread__maps(thread)); + if (!machine) + return -1; + err = db_export__evsel(dbe, evsel); if (err) return err; - machine = maps__machine(al->maps); err = db_export__machine(dbe, machine); if (err) return err; diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index f9ef7d045c92..1dfa4d0eec4d 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -14,11 +14,19 @@ #ifdef HAVE_BACKTRACE_SUPPORT #include #endif +#include "addr_location.h" #include "color.h" -#include "event.h" #include "debug.h" +#include "env.h" +#include "event.h" +#include "machine.h" +#include "map.h" #include "print_binary.h" +#include "srcline.h" +#include "symbol.h" +#include "synthetic-events.h" #include "target.h" +#include "thread.h" #include "trace-event.h" #include "ui/helpline.h" #include "ui/ui.h" @@ -298,21 +306,66 @@ void perf_debug_setup(void) libapi_set_print(pr_warning_wrapper, pr_warning_wrapper, pr_debug_wrapper); } +void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size) +{ + /* TODO: async safety. printf, malloc, etc. aren't safe inside a signal handler. */ + pid_t pid = getpid(); + struct machine *machine; + struct thread *thread = NULL; + struct perf_env host_env; + + perf_env__init(&host_env); + machine = machine__new_live(&host_env, /*kernel_maps=*/false, pid); + + if (machine) + thread = machine__find_thread(machine, pid, pid); + +#ifdef HAVE_BACKTRACE_SUPPORT + if (!machine || !thread) { + /* + * Backtrace functions are async signal safe. Fall back on them + * if machine/thread creation fails. + */ + backtrace_symbols_fd(stackdump, stackdump_size, fileno(file)); + machine__delete(machine); + perf_env__exit(&host_env); + return; + } +#endif + + for (size_t i = 0; i < stackdump_size; i++) { + struct addr_location al; + u64 addr = (u64)(uintptr_t)stackdump[i]; + bool printed = false; + + addr_location__init(&al); + if (thread && thread__find_map(thread, PERF_RECORD_MISC_USER, addr, &al)) { + al.sym = map__find_symbol(al.map, al.addr); + if (al.sym) { + fprintf(file, " #%zd %p in %s ", i, stackdump[i], al.sym->name); + printed = true; + } + } + if (!printed) + fprintf(file, " #%zd %p ", i, stackdump[i]); + + map__fprintf_srcline(al.map, al.addr, "", file); + fprintf(file, "\n"); + addr_location__exit(&al); + } + thread__put(thread); + machine__delete(machine); + perf_env__exit(&host_env); +} + /* Obtain a backtrace and print it to stdout. */ #ifdef HAVE_BACKTRACE_SUPPORT void dump_stack(void) { - void *array[16]; - size_t size = backtrace(array, ARRAY_SIZE(array)); - char **strings = backtrace_symbols(array, size); - size_t i; + void *stackdump[32]; + size_t size = backtrace(stackdump, ARRAY_SIZE(stackdump)); - printf("Obtained %zd stack frames.\n", size); - - for (i = 0; i < size; i++) - printf("%s\n", strings[i]); - - free(strings); + __dump_stack(stdout, stackdump, size); } #else void dump_stack(void) {} diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index a4026d1fd6a3..6b737e195ce1 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -85,6 +85,7 @@ void debug_set_display_time(bool set); void perf_debug_setup(void); int perf_quiet_option(void); +void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size); void dump_stack(void); void sighandler_dump_stack(int sig); diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index b5deea7cbdf2..bb9ebd84ec2d 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -103,15 +103,19 @@ struct debuginfo *debuginfo__new(const char *path) char buf[PATH_MAX], nil = '\0'; struct dso *dso; struct debuginfo *dinfo = NULL; - struct build_id bid; + struct build_id bid = { .size = 0}; /* Try to open distro debuginfo files */ dso = dso__new(path); if (!dso) goto out; - /* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */ - if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0) + /* + * Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO. Don't block + * incase the path isn't for a regular file. + */ + assert(!dso__has_build_id(dso)); + if (filename__read_build_id(path, &bid, /*block=*/false) > 0) dso__set_build_id(dso, &bid); for (type = distro_dwarf_types; diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 8f0eb56c6fc6..b1e4919d016f 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1218,7 +1218,7 @@ int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, s char *build_id_msg = NULL; if (dso__has_build_id(dso)) { - build_id__sprintf(dso__bid(dso), bf + 15); + build_id__snprintf(dso__bid(dso), bf + 15, sizeof(bf) - 15); build_id_msg = bf; } scnprintf(buf, buflen, @@ -2284,6 +2284,13 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) } } + /* FIXME: LLVM and CAPSTONE should support source code */ + if (options->annotate_src && !options->hide_src_code) { + err = symbol__disassemble_objdump(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; + } + err = -1; for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) { enum perf_disassembler dis = options->disassemblers[i]; diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index ddacef881af2..c0afcbd954f8 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -513,6 +513,7 @@ int dlfilter__do_filter_event(struct dlfilter *d, d->d_addr_al = &d_addr_al; d_sample.size = sizeof(d_sample); + d_sample.p_stage_cyc = sample->weight3; d_ip_al.size = 0; /* To indicate d_ip_al is not initialized */ d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */ @@ -526,7 +527,6 @@ int dlfilter__do_filter_event(struct dlfilter *d, ASSIGN(period); ASSIGN(weight); ASSIGN(ins_lat); - ASSIGN(p_stage_cyc); ASSIGN(transaction); ASSIGN(insn_cnt); ASSIGN(cyc_cnt); diff --git a/tools/perf/util/drm_pmu.c b/tools/perf/util/drm_pmu.c new file mode 100644 index 000000000000..988890f37ba7 --- /dev/null +++ b/tools/perf/util/drm_pmu.c @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "drm_pmu.h" +#include "counts.h" +#include "cpumap.h" +#include "debug.h" +#include "evsel.h" +#include "pmu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum drm_pmu_unit { + DRM_PMU_UNIT_BYTES, + DRM_PMU_UNIT_CAPACITY, + DRM_PMU_UNIT_CYCLES, + DRM_PMU_UNIT_HZ, + DRM_PMU_UNIT_NS, + + DRM_PMU_UNIT_MAX, +}; + +struct drm_pmu_event { + const char *name; + const char *desc; + enum drm_pmu_unit unit; +}; + +struct drm_pmu { + struct perf_pmu pmu; + struct drm_pmu_event *events; + int num_events; +}; + +static const char * const drm_pmu_unit_strs[DRM_PMU_UNIT_MAX] = { + "bytes", + "capacity", + "cycles", + "hz", + "ns", +}; + +static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] = { + "1bytes", + "1capacity", + "1cycles", + "1hz", + "1ns", +}; + +bool perf_pmu__is_drm(const struct perf_pmu *pmu) +{ + return pmu && pmu->type >= PERF_PMU_TYPE_DRM_START && + pmu->type <= PERF_PMU_TYPE_DRM_END; +} + +bool evsel__is_drm(const struct evsel *evsel) +{ + return perf_pmu__is_drm(evsel->pmu); +} + +static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, size_t line_len) +{ + struct drm_pmu *drm; + struct perf_pmu *pmu; + const char *name; + __u32 max_drm_pmu_type = 0, type; + int i = 12; + + if (line[line_len - 1] == '\n') + line[line_len - 1] = '\0'; + while (isspace(line[i])) + i++; + + line[--i] = '_'; + line[--i] = 'm'; + line[--i] = 'r'; + line[--i] = 'd'; + name = &line[i]; + + list_for_each_entry(pmu, pmus, list) { + if (!perf_pmu__is_drm(pmu)) + continue; + if (pmu->type > max_drm_pmu_type) + max_drm_pmu_type = pmu->type; + if (!strcmp(pmu->name, name)) { + /* PMU already exists. */ + return NULL; + } + } + + if (max_drm_pmu_type != 0) + type = max_drm_pmu_type + 1; + else + type = PERF_PMU_TYPE_DRM_START; + + if (type > PERF_PMU_TYPE_DRM_END) { + zfree(&drm); + pr_err("Unable to encode DRM PMU type for %s\n", name); + return NULL; + } + + drm = zalloc(sizeof(*drm)); + if (!drm) + return NULL; + + if (perf_pmu__init(&drm->pmu, type, name) != 0) { + perf_pmu__delete(&drm->pmu); + return NULL; + } + + drm->pmu.cpus = perf_cpu_map__new("0"); + if (!drm->pmu.cpus) { + perf_pmu__delete(&drm->pmu); + return NULL; + } + return drm; +} + + +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) +{ + const char *colon = strchr(line, ':'); + struct drm_pmu_event *tmp; + + if (!colon) + return -EINVAL; + + tmp = reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_event)); + if (!tmp) + return -ENOMEM; + tmp[*num_events].unit = unit; + tmp[*num_events].desc = desc; + tmp[*num_events].name = strndup(line, colon - line); + if (!tmp[*num_events].name) + return -ENOMEM; + (*num_events)++; + *events = tmp; + return 0; +} + +static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name) +{ + struct list_head *pmus = args; + char buf[640]; + struct io io; + char *line = NULL; + size_t line_len; + struct drm_pmu *drm = NULL; + struct drm_pmu_event *events = NULL; + int num_events = 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd == -1) { + /* Failed to open file, ignore. */ + return 0; + } + + while (io__getline(&io, &line, &line_len) > 0) { + if (starts_with(line, "drm-driver:")) { + drm = add_drm_pmu(pmus, line, line_len); + if (!drm) + break; + continue; + } + /* + * Note the string matching below is alphabetical, with more + * specific matches appearing before less specific. + */ + if (starts_with(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-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES, + "Busy cycles"); + continue; + } + if (starts_with(line, "drm-engine-capacity-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY, + "Engine capacity"); + continue; + } + if (starts_with(line, "drm-engine-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_NS, + "Utilization in ns"); + continue; + } + if (starts_with(line, "drm-maxfreq-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ, + "Maximum frequency"); + continue; + } + if (starts_with(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-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of resident memory buffers"); + continue; + } + if (starts_with(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-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Total busy cycles"); + continue; + } + if (starts_with(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:")) + pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line); + } + if (drm) { + drm->events = events; + drm->num_events = num_events; + list_add_tail(&drm->pmu.list, pmus); + } + free(line); + if (io.fd != -1) + close(io.fd); + return 0; +} + +void drm_pmu__exit(struct perf_pmu *pmu) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + + free(drm->events); +} + +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-")) + return false; + + for (int i = 0; i < drm->num_events; i++) { + if (!strcasecmp(drm->events[i].name, name)) + return true; + } + return false; +} + +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + + for (int i = 0; i < drm->num_events; i++) { + char encoding_buf[128]; + struct pmu_event_info info = { + .pmu = pmu, + .name = drm->events[i].name, + .alias = NULL, + .scale_unit = drm_pmu_scale_unit_strs[drm->events[i].unit], + .desc = drm->events[i].desc, + .long_desc = NULL, + .encoding_desc = encoding_buf, + .topic = "drm", + .pmu_name = pmu->name, + .event_type_desc = "DRM event", + }; + int ret; + + snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%x/", pmu->name, i); + + ret = cb(state, &info); + if (ret) + return ret; + } + return 0; +} + +size_t drm_pmu__num_events(const struct perf_pmu *pmu) +{ + const struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + + return drm->num_events; +} + +static int drm_pmu__index_for_event(const struct drm_pmu *drm, const char *name) +{ + for (int i = 0; i < drm->num_events; i++) { + if (!strcmp(drm->events[i].name, name)) + return i; + } + return -1; +} + +static int drm_pmu__config_term(const struct drm_pmu *drm, + struct perf_event_attr *attr, + struct parse_events_term *term, + struct parse_events_error *err) +{ + if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { + int i = drm_pmu__index_for_event(drm, term->config); + + if (i >= 0) { + attr->config = i; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term; + + list_for_each_entry(term, &terms->terms, list) { + if (drm_pmu__config_term(drm, attr, term, err)) + return -EINVAL; + } + + return 0; +} + +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_terms *terms, + struct perf_pmu_info *info, struct parse_events_error *err) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term = + list_first_entry(&terms->terms, struct parse_events_term, list); + + if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { + int i = drm_pmu__index_for_event(drm, term->config); + + if (i >= 0) { + info->unit = drm_pmu_unit_strs[drm->events[i].unit]; + info->scale = 1; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +struct minor_info { + unsigned int *minors; + int minors_num, minors_len; +}; + +static int for_each_drm_fdinfo_in_dir(int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), + void *args, int proc_dir, const char *pid_name, + struct minor_info *minors) +{ + char buf[256]; + DIR *fd_dir; + struct dirent *fd_entry; + int fd_dir_fd, fdinfo_dir_fd = -1; + + + scnprintf(buf, sizeof(buf), "%s/fd", pid_name); + fd_dir_fd = openat(proc_dir, buf, O_DIRECTORY); + if (fd_dir_fd == -1) + return 0; /* Presumably lost race to open. */ + fd_dir = fdopendir(fd_dir_fd); + if (!fd_dir) { + close(fd_dir_fd); + return -ENOMEM; + } + while ((fd_entry = readdir(fd_dir)) != NULL) { + struct stat stat; + unsigned int minor; + bool is_dup = false; + int ret; + + if (fd_entry->d_type != DT_LNK) + continue; + + if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) != 0) + continue; + + if ((stat.st_mode & S_IFMT) != S_IFCHR || major(stat.st_rdev) != 226) + continue; + + minor = minor(stat.st_rdev); + for (int i = 0; i < minors->minors_num; i++) { + if (minor(stat.st_rdev) == minors->minors[i]) { + is_dup = true; + break; + } + } + if (is_dup) + continue; + + if (minors->minors_num == minors->minors_len) { + unsigned int *tmp = reallocarray(minors->minors, minors->minors_len + 4, + sizeof(unsigned int)); + + if (tmp) { + minors->minors = tmp; + minors->minors_len += 4; + } + } + minors->minors[minors->minors_num++] = minor; + if (fdinfo_dir_fd == -1) { + /* Open fdinfo dir if we have a DRM fd. */ + scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name); + fdinfo_dir_fd = openat(proc_dir, buf, O_DIRECTORY); + if (fdinfo_dir_fd == -1) + continue; + } + ret = cb(args, fdinfo_dir_fd, fd_entry->d_name); + if (ret) + return ret; + } + if (fdinfo_dir_fd != -1) + close(fdinfo_dir_fd); + closedir(fd_dir); + return 0; +} + +static int for_each_drm_fdinfo(bool skip_all_duplicates, + int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), + void *args) +{ + DIR *proc_dir; + struct dirent *proc_entry; + int ret; + /* + * minors maintains an array of DRM minor device numbers seen for a pid, + * or for all pids if skip_all_duplicates is true, so that duplicates + * are ignored. + */ + struct minor_info minors = { + .minors = NULL, + .minors_num = 0, + .minors_len = 0, + }; + + proc_dir = opendir(procfs__mountpoint()); + if (!proc_dir) + return 0; + + /* Walk through the /proc directory. */ + while ((proc_entry = readdir(proc_dir)) != NULL) { + if (proc_entry->d_type != DT_DIR || + !isdigit(proc_entry->d_name[0])) + continue; + if (!skip_all_duplicates) { + /* Reset the seen minor numbers for each pid. */ + minors.minors_num = 0; + } + ret = for_each_drm_fdinfo_in_dir(cb, args, + dirfd(proc_dir), proc_entry->d_name, + &minors); + if (ret) + break; + } + free(minors.minors); + closedir(proc_dir); + return ret; +} + +int perf_pmus__read_drm_pmus(struct list_head *pmus) +{ + return for_each_drm_fdinfo(/*skip_all_duplicates=*/true, read_drm_pmus_cb, pmus); +} + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx) +{ + (void)evsel; + (void)threads; + (void)start_cpu_map_idx; + (void)end_cpu_map_idx; + return 0; +} + +static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum drm_pmu_unit unit) +{ + char *unit_ptr = NULL; + uint64_t count = strtoul(count_and_unit, &unit_ptr, 10); + + if (!unit_ptr) + return 0; + + while (isblank(*unit_ptr)) + unit_ptr++; + + switch (unit) { + case DRM_PMU_UNIT_BYTES: + if (*unit_ptr == '\0') + assert(count == 0); /* Generally undocumented, happens for 0. */ + else if (!strcmp(unit_ptr, "KiB")) + count *= 1024; + else if (!strcmp(unit_ptr, "MiB")) + count *= 1024 * 1024; + else + pr_err("Unexpected bytes unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_CAPACITY: + /* No units expected. */ + break; + case DRM_PMU_UNIT_CYCLES: + /* No units expected. */ + break; + case DRM_PMU_UNIT_HZ: + if (!strcmp(unit_ptr, "Hz")) + count *= 1; + else if (!strcmp(unit_ptr, "KHz")) + count *= 1000; + else if (!strcmp(unit_ptr, "MHz")) + count *= 1000000; + else + pr_err("Unexpected hz unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_NS: + /* Only unit ns expected. */ + break; + case DRM_PMU_UNIT_MAX: + default: + break; + } + return count; +} + +static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name, + const char *match, enum drm_pmu_unit unit) +{ + char buf[640]; + struct io io; + char *line = NULL; + size_t line_len; + uint64_t count = 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd == -1) { + /* Failed to open file, ignore. */ + return 0; + } + while (io__getline(&io, &line, &line_len) > 0) { + size_t i = strlen(match); + + if (strncmp(line, match, i)) + continue; + if (line[i] != ':') + continue; + while (isblank(line[++i])) + ; + if (line[line_len - 1] == '\n') + line[line_len - 1] = '\0'; + count = read_count_and_apply_unit(&line[i], unit); + break; + } + free(line); + close(io.fd); + return count; +} + +struct read_drm_event_cb_args { + const char *match; + uint64_t count; + enum drm_pmu_unit unit; +}; + +static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *fd_name) +{ + struct read_drm_event_cb_args *args = vargs; + + args->count += read_drm_event(fdinfo_dir_fd, fd_name, args->match, args->unit); + return 0; +} + +static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evsel *evsel) +{ + struct read_drm_event_cb_args args = { + .count = 0, + .match = drm->events[evsel->core.attr.config].name, + .unit = drm->events[evsel->core.attr.config].unit, + }; + + for_each_drm_fdinfo(/*skip_all_duplicates=*/false, read_drm_event_cb, &args); + return args.count; +} + +static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *evsel, int pid) +{ + struct read_drm_event_cb_args args = { + .count = 0, + .match = drm->events[evsel->core.attr.config].name, + .unit = drm->events[evsel->core.attr.config].unit, + }; + struct minor_info minors = { + .minors = NULL, + .minors_num = 0, + .minors_len = 0, + }; + int proc_dir = open(procfs__mountpoint(), O_DIRECTORY); + char pid_name[12]; + int ret; + + if (proc_dir < 0) + return 0; + + snprintf(pid_name, sizeof(pid_name), "%d", pid); + ret = for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pid_name, &minors); + free(minors.minors); + close(proc_dir); + return ret == 0 ? args.count : 0; +} + +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) +{ + struct drm_pmu *drm = container_of(evsel->pmu, struct drm_pmu, pmu); + struct perf_counts_values *count, *old_count = NULL; + int pid = perf_thread_map__pid(evsel->core.threads, thread); + uint64_t counter; + + if (pid != -1) + counter = drm_pmu__read_for_pid(drm, evsel, pid); + else + counter = drm_pmu__read_system_wide(drm, evsel); + + if (evsel->prev_raw_counts) + old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); + + count = perf_counts(evsel->counts, cpu_map_idx, thread); + if (old_count) { + count->val = old_count->val + counter; + count->run = old_count->run + 1; + count->ena = old_count->ena + 1; + } else { + count->val = counter; + count->run++; + count->ena++; + } + return 0; +} diff --git a/tools/perf/util/drm_pmu.h b/tools/perf/util/drm_pmu.h new file mode 100644 index 000000000000..e7f366fca8a4 --- /dev/null +++ b/tools/perf/util/drm_pmu.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __DRM_PMU_H +#define __DRM_PMU_H +/* + * Linux DRM clients expose information through usage stats as documented in + * Documentation/gpu/drm-usage-stats.rst (available online at + * https://docs.kernel.org/gpu/drm-usage-stats.html). This is a tool like PMU + * that exposes DRM information. + */ + +#include "pmu.h" +#include + +struct list_head; +struct perf_thread_map; + +void drm_pmu__exit(struct perf_pmu *pmu); +bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name); +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb); +size_t drm_pmu__num_events(const struct perf_pmu *pmu); +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err); +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_terms *terms, + struct perf_pmu_info *info, struct parse_events_error *err); + + +bool perf_pmu__is_drm(const struct perf_pmu *pmu); +bool evsel__is_drm(const struct evsel *evsel); + +int perf_pmus__read_drm_pmus(struct list_head *pmus); + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx); +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread); + +#endif /* __DRM_PMU_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 057fcf4225ac..282e3af85d5a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -217,7 +217,7 @@ int dso__read_binary_type_filename(const struct dso *dso, break; } - build_id__sprintf(dso__bid_const(dso), build_id_hex); + build_id__snprintf(dso__bid(dso), build_id_hex, sizeof(build_id_hex)); len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); snprintf(filename + len, size - len, "%.2s/%s.debug", build_id_hex, build_id_hex + 2); @@ -1382,64 +1382,76 @@ static void dso__set_long_name_id(struct dso *dso, const char *name, bool name_a static int __dso_id__cmp(const struct dso_id *a, const struct dso_id *b) { - if (a->maj > b->maj) return -1; - if (a->maj < b->maj) return 1; + if (a->mmap2_valid && b->mmap2_valid) { + if (a->maj > b->maj) return -1; + if (a->maj < b->maj) return 1; - if (a->min > b->min) return -1; - if (a->min < b->min) return 1; + if (a->min > b->min) return -1; + if (a->min < b->min) return 1; - if (a->ino > b->ino) return -1; - if (a->ino < b->ino) return 1; - - /* - * Synthesized MMAP events have zero ino_generation, avoid comparing - * them with MMAP events with actual ino_generation. - * - * I found it harmful because the mismatch resulted in a new - * dso that did not have a build ID whereas the original dso did have a - * build ID. The build ID was essential because the object was not found - * otherwise. - Adrian - */ - if (a->ino_generation && b->ino_generation) { + if (a->ino > b->ino) return -1; + if (a->ino < b->ino) return 1; + } + if (a->mmap2_ino_generation_valid && b->mmap2_ino_generation_valid) { if (a->ino_generation > b->ino_generation) return -1; if (a->ino_generation < b->ino_generation) return 1; } - + if (build_id__is_defined(&a->build_id) && build_id__is_defined(&b->build_id)) { + if (a->build_id.size != b->build_id.size) + return a->build_id.size < b->build_id.size ? -1 : 1; + return memcmp(a->build_id.data, b->build_id.data, a->build_id.size); + } return 0; } -bool dso_id__empty(const struct dso_id *id) -{ - if (!id) - return true; +const struct dso_id dso_id_empty = { + { + .maj = 0, + .min = 0, + .ino = 0, + .ino_generation = 0, + }, + .mmap2_valid = false, + .mmap2_ino_generation_valid = false, + { + .size = 0, + } +}; - return !id->maj && !id->min && !id->ino && !id->ino_generation; -} - -void __dso__inject_id(struct dso *dso, const struct dso_id *id) +void __dso__improve_id(struct dso *dso, const struct dso_id *id) { struct dsos *dsos = dso__dsos(dso); struct dso_id *dso_id = dso__id(dso); + bool changed = false; /* dsos write lock held by caller. */ - dso_id->maj = id->maj; - dso_id->min = id->min; - dso_id->ino = id->ino; - dso_id->ino_generation = id->ino_generation; - - if (dsos) + if (id->mmap2_valid && !dso_id->mmap2_valid) { + dso_id->maj = id->maj; + dso_id->min = id->min; + dso_id->ino = id->ino; + dso_id->mmap2_valid = true; + changed = true; + } + if (id->mmap2_ino_generation_valid && !dso_id->mmap2_ino_generation_valid) { + dso_id->ino_generation = id->ino_generation; + dso_id->mmap2_ino_generation_valid = true; + changed = true; + } + if (build_id__is_defined(&id->build_id) && !build_id__is_defined(&dso_id->build_id)) { + dso_id->build_id = id->build_id; + changed = true; + } + if (changed && dsos) dsos->sorted = false; } int dso_id__cmp(const struct dso_id *a, const struct dso_id *b) { - /* - * The second is always dso->id, so zeroes if not set, assume passing - * NULL for a means a zeroed id - */ - if (dso_id__empty(a) || dso_id__empty(b)) + if (a == &dso_id_empty || b == &dso_id_empty) { + /* There is no valid data to compare so the comparison always returns identical. */ return 0; + } return __dso_id__cmp(a, b); } @@ -1540,7 +1552,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id) dso->loaded = 0; dso->rel = 0; dso->sorted_by_name = 0; - dso->has_build_id = 0; dso->has_srcline = 1; dso->a2l_fails = 1; dso->kernel = DSO_SPACE__USER; @@ -1612,6 +1623,10 @@ struct dso *dso__get(struct dso *dso) void dso__put(struct dso *dso) { +#ifdef REFCNT_CHECKING + if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2) + dso__data_close(dso); +#endif if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt)) dso__delete(dso); else @@ -1645,15 +1660,14 @@ int dso__swap_init(struct dso *dso, unsigned char eidata) return 0; } -void dso__set_build_id(struct dso *dso, struct build_id *bid) +void dso__set_build_id(struct dso *dso, const struct build_id *bid) { - RC_CHK_ACCESS(dso)->bid = *bid; - RC_CHK_ACCESS(dso)->has_build_id = 1; + dso__id(dso)->build_id = *bid; } -bool dso__build_id_equal(const struct dso *dso, struct build_id *bid) +bool dso__build_id_equal(const struct dso *dso, const struct build_id *bid) { - const struct build_id *dso_bid = dso__bid_const(dso); + const struct build_id *dso_bid = dso__bid(dso); if (dso_bid->size > bid->size && dso_bid->size == BUILD_ID_SIZE) { /* @@ -1672,18 +1686,20 @@ bool dso__build_id_equal(const struct dso *dso, struct build_id *bid) void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) { char path[PATH_MAX]; + struct build_id bid = { .size = 0, }; if (machine__is_default_guest(machine)) return; sprintf(path, "%s/sys/kernel/notes", machine->root_dir); - if (sysfs__read_build_id(path, dso__bid(dso)) == 0) - dso__set_has_build_id(dso); + sysfs__read_build_id(path, &bid); + dso__set_build_id(dso, &bid); } int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir) { char filename[PATH_MAX]; + struct build_id bid = { .size = 0, }; /* * kernel module short names are of the form "[module]" and * we need just "module" here. @@ -1694,9 +1710,8 @@ int dso__kernel_module_get_build_id(struct dso *dso, "%s/sys/module/%.*s/notes/.note.gnu.build-id", root_dir, (int)strlen(name) - 1, name); - if (sysfs__read_build_id(filename, dso__bid(dso)) == 0) - dso__set_has_build_id(dso); - + sysfs__read_build_id(filename, &bid); + dso__set_build_id(dso, &bid); return 0; } @@ -1704,7 +1719,7 @@ static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) { char sbuild_id[SBUILD_ID_SIZE]; - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); return fprintf(fp, "%s", sbuild_id); } diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index c87564471f9b..3457d713d3c5 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -185,14 +185,33 @@ enum dso_load_errno { #define DSO__DATA_CACHE_SIZE 4096 #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) -/* - * Data about backing storage DSO, comes from PERF_RECORD_MMAP2 meta events +/** + * struct dso_id + * + * Data about backing storage DSO, comes from PERF_RECORD_MMAP2 meta events, + * reading from /proc/pid/maps or synthesis of build_ids from DSOs. Possibly + * incomplete at any particular use. */ struct dso_id { - u32 maj; - u32 min; - u64 ino; - u64 ino_generation; + /* Data related to the mmap2 event or read from /proc/pid/maps. */ + struct { + u32 maj; + u32 min; + u64 ino; + u64 ino_generation; + }; + /** @mmap2_valid: Are the maj, min and ino fields valid? */ + bool mmap2_valid; + /** + * @mmap2_ino_generation_valid: Is the ino_generation valid? Generally + * false for /proc/pid/maps mmap event. + */ + bool mmap2_ino_generation_valid; + /** + * @build_id: A possibly populated build_id. build_id__is_defined checks + * whether it is populated. + */ + struct build_id build_id; }; struct dso_cache { @@ -243,7 +262,6 @@ DECLARE_RC_STRUCT(dso) { u64 addr; struct symbol *symbol; } last_find_result; - struct build_id bid; u64 text_offset; u64 text_end; const char *short_name; @@ -276,7 +294,6 @@ DECLARE_RC_STRUCT(dso) { enum dso_swap_type needs_swap:2; bool is_kmod:1; u8 adjust_symbols:1; - u8 has_build_id:1; u8 header_build_id:1; u8 has_srcline:1; u8 hit:1; @@ -292,6 +309,9 @@ DECLARE_RC_STRUCT(dso) { }; extern struct mutex _dso__data_open_lock; +extern const struct dso_id dso_id_empty; + +int dso_id__cmp(const struct dso_id *a, const struct dso_id *b); /* dso__for_each_symbol - iterate over the symbols of given type * @@ -362,31 +382,11 @@ static inline void dso__set_auxtrace_cache(struct dso *dso, struct auxtrace_cach RC_CHK_ACCESS(dso)->auxtrace_cache = cache; } -static inline struct build_id *dso__bid(struct dso *dso) -{ - return &RC_CHK_ACCESS(dso)->bid; -} - -static inline const struct build_id *dso__bid_const(const struct dso *dso) -{ - return &RC_CHK_ACCESS(dso)->bid; -} - static inline struct dso_bpf_prog *dso__bpf_prog(struct dso *dso) { return &RC_CHK_ACCESS(dso)->bpf_prog; } -static inline bool dso__has_build_id(const struct dso *dso) -{ - return RC_CHK_ACCESS(dso)->has_build_id; -} - -static inline void dso__set_has_build_id(struct dso *dso) -{ - RC_CHK_ACCESS(dso)->has_build_id = true; -} - static inline bool dso__has_srcline(const struct dso *dso) { return RC_CHK_ACCESS(dso)->has_srcline; @@ -462,6 +462,16 @@ static inline const struct dso_id *dso__id_const(const struct dso *dso) return &RC_CHK_ACCESS(dso)->id; } +static inline const struct build_id *dso__bid(const struct dso *dso) +{ + return &dso__id_const(dso)->build_id; +} + +static inline bool dso__has_build_id(const struct dso *dso) +{ + return build_id__is_defined(dso__bid(dso)); +} + static inline struct rb_root_cached *dso__inlined_nodes(struct dso *dso) { return &RC_CHK_ACCESS(dso)->inlined_nodes; @@ -699,9 +709,6 @@ static inline void dso__set_text_offset(struct dso *dso, u64 val) RC_CHK_ACCESS(dso)->text_offset = val; } -int dso_id__cmp(const struct dso_id *a, const struct dso_id *b); -bool dso_id__empty(const struct dso_id *id); - struct dso *dso__new_id(const char *name, const struct dso_id *id); struct dso *dso__new(const char *name); void dso__delete(struct dso *dso); @@ -709,7 +716,7 @@ void dso__delete(struct dso *dso); int dso__cmp_id(struct dso *a, struct dso *b); void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated); -void __dso__inject_id(struct dso *dso, const struct dso_id *id); +void __dso__improve_id(struct dso *dso, const struct dso_id *id); int dso__name_len(const struct dso *dso); @@ -739,8 +746,8 @@ void dso__sort_by_name(struct dso *dso); int dso__swap_init(struct dso *dso, unsigned char eidata); -void dso__set_build_id(struct dso *dso, struct build_id *bid); -bool dso__build_id_equal(const struct dso *dso, struct build_id *bid); +void dso__set_build_id(struct dso *dso, const struct build_id *bid); +bool dso__build_id_equal(const struct dso *dso, const struct build_id *bid); void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine); int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index 4d213017d202..64c1d65b0149 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -72,6 +72,7 @@ static int dsos__read_build_ids_cb(struct dso *dso, void *data) { struct dsos__read_build_ids_cb_args *args = data; struct nscookie nsc; + struct build_id bid = { .size = 0, }; if (args->with_hits && !dso__hit(dso) && !dso__is_vdso(dso)) return 0; @@ -80,15 +81,15 @@ static int dsos__read_build_ids_cb(struct dso *dso, void *data) return 0; } nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), dso__bid(dso)) > 0) { + if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) { + dso__set_build_id(dso, &bid); args->have_build_id = true; - dso__set_has_build_id(dso); } else if (errno == ENOENT && dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, dso__bid(dso)) > 0) { + if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) { + dso__set_build_id(dso, &bid); args->have_build_id = true; - dso__set_has_build_id(dso); } free(new_name); } @@ -286,7 +287,7 @@ struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short) struct dso *res; down_read(&dsos->lock); - res = __dsos__find_id(dsos, name, NULL, cmp_short, /*write_locked=*/false); + res = __dsos__find_id(dsos, name, &dso_id_empty, cmp_short, /*write_locked=*/false); up_read(&dsos->lock); return res; } @@ -344,8 +345,8 @@ static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const { struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true); - if (dso && dso_id__empty(dso__id(dso)) && !dso_id__empty(id)) - __dso__inject_id(dso, id); + if (dso) + __dso__improve_id(dso, id); return dso ? dso : __dsos__addnew_id(dsos, name, id); } @@ -373,7 +374,7 @@ static int dsos__fprintf_buildid_cb(struct dso *dso, void *data) if (args->skip && args->skip(dso, args->parm)) return 0; - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); args->ret += fprintf(args->fp, "%-40s %s\n", sbuild_id, dso__long_name(dso)); return 0; } @@ -436,7 +437,8 @@ struct dso *dsos__findnew_module_dso(struct dsos *dsos, down_write(&dsos->lock); - dso = __dsos__find_id(dsos, m->name, NULL, /*cmp_short=*/true, /*write_locked=*/true); + dso = __dsos__find_id(dsos, m->name, &dso_id_empty, /*cmp_short=*/true, + /*write_locked=*/true); if (dso) { up_write(&dsos->lock); return dso; diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 36411749e007..c8c248754621 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -3,8 +3,10 @@ #include "debug.h" #include "env.h" #include "util/header.h" -#include "linux/compiler.h" +#include "util/rwsem.h" +#include #include +#include #include #include #include "cgroup.h" @@ -17,8 +19,6 @@ #include "strbuf.h" #include "trace/beauty/beauty.h" -struct perf_env perf_env; - #ifdef HAVE_LIBBPF_SUPPORT #include "bpf-event.h" #include "bpf-utils.h" @@ -89,6 +89,20 @@ struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, return node; } +void perf_env__iterate_bpf_prog_info(struct perf_env *env, + void (*cb)(struct bpf_prog_info_node *node, + void *data), + void *data) +{ + struct rb_node *first; + + down_read(&env->bpf_progs.lock); + first = rb_first(&env->bpf_progs.infos); + for (struct rb_node *node = first; node != NULL; node = rb_next(node)) + (*cb)(rb_entry(node, struct bpf_prog_info_node, rb_node), data); + up_read(&env->bpf_progs.lock); +} + bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node) { bool ret; @@ -174,6 +188,7 @@ static void perf_env__purge_bpf(struct perf_env *env) next = rb_next(&node->rb_node); rb_erase(&node->rb_node, root); zfree(&node->info_linear); + bpf_metadata_free(node->metadata); free(node); } @@ -254,6 +269,7 @@ void perf_env__exit(struct perf_env *env) void perf_env__init(struct perf_env *env) { + memset(env, 0, sizeof(*env)); #ifdef HAVE_LIBBPF_SUPPORT env->bpf_progs.infos = RB_ROOT; env->bpf_progs.btfs = RB_ROOT; @@ -416,6 +432,116 @@ static int perf_env__read_nr_cpus_avail(struct perf_env *env) return env->nr_cpus_avail ? 0 : -ENOENT; } +static int __perf_env__read_core_pmu_caps(const struct perf_pmu *pmu, + int *nr_caps, char ***caps, + unsigned int *max_branches, + unsigned int *br_cntr_nr, + unsigned int *br_cntr_width) +{ + struct perf_pmu_caps *pcaps = NULL; + char *ptr, **tmp; + int ret = 0; + + *nr_caps = 0; + *caps = NULL; + + if (!pmu->nr_caps) + return 0; + + *caps = calloc(pmu->nr_caps, sizeof(char *)); + if (!*caps) + return -ENOMEM; + + tmp = *caps; + list_for_each_entry(pcaps, &pmu->caps, list) { + if (asprintf(&ptr, "%s=%s", pcaps->name, pcaps->value) < 0) { + ret = -ENOMEM; + goto error; + } + + *tmp++ = ptr; + + if (!strcmp(pcaps->name, "branches")) + *max_branches = atoi(pcaps->value); + else if (!strcmp(pcaps->name, "branch_counter_nr")) + *br_cntr_nr = atoi(pcaps->value); + else if (!strcmp(pcaps->name, "branch_counter_width")) + *br_cntr_width = atoi(pcaps->value); + } + *nr_caps = pmu->nr_caps; + return 0; +error: + while (tmp-- != *caps) + zfree(tmp); + zfree(caps); + *nr_caps = 0; + return ret; +} + +int perf_env__read_core_pmu_caps(struct perf_env *env) +{ + struct pmu_caps *pmu_caps; + struct perf_pmu *pmu = NULL; + int nr_pmu, i = 0, j; + int ret; + + nr_pmu = perf_pmus__num_core_pmus(); + + if (!nr_pmu) + return -ENODEV; + + if (nr_pmu == 1) { + pmu = perf_pmus__find_core_pmu(); + if (!pmu) + return -ENODEV; + ret = perf_pmu__caps_parse(pmu); + if (ret < 0) + return ret; + return __perf_env__read_core_pmu_caps(pmu, &env->nr_cpu_pmu_caps, + &env->cpu_pmu_caps, + &env->max_branches, + &env->br_cntr_nr, + &env->br_cntr_width); + } + + pmu_caps = calloc(nr_pmu, sizeof(*pmu_caps)); + if (!pmu_caps) + return -ENOMEM; + + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + if (perf_pmu__caps_parse(pmu) <= 0) + continue; + ret = __perf_env__read_core_pmu_caps(pmu, &pmu_caps[i].nr_caps, + &pmu_caps[i].caps, + &pmu_caps[i].max_branches, + &pmu_caps[i].br_cntr_nr, + &pmu_caps[i].br_cntr_width); + if (ret) + goto error; + + pmu_caps[i].pmu_name = strdup(pmu->name); + if (!pmu_caps[i].pmu_name) { + ret = -ENOMEM; + goto error; + } + i++; + } + + env->nr_pmus_with_caps = nr_pmu; + env->pmu_caps = pmu_caps; + + return 0; +error: + for (i = 0; i < nr_pmu; i++) { + for (j = 0; j < pmu_caps[i].nr_caps; j++) + zfree(&pmu_caps[i].caps[j]); + zfree(&pmu_caps[i].caps); + zfree(&pmu_caps[i].pmu_name); + } + zfree(&pmu_caps); + return ret; +} + const char *perf_env__raw_arch(struct perf_env *env) { return env && !perf_env__read_arch(env) ? env->arch : "unknown"; diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index d90e343cf1fa..e00179787a34 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -150,8 +150,7 @@ enum perf_compress_type { struct bpf_prog_info_node; struct btf_node; -extern struct perf_env perf_env; - +int perf_env__read_core_pmu_caps(struct perf_env *env); void perf_env__exit(struct perf_env *env); int perf_env__kernel_is_64_bit(struct perf_env *env); @@ -174,16 +173,22 @@ const char *perf_env__raw_arch(struct perf_env *env); int perf_env__nr_cpus_avail(struct perf_env *env); void perf_env__init(struct perf_env *env); +#ifdef HAVE_LIBBPF_SUPPORT bool __perf_env__insert_bpf_prog_info(struct perf_env *env, struct bpf_prog_info_node *info_node); bool perf_env__insert_bpf_prog_info(struct perf_env *env, struct bpf_prog_info_node *info_node); struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, __u32 prog_id); +void perf_env__iterate_bpf_prog_info(struct perf_env *env, + void (*cb)(struct bpf_prog_info_node *node, + void *data), + void *data); bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); bool __perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); struct btf_node *__perf_env__find_btf(struct perf_env *env, __u32 btf_id); +#endif // HAVE_LIBBPF_SUPPORT int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu); char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 7544a3104e21..fcf44149feb2 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,9 +1,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -78,6 +81,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_COMPRESSED] = "COMPRESSED", [PERF_RECORD_FINISHED_INIT] = "FINISHED_INIT", [PERF_RECORD_COMPRESSED2] = "COMPRESSED2", + [PERF_RECORD_BPF_METADATA] = "BPF_METADATA", }; const char *perf_event__name(unsigned int id) @@ -330,7 +334,7 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) build_id__init(&bid, event->mmap2.build_id, event->mmap2.build_id_size); - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 " <%s>]: %c%c%c%c %s\n", @@ -505,6 +509,20 @@ size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp) event->bpf.type, event->bpf.flags, event->bpf.id); } +size_t perf_event__fprintf_bpf_metadata(union perf_event *event, FILE *fp) +{ + struct perf_record_bpf_metadata *metadata = &event->bpf_metadata; + size_t ret; + + ret = fprintf(fp, " prog %s\n", metadata->prog_name); + for (__u32 i = 0; i < metadata->nr_entries; i++) { + ret += fprintf(fp, " entry %d: %20s = %s\n", i, + metadata->entries[i].key, + metadata->entries[i].value); + } + return ret; +} + static int text_poke_printer(enum binary_printer_ops op, unsigned int val, void *extra, FILE *fp) { @@ -602,6 +620,9 @@ size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FIL case PERF_RECORD_AUX_OUTPUT_HW_ID: ret += perf_event__fprintf_aux_output_hw_id(event, fp); break; + case PERF_RECORD_BPF_METADATA: + ret += perf_event__fprintf_bpf_metadata(event, fp); + break; default: ret += fprintf(fp, "\n"); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 664bf39567ce..e40d16d3246c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -370,6 +370,7 @@ size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp); size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp); size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp); size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_bpf_metadata(union perf_event *event, FILE *fp); size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *machine,FILE *fp); size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FILE *fp); @@ -390,11 +391,6 @@ extern unsigned int proc_map_timeout; #define PAGE_SIZE_NAME_LEN 32 char *get_page_size_name(u64 size, char *str); -void arch_perf_parse_sample_weight(struct perf_sample *data, const __u64 *array, u64 type); -void arch_perf_synthesize_sample_weight(const struct perf_sample *data, __u64 *array, u64 type); -const char *arch_perf_header_entry(const char *se_header); -int arch_support_sort_key(const char *sort_key); - static inline bool perf_event_header__cpumode_is_guest(u8 cpumode) { return cpumode == PERF_RECORD_MISC_GUEST_KERNEL || diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index dcd1130502df..80d8387e6b97 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -35,6 +35,7 @@ #include "util/util.h" #include "util/env.h" #include "util/intel-tpebs.h" +#include "util/metricgroup.h" #include "util/strbuf.h" #include #include @@ -83,6 +84,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, evlist->ctl_fd.ack = -1; evlist->ctl_fd.pos = -1; evlist->nr_br_cntr = -1; + metricgroup__rblist_init(&evlist->metric_events); } struct evlist *evlist__new(void) @@ -173,6 +175,7 @@ static void evlist__purge(struct evlist *evlist) void evlist__exit(struct evlist *evlist) { + metricgroup__rblist_exit(&evlist->metric_events); event_enable_timer__exit(&evlist->eet); zfree(&evlist->mmap); zfree(&evlist->overwrite_mmap); @@ -1006,8 +1009,7 @@ int evlist__create_maps(struct evlist *evlist, struct target *target) * per-thread data. thread_map__new_str will call * thread_map__new_all_cpus to enumerate all threads. */ - threads = thread_map__new_str(target->pid, target->tid, target->uid, - all_threads); + threads = thread_map__new_str(target->pid, target->tid, all_threads); if (!threads) return -1; @@ -2547,20 +2549,7 @@ void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_lis return; evlist__for_each_entry(evlist, pos) { - struct perf_cpu_map *intersect, *to_test, *online = cpu_map__online(); - const struct perf_pmu *pmu = evsel__find_pmu(pos); - - to_test = pmu && pmu->is_core ? pmu->cpus : online; - intersect = perf_cpu_map__intersect(to_test, user_requested_cpus); - if (!perf_cpu_map__equal(intersect, user_requested_cpus)) { - char buf[128]; - - cpu_map__snprint(to_test, buf, sizeof(buf)); - pr_warning("WARNING: A requested CPU in '%s' is not supported by PMU '%s' (CPUs %s) for event '%s'\n", - cpu_list, pmu ? pmu->name : "cpu", buf, evsel__name(pos)); - } - perf_cpu_map__put(intersect); - perf_cpu_map__put(online); + evsel__warn_user_requested_cpus(pos, user_requested_cpus); } perf_cpu_map__put(user_requested_cpus); } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 85859708393e..5e71e3dc6042 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -12,6 +12,7 @@ #include #include "events_stats.h" #include "evsel.h" +#include "rblist.h" #include #include #include @@ -70,7 +71,7 @@ struct evlist { struct mmap *overwrite_mmap; struct evsel *selected; struct events_stats stats; - struct perf_env *env; + struct perf_session *session; void (*trace_event_sample_raw)(struct evlist *evlist, union perf_event *event, struct perf_sample *sample); @@ -86,6 +87,11 @@ struct evlist { int pos; /* index at evlist core object to check signals */ } ctl_fd; struct event_enable_timer *eet; + /** + * @metric_events: A list of struct metric_event which each have a list + * of struct metric_expr. + */ + struct rblist metric_events; }; struct evsel_str_handler { @@ -105,6 +111,7 @@ void evlist__add(struct evlist *evlist, struct evsel *entry); void evlist__remove(struct evlist *evlist, struct evsel *evsel); int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs); +int arch_evlist__add_required_events(struct list_head *list); int evlist__add_dummy(struct evlist *evlist); struct evsel *evlist__add_aux_dummy(struct evlist *evlist, bool system_wide); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d55482f094bf..d264c143b592 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -48,6 +48,7 @@ #include "record.h" #include "debug.h" #include "trace-event.h" +#include "session.h" #include "stat.h" #include "string2.h" #include "memswap.h" @@ -56,8 +57,10 @@ #include "off_cpu.h" #include "pmu.h" #include "pmus.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "tool_pmu.h" +#include "tp_pmu.h" #include "rlimit.h" #include "../perf-sys.h" #include "util/parse-branch-options.h" @@ -487,7 +490,7 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig) return NULL; evsel->core.cpus = perf_cpu_map__get(orig->core.cpus); - evsel->core.own_cpus = perf_cpu_map__get(orig->core.own_cpus); + evsel->core.pmu_cpus = perf_cpu_map__get(orig->core.pmu_cpus); evsel->core.threads = perf_thread_map__get(orig->core.threads); evsel->core.nr_members = orig->core.nr_members; evsel->core.system_wide = orig->core.system_wide; @@ -570,24 +573,6 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig) return NULL; } -static int trace_event__id(const char *sys, const char *name) -{ - char *tp_dir = get_events_file(sys); - char path[PATH_MAX]; - int id, err; - - if (!tp_dir) - return -1; - - scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name); - put_events_file(tp_dir); - err = filename__read_int(path, &id); - if (err) - return err; - - return id; -} - /* * Returns pointer with encoded error via interface. */ @@ -621,7 +606,7 @@ struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx, bool event_attr_init(&attr); if (format) { - id = trace_event__id(sys, name); + id = tp_pmu__id(sys, name); if (id < 0) { err = id; goto out_free; @@ -1526,7 +1511,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, attr->exclude_user = 1; } - if (evsel->core.own_cpus || evsel->unit) + if (evsel->core.pmu_cpus || evsel->unit) evsel->core.attr.read_format |= PERF_FORMAT_ID; /* @@ -1656,6 +1641,15 @@ static void evsel__free_config_terms(struct evsel *evsel) free_config_terms(&evsel->config_terms); } +static void (*evsel__priv_destructor)(void *priv); + +void evsel__set_priv_destructor(void (*destructor)(void *priv)) +{ + assert(evsel__priv_destructor == NULL); + + evsel__priv_destructor = destructor; +} + void evsel__exit(struct evsel *evsel) { assert(list_empty(&evsel->core.node)); @@ -1669,9 +1663,7 @@ void evsel__exit(struct evsel *evsel) perf_evsel__free_id(&evsel->core); evsel__free_config_terms(evsel); cgroup__put(evsel->cgrp); - perf_cpu_map__put(evsel->core.cpus); - perf_cpu_map__put(evsel->core.own_cpus); - perf_thread_map__put(evsel->core.threads); + perf_evsel__exit(&evsel->core); zfree(&evsel->group_name); zfree(&evsel->name); #ifdef HAVE_LIBTRACEEVENT @@ -1686,6 +1678,8 @@ void evsel__exit(struct evsel *evsel) hashmap__free(evsel->per_pkg_mask); evsel->per_pkg_mask = NULL; zfree(&evsel->metric_events); + if (evsel__priv_destructor) + evsel__priv_destructor(evsel->priv); perf_evsel__object.fini(evsel); if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME || evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) @@ -1889,6 +1883,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread) if (evsel__is_hwmon(evsel)) return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread); + if (evsel__is_drm(evsel)) + return evsel__drm_pmu_read(evsel, cpu_map_idx, thread); + if (evsel__is_retire_lat(evsel)) return evsel__tpebs_read(evsel, cpu_map_idx, thread); @@ -2610,6 +2607,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, start_cpu_map_idx, end_cpu_map_idx); } + if (evsel__is_drm(evsel)) { + return evsel__drm_pmu_open(evsel, threads, + start_cpu_map_idx, + end_cpu_map_idx); + } for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { cpu = perf_cpu_map__cpu(cpus, idx); @@ -2743,17 +2745,32 @@ void evsel__close(struct evsel *evsel) perf_evsel__free_id(&evsel->core); } -int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx) +int evsel__open_per_cpu_and_thread(struct evsel *evsel, + struct perf_cpu_map *cpus, int cpu_map_idx, + struct perf_thread_map *threads) { if (cpu_map_idx == -1) - return evsel__open_cpu(evsel, cpus, NULL, 0, perf_cpu_map__nr(cpus)); + return evsel__open_cpu(evsel, cpus, threads, 0, perf_cpu_map__nr(cpus)); - return evsel__open_cpu(evsel, cpus, NULL, cpu_map_idx, cpu_map_idx + 1); + return evsel__open_cpu(evsel, cpus, threads, cpu_map_idx, cpu_map_idx + 1); +} + +int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx) +{ + struct perf_thread_map *threads = thread_map__new_by_tid(-1); + int ret = evsel__open_per_cpu_and_thread(evsel, cpus, cpu_map_idx, threads); + + perf_thread_map__put(threads); + return ret; } int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads) { - return evsel__open(evsel, NULL, threads); + struct perf_cpu_map *cpus = perf_cpu_map__new_any_cpu(); + int ret = evsel__open_per_cpu_and_thread(evsel, cpus, -1, threads); + + perf_cpu_map__put(cpus); + return ret; } static int perf_evsel__parse_id_sample(const struct evsel *evsel, @@ -2846,11 +2863,18 @@ perf_event__check_size(union perf_event *event, unsigned int sample_size) return 0; } -void __weak arch_perf_parse_sample_weight(struct perf_sample *data, - const __u64 *array, - u64 type __maybe_unused) +static void perf_parse_sample_weight(struct perf_sample *data, const __u64 *array, u64 type) { - data->weight = *array; + union perf_sample_weight weight; + + weight.full = *array; + if (type & PERF_SAMPLE_WEIGHT_STRUCT) { + data->weight = weight.var1_dw; + data->ins_lat = weight.var2_w; + data->weight3 = weight.var3_w; + } else { + data->weight = weight.full; + } } u64 evsel__bitfield_swap_branch_flags(u64 value) @@ -3236,7 +3260,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, if (type & PERF_SAMPLE_WEIGHT_TYPE) { OVERFLOW_CHECK_u64(array); - arch_perf_parse_sample_weight(data, array, type); + perf_parse_sample_weight(data, array, type); array++; } @@ -3839,11 +3863,16 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel)); } +struct perf_session *evsel__session(struct evsel *evsel) +{ + return evsel && evsel->evlist ? evsel->evlist->session : NULL; +} + struct perf_env *evsel__env(struct evsel *evsel) { - if (evsel && evsel->evlist && evsel->evlist->env) - return evsel->evlist->env; - return &perf_env; + struct perf_session *session = evsel__session(evsel); + + return session ? perf_session__env(session) : NULL; } static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist) @@ -4071,3 +4100,27 @@ void evsel__uniquify_counter(struct evsel *counter) counter->uniquified_name = false; } } + +void evsel__warn_user_requested_cpus(struct evsel *evsel, struct perf_cpu_map *user_requested_cpus) +{ + struct perf_cpu_map *intersect, *online = NULL; + const struct perf_pmu *pmu = evsel__find_pmu(evsel); + + if (pmu && pmu->is_core) { + intersect = perf_cpu_map__intersect(pmu->cpus, user_requested_cpus); + } else { + online = cpu_map__online(); + intersect = perf_cpu_map__intersect(online, user_requested_cpus); + } + if (!perf_cpu_map__equal(intersect, user_requested_cpus)) { + char buf1[128]; + char buf2[128]; + + cpu_map__snprint(user_requested_cpus, buf1, sizeof(buf1)); + cpu_map__snprint(online ?: pmu->cpus, buf2, sizeof(buf2)); + pr_warning("WARNING: A requested CPU in '%s' is not supported by PMU '%s' (CPUs %s) for event '%s'\n", + buf1, pmu ? pmu->name : "cpu", buf2, evsel__name(evsel)); + } + perf_cpu_map__put(intersect); + perf_cpu_map__put(online); +} diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 6dbc9690e0c9..5797a02e5d6a 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -280,6 +280,8 @@ void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx); void evsel__exit(struct evsel *evsel); void evsel__delete(struct evsel *evsel); +void evsel__set_priv_destructor(void (*destructor)(void *priv)); + struct callchain_param; void evsel__config(struct evsel *evsel, struct record_opts *opts, @@ -349,6 +351,9 @@ int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); int evsel__disable_cpu(struct evsel *evsel, int cpu_map_idx); +int evsel__open_per_cpu_and_thread(struct evsel *evsel, + struct perf_cpu_map *cpus, int cpu_map_idx, + struct perf_thread_map *threads); int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx); int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads); int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, @@ -537,6 +542,7 @@ static inline bool evsel__is_dummy_event(struct evsel *evsel) (evsel->core.attr.config == PERF_COUNT_SW_DUMMY); } +struct perf_session *evsel__session(struct evsel *evsel); struct perf_env *evsel__env(struct evsel *evsel); int evsel__store_ids(struct evsel *evsel, struct evlist *evlist); @@ -572,4 +578,6 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, bool evsel__is_offcpu_event(struct evsel *evsel); +void evsel__warn_user_requested_cpus(struct evsel *evsel, struct perf_cpu_map *user_requested_cpus); + #endif /* __PERF_EVSEL_H */ diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index 6413537442aa..7fda0ff89c16 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -166,8 +166,12 @@ int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, data_ptr->kind = EXPR_ID_DATA__VALUE; ret = hashmap__set(ctx->ids, id, data_ptr, &old_key, &old_data); - if (ret) + if (ret) { free(data_ptr); + } else if (old_data) { + data_ptr->val.val += old_data->val.val; + data_ptr->val.source_count += old_data->val.source_count; + } free(old_key); free(old_data); return ret; @@ -397,7 +401,7 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx if (ev != TOOL_PMU__EVENT_NONE) { u64 count; - if (tool_pmu__read_event(ev, &count)) + if (tool_pmu__read_event(ev, /*evsel=*/NULL, &count)) result = count; else pr_err("Failure to read '%s'", literal); diff --git a/tools/perf/util/ftrace.h b/tools/perf/util/ftrace.h index a9bc47da83a5..950f2efafad2 100644 --- a/tools/perf/util/ftrace.h +++ b/tools/perf/util/ftrace.h @@ -17,6 +17,7 @@ struct perf_ftrace { struct list_head notrace; struct list_head graph_funcs; struct list_head nograph_funcs; + struct list_head event_pair; struct hashmap *profile_hash; unsigned long percpu_buffer_size; bool inherit; @@ -29,6 +30,10 @@ struct perf_ftrace { int graph_depth; int func_stack_trace; int func_irq_info; + int graph_args; + int graph_retval; + int graph_retval_hex; + int graph_retaddr; int graph_nosleep_time; int graph_noirqs; int graph_verbose; diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index cdce7f173d00..591548b10e34 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -12,15 +12,14 @@ #include #include #include -#include #include -#include #include #ifdef HAVE_LIBDW_SUPPORT #include #endif #include "genelf.h" +#include "sha1.h" #include "../util/jitdump.h" #include @@ -28,25 +27,6 @@ #define NT_GNU_BUILD_ID 3 #endif -#define BUILD_ID_URANDOM /* different uuid for each run */ - -#ifdef HAVE_LIBCRYPTO_SUPPORT - -#define BUILD_ID_MD5 -#undef BUILD_ID_SHA /* does not seem to work well when linked with Java */ -#undef BUILD_ID_URANDOM /* different uuid for each run */ - -#ifdef BUILD_ID_SHA -#include -#endif - -#ifdef BUILD_ID_MD5 -#include -#include -#endif -#endif - - typedef struct { unsigned int namesz; /* Size of entry's owner string */ unsigned int descsz; /* Size of the note descriptor */ @@ -71,7 +51,7 @@ static char shd_string_table[] = { static struct buildid_note { Elf_Note desc; /* descsz: size of build-id, must be multiple of 4 */ char name[4]; /* GNU\0 */ - char build_id[20]; + u8 build_id[SHA1_DIGEST_SIZE]; } bnote; static Elf_Sym symtab[]={ @@ -92,65 +72,6 @@ static Elf_Sym symtab[]={ } }; -#ifdef BUILD_ID_URANDOM -static void -gen_build_id(struct buildid_note *note, - unsigned long load_addr __maybe_unused, - const void *code __maybe_unused, - size_t csize __maybe_unused) -{ - int fd; - size_t sz = sizeof(note->build_id); - ssize_t sret; - - fd = open("/dev/urandom", O_RDONLY); - if (fd == -1) - err(1, "cannot access /dev/urandom for buildid"); - - sret = read(fd, note->build_id, sz); - - close(fd); - - if (sret != (ssize_t)sz) - memset(note->build_id, 0, sz); -} -#endif - -#ifdef BUILD_ID_SHA -static void -gen_build_id(struct buildid_note *note, - unsigned long load_addr __maybe_unused, - const void *code, - size_t csize) -{ - if (sizeof(note->build_id) < SHA_DIGEST_LENGTH) - errx(1, "build_id too small for SHA1"); - - SHA1(code, csize, (unsigned char *)note->build_id); -} -#endif - -#ifdef BUILD_ID_MD5 -static void -gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *code, size_t csize) -{ - EVP_MD_CTX *mdctx; - - if (sizeof(note->build_id) < 16) - errx(1, "build_id too small for MD5"); - - mdctx = EVP_MD_CTX_new(); - if (!mdctx) - errx(2, "failed to create EVP_MD_CTX"); - - EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); - EVP_DigestUpdate(mdctx, &load_addr, sizeof(load_addr)); - EVP_DigestUpdate(mdctx, code, csize); - EVP_DigestFinal_ex(mdctx, (unsigned char *)note->build_id, NULL); - EVP_MD_CTX_free(mdctx); -} -#endif - static int jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size, uint64_t base_offset) @@ -239,7 +160,7 @@ jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, * csize: the code size in bytes */ int -jit_write_elf(int fd, uint64_t load_addr, const char *sym, +jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, const void *code, int csize, void *debug __maybe_unused, int nr_debug_entries __maybe_unused, void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size) @@ -473,7 +394,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, /* * build-id generation */ - gen_build_id(&bnote, load_addr, code, csize); + sha1(code, csize, bnote.build_id); bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */ bnote.desc.descsz = sizeof(bnote.build_id); bnote.desc.type = NT_GNU_BUILD_ID; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e3cdc3b7b4ab..4f2a6e10ed5c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -557,6 +557,7 @@ static int write_event_desc(struct feat_fd *ff, static int write_cmdline(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { + struct perf_env *env = &ff->ph->env; char pbuf[MAXPATHLEN], *buf; int i, ret, n; @@ -564,7 +565,7 @@ static int write_cmdline(struct feat_fd *ff, buf = perf_exe(pbuf, MAXPATHLEN); /* account for binary path */ - n = perf_env.nr_cmdline + 1; + n = env->nr_cmdline + 1; ret = do_write(ff, &n, sizeof(n)); if (ret < 0) @@ -574,8 +575,8 @@ static int write_cmdline(struct feat_fd *ff, if (ret < 0) return ret; - for (i = 0 ; i < perf_env.nr_cmdline; i++) { - ret = do_write_string(ff, perf_env.cmdline_argv[i]); + for (i = 0 ; i < env->nr_cmdline; i++) { + ret = do_write_string(ff, env->cmdline_argv[i]); if (ret < 0) return ret; } @@ -586,6 +587,7 @@ static int write_cmdline(struct feat_fd *ff, static int write_cpu_topology(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct cpu_topology *tp; u32 i; int ret, j; @@ -613,17 +615,17 @@ static int write_cpu_topology(struct feat_fd *ff, break; } - ret = perf_env__read_cpu_topology_map(&perf_env); + ret = perf_env__read_cpu_topology_map(env); if (ret < 0) goto done; - for (j = 0; j < perf_env.nr_cpus_avail; j++) { - ret = do_write(ff, &perf_env.cpu[j].core_id, - sizeof(perf_env.cpu[j].core_id)); + for (j = 0; j < env->nr_cpus_avail; j++) { + ret = do_write(ff, &env->cpu[j].core_id, + sizeof(env->cpu[j].core_id)); if (ret < 0) return ret; - ret = do_write(ff, &perf_env.cpu[j].socket_id, - sizeof(perf_env.cpu[j].socket_id)); + ret = do_write(ff, &env->cpu[j].socket_id, + sizeof(env->cpu[j].socket_id)); if (ret < 0) return ret; } @@ -641,9 +643,9 @@ static int write_cpu_topology(struct feat_fd *ff, goto done; } - for (j = 0; j < perf_env.nr_cpus_avail; j++) { - ret = do_write(ff, &perf_env.cpu[j].die_id, - sizeof(perf_env.cpu[j].die_id)); + for (j = 0; j < env->nr_cpus_avail; j++) { + ret = do_write(ff, &env->cpu[j].die_id, + sizeof(env->cpu[j].die_id)); if (ret < 0) return ret; } @@ -1016,10 +1018,13 @@ static int write_bpf_prog_info(struct feat_fd *ff, struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; - int ret; + int ret = 0; down_read(&env->bpf_progs.lock); + if (env->bpf_progs.infos_cnt == 0) + goto out; + ret = do_write(ff, &env->bpf_progs.infos_cnt, sizeof(env->bpf_progs.infos_cnt)); if (ret < 0) @@ -1058,10 +1063,13 @@ static int write_bpf_btf(struct feat_fd *ff, struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; - int ret; + int ret = 0; down_read(&env->bpf_progs.lock); + if (env->bpf_progs.btfs_cnt == 0) + goto out; + ret = do_write(ff, &env->bpf_progs.btfs_cnt, sizeof(env->bpf_progs.btfs_cnt)); @@ -1814,6 +1822,9 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) root = &env->bpf_progs.infos; next = rb_first(root); + if (!next) + printf("# bpf_prog_info empty\n"); + while (next) { struct bpf_prog_info_node *node; @@ -1838,6 +1849,9 @@ static void print_bpf_btf(struct feat_fd *ff, FILE *fp) root = &env->bpf_progs.btfs; next = rb_first(root); + if (!next) + printf("# btf info empty\n"); + while (next) { struct btf_node *node; @@ -2111,17 +2125,18 @@ static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) static void print_pmu_caps(struct feat_fd *ff, FILE *fp) { + struct perf_env *env = &ff->ph->env; struct pmu_caps *pmu_caps; - for (int i = 0; i < ff->ph->env.nr_pmus_with_caps; i++) { - pmu_caps = &ff->ph->env.pmu_caps[i]; + for (int i = 0; i < env->nr_pmus_with_caps; i++) { + pmu_caps = &env->pmu_caps[i]; __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps, pmu_caps->pmu_name); } - if (strcmp(perf_env__arch(&ff->ph->env), "x86") == 0 && - perf_env__has_pmu_mapping(&ff->ph->env, "ibs_op")) { - char *max_precise = perf_env__find_pmu_cap(&ff->ph->env, "cpu", "max_precise"); + if (strcmp(perf_env__arch(env), "x86") == 0 && + perf_env__has_pmu_mapping(env, "ibs_op")) { + char *max_precise = perf_env__find_pmu_cap(env, "cpu", "max_precise"); if (max_precise != NULL && atoi(max_precise) == 0) fprintf(fp, "# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details.\n"); @@ -2130,18 +2145,19 @@ static void print_pmu_caps(struct feat_fd *ff, FILE *fp) static void print_pmu_mappings(struct feat_fd *ff, FILE *fp) { + struct perf_env *env = &ff->ph->env; const char *delimiter = "# pmu mappings: "; char *str, *tmp; u32 pmu_num; u32 type; - pmu_num = ff->ph->env.nr_pmu_mappings; + pmu_num = env->nr_pmu_mappings; if (!pmu_num) { fprintf(fp, "# pmu mappings: not available\n"); return; } - str = ff->ph->env.pmu_mappings; + str = env->pmu_mappings; while (pmu_num) { type = strtoul(str, &tmp, 0); @@ -2223,17 +2239,18 @@ static void memory_node__fprintf(struct memory_node *n, static void print_mem_topology(struct feat_fd *ff, FILE *fp) { + struct perf_env *env = &ff->ph->env; struct memory_node *nodes; int i, nr; - nodes = ff->ph->env.memory_nodes; - nr = ff->ph->env.nr_memory_nodes; + nodes = env->memory_nodes; + nr = env->nr_memory_nodes; fprintf(fp, "# memory nodes (nr %d, block size 0x%llx):\n", - nr, ff->ph->env.memory_bsize); + nr, env->memory_bsize); for (i = 0; i < nr; i++) { - memory_node__fprintf(&nodes[i], ff->ph->env.memory_bsize, fp); + memory_node__fprintf(&nodes[i], env->memory_bsize, fp); } } @@ -2291,7 +2308,7 @@ static int __event_process_build_id(struct perf_record_header_build_id *bev, free(m.name); } - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); pr_debug("build id event received for %s: %s [%zu]\n", dso__long_name(dso), sbuild_id, size); dso__put(dso); @@ -2431,6 +2448,7 @@ static int process_build_id(struct feat_fd *ff, void *data __maybe_unused) static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; int ret; u32 nr_cpus_avail, nr_cpus_online; @@ -2441,20 +2459,21 @@ static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused) ret = do_read_u32(ff, &nr_cpus_online); if (ret) return ret; - ff->ph->env.nr_cpus_avail = (int)nr_cpus_avail; - ff->ph->env.nr_cpus_online = (int)nr_cpus_online; + env->nr_cpus_avail = (int)nr_cpus_avail; + env->nr_cpus_online = (int)nr_cpus_online; return 0; } static int process_total_mem(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; u64 total_mem; int ret; ret = do_read_u64(ff, &total_mem); if (ret) return -1; - ff->ph->env.total_mem = (unsigned long long)total_mem; + env->total_mem = (unsigned long long)total_mem; return 0; } @@ -2515,13 +2534,14 @@ process_event_desc(struct feat_fd *ff, void *data __maybe_unused) static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; char *str, *cmdline = NULL, **argv = NULL; u32 nr, i, len = 0; if (do_read_u32(ff, &nr)) return -1; - ff->ph->env.nr_cmdline = nr; + env->nr_cmdline = nr; cmdline = zalloc(ff->size + nr + 1); if (!cmdline) @@ -2541,8 +2561,8 @@ static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused) len += strlen(str) + 1; free(str); } - ff->ph->env.cmdline = cmdline; - ff->ph->env.cmdline_argv = (const char **) argv; + env->cmdline = cmdline; + env->cmdline_argv = (const char **) argv; return 0; error: @@ -2556,19 +2576,18 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) u32 nr, i; char *str = NULL; struct strbuf sb; - int cpu_nr = ff->ph->env.nr_cpus_avail; + struct perf_env *env = &ff->ph->env; + int cpu_nr = env->nr_cpus_avail; u64 size = 0; - struct perf_header *ph = ff->ph; - bool do_core_id_test = true; - ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu)); - if (!ph->env.cpu) + env->cpu = calloc(cpu_nr, sizeof(*env->cpu)); + if (!env->cpu) return -1; if (do_read_u32(ff, &nr)) goto free_cpu; - ph->env.nr_sibling_cores = nr; + env->nr_sibling_cores = nr; size += sizeof(u32); if (strbuf_init(&sb, 128) < 0) goto free_cpu; @@ -2584,12 +2603,12 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) size += string_size(str); zfree(&str); } - ph->env.sibling_cores = strbuf_detach(&sb, NULL); + env->sibling_cores = strbuf_detach(&sb, NULL); if (do_read_u32(ff, &nr)) return -1; - ph->env.nr_sibling_threads = nr; + env->nr_sibling_threads = nr; size += sizeof(u32); for (i = 0; i < nr; i++) { @@ -2603,43 +2622,28 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) size += string_size(str); zfree(&str); } - ph->env.sibling_threads = strbuf_detach(&sb, NULL); + env->sibling_threads = strbuf_detach(&sb, NULL); /* * The header may be from old perf, * which doesn't include core id and socket id information. */ if (ff->size <= size) { - zfree(&ph->env.cpu); + zfree(&env->cpu); return 0; } - /* On s390 the socket_id number is not related to the numbers of cpus. - * The socket_id number might be higher than the numbers of cpus. - * This depends on the configuration. - * AArch64 is the same. - */ - if (ph->env.arch && (!strncmp(ph->env.arch, "s390", 4) - || !strncmp(ph->env.arch, "aarch64", 7))) - do_core_id_test = false; - for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu; - ph->env.cpu[i].core_id = nr; + env->cpu[i].core_id = nr; size += sizeof(u32); if (do_read_u32(ff, &nr)) goto free_cpu; - if (do_core_id_test && nr != (u32)-1 && nr > (u32)cpu_nr) { - pr_debug("socket_id number is too big." - "You may need to upgrade the perf tool.\n"); - goto free_cpu; - } - - ph->env.cpu[i].socket_id = nr; + env->cpu[i].socket_id = nr; size += sizeof(u32); } @@ -2653,7 +2657,7 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr)) return -1; - ph->env.nr_sibling_dies = nr; + env->nr_sibling_dies = nr; size += sizeof(u32); for (i = 0; i < nr; i++) { @@ -2667,13 +2671,13 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) size += string_size(str); zfree(&str); } - ph->env.sibling_dies = strbuf_detach(&sb, NULL); + env->sibling_dies = strbuf_detach(&sb, NULL); for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu; - ph->env.cpu[i].die_id = nr; + env->cpu[i].die_id = nr; } return 0; @@ -2682,12 +2686,13 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) strbuf_release(&sb); zfree(&str); free_cpu: - zfree(&ph->env.cpu); + zfree(&env->cpu); return -1; } static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct numa_node *nodes, *n; u32 nr, i; char *str; @@ -2722,8 +2727,8 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused) if (!n->map) goto error; } - ff->ph->env.nr_numa_nodes = nr; - ff->ph->env.numa_nodes = nodes; + env->nr_numa_nodes = nr; + env->numa_nodes = nodes; return 0; error: @@ -2733,6 +2738,7 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused) static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; char *name; u32 pmu_num; u32 type; @@ -2746,7 +2752,7 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused) return 0; } - ff->ph->env.nr_pmu_mappings = pmu_num; + env->nr_pmu_mappings = pmu_num; if (strbuf_init(&sb, 128) < 0) return -1; @@ -2765,14 +2771,14 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused) goto error; if (!strcmp(name, "msr")) - ff->ph->env.msr_pmu_type = type; + env->msr_pmu_type = type; free(name); pmu_num--; } /* AMD may set it by evlist__has_amd_ibs() from perf_session__new() */ - free(ff->ph->env.pmu_mappings); - ff->ph->env.pmu_mappings = strbuf_detach(&sb, NULL); + free(env->pmu_mappings); + env->pmu_mappings = strbuf_detach(&sb, NULL); return 0; error: @@ -2782,6 +2788,7 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused) static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; size_t ret = -1; u32 i, nr, nr_groups; struct perf_session *session; @@ -2795,7 +2802,7 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr_groups)) return -1; - ff->ph->env.nr_groups = nr_groups; + env->nr_groups = nr_groups; if (!nr_groups) { pr_debug("group desc not available\n"); return 0; @@ -2879,6 +2886,7 @@ static int process_auxtrace(struct feat_fd *ff, void *data __maybe_unused) static int process_cache(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct cpu_cache_level *caches; u32 cnt, i, version; @@ -2919,8 +2927,8 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused) #undef _R } - ff->ph->env.caches = caches; - ff->ph->env.caches_cnt = cnt; + env->caches = caches; + env->caches_cnt = cnt; return 0; out_free_caches: for (i = 0; i < cnt; i++) { @@ -2956,6 +2964,7 @@ static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused) static int process_mem_topology(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct memory_node *nodes; u64 version, i, nr, bsize; int ret = -1; @@ -2994,9 +3003,9 @@ static int process_mem_topology(struct feat_fd *ff, nodes[i] = n; } - ff->ph->env.memory_bsize = bsize; - ff->ph->env.memory_nodes = nodes; - ff->ph->env.nr_memory_nodes = nr; + env->memory_bsize = bsize; + env->memory_nodes = nodes; + env->nr_memory_nodes = nr; ret = 0; out: @@ -3008,7 +3017,9 @@ static int process_mem_topology(struct feat_fd *ff, static int process_clockid(struct feat_fd *ff, void *data __maybe_unused) { - if (do_read_u64(ff, &ff->ph->env.clock.clockid_res_ns)) + struct perf_env *env = &ff->ph->env; + + if (do_read_u64(ff, &env->clock.clockid_res_ns)) return -1; return 0; @@ -3017,6 +3028,7 @@ static int process_clockid(struct feat_fd *ff, static int process_clock_data(struct feat_fd *ff, void *_data __maybe_unused) { + struct perf_env *env = &ff->ph->env; u32 data32; u64 data64; @@ -3031,26 +3043,27 @@ static int process_clock_data(struct feat_fd *ff, if (do_read_u32(ff, &data32)) return -1; - ff->ph->env.clock.clockid = data32; + env->clock.clockid = data32; /* TOD ref time */ if (do_read_u64(ff, &data64)) return -1; - ff->ph->env.clock.tod_ns = data64; + env->clock.tod_ns = data64; /* clockid ref time */ if (do_read_u64(ff, &data64)) return -1; - ff->ph->env.clock.clockid_ns = data64; - ff->ph->env.clock.enabled = true; + env->clock.clockid_ns = data64; + env->clock.enabled = true; return 0; } static int process_hybrid_topology(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct hybrid_node *nodes, *n; u32 nr, i; @@ -3074,8 +3087,8 @@ static int process_hybrid_topology(struct feat_fd *ff, goto error; } - ff->ph->env.nr_hybrid_nodes = nr; - ff->ph->env.hybrid_nodes = nodes; + env->nr_hybrid_nodes = nr; + env->hybrid_nodes = nodes; return 0; error: @@ -3161,6 +3174,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) /* after reading from file, translate offset to address */ bpil_offs_to_addr(info_linear); info_node->info_linear = info_linear; + info_node->metadata = NULL; if (!__perf_env__insert_bpf_prog_info(env, info_node)) { free(info_linear); free(info_node); @@ -3227,19 +3241,21 @@ static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused) static int process_compressed(struct feat_fd *ff, void *data __maybe_unused) { - if (do_read_u32(ff, &(ff->ph->env.comp_ver))) + struct perf_env *env = &ff->ph->env; + + if (do_read_u32(ff, &(env->comp_ver))) return -1; - if (do_read_u32(ff, &(ff->ph->env.comp_type))) + if (do_read_u32(ff, &(env->comp_type))) return -1; - if (do_read_u32(ff, &(ff->ph->env.comp_level))) + if (do_read_u32(ff, &(env->comp_level))) return -1; - if (do_read_u32(ff, &(ff->ph->env.comp_ratio))) + if (do_read_u32(ff, &(env->comp_ratio))) return -1; - if (do_read_u32(ff, &(ff->ph->env.comp_mmap_len))) + if (do_read_u32(ff, &(env->comp_mmap_len))) return -1; return 0; @@ -3311,19 +3327,21 @@ static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps, static int process_cpu_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) { - int ret = __process_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, - &ff->ph->env.cpu_pmu_caps, - &ff->ph->env.max_branches, - &ff->ph->env.br_cntr_nr, - &ff->ph->env.br_cntr_width); + struct perf_env *env = &ff->ph->env; + int ret = __process_pmu_caps(ff, &env->nr_cpu_pmu_caps, + &env->cpu_pmu_caps, + &env->max_branches, + &env->br_cntr_nr, + &env->br_cntr_width); - if (!ret && !ff->ph->env.cpu_pmu_caps) + if (!ret && !env->cpu_pmu_caps) pr_debug("cpu pmu capabilities not available\n"); return ret; } static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) { + struct perf_env *env = &ff->ph->env; struct pmu_caps *pmu_caps; u32 nr_pmu, i; int ret; @@ -3361,8 +3379,8 @@ static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) } } - ff->ph->env.nr_pmus_with_caps = nr_pmu; - ff->ph->env.pmu_caps = pmu_caps; + env->nr_pmus_with_caps = nr_pmu; + env->pmu_caps = pmu_caps; return 0; err: @@ -3660,6 +3678,7 @@ static int perf_session__do_write_header(struct perf_session *session, struct perf_header *header = &session->header; struct evsel *evsel; struct feat_fd ff = { + .ph = header, .fd = fd, }; u64 attr_offset = sizeof(f_header), attr_size = 0; @@ -4228,7 +4247,7 @@ int perf_session__read_header(struct perf_session *session) if (session->evlist == NULL) return -ENOMEM; - session->evlist->env = &header->env; + session->evlist->session = session; session->machines.host.env = &header->env; /* @@ -4341,12 +4360,12 @@ int perf_session__read_header(struct perf_session *session) int perf_event__process_feature(struct perf_session *session, union perf_event *event) { - const struct perf_tool *tool = session->tool; struct feat_fd ff = { .fd = 0 }; struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event; int type = fe->header.type; u64 feat = fe->feat_id; int ret = 0; + bool print = dump_trace; if (type < 0 || type >= PERF_RECORD_HEADER_MAX) { pr_warning("invalid record type %d in pipe-mode\n", type); @@ -4357,28 +4376,35 @@ int perf_event__process_feature(struct perf_session *session, return -1; } - if (!feat_ops[feat].process) - return 0; - ff.buf = (void *)fe->data; ff.size = event->header.size - sizeof(*fe); ff.ph = &session->header; - if (feat_ops[feat].process(&ff, NULL)) { + if (feat_ops[feat].process && feat_ops[feat].process(&ff, NULL)) { ret = -1; goto out; } - if (!feat_ops[feat].print || !tool->show_feat_hdr) - goto out; - - if (!feat_ops[feat].full_only || - tool->show_feat_hdr >= SHOW_FEAT_HEADER_FULL_INFO) { - feat_ops[feat].print(&ff, stdout); - } else { - fprintf(stdout, "# %s info available, use -I to display\n", - feat_ops[feat].name); + if (session->tool->show_feat_hdr) { + if (!feat_ops[feat].full_only || + session->tool->show_feat_hdr >= SHOW_FEAT_HEADER_FULL_INFO) { + print = true; + } else { + fprintf(stdout, "# %s info available, use -I to display\n", + feat_ops[feat].name); + } } + + if (dump_trace) + printf(", "); + + if (print) { + if (feat_ops[feat].print) + feat_ops[feat].print(&ff, stdout); + else + printf("# %s", feat_ops[feat].name); + } + out: free_event_desc(ff.events); return ret; @@ -4420,6 +4446,11 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) return ret; } +size_t perf_event__fprintf_attr(union perf_event *event, FILE *fp) +{ + return perf_event_attr__fprintf(fp, &event->attr.attr, __desc_attr__fprintf, NULL); +} + int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) @@ -4429,6 +4460,9 @@ int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, struct evsel *evsel; struct evlist *evlist = *pevlist; + if (dump_trace) + perf_event__fprintf_attr(event, stdout); + if (evlist == NULL) { *pevlist = evlist = evlist__new(); if (evlist == NULL) @@ -4495,8 +4529,8 @@ int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused case PERF_EVENT_UPDATE__CPUS: map = cpu_map__new_data(&ev->cpus.cpus); if (map) { - perf_cpu_map__put(evsel->core.own_cpus); - evsel->core.own_cpus = map; + perf_cpu_map__put(evsel->core.pmu_cpus); + evsel->core.pmu_cpus = map; } else pr_err("failed to get event_update cpus\n"); default: diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 5201af6305f4..d16dfceccd74 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -175,6 +175,7 @@ int perf_event__process_attr(const struct perf_tool *tool, union perf_event *eve int perf_event__process_event_update(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist); +size_t perf_event__fprintf_attr(union perf_event *event, FILE *fp); size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); #ifdef HAVE_LIBTRACEEVENT int perf_event__process_tracing_data(struct perf_session *session, diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index afc6855327ab..64ff427040c3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -829,7 +829,7 @@ __hists__add_entry(struct hists *hists, .period = sample->period, .weight1 = sample->weight, .weight2 = sample->ins_lat, - .weight3 = sample->p_stage_cyc, + .weight3 = sample->weight3, .latency = al->latency, }, .parent = sym_parent, @@ -846,7 +846,7 @@ __hists__add_entry(struct hists *hists, .time = hist_time(sample->time), .weight = sample->weight, .ins_lat = sample->ins_lat, - .p_stage_cyc = sample->p_stage_cyc, + .weight3 = sample->weight3, .simd_flags = sample->simd_flags, }, *he = hists__findnew_entry(hists, &entry, al, sample_self); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index c64254088fc7..70438d03ca9c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -255,7 +255,8 @@ struct hist_entry { u64 code_page_size; u64 weight; u64 ins_lat; - u64 p_stage_cyc; + /** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */ + u64 weight3; s32 socket; s32 cpu; int parallelism; diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index c25e7296f1c1..416dfea9ffff 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -104,7 +104,7 @@ static const char *const hwmon_units[HWMON_TYPE_MAX] = { struct hwmon_pmu { struct perf_pmu pmu; struct hashmap events; - int hwmon_dir_fd; + char *hwmon_dir; }; /** @@ -245,7 +245,7 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) return 0; /* Use openat so that the directory contents are refreshed. */ - io_dir__init(&dir, openat(pmu->hwmon_dir_fd, ".", O_CLOEXEC | O_DIRECTORY | O_RDONLY)); + io_dir__init(&dir, open(pmu->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); if (dir.dirfd < 0) return -ENOENT; @@ -283,7 +283,7 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) __set_bit(item, alarm ? value->alarm_items : value->items); if (item == HWMON_ITEM_LABEL) { char buf[128]; - int fd = openat(pmu->hwmon_dir_fd, ent->d_name, O_RDONLY); + int fd = openat(dir.dirfd, ent->d_name, O_RDONLY); ssize_t read_len; if (fd < 0) @@ -342,9 +342,10 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) return err; } -struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir, const char *sysfs_name, const char *name) +struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, const char *hwmon_dir, + const char *sysfs_name, const char *name) { - char buf[32]; + char buf[64]; struct hwmon_pmu *hwm; __u32 type = PERF_PMU_TYPE_HWMON_START + strtoul(sysfs_name + 5, NULL, 10); @@ -365,7 +366,11 @@ struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir, const cha return NULL; } - hwm->hwmon_dir_fd = hwmon_dir; + hwm->hwmon_dir = strdup(hwmon_dir); + if (!hwm->hwmon_dir) { + perf_pmu__delete(&hwm->pmu); + return NULL; + } hwm->pmu.alias_name = strdup(sysfs_name); if (!hwm->pmu.alias_name) { perf_pmu__delete(&hwm->pmu); @@ -399,7 +404,7 @@ void hwmon_pmu__exit(struct perf_pmu *pmu) free(value); } hashmap__clear(&hwm->events); - close(hwm->hwmon_dir_fd); + zfree(&hwm->hwmon_dir); } static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, size_t out_buf_len, @@ -409,6 +414,10 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si size_t bit; char buf[64]; size_t len = 0; + int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY); + + if (dir < 0) + return 0; for_each_set_bit(bit, items, HWMON_ITEM__MAX) { int fd; @@ -421,7 +430,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si key.num, hwmon_item_strs[bit], is_alarm ? "_alarm" : ""); - fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY); + fd = openat(dir, buf, O_RDONLY); if (fd > 0) { ssize_t read_len = read(fd, buf, sizeof(buf)); @@ -443,6 +452,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si close(fd); } } + close(dir); return len; } @@ -712,6 +722,7 @@ int perf_pmus__read_hwmon_pmus(struct list_head *pmus) size_t line_len; int hwmon_dir, name_fd; struct io io; + char buf2[128]; if (class_hwmon_ent->d_type != DT_LNK) continue; @@ -730,12 +741,13 @@ int perf_pmus__read_hwmon_pmus(struct list_head *pmus) close(hwmon_dir); continue; } - io__init(&io, name_fd, buf, sizeof(buf)); + io__init(&io, name_fd, buf2, sizeof(buf2)); io__getline(&io, &line, &line_len); if (line_len > 0 && line[line_len - 1] == '\n') line[line_len - 1] = '\0'; - hwmon_pmu__new(pmus, hwmon_dir, class_hwmon_ent->d_name, line); + hwmon_pmu__new(pmus, buf, class_hwmon_ent->d_name, line); close(name_fd); + close(hwmon_dir); } free(line); close(class_hwmon_dir.dirfd); @@ -753,6 +765,10 @@ int evsel__hwmon_pmu_open(struct evsel *evsel, .type_and_num = evsel->core.attr.config, }; int idx = 0, thread = 0, nthreads, err = 0; + int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY); + + if (dir < 0) + return -errno; nthreads = perf_thread_map__nr(threads); for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { @@ -763,7 +779,7 @@ int evsel__hwmon_pmu_open(struct evsel *evsel, snprintf(buf, sizeof(buf), "%s%d_input", hwmon_type_strs[key.type], key.num); - fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY); + fd = openat(dir, buf, O_RDONLY); FD(evsel, idx, thread) = fd; if (fd < 0) { err = -errno; @@ -771,6 +787,7 @@ int evsel__hwmon_pmu_open(struct evsel *evsel, } } } + close(dir); return 0; out_close: if (err) @@ -784,6 +801,7 @@ int evsel__hwmon_pmu_open(struct evsel *evsel, } thread = nthreads; } while (--idx >= 0); + close(dir); return err; } diff --git a/tools/perf/util/hwmon_pmu.h b/tools/perf/util/hwmon_pmu.h index b3329774d2b2..dc711b289ff5 100644 --- a/tools/perf/util/hwmon_pmu.h +++ b/tools/perf/util/hwmon_pmu.h @@ -135,14 +135,14 @@ bool parse_hwmon_filename(const char *filename, * hwmon_pmu__new() - Allocate and construct a hwmon PMU. * * @pmus: The list of PMUs to be added to. - * @hwmon_dir: An O_DIRECTORY file descriptor for a hwmon directory. + * @hwmon_dir: The path to a hwmon directory. * @sysfs_name: Name of the hwmon sysfs directory like hwmon0. * @name: The contents of the "name" file in the hwmon directory. * * Exposed for testing. Regular construction should happen via * perf_pmus__read_hwmon_pmus. */ -struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir, +struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, const char *hwmon_dir, const char *sysfs_name, const char *name); void hwmon_pmu__exit(struct perf_pmu *pmu); diff --git a/tools/perf/util/include/linux/linkage.h b/tools/perf/util/include/linux/linkage.h index 178b00205fe6..89979ca23c3f 100644 --- a/tools/perf/util/include/linux/linkage.h +++ b/tools/perf/util/include/linux/linkage.h @@ -132,4 +132,8 @@ SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) #endif +#ifndef SYM_PIC_ALIAS +#define SYM_PIC_ALIAS(sym) SYM_ALIAS(__pi_ ## sym, sym, SYM_T_FUNC, SYM_L_GLOBAL) +#endif + #endif /* PERF_LINUX_LINKAGE_H_ */ diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index 3b92ebf5c112..8c9aee157ec4 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -210,8 +210,8 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, * latency value will be used. Save the number of samples and the sum of * retire latency value for each event. */ - t->last = sample->retire_lat; - update_stats(&t->stats, sample->retire_lat); + t->last = sample->weight3; + update_stats(&t->stats, sample->weight3); mutex_unlock(tpebs_mtx_get()); return 0; } diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 624964f01b5f..b062b1f234b6 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -14,9 +14,9 @@ #include #include -#include "build-id.h" #include "event.h" #include "debug.h" +#include "dso.h" #include "evlist.h" #include "namespaces.h" #include "symbol.h" @@ -531,9 +531,22 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) /* * mark dso as use to generate buildid in the header */ - if (!ret) - build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); + if (!ret) { + struct dso_id dso_id = { + { + .maj = event->mmap2.maj, + .min = event->mmap2.min, + .ino = event->mmap2.ino, + .ino_generation = event->mmap2.ino_generation, + }, + .mmap2_valid = true, + .mmap2_ino_generation_valid = true, + }; + struct dso *dso = machine__findnew_dso_id(jd->machine, filename, &dso_id); + if (dso) + dso__set_hit(dso); + } out: perf_sample__exit(&sample); free(event); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 7ec12c207970..b5dd42588c91 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -129,7 +129,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return 0; } -static struct machine *__machine__new_host(bool kernel_maps) +static struct machine *__machine__new_host(struct perf_env *host_env, bool kernel_maps) { struct machine *machine = malloc(sizeof(*machine)); @@ -142,13 +142,13 @@ static struct machine *__machine__new_host(bool kernel_maps) free(machine); return NULL; } - machine->env = &perf_env; + machine->env = host_env; return machine; } -struct machine *machine__new_host(void) +struct machine *machine__new_host(struct perf_env *host_env) { - return __machine__new_host(/*kernel_maps=*/true); + return __machine__new_host(host_env, /*kernel_maps=*/true); } static int mmap_handler(const struct perf_tool *tool __maybe_unused, @@ -168,9 +168,9 @@ static int machine__init_live(struct machine *machine, pid_t pid) mmap_handler, machine, true); } -struct machine *machine__new_live(bool kernel_maps, pid_t pid) +struct machine *machine__new_live(struct perf_env *host_env, bool kernel_maps, pid_t pid) { - struct machine *machine = __machine__new_host(kernel_maps); + struct machine *machine = __machine__new_host(host_env, kernel_maps); if (!machine) return NULL; @@ -182,9 +182,9 @@ struct machine *machine__new_live(bool kernel_maps, pid_t pid) return machine; } -struct machine *machine__new_kallsyms(void) +struct machine *machine__new_kallsyms(struct perf_env *host_env) { - struct machine *machine = machine__new_host(); + struct machine *machine = machine__new_host(host_env); /* * FIXME: * 1) We should switch to machine__load_kallsyms(), i.e. not explicitly @@ -1731,21 +1731,21 @@ int machine__process_mmap2_event(struct machine *machine, { struct thread *thread; struct map *map; - struct dso_id dso_id = { - .maj = event->mmap2.maj, - .min = event->mmap2.min, - .ino = event->mmap2.ino, - .ino_generation = event->mmap2.ino_generation, - }; - struct build_id __bid, *bid = NULL; + struct dso_id dso_id = dso_id_empty; int ret = 0; if (dump_trace) perf_event__fprintf_mmap2(event, stdout); if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { - bid = &__bid; - build_id__init(bid, event->mmap2.build_id, event->mmap2.build_id_size); + build_id__init(&dso_id.build_id, event->mmap2.build_id, event->mmap2.build_id_size); + } else { + dso_id.maj = event->mmap2.maj; + dso_id.min = event->mmap2.min; + dso_id.ino = event->mmap2.ino; + dso_id.ino_generation = event->mmap2.ino_generation; + dso_id.mmap2_valid = true; + dso_id.mmap2_ino_generation_valid = true; } if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || @@ -1757,7 +1757,7 @@ int machine__process_mmap2_event(struct machine *machine, }; strlcpy(xm.name, event->mmap2.filename, KMAP_NAME_LEN); - ret = machine__process_kernel_mmap_event(machine, &xm, bid); + ret = machine__process_kernel_mmap_event(machine, &xm, &dso_id.build_id); if (ret < 0) goto out_problem; return 0; @@ -1771,7 +1771,7 @@ int machine__process_mmap2_event(struct machine *machine, map = map__new(machine, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, &dso_id, event->mmap2.prot, - event->mmap2.flags, bid, + event->mmap2.flags, event->mmap2.filename, thread); if (map == NULL) @@ -1829,8 +1829,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event prot = PROT_EXEC; map = map__new(machine, event->mmap.start, - event->mmap.len, event->mmap.pgoff, - NULL, prot, 0, NULL, event->mmap.filename, thread); + event->mmap.len, event->mmap.pgoff, + &dso_id_empty, prot, /*flags=*/0, event->mmap.filename, thread); if (map == NULL) goto out_problem_map; @@ -3192,7 +3192,7 @@ struct dso *machine__findnew_dso_id(struct machine *machine, const char *filenam struct dso *machine__findnew_dso(struct machine *machine, const char *filename) { - return machine__findnew_dso_id(machine, filename, NULL); + return machine__findnew_dso_id(machine, filename, &dso_id_empty); } char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 180b369c366c..22a42c5825fa 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -169,9 +169,9 @@ struct thread *machine__findnew_guest_code(struct machine *machine, pid_t pid); void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size); void machines__set_comm_exec(struct machines *machines, bool comm_exec); -struct machine *machine__new_host(void); -struct machine *machine__new_kallsyms(void); -struct machine *machine__new_live(bool kernel_maps, pid_t pid); +struct machine *machine__new_host(struct perf_env *host_env); +struct machine *machine__new_kallsyms(struct perf_env *host_env); +struct machine *machine__new_live(struct perf_env *host_env, bool kernel_maps, pid_t pid); int machine__init(struct machine *machine, const char *root_dir, pid_t pid); void machine__exit(struct machine *machine); void machine__delete_threads(struct machine *machine); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index d729438b7d65..b46c68c24d1c 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -120,8 +120,8 @@ static void map__init(struct map *map, u64 start, u64 end, u64 pgoff, } struct map *map__new(struct machine *machine, u64 start, u64 len, - u64 pgoff, struct dso_id *id, - u32 prot, u32 flags, struct build_id *bid, + u64 pgoff, const struct dso_id *id, + u32 prot, u32 flags, char *filename, struct thread *thread) { struct map *result; @@ -132,7 +132,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, map = zalloc(sizeof(*map)); if (ADD_RC_CHK(result, map)) { char newfilename[PATH_MAX]; - struct dso *dso, *header_bid_dso; + struct dso *dso; int anon, no_dso, vdso, android; android = is_android_lib(filename); @@ -189,16 +189,15 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, dso__set_nsinfo(dso, nsi); mutex_unlock(dso__lock(dso)); - if (build_id__is_defined(bid)) { - dso__set_build_id(dso, bid); - } else { + if (!build_id__is_defined(&id->build_id)) { /* * If the mmap event had no build ID, search for an existing dso from the * build ID header by name. Otherwise only the dso loaded at the time of * reading the header will have the build ID set and all future mmaps will * have it missing. */ - header_bid_dso = dsos__find(&machine->dsos, filename, false); + struct dso *header_bid_dso = dsos__find(&machine->dsos, filename, false); + if (header_bid_dso && dso__header_build_id(header_bid_dso)) { dso__set_build_id(dso, dso__bid(header_bid_dso)); dso__set_header_build_id(dso, 1); @@ -354,7 +353,7 @@ int map__load(struct map *map) if (dso__has_build_id(dso)) { char sbuild_id[SBUILD_ID_SIZE]; - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); pr_debug("%s with build id %s not found", name, sbuild_id); } else pr_debug("Failed to open %s", name); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4262f5a143be..9cadf533a561 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -173,11 +173,10 @@ struct thread; __map__for_each_symbol_by_name(map, sym_name, (pos), idx) struct dso_id; -struct build_id; struct map *map__new(struct machine *machine, u64 start, u64 len, - u64 pgoff, struct dso_id *id, u32 prot, u32 flags, - struct build_id *bid, char *filename, struct thread *thread); + u64 pgoff, const struct dso_id *id, u32 prot, u32 flags, + char *filename, struct thread *thread); struct map *map__new2(u64 start, struct dso *dso); void map__delete(struct map *map); struct map *map__clone(struct map *map); diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 43d35f956a33..595b83142d2c 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -103,7 +103,7 @@ static void metric_event_delete(struct rblist *rblist __maybe_unused, free(me); } -static void metricgroup__rblist_init(struct rblist *metric_events) +void metricgroup__rblist_init(struct rblist *metric_events) { rblist__init(metric_events); metric_events->node_cmp = metric_event_cmp; @@ -179,7 +179,7 @@ static void metric__watchdog_constraint_hint(const char *name, bool foot) " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); } -static bool metric__group_events(const struct pmu_metric *pm) +static bool metric__group_events(const struct pmu_metric *pm, bool metric_no_threshold) { switch (pm->event_grouping) { case MetricNoGroupEvents: @@ -191,6 +191,13 @@ static bool metric__group_events(const struct pmu_metric *pm) return false; case MetricNoGroupEventsSmt: return !smt_on(); + case MetricNoGroupEventsThresholdAndNmi: + if (metric_no_threshold) + return true; + if (!sysctl__nmi_watchdog_enabled()) + return true; + metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false); + return false; case MetricGroupEvents: default: return true; @@ -212,6 +219,7 @@ static void metric__free(struct metric *m) static struct metric *metric__new(const struct pmu_metric *pm, const char *modifier, bool metric_no_group, + bool metric_no_threshold, int runtime, const char *user_requested_cpu_list, bool system_wide) @@ -246,7 +254,7 @@ static struct metric *metric__new(const struct pmu_metric *pm, } m->pctx->sctx.runtime = runtime; m->pctx->sctx.system_wide = system_wide; - m->group_events = !metric_no_group && metric__group_events(pm); + m->group_events = !metric_no_group && metric__group_events(pm, metric_no_threshold); m->metric_refs = NULL; m->evlist = NULL; @@ -384,107 +392,6 @@ static bool match_pm_metric_or_groups(const struct pmu_metric *pm, const char *p match_metric_or_groups(pm->metric_name, metric_or_groups); } -/** struct mep - RB-tree node for building printing information. */ -struct mep { - /** nd - RB-tree element. */ - struct rb_node nd; - /** @metric_group: Owned metric group name, separated others with ';'. */ - char *metric_group; - const char *metric_name; - const char *metric_desc; - const char *metric_long_desc; - const char *metric_expr; - const char *metric_threshold; - const char *metric_unit; - const char *pmu_name; -}; - -static int mep_cmp(struct rb_node *rb_node, const void *entry) -{ - struct mep *a = container_of(rb_node, struct mep, nd); - struct mep *b = (struct mep *)entry; - int ret; - - ret = strcmp(a->metric_group, b->metric_group); - if (ret) - return ret; - - return strcmp(a->metric_name, b->metric_name); -} - -static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) -{ - struct mep *me = malloc(sizeof(struct mep)); - - if (!me) - return NULL; - - memcpy(me, entry, sizeof(struct mep)); - return &me->nd; -} - -static void mep_delete(struct rblist *rl __maybe_unused, - struct rb_node *nd) -{ - struct mep *me = container_of(nd, struct mep, nd); - - zfree(&me->metric_group); - free(me); -} - -static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, - const char *metric_name) -{ - struct rb_node *nd; - struct mep me = { - .metric_group = strdup(metric_group), - .metric_name = metric_name, - }; - nd = rblist__find(groups, &me); - if (nd) { - free(me.metric_group); - return container_of(nd, struct mep, nd); - } - rblist__add_node(groups, &me); - nd = rblist__find(groups, &me); - if (nd) - return container_of(nd, struct mep, nd); - return NULL; -} - -static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, - struct rblist *groups) -{ - const char *g; - char *omg, *mg; - - mg = strdup(pm->metric_group ?: pm->metric_name); - if (!mg) - return -ENOMEM; - omg = mg; - while ((g = strsep(&mg, ";")) != NULL) { - struct mep *me; - - g = skip_spaces(g); - if (strlen(g)) - me = mep_lookup(groups, g, pm->metric_name); - else - me = mep_lookup(groups, pm->metric_name, pm->metric_name); - - if (me) { - me->metric_desc = pm->desc; - me->metric_long_desc = pm->long_desc; - me->metric_expr = pm->metric_expr; - me->metric_threshold = pm->metric_threshold; - me->metric_unit = pm->unit; - me->pmu_name = pm->pmu; - } - } - free(omg); - - return 0; -} - struct metricgroup_iter_data { pmu_metric_iter_fn fn; void *data; @@ -510,54 +417,22 @@ static int metricgroup__sys_event_iter(const struct pmu_metric *pm, return 0; } -static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, - const struct pmu_metrics_table *table __maybe_unused, - void *vdata) +int metricgroup__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, + void *data) { - struct rblist *groups = vdata; + struct metricgroup_iter_data sys_data = { + .fn = fn, + .data = data, + }; - return metricgroup__add_to_mep_groups(pm, groups); -} - -void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) -{ - struct rblist groups; - const struct pmu_metrics_table *table; - struct rb_node *node, *next; - - rblist__init(&groups); - groups.node_new = mep_new; - groups.node_cmp = mep_cmp; - groups.node_delete = mep_delete; - table = pmu_metrics_table__find(); if (table) { - pmu_metrics_table__for_each_metric(table, - metricgroup__add_to_mep_groups_callback, - &groups); - } - { - struct metricgroup_iter_data data = { - .fn = metricgroup__add_to_mep_groups_callback, - .data = &groups, - }; - pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); + int ret = pmu_metrics_table__for_each_metric(table, fn, data); + + if (ret) + return ret; } - for (node = rb_first_cached(&groups.entries); node; node = next) { - struct mep *me = container_of(node, struct mep, nd); - - print_cb->print_metric(print_state, - me->metric_group, - me->metric_name, - me->metric_desc, - me->metric_long_desc, - me->metric_expr, - me->metric_threshold, - me->metric_unit, - me->pmu_name); - next = rb_next(node); - rblist__remove_node(&groups, node); - } + return pmu_for_each_sys_metric(metricgroup__sys_event_iter, &sys_data); } static const char *code_characters = ",-=@"; @@ -964,8 +839,8 @@ static int __add_metric(struct list_head *metric_list, * This metric is the root of a tree and may reference other * metrics that are added recursively. */ - root_metric = metric__new(pm, modifier, metric_no_group, runtime, - user_requested_cpu_list, system_wide); + root_metric = metric__new(pm, modifier, metric_no_group, metric_no_threshold, + runtime, user_requested_cpu_list, system_wide); if (!root_metric) return -ENOMEM; @@ -1090,29 +965,6 @@ static int add_metric(struct list_head *metric_list, return ret; } -static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, - const struct pmu_metrics_table *table __maybe_unused, - void *data) -{ - struct metricgroup_add_iter_data *d = data; - int ret; - - if (!match_pm_metric_or_groups(pm, d->pmu, d->metric_name)) - return 0; - - ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, - d->metric_no_threshold, d->user_requested_cpu_list, - d->system_wide, d->root_metric, d->visited, d->table); - if (ret) - goto out; - - *(d->has_match) = true; - -out: - *(d->ret) = ret; - return ret; -} - /** * metric_list_cmp - list_sort comparator that sorts metrics with more events to * the front. tool events are excluded from the count. @@ -1216,55 +1068,26 @@ static int metricgroup__add_metric(const char *pmu, const char *metric_name, con { LIST_HEAD(list); int ret; - bool has_match = false; + struct metricgroup__add_metric_data data = { + .list = &list, + .pmu = pmu, + .metric_name = metric_name, + .modifier = modifier, + .metric_no_group = metric_no_group, + .metric_no_threshold = metric_no_threshold, + .user_requested_cpu_list = user_requested_cpu_list, + .system_wide = system_wide, + .has_match = false, + }; - { - struct metricgroup__add_metric_data data = { - .list = &list, - .pmu = pmu, - .metric_name = metric_name, - .modifier = modifier, - .metric_no_group = metric_no_group, - .metric_no_threshold = metric_no_threshold, - .user_requested_cpu_list = user_requested_cpu_list, - .system_wide = system_wide, - .has_match = false, - }; - /* - * Iterate over all metrics seeing if metric matches either the - * name or group. When it does add the metric to the list. - */ - ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback, - &data); - if (ret) - goto out; - - has_match = data.has_match; - } - { - struct metricgroup_iter_data data = { - .fn = metricgroup__add_metric_sys_event_iter, - .data = (void *) &(struct metricgroup_add_iter_data) { - .metric_list = &list, - .pmu = pmu, - .metric_name = metric_name, - .modifier = modifier, - .metric_no_group = metric_no_group, - .user_requested_cpu_list = user_requested_cpu_list, - .system_wide = system_wide, - .has_match = &has_match, - .ret = &ret, - .table = table, - }, - }; - - pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); - } - /* End of pmu events. */ - if (!has_match) + /* + * Iterate over all metrics seeing if metric matches either the + * name or group. When it does add the metric to the list. + */ + ret = metricgroup__for_each_metric(table, metricgroup__add_metric_callback, &data); + if (!ret && !data.has_match) ret = -EINVAL; -out: /* * add to metric_list so that they can be released * even if it's failed @@ -1508,7 +1331,6 @@ static int parse_groups(struct evlist *perf_evlist, const char *user_requested_cpu_list, bool system_wide, bool fake_pmu, - struct rblist *metric_events_list, const struct pmu_metrics_table *table) { struct evlist *combined_evlist = NULL; @@ -1518,8 +1340,6 @@ static int parse_groups(struct evlist *perf_evlist, bool is_default = !strcmp(str, "Default"); int ret; - if (metric_events_list->nr_entries == 0) - metricgroup__rblist_init(metric_events_list); ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold, user_requested_cpu_list, system_wide, &metric_list, table); @@ -1610,7 +1430,8 @@ static int parse_groups(struct evlist *perf_evlist, goto out; } - me = metricgroup__lookup(metric_events_list, metric_events[0], true); + me = metricgroup__lookup(&perf_evlist->metric_events, metric_events[0], + /*create=*/true); expr = malloc(sizeof(struct metric_expr)); if (!expr) { @@ -1670,8 +1491,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, - bool hardware_aware_grouping, - struct rblist *metric_events) + bool hardware_aware_grouping) { const struct pmu_metrics_table *table = pmu_metrics_table__find(); @@ -1682,13 +1502,12 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge, metric_no_threshold, user_requested_cpu_list, system_wide, - /*fake_pmu=*/false, metric_events, table); + /*fake_pmu=*/false, table); } int metricgroup__parse_groups_test(struct evlist *evlist, const struct pmu_metrics_table *table, - const char *str, - struct rblist *metric_events) + const char *str) { return parse_groups(evlist, "all", str, /*metric_no_group=*/false, @@ -1696,7 +1515,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist, /*metric_no_threshold=*/false, /*user_requested_cpu_list=*/NULL, /*system_wide=*/false, - /*fake_pmu=*/true, metric_events, table); + /*fake_pmu=*/true, table); } struct metricgroup__has_metric_data { @@ -1781,7 +1600,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx); if (!evsel) return -EINVAL; - new_me = metricgroup__lookup(new_metric_events, evsel, true); + new_me = metricgroup__lookup(new_metric_events, evsel, /*create=*/true); if (!new_me) return -ENOMEM; diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h index a04ac1afa6cc..324880b2ed8f 100644 --- a/tools/perf/util/metricgroup.h +++ b/tools/perf/util/metricgroup.h @@ -77,17 +77,17 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, - bool hardware_aware_grouping, - struct rblist *metric_events); + bool hardware_aware_grouping); int metricgroup__parse_groups_test(struct evlist *evlist, const struct pmu_metrics_table *table, - const char *str, - struct rblist *metric_events); + const char *str); -void metricgroup__print(const struct print_callbacks *print_cb, void *print_state); +int metricgroup__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, + void *data); bool metricgroup__has_metric_or_groups(const char *pmu, const char *metric_or_groups); unsigned int metricgroups__topdown_max_level(void); int arch_get_runtimeparam(const struct pmu_metric *pm); +void metricgroup__rblist_init(struct rblist *metric_events); void metricgroup__rblist_exit(struct rblist *metric_events); int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 2380de56a207..8282ddf68b98 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -17,14 +17,14 @@ #include "string2.h" #include "strbuf.h" #include "debug.h" -#include -#include #include #include #include #include "pmu.h" #include "pmus.h" +#include "tp_pmu.h" #include "asm/bug.h" +#include "ui/ui.h" #include "util/parse-branch-options.h" #include "util/evsel_config.h" #include "util/event.h" @@ -32,6 +32,7 @@ #include "util/stat.h" #include "util/util.h" #include "tracepoint.h" +#include #define MAX_NAME_LEN 100 @@ -83,77 +84,21 @@ const struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { }, }; -const struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { - [PERF_COUNT_SW_CPU_CLOCK] = { - .symbol = "cpu-clock", - .alias = "", - }, - [PERF_COUNT_SW_TASK_CLOCK] = { - .symbol = "task-clock", - .alias = "", - }, - [PERF_COUNT_SW_PAGE_FAULTS] = { - .symbol = "page-faults", - .alias = "faults", - }, - [PERF_COUNT_SW_CONTEXT_SWITCHES] = { - .symbol = "context-switches", - .alias = "cs", - }, - [PERF_COUNT_SW_CPU_MIGRATIONS] = { - .symbol = "cpu-migrations", - .alias = "migrations", - }, - [PERF_COUNT_SW_PAGE_FAULTS_MIN] = { - .symbol = "minor-faults", - .alias = "", - }, - [PERF_COUNT_SW_PAGE_FAULTS_MAJ] = { - .symbol = "major-faults", - .alias = "", - }, - [PERF_COUNT_SW_ALIGNMENT_FAULTS] = { - .symbol = "alignment-faults", - .alias = "", - }, - [PERF_COUNT_SW_EMULATION_FAULTS] = { - .symbol = "emulation-faults", - .alias = "", - }, - [PERF_COUNT_SW_DUMMY] = { - .symbol = "dummy", - .alias = "", - }, - [PERF_COUNT_SW_BPF_OUTPUT] = { - .symbol = "bpf-output", - .alias = "", - }, - [PERF_COUNT_SW_CGROUP_SWITCHES] = { - .symbol = "cgroup-switches", - .alias = "", - }, +static const char *const event_types[] = { + [PERF_TYPE_HARDWARE] = "hardware", + [PERF_TYPE_SOFTWARE] = "software", + [PERF_TYPE_TRACEPOINT] = "tracepoint", + [PERF_TYPE_HW_CACHE] = "hardware-cache", + [PERF_TYPE_RAW] = "raw", + [PERF_TYPE_BREAKPOINT] = "breakpoint", }; -const char *event_type(int type) +const char *event_type(size_t type) { - switch (type) { - case PERF_TYPE_HARDWARE: - return "hardware"; + if (type >= PERF_TYPE_MAX) + return "unknown"; - case PERF_TYPE_SOFTWARE: - return "software"; - - case PERF_TYPE_TRACEPOINT: - return "tracepoint"; - - case PERF_TYPE_HW_CACHE: - return "hardware-cache"; - - default: - break; - } - - return "unknown"; + return event_types[type]; } static char *get_config_str(const struct parse_events_terms *head_terms, @@ -191,10 +136,22 @@ static struct perf_cpu_map *get_config_cpu(const struct parse_events_terms *head list_for_each_entry(term, &head_terms->terms, list) { if (term->type_term == PARSE_EVENTS__TERM_TYPE_CPU) { - struct perf_cpu_map *cpu = perf_cpu_map__new_int(term->val.num); + struct perf_cpu_map *term_cpus; - perf_cpu_map__merge(&cpus, cpu); - perf_cpu_map__put(cpu); + if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { + term_cpus = perf_cpu_map__new_int(term->val.num); + } else { + struct perf_pmu *pmu = perf_pmus__find(term->val.str); + + if (pmu && perf_cpu_map__is_empty(pmu->cpus)) + term_cpus = pmu->is_core ? cpu_map__online() : NULL; + else if (pmu) + term_cpus = perf_cpu_map__get(pmu->cpus); + else + term_cpus = perf_cpu_map__new(term->val.str); + } + perf_cpu_map__merge(&cpus, term_cpus); + perf_cpu_map__put(term_cpus); } } @@ -251,11 +208,12 @@ __add_event(struct list_head *list, int *idx, bool init_attr, const char *name, const char *metric_id, struct perf_pmu *pmu, struct list_head *config_terms, struct evsel *first_wildcard_match, - struct perf_cpu_map *cpu_list, u64 alternate_hw_config) + struct perf_cpu_map *user_cpus, u64 alternate_hw_config) { struct evsel *evsel; bool is_pmu_core; - struct perf_cpu_map *cpus; + struct perf_cpu_map *cpus, *pmu_cpus; + bool has_user_cpus = !perf_cpu_map__is_empty(user_cpus); /* * Ensure the first_wildcard_match's PMU matches that of the new event @@ -279,8 +237,6 @@ __add_event(struct list_head *list, int *idx, } if (pmu) { - is_pmu_core = pmu->is_core; - cpus = perf_cpu_map__get(perf_cpu_map__is_empty(cpu_list) ? pmu->cpus : cpu_list); perf_pmu__warn_invalid_formats(pmu); if (attr->type == PERF_TYPE_RAW || attr->type >= PERF_TYPE_MAX) { perf_pmu__warn_invalid_config(pmu, attr->config, name, @@ -292,45 +248,75 @@ __add_event(struct list_head *list, int *idx, perf_pmu__warn_invalid_config(pmu, attr->config3, name, PERF_PMU_FORMAT_VALUE_CONFIG3, "config3"); } + } + /* + * If a PMU wasn't given, such as for legacy events, find now that + * warnings won't be generated. + */ + if (!pmu) + pmu = perf_pmus__find_by_attr(attr); + + if (pmu) { + is_pmu_core = pmu->is_core; + pmu_cpus = perf_cpu_map__get(pmu->cpus); + if (perf_cpu_map__is_empty(pmu_cpus)) + pmu_cpus = cpu_map__online(); } else { is_pmu_core = (attr->type == PERF_TYPE_HARDWARE || attr->type == PERF_TYPE_HW_CACHE); - if (perf_cpu_map__is_empty(cpu_list)) - cpus = is_pmu_core ? perf_cpu_map__new_online_cpus() : NULL; - else - cpus = perf_cpu_map__get(cpu_list); + pmu_cpus = is_pmu_core ? cpu_map__online() : NULL; } + + if (has_user_cpus) + cpus = perf_cpu_map__get(user_cpus); + else + cpus = perf_cpu_map__get(pmu_cpus); + if (init_attr) event_attr_init(attr); evsel = evsel__new_idx(attr, *idx); - if (!evsel) { - perf_cpu_map__put(cpus); - return NULL; + if (!evsel) + goto out_err; + + if (name) { + evsel->name = strdup(name); + if (!evsel->name) + goto out_err; + } + + if (metric_id) { + evsel->metric_id = strdup(metric_id); + if (!evsel->metric_id) + goto out_err; } (*idx)++; evsel->core.cpus = cpus; - evsel->core.own_cpus = perf_cpu_map__get(cpus); + evsel->core.pmu_cpus = pmu_cpus; evsel->core.requires_cpu = pmu ? pmu->is_uncore : false; evsel->core.is_pmu_core = is_pmu_core; evsel->pmu = pmu; evsel->alternate_hw_config = alternate_hw_config; evsel->first_wildcard_match = first_wildcard_match; - if (name) - evsel->name = strdup(name); - - if (metric_id) - evsel->metric_id = strdup(metric_id); - if (config_terms) list_splice_init(config_terms, &evsel->config_terms); if (list) list_add_tail(&evsel->core.node, list); + if (has_user_cpus) + evsel__warn_user_requested_cpus(evsel, user_cpus); + return evsel; +out_err: + perf_cpu_map__put(cpus); + perf_cpu_map__put(pmu_cpus); + zfree(&evsel->name); + zfree(&evsel->metric_id); + free(evsel); + return NULL; } struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, @@ -489,7 +475,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, int ret = 0; struct evsel *first_wildcard_match = NULL; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_for_event(pmu, name)) != NULL) { LIST_HEAD(config_terms); struct perf_event_attr attr; @@ -613,105 +599,82 @@ static int add_tracepoint(struct parse_events_state *parse_state, return 0; } -static int add_tracepoint_multi_event(struct parse_events_state *parse_state, - struct list_head *list, - const char *sys_name, const char *evt_name, - struct parse_events_error *err, - struct parse_events_terms *head_config, YYLTYPE *loc) +struct add_tracepoint_multi_args { + struct parse_events_state *parse_state; + struct list_head *list; + const char *sys_glob; + const char *evt_glob; + struct parse_events_error *err; + struct parse_events_terms *head_config; + YYLTYPE *loc; + int found; +}; + +static int add_tracepoint_multi_event_cb(void *state, const char *sys_name, const char *evt_name) { - char *evt_path; - struct io_dirent64 *evt_ent; - struct io_dir evt_dir; - int ret = 0, found = 0; + struct add_tracepoint_multi_args *args = state; + int ret; - evt_path = get_events_file(sys_name); - if (!evt_path) { - tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); - return -1; - } - io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); - if (evt_dir.dirfd < 0) { - put_events_file(evt_path); - tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); - return -1; - } + if (!strglobmatch(evt_name, args->evt_glob)) + return 0; - while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) { - if (!strcmp(evt_ent->d_name, ".") - || !strcmp(evt_ent->d_name, "..") - || !strcmp(evt_ent->d_name, "enable") - || !strcmp(evt_ent->d_name, "filter")) - continue; + args->found++; + ret = add_tracepoint(args->parse_state, args->list, sys_name, evt_name, + args->err, args->head_config, args->loc); - if (!strglobmatch(evt_ent->d_name, evt_name)) - continue; - - found++; - - ret = add_tracepoint(parse_state, list, sys_name, evt_ent->d_name, - err, head_config, loc); - } - - if (!found) { - tracepoint_error(err, ENOENT, sys_name, evt_name, loc->first_column); - ret = -1; - } - - put_events_file(evt_path); - close(evt_dir.dirfd); return ret; } -static int add_tracepoint_event(struct parse_events_state *parse_state, - struct list_head *list, - const char *sys_name, const char *evt_name, - struct parse_events_error *err, - struct parse_events_terms *head_config, YYLTYPE *loc) +static int add_tracepoint_multi_event(struct add_tracepoint_multi_args *args, const char *sys_name) { - return strpbrk(evt_name, "*?") ? - add_tracepoint_multi_event(parse_state, list, sys_name, evt_name, - err, head_config, loc) : - add_tracepoint(parse_state, list, sys_name, evt_name, - err, head_config, loc); + if (strpbrk(args->evt_glob, "*?") == NULL) { + /* Not a glob. */ + args->found++; + return add_tracepoint(args->parse_state, args->list, sys_name, args->evt_glob, + args->err, args->head_config, args->loc); + } + + return tp_pmu__for_each_tp_event(sys_name, args, add_tracepoint_multi_event_cb); +} + +static int add_tracepoint_multi_sys_cb(void *state, const char *sys_name) +{ + struct add_tracepoint_multi_args *args = state; + + if (!strglobmatch(sys_name, args->sys_glob)) + return 0; + + return add_tracepoint_multi_event(args, sys_name); } static int add_tracepoint_multi_sys(struct parse_events_state *parse_state, struct list_head *list, - const char *sys_name, const char *evt_name, + const char *sys_glob, const char *evt_glob, struct parse_events_error *err, struct parse_events_terms *head_config, YYLTYPE *loc) { - struct io_dirent64 *events_ent; - struct io_dir events_dir; - int ret = 0; - char *events_dir_path = get_tracing_file("events"); + struct add_tracepoint_multi_args args = { + .parse_state = parse_state, + .list = list, + .sys_glob = sys_glob, + .evt_glob = evt_glob, + .err = err, + .head_config = head_config, + .loc = loc, + .found = 0, + }; + int ret; - if (!events_dir_path) { - tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); - return -1; + if (strpbrk(sys_glob, "*?") == NULL) { + /* Not a glob. */ + ret = add_tracepoint_multi_event(&args, sys_glob); + } else { + ret = tp_pmu__for_each_tp_sys(&args, add_tracepoint_multi_sys_cb); } - io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); - put_events_file(events_dir_path); - if (events_dir.dirfd < 0) { - tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); - return -1; + if (args.found == 0) { + tracepoint_error(err, ENOENT, sys_glob, evt_glob, loc->first_column); + return -ENOENT; } - - while (!ret && (events_ent = io_dir__readdir(&events_dir))) { - if (!strcmp(events_ent->d_name, ".") - || !strcmp(events_ent->d_name, "..") - || !strcmp(events_ent->d_name, "enable") - || !strcmp(events_ent->d_name, "header_event") - || !strcmp(events_ent->d_name, "header_page")) - continue; - - if (!strglobmatch(events_ent->d_name, sys_name)) - continue; - - ret = add_tracepoint_event(parse_state, list, events_ent->d_name, - evt_name, err, head_config, loc); - } - close(events_dir.dirfd); return ret; } @@ -1048,15 +1011,32 @@ do { \ return -EINVAL; } break; - case PARSE_EVENTS__TERM_TYPE_CPU: - CHECK_TYPE_VAL(NUM); - if (term->val.num >= (u64)cpu__max_present_cpu().cpu) { + case PARSE_EVENTS__TERM_TYPE_CPU: { + struct perf_cpu_map *map; + + if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { + if (term->val.num >= (u64)cpu__max_present_cpu().cpu) { + parse_events_error__handle(err, term->err_val, + strdup("too big"), + /*help=*/NULL); + return -EINVAL; + } + break; + } + assert(term->type_val == PARSE_EVENTS__TERM_TYPE_STR); + if (perf_pmus__find(term->val.str) != NULL) + break; + + map = perf_cpu_map__new(term->val.str); + if (!map) { parse_events_error__handle(err, term->err_val, - strdup("too big"), - NULL); + strdup("not a valid PMU or CPU number"), + /*help=*/NULL); return -EINVAL; } + perf_cpu_map__put(map); break; + } case PARSE_EVENTS__TERM_TYPE_DRV_CFG: case PARSE_EVENTS__TERM_TYPE_USER: case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: @@ -1403,12 +1383,8 @@ int parse_events_add_tracepoint(struct parse_events_state *parse_state, return -EINVAL; } - if (strpbrk(sys, "*?")) - return add_tracepoint_multi_sys(parse_state, list, sys, event, - err, head_config, loc); - else - return add_tracepoint_event(parse_state, list, sys, event, - err, head_config, loc); + return add_tracepoint_multi_sys(parse_state, list, sys, event, + err, head_config, loc); } static int __parse_events_add_numeric(struct parse_events_state *parse_state, @@ -1680,7 +1656,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, INIT_LIST_HEAD(list); - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_for_event(pmu, event_name)) != NULL) { + if (parse_events__filter_pmu(parse_state, pmu)) continue; @@ -1759,19 +1736,21 @@ int parse_events_multi_pmu_add_or_add_pmu(struct parse_events_state *parse_state pmu = NULL; /* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */ - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!parse_events__filter_pmu(parse_state, pmu) && - perf_pmu__wildcard_match(pmu, event_or_pmu)) { - if (!parse_events_add_pmu(parse_state, *listp, pmu, - const_parsed_terms, - first_wildcard_match, - /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) { - ok++; - parse_state->wild_card_pmus = true; - } - if (first_wildcard_match == NULL) - first_wildcard_match = - container_of((*listp)->prev, struct evsel, core.node); + while ((pmu = perf_pmus__scan_matching_wildcard(pmu, event_or_pmu)) != NULL) { + + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + + if (!parse_events_add_pmu(parse_state, *listp, pmu, + const_parsed_terms, + first_wildcard_match, + /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) { + ok++; + parse_state->wild_card_pmus = true; + } + if (first_wildcard_match == NULL) { + first_wildcard_match = + container_of((*listp)->prev, struct evsel, core.node); } } if (ok) @@ -1829,13 +1808,11 @@ static int parse_events__modifier_list(struct parse_events_state *parse_state, int eH = group ? evsel->core.attr.exclude_host : 0; int eG = group ? evsel->core.attr.exclude_guest : 0; int exclude = eu | ek | eh; - int exclude_GH = group ? evsel->exclude_GH : 0; + int exclude_GH = eG | eH; if (mod.user) { if (!exclude) exclude = eu = ek = eh = 1; - if (!exclude_GH && !perf_guest && exclude_GH_default) - eG = 1; eu = 0; } if (mod.kernel) { @@ -1858,6 +1835,13 @@ static int parse_events__modifier_list(struct parse_events_state *parse_state, exclude_GH = eG = eH = 1; eH = 0; } + if (!exclude_GH && exclude_GH_default) { + if (perf_host) + eG = 1; + else if (perf_guest) + eH = 1; + } + evsel->core.attr.exclude_user = eu; evsel->core.attr.exclude_kernel = ek; evsel->core.attr.exclude_hv = eh; @@ -2128,6 +2112,11 @@ static int evlist__cmp(void *_fg_idx, const struct list_head *l, const struct li return arch_evlist__cmp(lhs, rhs); } +int __weak arch_evlist__add_required_events(struct list_head *list __always_unused) +{ + return 0; +} + static int parse_events__sort_events_and_fix_groups(struct list_head *list) { int idx = 0, force_grouped_idx = -1; @@ -2139,6 +2128,11 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) struct evsel *force_grouped_leader = NULL; bool last_event_was_forced_leader = false; + /* On x86 topdown metrics events require a slots event. */ + ret = arch_evlist__add_required_events(list); + if (ret) + return ret; + /* * Compute index to insert ungrouped events at. Place them where the * first ungrouped event appears. @@ -2561,12 +2555,17 @@ foreach_evsel_in_last_glob(struct evlist *evlist, return 0; } +/* Will a tracepoint filter work for str or should a BPF filter be used? */ +static bool is_possible_tp_filter(const char *str) +{ + return strstr(str, "uid") == NULL; +} + static int set_filter(struct evsel *evsel, const void *arg) { const char *str = arg; - bool found = false; int nr_addr_filters = 0; - struct perf_pmu *pmu = NULL; + struct perf_pmu *pmu; if (evsel == NULL) { fprintf(stderr, @@ -2574,7 +2573,7 @@ static int set_filter(struct evsel *evsel, const void *arg) return -1; } - if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) { + if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT && is_possible_tp_filter(str)) { if (evsel__append_tp_filter(evsel, str) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); @@ -2584,16 +2583,11 @@ static int set_filter(struct evsel *evsel, const void *arg) return 0; } - while ((pmu = perf_pmus__scan(pmu)) != NULL) - if (pmu->type == evsel->core.attr.type) { - found = true; - break; - } - - if (found) + pmu = evsel__find_pmu(evsel); + if (pmu) { perf_pmu__scan_file(pmu, "nr_addr_filters", "%d", &nr_addr_filters); - + } if (!nr_addr_filters) return perf_bpf_filter__parse(&evsel->bpf_filters, str); @@ -2615,6 +2609,30 @@ int parse_filter(const struct option *opt, const char *str, (const void *)str); } +int parse_uid_filter(struct evlist *evlist, uid_t uid) +{ + struct option opt = { + .value = &evlist, + }; + char buf[128]; + int ret; + + snprintf(buf, sizeof(buf), "uid == %d", uid); + ret = parse_filter(&opt, buf, /*unset=*/0); + if (ret) { + if (use_browser >= 1) { + /* + * Use ui__warning so a pop up appears above the + * underlying BPF error message. + */ + ui__warning("Failed to add UID filtering that uses BPF filtering.\n"); + } else { + fprintf(stderr, "Failed to add UID filtering that uses BPF filtering.\n"); + } + } + return ret; +} + static int add_exclude_perf_filter(struct evsel *evsel, const void *arg __maybe_unused) { diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index ab242f671031..62dc7202e3ba 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -11,6 +11,7 @@ #include #include #include +#include struct evsel; struct evlist; @@ -20,7 +21,7 @@ struct option; struct perf_pmu; struct strbuf; -const char *event_type(int type); +const char *event_type(size_t type); /* Arguments encoded in opt->value. */ struct parse_events_option_args { @@ -45,6 +46,7 @@ static inline int parse_events(struct evlist *evlist, const char *str, int parse_event(struct evlist *evlist, const char *str); int parse_filter(const struct option *opt, const char *str, int unset); +int parse_uid_filter(struct evlist *evlist, uid_t uid); int exclude_perf(const struct option *opt, const char *arg, int unset); enum parse_events__term_val_type { @@ -262,7 +264,6 @@ struct event_symbol { const char *alias; }; extern const struct event_symbol event_symbols_hw[]; -extern const struct event_symbol event_symbols_sw[]; char *parse_events_formats_error_string(char *additional_terms); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 4af7b9c1f44d..2034590eb789 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -117,12 +117,12 @@ do { \ yyless(0); \ } while (0) -static int sym(yyscan_t scanner, int type, int config) +static int sym(yyscan_t scanner, int config) { YYSTYPE *yylval = parse_events_get_lval(scanner); - yylval->num = (type << 16) + config; - return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW; + yylval->num = config; + return PE_VALUE_SYM_HW; } static int term(yyscan_t scanner, enum parse_events__term_type type) @@ -391,28 +391,16 @@ r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } <> { BEGIN(INITIAL); } } -cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } -stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } -stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } -instructions { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } -cache-references { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } -cache-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } -branch-instructions|branches { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } -branch-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } -bus-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } -ref-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } -cpu-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } -task-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } -page-faults|faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } -minor-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } -major-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } -context-switches|cs { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } -cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } -alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } -emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } -dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } -bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); } -cgroup-switches { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); } +cpu-cycles|cycles { return sym(yyscanner, PERF_COUNT_HW_CPU_CYCLES); } +stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } +stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } +instructions { return sym(yyscanner, PERF_COUNT_HW_INSTRUCTIONS); } +cache-references { return sym(yyscanner, PERF_COUNT_HW_CACHE_REFERENCES); } +cache-misses { return sym(yyscanner, PERF_COUNT_HW_CACHE_MISSES); } +branch-instructions|branches { return sym(yyscanner, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } +branch-misses { return sym(yyscanner, PERF_COUNT_HW_BRANCH_MISSES); } +bus-cycles { return sym(yyscanner, PERF_COUNT_HW_BUS_CYCLES); } +ref-cycles { return sym(yyscanner, PERF_COUNT_HW_REF_CPU_CYCLES); } {lc_type} { return str(yyscanner, PE_LEGACY_CACHE); } {lc_type}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index f888cbb076d6..a2361c0040d7 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -55,7 +55,7 @@ static void free_list_evsel(struct list_head* list_evsel) %} %token PE_START_EVENTS PE_START_TERMS -%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM +%token PE_VALUE PE_VALUE_SYM_HW PE_TERM %token PE_EVENT_NAME %token PE_RAW PE_NAME %token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH @@ -66,10 +66,8 @@ static void free_list_evsel(struct list_head* list_evsel) %token PE_TERM_HW %type PE_VALUE %type PE_VALUE_SYM_HW -%type PE_VALUE_SYM_SW %type PE_MODIFIER_EVENT %type PE_TERM -%type value_sym %type PE_RAW %type PE_NAME %type PE_LEGACY_CACHE @@ -306,24 +304,19 @@ PE_NAME sep_dc $$ = list; } -value_sym: -PE_VALUE_SYM_HW -| -PE_VALUE_SYM_SW - event_legacy_symbol: -value_sym '/' event_config '/' +PE_VALUE_SYM_HW '/' event_config '/' { struct list_head *list; - int type = $1 >> 16; - int config = $1 & 255; int err; - bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE); list = alloc_list(); if (!list) YYNOMEM; - err = parse_events_add_numeric(_parse_state, list, type, config, $3, wildcard); + err = parse_events_add_numeric(_parse_state, list, + PERF_TYPE_HARDWARE, $1, + $3, + /*wildcard=*/true); parse_events_terms__delete($3); if (err) { free_list_evsel(list); @@ -332,18 +325,18 @@ value_sym '/' event_config '/' $$ = list; } | -value_sym sep_slash_slash_dc +PE_VALUE_SYM_HW sep_slash_slash_dc { struct list_head *list; - int type = $1 >> 16; - int config = $1 & 255; - bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE); int err; list = alloc_list(); if (!list) YYNOMEM; - err = parse_events_add_numeric(_parse_state, list, type, config, /*head_config=*/NULL, wildcard); + err = parse_events_add_numeric(_parse_state, list, + PERF_TYPE_HARDWARE, $1, + /*head_config=*/NULL, + /*wildcard=*/true); if (err) PE_ABORT(err); $$ = list; diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index 0dacc133ed39..e5b3a2a5ddef 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -47,10 +47,6 @@ int parse_libpfm_events_option(const struct option *opt, const char *str, p_orig = p = strdup(str); if (!p) return -1; - /* - * force loading of the PMU list - */ - perf_pmus__scan(NULL); for (q = p; strsep(&p, ",{}"); q = p) { sep = p ? str + (p - p_orig - 1) : ""; @@ -234,6 +230,7 @@ print_libpfm_event(const struct print_callbacks *print_cb, void *print_state, if (is_libpfm_event_supported(name, cpus, threads)) { print_cb->print_event(print_state, topic, pinfo->name, + /*pmu_type=*/PERF_TYPE_RAW, name, info->equiv, /*scale_unit=*/NULL, /*deprecated=*/NULL, "PFM event", @@ -269,6 +266,7 @@ print_libpfm_event(const struct print_callbacks *print_cb, void *print_state, print_cb->print_event(print_state, topic, pinfo->name, + /*pmu_type=*/PERF_TYPE_RAW, name, /*alias=*/NULL, /*scale_unit=*/NULL, /*deprecated=*/NULL, "PFM event", diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 609828513f6c..5a291f1380ed 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -20,9 +20,11 @@ #include "debug.h" #include "evsel.h" #include "pmu.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "pmus.h" #include "tool_pmu.h" +#include "tp_pmu.h" #include #include #include "parse-events.h" @@ -452,7 +454,7 @@ static struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, { struct perf_pmu_alias *alias; bool has_sysfs_event; - char event_file_name[FILENAME_MAX + 8]; + char event_file_name[NAME_MAX + 8]; if (hashmap__find(pmu->aliases, name, &alias)) return alias; @@ -622,8 +624,7 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name, alias->name = strdup(name); alias->desc = desc ? strdup(desc) : NULL; - alias->long_desc = long_desc ? strdup(long_desc) : - desc ? strdup(desc) : NULL; + alias->long_desc = long_desc ? strdup(long_desc) : NULL; alias->topic = topic ? strdup(topic) : NULL; alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL; if (unit) { @@ -752,7 +753,7 @@ static int pmu_aliases_parse(struct perf_pmu *pmu) static int pmu_aliases_parse_eager(struct perf_pmu *pmu, int sysfs_fd) { - char path[FILENAME_MAX + 7]; + char path[NAME_MAX + 8]; int ret, events_dir_fd; scnprintf(path, sizeof(path), "%s/events", pmu->name); @@ -1181,6 +1182,32 @@ int perf_pmu__init(struct perf_pmu *pmu, __u32 type, const char *name) return 0; } +static __u32 wellknown_pmu_type(const char *pmu_name) +{ + struct { + const char *pmu_name; + __u32 type; + } wellknown_pmus[] = { + { + "software", + PERF_TYPE_SOFTWARE + }, + { + "tracepoint", + PERF_TYPE_TRACEPOINT + }, + { + "breakpoint", + PERF_TYPE_BREAKPOINT + }, + }; + for (size_t i = 0; i < ARRAY_SIZE(wellknown_pmus); i++) { + if (!strcmp(wellknown_pmus[i].pmu_name, pmu_name)) + return wellknown_pmus[i].type; + } + return PERF_TYPE_MAX; +} + struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *name, bool eager_load) { @@ -1200,8 +1227,12 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char * that type value is successfully assigned (return 1). */ if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &pmu->type) != 1) { - perf_pmu__delete(pmu); - return NULL; + /* Double check the PMU's name isn't wellknown. */ + pmu->type = wellknown_pmu_type(name); + if (pmu->type == PERF_TYPE_MAX) { + perf_pmu__delete(pmu); + return NULL; + } } /* @@ -1627,6 +1658,8 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu, if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__config_terms(pmu, attr, terms, err); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__config_terms(pmu, attr, terms, err); list_for_each_entry(term, &terms->terms, list) { if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err)) @@ -1767,6 +1800,10 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_ ret = hwmon_pmu__check_alias(head_terms, info, err); goto out; } + if (perf_pmu__is_drm(pmu)) { + ret = drm_pmu__check_alias(pmu, head_terms, info, err); + goto out; + } /* Fake PMU doesn't rewrite terms. */ if (perf_pmu__is_fake(pmu)) @@ -1947,8 +1984,12 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name) return false; if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(name)) return false; + if (perf_pmu__is_tracepoint(pmu)) + return tp_pmu__have_event(pmu, name); if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__have_event(pmu, name); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__have_event(pmu, name); if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL) return true; if (pmu->cpu_aliases_added || !pmu->events_table) @@ -1960,8 +2001,12 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu) { size_t nr; + if (perf_pmu__is_tracepoint(pmu)) + return tp_pmu__num_events(pmu); if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__num_events(pmu); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__num_events(pmu); pmu_aliases_parse(pmu); nr = pmu->sysfs_aliases + pmu->sys_json_aliases; @@ -2028,8 +2073,12 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, struct hashmap_entry *entry; size_t bkt; + if (perf_pmu__is_tracepoint(pmu)) + return tp_pmu__for_each_event(pmu, state, cb); if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__for_each_event(pmu, state, cb); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__for_each_event(pmu, state, cb); strbuf_init(&sb, /*hint=*/ 0); pmu_aliases_parse(pmu); @@ -2511,6 +2560,8 @@ void perf_pmu__delete(struct perf_pmu *pmu) if (perf_pmu__is_hwmon(pmu)) hwmon_pmu__exit(pmu); + else if (perf_pmu__is_drm(pmu)) + drm_pmu__exit(pmu); perf_pmu__del_formats(&pmu->format); perf_pmu__del_aliases(pmu); diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 71b8636fd07d..1ebcf0242af8 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -39,7 +39,9 @@ struct perf_pmu_caps { enum { PERF_PMU_TYPE_PE_START = 0, - PERF_PMU_TYPE_PE_END = 0xFFFEFFFF, + PERF_PMU_TYPE_PE_END = 0xFFFDFFFF, + PERF_PMU_TYPE_DRM_START = 0xFFFE0000, + PERF_PMU_TYPE_DRM_END = 0xFFFEFFFF, PERF_PMU_TYPE_HWMON_START = 0xFFFF0000, PERF_PMU_TYPE_HWMON_END = 0xFFFFFFFD, PERF_PMU_TYPE_TOOL = 0xFFFFFFFE, @@ -300,7 +302,6 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char bool eager_load); struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus); void perf_pmu__delete(struct perf_pmu *pmu); -struct perf_pmu *perf_pmus__find_core_pmu(void); const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config); bool perf_pmu__is_fake(const struct perf_pmu *pmu); diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 3bbd26fec78a..98be2eb8f1f0 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -12,6 +12,7 @@ #include #include "cpumap.h" #include "debug.h" +#include "drm_pmu.h" #include "evsel.h" #include "pmus.h" #include "pmu.h" @@ -19,6 +20,7 @@ #include "tool_pmu.h" #include "print-events.h" #include "strbuf.h" +#include "string2.h" /* * core_pmus: A PMU belongs to core_pmus if it's name is "cpu" or it's sysfs @@ -42,16 +44,19 @@ enum perf_tool_pmu_type { PERF_TOOL_PMU_TYPE_PE_OTHER, PERF_TOOL_PMU_TYPE_TOOL, PERF_TOOL_PMU_TYPE_HWMON, + PERF_TOOL_PMU_TYPE_DRM, #define PERF_TOOL_PMU_TYPE_PE_CORE_MASK (1 << PERF_TOOL_PMU_TYPE_PE_CORE) #define PERF_TOOL_PMU_TYPE_PE_OTHER_MASK (1 << PERF_TOOL_PMU_TYPE_PE_OTHER) #define PERF_TOOL_PMU_TYPE_TOOL_MASK (1 << PERF_TOOL_PMU_TYPE_TOOL) #define PERF_TOOL_PMU_TYPE_HWMON_MASK (1 << PERF_TOOL_PMU_TYPE_HWMON) +#define PERF_TOOL_PMU_TYPE_DRM_MASK (1 << PERF_TOOL_PMU_TYPE_DRM) #define PERF_TOOL_PMU_TYPE_ALL_MASK (PERF_TOOL_PMU_TYPE_PE_CORE_MASK | \ PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | \ PERF_TOOL_PMU_TYPE_TOOL_MASK | \ - PERF_TOOL_PMU_TYPE_HWMON_MASK) + PERF_TOOL_PMU_TYPE_HWMON_MASK | \ + PERF_TOOL_PMU_TYPE_DRM_MASK) }; static unsigned int read_pmu_types; @@ -172,6 +177,8 @@ struct perf_pmu *perf_pmus__find(const char *name) /* Looking up an individual perf event PMU failed, check if a tool PMU should be read. */ if (!strncmp(name, "hwmon_", 6)) to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + else if (!strncmp(name, "drm_", 4)) + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; else if (!strcmp(name, "tool")) to_read_pmus |= PERF_TOOL_PMU_TYPE_TOOL_MASK; @@ -272,6 +279,10 @@ static void pmu_read_sysfs(unsigned int to_read_types) (read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) == 0) perf_pmus__read_hwmon_pmus(&other_pmus); + if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) != 0 && + (read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) == 0) + perf_pmus__read_drm_pmus(&other_pmus); + list_sort(NULL, &other_pmus, pmus_cmp); read_pmu_types |= to_read_types; @@ -304,6 +315,8 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type) if (type >= PERF_PMU_TYPE_PE_START && type <= PERF_PMU_TYPE_PE_END) { to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK; + } else if (type >= PERF_PMU_TYPE_DRM_START && type <= PERF_PMU_TYPE_DRM_END) { + to_read_pmus = PERF_TOOL_PMU_TYPE_DRM_MASK; } else if (type >= PERF_PMU_TYPE_HWMON_START && type <= PERF_PMU_TYPE_HWMON_END) { to_read_pmus = PERF_TOOL_PMU_TYPE_HWMON_MASK; } else { @@ -350,6 +363,92 @@ struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu) return NULL; } +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event) +{ + bool use_core_pmus = !pmu || pmu->is_core; + + if (!pmu) { + /* Hwmon filename values that aren't used. */ + enum hwmon_type type; + int number; + /* + * Core PMUs, other sysfs PMUs and tool PMU can take all event + * types or aren't wother optimizing for. + */ + unsigned int to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* Could the event be a hwmon event? */ + if (parse_hwmon_filename(event, &type, &number, /*item=*/NULL, /*alarm=*/NULL)) + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + + /* Could the event be a DRM event? */ + if (strlen(event) > 4 && strncmp("drm-", event, 4) == 0) + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu = list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) + return pmu; + + pmu = NULL; + pmu = list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) + return pmu; + return NULL; +} + +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard) +{ + bool use_core_pmus = !pmu || pmu->is_core; + + if (!pmu) { + /* + * Core PMUs, other sysfs PMUs and tool PMU can have any name or + * aren't wother optimizing for. + */ + unsigned int to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* + * Hwmon PMUs have an alias from a sysfs name like hwmon0, + * hwmon1, etc. or have a name of hwmon_. They therefore + * can only have a wildcard match if the wildcard begins with + * "hwmon". Similarly drm PMUs must start "drm_", avoid reading + * such events unless the PMU could match. + */ + if (strisglob(wildcard)) { + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK | + PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >= 4 && strncmp("drm_", wildcard, 4) == 0) { + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >= 5 && strncmp("hwmon", wildcard, 5) == 0) { + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + } + + pmu_read_sysfs(to_read_pmus); + pmu = list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + pmu = NULL; + pmu = list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + return NULL; +} + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) { bool use_core_pmus = !pmu || pmu->is_core; @@ -546,6 +645,7 @@ void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *p print_cb->print_event(print_state, aliases[j].topic, aliases[j].pmu_name, + aliases[j].pmu->type, aliases[j].name, aliases[j].alias, aliases[j].scale_unit, @@ -650,6 +750,7 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + pmu->type, format_args.short_string.buf, /*event_alias=*/NULL, /*scale_unit=*/NULL, @@ -715,24 +816,18 @@ bool perf_pmus__supports_extended_type(void) return perf_pmus__do_support_extended_type; } -struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) +struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr) { - struct perf_pmu *pmu = evsel->pmu; - bool legacy_core_type; + struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); + u32 type = attr->type; + bool legacy_core_type = type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE; - if (pmu) - return pmu; - - pmu = perf_pmus__find_by_type(evsel->core.attr.type); - legacy_core_type = - evsel->core.attr.type == PERF_TYPE_HARDWARE || - evsel->core.attr.type == PERF_TYPE_HW_CACHE; if (!pmu && legacy_core_type && perf_pmus__supports_extended_type()) { - u32 type = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT; + type = attr->config >> PERF_PMU_TYPE_SHIFT; pmu = perf_pmus__find_by_type(type); } - if (!pmu && (legacy_core_type || evsel->core.attr.type == PERF_TYPE_RAW)) { + if (!pmu && (legacy_core_type || type == PERF_TYPE_RAW)) { /* * For legacy events, if there was no extended type info then * assume the PMU is the first core PMU. @@ -743,6 +838,17 @@ struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) */ pmu = perf_pmus__find_core_pmu(); } + return pmu; +} + +struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) +{ + struct perf_pmu *pmu = evsel->pmu; + + if (pmu) + return pmu; + + pmu = perf_pmus__find_by_attr(&evsel->core.attr); ((struct evsel *)evsel)->pmu = pmu; return pmu; } @@ -762,7 +868,7 @@ struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name) return perf_pmu__lookup(&other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true); } -struct perf_pmu *perf_pmus__add_test_hwmon_pmu(int hwmon_dir, +struct perf_pmu *perf_pmus__add_test_hwmon_pmu(const char *hwmon_dir, const char *sysfs_name, const char *name) { diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 8def20e615ad..7cb36863711a 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -5,6 +5,7 @@ #include #include +struct perf_event_attr; struct perf_pmu; struct print_callbacks; @@ -16,9 +17,12 @@ void perf_pmus__destroy(void); struct perf_pmu *perf_pmus__find(const char *name); struct perf_pmu *perf_pmus__find_by_type(unsigned int type); +struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr); struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event); +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); @@ -29,9 +33,10 @@ int perf_pmus__num_core_pmus(void); bool perf_pmus__supports_extended_type(void); struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name); -struct perf_pmu *perf_pmus__add_test_hwmon_pmu(int hwmon_dir, +struct perf_pmu *perf_pmus__add_test_hwmon_pmu(const char *hwmon_dir, const char *sysfs_name, const char *name); struct perf_pmu *perf_pmus__fake_pmu(void); +struct perf_pmu *perf_pmus__find_core_pmu(void); #endif /* __PMUS_H */ diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index a786cbfb0ff5..4153124a9948 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -44,97 +44,6 @@ static const char * const event_type_descriptors[] = { "Hardware breakpoint", }; -/* - * Print the events from /tracing/events - */ -void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unused, void *print_state __maybe_unused) -{ - char *events_path = get_tracing_file("events"); - int events_fd = open(events_path, O_PATH); - struct dirent **sys_namelist = NULL; - int sys_items; - - if (events_fd < 0) { - pr_err("Error: failed to open tracing events directory\n"); - pr_err("%s: %s\n", events_path, strerror(errno)); - return; - } - put_tracing_file(events_path); - - sys_items = tracing_events__scandir_alphasort(&sys_namelist); - - for (int i = 0; i < sys_items; i++) { - struct dirent *sys_dirent = sys_namelist[i]; - struct dirent **evt_namelist = NULL; - int dir_fd; - int evt_items; - - if (sys_dirent->d_type != DT_DIR || - !strcmp(sys_dirent->d_name, ".") || - !strcmp(sys_dirent->d_name, "..")) - goto next_sys; - - dir_fd = openat(events_fd, sys_dirent->d_name, O_PATH); - if (dir_fd < 0) - goto next_sys; - - evt_items = scandirat(events_fd, sys_dirent->d_name, &evt_namelist, NULL, alphasort); - for (int j = 0; j < evt_items; j++) { - /* - * Buffer sized at twice the max filename length + 1 - * separator + 1 \0 terminator. - */ - char buf[NAME_MAX * 2 + 2]; - /* 16 possible hex digits and 22 other characters and \0. */ - char encoding[16 + 22]; - struct dirent *evt_dirent = evt_namelist[j]; - struct io id; - __u64 config; - - if (evt_dirent->d_type != DT_DIR || - !strcmp(evt_dirent->d_name, ".") || - !strcmp(evt_dirent->d_name, "..")) - goto next_evt; - - snprintf(buf, sizeof(buf), "%s/id", evt_dirent->d_name); - io__init(&id, openat(dir_fd, buf, O_RDONLY), buf, sizeof(buf)); - - if (id.fd < 0) - goto next_evt; - - if (io__get_dec(&id, &config) < 0) { - close(id.fd); - goto next_evt; - } - close(id.fd); - - snprintf(buf, sizeof(buf), "%s:%s", - sys_dirent->d_name, evt_dirent->d_name); - snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%llx/", config); - print_cb->print_event(print_state, - /*topic=*/NULL, - /*pmu_name=*/NULL, /* really "tracepoint" */ - /*event_name=*/buf, - /*event_alias=*/NULL, - /*scale_unit=*/NULL, - /*deprecated=*/false, - "Tracepoint event", - /*desc=*/NULL, - /*long_desc=*/NULL, - encoding); -next_evt: - free(evt_namelist[j]); - } - close(dir_fd); - free(evt_namelist); -next_sys: - free(sys_namelist[i]); - } - - free(sys_namelist); - close(events_fd); -} - void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) { struct strlist *bidlist, *sdtlist; @@ -212,6 +121,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_TRACEPOINT, evt_name ?: sdt_name->s, /*event_alias=*/NULL, /*deprecated=*/false, @@ -268,6 +178,7 @@ bool is_event_supported(u8 type, u64 config) ret = evsel__open(evsel, NULL, tmap) >= 0; } + evsel__close(evsel); evsel__delete(evsel); } @@ -312,6 +223,7 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta print_cb->print_event(print_state, "cache", pmu->name, + pmu->type, name, alias_name, /*scale_unit=*/NULL, @@ -368,6 +280,7 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + type, nd->s, alias, /*scale_unit=*/NULL, @@ -380,6 +293,139 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta strlist__delete(evt_name_list); } +/** struct mep - RB-tree node for building printing information. */ +struct mep { + /** nd - RB-tree element. */ + struct rb_node nd; + /** @metric_group: Owned metric group name, separated others with ';'. */ + char *metric_group; + const char *metric_name; + const char *metric_desc; + const char *metric_long_desc; + const char *metric_expr; + const char *metric_threshold; + const char *metric_unit; + const char *pmu_name; +}; + +static int mep_cmp(struct rb_node *rb_node, const void *entry) +{ + struct mep *a = container_of(rb_node, struct mep, nd); + struct mep *b = (struct mep *)entry; + int ret; + + ret = strcmp(a->metric_group, b->metric_group); + if (ret) + return ret; + + return strcmp(a->metric_name, b->metric_name); +} + +static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) +{ + struct mep *me = malloc(sizeof(struct mep)); + + if (!me) + return NULL; + + memcpy(me, entry, sizeof(struct mep)); + return &me->nd; +} + +static void mep_delete(struct rblist *rl __maybe_unused, + struct rb_node *nd) +{ + struct mep *me = container_of(nd, struct mep, nd); + + zfree(&me->metric_group); + free(me); +} + +static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, + const char *metric_name) +{ + struct rb_node *nd; + struct mep me = { + .metric_group = strdup(metric_group), + .metric_name = metric_name, + }; + nd = rblist__find(groups, &me); + if (nd) { + free(me.metric_group); + return container_of(nd, struct mep, nd); + } + rblist__add_node(groups, &me); + nd = rblist__find(groups, &me); + if (nd) + return container_of(nd, struct mep, nd); + return NULL; +} + +static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *vdata) +{ + struct rblist *groups = vdata; + const char *g; + char *omg, *mg; + + mg = strdup(pm->metric_group ?: pm->metric_name); + if (!mg) + return -ENOMEM; + omg = mg; + while ((g = strsep(&mg, ";")) != NULL) { + struct mep *me; + + g = skip_spaces(g); + if (strlen(g)) + me = mep_lookup(groups, g, pm->metric_name); + else + me = mep_lookup(groups, pm->metric_name, pm->metric_name); + + if (me) { + me->metric_desc = pm->desc; + me->metric_long_desc = pm->long_desc; + me->metric_expr = pm->metric_expr; + me->metric_threshold = pm->metric_threshold; + me->metric_unit = pm->unit; + me->pmu_name = pm->pmu; + } + } + free(omg); + + return 0; +} + +void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) +{ + struct rblist groups; + struct rb_node *node, *next; + const struct pmu_metrics_table *table = pmu_metrics_table__find(); + + rblist__init(&groups); + groups.node_new = mep_new; + groups.node_cmp = mep_cmp; + groups.node_delete = mep_delete; + + metricgroup__for_each_metric(table, metricgroup__add_to_mep_groups_callback, &groups); + + for (node = rb_first_cached(&groups.entries); node; node = next) { + struct mep *me = container_of(node, struct mep, nd); + + print_cb->print_metric(print_state, + me->metric_group, + me->metric_name, + me->metric_desc, + me->metric_long_desc, + me->metric_expr, + me->metric_threshold, + me->metric_unit, + me->pmu_name); + next = rb_next(node); + rblist__remove_node(&groups, node); + } +} + /* * Print the help text for the event symbols: */ @@ -387,8 +433,6 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) { print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX); - print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE, - event_symbols_sw, PERF_COUNT_SW_MAX); print_hwcache_events(print_cb, print_state); @@ -397,6 +441,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_RAW, "rNNN", /*event_alias=*/NULL, /*scale_unit=*/NULL, @@ -411,6 +456,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_BREAKPOINT, "mem:[/len][:access]", /*scale_unit=*/NULL, /*event_alias=*/NULL, @@ -420,8 +466,6 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) /*long_desc=*/NULL, /*encoding_desc=*/NULL); - print_tracepoint_events(print_cb, print_state); - print_sdt_events(print_cb, print_state); metricgroup__print(print_cb, print_state); diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h index 8f19c2bea64a..d6ba384f0c66 100644 --- a/tools/perf/util/print-events.h +++ b/tools/perf/util/print-events.h @@ -12,7 +12,7 @@ struct print_callbacks { void (*print_start)(void *print_state); void (*print_end)(void *print_state); void (*print_event)(void *print_state, const char *topic, - const char *pmu_name, + const char *pmu_name, u32 pmu_type, const char *event_name, const char *event_alias, const char *scale_unit, bool deprecated, const char *event_type_desc, @@ -37,7 +37,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) void print_symbol_events(const struct print_callbacks *print_cb, void *print_state, unsigned int type, const struct event_symbol *syms, unsigned int max); -void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state); +void metricgroup__print(const struct print_callbacks *print_cb, void *print_state); bool is_event_supported(u8 type, u64 config); #endif /* __PERF_PRINT_EVENTS_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 307ad6242a4e..6ab2eb551b6c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -75,12 +75,14 @@ int e_snprintf(char *str, size_t size, const char *format, ...) } static struct machine *host_machine; +static struct perf_env host_env; /* Initialize symbol maps and path of vmlinux/modules */ int init_probe_symbol_maps(bool user_only) { int ret; + perf_env__init(&host_env); symbol_conf.allow_aliases = true; ret = symbol__init(NULL); if (ret < 0) { @@ -94,7 +96,7 @@ int init_probe_symbol_maps(bool user_only) if (symbol_conf.vmlinux_name) pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); - host_machine = machine__new_host(); + host_machine = machine__new_host(&host_env); if (!host_machine) { pr_debug("machine__new_host() failed.\n"); symbol__exit(); @@ -111,6 +113,7 @@ void exit_probe_symbol_maps(void) machine__delete(host_machine); host_machine = NULL; symbol__exit(); + perf_env__exit(&host_env); } static struct ref_reloc_sym *kernel_get_ref_reloc_sym(struct map **pmap) @@ -502,7 +505,7 @@ static struct debuginfo *open_from_debuginfod(struct dso *dso, struct nsinfo *ns if (!c) return NULL; - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); fd = debuginfod_find_debuginfo(c, (const unsigned char *)sbuild_id, 0, &path); if (fd >= 0) @@ -1063,7 +1066,6 @@ static int sprint_line_description(char *sbuf, size_t size, struct line_range *l static int __show_line_range(struct line_range *lr, const char *module, bool user) { - struct build_id bid; int l = 1; struct int_node *ln; struct debuginfo *dinfo; @@ -1088,8 +1090,10 @@ static int __show_line_range(struct line_range *lr, const char *module, ret = -ENOENT; } if (dinfo->build_id) { + struct build_id bid; + build_id__init(&bid, dinfo->build_id, BUILD_ID_SIZE); - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); } debuginfo__delete(dinfo); if (ret == 0 || ret == -ENOENT) { diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index ec8ac242fedb..5069fb61f48c 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -448,10 +448,10 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target, if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) { target = DSO__NAME_KALLSYMS; is_kallsyms = true; - ret = sysfs__sprintf_build_id("/", sbuildid); + ret = sysfs__snprintf_build_id("/", sbuildid, sizeof(sbuildid)); } else { nsinfo__mountns_enter(nsi, &nsc); - ret = filename__sprintf_build_id(target, sbuildid); + ret = filename__snprintf_build_id(target, sbuildid, sizeof(sbuildid)); nsinfo__mountns_exit(&nsc); } diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3cc7c40f5097..5ffd97ee4898 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -848,7 +848,6 @@ static int probe_point_lazy_walker(const char *fname, int lineno, /* Find probe points from lazy pattern */ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { - struct build_id bid; char sbuild_id[SBUILD_ID_SIZE] = ""; int ret = 0; char *fpath; @@ -858,8 +857,10 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) comp_dir = cu_get_comp_dir(&pf->cu_die); if (pf->dbg->build_id) { + struct build_id bid; + build_id__init(&bid, pf->dbg->build_id, BUILD_ID_SIZE); - build_id__sprintf(&bid, sbuild_id); + build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); } ret = find_source_path(pf->fname, sbuild_id, comp_dir, &fpath); if (ret < 0) { diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 321c333877fa..ea77bea0306f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -10,6 +10,7 @@ #endif #include #include "callchain.h" +#include "counts.h" #include "evlist.h" #include "evsel.h" #include "event.h" @@ -18,6 +19,7 @@ #include "strbuf.h" #include "thread_map.h" #include "trace-event.h" +#include "metricgroup.h" #include "mmap.h" #include "util/sample.h" #include @@ -335,7 +337,6 @@ tracepoint_field(const struct pyrf_event *pe, struct tep_format_field *field) static PyObject* get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) { - const char *str = _PyUnicode_AsString(PyObject_Str(attr_name)); struct evsel *evsel = pevent->evsel; struct tep_event *tp_format = evsel__tp_format(evsel); struct tep_format_field *field; @@ -343,7 +344,18 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) if (IS_ERR_OR_NULL(tp_format)) return NULL; + PyObject *obj = PyObject_Str(attr_name); + if (obj == NULL) + return NULL; + + const char *str = PyUnicode_AsUTF8(obj); + if (str == NULL) { + Py_DECREF(obj); + return NULL; + } + field = tep_find_any_field(tp_format, str); + Py_DECREF(obj); return field ? tracepoint_field(pevent, field) : NULL; } #endif /* HAVE_LIBTRACEEVENT */ @@ -527,8 +539,10 @@ static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_cpu_map *pcpus = (void *)obj; - if (i >= perf_cpu_map__nr(pcpus->cpus)) + if (i >= perf_cpu_map__nr(pcpus->cpus)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } return Py_BuildValue("i", perf_cpu_map__cpu(pcpus->cpus, i).cpu); } @@ -566,14 +580,14 @@ struct pyrf_thread_map { static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, PyObject *args, PyObject *kwargs) { - static char *kwlist[] = { "pid", "tid", "uid", NULL }; - int pid = -1, tid = -1, uid = UINT_MAX; + static char *kwlist[] = { "pid", "tid", NULL }; + int pid = -1, tid = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", - kwlist, &pid, &tid, &uid)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", + kwlist, &pid, &tid)) return -1; - pthreads->threads = thread_map__new(pid, tid, uid); + pthreads->threads = thread_map__new(pid, tid); if (pthreads->threads == NULL) return -1; return 0; @@ -596,8 +610,10 @@ static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_thread_map *pthreads = (void *)obj; - if (i >= perf_thread_map__nr(pthreads->threads)) + if (i >= perf_thread_map__nr(pthreads->threads)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } return Py_BuildValue("i", perf_thread_map__pid(pthreads->threads, i)); } @@ -888,12 +904,38 @@ static PyObject *pyrf_evsel__threads(struct pyrf_evsel *pevsel) return (PyObject *)pthread_map; } +/* + * Ensure evsel's counts and prev_raw_counts are allocated, the latter + * used by tool PMUs to compute the cumulative count as expected by + * stat's process_counter_values. + */ +static int evsel__ensure_counts(struct evsel *evsel) +{ + int nthreads, ncpus; + + if (evsel->counts != NULL) + return 0; + + nthreads = perf_thread_map__nr(evsel->core.threads); + ncpus = perf_cpu_map__nr(evsel->core.cpus); + + evsel->counts = perf_counts__new(ncpus, nthreads); + if (evsel->counts == NULL) + return -ENOMEM; + + evsel->prev_raw_counts = perf_counts__new(ncpus, nthreads); + if (evsel->prev_raw_counts == NULL) + return -ENOMEM; + + return 0; +} + static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, PyObject *args, PyObject *kwargs) { struct evsel *evsel = &pevsel->evsel; int cpu = 0, cpu_idx, thread = 0, thread_idx; - struct perf_counts_values counts; + struct perf_counts_values *old_count, *new_count; struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values, &pyrf_counts_values__type); @@ -909,13 +951,27 @@ static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, return NULL; } thread_idx = perf_thread_map__idx(evsel->core.threads, thread); - if (cpu_idx < 0) { + if (thread_idx < 0) { PyErr_Format(PyExc_TypeError, "Thread %d is not part of evsel's threads", thread); return NULL; } - perf_evsel__read(&(evsel->core), cpu_idx, thread_idx, &counts); - count_values->values = counts; + + if (evsel__ensure_counts(evsel)) + return PyErr_NoMemory(); + + /* Set up pointers to the old and newly read counter values. */ + old_count = perf_counts(evsel->prev_raw_counts, cpu_idx, thread_idx); + new_count = perf_counts(evsel->counts, cpu_idx, thread_idx); + /* Update the value in evsel->counts. */ + evsel__read_counter(evsel, cpu_idx, thread_idx); + /* Copy the value and turn it into the delta from old_count. */ + count_values->values = *new_count; + count_values->values.val -= old_count->val; + count_values->values.ena -= old_count->ena; + count_values->values.run -= old_count->run; + /* Save the new count over the old_count for the next read. */ + *old_count = *new_count; return (PyObject *)count_values; } @@ -924,10 +980,7 @@ static PyObject *pyrf_evsel__str(PyObject *self) struct pyrf_evsel *pevsel = (void *)self; struct evsel *evsel = &pevsel->evsel; - if (!evsel->pmu) - return PyUnicode_FromFormat("evsel(%s)", evsel__name(evsel)); - - return PyUnicode_FromFormat("evsel(%s/%s/)", evsel->pmu->name, evsel__name(evsel)); + return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evsel__name(evsel)); } static PyMethodDef pyrf_evsel__methods[] = { @@ -1529,10 +1582,37 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) return (PyObject *)pevsel; } +static int evlist__pos(struct evlist *evlist, struct evsel *evsel) +{ + struct evsel *pos; + int idx = 0; + + evlist__for_each_entry(evlist, pos) { + if (evsel == pos) + return idx; + idx++; + } + return -1; +} + +static struct evsel *evlist__at(struct evlist *evlist, int idx) +{ + struct evsel *pos; + int idx2 = 0; + + evlist__for_each_entry(evlist, pos) { + if (idx == idx2) + return pos; + idx2++; + } + return NULL; +} + static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) { struct pyrf_evlist *pevlist = PyObject_New(struct pyrf_evlist, &pyrf_evlist__type); struct evsel *pos; + struct rb_node *node; if (!pevlist) return NULL; @@ -1544,6 +1624,39 @@ static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) evlist__add(&pevlist->evlist, &pevsel->evsel); } + evlist__for_each_entry(&pevlist->evlist, pos) { + struct evsel *leader = evsel__leader(pos); + + if (pos != leader) { + int idx = evlist__pos(evlist, leader); + + if (idx >= 0) + evsel__set_leader(pos, evlist__at(&pevlist->evlist, idx)); + else if (leader == NULL) + evsel__set_leader(pos, pos); + } + } + metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=*/NULL, + &pevlist->evlist.metric_events, + &evlist->metric_events); + for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); node; + node = rb_next(node)) { + struct metric_event *me = container_of(node, struct metric_event, nd); + struct list_head *mpos; + int idx = evlist__pos(evlist, me->evsel); + + if (idx >= 0) + me->evsel = evlist__at(&pevlist->evlist, idx); + list_for_each(mpos, &me->head) { + struct metric_expr *e = container_of(mpos, struct metric_expr, nd); + + for (int j = 0; e->metric_events[j]; j++) { + idx = evlist__pos(evlist, e->metric_events[j]); + if (idx >= 0) + e->metric_events[j] = evlist__at(&pevlist->evlist, idx); + } + } + } return (PyObject *)pevlist; } diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index 0ce52f0280b8..c17dbe232c54 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c @@ -1142,7 +1142,7 @@ int s390_cpumsf_process_auxtrace_info(union perf_event *event, sf->machine = &session->machines.host; /* No kvm support */ sf->auxtrace_type = auxtrace_info->type; sf->pmu_type = PERF_TYPE_RAW; - sf->machine_type = s390_cpumsf_get_type(session->evlist->env->cpuid); + sf->machine_type = s390_cpumsf_get_type(perf_session__env(session)->cpuid); sf->auxtrace.process_event = s390_cpumsf_process_event; sf->auxtrace.process_auxtrace_event = s390_cpumsf_process_auxtrace_event; diff --git a/tools/perf/util/sample-raw.c b/tools/perf/util/sample-raw.c index f3f6bd9d290e..bcf442574d6e 100644 --- a/tools/perf/util/sample-raw.c +++ b/tools/perf/util/sample-raw.c @@ -6,15 +6,16 @@ #include "env.h" #include "header.h" #include "sample-raw.h" +#include "session.h" /* * Check platform the perf data file was created on and perform platform * specific interpretation. */ -void evlist__init_trace_event_sample_raw(struct evlist *evlist) +void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env *env) { - const char *arch_pf = perf_env__arch(evlist->env); - const char *cpuid = perf_env__cpuid(evlist->env); + const char *arch_pf = perf_env__arch(env); + const char *cpuid = perf_env__cpuid(env); if (arch_pf && !strcmp("s390", arch_pf)) evlist->trace_event_sample_raw = evlist__s390_sample_raw; diff --git a/tools/perf/util/sample-raw.h b/tools/perf/util/sample-raw.h index ea01c5811503..896e9a87e373 100644 --- a/tools/perf/util/sample-raw.h +++ b/tools/perf/util/sample-raw.h @@ -11,5 +11,5 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, bool evlist__has_amd_ibs(struct evlist *evlist); void evlist__amd_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample); -void evlist__init_trace_event_sample_raw(struct evlist *evlist); +void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env *env); #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h index 0e96240052e9..fae834144ef4 100644 --- a/tools/perf/util/sample.h +++ b/tools/perf/util/sample.h @@ -104,10 +104,8 @@ struct perf_sample { u8 cpumode; u16 misc; u16 ins_lat; - union { - u16 p_stage_cyc; - u16 retire_lat; - }; + /** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */ + u16 weight3; bool no_hw_idx; /* No hw_idx collected in branch_stack */ char insn[MAX_INSN]; void *raw_data; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 520729e78965..6655c0bbe0d8 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -780,14 +780,13 @@ static void set_sym_in_dict(PyObject *dict, struct addr_location *al, const char *sym_field, const char *symoff_field, const char *map_pgoff) { - char sbuild_id[SBUILD_ID_SIZE]; - if (al->map) { + char sbuild_id[SBUILD_ID_SIZE]; struct dso *dso = map__dso(al->map); pydict_set_item_string_decref(dict, dso_field, _PyUnicode_FromString(dso__name(dso))); - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); pydict_set_item_string_decref(dict, dso_bid_field, _PyUnicode_FromString(sbuild_id)); pydict_set_item_string_decref(dict, dso_map_start, @@ -1238,7 +1237,7 @@ static int python_export_dso(struct db_export *dbe, struct dso *dso, char sbuild_id[SBUILD_ID_SIZE]; PyObject *t; - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); t = tuple_new(5); @@ -1306,7 +1305,7 @@ static void python_export_sample_table(struct db_export *dbe, tuple_set_d64(t, 0, es->db_id); tuple_set_d64(t, 1, es->evsel->db_id); - tuple_set_d64(t, 2, maps__machine(es->al->maps)->db_id); + tuple_set_d64(t, 2, maps__machine(thread__maps(es->al->thread))->db_id); tuple_set_d64(t, 3, thread__db_id(es->al->thread)); tuple_set_d64(t, 4, es->comm_db_id); tuple_set_d64(t, 5, es->dso_db_id); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a320672c264e..26ae078278cd 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "map_symbol.h" #include "branch.h" @@ -137,7 +138,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe, struct perf_session *__perf_session__new(struct perf_data *data, struct perf_tool *tool, - bool trace_event_repipe) + bool trace_event_repipe, + struct perf_env *host_env) { int ret = -ENOMEM; struct perf_session *session = zalloc(sizeof(*session)); @@ -176,7 +178,7 @@ struct perf_session *__perf_session__new(struct perf_data *data, perf_session__set_comm_exec(session); } - evlist__init_trace_event_sample_raw(session->evlist); + evlist__init_trace_event_sample_raw(session->evlist, &session->header.env); /* Open the directory data. */ if (data->is_dir) { @@ -190,8 +192,11 @@ struct perf_session *__perf_session__new(struct perf_data *data, symbol_conf.kallsyms_name = perf_data__kallsyms_name(data); } } else { - session->machines.host.env = &perf_env; + assert(host_env != NULL); + session->machines.host.env = host_env; } + if (session->evlist) + session->evlist->session = session; session->machines.host.single_address_space = perf_env__single_address_space(session->machines.host.env); @@ -1094,7 +1099,7 @@ static void dump_sample(struct evsel *evsel, union perf_event *event, printf("... weight: %" PRIu64 "", sample->weight); if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) { printf(",0x%"PRIx16"", sample->ins_lat); - printf(",0x%"PRIx16"", sample->p_stage_cyc); + printf(",0x%"PRIx16"", sample->weight3); } printf("\n"); } @@ -1491,6 +1496,9 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_FINISHED_INIT: err = tool->finished_init(session, event); break; + case PERF_RECORD_BPF_METADATA: + err = tool->bpf_metadata(session, event); + break; default: err = -EINVAL; break; @@ -2554,7 +2562,7 @@ int perf_session__cpu_bitmap(struct perf_session *session, { int i, err = -1; struct perf_cpu_map *map; - int nr_cpus = min(session->header.env.nr_cpus_avail, MAX_NR_CPUS); + int nr_cpus = min(perf_session__env(session)->nr_cpus_avail, MAX_NR_CPUS); struct perf_cpu cpu; for (i = 0; i < PERF_TYPE_MAX; ++i) { @@ -2743,3 +2751,8 @@ int perf_session__dsos_hit_all(struct perf_session *session) return 0; } + +struct perf_env *perf_session__env(struct perf_session *session) +{ + return &session->header.env; +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index db1c120a9e67..cf88d65a25cb 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -107,12 +107,13 @@ struct perf_tool; struct perf_session *__perf_session__new(struct perf_data *data, struct perf_tool *tool, - bool trace_event_repipe); + bool trace_event_repipe, + struct perf_env *host_env); static inline struct perf_session *perf_session__new(struct perf_data *data, struct perf_tool *tool) { - return __perf_session__new(data, tool, /*trace_event_repipe=*/false); + return __perf_session__new(data, tool, /*trace_event_repipe=*/false, /*host_env=*/NULL); } void perf_session__delete(struct perf_session *session); @@ -208,4 +209,6 @@ int perf_event__process_finished_round(const struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); +struct perf_env *perf_session__env(struct perf_session *session); + #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/sha1.c b/tools/perf/util/sha1.c new file mode 100644 index 000000000000..7032fa4ff3fd --- /dev/null +++ b/tools/perf/util/sha1.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SHA-1 message digest algorithm + * + * Copyright 2025 Google LLC + */ +#include +#include +#include +#include + +#include "sha1.h" + +#define SHA1_BLOCK_SIZE 64 + +static const u32 sha1_K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; + +#define SHA1_ROUND(i, a, b, c, d, e) \ + do { \ + if ((i) >= 16) \ + w[i] = rol32(w[(i) - 16] ^ w[(i) - 14] ^ w[(i) - 8] ^ \ + w[(i) - 3], \ + 1); \ + e += w[i] + rol32(a, 5) + sha1_K[(i) / 20]; \ + if ((i) < 20) \ + e += (b & (c ^ d)) ^ d; \ + else if ((i) < 40 || (i) >= 60) \ + e += b ^ c ^ d; \ + else \ + e += (c & d) ^ (b & (c ^ d)); \ + b = rol32(b, 30); \ + /* The new (a, b, c, d, e) is the old (e, a, b, c, d). */ \ + } while (0) + +#define SHA1_5ROUNDS(i) \ + do { \ + SHA1_ROUND((i) + 0, a, b, c, d, e); \ + SHA1_ROUND((i) + 1, e, a, b, c, d); \ + SHA1_ROUND((i) + 2, d, e, a, b, c); \ + SHA1_ROUND((i) + 3, c, d, e, a, b); \ + SHA1_ROUND((i) + 4, b, c, d, e, a); \ + } while (0) + +#define SHA1_20ROUNDS(i) \ + do { \ + SHA1_5ROUNDS((i) + 0); \ + SHA1_5ROUNDS((i) + 5); \ + SHA1_5ROUNDS((i) + 10); \ + SHA1_5ROUNDS((i) + 15); \ + } while (0) + +static void sha1_blocks(u32 h[5], const u8 *data, size_t nblocks) +{ + while (nblocks--) { + u32 a = h[0]; + u32 b = h[1]; + u32 c = h[2]; + u32 d = h[3]; + u32 e = h[4]; + u32 w[80]; + + for (int i = 0; i < 16; i++) + w[i] = get_unaligned_be32(&data[i * 4]); + SHA1_20ROUNDS(0); + SHA1_20ROUNDS(20); + SHA1_20ROUNDS(40); + SHA1_20ROUNDS(60); + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; + data += SHA1_BLOCK_SIZE; + } +} + +/* Calculate the SHA-1 message digest of the given data. */ +void sha1(const void *data, size_t len, u8 out[SHA1_DIGEST_SIZE]) +{ + u32 h[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, + 0xC3D2E1F0 }; + u8 final_data[2 * SHA1_BLOCK_SIZE] = { 0 }; + size_t final_len = len % SHA1_BLOCK_SIZE; + + sha1_blocks(h, data, len / SHA1_BLOCK_SIZE); + + memcpy(final_data, data + len - final_len, final_len); + final_data[final_len] = 0x80; + final_len = round_up(final_len + 9, SHA1_BLOCK_SIZE); + put_unaligned_be64((u64)len * 8, &final_data[final_len - 8]); + + sha1_blocks(h, final_data, final_len / SHA1_BLOCK_SIZE); + + for (int i = 0; i < 5; i++) + put_unaligned_be32(h[i], &out[i * 4]); +} diff --git a/tools/perf/util/sha1.h b/tools/perf/util/sha1.h new file mode 100644 index 000000000000..e92c9966e1d5 --- /dev/null +++ b/tools/perf/util/sha1.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include + +#define SHA1_DIGEST_SIZE 20 + +void sha1(const void *data, size_t len, u8 out[SHA1_DIGEST_SIZE]); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 45e654653960..f3a565b0e230 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1746,22 +1746,27 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) if (rc) return rc; /* - * Addresses with no major/minor numbers are assumed to be + * Addresses with no major/minor numbers or build ID are assumed to be * anonymous in userspace. Sort those on pid then address. * * The kernel and non-zero major/minor mapped areas are * assumed to be unity mapped. Sort those on address. */ + if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) { + const struct dso_id *dso_id = dso__id_const(l_dso); - if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && - (!(map__flags(l_map) & MAP_SHARED)) && !dso__id(l_dso)->maj && !dso__id(l_dso)->min && - !dso__id(l_dso)->ino && !dso__id(l_dso)->ino_generation) { - /* userspace anonymous */ + if (!dso_id->mmap2_valid) + dso_id = dso__id_const(r_dso); - if (thread__pid(left->thread) > thread__pid(right->thread)) - return -1; - if (thread__pid(left->thread) < thread__pid(right->thread)) - return 1; + if (!build_id__is_defined(&dso_id->build_id) && + (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) { + /* userspace anonymous */ + + if (thread__pid(left->thread) > thread__pid(right->thread)) + return -1; + if (thread__pid(left->thread) < thread__pid(right->thread)) + return 1; + } } addr: @@ -1786,6 +1791,7 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, if (he->mem_info) { struct map *map = mem_info__daddr(he->mem_info)->ms.map; struct dso *dso = map ? map__dso(map) : NULL; + const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty; addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl); ms = &mem_info__daddr(he->mem_info)->ms; @@ -1794,8 +1800,7 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && map && !(map__prot(map) & PROT_EXEC) && (map__flags(map) & MAP_SHARED) && - (dso__id(dso)->maj || dso__id(dso)->min || dso__id(dso)->ino || - dso__id(dso)->ino_generation)) + (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) level = 's'; else if (!map) level = 'X'; @@ -1879,21 +1884,20 @@ struct sort_entry sort_global_ins_lat = { static int64_t sort__p_stage_cyc_cmp(struct hist_entry *left, struct hist_entry *right) { - return left->p_stage_cyc - right->p_stage_cyc; + return left->weight3 - right->weight3; } static int hist_entry__global_p_stage_cyc_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*u", width, - he->p_stage_cyc * he->stat.nr_events); + return repsep_snprintf(bf, size, "%-*u", width, he->weight3 * he->stat.nr_events); } static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*u", width, he->p_stage_cyc); + return repsep_snprintf(bf, size, "%-*u", width, he->weight3); } struct sort_entry sort_local_p_stage_cyc = { @@ -2526,19 +2530,44 @@ struct sort_dimension { int taken; }; -int __weak arch_support_sort_key(const char *sort_key __maybe_unused) +static int arch_support_sort_key(const char *sort_key, struct perf_env *env) { + const char *arch = perf_env__arch(env); + + if (!strcmp("x86", arch) || !strcmp("powerpc", arch)) { + if (!strcmp(sort_key, "p_stage_cyc")) + return 1; + if (!strcmp(sort_key, "local_p_stage_cyc")) + return 1; + } return 0; } -const char * __weak arch_perf_header_entry(const char *se_header) +static const char *arch_perf_header_entry(const char *se_header, struct perf_env *env) { + const char *arch = perf_env__arch(env); + + if (!strcmp("x86", arch)) { + if (!strcmp(se_header, "Local Pipeline Stage Cycle")) + return "Local Retire Latency"; + else if (!strcmp(se_header, "Pipeline Stage Cycle")) + return "Retire Latency"; + } else if (!strcmp("powerpc", arch)) { + if (!strcmp(se_header, "Local INSTR Latency")) + return "Finish Cyc"; + else if (!strcmp(se_header, "INSTR Latency")) + return "Global Finish_cyc"; + else if (!strcmp(se_header, "Local Pipeline Stage Cycle")) + return "Dispatch Cyc"; + else if (!strcmp(se_header, "Pipeline Stage Cycle")) + return "Global Dispatch_cyc"; + } return se_header; } -static void sort_dimension_add_dynamic_header(struct sort_dimension *sd) +static void sort_dimension_add_dynamic_header(struct sort_dimension *sd, struct perf_env *env) { - sd->entry->se_header = arch_perf_header_entry(sd->entry->se_header); + sd->entry->se_header = arch_perf_header_entry(sd->entry->se_header, env); } #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } @@ -3590,7 +3619,7 @@ int hpp_dimension__add_output(unsigned col, bool implicit) } int sort_dimension__add(struct perf_hpp_list *list, const char *tok, - struct evlist *evlist, + struct evlist *evlist, struct perf_env *env, int level) { unsigned int i, j; @@ -3603,7 +3632,7 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, */ for (j = 0; j < ARRAY_SIZE(arch_specific_sort_keys); j++) { if (!strcmp(arch_specific_sort_keys[j], tok) && - !arch_support_sort_key(tok)) { + !arch_support_sort_key(tok, env)) { return 0; } } @@ -3616,7 +3645,7 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, for (j = 0; j < ARRAY_SIZE(dynamic_headers); j++) { if (sd->name && !strcmp(dynamic_headers[j], sd->name)) - sort_dimension_add_dynamic_header(sd); + sort_dimension_add_dynamic_header(sd, env); } if (sd->entry == &sort_parent && parent_pattern) { @@ -3712,13 +3741,13 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, } /* This should match with sort_dimension__add() above */ -static bool is_hpp_sort_key(const char *key) +static bool is_hpp_sort_key(const char *key, struct perf_env *env) { unsigned i; for (i = 0; i < ARRAY_SIZE(arch_specific_sort_keys); i++) { if (!strcmp(arch_specific_sort_keys[i], key) && - !arch_support_sort_key(key)) { + !arch_support_sort_key(key, env)) { return false; } } @@ -3740,7 +3769,7 @@ static bool is_hpp_sort_key(const char *key) } static int setup_sort_list(struct perf_hpp_list *list, char *str, - struct evlist *evlist) + struct evlist *evlist, struct perf_env *env) { char *tmp, *tok; int ret = 0; @@ -3769,7 +3798,7 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str, } if (*tok) { - if (is_hpp_sort_key(tok)) { + if (is_hpp_sort_key(tok, env)) { /* keep output (hpp) sort keys in the same level */ if (prev_was_hpp) { bool next_same = (level == next_level); @@ -3782,7 +3811,7 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str, prev_was_hpp = false; } - ret = sort_dimension__add(list, tok, evlist, level); + ret = sort_dimension__add(list, tok, evlist, env, level); if (ret == -EINVAL) { if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok))) ui__error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system"); @@ -3911,7 +3940,7 @@ static char *setup_overhead(char *keys) return keys; } -static int __setup_sorting(struct evlist *evlist) +static int __setup_sorting(struct evlist *evlist, struct perf_env *env) { char *str; const char *sort_keys; @@ -3951,7 +3980,7 @@ static int __setup_sorting(struct evlist *evlist) } } - ret = setup_sort_list(&perf_hpp_list, str, evlist); + ret = setup_sort_list(&perf_hpp_list, str, evlist, env); free(str); return ret; @@ -4187,16 +4216,16 @@ static int __setup_output_field(void) return ret; } -int setup_sorting(struct evlist *evlist) +int setup_sorting(struct evlist *evlist, struct perf_env *env) { int err; - err = __setup_sorting(evlist); + err = __setup_sorting(evlist, env); if (err < 0) return err; if (parent_pattern != default_parent_pattern) { - err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1); + err = sort_dimension__add(&perf_hpp_list, "parent", evlist, env, -1); if (err < 0) return err; } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index a742ab7f3c67..d7787958e06b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -6,6 +6,7 @@ #include "hist.h" struct option; +struct perf_env; extern regex_t parent_regex; extern const char *sort_order; @@ -130,7 +131,7 @@ extern struct sort_entry sort_thread; struct evlist; struct tep_handle; -int setup_sorting(struct evlist *evlist); +int setup_sorting(struct evlist *evlist, struct perf_env *env); int setup_output_field(void); void reset_output_field(void); void sort__setup_elide(FILE *fp); @@ -145,7 +146,7 @@ bool is_strict_order(const char *order); int hpp_dimension__add_output(unsigned col, bool implicit); void reset_dimensions(void); int sort_dimension__add(struct perf_hpp_list *list, const char *tok, - struct evlist *evlist, + struct evlist *evlist, struct perf_env *env, int level); int output_field_add(struct perf_hpp_list *list, const char *tok, int *level); int64_t diff --git a/tools/perf/util/spark.c b/tools/perf/util/spark.c index 70272a8b81a6..65ca253cc22e 100644 --- a/tools/perf/util/spark.c +++ b/tools/perf/util/spark.c @@ -1,9 +1,7 @@ -#include -#include -#include -#include +// SPDX-License-Identifier: GPL-2.0 #include "spark.h" -#include "stat.h" +#include +#include #define SPARK_SHIFT 8 diff --git a/tools/perf/util/spark.h b/tools/perf/util/spark.h index 25402d7d7a64..78597c38ef35 100644 --- a/tools/perf/util/spark.h +++ b/tools/perf/util/spark.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef SPARK_H #define SPARK_H 1 diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index f32d0d4f4bc9..3e3449e35dd4 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -524,12 +524,12 @@ static enum a2l_style addr2line_configure(struct child_process *a2l, const char style = LLVM; cached = true; lines = 1; - pr_debug("Detected LLVM addr2line style\n"); + pr_debug3("Detected LLVM addr2line style\n"); } else if (ch == '0') { style = GNU_BINUTILS; cached = true; lines = 3; - pr_debug("Detected binutils addr2line style\n"); + pr_debug3("Detected binutils addr2line style\n"); } else { if (!symbol_conf.disable_add2line_warn) { char *output = NULL; @@ -595,7 +595,7 @@ static int read_addr2line_record(struct io *io, if (io__getline(io, &line, &line_len) < 0 || !line_len) goto error; - pr_debug("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line); + pr_debug3("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line); if (style == LLVM && line_len == 2 && line[0] == ',') { /* Found the llvm-addr2line sentinel character. */ zfree(&line); @@ -641,7 +641,7 @@ static int read_addr2line_record(struct io *io, if (first && (io__getline(io, &line, &line_len) < 0 || !line_len)) goto error; - pr_debug("%s %s: addr2line read line: %s", __func__, dso_name, line); + pr_debug3("%s %s: addr2line read line: %s", __func__, dso_name, line); if (function != NULL) *function = strdup(strim(line)); @@ -652,7 +652,7 @@ static int read_addr2line_record(struct io *io, if (io__getline(io, &line, &line_len) < 0 || !line_len) goto error; - pr_debug("%s %s: addr2line filename:number : %s", __func__, dso_name, line); + pr_debug3("%s %s: addr2line filename:number : %s", __func__, dso_name, line); if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 && style == GNU_BINUTILS) { ret = 0; diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 729ad5cd52cb..a67b991f4e81 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -50,15 +50,15 @@ static int aggr_header_lens[] = { }; static const char *aggr_header_csv[] = { - [AGGR_CORE] = "core,cpus,", - [AGGR_CACHE] = "cache,cpus,", - [AGGR_CLUSTER] = "cluster,cpus,", - [AGGR_DIE] = "die,cpus,", - [AGGR_SOCKET] = "socket,cpus,", - [AGGR_NONE] = "cpu,", - [AGGR_THREAD] = "comm-pid,", - [AGGR_NODE] = "node,", - [AGGR_GLOBAL] = "" + [AGGR_CORE] = "core,ctrs,", + [AGGR_CACHE] = "cache,ctrs,", + [AGGR_CLUSTER] = "cluster,ctrs,", + [AGGR_DIE] = "die,ctrs,", + [AGGR_SOCKET] = "socket,ctrs,", + [AGGR_NONE] = "cpu,", + [AGGR_THREAD] = "comm-pid,", + [AGGR_NODE] = "node,", + [AGGR_GLOBAL] = "" }; static const char *aggr_header_std[] = { @@ -304,7 +304,7 @@ static void print_aggr_id_std(struct perf_stat_config *config, return; } - fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr); + fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, /*strlen("ctrs")*/ 4, aggr_nr); } static void print_aggr_id_csv(struct perf_stat_config *config, @@ -366,27 +366,27 @@ static void print_aggr_id_json(struct perf_stat_config *config, struct outstate { switch (config->aggr_mode) { case AGGR_CORE: - json_out(os, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d", + json_out(os, "\"core\" : \"S%d-D%d-C%d\", \"counters\" : %d", id.socket, id.die, id.core, aggr_nr); break; case AGGR_CACHE: - json_out(os, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d", + json_out(os, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"counters\" : %d", id.socket, id.die, id.cache_lvl, id.cache, aggr_nr); break; case AGGR_CLUSTER: - json_out(os, "\"cluster\" : \"S%d-D%d-CLS%d\", \"aggregate-number\" : %d", + json_out(os, "\"cluster\" : \"S%d-D%d-CLS%d\", \"counters\" : %d", id.socket, id.die, id.cluster, aggr_nr); break; case AGGR_DIE: - json_out(os, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d", + json_out(os, "\"die\" : \"S%d-D%d\", \"counters\" : %d", id.socket, id.die, aggr_nr); break; case AGGR_SOCKET: - json_out(os, "\"socket\" : \"S%d\", \"aggregate-number\" : %d", + json_out(os, "\"socket\" : \"S%d\", \"counters\" : %d", id.socket, aggr_nr); break; case AGGR_NODE: - json_out(os, "\"node\" : \"N%d\", \"aggregate-number\" : %d", + json_out(os, "\"node\" : \"N%d\", \"counters\" : %d", id.node, aggr_nr); break; case AGGR_NONE: @@ -899,12 +899,11 @@ static void printout(struct perf_stat_config *config, struct outstate *os, print_noise(config, os, counter, noise, /*before_metric=*/true); print_running(config, os, run, ena, /*before_metric=*/true); from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx, - &num, from, &out, - &config->metric_events); + &num, from, &out); } while (from != NULL); - } else - perf_stat__print_shadow_stats(config, counter, uval, aggr_idx, - &out, &config->metric_events); + } else { + perf_stat__print_shadow_stats(config, counter, uval, aggr_idx, &out); + } } else { pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/NULL, /*val=*/0); } @@ -1016,7 +1015,7 @@ static void print_counter_aggrdata(struct perf_stat_config *config, ena = aggr->counts.ena; run = aggr->counts.run; - if (perf_stat__skip_metric_event(counter, &config->metric_events, ena, run)) + if (perf_stat__skip_metric_event(counter, ena, run)) return; if (val == 0 && should_skip_zero_counter(config, counter, &id)) @@ -1275,10 +1274,7 @@ static void print_metric_headers(struct perf_stat_config *config, os.evsel = counter; - perf_stat__print_shadow_stats(config, counter, 0, - 0, - &out, - &config->metric_events); + perf_stat__print_shadow_stats(config, counter, 0, 0, &out); } if (!config->json_output) @@ -1317,7 +1313,7 @@ static void print_header_interval_std(struct perf_stat_config *config, case AGGR_CLUSTER: case AGGR_CACHE: case AGGR_CORE: - fprintf(output, "#%*s %-*s cpus", + fprintf(output, "#%*s %-*s ctrs", INTERVAL_LEN - 1, "time", aggr_header_lens[config->aggr_mode], aggr_header_std[config->aggr_mode]); diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index d83bda5824d2..abaf6b579bfc 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -635,14 +635,14 @@ void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config, int aggr_idx, int *num, void *from, - struct perf_stat_output_ctx *out, - struct rblist *metric_events) + struct perf_stat_output_ctx *out) { struct metric_event *me; struct metric_expr *mexp = from; void *ctxp = out->ctx; bool header_printed = false; const char *name = NULL; + struct rblist *metric_events = &evsel->evlist->metric_events; me = metricgroup__lookup(metric_events, evsel, false); if (me == NULL) @@ -683,8 +683,7 @@ void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config, void perf_stat__print_shadow_stats(struct perf_stat_config *config, struct evsel *evsel, double avg, int aggr_idx, - struct perf_stat_output_ctx *out, - struct rblist *metric_events) + struct perf_stat_output_ctx *out) { typedef void (*stat_print_function_t)(struct perf_stat_config *config, const struct evsel *evsel, @@ -735,7 +734,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config, } perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx, - &num, NULL, out, metric_events); + &num, NULL, out); if (num == 0) { print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, @@ -748,7 +747,6 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config, * if it's not running or not the metric event. */ bool perf_stat__skip_metric_event(struct evsel *evsel, - struct rblist *metric_events, u64 ena, u64 run) { if (!evsel->default_metricgroup) @@ -757,5 +755,5 @@ bool perf_stat__skip_metric_event(struct evsel *evsel, if (!ena || !run) return true; - return !metricgroup__lookup(metric_events, evsel, false); + return !metricgroup__lookup(&evsel->evlist->metric_events, evsel, false); } diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 355a7d5c8ab8..50b1a92d16df 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -526,7 +526,7 @@ static int evsel__merge_aggr_counters(struct evsel *evsel, struct evsel *alias) struct perf_counts_values *aggr_counts_a = &ps_a->aggr[i].counts; struct perf_counts_values *aggr_counts_b = &ps_b->aggr[i].counts; - /* NB: don't increase aggr.nr for aliases */ + ps_a->aggr[i].nr += ps_b->aggr[i].nr; aggr_counts_a->val += aggr_counts_b->val; aggr_counts_a->ena += aggr_counts_b->ena; @@ -769,8 +769,6 @@ int create_perf_stat_counter(struct evsel *evsel, attr->enable_on_exec = 1; } - if (target__has_cpu(target) && !target__has_per_thread(target)) - return evsel__open_per_cpu(evsel, evsel__cpus(evsel), cpu_map_idx); - - return evsel__open_per_thread(evsel, evsel->core.threads); + return evsel__open_per_cpu_and_thread(evsel, evsel__cpus(evsel), cpu_map_idx, + evsel->core.threads); } diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 1bcd7634bf47..4b0f14ae4e5f 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -7,7 +7,6 @@ #include #include #include "cpumap.h" -#include "rblist.h" #include "counts.h" struct perf_cpu_map; @@ -108,7 +107,6 @@ struct perf_stat_config { aggr_get_id_t aggr_get_id; struct cpu_aggr_map *cpus_aggr_map; u64 *walltime_run; - struct rblist metric_events; int ctl_fd; int ctl_fd_ack; bool ctl_fd_close; @@ -187,18 +185,14 @@ struct perf_stat_output_ctx { void perf_stat__print_shadow_stats(struct perf_stat_config *config, struct evsel *evsel, double avg, int aggr_idx, - struct perf_stat_output_ctx *out, - struct rblist *metric_events); -bool perf_stat__skip_metric_event(struct evsel *evsel, - struct rblist *metric_events, - u64 ena, u64 run); + struct perf_stat_output_ctx *out); +bool perf_stat__skip_metric_event(struct evsel *evsel, u64 ena, u64 run); void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config, struct evsel *evsel, int aggr_idx, int *num, void *from, - struct perf_stat_output_ctx *out, - struct rblist *metric_events); + struct perf_stat_output_ctx *out); int evlist__alloc_stats(struct perf_stat_config *config, struct evlist *evlist, bool alloc_raw); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 6d2c280a1730..1346fd180653 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -873,13 +873,17 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) #ifdef HAVE_LIBBFD_BUILDID_SUPPORT -static int read_build_id(const char *filename, struct build_id *bid) +static int read_build_id(const char *filename, struct build_id *bid, bool block) { size_t size = sizeof(bid->data); - int err = -1; + int err = -1, fd; bfd *abfd; - abfd = bfd_openr(filename, NULL); + fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (fd < 0) + return -1; + + abfd = bfd_fdopenr(filename, /*target=*/NULL, fd); if (!abfd) return -1; @@ -902,7 +906,7 @@ static int read_build_id(const char *filename, struct build_id *bid) #else // HAVE_LIBBFD_BUILDID_SUPPORT -static int read_build_id(const char *filename, struct build_id *bid) +static int read_build_id(const char *filename, struct build_id *bid, bool block) { size_t size = sizeof(bid->data); int fd, err = -1; @@ -911,7 +915,7 @@ static int read_build_id(const char *filename, struct build_id *bid) if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); if (fd < 0) goto out; @@ -934,7 +938,7 @@ static int read_build_id(const char *filename, struct build_id *bid) #endif // HAVE_LIBBFD_BUILDID_SUPPORT -int filename__read_build_id(const char *filename, struct build_id *bid) +int filename__read_build_id(const char *filename, struct build_id *bid, bool block) { struct kmod_path m = { .name = NULL, }; char path[PATH_MAX]; @@ -958,9 +962,10 @@ int filename__read_build_id(const char *filename, struct build_id *bid) } close(fd); filename = path; + block = true; } - err = read_build_id(filename, bid); + err = read_build_id(filename, bid, block); if (m.comp) unlink(filename); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index c73fe2e09fe9..41e4ebe5eac5 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -86,13 +85,10 @@ int filename__read_debuglink(const char *filename __maybe_unused, /* * Just try PT_NOTE header otherwise fails */ -int filename__read_build_id(const char *filename, struct build_id *bid) +int filename__read_build_id(const char *filename, struct build_id *bid, bool block) { - FILE *fp; - int ret = -1; + int fd, ret = -1; bool need_swap = false, elf32; - u8 e_ident[EI_NIDENT]; - int i; union { struct { Elf32_Ehdr ehdr32; @@ -103,28 +99,27 @@ int filename__read_build_id(const char *filename, struct build_id *bid) Elf64_Phdr *phdr64; }; } hdrs; - void *phdr; - size_t phdr_size; - void *buf = NULL; - size_t buf_size = 0; + void *phdr, *buf = NULL; + ssize_t phdr_size, ehdr_size, buf_size = 0; - fp = fopen(filename, "r"); - if (fp == NULL) + fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (fd < 0) return -1; - if (fread(e_ident, sizeof(e_ident), 1, fp) != 1) + if (read(fd, hdrs.ehdr32.e_ident, EI_NIDENT) != EI_NIDENT) goto out; - if (memcmp(e_ident, ELFMAG, SELFMAG) || - e_ident[EI_VERSION] != EV_CURRENT) + if (memcmp(hdrs.ehdr32.e_ident, ELFMAG, SELFMAG) || + hdrs.ehdr32.e_ident[EI_VERSION] != EV_CURRENT) goto out; - need_swap = check_need_swap(e_ident[EI_DATA]); - elf32 = e_ident[EI_CLASS] == ELFCLASS32; + need_swap = check_need_swap(hdrs.ehdr32.e_ident[EI_DATA]); + elf32 = hdrs.ehdr32.e_ident[EI_CLASS] == ELFCLASS32; + ehdr_size = (elf32 ? sizeof(hdrs.ehdr32) : sizeof(hdrs.ehdr64)) - EI_NIDENT; - if (fread(elf32 ? (void *)&hdrs.ehdr32 : (void *)&hdrs.ehdr64, - elf32 ? sizeof(hdrs.ehdr32) : sizeof(hdrs.ehdr64), - 1, fp) != 1) + if (read(fd, + (elf32 ? (void *)&hdrs.ehdr32 : (void *)&hdrs.ehdr64) + EI_NIDENT, + ehdr_size) != ehdr_size) goto out; if (need_swap) { @@ -138,14 +133,18 @@ int filename__read_build_id(const char *filename, struct build_id *bid) hdrs.ehdr64.e_phnum = bswap_16(hdrs.ehdr64.e_phnum); } } - phdr_size = elf32 ? hdrs.ehdr32.e_phentsize * hdrs.ehdr32.e_phnum - : hdrs.ehdr64.e_phentsize * hdrs.ehdr64.e_phnum; + if ((elf32 && hdrs.ehdr32.e_phentsize != sizeof(Elf32_Phdr)) || + (!elf32 && hdrs.ehdr64.e_phentsize != sizeof(Elf64_Phdr))) + goto out; + + phdr_size = elf32 ? sizeof(Elf32_Phdr) * hdrs.ehdr32.e_phnum + : sizeof(Elf64_Phdr) * hdrs.ehdr64.e_phnum; phdr = malloc(phdr_size); if (phdr == NULL) goto out; - fseek(fp, elf32 ? hdrs.ehdr32.e_phoff : hdrs.ehdr64.e_phoff, SEEK_SET); - if (fread(phdr, phdr_size, 1, fp) != 1) + lseek(fd, elf32 ? hdrs.ehdr32.e_phoff : hdrs.ehdr64.e_phoff, SEEK_SET); + if (read(fd, phdr, phdr_size) != phdr_size) goto out_free; if (elf32) @@ -153,8 +152,8 @@ int filename__read_build_id(const char *filename, struct build_id *bid) else hdrs.phdr64 = phdr; - for (i = 0; i < elf32 ? hdrs.ehdr32.e_phnum : hdrs.ehdr64.e_phnum; i++) { - size_t p_filesz; + for (int i = 0; i < (elf32 ? hdrs.ehdr32.e_phnum : hdrs.ehdr64.e_phnum); i++) { + ssize_t p_filesz; if (need_swap) { if (elf32) { @@ -180,8 +179,8 @@ int filename__read_build_id(const char *filename, struct build_id *bid) goto out_free; buf = tmp; } - fseek(fp, elf32 ? hdrs.phdr32[i].p_offset : hdrs.phdr64[i].p_offset, SEEK_SET); - if (fread(buf, p_filesz, 1, fp) != 1) + lseek(fd, elf32 ? hdrs.phdr32[i].p_offset : hdrs.phdr64[i].p_offset, SEEK_SET); + if (read(fd, buf, p_filesz) != p_filesz) goto out_free; ret = read_build_id(buf, p_filesz, bid, need_swap); @@ -194,7 +193,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid) free(buf); free(phdr); out: - fclose(fp); + close(fd); return ret; } @@ -317,14 +316,14 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, struct symsrc *runtime_ss __maybe_unused, int kmodule __maybe_unused) { - struct build_id bid; + struct build_id bid = { .size = 0, }; int ret; ret = fd__is_64_bit(ss->fd); if (ret >= 0) RC_CHK_ACCESS(dso)->is_64_bit = ret; - if (filename__read_build_id(ss->name, &bid) > 0) + if (filename__read_build_id(ss->name, &bid, /*block=*/true) > 0) dso__set_build_id(dso, &bid); return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 8b30c6f16a9e..3fed54de5401 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -631,7 +631,7 @@ void dso__sort_by_name(struct dso *dso) { mutex_lock(dso__lock(dso)); if (!dso__sorted_by_name(dso)) { - size_t len; + size_t len = 0; dso__set_symbol_names(dso, symbols__sort_by_name(dso__symbols(dso), &len)); if (dso__symbol_names(dso)) { @@ -1422,6 +1422,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, goto out_err; } } + map__zput(new_node->map); free(new_node); } @@ -1812,7 +1813,6 @@ int dso__load(struct dso *dso, struct map *map) struct symsrc *syms_ss = NULL, *runtime_ss = NULL; bool kmod; bool perfmap; - struct build_id bid; struct nscookie nsc; char newmapname[PATH_MAX]; const char *map_path = dso__long_name(dso); @@ -1869,12 +1869,14 @@ int dso__load(struct dso *dso, struct map *map) /* * Read the build id if possible. This is required for - * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work + * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work. Don't block in case path + * isn't for a regular file. */ - if (!dso__has_build_id(dso) && - is_regular_file(dso__long_name(dso))) { + if (!dso__has_build_id(dso)) { + struct build_id bid = { .size = 0, }; + __symbol__join_symfs(name, PATH_MAX, dso__long_name(dso)); - if (filename__read_build_id(name, &bid) > 0) + if (filename__read_build_id(name, &bid, /*block=*/false) > 0) dso__set_build_id(dso, &bid); } @@ -2121,7 +2123,7 @@ static bool filename__readable(const char *file) static char *dso__find_kallsyms(struct dso *dso, struct map *map) { - struct build_id bid; + struct build_id bid = { .size = 0, }; char sbuild_id[SBUILD_ID_SIZE]; bool is_host = false; char path[PATH_MAX]; @@ -2151,7 +2153,7 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) goto proc_kallsyms; } - build_id__sprintf(dso__bid(dso), sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); /* Find kallsyms in build-id cache with kcore */ scnprintf(path, sizeof(path), "%s/%s/%s", diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 3fb5d146d9b1..347106218799 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -140,7 +140,7 @@ struct symbol *dso__next_symbol(struct symbol *sym); enum dso_type dso__type_fd(int fd); -int filename__read_build_id(const char *filename, struct build_id *id); +int filename__read_build_id(const char *filename, struct build_id *id, bool block); int sysfs__read_build_id(const char *filename, struct build_id *bid); int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index cd9aa82c7d5a..7a80d2c14d9b 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -43,7 +43,7 @@ struct symbol_conf { report_individual_block, inline_name, disable_add2line_warn, - buildid_mmap2, + no_buildid_mmap2, guest_code, lazy_load_kernel_maps, keep_exited_threads, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 2fc4d0537840..fcd1fd13c30e 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -368,11 +368,11 @@ static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event, struct machine *machine, bool is_kernel) { - struct build_id bid; + struct build_id bid = { .size = 0, }; struct nsinfo *nsi; struct nscookie nc; struct dso *dso = NULL; - struct dso_id id; + struct dso_id dso_id = dso_id_empty; int rc; if (is_kernel) { @@ -380,12 +380,18 @@ static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event, goto out; } - id.maj = event->maj; - id.min = event->min; - id.ino = event->ino; - id.ino_generation = event->ino_generation; + if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { + build_id__init(&dso_id.build_id, event->build_id, event->build_id_size); + } else { + dso_id.maj = event->maj; + dso_id.min = event->min; + dso_id.ino = event->ino; + dso_id.ino_generation = event->ino_generation; + dso_id.mmap2_valid = true; + dso_id.mmap2_ino_generation_valid = true; + }; - dso = dsos__findnew_id(&machine->dsos, event->filename, &id); + dso = dsos__findnew_id(&machine->dsos, event->filename, &dso_id); if (dso && dso__has_build_id(dso)) { bid = *dso__bid(dso); rc = 0; @@ -395,7 +401,7 @@ static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event, nsi = nsinfo__new(event->pid); nsinfo__mountns_enter(nsi, &nc); - rc = filename__read_build_id(event->filename, &bid) > 0 ? 0 : -1; + rc = filename__read_build_id(event->filename, &bid, /*block=*/false) > 0 ? 0 : -1; nsinfo__mountns_exit(&nc); nsinfo__put(nsi); @@ -526,7 +532,7 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool, event->mmap2.pid = tgid; event->mmap2.tid = pid; - if (symbol_conf.buildid_mmap2) + if (!symbol_conf.no_buildid_mmap2) perf_record_mmap2__read_build_id(&event->mmap2, machine, false); if (perf_tool__process_synth_event(tool, event, machine, process) != 0) { @@ -684,7 +690,7 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) return 0; dso = map__dso(map); - if (symbol_conf.buildid_mmap2) { + if (!symbol_conf.no_buildid_mmap2) { size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); event->mmap2.header.type = PERF_RECORD_MMAP2; event->mmap2.header.size = (sizeof(event->mmap2) - @@ -728,9 +734,9 @@ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__han .process = process, .machine = machine, }; - size_t size = symbol_conf.buildid_mmap2 - ? sizeof(args.event->mmap2) - : sizeof(args.event->mmap); + size_t size = symbol_conf.no_buildid_mmap2 + ? sizeof(args.event->mmap) + : sizeof(args.event->mmap2); args.event = zalloc(size + machine->id_hdr_size); if (args.event == NULL) { @@ -1118,8 +1124,8 @@ static int __perf_event__synthesize_kernel_mmap(const struct perf_tool *tool, struct machine *machine) { union perf_event *event; - size_t size = symbol_conf.buildid_mmap2 ? - sizeof(event->mmap2) : sizeof(event->mmap); + size_t size = symbol_conf.no_buildid_mmap2 ? + sizeof(event->mmap) : sizeof(event->mmap2); struct map *map = machine__kernel_map(machine); struct kmap *kmap; int err; @@ -1153,7 +1159,7 @@ static int __perf_event__synthesize_kernel_mmap(const struct perf_tool *tool, event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; } - if (symbol_conf.buildid_mmap2) { + if (!symbol_conf.no_buildid_mmap2) { size = snprintf(event->mmap2.filename, sizeof(event->mmap2.filename), "%s%s", machine->mmap_name, kmap->ref_reloc_sym->name) + 1; size = PERF_ALIGN(size, sizeof(u64)); @@ -1567,10 +1573,16 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, return result; } -void __weak arch_perf_synthesize_sample_weight(const struct perf_sample *data, +static void perf_synthesize_sample_weight(const struct perf_sample *data, __u64 *array, u64 type __maybe_unused) { *array = data->weight; + + if (type & PERF_SAMPLE_WEIGHT_STRUCT) { + *array &= 0xffffffff; + *array |= ((u64)data->ins_lat << 32); + *array |= ((u64)data->weight3 << 48); + } } static __u64 *copy_read_group_values(__u64 *array, __u64 read_format, @@ -1730,7 +1742,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo } if (type & PERF_SAMPLE_WEIGHT_TYPE) { - arch_perf_synthesize_sample_weight(sample, array, type); + perf_synthesize_sample_weight(sample, array, type); array++; } @@ -2045,7 +2057,7 @@ int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struc int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process) { - struct synthesize_cpu_map_data syn_data = { .map = evsel->core.own_cpus }; + struct synthesize_cpu_map_data syn_data = { .map = evsel->core.pmu_cpus }; struct perf_record_event_update *ev; int err; @@ -2126,7 +2138,7 @@ int perf_event__synthesize_extra_attr(const struct perf_tool *tool, struct evlis } } - if (evsel->core.own_cpus) { + if (evsel->core.pmu_cpus) { err = perf_event__synthesize_event_update_cpus(tool, evsel, process); if (err < 0) { pr_err("Couldn't synthesize evsel cpus.\n"); @@ -2248,7 +2260,9 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, memset(&ev, 0, len); - ev.build_id.size = min(bid->size, sizeof(ev.build_id.build_id)); + ev.build_id.size = bid->size; + if (ev.build_id.size > sizeof(ev.build_id.build_id)) + ev.build_id.size = sizeof(ev.build_id.build_id); memcpy(ev.build_id.build_id, bid->data, ev.build_id.size); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc | PERF_RECORD_MISC_BUILD_ID_SIZE; @@ -2308,7 +2322,9 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, ev.mmap2.len = len; ev.mmap2.pgoff = pgoff; - ev.mmap2.build_id_size = min(bid->size, sizeof(ev.mmap2.build_id)); + ev.mmap2.build_id_size = bid->size; + if (ev.mmap2.build_id_size > sizeof(ev.mmap2.build_id)) + ev.build_id.size = sizeof(ev.mmap2.build_id); memcpy(ev.mmap2.build_id, bid->data, ev.mmap2.build_id_size); ev.mmap2.prot = prot; diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index b9c936b5cfeb..ee29615d68e5 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -92,6 +92,8 @@ int perf_event__synthesize_threads(const struct perf_tool *tool, perf_event__han int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, struct evlist *evlist, perf_event__handler_t process); int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); pid_t perf_event__synthesize_comm(const struct perf_tool *tool, union perf_event *event, pid_t pid, perf_event__handler_t process, struct machine *machine); +void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, + perf_event__handler_t process); int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 0f383418e3df..8cf71bea295a 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -28,20 +28,6 @@ enum target_errno target__validate(struct target *target) ret = TARGET_ERRNO__PID_OVERRIDE_CPU; } - /* UID and PID are mutually exclusive */ - if (target->tid && target->uid_str) { - target->uid_str = NULL; - if (ret == TARGET_ERRNO__SUCCESS) - ret = TARGET_ERRNO__PID_OVERRIDE_UID; - } - - /* UID and CPU are mutually exclusive */ - if (target->uid_str && target->cpu_list) { - target->cpu_list = NULL; - if (ret == TARGET_ERRNO__SUCCESS) - ret = TARGET_ERRNO__UID_OVERRIDE_CPU; - } - /* PID and SYSTEM are mutually exclusive */ if (target->tid && target->system_wide) { target->system_wide = false; @@ -49,13 +35,6 @@ enum target_errno target__validate(struct target *target) ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM; } - /* UID and SYSTEM are mutually exclusive */ - if (target->uid_str && target->system_wide) { - target->system_wide = false; - if (ret == TARGET_ERRNO__SUCCESS) - ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; - } - /* BPF and CPU are mutually exclusive */ if (target->bpf_str && target->cpu_list) { target->cpu_list = NULL; @@ -70,13 +49,6 @@ enum target_errno target__validate(struct target *target) ret = TARGET_ERRNO__BPF_OVERRIDE_PID; } - /* BPF and UID are mutually exclusive */ - if (target->bpf_str && target->uid_str) { - target->uid_str = NULL; - if (ret == TARGET_ERRNO__SUCCESS) - ret = TARGET_ERRNO__BPF_OVERRIDE_UID; - } - /* BPF and THREADS are mutually exclusive */ if (target->bpf_str && target->per_thread) { target->per_thread = false; @@ -94,15 +66,13 @@ enum target_errno target__validate(struct target *target) return ret; } -enum target_errno target__parse_uid(struct target *target) +uid_t parse_uid(const char *str) { struct passwd pwd, *result; char buf[1024]; - const char *str = target->uid_str; - target->uid = UINT_MAX; if (str == NULL) - return TARGET_ERRNO__SUCCESS; + return UINT_MAX; /* Try user name first */ getpwnam_r(str, &pwd, buf, sizeof(buf), &result); @@ -115,16 +85,15 @@ enum target_errno target__parse_uid(struct target *target) int uid = strtol(str, &endptr, 10); if (*endptr != '\0') - return TARGET_ERRNO__INVALID_UID; + return UINT_MAX; getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); if (result == NULL) - return TARGET_ERRNO__USER_NOT_FOUND; + return UINT_MAX; } - target->uid = result->pw_uid; - return TARGET_ERRNO__SUCCESS; + return result->pw_uid; } /* @@ -132,20 +101,14 @@ enum target_errno target__parse_uid(struct target *target) */ static const char *target__error_str[] = { "PID/TID switch overriding CPU", - "PID/TID switch overriding UID", - "UID switch overriding CPU", "PID/TID switch overriding SYSTEM", - "UID switch overriding SYSTEM", "SYSTEM/CPU switch overriding PER-THREAD", "BPF switch overriding CPU", "BPF switch overriding PID/TID", - "BPF switch overriding UID", "BPF switch overriding THREAD", - "Invalid User: %s", - "Problems obtaining information for user %s", }; -int target__strerror(struct target *target, int errnum, +int target__strerror(struct target *target __maybe_unused, int errnum, char *buf, size_t buflen) { int idx; @@ -170,11 +133,6 @@ int target__strerror(struct target *target, int errnum, snprintf(buf, buflen, "%s", msg); break; - case TARGET_ERRNO__INVALID_UID: - case TARGET_ERRNO__USER_NOT_FOUND: - snprintf(buf, buflen, msg, target->uid_str); - break; - default: /* cannot reach here */ break; diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index 2ee2cc30340f..84ebb9c940c6 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -9,9 +9,7 @@ struct target { const char *pid; const char *tid; const char *cpu_list; - const char *uid_str; const char *bpf_str; - uid_t uid; bool system_wide; bool uses_mmap; bool default_per_cpu; @@ -36,31 +34,24 @@ enum target_errno { /* for target__validate() */ TARGET_ERRNO__PID_OVERRIDE_CPU = __TARGET_ERRNO__START, - TARGET_ERRNO__PID_OVERRIDE_UID, - TARGET_ERRNO__UID_OVERRIDE_CPU, TARGET_ERRNO__PID_OVERRIDE_SYSTEM, - TARGET_ERRNO__UID_OVERRIDE_SYSTEM, TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD, TARGET_ERRNO__BPF_OVERRIDE_CPU, TARGET_ERRNO__BPF_OVERRIDE_PID, - TARGET_ERRNO__BPF_OVERRIDE_UID, TARGET_ERRNO__BPF_OVERRIDE_THREAD, - /* for target__parse_uid() */ - TARGET_ERRNO__INVALID_UID, - TARGET_ERRNO__USER_NOT_FOUND, - __TARGET_ERRNO__END, }; enum target_errno target__validate(struct target *target); -enum target_errno target__parse_uid(struct target *target); + +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) { - return target->tid || target->pid || target->uid_str; + return target->tid || target->pid; } static inline bool target__has_cpu(struct target *target) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index ffb48cc2103f..aa9c58bbf9d3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -41,6 +41,7 @@ int thread__init_maps(struct thread *thread, struct machine *machine) } struct thread *thread__new(pid_t pid, pid_t tid) + NO_THREAD_SAFETY_ANALYSIS /* Allocation/creation is inherently single threaded. */ { RC_STRUCT(thread) *_thread = zalloc(sizeof(*_thread)); struct thread *thread; @@ -200,7 +201,8 @@ int thread__set_namespaces(struct thread *thread, u64 timestamp, return ret; } -struct comm *thread__comm(struct thread *thread) +static struct comm *__thread__comm(struct thread *thread) + SHARED_LOCKS_REQUIRED(thread__comm_lock(thread)) { if (list_empty(thread__comm_list(thread))) return NULL; @@ -208,16 +210,30 @@ struct comm *thread__comm(struct thread *thread) return list_first_entry(thread__comm_list(thread), struct comm, list); } +struct comm *thread__comm(struct thread *thread) +{ + struct comm *res = NULL; + + down_read(thread__comm_lock(thread)); + res = __thread__comm(thread); + up_read(thread__comm_lock(thread)); + return res; +} + struct comm *thread__exec_comm(struct thread *thread) { struct comm *comm, *last = NULL, *second_last = NULL; + down_read(thread__comm_lock(thread)); list_for_each_entry(comm, thread__comm_list(thread), list) { - if (comm->exec) + if (comm->exec) { + up_read(thread__comm_lock(thread)); return comm; + } second_last = last; last = comm; } + up_read(thread__comm_lock(thread)); /* * 'last' with no start time might be the parent's comm of a synthesized @@ -233,8 +249,9 @@ struct comm *thread__exec_comm(struct thread *thread) static int ____thread__set_comm(struct thread *thread, const char *str, u64 timestamp, bool exec) + EXCLUSIVE_LOCKS_REQUIRED(thread__comm_lock(thread)) { - struct comm *new, *curr = thread__comm(thread); + struct comm *new, *curr = __thread__comm(thread); /* Override the default :tid entry */ if (!thread__comm_set(thread)) { @@ -285,8 +302,9 @@ int thread__set_comm_from_proc(struct thread *thread) } static const char *__thread__comm_str(struct thread *thread) + SHARED_LOCKS_REQUIRED(thread__comm_lock(thread)) { - const struct comm *comm = thread__comm(thread); + const struct comm *comm = __thread__comm(thread); if (!comm) return NULL; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 2b90bbed7a61..310eaea344bb 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -236,16 +236,17 @@ static inline struct rw_semaphore *thread__namespaces_lock(struct thread *thread return &RC_CHK_ACCESS(thread)->namespaces_lock; } -static inline struct list_head *thread__comm_list(struct thread *thread) -{ - return &RC_CHK_ACCESS(thread)->comm_list; -} - static inline struct rw_semaphore *thread__comm_lock(struct thread *thread) { return &RC_CHK_ACCESS(thread)->comm_lock; } +static inline struct list_head *thread__comm_list(struct thread *thread) + SHARED_LOCKS_REQUIRED(thread__comm_lock(thread)) +{ + return &RC_CHK_ACCESS(thread)->comm_list; +} + static inline u64 thread__db_id(const struct thread *thread) { return RC_CHK_ACCESS(thread)->db_id; diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index b5f12390c355..ca193c1374ed 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -72,7 +72,7 @@ struct perf_thread_map *thread_map__new_by_tid(pid_t tid) return threads; } -static struct perf_thread_map *__thread_map__new_all_cpus(uid_t uid) +static struct perf_thread_map *thread_map__new_all_cpus(void) { DIR *proc; int max_threads = 32, items, i; @@ -98,15 +98,6 @@ static struct perf_thread_map *__thread_map__new_all_cpus(uid_t uid) if (*end) /* only interested in proper numerical dirents */ continue; - snprintf(path, sizeof(path), "/proc/%s", dirent->d_name); - - if (uid != UINT_MAX) { - struct stat st; - - if (stat(path, &st) != 0 || st.st_uid != uid) - continue; - } - snprintf(path, sizeof(path), "/proc/%d/task", pid); items = scandir(path, &namelist, filter, NULL); if (items <= 0) { @@ -157,24 +148,11 @@ static struct perf_thread_map *__thread_map__new_all_cpus(uid_t uid) goto out_closedir; } -struct perf_thread_map *thread_map__new_all_cpus(void) -{ - return __thread_map__new_all_cpus(UINT_MAX); -} - -struct perf_thread_map *thread_map__new_by_uid(uid_t uid) -{ - return __thread_map__new_all_cpus(uid); -} - -struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) +struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid) { if (pid != -1) return thread_map__new_by_pid(pid); - if (tid == -1 && uid != UINT_MAX) - return thread_map__new_by_uid(uid); - return thread_map__new_by_tid(tid); } @@ -289,15 +267,11 @@ struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str) goto out; } -struct perf_thread_map *thread_map__new_str(const char *pid, const char *tid, - uid_t uid, bool all_threads) +struct perf_thread_map *thread_map__new_str(const char *pid, const char *tid, bool all_threads) { if (pid) return thread_map__new_by_pid_str(pid); - if (!tid && uid != UINT_MAX) - return thread_map__new_by_uid(uid); - if (all_threads) return thread_map__new_all_cpus(); diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 00ec05fc1656..fc16d87f32fb 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -11,13 +11,11 @@ struct perf_record_thread_map; struct perf_thread_map *thread_map__new_dummy(void); struct perf_thread_map *thread_map__new_by_pid(pid_t pid); struct perf_thread_map *thread_map__new_by_tid(pid_t tid); -struct perf_thread_map *thread_map__new_by_uid(uid_t uid); -struct perf_thread_map *thread_map__new_all_cpus(void); -struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); +struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid); struct perf_thread_map *thread_map__new_event(struct perf_record_thread_map *event); struct perf_thread_map *thread_map__new_str(const char *pid, - const char *tid, uid_t uid, bool all_threads); + const char *tid, bool all_threads); struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str); diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 37bd8ac63b01..e83c7ababc2a 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -1,12 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "data.h" #include "debug.h" +#include "event.h" #include "header.h" #include "session.h" #include "stat.h" #include "tool.h" #include "tsc.h" +#include #include +#include #include #ifdef HAVE_ZSTD_SUPPORT @@ -17,7 +20,7 @@ static int perf_session__process_compressed_event(struct perf_session *session, void *src; size_t decomp_size, src_size; u64 decomp_last_rem = 0; - size_t mmap_len, decomp_len = session->header.env.comp_mmap_len; + size_t mmap_len, decomp_len = perf_session__env(session)->comp_mmap_len; struct decomp *decomp, *decomp_last = session->active_decomp->decomp_last; if (decomp_last) { @@ -237,6 +240,16 @@ static int perf_session__process_compressed_event_stub(struct perf_session *sess return 0; } +static int perf_event__process_bpf_metadata_stub(struct perf_session *perf_session __maybe_unused, + union perf_event *event) +{ + if (dump_trace) + perf_event__fprintf_bpf_metadata(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__init(struct perf_tool *tool, bool ordered_events) { tool->ordered_events = ordered_events; @@ -293,6 +306,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->compressed = perf_session__process_compressed_event_stub; #endif tool->finished_init = process_event_op2_stub; + tool->bpf_metadata = perf_event__process_bpf_metadata_stub; } bool perf_tool__compressed_is_stub(const struct perf_tool *tool) diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index db1c7642b0d1..18b76ff0f26a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -77,7 +77,8 @@ struct perf_tool { stat, stat_round, feature, - finished_init; + finished_init, + bpf_metadata; event_op4 compressed; event_op3 auxtrace; bool ordered_events; diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c index 4630b8cc8e52..d99e699e646d 100644 --- a/tools/perf/util/tool_pmu.c +++ b/tools/perf/util/tool_pmu.c @@ -332,7 +332,7 @@ static bool has_pmem(void) return has_pmem; } -bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result) +bool tool_pmu__read_event(enum tool_pmu_event ev, struct evsel *evsel, u64 *result) { const struct cpu_topology *topology; @@ -347,18 +347,60 @@ bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result) return true; case TOOL_PMU__EVENT_NUM_CPUS: - *result = cpu__max_present_cpu().cpu; + if (!evsel || perf_cpu_map__is_empty(evsel->core.cpus)) { + /* No evsel to be specific to. */ + *result = cpu__max_present_cpu().cpu; + } else if (!perf_cpu_map__has_any_cpu(evsel->core.cpus)) { + /* Evsel just has specific CPUs. */ + *result = perf_cpu_map__nr(evsel->core.cpus); + } else { + /* + * "Any CPU" event that can be scheduled on any CPU in + * the PMU's cpumask. The PMU cpumask should be saved in + * pmu_cpus. If not present fall back to max. + */ + if (!perf_cpu_map__is_empty(evsel->core.pmu_cpus)) + *result = perf_cpu_map__nr(evsel->core.pmu_cpus); + else + *result = cpu__max_present_cpu().cpu; + } return true; case TOOL_PMU__EVENT_NUM_CPUS_ONLINE: { struct perf_cpu_map *online = cpu_map__online(); - if (online) { + if (!online) + return false; + + if (!evsel || perf_cpu_map__is_empty(evsel->core.cpus)) { + /* No evsel to be specific to. */ *result = perf_cpu_map__nr(online); - perf_cpu_map__put(online); - return true; + } else if (!perf_cpu_map__has_any_cpu(evsel->core.cpus)) { + /* Evsel just has specific CPUs. */ + struct perf_cpu_map *tmp = + perf_cpu_map__intersect(online, evsel->core.cpus); + + *result = perf_cpu_map__nr(tmp); + perf_cpu_map__put(tmp); + } else { + /* + * "Any CPU" event that can be scheduled on any CPU in + * the PMU's cpumask. The PMU cpumask should be saved in + * pmu_cpus, if not present then just the online cpu + * mask. + */ + if (!perf_cpu_map__is_empty(evsel->core.pmu_cpus)) { + struct perf_cpu_map *tmp = + perf_cpu_map__intersect(online, evsel->core.pmu_cpus); + + *result = perf_cpu_map__nr(tmp); + perf_cpu_map__put(tmp); + } else { + *result = perf_cpu_map__nr(online); + } } - return false; + perf_cpu_map__put(online); + return true; } case TOOL_PMU__EVENT_NUM_DIES: topology = online_topology(); @@ -417,7 +459,7 @@ int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); val = 0; if (cpu_map_idx == 0 && thread == 0) { - if (!tool_pmu__read_event(ev, &val)) { + if (!tool_pmu__read_event(ev, evsel, &val)) { count->lost++; val = 0; } diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h index c6ad1dd90a56..d642e7d73910 100644 --- a/tools/perf/util/tool_pmu.h +++ b/tools/perf/util/tool_pmu.h @@ -34,7 +34,7 @@ enum tool_pmu_event tool_pmu__str_to_event(const char *str); bool tool_pmu__skip_event(const char *name); int tool_pmu__num_skip_events(void); -bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result); +bool tool_pmu__read_event(enum tool_pmu_event ev, struct evsel *evsel, u64 *result); u64 tool_pmu__cpu_slots_per_cycle(void); diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 4db3d1bd686c..b06e10a116bb 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -88,9 +88,9 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) else if (target->tid) ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", target->tid); - else if (target->uid_str != NULL) + else if (top->uid_str != NULL) ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", - target->uid_str); + top->uid_str); else ret += SNPRINTF(bf + ret, size - ret, " (all"); diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 4c5588dbb131..04ff926846be 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -48,6 +48,7 @@ struct perf_top { const char *sym_filter; float min_percent; unsigned int nr_threads_synthesize; + const char *uid_str; struct { struct ordered_events *in; diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c new file mode 100644 index 000000000000..e7534a973247 --- /dev/null +++ b/tools/perf/util/tp_pmu.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "tp_pmu.h" +#include "pmus.h" +#include +#include +#include +#include +#include +#include + +int tp_pmu__id(const char *sys, const char *name) +{ + char *tp_dir = get_events_file(sys); + char path[PATH_MAX]; + int id, err; + + if (!tp_dir) + return -1; + + scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name); + put_events_file(tp_dir); + err = filename__read_int(path, &id); + if (err) + return err; + + return id; +} + + +int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb) +{ + char *evt_path; + struct io_dirent64 *evt_ent; + struct io_dir evt_dir; + int ret = 0; + + evt_path = get_events_file(sys); + if (!evt_path) + return -errno; + + io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); + if (evt_dir.dirfd < 0) { + ret = -errno; + put_events_file(evt_path); + return ret; + } + put_events_file(evt_path); + + while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) { + if (!strcmp(evt_ent->d_name, ".") + || !strcmp(evt_ent->d_name, "..") + || !strcmp(evt_ent->d_name, "enable") + || !strcmp(evt_ent->d_name, "filter")) + continue; + + ret = cb(state, sys, evt_ent->d_name); + if (ret) + break; + } + close(evt_dir.dirfd); + return ret; +} + +int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb) +{ + struct io_dirent64 *events_ent; + struct io_dir events_dir; + int ret = 0; + char *events_dir_path = get_tracing_file("events"); + + if (!events_dir_path) + return -errno; + + io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); + if (events_dir.dirfd < 0) { + ret = -errno; + put_events_file(events_dir_path); + return ret; + } + put_events_file(events_dir_path); + + while (!ret && (events_ent = io_dir__readdir(&events_dir))) { + if (!strcmp(events_ent->d_name, ".") || + !strcmp(events_ent->d_name, "..") || + !strcmp(events_ent->d_name, "enable") || + !strcmp(events_ent->d_name, "header_event") || + !strcmp(events_ent->d_name, "header_page")) + continue; + + ret = cb(state, events_ent->d_name); + if (ret) + break; + } + close(events_dir.dirfd); + return ret; +} + +bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu) +{ + return pmu->type == PERF_TYPE_TRACEPOINT; +} + +struct for_each_event_args { + void *state; + pmu_event_callback cb; + const struct perf_pmu *pmu; +}; + +static int for_each_event_cb(void *state, const char *sys_name, const char *evt_name) +{ + struct for_each_event_args *args = state; + char name[2 * FILENAME_MAX + 2]; + /* 16 possible hex digits and 22 other characters and \0. */ + char encoding[16 + 22]; + char *format = NULL; + size_t format_size; + struct pmu_event_info info = { + .pmu = args->pmu, + .pmu_name = args->pmu->name, + .event_type_desc = "Tracepoint event", + }; + char *tp_dir = get_events_file(sys_name); + char path[PATH_MAX]; + int id, err; + + if (!tp_dir) + return -1; + + scnprintf(path, sizeof(path), "%s/%s/id", tp_dir, evt_name); + err = filename__read_int(path, &id); + if (err == 0) { + snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%x/", id); + info.encoding_desc = encoding; + } + + scnprintf(path, sizeof(path), "%s/%s/format", tp_dir, evt_name); + put_events_file(tp_dir); + err = filename__read_str(path, &format, &format_size); + if (err == 0) { + info.long_desc = format; + for (size_t i = 0 ; i < format_size; i++) { + /* Swap tabs to spaces due to some rendering issues. */ + if (format[i] == '\t') + format[i] = ' '; + } + } + snprintf(name, sizeof(name), "%s:%s", sys_name, evt_name); + info.name = name; + err = args->cb(args->state, &info); + free(format); + return err; +} + +static int for_each_event_sys_cb(void *state, const char *sys_name) +{ + return tp_pmu__for_each_tp_event(sys_name, state, for_each_event_cb); +} + +int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb) +{ + struct for_each_event_args args = { + .state = state, + .cb = cb, + .pmu = pmu, + }; + + return tp_pmu__for_each_tp_sys(&args, for_each_event_sys_cb); +} + +static int num_events_cb(void *state, const char *sys_name __maybe_unused, + const char *evt_name __maybe_unused) +{ + size_t *count = state; + + (*count)++; + return 0; +} + +static int num_events_sys_cb(void *state, const char *sys_name) +{ + return tp_pmu__for_each_tp_event(sys_name, state, num_events_cb); +} + +size_t tp_pmu__num_events(struct perf_pmu *pmu __maybe_unused) +{ + size_t count = 0; + + tp_pmu__for_each_tp_sys(&count, num_events_sys_cb); + return count; +} + +bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name) +{ + char *dup_name, *colon; + int id; + + colon = strchr(name, ':'); + if (colon == NULL) + return false; + + dup_name = strdup(name); + if (!dup_name) + return false; + + colon = dup_name + (colon - name); + *colon = '\0'; + id = tp_pmu__id(dup_name, colon + 1); + free(dup_name); + return id >= 0; +} diff --git a/tools/perf/util/tp_pmu.h b/tools/perf/util/tp_pmu.h new file mode 100644 index 000000000000..30456bd6943d --- /dev/null +++ b/tools/perf/util/tp_pmu.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __TP_PMU_H +#define __TP_PMU_H + +#include "pmu.h" + +typedef int (*tp_sys_callback)(void *state, const char *sys_name); +typedef int (*tp_event_callback)(void *state, const char *sys_name, const char *evt_name); + +int tp_pmu__id(const char *sys, const char *name); +int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb); +int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb); + +bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu); +int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb); +size_t tp_pmu__num_events(struct perf_pmu *pmu); +bool tp_pmu__have_event(struct perf_pmu *pmu, const char *name); + +#endif /* __TP_PMU_H */ diff --git a/tools/perf/util/trace_augment.h b/tools/perf/util/trace_augment.h index 57a3e5045937..4f729bc67753 100644 --- a/tools/perf/util/trace_augment.h +++ b/tools/perf/util/trace_augment.h @@ -1,6 +1,66 @@ #ifndef TRACE_AUGMENT_H #define TRACE_AUGMENT_H -#define TRACE_AUG_MAX_BUF 32 /* for buffer augmentation in perf trace */ +#include + +struct bpf_program; +struct evlist; + +#ifdef HAVE_BPF_SKEL + +int augmented_syscalls__prepare(void); +int augmented_syscalls__create_bpf_output(struct evlist *evlist); +void augmented_syscalls__setup_bpf_output(void); +int augmented_syscalls__set_filter_pids(unsigned int nr, pid_t *pids); +int augmented_syscalls__get_map_fds(int *enter_fd, int *exit_fd, int *beauty_fd); +struct bpf_program *augmented_syscalls__find_by_title(const char *name); +struct bpf_program *augmented_syscalls__unaugmented(void); +void augmented_syscalls__cleanup(void); + +#else /* !HAVE_BPF_SKEL */ + +static inline int augmented_syscalls__prepare(void) +{ + return -1; +} + +static inline int augmented_syscalls__create_bpf_output(struct evlist *evlist __maybe_unused) +{ + return -1; +} + +static inline void augmented_syscalls__setup_bpf_output(void) +{ +} + +static inline int augmented_syscalls__set_filter_pids(unsigned int nr __maybe_unused, + pid_t *pids __maybe_unused) +{ + return 0; +} + +static inline int augmented_syscalls__get_map_fds(int *enter_fd __maybe_unused, + int *exit_fd __maybe_unused, + int *beauty_fd __maybe_unused) +{ + return -1; +} + +static inline struct bpf_program * +augmented_syscalls__find_by_title(const char *name __maybe_unused) +{ + return NULL; +} + +static inline struct bpf_program *augmented_syscalls__unaugmented(void) +{ + return NULL; +} + +static inline void augmented_syscalls__cleanup(void) +{ +} + +#endif /* HAVE_BPF_SKEL */ #endif diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 793d11832694..ae70fb56a057 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -84,8 +84,11 @@ static int __report_module(struct addr_location *al, u64 ip, char filename[PATH_MAX]; __symbol__join_symfs(filename, sizeof(filename), dso__long_name(dso)); - mod = dwfl_report_elf(ui->dwfl, dso__short_name(dso), filename, -1, - base, false); + /* Don't hang up on device files like /dev/dri/renderD128. */ + if (is_regular_file(filename)) { + mod = dwfl_report_elf(ui->dwfl, dso__short_name(dso), filename, -1, + base, false); + } } if (!mod) { char filename[PATH_MAX]; diff --git a/tools/power/cpupower/bindings/python/Makefile b/tools/power/cpupower/bindings/python/Makefile index 81db39a03efb..4527cd732b42 100644 --- a/tools/power/cpupower/bindings/python/Makefile +++ b/tools/power/cpupower/bindings/python/Makefile @@ -4,20 +4,22 @@ # This Makefile expects you have already run `make install-lib` in the lib # directory for the bindings to be created. -CC := gcc +CC ?= gcc +# CFLAGS ?= +LDFLAGS ?= -lcpupower HAVE_SWIG := $(shell if which swig >/dev/null 2>&1; then echo 1; else echo 0; fi) HAVE_PYCONFIG := $(shell if which python-config >/dev/null 2>&1; then echo 1; else echo 0; fi) -PY_INCLUDE = $(firstword $(shell python-config --includes)) -INSTALL_DIR = $(shell python3 -c "import site; print(site.getsitepackages()[0])") +PY_INCLUDE ?= $(firstword $(shell python-config --includes)) +INSTALL_DIR ?= $(shell python3 -c "import site; print(site.getsitepackages()[0])") all: _raw_pylibcpupower.so _raw_pylibcpupower.so: raw_pylibcpupower_wrap.o - $(CC) -shared -lcpupower raw_pylibcpupower_wrap.o -o _raw_pylibcpupower.so + $(CC) -shared $(LDFLAGS) raw_pylibcpupower_wrap.o -o _raw_pylibcpupower.so raw_pylibcpupower_wrap.o: raw_pylibcpupower_wrap.c - $(CC) -fPIC -c raw_pylibcpupower_wrap.c $(PY_INCLUDE) + $(CC) $(CFLAGS) $(PY_INCLUDE) -fPIC -c raw_pylibcpupower_wrap.c raw_pylibcpupower_wrap.c: raw_pylibcpupower.swg ifeq ($(HAVE_SWIG),0) diff --git a/tools/power/cpupower/man/cpupower-set.1 b/tools/power/cpupower/man/cpupower-set.1 index 500653ef98c7..8ac82b6f9189 100644 --- a/tools/power/cpupower/man/cpupower-set.1 +++ b/tools/power/cpupower/man/cpupower-set.1 @@ -81,10 +81,11 @@ Refer to the AMD P-State kernel documentation for further information. .RE .PP -\-\-turbo\-boost, \-t +\-\-turbo\-boost, \-\-boost, \-t .RS 4 -This option is used to enable or disable the turbo boost feature on -supported Intel and AMD processors. +This option is used to enable or disable the boost feature on +supported Intel and AMD processors, and other boost supported systems. +(The --boost option is an alias for the --turbo-boost option) This option takes as parameter either \fB1\fP to enable, or \fB0\fP to disable the feature. diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index fc750e127404..7d3732f5f2f6 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -128,7 +128,7 @@ static int get_boost_mode_x86(unsigned int cpu) /* ToDo: Make this more global */ unsigned long pstates[MAX_HW_PSTATES] = {0,}; - ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states); + ret = cpufreq_has_x86_boost_support(cpu, &support, &active, &b_states); if (ret) { printf(_("Error while evaluating Boost Capabilities" " on CPU %d -- are you root?\n"), cpu); @@ -204,6 +204,18 @@ static int get_boost_mode_x86(unsigned int cpu) return 0; } +static int get_boost_mode_generic(unsigned int cpu) +{ + bool active; + + if (!cpufreq_has_generic_boost_support(&active)) { + printf(_(" boost state support:\n")); + printf(_(" Active: %s\n"), active ? _("yes") : _("no")); + } + + return 0; +} + /* --boost / -b */ static int get_boost_mode(unsigned int cpu) @@ -214,6 +226,8 @@ static int get_boost_mode(unsigned int cpu) cpupower_cpu_info.vendor == X86_VENDOR_HYGON || cpupower_cpu_info.vendor == X86_VENDOR_INTEL) return get_boost_mode_x86(cpu); + else + get_boost_mode_generic(cpu); freqs = cpufreq_get_boost_frequencies(cpu); if (freqs) { diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c index 0677b58374ab..c2117e5650dd 100644 --- a/tools/power/cpupower/utils/cpupower-set.c +++ b/tools/power/cpupower/utils/cpupower-set.c @@ -21,6 +21,7 @@ static struct option set_opts[] = { {"epp", required_argument, NULL, 'e'}, {"amd-pstate-mode", required_argument, NULL, 'm'}, {"turbo-boost", required_argument, NULL, 't'}, + {"boost", required_argument, NULL, 't'}, { }, }; @@ -62,8 +63,8 @@ int cmd_set(int argc, char **argv) params.params = 0; /* parameter parsing */ - while ((ret = getopt_long(argc, argv, "b:e:m:", - set_opts, NULL)) != -1) { + while ((ret = getopt_long(argc, argv, "b:e:m:t:", + set_opts, NULL)) != -1) { switch (ret) { case 'b': if (params.perf_bias) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 95749b8ee475..82ea62bdf5a2 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -103,6 +103,9 @@ extern struct cpupower_cpu_info cpupower_cpu_info; /* cpuid and cpuinfo helpers **************************/ +int cpufreq_has_generic_boost_support(bool *active); +int cpupower_set_turbo_boost(int turbo_boost); + /* X86 ONLY ****************************************/ #if defined(__i386__) || defined(__x86_64__) @@ -118,7 +121,6 @@ extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); extern int cpupower_set_epp(unsigned int cpu, char *epp); extern int cpupower_set_amd_pstate_mode(char *mode); -extern int cpupower_set_turbo_boost(int turbo_boost); /* Read/Write msr ****************************/ @@ -139,8 +141,8 @@ extern int decode_pstates(unsigned int cpu, int boost_states, /* AMD HW pstate decoding **************************/ -extern int cpufreq_has_boost_support(unsigned int cpu, int *support, - int *active, int * states); +int cpufreq_has_x86_boost_support(unsigned int cpu, int *support, + int *active, int *states); /* AMD P-State stuff **************************/ bool cpupower_amd_pstate_enabled(void); @@ -181,13 +183,11 @@ static inline int cpupower_set_epp(unsigned int cpu, char *epp) { return -1; }; static inline int cpupower_set_amd_pstate_mode(char *mode) { return -1; }; -static inline int cpupower_set_turbo_boost(int turbo_boost) -{ return -1; }; /* Read/Write msr ****************************/ -static inline int cpufreq_has_boost_support(unsigned int cpu, int *support, - int *active, int * states) +static inline int cpufreq_has_x86_boost_support(unsigned int cpu, int *support, + int *active, int *states) { return -1; } static inline bool cpupower_amd_pstate_enabled(void) diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index 76e461ff4f74..166dc1e470ea 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -8,15 +8,14 @@ #include "helpers/helpers.h" #include "helpers/sysfs.h" #include "cpufreq.h" +#include "cpupower_intern.h" #if defined(__i386__) || defined(__x86_64__) -#include "cpupower_intern.h" - #define MSR_AMD_HWCR 0xc0010015 -int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, - int *states) +int cpufreq_has_x86_boost_support(unsigned int cpu, int *support, int *active, + int *states) { int ret; unsigned long long val; @@ -124,24 +123,6 @@ int cpupower_set_amd_pstate_mode(char *mode) return 0; } -int cpupower_set_turbo_boost(int turbo_boost) -{ - char path[SYSFS_PATH_MAX]; - char linebuf[2] = {}; - - snprintf(path, sizeof(path), PATH_TO_CPU "cpufreq/boost"); - - if (!is_valid_path(path)) - return -1; - - snprintf(linebuf, sizeof(linebuf), "%d", turbo_boost); - - if (cpupower_write_sysfs(path, linebuf, 2) <= 0) - return -1; - - return 0; -} - bool cpupower_amd_pstate_enabled(void) { char *driver = cpufreq_get_driver(0); @@ -160,6 +141,39 @@ bool cpupower_amd_pstate_enabled(void) #endif /* #if defined(__i386__) || defined(__x86_64__) */ +int cpufreq_has_generic_boost_support(bool *active) +{ + char path[SYSFS_PATH_MAX]; + char linebuf[2] = {}; + unsigned long val; + char *endp; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpufreq/boost"); + + if (!is_valid_path(path)) + return -EACCES; + + if (cpupower_read_sysfs(path, linebuf, 2) <= 0) + return -EINVAL; + + val = strtoul(linebuf, &endp, 0); + if (endp == linebuf || errno == ERANGE) + return -EINVAL; + + switch (val) { + case 0: + *active = false; + break; + case 1: + *active = true; + break; + default: + return -EINVAL; + } + + return 0; +} + /* get_cpustate * * Gather the information of all online CPUs into bitmask struct @@ -259,3 +273,21 @@ void print_speed(unsigned long speed, int no_rounding) } } } + +int cpupower_set_turbo_boost(int turbo_boost) +{ + char path[SYSFS_PATH_MAX]; + char linebuf[2] = {}; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpufreq/boost"); + + if (!is_valid_path(path)) + return -1; + + snprintf(linebuf, sizeof(linebuf), "%d", turbo_boost); + + if (cpupower_write_sysfs(path, linebuf, 2) <= 0) + return -1; + + return 0; +} diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index ad493157f826..e8b3841d5c0f 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c @@ -121,10 +121,8 @@ void print_header(int topology_depth) switch (topology_depth) { case TOPOLOGY_DEPTH_PKG: printf(" PKG|"); - break; case TOPOLOGY_DEPTH_CORE: printf("CORE|"); - break; case TOPOLOGY_DEPTH_CPU: printf(" CPU|"); break; @@ -167,10 +165,8 @@ void print_results(int topology_depth, int cpu) switch (topology_depth) { case TOPOLOGY_DEPTH_PKG: printf("%4d|", cpu_top.core_info[cpu].pkg); - break; case TOPOLOGY_DEPTH_CORE: printf("%4d|", cpu_top.core_info[cpu].core); - break; case TOPOLOGY_DEPTH_CPU: printf("%4d|", cpu_top.core_info[cpu].cpu); break; diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index 73b6b10cbdd2..5ae02c3d5b64 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c @@ -240,9 +240,9 @@ static int mperf_stop(void) int cpu; for (cpu = 0; cpu < cpu_count; cpu++) { - mperf_measure_stats(cpu); - mperf_get_tsc(&tsc_at_measure_end[cpu]); clock_gettime(CLOCK_REALTIME, &time_end[cpu]); + mperf_get_tsc(&tsc_at_measure_end[cpu]); + mperf_measure_stats(cpu); } return 0; diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index fb11108aaf42..3340def58d01 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 @@ -47,10 +47,11 @@ name as necessary to disambiguate it from others is necessary. Note that option MSRs are read as 64-bits, u32 truncates the displayed value to 32-bits. default: u64 - format: {\fBraw\fP | \fBdelta\fP | \fBpercent\fP} + format: {\fBraw\fP | \fBdelta\fP | \fBpercent\fP | \fBaverage\fP} 'raw' shows the MSR contents in hex. 'delta' shows the difference in values during the measurement interval. 'percent' shows the delta as a percentage of the cycles elapsed. + 'average' similar to raw, but also averaged for node/package summaries (or when using -S). default: delta name: "name_string" @@ -186,6 +187,14 @@ The system configuration dump (if --quiet is not used) is followed by statistics .PP \fBSAMAMHz\fP Instantaneous snapshot of what sysfs presents at the end of the measurement interval. From /sys/class/drm/card0/gt/gt1/rps_act_freq_mhz or /sys/class/drm/card0/device/tile0/gtN/freq0/act_freq depending on the graphics driver being used. .PP +\fBTotl%C0\fP Weighted percentage of time that CPUs are busy. If N CPUs are busy during an interval, the percentage is N * 100%. +.PP +\fBAny%C0\fP Percentage of time that at least one CPU is busy. +.PP +\fBGFX%C0\fP Percentage of time that at least one GFX compute engine is busy. +.PP +\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute enginer is busy. +.PP \fBPkg%pc2, Pkg%pc3, Pkg%pc6, Pkg%pc7\fP percentage residency in hardware package idle states. These numbers are from hardware residency counters. .PP \fBPkgWatt\fP Watts consumed by the whole package. diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 5230e072e414..72a280e7a9d5 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -67,6 +67,7 @@ #include #include #include +#include #define UNUSED(x) (void)(x) @@ -194,6 +195,7 @@ struct msr_counter bic[] = { { 0x0, "APIC", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "X2APIC", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "Die", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "L3", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "GFXAMHz", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "IPC", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "CoreThr", NULL, 0, 0, 0, NULL, 0 }, @@ -209,91 +211,238 @@ struct msr_counter bic[] = { { 0x0, "pct_idle", NULL, 0, 0, 0, NULL, 0 }, }; -#define MAX_BIC (sizeof(bic) / sizeof(struct msr_counter)) -#define BIC_USEC (1ULL << 0) -#define BIC_TOD (1ULL << 1) -#define BIC_Package (1ULL << 2) -#define BIC_Node (1ULL << 3) -#define BIC_Avg_MHz (1ULL << 4) -#define BIC_Busy (1ULL << 5) -#define BIC_Bzy_MHz (1ULL << 6) -#define BIC_TSC_MHz (1ULL << 7) -#define BIC_IRQ (1ULL << 8) -#define BIC_SMI (1ULL << 9) -#define BIC_cpuidle (1ULL << 10) -#define BIC_CPU_c1 (1ULL << 11) -#define BIC_CPU_c3 (1ULL << 12) -#define BIC_CPU_c6 (1ULL << 13) -#define BIC_CPU_c7 (1ULL << 14) -#define BIC_ThreadC (1ULL << 15) -#define BIC_CoreTmp (1ULL << 16) -#define BIC_CoreCnt (1ULL << 17) -#define BIC_PkgTmp (1ULL << 18) -#define BIC_GFX_rc6 (1ULL << 19) -#define BIC_GFXMHz (1ULL << 20) -#define BIC_Pkgpc2 (1ULL << 21) -#define BIC_Pkgpc3 (1ULL << 22) -#define BIC_Pkgpc6 (1ULL << 23) -#define BIC_Pkgpc7 (1ULL << 24) -#define BIC_Pkgpc8 (1ULL << 25) -#define BIC_Pkgpc9 (1ULL << 26) -#define BIC_Pkgpc10 (1ULL << 27) -#define BIC_CPU_LPI (1ULL << 28) -#define BIC_SYS_LPI (1ULL << 29) -#define BIC_PkgWatt (1ULL << 30) -#define BIC_CorWatt (1ULL << 31) -#define BIC_GFXWatt (1ULL << 32) -#define BIC_PkgCnt (1ULL << 33) -#define BIC_RAMWatt (1ULL << 34) -#define BIC_PKG__ (1ULL << 35) -#define BIC_RAM__ (1ULL << 36) -#define BIC_Pkg_J (1ULL << 37) -#define BIC_Cor_J (1ULL << 38) -#define BIC_GFX_J (1ULL << 39) -#define BIC_RAM_J (1ULL << 40) -#define BIC_Mod_c6 (1ULL << 41) -#define BIC_Totl_c0 (1ULL << 42) -#define BIC_Any_c0 (1ULL << 43) -#define BIC_GFX_c0 (1ULL << 44) -#define BIC_CPUGFX (1ULL << 45) -#define BIC_Core (1ULL << 46) -#define BIC_CPU (1ULL << 47) -#define BIC_APIC (1ULL << 48) -#define BIC_X2APIC (1ULL << 49) -#define BIC_Die (1ULL << 50) -#define BIC_GFXACTMHz (1ULL << 51) -#define BIC_IPC (1ULL << 52) -#define BIC_CORE_THROT_CNT (1ULL << 53) -#define BIC_UNCORE_MHZ (1ULL << 54) -#define BIC_SAM_mc6 (1ULL << 55) -#define BIC_SAMMHz (1ULL << 56) -#define BIC_SAMACTMHz (1ULL << 57) -#define BIC_Diec6 (1ULL << 58) -#define BIC_SysWatt (1ULL << 59) -#define BIC_Sys_J (1ULL << 60) -#define BIC_NMI (1ULL << 61) -#define BIC_CPU_c1e (1ULL << 62) -#define BIC_pct_idle (1ULL << 63) +/* n.b. bic_names must match the order in bic[], above */ +enum bic_names { + BIC_USEC, + BIC_TOD, + BIC_Package, + BIC_Node, + BIC_Avg_MHz, + BIC_Busy, + BIC_Bzy_MHz, + BIC_TSC_MHz, + BIC_IRQ, + BIC_SMI, + BIC_cpuidle, + BIC_CPU_c1, + BIC_CPU_c3, + BIC_CPU_c6, + BIC_CPU_c7, + BIC_ThreadC, + BIC_CoreTmp, + BIC_CoreCnt, + BIC_PkgTmp, + BIC_GFX_rc6, + BIC_GFXMHz, + BIC_Pkgpc2, + BIC_Pkgpc3, + BIC_Pkgpc6, + BIC_Pkgpc7, + BIC_Pkgpc8, + BIC_Pkgpc9, + BIC_Pkgpc10, + BIC_CPU_LPI, + BIC_SYS_LPI, + BIC_PkgWatt, + BIC_CorWatt, + BIC_GFXWatt, + BIC_PkgCnt, + BIC_RAMWatt, + BIC_PKG__, + BIC_RAM__, + BIC_Pkg_J, + BIC_Cor_J, + BIC_GFX_J, + BIC_RAM_J, + BIC_Mod_c6, + BIC_Totl_c0, + BIC_Any_c0, + BIC_GFX_c0, + BIC_CPUGFX, + BIC_Core, + BIC_CPU, + BIC_APIC, + BIC_X2APIC, + BIC_Die, + BIC_L3, + BIC_GFXACTMHz, + BIC_IPC, + BIC_CORE_THROT_CNT, + BIC_UNCORE_MHZ, + BIC_SAM_mc6, + BIC_SAMMHz, + BIC_SAMACTMHz, + BIC_Diec6, + BIC_SysWatt, + BIC_Sys_J, + BIC_NMI, + BIC_CPU_c1e, + BIC_pct_idle, + MAX_BIC +}; -#define BIC_GROUP_TOPOLOGY (BIC_Package | BIC_Node | BIC_CoreCnt | BIC_PkgCnt | BIC_Core | BIC_CPU | BIC_Die) -#define BIC_GROUP_THERMAL_PWR (BIC_CoreTmp | BIC_PkgTmp | BIC_PkgWatt | BIC_CorWatt | BIC_GFXWatt | BIC_RAMWatt | BIC_PKG__ | BIC_RAM__ | BIC_SysWatt) -#define BIC_GROUP_FREQUENCY (BIC_Avg_MHz | BIC_Busy | BIC_Bzy_MHz | BIC_TSC_MHz | BIC_GFXMHz | BIC_GFXACTMHz | BIC_SAMMHz | BIC_SAMACTMHz | BIC_UNCORE_MHZ) -#define BIC_GROUP_HW_IDLE (BIC_Busy | BIC_CPU_c1 | BIC_CPU_c3 | BIC_CPU_c6 | BIC_CPU_c7 | BIC_GFX_rc6 | BIC_Pkgpc2 | BIC_Pkgpc3 | BIC_Pkgpc6 | BIC_Pkgpc7 | BIC_Pkgpc8 | BIC_Pkgpc9 | BIC_Pkgpc10 | BIC_CPU_LPI | BIC_SYS_LPI | BIC_Mod_c6 | BIC_Totl_c0 | BIC_Any_c0 | BIC_GFX_c0 | BIC_CPUGFX | BIC_SAM_mc6 | BIC_Diec6) -#define BIC_GROUP_SW_IDLE (BIC_Busy | BIC_cpuidle | BIC_pct_idle ) -#define BIC_GROUP_IDLE (BIC_GROUP_HW_IDLE | BIC_pct_idle) -#define BIC_OTHER (BIC_IRQ | BIC_NMI | BIC_SMI | BIC_ThreadC | BIC_CoreTmp | BIC_IPC) +void print_bic_set(char *s, cpu_set_t *set) +{ + int i; -#define BIC_DISABLED_BY_DEFAULT (BIC_USEC | BIC_TOD | BIC_APIC | BIC_X2APIC | BIC_cpuidle) + assert(MAX_BIC < CPU_SETSIZE); -unsigned long long bic_enabled = (0xFFFFFFFFFFFFFFFFULL & ~BIC_DISABLED_BY_DEFAULT); -unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_cpuidle | BIC_pct_idle | BIC_APIC | BIC_X2APIC; + printf("%s:", s); -#define DO_BIC(COUNTER_NAME) (bic_enabled & bic_present & COUNTER_NAME) -#define DO_BIC_READ(COUNTER_NAME) (bic_present & COUNTER_NAME) -#define ENABLE_BIC(COUNTER_NAME) (bic_enabled |= COUNTER_NAME) -#define BIC_PRESENT(COUNTER_BIT) (bic_present |= COUNTER_BIT) -#define BIC_NOT_PRESENT(COUNTER_BIT) (bic_present &= ~COUNTER_BIT) -#define BIC_IS_ENABLED(COUNTER_BIT) (bic_enabled & COUNTER_BIT) + for (i = 0; i <= MAX_BIC; ++i) { + + if (CPU_ISSET(i, set)) { + assert(i < MAX_BIC); + printf(" %s", bic[i].name); + } + } + putchar('\n'); +} + +static cpu_set_t bic_group_topology; +static cpu_set_t bic_group_thermal_pwr; +static cpu_set_t bic_group_frequency; +static cpu_set_t bic_group_hw_idle; +static cpu_set_t bic_group_sw_idle; +static cpu_set_t bic_group_idle; +static cpu_set_t bic_group_other; +static cpu_set_t bic_group_disabled_by_default; +static cpu_set_t bic_enabled; +static cpu_set_t bic_present; + +/* modify */ +#define BIC_INIT(set) CPU_ZERO(set) + +#define SET_BIC(COUNTER_NUMBER, set) CPU_SET(COUNTER_NUMBER, set) +#define CLR_BIC(COUNTER_NUMBER, set) CPU_CLR(COUNTER_NUMBER, set) + +#define BIC_PRESENT(COUNTER_NUMBER) SET_BIC(COUNTER_NUMBER, &bic_present) +#define BIC_NOT_PRESENT(COUNTER_NUMBER) CPU_CLR(COUNTER_NUMBER, &bic_present) + +/* test */ +#define BIC_IS_ENABLED(COUNTER_NUMBER) CPU_ISSET(COUNTER_NUMBER, &bic_enabled) +#define DO_BIC_READ(COUNTER_NUMBER) CPU_ISSET(COUNTER_NUMBER, &bic_present) +#define DO_BIC(COUNTER_NUMBER) (CPU_ISSET(COUNTER_NUMBER, &bic_enabled) && CPU_ISSET(COUNTER_NUMBER, &bic_present)) + +static void bic_set_all(cpu_set_t *set) +{ + int i; + + assert(MAX_BIC < CPU_SETSIZE); + + for (i = 0; i < MAX_BIC; ++i) + SET_BIC(i, set); +} + +/* + * bic_clear_bits() + * clear all the bits from "clr" in "dst" + */ +static void bic_clear_bits(cpu_set_t *dst, cpu_set_t *clr) +{ + int i; + + assert(MAX_BIC < CPU_SETSIZE); + + for (i = 0; i < MAX_BIC; ++i) + if (CPU_ISSET(i, clr)) + CLR_BIC(i, dst); +} + +static void bic_groups_init(void) +{ + BIC_INIT(&bic_group_topology); + SET_BIC(BIC_Package, &bic_group_topology); + SET_BIC(BIC_Node, &bic_group_topology); + SET_BIC(BIC_CoreCnt, &bic_group_topology); + SET_BIC(BIC_PkgCnt, &bic_group_topology); + SET_BIC(BIC_Core, &bic_group_topology); + SET_BIC(BIC_CPU, &bic_group_topology); + SET_BIC(BIC_Die, &bic_group_topology); + SET_BIC(BIC_L3, &bic_group_topology); + + BIC_INIT(&bic_group_thermal_pwr); + SET_BIC(BIC_CoreTmp, &bic_group_thermal_pwr); + SET_BIC(BIC_PkgTmp, &bic_group_thermal_pwr); + SET_BIC(BIC_PkgWatt, &bic_group_thermal_pwr); + SET_BIC(BIC_CorWatt, &bic_group_thermal_pwr); + SET_BIC(BIC_GFXWatt, &bic_group_thermal_pwr); + SET_BIC(BIC_RAMWatt, &bic_group_thermal_pwr); + SET_BIC(BIC_PKG__, &bic_group_thermal_pwr); + SET_BIC(BIC_RAM__, &bic_group_thermal_pwr); + SET_BIC(BIC_SysWatt, &bic_group_thermal_pwr); + + BIC_INIT(&bic_group_frequency); + SET_BIC(BIC_Avg_MHz, &bic_group_frequency); + SET_BIC(BIC_Busy, &bic_group_frequency); + SET_BIC(BIC_Bzy_MHz, &bic_group_frequency); + SET_BIC(BIC_TSC_MHz, &bic_group_frequency); + SET_BIC(BIC_GFXMHz, &bic_group_frequency); + SET_BIC(BIC_GFXACTMHz, &bic_group_frequency); + SET_BIC(BIC_SAMMHz, &bic_group_frequency); + SET_BIC(BIC_SAMACTMHz, &bic_group_frequency); + SET_BIC(BIC_UNCORE_MHZ, &bic_group_frequency); + + BIC_INIT(&bic_group_hw_idle); + SET_BIC(BIC_Busy, &bic_group_hw_idle); + SET_BIC(BIC_CPU_c1, &bic_group_hw_idle); + SET_BIC(BIC_CPU_c3, &bic_group_hw_idle); + SET_BIC(BIC_CPU_c6, &bic_group_hw_idle); + SET_BIC(BIC_CPU_c7, &bic_group_hw_idle); + SET_BIC(BIC_GFX_rc6, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc2, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc3, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc6, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc7, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc8, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc9, &bic_group_hw_idle); + SET_BIC(BIC_Pkgpc10, &bic_group_hw_idle); + SET_BIC(BIC_CPU_LPI, &bic_group_hw_idle); + SET_BIC(BIC_SYS_LPI, &bic_group_hw_idle); + SET_BIC(BIC_Mod_c6, &bic_group_hw_idle); + SET_BIC(BIC_Totl_c0, &bic_group_hw_idle); + SET_BIC(BIC_Any_c0, &bic_group_hw_idle); + SET_BIC(BIC_GFX_c0, &bic_group_hw_idle); + SET_BIC(BIC_CPUGFX, &bic_group_hw_idle); + SET_BIC(BIC_SAM_mc6, &bic_group_hw_idle); + SET_BIC(BIC_Diec6, &bic_group_hw_idle); + + BIC_INIT(&bic_group_sw_idle); + SET_BIC(BIC_Busy, &bic_group_sw_idle); + SET_BIC(BIC_cpuidle, &bic_group_sw_idle); + SET_BIC(BIC_pct_idle, &bic_group_sw_idle); + + BIC_INIT(&bic_group_idle); + CPU_OR(&bic_group_idle, &bic_group_idle, &bic_group_hw_idle); + SET_BIC(BIC_pct_idle, &bic_group_idle); + + BIC_INIT(&bic_group_other); + SET_BIC(BIC_IRQ, &bic_group_other); + SET_BIC(BIC_NMI, &bic_group_other); + SET_BIC(BIC_SMI, &bic_group_other); + SET_BIC(BIC_ThreadC, &bic_group_other); + SET_BIC(BIC_CoreTmp, &bic_group_other); + SET_BIC(BIC_IPC, &bic_group_other); + + BIC_INIT(&bic_group_disabled_by_default); + SET_BIC(BIC_USEC, &bic_group_disabled_by_default); + SET_BIC(BIC_TOD, &bic_group_disabled_by_default); + SET_BIC(BIC_cpuidle, &bic_group_disabled_by_default); + SET_BIC(BIC_APIC, &bic_group_disabled_by_default); + SET_BIC(BIC_X2APIC, &bic_group_disabled_by_default); + + BIC_INIT(&bic_enabled); + bic_set_all(&bic_enabled); + bic_clear_bits(&bic_enabled, &bic_group_disabled_by_default); + + BIC_INIT(&bic_present); + SET_BIC(BIC_USEC, &bic_present); + SET_BIC(BIC_TOD, &bic_present); + SET_BIC(BIC_cpuidle, &bic_present); + SET_BIC(BIC_APIC, &bic_present); + SET_BIC(BIC_X2APIC, &bic_present); + SET_BIC(BIC_pct_idle, &bic_present); +} /* * MSR_PKG_CST_CONFIG_CONTROL decoding for pkg_cstate_limit: @@ -840,20 +989,21 @@ static const struct platform_features spr_features = { }; static const struct platform_features dmr_features = { - .has_msr_misc_feature_control = spr_features.has_msr_misc_feature_control, - .has_msr_misc_pwr_mgmt = spr_features.has_msr_misc_pwr_mgmt, - .has_nhm_msrs = spr_features.has_nhm_msrs, - .has_config_tdp = spr_features.has_config_tdp, - .bclk_freq = spr_features.bclk_freq, - .supported_cstates = spr_features.supported_cstates, - .cst_limit = spr_features.cst_limit, - .has_msr_core_c1_res = spr_features.has_msr_core_c1_res, - .has_msr_module_c6_res_ms = 1, /* DMR has Dual Core Module and MC6 MSR */ - .has_irtl_msrs = spr_features.has_irtl_msrs, - .has_cst_prewake_bit = spr_features.has_cst_prewake_bit, - .has_fixed_rapl_psys_unit = spr_features.has_fixed_rapl_psys_unit, - .trl_msrs = spr_features.trl_msrs, - .rapl_msrs = 0, /* DMR does not have RAPL MSRs */ + .has_msr_misc_feature_control = spr_features.has_msr_misc_feature_control, + .has_msr_misc_pwr_mgmt = spr_features.has_msr_misc_pwr_mgmt, + .has_nhm_msrs = spr_features.has_nhm_msrs, + .bclk_freq = spr_features.bclk_freq, + .supported_cstates = spr_features.supported_cstates, + .cst_limit = spr_features.cst_limit, + .has_msr_core_c1_res = spr_features.has_msr_core_c1_res, + .has_cst_prewake_bit = spr_features.has_cst_prewake_bit, + .has_fixed_rapl_psys_unit = spr_features.has_fixed_rapl_psys_unit, + .trl_msrs = spr_features.trl_msrs, + .has_msr_module_c6_res_ms = 1, /* DMR has Dual-Core-Module and MC6 MSR */ + .rapl_msrs = 0, /* DMR does not have RAPL MSRs */ + .plr_msrs = 0, /* DMR does not have PLR MSRs */ + .has_irtl_msrs = 0, /* DMR does not have IRTL MSRs */ + .has_config_tdp = 0, /* DMR does not have CTDP MSRs */ }; static const struct platform_features srf_features = { @@ -1204,7 +1354,7 @@ struct rapl_counter_arch_info { int msr_shift; /* Positive mean shift right, negative mean shift left */ double *platform_rapl_msr_scale; /* Scale applied to values read by MSR (platform dependent, filled at runtime) */ unsigned int rci_index; /* Maps data from perf counters to global variables */ - unsigned long long bic; + unsigned int bic_number; double compat_scale; /* Some counters require constant scaling to be in the same range as other, similar ones */ unsigned long long flags; }; @@ -1219,7 +1369,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_energy_units, .rci_index = RAPL_RCI_INDEX_ENERGY_PKG, - .bic = BIC_PkgWatt | BIC_Pkg_J, + .bic_number = BIC_PkgWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_PKG, + .perf_subsys = "power", + .perf_name = "energy-pkg", + .msr = MSR_PKG_ENERGY_STATUS, + .msr_mask = 0xFFFFFFFFFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_energy_units, + .rci_index = RAPL_RCI_INDEX_ENERGY_PKG, + .bic_number = BIC_Pkg_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1232,7 +1395,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_energy_units, .rci_index = RAPL_RCI_INDEX_ENERGY_PKG, - .bic = BIC_PkgWatt | BIC_Pkg_J, + .bic_number = BIC_PkgWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_AMD_F17H, + .perf_subsys = "power", + .perf_name = "energy-pkg", + .msr = MSR_PKG_ENERGY_STAT, + .msr_mask = 0xFFFFFFFFFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_energy_units, + .rci_index = RAPL_RCI_INDEX_ENERGY_PKG, + .bic_number = BIC_Pkg_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1245,7 +1421,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_energy_units, .rci_index = RAPL_RCI_INDEX_ENERGY_CORES, - .bic = BIC_CorWatt | BIC_Cor_J, + .bic_number = BIC_CorWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_CORE_ENERGY_STATUS, + .perf_subsys = "power", + .perf_name = "energy-cores", + .msr = MSR_PP0_ENERGY_STATUS, + .msr_mask = 0xFFFFFFFFFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_energy_units, + .rci_index = RAPL_RCI_INDEX_ENERGY_CORES, + .bic_number = BIC_Cor_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1258,7 +1447,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_dram_energy_units, .rci_index = RAPL_RCI_INDEX_DRAM, - .bic = BIC_RAMWatt | BIC_RAM_J, + .bic_number = BIC_RAMWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_DRAM, + .perf_subsys = "power", + .perf_name = "energy-ram", + .msr = MSR_DRAM_ENERGY_STATUS, + .msr_mask = 0xFFFFFFFFFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_dram_energy_units, + .rci_index = RAPL_RCI_INDEX_DRAM, + .bic_number = BIC_RAM_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1271,7 +1473,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_energy_units, .rci_index = RAPL_RCI_INDEX_GFX, - .bic = BIC_GFXWatt | BIC_GFX_J, + .bic_number = BIC_GFXWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_GFX, + .perf_subsys = "power", + .perf_name = "energy-gpu", + .msr = MSR_PP1_ENERGY_STATUS, + .msr_mask = 0xFFFFFFFFFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_energy_units, + .rci_index = RAPL_RCI_INDEX_GFX, + .bic_number = BIC_GFX_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1284,7 +1499,7 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_time_units, .rci_index = RAPL_RCI_INDEX_PKG_PERF_STATUS, - .bic = BIC_PKG__, + .bic_number = BIC_PKG__, .compat_scale = 100.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1297,7 +1512,7 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_time_units, .rci_index = RAPL_RCI_INDEX_DRAM_PERF_STATUS, - .bic = BIC_RAM__, + .bic_number = BIC_RAM__, .compat_scale = 100.0, .flags = RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1310,7 +1525,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_energy_units, .rci_index = RAPL_RCI_INDEX_CORE_ENERGY, - .bic = BIC_CorWatt | BIC_Cor_J, + .bic_number = BIC_CorWatt, + .compat_scale = 1.0, + .flags = 0, + }, + { + .feature_mask = RAPL_AMD_F17H, + .perf_subsys = NULL, + .perf_name = NULL, + .msr = MSR_CORE_ENERGY_STAT, + .msr_mask = 0xFFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_energy_units, + .rci_index = RAPL_RCI_INDEX_CORE_ENERGY, + .bic_number = BIC_Cor_J, .compat_scale = 1.0, .flags = 0, }, @@ -1323,7 +1551,20 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = { .msr_shift = 0, .platform_rapl_msr_scale = &rapl_psys_energy_units, .rci_index = RAPL_RCI_INDEX_ENERGY_PLATFORM, - .bic = BIC_SysWatt | BIC_Sys_J, + .bic_number = BIC_SysWatt, + .compat_scale = 1.0, + .flags = RAPL_COUNTER_FLAG_PLATFORM_COUNTER | RAPL_COUNTER_FLAG_USE_MSR_SUM, + }, + { + .feature_mask = RAPL_PSYS, + .perf_subsys = "power", + .perf_name = "energy-psys", + .msr = MSR_PLATFORM_ENERGY_STATUS, + .msr_mask = 0x00000000FFFFFFFF, + .msr_shift = 0, + .platform_rapl_msr_scale = &rapl_psys_energy_units, + .rci_index = RAPL_RCI_INDEX_ENERGY_PLATFORM, + .bic_number = BIC_Sys_J, .compat_scale = 1.0, .flags = RAPL_COUNTER_FLAG_PLATFORM_COUNTER | RAPL_COUNTER_FLAG_USE_MSR_SUM, }, @@ -1372,7 +1613,7 @@ struct cstate_counter_arch_info { const char *perf_name; unsigned long long msr; unsigned int rci_index; /* Maps data from perf counters to global variables */ - unsigned long long bic; + unsigned int bic_number; unsigned long long flags; int pkg_cstate_limit; }; @@ -1384,7 +1625,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c1-residency", .msr = MSR_CORE_C1_RES, .rci_index = CCSTATE_RCI_INDEX_C1_RESIDENCY, - .bic = BIC_CPU_c1, + .bic_number = BIC_CPU_c1, .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_THREAD, .pkg_cstate_limit = 0, }, @@ -1394,7 +1635,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c3-residency", .msr = MSR_CORE_C3_RESIDENCY, .rci_index = CCSTATE_RCI_INDEX_C3_RESIDENCY, - .bic = BIC_CPU_c3, + .bic_number = BIC_CPU_c3, .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY, .pkg_cstate_limit = 0, }, @@ -1404,7 +1645,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c6-residency", .msr = MSR_CORE_C6_RESIDENCY, .rci_index = CCSTATE_RCI_INDEX_C6_RESIDENCY, - .bic = BIC_CPU_c6, + .bic_number = BIC_CPU_c6, .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY, .pkg_cstate_limit = 0, }, @@ -1414,7 +1655,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c7-residency", .msr = MSR_CORE_C7_RESIDENCY, .rci_index = CCSTATE_RCI_INDEX_C7_RESIDENCY, - .bic = BIC_CPU_c7, + .bic_number = BIC_CPU_c7, .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY, .pkg_cstate_limit = 0, }, @@ -1424,7 +1665,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c2-residency", .msr = MSR_PKG_C2_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C2_RESIDENCY, - .bic = BIC_Pkgpc2, + .bic_number = BIC_Pkgpc2, .flags = 0, .pkg_cstate_limit = PCL__2, }, @@ -1434,7 +1675,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c3-residency", .msr = MSR_PKG_C3_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C3_RESIDENCY, - .bic = BIC_Pkgpc3, + .bic_number = BIC_Pkgpc3, .flags = 0, .pkg_cstate_limit = PCL__3, }, @@ -1444,7 +1685,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c6-residency", .msr = MSR_PKG_C6_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C6_RESIDENCY, - .bic = BIC_Pkgpc6, + .bic_number = BIC_Pkgpc6, .flags = 0, .pkg_cstate_limit = PCL__6, }, @@ -1454,7 +1695,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c7-residency", .msr = MSR_PKG_C7_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C7_RESIDENCY, - .bic = BIC_Pkgpc7, + .bic_number = BIC_Pkgpc7, .flags = 0, .pkg_cstate_limit = PCL__7, }, @@ -1464,7 +1705,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c8-residency", .msr = MSR_PKG_C8_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C8_RESIDENCY, - .bic = BIC_Pkgpc8, + .bic_number = BIC_Pkgpc8, .flags = 0, .pkg_cstate_limit = PCL__8, }, @@ -1474,7 +1715,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c9-residency", .msr = MSR_PKG_C9_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C9_RESIDENCY, - .bic = BIC_Pkgpc9, + .bic_number = BIC_Pkgpc9, .flags = 0, .pkg_cstate_limit = PCL__9, }, @@ -1484,7 +1725,7 @@ static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = { .perf_name = "c10-residency", .msr = MSR_PKG_C10_RESIDENCY, .rci_index = PCSTATE_RCI_INDEX_C10_RESIDENCY, - .bic = BIC_Pkgpc10, + .bic_number = BIC_Pkgpc10, .flags = 0, .pkg_cstate_limit = PCL_10, }, @@ -1840,8 +2081,6 @@ struct pkg_data { ((node_no) * topo.cores_per_node) + \ (core_no)) -#define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no) - /* * The accumulated sum of MSR is defined as a monotonic * increasing MSR, it will be accumulated periodically, @@ -2036,6 +2275,7 @@ struct platform_counters { struct cpu_topology { int physical_package_id; int die_id; + int l3_id; int logical_cpu_id; int physical_node_id; int logical_node_id; /* 0-based count within the package */ @@ -2057,6 +2297,7 @@ struct topo_params { int max_core_id; int max_package_id; int max_die_id; + int max_l3_id; int max_node_num; int nodes_per_pkg; int cores_per_node; @@ -2090,6 +2331,8 @@ int cpu_is_not_allowed(int cpu) * skip non-present cpus */ +#define PER_THREAD_PARAMS struct thread_data *t, struct core_data *c, struct pkg_data *p + 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) { @@ -2103,16 +2346,15 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk for (thread_no = 0; thread_no < topo.threads_per_core; ++thread_no) { struct thread_data *t; struct core_data *c; - struct pkg_data *p; + t = GET_THREAD(thread_base, thread_no, core_no, node_no, pkg_no); if (cpu_is_not_allowed(t->cpu_id)) continue; c = GET_CORE(core_base, core_no, node_no, pkg_no); - p = GET_PKG(pkg_base, pkg_no); - retval |= func(t, c, p); + retval |= func(t, c, &pkg_base[pkg_no]); } } } @@ -2120,21 +2362,21 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk return retval; } -int is_cpu_first_thread_in_core(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int is_cpu_first_thread_in_core(PER_THREAD_PARAMS) { UNUSED(p); return ((int)t->cpu_id == c->base_cpu || c->base_cpu < 0); } -int is_cpu_first_core_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int is_cpu_first_core_in_package(PER_THREAD_PARAMS) { UNUSED(c); return ((int)t->cpu_id == p->base_cpu || p->base_cpu < 0); } -int is_cpu_first_thread_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int is_cpu_first_thread_in_package(PER_THREAD_PARAMS) { return is_cpu_first_thread_in_core(t, c, p) && is_cpu_first_core_in_package(t, c, p); } @@ -2179,10 +2421,13 @@ int get_msr_fd(int cpu) static void bic_disable_msr_access(void) { - const unsigned long bic_msrs = BIC_Mod_c6 | BIC_CoreTmp | - BIC_Totl_c0 | BIC_Any_c0 | BIC_GFX_c0 | BIC_CPUGFX | BIC_PkgTmp; - - bic_enabled &= ~bic_msrs; + CLR_BIC(BIC_Mod_c6, &bic_enabled); + CLR_BIC(BIC_CoreTmp, &bic_enabled); + CLR_BIC(BIC_Totl_c0, &bic_enabled); + CLR_BIC(BIC_Any_c0, &bic_enabled); + CLR_BIC(BIC_GFX_c0, &bic_enabled); + CLR_BIC(BIC_CPUGFX, &bic_enabled); + CLR_BIC(BIC_PkgTmp, &bic_enabled); free_sys_msr_counters(); } @@ -2310,6 +2555,8 @@ char *deferred_add_names[MAX_DEFERRED]; char *deferred_skip_names[MAX_DEFERRED]; int deferred_add_index; int deferred_skip_index; +unsigned int deferred_add_consumed; +unsigned int deferred_skip_consumed; /* * HIDE_LIST - hide this list of counters, show the rest [default] @@ -2380,10 +2627,9 @@ void help(void) * for all the strings in comma separate name_list, * set the approprate bit in return value. */ -unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode) +void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) { unsigned int i; - unsigned long long retval = 0; while (name_list) { char *comma; @@ -2395,41 +2641,39 @@ unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode) for (i = 0; i < MAX_BIC; ++i) { if (!strcmp(name_list, bic[i].name)) { - retval |= (1ULL << i); + SET_BIC(i, ret_set); break; } if (!strcmp(name_list, "all")) { - retval |= ~0; + bic_set_all(ret_set); break; } else if (!strcmp(name_list, "topology")) { - retval |= BIC_GROUP_TOPOLOGY; + CPU_OR(ret_set, ret_set, &bic_group_topology); break; } else if (!strcmp(name_list, "power")) { - retval |= BIC_GROUP_THERMAL_PWR; + CPU_OR(ret_set, ret_set, &bic_group_thermal_pwr); break; } else if (!strcmp(name_list, "idle")) { - retval |= BIC_GROUP_IDLE; + CPU_OR(ret_set, ret_set, &bic_group_idle); break; } else if (!strcmp(name_list, "swidle")) { - retval |= BIC_GROUP_SW_IDLE; + CPU_OR(ret_set, ret_set, &bic_group_sw_idle); break; } else if (!strcmp(name_list, "sysfs")) { /* legacy compatibility */ - retval |= BIC_GROUP_SW_IDLE; + CPU_OR(ret_set, ret_set, &bic_group_sw_idle); break; } else if (!strcmp(name_list, "hwidle")) { - retval |= BIC_GROUP_HW_IDLE; + CPU_OR(ret_set, ret_set, &bic_group_hw_idle); break; } else if (!strcmp(name_list, "frequency")) { - retval |= BIC_GROUP_FREQUENCY; + CPU_OR(ret_set, ret_set, &bic_group_frequency); break; } else if (!strcmp(name_list, "other")) { - retval |= BIC_OTHER; + CPU_OR(ret_set, ret_set, &bic_group_other); break; } - } if (i == MAX_BIC) { - fprintf(stderr, "deferred %s\n", name_list); if (mode == SHOW_LIST) { deferred_add_names[deferred_add_index++] = name_list; if (deferred_add_index >= MAX_DEFERRED) { @@ -2456,7 +2700,6 @@ unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode) name_list++; } - return retval; } void print_header(char *delim) @@ -2474,6 +2717,8 @@ void print_header(char *delim) outp += sprintf(outp, "%sPackage", (printed++ ? delim : "")); if (DO_BIC(BIC_Die)) outp += sprintf(outp, "%sDie", (printed++ ? delim : "")); + if (DO_BIC(BIC_L3)) + outp += sprintf(outp, "%sL3", (printed++ ? delim : "")); if (DO_BIC(BIC_Node)) outp += sprintf(outp, "%sNode", (printed++ ? delim : "")); if (DO_BIC(BIC_Core)) @@ -2514,7 +2759,7 @@ void print_header(char *delim) for (mp = sys.tp; mp; mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 64) outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), mp->name); else @@ -2589,7 +2834,7 @@ void print_header(char *delim) } for (mp = sys.cp; mp; mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 64) outp += sprintf(outp, "%s%18.18s", delim, mp->name); else @@ -2719,7 +2964,7 @@ void print_header(char *delim) outp += sprintf(outp, "%sUncMHz", (printed++ ? delim : "")); for (mp = sys.pp; mp; mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 64) outp += sprintf(outp, "%s%18.18s", delim, mp->name); else if (mp->width == 32) @@ -2777,7 +3022,7 @@ void print_header(char *delim) outp += sprintf(outp, "\n"); } -int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int dump_counters(PER_THREAD_PARAMS) { int i; struct msr_counter *mp; @@ -2892,7 +3137,7 @@ double rapl_counter_get_value(const struct rapl_counter *c, enum rapl_unit desir /* * column formatting convention & formats */ -int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int format_counters(PER_THREAD_PARAMS) { static int count; @@ -2945,6 +3190,8 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_Die)) outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + if (DO_BIC(BIC_L3)) + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_Node)) outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_Core)) @@ -2968,6 +3215,12 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data else outp += sprintf(outp, "%s-", (printed++ ? delim : "")); } + if (DO_BIC(BIC_L3)) { + if (c) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), cpus[t->cpu_id].l3_id); + else + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + } if (DO_BIC(BIC_Node)) { if (t) outp += sprintf(outp, "%s%d", @@ -3032,7 +3285,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data /* Added counters */ for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 32) outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)t->counter[i]); @@ -3129,7 +3382,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->core_throt_cnt); for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 32) outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)c->counter[i]); @@ -3328,7 +3581,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), p->uncore_mhz); for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { if (mp->width == 32) outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)p->counter[i]); @@ -3426,7 +3679,7 @@ void flush_output_stderr(void) outp = output_buffer; } -void format_all_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +void format_all_counters(PER_THREAD_PARAMS) { static int count; @@ -3505,7 +3758,7 @@ int delta_package(struct pkg_data *new, struct pkg_data *old) new->rapl_dram_perf_status.raw_value - old->rapl_dram_perf_status.raw_value; for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) old->counter[i] = new->counter[i]; else if (mp->format == FORMAT_AVERAGE) old->counter[i] = new->counter[i]; @@ -3549,7 +3802,7 @@ void delta_core(struct core_data *new, struct core_data *old) DELTA_WRAP32(new->core_energy.raw_value, old->core_energy.raw_value); for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) old->counter[i] = new->counter[i]; else old->counter[i] = new->counter[i] - old->counter[i]; @@ -3663,7 +3916,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d old->smi_count = new->smi_count - old->smi_count; for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { - if (mp->format == FORMAT_RAW) + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) old->counter[i] = new->counter[i]; else old->counter[i] = new->counter[i] - old->counter[i]; @@ -3717,7 +3970,7 @@ void rapl_counter_clear(struct rapl_counter *c) c->unit = RAPL_UNIT_INVALID; } -void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +void clear_counters(PER_THREAD_PARAMS) { int i; struct msr_counter *mp; @@ -3814,7 +4067,7 @@ void rapl_counter_accumulate(struct rapl_counter *dst, const struct rapl_counter dst->raw_value += src->raw_value; } -int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int sum_counters(PER_THREAD_PARAMS) { int i; struct msr_counter *mp; @@ -3962,7 +4215,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) * sum the counters for all cpus in the system * compute the weighted average */ -void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data *p) +void compute_average(PER_THREAD_PARAMS) { int i; struct msr_counter *mp; @@ -4545,7 +4798,7 @@ char *find_sysfs_path_by_id(struct sysfs_path *sp, int id) return NULL; } -int get_cstate_counters(unsigned int cpu, struct thread_data *t, struct core_data *c, struct pkg_data *p) +int get_cstate_counters(unsigned int cpu, PER_THREAD_PARAMS) { /* * Overcommit memory a little bit here, @@ -4845,7 +5098,7 @@ static inline int get_rapl_domain_id(int cpu) * migrate to cpu * acquire and record local counters for that cpu */ -int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int get_counters(PER_THREAD_PARAMS) { int cpu = t->cpu_id; unsigned long long msr; @@ -5673,6 +5926,11 @@ int get_die_id(int cpu) return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/die_id", cpu); } +int get_l3_id(int cpu) +{ + return parse_int_file("/sys/devices/system/cpu/cpu%d/cache/index3/id", cpu); +} + int get_core_id(int cpu) { return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", cpu); @@ -5861,7 +6119,6 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *, for (thread_no = 0; thread_no < topo.threads_per_core; ++thread_no) { struct thread_data *t, *t2; struct core_data *c, *c2; - struct pkg_data *p, *p2; t = GET_THREAD(thread_base, thread_no, core_no, node_no, pkg_no); @@ -5873,10 +6130,7 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *, c = GET_CORE(core_base, core_no, node_no, pkg_no); c2 = GET_CORE(core_base2, core_no, node_no, pkg_no); - p = GET_PKG(pkg_base, pkg_no); - p2 = GET_PKG(pkg_base2, pkg_no); - - retval |= func(t, c, p, t2, c2, p2); + retval |= func(t, c, &pkg_base[pkg_no], t2, c2, &pkg_base2[pkg_no]); } } } @@ -6334,7 +6588,7 @@ int get_msr_sum(int cpu, off_t offset, unsigned long long *msr) timer_t timerid; /* Timer callback, update the sum of MSRs periodically. */ -static int update_msr_sum(struct thread_data *t, struct core_data *c, struct pkg_data *p) +static int update_msr_sum(PER_THREAD_PARAMS) { int i, ret; int cpu = t->cpu_id; @@ -6572,8 +6826,16 @@ int check_for_cap_sys_rawio(void) int ret = 0; caps = cap_get_proc(); - if (caps == NULL) + if (caps == NULL) { + /* + * CONFIG_MULTIUSER=n kernels have no cap_get_proc() + * Allow them to continue and attempt to access MSRs + */ + if (errno == ENOSYS) + return 0; + return 1; + } if (cap_get_flag(caps, CAP_SYS_RAWIO, CAP_EFFECTIVE, &cap_flag_value)) { ret = 1; @@ -6740,7 +7002,8 @@ static void probe_intel_uncore_frequency_legacy(void) sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i, j); - if (access(path_base, R_OK)) + sprintf(path, "%s/current_freq_khz", path_base); + if (access(path, R_OK)) continue; BIC_PRESENT(BIC_UNCORE_MHZ); @@ -7072,7 +7335,7 @@ static void dump_sysfs_pstate_config(void) * print_epb() * Decode the ENERGY_PERF_BIAS MSR */ -int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int print_epb(PER_THREAD_PARAMS) { char *epb_string; int cpu, epb; @@ -7121,7 +7384,7 @@ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) * print_hwp() * Decode the MSR_HWP_CAPABILITIES */ -int print_hwp(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int print_hwp(PER_THREAD_PARAMS) { unsigned long long msr; int cpu; @@ -7210,7 +7473,7 @@ int print_hwp(struct thread_data *t, struct core_data *c, struct pkg_data *p) /* * print_perf_limit() */ -int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int print_perf_limit(PER_THREAD_PARAMS) { unsigned long long msr; int cpu; @@ -7335,21 +7598,28 @@ void rapl_probe_intel(void) unsigned long long msr; unsigned int time_unit; double tdp; - const unsigned long long bic_watt_bits = BIC_SysWatt | BIC_PkgWatt | BIC_CorWatt | BIC_RAMWatt | BIC_GFXWatt; - const unsigned long long bic_joules_bits = BIC_Sys_J | BIC_Pkg_J | BIC_Cor_J | BIC_RAM_J | BIC_GFX_J; - if (rapl_joules) - bic_enabled &= ~bic_watt_bits; - else - bic_enabled &= ~bic_joules_bits; + if (rapl_joules) { + CLR_BIC(BIC_SysWatt, &bic_enabled); + CLR_BIC(BIC_PkgWatt, &bic_enabled); + CLR_BIC(BIC_CorWatt, &bic_enabled); + CLR_BIC(BIC_RAMWatt, &bic_enabled); + CLR_BIC(BIC_GFXWatt, &bic_enabled); + } else { + CLR_BIC(BIC_Sys_J, &bic_enabled); + CLR_BIC(BIC_Pkg_J, &bic_enabled); + CLR_BIC(BIC_Cor_J, &bic_enabled); + CLR_BIC(BIC_RAM_J, &bic_enabled); + CLR_BIC(BIC_GFX_J, &bic_enabled); + } if (!platform->rapl_msrs || no_msr) return; if (!(platform->rapl_msrs & RAPL_PKG_PERF_STATUS)) - bic_enabled &= ~BIC_PKG__; + CLR_BIC(BIC_PKG__, &bic_enabled); if (!(platform->rapl_msrs & RAPL_DRAM_PERF_STATUS)) - bic_enabled &= ~BIC_RAM__; + CLR_BIC(BIC_RAM__, &bic_enabled); /* units on package 0, verify later other packages match */ if (get_msr(base_cpu, MSR_RAPL_POWER_UNIT, &msr)) @@ -7388,13 +7658,14 @@ void rapl_probe_amd(void) { unsigned long long msr; double tdp; - const unsigned long long bic_watt_bits = BIC_PkgWatt | BIC_CorWatt; - const unsigned long long bic_joules_bits = BIC_Pkg_J | BIC_Cor_J; - if (rapl_joules) - bic_enabled &= ~bic_watt_bits; - else - bic_enabled &= ~bic_joules_bits; + if (rapl_joules) { + CLR_BIC(BIC_SysWatt, &bic_enabled); + CLR_BIC(BIC_CorWatt, &bic_enabled); + } else { + CLR_BIC(BIC_Pkg_J, &bic_enabled); + CLR_BIC(BIC_Cor_J, &bic_enabled); + } if (!platform->rapl_msrs || no_msr) return; @@ -7577,7 +7848,7 @@ static int print_rapl_sysfs(void) return 0; } -int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int print_rapl(PER_THREAD_PARAMS) { unsigned long long msr; const char *msr_name; @@ -7731,7 +8002,7 @@ void probe_rapl(void) * below this value, including the Digital Thermal Sensor (DTS), * Package Thermal Management Sensor (PTM), and thermal event thresholds. */ -int set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int set_temperature_target(PER_THREAD_PARAMS) { unsigned long long msr; unsigned int tcc_default, tcc_offset; @@ -7799,7 +8070,7 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk return 0; } -int print_thermal(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int print_thermal(PER_THREAD_PARAMS) { unsigned long long msr; unsigned int dts, dts2; @@ -7879,7 +8150,7 @@ void probe_thermal(void) for_all_cpus(print_thermal, ODD_COUNTERS); } -int get_cpu_type(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int get_cpu_type(PER_THREAD_PARAMS) { unsigned int eax, ebx, ecx, edx; @@ -8141,7 +8412,7 @@ void rapl_perf_init(void) enum rapl_unit unit; unsigned int next_domain; - if (!BIC_IS_ENABLED(cai->bic)) + if (!BIC_IS_ENABLED(cai->bic_number)) continue; memset(domain_visited, 0, num_domains * sizeof(*domain_visited)); @@ -8205,7 +8476,7 @@ void rapl_perf_init(void) /* If any CPU has access to the counter, make it present */ if (has_counter) - BIC_PRESENT(cai->bic); + BIC_PRESENT(cai->bic_number); } free(domain_visited); @@ -8426,7 +8697,7 @@ void cstate_perf_init_(bool soft_c1) if (!per_core && pkg_visited[pkg_id]) continue; - const bool counter_needed = BIC_IS_ENABLED(cai->bic) || + const bool counter_needed = BIC_IS_ENABLED(cai->bic_number) || (soft_c1 && (cai->flags & CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY)); const bool counter_supported = (platform->supported_cstates & cai->feature_mask); @@ -8453,7 +8724,7 @@ void cstate_perf_init_(bool soft_c1) /* If any CPU has access to the counter, make it present */ if (has_counter) - BIC_PRESENT(cai->bic); + BIC_PRESENT(cai->bic_number); } free(cores_visited); @@ -8949,6 +9220,11 @@ void topology_probe(bool startup) if (cpus[i].die_id > topo.max_die_id) topo.max_die_id = cpus[i].die_id; + /* get l3 information */ + cpus[i].l3_id = get_l3_id(i); + if (cpus[i].l3_id > topo.max_l3_id) + topo.max_l3_id = cpus[i].l3_id; + /* get numa node information */ cpus[i].physical_node_id = get_physical_node_id(&cpus[i]); if (cpus[i].physical_node_id > topo.max_node_num) @@ -8981,6 +9257,9 @@ void topology_probe(bool startup) if (!summary_only && topo.num_die > 1) BIC_PRESENT(BIC_Die); + if (!summary_only && topo.max_l3_id > 0) + BIC_PRESENT(BIC_L3); + topo.num_packages = max_package_id + 1; if (debug > 1) fprintf(outf, "max_package_id %d, sizing for %d packages\n", max_package_id, topo.num_packages); @@ -9004,8 +9283,8 @@ void topology_probe(bool startup) if (cpu_is_not_present(i)) continue; fprintf(outf, - "cpu %d pkg %d die %d node %d lnode %d core %d thread %d\n", - i, cpus[i].physical_package_id, cpus[i].die_id, + "cpu %d pkg %d die %d l3 %d node %d lnode %d core %d thread %d\n", + i, cpus[i].physical_package_id, cpus[i].die_id, cpus[i].l3_id, cpus[i].physical_node_id, cpus[i].logical_node_id, cpus[i].physical_core_id, cpus[i].thread_id); } @@ -9060,7 +9339,6 @@ void init_counter(struct thread_data *thread_base, struct core_data *core_base, int thread_id = cpus[cpu_id].thread_id; struct thread_data *t; struct core_data *c; - struct pkg_data *p; /* Workaround for systems where physical_node_id==-1 * and logical_node_id==(-1 - topo.num_cpus) @@ -9070,18 +9348,17 @@ void init_counter(struct thread_data *thread_base, struct core_data *core_base, t = GET_THREAD(thread_base, thread_id, core_id, node_id, pkg_id); c = GET_CORE(core_base, core_id, node_id, pkg_id); - p = GET_PKG(pkg_base, pkg_id); t->cpu_id = cpu_id; if (!cpu_is_not_allowed(cpu_id)) { if (c->base_cpu < 0) c->base_cpu = t->cpu_id; - if (p->base_cpu < 0) - p->base_cpu = t->cpu_id; + if (pkg_base[pkg_id].base_cpu < 0) + pkg_base[pkg_id].base_cpu = t->cpu_id; } c->core_id = core_id; - p->package_id = pkg_id; + pkg_base[pkg_id].package_id = pkg_id; } int initialize_counters(int cpu_id) @@ -9121,7 +9398,7 @@ void allocate_irq_buffers(void) err(-1, "calloc %d NMI", topo.max_cpu_num + 1); } -int update_topo(struct thread_data *t, struct core_data *c, struct pkg_data *p) +int update_topo(PER_THREAD_PARAMS) { topo.allowed_cpus++; if ((int)t->cpu_id == c->base_cpu) @@ -9189,7 +9466,7 @@ void check_msr_access(void) void check_perf_access(void) { if (no_perf || !BIC_IS_ENABLED(BIC_IPC) || !has_instr_count_access()) - bic_enabled &= ~BIC_IPC; + CLR_BIC(BIC_IPC, &bic_enabled); } bool perf_has_hybrid_devices(void) @@ -9758,8 +10035,8 @@ void turbostat_init() * disable more BICs, since it can't be reported accurately. */ if (platform->enable_tsc_tweak && !has_base_hz) { - bic_enabled &= ~BIC_Busy; - bic_enabled &= ~BIC_Bzy_MHz; + CLR_BIC(BIC_Busy, &bic_enabled); + CLR_BIC(BIC_Bzy_MHz, &bic_enabled); } } @@ -9817,6 +10094,7 @@ int fork_it(char **argv) timersub(&tv_odd, &tv_even, &tv_delta); if (for_all_cpus_2(delta_cpu, ODD_COUNTERS, EVEN_COUNTERS)) fprintf(outf, "%s: Counter reset detected\n", progname); + delta_platform(&platform_counters_odd, &platform_counters_even); compute_average(EVEN_COUNTERS); format_all_counters(EVEN_COUNTERS); @@ -9848,7 +10126,7 @@ int get_and_dump_counters(void) void print_version() { - fprintf(outf, "turbostat version 2025.06.08 - Len Brown \n"); + fprintf(outf, "turbostat version 2025.09.09 - Len Brown \n"); } #define COMMAND_LINE_SIZE 2048 @@ -10145,6 +10423,10 @@ void parse_add_command_msr(char *add_command) format = FORMAT_RAW; goto next; } + if (!strncmp(add_command, "average", strlen("average"))) { + format = FORMAT_AVERAGE; + goto next; + } if (!strncmp(add_command, "delta", strlen("delta"))) { format = FORMAT_DELTA; goto next; @@ -10417,13 +10699,19 @@ void parse_add_command_pmt(char *add_command) has_format = true; } + if (strcmp("average", format_name) == 0) { + format = FORMAT_AVERAGE; + has_format = true; + } + if (strcmp("delta", format_name) == 0) { format = FORMAT_DELTA; has_format = true; } if (!has_format) { - fprintf(stderr, "%s: Invalid format %s. Expected raw or delta\n", __func__, format_name); + fprintf(stderr, "%s: Invalid format %s. Expected raw, average or delta\n", + __func__, format_name); exit(1); } } @@ -10513,8 +10801,10 @@ int is_deferred_add(char *name) int i; for (i = 0; i < deferred_add_index; ++i) - if (!strcmp(name, deferred_add_names[i])) + if (!strcmp(name, deferred_add_names[i])) { + deferred_add_consumed |= (1 << i); return 1; + } return 0; } @@ -10523,11 +10813,34 @@ int is_deferred_skip(char *name) int i; for (i = 0; i < deferred_skip_index; ++i) - if (!strcmp(name, deferred_skip_names[i])) + if (!strcmp(name, deferred_skip_names[i])) { + deferred_skip_consumed |= (1 << i); return 1; + } return 0; } +void verify_deferred_consumed(void) +{ + int i; + int fail = 0; + + for (i = 0; i < deferred_add_index; ++i) { + if (!(deferred_add_consumed & (1 << i))) { + warnx("Counter '%s' can not be added.", deferred_add_names[i]); + fail++; + } + } + for (i = 0; i < deferred_skip_index; ++i) { + if (!(deferred_skip_consumed & (1 << i))) { + warnx("Counter '%s' can not be skipped.", deferred_skip_names[i]); + fail++; + } + } + if (fail) + exit(-EINVAL); +} + void probe_cpuidle_residency(void) { char path[64]; @@ -10537,9 +10850,6 @@ void probe_cpuidle_residency(void) int min_state = 1024, max_state = 0; char *sp; - if (!DO_BIC(BIC_pct_idle)) - return; - for (state = 10; state >= 0; --state) { sprintf(path, "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/name", base_cpu, state); @@ -10752,22 +11062,29 @@ void cmdline(int argc, char **argv) no_perf = 1; break; case 'e': - /* --enable specified counter */ - bic_enabled = bic_enabled | bic_lookup(optarg, SHOW_LIST); + /* --enable specified counter, without clearning existing list */ + bic_lookup(&bic_enabled, optarg, SHOW_LIST); break; case 'f': force_load++; break; case 'd': debug++; - ENABLE_BIC(BIC_DISABLED_BY_DEFAULT); + bic_set_all(&bic_enabled); break; case 'H': /* * --hide: do not show those specified * multiple invocations simply clear more bits in enabled mask */ - bic_enabled &= ~bic_lookup(optarg, HIDE_LIST); + { + cpu_set_t bic_group_hide; + + BIC_INIT(&bic_group_hide); + + bic_lookup(&bic_group_hide, optarg, HIDE_LIST); + bic_clear_bits(&bic_enabled, &bic_group_hide); + } break; case 'h': default: @@ -10791,7 +11108,7 @@ void cmdline(int argc, char **argv) rapl_joules++; break; case 'l': - ENABLE_BIC(BIC_DISABLED_BY_DEFAULT); + bic_set_all(&bic_enabled); list_header_only++; quiet++; break; @@ -10828,9 +11145,8 @@ void cmdline(int argc, char **argv) * subsequent invocations can add to it. */ if (shown == 0) - bic_enabled = bic_lookup(optarg, SHOW_LIST); - else - bic_enabled |= bic_lookup(optarg, SHOW_LIST); + BIC_INIT(&bic_enabled); + bic_lookup(&bic_enabled, optarg, SHOW_LIST); shown = 1; break; case 'S': @@ -10867,6 +11183,8 @@ int main(int argc, char **argv) { int fd, ret; + bic_groups_init(); + fd = open("/sys/fs/cgroup/cgroup.procs", O_WRONLY); if (fd < 0) goto skip_cgroup_setting; @@ -10889,6 +11207,8 @@ int main(int argc, char **argv) probe_cpuidle_residency(); probe_cpuidle_counts(); + verify_deferred_consumed(); + if (!getuid()) set_rlimit(); diff --git a/tools/sched/dl_bw_dump.py b/tools/sched/dl_bw_dump.py new file mode 100644 index 000000000000..aae4e42b1769 --- /dev/null +++ b/tools/sched/dl_bw_dump.py @@ -0,0 +1,57 @@ +#!/usr/bin/env drgn +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Juri Lelli +# Copyright (C) 2025 Red Hat, Inc. + +desc = """ +This is a drgn script to show dl_rq bandwidth accounting information. For more +info on drgn, visit https://github.com/osandov/drgn. + +Only online CPUs are reported. +""" + +import os +import argparse + +import drgn +from drgn import FaultError +from drgn.helpers.common import * +from drgn.helpers.linux import * + +def print_dl_bws_info(): + + print("Retrieving dl_rq bandwidth accounting information:") + + runqueues = prog['runqueues'] + + for cpu_id in for_each_possible_cpu(prog): + try: + rq = per_cpu(runqueues, cpu_id) + + if rq.online == 0: + continue + + dl_rq = rq.dl + + print(f" From CPU: {cpu_id}") + + # Access and print relevant fields from struct dl_rq + print(f" running_bw : {dl_rq.running_bw}") + print(f" this_bw : {dl_rq.this_bw}") + print(f" extra_bw : {dl_rq.extra_bw}") + print(f" max_bw : {dl_rq.max_bw}") + print(f" bw_ratio : {dl_rq.bw_ratio}") + + except drgn.FaultError as fe: + print(f" (CPU {cpu_id}: Fault accessing kernel memory: {fe})") + except AttributeError as ae: + print(f" (CPU {cpu_id}: Missing attribute for root_domain (kernel struct change?): {ae})") + except Exception as e: + print(f" (CPU {cpu_id}: An unexpected error occurred: {e})") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=desc, + formatter_class=argparse.RawTextHelpFormatter) + args = parser.parse_args() + + print_dl_bws_info() diff --git a/tools/sched/root_domains_dump.py b/tools/sched/root_domains_dump.py new file mode 100644 index 000000000000..56dc91f017b2 --- /dev/null +++ b/tools/sched/root_domains_dump.py @@ -0,0 +1,68 @@ +#!/usr/bin/env drgn +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Juri Lelli +# Copyright (C) 2025 Red Hat, Inc. + +desc = """ +This is a drgn script to show the current root domains configuration. For more +info on drgn, visit https://github.com/osandov/drgn. + +Root domains are only printed once, as multiple CPUs might be attached to the +same root domain. +""" + +import os +import argparse + +import drgn +from drgn import FaultError +from drgn.helpers.common import * +from drgn.helpers.linux import * + +def print_root_domains_info(): + + # To store unique root domains found + seen_root_domains = set() + + print("Retrieving (unique) Root Domain Information:") + + runqueues = prog['runqueues'] + def_root_domain = prog['def_root_domain'] + + for cpu_id in for_each_possible_cpu(prog): + try: + rq = per_cpu(runqueues, cpu_id) + + root_domain = rq.rd + + # Check if we've already processed this root domain to avoid duplicates + # Use the memory address of the root_domain as a unique identifier + root_domain_cast = int(root_domain) + if root_domain_cast in seen_root_domains: + continue + seen_root_domains.add(root_domain_cast) + + if root_domain_cast == int(def_root_domain.address_): + print(f"\n--- Root Domain @ def_root_domain ---") + else: + print(f"\n--- Root Domain @ 0x{root_domain_cast:x} ---") + + print(f" From CPU: {cpu_id}") # This CPU belongs to this root domain + + # Access and print relevant fields from struct root_domain + print(f" Span : {cpumask_to_cpulist(root_domain.span[0])}") + print(f" Online : {cpumask_to_cpulist(root_domain.span[0])}") + + except drgn.FaultError as fe: + print(f" (CPU {cpu_id}: Fault accessing kernel memory: {fe})") + except AttributeError as ae: + print(f" (CPU {cpu_id}: Missing attribute for root_domain (kernel struct change?): {ae})") + except Exception as e: + print(f" (CPU {cpu_id}: An unexpected error occurred: {e})") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=desc, + formatter_class=argparse.RawTextHelpFormatter) + args = parser.parse_args() + + print_root_domains_info() diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c index c3cd9a17d48e..69d877501cb7 100644 --- a/tools/sched_ext/scx_qmap.bpf.c +++ b/tools/sched_ext/scx_qmap.bpf.c @@ -615,6 +615,26 @@ void BPF_STRUCT_OPS(qmap_dump_task, struct scx_dump_ctx *dctx, struct task_struc taskc->force_local, taskc->core_sched_seq); } +s32 BPF_STRUCT_OPS(qmap_cgroup_init, struct cgroup *cgrp, struct scx_cgroup_init_args *args) +{ + bpf_printk("CGRP INIT %llu weight=%u period=%lu quota=%ld burst=%lu", + cgrp->kn->id, args->weight, args->bw_period_us, + args->bw_quota_us, args->bw_burst_us); + return 0; +} + +void BPF_STRUCT_OPS(qmap_cgroup_set_weight, struct cgroup *cgrp, u32 weight) +{ + bpf_printk("CGRP SET %llu weight=%u", cgrp->kn->id, weight); +} + +void BPF_STRUCT_OPS(qmap_cgroup_set_bandwidth, struct cgroup *cgrp, + u64 period_us, u64 quota_us, u64 burst_us) +{ + bpf_printk("CGRP SET %llu period=%lu quota=%ld burst=%lu", cgrp->kn->id, + period_us, quota_us, burst_us); +} + /* * Print out the online and possible CPU map using bpf_printk() as a * demonstration of using the cpumask kfuncs and ops.cpu_on/offline(). @@ -840,6 +860,9 @@ SCX_OPS_DEFINE(qmap_ops, .dump = (void *)qmap_dump, .dump_cpu = (void *)qmap_dump_cpu, .dump_task = (void *)qmap_dump_task, + .cgroup_init = (void *)qmap_cgroup_init, + .cgroup_set_weight = (void *)qmap_cgroup_set_weight, + .cgroup_set_bandwidth = (void *)qmap_cgroup_set_bandwidth, .cpu_online = (void *)qmap_cpu_online, .cpu_offline = (void *)qmap_cpu_offline, .init = (void *)qmap_init, diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 5158250988ce..ded48263dd5e 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -101,7 +101,9 @@ else ifneq ($(CROSS_COMPILE),) # Allow userspace to override CLANG_CROSS_FLAGS to specify their own # sysroots and flags or to avoid the GCC call in pure Clang builds. ifeq ($(CLANG_CROSS_FLAGS),) -CLANG_CROSS_FLAGS := --target=$(notdir $(CROSS_COMPILE:%-=%)) +CLANG_TARGET := $(notdir $(CROSS_COMPILE:%-=%)) +CLANG_TARGET := $(subst s390-linux,s390x-linux,$(CLANG_TARGET)) +CLANG_CROSS_FLAGS := --target=$(CLANG_TARGET) GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)gcc 2>/dev/null)) ifneq ($(GCC_TOOLCHAIN_DIR),) CLANG_CROSS_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE)) diff --git a/tools/scripts/syscall.tbl b/tools/scripts/syscall.tbl index 580b4e246aec..d1ae5e92c615 100644 --- a/tools/scripts/syscall.tbl +++ b/tools/scripts/syscall.tbl @@ -408,3 +408,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 31a2d73c963f..d07f14cb7aa4 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -62,7 +62,6 @@ 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-y += $(CXL_CORE_SRC)/ras.o -cxl_core-y += $(CXL_CORE_SRC)/acpi.o cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o diff --git a/tools/testing/cxl/config_check.c b/tools/testing/cxl/config_check.c index 0902c5d6e410..a80bc2c062fe 100644 --- a/tools/testing/cxl/config_check.c +++ b/tools/testing/cxl/config_check.c @@ -14,4 +14,5 @@ void check(void) BUILD_BUG_ON(!IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)); BUILD_BUG_ON(!IS_ENABLED(CONFIG_NVDIMM_SECURITY_TEST)); BUILD_BUG_ON(!IS_ENABLED(CONFIG_DEBUG_FS)); + BUILD_BUG_ON(!IS_ENABLED(CONFIG_MEMORY_HOTPLUG)); } diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 8a5815ca870d..6a25cca5636f 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -2,6 +2,7 @@ // Copyright(c) 2021 Intel Corporation. All rights reserved. #include +#include #include #include #include @@ -1328,6 +1329,7 @@ static int cxl_mem_init(void) static __init int cxl_test_init(void) { int rc, i; + struct range mappable; cxl_acpi_test(); cxl_core_test(); @@ -1342,8 +1344,11 @@ static __init int cxl_test_init(void) rc = -ENOMEM; goto err_gen_pool_create; } + mappable = mhp_get_pluggable_range(true); - rc = gen_pool_add(cxl_mock_pool, iomem_resource.end + 1 - SZ_64G, + rc = gen_pool_add(cxl_mock_pool, + min(iomem_resource.end + 1 - SZ_64G, + mappable.end + 1 - SZ_64G), SZ_64G, NUMA_NO_NODE); if (rc) goto err_gen_pool_add; diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 0f1d91f57ba3..d533481672b7 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1828,27 +1828,10 @@ static ssize_t fw_buf_checksum_show(struct device *dev, { struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); u8 hash[SHA256_DIGEST_SIZE]; - unsigned char *hstr, *hptr; - struct sha256_state sctx; - ssize_t written = 0; - int i; - sha256_init(&sctx); - sha256_update(&sctx, mdata->fw, mdata->fw_size); - sha256_final(&sctx, hash); + sha256(mdata->fw, mdata->fw_size, hash); - hstr = kzalloc((SHA256_DIGEST_SIZE * 2) + 1, GFP_KERNEL); - if (!hstr) - return -ENOMEM; - - hptr = hstr; - for (i = 0; i < SHA256_DIGEST_SIZE; i++) - hptr += sprintf(hptr, "%02x", hash[i]); - - written = sysfs_emit(buf, "%s\n", hstr); - - kfree(hstr); - return written; + return sysfs_emit(buf, "%*phN\n", SHA256_DIGEST_SIZE, hash); } static DEVICE_ATTR_RO(fw_buf_checksum); diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a5f7fdd0c1fb..001c4df9f7df 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -21,6 +21,8 @@ my %opt; my %repeat_tests; my %repeats; my %evals; +my @command_vars; +my %command_tmp_vars; #default opts my %default = ( @@ -216,6 +218,7 @@ my $patchcheck_type; my $patchcheck_start; my $patchcheck_cherry; my $patchcheck_end; +my $patchcheck_skip; my $build_time; my $install_time; @@ -380,6 +383,7 @@ my %option_map = ( "PATCHCHECK_START" => \$patchcheck_start, "PATCHCHECK_CHERRY" => \$patchcheck_cherry, "PATCHCHECK_END" => \$patchcheck_end, + "PATCHCHECK_SKIP" => \$patchcheck_skip, ); # Options may be used by other options, record them. @@ -900,14 +904,22 @@ sub set_eval { } sub set_variable { - my ($lvalue, $rvalue) = @_; + my ($lvalue, $rvalue, $command) = @_; + # Command line variables override all others + if (defined($command_tmp_vars{$lvalue})) { + return; + } if ($rvalue =~ /^\s*$/) { delete $variable{$lvalue}; } else { $rvalue = process_variables($rvalue); $variable{$lvalue} = $rvalue; } + + if (defined($command)) { + $command_tmp_vars{$lvalue} = 1; + } } sub process_compare { @@ -1286,6 +1298,19 @@ sub read_config { $test_case = __read_config $config, \$test_num; + foreach my $val (@command_vars) { + chomp $val; + my %command_overrides; + if ($val =~ m/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { + my $lvalue = $1; + my $rvalue = $2; + + set_value($lvalue, $rvalue, 1, \%command_overrides, "COMMAND LINE"); + } else { + die "Invalid option definition '$val'\n"; + } + } + # make sure we have all mandatory configs get_mandatory_configs; @@ -1371,7 +1396,10 @@ sub __eval_option { # If a variable contains itself, use the default var if (($var eq $name) && defined($opt{$var})) { $o = $opt{$var}; - $retval = "$retval$o"; + # Only append if the default doesn't contain itself + if ($o !~ m/\$\{$var\}/) { + $retval = "$retval$o"; + } } elsif (defined($opt{$o})) { $o = $opt{$o}; $retval = "$retval$o"; @@ -3511,11 +3539,37 @@ sub patchcheck { @list = reverse @list; } + my %skip_list; + my $will_skip = 0; + + if (defined($patchcheck_skip)) { + foreach my $s (split /\s+/, $patchcheck_skip) { + $s = `git log --pretty=oneline $s~1..$s`; + $s =~ s/^(\S+).*/$1/; + chomp $s; + $skip_list{$s} = 1; + $will_skip++; + } + } + doprint("Going to test the following commits:\n"); foreach my $l (@list) { + my $sha1 = $l; + $sha1 =~ s/^([[:xdigit:]]+).*/$1/; + next if (defined($skip_list{$sha1})); doprint "$l\n"; } + if ($will_skip) { + doprint("\nSkipping the following commits:\n"); + foreach my $l (@list) { + my $sha1 = $l; + $sha1 =~ s/^([[:xdigit:]]+).*/$1/; + next if (!defined($skip_list{$sha1})); + doprint "$l\n"; + } + } + my $save_clean = $noclean; my %ignored_warnings; @@ -3530,6 +3584,11 @@ sub patchcheck { my $sha1 = $item; $sha1 =~ s/^([[:xdigit:]]+).*/$1/; + if (defined($skip_list{$sha1})) { + doprint "\nSkipping \"$item\"\n\n"; + next; + } + doprint "\nProcessing commit \"$item\"\n\n"; run_command "git checkout $sha1" or @@ -4242,8 +4301,55 @@ sub cancel_test { die "\nCaught Sig Int, test interrupted: $!\n" } -$#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl [config-file]\n"; +sub die_usage { + die << "EOF" +ktest.pl version: $VERSION + usage: ktest.pl [options] [config-file] + [options]: + -D value: Where value can act as an option override. + -D BUILD_NOCLEAN=1 + Sets global BUILD_NOCLEAN to 1 + -D TEST_TYPE[2]=build + Sets TEST_TYPE of test 2 to "build" + It can also override all temp variables. + -D USE_TEMP_DIR:=1 + Will override all variables that use + "USE_TEMP_DIR=" + +EOF +; +} + +while ( $#ARGV >= 0 ) { + if ( $ARGV[0] eq "-D" ) { + shift; + die_usage if ($#ARGV < 1); + my $val = shift; + + if ($val =~ m/(.*?):=(.*)$/) { + set_variable($1, $2, 1); + } else { + $command_vars[$#command_vars + 1] = $val; + } + + } elsif ( $ARGV[0] =~ m/^-D(.*)/) { + my $val = $1; + shift; + + if ($val =~ m/(.*?):=(.*)$/) { + set_variable($1, $2, 1); + } else { + $command_vars[$#command_vars + 1] = $val; + } + } elsif ( $ARGV[0] eq "-h" ) { + die_usage; + } else { + last; + } +} + +$#ARGV < 1 or die_usage; if ($#ARGV == 0) { $ktest_config = $ARGV[0]; if (! -f $ktest_config) { @@ -4466,6 +4572,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { doprint "RUNNING TEST $i of $opt{NUM_TESTS}$name with option $test_type $run_type$installme\n\n"; + # Always show which build directory and output directory is being used + doprint "BUILD_DIR=$builddir\n"; + doprint "OUTPUT_DIR=$outputdir\n\n"; + if (defined($pre_test)) { my $ret = run_command $pre_test; if (!$ret && defined($pre_test_die) && diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index f43477a9b857..9c4c449a8f3e 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -1017,6 +1017,8 @@ # Note, PATCHCHECK_CHERRY requires PATCHCHECK_END to be defined. # (default 0) # +# PATCHCHECK_SKIP is an optional list of shas to skip testing +# # PATCHCHECK_TYPE is required and is the type of test to run: # build, boot, test. # diff --git a/tools/testing/nvdimm/pmem-dax.c b/tools/testing/nvdimm/pmem-dax.c index c1ec099a3b1d..05e763aab104 100644 --- a/tools/testing/nvdimm/pmem-dax.c +++ b/tools/testing/nvdimm/pmem-dax.c @@ -10,7 +10,7 @@ long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff, long nr_pages, enum dax_access_mode mode, void **kaddr, - pfn_t *pfn) + unsigned long *pfn) { resource_size_t offset = PFN_PHYS(pgoff) + pmem->data_offset; @@ -29,7 +29,7 @@ long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff, *kaddr = pmem->virt_addr + offset; page = vmalloc_to_page(pmem->virt_addr + offset); if (pfn) - *pfn = page_to_pfn_t(page); + *pfn = page_to_pfn(page); pr_debug_ratelimited("%s: pmem: %p pgoff: %#lx pfn: %#lx\n", __func__, pmem, pgoff, page_to_pfn(page)); @@ -39,7 +39,7 @@ long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff, if (kaddr) *kaddr = pmem->virt_addr + offset; if (pfn) - *pfn = phys_to_pfn_t(pmem->phys_addr + offset, pmem->pfn_flags); + *pfn = PHYS_PFN(pmem->phys_addr + offset); /* * If badblocks are present, limit known good range to the diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index e4313726fae3..f7e7bfe9bb85 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -135,16 +134,6 @@ void *__wrap_devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap) } EXPORT_SYMBOL_GPL(__wrap_devm_memremap_pages); -pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags) -{ - struct nfit_test_resource *nfit_res = get_nfit_res(addr); - - if (nfit_res) - flags &= ~PFN_MAP; - return phys_to_pfn_t(addr, flags); -} -EXPORT_SYMBOL(__wrap_phys_to_pfn_t); - void *__wrap_memremap(resource_size_t offset, size_t size, unsigned long flags) { diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index b00583d1eace..b9047fb8ea4a 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h @@ -212,7 +212,6 @@ void __iomem *__wrap_devm_ioremap(struct device *dev, void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, size_t size, unsigned long flags); void *__wrap_devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap); -pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags); void *__wrap_memremap(resource_size_t offset, size_t size, unsigned long flags); void __wrap_devm_memunmap(struct device *dev, void *addr); diff --git a/tools/testing/radix-tree/maple.c b/tools/testing/radix-tree/maple.c index 2c0b38301253..172700fb7784 100644 --- a/tools/testing/radix-tree/maple.c +++ b/tools/testing/radix-tree/maple.c @@ -35062,7 +35062,7 @@ void run_check_rcu_slowread(struct maple_tree *mt, struct rcu_test_struct *vals) int i; void *(*function)(void *); - pthread_t readers[20]; + pthread_t readers[30]; unsigned int index = vals->index; mt_set_in_rcu(mt); @@ -35080,14 +35080,14 @@ void run_check_rcu_slowread(struct maple_tree *mt, struct rcu_test_struct *vals) } } - usleep(5); /* small yield to ensure all threads are at least started. */ + usleep(3); /* small yield to ensure all threads are at least started. */ while (index <= vals->last) { mtree_store(mt, index, (index % 2 ? vals->entry2 : vals->entry3), GFP_KERNEL); index++; - usleep(5); + usleep(2); } while (i--) @@ -35098,6 +35098,7 @@ void run_check_rcu_slowread(struct maple_tree *mt, struct rcu_test_struct *vals) MT_BUG_ON(mt, !vals->seen_entry3); MT_BUG_ON(mt, !vals->seen_both); } + static noinline void __init check_rcu_simulated(struct maple_tree *mt) { unsigned long i, nr_entries = 1000; @@ -35668,6 +35669,18 @@ static noinline void __init check_prealloc(struct maple_tree *mt) allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated != 0); + + /* Chaining multiple preallocations */ + mt_set_in_rcu(mt); + mas_set_range(&mas, 800, 805); /* Slot store, should be 0 allocations */ + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); + allocated = mas_allocated(&mas); + MT_BUG_ON(mt, allocated != 0); + mas.last = 809; /* Node store */ + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); + allocated = mas_allocated(&mas); + MT_BUG_ON(mt, allocated != 1); + mas_store_prealloc(&mas, ptr); } /* End of preallocation testing */ diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 339b31e6a6b5..030da61dbff3 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -77,6 +77,7 @@ TARGETS += net/ovpn TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao +TARGETS += nolibc TARGETS += nsfs TARGETS += pci_endpoint TARGETS += pcie_bwctrl @@ -293,6 +294,14 @@ ifdef INSTALL_PATH $(MAKE) -s --no-print-directory OUTPUT=$$BUILD_TARGET COLLECTION=$$TARGET \ -C $$TARGET emit_tests >> $(TEST_LIST); \ done; + @VERSION=$$(git describe HEAD 2>/dev/null); \ + if [ -n "$$VERSION" ]; then \ + echo "$$VERSION" > $(INSTALL_PATH)/VERSION; \ + printf "Version saved to $(INSTALL_PATH)/VERSION\n"; \ + else \ + printf "Unable to get version from git describe\n"; \ + fi + @echo "**Kselftest Installation is complete: $(INSTALL_PATH)**" else $(error Error: set INSTALL_PATH to use install) endif diff --git a/tools/testing/selftests/alsa/utimer-test.c b/tools/testing/selftests/alsa/utimer-test.c index 32ee3ce57721..37964f311a33 100644 --- a/tools/testing/selftests/alsa/utimer-test.c +++ b/tools/testing/selftests/alsa/utimer-test.c @@ -135,6 +135,7 @@ TEST_F(timer_f, utimer) { pthread_join(ticking_thread, NULL); ASSERT_EQ(total_ticks, TICKS_COUNT); pclose(rfp); + free(buf); } TEST(wrong_timers_test) { diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/selftests/arm64/abi/Makefile index a6d30c620908..483488f8c2ad 100644 --- a/tools/testing/selftests/arm64/abi/Makefile +++ b/tools/testing/selftests/arm64/abi/Makefile @@ -12,4 +12,4 @@ $(OUTPUT)/syscall-abi: syscall-abi.c syscall-abi-asm.S $(OUTPUT)/tpidr2: tpidr2.c $(CC) -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \ -static -include ../../../../include/nolibc/nolibc.h \ - -ffreestanding -Wall $^ -o $@ -lgcc + -I../.. -ffreestanding -Wall $^ -o $@ -lgcc diff --git a/tools/testing/selftests/arm64/abi/hwcap.c b/tools/testing/selftests/arm64/abi/hwcap.c index 35f521e5f41c..002ec38a8bbb 100644 --- a/tools/testing/selftests/arm64/abi/hwcap.c +++ b/tools/testing/selftests/arm64/abi/hwcap.c @@ -21,6 +21,10 @@ #define TESTS_PER_HWCAP 3 +#ifndef AT_HWCAP3 +#define AT_HWCAP3 29 +#endif + /* * Function expected to generate exception when the feature is not * supported and return when it is supported. If the specific exception @@ -1098,6 +1102,18 @@ static const struct hwcap_data { .sigill_fn = hbc_sigill, .sigill_reliable = true, }, + { + .name = "MTE_FAR", + .at_hwcap = AT_HWCAP3, + .hwcap_bit = HWCAP3_MTE_FAR, + .cpuinfo = "mtefar", + }, + { + .name = "MTE_STOREONLY", + .at_hwcap = AT_HWCAP3, + .hwcap_bit = HWCAP3_MTE_STORE_ONLY, + .cpuinfo = "mtestoreonly", + }, }; typedef void (*sighandler_fn)(int, siginfo_t *, void *); diff --git a/tools/testing/selftests/arm64/abi/tpidr2.c b/tools/testing/selftests/arm64/abi/tpidr2.c index eb19dcc37a75..f58a9f89b952 100644 --- a/tools/testing/selftests/arm64/abi/tpidr2.c +++ b/tools/testing/selftests/arm64/abi/tpidr2.c @@ -3,31 +3,12 @@ #include #include +#include "kselftest.h" + #define SYS_TPIDR2 "S3_3_C13_C0_5" #define EXPECTED_TESTS 5 -static void putstr(const char *str) -{ - write(1, str, strlen(str)); -} - -static void putnum(unsigned int num) -{ - char c; - - if (num / 10) - putnum(num / 10); - - c = '0' + (num % 10); - write(1, &c, 1); -} - -static int tests_run; -static int tests_passed; -static int tests_failed; -static int tests_skipped; - static void set_tpidr2(uint64_t val) { asm volatile ( @@ -50,20 +31,6 @@ static uint64_t get_tpidr2(void) return val; } -static void print_summary(void) -{ - if (tests_passed + tests_failed + tests_skipped != EXPECTED_TESTS) - putstr("# UNEXPECTED TEST COUNT: "); - - putstr("# Totals: pass:"); - putnum(tests_passed); - putstr(" fail:"); - putnum(tests_failed); - putstr(" xfail:0 xpass:0 skip:"); - putnum(tests_skipped); - putstr(" error:0\n"); -} - /* Processes should start with TPIDR2 == 0 */ static int default_value(void) { @@ -105,9 +72,8 @@ static int write_fork_read(void) if (newpid == 0) { /* In child */ if (get_tpidr2() != oldpid) { - putstr("# TPIDR2 changed in child: "); - putnum(get_tpidr2()); - putstr("\n"); + ksft_print_msg("TPIDR2 changed in child: %llx\n", + get_tpidr2()); exit(0); } @@ -115,14 +81,12 @@ static int write_fork_read(void) if (get_tpidr2() == getpid()) { exit(1); } else { - putstr("# Failed to set TPIDR2 in child\n"); + ksft_print_msg("Failed to set TPIDR2 in child\n"); exit(0); } } if (newpid < 0) { - putstr("# fork() failed: -"); - putnum(-newpid); - putstr("\n"); + ksft_print_msg("fork() failed: %d\n", newpid); return 0; } @@ -132,23 +96,22 @@ static int write_fork_read(void) if (waiting < 0) { if (errno == EINTR) continue; - putstr("# waitpid() failed: "); - putnum(errno); - putstr("\n"); + ksft_print_msg("waitpid() failed: %d\n", errno); return 0; } if (waiting != newpid) { - putstr("# waitpid() returned wrong PID\n"); + ksft_print_msg("waitpid() returned wrong PID: %d != %d\n", + waiting, newpid); return 0; } if (!WIFEXITED(status)) { - putstr("# child did not exit\n"); + ksft_print_msg("child did not exit\n"); return 0; } if (getpid() != get_tpidr2()) { - putstr("# TPIDR2 corrupted in parent\n"); + ksft_print_msg("TPIDR2 corrupted in parent\n"); return 0; } @@ -188,35 +151,32 @@ static int write_clone_read(void) stack = malloc(__STACK_SIZE); if (!stack) { - putstr("# malloc() failed\n"); + ksft_print_msg("malloc() failed\n"); return 0; } ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE, &parent_tid, 0, &child_tid); if (ret == -1) { - putstr("# clone() failed\n"); - putnum(errno); - putstr("\n"); + ksft_print_msg("clone() failed: %d\n", errno); return 0; } if (ret == 0) { /* In child */ if (get_tpidr2() != 0) { - putstr("# TPIDR2 non-zero in child: "); - putnum(get_tpidr2()); - putstr("\n"); + ksft_print_msg("TPIDR2 non-zero in child: %llx\n", + get_tpidr2()); exit(0); } if (gettid() == 0) - putstr("# Child TID==0\n"); + ksft_print_msg("Child TID==0\n"); set_tpidr2(gettid()); if (get_tpidr2() == gettid()) { exit(1); } else { - putstr("# Failed to set TPIDR2 in child\n"); + ksft_print_msg("Failed to set TPIDR2 in child\n"); exit(0); } } @@ -227,25 +187,22 @@ static int write_clone_read(void) if (waiting < 0) { if (errno == EINTR) continue; - putstr("# wait4() failed: "); - putnum(errno); - putstr("\n"); + ksft_print_msg("wait4() failed: %d\n", errno); return 0; } if (waiting != ret) { - putstr("# wait4() returned wrong PID "); - putnum(waiting); - putstr("\n"); + ksft_print_msg("wait4() returned wrong PID %d\n", + waiting); return 0; } if (!WIFEXITED(status)) { - putstr("# child did not exit\n"); + ksft_print_msg("child did not exit\n"); return 0; } if (parent != get_tpidr2()) { - putstr("# TPIDR2 corrupted in parent\n"); + ksft_print_msg("TPIDR2 corrupted in parent\n"); return 0; } @@ -253,35 +210,14 @@ static int write_clone_read(void) } } -#define run_test(name) \ - if (name()) { \ - tests_passed++; \ - } else { \ - tests_failed++; \ - putstr("not "); \ - } \ - putstr("ok "); \ - putnum(++tests_run); \ - putstr(" " #name "\n"); - -#define skip_test(name) \ - tests_skipped++; \ - putstr("ok "); \ - putnum(++tests_run); \ - putstr(" # SKIP " #name "\n"); - int main(int argc, char **argv) { int ret; - putstr("TAP version 13\n"); - putstr("1.."); - putnum(EXPECTED_TESTS); - putstr("\n"); + ksft_print_header(); + ksft_set_plan(5); - putstr("# PID: "); - putnum(getpid()); - putstr("\n"); + ksft_print_msg("PID: %d\n", getpid()); /* * This test is run with nolibc which doesn't support hwcap and @@ -290,23 +226,21 @@ int main(int argc, char **argv) */ ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0); if (ret >= 0) { - run_test(default_value); - run_test(write_read); - run_test(write_sleep_read); - run_test(write_fork_read); - run_test(write_clone_read); + ksft_test_result(default_value(), "default_value\n"); + ksft_test_result(write_read, "write_read\n"); + ksft_test_result(write_sleep_read, "write_sleep_read\n"); + ksft_test_result(write_fork_read, "write_fork_read\n"); + ksft_test_result(write_clone_read, "write_clone_read\n"); } else { - putstr("# SME support not present\n"); + ksft_print_msg("SME support not present\n"); - skip_test(default_value); - skip_test(write_read); - skip_test(write_sleep_read); - skip_test(write_fork_read); - skip_test(write_clone_read); + ksft_test_result_skip("default_value\n"); + ksft_test_result_skip("write_read\n"); + ksft_test_result_skip("write_sleep_read\n"); + ksft_test_result_skip("write_fork_read\n"); + ksft_test_result_skip("write_clone_read\n"); } - print_summary(); - - return 0; + ksft_finished(); } diff --git a/tools/testing/selftests/arm64/fp/fp-ptrace.c b/tools/testing/selftests/arm64/fp/fp-ptrace.c index 191c47ca0ed8..cdd7a45c045d 100644 --- a/tools/testing/selftests/arm64/fp/fp-ptrace.c +++ b/tools/testing/selftests/arm64/fp/fp-ptrace.c @@ -1061,11 +1061,31 @@ static bool sve_write_supported(struct test_config *config) if (config->sme_vl_in != config->sme_vl_expected) { return false; } + + if (!sve_supported()) + return false; } return true; } +static bool sve_write_fpsimd_supported(struct test_config *config) +{ + if (!sve_supported()) + return false; + + if ((config->svcr_in & SVCR_ZA) != (config->svcr_expected & SVCR_ZA)) + return false; + + if (config->svcr_expected & SVCR_SM) + return false; + + if (config->sme_vl_in != config->sme_vl_expected) + return false; + + return true; +} + static void fpsimd_write_expected(struct test_config *config) { int vl; @@ -1134,6 +1154,9 @@ static void sve_write_expected(struct test_config *config) int vl = vl_expected(config); int sme_vq = __sve_vq_from_vl(config->sme_vl_expected); + if (!vl) + return; + fill_random(z_expected, __SVE_ZREGS_SIZE(__sve_vq_from_vl(vl))); fill_random(p_expected, __SVE_PREGS_SIZE(__sve_vq_from_vl(vl))); @@ -1152,7 +1175,7 @@ static void sve_write_expected(struct test_config *config) } } -static void sve_write(pid_t child, struct test_config *config) +static void sve_write_sve(pid_t child, struct test_config *config) { struct user_sve_header *sve; struct iovec iov; @@ -1161,7 +1184,10 @@ static void sve_write(pid_t child, struct test_config *config) vl = vl_expected(config); vq = __sve_vq_from_vl(vl); - iov.iov_len = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE); + if (!vl) + return; + + iov.iov_len = SVE_PT_SIZE(vq, SVE_PT_REGS_SVE); iov.iov_base = malloc(iov.iov_len); if (!iov.iov_base) { ksft_print_msg("Failed allocating %lu byte SVE write buffer\n", @@ -1195,6 +1221,44 @@ static void sve_write(pid_t child, struct test_config *config) free(iov.iov_base); } +static void sve_write_fpsimd(pid_t child, struct test_config *config) +{ + struct user_sve_header *sve; + struct user_fpsimd_state *fpsimd; + struct iovec iov; + int ret, vl, vq; + + vl = vl_expected(config); + vq = __sve_vq_from_vl(vl); + + if (!vl) + return; + + iov.iov_len = SVE_PT_SIZE(vq, SVE_PT_REGS_FPSIMD); + iov.iov_base = malloc(iov.iov_len); + if (!iov.iov_base) { + ksft_print_msg("Failed allocating %lu byte SVE write buffer\n", + iov.iov_len); + return; + } + memset(iov.iov_base, 0, iov.iov_len); + + sve = iov.iov_base; + sve->size = iov.iov_len; + sve->flags = SVE_PT_REGS_FPSIMD; + sve->vl = vl; + + fpsimd = iov.iov_base + SVE_PT_REGS_OFFSET; + memcpy(&fpsimd->vregs, v_expected, sizeof(v_expected)); + + ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_SVE, &iov); + if (ret != 0) + ksft_print_msg("Failed to write SVE: %s (%d)\n", + strerror(errno), errno); + + free(iov.iov_base); +} + static bool za_write_supported(struct test_config *config) { if ((config->svcr_in & SVCR_SM) != (config->svcr_expected & SVCR_SM)) @@ -1386,7 +1450,13 @@ static struct test_definition sve_test_defs[] = { .name = "SVE write", .supported = sve_write_supported, .set_expected_values = sve_write_expected, - .modify_values = sve_write, + .modify_values = sve_write_sve, + }, + { + .name = "SVE write FPSIMD format", + .supported = sve_write_fpsimd_supported, + .set_expected_values = fpsimd_write_expected, + .modify_values = sve_write_fpsimd, }, }; @@ -1607,7 +1677,7 @@ int main(void) * Run the test set if there is no SVE or SME, with those we * have to pick a VL for each run. */ - if (!sve_supported()) { + if (!sve_supported() && !sme_supported()) { test_config.sve_vl_in = 0; test_config.sve_vl_expected = 0; test_config.sme_vl_in = 0; diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 577b6e05e860..b22303778fb0 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -170,7 +170,7 @@ static void ptrace_set_get_inherit(pid_t child, const struct vec_type *type) memset(&sve, 0, sizeof(sve)); sve.size = sizeof(sve); sve.vl = sve_vl_from_vq(SVE_VQ_MIN); - sve.flags = SVE_PT_VL_INHERIT; + sve.flags = SVE_PT_VL_INHERIT | SVE_PT_REGS_SVE; ret = set_sve(child, type, &sve); if (ret != 0) { ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n", @@ -235,6 +235,7 @@ static void ptrace_set_get_vl(pid_t child, const struct vec_type *type, /* Set the VL by doing a set with no register payload */ memset(&sve, 0, sizeof(sve)); sve.size = sizeof(sve); + sve.flags = SVE_PT_REGS_SVE; sve.vl = vl; ret = set_sve(child, type, &sve); if (ret != 0) { @@ -253,7 +254,7 @@ static void ptrace_set_get_vl(pid_t child, const struct vec_type *type, return; } - ksft_test_result(new_sve->vl = prctl_vl, "Set %s VL %u\n", + ksft_test_result(new_sve->vl == prctl_vl, "Set %s VL %u\n", type->name, vl); free(new_sve); @@ -301,8 +302,10 @@ static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type) p[j] = j; } + /* This should only succeed for SVE */ ret = set_sve(child, type, sve); - ksft_test_result(ret == 0, "%s FPSIMD set via SVE: %d\n", + ksft_test_result((type->regset == NT_ARM_SVE) == (ret == 0), + "%s FPSIMD set via SVE: %d\n", type->name, ret); if (ret) goto out; @@ -750,9 +753,6 @@ int main(void) ksft_print_header(); ksft_set_plan(EXPECTED_TESTS); - if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) - ksft_exit_skip("SVE not available\n"); - child = fork(); if (!child) return do_child(); diff --git a/tools/testing/selftests/arm64/gcs/basic-gcs.c b/tools/testing/selftests/arm64/gcs/basic-gcs.c index 3fb9742342a3..54f9c888249d 100644 --- a/tools/testing/selftests/arm64/gcs/basic-gcs.c +++ b/tools/testing/selftests/arm64/gcs/basic-gcs.c @@ -298,6 +298,68 @@ static bool test_fork(void) return pass; } +/* A vfork()ed process can run and exit */ +static bool test_vfork(void) +{ + unsigned long child_mode; + int ret, status; + pid_t pid; + bool pass = true; + + pid = vfork(); + if (pid == -1) { + ksft_print_msg("vfork() failed: %d\n", errno); + pass = false; + goto out; + } + if (pid == 0) { + /* + * In child, make sure we can call a function, read + * the GCS pointer and status and then exit. + */ + valid_gcs_function(); + get_gcspr(); + + ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, + &child_mode, 0, 0, 0); + if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) { + ksft_print_msg("GCS not enabled in child\n"); + ret = EXIT_FAILURE; + } + + _exit(ret); + } + + /* + * In parent, check we can still do function calls then check + * on the child. + */ + valid_gcs_function(); + + ksft_print_msg("Waiting for child %d\n", pid); + + ret = waitpid(pid, &status, 0); + if (ret == -1) { + ksft_print_msg("Failed to wait for child: %d\n", + errno); + return false; + } + + if (!WIFEXITED(status)) { + ksft_print_msg("Child exited due to signal %d\n", + WTERMSIG(status)); + pass = false; + } else if (WEXITSTATUS(status)) { + ksft_print_msg("Child exited with status %d\n", + WEXITSTATUS(status)); + pass = false; + } + +out: + + return pass; +} + typedef bool (*gcs_test)(void); static struct { @@ -314,6 +376,7 @@ static struct { { "enable_invalid", enable_invalid, true }, { "map_guarded_stack", map_guarded_stack }, { "fork", test_fork }, + { "vfork", test_vfork }, }; int main(void) diff --git a/tools/testing/selftests/arm64/mte/check_buffer_fill.c b/tools/testing/selftests/arm64/mte/check_buffer_fill.c index 2ee7f114d7fa..ff4e07503349 100644 --- a/tools/testing/selftests/arm64/mte/check_buffer_fill.c +++ b/tools/testing/selftests/arm64/mte/check_buffer_fill.c @@ -31,7 +31,7 @@ static int check_buffer_by_byte(int mem_type, int mode) int i, j, item; bool err; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); item = ARRAY_SIZE(sizes); for (i = 0; i < item; i++) { @@ -68,7 +68,7 @@ static int check_buffer_underflow_by_byte(int mem_type, int mode, bool err; char *und_ptr = NULL; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); item = ARRAY_SIZE(sizes); for (i = 0; i < item; i++) { ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0, @@ -164,7 +164,7 @@ static int check_buffer_overflow_by_byte(int mem_type, int mode, size_t tagged_size, overflow_size; char *over_ptr = NULL; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); item = ARRAY_SIZE(sizes); for (i = 0; i < item; i++) { ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0, @@ -337,7 +337,7 @@ static int check_buffer_by_block(int mem_type, int mode) { int i, item, result = KSFT_PASS; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); item = ARRAY_SIZE(sizes); cur_mte_cxt.fault_valid = false; for (i = 0; i < item; i++) { @@ -368,7 +368,7 @@ static int check_memory_initial_tags(int mem_type, int mode, int mapping) int run, fd; int total = ARRAY_SIZE(sizes); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); for (run = 0; run < total; run++) { /* check initial tags for anonymous mmap */ ptr = (char *)mte_allocate_memory(sizes[run], mem_type, mapping, false); @@ -415,7 +415,7 @@ int main(int argc, char *argv[]) return err; /* Register SIGSEGV handler */ - mte_register_signal(SIGSEGV, mte_default_handler); + mte_register_signal(SIGSEGV, mte_default_handler, false); /* Set test plan */ ksft_set_plan(20); diff --git a/tools/testing/selftests/arm64/mte/check_child_memory.c b/tools/testing/selftests/arm64/mte/check_child_memory.c index 7597fc632cad..5e97ee792e4d 100644 --- a/tools/testing/selftests/arm64/mte/check_child_memory.c +++ b/tools/testing/selftests/arm64/mte/check_child_memory.c @@ -88,7 +88,7 @@ static int check_child_memory_mapping(int mem_type, int mode, int mapping) int item = ARRAY_SIZE(sizes); item = ARRAY_SIZE(sizes); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); for (run = 0; run < item; run++) { ptr = (char *)mte_allocate_memory_tag_range(sizes[run], mem_type, mapping, UNDERFLOW, OVERFLOW); @@ -109,7 +109,7 @@ static int check_child_file_mapping(int mem_type, int mode, int mapping) int run, fd, map_size, result = KSFT_PASS; int total = ARRAY_SIZE(sizes); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); for (run = 0; run < total; run++) { fd = create_temp_file(); if (fd == -1) @@ -160,8 +160,8 @@ int main(int argc, char *argv[]) return err; /* Register SIGSEGV handler */ - mte_register_signal(SIGSEGV, mte_default_handler); - mte_register_signal(SIGBUS, mte_default_handler); + mte_register_signal(SIGSEGV, mte_default_handler, false); + mte_register_signal(SIGBUS, mte_default_handler, false); /* Set test plan */ ksft_set_plan(12); diff --git a/tools/testing/selftests/arm64/mte/check_hugetlb_options.c b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c index 3bfcd3848432..aad1234c7e0f 100644 --- a/tools/testing/selftests/arm64/mte/check_hugetlb_options.c +++ b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c @@ -151,7 +151,7 @@ static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int map_size = default_huge_page_size(); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false); if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS) return KSFT_FAIL; @@ -180,7 +180,7 @@ static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) unsigned long map_size; prot_flag = PROT_READ | PROT_WRITE; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); map_size = default_huge_page_size(); map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, 0, 0); @@ -210,7 +210,7 @@ static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mappin map_size = default_huge_page_size(); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, 0, 0); if (check_allocated_memory_range(ptr, map_size, mem_type, @@ -235,8 +235,8 @@ int main(int argc, char *argv[]) return err; /* Register signal handlers */ - mte_register_signal(SIGBUS, mte_default_handler); - mte_register_signal(SIGSEGV, mte_default_handler); + mte_register_signal(SIGBUS, mte_default_handler, false); + mte_register_signal(SIGSEGV, mte_default_handler, false); allocate_hugetlb(); diff --git a/tools/testing/selftests/arm64/mte/check_ksm_options.c b/tools/testing/selftests/arm64/mte/check_ksm_options.c index 88c74bc46d4f..0cf5faef1724 100644 --- a/tools/testing/selftests/arm64/mte/check_ksm_options.c +++ b/tools/testing/selftests/arm64/mte/check_ksm_options.c @@ -106,7 +106,7 @@ static int check_madvise_options(int mem_type, int mode, int mapping) return err; } - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true); if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS) return KSFT_FAIL; @@ -141,8 +141,8 @@ int main(int argc, char *argv[]) return KSFT_FAIL; } /* Register signal handlers */ - mte_register_signal(SIGBUS, mte_default_handler); - mte_register_signal(SIGSEGV, mte_default_handler); + mte_register_signal(SIGBUS, mte_default_handler, false); + mte_register_signal(SIGSEGV, mte_default_handler, false); /* Set test plan */ ksft_set_plan(4); diff --git a/tools/testing/selftests/arm64/mte/check_mmap_options.c b/tools/testing/selftests/arm64/mte/check_mmap_options.c index 17694caaff53..c100af3012cb 100644 --- a/tools/testing/selftests/arm64/mte/check_mmap_options.c +++ b/tools/testing/selftests/arm64/mte/check_mmap_options.c @@ -3,6 +3,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -23,6 +24,35 @@ #define OVERFLOW MT_GRANULE_SIZE #define TAG_CHECK_ON 0 #define TAG_CHECK_OFF 1 +#define ATAG_CHECK_ON 1 +#define ATAG_CHECK_OFF 0 + +#define TEST_NAME_MAX 256 + +enum mte_mem_check_type { + CHECK_ANON_MEM = 0, + CHECK_FILE_MEM = 1, + CHECK_CLEAR_PROT_MTE = 2, +}; + +enum mte_tag_op_type { + TAG_OP_ALL = 0, + TAG_OP_STONLY = 1, +}; + +struct check_mmap_testcase { + int check_type; + int mem_type; + int mte_sync; + int mapping; + int tag_check; + int atag_check; + int tag_op; + bool enable_tco; +}; + +#define TAG_OP_ALL 0 +#define TAG_OP_STONLY 1 static size_t page_size; static int sizes[] = { @@ -30,8 +60,17 @@ static int sizes[] = { /* page size - 1*/ 0, /* page_size */ 0, /* page size + 1 */ 0 }; -static int check_mte_memory(char *ptr, int size, int mode, int tag_check) +static int check_mte_memory(char *ptr, int size, int mode, + int tag_check,int atag_check, int tag_op) { + char buf[MT_GRANULE_SIZE]; + + if (!mtefar_support && atag_check == ATAG_CHECK_ON) + return KSFT_SKIP; + + if (atag_check == ATAG_CHECK_ON) + ptr = mte_insert_atag(ptr); + mte_initialize_current_context(mode, (uintptr_t)ptr, size); memset(ptr, '1', size); mte_wait_after_trig(); @@ -54,16 +93,34 @@ static int check_mte_memory(char *ptr, int size, int mode, int tag_check) if (cur_mte_cxt.fault_valid == true && tag_check == TAG_CHECK_OFF) return KSFT_FAIL; + if (tag_op == TAG_OP_STONLY) { + mte_initialize_current_context(mode, (uintptr_t)ptr, -UNDERFLOW); + memcpy(buf, ptr - UNDERFLOW, MT_GRANULE_SIZE); + mte_wait_after_trig(); + if (cur_mte_cxt.fault_valid == true) + return KSFT_FAIL; + + mte_initialize_current_context(mode, (uintptr_t)ptr, size + OVERFLOW); + memcpy(buf, ptr + size, MT_GRANULE_SIZE); + mte_wait_after_trig(); + if (cur_mte_cxt.fault_valid == true) + return KSFT_FAIL; + } + return KSFT_PASS; } -static int check_anonymous_memory_mapping(int mem_type, int mode, int mapping, int tag_check) +static int check_anonymous_memory_mapping(int mem_type, int mode, int mapping, + int tag_check, int atag_check, int tag_op) { char *ptr, *map_ptr; int run, result, map_size; int item = ARRAY_SIZE(sizes); - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + if (tag_op == TAG_OP_STONLY && !mtestonly_support) + return KSFT_SKIP; + + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, tag_op); for (run = 0; run < item; run++) { map_size = sizes[run] + OVERFLOW + UNDERFLOW; map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false); @@ -79,23 +136,27 @@ static int check_anonymous_memory_mapping(int mem_type, int mode, int mapping, i munmap((void *)map_ptr, map_size); return KSFT_FAIL; } - result = check_mte_memory(ptr, sizes[run], mode, tag_check); + result = check_mte_memory(ptr, sizes[run], mode, tag_check, atag_check, tag_op); mte_clear_tags((void *)ptr, sizes[run]); mte_free_memory((void *)map_ptr, map_size, mem_type, false); - if (result == KSFT_FAIL) - return KSFT_FAIL; + if (result != KSFT_PASS) + return result; } return KSFT_PASS; } -static int check_file_memory_mapping(int mem_type, int mode, int mapping, int tag_check) +static int check_file_memory_mapping(int mem_type, int mode, int mapping, + int tag_check, int atag_check, int tag_op) { char *ptr, *map_ptr; int run, fd, map_size; int total = ARRAY_SIZE(sizes); int result = KSFT_PASS; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + if (tag_op == TAG_OP_STONLY && !mtestonly_support) + return KSFT_SKIP; + + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, tag_op); for (run = 0; run < total; run++) { fd = create_temp_file(); if (fd == -1) @@ -117,24 +178,24 @@ static int check_file_memory_mapping(int mem_type, int mode, int mapping, int ta close(fd); return KSFT_FAIL; } - result = check_mte_memory(ptr, sizes[run], mode, tag_check); + result = check_mte_memory(ptr, sizes[run], mode, tag_check, atag_check, tag_op); mte_clear_tags((void *)ptr, sizes[run]); munmap((void *)map_ptr, map_size); close(fd); - if (result == KSFT_FAIL) - break; + if (result != KSFT_PASS) + return result; } - return result; + return KSFT_PASS; } -static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) +static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping, int atag_check) { char *ptr, *map_ptr; int run, prot_flag, result, fd, map_size; int total = ARRAY_SIZE(sizes); prot_flag = PROT_READ | PROT_WRITE; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); for (run = 0; run < total; run++) { map_size = sizes[run] + OVERFLOW + UNDERFLOW; ptr = (char *)mte_allocate_memory_tag_range(sizes[run], mem_type, mapping, @@ -150,10 +211,10 @@ static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n"); return KSFT_FAIL; } - result = check_mte_memory(ptr, sizes[run], mode, TAG_CHECK_ON); + result = check_mte_memory(ptr, sizes[run], mode, TAG_CHECK_ON, atag_check, TAG_OP_ALL); mte_free_memory_tag_range((void *)ptr, sizes[run], mem_type, UNDERFLOW, OVERFLOW); if (result != KSFT_PASS) - return KSFT_FAIL; + return result; fd = create_temp_file(); if (fd == -1) @@ -174,19 +235,715 @@ static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) close(fd); return KSFT_FAIL; } - result = check_mte_memory(ptr, sizes[run], mode, TAG_CHECK_ON); + result = check_mte_memory(ptr, sizes[run], mode, TAG_CHECK_ON, atag_check, TAG_OP_ALL); mte_free_memory_tag_range((void *)ptr, sizes[run], mem_type, UNDERFLOW, OVERFLOW); close(fd); if (result != KSFT_PASS) - return KSFT_FAIL; + return result; } return KSFT_PASS; } +const char *format_test_name(struct check_mmap_testcase *tc) +{ + static char test_name[TEST_NAME_MAX]; + const char *check_type_str; + const char *mem_type_str; + const char *sync_str; + const char *mapping_str; + const char *tag_check_str; + const char *atag_check_str; + const char *tag_op_str; + + switch (tc->check_type) { + case CHECK_ANON_MEM: + check_type_str = "anonymous memory"; + break; + case CHECK_FILE_MEM: + check_type_str = "file memory"; + break; + case CHECK_CLEAR_PROT_MTE: + check_type_str = "clear PROT_MTE flags"; + break; + default: + assert(0); + break; + } + + switch (tc->mem_type) { + case USE_MMAP: + mem_type_str = "mmap"; + break; + case USE_MPROTECT: + mem_type_str = "mmap/mprotect"; + break; + default: + assert(0); + break; + } + + switch (tc->mte_sync) { + case MTE_NONE_ERR: + sync_str = "no error"; + break; + case MTE_SYNC_ERR: + sync_str = "sync error"; + break; + case MTE_ASYNC_ERR: + sync_str = "async error"; + break; + default: + assert(0); + break; + } + + switch (tc->mapping) { + case MAP_SHARED: + mapping_str = "shared"; + break; + case MAP_PRIVATE: + mapping_str = "private"; + break; + default: + assert(0); + break; + } + + switch (tc->tag_check) { + case TAG_CHECK_ON: + tag_check_str = "tag check on"; + break; + case TAG_CHECK_OFF: + tag_check_str = "tag check off"; + break; + default: + assert(0); + break; + } + + switch (tc->atag_check) { + case ATAG_CHECK_ON: + atag_check_str = "with address tag [63:60]"; + break; + case ATAG_CHECK_OFF: + atag_check_str = "without address tag [63:60]"; + break; + default: + assert(0); + break; + } + + snprintf(test_name, sizeof(test_name), + "Check %s with %s mapping, %s mode, %s memory and %s (%s)\n", + check_type_str, mapping_str, sync_str, mem_type_str, + tag_check_str, atag_check_str); + + switch (tc->tag_op) { + case TAG_OP_ALL: + tag_op_str = ""; + break; + case TAG_OP_STONLY: + tag_op_str = " / store-only"; + break; + default: + assert(0); + break; + } + + snprintf(test_name, TEST_NAME_MAX, + "Check %s with %s mapping, %s mode, %s memory and %s (%s%s)\n", + check_type_str, mapping_str, sync_str, mem_type_str, + tag_check_str, atag_check_str, tag_op_str); + + return test_name; +} + int main(int argc, char *argv[]) { - int err; + int err, i; int item = ARRAY_SIZE(sizes); + struct check_mmap_testcase test_cases[]= { + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_OFF, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = true, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_OFF, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = true, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_NONE_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_OFF, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_NONE_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_OFF, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_CLEAR_PROT_MTE, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_CLEAR_PROT_MTE, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_OFF, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_ANON_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_SHARED, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_FILE_MEM, + .mem_type = USE_MMAP, + .mte_sync = MTE_ASYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_STONLY, + .enable_tco = false, + }, + { + .check_type = CHECK_CLEAR_PROT_MTE, + .mem_type = USE_MMAP, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + { + .check_type = CHECK_CLEAR_PROT_MTE, + .mem_type = USE_MPROTECT, + .mte_sync = MTE_SYNC_ERR, + .mapping = MAP_PRIVATE, + .tag_check = TAG_CHECK_ON, + .atag_check = ATAG_CHECK_ON, + .tag_op = TAG_OP_ALL, + .enable_tco = false, + }, + }; err = mte_default_setup(); if (err) @@ -200,64 +957,51 @@ int main(int argc, char *argv[]) sizes[item - 2] = page_size; sizes[item - 1] = page_size + 1; - /* Register signal handlers */ - mte_register_signal(SIGBUS, mte_default_handler); - mte_register_signal(SIGSEGV, mte_default_handler); - /* Set test plan */ - ksft_set_plan(22); + ksft_set_plan(ARRAY_SIZE(test_cases)); - mte_enable_pstate_tco(); + for (i = 0 ; i < ARRAY_SIZE(test_cases); i++) { + /* Register signal handlers */ + mte_register_signal(SIGBUS, mte_default_handler, + test_cases[i].atag_check == ATAG_CHECK_ON); + mte_register_signal(SIGSEGV, mte_default_handler, + test_cases[i].atag_check == ATAG_CHECK_ON); - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_OFF), - "Check anonymous memory with private mapping, sync error mode, mmap memory and tag check off\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_OFF), - "Check file memory with private mapping, sync error mode, mmap/mprotect memory and tag check off\n"); + if (test_cases[i].enable_tco) + mte_enable_pstate_tco(); + else + mte_disable_pstate_tco(); - mte_disable_pstate_tco(); - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE, TAG_CHECK_OFF), - "Check anonymous memory with private mapping, no error mode, mmap memory and tag check off\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_NONE_ERR, MAP_PRIVATE, TAG_CHECK_OFF), - "Check file memory with private mapping, no error mode, mmap/mprotect memory and tag check off\n"); - - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check anonymous memory with private mapping, sync error mode, mmap memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check anonymous memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check anonymous memory with shared mapping, sync error mode, mmap memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check anonymous memory with shared mapping, sync error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check anonymous memory with private mapping, async error mode, mmap memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check anonymous memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check anonymous memory with shared mapping, async error mode, mmap memory and tag check on\n"); - evaluate_test(check_anonymous_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check anonymous memory with shared mapping, async error mode, mmap/mprotect memory and tag check on\n"); - - evaluate_test(check_file_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check file memory with private mapping, sync error mode, mmap memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check file memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check file memory with shared mapping, sync error mode, mmap memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check file memory with shared mapping, sync error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check file memory with private mapping, async error mode, mmap memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE, TAG_CHECK_ON), - "Check file memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check file memory with shared mapping, async error mode, mmap memory and tag check on\n"); - evaluate_test(check_file_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_SHARED, TAG_CHECK_ON), - "Check file memory with shared mapping, async error mode, mmap/mprotect memory and tag check on\n"); - - evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE), - "Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n"); - evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE), - "Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n"); + switch (test_cases[i].check_type) { + case CHECK_ANON_MEM: + evaluate_test(check_anonymous_memory_mapping(test_cases[i].mem_type, + test_cases[i].mte_sync, + test_cases[i].mapping, + test_cases[i].tag_check, + test_cases[i].atag_check, + test_cases[i].tag_op), + format_test_name(&test_cases[i])); + break; + case CHECK_FILE_MEM: + evaluate_test(check_file_memory_mapping(test_cases[i].mem_type, + test_cases[i].mte_sync, + test_cases[i].mapping, + test_cases[i].tag_check, + test_cases[i].atag_check, + test_cases[i].tag_op), + format_test_name(&test_cases[i])); + break; + case CHECK_CLEAR_PROT_MTE: + evaluate_test(check_clear_prot_mte_flag(test_cases[i].mem_type, + test_cases[i].mte_sync, + test_cases[i].mapping, + test_cases[i].atag_check), + format_test_name(&test_cases[i])); + break; + default: + exit(KSFT_FAIL); + } + } mte_restore_setup(); ksft_print_cnts(); diff --git a/tools/testing/selftests/arm64/mte/check_prctl.c b/tools/testing/selftests/arm64/mte/check_prctl.c index 4c89e9538ca0..f7f320defa7b 100644 --- a/tools/testing/selftests/arm64/mte/check_prctl.c +++ b/tools/testing/selftests/arm64/mte/check_prctl.c @@ -12,6 +12,10 @@ #include "kselftest.h" +#ifndef AT_HWCAP3 +#define AT_HWCAP3 29 +#endif + static int set_tagged_addr_ctrl(int val) { int ret; @@ -60,7 +64,7 @@ void check_basic_read(void) /* * Attempt to set a specified combination of modes. */ -void set_mode_test(const char *name, int hwcap2, int mask) +void set_mode_test(const char *name, int hwcap2, int hwcap3, int mask) { int ret; @@ -69,6 +73,11 @@ void set_mode_test(const char *name, int hwcap2, int mask) return; } + if ((getauxval(AT_HWCAP3) & hwcap3) != hwcap3) { + ksft_test_result_skip("%s\n", name); + return; + } + ret = set_tagged_addr_ctrl(mask); if (ret < 0) { ksft_test_result_fail("%s\n", name); @@ -81,7 +90,7 @@ void set_mode_test(const char *name, int hwcap2, int mask) return; } - if ((ret & PR_MTE_TCF_MASK) == mask) { + if ((ret & (PR_MTE_TCF_MASK | PR_MTE_STORE_ONLY)) == mask) { ksft_test_result_pass("%s\n", name); } else { ksft_print_msg("Got %x, expected %x\n", @@ -93,12 +102,16 @@ void set_mode_test(const char *name, int hwcap2, int mask) struct mte_mode { int mask; int hwcap2; + int hwcap3; const char *name; } mte_modes[] = { - { PR_MTE_TCF_NONE, 0, "NONE" }, - { PR_MTE_TCF_SYNC, HWCAP2_MTE, "SYNC" }, - { PR_MTE_TCF_ASYNC, HWCAP2_MTE, "ASYNC" }, - { PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC, HWCAP2_MTE, "SYNC+ASYNC" }, + { PR_MTE_TCF_NONE, 0, 0, "NONE" }, + { PR_MTE_TCF_SYNC, HWCAP2_MTE, 0, "SYNC" }, + { PR_MTE_TCF_ASYNC, HWCAP2_MTE, 0, "ASYNC" }, + { PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC, HWCAP2_MTE, 0, "SYNC+ASYNC" }, + { PR_MTE_TCF_SYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+STONLY" }, + { PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "ASYNC+STONLY" }, + { PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+ASYNC+STONLY" }, }; int main(void) @@ -106,11 +119,11 @@ int main(void) int i; ksft_print_header(); - ksft_set_plan(5); + ksft_set_plan(ARRAY_SIZE(mte_modes)); check_basic_read(); for (i = 0; i < ARRAY_SIZE(mte_modes); i++) - set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2, + set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2, mte_modes[i].hwcap3, mte_modes[i].mask); ksft_print_cnts(); diff --git a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c index a3d1e23fe02a..4b764f2a8185 100644 --- a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c +++ b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c @@ -57,7 +57,7 @@ static int check_single_included_tags(int mem_type, int mode) return KSFT_FAIL; for (tag = 0; (tag < MT_TAG_COUNT) && (result == KSFT_PASS); tag++) { - ret = mte_switch_mode(mode, MT_INCLUDE_VALID_TAG(tag)); + ret = mte_switch_mode(mode, MT_INCLUDE_VALID_TAG(tag), false); if (ret != 0) result = KSFT_FAIL; /* Try to catch a excluded tag by a number of tries. */ @@ -91,7 +91,7 @@ static int check_multiple_included_tags(int mem_type, int mode) for (tag = 0; (tag < MT_TAG_COUNT - 1) && (result == KSFT_PASS); tag++) { excl_mask |= 1 << tag; - mte_switch_mode(mode, MT_INCLUDE_VALID_TAGS(excl_mask)); + mte_switch_mode(mode, MT_INCLUDE_VALID_TAGS(excl_mask), false); /* Try to catch a excluded tag by a number of tries. */ for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) { ptr = mte_insert_tags(ptr, BUFFER_SIZE); @@ -120,7 +120,7 @@ static int check_all_included_tags(int mem_type, int mode) mem_type, false) != KSFT_PASS) return KSFT_FAIL; - ret = mte_switch_mode(mode, MT_INCLUDE_TAG_MASK); + ret = mte_switch_mode(mode, MT_INCLUDE_TAG_MASK, false); if (ret != 0) return KSFT_FAIL; /* Try to catch a excluded tag by a number of tries. */ @@ -145,7 +145,7 @@ static int check_none_included_tags(int mem_type, int mode) if (check_allocated_memory(ptr, BUFFER_SIZE, mem_type, false) != KSFT_PASS) return KSFT_FAIL; - ret = mte_switch_mode(mode, MT_EXCLUDE_TAG_MASK); + ret = mte_switch_mode(mode, MT_EXCLUDE_TAG_MASK, false); if (ret != 0) return KSFT_FAIL; /* Try to catch a excluded tag by a number of tries. */ @@ -180,7 +180,7 @@ int main(int argc, char *argv[]) return err; /* Register SIGSEGV handler */ - mte_register_signal(SIGSEGV, mte_default_handler); + mte_register_signal(SIGSEGV, mte_default_handler, false); /* Set test plan */ ksft_set_plan(4); diff --git a/tools/testing/selftests/arm64/mte/check_user_mem.c b/tools/testing/selftests/arm64/mte/check_user_mem.c index f4ae5f87a3b7..fb7936c4e097 100644 --- a/tools/testing/selftests/arm64/mte/check_user_mem.c +++ b/tools/testing/selftests/arm64/mte/check_user_mem.c @@ -44,7 +44,7 @@ static int check_usermem_access_fault(int mem_type, int mode, int mapping, err = KSFT_PASS; len = 2 * page_sz; - mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false); fd = create_temp_file(); if (fd == -1) return KSFT_FAIL; @@ -211,7 +211,7 @@ int main(int argc, char *argv[]) return err; /* Register signal handlers */ - mte_register_signal(SIGSEGV, mte_default_handler); + mte_register_signal(SIGSEGV, mte_default_handler, false); /* Set test plan */ ksft_set_plan(64); diff --git a/tools/testing/selftests/arm64/mte/mte_common_util.c b/tools/testing/selftests/arm64/mte/mte_common_util.c index a1dc2fe5285b..397e57dd946a 100644 --- a/tools/testing/selftests/arm64/mte/mte_common_util.c +++ b/tools/testing/selftests/arm64/mte/mte_common_util.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -19,20 +20,40 @@ #include "mte_common_util.h" #include "mte_def.h" +#ifndef SA_EXPOSE_TAGBITS +#define SA_EXPOSE_TAGBITS 0x00000800 +#endif + #define INIT_BUFFER_SIZE 256 struct mte_fault_cxt cur_mte_cxt; +bool mtefar_support; +bool mtestonly_support; static unsigned int mte_cur_mode; static unsigned int mte_cur_pstate_tco; +static bool mte_cur_stonly; void mte_default_handler(int signum, siginfo_t *si, void *uc) { + struct sigaction sa; unsigned long addr = (unsigned long)si->si_addr; + unsigned char si_tag, si_atag; + + sigaction(signum, NULL, &sa); + + if (sa.sa_flags & SA_EXPOSE_TAGBITS) { + si_tag = MT_FETCH_TAG(addr); + si_atag = MT_FETCH_ATAG(addr); + addr = MT_CLEAR_TAGS(addr); + } else { + si_tag = 0; + si_atag = 0; + } if (signum == SIGSEGV) { #ifdef DEBUG - ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx\n", - ((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code); + ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx, si_tag=%x, si_atag=%x\n", + ((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code, si_tag, si_atag); #endif if (si->si_code == SEGV_MTEAERR) { if (cur_mte_cxt.trig_si_code == si->si_code) @@ -45,13 +66,18 @@ void mte_default_handler(int signum, siginfo_t *si, void *uc) } /* Compare the context for precise error */ else if (si->si_code == SEGV_MTESERR) { + if ((!mtefar_support && si_atag) || (si_atag != MT_FETCH_ATAG(cur_mte_cxt.trig_addr))) { + ksft_print_msg("Invalid MTE synchronous exception caught for address tag! si_tag=%x, si_atag: %x\n", si_tag, si_atag); + exit(KSFT_FAIL); + } + if (cur_mte_cxt.trig_si_code == si->si_code && ((cur_mte_cxt.trig_range >= 0 && - addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) && - addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) || + addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) && + addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) || (cur_mte_cxt.trig_range < 0 && - addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) && - addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) { + addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) && + addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) { cur_mte_cxt.fault_valid = true; /* Adjust the pc by 4 */ ((ucontext_t *)uc)->uc_mcontext.pc += 4; @@ -67,11 +93,11 @@ void mte_default_handler(int signum, siginfo_t *si, void *uc) ksft_print_msg("INFO: SIGBUS signal at pc=%llx, fault addr=%lx, si_code=%x\n", ((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code); if ((cur_mte_cxt.trig_range >= 0 && - addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) && - addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) || + addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) && + addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) || (cur_mte_cxt.trig_range < 0 && - addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) && - addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) { + addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) && + addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) { cur_mte_cxt.fault_valid = true; /* Adjust the pc by 4 */ ((ucontext_t *)uc)->uc_mcontext.pc += 4; @@ -79,12 +105,17 @@ void mte_default_handler(int signum, siginfo_t *si, void *uc) } } -void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *)) +void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *), + bool export_tags) { struct sigaction sa; sa.sa_sigaction = handler; sa.sa_flags = SA_SIGINFO; + + if (export_tags && signal == SIGSEGV) + sa.sa_flags |= SA_EXPOSE_TAGBITS; + sigemptyset(&sa.sa_mask); sigaction(signal, &sa, NULL); } @@ -120,6 +151,19 @@ void mte_clear_tags(void *ptr, size_t size) mte_clear_tag_address_range(ptr, size); } +void *mte_insert_atag(void *ptr) +{ + unsigned char atag; + + atag = mtefar_support ? (random() % MT_ATAG_MASK) + 1 : 0; + return (void *)MT_SET_ATAG((unsigned long)ptr, atag); +} + +void *mte_clear_atag(void *ptr) +{ + return (void *)MT_CLEAR_ATAG((unsigned long)ptr); +} + static void *__mte_allocate_memory_range(size_t size, int mem_type, int mapping, size_t range_before, size_t range_after, bool tags, int fd) @@ -272,7 +316,7 @@ void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range) cur_mte_cxt.trig_si_code = 0; } -int mte_switch_mode(int mte_option, unsigned long incl_mask) +int mte_switch_mode(int mte_option, unsigned long incl_mask, bool stonly) { unsigned long en = 0; @@ -304,6 +348,9 @@ int mte_switch_mode(int mte_option, unsigned long incl_mask) break; } + if (mtestonly_support && stonly) + en |= PR_MTE_STORE_ONLY; + en |= (incl_mask << PR_MTE_TAG_SHIFT); /* Enable address tagging ABI, mte error reporting mode and tag inclusion mask. */ if (prctl(PR_SET_TAGGED_ADDR_CTRL, en, 0, 0, 0) != 0) { @@ -316,12 +363,21 @@ int mte_switch_mode(int mte_option, unsigned long incl_mask) int mte_default_setup(void) { unsigned long hwcaps2 = getauxval(AT_HWCAP2); + unsigned long hwcaps3 = getauxval(AT_HWCAP3); unsigned long en = 0; int ret; + /* To generate random address tag */ + srandom(time(NULL)); + if (!(hwcaps2 & HWCAP2_MTE)) ksft_exit_skip("MTE features unavailable\n"); + mtefar_support = !!(hwcaps3 & HWCAP3_MTE_FAR); + + if (hwcaps3 & HWCAP3_MTE_STORE_ONLY) + mtestonly_support = true; + /* Get current mte mode */ ret = prctl(PR_GET_TAGGED_ADDR_CTRL, en, 0, 0, 0); if (ret < 0) { @@ -335,6 +391,8 @@ int mte_default_setup(void) else if (ret & PR_MTE_TCF_NONE) mte_cur_mode = MTE_NONE_ERR; + mte_cur_stonly = (ret & PR_MTE_STORE_ONLY) ? true : false; + mte_cur_pstate_tco = mte_get_pstate_tco(); /* Disable PSTATE.TCO */ mte_disable_pstate_tco(); @@ -343,7 +401,7 @@ int mte_default_setup(void) void mte_restore_setup(void) { - mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG); + mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG, mte_cur_stonly); if (mte_cur_pstate_tco == MT_PSTATE_TCO_EN) mte_enable_pstate_tco(); else if (mte_cur_pstate_tco == MT_PSTATE_TCO_DIS) diff --git a/tools/testing/selftests/arm64/mte/mte_common_util.h b/tools/testing/selftests/arm64/mte/mte_common_util.h index a0017a303beb..250d671329a5 100644 --- a/tools/testing/selftests/arm64/mte/mte_common_util.h +++ b/tools/testing/selftests/arm64/mte/mte_common_util.h @@ -37,10 +37,13 @@ struct mte_fault_cxt { }; extern struct mte_fault_cxt cur_mte_cxt; +extern bool mtefar_support; +extern bool mtestonly_support; /* MTE utility functions */ void mte_default_handler(int signum, siginfo_t *si, void *uc); -void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *)); +void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *), + bool export_tags); void mte_wait_after_trig(void); void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags); void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping, @@ -54,9 +57,11 @@ void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type, size_t range_before, size_t range_after); void *mte_insert_tags(void *ptr, size_t size); void mte_clear_tags(void *ptr, size_t size); +void *mte_insert_atag(void *ptr); +void *mte_clear_atag(void *ptr); int mte_default_setup(void); void mte_restore_setup(void); -int mte_switch_mode(int mte_option, unsigned long incl_mask); +int mte_switch_mode(int mte_option, unsigned long incl_mask, bool stonly); void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range); /* Common utility functions */ diff --git a/tools/testing/selftests/arm64/mte/mte_def.h b/tools/testing/selftests/arm64/mte/mte_def.h index 9b188254b61a..6ad22f07c9b8 100644 --- a/tools/testing/selftests/arm64/mte/mte_def.h +++ b/tools/testing/selftests/arm64/mte/mte_def.h @@ -42,6 +42,8 @@ #define MT_TAG_COUNT 16 #define MT_INCLUDE_TAG_MASK 0xFFFF #define MT_EXCLUDE_TAG_MASK 0x0 +#define MT_ATAG_SHIFT 60 +#define MT_ATAG_MASK 0xFUL #define MT_ALIGN_GRANULE (MT_GRANULE_SIZE - 1) #define MT_CLEAR_TAG(x) ((x) & ~(MT_TAG_MASK << MT_TAG_SHIFT)) @@ -49,6 +51,12 @@ #define MT_FETCH_TAG(x) ((x >> MT_TAG_SHIFT) & (MT_TAG_MASK)) #define MT_ALIGN_UP(x) ((x + MT_ALIGN_GRANULE) & ~(MT_ALIGN_GRANULE)) +#define MT_CLEAR_ATAG(x) ((x) & ~(MT_TAG_MASK << MT_ATAG_SHIFT)) +#define MT_SET_ATAG(x, y) ((x) | (((y) & MT_ATAG_MASK) << MT_ATAG_SHIFT)) +#define MT_FETCH_ATAG(x) ((x >> MT_ATAG_SHIFT) & (MT_ATAG_MASK)) + +#define MT_CLEAR_TAGS(x) (MT_CLEAR_ATAG(MT_CLEAR_TAG(x))) + #define MT_PSTATE_TCO_SHIFT 25 #define MT_PSTATE_TCO_MASK ~(0x1 << MT_PSTATE_TCO_SHIFT) #define MT_PSTATE_TCO_EN 1 diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index e2a2c46c008b..3d8378972d26 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -21,7 +21,6 @@ test_lirc_mode2_user flow_dissector_load test_tcpnotify_user test_libbpf -test_sysctl xdping test_cpp *.d diff --git a/tools/testing/selftests/bpf/DENYLIST b/tools/testing/selftests/bpf/DENYLIST index 1789a61d0a9b..f748f2c33b22 100644 --- a/tools/testing/selftests/bpf/DENYLIST +++ b/tools/testing/selftests/bpf/DENYLIST @@ -1,6 +1,5 @@ # TEMPORARY # Alphabetical order -dynptr/test_probe_read_user_str_dynptr # disabled until https://patchwork.kernel.org/project/linux-mm/patch/20250422131449.57177-1-mykyta.yatsenko5@gmail.com/ makes it into the bpf-next get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge stacktrace_build_id stacktrace_build_id_nmi diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 deleted file mode 100644 index 12e99c0277a8..000000000000 --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 +++ /dev/null @@ -1 +0,0 @@ -tracing_struct/struct_many_args # struct_many_args:FAIL:tracing_struct_many_args__attach unexpected error: -524 diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index cf5ed3bee573..4863106034df 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -73,7 +73,7 @@ endif # Order correspond to 'make run_tests' order TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_progs \ test_sockmap \ - test_tcpnotify_user test_sysctl \ + test_tcpnotify_user \ test_progs-no_alu32 TEST_INST_SUBDIRS := no_alu32 @@ -109,6 +109,7 @@ TEST_PROGS := test_kmod.sh \ test_xdping.sh \ test_bpftool_build.sh \ test_bpftool.sh \ + test_bpftool_map.sh \ test_bpftool_metadata.sh \ test_doc_build.sh \ test_xsk.sh \ @@ -220,7 +221,7 @@ ifeq ($(VMLINUX_BTF),) $(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)") endif -# Define simple and short `make test_progs`, `make test_sysctl`, etc targets +# Define simple and short `make test_progs`, `make test_maps`, etc targets # to build individual tests. # NOTE: Semicolon at the end is critical to override lib.mk's default static # rule for binaries. @@ -329,7 +330,6 @@ NETWORK_HELPERS := $(OUTPUT)/network_helpers.o $(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS) $(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS) -$(OUTPUT)/test_sysctl: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(OUTPUT)/test_tag: $(TESTING_HELPERS) $(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS) $(OUTPUT)/xdping: $(TESTING_HELPERS) @@ -841,6 +841,11 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \ $(call msg,BINARY,,$@) $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ +# This works around GCC warning about snprintf truncating strings like: +# +# char a[PATH_MAX], b[PATH_MAX]; +# snprintf(a, "%s/foo", b); // triggers -Wformat-truncation +$(OUTPUT)/veristat.o: CFLAGS += -Wno-format-truncation $(OUTPUT)/veristat.o: $(BPFOBJ) $(OUTPUT)/veristat: $(OUTPUT)/veristat.o $(call msg,BINARY,,$@) diff --git a/tools/testing/selftests/bpf/bpf_arena_common.h b/tools/testing/selftests/bpf/bpf_arena_common.h index 68a51dcc0669..16f8ce832004 100644 --- a/tools/testing/selftests/bpf/bpf_arena_common.h +++ b/tools/testing/selftests/bpf/bpf_arena_common.h @@ -46,8 +46,11 @@ void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt, int node_id, __u64 flags) __ksym __weak; +int bpf_arena_reserve_pages(void *map, void __arena *addr, __u32 page_cnt) __ksym __weak; void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak; +#define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start) + #else /* when compiled as user space code */ #define __arena diff --git a/tools/testing/selftests/bpf/bpf_atomic.h b/tools/testing/selftests/bpf/bpf_atomic.h index a9674e544322..c550e5711967 100644 --- a/tools/testing/selftests/bpf/bpf_atomic.h +++ b/tools/testing/selftests/bpf/bpf_atomic.h @@ -61,7 +61,7 @@ extern bool CONFIG_X86_64 __kconfig __weak; #define smp_mb() \ ({ \ - unsigned long __val; \ + volatile unsigned long __val; \ __sync_fetch_and_add(&__val, 0); \ }) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 5e512a1d09d1..da7e230f2781 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -596,4 +596,7 @@ extern int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) __weak __ksym; extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym; extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym; +extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str, + struct bpf_dynptr *value_p) __weak __ksym; + #endif diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h index 8215c9b3115e..9386dfe8b884 100644 --- a/tools/testing/selftests/bpf/bpf_kfuncs.h +++ b/tools/testing/selftests/bpf/bpf_kfuncs.h @@ -69,7 +69,7 @@ extern int bpf_get_file_xattr(struct file *file, const char *name, struct bpf_dynptr *value_ptr) __ksym; extern int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *digest_ptr) __ksym; -extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym; extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym; extern void bpf_key_put(struct bpf_key *key) __ksym; extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index e4535451322e..15f626014872 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -318,6 +319,26 @@ int join_parent_cgroup(const char *relative_path) return join_cgroup_from_top(cgroup_path); } +/** + * set_cgroup_xattr() - Set xattr on a cgroup dir + * @relative_path: The cgroup path, relative to the workdir, to set xattr + * @name: xattr name + * @value: xattr value + * + * This function set xattr on cgroup dir. + * + * On success, it returns 0, otherwise on failure it returns -1. + */ +int set_cgroup_xattr(const char *relative_path, + const char *name, + const char *value) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, relative_path); + return setxattr(cgroup_path, name, value, strlen(value) + 1, 0); +} + /** * __cleanup_cgroup_environment() - Delete temporary cgroups * diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h index 502845160d88..182e1ac36c95 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.h +++ b/tools/testing/selftests/bpf/cgroup_helpers.h @@ -26,6 +26,10 @@ int join_cgroup(const char *relative_path); int join_root_cgroup(void); int join_parent_cgroup(const char *relative_path); +int set_cgroup_xattr(const char *relative_path, + const char *name, + const char *value); + int setup_cgroup_environment(void); void cleanup_cgroup_environment(void); diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index f74e1ea0ad3b..8916ab814a3e 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -97,6 +97,9 @@ CONFIG_NF_TABLES_NETDEV=y CONFIG_NF_TABLES_IPV4=y CONFIG_NF_TABLES_IPV6=y CONFIG_NETFILTER_INGRESS=y +CONFIG_IP_NF_IPTABLES_LEGACY=y +CONFIG_IP6_NF_IPTABLES_LEGACY=y +CONFIG_NETFILTER_XTABLES_LEGACY=y CONFIG_NF_FLOW_TABLE=y CONFIG_NF_FLOW_TABLE_INET=y CONFIG_NETFILTER_NETLINK=y @@ -105,6 +108,7 @@ CONFIG_IP_NF_IPTABLES=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_FILTER=y CONFIG_NF_NAT=y +CONFIG_PACKET=y CONFIG_RC_CORE=y CONFIG_SECURITY=y CONFIG_SECURITYFS=y diff --git a/tools/testing/selftests/bpf/config.ppc64el b/tools/testing/selftests/bpf/config.ppc64el new file mode 100644 index 000000000000..9acf389dc4ce --- /dev/null +++ b/tools/testing/selftests/bpf/config.ppc64el @@ -0,0 +1,93 @@ +CONFIG_ALTIVEC=y +CONFIG_AUDIT=y +CONFIG_BLK_CGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BONDING=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_BPF_PRELOAD_UMD=y +CONFIG_BPF_PRELOAD=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CGROUP_NET_CLASSID=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CGROUPS=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=hvc0 wg.success=hvc1 panic_on_warn=1" +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPUSETS=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_FS=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DEVTMPFS=y +CONFIG_EXPERT=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_FRAME_POINTER=y +CONFIG_FRAME_WARN=1280 +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HUGETLBFS=y +CONFIG_HVC_CONSOLE=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_JUMP_LABEL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_KPROBES=y +CONFIG_MEMCG=y +CONFIG_NAMESPACES=y +CONFIG_NET_ACT_BPF=y +CONFIG_NETDEVICES=y +CONFIG_NETFILTER_XT_MATCH_BPF=y +CONFIG_NET_L3_MASTER_DEV=y +CONFIG_NET_VRF=y +CONFIG_NET=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NONPORTABLE=y +CONFIG_NR_CPUS=256 +CONFIG_PACKET=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI=y +CONFIG_POSIX_MQUEUE=y +CONFIG_PPC64=y +CONFIG_PPC_OF_BOOT_TRAMPOLINE=y +CONFIG_PPC_PSERIES=y +CONFIG_PPC_RADIX_MMU=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_KCORE=y +CONFIG_PROFILING=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RT_GROUP_SCHED=y +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SMP=y +CONFIG_SOC_VIRT=y +CONFIG_SYSVIPC=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_THREAD_SHIFT=14 +CONFIG_TLS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS=y +CONFIG_TUN=y +CONFIG_UNIX=y +CONFIG_UPROBES=y +CONFIG_USER_NS=y +CONFIG_VETH=y +CONFIG_VLAN_8021Q=y +CONFIG_VSOCKETS_LOOPBACK=y +CONFIG_VSX=y +CONFIG_XFRM_USER=y diff --git a/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c b/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c index 67557cda2208..42b49870e520 100644 --- a/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c +++ b/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c @@ -13,7 +13,7 @@ static void test_fail_cases(void) { LIBBPF_OPTS(bpf_map_create_opts, opts); - __u32 value; + __u32 value = 0; int fd, err; /* Invalid key size */ diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c index 6befa870434b..4a0670c056ba 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c @@ -489,10 +489,28 @@ static void pe_subtest(struct test_bpf_cookie *skel) bpf_link__destroy(link); } +static int verify_tracing_link_info(int fd, u64 cookie) +{ + struct bpf_link_info info; + int err; + u32 len = sizeof(info); + + err = bpf_link_get_info_by_fd(fd, &info, &len); + if (!ASSERT_OK(err, "get_link_info")) + return -1; + + if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_TRACING, "link_type")) + return -1; + + ASSERT_EQ(info.tracing.cookie, cookie, "tracing_cookie"); + + return 0; +} + static void tracing_subtest(struct test_bpf_cookie *skel) { __u64 cookie; - int prog_fd; + int prog_fd, err; int fentry_fd = -1, fexit_fd = -1, fmod_ret_fd = -1; LIBBPF_OPTS(bpf_test_run_opts, opts); LIBBPF_OPTS(bpf_link_create_opts, link_opts); @@ -507,6 +525,10 @@ static void tracing_subtest(struct test_bpf_cookie *skel) if (!ASSERT_GE(fentry_fd, 0, "fentry.link_create")) goto cleanup; + err = verify_tracing_link_info(fentry_fd, cookie); + if (!ASSERT_OK(err, "verify_tracing_link_info")) + goto cleanup; + cookie = 0x20000000000000L; prog_fd = bpf_program__fd(skel->progs.fexit_test1); link_opts.tracing.cookie = cookie; @@ -635,10 +657,29 @@ static void tp_btf_subtest(struct test_bpf_cookie *skel) bpf_link__destroy(link); } +static int verify_raw_tp_link_info(int fd, u64 cookie) +{ + struct bpf_link_info info; + int err; + u32 len = sizeof(info); + + memset(&info, 0, sizeof(info)); + err = bpf_link_get_info_by_fd(fd, &info, &len); + if (!ASSERT_OK(err, "get_link_info")) + return -1; + + if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_RAW_TRACEPOINT, "link_type")) + return -1; + + ASSERT_EQ(info.raw_tracepoint.cookie, cookie, "raw_tp_cookie"); + + return 0; +} + static void raw_tp_subtest(struct test_bpf_cookie *skel) { __u64 cookie; - int prog_fd, link_fd = -1; + int err, prog_fd, link_fd = -1; struct bpf_link *link = NULL; LIBBPF_OPTS(bpf_raw_tp_opts, raw_tp_opts); LIBBPF_OPTS(bpf_raw_tracepoint_opts, opts); @@ -656,6 +697,11 @@ static void raw_tp_subtest(struct test_bpf_cookie *skel) goto cleanup; usleep(1); /* trigger */ + + err = verify_raw_tp_link_info(link_fd, cookie); + if (!ASSERT_OK(err, "verify_raw_tp_link_info")) + goto cleanup; + close(link_fd); /* detach */ link_fd = -1; diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index add4a18c33bd..5225d69bf79b 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -323,7 +323,7 @@ static void test_task_pidfd(void) static void test_task_sleepable(void) { struct bpf_iter_tasks *skel; - int pid, status, err, data_pipe[2], finish_pipe[2], c; + int pid, status, err, data_pipe[2], finish_pipe[2], c = 0; char *test_data = NULL; char *test_data_long = NULL; char *data[2]; diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c index fe2c502e5089..ecc3d47919ad 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c @@ -78,7 +78,7 @@ static int test_setup_uffd(void *fault_addr) } uffd_register.range.start = (unsigned long)fault_addr; - uffd_register.range.len = 4096; + uffd_register.range.len = getpagesize(); uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) { close(uffd); diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index c0a776feec23..82903585c870 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -879,6 +879,122 @@ static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d, "static int bpf_cgrp_storage_busy = (int)2", 2); } +struct btf_dump_string_ctx { + struct btf *btf; + struct btf_dump *d; + char *str; + struct btf_dump_type_data_opts *opts; + int array_id; +}; + +static int btf_dump_one_string(struct btf_dump_string_ctx *ctx, + char *ptr, size_t ptr_sz, + const char *expected_val) +{ + size_t type_sz; + int ret; + + ctx->str[0] = '\0'; + type_sz = btf__resolve_size(ctx->btf, ctx->array_id); + ret = btf_dump__dump_type_data(ctx->d, ctx->array_id, ptr, ptr_sz, ctx->opts); + if (type_sz <= ptr_sz) { + if (!ASSERT_EQ(ret, type_sz, "failed/unexpected type_sz")) + return -EINVAL; + } + if (!ASSERT_STREQ(ctx->str, expected_val, "ensure expected/actual match")) + return -EFAULT; + return 0; +} + +static void btf_dump_strings(struct btf_dump_string_ctx *ctx) +{ + struct btf_dump_type_data_opts *opts = ctx->opts; + + opts->emit_strings = true; + + opts->compact = true; + opts->emit_zeroes = false; + + opts->skip_names = false; + btf_dump_one_string(ctx, "foo", 4, "(char[4])\"foo\""); + + opts->skip_names = true; + btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + + /* This should have no effect. */ + opts->emit_zeroes = false; + btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + + /* This should have no effect. */ + opts->compact = false; + btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + + /* Non-printable characters come out as hex. */ + btf_dump_one_string(ctx, "fo\xff", 4, "\"fo\\xff\""); + btf_dump_one_string(ctx, "fo\x7", 4, "\"fo\\x07\""); + + /* + * Strings that are too long for the specified type ("char[4]") + * should fall back to the current behavior. + */ + opts->compact = true; + btf_dump_one_string(ctx, "abcde", 6, "['a','b','c','d',]"); + + /* + * Strings that are too short for the specified type ("char[4]") + * should work normally. + */ + btf_dump_one_string(ctx, "ab", 3, "\"ab\""); + + /* Non-NUL-terminated arrays don't get printed as strings. */ + char food[4] = { 'f', 'o', 'o', 'd' }; + char bye[3] = { 'b', 'y', 'e' }; + + btf_dump_one_string(ctx, food, 4, "['f','o','o','d',]"); + btf_dump_one_string(ctx, bye, 3, "['b','y','e',]"); + + /* The embedded NUL should terminate the string. */ + char embed[4] = { 'f', 'o', '\0', 'd' }; + + btf_dump_one_string(ctx, embed, 4, "\"fo\""); +} + +static void test_btf_dump_string_data(void) +{ + struct test_ctx t = {}; + char str[STRSIZE]; + struct btf_dump *d; + DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts); + struct btf_dump_string_ctx ctx; + int char_id, int_id, array_id; + + if (test_ctx__init(&t)) + return; + + d = btf_dump__new(t.btf, btf_dump_snprintf, str, NULL); + if (!ASSERT_OK_PTR(d, "could not create BTF dump")) + return; + + /* Generate BTF for a four-element char array. */ + char_id = btf__add_int(t.btf, "char", 1, BTF_INT_CHAR); + ASSERT_EQ(char_id, 1, "char_id"); + int_id = btf__add_int(t.btf, "int", 4, BTF_INT_SIGNED); + ASSERT_EQ(int_id, 2, "int_id"); + array_id = btf__add_array(t.btf, int_id, char_id, 4); + ASSERT_EQ(array_id, 3, "array_id"); + + ctx.btf = t.btf; + ctx.d = d; + ctx.str = str; + ctx.opts = &opts; + ctx.array_id = array_id; + + btf_dump_strings(&ctx); + + btf_dump__free(d); + test_ctx__free(&t); +} + static void test_btf_datasec(struct btf *btf, struct btf_dump *d, char *str, const char *name, const char *expected_val, void *data, size_t data_sz) @@ -970,6 +1086,8 @@ void test_btf_dump() { test_btf_dump_struct_data(btf, d, str); if (test__start_subtest("btf_dump: var_data")) test_btf_dump_var_data(btf, d, str); + if (test__start_subtest("btf_dump: string_data")) + test_btf_dump_string_data(); btf_dump__free(d); btf__free(btf); diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c new file mode 100644 index 000000000000..bb60704a3ef9 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include "cgroup_helpers.h" +#include "cgroup_mprog.skel.h" + +static void assert_mprog_count(int cg, int atype, int expected) +{ + __u32 count = 0, attach_flags = 0; + int err; + + err = bpf_prog_query(cg, atype, 0, &attach_flags, + NULL, &count); + ASSERT_EQ(count, expected, "count"); + ASSERT_EQ(err, 0, "prog_query"); +} + +static void test_prog_attach_detach(int atype) +{ + LIBBPF_OPTS(bpf_prog_attach_opts, opta); + LIBBPF_OPTS(bpf_prog_detach_opts, optd); + LIBBPF_OPTS(bpf_prog_query_opts, optq); + __u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; + struct cgroup_mprog *skel; + __u32 prog_ids[10]; + int cg, err; + + cg = test__join_cgroup("/prog_attach_detach"); + if (!ASSERT_GE(cg, 0, "join_cgroup /prog_attach_detach")) + return; + + skel = cgroup_mprog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + fd1 = bpf_program__fd(skel->progs.getsockopt_1); + fd2 = bpf_program__fd(skel->progs.getsockopt_2); + fd3 = bpf_program__fd(skel->progs.getsockopt_3); + fd4 = bpf_program__fd(skel->progs.getsockopt_4); + + id1 = id_from_prog_fd(fd1); + id2 = id_from_prog_fd(fd2); + id3 = id_from_prog_fd(fd3); + id4 = id_from_prog_fd(fd4); + + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, + .expected_revision = 1, + ); + + /* ordering: [fd1] */ + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup; + + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, + .expected_revision = 2, + ); + + /* ordering: [fd2, fd1] */ + err = bpf_prog_attach_opts(fd2, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup1; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, + .relative_fd = fd2, + .expected_revision = 3, + ); + + /* ordering: [fd2, fd3, fd1] */ + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 3); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI, + .expected_revision = 4, + ); + + /* ordering: [fd2, fd3, fd1, fd4] */ + err = bpf_prog_attach_opts(fd4, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup3; + + assert_mprog_count(cg, atype, 4); + + /* retrieve optq.prog_cnt */ + err = bpf_prog_query_opts(cg, atype, &optq); + if (!ASSERT_OK(err, "prog_query")) + goto cleanup4; + + /* optq.prog_cnt will be used in below query */ + memset(prog_ids, 0, sizeof(prog_ids)); + optq.prog_ids = prog_ids; + err = bpf_prog_query_opts(cg, atype, &optq); + if (!ASSERT_OK(err, "prog_query")) + goto cleanup4; + + ASSERT_EQ(optq.count, 4, "count"); + ASSERT_EQ(optq.revision, 5, "revision"); + ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); + ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); + ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); + ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); + ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); + ASSERT_EQ(optq.link_ids, NULL, "link_ids"); + +cleanup4: + optd.expected_revision = 5; + err = bpf_prog_detach_opts(fd4, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 3); + +cleanup3: + LIBBPF_OPTS_RESET(optd); + err = bpf_prog_detach_opts(fd3, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 2); + + /* Check revision after two detach operations */ + err = bpf_prog_query_opts(cg, atype, &optq); + ASSERT_OK(err, "prog_query"); + ASSERT_EQ(optq.revision, 7, "revision"); + +cleanup2: + err = bpf_prog_detach_opts(fd2, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 1); + +cleanup1: + err = bpf_prog_detach_opts(fd1, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 0); + +cleanup: + cgroup_mprog__destroy(skel); + close(cg); +} + +static void test_link_attach_detach(int atype) +{ + LIBBPF_OPTS(bpf_cgroup_opts, opta); + LIBBPF_OPTS(bpf_cgroup_opts, optd); + LIBBPF_OPTS(bpf_prog_query_opts, optq); + struct bpf_link *link1, *link2, *link3, *link4; + __u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; + struct cgroup_mprog *skel; + __u32 prog_ids[10]; + int cg, err; + + cg = test__join_cgroup("/link_attach_detach"); + if (!ASSERT_GE(cg, 0, "join_cgroup /link_attach_detach")) + return; + + skel = cgroup_mprog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + fd1 = bpf_program__fd(skel->progs.getsockopt_1); + fd2 = bpf_program__fd(skel->progs.getsockopt_2); + fd3 = bpf_program__fd(skel->progs.getsockopt_3); + fd4 = bpf_program__fd(skel->progs.getsockopt_4); + + id1 = id_from_prog_fd(fd1); + id2 = id_from_prog_fd(fd2); + id3 = id_from_prog_fd(fd3); + id4 = id_from_prog_fd(fd4); + + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .expected_revision = 1, + ); + + /* ordering: [fd1] */ + link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); + if (!ASSERT_OK_PTR(link1, "link_attach")) + goto cleanup; + + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_BEFORE | BPF_F_LINK, + .relative_id = id_from_link_fd(bpf_link__fd(link1)), + .expected_revision = 2, + ); + + /* ordering: [fd2, fd1] */ + link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); + if (!ASSERT_OK_PTR(link2, "link_attach")) + goto cleanup1; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_AFTER | BPF_F_LINK, + .relative_fd = bpf_link__fd(link2), + .expected_revision = 3, + ); + + /* ordering: [fd2, fd3, fd1] */ + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); + if (!ASSERT_OK_PTR(link3, "link_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 3); + + LIBBPF_OPTS_RESET(opta, + .expected_revision = 4, + ); + + /* ordering: [fd2, fd3, fd1, fd4] */ + link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); + if (!ASSERT_OK_PTR(link4, "link_attach")) + goto cleanup3; + + assert_mprog_count(cg, atype, 4); + + /* retrieve optq.prog_cnt */ + err = bpf_prog_query_opts(cg, atype, &optq); + if (!ASSERT_OK(err, "prog_query")) + goto cleanup4; + + /* optq.prog_cnt will be used in below query */ + memset(prog_ids, 0, sizeof(prog_ids)); + optq.prog_ids = prog_ids; + err = bpf_prog_query_opts(cg, atype, &optq); + if (!ASSERT_OK(err, "prog_query")) + goto cleanup4; + + ASSERT_EQ(optq.count, 4, "count"); + ASSERT_EQ(optq.revision, 5, "revision"); + ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); + ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); + ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); + ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); + ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); + ASSERT_EQ(optq.link_ids, NULL, "link_ids"); + +cleanup4: + bpf_link__destroy(link4); + assert_mprog_count(cg, atype, 3); + +cleanup3: + bpf_link__destroy(link3); + assert_mprog_count(cg, atype, 2); + + /* Check revision after two detach operations */ + err = bpf_prog_query_opts(cg, atype, &optq); + ASSERT_OK(err, "prog_query"); + ASSERT_EQ(optq.revision, 7, "revision"); + +cleanup2: + bpf_link__destroy(link2); + assert_mprog_count(cg, atype, 1); + +cleanup1: + bpf_link__destroy(link1); + assert_mprog_count(cg, atype, 0); + +cleanup: + cgroup_mprog__destroy(skel); + close(cg); +} + +static void test_preorder_prog_attach_detach(int atype) +{ + LIBBPF_OPTS(bpf_prog_attach_opts, opta); + LIBBPF_OPTS(bpf_prog_detach_opts, optd); + __u32 fd1, fd2, fd3, fd4; + struct cgroup_mprog *skel; + int cg, err; + + cg = test__join_cgroup("/preorder_prog_attach_detach"); + if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_prog_attach_detach")) + return; + + skel = cgroup_mprog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + fd1 = bpf_program__fd(skel->progs.getsockopt_1); + fd2 = bpf_program__fd(skel->progs.getsockopt_2); + fd3 = bpf_program__fd(skel->progs.getsockopt_3); + fd4 = bpf_program__fd(skel->progs.getsockopt_4); + + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI, + .expected_revision = 1, + ); + + /* ordering: [fd1] */ + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup; + + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER, + .expected_revision = 2, + ); + + /* ordering: [fd1, fd2] */ + err = bpf_prog_attach_opts(fd2, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup1; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, + .relative_fd = fd2, + .expected_revision = 3, + ); + + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); + if (!ASSERT_EQ(err, -EINVAL, "prog_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_PREORDER, + .relative_fd = fd2, + .expected_revision = 3, + ); + + /* ordering: [fd1, fd2, fd3] */ + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 3); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI, + .expected_revision = 4, + ); + + /* ordering: [fd2, fd3, fd1, fd4] */ + err = bpf_prog_attach_opts(fd4, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup3; + + assert_mprog_count(cg, atype, 4); + + err = bpf_prog_detach_opts(fd4, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 3); + +cleanup3: + err = bpf_prog_detach_opts(fd3, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 2); + +cleanup2: + err = bpf_prog_detach_opts(fd2, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 1); + +cleanup1: + err = bpf_prog_detach_opts(fd1, cg, atype, &optd); + ASSERT_OK(err, "prog_detach"); + assert_mprog_count(cg, atype, 0); + +cleanup: + cgroup_mprog__destroy(skel); + close(cg); +} + +static void test_preorder_link_attach_detach(int atype) +{ + LIBBPF_OPTS(bpf_cgroup_opts, opta); + struct bpf_link *link1, *link2, *link3, *link4; + struct cgroup_mprog *skel; + __u32 fd2; + int cg; + + cg = test__join_cgroup("/preorder_link_attach_detach"); + if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_link_attach_detach")) + return; + + skel = cgroup_mprog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + fd2 = bpf_program__fd(skel->progs.getsockopt_2); + + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .expected_revision = 1, + ); + + /* ordering: [fd1] */ + link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); + if (!ASSERT_OK_PTR(link1, "link_attach")) + goto cleanup; + + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_PREORDER, + .expected_revision = 2, + ); + + /* ordering: [fd1, fd2] */ + link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); + if (!ASSERT_OK_PTR(link2, "link_attach")) + goto cleanup1; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_AFTER, + .relative_fd = fd2, + .expected_revision = 3, + ); + + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); + if (!ASSERT_ERR_PTR(link3, "link_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 2); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_AFTER | BPF_F_PREORDER | BPF_F_LINK, + .relative_fd = bpf_link__fd(link2), + .expected_revision = 3, + ); + + /* ordering: [fd1, fd2, fd3] */ + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); + if (!ASSERT_OK_PTR(link3, "link_attach")) + goto cleanup2; + + assert_mprog_count(cg, atype, 3); + + LIBBPF_OPTS_RESET(opta, + .expected_revision = 4, + ); + + /* ordering: [fd2, fd3, fd1, fd4] */ + link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); + if (!ASSERT_OK_PTR(link4, "prog_attach")) + goto cleanup3; + + assert_mprog_count(cg, atype, 4); + + bpf_link__destroy(link4); + assert_mprog_count(cg, atype, 3); + +cleanup3: + bpf_link__destroy(link3); + assert_mprog_count(cg, atype, 2); + +cleanup2: + bpf_link__destroy(link2); + assert_mprog_count(cg, atype, 1); + +cleanup1: + bpf_link__destroy(link1); + assert_mprog_count(cg, atype, 0); + +cleanup: + cgroup_mprog__destroy(skel); + close(cg); +} + +static void test_invalid_attach_detach(int atype) +{ + LIBBPF_OPTS(bpf_prog_attach_opts, opta); + __u32 fd1, fd2, id2; + struct cgroup_mprog *skel; + int cg, err; + + cg = test__join_cgroup("/invalid_attach_detach"); + if (!ASSERT_GE(cg, 0, "join_cgroup /invalid_attach_detach")) + return; + + skel = cgroup_mprog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + fd1 = bpf_program__fd(skel->progs.getsockopt_1); + fd2 = bpf_program__fd(skel->progs.getsockopt_2); + + id2 = id_from_prog_fd(fd2); + + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, + .relative_id = id2, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_ID, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -ENOENT, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_ID, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -ENOENT, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, + .relative_id = id2, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_LINK, + .relative_id = id2, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI, + .relative_id = id2, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, + .relative_fd = fd1, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -ENOENT, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, + .relative_fd = fd1, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -ENOENT, "prog_attach"); + assert_mprog_count(cg, atype, 0); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup; + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 1); + + LIBBPF_OPTS_RESET(opta, + .flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE | BPF_F_AFTER, + .replace_prog_fd = fd1, + ); + + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); + ASSERT_EQ(err, -EINVAL, "prog_attach"); + assert_mprog_count(cg, atype, 1); +cleanup: + cgroup_mprog__destroy(skel); + close(cg); +} + +void test_cgroup_mprog_opts(void) +{ + if (test__start_subtest("prog_attach_detach")) + test_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); + if (test__start_subtest("link_attach_detach")) + test_link_attach_detach(BPF_CGROUP_GETSOCKOPT); + if (test__start_subtest("preorder_prog_attach_detach")) + test_preorder_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); + if (test__start_subtest("preorder_link_attach_detach")) + test_preorder_link_attach_detach(BPF_CGROUP_GETSOCKOPT); + if (test__start_subtest("invalid_attach_detach")) + test_invalid_attach_detach(BPF_CGROUP_GETSOCKOPT); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c new file mode 100644 index 000000000000..a36d2e968bc5 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include "cgroup_helpers.h" +#include "cgroup_preorder.skel.h" + +static int run_getsockopt_test(int cg_parent, int sock_fd, bool has_relative_fd) +{ + LIBBPF_OPTS(bpf_prog_attach_opts, opts); + enum bpf_attach_type prog_p_atype, prog_p2_atype; + int prog_p_fd, prog_p2_fd; + struct cgroup_preorder *skel = NULL; + struct bpf_program *prog; + __u8 *result, buf; + socklen_t optlen = 1; + int err = 0; + + skel = cgroup_preorder__open_and_load(); + if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load")) + return 0; + + LIBBPF_OPTS_RESET(opts); + opts.flags = BPF_F_ALLOW_MULTI; + prog = skel->progs.parent; + prog_p_fd = bpf_program__fd(prog); + prog_p_atype = bpf_program__expected_attach_type(prog); + err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts); + if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent")) + goto close_skel; + + opts.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE; + if (has_relative_fd) + opts.relative_fd = prog_p_fd; + prog = skel->progs.parent_2; + prog_p2_fd = bpf_program__fd(prog); + prog_p2_atype = bpf_program__expected_attach_type(prog); + err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts); + if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2")) + goto detach_parent; + + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); + if (!ASSERT_OK(err, "getsockopt")) + goto detach_parent_2; + + result = skel->bss->result; + ASSERT_TRUE(result[0] == 4 && result[1] == 3, "result values"); + +detach_parent_2: + ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype), + "bpf_prog_detach2-parent_2"); +detach_parent: + ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype), + "bpf_prog_detach2-parent"); +close_skel: + cgroup_preorder__destroy(skel); + return err; +} + +void test_cgroup_mprog_ordering(void) +{ + int cg_parent = -1, sock_fd = -1; + + cg_parent = test__join_cgroup("/parent"); + if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) + goto out; + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (!ASSERT_GE(sock_fd, 0, "socket")) + goto out; + + ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, false), "getsockopt_test_1"); + ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, true), "getsockopt_test_2"); + +out: + close(sock_fd); + close(cg_parent); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c new file mode 100644 index 000000000000..e0dd966e4a3e --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cgroup_helpers.h" + +#include "read_cgroupfs_xattr.skel.h" +#include "cgroup_read_xattr.skel.h" + +#define CGROUP_FS_PARENT "foo/" +#define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/" +#define TMP_FILE "/tmp/selftests_cgroup_xattr" + +static const char xattr_value_a[] = "bpf_selftest_value_a"; +static const char xattr_value_b[] = "bpf_selftest_value_b"; +static const char xattr_name[] = "user.bpf_test"; + +static void test_read_cgroup_xattr(void) +{ + int tmp_fd, parent_cgroup_fd = -1, child_cgroup_fd = -1; + struct read_cgroupfs_xattr *skel = NULL; + + parent_cgroup_fd = test__join_cgroup(CGROUP_FS_PARENT); + if (!ASSERT_OK_FD(parent_cgroup_fd, "create parent cgroup")) + return; + if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a), + "set parent xattr")) + goto out; + + child_cgroup_fd = test__join_cgroup(CGROUP_FS_CHILD); + if (!ASSERT_OK_FD(child_cgroup_fd, "create child cgroup")) + goto out; + if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b), + "set child xattr")) + goto out; + + skel = read_cgroupfs_xattr__open_and_load(); + if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load")) + goto out; + + skel->bss->target_pid = gettid(); + + if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach")) + goto out; + + tmp_fd = open(TMP_FILE, O_RDONLY | O_CREAT); + ASSERT_OK_FD(tmp_fd, "open tmp file"); + close(tmp_fd); + + ASSERT_TRUE(skel->bss->found_value_a, "found_value_a"); + ASSERT_TRUE(skel->bss->found_value_b, "found_value_b"); + +out: + close(child_cgroup_fd); + close(parent_cgroup_fd); + read_cgroupfs_xattr__destroy(skel); + unlink(TMP_FILE); +} + +void test_cgroup_xattr(void) +{ + RUN_TESTS(cgroup_read_xattr); + + if (test__start_subtest("read_cgroupfs_xattr")) + test_read_cgroup_xattr(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index 62e7ec775f24..9b2d9ceda210 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -21,6 +21,14 @@ static struct { {"test_dynptr_data", SETUP_SYSCALL_SLEEP}, {"test_dynptr_copy", SETUP_SYSCALL_SLEEP}, {"test_dynptr_copy_xdp", SETUP_XDP_PROG}, + {"test_dynptr_memset_zero", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_notzero", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_zero_offset", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_zero_adjusted", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_overflow", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_overflow_offset", SETUP_SYSCALL_SLEEP}, + {"test_dynptr_memset_readonly", SETUP_SKB_PROG}, + {"test_dynptr_memset_xdp_chunks", SETUP_XDP_PROG}, {"test_ringbuf", SETUP_SYSCALL_SLEEP}, {"test_skb_readonly", SETUP_SKB_PROG}, {"test_dynptr_skb_data", SETUP_SKB_PROG}, @@ -43,6 +51,8 @@ static struct { {"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP}, }; +#define PAGE_SIZE_64K 65536 + static void verify_success(const char *prog_name, enum test_setup_type setup_type) { char user_data[384] = {[0 ... 382] = 'a', '\0'}; @@ -138,14 +148,18 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ } case SETUP_XDP_PROG: { - char data[5000]; + char data[90000]; int err, prog_fd; LIBBPF_OPTS(bpf_test_run_opts, opts, .data_in = &data, - .data_size_in = sizeof(data), .repeat = 1, ); + if (getpagesize() == PAGE_SIZE_64K) + opts.data_size_in = sizeof(data); + else + opts.data_size_in = 5000; + prog_fd = bpf_program__fd(prog); err = bpf_prog_test_run_opts(prog_fd, &opts); diff --git a/tools/testing/selftests/bpf/prog_tests/fd_array.c b/tools/testing/selftests/bpf/prog_tests/fd_array.c index 9add890c2d37..241b2c8c6e0f 100644 --- a/tools/testing/selftests/bpf/prog_tests/fd_array.c +++ b/tools/testing/selftests/bpf/prog_tests/fd_array.c @@ -312,7 +312,7 @@ static void check_fd_array_cnt__referenced_btfs(void) /* btf should still exist when original file descriptor is closed */ err = get_btf_id_by_fd(extra_fds[0], &btf_id); - if (!ASSERT_GE(err, 0, "get_btf_id_by_fd")) + if (!ASSERT_EQ(err, 0, "get_btf_id_by_fd")) goto cleanup; Close(extra_fds[0]); diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c b/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c deleted file mode 100644 index 568d3aa48a78..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include "fexit_noreturns.skel.h" - -void test_fexit_noreturns(void) -{ - RUN_TESTS(fexit_noreturns); -} diff --git a/tools/testing/selftests/bpf/prog_tests/free_timer.c b/tools/testing/selftests/bpf/prog_tests/free_timer.c index b7b77a6b2979..0de8facca4c5 100644 --- a/tools/testing/selftests/bpf/prog_tests/free_timer.c +++ b/tools/testing/selftests/bpf/prog_tests/free_timer.c @@ -124,6 +124,10 @@ void test_free_timer(void) int err; skel = free_timer__open_and_load(); + if (!skel && errno == EOPNOTSUPP) { + test__skip(); + return; + } if (!ASSERT_OK_PTR(skel, "open_load")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 5266c7022863..14c5a7ef0e87 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -72,7 +72,7 @@ static struct { { "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" }, { "obj_new_acq", "Unreleased reference id=" }, { "use_after_drop", "invalid mem access 'scalar'" }, - { "ptr_walk_scalar", "type=scalar expected=percpu_ptr_" }, + { "ptr_walk_scalar", "type=rdonly_untrusted_mem expected=percpu_ptr_" }, { "direct_read_lock", "direct access to bpf_spin_lock is disallowed" }, { "direct_write_lock", "direct access to bpf_spin_lock is disallowed" }, { "direct_read_head", "direct access to bpf_list_head is disallowed" }, diff --git a/tools/testing/selftests/bpf/prog_tests/log_buf.c b/tools/testing/selftests/bpf/prog_tests/log_buf.c index 169ce689b97c..d6f14a232002 100644 --- a/tools/testing/selftests/bpf/prog_tests/log_buf.c +++ b/tools/testing/selftests/bpf/prog_tests/log_buf.c @@ -7,6 +7,10 @@ #include "test_log_buf.skel.h" #include "bpf_util.h" +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + static size_t libbpf_log_pos; static char libbpf_log_buf[1024 * 1024]; static bool libbpf_log_error; diff --git a/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c new file mode 100644 index 000000000000..40d4f687bd9c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "mem_rdonly_untrusted.skel.h" + +void test_mem_rdonly_untrusted(void) +{ + RUN_TESTS(mem_rdonly_untrusted); +} diff --git a/tools/testing/selftests/bpf/prog_tests/recursive_attach.c b/tools/testing/selftests/bpf/prog_tests/recursive_attach.c index 8100509e561b..0ffa01d54ce2 100644 --- a/tools/testing/selftests/bpf/prog_tests/recursive_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/recursive_attach.c @@ -149,3 +149,70 @@ void test_fentry_attach_btf_presence(void) fentry_recursive_target__destroy(target_skel); fentry_recursive__destroy(tracing_skel); } + +static void *fentry_target_test_run(void *arg) +{ + for (;;) { + int prog_fd = __atomic_load_n((int *)arg, __ATOMIC_SEQ_CST); + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err; + + if (prog_fd == -1) + break; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "fentry_target test_run")) + break; + } + + return NULL; +} + +void test_fentry_attach_stress(void) +{ + struct fentry_recursive_target *target_skel = NULL; + struct fentry_recursive *tracing_skel = NULL; + struct bpf_program *prog; + int err, i, tgt_prog_fd; + pthread_t thread; + + target_skel = fentry_recursive_target__open_and_load(); + if (!ASSERT_OK_PTR(target_skel, + "fentry_recursive_target__open_and_load")) + goto close_prog; + tgt_prog_fd = bpf_program__fd(target_skel->progs.fentry_target); + err = pthread_create(&thread, NULL, + fentry_target_test_run, &tgt_prog_fd); + if (!ASSERT_OK(err, "bpf_program__set_attach_target")) + goto close_prog; + + for (i = 0; i < 1000; i++) { + tracing_skel = fentry_recursive__open(); + if (!ASSERT_OK_PTR(tracing_skel, "fentry_recursive__open")) + goto stop_thread; + + prog = tracing_skel->progs.recursive_attach; + err = bpf_program__set_attach_target(prog, tgt_prog_fd, + "fentry_target"); + if (!ASSERT_OK(err, "bpf_program__set_attach_target")) + goto stop_thread; + + err = fentry_recursive__load(tracing_skel); + if (!ASSERT_OK(err, "fentry_recursive__load")) + goto stop_thread; + + err = fentry_recursive__attach(tracing_skel); + if (!ASSERT_OK(err, "fentry_recursive__attach")) + goto stop_thread; + + fentry_recursive__destroy(tracing_skel); + tracing_skel = NULL; + } + +stop_thread: + __atomic_store_n(&tgt_prog_fd, -1, __ATOMIC_SEQ_CST); + err = pthread_join(thread, NULL); + ASSERT_OK(err, "pthread_join"); +close_prog: + fentry_recursive__destroy(tracing_skel); + fentry_recursive_target__destroy(target_skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c index 39d42271cc46..e261b0e872db 100644 --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c @@ -465,6 +465,20 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t, return range_improve(x_t, x, x_swap); } + if (!t_is_32(x_t) && !t_is_32(y_t) && x_t != y_t) { + if (x_t == S64 && x.a > x.b) { + if (x.b < y.a && x.a <= y.b) + return range(x_t, x.a, y.b); + if (x.a > y.b && x.b >= y.a) + return range(x_t, y.a, x.b); + } else if (x_t == U64 && y.a > y.b) { + if (y.b < x.a && y.a <= x.b) + return range(x_t, y.a, x.b); + if (y.a > x.b && y.b >= x.a) + return range(x_t, x.a, y.b); + } + } + /* otherwise, plain range cast and intersection works */ return range_improve(x_t, x, y_cast); } diff --git a/tools/testing/selftests/bpf/prog_tests/ringbuf.c b/tools/testing/selftests/bpf/prog_tests/ringbuf.c index da430df45aa4..d1e4cb28a72c 100644 --- a/tools/testing/selftests/bpf/prog_tests/ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/ringbuf.c @@ -97,7 +97,7 @@ static void ringbuf_write_subtest(void) if (!ASSERT_OK_PTR(skel, "skel_open")) return; - skel->maps.ringbuf.max_entries = 0x4000; + skel->maps.ringbuf.max_entries = 0x40000; err = test_ringbuf_write_lskel__load(skel); if (!ASSERT_OK(err, "skel_load")) @@ -108,7 +108,7 @@ static void ringbuf_write_subtest(void) mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0); if (!ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos")) goto cleanup; - *mmap_ptr = 0x3000; + *mmap_ptr = 0x30000; ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw"); skel->bss->pid = getpid(); diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf.c b/tools/testing/selftests/bpf/prog_tests/snprintf.c index 4be6fdb78c6a..594441acb707 100644 --- a/tools/testing/selftests/bpf/prog_tests/snprintf.c +++ b/tools/testing/selftests/bpf/prog_tests/snprintf.c @@ -116,6 +116,8 @@ static void test_snprintf_negative(void) ASSERT_ERR(load_single_snprintf("%llc"), "invalid specifier 7"); ASSERT_ERR(load_single_snprintf("\x80"), "non ascii character"); 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"); } void test_snprintf(void) diff --git a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c index a4517bee34d5..27781df8f2fb 100644 --- a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2024 Meta +#include #include #include "network_helpers.h" #include "sock_iter_batch.skel.h" #define TEST_NS "sock_iter_batch_netns" +#define TEST_CHILD_NS "sock_iter_batch_child_netns" static const int init_batch_size = 16; static const int nr_soreuse = 4; @@ -118,6 +120,45 @@ static int get_nth_socket(int *fds, int fds_len, struct bpf_link *link, int n) return nth_sock_idx; } +static void destroy(int fd) +{ + struct sock_iter_batch *skel = NULL; + __u64 cookie = socket_cookie(fd); + struct bpf_link *link = NULL; + int iter_fd = -1; + int nread; + __u64 out; + + skel = sock_iter_batch__open(); + if (!ASSERT_OK_PTR(skel, "sock_iter_batch__open")) + goto done; + + skel->rodata->destroy_cookie = cookie; + + if (!ASSERT_OK(sock_iter_batch__load(skel), "sock_iter_batch__load")) + goto done; + + link = bpf_program__attach_iter(skel->progs.iter_tcp_destroy, NULL); + if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter")) + goto done; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create")) + goto done; + + /* Delete matching socket. */ + nread = read(iter_fd, &out, sizeof(out)); + ASSERT_GE(nread, 0, "nread"); + if (nread) + ASSERT_EQ(out, cookie, "cookie matches"); +done: + if (iter_fd >= 0) + close(iter_fd); + bpf_link__destroy(link); + sock_iter_batch__destroy(skel); + close(fd); +} + static int get_seen_count(int fd, struct sock_count counts[], int n) { __u64 cookie = socket_cookie(fd); @@ -152,8 +193,71 @@ static void check_n_were_seen_once(int *fds, int fds_len, int n, ASSERT_EQ(seen_once, n, "seen_once"); } +static int accept_from_one(struct pollfd *server_poll_fds, + int server_poll_fds_len) +{ + static const int poll_timeout_ms = 5000; /* 5s */ + int ret; + int i; + + ret = poll(server_poll_fds, server_poll_fds_len, poll_timeout_ms); + if (!ASSERT_EQ(ret, 1, "poll")) + return -1; + + for (i = 0; i < server_poll_fds_len; i++) + if (server_poll_fds[i].revents & POLLIN) + return accept(server_poll_fds[i].fd, NULL, NULL); + + return -1; +} + +static int *connect_to_server(int family, int sock_type, const char *addr, + __u16 port, int nr_connects, int *server_fds, + int server_fds_len) +{ + struct pollfd *server_poll_fds = NULL; + int *established_socks = NULL; + int i; + + server_poll_fds = calloc(server_fds_len, sizeof(*server_poll_fds)); + if (!ASSERT_OK_PTR(server_poll_fds, "server_poll_fds")) + return NULL; + + for (i = 0; i < server_fds_len; i++) { + server_poll_fds[i].fd = server_fds[i]; + server_poll_fds[i].events = POLLIN; + } + + i = 0; + + established_socks = malloc(sizeof(*established_socks) * nr_connects*2); + if (!ASSERT_OK_PTR(established_socks, "established_socks")) + goto error; + + while (nr_connects--) { + established_socks[i] = connect_to_addr_str(family, sock_type, + addr, port, NULL); + if (!ASSERT_OK_FD(established_socks[i], "connect_to_addr_str")) + goto error; + i++; + established_socks[i] = accept_from_one(server_poll_fds, + server_fds_len); + if (!ASSERT_OK_FD(established_socks[i], "accept_from_one")) + goto error; + i++; + } + + free(server_poll_fds); + return established_socks; +error: + free_fds(established_socks, i); + free(server_poll_fds); + return NULL; +} + static void remove_seen(int family, int sock_type, const char *addr, __u16 port, - int *socks, int socks_len, struct sock_count *counts, + int *socks, int socks_len, int *established_socks, + int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd) { int close_idx; @@ -182,8 +286,46 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port, counts_len); } +static void remove_seen_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int close_idx; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Leave one established socket. */ + read_n(iter_fd, established_socks_len - 1, counts, counts_len); + + /* Close a socket we've already seen to remove it from the bucket. */ + close_idx = get_nth_socket(established_socks, established_socks_len, + link, listen_socks_len + 1); + if (!ASSERT_GE(close_idx, 0, "close_idx")) + return; + destroy(established_socks[close_idx]); + established_socks[close_idx] = -1; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure the last socket wasn't skipped and that there were no + * repeats. + */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); +} + static void remove_unseen(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, + int *established_socks, int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd) { @@ -214,8 +356,54 @@ static void remove_unseen(int family, int sock_type, const char *addr, counts_len); } +static void remove_unseen_established(int family, int sock_type, + const char *addr, __u16 port, + int *listen_socks, int listen_socks_len, + int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int close_idx; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established socket. */ + read_n(iter_fd, 1, counts, counts_len); + + /* Make sure we saw one established socks. */ + check_n_were_seen_once(established_socks, established_socks_len, 1, + counts, counts_len); + + /* Close what would be the next socket in the bucket to exercise the + * condition where we need to skip past the first cookie we remembered. + */ + close_idx = get_nth_socket(established_socks, established_socks_len, + link, listen_socks_len + 1); + if (!ASSERT_GE(close_idx, 0, "close_idx")) + return; + + destroy(established_socks[close_idx]); + established_socks[close_idx] = -1; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure the remaining sockets were seen exactly once and that we + * didn't repeat the socket that was already seen. + */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); +} + static void remove_all(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, + int *established_socks, int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd) { @@ -242,8 +430,57 @@ static void remove_all(int family, int sock_type, const char *addr, ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n"); } +static void remove_all_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int *close_idx = NULL; + int i; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established socket. */ + read_n(iter_fd, 1, counts, counts_len); + + /* Make sure we saw one established socks. */ + check_n_were_seen_once(established_socks, established_socks_len, 1, + counts, counts_len); + + /* Close all remaining sockets to exhaust the list of saved cookies and + * exit without putting any sockets into the batch on the next read. + */ + close_idx = malloc(sizeof(int) * (established_socks_len - 1)); + if (!ASSERT_OK_PTR(close_idx, "close_idx malloc")) + return; + for (i = 0; i < established_socks_len - 1; i++) { + close_idx[i] = get_nth_socket(established_socks, + established_socks_len, link, + listen_socks_len + i); + if (!ASSERT_GE(close_idx[i], 0, "close_idx")) + return; + } + + for (i = 0; i < established_socks_len - 1; i++) { + destroy(established_socks[close_idx[i]]); + established_socks[close_idx[i]] = -1; + } + + /* Make sure there are no more sockets returned */ + ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n"); + free(close_idx); +} + static void add_some(int family, int sock_type, const char *addr, __u16 port, - int *socks, int socks_len, struct sock_count *counts, + int *socks, int socks_len, int *established_socks, + int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd) { int *new_socks = NULL; @@ -271,8 +508,52 @@ static void add_some(int family, int sock_type, const char *addr, __u16 port, free_fds(new_socks, socks_len); } +static void add_some_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, + int counts_len, struct bpf_link *link, + int iter_fd) +{ + int *new_socks = NULL; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established_socks_len - 1 sockets. */ + read_n(iter_fd, established_socks_len - 1, counts, counts_len); + + /* Make sure we saw established_socks_len - 1 sockets exactly once. */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); + + /* Double the number of established sockets in the bucket. */ + new_socks = connect_to_server(family, sock_type, addr, port, + established_socks_len / 2, listen_socks, + listen_socks_len); + if (!ASSERT_OK_PTR(new_socks, "connect_to_server")) + goto done; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure each of the original sockets was seen exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len, counts, counts_len); +done: + free_fds(new_socks, established_socks_len); +} + static void force_realloc(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, + int *established_socks, int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd) { @@ -299,11 +580,32 @@ static void force_realloc(int family, int sock_type, const char *addr, free_fds(new_socks, socks_len); } +static void force_realloc_established(int family, int sock_type, + const char *addr, __u16 port, + int *listen_socks, int listen_socks_len, + int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + /* Iterate through all sockets to trigger a realloc. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure each socket was seen exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len, counts, counts_len); +} + struct test_case { void (*test)(int family, int sock_type, const char *addr, __u16 port, - int *socks, int socks_len, struct sock_count *counts, + int *socks, int socks_len, int *established_socks, + int established_socks_len, struct sock_count *counts, int counts_len, struct bpf_link *link, int iter_fd); const char *description; + int ehash_buckets; + int connections; int init_socks; int max_socks; int sock_type; @@ -358,18 +660,140 @@ static struct test_case resume_tests[] = { .family = AF_INET6, .test = force_realloc, }, + { + .description = "tcp: resume after removing a seen socket (listening)", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_seen, + }, + { + .description = "tcp: resume after removing one unseen socket (listening)", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_unseen, + }, + { + .description = "tcp: resume after removing all unseen sockets (listening)", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_all, + }, + { + .description = "tcp: resume after adding a few sockets (listening)", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_STREAM, + /* Use AF_INET so that new sockets are added to the head of the + * bucket's list. + */ + .family = AF_INET, + .test = add_some, + }, + { + .description = "tcp: force a realloc to occur (listening)", + .init_socks = init_batch_size, + .max_socks = init_batch_size * 2, + .sock_type = SOCK_STREAM, + /* Use AF_INET6 so that new sockets are added to the tail of the + * bucket's list, needing to be added to the next batch to force + * a realloc. + */ + .family = AF_INET6, + .test = force_realloc, + }, + { + .description = "tcp: resume after removing a seen socket (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_seen_established, + }, + { + .description = "tcp: resume after removing one unseen socket (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_unseen_established, + }, + { + .description = "tcp: resume after removing all unseen sockets (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_all_established, + }, + { + .description = "tcp: resume after adding a few sockets (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = add_some_established, + }, + { + .description = "tcp: force a realloc to occur (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + /* Bucket size will need to double when going from listening to + * established sockets. + */ + .connections = init_batch_size, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse + (init_batch_size * 2), + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = force_realloc_established, + }, }; static void do_resume_test(struct test_case *tc) { struct sock_iter_batch *skel = NULL; + struct sock_count *counts = NULL; static const __u16 port = 10001; + struct nstoken *nstoken = NULL; struct bpf_link *link = NULL; - struct sock_count *counts; + int *established_fds = NULL; int err, iter_fd = -1; const char *addr; int *fds = NULL; - int local_port; + + if (tc->ehash_buckets) { + SYS_NOFAIL("ip netns del " TEST_CHILD_NS); + SYS(done, "sysctl -wq net.ipv4.tcp_child_ehash_entries=%d", + tc->ehash_buckets); + SYS(done, "ip netns add %s", TEST_CHILD_NS); + SYS(done, "ip -net %s link set dev lo up", TEST_CHILD_NS); + nstoken = open_netns(TEST_CHILD_NS); + if (!ASSERT_OK_PTR(nstoken, "open_child_netns")) + goto done; + } counts = calloc(tc->max_socks, sizeof(*counts)); if (!ASSERT_OK_PTR(counts, "counts")) @@ -384,11 +808,18 @@ static void do_resume_test(struct test_case *tc) tc->init_socks); if (!ASSERT_OK_PTR(fds, "start_reuseport_server")) goto done; - local_port = get_socket_local_port(*fds); - if (!ASSERT_GE(local_port, 0, "get_socket_local_port")) - goto done; - skel->rodata->ports[0] = ntohs(local_port); + if (tc->connections) { + established_fds = connect_to_server(tc->family, tc->sock_type, + addr, port, + tc->connections, fds, + tc->init_socks); + if (!ASSERT_OK_PTR(established_fds, "connect_to_server")) + goto done; + } + skel->rodata->ports[0] = 0; + skel->rodata->ports[1] = 0; skel->rodata->sf = tc->family; + skel->rodata->ss = 0; err = sock_iter_batch__load(skel); if (!ASSERT_OK(err, "sock_iter_batch__load")) @@ -406,10 +837,15 @@ static void do_resume_test(struct test_case *tc) goto done; tc->test(tc->family, tc->sock_type, addr, port, fds, tc->init_socks, - counts, tc->max_socks, link, iter_fd); + established_fds, tc->connections*2, counts, tc->max_socks, + link, iter_fd); done: + close_netns(nstoken); + SYS_NOFAIL("ip netns del " TEST_CHILD_NS); + SYS_NOFAIL("sysctl -w net.ipv4.tcp_child_ehash_entries=0"); free(counts); free_fds(fds, tc->init_socks); + free_fds(established_fds, tc->connections*2); if (iter_fd >= 0) close(iter_fd); bpf_link__destroy(link); @@ -454,6 +890,8 @@ static void do_test(int sock_type, bool onebyone) skel->rodata->ports[i] = ntohs(local_port); } skel->rodata->sf = AF_INET6; + if (sock_type == SOCK_STREAM) + skel->rodata->ss = TCP_LISTEN; err = sock_iter_batch__load(skel); if (!ASSERT_OK(err, "sock_iter_batch__load")) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c index b6c471da5c28..b87e7f39e15a 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c @@ -314,6 +314,95 @@ static void test_sockmap_ktls_tx_no_buf(int family, int sotype, bool push) test_sockmap_ktls__destroy(skel); } +static void test_sockmap_ktls_tx_pop(int family, int sotype) +{ + char msg[37] = "0123456789abcdefghijklmnopqrstuvwxyz\0"; + int c = 0, p = 0, one = 1, sent, recvd; + struct test_sockmap_ktls *skel; + int prog_fd, map_fd; + char rcv[50] = {0}; + int err; + int i, m, r; + + skel = test_sockmap_ktls__open_and_load(); + if (!ASSERT_TRUE(skel, "open ktls skel")) + return; + + err = create_pair(family, sotype, &c, &p); + if (!ASSERT_OK(err, "create_pair()")) + goto out; + + prog_fd = bpf_program__fd(skel->progs.prog_sk_policy); + map_fd = bpf_map__fd(skel->maps.sock_map); + + err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); + if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) + goto out; + + err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); + if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) + goto out; + + err = init_ktls_pairs(c, p); + if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) + goto out; + + struct { + int pop_start; + int pop_len; + } pop_policy[] = { + /* trim the start */ + {0, 2}, + {0, 10}, + {1, 2}, + {1, 10}, + /* trim the end */ + {35, 2}, + /* New entries should be added before this line */ + {-1, -1}, + }; + + i = 0; + while (pop_policy[i].pop_start >= 0) { + skel->bss->pop_start = pop_policy[i].pop_start; + skel->bss->pop_end = pop_policy[i].pop_len; + + sent = send(c, msg, sizeof(msg), 0); + if (!ASSERT_EQ(sent, sizeof(msg), "send(msg)")) + goto out; + + recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); + if (!ASSERT_EQ(recvd, sizeof(msg) - pop_policy[i].pop_len, "pop len mismatch")) + goto out; + + /* verify the data + * msg: 0123456789a bcdefghij klmnopqrstuvwxyz + * | | + * popped data + */ + for (m = 0, r = 0; m < sizeof(msg);) { + /* skip checking the data that has been popped */ + if (m >= pop_policy[i].pop_start && + m <= pop_policy[i].pop_start + pop_policy[i].pop_len - 1) { + m++; + continue; + } + + if (!ASSERT_EQ(msg[m], rcv[r], "data mismatch")) + goto out; + m++; + r++; + } + i++; + } +out: + if (c) + close(c); + if (p) + close(p); + test_sockmap_ktls__destroy(skel); +} + static void run_tests(int family, enum bpf_map_type map_type) { int map; @@ -338,6 +427,8 @@ static void run_ktls_test(int family, int sotype) test_sockmap_ktls_tx_cork(family, sotype, true); if (test__start_subtest("tls tx egress with no buf")) test_sockmap_ktls_tx_no_buf(family, sotype, true); + if (test__start_subtest("tls tx with pop")) + test_sockmap_ktls_tx_pop(family, sotype); } void test_sockmap_ktls(void) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index 1d98eee7a2c3..f1bdccc7e4e7 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -924,6 +924,8 @@ static void redir_partial(int family, int sotype, int sock_map, int parser_map) goto close; n = xsend(c1, buf, sizeof(buf), 0); + if (n == -1) + goto close; if (n < sizeof(buf)) FAIL("incomplete write"); diff --git a/tools/testing/selftests/bpf/prog_tests/stream.c b/tools/testing/selftests/bpf/prog_tests/stream.c new file mode 100644 index 000000000000..d9f0185dca61 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/stream.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include + +#include "stream.skel.h" +#include "stream_fail.skel.h" + +void test_stream_failure(void) +{ + RUN_TESTS(stream_fail); +} + +void test_stream_success(void) +{ + RUN_TESTS(stream); + return; +} + +struct { + int prog_off; + const char *errstr; +} stream_error_arr[] = { + { + offsetof(struct stream, progs.stream_cond_break), + "ERROR: Timeout detected for may_goto instruction\n" + "CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" + "Call trace:\n" + "([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" + "|[ \t]+[^\n]+\n)*", + }, + { + offsetof(struct stream, progs.stream_deadlock), + "ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock\n" + "Attempted lock = (0x[0-9a-fA-F]+)\n" + "Total held locks = 1\n" + "Held lock\\[ 0\\] = \\1\n" // Lock address must match + "CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" + "Call trace:\n" + "([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" + "|[ \t]+[^\n]+\n)*", + }, +}; + +static int match_regex(const char *pattern, const char *string) +{ + int err, rc; + regex_t re; + + err = regcomp(&re, pattern, REG_EXTENDED | REG_NEWLINE); + if (err) + return -1; + rc = regexec(&re, string, 0, NULL, 0); + regfree(&re); + return rc == 0 ? 1 : 0; +} + +void test_stream_errors(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts); + struct stream *skel; + int ret, prog_fd; + char buf[1024]; + + skel = stream__open_and_load(); + if (!ASSERT_OK_PTR(skel, "stream__open_and_load")) + return; + + for (int i = 0; i < ARRAY_SIZE(stream_error_arr); i++) { + struct bpf_program **prog; + + prog = (struct bpf_program **)(((char *)skel) + stream_error_arr[i].prog_off); + prog_fd = bpf_program__fd(*prog); + ret = bpf_prog_test_run_opts(prog_fd, &opts); + ASSERT_OK(ret, "ret"); + ASSERT_OK(opts.retval, "retval"); + +#if !defined(__x86_64__) + ASSERT_TRUE(1, "Timed may_goto unsupported, skip."); + if (i == 0) { + ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts); + ASSERT_EQ(ret, 0, "stream read"); + continue; + } +#endif + + ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts); + ASSERT_GT(ret, 0, "stream read"); + ASSERT_LE(ret, 1023, "len for buf"); + buf[ret] = '\0'; + + ret = match_regex(stream_error_arr[i].errstr, buf); + if (!ASSERT_TRUE(ret == 1, "regex match")) + fprintf(stderr, "Output from stream:\n%s\n", buf); + } + + stream__destroy(skel); +} + +void test_stream_syscall(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts); + struct stream *skel; + int ret, prog_fd; + char buf[64]; + + skel = stream__open_and_load(); + if (!ASSERT_OK_PTR(skel, "stream__open_and_load")) + return; + + prog_fd = bpf_program__fd(skel->progs.stream_syscall); + ret = bpf_prog_test_run_opts(prog_fd, &opts); + ASSERT_OK(ret, "ret"); + ASSERT_OK(opts.retval, "retval"); + + ASSERT_LT(bpf_prog_stream_read(0, BPF_STREAM_STDOUT, buf, sizeof(buf), &ropts), 0, "error"); + ret = -errno; + ASSERT_EQ(ret, -EINVAL, "bad prog_fd"); + + ASSERT_LT(bpf_prog_stream_read(prog_fd, 0, buf, sizeof(buf), &ropts), 0, "error"); + ret = -errno; + ASSERT_EQ(ret, -ENOENT, "bad stream id"); + + ASSERT_LT(bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, NULL, sizeof(buf), NULL), 0, "error"); + ret = -errno; + ASSERT_EQ(ret, -EFAULT, "bad stream buf"); + + ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL); + ASSERT_EQ(ret, 2, "bytes"); + ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL); + ASSERT_EQ(ret, 1, "bytes"); + ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 1, &ropts); + ASSERT_EQ(ret, 0, "no bytes stdout"); + ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, 1, &ropts); + ASSERT_EQ(ret, 0, "no bytes stderr"); + + stream__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c new file mode 100644 index 000000000000..35af8044d059 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Red Hat, Inc.*/ +#include +#include "string_kfuncs_success.skel.h" +#include "string_kfuncs_failure1.skel.h" +#include "string_kfuncs_failure2.skel.h" +#include + +static const char * const test_cases[] = { + "strcmp", + "strchr", + "strchrnul", + "strnchr", + "strrchr", + "strlen", + "strnlen", + "strspn_str", + "strspn_accept", + "strcspn_str", + "strcspn_reject", + "strstr", + "strnstr", +}; + +void run_too_long_tests(void) +{ + struct string_kfuncs_failure2 *skel; + struct bpf_program *prog; + char test_name[256]; + int err, i; + + skel = string_kfuncs_failure2__open_and_load(); + if (!ASSERT_OK_PTR(skel, "string_kfuncs_failure2__open_and_load")) + return; + + memset(skel->bss->long_str, 'a', sizeof(skel->bss->long_str)); + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + sprintf(test_name, "test_%s_too_long", test_cases[i]); + if (!test__start_subtest(test_name)) + continue; + + prog = bpf_object__find_program_by_name(skel->obj, test_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + LIBBPF_OPTS(bpf_test_run_opts, topts); + err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts); + if (!ASSERT_OK(err, "bpf_prog_test_run")) + goto cleanup; + + ASSERT_EQ(topts.retval, -E2BIG, "reading too long string fails with -E2BIG"); + } + +cleanup: + string_kfuncs_failure2__destroy(skel); +} + +void test_string_kfuncs(void) +{ + RUN_TESTS(string_kfuncs_success); + RUN_TESTS(string_kfuncs_failure1); + + run_too_long_tests(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c index 66a900327f91..0ab36503c3b2 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -1195,7 +1195,7 @@ static void test_tailcall_hierarchy_count(const char *which, bool test_fentry, bool test_fexit, bool test_fentry_entry) { - int err, map_fd, prog_fd, main_data_fd, fentry_data_fd, fexit_data_fd, i, val; + int err, map_fd, prog_fd, main_data_fd, fentry_data_fd = 0, fexit_data_fd = 0, i, val; struct bpf_object *obj = NULL, *fentry_obj = NULL, *fexit_obj = NULL; struct bpf_link *fentry_link = NULL, *fexit_link = NULL; struct bpf_program *prog, *fentry_prog; diff --git a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h b/tools/testing/selftests/bpf/prog_tests/tc_helpers.h index 924d0e25320c..d52a62af77bf 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h +++ b/tools/testing/selftests/bpf/prog_tests/tc_helpers.h @@ -8,34 +8,6 @@ # define loopback 1 #endif -static inline __u32 id_from_prog_fd(int fd) -{ - struct bpf_prog_info prog_info = {}; - __u32 prog_info_len = sizeof(prog_info); - int err; - - err = bpf_obj_get_info_by_fd(fd, &prog_info, &prog_info_len); - if (!ASSERT_OK(err, "id_from_prog_fd")) - return 0; - - ASSERT_NEQ(prog_info.id, 0, "prog_info.id"); - return prog_info.id; -} - -static inline __u32 id_from_link_fd(int fd) -{ - struct bpf_link_info link_info = {}; - __u32 link_info_len = sizeof(link_info); - int err; - - err = bpf_link_get_info_by_fd(fd, &link_info, &link_info_len); - if (!ASSERT_OK(err, "id_from_link_fd")) - return 0; - - ASSERT_NEQ(link_info.id, 0, "link_info.id"); - return link_info.id; -} - static inline __u32 ifindex_from_link_fd(int fd) { struct bpf_link_info link_info = {}; diff --git a/tools/testing/selftests/bpf/test_sysctl.c b/tools/testing/selftests/bpf/prog_tests/test_sysctl.c similarity index 98% rename from tools/testing/selftests/bpf/test_sysctl.c rename to tools/testing/selftests/bpf/prog_tests/test_sysctl.c index bcdbd27f22f0..273dd41ca09e 100644 --- a/tools/testing/selftests/bpf/test_sysctl.c +++ b/tools/testing/selftests/bpf/prog_tests/test_sysctl.c @@ -1,22 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Facebook -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include "bpf_util.h" +#include "test_progs.h" #include "cgroup_helpers.h" -#include "testing_helpers.h" #define CG_PATH "/foo" #define MAX_INSNS 512 @@ -1608,26 +1594,19 @@ static int run_tests(int cgfd) return fails ? -1 : 0; } -int main(int argc, char **argv) +void test_sysctl(void) { - int cgfd = -1; - int err = 0; + int cgfd; cgfd = cgroup_setup_and_join(CG_PATH); - if (cgfd < 0) - goto err; + if (!ASSERT_OK_FD(cgfd < 0, "create_cgroup")) + goto out; - /* Use libbpf 1.0 API mode */ - libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + if (!ASSERT_OK(run_tests(cgfd), "run_tests")) + goto out; - if (run_tests(cgfd)) - goto err; - - goto out; -err: - err = -1; out: close(cgfd); cleanup_cgroup_environment(); - return err; + return; } diff --git a/tools/testing/selftests/bpf/prog_tests/test_veristat.c b/tools/testing/selftests/bpf/prog_tests/test_veristat.c index 47b56c258f3f..367f47e4a936 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_veristat.c +++ b/tools/testing/selftests/bpf/prog_tests/test_veristat.c @@ -60,13 +60,19 @@ static void test_set_global_vars_succeeds(void) " -G \"var_s8 = -128\" "\ " -G \"var_u8 = 255\" "\ " -G \"var_ea = EA2\" "\ - " -G \"var_eb = EB2\" "\ - " -G \"var_ec = EC2\" "\ + " -G \"var_eb = EB2\" "\ + " -G \"var_ec=EC2\" "\ " -G \"var_b = 1\" "\ - " -G \"struct1.struct2.u.var_u8 = 170\" "\ + " -G \"struct1[2].struct2[1][2].u.var_u8[2]=170\" "\ " -G \"union1.struct3.var_u8_l = 0xaa\" "\ " -G \"union1.struct3.var_u8_h = 0xaa\" "\ - "-vl2 > %s", fix->veristat, fix->tmpfile); + " -G \"arr[3]= 171\" " \ + " -G \"arr[EA2] =172\" " \ + " -G \"enum_arr[EC2]=EA3\" " \ + " -G \"three_d[31][7][EA2]=173\"" \ + " -G \"struct1[2].struct2[1][2].u.mat[5][3]=174\" " \ + " -G \"struct11 [ 7 ] [ 5 ] .struct2[0][1].u.mat[3][0] = 175\" " \ + " -vl2 > %s", fix->veristat, fix->tmpfile); read(fix->fd, fix->output, fix->sz); __CHECK_STR("_w=0xf000000000000001 ", "var_s64 = 0xf000000000000001"); @@ -81,8 +87,14 @@ static void test_set_global_vars_succeeds(void) __CHECK_STR("_w=12 ", "var_eb = EB2"); __CHECK_STR("_w=13 ", "var_ec = EC2"); __CHECK_STR("_w=1 ", "var_b = 1"); - __CHECK_STR("_w=170 ", "struct1.struct2.u.var_u8 = 170"); + __CHECK_STR("_w=170 ", "struct1[2].struct2[1][2].u.var_u8[2]=170"); __CHECK_STR("_w=0xaaaa ", "union1.var_u16 = 0xaaaa"); + __CHECK_STR("_w=171 ", "arr[3]= 171"); + __CHECK_STR("_w=172 ", "arr[EA2] =172"); + __CHECK_STR("_w=10 ", "enum_arr[EC2]=EA3"); + __CHECK_STR("_w=173 ", "matrix[31][7][11]=173"); + __CHECK_STR("_w=174 ", "struct1[2].struct2[1][2].u.mat[5][3]=174"); + __CHECK_STR("_w=175 ", "struct11[7][5].struct2[0][1].u.mat[3][0]=175"); out: teardown_fixture(fix); @@ -129,6 +141,95 @@ static void test_set_global_vars_out_of_range(void) teardown_fixture(fix); } +static void test_unsupported_ptr_array_type(void) +{ + struct fixture *fix = init_fixture(); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"ptr_arr[0] = 0\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + read(fix->fd, fix->output, fix->sz); + __CHECK_STR("Can't set ptr_arr[0]. Only ints and enums are supported", "ptr_arr"); + +out: + teardown_fixture(fix); +} + +static void test_array_out_of_bounds(void) +{ + struct fixture *fix = init_fixture(); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"arr[99] = 0\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + read(fix->fd, fix->output, fix->sz); + __CHECK_STR("Array index 99 is out of bounds", "arr[99]"); + +out: + teardown_fixture(fix); +} + +static void test_array_index_not_found(void) +{ + struct fixture *fix = init_fixture(); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"arr[EG2] = 0\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + read(fix->fd, fix->output, fix->sz); + __CHECK_STR("Can't resolve enum value EG2", "arr[EG2]"); + +out: + teardown_fixture(fix); +} + +static void test_array_index_for_non_array(void) +{ + struct fixture *fix = init_fixture(); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"var_b[0] = 1\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + pread(fix->fd, fix->output, fix->sz, 0); + __CHECK_STR("Array index is not expected for var_b", "var_b[0] = 1"); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"union1.struct3[0].var_u8_l=1\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + pread(fix->fd, fix->output, fix->sz, 0); + __CHECK_STR("Array index is not expected for struct3", "union1.struct3[0].var_u8_l=1"); + +out: + teardown_fixture(fix); +} + +static void test_no_array_index_for_array(void) +{ + struct fixture *fix = init_fixture(); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"arr = 1\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + pread(fix->fd, fix->output, fix->sz, 0); + __CHECK_STR("Can't set arr. Only ints and enums are supported", "arr = 1"); + + SYS_FAIL(out, + "%s set_global_vars.bpf.o -G \"struct1[0].struct2.u.var_u8[2]=1\" -vl2 2> %s", + fix->veristat, fix->tmpfile); + + pread(fix->fd, fix->output, fix->sz, 0); + __CHECK_STR("Can't resolve field u for non-composite type", "struct1[0].struct2.u.var_u8[2]=1"); + +out: + teardown_fixture(fix); +} + void test_veristat(void) { if (test__start_subtest("set_global_vars_succeeds")) @@ -139,6 +240,22 @@ void test_veristat(void) if (test__start_subtest("set_global_vars_from_file_succeeds")) test_set_global_vars_from_file_succeeds(); + + if (test__start_subtest("test_unsupported_ptr_array_type")) + test_unsupported_ptr_array_type(); + + if (test__start_subtest("test_array_out_of_bounds")) + test_array_out_of_bounds(); + + if (test__start_subtest("test_array_index_not_found")) + test_array_index_not_found(); + + if (test__start_subtest("test_array_index_for_non_array")) + test_array_index_for_non_array(); + + if (test__start_subtest("test_no_array_index_for_array")) + test_no_array_index_for_array(); + } #undef __CHECK_STR diff --git a/tools/testing/selftests/bpf/prog_tests/timer.c b/tools/testing/selftests/bpf/prog_tests/timer.c index d66687f1ee6a..56f660ca567b 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer.c +++ b/tools/testing/selftests/bpf/prog_tests/timer.c @@ -86,6 +86,10 @@ void serial_test_timer(void) int err; timer_skel = timer__open_and_load(); + if (!timer_skel && errno == EOPNOTSUPP) { + test__skip(); + return; + } if (!ASSERT_OK_PTR(timer_skel, "timer_skel_load")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/timer_crash.c b/tools/testing/selftests/bpf/prog_tests/timer_crash.c index f74b82305da8..b841597c8a3a 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer_crash.c +++ b/tools/testing/selftests/bpf/prog_tests/timer_crash.c @@ -12,6 +12,10 @@ static void test_timer_crash_mode(int mode) struct timer_crash *skel; skel = timer_crash__open_and_load(); + if (!skel && errno == EOPNOTSUPP) { + test__skip(); + return; + } if (!ASSERT_OK_PTR(skel, "timer_crash__open_and_load")) return; skel->bss->pid = getpid(); diff --git a/tools/testing/selftests/bpf/prog_tests/timer_lockup.c b/tools/testing/selftests/bpf/prog_tests/timer_lockup.c index 1a2f99596916..eb303fa1e09a 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer_lockup.c +++ b/tools/testing/selftests/bpf/prog_tests/timer_lockup.c @@ -59,6 +59,10 @@ void test_timer_lockup(void) } skel = timer_lockup__open_and_load(); + if (!skel && errno == EOPNOTSUPP) { + test__skip(); + return; + } if (!ASSERT_OK_PTR(skel, "timer_lockup__open_and_load")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/timer_mim.c b/tools/testing/selftests/bpf/prog_tests/timer_mim.c index 9ff7843909e7..c930c7d7105b 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer_mim.c +++ b/tools/testing/selftests/bpf/prog_tests/timer_mim.c @@ -65,6 +65,10 @@ void serial_test_timer_mim(void) goto cleanup; timer_skel = timer_mim__open_and_load(); + if (!timer_skel && errno == EOPNOTSUPP) { + test__skip(); + return; + } if (!ASSERT_OK_PTR(timer_skel, "timer_skel_load")) goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index f9392df23f8a..b81dde283052 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -115,7 +115,7 @@ static int create_bpffs_fd(void) static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts) { - int mnt_fd, err; + int err; /* set up token delegation mount options */ err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds, opts->cmds_str); @@ -136,12 +136,7 @@ static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts) if (err < 0) return -errno; - /* create O_PATH fd for detached mount */ - mnt_fd = sys_fsmount(fs_fd, 0, 0); - if (err < 0) - return -errno; - - return mnt_fd; + return 0; } /* send FD over Unix domain (AF_UNIX) socket */ @@ -287,6 +282,7 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba { int mnt_fd = -1, fs_fd = -1, err = 0, bpffs_fd = -1, token_fd = -1; struct token_lsm *lsm_skel = NULL; + char one; /* load and attach LSM "policy" before we go into unpriv userns */ lsm_skel = token_lsm__open_and_load(); @@ -333,13 +329,19 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba err = sendfd(sock_fd, fs_fd); if (!ASSERT_OK(err, "send_fs_fd")) goto cleanup; - zclose(fs_fd); + + /* wait that the parent reads the fd, does the fsconfig() calls + * and send us a signal that it is done + */ + err = read(sock_fd, &one, sizeof(one)); + if (!ASSERT_GE(err, 0, "read_one")) + goto cleanup; /* avoid mucking around with mount namespaces and mounting at - * well-known path, just get detach-mounted BPF FS fd back from parent + * well-known path, just create O_PATH fd for detached mount */ - err = recvfd(sock_fd, &mnt_fd); - if (!ASSERT_OK(err, "recv_mnt_fd")) + mnt_fd = sys_fsmount(fs_fd, 0, 0); + if (!ASSERT_OK_FD(mnt_fd, "mnt_fd")) goto cleanup; /* try to fspick() BPF FS and try to add some delegation options */ @@ -429,24 +431,24 @@ static int wait_for_pid(pid_t pid) static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd) { - int fs_fd = -1, mnt_fd = -1, token_fd = -1, err; + int fs_fd = -1, token_fd = -1, err; + char one = 1; err = recvfd(sock_fd, &fs_fd); if (!ASSERT_OK(err, "recv_bpffs_fd")) goto cleanup; - mnt_fd = materialize_bpffs_fd(fs_fd, bpffs_opts); - if (!ASSERT_GE(mnt_fd, 0, "materialize_bpffs_fd")) { + err = materialize_bpffs_fd(fs_fd, bpffs_opts); + if (!ASSERT_GE(err, 0, "materialize_bpffs_fd")) { err = -EINVAL; goto cleanup; } - zclose(fs_fd); - /* pass BPF FS context object to parent */ - err = sendfd(sock_fd, mnt_fd); - if (!ASSERT_OK(err, "send_mnt_fd")) + /* notify the child that we did the fsconfig() calls and it can proceed. */ + err = write(sock_fd, &one, sizeof(one)); + if (!ASSERT_EQ(err, sizeof(one), "send_one")) goto cleanup; - zclose(mnt_fd); + zclose(fs_fd); /* receive BPF token FD back from child for some extra tests */ err = recvfd(sock_fd, &token_fd); @@ -459,7 +461,6 @@ static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd) cleanup: zclose(sock_fd); zclose(fs_fd); - zclose(mnt_fd); zclose(token_fd); if (child_pid > 0) @@ -1046,6 +1047,41 @@ static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *l #define bit(n) (1ULL << (n)) +static int userns_bpf_token_info(int mnt_fd, struct token_lsm *lsm_skel) +{ + int err, token_fd = -1; + struct bpf_token_info info; + u32 len = sizeof(struct bpf_token_info); + + /* create BPF token from BPF FS mount */ + token_fd = bpf_token_create(mnt_fd, NULL); + if (!ASSERT_GT(token_fd, 0, "token_create")) { + err = -EINVAL; + goto cleanup; + } + + memset(&info, 0, len); + err = bpf_obj_get_info_by_fd(token_fd, &info, &len); + if (!ASSERT_ERR(err, "bpf_obj_get_token_info")) + goto cleanup; + if (!ASSERT_EQ(info.allowed_cmds, bit(BPF_MAP_CREATE), "token_info_cmds_map_create")) { + err = -EINVAL; + goto cleanup; + } + if (!ASSERT_EQ(info.allowed_progs, bit(BPF_PROG_TYPE_XDP), "token_info_progs_xdp")) { + err = -EINVAL; + goto cleanup; + } + + /* The BPF_PROG_TYPE_EXT is not set in token */ + if (ASSERT_EQ(info.allowed_progs, bit(BPF_PROG_TYPE_EXT), "token_info_progs_ext")) + err = -EINVAL; + +cleanup: + zclose(token_fd); + return err; +} + void test_token(void) { if (test__start_subtest("map_token")) { @@ -1149,4 +1185,13 @@ void test_token(void) subtest_userns(&opts, userns_obj_priv_implicit_token_envvar); } + if (test__start_subtest("bpf_token_info")) { + struct bpffs_opts opts = { + .cmds = bit(BPF_MAP_CREATE), + .progs = bit(BPF_PROG_TYPE_XDP), + .attachs = ~0ULL, + }; + + subtest_userns(&opts, userns_bpf_token_info); + } } diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c index a222df765bc3..10e231965589 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c @@ -28,10 +28,62 @@ static void test_bpf_spin_lock(bool is_spin_lock) tracing_failure__destroy(skel); } +static void test_tracing_fail_prog(const char *prog_name, const char *exp_msg) +{ + struct tracing_failure *skel; + struct bpf_program *prog; + char log_buf[256]; + int err; + + skel = tracing_failure__open(); + if (!ASSERT_OK_PTR(skel, "tracing_failure__open")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto out; + + bpf_program__set_autoload(prog, true); + bpf_program__set_log_buf(prog, log_buf, sizeof(log_buf)); + + err = tracing_failure__load(skel); + if (!ASSERT_ERR(err, "tracing_failure__load")) + goto out; + + ASSERT_HAS_SUBSTR(log_buf, exp_msg, "log_buf"); +out: + tracing_failure__destroy(skel); +} + +static void test_tracing_deny(void) +{ + int btf_id; + + /* __rcu_read_lock depends on CONFIG_PREEMPT_RCU */ + btf_id = libbpf_find_vmlinux_btf_id("__rcu_read_lock", BPF_TRACE_FENTRY); + if (btf_id <= 0) { + test__skip(); + return; + } + + test_tracing_fail_prog("tracing_deny", + "Attaching tracing programs to function '__rcu_read_lock' is rejected."); +} + +static void test_fexit_noreturns(void) +{ + test_tracing_fail_prog("fexit_noreturns", + "Attaching fexit/fmod_ret to __noreturn function 'do_exit' is rejected."); +} + void test_tracing_failure(void) { if (test__start_subtest("bpf_spin_lock")) test_bpf_spin_lock(true); if (test__start_subtest("bpf_spin_unlock")) test_bpf_spin_lock(false); + if (test__start_subtest("tracing_deny")) + test_tracing_deny(); + if (test__start_subtest("fexit_noreturns")) + test_fexit_noreturns(); } diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c index c397336fe1ed..b17dc39a23db 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c @@ -251,7 +251,7 @@ static void test_uretprobe_syscall_call(void) .retprobe = true, ); struct uprobe_syscall_executed *skel; - int pid, status, err, go[2], c; + int pid, status, err, go[2], c = 0; if (!ASSERT_OK(pipe(go), "pipe")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c b/tools/testing/selftests/bpf/prog_tests/usdt.c index 495d66414b57..9057e983cc54 100644 --- a/tools/testing/selftests/bpf/prog_tests/usdt.c +++ b/tools/testing/selftests/bpf/prog_tests/usdt.c @@ -270,8 +270,16 @@ static void subtest_multispec_usdt(void) */ trigger_300_usdts(); - /* we'll reuse usdt_100 BPF program for usdt_300 test */ bpf_link__destroy(skel->links.usdt_100); + + bss->usdt_100_called = 0; + bss->usdt_100_sum = 0; + + /* If built with arm64/clang, there will be much less number of specs + * for usdt_300 call sites. + */ +#if !defined(__aarch64__) || !defined(__clang__) + /* we'll reuse usdt_100 BPF program for usdt_300 test */ skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe", "test", "usdt_300", NULL); err = -errno; @@ -282,13 +290,11 @@ static void subtest_multispec_usdt(void) /* let's check that there are no "dangling" BPF programs attached due * to partial success of the above test:usdt_300 attachment */ - bss->usdt_100_called = 0; - bss->usdt_100_sum = 0; - f300(777); /* this is 301st instance of usdt_300 */ ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called"); ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum"); +#endif /* This time we have USDT with 400 inlined invocations, but arg specs * should be the same across all sites, so libbpf will only need to diff --git a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c index d424e7ecbd12..9fd3ae987321 100644 --- a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c @@ -21,8 +21,7 @@ #include "../progs/test_user_ringbuf.h" static const long c_sample_size = sizeof(struct sample) + BPF_RINGBUF_HDR_SZ; -static const long c_ringbuf_size = 1 << 12; /* 1 small page */ -static const long c_max_entries = c_ringbuf_size / c_sample_size; +static long c_ringbuf_size, c_max_entries; static void drain_current_samples(void) { @@ -424,7 +423,9 @@ static void test_user_ringbuf_loop(void) uint32_t remaining_samples = total_samples; int err; - BUILD_BUG_ON(total_samples <= c_max_entries); + if (!ASSERT_LT(c_max_entries, total_samples, "compare_c_max_entries")) + return; + err = load_skel_create_user_ringbuf(&skel, &ringbuf); if (err) return; @@ -686,6 +687,9 @@ void test_user_ringbuf(void) { int i; + c_ringbuf_size = getpagesize(); /* 1 page */ + c_max_entries = c_ringbuf_size / c_sample_size; + for (i = 0; i < ARRAY_SIZE(success_tests); i++) { if (!test__start_subtest(success_tests[i].test_name)) continue; diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index c9da06741104..77ec95d4ffaa 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -85,6 +85,7 @@ #include "verifier_store_release.skel.h" #include "verifier_subprog_precision.skel.h" #include "verifier_subreg.skel.h" +#include "verifier_tailcall.skel.h" #include "verifier_tailcall_jit.skel.h" #include "verifier_typedef.skel.h" #include "verifier_uninit.skel.h" @@ -219,6 +220,7 @@ void test_verifier_stack_ptr(void) { RUN(verifier_stack_ptr); } void test_verifier_store_release(void) { RUN(verifier_store_release); } void test_verifier_subprog_precision(void) { RUN(verifier_subprog_precision); } void test_verifier_subreg(void) { RUN(verifier_subreg); } +void test_verifier_tailcall(void) { RUN(verifier_tailcall); } void test_verifier_tailcall_jit(void) { RUN(verifier_tailcall_jit); } void test_verifier_typedef(void) { RUN(verifier_typedef); } void test_verifier_uninit(void) { RUN(verifier_uninit); } diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c index ab0f02faa80c..4d69d9d55e17 100644 --- a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c +++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c @@ -268,7 +268,7 @@ static void test_verify_pkcs7_sig_from_map(void) char *tmp_dir; struct test_verify_pkcs7_sig *skel = NULL; struct bpf_map *map; - struct data data; + struct data data = {}; int ret, zero = 0; /* Trigger creation of session keyring. */ diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index b2b2d85dbb1b..43264347e7d7 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -37,21 +37,26 @@ static void test_xdp_adjust_tail_shrink(void) bpf_object__close(obj); } -static void test_xdp_adjust_tail_grow(void) +static void test_xdp_adjust_tail_grow(bool is_64k_pagesize) { const char *file = "./test_xdp_adjust_tail_grow.bpf.o"; struct bpf_object *obj; - char buf[4096]; /* avoid segfault: large buf to hold grow results */ + char buf[8192]; /* avoid segfault: large buf to hold grow results */ __u32 expect_sz; int err, prog_fd; LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &pkt_v4, - .data_size_in = sizeof(pkt_v4), .data_out = buf, .data_size_out = sizeof(buf), .repeat = 1, ); + /* topts.data_size_in as a special signal to bpf prog */ + if (is_64k_pagesize) + topts.data_size_in = sizeof(pkt_v4) - 1; + else + topts.data_size_in = sizeof(pkt_v4); + err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); if (!ASSERT_OK(err, "test_xdp_adjust_tail_grow")) return; @@ -208,7 +213,7 @@ static void test_xdp_adjust_frags_tail_shrink(void) bpf_object__close(obj); } -static void test_xdp_adjust_frags_tail_grow(void) +static void test_xdp_adjust_frags_tail_grow_4k(void) { const char *file = "./test_xdp_adjust_tail_grow.bpf.o"; __u32 exp_size; @@ -246,14 +251,20 @@ static void test_xdp_adjust_frags_tail_grow(void) ASSERT_EQ(topts.retval, XDP_TX, "9Kb+10b retval"); ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size"); - for (i = 0; i < 9000; i++) - ASSERT_EQ(buf[i], 1, "9Kb+10b-old"); + for (i = 0; i < 9000; i++) { + if (buf[i] != 1) + ASSERT_EQ(buf[i], 1, "9Kb+10b-old"); + } - for (i = 9000; i < 9010; i++) - ASSERT_EQ(buf[i], 0, "9Kb+10b-new"); + for (i = 9000; i < 9010; i++) { + if (buf[i] != 0) + ASSERT_EQ(buf[i], 0, "9Kb+10b-new"); + } - for (i = 9010; i < 16384; i++) - ASSERT_EQ(buf[i], 1, "9Kb+10b-untouched"); + for (i = 9010; i < 16384; i++) { + if (buf[i] != 1) + ASSERT_EQ(buf[i], 1, "9Kb+10b-untouched"); + } /* Test a too large grow */ memset(buf, 1, 16384); @@ -273,16 +284,93 @@ static void test_xdp_adjust_frags_tail_grow(void) bpf_object__close(obj); } +static void test_xdp_adjust_frags_tail_grow_64k(void) +{ + const char *file = "./test_xdp_adjust_tail_grow.bpf.o"; + __u32 exp_size; + struct bpf_program *prog; + struct bpf_object *obj; + int err, i, prog_fd; + __u8 *buf; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + obj = bpf_object__open(file); + if (libbpf_get_error(obj)) + return; + + prog = bpf_object__next_program(obj, NULL); + if (bpf_object__load(obj)) + goto out; + + prog_fd = bpf_program__fd(prog); + + buf = malloc(262144); + if (!ASSERT_OK_PTR(buf, "alloc buf 256Kb")) + goto out; + + /* Test case add 10 bytes to last frag */ + memset(buf, 1, 262144); + exp_size = 90000 + 10; + + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = 90000; + topts.data_size_out = 262144; + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "90Kb+10b"); + ASSERT_EQ(topts.retval, XDP_TX, "90Kb+10b retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "90Kb+10b size"); + + for (i = 0; i < 90000; i++) { + if (buf[i] != 1) + ASSERT_EQ(buf[i], 1, "90Kb+10b-old"); + } + + for (i = 90000; i < 90010; i++) { + if (buf[i] != 0) + ASSERT_EQ(buf[i], 0, "90Kb+10b-new"); + } + + for (i = 90010; i < 262144; i++) { + if (buf[i] != 1) + ASSERT_EQ(buf[i], 1, "90Kb+10b-untouched"); + } + + /* Test a too large grow */ + memset(buf, 1, 262144); + exp_size = 90001; + + topts.data_in = topts.data_out = buf; + topts.data_size_in = 90001; + topts.data_size_out = 262144; + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "90Kb+10b"); + ASSERT_EQ(topts.retval, XDP_DROP, "90Kb+10b retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "90Kb+10b size"); + + free(buf); +out: + bpf_object__close(obj); +} + void test_xdp_adjust_tail(void) { + int page_size = getpagesize(); + if (test__start_subtest("xdp_adjust_tail_shrink")) test_xdp_adjust_tail_shrink(); if (test__start_subtest("xdp_adjust_tail_grow")) - test_xdp_adjust_tail_grow(); + test_xdp_adjust_tail_grow(page_size == 65536); if (test__start_subtest("xdp_adjust_tail_grow2")) test_xdp_adjust_tail_grow2(); if (test__start_subtest("xdp_adjust_frags_tail_shrink")) test_xdp_adjust_frags_tail_shrink(); - if (test__start_subtest("xdp_adjust_frags_tail_grow")) - test_xdp_adjust_frags_tail_grow(); + if (test__start_subtest("xdp_adjust_frags_tail_grow")) { + if (page_size == 65536) + test_xdp_adjust_frags_tail_grow_64k(); + else + test_xdp_adjust_frags_tail_grow_4k(); + } } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c index 7dac044664ac..dd34b0cc4b4e 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -66,16 +66,25 @@ static int attach_tc_prog(struct bpf_tc_hook *hook, int fd) #else #define MAX_PKT_SIZE 3408 #endif + +#define PAGE_SIZE_4K 4096 +#define PAGE_SIZE_64K 65536 + static void test_max_pkt_size(int fd) { - char data[MAX_PKT_SIZE + 1] = {}; + char data[PAGE_SIZE_64K + 1] = {}; int err; DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, .data_in = &data, - .data_size_in = MAX_PKT_SIZE, .flags = BPF_F_TEST_XDP_LIVE_FRAMES, .repeat = 1, ); + + if (getpagesize() == PAGE_SIZE_64K) + opts.data_size_in = MAX_PKT_SIZE + PAGE_SIZE_64K - PAGE_SIZE_4K; + else + opts.data_size_in = MAX_PKT_SIZE; + err = bpf_prog_test_run_opts(fd, &opts); ASSERT_OK(err, "prog_run_max_size"); diff --git a/tools/testing/selftests/bpf/progs/bpf_arena_spin_lock.h b/tools/testing/selftests/bpf/progs/bpf_arena_spin_lock.h index d67466c1ff77..f90531cf3ee5 100644 --- a/tools/testing/selftests/bpf/progs/bpf_arena_spin_lock.h +++ b/tools/testing/selftests/bpf/progs/bpf_arena_spin_lock.h @@ -302,7 +302,7 @@ int arena_spin_lock_slowpath(arena_spinlock_t __arena __arg_arena *lock, u32 val * barriers. */ if (val & _Q_LOCKED_MASK) - smp_cond_load_acquire_label(&lock->locked, !VAL, release_err); + (void)smp_cond_load_acquire_label(&lock->locked, !VAL, release_err); /* * take ownership and clear the pending bit. @@ -380,7 +380,7 @@ int arena_spin_lock_slowpath(arena_spinlock_t __arena __arg_arena *lock, u32 val /* Link @node into the waitqueue. */ WRITE_ONCE(prev->next, node); - arch_mcs_spin_lock_contended_label(&node->locked, release_node_err); + (void)arch_mcs_spin_lock_contended_label(&node->locked, release_node_err); /* * While waiting for the MCS lock, the next pointer may have diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_map_elem.c b/tools/testing/selftests/bpf/progs/bpf_iter_map_elem.c new file mode 100644 index 000000000000..2f20485e0de3 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_iter_map_elem.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +__u32 value_sum = 0; + +SEC("iter/bpf_map_elem") +int dump_bpf_map_values(struct bpf_iter__bpf_map_elem *ctx) +{ + __u32 value = 0; + + if (ctx->value == (void *)0) + return 0; + + bpf_probe_read_kernel(&value, sizeof(value), ctx->value); + value_sum += value; + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 6e208e24ba3b..c1cfd297aabf 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -83,9 +83,11 @@ * expect return value to match passed parameter: * - a decimal number * - a hexadecimal number, when starts from 0x - * - literal INT_MIN - * - literal POINTER_VALUE (see definition below) - * - literal TEST_DATA_LEN (see definition below) + * - a macro which expands to one of the above + * - literal _INT_MIN (expands to INT_MIN) + * In addition, two special macros are defined below: + * - POINTER_VALUE + * - TEST_DATA_LEN * __retval_unpriv Same, but load program in unprivileged mode. * * __description Text to be used instead of a program name for display @@ -125,8 +127,8 @@ #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) #define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag))) -#define __retval(val) __attribute__((btf_decl_tag("comment:test_retval="#val))) -#define __retval_unpriv(val) __attribute__((btf_decl_tag("comment:test_retval_unpriv="#val))) +#define __retval(val) __attribute__((btf_decl_tag("comment:test_retval="XSTR(val)))) +#define __retval_unpriv(val) __attribute__((btf_decl_tag("comment:test_retval_unpriv="XSTR(val)))) #define __auxiliary __attribute__((btf_decl_tag("comment:test_auxiliary"))) #define __auxiliary_unpriv __attribute__((btf_decl_tag("comment:test_auxiliary_unpriv"))) #define __btf_path(path) __attribute__((btf_decl_tag("comment:test_btf_path=" path))) @@ -155,7 +157,7 @@ #define __imm_insn(name, expr) [name]"i"(*(long *)&(expr)) /* Magic constants used with __retval() */ -#define POINTER_VALUE 0xcafe4all +#define POINTER_VALUE 0xbadcafe #define TEST_DATA_LEN 64 #ifndef __used @@ -227,8 +229,17 @@ #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_riscv) && __riscv_xlen == 64)) || \ + (defined(__TARGET_ARCH_powerpc)) #define CAN_USE_LOAD_ACQ_STORE_REL #endif +#if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) +#define SPEC_V1 +#endif + +#if defined(__TARGET_ARCH_x86) +#define SPEC_V4 +#endif + #endif diff --git a/tools/testing/selftests/bpf/progs/cgroup_mprog.c b/tools/testing/selftests/bpf/progs/cgroup_mprog.c new file mode 100644 index 000000000000..6a0ea02c4de2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_mprog.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include + +char _license[] SEC("license") = "GPL"; + +SEC("cgroup/getsockopt") +int getsockopt_1(struct bpf_sockopt *ctx) +{ + return 1; +} + +SEC("cgroup/getsockopt") +int getsockopt_2(struct bpf_sockopt *ctx) +{ + return 1; +} + +SEC("cgroup/getsockopt") +int getsockopt_3(struct bpf_sockopt *ctx) +{ + return 1; +} + +SEC("cgroup/getsockopt") +int getsockopt_4(struct bpf_sockopt *ctx) +{ + return 1; +} diff --git a/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c b/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c new file mode 100644 index 000000000000..092db1d0435e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include +#include "bpf_experimental.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +char value[16]; + +static __always_inline void read_xattr(struct cgroup *cgroup) +{ + struct bpf_dynptr value_ptr; + + bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr); + bpf_cgroup_read_xattr(cgroup, "user.bpf_test", + &value_ptr); +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(trusted_cgroup_ptr_sleepable) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup *cgrp; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + read_xattr(cgrp); + bpf_cgroup_release(cgrp); + return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(trusted_cgroup_ptr_non_sleepable) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup *cgrp; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + read_xattr(cgrp); + bpf_cgroup_release(cgrp); + return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(use_css_iter_non_sleepable) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup_subsys_state *css; + struct cgroup *cgrp; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) + read_xattr(css->cgroup); + + bpf_cgroup_release(cgrp); + return 0; +} + +SEC("lsm.s/socket_connect") +__failure __msg("expected an RCU CS") +int BPF_PROG(use_css_iter_sleepable_missing_rcu_lock) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup_subsys_state *css; + struct cgroup *cgrp; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) + read_xattr(css->cgroup); + + bpf_cgroup_release(cgrp); + return 0; +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(use_css_iter_sleepable_with_rcu_lock) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup_subsys_state *css; + struct cgroup *cgrp; + + bpf_rcu_read_lock(); + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + goto out; + + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) + read_xattr(css->cgroup); + + bpf_cgroup_release(cgrp); +out: + bpf_rcu_read_unlock(); + return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(use_bpf_cgroup_ancestor) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup *cgrp, *ancestor; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + ancestor = bpf_cgroup_ancestor(cgrp, 1); + if (!ancestor) + goto out; + + read_xattr(cgrp); + bpf_cgroup_release(ancestor); +out: + bpf_cgroup_release(cgrp); + return 0; +} + +SEC("cgroup/sendmsg4") +__success +int BPF_PROG(cgroup_skb) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup *cgrp, *ancestor; + + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) + return 0; + + ancestor = bpf_cgroup_ancestor(cgrp, 1); + if (!ancestor) + goto out; + + read_xattr(cgrp); + bpf_cgroup_release(ancestor); +out: + bpf_cgroup_release(cgrp); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/compute_live_registers.c b/tools/testing/selftests/bpf/progs/compute_live_registers.c index f3d79aecbf93..6884ab99a421 100644 --- a/tools/testing/selftests/bpf/progs/compute_live_registers.c +++ b/tools/testing/selftests/bpf/progs/compute_live_registers.c @@ -240,6 +240,22 @@ __naked void if2(void) ::: __clobber_all); } +/* Verifier misses that r2 is alive if jset is not handled properly */ +SEC("socket") +__log_level(2) +__msg("2: 012....... (45) if r1 & 0x7 goto pc+1") +__naked void if3_jset_bug(void) +{ + asm volatile ( + "r0 = 1;" + "r2 = 2;" + "if r1 & 0x7 goto +1;" + "exit;" + "r0 = r2;" + "exit;" + ::: __clobber_all); +} + SEC("socket") __log_level(2) __msg("0: .......... (b7) r1 = 0") diff --git a/tools/testing/selftests/bpf/progs/crypto_sanity.c b/tools/testing/selftests/bpf/progs/crypto_sanity.c index 645be6cddf36..dfd8a258f14a 100644 --- a/tools/testing/selftests/bpf/progs/crypto_sanity.c +++ b/tools/testing/selftests/bpf/progs/crypto_sanity.c @@ -14,7 +14,7 @@ unsigned char key[256] = {}; u16 udp_test_port = 7777; u32 authsize, key_len; char algo[128] = {}; -char dst[16] = {}; +char dst[16] = {}, dst_bad[8] = {}; int status; static int skb_dynptr_validate(struct __sk_buff *skb, struct bpf_dynptr *psrc) @@ -59,10 +59,9 @@ int skb_crypto_setup(void *ctx) .authsize = authsize, }; struct bpf_crypto_ctx *cctx; - int err = 0; + int err; status = 0; - if (key_len > 256) { status = -EINVAL; return 0; @@ -70,8 +69,8 @@ int skb_crypto_setup(void *ctx) __builtin_memcpy(¶ms.algo, algo, sizeof(algo)); __builtin_memcpy(¶ms.key, key, sizeof(key)); - cctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + cctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err); if (!cctx) { status = err; return 0; @@ -80,7 +79,6 @@ int skb_crypto_setup(void *ctx) err = crypto_ctx_insert(cctx); if (err && err != -EEXIST) status = err; - return 0; } @@ -92,6 +90,7 @@ int decrypt_sanity(struct __sk_buff *skb) struct bpf_dynptr psrc, pdst; int err; + status = 0; err = skb_dynptr_validate(skb, &psrc); if (err < 0) { status = err; @@ -110,13 +109,23 @@ int decrypt_sanity(struct __sk_buff *skb) return TC_ACT_SHOT; } - /* dst is a global variable to make testing part easier to check. In real - * production code, a percpu map should be used to store the result. + /* Check also bad case where the dst buffer is smaller than the + * skb's linear section. + */ + bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst); + status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL); + if (!status) + status = -EIO; + if (status != -EINVAL) + goto err; + + /* dst is a global variable to make testing part easier to check. + * In real production code, a percpu map should be used to store + * the result. */ bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst); - status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL); - +err: return TC_ACT_SHOT; } @@ -129,7 +138,6 @@ int encrypt_sanity(struct __sk_buff *skb) int err; status = 0; - err = skb_dynptr_validate(skb, &psrc); if (err < 0) { status = err; @@ -148,13 +156,23 @@ int encrypt_sanity(struct __sk_buff *skb) return TC_ACT_SHOT; } - /* dst is a global variable to make testing part easier to check. In real - * production code, a percpu map should be used to store the result. + /* Check also bad case where the dst buffer is smaller than the + * skb's linear section. + */ + bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst); + status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL); + if (!status) + status = -EIO; + if (status != -EINVAL) + goto err; + + /* dst is a global variable to make testing part easier to check. + * In real production code, a percpu map should be used to store + * the result. */ bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst); - status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL); - +err: return TC_ACT_SHOT; } diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c index a0391f9da2d4..8315273cb900 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_success.c +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -9,6 +9,8 @@ #include "bpf_misc.h" #include "errno.h" +#define PAGE_SIZE_64K 65536 + char _license[] SEC("license") = "GPL"; int pid, err, val; @@ -611,11 +613,12 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp) struct bpf_dynptr ptr_buf, ptr_xdp; char data[] = "qwertyuiopasdfghjkl"; char buf[32] = {'\0'}; - __u32 len = sizeof(data); + __u32 len = sizeof(data), xdp_data_size; int i, chunks = 200; /* ptr_xdp is backed by non-contiguous memory */ bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp); + xdp_data_size = bpf_dynptr_size(&ptr_xdp); bpf_ringbuf_reserve_dynptr(&ringbuf, len * chunks, 0, &ptr_buf); /* Destination dynptr is backed by non-contiguous memory */ @@ -673,7 +676,7 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp) goto out; } - if (bpf_dynptr_copy(&ptr_xdp, 2000, &ptr_xdp, 0, len * chunks) != -E2BIG) + if (bpf_dynptr_copy(&ptr_xdp, xdp_data_size - 3000, &ptr_xdp, 0, len * chunks) != -E2BIG) err = 1; out: @@ -681,6 +684,173 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp) return XDP_DROP; } +char memset_zero_data[] = "data to be zeroed"; + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_zero(void *ctx) +{ + __u32 data_sz = sizeof(memset_zero_data); + char zeroes[32] = {'\0'}; + struct bpf_dynptr ptr; + + err = bpf_dynptr_from_mem(memset_zero_data, data_sz, 0, &ptr); + err = err ?: bpf_dynptr_memset(&ptr, 0, data_sz, 0); + err = err ?: bpf_memcmp(zeroes, memset_zero_data, data_sz); + + return 0; +} + +#define DYNPTR_MEMSET_VAL 42 + +char memset_notzero_data[] = "data to be overwritten"; + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_notzero(void *ctx) +{ + u32 data_sz = sizeof(memset_notzero_data); + struct bpf_dynptr ptr; + char expected[32]; + + __builtin_memset(expected, DYNPTR_MEMSET_VAL, data_sz); + + err = bpf_dynptr_from_mem(memset_notzero_data, data_sz, 0, &ptr); + err = err ?: bpf_dynptr_memset(&ptr, 0, data_sz, DYNPTR_MEMSET_VAL); + err = err ?: bpf_memcmp(expected, memset_notzero_data, data_sz); + + return 0; +} + +char memset_zero_offset_data[] = "data to be zeroed partially"; + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_zero_offset(void *ctx) +{ + char expected[] = "data to \0\0\0\0eroed partially"; + __u32 data_sz = sizeof(memset_zero_offset_data); + struct bpf_dynptr ptr; + + err = bpf_dynptr_from_mem(memset_zero_offset_data, data_sz, 0, &ptr); + err = err ?: bpf_dynptr_memset(&ptr, 8, 4, 0); + err = err ?: bpf_memcmp(expected, memset_zero_offset_data, data_sz); + + return 0; +} + +char memset_zero_adjusted_data[] = "data to be zeroed partially"; + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_zero_adjusted(void *ctx) +{ + char expected[] = "data\0\0\0\0be zeroed partially"; + __u32 data_sz = sizeof(memset_zero_adjusted_data); + struct bpf_dynptr ptr; + + err = bpf_dynptr_from_mem(memset_zero_adjusted_data, data_sz, 0, &ptr); + err = err ?: bpf_dynptr_adjust(&ptr, 4, 8); + err = err ?: bpf_dynptr_memset(&ptr, 0, bpf_dynptr_size(&ptr), 0); + err = err ?: bpf_memcmp(expected, memset_zero_adjusted_data, data_sz); + + return 0; +} + +char memset_overflow_data[] = "memset overflow data"; + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_overflow(void *ctx) +{ + __u32 data_sz = sizeof(memset_overflow_data); + struct bpf_dynptr ptr; + int ret; + + err = bpf_dynptr_from_mem(memset_overflow_data, data_sz, 0, &ptr); + ret = bpf_dynptr_memset(&ptr, 0, data_sz + 1, 0); + if (ret != -E2BIG) + err = 1; + + return 0; +} + +SEC("?tp/syscalls/sys_enter_nanosleep") +int test_dynptr_memset_overflow_offset(void *ctx) +{ + __u32 data_sz = sizeof(memset_overflow_data); + struct bpf_dynptr ptr; + int ret; + + err = bpf_dynptr_from_mem(memset_overflow_data, data_sz, 0, &ptr); + ret = bpf_dynptr_memset(&ptr, 1, data_sz, 0); + if (ret != -E2BIG) + err = 1; + + return 0; +} + +SEC("?cgroup_skb/egress") +int test_dynptr_memset_readonly(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + int ret; + + err = bpf_dynptr_from_skb(skb, 0, &ptr); + + /* cgroup skbs are read only, memset should fail */ + ret = bpf_dynptr_memset(&ptr, 0, bpf_dynptr_size(&ptr), 0); + if (ret != -EINVAL) + err = 1; + + return 0; +} + +#define min_t(type, x, y) ({ \ + type __x = (x); \ + type __y = (y); \ + __x < __y ? __x : __y; }) + +SEC("xdp") +int test_dynptr_memset_xdp_chunks(struct xdp_md *xdp) +{ + u32 data_sz, chunk_sz, offset = 0; + const int max_chunks = 200; + struct bpf_dynptr ptr_xdp; + char expected_buf[32]; + char buf[32]; + int i; + + __builtin_memset(expected_buf, DYNPTR_MEMSET_VAL, sizeof(expected_buf)); + + /* ptr_xdp is backed by non-contiguous memory */ + bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp); + data_sz = bpf_dynptr_size(&ptr_xdp); + + err = bpf_dynptr_memset(&ptr_xdp, 0, data_sz, DYNPTR_MEMSET_VAL); + if (err) { + /* bpf_dynptr_memset() eventually called bpf_xdp_pointer() + * where if data_sz is greater than 0xffff, -EFAULT will be + * returned. For 64K page size, data_sz is greater than + * 64K, so error is expected and let us zero out error and + * return success. + */ + if (data_sz >= PAGE_SIZE_64K) + err = 0; + goto out; + } + + bpf_for(i, 0, max_chunks) { + offset = i * sizeof(buf); + if (offset >= data_sz) + goto out; + chunk_sz = min_t(u32, sizeof(buf), data_sz - offset); + err = bpf_dynptr_read(&buf, chunk_sz, &ptr_xdp, offset, 0); + if (err) + goto out; + err = bpf_memcmp(buf, expected_buf, sizeof(buf)); + if (err) + goto out; + } +out: + return XDP_DROP; +} + void *user_ptr; /* Contains the copy of the data pointed by user_ptr. * Size 384 to make it not fit into a single kernel chunk when copying diff --git a/tools/testing/selftests/bpf/progs/fexit_noreturns.c b/tools/testing/selftests/bpf/progs/fexit_noreturns.c deleted file mode 100644 index 54654539f550..000000000000 --- a/tools/testing/selftests/bpf/progs/fexit_noreturns.c +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include -#include "bpf_misc.h" - -char _license[] SEC("license") = "GPL"; - -SEC("fexit/do_exit") -__failure __msg("Attaching fexit/fmod_ret to __noreturn functions is rejected.") -int BPF_PROG(noreturns) -{ - return 0; -} diff --git a/tools/testing/selftests/bpf/progs/iters.c b/tools/testing/selftests/bpf/progs/iters.c index 76adf4a8f2da..7dd92a303bf6 100644 --- a/tools/testing/selftests/bpf/progs/iters.c +++ b/tools/testing/selftests/bpf/progs/iters.c @@ -1649,4 +1649,281 @@ int clean_live_states(const void *ctx) return 0; } +SEC("?raw_tp") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("misaligned stack access off 0+-31+0 size 8") +__naked int absent_mark_in_the_middle_state(void) +{ + /* This is equivalent to C program below. + * + * r8 = bpf_get_prandom_u32(); + * r6 = -32; + * bpf_iter_num_new(&fp[-8], 0, 10); + * if (unlikely(bpf_get_prandom_u32())) + * r6 = -31; + * while (bpf_iter_num_next(&fp[-8])) { + * if (unlikely(bpf_get_prandom_u32())) + * *(fp + r6) = 7; + * } + * bpf_iter_num_destroy(&fp[-8]) + * return 0 + */ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r8 = r0;" + "r7 = 0;" + "r6 = -32;" + "r0 = 0;" + "*(u64 *)(r10 - 16) = r0;" + "r1 = r10;" + "r1 += -8;" + "r2 = 0;" + "r3 = 10;" + "call %[bpf_iter_num_new];" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto change_r6_%=;" + "loop_%=:" + "call noop;" + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_next];" + "if r0 == 0 goto loop_end_%=;" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto use_r6_%=;" + "goto loop_%=;" + "loop_end_%=:" + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_destroy];" + "r0 = 0;" + "exit;" + "use_r6_%=:" + "r0 = r10;" + "r0 += r6;" + "r1 = 7;" + "*(u64 *)(r0 + 0) = r1;" + "goto loop_%=;" + "change_r6_%=:" + "r6 = -31;" + "goto loop_%=;" + : + : __imm(bpf_iter_num_new), + __imm(bpf_iter_num_next), + __imm(bpf_iter_num_destroy), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + +__used __naked +static int noop(void) +{ + asm volatile ( + "r0 = 0;" + "exit;" + ); +} + +SEC("?raw_tp") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("misaligned stack access off 0+-31+0 size 8") +__naked int absent_mark_in_the_middle_state2(void) +{ + /* This is equivalent to C program below. + * + * r8 = bpf_get_prandom_u32(); + * r6 = -32; + * bpf_iter_num_new(&fp[-8], 0, 10); + * if (unlikely(bpf_get_prandom_u32())) { + * r6 = -31; + * jump_into_loop: + * goto +0; + * goto loop; + * } + * if (unlikely(bpf_get_prandom_u32())) + * goto jump_into_loop; + * loop: + * while (bpf_iter_num_next(&fp[-8])) { + * if (unlikely(bpf_get_prandom_u32())) + * *(fp + r6) = 7; + * } + * bpf_iter_num_destroy(&fp[-8]) + * return 0 + */ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r8 = r0;" + "r7 = 0;" + "r6 = -32;" + "r0 = 0;" + "*(u64 *)(r10 - 16) = r0;" + "r1 = r10;" + "r1 += -8;" + "r2 = 0;" + "r3 = 10;" + "call %[bpf_iter_num_new];" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto change_r6_%=;" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto jump_into_loop_%=;" + "loop_%=:" + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_next];" + "if r0 == 0 goto loop_end_%=;" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto use_r6_%=;" + "goto loop_%=;" + "loop_end_%=:" + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_destroy];" + "r0 = 0;" + "exit;" + "use_r6_%=:" + "r0 = r10;" + "r0 += r6;" + "r1 = 7;" + "*(u64 *)(r0 + 0) = r1;" + "goto loop_%=;" + "change_r6_%=:" + "r6 = -31;" + "jump_into_loop_%=: " + "goto +0;" + "goto loop_%=;" + : + : __imm(bpf_iter_num_new), + __imm(bpf_iter_num_next), + __imm(bpf_iter_num_destroy), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + +SEC("?raw_tp") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("misaligned stack access off 0+-31+0 size 8") +__naked int absent_mark_in_the_middle_state3(void) +{ + /* + * bpf_iter_num_new(&fp[-8], 0, 10) + * loop1(-32, &fp[-8]) + * loop1_wrapper(&fp[-8]) + * bpf_iter_num_destroy(&fp[-8]) + */ + asm volatile ( + "r1 = r10;" + "r1 += -8;" + "r2 = 0;" + "r3 = 10;" + "call %[bpf_iter_num_new];" + /* call #1 */ + "r1 = -32;" + "r2 = r10;" + "r2 += -8;" + "call loop1;" + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_destroy];" + /* call #2 */ + "r1 = r10;" + "r1 += -8;" + "r2 = 0;" + "r3 = 10;" + "call %[bpf_iter_num_new];" + "r1 = r10;" + "r1 += -8;" + "call loop1_wrapper;" + /* return */ + "r1 = r10;" + "r1 += -8;" + "call %[bpf_iter_num_destroy];" + "r0 = 0;" + "exit;" + : + : __imm(bpf_iter_num_new), + __imm(bpf_iter_num_destroy), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + +__used __naked +static int loop1(void) +{ + /* + * int loop1(num, iter) { + * r6 = num; + * r7 = iter; + * while (bpf_iter_num_next(r7)) { + * if (unlikely(bpf_get_prandom_u32())) + * *(fp + r6) = 7; + * } + * return 0 + * } + */ + asm volatile ( + "r6 = r1;" + "r7 = r2;" + "call %[bpf_get_prandom_u32];" + "r8 = r0;" + "loop_%=:" + "r1 = r7;" + "call %[bpf_iter_num_next];" + "if r0 == 0 goto loop_end_%=;" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto use_r6_%=;" + "goto loop_%=;" + "loop_end_%=:" + "r0 = 0;" + "exit;" + "use_r6_%=:" + "r0 = r10;" + "r0 += r6;" + "r1 = 7;" + "*(u64 *)(r0 + 0) = r1;" + "goto loop_%=;" + : + : __imm(bpf_iter_num_next), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + +__used __naked +static int loop1_wrapper(void) +{ + /* + * int loop1_wrapper(iter) { + * r6 = -32; + * r7 = iter; + * if (unlikely(bpf_get_prandom_u32())) + * r6 = -31; + * loop1(r6, r7); + * return 0; + * } + */ + asm volatile ( + "r6 = -32;" + "r7 = r1;" + "call %[bpf_get_prandom_u32];" + "r8 = r0;" + "call %[bpf_get_prandom_u32];" + "if r0 == r8 goto change_r6_%=;" + "loop_%=:" + "r1 = r6;" + "r2 = r7;" + "call loop1;" + "r0 = 0;" + "exit;" + "change_r6_%=:" + "r6 = -31;" + "goto loop_%=;" + : + : __imm(bpf_iter_num_next), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/linked_list_fail.c b/tools/testing/selftests/bpf/progs/linked_list_fail.c index 6438982b928b..ddd26d1a083f 100644 --- a/tools/testing/selftests/bpf/progs/linked_list_fail.c +++ b/tools/testing/selftests/bpf/progs/linked_list_fail.c @@ -226,8 +226,7 @@ int obj_new_no_composite(void *ctx) SEC("?tc") int obj_new_no_struct(void *ctx) { - - bpf_obj_new(union { int data; unsigned udata; }); + (void)bpf_obj_new(union { int data; unsigned udata; }); return 0; } @@ -252,7 +251,7 @@ int new_null_ret(void *ctx) SEC("?tc") int obj_new_acq(void *ctx) { - bpf_obj_new(struct foo); + (void)bpf_obj_new(struct foo); return 0; } diff --git a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c new file mode 100644 index 000000000000..4f94c971ae86 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" +#include "../test_kmods/bpf_testmod_kfunc.h" + +SEC("tp_btf/sys_enter") +__success +__log_level(2) +__msg("r8 = *(u64 *)(r7 +0) ; R7_w=ptr_nameidata(off={{[0-9]+}}) R8_w=rdonly_untrusted_mem(sz=0)") +__msg("r9 = *(u8 *)(r8 +0) ; R8_w=rdonly_untrusted_mem(sz=0) R9_w=scalar") +int btf_id_to_ptr_mem(void *ctx) +{ + struct task_struct *task; + struct nameidata *idata; + u64 ret, off; + + task = bpf_get_current_task_btf(); + idata = task->nameidata; + off = bpf_core_field_offset(struct nameidata, pathname); + /* + * asm block to have reliable match target for __msg, equivalent of: + * ret = task->nameidata->pathname[0]; + */ + asm volatile ( + "r7 = %[idata];" + "r7 += %[off];" + "r8 = *(u64 *)(r7 + 0);" + "r9 = *(u8 *)(r8 + 0);" + "%[ret] = r9;" + : [ret]"=r"(ret) + : [idata]"r"(idata), + [off]"r"(off) + : "r7", "r8", "r9"); + return ret; +} + +SEC("socket") +__success +__retval(0) +int ldx_is_ok_bad_addr(void *ctx) +{ + char *p; + + if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID)) + return 42; + + p = bpf_rdonly_cast(0, 0); + return p[0x7fff]; +} + +SEC("socket") +__success +__retval(1) +int ldx_is_ok_good_addr(void *ctx) +{ + int v, *p; + + v = 1; + p = bpf_rdonly_cast(&v, 0); + return *p; +} + +SEC("socket") +__success +int offset_not_tracked(void *ctx) +{ + int *p, i, s; + + p = bpf_rdonly_cast(0, 0); + s = 0; + bpf_for(i, 0, 1000 * 1000 * 1000) { + p++; + s += *p; + } + return s; +} + +SEC("socket") +__failure +__msg("cannot write into rdonly_untrusted_mem") +int stx_not_ok(void *ctx) +{ + int v, *p; + + v = 1; + p = bpf_rdonly_cast(&v, 0); + *p = 1; + return 0; +} + +SEC("socket") +__failure +__msg("cannot write into rdonly_untrusted_mem") +int atomic_not_ok(void *ctx) +{ + int v, *p; + + v = 1; + p = bpf_rdonly_cast(&v, 0); + __sync_fetch_and_add(p, 1); + return 0; +} + +SEC("socket") +__failure +__msg("cannot write into rdonly_untrusted_mem") +int atomic_rmw_not_ok(void *ctx) +{ + long v, *p; + + v = 1; + p = bpf_rdonly_cast(&v, 0); + return __sync_val_compare_and_swap(p, 0, 42); +} + +SEC("socket") +__failure +__msg("invalid access to memory, mem_size=0 off=0 size=4") +__msg("R1 min value is outside of the allowed memory range") +int kfunc_param_not_ok(void *ctx) +{ + int *p; + + p = bpf_rdonly_cast(0, 0); + bpf_kfunc_trusted_num_test(p); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure +__msg("R1 type=rdonly_untrusted_mem expected=") +int helper_param_not_ok(void *ctx) +{ + char *p; + + p = bpf_rdonly_cast(0, 0); + /* + * Any helper with ARG_CONST_SIZE_OR_ZERO constraint will do, + * the most permissive constraint + */ + bpf_copy_from_user(p, 0, (void *)42); + return 0; +} + +static __noinline u64 *get_some_addr(void) +{ + if (bpf_get_prandom_u32()) + return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock)); + else + return bpf_rdonly_cast(0, 0); +} + +SEC("socket") +__success +__retval(0) +int mixed_mem_type(void *ctx) +{ + u64 *p; + + /* Try to avoid compiler hoisting load to if branches by using __noinline func. */ + p = get_some_addr(); + return *p; +} + +__attribute__((__aligned__(8))) +u8 global[] = { + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + 0x99 +}; + +__always_inline +static u64 combine(void *p) +{ + u64 acc; + + acc = 0; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + acc |= (*(u64 *)p >> 56) << 24; + acc |= (*(u32 *)p >> 24) << 16; + acc |= (*(u16 *)p >> 8) << 8; + acc |= *(u8 *)p; +#else + acc |= (*(u64 *)p & 0xff) << 24; + acc |= (*(u32 *)p & 0xff) << 16; + acc |= (*(u16 *)p & 0xff) << 8; + acc |= *(u8 *)p; +#endif + return acc; +} + +SEC("socket") +__retval(0x88442211) +int diff_size_access(void *ctx) +{ + return combine(bpf_rdonly_cast(&global, 0)); +} + +SEC("socket") +__retval(0x99553322) +int misaligned_access(void *ctx) +{ + return combine(bpf_rdonly_cast(&global, 0) + 1); +} + +__weak int return_one(void) +{ + return 1; +} + +SEC("socket") +__success +__retval(1) +int null_check(void *ctx) +{ + int *p; + + p = bpf_rdonly_cast(0, 0); + if (p == 0) + /* make this a function call to avoid compiler + * moving r0 assignment before check. + */ + return return_one(); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/rbtree.c b/tools/testing/selftests/bpf/progs/rbtree.c index a3620c15c136..49fe93d7e059 100644 --- a/tools/testing/selftests/bpf/progs/rbtree.c +++ b/tools/testing/selftests/bpf/progs/rbtree.c @@ -61,19 +61,19 @@ static long __add_three(struct bpf_rb_root *root, struct bpf_spin_lock *lock) } m->key = 1; - bpf_spin_lock(&glock); - bpf_rbtree_add(&groot, &n->node, less); - bpf_rbtree_add(&groot, &m->node, less); - bpf_spin_unlock(&glock); + bpf_spin_lock(lock); + bpf_rbtree_add(root, &n->node, less); + bpf_rbtree_add(root, &m->node, less); + bpf_spin_unlock(lock); n = bpf_obj_new(typeof(*n)); if (!n) return 3; n->key = 3; - bpf_spin_lock(&glock); - bpf_rbtree_add(&groot, &n->node, less); - bpf_spin_unlock(&glock); + bpf_spin_lock(lock); + bpf_rbtree_add(root, &n->node, less); + bpf_spin_unlock(lock); return 0; } diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c index 43637ee2cdcd..3a868a199349 100644 --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c @@ -16,10 +16,11 @@ struct { __type(value, long); } map_a SEC(".maps"); -__u32 user_data, key_serial, target_pid; +__u32 user_data, target_pid; +__s32 key_serial; __u64 flags, task_storage_val, cgroup_id; -struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym; void bpf_key_put(struct bpf_key *key) __ksym; void bpf_rcu_read_lock(void) __ksym; void bpf_rcu_read_unlock(void) __ksym; diff --git a/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c b/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c new file mode 100644 index 000000000000..405adbe5e8b0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include +#include "bpf_experimental.h" + +char _license[] SEC("license") = "GPL"; + +pid_t target_pid = 0; + +char xattr_value[64]; +static const char expected_value_a[] = "bpf_selftest_value_a"; +static const char expected_value_b[] = "bpf_selftest_value_b"; +bool found_value_a; +bool found_value_b; + +SEC("lsm.s/file_open") +int BPF_PROG(test_file_open) +{ + u64 cgrp_id = bpf_get_current_cgroup_id(); + struct cgroup_subsys_state *css, *tmp; + struct bpf_dynptr value_ptr; + struct cgroup *cgrp; + + if ((bpf_get_current_pid_tgid() >> 32) != target_pid) + return 0; + + bpf_rcu_read_lock(); + cgrp = bpf_cgroup_from_id(cgrp_id); + if (!cgrp) { + bpf_rcu_read_unlock(); + return 0; + } + + css = &cgrp->self; + bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr); + bpf_for_each(css, tmp, css, BPF_CGROUP_ITER_ANCESTORS_UP) { + int ret; + + ret = bpf_cgroup_read_xattr(tmp->cgroup, "user.bpf_test", + &value_ptr); + if (ret < 0) + continue; + + if (ret == sizeof(expected_value_a) && + !bpf_strncmp(xattr_value, sizeof(expected_value_a), expected_value_a)) + found_value_a = true; + if (ret == sizeof(expected_value_b) && + !bpf_strncmp(xattr_value, sizeof(expected_value_b), expected_value_b)) + found_value_b = true; + } + + bpf_rcu_read_unlock(); + bpf_cgroup_release(cgrp); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/security_bpf_map.c b/tools/testing/selftests/bpf/progs/security_bpf_map.c new file mode 100644 index 000000000000..7216b3450e96 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/security_bpf_map.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +#define EPERM 1 /* Operation not permitted */ + +/* From include/linux/mm.h. */ +#define FMODE_WRITE 0x2 + +struct map; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 1); +} prot_status_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 3); +} prot_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 3); +} not_prot_map SEC(".maps"); + +SEC("fmod_ret/security_bpf_map") +int BPF_PROG(fmod_bpf_map, struct bpf_map *map, int fmode) +{ + __u32 key = 0; + __u32 *status_ptr = bpf_map_lookup_elem(&prot_status_map, &key); + + if (!status_ptr || !*status_ptr) + return 0; + + if (map == &prot_map) { + /* Allow read-only access */ + if (fmode & FMODE_WRITE) + return -EPERM; + } + + return 0; +} + +/* + * This program keeps references to maps. This is needed to prevent + * optimizing them out. + */ +SEC("fentry/bpf_fentry_test1") +int BPF_PROG(fentry_dummy1, int a) +{ + __u32 key = 0; + __u32 val1 = a; + __u32 val2 = a + 1; + + bpf_map_update_elem(&prot_map, &key, &val1, BPF_ANY); + bpf_map_update_elem(¬_prot_map, &key, &val2, BPF_ANY); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/set_global_vars.c b/tools/testing/selftests/bpf/progs/set_global_vars.c index 90f5656c3991..ebaef28b2cb3 100644 --- a/tools/testing/selftests/bpf/progs/set_global_vars.c +++ b/tools/testing/selftests/bpf/progs/set_global_vars.c @@ -7,22 +7,30 @@ char _license[] SEC("license") = "GPL"; -enum Enum { EA1 = 0, EA2 = 11 }; +typedef __s32 s32; +typedef s32 i32; +typedef __u8 u8; + +enum Enum { EA1 = 0, EA2 = 11, EA3 = 10 }; enum Enumu64 {EB1 = 0llu, EB2 = 12llu }; enum Enums64 { EC1 = 0ll, EC2 = 13ll }; const volatile __s64 var_s64 = -1; const volatile __u64 var_u64 = 0; -const volatile __s32 var_s32 = -1; +const volatile i32 var_s32 = -1; const volatile __u32 var_u32 = 0; const volatile __s16 var_s16 = -1; const volatile __u16 var_u16 = 0; const volatile __s8 var_s8 = -1; -const volatile __u8 var_u8 = 0; +const volatile u8 var_u8 = 0; const volatile enum Enum var_ea = EA1; const volatile enum Enumu64 var_eb = EB1; const volatile enum Enums64 var_ec = EC1; const volatile bool var_b = false; +const volatile i32 arr[32]; +const volatile enum Enum enum_arr[32]; +const volatile i32 three_d[47][19][17]; +const volatile i32 *ptr_arr[32]; struct Struct { int:16; @@ -35,35 +43,39 @@ struct Struct { volatile struct { const int:1; union { - const volatile __u8 var_u8; + const volatile u8 var_u8[3]; const volatile __s16 filler3; const int:1; + s32 mat[7][5]; } u; }; - } struct2; + } struct2[2][4]; }; const volatile __u32 stru = 0; /* same prefix as below */ -const volatile struct Struct struct1 = {.struct2 = {.u = {.var_u8 = 1}}}; +const volatile struct Struct struct1[3]; +const volatile struct Struct struct11[11][7]; + +struct Struct3 { + struct { + u8 var_u8_l; + }; + struct { + struct { + u8 var_u8_h; + }; + }; +}; + +typedef struct Struct3 Struct3_t; union Union { __u16 var_u16; - struct Struct3 { - struct { - __u8 var_u8_l; - }; - struct { - struct { - __u8 var_u8_h; - }; - }; - } struct3; + Struct3_t struct3; }; const volatile union Union union1 = {.var_u16 = -1}; -char arr[4] = {0}; - SEC("socket") int test_set_globals(void *ctx) { @@ -81,8 +93,14 @@ int test_set_globals(void *ctx) a = var_eb; a = var_ec; a = var_b; - a = struct1.struct2.u.var_u8; + a = struct1[2].struct2[1][2].u.var_u8[2]; a = union1.var_u16; + a = arr[3]; + a = arr[EA2]; + a = enum_arr[EC2]; + a = three_d[31][7][EA2]; + a = struct1[2].struct2[1][2].u.mat[5][3]; + a = struct11[7][5].struct2[0][1].u.mat[3][0]; return a; } diff --git a/tools/testing/selftests/bpf/progs/sock_iter_batch.c b/tools/testing/selftests/bpf/progs/sock_iter_batch.c index 8f483337e103..77966ded5467 100644 --- a/tools/testing/selftests/bpf/progs/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/progs/sock_iter_batch.c @@ -23,6 +23,7 @@ static bool ipv4_addr_loopback(__be32 a) } volatile const unsigned int sf; +volatile const unsigned int ss; volatile const __u16 ports[2]; unsigned int bucket[2]; @@ -42,16 +43,18 @@ int iter_tcp_soreuse(struct bpf_iter__tcp *ctx) sock_cookie = bpf_get_socket_cookie(sk); sk = bpf_core_cast(sk, struct sock); if (sk->sk_family != sf || - sk->sk_state != TCP_LISTEN || - sk->sk_family == AF_INET6 ? + (ss && sk->sk_state != ss) || + (sk->sk_family == AF_INET6 ? !ipv6_addr_loopback(&sk->sk_v6_rcv_saddr) : - !ipv4_addr_loopback(sk->sk_rcv_saddr)) + !ipv4_addr_loopback(sk->sk_rcv_saddr))) return 0; if (sk->sk_num == ports[0]) idx = 0; else if (sk->sk_num == ports[1]) idx = 1; + else if (!ports[0] && !ports[1]) + idx = 0; else return 0; @@ -67,6 +70,27 @@ int iter_tcp_soreuse(struct bpf_iter__tcp *ctx) return 0; } +volatile const __u64 destroy_cookie; + +SEC("iter/tcp") +int iter_tcp_destroy(struct bpf_iter__tcp *ctx) +{ + struct sock_common *sk_common = (struct sock_common *)ctx->sk_common; + __u64 sock_cookie; + + if (!sk_common) + return 0; + + sock_cookie = bpf_get_socket_cookie(sk_common); + if (sock_cookie != destroy_cookie) + return 0; + + bpf_sock_destroy(sk_common); + bpf_seq_write(ctx->meta->seq, &sock_cookie, sizeof(sock_cookie)); + + return 0; +} + #define udp_sk(ptr) container_of(ptr, struct udp_sock, inet.sk) SEC("iter/udp") @@ -83,15 +107,17 @@ int iter_udp_soreuse(struct bpf_iter__udp *ctx) sock_cookie = bpf_get_socket_cookie(sk); sk = bpf_core_cast(sk, struct sock); if (sk->sk_family != sf || - sk->sk_family == AF_INET6 ? + (sk->sk_family == AF_INET6 ? !ipv6_addr_loopback(&sk->sk_v6_rcv_saddr) : - !ipv4_addr_loopback(sk->sk_rcv_saddr)) + !ipv4_addr_loopback(sk->sk_rcv_saddr))) return 0; if (sk->sk_num == ports[0]) idx = 0; else if (sk->sk_num == ports[1]) idx = 1; + else if (!ports[0] && !ports[1]) + idx = 0; else return 0; diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c new file mode 100644 index 000000000000..35790897dc87 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/stream.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include +#include "bpf_misc.h" +#include "bpf_experimental.h" + +struct arr_elem { + struct bpf_res_spin_lock lock; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct arr_elem); +} arrmap SEC(".maps"); + +#define ENOSPC 28 +#define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +int size; + +SEC("syscall") +__success __retval(0) +int stream_exhaust(void *ctx) +{ + /* Use global variable for loop convergence. */ + size = 0; + bpf_repeat(BPF_MAX_LOOPS) { + if (bpf_stream_printk(BPF_STDOUT, _STR) == -ENOSPC && size == 99954) + return 0; + size += sizeof(_STR) - 1; + } + return 1; +} + +SEC("syscall") +__success __retval(0) +int stream_cond_break(void *ctx) +{ + while (can_loop) + ; + return 0; +} + +SEC("syscall") +__success __retval(0) +int stream_deadlock(void *ctx) +{ + struct bpf_res_spin_lock *lock, *nlock; + + lock = bpf_map_lookup_elem(&arrmap, &(int){0}); + if (!lock) + return 1; + nlock = bpf_map_lookup_elem(&arrmap, &(int){0}); + if (!nlock) + return 1; + if (bpf_res_spin_lock(lock)) + return 1; + if (bpf_res_spin_lock(nlock)) { + bpf_res_spin_unlock(lock); + return 0; + } + bpf_res_spin_unlock(nlock); + bpf_res_spin_unlock(lock); + return 1; +} + +SEC("syscall") +__success __retval(0) +int stream_syscall(void *ctx) +{ + bpf_stream_printk(BPF_STDOUT, "foo"); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/stream_fail.c b/tools/testing/selftests/bpf/progs/stream_fail.c new file mode 100644 index 000000000000..b4a0d0cc8ec8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/stream_fail.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include +#include +#include "bpf_misc.h" + +SEC("syscall") +__failure __msg("Possibly NULL pointer passed") +int stream_vprintk_null_arg(void *ctx) +{ + bpf_stream_vprintk(BPF_STDOUT, "", NULL, 0, NULL); + return 0; +} + +SEC("syscall") +__failure __msg("R3 type=scalar expected=") +int stream_vprintk_scalar_arg(void *ctx) +{ + bpf_stream_vprintk(BPF_STDOUT, "", (void *)46, 0, NULL); + return 0; +} + +SEC("syscall") +__failure __msg("arg#1 doesn't point to a const string") +int stream_vprintk_string_arg(void *ctx) +{ + bpf_stream_vprintk(BPF_STDOUT, ctx, NULL, 0, NULL); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c new file mode 100644 index 000000000000..53af438bd998 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Red Hat, Inc.*/ +#include "vmlinux.h" +#include +#include +#include "bpf_misc.h" +#include "errno.h" + +char *user_ptr = (char *)1; +char *invalid_kern_ptr = (char *)-1; + +/* + * When passing userspace pointers, the error code differs based on arch: + * -ERANGE on arches with non-overlapping address spaces + * -EFAULT on other arches + */ +#if defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_loongarch) || \ + defined(__TARGET_ARCH_powerpc) || defined(__TARGET_ARCH_x86) +#define USER_PTR_ERR -ERANGE +#else +#define USER_PTR_ERR -EFAULT +#endif + +/* + * On s390, __get_kernel_nofault (used in string kfuncs) returns 0 for NULL and + * user_ptr (instead of causing an exception) so the below two groups of tests + * are not applicable. + */ +#ifndef __TARGET_ARCH_s390 + +/* Passing NULL to string kfuncs (treated as a userspace ptr) */ +SEC("syscall") __retval(USER_PTR_ERR) int test_strcmp_null1(void *ctx) { return bpf_strcmp(NULL, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strcmp_null2(void *ctx) { return bpf_strcmp("hello", NULL); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strchr_null(void *ctx) { return bpf_strchr(NULL, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strchrnul_null(void *ctx) { return bpf_strchrnul(NULL, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strnchr_null(void *ctx) { return bpf_strnchr(NULL, 1, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strrchr_null(void *ctx) { return bpf_strrchr(NULL, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strlen_null(void *ctx) { return bpf_strlen(NULL); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strnlen_null(void *ctx) { return bpf_strnlen(NULL, 1); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strspn_null1(void *ctx) { return bpf_strspn(NULL, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strspn_null2(void *ctx) { return bpf_strspn("hello", NULL); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strcspn_null1(void *ctx) { return bpf_strcspn(NULL, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strcspn_null2(void *ctx) { return bpf_strcspn("hello", NULL); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strstr_null1(void *ctx) { return bpf_strstr(NULL, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strstr_null2(void *ctx) { return bpf_strstr("hello", NULL); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strnstr_null1(void *ctx) { return bpf_strnstr(NULL, "hello", 1); } +SEC("syscall") __retval(USER_PTR_ERR)int test_strnstr_null2(void *ctx) { return bpf_strnstr("hello", NULL, 1); } + +/* Passing userspace ptr to string kfuncs */ +SEC("syscall") __retval(USER_PTR_ERR) int test_strcmp_user_ptr1(void *ctx) { return bpf_strcmp(user_ptr, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strcmp_user_ptr2(void *ctx) { return bpf_strcmp("hello", user_ptr); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strchr_user_ptr(void *ctx) { return bpf_strchr(user_ptr, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strchrnul_user_ptr(void *ctx) { return bpf_strchrnul(user_ptr, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strnchr_user_ptr(void *ctx) { return bpf_strnchr(user_ptr, 1, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strrchr_user_ptr(void *ctx) { return bpf_strrchr(user_ptr, 'a'); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strlen_user_ptr(void *ctx) { return bpf_strlen(user_ptr); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strnlen_user_ptr(void *ctx) { return bpf_strnlen(user_ptr, 1); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strspn_user_ptr1(void *ctx) { return bpf_strspn(user_ptr, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strspn_user_ptr2(void *ctx) { return bpf_strspn("hello", user_ptr); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strcspn_user_ptr1(void *ctx) { return bpf_strcspn(user_ptr, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strcspn_user_ptr2(void *ctx) { return bpf_strcspn("hello", user_ptr); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strstr_user_ptr1(void *ctx) { return bpf_strstr(user_ptr, "hello"); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strstr_user_ptr2(void *ctx) { return bpf_strstr("hello", user_ptr); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strnstr_user_ptr1(void *ctx) { return bpf_strnstr(user_ptr, "hello", 1); } +SEC("syscall") __retval(USER_PTR_ERR) int test_strnstr_user_ptr2(void *ctx) { return bpf_strnstr("hello", user_ptr, 1); } + +#endif /* __TARGET_ARCH_s390 */ + +/* Passing invalid kernel ptr to string kfuncs should always return -EFAULT */ +SEC("syscall") __retval(-EFAULT) int test_strcmp_pagefault1(void *ctx) { return bpf_strcmp(invalid_kern_ptr, "hello"); } +SEC("syscall") __retval(-EFAULT) int test_strcmp_pagefault2(void *ctx) { return bpf_strcmp("hello", invalid_kern_ptr); } +SEC("syscall") __retval(-EFAULT) int test_strchr_pagefault(void *ctx) { return bpf_strchr(invalid_kern_ptr, 'a'); } +SEC("syscall") __retval(-EFAULT) int test_strchrnul_pagefault(void *ctx) { return bpf_strchrnul(invalid_kern_ptr, 'a'); } +SEC("syscall") __retval(-EFAULT) int test_strnchr_pagefault(void *ctx) { return bpf_strnchr(invalid_kern_ptr, 1, 'a'); } +SEC("syscall") __retval(-EFAULT) int test_strrchr_pagefault(void *ctx) { return bpf_strrchr(invalid_kern_ptr, 'a'); } +SEC("syscall") __retval(-EFAULT) int test_strlen_pagefault(void *ctx) { return bpf_strlen(invalid_kern_ptr); } +SEC("syscall") __retval(-EFAULT) int test_strnlen_pagefault(void *ctx) { return bpf_strnlen(invalid_kern_ptr, 1); } +SEC("syscall") __retval(-EFAULT) int test_strspn_pagefault1(void *ctx) { return bpf_strspn(invalid_kern_ptr, "hello"); } +SEC("syscall") __retval(-EFAULT) int test_strspn_pagefault2(void *ctx) { return bpf_strspn("hello", invalid_kern_ptr); } +SEC("syscall") __retval(-EFAULT) int test_strcspn_pagefault1(void *ctx) { return bpf_strcspn(invalid_kern_ptr, "hello"); } +SEC("syscall") __retval(-EFAULT) int test_strcspn_pagefault2(void *ctx) { return bpf_strcspn("hello", invalid_kern_ptr); } +SEC("syscall") __retval(-EFAULT) int test_strstr_pagefault1(void *ctx) { return bpf_strstr(invalid_kern_ptr, "hello"); } +SEC("syscall") __retval(-EFAULT) int test_strstr_pagefault2(void *ctx) { return bpf_strstr("hello", invalid_kern_ptr); } +SEC("syscall") __retval(-EFAULT) int test_strnstr_pagefault1(void *ctx) { return bpf_strnstr(invalid_kern_ptr, "hello", 1); } +SEC("syscall") __retval(-EFAULT) int test_strnstr_pagefault2(void *ctx) { return bpf_strnstr("hello", invalid_kern_ptr, 1); } + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c new file mode 100644 index 000000000000..89fb4669b0e9 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Red Hat, Inc.*/ +#include "vmlinux.h" +#include +#include + +char long_str[XATTR_SIZE_MAX + 1]; + +SEC("syscall") int test_strcmp_too_long(void *ctx) { return bpf_strcmp(long_str, long_str); } +SEC("syscall") int test_strchr_too_long(void *ctx) { return bpf_strchr(long_str, 'b'); } +SEC("syscall") int test_strchrnul_too_long(void *ctx) { return bpf_strchrnul(long_str, 'b'); } +SEC("syscall") int test_strnchr_too_long(void *ctx) { return bpf_strnchr(long_str, sizeof(long_str), 'b'); } +SEC("syscall") int test_strrchr_too_long(void *ctx) { return bpf_strrchr(long_str, 'b'); } +SEC("syscall") int test_strlen_too_long(void *ctx) { return bpf_strlen(long_str); } +SEC("syscall") int test_strnlen_too_long(void *ctx) { return bpf_strnlen(long_str, sizeof(long_str)); } +SEC("syscall") int test_strspn_str_too_long(void *ctx) { return bpf_strspn(long_str, "a"); } +SEC("syscall") int test_strspn_accept_too_long(void *ctx) { return bpf_strspn("b", long_str); } +SEC("syscall") int test_strcspn_str_too_long(void *ctx) { return bpf_strcspn(long_str, "b"); } +SEC("syscall") int test_strcspn_reject_too_long(void *ctx) { return bpf_strcspn("b", long_str); } +SEC("syscall") int test_strstr_too_long(void *ctx) { return bpf_strstr(long_str, "hello"); } +SEC("syscall") int test_strnstr_too_long(void *ctx) { return bpf_strnstr(long_str, "hello", sizeof(long_str)); } + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c new file mode 100644 index 000000000000..a47690174e0e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Red Hat, Inc.*/ +#include "vmlinux.h" +#include +#include "bpf_misc.h" +#include "errno.h" + +char str[] = "hello world"; + +#define __test(retval) SEC("syscall") __success __retval(retval) + +/* Functional tests */ +__test(0) int test_strcmp_eq(void *ctx) { return bpf_strcmp(str, "hello world"); } +__test(1) int test_strcmp_neq(void *ctx) { return bpf_strcmp(str, "hello"); } +__test(1) int test_strchr_found(void *ctx) { return bpf_strchr(str, 'e'); } +__test(11) int test_strchr_null(void *ctx) { return bpf_strchr(str, '\0'); } +__test(-ENOENT) int test_strchr_notfound(void *ctx) { return bpf_strchr(str, 'x'); } +__test(1) int test_strchrnul_found(void *ctx) { return bpf_strchrnul(str, 'e'); } +__test(11) int test_strchrnul_notfound(void *ctx) { return bpf_strchrnul(str, 'x'); } +__test(1) int test_strnchr_found(void *ctx) { return bpf_strnchr(str, 5, 'e'); } +__test(11) int test_strnchr_null(void *ctx) { return bpf_strnchr(str, 12, '\0'); } +__test(-ENOENT) int test_strnchr_notfound(void *ctx) { return bpf_strnchr(str, 5, 'w'); } +__test(9) int test_strrchr_found(void *ctx) { return bpf_strrchr(str, 'l'); } +__test(11) int test_strrchr_null(void *ctx) { return bpf_strrchr(str, '\0'); } +__test(-ENOENT) int test_strrchr_notfound(void *ctx) { return bpf_strrchr(str, 'x'); } +__test(11) int test_strlen(void *ctx) { return bpf_strlen(str); } +__test(11) int test_strnlen(void *ctx) { return bpf_strnlen(str, 12); } +__test(5) int test_strspn(void *ctx) { return bpf_strspn(str, "ehlo"); } +__test(2) int test_strcspn(void *ctx) { return bpf_strcspn(str, "lo"); } +__test(6) int test_strstr_found(void *ctx) { return bpf_strstr(str, "world"); } +__test(-ENOENT) int test_strstr_notfound(void *ctx) { return bpf_strstr(str, "hi"); } +__test(0) int test_strstr_empty(void *ctx) { return bpf_strstr(str, ""); } +__test(0) int test_strnstr_found1(void *ctx) { return bpf_strnstr("", "", 0); } +__test(0) int test_strnstr_found2(void *ctx) { return bpf_strnstr(str, "hello", 5); } +__test(0) int test_strnstr_found3(void *ctx) { return bpf_strnstr(str, "hello", 6); } +__test(-ENOENT) int test_strnstr_notfound1(void *ctx) { return bpf_strnstr(str, "hi", 10); } +__test(-ENOENT) int test_strnstr_notfound2(void *ctx) { return bpf_strnstr(str, "hello", 4); } +__test(-ENOENT) int test_strnstr_notfound3(void *ctx) { return bpf_strnstr("", "a", 0); } +__test(0) int test_strnstr_empty(void *ctx) { return bpf_strnstr(str, "", 1); } + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/struct_ops_private_stack.c b/tools/testing/selftests/bpf/progs/struct_ops_private_stack.c index 0e4d2ff63ab8..dbe646013811 100644 --- a/tools/testing/selftests/bpf/progs/struct_ops_private_stack.c +++ b/tools/testing/selftests/bpf/progs/struct_ops_private_stack.c @@ -7,7 +7,7 @@ char _license[] SEC("license") = "GPL"; -#if defined(__TARGET_ARCH_x86) +#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) bool skip __attribute((__section__(".data"))) = false; #else bool skip = true; diff --git a/tools/testing/selftests/bpf/progs/struct_ops_private_stack_fail.c b/tools/testing/selftests/bpf/progs/struct_ops_private_stack_fail.c index 58d5d8dc2235..3d89ad7cbe2a 100644 --- a/tools/testing/selftests/bpf/progs/struct_ops_private_stack_fail.c +++ b/tools/testing/selftests/bpf/progs/struct_ops_private_stack_fail.c @@ -7,7 +7,7 @@ char _license[] SEC("license") = "GPL"; -#if defined(__TARGET_ARCH_x86) +#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) bool skip __attribute((__section__(".data"))) = false; #else bool skip = true; diff --git a/tools/testing/selftests/bpf/progs/struct_ops_private_stack_recur.c b/tools/testing/selftests/bpf/progs/struct_ops_private_stack_recur.c index 31e58389bb8b..b1f6d7e5a8e5 100644 --- a/tools/testing/selftests/bpf/progs/struct_ops_private_stack_recur.c +++ b/tools/testing/selftests/bpf/progs/struct_ops_private_stack_recur.c @@ -7,7 +7,7 @@ char _license[] SEC("license") = "GPL"; -#if defined(__TARGET_ARCH_x86) +#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) bool skip __attribute((__section__(".data"))) = false; #else bool skip = true; diff --git a/tools/testing/selftests/bpf/progs/test_global_map_resize.c b/tools/testing/selftests/bpf/progs/test_global_map_resize.c index a3f220ba7025..ee65bad0436d 100644 --- a/tools/testing/selftests/bpf/progs/test_global_map_resize.c +++ b/tools/testing/selftests/bpf/progs/test_global_map_resize.c @@ -32,6 +32,16 @@ int my_int_last SEC(".data.array_not_last"); int percpu_arr[1] SEC(".data.percpu_arr"); +/* at least one extern is included, to ensure that a specific + * regression is tested whereby resizing resulted in a free-after-use + * bug after type information is invalidated by the resize operation. + * + * There isn't a particularly good API to test for this specific condition, + * but by having externs for the resizing tests it will cover this path. + */ +extern int LINUX_KERNEL_VERSION __kconfig; +long version_sink; + SEC("tp/syscalls/sys_enter_getpid") int bss_array_sum(void *ctx) { @@ -44,6 +54,9 @@ int bss_array_sum(void *ctx) for (size_t i = 0; i < bss_array_len; ++i) sum += array[i]; + /* see above; ensure this is not optimized out */ + version_sink = LINUX_KERNEL_VERSION; + return 0; } @@ -59,6 +72,9 @@ int data_array_sum(void *ctx) for (size_t i = 0; i < data_array_len; ++i) sum += my_array[i]; + /* see above; ensure this is not optimized out */ + version_sink = LINUX_KERNEL_VERSION; + return 0; } diff --git a/tools/testing/selftests/bpf/progs/test_lookup_key.c b/tools/testing/selftests/bpf/progs/test_lookup_key.c index cdbbb12f1491..1f7e1e59b073 100644 --- a/tools/testing/selftests/bpf/progs/test_lookup_key.c +++ b/tools/testing/selftests/bpf/progs/test_lookup_key.c @@ -14,11 +14,11 @@ char _license[] SEC("license") = "GPL"; __u32 monitored_pid; -__u32 key_serial; +__s32 key_serial; __u32 key_id; __u64 flags; -extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym; extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym; extern void bpf_key_put(struct bpf_key *key) __ksym; diff --git a/tools/testing/selftests/bpf/progs/test_ringbuf_write.c b/tools/testing/selftests/bpf/progs/test_ringbuf_write.c index 350513c0e4c9..f063a0013f85 100644 --- a/tools/testing/selftests/bpf/progs/test_ringbuf_write.c +++ b/tools/testing/selftests/bpf/progs/test_ringbuf_write.c @@ -26,11 +26,11 @@ int test_ringbuf_write(void *ctx) if (cur_pid != pid) return 0; - sample1 = bpf_ringbuf_reserve(&ringbuf, 0x3000, 0); + sample1 = bpf_ringbuf_reserve(&ringbuf, 0x30000, 0); if (!sample1) return 0; /* first one can pass */ - sample2 = bpf_ringbuf_reserve(&ringbuf, 0x3000, 0); + sample2 = bpf_ringbuf_reserve(&ringbuf, 0x30000, 0); if (!sample2) { bpf_ringbuf_discard(sample1, 0); __sync_fetch_and_add(&discarded, 1); diff --git a/tools/testing/selftests/bpf/progs/test_sig_in_xattr.c b/tools/testing/selftests/bpf/progs/test_sig_in_xattr.c index 8ef6b39335b6..34b30e2603f0 100644 --- a/tools/testing/selftests/bpf/progs/test_sig_in_xattr.c +++ b/tools/testing/selftests/bpf/progs/test_sig_in_xattr.c @@ -40,7 +40,7 @@ char digest[MAGIC_SIZE + SIZEOF_STRUCT_FSVERITY_DIGEST + SHA256_DIGEST_SIZE]; __u32 monitored_pid; char sig[MAX_SIG_SIZE]; __u32 sig_size; -__u32 user_keyring_serial; +__s32 user_keyring_serial; SEC("lsm.s/file_open") int BPF_PROG(test_file_open, struct file *f) diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_change_tail.c b/tools/testing/selftests/bpf/progs/test_sockmap_change_tail.c index 2796dd8545eb..1c7941a4ad00 100644 --- a/tools/testing/selftests/bpf/progs/test_sockmap_change_tail.c +++ b/tools/testing/selftests/bpf/progs/test_sockmap_change_tail.c @@ -1,8 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2024 ByteDance */ -#include +#include "vmlinux.h" #include +#ifndef PAGE_SIZE +#define PAGE_SIZE __PAGE_SIZE +#endif +#define BPF_SKB_MAX_LEN (PAGE_SIZE << 2) + struct { __uint(type, BPF_MAP_TYPE_SOCKMAP); __uint(max_entries, 1); @@ -31,7 +36,7 @@ int prog_skb_verdict(struct __sk_buff *skb) change_tail_ret = bpf_skb_change_tail(skb, skb->len + 1, 0); return SK_PASS; } else if (data[0] == 'E') { /* Error */ - change_tail_ret = bpf_skb_change_tail(skb, 65535, 0); + change_tail_ret = bpf_skb_change_tail(skb, BPF_SKB_MAX_LEN, 0); return SK_PASS; } return SK_PASS; diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c index 8bdb9987c0c7..83df4919c224 100644 --- a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c +++ b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c @@ -7,6 +7,8 @@ int cork_byte; int push_start; int push_end; int apply_bytes; +int pop_start; +int pop_end; struct { __uint(type, BPF_MAP_TYPE_SOCKMAP); @@ -22,6 +24,8 @@ int prog_sk_policy(struct sk_msg_md *msg) bpf_msg_cork_bytes(msg, cork_byte); if (push_start > 0 && push_end > 0) bpf_msg_push_data(msg, push_start, push_end, 0); + if (pop_start >= 0 && pop_end > 0) + bpf_msg_pop_data(msg, pop_start, pop_end, 0); return SK_PASS; } diff --git a/tools/testing/selftests/bpf/progs/test_tc_change_tail.c b/tools/testing/selftests/bpf/progs/test_tc_change_tail.c index 28edafe803f0..fcba8299f0bc 100644 --- a/tools/testing/selftests/bpf/progs/test_tc_change_tail.c +++ b/tools/testing/selftests/bpf/progs/test_tc_change_tail.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -#include +#include "vmlinux.h" #include -#include -#include -#include -#include -#include + +#ifndef PAGE_SIZE +#define PAGE_SIZE __PAGE_SIZE +#endif +#define BPF_SKB_MAX_LEN (PAGE_SIZE << 2) long change_tail_ret = 1; @@ -94,7 +94,7 @@ int change_tail(struct __sk_buff *skb) bpf_skb_change_tail(skb, len, 0); return TCX_PASS; } else if (payload[0] == 'E') { /* Error */ - change_tail_ret = bpf_skb_change_tail(skb, 65535, 0); + change_tail_ret = bpf_skb_change_tail(skb, BPF_SKB_MAX_LEN, 0); return TCX_PASS; } else if (payload[0] == 'Z') { /* Zero */ change_tail_ret = bpf_skb_change_tail(skb, 0, 0); diff --git a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c index e96d09e11115..ff8d755548b9 100644 --- a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c +++ b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c @@ -17,7 +17,7 @@ #define MAX_SIG_SIZE 1024 __u32 monitored_pid; -__u32 user_keyring_serial; +__s32 user_keyring_serial; __u64 system_keyring_id; struct data { diff --git a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c index dc74d8cf9e3f..5904f45cfbc4 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c @@ -19,7 +19,9 @@ int _xdp_adjust_tail_grow(struct xdp_md *xdp) /* Data length determine test case */ if (data_len == 54) { /* sizeof(pkt_v4) */ - offset = 4096; /* test too large offset */ + offset = 4096; /* test too large offset, 4k page size */ + } else if (data_len == 53) { /* sizeof(pkt_v4) - 1 */ + offset = 65536; /* test too large offset, 64k page size */ } else if (data_len == 74) { /* sizeof(pkt_v6) */ offset = 40; } else if (data_len == 64) { @@ -31,6 +33,10 @@ int _xdp_adjust_tail_grow(struct xdp_md *xdp) offset = 10; } else if (data_len == 9001) { offset = 4096; + } else if (data_len == 90000) { + offset = 10; /* test a small offset, 64k page size */ + } else if (data_len == 90001) { + offset = 65536; /* test too large offset, 64k page size */ } else { return XDP_ABORTED; /* No matching test */ } diff --git a/tools/testing/selftests/bpf/progs/tracing_failure.c b/tools/testing/selftests/bpf/progs/tracing_failure.c index d41665d2ec8c..65e485c4468c 100644 --- a/tools/testing/selftests/bpf/progs/tracing_failure.c +++ b/tools/testing/selftests/bpf/progs/tracing_failure.c @@ -18,3 +18,15 @@ int BPF_PROG(test_spin_unlock, struct bpf_spin_lock *lock) { return 0; } + +SEC("?fentry/__rcu_read_lock") +int BPF_PROG(tracing_deny) +{ + return 0; +} + +SEC("?fexit/do_exit") +int BPF_PROG(fexit_noreturns) +{ + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/verifier_and.c b/tools/testing/selftests/bpf/progs/verifier_and.c index e97e518516b6..2b4fdca162be 100644 --- a/tools/testing/selftests/bpf/progs/verifier_and.c +++ b/tools/testing/selftests/bpf/progs/verifier_and.c @@ -85,8 +85,14 @@ l0_%=: r0 = r0; \ SEC("socket") __description("check known subreg with unknown reg") -__success __failure_unpriv __msg_unpriv("R1 !read_ok") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if w0 < 0x1 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R1 !read_ok'` */ +__xlated_unpriv("goto pc-1") /* `r1 = *(u32*)(r1 + 512)`, sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void known_subreg_with_unknown_reg(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c index 67509c5d3982..7f4827eede3c 100644 --- a/tools/testing/selftests/bpf/progs/verifier_arena.c +++ b/tools/testing/selftests/bpf/progs/verifier_arena.c @@ -3,6 +3,7 @@ #define BPF_NO_KFUNC_PROTOTYPES #include +#include #include #include #include "bpf_misc.h" @@ -114,6 +115,111 @@ int basic_alloc3(void *ctx) return 0; } +SEC("syscall") +__success __retval(0) +int basic_reserve1(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + char __arena *page; + int ret; + + page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); + if (!page) + return 1; + + page += __PAGE_SIZE; + + /* Reserve the second page */ + ret = bpf_arena_reserve_pages(&arena, page, 1); + if (ret) + return 2; + + /* Try to explicitly allocate the reserved page. */ + page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); + if (page) + return 3; + + /* Try to implicitly allocate the page (since there's only 2 of them). */ + page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); + if (page) + return 4; +#endif + return 0; +} + +SEC("syscall") +__success __retval(0) +int basic_reserve2(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + char __arena *page; + int ret; + + page = arena_base(&arena); + ret = bpf_arena_reserve_pages(&arena, page, 1); + if (ret) + return 1; + + page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); + if ((u64)page) + return 2; +#endif + return 0; +} + +/* Reserve the same page twice, should return -EBUSY. */ +SEC("syscall") +__success __retval(0) +int reserve_twice(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + char __arena *page; + int ret; + + page = arena_base(&arena); + + ret = bpf_arena_reserve_pages(&arena, page, 1); + if (ret) + return 1; + + ret = bpf_arena_reserve_pages(&arena, page, 1); + if (ret != -EBUSY) + return 2; +#endif + return 0; +} + +/* Try to reserve past the end of the arena. */ +SEC("syscall") +__success __retval(0) +int reserve_invalid_region(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + char __arena *page; + int ret; + + /* Try a NULL pointer. */ + ret = bpf_arena_reserve_pages(&arena, NULL, 3); + if (ret != -EINVAL) + return 1; + + page = arena_base(&arena); + + ret = bpf_arena_reserve_pages(&arena, page, 3); + if (ret != -EINVAL) + return 2; + + ret = bpf_arena_reserve_pages(&arena, page, 4096); + if (ret != -EINVAL) + return 3; + + ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1); + if (ret != -EINVAL) + return 4; +#endif + return 0; +} + SEC("iter.s/bpf_map") __success __log_level(2) int iter_maps1(struct bpf_iter__bpf_map *ctx) diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_large.c b/tools/testing/selftests/bpf/progs/verifier_arena_large.c index f94f30cf1bb8..9dbdf123542d 100644 --- a/tools/testing/selftests/bpf/progs/verifier_arena_large.c +++ b/tools/testing/selftests/bpf/progs/verifier_arena_large.c @@ -67,6 +67,104 @@ int big_alloc1(void *ctx) return 0; } +/* Try to access a reserved page. Behavior should be identical with accessing unallocated pages. */ +SEC("syscall") +__success __retval(0) +int access_reserved(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + volatile char __arena *page; + char __arena *base; + const size_t len = 4; + int ret, i; + + /* Get a separate region of the arena. */ + page = base = arena_base(&arena) + 16384 * PAGE_SIZE; + + ret = bpf_arena_reserve_pages(&arena, base, len); + if (ret) + return 1; + + /* Try to dirty reserved memory. */ + for (i = 0; i < len && can_loop; i++) + *page = 0x5a; + + for (i = 0; i < len && can_loop; i++) { + page = (volatile char __arena *)(base + i * PAGE_SIZE); + + /* + * Error out in case either the write went through, + * or the address has random garbage. + */ + if (*page == 0x5a) + return 2 + 2 * i; + + if (*page) + return 2 + 2 * i + 1; + } +#endif + return 0; +} + +/* Try to allocate a region overlapping with a reservation. */ +SEC("syscall") +__success __retval(0) +int request_partially_reserved(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + volatile char __arena *page; + char __arena *base; + int ret; + + /* Add an arbitrary page offset. */ + page = base = arena_base(&arena) + 4096 * __PAGE_SIZE; + + ret = bpf_arena_reserve_pages(&arena, base + 3 * __PAGE_SIZE, 4); + if (ret) + return 1; + + page = bpf_arena_alloc_pages(&arena, base, 5, NUMA_NO_NODE, 0); + if ((u64)page != 0ULL) + return 2; +#endif + return 0; +} + +SEC("syscall") +__success __retval(0) +int free_reserved(void *ctx) +{ +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + char __arena *addr; + char __arena *page; + int ret; + + /* Add an arbitrary page offset. */ + addr = arena_base(&arena) + 32768 * __PAGE_SIZE; + + page = bpf_arena_alloc_pages(&arena, addr, 2, NUMA_NO_NODE, 0); + if (!page) + return 1; + + ret = bpf_arena_reserve_pages(&arena, addr + 2 * __PAGE_SIZE, 2); + if (ret) + return 2; + + /* + * Reserved and allocated pages should be interchangeable for + * bpf_arena_free_pages(). Free a reserved and an allocated + * page with a single call. + */ + bpf_arena_free_pages(&arena, addr + __PAGE_SIZE , 2); + + /* The free call above should have succeeded, so this allocation should too. */ + page = bpf_arena_alloc_pages(&arena, addr + __PAGE_SIZE, 2, NUMA_NO_NODE, 0); + if (!page) + return 3; +#endif + return 0; +} + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) #define PAGE_CNT 100 __u8 __arena * __arena page[PAGE_CNT]; /* occupies the first page */ diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c index 0eb33bb801b5..87a2c60d86e6 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bounds.c +++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c @@ -2,6 +2,7 @@ /* Converted from tools/testing/selftests/bpf/verifier/bounds.c */ #include +#include <../../../include/linux/filter.h> #include #include "bpf_misc.h" @@ -620,8 +621,14 @@ l1_%=: exit; \ SEC("socket") __description("bounds check mixed 32bit and 64bit arithmetic. test1") -__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("exit") +#endif __naked void _32bit_and_64bit_arithmetic_test1(void) { asm volatile (" \ @@ -643,8 +650,14 @@ l1_%=: exit; \ SEC("socket") __description("bounds check mixed 32bit and 64bit arithmetic. test2") -__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("exit") +#endif __naked void _32bit_and_64bit_arithmetic_test2(void) { asm volatile (" \ @@ -691,9 +704,14 @@ l0_%=: r0 = 0; \ SEC("socket") __description("bounds check for reg = 0, reg xor 1") -__success __failure_unpriv -__msg_unpriv("R0 min value is outside of the allowed memory range") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if r1 != 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void reg_0_reg_xor_1(void) { asm volatile (" \ @@ -719,9 +737,14 @@ l1_%=: r0 = 0; \ SEC("socket") __description("bounds check for reg32 = 0, reg32 xor 1") -__success __failure_unpriv -__msg_unpriv("R0 min value is outside of the allowed memory range") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if w1 != 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void reg32_0_reg32_xor_1(void) { asm volatile (" \ @@ -747,9 +770,14 @@ l1_%=: r0 = 0; \ SEC("socket") __description("bounds check for reg = 2, reg xor 3") -__success __failure_unpriv -__msg_unpriv("R0 min value is outside of the allowed memory range") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if r1 > 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void reg_2_reg_xor_3(void) { asm volatile (" \ @@ -829,9 +857,14 @@ l1_%=: r0 = 0; \ SEC("socket") __description("bounds check for reg > 0, reg xor 3") -__success __failure_unpriv -__msg_unpriv("R0 min value is outside of the allowed memory range") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if r1 >= 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void reg_0_reg_xor_3(void) { asm volatile (" \ @@ -858,9 +891,14 @@ l1_%=: r0 = 0; \ SEC("socket") __description("bounds check for reg32 > 0, reg32 xor 3") -__success __failure_unpriv -__msg_unpriv("R0 min value is outside of the allowed memory range") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if w1 >= 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("r0 = 0") +#endif __naked void reg32_0_reg32_xor_3(void) { asm volatile (" \ @@ -1028,7 +1066,7 @@ l0_%=: r0 = 0; \ SEC("xdp") __description("bound check with JMP_JSLT for crossing 64-bit signed boundary") __success __retval(0) -__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */ +__flag(BPF_F_TEST_REG_INVARIANTS) __naked void crossing_64_bit_signed_boundary_2(void) { asm volatile (" \ @@ -1334,4 +1372,300 @@ __naked void mult_sign_ovf(void) __imm(bpf_skb_store_bytes) : __clobber_all); } + +SEC("socket") +__description("64-bit addition, all outcomes overflow") +__success __log_level(2) +__msg("5: (0f) r3 += r3 {{.*}} R3_w=scalar(umin=0x4000000000000000,umax=0xfffffffffffffffe)") +__retval(0) +__naked void add64_full_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r4 = r0;" + "r3 = 0xa000000000000000 ll;" + "r3 |= r4;" + "r3 += r3;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("64-bit addition, partial overflow, result in unbounded reg") +__success __log_level(2) +__msg("4: (0f) r3 += r3 {{.*}} R3_w=scalar()") +__retval(0) +__naked void add64_partial_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r4 = r0;" + "r3 = 2;" + "r3 |= r4;" + "r3 += r3;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("32-bit addition overflow, all outcomes overflow") +__success __log_level(2) +__msg("4: (0c) w3 += w3 {{.*}} R3_w=scalar(smin=umin=umin32=0x40000000,smax=umax=umax32=0xfffffffe,var_off=(0x0; 0xffffffff))") +__retval(0) +__naked void add32_full_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "w4 = w0;" + "w3 = 0xa0000000;" + "w3 |= w4;" + "w3 += w3;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("32-bit addition, partial overflow, result in unbounded u32 bounds") +__success __log_level(2) +__msg("4: (0c) w3 += w3 {{.*}} R3_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))") +__retval(0) +__naked void add32_partial_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "w4 = w0;" + "w3 = 2;" + "w3 |= w4;" + "w3 += w3;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("64-bit subtraction, all outcomes underflow") +__success __log_level(2) +__msg("6: (1f) r3 -= r1 {{.*}} R3_w=scalar(umin=1,umax=0x8000000000000000)") +__retval(0) +__naked void sub64_full_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = r0;" + "r2 = 0x8000000000000000 ll;" + "r1 |= r2;" + "r3 = 0;" + "r3 -= r1;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("64-bit subtraction, partial overflow, result in unbounded reg") +__success __log_level(2) +__msg("3: (1f) r3 -= r2 {{.*}} R3_w=scalar()") +__retval(0) +__naked void sub64_partial_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r3 = r0;" + "r2 = 1;" + "r3 -= r2;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("32-bit subtraction overflow, all outcomes underflow") +__success __log_level(2) +__msg("5: (1c) w3 -= w1 {{.*}} R3_w=scalar(smin=umin=umin32=1,smax=umax=umax32=0x80000000,var_off=(0x0; 0xffffffff))") +__retval(0) +__naked void sub32_full_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "w1 = w0;" + "w2 = 0x80000000;" + "w1 |= w2;" + "w3 = 0;" + "w3 -= w1;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("32-bit subtraction, partial overflow, result in unbounded u32 bounds") +__success __log_level(2) +__msg("3: (1c) w3 -= w2 {{.*}} R3_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))") +__retval(0) +__naked void sub32_partial_overflow(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "w3 = w0;" + "w2 = 1;" + "w3 -= w2;" + "r0 = 0;" + "exit" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__description("dead branch on jset, does not result in invariants violation error") +__success __log_level(2) +__retval(0) __flag(BPF_F_TEST_REG_INVARIANTS) +__naked void jset_range_analysis(void) +{ + asm volatile (" \ + call %[bpf_get_netns_cookie]; \ + if r0 == 0 goto l0_%=; \ + if r0 & 0xffffffff goto +0; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_netns_cookie) + : __clobber_all); +} + +/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges + * overlap on the negative side. At instruction 7, the ranges look as follows: + * + * 0 umin=0xfffffcf1 umax=0xff..ff6e U64_MAX + * | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | + * |----------------------------|------------------------------| + * |xxxxxxxxxx] [xxxxxxxxxxxx| + * 0 smax=0xeffffeee smin=-655 -1 + * + * We should therefore deduce the following new bounds: + * + * 0 u64=[0xff..ffd71;0xff..ff6e] U64_MAX + * | [xxx] | + * |----------------------------|------------------------------| + * | [xxx] | + * 0 s64=[-655;-146] -1 + * + * Without the deduction cross sign boundary, we end up with an invariant + * violation error. + */ +SEC("socket") +__description("bounds deduction cross sign boundary, negative overlap") +__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) +__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=smin32=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,umin32=0xfffffd71,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") +__retval(0) +__naked void bounds_deduct_negative_overlap(void) +{ + asm volatile(" \ + call %[bpf_get_prandom_u32]; \ + w3 = w0; \ + w6 = (s8)w0; \ + r0 = (s8)r0; \ + if w6 >= 0xf0000000 goto l0_%=; \ + r0 += r6; \ + r6 += 400; \ + r0 -= r6; \ + if r3 < r0 goto l0_%=; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges + * overlap on the positive side. At instruction 3, the ranges look as follows: + * + * 0 umin=0 umax=0xffffffffffffff00 U64_MAX + * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | + * |----------------------------|------------------------------| + * |xxxxxxxx] [xxxxxxxx| + * 0 smax=127 smin=-128 -1 + * + * We should therefore deduce the following new bounds: + * + * 0 u64=[0;127] U64_MAX + * [xxxxxxxx] | + * |----------------------------|------------------------------| + * [xxxxxxxx] | + * 0 s64=[0;127] -1 + * + * Without the deduction cross sign boundary, the program is rejected due to + * the frame pointer write. + */ +SEC("socket") +__description("bounds deduction cross sign boundary, positive overlap") +__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) +__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))") +__retval(0) +__naked void bounds_deduct_positive_overlap(void) +{ + asm volatile(" \ + call %[bpf_get_prandom_u32]; \ + r0 = (s8)r0; \ + r1 = 0xffffffffffffff00; \ + if r0 > r1 goto l0_%=; \ + if r0 < 128 goto l0_%=; \ + r10 = 0; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* This test is the same as above, but the s64 and u64 ranges overlap in two + * places. At instruction 3, the ranges look as follows: + * + * 0 umin=0 umax=0xffffffffffffff80 U64_MAX + * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | + * |----------------------------|------------------------------| + * |xxxxxxxx] [xxxxxxxx| + * 0 smax=127 smin=-128 -1 + * + * 0xffffffffffffff80 = (u64)-128. We therefore can't deduce anything new and + * the program should fail due to the frame pointer write. + */ +SEC("socket") +__description("bounds deduction cross sign boundary, two overlaps") +__failure __flag(BPF_F_TEST_REG_INVARIANTS) +__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=-128,smax=smax32=127,umax=0xffffffffffffff80)") +__msg("frame pointer is read only") +__naked void bounds_deduct_two_overlaps(void) +{ + asm volatile(" \ + call %[bpf_get_prandom_u32]; \ + r0 = (s8)r0; \ + r1 = 0xffffffffffffff80; \ + if r0 > r1 goto l0_%=; \ + if r0 < 128 goto l0_%=; \ + r10 = 0; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds_deduction.c b/tools/testing/selftests/bpf/progs/verifier_bounds_deduction.c index c506afbdd936..260a6df264e3 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bounds_deduction.c +++ b/tools/testing/selftests/bpf/progs/verifier_bounds_deduction.c @@ -159,13 +159,16 @@ __failure_unpriv __naked void deducing_bounds_from_const_10(void) { asm volatile (" \ + r6 = r1; \ r0 = 0; \ if r0 s<= 0 goto l0_%=; \ -l0_%=: /* Marks reg as unknown. */ \ - r0 = -r0; \ - r0 -= r1; \ +l0_%=: /* Marks r0 as unknown. */ \ + call %[bpf_get_prandom_u32]; \ + r0 -= r6; \ exit; \ -" ::: __clobber_all); +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); } char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c index a83809a1dbbf..424463094760 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ctx.c +++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* Converted from tools/testing/selftests/bpf/verifier/ctx.c */ -#include +#include "vmlinux.h" #include #include "bpf_misc.h" +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) + SEC("tc") __description("context stores via BPF_ATOMIC") __failure __msg("BPF_ATOMIC stores into R1 ctx is not allowed") @@ -218,4 +220,48 @@ __naked void null_check_8_null_bind(void) : __clobber_all); } +#define narrow_load(type, ctx, field) \ + SEC(type) \ + __description("narrow load on field " #field " of " #ctx) \ + __failure __msg("invalid bpf_context access") \ + __naked void invalid_narrow_load##ctx##field(void) \ + { \ + asm volatile (" \ + r1 = *(u32 *)(r1 + %[off]); \ + r0 = 0; \ + exit;" \ + : \ + : __imm_const(off, offsetof(struct ctx, field) + 4) \ + : __clobber_all); \ + } + +narrow_load("cgroup/getsockopt", bpf_sockopt, sk); +narrow_load("cgroup/getsockopt", bpf_sockopt, optval); +narrow_load("cgroup/getsockopt", bpf_sockopt, optval_end); +narrow_load("tc", __sk_buff, sk); +narrow_load("cgroup/bind4", bpf_sock_addr, sk); +narrow_load("sockops", bpf_sock_ops, sk); +narrow_load("sockops", bpf_sock_ops, skb_data); +narrow_load("sockops", bpf_sock_ops, skb_data_end); +narrow_load("sockops", bpf_sock_ops, skb_hwtstamp); + +#define unaligned_access(type, ctx, field) \ + SEC(type) \ + __description("unaligned access on field " #field " of " #ctx) \ + __failure __msg("invalid bpf_context access") \ + __naked void unaligned_ctx_access_##ctx##field(void) \ + { \ + asm volatile (" \ + r1 = *(u%[size] *)(r1 + %[off]); \ + r0 = 0; \ + exit;" \ + : \ + : __imm_const(size, sizeof_field(struct ctx, field) * 8), \ + __imm_const(off, offsetof(struct ctx, field) + 1) \ + : __clobber_all); \ + } + +unaligned_access("flow_dissector", __sk_buff, data); +unaligned_access("netfilter", bpf_nf_ctx, skb); + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_div_overflow.c b/tools/testing/selftests/bpf/progs/verifier_div_overflow.c index 458984da804c..34e0c012ee76 100644 --- a/tools/testing/selftests/bpf/progs/verifier_div_overflow.c +++ b/tools/testing/selftests/bpf/progs/verifier_div_overflow.c @@ -77,7 +77,7 @@ l0_%=: exit; \ SEC("tc") __description("MOD32 overflow, check 1") -__success __retval(INT_MIN) +__success __retval(_INT_MIN) __naked void mod32_overflow_check_1(void) { asm volatile (" \ @@ -92,7 +92,7 @@ __naked void mod32_overflow_check_1(void) SEC("tc") __description("MOD32 overflow, check 2") -__success __retval(INT_MIN) +__success __retval(_INT_MIN) __naked void mod32_overflow_check_2(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c index 4ab0ef18d7eb..181da86ba5f0 100644 --- a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c +++ b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c @@ -179,4 +179,132 @@ int BPF_PROG(trusted_acq_rel, struct task_struct *task, u64 clone_flags) return subprog_trusted_acq_rel(task); } +__weak int subprog_untrusted_bad_tags(struct task_struct *task __arg_untrusted __arg_nullable) +{ + return task->pid; +} + +SEC("tp_btf/sys_enter") +__failure +__msg("arg#0 untrusted cannot be combined with any other tags") +int untrusted_bad_tags(void *ctx) +{ + return subprog_untrusted_bad_tags(0); +} + +struct local_type_wont_be_accepted {}; + +__weak int subprog_untrusted_bad_type(struct local_type_wont_be_accepted *p __arg_untrusted) +{ + return 0; +} + +SEC("tp_btf/sys_enter") +__failure +__msg("arg#0 reference type('STRUCT local_type_wont_be_accepted') has no matches") +int untrusted_bad_type(void *ctx) +{ + return subprog_untrusted_bad_type(bpf_rdonly_cast(0, 0)); +} + +__weak int subprog_untrusted(const volatile struct task_struct *restrict task __arg_untrusted) +{ + return task->pid; +} + +SEC("tp_btf/sys_enter") +__success +__log_level(2) +__msg("r1 = {{.*}}; {{.*}}R1_w=trusted_ptr_task_struct()") +__msg("Func#1 ('subprog_untrusted') is global and assumed valid.") +__msg("Validating subprog_untrusted() func#1...") +__msg(": R1=untrusted_ptr_task_struct") +int trusted_to_untrusted(void *ctx) +{ + return subprog_untrusted(bpf_get_current_task_btf()); +} + +char mem[16]; +u32 off; + +SEC("tp_btf/sys_enter") +__success +int anything_to_untrusted(void *ctx) +{ + /* untrusted to untrusted */ + subprog_untrusted(bpf_core_cast(0, struct task_struct)); + /* wrong type to untrusted */ + subprog_untrusted((void *)bpf_core_cast(0, struct bpf_verifier_env)); + /* map value to untrusted */ + subprog_untrusted((void *)mem); + /* scalar to untrusted */ + subprog_untrusted(0); + /* variable offset to untrusted (map) */ + subprog_untrusted((void *)mem + off); + /* variable offset to untrusted (trusted) */ + subprog_untrusted((void *)bpf_get_current_task_btf() + off); + return 0; +} + +__weak int subprog_untrusted2(struct task_struct *task __arg_untrusted) +{ + return subprog_trusted_task_nullable(task); +} + +SEC("tp_btf/sys_enter") +__failure +__msg("R1 type=untrusted_ptr_ expected=ptr_, trusted_ptr_, rcu_ptr_") +__msg("Caller passes invalid args into func#{{.*}} ('subprog_trusted_task_nullable')") +int untrusted_to_trusted(void *ctx) +{ + return subprog_untrusted2(bpf_get_current_task_btf()); +} + +__weak int subprog_void_untrusted(void *p __arg_untrusted) +{ + return *(int *)p; +} + +__weak int subprog_char_untrusted(char *p __arg_untrusted) +{ + return *(int *)p; +} + +__weak int subprog_enum_untrusted(enum bpf_attach_type *p __arg_untrusted) +{ + return *(int *)p; +} + +SEC("tp_btf/sys_enter") +__success +__log_level(2) +__msg("r1 = {{.*}}; {{.*}}R1_w=trusted_ptr_task_struct()") +__msg("Func#1 ('subprog_void_untrusted') is global and assumed valid.") +__msg("Validating subprog_void_untrusted() func#1...") +__msg(": R1=rdonly_untrusted_mem(sz=0)") +int trusted_to_untrusted_mem(void *ctx) +{ + return subprog_void_untrusted(bpf_get_current_task_btf()); +} + +SEC("tp_btf/sys_enter") +__success +int anything_to_untrusted_mem(void *ctx) +{ + /* untrusted to untrusted mem */ + subprog_void_untrusted(bpf_core_cast(0, struct task_struct)); + /* map value to untrusted mem */ + subprog_void_untrusted(mem); + /* scalar to untrusted mem */ + subprog_void_untrusted(0); + /* variable offset to untrusted mem (map) */ + subprog_void_untrusted((void *)mem + off); + /* variable offset to untrusted mem (trusted) */ + subprog_void_untrusted(bpf_get_current_task_btf() + off); + /* variable offset to untrusted char/enum (map) */ + subprog_char_untrusted(mem + off); + subprog_enum_untrusted((void *)mem + off); + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_map_in_map.c b/tools/testing/selftests/bpf/progs/verifier_map_in_map.c index 7d088ba99ea5..16b761e510f0 100644 --- a/tools/testing/selftests/bpf/progs/verifier_map_in_map.c +++ b/tools/testing/selftests/bpf/progs/verifier_map_in_map.c @@ -139,4 +139,122 @@ __naked void on_the_inner_map_pointer(void) : __clobber_all); } +SEC("socket") +__description("map_ptr is never null") +__success +__naked void map_ptr_is_never_null(void) +{ + asm volatile (" \ + r0 = 0; \ + r1 = %[map_in_map] ll; \ + if r1 != 0 goto l0_%=; \ + r10 = 42; \ +l0_%=: exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_in_map) + : __clobber_all); +} + +SEC("socket") +__description("map_ptr is never null inner") +__success +__naked void map_ptr_is_never_null_inner(void) +{ + asm volatile (" \ + r1 = 0; \ + *(u32*)(r10 - 4) = r1; \ + r2 = r10; \ + r2 += -4; \ + r1 = %[map_in_map] ll; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r10 = 42; \ +l0_%=: exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_in_map) + : __clobber_all); +} + +SEC("socket") +__description("map_ptr is never null inner spill fill") +__success +__naked void map_ptr_is_never_null_inner_spill_fill(void) +{ + asm volatile (" \ + r1 = 0; \ + *(u32*)(r10 - 4) = r1; \ + r2 = r10; \ + r2 += -4; \ + r1 = %[map_in_map] ll; \ + call %[bpf_map_lookup_elem]; \ + if r0 != 0 goto l0_%=; \ + exit; \ +l0_%=: *(u64 *)(r10 -16) = r0; \ + r1 = *(u64 *)(r10 -16); \ + if r1 == 0 goto l1_%=; \ + exit; \ +l1_%=: r10 = 42; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_in_map) + : __clobber_all); +} + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); + __array(values, struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 64 * 1024); + }); +} rb_in_map SEC(".maps"); + +struct rb_ctx { + void *rb; + struct bpf_dynptr dptr; +}; + +static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz) +{ + struct rb_ctx rb_ctx = {}; + void *rb; + __u32 cpu = bpf_get_smp_processor_id(); + __u32 rb_slot = cpu & 1; + + rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot); + if (!rb) + return rb_ctx; + + rb_ctx.rb = rb; + bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr); + + return rb_ctx; +} + +static __noinline void __rb_event_submit(struct rb_ctx *ctx) +{ + if (!ctx->rb) + return; + + /* If the verifier (incorrectly) concludes that ctx->rb can be + * NULL at this point, we'll get "BPF_EXIT instruction in main + * prog would lead to reference leak" error + */ + bpf_ringbuf_submit_dynptr(&ctx->dptr, 0); +} + +SEC("socket") +int map_ptr_is_never_null_rb(void *ctx) +{ + struct rb_ctx event_ctx = __rb_event_reserve(256); + __rb_event_submit(&event_ctx); + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_movsx.c b/tools/testing/selftests/bpf/progs/verifier_movsx.c index 994bbc346d25..a4d8814eb5ed 100644 --- a/tools/testing/selftests/bpf/progs/verifier_movsx.c +++ b/tools/testing/selftests/bpf/progs/verifier_movsx.c @@ -245,7 +245,13 @@ l0_%=: \ SEC("socket") __description("MOV32SX, S8, var_off not u32_max, positive after s8 extension") __success __retval(0) -__failure_unpriv __msg_unpriv("frame pointer is read only") +__success_unpriv +#ifdef SPEC_V1 +__xlated_unpriv("w0 = 0") +__xlated_unpriv("exit") +__xlated_unpriv("nospec") /* inserted to prevent `frame pointer is read only` */ +__xlated_unpriv("goto pc-1") +#endif __naked void mov64sx_s32_varoff_2(void) { asm volatile (" \ @@ -267,7 +273,13 @@ l0_%=: \ SEC("socket") __description("MOV32SX, S8, var_off not u32_max, negative after s8 extension") __success __retval(0) -__failure_unpriv __msg_unpriv("frame pointer is read only") +__success_unpriv +#ifdef SPEC_V1 +__xlated_unpriv("w0 = 0") +__xlated_unpriv("exit") +__xlated_unpriv("nospec") /* inserted to prevent `frame pointer is read only` */ +__xlated_unpriv("goto pc-1") +#endif __naked void mov64sx_s32_varoff_3(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 9fe5d255ee37..73fee2aec698 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -231,4 +231,74 @@ __naked void bpf_cond_op_not_r10(void) ::: __clobber_all); } +SEC("lsm.s/socket_connect") +__success __log_level(2) +__msg("0: (b7) r0 = 1 ; R0_w=1") +__msg("1: (84) w0 = -w0 ; R0_w=0xffffffff") +__msg("mark_precise: frame0: last_idx 2 first_idx 0 subseq_idx -1") +__msg("mark_precise: frame0: regs=r0 stack= before 1: (84) w0 = -w0") +__msg("mark_precise: frame0: regs=r0 stack= before 0: (b7) r0 = 1") +__naked int bpf_neg_2(void) +{ + /* + * lsm.s/socket_connect requires a return value within [-4095, 0]. + * Returning -1 is allowed + */ + asm volatile ( + "r0 = 1;" + "w0 = -w0;" + "exit;" + ::: __clobber_all); +} + +SEC("lsm.s/socket_connect") +__failure __msg("At program exit the register R0 has") +__naked int bpf_neg_3(void) +{ + /* + * lsm.s/socket_connect requires a return value within [-4095, 0]. + * Returning -10000 is not allowed. + */ + asm volatile ( + "r0 = 10000;" + "w0 = -w0;" + "exit;" + ::: __clobber_all); +} + +SEC("lsm.s/socket_connect") +__success __log_level(2) +__msg("0: (b7) r0 = 1 ; R0_w=1") +__msg("1: (87) r0 = -r0 ; R0_w=-1") +__msg("mark_precise: frame0: last_idx 2 first_idx 0 subseq_idx -1") +__msg("mark_precise: frame0: regs=r0 stack= before 1: (87) r0 = -r0") +__msg("mark_precise: frame0: regs=r0 stack= before 0: (b7) r0 = 1") +__naked int bpf_neg_4(void) +{ + /* + * lsm.s/socket_connect requires a return value within [-4095, 0]. + * Returning -1 is allowed + */ + asm volatile ( + "r0 = 1;" + "r0 = -r0;" + "exit;" + ::: __clobber_all); +} + +SEC("lsm.s/socket_connect") +__failure __msg("At program exit the register R0 has") +__naked int bpf_neg_5(void) +{ + /* + * lsm.s/socket_connect requires a return value within [-4095, 0]. + * Returning -10000 is not allowed. + */ + asm volatile ( + "r0 = 10000;" + "r0 = -r0;" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_private_stack.c b/tools/testing/selftests/bpf/progs/verifier_private_stack.c index fc91b414364e..1ecd34ebde19 100644 --- a/tools/testing/selftests/bpf/progs/verifier_private_stack.c +++ b/tools/testing/selftests/bpf/progs/verifier_private_stack.c @@ -8,7 +8,7 @@ /* From include/linux/filter.h */ #define MAX_BPF_STACK 512 -#if defined(__TARGET_ARCH_x86) +#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) struct elem { struct bpf_timer t; @@ -30,6 +30,18 @@ __jited(" movabsq $0x{{.*}}, %r9") __jited(" addq %gs:{{.*}}, %r9") __jited(" movl $0x2a, %edi") __jited(" movq %rdi, -0x100(%r9)") +__arch_arm64 +__jited(" stp x25, x27, [sp, {{.*}}]!") +__jited(" mov x27, {{.*}}") +__jited(" movk x27, {{.*}}, lsl #16") +__jited(" movk x27, {{.*}}") +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") +__jited(" mov x0, #0x2a") +__jited(" str x0, [x27]") +__jited("...") +__jited(" ldp x25, x27, [sp], {{.*}}") __naked void private_stack_single_prog(void) { asm volatile (" \ @@ -45,6 +57,9 @@ __description("No private stack") __success __arch_x86_64 __jited(" subq $0x8, %rsp") +__arch_arm64 +__jited(" mov x25, sp") +__jited(" sub sp, sp, #0x10") __naked void no_private_stack_nested(void) { asm volatile (" \ @@ -81,6 +96,19 @@ __jited(" pushq %r9") __jited(" callq 0x{{.*}}") __jited(" popq %r9") __jited(" xorl %eax, %eax") +__arch_arm64 +__jited(" stp x25, x27, [sp, {{.*}}]!") +__jited(" mov x27, {{.*}}") +__jited(" movk x27, {{.*}}, lsl #16") +__jited(" movk x27, {{.*}}") +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") +__jited(" mov x0, #0x2a") +__jited(" str x0, [x27]") +__jited(" bl {{.*}}") +__jited("...") +__jited(" ldp x25, x27, [sp], {{.*}}") __naked void private_stack_nested_1(void) { asm volatile (" \ @@ -131,6 +159,24 @@ __jited(" movq %rdi, -0x200(%r9)") __jited(" pushq %r9") __jited(" callq") __jited(" popq %r9") +__arch_arm64 +__jited("func #1") +__jited("...") +__jited(" stp x25, x27, [sp, {{.*}}]!") +__jited(" mov x27, {{.*}}") +__jited(" movk x27, {{.*}}, lsl #16") +__jited(" movk x27, {{.*}}") +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") +__jited(" bl 0x{{.*}}") +__jited(" add x7, x0, #0x0") +__jited(" mov x0, #0x2a") +__jited(" str x0, [x27]") +__jited(" bl 0x{{.*}}") +__jited(" add x7, x0, #0x0") +__jited(" mov x7, #0x0") +__jited(" ldp x25, x27, [sp], {{.*}}") __naked void private_stack_callback(void) { asm volatile (" \ @@ -154,6 +200,28 @@ __arch_x86_64 __jited(" pushq %r9") __jited(" callq") __jited(" popq %r9") +__arch_arm64 +__jited(" stp x29, x30, [sp, #-0x10]!") +__jited(" mov x29, sp") +__jited(" stp xzr, x26, [sp, #-0x10]!") +__jited(" mov x26, sp") +__jited(" stp x19, x20, [sp, #-0x10]!") +__jited(" stp x21, x22, [sp, #-0x10]!") +__jited(" stp x23, x24, [sp, #-0x10]!") +__jited(" stp x25, x26, [sp, #-0x10]!") +__jited(" stp x27, x28, [sp, #-0x10]!") +__jited(" mov x27, {{.*}}") +__jited(" movk x27, {{.*}}, lsl #16") +__jited(" movk x27, {{.*}}") +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") +__jited(" mov x0, #0x2a") +__jited(" str x0, [x27]") +__jited(" mov x0, #0x0") +__jited(" bl 0x{{.*}}") +__jited(" add x7, x0, #0x0") +__jited(" ldp x27, x28, [sp], #0x10") int private_stack_exception_main_prog(void) { asm volatile (" \ @@ -179,6 +247,19 @@ __jited(" movq %rdi, -0x200(%r9)") __jited(" pushq %r9") __jited(" callq") __jited(" popq %r9") +__arch_arm64 +__jited(" stp x27, x28, [sp, #-0x10]!") +__jited(" mov x27, {{.*}}") +__jited(" movk x27, {{.*}}, lsl #16") +__jited(" movk x27, {{.*}}") +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") +__jited(" mov x0, #0x2a") +__jited(" str x0, [x27]") +__jited(" bl 0x{{.*}}") +__jited(" add x7, x0, #0x0") +__jited(" ldp x27, x28, [sp], #0x10") int private_stack_exception_sub_prog(void) { asm volatile (" \ @@ -220,6 +301,10 @@ __description("Private stack, async callback, not nested") __success __retval(0) __arch_x86_64 __jited(" movabsq $0x{{.*}}, %r9") +__arch_arm64 +__jited(" mrs x10, TPIDR_EL{{[0-1]}}") +__jited(" add x27, x27, x10") +__jited(" add x25, x27, {{.*}}") int private_stack_async_callback_1(void) { struct bpf_timer *arr_timer; @@ -241,6 +326,8 @@ __description("Private stack, async callback, potential nesting") __success __retval(0) __arch_x86_64 __jited(" subq $0x100, %rsp") +__arch_arm64 +__jited(" sub sp, sp, #0x100") int private_stack_async_callback_2(void) { struct bpf_timer *arr_timer; diff --git a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c index 683a882b3e6d..910365201f68 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c +++ b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c @@ -27,7 +27,7 @@ struct bpf_key {} __attribute__((preserve_access_index)); extern void bpf_key_put(struct bpf_key *key) __ksym; extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym; -extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym; /* BTF FUNC records are not generated for kfuncs referenced * from inline assembly. These records are necessary for diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall.c b/tools/testing/selftests/bpf/progs/verifier_tailcall.c new file mode 100644 index 000000000000..b4acce60fb9b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_tailcall.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} map_array SEC(".maps"); + +SEC("socket") +__description("invalid map type for tail call") +__failure __msg("expected prog array map for tail call") +__failure_unpriv +__naked void invalid_map_for_tail_call(void) +{ + asm volatile (" \ + r2 = %[map_array] ll; \ + r3 = 0; \ + call %[bpf_tail_call]; \ + exit; \ +" : + : __imm(bpf_tail_call), + __imm_addr(map_array) + : __clobber_all); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c index a4a5e2071604..28b4f7035ceb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -572,8 +572,14 @@ l0_%=: exit; \ SEC("socket") __description("alu32: mov u32 const") -__success __failure_unpriv __msg_unpriv("R7 invalid mem access 'scalar'") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if r0 == 0x0 goto pc+2") +__xlated_unpriv("nospec") /* inserted to prevent `R7 invalid mem access 'scalar'` */ +__xlated_unpriv("goto pc-1") /* sanitized dead code */ +__xlated_unpriv("exit") +#endif __naked void alu32_mov_u32_const(void) { asm volatile (" \ @@ -619,12 +625,11 @@ __naked void pass_pointer_to_tail_call(void) SEC("socket") __description("unpriv: cmp map pointer with zero") -__success __failure_unpriv __msg_unpriv("R1 pointer comparison") +__success __success_unpriv __retval(0) __naked void cmp_map_pointer_with_zero(void) { asm volatile (" \ - r1 = 0; \ r1 = %[map_hash_8b] ll; \ if r1 == 0 goto l0_%=; \ l0_%=: r0 = 0; \ @@ -634,6 +639,22 @@ l0_%=: r0 = 0; \ : __clobber_all); } +SEC("socket") +__description("unpriv: cmp map pointer with const") +__success __failure_unpriv __msg_unpriv("R1 pointer comparison prohibited") +__retval(0) +__naked void cmp_map_pointer_with_const(void) +{ + asm volatile (" \ + r1 = %[map_hash_8b] ll; \ + if r1 == 0x0000beef goto l0_%=; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm_addr(map_hash_8b) + : __clobber_all); +} + SEC("socket") __description("unpriv: write into frame pointer") __failure __msg("frame pointer is read only") @@ -723,4 +744,210 @@ l0_%=: r0 = 0; \ " ::: __clobber_all); } +SEC("socket") +__description("unpriv: Spectre v1 path-based type confusion of scalar as stack-ptr") +__success __success_unpriv __retval(0) +#ifdef SPEC_V1 +__xlated_unpriv("if r0 != 0x1 goto pc+2") +/* This nospec prevents the exploit because it forces the mispredicted (not + * taken) `if r0 != 0x0 goto l0_%=` to resolve before using r6 as a pointer. + * This causes the CPU to realize that `r6 = r9` should have never executed. It + * ensures that r6 always contains a readable stack slot ptr when the insn after + * the nospec executes. + */ +__xlated_unpriv("nospec") +__xlated_unpriv("r9 = *(u8 *)(r6 +0)") +#endif +__naked void unpriv_spec_v1_type_confusion(void) +{ + asm volatile (" \ + r1 = 0; \ + *(u64*)(r10 - 8) = r1; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto l2_%=; \ + /* r0: pointer to a map array entry */ \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + /* r1, r2: prepared call args */ \ + r6 = r10; \ + r6 += -8; \ + /* r6: pointer to readable stack slot */ \ + r9 = 0xffffc900; \ + r9 <<= 32; \ + /* r9: scalar controlled by attacker */ \ + r0 = *(u64 *)(r0 + 0); /* cache miss */ \ + if r0 != 0x0 goto l0_%=; \ + r6 = r9; \ +l0_%=: if r0 != 0x1 goto l1_%=; \ + r9 = *(u8 *)(r6 + 0); \ +l1_%=: /* leak r9 */ \ + r9 &= 1; \ + r9 <<= 9; \ + *(u64*)(r10 - 8) = r9; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto l2_%=; \ + /* leak secret into is_cached(map[0|512]): */ \ + r0 = *(u64 *)(r0 + 0); \ +l2_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash_8b) + : __clobber_all); +} + +SEC("socket") +__description("unpriv: ldimm64 before Spectre v4 barrier") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V4 +__xlated_unpriv("r1 = 0x2020200005642020") /* should not matter */ +__xlated_unpriv("*(u64 *)(r10 -8) = r1") +__xlated_unpriv("nospec") +#endif +__naked void unpriv_ldimm64_spectre_v4(void) +{ + asm volatile (" \ + r1 = 0x2020200005642020 ll; \ + *(u64 *)(r10 -8) = r1; \ + r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("unpriv: Spectre v1 and v4 barrier") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V1 +#ifdef SPEC_V4 +/* starts with r0 == r8 == r9 == 0 */ +__xlated_unpriv("if r8 != 0x0 goto pc+1") +__xlated_unpriv("goto pc+2") +__xlated_unpriv("if r9 == 0x0 goto pc+4") +__xlated_unpriv("r2 = r0") +/* Following nospec required to prevent following dangerous `*(u64 *)(NOT_FP -64) + * = r1` iff `if r9 == 0 goto pc+4` was mispredicted because of Spectre v1. The + * test therefore ensures the Spectre-v4--induced nospec does not prevent the + * Spectre-v1--induced speculative path from being fully analyzed. + */ +__xlated_unpriv("nospec") /* Spectre v1 */ +__xlated_unpriv("*(u64 *)(r2 -64) = r1") /* could be used to leak r2 */ +__xlated_unpriv("nospec") /* Spectre v4 */ +#endif +#endif +__naked void unpriv_spectre_v1_and_v4(void) +{ + asm volatile (" \ + r1 = 0; \ + *(u64*)(r10 - 8) = r1; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + r8 = r0; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + r9 = r0; \ + r0 = r10; \ + r1 = 0; \ + r2 = r10; \ + if r8 != 0 goto l0_%=; \ + if r9 != 0 goto l0_%=; \ + r0 = 0; \ +l0_%=: if r8 != 0 goto l1_%=; \ + goto l2_%=; \ +l1_%=: if r9 == 0 goto l3_%=; \ + r2 = r0; \ +l2_%=: *(u64 *)(r2 -64) = r1; \ +l3_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash_8b) + : __clobber_all); +} + +SEC("socket") +__description("unpriv: Spectre v1 and v4 barrier (simple)") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V1 +#ifdef SPEC_V4 +__xlated_unpriv("if r8 != 0x0 goto pc+1") +__xlated_unpriv("goto pc+2") +__xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */ +__xlated_unpriv("goto pc-1") /* r2 = r0 */ +__xlated_unpriv("nospec") +__xlated_unpriv("*(u64 *)(r2 -64) = r1") +__xlated_unpriv("nospec") +#endif +#endif +__naked void unpriv_spectre_v1_and_v4_simple(void) +{ + asm volatile (" \ + r8 = 0; \ + r9 = 0; \ + r0 = r10; \ + r1 = 0; \ + r2 = r10; \ + if r8 != 0 goto l0_%=; \ + if r9 != 0 goto l0_%=; \ + r0 = 0; \ +l0_%=: if r8 != 0 goto l1_%=; \ + goto l2_%=; \ +l1_%=: if r9 == 0 goto l3_%=; \ + r2 = r0; \ +l2_%=: *(u64 *)(r2 -64) = r1; \ +l3_%=: r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("unpriv: ldimm64 before Spectre v1 and v4 barrier (simple)") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V1 +#ifdef SPEC_V4 +__xlated_unpriv("if r8 != 0x0 goto pc+1") +__xlated_unpriv("goto pc+4") +__xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */ +__xlated_unpriv("goto pc-1") /* r2 = r0 */ +__xlated_unpriv("goto pc-1") /* r1 = 0x2020200005642020 ll */ +__xlated_unpriv("goto pc-1") /* second part of ldimm64 */ +__xlated_unpriv("nospec") +__xlated_unpriv("*(u64 *)(r2 -64) = r1") +__xlated_unpriv("nospec") +#endif +#endif +__naked void unpriv_ldimm64_spectre_v1_and_v4_simple(void) +{ + asm volatile (" \ + r8 = 0; \ + r9 = 0; \ + r0 = r10; \ + r1 = 0; \ + r2 = r10; \ + if r8 != 0 goto l0_%=; \ + if r9 != 0 goto l0_%=; \ + r0 = 0; \ +l0_%=: if r8 != 0 goto l1_%=; \ + goto l2_%=; \ +l1_%=: if r9 == 0 goto l3_%=; \ + r2 = r0; \ + r1 = 0x2020200005642020 ll; \ +l2_%=: *(u64 *)(r2 -64) = r1; \ +l3_%=: r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_value_ptr_arith.c b/tools/testing/selftests/bpf/progs/verifier_value_ptr_arith.c index 5ba6e53571c8..af7938ce56cb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_value_ptr_arith.c +++ b/tools/testing/selftests/bpf/progs/verifier_value_ptr_arith.c @@ -231,6 +231,10 @@ __retval(1) __naked void ptr_unknown_vs_unknown_lt(void) { asm volatile (" \ + r8 = r1; \ + call %[bpf_get_prandom_u32]; \ + r9 = r0; \ + r1 = r8; \ r0 = *(u32*)(r1 + %[__sk_buff_len]); \ r1 = 0; \ *(u64*)(r10 - 8) = r1; \ @@ -245,11 +249,11 @@ l1_%=: call %[bpf_map_lookup_elem]; \ r4 = *(u8*)(r0 + 0); \ if r4 == 1 goto l3_%=; \ r1 = 6; \ - r1 = -r1; \ + r1 = r9; \ r1 &= 0x3; \ goto l4_%=; \ l3_%=: r1 = 6; \ - r1 = -r1; \ + r1 = r9; \ r1 &= 0x7; \ l4_%=: r1 += r0; \ r0 = *(u8*)(r1 + 0); \ @@ -259,7 +263,8 @@ l2_%=: r0 = 1; \ : __imm(bpf_map_lookup_elem), __imm_addr(map_array_48b), __imm_addr(map_hash_16b), - __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len)) + __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len)), + __imm(bpf_get_prandom_u32) : __clobber_all); } @@ -271,6 +276,10 @@ __retval(1) __naked void ptr_unknown_vs_unknown_gt(void) { asm volatile (" \ + r8 = r1; \ + call %[bpf_get_prandom_u32]; \ + r9 = r0; \ + r1 = r8; \ r0 = *(u32*)(r1 + %[__sk_buff_len]); \ r1 = 0; \ *(u64*)(r10 - 8) = r1; \ @@ -285,11 +294,11 @@ l1_%=: call %[bpf_map_lookup_elem]; \ r4 = *(u8*)(r0 + 0); \ if r4 == 1 goto l3_%=; \ r1 = 6; \ - r1 = -r1; \ + r1 = r9; \ r1 &= 0x7; \ goto l4_%=; \ l3_%=: r1 = 6; \ - r1 = -r1; \ + r1 = r9; \ r1 &= 0x3; \ l4_%=: r1 += r0; \ r0 = *(u8*)(r1 + 0); \ @@ -299,7 +308,8 @@ l2_%=: r0 = 1; \ : __imm(bpf_map_lookup_elem), __imm_addr(map_array_48b), __imm_addr(map_hash_16b), - __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len)) + __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len)), + __imm(bpf_get_prandom_u32) : __clobber_all); } @@ -398,7 +408,8 @@ l2_%=: r0 = 1; \ SEC("socket") __description("map access: mixing value pointer and scalar, 1") -__success __failure_unpriv __msg_unpriv("R2 pointer comparison prohibited") +__success __failure_unpriv +__msg_unpriv("R2 tried to add from different maps, paths or scalars, pointer arithmetic with it prohibited for !root") __retval(0) __naked void value_pointer_and_scalar_1(void) { @@ -433,6 +444,7 @@ l2_%=: /* common instruction */ \ l3_%=: /* branch B */ \ r0 = 0x13371337; \ /* verifier follows fall-through */ \ + /* unpriv: nospec (inserted to prevent `R2 pointer comparison prohibited`) */\ if r2 != 0x100000 goto l4_%=; \ r0 = 0; \ exit; \ @@ -450,7 +462,8 @@ l4_%=: /* fake-dead code; targeted from branch A to \ SEC("socket") __description("map access: mixing value pointer and scalar, 2") -__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'") +__success __failure_unpriv +__msg_unpriv("R2 tried to add from different maps, paths or scalars, pointer arithmetic with it prohibited for !root") __retval(0) __naked void value_pointer_and_scalar_2(void) { @@ -492,6 +505,7 @@ l4_%=: /* fake-dead code; targeted from branch A to \ * prevent dead code sanitization, rejected \ * via branch B however \ */ \ + /* unpriv: nospec (inserted to prevent `R0 invalid mem access 'scalar'`) */\ r0 = *(u8*)(r0 + 0); \ r0 = 0; \ exit; \ @@ -1296,9 +1310,13 @@ l0_%=: r0 = 1; \ SEC("socket") __description("map access: value_ptr -= unknown scalar, 2") -__success __failure_unpriv -__msg_unpriv("R0 pointer arithmetic of map value goes out of range") +__success __success_unpriv __retval(1) +#ifdef SPEC_V1 +__xlated_unpriv("r1 &= 7") +__xlated_unpriv("nospec") /* inserted to prevent `R0 pointer arithmetic of map value goes out of range` */ +__xlated_unpriv("r0 -= r1") +#endif __naked void value_ptr_unknown_scalar_2_2(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c index a7c0a553aa50..3e2d76ee8050 100644 --- a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c @@ -2,6 +2,7 @@ /* Copyright (c) 2024 Google LLC. */ #include +#include #include #include @@ -82,4 +83,21 @@ int BPF_PROG(path_d_path_from_file_argument, struct file *file) return 0; } +SEC("lsm.s/inode_rename") +__success +int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + struct inode *inode = new_dentry->d_inode; + ino_t ino; + + if (!inode) + return 0; + ino = inode->i_ino; + if (ino == 0) + return -EACCES; + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c index d6d3f4fcb24c..4b392c6c8fc4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c @@ -2,6 +2,7 @@ /* Copyright (c) 2024 Google LLC. */ #include +#include #include #include #include @@ -158,4 +159,18 @@ int BPF_PROG(path_d_path_kfunc_non_lsm, struct path *path, struct file *f) return 0; } +SEC("lsm.s/inode_rename") +__failure __msg("invalid mem access 'trusted_ptr_or_null_'") +int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + struct inode *inode = new_dentry->d_inode; + ino_t ino; + + ino = inode->i_ino; + if (ino == 0) + return -EACCES; + return 0; +} char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_bpftool_map.sh b/tools/testing/selftests/bpf/test_bpftool_map.sh new file mode 100755 index 000000000000..515b1df0501e --- /dev/null +++ b/tools/testing/selftests/bpf/test_bpftool_map.sh @@ -0,0 +1,398 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# Kselftest framework requirement - SKIP code is 4. +ksft_skip=4 + +TESTNAME="bpftool_map" +BPF_FILE="security_bpf_map.bpf.o" +BPF_ITER_FILE="bpf_iter_map_elem.bpf.o" +PROTECTED_MAP_NAME="prot_map" +NOT_PROTECTED_MAP_NAME="not_prot_map" +BPF_FS_TMP_PARENT="/tmp" +BPF_FS_PARENT=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts) +BPF_FS_PARENT=${BPF_FS_PARENT:-$BPF_FS_TMP_PARENT} +# bpftool will mount bpf file system under BPF_DIR if it is not mounted +# under BPF_FS_PARENT. +BPF_DIR="$BPF_FS_PARENT/test_$TESTNAME" +SCRIPT_DIR=$(dirname $(realpath "$0")) +BPF_FILE_PATH="$SCRIPT_DIR/$BPF_FILE" +BPF_ITER_FILE_PATH="$SCRIPT_DIR/$BPF_ITER_FILE" +BPFTOOL_PATH="bpftool" +# Assume the script is located under tools/testing/selftests/bpf/ +KDIR_ROOT_DIR=$(realpath "$SCRIPT_DIR"/../../../../) + +_cleanup() +{ + set +eu + + # If BPF_DIR is a mount point this will not remove the mount point itself. + [ -d "$BPF_DIR" ] && rm -rf "$BPF_DIR" 2> /dev/null + + # Unmount if BPF filesystem was temporarily created. + if [ "$BPF_FS_PARENT" = "$BPF_FS_TMP_PARENT" ]; then + # A loop and recursive unmount are required as bpftool might + # create multiple mounts. For example, a bind mount of the directory + # to itself. The bind mount is created to change mount propagation + # flags on an actual mount point. + max_attempts=3 + attempt=0 + while mountpoint -q "$BPF_DIR" && [ $attempt -lt $max_attempts ]; do + umount -R "$BPF_DIR" 2>/dev/null + attempt=$((attempt+1)) + done + + # The directory still exists. Remove it now. + [ -d "$BPF_DIR" ] && rm -rf "$BPF_DIR" 2>/dev/null + fi +} + +cleanup_skip() +{ + echo "selftests: $TESTNAME [SKIP]" + _cleanup + + exit $ksft_skip +} + +cleanup() +{ + if [ "$?" = 0 ]; then + echo "selftests: $TESTNAME [PASS]" + else + echo "selftests: $TESTNAME [FAILED]" + fi + _cleanup +} + +check_root_privileges() { + if [ $(id -u) -ne 0 ]; then + echo "Need root privileges" + exit $ksft_skip + fi +} + +# Function to verify bpftool path. +# Parameters: +# $1: bpftool path +verify_bpftool_path() { + local bpftool_path="$1" + if ! "$bpftool_path" version > /dev/null 2>&1; then + echo "Could not run test without bpftool" + exit $ksft_skip + fi +} + +# Function to verify BTF support. +# The test requires BTF support for fmod_ret programs. +verify_btf_support() { + if [ ! -f /sys/kernel/btf/vmlinux ]; then + echo "Could not run test without BTF support" + exit $ksft_skip + fi +} + +# Function to initialize map entries with keys [0..2] and values set to 0. +# Parameters: +# $1: Map name +# $2: bpftool path +initialize_map_entries() { + local map_name="$1" + local bpftool_path="$2" + + for key in 0 1 2; do + "$bpftool_path" map update name "$map_name" key $key 0 0 0 value 0 0 0 $key + done +} + +# Test read access to the map. +# Parameters: +# $1: Name command (name/pinned) +# $2: Map name +# $3: bpftool path +# $4: key +access_for_read() { + local name_cmd="$1" + local map_name="$2" + local bpftool_path="$3" + local key="$4" + + # Test read access to the map. + if ! "$bpftool_path" map lookup "$name_cmd" "$map_name" key $key 1>/dev/null; then + echo " Read access to $key in $map_name failed" + exit 1 + fi + + # Test read access to map's BTF data. + if ! "$bpftool_path" btf dump map "$name_cmd" "$map_name" 1>/dev/null; then + echo " Read access to $map_name for BTF data failed" + exit 1 + fi +} + +# Test write access to the map. +# Parameters: +# $1: Name command (name/pinned) +# $2: Map name +# $3: bpftool path +# $4: key +# $5: Whether write should succeed (true/false) +access_for_write() { + local name_cmd="$1" + local map_name="$2" + local bpftool_path="$3" + local key="$4" + local write_should_succeed="$5" + local value="1 1 1 1" + + if "$bpftool_path" map update "$name_cmd" "$map_name" key $key value \ + $value 2>/dev/null; then + if [ "$write_should_succeed" = "false" ]; then + echo " Write access to $key in $map_name succeeded but should have failed" + exit 1 + fi + else + if [ "$write_should_succeed" = "true" ]; then + echo " Write access to $key in $map_name failed but should have succeeded" + exit 1 + fi + fi +} + +# Test entry deletion for the map. +# Parameters: +# $1: Name command (name/pinned) +# $2: Map name +# $3: bpftool path +# $4: key +# $5: Whether write should succeed (true/false) +access_for_deletion() { + local name_cmd="$1" + local map_name="$2" + local bpftool_path="$3" + local key="$4" + local write_should_succeed="$5" + local value="1 1 1 1" + + # Test deletion by key for the map. + # Before deleting, check the key exists. + if ! "$bpftool_path" map lookup "$name_cmd" "$map_name" key $key 1>/dev/null; then + echo " Key $key does not exist in $map_name" + exit 1 + fi + + # Delete by key. + if "$bpftool_path" map delete "$name_cmd" "$map_name" key $key 2>/dev/null; then + if [ "$write_should_succeed" = "false" ]; then + echo " Deletion for $key in $map_name succeeded but should have failed" + exit 1 + fi + else + if [ "$write_should_succeed" = "true" ]; then + echo " Deletion for $key in $map_name failed but should have succeeded" + exit 1 + fi + fi + + # After deleting, check the entry existence according to the expected status. + if "$bpftool_path" map lookup "$name_cmd" "$map_name" key $key 1>/dev/null; then + if [ "$write_should_succeed" = "true" ]; then + echo " Key $key for $map_name was not deleted but should have been deleted" + exit 1 + fi + else + if [ "$write_should_succeed" = "false" ]; then + echo "Key $key for $map_name was deleted but should have not been deleted" + exit 1 + fi + fi + + # Test creation of map's deleted entry, if deletion was successful. + # Otherwise, the entry exists. + if "$bpftool_path" map update "$name_cmd" "$map_name" key $key value \ + $value 2>/dev/null; then + if [ "$write_should_succeed" = "false" ]; then + echo " Write access to $key in $map_name succeeded after deletion attempt but should have failed" + exit 1 + fi + else + if [ "$write_should_succeed" = "true" ]; then + echo " Write access to $key in $map_name failed after deletion attempt but should have succeeded" + exit 1 + fi + fi +} + +# Test map elements iterator. +# Parameters: +# $1: Name command (name/pinned) +# $2: Map name +# $3: bpftool path +# $4: BPF_DIR +# $5: bpf iterator object file path +iterate_map_elem() { + local name_cmd="$1" + local map_name="$2" + local bpftool_path="$3" + local bpf_dir="$4" + local bpf_file="$5" + local pin_path="$bpf_dir/map_iterator" + + "$bpftool_path" iter pin "$bpf_file" "$pin_path" map "$name_cmd" "$map_name" + if [ ! -f "$pin_path" ]; then + echo " Failed to pin iterator to $pin_path" + exit 1 + fi + + cat "$pin_path" 1>/dev/null + rm "$pin_path" 2>/dev/null +} + +# Function to test map access with configurable write expectations +# Parameters: +# $1: Name command (name/pinned) +# $2: Map name +# $3: bpftool path +# $4: key for rw +# $5: key to delete +# $6: Whether write should succeed (true/false) +# $7: BPF_DIR +# $8: bpf iterator object file path +access_map() { + local name_cmd="$1" + local map_name="$2" + local bpftool_path="$3" + local key_for_rw="$4" + local key_to_del="$5" + local write_should_succeed="$6" + local bpf_dir="$7" + local bpf_iter_file_path="$8" + + access_for_read "$name_cmd" "$map_name" "$bpftool_path" "$key_for_rw" + access_for_write "$name_cmd" "$map_name" "$bpftool_path" "$key_for_rw" \ + "$write_should_succeed" + access_for_deletion "$name_cmd" "$map_name" "$bpftool_path" "$key_to_del" \ + "$write_should_succeed" + iterate_map_elem "$name_cmd" "$map_name" "$bpftool_path" "$bpf_dir" \ + "$bpf_iter_file_path" +} + +# Function to test map access with configurable write expectations +# Parameters: +# $1: Map name +# $2: bpftool path +# $3: BPF_DIR +# $4: Whether write should succeed (true/false) +# $5: bpf iterator object file path +test_map_access() { + local map_name="$1" + local bpftool_path="$2" + local bpf_dir="$3" + local pin_path="$bpf_dir/${map_name}_pinned" + local write_should_succeed="$4" + local bpf_iter_file_path="$5" + + # Test access to the map by name. + access_map "name" "$map_name" "$bpftool_path" "0 0 0 0" "1 0 0 0" \ + "$write_should_succeed" "$bpf_dir" "$bpf_iter_file_path" + + # Pin the map to the BPF filesystem + "$bpftool_path" map pin name "$map_name" "$pin_path" + if [ ! -e "$pin_path" ]; then + echo " Failed to pin $map_name" + exit 1 + fi + + # Test access to the pinned map. + access_map "pinned" "$pin_path" "$bpftool_path" "0 0 0 0" "2 0 0 0" \ + "$write_should_succeed" "$bpf_dir" "$bpf_iter_file_path" +} + +# Function to test map creation and map-of-maps +# Parameters: +# $1: bpftool path +# $2: BPF_DIR +test_map_creation_and_map_of_maps() { + local bpftool_path="$1" + local bpf_dir="$2" + local outer_map_name="outer_map_tt" + local inner_map_name="inner_map_tt" + + "$bpftool_path" map create "$bpf_dir/$inner_map_name" type array key 4 \ + value 4 entries 4 name "$inner_map_name" + if [ ! -f "$bpf_dir/$inner_map_name" ]; then + echo " Failed to create inner map file at $bpf_dir/$outer_map_name" + return 1 + fi + + "$bpftool_path" map create "$bpf_dir/$outer_map_name" type hash_of_maps \ + key 4 value 4 entries 2 name "$outer_map_name" inner_map name "$inner_map_name" + if [ ! -f "$bpf_dir/$outer_map_name" ]; then + echo " Failed to create outer map file at $bpf_dir/$outer_map_name" + return 1 + fi + + # Add entries to the outer map by name and by pinned path. + "$bpftool_path" map update pinned "$bpf_dir/$outer_map_name" key 0 0 0 0 \ + value pinned "$bpf_dir/$inner_map_name" + "$bpftool_path" map update name "$outer_map_name" key 1 0 0 0 value \ + name "$inner_map_name" + + # The outer map should be full by now. + # The following map update command is expected to fail. + if "$bpftool_path" map update name "$outer_map_name" key 2 0 0 0 value name \ + "$inner_map_name" 2>/dev/null; then + echo " Update for $outer_map_name succeeded but should have failed" + exit 1 + fi +} + +# Function to test map access with the btf list command +# Parameters: +# $1: bpftool path +test_map_access_with_btf_list() { + local bpftool_path="$1" + + # The btf list command iterates over maps for + # loaded BPF programs. + if ! "$bpftool_path" btf list 1>/dev/null; then + echo " Failed to access btf data" + exit 1 + fi +} + +set -eu + +trap cleanup_skip EXIT + +check_root_privileges + +verify_bpftool_path "$BPFTOOL_PATH" + +verify_btf_support + +trap cleanup EXIT + +# Load and attach the BPF programs to control maps access. +"$BPFTOOL_PATH" prog loadall "$BPF_FILE_PATH" "$BPF_DIR" autoattach + +initialize_map_entries "$PROTECTED_MAP_NAME" "$BPFTOOL_PATH" +initialize_map_entries "$NOT_PROTECTED_MAP_NAME" "$BPFTOOL_PATH" + +# Activate the map protection mechanism. Protection status is controlled +# by a value stored in the prot_status_map at index 0. +"$BPFTOOL_PATH" map update name prot_status_map key 0 0 0 0 value 1 0 0 0 + +# Test protected map (write should fail). +test_map_access "$PROTECTED_MAP_NAME" "$BPFTOOL_PATH" "$BPF_DIR" "false" \ + "$BPF_ITER_FILE_PATH" + +# Test not protected map (write should succeed). +test_map_access "$NOT_PROTECTED_MAP_NAME" "$BPFTOOL_PATH" "$BPF_DIR" "true" \ + "$BPF_ITER_FILE_PATH" + +test_map_creation_and_map_of_maps "$BPFTOOL_PATH" "$BPF_DIR" + +test_map_access_with_btf_list "$BPFTOOL_PATH" + +exit 0 diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 9551d8d5f8f9..78423cf89e01 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -40,7 +40,7 @@ #define TEST_TAG_LOAD_MODE_PFX "comment:load_mode=" /* Warning: duplicated in bpf_misc.h */ -#define POINTER_VALUE 0xcafe4all +#define POINTER_VALUE 0xbadcafe #define TEST_DATA_LEN 64 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS @@ -318,20 +318,14 @@ static int parse_caps(const char *str, __u64 *val, const char *name) static int parse_retval(const char *str, int *val, const char *name) { - struct { - char *name; - int val; - } named_values[] = { - { "INT_MIN" , INT_MIN }, - { "POINTER_VALUE", POINTER_VALUE }, - { "TEST_DATA_LEN", TEST_DATA_LEN }, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(named_values); ++i) { - if (strcmp(str, named_values[i].name) != 0) - continue; - *val = named_values[i].val; + /* + * INT_MIN is defined as (-INT_MAX -1), i.e. it doesn't expand to a + * single int and cannot be parsed with strtol, so we handle it + * separately here. In addition, it expands to different expressions in + * different compilers so we use a prefixed _INT_MIN instead. + */ + if (strcmp(str, "_INT_MIN") == 0) { + *val = INT_MIN; return 0; } @@ -1103,9 +1097,9 @@ void run_subtest(struct test_loader *tester, } } - do_prog_test_run(bpf_program__fd(tprog), &retval, - bpf_program__type(tprog) == BPF_PROG_TYPE_SYSCALL ? true : false); - if (retval != subspec->retval && subspec->retval != POINTER_VALUE) { + err = do_prog_test_run(bpf_program__fd(tprog), &retval, + bpf_program__type(tprog) == BPF_PROG_TYPE_SYSCALL ? true : false); + if (!err && retval != subspec->retval && subspec->retval != POINTER_VALUE) { PRINT_FAIL("Unexpected retval: %d != %d\n", retval, subspec->retval); goto tobj_cleanup; } diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index fda7589c5023..0921939532c6 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -138,6 +138,18 @@ static int sched_next_online(int pid, int *next_to_try) return ret; } +/* Derive target_free from map_size, same as bpf_common_lru_populate */ +static unsigned int __tgt_size(unsigned int map_size) +{ + return (map_size / nr_cpus) / 2; +} + +/* Inverse of how bpf_common_lru_populate derives target_free from map_size. */ +static unsigned int __map_size(unsigned int tgt_free) +{ + return tgt_free * nr_cpus * 2; +} + /* Size of the LRU map is 2 * Add key=1 (+1 key) * Add key=2 (+1 key) @@ -231,11 +243,11 @@ static void test_lru_sanity0(int map_type, int map_flags) printf("Pass\n"); } -/* Size of the LRU map is 1.5*tgt_free - * Insert 1 to tgt_free (+tgt_free keys) - * Lookup 1 to tgt_free/2 - * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys) - * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU +/* Verify that unreferenced elements are recycled before referenced ones. + * Insert elements. + * Reference a subset of these. + * Insert more, enough to trigger recycling. + * Verify that unreferenced are recycled. */ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) { @@ -257,7 +269,7 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) batch_size = tgt_free / 2; assert(batch_size * 2 == tgt_free); - map_size = tgt_free + batch_size; + map_size = __map_size(tgt_free) + batch_size; lru_map_fd = create_map(map_type, map_flags, map_size); assert(lru_map_fd != -1); @@ -266,13 +278,13 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) value[0] = 1234; - /* Insert 1 to tgt_free (+tgt_free keys) */ - end_key = 1 + tgt_free; + /* Insert map_size - batch_size keys */ + end_key = 1 + __map_size(tgt_free); for (key = 1; key < end_key; key++) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); - /* Lookup 1 to tgt_free/2 */ + /* Lookup 1 to batch_size */ end_key = 1 + batch_size; for (key = 1; key < end_key; key++) { assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); @@ -280,12 +292,13 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) BPF_NOEXIST)); } - /* Insert 1+tgt_free to 2*tgt_free - * => 1+tgt_free/2 to LOCALFREE_TARGET will be + /* Insert another map_size - batch_size keys + * Map will contain 1 to batch_size plus these latest, i.e., + * => previous 1+batch_size to map_size - batch_size will have been * removed by LRU */ - key = 1 + tgt_free; - end_key = key + tgt_free; + key = 1 + __map_size(tgt_free); + end_key = key + __map_size(tgt_free); for (; key < end_key; key++) { assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); @@ -301,17 +314,8 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) printf("Pass\n"); } -/* Size of the LRU map 1.5 * tgt_free - * Insert 1 to tgt_free (+tgt_free keys) - * Update 1 to tgt_free/2 - * => The original 1 to tgt_free/2 will be removed due to - * the LRU shrink process - * Re-insert 1 to tgt_free/2 again and do a lookup immeidately - * Insert 1+tgt_free to tgt_free*3/2 - * Insert 1+tgt_free*3/2 to tgt_free*5/2 - * => Key 1+tgt_free to tgt_free*3/2 - * will be removed from LRU because it has never - * been lookup and ref bit is not set +/* Verify that insertions exceeding map size will recycle the oldest. + * Verify that unreferenced elements are recycled before referenced. */ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) { @@ -334,7 +338,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) batch_size = tgt_free / 2; assert(batch_size * 2 == tgt_free); - map_size = tgt_free + batch_size; + map_size = __map_size(tgt_free) + batch_size; lru_map_fd = create_map(map_type, map_flags, map_size); assert(lru_map_fd != -1); @@ -343,8 +347,8 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) value[0] = 1234; - /* Insert 1 to tgt_free (+tgt_free keys) */ - end_key = 1 + tgt_free; + /* Insert map_size - batch_size keys */ + end_key = 1 + __map_size(tgt_free); for (key = 1; key < end_key; key++) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); @@ -357,8 +361,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) * shrink the inactive list to get tgt_free * number of free nodes. * - * Hence, the oldest key 1 to tgt_free/2 - * are removed from the LRU list. + * Hence, the oldest key is removed from the LRU list. */ key = 1; if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { @@ -370,8 +373,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) BPF_EXIST)); } - /* Re-insert 1 to tgt_free/2 again and do a lookup - * immeidately. + /* Re-insert 1 to batch_size again and do a lookup immediately. */ end_key = 1 + batch_size; value[0] = 4321; @@ -387,17 +389,18 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) value[0] = 1234; - /* Insert 1+tgt_free to tgt_free*3/2 */ - end_key = 1 + tgt_free + batch_size; - for (key = 1 + tgt_free; key < end_key; key++) + /* Insert batch_size new elements */ + key = 1 + __map_size(tgt_free); + end_key = key + batch_size; + for (; key < end_key; key++) /* These newly added but not referenced keys will be * gone during the next LRU shrink. */ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); - /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */ - end_key = key + tgt_free; + /* Insert map_size - batch_size elements */ + end_key += __map_size(tgt_free); for (; key < end_key; key++) { assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); @@ -413,12 +416,12 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) printf("Pass\n"); } -/* Size of the LRU map is 2*tgt_free - * It is to test the active/inactive list rotation - * Insert 1 to 2*tgt_free (+2*tgt_free keys) - * Lookup key 1 to tgt_free*3/2 - * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys) - * => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU +/* Test the active/inactive list rotation + * + * Fill the whole map, deplete the free list. + * Reference all except the last lru->target_free elements. + * Insert lru->target_free new elements. This triggers one shrink. + * Verify that the non-referenced elements are replaced. */ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) { @@ -437,8 +440,7 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) assert(sched_next_online(0, &next_cpu) != -1); - batch_size = tgt_free / 2; - assert(batch_size * 2 == tgt_free); + batch_size = __tgt_size(tgt_free); map_size = tgt_free * 2; lru_map_fd = create_map(map_type, map_flags, map_size); @@ -449,23 +451,21 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) value[0] = 1234; - /* Insert 1 to 2*tgt_free (+2*tgt_free keys) */ - end_key = 1 + (2 * tgt_free); + /* Fill the map */ + end_key = 1 + map_size; for (key = 1; key < end_key; key++) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); - /* Lookup key 1 to tgt_free*3/2 */ - end_key = tgt_free + batch_size; + /* Reference all but the last batch_size */ + end_key = 1 + map_size - batch_size; for (key = 1; key < end_key; key++) { assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, BPF_NOEXIST)); } - /* Add 1+2*tgt_free to tgt_free*5/2 - * (+tgt_free/2 keys) - */ + /* Insert new batch_size: replaces the non-referenced elements */ key = 2 * tgt_free + 1; end_key = key + batch_size; for (; key < end_key; key++) { @@ -500,7 +500,8 @@ static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free) lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free * nr_cpus); else - lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free); + lru_map_fd = create_map(map_type, map_flags, + 3 * __map_size(tgt_free)); assert(lru_map_fd != -1); expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 986ce32b113a..3fae9ce46ca9 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -535,7 +535,7 @@ static void test_devmap_hash(unsigned int task, void *data) static void test_queuemap(unsigned int task, void *data) { const int MAP_SIZE = 32; - __u32 vals[MAP_SIZE + MAP_SIZE/2], val; + __u32 vals[MAP_SIZE + MAP_SIZE/2], val = 0; int fd, i; /* Fill test values to be used */ @@ -591,7 +591,7 @@ static void test_queuemap(unsigned int task, void *data) static void test_stackmap(unsigned int task, void *data) { const int MAP_SIZE = 32; - __u32 vals[MAP_SIZE + MAP_SIZE/2], val; + __u32 vals[MAP_SIZE + MAP_SIZE/2], val = 0; int fd, i; /* Fill test values to be used */ diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h index 870694f2a359..df2222a1806f 100644 --- a/tools/testing/selftests/bpf/test_progs.h +++ b/tools/testing/selftests/bpf/test_progs.h @@ -460,6 +460,34 @@ static inline void *u64_to_ptr(__u64 ptr) return (void *) (unsigned long) ptr; } +static inline __u32 id_from_prog_fd(int fd) +{ + struct bpf_prog_info prog_info = {}; + __u32 prog_info_len = sizeof(prog_info); + int err; + + err = bpf_obj_get_info_by_fd(fd, &prog_info, &prog_info_len); + if (!ASSERT_OK(err, "id_from_prog_fd")) + return 0; + + ASSERT_NEQ(prog_info.id, 0, "prog_info.id"); + return prog_info.id; +} + +static inline __u32 id_from_link_fd(int fd) +{ + struct bpf_link_info link_info = {}; + __u32 link_info_len = sizeof(link_info); + int err; + + err = bpf_link_get_info_by_fd(fd, &link_info, &link_info_len); + if (!ASSERT_OK(err, "id_from_link_fd")) + return 0; + + ASSERT_NEQ(link_info.id, 0, "link_info.id"); + return link_info.id; +} + int bpf_find_map(const char *test, struct bpf_object *obj, const char *name); int compare_map_keys(int map1_fd, int map2_fd); int compare_stack_ips(int smap_fd, int amap_fd, int stack_trace_len); diff --git a/tools/testing/selftests/bpf/unpriv_helpers.c b/tools/testing/selftests/bpf/unpriv_helpers.c index 220f6a963813..f997d7ec8fd0 100644 --- a/tools/testing/selftests/bpf/unpriv_helpers.c +++ b/tools/testing/selftests/bpf/unpriv_helpers.c @@ -1,15 +1,76 @@ // SPDX-License-Identifier: GPL-2.0-only +#include +#include #include #include #include #include +#include #include #include +#include #include "unpriv_helpers.h" -static bool get_mitigations_off(void) +static gzFile open_config(void) +{ + struct utsname uts; + char buf[PATH_MAX]; + gzFile config; + + if (uname(&uts)) { + perror("uname"); + goto config_gz; + } + + snprintf(buf, sizeof(buf), "/boot/config-%s", uts.release); + config = gzopen(buf, "rb"); + if (config) + return config; + fprintf(stderr, "gzopen %s: %s\n", buf, strerror(errno)); + +config_gz: + config = gzopen("/proc/config.gz", "rb"); + if (!config) + perror("gzopen /proc/config.gz"); + return config; +} + +static int config_contains(const char *pat) +{ + const char *msg; + char buf[1024]; + gzFile config; + int n, err; + + config = open_config(); + if (!config) + return -1; + + for (;;) { + if (!gzgets(config, buf, sizeof(buf))) { + msg = gzerror(config, &err); + if (err == Z_ERRNO) + perror("gzgets /proc/config.gz"); + else if (err != Z_OK) + fprintf(stderr, "gzgets /proc/config.gz: %s", msg); + gzclose(config); + return -1; + } + n = strlen(buf); + if (buf[n - 1] == '\n') + buf[n - 1] = 0; + if (strcmp(buf, pat) == 0) { + gzclose(config); + return 1; + } + } + gzclose(config); + return 0; +} + +static bool cmdline_contains(const char *pat) { char cmdline[4096], *c; int fd, ret = false; @@ -27,7 +88,7 @@ static bool get_mitigations_off(void) cmdline[sizeof(cmdline) - 1] = '\0'; for (c = strtok(cmdline, " \n"); c; c = strtok(NULL, " \n")) { - if (strncmp(c, "mitigations=off", strlen(c))) + if (strncmp(c, pat, strlen(c))) continue; ret = true; break; @@ -37,8 +98,21 @@ static bool get_mitigations_off(void) return ret; } +static int get_mitigations_off(void) +{ + int enabled_in_config; + + if (cmdline_contains("mitigations=off")) + return 1; + enabled_in_config = config_contains("CONFIG_CPU_MITIGATIONS=y"); + if (enabled_in_config < 0) + return -1; + return !enabled_in_config; +} + bool get_unpriv_disabled(void) { + int mitigations_off; bool disabled; char buf[2]; FILE *fd; @@ -52,5 +126,19 @@ bool get_unpriv_disabled(void) disabled = true; } - return disabled ? true : get_mitigations_off(); + if (disabled) + return true; + + /* + * Some unpriv tests rely on spectre mitigations being on. + * If mitigations are off or status can't be determined + * assume that unpriv tests are disabled. + */ + mitigations_off = get_mitigations_off(); + if (mitigations_off < 0) { + fprintf(stderr, + "Can't determine if mitigations are enabled, disabling unpriv tests."); + return true; + } + return mitigations_off; } diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index 18596ae0b0c1..f3492efc8834 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -2409,3 +2409,27 @@ .errstr_unpriv = "", .prog_type = BPF_PROG_TYPE_CGROUP_SKB, }, +{ + "calls: several args with ref_obj_id", + .insns = { + /* Reserve at least sizeof(struct iphdr) bytes in the ring buffer. + * With a smaller size, the verifier would reject the call to + * bpf_tcp_raw_gen_syncookie_ipv4 before we can reach the + * ref_obj_id error. + */ + BPF_MOV64_IMM(BPF_REG_2, 20), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve), + /* if r0 == 0 goto */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tcp_raw_gen_syncookie_ipv4), + BPF_EXIT_INSN(), + }, + .fixup_map_ringbuf = { 2 }, + .result = REJECT, + .errstr = "more than one arg with ref_obj_id", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, diff --git a/tools/testing/selftests/bpf/verifier/dead_code.c b/tools/testing/selftests/bpf/verifier/dead_code.c index ee454327e5c6..77207b498c6f 100644 --- a/tools/testing/selftests/bpf/verifier/dead_code.c +++ b/tools/testing/selftests/bpf/verifier/dead_code.c @@ -2,14 +2,13 @@ "dead code: start", .insns = { BPF_JMP_IMM(BPF_JA, 0, 0, 2), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_JMP_IMM(BPF_JA, 0, 0, 2), BPF_MOV64_IMM(BPF_REG_0, 7), BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 10, -4), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 7, }, diff --git a/tools/testing/selftests/bpf/verifier/jmp32.c b/tools/testing/selftests/bpf/verifier/jmp32.c index 43776f6f92f4..91d83e9cb148 100644 --- a/tools/testing/selftests/bpf/verifier/jmp32.c +++ b/tools/testing/selftests/bpf/verifier/jmp32.c @@ -84,11 +84,10 @@ BPF_JMP32_IMM(BPF_JSET, BPF_REG_7, 0x10, 1), BPF_EXIT_INSN(), BPF_JMP32_IMM(BPF_JGE, BPF_REG_7, 0x10, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -149,11 +148,10 @@ BPF_JMP32_IMM(BPF_JEQ, BPF_REG_7, 0x10, 1), BPF_EXIT_INSN(), BPF_JMP32_IMM(BPF_JSGE, BPF_REG_7, 0xf, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -214,11 +212,10 @@ BPF_JMP32_IMM(BPF_JNE, BPF_REG_7, 0x10, 1), BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x10, 1), BPF_EXIT_INSN(), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -283,11 +280,10 @@ BPF_JMP32_REG(BPF_JGE, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP32_IMM(BPF_JGE, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -354,11 +350,10 @@ BPF_JMP32_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JGT, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -425,11 +420,10 @@ BPF_JMP32_REG(BPF_JLE, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP32_IMM(BPF_JLE, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -496,11 +490,10 @@ BPF_JMP32_REG(BPF_JLT, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JSLT, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -567,11 +560,10 @@ BPF_JMP32_REG(BPF_JSGE, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -638,11 +630,10 @@ BPF_JMP32_REG(BPF_JSGT, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JSGT, BPF_REG_7, -2, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -709,11 +700,10 @@ BPF_JMP32_REG(BPF_JSLE, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JSLE, BPF_REG_7, 0x7ffffff0, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -780,11 +770,10 @@ BPF_JMP32_REG(BPF_JSLT, BPF_REG_7, BPF_REG_8, 1), BPF_EXIT_INSN(), BPF_JMP32_IMM(BPF_JSLT, BPF_REG_7, -1, 1), + /* unpriv: nospec (inserted to prevent "R0 invalid mem access 'scalar'") */ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'scalar'", - .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, diff --git a/tools/testing/selftests/bpf/verifier/jset.c b/tools/testing/selftests/bpf/verifier/jset.c index 11fc68da735e..e901eefd774a 100644 --- a/tools/testing/selftests/bpf/verifier/jset.c +++ b/tools/testing/selftests/bpf/verifier/jset.c @@ -78,12 +78,11 @@ .insns = { BPF_MOV64_IMM(BPF_REG_0, 1), BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .retval = 1, .result = ACCEPT, }, @@ -136,13 +135,12 @@ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32), BPF_ALU64_IMM(BPF_OR, BPF_REG_0, 2), BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 3, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -154,16 +152,16 @@ BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xff), BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0xf0, 3), BPF_JMP_IMM(BPF_JLT, BPF_REG_1, 0x10, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0x10, 1), BPF_EXIT_INSN(), BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0x10, 1), + /* unpriv: nospec (inserted to prevent "R9 !read_ok") */ BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0), BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, - .errstr_unpriv = "R9 !read_ok", - .result_unpriv = REJECT, .result = ACCEPT, }, diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index b2bb20b00952..d532dd82a3a8 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -49,6 +50,7 @@ enum stat_id { STACK, PROG_TYPE, ATTACH_TYPE, + MEMORY_PEAK, FILE_NAME, PROG_NAME, @@ -155,13 +157,27 @@ struct filter { bool abs; }; -struct var_preset { - char *name; +struct rvalue { enum { INTEGRAL, ENUMERATOR } type; union { long long ivalue; char *svalue; }; +}; + +struct field_access { + enum { FIELD_NAME, ARRAY_INDEX } type; + union { + char *name; + struct rvalue index; + }; +}; + +struct var_preset { + struct field_access *atoms; + int atom_count; + char *full_name; + struct rvalue value; bool applied; }; @@ -208,6 +224,9 @@ static struct env { int top_src_lines; struct var_preset *presets; int npresets; + char orig_cgroup[PATH_MAX]; + char stat_cgroup[PATH_MAX]; + int memory_peak_fd; } env; static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) @@ -219,6 +238,22 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va return vfprintf(stderr, format, args); } +#define log_errno(fmt, ...) log_errno_aux(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +__attribute__((format(printf, 3, 4))) +static int log_errno_aux(const char *file, int line, const char *fmt, ...) +{ + int err = -errno; + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file, line); + vfprintf(stderr, fmt, ap); + fprintf(stderr, " failed with error '%s'.\n", strerror(errno)); + va_end(ap); + return err; +} + #ifndef VERISTAT_VERSION #define VERISTAT_VERSION "" #endif @@ -344,6 +379,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) fprintf(stderr, "invalid top N specifier: %s\n", arg); argp_usage(state); } + break; case 'C': env.comparison_mode = true; break; @@ -734,13 +770,13 @@ static int append_file_from_file(const char *path) } static const struct stat_specs default_csv_output_spec = { - .spec_cnt = 14, + .spec_cnt = 15, .ids = { FILE_NAME, PROG_NAME, VERDICT, DURATION, TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, MAX_STATES_PER_INSN, MARK_READ_MAX_LEN, SIZE, JITED_SIZE, PROG_TYPE, ATTACH_TYPE, - STACK, + STACK, MEMORY_PEAK, }, }; @@ -781,6 +817,7 @@ static struct stat_def { [STACK] = {"Stack depth", {"stack_depth", "stack"}, }, [PROG_TYPE] = { "Program type", {"prog_type"}, }, [ATTACH_TYPE] = { "Attach type", {"attach_type", }, }, + [MEMORY_PEAK] = { "Peak memory (MiB)", {"mem_peak", }, }, }; static bool parse_stat_id_var(const char *name, size_t len, int *id, @@ -854,6 +891,18 @@ static bool is_desc_sym(char c) return c == 'v' || c == 'V' || c == '.' || c == '!' || c == '_'; } +static char *rtrim(char *str) +{ + int i; + + for (i = strlen(str) - 1; i > 0; --i) { + if (!isspace(str[i])) + break; + str[i] = '\0'; + } + return str; +} + static int parse_stat(const char *stat_name, struct stat_specs *specs) { int id; @@ -1182,6 +1231,7 @@ static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const ch case BPF_MAP_TYPE_TASK_STORAGE: case BPF_MAP_TYPE_INODE_STORAGE: case BPF_MAP_TYPE_CGROUP_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: break; case BPF_MAP_TYPE_STRUCT_OPS: mask_unrelated_struct_ops_progs(obj, map, prog); @@ -1278,16 +1328,243 @@ static int max_verifier_log_size(void) return log_size; } +static bool output_stat_enabled(int id) +{ + int i; + + for (i = 0; i < env.output_spec.spec_cnt; i++) + if (env.output_spec.ids[i] == id) + return true; + return false; +} + +__attribute__((format(printf, 2, 3))) +static int write_one_line(const char *file, const char *fmt, ...) +{ + int err, saved_errno; + va_list ap; + FILE *f; + + f = fopen(file, "w"); + if (!f) + return -1; + + va_start(ap, fmt); + errno = 0; + err = vfprintf(f, fmt, ap); + saved_errno = errno; + va_end(ap); + fclose(f); + errno = saved_errno; + return err < 0 ? -1 : 0; +} + +__attribute__((format(scanf, 3, 4))) +static int scanf_one_line(const char *file, int fields_expected, const char *fmt, ...) +{ + int res = 0, saved_errno = 0; + char *line = NULL; + size_t line_len; + va_list ap; + FILE *f; + + f = fopen(file, "r"); + if (!f) + return -1; + + va_start(ap, fmt); + while (getline(&line, &line_len, f) > 0) { + res = vsscanf(line, fmt, ap); + if (res == fields_expected) + goto out; + } + if (ferror(f)) { + saved_errno = errno; + res = -1; + } + +out: + va_end(ap); + free(line); + fclose(f); + errno = saved_errno; + return res; +} + +static void destroy_stat_cgroup(void) +{ + char buf[PATH_MAX]; + int err; + + close(env.memory_peak_fd); + + if (env.orig_cgroup[0]) { + snprintf(buf, sizeof(buf), "%s/cgroup.procs", env.orig_cgroup); + err = write_one_line(buf, "%d\n", getpid()); + if (err < 0) + log_errno("moving self to original cgroup %s\n", env.orig_cgroup); + } + + if (env.stat_cgroup[0]) { + err = rmdir(env.stat_cgroup); + if (err < 0) + log_errno("deletion of cgroup %s", env.stat_cgroup); + } + + env.memory_peak_fd = -1; + env.orig_cgroup[0] = 0; + env.stat_cgroup[0] = 0; +} + +/* + * Creates a cgroup at /sys/fs/cgroup/veristat-accounting-, + * moves current process to this cgroup. + */ +static void create_stat_cgroup(void) +{ + char cgroup_fs_mount[4096]; + char buf[4096]; + int err; + + env.memory_peak_fd = -1; + + if (!output_stat_enabled(MEMORY_PEAK)) + return; + + err = scanf_one_line("/proc/self/mounts", 2, "%*s %4095s cgroup2 %s", + cgroup_fs_mount, buf); + if (err != 2) { + if (err < 0) + log_errno("reading /proc/self/mounts"); + else if (!env.quiet) + fprintf(stderr, "Can't find cgroupfs v2 mount point.\n"); + goto err_out; + } + + /* cgroup-v2.rst promises the line "0::" for cgroups v2 */ + err = scanf_one_line("/proc/self/cgroup", 1, "0::%4095s", buf); + if (err != 1) { + if (err < 0) + log_errno("reading /proc/self/cgroup"); + else if (!env.quiet) + fprintf(stderr, "Can't infer veristat process cgroup."); + goto err_out; + } + + snprintf(env.orig_cgroup, sizeof(env.orig_cgroup), "%s/%s", cgroup_fs_mount, buf); + + snprintf(buf, sizeof(buf), "%s/veristat-accounting-%d", cgroup_fs_mount, getpid()); + err = mkdir(buf, 0777); + if (err < 0) { + log_errno("creation of cgroup %s", buf); + goto err_out; + } + strcpy(env.stat_cgroup, buf); + + snprintf(buf, sizeof(buf), "%s/cgroup.procs", env.stat_cgroup); + err = write_one_line(buf, "%d\n", getpid()); + if (err < 0) { + log_errno("entering cgroup %s", buf); + goto err_out; + } + + snprintf(buf, sizeof(buf), "%s/memory.peak", env.stat_cgroup); + env.memory_peak_fd = open(buf, O_RDWR | O_APPEND); + if (env.memory_peak_fd < 0) { + log_errno("opening %s", buf); + goto err_out; + } + + return; + +err_out: + if (!env.quiet) + fprintf(stderr, "Memory usage metric unavailable.\n"); + destroy_stat_cgroup(); +} + +/* Current value of /sys/fs/cgroup/veristat-accounting-/memory.peak */ +static long cgroup_memory_peak(void) +{ + long err, memory_peak; + char buf[32]; + + if (env.memory_peak_fd < 0) + return -1; + + err = pread(env.memory_peak_fd, buf, sizeof(buf) - 1, 0); + if (err <= 0) { + log_errno("pread(%s/memory.peak)", env.stat_cgroup); + return -1; + } + + buf[err] = 0; + errno = 0; + memory_peak = strtoll(buf, NULL, 10); + if (errno) { + log_errno("%s/memory.peak:strtoll(%s)", env.stat_cgroup, buf); + return -1; + } + + return memory_peak; +} + +static int reset_stat_cgroup(void) +{ + char buf[] = "r\n"; + int err; + + if (env.memory_peak_fd < 0) + return -1; + + err = pwrite(env.memory_peak_fd, buf, sizeof(buf), 0); + if (err <= 0) { + log_errno("pwrite(%s/memory.peak)", env.stat_cgroup); + return -1; + } + return 0; +} + +static int parse_rvalue(const char *val, struct rvalue *rvalue) +{ + long long value; + char *val_end; + + if (val[0] == '-' || isdigit(val[0])) { + /* must be a number */ + errno = 0; + value = strtoll(val, &val_end, 0); + if (errno == ERANGE) { + errno = 0; + value = strtoull(val, &val_end, 0); + } + if (errno || *val_end != '\0') { + fprintf(stderr, "Failed to parse value '%s'\n", val); + return -EINVAL; + } + rvalue->ivalue = value; + rvalue->type = INTEGRAL; + } else { + /* if not a number, consider it enum value */ + rvalue->svalue = strdup(val); + if (!rvalue->svalue) + return -ENOMEM; + rvalue->type = ENUMERATOR; + } + return 0; +} + static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog) { const char *base_filename = basename(strdupa(filename)); const char *prog_name = bpf_program__name(prog); + long mem_peak_a, mem_peak_b, mem_peak = -1; char *buf; int buf_sz, log_level; struct verif_stats *stats; struct bpf_prog_info info; __u32 info_len = sizeof(info); - int err = 0; + int err = 0, cgroup_err; void *tmp; int fd; @@ -1332,7 +1609,15 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf if (env.force_reg_invariants) bpf_program__set_flags(prog, bpf_program__flags(prog) | BPF_F_TEST_REG_INVARIANTS); - err = bpf_object__load(obj); + err = bpf_object__prepare(obj); + if (!err) { + cgroup_err = reset_stat_cgroup(); + mem_peak_a = cgroup_memory_peak(); + err = bpf_object__load(obj); + mem_peak_b = cgroup_memory_peak(); + if (!cgroup_err && mem_peak_a >= 0 && mem_peak_b >= 0) + mem_peak = mem_peak_b - mem_peak_a; + } env.progs_processed++; stats->file_name = strdup(base_filename); @@ -1341,6 +1626,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf stats->stats[SIZE] = bpf_program__insn_cnt(prog); stats->stats[PROG_TYPE] = bpf_program__type(prog); stats->stats[ATTACH_TYPE] = bpf_program__expected_attach_type(prog); + stats->stats[MEMORY_PEAK] = mem_peak < 0 ? -1 : mem_peak / (1024 * 1024); memset(&info, 0, info_len); fd = bpf_program__fd(prog); @@ -1361,15 +1647,74 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf free(buf); return 0; -}; +} + +static int append_preset_atom(struct var_preset *preset, char *value, bool is_index) +{ + struct field_access *tmp; + int i = preset->atom_count; + int err; + + tmp = reallocarray(preset->atoms, i + 1, sizeof(*preset->atoms)); + if (!tmp) + return -ENOMEM; + + preset->atoms = tmp; + preset->atom_count++; + + if (is_index) { + preset->atoms[i].type = ARRAY_INDEX; + err = parse_rvalue(value, &preset->atoms[i].index); + if (err) + return err; + } else { + preset->atoms[i].type = FIELD_NAME; + preset->atoms[i].name = strdup(value); + if (!preset->atoms[i].name) + return -ENOMEM; + } + return 0; +} + +static int parse_var_atoms(const char *full_var, struct var_preset *preset) +{ + char expr[256], var[256], *name, *saveptr; + int n, len, off, err; + + snprintf(expr, sizeof(expr), "%s", full_var); + preset->atom_count = 0; + while ((name = strtok_r(preset->atom_count ? NULL : expr, ".", &saveptr))) { + len = strlen(name); + /* parse variable name */ + if (sscanf(name, "%[a-zA-Z0-9_] %n", var, &off) != 1) { + fprintf(stderr, "Can't parse %s\n", name); + return -EINVAL; + } + err = append_preset_atom(preset, var, false); + if (err) + return err; + + /* parse optional array indexes */ + while (off < len) { + if (sscanf(name + off, " [ %[a-zA-Z0-9_] ] %n", var, &n) != 1) { + fprintf(stderr, "Can't parse %s as index\n", name + off); + return -EINVAL; + } + err = append_preset_atom(preset, var, true); + if (err) + return err; + off += n; + } + } + return 0; +} static int append_var_preset(struct var_preset **presets, int *cnt, const char *expr) { void *tmp; struct var_preset *cur; - char var[256], val[256], *val_end; - long long value; - int n; + char var[256], val[256]; + int n, err; tmp = realloc(*presets, (*cnt + 1) * sizeof(**presets)); if (!tmp) @@ -1379,37 +1724,25 @@ static int append_var_preset(struct var_preset **presets, int *cnt, const char * memset(cur, 0, sizeof(*cur)); (*cnt)++; - if (sscanf(expr, "%s = %s %n", var, val, &n) != 2 || n != strlen(expr)) { + if (sscanf(expr, " %[][a-zA-Z0-9_. ] = %s %n", var, val, &n) != 2 || n != strlen(expr)) { fprintf(stderr, "Failed to parse expression '%s'\n", expr); return -EINVAL; } + /* Remove trailing spaces from var, as scanf may add those */ + rtrim(var); - if (val[0] == '-' || isdigit(val[0])) { - /* must be a number */ - errno = 0; - value = strtoll(val, &val_end, 0); - if (errno == ERANGE) { - errno = 0; - value = strtoull(val, &val_end, 0); - } - if (errno || *val_end != '\0') { - fprintf(stderr, "Failed to parse value '%s'\n", val); - return -EINVAL; - } - cur->ivalue = value; - cur->type = INTEGRAL; - } else { - /* if not a number, consider it enum value */ - cur->svalue = strdup(val); - if (!cur->svalue) - return -ENOMEM; - cur->type = ENUMERATOR; - } + err = parse_rvalue(val, &cur->value); + if (err) + return err; - cur->name = strdup(var); - if (!cur->name) + cur->full_name = strdup(var); + if (!cur->full_name) return -ENOMEM; + err = parse_var_atoms(var, cur); + if (err) + return err; + return 0; } @@ -1486,22 +1819,96 @@ static bool is_preset_supported(const struct btf_type *t) return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t); } -const int btf_find_member(const struct btf *btf, - const struct btf_type *parent_type, - __u32 parent_offset, - const char *member_name, - int *member_tid, - __u32 *member_offset) +static int find_enum_value(const struct btf *btf, const char *name, long long *value) +{ + const struct btf_type *t; + int cnt, i; + long long lvalue; + + cnt = btf__type_cnt(btf); + for (i = 1; i != cnt; ++i) { + t = btf__type_by_id(btf, i); + + if (!btf_is_any_enum(t)) + continue; + + if (enum_value_from_name(btf, t, name, &lvalue) == 0) { + *value = lvalue; + return 0; + } + } + return -ESRCH; +} + +static int resolve_rvalue(struct btf *btf, const struct rvalue *rvalue, long long *result) +{ + int err = 0; + + switch (rvalue->type) { + case INTEGRAL: + *result = rvalue->ivalue; + return 0; + case ENUMERATOR: + err = find_enum_value(btf, rvalue->svalue, result); + if (err) { + fprintf(stderr, "Can't resolve enum value %s\n", rvalue->svalue); + return err; + } + return 0; + default: + fprintf(stderr, "Unknown rvalue type\n"); + return -EOPNOTSUPP; + } + return 0; +} + +static int adjust_var_secinfo_array(struct btf *btf, int tid, struct field_access *atom, + const char *array_name, struct btf_var_secinfo *sinfo) +{ + const struct btf_type *t; + struct btf_array *barr; + long long idx; + int err; + + tid = btf__resolve_type(btf, tid); + t = btf__type_by_id(btf, tid); + if (!btf_is_array(t)) { + fprintf(stderr, "Array index is not expected for %s\n", + array_name); + return -EINVAL; + } + barr = btf_array(t); + err = resolve_rvalue(btf, &atom->index, &idx); + if (err) + return err; + if (idx < 0 || idx >= barr->nelems) { + fprintf(stderr, "Array index %lld is out of bounds [0, %u): %s\n", + idx, barr->nelems, array_name); + return -EINVAL; + } + sinfo->size = btf__resolve_size(btf, barr->type); + sinfo->offset += sinfo->size * idx; + sinfo->type = btf__resolve_type(btf, barr->type); + return 0; +} + +static int adjust_var_secinfo_member(const struct btf *btf, + const struct btf_type *parent_type, + __u32 parent_offset, + const char *member_name, + struct btf_var_secinfo *sinfo) { int i; - if (!btf_is_composite(parent_type)) + if (!btf_is_composite(parent_type)) { + fprintf(stderr, "Can't resolve field %s for non-composite type\n", member_name); return -EINVAL; + } for (i = 0; i < btf_vlen(parent_type); ++i) { const struct btf_member *member; const struct btf_type *member_type; - int tid; + int tid, off; member = btf_members(parent_type) + i; tid = btf__resolve_type(btf, member->type); @@ -1509,6 +1916,7 @@ const int btf_find_member(const struct btf *btf, return -EINVAL; member_type = btf__type_by_id(btf, tid); + off = parent_offset + member->offset; if (member->name_off) { const char *name = btf__name_by_offset(btf, member->name_off); @@ -1518,48 +1926,62 @@ const int btf_find_member(const struct btf *btf, name); return -EINVAL; } - *member_offset = parent_offset + member->offset; - *member_tid = tid; + sinfo->offset += off / 8; + sinfo->type = tid; + sinfo->size = member_type->size; return 0; } } else if (btf_is_composite(member_type)) { int err; - err = btf_find_member(btf, member_type, parent_offset + member->offset, - member_name, member_tid, member_offset); + err = adjust_var_secinfo_member(btf, member_type, off, + member_name, sinfo); if (!err) return 0; } } - return -EINVAL; + return -ESRCH; } static int adjust_var_secinfo(struct btf *btf, const struct btf_type *t, - struct btf_var_secinfo *sinfo, const char *var) + struct btf_var_secinfo *sinfo, struct var_preset *preset) { - char expr[256], *saveptr; - const struct btf_type *base_type, *member_type; - int err, member_tid; - char *name; - __u32 member_offset = 0; + const struct btf_type *base_type; + const char *prev_name; + int err, i; + int tid; - base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); - snprintf(expr, sizeof(expr), "%s", var); - strtok_r(expr, ".", &saveptr); + assert(preset->atom_count > 0); + assert(preset->atoms[0].type == FIELD_NAME); - while ((name = strtok_r(NULL, ".", &saveptr))) { - err = btf_find_member(btf, base_type, 0, name, &member_tid, &member_offset); - if (err) { - fprintf(stderr, "Could not find member %s for variable %s\n", name, var); - return err; + tid = btf__resolve_type(btf, t->type); + base_type = btf__type_by_id(btf, tid); + prev_name = preset->atoms[0].name; + + for (i = 1; i < preset->atom_count; ++i) { + struct field_access *atom = preset->atoms + i; + + switch (atom->type) { + case ARRAY_INDEX: + err = adjust_var_secinfo_array(btf, tid, atom, prev_name, sinfo); + break; + case FIELD_NAME: + err = adjust_var_secinfo_member(btf, base_type, 0, atom->name, sinfo); + if (err == -ESRCH) + fprintf(stderr, "Can't find '%s'\n", atom->name); + prev_name = atom->name; + break; + default: + fprintf(stderr, "Unknown field_access type\n"); + return -EOPNOTSUPP; } - member_type = btf__type_by_id(btf, member_tid); - sinfo->offset += member_offset / 8; - sinfo->size = member_type->size; - sinfo->type = member_tid; - base_type = member_type; + if (err) + return err; + base_type = btf__type_by_id(btf, sinfo->type); + tid = sinfo->type; } + return 0; } @@ -1569,7 +1991,7 @@ static int set_global_var(struct bpf_object *obj, struct btf *btf, { const struct btf_type *base_type; void *ptr; - long long value = preset->ivalue; + long long value = preset->value.ivalue; size_t size; base_type = btf__type_by_id(btf, btf__resolve_type(btf, sinfo->type)); @@ -1578,22 +2000,23 @@ static int set_global_var(struct bpf_object *obj, struct btf *btf, return -EINVAL; } if (!is_preset_supported(base_type)) { - fprintf(stderr, "Setting value for type %s is not supported\n", - btf__name_by_offset(btf, base_type->name_off)); + fprintf(stderr, "Can't set %s. Only ints and enums are supported\n", + preset->full_name); return -EINVAL; } - if (preset->type == ENUMERATOR) { + if (preset->value.type == ENUMERATOR) { if (btf_is_any_enum(base_type)) { - if (enum_value_from_name(btf, base_type, preset->svalue, &value)) { + if (enum_value_from_name(btf, base_type, preset->value.svalue, &value)) { fprintf(stderr, "Failed to find integer value for enum element %s\n", - preset->svalue); + preset->value.svalue); return -EINVAL; } } else { fprintf(stderr, "Value %s is not supported for type %s\n", - preset->svalue, btf__name_by_offset(btf, base_type->name_off)); + preset->value.svalue, + btf__name_by_offset(btf, base_type->name_off)); return -EINVAL; } } @@ -1660,20 +2083,16 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i for (j = 0; j < n; ++j, ++sinfo) { const struct btf_type *var_type = btf__type_by_id(btf, sinfo->type); const char *var_name; - int var_len; if (!btf_is_var(var_type)) continue; var_name = btf__name_by_offset(btf, var_type->name_off); - var_len = strlen(var_name); for (k = 0; k < npresets; ++k) { struct btf_var_secinfo tmp_sinfo; - if (strncmp(var_name, presets[k].name, var_len) != 0 || - (presets[k].name[var_len] != '\0' && - presets[k].name[var_len] != '.')) + if (strcmp(var_name, presets[k].atoms[0].name) != 0) continue; if (presets[k].applied) { @@ -1683,7 +2102,7 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i } tmp_sinfo = *sinfo; err = adjust_var_secinfo(btf, var_type, - &tmp_sinfo, presets[k].name); + &tmp_sinfo, presets + k); if (err) return err; @@ -1698,7 +2117,8 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i for (i = 0; i < npresets; ++i) { if (!presets[i].applied) { fprintf(stderr, "Global variable preset %s has not been applied\n", - presets[i].name); + presets[i].full_name); + err = -EINVAL; } presets[i].applied = false; } @@ -1824,6 +2244,7 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2, case TOTAL_STATES: case PEAK_STATES: case MAX_STATES_PER_INSN: + case MEMORY_PEAK: case MARK_READ_MAX_LEN: { long v1 = s1->stats[id]; long v2 = s2->stats[id]; @@ -2053,6 +2474,7 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id, case STACK: case SIZE: case JITED_SIZE: + case MEMORY_PEAK: *val = s ? s->stats[id] : 0; break; default: @@ -2139,6 +2561,7 @@ static int parse_stat_value(const char *str, enum stat_id id, struct verif_stats case MARK_READ_MAX_LEN: case SIZE: case JITED_SIZE: + case MEMORY_PEAK: case STACK: { long val; int err, n; @@ -2776,7 +3199,7 @@ static void output_prog_stats(void) static int handle_verif_mode(void) { - int i, err; + int i, err = 0; if (env.filename_cnt == 0) { fprintf(stderr, "Please provide path to BPF object file!\n\n"); @@ -2784,11 +3207,12 @@ static int handle_verif_mode(void) return -EINVAL; } + create_stat_cgroup(); for (i = 0; i < env.filename_cnt; i++) { err = process_obj(env.filenames[i]); if (err) { fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err); - return err; + goto out; } } @@ -2796,7 +3220,9 @@ static int handle_verif_mode(void) output_prog_stats(); - return 0; +out: + destroy_stat_cgroup(); + return err; } static int handle_replay_mode(void) @@ -2826,7 +3252,7 @@ static int handle_replay_mode(void) int main(int argc, char **argv) { - int err = 0, i; + int err = 0, i, j; if (argp_parse(&argp, argc, argv, 0, NULL, NULL)) return 1; @@ -2885,9 +3311,19 @@ int main(int argc, char **argv) } free(env.deny_filters); for (i = 0; i < env.npresets; ++i) { - free(env.presets[i].name); - if (env.presets[i].type == ENUMERATOR) - free(env.presets[i].svalue); + free(env.presets[i].full_name); + for (j = 0; j < env.presets[i].atom_count; ++j) { + switch (env.presets[i].atoms[j].type) { + case FIELD_NAME: + free(env.presets[i].atoms[j].name); + break; + case ARRAY_INDEX: + if (env.presets[i].atoms[j].index.type == ENUMERATOR) + free(env.presets[i].atoms[j].index.svalue); + break; + } + } + free(env.presets[i].atoms); } free(env.presets); return -err; diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh index 79505d294c44..2f869daf8a06 100755 --- a/tools/testing/selftests/bpf/vmtest.sh +++ b/tools/testing/selftests/bpf/vmtest.sh @@ -43,6 +43,15 @@ riscv64) BZIMAGE="arch/riscv/boot/Image" ARCH="riscv" ;; +ppc64el) + QEMU_BINARY=qemu-system-ppc64 + QEMU_CONSOLE="hvc0" + # KVM could not be tested for powerpc, therefore not enabled for now. + HOST_FLAGS=(-machine pseries -cpu POWER9) + CROSS_FLAGS=(-machine pseries -cpu POWER9) + BZIMAGE="vmlinux" + ARCH="powerpc" + ;; *) echo "Unsupported architecture" exit 1 diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c index 0ced4026ee44..a29de0713f19 100644 --- a/tools/testing/selftests/bpf/xskxceiver.c +++ b/tools/testing/selftests/bpf/xskxceiver.c @@ -109,6 +109,8 @@ #include +#define MAX_TX_BUDGET_DEFAULT 32 + static bool opt_verbose; static bool opt_print_tests; static enum test_mode opt_mode = TEST_MODE_ALL; @@ -1091,11 +1093,45 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len) return true; } +static u32 load_value(u32 *counter) +{ + return __atomic_load_n(counter, __ATOMIC_ACQUIRE); +} + +static bool kick_tx_with_check(struct xsk_socket_info *xsk, int *ret) +{ + u32 max_budget = MAX_TX_BUDGET_DEFAULT; + u32 cons, ready_to_send; + int delta; + + cons = load_value(xsk->tx.consumer); + ready_to_send = load_value(xsk->tx.producer) - cons; + *ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); + + delta = load_value(xsk->tx.consumer) - cons; + /* By default, xsk should consume exact @max_budget descs at one + * send in this case where hitting the max budget limit in while + * loop is triggered in __xsk_generic_xmit(). Please make sure that + * the number of descs to be sent is larger than @max_budget, or + * else the tx.consumer will be updated in xskq_cons_peek_desc() + * in time which hides the issue we try to verify. + */ + if (ready_to_send > max_budget && delta != max_budget) + return false; + + return true; +} + static int kick_tx(struct xsk_socket_info *xsk) { int ret; - ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); + if (xsk->check_consumer) { + if (!kick_tx_with_check(xsk, &ret)) + return TEST_FAILURE; + } else { + ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); + } if (ret >= 0) return TEST_PASS; if (errno == ENOBUFS || errno == EAGAIN || errno == EBUSY || errno == ENETDOWN) { @@ -2613,6 +2649,23 @@ static int testapp_adjust_tail_grow_mb(struct test_spec *test) XSK_UMEM__LARGE_FRAME_SIZE * 2); } +static int testapp_tx_queue_consumer(struct test_spec *test) +{ + int nr_packets; + + if (test->mode == TEST_MODE_ZC) { + ksft_test_result_skip("Can not run TX_QUEUE_CONSUMER test for ZC mode\n"); + return TEST_SKIP; + } + + nr_packets = MAX_TX_BUDGET_DEFAULT + 1; + pkt_stream_replace(test, nr_packets, MIN_PKT_SIZE); + test->ifobj_tx->xsk->batch_size = nr_packets; + test->ifobj_tx->xsk->check_consumer = true; + + return testapp_validate_traffic(test); +} + static void run_pkt_test(struct test_spec *test) { int ret; @@ -2723,6 +2776,7 @@ static const struct test_spec tests[] = { {.name = "XDP_ADJUST_TAIL_SHRINK_MULTI_BUFF", .test_func = testapp_adjust_tail_shrink_mb}, {.name = "XDP_ADJUST_TAIL_GROW", .test_func = testapp_adjust_tail_grow}, {.name = "XDP_ADJUST_TAIL_GROW_MULTI_BUFF", .test_func = testapp_adjust_tail_grow_mb}, + {.name = "TX_QUEUE_CONSUMER", .test_func = testapp_tx_queue_consumer}, }; static void print_tests(void) diff --git a/tools/testing/selftests/bpf/xskxceiver.h b/tools/testing/selftests/bpf/xskxceiver.h index 67fc44b2813b..4df3a5d329ac 100644 --- a/tools/testing/selftests/bpf/xskxceiver.h +++ b/tools/testing/selftests/bpf/xskxceiver.h @@ -95,6 +95,7 @@ struct xsk_socket_info { u32 batch_size; u8 dst_mac[ETH_ALEN]; u8 src_mac[ETH_ALEN]; + bool check_consumer; }; struct pkt { diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c index 8d275f03e977..8d233ac95696 100644 --- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c +++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c @@ -127,22 +127,42 @@ int run_test(int cpu) return KSFT_PASS; } +/* + * Reads the suspend success count from sysfs. + * Returns the count on success or exits on failure. + */ +static int get_suspend_success_count_or_fail(void) +{ + FILE *fp; + int val; + + fp = fopen("/sys/power/suspend_stats/success", "r"); + if (!fp) + ksft_exit_fail_msg( + "Failed to open suspend_stats/success: %s\n", + strerror(errno)); + + if (fscanf(fp, "%d", &val) != 1) { + fclose(fp); + ksft_exit_fail_msg( + "Failed to read suspend success count\n"); + } + + fclose(fp); + return val; +} + void suspend(void) { - int power_state_fd; int timerfd; int err; + int count_before; + int count_after; struct itimerspec spec = {}; if (getuid() != 0) ksft_exit_skip("Please run the test as root - Exiting.\n"); - power_state_fd = open("/sys/power/state", O_RDWR); - if (power_state_fd < 0) - ksft_exit_fail_msg( - "open(\"/sys/power/state\") failed %s)\n", - strerror(errno)); - timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); if (timerfd < 0) ksft_exit_fail_msg("timerfd_create() failed\n"); @@ -152,14 +172,15 @@ void suspend(void) if (err < 0) ksft_exit_fail_msg("timerfd_settime() failed\n"); + count_before = get_suspend_success_count_or_fail(); + system("(echo mem > /sys/power/state) 2> /dev/null"); - timerfd_gettime(timerfd, &spec); - if (spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0) + count_after = get_suspend_success_count_or_fail(); + if (count_after <= count_before) ksft_exit_fail_msg("Failed to enter Suspend state\n"); close(timerfd); - close(power_state_fd); } int main(int argc, char **argv) diff --git a/tools/testing/selftests/cachestat/test_cachestat.c b/tools/testing/selftests/cachestat/test_cachestat.c index 632ab44737ec..c952640f163b 100644 --- a/tools/testing/selftests/cachestat/test_cachestat.c +++ b/tools/testing/selftests/cachestat/test_cachestat.c @@ -33,6 +33,11 @@ void print_cachestat(struct cachestat *cs) cs->nr_evicted, cs->nr_recently_evicted); } +enum file_type { + FILE_MMAP, + FILE_SHMEM +}; + bool write_exactly(int fd, size_t filesize) { int random_fd = open("/dev/urandom", O_RDONLY); @@ -201,8 +206,20 @@ static int test_cachestat(const char *filename, bool write_random, bool create, out: return ret; } +const char *file_type_str(enum file_type type) +{ + switch (type) { + case FILE_SHMEM: + return "shmem"; + case FILE_MMAP: + return "mmap"; + default: + return "unknown"; + } +} -bool test_cachestat_shmem(void) + +bool run_cachestat_test(enum file_type type) { size_t PS = sysconf(_SC_PAGESIZE); size_t filesize = PS * 512 * 2; /* 2 2MB huge pages */ @@ -212,27 +229,50 @@ bool test_cachestat_shmem(void) char *filename = "tmpshmcstat"; struct cachestat cs; bool ret = true; + int fd; unsigned long num_pages = compute_len / PS; - int fd = shm_open(filename, O_CREAT | O_RDWR, 0600); + if (type == FILE_SHMEM) + fd = shm_open(filename, O_CREAT | O_RDWR, 0600); + else + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { - ksft_print_msg("Unable to create shmem file.\n"); + ksft_print_msg("Unable to create %s file.\n", + file_type_str(type)); ret = false; goto out; } if (ftruncate(fd, filesize)) { - ksft_print_msg("Unable to truncate shmem file.\n"); + ksft_print_msg("Unable to truncate %s file.\n",file_type_str(type)); ret = false; goto close_fd; } + switch (type) { + case FILE_SHMEM: + if (!write_exactly(fd, filesize)) { + ksft_print_msg("Unable to write to file.\n"); + ret = false; + goto close_fd; + } + break; + case FILE_MMAP: + char *map = mmap(NULL, filesize, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); - if (!write_exactly(fd, filesize)) { - ksft_print_msg("Unable to write to shmem file.\n"); + if (map == MAP_FAILED) { + ksft_print_msg("mmap failed.\n"); + ret = false; + goto close_fd; + } + for (int i = 0; i < filesize; i++) + map[i] = 'A'; + break; + default: + ksft_print_msg("Unsupported file type.\n"); ret = false; goto close_fd; } - syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0); if (syscall_ret) { @@ -308,12 +348,18 @@ int main(void) break; } - if (test_cachestat_shmem()) + if (run_cachestat_test(FILE_SHMEM)) ksft_test_result_pass("cachestat works with a shmem file\n"); else { ksft_test_result_fail("cachestat fails with a shmem file\n"); ret = 1; } + if (run_cachestat_test(FILE_MMAP)) + ksft_test_result_pass("cachestat works with a mmap file\n"); + else { + ksft_test_result_fail("cachestat fails with a mmap file\n"); + ret = 1; + } return ret; } diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index 8832f3d1cb61..0e89fcff4d05 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -19,6 +19,8 @@ #include "cgroup_util.h" #include "../../clone3/clone3_selftests.h" +bool cg_test_v1_named; + /* Returns read len on success, or -errno on failure. */ ssize_t read_text(const char *path, char *buf, size_t max_len) { @@ -361,7 +363,7 @@ int cg_enter_current(const char *cgroup) int cg_enter_current_thread(const char *cgroup) { - return cg_write(cgroup, "cgroup.threads", "0"); + return cg_write(cgroup, CG_THREADS_FILE, "0"); } int cg_run(const char *cgroup, diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h index adb2bc193183..c69cab66254b 100644 --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h @@ -13,6 +13,10 @@ #define TEST_UID 65534 /* usually nobody, any !root is fine */ +#define CG_THREADS_FILE (!cg_test_v1_named ? "cgroup.threads" : "tasks") +#define CG_NAMED_NAME "selftest" +#define CG_PATH_FORMAT (!cg_test_v1_named ? "0::%s" : (":name=" CG_NAMED_NAME ":%s")) + /* * Checks if two given values differ by less than err% of their sum. */ @@ -65,3 +69,4 @@ extern int dirfd_open_opath(const char *dir); extern int cg_prepare_for_wait(const char *cgroup); extern int memcg_prepare_for_wait(const char *cgroup); extern int cg_wait_for(int fd); +extern bool cg_test_v1_named; diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index a5672a91d273..a360e2eb2eef 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,6 +21,9 @@ #include "cgroup_util.h" static bool nsdelegate; +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0 +#endif static int touch_anon(char *buf, size_t size) { @@ -148,6 +153,9 @@ static int test_cgcore_populated(const char *root) int cgroup_fd = -EBADF; pid_t pid; + if (cg_test_v1_named) + return KSFT_SKIP; + cg_test_a = cg_name(root, "cg_test_a"); cg_test_b = cg_name(root, "cg_test_a/cg_test_b"); cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c"); @@ -277,6 +285,9 @@ static int test_cgcore_invalid_domain(const char *root) int ret = KSFT_FAIL; char *grandparent = NULL, *parent = NULL, *child = NULL; + if (cg_test_v1_named) + return KSFT_SKIP; + grandparent = cg_name(root, "cg_test_grandparent"); parent = cg_name(root, "cg_test_grandparent/cg_test_parent"); child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child"); @@ -339,6 +350,9 @@ static int test_cgcore_parent_becomes_threaded(const char *root) int ret = KSFT_FAIL; char *parent = NULL, *child = NULL; + if (cg_test_v1_named) + return KSFT_SKIP; + parent = cg_name(root, "cg_test_parent"); child = cg_name(root, "cg_test_parent/cg_test_child"); if (!parent || !child) @@ -378,7 +392,8 @@ static int test_cgcore_no_internal_process_constraint_on_threads(const char *roo int ret = KSFT_FAIL; char *parent = NULL, *child = NULL; - if (cg_read_strstr(root, "cgroup.controllers", "cpu") || + if (cg_test_v1_named || + cg_read_strstr(root, "cgroup.controllers", "cpu") || cg_write(root, "cgroup.subtree_control", "+cpu")) { ret = KSFT_SKIP; goto cleanup; @@ -430,6 +445,9 @@ static int test_cgcore_top_down_constraint_enable(const char *root) int ret = KSFT_FAIL; char *parent = NULL, *child = NULL; + if (cg_test_v1_named) + return KSFT_SKIP; + parent = cg_name(root, "cg_test_parent"); child = cg_name(root, "cg_test_parent/cg_test_child"); if (!parent || !child) @@ -465,6 +483,9 @@ static int test_cgcore_top_down_constraint_disable(const char *root) int ret = KSFT_FAIL; char *parent = NULL, *child = NULL; + if (cg_test_v1_named) + return KSFT_SKIP; + parent = cg_name(root, "cg_test_parent"); child = cg_name(root, "cg_test_parent/cg_test_child"); if (!parent || !child) @@ -506,6 +527,9 @@ static int test_cgcore_internal_process_constraint(const char *root) int ret = KSFT_FAIL; char *parent = NULL, *child = NULL; + if (cg_test_v1_named) + return KSFT_SKIP; + parent = cg_name(root, "cg_test_parent"); child = cg_name(root, "cg_test_parent/cg_test_child"); if (!parent || !child) @@ -573,7 +597,7 @@ static int test_cgcore_proc_migration(const char *root) } cg_enter_current(dst); - if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1) + if (cg_read_lc(dst, CG_THREADS_FILE) != n_threads + 1) goto cleanup; ret = KSFT_PASS; @@ -605,7 +629,7 @@ static void *migrating_thread_fn(void *arg) char lines[3][PATH_MAX]; for (g = 1; g < 3; ++g) - snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0])); + snprintf(lines[g], sizeof(lines[g]), CG_PATH_FORMAT, grps[g] + strlen(grps[0])); for (i = 0; i < n_iterations; ++i) { cg_enter_current_thread(grps[(i % 2) + 1]); @@ -642,10 +666,12 @@ static int test_cgcore_thread_migration(const char *root) if (cg_create(grps[2])) goto cleanup; - if (cg_write(grps[1], "cgroup.type", "threaded")) - goto cleanup; - if (cg_write(grps[2], "cgroup.type", "threaded")) - goto cleanup; + if (!cg_test_v1_named) { + if (cg_write(grps[1], "cgroup.type", "threaded")) + goto cleanup; + if (cg_write(grps[2], "cgroup.type", "threaded")) + goto cleanup; + } if (cg_enter_current(grps[1])) goto cleanup; @@ -659,7 +685,7 @@ static int test_cgcore_thread_migration(const char *root) if (retval) goto cleanup; - snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0])); + snprintf(line, sizeof(line), CG_PATH_FORMAT, grps[1] + strlen(grps[0])); if (proc_read_strstr(0, 1, "cgroup", line)) goto cleanup; @@ -842,6 +868,38 @@ static int test_cgcore_lesser_ns_open(const char *root) return ret; } +static int setup_named_v1_root(char *root, size_t len, const char *name) +{ + char options[PATH_MAX]; + int r; + + r = snprintf(root, len, "/mnt/cg_selftest"); + if (r < 0) + return r; + + r = snprintf(options, sizeof(options), "none,name=%s", name); + if (r < 0) + return r; + + r = mkdir(root, 0755); + if (r < 0 && errno != EEXIST) + return r; + + r = mount("none", root, "cgroup", 0, options); + if (r < 0) + return r; + + return 0; +} + +static void cleanup_named_v1_root(char *root) +{ + if (!cg_test_v1_named) + return; + umount(root); + rmdir(root); +} + #define T(x) { x, #x } struct corecg_test { int (*fn)(const char *root); @@ -867,13 +925,18 @@ int main(int argc, char *argv[]) char root[PATH_MAX]; int i, ret = EXIT_SUCCESS; - if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) - ksft_exit_skip("cgroup v2 isn't mounted\n"); + if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) { + if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME)) + ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n"); + cg_test_v1_named = true; + goto post_v2_setup; + } if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) if (cg_write(root, "cgroup.subtree_control", "+memory")) ksft_exit_skip("Failed to set memory controller\n"); +post_v2_setup: for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: @@ -889,5 +952,6 @@ int main(int argc, char *argv[]) } } + cleanup_named_v1_root(root); return ret; } diff --git a/tools/testing/selftests/cgroup/test_cpu.c b/tools/testing/selftests/cgroup/test_cpu.c index a2b50af8e9ee..2a60e6c41940 100644 --- a/tools/testing/selftests/cgroup/test_cpu.c +++ b/tools/testing/selftests/cgroup/test_cpu.c @@ -2,6 +2,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -645,10 +646,16 @@ test_cpucg_nested_weight_underprovisioned(const char *root) static int test_cpucg_max(const char *root) { int ret = KSFT_FAIL; - long usage_usec, user_usec; - long usage_seconds = 1; - long expected_usage_usec = usage_seconds * USEC_PER_SEC; + long quota_usec = 1000; + long default_period_usec = 100000; /* cpu.max's default period */ + long duration_seconds = 1; + + long duration_usec = duration_seconds * USEC_PER_SEC; + long usage_usec, n_periods, remainder_usec, expected_usage_usec; char *cpucg; + char quota_buf[32]; + + snprintf(quota_buf, sizeof(quota_buf), "%ld", quota_usec); cpucg = cg_name(root, "cpucg_test"); if (!cpucg) @@ -657,13 +664,13 @@ static int test_cpucg_max(const char *root) if (cg_create(cpucg)) goto cleanup; - if (cg_write(cpucg, "cpu.max", "1000")) + if (cg_write(cpucg, "cpu.max", quota_buf)) goto cleanup; struct cpu_hog_func_param param = { .nprocs = 1, .ts = { - .tv_sec = usage_seconds, + .tv_sec = duration_seconds, .tv_nsec = 0, }, .clock_type = CPU_HOG_CLOCK_WALL, @@ -672,14 +679,19 @@ static int test_cpucg_max(const char *root) goto cleanup; usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec"); - user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec"); - if (user_usec <= 0) + if (usage_usec <= 0) goto cleanup; - if (user_usec >= expected_usage_usec) - goto cleanup; + /* + * The following calculation applies only since + * the cpu hog is set to run as per wall-clock time + */ + n_periods = duration_usec / default_period_usec; + remainder_usec = duration_usec - n_periods * default_period_usec; + expected_usage_usec + = n_periods * quota_usec + MIN(remainder_usec, quota_usec); - if (values_close(usage_usec, expected_usage_usec, 95)) + if (!values_close(usage_usec, expected_usage_usec, 10)) goto cleanup; ret = KSFT_PASS; @@ -698,10 +710,16 @@ static int test_cpucg_max(const char *root) static int test_cpucg_max_nested(const char *root) { int ret = KSFT_FAIL; - long usage_usec, user_usec; - long usage_seconds = 1; - long expected_usage_usec = usage_seconds * USEC_PER_SEC; + long quota_usec = 1000; + long default_period_usec = 100000; /* cpu.max's default period */ + long duration_seconds = 1; + + long duration_usec = duration_seconds * USEC_PER_SEC; + long usage_usec, n_periods, remainder_usec, expected_usage_usec; char *parent, *child; + char quota_buf[32]; + + snprintf(quota_buf, sizeof(quota_buf), "%ld", quota_usec); parent = cg_name(root, "cpucg_parent"); child = cg_name(parent, "cpucg_child"); @@ -717,13 +735,13 @@ static int test_cpucg_max_nested(const char *root) if (cg_create(child)) goto cleanup; - if (cg_write(parent, "cpu.max", "1000")) + if (cg_write(parent, "cpu.max", quota_buf)) goto cleanup; struct cpu_hog_func_param param = { .nprocs = 1, .ts = { - .tv_sec = usage_seconds, + .tv_sec = duration_seconds, .tv_nsec = 0, }, .clock_type = CPU_HOG_CLOCK_WALL, @@ -732,14 +750,19 @@ static int test_cpucg_max_nested(const char *root) goto cleanup; usage_usec = cg_read_key_long(child, "cpu.stat", "usage_usec"); - user_usec = cg_read_key_long(child, "cpu.stat", "user_usec"); - if (user_usec <= 0) + if (usage_usec <= 0) goto cleanup; - if (user_usec >= expected_usage_usec) - goto cleanup; + /* + * The following calculation applies only since + * the cpu hog is set to run as per wall-clock time + */ + n_periods = duration_usec / default_period_usec; + remainder_usec = duration_usec - n_periods * default_period_usec; + expected_usage_usec + = n_periods * quota_usec + MIN(remainder_usec, quota_usec); - if (values_close(usage_usec, expected_usage_usec, 95)) + if (!values_close(usage_usec, expected_usage_usec, 10)) goto cleanup; ret = KSFT_PASS; diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index 96693d8772be..63b3c9aad399 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -308,6 +308,7 @@ static int test_kmem_dead_cgroups(const char *root) char *parent; long dead; int i; + int max_time = 20; parent = cg_name(root, "kmem_dead_cgroups_test"); if (!parent) @@ -322,7 +323,7 @@ static int test_kmem_dead_cgroups(const char *root) if (cg_run_in_subcgroups(parent, alloc_dcache, (void *)100, 30)) goto cleanup; - for (i = 0; i < 5; i++) { + for (i = 0; i < max_time; i++) { dead = cg_read_key_long(parent, "cgroup.stat", "nr_dying_descendants "); if (dead == 0) { @@ -334,6 +335,8 @@ static int test_kmem_dead_cgroups(const char *root) * let's wait a bit and repeat. */ sleep(1); + if (i > 5) + printf("Waiting time longer than 5s; wait: %ds (dead: %ld)\n", i, dead); } cleanup: diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 40de679248b8..e1f578ca2841 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -338,7 +338,7 @@ static int test_zswap_writeback_one(const char *cgroup, bool wb) return -1; if (wb != !!zswpwb_after) { - ksft_print_msg("zswpwb_after is %ld while wb is %s", + ksft_print_msg("zswpwb_after is %ld while wb is %s\n", zswpwb_after, wb ? "enabled" : "disabled"); return -1; } diff --git a/tools/testing/selftests/coredump/Makefile b/tools/testing/selftests/coredump/Makefile index ed210037b29d..77b3665c73c7 100644 --- a/tools/testing/selftests/coredump/Makefile +++ b/tools/testing/selftests/coredump/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -CFLAGS = $(KHDR_INCLUDES) +CFLAGS += -Wall -O0 -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) TEST_GEN_PROGS := stackdump_test TEST_FILES := stackdump diff --git a/tools/testing/selftests/coredump/config b/tools/testing/selftests/coredump/config new file mode 100644 index 000000000000..a05ef112b4f9 --- /dev/null +++ b/tools/testing/selftests/coredump/config @@ -0,0 +1,3 @@ +CONFIG_COREDUMP=y +CONFIG_NET=y +CONFIG_UNIX=y diff --git a/tools/testing/selftests/coredump/stackdump_test.c b/tools/testing/selftests/coredump/stackdump_test.c index 9984413be9f0..a4ac80bb1003 100644 --- a/tools/testing/selftests/coredump/stackdump_test.c +++ b/tools/testing/selftests/coredump/stackdump_test.c @@ -1,12 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include +#include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -14,16 +20,23 @@ #include #include "../kselftest_harness.h" +#include "../filesystems/wrappers.h" #include "../pidfd/pidfd.h" #define STACKDUMP_FILE "stack_values" #define STACKDUMP_SCRIPT "stackdump" #define NUM_THREAD_SPAWN 128 +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + static void *do_nothing(void *) { while (1) pause(); + + return NULL; } static void crashing_child(void) @@ -42,16 +55,32 @@ FIXTURE(coredump) { char original_core_pattern[256]; pid_t pid_coredump_server; + int fd_tmpfs_detached; }; +static int create_detached_tmpfs(void) +{ + int fd_context, fd_tmpfs; + + fd_context = sys_fsopen("tmpfs", 0); + if (fd_context < 0) + return -1; + + if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) + return -1; + + fd_tmpfs = sys_fsmount(fd_context, 0, 0); + close(fd_context); + return fd_tmpfs; +} + FIXTURE_SETUP(coredump) { - char buf[PATH_MAX]; FILE *file; - char *dir; int ret; self->pid_coredump_server = -ESRCH; + self->fd_tmpfs_detached = -1; file = fopen("/proc/sys/kernel/core_pattern", "r"); ASSERT_NE(NULL, file); @@ -60,6 +89,8 @@ FIXTURE_SETUP(coredump) ASSERT_LT(ret, sizeof(self->original_core_pattern)); self->original_core_pattern[ret] = '\0'; + self->fd_tmpfs_detached = create_detached_tmpfs(); + ASSERT_GE(self->fd_tmpfs_detached, 0); ret = fclose(file); ASSERT_EQ(0, ret); @@ -98,6 +129,15 @@ FIXTURE_TEARDOWN(coredump) goto fail; } + if (self->fd_tmpfs_detached >= 0) { + ret = close(self->fd_tmpfs_detached); + if (ret < 0) { + reason = "Unable to close detached tmpfs"; + goto fail; + } + self->fd_tmpfs_detached = -1; + } + return; fail: /* This should never happen */ @@ -106,11 +146,10 @@ FIXTURE_TEARDOWN(coredump) TEST_F_TIMEOUT(coredump, stackdump, 120) { - struct sigaction action = {}; unsigned long long stack; char *test_dir, *line; size_t line_length; - char buf[PATH_MAX]; + char buf[PAGE_SIZE]; int ret, i, status; FILE *file; pid_t pid; @@ -169,153 +208,166 @@ TEST_F_TIMEOUT(coredump, stackdump, 120) fclose(file); } +static int create_and_listen_unix_socket(const char *path) +{ + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + }; + assert(strlen(path) < sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + size_t addr_len = + offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1; + int fd, ret; + + fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + goto out; + + ret = bind(fd, (const struct sockaddr *)&addr, addr_len); + if (ret < 0) + goto out; + + ret = listen(fd, 128); + if (ret < 0) + goto out; + + return fd; + +out: + if (fd >= 0) + close(fd); + return -1; +} + +static bool set_core_pattern(const char *pattern) +{ + int fd; + ssize_t ret; + + fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); + if (fd < 0) + return false; + + ret = write(fd, pattern, strlen(pattern)); + close(fd); + if (ret < 0) + return false; + + fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern)); + return ret == strlen(pattern); +} + +static int get_peer_pidfd(int fd) +{ + int fd_peer_pidfd; + socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd); + int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd, + &fd_peer_pidfd_len); + if (ret < 0) { + fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); + return -1; + } + return fd_peer_pidfd; +} + +static bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info) +{ + memset(info, 0, sizeof(*info)); + info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; + return ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info) == 0; +} + +static void +wait_and_check_coredump_server(pid_t pid_coredump_server, + struct __test_metadata *const _metadata, + FIXTURE_DATA(coredump)* self) +{ + int status; + waitpid(pid_coredump_server, &status, 0); + self->pid_coredump_server = -ESRCH; + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); +} + TEST_F(coredump, socket) { - int fd, pidfd, ret, status; - FILE *file; + int pidfd, ret, status; pid_t pid, pid_coredump_server; struct stat st; - char core_file[PATH_MAX]; struct pidfd_info info = {}; int ipc_sockets[2]; char c; - const struct sockaddr_un coredump_sk = { - .sun_family = AF_UNIX, - .sun_path = "/tmp/coredump.socket", - }; - size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + - sizeof("/tmp/coredump.socket"); + + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); ASSERT_EQ(ret, 0); - file = fopen("/proc/sys/kernel/core_pattern", "w"); - ASSERT_NE(file, NULL); - - ret = fprintf(file, "@/tmp/coredump.socket"); - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); - ASSERT_EQ(fclose(file), 0); - pid_coredump_server = fork(); ASSERT_GE(pid_coredump_server, 0); if (pid_coredump_server == 0) { - int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; - socklen_t fd_peer_pidfd_len; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; + int exit_code = EXIT_FAILURE; close(ipc_sockets[0]); - fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); if (fd_server < 0) - _exit(EXIT_FAILURE); + goto out; - ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); - if (ret < 0) { - fprintf(stderr, "Failed to bind coredump socket\n"); - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } - - ret = listen(fd_server, 1); - if (ret < 0) { - fprintf(stderr, "Failed to listen on coredump socket\n"); - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } - - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; close(ipc_sockets[1]); fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); - if (fd_coredump < 0) { - fprintf(stderr, "Failed to accept coredump socket connection\n"); - close(fd_server); - _exit(EXIT_FAILURE); - } + if (fd_coredump < 0) + goto out; - fd_peer_pidfd_len = sizeof(fd_peer_pidfd); - ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, - &fd_peer_pidfd, &fd_peer_pidfd_len); - if (ret < 0) { - fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); - close(fd_coredump); - close(fd_server); - _exit(EXIT_FAILURE); - } + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; - memset(&info, 0, sizeof(info)); - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; - ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); - if (ret < 0) { - fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; - if (!(info.mask & PIDFD_INFO_COREDUMP)) { - fprintf(stderr, "Missing coredump information from coredumping task\n"); - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; - if (!(info.coredump_mask & PIDFD_COREDUMPED)) { - fprintf(stderr, "Received connection from non-coredumping task\n"); - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; fd_core_file = creat("/tmp/coredump.file", 0644); - if (fd_core_file < 0) { - fprintf(stderr, "Failed to create coredump file\n"); - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } + if (fd_core_file < 0) + goto out; for (;;) { char buffer[4096]; ssize_t bytes_read, bytes_write; bytes_read = read(fd_coredump, buffer, sizeof(buffer)); - if (bytes_read < 0) { - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - close(fd_core_file); - _exit(EXIT_FAILURE); - } + if (bytes_read < 0) + goto out; if (bytes_read == 0) break; bytes_write = write(fd_core_file, buffer, bytes_read); - if (bytes_read != bytes_write) { - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - close(fd_core_file); - _exit(EXIT_FAILURE); - } + if (bytes_read != bytes_write) + goto out; } - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - close(fd_core_file); - _exit(EXIT_SUCCESS); + exit_code = EXIT_SUCCESS; +out: + if (fd_core_file >= 0) + close(fd_core_file); + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); } self->pid_coredump_server = pid_coredump_server; @@ -335,48 +387,27 @@ TEST_F(coredump, socket) ASSERT_TRUE(WIFSIGNALED(status)); ASSERT_TRUE(WCOREDUMP(status)); - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; - ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); - waitpid(pid_coredump_server, &status, 0); - self->pid_coredump_server = -ESRCH; - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 0); + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); ASSERT_GT(st.st_size, 0); - /* - * We should somehow validate the produced core file. - * For now just allow for visual inspection - */ system("file /tmp/coredump.file"); } TEST_F(coredump, socket_detect_userspace_client) { - int fd, pidfd, ret, status; - FILE *file; + int pidfd, ret, status; pid_t pid, pid_coredump_server; struct stat st; - char core_file[PATH_MAX]; struct pidfd_info info = {}; int ipc_sockets[2]; char c; - const struct sockaddr_un coredump_sk = { - .sun_family = AF_UNIX, - .sun_path = "/tmp/coredump.socket", - }; - size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + - sizeof("/tmp/coredump.socket"); - file = fopen("/proc/sys/kernel/core_pattern", "w"); - ASSERT_NE(file, NULL); - - ret = fprintf(file, "@/tmp/coredump.socket"); - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); - ASSERT_EQ(fclose(file), 0); + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); ASSERT_EQ(ret, 0); @@ -384,88 +415,46 @@ TEST_F(coredump, socket_detect_userspace_client) pid_coredump_server = fork(); ASSERT_GE(pid_coredump_server, 0); if (pid_coredump_server == 0) { - int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; - socklen_t fd_peer_pidfd_len; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; close(ipc_sockets[0]); - fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); if (fd_server < 0) - _exit(EXIT_FAILURE); + goto out; - ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); - if (ret < 0) { - fprintf(stderr, "Failed to bind coredump socket\n"); - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } - - ret = listen(fd_server, 1); - if (ret < 0) { - fprintf(stderr, "Failed to listen on coredump socket\n"); - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } - - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; close(ipc_sockets[1]); fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); - if (fd_coredump < 0) { - fprintf(stderr, "Failed to accept coredump socket connection\n"); - close(fd_server); - _exit(EXIT_FAILURE); - } + if (fd_coredump < 0) + goto out; - fd_peer_pidfd_len = sizeof(fd_peer_pidfd); - ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, - &fd_peer_pidfd, &fd_peer_pidfd_len); - if (ret < 0) { - fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); - close(fd_coredump); - close(fd_server); - _exit(EXIT_FAILURE); - } + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; - memset(&info, 0, sizeof(info)); - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; - ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); - if (ret < 0) { - fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); - close(fd_coredump); - close(fd_server); + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (info.coredump_mask & PIDFD_COREDUMPED) + goto out; + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } - - if (!(info.mask & PIDFD_INFO_COREDUMP)) { - fprintf(stderr, "Missing coredump information from coredumping task\n"); + if (fd_coredump >= 0) close(fd_coredump); + if (fd_server >= 0) close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } - - if (info.coredump_mask & PIDFD_COREDUMPED) { - fprintf(stderr, "Received unexpected connection from coredumping task\n"); - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - _exit(EXIT_FAILURE); - } - - close(fd_coredump); - close(fd_server); - close(fd_peer_pidfd); - close(fd_core_file); - _exit(EXIT_SUCCESS); + _exit(exit_code); } self->pid_coredump_server = pid_coredump_server; @@ -478,17 +467,22 @@ TEST_F(coredump, socket_detect_userspace_client) if (pid == 0) { int fd_socket; ssize_t ret; + const struct sockaddr_un coredump_sk = { + .sun_family = AF_UNIX, + .sun_path = "/tmp/coredump.socket", + }; + size_t coredump_sk_len = + offsetof(struct sockaddr_un, sun_path) + + sizeof("/tmp/coredump.socket"); fd_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (fd_socket < 0) _exit(EXIT_FAILURE); - ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len); if (ret < 0) _exit(EXIT_FAILURE); - (void *)write(fd_socket, &(char){ 0 }, 1); close(fd_socket); _exit(EXIT_SUCCESS); } @@ -500,15 +494,11 @@ TEST_F(coredump, socket_detect_userspace_client) ASSERT_TRUE(WIFEXITED(status)); ASSERT_EQ(WEXITSTATUS(status), 0); - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; - ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0); - waitpid(pid_coredump_server, &status, 0); - self->pid_coredump_server = -ESRCH; - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 0); + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); ASSERT_NE(stat("/tmp/coredump.file", &st), 0); ASSERT_EQ(errno, ENOENT); @@ -516,17 +506,10 @@ TEST_F(coredump, socket_detect_userspace_client) TEST_F(coredump, socket_enoent) { - int pidfd, ret, status; - FILE *file; + int pidfd, status; pid_t pid; - char core_file[PATH_MAX]; - file = fopen("/proc/sys/kernel/core_pattern", "w"); - ASSERT_NE(file, NULL); - - ret = fprintf(file, "@/tmp/coredump.socket"); - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); - ASSERT_EQ(fclose(file), 0); + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); pid = fork(); ASSERT_GE(pid, 0); @@ -544,7 +527,6 @@ TEST_F(coredump, socket_enoent) TEST_F(coredump, socket_no_listener) { int pidfd, ret, status; - FILE *file; pid_t pid, pid_coredump_server; int ipc_sockets[2]; char c; @@ -555,45 +537,36 @@ TEST_F(coredump, socket_no_listener) size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + sizeof("/tmp/coredump.socket"); + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); ASSERT_EQ(ret, 0); - file = fopen("/proc/sys/kernel/core_pattern", "w"); - ASSERT_NE(file, NULL); - - ret = fprintf(file, "@/tmp/coredump.socket"); - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); - ASSERT_EQ(fclose(file), 0); - pid_coredump_server = fork(); ASSERT_GE(pid_coredump_server, 0); if (pid_coredump_server == 0) { - int fd_server; - socklen_t fd_peer_pidfd_len; + int fd_server = -1; + int exit_code = EXIT_FAILURE; close(ipc_sockets[0]); fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd_server < 0) - _exit(EXIT_FAILURE); + goto out; ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); - if (ret < 0) { - fprintf(stderr, "Failed to bind coredump socket\n"); - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } + if (ret < 0) + goto out; - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { - close(fd_server); - close(ipc_sockets[1]); - _exit(EXIT_FAILURE); - } + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; - close(fd_server); + exit_code = EXIT_SUCCESS; +out: + if (fd_server >= 0) + close(fd_server); close(ipc_sockets[1]); - _exit(EXIT_SUCCESS); + _exit(exit_code); } self->pid_coredump_server = pid_coredump_server; @@ -613,10 +586,1242 @@ TEST_F(coredump, socket_no_listener) ASSERT_TRUE(WIFSIGNALED(status)); ASSERT_FALSE(WCOREDUMP(status)); - waitpid(pid_coredump_server, &status, 0); - self->pid_coredump_server = -ESRCH; - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 0); + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +static ssize_t recv_marker(int fd) +{ + enum coredump_mark mark = COREDUMP_MARK_REQACK; + ssize_t ret; + + ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL); + if (ret != sizeof(mark)) + return -1; + + switch (mark) { + case COREDUMP_MARK_REQACK: + fprintf(stderr, "Received marker: ReqAck\n"); + return COREDUMP_MARK_REQACK; + case COREDUMP_MARK_MINSIZE: + fprintf(stderr, "Received marker: MinSize\n"); + return COREDUMP_MARK_MINSIZE; + case COREDUMP_MARK_MAXSIZE: + fprintf(stderr, "Received marker: MaxSize\n"); + return COREDUMP_MARK_MAXSIZE; + case COREDUMP_MARK_UNSUPPORTED: + fprintf(stderr, "Received marker: Unsupported\n"); + return COREDUMP_MARK_UNSUPPORTED; + case COREDUMP_MARK_CONFLICTING: + fprintf(stderr, "Received marker: Conflicting\n"); + return COREDUMP_MARK_CONFLICTING; + default: + fprintf(stderr, "Received unknown marker: %u\n", mark); + break; + } + return -1; +} + +static bool read_marker(int fd, enum coredump_mark mark) +{ + ssize_t ret; + + ret = recv_marker(fd); + if (ret < 0) + return false; + return ret == mark; +} + +static bool read_coredump_req(int fd, struct coredump_req *req) +{ + ssize_t ret; + size_t field_size, user_size, ack_size, kernel_size, remaining_size; + + memset(req, 0, sizeof(*req)); + field_size = sizeof(req->size); + + /* Peek the size of the coredump request. */ + ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL); + if (ret != field_size) + return false; + kernel_size = req->size; + + if (kernel_size < COREDUMP_ACK_SIZE_VER0) + return false; + if (kernel_size >= PAGE_SIZE) + return false; + + /* Use the minimum of user and kernel size to read the full request. */ + user_size = sizeof(struct coredump_req); + ack_size = user_size < kernel_size ? user_size : kernel_size; + ret = recv(fd, req, ack_size, MSG_WAITALL); + if (ret != ack_size) + return false; + + fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n", + req->size, (unsigned long long)req->mask); + + if (user_size > kernel_size) + remaining_size = user_size - kernel_size; + else + remaining_size = kernel_size - user_size; + + if (PAGE_SIZE <= remaining_size) + return false; + + /* + * Discard any additional data if the kernel's request was larger than + * what we knew about or cared about. + */ + if (remaining_size) { + char buffer[PAGE_SIZE]; + + ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL); + if (ret != remaining_size) + return false; + fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size); + } + + return true; +} + +static bool send_coredump_ack(int fd, const struct coredump_req *req, + __u64 mask, size_t size_ack) +{ + ssize_t ret; + /* + * Wrap struct coredump_ack in a larger struct so we can + * simulate sending to much data to the kernel. + */ + struct large_ack_for_size_testing { + struct coredump_ack ack; + char buffer[PAGE_SIZE]; + } large_ack = {}; + + if (!size_ack) + size_ack = sizeof(struct coredump_ack) < req->size_ack ? + sizeof(struct coredump_ack) : + req->size_ack; + large_ack.ack.mask = mask; + large_ack.ack.size = size_ack; + ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL); + if (ret != size_ack) + return false; + + fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n", + size_ack, (unsigned long long)mask); + return true; +} + +static bool check_coredump_req(const struct coredump_req *req, size_t min_size, + __u64 required_mask) +{ + if (req->size < min_size) + return false; + if ((req->mask & required_mask) != required_mask) + return false; + if (req->mask & ~required_mask) + return false; + return true; +} + +TEST_F(coredump, socket_request_kernel) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct stat st; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_core_file = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + fd_core_file = creat("/tmp/coredump.file", 0644); + if (fd_core_file < 0) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_KERNEL | COREDUMP_WAIT, 0)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) + goto out; + + for (;;) { + char buffer[4096]; + ssize_t bytes_read, bytes_write; + + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); + if (bytes_read < 0) + goto out; + + if (bytes_read == 0) + break; + + bytes_write = write(fd_core_file, buffer, bytes_read); + if (bytes_read != bytes_write) + goto out; + } + + exit_code = EXIT_SUCCESS; +out: + if (fd_core_file >= 0) + close(fd_core_file); + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_TRUE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); + + ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); + ASSERT_GT(st.st_size, 0); + system("file /tmp/coredump.file"); +} + +TEST_F(coredump, socket_request_userspace) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_USERSPACE | COREDUMP_WAIT, 0)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) + goto out; + + for (;;) { + char buffer[4096]; + ssize_t bytes_read; + + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); + if (bytes_read > 0) + goto out; + + if (bytes_read < 0) + goto out; + + if (bytes_read == 0) + break; + } + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_TRUE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_request_reject) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_REJECT | COREDUMP_WAIT, 0)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) + goto out; + + for (;;) { + char buffer[4096]; + ssize_t bytes_read; + + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); + if (bytes_read > 0) + goto out; + + if (bytes_read < 0) + goto out; + + if (bytes_read == 0) + break; + } + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_FALSE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_request_invalid_flag_combination) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_KERNEL | COREDUMP_REJECT | COREDUMP_WAIT, 0)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_CONFLICTING)) + goto out; + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_FALSE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_request_unknown_flag) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, (1ULL << 63), 0)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_UNSUPPORTED)) + goto out; + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_FALSE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_request_invalid_size_small) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_REJECT | COREDUMP_WAIT, + COREDUMP_ACK_SIZE_VER0 / 2)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_MINSIZE)) + goto out; + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_FALSE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_request_invalid_size_large) +{ + int pidfd, ret, status; + pid_t pid, pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + struct coredump_req req = {}; + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; + int exit_code = EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + + close(ipc_sockets[1]); + + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) + goto out; + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + + if (!(info.mask & PIDFD_INFO_COREDUMP)) + goto out; + + if (!(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + + if (!read_coredump_req(fd_coredump, &req)) + goto out; + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_REJECT | COREDUMP_WAIT, + COREDUMP_ACK_SIZE_VER0 + PAGE_SIZE)) + goto out; + + if (!read_marker(fd_coredump, COREDUMP_MARK_MAXSIZE)) + goto out; + + exit_code = EXIT_SUCCESS; +out: + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + crashing_child(); + + pidfd = sys_pidfd_open(pid, 0); + ASSERT_GE(pidfd, 0); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_FALSE(WCOREDUMP(status)); + + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +static int open_coredump_tmpfile(int fd_tmpfs_detached) +{ + return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600); +} + +#define NUM_CRASHING_COREDUMPS 5 + +TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps, 500) +{ + int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS]; + pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; + int exit_code = EXIT_FAILURE; + struct coredump_req req = {}; + + close(ipc_sockets[0]); + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) { + fprintf(stderr, "Failed to create and listen on unix socket\n"); + goto out; + } + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) { + fprintf(stderr, "Failed to notify parent via ipc socket\n"); + goto out; + } + close(ipc_sockets[1]); + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) { + fprintf(stderr, "accept4 failed: %m\n"); + goto out; + } + + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) { + fprintf(stderr, "get_peer_pidfd failed for fd %d: %m\n", fd_coredump); + goto out; + } + + if (!get_pidfd_info(fd_peer_pidfd, &info)) { + fprintf(stderr, "get_pidfd_info failed for fd %d\n", fd_peer_pidfd); + goto out; + } + + if (!(info.mask & PIDFD_INFO_COREDUMP)) { + fprintf(stderr, "pidfd info missing PIDFD_INFO_COREDUMP for fd %d\n", fd_peer_pidfd); + goto out; + } + if (!(info.coredump_mask & PIDFD_COREDUMPED)) { + fprintf(stderr, "pidfd info missing PIDFD_COREDUMPED for fd %d\n", fd_peer_pidfd); + goto out; + } + + if (!read_coredump_req(fd_coredump, &req)) { + fprintf(stderr, "read_coredump_req failed for fd %d\n", fd_coredump); + goto out; + } + + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) { + fprintf(stderr, "check_coredump_req failed for fd %d\n", fd_coredump); + goto out; + } + + if (!send_coredump_ack(fd_coredump, &req, + COREDUMP_KERNEL | COREDUMP_WAIT, 0)) { + fprintf(stderr, "send_coredump_ack failed for fd %d\n", fd_coredump); + goto out; + } + + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) { + fprintf(stderr, "read_marker failed for fd %d\n", fd_coredump); + goto out; + } + + fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached); + if (fd_core_file < 0) { + fprintf(stderr, "%m - open_coredump_tmpfile failed for fd %d\n", fd_coredump); + goto out; + } + + for (;;) { + char buffer[4096]; + ssize_t bytes_read, bytes_write; + + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); + if (bytes_read < 0) { + fprintf(stderr, "read failed for fd %d: %m\n", fd_coredump); + goto out; + } + + if (bytes_read == 0) + break; + + bytes_write = write(fd_core_file, buffer, bytes_read); + if (bytes_read != bytes_write) { + fprintf(stderr, "write failed for fd %d: %m\n", fd_core_file); + goto out; + } + } + + close(fd_core_file); + close(fd_peer_pidfd); + close(fd_coredump); + fd_peer_pidfd = -1; + fd_coredump = -1; + } + + exit_code = EXIT_SUCCESS; +out: + if (fd_core_file >= 0) + close(fd_core_file); + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_server >= 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + pid[i] = fork(); + ASSERT_GE(pid[i], 0); + if (pid[i] == 0) + crashing_child(); + pidfd[i] = sys_pidfd_open(pid[i], 0); + ASSERT_GE(pidfd[i], 0); + } + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + waitpid(pid[i], &status[i], 0); + ASSERT_TRUE(WIFSIGNALED(status[i])); + ASSERT_TRUE(WCOREDUMP(status[i])); + } + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; + ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + } + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +#define MAX_EVENTS 128 + +static void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file) +{ + int epfd = -1; + int exit_code = EXIT_FAILURE; + + epfd = epoll_create1(0); + if (epfd < 0) + goto out; + + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; + ev.data.fd = fd_coredump; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0) + goto out; + + for (;;) { + struct epoll_event events[1]; + int n = epoll_wait(epfd, events, 1, -1); + if (n < 0) + break; + + if (events[0].events & (EPOLLIN | EPOLLRDHUP)) { + for (;;) { + char buffer[4096]; + ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer)); + if (bytes_read < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + goto out; + } + if (bytes_read == 0) + goto done; + ssize_t bytes_write = write(fd_core_file, buffer, bytes_read); + if (bytes_write != bytes_read) + goto out; + } + } + } + +done: + exit_code = EXIT_SUCCESS; +out: + if (epfd >= 0) + close(epfd); + if (fd_core_file >= 0) + close(fd_core_file); + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_coredump >= 0) + close(fd_coredump); + _exit(exit_code); +} + +TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps_epoll_workers, 500) +{ + int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS]; + pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server, worker_pids[NUM_CRASHING_COREDUMPS]; + struct pidfd_info info = {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); + + pid_coredump_server = fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server == 0) { + int fd_server = -1, exit_code = EXIT_FAILURE, n_conns = 0; + fd_server = -1; + exit_code = EXIT_FAILURE; + n_conns = 0; + close(ipc_sockets[0]); + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); + if (fd_server < 0) + goto out; + + if (write_nointr(ipc_sockets[1], "1", 1) < 0) + goto out; + close(ipc_sockets[1]); + + while (n_conns < NUM_CRASHING_COREDUMPS) { + int fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; + struct coredump_req req = {}; + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); + if (fd_coredump < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + goto out; + } + fd_peer_pidfd = get_peer_pidfd(fd_coredump); + if (fd_peer_pidfd < 0) + goto out; + if (!get_pidfd_info(fd_peer_pidfd, &info)) + goto out; + if (!(info.mask & PIDFD_INFO_COREDUMP) || !(info.coredump_mask & PIDFD_COREDUMPED)) + goto out; + if (!read_coredump_req(fd_coredump, &req)) + goto out; + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, + COREDUMP_KERNEL | COREDUMP_USERSPACE | + COREDUMP_REJECT | COREDUMP_WAIT)) + goto out; + if (!send_coredump_ack(fd_coredump, &req, COREDUMP_KERNEL | COREDUMP_WAIT, 0)) + goto out; + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) + goto out; + fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached); + if (fd_core_file < 0) + goto out; + pid_t worker = fork(); + if (worker == 0) { + close(fd_server); + process_coredump_worker(fd_coredump, fd_peer_pidfd, fd_core_file); + } + worker_pids[n_conns] = worker; + if (fd_coredump >= 0) + close(fd_coredump); + if (fd_peer_pidfd >= 0) + close(fd_peer_pidfd); + if (fd_core_file >= 0) + close(fd_core_file); + n_conns++; + } + exit_code = EXIT_SUCCESS; +out: + if (fd_server >= 0) + close(fd_server); + + // Reap all worker processes + for (int i = 0; i < n_conns; i++) { + int wstatus; + if (waitpid(worker_pids[i], &wstatus, 0) < 0) { + fprintf(stderr, "Failed to wait for worker %d: %m\n", worker_pids[i]); + } else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != EXIT_SUCCESS) { + fprintf(stderr, "Worker %d exited with error code %d\n", worker_pids[i], WEXITSTATUS(wstatus)); + exit_code = EXIT_FAILURE; + } + } + + _exit(exit_code); + } + self->pid_coredump_server = pid_coredump_server; + + EXPECT_EQ(close(ipc_sockets[1]), 0); + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); + EXPECT_EQ(close(ipc_sockets[0]), 0); + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + pid[i] = fork(); + ASSERT_GE(pid[i], 0); + if (pid[i] == 0) + crashing_child(); + pidfd[i] = sys_pidfd_open(pid[i], 0); + ASSERT_GE(pidfd[i], 0); + } + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + ASSERT_GE(waitpid(pid[i], &status[i], 0), 0); + ASSERT_TRUE(WIFSIGNALED(status[i])); + ASSERT_TRUE(WCOREDUMP(status[i])); + } + + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; + ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0); + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); + } + + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); +} + +TEST_F(coredump, socket_invalid_paths) +{ + ASSERT_FALSE(set_core_pattern("@ /tmp/coredump.socket")); + ASSERT_FALSE(set_core_pattern("@/tmp/../coredump.socket")); + ASSERT_FALSE(set_core_pattern("@../coredump.socket")); + ASSERT_FALSE(set_core_pattern("@/tmp/coredump.socket/..")); + ASSERT_FALSE(set_core_pattern("@..")); + + ASSERT_FALSE(set_core_pattern("@@ /tmp/coredump.socket")); + ASSERT_FALSE(set_core_pattern("@@/tmp/../coredump.socket")); + ASSERT_FALSE(set_core_pattern("@@../coredump.socket")); + ASSERT_FALSE(set_core_pattern("@@/tmp/coredump.socket/..")); + ASSERT_FALSE(set_core_pattern("@@..")); + + ASSERT_FALSE(set_core_pattern("@@@/tmp/coredump.socket")); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh index d5dc7e0dc726..6232a46ca6e1 100755 --- a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh +++ b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh @@ -67,7 +67,7 @@ hotpluggable_cpus() done } -hotplaggable_offline_cpus() +hotpluggable_offline_cpus() { hotpluggable_cpus 0 } @@ -151,7 +151,7 @@ offline_cpu_expect_fail() online_all_hot_pluggable_cpus() { - for cpu in `hotplaggable_offline_cpus`; do + for cpu in `hotpluggable_offline_cpus`; do online_cpu_expect_success $cpu done } diff --git a/tools/testing/selftests/damon/Makefile b/tools/testing/selftests/damon/Makefile index ff21524be458..9a3499827d4b 100644 --- a/tools/testing/selftests/damon/Makefile +++ b/tools/testing/selftests/damon/Makefile @@ -4,9 +4,11 @@ TEST_GEN_FILES += access_memory access_memory_even TEST_FILES = _damon_sysfs.py +TEST_FILES += drgn_dump_damon_status.py # functionality tests TEST_PROGS += sysfs.sh +TEST_PROGS += sysfs.py TEST_PROGS += sysfs_update_schemes_tried_regions_wss_estimation.py TEST_PROGS += damos_quota.py damos_quota_goal.py damos_apply_interval.py TEST_PROGS += damos_tried_regions.py damon_nr_regions.py @@ -15,6 +17,7 @@ TEST_PROGS += reclaim.sh lru_sort.sh # regression tests (reproducers of previously found bugs) TEST_PROGS += sysfs_update_removed_scheme_dir.sh TEST_PROGS += sysfs_update_schemes_tried_regions_hang.py +TEST_PROGS += sysfs_memcg_path_leak.sh EXTRA_CLEAN = __pycache__ diff --git a/tools/testing/selftests/damon/_common.sh b/tools/testing/selftests/damon/_common.sh new file mode 100644 index 000000000000..0279698f733e --- /dev/null +++ b/tools/testing/selftests/damon/_common.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +check_dependencies() +{ + if [ $EUID -ne 0 ] + then + echo "Run as root" + exit $ksft_skip + fi +} diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 5b1cb6b3ce4e..a0e6290833fb 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -52,9 +52,9 @@ class DamosAccessPattern: if self.size is None: self.size = [0, 2**64 - 1] if self.nr_accesses is None: - self.nr_accesses = [0, 2**64 - 1] + self.nr_accesses = [0, 2**32 - 1] if self.age is None: - self.age = [0, 2**64 - 1] + self.age = [0, 2**32 - 1] def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'access_pattern') @@ -93,14 +93,16 @@ class DamosQuotaGoal: metric = None target_value = None current_value = None + nid = None effective_bytes = None quota = None # owner quota idx = None - def __init__(self, metric, target_value=10000, current_value=0): + def __init__(self, metric, target_value=10000, current_value=0, nid=0): self.metric = metric self.target_value = target_value self.current_value = current_value + self.nid = nid def sysfs_dir(self): return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx) @@ -118,6 +120,10 @@ class DamosQuotaGoal: self.current_value) if err is not None: return err + err = write_file(os.path.join(self.sysfs_dir(), 'nid'), self.nid) + if err is not None: + return err + return None class DamosQuota: @@ -125,12 +131,20 @@ class DamosQuota: ms = None # time quota goals = None # quota goals reset_interval_ms = None # quota reset interval + weight_sz_permil = None + weight_nr_accesses_permil = None + weight_age_permil = None scheme = None # owner scheme - def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0): + def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0, + weight_sz_permil=0, weight_nr_accesses_permil=0, + weight_age_permil=0): self.sz = sz self.ms = ms self.reset_interval_ms = reset_interval_ms + self.weight_sz_permil = weight_sz_permil + self.weight_nr_accesses_permil = weight_nr_accesses_permil + self.weight_age_permil = weight_age_permil self.goals = goals if goals is not None else [] for idx, goal in enumerate(self.goals): goal.idx = idx @@ -151,6 +165,20 @@ class DamosQuota: if err is not None: return err + err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'sz_permil'), self.weight_sz_permil) + if err is not None: + return err + err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'nr_accesses_permil'), + self.weight_nr_accesses_permil) + if err is not None: + return err + err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'age_permil'), self.weight_age_permil) + if err is not None: + return err + nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals') content, err = read_file(nr_goals_file) if err is not None: @@ -165,6 +193,178 @@ class DamosQuota: return err return None +class DamosWatermarks: + metric = None + interval = None + high = None + mid = None + low = None + scheme = None # owner scheme + + def __init__(self, metric='none', interval=0, high=0, mid=0, low=0): + self.metric = metric + self.interval = interval + self.high = high + self.mid = mid + self.low = low + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), 'watermarks') + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'metric'), self.metric) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'interval_us'), + self.interval) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'high'), self.high) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'mid'), self.mid) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'low'), self.low) + if err is not None: + return err + +class DamosFilter: + type_ = None + matching = None + allow = None + memcg_path = None + addr_start = None + addr_end = None + target_idx = None + min_ = None + max_ = None + idx = None + filters = None # owner filters + + def __init__(self, type_='anon', matching=False, allow=False, + memcg_path='', addr_start=0, addr_end=0, target_idx=0, min_=0, + max_=0): + self.type_ = type_ + self.matching = matching + self.allow = allow + self.memcg_path = memcg_path, + self.addr_start = addr_start + self.addr_end = addr_end + self.target_idx = target_idx + self.min_ = min_ + self.max_ = max_ + + def sysfs_dir(self): + return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx) + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'matching'), + self.matching) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'allow'), self.allow) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path'), + self.memcg_path) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'addr_start'), + self.addr_start) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'addr_end'), + self.addr_end) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx'), + self.target_idx) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'min'), self.min_) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'max'), self.max_) + if err is not None: + return err + return None + +class DamosFilters: + name = None + filters = None + scheme = None # owner scheme + + def __init__(self, name, filters=[]): + self.name = name + self.filters = filters + for idx, filter_ in enumerate(self.filters): + filter_.idx = idx + filter_.filters = self + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), self.name) + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters'), + len(self.filters)) + if err is not None: + return err + for filter_ in self.filters: + err = filter_.stage() + if err is not None: + return err + return None + +class DamosDest: + id = None + weight = None + idx = None + dests = None # owner dests + + def __init__(self, id=0, weight=0): + self.id = id + self.weight = weight + + def sysfs_dir(self): + return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx) + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight) + if err is not None: + return err + return None + +class DamosDests: + dests = None + scheme = None # owner scheme + + def __init__(self, dests=[]): + self.dests = dests + for idx, dest in enumerate(self.dests): + dest.idx = idx + dest.dests = self + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), 'dests') + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'), + len(self.dests)) + if err is not None: + return err + for dest in self.dests: + err = dest.stage() + if err is not None: + return err + return None + class DamosStats: nr_tried = None sz_tried = None @@ -190,8 +390,13 @@ class Damos: action = None access_pattern = None quota = None + watermarks = None + core_filters = None + ops_filters = None + filters = None apply_interval_us = None - # todo: Support watermarks, stats + target_nid = None + dests = None idx = None context = None tried_bytes = None @@ -199,12 +404,30 @@ class Damos: tried_regions = None def __init__(self, action='stat', access_pattern=DamosAccessPattern(), - quota=DamosQuota(), apply_interval_us=0): + quota=DamosQuota(), watermarks=DamosWatermarks(), + core_filters=[], ops_filters=[], filters=[], target_nid=0, + dests=DamosDests(), apply_interval_us=0): self.action = action self.access_pattern = access_pattern self.access_pattern.scheme = self self.quota = quota self.quota.scheme = self + self.watermarks = watermarks + self.watermarks.scheme = self + + self.core_filters = DamosFilters(name='core_filters', + filters=core_filters) + self.core_filters.scheme = self + self.ops_filters = DamosFilters(name='ops_filters', + filters=ops_filters) + self.ops_filters.scheme = self + self.filters = DamosFilters(name='filters', filters=filters) + self.filters.scheme = self + + self.target_nid = target_nid + self.dests = dests + self.dests.scheme = self + self.apply_interval_us = apply_interval_us def sysfs_dir(self): @@ -227,15 +450,26 @@ class Damos: if err is not None: return err - # disable watermarks - err = write_file( - os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none') + err = self.watermarks.stage() if err is not None: return err - # disable filters - err = write_file( - os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0') + err = self.core_filters.stage() + if err is not None: + return err + err = self.ops_filters.stage() + if err is not None: + return err + err = self.filters.stage() + if err is not None: + return err + + err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' % + self.target_nid) + if err is not None: + return err + + err = self.dests.stage() if err is not None: return err @@ -260,18 +494,56 @@ class DamonTarget: return write_file( os.path.join(self.sysfs_dir(), 'pid_target'), self.pid) +class IntervalsGoal: + access_bp = None + aggrs = None + min_sample_us = None + max_sample_us = None + attrs = None # owner DamonAttrs + + def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0): + self.access_bp = access_bp + self.aggrs = aggrs + self.min_sample_us = min_sample_us + self.max_sample_us = max_sample_us + + def sysfs_dir(self): + return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal') + + def stage(self): + err = write_file( + os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'), + self.min_sample_us) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'), + self.max_sample_us) + if err is not None: + return err + return None + class DamonAttrs: sample_us = None aggr_us = None + intervals_goal = None update_us = None min_nr_regions = None max_nr_regions = None context = None - def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000, + def __init__(self, sample_us=5000, aggr_us=100000, + intervals_goal=IntervalsGoal(), update_us=1000000, min_nr_regions=10, max_nr_regions=1000): self.sample_us = sample_us self.aggr_us = aggr_us + self.intervals_goal = intervals_goal + self.intervals_goal.attrs = self self.update_us = update_us self.min_nr_regions = min_nr_regions self.max_nr_regions = max_nr_regions @@ -291,6 +563,9 @@ class DamonAttrs: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'), self.aggr_us) + if err is not None: + return err + err = self.intervals_goal.stage() if err is not None: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'), @@ -408,6 +683,9 @@ class Kdamond: if err is not None: return err err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on') + if err is not None: + return err + self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid')) return err def stop(self): diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py new file mode 100755 index 000000000000..7233369a3a44 --- /dev/null +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -0,0 +1,222 @@ +#!/usr/bin/env drgn +# SPDX-License-Identifier: GPL-2.0 + +''' +Read DAMON context data and dump as a json string. +''' +import drgn +from drgn import FaultError, NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof +from drgn.helpers.common import * +from drgn.helpers.linux import * + +import json +import sys + +if "prog" not in globals(): + try: + prog = drgn.get_default_prog() + except drgn.NoDefaultProgramError: + prog = drgn.program_from_kernel() + drgn.set_default_prog(prog) + +def to_dict(object, attr_name_converter): + d = {} + for attr_name, converter in attr_name_converter: + d[attr_name] = converter(getattr(object, attr_name)) + return d + +def ops_to_dict(ops): + return to_dict(ops, [ + ['id', int], + ]) + +def intervals_goal_to_dict(goal): + return to_dict(goal, [ + ['access_bp', int], + ['aggrs', int], + ['min_sample_us', int], + ['max_sample_us', int], + ]) + +def attrs_to_dict(attrs): + return to_dict(attrs, [ + ['sample_interval', int], + ['aggr_interval', int], + ['ops_update_interval', int], + ['intervals_goal', intervals_goal_to_dict], + ['min_nr_regions', int], + ['max_nr_regions', int], + ]) + +def addr_range_to_dict(addr_range): + return to_dict(addr_range, [ + ['start', int], + ['end', int], + ]) + +def region_to_dict(region): + return to_dict(region, [ + ['ar', addr_range_to_dict], + ['sampling_addr', int], + ['nr_accesses', int], + ['nr_accesses_bp', int], + ['age', int], + ]) + +def regions_to_list(regions): + return [region_to_dict(r) + for r in list_for_each_entry( + 'struct damon_region', regions.address_of_(), 'list')] + +def target_to_dict(target): + return to_dict(target, [ + ['pid', int], + ['nr_regions', int], + ['regions_list', regions_to_list], + ]) + +def targets_to_list(targets): + return [target_to_dict(t) + for t in list_for_each_entry( + 'struct damon_target', targets.address_of_(), 'list')] + +def damos_access_pattern_to_dict(pattern): + return to_dict(pattern, [ + ['min_sz_region', int], + ['max_sz_region', int], + ['min_nr_accesses', int], + ['max_nr_accesses', int], + ['min_age_region', int], + ['max_age_region', int], + ]) + +def damos_quota_goal_to_dict(goal): + return to_dict(goal, [ + ['metric', int], + ['target_value', int], + ['current_value', int], + ['last_psi_total', int], + ['nid', int], + ]) + +def damos_quota_goals_to_list(goals): + return [damos_quota_goal_to_dict(g) + for g in list_for_each_entry( + 'struct damos_quota_goal', goals.address_of_(), 'list')] + +def damos_quota_to_dict(quota): + return to_dict(quota, [ + ['reset_interval', int], + ['ms', int], ['sz', int], + ['goals', damos_quota_goals_to_list], + ['esz', int], + ['weight_sz', int], + ['weight_nr_accesses', int], + ['weight_age', int], + ]) + +def damos_watermarks_to_dict(watermarks): + return to_dict(watermarks, [ + ['metric', int], + ['interval', int], + ['high', int], ['mid', int], ['low', int], + ]) + +def damos_migrate_dests_to_dict(dests): + nr_dests = int(dests.nr_dests) + node_id_arr = [] + weight_arr = [] + for i in range(nr_dests): + node_id_arr.append(int(dests.node_id_arr[i])) + weight_arr.append(int(dests.weight_arr[i])) + return { + 'node_id_arr': node_id_arr, + 'weight_arr': weight_arr, + 'nr_dests': nr_dests, + } + +def damos_filter_to_dict(damos_filter): + filter_type_keyword = { + 0: 'anon', + 1: 'active', + 2: 'memcg', + 3: 'young', + 4: 'hugepage_size', + 5: 'unmapped', + 6: 'addr', + 7: 'target' + } + dict_ = { + 'type': filter_type_keyword[int(damos_filter.type)], + 'matching': bool(damos_filter.matching), + 'allow': bool(damos_filter.allow), + } + type_ = dict_['type'] + if type_ == 'memcg': + dict_['memcg_id'] = int(damos_filter.memcg_id) + elif type_ == 'addr': + dict_['addr_range'] = [int(damos_filter.addr_range.start), + int(damos_filter.addr_range.end)] + elif type_ == 'target': + dict_['target_idx'] = int(damos_filter.target_idx) + elif type_ == 'hugeapge_size': + dict_['sz_range'] = [int(damos_filter.sz_range.min), + int(damos_filter.sz_range.max)] + return dict_ + +def scheme_to_dict(scheme): + dict_ = to_dict(scheme, [ + ['pattern', damos_access_pattern_to_dict], + ['action', int], + ['apply_interval_us', int], + ['quota', damos_quota_to_dict], + ['wmarks', damos_watermarks_to_dict], + ['target_nid', int], + ['migrate_dests', damos_migrate_dests_to_dict], + ]) + filters = [] + for f in list_for_each_entry( + 'struct damos_filter', scheme.filters.address_of_(), 'list'): + filters.append(damos_filter_to_dict(f)) + dict_['filters'] = filters + ops_filters = [] + for f in list_for_each_entry( + 'struct damos_filter', scheme.ops_filters.address_of_(), 'list'): + ops_filters.append(damos_filter_to_dict(f)) + dict_['ops_filters'] = ops_filters + + return dict_ + +def schemes_to_list(schemes): + return [scheme_to_dict(s) + for s in list_for_each_entry( + 'struct damos', schemes.address_of_(), 'list')] + +def damon_ctx_to_dict(ctx): + return to_dict(ctx, [ + ['ops', ops_to_dict], + ['attrs', attrs_to_dict], + ['adaptive_targets', targets_to_list], + ['schemes', schemes_to_list], + ]) + +def main(): + if len(sys.argv) < 3: + print('Usage: %s ' % sys.argv[0]) + exit(1) + + pid = int(sys.argv[1]) + file_to_store = sys.argv[2] + + kthread_data = cast('struct kthread *', + find_task(prog, pid).worker_private).data + ctx = cast('struct damon_ctx *', kthread_data) + status = {'contexts': [damon_ctx_to_dict(ctx)]} + if file_to_store == 'stdout': + print(json.dumps(status, indent=4)) + else: + with open(file_to_store, 'w') as f: + json.dump(status, f, indent=4) + +if __name__ == '__main__': + main() diff --git a/tools/testing/selftests/damon/lru_sort.sh b/tools/testing/selftests/damon/lru_sort.sh index 61b80197c896..1e4849db78a9 100755 --- a/tools/testing/selftests/damon/lru_sort.sh +++ b/tools/testing/selftests/damon/lru_sort.sh @@ -1,14 +1,12 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +source _common.sh + # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -if [ $EUID -ne 0 ] -then - echo "Run as root" - exit $ksft_skip -fi +check_dependencies damon_lru_sort_enabled="/sys/module/damon_lru_sort/parameters/enabled" if [ ! -f "$damon_lru_sort_enabled" ] diff --git a/tools/testing/selftests/damon/reclaim.sh b/tools/testing/selftests/damon/reclaim.sh index 78dbc2334cbe..e56ceb035129 100755 --- a/tools/testing/selftests/damon/reclaim.sh +++ b/tools/testing/selftests/damon/reclaim.sh @@ -1,14 +1,12 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +source _common.sh + # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -if [ $EUID -ne 0 ] -then - echo "Run as root" - exit $ksft_skip -fi +check_dependencies damon_reclaim_enabled="/sys/module/damon_reclaim/parameters/enabled" if [ ! -f "$damon_reclaim_enabled" ] diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py new file mode 100755 index 000000000000..2666c6f0f1a5 --- /dev/null +++ b/tools/testing/selftests/damon/sysfs.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import json +import os +import subprocess + +import _damon_sysfs + +def dump_damon_status_dict(pid): + try: + subprocess.check_output(['which', 'drgn'], stderr=subprocess.DEVNULL) + except: + return None, 'drgn not found' + file_dir = os.path.dirname(os.path.abspath(__file__)) + dump_script = os.path.join(file_dir, 'drgn_dump_damon_status.py') + rc = subprocess.call(['drgn', dump_script, pid, 'damon_dump_output'], + stderr=subprocess.DEVNULL) + if rc != 0: + return None, 'drgn fail' + try: + with open('damon_dump_output', 'r') as f: + return json.load(f), None + except Exception as e: + return None, 'json.load fail (%s)' % e + +def fail(expectation, status): + print('unexpected %s' % expectation) + print(json.dumps(status, indent=4)) + exit(1) + +def assert_true(condition, expectation, status): + if condition is not True: + fail(expectation, status) + +def assert_watermarks_committed(watermarks, dump): + wmark_metric_val = { + 'none': 0, + 'free_mem_rate': 1, + } + assert_true(dump['metric'] == wmark_metric_val[watermarks.metric], + 'metric', dump) + assert_true(dump['interval'] == watermarks.interval, 'interval', dump) + assert_true(dump['high'] == watermarks.high, 'high', dump) + assert_true(dump['mid'] == watermarks.mid, 'mid', dump) + assert_true(dump['low'] == watermarks.low, 'low', dump) + +def assert_quota_goal_committed(qgoal, dump): + metric_val = { + 'user_input': 0, + 'some_mem_psi_us': 1, + 'node_mem_used_bp': 2, + 'node_mem_free_bp': 3, + } + assert_true(dump['metric'] == metric_val[qgoal.metric], 'metric', dump) + assert_true(dump['target_value'] == qgoal.target_value, 'target_value', + dump) + if qgoal.metric == 'user_input': + assert_true(dump['current_value'] == qgoal.current_value, + 'current_value', dump) + assert_true(dump['nid'] == qgoal.nid, 'nid', dump) + +def assert_quota_committed(quota, dump): + assert_true(dump['reset_interval'] == quota.reset_interval_ms, + 'reset_interval', dump) + assert_true(dump['ms'] == quota.ms, 'ms', dump) + assert_true(dump['sz'] == quota.sz, 'sz', dump) + for idx, qgoal in enumerate(quota.goals): + assert_quota_goal_committed(qgoal, dump['goals'][idx]) + assert_true(dump['weight_sz'] == quota.weight_sz_permil, 'weight_sz', dump) + assert_true(dump['weight_nr_accesses'] == quota.weight_nr_accesses_permil, + 'weight_nr_accesses', dump) + assert_true( + dump['weight_age'] == quota.weight_age_permil, 'weight_age', dump) + + +def assert_migrate_dests_committed(dests, dump): + assert_true(dump['nr_dests'] == len(dests.dests), 'nr_dests', dump) + for idx, dest in enumerate(dests.dests): + assert_true(dump['node_id_arr'][idx] == dest.id, 'node_id', dump) + assert_true(dump['weight_arr'][idx] == dest.weight, 'weight', dump) + +def assert_filter_committed(filter_, dump): + assert_true(filter_.type_ == dump['type'], 'type', dump) + assert_true(filter_.matching == dump['matching'], 'matching', dump) + assert_true(filter_.allow == dump['allow'], 'allow', dump) + # TODO: check memcg_path and memcg_id if type is memcg + if filter_.type_ == 'addr': + assert_true([filter_.addr_start, filter_.addr_end] == + dump['addr_range'], 'addr_range', dump) + elif filter_.type_ == 'target': + assert_true(filter_.target_idx == dump['target_idx'], 'target_idx', + dump) + elif filter_.type_ == 'hugepage_size': + assert_true([filter_.min_, filter_.max_] == dump['sz_range'], + 'sz_range', dump) + +def assert_access_pattern_committed(pattern, dump): + assert_true(dump['min_sz_region'] == pattern.size[0], 'min_sz_region', + dump) + assert_true(dump['max_sz_region'] == pattern.size[1], 'max_sz_region', + dump) + assert_true(dump['min_nr_accesses'] == pattern.nr_accesses[0], + 'min_nr_accesses', dump) + assert_true(dump['max_nr_accesses'] == pattern.nr_accesses[1], + 'max_nr_accesses', dump) + assert_true(dump['min_age_region'] == pattern.age[0], 'min_age_region', + dump) + assert_true(dump['max_age_region'] == pattern.age[1], 'miaxage_region', + dump) + +def assert_scheme_committed(scheme, dump): + assert_access_pattern_committed(scheme.access_pattern, dump['pattern']) + action_val = { + 'willneed': 0, + 'cold': 1, + 'pageout': 2, + 'hugepage': 3, + 'nohugeapge': 4, + 'lru_prio': 5, + 'lru_deprio': 6, + 'migrate_hot': 7, + 'migrate_cold': 8, + 'stat': 9, + } + assert_true(dump['action'] == action_val[scheme.action], 'action', dump) + assert_true(dump['apply_interval_us'] == scheme. apply_interval_us, + 'apply_interval_us', dump) + assert_true(dump['target_nid'] == scheme.target_nid, 'target_nid', dump) + assert_migrate_dests_committed(scheme.dests, dump['migrate_dests']) + assert_quota_committed(scheme.quota, dump['quota']) + assert_watermarks_committed(scheme.watermarks, dump['wmarks']) + # TODO: test filters directory + for idx, f in enumerate(scheme.core_filters.filters): + assert_filter_committed(f, dump['filters'][idx]) + for idx, f in enumerate(scheme.ops_filters.filters): + assert_filter_committed(f, dump['ops_filters'][idx]) + +def assert_schemes_committed(schemes, dump): + assert_true(len(schemes) == len(dump), 'len_schemes', dump) + for idx, scheme in enumerate(schemes): + assert_scheme_committed(scheme, dump[idx]) + +def assert_monitoring_attrs_committed(attrs, dump): + assert_true(dump['sample_interval'] == attrs.sample_us, 'sample_interval', + dump) + assert_true(dump['aggr_interval'] == attrs.aggr_us, 'aggr_interval', dump) + assert_true(dump['intervals_goal']['access_bp'] == + attrs.intervals_goal.access_bp, 'access_bp', + dump['intervals_goal']) + assert_true(dump['intervals_goal']['aggrs'] == attrs.intervals_goal.aggrs, + 'aggrs', dump['intervals_goal']) + assert_true(dump['intervals_goal']['min_sample_us'] == + attrs.intervals_goal.min_sample_us, 'min_sample_us', + dump['intervals_goal']) + assert_true(dump['intervals_goal']['max_sample_us'] == + attrs.intervals_goal.max_sample_us, 'max_sample_us', + dump['intervals_goal']) + + assert_true(dump['ops_update_interval'] == attrs.update_us, + 'ops_update_interval', dump) + assert_true(dump['min_nr_regions'] == attrs.min_nr_regions, + 'min_nr_regions', dump) + assert_true(dump['max_nr_regions'] == attrs.max_nr_regions, + 'max_nr_regions', dump) + +def assert_ctx_committed(ctx, dump): + ops_val = { + 'vaddr': 0, + 'fvaddr': 1, + 'paddr': 2, + } + assert_true(dump['ops']['id'] == ops_val[ctx.ops], 'ops_id', dump) + assert_monitoring_attrs_committed(ctx.monitoring_attrs, dump['attrs']) + assert_schemes_committed(ctx.schemes, dump['schemes']) + +def assert_ctxs_committed(ctxs, dump): + assert_true(len(ctxs) == len(dump), 'ctxs length', dump) + for idx, ctx in enumerate(ctxs): + assert_ctx_committed(ctx, dump[idx]) + +def main(): + kdamonds = _damon_sysfs.Kdamonds( + [_damon_sysfs.Kdamond( + contexts=[_damon_sysfs.DamonCtx( + targets=[_damon_sysfs.DamonTarget(pid=-1)], + schemes=[_damon_sysfs.Damos()], + )])]) + err = kdamonds.start() + if err is not None: + print('kdamond start failed: %s' % err) + exit(1) + + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) + if err is not None: + print(err) + kdamonds.stop() + exit(1) + + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts']) + + context = _damon_sysfs.DamonCtx( + monitoring_attrs=_damon_sysfs.DamonAttrs( + sample_us=100000, aggr_us=2000000, + intervals_goal=_damon_sysfs.IntervalsGoal( + access_bp=400, aggrs=3, min_sample_us=5000, + max_sample_us=10000000), + update_us=2000000), + schemes=[_damon_sysfs.Damos( + action='pageout', + access_pattern=_damon_sysfs.DamosAccessPattern( + size=[4096, 2**10], + nr_accesses=[3, 317], + age=[5,71]), + quota=_damon_sysfs.DamosQuota( + sz=100*1024*1024, ms=100, + goals=[_damon_sysfs.DamosQuotaGoal( + metric='node_mem_used_bp', + target_value=9950, + nid=1)], + reset_interval_ms=1500, + weight_sz_permil=20, + weight_nr_accesses_permil=200, + weight_age_permil=1000), + watermarks=_damon_sysfs.DamosWatermarks( + metric = 'free_mem_rate', interval = 500000, # 500 ms + high = 500, mid = 400, low = 50), + target_nid=1, + apply_interval_us=1000000, + dests=_damon_sysfs.DamosDests( + dests=[_damon_sysfs.DamosDest(id=1, weight=30), + _damon_sysfs.DamosDest(id=0, weight=70)]), + core_filters=[ + _damon_sysfs.DamosFilter(type_='addr', matching=True, + allow=False, addr_start=42, + addr_end=4242), + ], + ops_filters=[ + _damon_sysfs.DamosFilter(type_='anon', matching=True, + allow=True), + ], + )]) + context.idx = 0 + context.kdamond = kdamonds.kdamonds[0] + kdamonds.kdamonds[0].contexts = [context] + kdamonds.kdamonds[0].commit() + + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) + if err is not None: + print(err) + exit(1) + + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts']) + + # test online commitment of minimum context. + context = _damon_sysfs.DamonCtx() + context.idx = 0 + context.kdamond = kdamonds.kdamonds[0] + kdamonds.kdamonds[0].contexts = [context] + kdamonds.kdamonds[0].commit() + + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) + if err is not None: + print(err) + exit(1) + + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts']) + + kdamonds.stop() + +if __name__ == '__main__': + main() diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index e9a976d296e2..83e3b7f63d81 100755 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +source _common.sh + # Kselftest frmework requirement - SKIP code is 4. ksft_skip=4 @@ -364,14 +366,5 @@ test_damon_sysfs() test_kdamonds "$damon_sysfs/kdamonds" } -check_dependencies() -{ - if [ $EUID -ne 0 ] - then - echo "Run as root" - exit $ksft_skip - fi -} - check_dependencies test_damon_sysfs "/sys/kernel/mm/damon/admin" diff --git a/tools/testing/selftests/damon/sysfs_memcg_path_leak.sh b/tools/testing/selftests/damon/sysfs_memcg_path_leak.sh new file mode 100755 index 000000000000..64c5d8c518a4 --- /dev/null +++ b/tools/testing/selftests/damon/sysfs_memcg_path_leak.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +if [ $EUID -ne 0 ] +then + echo "Run as root" + exit $ksft_skip +fi + +damon_sysfs="/sys/kernel/mm/damon/admin" +if [ ! -d "$damon_sysfs" ] +then + echo "damon sysfs not found" + exit $ksft_skip +fi + +# ensure filter directory +echo 1 > "$damon_sysfs/kdamonds/nr_kdamonds" +echo 1 > "$damon_sysfs/kdamonds/0/contexts/nr_contexts" +echo 1 > "$damon_sysfs/kdamonds/0/contexts/0/schemes/nr_schemes" +echo 1 > "$damon_sysfs/kdamonds/0/contexts/0/schemes/0/filters/nr_filters" + +filter_dir="$damon_sysfs/kdamonds/0/contexts/0/schemes/0/filters/0" + +before_kb=$(grep Slab /proc/meminfo | awk '{print $2}') + +# try to leak 3000 KiB +for i in {1..102400}; +do + echo "012345678901234567890123456789" > "$filter_dir/memcg_path" +done + +after_kb=$(grep Slab /proc/meminfo | awk '{print $2}') +# expect up to 1500 KiB free from other tasks memory +expected_after_kb_max=$((before_kb + 1500)) + +if [ "$after_kb" -gt "$expected_after_kb_max" ] +then + echo "maybe memcg_path are leaking: $before_kb -> $after_kb" + exit 1 +else + exit 0 +fi diff --git a/tools/testing/selftests/damon/sysfs_update_removed_scheme_dir.sh b/tools/testing/selftests/damon/sysfs_update_removed_scheme_dir.sh index ade35576e748..35fc32beeaf7 100755 --- a/tools/testing/selftests/damon/sysfs_update_removed_scheme_dir.sh +++ b/tools/testing/selftests/damon/sysfs_update_removed_scheme_dir.sh @@ -1,14 +1,12 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +source _common.sh + # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -if [ $EUID -ne 0 ] -then - echo "Run as root" - exit $ksft_skip -fi +check_dependencies damon_sysfs="/sys/kernel/mm/damon/admin" if [ ! -d "$damon_sysfs" ] diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c index 6062723a172e..77aa2897e79f 100644 --- a/tools/testing/selftests/drivers/dma-buf/udmabuf.c +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) void *addr1, *addr2; ksft_print_header(); - ksft_set_plan(6); + ksft_set_plan(7); devfd = open("/dev/udmabuf", O_RDWR); if (devfd < 0) { @@ -248,6 +248,24 @@ int main(int argc, char *argv[]) else ksft_test_result_pass("%s: [PASS,test-6]\n", TEST_PREFIX); + close(buf); + close(memfd); + + /* same test as above but we pin first before writing to memfd */ + page_size = getpagesize() * 512; /* 2 MB */ + size = MEMFD_SIZE * page_size; + memfd = create_memfd_with_seals(size, true); + buf = create_udmabuf_list(devfd, memfd, size); + addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize()); + addr1 = mmap_fd(memfd, size); + write_to_memfd(addr1, size, 'a'); + write_to_memfd(addr1, size, 'b'); + ret = compare_chunks(addr1, addr2, size); + if (ret < 0) + ksft_test_result_fail("%s: [FAIL,test-7]\n", TEST_PREFIX); + else + ksft_test_result_pass("%s: [PASS,test-7]\n", TEST_PREFIX); + close(buf); close(memfd); close(devfd); diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile index be780bcb73a3..984ece05f7f9 100644 --- a/tools/testing/selftests/drivers/net/Makefile +++ b/tools/testing/selftests/drivers/net/Makefile @@ -11,15 +11,19 @@ TEST_GEN_FILES := \ TEST_PROGS := \ napi_id.py \ + napi_threaded.py \ netcons_basic.sh \ + netcons_cmdline.sh \ netcons_fragmented_msg.sh \ netcons_overflow.sh \ netcons_sysdata.sh \ + netpoll_basic.py \ ping.py \ queues.py \ stats.py \ shaper.py \ hds.py \ + xdp.py \ # end of TEST_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile index 2b10854e4b1e..44b98f17f8ff 100644 --- a/tools/testing/selftests/drivers/net/bonding/Makefile +++ b/tools/testing/selftests/drivers/net/bonding/Makefile @@ -10,7 +10,8 @@ TEST_PROGS := \ mode-2-recovery-updelay.sh \ bond_options.sh \ bond-eth-type-change.sh \ - bond_macvlan_ipvlan.sh + bond_macvlan_ipvlan.sh \ + bond_passive_lacp.sh TEST_FILES := \ lag_lib.sh \ diff --git a/tools/testing/selftests/drivers/net/bonding/bond_passive_lacp.sh b/tools/testing/selftests/drivers/net/bonding/bond_passive_lacp.sh new file mode 100755 index 000000000000..9c3b089813df --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/bond_passive_lacp.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test if a bond interface works with lacp_active=off. + +# shellcheck disable=SC2034 +REQUIRE_MZ=no +NUM_NETIFS=0 +lib_dir=$(dirname "$0") +# shellcheck disable=SC1091 +source "$lib_dir"/../../../net/forwarding/lib.sh + +# shellcheck disable=SC2317 +check_port_state() +{ + local netns=$1 + local port=$2 + local state=$3 + + ip -n "${netns}" -d -j link show "$port" | \ + jq -e ".[].linkinfo.info_slave_data.ad_actor_oper_port_state_str | index(\"${state}\") != null" > /dev/null +} + +check_pkt_count() +{ + RET=0 + local ns="$1" + local iface="$2" + + # wait 65s, one per 30s + slowwait_for_counter 65 2 tc_rule_handle_stats_get \ + "dev ${iface} egress" 101 ".packets" "-n ${ns}" &> /dev/null +} + +setup() { + setup_ns c_ns s_ns + + # shellcheck disable=SC2154 + ip -n "${c_ns}" link add eth0 type veth peer name eth0 netns "${s_ns}" + ip -n "${c_ns}" link add eth1 type veth peer name eth1 netns "${s_ns}" + + # Add tc filter to count the pkts + tc -n "${c_ns}" qdisc add dev eth0 clsact + tc -n "${c_ns}" filter add dev eth0 egress handle 101 protocol 0x8809 matchall action pass + tc -n "${s_ns}" qdisc add dev eth1 clsact + tc -n "${s_ns}" filter add dev eth1 egress handle 101 protocol 0x8809 matchall action pass + + ip -n "${s_ns}" link add bond0 type bond mode 802.3ad lacp_active on lacp_rate fast + ip -n "${s_ns}" link set eth0 master bond0 + ip -n "${s_ns}" link set eth1 master bond0 + + ip -n "${c_ns}" link add bond0 type bond mode 802.3ad lacp_active off lacp_rate fast + ip -n "${c_ns}" link set eth0 master bond0 + ip -n "${c_ns}" link set eth1 master bond0 + +} + +trap cleanup_all_ns EXIT +setup + +# The bond will send 2 lacpdu pkts during init time, let's wait at least 2s +# after interface up +ip -n "${c_ns}" link set bond0 up +sleep 2 + +# 1. The passive side shouldn't send LACPDU. +check_pkt_count "${c_ns}" "eth0" && RET=1 +log_test "802.3ad lacp_active off" "init port" + +ip -n "${s_ns}" link set bond0 up +# 2. The passive side should not have the 'active' flag. +RET=0 +slowwait 2 check_port_state "${c_ns}" "eth0" "active" && RET=1 +log_test "802.3ad lacp_active off" "port state active" + +# 3. The active side should have the 'active' flag. +RET=0 +slowwait 2 check_port_state "${s_ns}" "eth0" "active" || RET=1 +log_test "802.3ad lacp_active on" "port state active" + +# 4. Make sure the connection is not expired. +RET=0 +slowwait 5 check_port_state "${s_ns}" "eth0" "distributing" +slowwait 10 check_port_state "${s_ns}" "eth0" "expired" && RET=1 +log_test "bond 802.3ad lacp_active off" "port connection" + +# After testing, disconnect one port on each side to check the state. +ip -n "${s_ns}" link set eth0 nomaster +ip -n "${s_ns}" link set eth0 up +ip -n "${c_ns}" link set eth1 nomaster +ip -n "${c_ns}" link set eth1 up +# Due to Periodic Machine and Rx Machine state change, the bond will still +# send lacpdu pkts in a few seconds. sleep at lease 5s to make sure +# negotiation finished +sleep 5 + +# 5. The active side should keep sending LACPDU. +check_pkt_count "${s_ns}" "eth1" || RET=1 +log_test "bond 802.3ad lacp_active on" "port pkt after disconnect" + +# 6. The passive side shouldn't send LACPDU anymore. +check_pkt_count "${c_ns}" "eth0" && RET=1 +log_test "bond 802.3ad lacp_active off" "port pkt after disconnect" + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/drivers/net/bonding/config b/tools/testing/selftests/drivers/net/bonding/config index dad4e5fda4db..4d16a69ffc65 100644 --- a/tools/testing/selftests/drivers/net/bonding/config +++ b/tools/testing/selftests/drivers/net/bonding/config @@ -6,6 +6,7 @@ CONFIG_MACVLAN=y CONFIG_IPVLAN=y CONFIG_NET_ACT_GACT=y CONFIG_NET_CLS_FLOWER=y +CONFIG_NET_CLS_MATCHALL=m CONFIG_NET_SCH_INGRESS=y CONFIG_NLMON=y CONFIG_VETH=y diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index df2c047ffa90..fdc97355588c 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -16,6 +16,7 @@ TEST_PROGS = \ irq.py \ loopback.sh \ pp_alloc_fail.py \ + rss_api.py \ rss_ctx.py \ rss_input_xfrm.py \ tso.py \ diff --git a/tools/testing/selftests/drivers/net/hw/csum.py b/tools/testing/selftests/drivers/net/hw/csum.py index cd23af875317..3e3a89a34afe 100755 --- a/tools/testing/selftests/drivers/net/hw/csum.py +++ b/tools/testing/selftests/drivers/net/hw/csum.py @@ -17,7 +17,7 @@ def test_receive(cfg, ipver="6", extra_args=None): ip_args = f"-{ipver} -S {cfg.remote_addr_v[ipver]} -D {cfg.addr_v[ipver]}" rx_cmd = f"{cfg.bin_local} -i {cfg.ifname} -n 100 {ip_args} -r 1 -R {extra_args}" - tx_cmd = f"{cfg.bin_remote} -i {cfg.ifname} -n 100 {ip_args} -r 1 -T {extra_args}" + tx_cmd = f"{cfg.bin_remote} -i {cfg.remote_ifname} -n 100 {ip_args} -r 1 -T {extra_args}" with bkg(rx_cmd, exit_wait=True): wait_port_listen(34000, proto="udp") @@ -37,7 +37,7 @@ def test_transmit(cfg, ipver="6", extra_args=None): if extra_args != "-U -Z": extra_args += " -r 1" - rx_cmd = f"{cfg.bin_remote} -i {cfg.ifname} -L 1 -n 100 {ip_args} -R {extra_args}" + rx_cmd = f"{cfg.bin_remote} -i {cfg.remote_ifname} -L 1 -n 100 {ip_args} -R {extra_args}" tx_cmd = f"{cfg.bin_local} -i {cfg.ifname} -L 1 -n 100 {ip_args} -T {extra_args}" with bkg(rx_cmd, host=cfg.remote, exit_wait=True): diff --git a/tools/testing/selftests/drivers/net/hw/devlink_rate_tc_bw.py b/tools/testing/selftests/drivers/net/hw/devlink_rate_tc_bw.py new file mode 100755 index 000000000000..ead6784d1910 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/devlink_rate_tc_bw.py @@ -0,0 +1,465 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +Devlink Rate TC Bandwidth Test Suite +=================================== + +This test suite verifies the functionality of devlink-rate traffic class (TC) +bandwidth distribution in a virtualized environment. The tests validate that +bandwidth can be properly allocated between different traffic classes and +that TC mapping works as expected. + +Test Environment: +---------------- +- Creates 1 VF +- Establishes a bridge connecting the VF representor and the uplink representor +- Sets up 2 VLAN interfaces on the VF with different VLAN IDs (101, 102) +- Configures different traffic classes (TC3 and TC4) for each VLAN + +Test Cases: +---------- +1. test_no_tc_mapping_bandwidth: + - Verifies that without TC mapping, bandwidth is NOT distributed according to + the configured 80/20 split between TC4 and TC3 + - This test should fail if bandwidth matches the 80/20 split without TC + mapping + - Expected: Bandwidth should NOT be distributed as 80/20 + +2. test_tc_mapping_bandwidth: + - Configures TC mapping using mqprio qdisc + - Verifies that with TC mapping, bandwidth IS distributed according to the + configured 80/20 split between TC3 and TC4 + - Expected: Bandwidth should be distributed as 80/20 + +Bandwidth Distribution: +---------------------- +- TC3 (VLAN 101): Configured for 80% of total bandwidth +- TC4 (VLAN 102): Configured for 20% of total bandwidth +- Total bandwidth: 1Gbps +- Tolerance: +-12% + +Hardware-Specific Behavior (mlx5): +-------------------------- +mlx5 hardware enforces traffic class separation by ensuring that each transmit +queue (SQ) is associated with a single TC. If a packet is sent on a queue that +doesn't match the expected TC (based on DSCP or VLAN priority and hypervisor-set +mapping), the hardware moves the queue to the correct TC scheduler to preserve +traffic isolation. + +This behavior means that even without explicit TC-to-queue mapping, bandwidth +enforcement may still appear to work—because the hardware dynamically adjusts +the scheduling context. However, this can lead to performance issues in high +rates and HOL blocking if traffic from different TCs is mixed on the same queue. +""" + +import json +import os +import subprocess +import threading +import time + +from lib.py import ksft_pr, ksft_run, ksft_exit +from lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx +from lib.py import NetDrvEpEnv, DevlinkFamily +from lib.py import NlError +from lib.py import cmd, defer, ethtool, ip + + +class BandwidthValidator: + """ + Validates bandwidth totals and per-TC shares against expected values + with a tolerance. + """ + + def __init__(self): + self.tolerance_percent = 12 + self.expected_total_gbps = 1.0 + self.total_min_expected = self.min_expected(self.expected_total_gbps) + self.total_max_expected = self.max_expected(self.expected_total_gbps) + self.tc_expected_percent = { + 3: 20.0, + 4: 80.0, + } + + def min_expected(self, value): + """Calculates the minimum acceptable value based on tolerance.""" + return value - (value * self.tolerance_percent / 100) + + def max_expected(self, value): + """Calculates the maximum acceptable value based on tolerance.""" + return value + (value * self.tolerance_percent / 100) + + def bound(self, expected, value): + """Returns True if value is within expected tolerance.""" + return self.min_expected(expected) <= value <= self.max_expected(expected) + + def tc_bandwidth_bound(self, value, tc_ix): + """ + Returns True if the given bandwidth value is within tolerance + for the TC's expected bandwidth. + """ + expected = self.tc_expected_percent[tc_ix] + return self.bound(expected, value) + + +def setup_vf(cfg, set_tc_mapping=True): + """ + Sets up a VF on the given network interface. + + Enables SR-IOV and switchdev mode, brings the VF interface up, + and optionally configures TC mapping using mqprio. + """ + try: + cmd(f"devlink dev eswitch set pci/{cfg.pci} mode switchdev") + defer(cmd, f"devlink dev eswitch set pci/{cfg.pci} mode legacy") + except Exception as exc: + raise KsftSkipEx(f"Failed to enable switchdev mode on {cfg.pci}") from exc + try: + cmd(f"echo 1 > /sys/class/net/{cfg.ifname}/device/sriov_numvfs") + defer(cmd, f"echo 0 > /sys/class/net/{cfg.ifname}/device/sriov_numvfs") + except Exception as exc: + raise KsftSkipEx(f"Failed to enable SR-IOV on {cfg.ifname}") from exc + + time.sleep(2) + vf_ifc = (os.listdir( + f"/sys/class/net/{cfg.ifname}/device/virtfn0/net") or [None])[0] + if vf_ifc: + ip(f"link set dev {vf_ifc} up") + else: + raise KsftSkipEx("VF interface not found") + if set_tc_mapping: + cmd(f"tc qdisc add dev {vf_ifc} root handle 5 mqprio mode dcb hw 1 num_tc 8") + + return vf_ifc + + +def setup_vlans_on_vf(vf_ifc): + """ + Sets up two VLAN interfaces on the given VF, each mapped to a different TC. + """ + vlan_configs = [ + {"vlan_id": 101, "tc": 3, "ip": "198.51.100.2"}, + {"vlan_id": 102, "tc": 4, "ip": "198.51.100.10"}, + ] + + for config in vlan_configs: + vlan_dev = f"{vf_ifc}.{config['vlan_id']}" + ip(f"link add link {vf_ifc} name {vlan_dev} type vlan id {config['vlan_id']}") + ip(f"addr add {config['ip']}/29 dev {vlan_dev}") + ip(f"link set dev {vlan_dev} up") + ip(f"link set dev {vlan_dev} type vlan egress-qos-map 0:{config['tc']}") + ksft_pr(f"Created VLAN {vlan_dev} on {vf_ifc} with tc {config['tc']} and IP {config['ip']}") + + +def get_vf_info(cfg): + """ + Finds the VF representor interface and devlink port index + for the given PCI device used in the test environment. + """ + cfg.vf_representor = None + cfg.vf_port_index = None + out = subprocess.check_output(["devlink", "-j", "port", "show"], encoding="utf-8") + ports = json.loads(out)["port"] + + for port_name, props in ports.items(): + netdev = props.get("netdev") + + if (port_name.startswith(f"pci/{cfg.pci}/") and + props.get("vfnum") == 0): + cfg.vf_representor = netdev + cfg.vf_port_index = int(port_name.split("/")[-1]) + break + + +def setup_bridge(cfg): + """ + Creates and configures a Linux bridge, with both the uplink + and VF representor interfaces attached to it. + """ + bridge_name = f"br_{os.getpid()}" + ip(f"link add name {bridge_name} type bridge") + defer(cmd, f"ip link del name {bridge_name} type bridge") + + ip(f"link set dev {cfg.ifname} master {bridge_name}") + + rep_name = cfg.vf_representor + if rep_name: + ip(f"link set dev {rep_name} master {bridge_name}") + ip(f"link set dev {rep_name} up") + ksft_pr(f"Set representor {rep_name} up and added to bridge") + else: + raise KsftSkipEx("Could not find representor for the VF") + + ip(f"link set dev {bridge_name} up") + + +def setup_devlink_rate(cfg): + """ + Configures devlink rate tx_max and traffic class bandwidth for the VF. + """ + port_index = cfg.vf_port_index + if port_index is None: + raise KsftSkipEx("Could not find VF port index") + try: + cfg.devnl.rate_set({ + "bus-name": "pci", + "dev-name": cfg.pci, + "port-index": port_index, + "rate-tx-max": 125000000, + "rate-tc-bws": [ + {"index": 0, "bw": 0}, + {"index": 1, "bw": 0}, + {"index": 2, "bw": 0}, + {"index": 3, "bw": 20}, + {"index": 4, "bw": 80}, + {"index": 5, "bw": 0}, + {"index": 6, "bw": 0}, + {"index": 7, "bw": 0}, + ] + }) + except NlError as exc: + if exc.error == 95: # EOPNOTSUPP + raise KsftSkipEx("devlink rate configuration is not supported on the VF") from exc + raise KsftFailEx(f"rate_set failed on VF port {port_index}") from exc + + +def setup_remote_server(cfg): + """ + Sets up VLAN interfaces and starts iperf3 servers on the remote side. + """ + remote_dev = cfg.remote_ifname + vlan_ids = [101, 102] + remote_ips = ["198.51.100.1", "198.51.100.9"] + + for vlan_id, ip_addr in zip(vlan_ids, remote_ips): + vlan_dev = f"{remote_dev}.{vlan_id}" + cmd(f"ip link add link {remote_dev} name {vlan_dev} " + f"type vlan id {vlan_id}", host=cfg.remote) + cmd(f"ip addr add {ip_addr}/29 dev {vlan_dev}", host=cfg.remote) + cmd(f"ip link set dev {vlan_dev} up", host=cfg.remote) + cmd(f"iperf3 -s -1 -B {ip_addr}",background=True, host=cfg.remote) + defer(cmd, f"ip link del {vlan_dev}", host=cfg.remote) + + +def setup_test_environment(cfg, set_tc_mapping=True): + """ + Sets up the complete test environment including VF creation, VLANs, + bridge configuration, devlink rate setup, and the remote server. + """ + vf_ifc = setup_vf(cfg, set_tc_mapping) + ksft_pr(f"Created VF interface: {vf_ifc}") + + setup_vlans_on_vf(vf_ifc) + + get_vf_info(cfg) + setup_bridge(cfg) + + setup_devlink_rate(cfg) + setup_remote_server(cfg) + time.sleep(2) + + +def run_iperf_client(server_ip, local_ip, barrier, min_expected_gbps=0.1): + """ + Runs a single iperf3 client instance, binding to the given local IP. + Waits on a barrier to synchronize with other threads. + """ + try: + barrier.wait(timeout=10) + except Exception as exc: + raise KsftFailEx("iperf3 barrier wait timed") from exc + + iperf_cmd = ["iperf3", "-c", server_ip, "-B", local_ip, "-J"] + result = subprocess.run(iperf_cmd, capture_output=True, text=True, + check=True) + + try: + output = json.loads(result.stdout) + bits_per_second = output["end"]["sum_received"]["bits_per_second"] + gbps = bits_per_second / 1e9 + if gbps < min_expected_gbps: + ksft_pr( + f"iperf3 bandwidth too low: {gbps:.2f} Gbps " + f"(expected ≥ {min_expected_gbps} Gbps)" + ) + return None + return gbps + except json.JSONDecodeError as exc: + ksft_pr(f"Failed to parse iperf3 JSON output: {exc}") + return None + + +def run_bandwidth_test(): + """ + Launches iperf3 client threads for each VLAN/TC pair and collects results. + """ + def _run_iperf_client_thread(server_ip, local_ip, results, barrier, tc_ix): + results[tc_ix] = run_iperf_client(server_ip, local_ip, barrier) + + vf_vlan_data = [ + # (local_ip, remote_ip, TC) + ("198.51.100.2", "198.51.100.1", 3), + ("198.51.100.10", "198.51.100.9", 4), + ] + + results = {} + threads = [] + start_barrier = threading.Barrier(len(vf_vlan_data)) + + for local_ip, remote_ip, tc_ix in vf_vlan_data: + thread = threading.Thread( + target=_run_iperf_client_thread, + args=(remote_ip, local_ip, results, start_barrier, tc_ix) + ) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + + for tc_ix, tc_bw in results.items(): + if tc_bw is None: + raise KsftFailEx("iperf3 client failed; cannot evaluate bandwidth") + + return results + +def calculate_bandwidth_percentages(results): + """ + Calculates the percentage of total bandwidth received by TC3 and TC4. + """ + if 3 not in results or 4 not in results: + raise KsftFailEx(f"Missing expected TC results in {results}") + + tc3_bw = results[3] + tc4_bw = results[4] + total_bw = tc3_bw + tc4_bw + tc3_percentage = (tc3_bw / total_bw) * 100 + tc4_percentage = (tc4_bw / total_bw) * 100 + + return { + 'tc3_bw': tc3_bw, + 'tc4_bw': tc4_bw, + 'tc3_percentage': tc3_percentage, + 'tc4_percentage': tc4_percentage, + 'total_bw': total_bw + } + + +def print_bandwidth_results(bw_data, test_name): + """ + Prints bandwidth measurements and TC usage summary for a given test. + """ + ksft_pr(f"Bandwidth check results {test_name}:") + ksft_pr(f"TC 3: {bw_data['tc3_bw']:.2f} Gbits/sec") + ksft_pr(f"TC 4: {bw_data['tc4_bw']:.2f} Gbits/sec") + ksft_pr(f"Total bandwidth: {bw_data['total_bw']:.2f} Gbits/sec") + ksft_pr(f"TC 3 percentage: {bw_data['tc3_percentage']:.1f}%") + ksft_pr(f"TC 4 percentage: {bw_data['tc4_percentage']:.1f}%") + + +def verify_total_bandwidth(bw_data, validator): + """ + Ensures the total measured bandwidth falls within the acceptable tolerance. + """ + total = bw_data['total_bw'] + + if validator.bound(validator.expected_total_gbps, total): + return + + if total < validator.total_min_expected: + raise KsftSkipEx( + f"Total bandwidth {total:.2f} Gbps < minimum " + f"{validator.total_min_expected:.2f} Gbps; " + f"parent tx_max ({validator.expected_total_gbps:.1f} G) " + f"not reached, cannot validate share" + ) + + raise KsftFailEx( + f"Total bandwidth {total:.2f} Gbps exceeds allowed ceiling " + f"{validator.total_max_expected:.2f} Gbps " + f"(VF tx_max set to {validator.expected_total_gbps:.1f} G)" + ) + + +def check_bandwidth_distribution(bw_data, validator): + """ + Checks whether the measured TC3 and TC4 bandwidth percentages + fall within their expected tolerance ranges. + + Returns: + bool: True if both TC3 and TC4 percentages are within bounds. + """ + tc3_valid = validator.tc_bandwidth_bound(bw_data['tc3_percentage'], 3) + tc4_valid = validator.tc_bandwidth_bound(bw_data['tc4_percentage'], 4) + + return tc3_valid and tc4_valid + + +def run_bandwidth_distribution_test(cfg, set_tc_mapping): + """ + Runs parallel iperf3 tests for both TCs and collects results. + """ + setup_test_environment(cfg, set_tc_mapping) + bandwidths = run_bandwidth_test() + bw_data = calculate_bandwidth_percentages(bandwidths) + test_name = "with TC mapping" if set_tc_mapping else "without TC mapping" + print_bandwidth_results(bw_data, test_name) + + verify_total_bandwidth(bw_data, cfg.bw_validator) + + return check_bandwidth_distribution(bw_data, cfg.bw_validator) + + +def test_no_tc_mapping_bandwidth(cfg): + """ + Verifies that bandwidth is not split 80/20 without traffic class mapping. + """ + pass_bw_msg = "Bandwidth is NOT distributed as 80/20 without TC mapping" + fail_bw_msg = "Bandwidth matched 80/20 split without TC mapping" + is_mlx5 = "driver: mlx5" in ethtool(f"-i {cfg.ifname}").stdout + + if run_bandwidth_distribution_test(cfg, set_tc_mapping=False): + if is_mlx5: + raise KsftXfailEx(fail_bw_msg) + raise KsftFailEx(fail_bw_msg) + if is_mlx5: + raise KsftFailEx("mlx5 behavior changed:" + pass_bw_msg) + ksft_pr(pass_bw_msg) + + +def test_tc_mapping_bandwidth(cfg): + """ + Verifies that bandwidth is correctly split 80/20 between TC3 and TC4 + when traffic class mapping is set. + """ + if run_bandwidth_distribution_test(cfg, set_tc_mapping=True): + ksft_pr("Bandwidth is distributed as 80/20 with TC mapping") + else: + raise KsftFailEx("Bandwidth did not match 80/20 split with TC mapping") + + +def main() -> None: + """ + Main entry point for running the test cases. + """ + with NetDrvEpEnv(__file__, nsim_test=False) as cfg: + cfg.devnl = DevlinkFamily() + + cfg.pci = os.path.basename( + os.path.realpath(f"/sys/class/net/{cfg.ifname}/device") + ) + if not cfg.pci: + raise KsftSkipEx("Could not get PCI address of the interface") + cfg.require_cmd("iperf3", local=True, remote=True) + + cfg.bw_validator = BandwidthValidator() + + cases = [test_no_tc_mapping_bandwidth, test_tc_mapping_bandwidth] + + ksft_run(cases=cases, args=(cfg,)) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py index 7947650210a0..baa2f24240ba 100755 --- a/tools/testing/selftests/drivers/net/hw/devmem.py +++ b/tools/testing/selftests/drivers/net/hw/devmem.py @@ -51,15 +51,14 @@ def check_tx(cfg) -> None: @ksft_disruptive def check_tx_chunks(cfg) -> None: - cfg.require_ipver("6") require_devmem(cfg) port = rand_port() - listen_cmd = f"socat -U - TCP6-LISTEN:{port}" + listen_cmd = f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}" with bkg(listen_cmd, exit_wait=True) as socat: wait_port_listen(port) - cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_remote} -f {cfg.ifname} -s {cfg.addr_v['6']} -p {port} -z 3", host=cfg.remote, shell=True) + cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_remote} -f {cfg.ifname} -s {cfg.addr} -p {port} -z 3", host=cfg.remote, shell=True) ksft_eq(socat.stdout.strip(), "hello\nworld") diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py index 9c03fd777f3d..712c806508b5 100755 --- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py +++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py @@ -3,37 +3,37 @@ import re from os import path -from lib.py import ksft_run, ksft_exit +from lib.py import ksft_run, ksft_exit, KsftSkipEx from lib.py import NetDrvEpEnv from lib.py import bkg, cmd, defer, ethtool, rand_port, wait_port_listen def _get_current_settings(cfg): - output = ethtool(f"-g {cfg.ifname}", json=True, host=cfg.remote)[0] + output = ethtool(f"-g {cfg.ifname}", json=True)[0] return (output['rx'], output['hds-thresh']) def _get_combined_channels(cfg): - output = ethtool(f"-l {cfg.ifname}", host=cfg.remote).stdout + output = ethtool(f"-l {cfg.ifname}").stdout values = re.findall(r'Combined:\s+(\d+)', output) return int(values[1]) def _create_rss_ctx(cfg, chan): - output = ethtool(f"-X {cfg.ifname} context new start {chan} equal 1", host=cfg.remote).stdout + output = ethtool(f"-X {cfg.ifname} context new start {chan} equal 1").stdout values = re.search(r'New RSS context is (\d+)', output).group(1) ctx_id = int(values) - return (ctx_id, defer(ethtool, f"-X {cfg.ifname} delete context {ctx_id}", host=cfg.remote)) + return (ctx_id, defer(ethtool, f"-X {cfg.ifname} delete context {ctx_id}")) def _set_flow_rule(cfg, port, chan): - output = ethtool(f"-N {cfg.ifname} flow-type tcp6 dst-port {port} action {chan}", host=cfg.remote).stdout + output = ethtool(f"-N {cfg.ifname} flow-type tcp6 dst-port {port} action {chan}").stdout values = re.search(r'ID (\d+)', output).group(1) return int(values) def _set_flow_rule_rss(cfg, port, ctx_id): - output = ethtool(f"-N {cfg.ifname} flow-type tcp6 dst-port {port} context {ctx_id}", host=cfg.remote).stdout + output = ethtool(f"-N {cfg.ifname} flow-type tcp6 dst-port {port} context {ctx_id}").stdout values = re.search(r'ID (\d+)', output).group(1) return int(values) @@ -47,26 +47,26 @@ def test_zcrx(cfg) -> None: (rx_ring, hds_thresh) = _get_current_settings(cfg) port = rand_port() - ethtool(f"-G {cfg.ifname} tcp-data-split on", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto", host=cfg.remote) + ethtool(f"-G {cfg.ifname} tcp-data-split on") + defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto") - ethtool(f"-G {cfg.ifname} hds-thresh 0", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} hds-thresh 0") + defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}") - ethtool(f"-G {cfg.ifname} rx 64", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} rx 64") + defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}") - ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}", host=cfg.remote) - defer(ethtool, f"-X {cfg.ifname} default", host=cfg.remote) + ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}") + defer(ethtool, f"-X {cfg.ifname} default") flow_rule_id = _set_flow_rule(cfg, port, combined_chans - 1) - defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}", host=cfg.remote) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") - rx_cmd = f"{cfg.bin_remote} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1}" - tx_cmd = f"{cfg.bin_local} -c -h {cfg.remote_addr_v['6']} -p {port} -l 12840" - with bkg(rx_cmd, host=cfg.remote, exit_wait=True): - wait_port_listen(port, proto="tcp", host=cfg.remote) - cmd(tx_cmd) + rx_cmd = f"{cfg.bin_local} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1}" + tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {port} -l 12840" + with bkg(rx_cmd, exit_wait=True): + wait_port_listen(port, proto="tcp") + cmd(tx_cmd, host=cfg.remote) def test_zcrx_oneshot(cfg) -> None: @@ -78,26 +78,26 @@ def test_zcrx_oneshot(cfg) -> None: (rx_ring, hds_thresh) = _get_current_settings(cfg) port = rand_port() - ethtool(f"-G {cfg.ifname} tcp-data-split on", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto", host=cfg.remote) + ethtool(f"-G {cfg.ifname} tcp-data-split on") + defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto") - ethtool(f"-G {cfg.ifname} hds-thresh 0", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} hds-thresh 0") + defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}") - ethtool(f"-G {cfg.ifname} rx 64", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} rx 64") + defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}") - ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}", host=cfg.remote) - defer(ethtool, f"-X {cfg.ifname} default", host=cfg.remote) + ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}") + defer(ethtool, f"-X {cfg.ifname} default") flow_rule_id = _set_flow_rule(cfg, port, combined_chans - 1) - defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}", host=cfg.remote) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") - rx_cmd = f"{cfg.bin_remote} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1} -o 4" - tx_cmd = f"{cfg.bin_local} -c -h {cfg.remote_addr_v['6']} -p {port} -l 4096 -z 16384" - with bkg(rx_cmd, host=cfg.remote, exit_wait=True): - wait_port_listen(port, proto="tcp", host=cfg.remote) - cmd(tx_cmd) + rx_cmd = f"{cfg.bin_local} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1} -o 4" + tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {port} -l 4096 -z 16384" + with bkg(rx_cmd, exit_wait=True): + wait_port_listen(port, proto="tcp") + cmd(tx_cmd, host=cfg.remote) def test_zcrx_rss(cfg) -> None: @@ -109,27 +109,27 @@ def test_zcrx_rss(cfg) -> None: (rx_ring, hds_thresh) = _get_current_settings(cfg) port = rand_port() - ethtool(f"-G {cfg.ifname} tcp-data-split on", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto", host=cfg.remote) + ethtool(f"-G {cfg.ifname} tcp-data-split on") + defer(ethtool, f"-G {cfg.ifname} tcp-data-split auto") - ethtool(f"-G {cfg.ifname} hds-thresh 0", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} hds-thresh 0") + defer(ethtool, f"-G {cfg.ifname} hds-thresh {hds_thresh}") - ethtool(f"-G {cfg.ifname} rx 64", host=cfg.remote) - defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}", host=cfg.remote) + ethtool(f"-G {cfg.ifname} rx 64") + defer(ethtool, f"-G {cfg.ifname} rx {rx_ring}") - ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}", host=cfg.remote) - defer(ethtool, f"-X {cfg.ifname} default", host=cfg.remote) + ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}") + defer(ethtool, f"-X {cfg.ifname} default") (ctx_id, delete_ctx) = _create_rss_ctx(cfg, combined_chans - 1) flow_rule_id = _set_flow_rule_rss(cfg, port, ctx_id) - defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}", host=cfg.remote) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") - rx_cmd = f"{cfg.bin_remote} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1}" - tx_cmd = f"{cfg.bin_local} -c -h {cfg.remote_addr_v['6']} -p {port} -l 12840" - with bkg(rx_cmd, host=cfg.remote, exit_wait=True): - wait_port_listen(port, proto="tcp", host=cfg.remote) - cmd(tx_cmd) + rx_cmd = f"{cfg.bin_local} -s -p {port} -i {cfg.ifname} -q {combined_chans - 1}" + tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {port} -l 12840" + with bkg(rx_cmd, exit_wait=True): + wait_port_listen(port, proto="tcp") + cmd(tx_cmd, host=cfg.remote) def main() -> None: diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py index b582885786f5..1462a339a74b 100644 --- a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py +++ b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py @@ -7,8 +7,25 @@ KSFT_DIR = (Path(__file__).parent / "../../../../..").resolve() try: sys.path.append(KSFT_DIR.as_posix()) + from net.lib.py import * from drivers.net.lib.py import * + + # Import one by one to avoid pylint false positives + from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ + NlError, RtnlFamily, DevlinkFamily + from net.lib.py import CmdExitFailure + from net.lib.py import bkg, cmd, defer, ethtool, fd_read_timeout, ip, \ + rand_port, tool, wait_port_listen + from net.lib.py import fd_read_timeout + from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx + from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ + ksft_setup + from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ + ksft_ne, ksft_not_in, ksft_raises, ksft_true + from net.lib.py import NetNSEnter + from drivers.net.lib.py import GenerateTraffic + from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv except ModuleNotFoundError as e: ksft_pr("Failed importing `net` library from kernel sources") ksft_pr(str(e)) diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c index 02e4d3d7ded2..72f828021f83 100644 --- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c +++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c @@ -526,12 +526,10 @@ static struct netdev_queue_id *create_queues(void) struct netdev_queue_id *queues; size_t i = 0; - queues = calloc(num_queues, sizeof(*queues)); + queues = netdev_queue_id_alloc(num_queues); for (i = 0; i < num_queues; i++) { - queues[i]._present.type = 1; - queues[i]._present.id = 1; - queues[i].type = NETDEV_QUEUE_TYPE_RX; - queues[i].id = start_queue + i; + netdev_queue_id_set_type(&queues[i], NETDEV_QUEUE_TYPE_RX); + netdev_queue_id_set_id(&queues[i], start_queue + i); } return queues; @@ -852,7 +850,6 @@ static int do_client(struct memory_buffer *mem) ssize_t line_size = 0; struct cmsghdr *cmsg; char *line = NULL; - unsigned long mid; size_t len = 0; int socket_fd; __u32 ddmabuf; diff --git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py new file mode 100755 index 000000000000..19847f3d4a00 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/rss_api.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +API level tests for RSS (mostly Netlink vs IOCTL). +""" + +import errno +import glob +import random +from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises +from lib.py import KsftSkipEx, KsftFailEx +from lib.py import defer, ethtool, CmdExitFailure +from lib.py import EthtoolFamily, NlError +from lib.py import NetDrvEnv + + +def _require_2qs(cfg): + qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) + if qcnt < 2: + raise KsftSkipEx(f"Local has only {qcnt} queues") + return qcnt + + +def _ethtool_create(cfg, act, opts): + output = ethtool(f"{act} {cfg.ifname} {opts}").stdout + # Output will be something like: "New RSS context is 1" or + # "Added rule with ID 7", we want the integer from the end + return int(output.split()[-1]) + + +def _ethtool_get_cfg(cfg, fl_type, to_nl=False): + descr = ethtool(f"-n {cfg.ifname} rx-flow-hash {fl_type}").stdout + + if to_nl: + converter = { + "IP SA": "ip-src", + "IP DA": "ip-dst", + "L4 bytes 0 & 1 [TCP/UDP src port]": "l4-b-0-1", + "L4 bytes 2 & 3 [TCP/UDP dst port]": "l4-b-2-3", + } + + ret = set() + else: + converter = { + "IP SA": "s", + "IP DA": "d", + "L3 proto": "t", + "L4 bytes 0 & 1 [TCP/UDP src port]": "f", + "L4 bytes 2 & 3 [TCP/UDP dst port]": "n", + } + + ret = "" + + for line in descr.split("\n")[1:-2]: + # if this raises we probably need to add more keys to converter above + if to_nl: + ret.add(converter[line]) + else: + ret += converter[line] + return ret + + +def test_rxfh_nl_set_fail(cfg): + """ + Test error path of Netlink SET. + """ + _require_2qs(cfg) + + ethnl = EthtoolFamily() + ethnl.ntf_subscribe("monitor") + + with ksft_raises(NlError): + ethnl.rss_set({"header": {"dev-name": "lo"}, + "indir": None}) + + with ksft_raises(NlError): + ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "indir": [100000]}) + ntf = next(ethnl.poll_ntf(duration=0.2), None) + ksft_is(ntf, None) + + +def test_rxfh_nl_set_indir(cfg): + """ + Test setting indirection table via Netlink. + """ + qcnt = _require_2qs(cfg) + + # Test some SETs with a value + reset = defer(cfg.ethnl.rss_set, + {"header": {"dev-index": cfg.ifindex}, "indir": None}) + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "indir": [1]}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(set(rss.get("indir", [-1])), {1}) + + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "indir": [0, 1]}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) + + # Make sure we can't set the queue count below max queue used + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 0 rx 1") + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 1 rx 0") + + # Test reset back to default + reset.exec() + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(set(rss.get("indir", [-1])), set(range(qcnt))) + + +def test_rxfh_nl_set_indir_ctx(cfg): + """ + Test setting indirection table for a custom context via Netlink. + """ + _require_2qs(cfg) + + # Get setting for ctx 0, we'll make sure they don't get clobbered + dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + + # Create context + ctx_id = _ethtool_create(cfg, "-X", "context new") + defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") + + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "context": ctx_id, "indir": [1]}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, + "context": ctx_id}) + ksft_eq(set(rss.get("indir", [-1])), {1}) + + ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(ctx0, dflt) + + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "context": ctx_id, "indir": [0, 1]}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, + "context": ctx_id}) + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) + + ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(ctx0, dflt) + + # Make sure we can't set the queue count below max queue used + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 0 rx 1") + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 1 rx 0") + + +def test_rxfh_indir_ntf(cfg): + """ + Check that Netlink notifications are generated when RSS indirection + table was modified. + """ + _require_2qs(cfg) + + ethnl = EthtoolFamily() + ethnl.ntf_subscribe("monitor") + + ethtool(f"--disable-netlink -X {cfg.ifname} weight 0 1") + reset = defer(ethtool, f"-X {cfg.ifname} default") + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("No notification received") + ksft_eq(ntf["name"], "rss-ntf") + ksft_eq(set(ntf["msg"]["indir"]), {1}) + + reset.exec() + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("No notification received after reset") + ksft_eq(ntf["name"], "rss-ntf") + ksft_is(ntf["msg"].get("context"), None) + ksft_ne(set(ntf["msg"]["indir"]), {1}) + + +def test_rxfh_indir_ctx_ntf(cfg): + """ + Check that Netlink notifications are generated when RSS indirection + table was modified on an additional RSS context. + """ + _require_2qs(cfg) + + ctx_id = _ethtool_create(cfg, "-X", "context new") + defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") + + ethnl = EthtoolFamily() + ethnl.ntf_subscribe("monitor") + + ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} weight 0 1") + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("No notification received") + ksft_eq(ntf["name"], "rss-ntf") + ksft_eq(ntf["msg"].get("context"), ctx_id) + ksft_eq(set(ntf["msg"]["indir"]), {1}) + + +def test_rxfh_nl_set_key(cfg): + """ + Test setting hashing key via Netlink. + """ + + dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + defer(cfg.ethnl.rss_set, + {"header": {"dev-index": cfg.ifindex}, + "hkey": dflt["hkey"], "indir": None}) + + # Empty key should error out + with ksft_raises(NlError) as cm: + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "hkey": None}) + ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.hkey') + + # Set key to random + mod = random.randbytes(len(dflt["hkey"])) + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "hkey": mod}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(rss.get("hkey", [-1]), mod) + + # Set key to random and indir tbl to something at once + mod = random.randbytes(len(dflt["hkey"])) + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "indir": [0, 1], "hkey": mod}) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(rss.get("hkey", [-1]), mod) + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) + + +def test_rxfh_fields(cfg): + """ + Test reading Rx Flow Hash over Netlink. + """ + + flow_types = ["tcp4", "tcp6", "udp4", "udp6"] + ethnl = EthtoolFamily() + + cfg_nl = ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + for fl_type in flow_types: + one = _ethtool_get_cfg(cfg, fl_type, to_nl=True) + ksft_eq(one, cfg_nl["flow-hash"][fl_type], + comment="Config for " + fl_type) + + +def test_rxfh_fields_set(cfg): + """ Test configuring Rx Flow Hash over Netlink. """ + + flow_types = ["tcp4", "tcp6", "udp4", "udp6"] + + # Collect current settings + cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + # symmetric hashing is config-order-sensitive make sure we leave + # symmetric mode, or make the flow-hash sym-compatible first + changes = [{"flow-hash": cfg_old["flow-hash"],}, + {"input-xfrm": cfg_old.get("input-xfrm", {}),}] + if cfg_old.get("input-xfrm"): + changes = list(reversed(changes)) + for old in changes: + defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) + + # symmetric hashing prevents some of the configs below + if cfg_old.get("input-xfrm"): + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "input-xfrm": {}}) + + for fl_type in flow_types: + cur = _ethtool_get_cfg(cfg, fl_type) + if cur == "sdfn": + change_nl = {"ip-src", "ip-dst"} + change_ic = "sd" + else: + change_nl = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} + change_ic = "sdfn" + + cfg.ethnl.rss_set({ + "header": {"dev-index": cfg.ifindex}, + "flow-hash": {fl_type: change_nl} + }) + reset = defer(ethtool, f"--disable-netlink -N {cfg.ifname} " + f"rx-flow-hash {fl_type} {cur}") + + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(change_nl, cfg_nl["flow-hash"][fl_type], + comment=f"Config for {fl_type} over Netlink") + cfg_ic = _ethtool_get_cfg(cfg, fl_type) + ksft_eq(change_ic, cfg_ic, + comment=f"Config for {fl_type} over IOCTL") + + reset.exec() + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + ksft_eq(cfg_old["flow-hash"][fl_type], cfg_nl["flow-hash"][fl_type], + comment=f"Un-config for {fl_type} over Netlink") + cfg_ic = _ethtool_get_cfg(cfg, fl_type) + ksft_eq(cur, cfg_ic, comment=f"Un-config for {fl_type} over IOCTL") + + # Try to set multiple at once, the defer was already installed at the start + change = {"ip-src"} + if change == cfg_old["flow-hash"]["tcp4"]: + change = {"ip-dst"} + cfg.ethnl.rss_set({ + "header": {"dev-index": cfg.ifindex}, + "flow-hash": {x: change for x in flow_types} + }) + + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + for fl_type in flow_types: + ksft_eq(change, cfg_nl["flow-hash"][fl_type], + comment=f"multi-config for {fl_type} over Netlink") + + +def test_rxfh_fields_set_xfrm(cfg): + """ Test changing Rx Flow Hash vs xfrm_input at once. """ + + def set_rss(cfg, xfrm, fh): + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "input-xfrm": xfrm, "flow-hash": fh}) + + # Install the reset handler + cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + # symmetric hashing is config-order-sensitive make sure we leave + # symmetric mode, or make the flow-hash sym-compatible first + changes = [{"flow-hash": cfg_old["flow-hash"],}, + {"input-xfrm": cfg_old.get("input-xfrm", {}),}] + if cfg_old.get("input-xfrm"): + changes = list(reversed(changes)) + for old in changes: + defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) + + # Make sure we start with input-xfrm off, and tcp4 config non-sym + set_rss(cfg, {}, {}) + set_rss(cfg, {}, {"tcp4": {"ip-src"}}) + + # Setting sym and fixing tcp4 config not expected to pass right now + with ksft_raises(NlError): + set_rss(cfg, {"sym-xor"}, {"tcp4": {"ip-src", "ip-dst"}}) + # One at a time should work, hopefully + set_rss(cfg, 0, {"tcp4": {"ip-src", "ip-dst"}}) + no_support = False + try: + set_rss(cfg, {"sym-xor"}, {}) + except NlError: + try: + set_rss(cfg, {"sym-or-xor"}, {}) + except NlError: + no_support = True + if no_support: + raise KsftSkipEx("no input-xfrm supported") + # Disabling two at once should not work either without kernel changes + with ksft_raises(NlError): + set_rss(cfg, {}, {"tcp4": {"ip-src"}}) + + +def test_rxfh_fields_ntf(cfg): + """ Test Rx Flow Hash notifications. """ + + cur = _ethtool_get_cfg(cfg, "tcp4") + if cur == "sdfn": + change = {"ip-src", "ip-dst"} + else: + change = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} + + ethnl = EthtoolFamily() + ethnl.ntf_subscribe("monitor") + + ethnl.rss_set({ + "header": {"dev-index": cfg.ifindex}, + "flow-hash": {"tcp4": change} + }) + reset = defer(ethtool, + f"--disable-netlink -N {cfg.ifname} rx-flow-hash tcp4 {cur}") + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("No notification received after IOCTL change") + ksft_eq(ntf["name"], "rss-ntf") + ksft_eq(ntf["msg"]["flow-hash"]["tcp4"], change) + ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) + + reset.exec() + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("No notification received after Netlink change") + ksft_eq(ntf["name"], "rss-ntf") + ksft_ne(ntf["msg"]["flow-hash"]["tcp4"], change) + ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) + + +def test_rss_ctx_add(cfg): + """ Test creating an additional RSS context via Netlink """ + + _require_2qs(cfg) + + # Test basic creation + ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) + d = defer(ethtool, f"-X {cfg.ifname} context {ctx.get('context')} delete") + ksft_ne(ctx.get("context", 0), 0) + ksft_ne(set(ctx.get("indir", [0])), {0}, + comment="Driver should init the indirection table") + + # Try requesting the ID we just got allocated + with ksft_raises(NlError) as cm: + ctx = cfg.ethnl.rss_create_act({ + "header": {"dev-index": cfg.ifindex}, + "context": ctx.get("context"), + }) + ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") + d.exec() + ksft_eq(cm.exception.nl_msg.error, -errno.EBUSY) + + # Test creating with a specified RSS table, and context ID + ctx_id = ctx.get("context") + ctx = cfg.ethnl.rss_create_act({ + "header": {"dev-index": cfg.ifindex}, + "context": ctx_id, + "indir": [1], + }) + ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") + ksft_eq(ctx.get("context"), ctx_id) + ksft_eq(set(ctx.get("indir", [0])), {1}) + + +def test_rss_ctx_ntf(cfg): + """ Test notifications for creating additional RSS contexts """ + + ethnl = EthtoolFamily() + ethnl.ntf_subscribe("monitor") + + # Create / delete via Netlink + ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) + cfg.ethnl.rss_delete_act({ + "header": {"dev-index": cfg.ifindex}, + "context": ctx["context"], + }) + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("[NL] No notification after context creation") + ksft_eq(ntf["name"], "rss-create-ntf") + ksft_eq(ctx, ntf["msg"]) + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("[NL] No notification after context deletion") + ksft_eq(ntf["name"], "rss-delete-ntf") + + # Create / deleve via IOCTL + ctx_id = _ethtool_create(cfg, "--disable-netlink -X", "context new") + ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} delete") + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("[IOCTL] No notification after context creation") + ksft_eq(ntf["name"], "rss-create-ntf") + + ntf = next(ethnl.poll_ntf(duration=0.2), None) + if ntf is None: + raise KsftFailEx("[IOCTL] No notification after context deletion") + ksft_eq(ntf["name"], "rss-delete-ntf") + + +def main() -> None: + """ Ksft boiler plate main """ + + with NetDrvEnv(__file__, nsim_test=False) as cfg: + cfg.ethnl = EthtoolFamily() + ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py index f439c434ba36..72880e388478 100755 --- a/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py +++ b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py @@ -32,16 +32,16 @@ def test_rss_input_xfrm(cfg, ipver): if multiprocessing.cpu_count() < 2: raise KsftSkipEx("Need at least two CPUs to test symmetric RSS hash") - cfg.require_cmd("socat", remote=True) + cfg.require_cmd("socat", local=False, remote=True) if not hasattr(socket, "SO_INCOMING_CPU"): raise KsftSkipEx("socket.SO_INCOMING_CPU was added in Python 3.11") - input_xfrm = cfg.ethnl.rss_get( - {'header': {'dev-name': cfg.ifname}}).get('input_xfrm') + rss = cfg.ethnl.rss_get({'header': {'dev-name': cfg.ifname}}) + input_xfrm = set(filter(lambda x: 'sym' in x, rss.get('input-xfrm', {}))) # Check for symmetric xor/or-xor - if not input_xfrm or (input_xfrm != 1 and input_xfrm != 2): + if not input_xfrm: raise KsftSkipEx("Symmetric RSS hash not requested") cpus = set() diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py index 3370827409aa..c13dd5efa27a 100755 --- a/tools/testing/selftests/drivers/net/hw/tso.py +++ b/tools/testing/selftests/drivers/net/hw/tso.py @@ -34,7 +34,7 @@ def tcp_sock_get_retrans(sock): def run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso): - cfg.require_cmd("socat", remote=True) + cfg.require_cmd("socat", local=False, remote=True) port = rand_port() listen_cmd = f"socat -{ipver} -t 2 -u TCP-LISTEN:{port},reuseport /dev/null,ignoreeof" @@ -102,7 +102,7 @@ def build_tunnel(cfg, outer_ipver, tun_info): remote_addr = cfg.remote_addr_v[outer_ipver] tun_type = tun_info[0] - tun_arg = tun_info[2] + tun_arg = tun_info[1] ip(f"link add {tun_type}-ksft type {tun_type} {tun_arg} local {local_addr} remote {remote_addr} dev {cfg.ifname}") defer(ip, f"link del {tun_type}-ksft") ip(f"link set dev {tun_type}-ksft up") @@ -119,15 +119,30 @@ def build_tunnel(cfg, outer_ipver, tun_info): return remote_v4, remote_v6 +def restore_wanted_features(cfg): + features_cmd = "" + for feature in cfg.hw_features: + setting = "on" if feature in cfg.wanted_features else "off" + features_cmd += f" {feature} {setting}" + try: + ethtool(f"-K {cfg.ifname} {features_cmd}") + except Exception as e: + ksft_pr(f"WARNING: failure restoring wanted features: {e}") + + def test_builder(name, cfg, outer_ipver, feature, tun=None, inner_ipver=None): """Construct specific tests from the common template.""" def f(cfg): cfg.require_ipver(outer_ipver) + defer(restore_wanted_features, cfg) if not cfg.have_stat_super_count and \ not cfg.have_stat_wire_count: raise KsftSkipEx(f"Device does not support LSO queue stats") + if feature not in cfg.hw_features: + raise KsftSkipEx(f"Device does not support {feature}") + ipver = outer_ipver if tun: remote_v4, remote_v6 = build_tunnel(cfg, ipver, tun) @@ -136,36 +151,21 @@ def test_builder(name, cfg, outer_ipver, feature, tun=None, inner_ipver=None): remote_v4 = cfg.remote_addr_v["4"] remote_v6 = cfg.remote_addr_v["6"] - tun_partial = tun and tun[1] - # Tunnel which can silently fall back to gso-partial - has_gso_partial = tun and 'tx-gso-partial' in cfg.features - - # For TSO4 via partial we need mangleid - if ipver == "4" and feature in cfg.partial_features: - ksft_pr("Testing with mangleid enabled") - if 'tx-tcp-mangleid-segmentation' not in cfg.features: - ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation on") - defer(ethtool, f"-K {cfg.ifname} tx-tcp-mangleid-segmentation off") - # First test without the feature enabled. ethtool(f"-K {cfg.ifname} {feature} off") - if has_gso_partial: - ethtool(f"-K {cfg.ifname} tx-gso-partial off") run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=False) - # Now test with the feature enabled. - # For compatible tunnels only - just GSO partial, not specific feature. - if has_gso_partial: + ethtool(f"-K {cfg.ifname} tx-gso-partial off") + ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation off") + if feature in cfg.partial_features: ethtool(f"-K {cfg.ifname} tx-gso-partial on") - run_one_stream(cfg, ipver, remote_v4, remote_v6, - should_lso=tun_partial) + if ipver == "4": + ksft_pr("Testing with mangleid enabled") + ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation on") # Full feature enabled. - if feature in cfg.features: - ethtool(f"-K {cfg.ifname} {feature} on") - run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=True) - else: - raise KsftXfailEx(f"Device does not support {feature}") + ethtool(f"-K {cfg.ifname} {feature} on") + run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=True) f.__name__ = name + ((outer_ipver + "_") if tun else "") + "ipv" + inner_ipver return f @@ -176,23 +176,39 @@ def query_nic_features(cfg) -> None: cfg.have_stat_super_count = False cfg.have_stat_wire_count = False - cfg.features = set() features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) - for f in features["active"]["bits"]["bit"]: - cfg.features.add(f["name"]) + + cfg.wanted_features = set() + for f in features["wanted"]["bits"]["bit"]: + cfg.wanted_features.add(f["name"]) + + cfg.hw_features = set() + hw_all_features_cmd = "" + for f in features["hw"]["bits"]["bit"]: + if f.get("value", False): + feature = f["name"] + cfg.hw_features.add(feature) + hw_all_features_cmd += f" {feature} on" + try: + ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}") + except Exception as e: + ksft_pr(f"WARNING: failure enabling all hw features: {e}") + ksft_pr("partial gso feature detection may be impacted") # Check which features are supported via GSO partial cfg.partial_features = set() - if 'tx-gso-partial' in cfg.features: + if 'tx-gso-partial' in cfg.hw_features: ethtool(f"-K {cfg.ifname} tx-gso-partial off") no_partial = set() features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) for f in features["active"]["bits"]["bit"]: no_partial.add(f["name"]) - cfg.partial_features = cfg.features - no_partial + cfg.partial_features = cfg.hw_features - no_partial ethtool(f"-K {cfg.ifname} tx-gso-partial on") + restore_wanted_features(cfg) + stats = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True) if stats: if 'tx-hw-gso-packets' in stats[0]: @@ -211,13 +227,14 @@ def main() -> None: query_nic_features(cfg) test_info = ( - # name, v4/v6 ethtool_feature tun:(type, partial, args) - ("", "4", "tx-tcp-segmentation", None), - ("", "6", "tx-tcp6-segmentation", None), - ("vxlan", "", "tx-udp_tnl-segmentation", ("vxlan", True, "id 100 dstport 4789 noudpcsum")), - ("vxlan_csum", "", "tx-udp_tnl-csum-segmentation", ("vxlan", False, "id 100 dstport 4789 udpcsum")), - ("gre", "4", "tx-gre-segmentation", ("gre", False, "")), - ("gre", "6", "tx-gre-segmentation", ("ip6gre", False, "")), + # name, v4/v6 ethtool_feature tun:(type, args, inner ip versions) + ("", "4", "tx-tcp-segmentation", None), + ("", "6", "tx-tcp6-segmentation", None), + ("vxlan", "4", "tx-udp_tnl-segmentation", ("vxlan", "id 100 dstport 4789 noudpcsum", ("4", "6"))), + ("vxlan", "6", "tx-udp_tnl-segmentation", ("vxlan", "id 100 dstport 4789 udp6zerocsumtx udp6zerocsumrx", ("4", "6"))), + ("vxlan_csum", "", "tx-udp_tnl-csum-segmentation", ("vxlan", "id 100 dstport 4789 udpcsum", ("4", "6"))), + ("gre", "4", "tx-gre-segmentation", ("gre", "", ("4", "6"))), + ("gre", "6", "tx-gre-segmentation", ("ip6gre","", ("4", "6"))), ) cases = [] @@ -227,11 +244,13 @@ def main() -> None: if info[1] and outer_ipver != info[1]: continue - cases.append(test_builder(info[0], cfg, outer_ipver, info[2], - tun=info[3], inner_ipver="4")) if info[3]: - cases.append(test_builder(info[0], cfg, outer_ipver, info[2], - tun=info[3], inner_ipver="6")) + cases += [ + test_builder(info[0], cfg, outer_ipver, info[2], info[3], inner_ipver) + for inner_ipver in info[3][2] + ] + else: + cases.append(test_builder(info[0], cfg, outer_ipver, info[2], None, outer_ipver)) ksft_run(cases=cases, args=(cfg, )) ksft_exit() diff --git a/tools/testing/selftests/drivers/net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/lib/py/__init__.py index 401e70f7f136..8711c67ad658 100644 --- a/tools/testing/selftests/drivers/net/lib/py/__init__.py +++ b/tools/testing/selftests/drivers/net/lib/py/__init__.py @@ -7,7 +7,21 @@ KSFT_DIR = (Path(__file__).parent / "../../../..").resolve() try: sys.path.append(KSFT_DIR.as_posix()) + from net.lib.py import * + + # Import one by one to avoid pylint false positives + from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ + NlError, RtnlFamily, DevlinkFamily + from net.lib.py import CmdExitFailure + from net.lib.py import bkg, cmd, bpftool, bpftrace, defer, ethtool, \ + fd_read_timeout, ip, rand_port, tool, wait_port_listen + from net.lib.py import fd_read_timeout + from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx + from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ + ksft_setup + from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ + ksft_ne, ksft_not_in, ksft_raises, ksft_true except ModuleNotFoundError as e: ksft_pr("Failed importing `net` library from kernel sources") ksft_pr(str(e)) diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py index 3bccddf8cbc5..1b8bd648048f 100644 --- a/tools/testing/selftests/drivers/net/lib/py/env.py +++ b/tools/testing/selftests/drivers/net/lib/py/env.py @@ -259,7 +259,7 @@ class NetDrvEpEnv(NetDrvEnvBase): if not self._require_cmd(comm, "local"): raise KsftSkipEx("Test requires command: " + comm) if remote: - if not self._require_cmd(comm, "remote"): + if not self._require_cmd(comm, "remote", host=self.remote): raise KsftSkipEx("Test requires (remote) command: " + comm) def wait_hw_stats_settle(self): diff --git a/tools/testing/selftests/drivers/net/lib/py/load.py b/tools/testing/selftests/drivers/net/lib/py/load.py index d9c10613ae67..c4e808407cc4 100644 --- a/tools/testing/selftests/drivers/net/lib/py/load.py +++ b/tools/testing/selftests/drivers/net/lib/py/load.py @@ -1,21 +1,21 @@ # SPDX-License-Identifier: GPL-2.0 +import re import time from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen class GenerateTraffic: def __init__(self, env, port=None): - env.require_cmd("iperf3", remote=True) + env.require_cmd("iperf3", local=True, remote=True) self.env = env - if port is None: - port = rand_port() - self._iperf_server = cmd(f"iperf3 -s -1 -p {port}", background=True) - wait_port_listen(port) + self.port = rand_port() if port is None else port + self._iperf_server = cmd(f"iperf3 -s -1 -p {self.port}", background=True) + wait_port_listen(self.port) time.sleep(0.1) - self._iperf_client = cmd(f"iperf3 -c {env.addr} -P 16 -p {port} -t 86400", + self._iperf_client = cmd(f"iperf3 -c {env.addr} -P 16 -p {self.port} -t 86400", background=True, host=env.remote) # Wait for traffic to ramp up @@ -56,3 +56,16 @@ class GenerateTraffic: ksft_pr(">> Server:") ksft_pr(self._iperf_server.stdout) ksft_pr(self._iperf_server.stderr) + self._wait_client_stopped() + + def _wait_client_stopped(self, sleep=0.005, timeout=5): + end = time.monotonic() + timeout + + live_port_pattern = re.compile(fr":{self.port:04X} 0[^6] ") + + while time.monotonic() < end: + data = cmd("cat /proc/net/tcp*", host=self.env.remote).stdout + if not live_port_pattern.search(data): + return + time.sleep(sleep) + raise Exception(f"Waiting for client to stop timed out after {timeout}s") diff --git a/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh b/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh index 29b01b8e2215..b6071e80ebbb 100644 --- a/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh +++ b/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh @@ -11,9 +11,11 @@ set -euo pipefail LIBDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") SRCIF="" # to be populated later -SRCIP=192.0.2.1 +SRCIP4="192.0.2.1" +SRCIP6="fc00::1" DSTIF="" # to be populated later -DSTIP=192.0.2.2 +DSTIP4="192.0.2.2" +DSTIP6="fc00::2" PORT="6666" MSG="netconsole selftest" @@ -80,7 +82,23 @@ function configure_ip() { ip link set "${SRCIF}" up } +function select_ipv4_or_ipv6() +{ + local VERSION=${1} + + if [[ "$VERSION" == "ipv6" ]] + then + DSTIP="${DSTIP6}" + SRCIP="${SRCIP6}" + else + DSTIP="${DSTIP4}" + SRCIP="${SRCIP4}" + fi +} + function set_network() { + local IP_VERSION=${1:-"ipv4"} + # setup_ns function is coming from lib.sh setup_ns NAMESPACE @@ -91,10 +109,13 @@ function set_network() { # Link both interfaces back to back link_ifaces + select_ipv4_or_ipv6 "${IP_VERSION}" configure_ip } function create_dynamic_target() { + local FORMAT=${1:-"extended"} + DSTMAC=$(ip netns exec "${NAMESPACE}" \ ip link show "${DSTIF}" | awk '/ether/ {print $2}') @@ -106,7 +127,33 @@ function create_dynamic_target() { echo "${DSTMAC}" > "${NETCONS_PATH}"/remote_mac echo "${SRCIF}" > "${NETCONS_PATH}"/dev_name + if [ "${FORMAT}" == "basic" ] + then + # Basic target does not support release + echo 0 > "${NETCONS_PATH}"/release + echo 0 > "${NETCONS_PATH}"/extended + elif [ "${FORMAT}" == "extended" ] + then + echo 1 > "${NETCONS_PATH}"/extended + fi + echo 1 > "${NETCONS_PATH}"/enabled + + # This will make sure that the kernel was able to + # load the netconsole driver configuration. The console message + # gets more organized/sequential as well. + sleep 1 +} + +# Generate the command line argument for netconsole following: +# netconsole=[+][src-port]@[src-ip]/[],[tgt-port]@/[tgt-macaddr] +function create_cmdline_str() { + DSTMAC=$(ip netns exec "${NAMESPACE}" \ + ip link show "${DSTIF}" | awk '/ether/ {print $2}') + SRCPORT="1514" + TGTPORT="6666" + + echo "netconsole=\"+${SRCPORT}@${SRCIP}/${SRCIF},${TGTPORT}@${DSTIP}/${DSTMAC}\"" } # Do not append the release to the header of the message @@ -116,16 +163,9 @@ function disable_release_append() { echo 1 > "${NETCONS_PATH}"/enabled } -function cleanup() { +function do_cleanup() { local NSIM_DEV_SYS_DEL="/sys/bus/netdevsim/del_device" - # delete netconsole dynamic reconfiguration - echo 0 > "${NETCONS_PATH}"/enabled - # Remove all the keys that got created during the selftest - find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete - # Remove the configfs entry - rmdir "${NETCONS_PATH}" - # Delete netdevsim devices echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_DEL" echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_DEL" @@ -137,6 +177,17 @@ function cleanup() { echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk } +function cleanup() { + # delete netconsole dynamic reconfiguration + echo 0 > "${NETCONS_PATH}"/enabled + # Remove all the keys that got created during the selftest + find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete + # Remove the configfs entry + rmdir "${NETCONS_PATH}" + + do_cleanup +} + function set_user_data() { if [[ ! -d "${NETCONS_PATH}""/userdata" ]] then @@ -152,18 +203,24 @@ function set_user_data() { function listen_port_and_save_to() { local OUTPUT=${1} + local IPVERSION=${2:-"ipv4"} + + if [ "${IPVERSION}" == "ipv4" ] + then + SOCAT_MODE="UDP-LISTEN" + else + SOCAT_MODE="UDP6-LISTEN" + fi + # Just wait for 2 seconds timeout 2 ip netns exec "${NAMESPACE}" \ - socat UDP-LISTEN:"${PORT}",fork "${OUTPUT}" + socat "${SOCAT_MODE}":"${PORT}",fork "${OUTPUT}" } -function validate_result() { +# Only validate that the message arrived properly +function validate_msg() { local TMPFILENAME="$1" - # TMPFILENAME will contain something like: - # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM - # key=value - # Check if the file exists if [ ! -f "$TMPFILENAME" ]; then echo "FAIL: File was not generated." >&2 @@ -175,17 +232,32 @@ function validate_result() { cat "${TMPFILENAME}" >&2 exit "${ksft_fail}" fi +} - if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then - echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2 - cat "${TMPFILENAME}" >&2 - exit "${ksft_fail}" +# Validate the message and userdata +function validate_result() { + local TMPFILENAME="$1" + + # TMPFILENAME will contain something like: + # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM + # key=value + + validate_msg "${TMPFILENAME}" + + # userdata is not supported on basic format target, + # thus, do not validate it. + if [ "${FORMAT}" != "basic" ]; + then + if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then + echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2 + cat "${TMPFILENAME}" >&2 + exit "${ksft_fail}" + fi fi # Delete the file once it is validated, otherwise keep it # for debugging purposes rm "${TMPFILENAME}" - exit "${ksft_pass}" } function check_for_dependencies() { @@ -209,6 +281,11 @@ function check_for_dependencies() { exit "${ksft_skip}" fi + if [ ! -f /proc/net/if_inet6 ]; then + echo "SKIP: IPv6 not configured. Check if CONFIG_IPV6 is enabled" >&2 + exit "${ksft_skip}" + fi + if [ ! -f "${NSIM_DEV_SYS_NEW}" ]; then echo "SKIP: file ${NSIM_DEV_SYS_NEW} does not exist. Check if CONFIG_NETDEVSIM is enabled" >&2 exit "${ksft_skip}" @@ -224,8 +301,15 @@ function check_for_dependencies() { exit "${ksft_skip}" fi - if ip addr list | grep -E "inet.*(${SRCIP}|${DSTIP})" 2> /dev/null; then - echo "SKIP: IPs already in use. Skipping it" >&2 + REGEXP4="inet.*(${SRCIP4}|${DSTIP4})" + REGEXP6="inet.*(${SRCIP6}|${DSTIP6})" + if ip addr list | grep -E "${REGEXP4}" 2> /dev/null; then + echo "SKIP: IPv4s already in use. Skipping it" >&2 + exit "${ksft_skip}" + fi + + if ip addr list | grep -E "${REGEXP6}" 2> /dev/null; then + echo "SKIP: IPv6s already in use. Skipping it" >&2 exit "${ksft_skip}" fi } @@ -239,10 +323,41 @@ function check_for_taskset() { # This is necessary if running multiple tests in a row function pkill_socat() { - PROCESS_NAME="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}" + PROCESS_NAME4="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}" + PROCESS_NAME6="socat UDP6-LISTEN:6666,fork ${OUTPUT_FILE}" # socat runs under timeout(1), kill it if it is still alive # do not fail if socat doesn't exist anymore set +e - pkill -f "${PROCESS_NAME}" + pkill -f "${PROCESS_NAME4}" + pkill -f "${PROCESS_NAME6}" set -e } + +# Check if netconsole was compiled as a module, otherwise exit +function check_netconsole_module() { + if modinfo netconsole | grep filename: | grep -q builtin + then + echo "SKIP: netconsole should be compiled as a module" >&2 + exit "${ksft_skip}" + fi +} + +# A wrapper to translate protocol version to udp version +function wait_for_port() { + local NAMESPACE=${1} + local PORT=${2} + IP_VERSION=${3} + + if [ "${IP_VERSION}" == "ipv6" ] + then + PROTOCOL="udp6" + else + PROTOCOL="udp" + fi + + wait_local_port_listen "${NAMESPACE}" "${PORT}" "${PROTOCOL}" + # even after the port is open, let's wait 1 second before writing + # otherwise the packet could be missed, and the test will fail. Happens + # more frequently on IPv6 + sleep 1 +} diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/resource_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/resource_scale.sh index 899b6892603f..d7505b933aef 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/resource_scale.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/resource_scale.sh @@ -51,7 +51,7 @@ for current_test in ${TESTS:-$ALL_TESTS}; do fi ${current_test}_setup_prepare - setup_wait $num_netifs + setup_wait_n $num_netifs # Update target in case occupancy of a certain resource changed # following the test setup. target=$(${current_test}_get_target "$should_fail") diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh index 482ebb744eba..7b98cdd0580d 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh @@ -55,7 +55,7 @@ for current_test in ${TESTS:-$ALL_TESTS}; do continue fi ${current_test}_setup_prepare - setup_wait $num_netifs + setup_wait_n $num_netifs # Update target in case occupancy of a certain resource # changed following the test setup. target=$(${current_test}_get_target "$should_fail") diff --git a/tools/testing/selftests/drivers/net/napi_id.py b/tools/testing/selftests/drivers/net/napi_id.py index 356bac46ba04..d05eddcad539 100755 --- a/tools/testing/selftests/drivers/net/napi_id.py +++ b/tools/testing/selftests/drivers/net/napi_id.py @@ -7,10 +7,10 @@ from lib.py import bkg, cmd, rand_port, NetNSEnter def test_napi_id(cfg) -> None: port = rand_port() - listen_cmd = f"{cfg.test_dir}/napi_id_helper {cfg.addr_v['4']} {port}" + listen_cmd = f"{cfg.test_dir}/napi_id_helper {cfg.addr} {port}" with bkg(listen_cmd, ksft_wait=3) as server: - cmd(f"echo a | socat - TCP:{cfg.addr_v['4']}:{port}", host=cfg.remote, shell=True) + cmd(f"echo a | socat - TCP:{cfg.baddr}:{port}", host=cfg.remote, shell=True) ksft_eq(0, server.ret) diff --git a/tools/testing/selftests/drivers/net/napi_id_helper.c b/tools/testing/selftests/drivers/net/napi_id_helper.c index eecd610c2109..7f49ca6c8637 100644 --- a/tools/testing/selftests/drivers/net/napi_id_helper.c +++ b/tools/testing/selftests/drivers/net/napi_id_helper.c @@ -7,41 +7,58 @@ #include #include #include +#include #include "../../net/lib/ksft.h" int main(int argc, char *argv[]) { - struct sockaddr_in address; + struct sockaddr_storage address; + struct addrinfo *result; + struct addrinfo hints; unsigned int napi_id; - unsigned int port; + socklen_t addr_len; socklen_t optlen; char buf[1024]; int opt = 1; + int family; int server; int client; int ret; - server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + ret = getaddrinfo(argv[1], argv[2], &hints, &result); + if (ret != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + return 1; + } + + family = result->ai_family; + addr_len = result->ai_addrlen; + + server = socket(family, SOCK_STREAM, IPPROTO_TCP); if (server < 0) { perror("socket creation failed"); + freeaddrinfo(result); if (errno == EAFNOSUPPORT) return -1; return 1; } - port = atoi(argv[2]); - if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt"); + freeaddrinfo(result); return 1; } - address.sin_family = AF_INET; - inet_pton(AF_INET, argv[1], &address.sin_addr); - address.sin_port = htons(port); + memcpy(&address, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); - if (bind(server, (struct sockaddr *)&address, sizeof(address)) < 0) { + if (bind(server, (struct sockaddr *)&address, addr_len) < 0) { perror("bind failed"); return 1; } diff --git a/tools/testing/selftests/drivers/net/napi_threaded.py b/tools/testing/selftests/drivers/net/napi_threaded.py new file mode 100755 index 000000000000..9699a100a87d --- /dev/null +++ b/tools/testing/selftests/drivers/net/napi_threaded.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +Test napi threaded states. +""" + +from lib.py import ksft_run, ksft_exit +from lib.py import ksft_eq, ksft_ne, ksft_ge +from lib.py import NetDrvEnv, NetdevFamily +from lib.py import cmd, defer, ethtool + + +def _assert_napi_threaded_enabled(nl, napi_id) -> None: + napi = nl.napi_get({'id': napi_id}) + ksft_eq(napi['threaded'], 'enabled') + ksft_ne(napi.get('pid'), None) + + +def _assert_napi_threaded_disabled(nl, napi_id) -> None: + napi = nl.napi_get({'id': napi_id}) + ksft_eq(napi['threaded'], 'disabled') + ksft_eq(napi.get('pid'), None) + + +def _set_threaded_state(cfg, threaded) -> None: + cmd(f"echo {threaded} > /sys/class/net/{cfg.ifname}/threaded") + + +def _setup_deferred_cleanup(cfg) -> None: + combined = ethtool(f"-l {cfg.ifname}", json=True)[0].get("combined", 0) + ksft_ge(combined, 2) + defer(ethtool, f"-L {cfg.ifname} combined {combined}") + + threaded = cmd(f"cat /sys/class/net/{cfg.ifname}/threaded").stdout + defer(_set_threaded_state, cfg, threaded) + + return combined + + +def enable_dev_threaded_disable_napi_threaded(cfg, nl) -> None: + """ + Test that when napi threaded is enabled at device level and + then disabled at napi level for one napi, the threaded state + of all napis is preserved after a change in number of queues. + """ + + napis = nl.napi_get({'ifindex': cfg.ifindex}, dump=True) + ksft_ge(len(napis), 2) + + napi0_id = napis[0]['id'] + napi1_id = napis[1]['id'] + + qcnt = _setup_deferred_cleanup(cfg) + + # set threaded + _set_threaded_state(cfg, 1) + + # check napi threaded is set for both napis + _assert_napi_threaded_enabled(nl, napi0_id) + _assert_napi_threaded_enabled(nl, napi1_id) + + # disable threaded for napi1 + nl.napi_set({'id': napi1_id, 'threaded': 'disabled'}) + + cmd(f"ethtool -L {cfg.ifname} combined 1") + cmd(f"ethtool -L {cfg.ifname} combined {qcnt}") + _assert_napi_threaded_enabled(nl, napi0_id) + _assert_napi_threaded_disabled(nl, napi1_id) + + +def change_num_queues(cfg, nl) -> None: + """ + Test that when napi threaded is enabled at device level, + the napi threaded state is preserved after a change in + number of queues. + """ + + napis = nl.napi_get({'ifindex': cfg.ifindex}, dump=True) + ksft_ge(len(napis), 2) + + napi0_id = napis[0]['id'] + napi1_id = napis[1]['id'] + + qcnt = _setup_deferred_cleanup(cfg) + + # set threaded + _set_threaded_state(cfg, 1) + + # check napi threaded is set for both napis + _assert_napi_threaded_enabled(nl, napi0_id) + _assert_napi_threaded_enabled(nl, napi1_id) + + cmd(f"ethtool -L {cfg.ifname} combined 1") + cmd(f"ethtool -L {cfg.ifname} combined {qcnt}") + + # check napi threaded is set for both napis + _assert_napi_threaded_enabled(nl, napi0_id) + _assert_napi_threaded_enabled(nl, napi1_id) + + +def main() -> None: + """ Ksft boiler plate main """ + + with NetDrvEnv(__file__, queue_count=2) as cfg: + ksft_run([change_num_queues, + enable_dev_threaded_disable_napi_threaded], + args=(cfg, NetdevFamily())) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/netcons_basic.sh b/tools/testing/selftests/drivers/net/netcons_basic.sh index fe765da498e8..a3446b569976 100755 --- a/tools/testing/selftests/drivers/net/netcons_basic.sh +++ b/tools/testing/selftests/drivers/net/netcons_basic.sh @@ -32,21 +32,42 @@ check_for_dependencies echo "6 5" > /proc/sys/kernel/printk # Remove the namespace, interfaces and netconsole target on exit trap cleanup EXIT -# Create one namespace and two interfaces -set_network -# Create a dynamic target for netconsole -create_dynamic_target -# Set userdata "key" with the "value" value -set_user_data -# Listed for netconsole port inside the namespace and destination interface -listen_port_and_save_to "${OUTPUT_FILE}" & -# Wait for socat to start and listen to the port. -wait_local_port_listen "${NAMESPACE}" "${PORT}" udp -# Send the message -echo "${MSG}: ${TARGET}" > /dev/kmsg -# Wait until socat saves the file to disk -busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" -# Make sure the message was received in the dst part -# and exit -validate_result "${OUTPUT_FILE}" +# Run the test twice, with different format modes +for FORMAT in "basic" "extended" +do + for IP_VERSION in "ipv6" "ipv4" + do + echo "Running with target mode: ${FORMAT} (${IP_VERSION})" + # Create one namespace and two interfaces + set_network "${IP_VERSION}" + # Create a dynamic target for netconsole + create_dynamic_target "${FORMAT}" + # Only set userdata for extended format + if [ "$FORMAT" == "extended" ] + then + # Set userdata "key" with the "value" value + set_user_data + fi + # Listed for netconsole port inside the namespace and + # destination interface + listen_port_and_save_to "${OUTPUT_FILE}" "${IP_VERSION}" & + # Wait for socat to start and listen to the port. + wait_for_port "${NAMESPACE}" "${PORT}" "${IP_VERSION}" + # Send the message + echo "${MSG}: ${TARGET}" > /dev/kmsg + # Wait until socat saves the file to disk + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + + # Make sure the message was received in the dst part + # and exit + validate_result "${OUTPUT_FILE}" "${FORMAT}" + # kill socat in case it is still running + pkill_socat + cleanup + echo "${FORMAT} : ${IP_VERSION} : Test passed" >&2 + done +done + +trap - EXIT +exit "${ksft_pass}" diff --git a/tools/testing/selftests/drivers/net/netcons_cmdline.sh b/tools/testing/selftests/drivers/net/netcons_cmdline.sh new file mode 100755 index 000000000000..ad2fb8b1c463 --- /dev/null +++ b/tools/testing/selftests/drivers/net/netcons_cmdline.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# This is a selftest to test cmdline arguments on netconsole. +# It exercises loading of netconsole from cmdline instead of the dynamic +# reconfiguration. This includes parsing the long netconsole= line and all the +# flow through init_netconsole(). +# +# Author: Breno Leitao + +set -euo pipefail + +SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") + +source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh + +check_netconsole_module + +modprobe netdevsim 2> /dev/null || true +rmmod netconsole 2> /dev/null || true + +# The content of kmsg will be save to the following file +OUTPUT_FILE="/tmp/${TARGET}" + +# Check for basic system dependency and exit if not found +# check_for_dependencies +# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) +echo "6 5" > /proc/sys/kernel/printk +# Remove the namespace and network interfaces +trap do_cleanup EXIT +# Create one namespace and two interfaces +set_network +# Create the command line for netconsole, with the configuration from the +# function above +CMDLINE="$(create_cmdline_str)" + +# Load the module, with the cmdline set +modprobe netconsole "${CMDLINE}" + +# Listed for netconsole port inside the namespace and destination interface +listen_port_and_save_to "${OUTPUT_FILE}" & +# Wait for socat to start and listen to the port. +wait_local_port_listen "${NAMESPACE}" "${PORT}" udp +# Send the message +echo "${MSG}: ${TARGET}" > /dev/kmsg +# Wait until socat saves the file to disk +busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" +# Make sure the message was received in the dst part +# and exit +validate_msg "${OUTPUT_FILE}" + +exit "${ksft_pass}" diff --git a/tools/testing/selftests/drivers/net/netcons_sysdata.sh b/tools/testing/selftests/drivers/net/netcons_sysdata.sh index a737e377bf08..baf69031089e 100755 --- a/tools/testing/selftests/drivers/net/netcons_sysdata.sh +++ b/tools/testing/selftests/drivers/net/netcons_sysdata.sh @@ -53,6 +53,17 @@ function set_release() { echo 1 > "${NETCONS_PATH}/userdata/release_enabled" } +# Enable the msgid to be appended to sysdata +function set_msgid() { + if [[ ! -f "${NETCONS_PATH}/userdata/msgid_enabled" ]] + then + echo "Not able to enable msgid sysdata append. Configfs not available in ${NETCONS_PATH}/userdata/msgid_enabled" >&2 + exit "${ksft_skip}" + fi + + echo 1 > "${NETCONS_PATH}/userdata/msgid_enabled" +} + # Disable the sysdata cpu_nr feature function unset_cpu_nr() { echo 0 > "${NETCONS_PATH}/userdata/cpu_nr_enabled" @@ -67,6 +78,10 @@ function unset_release() { echo 0 > "${NETCONS_PATH}/userdata/release_enabled" } +function unset_msgid() { + echo 0 > "${NETCONS_PATH}/userdata/msgid_enabled" +} + # Test if MSG contains sysdata function validate_sysdata() { # OUTPUT_FILE will contain something like: @@ -74,6 +89,7 @@ function validate_sysdata() { # userdatakey=userdatavalue # cpu=X # taskname= + # msgid= # Echo is what this test uses to create the message. See runtest() # function @@ -104,6 +120,12 @@ function validate_sysdata() { exit "${ksft_fail}" fi + if ! grep -q "msgid=[0-9]\+$" "${OUTPUT_FILE}"; then + echo "FAIL: 'msgid=' not found in ${OUTPUT_FILE}" >&2 + cat "${OUTPUT_FILE}" >&2 + exit "${ksft_fail}" + fi + rm "${OUTPUT_FILE}" pkill_socat } @@ -155,6 +177,12 @@ function validate_no_sysdata() { exit "${ksft_fail}" fi + if grep -q "msgid=" "${OUTPUT_FILE}"; then + echo "FAIL: 'msgid= found in ${OUTPUT_FILE}" >&2 + cat "${OUTPUT_FILE}" >&2 + exit "${ksft_fail}" + fi + rm "${OUTPUT_FILE}" } @@ -206,6 +234,7 @@ set_cpu_nr # Enable taskname to be appended to sysdata set_taskname set_release +set_msgid runtest # Make sure the message was received in the dst part # and exit @@ -235,6 +264,7 @@ MSG="Test #3 from CPU${CPU}" unset_cpu_nr unset_taskname unset_release +unset_msgid runtest # At this time, cpu= shouldn't be present in the msg validate_no_sysdata diff --git a/tools/testing/selftests/drivers/net/netdevsim/devlink.sh b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh index b5ea2526f23c..030762b203d7 100755 --- a/tools/testing/selftests/drivers/net/netdevsim/devlink.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh @@ -40,6 +40,8 @@ fw_flash_test() return fi + echo "10"> $DEBUGFS_DIR/fw_update_flash_chunk_time_ms + devlink dev flash $DL_HANDLE file $DUMMYFILE check_err $? "Failed to flash with status updates on" @@ -608,6 +610,46 @@ rate_attr_parent_check() check_err $? "Unexpected parent attr value $api_value != $parent" } +rate_attr_tc_bw_check() +{ + local handle=$1 + local tc_bw=$2 + local debug_file=$3 + + local tc_bw_str="" + for bw in $tc_bw; do + local tc=${bw%%:*} + local value=${bw##*:} + tc_bw_str="$tc_bw_str $tc:$value" + done + tc_bw_str=${tc_bw_str# } + + rate_attr_set "$handle" tc-bw "$tc_bw_str" + check_err $? "Failed to set tc-bw values" + + for bw in $tc_bw; do + local tc=${bw%%:*} + local value=${bw##*:} + local debug_value + debug_value=$(cat "$debug_file"/tc"${tc}"_bw) + check_err $? "Failed to read tc-bw value from debugfs for tc$tc" + [ "$debug_value" == "$value" ] + check_err $? "Unexpected tc-bw debug value for tc$tc: $debug_value != $value" + done + + for bw in $tc_bw; do + local tc=${bw%%:*} + local expected_value=${bw##*:} + local api_value + api_value=$(rate_attr_get "$handle" tc_"$tc") + if [ "$api_value" = "null" ]; then + api_value=0 + fi + [ "$api_value" == "$expected_value" ] + check_err $? "Unexpected tc-bw value for tc$tc: $api_value != $expected_value" + done +} + rate_node_add() { local handle=$1 @@ -649,6 +691,13 @@ rate_test() rate=$(($rate+100)) done + local tc_bw="0:0 1:40 2:0 3:0 4:0 5:0 6:60 7:0" + for r_obj in $leafs + do + rate_attr_tc_bw_check "$r_obj" "$tc_bw" \ + "$DEBUGFS_DIR"/ports/"${r_obj##*/}" + done + local node1_name='group1' local node1="$DL_HANDLE/$node1_name" rate_node_add "$node1" @@ -666,6 +715,12 @@ rate_test() rate_attr_tx_rate_check $node1 tx_max $node_tx_max \ $DEBUGFS_DIR/rate_nodes/${node1##*/}/tx_max + + local tc_bw="0:20 1:0 2:0 3:0 4:0 5:20 6:60 7:0" + rate_attr_tc_bw_check $node1 "$tc_bw" \ + "$DEBUGFS_DIR"/rate_nodes/"${node1##*/}" + + rate_node_del "$node1" check_err $? "Failed to delete node $node1" local num_nodes=`rate_nodes_get $DL_HANDLE | wc -w` diff --git a/tools/testing/selftests/drivers/net/netdevsim/nexthop.sh b/tools/testing/selftests/drivers/net/netdevsim/nexthop.sh index e8e0dc088d6a..01d0c044a5fc 100755 --- a/tools/testing/selftests/drivers/net/netdevsim/nexthop.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/nexthop.sh @@ -1053,6 +1053,6 @@ trap cleanup EXIT setup_prepare -tests_run +xfail_on_slow tests_run exit $EXIT_STATUS diff --git a/tools/testing/selftests/drivers/net/netdevsim/peer.sh b/tools/testing/selftests/drivers/net/netdevsim/peer.sh index 1bb46ec435d4..7f32b5600925 100755 --- a/tools/testing/selftests/drivers/net/netdevsim/peer.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/peer.sh @@ -1,7 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0-only -source ../../../net/lib.sh +lib_dir=$(dirname $0)/../../../net +source $lib_dir/lib.sh NSIM_DEV_1_ID=$((256 + RANDOM % 256)) NSIM_DEV_1_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_DEV_1_ID diff --git a/tools/testing/selftests/drivers/net/netdevsim/udp_tunnel_nic.sh b/tools/testing/selftests/drivers/net/netdevsim/udp_tunnel_nic.sh index 92c2f0376c08..4c859ecdad94 100755 --- a/tools/testing/selftests/drivers/net/netdevsim/udp_tunnel_nic.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/udp_tunnel_nic.sh @@ -266,7 +266,6 @@ for port in 0 1; do echo $NSIM_ID > /sys/bus/netdevsim/new_device else echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep echo 1 > $NSIM_DEV_SYS/new_port fi NSIM_NETDEV=`get_netdev_name old_netdevs` @@ -350,23 +349,11 @@ old_netdevs=$(ls /sys/class/net) port=0 echo $NSIM_ID > /sys/bus/netdevsim/new_device echo 0 > $NSIM_DEV_SYS/del_port -echo 1000 > $NSIM_DEV_DFS/udp_ports_sleep echo 0 > $NSIM_DEV_SYS/new_port NSIM_NETDEV=`get_netdev_name old_netdevs` msg="create VxLANs" -exp0=( 0 0 0 0 ) # sleep is longer than out wait -new_vxlan vxlan0 10000 $NSIM_NETDEV - -modprobe -r vxlan -modprobe -r udp_tunnel - -msg="remove tunnels" -exp0=( 0 0 0 0 ) -check_tables - -msg="create VxLANs" -exp0=( 0 0 0 0 ) # sleep is longer than out wait +exp0=( `mke 10000 1` 0 0 0 ) new_vxlan vxlan0 10000 $NSIM_NETDEV exp0=( 0 0 0 0 ) @@ -428,7 +415,6 @@ echo 0 > $NSIM_DEV_SYS/del_port for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -486,7 +472,6 @@ echo 1 > $NSIM_DEV_DFS/udp_ports_sync_all for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -543,7 +528,6 @@ echo 0 > $NSIM_DEV_SYS/del_port for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -573,7 +557,6 @@ echo 1 > $NSIM_DEV_DFS/udp_ports_ipv4_only for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -634,7 +617,6 @@ echo 0 > $NSIM_DEV_SYS/del_port for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -690,7 +672,6 @@ echo 0 > $NSIM_DEV_SYS/del_port for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -750,7 +731,6 @@ echo 0 > $NSIM_DEV_SYS/del_port for port in 0 1; do if [ $port -ne 0 ]; then echo 1 > $NSIM_DEV_DFS/udp_ports_open_only - echo 1 > $NSIM_DEV_DFS/udp_ports_sleep fi echo $port > $NSIM_DEV_SYS/new_port @@ -809,7 +789,6 @@ echo $NSIM_ID > /sys/bus/netdevsim/new_device echo 0 > $NSIM_DEV_SYS/del_port echo 0 > $NSIM_DEV_DFS/udp_ports_open_only -echo 1 > $NSIM_DEV_DFS/udp_ports_sleep echo 1 > $NSIM_DEV_DFS/udp_ports_shared old_netdevs=$(ls /sys/class/net) diff --git a/tools/testing/selftests/drivers/net/netpoll_basic.py b/tools/testing/selftests/drivers/net/netpoll_basic.py new file mode 100755 index 000000000000..408bd54d6779 --- /dev/null +++ b/tools/testing/selftests/drivers/net/netpoll_basic.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Author: Breno Leitao +""" + This test aims to evaluate the netpoll polling mechanism (as in + netpoll_poll_dev()). It presents a complex scenario where the network + attempts to send a packet but fails, prompting it to poll the NIC from within + the netpoll TX side. + + This has been a crucial path in netpoll that was previously untested. Jakub + suggested using a single RX/TX queue, pushing traffic to the NIC, and then + sending netpoll messages (via netconsole) to trigger the poll. + + In parallel, bpftrace is used to detect if netpoll_poll_dev() was called. If + so, the test passes, otherwise it will be skipped. This test is very dependent on + the driver and environment, given we are trying to trigger a tricky scenario. +""" + +import errno +import logging +import os +import random +import string +import threading +import time +from typing import Optional + +from lib.py import ( + bpftrace, + CmdExitFailure, + defer, + ethtool, + GenerateTraffic, + ksft_exit, + ksft_pr, + ksft_run, + KsftFailEx, + KsftSkipEx, + NetDrvEpEnv, + KsftXfailEx, +) + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", +) + +NETCONSOLE_CONFIGFS_PATH: str = "/sys/kernel/config/netconsole" +NETCONS_REMOTE_PORT: int = 6666 +NETCONS_LOCAL_PORT: int = 1514 + +# Max number of netcons messages to send. Each iteration will setup +# netconsole and send MAX_WRITES messages +ITERATIONS: int = 20 +# Number of writes to /dev/kmsg per iteration +MAX_WRITES: int = 40 +# MAPS contains the information coming from bpftrace it will have only one +# key: "hits", which tells the number of times netpoll_poll_dev() was called +MAPS: dict[str, int] = {} +# Thread to run bpftrace in parallel +BPF_THREAD: Optional[threading.Thread] = None +# Time bpftrace will be running in parallel. +BPFTRACE_TIMEOUT: int = 10 + + +def ethtool_get_ringsize(interface_name: str) -> tuple[int, int]: + """ + Read the ringsize using ethtool. This will be used to restore it after the test + """ + try: + ethtool_result = ethtool(f"-g {interface_name}", json=True)[0] + rxs = ethtool_result["rx"] + txs = ethtool_result["tx"] + except (KeyError, IndexError) as exception: + raise KsftSkipEx( + f"Failed to read RX/TX ringsize: {exception}. Not going to mess with them." + ) from exception + + return rxs, txs + + +def ethtool_set_ringsize(interface_name: str, ring_size: tuple[int, int]) -> bool: + """Try to the number of RX and TX ringsize.""" + rxs = ring_size[0] + txs = ring_size[1] + + logging.debug("Setting ring size to %d/%d", rxs, txs) + try: + ethtool(f"-G {interface_name} rx {rxs} tx {txs}") + except CmdExitFailure: + # This might fail on real device, retry with a higher value, + # worst case, keep it as it is. + return False + + return True + + +def ethtool_get_queues_cnt(interface_name: str) -> tuple[int, int, int]: + """Read the number of RX, TX and combined queues using ethtool""" + + try: + ethtool_result = ethtool(f"-l {interface_name}", json=True)[0] + rxq = ethtool_result.get("rx", -1) + txq = ethtool_result.get("tx", -1) + combined = ethtool_result.get("combined", -1) + + except IndexError as exception: + raise KsftSkipEx( + f"Failed to read queues numbers: {exception}. Not going to mess with them." + ) from exception + + return rxq, txq, combined + + +def ethtool_set_queues_cnt(interface_name: str, queues: tuple[int, int, int]) -> None: + """Set the number of RX, TX and combined queues using ethtool""" + rxq, txq, combined = queues + + cmdline = f"-L {interface_name}" + + if rxq != -1: + cmdline += f" rx {rxq}" + if txq != -1: + cmdline += f" tx {txq}" + if combined != -1: + cmdline += f" combined {combined}" + + logging.debug("calling: ethtool %s", cmdline) + + try: + ethtool(cmdline) + except CmdExitFailure as exception: + raise KsftSkipEx( + f"Failed to configure RX/TX queues: {exception}. Ethtool not available?" + ) from exception + + +def netcons_generate_random_target_name() -> str: + """Generate a random target name starting with 'netcons'""" + random_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=8)) + return f"netcons_{random_suffix}" + + +def netcons_create_target( + config_data: dict[str, str], + target_name: str, +) -> None: + """Create a netconsole dynamic target against the interfaces""" + logging.debug("Using netconsole name: %s", target_name) + try: + os.makedirs(f"{NETCONSOLE_CONFIGFS_PATH}/{target_name}", exist_ok=True) + logging.debug( + "Created target directory: %s/%s", NETCONSOLE_CONFIGFS_PATH, target_name + ) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise KsftFailEx( + f"Failed to create netconsole target directory: {exception}" + ) from exception + + try: + for key, value in config_data.items(): + path = f"{NETCONSOLE_CONFIGFS_PATH}/{target_name}/{key}" + logging.debug("Writing %s to %s", key, path) + with open(path, "w", encoding="utf-8") as file: + # Always convert to string to write to file + file.write(str(value)) + + # Read all configuration values for debugging purposes + for debug_key in config_data.keys(): + with open( + f"{NETCONSOLE_CONFIGFS_PATH}/{target_name}/{debug_key}", + "r", + encoding="utf-8", + ) as file: + content = file.read() + logging.debug( + "%s/%s/%s : %s", + NETCONSOLE_CONFIGFS_PATH, + target_name, + debug_key, + content.strip(), + ) + + except Exception as exception: + raise KsftFailEx( + f"Failed to configure netconsole target: {exception}" + ) from exception + + +def netcons_configure_target( + cfg: NetDrvEpEnv, interface_name: str, target_name: str +) -> None: + """Configure netconsole on the interface with the given target name""" + config_data = { + "extended": "1", + "dev_name": interface_name, + "local_port": NETCONS_LOCAL_PORT, + "remote_port": NETCONS_REMOTE_PORT, + "local_ip": cfg.addr, + "remote_ip": cfg.remote_addr, + "remote_mac": "00:00:00:00:00:00", # Not important for this test + "enabled": "1", + } + + netcons_create_target(config_data, target_name) + logging.debug( + "Created netconsole target: %s on interface %s", target_name, interface_name + ) + + +def netcons_delete_target(name: str) -> None: + """Delete a netconsole dynamic target""" + target_path = f"{NETCONSOLE_CONFIGFS_PATH}/{name}" + try: + if os.path.exists(target_path): + os.rmdir(target_path) + except OSError as exception: + raise KsftFailEx( + f"Failed to delete netconsole target: {exception}" + ) from exception + + +def netcons_load_module() -> None: + """Try to load the netconsole module""" + os.system("modprobe netconsole") + + +def bpftrace_call() -> None: + """Call bpftrace to find how many times netpoll_poll_dev() is called. + Output is saved in the global variable `maps`""" + + # This is going to update the global variable, that will be seen by the + # main function + global MAPS # pylint: disable=W0603 + + # This will be passed to bpftrace as in bpftrace -e "expr" + expr = "kprobe:netpoll_poll_dev { @hits = count(); }" + + MAPS = bpftrace(expr, timeout=BPFTRACE_TIMEOUT, json=True) + logging.debug("BPFtrace output: %s", MAPS) + + +def bpftrace_start(): + """Start a thread to call `call_bpf` in a parallel thread""" + global BPF_THREAD # pylint: disable=W0603 + + BPF_THREAD = threading.Thread(target=bpftrace_call) + BPF_THREAD.start() + if not BPF_THREAD.is_alive(): + raise KsftSkipEx("BPFtrace thread is not alive. Skipping test") + + +def bpftrace_stop() -> None: + """Stop the bpftrace thread""" + if BPF_THREAD: + BPF_THREAD.join() + + +def bpftrace_any_hit(join: bool) -> bool: + """Check if netpoll_poll_dev() was called by checking the global variable `maps`""" + if not BPF_THREAD: + raise KsftFailEx("BPFtrace didn't start") + + if BPF_THREAD.is_alive(): + if join: + # Wait for bpftrace to finish + BPF_THREAD.join() + else: + # bpftrace is still running, so, we will not check the result yet + return False + + logging.debug("MAPS coming from bpftrace = %s", MAPS) + if "hits" not in MAPS.keys(): + raise KsftFailEx(f"bpftrace failed to run!?: {MAPS}") + + logging.debug("Got a total of %d hits", MAPS["hits"]) + return MAPS["hits"] > 0 + + +def do_netpoll_flush_monitored(cfg: NetDrvEpEnv, ifname: str, target_name: str) -> None: + """Print messages to the console, trying to trigger a netpoll poll""" + # Start bpftrace in parallel, so, it is watching + # netpoll_poll_dev() while we are sending netconsole messages + bpftrace_start() + defer(bpftrace_stop) + + do_netpoll_flush(cfg, ifname, target_name) + + if bpftrace_any_hit(join=True): + ksft_pr("netpoll_poll_dev() was called. Success") + return + + raise KsftXfailEx("netpoll_poll_dev() was not called during the test...") + + +def do_netpoll_flush(cfg: NetDrvEpEnv, ifname: str, target_name: str) -> None: + """Print messages to the console, trying to trigger a netpoll poll""" + netcons_configure_target(cfg, ifname, target_name) + retry = 0 + + for i in range(int(ITERATIONS)): + if not BPF_THREAD.is_alive() or bpftrace_any_hit(join=False): + # bpftrace is done, stop sending messages + break + + msg = f"netcons test #{i}" + with open("/dev/kmsg", "w", encoding="utf-8") as kmsg: + for j in range(MAX_WRITES): + try: + kmsg.write(f"{msg}-{j}\n") + except OSError as exception: + # in some cases, kmsg can be busy, so, we will retry + time.sleep(1) + retry += 1 + if retry < 5: + logging.info("Failed to write to kmsg. Retrying") + # Just retry a few times + continue + raise KsftFailEx( + f"Failed to write to kmsg: {exception}" + ) from exception + + netcons_delete_target(target_name) + netcons_configure_target(cfg, ifname, target_name) + # If we sleep here, we will have a better chance of triggering + # This number is based on a few tests I ran while developing this test + time.sleep(0.4) + + +def configure_network(ifname: str) -> None: + """Configure ring size and queue numbers""" + + # Set defined queues to 1 to force congestion + prev_queues = ethtool_get_queues_cnt(ifname) + logging.debug("RX/TX/combined queues: %s", prev_queues) + # Only set the queues to 1 if they exists in the device. I.e, they are > 0 + ethtool_set_queues_cnt(ifname, tuple(1 if x > 0 else x for x in prev_queues)) + defer(ethtool_set_queues_cnt, ifname, prev_queues) + + # Try to set the ring size to some low value. + # Do not fail if the hardware do not accepted desired values + prev_ring_size = ethtool_get_ringsize(ifname) + for size in [(1, 1), (128, 128), (256, 256)]: + if ethtool_set_ringsize(ifname, size): + # hardware accepted the desired ringsize + logging.debug("Set RX/TX ringsize to: %s from %s", size, prev_ring_size) + break + defer(ethtool_set_ringsize, ifname, prev_ring_size) + + +def test_netpoll(cfg: NetDrvEpEnv) -> None: + """ + Test netpoll by sending traffic to the interface and then sending + netconsole messages to trigger a poll + """ + + ifname = cfg.ifname + configure_network(ifname) + target_name = netcons_generate_random_target_name() + traffic = None + + try: + traffic = GenerateTraffic(cfg) + do_netpoll_flush_monitored(cfg, ifname, target_name) + finally: + if traffic: + traffic.stop() + + # Revert RX/TX queues + netcons_delete_target(target_name) + + +def test_check_dependencies() -> None: + """Check if the dependencies are met""" + if not os.path.exists(NETCONSOLE_CONFIGFS_PATH): + raise KsftSkipEx( + f"Directory {NETCONSOLE_CONFIGFS_PATH} does not exist. CONFIG_NETCONSOLE_DYNAMIC might not be set." # pylint: disable=C0301 + ) + + +def main() -> None: + """Main function to run the test""" + netcons_load_module() + test_check_dependencies() + with NetDrvEpEnv(__file__) as cfg: + ksft_run( + [test_netpoll], + args=(cfg,), + ) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/ping.py b/tools/testing/selftests/drivers/net/ping.py index e0f114612c1a..da3623c5e8a9 100755 --- a/tools/testing/selftests/drivers/net/ping.py +++ b/tools/testing/selftests/drivers/net/ping.py @@ -30,7 +30,7 @@ def _test_v6(cfg) -> None: cmd("ping -s 65000 -c 1 -W0.5 " + cfg.addr_v["6"], host=cfg.remote) def _test_tcp(cfg) -> None: - cfg.require_cmd("socat", remote=True) + cfg.require_cmd("socat", local=False, remote=True) port = rand_port() listen_cmd = f"socat -{cfg.addr_ipver} -t 2 -u TCP-LISTEN:{port},reuseport STDOUT" diff --git a/tools/testing/selftests/drivers/net/stats.py b/tools/testing/selftests/drivers/net/stats.py index efcc1e10575b..c2bb5d3f1ca1 100755 --- a/tools/testing/selftests/drivers/net/stats.py +++ b/tools/testing/selftests/drivers/net/stats.py @@ -1,12 +1,16 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 +""" +Tests related to standard netdevice statistics. +""" + import errno import subprocess import time from lib.py import ksft_run, ksft_exit, ksft_pr from lib.py import ksft_ge, ksft_eq, ksft_is, ksft_in, ksft_lt, ksft_true, ksft_raises -from lib.py import KsftSkipEx, KsftXfailEx +from lib.py import KsftSkipEx, KsftFailEx from lib.py import ksft_disruptive from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError from lib.py import NetDrvEnv @@ -18,13 +22,16 @@ rtnl = RtnlFamily() def check_pause(cfg) -> None: - global ethnl + """ + Check that drivers which support Pause config also report standard + pause stats. + """ try: ethnl.pause_get({"header": {"dev-index": cfg.ifindex}}) except NlError as e: if e.error == errno.EOPNOTSUPP: - raise KsftXfailEx("pause not supported by the device") + raise KsftSkipEx("pause not supported by the device") from e raise data = ethnl.pause_get({"header": {"dev-index": cfg.ifindex, @@ -33,13 +40,16 @@ def check_pause(cfg) -> None: def check_fec(cfg) -> None: - global ethnl + """ + Check that drivers which support FEC config also report standard + FEC stats. + """ try: ethnl.fec_get({"header": {"dev-index": cfg.ifindex}}) except NlError as e: if e.error == errno.EOPNOTSUPP: - raise KsftXfailEx("FEC not supported by the device") + raise KsftSkipEx("FEC not supported by the device") from e raise data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex, @@ -48,15 +58,17 @@ def check_fec(cfg) -> None: def pkt_byte_sum(cfg) -> None: - global netfam, rtnl + """ + Check that qstat and interface stats match in value. + """ def get_qstat(test): - global netfam stats = netfam.qstats_get({}, dump=True) if stats: for qs in stats: if qs["ifindex"]== test.ifindex: return qs + return None qstat = get_qstat(cfg) if qstat is None: @@ -77,15 +89,14 @@ def pkt_byte_sum(cfg) -> None: for _ in range(10): rtstat = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] if stat_cmp(rtstat, qstat) < 0: - raise Exception("RTNL stats are lower, fetched later") + raise KsftFailEx("RTNL stats are lower, fetched later") qstat = get_qstat(cfg) if stat_cmp(rtstat, qstat) > 0: - raise Exception("Qstats are lower, fetched later") + raise KsftFailEx("Qstats are lower, fetched later") def qstat_by_ifindex(cfg) -> None: - global netfam - global rtnl + """ Qstats Netlink API tests - querying by ifindex. """ # Construct a map ifindex -> [dump, by-index, dump] ifindexes = {} @@ -93,7 +104,7 @@ def qstat_by_ifindex(cfg) -> None: for entry in stats: ifindexes[entry['ifindex']] = [entry, None, None] - for ifindex in ifindexes.keys(): + for ifindex in ifindexes: entry = netfam.qstats_get({"ifindex": ifindex}, dump=True) ksft_eq(len(entry), 1) ifindexes[entry[0]['ifindex']][1] = entry[0] @@ -145,7 +156,7 @@ def qstat_by_ifindex(cfg) -> None: # Try to get stats for lowest unused ifindex but not 0 devs = rtnl.getlink({}, dump=True) - all_ifindexes = set([dev["ifi-index"] for dev in devs]) + all_ifindexes = set(dev["ifi-index"] for dev in devs) lowest = 2 while lowest in all_ifindexes: lowest += 1 @@ -158,18 +169,20 @@ def qstat_by_ifindex(cfg) -> None: @ksft_disruptive def check_down(cfg) -> None: + """ Test statistics (interface and qstat) are not impacted by ifdown """ + try: qstat = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] except NlError as e: if e.error == errno.EOPNOTSUPP: - raise KsftSkipEx("qstats not supported by the device") + raise KsftSkipEx("qstats not supported by the device") from e raise ip(f"link set dev {cfg.dev['ifname']} down") defer(ip, f"link set dev {cfg.dev['ifname']} up") qstat2 = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] - for k, v in qstat.items(): + for k in qstat: ksft_ge(qstat2[k], qstat[k], comment=f"{k} went backwards on device down") # exercise per-queue API to make sure that "device down" state @@ -263,6 +276,8 @@ def procfs_downup_hammer(cfg) -> None: def main() -> None: + """ Ksft boiler plate main """ + with NetDrvEnv(__file__, queue_count=100) as cfg: ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex, check_down, procfs_hammer, procfs_downup_hammer], diff --git a/tools/testing/selftests/drivers/net/xdp.py b/tools/testing/selftests/drivers/net/xdp.py new file mode 100755 index 000000000000..1dd8bf3bf6c9 --- /dev/null +++ b/tools/testing/selftests/drivers/net/xdp.py @@ -0,0 +1,658 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +This file contains tests to verify native XDP support in network drivers. +The tests utilize the BPF program `xdp_native.bpf.o` from the `selftests.net.lib` +directory, with each test focusing on a specific aspect of XDP functionality. +""" +import random +import string +from dataclasses import dataclass +from enum import Enum + +from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ne, ksft_pr +from lib.py import KsftFailEx, NetDrvEpEnv, EthtoolFamily, NlError +from lib.py import bkg, cmd, rand_port, wait_port_listen +from lib.py import ip, bpftool, defer + + +class TestConfig(Enum): + """Enum for XDP configuration options.""" + MODE = 0 # Configures the BPF program for a specific test + PORT = 1 # Port configuration to communicate with the remote host + ADJST_OFFSET = 2 # Tail/Head adjustment offset for extension/shrinking + ADJST_TAG = 3 # Adjustment tag to annotate the start and end of extension + + +class XDPAction(Enum): + """Enum for XDP actions.""" + PASS = 0 # Pass the packet up to the stack + DROP = 1 # Drop the packet + TX = 2 # Route the packet to the remote host + TAIL_ADJST = 3 # Adjust the tail of the packet + HEAD_ADJST = 4 # Adjust the head of the packet + + +class XDPStats(Enum): + """Enum for XDP statistics.""" + RX = 0 # Count of valid packets received for testing + PASS = 1 # Count of packets passed up to the stack + DROP = 2 # Count of packets dropped + TX = 3 # Count of incoming packets routed to the remote host + ABORT = 4 # Count of packets that were aborted + + +@dataclass +class BPFProgInfo: + """Data class to store information about a BPF program.""" + name: str # Name of the BPF program + file: str # BPF program object file + xdp_sec: str = "xdp" # XDP section name (e.g., "xdp" or "xdp.frags") + mtu: int = 1500 # Maximum Transmission Unit, default is 1500 + + +def _exchg_udp(cfg, port, test_string): + """ + Exchanges UDP packets between a local and remote host using the socat tool. + + Args: + cfg: Configuration object containing network settings. + port: Port number to use for the UDP communication. + test_string: String that the remote host will send. + + Returns: + The string received by the test host. + """ + cfg.require_cmd("socat", remote=True) + + rx_udp_cmd = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT" + tx_udp_cmd = f"echo -n {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}" + + with bkg(rx_udp_cmd, exit_wait=True) as nc: + wait_port_listen(port, proto="udp") + cmd(tx_udp_cmd, host=cfg.remote, shell=True) + + return nc.stdout.strip() + + +def _test_udp(cfg, port, size=256): + """ + Tests UDP packet exchange between a local and remote host. + + Args: + cfg: Configuration object containing network settings. + port: Port number to use for the UDP communication. + size: The length of the test string to be exchanged, default is 256 characters. + + Returns: + bool: True if the received string matches the sent string, False otherwise. + """ + test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(size)) + recvd_str = _exchg_udp(cfg, port, test_str) + + return recvd_str == test_str + + +def _load_xdp_prog(cfg, bpf_info): + """ + Loads an XDP program onto a network interface. + + Args: + cfg: Configuration object containing network settings. + bpf_info: BPFProgInfo object containing information about the BPF program. + + Returns: + dict: A dictionary containing the XDP program ID, name, and associated map IDs. + """ + abs_path = cfg.net_lib_dir / bpf_info.file + prog_info = {} + + cmd(f"ip link set dev {cfg.remote_ifname} mtu {bpf_info.mtu}", shell=True, host=cfg.remote) + defer(ip, f"link set dev {cfg.remote_ifname} mtu 1500", host=cfg.remote) + + cmd( + f"ip link set dev {cfg.ifname} mtu {bpf_info.mtu} xdp obj {abs_path} sec {bpf_info.xdp_sec}", + shell=True + ) + defer(ip, f"link set dev {cfg.ifname} mtu 1500 xdp off") + + xdp_info = ip(f"-d link show dev {cfg.ifname}", json=True)[0] + prog_info["id"] = xdp_info["xdp"]["prog"]["id"] + prog_info["name"] = xdp_info["xdp"]["prog"]["name"] + prog_id = prog_info["id"] + + map_ids = bpftool(f"prog show id {prog_id}", json=True)["map_ids"] + prog_info["maps"] = {} + for map_id in map_ids: + name = bpftool(f"map show id {map_id}", json=True)["name"] + prog_info["maps"][name] = map_id + + return prog_info + + +def format_hex_bytes(value): + """ + Helper function that converts an integer into a formatted hexadecimal byte string. + + Args: + value: An integer representing the number to be converted. + + Returns: + A string representing hexadecimal equivalent of value, with bytes separated by spaces. + """ + hex_str = value.to_bytes(4, byteorder='little', signed=True) + return ' '.join(f'{byte:02x}' for byte in hex_str) + + +def _set_xdp_map(map_name, key, value): + """ + Updates an XDP map with a given key-value pair using bpftool. + + Args: + map_name: The name of the XDP map to update. + key: The key to update in the map, formatted as a hexadecimal string. + value: The value to associate with the key, formatted as a hexadecimal string. + """ + key_formatted = format_hex_bytes(key) + value_formatted = format_hex_bytes(value) + bpftool( + f"map update name {map_name} key hex {key_formatted} value hex {value_formatted}" + ) + + +def _get_stats(xdp_map_id): + """ + Retrieves and formats statistics from an XDP map. + + Args: + xdp_map_id: The ID of the XDP map from which to retrieve statistics. + + Returns: + A dictionary containing formatted packet statistics for various XDP actions. + The keys are based on the XDPStats Enum values. + + Raises: + KsftFailEx: If the stats retrieval fails. + """ + stats_dump = bpftool(f"map dump id {xdp_map_id}", json=True) + if not stats_dump: + raise KsftFailEx(f"Failed to get stats for map {xdp_map_id}") + + stats_formatted = {} + for key in range(0, 5): + val = stats_dump[key]["formatted"]["value"] + if stats_dump[key]["formatted"]["key"] == XDPStats.RX.value: + stats_formatted[XDPStats.RX.value] = val + elif stats_dump[key]["formatted"]["key"] == XDPStats.PASS.value: + stats_formatted[XDPStats.PASS.value] = val + elif stats_dump[key]["formatted"]["key"] == XDPStats.DROP.value: + stats_formatted[XDPStats.DROP.value] = val + elif stats_dump[key]["formatted"]["key"] == XDPStats.TX.value: + stats_formatted[XDPStats.TX.value] = val + elif stats_dump[key]["formatted"]["key"] == XDPStats.ABORT.value: + stats_formatted[XDPStats.ABORT.value] = val + + return stats_formatted + + +def _test_pass(cfg, bpf_info, msg_sz): + """ + Tests the XDP_PASS action by exchanging UDP packets. + + Args: + cfg: Configuration object containing network settings. + bpf_info: BPFProgInfo object containing information about the BPF program. + msg_sz: Size of the test message to send. + """ + + prog_info = _load_xdp_prog(cfg, bpf_info) + port = rand_port() + + _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.PASS.value) + _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) + + ksft_eq(_test_udp(cfg, port, msg_sz), True, "UDP packet exchange failed") + stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) + + ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should not be zero") + ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.PASS.value], "RX and PASS stats mismatch") + + +def test_xdp_native_pass_sb(cfg): + """ + Tests the XDP_PASS action for single buffer case. + + Args: + cfg: Configuration object containing network settings. + """ + bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) + + _test_pass(cfg, bpf_info, 256) + + +def test_xdp_native_pass_mb(cfg): + """ + Tests the XDP_PASS action for a multi-buff size. + + Args: + cfg: Configuration object containing network settings. + """ + bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) + + _test_pass(cfg, bpf_info, 8000) + + +def _test_drop(cfg, bpf_info, msg_sz): + """ + Tests the XDP_DROP action by exchanging UDP packets. + + Args: + cfg: Configuration object containing network settings. + bpf_info: BPFProgInfo object containing information about the BPF program. + msg_sz: Size of the test message to send. + """ + + prog_info = _load_xdp_prog(cfg, bpf_info) + port = rand_port() + + _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.DROP.value) + _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) + + ksft_eq(_test_udp(cfg, port, msg_sz), False, "UDP packet exchange should fail") + stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) + + ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should be zero") + ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.DROP.value], "RX and DROP stats mismatch") + + +def test_xdp_native_drop_sb(cfg): + """ + Tests the XDP_DROP action for a signle-buff case. + + Args: + cfg: Configuration object containing network settings. + """ + bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) + + _test_drop(cfg, bpf_info, 256) + + +def test_xdp_native_drop_mb(cfg): + """ + Tests the XDP_DROP action for a multi-buff case. + + Args: + cfg: Configuration object containing network settings. + """ + bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) + + _test_drop(cfg, bpf_info, 8000) + + +def test_xdp_native_tx_mb(cfg): + """ + Tests the XDP_TX action for a multi-buff case. + + Args: + cfg: Configuration object containing network settings. + """ + cfg.require_cmd("socat", remote=True) + + bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) + prog_info = _load_xdp_prog(cfg, bpf_info) + port = rand_port() + + _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TX.value) + _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) + + test_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(8000)) + rx_udp = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT" + tx_udp = f"echo {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}" + + with bkg(rx_udp, host=cfg.remote, exit_wait=True) as rnc: + wait_port_listen(port, proto="udp", host=cfg.remote) + cmd(tx_udp, host=cfg.remote, shell=True) + + stats = _get_stats(prog_info['maps']['map_xdp_stats']) + + ksft_eq(rnc.stdout.strip(), test_string, "UDP packet exchange failed") + ksft_eq(stats[XDPStats.TX.value], 1, "TX stats mismatch") + + +def _validate_res(res, offset_lst, pkt_sz_lst): + """ + Validates the result of a test. + + Args: + res: The result of the test, which should be a dictionary with a "status" key. + + Raises: + KsftFailEx: If the test fails to pass any combination of offset and packet size. + """ + if "status" not in res: + raise KsftFailEx("Missing 'status' key in result dictionary") + + # Validate that not a single case was successful + if res["status"] == "fail": + if res["offset"] == offset_lst[0] and res["pkt_sz"] == pkt_sz_lst[0]: + raise KsftFailEx(f"{res['reason']}") + + # Get the previous offset and packet size to report the successful run + tmp_idx = offset_lst.index(res["offset"]) + prev_offset = offset_lst[tmp_idx - 1] + if tmp_idx == 0: + tmp_idx = pkt_sz_lst.index(res["pkt_sz"]) + prev_pkt_sz = pkt_sz_lst[tmp_idx - 1] + else: + prev_pkt_sz = res["pkt_sz"] + + # Use these values for error reporting + ksft_pr( + f"Failed run: pkt_sz {res['pkt_sz']}, offset {res['offset']}. " + f"Last successful run: pkt_sz {prev_pkt_sz}, offset {prev_offset}. " + f"Reason: {res['reason']}" + ) + + +def _check_for_failures(recvd_str, stats): + """ + Checks for common failures while adjusting headroom or tailroom. + + Args: + recvd_str: The string received from the remote host after sending a test string. + stats: A dictionary containing formatted packet statistics for various XDP actions. + + Returns: + str: A string describing the failure reason if a failure is detected, otherwise None. + """ + + # Any adjustment failure result in an abort hence, we track this counter + if stats[XDPStats.ABORT.value] != 0: + return "Adjustment failed" + + # Since we are using aggregate stats for a single test across all offsets and packet sizes + # we can't use RX stats only to track data exchange failure without taking a previous + # snapshot. An easier way is to simply check for non-zero length of received string. + if len(recvd_str) == 0: + return "Data exchange failed" + + # Check for RX and PASS stats mismatch. Ideally, they should be equal for a successful run + if stats[XDPStats.RX.value] != stats[XDPStats.PASS.value]: + return "RX stats mismatch" + + return None + + +def _test_xdp_native_tail_adjst(cfg, pkt_sz_lst, offset_lst): + """ + Tests the XDP tail adjustment functionality. + + This function loads the appropriate XDP program based on the provided + program name and configures the XDP map for tail adjustment. It then + validates the tail adjustment by sending and receiving UDP packets + with specified packet sizes and offsets. + + Args: + cfg: Configuration object containing network settings. + prog: Name of the XDP program to load. + pkt_sz_lst: List of packet sizes to test. + offset_lst: List of offsets to validate support for tail adjustment. + + Returns: + dict: A dictionary with test status and failure details if applicable. + """ + port = rand_port() + bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) + + prog_info = _load_xdp_prog(cfg, bpf_info) + + # Configure the XDP map for tail adjustment + _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TAIL_ADJST.value) + _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) + + for offset in offset_lst: + tag = format(random.randint(65, 90), "02x") + + _set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) + if offset > 0: + _set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) + + for pkt_sz in pkt_sz_lst: + test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) + recvd_str = _exchg_udp(cfg, port, test_str) + stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) + + failure = _check_for_failures(recvd_str, stats) + if failure is not None: + return { + "status": "fail", + "reason": failure, + "offset": offset, + "pkt_sz": pkt_sz, + } + + # Validate data content based on offset direction + expected_data = None + if offset > 0: + expected_data = test_str + (offset * chr(int(tag, 16))) + else: + expected_data = test_str[0:pkt_sz + offset] + + if recvd_str != expected_data: + return { + "status": "fail", + "reason": "Data mismatch", + "offset": offset, + "pkt_sz": pkt_sz, + } + + return {"status": "pass"} + + +def test_xdp_native_adjst_tail_grow_data(cfg): + """ + Tests the XDP tail adjustment by growing packet data. + + Args: + cfg: Configuration object containing network settings. + """ + pkt_sz_lst = [512, 1024, 2048] + offset_lst = [1, 16, 32, 64, 128, 256] + res = _test_xdp_native_tail_adjst( + cfg, + pkt_sz_lst, + offset_lst, + ) + + _validate_res(res, offset_lst, pkt_sz_lst) + + +def test_xdp_native_adjst_tail_shrnk_data(cfg): + """ + Tests the XDP tail adjustment by shrinking packet data. + + Args: + cfg: Configuration object containing network settings. + """ + pkt_sz_lst = [512, 1024, 2048] + offset_lst = [-16, -32, -64, -128, -256] + res = _test_xdp_native_tail_adjst( + cfg, + pkt_sz_lst, + offset_lst, + ) + + _validate_res(res, offset_lst, pkt_sz_lst) + + +def get_hds_thresh(cfg): + """ + Retrieves the header data split (HDS) threshold for a network interface. + + Args: + cfg: Configuration object containing network settings. + + Returns: + The HDS threshold value. If the threshold is not supported or an error occurs, + a default value of 1500 is returned. + """ + netnl = cfg.netnl + hds_thresh = 1500 + + try: + rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}}) + if 'hds-thresh' not in rings: + ksft_pr(f'hds-thresh not supported. Using default: {hds_thresh}') + return hds_thresh + hds_thresh = rings['hds-thresh'] + except NlError as e: + ksft_pr(f"Failed to get rings: {e}. Using default: {hds_thresh}") + + return hds_thresh + + +def _test_xdp_native_head_adjst(cfg, prog, pkt_sz_lst, offset_lst): + """ + Tests the XDP head adjustment action for a multi-buffer case. + + Args: + cfg: Configuration object containing network settings. + netnl: Network namespace or link object (not used in this function). + + This function sets up the packet size and offset lists, then performs + the head adjustment test by sending and receiving UDP packets. + """ + cfg.require_cmd("socat", remote=True) + + prog_info = _load_xdp_prog(cfg, BPFProgInfo(prog, "xdp_native.bpf.o", "xdp.frags", 9000)) + port = rand_port() + + _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.HEAD_ADJST.value) + _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) + + hds_thresh = get_hds_thresh(cfg) + for offset in offset_lst: + for pkt_sz in pkt_sz_lst: + # The "head" buffer must contain at least the Ethernet header + # after we eat into it. We send large-enough packets, but if HDS + # is enabled head will only contain headers. Don't try to eat + # more than 28 bytes (UDPv4 + eth hdr left: (14 + 20 + 8) - 14) + l2_cut_off = 28 if cfg.addr_ipver == 4 else 48 + if pkt_sz > hds_thresh and offset > l2_cut_off: + ksft_pr( + f"Failed run: pkt_sz ({pkt_sz}) > HDS threshold ({hds_thresh}) and " + f"offset {offset} > {l2_cut_off}" + ) + return {"status": "pass"} + + test_str = ''.join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) + tag = format(random.randint(65, 90), '02x') + + _set_xdp_map("map_xdp_setup", + TestConfig.ADJST_OFFSET.value, + offset) + _set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) + _set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) + + recvd_str = _exchg_udp(cfg, port, test_str) + + # Check for failures around adjustment and data exchange + failure = _check_for_failures(recvd_str, _get_stats(prog_info['maps']['map_xdp_stats'])) + if failure is not None: + return { + "status": "fail", + "reason": failure, + "offset": offset, + "pkt_sz": pkt_sz + } + + # Validate data content based on offset direction + expected_data = None + if offset < 0: + expected_data = chr(int(tag, 16)) * (0 - offset) + test_str + else: + expected_data = test_str[offset:] + + if recvd_str != expected_data: + return { + "status": "fail", + "reason": "Data mismatch", + "offset": offset, + "pkt_sz": pkt_sz + } + + return {"status": "pass"} + + +def test_xdp_native_adjst_head_grow_data(cfg): + """ + Tests the XDP headroom growth support. + + Args: + cfg: Configuration object containing network settings. + + This function sets up the packet size and offset lists, then calls the + _test_xdp_native_head_adjst_mb function to perform the actual test. The + test is passed if the headroom is successfully extended for given packet + sizes and offsets. + """ + pkt_sz_lst = [512, 1024, 2048] + + # Negative values result in headroom shrinking, resulting in growing of payload + offset_lst = [-16, -32, -64, -128, -256] + res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) + + _validate_res(res, offset_lst, pkt_sz_lst) + + +def test_xdp_native_adjst_head_shrnk_data(cfg): + """ + Tests the XDP headroom shrinking support. + + Args: + cfg: Configuration object containing network settings. + + This function sets up the packet size and offset lists, then calls the + _test_xdp_native_head_adjst_mb function to perform the actual test. The + test is passed if the headroom is successfully shrunk for given packet + sizes and offsets. + """ + pkt_sz_lst = [512, 1024, 2048] + + # Positive values result in headroom growing, resulting in shrinking of payload + offset_lst = [16, 32, 64, 128, 256] + res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) + + _validate_res(res, offset_lst, pkt_sz_lst) + + +def main(): + """ + Main function to execute the XDP tests. + + This function runs a series of tests to validate the XDP support for + both the single and multi-buffer. It uses the NetDrvEpEnv context + manager to manage the network driver environment and the ksft_run + function to execute the tests. + """ + with NetDrvEpEnv(__file__) as cfg: + cfg.netnl = EthtoolFamily() + ksft_run( + [ + test_xdp_native_pass_sb, + test_xdp_native_pass_mb, + test_xdp_native_drop_sb, + test_xdp_native_drop_mb, + test_xdp_native_tx_mb, + test_xdp_native_adjst_tail_grow_data, + test_xdp_native_adjst_tail_shrnk_data, + test_xdp_native_adjst_head_grow_data, + test_xdp_native_adjst_head_shrnk_data, + ], + args=(cfg,)) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 7afa58e2bb20..fcbdb1297e24 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -3,3 +3,4 @@ dnotify_test devpts_pts file_stressor anon_inode_test +kernfs_test diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index b02326193fee..73d4650af1a5 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS += $(KHDR_INCLUDES) -TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test +TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test TEST_GEN_PROGS_EXTENDED := dnotify_test include ../lib.mk diff --git a/tools/testing/selftests/filesystems/kernfs_test.c b/tools/testing/selftests/filesystems/kernfs_test.c new file mode 100644 index 000000000000..16538b3b318e --- /dev/null +++ b/tools/testing/selftests/filesystems/kernfs_test.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ + +#include +#include +#include +#include + +#include "../kselftest_harness.h" +#include "wrappers.h" + +TEST(kernfs_listxattr) +{ + int fd; + + /* Read-only file that can never have any extended attributes set. */ + fd = open("/sys/kernel/warn_count", O_RDONLY | O_CLOEXEC); + ASSERT_GE(fd, 0); + ASSERT_EQ(flistxattr(fd, NULL, 0), 0); + EXPECT_EQ(close(fd), 0); +} + +TEST(kernfs_getxattr) +{ + int fd; + char buf[1]; + + /* Read-only file that can never have any extended attributes set. */ + fd = open("/sys/kernel/warn_count", O_RDONLY | O_CLOEXEC); + ASSERT_GE(fd, 0); + ASSERT_LT(fgetxattr(fd, "user.foo", buf, sizeof(buf)), 0); + ASSERT_EQ(errno, ENODATA); + EXPECT_EQ(close(fd), 0); +} + +TEST_HARNESS_MAIN + diff --git a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c index 63ce708d93ed..e4b7c2b457ee 100644 --- a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c +++ b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c @@ -2,6 +2,13 @@ // Copyright (c) 2025 Miklos Szeredi #define _GNU_SOURCE + +// Needed for linux/fanotify.h +typedef struct { + int val[2]; +} __kernel_fsid_t; +#define __kernel_fsid_t __kernel_fsid_t + #include #include #include @@ -10,20 +17,12 @@ #include #include #include +#include #include "../../kselftest_harness.h" #include "../statmount/statmount.h" #include "../utils.h" -// Needed for linux/fanotify.h -#ifndef __kernel_fsid_t -typedef struct { - int val[2]; -} __kernel_fsid_t; -#endif - -#include - static const char root_mntpoint_templ[] = "/tmp/mount-notify_test_root.XXXXXX"; static const int mark_cmds[] = { diff --git a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test_ns.c b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test_ns.c index 090a5ca65004..9f57ca46e3af 100644 --- a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test_ns.c +++ b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test_ns.c @@ -2,6 +2,13 @@ // Copyright (c) 2025 Miklos Szeredi #define _GNU_SOURCE + +// Needed for linux/fanotify.h +typedef struct { + int val[2]; +} __kernel_fsid_t; +#define __kernel_fsid_t __kernel_fsid_t + #include #include #include @@ -10,21 +17,12 @@ #include #include #include +#include #include "../../kselftest_harness.h" -#include "../../pidfd/pidfd.h" #include "../statmount/statmount.h" #include "../utils.h" -// Needed for linux/fanotify.h -#ifndef __kernel_fsid_t -typedef struct { - int val[2]; -} __kernel_fsid_t; -#endif - -#include - static const char root_mntpoint_templ[] = "/tmp/mount-notify_test_root.XXXXXX"; static const int mark_types[] = { diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc index 73f6c6fcecab..2506f464811b 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc @@ -16,36 +16,41 @@ ocnt=`cat enabled_functions | wc -l` echo "f:myevent1 $PLACE" >> dynamic_events -# Make sure the event is attached and is the only one -grep -q $PLACE enabled_functions -cnt=`cat enabled_functions | wc -l` -if [ $cnt -ne $((ocnt + 1)) ]; then - exit_fail -fi - echo "f:myevent2 $PLACE%return" >> dynamic_events -# It should till be the only attached function -cnt=`cat enabled_functions | wc -l` -if [ $cnt -ne $((ocnt + 1)) ]; then - exit_fail -fi - # add another event echo "f:myevent3 $PLACE2" >> dynamic_events -grep -q $PLACE2 enabled_functions -cnt=`cat enabled_functions | wc -l` -if [ $cnt -ne $((ocnt + 2)) ]; then - exit_fail -fi - grep -q myevent1 dynamic_events grep -q myevent2 dynamic_events grep -q myevent3 dynamic_events test -d events/fprobes/myevent1 test -d events/fprobes/myevent2 +echo 1 > events/fprobes/myevent1/enable +# Make sure the event is attached and is the only one +grep -q $PLACE enabled_functions +cnt=`cat enabled_functions | wc -l` +if [ $cnt -ne $((ocnt + 1)) ]; then + exit_fail +fi + +echo 1 > events/fprobes/myevent2/enable +# It should till be the only attached function +cnt=`cat enabled_functions | wc -l` +if [ $cnt -ne $((ocnt + 1)) ]; then + exit_fail +fi + +echo 1 > events/fprobes/myevent3/enable +# If the function is different, the attached function should be increased +grep -q $PLACE2 enabled_functions +cnt=`cat enabled_functions | wc -l` +if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail +fi + +echo 0 > events/fprobes/myevent2/enable echo "-:myevent2" >> dynamic_events grep -q myevent1 dynamic_events @@ -57,6 +62,7 @@ if [ $cnt -ne $((ocnt + 2)) ]; then exit_fail fi +echo 0 > events/fprobes/enable echo > dynamic_events # Should have none left @@ -67,12 +73,14 @@ fi echo "f:myevent4 $PLACE" >> dynamic_events +echo 1 > events/fprobes/myevent4/enable # Should only have one enabled cnt=`cat enabled_functions | wc -l` if [ $cnt -ne $((ocnt + 1)) ]; then exit_fail fi +echo 0 > events/fprobes/enable echo > dynamic_events # Should have none left diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc index b7c8f29c09a9..65916bb55dfb 100644 --- a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc @@ -14,11 +14,35 @@ fail() { #msg exit_fail } +# As reading trace can last forever, simply look for 3 different +# events then exit out of reading the file. If there's not 3 different +# events, then the test has failed. +check_unique() { + cat trace | grep -v '^#' | awk ' + BEGIN { cnt = 0; } + { + for (i = 0; i < cnt; i++) { + if (event[i] == $5) { + break; + } + } + if (i == cnt) { + event[cnt++] = $5; + if (cnt > 2) { + exit; + } + } + } + END { + printf "%d", cnt; + }' +} + echo 'sched:*' > set_event yield -count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +count=`check_unique` if [ $count -lt 3 ]; then fail "at least fork, exec and exit events should be recorded" fi @@ -29,7 +53,7 @@ echo 1 > events/sched/enable yield -count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +count=`check_unique` if [ $count -lt 3 ]; then fail "at least fork, exec and exit events should be recorded" fi diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc index 4b994b6df5ac..ed81eaf2afd6 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc @@ -29,7 +29,7 @@ ftrace_filter_check 'schedule*' '^schedule.*$' ftrace_filter_check '*pin*lock' '.*pin.*lock$' # filter by start*mid* -ftrace_filter_check 'mutex*try*' '^mutex.*try.*' +ftrace_filter_check 'mutex*unl*' '^mutex.*unl.*' # Advanced full-glob matching feature is recently supported. # Skip the tests if we are sure the kernel does not support it. diff --git a/tools/testing/selftests/futex/functional/.gitignore b/tools/testing/selftests/futex/functional/.gitignore index 7b24ae89594a..776ad658f75e 100644 --- a/tools/testing/selftests/futex/functional/.gitignore +++ b/tools/testing/selftests/futex/functional/.gitignore @@ -11,3 +11,4 @@ futex_wait_timeout futex_wait_uninitialized_heap futex_wait_wouldblock futex_waitv +futex_numa diff --git a/tools/testing/selftests/futex/functional/futex_numa_mpol.c b/tools/testing/selftests/futex/functional/futex_numa_mpol.c index 20a9d3ecf743..a9ecfb2d3932 100644 --- a/tools/testing/selftests/futex/functional/futex_numa_mpol.c +++ b/tools/testing/selftests/futex/functional/futex_numa_mpol.c @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) struct futex32_numa *futex_numa; int mem_size, i; void *futex_ptr; - char c; + int c; while ((c = getopt(argc, argv, "chv:")) != -1) { switch (c) { @@ -210,6 +210,10 @@ int main(int argc, char *argv[]) ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask, sizeof(nodemask) * 8, 0); if (ret == 0) { + ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0); + if (ret != 0) + ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno); + ksft_print_msg("Node %d test\n", i); futex_numa->futex = 0; futex_numa->numa = FUTEX_NO_NODE; @@ -220,8 +224,8 @@ int main(int argc, char *argv[]) if (0) test_futex_mpol(futex_numa, 0); if (futex_numa->numa != i) { - ksft_test_result_fail("Returned NUMA node is %d expected %d\n", - futex_numa->numa, i); + ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n", + futex_numa->numa, i); } } } diff --git a/tools/testing/selftests/futex/functional/futex_priv_hash.c b/tools/testing/selftests/futex/functional/futex_priv_hash.c index 2dca18fefedc..aea001ac4946 100644 --- a/tools/testing/selftests/futex/functional/futex_priv_hash.c +++ b/tools/testing/selftests/futex/functional/futex_priv_hash.c @@ -26,14 +26,12 @@ static int counter; #ifndef PR_FUTEX_HASH #define PR_FUTEX_HASH 78 # define PR_FUTEX_HASH_SET_SLOTS 1 -# define FH_FLAG_IMMUTABLE (1ULL << 0) # define PR_FUTEX_HASH_GET_SLOTS 2 -# define PR_FUTEX_HASH_GET_IMMUTABLE 3 #endif -static int futex_hash_slots_set(unsigned int slots, int flags) +static int futex_hash_slots_set(unsigned int slots) { - return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, slots, flags); + return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, slots, 0); } static int futex_hash_slots_get(void) @@ -41,16 +39,11 @@ static int futex_hash_slots_get(void) return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS); } -static int futex_hash_immutable_get(void) -{ - return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_IMMUTABLE); -} - static void futex_hash_slots_set_verify(int slots) { int ret; - ret = futex_hash_slots_set(slots, 0); + ret = futex_hash_slots_set(slots); if (ret != 0) { ksft_test_result_fail("Failed to set slots to %d: %m\n", slots); ksft_finished(); @@ -64,13 +57,13 @@ static void futex_hash_slots_set_verify(int slots) ksft_test_result_pass("SET and GET slots %d passed\n", slots); } -static void futex_hash_slots_set_must_fail(int slots, int flags) +static void futex_hash_slots_set_must_fail(int slots) { int ret; - ret = futex_hash_slots_set(slots, flags); - ksft_test_result(ret < 0, "futex_hash_slots_set(%d, %d)\n", - slots, flags); + ret = futex_hash_slots_set(slots); + ksft_test_result(ret < 0, "futex_hash_slots_set(%d)\n", + slots); } static void *thread_return_fn(void *arg) @@ -111,6 +104,30 @@ static void join_max_threads(void) } } +#define SEC_IN_NSEC 1000000000 +#define MSEC_IN_NSEC 1000000 + +static void futex_dummy_op(void) +{ + pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + struct timespec timeout; + int ret; + + pthread_mutex_lock(&lock); + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_nsec += 100 * MSEC_IN_NSEC; + if (timeout.tv_nsec >= SEC_IN_NSEC) { + timeout.tv_nsec -= SEC_IN_NSEC; + timeout.tv_sec++; + } + ret = pthread_mutex_timedlock(&lock, &timeout); + if (ret == 0) + ksft_exit_fail_msg("Successfully locked an already locked mutex.\n"); + + if (ret != ETIMEDOUT) + ksft_exit_fail_msg("pthread_mutex_timedlock() did not timeout: %d.\n", ret); +} + static void usage(char *prog) { printf("Usage: %s\n", prog); @@ -128,18 +145,14 @@ int main(int argc, char *argv[]) { int futex_slots1, futex_slotsn, online_cpus; pthread_mutexattr_t mutex_attr_pi; - int use_global_hash = 0; - int ret; - char c; + int ret, retry = 20; + int c; - while ((c = getopt(argc, argv, "cghv:")) != -1) { + while ((c = getopt(argc, argv, "chv:")) != -1) { switch (c) { case 'c': log_color(1); break; - case 'g': - use_global_hash = 1; - break; case 'h': usage(basename(argv[0])); exit(0); @@ -154,7 +167,7 @@ int main(int argc, char *argv[]) } ksft_print_header(); - ksft_set_plan(22); + ksft_set_plan(21); ret = pthread_mutexattr_init(&mutex_attr_pi); ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT); @@ -167,10 +180,6 @@ int main(int argc, char *argv[]) if (ret != 0) ksft_exit_fail_msg("futex_hash_slots_get() failed: %d, %m\n", ret); - ret = futex_hash_immutable_get(); - if (ret != 0) - ksft_exit_fail_msg("futex_hash_immutable_get() failed: %d, %m\n", ret); - ksft_test_result_pass("Basic get slots and immutable status.\n"); ret = pthread_create(&threads[0], NULL, thread_return_fn, NULL); if (ret != 0) @@ -208,8 +217,24 @@ int main(int argc, char *argv[]) */ ksft_print_msg("Online CPUs: %d\n", online_cpus); if (online_cpus > 16) { +retry_getslots: futex_slotsn = futex_hash_slots_get(); if (futex_slotsn < 0 || futex_slots1 == futex_slotsn) { + retry--; + /* + * Auto scaling on thread creation can be slightly delayed + * because it waits for a RCU grace period twice. The new + * private hash is assigned upon the first futex operation + * after grace period. + * To cover all this for testing purposes the function + * below will acquire a lock and acquire it again with a + * 100ms timeout which must timeout. This ensures we + * sleep for 100ms and issue a futex operation. + */ + if (retry > 0) { + futex_dummy_op(); + goto retry_getslots; + } ksft_print_msg("Expected increase of hash buckets but got: %d -> %d\n", futex_slots1, futex_slotsn); ksft_exit_fail_msg(test_msg_auto_inc); @@ -227,7 +252,7 @@ int main(int argc, char *argv[]) futex_hash_slots_set_verify(32); futex_hash_slots_set_verify(16); - ret = futex_hash_slots_set(15, 0); + ret = futex_hash_slots_set(15); ksft_test_result(ret < 0, "Use 15 slots\n"); futex_hash_slots_set_verify(2); @@ -245,28 +270,23 @@ int main(int argc, char *argv[]) ksft_test_result(ret == 2, "No more auto-resize after manaul setting, got %d\n", ret); - futex_hash_slots_set_must_fail(1 << 29, 0); + futex_hash_slots_set_must_fail(1 << 29); + futex_hash_slots_set_verify(4); /* - * Once the private hash has been made immutable or global hash has been requested, - * then this requested can not be undone. + * Once the global hash has been requested, then this requested can not + * be undone. */ - if (use_global_hash) { - ret = futex_hash_slots_set(0, 0); - ksft_test_result(ret == 0, "Global hash request\n"); - } else { - ret = futex_hash_slots_set(4, FH_FLAG_IMMUTABLE); - ksft_test_result(ret == 0, "Immutable resize to 4\n"); - } + ret = futex_hash_slots_set(0); + ksft_test_result(ret == 0, "Global hash request\n"); if (ret != 0) goto out; - futex_hash_slots_set_must_fail(4, 0); - futex_hash_slots_set_must_fail(4, FH_FLAG_IMMUTABLE); - futex_hash_slots_set_must_fail(8, 0); - futex_hash_slots_set_must_fail(8, FH_FLAG_IMMUTABLE); - futex_hash_slots_set_must_fail(0, FH_FLAG_IMMUTABLE); - futex_hash_slots_set_must_fail(6, FH_FLAG_IMMUTABLE); + futex_hash_slots_set_must_fail(4); + futex_hash_slots_set_must_fail(8); + futex_hash_slots_set_must_fail(8); + futex_hash_slots_set_must_fail(0); + futex_hash_slots_set_must_fail(6); ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS); if (ret != 0) { @@ -277,14 +297,7 @@ int main(int argc, char *argv[]) join_max_threads(); ret = futex_hash_slots_get(); - if (use_global_hash) { - ksft_test_result(ret == 0, "Continue to use global hash\n"); - } else { - ksft_test_result(ret == 4, "Continue to use the 4 hash buckets\n"); - } - - ret = futex_hash_immutable_get(); - ksft_test_result(ret == 1, "Hash reports to be immutable\n"); + ksft_test_result(ret == 0, "Continue to use global hash\n"); out: ksft_finished(); diff --git a/tools/testing/selftests/futex/include/futex2test.h b/tools/testing/selftests/futex/include/futex2test.h index ea79662405bc..1f625b39948a 100644 --- a/tools/testing/selftests/futex/include/futex2test.h +++ b/tools/testing/selftests/futex/include/futex2test.h @@ -4,6 +4,7 @@ * * Copyright 2021 Collabora Ltd. */ +#include #include #define u64_to_ptr(x) ((void *)(uintptr_t)(x)) @@ -65,7 +66,12 @@ struct futex32_numa { static inline int futex_waitv(volatile struct futex_waitv *waiters, unsigned long nr_waiters, unsigned long flags, struct timespec *timo, clockid_t clockid) { - return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid); + struct __kernel_timespec ts = { + .tv_sec = timo->tv_sec, + .tv_nsec = timo->tv_nsec, + }; + + return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, &ts, clockid); } /* diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/testing/selftests/futex/include/futextest.h index ddbcfc9b7bac..7a5fd1d5355e 100644 --- a/tools/testing/selftests/futex/include/futextest.h +++ b/tools/testing/selftests/futex/include/futextest.h @@ -47,6 +47,17 @@ typedef volatile u_int32_t futex_t; FUTEX_PRIVATE_FLAG) #endif +/* + * SYS_futex is expected from system C library, in glibc some 32-bit + * architectures (e.g. RV32) are using 64-bit time_t, therefore it doesn't have + * SYS_futex defined but just SYS_futex_time64. Define SYS_futex as + * SYS_futex_time64 in this situation to ensure the compilation and the + * compatibility. + */ +#if !defined(SYS_futex) && defined(SYS_futex_time64) +#define SYS_futex SYS_futex_time64 +#endif + /** * futex() - SYS_futex syscall wrapper * @uaddr: address of first futex diff --git a/tools/testing/selftests/hid/config.common b/tools/testing/selftests/hid/config.common index b1f40857307d..38c51158adf8 100644 --- a/tools/testing/selftests/hid/config.common +++ b/tools/testing/selftests/hid/config.common @@ -135,6 +135,7 @@ CONFIG_NET_EMATCH=y CONFIG_NETFILTER_NETLINK_LOG=y CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XTABLES_LEGACY=y CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y CONFIG_NETFILTER_XT_MATCH_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y diff --git a/tools/testing/selftests/hid/tests/base.py b/tools/testing/selftests/hid/tests/base.py index 3a465768e507..5175cf235b2f 100644 --- a/tools/testing/selftests/hid/tests/base.py +++ b/tools/testing/selftests/hid/tests/base.py @@ -5,6 +5,7 @@ # Copyright (c) 2017 Benjamin Tissoires # Copyright (c) 2017 Red Hat, Inc. +import dataclasses import libevdev import os import pytest @@ -145,6 +146,18 @@ class UHIDTestDevice(BaseDevice): self.name = name +@dataclasses.dataclass +class HidBpf: + object_name: str + has_rdesc_fixup: bool + + +@dataclasses.dataclass +class KernelModule: + driver_name: str + module_name: str + + class BaseTestCase: class TestUhid(object): syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore @@ -155,20 +168,20 @@ class BaseTestCase: # List of kernel modules to load before starting the test # if any module is not available (not compiled), the test will skip. - # Each element is a tuple '(kernel driver name, kernel module)', - # for example ("playstation", "hid-playstation") - kernel_modules: List[Tuple[str, str]] = [] + # Each element is a KernelModule object, for example + # KernelModule("playstation", "hid-playstation") + kernel_modules: List[KernelModule] = [] # List of in kernel HID-BPF object files to load # before starting the test # Any existing pre-loaded HID-BPF module will be removed # before the ones in this list will be manually loaded. - # Each Element is a tuple '(hid_bpf_object, rdesc_fixup_present)', - # for example '("xppen-ArtistPro16Gen2.bpf.o", True)' - # If 'rdesc_fixup_present' is True, the test needs to wait + # Each Element is a HidBpf object, for example + # 'HidBpf("xppen-ArtistPro16Gen2.bpf.o", True)' + # If 'has_rdesc_fixup' is True, the test needs to wait # for one unbind and rebind before it can be sure the kernel is # ready - hid_bpfs: List[Tuple[str, bool]] = [] + hid_bpfs: List[HidBpf] = [] def assertInputEventsIn(self, expected_events, effective_events): effective_events = effective_events.copy() @@ -232,25 +245,26 @@ class BaseTestCase: @pytest.fixture() def load_kernel_module(self): - for kernel_driver, kernel_module in self.kernel_modules: - self._load_kernel_module(kernel_driver, kernel_module) + for k in self.kernel_modules: + self._load_kernel_module(k.driver_name, k.module_name) yield def load_hid_bpfs(self): + # this function will only work when run in the kernel tree script_dir = Path(os.path.dirname(os.path.realpath(__file__))) root_dir = (script_dir / "../../../../..").resolve() bpf_dir = root_dir / "drivers/hid/bpf/progs" + if not bpf_dir.exists(): + pytest.skip("looks like we are not in the kernel tree, skipping") + udev_hid_bpf = shutil.which("udev-hid-bpf") if not udev_hid_bpf: pytest.skip("udev-hid-bpf not found in $PATH, skipping") - wait = False - for _, rdesc_fixup in self.hid_bpfs: - if rdesc_fixup: - wait = True + wait = any(b.has_rdesc_fixup for b in self.hid_bpfs) - for hid_bpf, _ in self.hid_bpfs: + for hid_bpf in self.hid_bpfs: # We need to start `udev-hid-bpf` in the background # and dispatch uhid events in case the kernel needs # to fetch features on the device @@ -260,13 +274,13 @@ class BaseTestCase: "--verbose", "add", str(self.uhdev.sys_path), - str(bpf_dir / hid_bpf), + str(bpf_dir / hid_bpf.object_name), ], ) while process.poll() is None: self.uhdev.dispatch(1) - if process.poll() != 0: + if process.returncode != 0: pytest.fail( f"Couldn't insert hid-bpf program '{hid_bpf}', marking the test as failed" ) diff --git a/tools/testing/selftests/hid/tests/base_device.py b/tools/testing/selftests/hid/tests/base_device.py index e0515be97f83..59465c58d94d 100644 --- a/tools/testing/selftests/hid/tests/base_device.py +++ b/tools/testing/selftests/hid/tests/base_device.py @@ -18,10 +18,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import dataclasses import fcntl import functools import libevdev import os +import threading try: import pyudev @@ -104,6 +106,12 @@ class PowerSupply(object): return self._type.str_value +@dataclasses.dataclass +class HidReadiness: + is_ready: bool = False + count: int = 0 + + class HIDIsReady(object): """ Companion class that binds to a kernel mechanism @@ -115,18 +123,18 @@ class HIDIsReady(object): def __init__(self: "HIDIsReady", uhid: UHIDDevice) -> None: self.uhid = uhid - def is_ready(self: "HIDIsReady") -> bool: + def is_ready(self: "HIDIsReady") -> HidReadiness: """ Overwrite in subclasses: should return True or False whether the attached uhid device is ready or not. """ - return False + return HidReadiness() class UdevHIDIsReady(HIDIsReady): _pyudev_context: ClassVar[Optional[pyudev.Context]] = None _pyudev_monitor: ClassVar[Optional[pyudev.Monitor]] = None - _uhid_devices: ClassVar[Dict[int, Tuple[bool, int]]] = {} + _uhid_devices: ClassVar[Dict[int, HidReadiness]] = {} def __init__(self: "UdevHIDIsReady", uhid: UHIDDevice) -> None: super().__init__(uhid) @@ -157,18 +165,19 @@ class UdevHIDIsReady(HIDIsReady): id = int(event.sys_path.strip().split(".")[-1], 16) - device_ready, count = cls._uhid_devices.get(id, (False, 0)) + readiness = cls._uhid_devices.setdefault(id, HidReadiness()) ready = event.action == "bind" - if not device_ready and ready: - count += 1 - cls._uhid_devices[id] = (ready, count) + if not readiness.is_ready and ready: + readiness.count += 1 - def is_ready(self: "UdevHIDIsReady") -> Tuple[bool, int]: + readiness.is_ready = ready + + def is_ready(self: "UdevHIDIsReady") -> HidReadiness: try: return self._uhid_devices[self.uhid.hid_id] except KeyError: - return (False, 0) + return HidReadiness() class EvdevMatch(object): @@ -322,11 +331,11 @@ class BaseDevice(UHIDDevice): @property def kernel_is_ready(self: "BaseDevice") -> bool: - return self._kernel_is_ready.is_ready()[0] and self.started + return self._kernel_is_ready.is_ready().is_ready and self.started @property def kernel_ready_count(self: "BaseDevice") -> int: - return self._kernel_is_ready.is_ready()[1] + return self._kernel_is_ready.is_ready().count @property def input_nodes(self: "BaseDevice") -> List[EvdevDevice]: @@ -336,10 +345,28 @@ class BaseDevice(UHIDDevice): if not self.kernel_is_ready or not self.started: return [] + # Starting with kernel v6.16, an event is emitted when + # userspace opens a kernel device, and for some devices + # this translates into a SET_REPORT. + # Because EvdevDevice(path) opens every single evdev node + # we need to have a separate thread to process the incoming + # SET_REPORT or we end up having to wait for the kernel + # timeout of 5 seconds. + done = False + + def dispatch(): + while not done: + self.dispatch(1) + + t = threading.Thread(target=dispatch) + t.start() + self._input_nodes = [ EvdevDevice(path) for path in self.walk_sysfs("input", "input/input*/event*") ] + done = True + t.join() return self._input_nodes def match_evdev_rule(self, application, evdev): diff --git a/tools/testing/selftests/hid/tests/test_apple_keyboard.py b/tools/testing/selftests/hid/tests/test_apple_keyboard.py index f81071d46166..0e17588b945c 100644 --- a/tools/testing/selftests/hid/tests/test_apple_keyboard.py +++ b/tools/testing/selftests/hid/tests/test_apple_keyboard.py @@ -8,13 +8,14 @@ from .test_keyboard import ArrayKeyboard, TestArrayKeyboard from hidtools.util import BusType +from . import base import libevdev import logging logger = logging.getLogger("hidtools.test.apple-keyboard") -KERNEL_MODULE = ("apple", "hid-apple") +KERNEL_MODULE = base.KernelModule("apple", "hid-apple") class KbdData(object): diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py index 8d5b5ffdae49..612197805931 100644 --- a/tools/testing/selftests/hid/tests/test_gamepad.py +++ b/tools/testing/selftests/hid/tests/test_gamepad.py @@ -12,6 +12,7 @@ import pytest from .base_gamepad import BaseGamepad, JoystickGamepad, AxisMapping from hidtools.util import BusType +from .base import HidBpf import logging @@ -654,7 +655,7 @@ class TestAsusGamepad(BaseTest.TestGamepad): class TestRaptorMach2Joystick(BaseTest.TestGamepad): - hid_bpfs = [("FR-TEC__Raptor-Mach-2.bpf.o", True)] + hid_bpfs = [HidBpf("FR-TEC__Raptor-Mach-2.bpf.o", True)] def create_device(self): return RaptorMach2Joystick( diff --git a/tools/testing/selftests/hid/tests/test_ite_keyboard.py b/tools/testing/selftests/hid/tests/test_ite_keyboard.py index 38550c167bae..f695eaad1648 100644 --- a/tools/testing/selftests/hid/tests/test_ite_keyboard.py +++ b/tools/testing/selftests/hid/tests/test_ite_keyboard.py @@ -11,10 +11,11 @@ from hidtools.util import BusType import libevdev import logging +from . import base logger = logging.getLogger("hidtools.test.ite-keyboard") -KERNEL_MODULE = ("itetech", "hid_ite") +KERNEL_MODULE = base.KernelModule("itetech", "hid_ite") class KbdData(object): diff --git a/tools/testing/selftests/hid/tests/test_mouse.py b/tools/testing/selftests/hid/tests/test_mouse.py index 66daf7e5975c..eb4e15a0e53b 100644 --- a/tools/testing/selftests/hid/tests/test_mouse.py +++ b/tools/testing/selftests/hid/tests/test_mouse.py @@ -439,6 +439,68 @@ class BadResolutionMultiplierMouse(ResolutionMultiplierMouse): return 32 # EPIPE +class BadReportDescriptorMouse(BaseMouse): + """ + This "device" was one autogenerated by syzbot. There are a lot of issues in + it, and the most problematic is that it declares features that have no + size. + + This leads to report->size being set to 0 and can mess up with usbhid + internals. Fortunately, uhid merely passes the incoming buffer, without + touching it so a buffer of size 0 will be translated to [] without + triggering a kernel oops. + + Because the report descriptor is wrong, no input are created, and we need + to tweak a little bit the parameters to make it look correct. + """ + + # fmt: off + report_descriptor = [ + 0x96, 0x01, 0x00, # Report Count (1) 0 + 0x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3 + # 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow + 0x2a, 0x90, 0xa0, # Usage Maximum (41104) 6 + 0x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 9 + 0xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 14 + 0x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 19 + 0x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24 + ] + # fmt: on + + def __init__( + self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA) + ): + super().__init__(rdesc, name, input_info) + self.high_resolution_report_called = False + + def get_evdev(self, application=None): + assert self._input_nodes is None + return ( + "Ok" # should be a list or None, but both would fail, so abusing the system + ) + + def next_sync_events(self, application=None): + # there are no evdev nodes, so no events + return [] + + def is_ready(self): + # we wait for the SET_REPORT command to come + return self.high_resolution_report_called + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + raise InvalidHIDCommunication(f"Unexpected report type: {rtype}") + if rnum != 0x0: + raise InvalidHIDCommunication(f"Unexpected report number: {rnum}") + + if len(data) != 1: + raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'") + + self.high_resolution_report_called = True + + return 0 + + class ResolutionMultiplierHWheelMouse(TwoWheelMouse): # fmt: off report_descriptor = [ @@ -975,3 +1037,11 @@ class TestMiMouse(TestWheelMouse): # assert below print out the real error pass assert remaining == [] + + +class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid): + def create_device(self): + return BadReportDescriptorMouse() + + def assertName(self, uhdev): + pass diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py index 4265012231c6..5d2ffa3d5977 100644 --- a/tools/testing/selftests/hid/tests/test_multitouch.py +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -17,7 +17,7 @@ import time logger = logging.getLogger("hidtools.test.multitouch") -KERNEL_MODULE = ("hid-multitouch", "hid_multitouch") +KERNEL_MODULE = base.KernelModule("hid-multitouch", "hid_multitouch") def BIT(x): diff --git a/tools/testing/selftests/hid/tests/test_sony.py b/tools/testing/selftests/hid/tests/test_sony.py index 7e52c28e59c5..7fd3a8e6137d 100644 --- a/tools/testing/selftests/hid/tests/test_sony.py +++ b/tools/testing/selftests/hid/tests/test_sony.py @@ -7,6 +7,7 @@ # from .base import application_matches +from .base import KernelModule from .test_gamepad import BaseTest from hidtools.device.sony_gamepad import ( PS3Controller, @@ -24,9 +25,9 @@ import pytest logger = logging.getLogger("hidtools.test.sony") -PS3_MODULE = ("sony", "hid_sony") -PS4_MODULE = ("playstation", "hid_playstation") -PS5_MODULE = ("playstation", "hid_playstation") +PS3_MODULE = KernelModule("sony", "hid_sony") +PS4_MODULE = KernelModule("playstation", "hid_playstation") +PS5_MODULE = KernelModule("playstation", "hid_playstation") class SonyBaseTest: diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py index a9e2de1e8861..50d5699812bb 100644 --- a/tools/testing/selftests/hid/tests/test_tablet.py +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -10,6 +10,7 @@ from . import base import copy from enum import Enum from hidtools.util import BusType +from .base import HidBpf import libevdev import logging import pytest @@ -1228,9 +1229,9 @@ class Huion_Kamvas_Pro_19_256c_006b(PenDigitizer): pen.current_state = state def call_input_event(self, report): - if report[0] == 0x0a: + if report[0] == 0x0A: # ensures the original second Eraser usage is null - report[1] &= 0xdf + report[1] &= 0xDF # ensures the original last bit is equal to bit 6 (In Range) if report[1] & 0x40: @@ -1472,7 +1473,7 @@ class TestGoodix_27c6_0e00(BaseTest.TestTablet): class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet): - hid_bpfs = [("XPPen__ArtistPro16Gen2.bpf.o", True)] + hid_bpfs = [HidBpf("XPPen__ArtistPro16Gen2.bpf.o", True)] def create_device(self): dev = XPPen_ArtistPro16Gen2_28bd_095b( @@ -1484,7 +1485,7 @@ class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet): class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet): - hid_bpfs = [("XPPen__Artist24.bpf.o", True)] + hid_bpfs = [HidBpf("XPPen__Artist24.bpf.o", True)] def create_device(self): return XPPen_Artist24_28bd_093a( @@ -1495,7 +1496,7 @@ class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet): class TestHuion_Kamvas_Pro_19_256c_006b(BaseTest.TestTablet): - hid_bpfs = [("Huion__Kamvas-Pro-19.bpf.o", True)] + hid_bpfs = [HidBpf("Huion__Kamvas-Pro-19.bpf.o", True)] def create_device(self): return Huion_Kamvas_Pro_19_256c_006b( diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py index b62c7dba6777..2d6d04f0ff80 100644 --- a/tools/testing/selftests/hid/tests/test_wacom_generic.py +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -40,7 +40,7 @@ import logging logger = logging.getLogger("hidtools.test.wacom") -KERNEL_MODULE = ("wacom", "wacom") +KERNEL_MODULE = base.KernelModule("wacom", "wacom") class ProximityState(Enum): @@ -892,9 +892,9 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest locations. The value of `t` may be incremented over time to move the points along a linear path. """ - return [ self.make_contact(id, t) for id in range(0, n) ] + return [self.make_contact(id, t) for id in range(0, n)] - def assert_contact(self, uhdev, evdev, contact_ids, t=0): + def assert_contact(self, evdev, contact_ids, t=0): """ Assert properties of a contact generated by make_contact. """ @@ -916,12 +916,12 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_X] == x assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_Y] == y - def assert_contacts(self, uhdev, evdev, data, t=0): + def assert_contacts(self, evdev, data, t=0): """ Assert properties of a list of contacts generated by make_contacts. """ for contact_ids in data: - self.assert_contact(uhdev, evdev, contact_ids, t) + self.assert_contact(evdev, contact_ids, t) def test_contact_id_0(self): """ @@ -997,12 +997,16 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events - self.assert_contacts(uhdev, evdev, - [ self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), - self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), - self.ContactIds(contact_id = 2, tracking_id = -1, slot_num = None), - self.ContactIds(contact_id = 3, tracking_id = 1, slot_num = 1), - self.ContactIds(contact_id = 4, tracking_id = -1, slot_num = None) ]) + self.assert_contacts( + evdev, + [ + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + self.ContactIds(contact_id=2, tracking_id=-1, slot_num=None), + self.ContactIds(contact_id=3, tracking_id=1, slot_num=1), + self.ContactIds(contact_id=4, tracking_id=-1, slot_num=None), + ], + ) def confidence_change_assert_playback(self, uhdev, evdev, timeline): """ @@ -1026,8 +1030,8 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) - ids = [ x[0] for x in state ] - self.assert_contacts(uhdev, evdev, ids, t) + ids = [x[0] for x in state] + self.assert_contacts(evdev, ids, t) t += 1 @@ -1044,27 +1048,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident - # First finger looses confidence and clears only the tipswitch flag - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident + # First finger looses confidence and clears only the tipswitch flag + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_loss_b(self): """ @@ -1079,27 +1124,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger looses confidence and has both flags cleared simultaneously - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger looses confidence and has both flags cleared simultaneously + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_loss_c(self): """ @@ -1113,27 +1199,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # First finger looses confidence and clears only the confidence flag - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # First finger looses confidence and clears only the confidence flag + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_gain_a(self): """ @@ -1144,27 +1271,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident - # Only second finger is confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # First finger gains confidence - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger remains confident - [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger remains confident - [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident + # Only second finger is confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # First finger gains confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger remains confident + [ + ( + self.ContactIds(contact_id=0, tracking_id=1, slot_num=1), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger remains confident + [ + ( + self.ContactIds(contact_id=0, tracking_id=1, slot_num=1), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + ], + ) def test_confidence_gain_b(self): """ @@ -1175,24 +1343,65 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # First and second finger confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # Firtst finger looses confidence - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger gains confidence - [(self.ContactIds(contact_id = 0, tracking_id = 2, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident - # First finger goes up - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # First and second finger confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # Firtst finger looses confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger gains confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=2, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident + # First finger goes up + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index 1a8e85afe9aa..3eebf5e3b974 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -54,6 +54,8 @@ static __attribute__((constructor)) void setup_sizes(void) mfd_buffer = memfd_mmap(BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, &mfd); + assert(mfd_buffer != MAP_FAILED); + assert(mfd > 0); } FIXTURE(iommufd) @@ -764,19 +766,34 @@ TEST_F(iommufd_ioas, get_hw_info) uint8_t max_pasid = 0; /* Provide a zero-size user_buffer */ - test_cmd_get_hw_info(self->device_id, NULL, 0); + test_cmd_get_hw_info(self->device_id, + IOMMU_HW_INFO_TYPE_DEFAULT, NULL, 0); /* Provide a user_buffer with exact size */ - test_cmd_get_hw_info(self->device_id, &buffer_exact, sizeof(buffer_exact)); + test_cmd_get_hw_info(self->device_id, + IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact, + sizeof(buffer_exact)); + + /* Request for a wrong data_type, and a correct one */ + test_err_get_hw_info(EOPNOTSUPP, self->device_id, + IOMMU_HW_INFO_TYPE_SELFTEST + 1, + &buffer_exact, sizeof(buffer_exact)); + test_cmd_get_hw_info(self->device_id, + IOMMU_HW_INFO_TYPE_SELFTEST, &buffer_exact, + sizeof(buffer_exact)); /* * Provide a user_buffer with size larger than the exact size to check if * kernel zero the trailing bytes. */ - test_cmd_get_hw_info(self->device_id, &buffer_larger, sizeof(buffer_larger)); + test_cmd_get_hw_info(self->device_id, + IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger, + sizeof(buffer_larger)); /* * Provide a user_buffer with size smaller than the exact size to check if * the fields within the size range still gets updated. */ - test_cmd_get_hw_info(self->device_id, &buffer_smaller, sizeof(buffer_smaller)); + test_cmd_get_hw_info(self->device_id, + IOMMU_HW_INFO_TYPE_DEFAULT, + &buffer_smaller, sizeof(buffer_smaller)); test_cmd_get_hw_info_pasid(self->device_id, &max_pasid); ASSERT_EQ(0, max_pasid); if (variant->pasid_capable) { @@ -786,9 +803,11 @@ TEST_F(iommufd_ioas, get_hw_info) } } else { test_err_get_hw_info(ENOENT, self->device_id, - &buffer_exact, sizeof(buffer_exact)); + IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact, + sizeof(buffer_exact)); test_err_get_hw_info(ENOENT, self->device_id, - &buffer_larger, sizeof(buffer_larger)); + IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger, + sizeof(buffer_larger)); } } @@ -951,6 +970,33 @@ TEST_F(iommufd_ioas, area_auto_iova) test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1)); } +/* https://lore.kernel.org/r/685af644.a00a0220.2e5631.0094.GAE@google.com */ +TEST_F(iommufd_ioas, reserved_overflow) +{ + struct iommu_test_cmd test_cmd = { + .size = sizeof(test_cmd), + .op = IOMMU_TEST_OP_ADD_RESERVED, + .id = self->ioas_id, + .add_reserved.start = 6, + }; + unsigned int map_len; + __u64 iova; + + if (PAGE_SIZE == 4096) { + test_cmd.add_reserved.length = 0xffffffffffff8001; + map_len = 0x5000; + } else { + test_cmd.add_reserved.length = + 0xffffffffffffffff - MOCK_PAGE_SIZE * 16; + map_len = MOCK_PAGE_SIZE * 10; + } + + ASSERT_EQ(0, + ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED), + &test_cmd)); + test_err_ioctl_ioas_map(ENOSPC, buffer, map_len, &iova); +} + TEST_F(iommufd_ioas, area_allowed) { struct iommu_test_cmd test_cmd = { @@ -1746,13 +1792,15 @@ TEST_F(iommufd_mock_domain, all_aligns) unsigned int end; uint8_t *buf; int prot = PROT_READ | PROT_WRITE; - int mfd; + int mfd = -1; if (variant->file) buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd); else buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0); ASSERT_NE(MAP_FAILED, buf); + if (variant->file) + ASSERT_GT(mfd, 0); check_refs(buf, buf_size, 0); /* @@ -1798,13 +1846,15 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) unsigned int end; uint8_t *buf; int prot = PROT_READ | PROT_WRITE; - int mfd; + int mfd = -1; if (variant->file) buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd); else buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0); ASSERT_NE(MAP_FAILED, buf); + if (variant->file) + ASSERT_GT(mfd, 0); check_refs(buf, buf_size, 0); /* @@ -2008,6 +2058,7 @@ FIXTURE_VARIANT(iommufd_dirty_tracking) FIXTURE_SETUP(iommufd_dirty_tracking) { + size_t mmap_buffer_size; unsigned long size; int mmap_flags; void *vrc; @@ -2022,22 +2073,33 @@ FIXTURE_SETUP(iommufd_dirty_tracking) self->fd = open("/dev/iommu", O_RDWR); ASSERT_NE(-1, self->fd); - rc = posix_memalign(&self->buffer, HUGEPAGE_SIZE, variant->buffer_size); - if (rc || !self->buffer) { - SKIP(return, "Skipping buffer_size=%lu due to errno=%d", - variant->buffer_size, rc); - } - mmap_flags = MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED; + mmap_buffer_size = variant->buffer_size; if (variant->hugepages) { /* * MAP_POPULATE will cause the kernel to fail mmap if THPs are * not available. */ mmap_flags |= MAP_HUGETLB | MAP_POPULATE; + + /* + * Allocation must be aligned to the HUGEPAGE_SIZE, because the + * following mmap() will automatically align the length to be a + * multiple of the underlying huge page size. Failing to do the + * same at this allocation will result in a memory overwrite by + * the mmap(). + */ + if (mmap_buffer_size < HUGEPAGE_SIZE) + mmap_buffer_size = HUGEPAGE_SIZE; + } + + rc = posix_memalign(&self->buffer, HUGEPAGE_SIZE, mmap_buffer_size); + if (rc || !self->buffer) { + SKIP(return, "Skipping buffer_size=%lu due to errno=%d", + mmap_buffer_size, rc); } assert((uintptr_t)self->buffer % HUGEPAGE_SIZE == 0); - vrc = mmap(self->buffer, variant->buffer_size, PROT_READ | PROT_WRITE, + vrc = mmap(self->buffer, mmap_buffer_size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); assert(vrc == self->buffer); @@ -2066,8 +2128,8 @@ FIXTURE_SETUP(iommufd_dirty_tracking) FIXTURE_TEARDOWN(iommufd_dirty_tracking) { - munmap(self->buffer, variant->buffer_size); - munmap(self->bitmap, DIV_ROUND_UP(self->bitmap_size, BITS_PER_BYTE)); + free(self->buffer); + free(self->bitmap); teardown_iommufd(self->fd, _metadata); } @@ -2175,8 +2237,7 @@ TEST_F(iommufd_dirty_tracking, device_dirty_capability) test_cmd_hwpt_alloc(self->idev_id, self->ioas_id, 0, &hwpt_id); test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL); - test_cmd_get_hw_capabilities(self->idev_id, caps, - IOMMU_HW_CAP_DIRTY_TRACKING); + test_cmd_get_hw_capabilities(self->idev_id, caps); ASSERT_EQ(IOMMU_HW_CAP_DIRTY_TRACKING, caps & IOMMU_HW_CAP_DIRTY_TRACKING); @@ -2688,7 +2749,7 @@ FIXTURE_SETUP(iommufd_viommu) /* Allocate a vIOMMU taking refcount of the parent hwpt */ test_cmd_viommu_alloc(self->device_id, self->hwpt_id, - IOMMU_VIOMMU_TYPE_SELFTEST, + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, &self->viommu_id); /* Allocate a regular nested hwpt */ @@ -2727,24 +2788,27 @@ TEST_F(iommufd_viommu, viommu_negative_tests) if (self->device_id) { /* Negative test -- invalid hwpt (hwpt_id=0) */ test_err_viommu_alloc(ENOENT, device_id, 0, - IOMMU_VIOMMU_TYPE_SELFTEST, NULL); + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, + NULL); /* Negative test -- not a nesting parent hwpt */ test_cmd_hwpt_alloc(device_id, ioas_id, 0, &hwpt_id); test_err_viommu_alloc(EINVAL, device_id, hwpt_id, - IOMMU_VIOMMU_TYPE_SELFTEST, NULL); + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, + NULL); test_ioctl_destroy(hwpt_id); /* Negative test -- unsupported viommu type */ test_err_viommu_alloc(EOPNOTSUPP, device_id, self->hwpt_id, - 0xdead, NULL); + 0xdead, NULL, 0, NULL); EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->hwpt_id)); EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->viommu_id)); } else { test_err_viommu_alloc(ENOENT, self->device_id, self->hwpt_id, - IOMMU_VIOMMU_TYPE_SELFTEST, NULL); + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, + NULL); } } @@ -2760,35 +2824,66 @@ TEST_F(iommufd_viommu, viommu_alloc_nested_iopf) uint32_t fault_fd; uint32_t vdev_id; - if (self->device_id) { - test_ioctl_fault_alloc(&fault_id, &fault_fd); - test_err_hwpt_alloc_iopf( - ENOENT, dev_id, viommu_id, UINT32_MAX, - IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id, - IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)); - test_err_hwpt_alloc_iopf( - EOPNOTSUPP, dev_id, viommu_id, fault_id, - IOMMU_HWPT_FAULT_ID_VALID | (1 << 31), &iopf_hwpt_id, - IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)); - test_cmd_hwpt_alloc_iopf( - dev_id, viommu_id, fault_id, IOMMU_HWPT_FAULT_ID_VALID, - &iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST, &data, - sizeof(data)); + if (!dev_id) + SKIP(return, "Skipping test for variant no_viommu"); - /* Must allocate vdevice before attaching to a nested hwpt */ - test_err_mock_domain_replace(ENOENT, self->stdev_id, - iopf_hwpt_id); - test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id); - test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id); - EXPECT_ERRNO(EBUSY, - _test_ioctl_destroy(self->fd, iopf_hwpt_id)); - test_cmd_trigger_iopf(dev_id, fault_fd); + test_ioctl_fault_alloc(&fault_id, &fault_fd); + test_err_hwpt_alloc_iopf(ENOENT, dev_id, viommu_id, UINT32_MAX, + IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id, + IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)); + test_err_hwpt_alloc_iopf(EOPNOTSUPP, dev_id, viommu_id, fault_id, + IOMMU_HWPT_FAULT_ID_VALID | (1 << 31), + &iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST, &data, + sizeof(data)); + test_cmd_hwpt_alloc_iopf(dev_id, viommu_id, fault_id, + IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id, + IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)); - test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id); - test_ioctl_destroy(iopf_hwpt_id); - close(fault_fd); - test_ioctl_destroy(fault_id); - } + /* Must allocate vdevice before attaching to a nested hwpt */ + test_err_mock_domain_replace(ENOENT, self->stdev_id, iopf_hwpt_id); + test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id); + test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id); + EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, iopf_hwpt_id)); + test_cmd_trigger_iopf(dev_id, fault_fd); + + test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id); + test_ioctl_destroy(iopf_hwpt_id); + close(fault_fd); + test_ioctl_destroy(fault_id); +} + +TEST_F(iommufd_viommu, viommu_alloc_with_data) +{ + struct iommu_viommu_selftest data = { + .in_data = 0xbeef, + }; + uint32_t *test; + + if (!self->device_id) + SKIP(return, "Skipping test for variant no_viommu"); + + test_cmd_viommu_alloc(self->device_id, self->hwpt_id, + IOMMU_VIOMMU_TYPE_SELFTEST, &data, sizeof(data), + &self->viommu_id); + ASSERT_EQ(data.out_data, data.in_data); + + /* Negative mmap tests -- offset and length cannot be changed */ + test_err_mmap(ENXIO, data.out_mmap_length, + data.out_mmap_offset + PAGE_SIZE); + test_err_mmap(ENXIO, data.out_mmap_length, + data.out_mmap_offset + PAGE_SIZE * 2); + test_err_mmap(ENXIO, data.out_mmap_length / 2, data.out_mmap_offset); + test_err_mmap(ENXIO, data.out_mmap_length * 2, data.out_mmap_offset); + + /* Now do a correct mmap for a loopback test */ + test = mmap(NULL, data.out_mmap_length, PROT_READ | PROT_WRITE, + MAP_SHARED, self->fd, data.out_mmap_offset); + ASSERT_NE(MAP_FAILED, test); + ASSERT_EQ(data.in_data, *test); + + /* The owner of the mmap region should be blocked */ + EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->viommu_id)); + munmap(test, data.out_mmap_length); } TEST_F(iommufd_viommu, vdevice_alloc) @@ -2849,169 +2944,234 @@ TEST_F(iommufd_viommu, vdevice_cache) uint32_t vdev_id = 0; uint32_t num_inv; - if (dev_id) { - test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id); + if (!dev_id) + SKIP(return, "Skipping test for variant no_viommu"); - test_cmd_dev_check_cache_all(dev_id, - IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id); - /* Check data_type by passing zero-length array */ - num_inv = 0; - test_cmd_viommu_invalidate(viommu_id, inv_reqs, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + test_cmd_dev_check_cache_all(dev_id, IOMMU_TEST_DEV_CACHE_DEFAULT); - /* Negative test: Invalid data_type */ - num_inv = 1; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST_INVALID, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + /* Check data_type by passing zero-length array */ + num_inv = 0; + test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs), + &num_inv); + assert(!num_inv); - /* Negative test: structure size sanity */ - num_inv = 1; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs) + 1, &num_inv); - assert(!num_inv); + /* Negative test: Invalid data_type */ + num_inv = 1; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST_INVALID, + sizeof(*inv_reqs), &num_inv); + assert(!num_inv); - num_inv = 1; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - 1, &num_inv); - assert(!num_inv); + /* Negative test: structure size sanity */ + num_inv = 1; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs) + 1, &num_inv); + assert(!num_inv); - /* Negative test: invalid flag is passed */ - num_inv = 1; - inv_reqs[0].flags = 0xffffffff; - inv_reqs[0].vdev_id = 0x99; - test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + num_inv = 1; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 1, + &num_inv); + assert(!num_inv); - /* Negative test: invalid data_uptr when array is not empty */ - num_inv = 1; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - test_err_viommu_invalidate(EINVAL, viommu_id, NULL, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + /* Negative test: invalid flag is passed */ + num_inv = 1; + inv_reqs[0].flags = 0xffffffff; + inv_reqs[0].vdev_id = 0x99; + test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(!num_inv); - /* Negative test: invalid entry_len when array is not empty */ - num_inv = 1; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - 0, &num_inv); - assert(!num_inv); + /* Negative test: invalid data_uptr when array is not empty */ + num_inv = 1; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + test_err_viommu_invalidate(EINVAL, viommu_id, NULL, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(!num_inv); - /* Negative test: invalid cache_id */ - num_inv = 1; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].cache_id = MOCK_DEV_CACHE_ID_MAX + 1; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + /* Negative test: invalid entry_len when array is not empty */ + num_inv = 1; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 0, + &num_inv); + assert(!num_inv); - /* Negative test: invalid vdev_id */ - num_inv = 1; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x9; - inv_reqs[0].cache_id = 0; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(!num_inv); + /* Negative test: invalid cache_id */ + num_inv = 1; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].cache_id = MOCK_DEV_CACHE_ID_MAX + 1; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(!num_inv); - /* - * Invalidate the 1st cache entry but fail the 2nd request - * due to invalid flags configuration in the 2nd request. - */ - num_inv = 2; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].cache_id = 0; - inv_reqs[1].flags = 0xffffffff; - inv_reqs[1].vdev_id = 0x99; - inv_reqs[1].cache_id = 1; - test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(num_inv == 1); - test_cmd_dev_check_cache(dev_id, 0, 0); - test_cmd_dev_check_cache(dev_id, 1, - IOMMU_TEST_DEV_CACHE_DEFAULT); - test_cmd_dev_check_cache(dev_id, 2, - IOMMU_TEST_DEV_CACHE_DEFAULT); - test_cmd_dev_check_cache(dev_id, 3, - IOMMU_TEST_DEV_CACHE_DEFAULT); + /* Negative test: invalid vdev_id */ + num_inv = 1; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x9; + inv_reqs[0].cache_id = 0; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(!num_inv); - /* - * Invalidate the 1st cache entry but fail the 2nd request - * due to invalid cache_id configuration in the 2nd request. - */ - num_inv = 2; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].cache_id = 0; - inv_reqs[1].flags = 0; - inv_reqs[1].vdev_id = 0x99; - inv_reqs[1].cache_id = MOCK_DEV_CACHE_ID_MAX + 1; - test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, - IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, - sizeof(*inv_reqs), &num_inv); - assert(num_inv == 1); - test_cmd_dev_check_cache(dev_id, 0, 0); - test_cmd_dev_check_cache(dev_id, 1, - IOMMU_TEST_DEV_CACHE_DEFAULT); - test_cmd_dev_check_cache(dev_id, 2, - IOMMU_TEST_DEV_CACHE_DEFAULT); - test_cmd_dev_check_cache(dev_id, 3, - IOMMU_TEST_DEV_CACHE_DEFAULT); + /* + * Invalidate the 1st cache entry but fail the 2nd request + * due to invalid flags configuration in the 2nd request. + */ + num_inv = 2; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].cache_id = 0; + inv_reqs[1].flags = 0xffffffff; + inv_reqs[1].vdev_id = 0x99; + inv_reqs[1].cache_id = 1; + test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(num_inv == 1); + test_cmd_dev_check_cache(dev_id, 0, 0); + test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT); - /* Invalidate the 2nd cache entry and verify */ - num_inv = 1; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].cache_id = 1; - test_cmd_viommu_invalidate(viommu_id, inv_reqs, - sizeof(*inv_reqs), &num_inv); - assert(num_inv == 1); - test_cmd_dev_check_cache(dev_id, 0, 0); - test_cmd_dev_check_cache(dev_id, 1, 0); - test_cmd_dev_check_cache(dev_id, 2, - IOMMU_TEST_DEV_CACHE_DEFAULT); - test_cmd_dev_check_cache(dev_id, 3, - IOMMU_TEST_DEV_CACHE_DEFAULT); + /* + * Invalidate the 1st cache entry but fail the 2nd request + * due to invalid cache_id configuration in the 2nd request. + */ + num_inv = 2; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].cache_id = 0; + inv_reqs[1].flags = 0; + inv_reqs[1].vdev_id = 0x99; + inv_reqs[1].cache_id = MOCK_DEV_CACHE_ID_MAX + 1; + test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs, + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, + sizeof(*inv_reqs), &num_inv); + assert(num_inv == 1); + test_cmd_dev_check_cache(dev_id, 0, 0); + test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT); - /* Invalidate the 3rd and 4th cache entries and verify */ - num_inv = 2; - inv_reqs[0].flags = 0; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].cache_id = 2; - inv_reqs[1].flags = 0; - inv_reqs[1].vdev_id = 0x99; - inv_reqs[1].cache_id = 3; - test_cmd_viommu_invalidate(viommu_id, inv_reqs, - sizeof(*inv_reqs), &num_inv); - assert(num_inv == 2); - test_cmd_dev_check_cache_all(dev_id, 0); + /* Invalidate the 2nd cache entry and verify */ + num_inv = 1; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].cache_id = 1; + test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs), + &num_inv); + assert(num_inv == 1); + test_cmd_dev_check_cache(dev_id, 0, 0); + test_cmd_dev_check_cache(dev_id, 1, 0); + test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT); + test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT); - /* Invalidate all cache entries for nested_dev_id[1] and verify */ - num_inv = 1; - inv_reqs[0].vdev_id = 0x99; - inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL; - test_cmd_viommu_invalidate(viommu_id, inv_reqs, - sizeof(*inv_reqs), &num_inv); - assert(num_inv == 1); - test_cmd_dev_check_cache_all(dev_id, 0); - test_ioctl_destroy(vdev_id); - } + /* Invalidate the 3rd and 4th cache entries and verify */ + num_inv = 2; + inv_reqs[0].flags = 0; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].cache_id = 2; + inv_reqs[1].flags = 0; + inv_reqs[1].vdev_id = 0x99; + inv_reqs[1].cache_id = 3; + test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs), + &num_inv); + assert(num_inv == 2); + test_cmd_dev_check_cache_all(dev_id, 0); + + /* Invalidate all cache entries for nested_dev_id[1] and verify */ + num_inv = 1; + inv_reqs[0].vdev_id = 0x99; + inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL; + test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs), + &num_inv); + assert(num_inv == 1); + test_cmd_dev_check_cache_all(dev_id, 0); + test_ioctl_destroy(vdev_id); +} + +TEST_F(iommufd_viommu, hw_queue) +{ + __u64 iova = MOCK_APERTURE_START, iova2; + uint32_t viommu_id = self->viommu_id; + uint32_t hw_queue_id[2]; + + if (!viommu_id) + SKIP(return, "Skipping test for variant no_viommu"); + + /* Fail IOMMU_HW_QUEUE_TYPE_DEFAULT */ + test_err_hw_queue_alloc(EOPNOTSUPP, viommu_id, + IOMMU_HW_QUEUE_TYPE_DEFAULT, 0, iova, PAGE_SIZE, + &hw_queue_id[0]); + /* Fail queue addr and length */ + test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, + 0, iova, 0, &hw_queue_id[0]); + test_err_hw_queue_alloc(EOVERFLOW, viommu_id, + IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, ~(uint64_t)0, + PAGE_SIZE, &hw_queue_id[0]); + /* Fail missing iova */ + test_err_hw_queue_alloc(ENOENT, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, + 0, iova, PAGE_SIZE, &hw_queue_id[0]); + + /* Map iova */ + test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova); + test_ioctl_ioas_map(buffer + PAGE_SIZE, PAGE_SIZE, &iova2); + + /* Fail index=1 and =MAX; must start from index=0 */ + test_err_hw_queue_alloc(EIO, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1, + iova, PAGE_SIZE, &hw_queue_id[0]); + test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, + IOMMU_TEST_HW_QUEUE_MAX, iova, PAGE_SIZE, + &hw_queue_id[0]); + + /* Allocate index=0, declare ownership of the iova */ + test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, + iova, PAGE_SIZE, &hw_queue_id[0]); + /* Fail duplicated index */ + test_err_hw_queue_alloc(EEXIST, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, + 0, iova, PAGE_SIZE, &hw_queue_id[0]); + /* Fail unmap, due to iova ownership */ + test_err_ioctl_ioas_unmap(EBUSY, iova, PAGE_SIZE); + /* The 2nd page is not pinned, so it can be unmmap */ + test_ioctl_ioas_unmap(iova2, PAGE_SIZE); + + /* Allocate index=1, with an unaligned case */ + test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1, + iova + PAGE_SIZE / 2, PAGE_SIZE / 2, + &hw_queue_id[1]); + /* Fail to destroy, due to dependency */ + EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hw_queue_id[0])); + + /* Destroy in descending order */ + test_ioctl_destroy(hw_queue_id[1]); + test_ioctl_destroy(hw_queue_id[0]); + /* Now it can unmap the first page */ + test_ioctl_ioas_unmap(iova, PAGE_SIZE); +} + +TEST_F(iommufd_viommu, vdevice_tombstone) +{ + uint32_t viommu_id = self->viommu_id; + uint32_t dev_id = self->device_id; + uint32_t vdev_id = 0; + + if (!dev_id) + SKIP(return, "Skipping test for variant no_viommu"); + + test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id); + test_ioctl_destroy(self->stdev_id); + EXPECT_ERRNO(ENOENT, _test_ioctl_destroy(self->fd, vdev_id)); } FIXTURE(iommufd_device_pasid) @@ -3105,8 +3265,7 @@ TEST_F(iommufd_device_pasid, pasid_attach) /* Allocate a regular nested hwpt based on viommu */ test_cmd_viommu_alloc(self->device_id, parent_hwpt_id, - IOMMU_VIOMMU_TYPE_SELFTEST, - &viommu_id); + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, &viommu_id); test_cmd_hwpt_alloc_nested(self->device_id, viommu_id, IOMMU_HWPT_ALLOC_PASID, &nested_hwpt_id[2], diff --git a/tools/testing/selftests/iommu/iommufd_fail_nth.c b/tools/testing/selftests/iommu/iommufd_fail_nth.c index e11ec4b121fc..651fc9f13c08 100644 --- a/tools/testing/selftests/iommu/iommufd_fail_nth.c +++ b/tools/testing/selftests/iommu/iommufd_fail_nth.c @@ -634,6 +634,7 @@ TEST_FAIL_NTH(basic_fail_nth, device) uint32_t idev_id; uint32_t hwpt_id; uint32_t viommu_id; + uint32_t hw_queue_id; uint32_t vdev_id; __u64 iova; @@ -666,8 +667,8 @@ TEST_FAIL_NTH(basic_fail_nth, device) &self->stdev_id, NULL, &idev_id)) return -1; - if (_test_cmd_get_hw_info(self->fd, idev_id, &info, - sizeof(info), NULL, NULL)) + if (_test_cmd_get_hw_info(self->fd, idev_id, IOMMU_HW_INFO_TYPE_DEFAULT, + &info, sizeof(info), NULL, NULL)) return -1; if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0, @@ -688,13 +689,19 @@ TEST_FAIL_NTH(basic_fail_nth, device) IOMMU_HWPT_DATA_NONE, 0, 0)) return -1; - if (_test_cmd_viommu_alloc(self->fd, idev_id, hwpt_id, - IOMMU_VIOMMU_TYPE_SELFTEST, 0, &viommu_id)) + if (_test_cmd_viommu_alloc(self->fd, idev_id, hwpt_id, 0, + IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, + &viommu_id)) return -1; if (_test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, 0, &vdev_id)) return -1; + if (_test_cmd_hw_queue_alloc(self->fd, viommu_id, + IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, iova, + PAGE_SIZE, &hw_queue_id)) + return -1; + if (_test_ioctl_fault_alloc(self->fd, &fault_id, &fault_fd)) return -1; close(fault_fd); diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h index 72f6636e5d90..3c3e08b8c90e 100644 --- a/tools/testing/selftests/iommu/iommufd_utils.h +++ b/tools/testing/selftests/iommu/iommufd_utils.h @@ -56,17 +56,26 @@ static unsigned long PAGE_SIZE; #define offsetofend(TYPE, MEMBER) \ (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) +#define test_err_mmap(_errno, length, offset) \ + EXPECT_ERRNO(_errno, (long)mmap(NULL, length, PROT_READ | PROT_WRITE, \ + MAP_SHARED, self->fd, offset)) + static inline void *memfd_mmap(size_t length, int prot, int flags, int *mfd_p) { int mfd_flags = (flags & MAP_HUGETLB) ? MFD_HUGETLB : 0; int mfd = memfd_create("buffer", mfd_flags); + void *buf = MAP_FAILED; if (mfd <= 0) return MAP_FAILED; if (ftruncate(mfd, length)) - return MAP_FAILED; + goto out; *mfd_p = mfd; - return mmap(0, length, prot, flags, mfd, 0); + buf = mmap(0, length, prot, flags, mfd, 0); +out: + if (buf == MAP_FAILED) + close(mfd); + return buf; } /* @@ -757,20 +766,24 @@ static void teardown_iommufd(int fd, struct __test_metadata *_metadata) #endif /* @data can be NULL */ -static int _test_cmd_get_hw_info(int fd, __u32 device_id, void *data, - size_t data_len, uint32_t *capabilities, - uint8_t *max_pasid) +static int _test_cmd_get_hw_info(int fd, __u32 device_id, __u32 data_type, + void *data, size_t data_len, + uint32_t *capabilities, uint8_t *max_pasid) { struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; struct iommu_hw_info cmd = { .size = sizeof(cmd), .dev_id = device_id, .data_len = data_len, + .in_data_type = data_type, .data_uptr = (uint64_t)data, .out_capabilities = 0, }; int ret; + if (data_type != IOMMU_HW_INFO_TYPE_DEFAULT) + cmd.flags |= IOMMU_HW_INFO_FLAG_INPUT_TYPE; + ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); if (ret) return ret; @@ -813,20 +826,23 @@ static int _test_cmd_get_hw_info(int fd, __u32 device_id, void *data, return 0; } -#define test_cmd_get_hw_info(device_id, data, data_len) \ - ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, data, \ - data_len, NULL, NULL)) +#define test_cmd_get_hw_info(device_id, data_type, data, data_len) \ + ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, data_type, \ + data, data_len, NULL, NULL)) -#define test_err_get_hw_info(_errno, device_id, data, data_len) \ - EXPECT_ERRNO(_errno, _test_cmd_get_hw_info(self->fd, device_id, data, \ - data_len, NULL, NULL)) +#define test_err_get_hw_info(_errno, device_id, data_type, data, data_len) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_get_hw_info(self->fd, device_id, data_type, \ + data, data_len, NULL, NULL)) -#define test_cmd_get_hw_capabilities(device_id, caps, mask) \ - ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, NULL, \ +#define test_cmd_get_hw_capabilities(device_id, caps) \ + ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ + IOMMU_HW_INFO_TYPE_DEFAULT, NULL, \ 0, &caps, NULL)) -#define test_cmd_get_hw_info_pasid(device_id, max_pasid) \ - ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, NULL, \ +#define test_cmd_get_hw_info_pasid(device_id, max_pasid) \ + ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ + IOMMU_HW_INFO_TYPE_DEFAULT, NULL, \ 0, NULL, max_pasid)) static int _test_ioctl_fault_alloc(int fd, __u32 *fault_id, __u32 *fault_fd) @@ -897,7 +913,8 @@ static int _test_cmd_trigger_iopf(int fd, __u32 device_id, __u32 pasid, pasid, fault_fd)) static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id, - __u32 type, __u32 flags, __u32 *viommu_id) + __u32 flags, __u32 type, void *data, + __u32 data_len, __u32 *viommu_id) { struct iommu_viommu_alloc cmd = { .size = sizeof(cmd), @@ -905,6 +922,8 @@ static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id, .type = type, .dev_id = device_id, .hwpt_id = hwpt_id, + .data_uptr = (uint64_t)data, + .data_len = data_len, }; int ret; @@ -916,13 +935,15 @@ static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id, return 0; } -#define test_cmd_viommu_alloc(device_id, hwpt_id, type, viommu_id) \ - ASSERT_EQ(0, _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, \ - type, 0, viommu_id)) -#define test_err_viommu_alloc(_errno, device_id, hwpt_id, type, viommu_id) \ - EXPECT_ERRNO(_errno, \ - _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, \ - type, 0, viommu_id)) +#define test_cmd_viommu_alloc(device_id, hwpt_id, type, data, data_len, \ + viommu_id) \ + ASSERT_EQ(0, _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \ + type, data, data_len, viommu_id)) +#define test_err_viommu_alloc(_errno, device_id, hwpt_id, type, data, \ + data_len, viommu_id) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \ + type, data, data_len, viommu_id)) static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id, __u64 virt_id, __u32 *vdev_id) @@ -951,6 +972,37 @@ static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id, _test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \ virt_id, vdev_id)) +static int _test_cmd_hw_queue_alloc(int fd, __u32 viommu_id, __u32 type, + __u32 idx, __u64 base_addr, __u64 length, + __u32 *hw_queue_id) +{ + struct iommu_hw_queue_alloc cmd = { + .size = sizeof(cmd), + .viommu_id = viommu_id, + .type = type, + .index = idx, + .nesting_parent_iova = base_addr, + .length = length, + }; + int ret; + + ret = ioctl(fd, IOMMU_HW_QUEUE_ALLOC, &cmd); + if (ret) + return ret; + if (hw_queue_id) + *hw_queue_id = cmd.out_hw_queue_id; + return 0; +} + +#define test_cmd_hw_queue_alloc(viommu_id, type, idx, base_addr, len, out_qid) \ + ASSERT_EQ(0, _test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \ + base_addr, len, out_qid)) +#define test_err_hw_queue_alloc(_errno, viommu_id, type, idx, base_addr, len, \ + out_qid) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \ + base_addr, len, out_qid)) + static int _test_cmd_veventq_alloc(int fd, __u32 viommu_id, __u32 type, __u32 *veventq_id, __u32 *veventq_fd) { diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c index e9dbb84c100a..5e36aeeb9901 100644 --- a/tools/testing/selftests/ipc/msgque.c +++ b/tools/testing/selftests/ipc/msgque.c @@ -39,26 +39,26 @@ int restore_queue(struct msgque_data *msgque) fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); if (fd == -1) { - printf("Failed to open /proc/sys/kernel/msg_next_id\n"); + ksft_test_result_fail("Failed to open /proc/sys/kernel/msg_next_id\n"); return -errno; } sprintf(buf, "%d", msgque->msq_id); ret = write(fd, buf, strlen(buf)); if (ret != strlen(buf)) { - printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); + ksft_test_result_fail("Failed to write to /proc/sys/kernel/msg_next_id\n"); return -errno; } id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); if (id == -1) { - printf("Failed to create queue\n"); + ksft_test_result_fail("Failed to create queue\n"); return -errno; } if (id != msgque->msq_id) { - printf("Restored queue has wrong id (%d instead of %d)\n", - id, msgque->msq_id); + ksft_test_result_fail("Restored queue has wrong id (%d instead of %d)\n" + , id, msgque->msq_id); ret = -EFAULT; goto destroy; } @@ -66,7 +66,7 @@ int restore_queue(struct msgque_data *msgque) for (i = 0; i < msgque->qnum; i++) { if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, msgque->messages[i].msize, IPC_NOWAIT) != 0) { - printf("msgsnd failed (%m)\n"); + ksft_test_result_fail("msgsnd failed (%m)\n"); ret = -errno; goto destroy; } @@ -90,23 +90,22 @@ int check_and_destroy_queue(struct msgque_data *msgque) if (ret < 0) { if (errno == ENOMSG) break; - printf("Failed to read IPC message: %m\n"); + ksft_test_result_fail("Failed to read IPC message: %m\n"); ret = -errno; goto err; } if (ret != msgque->messages[cnt].msize) { - printf("Wrong message size: %d (expected %d)\n", ret, - msgque->messages[cnt].msize); + ksft_test_result_fail("Wrong message size: %d (expected %d)\n", ret, msgque->messages[cnt].msize); ret = -EINVAL; goto err; } if (message.mtype != msgque->messages[cnt].mtype) { - printf("Wrong message type\n"); + ksft_test_result_fail("Wrong message type\n"); ret = -EINVAL; goto err; } if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { - printf("Wrong message content\n"); + ksft_test_result_fail("Wrong message content\n"); ret = -EINVAL; goto err; } @@ -114,7 +113,7 @@ int check_and_destroy_queue(struct msgque_data *msgque) } if (cnt != msgque->qnum) { - printf("Wrong message number\n"); + ksft_test_result_fail("Wrong message number\n"); ret = -EINVAL; goto err; } @@ -139,7 +138,7 @@ int dump_queue(struct msgque_data *msgque) if (ret < 0) { if (errno == EINVAL) continue; - printf("Failed to get stats for IPC queue with id %d\n", + ksft_test_result_fail("Failed to get stats for IPC queue with id %d\n", kern_id); return -errno; } @@ -150,7 +149,7 @@ int dump_queue(struct msgque_data *msgque) msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); if (msgque->messages == NULL) { - printf("Failed to get stats for IPC queue\n"); + ksft_test_result_fail("Failed to get stats for IPC queue\n"); return -ENOMEM; } @@ -162,7 +161,7 @@ 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) { - printf("Failed to copy IPC message: %m (%d)\n", errno); + ksft_test_result_fail("Failed to copy IPC message: %m (%d)\n", errno); return -errno; } msgque->messages[i].msize = ret; @@ -178,7 +177,7 @@ int fill_msgque(struct msgque_data *msgque) memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), IPC_NOWAIT) != 0) { - printf("First message send failed (%m)\n"); + ksft_test_result_fail("First message send failed (%m)\n"); return -errno; } @@ -186,7 +185,7 @@ int fill_msgque(struct msgque_data *msgque) memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), IPC_NOWAIT) != 0) { - printf("Second message send failed (%m)\n"); + ksft_test_result_fail("Second message send failed (%m)\n"); return -errno; } return 0; @@ -202,44 +201,44 @@ int main(int argc, char **argv) msgque.key = ftok(argv[0], 822155650); if (msgque.key == -1) { - printf("Can't make key: %d\n", -errno); + ksft_test_result_fail("Can't make key: %d\n", -errno); ksft_exit_fail(); } msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); if (msgque.msq_id == -1) { err = -errno; - printf("Can't create queue: %d\n", err); + ksft_test_result_fail("Can't create queue: %d\n", err); goto err_out; } err = fill_msgque(&msgque); if (err) { - printf("Failed to fill queue: %d\n", err); + ksft_test_result_fail("Failed to fill queue: %d\n", err); goto err_destroy; } err = dump_queue(&msgque); if (err) { - printf("Failed to dump queue: %d\n", err); + ksft_test_result_fail("Failed to dump queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to check and destroy queue: %d\n", err); + ksft_test_result_fail("Failed to check and destroy queue: %d\n", err); goto err_out; } err = restore_queue(&msgque); if (err) { - printf("Failed to restore queue: %d\n", err); + ksft_test_result_fail("Failed to restore queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to test queue: %d\n", err); + ksft_test_result_fail("Failed to test queue: %d\n", err); goto err_out; } ksft_exit_pass(); diff --git a/tools/testing/selftests/kexec/Makefile b/tools/testing/selftests/kexec/Makefile index e3000ccb9a5d..874cfdd3b75b 100644 --- a/tools/testing/selftests/kexec/Makefile +++ b/tools/testing/selftests/kexec/Makefile @@ -12,7 +12,7 @@ include ../../../scripts/Makefile.arch ifeq ($(IS_64_BIT)$(ARCH_PROCESSED),1x86) TEST_PROGS += test_kexec_jump.sh -test_kexec_jump.sh: $(OUTPUT)/test_kexec_jump +TEST_GEN_PROGS := test_kexec_jump endif include ../lib.mk diff --git a/tools/testing/selftests/kho/arm64.conf b/tools/testing/selftests/kho/arm64.conf new file mode 100644 index 000000000000..ee696807cd35 --- /dev/null +++ b/tools/testing/selftests/kho/arm64.conf @@ -0,0 +1,9 @@ +QEMU_CMD="qemu-system-aarch64 -M virt -cpu max" +QEMU_KCONFIG=" +CONFIG_SERIAL_AMBA_PL010=y +CONFIG_SERIAL_AMBA_PL010_CONSOLE=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +" +KERNEL_IMAGE="Image" +KERNEL_CMDLINE="console=ttyAMA0" diff --git a/tools/testing/selftests/kho/init.c b/tools/testing/selftests/kho/init.c new file mode 100644 index 000000000000..8034e24c6bf6 --- /dev/null +++ b/tools/testing/selftests/kho/init.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef NOLIBC +#include +#include +#include +#include +#include +#include +#include +#endif + +/* from arch/x86/include/asm/setup.h */ +#define COMMAND_LINE_SIZE 2048 + +/* from include/linux/kexex.h */ +#define KEXEC_FILE_NO_INITRAMFS 0x00000004 + +#define KHO_FINILIZE "/debugfs/kho/out/finalize" +#define KERNEL_IMAGE "/kernel" + +static int mount_filesystems(void) +{ + if (mount("debugfs", "/debugfs", "debugfs", 0, NULL) < 0) + return -1; + + return mount("proc", "/proc", "proc", 0, NULL); +} + +static int kho_enable(void) +{ + const char enable[] = "1"; + int fd; + + fd = open(KHO_FINILIZE, O_RDWR); + if (fd < 0) + return -1; + + if (write(fd, enable, sizeof(enable)) != sizeof(enable)) + return 1; + + close(fd); + return 0; +} + +static long kexec_file_load(int kernel_fd, int initrd_fd, + unsigned long cmdline_len, const char *cmdline, + unsigned long flags) +{ + return syscall(__NR_kexec_file_load, kernel_fd, initrd_fd, cmdline_len, + cmdline, flags); +} + +static int kexec_load(void) +{ + char cmdline[COMMAND_LINE_SIZE]; + ssize_t len; + int fd, err; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd < 0) + return -1; + + len = read(fd, cmdline, sizeof(cmdline)); + close(fd); + if (len < 0) + return -1; + + /* replace \n with \0 */ + cmdline[len - 1] = 0; + fd = open(KERNEL_IMAGE, O_RDONLY); + if (fd < 0) + return -1; + + err = kexec_file_load(fd, -1, len, cmdline, KEXEC_FILE_NO_INITRAMFS); + close(fd); + + return err ? : 0; +} + +int main(int argc, char *argv[]) +{ + if (mount_filesystems()) + goto err_reboot; + + if (kho_enable()) + goto err_reboot; + + if (kexec_load()) + goto err_reboot; + + if (reboot(RB_KEXEC)) + goto err_reboot; + + return 0; + +err_reboot: + reboot(RB_AUTOBOOT); + return -1; +} diff --git a/tools/testing/selftests/kho/vmtest.sh b/tools/testing/selftests/kho/vmtest.sh new file mode 100755 index 000000000000..ec70a17bd476 --- /dev/null +++ b/tools/testing/selftests/kho/vmtest.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +set -ue + +CROSS_COMPILE="${CROSS_COMPILE:-""}" + +test_dir=$(realpath "$(dirname "$0")") +kernel_dir=$(realpath "$test_dir/../../../..") + +tmp_dir=$(mktemp -d /tmp/kho-test.XXXXXXXX) +headers_dir="$tmp_dir/usr" +initrd_dir="$tmp_dir/initrd" +initrd="$tmp_dir/initrd.cpio" + +source "$test_dir/../kselftest/ktap_helpers.sh" + +function usage() { + cat < "$kho_config" </dev/null || skip "$opt is missing" + done < "$kho_config" + + $make_cmd "$kimage" + $make_cmd headers_install INSTALL_HDR_PATH="$headers_dir" +} + +function mkinitrd() { + local kernel=$1 + + mkdir -p "$initrd_dir"/{dev,debugfs,proc} + sudo mknod "$initrd_dir/dev/console" c 5 1 + + "$CROSS_COMPILE"gcc -s -static -Os -nostdinc -I"$headers_dir/include" \ + -fno-asynchronous-unwind-tables -fno-ident -nostdlib \ + -include "$test_dir/../../../include/nolibc/nolibc.h" \ + -o "$initrd_dir/init" "$test_dir/init.c" \ + + cp "$kernel" "$initrd_dir/kernel" + + pushd "$initrd_dir" &>/dev/null + find . | cpio -H newc --create > "$initrd" 2>/dev/null + popd &>/dev/null +} + +function run_qemu() { + local qemu_cmd=$1 + local cmdline=$2 + local kernel=$3 + local serial="$tmp_dir/qemu.serial" + + cmdline="$cmdline kho=on panic=-1" + + $qemu_cmd -m 1G -smp 2 -no-reboot -nographic -nodefaults \ + -accel kvm -accel hvf -accel tcg \ + -serial file:"$serial" \ + -append "$cmdline" \ + -kernel "$kernel" \ + -initrd "$initrd" + + grep "KHO restore succeeded" "$serial" &> /dev/null || fail "KHO failed" +} + +function target_to_arch() { + local target=$1 + + case $target in + aarch64) echo "arm64" ;; + x86_64) echo "x86" ;; + *) skip "architecture $target is not supported" + esac +} + +function main() { + local build_dir="$kernel_dir/.kho" + local jobs=$(($(nproc) * 2)) + local target="$(uname -m)" + + # skip the test if any of the preparation steps fails + set -o errtrace + trap skip ERR + + while getopts 'hd:j:t:' opt; do + case $opt in + d) + build_dir="$OPTARG" + ;; + j) + jobs="$OPTARG" + ;; + t) + target="$OPTARG" + ;; + h) + usage + exit 0 + ;; + *) + echo Unknown argument "$opt" + usage + exit 1 + ;; + esac + done + + ktap_print_header + ktap_set_plan 1 + + if [[ "$target" != "$(uname -m)" ]] && [[ -z "$CROSS_COMPILE" ]]; then + skip "Cross-platform testing needs to specify CROSS_COMPILE" + fi + + mkdir -p "$build_dir" + local arch=$(target_to_arch "$target") + source "$test_dir/$arch.conf" + + # build the kernel and create initrd + # initrd includes the kernel image that will be kexec'ed + local make_cmd="make ARCH=$arch CROSS_COMPILE=$CROSS_COMPILE -j$jobs" + build_kernel "$build_dir" "$make_cmd" "$QEMU_KCONFIG" "$KERNEL_IMAGE" + + local kernel="$build_dir/arch/$arch/boot/$KERNEL_IMAGE" + mkinitrd "$kernel" + + run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE" "$kernel" + + ktap_test_pass "KHO succeeded" +} + +main "$@" diff --git a/tools/testing/selftests/kho/x86.conf b/tools/testing/selftests/kho/x86.conf new file mode 100644 index 000000000000..b419e610ca22 --- /dev/null +++ b/tools/testing/selftests/kho/x86.conf @@ -0,0 +1,7 @@ +QEMU_CMD=qemu-system-x86_64 +QEMU_KCONFIG=" +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +" +KERNEL_IMAGE="bzImage" +KERNEL_CMDLINE="console=ttyS0" diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 2925e47db995..8516e8434bc4 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -751,7 +751,7 @@ for (; _metadata->trigger; _metadata->trigger = \ __bail(_assert, _metadata)) -#define is_signed_type(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1)) +#define is_signed_var(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1)) #define __EXPECT(_expected, _expected_str, _seen, _seen_str, _t, _assert) do { \ /* Avoid multiple evaluation of the cases */ \ @@ -759,7 +759,7 @@ __typeof__(_seen) __seen = (_seen); \ if (!(__exp _t __seen)) { \ /* Report with actual signedness to avoid weird output. */ \ - switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \ + switch (is_signed_var(__exp) * 2 + is_signed_var(__seen)) { \ case 0: { \ uintmax_t __exp_print = (uintmax_t)__exp; \ uintmax_t __seen_print = (uintmax_t)__seen; \ diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 38b95998e1e6..41b40c676d7f 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -59,6 +59,7 @@ TEST_PROGS_x86 += x86/nx_huge_pages_test.sh TEST_GEN_PROGS_COMMON = demand_paging_test TEST_GEN_PROGS_COMMON += dirty_log_test TEST_GEN_PROGS_COMMON += guest_print_test +TEST_GEN_PROGS_COMMON += irqfd_test TEST_GEN_PROGS_COMMON += kvm_binary_stats_test TEST_GEN_PROGS_COMMON += kvm_create_max_vcpus TEST_GEN_PROGS_COMMON += kvm_page_table_test @@ -134,6 +135,7 @@ TEST_GEN_PROGS_x86 += x86/amx_test TEST_GEN_PROGS_x86 += x86/max_vcpuid_cap_test TEST_GEN_PROGS_x86 += x86/triple_fault_event_test TEST_GEN_PROGS_x86 += x86/recalc_apic_map_test +TEST_GEN_PROGS_x86 += x86/aperfmperf_test TEST_GEN_PROGS_x86 += access_tracking_perf_test TEST_GEN_PROGS_x86 += coalesced_io_test TEST_GEN_PROGS_x86 += dirty_log_perf_test @@ -156,7 +158,7 @@ TEST_GEN_PROGS_arm64 += arm64/arch_timer_edge_cases TEST_GEN_PROGS_arm64 += arm64/debug-exceptions TEST_GEN_PROGS_arm64 += arm64/host_sve TEST_GEN_PROGS_arm64 += arm64/hypercalls -TEST_GEN_PROGS_arm64 += arm64/mmio_abort +TEST_GEN_PROGS_arm64 += arm64/external_aborts TEST_GEN_PROGS_arm64 += arm64/page_fault_test TEST_GEN_PROGS_arm64 += arm64/psci_test TEST_GEN_PROGS_arm64 += arm64/set_id_regs @@ -167,6 +169,7 @@ TEST_GEN_PROGS_arm64 += arm64/vgic_irq TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access TEST_GEN_PROGS_arm64 += arm64/no-vgic-v3 +TEST_GEN_PROGS_arm64 += arm64/kvm-uuid TEST_GEN_PROGS_arm64 += access_tracking_perf_test TEST_GEN_PROGS_arm64 += arch_timer TEST_GEN_PROGS_arm64 += coalesced_io_test diff --git a/tools/testing/selftests/kvm/access_tracking_perf_test.c b/tools/testing/selftests/kvm/access_tracking_perf_test.c index da7196fd1b23..c9de66537ec3 100644 --- a/tools/testing/selftests/kvm/access_tracking_perf_test.c +++ b/tools/testing/selftests/kvm/access_tracking_perf_test.c @@ -596,11 +596,8 @@ int main(int argc, char *argv[]) if (ret) return ret; } else { - page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR); - __TEST_REQUIRE(page_idle_fd >= 0, - "Couldn't open /sys/kernel/mm/page_idle/bitmap. " - "Is CONFIG_IDLE_PAGE_TRACKING enabled?"); - + page_idle_fd = __open_path_or_exit("/sys/kernel/mm/page_idle/bitmap", O_RDWR, + "Is CONFIG_IDLE_PAGE_TRACKING enabled?"); close(page_idle_fd); puts("Using page_idle for aging"); diff --git a/tools/testing/selftests/kvm/arch_timer.c b/tools/testing/selftests/kvm/arch_timer.c index acb2cb596332..cf8fb67104f1 100644 --- a/tools/testing/selftests/kvm/arch_timer.c +++ b/tools/testing/selftests/kvm/arch_timer.c @@ -98,16 +98,11 @@ static uint32_t test_get_pcpu(void) static int test_migrate_vcpu(unsigned int vcpu_idx) { int ret; - cpu_set_t cpuset; uint32_t new_pcpu = test_get_pcpu(); - CPU_ZERO(&cpuset); - CPU_SET(new_pcpu, &cpuset); - pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu_idx, new_pcpu); - ret = pthread_setaffinity_np(pt_vcpu_run[vcpu_idx], - sizeof(cpuset), &cpuset); + ret = __pin_task_to_cpu(pt_vcpu_run[vcpu_idx], new_pcpu); /* Allow the error where the vCPU thread is already finished */ TEST_ASSERT(ret == 0 || ret == ESRCH, diff --git a/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c b/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c index cef8f7323ceb..713005b6f508 100644 --- a/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c +++ b/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c @@ -146,7 +146,7 @@ static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu) val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); - el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val); + el0 = FIELD_GET(ID_AA64PFR0_EL1_EL0, val); return el0 == ID_AA64PFR0_EL1_EL0_IMP; } 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 b4d22b3ab7cc..ce74d069cb7b 100644 --- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c +++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c @@ -862,25 +862,6 @@ static uint32_t next_pcpu(void) return next; } -static void migrate_self(uint32_t new_pcpu) -{ - int ret; - cpu_set_t cpuset; - pthread_t thread; - - thread = pthread_self(); - - CPU_ZERO(&cpuset); - CPU_SET(new_pcpu, &cpuset); - - pr_debug("Migrating from %u to %u\n", sched_getcpu(), new_pcpu); - - ret = pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset); - - TEST_ASSERT(ret == 0, "Failed to migrate to pCPU: %u; ret: %d\n", - new_pcpu, ret); -} - static void kvm_set_cntxct(struct kvm_vcpu *vcpu, uint64_t cnt, enum arch_timer timer) { @@ -907,7 +888,7 @@ static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) sched_yield(); break; case USERSPACE_MIGRATE_SELF: - migrate_self(next_pcpu()); + pin_self_to_cpu(next_pcpu()); break; default: break; @@ -919,7 +900,7 @@ static void test_run(struct kvm_vm *vm, struct kvm_vcpu *vcpu) struct ucall uc; /* Start on CPU 0 */ - migrate_self(0); + pin_self_to_cpu(0); while (true) { vcpu_run(vcpu); @@ -954,6 +935,8 @@ static void test_init_timer_irq(struct kvm_vm *vm, struct kvm_vcpu *vcpu) pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq); } +static int gic_fd; + static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu, enum arch_timer timer) { @@ -968,12 +951,20 @@ static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu, vcpu_args_set(*vcpu, 1, timer); test_init_timer_irq(*vm, *vcpu); - vgic_v3_setup(*vm, 1, 64); + gic_fd = vgic_v3_setup(*vm, 1, 64); + __TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3"); + sync_global_to_guest(*vm, test_args); sync_global_to_guest(*vm, CVAL_MAX); sync_global_to_guest(*vm, DEF_CNT); } +static void test_vm_cleanup(struct kvm_vm *vm) +{ + close(gic_fd); + kvm_vm_free(vm); +} + static void test_print_help(char *name) { pr_info("Usage: %s [-h] [-b] [-i iterations] [-l long_wait_ms] [-p] [-v]\n" @@ -1060,13 +1051,13 @@ int main(int argc, char *argv[]) if (test_args.test_virtual) { test_vm_create(&vm, &vcpu, VIRTUAL); test_run(vm, vcpu); - kvm_vm_free(vm); + test_vm_cleanup(vm); } if (test_args.test_physical) { test_vm_create(&vm, &vcpu, PHYSICAL); test_run(vm, vcpu); - kvm_vm_free(vm); + test_vm_cleanup(vm); } return 0; diff --git a/tools/testing/selftests/kvm/arm64/debug-exceptions.c b/tools/testing/selftests/kvm/arm64/debug-exceptions.c index c7fb55c9135b..1d431de8729c 100644 --- a/tools/testing/selftests/kvm/arm64/debug-exceptions.c +++ b/tools/testing/selftests/kvm/arm64/debug-exceptions.c @@ -116,12 +116,12 @@ static void reset_debug_state(void) /* Reset all bcr/bvr/wcr/wvr registers */ dfr0 = read_sysreg(id_aa64dfr0_el1); - brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), dfr0); + brps = FIELD_GET(ID_AA64DFR0_EL1_BRPs, dfr0); for (i = 0; i <= brps; i++) { write_dbgbcr(i, 0); write_dbgbvr(i, 0); } - wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), dfr0); + wrps = FIELD_GET(ID_AA64DFR0_EL1_WRPs, dfr0); for (i = 0; i <= wrps; i++) { write_dbgwcr(i, 0); write_dbgwvr(i, 0); @@ -140,7 +140,7 @@ static void enable_os_lock(void) static void enable_monitor_debug_exceptions(void) { - uint32_t mdscr; + uint64_t mdscr; asm volatile("msr daifclr, #8"); @@ -223,7 +223,7 @@ void install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr, static void install_ss(void) { - uint32_t mdscr; + uint64_t mdscr; asm volatile("msr daifclr, #8"); @@ -418,7 +418,7 @@ static void guest_code_ss(int test_cnt) static int debug_version(uint64_t id_aa64dfr0) { - return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 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) @@ -539,14 +539,14 @@ void test_guest_debug_exceptions_all(uint64_t aa64dfr0) int b, w, c; /* Number of breakpoints */ - brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), aa64dfr0) + 1; + brp_num = FIELD_GET(ID_AA64DFR0_EL1_BRPs, aa64dfr0) + 1; __TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required"); /* Number of watchpoints */ - wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), aa64dfr0) + 1; + wrp_num = FIELD_GET(ID_AA64DFR0_EL1_WRPs, aa64dfr0) + 1; /* Number of context aware breakpoints */ - ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_CTX_CMPs), aa64dfr0) + 1; + ctx_brp_num = FIELD_GET(ID_AA64DFR0_EL1_CTX_CMPs, aa64dfr0) + 1; pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__, brp_num, wrp_num, ctx_brp_num); diff --git a/tools/testing/selftests/kvm/arm64/external_aborts.c b/tools/testing/selftests/kvm/arm64/external_aborts.c new file mode 100644 index 000000000000..062bf84cced1 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/external_aborts.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * external_abort - Tests for userspace external abort injection + * + * Copyright (c) 2024 Google LLC + */ +#include "processor.h" +#include "test_util.h" + +#define MMIO_ADDR 0x8000000ULL +#define EXPECTED_SERROR_ISS (ESR_ELx_ISV | 0x1d1ed) + +static u64 expected_abort_pc; + +static void expect_sea_handler(struct ex_regs *regs) +{ + u64 esr = read_sysreg(esr_el1); + + GUEST_ASSERT_EQ(regs->pc, expected_abort_pc); + GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_CUR); + GUEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT); + + GUEST_DONE(); +} + +static void unexpected_dabt_handler(struct ex_regs *regs) +{ + GUEST_FAIL("Unexpected data abort at PC: %lx\n", regs->pc); +} + +static struct kvm_vm *vm_create_with_dabt_handler(struct kvm_vcpu **vcpu, void *guest_code, + handler_fn dabt_handler) +{ + struct kvm_vm *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_DABT_CUR, dabt_handler); + + virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1); + + return vm; +} + +static void vcpu_inject_sea(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_events events = {}; + + events.exception.ext_dabt_pending = true; + vcpu_events_set(vcpu, &events); +} + +static bool vcpu_has_ras(struct kvm_vcpu *vcpu) +{ + u64 pfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); + + return SYS_FIELD_GET(ID_AA64PFR0_EL1, RAS, pfr0); +} + +static bool guest_has_ras(void) +{ + return SYS_FIELD_GET(ID_AA64PFR0_EL1, RAS, read_sysreg(id_aa64pfr0_el1)); +} + +static void vcpu_inject_serror(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_events events = {}; + + events.exception.serror_pending = true; + if (vcpu_has_ras(vcpu)) { + events.exception.serror_has_esr = true; + events.exception.serror_esr = EXPECTED_SERROR_ISS; + } + + vcpu_events_set(vcpu, &events); +} + +static void __vcpu_run_expect(struct kvm_vcpu *vcpu, unsigned int cmd) +{ + struct ucall uc; + + vcpu_run(vcpu); + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + default: + if (uc.cmd == cmd) + return; + + TEST_FAIL("Unexpected ucall: %lu", uc.cmd); + } +} + +static void vcpu_run_expect_done(struct kvm_vcpu *vcpu) +{ + __vcpu_run_expect(vcpu, UCALL_DONE); +} + +static void vcpu_run_expect_sync(struct kvm_vcpu *vcpu) +{ + __vcpu_run_expect(vcpu, UCALL_SYNC); +} + +extern char test_mmio_abort_insn; + +static noinline void test_mmio_abort_guest(void) +{ + WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_abort_insn); + + asm volatile("test_mmio_abort_insn:\n\t" + "ldr x0, [%0]\n\t" + : : "r" (MMIO_ADDR) : "x0", "memory"); + + GUEST_FAIL("MMIO instruction should not retire"); +} + +/* + * Test that KVM doesn't complete MMIO emulation when userspace has made an + * external abort pending for the instruction. + */ +static void test_mmio_abort(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_abort_guest, + expect_sea_handler); + struct kvm_run *run = vcpu->run; + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO); + TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR); + TEST_ASSERT_EQ(run->mmio.len, sizeof(unsigned long)); + TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read"); + + vcpu_inject_sea(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +extern char test_mmio_nisv_insn; + +static void test_mmio_nisv_guest(void) +{ + WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_nisv_insn); + + asm volatile("test_mmio_nisv_insn:\n\t" + "ldr x0, [%0], #8\n\t" + : : "r" (MMIO_ADDR) : "x0", "memory"); + + GUEST_FAIL("MMIO instruction should not retire"); +} + +/* + * Test that the KVM_RUN ioctl fails for ESR_EL2.ISV=0 MMIO aborts if userspace + * hasn't enabled KVM_CAP_ARM_NISV_TO_USER. + */ +static void test_mmio_nisv(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest, + unexpected_dabt_handler); + + TEST_ASSERT(_vcpu_run(vcpu), "Expected nonzero return code from KVM_RUN"); + TEST_ASSERT_EQ(errno, ENOSYS); + + kvm_vm_free(vm); +} + +/* + * Test that ESR_EL2.ISV=0 MMIO aborts reach userspace and that an injected SEA + * reaches the guest. + */ +static void test_mmio_nisv_abort(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest, + expect_sea_handler); + struct kvm_run *run = vcpu->run; + + vm_enable_cap(vm, KVM_CAP_ARM_NISV_TO_USER, 1); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_NISV); + TEST_ASSERT_EQ(run->arm_nisv.fault_ipa, MMIO_ADDR); + + vcpu_inject_sea(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +static void unexpected_serror_handler(struct ex_regs *regs) +{ + GUEST_FAIL("Took unexpected SError exception"); +} + +static void test_serror_masked_guest(void) +{ + GUEST_ASSERT(read_sysreg(isr_el1) & ISR_EL1_A); + + isb(); + + GUEST_DONE(); +} + +static void test_serror_masked(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_serror_masked_guest, + unexpected_dabt_handler); + + vm_install_exception_handler(vm, VECTOR_ERROR_CURRENT, unexpected_serror_handler); + + vcpu_inject_serror(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +static void expect_serror_handler(struct ex_regs *regs) +{ + u64 esr = read_sysreg(esr_el1); + + GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_SERROR); + if (guest_has_ras()) + GUEST_ASSERT_EQ(ESR_ELx_ISS(esr), EXPECTED_SERROR_ISS); + + GUEST_DONE(); +} + +static void test_serror_guest(void) +{ + GUEST_ASSERT(read_sysreg(isr_el1) & ISR_EL1_A); + + local_serror_enable(); + isb(); + local_serror_disable(); + + GUEST_FAIL("Should've taken pending SError exception"); +} + +static void test_serror(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_serror_guest, + unexpected_dabt_handler); + + vm_install_exception_handler(vm, VECTOR_ERROR_CURRENT, expect_serror_handler); + + vcpu_inject_serror(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +static void test_serror_emulated_guest(void) +{ + GUEST_ASSERT(!(read_sysreg(isr_el1) & ISR_EL1_A)); + + local_serror_enable(); + GUEST_SYNC(0); + local_serror_disable(); + + GUEST_FAIL("Should've taken unmasked SError exception"); +} + +static void test_serror_emulated(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_serror_emulated_guest, + unexpected_dabt_handler); + + vm_install_exception_handler(vm, VECTOR_ERROR_CURRENT, expect_serror_handler); + + vcpu_run_expect_sync(vcpu); + vcpu_inject_serror(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +static void test_mmio_ease_guest(void) +{ + sysreg_clear_set_s(SYS_SCTLR2_EL1, 0, SCTLR2_EL1_EASE); + isb(); + + test_mmio_abort_guest(); +} + +/* + * Test that KVM doesn't complete MMIO emulation when userspace has made an + * external abort pending for the instruction. + */ +static void test_mmio_ease(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_ease_guest, + unexpected_dabt_handler); + struct kvm_run *run = vcpu->run; + u64 pfr1; + + pfr1 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1)); + if (!SYS_FIELD_GET(ID_AA64PFR1_EL1, DF2, pfr1)) { + pr_debug("Skipping %s\n", __func__); + return; + } + + /* + * SCTLR2_ELx.EASE changes the exception vector to the SError vector but + * doesn't further modify the exception context (e.g. ESR_ELx, FAR_ELx). + */ + vm_install_exception_handler(vm, VECTOR_ERROR_CURRENT, expect_sea_handler); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO); + TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR); + TEST_ASSERT_EQ(run->mmio.len, sizeof(unsigned long)); + TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read"); + + vcpu_inject_sea(vcpu); + vcpu_run_expect_done(vcpu); + kvm_vm_free(vm); +} + +int main(void) +{ + test_mmio_abort(); + test_mmio_nisv(); + test_mmio_nisv_abort(); + test_serror(); + test_serror_masked(); + test_serror_emulated(); + test_mmio_ease(); +} diff --git a/tools/testing/selftests/kvm/arm64/get-reg-list.c b/tools/testing/selftests/kvm/arm64/get-reg-list.c index d01798b6b3b4..011fad95dd02 100644 --- a/tools/testing/selftests/kvm/arm64/get-reg-list.c +++ b/tools/testing/selftests/kvm/arm64/get-reg-list.c @@ -15,6 +15,12 @@ #include "test_util.h" #include "processor.h" +#define SYS_REG(r) ARM64_SYS_REG(sys_reg_Op0(SYS_ ## r), \ + sys_reg_Op1(SYS_ ## r), \ + sys_reg_CRn(SYS_ ## r), \ + sys_reg_CRm(SYS_ ## r), \ + sys_reg_Op2(SYS_ ## r)) + struct feature_id_reg { __u64 reg; __u64 id_reg; @@ -22,37 +28,43 @@ struct feature_id_reg { __u64 feat_min; }; -static struct feature_id_reg feat_id_regs[] = { - { - ARM64_SYS_REG(3, 0, 2, 0, 3), /* TCR2_EL1 */ - ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */ - 0, - 1 - }, - { - ARM64_SYS_REG(3, 0, 10, 2, 2), /* PIRE0_EL1 */ - ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */ - 8, - 1 - }, - { - ARM64_SYS_REG(3, 0, 10, 2, 3), /* PIR_EL1 */ - ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */ - 8, - 1 - }, - { - ARM64_SYS_REG(3, 0, 10, 2, 4), /* POR_EL1 */ - ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */ - 16, - 1 - }, - { - ARM64_SYS_REG(3, 3, 10, 2, 4), /* POR_EL0 */ - ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */ - 16, - 1 +#define FEAT(id, f, v) \ + .id_reg = SYS_REG(id), \ + .feat_shift = id ## _ ## f ## _SHIFT, \ + .feat_min = id ## _ ## f ## _ ## v + +#define REG_FEAT(r, id, f, v) \ + { \ + .reg = SYS_REG(r), \ + FEAT(id, f, v) \ } + +static struct feature_id_reg feat_id_regs[] = { + REG_FEAT(TCR2_EL1, ID_AA64MMFR3_EL1, TCRX, IMP), + REG_FEAT(TCR2_EL2, ID_AA64MMFR3_EL1, TCRX, IMP), + REG_FEAT(PIRE0_EL1, ID_AA64MMFR3_EL1, S1PIE, IMP), + REG_FEAT(PIRE0_EL2, ID_AA64MMFR3_EL1, S1PIE, IMP), + REG_FEAT(PIR_EL1, ID_AA64MMFR3_EL1, S1PIE, IMP), + REG_FEAT(PIR_EL2, ID_AA64MMFR3_EL1, S1PIE, IMP), + REG_FEAT(POR_EL1, ID_AA64MMFR3_EL1, S1POE, IMP), + REG_FEAT(POR_EL0, ID_AA64MMFR3_EL1, S1POE, IMP), + REG_FEAT(POR_EL2, ID_AA64MMFR3_EL1, S1POE, IMP), + REG_FEAT(HCRX_EL2, ID_AA64MMFR1_EL1, HCX, IMP), + REG_FEAT(HFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HFGWTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HFGITR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HDFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HDFGWTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HAFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP), + REG_FEAT(HFGRTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2), + REG_FEAT(HFGWTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2), + REG_FEAT(HFGITR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2), + REG_FEAT(HDFGRTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2), + REG_FEAT(HDFGWTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2), + REG_FEAT(ZCR_EL2, ID_AA64PFR0_EL1, SVE, IMP), + REG_FEAT(SCTLR2_EL1, ID_AA64MMFR3_EL1, SCTLRX, IMP), + REG_FEAT(VDISR_EL2, ID_AA64PFR0_EL1, RAS, IMP), + REG_FEAT(VSESR_EL2, ID_AA64PFR0_EL1, RAS, IMP), }; bool filter_reg(__u64 reg) @@ -469,6 +481,7 @@ static __u64 base_regs[] = { ARM64_SYS_REG(3, 0, 1, 0, 0), /* SCTLR_EL1 */ ARM64_SYS_REG(3, 0, 1, 0, 1), /* ACTLR_EL1 */ ARM64_SYS_REG(3, 0, 1, 0, 2), /* CPACR_EL1 */ + KVM_ARM64_SYS_REG(SYS_SCTLR2_EL1), ARM64_SYS_REG(3, 0, 2, 0, 0), /* TTBR0_EL1 */ ARM64_SYS_REG(3, 0, 2, 0, 1), /* TTBR1_EL1 */ ARM64_SYS_REG(3, 0, 2, 0, 2), /* TCR_EL1 */ @@ -686,6 +699,62 @@ static __u64 pauth_generic_regs[] = { ARM64_SYS_REG(3, 0, 2, 3, 1), /* APGAKEYHI_EL1 */ }; +static __u64 el2_regs[] = { + SYS_REG(VPIDR_EL2), + SYS_REG(VMPIDR_EL2), + SYS_REG(SCTLR_EL2), + SYS_REG(ACTLR_EL2), + SYS_REG(HCR_EL2), + SYS_REG(MDCR_EL2), + SYS_REG(CPTR_EL2), + SYS_REG(HSTR_EL2), + SYS_REG(HFGRTR_EL2), + SYS_REG(HFGWTR_EL2), + SYS_REG(HFGITR_EL2), + SYS_REG(HACR_EL2), + SYS_REG(ZCR_EL2), + SYS_REG(HCRX_EL2), + SYS_REG(TTBR0_EL2), + SYS_REG(TTBR1_EL2), + SYS_REG(TCR_EL2), + SYS_REG(TCR2_EL2), + SYS_REG(VTTBR_EL2), + SYS_REG(VTCR_EL2), + SYS_REG(VNCR_EL2), + SYS_REG(HDFGRTR2_EL2), + SYS_REG(HDFGWTR2_EL2), + SYS_REG(HFGRTR2_EL2), + SYS_REG(HFGWTR2_EL2), + SYS_REG(HDFGRTR_EL2), + SYS_REG(HDFGWTR_EL2), + SYS_REG(HAFGRTR_EL2), + SYS_REG(HFGITR2_EL2), + SYS_REG(SPSR_EL2), + SYS_REG(ELR_EL2), + SYS_REG(AFSR0_EL2), + SYS_REG(AFSR1_EL2), + SYS_REG(ESR_EL2), + SYS_REG(FAR_EL2), + SYS_REG(HPFAR_EL2), + SYS_REG(MAIR_EL2), + SYS_REG(PIRE0_EL2), + SYS_REG(PIR_EL2), + SYS_REG(POR_EL2), + SYS_REG(AMAIR_EL2), + SYS_REG(VBAR_EL2), + SYS_REG(CONTEXTIDR_EL2), + SYS_REG(TPIDR_EL2), + SYS_REG(CNTVOFF_EL2), + SYS_REG(CNTHCTL_EL2), + SYS_REG(CNTHP_CTL_EL2), + SYS_REG(CNTHP_CVAL_EL2), + SYS_REG(CNTHV_CTL_EL2), + SYS_REG(CNTHV_CVAL_EL2), + SYS_REG(SP_EL2), + SYS_REG(VDISR_EL2), + SYS_REG(VSESR_EL2), +}; + #define BASE_SUBLIST \ { "base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), } #define VREGS_SUBLIST \ @@ -712,6 +781,14 @@ static __u64 pauth_generic_regs[] = { .regs = pauth_generic_regs, \ .regs_n = ARRAY_SIZE(pauth_generic_regs), \ } +#define EL2_SUBLIST \ + { \ + .name = "EL2", \ + .capability = KVM_CAP_ARM_EL2, \ + .feature = KVM_ARM_VCPU_HAS_EL2, \ + .regs = el2_regs, \ + .regs_n = ARRAY_SIZE(el2_regs), \ + } static struct vcpu_reg_list vregs_config = { .sublists = { @@ -761,6 +838,65 @@ static struct vcpu_reg_list pauth_pmu_config = { }, }; +static struct vcpu_reg_list el2_vregs_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + VREGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list el2_vregs_pmu_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + VREGS_SUBLIST, + PMU_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list el2_sve_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + SVE_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list el2_sve_pmu_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + SVE_SUBLIST, + PMU_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list el2_pauth_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + VREGS_SUBLIST, + PAUTH_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list el2_pauth_pmu_config = { + .sublists = { + BASE_SUBLIST, + EL2_SUBLIST, + VREGS_SUBLIST, + PAUTH_SUBLIST, + PMU_SUBLIST, + {0}, + }, +}; + struct vcpu_reg_list *vcpu_configs[] = { &vregs_config, &vregs_pmu_config, @@ -768,5 +904,12 @@ struct vcpu_reg_list *vcpu_configs[] = { &sve_pmu_config, &pauth_config, &pauth_pmu_config, + + &el2_vregs_config, + &el2_vregs_pmu_config, + &el2_sve_config, + &el2_sve_pmu_config, + &el2_pauth_config, + &el2_pauth_pmu_config, }; int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); diff --git a/tools/testing/selftests/kvm/arm64/kvm-uuid.c b/tools/testing/selftests/kvm/arm64/kvm-uuid.c new file mode 100644 index 000000000000..af9581b860f1 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/kvm-uuid.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Check that nobody has tampered with KVM's UID + +#include +#include +#include +#include + +#include "processor.h" + +/* + * Do NOT redefine these constants, or try to replace them with some + * "common" version. They are hardcoded here to detect any potential + * breakage happening in the rest of the kernel. + * + * KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 + */ +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 0xb66fb428U +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 0xe911c52eU +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 0x564bcaa9U +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3 0x743a004dU + +static void guest_code(void) +{ + struct arm_smccc_res res = {}; + + smccc_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &res); + + __GUEST_ASSERT(res.a0 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 && + res.a1 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 && + res.a2 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 && + res.a3 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3, + "Unexpected KVM-specific UID %lx %lx %lx %lx\n", res.a0, res.a1, res.a2, res.a3); + GUEST_DONE(); +} + +int main (int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + bool guest_done = false; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + while (!guest_done) { + vcpu_run(vcpu); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + break; + case UCALL_DONE: + guest_done = true; + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_PRINTF: + printf("%s", uc.buffer); + break; + default: + TEST_FAIL("Unexpected guest exit"); + } + } + + kvm_vm_free(vm); + + return 0; +} diff --git a/tools/testing/selftests/kvm/arm64/mmio_abort.c b/tools/testing/selftests/kvm/arm64/mmio_abort.c deleted file mode 100644 index 8b7a80a51b1c..000000000000 --- a/tools/testing/selftests/kvm/arm64/mmio_abort.c +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mmio_abort - Tests for userspace MMIO abort injection - * - * Copyright (c) 2024 Google LLC - */ -#include "processor.h" -#include "test_util.h" - -#define MMIO_ADDR 0x8000000ULL - -static u64 expected_abort_pc; - -static void expect_sea_handler(struct ex_regs *regs) -{ - u64 esr = read_sysreg(esr_el1); - - GUEST_ASSERT_EQ(regs->pc, expected_abort_pc); - GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_CUR); - GUEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT); - - GUEST_DONE(); -} - -static void unexpected_dabt_handler(struct ex_regs *regs) -{ - GUEST_FAIL("Unexpected data abort at PC: %lx\n", regs->pc); -} - -static struct kvm_vm *vm_create_with_dabt_handler(struct kvm_vcpu **vcpu, void *guest_code, - handler_fn dabt_handler) -{ - struct kvm_vm *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_DABT_CUR, dabt_handler); - - virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1); - - return vm; -} - -static void vcpu_inject_extabt(struct kvm_vcpu *vcpu) -{ - struct kvm_vcpu_events events = {}; - - events.exception.ext_dabt_pending = true; - vcpu_events_set(vcpu, &events); -} - -static void vcpu_run_expect_done(struct kvm_vcpu *vcpu) -{ - struct ucall uc; - - vcpu_run(vcpu); - switch (get_ucall(vcpu, &uc)) { - case UCALL_ABORT: - REPORT_GUEST_ASSERT(uc); - break; - case UCALL_DONE: - break; - default: - TEST_FAIL("Unexpected ucall: %lu", uc.cmd); - } -} - -extern char test_mmio_abort_insn; - -static void test_mmio_abort_guest(void) -{ - WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_abort_insn); - - asm volatile("test_mmio_abort_insn:\n\t" - "ldr x0, [%0]\n\t" - : : "r" (MMIO_ADDR) : "x0", "memory"); - - GUEST_FAIL("MMIO instruction should not retire"); -} - -/* - * Test that KVM doesn't complete MMIO emulation when userspace has made an - * external abort pending for the instruction. - */ -static void test_mmio_abort(void) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_abort_guest, - expect_sea_handler); - struct kvm_run *run = vcpu->run; - - vcpu_run(vcpu); - TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO); - TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR); - TEST_ASSERT_EQ(run->mmio.len, sizeof(unsigned long)); - TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read"); - - vcpu_inject_extabt(vcpu); - vcpu_run_expect_done(vcpu); - kvm_vm_free(vm); -} - -extern char test_mmio_nisv_insn; - -static void test_mmio_nisv_guest(void) -{ - WRITE_ONCE(expected_abort_pc, (u64)&test_mmio_nisv_insn); - - asm volatile("test_mmio_nisv_insn:\n\t" - "ldr x0, [%0], #8\n\t" - : : "r" (MMIO_ADDR) : "x0", "memory"); - - GUEST_FAIL("MMIO instruction should not retire"); -} - -/* - * Test that the KVM_RUN ioctl fails for ESR_EL2.ISV=0 MMIO aborts if userspace - * hasn't enabled KVM_CAP_ARM_NISV_TO_USER. - */ -static void test_mmio_nisv(void) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest, - unexpected_dabt_handler); - - TEST_ASSERT(_vcpu_run(vcpu), "Expected nonzero return code from KVM_RUN"); - TEST_ASSERT_EQ(errno, ENOSYS); - - kvm_vm_free(vm); -} - -/* - * Test that ESR_EL2.ISV=0 MMIO aborts reach userspace and that an injected SEA - * reaches the guest. - */ -static void test_mmio_nisv_abort(void) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_mmio_nisv_guest, - expect_sea_handler); - struct kvm_run *run = vcpu->run; - - vm_enable_cap(vm, KVM_CAP_ARM_NISV_TO_USER, 1); - - vcpu_run(vcpu); - TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_NISV); - TEST_ASSERT_EQ(run->arm_nisv.fault_ipa, MMIO_ADDR); - - vcpu_inject_extabt(vcpu); - vcpu_run_expect_done(vcpu); - kvm_vm_free(vm); -} - -int main(void) -{ - test_mmio_abort(); - test_mmio_nisv(); - test_mmio_nisv_abort(); -} diff --git a/tools/testing/selftests/kvm/arm64/no-vgic-v3.c b/tools/testing/selftests/kvm/arm64/no-vgic-v3.c index ebd70430c89d..f222538e6084 100644 --- a/tools/testing/selftests/kvm/arm64/no-vgic-v3.c +++ b/tools/testing/selftests/kvm/arm64/no-vgic-v3.c @@ -54,7 +54,7 @@ static void guest_code(void) * 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(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), + __GUEST_ASSERT(FIELD_GET(ID_AA64PFR0_EL1_GIC, read_sysreg(id_aa64pfr0_el1)) == 0, "GICv3 wrongly advertised"); @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) 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(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), pfr0), + __TEST_REQUIRE(FIELD_GET(ID_AA64PFR0_EL1_GIC, pfr0), "GICv3 not supported."); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/arm64/page_fault_test.c b/tools/testing/selftests/kvm/arm64/page_fault_test.c index dc6559dad9d8..4ccbd389d133 100644 --- a/tools/testing/selftests/kvm/arm64/page_fault_test.c +++ b/tools/testing/selftests/kvm/arm64/page_fault_test.c @@ -95,14 +95,14 @@ static bool guest_check_lse(void) uint64_t isar0 = read_sysreg(id_aa64isar0_el1); uint64_t atomic; - atomic = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR0_EL1_ATOMIC), isar0); + atomic = FIELD_GET(ID_AA64ISAR0_EL1_ATOMIC, isar0); return atomic >= 2; } static bool guest_check_dc_zva(void) { uint64_t dczid = read_sysreg(dczid_el0); - uint64_t dzp = FIELD_GET(ARM64_FEATURE_MASK(DCZID_EL0_DZP), dczid); + uint64_t dzp = FIELD_GET(DCZID_EL0_DZP, dczid); return dzp == 0; } @@ -195,7 +195,7 @@ static bool guest_set_ha(void) uint64_t hadbs, tcr; /* Skip if HA is not supported. */ - hadbs = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_EL1_HAFDBS), mmfr1); + hadbs = FIELD_GET(ID_AA64MMFR1_EL1_HAFDBS, mmfr1); if (hadbs == 0) return false; diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c index 8f422bfdfcb9..189321e96925 100644 --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c @@ -139,6 +139,7 @@ static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = { }; static const struct reg_ftr_bits ftr_id_aa64pfr1_el1[] = { + REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, DF2, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, CSV2_frac, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, SSBS, ID_AA64PFR1_EL1_SSBS_NI), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, BT, 0), @@ -187,6 +188,14 @@ static const struct reg_ftr_bits ftr_id_aa64mmfr2_el1[] = { REG_FTR_END, }; +static const struct reg_ftr_bits ftr_id_aa64mmfr3_el1[] = { + REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR3_EL1, S1POE, 0), + REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR3_EL1, S1PIE, 0), + REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR3_EL1, SCTLRX, 0), + REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR3_EL1, TCRX, 0), + REG_FTR_END, +}; + static const struct reg_ftr_bits ftr_id_aa64zfr0_el1[] = { REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, F64MM, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, F32MM, 0), @@ -217,6 +226,7 @@ static struct test_feature_reg test_regs[] = { TEST_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1), TEST_REG(SYS_ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1_el1), TEST_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2_el1), + TEST_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3_el1), TEST_REG(SYS_ID_AA64ZFR0_EL1, ftr_id_aa64zfr0_el1), }; @@ -233,6 +243,7 @@ static void guest_code(void) GUEST_REG_SYNC(SYS_ID_AA64MMFR0_EL1); GUEST_REG_SYNC(SYS_ID_AA64MMFR1_EL1); GUEST_REG_SYNC(SYS_ID_AA64MMFR2_EL1); + GUEST_REG_SYNC(SYS_ID_AA64MMFR3_EL1); GUEST_REG_SYNC(SYS_ID_AA64ZFR0_EL1); GUEST_REG_SYNC(SYS_CTR_EL0); GUEST_REG_SYNC(SYS_MIDR_EL1); @@ -584,8 +595,8 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu) */ val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1)); - mte = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE), val); - mte_frac = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE_frac), val); + mte = FIELD_GET(ID_AA64PFR1_EL1_MTE, val); + mte_frac = FIELD_GET(ID_AA64PFR1_EL1_MTE_frac, val); if (mte != ID_AA64PFR1_EL1_MTE_MTE2 || mte_frac != ID_AA64PFR1_EL1_MTE_frac_NI) { ksft_test_result_skip("MTE_ASYNC or MTE_ASYMM are supported, nothing to test\n"); @@ -602,7 +613,7 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu) } val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1)); - mte_frac = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE_frac), val); + mte_frac = FIELD_GET(ID_AA64PFR1_EL1_MTE_frac, val); if (mte_frac == ID_AA64PFR1_EL1_MTE_frac_NI) ksft_test_result_pass("ID_AA64PFR1_EL1.MTE_frac=0 accepted and still 0xF\n"); else @@ -764,7 +775,7 @@ int main(void) /* Check for AARCH64 only system */ val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); - el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val); + el0 = FIELD_GET(ID_AA64PFR0_EL1_EL0, val); aarch64_only = (el0 == ID_AA64PFR0_EL1_EL0_IMP); ksft_print_header(); @@ -774,8 +785,8 @@ int main(void) ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) + ARRAY_SIZE(ftr_id_aa64pfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + - ARRAY_SIZE(ftr_id_aa64zfr0_el1) - ARRAY_SIZE(test_regs) + 3 + - MPAM_IDREG_TEST + MTE_IDREG_TEST; + ARRAY_SIZE(ftr_id_aa64mmfr3_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) - + ARRAY_SIZE(test_regs) + 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST; ksft_set_plan(test_cnt); diff --git a/tools/testing/selftests/kvm/arm64/vgic_init.c b/tools/testing/selftests/kvm/arm64/vgic_init.c index b3b5fb0ff0a9..a8e0f46bc0ab 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_init.c +++ b/tools/testing/selftests/kvm/arm64/vgic_init.c @@ -9,17 +9,18 @@ #include #include +#include + #include "test_util.h" #include "kvm_util.h" #include "processor.h" #include "vgic.h" +#include "gic_v3.h" #define NR_VCPUS 4 #define REG_OFFSET(vcpu, offset) (((uint64_t)vcpu << 32) | offset) -#define GICR_TYPER 0x8 - #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) @@ -675,6 +676,44 @@ static void test_v3_its_region(void) vm_gic_destroy(&v); } +static void test_v3_nassgicap(void) +{ + struct kvm_vcpu *vcpus[NR_VCPUS]; + bool has_nassgicap; + struct vm_gic vm; + u32 typer2; + int ret; + + vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); + kvm_device_attr_get(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_TYPER2, &typer2); + has_nassgicap = typer2 & GICD_TYPER2_nASSGIcap; + + typer2 |= GICD_TYPER2_nASSGIcap; + ret = __kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_TYPER2, &typer2); + if (has_nassgicap) + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEVICE_ATTR_SET, ret)); + else + TEST_ASSERT(ret && errno == EINVAL, + "Enabled nASSGIcap even though it's unavailable"); + + typer2 &= ~GICD_TYPER2_nASSGIcap; + kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_TYPER2, &typer2); + + kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); + + typer2 ^= GICD_TYPER2_nASSGIcap; + ret = __kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_TYPER2, &typer2); + TEST_ASSERT(ret && errno == EBUSY, + "Changed nASSGIcap after initializing the VGIC"); + + vm_gic_destroy(&vm); +} + /* * Returns 0 if it's possible to create GIC device of a given type (V2 or V3). */ @@ -715,6 +754,220 @@ int test_kvm_device(uint32_t gic_dev_type) return 0; } +struct sr_def { + const char *name; + u32 encoding; +}; + +#define PACK_SR(r) \ + ((sys_reg_Op0(r) << 14) | \ + (sys_reg_Op1(r) << 11) | \ + (sys_reg_CRn(r) << 7) | \ + (sys_reg_CRm(r) << 3) | \ + (sys_reg_Op2(r))) + +#define SR(r) \ + { \ + .name = #r, \ + .encoding = r, \ + } + +static const struct sr_def sysregs_el1[] = { + SR(SYS_ICC_PMR_EL1), + SR(SYS_ICC_BPR0_EL1), + SR(SYS_ICC_AP0R0_EL1), + SR(SYS_ICC_AP0R1_EL1), + SR(SYS_ICC_AP0R2_EL1), + SR(SYS_ICC_AP0R3_EL1), + SR(SYS_ICC_AP1R0_EL1), + SR(SYS_ICC_AP1R1_EL1), + SR(SYS_ICC_AP1R2_EL1), + SR(SYS_ICC_AP1R3_EL1), + SR(SYS_ICC_BPR1_EL1), + SR(SYS_ICC_CTLR_EL1), + SR(SYS_ICC_SRE_EL1), + SR(SYS_ICC_IGRPEN0_EL1), + SR(SYS_ICC_IGRPEN1_EL1), +}; + +static const struct sr_def sysregs_el2[] = { + SR(SYS_ICH_AP0R0_EL2), + SR(SYS_ICH_AP0R1_EL2), + SR(SYS_ICH_AP0R2_EL2), + SR(SYS_ICH_AP0R3_EL2), + SR(SYS_ICH_AP1R0_EL2), + SR(SYS_ICH_AP1R1_EL2), + SR(SYS_ICH_AP1R2_EL2), + SR(SYS_ICH_AP1R3_EL2), + SR(SYS_ICH_HCR_EL2), + SR(SYS_ICC_SRE_EL2), + SR(SYS_ICH_VTR_EL2), + SR(SYS_ICH_VMCR_EL2), + SR(SYS_ICH_LR0_EL2), + SR(SYS_ICH_LR1_EL2), + SR(SYS_ICH_LR2_EL2), + SR(SYS_ICH_LR3_EL2), + SR(SYS_ICH_LR4_EL2), + SR(SYS_ICH_LR5_EL2), + SR(SYS_ICH_LR6_EL2), + SR(SYS_ICH_LR7_EL2), + SR(SYS_ICH_LR8_EL2), + SR(SYS_ICH_LR9_EL2), + SR(SYS_ICH_LR10_EL2), + SR(SYS_ICH_LR11_EL2), + SR(SYS_ICH_LR12_EL2), + SR(SYS_ICH_LR13_EL2), + SR(SYS_ICH_LR14_EL2), + SR(SYS_ICH_LR15_EL2), +}; + +static void test_sysreg_array(int gic, const struct sr_def *sr, int nr, + int (*check)(int, const struct sr_def *, const char *)) +{ + for (int i = 0; i < nr; i++) { + u64 val; + u64 attr; + int ret; + + /* Assume MPIDR_EL1.Aff*=0 */ + attr = PACK_SR(sr[i].encoding); + + /* + * The API is braindead. A register can be advertised as + * available, and yet not be readable or writable. + * ICC_APnR{1,2,3}_EL1 are examples of such non-sense, and + * ICH_APnR{1,2,3}_EL2 do follow suit for consistency. + * + * On the bright side, no known HW is implementing more than + * 5 bits of priority, so we're safe. Sort of... + */ + ret = __kvm_has_device_attr(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + attr); + TEST_ASSERT(ret == 0, "%s unavailable", sr[i].name); + + /* Check that we can write back what we read */ + ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + attr, &val); + TEST_ASSERT(ret == 0 || !check(gic, &sr[i], "read"), "%s unreadable", sr[i].name); + ret = __kvm_device_attr_set(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + attr, &val); + TEST_ASSERT(ret == 0 || !check(gic, &sr[i], "write"), "%s unwritable", sr[i].name); + } +} + +static u8 get_ctlr_pribits(int gic) +{ + int ret; + u64 val; + u8 pri; + + ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + PACK_SR(SYS_ICC_CTLR_EL1), &val); + TEST_ASSERT(ret == 0, "ICC_CTLR_EL1 unreadable"); + + pri = FIELD_GET(ICC_CTLR_EL1_PRI_BITS_MASK, val) + 1; + TEST_ASSERT(pri >= 5 && pri <= 7, "Bad pribits %d", pri); + + return pri; +} + +static int check_unaccessible_el1_regs(int gic, const struct sr_def *sr, const char *what) +{ + switch (sr->encoding) { + case SYS_ICC_AP0R1_EL1: + case SYS_ICC_AP1R1_EL1: + if (get_ctlr_pribits(gic) >= 6) + return -EINVAL; + break; + case SYS_ICC_AP0R2_EL1: + case SYS_ICC_AP0R3_EL1: + case SYS_ICC_AP1R2_EL1: + case SYS_ICC_AP1R3_EL1: + if (get_ctlr_pribits(gic) == 7) + return 0; + break; + default: + return -EINVAL; + } + + pr_info("SKIP %s for %s\n", sr->name, what); + return 0; +} + +static u8 get_vtr_pribits(int gic) +{ + int ret; + u64 val; + u8 pri; + + ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + PACK_SR(SYS_ICH_VTR_EL2), &val); + TEST_ASSERT(ret == 0, "ICH_VTR_EL2 unreadable"); + + pri = FIELD_GET(ICH_VTR_EL2_PRIbits, val) + 1; + TEST_ASSERT(pri >= 5 && pri <= 7, "Bad pribits %d", pri); + + return pri; +} + +static int check_unaccessible_el2_regs(int gic, const struct sr_def *sr, const char *what) +{ + switch (sr->encoding) { + case SYS_ICH_AP0R1_EL2: + case SYS_ICH_AP1R1_EL2: + if (get_vtr_pribits(gic) >= 6) + return -EINVAL; + break; + case SYS_ICH_AP0R2_EL2: + case SYS_ICH_AP0R3_EL2: + case SYS_ICH_AP1R2_EL2: + case SYS_ICH_AP1R3_EL2: + if (get_vtr_pribits(gic) == 7) + return -EINVAL; + break; + default: + return -EINVAL; + } + + pr_info("SKIP %s for %s\n", sr->name, what); + return 0; +} + +static void test_v3_sysregs(void) +{ + struct kvm_vcpu_init init = {}; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + u32 feat = 0; + int gic; + + if (kvm_check_cap(KVM_CAP_ARM_EL2)) + feat |= BIT(KVM_ARM_VCPU_HAS_EL2); + + vm = vm_create(1); + + vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); + init.features[0] |= feat; + + vcpu = aarch64_vcpu_add(vm, 0, &init, NULL); + TEST_ASSERT(vcpu, "Can't create a vcpu?"); + + gic = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3); + TEST_ASSERT(gic >= 0, "No GIC???"); + + kvm_device_attr_set(gic, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); + + test_sysreg_array(gic, sysregs_el1, ARRAY_SIZE(sysregs_el1), check_unaccessible_el1_regs); + if (feat) + test_sysreg_array(gic, sysregs_el2, ARRAY_SIZE(sysregs_el2), check_unaccessible_el2_regs); + else + pr_info("SKIP EL2 registers, not available\n"); + + close(gic); + kvm_vm_free(vm); +} + void run_tests(uint32_t gic_dev_type) { test_vcpus_then_vgic(gic_dev_type); @@ -730,6 +983,8 @@ void run_tests(uint32_t gic_dev_type) test_v3_last_bit_single_rdist(); test_v3_redist_ipa_range_check_at_vcpu_run(); test_v3_its_region(); + test_v3_sysregs(); + test_v3_nassgicap(); } } diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c index f4ac28d53747..a09dd423c2d7 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_irq.c +++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c @@ -620,18 +620,12 @@ 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++) { - fd[f] = eventfd(0, 0); - TEST_ASSERT(fd[f] != -1, __KVM_SYSCALL_ERROR("eventfd()", fd[f])); - } + for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) + fd[f] = kvm_new_eventfd(); for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { - struct kvm_irqfd irqfd = { - .fd = fd[f], - .gsi = i - MIN_SPI, - }; assert(i <= (uint64_t)UINT_MAX); - vm_ioctl(vm, KVM_IRQFD, &irqfd); + kvm_assign_irqfd(vm, i - MIN_SPI, fd[f]); } for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c index f16b3b27e32e..a0c4ab839155 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -441,7 +441,7 @@ static void create_vpmu_vm(void *guest_code) /* Make sure that PMUv3 support is indicated in the ID register */ dfr0 = vcpu_get_reg(vpmu_vm.vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1)); - pmuver = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), dfr0); + pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer, dfr0); TEST_ASSERT(pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP, "Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver); diff --git a/tools/testing/selftests/kvm/config b/tools/testing/selftests/kvm/config index 8835fed09e9f..96d874b239eb 100644 --- a/tools/testing/selftests/kvm/config +++ b/tools/testing/selftests/kvm/config @@ -1,5 +1,6 @@ CONFIG_KVM=y CONFIG_KVM_INTEL=y CONFIG_KVM_AMD=y +CONFIG_EVENTFD=y CONFIG_USERFAULTFD=y CONFIG_IDLE_PAGE_TRACKING=y diff --git a/tools/testing/selftests/kvm/include/arm64/processor.h b/tools/testing/selftests/kvm/include/arm64/processor.h index b0fc0f945766..255fed769a8a 100644 --- a/tools/testing/selftests/kvm/include/arm64/processor.h +++ b/tools/testing/selftests/kvm/include/arm64/processor.h @@ -254,6 +254,16 @@ static inline void local_irq_disable(void) asm volatile("msr daifset, #3" : : : "memory"); } +static inline void local_serror_enable(void) +{ + asm volatile("msr daifclr, #4" : : : "memory"); +} + +static inline void local_serror_disable(void) +{ + asm volatile("msr daifset, #4" : : : "memory"); +} + /** * struct arm_smccc_res - Result from SMC/HVC call * @a0-a3 result values from registers 0 to 3 diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index bee65ca08721..23a506d7eca3 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -18,8 +18,11 @@ #include #include +#include #include +#include + #include "kvm_util_arch.h" #include "kvm_util_types.h" #include "sparsebit.h" @@ -253,6 +256,7 @@ struct vm_guest_mode_params { }; extern const struct vm_guest_mode_params vm_guest_mode_params[]; +int __open_path_or_exit(const char *path, int flags, const char *enoent_help); int open_path_or_exit(const char *path, int flags); int open_kvm_dev_path_or_exit(void); @@ -502,6 +506,45 @@ 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) +{ + struct kvm_irqfd irqfd = { + .fd = eventfd, + .gsi = gsi, + .flags = flags, + .resamplefd = -1, + }; + + return __vm_ioctl(vm, KVM_IRQFD, &irqfd); +} + +static inline void kvm_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd, + uint32_t 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) +{ + kvm_irqfd(vm, gsi, eventfd, 0); +} + +static inline void kvm_deassign_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd) +{ + kvm_irqfd(vm, gsi, eventfd, KVM_IRQFD_FLAG_DEASSIGN); +} + +static inline int kvm_new_eventfd(void) +{ + int fd = eventfd(0, 0); + + TEST_ASSERT(fd >= 0, __KVM_SYSCALL_ERROR("eventfd()", fd)); + return fd; +} + static inline void read_stats_header(int stats_fd, struct kvm_stats_header *header) { ssize_t ret; @@ -1013,7 +1056,34 @@ struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm); void kvm_set_files_rlimit(uint32_t nr_vcpus); -void kvm_pin_this_task_to_pcpu(uint32_t pcpu); +int __pin_task_to_cpu(pthread_t task, int cpu); + +static inline void pin_task_to_cpu(pthread_t task, int cpu) +{ + int r; + + r = __pin_task_to_cpu(task, cpu); + TEST_ASSERT(!r, "Failed to set thread affinity to pCPU '%u'", cpu); +} + +static inline int pin_task_to_any_cpu(pthread_t task) +{ + int cpu = sched_getcpu(); + + pin_task_to_cpu(task, cpu); + return cpu; +} + +static inline void pin_self_to_cpu(int cpu) +{ + pin_task_to_cpu(pthread_self(), cpu); +} + +static inline int pin_self_to_any_cpu(void) +{ + return pin_task_to_any_cpu(pthread_self()); +} + void kvm_print_vcpu_pinning_help(void); void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], int nr_vcpus); diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index b11b5a53ebd5..2efb05c2f2fb 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1150,7 +1150,6 @@ do { \ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits); void kvm_init_vm_address_properties(struct kvm_vm *vm); -bool vm_is_unrestricted_guest(struct kvm_vm *vm); struct ex_regs { uint64_t rax, rcx, rdx, rbx; @@ -1325,6 +1324,11 @@ static inline bool kvm_is_forced_emulation_enabled(void) return !!get_kvm_param_integer("force_emulation_prefix"); } +static inline bool kvm_is_unrestricted_guest_enabled(void) +{ + return get_kvm_intel_param_bool("unrestricted_guest"); +} + uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, int *level); uint64_t *vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr); diff --git a/tools/testing/selftests/kvm/irqfd_test.c b/tools/testing/selftests/kvm/irqfd_test.c new file mode 100644 index 000000000000..7c301b4c7005 --- /dev/null +++ b/tools/testing/selftests/kvm/irqfd_test.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kvm_util.h" + +static struct kvm_vm *vm1; +static struct kvm_vm *vm2; +static int __eventfd; +static bool done; + +/* + * KVM de-assigns based on eventfd *and* GSI, but requires unique eventfds when + * assigning (the API isn't symmetrical). Abuse the oddity and use a per-task + * GSI base to avoid false failures due to cross-task de-assign, i.e. so that + * the secondary doesn't de-assign the primary's eventfd and cause assign to + * unexpectedly succeed on the primary. + */ +#define GSI_BASE_PRIMARY 0x20 +#define GSI_BASE_SECONDARY 0x30 + +static void juggle_eventfd_secondary(struct kvm_vm *vm, int eventfd) +{ + int r, i; + + /* + * The secondary task can encounter EBADF since the primary can close + * the eventfd at any time. And because the primary can recreate the + * eventfd, at the safe fd in the file table, the secondary can also + * encounter "unexpected" success, e.g. if the close+recreate happens + * between the first and second assignments. The secondary's role is + * mostly to antagonize KVM, not to detect bugs. + */ + for (i = 0; i < 2; i++) { + r = __kvm_irqfd(vm, GSI_BASE_SECONDARY, eventfd, 0); + TEST_ASSERT(!r || errno == EBUSY || errno == EBADF, + "Wanted success, EBUSY, or EBADF, r = %d, errno = %d", + r, errno); + + /* De-assign should succeed unless the eventfd was closed. */ + r = __kvm_irqfd(vm, GSI_BASE_SECONDARY + i, eventfd, KVM_IRQFD_FLAG_DEASSIGN); + TEST_ASSERT(!r || errno == EBADF, + "De-assign should succeed unless the fd was closed"); + } +} + +static void *secondary_irqfd_juggler(void *ign) +{ + while (!READ_ONCE(done)) { + juggle_eventfd_secondary(vm1, READ_ONCE(__eventfd)); + juggle_eventfd_secondary(vm2, READ_ONCE(__eventfd)); + } + + return NULL; +} + +static void juggle_eventfd_primary(struct kvm_vm *vm, int eventfd) +{ + int r1, r2; + + /* + * At least one of the assigns should fail. KVM disallows assigning a + * single eventfd to multiple GSIs (or VMs), so it's possible that both + * assignments can fail, too. + */ + r1 = __kvm_irqfd(vm, GSI_BASE_PRIMARY, eventfd, 0); + TEST_ASSERT(!r1 || errno == EBUSY, + "Wanted success or EBUSY, r = %d, errno = %d", r1, errno); + + r2 = __kvm_irqfd(vm, GSI_BASE_PRIMARY + 1, eventfd, 0); + TEST_ASSERT(r1 || (r2 && errno == EBUSY), + "Wanted failure (EBUSY), r1 = %d, r2 = %d, errno = %d", + r1, r2, errno); + + /* + * De-assign should always succeed, even if the corresponding assign + * failed. + */ + kvm_irqfd(vm, GSI_BASE_PRIMARY, eventfd, KVM_IRQFD_FLAG_DEASSIGN); + kvm_irqfd(vm, GSI_BASE_PRIMARY + 1, eventfd, KVM_IRQFD_FLAG_DEASSIGN); +} + +int main(int argc, char *argv[]) +{ + pthread_t racing_thread; + int r, i; + + /* Create "full" VMs, as KVM_IRQFD requires an in-kernel IRQ chip. */ + vm1 = vm_create(1); + vm2 = vm_create(1); + + WRITE_ONCE(__eventfd, kvm_new_eventfd()); + + kvm_irqfd(vm1, 10, __eventfd, 0); + + r = __kvm_irqfd(vm1, 11, __eventfd, 0); + TEST_ASSERT(r && errno == EBUSY, + "Wanted EBUSY, r = %d, errno = %d", r, errno); + + r = __kvm_irqfd(vm2, 12, __eventfd, 0); + TEST_ASSERT(r && errno == EBUSY, + "Wanted EBUSY, r = %d, errno = %d", r, errno); + + /* + * De-assign all eventfds, along with multiple eventfds that were never + * assigned. KVM's ABI is that de-assign is allowed so long as the + * eventfd itself is valid. + */ + kvm_irqfd(vm1, 11, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN); + kvm_irqfd(vm1, 12, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN); + kvm_irqfd(vm1, 13, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN); + kvm_irqfd(vm1, 14, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN); + kvm_irqfd(vm1, 10, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN); + + close(__eventfd); + + pthread_create(&racing_thread, NULL, secondary_irqfd_juggler, vm2); + + for (i = 0; i < 10000; i++) { + WRITE_ONCE(__eventfd, kvm_new_eventfd()); + + juggle_eventfd_primary(vm1, __eventfd); + juggle_eventfd_primary(vm2, __eventfd); + close(__eventfd); + } + + WRITE_ONCE(done, true); + pthread_join(racing_thread, NULL); +} diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c index 9d69904cb608..eb115123d741 100644 --- a/tools/testing/selftests/kvm/lib/arm64/processor.c +++ b/tools/testing/selftests/kvm/lib/arm64/processor.c @@ -573,15 +573,15 @@ void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k, err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®); TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd)); - gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val); + gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN4, val); *ipa4k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN4_NI, ID_AA64MMFR0_EL1_TGRAN4_52_BIT); - gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val); + gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN64, val); *ipa64k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN64_NI, ID_AA64MMFR0_EL1_TGRAN64_IMP); - gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val); + gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN16, val); *ipa16k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN16_NI, ID_AA64MMFR0_EL1_TGRAN16_52_BIT); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index a055343a7bf7..c3f5142b0a54 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -26,15 +26,27 @@ static uint32_t last_guest_seed; static int vcpu_mmap_sz(void); -int open_path_or_exit(const char *path, int flags) +int __open_path_or_exit(const char *path, int flags, const char *enoent_help) { int fd; fd = open(path, flags); - __TEST_REQUIRE(fd >= 0 || errno != ENOENT, "Cannot open %s: %s", path, strerror(errno)); - TEST_ASSERT(fd >= 0, "Failed to open '%s'", path); + if (fd < 0) + goto error; return fd; + +error: + if (errno == EACCES || errno == ENOENT) + ksft_exit_skip("- Cannot open '%s': %s. %s\n", + path, strerror(errno), + errno == EACCES ? "Root required?" : enoent_help); + TEST_FAIL("Failed to open '%s'", path); +} + +int open_path_or_exit(const char *path, int flags) +{ + return __open_path_or_exit(path, flags, ""); } /* @@ -48,7 +60,7 @@ int open_path_or_exit(const char *path, int flags) */ static int _open_kvm_dev_path_or_exit(int flags) { - return open_path_or_exit(KVM_DEV_PATH, flags); + return __open_path_or_exit(KVM_DEV_PATH, flags, "Is KVM loaded and enabled?"); } int open_kvm_dev_path_or_exit(void) @@ -64,6 +76,9 @@ static ssize_t get_module_param(const char *module_name, const char *param, ssize_t bytes_read; int fd, r; + /* Verify KVM is loaded, to provide a more helpful SKIP message. */ + close(open_kvm_dev_path_or_exit()); + r = snprintf(path, path_size, "/sys/module/%s/parameters/%s", module_name, param); TEST_ASSERT(r < path_size, @@ -605,15 +620,14 @@ struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm) return vm_vcpu_recreate(vm, 0); } -void kvm_pin_this_task_to_pcpu(uint32_t pcpu) +int __pin_task_to_cpu(pthread_t task, int cpu) { - cpu_set_t mask; - int r; + cpu_set_t cpuset; - CPU_ZERO(&mask); - CPU_SET(pcpu, &mask); - r = sched_setaffinity(0, sizeof(mask), &mask); - TEST_ASSERT(!r, "sched_setaffinity() failed for pCPU '%u'.", pcpu); + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + return pthread_setaffinity_np(task, sizeof(cpuset), &cpuset); } static uint32_t parse_pcpu(const char *cpu_str, const cpu_set_t *allowed_mask) @@ -667,7 +681,7 @@ void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], /* 2. Check if the main worker needs to be pinned. */ if (cpu) { - kvm_pin_this_task_to_pcpu(parse_pcpu(cpu, &allowed_mask)); + pin_self_to_cpu(parse_pcpu(cpu, &allowed_mask)); cpu = strtok(NULL, delim); } @@ -1716,7 +1730,18 @@ void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa) /* Create an interrupt controller chip for the specified VM. */ void vm_create_irqchip(struct kvm_vm *vm) { - vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL); + int r; + + /* + * Allocate a fully in-kernel IRQ chip by default, but fall back to a + * split model (x86 only) if that fails (KVM x86 allows compiling out + * support for KVM_CREATE_IRQCHIP). + */ + r = __vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL); + if (r && errno == ENOTTY && kvm_has_cap(KVM_CAP_SPLIT_IRQCHIP)) + vm_enable_cap(vm, KVM_CAP_SPLIT_IRQCHIP, 24); + else + TEST_ASSERT_VM_VCPU_IOCTL(!r, KVM_CREATE_IRQCHIP, r, vm); vm->has_irqchip = true; } diff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c index 313277486a1d..557c0a0a5658 100644 --- a/tools/testing/selftests/kvm/lib/memstress.c +++ b/tools/testing/selftests/kvm/lib/memstress.c @@ -265,7 +265,7 @@ static void *vcpu_thread_main(void *data) int vcpu_idx = vcpu->vcpu_idx; if (memstress_args.pin_vcpus) - kvm_pin_this_task_to_pcpu(memstress_args.vcpu_to_pcpu[vcpu_idx]); + pin_self_to_cpu(memstress_args.vcpu_to_pcpu[vcpu_idx]); WRITE_ONCE(vcpu->running, true); diff --git a/tools/testing/selftests/kvm/lib/sparsebit.c b/tools/testing/selftests/kvm/lib/sparsebit.c index cfed9d26cc71..a99188f87a38 100644 --- a/tools/testing/selftests/kvm/lib/sparsebit.c +++ b/tools/testing/selftests/kvm/lib/sparsebit.c @@ -116,7 +116,7 @@ * * + A node with all mask bits set only occurs when the last bit * described by the previous node is not equal to this nodes - * starting index - 1. All such occurences of this condition are + * starting index - 1. All such occurrences of this condition are * avoided by moving the setting of the nodes mask bits into * the previous nodes num_after setting. * @@ -592,7 +592,7 @@ static struct node *node_split(struct sparsebit *s, sparsebit_idx_t idx) * * + A node with all mask bits set only occurs when the last bit * described by the previous node is not equal to this nodes - * starting index - 1. All such occurences of this condition are + * starting index - 1. All such occurrences of this condition are * avoided by moving the setting of the nodes mask bits into * the previous nodes num_after setting. */ diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index a92dc1dad085..d4c19ac885a9 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -1264,16 +1264,6 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) return min(max_gfn, ht_gfn - 1); } -/* Returns true if kvm_intel was loaded with unrestricted_guest=1. */ -bool vm_is_unrestricted_guest(struct kvm_vm *vm) -{ - /* Ensure that a KVM vendor-specific module is loaded. */ - if (vm == NULL) - close(open_kvm_dev_path_or_exit()); - - return get_kvm_intel_param_bool("unrestricted_guest"); -} - void kvm_selftest_arch_init(void) { host_cpu_is_intel = this_cpu_is_intel(); diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c new file mode 100644 index 000000000000..8b15a13df939 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test for KVM_X86_DISABLE_EXITS_APERFMPERF + * + * Copyright (C) 2025, Google LLC. + * + * Test the ability to disable VM-exits for rdmsr of IA32_APERF and + * IA32_MPERF. When these VM-exits are disabled, reads of these MSRs + * return the host's values. + * + * Note: Requires read access to /dev/cpu//msr to read host MSRs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" +#include "test_util.h" +#include "vmx.h" + +#define NUM_ITERATIONS 10000 + +static int open_dev_msr(int cpu) +{ + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "/dev/cpu/%d/msr", cpu); + return open_path_or_exit(path, O_RDONLY); +} + +static uint64_t read_dev_msr(int msr_fd, uint32_t msr) +{ + uint64_t data; + ssize_t rc; + + rc = pread(msr_fd, &data, sizeof(data), msr); + TEST_ASSERT(rc == sizeof(data), "Read of MSR 0x%x failed", msr); + + return data; +} + +static void guest_read_aperf_mperf(void) +{ + int i; + + for (i = 0; i < NUM_ITERATIONS; i++) + GUEST_SYNC2(rdmsr(MSR_IA32_APERF), rdmsr(MSR_IA32_MPERF)); +} + +#define L2_GUEST_STACK_SIZE 64 + +static void l2_guest_code(void) +{ + guest_read_aperf_mperf(); + GUEST_DONE(); +} + +static void l1_svm_code(struct svm_test_data *svm) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + + generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + run_guest(vmcb, svm->vmcb_gpa); +} + +static void l1_vmx_code(struct vmx_pages *vmx) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + + GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); + GUEST_ASSERT_EQ(load_vmcs(vmx), true); + + prepare_vmcs(vmx, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + /* + * Enable MSR bitmaps (the bitmap itself is allocated, zeroed, and set + * in the VMCS by prepare_vmcs()), as MSR exiting mandatory on Intel. + */ + vmwrite(CPU_BASED_VM_EXEC_CONTROL, + vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); + + GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); + GUEST_ASSERT(!vmlaunch()); +} + +static void guest_code(void *nested_test_data) +{ + guest_read_aperf_mperf(); + + if (this_cpu_has(X86_FEATURE_SVM)) + l1_svm_code(nested_test_data); + else if (this_cpu_has(X86_FEATURE_VMX)) + l1_vmx_code(nested_test_data); + else + GUEST_DONE(); + + TEST_FAIL("L2 should have signaled 'done'"); +} + +static void guest_no_aperfmperf(void) +{ + uint64_t msr_val; + uint8_t vector; + + vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); + GUEST_ASSERT(vector == GP_VECTOR); + + vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); + GUEST_ASSERT(vector == GP_VECTOR); + + GUEST_DONE(); +} + +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; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int msr_fd, cpu, i; + + /* Sanity check that APERF/MPERF are unsupported by default. */ + vm = vm_create_with_one_vcpu(&vcpu, guest_no_aperfmperf); + vcpu_run(vcpu); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); + kvm_vm_free(vm); + + cpu = pin_self_to_any_cpu(); + + msr_fd = open_dev_msr(cpu); + + /* + * This test requires a non-standard VM initialization, because + * KVM_ENABLE_CAP cannot be used on a VM file descriptor after + * a VCPU has been created. + */ + vm = vm_create(1); + + TEST_REQUIRE(vm_check_cap(vm, KVM_CAP_X86_DISABLE_EXITS) & + KVM_X86_DISABLE_EXITS_APERFMPERF); + + vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, + KVM_X86_DISABLE_EXITS_APERFMPERF); + + vcpu = vm_vcpu_add(vm, 0, guest_code); + + if (!has_nested) + nested_test_data_gva = NONCANONICAL; + else if (kvm_cpu_has(X86_FEATURE_SVM)) + vcpu_alloc_svm(vm, &nested_test_data_gva); + else + vcpu_alloc_vmx(vm, &nested_test_data_gva); + + vcpu_args_set(vcpu, 1, nested_test_data_gva); + + host_aperf_before = read_dev_msr(msr_fd, MSR_IA32_APERF); + 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; + struct ucall uc; + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_DONE: + goto done; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + case UCALL_SYNC: + guest_aperf = uc.args[0]; + guest_mperf = uc.args[1]; + + host_aperf_after = read_dev_msr(msr_fd, MSR_IA32_APERF); + host_mperf_after = read_dev_msr(msr_fd, MSR_IA32_MPERF); + + TEST_ASSERT(host_aperf_before < guest_aperf, + "APERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", + host_aperf_before, guest_aperf); + TEST_ASSERT(guest_aperf < host_aperf_after, + "APERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", + guest_aperf, host_aperf_after); + TEST_ASSERT(host_mperf_before < guest_mperf, + "MPERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", + host_mperf_before, guest_mperf); + TEST_ASSERT(guest_mperf < host_mperf_after, + "MPERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", + guest_mperf, host_mperf_after); + + host_aperf_before = host_aperf_after; + host_mperf_before = host_mperf_after; + + break; + } + } + TEST_FAIL("Didn't receive UCALL_DONE\n"); +done: + kvm_vm_free(vm); + close(msr_fd); + + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/monitor_mwait_test.c b/tools/testing/selftests/kvm/x86/monitor_mwait_test.c index 390ae2d87493..0eb371c62ab8 100644 --- a/tools/testing/selftests/kvm/x86/monitor_mwait_test.c +++ b/tools/testing/selftests/kvm/x86/monitor_mwait_test.c @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) int testcase; char test[80]; + TEST_REQUIRE(this_cpu_has(X86_FEATURE_MWAIT)); TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2)); ksft_print_header(); 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 32b2794b78fe..8463a9956410 100644 --- a/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c +++ b/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c @@ -343,6 +343,12 @@ static void guest_code_permission_bitmap(void) data = test_rdmsr(MSR_GS_BASE); GUEST_ASSERT(data == MSR_GS_BASE); + /* Access the MSRs again to ensure KVM has disabled interception.*/ + data = test_rdmsr(MSR_FS_BASE); + GUEST_ASSERT(data != MSR_FS_BASE); + data = test_rdmsr(MSR_GS_BASE); + GUEST_ASSERT(data != MSR_GS_BASE); + GUEST_DONE(); } @@ -682,6 +688,8 @@ KVM_ONE_VCPU_TEST(user_msr, msr_permission_bitmap, guest_code_permission_bitmap) "Expected ucall state to be UCALL_SYNC."); vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_gs); run_guest_then_process_rdmsr(vcpu, MSR_GS_BASE); + + vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_allow); run_guest_then_process_ucall_done(vcpu); } diff --git a/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c index 3fd6eceab46f..2cae86d9d5e2 100644 --- a/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c @@ -110,7 +110,7 @@ int main(int argc, char *argv[]) struct kvm_vm *vm; TEST_REQUIRE(host_cpu_is_intel); - TEST_REQUIRE(!vm_is_unrestricted_guest(NULL)); + TEST_REQUIRE(!kvm_is_unrestricted_guest_enabled()); vm = vm_create_with_one_vcpu(&vcpu, guest_code); get_set_sigalrm_vcpu(vcpu); diff --git a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c index 287829f850f7..23909b501ac2 100644 --- a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c +++ b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c @@ -547,15 +547,9 @@ int main(int argc, char *argv[]) int irq_fd[2] = { -1, -1 }; if (do_eventfd_tests) { - irq_fd[0] = eventfd(0, 0); - irq_fd[1] = eventfd(0, 0); + irq_fd[0] = kvm_new_eventfd(); + irq_fd[1] = kvm_new_eventfd(); - /* Unexpected, but not a KVM failure */ - if (irq_fd[0] == -1 || irq_fd[1] == -1) - do_evtchn_tests = do_eventfd_tests = false; - } - - if (do_eventfd_tests) { irq_routes.info.nr = 2; irq_routes.entries[0].gsi = 32; @@ -572,15 +566,8 @@ int main(int argc, char *argv[]) vm_ioctl(vm, KVM_SET_GSI_ROUTING, &irq_routes.info); - struct kvm_irqfd ifd = { }; - - ifd.fd = irq_fd[0]; - ifd.gsi = 32; - vm_ioctl(vm, KVM_IRQFD, &ifd); - - ifd.fd = irq_fd[1]; - ifd.gsi = 33; - vm_ioctl(vm, KVM_IRQFD, &ifd); + kvm_assign_irqfd(vm, 32, irq_fd[0]); + kvm_assign_irqfd(vm, 33, irq_fd[1]); struct sigaction sa = { }; sa.sa_handler = handle_alrm; diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 18a6014920b5..b16986aa6442 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -403,11 +403,12 @@ static int audit_init_filter_exe(struct audit_filter *filter, const char *path) /* It is assume that there is not already filtering rules. */ filter->record_type = AUDIT_EXE; if (!path) { - filter->exe_len = readlink("/proc/self/exe", filter->exe, - sizeof(filter->exe) - 1); - if (filter->exe_len < 0) + int ret = readlink("/proc/self/exe", filter->exe, + sizeof(filter->exe) - 1); + if (ret < 0) return -errno; + filter->exe_len = ret; return 0; } diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index cfc571afd0eb..46d02d49835a 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -7,6 +7,7 @@ #define _GNU_SOURCE #include +#include #include #include #include diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 73729382d40f..fa0f18ec62c4 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -1832,6 +1832,46 @@ TEST_F_FORK(layout1, release_inodes) ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); } +/* + * This test checks that a rule on a directory used as a mount point does not + * grant access to the mount covering it. It is a generalization of the bind + * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points. + */ +TEST_F_FORK(layout1, covered_rule) +{ + const struct rule layer1[] = { + { + .path = dir_s3d2, + .access = LANDLOCK_ACCESS_FS_READ_DIR, + }, + {}, + }; + int ruleset_fd; + + /* Unmount to simplify FIXTURE_TEARDOWN. */ + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, umount(dir_s3d2)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + /* Creates a ruleset with the future hidden directory. */ + ruleset_fd = + create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); + ASSERT_LE(0, ruleset_fd); + + /* Covers with a new mount point. */ + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); + + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + /* Checks that access to the new mount point is denied. */ + ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); +} + enum relative_access { REL_OPEN, REL_CHDIR, diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index 7afe05e8c4d7..bd09fdaf53e0 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -2,7 +2,7 @@ CONFIG_LKDTM=y CONFIG_DEBUG_LIST=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_FORTIFY_SOURCE=y -CONFIG_GCC_PLUGIN_STACKLEAK=y +CONFIG_KSTACK_ERASE=y CONFIG_HARDENED_USERCOPY=y CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y CONFIG_INIT_ON_FREE_DEFAULT_ON=y diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index 824266982aa3..e7b23a8a05fe 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -21,6 +21,7 @@ on-fault-limit transhuge-stress pagemap_ioctl pfnmap +process_madv *.tmp* protection_keys protection_keys_32 @@ -38,9 +39,6 @@ map_fixed_noreplace write_to_hugetlbfs hmm-tests memfd_secret -hugetlb_dio -pkey_sighandler_tests_32 -pkey_sighandler_tests_64 soft-dirty split_huge_page_test ksm_tests diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index ae6f994d3add..d13b3cef2a2b 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -85,6 +85,7 @@ TEST_GEN_FILES += mseal_test TEST_GEN_FILES += on-fault-limit TEST_GEN_FILES += pagemap_ioctl TEST_GEN_FILES += pfnmap +TEST_GEN_FILES += process_madv TEST_GEN_FILES += thuge-gen TEST_GEN_FILES += transhuge-stress TEST_GEN_FILES += uffd-stress diff --git a/tools/testing/selftests/mm/config b/tools/testing/selftests/mm/config index a28baa536332..deba93379c80 100644 --- a/tools/testing/selftests/mm/config +++ b/tools/testing/selftests/mm/config @@ -8,3 +8,6 @@ CONFIG_GUP_TEST=y CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_MEM_SOFT_DIRTY=y CONFIG_ANON_VMA_NAME=y +CONFIG_FTRACE=y +CONFIG_PROFILING=y +CONFIG_UPROBES=y diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index dbbcc5eb3dce..c744c603d688 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -72,31 +72,6 @@ static int detect_thp_sizes(size_t sizes[], int max) return count; } -static void detect_huge_zeropage(void) -{ - int fd = open("/sys/kernel/mm/transparent_hugepage/use_zero_page", - O_RDONLY); - size_t enabled = 0; - char buf[15]; - int ret; - - if (fd < 0) - return; - - ret = pread(fd, buf, sizeof(buf), 0); - if (ret > 0 && ret < sizeof(buf)) { - buf[ret] = 0; - - enabled = strtoul(buf, NULL, 10); - if (enabled == 1) { - has_huge_zeropage = true; - ksft_print_msg("[INFO] huge zeropage is enabled\n"); - } - } - - close(fd); -} - static bool range_is_swapped(void *addr, size_t size) { for (; size; addr += pagesize, size -= pagesize) @@ -113,11 +88,11 @@ struct comm_pipes { static int setup_comm_pipes(struct comm_pipes *comm_pipes) { if (pipe(comm_pipes->child_ready) < 0) { - ksft_perror("pipe()"); + ksft_perror("pipe() failed"); return -errno; } if (pipe(comm_pipes->parent_ready) < 0) { - ksft_perror("pipe()"); + ksft_perror("pipe() failed"); close(comm_pipes->child_ready[0]); close(comm_pipes->child_ready[1]); return -errno; @@ -268,8 +243,10 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect, * fail because (a) harder to fix and (b) nobody really cares. * Flag them as expected failure for now. */ + ksft_print_msg("Leak from parent into child\n"); log_test_result(KSFT_XFAIL); } else { + ksft_print_msg("Leak from parent into child\n"); log_test_result(KSFT_FAIL); } close_comm_pipes: @@ -332,7 +309,7 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size, if (before_fork) { transferred = vmsplice(fds[1], &iov, 1, 0); if (transferred <= 0) { - ksft_print_msg("vmsplice() failed\n"); + ksft_perror("vmsplice() failed\n"); log_test_result(KSFT_FAIL); goto close_pipe; } @@ -397,8 +374,10 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size, * fail because (a) harder to fix and (b) nobody really cares. * Flag them as expected failure for now. */ + ksft_print_msg("Leak from child into parent\n"); log_test_result(KSFT_XFAIL); } else { + ksft_print_msg("Leak from child into parent\n"); log_test_result(KSFT_FAIL); } close_pipe: @@ -562,7 +541,7 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork) while (total < size) { cur = pread(fd, tmp + total, size - total, total); if (cur < 0) { - ksft_print_msg("pread() failed\n"); + ksft_perror("pread() failed\n"); log_test_result(KSFT_FAIL); goto quit_child; } @@ -570,10 +549,12 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork) } /* Finally, check if we read what we expected. */ - if (!memcmp(mem, tmp, size)) + if (!memcmp(mem, tmp, size)) { log_test_result(KSFT_PASS); - else + } else { + ksft_print_msg("Longtom R/W pin is not reliable\n"); log_test_result(KSFT_FAIL); + } quit_child: if (use_fork) { @@ -628,7 +609,7 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test, tmp = malloc(size); if (!tmp) { - ksft_print_msg("malloc() failed\n"); + ksft_perror("malloc() failed\n"); log_test_result(KSFT_FAIL); return; } @@ -725,10 +706,12 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test, ksft_perror("PIN_LONGTERM_TEST_READ failed"); log_test_result(KSFT_FAIL); } else { - if (!memcmp(mem, tmp, size)) + if (!memcmp(mem, tmp, size)) { log_test_result(KSFT_PASS); - else + } else { + ksft_print_msg("Longterm R/O pin is not reliable\n"); log_test_result(KSFT_FAIL); + } } ret = ioctl(gup_fd, PIN_LONGTERM_TEST_STOP); @@ -1417,10 +1400,12 @@ static void do_test_anon_thp_collapse(char *mem, size_t size, else ret = -EINVAL; - if (!ret) + if (!ret) { log_test_result(KSFT_PASS); - else + } else { + ksft_print_msg("Leak from parent into child\n"); log_test_result(KSFT_FAIL); + } close_comm_pipes: close_comm_pipes(&comm_pipes); } @@ -1528,10 +1513,12 @@ static void test_cow(char *mem, const char *smem, size_t size) memset(mem, 0xff, size); /* See if we still read the old values via the other mapping. */ - if (!memcmp(smem, old, size)) + if (!memcmp(smem, old, size)) { log_test_result(KSFT_PASS); - else + } else { + ksft_print_msg("Other mapping modified\n"); log_test_result(KSFT_FAIL); + } free(old); } @@ -1547,7 +1534,7 @@ static void test_ro_fast_pin(char *mem, const char *smem, size_t size) static void run_with_zeropage(non_anon_test_fn fn, const char *desc) { - char *mem, *smem, tmp; + char *mem, *smem; log_test_start("%s ... with shared zeropage", desc); @@ -1567,8 +1554,8 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc) } /* Read from the page to populate the shared zeropage. */ - tmp = *mem + *smem; - asm volatile("" : "+r" (tmp)); + FORCE_READ(*mem); + FORCE_READ(*smem); fn(mem, smem, pagesize); munmap: @@ -1579,7 +1566,7 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc) static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc) { - char *mem, *smem, *mmap_mem, *mmap_smem, tmp; + char *mem, *smem, *mmap_mem, *mmap_smem; size_t mmap_size; int ret; @@ -1613,13 +1600,13 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc) smem = (char *)(((uintptr_t)mmap_smem + pmdsize) & ~(pmdsize - 1)); ret = madvise(mem, pmdsize, MADV_HUGEPAGE); - if (ret != 0) { + if (ret) { ksft_perror("madvise()"); log_test_result(KSFT_FAIL); goto munmap; } - ret |= madvise(smem, pmdsize, MADV_HUGEPAGE); - if (ret != 0) { + ret = madvise(smem, pmdsize, MADV_HUGEPAGE); + if (ret) { ksft_perror("madvise()"); log_test_result(KSFT_FAIL); goto munmap; @@ -1630,8 +1617,8 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc) * the first sub-page and test if we get another sub-page populated * automatically. */ - tmp = *mem + *smem; - asm volatile("" : "+r" (tmp)); + FORCE_READ(mem); + FORCE_READ(smem); if (!pagemap_is_populated(pagemap_fd, mem + pagesize) || !pagemap_is_populated(pagemap_fd, smem + pagesize)) { ksft_test_result_skip("Did not get THPs populated\n"); @@ -1647,7 +1634,7 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc) static void run_with_memfd(non_anon_test_fn fn, const char *desc) { - char *mem, *smem, tmp; + char *mem, *smem; int fd; log_test_start("%s ... with memfd", desc); @@ -1681,8 +1668,8 @@ static void run_with_memfd(non_anon_test_fn fn, const char *desc) } /* Fault the page in. */ - tmp = *mem + *smem; - asm volatile("" : "+r" (tmp)); + FORCE_READ(mem); + FORCE_READ(smem); fn(mem, smem, pagesize); munmap: @@ -1695,7 +1682,7 @@ static void run_with_memfd(non_anon_test_fn fn, const char *desc) static void run_with_tmpfile(non_anon_test_fn fn, const char *desc) { - char *mem, *smem, tmp; + char *mem, *smem; FILE *file; int fd; @@ -1737,8 +1724,8 @@ static void run_with_tmpfile(non_anon_test_fn fn, const char *desc) } /* Fault the page in. */ - tmp = *mem + *smem; - asm volatile("" : "+r" (tmp)); + FORCE_READ(mem); + FORCE_READ(smem); fn(mem, smem, pagesize); munmap: @@ -1753,7 +1740,7 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc, size_t hugetlbsize) { int flags = MFD_HUGETLB; - char *mem, *smem, tmp; + char *mem, *smem; int fd; log_test_start("%s ... with memfd hugetlb (%zu kB)", desc, @@ -1791,8 +1778,8 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc, } /* Fault the page in. */ - tmp = *mem + *smem; - asm volatile("" : "+r" (tmp)); + FORCE_READ(mem); + FORCE_READ(smem); fn(mem, smem, hugetlbsize); munmap: @@ -1891,7 +1878,7 @@ int main(int argc, char **argv) } nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes, ARRAY_SIZE(hugetlbsizes)); - detect_huge_zeropage(); + has_huge_zeropage = detect_huge_zeropage(); ksft_set_plan(ARRAY_SIZE(anon_test_cases) * tests_per_anon_test_case() + ARRAY_SIZE(anon_thp_test_cases) * tests_per_anon_thp_test_case() + diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c index 93af3d3760f9..8dd81c0a4a5a 100644 --- a/tools/testing/selftests/mm/guard-regions.c +++ b/tools/testing/selftests/mm/guard-regions.c @@ -35,13 +35,6 @@ static volatile sig_atomic_t signal_jump_set; static sigjmp_buf signal_jmp_buf; -/* - * Ignore the checkpatch warning, we must read from x but don't want to do - * anything with it in order to trigger a read page fault. We therefore must use - * volatile to stop the compiler from optimising this away. - */ -#define FORCE_READ(x) (*(volatile typeof(x) *)x) - /* * How is the test backing the mapping being tested? */ @@ -152,7 +145,7 @@ static bool try_access_buf(char *ptr, bool write) if (write) *ptr = 'x'; else - FORCE_READ(ptr); + FORCE_READ(*ptr); } signal_jump_set = false; @@ -582,7 +575,7 @@ TEST_F(guard_regions, process_madvise) /* OK we don't have permission to do this, skip. */ if (count == -1 && errno == EPERM) - ksft_exit_skip("No process_madvise() permissions, try running as root.\n"); + SKIP(return, "No process_madvise() permissions, try running as root.\n"); /* Returns the number of bytes advised. */ ASSERT_EQ(count, 6 * page_size); diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c index 29047d2e0c49..268dadb8ce43 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -114,7 +114,15 @@ static void do_test(int fd, size_t size, enum test_type type, bool shared) } if (fallocate(fd, 0, 0, size)) { - if (size == pagesize) { + /* + * Some filesystems (eg, NFSv3) don't support + * fallocate(), report this as a skip rather than a + * test failure. + */ + if (errno == EOPNOTSUPP) { + ksft_print_msg("fallocate() not supported by filesystem\n"); + result = KSFT_SKIP; + } else if (size == pagesize) { ksft_print_msg("fallocate() failed (%s)\n", strerror(errno)); result = KSFT_FAIL; } else { diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index e74107185324..c5940c0595be 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -47,14 +47,13 @@ void write_fault_pages(void *addr, unsigned long nr_pages) void read_fault_pages(void *addr, unsigned long nr_pages) { - volatile unsigned long dummy = 0; unsigned long i; for (i = 0; i < nr_pages; i++) { - dummy += *((unsigned long *)(addr + (i * huge_page_size))); - + unsigned long *addr2 = + ((unsigned long *)(addr + (i * huge_page_size))); /* Prevent the compiler from optimizing out the entire loop: */ - asm volatile("" : "+r" (dummy)); + FORCE_READ(*addr2); } } diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 8a4d34cce36b..a18c50d51141 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -561,8 +561,6 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages, usleep(TICK); } - madvise(p, nr_hpages * hpage_pmd_size, MADV_NOHUGEPAGE); - return timeout == -1; } @@ -1190,6 +1188,11 @@ int main(int argc, char **argv) .read_ahead_kb = 0, }; + if (!thp_is_enabled()) { + printf("Transparent Hugepages not available\n"); + return KSFT_SKIP; + } + parse_test_type(argc, argv); setbuf(stdout, NULL); diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c index b61803e36d1c..d8bd1911dfc0 100644 --- a/tools/testing/selftests/mm/ksm_functional_tests.c +++ b/tools/testing/selftests/mm/ksm_functional_tests.c @@ -393,9 +393,13 @@ static void test_unmerge_uffd_wp(void) /* See if UFFD-WP is around. */ uffdio_api.api = UFFD_API; - uffdio_api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP; + uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) { - ksft_test_result_fail("UFFDIO_API failed\n"); + if (errno == EINVAL) + ksft_test_result_skip("The API version requested is not supported\n"); + else + ksft_test_result_fail("UFFDIO_API failed: %s\n", strerror(errno)); + goto close_uffd; } if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { @@ -403,6 +407,26 @@ static void test_unmerge_uffd_wp(void) goto close_uffd; } + /* + * UFFDIO_API must only be called once to enable features. + * So we close the old userfaultfd and create a new one to + * actually enable UFFD_FEATURE_PAGEFAULT_FLAG_WP. + */ + close(uffd); + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + if (uffd < 0) { + ksft_test_result_fail("__NR_userfaultfd failed\n"); + goto unmap; + } + + /* Now, enable it ("two-step handshake") */ + uffdio_api.api = UFFD_API; + uffdio_api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP; + if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) { + ksft_test_result_fail("UFFDIO_API failed: %s\n", strerror(errno)); + goto close_uffd; + } + /* Register UFFD-WP, no need for an actual handler. */ if (uffd_register(uffd, map, size, false, true, false)) { ksft_test_result_fail("UFFDIO_REGISTER_MODE_WP failed\n"); diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c index e80deac1436b..b77462b5c240 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -15,6 +15,7 @@ #include "../kselftest.h" #include #include "vm_util.h" +#include "thp_settings.h" #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" #define KSM_FP(s) (KSM_SYSFS_PATH s) @@ -527,6 +528,11 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot, unsigned long scan_time_ns; int pagemap_fd, n_normal_pages, n_huge_pages; + if (!thp_is_enabled()) { + printf("Transparent Hugepages not available\n"); + return KSFT_SKIP; + } + map_size *= MB; size_t len = map_size; diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c index bbae66fc5038..cc4253f47f10 100644 --- a/tools/testing/selftests/mm/merge.c +++ b/tools/testing/selftests/mm/merge.c @@ -2,15 +2,18 @@ #define _GNU_SOURCE #include "../kselftest_harness.h" +#include #include #include #include #include #include +#include #include #include #include #include "vm_util.h" +#include FIXTURE(merge) { @@ -23,7 +26,7 @@ FIXTURE_SETUP(merge) { self->page_size = psize(); /* Carve out PROT_NONE region to map over. */ - self->carveout = mmap(NULL, 12 * self->page_size, PROT_NONE, + self->carveout = mmap(NULL, 30 * self->page_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); ASSERT_NE(self->carveout, MAP_FAILED); /* Setup PROCMAP_QUERY interface. */ @@ -32,8 +35,13 @@ FIXTURE_SETUP(merge) FIXTURE_TEARDOWN(merge) { - ASSERT_EQ(munmap(self->carveout, 12 * self->page_size), 0); + ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); ASSERT_EQ(close_procmap(&self->procmap), 0); + /* + * Clear unconditionally, as some tests set this. It is no issue if this + * fails (KSM may be disabled for instance). + */ + prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); } TEST_F(merge, mprotect_unfaulted_left) @@ -470,7 +478,9 @@ TEST_F(merge, handle_uprobe_upon_merged_vma) ASSERT_GE(fd, 0); ASSERT_EQ(ftruncate(fd, page_size), 0); - ASSERT_EQ(read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type), 0); + if (read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type) != 0) { + SKIP(goto out, "Failed to read uprobe sysfs file, skipping"); + } memset(&attr, 0, attr_sz); attr.size = attr_sz; @@ -491,8 +501,674 @@ TEST_F(merge, handle_uprobe_upon_merged_vma) ASSERT_NE(mremap(ptr2, page_size, page_size, MREMAP_MAYMOVE | MREMAP_FIXED, ptr1), MAP_FAILED); +out: close(fd); remove(probe_file); } +TEST_F(merge, ksm_merge) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2; + int err; + + /* + * Map two R/W immediately adjacent to one another, they should + * trivially merge: + * + * |-----------|-----------| + * | R/W | R/W | + * |-----------|-----------| + * ptr ptr2 + */ + + 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, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + 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); + + /* Unmap the second half of this merged VMA. */ + ASSERT_EQ(munmap(ptr2, page_size), 0); + + /* OK, now enable global KSM merge. We clear this on test teardown. */ + err = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0); + if (err == -1) { + int errnum = errno; + + /* Only non-failure case... */ + ASSERT_EQ(errnum, EINVAL); + /* ...but indicates we should skip. */ + SKIP(return, "KSM memory merging not supported, skipping."); + } + + /* + * Now map a VMA adjacent to the existing that was just made + * VM_MERGEABLE, this should merge as well. + */ + ptr2 = mmap(&carveout[2 * page_size], page_size, + PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + 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); + + /* Now this VMA altogether. */ + ASSERT_EQ(munmap(ptr, 2 * page_size), 0); + + /* Try the same operation as before, asserting this also merges fine. */ + 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, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + 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, mremap_unfaulted_to_faulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2; + + /* + * Map two distinct areas: + * + * |-----------| |-----------| + * | unfaulted | | unfaulted | + * |-----------| |-----------| + * ptr ptr2 + */ + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + + /* Offset ptr2 further away. */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Fault in ptr: + * \ + * |-----------| / |-----------| + * | faulted | \ | unfaulted | + * |-----------| / |-----------| + * ptr \ ptr2 + */ + ptr[0] = 'x'; + + /* + * Now move ptr2 adjacent to ptr: + * + * |-----------|-----------| + * | faulted | unfaulted | + * |-----------|-----------| + * ptr ptr2 + * + * It should merge: + * + * |----------------------| + * | faulted | + * |----------------------| + * ptr + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 10 * page_size); +} + +TEST_F(merge, mremap_unfaulted_behind_faulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2; + + /* + * Map two distinct areas: + * + * |-----------| |-----------| + * | unfaulted | | unfaulted | + * |-----------| |-----------| + * ptr ptr2 + */ + ptr = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + ptr2 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + + /* Offset ptr2 further away. */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Fault in ptr: + * \ + * |-----------| / |-----------| + * | faulted | \ | unfaulted | + * |-----------| / |-----------| + * ptr \ ptr2 + */ + ptr[0] = 'x'; + + /* + * Now move ptr2 adjacent, but behind, ptr: + * + * |-----------|-----------| + * | unfaulted | faulted | + * |-----------|-----------| + * ptr2 ptr + * + * It should merge: + * + * |----------------------| + * | faulted | + * |----------------------| + * ptr2 + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size); +} + +TEST_F(merge, mremap_unfaulted_between_faulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2, *ptr3; + + /* + * Map three distinct areas: + * + * |-----------| |-----------| |-----------| + * | unfaulted | | unfaulted | | unfaulted | + * |-----------| |-----------| |-----------| + * ptr ptr2 ptr3 + */ + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr3, MAP_FAILED); + + /* Offset ptr3 further away. */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + ASSERT_NE(ptr3, MAP_FAILED); + + /* Offset ptr2 further away. */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Fault in ptr, ptr3: + * \ \ + * |-----------| / |-----------| / |-----------| + * | faulted | \ | unfaulted | \ | faulted | + * |-----------| / |-----------| / |-----------| + * ptr \ ptr2 \ ptr3 + */ + ptr[0] = 'x'; + ptr3[0] = 'x'; + + /* + * Move ptr3 back into place, leaving a place for ptr2: + * \ + * |-----------| |-----------| / |-----------| + * | faulted | | faulted | \ | unfaulted | + * |-----------| |-----------| / |-----------| + * ptr ptr3 \ ptr2 + */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); + ASSERT_NE(ptr3, MAP_FAILED); + + /* + * Finally, move ptr2 into place: + * + * |-----------|-----------|-----------| + * | faulted | unfaulted | faulted | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * It should merge, but only ptr, ptr2: + * + * |-----------------------|-----------| + * | faulted | unfaulted | + * |-----------------------|-----------| + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 10 * page_size); + + ASSERT_TRUE(find_vma_procmap(procmap, ptr3)); + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr3); + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr3 + 5 * page_size); +} + +TEST_F(merge, mremap_unfaulted_between_faulted_unfaulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2, *ptr3; + + /* + * Map three distinct areas: + * + * |-----------| |-----------| |-----------| + * | unfaulted | | unfaulted | | unfaulted | + * |-----------| |-----------| |-----------| + * ptr ptr2 ptr3 + */ + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr3, MAP_FAILED); + + /* Offset ptr3 further away. */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + ASSERT_NE(ptr3, MAP_FAILED); + + + /* Offset ptr2 further away. */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Fault in ptr: + * \ \ + * |-----------| / |-----------| / |-----------| + * | faulted | \ | unfaulted | \ | unfaulted | + * |-----------| / |-----------| / |-----------| + * ptr \ ptr2 \ ptr3 + */ + ptr[0] = 'x'; + + /* + * Move ptr3 back into place, leaving a place for ptr2: + * \ + * |-----------| |-----------| / |-----------| + * | faulted | | unfaulted | \ | unfaulted | + * |-----------| |-----------| / |-----------| + * ptr ptr3 \ ptr2 + */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); + ASSERT_NE(ptr3, MAP_FAILED); + + /* + * Finally, move ptr2 into place: + * + * |-----------|-----------|-----------| + * | faulted | unfaulted | unfaulted | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * It should merge: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 15 * page_size); +} + +TEST_F(merge, mremap_unfaulted_between_correctly_placed_faulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2; + + /* + * Map one larger area: + * + * |-----------------------------------| + * | unfaulted | + * |-----------------------------------| + */ + ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + + /* + * Fault in ptr: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + */ + ptr[0] = 'x'; + + /* + * Unmap middle: + * + * |-----------| |-----------| + * | faulted | | faulted | + * |-----------| |-----------| + * + * Now the faulted areas are compatible with each other (anon_vma the + * same, vma->vm_pgoff equal to virtual page offset). + */ + ASSERT_EQ(munmap(&ptr[5 * page_size], 5 * page_size), 0); + + /* + * Map a new area, ptr2: + * \ + * |-----------| |-----------| / |-----------| + * | faulted | | faulted | \ | unfaulted | + * |-----------| |-----------| / |-----------| + * ptr \ ptr2 + */ + ptr2 = mmap(&carveout[20 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Finally, move ptr2 into place: + * + * |-----------|-----------|-----------| + * | faulted | unfaulted | faulted | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * It should merge: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 15 * page_size); +} + +TEST_F(merge, mremap_correct_placed_faulted) +{ + unsigned int page_size = self->page_size; + char *carveout = self->carveout; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2, *ptr3; + + /* + * Map one larger area: + * + * |-----------------------------------| + * | unfaulted | + * |-----------------------------------| + */ + ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + + /* + * Fault in ptr: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + */ + ptr[0] = 'x'; + + /* + * Offset the final and middle 5 pages further away: + * \ \ + * |-----------| / |-----------| / |-----------| + * | faulted | \ | faulted | \ | faulted | + * |-----------| / |-----------| / |-----------| + * ptr \ ptr2 \ ptr3 + */ + ptr3 = &ptr[10 * page_size]; + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + ASSERT_NE(ptr3, MAP_FAILED); + ptr2 = &ptr[5 * page_size]; + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Move ptr2 into its correct place: + * \ + * |-----------|-----------| / |-----------| + * | faulted | faulted | \ | faulted | + * |-----------|-----------| / |-----------| + * ptr ptr2 \ ptr3 + * + * It should merge: + * \ + * |-----------------------| / |-----------| + * | faulted | \ | faulted | + * |-----------------------| / |-----------| + * ptr \ ptr3 + */ + + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 10 * page_size); + + /* + * Now move ptr out of place: + * \ \ + * |-----------| / |-----------| / |-----------| + * | faulted | \ | faulted | \ | faulted | + * |-----------| / |-----------| / |-----------| + * ptr2 \ ptr \ ptr3 + */ + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); + ASSERT_NE(ptr, MAP_FAILED); + + /* + * Now move ptr back into place: + * \ + * |-----------|-----------| / |-----------| + * | faulted | faulted | \ | faulted | + * |-----------|-----------| / |-----------| + * ptr ptr2 \ ptr3 + * + * It should merge: + * \ + * |-----------------------| / |-----------| + * | faulted | \ | faulted | + * |-----------------------| / |-----------| + * ptr \ ptr3 + */ + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); + ASSERT_NE(ptr, MAP_FAILED); + + 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 + 10 * page_size); + + /* + * Now move ptr out of place again: + * \ \ + * |-----------| / |-----------| / |-----------| + * | faulted | \ | faulted | \ | faulted | + * |-----------| / |-----------| / |-----------| + * ptr2 \ ptr \ ptr3 + */ + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); + ASSERT_NE(ptr, MAP_FAILED); + + /* + * Now move ptr3 back into place: + * \ + * |-----------|-----------| / |-----------| + * | faulted | faulted | \ | faulted | + * |-----------|-----------| / |-----------| + * ptr2 ptr3 \ ptr + * + * It should merge: + * \ + * |-----------------------| / |-----------| + * | faulted | \ | faulted | + * |-----------------------| / |-----------| + * ptr2 \ ptr + */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr2[5 * page_size]); + ASSERT_NE(ptr3, MAP_FAILED); + + ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size); + + /* + * Now move ptr back into place: + * + * |-----------|-----------------------| + * | faulted | faulted | + * |-----------|-----------------------| + * ptr ptr2 + * + * It should merge: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + * ptr + */ + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); + ASSERT_NE(ptr, MAP_FAILED); + + 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 + 15 * page_size); + + /* + * Now move ptr2 out of the way: + * \ + * |-----------| |-----------| / |-----------| + * | faulted | | faulted | \ | faulted | + * |-----------| |-----------| / |-----------| + * ptr ptr3 \ ptr2 + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + ASSERT_NE(ptr2, MAP_FAILED); + + /* + * Now move it back: + * + * |-----------|-----------|-----------| + * | faulted | faulted | faulted | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * It should merge: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + * ptr + */ + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); + ASSERT_NE(ptr2, MAP_FAILED); + + 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 + 15 * page_size); + + /* + * Move ptr3 out of place: + * \ + * |-----------------------| / |-----------| + * | faulted | \ | faulted | + * |-----------------------| / |-----------| + * ptr \ ptr3 + */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 1000); + ASSERT_NE(ptr3, MAP_FAILED); + + /* + * Now move it back: + * + * |-----------|-----------|-----------| + * | faulted | faulted | faulted | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * It should merge: + * + * |-----------------------------------| + * | faulted | + * |-----------------------------------| + * ptr + */ + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); + ASSERT_NE(ptr3, MAP_FAILED); + + 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 + 15 * page_size); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 1e3a595fbf01..ea945eebec2f 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -5,6 +5,8 @@ */ #include "../kselftest_harness.h" +#include "thp_settings.h" + #include #include #include @@ -14,6 +16,7 @@ #include #include #include +#include "vm_util.h" #define TWOMEG (2<<20) #define RUNTIME (20) @@ -101,15 +104,13 @@ int migrate(uint64_t *ptr, int n1, int n2) void *access_mem(void *ptr) { - volatile uint64_t y = 0; - volatile uint64_t *x = ptr; - while (1) { pthread_testcancel(); - y += *x; - - /* Prevent the compiler from optimizing out the writes to y: */ - asm volatile("" : "+r" (y)); + /* Force a read from the memory pointed to by ptr. This ensures + * the memory access actually happens and prevents the compiler + * from optimizing away this entire loop. + */ + FORCE_READ(*(uint64_t *)ptr); } return NULL; @@ -185,6 +186,9 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) uint64_t *ptr; int i; + if (!thp_is_enabled()) + SKIP(return, "Transparent Hugepages not available"); + if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); @@ -214,6 +218,9 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) uint64_t *ptr; int i; + if (!thp_is_enabled()) + SKIP(return, "Transparent Hugepages not available"); + if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c index bb84476a177f..5bd52a951cbd 100644 --- a/tools/testing/selftests/mm/mremap_test.c +++ b/tools/testing/selftests/mm/mremap_test.c @@ -5,10 +5,14 @@ #define _GNU_SOURCE #include +#include +#include #include #include #include +#include #include +#include #include #include @@ -168,6 +172,7 @@ static bool is_range_mapped(FILE *maps_fp, unsigned long start, if (first_val <= start && second_val >= end) { success = true; + fflush(maps_fp); break; } } @@ -175,6 +180,15 @@ static bool is_range_mapped(FILE *maps_fp, unsigned long start, return success; } +/* Check if [ptr, ptr + size) mapped in /proc/self/maps. */ +static bool is_ptr_mapped(FILE *maps_fp, void *ptr, unsigned long size) +{ + unsigned long start = (unsigned long)ptr; + unsigned long end = start + size; + + return is_range_mapped(maps_fp, start, end); +} + /* * Returns the start address of the mapping on success, else returns * NULL on failure. @@ -380,6 +394,602 @@ static void mremap_move_within_range(unsigned int pattern_seed, char *rand_addr) ksft_test_result_fail("%s\n", test_name); } +static bool is_multiple_vma_range_ok(unsigned int pattern_seed, + char *ptr, unsigned long page_size) +{ + int i; + + srand(pattern_seed); + for (i = 0; i <= 10; i += 2) { + int j; + char *buf = &ptr[i * page_size]; + size_t size = i == 4 ? 2 * page_size : page_size; + + for (j = 0; j < size; j++) { + char chr = rand(); + + if (chr != buf[j]) { + ksft_print_msg("page %d offset %d corrupted, expected %d got %d\n", + i, j, chr, buf[j]); + return false; + } + } + } + + return true; +} + +static void mremap_move_multiple_vmas(unsigned int pattern_seed, + unsigned long page_size, + bool dont_unmap) +{ + int mremap_flags = MREMAP_FIXED | MREMAP_MAYMOVE; + char *test_name = "mremap move multiple vmas"; + const size_t size = 11 * page_size; + bool success = true; + char *ptr, *tgt_ptr; + int i; + + if (dont_unmap) + mremap_flags |= MREMAP_DONTUNMAP; + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + + tgt_ptr = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (tgt_ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + if (munmap(tgt_ptr, 2 * size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + + /* + * Unmap so we end up with: + * + * 0 2 4 5 6 8 10 offset in buffer + * |*| |*| |*****| |*| |*| + * |*| |*| |*****| |*| |*| + * 0 1 2 3 4 5 6 pattern offset + */ + for (i = 1; i < 10; i += 2) { + if (i == 5) + continue; + + if (munmap(&ptr[i * page_size], page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + } + + srand(pattern_seed); + + /* Set up random patterns. */ + for (i = 0; i <= 10; i += 2) { + int j; + size_t size = i == 4 ? 2 * page_size : page_size; + char *buf = &ptr[i * page_size]; + + for (j = 0; j < size; j++) + buf[j] = rand(); + } + + /* First, just move the whole thing. */ + if (mremap(ptr, size, size, mremap_flags, tgt_ptr) == MAP_FAILED) { + perror("mremap"); + success = false; + goto out_unmap; + } + /* Check move was ok. */ + if (!is_multiple_vma_range_ok(pattern_seed, tgt_ptr, page_size)) { + success = false; + goto out_unmap; + } + + /* Move next to itself. */ + if (mremap(tgt_ptr, size, size, mremap_flags, + &tgt_ptr[size]) == MAP_FAILED) { + perror("mremap"); + success = false; + goto out_unmap; + } + /* Check that the move is ok. */ + if (!is_multiple_vma_range_ok(pattern_seed, &tgt_ptr[size], page_size)) { + success = false; + goto out_unmap; + } + + /* Map a range to overwrite. */ + if (mmap(tgt_ptr, size, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0) == MAP_FAILED) { + perror("mmap tgt"); + success = false; + goto out_unmap; + } + /* Move and overwrite. */ + if (mremap(&tgt_ptr[size], size, size, + mremap_flags, tgt_ptr) == MAP_FAILED) { + perror("mremap"); + success = false; + goto out_unmap; + } + /* Check that the move is ok. */ + if (!is_multiple_vma_range_ok(pattern_seed, tgt_ptr, page_size)) { + success = false; + goto out_unmap; + } + +out_unmap: + if (munmap(tgt_ptr, 2 * size)) + perror("munmap tgt"); + if (munmap(ptr, size)) + perror("munmap src"); + +out: + if (success) + ksft_test_result_pass("%s%s\n", test_name, + dont_unmap ? " [dontunnmap]" : ""); + else + ksft_test_result_fail("%s%s\n", test_name, + dont_unmap ? " [dontunnmap]" : ""); +} + +static void mremap_shrink_multiple_vmas(unsigned long page_size, + bool inplace) +{ + char *test_name = "mremap shrink multiple vmas"; + const size_t size = 10 * page_size; + bool success = true; + char *ptr, *tgt_ptr; + void *res; + int i; + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + + tgt_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (tgt_ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + if (munmap(tgt_ptr, size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + + /* + * Unmap so we end up with: + * + * 0 2 4 6 8 10 offset in buffer + * |*| |*| |*| |*| |*| |*| + * |*| |*| |*| |*| |*| |*| + */ + for (i = 1; i < 10; i += 2) { + if (munmap(&ptr[i * page_size], page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + } + + /* + * Shrink in-place across multiple VMAs and gaps so we end up with: + * + * 0 + * |*| + * |*| + */ + if (inplace) + res = mremap(ptr, size, page_size, 0); + else + res = mremap(ptr, size, page_size, MREMAP_MAYMOVE | MREMAP_FIXED, + tgt_ptr); + + if (res == MAP_FAILED) { + perror("mremap"); + success = false; + goto out_unmap; + } + +out_unmap: + if (munmap(tgt_ptr, size)) + perror("munmap tgt"); + if (munmap(ptr, size)) + perror("munmap src"); +out: + if (success) + ksft_test_result_pass("%s%s\n", test_name, + inplace ? " [inplace]" : ""); + else + ksft_test_result_fail("%s%s\n", test_name, + inplace ? " [inplace]" : ""); +} + +static void mremap_move_multiple_vmas_split(unsigned int pattern_seed, + unsigned long page_size, + bool dont_unmap) +{ + char *test_name = "mremap move multiple vmas split"; + int mremap_flags = MREMAP_FIXED | MREMAP_MAYMOVE; + const size_t size = 10 * page_size; + bool success = true; + char *ptr, *tgt_ptr; + int i; + + if (dont_unmap) + mremap_flags |= MREMAP_DONTUNMAP; + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + + tgt_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (tgt_ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out; + } + if (munmap(tgt_ptr, size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + + /* + * Unmap so we end up with: + * + * 0 1 2 3 4 5 6 7 8 9 10 offset in buffer + * |**********| |*******| + * |**********| |*******| + * 0 1 2 3 4 5 6 7 8 9 pattern offset + */ + if (munmap(&ptr[5 * page_size], page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + + /* Set up random patterns. */ + srand(pattern_seed); + for (i = 0; i < 10; i++) { + int j; + char *buf = &ptr[i * page_size]; + + if (i == 5) + continue; + + for (j = 0; j < page_size; j++) + buf[j] = rand(); + } + + /* + * Move the below: + * + * <-------------> + * 0 1 2 3 4 5 6 7 8 9 10 offset in buffer + * |**********| |*******| + * |**********| |*******| + * 0 1 2 3 4 5 6 7 8 9 pattern offset + * + * Into: + * + * 0 1 2 3 4 5 6 7 offset in buffer + * |*****| |*****| + * |*****| |*****| + * 2 3 4 5 6 7 pattern offset + */ + if (mremap(&ptr[2 * page_size], size - 3 * page_size, size - 3 * page_size, + mremap_flags, tgt_ptr) == MAP_FAILED) { + perror("mremap"); + success = false; + goto out_unmap; + } + + /* Offset into random pattern. */ + srand(pattern_seed); + for (i = 0; i < 2 * page_size; i++) + rand(); + + /* Check pattern. */ + for (i = 0; i < 7; i++) { + int j; + char *buf = &tgt_ptr[i * page_size]; + + if (i == 3) + continue; + + for (j = 0; j < page_size; j++) { + char chr = rand(); + + if (chr != buf[j]) { + ksft_print_msg("page %d offset %d corrupted, expected %d got %d\n", + i, j, chr, buf[j]); + goto out_unmap; + } + } + } + +out_unmap: + if (munmap(tgt_ptr, size)) + perror("munmap tgt"); + if (munmap(ptr, size)) + perror("munmap src"); +out: + if (success) + ksft_test_result_pass("%s%s\n", test_name, + dont_unmap ? " [dontunnmap]" : ""); + else + ksft_test_result_fail("%s%s\n", test_name, + dont_unmap ? " [dontunnmap]" : ""); +} + +#ifdef __NR_userfaultfd +static void mremap_move_multi_invalid_vmas(FILE *maps_fp, + unsigned long page_size) +{ + char *test_name = "mremap move multiple invalid vmas"; + const size_t size = 10 * page_size; + bool success = true; + char *ptr, *tgt_ptr; + int uffd, err, i; + void *res; + struct uffdio_api api = { + .api = UFFD_API, + .features = UFFD_EVENT_PAGEFAULT, + }; + + uffd = syscall(__NR_userfaultfd, O_NONBLOCK); + if (uffd == -1) { + err = errno; + perror("userfaultfd"); + if (err == EPERM) { + ksft_test_result_skip("%s - missing uffd", test_name); + return; + } + success = false; + goto out; + } + if (ioctl(uffd, UFFDIO_API, &api)) { + perror("ioctl UFFDIO_API"); + success = false; + goto out_close_uffd; + } + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out_close_uffd; + } + + tgt_ptr = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (tgt_ptr == MAP_FAILED) { + perror("mmap"); + success = false; + goto out_close_uffd; + } + if (munmap(tgt_ptr, size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + + /* + * Unmap so we end up with: + * + * 0 2 4 6 8 10 offset in buffer + * |*| |*| |*| |*| |*| + * |*| |*| |*| |*| |*| + * + * Additionally, register each with UFFD. + */ + for (i = 0; i < 10; i += 2) { + void *unmap_ptr = &ptr[(i + 1) * page_size]; + unsigned long start = (unsigned long)&ptr[i * page_size]; + struct uffdio_register reg = { + .range = { + .start = start, + .len = page_size, + }, + .mode = UFFDIO_REGISTER_MODE_MISSING, + }; + + if (ioctl(uffd, UFFDIO_REGISTER, ®) == -1) { + perror("ioctl UFFDIO_REGISTER"); + success = false; + goto out_unmap; + } + if (munmap(unmap_ptr, page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + } + + /* + * Now try to move the entire range which is invalid for multi VMA move. + * + * This will fail, and no VMA should be moved, as we check this ahead of + * time. + */ + res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr); + err = errno; + if (res != MAP_FAILED) { + fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n"); + success = false; + goto out_unmap; + } + if (err != EFAULT) { + errno = err; + perror("mrmeap() unexpected error"); + success = false; + goto out_unmap; + } + if (is_ptr_mapped(maps_fp, tgt_ptr, page_size)) { + fprintf(stderr, + "Invalid uffd-armed VMA at start of multi range moved\n"); + success = false; + goto out_unmap; + } + + /* + * Now try to move a single VMA, this should succeed as not multi VMA + * move. + */ + res = mremap(ptr, page_size, page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr); + if (res == MAP_FAILED) { + perror("mremap single invalid-multi VMA"); + success = false; + goto out_unmap; + } + + /* + * Unmap the VMA, and remap a non-uffd registered (therefore, multi VMA + * move valid) VMA at the start of ptr range. + */ + if (munmap(tgt_ptr, page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + res = mmap(ptr, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); + if (res == MAP_FAILED) { + perror("mmap"); + success = false; + goto out_unmap; + } + + /* + * Now try to move the entire range, we should succeed in moving the + * first VMA, but no others, and report a failure. + */ + res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr); + err = errno; + if (res != MAP_FAILED) { + fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n"); + success = false; + goto out_unmap; + } + if (err != EFAULT) { + errno = err; + perror("mrmeap() unexpected error"); + success = false; + goto out_unmap; + } + if (!is_ptr_mapped(maps_fp, tgt_ptr, page_size)) { + fprintf(stderr, "Valid VMA not moved\n"); + success = false; + goto out_unmap; + } + + /* + * Unmap the VMA, and map valid VMA at start of ptr range, and replace + * all existing multi-move invalid VMAs, except the last, with valid + * multi-move VMAs. + */ + if (munmap(tgt_ptr, page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + if (munmap(ptr, size - 2 * page_size)) { + perror("munmap"); + success = false; + goto out_unmap; + } + for (i = 0; i < 8; i += 2) { + res = mmap(&ptr[i * page_size], page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); + if (res == MAP_FAILED) { + perror("mmap"); + success = false; + goto out_unmap; + } + } + + /* + * Now try to move the entire range, we should succeed in moving all but + * the last VMA, and report a failure. + */ + res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr); + err = errno; + if (res != MAP_FAILED) { + fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n"); + success = false; + goto out_unmap; + } + if (err != EFAULT) { + errno = err; + perror("mrmeap() unexpected error"); + success = false; + goto out_unmap; + } + + for (i = 0; i < 10; i += 2) { + bool is_mapped = is_ptr_mapped(maps_fp, + &tgt_ptr[i * page_size], page_size); + + if (i < 8 && !is_mapped) { + fprintf(stderr, "Valid VMA not moved at %d\n", i); + success = false; + goto out_unmap; + } else if (i == 8 && is_mapped) { + fprintf(stderr, "Invalid VMA moved at %d\n", i); + success = false; + goto out_unmap; + } + } + +out_unmap: + if (munmap(tgt_ptr, size)) + perror("munmap tgt"); + if (munmap(ptr, size)) + perror("munmap src"); +out_close_uffd: + close(uffd); +out: + if (success) + ksft_test_result_pass("%s\n", test_name); + else + ksft_test_result_fail("%s\n", test_name); +} +#else +static void mremap_move_multi_invalid_vmas(FILE *maps_fp, unsigned long page_size) +{ + char *test_name = "mremap move multiple invalid vmas"; + + ksft_test_result_skip("%s - missing uffd", test_name); +} +#endif /* __NR_userfaultfd */ + /* Returns the time taken for the remap on success else returns -1. */ static long long remap_region(struct config c, unsigned int threshold_mb, char *rand_addr) @@ -721,7 +1331,7 @@ int main(int argc, char **argv) char *rand_addr; size_t rand_size; int num_expand_tests = 2; - int num_misc_tests = 2; + int num_misc_tests = 9; struct test test_cases[MAX_TEST] = {}; struct test perf_test_cases[MAX_PERF_TEST]; int page_size; @@ -844,10 +1454,17 @@ int main(int argc, char **argv) mremap_expand_merge(maps_fp, page_size); mremap_expand_merge_offset(maps_fp, page_size); - fclose(maps_fp); - mremap_move_within_range(pattern_seed, rand_addr); mremap_move_1mb_from_start(pattern_seed, rand_addr); + mremap_shrink_multiple_vmas(page_size, /* inplace= */true); + mremap_shrink_multiple_vmas(page_size, /* inplace= */false); + mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ false); + mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ true); + mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ false); + mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ true); + mremap_move_multi_invalid_vmas(maps_fp, page_size); + + fclose(maps_fp); if (run_perf_tests) { ksft_print_msg("\n%s\n", diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index b07acc86f4f0..e6face7c0166 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 + #define _GNU_SOURCE #include #include @@ -34,8 +35,8 @@ #define PAGEMAP "/proc/self/pagemap" int pagemap_fd; int uffd; -unsigned long page_size; -unsigned int hpage_size; +size_t page_size; +size_t hpage_size; const char *progname; #define LEN(region) ((region.end - region.start)/page_size) @@ -1480,6 +1481,66 @@ static void transact_test(int page_size) extra_thread_faults); } +void zeropfn_tests(void) +{ + unsigned long long mem_size; + struct page_region vec; + int i, ret; + char *mmap_mem, *mem; + + /* Test with normal memory */ + mem_size = 10 * page_size; + mem = mmap(NULL, mem_size, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0); + if (mem == MAP_FAILED) + ksft_exit_fail_msg("error nomem\n"); + + /* Touch each page to ensure it's mapped */ + for (i = 0; i < mem_size; i += page_size) + (void)((volatile char *)mem)[i]; + + ret = pagemap_ioctl(mem, mem_size, &vec, 1, 0, + (mem_size / page_size), PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret == 1 && LEN(vec) == (mem_size / page_size), + "%s all pages must have PFNZERO set\n", __func__); + + munmap(mem, mem_size); + + /* Test with huge page if user_zero_page is set to 1 */ + if (!detect_huge_zeropage()) { + ksft_test_result_skip("%s use_zero_page not supported or set to 1\n", __func__); + return; + } + + mem_size = 2 * hpage_size; + mmap_mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mmap_mem == MAP_FAILED) + ksft_exit_fail_msg("error nomem\n"); + + /* We need a THP-aligned memory area. */ + mem = (char *)(((uintptr_t)mmap_mem + hpage_size) & ~(hpage_size - 1)); + + ret = madvise(mem, hpage_size, MADV_HUGEPAGE); + if (!ret) { + FORCE_READ(*mem); + + ret = pagemap_ioctl(mem, hpage_size, &vec, 1, 0, + 0, PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret == 1 && LEN(vec) == (hpage_size / page_size), + "%s all huge pages must have PFNZERO set\n", __func__); + } else { + ksft_test_result_skip("%s huge page not supported\n", __func__); + } + + munmap(mmap_mem, mem_size); +} + int main(int __attribute__((unused)) argc, char *argv[]) { int shmid, buf_size, fd, i, ret; @@ -1494,7 +1555,7 @@ int main(int __attribute__((unused)) argc, char *argv[]) if (init_uffd()) ksft_exit_pass(); - ksft_set_plan(115); + ksft_set_plan(117); page_size = getpagesize(); hpage_size = read_pmd_pagesize(); @@ -1669,6 +1730,9 @@ int main(int __attribute__((unused)) argc, char *argv[]) /* 16. Userfaultfd tests */ userfaultfd_tests(); + /* 17. ZEROPFN tests */ + zeropfn_tests(); + close(pagemap_fd); ksft_exit_pass(); } diff --git a/tools/testing/selftests/mm/process_madv.c b/tools/testing/selftests/mm/process_madv.c new file mode 100644 index 000000000000..471cae8427f1 --- /dev/null +++ b/tools/testing/selftests/mm/process_madv.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define _GNU_SOURCE +#include "../kselftest_harness.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vm_util.h" + +#include "../pidfd/pidfd.h" + +FIXTURE(process_madvise) +{ + unsigned long page_size; + pid_t child_pid; + int remote_pidfd; + int pidfd; +}; + +FIXTURE_SETUP(process_madvise) +{ + self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); + self->pidfd = PIDFD_SELF; + self->remote_pidfd = -1; + self->child_pid = -1; +}; + +FIXTURE_TEARDOWN_PARENT(process_madvise) +{ + /* This teardown is guaranteed to run, even if tests SKIP or ASSERT */ + if (self->child_pid > 0) { + kill(self->child_pid, SIGKILL); + waitpid(self->child_pid, NULL, 0); + } + + if (self->remote_pidfd >= 0) + close(self->remote_pidfd); +} + +static ssize_t sys_process_madvise(int pidfd, const struct iovec *iovec, + size_t vlen, int advice, unsigned int flags) +{ + return syscall(__NR_process_madvise, pidfd, iovec, vlen, advice, flags); +} + +/* + * This test uses PIDFD_SELF to target the current process. The main + * goal is to verify the basic behavior of process_madvise() with + * a vector of non-contiguous memory ranges, not its cross-process + * capabilities. + */ +TEST_F(process_madvise, basic) +{ + const unsigned long pagesize = self->page_size; + const int madvise_pages = 4; + struct iovec vec[madvise_pages]; + int pidfd = self->pidfd; + ssize_t ret; + char *map; + + /* + * Create a single large mapping. We will pick pages from this + * mapping to advise on. This ensures we test non-contiguous iovecs. + */ + map = mmap(NULL, pagesize * 10, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + + /* Fill the entire region with a known pattern. */ + memset(map, 'A', pagesize * 10); + + /* + * Setup the iovec to point to 4 non-contiguous pages + * within the mapping. + */ + vec[0].iov_base = &map[0 * pagesize]; + vec[0].iov_len = pagesize; + vec[1].iov_base = &map[3 * pagesize]; + vec[1].iov_len = pagesize; + vec[2].iov_base = &map[5 * pagesize]; + vec[2].iov_len = pagesize; + vec[3].iov_base = &map[8 * pagesize]; + vec[3].iov_len = pagesize; + + ret = sys_process_madvise(pidfd, vec, madvise_pages, MADV_DONTNEED, 0); + if (ret == -1 && errno == EPERM) + SKIP(return, + "process_madvise() unsupported or permission denied, try running as root.\n"); + else if (errno == EINVAL) + SKIP(return, + "process_madvise() unsupported or parameter invalid, please check arguments.\n"); + + /* The call should succeed and report the total bytes processed. */ + ASSERT_EQ(ret, madvise_pages * pagesize); + + /* Check that advised pages are now zero. */ + for (int i = 0; i < madvise_pages; i++) { + char *advised_page = (char *)vec[i].iov_base; + + /* Content must be 0, not 'A'. */ + ASSERT_EQ(*advised_page, '\0'); + } + + /* Check that an un-advised page in between is still 'A'. */ + char *unadvised_page = &map[1 * pagesize]; + + for (int i = 0; i < pagesize; i++) + ASSERT_EQ(unadvised_page[i], 'A'); + + /* Cleanup. */ + ASSERT_EQ(munmap(map, pagesize * 10), 0); +} + +/* + * This test deterministically validates process_madvise() with MADV_COLLAPSE + * on a remote process, other advices are difficult to verify reliably. + * + * The test verifies that a memory region in a child process, + * focus on process_madv remote result, only check addresses and lengths. + * The correctness of the MADV_COLLAPSE can be found in the relevant test examples in khugepaged. + */ +TEST_F(process_madvise, remote_collapse) +{ + const unsigned long pagesize = self->page_size; + long huge_page_size; + int pipe_info[2]; + ssize_t ret; + struct iovec vec; + + struct child_info { + pid_t pid; + void *map_addr; + } info; + + huge_page_size = read_pmd_pagesize(); + if (huge_page_size <= 0) + SKIP(return, "Could not determine a valid huge page size.\n"); + + ASSERT_EQ(pipe(pipe_info), 0); + + self->child_pid = fork(); + ASSERT_NE(self->child_pid, -1); + + if (self->child_pid == 0) { + char *map; + size_t map_size = 2 * huge_page_size; + + close(pipe_info[0]); + + map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(map, MAP_FAILED); + + /* Fault in as small pages */ + for (size_t i = 0; i < map_size; i += pagesize) + map[i] = 'A'; + + /* Send info and pause */ + info.pid = getpid(); + info.map_addr = map; + ret = write(pipe_info[1], &info, sizeof(info)); + ASSERT_EQ(ret, sizeof(info)); + close(pipe_info[1]); + + pause(); + exit(0); + } + + close(pipe_info[1]); + + /* Receive child info */ + ret = read(pipe_info[0], &info, sizeof(info)); + if (ret <= 0) { + waitpid(self->child_pid, NULL, 0); + SKIP(return, "Failed to read child info from pipe.\n"); + } + ASSERT_EQ(ret, sizeof(info)); + close(pipe_info[0]); + self->child_pid = info.pid; + + self->remote_pidfd = syscall(__NR_pidfd_open, self->child_pid, 0); + ASSERT_GE(self->remote_pidfd, 0); + + vec.iov_base = info.map_addr; + vec.iov_len = huge_page_size; + + ret = sys_process_madvise(self->remote_pidfd, &vec, 1, MADV_COLLAPSE, + 0); + if (ret == -1) { + if (errno == EINVAL) + SKIP(return, "PROCESS_MADV_ADVISE is not supported.\n"); + else if (errno == EPERM) + SKIP(return, + "No process_madvise() permissions, try running as root.\n"); + return; + } + + ASSERT_EQ(ret, huge_page_size); +} + +/* + * Test process_madvise() with a pidfd for a process that has already + * exited to ensure correct error handling. + */ +TEST_F(process_madvise, exited_process_pidfd) +{ + const unsigned long pagesize = self->page_size; + struct iovec vec; + char *map; + ssize_t ret; + + map = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (map == MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + + vec.iov_base = map; + vec.iov_len = pagesize; + + /* + * Using a pidfd for a process that has already exited should fail + * with ESRCH. + */ + self->child_pid = fork(); + ASSERT_NE(self->child_pid, -1); + + if (self->child_pid == 0) + exit(0); + + self->remote_pidfd = syscall(__NR_pidfd_open, self->child_pid, 0); + ASSERT_GE(self->remote_pidfd, 0); + + /* Wait for the child to ensure it has terminated. */ + waitpid(self->child_pid, NULL, 0); + + ret = sys_process_madvise(self->remote_pidfd, &vec, 1, MADV_DONTNEED, + 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, ESRCH); +} + +/* + * Test process_madvise() with bad pidfds to ensure correct error + * handling. + */ +TEST_F(process_madvise, bad_pidfd) +{ + const unsigned long pagesize = self->page_size; + struct iovec vec; + char *map; + ssize_t ret; + + map = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (map == MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + + vec.iov_base = map; + vec.iov_len = pagesize; + + /* Using an invalid fd number (-1) should fail with EBADF. */ + ret = sys_process_madvise(-1, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBADF); + + /* + * Using a valid fd that is not a pidfd (e.g. stdin) should fail + * with EBADF. + */ + ret = sys_process_madvise(STDIN_FILENO, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBADF); +} + +/* + * Test that process_madvise() rejects vlen > UIO_MAXIOV. + * The kernel should return -EINVAL when the number of iovecs exceeds 1024. + */ +TEST_F(process_madvise, invalid_vlen) +{ + const unsigned long pagesize = self->page_size; + int pidfd = self->pidfd; + struct iovec vec; + char *map; + ssize_t ret; + + map = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (map == MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + + vec.iov_base = map; + vec.iov_len = pagesize; + + ret = sys_process_madvise(pidfd, &vec, 1025, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); + + /* Cleanup. */ + ASSERT_EQ(munmap(map, pagesize), 0); +} + +/* + * Test process_madvise() with an invalid flag value. Currently, only a flag + * value of 0 is supported. This test is reserved for the future, e.g., if + * synchronous flags are added. + */ +TEST_F(process_madvise, flag) +{ + const unsigned long pagesize = self->page_size; + unsigned int invalid_flag; + int pidfd = self->pidfd; + struct iovec vec; + char *map; + ssize_t ret; + + map = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (map == MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + + vec.iov_base = map; + vec.iov_len = pagesize; + + invalid_flag = 0x80000000; + + ret = sys_process_madvise(pidfd, &vec, 1, MADV_DONTNEED, invalid_flag); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); + + /* Cleanup. */ + ASSERT_EQ(munmap(map, pagesize), 0); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index dddd1dd8af14..471e539d82b8 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -65,6 +65,8 @@ separated by spaces: test pagemap_scan IOCTL - pfnmap tests for VM_PFNMAP handling +- process_madv + test for process_madv - cow test copy-on-write semantics - thp @@ -425,11 +427,16 @@ CATEGORY="madv_guard" run_test ./guard-regions # MADV_POPULATE_READ and MADV_POPULATE_WRITE tests CATEGORY="madv_populate" run_test ./madv_populate +# PROCESS_MADV test +CATEGORY="process_madv" run_test ./process_madv + CATEGORY="vma_merge" run_test ./merge if [ -x ./memfd_secret ] then -(echo 0 > /proc/sys/kernel/yama/ptrace_scope 2>&1) | tap_prefix +if [ -f /proc/sys/kernel/yama/ptrace_scope ]; then + (echo 0 > /proc/sys/kernel/yama/ptrace_scope 2>&1) | tap_prefix +fi CATEGORY="memfd_secret" run_test ./memfd_secret fi @@ -483,6 +490,10 @@ CATEGORY="thp" run_test ./khugepaged CATEGORY="thp" run_test ./khugepaged -s 2 +CATEGORY="thp" run_test ./khugepaged all:shmem + +CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem + CATEGORY="thp" run_test ./transhuge-stress -d 20 # Try to create XFS if not provided diff --git a/tools/testing/selftests/mm/settings b/tools/testing/selftests/mm/settings index a953c96aa16e..e2206265f67c 100644 --- a/tools/testing/selftests/mm/settings +++ b/tools/testing/selftests/mm/settings @@ -1 +1 @@ -timeout=180 +timeout=900 diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c index 8e1462ce0532..8a3f2b4b2186 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -6,8 +6,10 @@ #include #include #include + #include "../kselftest.h" #include "vm_util.h" +#include "thp_settings.h" #define PAGEMAP_FILE_PATH "/proc/self/pagemap" #define TEST_ITERATIONS 10000 @@ -78,8 +80,13 @@ static void test_hugepage(int pagemap_fd, int pagesize) { char *map; int i, ret; - size_t hpage_len = read_pmd_pagesize(); + if (!thp_is_enabled()) { + ksft_test_result_skip("Transparent Hugepages not available\n"); + return; + } + + size_t hpage_len = read_pmd_pagesize(); if (!hpage_len) ksft_exit_fail_msg("Reading PMD pagesize failed"); diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index aa7400ed0e99..44a3f8a58806 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -31,6 +31,7 @@ uint64_t pmd_pagesize; #define INPUT_MAX 80 #define PID_FMT "%d,0x%lx,0x%lx,%d" +#define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d" #define PFN_MASK ((1UL<<55)-1) @@ -398,7 +399,6 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd, char **addr) { size_t i; - int dummy = 0; unsigned char buf[1024]; srand(time(NULL)); @@ -439,9 +439,11 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd, } madvise(*addr, fd_size, MADV_HUGEPAGE); - for (size_t i = 0; i < fd_size; i++) - dummy += *(*addr + i); - asm volatile("" : "+r" (dummy)); + for (size_t i = 0; i < fd_size; i++) { + char *addr2 = *addr + i; + + FORCE_READ(*addr2); + } if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) { ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n"); @@ -483,7 +485,7 @@ void split_thp_in_pagecache_to_order_at(size_t fd_size, const char *fs_loc, write_debugfs(PID_FMT, getpid(), (uint64_t)addr, (uint64_t)addr + fd_size, order); else - write_debugfs(PID_FMT, getpid(), (uint64_t)addr, + write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)addr, (uint64_t)addr + fd_size, order, offset); for (i = 0; i < fd_size; i++) diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/thp_settings.c index ad872af1c81a..bad60ac52874 100644 --- a/tools/testing/selftests/mm/thp_settings.c +++ b/tools/testing/selftests/mm/thp_settings.c @@ -381,3 +381,14 @@ unsigned long thp_shmem_supported_orders(void) { return __thp_supported_orders(true); } + +bool thp_is_enabled(void) +{ + if (access(THP_SYSFS, F_OK) != 0) + return false; + + int mode = thp_read_string("enabled", thp_enabled_strings); + + /* THP is considered enabled if it's either "always" or "madvise" */ + return mode == 1 || mode == 3; +} diff --git a/tools/testing/selftests/mm/thp_settings.h b/tools/testing/selftests/mm/thp_settings.h index fc131d23d593..6c07f70beee9 100644 --- a/tools/testing/selftests/mm/thp_settings.h +++ b/tools/testing/selftests/mm/thp_settings.h @@ -84,4 +84,6 @@ void thp_set_read_ahead_path(char *path); unsigned long thp_supported_orders(void); unsigned long thp_shmem_supported_orders(void); +bool thp_is_enabled(void); + #endif /* __THP_SETTINGS_H__ */ diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c index 95b6f043a3cb..8e2b08dc5762 100644 --- a/tools/testing/selftests/mm/thuge-gen.c +++ b/tools/testing/selftests/mm/thuge-gen.c @@ -77,38 +77,18 @@ void show(unsigned long ps) system(buf); } -unsigned long thuge_read_sysfs(int warn, char *fmt, ...) -{ - char *line = NULL; - size_t linelen = 0; - char buf[100]; - FILE *f; - va_list ap; - unsigned long val = 0; - - va_start(ap, fmt); - vsnprintf(buf, sizeof buf, fmt, ap); - va_end(ap); - - f = fopen(buf, "r"); - if (!f) { - if (warn) - ksft_print_msg("missing %s\n", buf); - return 0; - } - if (getline(&line, &linelen, f) > 0) { - sscanf(line, "%lu", &val); - } - fclose(f); - free(line); - return val; -} - unsigned long read_free(unsigned long ps) { - return thuge_read_sysfs(ps != getpagesize(), - "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", - ps >> 10); + unsigned long val = 0; + char buf[100]; + + snprintf(buf, sizeof(buf), + "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", + ps >> 10); + if (read_sysfs(buf, &val) && ps != getpagesize()) + ksft_print_msg("missing %s\n", buf); + + return val; } void test_mmap(unsigned long size, unsigned flags) @@ -173,6 +153,7 @@ void test_shmget(unsigned long size, unsigned flags) void find_pagesizes(void) { unsigned long largest = getpagesize(); + unsigned long shmmax_val = 0; int i; glob_t g; @@ -195,7 +176,8 @@ void find_pagesizes(void) } globfree(&g); - if (thuge_read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) + read_sysfs("/proc/sys/kernel/shmmax", &shmmax_val); + if (shmmax_val < NUM_PAGES * largest) ksft_exit_fail_msg("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES); diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index c73fd5d455c8..50501b38e34e 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -1027,6 +1027,9 @@ static void uffd_poison_handle_fault( do_uffdio_poison(uffd, offset); } +/* Make sure to cover odd/even, and minimum duplications */ +#define UFFD_POISON_TEST_NPAGES 4 + static void uffd_poison_test(uffd_test_args_t *targs) { pthread_t uffd_mon; @@ -1034,12 +1037,17 @@ static void uffd_poison_test(uffd_test_args_t *targs) struct uffd_args args = { 0 }; struct sigaction act = { 0 }; unsigned long nr_sigbus = 0; - unsigned long nr; + unsigned long nr, poison_pages = UFFD_POISON_TEST_NPAGES; + + if (nr_pages < poison_pages) { + uffd_test_skip("Too few pages for POISON test"); + return; + } fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); - uffd_register_poison(uffd, area_dst, nr_pages * page_size); - memset(area_src, 0, nr_pages * page_size); + uffd_register_poison(uffd, area_dst, poison_pages * page_size); + memset(area_src, 0, poison_pages * page_size); args.handle_fault = uffd_poison_handle_fault; if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) @@ -1051,7 +1059,7 @@ static void uffd_poison_test(uffd_test_args_t *targs) if (sigaction(SIGBUS, &act, 0)) err("sigaction"); - for (nr = 0; nr < nr_pages; ++nr) { + for (nr = 0; nr < poison_pages; ++nr) { unsigned long offset = nr * page_size; const char *bytes = (const char *) area_dst + offset; const char *i; @@ -1078,9 +1086,9 @@ static void uffd_poison_test(uffd_test_args_t *targs) if (pthread_join(uffd_mon, NULL)) err("pthread_join()"); - if (nr_sigbus != nr_pages / 2) + if (nr_sigbus != poison_pages / 2) err("expected to receive %lu SIGBUS, actually received %lu", - nr_pages / 2, nr_sigbus); + poison_pages / 2, nr_sigbus); uffd_test_pass(); } diff --git a/tools/testing/selftests/mm/virtual_address_range.c b/tools/testing/selftests/mm/virtual_address_range.c index b380e102b22f..169dbd692bf5 100644 --- a/tools/testing/selftests/mm/virtual_address_range.c +++ b/tools/testing/selftests/mm/virtual_address_range.c @@ -77,8 +77,11 @@ static void validate_addr(char *ptr, int high_addr) { unsigned long addr = (unsigned long) ptr; - if (high_addr && addr < HIGH_ADDR_MARK) - ksft_exit_fail_msg("Bad address %lx\n", addr); + if (high_addr) { + if (addr < HIGH_ADDR_MARK) + ksft_exit_fail_msg("Bad address %lx\n", addr); + return; + } if (addr > HIGH_ADDR_MARK) ksft_exit_fail_msg("Bad address %lx\n", addr); diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 5492e3f784df..9dafa7669ef9 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -524,3 +524,34 @@ int read_sysfs(const char *file_path, unsigned long *val) return 0; } + +void *sys_mremap(void *old_address, unsigned long old_size, + unsigned long new_size, int flags, void *new_address) +{ + return (void *)syscall(__NR_mremap, (unsigned long)old_address, + old_size, new_size, flags, + (unsigned long)new_address); +} + +bool detect_huge_zeropage(void) +{ + int fd = open("/sys/kernel/mm/transparent_hugepage/use_zero_page", + O_RDONLY); + bool enabled = 0; + char buf[15]; + int ret; + + if (fd < 0) + return 0; + + ret = pread(fd, buf, sizeof(buf), 0); + if (ret > 0 && ret < sizeof(buf)) { + buf[ret] = 0; + + if (strtoul(buf, NULL, 10) == 1) + enabled = 1; + } + + close(fd); + return enabled; +} diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index b8136d12a0f8..b55d1809debc 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -18,6 +18,13 @@ #define PM_SWAP BIT_ULL(62) #define PM_PRESENT BIT_ULL(63) +/* + * Ignore the checkpatch warning, we must read from x but don't want to do + * anything with it in order to trigger a read page fault. We therefore must use + * volatile to stop the compiler from optimising this away. + */ +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x)) + extern unsigned int __page_size; extern unsigned int __page_shift; @@ -44,6 +51,8 @@ static inline unsigned int pshift(void) return __page_shift; } +bool detect_huge_zeropage(void); + /* * Plan 9 FS has bugs (at least on QEMU) where certain operations fail with * ENOENT on unlinked files. See @@ -117,6 +126,9 @@ static inline void log_test_result(int result) ksft_test_result_report(result, "%s\n", test_name); } +void *sys_mremap(void *old_address, unsigned long old_size, + unsigned long new_size, int flags, void *new_address); + /* * On ppc64 this will only work with radix 2M hugepage size */ diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c index b1e4618399be..a688871a98eb 100644 --- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c +++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c @@ -107,6 +107,26 @@ #endif #endif +#ifndef __NR_open_tree_attr + #if defined __alpha__ + #define __NR_open_tree_attr 577 + #elif defined _MIPS_SIM + #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ + #define __NR_open_tree_attr (467 + 4000) + #endif + #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ + #define __NR_open_tree_attr (467 + 6000) + #endif + #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ + #define __NR_open_tree_attr (467 + 5000) + #endif + #elif defined __ia64__ + #define __NR_open_tree_attr (467 + 1024) + #else + #define __NR_open_tree_attr 467 + #endif +#endif + #ifndef MOUNT_ATTR_IDMAP #define MOUNT_ATTR_IDMAP 0x00100000 #endif @@ -121,6 +141,12 @@ static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flag return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); } +static inline int sys_open_tree_attr(int dfd, const char *path, unsigned int flags, + struct mount_attr *attr, size_t size) +{ + return syscall(__NR_open_tree_attr, dfd, path, flags, attr, size); +} + static ssize_t write_nointr(int fd, const void *buf, size_t count) { ssize_t ret; @@ -1222,6 +1248,12 @@ TEST_F(mount_setattr_idmapped, attached_mount_inside_current_mount_namespace) attr.userns_fd = get_userns_fd(0, 10000, 10000); ASSERT_GE(attr.userns_fd, 0); ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + /* + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way + * to bypass this mount_setattr() restriction. + */ + ASSERT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + ASSERT_EQ(close(attr.userns_fd), 0); ASSERT_EQ(close(open_tree_fd), 0); } @@ -1255,6 +1287,12 @@ TEST_F(mount_setattr_idmapped, attached_mount_outside_current_mount_namespace) ASSERT_GE(attr.userns_fd, 0); ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + /* + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way + * to bypass this mount_setattr() restriction. + */ + ASSERT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + ASSERT_EQ(close(attr.userns_fd), 0); ASSERT_EQ(close(open_tree_fd), 0); } @@ -1321,6 +1359,19 @@ TEST_F(mount_setattr_idmapped, detached_mount_outside_current_mount_namespace) ASSERT_EQ(close(open_tree_fd), 0); } +static bool expected_uid_gid(int dfd, const char *path, int flags, + uid_t expected_uid, gid_t expected_gid) +{ + int ret; + struct stat st; + + ret = fstatat(dfd, path, &st, flags); + if (ret < 0) + return false; + + return st.st_uid == expected_uid && st.st_gid == expected_gid; +} + /** * Validate that currently changing the idmapping of an idmapped mount fails. */ @@ -1331,6 +1382,8 @@ TEST_F(mount_setattr_idmapped, change_idmapping) .attr_set = MOUNT_ATTR_IDMAP, }; + ASSERT_TRUE(expected_uid_gid(-EBADF, "/mnt/D", 0, 0, 0)); + if (!mount_setattr_supported()) SKIP(return, "mount_setattr syscall not supported"); @@ -1348,27 +1401,25 @@ TEST_F(mount_setattr_idmapped, change_idmapping) AT_EMPTY_PATH, &attr, sizeof(attr)), 0); ASSERT_EQ(close(attr.userns_fd), 0); + EXPECT_FALSE(expected_uid_gid(open_tree_fd, ".", 0, 0, 0)); + EXPECT_TRUE(expected_uid_gid(open_tree_fd, ".", 0, 10000, 10000)); + /* Change idmapping on a detached mount that is already idmapped. */ attr.userns_fd = get_userns_fd(0, 20000, 10000); ASSERT_GE(attr.userns_fd, 0); ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + /* + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way + * to bypass this mount_setattr() restriction. + */ + EXPECT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); + EXPECT_FALSE(expected_uid_gid(open_tree_fd, ".", 0, 20000, 20000)); + EXPECT_TRUE(expected_uid_gid(open_tree_fd, ".", 0, 10000, 10000)); + ASSERT_EQ(close(attr.userns_fd), 0); ASSERT_EQ(close(open_tree_fd), 0); } -static bool expected_uid_gid(int dfd, const char *path, int flags, - uid_t expected_uid, gid_t expected_gid) -{ - int ret; - struct stat st; - - ret = fstatat(dfd, path, &st, flags); - if (ret < 0) - return false; - - return st.st_uid == expected_uid && st.st_gid == expected_gid; -} - TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid) { int open_tree_fd = -EBADF; diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 532bb732bc6d..47c293c2962f 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -34,6 +34,7 @@ reuseport_bpf_numa reuseport_dualstack rxtimestamp sctp_hello +scm_inq scm_pidfd scm_rights sk_bind_sendto_listen @@ -50,6 +51,7 @@ tap tcp_fastopen_backup_key tcp_inq tcp_mmap +tfo timestamping tls toeplitz diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index ab996bd22a5f..2b31d4a93ad7 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -41,6 +41,7 @@ TEST_PROGS += netns-name.sh TEST_PROGS += link_netns.py TEST_PROGS += nl_netdev.py TEST_PROGS += rtnetlink.py +TEST_PROGS += rtnetlink_notification.sh TEST_PROGS += srv6_end_dt46_l3vpn_test.sh TEST_PROGS += srv6_end_dt4_l3vpn_test.sh TEST_PROGS += srv6_end_dt6_l3vpn_test.sh @@ -62,6 +63,7 @@ TEST_PROGS += ip_local_port_range.sh TEST_PROGS += rps_default_mask.sh TEST_PROGS += big_tcp.sh TEST_PROGS += netns-sysctl.sh +TEST_PROGS += netdev-l2addr.sh TEST_PROGS_EXTENDED := toeplitz_client.sh toeplitz.sh xfrm_policy_add_speed.sh TEST_GEN_FILES = socket nettest TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any @@ -97,8 +99,10 @@ TEST_GEN_PROGS += bind_wildcard TEST_GEN_PROGS += bind_timewait TEST_PROGS += test_vxlan_mdb.sh TEST_PROGS += test_bridge_neigh_suppress.sh +TEST_PROGS += test_vxlan_nh.sh TEST_PROGS += test_vxlan_nolocalbypass.sh TEST_PROGS += test_bridge_backup_port.sh +TEST_PROGS += test_neigh.sh TEST_PROGS += fdb_flush.sh fdb_notify.sh TEST_PROGS += fq_band_pktlimit.sh TEST_PROGS += vlan_hw_filter.sh @@ -110,6 +114,11 @@ TEST_GEN_PROGS += proc_net_pktgen TEST_PROGS += lwt_dst_cache_ref_loop.sh TEST_PROGS += skf_net_off.sh TEST_GEN_FILES += skf_net_off +TEST_GEN_FILES += tfo +TEST_PROGS += tfo_passive.sh +TEST_PROGS += broadcast_ether_dst.sh +TEST_PROGS += broadcast_pmtu.sh +TEST_PROGS += ipv6_force_forwarding.sh # YNL files, must be before "include ..lib.mk" YNL_GEN_FILES := busy_poller netlink-dumps diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 50584479540b..a4b61c6d0290 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -1,4 +1,4 @@ CFLAGS += $(KHDR_INCLUDES) -TEST_GEN_PROGS := diag_uid msg_oob scm_pidfd scm_rights unix_connect +TEST_GEN_PROGS := diag_uid msg_oob scm_inq scm_pidfd scm_rights unix_connect include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/msg_oob.c b/tools/testing/selftests/net/af_unix/msg_oob.c index 3ed3882a93b8..b5f474969917 100644 --- a/tools/testing/selftests/net/af_unix/msg_oob.c +++ b/tools/testing/selftests/net/af_unix/msg_oob.c @@ -210,7 +210,7 @@ static void __sendpair(struct __test_metadata *_metadata, static void __recvpair(struct __test_metadata *_metadata, FIXTURE_DATA(msg_oob) *self, const char *expected_buf, int expected_len, - int buf_len, int flags) + int buf_len, int flags, bool is_sender) { int i, ret[2], recv_errno[2], expected_errno = 0; char recv_buf[2][BUF_SZ] = {}; @@ -221,7 +221,9 @@ static void __recvpair(struct __test_metadata *_metadata, errno = 0; for (i = 0; i < 2; i++) { - ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags); + int index = is_sender ? i * 2 : i * 2 + 1; + + ret[i] = recv(self->fd[index], recv_buf[i], buf_len, flags); recv_errno[i] = errno; } @@ -308,6 +310,20 @@ static void __siocatmarkpair(struct __test_metadata *_metadata, ASSERT_EQ(answ[0], answ[1]); } +static void __resetpair(struct __test_metadata *_metadata, + FIXTURE_DATA(msg_oob) *self, + const FIXTURE_VARIANT(msg_oob) *variant, + bool reset) +{ + int i; + + for (i = 0; i < 2; i++) + close(self->fd[i * 2 + 1]); + + __recvpair(_metadata, self, "", reset ? -ECONNRESET : 0, 1, + variant->peek ? MSG_PEEK : 0, true); +} + #define sendpair(buf, len, flags) \ __sendpair(_metadata, self, buf, len, flags) @@ -316,9 +332,10 @@ static void __siocatmarkpair(struct __test_metadata *_metadata, if (variant->peek) \ __recvpair(_metadata, self, \ expected_buf, expected_len, \ - buf_len, (flags) | MSG_PEEK); \ + buf_len, (flags) | MSG_PEEK, false); \ __recvpair(_metadata, self, \ - expected_buf, expected_len, buf_len, flags); \ + expected_buf, expected_len, \ + buf_len, flags, false); \ } while (0) #define epollpair(oob_remaining) \ @@ -330,6 +347,9 @@ static void __siocatmarkpair(struct __test_metadata *_metadata, #define setinlinepair() \ __setinlinepair(_metadata, self) +#define resetpair(reset) \ + __resetpair(_metadata, self, variant, reset) + #define tcp_incompliant \ for (self->tcp_compliant = false; \ self->tcp_compliant == false; \ @@ -344,6 +364,21 @@ TEST_F(msg_oob, non_oob) recvpair("", -EINVAL, 1, MSG_OOB); epollpair(false); siocatmarkpair(false); + + resetpair(true); +} + +TEST_F(msg_oob, non_oob_no_reset) +{ + sendpair("x", 1, 0); + epollpair(false); + siocatmarkpair(false); + + recvpair("x", 1, 1, 0); + epollpair(false); + siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, oob) @@ -355,6 +390,19 @@ TEST_F(msg_oob, oob) recvpair("x", 1, 1, MSG_OOB); epollpair(false); siocatmarkpair(true); + + tcp_incompliant { + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ + } +} + +TEST_F(msg_oob, oob_reset) +{ + sendpair("x", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); + + resetpair(true); } TEST_F(msg_oob, oob_drop) @@ -370,6 +418,8 @@ TEST_F(msg_oob, oob_drop) recvpair("", -EINVAL, 1, MSG_OOB); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, oob_ahead) @@ -385,6 +435,10 @@ TEST_F(msg_oob, oob_ahead) recvpair("hell", 4, 4, 0); epollpair(false); siocatmarkpair(true); + + tcp_incompliant { + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ + } } TEST_F(msg_oob, oob_break) @@ -403,6 +457,8 @@ TEST_F(msg_oob, oob_break) recvpair("", -EAGAIN, 1, 0); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, oob_ahead_break) @@ -426,6 +482,8 @@ TEST_F(msg_oob, oob_ahead_break) recvpair("world", 5, 5, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, oob_break_drop) @@ -449,6 +507,8 @@ TEST_F(msg_oob, oob_break_drop) recvpair("", -EINVAL, 1, MSG_OOB); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, ex_oob_break) @@ -476,6 +536,8 @@ TEST_F(msg_oob, ex_oob_break) recvpair("ld", 2, 2, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, ex_oob_drop) @@ -498,6 +560,8 @@ TEST_F(msg_oob, ex_oob_drop) epollpair(false); siocatmarkpair(true); } + + resetpair(false); } TEST_F(msg_oob, ex_oob_drop_2) @@ -523,6 +587,8 @@ TEST_F(msg_oob, ex_oob_drop_2) epollpair(false); siocatmarkpair(true); } + + resetpair(false); } TEST_F(msg_oob, ex_oob_oob) @@ -546,6 +612,54 @@ TEST_F(msg_oob, ex_oob_oob) recvpair("", -EINVAL, 1, MSG_OOB); epollpair(false); siocatmarkpair(false); + + resetpair(false); +} + +TEST_F(msg_oob, ex_oob_ex_oob) +{ + sendpair("x", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); + + recvpair("x", 1, 1, MSG_OOB); + epollpair(false); + siocatmarkpair(true); + + sendpair("y", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); + + recvpair("y", 1, 1, MSG_OOB); + epollpair(false); + siocatmarkpair(true); + + tcp_incompliant { + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ + } +} + +TEST_F(msg_oob, ex_oob_ex_oob_oob) +{ + sendpair("x", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); + + recvpair("x", 1, 1, MSG_OOB); + epollpair(false); + siocatmarkpair(true); + + sendpair("y", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); + + recvpair("y", 1, 1, MSG_OOB); + epollpair(false); + siocatmarkpair(true); + + sendpair("z", 1, MSG_OOB); + epollpair(true); + siocatmarkpair(true); } TEST_F(msg_oob, ex_oob_ahead_break) @@ -576,6 +690,10 @@ TEST_F(msg_oob, ex_oob_ahead_break) recvpair("d", 1, 1, MSG_OOB); epollpair(false); siocatmarkpair(true); + + tcp_incompliant { + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ + } } TEST_F(msg_oob, ex_oob_siocatmark) @@ -595,6 +713,8 @@ TEST_F(msg_oob, ex_oob_siocatmark) recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ epollpair(true); siocatmarkpair(false); + + resetpair(true); } TEST_F(msg_oob, inline_oob) @@ -612,6 +732,8 @@ TEST_F(msg_oob, inline_oob) recvpair("x", 1, 1, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, inline_oob_break) @@ -633,6 +755,8 @@ TEST_F(msg_oob, inline_oob_break) recvpair("o", 1, 1, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, inline_oob_ahead_break) @@ -661,6 +785,8 @@ TEST_F(msg_oob, inline_oob_ahead_break) epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, inline_ex_oob_break) @@ -686,6 +812,8 @@ TEST_F(msg_oob, inline_ex_oob_break) recvpair("rld", 3, 3, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, inline_ex_oob_no_drop) @@ -707,6 +835,8 @@ TEST_F(msg_oob, inline_ex_oob_no_drop) recvpair("y", 1, 1, 0); epollpair(false); siocatmarkpair(false); + + resetpair(false); } TEST_F(msg_oob, inline_ex_oob_drop) @@ -731,6 +861,8 @@ TEST_F(msg_oob, inline_ex_oob_drop) epollpair(false); siocatmarkpair(false); } + + resetpair(false); } TEST_F(msg_oob, inline_ex_oob_siocatmark) @@ -752,6 +884,8 @@ TEST_F(msg_oob, inline_ex_oob_siocatmark) recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ epollpair(true); siocatmarkpair(false); + + resetpair(true); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/af_unix/scm_inq.c b/tools/testing/selftests/net/af_unix/scm_inq.c new file mode 100644 index 000000000000..9d22561e7b8f --- /dev/null +++ b/tools/testing/selftests/net/af_unix/scm_inq.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025 Google LLC */ + +#include +#include +#include +#include + +#include "../../kselftest_harness.h" + +#define NR_CHUNKS 100 +#define MSG_LEN 256 + +struct scm_inq { + struct cmsghdr cmsghdr; + int inq; +}; + +FIXTURE(scm_inq) +{ + int fd[2]; +}; + +FIXTURE_VARIANT(scm_inq) +{ + int type; +}; + +FIXTURE_VARIANT_ADD(scm_inq, stream) +{ + .type = SOCK_STREAM, +}; + +FIXTURE_VARIANT_ADD(scm_inq, dgram) +{ + .type = SOCK_DGRAM, +}; + +FIXTURE_VARIANT_ADD(scm_inq, seqpacket) +{ + .type = SOCK_SEQPACKET, +}; + +FIXTURE_SETUP(scm_inq) +{ + int err; + + err = socketpair(AF_UNIX, variant->type | SOCK_NONBLOCK, 0, self->fd); + ASSERT_EQ(0, err); +} + +FIXTURE_TEARDOWN(scm_inq) +{ + close(self->fd[0]); + close(self->fd[1]); +} + +static void send_chunks(struct __test_metadata *_metadata, + FIXTURE_DATA(scm_inq) *self) +{ + char buf[MSG_LEN] = {}; + int i, ret; + + for (i = 0; i < NR_CHUNKS; i++) { + ret = send(self->fd[0], buf, sizeof(buf), 0); + ASSERT_EQ(sizeof(buf), ret); + } +} + +static void recv_chunks(struct __test_metadata *_metadata, + FIXTURE_DATA(scm_inq) *self) +{ + struct msghdr msg = {}; + struct iovec iov = {}; + struct scm_inq cmsg; + char buf[MSG_LEN]; + int i, ret; + int inq; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsg; + msg.msg_controllen = CMSG_SPACE(sizeof(cmsg.inq)); + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + for (i = 0; i < NR_CHUNKS; i++) { + memset(buf, 0, sizeof(buf)); + memset(&cmsg, 0, sizeof(cmsg)); + + ret = recvmsg(self->fd[1], &msg, 0); + ASSERT_EQ(MSG_LEN, ret); + ASSERT_NE(NULL, CMSG_FIRSTHDR(&msg)); + ASSERT_EQ(CMSG_LEN(sizeof(cmsg.inq)), cmsg.cmsghdr.cmsg_len); + ASSERT_EQ(SOL_SOCKET, cmsg.cmsghdr.cmsg_level); + ASSERT_EQ(SCM_INQ, cmsg.cmsghdr.cmsg_type); + + ret = ioctl(self->fd[1], SIOCINQ, &inq); + ASSERT_EQ(0, ret); + ASSERT_EQ(cmsg.inq, inq); + } +} + +TEST_F(scm_inq, basic) +{ + int err, inq; + + err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int)); + if (variant->type != SOCK_STREAM) { + ASSERT_EQ(-ENOPROTOOPT, -errno); + return; + } + + ASSERT_EQ(0, err); + + err = ioctl(self->fd[1], SIOCINQ, &inq); + ASSERT_EQ(0, err); + ASSERT_EQ(0, inq); + + send_chunks(_metadata, self); + recv_chunks(_metadata, self); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/af_unix/scm_pidfd.c b/tools/testing/selftests/net/af_unix/scm_pidfd.c index 7e534594167e..37e034874034 100644 --- a/tools/testing/selftests/net/af_unix/scm_pidfd.c +++ b/tools/testing/selftests/net/af_unix/scm_pidfd.c @@ -15,6 +15,7 @@ #include #include +#include "../../pidfd/pidfd.h" #include "../../kselftest_harness.h" #define clean_errno() (errno == 0 ? "None" : strerror(errno)) @@ -26,6 +27,8 @@ #define SCM_PIDFD 0x04 #endif +#define CHILD_EXIT_CODE_OK 123 + static void child_die() { exit(1); @@ -126,16 +129,65 @@ static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen) return result; } +struct cmsg_data { + struct ucred *ucred; + int *pidfd; +}; + +static int parse_cmsg(struct msghdr *msg, struct cmsg_data *res) +{ + struct cmsghdr *cmsg; + int data = 0; + + if (msg->msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { + log_err("recvmsg: truncated"); + return 1; + } + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_PIDFD) { + if (cmsg->cmsg_len < sizeof(*res->pidfd)) { + log_err("CMSG parse: SCM_PIDFD wrong len"); + return 1; + } + + res->pidfd = (void *)CMSG_DATA(cmsg); + } + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + if (cmsg->cmsg_len < sizeof(*res->ucred)) { + log_err("CMSG parse: SCM_CREDENTIALS wrong len"); + return 1; + } + + res->ucred = (void *)CMSG_DATA(cmsg); + } + } + + if (!res->pidfd) { + log_err("CMSG parse: SCM_PIDFD not found"); + return 1; + } + + if (!res->ucred) { + log_err("CMSG parse: SCM_CREDENTIALS not found"); + return 1; + } + + return 0; +} + static int cmsg_check(int fd) { struct msghdr msg = { 0 }; - struct cmsghdr *cmsg; + struct cmsg_data res; struct iovec iov; - struct ucred *ucred = NULL; int data = 0; char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))] = { 0 }; - int *pidfd = NULL; pid_t parent_pid; int err; @@ -158,53 +210,99 @@ static int cmsg_check(int fd) return 1; } - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_PIDFD) { - if (cmsg->cmsg_len < sizeof(*pidfd)) { - log_err("CMSG parse: SCM_PIDFD wrong len"); - return 1; - } - - pidfd = (void *)CMSG_DATA(cmsg); - } - - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS) { - if (cmsg->cmsg_len < sizeof(*ucred)) { - log_err("CMSG parse: SCM_CREDENTIALS wrong len"); - return 1; - } - - ucred = (void *)CMSG_DATA(cmsg); - } - } - /* send(pfd, "x", sizeof(char), 0) */ if (data != 'x') { log_err("recvmsg: data corruption"); return 1; } - if (!pidfd) { - log_err("CMSG parse: SCM_PIDFD not found"); - return 1; - } - - if (!ucred) { - log_err("CMSG parse: SCM_CREDENTIALS not found"); + if (parse_cmsg(&msg, &res)) { + log_err("CMSG parse: parse_cmsg() failed"); return 1; } /* pidfd from SCM_PIDFD should point to the parent process PID */ parent_pid = - get_pid_from_fdinfo_file(*pidfd, "Pid:", sizeof("Pid:") - 1); + get_pid_from_fdinfo_file(*res.pidfd, "Pid:", sizeof("Pid:") - 1); if (parent_pid != getppid()) { log_err("wrong SCM_PIDFD %d != %d", parent_pid, getppid()); + close(*res.pidfd); return 1; } + close(*res.pidfd); + return 0; +} + +static int cmsg_check_dead(int fd, int expected_pid) +{ + int err; + struct msghdr msg = { 0 }; + struct cmsg_data res; + struct iovec iov; + int data = 0; + char control[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int))] = { 0 }; + pid_t client_pid; + struct pidfd_info info = { + .mask = PIDFD_INFO_EXIT, + }; + + iov.iov_base = &data; + iov.iov_len = sizeof(data); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + err = recvmsg(fd, &msg, 0); + if (err < 0) { + log_err("recvmsg"); + return 1; + } + + if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { + log_err("recvmsg: truncated"); + return 1; + } + + /* send(cfd, "y", sizeof(char), 0) */ + if (data != 'y') { + log_err("recvmsg: data corruption"); + return 1; + } + + if (parse_cmsg(&msg, &res)) { + log_err("CMSG parse: parse_cmsg() failed"); + return 1; + } + + /* + * pidfd from SCM_PIDFD should point to the client_pid. + * Let's read exit information and check if it's what + * we expect to see. + */ + if (ioctl(*res.pidfd, PIDFD_GET_INFO, &info)) { + log_err("%s: ioctl(PIDFD_GET_INFO) failed", __func__); + close(*res.pidfd); + return 1; + } + + if (!(info.mask & PIDFD_INFO_EXIT)) { + log_err("%s: No exit information from ioctl(PIDFD_GET_INFO)", __func__); + close(*res.pidfd); + return 1; + } + + err = WIFEXITED(info.exit_code) ? WEXITSTATUS(info.exit_code) : 1; + if (err != CHILD_EXIT_CODE_OK) { + log_err("%s: wrong exit_code %d != %d", __func__, err, CHILD_EXIT_CODE_OK); + close(*res.pidfd); + return 1; + } + + close(*res.pidfd); return 0; } @@ -291,6 +389,24 @@ static void fill_sockaddr(struct sock_addr *addr, bool abstract) memcpy(sun_path_buf, addr->sock_name, strlen(addr->sock_name)); } +static int sk_enable_cred_pass(int sk) +{ + int on = 0; + + on = 1; + if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { + log_err("Failed to set SO_PASSCRED"); + return 1; + } + + if (setsockopt(sk, SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) { + log_err("Failed to set SO_PASSPIDFD"); + return 1; + } + + return 0; +} + static void client(FIXTURE_DATA(scm_pidfd) *self, const FIXTURE_VARIANT(scm_pidfd) *variant) { @@ -299,7 +415,6 @@ static void client(FIXTURE_DATA(scm_pidfd) *self, struct ucred peer_cred; int peer_pidfd; pid_t peer_pid; - int on = 0; cfd = socket(AF_UNIX, variant->type, 0); if (cfd < 0) { @@ -322,14 +437,8 @@ static void client(FIXTURE_DATA(scm_pidfd) *self, child_die(); } - on = 1; - if (setsockopt(cfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { - log_err("Failed to set SO_PASSCRED"); - child_die(); - } - - if (setsockopt(cfd, SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) { - log_err("Failed to set SO_PASSPIDFD"); + if (sk_enable_cred_pass(cfd)) { + log_err("sk_enable_cred_pass() failed"); child_die(); } @@ -340,6 +449,12 @@ static void client(FIXTURE_DATA(scm_pidfd) *self, child_die(); } + /* send something to the parent so it can receive SCM_PIDFD too and validate it */ + if (send(cfd, "y", sizeof(char), 0) == -1) { + log_err("Failed to send(cfd, \"y\", sizeof(char), 0)"); + child_die(); + } + /* skip further for SOCK_DGRAM as it's not applicable */ if (variant->type == SOCK_DGRAM) return; @@ -398,7 +513,13 @@ TEST_F(scm_pidfd, test) close(self->server); close(self->startup_pipe[0]); client(self, variant); - exit(0); + + /* + * It's a bit unusual, but in case of success we return non-zero + * exit code (CHILD_EXIT_CODE_OK) and then we expect to read it + * from ioctl(PIDFD_GET_INFO) in cmsg_check_dead(). + */ + exit(CHILD_EXIT_CODE_OK); } close(self->startup_pipe[1]); @@ -421,9 +542,17 @@ TEST_F(scm_pidfd, test) ASSERT_NE(-1, err); } - close(pfd); waitpid(self->client_pid, &child_status, 0); - ASSERT_EQ(0, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1); + /* see comment before exit(CHILD_EXIT_CODE_OK) */ + ASSERT_EQ(CHILD_EXIT_CODE_OK, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1); + + err = sk_enable_cred_pass(pfd); + ASSERT_EQ(0, err); + + err = cmsg_check_dead(pfd, self->client_pid); + ASSERT_EQ(0, err); + + close(pfd); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/bench/Makefile b/tools/testing/selftests/net/bench/Makefile new file mode 100644 index 000000000000..2546c45e42f7 --- /dev/null +++ b/tools/testing/selftests/net/bench/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_GEN_MODS_DIR := page_pool + +TEST_PROGS += test_bench_page_pool.sh + +include ../../lib.mk diff --git a/tools/testing/selftests/net/bench/page_pool/Makefile b/tools/testing/selftests/net/bench/page_pool/Makefile new file mode 100644 index 000000000000..0549a16ba275 --- /dev/null +++ b/tools/testing/selftests/net/bench/page_pool/Makefile @@ -0,0 +1,17 @@ +BENCH_PAGE_POOL_SIMPLE_TEST_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) +KDIR ?= /lib/modules/$(shell uname -r)/build + +ifeq ($(V),1) +Q = +else +Q = @ +endif + +obj-m += bench_page_pool.o +bench_page_pool-y += bench_page_pool_simple.o time_bench.o + +all: + +$(Q)make -C $(KDIR) M=$(BENCH_PAGE_POOL_SIMPLE_TEST_DIR) modules + +clean: + +$(Q)make -C $(KDIR) M=$(BENCH_PAGE_POOL_SIMPLE_TEST_DIR) clean diff --git a/tools/testing/selftests/net/bench/page_pool/bench_page_pool_simple.c b/tools/testing/selftests/net/bench/page_pool/bench_page_pool_simple.c new file mode 100644 index 000000000000..cb6468adbda4 --- /dev/null +++ b/tools/testing/selftests/net/bench/page_pool/bench_page_pool_simple.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Benchmark module for page_pool. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "time_bench.h" + +static int verbose = 1; +#define MY_POOL_SIZE 1024 + +/* Makes tests selectable. Useful for perf-record to analyze a single test. + * Hint: Bash shells support writing binary number like: $((2#101010) + * + * # modprobe bench_page_pool_simple run_flags=$((2#100)) + */ +static unsigned long run_flags = 0xFFFFFFFF; +module_param(run_flags, ulong, 0); +MODULE_PARM_DESC(run_flags, "Limit which bench test that runs"); + +/* Count the bit number from the enum */ +enum benchmark_bit { + bit_run_bench_baseline, + bit_run_bench_no_softirq01, + bit_run_bench_no_softirq02, + bit_run_bench_no_softirq03, +}; + +#define bit(b) (1 << (b)) +#define enabled(b) ((run_flags & (bit(b)))) + +/* notice time_bench is limited to U32_MAX nr loops */ +static unsigned long loops = 10000000; +module_param(loops, ulong, 0); +MODULE_PARM_DESC(loops, "Specify loops bench will run"); + +/* Timing at the nanosec level, we need to know the overhead + * introduced by the for loop itself + */ +static int time_bench_for_loop(struct time_bench_record *rec, void *data) +{ + uint64_t loops_cnt = 0; + int i; + + time_bench_start(rec); + /** Loop to measure **/ + for (i = 0; i < rec->loops; i++) { + loops_cnt++; + barrier(); /* avoid compiler to optimize this loop */ + } + time_bench_stop(rec, loops_cnt); + return loops_cnt; +} + +static int time_bench_atomic_inc(struct time_bench_record *rec, void *data) +{ + uint64_t loops_cnt = 0; + atomic_t cnt; + int i; + + atomic_set(&cnt, 0); + + time_bench_start(rec); + /** Loop to measure **/ + for (i = 0; i < rec->loops; i++) { + atomic_inc(&cnt); + barrier(); /* avoid compiler to optimize this loop */ + } + loops_cnt = atomic_read(&cnt); + time_bench_stop(rec, loops_cnt); + return loops_cnt; +} + +/* The ptr_ping in page_pool uses a spinlock. We need to know the minimum + * overhead of taking+releasing a spinlock, to know the cycles that can be saved + * by e.g. amortizing this via bulking. + */ +static int time_bench_lock(struct time_bench_record *rec, void *data) +{ + uint64_t loops_cnt = 0; + spinlock_t lock; + int i; + + spin_lock_init(&lock); + + time_bench_start(rec); + /** Loop to measure **/ + for (i = 0; i < rec->loops; i++) { + spin_lock(&lock); + loops_cnt++; + barrier(); /* avoid compiler to optimize this loop */ + spin_unlock(&lock); + } + time_bench_stop(rec, loops_cnt); + return loops_cnt; +} + +/* Helper for filling some page's into ptr_ring */ +static void pp_fill_ptr_ring(struct page_pool *pp, int elems) +{ + /* GFP_ATOMIC needed when under run softirq */ + gfp_t gfp_mask = GFP_ATOMIC; + struct page **array; + int i; + + array = kcalloc(elems, sizeof(struct page *), gfp_mask); + + for (i = 0; i < elems; i++) + array[i] = page_pool_alloc_pages(pp, gfp_mask); + for (i = 0; i < elems; i++) + page_pool_put_page(pp, array[i], -1, false); + + kfree(array); +} + +enum test_type { type_fast_path, type_ptr_ring, type_page_allocator }; + +/* Depends on compile optimizing this function */ +static int time_bench_page_pool(struct time_bench_record *rec, void *data, + enum test_type type, const char *func) +{ + uint64_t loops_cnt = 0; + gfp_t gfp_mask = GFP_ATOMIC; /* GFP_ATOMIC is not really needed */ + int i, err; + + struct page_pool *pp; + struct page *page; + + struct page_pool_params pp_params = { + .order = 0, + .flags = 0, + .pool_size = MY_POOL_SIZE, + .nid = NUMA_NO_NODE, + .dev = NULL, /* Only use for DMA mapping */ + .dma_dir = DMA_BIDIRECTIONAL, + }; + + pp = page_pool_create(&pp_params); + if (IS_ERR(pp)) { + err = PTR_ERR(pp); + pr_warn("%s: Error(%d) creating page_pool\n", func, err); + goto out; + } + pp_fill_ptr_ring(pp, 64); + + if (in_serving_softirq()) + pr_warn("%s(): in_serving_softirq fast-path\n", func); + else + pr_warn("%s(): Cannot use page_pool fast-path\n", func); + + time_bench_start(rec); + /** Loop to measure **/ + for (i = 0; i < rec->loops; i++) { + /* Common fast-path alloc that depend on in_serving_softirq() */ + page = page_pool_alloc_pages(pp, gfp_mask); + if (!page) + break; + loops_cnt++; + barrier(); /* avoid compiler to optimize this loop */ + + /* The benchmarks purpose it to test different return paths. + * Compiler should inline optimize other function calls out + */ + if (type == type_fast_path) { + /* Fast-path recycling e.g. XDP_DROP use-case */ + page_pool_recycle_direct(pp, page); + + } else if (type == type_ptr_ring) { + /* Normal return path */ + page_pool_put_page(pp, page, -1, false); + + } else if (type == type_page_allocator) { + /* Test if not pages are recycled, but instead + * returned back into systems page allocator + */ + get_page(page); /* cause no-recycling */ + page_pool_put_page(pp, page, -1, false); + put_page(page); + } else { + BUILD_BUG(); + } + } + time_bench_stop(rec, loops_cnt); +out: + page_pool_destroy(pp); + return loops_cnt; +} + +static int time_bench_page_pool01_fast_path(struct time_bench_record *rec, + void *data) +{ + return time_bench_page_pool(rec, data, type_fast_path, __func__); +} + +static int time_bench_page_pool02_ptr_ring(struct time_bench_record *rec, + void *data) +{ + return time_bench_page_pool(rec, data, type_ptr_ring, __func__); +} + +static int time_bench_page_pool03_slow(struct time_bench_record *rec, + void *data) +{ + return time_bench_page_pool(rec, data, type_page_allocator, __func__); +} + +static int run_benchmark_tests(void) +{ + uint32_t nr_loops = loops; + + /* Baseline tests */ + if (enabled(bit_run_bench_baseline)) { + time_bench_loop(nr_loops * 10, 0, "for_loop", NULL, + time_bench_for_loop); + time_bench_loop(nr_loops * 10, 0, "atomic_inc", NULL, + time_bench_atomic_inc); + time_bench_loop(nr_loops, 0, "lock", NULL, time_bench_lock); + } + + /* This test cannot activate correct code path, due to no-softirq ctx */ + if (enabled(bit_run_bench_no_softirq01)) + time_bench_loop(nr_loops, 0, "no-softirq-page_pool01", NULL, + time_bench_page_pool01_fast_path); + if (enabled(bit_run_bench_no_softirq02)) + time_bench_loop(nr_loops, 0, "no-softirq-page_pool02", NULL, + time_bench_page_pool02_ptr_ring); + if (enabled(bit_run_bench_no_softirq03)) + time_bench_loop(nr_loops, 0, "no-softirq-page_pool03", NULL, + time_bench_page_pool03_slow); + + return 0; +} + +static int __init bench_page_pool_simple_module_init(void) +{ + if (verbose) + pr_info("Loaded\n"); + + if (loops > U32_MAX) { + pr_err("Module param loops(%lu) exceeded U32_MAX(%u)\n", loops, + U32_MAX); + return -ECHRNG; + } + + run_benchmark_tests(); + + return 0; +} +module_init(bench_page_pool_simple_module_init); + +static void __exit bench_page_pool_simple_module_exit(void) +{ + if (verbose) + pr_info("Unloaded\n"); +} +module_exit(bench_page_pool_simple_module_exit); + +MODULE_DESCRIPTION("Benchmark of page_pool simple cases"); +MODULE_AUTHOR("Jesper Dangaard Brouer "); +MODULE_LICENSE("GPL"); diff --git a/tools/testing/selftests/net/bench/page_pool/time_bench.c b/tools/testing/selftests/net/bench/page_pool/time_bench.c new file mode 100644 index 000000000000..073bb36ec5f2 --- /dev/null +++ b/tools/testing/selftests/net/bench/page_pool/time_bench.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Benchmarking code execution time inside the kernel + * + * Copyright (C) 2014, Red Hat, Inc., Jesper Dangaard Brouer + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include /* perf_event_create_kernel_counter() */ + +/* For concurrency testing */ +#include +#include +#include +#include + +#include "time_bench.h" + +static int verbose = 1; + +/** TSC (Time-Stamp Counter) based ** + * See: linux/time_bench.h + * tsc_start_clock() and tsc_stop_clock() + */ + +/** Wall-clock based ** + */ + +/** PMU (Performance Monitor Unit) based ** + */ +#define PERF_FORMAT \ + (PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED | \ + PERF_FORMAT_TOTAL_TIME_RUNNING) + +struct raw_perf_event { + uint64_t config; /* event */ + uint64_t config1; /* umask */ + struct perf_event *save; + char *desc; +}; + +/* if HT is enable a maximum of 4 events (5 if one is instructions + * retired can be specified, if HT is disabled a maximum of 8 (9 if + * one is instructions retired) can be specified. + * + * From Table 19-1. Architectural Performance Events + * Architectures Software Developer’s Manual Volume 3: System Programming + * Guide + */ +struct raw_perf_event perf_events[] = { + { 0x3c, 0x00, NULL, "Unhalted CPU Cycles" }, + { 0xc0, 0x00, NULL, "Instruction Retired" } +}; + +#define NUM_EVTS (ARRAY_SIZE(perf_events)) + +/* WARNING: PMU config is currently broken! + */ +bool time_bench_PMU_config(bool enable) +{ + int i; + struct perf_event_attr perf_conf; + struct perf_event *perf_event; + int cpu; + + preempt_disable(); + cpu = smp_processor_id(); + pr_info("DEBUG: cpu:%d\n", cpu); + preempt_enable(); + + memset(&perf_conf, 0, sizeof(struct perf_event_attr)); + perf_conf.type = PERF_TYPE_RAW; + perf_conf.size = sizeof(struct perf_event_attr); + perf_conf.read_format = PERF_FORMAT; + perf_conf.pinned = 1; + perf_conf.exclude_user = 1; /* No userspace events */ + perf_conf.exclude_kernel = 0; /* Only kernel events */ + + for (i = 0; i < NUM_EVTS; i++) { + perf_conf.disabled = enable; + //perf_conf.disabled = (i == 0) ? 1 : 0; + perf_conf.config = perf_events[i].config; + perf_conf.config1 = perf_events[i].config1; + if (verbose) + pr_info("%s() enable PMU counter: %s\n", + __func__, perf_events[i].desc); + perf_event = perf_event_create_kernel_counter(&perf_conf, cpu, + NULL /* task */, + NULL /* overflow_handler*/, + NULL /* context */); + if (perf_event) { + perf_events[i].save = perf_event; + pr_info("%s():DEBUG perf_event success\n", __func__); + + perf_event_enable(perf_event); + } else { + pr_info("%s():DEBUG perf_event is NULL\n", __func__); + } + } + + return true; +} + +/** Generic functions ** + */ + +/* Calculate stats, store results in record */ +bool time_bench_calc_stats(struct time_bench_record *rec) +{ +#define NANOSEC_PER_SEC 1000000000 /* 10^9 */ + uint64_t ns_per_call_tmp_rem = 0; + uint32_t ns_per_call_remainder = 0; + uint64_t pmc_ipc_tmp_rem = 0; + uint32_t pmc_ipc_remainder = 0; + uint32_t pmc_ipc_div = 0; + uint32_t invoked_cnt_precision = 0; + uint32_t invoked_cnt = 0; /* 32-bit due to div_u64_rem() */ + + if (rec->flags & TIME_BENCH_LOOP) { + if (rec->invoked_cnt < 1000) { + pr_err("ERR: need more(>1000) loops(%llu) for timing\n", + rec->invoked_cnt); + return false; + } + if (rec->invoked_cnt > ((1ULL << 32) - 1)) { + /* div_u64_rem() can only support div with 32bit*/ + pr_err("ERR: Invoke cnt(%llu) too big overflow 32bit\n", + rec->invoked_cnt); + return false; + } + invoked_cnt = (uint32_t)rec->invoked_cnt; + } + + /* TSC (Time-Stamp Counter) records */ + if (rec->flags & TIME_BENCH_TSC) { + rec->tsc_interval = rec->tsc_stop - rec->tsc_start; + if (rec->tsc_interval == 0) { + pr_err("ABORT: timing took ZERO TSC time\n"); + return false; + } + /* Calculate stats */ + if (rec->flags & TIME_BENCH_LOOP) + rec->tsc_cycles = rec->tsc_interval / invoked_cnt; + else + rec->tsc_cycles = rec->tsc_interval; + } + + /* Wall-clock time calc */ + if (rec->flags & TIME_BENCH_WALLCLOCK) { + rec->time_start = rec->ts_start.tv_nsec + + (NANOSEC_PER_SEC * rec->ts_start.tv_sec); + rec->time_stop = rec->ts_stop.tv_nsec + + (NANOSEC_PER_SEC * rec->ts_stop.tv_sec); + rec->time_interval = rec->time_stop - rec->time_start; + if (rec->time_interval == 0) { + pr_err("ABORT: timing took ZERO wallclock time\n"); + return false; + } + /* Calculate stats */ + /*** Division in kernel it tricky ***/ + /* Orig: time_sec = (time_interval / NANOSEC_PER_SEC); */ + /* remainder only correct because NANOSEC_PER_SEC is 10^9 */ + rec->time_sec = div_u64_rem(rec->time_interval, NANOSEC_PER_SEC, + &rec->time_sec_remainder); + //TODO: use existing struct timespec records instead of div? + + if (rec->flags & TIME_BENCH_LOOP) { + /*** Division in kernel it tricky ***/ + /* Orig: ns = ((double)time_interval / invoked_cnt); */ + /* First get quotient */ + rec->ns_per_call_quotient = + div_u64_rem(rec->time_interval, invoked_cnt, + &ns_per_call_remainder); + /* Now get decimals .xxx precision (incorrect roundup)*/ + ns_per_call_tmp_rem = ns_per_call_remainder; + invoked_cnt_precision = invoked_cnt / 1000; + if (invoked_cnt_precision > 0) { + rec->ns_per_call_decimal = + div_u64_rem(ns_per_call_tmp_rem, + invoked_cnt_precision, + &ns_per_call_remainder); + } + } + } + + /* Performance Monitor Unit (PMU) counters */ + if (rec->flags & TIME_BENCH_PMU) { + //FIXME: Overflow handling??? + rec->pmc_inst = rec->pmc_inst_stop - rec->pmc_inst_start; + rec->pmc_clk = rec->pmc_clk_stop - rec->pmc_clk_start; + + /* Calc Instruction Per Cycle (IPC) */ + /* First get quotient */ + rec->pmc_ipc_quotient = div_u64_rem(rec->pmc_inst, rec->pmc_clk, + &pmc_ipc_remainder); + /* Now get decimals .xxx precision (incorrect roundup)*/ + pmc_ipc_tmp_rem = pmc_ipc_remainder; + pmc_ipc_div = rec->pmc_clk / 1000; + if (pmc_ipc_div > 0) { + rec->pmc_ipc_decimal = div_u64_rem(pmc_ipc_tmp_rem, + pmc_ipc_div, + &pmc_ipc_remainder); + } + } + + return true; +} + +/* Generic function for invoking a loop function and calculating + * execution time stats. The function being called/timed is assumed + * to perform a tight loop, and update the timing record struct. + */ +bool time_bench_loop(uint32_t loops, int step, char *txt, void *data, + int (*func)(struct time_bench_record *record, void *data)) +{ + struct time_bench_record rec; + + /* Setup record */ + memset(&rec, 0, sizeof(rec)); /* zero func might not update all */ + rec.version_abi = 1; + rec.loops = loops; + rec.step = step; + rec.flags = (TIME_BENCH_LOOP | TIME_BENCH_TSC | TIME_BENCH_WALLCLOCK); + + /*** Loop function being timed ***/ + if (!func(&rec, data)) { + pr_err("ABORT: function being timed failed\n"); + return false; + } + + if (rec.invoked_cnt < loops) + pr_warn("WARNING: Invoke count(%llu) smaller than loops(%d)\n", + rec.invoked_cnt, loops); + + /* Calculate stats */ + time_bench_calc_stats(&rec); + + pr_info("Type:%s Per elem: %llu cycles(tsc) %llu.%03llu ns (step:%d) - (measurement period time:%llu.%09u sec time_interval:%llu) - (invoke count:%llu tsc_interval:%llu)\n", + txt, rec.tsc_cycles, rec.ns_per_call_quotient, + rec.ns_per_call_decimal, rec.step, rec.time_sec, + rec.time_sec_remainder, rec.time_interval, rec.invoked_cnt, + rec.tsc_interval); + if (rec.flags & TIME_BENCH_PMU) + pr_info("Type:%s PMU inst/clock%llu/%llu = %llu.%03llu IPC (inst per cycle)\n", + txt, rec.pmc_inst, rec.pmc_clk, rec.pmc_ipc_quotient, + rec.pmc_ipc_decimal); + return true; +} + +/* Function getting invoked by kthread */ +static int invoke_test_on_cpu_func(void *private) +{ + struct time_bench_cpu *cpu = private; + struct time_bench_sync *sync = cpu->sync; + cpumask_t newmask = CPU_MASK_NONE; + void *data = cpu->data; + + /* Restrict CPU */ + cpumask_set_cpu(cpu->rec.cpu, &newmask); + set_cpus_allowed_ptr(current, &newmask); + + /* Synchronize start of concurrency test */ + atomic_inc(&sync->nr_tests_running); + wait_for_completion(&sync->start_event); + + /* Start benchmark function */ + if (!cpu->bench_func(&cpu->rec, data)) { + pr_err("ERROR: function being timed failed on CPU:%d(%d)\n", + cpu->rec.cpu, smp_processor_id()); + } else { + if (verbose) + pr_info("SUCCESS: ran on CPU:%d(%d)\n", cpu->rec.cpu, + smp_processor_id()); + } + cpu->did_bench_run = true; + + /* End test */ + atomic_dec(&sync->nr_tests_running); + /* Wait for kthread_stop() telling us to stop */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + __set_current_state(TASK_RUNNING); + return 0; +} + +void time_bench_print_stats_cpumask(const char *desc, + struct time_bench_cpu *cpu_tasks, + const struct cpumask *mask) +{ + uint64_t average = 0; + int cpu; + int step = 0; + struct sum { + uint64_t tsc_cycles; + int records; + } sum = { 0 }; + + /* Get stats */ + for_each_cpu(cpu, mask) { + struct time_bench_cpu *c = &cpu_tasks[cpu]; + struct time_bench_record *rec = &c->rec; + + /* Calculate stats */ + time_bench_calc_stats(rec); + + pr_info("Type:%s CPU(%d) %llu cycles(tsc) %llu.%03llu ns (step:%d) - (measurement period time:%llu.%09u sec time_interval:%llu) - (invoke count:%llu tsc_interval:%llu)\n", + desc, cpu, rec->tsc_cycles, rec->ns_per_call_quotient, + rec->ns_per_call_decimal, rec->step, rec->time_sec, + rec->time_sec_remainder, rec->time_interval, + rec->invoked_cnt, rec->tsc_interval); + + /* Collect average */ + sum.records++; + sum.tsc_cycles += rec->tsc_cycles; + step = rec->step; + } + + if (sum.records) /* avoid div-by-zero */ + average = sum.tsc_cycles / sum.records; + pr_info("Sum Type:%s Average: %llu cycles(tsc) CPUs:%d step:%d\n", desc, + average, sum.records, step); +} + +void time_bench_run_concurrent(uint32_t loops, int step, void *data, + const struct cpumask *mask, /* Support masking outsome CPUs*/ + struct time_bench_sync *sync, + struct time_bench_cpu *cpu_tasks, + int (*func)(struct time_bench_record *record, void *data)) +{ + int cpu, running = 0; + + if (verbose) // DEBUG + pr_warn("%s() Started on CPU:%d\n", __func__, + smp_processor_id()); + + /* Reset sync conditions */ + atomic_set(&sync->nr_tests_running, 0); + init_completion(&sync->start_event); + + /* Spawn off jobs on all CPUs */ + for_each_cpu(cpu, mask) { + struct time_bench_cpu *c = &cpu_tasks[cpu]; + + running++; + c->sync = sync; /* Send sync variable along */ + c->data = data; /* Send opaque along */ + + /* Init benchmark record */ + memset(&c->rec, 0, sizeof(struct time_bench_record)); + c->rec.version_abi = 1; + c->rec.loops = loops; + c->rec.step = step; + c->rec.flags = (TIME_BENCH_LOOP | TIME_BENCH_TSC | + TIME_BENCH_WALLCLOCK); + c->rec.cpu = cpu; + c->bench_func = func; + c->task = kthread_run(invoke_test_on_cpu_func, c, + "time_bench%d", cpu); + if (IS_ERR(c->task)) { + pr_err("%s(): Failed to start test func\n", __func__); + return; /* Argh, what about cleanup?! */ + } + } + + /* Wait until all processes are running */ + while (atomic_read(&sync->nr_tests_running) < running) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10); + } + /* Kick off all CPU concurrently on completion event */ + complete_all(&sync->start_event); + + /* Wait for CPUs to finish */ + while (atomic_read(&sync->nr_tests_running)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10); + } + + /* Stop the kthreads */ + for_each_cpu(cpu, mask) { + struct time_bench_cpu *c = &cpu_tasks[cpu]; + + kthread_stop(c->task); + } + + if (verbose) // DEBUG - happens often, finish on another CPU + pr_warn("%s() Finished on CPU:%d\n", __func__, + smp_processor_id()); +} diff --git a/tools/testing/selftests/net/bench/page_pool/time_bench.h b/tools/testing/selftests/net/bench/page_pool/time_bench.h new file mode 100644 index 000000000000..e113fcf341dc --- /dev/null +++ b/tools/testing/selftests/net/bench/page_pool/time_bench.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Benchmarking code execution time inside the kernel + * + * Copyright (C) 2014, Red Hat, Inc., Jesper Dangaard Brouer + * for licensing details see kernel-base/COPYING + */ +#ifndef _LINUX_TIME_BENCH_H +#define _LINUX_TIME_BENCH_H + +/* Main structure used for recording a benchmark run */ +struct time_bench_record { + uint32_t version_abi; + uint32_t loops; /* Requested loop invocations */ + uint32_t step; /* option for e.g. bulk invocations */ + + uint32_t flags; /* Measurements types enabled */ +#define TIME_BENCH_LOOP BIT(0) +#define TIME_BENCH_TSC BIT(1) +#define TIME_BENCH_WALLCLOCK BIT(2) +#define TIME_BENCH_PMU BIT(3) + + uint32_t cpu; /* Used when embedded in time_bench_cpu */ + + /* Records */ + uint64_t invoked_cnt; /* Returned actual invocations */ + uint64_t tsc_start; + uint64_t tsc_stop; + struct timespec64 ts_start; + struct timespec64 ts_stop; + /* PMU counters for instruction and cycles + * instructions counter including pipelined instructions + */ + uint64_t pmc_inst_start; + uint64_t pmc_inst_stop; + /* CPU unhalted clock counter */ + uint64_t pmc_clk_start; + uint64_t pmc_clk_stop; + + /* Result records */ + uint64_t tsc_interval; + uint64_t time_start, time_stop, time_interval; /* in nanosec */ + uint64_t pmc_inst, pmc_clk; + + /* Derived result records */ + uint64_t tsc_cycles; // +decimal? + uint64_t ns_per_call_quotient, ns_per_call_decimal; + uint64_t time_sec; + uint32_t time_sec_remainder; + uint64_t pmc_ipc_quotient, pmc_ipc_decimal; /* inst per cycle */ +}; + +/* For synchronizing parallel CPUs to run concurrently */ +struct time_bench_sync { + atomic_t nr_tests_running; + struct completion start_event; +}; + +/* Keep track of CPUs executing our bench function. + * + * Embed a time_bench_record for storing info per cpu + */ +struct time_bench_cpu { + struct time_bench_record rec; + struct time_bench_sync *sync; /* back ptr */ + struct task_struct *task; + /* "data" opaque could have been placed in time_bench_sync, + * but to avoid any false sharing, place it per CPU + */ + void *data; + /* Support masking outsome CPUs, mark if it ran */ + bool did_bench_run; + /* int cpu; // note CPU stored in time_bench_record */ + int (*bench_func)(struct time_bench_record *record, void *data); +}; + +/* + * Below TSC assembler code is not compatible with other archs, and + * can also fail on guests if cpu-flags are not correct. + * + * The way TSC reading is used, many iterations, does not require as + * high accuracy as described below (in Intel Doc #324264). + * + * Considering changing to use get_cycles() (#include ). + */ + +/** TSC (Time-Stamp Counter) based ** + * Recommend reading, to understand details of reading TSC accurately: + * Intel Doc #324264, "How to Benchmark Code Execution Times on Intel" + * + * Consider getting exclusive ownership of CPU by using: + * unsigned long flags; + * preempt_disable(); + * raw_local_irq_save(flags); + * _your_code_ + * raw_local_irq_restore(flags); + * preempt_enable(); + * + * Clobbered registers: "%rax", "%rbx", "%rcx", "%rdx" + * RDTSC only change "%rax" and "%rdx" but + * CPUID clears the high 32-bits of all (rax/rbx/rcx/rdx) + */ +static __always_inline uint64_t tsc_start_clock(void) +{ + /* See: Intel Doc #324264 */ + unsigned int hi, lo; + + asm volatile("CPUID\n\t" + "RDTSC\n\t" + "mov %%edx, %0\n\t" + "mov %%eax, %1\n\t" + : "=r"(hi), "=r"(lo)::"%rax", "%rbx", "%rcx", "%rdx"); + //FIXME: on 32bit use clobbered %eax + %edx + return ((uint64_t)lo) | (((uint64_t)hi) << 32); +} + +static __always_inline uint64_t tsc_stop_clock(void) +{ + /* See: Intel Doc #324264 */ + unsigned int hi, lo; + + asm volatile("RDTSCP\n\t" + "mov %%edx, %0\n\t" + "mov %%eax, %1\n\t" + "CPUID\n\t" + : "=r"(hi), "=r"(lo)::"%rax", "%rbx", "%rcx", "%rdx"); + return ((uint64_t)lo) | (((uint64_t)hi) << 32); +} + +/** Wall-clock based ** + * + * use: getnstimeofday() + * getnstimeofday(&rec->ts_start); + * getnstimeofday(&rec->ts_stop); + * + * API changed see: Documentation/core-api/timekeeping.rst + * https://www.kernel.org/doc/html/latest/core-api/timekeeping.html#c.getnstimeofday + * + * We should instead use: ktime_get_real_ts64() is a direct + * replacement, but consider using monotonic time (ktime_get_ts64()) + * and/or a ktime_t based interface (ktime_get()/ktime_get_real()). + */ + +/** PMU (Performance Monitor Unit) based ** + * + * Needed for calculating: Instructions Per Cycle (IPC) + * - The IPC number tell how efficient the CPU pipelining were + */ +//lookup: perf_event_create_kernel_counter() + +bool time_bench_PMU_config(bool enable); + +/* Raw reading via rdpmc() using fixed counters + * + * From: https://github.com/andikleen/simple-pmu + */ +enum { + FIXED_SELECT = (1U << 30), /* == 0x40000000 */ + FIXED_INST_RETIRED_ANY = 0, + FIXED_CPU_CLK_UNHALTED_CORE = 1, + FIXED_CPU_CLK_UNHALTED_REF = 2, +}; + +static __always_inline unsigned int long long p_rdpmc(unsigned int in) +{ + unsigned int d, a; + + asm volatile("rdpmc" : "=d"(d), "=a"(a) : "c"(in) : "memory"); + return ((unsigned long long)d << 32) | a; +} + +/* These PMU counter needs to be enabled, but I don't have the + * configure code implemented. My current hack is running: + * sudo perf stat -e cycles:k -e instructions:k insmod lib/ring_queue_test.ko + */ +/* Reading all pipelined instruction */ +static __always_inline unsigned long long pmc_inst(void) +{ + return p_rdpmc(FIXED_SELECT | FIXED_INST_RETIRED_ANY); +} + +/* Reading CPU clock cycles */ +static __always_inline unsigned long long pmc_clk(void) +{ + return p_rdpmc(FIXED_SELECT | FIXED_CPU_CLK_UNHALTED_CORE); +} + +/* Raw reading via MSR rdmsr() is likely wrong + * FIXME: How can I know which raw MSR registers are conf for what? + */ +#define MSR_IA32_PCM0 0x400000C1 /* PERFCTR0 */ +#define MSR_IA32_PCM1 0x400000C2 /* PERFCTR1 */ +#define MSR_IA32_PCM2 0x400000C3 +static inline uint64_t msr_inst(unsigned long long *msr_result) +{ + return rdmsrq_safe(MSR_IA32_PCM0, msr_result); +} + +/** Generic functions ** + */ +bool time_bench_loop(uint32_t loops, int step, char *txt, void *data, + int (*func)(struct time_bench_record *rec, void *data)); +bool time_bench_calc_stats(struct time_bench_record *rec); + +void time_bench_run_concurrent(uint32_t loops, int step, void *data, + const struct cpumask *mask, /* Support masking outsome CPUs*/ + struct time_bench_sync *sync, struct time_bench_cpu *cpu_tasks, + int (*func)(struct time_bench_record *record, void *data)); +void time_bench_print_stats_cpumask(const char *desc, + struct time_bench_cpu *cpu_tasks, + const struct cpumask *mask); + +//FIXME: use rec->flags to select measurement, should be MACRO +static __always_inline void time_bench_start(struct time_bench_record *rec) +{ + //getnstimeofday(&rec->ts_start); + ktime_get_real_ts64(&rec->ts_start); + if (rec->flags & TIME_BENCH_PMU) { + rec->pmc_inst_start = pmc_inst(); + rec->pmc_clk_start = pmc_clk(); + } + rec->tsc_start = tsc_start_clock(); +} + +static __always_inline void time_bench_stop(struct time_bench_record *rec, + uint64_t invoked_cnt) +{ + rec->tsc_stop = tsc_stop_clock(); + if (rec->flags & TIME_BENCH_PMU) { + rec->pmc_inst_stop = pmc_inst(); + rec->pmc_clk_stop = pmc_clk(); + } + //getnstimeofday(&rec->ts_stop); + ktime_get_real_ts64(&rec->ts_stop); + rec->invoked_cnt = invoked_cnt; +} + +#endif /* _LINUX_TIME_BENCH_H */ diff --git a/tools/testing/selftests/net/bench/test_bench_page_pool.sh b/tools/testing/selftests/net/bench/test_bench_page_pool.sh new file mode 100755 index 000000000000..7b8b18cfedce --- /dev/null +++ b/tools/testing/selftests/net/bench/test_bench_page_pool.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# + +set -e + +DRIVER="./page_pool/bench_page_pool.ko" +result="" + +function run_test() +{ + rmmod "bench_page_pool.ko" || true + insmod $DRIVER > /dev/null 2>&1 + result=$(dmesg | tail -10) + echo "$result" + + echo + echo "Fast path results:" + echo "${result}" | grep -o -E "no-softirq-page_pool01 Per elem: ([0-9]+) cycles\(tsc\) ([0-9]+\.[0-9]+) ns" + + echo + echo "ptr_ring results:" + echo "${result}" | grep -o -E "no-softirq-page_pool02 Per elem: ([0-9]+) cycles\(tsc\) ([0-9]+\.[0-9]+) ns" + + echo + echo "slow path results:" + echo "${result}" | grep -o -E "no-softirq-page_pool03 Per elem: ([0-9]+) cycles\(tsc\) ([0-9]+\.[0-9]+) ns" +} + +run_test + +exit 0 diff --git a/tools/testing/selftests/net/bind_bhash.c b/tools/testing/selftests/net/bind_bhash.c index 57ff67a3751e..da04b0b19b73 100644 --- a/tools/testing/selftests/net/bind_bhash.c +++ b/tools/testing/selftests/net/bind_bhash.c @@ -75,7 +75,7 @@ static void *setup(void *arg) int *array = (int *)arg; for (i = 0; i < MAX_CONNECTIONS; i++) { - sock_fd = bind_socket(SO_REUSEADDR | SO_REUSEPORT, setup_addr); + sock_fd = bind_socket(SO_REUSEPORT, setup_addr); if (sock_fd < 0) { ret = sock_fd; pthread_exit(&ret); @@ -103,7 +103,7 @@ int main(int argc, const char *argv[]) setup_addr = use_v6 ? setup_addr_v6 : setup_addr_v4; - listener_fd = bind_socket(SO_REUSEADDR | SO_REUSEPORT, setup_addr); + listener_fd = bind_socket(SO_REUSEPORT, setup_addr); if (listen(listener_fd, 100) < 0) { perror("listen failed"); return -1; diff --git a/tools/testing/selftests/net/broadcast_ether_dst.sh b/tools/testing/selftests/net/broadcast_ether_dst.sh new file mode 100755 index 000000000000..334a7eca8a80 --- /dev/null +++ b/tools/testing/selftests/net/broadcast_ether_dst.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Author: Brett A C Sheffield +# Author: Oscar Maes +# +# Ensure destination ethernet field is correctly set for +# broadcast packets + +source lib.sh + +CLIENT_IP4="192.168.0.1" +GW_IP4="192.168.0.2" + +setup() { + setup_ns CLIENT_NS SERVER_NS + + ip -net "${SERVER_NS}" link add link1 type veth \ + peer name link0 netns "${CLIENT_NS}" + + ip -net "${CLIENT_NS}" link set link0 up + ip -net "${CLIENT_NS}" addr add "${CLIENT_IP4}"/24 dev link0 + + ip -net "${SERVER_NS}" link set link1 up + + ip -net "${CLIENT_NS}" route add default via "${GW_IP4}" + ip netns exec "${CLIENT_NS}" arp -s "${GW_IP4}" 00:11:22:33:44:55 +} + +cleanup() { + rm -f "${CAPFILE}" "${OUTPUT}" + ip -net "${SERVER_NS}" link del link1 + cleanup_ns "${CLIENT_NS}" "${SERVER_NS}" +} + +test_broadcast_ether_dst() { + local rc=0 + CAPFILE=$(mktemp -u cap.XXXXXXXXXX) + OUTPUT=$(mktemp -u out.XXXXXXXXXX) + + echo "Testing ethernet broadcast destination" + + # start tcpdump listening for icmp + # tcpdump will exit after receiving a single packet + # timeout will kill tcpdump if it is still running after 2s + timeout 2s ip netns exec "${CLIENT_NS}" \ + tcpdump -i link0 -c 1 -w "${CAPFILE}" icmp &> "${OUTPUT}" & + pid=$! + slowwait 1 grep -qs "listening" "${OUTPUT}" + + # send broadcast ping + ip netns exec "${CLIENT_NS}" \ + ping -W0.01 -c1 -b 255.255.255.255 &> /dev/null + + # wait for tcpdump for exit after receiving packet + wait "${pid}" + + # compare ethernet destination field to ff:ff:ff:ff:ff:ff + ether_dst=$(tcpdump -r "${CAPFILE}" -tnne 2>/dev/null | \ + awk '{sub(/,/,"",$3); print $3}') + if [[ "${ether_dst}" == "ff:ff:ff:ff:ff:ff" ]]; then + echo "[ OK ]" + rc="${ksft_pass}" + else + echo "[FAIL] expected dst ether addr to be ff:ff:ff:ff:ff:ff," \ + "got ${ether_dst}" + rc="${ksft_fail}" + fi + + return "${rc}" +} + +if [ ! -x "$(command -v tcpdump)" ]; then + echo "SKIP: Could not run test without tcpdump tool" + exit "${ksft_skip}" +fi + +trap cleanup EXIT + +setup +test_broadcast_ether_dst + +exit $? diff --git a/tools/testing/selftests/net/broadcast_pmtu.sh b/tools/testing/selftests/net/broadcast_pmtu.sh new file mode 100755 index 000000000000..726eb5d25839 --- /dev/null +++ b/tools/testing/selftests/net/broadcast_pmtu.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Ensures broadcast route MTU is respected + +CLIENT_NS=$(mktemp -u client-XXXXXXXX) +CLIENT_IP4="192.168.0.1/24" +CLIENT_BROADCAST_ADDRESS="192.168.0.255" + +SERVER_NS=$(mktemp -u server-XXXXXXXX) +SERVER_IP4="192.168.0.2/24" + +setup() { + ip netns add "${CLIENT_NS}" + ip netns add "${SERVER_NS}" + + ip -net "${SERVER_NS}" link add link1 type veth peer name link0 netns "${CLIENT_NS}" + + ip -net "${CLIENT_NS}" link set link0 up + ip -net "${CLIENT_NS}" link set link0 mtu 9000 + ip -net "${CLIENT_NS}" addr add "${CLIENT_IP4}" dev link0 + + ip -net "${SERVER_NS}" link set link1 up + ip -net "${SERVER_NS}" link set link1 mtu 1500 + ip -net "${SERVER_NS}" addr add "${SERVER_IP4}" dev link1 + + read -r -a CLIENT_BROADCAST_ENTRY <<< "$(ip -net "${CLIENT_NS}" route show table local type broadcast)" + ip -net "${CLIENT_NS}" route del "${CLIENT_BROADCAST_ENTRY[@]}" + ip -net "${CLIENT_NS}" route add "${CLIENT_BROADCAST_ENTRY[@]}" mtu 1500 + + ip net exec "${SERVER_NS}" sysctl -wq net.ipv4.icmp_echo_ignore_broadcasts=0 +} + +cleanup() { + ip -net "${SERVER_NS}" link del link1 + ip netns del "${CLIENT_NS}" + ip netns del "${SERVER_NS}" +} + +trap cleanup EXIT + +setup && + echo "Testing for broadcast route MTU" && + ip net exec "${CLIENT_NS}" ping -f -M want -q -c 1 -s 8000 -w 1 -b "${CLIENT_BROADCAST_ADDRESS}" > /dev/null 2>&1 + +exit $? + diff --git a/tools/testing/selftests/net/can/config b/tools/testing/selftests/net/can/config new file mode 100644 index 000000000000..188f79796670 --- /dev/null +++ b/tools/testing/selftests/net/can/config @@ -0,0 +1,3 @@ +CONFIG_CAN=m +CONFIG_CAN_DEV=m +CONFIG_CAN_VCAN=m diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index 3cfef5153823..c24417d0047b 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -30,16 +30,25 @@ CONFIG_NET_FOU=y CONFIG_NET_FOU_IP_TUNNELS=y CONFIG_NETFILTER=y CONFIG_NETFILTER_ADVANCED=y +CONFIG_NETFILTER_XTABLES_LEGACY=y CONFIG_NF_CONNTRACK=m CONFIG_IPV6_MROUTE=y CONFIG_IPV6_SIT=y CONFIG_NF_NAT=m CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_IPTABLES_LEGACY=m CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_IPTABLES_LEGACY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_FILTER=m CONFIG_IP6_NF_NAT=m CONFIG_IP6_NF_RAW=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_REJECT=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IPV6_GRE=m CONFIG_IPV6_SEG6_LWTUNNEL=y @@ -57,6 +66,8 @@ CONFIG_NF_TABLES_IPV6=y CONFIG_NF_TABLES_IPV4=y CONFIG_NFT_NAT=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_TARGET_HL=m +CONFIG_NETFILTER_XT_NAT=m CONFIG_NET_ACT_CSUM=m CONFIG_NET_ACT_CT=m CONFIG_NET_ACT_GACT=m diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testing/selftests/net/forwarding/Makefile index 00bde7b6f39e..d7bb2e80e88c 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -102,6 +102,7 @@ TEST_PROGS = bridge_fdb_learning_limit.sh \ vxlan_bridge_1d_port_8472.sh \ vxlan_bridge_1d.sh \ vxlan_bridge_1q_ipv6.sh \ + vxlan_bridge_1q_mc_ul.sh \ vxlan_bridge_1q_port_8472_ipv6.sh \ vxlan_bridge_1q_port_8472.sh \ vxlan_bridge_1q.sh \ diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 508f3c700d71..890b3374dacd 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -37,6 +37,7 @@ declare -A NETIFS=( : "${TEAMD:=teamd}" : "${MCD:=smcrouted}" : "${MC_CLI:=smcroutectl}" +: "${MCD_TABLE_NAME:=selftests}" # Constants for netdevice bring-up: # Default time in seconds to wait for an interface to come up before giving up @@ -141,6 +142,20 @@ check_tc_version() fi } +check_tc_erspan_support() +{ + local dev=$1; shift + + tc filter add dev $dev ingress pref 1 handle 1 flower \ + erspan_opts 1:0:0:0 &> /dev/null + if [[ $? -ne 0 ]]; then + echo "SKIP: iproute2 too old; tc is missing erspan support" + return $ksft_skip + fi + tc filter del dev $dev ingress pref 1 handle 1 flower \ + erspan_opts 1:0:0:0 &> /dev/null +} + # Old versions of tc don't understand "mpls_uc" check_tc_mpls_support() { @@ -525,9 +540,9 @@ setup_wait_dev_with_timeout() return 1 } -setup_wait() +setup_wait_n() { - local num_netifs=${1:-$NUM_NETIFS} + local num_netifs=$1; shift local i for ((i = 1; i <= num_netifs; ++i)); do @@ -538,6 +553,11 @@ setup_wait() sleep $WAIT_TIME } +setup_wait() +{ + setup_wait_n "$NUM_NETIFS" +} + wait_for_dev() { local dev=$1; shift @@ -1757,6 +1777,51 @@ mc_send() msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 } +adf_mcd_start() +{ + local ifs=("$@") + + local table_name="$MCD_TABLE_NAME" + local smcroutedir + local pid + local if + local i + + check_command "$MCD" || return 1 + check_command "$MC_CLI" || return 1 + + smcroutedir=$(mktemp -d) + defer rm -rf "$smcroutedir" + + for ((i = 1; i <= NUM_NETIFS; ++i)); do + echo "phyint ${NETIFS[p$i]} enable" >> \ + "$smcroutedir/$table_name.conf" + done + + for if in "${ifs[@]}"; do + if ! ip_link_has_flag "$if" MULTICAST; then + ip link set dev "$if" multicast on + defer ip link set dev "$if" multicast off + fi + + echo "phyint $if enable" >> \ + "$smcroutedir/$table_name.conf" + done + + "$MCD" -N -I "$table_name" -f "$smcroutedir/$table_name.conf" \ + -P "$smcroutedir/$table_name.pid" + busywait "$BUSYWAIT_TIMEOUT" test -e "$smcroutedir/$table_name.pid" + pid=$(cat "$smcroutedir/$table_name.pid") + defer kill_process "$pid" +} + +mc_cli() +{ + local table_name="$MCD_TABLE_NAME" + + "$MC_CLI" -I "$table_name" "$@" +} + start_ip_monitor() { local mtype=$1; shift diff --git a/tools/testing/selftests/net/forwarding/router.sh b/tools/testing/selftests/net/forwarding/router.sh index b98ea9449b8b..dfb6646cb97b 100755 --- a/tools/testing/selftests/net/forwarding/router.sh +++ b/tools/testing/selftests/net/forwarding/router.sh @@ -18,6 +18,8 @@ # | 2001:db8:1::1/64 2001:db8:2::1/64 | # | | # +-----------------------------------------------------------------+ +# +#shellcheck disable=SC2034 # SC doesn't see our uses of global variables ALL_TESTS=" ping_ipv4 @@ -27,6 +29,7 @@ ALL_TESTS=" ipv4_sip_equal_dip ipv6_sip_equal_dip ipv4_dip_link_local + ipv4_sip_link_local " NUM_NETIFS=4 @@ -330,6 +333,32 @@ ipv4_dip_link_local() tc filter del dev $rp2 egress protocol ip pref 1 handle 101 flower } +ipv4_sip_link_local() +{ + local sip=169.254.1.1 + + RET=0 + + # Disable rpfilter to prevent packets to be dropped because of it. + sysctl_set net.ipv4.conf.all.rp_filter 0 + sysctl_set net.ipv4.conf."$rp1".rp_filter 0 + + tc filter add dev "$rp2" egress protocol ip pref 1 handle 101 \ + flower src_ip "$sip" action pass + + $MZ "$h1" -t udp "sp=54321,dp=12345" -c 5 -d 1msec -b "$rp1mac" \ + -A "$sip" -B 198.51.100.2 -q + + tc_check_packets "dev $rp2 egress" 101 5 + check_err $? "Packets were dropped" + + log_test "IPv4 source IP is link-local" + + tc filter del dev "$rp2" egress protocol ip pref 1 handle 101 flower + sysctl_restore net.ipv4.conf."$rp1".rp_filter + sysctl_restore net.ipv4.conf.all.rp_filter +} + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/router_multicast.sh b/tools/testing/selftests/net/forwarding/router_multicast.sh index 5a58b1ec8aef..83e52abdbc2e 100755 --- a/tools/testing/selftests/net/forwarding/router_multicast.sh +++ b/tools/testing/selftests/net/forwarding/router_multicast.sh @@ -33,10 +33,6 @@ NUM_NETIFS=6 source lib.sh source tc_common.sh -require_command $MCD -require_command $MC_CLI -table_name=selftests - h1_create() { simple_if_init $h1 198.51.100.2/28 2001:db8:1::2/64 @@ -149,25 +145,6 @@ router_destroy() ip link set dev $rp1 down } -start_mcd() -{ - SMCROUTEDIR="$(mktemp -d)" - - for ((i = 1; i <= $NUM_NETIFS; ++i)); do - echo "phyint ${NETIFS[p$i]} enable" >> \ - $SMCROUTEDIR/$table_name.conf - done - - $MCD -N -I $table_name -f $SMCROUTEDIR/$table_name.conf \ - -P $SMCROUTEDIR/$table_name.pid -} - -kill_mcd() -{ - pkill $MCD - rm -rf $SMCROUTEDIR -} - setup_prepare() { h1=${NETIFS[p1]} @@ -179,7 +156,7 @@ setup_prepare() rp3=${NETIFS[p5]} h3=${NETIFS[p6]} - start_mcd + adf_mcd_start || exit "$EXIT_STATUS" vrf_prepare @@ -206,7 +183,7 @@ cleanup() vrf_cleanup - kill_mcd + defer_scopes_cleanup } create_mcast_sg() @@ -214,9 +191,9 @@ create_mcast_sg() local if_name=$1; shift local s_addr=$1; shift local mcast=$1; shift - local dest_ifs=${@} + local dest_ifs=("${@}") - $MC_CLI -I $table_name add $if_name $s_addr $mcast $dest_ifs + mc_cli add "$if_name" "$s_addr" "$mcast" "${dest_ifs[@]}" } delete_mcast_sg() @@ -224,9 +201,9 @@ delete_mcast_sg() local if_name=$1; shift local s_addr=$1; shift local mcast=$1; shift - local dest_ifs=${@} + local dest_ifs=("${@}") - $MC_CLI -I $table_name remove $if_name $s_addr $mcast $dest_ifs + mc_cli remove "$if_name" "$s_addr" "$mcast" "${dest_ifs[@]}" } mcast_v4() diff --git a/tools/testing/selftests/net/forwarding/sch_ets.sh b/tools/testing/selftests/net/forwarding/sch_ets.sh index 1f6f53e284b5..6269d5e23487 100755 --- a/tools/testing/selftests/net/forwarding/sch_ets.sh +++ b/tools/testing/selftests/net/forwarding/sch_ets.sh @@ -11,6 +11,7 @@ ALL_TESTS=" ets_test_strict ets_test_mixed ets_test_dwrr + ets_test_plug classifier_mode ets_test_strict ets_test_mixed diff --git a/tools/testing/selftests/net/forwarding/sch_ets_tests.sh b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh index 08240d3e3c87..79d837a2868a 100644 --- a/tools/testing/selftests/net/forwarding/sch_ets_tests.sh +++ b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh @@ -224,3 +224,11 @@ ets_test_dwrr() ets_set_dwrr_two_bands xfail_on_slow ets_dwrr_test_01 } + +ets_test_plug() +{ + ets_change_qdisc $put 2 "3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3" "1514 1514" + tc qdisc add dev $put handle 20: parent 10:4 plug + start_traffic_pktsize 100 $h1.10 192.0.2.1 192.0.2.2 00:c1:a0:c1:a0:00 "-c 1" + ets_qdisc_setup $put 2 +} diff --git a/tools/testing/selftests/net/forwarding/tc_flower.sh b/tools/testing/selftests/net/forwarding/tc_flower.sh index b1daad19b01e..b58909a93112 100755 --- a/tools/testing/selftests/net/forwarding/tc_flower.sh +++ b/tools/testing/selftests/net/forwarding/tc_flower.sh @@ -6,7 +6,7 @@ ALL_TESTS="match_dst_mac_test match_src_mac_test match_dst_ip_test \ match_ip_tos_test match_indev_test match_ip_ttl_test match_mpls_label_test \ match_mpls_tc_test match_mpls_bos_test match_mpls_ttl_test \ - match_mpls_lse_test" + match_mpls_lse_test match_erspan_opts_test" NUM_NETIFS=2 source tc_common.sh source lib.sh @@ -676,6 +676,56 @@ match_mpls_lse_test() log_test "mpls lse match ($tcflags)" } +match_erspan_opts_test() +{ + RET=0 + + check_tc_erspan_support $h2 || return 0 + + # h1 erspan setup + tunnel_create erspan1 erspan 192.0.2.1 192.0.2.2 dev $h1 seq key 1001 \ + tos C ttl 64 erspan_ver 1 erspan 6789 # ERSPAN Type II + tunnel_create erspan2 erspan 192.0.2.1 192.0.2.2 dev $h1 seq key 1002 \ + tos C ttl 64 erspan_ver 2 erspan_dir egress erspan_hwid 63 \ + # ERSPAN Type III + ip link set dev erspan1 master v$h1 + ip link set dev erspan2 master v$h1 + # h2 erspan setup + ip link add ep-ex type erspan ttl 64 external # To collect tunnel info + ip link set ep-ex up + ip link set dev ep-ex master v$h2 + tc qdisc add dev ep-ex clsact + + # ERSPAN Type II [decap direction] + tc filter add dev ep-ex ingress protocol ip handle 101 flower \ + $tcflags enc_src_ip 192.0.2.1 enc_dst_ip 192.0.2.2 \ + enc_key_id 1001 erspan_opts 1:6789:0:0 \ + action drop + # ERSPAN Type III [decap direction] + tc filter add dev ep-ex ingress protocol ip handle 102 flower \ + $tcflags enc_src_ip 192.0.2.1 enc_dst_ip 192.0.2.2 \ + enc_key_id 1002 erspan_opts 2:0:1:63 action drop + + ep1mac=$(mac_get erspan1) + $MZ erspan1 -c 1 -p 64 -a $ep1mac -b $h2mac -t ip -q + tc_check_packets "dev ep-ex ingress" 101 1 + check_err $? "ERSPAN Type II" + + ep2mac=$(mac_get erspan2) + $MZ erspan2 -c 1 -p 64 -a $ep1mac -b $h2mac -t ip -q + tc_check_packets "dev ep-ex ingress" 102 1 + check_err $? "ERSPAN Type III" + + # h2 erspan cleanup + tc qdisc del dev ep-ex clsact + tunnel_destroy ep-ex + # h1 erspan cleanup + tunnel_destroy erspan2 # ERSPAN Type III + tunnel_destroy erspan1 # ERSPAN Type II + + log_test "erspan_opts match ($tcflags)" +} + setup_prepare() { h1=${NETIFS[p1]} diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh new file mode 100755 index 000000000000..462db0b603e7 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh @@ -0,0 +1,771 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# +-----------------------------------------+ +# | + $h1.10 + $h1.20 | +# | | 192.0.2.1/28 | 2001:db8:1::1/64 | +# | \________ ________/ | +# | \ / | +# | + $h1 H1 (vrf) | +# +-----------|-----------------------------+ +# | +# +-----------|----------------------------------------------------------------+ +# | +---------|--------------------------------------+ SWITCH (main vrf) | +# | | + $swp1 BR1 (802.1q) | | +# | | vid 10 20 | | +# | | | | +# | | + vx10 (vxlan) + vx20 (vxlan) | + lo10 (dummy) | +# | | local 192.0.2.100 local 2001:db8:4::1 | 192.0.2.100/28 | +# | | group 233.252.0.1 group ff0e::1:2:3 | 2001:db8:4::1/64 | +# | | id 1000 id 2000 | | +# | | vid 10 pvid untagged vid 20 pvid untagged | | +# | +------------------------------------------------+ | +# | | +# | + $swp2 $swp3 + | +# | | 192.0.2.33/28 192.0.2.65/28 | | +# | | 2001:db8:2::1/64 2001:db8:3::1/64 | | +# | | | | +# +---|--------------------------------------------------------------------|---+ +# | | +# +---|--------------------------------+ +--------------------------------|---+ +# | | H2 (vrf) | | H3 (vrf) | | +# | +-|----------------------------+ | | +-----------------------------|-+ | +# | | + $h2 BR2 (802.1d) | | | | BR3 (802.1d) $h3 + | | +# | | | | | | | | +# | | + v1$h2 (veth) | | | | v1$h3 (veth) + | | +# | +-|----------------------------+ | | +-----------------------------|-+ | +# | | | | | | +# +---|--------------------------------+ +--------------------------------|---+ +# | | +# +---|--------------------------------+ +--------------------------------|---+ +# | + v2$h2 (veth) NS2 (netns) | | NS3 (netns) v2$h3 (veth) + | +# | 192.0.2.34/28 | | 192.0.2.66/28 | +# | 2001:db8:2::2/64 | | 2001:db8:3::2/64 | +# | | | | +# | +--------------------------------+ | | +--------------------------------+ | +# | | BR1 (802.1q) | | | | BR1 (802.1q) | | +# | | + vx10 (vxlan) | | | | + vx10 (vxlan) | | +# | | local 192.0.2.34 | | | | local 192.0.2.50 | | +# | | group 233.252.0.1 dev v2$h2 | | | | group 233.252.0.1 dev v2$h3 | | +# | | id 1000 dstport $VXPORT | | | | id 1000 dstport $VXPORT | | +# | | vid 10 pvid untagged | | | | vid 10 pvid untagged | | +# | | | | | | | | +# | | + vx20 (vxlan) | | | | + vx20 (vxlan) | | +# | | local 2001:db8:2::2 | | | | local 2001:db8:3::2 | | +# | | group ff0e::1:2:3 dev v2$h2 | | | | group ff0e::1:2:3 dev v2$h3 | | +# | | id 2000 dstport $VXPORT | | | | id 2000 dstport $VXPORT | | +# | | vid 20 pvid untagged | | | | vid 20 pvid untagged | | +# | | | | | | | | +# | | + w1 (veth) | | | | + w1 (veth) | | +# | | | vid 10 20 | | | | | vid 10 20 | | +# | +--|-----------------------------+ | | +--|-----------------------------+ | +# | | | | | | +# | +--|-----------------------------+ | | +--|-----------------------------+ | +# | | + w2 (veth) VW2 (vrf) | | | | + w2 (veth) VW2 (vrf) | | +# | | |\ | | | | |\ | | +# | | | + w2.10 | | | | | + w2.10 | | +# | | | 192.0.2.3/28 | | | | | 192.0.2.4/28 | | +# | | | | | | | | | | +# | | + w2.20 | | | | + w2.20 | | +# | | 2001:db8:1::3/64 | | | | 2001:db8:1::4/64 | | +# | +--------------------------------+ | | +--------------------------------+ | +# +------------------------------------+ +------------------------------------+ +# +#shellcheck disable=SC2317 # SC doesn't see our uses of functions. + +: "${VXPORT:=4789}" +export VXPORT + +: "${GROUP4:=233.252.0.1}" +export GROUP4 + +: "${GROUP6:=ff0e::1:2:3}" +export GROUP6 + +: "${IPMR:=lo10}" + +ALL_TESTS=" + ipv4_nomcroute + ipv4_mcroute + ipv4_mcroute_changelink + ipv4_mcroute_starg + ipv4_mcroute_noroute + ipv4_mcroute_fdb + ipv4_mcroute_fdb_oif0 + ipv4_mcroute_fdb_oif0_sep + + ipv6_nomcroute + ipv6_mcroute + ipv6_mcroute_changelink + ipv6_mcroute_starg + ipv6_mcroute_noroute + ipv6_mcroute_fdb + ipv6_mcroute_fdb_oif0 + + ipv4_nomcroute_rx + ipv4_mcroute_rx + ipv4_mcroute_starg_rx + ipv4_mcroute_fdb_oif0_sep_rx + ipv4_mcroute_fdb_sep_rx + + ipv6_nomcroute_rx + ipv6_mcroute_rx + ipv6_mcroute_starg_rx + ipv6_mcroute_fdb_sep_rx +" + +NUM_NETIFS=6 +source lib.sh + +h1_create() +{ + simple_if_init "$h1" + defer simple_if_fini "$h1" + + ip_link_add "$h1.10" master "v$h1" link "$h1" type vlan id 10 + ip_link_set_up "$h1.10" + ip_addr_add "$h1.10" 192.0.2.1/28 + + ip_link_add "$h1.20" master "v$h1" link "$h1" type vlan id 20 + ip_link_set_up "$h1.20" + ip_addr_add "$h1.20" 2001:db8:1::1/64 +} + +install_capture() +{ + local dev=$1; shift + + tc qdisc add dev "$dev" clsact + defer tc qdisc del dev "$dev" clsact + + tc filter add dev "$dev" ingress proto ip pref 104 \ + flower skip_hw ip_proto udp dst_port "$VXPORT" \ + action pass + defer tc filter del dev "$dev" ingress proto ip pref 104 + + tc filter add dev "$dev" ingress proto ipv6 pref 106 \ + flower skip_hw ip_proto udp dst_port "$VXPORT" \ + action pass + defer tc filter del dev "$dev" ingress proto ipv6 pref 106 +} + +h2_create() +{ + # $h2 + ip_link_set_up "$h2" + + # H2 + vrf_create "v$h2" + defer vrf_destroy "v$h2" + + ip_link_set_up "v$h2" + + # br2 + ip_link_add br2 type bridge vlan_filtering 0 mcast_snooping 0 + ip_link_set_master br2 "v$h2" + ip_link_set_up br2 + + # $h2 + ip_link_set_master "$h2" br2 + install_capture "$h2" + + # v1$h2 + ip_link_set_up "v1$h2" + ip_link_set_master "v1$h2" br2 +} + +h3_create() +{ + # $h3 + ip_link_set_up "$h3" + + # H3 + vrf_create "v$h3" + defer vrf_destroy "v$h3" + + ip_link_set_up "v$h3" + + # br3 + ip_link_add br3 type bridge vlan_filtering 0 mcast_snooping 0 + ip_link_set_master br3 "v$h3" + ip_link_set_up br3 + + # $h3 + ip_link_set_master "$h3" br3 + install_capture "$h3" + + # v1$h3 + ip_link_set_up "v1$h3" + ip_link_set_master "v1$h3" br3 +} + +switch_create() +{ + local swp1_mac + + # br1 + swp1_mac=$(mac_get "$swp1") + ip_link_add br1 type bridge vlan_filtering 1 \ + vlan_default_pvid 0 mcast_snooping 0 + ip_link_set_addr br1 "$swp1_mac" + ip_link_set_up br1 + + # A dummy to force the IPv6 OIF=0 test to install a suitable MC route on + # $IPMR to be deterministic. Also used for the IPv6 RX!=TX ping test. + ip_link_add "X$IPMR" up type dummy + + # IPMR + ip_link_add "$IPMR" up type dummy + ip_addr_add "$IPMR" 192.0.2.100/28 + ip_addr_add "$IPMR" 2001:db8:4::1/64 + + # $swp1 + ip_link_set_up "$swp1" + ip_link_set_master "$swp1" br1 + bridge_vlan_add vid 10 dev "$swp1" + bridge_vlan_add vid 20 dev "$swp1" + + # $swp2 + ip_link_set_up "$swp2" + ip_addr_add "$swp2" 192.0.2.33/28 + ip_addr_add "$swp2" 2001:db8:2::1/64 + + # $swp3 + ip_link_set_up "$swp3" + ip_addr_add "$swp3" 192.0.2.65/28 + ip_addr_add "$swp3" 2001:db8:3::1/64 +} + +vx_create() +{ + local name=$1; shift + local vid=$1; shift + + ip_link_add "$name" up type vxlan dstport "$VXPORT" \ + nolearning noudpcsum tos inherit ttl 16 \ + "$@" + ip_link_set_master "$name" br1 + bridge_vlan_add vid "$vid" dev "$name" pvid untagged +} +export -f vx_create + +vx_wait() +{ + # Wait for all the ARP, IGMP etc. noise to settle down so that the + # tunnel is clear for measurements. + sleep 10 +} + +vx10_create() +{ + vx_create vx10 10 id 1000 "$@" +} +export -f vx10_create + +vx20_create() +{ + vx_create vx20 20 id 2000 "$@" +} +export -f vx20_create + +vx10_create_wait() +{ + vx10_create "$@" + vx_wait +} + +vx20_create_wait() +{ + vx20_create "$@" + vx_wait +} + +ns_init_common() +{ + local ns=$1; shift + local if_in=$1; shift + local ipv4_in=$1; shift + local ipv6_in=$1; shift + local ipv4_host=$1; shift + local ipv6_host=$1; shift + + # v2$h2 / v2$h3 + ip_link_set_up "$if_in" + ip_addr_add "$if_in" "$ipv4_in" + ip_addr_add "$if_in" "$ipv6_in" + + # br1 + ip_link_add br1 type bridge vlan_filtering 1 \ + vlan_default_pvid 0 mcast_snooping 0 + ip_link_set_up br1 + + # vx10, vx20 + vx10_create local "${ipv4_in%/*}" group "$GROUP4" dev "$if_in" + vx20_create local "${ipv6_in%/*}" group "$GROUP6" dev "$if_in" + + # w1 + ip_link_add w1 type veth peer name w2 + ip_link_set_master w1 br1 + ip_link_set_up w1 + bridge_vlan_add vid 10 dev w1 + bridge_vlan_add vid 20 dev w1 + + # w2 + simple_if_init w2 + defer simple_if_fini w2 + + # w2.10 + ip_link_add w2.10 master vw2 link w2 type vlan id 10 + ip_link_set_up w2.10 + ip_addr_add w2.10 "$ipv4_host" + + # w2.20 + ip_link_add w2.20 master vw2 link w2 type vlan id 20 + ip_link_set_up w2.20 + ip_addr_add w2.20 "$ipv6_host" +} +export -f ns_init_common + +ns2_create() +{ + # NS2 + ip netns add ns2 + defer ip netns del ns2 + + # v2$h2 + ip link set dev "v2$h2" netns ns2 + defer ip -n ns2 link set dev "v2$h2" netns 1 + + in_ns ns2 \ + ns_init_common ns2 "v2$h2" \ + 192.0.2.34/28 2001:db8:2::2/64 \ + 192.0.2.3/28 2001:db8:1::3/64 +} + +ns3_create() +{ + # NS3 + ip netns add ns3 + defer ip netns del ns3 + + # v2$h3 + ip link set dev "v2$h3" netns ns3 + defer ip -n ns3 link set dev "v2$h3" netns 1 + + ip -n ns3 link set dev "v2$h3" up + + in_ns ns3 \ + ns_init_common ns3 "v2$h3" \ + 192.0.2.66/28 2001:db8:3::2/64 \ + 192.0.2.4/28 2001:db8:1::4/64 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + defer vrf_cleanup + + forwarding_enable + defer forwarding_restore + + ip_link_add "v1$h2" type veth peer name "v2$h2" + ip_link_add "v1$h3" type veth peer name "v2$h3" + + h1_create + h2_create + h3_create + switch_create + ns2_create + ns3_create +} + +adf_install_broken_sg() +{ + adf_mcd_start "$IPMR" || exit "$EXIT_STATUS" + + mc_cli add "$swp2" 192.0.2.100 "$GROUP4" "$swp1" "$swp3" + defer mc_cli remove "$swp2" 192.0.2.100 "$GROUP4" "$swp1" "$swp3" + + mc_cli add "$swp2" 2001:db8:4::1 "$GROUP6" "$swp1" "$swp3" + defer mc_cli remove "$swp2" 2001:db8:4::1 "$GROUP6" "$swp1" "$swp3" +} + +adf_install_rx() +{ + mc_cli add "$swp2" 0.0.0.0 "$GROUP4" "$IPMR" + defer mc_cli remove "$swp2" 0.0.0.0 "$GROUP4" lo10 + + mc_cli add "$swp3" 0.0.0.0 "$GROUP4" "$IPMR" + defer mc_cli remove "$swp3" 0.0.0.0 "$GROUP4" lo10 + + mc_cli add "$swp2" :: "$GROUP6" "$IPMR" + defer mc_cli remove "$swp2" :: "$GROUP6" lo10 + + mc_cli add "$swp3" :: "$GROUP6" "$IPMR" + defer mc_cli remove "$swp3" :: "$GROUP6" lo10 +} + +adf_install_sg() +{ + adf_mcd_start "$IPMR" || exit "$EXIT_STATUS" + + mc_cli add "$IPMR" 192.0.2.100 "$GROUP4" "$swp2" "$swp3" + defer mc_cli remove "$IPMR" 192.0.2.33 "$GROUP4" "$swp2" "$swp3" + + mc_cli add "$IPMR" 2001:db8:4::1 "$GROUP6" "$swp2" "$swp3" + defer mc_cli remove "$IPMR" 2001:db8:4::1 "$GROUP6" "$swp2" "$swp3" + + adf_install_rx +} + +adf_install_sg_sep() +{ + adf_mcd_start lo || exit "$EXIT_STATUS" + + mc_cli add lo 192.0.2.120 "$GROUP4" "$swp2" "$swp3" + defer mc_cli remove lo 192.0.2.120 "$GROUP4" "$swp2" "$swp3" + + mc_cli add lo 2001:db8:5::1 "$GROUP6" "$swp2" "$swp3" + defer mc_cli remove lo 2001:db8:5::1 "$GROUP6" "$swp2" "$swp3" +} + +adf_install_sg_sep_rx() +{ + local lo=$1; shift + + adf_mcd_start "$IPMR" "$lo" || exit "$EXIT_STATUS" + + mc_cli add "$lo" 192.0.2.120 "$GROUP4" "$swp2" "$swp3" + defer mc_cli remove "$lo" 192.0.2.120 "$GROUP4" "$swp2" "$swp3" + + mc_cli add "$lo" 2001:db8:5::1 "$GROUP6" "$swp2" "$swp3" + defer mc_cli remove "$lo" 2001:db8:5::1 "$GROUP6" "$swp2" "$swp3" + + adf_install_rx +} + +adf_install_starg() +{ + adf_mcd_start "$IPMR" || exit "$EXIT_STATUS" + + mc_cli add "$IPMR" 0.0.0.0 "$GROUP4" "$swp2" "$swp3" + defer mc_cli remove "$IPMR" 0.0.0.0 "$GROUP4" "$swp2" "$swp3" + + mc_cli add "$IPMR" :: "$GROUP6" "$swp2" "$swp3" + defer mc_cli remove "$IPMR" :: "$GROUP6" "$swp2" "$swp3" + + adf_install_rx +} + +do_packets_v4() +{ + local mac + + mac=$(mac_get "$h2") + "$MZ" "$h1" -Q 10 -c 10 -d 100msec -p 64 -a own -b "$mac" \ + -A 192.0.2.1 -B 192.0.2.2 -t udp sp=1234,dp=2345 -q +} + +do_packets_v6() +{ + local mac + + mac=$(mac_get "$h2") + "$MZ" -6 "$h1" -Q 20 -c 10 -d 100msec -p 64 -a own -b "$mac" \ + -A 2001:db8:1::1 -B 2001:db8:1::2 -t udp sp=1234,dp=2345 -q +} + +do_test() +{ + local ipv=$1; shift + local expect_h2=$1; shift + local expect_h3=$1; shift + local what=$1; shift + + local pref=$((100 + ipv)) + local t0_h2 + local t0_h3 + local t1_h2 + local t1_h3 + local d_h2 + local d_h3 + + RET=0 + + t0_h2=$(tc_rule_stats_get "$h2" "$pref" ingress) + t0_h3=$(tc_rule_stats_get "$h3" "$pref" ingress) + + "do_packets_v$ipv" + sleep 1 + + t1_h2=$(tc_rule_stats_get "$h2" "$pref" ingress) + t1_h3=$(tc_rule_stats_get "$h3" "$pref" ingress) + + d_h2=$((t1_h2 - t0_h2)) + d_h3=$((t1_h3 - t0_h3)) + + ((d_h2 == expect_h2)) + check_err $? "Expected $expect_h2 packets on H2, got $d_h2" + + ((d_h3 == expect_h3)) + check_err $? "Expected $expect_h3 packets on H3, got $d_h3" + + log_test "VXLAN MC flood $what" +} + +ipv4_do_test_rx() +{ + local h3_should_fail=$1; shift + local what=$1; shift + + RET=0 + + ping_do "$h1.10" 192.0.2.3 + check_err $? "H2 should respond" + + ping_do "$h1.10" 192.0.2.4 + check_err_fail "$h3_should_fail" $? "H3 responds" + + log_test "VXLAN MC flood $what" +} + +ipv6_do_test_rx() +{ + local h3_should_fail=$1; shift + local what=$1; shift + + RET=0 + + ping6_do "$h1.20" 2001:db8:1::3 + check_err $? "H2 should respond" + + ping6_do "$h1.20" 2001:db8:1::4 + check_err_fail "$h3_should_fail" $? "H3 responds" + + log_test "VXLAN MC flood $what" +} + +ipv4_nomcroute() +{ + # Install a misleading (S,G) rule to attempt to trick the system into + # pushing the packets elsewhere. + adf_install_broken_sg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$swp2" + do_test 4 10 0 "IPv4 nomcroute" +} + +ipv6_nomcroute() +{ + # Like for IPv4, install a misleading (S,G). + adf_install_broken_sg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$swp2" + do_test 6 10 0 "IPv6 nomcroute" +} + +ipv4_nomcroute_rx() +{ + vx10_create local 192.0.2.100 group "$GROUP4" dev "$swp2" + ipv4_do_test_rx 1 "IPv4 nomcroute ping" +} + +ipv6_nomcroute_rx() +{ + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$swp2" + ipv6_do_test_rx 1 "IPv6 nomcroute ping" +} + +ipv4_mcroute() +{ + adf_install_sg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + do_test 4 10 10 "IPv4 mcroute" +} + +ipv6_mcroute() +{ + adf_install_sg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + do_test 6 10 10 "IPv6 mcroute" +} + +ipv4_mcroute_rx() +{ + adf_install_sg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + ipv4_do_test_rx 0 "IPv4 mcroute ping" +} + +ipv6_mcroute_rx() +{ + adf_install_sg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + ipv6_do_test_rx 0 "IPv6 mcroute ping" +} + +ipv4_mcroute_changelink() +{ + adf_install_sg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" + ip link set dev vx10 type vxlan mcroute + sleep 1 + do_test 4 10 10 "IPv4 mcroute changelink" +} + +ipv6_mcroute_changelink() +{ + adf_install_sg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + ip link set dev vx20 type vxlan mcroute + sleep 1 + do_test 6 10 10 "IPv6 mcroute changelink" +} + +ipv4_mcroute_starg() +{ + adf_install_starg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + do_test 4 10 10 "IPv4 mcroute (*,G)" +} + +ipv6_mcroute_starg() +{ + adf_install_starg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + do_test 6 10 10 "IPv6 mcroute (*,G)" +} + +ipv4_mcroute_starg_rx() +{ + adf_install_starg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + ipv4_do_test_rx 0 "IPv4 mcroute (*,G) ping" +} + +ipv6_mcroute_starg_rx() +{ + adf_install_starg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + ipv6_do_test_rx 0 "IPv6 mcroute (*,G) ping" +} + +ipv4_mcroute_noroute() +{ + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + do_test 4 0 0 "IPv4 mcroute, no route" +} + +ipv6_mcroute_noroute() +{ + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + do_test 6 0 0 "IPv6 mcroute, no route" +} + +ipv4_mcroute_fdb() +{ + adf_install_sg + vx10_create_wait local 192.0.2.100 dev "$IPMR" mcroute + bridge fdb add dev vx10 \ + 00:00:00:00:00:00 self static dst "$GROUP4" via "$IPMR" + do_test 4 10 10 "IPv4 mcroute FDB" +} + +ipv6_mcroute_fdb() +{ + adf_install_sg + vx20_create_wait local 2001:db8:4::1 dev "$IPMR" mcroute + bridge -6 fdb add dev vx20 \ + 00:00:00:00:00:00 self static dst "$GROUP6" via "$IPMR" + do_test 6 10 10 "IPv6 mcroute FDB" +} + +# Use FDB to configure VXLAN in a way where oif=0 for purposes of FIB lookup. +ipv4_mcroute_fdb_oif0() +{ + adf_install_sg + vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + bridge fdb del dev vx10 00:00:00:00:00:00 + bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" + do_test 4 10 10 "IPv4 mcroute oif=0" +} + +ipv6_mcroute_fdb_oif0() +{ + # The IPv6 tunnel lookup does not fall back to selection by source + # address. Instead it just does a FIB match, and that would find one of + # the several ff00::/8 multicast routes -- each device has one. In order + # to reliably force the $IPMR device, add a /128 route for the + # destination group address. + ip -6 route add table local multicast "$GROUP6/128" dev "$IPMR" + defer ip -6 route del table local multicast "$GROUP6/128" dev "$IPMR" + + adf_install_sg + vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + bridge -6 fdb del dev vx20 00:00:00:00:00:00 + bridge -6 fdb add dev vx20 00:00:00:00:00:00 self static dst "$GROUP6" + do_test 6 10 10 "IPv6 mcroute oif=0" +} + +# In oif=0 test as above, have FIB lookup resolve to loopback instead of IPMR. +# This doesn't work with IPv6 -- a MC route on lo would be marked as RTF_REJECT. +ipv4_mcroute_fdb_oif0_sep() +{ + adf_install_sg_sep + + ip_addr_add lo 192.0.2.120/28 + vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + bridge fdb del dev vx10 00:00:00:00:00:00 + bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" + do_test 4 10 10 "IPv4 mcroute TX!=RX oif=0" +} + +ipv4_mcroute_fdb_oif0_sep_rx() +{ + adf_install_sg_sep_rx lo + + ip_addr_add lo 192.0.2.120/28 + vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + bridge fdb del dev vx10 00:00:00:00:00:00 + bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" + ipv4_do_test_rx 0 "IPv4 mcroute TX!=RX oif=0 ping" +} + +ipv4_mcroute_fdb_sep_rx() +{ + adf_install_sg_sep_rx lo + + ip_addr_add lo 192.0.2.120/28 + vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + bridge fdb del dev vx10 00:00:00:00:00:00 + bridge fdb add \ + dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" via lo + ipv4_do_test_rx 0 "IPv4 mcroute TX!=RX ping" +} + +ipv6_mcroute_fdb_sep_rx() +{ + adf_install_sg_sep_rx "X$IPMR" + + ip_addr_add "X$IPMR" 2001:db8:5::1/64 + vx20_create_wait local 2001:db8:5::1 group "$GROUP6" dev "$IPMR" mcroute + bridge -6 fdb del dev vx20 00:00:00:00:00:00 + bridge -6 fdb add dev vx20 00:00:00:00:00:00 \ + self static dst "$GROUP6" via "X$IPMR" + ipv6_do_test_rx 0 "IPv6 mcroute TX!=RX ping" +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/gre_ipv6_lladdr.sh b/tools/testing/selftests/net/gre_ipv6_lladdr.sh index 5b34f6e1f831..48eb999a3120 100755 --- a/tools/testing/selftests/net/gre_ipv6_lladdr.sh +++ b/tools/testing/selftests/net/gre_ipv6_lladdr.sh @@ -24,7 +24,10 @@ setup_basenet() ip -netns "${NS0}" address add dev lo 2001:db8::10/64 nodad } -# Check if network device has an IPv6 link-local address assigned. +# Check the IPv6 configuration of a network device. +# +# We currently check the generation of the link-local IPv6 address and the +# creation of the ff00::/8 multicast route. # # Parameters: # @@ -35,7 +38,7 @@ setup_basenet() # a link-local address) # * $4: The user visible name for the scenario being tested # -check_ipv6_ll_addr() +check_ipv6_device_config() { local DEV="$1" local EXTRA_MATCH="$2" @@ -45,7 +48,11 @@ check_ipv6_ll_addr() RET=0 set +e ip -netns "${NS0}" -6 address show dev "${DEV}" scope link | grep "fe80::" | grep -q "${EXTRA_MATCH}" - check_err_fail "${XRET}" $? "" + check_err_fail "${XRET}" $? "IPv6 link-local address generation" + + ip -netns "${NS0}" -6 route show table local type multicast ff00::/8 proto kernel | grep -q "${DEV}" + check_err_fail 0 $? "IPv6 multicast route creation" + log_test "${MSG}" set -e } @@ -102,20 +109,20 @@ test_gre_device() ;; esac - # Check that IPv6 link-local address is generated when device goes up + # Check the IPv6 device configuration when it goes up ip netns exec "${NS0}" sysctl -qw net.ipv6.conf.gretest.addr_gen_mode="${ADDR_GEN_MODE}" ip -netns "${NS0}" link set dev gretest up - check_ipv6_ll_addr gretest "${MATCH_REGEXP}" "${XRET}" "config: ${MSG}" + check_ipv6_device_config gretest "${MATCH_REGEXP}" "${XRET}" "config: ${MSG}" # Now disable link-local address generation ip -netns "${NS0}" link set dev gretest down ip netns exec "${NS0}" sysctl -qw net.ipv6.conf.gretest.addr_gen_mode=1 ip -netns "${NS0}" link set dev gretest up - # Check that link-local address generation works when re-enabled while - # the device is already up + # Check the IPv6 device configuration when link-local address + # generation is re-enabled while the device is already up ip netns exec "${NS0}" sysctl -qw net.ipv6.conf.gretest.addr_gen_mode="${ADDR_GEN_MODE}" - check_ipv6_ll_addr gretest "${MATCH_REGEXP}" "${XRET}" "update: ${MSG}" + check_ipv6_device_config gretest "${MATCH_REGEXP}" "${XRET}" "update: ${MSG}" ip -netns "${NS0}" link del dev gretest } @@ -126,7 +133,7 @@ test_gre4() local MODE for GRE_TYPE in "gre" "gretap"; do - printf "\n####\nTesting IPv6 link-local address generation on ${GRE_TYPE} devices\n####\n\n" + printf "\n####\nTesting IPv6 configuration of ${GRE_TYPE} devices\n####\n\n" for MODE in "eui64" "none" "stable-privacy" "random"; do test_gre_device "${GRE_TYPE}" 192.0.2.10 192.0.2.11 "${MODE}" @@ -142,7 +149,7 @@ test_gre6() local MODE for GRE_TYPE in "ip6gre" "ip6gretap"; do - printf "\n####\nTesting IPv6 link-local address generation on ${GRE_TYPE} devices\n####\n\n" + printf "\n####\nTesting IPv6 configuration of ${GRE_TYPE} devices\n####\n\n" for MODE in "eui64" "none" "stable-privacy" "random"; do test_gre_device "${GRE_TYPE}" 2001:db8::10 2001:db8::11 "${MODE}" diff --git a/tools/testing/selftests/net/ipv6_force_forwarding.sh b/tools/testing/selftests/net/ipv6_force_forwarding.sh new file mode 100755 index 000000000000..bf0243366caa --- /dev/null +++ b/tools/testing/selftests/net/ipv6_force_forwarding.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test IPv6 force_forwarding interface property +# +# This test verifies that the force_forwarding property works correctly: +# - When global forwarding is disabled, packets are not forwarded normally +# - When force_forwarding is enabled on an interface, packets are forwarded +# regardless of the global forwarding setting + +source lib.sh + +cleanup() { + cleanup_ns $ns1 $ns2 $ns3 +} + +trap cleanup EXIT + +setup_test() { + # Create three namespaces: sender, router, receiver + setup_ns ns1 ns2 ns3 + + # Create veth pairs: ns1 <-> ns2 <-> ns3 + ip link add name veth12 type veth peer name veth21 + ip link add name veth23 type veth peer name veth32 + + # Move interfaces to namespaces + ip link set veth12 netns $ns1 + ip link set veth21 netns $ns2 + ip link set veth23 netns $ns2 + ip link set veth32 netns $ns3 + + # Configure interfaces + ip -n $ns1 addr add 2001:db8:1::1/64 dev veth12 nodad + ip -n $ns2 addr add 2001:db8:1::2/64 dev veth21 nodad + ip -n $ns2 addr add 2001:db8:2::1/64 dev veth23 nodad + ip -n $ns3 addr add 2001:db8:2::2/64 dev veth32 nodad + + # Bring up interfaces + ip -n $ns1 link set veth12 up + ip -n $ns2 link set veth21 up + ip -n $ns2 link set veth23 up + ip -n $ns3 link set veth32 up + + # Add routes + ip -n $ns1 route add 2001:db8:2::/64 via 2001:db8:1::2 + ip -n $ns3 route add 2001:db8:1::/64 via 2001:db8:2::1 + + # Disable global forwarding + ip netns exec $ns2 sysctl -qw net.ipv6.conf.all.forwarding=0 +} + +test_force_forwarding() { + local ret=0 + + echo "TEST: force_forwarding functionality" + + # Check if force_forwarding sysctl exists + if ! ip netns exec $ns2 test -f /proc/sys/net/ipv6/conf/veth21/force_forwarding; then + echo "SKIP: force_forwarding not available" + return $ksft_skip + fi + + # Test 1: Without force_forwarding, ping should fail + ip netns exec $ns2 sysctl -qw net.ipv6.conf.veth21.force_forwarding=0 + ip netns exec $ns2 sysctl -qw net.ipv6.conf.veth23.force_forwarding=0 + + if ip netns exec $ns1 ping -6 -c 1 -W 2 2001:db8:2::2 &>/dev/null; then + echo "FAIL: ping succeeded when forwarding disabled" + ret=1 + else + echo "PASS: forwarding disabled correctly" + fi + + # Test 2: With force_forwarding enabled, ping should succeed + ip netns exec $ns2 sysctl -qw net.ipv6.conf.veth21.force_forwarding=1 + ip netns exec $ns2 sysctl -qw net.ipv6.conf.veth23.force_forwarding=1 + + if ip netns exec $ns1 ping -6 -c 1 -W 2 2001:db8:2::2 &>/dev/null; then + echo "PASS: force_forwarding enabled forwarding" + else + echo "FAIL: ping failed with force_forwarding enabled" + ret=1 + fi + + return $ret +} + +echo "IPv6 force_forwarding test" +echo "==========================" + +setup_test +test_force_forwarding +ret=$? + +if [ $ret -eq 0 ]; then + echo "OK" + exit 0 +elif [ $ret -eq $ksft_skip ]; then + echo "SKIP" + exit $ksft_skip +else + echo "FAIL" + exit 1 +fi diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index 006fdadcc4b9..c7add0dc4c60 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -240,6 +240,29 @@ create_netdevsim() { echo nsim$id } +create_netdevsim_port() { + local nsim_id="$1" + local ns="$2" + local port_id="$3" + local perm_addr="$4" + local orig_dev + local new_dev + local nsim_path + + nsim_path="/sys/bus/netdevsim/devices/netdevsim$nsim_id" + + echo "$port_id $perm_addr" | ip netns exec "$ns" tee "$nsim_path"/new_port > /dev/null || return 1 + + orig_dev=$(ip netns exec "$ns" find "$nsim_path"/net/ -maxdepth 1 -name 'e*' | tail -n 1) + orig_dev=$(basename "$orig_dev") + new_dev="nsim${nsim_id}p$port_id" + + ip -netns "$ns" link set dev "$orig_dev" name "$new_dev" + ip -netns "$ns" link set dev "$new_dev" up + + echo "$new_dev" +} + # Remove netdevsim with given id. cleanup_netdevsim() { local id="$1" @@ -312,7 +335,7 @@ log_test_result() local test_name=$1; shift local opt_str=$1; shift local result=$1; shift - local retmsg=$1; shift + local retmsg=$1 printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result" if [[ $retmsg ]]; then @@ -547,13 +570,19 @@ ip_link_set_addr() defer ip link set dev "$name" address "$old_addr" } -ip_link_is_up() +ip_link_has_flag() { local name=$1; shift + local flag=$1; shift local state=$(ip -j link show "$name" | - jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"') - [[ $state == "UP" ]] + jq --arg flag "$flag" 'any(.[].flags.[]; . == $flag)') + [[ $state == true ]] +} + +ip_link_is_up() +{ + ip_link_has_flag "$1" UP } ip_link_set_up() diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py index 8697bd27dc30..02be28dcc089 100644 --- a/tools/testing/selftests/net/lib/py/__init__.py +++ b/tools/testing/selftests/net/lib/py/__init__.py @@ -6,4 +6,4 @@ from .netns import NetNS, NetNSEnter from .nsim import * from .utils import * from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily -from .ynl import NetshaperFamily +from .ynl import NetshaperFamily, DevlinkFamily diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py index 61287c203b6e..8e35ed12ed9e 100644 --- a/tools/testing/selftests/net/lib/py/ksft.py +++ b/tools/testing/selftests/net/lib/py/ksft.py @@ -32,6 +32,7 @@ class KsftTerminate(KeyboardInterrupt): def ksft_pr(*objs, **kwargs): + kwargs["flush"] = True print("#", *objs, **kwargs) @@ -139,7 +140,7 @@ def ktap_result(ok, cnt=1, case="", comment=""): res += "." + str(case.__name__) if comment: res += " # " + comment - print(res) + print(res, flush=True) def ksft_flush_defer(): @@ -227,8 +228,8 @@ def ksft_run(cases=None, globs=None, case_pfx=None, args=()): totals = {"pass": 0, "fail": 0, "skip": 0, "xfail": 0} - print("TAP version 13") - print("1.." + str(len(cases))) + print("TAP version 13", flush=True) + print("1.." + str(len(cases)), flush=True) global KSFT_RESULT cnt = 0 diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py index 34470d65d871..f395c90fb0f1 100644 --- a/tools/testing/selftests/net/lib/py/utils.py +++ b/tools/testing/selftests/net/lib/py/utils.py @@ -175,6 +175,10 @@ def tool(name, args, json=None, ns=None, host=None): return cmd_obj +def bpftool(args, json=None, ns=None, host=None): + return tool('bpftool', args, json=json, ns=ns, host=host) + + def ip(args, json=None, ns=None, host=None): if ns: args = f'-netns {ns} ' + args @@ -185,6 +189,41 @@ def ethtool(args, json=None, ns=None, host=None): return tool('ethtool', args, json=json, ns=ns, host=host) +def bpftrace(expr, json=None, ns=None, host=None, timeout=None): + """ + Run bpftrace and return map data (if json=True). + The output of bpftrace is inconvenient, so the helper converts + to a dict indexed by map name, e.g.: + { + "@": { ... }, + "@map2": { ... }, + } + """ + cmd_arr = ['bpftrace'] + # Throw in --quiet if json, otherwise the output has two objects + if json: + cmd_arr += ['-f', 'json', '-q'] + if timeout: + expr += ' interval:s:' + str(timeout) + ' { exit(); }' + cmd_arr += ['-e', expr] + cmd_obj = cmd(cmd_arr, ns=ns, host=host, shell=False) + if json: + # bpftrace prints objects as lines + ret = {} + for l in cmd_obj.stdout.split('\n'): + if not l.strip(): + continue + one = _json.loads(l) + if one.get('type') != 'map': + continue + for k, v in one["data"].items(): + if k.startswith('@'): + k = k.lstrip('@') + ret[k] = v + return ret + return cmd_obj + + def rand_port(type=socket.SOCK_STREAM): """ Get a random unprivileged port. diff --git a/tools/testing/selftests/net/lib/py/ynl.py b/tools/testing/selftests/net/lib/py/ynl.py index 6329ae805abf..2b3a61ea3bfa 100644 --- a/tools/testing/selftests/net/lib/py/ynl.py +++ b/tools/testing/selftests/net/lib/py/ynl.py @@ -56,3 +56,8 @@ class NetshaperFamily(YnlFamily): def __init__(self, recv_size=0): super().__init__((SPEC_PATH / Path('net_shaper.yaml')).as_posix(), schema='', recv_size=recv_size) + +class DevlinkFamily(YnlFamily): + def __init__(self, recv_size=0): + super().__init__((SPEC_PATH / Path('devlink.yaml')).as_posix(), + schema='', recv_size=recv_size) diff --git a/tools/testing/selftests/net/lib/xdp_native.bpf.c b/tools/testing/selftests/net/lib/xdp_native.bpf.c new file mode 100644 index 000000000000..521ba38f2ddd --- /dev/null +++ b/tools/testing/selftests/net/lib/xdp_native.bpf.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ADJST_OFFSET 256 +#define MAX_PAYLOAD_LEN 5000 +#define MAX_HDR_LEN 64 + +enum { + XDP_MODE = 0, + XDP_PORT = 1, + XDP_ADJST_OFFSET = 2, + XDP_ADJST_TAG = 3, +} xdp_map_setup_keys; + +enum { + XDP_MODE_PASS = 0, + XDP_MODE_DROP = 1, + XDP_MODE_TX = 2, + XDP_MODE_TAIL_ADJST = 3, + XDP_MODE_HEAD_ADJST = 4, +} xdp_map_modes; + +enum { + STATS_RX = 0, + STATS_PASS = 1, + STATS_DROP = 2, + STATS_TX = 3, + STATS_ABORT = 4, +} xdp_stats; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 5); + __type(key, __u32); + __type(value, __s32); +} map_xdp_setup SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 5); + __type(key, __u32); + __type(value, __u64); +} map_xdp_stats SEC(".maps"); + +static __u32 min(__u32 a, __u32 b) +{ + return a < b ? a : b; +} + +static void record_stats(struct xdp_md *ctx, __u32 stat_type) +{ + __u64 *count; + + count = bpf_map_lookup_elem(&map_xdp_stats, &stat_type); + + if (count) + __sync_fetch_and_add(count, 1); +} + +static struct udphdr *filter_udphdr(struct xdp_md *ctx, __u16 port) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct udphdr *udph = NULL; + struct ethhdr *eth = data; + + if (data + sizeof(*eth) > data_end) + return NULL; + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = data + sizeof(*eth); + + if (iph + 1 > (struct iphdr *)data_end || + iph->protocol != IPPROTO_UDP) + return NULL; + + udph = (void *)eth + sizeof(*iph) + sizeof(*eth); + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6h = data + sizeof(*eth); + + if (ipv6h + 1 > (struct ipv6hdr *)data_end || + ipv6h->nexthdr != IPPROTO_UDP) + return NULL; + + udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth); + } else { + return NULL; + } + + if (udph + 1 > (struct udphdr *)data_end) + return NULL; + + if (udph->dest != bpf_htons(port)) + return NULL; + + record_stats(ctx, STATS_RX); + + return udph; +} + +static int xdp_mode_pass(struct xdp_md *ctx, __u16 port) +{ + struct udphdr *udph = NULL; + + udph = filter_udphdr(ctx, port); + if (!udph) + return XDP_PASS; + + record_stats(ctx, STATS_PASS); + + return XDP_PASS; +} + +static int xdp_mode_drop_handler(struct xdp_md *ctx, __u16 port) +{ + struct udphdr *udph = NULL; + + udph = filter_udphdr(ctx, port); + if (!udph) + return XDP_PASS; + + record_stats(ctx, STATS_DROP); + + return XDP_DROP; +} + +static void swap_machdr(void *data) +{ + struct ethhdr *eth = data; + __u8 tmp_mac[ETH_ALEN]; + + __builtin_memcpy(tmp_mac, eth->h_source, ETH_ALEN); + __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN); + __builtin_memcpy(eth->h_dest, tmp_mac, ETH_ALEN); +} + +static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct udphdr *udph = NULL; + struct ethhdr *eth = data; + + if (data + sizeof(*eth) > data_end) + return XDP_PASS; + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = data + sizeof(*eth); + __be32 tmp_ip = iph->saddr; + + if (iph + 1 > (struct iphdr *)data_end || + iph->protocol != IPPROTO_UDP) + return XDP_PASS; + + udph = data + sizeof(*iph) + sizeof(*eth); + + if (udph + 1 > (struct udphdr *)data_end) + return XDP_PASS; + if (udph->dest != bpf_htons(port)) + return XDP_PASS; + + record_stats(ctx, STATS_RX); + swap_machdr((void *)eth); + + iph->saddr = iph->daddr; + iph->daddr = tmp_ip; + + record_stats(ctx, STATS_TX); + + return XDP_TX; + + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6h = data + sizeof(*eth); + struct in6_addr tmp_ipv6; + + if (ipv6h + 1 > (struct ipv6hdr *)data_end || + ipv6h->nexthdr != IPPROTO_UDP) + return XDP_PASS; + + udph = data + sizeof(*ipv6h) + sizeof(*eth); + + if (udph + 1 > (struct udphdr *)data_end) + return XDP_PASS; + if (udph->dest != bpf_htons(port)) + return XDP_PASS; + + record_stats(ctx, STATS_RX); + swap_machdr((void *)eth); + + __builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6)); + __builtin_memcpy(&ipv6h->saddr, &ipv6h->daddr, + sizeof(tmp_ipv6)); + __builtin_memcpy(&ipv6h->daddr, &tmp_ipv6, sizeof(tmp_ipv6)); + + record_stats(ctx, STATS_TX); + + return XDP_TX; + } + + return XDP_PASS; +} + +static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct udphdr *udph = NULL; + struct ethhdr *eth = data; + __u32 len, len_new; + + if (data + sizeof(*eth) > data_end) + return NULL; + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = data + sizeof(*eth); + __u16 total_len; + + if (iph + 1 > (struct iphdr *)data_end) + return NULL; + + iph->tot_len = bpf_htons(bpf_ntohs(iph->tot_len) + offset); + + udph = (void *)eth + sizeof(*iph) + sizeof(*eth); + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + + len_new = bpf_htons(bpf_ntohs(udph->len) + offset); + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6h = data + sizeof(*eth); + __u16 payload_len; + + if (ipv6h + 1 > (struct ipv6hdr *)data_end) + return NULL; + + udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth); + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + + *udp_csum = ~((__u32)udph->check); + + len = ipv6h->payload_len; + len_new = bpf_htons(bpf_ntohs(len) + offset); + ipv6h->payload_len = len_new; + + *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, + sizeof(len_new), *udp_csum); + + len = udph->len; + len_new = bpf_htons(bpf_ntohs(udph->len) + offset); + *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, + sizeof(len_new), *udp_csum); + } else { + return NULL; + } + + udph->len = len_new; + + return udph; +} + +static __u16 csum_fold_helper(__u32 csum) +{ + return ~((csum & 0xffff) + (csum >> 16)) ? : 0xffff; +} + +static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, + __u32 hdr_len) +{ + char tmp_buff[MAX_ADJST_OFFSET]; + __u32 buff_pos, udp_csum = 0; + struct udphdr *udph = NULL; + __u32 buff_len; + + udph = update_pkt(ctx, 0 - offset, &udp_csum); + if (!udph) + return -1; + + buff_len = bpf_xdp_get_buff_len(ctx); + + offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET : + offset & 0xff; + if (offset == 0) + return -1; + + /* Make sure we have enough data to avoid eating the header */ + if (buff_len - offset < hdr_len) + return -1; + + buff_pos = buff_len - offset; + if (bpf_xdp_load_bytes(ctx, buff_pos, tmp_buff, offset) < 0) + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); + udph->check = (__u16)csum_fold_helper(udp_csum); + + if (bpf_xdp_adjust_tail(ctx, 0 - offset) < 0) + return -1; + + return 0; +} + +static int xdp_adjst_tail_grow_data(struct xdp_md *ctx, __u16 offset) +{ + char tmp_buff[MAX_ADJST_OFFSET]; + __u32 buff_pos, udp_csum = 0; + __u32 buff_len, hdr_len, key; + struct udphdr *udph; + __s32 *val; + __u8 tag; + + /* Proceed to update the packet headers before attempting to adjuste + * the tail. Once the tail is adjusted we lose access to the offset + * amount of data at the end of the packet which is crucial to update + * the checksum. + * Since any failure beyond this would abort the packet, we should + * not worry about passing a packet up the stack with wrong headers + */ + udph = update_pkt(ctx, offset, &udp_csum); + if (!udph) + return -1; + + key = XDP_ADJST_TAG; + val = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!val) + return -1; + + tag = (__u8)(*val); + + for (int i = 0; i < MAX_ADJST_OFFSET; i++) + __builtin_memcpy(&tmp_buff[i], &tag, 1); + + offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET : + offset & 0xff; + if (offset == 0) + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)tmp_buff, offset, udp_csum); + udph->check = (__u16)csum_fold_helper(udp_csum); + + buff_len = bpf_xdp_get_buff_len(ctx); + + if (bpf_xdp_adjust_tail(ctx, offset) < 0) { + bpf_printk("Failed to adjust tail\n"); + return -1; + } + + if (bpf_xdp_store_bytes(ctx, buff_len, tmp_buff, offset) < 0) + return -1; + + return 0; +} + +static int xdp_adjst_tail(struct xdp_md *ctx, __u16 port) +{ + void *data = (void *)(long)ctx->data; + struct udphdr *udph = NULL; + __s32 *adjust_offset, *val; + __u32 key, hdr_len; + void *offset_ptr; + __u8 tag; + int ret; + + udph = filter_udphdr(ctx, port); + if (!udph) + return XDP_PASS; + + hdr_len = (void *)udph - data + sizeof(struct udphdr); + key = XDP_ADJST_OFFSET; + adjust_offset = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!adjust_offset) + return XDP_PASS; + + if (*adjust_offset < 0) + ret = xdp_adjst_tail_shrnk_data(ctx, + (__u16)(0 - *adjust_offset), + hdr_len); + else + ret = xdp_adjst_tail_grow_data(ctx, (__u16)(*adjust_offset)); + if (ret) + goto abort_pkt; + + record_stats(ctx, STATS_PASS); + return XDP_PASS; + +abort_pkt: + record_stats(ctx, STATS_ABORT); + return XDP_ABORTED; +} + +static int xdp_adjst_head_shrnk_data(struct xdp_md *ctx, __u64 hdr_len, + __u32 offset) +{ + char tmp_buff[MAX_ADJST_OFFSET]; + struct udphdr *udph; + void *offset_ptr; + __u32 udp_csum = 0; + + /* Update the length information in the IP and UDP headers before + * adjusting the headroom. This simplifies accessing the relevant + * fields in the IP and UDP headers for fragmented packets. Any + * failure beyond this point will result in the packet being aborted, + * so we don't need to worry about incorrect length information for + * passed packets. + */ + udph = update_pkt(ctx, (__s16)(0 - offset), &udp_csum); + if (!udph) + return -1; + + offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET : + offset & 0xff; + if (offset == 0) + return -1; + + if (bpf_xdp_load_bytes(ctx, hdr_len, tmp_buff, offset) < 0) + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); + + udph->check = (__u16)csum_fold_helper(udp_csum); + + if (bpf_xdp_load_bytes(ctx, 0, tmp_buff, MAX_ADJST_OFFSET) < 0) + return -1; + + if (bpf_xdp_adjust_head(ctx, offset) < 0) + return -1; + + if (offset > MAX_ADJST_OFFSET) + return -1; + + if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0) + return -1; + + /* Added here to handle clang complain about negative value */ + hdr_len = hdr_len & 0xff; + + if (hdr_len == 0) + return -1; + + if (bpf_xdp_store_bytes(ctx, 0, tmp_buff, hdr_len) < 0) + return -1; + + return 0; +} + +static int xdp_adjst_head_grow_data(struct xdp_md *ctx, __u64 hdr_len, + __u32 offset) +{ + char hdr_buff[MAX_HDR_LEN]; + char data_buff[MAX_ADJST_OFFSET]; + void *offset_ptr; + __s32 *val; + __u32 key; + __u8 tag; + __u32 udp_csum = 0; + struct udphdr *udph; + + udph = update_pkt(ctx, (__s16)(offset), &udp_csum); + if (!udph) + return -1; + + key = XDP_ADJST_TAG; + val = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!val) + return -1; + + tag = (__u8)(*val); + for (int i = 0; i < MAX_ADJST_OFFSET; i++) + __builtin_memcpy(&data_buff[i], &tag, 1); + + offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET : + offset & 0xff; + if (offset == 0) + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)data_buff, offset, udp_csum); + udph->check = (__u16)csum_fold_helper(udp_csum); + + if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0) + return -1; + + /* Added here to handle clang complain about negative value */ + hdr_len = hdr_len & 0xff; + + if (hdr_len == 0) + return -1; + + if (bpf_xdp_load_bytes(ctx, 0, hdr_buff, hdr_len) < 0) + return -1; + + if (offset > MAX_ADJST_OFFSET) + return -1; + + if (bpf_xdp_adjust_head(ctx, 0 - offset) < 0) + return -1; + + if (bpf_xdp_store_bytes(ctx, 0, hdr_buff, hdr_len) < 0) + return -1; + + if (bpf_xdp_store_bytes(ctx, hdr_len, data_buff, offset) < 0) + return -1; + + return 0; +} + +static int xdp_head_adjst(struct xdp_md *ctx, __u16 port) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct udphdr *udph_ptr = NULL; + __u32 key, size, hdr_len; + __s32 *val; + int res; + + /* Filter packets based on UDP port */ + udph_ptr = filter_udphdr(ctx, port); + if (!udph_ptr) + return XDP_PASS; + + hdr_len = (void *)udph_ptr - data + sizeof(struct udphdr); + + key = XDP_ADJST_OFFSET; + val = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!val) + return XDP_PASS; + + switch (*val) { + case -16: + case 16: + size = 16; + break; + case -32: + case 32: + size = 32; + break; + case -64: + case 64: + size = 64; + break; + case -128: + case 128: + size = 128; + break; + case -256: + case 256: + size = 256; + break; + default: + bpf_printk("Invalid adjustment offset: %d\n", *val); + goto abort; + } + + if (*val < 0) + res = xdp_adjst_head_grow_data(ctx, hdr_len, size); + else + res = xdp_adjst_head_shrnk_data(ctx, hdr_len, size); + + if (res) + goto abort; + + record_stats(ctx, STATS_PASS); + return XDP_PASS; + +abort: + record_stats(ctx, STATS_ABORT); + return XDP_ABORTED; +} + +static int xdp_prog_common(struct xdp_md *ctx) +{ + __u32 key, *port; + __s32 *mode; + + key = XDP_MODE; + mode = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!mode) + return XDP_PASS; + + key = XDP_PORT; + port = bpf_map_lookup_elem(&map_xdp_setup, &key); + if (!port) + return XDP_PASS; + + switch (*mode) { + case XDP_MODE_PASS: + return xdp_mode_pass(ctx, (__u16)(*port)); + case XDP_MODE_DROP: + return xdp_mode_drop_handler(ctx, (__u16)(*port)); + case XDP_MODE_TX: + return xdp_mode_tx_handler(ctx, (__u16)(*port)); + case XDP_MODE_TAIL_ADJST: + return xdp_adjst_tail(ctx, (__u16)(*port)); + case XDP_MODE_HEAD_ADJST: + return xdp_head_adjst(ctx, (__u16)(*port)); + } + + /* Default action is to simple pass */ + return XDP_PASS; +} + +SEC("xdp") +int xdp_prog(struct xdp_md *ctx) +{ + return xdp_prog_common(ctx); +} + +SEC("xdp.frags") +int xdp_prog_frags(struct xdp_md *ctx) +{ + return xdp_prog_common(ctx); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index e47788bfa671..4c7e51336ab2 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -4,7 +4,8 @@ top_srcdir = ../../../../.. CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INCLUDES) -TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \ +TEST_PROGS := mptcp_connect.sh mptcp_connect_mmap.sh mptcp_connect_sendfile.sh \ + mptcp_connect_checksum.sh pm_netlink.sh mptcp_join.sh diag.sh \ simult_flows.sh mptcp_sockopt.sh userspace_pm.sh TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config index 4f80014cae49..968d440c03fe 100644 --- a/tools/testing/selftests/net/mptcp/config +++ b/tools/testing/selftests/net/mptcp/config @@ -13,6 +13,7 @@ CONFIG_NETFILTER_NETLINK=m CONFIG_NF_TABLES=m CONFIG_NFT_COMPAT=m CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XTABLES_LEGACY=y CONFIG_NETFILTER_XT_MATCH_BPF=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m CONFIG_NETFILTER_XT_MATCH_STATISTIC=m @@ -25,6 +26,7 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_REJECT=m CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IP6_NF_FILTER=m CONFIG_NET_ACT_CSUM=m diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index 7a3cb4c09e45..d847ff1737c3 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -28,7 +28,7 @@ flush_pids() } # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { ip netns pids "${ns}" | xargs --no-run-if-empty kill -SIGKILL &>/dev/null diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index ac1349c4b9e5..4f07ac9fa207 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -183,9 +183,10 @@ static void xgetaddrinfo(const char *node, const char *service, struct addrinfo *hints, struct addrinfo **res) { -again: - int err = getaddrinfo(node, service, hints, res); + int err; +again: + err = getaddrinfo(node, service, hints, res); if (err) { const char *errstr; diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 5e3c56253274..c2ab9f7f0d21 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -134,7 +134,7 @@ ns4="" TEST_GROUP="" # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { rm -f "$cin_disconnect" diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect_checksum.sh b/tools/testing/selftests/net/mptcp/mptcp_connect_checksum.sh new file mode 100755 index 000000000000..ce93ec2f107f --- /dev/null +++ b/tools/testing/selftests/net/mptcp/mptcp_connect_checksum.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +MPTCP_LIB_KSFT_TEST="$(basename "${0}" .sh)" \ + "$(dirname "${0}")/mptcp_connect.sh" -C "${@}" diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect_mmap.sh b/tools/testing/selftests/net/mptcp/mptcp_connect_mmap.sh new file mode 100755 index 000000000000..5dd30f9394af --- /dev/null +++ b/tools/testing/selftests/net/mptcp/mptcp_connect_mmap.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +MPTCP_LIB_KSFT_TEST="$(basename "${0}" .sh)" \ + "$(dirname "${0}")/mptcp_connect.sh" -m mmap "${@}" diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect_sendfile.sh b/tools/testing/selftests/net/mptcp/mptcp_connect_sendfile.sh new file mode 100755 index 000000000000..1d16fb1cc9bb --- /dev/null +++ b/tools/testing/selftests/net/mptcp/mptcp_connect_sendfile.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +MPTCP_LIB_KSFT_TEST="$(basename "${0}" .sh)" \ + "$(dirname "${0}")/mptcp_connect.sh" -m sendfile "${@}" diff --git a/tools/testing/selftests/net/mptcp/mptcp_inq.c b/tools/testing/selftests/net/mptcp/mptcp_inq.c index 3cf1e2a612ce..f3bcaa48df8f 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_inq.c +++ b/tools/testing/selftests/net/mptcp/mptcp_inq.c @@ -75,9 +75,10 @@ static void xgetaddrinfo(const char *node, const char *service, struct addrinfo *hints, struct addrinfo **res) { -again: - int err = getaddrinfo(node, service, hints, res); + int err; +again: + err = getaddrinfo(node, service, hints, res); if (err) { const char *errstr; diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index b8af65373b3a..7fd555b123b9 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -8,7 +8,7 @@ # ShellCheck incorrectly believes that most of the code here is unreachable # because it's invoked by variable name, see how the "tests" array is used -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 . "$(dirname "${0}")/mptcp_lib.sh" @@ -3842,6 +3842,7 @@ endpoint_tests() # remove and re-add if reset_with_events "delete re-add signal" && mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then + ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=0 pm_nl_set_limits $ns1 0 3 pm_nl_set_limits $ns2 3 3 pm_nl_add_endpoint $ns1 10.0.2.1 id 1 flags signal diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index 9934a68df237..e934dd26a59d 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -162,9 +162,10 @@ static void xgetaddrinfo(const char *node, const char *service, struct addrinfo *hints, struct addrinfo **res) { -again: - int err = getaddrinfo(node, service, hints, res); + int err; +again: + err = getaddrinfo(node, service, hints, res); if (err) { const char *errstr; diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 418a903c3a4d..f01989be6e9b 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -95,7 +95,7 @@ init() } # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { mptcp_lib_ns_exit "${ns1}" "${ns2}" "${ns_sbox}" diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 2e6648a2b2c0..ec6a87588191 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -32,7 +32,7 @@ ns1="" err=$(mktemp) # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { rm -f "${err}" @@ -70,8 +70,9 @@ format_endpoints() { mptcp_lib_pm_nl_format_endpoints "${@}" } +# This function is invoked indirectly +#shellcheck disable=SC2317,SC2329 get_endpoint() { - # shellcheck disable=SC2317 # invoked indirectly mptcp_lib_pm_nl_get_endpoint "${ns1}" "${@}" } @@ -198,6 +199,7 @@ set_limits 1 9 2>/dev/null check "get_limits" "${default_limits}" "subflows above hard limit" set_limits 8 8 +flush_endpoint ## to make sure it doesn't affect the limits check "get_limits" "$(format_limits 8 8)" "set limits" flush_endpoint diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index 2329c2f8519b..1903e8e84a31 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -35,7 +35,7 @@ usage() { } # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { rm -f "$cout" "$sout" diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 333064b0b5ac..970c329735ff 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -94,7 +94,7 @@ test_fail() } # This function is used in the cleanup trap -#shellcheck disable=SC2317 +#shellcheck disable=SC2317,SC2329 cleanup() { print_title "Cleanup" diff --git a/tools/testing/selftests/net/msg_zerocopy.c b/tools/testing/selftests/net/msg_zerocopy.c index 7ea5fb28c93d..1d5d3c4e7e87 100644 --- a/tools/testing/selftests/net/msg_zerocopy.c +++ b/tools/testing/selftests/net/msg_zerocopy.c @@ -77,6 +77,7 @@ static int cfg_cork; static bool cfg_cork_mixed; static int cfg_cpu = -1; /* default: pin to last cpu */ +static int cfg_expect_zerocopy = -1; static int cfg_family = PF_UNSPEC; static int cfg_ifindex = 1; static int cfg_payload_len; @@ -92,9 +93,9 @@ static socklen_t cfg_alen; static struct sockaddr_storage cfg_dst_addr; static struct sockaddr_storage cfg_src_addr; +static int exitcode; static char payload[IP_MAXPACKET]; static long packets, bytes, completions, expected_completions; -static int zerocopied = -1; static uint32_t next_completion; static uint32_t sends_since_notify; @@ -444,11 +445,13 @@ static bool do_recv_completion(int fd, int domain) next_completion = hi + 1; zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED); - if (zerocopied == -1) - zerocopied = zerocopy; - else if (zerocopied != zerocopy) { - fprintf(stderr, "serr: inconsistent\n"); - zerocopied = zerocopy; + if (cfg_expect_zerocopy != -1 && + cfg_expect_zerocopy != zerocopy) { + fprintf(stderr, "serr: ee_code: %u != expected %u\n", + zerocopy, cfg_expect_zerocopy); + exitcode = 1; + /* suppress repeated messages */ + cfg_expect_zerocopy = zerocopy; } if (cfg_verbose >= 2) @@ -571,7 +574,7 @@ static void do_tx(int domain, int type, int protocol) fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n", packets, bytes >> 20, completions, - zerocopied == 1 ? 'y' : 'n'); + cfg_zerocopy && cfg_expect_zerocopy == 1 ? 'y' : 'n'); } static int do_setup_rx(int domain, int type, int protocol) @@ -715,7 +718,7 @@ static void parse_opts(int argc, char **argv) cfg_payload_len = max_payload_len; - while ((c = getopt(argc, argv, "46c:C:D:i:l:mp:rs:S:t:vz")) != -1) { + while ((c = getopt(argc, argv, "46c:C:D:i:l:mp:rs:S:t:vzZ:")) != -1) { switch (c) { case '4': if (cfg_family != PF_UNSPEC) @@ -770,6 +773,9 @@ static void parse_opts(int argc, char **argv) case 'z': cfg_zerocopy = true; break; + case 'Z': + cfg_expect_zerocopy = !!atoi(optarg); + break; } } @@ -817,5 +823,5 @@ int main(int argc, char **argv) else error(1, 0, "unknown cfg_test %s", cfg_test); - return 0; + return exitcode; } diff --git a/tools/testing/selftests/net/msg_zerocopy.sh b/tools/testing/selftests/net/msg_zerocopy.sh index 89c22f5320e0..28178a38a4e7 100755 --- a/tools/testing/selftests/net/msg_zerocopy.sh +++ b/tools/testing/selftests/net/msg_zerocopy.sh @@ -6,6 +6,7 @@ set -e readonly DEV="veth0" +readonly DUMMY_DEV="dummy0" readonly DEV_MTU=65535 readonly BIN="./msg_zerocopy" @@ -14,21 +15,25 @@ readonly NSPREFIX="ns-${RAND}" readonly NS1="${NSPREFIX}1" readonly NS2="${NSPREFIX}2" -readonly SADDR4='192.168.1.1' -readonly DADDR4='192.168.1.2' -readonly SADDR6='fd::1' -readonly DADDR6='fd::2' +readonly LPREFIX4='192.168.1' +readonly RPREFIX4='192.168.2' +readonly LPREFIX6='fd' +readonly RPREFIX6='fc' + readonly path_sysctl_mem="net.core.optmem_max" # No arguments: automated test if [[ "$#" -eq "0" ]]; then - $0 4 tcp -t 1 - $0 6 tcp -t 1 - $0 4 udp -t 1 - $0 6 udp -t 1 - echo "OK. All tests passed" - exit 0 + ret=0 + + $0 4 tcp -t 1 || ret=1 + $0 6 tcp -t 1 || ret=1 + $0 4 udp -t 1 || ret=1 + $0 6 udp -t 1 || ret=1 + + [[ "$ret" == "0" ]] && echo "OK. All tests passed" + exit $ret fi # Argument parsing @@ -45,11 +50,18 @@ readonly EXTRA_ARGS="$@" # Argument parsing: configure addresses if [[ "${IP}" == "4" ]]; then - readonly SADDR="${SADDR4}" - readonly DADDR="${DADDR4}" + readonly SADDR="${LPREFIX4}.1" + readonly DADDR="${LPREFIX4}.2" + readonly DUMMY_ADDR="${RPREFIX4}.1" + readonly DADDR_TXONLY="${RPREFIX4}.2" + readonly MASK="24" elif [[ "${IP}" == "6" ]]; then - readonly SADDR="${SADDR6}" - readonly DADDR="${DADDR6}" + readonly SADDR="${LPREFIX6}::1" + readonly DADDR="${LPREFIX6}::2" + readonly DUMMY_ADDR="${RPREFIX6}::1" + readonly DADDR_TXONLY="${RPREFIX6}::2" + readonly MASK="64" + readonly NODAD="nodad" else echo "Invalid IP version ${IP}" exit 1 @@ -89,33 +101,61 @@ ip netns exec "${NS2}" sysctl -w -q "${path_sysctl_mem}=1000000" ip link add "${DEV}" mtu "${DEV_MTU}" netns "${NS1}" type veth \ peer name "${DEV}" mtu "${DEV_MTU}" netns "${NS2}" +ip link add "${DUMMY_DEV}" mtu "${DEV_MTU}" netns "${NS2}" type dummy + # Bring the devices up ip -netns "${NS1}" link set "${DEV}" up ip -netns "${NS2}" link set "${DEV}" up +ip -netns "${NS2}" link set "${DUMMY_DEV}" up # Set fixed MAC addresses on the devices ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02 ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06 # Add fixed IP addresses to the devices -ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}" -ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}" -ip -netns "${NS1}" addr add fd::1/64 dev "${DEV}" nodad -ip -netns "${NS2}" addr add fd::2/64 dev "${DEV}" nodad +ip -netns "${NS1}" addr add "${SADDR}/${MASK}" dev "${DEV}" ${NODAD} +ip -netns "${NS2}" addr add "${DADDR}/${MASK}" dev "${DEV}" ${NODAD} +ip -netns "${NS2}" addr add "${DUMMY_ADDR}/${MASK}" dev "${DUMMY_DEV}" ${NODAD} + +ip -netns "${NS1}" route add default via "${DADDR}" dev "${DEV}" +ip -netns "${NS2}" route add default via "${DADDR_TXONLY}" dev "${DUMMY_DEV}" + +ip netns exec "${NS2}" sysctl -wq net.ipv4.ip_forward=1 +ip netns exec "${NS2}" sysctl -wq net.ipv6.conf.all.forwarding=1 # Optionally disable sg or csum offload to test edge cases # ip netns exec "${NS1}" ethtool -K "${DEV}" sg off +ret=0 + do_test() { local readonly ARGS="$1" - echo "ipv${IP} ${TXMODE} ${ARGS}" - ip netns exec "${NS2}" "${BIN}" "-${IP}" -i "${DEV}" -t 2 -C 2 -S "${SADDR}" -D "${DADDR}" ${ARGS} -r "${RXMODE}" & + # tx-rx test + # packets queued to a local socket are copied, + # sender notification has SO_EE_CODE_ZEROCOPY_COPIED. + + echo -e "\nipv${IP} ${TXMODE} ${ARGS} tx-rx\n" + ip netns exec "${NS2}" "${BIN}" "-${IP}" -i "${DEV}" -t 2 -C 2 \ + -S "${SADDR}" -D "${DADDR}" ${ARGS} -r "${RXMODE}" & sleep 0.2 - ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 -S "${SADDR}" -D "${DADDR}" ${ARGS} "${TXMODE}" + ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 \ + -S "${SADDR}" -D "${DADDR}" ${ARGS} "${TXMODE}" -Z 0 || ret=1 wait + + # next test is unconnected tx to dummy0, cannot exercise with tcp + [[ "${TXMODE}" == "tcp" ]] && return + + # tx-only test: send out dummy0 + # packets leaving the host are not copied, + # sender notification does not have SO_EE_CODE_ZEROCOPY_COPIED. + + echo -e "\nipv${IP} ${TXMODE} ${ARGS} tx-only\n" + ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 \ + -S "${SADDR}" -D "${DADDR_TXONLY}" ${ARGS} "${TXMODE}" -Z 1 || ret=1 } do_test "${EXTRA_ARGS}" do_test "-z ${EXTRA_ARGS}" -echo ok + +[[ "$ret" == "0" ]] && echo "OK" diff --git a/tools/testing/selftests/net/netdev-l2addr.sh b/tools/testing/selftests/net/netdev-l2addr.sh new file mode 100755 index 000000000000..18509da293e5 --- /dev/null +++ b/tools/testing/selftests/net/netdev-l2addr.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source lib.sh +set -o pipefail + +NSIM_ADDR=2025 +TEST_ADDR="d0:be:d0:be:d0:00" + +RET_CODE=0 + +cleanup() { + cleanup_netdevsim "$NSIM_ADDR" + cleanup_ns "$NS" +} + +trap cleanup EXIT + +fail() { + echo "ERROR: ${1:-unexpected return code} (ret: $_)" >&2 + RET_CODE=1 +} + +get_addr() +{ + local type="$1" + local dev="$2" + local ns="$3" + + ip -j -n "$ns" link show dev "$dev" | jq -er ".[0].$type" +} + +setup_ns NS + +nsim=$(create_netdevsim $NSIM_ADDR "$NS") + +get_addr address "$nsim" "$NS" >/dev/null || fail "Couldn't get ether addr" +get_addr broadcast "$nsim" "$NS" >/dev/null || fail "Couldn't get brd addr" +get_addr permaddr "$nsim" "$NS" >/dev/null && fail "Found perm_addr without setting it" + +ip -n "$NS" link set dev "$nsim" address "$TEST_ADDR" +ip -n "$NS" link set dev "$nsim" brd "$TEST_ADDR" + +[[ "$(get_addr address "$nsim" "$NS")" == "$TEST_ADDR" ]] || fail "Couldn't set ether addr" +[[ "$(get_addr broadcast "$nsim" "$NS")" == "$TEST_ADDR" ]] || fail "Couldn't set brd addr" + +if create_netdevsim_port "$NSIM_ADDR" "$NS" 2 "FF:FF:FF:FF:FF:FF" 2>/dev/null; then + fail "Created netdevsim with broadcast permaddr" +fi + +nsim_port=$(create_netdevsim_port "$NSIM_ADDR" "$NS" 2 "$TEST_ADDR") + +get_addr address "$nsim_port" "$NS" >/dev/null || fail "Couldn't get ether addr" +get_addr broadcast "$nsim_port" "$NS" >/dev/null || fail "Couldn't get brd addr" +[[ "$(get_addr permaddr "$nsim_port" "$NS")" == "$TEST_ADDR" ]] || fail "Couldn't get permaddr" + +cleanup_netdevsim "$NSIM_ADDR" "$NS" + +exit $RET_CODE diff --git a/tools/testing/selftests/net/netfilter/.gitignore b/tools/testing/selftests/net/netfilter/.gitignore index 64c4f8d9aa6c..5d2be9a00627 100644 --- a/tools/testing/selftests/net/netfilter/.gitignore +++ b/tools/testing/selftests/net/netfilter/.gitignore @@ -5,3 +5,4 @@ conntrack_dump_flush conntrack_reverse_clash sctp_collision nf_queue +udpclash diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile index e9b2f553588d..a98ed892f55f 100644 --- a/tools/testing/selftests/net/netfilter/Makefile +++ b/tools/testing/selftests/net/netfilter/Makefile @@ -15,6 +15,7 @@ TEST_PROGS += conntrack_tcp_unreplied.sh TEST_PROGS += conntrack_resize.sh TEST_PROGS += conntrack_sctp_collision.sh TEST_PROGS += conntrack_vrf.sh +TEST_PROGS += conntrack_clash.sh TEST_PROGS += conntrack_reverse_clash.sh TEST_PROGS += ipvs.sh TEST_PROGS += nf_conntrack_packetdrill.sh @@ -44,6 +45,7 @@ TEST_GEN_FILES += connect_close nf_queue TEST_GEN_FILES += conntrack_dump_flush TEST_GEN_FILES += conntrack_reverse_clash TEST_GEN_FILES += sctp_collision +TEST_GEN_FILES += udpclash include ../../lib.mk @@ -52,6 +54,7 @@ $(OUTPUT)/nf_queue: LDLIBS += $(MNL_LDLIBS) $(OUTPUT)/conntrack_dump_flush: CFLAGS += $(MNL_CFLAGS) $(OUTPUT)/conntrack_dump_flush: LDLIBS += $(MNL_LDLIBS) +$(OUTPUT)/udpclash: LDLIBS += -lpthread TEST_FILES := lib.sh TEST_FILES += packetdrill diff --git a/tools/testing/selftests/net/netfilter/config b/tools/testing/selftests/net/netfilter/config index 363646f4fefe..79d5b33966ba 100644 --- a/tools/testing/selftests/net/netfilter/config +++ b/tools/testing/selftests/net/netfilter/config @@ -1,6 +1,8 @@ CONFIG_AUDIT=y CONFIG_BPF_SYSCALL=y CONFIG_BRIDGE=m +CONFIG_NETFILTER_XTABLES_LEGACY=y +CONFIG_BRIDGE_NF_EBTABLES_LEGACY=m CONFIG_BRIDGE_EBT_BROUTE=m CONFIG_BRIDGE_EBT_IP=m CONFIG_BRIDGE_EBT_REDIRECT=m @@ -14,7 +16,10 @@ CONFIG_INET_ESP=m CONFIG_IP_NF_MATCH_RPFILTER=m CONFIG_IP6_NF_MATCH_RPFILTER=m CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_IPTABLES_LEGACY=m CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_IPTABLES_LEGACY=m +CONFIG_IP_NF_NAT=m CONFIG_IP_NF_FILTER=m CONFIG_IP6_NF_FILTER=m CONFIG_IP_NF_RAW=m @@ -92,4 +97,4 @@ CONFIG_XFRM_STATISTICS=y CONFIG_NET_PKTGEN=m CONFIG_TUN=m CONFIG_INET_DIAG=m -CONFIG_SCTP_DIAG=m +CONFIG_INET_SCTP_DIAG=m diff --git a/tools/testing/selftests/net/netfilter/conntrack_clash.sh b/tools/testing/selftests/net/netfilter/conntrack_clash.sh new file mode 100755 index 000000000000..7fc6c5dbd551 --- /dev/null +++ b/tools/testing/selftests/net/netfilter/conntrack_clash.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source lib.sh + +clash_resolution_active=0 +dport=22111 +ret=0 + +cleanup() +{ + # netns cleanup also zaps any remaining socat echo server. + cleanup_all_ns +} + +checktool "nft --version" "run test without nft" +checktool "conntrack --version" "run test without conntrack" +checktool "socat -h" "run test without socat" + +trap cleanup EXIT + +setup_ns nsclient1 nsclient2 nsrouter + +ip netns exec "$nsrouter" nft -f -</dev/null & + done + + for port in $ports; do + wait_local_port_listen "$ns" $port udp + done +} + +add_addr() +{ + local ns="$1" + local dev="$2" + local i="$3" + local j="$4" + + ip -net "$ns" link set "$dev" up + ip -net "$ns" addr add "10.0.$i.$j/24" dev "$dev" +} + +ping_test() +{ + local ns="$1" + local daddr="$2" + + if ! ip netns exec "$ns" ping -q -c 1 $daddr > /dev/null;then + echo "FAIL: ping from $ns to $daddr" + exit 1 + fi +} + +run_one_clash_test() +{ + local ns="$1" + local ctns="$2" + local daddr="$3" + local dport="$4" + local entries + local cre + + if ! ip netns exec "$ns" timeout 30 ./udpclash $daddr $dport;then + echo "INFO: did not receive expected number of replies for $daddr:$dport" + ip netns exec "$ctns" conntrack -S + # don't fail: check if clash resolution triggered after all. + fi + + entries=$(ip netns exec "$ctns" conntrack -S | wc -l) + cre=$(ip netns exec "$ctns" conntrack -S | grep "clash_resolve=0" | wc -l) + + if [ "$cre" -ne "$entries" ];then + clash_resolution_active=1 + return 0 + fi + + # not a failure: clash resolution logic did not trigger. + # With right timing, xmit completed sequentially and + # no parallel insertion occurs. + return $ksft_skip +} + +run_clash_test() +{ + local ns="$1" + local ctns="$2" + local daddr="$3" + local dport="$4" + local softerr=0 + + for i in $(seq 1 10);do + run_one_clash_test "$ns" "$ctns" "$daddr" "$dport" + local rv=$? + if [ $rv -eq 0 ];then + echo "PASS: clash resolution test for $daddr:$dport on attempt $i" + return 0 + elif [ $rv -eq $ksft_skip ]; then + softerr=1 + fi + done + + [ $softerr -eq 1 ] && echo "SKIP: clash resolution for $daddr:$dport did not trigger" +} + +ip link add veth0 netns "$nsclient1" type veth peer name veth0 netns "$nsrouter" +ip link add veth0 netns "$nsclient2" type veth peer name veth1 netns "$nsrouter" +add_addr "$nsclient1" veth0 1 1 +add_addr "$nsclient2" veth0 2 1 +add_addr "$nsrouter" veth0 1 99 +add_addr "$nsrouter" veth1 2 99 + +ip -net "$nsclient1" route add default via 10.0.1.99 +ip -net "$nsclient2" route add default via 10.0.2.99 +ip netns exec "$nsrouter" sysctl -q net.ipv4.ip_forward=1 + +ping_test "$nsclient1" 10.0.1.99 +ping_test "$nsclient1" 10.0.2.1 +ping_test "$nsclient2" 10.0.1.1 + +spawn_servers "$nsclient2" + +# exercise clash resolution with nat: +# nsrouter is supposed to dnat to 10.0.2.1:900{0,1,2,3}. +run_clash_test "$nsclient1" "$nsrouter" 10.0.1.99 "$dport" + +# exercise clash resolution without nat. +load_simple_ruleset "$nsclient2" +run_clash_test "$nsclient2" "$nsclient2" 127.0.0.1 9001 + +if [ $clash_resolution_active -eq 0 ];then + [ "$ret" -eq 0 ] && ret=$ksft_skip + echo "SKIP: Clash resolution did not trigger" +fi + +exit $ret diff --git a/tools/testing/selftests/net/netfilter/conntrack_resize.sh b/tools/testing/selftests/net/netfilter/conntrack_resize.sh index 9e033e80219e..615fe3c6f405 100755 --- a/tools/testing/selftests/net/netfilter/conntrack_resize.sh +++ b/tools/testing/selftests/net/netfilter/conntrack_resize.sh @@ -12,6 +12,9 @@ tmpfile="" tmpfile_proc="" tmpfile_uniq="" ret=0 +have_socat=0 + +socat -h > /dev/null && have_socat=1 insert_count=2000 [ "$KSFT_MACHINE_SLOW" = "yes" ] && insert_count=400 @@ -123,7 +126,7 @@ ctflush() { done } -ctflood() +ct_pingflood() { local ns="$1" local duration="$2" @@ -152,6 +155,44 @@ ctflood() wait } +ct_udpflood() +{ + local ns="$1" + local duration="$2" + local now=$(date +%s) + local end=$((now + duration)) + + [ $have_socat -ne "1" ] && return + + while [ $now -lt $end ]; do +ip netns exec "$ns" bash<<"EOF" + for i in $(seq 1 100);do + dport=$(((RANDOM%65536)+1)) + + echo bar | socat -u STDIN UDP:"127.0.0.1:$dport" & + done > /dev/null 2>&1 + wait +EOF + now=$(date +%s) + done +} + +ct_udpclash() +{ + local ns="$1" + local duration="$2" + local now=$(date +%s) + local end=$((now + duration)) + + [ -x udpclash ] || return + + while [ $now -lt $end ]; do + ip netns exec "$ns" timeout 30 ./udpclash 127.0.0.1 $((RANDOM%65536)) > /dev/null 2>&1 + + now=$(date +%s) + done +} + # dump to /dev/null. We don't want dumps to cause infinite loops # or use-after-free even when conntrack table is altered while dumps # are in progress. @@ -169,6 +210,48 @@ ct_nulldump() wait } +ct_nulldump_loop() +{ + local ns="$1" + local duration="$2" + local now=$(date +%s) + local end=$((now + duration)) + + while [ $now -lt $end ]; do + ct_nulldump "$ns" + sleep $((RANDOM%2)) + now=$(date +%s) + done +} + +change_timeouts() +{ + local ns="$1" + local r1=$((RANDOM%2)) + local r2=$((RANDOM%2)) + + [ "$r1" -eq 1 ] && ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=$((RANDOM%5)) + [ "$r2" -eq 1 ] && ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_udp_timeout=$((RANDOM%5)) +} + +ct_change_timeouts_loop() +{ + local ns="$1" + local duration="$2" + local now=$(date +%s) + local end=$((now + duration)) + + while [ $now -lt $end ]; do + change_timeouts "$ns" + sleep $((RANDOM%2)) + now=$(date +%s) + done + + # restore defaults + ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=30 + ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_udp_timeout=30 +} + check_taint() { local tainted_then="$1" @@ -194,14 +277,19 @@ check_taint() insert_flood() { local n="$1" + local timeout="$2" local r=0 r=$((RANDOM%$insert_count)) - ctflood "$n" "$timeout" "floodresize" & + ct_pingflood "$n" "$timeout" "floodresize" & + ct_udpflood "$n" "$timeout" & + ct_udpclash "$n" "$timeout" & + insert_ctnetlink "$n" "$r" & ctflush "$n" "$timeout" & - ct_nulldump "$n" & + ct_nulldump_loop "$n" "$timeout" & + ct_change_timeouts_loop "$n" "$timeout" & wait } @@ -215,7 +303,7 @@ test_floodresize_all() read tainted_then < /proc/sys/kernel/tainted for n in "$nsclient1" "$nsclient2";do - insert_flood "$n" & + insert_flood "$n" "$timeout" & done # resize table constantly while flood/insert/dump/flushs @@ -306,7 +394,7 @@ test_dump_all() ip netns exec "$nsclient1" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=3600 - ctflood "$nsclient1" $timeout "dumpall" & + ct_pingflood "$nsclient1" $timeout "dumpall" & insert_ctnetlink "$nsclient2" $insert_count wait @@ -368,7 +456,7 @@ test_conntrack_disable() ct_flush_once "$nsclient1" ct_flush_once "$nsclient2" - ctflood "$nsclient1" "$timeout" "conntrack disable" + ct_pingflood "$nsclient1" "$timeout" "conntrack disable" ip netns exec "$nsclient2" ping -q -c 1 127.0.0.1 >/dev/null 2>&1 # Disabled, should not have picked up any connection. diff --git a/tools/testing/selftests/net/netfilter/ipvs.sh b/tools/testing/selftests/net/netfilter/ipvs.sh index 6af2ea3ad6b8..9c9d5b38ab71 100755 --- a/tools/testing/selftests/net/netfilter/ipvs.sh +++ b/tools/testing/selftests/net/netfilter/ipvs.sh @@ -151,7 +151,7 @@ test_nat() { test_tun() { ip netns exec "${ns0}" ip route add "${vip_v4}" via "${gip_v4}" dev br0 - ip netns exec "${ns1}" modprobe -q ipip + modprobe -q ipip ip netns exec "${ns1}" ip link set tunl0 up ip netns exec "${ns1}" sysctl -qw net.ipv4.ip_forward=0 ip netns exec "${ns1}" sysctl -qw net.ipv4.conf.all.send_redirects=0 @@ -160,10 +160,10 @@ test_tun() { ip netns exec "${ns1}" ipvsadm -a -i -t "${vip_v4}:${port}" -r ${rip_v4}:${port} ip netns exec "${ns1}" ip addr add ${vip_v4}/32 dev lo:1 - ip netns exec "${ns2}" modprobe -q ipip ip netns exec "${ns2}" ip link set tunl0 up ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.all.arp_ignore=1 ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.all.arp_announce=2 + ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.tunl0.rp_filter=0 ip netns exec "${ns2}" ip addr add "${vip_v4}/32" dev lo:1 test_service diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh index cd12b8b5ac0e..20e76b395c85 100755 --- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh +++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh @@ -1311,6 +1311,9 @@ maybe_send_match() { # - remove some elements, check that packets don't match anymore test_correctness_main() { range_size=1 + + send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1 + for i in $(seq "${start}" $((start + count))); do local elem="" diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index a4ee5496f2a1..45832df98295 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -20,6 +20,7 @@ ret=0 SOCAT_TIMEOUT=60 nsin="" +nsin_small="" ns1out="" ns2out="" @@ -36,7 +37,7 @@ cleanup() { cleanup_all_ns - rm -f "$nsin" "$ns1out" "$ns2out" + rm -f "$nsin" "$nsin_small" "$ns1out" "$ns2out" [ "$log_netns" -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns="$log_netns" } @@ -72,6 +73,7 @@ lmtu=1500 rmtu=2000 filesize=$((2 * 1024 * 1024)) +filesize_small=$((filesize / 16)) usage(){ echo "nft_flowtable.sh [OPTIONS]" @@ -89,7 +91,10 @@ do o) omtu=$OPTARG;; l) lmtu=$OPTARG;; r) rmtu=$OPTARG;; - s) filesize=$OPTARG;; + s) + filesize=$OPTARG + filesize_small=$((OPTARG / 16)) + ;; *) usage;; esac done @@ -215,6 +220,7 @@ if ! ip netns exec "$ns2" ping -c 1 -q 10.0.1.99 > /dev/null; then fi nsin=$(mktemp) +nsin_small=$(mktemp) ns1out=$(mktemp) ns2out=$(mktemp) @@ -265,6 +271,7 @@ check_counters() check_dscp() { local what=$1 + local pmtud="$2" local ok=1 local counter @@ -277,37 +284,39 @@ check_dscp() local pc4z=${counter%*bytes*} local pc4z=${pc4z#*packets} + local failmsg="FAIL: pmtu $pmtu: $what counters do not match, expected" + case "$what" in "dscp_none") if [ "$pc4" -gt 0 ] || [ "$pc4z" -eq 0 ]; then - echo "FAIL: dscp counters do not match, expected dscp3 == 0, dscp0 > 0, but got $pc4,$pc4z" 1>&2 + echo "$failmsg dscp3 == 0, dscp0 > 0, but got $pc4,$pc4z" 1>&2 ret=1 ok=0 fi ;; "dscp_fwd") if [ "$pc4" -eq 0 ] || [ "$pc4z" -eq 0 ]; then - echo "FAIL: dscp counters do not match, expected dscp3 and dscp0 > 0 but got $pc4,$pc4z" 1>&2 + echo "$failmsg dscp3 and dscp0 > 0 but got $pc4,$pc4z" 1>&2 ret=1 ok=0 fi ;; "dscp_ingress") if [ "$pc4" -eq 0 ] || [ "$pc4z" -gt 0 ]; then - echo "FAIL: dscp counters do not match, expected dscp3 > 0, dscp0 == 0 but got $pc4,$pc4z" 1>&2 + echo "$failmsg dscp3 > 0, dscp0 == 0 but got $pc4,$pc4z" 1>&2 ret=1 ok=0 fi ;; "dscp_egress") if [ "$pc4" -eq 0 ] || [ "$pc4z" -gt 0 ]; then - echo "FAIL: dscp counters do not match, expected dscp3 > 0, dscp0 == 0 but got $pc4,$pc4z" 1>&2 + echo "$failmsg dscp3 > 0, dscp0 == 0 but got $pc4,$pc4z" 1>&2 ret=1 ok=0 fi ;; *) - echo "FAIL: Unknown DSCP check" 1>&2 + echo "$failmsg: Unknown DSCP check" 1>&2 ret=1 ok=0 esac @@ -319,9 +328,9 @@ check_dscp() check_transfer() { - in=$1 - out=$2 - what=$3 + local in=$1 + local out=$2 + local what=$3 if ! cmp "$in" "$out" > /dev/null 2>&1; then echo "FAIL: file mismatch for $what" 1>&2 @@ -342,25 +351,39 @@ test_tcp_forwarding_ip() { local nsa=$1 local nsb=$2 - local dstip=$3 - local dstport=$4 + local pmtu=$3 + local dstip=$4 + local dstport=$5 local lret=0 + local socatc + local socatl + local infile="$nsin" - timeout "$SOCAT_TIMEOUT" ip netns exec "$nsb" socat -4 TCP-LISTEN:12345,reuseaddr STDIO < "$nsin" > "$ns2out" & + if [ $pmtu -eq 0 ]; then + infile="$nsin_small" + fi + + timeout "$SOCAT_TIMEOUT" ip netns exec "$nsb" socat -4 TCP-LISTEN:12345,reuseaddr STDIO < "$infile" > "$ns2out" & lpid=$! busywait 1000 listener_ready - timeout "$SOCAT_TIMEOUT" ip netns exec "$nsa" socat -4 TCP:"$dstip":"$dstport" STDIO < "$nsin" > "$ns1out" + timeout "$SOCAT_TIMEOUT" ip netns exec "$nsa" socat -4 TCP:"$dstip":"$dstport" STDIO < "$infile" > "$ns1out" + socatc=$? wait $lpid + socatl=$? - if ! check_transfer "$nsin" "$ns2out" "ns1 -> ns2"; then + if [ $socatl -ne 0 ] || [ $socatc -ne 0 ];then + rc=1 + fi + + if ! check_transfer "$infile" "$ns2out" "ns1 -> ns2"; then lret=1 ret=1 fi - if ! check_transfer "$nsin" "$ns1out" "ns1 <- ns2"; then + if ! check_transfer "$infile" "$ns1out" "ns1 <- ns2"; then lret=1 ret=1 fi @@ -370,14 +393,16 @@ test_tcp_forwarding_ip() test_tcp_forwarding() { - test_tcp_forwarding_ip "$1" "$2" 10.0.2.99 12345 + local pmtu="$3" + + test_tcp_forwarding_ip "$1" "$2" "$pmtu" 10.0.2.99 12345 return $? } test_tcp_forwarding_set_dscp() { - check_dscp "dscp_none" + local pmtu="$3" ip netns exec "$nsr1" nft -f - <&2 @@ -489,8 +519,9 @@ table ip nat { } EOF +check_dscp "dscp_none" "0" if ! test_tcp_forwarding_set_dscp "$ns1" "$ns2" 0 ""; then - echo "FAIL: flow offload for ns1/ns2 with dscp update" 1>&2 + echo "FAIL: flow offload for ns1/ns2 with dscp update and no pmtu discovery" 1>&2 exit 0 fi @@ -512,6 +543,14 @@ ip netns exec "$ns2" sysctl net.ipv4.ip_no_pmtu_disc=0 > /dev/null # are lower than file size and packets were forwarded via flowtable layer. # For earlier tests (large mtus), packets cannot be handled via flowtable # (except pure acks and other small packets). +ip netns exec "$nsr1" nft reset counters table inet filter >/dev/null +ip netns exec "$ns2" nft reset counters table inet filter >/dev/null + +if ! test_tcp_forwarding_set_dscp "$ns1" "$ns2" 1 ""; then + echo "FAIL: flow offload for ns1/ns2 with dscp update and pmtu discovery" 1>&2 + exit 0 +fi + ip netns exec "$nsr1" nft reset counters table inet filter >/dev/null if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 ""; then @@ -644,7 +683,7 @@ ip -net "$ns2" route del 192.168.10.1 via 10.0.2.1 ip -net "$ns2" route add default via 10.0.2.1 ip -net "$ns2" route add default via dead:2::1 -if test_tcp_forwarding "$ns1" "$ns2"; then +if test_tcp_forwarding "$ns1" "$ns2" 1; then check_counters "ipsec tunnel mode for ns1/ns2" else echo "FAIL: ipsec tunnel mode for ns1/ns2" @@ -668,7 +707,7 @@ if [ "$1" = "" ]; then fi echo "re-run with random mtus and file size: -o $o -l $l -r $r -s $filesize" - $0 -o "$o" -l "$l" -r "$r" -s "$filesize" + $0 -o "$o" -l "$l" -r "$r" -s "$filesize" || ret=1 fi exit $ret diff --git a/tools/testing/selftests/net/netfilter/nft_interface_stress.sh b/tools/testing/selftests/net/netfilter/nft_interface_stress.sh index 5ff7be9daeee..c0fffaa6dbd9 100755 --- a/tools/testing/selftests/net/netfilter/nft_interface_stress.sh +++ b/tools/testing/selftests/net/netfilter/nft_interface_stress.sh @@ -10,6 +10,8 @@ source lib.sh checktool "nft --version" "run test without nft tool" checktool "iperf3 --version" "run test without iperf3 tool" +read kernel_tainted < /proc/sys/kernel/tainted + # how many seconds to torture the kernel? # default to 80% of max run time but don't exceed 48s TEST_RUNTIME=$((${kselftest_timeout:-60} * 8 / 10)) @@ -135,7 +137,8 @@ else wait fi -[[ $( + * + * Emit THREAD_COUNT UDP packets sharing the same saddr:daddr pair. + * + * This mimics DNS resolver libraries that emit A and AAAA requests + * in parallel. + * + * This exercises conntrack clash resolution logic added and later + * refined in + * + * 71d8c47fc653 ("netfilter: conntrack: introduce clash resolution on insertion race") + * ed07d9a021df ("netfilter: nf_conntrack: resolve clash for matching conntracks") + * 6a757c07e51f ("netfilter: conntrack: allow insertion of clashing entries") + */ +#include +#include +#include +#include +#include +#include +#include + +#define THREAD_COUNT 128 + +struct thread_args { + const struct sockaddr_in *si_remote; + int sockfd; +}; + +static volatile int wait = 1; + +static void *thread_main(void *varg) +{ + const struct sockaddr_in *si_remote; + const struct thread_args *args = varg; + static const char msg[] = "foo"; + + si_remote = args->si_remote; + + while (wait == 1) + ; + + if (sendto(args->sockfd, msg, strlen(msg), MSG_NOSIGNAL, + (struct sockaddr *)si_remote, sizeof(*si_remote)) < 0) + exit(111); + + return varg; +} + +static int run_test(int fd, const struct sockaddr_in *si_remote) +{ + struct thread_args thread_args = { + .si_remote = si_remote, + .sockfd = fd, + }; + pthread_t *tid = calloc(THREAD_COUNT, sizeof(pthread_t)); + unsigned int repl_count = 0, timeout = 0; + int i; + + if (!tid) { + perror("calloc"); + return 1; + } + + for (i = 0; i < THREAD_COUNT; i++) { + int err = pthread_create(&tid[i], NULL, &thread_main, &thread_args); + + if (err != 0) { + perror("pthread_create"); + exit(1); + } + } + + wait = 0; + + for (i = 0; i < THREAD_COUNT; i++) + pthread_join(tid[i], NULL); + + while (repl_count < THREAD_COUNT) { + struct sockaddr_in si_repl; + socklen_t si_repl_len = sizeof(si_repl); + char repl[512]; + ssize_t ret; + + ret = recvfrom(fd, repl, sizeof(repl), MSG_NOSIGNAL, + (struct sockaddr *) &si_repl, &si_repl_len); + if (ret < 0) { + if (timeout++ > 5000) { + fputs("timed out while waiting for reply from thread\n", stderr); + break; + } + + /* give reply time to pass though the stack */ + usleep(1000); + continue; + } + + if (si_repl_len != sizeof(*si_remote)) { + fprintf(stderr, "warning: reply has unexpected repl_len %d vs %d\n", + (int)si_repl_len, (int)sizeof(si_repl)); + } else if (si_remote->sin_addr.s_addr != si_repl.sin_addr.s_addr || + si_remote->sin_port != si_repl.sin_port) { + char a[64], b[64]; + + inet_ntop(AF_INET, &si_remote->sin_addr, a, sizeof(a)); + inet_ntop(AF_INET, &si_repl.sin_addr, b, sizeof(b)); + + fprintf(stderr, "reply from wrong source: want %s:%d got %s:%d\n", + a, ntohs(si_remote->sin_port), b, ntohs(si_repl.sin_port)); + } + + repl_count++; + } + + printf("got %d of %d replies\n", repl_count, THREAD_COUNT); + + free(tid); + + return repl_count == THREAD_COUNT ? 0 : 1; +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_in si_local = { + .sin_family = AF_INET, + }; + struct sockaddr_in si_remote = { + .sin_family = AF_INET, + }; + int fd, ret; + + if (argc < 3) { + fputs("Usage: send_udp \n", stderr); + return 1; + } + + si_remote.sin_port = htons(atoi(argv[2])); + si_remote.sin_addr.s_addr = inet_addr(argv[1]); + + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_UDP); + if (fd < 0) { + perror("socket"); + return 1; + } + + if (bind(fd, (struct sockaddr *)&si_local, sizeof(si_local)) < 0) { + perror("bind"); + return 1; + } + + ret = run_test(fd, &si_remote); + + close(fd); + + return ret; +} diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index cd8a58097448..1f5227f3d64d 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -385,7 +385,7 @@ static int get_bind_to_device(int sd, char *name, size_t len) name[0] = '\0'; rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen); if (rc < 0) - log_err_errno("setsockopt(SO_BINDTODEVICE)"); + log_err_errno("getsockopt(SO_BINDTODEVICE)"); return rc; } @@ -535,7 +535,7 @@ static int set_freebind(int sd, int version) break; case AF_INET6: if (setsockopt(sd, SOL_IPV6, IPV6_FREEBIND, &one, sizeof(one))) { - log_err_errno("setsockopt(IPV6_FREEBIND"); + log_err_errno("setsockopt(IPV6_FREEBIND)"); rc = -1; } break; @@ -812,7 +812,7 @@ static int convert_addr(struct sock_args *args, const char *_str, sep++; if (str_to_uint(sep, 1, pfx_len_max, &args->prefix_len) != 0) { - fprintf(stderr, "Invalid port\n"); + fprintf(stderr, "Invalid prefix length\n"); return 1; } } else { @@ -1272,7 +1272,7 @@ static int msg_loop(int client, int sd, void *addr, socklen_t alen, } } - nfds = interactive ? MAX(fileno(stdin), sd) + 1 : sd + 1; + nfds = interactive ? MAX(fileno(stdin), sd) + 1 : sd + 1; while (1) { FD_ZERO(&rfds); FD_SET(sd, &rfds); @@ -1492,7 +1492,7 @@ static int lsock_init(struct sock_args *args) sd = socket(args->version, args->type, args->protocol); if (sd < 0) { log_err_errno("Error opening socket"); - return -1; + return -1; } if (set_reuseaddr(sd) != 0) @@ -1912,7 +1912,7 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args) * waiting to be told when to continue */ if (read(fd, &buf, sizeof(buf)) <= 0) { - log_err_errno("Failed to read IPC status from status"); + log_err_errno("Failed to read IPC status from pipe"); return 1; } if (!buf) { diff --git a/tools/testing/selftests/net/nl_netdev.py b/tools/testing/selftests/net/nl_netdev.py index beaee5e4e2aa..5c66421ab8aa 100755 --- a/tools/testing/selftests/net/nl_netdev.py +++ b/tools/testing/selftests/net/nl_netdev.py @@ -2,8 +2,9 @@ # SPDX-License-Identifier: GPL-2.0 import time +from os import system from lib.py import ksft_run, ksft_exit, ksft_pr -from lib.py import ksft_eq, ksft_ge, ksft_busy_wait +from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_busy_wait from lib.py import NetdevFamily, NetdevSimDev, ip @@ -34,6 +35,128 @@ def napi_list_check(nf) -> None: ksft_eq(len(napis), 100, comment=f"queue count after reset queue {q} mode {i}") +def napi_set_threaded(nf) -> None: + """ + Test that verifies various cases of napi threaded + set and unset at napi and device level. + """ + with NetdevSimDev(queue_count=2) as nsimdev: + nsim = nsimdev.nsims[0] + + ip(f"link set dev {nsim.ifname} up") + + napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) + ksft_eq(len(napis), 2) + + napi0_id = napis[0]['id'] + napi1_id = napis[1]['id'] + + # set napi threaded and verify + nf.napi_set({'id': napi0_id, 'threaded': "enabled"}) + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "enabled") + ksft_ne(napi0.get('pid'), None) + + # check it is not set for napi1 + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "disabled") + ksft_eq(napi1.get('pid'), None) + + ip(f"link set dev {nsim.ifname} down") + ip(f"link set dev {nsim.ifname} up") + + # verify if napi threaded is still set + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "enabled") + ksft_ne(napi0.get('pid'), None) + + # check it is still not set for napi1 + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "disabled") + ksft_eq(napi1.get('pid'), None) + + # unset napi threaded and verify + nf.napi_set({'id': napi0_id, 'threaded': "disabled"}) + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "disabled") + ksft_eq(napi0.get('pid'), None) + + # set threaded at device level + system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") + + # check napi threaded is set for both napis + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "enabled") + ksft_ne(napi0.get('pid'), None) + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "enabled") + ksft_ne(napi1.get('pid'), None) + + # unset threaded at device level + system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") + + # check napi threaded is unset for both napis + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "disabled") + ksft_eq(napi0.get('pid'), None) + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "disabled") + ksft_eq(napi1.get('pid'), None) + + # set napi threaded for napi0 + nf.napi_set({'id': napi0_id, 'threaded': 1}) + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "enabled") + ksft_ne(napi0.get('pid'), None) + + # unset threaded at device level + system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") + + # check napi threaded is unset for both napis + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "disabled") + ksft_eq(napi0.get('pid'), None) + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "disabled") + ksft_eq(napi1.get('pid'), None) + +def dev_set_threaded(nf) -> None: + """ + Test that verifies various cases of napi threaded + set and unset at device level using sysfs. + """ + with NetdevSimDev(queue_count=2) as nsimdev: + nsim = nsimdev.nsims[0] + + ip(f"link set dev {nsim.ifname} up") + + napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) + ksft_eq(len(napis), 2) + + napi0_id = napis[0]['id'] + napi1_id = napis[1]['id'] + + # set threaded + system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") + + # check napi threaded is set for both napis + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "enabled") + ksft_ne(napi0.get('pid'), None) + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "enabled") + ksft_ne(napi1.get('pid'), None) + + # unset threaded + system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") + + # check napi threaded is unset for both napis + napi0 = nf.napi_get({'id': napi0_id}) + ksft_eq(napi0['threaded'], "disabled") + ksft_eq(napi0.get('pid'), None) + napi1 = nf.napi_get({'id': napi1_id}) + ksft_eq(napi1['threaded'], "disabled") + ksft_eq(napi1.get('pid'), None) def nsim_rxq_reset_down(nf) -> None: """ @@ -122,7 +245,7 @@ def page_pool_check(nf) -> None: def main() -> None: nf = NetdevFamily() ksft_run([empty_check, lo_check, page_pool_check, napi_list_check, - nsim_rxq_reset_down], + dev_set_threaded, napi_set_threaded, nsim_rxq_reset_down], args=(nf, )) ksft_exit() diff --git a/tools/testing/selftests/net/packetdrill/ksft_runner.sh b/tools/testing/selftests/net/packetdrill/ksft_runner.sh index ef8b25a606d8..a7e790af38ff 100755 --- a/tools/testing/selftests/net/packetdrill/ksft_runner.sh +++ b/tools/testing/selftests/net/packetdrill/ksft_runner.sh @@ -35,20 +35,7 @@ failfunc=ktap_test_fail if [[ -n "${KSFT_MACHINE_SLOW}" ]]; then optargs+=('--tolerance_usecs=14000') - - # xfail tests that are known flaky with dbg config, not fixable. - # still run them for coverage (and expect 100% pass without dbg). - declare -ar xfail_list=( - "tcp_eor_no-coalesce-retrans.pkt" - "tcp_fast_recovery_prr-ss.*.pkt" - "tcp_slow_start_slow-start-after-win-update.pkt" - "tcp_timestamping.*.pkt" - "tcp_user_timeout_user-timeout-probe.pkt" - "tcp_zerocopy_epoll_.*.pkt" - "tcp_tcp_info_tcp-info-.*-limited.pkt" - ) - readonly xfail_regex="^($(printf '%s|' "${xfail_list[@]}"))$" - [[ "$script" =~ ${xfail_regex} ]] && failfunc=ktap_test_xfail + failfunc=ktap_test_xfail fi ktap_print_header diff --git a/tools/testing/selftests/net/packetdrill/tcp_blocking_blocking-read.pkt b/tools/testing/selftests/net/packetdrill/tcp_blocking_blocking-read.pkt index 914eabab367a..657e42ca65b5 100644 --- a/tools/testing/selftests/net/packetdrill/tcp_blocking_blocking-read.pkt +++ b/tools/testing/selftests/net/packetdrill/tcp_blocking_blocking-read.pkt @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Test for blocking read. + --tolerance_usecs=10000 +--mss=1000 `./defaults.sh` diff --git a/tools/testing/selftests/net/packetdrill/tcp_dsack_mult.pkt b/tools/testing/selftests/net/packetdrill/tcp_dsack_mult.pkt new file mode 100644 index 000000000000..c790d0af635e --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_dsack_mult.pkt @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +// Test various DSACK (RFC 2883) behaviors. + +--mss=1000 + +`./defaults.sh` + + + 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 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 1024 + +0 accept(3, ..., ...) = 4 + +// First SACK range. + +0 < P. 1001:2001(1000) ack 1 win 1024 + +0 > . 1:1(0) ack 1 + +// Check SACK coalescing (contiguous sequence). + +0 < P. 2001:3001(1000) ack 1 win 1024 + +0 > . 1:1(0) ack 1 + +// Check we have two SACK ranges for non contiguous sequences. + +0 < P. 4001:5001(1000) ack 1 win 1024 + +0 > . 1:1(0) ack 1 + +// Three ranges. + +0 < P. 7001:8001(1000) ack 1 win 1024 + +0 > . 1:1(0) ack 1 + +// DSACK (1001:3001) + SACK (6001:7001) + +0 < P. 1:6001(6000) ack 1 win 1024 + +0 > . 1:1(0) ack 6001 + +// DSACK (7001:8001) + +0 < P. 6001:8001(2000) ack 1 win 1024 + +0 > . 1:1(0) ack 8001 + +// DSACK for an older segment. + +0 < P. 1:1001(1000) ack 1 win 1024 + +0 > . 1:1(0) ack 8001 diff --git a/tools/testing/selftests/net/packetdrill/tcp_inq_client.pkt b/tools/testing/selftests/net/packetdrill/tcp_inq_client.pkt index df49c67645ac..e13f0eee9795 100644 --- a/tools/testing/selftests/net/packetdrill/tcp_inq_client.pkt +++ b/tools/testing/selftests/net/packetdrill/tcp_inq_client.pkt @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Test TCP_INQ and TCP_CM_INQ on the client side. + +--mss=1000 + `./defaults.sh ` diff --git a/tools/testing/selftests/net/packetdrill/tcp_inq_server.pkt b/tools/testing/selftests/net/packetdrill/tcp_inq_server.pkt index 04a5e2590c62..14dd5f813d50 100644 --- a/tools/testing/selftests/net/packetdrill/tcp_inq_server.pkt +++ b/tools/testing/selftests/net/packetdrill/tcp_inq_server.pkt @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Test TCP_INQ and TCP_CM_INQ on the server side. + +--mss=1000 + `./defaults.sh ` diff --git a/tools/testing/selftests/net/packetdrill/tcp_ooo-before-and-after-accept.pkt b/tools/testing/selftests/net/packetdrill/tcp_ooo-before-and-after-accept.pkt new file mode 100644 index 000000000000..09aabc775e80 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_ooo-before-and-after-accept.pkt @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 + +--mss=1000 + +`./defaults.sh +sysctl -q net.ipv4.tcp_rmem="4096 131072 $((32*1024*1024))"` + +// Test that a not-yet-accepted socket does not change +// its initial sk_rcvbuf (tcp_rmem[1]) when receiving ooo packets. + + +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 + + +0 < S 0:0(0) win 65535 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + +0 < . 2001:41001(39000) ack 1 win 257 + +0 > . 1:1(0) ack 1 + +0 < . 41001:101001(60000) ack 1 win 257 + +0 > . 1:1(0) ack 1 + +0 < . 1:1001(1000) ack 1 win 257 + +0 > . 1:1(0) ack 1001 + +0 < . 1001:2001(1000) ack 1 win 257 + +0 > . 1:1(0) ack 101001 + + +0 accept(3, ..., ...) = 4 + + +0 %{ assert SK_MEMINFO_RCVBUF == 131072, SK_MEMINFO_RCVBUF }% + + +0 close(4) = 0 + +0 close(3) = 0 + +// Test that ooo packets for accepted sockets do increase sk_rcvbuf + +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 + + +0 < S 0:0(0) win 65535 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + + +0 accept(3, ..., ...) = 4 + + +0 < . 2001:41001(39000) ack 1 win 257 + +0 > . 1:1(0) ack 1 + +0 < . 41001:101001(60000) ack 1 win 257 + +0 > . 1:1(0) ack 1 + + +0 %{ assert SK_MEMINFO_RCVBUF > 131072, SK_MEMINFO_RCVBUF }% + diff --git a/tools/testing/selftests/net/packetdrill/tcp_ooo_rcv_mss.pkt b/tools/testing/selftests/net/packetdrill/tcp_ooo_rcv_mss.pkt new file mode 100644 index 000000000000..7e6bc5fb0c8d --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_ooo_rcv_mss.pkt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +--mss=1000 + +`./defaults.sh +sysctl -q net.ipv4.tcp_rmem="4096 131072 $((32*1024*1024))"` + + +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 + + +0 < S 0:0(0) win 65535 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + + +0 accept(3, ..., ...) = 4 + + +0 < . 2001:11001(9000) ack 1 win 257 + +0 > . 1:1(0) ack 1 win 81 + +// check that ooo packet properly updates tcpi_rcv_mss + +0 %{ assert tcpi_rcv_mss == 1000, tcpi_rcv_mss }% + + +0 < . 11001:21001(10000) ack 1 win 257 + +0 > . 1:1(0) ack 1 win 81 + diff --git a/tools/testing/selftests/net/packetdrill/tcp_rcv_big_endseq.pkt b/tools/testing/selftests/net/packetdrill/tcp_rcv_big_endseq.pkt new file mode 100644 index 000000000000..3848b419e68c --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rcv_big_endseq.pkt @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 + +--mss=1000 + +`./defaults.sh` + + 0 `nstat -n` + +// Establish a connection. + +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [10000], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + + +0 accept(3, ..., ...) = 4 + + +0 < P. 1:4001(4000) ack 1 win 257 + +0 > . 1:1(0) ack 4001 win 5000 + +// packet in sequence : SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE / LINUX_MIB_BEYOND_WINDOW + +0 < P. 4001:54001(50000) ack 1 win 257 + +0 > . 1:1(0) ack 4001 win 5000 + +// ooo packet. : SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE / LINUX_MIB_BEYOND_WINDOW + +1 < P. 5001:55001(50000) ack 1 win 257 + +0 > . 1:1(0) ack 4001 win 5000 + +// SKB_DROP_REASON_TCP_INVALID_SEQUENCE / LINUX_MIB_BEYOND_WINDOW + +0 < P. 70001:80001(10000) ack 1 win 257 + +0 > . 1:1(0) ack 4001 win 5000 + + +0 read(4, ..., 100000) = 4000 + +// If queue is empty, accept a packet even if its end_seq is above wup + rcv_wnd + +0 < P. 4001:54001(50000) ack 1 win 257 + +0 > . 1:1(0) ack 54001 win 0 + +// Check LINUX_MIB_BEYOND_WINDOW has been incremented 3 times. ++0 `nstat | grep TcpExtBeyondWindow | grep -q " 3 "` diff --git a/tools/testing/selftests/net/packetdrill/tcp_rcv_toobig.pkt b/tools/testing/selftests/net/packetdrill/tcp_rcv_toobig.pkt new file mode 100644 index 000000000000..f575c0ff89da --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rcv_toobig.pkt @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 + +--mss=1000 + +`./defaults.sh` + + 0 `nstat -n` + +// Establish a connection. + +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [20000], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 win 18980 + +.1 < . 1:1(0) ack 1 win 257 + + +0 accept(3, ..., ...) = 4 + + +0 < P. 1:20001(20000) ack 1 win 257 + +.04 > . 1:1(0) ack 20001 win 18000 + + +0 setsockopt(4, SOL_SOCKET, SO_RCVBUF, [12000], 4) = 0 + +0 < P. 20001:80001(60000) ack 1 win 257 + +0 > . 1:1(0) ack 20001 win 18000 + + +0 read(4, ..., 20000) = 20000 +// A too big packet is accepted if the receive queue is empty + +0 < P. 20001:80001(60000) ack 1 win 257 + +0 > . 1:1(0) ack 80001 win 0 + diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 2e8243a65b50..d6c00efeb664 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -21,6 +21,7 @@ ALL_TESTS=" kci_test_vrf kci_test_encap kci_test_macsec + kci_test_macsec_vlan kci_test_ipsec kci_test_ipsec_offload kci_test_fdb_get @@ -30,6 +31,7 @@ ALL_TESTS=" kci_test_address_proto kci_test_enslave_bonding kci_test_mngtmpaddr + kci_test_operstate " devdummy="test-dummy0" @@ -291,6 +293,17 @@ kci_test_route_get() end_test "PASS: route get" } +check_addr_not_exist() +{ + dev=$1 + addr=$2 + if ip addr show dev $dev | grep -q $addr; then + return 1 + else + return 0 + fi +} + kci_test_addrlft() { for i in $(seq 10 100) ;do @@ -298,9 +311,8 @@ kci_test_addrlft() run_cmd ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1)) done - sleep 5 - run_cmd_grep_fail "10.23.11." ip addr show dev "$devdummy" - if [ $? -eq 0 ]; then + slowwait 5 check_addr_not_exist "$devdummy" "10.23.11." + if [ $? -eq 1 ]; then check_err 1 end_test "FAIL: preferred_lft addresses remaining" return @@ -561,6 +573,41 @@ kci_test_macsec() end_test "PASS: macsec" } +# Test __dev_set_rx_mode call from dev_uc_add under addr_list_lock spinlock. +# Make sure __dev_set_promiscuity is not grabbing (sleeping) netdev instance +# lock. +# https://lore.kernel.org/netdev/2aff4342b0f5b1539c02ffd8df4c7e58dd9746e7.camel@nvidia.com/ +kci_test_macsec_vlan() +{ + msname="test_macsec1" + vlanname="test_vlan1" + local ret=0 + run_cmd_grep "^Usage: ip macsec" ip macsec help + if [ $? -ne 0 ]; then + end_test "SKIP: macsec: iproute2 too old" + return $ksft_skip + fi + run_cmd ip link add link "$devdummy" "$msname" type macsec port 42 encrypt on + if [ $ret -ne 0 ];then + end_test "FAIL: can't add macsec interface, skipping test" + return 1 + fi + + run_cmd ip link set dev "$msname" up + ip link add link "$msname" name "$vlanname" type vlan id 1 + ip link set dev "$vlanname" address 00:11:22:33:44:88 + ip link set dev "$vlanname" up + run_cmd ip link del dev "$vlanname" + run_cmd ip link del dev "$msname" + + if [ $ret -ne 0 ];then + end_test "FAIL: macsec_vlan" + return 1 + fi + + end_test "PASS: macsec_vlan" +} + #------------------------------------------------------------------- # Example commands # ip x s add proto esp src 14.0.0.52 dst 14.0.0.70 \ @@ -673,6 +720,11 @@ kci_test_ipsec_offload() sysfsf=$sysfsd/ipsec sysfsnet=/sys/bus/netdevsim/devices/netdevsim0/net/ probed=false + esp4_offload_probed_default=false + + if lsmod | grep -q esp4_offload; then + esp4_offload_probed_default=true + fi if ! mount | grep -q debugfs; then mount -t debugfs none /sys/kernel/debug/ &> /dev/null @@ -766,6 +818,7 @@ EOF fi # clean up any leftovers + ! "$esp4_offload_probed_default" && lsmod | grep -q esp4_offload && rmmod esp4_offload echo 0 > /sys/bus/netdevsim/del_device $probed && rmmod netdevsim @@ -1334,6 +1387,39 @@ kci_test_mngtmpaddr() return $ret } +kci_test_operstate() +{ + local ret=0 + + # Check that it is possible to set operational state during device + # creation and that it is preserved when the administrative state of + # the device is toggled. + run_cmd ip link add name vx0 up state up type vxlan id 10010 dstport 4789 + run_cmd_grep "state UP" ip link show dev vx0 + run_cmd ip link set dev vx0 down + run_cmd_grep "state DOWN" ip link show dev vx0 + run_cmd ip link set dev vx0 up + run_cmd_grep "state UP" ip link show dev vx0 + + run_cmd ip link del dev vx0 + + # Check that it is possible to set the operational state of the device + # after creation. + run_cmd ip link add name vx0 up type vxlan id 10010 dstport 4789 + run_cmd_grep "state UNKNOWN" ip link show dev vx0 + run_cmd ip link set dev vx0 state up + run_cmd_grep "state UP" ip link show dev vx0 + + run_cmd ip link del dev vx0 + + if [ "$ret" -ne 0 ]; then + end_test "FAIL: operstate" + return 1 + fi + + end_test "PASS: operstate" +} + kci_test_rtnl() { local current_test diff --git a/tools/testing/selftests/net/rtnetlink_notification.sh b/tools/testing/selftests/net/rtnetlink_notification.sh new file mode 100755 index 000000000000..3f9780232bd6 --- /dev/null +++ b/tools/testing/selftests/net/rtnetlink_notification.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# This test is for checking rtnetlink notification callpaths, and get as much +# coverage as possible. +# +# set -e + +ALL_TESTS=" + kci_test_mcast_addr_notification + kci_test_anycast_addr_notification +" + +source lib.sh +test_dev="test-dummy1" + +kci_test_mcast_addr_notification() +{ + RET=0 + local tmpfile + local monitor_pid + local match_result + + tmpfile=$(mktemp) + defer rm "$tmpfile" + + ip monitor maddr > $tmpfile & + monitor_pid=$! + defer kill_process "$monitor_pid" + + sleep 1 + + if [ ! -e "/proc/$monitor_pid" ]; then + RET=$ksft_skip + log_test "mcast addr notification: iproute2 too old" + return $RET + fi + + ip link add name "$test_dev" type dummy + check_err $? "failed to add dummy interface" + ip link set "$test_dev" up + check_err $? "failed to set dummy interface up" + ip link del dev "$test_dev" + check_err $? "Failed to delete dummy interface" + sleep 1 + + # There should be 4 line matches as follows. + # 13: test-dummy1    inet6 mcast ff02::1 scope global  + # 13: test-dummy1    inet mcast 224.0.0.1 scope global  + # Deleted 13: test-dummy1    inet mcast 224.0.0.1 scope global  + # Deleted 13: test-dummy1    inet6 mcast ff02::1 scope global  + match_result=$(grep -cE "$test_dev.*(224.0.0.1|ff02::1)" "$tmpfile") + if [ "$match_result" -ne 4 ]; then + RET=$ksft_fail + fi + log_test "mcast addr notification: Expected 4 matches, got $match_result" + return $RET +} + +kci_test_anycast_addr_notification() +{ + RET=0 + local tmpfile + local monitor_pid + local match_result + + tmpfile=$(mktemp) + defer rm "$tmpfile" + + ip monitor acaddress > "$tmpfile" & + monitor_pid=$! + defer kill_process "$monitor_pid" + sleep 1 + + if [ ! -e "/proc/$monitor_pid" ]; then + RET=$ksft_skip + log_test "anycast addr notification: iproute2 too old" + return "$RET" + fi + + ip link add name "$test_dev" type dummy + check_err $? "failed to add dummy interface" + ip link set "$test_dev" up + check_err $? "failed to set dummy interface up" + sysctl -qw net.ipv6.conf."$test_dev".forwarding=1 + ip link del dev "$test_dev" + check_err $? "Failed to delete dummy interface" + sleep 1 + + # There should be 2 line matches as follows. + # 9: dummy2 inet6 any fe80:: scope global + # Deleted 9: dummy2 inet6 any fe80:: scope global + match_result=$(grep -cE "$test_dev.*(fe80::)" "$tmpfile") + if [ "$match_result" -ne 2 ]; then + RET=$ksft_fail + fi + log_test "anycast addr notification: Expected 2 matches, got $match_result" + return "$RET" +} + +#check for needed privileges +if [ "$(id -u)" -ne 0 ];then + RET=$ksft_skip + log_test "need root privileges" + exit $RET +fi + +require_command ip + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh index ba730655a7bf..4bc135e5c22c 100755 --- a/tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh +++ b/tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh @@ -594,7 +594,7 @@ setup_rt_local_sids() dev "${DUMMY_DEVNAME}" # all SIDs for VPNs start with a common locator. Routes and SRv6 - # Endpoint behavior instaces are grouped together in the 'localsid' + # Endpoint behavior instances are grouped together in the 'localsid' # table. ip -netns "${nsname}" -6 rule \ add to "${VPN_LOCATOR_SERVICE}::/16" \ diff --git a/tools/testing/selftests/net/srv6_end_x_next_csid_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_x_next_csid_l3vpn_test.sh index 4b86040c58c6..34b781a2ae74 100755 --- a/tools/testing/selftests/net/srv6_end_x_next_csid_l3vpn_test.sh +++ b/tools/testing/selftests/net/srv6_end_x_next_csid_l3vpn_test.sh @@ -72,6 +72,9 @@ # Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y in # the selftest network. # +# In addition, every router interface connecting rt-x to rt-y is assigned an +# IPv6 link-local address fe80::x:y/64. +# # Local SID/C-SID table # ===================== # @@ -521,6 +524,9 @@ setup_rt_networking() ip -netns "${nsname}" addr \ add "${net_prefix}::${rt}/64" dev "${devname}" nodad + ip -netns "${nsname}" addr \ + add "fe80::${rt}:${neigh}/64" dev "${devname}" nodad + ip -netns "${nsname}" link set "${devname}" up done @@ -609,6 +615,27 @@ set_end_x_nextcsid() nflen "${LCNODEFUNC_BLEN}" dev "${DUMMY_DEVNAME}" } +set_end_x_ll_nextcsid() +{ + local rt="$1" + local adj="$2" + + eval nsname=\${$(get_rtname "${rt}")} + lcnode_func_prefix="$(build_lcnode_func_prefix "${rt}")" + nh6_ll_addr="fe80::${adj}:${rt}" + oifname="veth-rt-${rt}-${adj}" + + # enabled NEXT-C-SID SRv6 End.X behavior via an IPv6 link-local nexthop + # address (note that "dev" is the dummy dum0 device chosen for the sake + # of simplicity). + ip -netns "${nsname}" -6 route \ + replace "${lcnode_func_prefix}" \ + table "${LOCALSID_TABLE_ID}" \ + encap seg6local action End.X nh6 "${nh6_ll_addr}" \ + oif "${oifname}" flavors next-csid lblen "${LCBLOCK_BLEN}" \ + nflen "${LCNODEFUNC_BLEN}" dev "${DUMMY_DEVNAME}" +} + set_underlay_sids_reachability() { local rt="$1" @@ -654,7 +681,7 @@ setup_rt_local_sids() set_underlay_sids_reachability "${rt}" "${rt_neighs}" # all SIDs for VPNs start with a common locator. Routes and SRv6 - # Endpoint behavior instaces are grouped together in the 'localsid' + # Endpoint behavior instances are grouped together in the 'localsid' # table. ip -netns "${nsname}" -6 rule \ add to "${VPN_LOCATOR_SERVICE}::/16" \ @@ -1016,6 +1043,27 @@ host_vpn_tests() check_and_log_hs_ipv4_connectivity 1 2 check_and_log_hs_ipv4_connectivity 2 1 + + # Setup the adjacencies in the SRv6 aware routers using IPv6 link-local + # addresses. + # - rt-3 SRv6 End.X adjacency with rt-4 + # - rt-4 SRv6 End.X adjacency with rt-1 + set_end_x_ll_nextcsid 3 4 + set_end_x_ll_nextcsid 4 1 + + log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv6), link-local" + + check_and_log_hs_ipv6_connectivity 1 2 + check_and_log_hs_ipv6_connectivity 2 1 + + log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv4), link-local" + + check_and_log_hs_ipv4_connectivity 1 2 + check_and_log_hs_ipv4_connectivity 2 1 + + # Restore the previous adjacencies. + set_end_x_nextcsid 3 4 + set_end_x_nextcsid 4 1 } __nextcsid_end_x_behavior_test() diff --git a/tools/testing/selftests/net/srv6_hencap_red_l3vpn_test.sh b/tools/testing/selftests/net/srv6_hencap_red_l3vpn_test.sh index 3efce1718c5f..6a68c7eff1dc 100755 --- a/tools/testing/selftests/net/srv6_hencap_red_l3vpn_test.sh +++ b/tools/testing/selftests/net/srv6_hencap_red_l3vpn_test.sh @@ -395,7 +395,7 @@ setup_rt_local_sids() dev "${VRF_DEVNAME}" # all SIDs for VPNs start with a common locator. Routes and SRv6 - # Endpoint behavior instaces are grouped together in the 'localsid' + # Endpoint behavior instances are grouped together in the 'localsid' # table. ip -netns "${nsname}" -6 rule \ add to "${VPN_LOCATOR_SERVICE}::/16" \ diff --git a/tools/testing/selftests/net/srv6_hl2encap_red_l2vpn_test.sh b/tools/testing/selftests/net/srv6_hl2encap_red_l2vpn_test.sh index cabc70538ffe..0979b5316fdf 100755 --- a/tools/testing/selftests/net/srv6_hl2encap_red_l2vpn_test.sh +++ b/tools/testing/selftests/net/srv6_hl2encap_red_l2vpn_test.sh @@ -343,7 +343,7 @@ setup_rt_local_sids() encap seg6local action End dev "${DUMMY_DEVNAME}" # all SIDs for VPNs start with a common locator. Routes and SRv6 - # Endpoint behaviors instaces are grouped together in the 'localsid' + # Endpoint behaviors instances are grouped together in the 'localsid' # table. ip -netns "${nsname}" -6 rule add \ to "${VPN_LOCATOR_SERVICE}::/16" \ diff --git a/tools/testing/selftests/net/tcp_ao/seq-ext.c b/tools/testing/selftests/net/tcp_ao/seq-ext.c index f00245263b20..6478da6a71c3 100644 --- a/tools/testing/selftests/net/tcp_ao/seq-ext.c +++ b/tools/testing/selftests/net/tcp_ao/seq-ext.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Check that after SEQ number wrap-around: * 1. SEQ-extension has upper bytes set - * 2. TCP conneciton is alive and no TCPAOBad segments + * 2. TCP connection is alive and no TCPAOBad segments * In order to test (2), the test doesn't just adjust seq number for a queue * on a connected socket, but migrates it to another sk+port number, so * that there won't be any delayed packets that will fail to verify diff --git a/tools/testing/selftests/net/test_neigh.sh b/tools/testing/selftests/net/test_neigh.sh new file mode 100755 index 000000000000..7c594bf6ead0 --- /dev/null +++ b/tools/testing/selftests/net/test_neigh.sh @@ -0,0 +1,366 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source lib.sh +TESTS=" + extern_valid_ipv4 + extern_valid_ipv6 +" +VERBOSE=0 + +################################################################################ +# Utilities + +run_cmd() +{ + local cmd="$1" + local out + local stderr="2>/dev/null" + + if [ "$VERBOSE" = "1" ]; then + echo "COMMAND: $cmd" + stderr= + fi + + out=$(eval "$cmd" "$stderr") + rc=$? + if [ "$VERBOSE" -eq 1 ] && [ -n "$out" ]; then + echo " $out" + fi + + return $rc +} + +################################################################################ +# Setup + +setup() +{ + set -e + + setup_ns ns1 ns2 + + ip -n "$ns1" link add veth0 type veth peer name veth1 netns "$ns2" + ip -n "$ns1" link set dev veth0 up + ip -n "$ns2" link set dev veth1 up + + ip -n "$ns1" address add 192.0.2.1/24 dev veth0 + ip -n "$ns1" address add 2001:db8:1::1/64 dev veth0 nodad + ip -n "$ns2" address add 192.0.2.2/24 dev veth1 + ip -n "$ns2" address add 2001:db8:1::2/64 dev veth1 nodad + + ip netns exec "$ns1" sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1 + ip netns exec "$ns2" sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1 + + sleep 5 + + set +e +} + +exit_cleanup_all() +{ + cleanup_all_ns + exit "${EXIT_STATUS}" +} + +################################################################################ +# Tests + +extern_valid_common() +{ + local af_str=$1; shift + local ip_addr=$1; shift + local tbl_name=$1; shift + local subnet=$1; shift + local mac + + mac=$(ip -n "$ns2" -j link show dev veth1 | jq -r '.[]["address"]') + + RET=0 + + # Check that simple addition works. + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "No \"extern_valid\" flag after addition" + + log_test "$af_str \"extern_valid\" flag: Add entry" + + RET=0 + + # Check that an entry cannot be added with "extern_valid" flag and an + # invalid state. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr nud none dev veth0 extern_valid" + check_fail $? "Managed to add an entry with \"extern_valid\" flag and an invalid state" + + log_test "$af_str \"extern_valid\" flag: Add with an invalid state" + + RET=0 + + # Check that entry cannot be added with both "extern_valid" flag and + # "use" / "managed" flag. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid use" + check_fail $? "Managed to add an entry with \"extern_valid\" flag and \"use\" flag" + + log_test "$af_str \"extern_valid\" flag: Add with \"use\" flag" + + RET=0 + + # Check that "extern_valid" flag can be toggled using replace. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0" + run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "Did not manage to set \"extern_valid\" flag with replace" + run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_fail $? "Did not manage to clear \"extern_valid\" flag with replace" + + log_test "$af_str \"extern_valid\" flag: Replace entry" + + RET=0 + + # Check that an existing "extern_valid" entry can be marked as + # "managed". + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid managed" + check_err $? "Did not manage to add \"managed\" flag to an existing \"extern_valid\" entry" + + log_test "$af_str \"extern_valid\" flag: Replace entry with \"managed\" flag" + + RET=0 + + # Check that entry cannot be replaced with "extern_valid" flag and an + # invalid state. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh replace $ip_addr nud none dev veth0 extern_valid" + check_fail $? "Managed to replace an entry with \"extern_valid\" flag and an invalid state" + + log_test "$af_str \"extern_valid\" flag: Replace with an invalid state" + + RET=0 + + # Check that an "extern_valid" entry is flushed when the interface is + # put administratively down. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 link set dev veth0 down" + run_cmd "ip -n $ns1 link set dev veth0 up" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0" + check_fail $? "\"extern_valid\" entry not flushed upon interface down" + + log_test "$af_str \"extern_valid\" flag: Interface down" + + RET=0 + + # Check that an "extern_valid" entry is not flushed when the interface + # loses its carrier. + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns2 link set dev veth1 down" + run_cmd "ip -n $ns2 link set dev veth1 up" + run_cmd "sleep 2" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0" + check_err $? "\"extern_valid\" entry flushed upon carrier down" + + log_test "$af_str \"extern_valid\" flag: Carrier down" + + RET=0 + + # Check that when entry transitions to "reachable" state it maintains + # the "extern_valid" flag. Wait "delay_probe" seconds for ARP request / + # NS to be sent. + local delay_probe + + delay_probe=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["delay_probe"]') + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid use" + run_cmd "sleep $((delay_probe / 1000 + 2))" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"REACHABLE\"" + check_err $? "Entry did not transition to \"reachable\" state" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "Entry did not maintain \"extern_valid\" flag after transition to \"reachable\" state" + + log_test "$af_str \"extern_valid\" flag: Transition to \"reachable\" state" + + RET=0 + + # Drop all packets, trigger resolution and check that entry goes back + # to "stale" state instead of "failed". + local mcast_reprobes + local retrans_time + local ucast_probes + local app_probes + local probes + local delay + + run_cmd "ip -n $ns1 neigh flush dev veth0" + run_cmd "tc -n $ns2 qdisc add dev veth1 clsact" + run_cmd "tc -n $ns2 filter add dev veth1 ingress proto all matchall action drop" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid use" + retrans_time=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["retrans"]') + ucast_probes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["ucast_probes"]') + app_probes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["app_probes"]') + mcast_reprobes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["mcast_reprobes"]') + delay=$((delay_probe + (ucast_probes + app_probes + mcast_reprobes) * retrans_time)) + run_cmd "sleep $((delay / 1000 + 2))" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"STALE\"" + check_err $? "Entry did not return to \"stale\" state" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "Entry did not maintain \"extern_valid\" flag after returning to \"stale\" state" + probes=$(ip -n "$ns1" -j -s neigh get "$ip_addr" dev veth0 | jq '.[]["probes"]') + if [[ $probes -eq 0 ]]; then + check_err 1 "No probes were sent" + fi + + log_test "$af_str \"extern_valid\" flag: Transition back to \"stale\" state" + + run_cmd "tc -n $ns2 qdisc del dev veth1 clsact" + + RET=0 + + # Forced garbage collection runs whenever the number of entries is + # larger than "thresh3" and deletes stale entries that have not been + # updated in the last 5 seconds. + # + # Check that an "extern_valid" entry survives a forced garbage + # collection. Add an entry, wait 5 seconds and add more entries than + # "thresh3" so that forced garbage collection will run. + # + # Note that the garbage collection thresholds are global resources and + # that changes in the initial namespace affect all the namespaces. + local forced_gc_runs_t0 + local forced_gc_runs_t1 + local orig_thresh1 + local orig_thresh2 + local orig_thresh3 + + run_cmd "ip -n $ns1 neigh flush dev veth0" + orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]') + orig_thresh2=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh2")) | .["thresh2"]') + orig_thresh3=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh3")) | .["thresh3"]') + run_cmd "ip ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0" + run_cmd "sleep 5" + forced_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]') + for i in {1..20}; do + run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0" + done + forced_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]') + if [[ $forced_gc_runs_t1 -eq $forced_gc_runs_t0 ]]; then + check_err 1 "Forced garbage collection did not run" + fi + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "Entry with \"extern_valid\" flag did not survive forced garbage collection" + run_cmd "ip -n $ns1 neigh get ${subnet}3 dev veth0" + check_fail $? "Entry without \"extern_valid\" flag survived forced garbage collection" + + log_test "$af_str \"extern_valid\" flag: Forced garbage collection" + + run_cmd "ip ntable change name $tbl_name thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1" + + RET=0 + + # Periodic garbage collection runs every "base_reachable"/2 seconds and + # if the number of entries is larger than "thresh1", then it deletes + # stale entries that have not been used in the last "gc_stale" seconds. + # + # Check that an "extern_valid" entry survives a periodic garbage + # collection. Add an "extern_valid" entry, add more than "thresh1" + # regular entries, wait "base_reachable" (longer than "gc_stale") + # seconds and check that the "extern_valid" entry was not deleted. + # + # Note that the garbage collection thresholds and "base_reachable" are + # global resources and that changes in the initial namespace affect all + # the namespaces. + local periodic_gc_runs_t0 + local periodic_gc_runs_t1 + local orig_base_reachable + local orig_gc_stale + + run_cmd "ip -n $ns1 neigh flush dev veth0" + orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]') + orig_base_reachable=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["base_reachable"]') + run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable 10000" + orig_gc_stale=$(ip -n "$ns1" -j ntable show name "$tbl_name" dev veth0 | jq '.[]["gc_stale"]') + run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale 1000" + run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid" + run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0" + # Wait orig_base_reachable/2 for the new interval to take effect. + run_cmd "sleep $(((orig_base_reachable / 1000) / 2 + 2))" + for i in {1..20}; do + run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0" + done + periodic_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]') + run_cmd "sleep 10" + periodic_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]') + [[ $periodic_gc_runs_t1 -ne $periodic_gc_runs_t0 ]] + check_err $? "Periodic garbage collection did not run" + run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\"" + check_err $? "Entry with \"extern_valid\" flag did not survive periodic garbage collection" + run_cmd "ip -n $ns1 neigh get ${subnet}3 dev veth0" + check_fail $? "Entry without \"extern_valid\" flag survived periodic garbage collection" + + log_test "$af_str \"extern_valid\" flag: Periodic garbage collection" + + run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale $orig_gc_stale" + run_cmd "ip ntable change name $tbl_name thresh1 $orig_thresh1 base_reachable $orig_base_reachable" +} + +extern_valid_ipv4() +{ + extern_valid_common "IPv4" 192.0.2.2 "arp_cache" 192.0.2. +} + +extern_valid_ipv6() +{ + extern_valid_common "IPv6" 2001:db8:1::2 "ndisc_cache" 2001:db8:1:: +} + +################################################################################ +# Usage + +usage() +{ + cat < Test(s) to run (default: all) + (options: $TESTS) + -p Pause on fail + -v Verbose mode (show commands and output) +EOF +} + +################################################################################ +# Main + +while getopts ":t:pvh" opt; do + case $opt in + t) TESTS=$OPTARG;; + p) PAUSE_ON_FAIL=yes;; + v) VERBOSE=$((VERBOSE + 1));; + h) usage; exit 0;; + *) usage; exit 1;; + esac +done + +require_command jq + +if ! ip neigh help 2>&1 | grep -q "extern_valid"; then + echo "SKIP: iproute2 ip too old, missing \"extern_valid\" support" + exit "$ksft_skip" +fi + +trap exit_cleanup_all EXIT + +for t in $TESTS +do + setup; $t; cleanup_all_ns; +done diff --git a/tools/testing/selftests/net/test_vxlan_nh.sh b/tools/testing/selftests/net/test_vxlan_nh.sh new file mode 100755 index 000000000000..20f3369f776b --- /dev/null +++ b/tools/testing/selftests/net/test_vxlan_nh.sh @@ -0,0 +1,223 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source lib.sh +TESTS=" + basic_tx_ipv4 + basic_tx_ipv6 + learning + proxy_ipv4 + proxy_ipv6 +" +VERBOSE=0 + +################################################################################ +# Utilities + +run_cmd() +{ + local cmd="$1" + local out + local stderr="2>/dev/null" + + if [ "$VERBOSE" = "1" ]; then + echo "COMMAND: $cmd" + stderr= + fi + + out=$(eval "$cmd" "$stderr") + rc=$? + if [ "$VERBOSE" -eq 1 ] && [ -n "$out" ]; then + echo " $out" + fi + + return $rc +} + +################################################################################ +# Cleanup + +exit_cleanup_all() +{ + cleanup_all_ns + exit "${EXIT_STATUS}" +} + +################################################################################ +# Tests + +nh_stats_get() +{ + ip -n "$ns1" -s -j nexthop show id 10 | jq ".[][\"group_stats\"][][\"packets\"]" +} + +tc_stats_get() +{ + tc_rule_handle_stats_get "dev dummy1 egress" 101 ".packets" "-n $ns1" +} + +basic_tx_common() +{ + local af_str=$1; shift + local proto=$1; shift + local local_addr=$1; shift + local plen=$1; shift + local remote_addr=$1; shift + + RET=0 + + # Test basic Tx functionality. Check that stats are incremented on + # both the FDB nexthop group and the egress device. + + run_cmd "ip -n $ns1 link add name dummy1 up type dummy" + run_cmd "ip -n $ns1 route add $remote_addr/$plen dev dummy1" + run_cmd "tc -n $ns1 qdisc add dev dummy1 clsact" + run_cmd "tc -n $ns1 filter add dev dummy1 egress proto $proto pref 1 handle 101 flower ip_proto udp dst_ip $remote_addr dst_port 4789 action pass" + + run_cmd "ip -n $ns1 address add $local_addr/$plen dev lo" + + run_cmd "ip -n $ns1 nexthop add id 1 via $remote_addr fdb" + run_cmd "ip -n $ns1 nexthop add id 10 group 1 fdb" + + run_cmd "ip -n $ns1 link add name vx0 up type vxlan id 10010 local $local_addr dstport 4789" + run_cmd "bridge -n $ns1 fdb add 00:11:22:33:44:55 dev vx0 self static nhid 10" + + run_cmd "ip netns exec $ns1 mausezahn vx0 -a own -b 00:11:22:33:44:55 -c 1 -q" + + busywait "$BUSYWAIT_TIMEOUT" until_counter_is "== 1" nh_stats_get > /dev/null + check_err $? "FDB nexthop group stats did not increase" + + busywait "$BUSYWAIT_TIMEOUT" until_counter_is "== 1" tc_stats_get > /dev/null + check_err $? "tc filter stats did not increase" + + log_test "VXLAN FDB nexthop: $af_str basic Tx" +} + +basic_tx_ipv4() +{ + basic_tx_common "IPv4" ipv4 192.0.2.1 32 192.0.2.2 +} + +basic_tx_ipv6() +{ + basic_tx_common "IPv6" ipv6 2001:db8:1::1 128 2001:db8:1::2 +} + +learning() +{ + RET=0 + + # When learning is enabled on the VXLAN device, an incoming packet + # might try to refresh an FDB entry that points to an FDB nexthop group + # instead of an ordinary remote destination. Check that the kernel does + # not crash in this situation. + + run_cmd "ip -n $ns1 address add 192.0.2.1/32 dev lo" + run_cmd "ip -n $ns1 address add 192.0.2.2/32 dev lo" + + run_cmd "ip -n $ns1 nexthop add id 1 via 192.0.2.3 fdb" + run_cmd "ip -n $ns1 nexthop add id 10 group 1 fdb" + + run_cmd "ip -n $ns1 link add name vx0 up type vxlan id 10010 local 192.0.2.1 dstport 12345 localbypass" + run_cmd "ip -n $ns1 link add name vx1 up type vxlan id 10020 local 192.0.2.2 dstport 54321 learning" + + run_cmd "bridge -n $ns1 fdb add 00:11:22:33:44:55 dev vx0 self static dst 192.0.2.2 port 54321 vni 10020" + run_cmd "bridge -n $ns1 fdb add 00:aa:bb:cc:dd:ee dev vx1 self static nhid 10" + + run_cmd "ip netns exec $ns1 mausezahn vx0 -a 00:aa:bb:cc:dd:ee -b 00:11:22:33:44:55 -c 1 -q" + + log_test "VXLAN FDB nexthop: learning" +} + +proxy_common() +{ + local af_str=$1; shift + local local_addr=$1; shift + local plen=$1; shift + local remote_addr=$1; shift + local neigh_addr=$1; shift + local ping_cmd=$1; shift + + RET=0 + + # When the "proxy" option is enabled on the VXLAN device, the device + # will suppress ARP requests and IPv6 Neighbor Solicitation messages if + # it is able to reply on behalf of the remote host. That is, if a + # matching and valid neighbor entry is configured on the VXLAN device + # whose MAC address is not behind the "any" remote (0.0.0.0 / ::). The + # FDB entry for the neighbor's MAC address might point to an FDB + # nexthop group instead of an ordinary remote destination. Check that + # the kernel does not crash in this situation. + + run_cmd "ip -n $ns1 address add $local_addr/$plen dev lo" + + run_cmd "ip -n $ns1 nexthop add id 1 via $remote_addr fdb" + run_cmd "ip -n $ns1 nexthop add id 10 group 1 fdb" + + run_cmd "ip -n $ns1 link add name vx0 up type vxlan id 10010 local $local_addr dstport 4789 proxy" + + run_cmd "ip -n $ns1 neigh add $neigh_addr lladdr 00:11:22:33:44:55 nud perm dev vx0" + + run_cmd "bridge -n $ns1 fdb add 00:11:22:33:44:55 dev vx0 self static nhid 10" + + run_cmd "ip netns exec $ns1 $ping_cmd" + + log_test "VXLAN FDB nexthop: $af_str proxy" +} + +proxy_ipv4() +{ + proxy_common "IPv4" 192.0.2.1 32 192.0.2.2 192.0.2.3 \ + "arping -b -c 1 -s 192.0.2.1 -I vx0 192.0.2.3" +} + +proxy_ipv6() +{ + proxy_common "IPv6" 2001:db8:1::1 128 2001:db8:1::2 2001:db8:1::3 \ + "ndisc6 -r 1 -s 2001:db8:1::1 -w 1 2001:db8:1::3 vx0" +} + +################################################################################ +# Usage + +usage() +{ + cat < Test(s) to run (default: all) + (options: $TESTS) + -p Pause on fail + -v Verbose mode (show commands and output) +EOF +} + +################################################################################ +# Main + +while getopts ":t:pvh" opt; do + case $opt in + t) TESTS=$OPTARG;; + p) PAUSE_ON_FAIL=yes;; + v) VERBOSE=$((VERBOSE + 1));; + h) usage; exit 0;; + *) usage; exit 1;; + esac +done + +require_command mausezahn +require_command arping +require_command ndisc6 +require_command jq + +if ! ip nexthop help 2>&1 | grep -q "stats"; then + echo "SKIP: iproute2 ip too old, missing nexthop stats support" + exit "$ksft_skip" +fi + +trap exit_cleanup_all EXIT + +for t in $TESTS +do + setup_ns ns1; $t; cleanup_all_ns; +done diff --git a/tools/testing/selftests/net/test_vxlan_vnifiltering.sh b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh index 6127a78ee988..8deacc565afa 100755 --- a/tools/testing/selftests/net/test_vxlan_vnifiltering.sh +++ b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh @@ -146,18 +146,17 @@ run_cmd() } check_hv_connectivity() { - ip netns exec $hv_1 ping -c 1 -W 1 $1 &>/dev/null - sleep 1 - ip netns exec $hv_1 ping -c 1 -W 1 $2 &>/dev/null + slowwait 5 ip netns exec $hv_1 ping -c 1 -W 1 $1 &>/dev/null + slowwait 5 ip netns exec $hv_1 ping -c 1 -W 1 $2 &>/dev/null return $? } check_vm_connectivity() { - run_cmd "ip netns exec $vm_11 ping -c 1 -W 1 10.0.10.12" + slowwait 5 run_cmd "ip netns exec $vm_11 ping -c 1 -W 1 10.0.10.12" log_test $? 0 "VM connectivity over $1 (ipv4 default rdst)" - run_cmd "ip netns exec $vm_21 ping -c 1 -W 1 10.0.10.22" + slowwait 5 run_cmd "ip netns exec $vm_21 ping -c 1 -W 1 10.0.10.22" log_test $? 0 "VM connectivity over $1 (ipv6 default rdst)" } diff --git a/tools/testing/selftests/net/tfo.c b/tools/testing/selftests/net/tfo.c new file mode 100644 index 000000000000..eb3cac5e583c --- /dev/null +++ b/tools/testing/selftests/net/tfo.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int cfg_server; +static int cfg_client; +static int cfg_port = 8000; +static struct sockaddr_in6 cfg_addr; +static char *cfg_outfile; + +static int parse_address(const char *str, int port, struct sockaddr_in6 *sin6) +{ + int ret; + + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + + ret = inet_pton(sin6->sin6_family, str, &sin6->sin6_addr); + if (ret != 1) { + /* fallback to plain IPv4 */ + ret = inet_pton(AF_INET, str, &sin6->sin6_addr.s6_addr32[3]); + if (ret != 1) + return -1; + + /* add ::ffff prefix */ + sin6->sin6_addr.s6_addr32[0] = 0; + sin6->sin6_addr.s6_addr32[1] = 0; + sin6->sin6_addr.s6_addr16[4] = 0; + sin6->sin6_addr.s6_addr16[5] = 0xffff; + } + + return 0; +} + +static void run_server(void) +{ + unsigned long qlen = 32; + int fd, opt, connfd; + socklen_t len; + char buf[64]; + FILE *outfile; + + outfile = fopen(cfg_outfile, "w"); + if (!outfile) + error(1, errno, "fopen() outfile"); + + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fd == -1) + error(1, errno, "socket()"); + + opt = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) + error(1, errno, "setsockopt(SO_REUSEADDR)"); + + if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) < 0) + error(1, errno, "setsockopt(TCP_FASTOPEN)"); + + if (bind(fd, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr)) < 0) + error(1, errno, "bind()"); + + if (listen(fd, 5) < 0) + error(1, errno, "listen()"); + + len = sizeof(cfg_addr); + connfd = accept(fd, (struct sockaddr *)&cfg_addr, &len); + if (connfd < 0) + error(1, errno, "accept()"); + + len = sizeof(opt); + if (getsockopt(connfd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &opt, &len) < 0) + error(1, errno, "getsockopt(SO_INCOMING_NAPI_ID)"); + + read(connfd, buf, 64); + fprintf(outfile, "%d\n", opt); + + fclose(outfile); + close(connfd); + close(fd); +} + +static void run_client(void) +{ + int fd; + char *msg = "Hello, world!"; + + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fd == -1) + error(1, errno, "socket()"); + + sendto(fd, msg, strlen(msg), MSG_FASTOPEN, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr)); + + close(fd); +} + +static void usage(const char *filepath) +{ + error(1, 0, "Usage: %s (-s|-c) -h -p -o ", filepath); +} + +static void parse_opts(int argc, char **argv) +{ + struct sockaddr_in6 *addr6 = (void *) &cfg_addr; + char *addr = NULL; + int ret; + int c; + + if (argc <= 1) + usage(argv[0]); + + while ((c = getopt(argc, argv, "sch:p:o:")) != -1) { + switch (c) { + case 's': + if (cfg_client) + error(1, 0, "Pass one of -s or -c"); + cfg_server = 1; + break; + case 'c': + if (cfg_server) + error(1, 0, "Pass one of -s or -c"); + cfg_client = 1; + break; + case 'h': + addr = optarg; + break; + case 'p': + cfg_port = strtoul(optarg, NULL, 0); + break; + case 'o': + cfg_outfile = strdup(optarg); + if (!cfg_outfile) + error(1, 0, "outfile invalid"); + break; + } + } + + if (cfg_server && addr) + error(1, 0, "Server cannot have -h specified"); + + memset(addr6, 0, sizeof(*addr6)); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(cfg_port); + addr6->sin6_addr = in6addr_any; + if (addr) { + ret = parse_address(addr, cfg_port, addr6); + if (ret) + error(1, 0, "Client address parse error: %s", addr); + } +} + +int main(int argc, char **argv) +{ + parse_opts(argc, argv); + + if (cfg_server) + run_server(); + else if (cfg_client) + run_client(); + + return 0; +} diff --git a/tools/testing/selftests/net/tfo_passive.sh b/tools/testing/selftests/net/tfo_passive.sh new file mode 100755 index 000000000000..80bf11fdc046 --- /dev/null +++ b/tools/testing/selftests/net/tfo_passive.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +source lib.sh + +NSIM_SV_ID=$((256 + RANDOM % 256)) +NSIM_SV_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_SV_ID +NSIM_CL_ID=$((512 + RANDOM % 256)) +NSIM_CL_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_CL_ID + +NSIM_DEV_SYS_NEW=/sys/bus/netdevsim/new_device +NSIM_DEV_SYS_DEL=/sys/bus/netdevsim/del_device +NSIM_DEV_SYS_LINK=/sys/bus/netdevsim/link_device +NSIM_DEV_SYS_UNLINK=/sys/bus/netdevsim/unlink_device + +SERVER_IP=192.168.1.1 +CLIENT_IP=192.168.1.2 +SERVER_PORT=48675 + +setup_ns() +{ + set -e + ip netns add nssv + ip netns add nscl + + NSIM_SV_NAME=$(find $NSIM_SV_SYS/net -maxdepth 1 -type d ! \ + -path $NSIM_SV_SYS/net -exec basename {} \;) + NSIM_CL_NAME=$(find $NSIM_CL_SYS/net -maxdepth 1 -type d ! \ + -path $NSIM_CL_SYS/net -exec basename {} \;) + + ip link set $NSIM_SV_NAME netns nssv + ip link set $NSIM_CL_NAME netns nscl + + ip netns exec nssv ip addr add "${SERVER_IP}/24" dev $NSIM_SV_NAME + ip netns exec nscl ip addr add "${CLIENT_IP}/24" dev $NSIM_CL_NAME + + ip netns exec nssv ip link set dev $NSIM_SV_NAME up + ip netns exec nscl ip link set dev $NSIM_CL_NAME up + + # Enable passive TFO + ip netns exec nssv sysctl -w net.ipv4.tcp_fastopen=519 > /dev/null + + set +e +} + +cleanup_ns() +{ + ip netns del nscl + ip netns del nssv +} + +### +### Code start +### + +modprobe netdevsim + +# linking + +echo $NSIM_SV_ID > $NSIM_DEV_SYS_NEW +echo $NSIM_CL_ID > $NSIM_DEV_SYS_NEW +udevadm settle + +setup_ns + +NSIM_SV_FD=$((256 + RANDOM % 256)) +exec {NSIM_SV_FD} \ + $NSIM_DEV_SYS_LINK + +if [ $? -ne 0 ]; then + echo "linking netdevsim1 with netdevsim2 should succeed" + cleanup_ns + exit 1 +fi + +out_file=$(mktemp) + +timeout -k 1s 30s ip netns exec nssv ./tfo \ + -s \ + -p ${SERVER_PORT} \ + -o ${out_file}& + +wait_local_port_listen nssv ${SERVER_PORT} tcp + +ip netns exec nscl ./tfo -c -h ${SERVER_IP} -p ${SERVER_PORT} + +wait + +res=$(cat $out_file) +rm $out_file + +if [ $res -eq 0 ]; then + echo "got invalid NAPI ID from passive TFO socket" + cleanup_ns + exit 1 +fi + +echo "$NSIM_SV_FD:$NSIM_SV_IFIDX" > $NSIM_DEV_SYS_UNLINK + +echo $NSIM_CL_ID > $NSIM_DEV_SYS_DEL + +cleanup_ns + +modprobe -r netdevsim + +exit 0 diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 5ded3b3a7538..0f5640d8dc7f 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -181,13 +181,12 @@ static int tls_send_cmsg(int fd, unsigned char record_type, return sendmsg(fd, &msg, flags); } -static int tls_recv_cmsg(struct __test_metadata *_metadata, - int fd, unsigned char record_type, - void *data, size_t len, int flags) +static int __tls_recv_cmsg(struct __test_metadata *_metadata, + int fd, unsigned char *ctype, + void *data, size_t len, int flags) { char cbuf[CMSG_SPACE(sizeof(char))]; struct cmsghdr *cmsg; - unsigned char ctype; struct msghdr msg; struct iovec vec; int n; @@ -206,7 +205,20 @@ static int tls_recv_cmsg(struct __test_metadata *_metadata, EXPECT_NE(cmsg, NULL); EXPECT_EQ(cmsg->cmsg_level, SOL_TLS); EXPECT_EQ(cmsg->cmsg_type, TLS_GET_RECORD_TYPE); - ctype = *((unsigned char *)CMSG_DATA(cmsg)); + if (ctype) + *ctype = *((unsigned char *)CMSG_DATA(cmsg)); + + return n; +} + +static int tls_recv_cmsg(struct __test_metadata *_metadata, + int fd, unsigned char record_type, + void *data, size_t len, int flags) +{ + unsigned char ctype; + int n; + + n = __tls_recv_cmsg(_metadata, fd, &ctype, data, len, flags); EXPECT_EQ(ctype, record_type); return n; @@ -2164,6 +2176,284 @@ TEST_F(tls, rekey_poll_delay) } } +struct raw_rec { + unsigned int plain_len; + unsigned char plain_data[100]; + unsigned int cipher_len; + unsigned char cipher_data[128]; +}; + +/* TLS 1.2, AES_CCM, data, seqno:0, plaintext: 'Hello world' */ +static const struct raw_rec id0_data_l11 = { + .plain_len = 11, + .plain_data = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, + }, + .cipher_len = 40, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0xa2, 0x33, + 0xde, 0x8d, 0x94, 0xf0, 0x29, 0x6c, 0xb1, 0xaf, + 0x6a, 0x75, 0xb2, 0x93, 0xad, 0x45, 0xd5, 0xfd, + 0x03, 0x51, 0x57, 0x8f, 0xf9, 0xcc, 0x3b, 0x42, + }, +}; + +/* TLS 1.2, AES_CCM, ctrl, seqno:0, plaintext: '' */ +static const struct raw_rec id0_ctrl_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x38, 0x7b, + 0xa6, 0x1c, 0xdd, 0xa7, 0x19, 0x33, 0xab, 0xae, + 0x88, 0xe1, 0xd2, 0x08, 0x4f, + }, +}; + +/* TLS 1.2, AES_CCM, data, seqno:0, plaintext: '' */ +static const struct raw_rec id0_data_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x37, 0x90, + 0x70, 0x45, 0x89, 0xfb, 0x5c, 0xc7, 0x89, 0x03, + 0x68, 0x80, 0xd3, 0xd8, 0xcc, + }, +}; + +/* TLS 1.2, AES_CCM, data, seqno:1, plaintext: 'Hello world' */ +static const struct raw_rec id1_data_l11 = { + .plain_len = 11, + .plain_data = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, + }, + .cipher_len = 40, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x1a, 0x9c, + 0xd0, 0xa8, 0x9a, 0xd6, 0x69, 0xd6, 0x1a, 0xe3, + 0xb5, 0x1f, 0x0d, 0x2c, 0xe2, 0x97, 0x46, 0xff, + 0x2b, 0xcc, 0x5a, 0xc4, 0xa3, 0xb9, 0xef, 0xba, + }, +}; + +/* TLS 1.2, AES_CCM, ctrl, seqno:1, plaintext: '' */ +static const struct raw_rec id1_ctrl_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0xf0, 0xfe, + 0xee, 0xd9, 0xe2, 0x5d, 0xc7, 0x11, 0x4c, 0xe6, + 0xb4, 0x7e, 0xef, 0x40, 0x2b, + }, +}; + +/* TLS 1.2, AES_CCM, data, seqno:1, plaintext: '' */ +static const struct raw_rec id1_data_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0xfc, 0x86, + 0xc8, 0xf0, 0x55, 0xf9, 0x47, 0x3f, 0x74, 0xdc, + 0xc9, 0xbf, 0xfe, 0x5b, 0xb1, + }, +}; + +/* TLS 1.2, AES_CCM, ctrl, seqno:2, plaintext: 'Hello world' */ +static const struct raw_rec id2_ctrl_l11 = { + .plain_len = 11, + .plain_data = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, + }, + .cipher_len = 40, + .cipher_data = { + 0x16, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xe5, 0x3d, 0x19, + 0x3d, 0xca, 0xb8, 0x16, 0xb6, 0xff, 0x79, 0x87, + 0x2a, 0x04, 0x11, 0x3d, 0xf8, 0x64, 0x5f, 0x36, + 0x8b, 0xa8, 0xee, 0x4c, 0x6d, 0x62, 0xa5, 0x00, + }, +}; + +/* TLS 1.2, AES_CCM, data, seqno:2, plaintext: 'Hello world' */ +static const struct raw_rec id2_data_l11 = { + .plain_len = 11, + .plain_data = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, + }, + .cipher_len = 40, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xe5, 0x3d, 0x19, + 0x3d, 0xca, 0xb8, 0x16, 0xb6, 0xff, 0x79, 0x87, + 0x8e, 0xa1, 0xd0, 0xcd, 0x33, 0xb5, 0x86, 0x2b, + 0x17, 0xf1, 0x52, 0x2a, 0x55, 0x62, 0x65, 0x11, + }, +}; + +/* TLS 1.2, AES_CCM, ctrl, seqno:2, plaintext: '' */ +static const struct raw_rec id2_ctrl_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x16, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xdc, 0x5c, 0x0e, + 0x41, 0xdd, 0xba, 0xd3, 0xcc, 0xcf, 0x6d, 0xd9, + 0x06, 0xdb, 0x79, 0xe5, 0x5d, + }, +}; + +/* TLS 1.2, AES_CCM, data, seqno:2, plaintext: '' */ +static const struct raw_rec id2_data_l0 = { + .plain_len = 0, + .plain_data = { + }, + .cipher_len = 29, + .cipher_data = { + 0x17, 0x03, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xc3, 0xca, 0x26, + 0x22, 0xe4, 0x25, 0xfb, 0x5f, 0x6d, 0xbf, 0x83, + 0x30, 0x48, 0x69, 0x1a, 0x47, + }, +}; + +FIXTURE(zero_len) +{ + int fd, cfd; + bool notls; +}; + +FIXTURE_VARIANT(zero_len) +{ + const struct raw_rec *recs[4]; + ssize_t recv_ret[4]; +}; + +FIXTURE_VARIANT_ADD(zero_len, data_data_data) +{ + .recs = { &id0_data_l11, &id1_data_l11, &id2_data_l11, }, + .recv_ret = { 33, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, data_0ctrl_data) +{ + .recs = { &id0_data_l11, &id1_ctrl_l0, &id2_data_l11, }, + .recv_ret = { 11, 0, 11, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, 0data_0data_0data) +{ + .recs = { &id0_data_l0, &id1_data_l0, &id2_data_l0, }, + .recv_ret = { -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, 0data_0data_ctrl) +{ + .recs = { &id0_data_l0, &id1_data_l0, &id2_ctrl_l11, }, + .recv_ret = { 0, 11, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, 0data_0data_0ctrl) +{ + .recs = { &id0_data_l0, &id1_data_l0, &id2_ctrl_l0, }, + .recv_ret = { 0, 0, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, 0ctrl_0ctrl_0ctrl) +{ + .recs = { &id0_ctrl_l0, &id1_ctrl_l0, &id2_ctrl_l0, }, + .recv_ret = { 0, 0, 0, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, 0data_0data_data) +{ + .recs = { &id0_data_l0, &id1_data_l0, &id2_data_l11, }, + .recv_ret = { 11, -EAGAIN, }, +}; + +FIXTURE_VARIANT_ADD(zero_len, data_0data_0data) +{ + .recs = { &id0_data_l11, &id1_data_l0, &id2_data_l0, }, + .recv_ret = { 11, -EAGAIN, }, +}; + +FIXTURE_SETUP(zero_len) +{ + struct tls_crypto_info_keys tls12; + int ret; + + tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128, + &tls12, 0); + + ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls); + if (self->notls) + return; + + /* Don't install keys on fd, we'll send raw records */ + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len); + ASSERT_EQ(ret, 0); +} + +FIXTURE_TEARDOWN(zero_len) +{ + close(self->fd); + close(self->cfd); +} + +TEST_F(zero_len, test) +{ + const struct raw_rec *const *rec; + unsigned char buf[128]; + int rec_off; + int i; + + for (i = 0; i < 4 && variant->recs[i]; i++) + EXPECT_EQ(send(self->fd, variant->recs[i]->cipher_data, + variant->recs[i]->cipher_len, 0), + variant->recs[i]->cipher_len); + + rec = &variant->recs[0]; + rec_off = 0; + for (i = 0; i < 4; i++) { + int j, ret; + + ret = variant->recv_ret[i] >= 0 ? variant->recv_ret[i] : -1; + EXPECT_EQ(__tls_recv_cmsg(_metadata, self->cfd, NULL, + buf, sizeof(buf), MSG_DONTWAIT), ret); + if (ret == -1) + EXPECT_EQ(errno, -variant->recv_ret[i]); + if (variant->recv_ret[i] == -EAGAIN) + break; + + for (j = 0; j < ret; j++) { + while (rec_off == (*rec)->plain_len) { + rec++; + rec_off = 0; + } + EXPECT_EQ(buf[j], (*rec)->plain_data[rec_off]); + rec_off++; + } + } +}; + FIXTURE(tls_err) { int fd, cfd; @@ -2708,6 +2998,67 @@ TEST(prequeue) { close(cfd); } +TEST(data_steal) { + struct tls_crypto_info_keys tls; + char buf[20000], buf2[20000]; + struct sockaddr_in addr; + int sfd, cfd, ret, fd; + int pid, status; + socklen_t len; + + len = sizeof(addr); + memrnd(buf, sizeof(buf)); + + tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls, 0); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + + fd = socket(AF_INET, SOCK_STREAM, 0); + sfd = socket(AF_INET, SOCK_STREAM, 0); + + ASSERT_EQ(bind(sfd, &addr, sizeof(addr)), 0); + ASSERT_EQ(listen(sfd, 10), 0); + ASSERT_EQ(getsockname(sfd, &addr, &len), 0); + ASSERT_EQ(connect(fd, &addr, sizeof(addr)), 0); + ASSERT_GE(cfd = accept(sfd, &addr, &len), 0); + close(sfd); + + ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")); + if (ret) { + ASSERT_EQ(errno, ENOENT); + SKIP(return, "no TLS support"); + } + ASSERT_EQ(setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")), 0); + + /* Spawn a child and get it into the read wait path of the underlying + * TCP socket. + */ + pid = fork(); + ASSERT_GE(pid, 0); + if (!pid) { + EXPECT_EQ(recv(cfd, buf, sizeof(buf) / 2, MSG_WAITALL), + sizeof(buf) / 2); + exit(!__test_passed(_metadata)); + } + + usleep(10000); + ASSERT_EQ(setsockopt(fd, SOL_TLS, TLS_TX, &tls, tls.len), 0); + ASSERT_EQ(setsockopt(cfd, SOL_TLS, TLS_RX, &tls, tls.len), 0); + + EXPECT_EQ(send(fd, buf, sizeof(buf), 0), sizeof(buf)); + EXPECT_EQ(wait(&status), pid); + EXPECT_EQ(status, 0); + EXPECT_EQ(recv(cfd, buf2, sizeof(buf2), MSG_DONTWAIT), -1); + /* Don't check errno, the error will be different depending + * on what random bytes TLS interpreted as the record length. + */ + + close(fd); + close(cfd); +} + static void __attribute__((constructor)) fips_check(void) { int res; FILE *f; diff --git a/tools/testing/selftests/net/udpgro.sh b/tools/testing/selftests/net/udpgro.sh index 1dc337c709f8..b17e032a6d75 100755 --- a/tools/testing/selftests/net/udpgro.sh +++ b/tools/testing/selftests/net/udpgro.sh @@ -48,7 +48,7 @@ run_one() { cfg_veth - ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${rx_args} & + ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 100 ${rx_args} & local PID1=$! wait_local_port_listen ${PEER_NS} 8000 udp @@ -95,7 +95,7 @@ run_one_nat() { # will land on the 'plain' one ip netns exec "${PEER_NS}" ./udpgso_bench_rx -G ${family} -b ${addr1} -n 0 & local PID1=$! - ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${family} -b ${addr2%/*} ${rx_args} & + ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 100 ${family} -b ${addr2%/*} ${rx_args} & local PID2=$! wait_local_port_listen "${PEER_NS}" 8000 udp @@ -117,9 +117,9 @@ run_one_2sock() { cfg_veth - ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${rx_args} -p 12345 & + ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 100 ${rx_args} -p 12345 & local PID1=$! - ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 2000 -R 10 ${rx_args} & + ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 2000 -R 100 ${rx_args} & local PID2=$! wait_local_port_listen "${PEER_NS}" 12345 udp diff --git a/tools/testing/selftests/net/vlan_hw_filter.sh b/tools/testing/selftests/net/vlan_hw_filter.sh index 7bc804ffaf7c..e195d5cab6f7 100755 --- a/tools/testing/selftests/net/vlan_hw_filter.sh +++ b/tools/testing/selftests/net/vlan_hw_filter.sh @@ -3,27 +3,101 @@ readonly NETNS="ns-$(mktemp -u XXXXXX)" +ALL_TESTS=" + test_vlan_filter_check + test_vlan0_del_crash_01 + test_vlan0_del_crash_02 + test_vlan0_del_crash_03 + test_vid0_memleak +" + ret=0 +setup() { + ip netns add ${NETNS} +} + cleanup() { - ip netns del $NETNS + ip netns del $NETNS 2>/dev/null } trap cleanup EXIT fail() { - echo "ERROR: ${1:-unexpected return code} (ret: $_)" >&2 - ret=1 + echo "ERROR: ${1:-unexpected return code} (ret: $_)" >&2 + ret=1 } -ip netns add ${NETNS} -ip netns exec ${NETNS} ip link add bond0 type bond mode 0 -ip netns exec ${NETNS} ip link add bond_slave_1 type veth peer veth2 -ip netns exec ${NETNS} ip link set bond_slave_1 master bond0 -ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off -ip netns exec ${NETNS} ip link add link bond_slave_1 name bond_slave_1.0 type vlan id 0 -ip netns exec ${NETNS} ip link add link bond0 name bond0.0 type vlan id 0 -ip netns exec ${NETNS} ip link set bond_slave_1 nomaster -ip netns exec ${NETNS} ip link del veth2 || fail "Please check vlan HW filter function" +tests_run() +{ + local current_test + for current_test in ${TESTS:-$ALL_TESTS}; do + $current_test + done +} +test_vlan_filter_check() { + setup + ip netns exec ${NETNS} ip link add bond0 type bond mode 0 + ip netns exec ${NETNS} ip link add bond_slave_1 type veth peer veth2 + ip netns exec ${NETNS} ip link set bond_slave_1 master bond0 + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off + ip netns exec ${NETNS} ip link add link bond_slave_1 name bond_slave_1.0 type vlan id 0 + ip netns exec ${NETNS} ip link add link bond0 name bond0.0 type vlan id 0 + ip netns exec ${NETNS} ip link set bond_slave_1 nomaster + ip netns exec ${NETNS} ip link del veth2 || fail "Please check vlan HW filter function" + cleanup +} + +#enable vlan_filter feature of real_dev with vlan0 during running time +test_vlan0_del_crash_01() { + setup + ip netns exec ${NETNS} ip link add bond0 type bond mode 0 + ip netns exec ${NETNS} ip link add link bond0 name vlan0 type vlan id 0 protocol 802.1q + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off + ip netns exec ${NETNS} ip link set dev bond0 up + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter on + ip netns exec ${NETNS} ip link set dev bond0 down + ip netns exec ${NETNS} ip link set dev bond0 up + ip netns exec ${NETNS} ip link del vlan0 || fail "Please check vlan HW filter function" + cleanup +} + +#enable vlan_filter feature and add vlan0 for real_dev during running time +test_vlan0_del_crash_02() { + setup + ip netns exec ${NETNS} ip link add bond0 type bond mode 0 + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off + ip netns exec ${NETNS} ip link set dev bond0 up + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter on + ip netns exec ${NETNS} ip link add link bond0 name vlan0 type vlan id 0 protocol 802.1q + ip netns exec ${NETNS} ip link set dev bond0 down + ip netns exec ${NETNS} ip link set dev bond0 up + ip netns exec ${NETNS} ip link del vlan0 || fail "Please check vlan HW filter function" + cleanup +} + +#enable vlan_filter feature of real_dev during running time +#test kernel_bug of vlan unregister +test_vlan0_del_crash_03() { + setup + ip netns exec ${NETNS} ip link add bond0 type bond mode 0 + ip netns exec ${NETNS} ip link add link bond0 name vlan0 type vlan id 0 protocol 802.1q + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off + ip netns exec ${NETNS} ip link set dev bond0 up + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter on + ip netns exec ${NETNS} ip link set dev bond0 down + ip netns exec ${NETNS} ip link del vlan0 || fail "Please check vlan HW filter function" + cleanup +} + +test_vid0_memleak() { + setup + ip netns exec ${NETNS} ip link add bond0 up type bond mode 0 + ip netns exec ${NETNS} ethtool -K bond0 rx-vlan-filter off + ip netns exec ${NETNS} ip link del dev bond0 || fail "Please check vlan HW filter function" + cleanup +} + +tests_run exit $ret diff --git a/tools/testing/selftests/net/vrf_route_leaking.sh b/tools/testing/selftests/net/vrf_route_leaking.sh index e9c2f71da207..ce34cb2e6e0b 100755 --- a/tools/testing/selftests/net/vrf_route_leaking.sh +++ b/tools/testing/selftests/net/vrf_route_leaking.sh @@ -275,7 +275,7 @@ setup_sym() # Wait for ip config to settle - sleep 2 + slowwait 5 ip netns exec $h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1 } setup_asym() @@ -370,7 +370,7 @@ setup_asym() ip -netns $r2 -6 addr add dev eth1 ${R2_N2_IP6}/64 nodad # Wait for ip config to settle - sleep 2 + slowwait 5 ip netns exec $h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1 } check_connectivity() diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index 94176ffe4646..40f5c2908dda 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -1,341 +1,26 @@ # SPDX-License-Identifier: GPL-2.0 -# Makefile for nolibc tests -# we're in ".../tools/testing/selftests/nolibc" -ifeq ($(srctree),) -srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR))) + +TEST_GEN_PROGS := nolibc-test + +include ../lib.mk +include $(top_srcdir)/scripts/Makefile.compiler + +cc-option = $(call __cc-option, $(CC),,$(1),$(2)) + +include Makefile.include + +CFLAGS = -nostdlib -nostdinc -static \ + -isystem $(top_srcdir)/tools/include/nolibc -isystem $(top_srcdir)/usr/include \ + $(CFLAGS_NOLIBC_TEST) + +ifeq ($(LLVM),) +LDLIBS := -lgcc endif -include $(srctree)/tools/scripts/utilities.mak -# We need this for the "__cc-option" macro. -include $(srctree)/scripts/Makefile.compiler - -ifneq ($(O),) -ifneq ($(call is-absolute,$(O)),y) -$(error Only absolute O= parameters are supported) -endif -objtree := $(O) -else -objtree ?= $(srctree) -endif - -ifeq ($(ARCH),) -include $(srctree)/scripts/subarch.include -ARCH = $(SUBARCH) -endif - -cc-option = $(call __cc-option, $(CC),$(CLANG_CROSS_FLAGS),$(1),$(2)) - -# XARCH extends the kernel's ARCH with a few variants of the same -# architecture that only differ by the configuration, the toolchain -# and the Qemu program used. It is copied as-is into ARCH except for -# a few specific values which are mapped like this: -# -# XARCH | ARCH | config -# -------------|-----------|------------------------- -# ppc | powerpc | 32 bits -# ppc64 | powerpc | 64 bits big endian -# ppc64le | powerpc | 64 bits little endian -# -# It is recommended to only use XARCH, though it does not harm if -# ARCH is already set. For simplicity, ARCH is sufficient for all -# architectures where both are equal. - -# configure default variants for target kernel supported architectures -XARCH_powerpc = ppc -XARCH_mips = mips32le -XARCH_riscv = riscv64 -XARCH = $(or $(XARCH_$(ARCH)),$(ARCH)) - -# map from user input variants to their kernel supported architectures -ARCH_armthumb = arm -ARCH_ppc = powerpc -ARCH_ppc64 = powerpc -ARCH_ppc64le = powerpc -ARCH_mips32le = mips -ARCH_mips32be = mips -ARCH_riscv32 = riscv -ARCH_riscv64 = riscv -ARCH_s390x = s390 -ARCH_sparc32 = sparc -ARCH_sparc64 = sparc -ARCH := $(or $(ARCH_$(XARCH)),$(XARCH)) - -# kernel image names by architecture -IMAGE_i386 = arch/x86/boot/bzImage -IMAGE_x86_64 = arch/x86/boot/bzImage -IMAGE_x86 = arch/x86/boot/bzImage -IMAGE_arm64 = arch/arm64/boot/Image -IMAGE_arm = arch/arm/boot/zImage -IMAGE_armthumb = arch/arm/boot/zImage -IMAGE_mips32le = vmlinuz -IMAGE_mips32be = vmlinuz -IMAGE_ppc = vmlinux -IMAGE_ppc64 = vmlinux -IMAGE_ppc64le = arch/powerpc/boot/zImage -IMAGE_riscv = arch/riscv/boot/Image -IMAGE_riscv32 = arch/riscv/boot/Image -IMAGE_riscv64 = arch/riscv/boot/Image -IMAGE_s390x = arch/s390/boot/bzImage -IMAGE_s390 = arch/s390/boot/bzImage -IMAGE_loongarch = arch/loongarch/boot/vmlinuz.efi -IMAGE_sparc32 = arch/sparc/boot/image -IMAGE_sparc64 = arch/sparc/boot/image -IMAGE_m68k = vmlinux -IMAGE = $(objtree)/$(IMAGE_$(XARCH)) -IMAGE_NAME = $(notdir $(IMAGE)) - -# default kernel configurations that appear to be usable -DEFCONFIG_i386 = defconfig -DEFCONFIG_x86_64 = defconfig -DEFCONFIG_x86 = defconfig -DEFCONFIG_arm64 = defconfig -DEFCONFIG_arm = multi_v7_defconfig -DEFCONFIG_armthumb = multi_v7_defconfig -DEFCONFIG_mips32le = malta_defconfig -DEFCONFIG_mips32be = malta_defconfig generic/eb.config -DEFCONFIG_ppc = pmac32_defconfig -DEFCONFIG_ppc64 = powernv_be_defconfig -DEFCONFIG_ppc64le = powernv_defconfig -DEFCONFIG_riscv = defconfig -DEFCONFIG_riscv32 = rv32_defconfig -DEFCONFIG_riscv64 = defconfig -DEFCONFIG_s390x = defconfig -DEFCONFIG_s390 = defconfig compat.config -DEFCONFIG_loongarch = defconfig -DEFCONFIG_sparc32 = sparc32_defconfig -DEFCONFIG_sparc64 = sparc64_defconfig -DEFCONFIG_m68k = virt_defconfig -DEFCONFIG = $(DEFCONFIG_$(XARCH)) - -EXTRACONFIG_m68k = -e CONFIG_BLK_DEV_INITRD -EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) -EXTRACONFIG_arm = -e CONFIG_NAMESPACES -EXTRACONFIG_armthumb = -e CONFIG_NAMESPACES - -# optional tests to run (default = all) -TEST = - -# QEMU_ARCH: arch names used by qemu -QEMU_ARCH_i386 = i386 -QEMU_ARCH_x86_64 = x86_64 -QEMU_ARCH_x86 = x86_64 -QEMU_ARCH_arm64 = aarch64 -QEMU_ARCH_arm = arm -QEMU_ARCH_armthumb = arm -QEMU_ARCH_mips32le = mipsel # works with malta_defconfig -QEMU_ARCH_mips32be = mips -QEMU_ARCH_ppc = ppc -QEMU_ARCH_ppc64 = ppc64 -QEMU_ARCH_ppc64le = ppc64 -QEMU_ARCH_riscv = riscv64 -QEMU_ARCH_riscv32 = riscv32 -QEMU_ARCH_riscv64 = riscv64 -QEMU_ARCH_s390x = s390x -QEMU_ARCH_s390 = s390x -QEMU_ARCH_loongarch = loongarch64 -QEMU_ARCH_sparc32 = sparc -QEMU_ARCH_sparc64 = sparc64 -QEMU_ARCH_m68k = m68k -QEMU_ARCH = $(QEMU_ARCH_$(XARCH)) - -QEMU_ARCH_USER_ppc64le = ppc64le -QEMU_ARCH_USER = $(or $(QEMU_ARCH_USER_$(XARCH)),$(QEMU_ARCH_$(XARCH))) - -QEMU_BIOS_DIR = /usr/share/edk2/ -QEMU_BIOS_loongarch = $(QEMU_BIOS_DIR)/loongarch64/OVMF_CODE.fd - -ifneq ($(QEMU_BIOS_$(XARCH)),) -QEMU_ARGS_BIOS = -bios $(QEMU_BIOS_$(XARCH)) -endif - -# QEMU_ARGS : some arch-specific args to pass to qemu -QEMU_ARGS_i386 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_arm64 = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_arm = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_armthumb = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_mips32le = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_mips32be = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_ppc = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_ppc64 = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_riscv32 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_riscv64 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_s390x = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_s390 = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_loongarch = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_sparc32 = -M SS-5 -m 256M -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_sparc64 = -M sun4u -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_m68k = -M virt -append "console=ttyGF0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS = -m 1G $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA) - -# OUTPUT is only set when run from the main makefile, otherwise -# it defaults to this nolibc directory. -OUTPUT ?= $(CURDIR)/ - -ifeq ($(V),1) -Q= -else -Q=@ -endif - -CFLAGS_i386 = $(call cc-option,-m32) -CFLAGS_arm = -marm -CFLAGS_armthumb = -mthumb -march=armv6t2 -CFLAGS_ppc = -m32 -mbig-endian -mno-vsx $(call cc-option,-mmultiple) -CFLAGS_ppc64 = -m64 -mbig-endian -mno-vsx $(call cc-option,-mmultiple) -CFLAGS_ppc64le = -m64 -mlittle-endian -mno-vsx $(call cc-option,-mabi=elfv2) -CFLAGS_s390x = -m64 -CFLAGS_s390 = -m31 -CFLAGS_mips32le = -EL -mabi=32 -fPIC -CFLAGS_mips32be = -EB -mabi=32 -CFLAGS_sparc32 = $(call cc-option,-m32) -ifeq ($(origin XARCH),command line) -CFLAGS_XARCH = $(CFLAGS_$(XARCH)) -endif -CFLAGS_STACKPROTECTOR ?= $(call cc-option,-mstack-protector-guard=global $(call cc-option,-fstack-protector-all)) -CFLAGS_SANITIZER ?= $(call cc-option,-fsanitize=undefined -fsanitize-trap=all) -CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra \ - $(call cc-option,-fno-stack-protector) $(call cc-option,-Wmissing-prototypes) \ - $(CFLAGS_XARCH) $(CFLAGS_STACKPROTECTOR) $(CFLAGS_SANITIZER) $(CFLAGS_EXTRA) -LDFLAGS := - -LIBGCC := -lgcc - -ifneq ($(LLVM),) -# Not needed for clang -LIBGCC := -endif - -# Modify CFLAGS based on LLVM= -include $(srctree)/tools/scripts/Makefile.include - -# GCC uses "s390", clang "systemz" -CLANG_CROSS_FLAGS := $(subst --target=s390-linux,--target=systemz-linux,$(CLANG_CROSS_FLAGS)) - -REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ - END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ - if (f || !p) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ - printf("\nSee all results in %s\n", ARGV[1]); }' +$(OUTPUT)/nolibc-test: nolibc-test.c nolibc-test-linkage.c | headers help: - @echo "Supported targets under selftests/nolibc:" - @echo " all call the \"run\" target below" - @echo " help this help" - @echo " sysroot create the nolibc sysroot here (uses \$$ARCH)" - @echo " nolibc-test build the executable (uses \$$CC and \$$CROSS_COMPILE)" - @echo " libc-test build an executable using the compiler's default libc instead" - @echo " run-user runs the executable under QEMU (uses \$$XARCH, \$$TEST)" - @echo " initramfs.cpio prepare the initramfs archive with nolibc-test" - @echo " initramfs prepare the initramfs tree with nolibc-test" - @echo " defconfig create a fresh new default config (uses \$$XARCH)" - @echo " kernel (re)build the kernel (uses \$$XARCH)" - @echo " kernel-standalone (re)build the kernel with the initramfs (uses \$$XARCH)" - @echo " run runs the kernel in QEMU after building it (uses \$$XARCH, \$$TEST)" - @echo " rerun runs a previously prebuilt kernel in QEMU (uses \$$XARCH, \$$TEST)" - @echo " clean clean the sysroot, initramfs, build and output files" - @echo "" - @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST." - @echo "" - @echo "Currently using the following variables:" - @echo " ARCH = $(ARCH)" - @echo " XARCH = $(XARCH)" - @echo " CROSS_COMPILE = $(CROSS_COMPILE)" - @echo " CC = $(CC)" - @echo " OUTPUT = $(OUTPUT)" - @echo " TEST = $(TEST)" - @echo " QEMU_ARCH = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$XARCH]" - @echo " IMAGE_NAME = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$XARCH]" - @echo "" + @echo "For the custom nolibc testsuite use '$(MAKE) -f Makefile.nolibc'; available targets:" + @$(MAKE) -f Makefile.nolibc help -all: run - -sysroot: sysroot/$(ARCH)/include - -sysroot/$(ARCH)/include: - $(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot - $(QUIET_MKDIR)mkdir -p sysroot - $(Q)$(MAKE) -C $(srctree) outputmakefile - $(Q)$(MAKE) -C $(srctree)/tools/include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone headers_check - $(Q)mv sysroot/sysroot sysroot/$(ARCH) - -ifneq ($(NOLIBC_SYSROOT),0) -nolibc-test: nolibc-test.c nolibc-test-linkage.c sysroot/$(ARCH)/include - $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ - -nostdlib -nostdinc -static -Isysroot/$(ARCH)/include nolibc-test.c nolibc-test-linkage.c $(LIBGCC) -else -nolibc-test: nolibc-test.c nolibc-test-linkage.c - $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ - -nostdlib -static -include $(srctree)/tools/include/nolibc/nolibc.h nolibc-test.c nolibc-test-linkage.c $(LIBGCC) -endif - -libc-test: nolibc-test.c nolibc-test-linkage.c - $(QUIET_CC)$(HOSTCC) -o $@ nolibc-test.c nolibc-test-linkage.c - -# local libc-test -run-libc-test: libc-test - $(Q)./libc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -# local nolibc-test -run-nolibc-test: nolibc-test - $(Q)./nolibc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -# qemu user-land test -run-user: nolibc-test - $(Q)qemu-$(QEMU_ARCH_USER) ./nolibc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -initramfs.cpio: kernel nolibc-test - $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(objtree)/usr/gen_init_cpio - > initramfs.cpio - -initramfs: nolibc-test - $(QUIET_MKDIR)mkdir -p initramfs - $(call QUIET_INSTALL, initramfs/init) - $(Q)cp nolibc-test initramfs/init - -defconfig: - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(DEFCONFIG) - $(Q)if [ -n "$(EXTRACONFIG)" ]; then \ - $(srctree)/scripts/config --file $(objtree)/.config $(EXTRACONFIG); \ - $(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) olddefconfig < /dev/null; \ - fi - -kernel: | defconfig - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) < /dev/null - -kernel-standalone: initramfs | defconfig - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs < /dev/null - -# run the tests after building the kernel -run: kernel initramfs.cpio - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" - $(Q)$(REPORT) $(CURDIR)/run.out - -# re-run the tests from an existing kernel -rerun: - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" - $(Q)$(REPORT) $(CURDIR)/run.out - -# report with existing test log -report: - $(Q)$(REPORT) $(CURDIR)/run.out - -clean: - $(call QUIET_CLEAN, sysroot) - $(Q)rm -rf sysroot - $(call QUIET_CLEAN, nolibc-test) - $(Q)rm -f nolibc-test - $(call QUIET_CLEAN, libc-test) - $(Q)rm -f libc-test - $(call QUIET_CLEAN, initramfs.cpio) - $(Q)rm -rf initramfs.cpio - $(call QUIET_CLEAN, initramfs) - $(Q)rm -rf initramfs - $(call QUIET_CLEAN, run.out) - $(Q)rm -rf run.out - -.PHONY: sysroot/$(ARCH)/include +.PHONY: help diff --git a/tools/testing/selftests/nolibc/Makefile.include b/tools/testing/selftests/nolibc/Makefile.include new file mode 100644 index 000000000000..66287fafbbe0 --- /dev/null +++ b/tools/testing/selftests/nolibc/Makefile.include @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +__CFLAGS_STACKPROTECTOR = $(call cc-option,-fstack-protector-all) $(call cc-option,-mstack-protector-guard=global) +_CFLAGS_STACKPROTECTOR ?= $(call try-run, \ + echo 'void foo(void) {}' | $(CC) -x c - -o - -S $(CLANG_CROSS_FLAGS) $(__CFLAGS_STACKPROTECTOR) | grep -q __stack_chk_guard, \ + $(__CFLAGS_STACKPROTECTOR)) +_CFLAGS_SANITIZER ?= $(call cc-option,-fsanitize=undefined -fsanitize-trap=all) +CFLAGS_NOLIBC_TEST ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra \ + $(call cc-option,-fno-stack-protector) $(call cc-option,-Wmissing-prototypes) \ + $(_CFLAGS_STACKPROTECTOR) $(_CFLAGS_SANITIZER) diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc new file mode 100644 index 000000000000..0fb759ba992e --- /dev/null +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -0,0 +1,383 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for nolibc tests +# we're in ".../tools/testing/selftests/nolibc" +ifeq ($(srctree),) +srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR))) +endif + +include $(srctree)/tools/scripts/utilities.mak +# We need this for the "__cc-option" macro. +include $(srctree)/scripts/Makefile.compiler + +ifneq ($(O),) +ifneq ($(call is-absolute,$(O)),y) +$(error Only absolute O= parameters are supported) +endif +objtree := $(O) +else +objtree ?= $(srctree) +endif + +ifeq ($(ARCH),) +include $(srctree)/scripts/subarch.include +ARCH = $(SUBARCH) +endif + +cc-option = $(call __cc-option, $(CC),$(CLANG_CROSS_FLAGS),$(1),$(2)) + +# XARCH extends the kernel's ARCH with a few variants of the same +# architecture that only differ by the configuration, the toolchain +# and the Qemu program used. It is copied as-is into ARCH except for +# a few specific values which are mapped like this: +# +# XARCH | ARCH | config +# -------------|-----------|------------------------- +# ppc | powerpc | 32 bits +# ppc64 | powerpc | 64 bits big endian +# ppc64le | powerpc | 64 bits little endian +# +# It is recommended to only use XARCH, though it does not harm if +# ARCH is already set. For simplicity, ARCH is sufficient for all +# architectures where both are equal. + +# configure default variants for target kernel supported architectures +XARCH_powerpc = ppc +XARCH_mips = mips32le +XARCH_riscv = riscv64 +XARCH = $(or $(XARCH_$(ARCH)),$(ARCH)) + +# map from user input variants to their kernel supported architectures +ARCH_x32 = x86 +ARCH_armthumb = arm +ARCH_ppc = powerpc +ARCH_ppc64 = powerpc +ARCH_ppc64le = powerpc +ARCH_mips32le = mips +ARCH_mips32be = mips +ARCH_mipsn32le = mips +ARCH_mipsn32be = mips +ARCH_mips64le = mips +ARCH_mips64be = mips +ARCH_riscv32 = riscv +ARCH_riscv64 = riscv +ARCH_s390x = s390 +ARCH_sparc32 = sparc +ARCH_sparc64 = sparc +ARCH_sh4 = sh +ARCH := $(or $(ARCH_$(XARCH)),$(XARCH)) + +# kernel image names by architecture +IMAGE_i386 = arch/x86/boot/bzImage +IMAGE_x86_64 = arch/x86/boot/bzImage +IMAGE_x32 = arch/x86/boot/bzImage +IMAGE_x86 = arch/x86/boot/bzImage +IMAGE_arm64 = arch/arm64/boot/Image +IMAGE_arm = arch/arm/boot/zImage +IMAGE_armthumb = arch/arm/boot/zImage +IMAGE_mips32le = vmlinuz +IMAGE_mips32be = vmlinuz +IMAGE_mipsn32le = vmlinuz +IMAGE_mipsn32be = vmlinuz +IMAGE_mips64le = vmlinuz +IMAGE_mips64be = vmlinuz +IMAGE_ppc = vmlinux +IMAGE_ppc64 = vmlinux +IMAGE_ppc64le = arch/powerpc/boot/zImage +IMAGE_riscv = arch/riscv/boot/Image +IMAGE_riscv32 = arch/riscv/boot/Image +IMAGE_riscv64 = arch/riscv/boot/Image +IMAGE_s390x = arch/s390/boot/bzImage +IMAGE_s390 = arch/s390/boot/bzImage +IMAGE_loongarch = arch/loongarch/boot/vmlinuz.efi +IMAGE_sparc32 = arch/sparc/boot/image +IMAGE_sparc64 = arch/sparc/boot/image +IMAGE_m68k = vmlinux +IMAGE_sh4 = arch/sh/boot/zImage +IMAGE = $(objtree)/$(IMAGE_$(XARCH)) +IMAGE_NAME = $(notdir $(IMAGE)) + +# default kernel configurations that appear to be usable +DEFCONFIG_i386 = defconfig +DEFCONFIG_x86_64 = defconfig +DEFCONFIG_x32 = defconfig +DEFCONFIG_x86 = defconfig +DEFCONFIG_arm64 = defconfig +DEFCONFIG_arm = multi_v7_defconfig +DEFCONFIG_armthumb = multi_v7_defconfig +DEFCONFIG_mips32le = malta_defconfig +DEFCONFIG_mips32be = malta_defconfig generic/eb.config +DEFCONFIG_mipsn32le = malta_defconfig generic/64r2.config +DEFCONFIG_mipsn32be = malta_defconfig generic/64r6.config generic/eb.config +DEFCONFIG_mips64le = malta_defconfig generic/64r6.config +DEFCONFIG_mips64be = malta_defconfig generic/64r2.config generic/eb.config +DEFCONFIG_ppc = pmac32_defconfig +DEFCONFIG_ppc64 = powernv_be_defconfig +DEFCONFIG_ppc64le = powernv_defconfig +DEFCONFIG_riscv = defconfig +DEFCONFIG_riscv32 = rv32_defconfig +DEFCONFIG_riscv64 = defconfig +DEFCONFIG_s390x = defconfig +DEFCONFIG_s390 = defconfig compat.config +DEFCONFIG_loongarch = defconfig +DEFCONFIG_sparc32 = sparc32_defconfig +DEFCONFIG_sparc64 = sparc64_defconfig +DEFCONFIG_m68k = virt_defconfig +DEFCONFIG_sh4 = rts7751r2dplus_defconfig +DEFCONFIG = $(DEFCONFIG_$(XARCH)) + +EXTRACONFIG_x32 = -e CONFIG_X86_X32_ABI +EXTRACONFIG_arm = -e CONFIG_NAMESPACES +EXTRACONFIG_armthumb = -e CONFIG_NAMESPACES +EXTRACONFIG_m68k = -e CONFIG_BLK_DEV_INITRD +EXTRACONFIG_sh4 = -e CONFIG_BLK_DEV_INITRD -e CONFIG_CMDLINE_FROM_BOOTLOADER +EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) + +# optional tests to run (default = all) +TEST = + +# QEMU_ARCH: arch names used by qemu +QEMU_ARCH_i386 = i386 +QEMU_ARCH_x86_64 = x86_64 +QEMU_ARCH_x32 = x86_64 +QEMU_ARCH_x86 = x86_64 +QEMU_ARCH_arm64 = aarch64 +QEMU_ARCH_arm = arm +QEMU_ARCH_armthumb = arm +QEMU_ARCH_mips32le = mipsel # works with malta_defconfig +QEMU_ARCH_mips32be = mips +QEMU_ARCH_mipsn32le = mips64el +QEMU_ARCH_mipsn32be = mips64 +QEMU_ARCH_mips64le = mips64el +QEMU_ARCH_mips64be = mips64 +QEMU_ARCH_ppc = ppc +QEMU_ARCH_ppc64 = ppc64 +QEMU_ARCH_ppc64le = ppc64 +QEMU_ARCH_riscv = riscv64 +QEMU_ARCH_riscv32 = riscv32 +QEMU_ARCH_riscv64 = riscv64 +QEMU_ARCH_s390x = s390x +QEMU_ARCH_s390 = s390x +QEMU_ARCH_loongarch = loongarch64 +QEMU_ARCH_sparc32 = sparc +QEMU_ARCH_sparc64 = sparc64 +QEMU_ARCH_m68k = m68k +QEMU_ARCH_sh4 = sh4 +QEMU_ARCH = $(QEMU_ARCH_$(XARCH)) + +QEMU_ARCH_USER_ppc64le = ppc64le +QEMU_ARCH_USER_mipsn32le = mipsn32el +QEMU_ARCH_USER_mipsn32be = mipsn32 +QEMU_ARCH_USER = $(or $(QEMU_ARCH_USER_$(XARCH)),$(QEMU_ARCH_$(XARCH))) + +QEMU_BIOS_DIR = /usr/share/edk2/ +QEMU_BIOS_loongarch = $(QEMU_BIOS_DIR)/loongarch64/OVMF_CODE.fd + +ifneq ($(QEMU_BIOS_$(XARCH)),) +QEMU_ARGS_BIOS = -bios $(QEMU_BIOS_$(XARCH)) +endif + +# QEMU_ARGS : some arch-specific args to pass to qemu +QEMU_ARGS_i386 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_x32 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_arm64 = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_arm = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_armthumb = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips32le = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips32be = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mipsn32le = -M malta -cpu 5KEc -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mipsn32be = -M malta -cpu I6400 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips64le = -M malta -cpu I6400 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips64be = -M malta -cpu 5KEc -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc64 = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_riscv32 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_riscv64 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_s390x = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_s390 = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_loongarch = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sparc32 = -M SS-5 -m 256M -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sparc64 = -M sun4u -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_m68k = -M virt -append "console=ttyGF0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sh4 = -M r2d -serial file:/dev/stdout -append "console=ttySC1,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS = -m 1G $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA) + +# OUTPUT is only set when run from the main makefile, otherwise +# it defaults to this nolibc directory. +OUTPUT ?= $(CURDIR)/ + +ifeq ($(V),1) +Q= +else +Q=@ +endif + +CFLAGS_i386 = $(call cc-option,-m32) +CFLAGS_x32 = -mx32 +CFLAGS_arm = -marm +CFLAGS_armthumb = -mthumb -march=armv6t2 +CFLAGS_ppc = -m32 -mbig-endian -mno-vsx $(call cc-option,-mmultiple) +CFLAGS_ppc64 = -m64 -mbig-endian -mno-vsx $(call cc-option,-mmultiple) +CFLAGS_ppc64le = -m64 -mlittle-endian -mno-vsx $(call cc-option,-mabi=elfv2) +CFLAGS_s390x = -m64 +CFLAGS_s390 = -m31 +CFLAGS_mips32le = -EL -mabi=32 -fPIC +CFLAGS_mips32be = -EB -mabi=32 +CFLAGS_mipsn32le = -EL -mabi=n32 -fPIC -march=mips64r2 +CFLAGS_mipsn32be = -EB -mabi=n32 -march=mips64r6 +CFLAGS_mips64le = -EL -mabi=64 -march=mips64r6 +CFLAGS_mips64be = -EB -mabi=64 -march=mips64r2 +CFLAGS_sparc32 = $(call cc-option,-m32) +CFLAGS_sh4 = -ml -m4 +ifeq ($(origin XARCH),command line) +CFLAGS_XARCH = $(CFLAGS_$(XARCH)) +endif + +include Makefile.include + +CFLAGS ?= $(CFLAGS_NOLIBC_TEST) $(CFLAGS_XARCH) $(CFLAGS_EXTRA) +LDFLAGS := + +LIBGCC := -lgcc + +ifeq ($(ARCH),x86) +# Not needed on x86, probably not present for x32 +LIBGCC := +endif + +ifneq ($(LLVM),) +# Not needed for clang +LIBGCC := +endif + +# Modify CFLAGS based on LLVM= +include $(srctree)/tools/scripts/Makefile.include + +REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ + /^Total number of errors:/{done++} \ + END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ + if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ + printf("\nSee all results in %s\n", ARGV[1]); }' + +help: + @echo "Supported targets under selftests/nolibc:" + @echo " all call the \"run\" target below" + @echo " help this help" + @echo " sysroot create the nolibc sysroot here (uses \$$ARCH)" + @echo " nolibc-test build the executable (uses \$$CC and \$$CROSS_COMPILE)" + @echo " libc-test build an executable using the compiler's default libc instead" + @echo " run-user runs the executable under QEMU (uses \$$XARCH, \$$TEST)" + @echo " initramfs.cpio prepare the initramfs archive with nolibc-test" + @echo " initramfs prepare the initramfs tree with nolibc-test" + @echo " defconfig create a fresh new default config (uses \$$XARCH)" + @echo " kernel (re)build the kernel (uses \$$XARCH)" + @echo " kernel-standalone (re)build the kernel with the initramfs (uses \$$XARCH)" + @echo " run runs the kernel in QEMU after building it (uses \$$XARCH, \$$TEST)" + @echo " rerun runs a previously prebuilt kernel in QEMU (uses \$$XARCH, \$$TEST)" + @echo " clean clean the sysroot, initramfs, build and output files" + @echo "" + @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST." + @echo "" + @echo "Currently using the following variables:" + @echo " ARCH = $(ARCH)" + @echo " XARCH = $(XARCH)" + @echo " CROSS_COMPILE = $(CROSS_COMPILE)" + @echo " CC = $(CC)" + @echo " OUTPUT = $(OUTPUT)" + @echo " TEST = $(TEST)" + @echo " QEMU_ARCH = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$XARCH]" + @echo " IMAGE_NAME = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$XARCH]" + @echo "" + +all: run + +sysroot: sysroot/$(ARCH)/include + +sysroot/$(ARCH)/include: + $(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot + $(QUIET_MKDIR)mkdir -p sysroot + $(Q)$(MAKE) -C $(srctree) outputmakefile + $(Q)$(MAKE) -C $(srctree)/tools/include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone headers_check + $(Q)mv sysroot/sysroot sysroot/$(ARCH) + +ifneq ($(NOLIBC_SYSROOT),0) +nolibc-test: nolibc-test.c nolibc-test-linkage.c sysroot/$(ARCH)/include + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ + -nostdlib -nostdinc -static -Isysroot/$(ARCH)/include nolibc-test.c nolibc-test-linkage.c $(LIBGCC) +else +nolibc-test: nolibc-test.c nolibc-test-linkage.c + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ + -nostdlib -static -include $(srctree)/tools/include/nolibc/nolibc.h nolibc-test.c nolibc-test-linkage.c $(LIBGCC) +endif + +libc-test: nolibc-test.c nolibc-test-linkage.c + $(QUIET_CC)$(HOSTCC) -o $@ nolibc-test.c nolibc-test-linkage.c + +# local libc-test +run-libc-test: libc-test + $(Q)./libc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +# local nolibc-test +run-nolibc-test: nolibc-test + $(Q)./nolibc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +# qemu user-land test +run-user: nolibc-test + $(Q)qemu-$(QEMU_ARCH_USER) ./nolibc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +initramfs.cpio: kernel nolibc-test + $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(objtree)/usr/gen_init_cpio - > initramfs.cpio + +initramfs: nolibc-test + $(QUIET_MKDIR)mkdir -p initramfs + $(call QUIET_INSTALL, initramfs/init) + $(Q)cp nolibc-test initramfs/init + +defconfig: + $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(DEFCONFIG) + $(Q)if [ -n "$(EXTRACONFIG)" ]; then \ + $(srctree)/scripts/config --file $(objtree)/.config $(EXTRACONFIG); \ + $(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) olddefconfig < /dev/null; \ + fi + +kernel: + $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) < /dev/null + +kernel-standalone: initramfs + $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs < /dev/null + +# run the tests after building the kernel +run: kernel initramfs.cpio + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial file:/dev/stdout $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)$(REPORT) $(CURDIR)/run.out + +# re-run the tests from an existing kernel +rerun: + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial file:/dev/stdout $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)$(REPORT) $(CURDIR)/run.out + +# report with existing test log +report: + $(Q)$(REPORT) $(CURDIR)/run.out + +clean: + $(call QUIET_CLEAN, sysroot) + $(Q)rm -rf sysroot + $(call QUIET_CLEAN, nolibc-test) + $(Q)rm -f nolibc-test + $(call QUIET_CLEAN, libc-test) + $(Q)rm -f libc-test + $(call QUIET_CLEAN, initramfs.cpio) + $(Q)rm -rf initramfs.cpio + $(call QUIET_CLEAN, initramfs) + $(Q)rm -rf initramfs + $(call QUIET_CLEAN, run.out) + $(Q)rm -rf run.out + +.PHONY: sysroot/$(ARCH)/include diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index dbe13000fb1a..a297ee0d6d07 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -877,7 +877,12 @@ int test_file_stream(void) return 0; } -int test_fork(void) +enum fork_type { + FORK_STANDARD, + FORK_VFORK, +}; + +int test_fork(enum fork_type type) { int status; pid_t pid; @@ -886,14 +891,23 @@ int test_fork(void) fflush(stdout); fflush(stderr); - pid = fork(); + switch (type) { + case FORK_STANDARD: + pid = fork(); + break; + case FORK_VFORK: + pid = vfork(); + break; + default: + return 1; + } switch (pid) { case -1: return 1; case 0: - exit(123); + _exit(123); default: pid = waitpid(pid, &status, 0); @@ -1330,7 +1344,7 @@ int run_syscall(int min, int max) CASE_TEST(dup3_m1); tmp = dup3(-1, 100, 0); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break; CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = "/", [1] = NULL }, NULL), -1, EACCES); break; CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break; - CASE_TEST(fork); EXPECT_SYSZR(1, test_fork()); break; + CASE_TEST(fork); EXPECT_SYSZR(1, test_fork(FORK_STANDARD)); break; CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break; CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break; CASE_TEST(directories); EXPECT_SYSZR(proc, test_dirent()); break; @@ -1349,6 +1363,7 @@ int run_syscall(int min, int max) CASE_TEST(mmap_bad); EXPECT_PTRER(1, mmap(NULL, 0, PROT_READ, MAP_PRIVATE, 0, 0), MAP_FAILED, EINVAL); break; CASE_TEST(munmap_bad); EXPECT_SYSER(1, munmap(NULL, 0), -1, EINVAL); break; CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; + CASE_TEST(nanosleep); ts.tv_nsec = -1; EXPECT_SYSER(1, nanosleep(&ts, NULL), -1, EINVAL); break; CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", O_RDONLY), -1); if (tmp != -1) close(tmp); break; CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", O_RDONLY), -1, ENOENT); if (tmp != -1) close(tmp); break; CASE_TEST(openat_dir); EXPECT_SYSZR(1, test_openat()); break; @@ -1374,6 +1389,7 @@ int run_syscall(int min, int max) CASE_TEST(uname_fault); EXPECT_SYSER(1, uname(NULL), -1, EFAULT); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break; CASE_TEST(unlink_blah); EXPECT_SYSER(1, unlink("/proc/self/blah"), -1, ENOENT); break; + CASE_TEST(vfork); EXPECT_SYSZR(1, test_fork(FORK_VFORK)); break; CASE_TEST(wait_child); EXPECT_SYSER(1, wait(&tmp), -1, ECHILD); break; CASE_TEST(waitpid_min); EXPECT_SYSER(1, waitpid(INT_MIN, &tmp, WNOHANG), -1, ESRCH); break; CASE_TEST(waitpid_child); EXPECT_SYSER(1, waitpid(getpid(), &tmp, WNOHANG), -1, ECHILD); break; @@ -1413,7 +1429,7 @@ int run_stdlib(int min, int max) * Add some more chars after the \0, to test functions that overwrite the buffer set * the \0 at the exact right position. */ - char buf[10] = "test123456"; + char buf[11] = "test123456"; buf[4] = '\0'; @@ -1646,6 +1662,28 @@ int test_strerror(void) return 0; } +static int test_printf_error(void) +{ + int fd, ret, saved_errno; + + fd = open("/dev/full", O_RDWR); + if (fd == -1) + return 1; + + errno = 0; + ret = dprintf(fd, "foo"); + saved_errno = errno; + close(fd); + + if (ret != -1) + return 2; + + if (saved_errno != ENOSPC) + return 3; + + return 0; +} + static int run_printf(int min, int max) { int test; @@ -1675,6 +1713,7 @@ static int run_printf(int min, int max) CASE_TEST(width_trunc); EXPECT_VFPRINTF(25, " ", "%25d", 1); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break; + CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */ @@ -1762,12 +1801,14 @@ int prepare(void) if (stat("/dev/.", &stat_buf) == 0 || mkdir("/dev", 0755) == 0) { if (stat("/dev/console", &stat_buf) != 0 || stat("/dev/null", &stat_buf) != 0 || - stat("/dev/zero", &stat_buf) != 0) { + stat("/dev/zero", &stat_buf) != 0 || + stat("/dev/full", &stat_buf) != 0) { /* try devtmpfs first, otherwise fall back to manual creation */ if (mount("/dev", "/dev", "devtmpfs", 0, 0) != 0) { mknod("/dev/console", 0600 | S_IFCHR, makedev(5, 1)); mknod("/dev/null", 0666 | S_IFCHR, makedev(1, 3)); mknod("/dev/zero", 0666 | S_IFCHR, makedev(1, 5)); + mknod("/dev/full", 0666 | S_IFCHR, makedev(1, 7)); } } } diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index 8277599e6441..e8af1fb505cf 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -18,15 +18,16 @@ test_mode=system werror=1 llvm= all_archs=( - i386 x86_64 + i386 x86_64 x32 arm64 arm armthumb - mips32le mips32be + mips32le mips32be mipsn32le mipsn32be mips64le mips64be ppc ppc64 ppc64le riscv32 riscv64 s390x s390 loongarch sparc32 sparc64 m68k + sh4 ) archs="${all_archs[@]}" @@ -114,6 +115,7 @@ crosstool_arch() { mips*) echo mips;; s390*) echo s390;; sparc*) echo sparc64;; + x32*) echo x86_64;; *) echo "$1";; esac } @@ -169,7 +171,7 @@ test_arch() { if [ "$werror" -ne 0 ]; then CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror" fi - MAKE=(make -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}") + MAKE=(make -f Makefile.nolibc -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}") case "$test_mode" in 'system') @@ -187,7 +189,11 @@ test_arch() { echo "Unsupported configuration" return fi - if [ "$arch" = "m68k" ] && [ "$llvm" = "1" ]; then + if [ "$arch" = "m68k" -o "$arch" = "sh4" ] && [ "$llvm" = "1" ]; then + echo "Unsupported configuration" + return + fi + if [ "$arch" = "x32" ] && [ "$test_mode" = "user" ]; then echo "Unsupported configuration" return fi diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index ac26481d29d9..da0db0e7c969 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -229,4 +229,32 @@ TEST_F(pci_ep_data_transfer, COPY_TEST) test_size[i]); } } + +FIXTURE(pcie_ep_doorbell) +{ + int fd; +}; + +FIXTURE_SETUP(pcie_ep_doorbell) +{ + self->fd = open(test_device, O_RDWR); + + ASSERT_NE(-1, self->fd) TH_LOG("Can't open PCI Endpoint Test device"); +}; + +FIXTURE_TEARDOWN(pcie_ep_doorbell) +{ + close(self->fd); +}; + +TEST_F(pcie_ep_doorbell, DOORBELL_TEST) +{ + int ret; + + pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO); + ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); + + pci_ep_ioctl(PCITEST_DOORBELL, 0); + EXPECT_FALSE(ret) TH_LOG("Test failed for Doorbell\n"); +} TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/perf_events/.gitignore b/tools/testing/selftests/perf_events/.gitignore index ee93dc4969b8..4931b3b6bbd3 100644 --- a/tools/testing/selftests/perf_events/.gitignore +++ b/tools/testing/selftests/perf_events/.gitignore @@ -2,3 +2,4 @@ sigtrap_threads remove_on_exec watermark_signal +mmap diff --git a/tools/testing/selftests/perf_events/Makefile b/tools/testing/selftests/perf_events/Makefile index 70e3ff211278..2e5d85770dfe 100644 --- a/tools/testing/selftests/perf_events/Makefile +++ b/tools/testing/selftests/perf_events/Makefile @@ -2,5 +2,5 @@ CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES) LDFLAGS += -lpthread -TEST_GEN_PROGS := sigtrap_threads remove_on_exec watermark_signal +TEST_GEN_PROGS := sigtrap_threads remove_on_exec watermark_signal mmap include ../lib.mk diff --git a/tools/testing/selftests/perf_events/mmap.c b/tools/testing/selftests/perf_events/mmap.c new file mode 100644 index 000000000000..ea0427aac1f9 --- /dev/null +++ b/tools/testing/selftests/perf_events/mmap.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../kselftest_harness.h" + +#define RB_SIZE 0x3000 +#define AUX_SIZE 0x10000 +#define AUX_OFFS 0x4000 + +#define HOLE_SIZE 0x1000 + +/* Reserve space for rb, aux with space for shrink-beyond-vma testing. */ +#define REGION_SIZE (2 * RB_SIZE + 2 * AUX_SIZE) +#define REGION_AUX_OFFS (2 * RB_SIZE) + +#define MAP_BASE 1 +#define MAP_AUX 2 + +#define EVENT_SRC_DIR "/sys/bus/event_source/devices" + +FIXTURE(perf_mmap) +{ + int fd; + void *ptr; + void *region; +}; + +FIXTURE_VARIANT(perf_mmap) +{ + bool aux; + unsigned long ptr_size; +}; + +FIXTURE_VARIANT_ADD(perf_mmap, rb) +{ + .aux = false, + .ptr_size = RB_SIZE, +}; + +FIXTURE_VARIANT_ADD(perf_mmap, aux) +{ + .aux = true, + .ptr_size = AUX_SIZE, +}; + +static bool read_event_type(struct dirent *dent, __u32 *type) +{ + char typefn[512]; + FILE *fp; + int res; + + snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name); + fp = fopen(typefn, "r"); + if (!fp) + return false; + + res = fscanf(fp, "%u", type); + fclose(fp); + return res > 0; +} + +FIXTURE_SETUP(perf_mmap) +{ + struct perf_event_attr attr = { + .size = sizeof(attr), + .disabled = 1, + .exclude_kernel = 1, + .exclude_hv = 1, + }; + struct perf_event_attr attr_ok = {}; + unsigned int eacces = 0, map = 0; + struct perf_event_mmap_page *rb; + struct dirent *dent; + void *aux, *region; + DIR *dir; + + self->ptr = NULL; + + dir = opendir(EVENT_SRC_DIR); + if (!dir) + SKIP(return, "perf not available."); + + region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + ASSERT_NE(region, MAP_FAILED); + self->region = region; + + // Try to find a suitable event on this system + while ((dent = readdir(dir))) { + int fd; + + if (!read_event_type(dent, &attr.type)) + continue; + + fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0); + if (fd < 0) { + if (errno == EACCES) + eacces++; + continue; + } + + // Check whether the event supports mmap() + rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + if (rb == MAP_FAILED) { + close(fd); + continue; + } + + if (!map) { + // Save the event in case that no AUX capable event is found + attr_ok = attr; + map = MAP_BASE; + } + + if (!variant->aux) + continue; + + rb->aux_offset = AUX_OFFS; + rb->aux_size = AUX_SIZE; + + // Check whether it supports a AUX buffer + aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, AUX_OFFS); + if (aux == MAP_FAILED) { + munmap(rb, RB_SIZE); + close(fd); + continue; + } + + attr_ok = attr; + map = MAP_AUX; + munmap(aux, AUX_SIZE); + munmap(rb, RB_SIZE); + close(fd); + break; + } + closedir(dir); + + if (!map) { + if (!eacces) + SKIP(return, "No mappable perf event found."); + else + SKIP(return, "No permissions for perf_event_open()"); + } + + self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0); + ASSERT_NE(self->fd, -1); + + rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0); + ASSERT_NE(rb, MAP_FAILED); + + if (!variant->aux) { + self->ptr = rb; + return; + } + + if (map != MAP_AUX) + SKIP(return, "No AUX event found."); + + rb->aux_offset = AUX_OFFS; + rb->aux_size = AUX_SIZE; + aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS); + ASSERT_NE(aux, MAP_FAILED); + self->ptr = aux; +} + +FIXTURE_TEARDOWN(perf_mmap) +{ + ASSERT_EQ(munmap(self->region, REGION_SIZE), 0); + if (self->fd != -1) + ASSERT_EQ(close(self->fd), 0); +} + +TEST_F(perf_mmap, remap) +{ + void *tmp, *ptr = self->ptr; + unsigned long size = variant->ptr_size; + + // Test the invalid remaps + ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); + ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); + ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED); + // Shrink the end of the mapping such that we only unmap past end of the VMA, + // which should succeed and poke a hole into the PROT_NONE region + ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); + + // Remap the whole buffer to a new address + tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(tmp, MAP_FAILED); + + // Try splitting offset 1 hole size into VMA, this should fail + ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE, + MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED); + // Remapping the whole thing should succeed fine + ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp); + ASSERT_EQ(ptr, tmp); + ASSERT_EQ(munmap(tmp, size), 0); +} + +TEST_F(perf_mmap, unmap) +{ + unsigned long size = variant->ptr_size; + + // Try to poke holes into the mappings + ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0); + ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0); + ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0); +} + +TEST_F(perf_mmap, map) +{ + unsigned long size = variant->ptr_size; + + // Try to poke holes into the mappings by mapping anonymous memory over it + ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); + ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); + ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/pidfd/.gitignore b/tools/testing/selftests/pidfd/.gitignore index 0406a065deb4..144e7ff65d6a 100644 --- a/tools/testing/selftests/pidfd/.gitignore +++ b/tools/testing/selftests/pidfd/.gitignore @@ -10,3 +10,5 @@ pidfd_file_handle_test pidfd_bind_mount pidfd_info_test pidfd_exec_helper +pidfd_xattr_test +pidfd_setattr_test diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile index fcbefc0d77f6..764a8f9ecefa 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -CFLAGS += -g $(KHDR_INCLUDES) -pthread -Wall +CFLAGS += -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) -pthread -Wall TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \ pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \ - pidfd_file_handle_test pidfd_bind_mount pidfd_info_test + pidfd_file_handle_test pidfd_bind_mount pidfd_info_test \ + pidfd_xattr_test pidfd_setattr_test TEST_GEN_PROGS_EXTENDED := pidfd_exec_helper diff --git a/tools/testing/selftests/pidfd/pidfd.h b/tools/testing/selftests/pidfd/pidfd.h index efd74063126e..f87993def738 100644 --- a/tools/testing/selftests/pidfd/pidfd.h +++ b/tools/testing/selftests/pidfd/pidfd.h @@ -16,9 +16,22 @@ #include #include +/* + * Remove the userspace definitions of the following preprocessor symbols + * to avoid duplicate-definition warnings from the subsequent in-kernel + * definitions. + */ +#undef SCHED_NORMAL +#undef SCHED_FLAG_KEEP_ALL +#undef SCHED_FLAG_UTIL_CLAMP + #include "../kselftest.h" #include "../clone3/clone3_selftests.h" +#ifndef FD_PIDFS_ROOT +#define FD_PIDFS_ROOT -10002 +#endif + #ifndef P_PIDFD #define P_PIDFD 3 #endif @@ -56,7 +69,7 @@ #endif #ifndef PIDFD_SELF_THREAD_GROUP -#define PIDFD_SELF_THREAD_GROUP -20000 /* Current thread group leader. */ +#define PIDFD_SELF_THREAD_GROUP -10001 /* Current thread group leader. */ #endif #ifndef PIDFD_SELF diff --git a/tools/testing/selftests/pidfd/pidfd_file_handle_test.c b/tools/testing/selftests/pidfd/pidfd_file_handle_test.c index 439b9c6c0457..6bd2e9c9565b 100644 --- a/tools/testing/selftests/pidfd/pidfd_file_handle_test.c +++ b/tools/testing/selftests/pidfd/pidfd_file_handle_test.c @@ -500,4 +500,64 @@ TEST_F(file_handle, valid_name_to_handle_at_flags) ASSERT_EQ(close(pidfd), 0); } +/* + * That we decode a file handle without having to pass a pidfd. + */ +TEST_F(file_handle, decode_purely_based_on_file_handle) +{ + int mnt_id; + struct file_handle *fh; + int pidfd = -EBADF; + struct stat st1, st2; + + fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ); + ASSERT_NE(fh, NULL); + memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ); + fh->handle_bytes = MAX_HANDLE_SZ; + + ASSERT_EQ(name_to_handle_at(self->child_pidfd1, "", fh, &mnt_id, AT_EMPTY_PATH), 0); + + ASSERT_EQ(fstat(self->child_pidfd1, &st1), 0); + + pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, 0); + ASSERT_GE(pidfd, 0); + + ASSERT_EQ(fstat(pidfd, &st2), 0); + ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); + + ASSERT_EQ(close(pidfd), 0); + + pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_CLOEXEC); + ASSERT_GE(pidfd, 0); + + ASSERT_EQ(fstat(pidfd, &st2), 0); + ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); + + ASSERT_EQ(close(pidfd), 0); + + pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_NONBLOCK); + ASSERT_GE(pidfd, 0); + + ASSERT_EQ(fstat(pidfd, &st2), 0); + ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); + + ASSERT_EQ(close(pidfd), 0); + + pidfd = open_by_handle_at(self->pidfd, fh, 0); + ASSERT_GE(pidfd, 0); + + ASSERT_EQ(fstat(pidfd, &st2), 0); + ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); + + ASSERT_EQ(close(pidfd), 0); + + pidfd = open_by_handle_at(-EBADF, fh, 0); + ASSERT_LT(pidfd, 0); + + pidfd = open_by_handle_at(AT_FDCWD, fh, 0); + ASSERT_LT(pidfd, 0); + + free(fh); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/pidfd/pidfd_setattr_test.c b/tools/testing/selftests/pidfd/pidfd_setattr_test.c new file mode 100644 index 000000000000..d7de05edc4b3 --- /dev/null +++ b/tools/testing/selftests/pidfd/pidfd_setattr_test.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pidfd.h" +#include "../kselftest_harness.h" + +FIXTURE(pidfs_setattr) +{ + pid_t child_pid; + int child_pidfd; +}; + +FIXTURE_SETUP(pidfs_setattr) +{ + self->child_pid = create_child(&self->child_pidfd, CLONE_NEWUSER | CLONE_NEWPID); + EXPECT_GE(self->child_pid, 0); + + if (self->child_pid == 0) + _exit(EXIT_SUCCESS); +} + +FIXTURE_TEARDOWN(pidfs_setattr) +{ + sys_waitid(P_PID, self->child_pid, NULL, WEXITED); + EXPECT_EQ(close(self->child_pidfd), 0); +} + +TEST_F(pidfs_setattr, no_chown) +{ + ASSERT_LT(fchown(self->child_pidfd, 1234, 5678), 0); + ASSERT_EQ(errno, EOPNOTSUPP); +} + +TEST_F(pidfs_setattr, no_chmod) +{ + ASSERT_LT(fchmod(self->child_pidfd, 0777), 0); + ASSERT_EQ(errno, EOPNOTSUPP); +} + +TEST_F(pidfs_setattr, no_exec) +{ + char *const argv[] = { NULL }; + char *const envp[] = { NULL }; + + ASSERT_LT(execveat(self->child_pidfd, "", argv, envp, AT_EMPTY_PATH), 0); + ASSERT_EQ(errno, EACCES); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/pidfd/pidfd_xattr_test.c b/tools/testing/selftests/pidfd/pidfd_xattr_test.c new file mode 100644 index 000000000000..5cf7bb0e4bf2 --- /dev/null +++ b/tools/testing/selftests/pidfd/pidfd_xattr_test.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pidfd.h" +#include "../kselftest_harness.h" + +FIXTURE(pidfs_xattr) +{ + pid_t child_pid; + int child_pidfd; +}; + +FIXTURE_SETUP(pidfs_xattr) +{ + self->child_pid = create_child(&self->child_pidfd, CLONE_NEWUSER | CLONE_NEWPID); + EXPECT_GE(self->child_pid, 0); + + if (self->child_pid == 0) + _exit(EXIT_SUCCESS); +} + +FIXTURE_TEARDOWN(pidfs_xattr) +{ + sys_waitid(P_PID, self->child_pid, NULL, WEXITED); +} + +TEST_F(pidfs_xattr, set_get_list_xattr_multiple) +{ + int ret, i; + char xattr_name[32]; + char xattr_value[32]; + char buf[32]; + const int num_xattrs = 10; + char list[PATH_MAX] = {}; + + for (i = 0; i < num_xattrs; i++) { + snprintf(xattr_name, sizeof(xattr_name), "trusted.testattr%d", i); + snprintf(xattr_value, sizeof(xattr_value), "testvalue%d", i); + ret = fsetxattr(self->child_pidfd, xattr_name, xattr_value, strlen(xattr_value), 0); + ASSERT_EQ(ret, 0); + } + + for (i = 0; i < num_xattrs; i++) { + snprintf(xattr_name, sizeof(xattr_name), "trusted.testattr%d", i); + snprintf(xattr_value, sizeof(xattr_value), "testvalue%d", i); + memset(buf, 0, sizeof(buf)); + ret = fgetxattr(self->child_pidfd, xattr_name, buf, sizeof(buf)); + ASSERT_EQ(ret, strlen(xattr_value)); + ASSERT_EQ(strcmp(buf, xattr_value), 0); + } + + ret = flistxattr(self->child_pidfd, list, sizeof(list)); + ASSERT_GT(ret, 0); + for (i = 0; i < num_xattrs; i++) { + snprintf(xattr_name, sizeof(xattr_name), "trusted.testattr%d", i); + bool found = false; + for (char *it = list; it < list + ret; it += strlen(it) + 1) { + if (strcmp(it, xattr_name)) + continue; + found = true; + break; + } + ASSERT_TRUE(found); + } + + for (i = 0; i < num_xattrs; i++) { + snprintf(xattr_name, sizeof(xattr_name), "trusted.testattr%d", i); + ret = fremovexattr(self->child_pidfd, xattr_name); + ASSERT_EQ(ret, 0); + + ret = fgetxattr(self->child_pidfd, xattr_name, buf, sizeof(buf)); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, ENODATA); + } +} + +TEST_F(pidfs_xattr, set_get_list_xattr_persistent) +{ + int ret; + char buf[32]; + char list[PATH_MAX] = {}; + + ret = fsetxattr(self->child_pidfd, "trusted.persistent", "persistent value", strlen("persistent value"), 0); + ASSERT_EQ(ret, 0); + + memset(buf, 0, sizeof(buf)); + ret = fgetxattr(self->child_pidfd, "trusted.persistent", buf, sizeof(buf)); + ASSERT_EQ(ret, strlen("persistent value")); + ASSERT_EQ(strcmp(buf, "persistent value"), 0); + + ret = flistxattr(self->child_pidfd, list, sizeof(list)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp(list, "trusted.persistent"), 0) + + ASSERT_EQ(close(self->child_pidfd), 0); + self->child_pidfd = -EBADF; + sleep(2); + + self->child_pidfd = sys_pidfd_open(self->child_pid, 0); + ASSERT_GE(self->child_pidfd, 0); + + memset(buf, 0, sizeof(buf)); + ret = fgetxattr(self->child_pidfd, "trusted.persistent", buf, sizeof(buf)); + ASSERT_EQ(ret, strlen("persistent value")); + ASSERT_EQ(strcmp(buf, "persistent value"), 0); + + ret = flistxattr(self->child_pidfd, list, sizeof(list)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp(list, "trusted.persistent"), 0); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore index 973968f45bba..19bb333e2485 100644 --- a/tools/testing/selftests/proc/.gitignore +++ b/tools/testing/selftests/proc/.gitignore @@ -5,6 +5,7 @@ /proc-2-is-kthread /proc-fsconfig-hidepid /proc-loadavg-001 +/proc-maps-race /proc-multiple-procfs /proc-empty-vm /proc-pid-vm diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile index b12921b9794b..50aba102201a 100644 --- a/tools/testing/selftests/proc/Makefile +++ b/tools/testing/selftests/proc/Makefile @@ -9,6 +9,7 @@ TEST_GEN_PROGS += fd-002-posix-eq TEST_GEN_PROGS += fd-003-kthread TEST_GEN_PROGS += proc-2-is-kthread TEST_GEN_PROGS += proc-loadavg-001 +TEST_GEN_PROGS += proc-maps-race TEST_GEN_PROGS += proc-empty-vm TEST_GEN_PROGS += proc-pid-vm TEST_GEN_PROGS += proc-self-map-files-001 diff --git a/tools/testing/selftests/proc/proc-maps-race.c b/tools/testing/selftests/proc/proc-maps-race.c new file mode 100644 index 000000000000..94bba4553130 --- /dev/null +++ b/tools/testing/selftests/proc/proc-maps-race.c @@ -0,0 +1,741 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2022 Google LLC. + * Author: Suren Baghdasaryan + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ +/* + * Fork a child that concurrently modifies address space while the main + * process is reading /proc/$PID/maps and verifying the results. Address + * space modifications include: + * VMA splitting and merging + * + */ +#define _GNU_SOURCE +#include "../kselftest_harness.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* /proc/pid/maps parsing routines */ +struct page_content { + char *data; + ssize_t size; +}; + +#define LINE_MAX_SIZE 256 + +struct line_content { + char text[LINE_MAX_SIZE]; + unsigned long start_addr; + unsigned long end_addr; +}; + +enum test_state { + INIT, + CHILD_READY, + PARENT_READY, + SETUP_READY, + SETUP_MODIFY_MAPS, + SETUP_MAPS_MODIFIED, + SETUP_RESTORE_MAPS, + SETUP_MAPS_RESTORED, + TEST_READY, + TEST_DONE, +}; + +struct vma_modifier_info; + +FIXTURE(proc_maps_race) +{ + struct vma_modifier_info *mod_info; + struct page_content page1; + struct page_content page2; + struct line_content last_line; + struct line_content first_line; + unsigned long duration_sec; + int shared_mem_size; + int page_size; + int vma_count; + bool verbose; + int maps_fd; + pid_t pid; +}; + +typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); +typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, + struct line_content *mod_first_line, + struct line_content *restored_last_line, + struct line_content *restored_first_line); + +struct vma_modifier_info { + int vma_count; + void *addr; + int prot; + void *next_addr; + vma_modifier_op vma_modify; + vma_modifier_op vma_restore; + vma_mod_result_check_op vma_mod_check; + pthread_mutex_t sync_lock; + pthread_cond_t sync_cond; + enum test_state curr_state; + bool exit; + void *child_mapped_addr[]; +}; + + +static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) +{ + ssize_t bytes_read; + + if (lseek(self->maps_fd, 0, SEEK_SET) < 0) + return false; + + bytes_read = read(self->maps_fd, self->page1.data, self->page_size); + if (bytes_read <= 0) + return false; + + self->page1.size = bytes_read; + + bytes_read = read(self->maps_fd, self->page2.data, self->page_size); + if (bytes_read <= 0) + return false; + + self->page2.size = bytes_read; + + return true; +} + +static void copy_first_line(struct page_content *page, char *first_line) +{ + char *pos = strchr(page->data, '\n'); + + strncpy(first_line, page->data, pos - page->data); + first_line[pos - page->data] = '\0'; +} + +static void copy_last_line(struct page_content *page, char *last_line) +{ + /* Get the last line in the first page */ + const char *end = page->data + page->size - 1; + /* skip last newline */ + const char *pos = end - 1; + + /* search previous newline */ + while (pos[-1] != '\n') + pos--; + strncpy(last_line, pos, end - pos); + last_line[end - pos] = '\0'; +} + +/* Read the last line of the first page and the first line of the second page */ +static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, + struct line_content *last_line, + struct line_content *first_line) +{ + if (!read_two_pages(self)) + return false; + + copy_last_line(&self->page1, last_line->text); + copy_first_line(&self->page2, first_line->text); + + return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, + &last_line->end_addr) == 2 && + sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, + &first_line->end_addr) == 2; +} + +/* Thread synchronization routines */ +static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) +{ + pthread_mutex_lock(&mod_info->sync_lock); + while (mod_info->curr_state != state) + pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); + pthread_mutex_unlock(&mod_info->sync_lock); +} + +static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) +{ + pthread_mutex_lock(&mod_info->sync_lock); + mod_info->curr_state = state; + pthread_cond_signal(&mod_info->sync_cond); + pthread_mutex_unlock(&mod_info->sync_lock); +} + +static void stop_vma_modifier(struct vma_modifier_info *mod_info) +{ + wait_for_state(mod_info, SETUP_READY); + mod_info->exit = true; + signal_state(mod_info, SETUP_MODIFY_MAPS); +} + +static void print_first_lines(char *text, int nr) +{ + const char *end = text; + + while (nr && (end = strchr(end, '\n')) != NULL) { + nr--; + end++; + } + + if (end) { + int offs = end - text; + + text[offs] = '\0'; + printf("%s", text); + text[offs] = '\n'; + printf("\n"); + } else { + printf("%s", text); + } +} + +static void print_last_lines(char *text, int nr) +{ + const char *start = text + strlen(text); + + nr++; /* to ignore the last newline */ + while (nr) { + while (start > text && *start != '\n') + start--; + nr--; + start--; + } + printf("%s", start); +} + +static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self) +{ + if (!self->verbose) + return; + + printf("%s", title); + /* Print 3 boundary lines from each page */ + print_last_lines(self->page1.data, 3); + printf("-----------------page boundary-----------------\n"); + print_first_lines(self->page2.data, 3); +} + +static bool print_boundaries_on(bool condition, const char *title, + FIXTURE_DATA(proc_maps_race) *self) +{ + if (self->verbose && condition) + print_boundaries(title, self); + + return condition; +} + +static void report_test_start(const char *name, bool verbose) +{ + if (verbose) + printf("==== %s ====\n", name); +} + +static struct timespec print_ts; + +static void start_test_loop(struct timespec *ts, bool verbose) +{ + if (verbose) + print_ts.tv_sec = ts->tv_sec; +} + +static void end_test_iteration(struct timespec *ts, bool verbose) +{ + if (!verbose) + return; + + /* Update every second */ + if (print_ts.tv_sec == ts->tv_sec) + return; + + printf("."); + fflush(stdout); + print_ts.tv_sec = ts->tv_sec; +} + +static void end_test_loop(bool verbose) +{ + if (verbose) + printf("\n"); +} + +static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, + struct line_content *mod_last_line, + struct line_content *mod_first_line, + struct line_content *restored_last_line, + struct line_content *restored_first_line) +{ + print_boundaries("Before modification", self); + + signal_state(self->mod_info, SETUP_MODIFY_MAPS); + wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); + + /* Copy last line of the first page and first line of the last page */ + if (!read_boundary_lines(self, mod_last_line, mod_first_line)) + return false; + + print_boundaries("After modification", self); + + signal_state(self->mod_info, SETUP_RESTORE_MAPS); + wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); + + /* Copy last line of the first page and first line of the last page */ + if (!read_boundary_lines(self, restored_last_line, restored_first_line)) + return false; + + print_boundaries("After restore", self); + + if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, + restored_last_line, restored_first_line)) + return false; + + /* + * The content of these lines after modify+resore should be the same + * as the original. + */ + return strcmp(restored_last_line->text, self->last_line.text) == 0 && + strcmp(restored_first_line->text, self->first_line.text) == 0; +} + +static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; +} + +static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; +} + +static inline bool check_split_result(struct line_content *mod_last_line, + struct line_content *mod_first_line, + struct line_content *restored_last_line, + struct line_content *restored_first_line) +{ + /* Make sure vmas at the boundaries are changing */ + return strcmp(mod_last_line->text, restored_last_line->text) != 0 && + strcmp(mod_first_line->text, restored_first_line->text) != 0; +} + +static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + return mremap(self->mod_info->addr, self->page_size * 3, + self->page_size, 0) != MAP_FAILED; +} + +static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + return mremap(self->mod_info->addr, self->page_size, + self->page_size * 3, 0) != MAP_FAILED; +} + +static inline bool check_shrink_result(struct line_content *mod_last_line, + struct line_content *mod_first_line, + struct line_content *restored_last_line, + struct line_content *restored_first_line) +{ + /* Make sure only the last vma of the first page is changing */ + return strcmp(mod_last_line->text, restored_last_line->text) != 0 && + strcmp(mod_first_line->text, restored_first_line->text) == 0; +} + +static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + /* + * Remap the last page of the next vma into the middle of the vma. + * This splits the current vma and the first and middle parts (the + * parts at lower addresses) become the last vma objserved in the + * first page and the first vma observed in the last page. + */ + return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, + self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, + self->mod_info->addr + self->page_size) != MAP_FAILED; +} + +static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) +{ + return mprotect(self->mod_info->addr + self->page_size, self->page_size, + self->mod_info->prot) == 0; +} + +static inline bool check_remap_result(struct line_content *mod_last_line, + struct line_content *mod_first_line, + struct line_content *restored_last_line, + struct line_content *restored_first_line) +{ + /* Make sure vmas at the boundaries are changing */ + return strcmp(mod_last_line->text, restored_last_line->text) != 0 && + strcmp(mod_first_line->text, restored_first_line->text) != 0; +} + +FIXTURE_SETUP(proc_maps_race) +{ + const char *verbose = getenv("VERBOSE"); + const char *duration = getenv("DURATION"); + struct vma_modifier_info *mod_info; + pthread_mutexattr_t mutex_attr; + pthread_condattr_t cond_attr; + unsigned long duration_sec; + char fname[32]; + + self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); + self->verbose = verbose && !strncmp(verbose, "1", 1); + duration_sec = duration ? atol(duration) : 0; + self->duration_sec = duration_sec ? duration_sec : 5UL; + + /* + * Have to map enough vmas for /proc/pid/maps to contain more than one + * page worth of vmas. Assume at least 32 bytes per line in maps output + */ + self->vma_count = self->page_size / 32 + 1; + self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); + + /* map shared memory for communication with the child process */ + self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(self->mod_info, MAP_FAILED); + mod_info = self->mod_info; + + /* Initialize shared members */ + pthread_mutexattr_init(&mutex_attr); + pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); + ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); + pthread_condattr_init(&cond_attr); + pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); + ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); + mod_info->vma_count = self->vma_count; + mod_info->curr_state = INIT; + mod_info->exit = false; + + self->pid = fork(); + if (!self->pid) { + /* Child process modifying the address space */ + int prot = PROT_READ | PROT_WRITE; + int i; + + for (i = 0; i < mod_info->vma_count; i++) { + mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); + /* change protection in adjacent maps to prevent merging */ + prot ^= PROT_WRITE; + } + signal_state(mod_info, CHILD_READY); + wait_for_state(mod_info, PARENT_READY); + while (true) { + signal_state(mod_info, SETUP_READY); + wait_for_state(mod_info, SETUP_MODIFY_MAPS); + if (mod_info->exit) + break; + + ASSERT_TRUE(mod_info->vma_modify(self)); + signal_state(mod_info, SETUP_MAPS_MODIFIED); + wait_for_state(mod_info, SETUP_RESTORE_MAPS); + ASSERT_TRUE(mod_info->vma_restore(self)); + signal_state(mod_info, SETUP_MAPS_RESTORED); + + wait_for_state(mod_info, TEST_READY); + while (mod_info->curr_state != TEST_DONE) { + ASSERT_TRUE(mod_info->vma_modify(self)); + ASSERT_TRUE(mod_info->vma_restore(self)); + } + } + for (i = 0; i < mod_info->vma_count; i++) + munmap(mod_info->child_mapped_addr[i], self->page_size * 3); + + exit(0); + } + + sprintf(fname, "/proc/%d/maps", self->pid); + self->maps_fd = open(fname, O_RDONLY); + ASSERT_NE(self->maps_fd, -1); + + /* Wait for the child to map the VMAs */ + wait_for_state(mod_info, CHILD_READY); + + /* Read first two pages */ + self->page1.data = malloc(self->page_size); + ASSERT_NE(self->page1.data, NULL); + self->page2.data = malloc(self->page_size); + ASSERT_NE(self->page2.data, NULL); + + ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); + + /* + * Find the addresses corresponding to the last line in the first page + * and the first line in the last page. + */ + mod_info->addr = NULL; + mod_info->next_addr = NULL; + for (int i = 0; i < mod_info->vma_count; i++) { + if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { + mod_info->addr = mod_info->child_mapped_addr[i]; + mod_info->prot = PROT_READ; + /* Even VMAs have write permission */ + if ((i % 2) == 0) + mod_info->prot |= PROT_WRITE; + } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { + mod_info->next_addr = mod_info->child_mapped_addr[i]; + } + + if (mod_info->addr && mod_info->next_addr) + break; + } + ASSERT_TRUE(mod_info->addr && mod_info->next_addr); + + signal_state(mod_info, PARENT_READY); + +} + +FIXTURE_TEARDOWN(proc_maps_race) +{ + int status; + + stop_vma_modifier(self->mod_info); + + free(self->page2.data); + free(self->page1.data); + + for (int i = 0; i < self->vma_count; i++) + munmap(self->mod_info->child_mapped_addr[i], self->page_size); + close(self->maps_fd); + waitpid(self->pid, &status, 0); + munmap(self->mod_info, self->shared_mem_size); +} + +TEST_F(proc_maps_race, test_maps_tearing_from_split) +{ + struct vma_modifier_info *mod_info = self->mod_info; + + struct line_content split_last_line; + struct line_content split_first_line; + struct line_content restored_last_line; + struct line_content restored_first_line; + + wait_for_state(mod_info, SETUP_READY); + + /* re-read the file to avoid using stale data from previous test */ + ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); + + mod_info->vma_modify = split_vma; + mod_info->vma_restore = merge_vma; + mod_info->vma_mod_check = check_split_result; + + report_test_start("Tearing from split", self->verbose); + ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, + &restored_last_line, &restored_first_line)); + + /* Now start concurrent modifications for self->duration_sec */ + signal_state(mod_info, TEST_READY); + + struct line_content new_last_line; + struct line_content new_first_line; + struct timespec start_ts, end_ts; + + clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); + start_test_loop(&start_ts, self->verbose); + do { + bool last_line_changed; + bool first_line_changed; + + ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); + + /* Check if we read vmas after split */ + if (!strcmp(new_last_line.text, split_last_line.text)) { + /* + * The vmas should be consistent with split results, + * however if vma was concurrently restored after a + * split, it can be reported twice (first the original + * split one, then the same vma but extended after the + * merge) because we found it as the next vma again. + * In that case new first line will be the same as the + * last restored line. + */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, split_first_line.text) && + strcmp(new_first_line.text, restored_last_line.text), + "Split result invalid", self)); + } else { + /* The vmas should be consistent with merge results */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_last_line.text, restored_last_line.text), + "Merge result invalid", self)); + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, restored_first_line.text), + "Merge result invalid", self)); + } + /* + * First and last lines should change in unison. If the last + * line changed then the first line should change as well and + * vice versa. + */ + last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; + first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; + ASSERT_EQ(last_line_changed, first_line_changed); + + clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); + end_test_iteration(&end_ts, self->verbose); + } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); + end_test_loop(self->verbose); + + /* Signal the modifyer thread to stop and wait until it exits */ + signal_state(mod_info, TEST_DONE); +} + +TEST_F(proc_maps_race, test_maps_tearing_from_resize) +{ + struct vma_modifier_info *mod_info = self->mod_info; + + struct line_content shrunk_last_line; + struct line_content shrunk_first_line; + struct line_content restored_last_line; + struct line_content restored_first_line; + + wait_for_state(mod_info, SETUP_READY); + + /* re-read the file to avoid using stale data from previous test */ + ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); + + mod_info->vma_modify = shrink_vma; + mod_info->vma_restore = expand_vma; + mod_info->vma_mod_check = check_shrink_result; + + report_test_start("Tearing from resize", self->verbose); + ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, + &restored_last_line, &restored_first_line)); + + /* Now start concurrent modifications for self->duration_sec */ + signal_state(mod_info, TEST_READY); + + struct line_content new_last_line; + struct line_content new_first_line; + struct timespec start_ts, end_ts; + + clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); + start_test_loop(&start_ts, self->verbose); + do { + ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); + + /* Check if we read vmas after shrinking it */ + if (!strcmp(new_last_line.text, shrunk_last_line.text)) { + /* + * The vmas should be consistent with shrunk results, + * however if the vma was concurrently restored, it + * can be reported twice (first as shrunk one, then + * as restored one) because we found it as the next vma + * again. In that case new first line will be the same + * as the last restored line. + */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, shrunk_first_line.text) && + strcmp(new_first_line.text, restored_last_line.text), + "Shrink result invalid", self)); + } else { + /* The vmas should be consistent with the original/resored state */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_last_line.text, restored_last_line.text), + "Expand result invalid", self)); + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, restored_first_line.text), + "Expand result invalid", self)); + } + + clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); + end_test_iteration(&end_ts, self->verbose); + } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); + end_test_loop(self->verbose); + + /* Signal the modifyer thread to stop and wait until it exits */ + signal_state(mod_info, TEST_DONE); +} + +TEST_F(proc_maps_race, test_maps_tearing_from_remap) +{ + struct vma_modifier_info *mod_info = self->mod_info; + + struct line_content remapped_last_line; + struct line_content remapped_first_line; + struct line_content restored_last_line; + struct line_content restored_first_line; + + wait_for_state(mod_info, SETUP_READY); + + /* re-read the file to avoid using stale data from previous test */ + ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); + + mod_info->vma_modify = remap_vma; + mod_info->vma_restore = patch_vma; + mod_info->vma_mod_check = check_remap_result; + + report_test_start("Tearing from remap", self->verbose); + ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, + &restored_last_line, &restored_first_line)); + + /* Now start concurrent modifications for self->duration_sec */ + signal_state(mod_info, TEST_READY); + + struct line_content new_last_line; + struct line_content new_first_line; + struct timespec start_ts, end_ts; + + clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); + start_test_loop(&start_ts, self->verbose); + do { + ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); + + /* Check if we read vmas after remapping it */ + if (!strcmp(new_last_line.text, remapped_last_line.text)) { + /* + * The vmas should be consistent with remap results, + * however if the vma was concurrently restored, it + * can be reported twice (first as split one, then + * as restored one) because we found it as the next vma + * again. In that case new first line will be the same + * as the last restored line. + */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, remapped_first_line.text) && + strcmp(new_first_line.text, restored_last_line.text), + "Remap result invalid", self)); + } else { + /* The vmas should be consistent with the original/resored state */ + ASSERT_FALSE(print_boundaries_on( + strcmp(new_last_line.text, restored_last_line.text), + "Remap restore result invalid", self)); + ASSERT_FALSE(print_boundaries_on( + strcmp(new_first_line.text, restored_first_line.text), + "Remap restore result invalid", self)); + } + + clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); + end_test_iteration(&end_ts, self->verbose); + } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); + end_test_loop(self->verbose); + + /* Signal the modifyer thread to stop and wait until it exits */ + signal_state(mod_info, TEST_DONE); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c index edc08a4433fd..ed1e2886ba3c 100644 --- a/tools/testing/selftests/ptp/testptp.c +++ b/tools/testing/selftests/ptp/testptp.c @@ -120,6 +120,7 @@ static void usage(char *progname) " -c query the ptp clock's capabilities\n" " -d name device to open\n" " -e val read 'val' external time stamp events\n" + " -E val enable rising (1), falling (2), or both (3) edges\n" " -f val adjust the ptp clock frequency by 'val' ppb\n" " -F chan Enable single channel mask and keep device open for debugfs verification.\n" " -g get the ptp clock time\n" @@ -178,6 +179,7 @@ int main(int argc, char *argv[]) int adjphase = 0; int capabilities = 0; int extts = 0; + int edge = 0; int flagtest = 0; int gettime = 0; int index = 0; @@ -202,7 +204,7 @@ int main(int argc, char *argv[]) progname = strrchr(argv[0], '/'); progname = progname ? 1+progname : argv[0]; - while (EOF != (c = getopt(argc, argv, "cd:e:f:F:ghH:i:k:lL:n:o:p:P:rsSt:T:w:x:Xy:z"))) { + while (EOF != (c = getopt(argc, argv, "cd:e:E:f:F:ghH:i:k:lL:n:o:p:P:rsSt:T:w:x:Xy:z"))) { switch (c) { case 'c': capabilities = 1; @@ -213,6 +215,11 @@ int main(int argc, char *argv[]) case 'e': extts = atoi(optarg); break; + case 'E': + edge = atoi(optarg); + edge = (edge & 1 ? PTP_RISING_EDGE : 0) | + (edge & 2 ? PTP_FALLING_EDGE : 0); + break; case 'f': adjfreq = atoi(optarg); break; @@ -444,7 +451,7 @@ int main(int argc, char *argv[]) if (!readonly) { memset(&extts_request, 0, sizeof(extts_request)); extts_request.index = index; - extts_request.flags = PTP_ENABLE_FEATURE; + extts_request.flags = PTP_ENABLE_FEATURE | edge; if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { perror("PTP_EXTTS_REQUEST"); extts = 0; diff --git a/tools/testing/selftests/ptrace/.gitignore b/tools/testing/selftests/ptrace/.gitignore index b7dde152e75a..f6be8efd57ea 100644 --- a/tools/testing/selftests/ptrace/.gitignore +++ b/tools/testing/selftests/ptrace/.gitignore @@ -3,3 +3,4 @@ get_syscall_info get_set_sud peeksiginfo vmaccess +set_syscall_info diff --git a/tools/testing/selftests/ptrace/peeksiginfo.c b/tools/testing/selftests/ptrace/peeksiginfo.c index a6884f66dc01..2f345d11e4b8 100644 --- a/tools/testing/selftests/ptrace/peeksiginfo.c +++ b/tools/testing/selftests/ptrace/peeksiginfo.c @@ -199,7 +199,7 @@ int main(int argc, char *argv[]) /* * Dump signal from the process-wide queue. - * The number of signals is not multible to the buffer size + * The number of signals is not multiple to the buffer size */ if (check_direct_path(child, 1, 3)) goto out; diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh index 11f8d232b0ee..3edfd064ef81 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh @@ -44,7 +44,7 @@ fi ncpus="`getconf _NPROCESSORS_ONLN`" make -j$((2 * ncpus)) $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1 retval=$? -if test $retval -ne 0 || grep "rcu[^/]*": < $resdir/Make.out | grep -E -q "Stop|Error|error:|warning:" || grep -E -q "Stop|Error|error:" < $resdir/Make.out +if test $retval -ne 0 || grep "rcu[^/]*": < $resdir/Make.out | grep -E -q "Stop|ERROR|Error|error:|warning:" || grep -E -q "Stop|ERROR|Error|error:" < $resdir/Make.out then echo Kernel build error grep -E "Stop|Error|error:|warning:" < $resdir/Make.out diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 42e5e8597a1a..617cba339d28 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -199,7 +199,7 @@ do fi ;; --kconfig|--kconfigs) - checkarg --kconfig "(Kconfig options)" $# "$2" '^\(#CHECK#\)\?CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\|"[^"]*"\)\( \(#CHECK#\)\?CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\|"[^"]*"\)\)*$' '^error$' + checkarg --kconfig "(Kconfig options)" $# "$2" '^\(#CHECK#\)\?CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\|"[^"]*"\)\( \+\(#CHECK#\)\?CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\|"[^"]*"\)\)* *$' '^error$' TORTURE_KCONFIG_ARG="`echo "$TORTURE_KCONFIG_ARG $2" | sed -e 's/^ *//' -e 's/ *$//'`" shift ;; @@ -442,18 +442,7 @@ echo $scriptname $args touch $resdir/$ds/log echo $scriptname $args >> $resdir/$ds/log echo ${TORTURE_SUITE} > $resdir/$ds/torture_suite -echo Build directory: `pwd` > $resdir/$ds/testid.txt -if test -d .git -then - echo Current commit: `git rev-parse HEAD` >> $resdir/$ds/testid.txt - echo >> $resdir/$ds/testid.txt - echo ' ---' Output of "'"git status"'": >> $resdir/$ds/testid.txt - git status >> $resdir/$ds/testid.txt - echo >> $resdir/$ds/testid.txt - echo >> $resdir/$ds/testid.txt - echo ' ---' Output of "'"git diff HEAD"'": >> $resdir/$ds/testid.txt - git diff HEAD >> $resdir/$ds/testid.txt -fi +mktestid.sh $resdir/$ds ___EOF___ kvm-assign-cpus.sh /sys/devices/system/node > $T/cpuarray.awk kvm-get-cpus-script.sh $T/cpuarray.awk $T/dumpbatches.awk diff --git a/tools/testing/selftests/rcutorture/bin/mktestid.sh b/tools/testing/selftests/rcutorture/bin/mktestid.sh new file mode 100755 index 000000000000..16f9907a4dae --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/mktestid.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Create a testid.txt file in the specified directory. +# +# Usage: mktestid.sh dirpath +# +# Copyright (C) Meta Platforms, Inc. 2025 +# +# Author: Paul E. McKenney + +resdir="$1" +if test -z "${resdir}" || ! test -d "${resdir}" || ! test -w "${resdir}" +then + echo Path '"'${resdir}'"' not writeable directory, no ${resdir}/testid.txt. + exit 1 +fi +echo Build directory: `pwd` > ${resdir}/testid.txt +if test -d .git +then + echo Current commit: `git rev-parse HEAD` >> ${resdir}/testid.txt + echo >> ${resdir}/testid.txt + echo ' ---' Output of "'"git status"'": >> ${resdir}/testid.txt + git status >> ${resdir}/testid.txt + echo >> ${resdir}/testid.txt + echo >> ${resdir}/testid.txt + echo ' ---' Output of "'"git diff HEAD"'": >> ${resdir}/testid.txt + git diff HEAD >> ${resdir}/testid.txt +fi diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index e03fdaca89b3..611bc03a8dc7 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -30,6 +30,15 @@ then VERBOSE_BATCH_CPUS=0 fi +# Machine architecture? ("uname -p" is said to be less portable.)1 +thisarch="`uname -m`" +if test "${thisarch}" = aarch64 +then + ifnotaarch64=no +else + ifnotaarch64=yes +fi + # Configurations/scenarios. configs_rcutorture= configs_locktorture= @@ -55,9 +64,9 @@ do_normal=yes explicit_normal=no do_kasan=yes do_kcsan=no -do_clocksourcewd=yes +do_clocksourcewd="${ifnotaarch64}" do_rt=yes -do_rcutasksflavors=yes +do_rcutasksflavors="${ifnotaarch64}" # FIXME: Back to "yes" when SMP=n auto-avoided do_srcu_lockdep=yes do_rcu_rust=no @@ -124,7 +133,7 @@ do ;; --do-all|--doall) do_allmodconfig=yes - do_rcutasksflavor=yes + do_rcutasksflavors="${ifnotaarch64}" # FIXME: Back to "yes" when SMP=n auto-avoided do_rcutorture=yes do_locktorture=yes do_scftorture=yes @@ -136,7 +145,7 @@ do explicit_normal=no do_kasan=yes do_kcsan=yes - do_clocksourcewd=yes + do_clocksourcewd="${ifnotaarch64}" do_srcu_lockdep=yes ;; --do-allmodconfig|--do-no-allmodconfig|--no-allmodconfig) @@ -274,7 +283,7 @@ then configs_rcutorture=CFLIST fi duration_rcutorture=$((duration_base*duration_rcutorture_frac/10)) -if test "$duration_rcutorture" -eq 0 +if test "$duration_rcutorture" -eq 0 && test "$do_locktorture" = "yes" then echo " --- Zero time for rcutorture, disabling" | tee -a $T/log do_rcutorture=no @@ -286,7 +295,7 @@ then configs_locktorture=CFLIST fi duration_locktorture=$((duration_base*duration_locktorture_frac/10)) -if test "$duration_locktorture" -eq 0 +if test "$duration_locktorture" -eq 0 && test "$do_locktorture" = "yes" then echo " --- Zero time for locktorture, disabling" | tee -a $T/log do_locktorture=no @@ -298,12 +307,19 @@ then configs_scftorture=CFLIST fi duration_scftorture=$((duration_base*duration_scftorture_frac/10)) -if test "$duration_scftorture" -eq 0 +if test "$duration_scftorture" -eq 0 && test "$do_scftorture" = "yes" then echo " --- Zero time for scftorture, disabling" | tee -a $T/log do_scftorture=no fi +# CONFIG_EXPERT=y is currently required for arm64 KCSAN runs. +kcsan_expert= +if test "${thisarch}" = aarch64 +then + kcsan_expert="CONFIG_EXPERT=y" +fi + touch $T/failures touch $T/successes @@ -362,13 +378,19 @@ function torture_set { then curflavor=$flavor torture_one "$@" - mv $T/last-resdir $T/last-resdir-nodebug || : + if test -e $T/last-resdir + then + mv $T/last-resdir $T/last-resdir-nodebug || : + fi fi if test "$do_kasan" = "yes" then curflavor=${flavor}-kasan torture_one "$@" --kasan - mv $T/last-resdir $T/last-resdir-kasan || : + if test -e $T/last-resdir + then + mv $T/last-resdir $T/last-resdir-kasan || : + fi fi if test "$do_kcsan" = "yes" then @@ -378,8 +400,16 @@ function torture_set { kcsan_kmake_tag="--kmake-args" cur_kcsan_kmake_args="$kcsan_kmake_args" fi - torture_one "$@" --kconfig "CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y" $kcsan_kmake_tag $cur_kcsan_kmake_args --kcsan - mv $T/last-resdir $T/last-resdir-kcsan || : + chk_rdr_state= + if test "${flavor}" = rcutorture + then + chk_rdr_state="CONFIG_RCU_TORTURE_TEST_CHK_RDR_STATE=y" + fi + torture_one "$@" --kconfig "CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y ${kcsan_expert} ${chk_rdr_state}" $kcsan_kmake_tag $cur_kcsan_kmake_args --kcsan + if test -e $T/last-resdir + then + mv $T/last-resdir $T/last-resdir-kcsan || : + fi fi } @@ -389,6 +419,7 @@ then echo " --- allmodconfig:" Start `date` | tee -a $T/log amcdir="tools/testing/selftests/rcutorture/res/$ds/allmodconfig" mkdir -p "$amcdir" + mktestid.sh "$amcdir" echo " --- make clean" | tee $amcdir/log > "$amcdir/Make.out" 2>&1 make -j$MAKE_ALLOTED_CPUS clean >> "$amcdir/Make.out" 2>&1 retcode=$? @@ -407,6 +438,10 @@ then make -j$MAKE_ALLOTED_CPUS >> "$amcdir/Make.out" 2>&1 retcode="$?" echo $retcode > "$amcdir/Make.exitcode" + if grep -E -q "Stop|ERROR|Error|error:|warning:" < "$amcdir/Make.out" + then + retcode=99 + fi buildphase='"make"' fi if test "$retcode" -eq 0 @@ -495,6 +530,7 @@ then echo " --- do-rcu-rust:" Start `date` | tee -a $T/log rrdir="tools/testing/selftests/rcutorture/res/$ds/results-rcu-rust" mkdir -p "$rrdir" + mktestid.sh "$rrdir" echo " --- make LLVM=1 rustavailable " | tee -a $rrdir/log > $rrdir/rustavailable.out make LLVM=1 rustavailable > $T/rustavailable.out 2>&1 retcode=$? @@ -681,7 +717,14 @@ nfailures=0 echo FAILURES: | tee -a $T/log if test -s "$T/failures" then - awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "sed -e " sq "1,/^ --- .* Test summary:$/d" sq " " $2 "/log | grep Summary: | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log | tee "$T/failuresum" + awk < "$T/failures" -v sq="'" ' + { + print "echo " sq $0 sq; + if ($2 != "") + print "sed -e " sq "1,/^ --- .* Test summary:$/d" sq " " $2 "/log | grep Summary: | sed -e " sq "s/^[^S]*/ /" sq; + else + print "echo " sq " " sq "Run failed to produce results directory."; + }' | sh | tee -a $T/log | tee "$T/failuresum" nfailures="`wc -l "$T/failures" | awk '{ print $1 }'`" grep "^ Summary: " "$T/failuresum" | grep -v '^ Summary: Bugs: [0-9]* (all bugs kcsan)$' > "$T/nonkcsan" @@ -691,15 +734,18 @@ then fi ret=2 fi -if test "$do_kcsan" = "yes" +if test "$do_kcsan" = "yes" && test -e tools/testing/selftests/rcutorture/res/$ds then TORTURE_KCONFIG_KCSAN_ARG=1 tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh tools/testing/selftests/rcutorture/res/$ds > tools/testing/selftests/rcutorture/res/$ds/kcsan.sum fi echo Started at $startdate, ended at `date`, duration `get_starttime_duration $starttime`. | tee -a $T/log echo Summary: Successes: $nsuccesses Failures: $nfailures. | tee -a $T/log -tdir="`cat $T/successes $T/failures | head -1 | awk '{ print $NF }' | sed -e 's,/[^/]\+/*$,,'`" -find "$tdir" -name 'ConfigFragment.diags' -print > $T/configerrors -find "$tdir" -name 'Make.out.diags' -print > $T/builderrors +tdir="`cat $T/successes $T/failures | awk 'NF > 1 { print $NF }' | head -1 | sed -e 's,/[^/]\+/*$,,'`" +if test -n "$tdir" +then + find "$tdir" -name 'ConfigFragment.diags' -print > $T/configerrors + find "$tdir" -name 'Make.out.diags' -print > $T/builderrors +fi if test -s "$T/configerrors" then echo " Scenarios with .config errors: `wc -l "$T/configerrors" | awk '{ print $1 }'`" diff --git a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED index 48d8a245c7fa..7d75f4b94943 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED +++ b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED @@ -5,3 +5,6 @@ CONFIG_HOTPLUG_CPU=y CONFIG_PREEMPT_NONE=n CONFIG_PREEMPT_VOLUNTARY=n CONFIG_PREEMPT=y +CONFIG_RCU_TORTURE_TEST_CHK_RDR_STATE=y +CONFIG_RCU_TORTURE_TEST_LOG_CPU=y +CONFIG_RCU_TORTURE_TEST_LOG_GP=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST index 45f572570a8c..98b6175e5aa0 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST +++ b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST @@ -5,7 +5,6 @@ TREE04 TREE05 TREE07 TREE09 -SRCU-L SRCU-N SRCU-P SRCU-T diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L deleted file mode 100644 index 3b4fa8dbef8a..000000000000 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG_RCU_TRACE=n -CONFIG_SMP=y -CONFIG_NR_CPUS=6 -CONFIG_HOTPLUG_CPU=y -CONFIG_PREEMPT_NONE=y -CONFIG_PREEMPT_VOLUNTARY=n -CONFIG_PREEMPT=n -#CHECK#CONFIG_RCU_EXPERT=n -CONFIG_KPROBES=n -CONFIG_FTRACE=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L.boot deleted file mode 100644 index 0207b3138c5b..000000000000 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-L.boot +++ /dev/null @@ -1,3 +0,0 @@ -rcutorture.torture_type=srcu -rcutorture.reader_flavor=0x4 -rcutorture.fwd_progress=3 diff --git a/tools/testing/selftests/sched_ext/exit.c b/tools/testing/selftests/sched_ext/exit.c index 9451782689de..ee25824b1cbe 100644 --- a/tools/testing/selftests/sched_ext/exit.c +++ b/tools/testing/selftests/sched_ext/exit.c @@ -22,6 +22,14 @@ static enum scx_test_status run(void *ctx) struct bpf_link *link; char buf[16]; + /* + * On single-CPU systems, ops.select_cpu() is never + * invoked, so skip this test to avoid getting stuck + * indefinitely. + */ + if (tc == EXIT_SELECT_CPU && libbpf_num_possible_cpus() == 1) + continue; + skel = exit__open(); SCX_ENUM_INIT(skel); skel->rodata->exit_point = tc; diff --git a/tools/testing/selftests/sched_ext/hotplug.c b/tools/testing/selftests/sched_ext/hotplug.c index 1c9ceb661c43..0cfbb111a2d0 100644 --- a/tools/testing/selftests/sched_ext/hotplug.c +++ b/tools/testing/selftests/sched_ext/hotplug.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/tools/testing/selftests/sched_ext/maximal.bpf.c b/tools/testing/selftests/sched_ext/maximal.bpf.c index 430f5e13bf55..01cf4f3da4e0 100644 --- a/tools/testing/selftests/sched_ext/maximal.bpf.c +++ b/tools/testing/selftests/sched_ext/maximal.bpf.c @@ -123,6 +123,10 @@ void BPF_STRUCT_OPS(maximal_cgroup_cancel_move, struct task_struct *p, void BPF_STRUCT_OPS(maximal_cgroup_set_weight, struct cgroup *cgrp, u32 weight) {} +void BPF_STRUCT_OPS(maximal_cgroup_set_bandwidth, struct cgroup *cgrp, + u64 period_us, u64 quota_us, u64 burst_us) +{} + s32 BPF_STRUCT_OPS_SLEEPABLE(maximal_init) { return scx_bpf_create_dsq(DSQ_ID, -1); @@ -160,6 +164,7 @@ struct sched_ext_ops maximal_ops = { .cgroup_move = (void *) maximal_cgroup_move, .cgroup_cancel_move = (void *) maximal_cgroup_cancel_move, .cgroup_set_weight = (void *) maximal_cgroup_set_weight, + .cgroup_set_bandwidth = (void *) maximal_cgroup_set_bandwidth, .init = (void *) maximal_init, .exit = (void *) maximal_exit, .name = "maximal", diff --git a/tools/testing/selftests/syscall_user_dispatch/sud_test.c b/tools/testing/selftests/syscall_user_dispatch/sud_test.c index d975a6767329..2eb2c06303f2 100644 --- a/tools/testing/selftests/syscall_user_dispatch/sud_test.c +++ b/tools/testing/selftests/syscall_user_dispatch/sud_test.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "../kselftest_harness.h" @@ -17,11 +19,15 @@ #ifndef PR_SET_SYSCALL_USER_DISPATCH # define PR_SET_SYSCALL_USER_DISPATCH 59 # define PR_SYS_DISPATCH_OFF 0 -# define PR_SYS_DISPATCH_ON 1 # define SYSCALL_DISPATCH_FILTER_ALLOW 0 # define SYSCALL_DISPATCH_FILTER_BLOCK 1 #endif +#ifndef PR_SYS_DISPATCH_EXCLUSIVE_ON +# define PR_SYS_DISPATCH_EXCLUSIVE_ON 1 +# define PR_SYS_DISPATCH_INCLUSIVE_ON 2 +#endif + #ifndef SYS_USER_DISPATCH # define SYS_USER_DISPATCH 2 #endif @@ -65,7 +71,7 @@ TEST_SIGNAL(dispatch_trigger_sigsys, SIGSYS) ret = sysinfo(&info); ASSERT_EQ(0, ret); - ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel); + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel); ASSERT_EQ(0, ret) { TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH"); } @@ -79,6 +85,21 @@ TEST_SIGNAL(dispatch_trigger_sigsys, SIGSYS) } } +static void prctl_valid(struct __test_metadata *_metadata, + unsigned long op, unsigned long off, + unsigned long size, void *sel) +{ + EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel)); +} + +static void prctl_invalid(struct __test_metadata *_metadata, + unsigned long op, unsigned long off, + unsigned long size, void *sel, int err) +{ + EXPECT_EQ(-1, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel)); + EXPECT_EQ(err, errno); +} + TEST(bad_prctl_param) { char sel = SYSCALL_DISPATCH_FILTER_ALLOW; @@ -86,57 +107,54 @@ TEST(bad_prctl_param) /* Invalid op */ op = -1; - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0, 0, &sel); - ASSERT_EQ(EINVAL, errno); + prctl_invalid(_metadata, op, 0, 0, &sel, EINVAL); /* PR_SYS_DISPATCH_OFF */ op = PR_SYS_DISPATCH_OFF; /* offset != 0 */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x1, 0x0, 0); - EXPECT_EQ(EINVAL, errno); + prctl_invalid(_metadata, op, 0x1, 0x0, 0, EINVAL); /* len != 0 */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0xff, 0); - EXPECT_EQ(EINVAL, errno); + prctl_invalid(_metadata, op, 0x0, 0xff, 0, EINVAL); /* sel != NULL */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x0, &sel); - EXPECT_EQ(EINVAL, errno); + prctl_invalid(_metadata, op, 0x0, 0x0, &sel, EINVAL); /* Valid parameter */ - errno = 0; - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x0, 0x0); - EXPECT_EQ(0, errno); + prctl_valid(_metadata, op, 0x0, 0x0, 0x0); - /* PR_SYS_DISPATCH_ON */ - op = PR_SYS_DISPATCH_ON; + /* PR_SYS_DISPATCH_EXCLUSIVE_ON */ + op = PR_SYS_DISPATCH_EXCLUSIVE_ON; /* Dispatcher region is bad (offset > 0 && len == 0) */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x1, 0x0, &sel); - EXPECT_EQ(EINVAL, errno); - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, -1L, 0x0, &sel); - EXPECT_EQ(EINVAL, errno); + prctl_invalid(_metadata, op, 0x1, 0x0, &sel, EINVAL); + prctl_invalid(_metadata, op, -1L, 0x0, &sel, EINVAL); /* Invalid selector */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x1, (void *) -1); - ASSERT_EQ(EFAULT, errno); + prctl_invalid(_metadata, op, 0x0, 0x1, (void *) -1, EFAULT); /* * Dispatcher range overflows unsigned long */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 1, -1L, &sel); - ASSERT_EQ(EINVAL, errno) { - TH_LOG("Should reject bad syscall range"); - } + prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 1, -1L, &sel, EINVAL); /* * Allowed range overflows usigned long */ - prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, -1L, 0x1, &sel); - ASSERT_EQ(EINVAL, errno) { - TH_LOG("Should reject bad syscall range"); - } + prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, -1L, 0x1, &sel, EINVAL); + + /* 0 len should fail for PR_SYS_DISPATCH_INCLUSIVE_ON */ + prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 1, 0, 0, EINVAL); + + /* Range wrap-around should fail */ + prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, -1L, 2, 0, EINVAL); + + /* Normal range shouldn't fail */ + prctl_valid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, 0); + + /* Invalid selector */ + prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, (void *) -1, EFAULT); } /* @@ -147,11 +165,13 @@ char glob_sel; int nr_syscalls_emulated; int si_code; int si_errno; +unsigned long syscall_addr; static void handle_sigsys(int sig, siginfo_t *info, void *ucontext) { si_code = info->si_code; si_errno = info->si_errno; + syscall_addr = (unsigned long)info->si_call_addr; if (info->si_syscall == MAGIC_SYSCALL_1) nr_syscalls_emulated++; @@ -174,31 +194,34 @@ static void handle_sigsys(int sig, siginfo_t *info, void *ucontext) #endif } +int setup_sigsys_handler(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + act.sa_sigaction = handle_sigsys; + act.sa_flags = SA_SIGINFO; + act.sa_mask = mask; + return sigaction(SIGSYS, &act, NULL); +} + TEST(dispatch_and_return) { long ret; - struct sigaction act; - sigset_t mask; glob_sel = 0; nr_syscalls_emulated = 0; si_code = 0; si_errno = 0; - memset(&act, 0, sizeof(act)); - sigemptyset(&mask); - - act.sa_sigaction = handle_sigsys; - act.sa_flags = SA_SIGINFO; - act.sa_mask = mask; - - ret = sigaction(SIGSYS, &act, NULL); - ASSERT_EQ(0, ret); + ASSERT_EQ(0, setup_sigsys_handler()); /* Make sure selector is good prior to prctl. */ SYSCALL_DISPATCH_OFF(glob_sel); - ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel); + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel); ASSERT_EQ(0, ret) { TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH"); } @@ -254,7 +277,7 @@ TEST_SIGNAL(bad_selector, SIGSYS) /* Make sure selector is good prior to prctl. */ SYSCALL_DISPATCH_OFF(glob_sel); - ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel); + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel); ASSERT_EQ(0, ret) { TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH"); } @@ -278,7 +301,7 @@ TEST(disable_dispatch) struct sysinfo info; char sel = 0; - ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel); + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel); ASSERT_EQ(0, ret) { TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH"); } @@ -310,7 +333,7 @@ TEST(direct_dispatch_range) * Instead of calculating libc addresses; allow the entire * memory map and lock the selector. */ - ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, -1L, &sel); + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, -1L, &sel); ASSERT_EQ(0, ret) { TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH"); } @@ -323,4 +346,35 @@ TEST(direct_dispatch_range) } } +static void test_range(struct __test_metadata *_metadata, + unsigned long op, unsigned long off, + unsigned long size, bool dispatch) +{ + nr_syscalls_emulated = 0; + SYSCALL_DISPATCH_OFF(glob_sel); + EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, &glob_sel)); + SYSCALL_DISPATCH_ON(glob_sel); + if (dispatch) { + EXPECT_EQ(syscall(MAGIC_SYSCALL_1), MAGIC_SYSCALL_1); + EXPECT_EQ(nr_syscalls_emulated, 1); + } else { + EXPECT_EQ(syscall(MAGIC_SYSCALL_1), -1); + EXPECT_EQ(nr_syscalls_emulated, 0); + } +} + +TEST(dispatch_range) +{ + ASSERT_EQ(0, setup_sigsys_handler()); + test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, true); + test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr, 1, false); + test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 200, false); + test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr+1, 100, true); + test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 100, true); + test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr, 1, true); + test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr-1, 1, false); + test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr+1, 1, false); + SYSCALL_DISPATCH_OFF(glob_sel); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index a10350c8a46e..b2d8bd9026a7 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 # Copyright (C) 2017 Luis R. Rodriguez diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config index db176fe7d0c3..c20aa16b1d63 100644 --- a/tools/testing/selftests/tc-testing/config +++ b/tools/testing/selftests/tc-testing/config @@ -21,6 +21,7 @@ CONFIG_NF_NAT=m CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NET_SCHED=y +CONFIG_IP_SET=m # # Queueing/Scheduling @@ -30,6 +31,7 @@ CONFIG_NET_SCH_CBS=m CONFIG_NET_SCH_CHOKE=m CONFIG_NET_SCH_CODEL=m CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_DUALPI2=m CONFIG_NET_SCH_ETF=m CONFIG_NET_SCH_FQ=m CONFIG_NET_SCH_FQ_CODEL=m 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 9aa44d8176d9..998e5a2f4579 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -127,6 +127,32 @@ "$IP addr del 10.10.10.10/24 dev $DUMMY" ] }, + { + "id": "5456", + "name": "Test htb_dequeue_tree with deactivation and row emptying", + "category": [ + "qdisc", + "htb" + ], + "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: htb default 1", + "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 64bit ", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: netem", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: blackhole" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.11.11", + "expExitCode": "1", + "verifyCmd": "$TC -j qdisc show dev $DUMMY", + "matchJSON": [], + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + }, { "id": "c024", "name": "Test TBF with SKBPRIO - catch qlen corner cases", @@ -159,6 +185,204 @@ "$IP addr del 10.10.10.10/24 dev $DUMMY || true" ] }, + { + "id": "34c0", + "name": "Test TBF with HHF Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "hhf" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 hhf limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 hhf limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "fd68", + "name": "Test TBF with CODEL Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "codel" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 codel limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 codel limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "514e", + "name": "Test TBF with PIE Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "pie" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 pie limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 pie limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "6c97", + "name": "Test TBF with FQ Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "fq" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 fq limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 fq limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "5d0b", + "name": "Test TBF with FQ_CODEL Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "fq_codel" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 fq_codel limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 fq_codel limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "21c3", + "name": "Test TBF with FQ_PIE Backlog Accounting in gso_skb case against underflow", + "category": [ + "qdisc", + "tbf", + "fq_pie" + ], + "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 8bit burst 100b latency 100ms", + "$TC qdisc replace dev $DUMMY handle 2: parent 1:1 fq_pie limit 1000", + [ + "ping -I $DUMMY -c2 10.10.11.11", + 1 + ], + "$TC qdisc change dev $DUMMY handle 2: parent 1:1 fq_pie limit 1" + ], + "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: parent 1:1", + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "backlog 0b 0p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, { "id": "a4bb", "name": "Test FQ_CODEL with HTB parent - force packet drop with empty queue", @@ -478,7 +702,6 @@ "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem duplicate 100%", "$TC filter add dev $DUMMY parent 1:0 protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1", "$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc ls m2 10Mbit", - "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%", "$TC filter add dev $DUMMY parent 1:0 protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2", "ping -c 1 10.10.10.1 -I$DUMMY > /dev/null || true", "$TC filter del dev $DUMMY parent 1:0 protocol ip prio 1", @@ -491,8 +714,8 @@ { "kind": "hfsc", "handle": "1:", - "bytes": 392, - "packets": 4 + "bytes": 294, + "packets": 3 } ], "matchCount": "1", @@ -635,5 +858,108 @@ "$TC qdisc del dev $DUMMY handle 1:0 root", "$IP addr del 10.10.10.10/24 dev $DUMMY || true" ] + }, + { + "id": "d74b", + "name": "Test use-after-free with DRR/NETEM/BLACKHOLE chain", + "category": [ + "qdisc", + "hfsc", + "drr", + "netem", + "blackhole" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "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: drr", + "$TC filter add dev $DUMMY parent 1: basic classid 1:1", + "$TC class add dev $DUMMY parent 1: classid 1:1 drr", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: hfsc def 1", + "$TC class add dev $DUMMY parent 2: classid 2:1 hfsc rt m1 8 d 1 m2 0", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: netem", + "$TC qdisc add dev $DUMMY parent 3:1 handle 4: blackhole", + "ping -c1 -W0.01 -I $DUMMY 10.10.11.11 || true", + "$TC class del dev $DUMMY classid 1:1" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.11.11", + "expExitCode": "1", + "verifyCmd": "$TC -j class ls dev $DUMMY classid 1:1", + "matchJSON": [], + "teardown": [ + "$TC qdisc del dev $DUMMY root handle 1: drr" + ] + }, + { + "id": "be28", + "name": "Try to add fq_codel qdisc as a child of an hhf qdisc", + "category": [ + "qdisc", + "fq_codel", + "hhf" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle a: hhf" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent a: handle b: fq_codel", + "expExitCode": "2", + "verifyCmd": "$TC -j qdisc ls dev $DUMMY handle b:", + "matchJSON": [], + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + }, + { + "id": "fcb5", + "name": "Try to add pie qdisc as a child of a drr qdisc", + "category": [ + "qdisc", + "pie", + "drr" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle a: drr" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent a: handle b: pie", + "expExitCode": "2", + "verifyCmd": "$TC -j qdisc ls dev $DUMMY handle b:", + "matchJSON": [], + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + }, + { + "id": "7801", + "name": "Try to add fq qdisc as a child of an inexistent hfsc class", + "category": [ + "qdisc", + "sfq", + "hfsc" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle a: hfsc" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent a:fff2 sfq limit 4", + "expExitCode": "2", + "verifyCmd": "$TC -j qdisc ls dev $DUMMY handle b:", + "matchJSON": [], + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json new file mode 100644 index 000000000000..cd1f2ee8f354 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json @@ -0,0 +1,254 @@ +[ + { + "id": "a4c7", + "name": "Create DualPI2 with default setting", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* step_thresh 1ms min_qlen_step 0p coupling_factor 2 drop_on_overload drop_dequeue classic_protection 10% l4s_ect split_gso", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "1ea4", + "name": "Create DualPI2 with memlimit", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 memlimit 20000000", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* memlimit 20000000B", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "2130", + "name": "Create DualPI2 with typical_rtt and max_rtt", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 typical_rtt 20ms max_rtt 200ms", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* target 20ms tupdate 20ms alpha 0.042969 beta 1.496094", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "90c1", + "name": "Create DualPI2 with max_rtt", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 max_rtt 300ms", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* target 50ms tupdate 50ms alpha 0.050781 beta 0.996094", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "7b3c", + "name": "Create DualPI2 with any_ect option", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 any_ect", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* any_ect", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "49a3", + "name": "Create DualPI2 with overflow option", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 overflow", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* overflow", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "d0a1", + "name": "Create DualPI2 with drop_enqueue option", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 drop_enqueue", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* drop_enqueue", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "f051", + "name": "Create DualPI2 with no_split_gso option", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 no_split_gso", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* no_split_gso", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "456b", + "name": "Create DualPI2 with packet step_thresh", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 step_thresh 3p", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* step_thresh 3p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "610c", + "name": "Create DualPI2 with packet min_qlen_step", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 min_qlen_step 1", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* min_qlen_step 1p", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "b4fa", + "name": "Create DualPI2 with packet coupling_factor", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 coupling_factor 1", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* coupling_factor 1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "37f1", + "name": "Create DualPI2 with packet classic_protection", + "category": [ + "qdisc", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 classic_protection 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* classic_protection 0%", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json index 3c4444961488..718d2df2aafa 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json @@ -336,5 +336,86 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "d34d", + "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change root", + "category": ["qdisc", "netem"], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: netem limit 1", + "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1" + ], + "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: netem duplicate 50%", + "expExitCode": "2", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "qdisc netem", + "matchCount": "2", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1:0 root" + ] + }, + { + "id": "b33f", + "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change non-root", + "category": ["qdisc", "netem"], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: netem limit 1", + "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1" + ], + "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2: netem duplicate 50%", + "expExitCode": "2", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "qdisc netem", + "matchCount": "2", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1:0 root" + ] + }, + { + "id": "cafe", + "name": "NETEM test qdisc duplication restriction in qdisc tree", + "category": ["qdisc", "netem"], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%", + "expExitCode": "2", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "qdisc netem", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1:0 root" + ] + }, + { + "id": "1337", + "name": "NETEM test qdisc duplication restriction in qdisc tree across branches", + "category": ["qdisc", "netem"], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY parent root handle 1:0 hfsc", + "$TC class add dev $DUMMY parent 1:0 classid 1:1 hfsc rt m2 10Mbit", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem", + "$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc rt m2 10Mbit" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%", + "expExitCode": "2", + "verifyCmd": "$TC -s qdisc show dev $DUMMY", + "matchPattern": "qdisc netem", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1:0 root" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json index 28c6ce6da7db..531a2f6e4900 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json @@ -264,5 +264,41 @@ "matchPattern": "sfq", "matchCount": "0", "teardown": [] + }, + { + "id": "cdc1", + "name": "Check that a negative perturb timer is rejected", + "category": [ + "qdisc", + "sfq" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq perturb -10", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "sfq", + "matchCount": "0", + "teardown": [] + }, + { + "id": "a9f0", + "name": "Check that a too big perturb timer is rejected", + "category": [ + "qdisc", + "sfq" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq perturb 1000000000", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "sfq", + "matchCount": "0", + "teardown": [] } ] diff --git a/tools/testing/selftests/tc-testing/tdc.sh b/tools/testing/selftests/tc-testing/tdc.sh index 589b18ed758a..dae19687912d 100755 --- a/tools/testing/selftests/tc-testing/tdc.sh +++ b/tools/testing/selftests/tc-testing/tdc.sh @@ -4,8 +4,7 @@ # If a module is required and was not compiled # the test that requires it will fail anyways try_modprobe() { - modprobe -q -R "$1" - if [ $? -ne 0 ]; then + if ! modprobe -q -R "$1"; then echo "Module $1 not found... skipping." else modprobe "$1" @@ -67,4 +66,5 @@ try_modprobe sch_hfsc try_modprobe sch_hhf try_modprobe sch_htb try_modprobe sch_teql -./tdc.py -J`nproc` +try_modprobe sch_dualpi2 +./tdc.py -J"$(nproc)" diff --git a/tools/testing/selftests/thermal/intel/workload_hint/workload_hint_test.c b/tools/testing/selftests/thermal/intel/workload_hint/workload_hint_test.c index a40097232967..ba58589a1145 100644 --- a/tools/testing/selftests/thermal/intel/workload_hint/workload_hint_test.c +++ b/tools/testing/selftests/thermal/intel/workload_hint/workload_hint_test.c @@ -32,12 +32,12 @@ void workload_hint_exit(int signum) fd = open(WORKLOAD_ENABLE_ATTRIBUTE, O_RDWR); if (fd < 0) { - perror("Unable to open workload type feature enable file\n"); + perror("Unable to open workload type feature enable file"); exit(1); } if (write(fd, "0\n", 2) < 0) { - perror("Can't disable workload hints\n"); + perror("Can't disable workload hints"); exit(1); } @@ -67,17 +67,15 @@ int main(int argc, char **argv) if (delay < 0) exit(1); - sprintf(delay_str, "%s\n", argv[1]); - sprintf(delay_str, "%s\n", argv[1]); fd = open(WORKLOAD_NOTIFICATION_DELAY_ATTRIBUTE, O_RDWR); if (fd < 0) { - perror("Unable to open workload notification delay\n"); + perror("Unable to open workload notification delay"); exit(1); } if (write(fd, delay_str, strlen(delay_str)) < 0) { - perror("Can't set delay\n"); + perror("Can't set delay"); exit(1); } @@ -94,12 +92,12 @@ int main(int argc, char **argv) /* Enable feature via sysfs knob */ fd = open(WORKLOAD_ENABLE_ATTRIBUTE, O_RDWR); if (fd < 0) { - perror("Unable to open workload type feature enable file\n"); + perror("Unable to open workload type feature enable file"); exit(1); } if (write(fd, "1\n", 2) < 0) { - perror("Can't enable workload hints\n"); + perror("Can't enable workload hints"); exit(1); } @@ -110,7 +108,7 @@ int main(int argc, char **argv) while (1) { fd = open(WORKLOAD_TYPE_INDEX_ATTRIBUTE, O_RDONLY); if (fd < 0) { - perror("Unable to open workload type file\n"); + perror("Unable to open workload type file"); exit(1); } diff --git a/tools/testing/selftests/ublk/fault_inject.c b/tools/testing/selftests/ublk/fault_inject.c index 6e60f7d97125..b227bd78b252 100644 --- a/tools/testing/selftests/ublk/fault_inject.c +++ b/tools/testing/selftests/ublk/fault_inject.c @@ -38,7 +38,8 @@ static int ublk_fault_inject_tgt_init(const struct dev_ctx *ctx, return 0; } -static int ublk_fault_inject_queue_io(struct ublk_queue *q, int tag) +static int ublk_fault_inject_queue_io(struct ublk_thread *t, + struct ublk_queue *q, int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); struct io_uring_sqe *sqe; @@ -46,25 +47,27 @@ static int ublk_fault_inject_queue_io(struct ublk_queue *q, int tag) .tv_nsec = (long long)q->dev->private_data, }; - ublk_io_alloc_sqes(ublk_get_io(q, tag), &sqe, 1); + ublk_io_alloc_sqes(t, &sqe, 1); io_uring_prep_timeout(sqe, &ts, 1, 0); sqe->user_data = build_user_data(tag, ublksrv_get_op(iod), 0, q->q_id, 1); - ublk_queued_tgt_io(q, tag, 1); + ublk_queued_tgt_io(t, q, tag, 1); return 0; } -static void ublk_fault_inject_tgt_io_done(struct ublk_queue *q, int tag, +static void ublk_fault_inject_tgt_io_done(struct ublk_thread *t, + struct ublk_queue *q, const struct io_uring_cqe *cqe) { + unsigned tag = user_data_to_tag(cqe->user_data); const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); if (cqe->res != -ETIME) ublk_err("%s: unexpected cqe res %d\n", __func__, cqe->res); - if (ublk_completed_tgt_io(q, tag)) - ublk_complete_io(q, tag, iod->nr_sectors << 9); + if (ublk_completed_tgt_io(t, q, tag)) + ublk_complete_io(t, q, tag, iod->nr_sectors << 9); else ublk_err("%s: io not complete after 1 cqe\n", __func__); } diff --git a/tools/testing/selftests/ublk/file_backed.c b/tools/testing/selftests/ublk/file_backed.c index cfa59b631693..cd9fe69ecce2 100644 --- a/tools/testing/selftests/ublk/file_backed.c +++ b/tools/testing/selftests/ublk/file_backed.c @@ -13,20 +13,22 @@ static enum io_uring_op ublk_to_uring_op(const struct ublksrv_io_desc *iod, int assert(0); } -static int loop_queue_flush_io(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag) +static int loop_queue_flush_io(struct ublk_thread *t, struct ublk_queue *q, + const struct ublksrv_io_desc *iod, int tag) { unsigned ublk_op = ublksrv_get_op(iod); struct io_uring_sqe *sqe[1]; - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, 1); - io_uring_prep_fsync(sqe[0], 1 /*fds[1]*/, IORING_FSYNC_DATASYNC); + ublk_io_alloc_sqes(t, sqe, 1); + io_uring_prep_fsync(sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/, IORING_FSYNC_DATASYNC); io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE); /* bit63 marks us as tgt io */ sqe[0]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1); return 1; } -static int loop_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag) +static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q, + const struct ublksrv_io_desc *iod, int tag) { unsigned ublk_op = ublksrv_get_op(iod); unsigned zc = ublk_queue_use_zc(q); @@ -36,11 +38,11 @@ static int loop_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_de void *addr = (zc | auto_zc) ? NULL : (void *)iod->addr; if (!zc || auto_zc) { - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, 1); + ublk_io_alloc_sqes(t, sqe, 1); if (!sqe[0]) return -ENOMEM; - io_uring_prep_rw(op, sqe[0], 1 /*fds[1]*/, + io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/, addr, iod->nr_sectors << 9, iod->start_sector << 9); @@ -52,27 +54,27 @@ static int loop_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_de return 1; } - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, 3); + ublk_io_alloc_sqes(t, sqe, 3); - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; sqe[0]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); - io_uring_prep_rw(op, sqe[1], 1 /*fds[1]*/, 0, + io_uring_prep_rw(op, sqe[1], ublk_get_registered_fd(q, 1) /*fds[1]*/, 0, iod->nr_sectors << 9, iod->start_sector << 9); sqe[1]->buf_index = tag; sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK; sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1); - io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); + io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1); return 2; } -static int loop_queue_tgt_io(struct ublk_queue *q, int tag) +static int loop_queue_tgt_io(struct ublk_thread *t, struct ublk_queue *q, int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); unsigned ublk_op = ublksrv_get_op(iod); @@ -80,7 +82,7 @@ static int loop_queue_tgt_io(struct ublk_queue *q, int tag) switch (ublk_op) { case UBLK_IO_OP_FLUSH: - ret = loop_queue_flush_io(q, iod, tag); + ret = loop_queue_flush_io(t, q, iod, tag); break; case UBLK_IO_OP_WRITE_ZEROES: case UBLK_IO_OP_DISCARD: @@ -88,7 +90,7 @@ static int loop_queue_tgt_io(struct ublk_queue *q, int tag) break; case UBLK_IO_OP_READ: case UBLK_IO_OP_WRITE: - ret = loop_queue_tgt_rw_io(q, iod, tag); + ret = loop_queue_tgt_rw_io(t, q, iod, tag); break; default: ret = -EINVAL; @@ -100,17 +102,19 @@ static int loop_queue_tgt_io(struct ublk_queue *q, int tag) return ret; } -static int ublk_loop_queue_io(struct ublk_queue *q, int tag) +static int ublk_loop_queue_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { - int queued = loop_queue_tgt_io(q, tag); + int queued = loop_queue_tgt_io(t, q, tag); - ublk_queued_tgt_io(q, tag, queued); + ublk_queued_tgt_io(t, q, tag, queued); return 0; } -static void ublk_loop_io_done(struct ublk_queue *q, int tag, +static void ublk_loop_io_done(struct ublk_thread *t, struct ublk_queue *q, const struct io_uring_cqe *cqe) { + unsigned tag = user_data_to_tag(cqe->user_data); unsigned op = user_data_to_op(cqe->user_data); struct ublk_io *io = ublk_get_io(q, tag); @@ -126,8 +130,8 @@ static void ublk_loop_io_done(struct ublk_queue *q, int tag, if (op == ublk_cmd_op_nr(UBLK_U_IO_REGISTER_IO_BUF)) io->tgt_ios += 1; - if (ublk_completed_tgt_io(q, tag)) - ublk_complete_io(q, tag, io->result); + if (ublk_completed_tgt_io(t, q, tag)) + ublk_complete_io(t, q, tag, io->result); } static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev) diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c index e2d2042810d4..b71faba86c3b 100644 --- a/tools/testing/selftests/ublk/kublk.c +++ b/tools/testing/selftests/ublk/kublk.c @@ -432,7 +432,7 @@ static void ublk_thread_deinit(struct ublk_thread *t) } } -static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) +static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags) { struct ublk_dev *dev = q->dev; int depth = dev->dev_info.queue_depth; @@ -441,17 +441,13 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) unsigned long off; q->tgt_ops = dev->tgt.ops; - q->state = 0; + q->flags = 0; q->q_depth = depth; + q->flags = dev->dev_info.flags; + q->flags |= extra_flags; - if (dev->dev_info.flags & (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_AUTO_BUF_REG)) { - q->state |= UBLKSRV_NO_BUF; - if (dev->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY) - q->state |= UBLKSRV_ZC; - if (dev->dev_info.flags & UBLK_F_AUTO_BUF_REG) - q->state |= UBLKSRV_AUTO_BUF_REG; - } - q->state |= extra_flags; + /* Cache fd in queue for fast path access */ + q->ublk_fd = dev->fds[0]; cmd_buf_size = ublk_queue_cmd_buf_sz(q); off = UBLKSRV_CMD_BUF_OFFSET + q->q_id * ublk_queue_max_cmd_buf_sz(); @@ -466,10 +462,10 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) io_buf_size = dev->dev_info.max_io_buf_bytes; for (i = 0; i < q->q_depth; i++) { q->ios[i].buf_addr = NULL; - q->ios[i].flags = UBLKSRV_NEED_FETCH_RQ | UBLKSRV_IO_FREE; + q->ios[i].flags = UBLKS_IO_NEED_FETCH_RQ | UBLKS_IO_FREE; q->ios[i].tag = i; - if (q->state & UBLKSRV_NO_BUF) + if (ublk_queue_no_buf(q)) continue; if (posix_memalign((void **)&q->ios[i].buf_addr, @@ -488,9 +484,10 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) return -ENOMEM; } -static int ublk_thread_init(struct ublk_thread *t) +static int ublk_thread_init(struct ublk_thread *t, unsigned long long extra_flags) { struct ublk_dev *dev = t->dev; + unsigned long long flags = dev->dev_info.flags | extra_flags; int ring_depth = dev->tgt.sq_depth, cq_depth = dev->tgt.cq_depth; int ret; @@ -519,7 +516,17 @@ static int ublk_thread_init(struct ublk_thread *t) io_uring_register_ring_fd(&t->ring); - ret = io_uring_register_files(&t->ring, dev->fds, dev->nr_fds); + if (flags & UBLKS_Q_NO_UBLK_FIXED_FD) { + /* Register only backing files starting from index 1, exclude ublk control device */ + if (dev->nr_fds > 1) { + ret = io_uring_register_files(&t->ring, &dev->fds[1], dev->nr_fds - 1); + } else { + /* No backing files to register, skip file registration */ + ret = 0; + } + } else { + ret = io_uring_register_files(&t->ring, dev->fds, dev->nr_fds); + } if (ret) { ublk_err("ublk dev %d thread %d register files failed %d\n", t->dev->dev_info.dev_id, t->idx, ret); @@ -583,15 +590,14 @@ static void ublk_set_auto_buf_reg(const struct ublk_queue *q, else buf.index = q->ios[tag].buf_index; - if (q->state & UBLKSRV_AUTO_BUF_REG_FALLBACK) + if (ublk_queue_auto_zc_fallback(q)) buf.flags = UBLK_AUTO_BUF_REG_FALLBACK; sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf); } -int ublk_queue_io_cmd(struct ublk_io *io) +int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io) { - struct ublk_thread *t = io->t; struct ublk_queue *q = ublk_io_to_queue(io); struct ublksrv_io_cmd *cmd; struct io_uring_sqe *sqe[1]; @@ -599,7 +605,7 @@ int ublk_queue_io_cmd(struct ublk_io *io) __u64 user_data; /* only freed io can be issued */ - if (!(io->flags & UBLKSRV_IO_FREE)) + if (!(io->flags & UBLKS_IO_FREE)) return 0; /* @@ -607,20 +613,20 @@ int ublk_queue_io_cmd(struct ublk_io *io) * getting data */ if (!(io->flags & - (UBLKSRV_NEED_FETCH_RQ | UBLKSRV_NEED_COMMIT_RQ_COMP | UBLKSRV_NEED_GET_DATA))) + (UBLKS_IO_NEED_FETCH_RQ | UBLKS_IO_NEED_COMMIT_RQ_COMP | UBLKS_IO_NEED_GET_DATA))) return 0; - if (io->flags & UBLKSRV_NEED_GET_DATA) + if (io->flags & UBLKS_IO_NEED_GET_DATA) cmd_op = UBLK_U_IO_NEED_GET_DATA; - else if (io->flags & UBLKSRV_NEED_COMMIT_RQ_COMP) + else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP) cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ; - else if (io->flags & UBLKSRV_NEED_FETCH_RQ) + else if (io->flags & UBLKS_IO_NEED_FETCH_RQ) cmd_op = UBLK_U_IO_FETCH_REQ; if (io_uring_sq_space_left(&t->ring) < 1) io_uring_submit(&t->ring); - ublk_io_alloc_sqes(io, sqe, 1); + ublk_io_alloc_sqes(t, sqe, 1); if (!sqe[0]) { ublk_err("%s: run out of sqe. thread %u, tag %d\n", __func__, t->idx, io->tag); @@ -634,18 +640,21 @@ int ublk_queue_io_cmd(struct ublk_io *io) /* These fields should be written once, never change */ ublk_set_sqe_cmd_op(sqe[0], cmd_op); - sqe[0]->fd = 0; /* dev->fds[0] */ + sqe[0]->fd = ublk_get_registered_fd(q, 0); /* dev->fds[0] */ sqe[0]->opcode = IORING_OP_URING_CMD; - sqe[0]->flags = IOSQE_FIXED_FILE; + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) + sqe[0]->flags = 0; /* Use raw FD, not fixed file */ + else + sqe[0]->flags = IOSQE_FIXED_FILE; sqe[0]->rw_flags = 0; cmd->tag = io->tag; cmd->q_id = q->q_id; - if (!(q->state & UBLKSRV_NO_BUF)) + if (!ublk_queue_no_buf(q)) cmd->addr = (__u64) (uintptr_t) io->buf_addr; else cmd->addr = 0; - if (q->state & UBLKSRV_AUTO_BUF_REG) + if (ublk_queue_use_auto_zc(q)) ublk_set_auto_buf_reg(q, sqe[0], io->tag); user_data = build_user_data(io->tag, _IOC_NR(cmd_op), 0, q->q_id, 0); @@ -657,7 +666,7 @@ int ublk_queue_io_cmd(struct ublk_io *io) ublk_dbg(UBLK_DBG_IO_CMD, "%s: (thread %u qid %d tag %u cmd_op %u) iof %x stopping %d\n", __func__, t->idx, q->q_id, io->tag, cmd_op, - io->flags, !!(t->state & UBLKSRV_THREAD_STOPPING)); + io->flags, !!(t->state & UBLKS_T_STOPPING)); return 1; } @@ -685,9 +694,8 @@ static void ublk_submit_fetch_commands(struct ublk_thread *t) int tag = i % dinfo->queue_depth; q = &t->dev->q[q_id]; io = &q->ios[tag]; - io->t = t; io->buf_index = j++; - ublk_queue_io_cmd(io); + ublk_queue_io_cmd(t, io); } } else { /* @@ -697,9 +705,8 @@ static void ublk_submit_fetch_commands(struct ublk_thread *t) struct ublk_queue *q = &t->dev->q[t->idx]; for (i = 0; i < q->q_depth; i++) { io = &q->ios[i]; - io->t = t; io->buf_index = i; - ublk_queue_io_cmd(io); + ublk_queue_io_cmd(t, io); } } } @@ -711,14 +718,13 @@ static int ublk_thread_is_idle(struct ublk_thread *t) static int ublk_thread_is_done(struct ublk_thread *t) { - return (t->state & UBLKSRV_THREAD_STOPPING) && ublk_thread_is_idle(t); + return (t->state & UBLKS_T_STOPPING) && ublk_thread_is_idle(t); } -static inline void ublksrv_handle_tgt_cqe(struct ublk_queue *q, - struct io_uring_cqe *cqe) +static inline void ublksrv_handle_tgt_cqe(struct ublk_thread *t, + struct ublk_queue *q, + struct io_uring_cqe *cqe) { - unsigned tag = user_data_to_tag(cqe->user_data); - if (cqe->res < 0 && cqe->res != -EAGAIN) ublk_err("%s: failed tgt io: res %d qid %u tag %u, cmd_op %u\n", __func__, cqe->res, q->q_id, @@ -726,7 +732,41 @@ static inline void ublksrv_handle_tgt_cqe(struct ublk_queue *q, user_data_to_op(cqe->user_data)); if (q->tgt_ops->tgt_io_done) - q->tgt_ops->tgt_io_done(q, tag, cqe); + q->tgt_ops->tgt_io_done(t, q, cqe); +} + +static void ublk_handle_uring_cmd(struct ublk_thread *t, + struct ublk_queue *q, + const struct io_uring_cqe *cqe) +{ + int fetch = (cqe->res != UBLK_IO_RES_ABORT) && + !(t->state & UBLKS_T_STOPPING); + unsigned tag = user_data_to_tag(cqe->user_data); + struct ublk_io *io = &q->ios[tag]; + + if (!fetch) { + t->state |= UBLKS_T_STOPPING; + io->flags &= ~UBLKS_IO_NEED_FETCH_RQ; + } + + if (cqe->res == UBLK_IO_RES_OK) { + assert(tag < q->q_depth); + if (q->tgt_ops->queue_io) + q->tgt_ops->queue_io(t, q, tag); + } else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) { + io->flags |= UBLKS_IO_NEED_GET_DATA | UBLKS_IO_FREE; + ublk_queue_io_cmd(t, io); + } else { + /* + * COMMIT_REQ will be completed immediately since no fetching + * piggyback is required. + * + * Marking IO_FREE only, then this io won't be issued since + * we only issue io with (UBLKS_IO_FREE | UBLKSRV_NEED_*) + * + * */ + io->flags = UBLKS_IO_FREE; + } } static void ublk_handle_cqe(struct ublk_thread *t, @@ -735,54 +775,27 @@ static void ublk_handle_cqe(struct ublk_thread *t, struct ublk_dev *dev = t->dev; unsigned q_id = user_data_to_q_id(cqe->user_data); struct ublk_queue *q = &dev->q[q_id]; - unsigned tag = user_data_to_tag(cqe->user_data); unsigned cmd_op = user_data_to_op(cqe->user_data); - int fetch = (cqe->res != UBLK_IO_RES_ABORT) && - !(t->state & UBLKSRV_THREAD_STOPPING); - struct ublk_io *io; if (cqe->res < 0 && cqe->res != -ENODEV) ublk_err("%s: res %d userdata %llx queue state %x\n", __func__, - cqe->res, cqe->user_data, q->state); + cqe->res, cqe->user_data, q->flags); ublk_dbg(UBLK_DBG_IO_CMD, "%s: res %d (qid %d tag %u cmd_op %u target %d/%d) stopping %d\n", - __func__, cqe->res, q->q_id, tag, cmd_op, - is_target_io(cqe->user_data), + __func__, cqe->res, q->q_id, user_data_to_tag(cqe->user_data), + cmd_op, is_target_io(cqe->user_data), user_data_to_tgt_data(cqe->user_data), - (t->state & UBLKSRV_THREAD_STOPPING)); + (t->state & UBLKS_T_STOPPING)); /* Don't retrieve io in case of target io */ if (is_target_io(cqe->user_data)) { - ublksrv_handle_tgt_cqe(q, cqe); + ublksrv_handle_tgt_cqe(t, q, cqe); return; } - io = &q->ios[tag]; t->cmd_inflight--; - if (!fetch) { - t->state |= UBLKSRV_THREAD_STOPPING; - io->flags &= ~UBLKSRV_NEED_FETCH_RQ; - } - - if (cqe->res == UBLK_IO_RES_OK) { - assert(tag < q->q_depth); - if (q->tgt_ops->queue_io) - q->tgt_ops->queue_io(q, tag); - } else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) { - io->flags |= UBLKSRV_NEED_GET_DATA | UBLKSRV_IO_FREE; - ublk_queue_io_cmd(io); - } else { - /* - * COMMIT_REQ will be completed immediately since no fetching - * piggyback is required. - * - * Marking IO_FREE only, then this io won't be issued since - * we only issue io with (UBLKSRV_IO_FREE | UBLKSRV_NEED_*) - * - * */ - io->flags = UBLKSRV_IO_FREE; - } + ublk_handle_uring_cmd(t, q, cqe); } static int ublk_reap_events_uring(struct ublk_thread *t) @@ -808,7 +821,7 @@ static int ublk_process_io(struct ublk_thread *t) t->dev->dev_info.dev_id, t->idx, io_uring_sq_ready(&t->ring), t->cmd_inflight, - (t->state & UBLKSRV_THREAD_STOPPING)); + (t->state & UBLKS_T_STOPPING)); if (ublk_thread_is_done(t)) return -ENODEV; @@ -817,8 +830,8 @@ static int ublk_process_io(struct ublk_thread *t) reapped = ublk_reap_events_uring(t); ublk_dbg(UBLK_DBG_THREAD, "submit result %d, reapped %d stop %d idle %d\n", - ret, reapped, (t->state & UBLKSRV_THREAD_STOPPING), - (t->state & UBLKSRV_THREAD_IDLE)); + ret, reapped, (t->state & UBLKS_T_STOPPING), + (t->state & UBLKS_T_IDLE)); return reapped; } @@ -836,6 +849,7 @@ struct ublk_thread_info { unsigned idx; sem_t *ready; cpu_set_t *affinity; + unsigned long long extra_flags; }; static void *ublk_io_handler_fn(void *data) @@ -848,7 +862,7 @@ static void *ublk_io_handler_fn(void *data) t->dev = info->dev; t->idx = info->idx; - ret = ublk_thread_init(t); + ret = ublk_thread_init(t, info->extra_flags); if (ret) { ublk_err("ublk dev %d thread %u init failed\n", dev_id, t->idx); @@ -915,7 +929,7 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev) { const struct ublksrv_ctrl_dev_info *dinfo = &dev->dev_info; struct ublk_thread_info *tinfo; - unsigned extra_flags = 0; + unsigned long long extra_flags = 0; cpu_set_t *affinity_buf; void *thread_ret; sem_t ready; @@ -937,7 +951,9 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev) return ret; if (ctx->auto_zc_fallback) - extra_flags = UBLKSRV_AUTO_BUF_REG_FALLBACK; + extra_flags = UBLKS_Q_AUTO_BUF_REG_FALLBACK; + if (ctx->no_ublk_fixed_fd) + extra_flags |= UBLKS_Q_NO_UBLK_FIXED_FD; for (i = 0; i < dinfo->nr_hw_queues; i++) { dev->q[i].dev = dev; @@ -955,6 +971,7 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev) tinfo[i].dev = dev; tinfo[i].idx = i; tinfo[i].ready = &ready; + tinfo[i].extra_flags = extra_flags; /* * If threads are not tied 1:1 to queues, setting thread @@ -1404,7 +1421,7 @@ static int cmd_dev_get_features(void) if (!((1ULL << i) & features)) continue; - if (i < sizeof(feat_map) / sizeof(feat_map[0])) + if (i < ARRAY_SIZE(feat_map)) feat = feat_map[i]; else feat = "unknown"; @@ -1475,13 +1492,13 @@ static void __cmd_create_help(char *exe, bool recovery) printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n", exe, recovery ? "recover" : "add"); printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1 ] [-g]\n"); - printf("\t[-e 0|1 ] [-i 0|1]\n"); + printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n"); printf("\t[--nthreads threads] [--per_io_tasks]\n"); printf("\t[target options] [backfile1] [backfile2] ...\n"); printf("\tdefault: nr_queues=2(max 32), depth=128(max 1024), dev_id=-1(auto allocation)\n"); printf("\tdefault: nthreads=nr_queues"); - for (i = 0; i < sizeof(tgt_ops_list) / sizeof(tgt_ops_list[0]); i++) { + for (i = 0; i < ARRAY_SIZE(tgt_ops_list); i++) { const struct ublk_tgt_ops *ops = tgt_ops_list[i]; if (ops->usage) @@ -1538,6 +1555,7 @@ int main(int argc, char *argv[]) { "size", 1, NULL, 's'}, { "nthreads", 1, NULL, 0 }, { "per_io_tasks", 0, NULL, 0 }, + { "no_ublk_fixed_fd", 0, NULL, 0 }, { 0, 0, 0, 0 } }; const struct ublk_tgt_ops *ops = NULL; @@ -1617,6 +1635,8 @@ int main(int argc, char *argv[]) ctx.nthreads = strtol(optarg, NULL, 10); if (!strcmp(longopts[option_idx].name, "per_io_tasks")) ctx.per_io_tasks = 1; + if (!strcmp(longopts[option_idx].name, "no_ublk_fixed_fd")) + ctx.no_ublk_fixed_fd = 1; break; case '?': /* diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h index 6be601536b3d..5e55484fb0aa 100644 --- a/tools/testing/selftests/ublk/kublk.h +++ b/tools/testing/selftests/ublk/kublk.h @@ -29,13 +29,9 @@ #include "ublk_dep.h" #include -#define __maybe_unused __attribute__((unused)) -#define MAX_BACK_FILES 4 -#ifndef min -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif +#include "utils.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define MAX_BACK_FILES 4 /****************** part 1: libublk ********************/ @@ -45,9 +41,6 @@ #define UBLK_CTRL_RING_DEPTH 32 #define ERROR_EVTFD_DEVID -2 -/* queue idle timeout */ -#define UBLKSRV_IO_IDLE_SECS 20 - #define UBLK_IO_MAX_BYTES (1 << 20) #define UBLK_MAX_QUEUES_SHIFT 5 #define UBLK_MAX_QUEUES (1 << UBLK_MAX_QUEUES_SHIFT) @@ -55,13 +48,6 @@ #define UBLK_MAX_THREADS (1 << UBLK_MAX_THREADS_SHIFT) #define UBLK_QUEUE_DEPTH 1024 -#define UBLK_DBG_DEV (1U << 0) -#define UBLK_DBG_THREAD (1U << 1) -#define UBLK_DBG_IO_CMD (1U << 2) -#define UBLK_DBG_IO (1U << 3) -#define UBLK_DBG_CTRL_CMD (1U << 4) -#define UBLK_LOG (1U << 5) - struct ublk_dev; struct ublk_queue; struct ublk_thread; @@ -91,6 +77,7 @@ struct dev_ctx { unsigned int recovery:1; unsigned int auto_zc_fallback:1; unsigned int per_io_tasks:1; + unsigned int no_ublk_fixed_fd:1; int _evtfd; int _shmid; @@ -121,11 +108,11 @@ struct ublk_ctrl_cmd_data { struct ublk_io { char *buf_addr; -#define UBLKSRV_NEED_FETCH_RQ (1UL << 0) -#define UBLKSRV_NEED_COMMIT_RQ_COMP (1UL << 1) -#define UBLKSRV_IO_FREE (1UL << 2) -#define UBLKSRV_NEED_GET_DATA (1UL << 3) -#define UBLKSRV_NEED_REG_BUF (1UL << 4) +#define UBLKS_IO_NEED_FETCH_RQ (1UL << 0) +#define UBLKS_IO_NEED_COMMIT_RQ_COMP (1UL << 1) +#define UBLKS_IO_FREE (1UL << 2) +#define UBLKS_IO_NEED_GET_DATA (1UL << 3) +#define UBLKS_IO_NEED_REG_BUF (1UL << 4) unsigned short flags; unsigned short refs; /* used by target code only */ @@ -136,7 +123,6 @@ struct ublk_io { unsigned short buf_index; unsigned short tgt_ios; void *private_data; - struct ublk_thread *t; }; struct ublk_tgt_ops { @@ -144,9 +130,9 @@ struct ublk_tgt_ops { int (*init_tgt)(const struct dev_ctx *ctx, struct ublk_dev *); void (*deinit_tgt)(struct ublk_dev *); - int (*queue_io)(struct ublk_queue *, int tag); - void (*tgt_io_done)(struct ublk_queue *, - int tag, const struct io_uring_cqe *); + int (*queue_io)(struct ublk_thread *, struct ublk_queue *, int tag); + void (*tgt_io_done)(struct ublk_thread *, struct ublk_queue *, + const struct io_uring_cqe *); /* * Target specific command line handling @@ -179,12 +165,12 @@ struct ublk_queue { const struct ublk_tgt_ops *tgt_ops; struct ublksrv_io_desc *io_cmd_buf; +/* borrow one bit of ublk uapi flags, which may never be used */ +#define UBLKS_Q_AUTO_BUF_REG_FALLBACK (1ULL << 63) +#define UBLKS_Q_NO_UBLK_FIXED_FD (1ULL << 62) + __u64 flags; + int ublk_fd; /* cached ublk char device fd */ struct ublk_io ios[UBLK_QUEUE_DEPTH]; -#define UBLKSRV_NO_BUF (1U << 2) -#define UBLKSRV_ZC (1U << 3) -#define UBLKSRV_AUTO_BUF_REG (1U << 4) -#define UBLKSRV_AUTO_BUF_REG_FALLBACK (1U << 5) - unsigned state; }; struct ublk_thread { @@ -196,8 +182,8 @@ struct ublk_thread { pthread_t thread; unsigned idx; -#define UBLKSRV_THREAD_STOPPING (1U << 0) -#define UBLKSRV_THREAD_IDLE (1U << 1) +#define UBLKS_T_STOPPING (1U << 0) +#define UBLKS_T_IDLE (1U << 1) unsigned state; }; @@ -217,22 +203,7 @@ struct ublk_dev { void *private_data; }; -#ifndef offsetof -#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) -#endif - -#ifndef container_of -#define container_of(ptr, type, member) ({ \ - unsigned long __mptr = (unsigned long)(ptr); \ - ((type *)(__mptr - offsetof(type, member))); }) -#endif - -#define round_up(val, rnd) \ - (((val) + ((rnd) - 1)) & ~((rnd) - 1)) - - -extern unsigned int ublk_dbg_mask; -extern int ublk_queue_io_cmd(struct ublk_io *io); +extern int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io); static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod) @@ -281,43 +252,15 @@ static inline unsigned short ublk_cmd_op_nr(unsigned int op) return _IOC_NR(op); } -static inline void ublk_err(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); -} - -static inline void ublk_log(const char *fmt, ...) -{ - if (ublk_dbg_mask & UBLK_LOG) { - va_list ap; - - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - } -} - -static inline void ublk_dbg(int level, const char *fmt, ...) -{ - if (level & ublk_dbg_mask) { - va_list ap; - - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - } -} - static inline struct ublk_queue *ublk_io_to_queue(const struct ublk_io *io) { return container_of(io, struct ublk_queue, ios[io->tag]); } -static inline int ublk_io_alloc_sqes(struct ublk_io *io, +static inline int ublk_io_alloc_sqes(struct ublk_thread *t, struct io_uring_sqe *sqes[], int nr_sqes) { - struct io_uring *ring = &io->t->ring; + struct io_uring *ring = &t->ring; unsigned left = io_uring_sq_space_left(ring); int i; @@ -333,34 +276,48 @@ static inline int ublk_io_alloc_sqes(struct ublk_io *io, return nr_sqes; } -static inline void io_uring_prep_buf_register(struct io_uring_sqe *sqe, - int dev_fd, int tag, int q_id, __u64 index) +static inline int ublk_get_registered_fd(struct ublk_queue *q, int fd_index) +{ + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) { + if (fd_index == 0) + /* Return the raw ublk FD for index 0 */ + return q->ublk_fd; + /* Adjust index for backing files (index 1 becomes 0, etc.) */ + return fd_index - 1; + } + return fd_index; +} + +static inline void __io_uring_prep_buf_reg_unreg(struct io_uring_sqe *sqe, + struct ublk_queue *q, int tag, int q_id, __u64 index) { struct ublksrv_io_cmd *cmd = (struct ublksrv_io_cmd *)sqe->cmd; + int dev_fd = ublk_get_registered_fd(q, 0); io_uring_prep_read(sqe, dev_fd, 0, 0, 0); sqe->opcode = IORING_OP_URING_CMD; - sqe->flags |= IOSQE_FIXED_FILE; - sqe->cmd_op = UBLK_U_IO_REGISTER_IO_BUF; + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) + sqe->flags &= ~IOSQE_FIXED_FILE; + else + sqe->flags |= IOSQE_FIXED_FILE; cmd->tag = tag; cmd->addr = index; cmd->q_id = q_id; } -static inline void io_uring_prep_buf_unregister(struct io_uring_sqe *sqe, - int dev_fd, int tag, int q_id, __u64 index) +static inline void io_uring_prep_buf_register(struct io_uring_sqe *sqe, + struct ublk_queue *q, int tag, int q_id, __u64 index) { - struct ublksrv_io_cmd *cmd = (struct ublksrv_io_cmd *)sqe->cmd; + __io_uring_prep_buf_reg_unreg(sqe, q, tag, q_id, index); + sqe->cmd_op = UBLK_U_IO_REGISTER_IO_BUF; +} - io_uring_prep_read(sqe, dev_fd, 0, 0, 0); - sqe->opcode = IORING_OP_URING_CMD; - sqe->flags |= IOSQE_FIXED_FILE; +static inline void io_uring_prep_buf_unregister(struct io_uring_sqe *sqe, + struct ublk_queue *q, int tag, int q_id, __u64 index) +{ + __io_uring_prep_buf_reg_unreg(sqe, q, tag, q_id, index); sqe->cmd_op = UBLK_U_IO_UNREGISTER_IO_BUF; - - cmd->tag = tag; - cmd->addr = index; - cmd->q_id = q_id; } static inline void *ublk_get_sqe_cmd(const struct io_uring_sqe *sqe) @@ -380,7 +337,7 @@ static inline int ublk_get_io_res(const struct ublk_queue *q, unsigned tag) static inline void ublk_mark_io_done(struct ublk_io *io, int res) { - io->flags |= (UBLKSRV_NEED_COMMIT_RQ_COMP | UBLKSRV_IO_FREE); + io->flags |= (UBLKS_IO_NEED_COMMIT_RQ_COMP | UBLKS_IO_FREE); io->result = res; } @@ -402,45 +359,58 @@ static inline struct ublk_io *ublk_get_io(struct ublk_queue *q, unsigned tag) return &q->ios[tag]; } -static inline int ublk_complete_io(struct ublk_queue *q, unsigned tag, int res) +static inline int ublk_complete_io(struct ublk_thread *t, struct ublk_queue *q, + unsigned tag, int res) { struct ublk_io *io = &q->ios[tag]; ublk_mark_io_done(io, res); - return ublk_queue_io_cmd(io); + return ublk_queue_io_cmd(t, io); } -static inline void ublk_queued_tgt_io(struct ublk_queue *q, unsigned tag, int queued) +static inline void ublk_queued_tgt_io(struct ublk_thread *t, struct ublk_queue *q, + unsigned tag, int queued) { if (queued < 0) - ublk_complete_io(q, tag, queued); + ublk_complete_io(t, q, tag, queued); else { struct ublk_io *io = ublk_get_io(q, tag); - io->t->io_inflight += queued; + t->io_inflight += queued; io->tgt_ios = queued; io->result = 0; } } -static inline int ublk_completed_tgt_io(struct ublk_queue *q, unsigned tag) +static inline int ublk_completed_tgt_io(struct ublk_thread *t, + struct ublk_queue *q, unsigned tag) { struct ublk_io *io = ublk_get_io(q, tag); - io->t->io_inflight--; + t->io_inflight--; return --io->tgt_ios == 0; } static inline int ublk_queue_use_zc(const struct ublk_queue *q) { - return q->state & UBLKSRV_ZC; + return q->flags & UBLK_F_SUPPORT_ZERO_COPY; } static inline int ublk_queue_use_auto_zc(const struct ublk_queue *q) { - return q->state & UBLKSRV_AUTO_BUF_REG; + return q->flags & UBLK_F_AUTO_BUF_REG; +} + +static inline int ublk_queue_auto_zc_fallback(const struct ublk_queue *q) +{ + return q->flags & UBLKS_Q_AUTO_BUF_REG_FALLBACK; +} + +static inline int ublk_queue_no_buf(const struct ublk_queue *q) +{ + return ublk_queue_use_zc(q) || ublk_queue_use_auto_zc(q); } extern const struct ublk_tgt_ops null_tgt_ops; @@ -451,10 +421,4 @@ extern const struct ublk_tgt_ops fault_inject_tgt_ops; void backing_file_tgt_deinit(struct ublk_dev *dev); int backing_file_tgt_init(struct ublk_dev *dev); -static inline unsigned int ilog2(unsigned int x) -{ - if (x == 0) - return 0; - return (sizeof(x) * 8 - 1) - __builtin_clz(x); -} #endif diff --git a/tools/testing/selftests/ublk/null.c b/tools/testing/selftests/ublk/null.c index afe0b99d77ee..280043f6b689 100644 --- a/tools/testing/selftests/ublk/null.c +++ b/tools/testing/selftests/ublk/null.c @@ -55,14 +55,15 @@ static void __setup_nop_io(int tag, const struct ublksrv_io_desc *iod, sqe->user_data = build_user_data(tag, ublk_op, 0, q_id, 1); } -static int null_queue_zc_io(struct ublk_queue *q, int tag) +static int null_queue_zc_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); struct io_uring_sqe *sqe[3]; - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, 3); + ublk_io_alloc_sqes(t, sqe, 3); - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); sqe[0]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; @@ -70,26 +71,28 @@ static int null_queue_zc_io(struct ublk_queue *q, int tag) __setup_nop_io(tag, iod, sqe[1], q->q_id); sqe[1]->flags |= IOSQE_IO_HARDLINK; - io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); + io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1); // buf register is marked as IOSQE_CQE_SKIP_SUCCESS return 2; } -static int null_queue_auto_zc_io(struct ublk_queue *q, int tag) +static int null_queue_auto_zc_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); struct io_uring_sqe *sqe[1]; - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, 1); + ublk_io_alloc_sqes(t, sqe, 1); __setup_nop_io(tag, iod, sqe[0], q->q_id); return 1; } -static void ublk_null_io_done(struct ublk_queue *q, int tag, - const struct io_uring_cqe *cqe) +static void ublk_null_io_done(struct ublk_thread *t, struct ublk_queue *q, + const struct io_uring_cqe *cqe) { + unsigned tag = user_data_to_tag(cqe->user_data); unsigned op = user_data_to_op(cqe->user_data); struct ublk_io *io = ublk_get_io(q, tag); @@ -105,11 +108,12 @@ static void ublk_null_io_done(struct ublk_queue *q, int tag, if (op == ublk_cmd_op_nr(UBLK_U_IO_REGISTER_IO_BUF)) io->tgt_ios += 1; - if (ublk_completed_tgt_io(q, tag)) - ublk_complete_io(q, tag, io->result); + if (ublk_completed_tgt_io(t, q, tag)) + ublk_complete_io(t, q, tag, io->result); } -static int ublk_null_queue_io(struct ublk_queue *q, int tag) +static int ublk_null_queue_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); unsigned auto_zc = ublk_queue_use_auto_zc(q); @@ -117,14 +121,14 @@ static int ublk_null_queue_io(struct ublk_queue *q, int tag) int queued; if (auto_zc && !ublk_io_auto_zc_fallback(iod)) - queued = null_queue_auto_zc_io(q, tag); + queued = null_queue_auto_zc_io(t, q, tag); else if (zc) - queued = null_queue_zc_io(q, tag); + queued = null_queue_zc_io(t, q, tag); else { - ublk_complete_io(q, tag, iod->nr_sectors << 9); + ublk_complete_io(t, q, tag, iod->nr_sectors << 9); return 0; } - ublk_queued_tgt_io(q, tag, queued); + ublk_queued_tgt_io(t, q, tag, queued); return 0; } @@ -134,7 +138,7 @@ static int ublk_null_queue_io(struct ublk_queue *q, int tag) */ static unsigned short ublk_null_buf_index(const struct ublk_queue *q, int tag) { - if (q->state & UBLKSRV_AUTO_BUF_REG_FALLBACK) + if (ublk_queue_auto_zc_fallback(q)) return (unsigned short)-1; return q->ios[tag].buf_index; } diff --git a/tools/testing/selftests/ublk/stripe.c b/tools/testing/selftests/ublk/stripe.c index 37d50bbf5f5e..791fa8dc1651 100644 --- a/tools/testing/selftests/ublk/stripe.c +++ b/tools/testing/selftests/ublk/stripe.c @@ -123,7 +123,8 @@ static inline enum io_uring_op stripe_to_uring_op( assert(0); } -static int stripe_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag) +static int stripe_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q, + const struct ublksrv_io_desc *iod, int tag) { const struct stripe_conf *conf = get_chunk_shift(q); unsigned auto_zc = (ublk_queue_use_auto_zc(q) != 0); @@ -138,10 +139,10 @@ static int stripe_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_ io->private_data = s; calculate_stripe_array(conf, iod, s, base); - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, s->nr + extra); + ublk_io_alloc_sqes(t, sqe, s->nr + extra); if (zc) { - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, io->buf_index); + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index); sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; sqe[0]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); @@ -167,7 +168,7 @@ static int stripe_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_ if (zc) { struct io_uring_sqe *unreg = sqe[s->nr + 1]; - io_uring_prep_buf_unregister(unreg, 0, tag, q->q_id, io->buf_index); + io_uring_prep_buf_unregister(unreg, q, tag, q->q_id, io->buf_index); unreg->user_data = build_user_data( tag, ublk_cmd_op_nr(unreg->cmd_op), 0, q->q_id, 1); } @@ -176,13 +177,14 @@ static int stripe_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_ return s->nr + zc; } -static int handle_flush(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag) +static int handle_flush(struct ublk_thread *t, struct ublk_queue *q, + const struct ublksrv_io_desc *iod, int tag) { const struct stripe_conf *conf = get_chunk_shift(q); struct io_uring_sqe *sqe[NR_STRIPE]; int i; - ublk_io_alloc_sqes(ublk_get_io(q, tag), sqe, conf->nr_files); + ublk_io_alloc_sqes(t, sqe, conf->nr_files); for (i = 0; i < conf->nr_files; i++) { io_uring_prep_fsync(sqe[i], i + 1, IORING_FSYNC_DATASYNC); io_uring_sqe_set_flags(sqe[i], IOSQE_FIXED_FILE); @@ -191,7 +193,8 @@ static int handle_flush(struct ublk_queue *q, const struct ublksrv_io_desc *iod, return conf->nr_files; } -static int stripe_queue_tgt_io(struct ublk_queue *q, int tag) +static int stripe_queue_tgt_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); unsigned ublk_op = ublksrv_get_op(iod); @@ -199,7 +202,7 @@ static int stripe_queue_tgt_io(struct ublk_queue *q, int tag) switch (ublk_op) { case UBLK_IO_OP_FLUSH: - ret = handle_flush(q, iod, tag); + ret = handle_flush(t, q, iod, tag); break; case UBLK_IO_OP_WRITE_ZEROES: case UBLK_IO_OP_DISCARD: @@ -207,7 +210,7 @@ static int stripe_queue_tgt_io(struct ublk_queue *q, int tag) break; case UBLK_IO_OP_READ: case UBLK_IO_OP_WRITE: - ret = stripe_queue_tgt_rw_io(q, iod, tag); + ret = stripe_queue_tgt_rw_io(t, q, iod, tag); break; default: ret = -EINVAL; @@ -218,17 +221,19 @@ static int stripe_queue_tgt_io(struct ublk_queue *q, int tag) return ret; } -static int ublk_stripe_queue_io(struct ublk_queue *q, int tag) +static int ublk_stripe_queue_io(struct ublk_thread *t, struct ublk_queue *q, + int tag) { - int queued = stripe_queue_tgt_io(q, tag); + int queued = stripe_queue_tgt_io(t, q, tag); - ublk_queued_tgt_io(q, tag, queued); + ublk_queued_tgt_io(t, q, tag, queued); return 0; } -static void ublk_stripe_io_done(struct ublk_queue *q, int tag, - const struct io_uring_cqe *cqe) +static void ublk_stripe_io_done(struct ublk_thread *t, struct ublk_queue *q, + const struct io_uring_cqe *cqe) { + unsigned tag = user_data_to_tag(cqe->user_data); const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); unsigned op = user_data_to_op(cqe->user_data); struct ublk_io *io = ublk_get_io(q, tag); @@ -257,13 +262,13 @@ static void ublk_stripe_io_done(struct ublk_queue *q, int tag, } } - if (ublk_completed_tgt_io(q, tag)) { + if (ublk_completed_tgt_io(t, q, tag)) { int res = io->result; if (!res) res = iod->nr_sectors << 9; - ublk_complete_io(q, tag, res); + ublk_complete_io(t, q, tag, res); free_stripe_array(io->private_data); io->private_data = NULL; diff --git a/tools/testing/selftests/ublk/test_stress_03.sh b/tools/testing/selftests/ublk/test_stress_03.sh index 6eef282d569f..3ed4c9b2d8c0 100755 --- a/tools/testing/selftests/ublk/test_stress_03.sh +++ b/tools/testing/selftests/ublk/test_stress_03.sh @@ -32,22 +32,23 @@ _create_backfile 2 128M ublk_io_and_remove 8G -t null -q 4 -z & ublk_io_and_remove 256M -t loop -q 4 -z "${UBLK_BACKFILES[0]}" & ublk_io_and_remove 256M -t stripe -q 4 -z "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & +wait if _have_feature "AUTO_BUF_REG"; then ublk_io_and_remove 8G -t null -q 4 --auto_zc & ublk_io_and_remove 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" & ublk_io_and_remove 256M -t stripe -q 4 --auto_zc "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & + wait fi -wait if _have_feature "PER_IO_DAEMON"; then ublk_io_and_remove 8G -t null -q 4 --auto_zc --nthreads 8 --per_io_tasks & ublk_io_and_remove 256M -t loop -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" & ublk_io_and_remove 256M -t stripe -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback --nthreads 8 --per_io_tasks & + wait fi -wait _cleanup_test "stress" _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 40d1437ca298..3f901db4d09d 100755 --- a/tools/testing/selftests/ublk/test_stress_04.sh +++ b/tools/testing/selftests/ublk/test_stress_04.sh @@ -28,14 +28,14 @@ _create_backfile 0 256M _create_backfile 1 128M _create_backfile 2 128M -ublk_io_and_kill_daemon 8G -t null -q 4 -z & -ublk_io_and_kill_daemon 256M -t loop -q 4 -z "${UBLK_BACKFILES[0]}" & +ublk_io_and_kill_daemon 8G -t null -q 4 -z --no_ublk_fixed_fd & +ublk_io_and_kill_daemon 256M -t loop -q 4 -z --no_ublk_fixed_fd "${UBLK_BACKFILES[0]}" & ublk_io_and_kill_daemon 256M -t stripe -q 4 -z "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & if _have_feature "AUTO_BUF_REG"; then ublk_io_and_kill_daemon 8G -t null -q 4 --auto_zc & ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" & - ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & + ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --no_ublk_fixed_fd "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & fi diff --git a/tools/testing/selftests/ublk/utils.h b/tools/testing/selftests/ublk/utils.h new file mode 100644 index 000000000000..36545d1567f1 --- /dev/null +++ b/tools/testing/selftests/ublk/utils.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef KUBLK_UTILS_H +#define KUBLK_UTILS_H + +#define __maybe_unused __attribute__((unused)) + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + unsigned long __mptr = (unsigned long)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); }) +#endif + +#define round_up(val, rnd) \ + (((val) + ((rnd) - 1)) & ~((rnd) - 1)) + +static inline unsigned int ilog2(unsigned int x) +{ + if (x == 0) + return 0; + return (sizeof(x) * 8 - 1) - __builtin_clz(x); +} + +#define UBLK_DBG_DEV (1U << 0) +#define UBLK_DBG_THREAD (1U << 1) +#define UBLK_DBG_IO_CMD (1U << 2) +#define UBLK_DBG_IO (1U << 3) +#define UBLK_DBG_CTRL_CMD (1U << 4) +#define UBLK_LOG (1U << 5) + +extern unsigned int ublk_dbg_mask; + +static inline void ublk_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); +} + +static inline void ublk_log(const char *fmt, ...) +{ + if (ublk_dbg_mask & UBLK_LOG) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + } +} + +static inline void ublk_dbg(int level, const char *fmt, ...) +{ + if (level & ublk_dbg_mask) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + } +} + +#endif diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 12a0614b9fd4..918a2caa070e 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -12,7 +12,7 @@ TEST_GEN_PROGS += vdso_test_correctness TEST_GEN_PROGS += vdso_test_getrandom TEST_GEN_PROGS += vdso_test_chacha -CFLAGS := -std=gnu99 -O2 +CFLAGS := -std=gnu99 -O2 -Wall -Wstrict-prototypes ifeq ($(CONFIG_X86_32),y) LDLIBS += -lgcc_s diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index 722260f97561..5fdd0f362337 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h @@ -58,6 +58,7 @@ #define VDSO_NAMES 1 #endif +__attribute__((unused)) static const char *versions[7] = { "LINUX_2.6", "LINUX_2.6.15", @@ -68,6 +69,7 @@ static const char *versions[7] = { "LINUX_5.10" }; +__attribute__((unused)) static const char *names[2][7] = { { "__kernel_gettimeofday", diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c deleted file mode 100644 index 9ce795b806f0..000000000000 --- a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vdso_test_gettimeofday.c: Sample code to test parse_vdso.c and - * vDSO gettimeofday() - * Copyright (c) 2014 Andy Lutomirski - * - * Compile with: - * gcc -std=gnu99 vdso_test_gettimeofday.c parse_vdso_gettimeofday.c - * - * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. - */ - -#include -#ifndef NOLIBC -#include -#include -#endif - -#include "../kselftest.h" -#include "parse_vdso.h" -#include "vdso_config.h" -#include "vdso_call.h" - -int main(int argc, char **argv) -{ - const char *version = versions[VDSO_VERSION]; - const char **name = (const char **)&names[VDSO_NAMES]; - - unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); - if (!sysinfo_ehdr) { - printf("AT_SYSINFO_EHDR is not present!\n"); - return KSFT_SKIP; - } - - vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); - - /* Find gettimeofday. */ - typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); - gtod_t gtod = (gtod_t)vdso_sym(version, name[0]); - - if (!gtod) { - printf("Could not find %s\n", name[0]); - return KSFT_SKIP; - } - - struct timeval tv; - long ret = VDSO_CALL(gtod, 2, &tv, 0); - - if (ret == 0) { - printf("The time is %lld.%06lld\n", - (long long)tv.tv_sec, (long long)tv.tv_usec); - } else { - printf("%s failed\n", name[0]); - return KSFT_FAIL; - } - - return 0; -} diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c new file mode 120000 index 000000000000..4d3d96f1e440 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c @@ -0,0 +1 @@ +vdso_test_gettimeofday.c \ No newline at end of file diff --git a/tools/testing/selftests/vDSO/vdso_test_chacha.c b/tools/testing/selftests/vDSO/vdso_test_chacha.c index 8757f738b0b1..0aad682b12c8 100644 --- a/tools/testing/selftests/vDSO/vdso_test_chacha.c +++ b/tools/testing/selftests/vDSO/vdso_test_chacha.c @@ -76,7 +76,8 @@ static void reference_chacha20_blocks(uint8_t *dst_bytes, const uint32_t *key, u void __weak __arch_chacha20_blocks_nostack(uint8_t *dst_bytes, const uint32_t *key, uint32_t *counter, size_t nblocks) { - ksft_exit_skip("Not implemented on architecture\n"); + ksft_test_result_skip("Not implemented on architecture\n"); + ksft_finished(); } int main(int argc, char *argv[]) diff --git a/tools/testing/selftests/vDSO/vdso_test_clock_getres.c b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c index 38d46a8bf7cb..b5d5f59f725a 100644 --- a/tools/testing/selftests/vDSO/vdso_test_clock_getres.c +++ b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c @@ -13,7 +13,6 @@ #define _GNU_SOURCE #include -#include #include #include #include diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 5fb97ad67eea..da651cf53c6c 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -108,7 +108,7 @@ static void *vsyscall_getcpu(void) } -static void fill_function_pointers() +static void fill_function_pointers(void) { void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); diff --git a/tools/testing/selftests/vDSO/vdso_test_getrandom.c b/tools/testing/selftests/vDSO/vdso_test_getrandom.c index 95057f7567db..dd1132508a0d 100644 --- a/tools/testing/selftests/vDSO/vdso_test_getrandom.c +++ b/tools/testing/selftests/vDSO/vdso_test_getrandom.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "../kselftest.h" @@ -101,6 +100,7 @@ static void *vgetrandom_get_state(void) return state; } +__attribute__((unused)) /* Example for libc implementors */ static void vgetrandom_put_state(void *state) { if (!state) @@ -242,6 +242,7 @@ static void kselftest(void) pid_t child; ksft_print_header(); + vgetrandom_init(); ksft_set_plan(2); for (size_t i = 0; i < 1000; ++i) { @@ -265,7 +266,7 @@ static void kselftest(void) } for (;;) { struct ptrace_syscall_info info = { 0 }; - int status, ret; + int status; ksft_assert(waitpid(child, &status, 0) >= 0); if (WIFEXITED(status)) { ksft_assert(WEXITSTATUS(status) == 0); @@ -295,8 +296,6 @@ static void usage(const char *argv0) int main(int argc, char *argv[]) { - vgetrandom_init(); - if (argc == 1) { kselftest(); return 0; @@ -306,6 +305,9 @@ int main(int argc, char *argv[]) usage(argv[0]); return 1; } + + vgetrandom_init(); + if (!strcmp(argv[1], "bench-single")) bench_single(); else if (!strcmp(argv[1], "bench-multi")) diff --git a/tools/testing/selftests/vsock/.gitignore b/tools/testing/selftests/vsock/.gitignore new file mode 100644 index 000000000000..9c5bf379480f --- /dev/null +++ b/tools/testing/selftests/vsock/.gitignore @@ -0,0 +1,2 @@ +vmtest.log +vsock_test diff --git a/tools/testing/selftests/vsock/Makefile b/tools/testing/selftests/vsock/Makefile new file mode 100644 index 000000000000..c407c0afd938 --- /dev/null +++ b/tools/testing/selftests/vsock/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 + +CURDIR := $(abspath .) +TOOLSDIR := $(abspath ../../..) +VSOCK_TEST_DIR := $(TOOLSDIR)/testing/vsock +VSOCK_TEST_SRCS := $(wildcard $(VSOCK_TEST_DIR)/*.c $(VSOCK_TEST_DIR)/*.h) + +$(OUTPUT)/vsock_test: $(VSOCK_TEST_DIR)/vsock_test + install -m 755 $< $@ + +$(VSOCK_TEST_DIR)/vsock_test: $(VSOCK_TEST_SRCS) + $(MAKE) -C $(VSOCK_TEST_DIR) vsock_test +TEST_PROGS += vmtest.sh +TEST_GEN_FILES := vsock_test + +include ../lib.mk + diff --git a/tools/testing/selftests/vsock/config b/tools/testing/selftests/vsock/config new file mode 100644 index 000000000000..5f0a4f17dfc9 --- /dev/null +++ b/tools/testing/selftests/vsock/config @@ -0,0 +1,111 @@ +CONFIG_BLK_DEV_INITRD=y +CONFIG_BPF=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_HAVE_EBPF_JIT=y +CONFIG_BPF_EVENTS=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_HAVE_KPROBES=y +CONFIG_KPROBES=y +CONFIG_KPROBE_EVENTS=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_UPROBES=y +CONFIG_UPROBE_EVENTS=y +CONFIG_DEBUG_FS=y +CONFIG_FW_CFG_SYSFS=y +CONFIG_FW_CFG_SYSFS_CMDLINE=y +CONFIG_DRM=y +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_DRM_VIRTIO_GPU_KMS=y +CONFIG_DRM_BOCHS=y +CONFIG_VIRTIO_IOMMU=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SEQUENCER=y +CONFIG_SND_PCI=y +CONFIG_SND_INTEL8X0=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SECURITYFS=y +CONFIG_CGROUP_BPF=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_FUSE_FS=y +CONFIG_VIRTIO_FS=y +CONFIG_SERIO=y +CONFIG_PCI=y +CONFIG_INPUT=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_X86_VERBOSE_BOOTUP=y +CONFIG_VGA_CONSOLE=y +CONFIG_FB=y +CONFIG_FB_VESA=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_DRV_CMOS=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_KVM_GUEST=y +CONFIG_KVM=y +CONFIG_KVM_INTEL=y +CONFIG_KVM_AMD=y +CONFIG_VSOCKETS=y +CONFIG_VSOCKETS_DIAG=y +CONFIG_VSOCKETS_LOOPBACK=y +CONFIG_VMWARE_VMCI_VSOCKETS=y +CONFIG_VIRTIO_VSOCKETS=y +CONFIG_VIRTIO_VSOCKETS_COMMON=y +CONFIG_HYPERV_VSOCKETS=y +CONFIG_VMWARE_VMCI=y +CONFIG_VHOST_VSOCK=y +CONFIG_HYPERV=y +CONFIG_UEVENT_HELPER=n +CONFIG_VIRTIO=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_NET=y +CONFIG_NET_CORE=y +CONFIG_NETDEVICES=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_INET=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_9P_FS=y +CONFIG_VIRTIO_NET=y +CONFIG_CMDLINE_OVERRIDE=n +CONFIG_BINFMT_SCRIPT=y +CONFIG_SHMEM=y +CONFIG_TMPFS=y +CONFIG_UNIX=y +CONFIG_MODULE_SIG_FORCE=n +CONFIG_DEVTMPFS=y +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_UNIX98_PTYS=y +CONFIG_EARLY_PRINTK=y +CONFIG_INOTIFY_USER=y +CONFIG_BLOCK=y +CONFIG_SCSI_LOWLEVEL=y +CONFIG_SCSI=y +CONFIG_SCSI_VIRTIO=y +CONFIG_BLK_DEV_SD=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_I6300ESB_WDT=y +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +CONFIG_OVERLAY_FS=y +CONFIG_DAX=y +CONFIG_DAX_DRIVER=y +CONFIG_FS_DAX=y +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTREMOVE=y +CONFIG_ZONE_DEVICE=y diff --git a/tools/testing/selftests/vsock/settings b/tools/testing/selftests/vsock/settings new file mode 100644 index 000000000000..694d70710ff0 --- /dev/null +++ b/tools/testing/selftests/vsock/settings @@ -0,0 +1 @@ +timeout=300 diff --git a/tools/testing/selftests/vsock/vmtest.sh b/tools/testing/selftests/vsock/vmtest.sh new file mode 100755 index 000000000000..edacebfc1632 --- /dev/null +++ b/tools/testing/selftests/vsock/vmtest.sh @@ -0,0 +1,487 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2025 Meta Platforms, Inc. and affiliates +# +# Dependencies: +# * virtme-ng +# * busybox-static (used by virtme-ng) +# * qemu (used by virtme-ng) + +readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +readonly KERNEL_CHECKOUT=$(realpath "${SCRIPT_DIR}"/../../../../) + +source "${SCRIPT_DIR}"/../kselftest/ktap_helpers.sh + +readonly VSOCK_TEST="${SCRIPT_DIR}"/vsock_test +readonly TEST_GUEST_PORT=51000 +readonly TEST_HOST_PORT=50000 +readonly TEST_HOST_PORT_LISTENER=50001 +readonly SSH_GUEST_PORT=22 +readonly SSH_HOST_PORT=2222 +readonly VSOCK_CID=1234 +readonly WAIT_PERIOD=3 +readonly WAIT_PERIOD_MAX=60 +readonly WAIT_TOTAL=$(( WAIT_PERIOD * WAIT_PERIOD_MAX )) +readonly QEMU_PIDFILE=$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + +# virtme-ng offers a netdev for ssh when using "--ssh", but we also need a +# control port forwarded for vsock_test. Because virtme-ng doesn't support +# adding an additional port to forward to the device created from "--ssh" and +# virtme-init mistakenly sets identical IPs to the ssh device and additional +# devices, we instead opt out of using --ssh, add the device manually, and also +# add the kernel cmdline options that virtme-init uses to setup the interface. +readonly QEMU_TEST_PORT_FWD="hostfwd=tcp::${TEST_HOST_PORT}-:${TEST_GUEST_PORT}" +readonly QEMU_SSH_PORT_FWD="hostfwd=tcp::${SSH_HOST_PORT}-:${SSH_GUEST_PORT}" +readonly QEMU_OPTS="\ + -netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \ + -device virtio-net-pci,netdev=n0 \ + -device vhost-vsock-pci,guest-cid=${VSOCK_CID} \ + --pidfile ${QEMU_PIDFILE} \ +" +readonly KERNEL_CMDLINE="\ + virtme.dhcp net.ifnames=0 biosdevname=0 \ + virtme.ssh virtme_ssh_channel=tcp virtme_ssh_user=$USER \ +" +readonly LOG=$(mktemp /tmp/vsock_vmtest_XXXX.log) +readonly TEST_NAMES=(vm_server_host_client vm_client_host_server vm_loopback) +readonly TEST_DESCS=( + "Run vsock_test in server mode on the VM and in client mode on the host." + "Run vsock_test in client mode on the VM and in server mode on the host." + "Run vsock_test using the loopback transport in the VM." +) + +VERBOSE=0 + +usage() { + local name + local desc + local i + + echo + echo "$0 [OPTIONS] [TEST]..." + echo "If no TEST argument is given, all tests will be run." + echo + echo "Options" + echo " -b: build the kernel from the current source tree and use it for guest VMs" + echo " -q: set the path to or name of qemu binary" + echo " -v: verbose output" + echo + echo "Available tests" + + for ((i = 0; i < ${#TEST_NAMES[@]}; i++)); do + name=${TEST_NAMES[${i}]} + desc=${TEST_DESCS[${i}]} + printf "\t%-35s%-35s\n" "${name}" "${desc}" + done + echo + + exit 1 +} + +die() { + echo "$*" >&2 + exit "${KSFT_FAIL}" +} + +vm_ssh() { + ssh -q -o UserKnownHostsFile=/dev/null -p ${SSH_HOST_PORT} localhost "$@" + return $? +} + +cleanup() { + if [[ -s "${QEMU_PIDFILE}" ]]; then + pkill -SIGTERM -F "${QEMU_PIDFILE}" > /dev/null 2>&1 + fi + + # If failure occurred during or before qemu start up, then we need + # to clean this up ourselves. + if [[ -e "${QEMU_PIDFILE}" ]]; then + rm "${QEMU_PIDFILE}" + fi +} + +check_args() { + local found + + for arg in "$@"; do + found=0 + for name in "${TEST_NAMES[@]}"; do + if [[ "${name}" = "${arg}" ]]; then + found=1 + break + fi + done + + if [[ "${found}" -eq 0 ]]; then + echo "${arg} is not an available test" >&2 + usage + fi + done + + for arg in "$@"; do + if ! command -v > /dev/null "test_${arg}"; then + echo "Test ${arg} not found" >&2 + usage + fi + done +} + +check_deps() { + for dep in vng ${QEMU} busybox pkill ssh; do + if [[ ! -x $(command -v "${dep}") ]]; then + echo -e "skip: dependency ${dep} not found!\n" + exit "${KSFT_SKIP}" + fi + done + + if [[ ! -x $(command -v "${VSOCK_TEST}") ]]; then + printf "skip: %s not found!" "${VSOCK_TEST}" + printf " Please build the kselftest vsock target.\n" + exit "${KSFT_SKIP}" + fi +} + +check_vng() { + local tested_versions + local version + local ok + + tested_versions=("1.33" "1.36") + version="$(vng --version)" + + ok=0 + for tv in "${tested_versions[@]}"; do + if [[ "${version}" == *"${tv}"* ]]; then + ok=1 + break + fi + done + + if [[ ! "${ok}" -eq 1 ]]; then + printf "warning: vng version '%s' has not been tested and may " "${version}" >&2 + printf "not function properly.\n\tThe following versions have been tested: " >&2 + echo "${tested_versions[@]}" >&2 + fi +} + +handle_build() { + if [[ ! "${BUILD}" -eq 1 ]]; then + return + fi + + if [[ ! -d "${KERNEL_CHECKOUT}" ]]; then + echo "-b requires vmtest.sh called from the kernel source tree" >&2 + exit 1 + fi + + pushd "${KERNEL_CHECKOUT}" &>/dev/null + + if ! vng --kconfig --config "${SCRIPT_DIR}"/config; then + die "failed to generate .config for kernel source tree (${KERNEL_CHECKOUT})" + fi + + if ! make -j$(nproc); then + die "failed to build kernel from source tree (${KERNEL_CHECKOUT})" + fi + + popd &>/dev/null +} + +vm_start() { + local logfile=/dev/null + local verbose_opt="" + local kernel_opt="" + local qemu + + qemu=$(command -v "${QEMU}") + + if [[ "${VERBOSE}" -eq 1 ]]; then + verbose_opt="--verbose" + logfile=/dev/stdout + fi + + if [[ "${BUILD}" -eq 1 ]]; then + kernel_opt="${KERNEL_CHECKOUT}" + fi + + vng \ + --run \ + ${kernel_opt} \ + ${verbose_opt} \ + --qemu-opts="${QEMU_OPTS}" \ + --qemu="${qemu}" \ + --user root \ + --append "${KERNEL_CMDLINE}" \ + --rw &> ${logfile} & + + if ! timeout ${WAIT_TOTAL} \ + bash -c 'while [[ ! -s '"${QEMU_PIDFILE}"' ]]; do sleep 1; done; exit 0'; then + die "failed to boot VM" + fi +} + +vm_wait_for_ssh() { + local i + + i=0 + while true; do + if [[ ${i} -gt ${WAIT_PERIOD_MAX} ]]; then + die "Timed out waiting for guest ssh" + fi + if vm_ssh -- true; then + break + fi + i=$(( i + 1 )) + sleep ${WAIT_PERIOD} + done +} + +# derived from selftests/net/net_helper.sh +wait_for_listener() +{ + local port=$1 + local interval=$2 + local max_intervals=$3 + local protocol=tcp + local pattern + local i + + pattern=":$(printf "%04X" "${port}") " + + # for tcp protocol additionally check the socket state + [ "${protocol}" = "tcp" ] && pattern="${pattern}0A" + for i in $(seq "${max_intervals}"); do + if awk '{print $2" "$4}' /proc/net/"${protocol}"* | \ + grep -q "${pattern}"; then + break + fi + sleep "${interval}" + done +} + +vm_wait_for_listener() { + local port=$1 + + vm_ssh < ${redirect} + else + __log_args "$@" | tee -a "${LOG}" > ${redirect} + fi +} + +log_setup() { + log "setup" "$@" +} + +log_host() { + local testname=$1 + + shift + log "test:${testname}:host" "$@" +} + +log_guest() { + local testname=$1 + + shift + log "test:${testname}:guest" "$@" +} + +test_vm_server_host_client() { + local testname="${FUNCNAME[0]#test_}" + + vm_ssh -- "${VSOCK_TEST}" \ + --mode=server \ + --control-port="${TEST_GUEST_PORT}" \ + --peer-cid=2 \ + 2>&1 | log_guest "${testname}" & + + vm_wait_for_listener "${TEST_GUEST_PORT}" + + ${VSOCK_TEST} \ + --mode=client \ + --control-host=127.0.0.1 \ + --peer-cid="${VSOCK_CID}" \ + --control-port="${TEST_HOST_PORT}" 2>&1 | log_host "${testname}" + + return $? +} + +test_vm_client_host_server() { + local testname="${FUNCNAME[0]#test_}" + + ${VSOCK_TEST} \ + --mode "server" \ + --control-port "${TEST_HOST_PORT_LISTENER}" \ + --peer-cid "${VSOCK_CID}" 2>&1 | log_host "${testname}" & + + host_wait_for_listener + + vm_ssh -- "${VSOCK_TEST}" \ + --mode=client \ + --control-host=10.0.2.2 \ + --peer-cid=2 \ + --control-port="${TEST_HOST_PORT_LISTENER}" 2>&1 | log_guest "${testname}" + + return $? +} + +test_vm_loopback() { + local testname="${FUNCNAME[0]#test_}" + local port=60000 # non-forwarded local port + + vm_ssh -- "${VSOCK_TEST}" \ + --mode=server \ + --control-port="${port}" \ + --peer-cid=1 2>&1 | log_guest "${testname}" & + + vm_wait_for_listener "${port}" + + vm_ssh -- "${VSOCK_TEST}" \ + --mode=client \ + --control-host="127.0.0.1" \ + --control-port="${port}" \ + --peer-cid=1 2>&1 | log_guest "${testname}" + + return $? +} + +run_test() { + local host_oops_cnt_before + local host_warn_cnt_before + local vm_oops_cnt_before + local vm_warn_cnt_before + local host_oops_cnt_after + local host_warn_cnt_after + local vm_oops_cnt_after + local vm_warn_cnt_after + local name + local rc + + host_oops_cnt_before=$(dmesg | grep -c -i 'Oops') + host_warn_cnt_before=$(dmesg --level=warn | wc -l) + vm_oops_cnt_before=$(vm_ssh -- dmesg | grep -c -i 'Oops') + vm_warn_cnt_before=$(vm_ssh -- dmesg --level=warn | wc -l) + + name=$(echo "${1}" | awk '{ print $1 }') + eval test_"${name}" + rc=$? + + host_oops_cnt_after=$(dmesg | grep -i 'Oops' | wc -l) + if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then + echo "FAIL: kernel oops detected on host" | log_host "${name}" + rc=$KSFT_FAIL + fi + + host_warn_cnt_after=$(dmesg --level=warn | wc -l) + if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then + echo "FAIL: kernel warning detected on host" | log_host "${name}" + rc=$KSFT_FAIL + fi + + vm_oops_cnt_after=$(vm_ssh -- dmesg | grep -i 'Oops' | wc -l) + if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then + echo "FAIL: kernel oops detected on vm" | log_host "${name}" + rc=$KSFT_FAIL + fi + + vm_warn_cnt_after=$(vm_ssh -- dmesg --level=warn | wc -l) + if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then + echo "FAIL: kernel warning detected on vm" | log_host "${name}" + rc=$KSFT_FAIL + fi + + return "${rc}" +} + +QEMU="qemu-system-$(uname -m)" + +while getopts :hvsq:b o +do + case $o in + v) VERBOSE=1;; + b) BUILD=1;; + q) QEMU=$OPTARG;; + h|*) usage;; + esac +done +shift $((OPTIND-1)) + +trap cleanup EXIT + +if [[ ${#} -eq 0 ]]; then + ARGS=("${TEST_NAMES[@]}") +else + ARGS=("$@") +fi + +check_args "${ARGS[@]}" +check_deps +check_vng +handle_build + +echo "1..${#ARGS[@]}" + +log_setup "Booting up VM" +vm_start +vm_wait_for_ssh +log_setup "VM booted up" + +cnt_pass=0 +cnt_fail=0 +cnt_skip=0 +cnt_total=0 +for arg in "${ARGS[@]}"; do + run_test "${arg}" + rc=$? + if [[ ${rc} -eq $KSFT_PASS ]]; then + cnt_pass=$(( cnt_pass + 1 )) + echo "ok ${cnt_total} ${arg}" + elif [[ ${rc} -eq $KSFT_SKIP ]]; then + cnt_skip=$(( cnt_skip + 1 )) + echo "ok ${cnt_total} ${arg} # SKIP" + elif [[ ${rc} -eq $KSFT_FAIL ]]; then + cnt_fail=$(( cnt_fail + 1 )) + echo "not ok ${cnt_total} ${arg} # exit=$rc" + fi + cnt_total=$(( cnt_total + 1 )) +done + +echo "SUMMARY: PASS=${cnt_pass} SKIP=${cnt_skip} FAIL=${cnt_fail}" +echo "Log: ${LOG}" + +if [ $((cnt_pass + cnt_skip)) -eq ${cnt_total} ]; then + exit "$KSFT_PASS" +else + exit "$KSFT_FAIL" +fi diff --git a/tools/testing/selftests/wireguard/qemu/kernel.config b/tools/testing/selftests/wireguard/qemu/kernel.config index f314d3789f17..0a5381717e9f 100644 --- a/tools/testing/selftests/wireguard/qemu/kernel.config +++ b/tools/testing/selftests/wireguard/qemu/kernel.config @@ -16,9 +16,13 @@ CONFIG_NETFILTER_ADVANCED=y CONFIG_NF_CONNTRACK=y CONFIG_NF_NAT=y CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XTABLES_LEGACY=y CONFIG_NETFILTER_XT_NAT=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_REJECT=m CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_MANGLE=y diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index f703fcfe9f7c..83148875a12c 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -12,7 +12,7 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh "$(CC)" trivial_program.c -no-pie) TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \ check_initial_reg_state sigreturn iopl ioperm \ - test_vsyscall mov_ss_trap \ + test_vsyscall mov_ss_trap sigtrap_loop \ syscall_arg_fault fsgsbase_restore sigaltstack TARGETS_C_BOTHBITS += nx_stack TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ diff --git a/tools/testing/selftests/x86/sigtrap_loop.c b/tools/testing/selftests/x86/sigtrap_loop.c new file mode 100644 index 000000000000..9d065479e89f --- /dev/null +++ b/tools/testing/selftests/x86/sigtrap_loop.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Intel Corporation + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#ifdef __x86_64__ +# define REG_IP REG_RIP +#else +# define REG_IP REG_EIP +#endif + +static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = handler; + sa.sa_flags = SA_SIGINFO | flags; + sigemptyset(&sa.sa_mask); + + if (sigaction(sig, &sa, 0)) + err(1, "sigaction"); + + return; +} + +static void sigtrap(int sig, siginfo_t *info, void *ctx_void) +{ + ucontext_t *ctx = (ucontext_t *)ctx_void; + static unsigned int loop_count_on_same_ip; + static unsigned long last_trap_ip; + + if (last_trap_ip == ctx->uc_mcontext.gregs[REG_IP]) { + printf("\tTrapped at %016lx\n", last_trap_ip); + + /* + * If the same IP is hit more than 10 times in a row, it is + * _considered_ an infinite loop. + */ + if (++loop_count_on_same_ip > 10) { + printf("[FAIL]\tDetected SIGTRAP infinite loop\n"); + exit(1); + } + + return; + } + + loop_count_on_same_ip = 0; + last_trap_ip = ctx->uc_mcontext.gregs[REG_IP]; + printf("\tTrapped at %016lx\n", last_trap_ip); +} + +int main(int argc, char *argv[]) +{ + sethandler(SIGTRAP, sigtrap, 0); + + /* + * Set the Trap Flag (TF) to single-step the test code, therefore to + * trigger a SIGTRAP signal after each instruction until the TF is + * cleared. + * + * Because the arithmetic flags are not significant here, the TF is + * set by pushing 0x302 onto the stack and then popping it into the + * flags register. + * + * Four instructions in the following asm code are executed with the + * TF set, thus the SIGTRAP handler is expected to run four times. + */ + printf("[RUN]\tSIGTRAP infinite loop detection\n"); + asm volatile( +#ifdef __x86_64__ + /* + * Avoid clobbering the redzone + * + * Equivalent to "sub $128, %rsp", however -128 can be encoded + * in a single byte immediate while 128 uses 4 bytes. + */ + "add $-128, %rsp\n\t" +#endif + "push $0x302\n\t" + "popf\n\t" + "nop\n\t" + "nop\n\t" + "push $0x202\n\t" + "popf\n\t" +#ifdef __x86_64__ + "sub $-128, %rsp\n\t" +#endif + ); + + printf("[OK]\tNo SIGTRAP infinite loop detected\n"); + return 0; +} diff --git a/tools/testing/shared/linux/idr.h b/tools/testing/shared/linux/idr.h index 4e342f2e37cf..676c5564e33f 100644 --- a/tools/testing/shared/linux/idr.h +++ b/tools/testing/shared/linux/idr.h @@ -1 +1,5 @@ +/* Avoid duplicate definitions due to system headers. */ +#ifdef __CONCAT +#undef __CONCAT +#endif #include "../../../../include/linux/idr.h" diff --git a/tools/testing/vma/vma.c b/tools/testing/vma/vma.c index 2be7597a2ac2..656e1c75b711 100644 --- a/tools/testing/vma/vma.c +++ b/tools/testing/vma/vma.c @@ -65,7 +65,7 @@ static struct vm_area_struct *alloc_vma(struct mm_struct *mm, unsigned long start, unsigned long end, pgoff_t pgoff, - vm_flags_t flags) + vm_flags_t vm_flags) { struct vm_area_struct *ret = vm_area_alloc(mm); @@ -75,7 +75,7 @@ static struct vm_area_struct *alloc_vma(struct mm_struct *mm, ret->vm_start = start; ret->vm_end = end; ret->vm_pgoff = pgoff; - ret->__vm_flags = flags; + ret->__vm_flags = vm_flags; vma_assert_detached(ret); return ret; @@ -103,9 +103,9 @@ static struct vm_area_struct *alloc_and_link_vma(struct mm_struct *mm, unsigned long start, unsigned long end, pgoff_t pgoff, - vm_flags_t flags) + vm_flags_t vm_flags) { - struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, flags); + struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, vm_flags); if (vma == NULL) return NULL; @@ -172,7 +172,7 @@ static int expand_existing(struct vma_merge_struct *vmg) * specified new range. */ static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start, - unsigned long end, pgoff_t pgoff, vm_flags_t flags) + unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags) { vma_iter_set(vmg->vmi, start); @@ -184,7 +184,7 @@ static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start, vmg->start = start; vmg->end = end; vmg->pgoff = pgoff; - vmg->flags = flags; + vmg->vm_flags = vm_flags; vmg->just_expand = false; vmg->__remove_middle = false; @@ -195,10 +195,10 @@ static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start, /* Helper function to set both the VMG range and its anon_vma. */ static void vmg_set_range_anon_vma(struct vma_merge_struct *vmg, unsigned long start, - unsigned long end, pgoff_t pgoff, vm_flags_t flags, + unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags, struct anon_vma *anon_vma) { - vmg_set_range(vmg, start, end, pgoff, flags); + vmg_set_range(vmg, start, end, pgoff, vm_flags); vmg->anon_vma = anon_vma; } @@ -211,12 +211,12 @@ static void vmg_set_range_anon_vma(struct vma_merge_struct *vmg, unsigned long s static struct vm_area_struct *try_merge_new_vma(struct mm_struct *mm, struct vma_merge_struct *vmg, unsigned long start, unsigned long end, - pgoff_t pgoff, vm_flags_t flags, + pgoff_t pgoff, vm_flags_t vm_flags, bool *was_merged) { struct vm_area_struct *merged; - vmg_set_range(vmg, start, end, pgoff, flags); + vmg_set_range(vmg, start, end, pgoff, vm_flags); merged = merge_new(vmg); if (merged) { @@ -229,7 +229,7 @@ static struct vm_area_struct *try_merge_new_vma(struct mm_struct *mm, ASSERT_EQ(vmg->state, VMA_MERGE_NOMERGE); - return alloc_and_link_vma(mm, start, end, pgoff, flags); + return alloc_and_link_vma(mm, start, end, pgoff, vm_flags); } /* @@ -301,17 +301,17 @@ static void vma_set_dummy_anon_vma(struct vm_area_struct *vma, static bool test_simple_merge(void) { struct vm_area_struct *vma; - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; - struct vm_area_struct *vma_left = alloc_vma(&mm, 0, 0x1000, 0, flags); - struct vm_area_struct *vma_right = alloc_vma(&mm, 0x2000, 0x3000, 2, flags); + struct vm_area_struct *vma_left = alloc_vma(&mm, 0, 0x1000, 0, vm_flags); + struct vm_area_struct *vma_right = alloc_vma(&mm, 0x2000, 0x3000, 2, vm_flags); VMA_ITERATOR(vmi, &mm, 0x1000); struct vma_merge_struct vmg = { .mm = &mm, .vmi = &vmi, .start = 0x1000, .end = 0x2000, - .flags = flags, + .vm_flags = vm_flags, .pgoff = 1, }; @@ -324,7 +324,7 @@ static bool test_simple_merge(void) ASSERT_EQ(vma->vm_start, 0); ASSERT_EQ(vma->vm_end, 0x3000); ASSERT_EQ(vma->vm_pgoff, 0); - ASSERT_EQ(vma->vm_flags, flags); + ASSERT_EQ(vma->vm_flags, vm_flags); detach_free_vma(vma); mtree_destroy(&mm.mm_mt); @@ -335,9 +335,9 @@ static bool test_simple_merge(void) static bool test_simple_modify(void) { struct vm_area_struct *vma; - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; - struct vm_area_struct *init_vma = alloc_vma(&mm, 0, 0x3000, 0, flags); + struct vm_area_struct *init_vma = alloc_vma(&mm, 0, 0x3000, 0, vm_flags); VMA_ITERATOR(vmi, &mm, 0x1000); ASSERT_FALSE(attach_vma(&mm, init_vma)); @@ -394,13 +394,13 @@ static bool test_simple_modify(void) static bool test_simple_expand(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; - struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, flags); + struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, vm_flags); VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { .vmi = &vmi, - .middle = vma, + .target = vma, .start = 0, .end = 0x3000, .pgoff = 0, @@ -422,9 +422,9 @@ static bool test_simple_expand(void) static bool test_simple_shrink(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; - struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x3000, 0, flags); + struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x3000, 0, vm_flags); VMA_ITERATOR(vmi, &mm, 0); ASSERT_FALSE(attach_vma(&mm, vma)); @@ -443,7 +443,7 @@ static bool test_simple_shrink(void) static bool test_merge_new(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { @@ -473,18 +473,18 @@ static bool test_merge_new(void) * 0123456789abc * AA B CC */ - vma_a = alloc_and_link_vma(&mm, 0, 0x2000, 0, flags); + vma_a = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags); ASSERT_NE(vma_a, NULL); /* We give each VMA a single avc so we can test anon_vma duplication. */ INIT_LIST_HEAD(&vma_a->anon_vma_chain); list_add(&dummy_anon_vma_chain_a.same_vma, &vma_a->anon_vma_chain); - vma_b = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, flags); + vma_b = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vm_flags); ASSERT_NE(vma_b, NULL); INIT_LIST_HEAD(&vma_b->anon_vma_chain); list_add(&dummy_anon_vma_chain_b.same_vma, &vma_b->anon_vma_chain); - vma_c = alloc_and_link_vma(&mm, 0xb000, 0xc000, 0xb, flags); + vma_c = alloc_and_link_vma(&mm, 0xb000, 0xc000, 0xb, vm_flags); ASSERT_NE(vma_c, NULL); INIT_LIST_HEAD(&vma_c->anon_vma_chain); list_add(&dummy_anon_vma_chain_c.same_vma, &vma_c->anon_vma_chain); @@ -495,7 +495,7 @@ static bool test_merge_new(void) * 0123456789abc * AA B ** CC */ - vma_d = try_merge_new_vma(&mm, &vmg, 0x7000, 0x9000, 7, flags, &merged); + vma_d = try_merge_new_vma(&mm, &vmg, 0x7000, 0x9000, 7, vm_flags, &merged); ASSERT_NE(vma_d, NULL); INIT_LIST_HEAD(&vma_d->anon_vma_chain); list_add(&dummy_anon_vma_chain_d.same_vma, &vma_d->anon_vma_chain); @@ -510,7 +510,7 @@ static bool test_merge_new(void) */ vma_a->vm_ops = &vm_ops; /* This should have no impact. */ vma_b->anon_vma = &dummy_anon_vma; - vma = try_merge_new_vma(&mm, &vmg, 0x2000, 0x3000, 2, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0x2000, 0x3000, 2, vm_flags, &merged); ASSERT_EQ(vma, vma_a); /* Merge with A, delete B. */ ASSERT_TRUE(merged); @@ -527,7 +527,7 @@ static bool test_merge_new(void) * 0123456789abc * AAAA* DD CC */ - vma = try_merge_new_vma(&mm, &vmg, 0x4000, 0x5000, 4, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0x4000, 0x5000, 4, vm_flags, &merged); ASSERT_EQ(vma, vma_a); /* Extend A. */ ASSERT_TRUE(merged); @@ -546,7 +546,7 @@ static bool test_merge_new(void) */ vma_d->anon_vma = &dummy_anon_vma; vma_d->vm_ops = &vm_ops; /* This should have no impact. */ - vma = try_merge_new_vma(&mm, &vmg, 0x6000, 0x7000, 6, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0x6000, 0x7000, 6, vm_flags, &merged); ASSERT_EQ(vma, vma_d); /* Prepend. */ ASSERT_TRUE(merged); @@ -564,7 +564,7 @@ static bool test_merge_new(void) * AAAAA*DDD CC */ vma_d->vm_ops = NULL; /* This would otherwise degrade the merge. */ - vma = try_merge_new_vma(&mm, &vmg, 0x5000, 0x6000, 5, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0x5000, 0x6000, 5, vm_flags, &merged); ASSERT_EQ(vma, vma_a); /* Merge with A, delete D. */ ASSERT_TRUE(merged); @@ -582,7 +582,7 @@ static bool test_merge_new(void) * AAAAAAAAA *CC */ vma_c->anon_vma = &dummy_anon_vma; - vma = try_merge_new_vma(&mm, &vmg, 0xa000, 0xb000, 0xa, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0xa000, 0xb000, 0xa, vm_flags, &merged); ASSERT_EQ(vma, vma_c); /* Prepend C. */ ASSERT_TRUE(merged); @@ -599,7 +599,7 @@ static bool test_merge_new(void) * 0123456789abc * AAAAAAAAA*CCC */ - vma = try_merge_new_vma(&mm, &vmg, 0x9000, 0xa000, 0x9, flags, &merged); + vma = try_merge_new_vma(&mm, &vmg, 0x9000, 0xa000, 0x9, vm_flags, &merged); ASSERT_EQ(vma, vma_a); /* Extend A and delete C. */ ASSERT_TRUE(merged); @@ -639,7 +639,7 @@ static bool test_merge_new(void) static bool test_vma_merge_special_flags(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { @@ -661,7 +661,7 @@ static bool test_vma_merge_special_flags(void) * 01234 * AAA */ - vma_left = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); + vma_left = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); ASSERT_NE(vma_left, NULL); /* 1. Set up new VMA with special flag that would otherwise merge. */ @@ -672,12 +672,12 @@ static bool test_vma_merge_special_flags(void) * * This should merge if not for the VM_SPECIAL flag. */ - vmg_set_range(&vmg, 0x3000, 0x4000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x4000, 3, vm_flags); for (i = 0; i < ARRAY_SIZE(special_flags); i++) { vm_flags_t special_flag = special_flags[i]; - vma_left->__vm_flags = flags | special_flag; - vmg.flags = flags | special_flag; + vma_left->__vm_flags = vm_flags | special_flag; + vmg.vm_flags = vm_flags | special_flag; vma = merge_new(&vmg); ASSERT_EQ(vma, NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); @@ -691,15 +691,15 @@ static bool test_vma_merge_special_flags(void) * * Create a VMA to modify. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vm_flags); ASSERT_NE(vma, NULL); vmg.middle = vma; for (i = 0; i < ARRAY_SIZE(special_flags); i++) { vm_flags_t special_flag = special_flags[i]; - vma_left->__vm_flags = flags | special_flag; - vmg.flags = flags | special_flag; + vma_left->__vm_flags = vm_flags | special_flag; + vmg.vm_flags = vm_flags | special_flag; vma = merge_existing(&vmg); ASSERT_EQ(vma, NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); @@ -711,7 +711,7 @@ static bool test_vma_merge_special_flags(void) static bool test_vma_merge_with_close(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { @@ -791,11 +791,11 @@ static bool test_vma_merge_with_close(void) * PPPPPPNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vm_flags); vma_next->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); ASSERT_EQ(merge_new(&vmg), vma_prev); ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS); ASSERT_EQ(vma_prev->vm_start, 0); @@ -816,11 +816,11 @@ static bool test_vma_merge_with_close(void) * proceed. */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vma->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -844,11 +844,11 @@ static bool test_vma_merge_with_close(void) * proceed. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vm_flags); vma->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); /* @@ -872,12 +872,12 @@ static bool test_vma_merge_with_close(void) * PPPVVNNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vm_flags); vma->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -898,12 +898,12 @@ static bool test_vma_merge_with_close(void) * PPPPPNNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vm_flags); vma_next->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -920,15 +920,15 @@ static bool test_vma_merge_with_close(void) static bool test_vma_merge_new_with_close(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { .mm = &mm, .vmi = &vmi, }; - struct vm_area_struct *vma_prev = alloc_and_link_vma(&mm, 0, 0x2000, 0, flags); - struct vm_area_struct *vma_next = alloc_and_link_vma(&mm, 0x5000, 0x7000, 5, flags); + struct vm_area_struct *vma_prev = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags); + struct vm_area_struct *vma_next = alloc_and_link_vma(&mm, 0x5000, 0x7000, 5, vm_flags); const struct vm_operations_struct vm_ops = { .close = dummy_close, }; @@ -958,7 +958,7 @@ static bool test_vma_merge_new_with_close(void) vma_prev->vm_ops = &vm_ops; vma_next->vm_ops = &vm_ops; - vmg_set_range(&vmg, 0x2000, 0x5000, 2, flags); + vmg_set_range(&vmg, 0x2000, 0x5000, 2, vm_flags); vma = merge_new(&vmg); ASSERT_NE(vma, NULL); ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS); @@ -975,7 +975,7 @@ static bool test_vma_merge_new_with_close(void) static bool test_merge_existing(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vm_area_struct *vma, *vma_prev, *vma_next; @@ -998,11 +998,11 @@ static bool test_merge_existing(void) * 0123456789 * VNNNNNN */ - vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, flags); + vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vm_flags); vma->vm_ops = &vm_ops; /* This should have no impact. */ - vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, flags); + vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, vm_flags); vma_next->vm_ops = &vm_ops; /* This should have no impact. */ - vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, flags, &dummy_anon_vma); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, vm_flags, &dummy_anon_vma); vmg.middle = vma; vmg.prev = vma; vma_set_dummy_anon_vma(vma, &avc); @@ -1032,10 +1032,10 @@ static bool test_merge_existing(void) * 0123456789 * NNNNNNN */ - vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, flags); - vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, flags); + vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, vm_flags); vma_next->vm_ops = &vm_ops; /* This should have no impact. */ - vmg_set_range_anon_vma(&vmg, 0x2000, 0x6000, 2, flags, &dummy_anon_vma); + vmg_set_range_anon_vma(&vmg, 0x2000, 0x6000, 2, vm_flags, &dummy_anon_vma); vmg.middle = vma; vma_set_dummy_anon_vma(vma, &avc); ASSERT_EQ(merge_existing(&vmg), vma_next); @@ -1060,11 +1060,11 @@ static bool test_merge_existing(void) * 0123456789 * PPPPPPV */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); vma_prev->vm_ops = &vm_ops; /* This should have no impact. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags); vma->vm_ops = &vm_ops; /* This should have no impact. */ - vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, flags, &dummy_anon_vma); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, vm_flags, &dummy_anon_vma); vmg.prev = vma_prev; vmg.middle = vma; vma_set_dummy_anon_vma(vma, &avc); @@ -1094,10 +1094,10 @@ static bool test_merge_existing(void) * 0123456789 * PPPPPPP */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); vma_prev->vm_ops = &vm_ops; /* This should have no impact. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags); - vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, flags, &dummy_anon_vma); + vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, &dummy_anon_vma); vmg.prev = vma_prev; vmg.middle = vma; vma_set_dummy_anon_vma(vma, &avc); @@ -1123,11 +1123,11 @@ static bool test_merge_existing(void) * 0123456789 * PPPPPPPPPP */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); vma_prev->vm_ops = &vm_ops; /* This should have no impact. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags); - vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, flags, &dummy_anon_vma); + vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vm_flags); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, &dummy_anon_vma); vmg.prev = vma_prev; vmg.middle = vma; vma_set_dummy_anon_vma(vma, &avc); @@ -1158,41 +1158,41 @@ static bool test_merge_existing(void) * PPPVVVVVNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, vm_flags); - vmg_set_range(&vmg, 0x4000, 0x5000, 4, flags); + vmg_set_range(&vmg, 0x4000, 0x5000, 4, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); - vmg_set_range(&vmg, 0x5000, 0x6000, 5, flags); + vmg_set_range(&vmg, 0x5000, 0x6000, 5, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); - vmg_set_range(&vmg, 0x6000, 0x7000, 6, flags); + vmg_set_range(&vmg, 0x6000, 0x7000, 6, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); - vmg_set_range(&vmg, 0x4000, 0x7000, 4, flags); + vmg_set_range(&vmg, 0x4000, 0x7000, 4, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); - vmg_set_range(&vmg, 0x4000, 0x6000, 4, flags); + vmg_set_range(&vmg, 0x4000, 0x6000, 4, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE); - vmg_set_range(&vmg, 0x5000, 0x6000, 5, flags); + vmg_set_range(&vmg, 0x5000, 0x6000, 5, vm_flags); vmg.prev = vma; vmg.middle = vma; ASSERT_EQ(merge_existing(&vmg), NULL); @@ -1205,7 +1205,7 @@ static bool test_merge_existing(void) static bool test_anon_vma_non_mergeable(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vm_area_struct *vma, *vma_prev, *vma_next; @@ -1229,9 +1229,9 @@ static bool test_anon_vma_non_mergeable(void) * 0123456789 * PPPPPPPNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vm_flags); /* * Give both prev and next single anon_vma_chain fields, so they will @@ -1239,7 +1239,7 @@ static bool test_anon_vma_non_mergeable(void) * * However, when prev is compared to next, the merge should fail. */ - vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, flags, NULL); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, NULL); vmg.prev = vma_prev; vmg.middle = vma; vma_set_dummy_anon_vma(vma_prev, &dummy_anon_vma_chain_1); @@ -1267,10 +1267,10 @@ static bool test_anon_vma_non_mergeable(void) * 0123456789 * PPPPPPPNNN */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vm_flags); - vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, flags, NULL); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, NULL); vmg.prev = vma_prev; vma_set_dummy_anon_vma(vma_prev, &dummy_anon_vma_chain_1); __vma_set_dummy_anon_vma(vma_next, &dummy_anon_vma_chain_2, &dummy_anon_vma_2); @@ -1292,7 +1292,7 @@ static bool test_anon_vma_non_mergeable(void) static bool test_dup_anon_vma(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { @@ -1313,12 +1313,12 @@ static bool test_dup_anon_vma(void) * This covers new VMA merging, as these operations amount to a VMA * expand. */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma_next = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vma_next->anon_vma = &dummy_anon_vma; - vmg_set_range(&vmg, 0, 0x5000, 0, flags); - vmg.middle = vma_prev; + vmg_set_range(&vmg, 0, 0x5000, 0, vm_flags); + vmg.target = vma_prev; vmg.next = vma_next; ASSERT_EQ(expand_existing(&vmg), 0); @@ -1339,16 +1339,16 @@ static bool test_dup_anon_vma(void) * extend delete delete */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vm_flags); /* Initialise avc so mergeability check passes. */ INIT_LIST_HEAD(&vma_next->anon_vma_chain); list_add(&dummy_anon_vma_chain.same_vma, &vma_next->anon_vma_chain); vma_next->anon_vma = &dummy_anon_vma; - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -1372,12 +1372,12 @@ static bool test_dup_anon_vma(void) * extend delete delete */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vm_flags); vmg.anon_vma = &dummy_anon_vma; vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain); - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -1401,11 +1401,11 @@ static bool test_dup_anon_vma(void) * extend shrink/delete */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, vm_flags); vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain); - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.middle = vma; @@ -1429,11 +1429,11 @@ static bool test_dup_anon_vma(void) * shrink/delete extend */ - vma = alloc_and_link_vma(&mm, 0, 0x5000, 0, flags); - vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags); + vma = alloc_and_link_vma(&mm, 0, 0x5000, 0, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vm_flags); vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain); - vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags); + vmg_set_range(&vmg, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma; vmg.middle = vma; @@ -1452,7 +1452,7 @@ static bool test_dup_anon_vma(void) static bool test_vmi_prealloc_fail(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vma_merge_struct vmg = { @@ -1468,11 +1468,11 @@ static bool test_vmi_prealloc_fail(void) * the duplicated anon_vma is unlinked. */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vma->anon_vma = &dummy_anon_vma; - vmg_set_range_anon_vma(&vmg, 0x3000, 0x5000, 3, flags, &dummy_anon_vma); + vmg_set_range_anon_vma(&vmg, 0x3000, 0x5000, 3, vm_flags, &dummy_anon_vma); vmg.prev = vma_prev; vmg.middle = vma; vma_set_dummy_anon_vma(vma, &avc); @@ -1496,12 +1496,12 @@ static bool test_vmi_prealloc_fail(void) * performed in this case too. */ - vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags); - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vma->anon_vma = &dummy_anon_vma; - vmg_set_range(&vmg, 0, 0x5000, 3, flags); - vmg.middle = vma_prev; + vmg_set_range(&vmg, 0, 0x5000, 3, vm_flags); + vmg.target = vma_prev; vmg.next = vma; fail_prealloc = true; @@ -1518,13 +1518,13 @@ static bool test_vmi_prealloc_fail(void) static bool test_merge_extend(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0x1000); struct vm_area_struct *vma; - vma = alloc_and_link_vma(&mm, 0, 0x1000, 0, flags); - alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, flags); + vma = alloc_and_link_vma(&mm, 0, 0x1000, 0, vm_flags); + alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vm_flags); /* * Extend a VMA into the gap between itself and the following VMA. @@ -1548,7 +1548,7 @@ static bool test_merge_extend(void) static bool test_copy_vma(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; bool need_locks = false; VMA_ITERATOR(vmi, &mm, 0); @@ -1556,7 +1556,7 @@ static bool test_copy_vma(void) /* Move backwards and do not merge. */ - vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vma_new = copy_vma(&vma, 0, 0x2000, 0, &need_locks); ASSERT_NE(vma_new, vma); ASSERT_EQ(vma_new->vm_start, 0); @@ -1568,8 +1568,8 @@ static bool test_copy_vma(void) /* Move a VMA into position next to another and merge the two. */ - vma = alloc_and_link_vma(&mm, 0, 0x2000, 0, flags); - vma_next = alloc_and_link_vma(&mm, 0x6000, 0x8000, 6, flags); + vma = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags); + vma_next = alloc_and_link_vma(&mm, 0x6000, 0x8000, 6, vm_flags); vma_new = copy_vma(&vma, 0x4000, 0x2000, 4, &need_locks); vma_assert_attached(vma_new); @@ -1581,11 +1581,11 @@ static bool test_copy_vma(void) static bool test_expand_only_mode(void) { - unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; struct mm_struct mm = {}; VMA_ITERATOR(vmi, &mm, 0); struct vm_area_struct *vma_prev, *vma; - VMG_STATE(vmg, &mm, &vmi, 0x5000, 0x9000, flags, 5); + VMG_STATE(vmg, &mm, &vmi, 0x5000, 0x9000, vm_flags, 5); /* * Place a VMA prior to the one we're expanding so we assert that we do @@ -1593,14 +1593,14 @@ static bool test_expand_only_mode(void) * have, through the use of the just_expand flag, indicated we do not * need to do so. */ - alloc_and_link_vma(&mm, 0, 0x2000, 0, flags); + alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags); /* * We will be positioned at the prev VMA, but looking to expand to * 0x9000. */ vma_iter_set(&vmi, 0x3000); - vma_prev = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vma_prev = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags); vmg.prev = vma_prev; vmg.just_expand = true; diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h index 14718ca23a05..3639aa8dd2b0 100644 --- a/tools/testing/vma/vma_internal.h +++ b/tools/testing/vma/vma_internal.h @@ -108,8 +108,10 @@ extern unsigned long dac_mmap_min_addr; #define CAP_IPC_LOCK 14 #ifdef CONFIG_64BIT -/* VM is sealed, in vm_flags */ -#define VM_SEALED _BITUL(63) +#define VM_SEALED_BIT 42 +#define VM_SEALED BIT(VM_SEALED_BIT) +#else +#define VM_SEALED VM_NONE #endif #define FIRST_USER_ADDRESS 0UL @@ -576,7 +578,7 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) return __pgprot(pgprot_val(oldprot) | pgprot_val(newprot)); } -static inline pgprot_t vm_get_page_prot(unsigned long vm_flags) +static inline pgprot_t vm_get_page_prot(vm_flags_t vm_flags) { return __pgprot(vm_flags); } @@ -1084,7 +1086,7 @@ static inline bool mpol_equal(struct mempolicy *, struct mempolicy *) } static inline void khugepaged_enter_vma(struct vm_area_struct *vma, - unsigned long vm_flags) + vm_flags_t vm_flags) { (void)vma; (void)vm_flags; @@ -1200,7 +1202,7 @@ bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot); /* Update vma->vm_page_prot to reflect vma->vm_flags. */ static inline void vma_set_page_prot(struct vm_area_struct *vma) { - unsigned long vm_flags = vma->vm_flags; + vm_flags_t vm_flags = vma->vm_flags; pgprot_t vm_page_prot; /* testing: we inline vm_pgprot_modify() to avoid clash with vma.h. */ @@ -1215,7 +1217,7 @@ static inline void vma_set_page_prot(struct vm_area_struct *vma) WRITE_ONCE(vma->vm_page_prot, vm_page_prot); } -static inline bool arch_validate_flags(unsigned long) +static inline bool arch_validate_flags(vm_flags_t) { return true; } @@ -1280,12 +1282,12 @@ static inline bool capable(int cap) return true; } -static inline bool mlock_future_ok(struct mm_struct *mm, unsigned long flags, +static inline bool mlock_future_ok(struct mm_struct *mm, vm_flags_t vm_flags, unsigned long bytes) { unsigned long locked_pages, limit_pages; - if (!(flags & VM_LOCKED) || capable(CAP_IPC_LOCK)) + if (!(vm_flags & VM_LOCKED) || capable(CAP_IPC_LOCK)) return true; locked_pages = bytes >> PAGE_SHIFT; @@ -1442,8 +1444,29 @@ static inline void free_anon_vma_name(struct vm_area_struct *vma) (void)vma; } +/* Declared in vma.h. */ +static inline void set_vma_from_desc(struct vm_area_struct *vma, + struct vm_area_desc *desc); + +static inline struct vm_area_desc *vma_to_desc(struct vm_area_struct *vma, + struct vm_area_desc *desc); + +static int compat_vma_mmap_prepare(struct file *file, + struct vm_area_struct *vma) +{ + struct vm_area_desc desc; + int err; + + err = file->f_op->mmap_prepare(vma_to_desc(vma, &desc)); + if (err) + return err; + set_vma_from_desc(vma, &desc); + + return 0; +} + /* Did the driver provide valid mmap hook configuration? */ -static inline bool file_has_valid_mmap_hooks(struct file *file) +static inline bool can_mmap_file(struct file *file) { bool has_mmap = file->f_op->mmap; bool has_mmap_prepare = file->f_op->mmap_prepare; @@ -1451,22 +1474,21 @@ static inline bool file_has_valid_mmap_hooks(struct file *file) /* Hooks are mutually exclusive. */ if (WARN_ON_ONCE(has_mmap && has_mmap_prepare)) return false; - if (WARN_ON_ONCE(!has_mmap && !has_mmap_prepare)) + if (!has_mmap && !has_mmap_prepare) return false; return true; } -static inline int call_mmap(struct file *file, struct vm_area_struct *vma) +static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma) { - if (WARN_ON_ONCE(file->f_op->mmap_prepare)) - return -EINVAL; + if (file->f_op->mmap_prepare) + return compat_vma_mmap_prepare(file, vma); return file->f_op->mmap(file, vma); } -static inline int __call_mmap_prepare(struct file *file, - struct vm_area_desc *desc) +static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc) { return file->f_op->mmap_prepare(desc); } @@ -1484,4 +1506,15 @@ static inline void vma_set_file(struct vm_area_struct *vma, struct file *file) fput(file); } +static inline bool shmem_file(struct file *) +{ + return false; +} + +static inline vm_flags_t ksm_vma_flags(const struct mm_struct *, const struct file *, + vm_flags_t vm_flags) +{ + return vm_flags; +} + #endif /* __MM_VMA_INTERNAL_H */ diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index 6e0b4e95e230..88211fd132d2 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -5,6 +5,7 @@ vsock_test: vsock_test.o vsock_test_zerocopy.o timeout.o control.o util.o msg_ze vsock_diag_test: vsock_diag_test.o timeout.o control.o util.o vsock_perf: vsock_perf.o msg_zerocopy_common.o +vsock_test: LDLIBS = -lpthread vsock_uring_test: LDLIBS = -luring vsock_uring_test: control.o util.o vsock_uring_test.o timeout.o msg_zerocopy_common.o diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 0c7e9cbcbc85..7b861a8e997a 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -7,6 +7,7 @@ * Author: Stefan Hajnoczi */ +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -23,6 +25,9 @@ #include "control.h" #include "util.h" +#define KALLSYMS_PATH "/proc/kallsyms" +#define KALLSYMS_LINE_LEN 512 + /* Install signal handlers */ void init_signals(void) { @@ -97,39 +102,52 @@ void vsock_wait_remote_close(int fd) close(epollfd); } +/* Wait until ioctl gives an expected int value. + * Return false if the op is not supported. + */ +bool vsock_ioctl_int(int fd, unsigned long op, int expected) +{ + int actual, ret; + char name[32]; + + snprintf(name, sizeof(name), "ioctl(%lu)", op); + + timeout_begin(TIMEOUT); + do { + ret = ioctl(fd, op, &actual); + if (ret < 0) { + if (errno == EOPNOTSUPP || errno == ENOTTY) + break; + + perror(name); + exit(EXIT_FAILURE); + } + timeout_check(name); + } while (actual != expected); + timeout_end(); + + return ret >= 0; +} + /* Wait until transport reports no data left to be sent. * Return false if transport does not implement the unsent_bytes() callback. */ bool vsock_wait_sent(int fd) { - int ret, sock_bytes_unsent; - - timeout_begin(TIMEOUT); - do { - ret = ioctl(fd, SIOCOUTQ, &sock_bytes_unsent); - if (ret < 0) { - if (errno == EOPNOTSUPP) - break; - - perror("ioctl(SIOCOUTQ)"); - exit(EXIT_FAILURE); - } - timeout_check("SIOCOUTQ"); - } while (sock_bytes_unsent != 0); - timeout_end(); - - return !ret; + return vsock_ioctl_int(fd, SIOCOUTQ, 0); } -/* Create socket , bind to and return the file descriptor. */ -int vsock_bind(unsigned int cid, unsigned int port, int type) +/* Create socket , bind to . + * Return the file descriptor, or -1 on error. + */ +int vsock_bind_try(unsigned int cid, unsigned int port, int type) { struct sockaddr_vm sa = { .svm_family = AF_VSOCK, .svm_cid = cid, .svm_port = port, }; - int fd; + int fd, saved_errno; fd = socket(AF_VSOCK, type, 0); if (fd < 0) { @@ -138,6 +156,22 @@ int vsock_bind(unsigned int cid, unsigned int port, int type) } if (bind(fd, (struct sockaddr *)&sa, sizeof(sa))) { + saved_errno = errno; + close(fd); + errno = saved_errno; + fd = -1; + } + + return fd; +} + +/* Create socket , bind to and return the file descriptor. */ +int vsock_bind(unsigned int cid, unsigned int port, int type) +{ + int fd; + + fd = vsock_bind_try(cid, port, type); + if (fd < 0) { perror("bind"); exit(EXIT_FAILURE); } @@ -836,3 +870,55 @@ void enable_so_linger(int fd, int timeout) exit(EXIT_FAILURE); } } + +static int __get_transports(void) +{ + char buf[KALLSYMS_LINE_LEN]; + const char *ksym; + int ret = 0; + FILE *f; + + f = fopen(KALLSYMS_PATH, "r"); + if (!f) { + perror("Can't open " KALLSYMS_PATH); + exit(EXIT_FAILURE); + } + + while (fgets(buf, sizeof(buf), f)) { + char *match; + int i; + + assert(buf[strlen(buf) - 1] == '\n'); + + for (i = 0; i < TRANSPORT_NUM; ++i) { + if (ret & BIT(i)) + continue; + + /* Match should be followed by '\t' or '\n'. + * See kallsyms.c:s_show(). + */ + ksym = transport_ksyms[i]; + match = strstr(buf, ksym); + if (match && isspace(match[strlen(ksym)])) { + ret |= BIT(i); + break; + } + } + } + + fclose(f); + return ret; +} + +/* Return integer with TRANSPORT_* bit set for every (known) registered vsock + * transport. + */ +int get_transports(void) +{ + static int tr = -1; + + if (tr == -1) + tr = __get_transports(); + + return tr; +} diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index 5e2db67072d5..142c02a6834a 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -3,8 +3,40 @@ #define UTIL_H #include +#include +#include #include +/* All known vsock transports, see callers of vsock_core_register() */ +#define KNOWN_TRANSPORTS(x) \ + x(LOOPBACK, "loopback") \ + x(VIRTIO, "virtio") \ + x(VHOST, "vhost") \ + x(VMCI, "vmci") \ + x(HYPERV, "hvs") + +enum transport { + TRANSPORT_COUNTER_BASE = __COUNTER__ + 1, + #define x(name, symbol) \ + TRANSPORT_##name = BIT(__COUNTER__ - TRANSPORT_COUNTER_BASE), + KNOWN_TRANSPORTS(x) + TRANSPORT_NUM = __COUNTER__ - TRANSPORT_COUNTER_BASE, + #undef x +}; + +static const char * const transport_ksyms[] = { + #define x(name, symbol) "d " symbol "_transport", + KNOWN_TRANSPORTS(x) + #undef x +}; + +static_assert(ARRAY_SIZE(transport_ksyms) == TRANSPORT_NUM); +static_assert(BITS_PER_TYPE(int) >= TRANSPORT_NUM); + +#define TRANSPORTS_G2H (TRANSPORT_VIRTIO | TRANSPORT_VMCI | TRANSPORT_HYPERV) +#define TRANSPORTS_H2G (TRANSPORT_VHOST | TRANSPORT_VMCI) +#define TRANSPORTS_LOCAL (TRANSPORT_LOOPBACK) + /* Tests can either run as the client or the server */ enum test_mode { TEST_MODE_UNSET, @@ -44,6 +76,7 @@ int vsock_connect(unsigned int cid, unsigned int port, int type); int vsock_accept(unsigned int cid, unsigned int port, struct sockaddr_vm *clientaddrp, int type); int vsock_stream_connect(unsigned int cid, unsigned int port); +int vsock_bind_try(unsigned int cid, unsigned int port, int type); int vsock_bind(unsigned int cid, unsigned int port, int type); int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type); @@ -54,6 +87,7 @@ int vsock_stream_listen(unsigned int cid, unsigned int port); int vsock_seqpacket_accept(unsigned int cid, unsigned int port, struct sockaddr_vm *clientaddrp); void vsock_wait_remote_close(int fd); +bool vsock_ioctl_int(int fd, unsigned long op, int expected); bool vsock_wait_sent(int fd); void send_buf(int fd, const void *buf, size_t len, int flags, ssize_t expected_ret); @@ -81,4 +115,5 @@ void setsockopt_timeval_check(int fd, int level, int optname, struct timeval val, char const *errmsg); void enable_so_zerocopy_check(int fd); void enable_so_linger(int fd, int timeout); +int get_transports(void); #endif /* UTIL_H */ diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index f669baaa0dca..d4517386e551 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include "vsock_test_zerocopy.h" #include "timeout.h" @@ -1305,6 +1308,54 @@ static void test_unsent_bytes_client(const struct test_opts *opts, int type) close(fd); } +static void test_unread_bytes_server(const struct test_opts *opts, int type) +{ + unsigned char buf[MSG_BUF_IOCTL_LEN]; + int client_fd; + + client_fd = vsock_accept(VMADDR_CID_ANY, opts->peer_port, NULL, type); + if (client_fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < sizeof(buf); i++) + buf[i] = rand() & 0xFF; + + send_buf(client_fd, buf, sizeof(buf), 0, sizeof(buf)); + control_writeln("SENT"); + + close(client_fd); +} + +static void test_unread_bytes_client(const struct test_opts *opts, int type) +{ + unsigned char buf[MSG_BUF_IOCTL_LEN]; + int fd; + + fd = vsock_connect(opts->peer_cid, opts->peer_port, type); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + control_expectln("SENT"); + /* The data has arrived but has not been read. The expected is + * MSG_BUF_IOCTL_LEN. + */ + if (!vsock_ioctl_int(fd, SIOCINQ, MSG_BUF_IOCTL_LEN)) { + fprintf(stderr, "Test skipped, SIOCINQ not supported.\n"); + goto out; + } + + recv_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); + /* All data has been consumed, so the expected is 0. */ + vsock_ioctl_int(fd, SIOCINQ, 0); + +out: + close(fd); +} + static void test_stream_unsent_bytes_client(const struct test_opts *opts) { test_unsent_bytes_client(opts, SOCK_STREAM); @@ -1325,6 +1376,26 @@ static void test_seqpacket_unsent_bytes_server(const struct test_opts *opts) test_unsent_bytes_server(opts, SOCK_SEQPACKET); } +static void test_stream_unread_bytes_client(const struct test_opts *opts) +{ + test_unread_bytes_client(opts, SOCK_STREAM); +} + +static void test_stream_unread_bytes_server(const struct test_opts *opts) +{ + test_unread_bytes_server(opts, SOCK_STREAM); +} + +static void test_seqpacket_unread_bytes_client(const struct test_opts *opts) +{ + test_unread_bytes_client(opts, SOCK_SEQPACKET); +} + +static void test_seqpacket_unread_bytes_server(const struct test_opts *opts) +{ + test_unread_bytes_server(opts, SOCK_SEQPACKET); +} + #define RCVLOWAT_CREDIT_UPD_BUF_SIZE (1024 * 128) /* This define is the same as in 'include/linux/virtio_vsock.h': * it is used to decide when to send credit update message during @@ -1718,16 +1789,27 @@ static void test_stream_msgzcopy_leak_zcskb_server(const struct test_opts *opts) #define MAX_PORT_RETRIES 24 /* net/vmw_vsock/af_vsock.c */ -/* Test attempts to trigger a transport release for an unbound socket. This can - * lead to a reference count mishandling. - */ -static void test_stream_transport_uaf_client(const struct test_opts *opts) +static bool test_stream_transport_uaf(int cid) { int sockets[MAX_PORT_RETRIES]; struct sockaddr_vm addr; - int fd, i, alen; + socklen_t alen; + int fd, i, c; + bool ret; - fd = vsock_bind(VMADDR_CID_ANY, VMADDR_PORT_ANY, SOCK_STREAM); + /* Probe for a transport by attempting a local CID bind. Unavailable + * transport (or more specifically: an unsupported transport/CID + * combination) results in EADDRNOTAVAIL, other errnos are fatal. + */ + fd = vsock_bind_try(cid, VMADDR_PORT_ANY, SOCK_STREAM); + if (fd < 0) { + if (errno != EADDRNOTAVAIL) { + perror("Unexpected bind() errno"); + exit(EXIT_FAILURE); + } + + return false; + } alen = sizeof(addr); if (getsockname(fd, (struct sockaddr *)&addr, &alen)) { @@ -1735,38 +1817,83 @@ static void test_stream_transport_uaf_client(const struct test_opts *opts) exit(EXIT_FAILURE); } + /* Drain the autobind pool; see __vsock_bind_connectible(). */ for (i = 0; i < MAX_PORT_RETRIES; ++i) - sockets[i] = vsock_bind(VMADDR_CID_ANY, ++addr.svm_port, - SOCK_STREAM); + sockets[i] = vsock_bind(cid, ++addr.svm_port, SOCK_STREAM); close(fd); - fd = socket(AF_VSOCK, SOCK_STREAM, 0); + + /* Setting SOCK_NONBLOCK makes connect() return soon after + * (re-)assigning the transport. We are not connecting to anything + * anyway, so there is no point entering the main loop in + * vsock_connect(); waiting for timeout, checking for signals, etc. + */ + fd = socket(AF_VSOCK, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd < 0) { perror("socket"); exit(EXIT_FAILURE); } - if (!vsock_connect_fd(fd, addr.svm_cid, addr.svm_port)) { - perror("Unexpected connect() #1 success"); + /* Assign transport, while failing to autobind. Autobind pool was + * drained, so EADDRNOTAVAIL coming from __vsock_bind_connectible() is + * expected. + * + * One exception is ENODEV which is thrown by vsock_assign_transport(), + * i.e. before vsock_auto_bind(), when the only transport loaded is + * vhost. + */ + if (!connect(fd, (struct sockaddr *)&addr, alen)) { + fprintf(stderr, "Unexpected connect() success\n"); + exit(EXIT_FAILURE); + } + if (errno == ENODEV && cid == VMADDR_CID_HOST) { + ret = false; + goto cleanup; + } + if (errno != EADDRNOTAVAIL) { + perror("Unexpected connect() errno"); exit(EXIT_FAILURE); } - /* Vulnerable system may crash now. */ - if (!vsock_connect_fd(fd, VMADDR_CID_HOST, VMADDR_PORT_ANY)) { - perror("Unexpected connect() #2 success"); - exit(EXIT_FAILURE); + /* Reassign transport, triggering old transport release and + * (potentially) unbinding of an unbound socket. + * + * Vulnerable system may crash now. + */ + for (c = VMADDR_CID_HYPERVISOR; c <= VMADDR_CID_HOST + 1; ++c) { + if (c != cid) { + addr.svm_cid = c; + (void)connect(fd, (struct sockaddr *)&addr, alen); + } } + ret = true; +cleanup: close(fd); while (i--) close(sockets[i]); - control_writeln("DONE"); + return ret; } -static void test_stream_transport_uaf_server(const struct test_opts *opts) +/* Test attempts to trigger a transport release for an unbound socket. This can + * lead to a reference count mishandling. + */ +static void test_stream_transport_uaf_client(const struct test_opts *opts) { - control_expectln("DONE"); + bool tested = false; + int cid, tr; + + for (cid = VMADDR_CID_HYPERVISOR; cid <= VMADDR_CID_HOST + 1; ++cid) + tested |= test_stream_transport_uaf(cid); + + tr = get_transports(); + if (!tr) + fprintf(stderr, "No transports detected\n"); + else if (tr == TRANSPORT_VIRTIO) + fprintf(stderr, "Setup unsupported: sole virtio transport\n"); + else if (!tested) + fprintf(stderr, "No transports tested\n"); } static void test_stream_connect_retry_client(const struct test_opts *opts) @@ -1811,6 +1938,180 @@ static void test_stream_connect_retry_server(const struct test_opts *opts) close(fd); } +#define TRANSPORT_CHANGE_TIMEOUT 2 /* seconds */ + +static void *test_stream_transport_change_thread(void *vargp) +{ + pid_t *pid = (pid_t *)vargp; + int ret; + + ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (ret) { + fprintf(stderr, "pthread_setcanceltype: %d\n", ret); + exit(EXIT_FAILURE); + } + + while (true) { + if (kill(*pid, SIGUSR1) < 0) { + perror("kill"); + exit(EXIT_FAILURE); + } + } + return NULL; +} + +static void test_transport_change_signal_handler(int signal) +{ + /* We need a custom handler for SIGUSR1 as the default one terminates the process. */ +} + +static void test_stream_transport_change_client(const struct test_opts *opts) +{ + __sighandler_t old_handler; + pid_t pid = getpid(); + pthread_t thread_id; + time_t tout; + int ret, tr; + + tr = get_transports(); + + /* Print a warning if there is a G2H transport loaded. + * This is on a best effort basis because VMCI can be either G2H and H2G, and there is + * no easy way to understand it. + * The bug we are testing only appears when G2H transports are not loaded. + * This is because `vsock_assign_transport`, when using CID 0, assigns a G2H transport + * to vsk->transport. If none is available it is set to NULL, causing the null-ptr-deref. + */ + if (tr & TRANSPORTS_G2H) + fprintf(stderr, "G2H Transport detected. This test will not fail.\n"); + + old_handler = signal(SIGUSR1, test_transport_change_signal_handler); + if (old_handler == SIG_ERR) { + perror("signal"); + exit(EXIT_FAILURE); + } + + ret = pthread_create(&thread_id, NULL, test_stream_transport_change_thread, &pid); + if (ret) { + fprintf(stderr, "pthread_create: %d\n", ret); + exit(EXIT_FAILURE); + } + + control_expectln("LISTENING"); + + tout = current_nsec() + TRANSPORT_CHANGE_TIMEOUT * NSEC_PER_SEC; + do { + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = opts->peer_cid, + .svm_port = opts->peer_port, + }; + bool send_control = false; + int s; + + s = socket(AF_VSOCK, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + ret = connect(s, (struct sockaddr *)&sa, sizeof(sa)); + /* The connect can fail due to signals coming from the thread, + * or because the receiver connection queue is full. + * Ignoring also the latter case because there is no way + * of synchronizing client's connect and server's accept when + * connect(s) are constantly being interrupted by signals. + */ + if (ret == -1 && (errno != EINTR && errno != ECONNRESET)) { + perror("connect"); + exit(EXIT_FAILURE); + } + + /* Notify the server if the connect() is successful or the + * receiver connection queue is full, so it will do accept() + * to drain it. + */ + if (!ret || errno == ECONNRESET) + send_control = true; + + /* Set CID to 0 cause a transport change. */ + sa.svm_cid = 0; + + /* There is a case where this will not fail: + * if the previous connect() is interrupted while the + * connection request is already sent, this second + * connect() will wait for the response. + */ + ret = connect(s, (struct sockaddr *)&sa, sizeof(sa)); + if (!ret || errno == ECONNRESET) + send_control = true; + + close(s); + + if (send_control) + control_writeulong(CONTROL_CONTINUE); + + } while (current_nsec() < tout); + + control_writeulong(CONTROL_DONE); + + ret = pthread_cancel(thread_id); + if (ret) { + fprintf(stderr, "pthread_cancel: %d\n", ret); + exit(EXIT_FAILURE); + } + + ret = pthread_join(thread_id, NULL); + if (ret) { + fprintf(stderr, "pthread_join: %d\n", ret); + exit(EXIT_FAILURE); + } + + if (signal(SIGUSR1, old_handler) == SIG_ERR) { + perror("signal"); + exit(EXIT_FAILURE); + } +} + +static void test_stream_transport_change_server(const struct test_opts *opts) +{ + int s = vsock_stream_listen(VMADDR_CID_ANY, opts->peer_port); + + /* Set the socket to be nonblocking because connects that have been interrupted + * (EINTR) can fill the receiver's accept queue anyway, leading to connect failure. + * As of today (6.15) in such situation there is no way to understand, from the + * client side, if the connection has been queued in the server or not. + */ + if (fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK) < 0) { + perror("fcntl"); + exit(EXIT_FAILURE); + } + control_writeln("LISTENING"); + + while (control_readulong() == CONTROL_CONTINUE) { + /* Must accept the connection, otherwise the `listen` + * queue will fill up and new connections will fail. + * There can be more than one queued connection, + * clear them all. + */ + while (true) { + int client = accept(s, NULL, NULL); + + if (client < 0) { + if (errno == EAGAIN) + break; + + perror("accept"); + exit(EXIT_FAILURE); + } + + close(client); + } + } + + close(s); +} + static void test_stream_linger_client(const struct test_opts *opts) { int fd; @@ -2034,7 +2335,6 @@ static struct test_case test_cases[] = { { .name = "SOCK_STREAM transport release use-after-free", .run_client = test_stream_transport_uaf_client, - .run_server = test_stream_transport_uaf_server, }, { .name = "SOCK_STREAM retry failed connect()", @@ -2051,6 +2351,21 @@ static struct test_case test_cases[] = { .run_client = test_stream_nolinger_client, .run_server = test_stream_nolinger_server, }, + { + .name = "SOCK_STREAM transport change null-ptr-deref", + .run_client = test_stream_transport_change_client, + .run_server = test_stream_transport_change_server, + }, + { + .name = "SOCK_STREAM ioctl(SIOCINQ) functionality", + .run_client = test_stream_unread_bytes_client, + .run_server = test_stream_unread_bytes_server, + }, + { + .name = "SOCK_SEQPACKET ioctl(SIOCINQ) functionality", + .run_client = test_seqpacket_unread_bytes_client, + .run_server = test_seqpacket_unread_bytes_server, + }, {}, }; diff --git a/tools/tracing/latency/Makefile.config b/tools/tracing/latency/Makefile.config index 0fe6b50f029b..6efa13e3ca93 100644 --- a/tools/tracing/latency/Makefile.config +++ b/tools/tracing/latency/Makefile.config @@ -1,7 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only +include $(srctree)/tools/scripts/utilities.mak + STOP_ERROR := +ifndef ($(NO_LIBTRACEEVENT),1) + ifeq ($(call get-executable,$(PKG_CONFIG)),) + $(error Error: $(PKG_CONFIG) needed by libtraceevent/libtracefs is missing on this system, please install it) + endif +endif + define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config index 5f2231d8d626..07ff5e8f3006 100644 --- a/tools/tracing/rtla/Makefile.config +++ b/tools/tracing/rtla/Makefile.config @@ -1,10 +1,18 @@ # SPDX-License-Identifier: GPL-2.0-only +include $(srctree)/tools/scripts/utilities.mak + STOP_ERROR := LIBTRACEEVENT_MIN_VERSION = 1.5 LIBTRACEFS_MIN_VERSION = 1.6 +ifndef ($(NO_LIBTRACEEVENT),1) + ifeq ($(call get-executable,$(PKG_CONFIG)),) + $(error Error: $(PKG_CONFIG) needed by libtraceevent/libtracefs is missing on this system, please install it) + endif +endif + define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build index 7bb7e39e391a..66631280b75b 100644 --- a/tools/tracing/rtla/src/Build +++ b/tools/tracing/rtla/src/Build @@ -1,5 +1,6 @@ rtla-y += trace.o rtla-y += utils.o +rtla-y += actions.o rtla-y += osnoise.o rtla-y += osnoise_top.o rtla-y += osnoise_hist.o diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c new file mode 100644 index 000000000000..aaf0808125d7 --- /dev/null +++ b/tools/tracing/rtla/src/actions.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +#include "actions.h" +#include "trace.h" +#include "utils.h" + +/* + * actions_init - initialize struct actions + */ +void +actions_init(struct actions *self) +{ + self->size = action_default_size; + self->list = calloc(self->size, sizeof(struct action)); + self->len = 0; + self->continue_flag = false; + + memset(&self->present, 0, sizeof(self->present)); + + /* This has to be set by the user */ + self->trace_output_inst = NULL; +} + +/* + * actions_destroy - destroy struct actions + */ +void +actions_destroy(struct actions *self) +{ + /* Free any action-specific data */ + for (struct action *action = self->list; action < self->list + self->len; action++) { + if (action->type == ACTION_SHELL) + free(action->command); + if (action->type == ACTION_TRACE_OUTPUT) + free(action->trace_output); + } + + /* Free action list */ + free(self->list); +} + +/* + * actions_new - Get pointer to new action + */ +static struct action * +actions_new(struct actions *self) +{ + if (self->size >= self->len) { + self->size *= 2; + self->list = realloc(self->list, self->size * sizeof(struct action)); + } + + return &self->list[self->len++]; +} + +/* + * actions_add_trace_output - add an action to output trace + */ +int +actions_add_trace_output(struct actions *self, const char *trace_output) +{ + struct action *action = actions_new(self); + + self->present[ACTION_TRACE_OUTPUT] = true; + action->type = ACTION_TRACE_OUTPUT; + action->trace_output = calloc(strlen(trace_output) + 1, sizeof(char)); + if (!action->trace_output) + return -1; + strcpy(action->trace_output, trace_output); + + return 0; +} + +/* + * actions_add_trace_output - add an action to send signal to a process + */ +int +actions_add_signal(struct actions *self, int signal, int pid) +{ + struct action *action = actions_new(self); + + self->present[ACTION_SIGNAL] = true; + action->type = ACTION_SIGNAL; + action->signal = signal; + action->pid = pid; + + return 0; +} + +/* + * actions_add_shell - add an action to execute a shell command + */ +int +actions_add_shell(struct actions *self, const char *command) +{ + struct action *action = actions_new(self); + + self->present[ACTION_SHELL] = true; + action->type = ACTION_SHELL; + action->command = calloc(strlen(command) + 1, sizeof(char)); + if (!action->command) + return -1; + strcpy(action->command, command); + + return 0; +} + +/* + * actions_add_continue - add an action to resume measurement + */ +int +actions_add_continue(struct actions *self) +{ + struct action *action = actions_new(self); + + self->present[ACTION_CONTINUE] = true; + action->type = ACTION_CONTINUE; + + return 0; +} + +/* + * actions_parse - add an action based on text specification + */ +int +actions_parse(struct actions *self, const char *trigger) +{ + enum action_type type = ACTION_NONE; + char *token; + char trigger_c[strlen(trigger)]; + + /* For ACTION_SIGNAL */ + int signal = 0, pid = 0; + + /* For ACTION_TRACE_OUTPUT */ + char *trace_output; + + strcpy(trigger_c, trigger); + token = strtok(trigger_c, ","); + + if (strcmp(token, "trace") == 0) + type = ACTION_TRACE_OUTPUT; + else if (strcmp(token, "signal") == 0) + type = ACTION_SIGNAL; + else if (strcmp(token, "shell") == 0) + type = ACTION_SHELL; + else if (strcmp(token, "continue") == 0) + type = ACTION_CONTINUE; + else + /* Invalid trigger type */ + return -1; + + token = strtok(NULL, ","); + + switch (type) { + case ACTION_TRACE_OUTPUT: + /* Takes no argument */ + if (token == NULL) + trace_output = "timerlat_trace.txt"; + else { + if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) { + trace_output = token + 5; + } else { + /* Invalid argument */ + return -1; + } + + token = strtok(NULL, ","); + if (token != NULL) + /* Only one argument allowed */ + return -1; + } + return actions_add_trace_output(self, trace_output); + case ACTION_SIGNAL: + /* Takes two arguments, num (signal) and pid */ + while (token != NULL) { + if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) { + signal = atoi(token + 4); + } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { + if (strncmp(token + 4, "parent", 7) == 0) + pid = -1; + else + pid = atoi(token + 4); + } else { + /* Invalid argument */ + return -1; + } + + token = strtok(NULL, ","); + } + + if (!signal || !pid) + /* Missing argument */ + return -1; + + return actions_add_signal(self, signal, pid); + case ACTION_SHELL: + if (token == NULL) + return -1; + if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0) + return actions_add_shell(self, token + 8); + return -1; + case ACTION_CONTINUE: + /* Takes no argument */ + if (token != NULL) + return -1; + return actions_add_continue(self); + default: + return -1; + } +} + +/* + * actions_perform - perform all actions + */ +int +actions_perform(struct actions *self) +{ + int pid, retval; + const struct action *action; + + for (action = self->list; action < self->list + self->len; action++) { + switch (action->type) { + case ACTION_TRACE_OUTPUT: + retval = save_trace_to_file(self->trace_output_inst, action->trace_output); + if (retval) { + err_msg("Error saving trace\n"); + return retval; + } + break; + case ACTION_SIGNAL: + if (action->pid == -1) + pid = getppid(); + else + pid = action->pid; + retval = kill(pid, action->signal); + if (retval) { + err_msg("Error sending signal\n"); + return retval; + } + break; + case ACTION_SHELL: + retval = system(action->command); + if (retval) + return retval; + break; + case ACTION_CONTINUE: + self->continue_flag = true; + return 0; + default: + break; + } + } + + return 0; +} diff --git a/tools/tracing/rtla/src/actions.h b/tools/tracing/rtla/src/actions.h new file mode 100644 index 000000000000..b10a19d55c49 --- /dev/null +++ b/tools/tracing/rtla/src/actions.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +enum action_type { + ACTION_NONE = 0, + ACTION_TRACE_OUTPUT, + ACTION_SIGNAL, + ACTION_SHELL, + ACTION_CONTINUE, + ACTION_FIELD_N +}; + +struct action { + enum action_type type; + union { + struct { + /* For ACTION_TRACE_OUTPUT */ + char *trace_output; + }; + struct { + /* For ACTION_SIGNAL */ + int signal; + int pid; + }; + struct { + /* For ACTION_SHELL */ + char *command; + }; + }; +}; + +static const int action_default_size = 8; + +struct actions { + struct action *list; + int len, size; + bool present[ACTION_FIELD_N]; + bool continue_flag; + + /* External dependencies */ + struct tracefs_instance *trace_output_inst; +}; + +void actions_init(struct actions *self); +void actions_destroy(struct actions *self); +int actions_add_trace_output(struct actions *self, const char *trace_output); +int actions_add_signal(struct actions *self, int signal, int pid); +int actions_add_shell(struct actions *self, const char *command); +int actions_add_continue(struct actions *self); +int actions_parse(struct actions *self, const char *trigger); +int actions_perform(struct actions *self); diff --git a/tools/tracing/rtla/src/timerlat.bpf.c b/tools/tracing/rtla/src/timerlat.bpf.c index 96196d46e170..084cd10c21fc 100644 --- a/tools/tracing/rtla/src/timerlat.bpf.c +++ b/tools/tracing/rtla/src/timerlat.bpf.c @@ -28,6 +28,13 @@ struct { __type(value, unsigned long long); } summary_irq SEC(".maps"), summary_thread SEC(".maps"), summary_user SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, unsigned int); + __type(value, unsigned long long); +} stop_tracing SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1); @@ -41,8 +48,6 @@ const volatile int irq_threshold; const volatile int thread_threshold; const volatile bool aa_only; -int stop_tracing; - nosubprog unsigned long long map_get(void *map, unsigned int key) { @@ -109,7 +114,7 @@ nosubprog void set_stop_tracing(void) int value = 0; /* Suppress further sample processing */ - stop_tracing = 1; + map_set(&stop_tracing, 0, 1); /* Signal to userspace */ bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); @@ -121,7 +126,7 @@ int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args) unsigned long long latency, latency_us; int bucket; - if (stop_tracing) + if (map_get(&stop_tracing, 0)) return 0; latency = tp_args->timer_latency / output_divisor; diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index c29e2ba2d7d8..63d6d43eafff 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -40,16 +40,22 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) CPU_SET(i, ¶ms->monitored_cpus); } - retval = osnoise_set_stop_us(tool->context, params->stop_us); - if (retval) { - err_msg("Failed to set stop us\n"); - goto out_err; - } + if (params->mode != TRACING_MODE_BPF) { + /* + * In tracefs and mixed mode, timerlat tracer handles stopping + * on threshold + */ + retval = osnoise_set_stop_us(tool->context, params->stop_us); + if (retval) { + err_msg("Failed to set stop us\n"); + goto out_err; + } - retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); - if (retval) { - err_msg("Failed to set stop total us\n"); - goto out_err; + retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); + if (retval) { + err_msg("Failed to set stop total us\n"); + goto out_err; + } } diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index 73045aef23fa..bc55ed04fc96 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -1,11 +1,28 @@ // SPDX-License-Identifier: GPL-2.0 +#include "actions.h" #include "osnoise.h" +/* + * Define timerlat tracing mode. + * + * There are three tracing modes: + * - tracefs-only, used when BPF is unavailable. + * - BPF-only, used when BPF is available and neither trace saving nor + * auto-analysis are enabled. + * - mixed mode, used when BPF is available and either trace saving or + * auto-analysis is enabled (which rely on sample collection through + * tracefs). + */ +enum timerlat_tracing_mode { + TRACING_MODE_BPF, + TRACING_MODE_TRACEFS, + TRACING_MODE_MIXED, +}; + struct timerlat_params { /* Common params */ char *cpus; cpu_set_t monitored_cpus; - char *trace_output; char *cgroup_name; unsigned long long runtime; long long stop_us; @@ -30,6 +47,11 @@ struct timerlat_params { cpu_set_t hk_cpu_set; struct sched_attr sched_param; struct trace_events *events; + enum timerlat_tracing_mode mode; + + struct actions threshold_actions; + struct actions end_actions; + union { struct { /* top only */ diff --git a/tools/tracing/rtla/src/timerlat_bpf.c b/tools/tracing/rtla/src/timerlat_bpf.c index 0bc44ce5d69b..1666215dd687 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.c +++ b/tools/tracing/rtla/src/timerlat_bpf.c @@ -106,6 +106,19 @@ int timerlat_bpf_wait(int timeout) return retval; } +/* + * timerlat_bpf_restart_tracing - restart stopped tracing + */ +int timerlat_bpf_restart_tracing(void) +{ + unsigned int key = 0; + unsigned long long value = 0; + + return bpf_map__update_elem(bpf->maps.stop_tracing, + &key, sizeof(key), + &value, sizeof(value), BPF_ANY); +} + static int get_value(struct bpf_map *map_irq, struct bpf_map *map_thread, struct bpf_map *map_user, diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h index f1b54dbddb0e..118487436d30 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.h +++ b/tools/tracing/rtla/src/timerlat_bpf.h @@ -18,6 +18,7 @@ int timerlat_bpf_attach(void); void timerlat_bpf_detach(void); void timerlat_bpf_destroy(void); int timerlat_bpf_wait(int timeout); +int timerlat_bpf_restart_tracing(void); int timerlat_bpf_get_hist_value(int key, long long *value_irq, long long *value_thread, @@ -28,6 +29,7 @@ int timerlat_bpf_get_summary_value(enum summary_field key, long long *value_thread, long long *value_user, int cpus); + static inline int have_libbpf_support(void) { return 1; } #else static inline int timerlat_bpf_init(struct timerlat_params *params) @@ -38,6 +40,7 @@ static inline int timerlat_bpf_attach(void) { return -1; } static inline void timerlat_bpf_detach(void) { }; static inline void timerlat_bpf_destroy(void) { }; static inline int timerlat_bpf_wait(int timeout) { return -1; } +static inline int timerlat_bpf_restart_tracing(void) { return -1; }; static inline int timerlat_bpf_get_hist_value(int key, long long *value_irq, long long *value_thread, diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 36d2294c963d..9baea1b251ed 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -757,6 +757,8 @@ static void timerlat_hist_usage(char *usage) " --warm-up s: let the workload run for s seconds before collecting data", " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", + " --on-threshold : define action to be executed at latency threshold, multiple are allowed", + " --on-end : define action to be executed at measurement end, multiple are allowed", NULL, }; @@ -786,11 +788,15 @@ static struct timerlat_params int auto_thresh; int retval; int c; + char *trace_output = NULL; params = calloc(1, sizeof(*params)); if (!params) exit(1); + actions_init(¶ms->threshold_actions); + actions_init(¶ms->end_actions); + /* disabled by default */ params->dma_latency = -1; @@ -802,6 +808,9 @@ static struct timerlat_params params->bucket_size = 1; params->entries = 256; + /* default to BPF mode */ + params->mode = TRACING_MODE_BPF; + while (1) { static struct option long_options[] = { {"auto", required_argument, 0, 'a'}, @@ -838,6 +847,8 @@ static struct timerlat_params {"warm-up", required_argument, 0, '\2'}, {"trace-buffer-size", required_argument, 0, '\3'}, {"deepest-idle-state", required_argument, 0, '\4'}, + {"on-threshold", required_argument, 0, '\5'}, + {"on-end", required_argument, 0, '\6'}, {0, 0, 0, 0} }; @@ -863,7 +874,7 @@ static struct timerlat_params params->print_stack = auto_thresh; /* set trace */ - params->trace_output = "timerlat_trace.txt"; + trace_output = "timerlat_trace.txt"; break; case 'c': @@ -953,13 +964,13 @@ static struct timerlat_params case 't': if (optarg) { if (optarg[0] == '=') - params->trace_output = &optarg[1]; + trace_output = &optarg[1]; else - params->trace_output = &optarg[0]; + trace_output = &optarg[0]; } else if (optind < argc && argv[optind][0] != '-') - params->trace_output = argv[optind]; + trace_output = argv[optind]; else - params->trace_output = "timerlat_trace.txt"; + trace_output = "timerlat_trace.txt"; break; case 'u': params->user_workload = 1; @@ -1029,11 +1040,28 @@ static struct timerlat_params case '\4': params->deepest_idle_state = get_llong_from_str(optarg); break; + case '\5': + retval = actions_parse(¶ms->threshold_actions, optarg); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case '\6': + retval = actions_parse(¶ms->end_actions, optarg); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; default: timerlat_hist_usage("Invalid option"); } } + if (trace_output) + actions_add_trace_output(¶ms->threshold_actions, trace_output); + if (geteuid()) { err_msg("rtla needs root permission\n"); exit(EXIT_FAILURE); @@ -1054,6 +1082,15 @@ static struct timerlat_params if (params->kernel_workload && params->user_workload) timerlat_hist_usage("--kernel-threads and --user-threads are mutually exclusive!"); + /* + * If auto-analysis or trace output is enabled, switch from BPF mode to + * mixed mode + */ + if (params->mode == TRACING_MODE_BPF && + (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->end_actions.present[ACTION_TRACE_OUTPUT] || !params->no_aa)) + params->mode = TRACING_MODE_MIXED; + return params; } @@ -1149,7 +1186,6 @@ int timerlat_hist_main(int argc, char *argv[]) pthread_t timerlat_u; int retval; int nr_cpus, i; - bool no_bpf = false; params = timerlat_hist_parse_args(argc, argv); if (!params) @@ -1161,12 +1197,6 @@ int timerlat_hist_main(int argc, char *argv[]) goto out_exit; } - retval = timerlat_hist_apply_config(tool, params); - if (retval) { - err_msg("Could not apply config\n"); - goto out_free; - } - trace = &tool->trace; /* * Save trace instance into global variable so that SIGINT can stop @@ -1175,24 +1205,30 @@ int timerlat_hist_main(int argc, char *argv[]) */ hist_inst = trace; + /* + * Try to enable BPF, unless disabled explicitly. + * If BPF enablement fails, fall back to tracefs mode. + */ if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) { debug_msg("RTLA_NO_BPF set, disabling BPF\n"); - no_bpf = true; - } - - if (!no_bpf && !tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { + params->mode = TRACING_MODE_TRACEFS; + } else if (!tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); - no_bpf = true; - } - - if (!no_bpf) { + params->mode = TRACING_MODE_TRACEFS; + } else { retval = timerlat_bpf_init(params); if (retval) { debug_msg("Could not enable BPF\n"); - no_bpf = true; + params->mode = TRACING_MODE_TRACEFS; } } + retval = timerlat_hist_apply_config(tool, params); + if (retval) { + err_msg("Could not apply config\n"); + goto out_free; + } + retval = enable_timerlat(trace); if (retval) { err_msg("Failed to enable timerlat tracer\n"); @@ -1245,12 +1281,15 @@ int timerlat_hist_main(int argc, char *argv[]) } } - if (params->trace_output) { + if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->end_actions.present[ACTION_TRACE_OUTPUT]) { record = osnoise_init_trace_tool("timerlat"); if (!record) { err_msg("Failed to enable the trace instance\n"); goto out_free; } + params->threshold_actions.trace_output_inst = record->trace.inst; + params->end_actions.trace_output_inst = record->trace.inst; if (params->events) { retval = trace_events_enable(&record->trace, params->events); @@ -1316,11 +1355,11 @@ int timerlat_hist_main(int argc, char *argv[]) * tracing while enabling other instances. The trace instance is the * one with most valuable information. */ - if (params->trace_output) + if (record) trace_instance_start(&record->trace); if (!params->no_aa) trace_instance_start(&aa->trace); - if (no_bpf) { + if (params->mode == TRACING_MODE_TRACEFS) { trace_instance_start(trace); } else { retval = timerlat_bpf_attach(); @@ -1333,7 +1372,7 @@ int timerlat_hist_main(int argc, char *argv[]) tool->start_time = time(NULL); timerlat_hist_set_signals(params); - if (no_bpf) { + if (params->mode == TRACING_MODE_TRACEFS) { while (!stop_tracing) { sleep(params->sleep_time); @@ -1348,8 +1387,20 @@ int timerlat_hist_main(int argc, char *argv[]) goto out_hist; } - if (osnoise_trace_is_off(tool, record)) - break; + if (osnoise_trace_is_off(tool, record)) { + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) + /* continue flag not set, break */ + break; + + /* continue action reached, re-enable tracing */ + if (record) + trace_instance_start(&record->trace); + if (!params->no_aa) + trace_instance_start(&aa->trace); + trace_instance_start(trace); + } /* is there still any user-threads ? */ if (params->user_workload) { @@ -1359,10 +1410,29 @@ int timerlat_hist_main(int argc, char *argv[]) } } } - } else - timerlat_bpf_wait(-1); + } else { + while (!stop_tracing) { + timerlat_bpf_wait(-1); - if (!no_bpf) { + if (!stop_tracing) { + /* Threshold overflow, perform actions on threshold */ + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) + /* continue flag not set, break */ + break; + + /* continue action reached, re-enable tracing */ + if (record) + trace_instance_start(&record->trace); + if (!params->no_aa) + trace_instance_start(&aa->trace); + timerlat_bpf_restart_tracing(); + } + } + } + + if (params->mode != TRACING_MODE_TRACEFS) { timerlat_bpf_detach(); retval = timerlat_hist_bpf_pull_data(tool); if (retval) { @@ -1378,6 +1448,8 @@ int timerlat_hist_main(int argc, char *argv[]) timerlat_print_stats(params, tool); + actions_perform(¶ms->end_actions); + return_value = PASSED; if (osnoise_trace_is_off(tool, record) && !stop_tracing) { @@ -1386,8 +1458,6 @@ int timerlat_hist_main(int argc, char *argv[]) if (!params->no_aa) timerlat_auto_analysis(params->stop_us, params->stop_total_us); - save_trace_to_file(record ? record->trace.inst : NULL, - params->trace_output); return_value = FAILED; } @@ -1409,10 +1479,12 @@ int timerlat_hist_main(int argc, char *argv[]) osnoise_destroy_tool(aa); osnoise_destroy_tool(record); osnoise_destroy_tool(tool); + actions_destroy(¶ms->threshold_actions); + actions_destroy(¶ms->end_actions); + if (params->mode != TRACING_MODE_TRACEFS) + timerlat_bpf_destroy(); free(params); free_cpu_idle_disable_states(); - if (!no_bpf) - timerlat_bpf_destroy(); out_exit: exit(return_value); } diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 7365e08fe986..c80b81c0b4da 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -516,6 +516,8 @@ static void timerlat_top_usage(char *usage) " --warm-up s: let the workload run for s seconds before collecting data", " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", + " --on-threshold : define action to be executed at latency threshold, multiple are allowed", + " --on-end: define action to be executed at measurement end, multiple are allowed", NULL, }; @@ -545,11 +547,15 @@ static struct timerlat_params long long auto_thresh; int retval; int c; + char *trace_output = NULL; params = calloc(1, sizeof(*params)); if (!params) exit(1); + actions_init(¶ms->threshold_actions); + actions_init(¶ms->end_actions); + /* disabled by default */ params->dma_latency = -1; @@ -559,6 +565,9 @@ static struct timerlat_params /* display data in microseconds */ params->output_divisor = 1000; + /* default to BPF mode */ + params->mode = TRACING_MODE_BPF; + while (1) { static struct option long_options[] = { {"auto", required_argument, 0, 'a'}, @@ -589,6 +598,8 @@ static struct timerlat_params {"warm-up", required_argument, 0, '6'}, {"trace-buffer-size", required_argument, 0, '7'}, {"deepest-idle-state", required_argument, 0, '8'}, + {"on-threshold", required_argument, 0, '9'}, + {"on-end", required_argument, 0, '\1'}, {0, 0, 0, 0} }; @@ -614,7 +625,8 @@ static struct timerlat_params params->print_stack = auto_thresh; /* set trace */ - params->trace_output = "timerlat_trace.txt"; + trace_output = "timerlat_trace.txt"; + break; case '5': /* it is here because it is similar to -a */ @@ -709,14 +721,13 @@ static struct timerlat_params case 't': if (optarg) { if (optarg[0] == '=') - params->trace_output = &optarg[1]; + trace_output = &optarg[1]; else - params->trace_output = &optarg[0]; + trace_output = &optarg[0]; } else if (optind < argc && argv[optind][0] != '-') - params->trace_output = argv[optind]; + trace_output = argv[optind]; else - params->trace_output = "timerlat_trace.txt"; - + trace_output = "timerlat_trace.txt"; break; case 'u': params->user_workload = true; @@ -768,11 +779,28 @@ static struct timerlat_params case '8': params->deepest_idle_state = get_llong_from_str(optarg); break; + case '9': + retval = actions_parse(¶ms->threshold_actions, optarg); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case '\1': + retval = actions_parse(¶ms->end_actions, optarg); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; default: timerlat_top_usage("Invalid option"); } } + if (trace_output) + actions_add_trace_output(¶ms->threshold_actions, trace_output); + if (geteuid()) { err_msg("rtla needs root permission\n"); exit(EXIT_FAILURE); @@ -790,6 +818,15 @@ static struct timerlat_params if (params->kernel_workload && params->user_workload) timerlat_top_usage("--kernel-threads and --user-threads are mutually exclusive!"); + /* + * If auto-analysis or trace output is enabled, switch from BPF mode to + * mixed mode + */ + if (params->mode == TRACING_MODE_BPF && + (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->end_actions.present[ACTION_TRACE_OUTPUT] || !params->no_aa)) + params->mode = TRACING_MODE_MIXED; + return params; } @@ -881,6 +918,7 @@ timerlat_top_set_signals(struct timerlat_params *params) static int timerlat_top_main_loop(struct osnoise_tool *top, struct osnoise_tool *record, + struct osnoise_tool *aa, struct timerlat_params *params, struct timerlat_u_params *params_u) { @@ -907,8 +945,20 @@ timerlat_top_main_loop(struct osnoise_tool *top, if (!params->quiet) timerlat_print_stats(params, top); - if (osnoise_trace_is_off(top, record)) - break; + if (osnoise_trace_is_off(top, record)) { + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) + /* continue flag not set, break */ + break; + + /* continue action reached, re-enable tracing */ + if (record) + trace_instance_start(&record->trace); + if (!params->no_aa) + trace_instance_start(&aa->trace); + trace_instance_start(trace); + } /* is there still any user-threads ? */ if (params->user_workload) { @@ -928,6 +978,7 @@ timerlat_top_main_loop(struct osnoise_tool *top, static int timerlat_top_bpf_main_loop(struct osnoise_tool *top, struct osnoise_tool *record, + struct osnoise_tool *aa, struct timerlat_params *params, struct timerlat_u_params *params_u) { @@ -939,22 +990,9 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top, return 0; } - if (params->quiet) { - /* Quiet mode: wait for stop and then, print results */ - timerlat_bpf_wait(-1); - - retval = timerlat_top_bpf_pull_data(top); - if (retval) { - err_msg("Error pulling BPF data\n"); - return retval; - } - - return 0; - } - /* Pull and display data in a loop */ while (!stop_tracing) { - wait_retval = timerlat_bpf_wait(params->sleep_time); + wait_retval = timerlat_bpf_wait(params->quiet ? -1 : params->sleep_time); retval = timerlat_top_bpf_pull_data(top); if (retval) { @@ -962,11 +1000,24 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top, return retval; } - timerlat_print_stats(params, top); + if (!params->quiet) + timerlat_print_stats(params, top); - if (wait_retval == 1) + if (wait_retval == 1) { /* Stopping requested by tracer */ - break; + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) + /* continue flag not set, break */ + break; + + /* continue action reached, re-enable tracing */ + if (record) + trace_instance_start(&record->trace); + if (!params->no_aa) + trace_instance_start(&aa->trace); + timerlat_bpf_restart_tracing(); + } /* is there still any user-threads ? */ if (params->user_workload) { @@ -994,7 +1045,6 @@ int timerlat_top_main(int argc, char *argv[]) char *max_lat; int retval; int nr_cpus, i; - bool no_bpf = false; params = timerlat_top_parse_args(argc, argv); if (!params) @@ -1006,38 +1056,38 @@ int timerlat_top_main(int argc, char *argv[]) goto out_exit; } + trace = &top->trace; + /* + * Save trace instance into global variable so that SIGINT can stop + * the timerlat tracer. + * Otherwise, rtla could loop indefinitely when overloaded. + */ + top_inst = trace; + + /* + * Try to enable BPF, unless disabled explicitly. + * If BPF enablement fails, fall back to tracefs mode. + */ + if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) { + debug_msg("RTLA_NO_BPF set, disabling BPF\n"); + params->mode = TRACING_MODE_TRACEFS; + } else if (!tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { + debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); + params->mode = TRACING_MODE_TRACEFS; + } else { + retval = timerlat_bpf_init(params); + if (retval) { + debug_msg("Could not enable BPF\n"); + params->mode = TRACING_MODE_TRACEFS; + } + } + retval = timerlat_top_apply_config(top, params); if (retval) { err_msg("Could not apply config\n"); goto out_free; } - trace = &top->trace; - /* - * Save trace instance into global variable so that SIGINT can stop - * the timerlat tracer. - * Otherwise, rtla could loop indefinitely when overloaded. - */ - top_inst = trace; - - if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) { - debug_msg("RTLA_NO_BPF set, disabling BPF\n"); - no_bpf = true; - } - - if (!no_bpf && !tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { - debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); - no_bpf = true; - } - - if (!no_bpf) { - retval = timerlat_bpf_init(params); - if (retval) { - debug_msg("Could not enable BPF\n"); - no_bpf = true; - } - } - retval = enable_timerlat(trace); if (retval) { err_msg("Failed to enable timerlat tracer\n"); @@ -1090,12 +1140,15 @@ int timerlat_top_main(int argc, char *argv[]) } } - if (params->trace_output) { + if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->end_actions.present[ACTION_TRACE_OUTPUT]) { record = osnoise_init_trace_tool("timerlat"); if (!record) { err_msg("Failed to enable the trace instance\n"); goto out_free; } + params->threshold_actions.trace_output_inst = record->trace.inst; + params->end_actions.trace_output_inst = record->trace.inst; if (params->events) { retval = trace_events_enable(&record->trace, params->events); @@ -1162,11 +1215,11 @@ int timerlat_top_main(int argc, char *argv[]) * tracing while enabling other instances. The trace instance is the * one with most valuable information. */ - if (params->trace_output) + if (record) trace_instance_start(&record->trace); if (!params->no_aa) trace_instance_start(&aa->trace); - if (no_bpf) { + if (params->mode == TRACING_MODE_TRACEFS) { trace_instance_start(trace); } else { retval = timerlat_bpf_attach(); @@ -1179,15 +1232,15 @@ int timerlat_top_main(int argc, char *argv[]) top->start_time = time(NULL); timerlat_top_set_signals(params); - if (no_bpf) - retval = timerlat_top_main_loop(top, record, params, ¶ms_u); + if (params->mode == TRACING_MODE_TRACEFS) + retval = timerlat_top_main_loop(top, record, aa, params, ¶ms_u); else - retval = timerlat_top_bpf_main_loop(top, record, params, ¶ms_u); + retval = timerlat_top_bpf_main_loop(top, record, aa, params, ¶ms_u); if (retval) goto out_top; - if (!no_bpf) + if (params->mode != TRACING_MODE_TRACEFS) timerlat_bpf_detach(); if (params->user_workload && !params_u.stopped_running) { @@ -1197,6 +1250,8 @@ int timerlat_top_main(int argc, char *argv[]) timerlat_print_stats(params, top); + actions_perform(¶ms->end_actions); + return_value = PASSED; if (osnoise_trace_is_off(top, record) && !stop_tracing) { @@ -1205,8 +1260,6 @@ int timerlat_top_main(int argc, char *argv[]) if (!params->no_aa) timerlat_auto_analysis(params->stop_us, params->stop_total_us); - save_trace_to_file(record ? record->trace.inst : NULL, - params->trace_output); return_value = FAILED; } else if (params->aa_only) { /* @@ -1239,6 +1292,10 @@ int timerlat_top_main(int argc, char *argv[]) osnoise_destroy_tool(aa); osnoise_destroy_tool(record); osnoise_destroy_tool(top); + actions_destroy(¶ms->threshold_actions); + actions_destroy(¶ms->end_actions); + if (params->mode != TRACING_MODE_TRACEFS) + timerlat_bpf_destroy(); free(params); free_cpu_idle_disable_states(); out_exit: diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh index f2616a8e4179..a97d644ead99 100644 --- a/tools/tracing/rtla/tests/engine.sh +++ b/tools/tracing/rtla/tests/engine.sh @@ -11,7 +11,7 @@ test_begin() { reset_osnoise() { # Reset osnoise options to default and remove any dangling instances created # by improperly exited rtla runs. - pushd /sys/kernel/tracing || return 1 + pushd /sys/kernel/tracing >/dev/null || return 1 # Remove dangling instances created by previous rtla run echo 0 > tracing_thresh @@ -35,11 +35,14 @@ reset_osnoise() { echo 0 > stop_tracing_us echo 1000 > timerlat_period_us - popd + popd >/dev/null } check() { + test_name=$0 + tested_command=$1 expected_exitcode=${3:-0} + expected_output=$4 # Simple check: run rtla with given arguments and test exit code. # If TEST_COUNT is set, run the test. Otherwise, just count. ctr=$(($ctr + 1)) @@ -49,8 +52,16 @@ check() { [ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise # Run rtla; in case of failure, include its output as comment # in the test results. - result=$(stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$? - if [ $exitcode -eq $expected_exitcode ] + result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$? + # Test if the results matches if requested + if [ -n "$expected_output" ] + then + grep -E "$expected_output" <<< "$result" > /dev/null; grep_result=$? + else + grep_result=0 + fi + + if [ $exitcode -eq $expected_exitcode ] && [ $grep_result -eq 0 ] then echo "ok $ctr - $1" else @@ -58,6 +69,8 @@ check() { # Add rtla output and exit code as comments in case of failure echo "$result" | col -b | while read line; do echo "# $line"; done printf "#\n# exit code %s\n" $exitcode + [ -n "$expected_output" ] && [ $grep_result -ne 0 ] && \ + printf "# Output match failed: \"%s\"\n" "$expected_output" fi fi } diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/hwnoise.t index 5f71401a139e..23ce250a6852 100644 --- a/tools/tracing/rtla/tests/hwnoise.t +++ b/tools/tracing/rtla/tests/hwnoise.t @@ -6,16 +6,17 @@ test_begin set_timeout 2m check "verify help page" \ - "hwnoise --help" + "hwnoise --help" 0 "summary of hardware-related noise" check "detect noise higher than one microsecond" \ - "hwnoise -c 0 -T 1 -d 5s -q" + "hwnoise -c 0 -T 1 -d 5s -q" 0 check "set the automatic trace mode" \ - "hwnoise -a 5 -d 30s" 2 + "hwnoise -a 5 -d 10s" 2 "osnoise hit stop tracing" check "set scheduling param to the osnoise tracer threads" \ - "hwnoise -P F:1 -c 0 -r 900000 -d 1M -q" + "hwnoise -P F:1 -c 0 -r 900000 -d 10s -q" check "stop the trace if a single sample is higher than 1 us" \ - "hwnoise -s 1 -T 1 -t -d 30s" 2 + "hwnoise -s 1 -T 1 -t -d 10s" 2 "Saving trace to osnoise_trace.txt" check "enable a trace event trigger" \ - "hwnoise -t -e osnoise:irq_noise trigger=\"hist:key=desc,duration:sort=desc,duration:vals=hitcount\" -d 1m" + "hwnoise -t -e osnoise:irq_noise --trigger=\"hist:key=desc,duration:sort=desc,duration:vals=hitcount\" -d 10s" \ + 0 "Saving event osnoise:irq_noise hist to osnoise_irq_noise_hist.txt" test_end diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t index 44908fc01abf..7574ec6a5a53 100644 --- a/tools/tracing/rtla/tests/osnoise.t +++ b/tools/tracing/rtla/tests/osnoise.t @@ -6,15 +6,15 @@ test_begin set_timeout 2m check "verify help page" \ - "osnoise --help" + "osnoise --help" 0 "osnoise version" check "verify the --priority/-P param" \ - "osnoise top -P F:1 -c 0 -r 900000 -d 1M -q" + "osnoise top -P F:1 -c 0 -r 900000 -d 10s -q" check "verify the --stop/-s param" \ - "osnoise top -s 30 -T 1 -t" 2 + "osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing" check "verify the --trace param" \ - "osnoise hist -s 30 -T 1 -t" 2 + "osnoise hist -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt" check "verify the --entries/-E param" \ - "osnoise hist -P F:1 -c 0 -r 900000 -d 1M -b 10 -E 25" + "osnoise hist -P F:1 -c 0 -r 900000 -d 10s -b 10 -E 25" # Test setting default period by putting an absurdly high period # and stopping on threshold. diff --git a/tools/tracing/rtla/tests/scripts/check-priority.sh b/tools/tracing/rtla/tests/scripts/check-priority.sh new file mode 100755 index 000000000000..79b702a34a96 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-priority.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +pids="$(pgrep ^$1)" || exit 1 +for pid in $pids +do + chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $2\$" >/dev/null + chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $3\$" >/dev/null +done && echo "Priorities are set correctly" diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t index 579c12a85e8f..c71aed5534bf 100644 --- a/tools/tracing/rtla/tests/timerlat.t +++ b/tools/tracing/rtla/tests/timerlat.t @@ -18,24 +18,55 @@ fi for option in $no_bpf_options do export RTLA_NO_BPF=$option + +# Basic tests check "verify help page" \ - "timerlat --help" + "timerlat --help" 0 "timerlat version" check "verify -s/--stack" \ - "timerlat top -s 3 -T 10 -t" 2 + "timerlat top -s 3 -T 10 -t" 2 "Blocking thread stack trace" check "verify -P/--priority" \ - "timerlat top -P F:1 -c 0 -d 1M -q" + "timerlat top -P F:1 -c 0 -d 10s -q -T 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh timerlatu/ SCHED_FIFO 1\"" \ + 2 "Priorities are set correctly" check "test in nanoseconds" \ - "timerlat top -i 2 -c 0 -n -d 30s" 2 + "timerlat top -i 2 -c 0 -n -d 10s" 2 "ns" check "set the automatic trace mode" \ - "timerlat top -a 5 --dump-tasks" 2 + "timerlat top -a 5" 2 "analyzing it" +check "dump tasks" \ + "timerlat top -a 5 --dump-tasks" 2 "Printing CPU tasks" check "print the auto-analysis if hits the stop tracing condition" \ "timerlat top --aa-only 5" 2 check "disable auto-analysis" \ "timerlat top -s 3 -T 10 -t --no-aa" 2 check "verify -c/--cpus" \ - "timerlat hist -c 0 -d 30s" + "timerlat hist -c 0 -d 10s" check "hist test in nanoseconds" \ - "timerlat hist -i 2 -c 0 -n -d 30s" 2 + "timerlat hist -i 2 -c 0 -n -d 10s" 2 "ns" + +# Actions tests +check "trace output through -t" \ + "timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$" +check "trace output through -t with custom filename" \ + "timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check "trace output through -A trace" \ + "timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$" +check "trace output through -A trace with custom filename" \ + "timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check "exec command" \ + "timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" +check "multiple actions" \ + "timerlat hist -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" +check "hist stop at failed action" \ + "timerlat hist -T 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA timerlat histogram$" +check "top stop at failed action" \ + "timerlat top -T 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1ALL" +check "hist with continue" \ + "timerlat hist -T 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check "top with continue" \ + "timerlat top -q -T 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check "hist with trace output at end" \ + "timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" +check "top with trace output at end" \ + "timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" done test_end diff --git a/tools/verification/dot2/Makefile b/tools/verification/dot2/Makefile deleted file mode 100644 index 021beb07a521..000000000000 --- a/tools/verification/dot2/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -INSTALL=install - -prefix ?= /usr -bindir ?= $(prefix)/bin -mandir ?= $(prefix)/share/man -miscdir ?= $(prefix)/share/dot2 -srcdir ?= $(prefix)/src - -PYLIB ?= $(shell python3 -c 'import sysconfig; print (sysconfig.get_path("purelib"))') - -.PHONY: all -all: - -.PHONY: clean -clean: - -.PHONY: install -install: - $(INSTALL) automata.py -D -m 644 $(DESTDIR)$(PYLIB)/dot2/automata.py - $(INSTALL) dot2c.py -D -m 644 $(DESTDIR)$(PYLIB)/dot2/dot2c.py - $(INSTALL) dot2c -D -m 755 $(DESTDIR)$(bindir)/ - $(INSTALL) dot2k.py -D -m 644 $(DESTDIR)$(PYLIB)/dot2/dot2k.py - $(INSTALL) dot2k -D -m 755 $(DESTDIR)$(bindir)/ - - mkdir -p ${miscdir}/ - cp -rp dot2k_templates $(DESTDIR)$(miscdir)/ diff --git a/tools/verification/dot2/dot2k b/tools/verification/dot2/dot2k deleted file mode 100644 index 767064f415e7..000000000000 --- a/tools/verification/dot2/dot2k +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0-only -# -# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira -# -# dot2k: transform dot files into a monitor for the Linux kernel. -# -# For further information, see: -# Documentation/trace/rv/da_monitor_synthesis.rst - -if __name__ == '__main__': - from dot2.dot2k import dot2k - import argparse - import sys - - def is_container(): - """Should work even before parsing the arguments""" - return "-c" in sys.argv or "--container" in sys.argv - - parser = argparse.ArgumentParser(description='transform .dot file into kernel rv monitor') - parser.add_argument('-d', "--dot", dest="dot_file", required=not is_container()) - parser.add_argument('-t', "--monitor_type", dest="monitor_type", required=not is_container(), - help=f"Available options: {', '.join(dot2k.monitor_types.keys())}") - parser.add_argument('-n', "--model_name", dest="model_name", required=is_container()) - parser.add_argument("-D", "--description", dest="description", required=False) - parser.add_argument("-a", "--auto_patch", dest="auto_patch", - action="store_true", required=False, - help="Patch the kernel in place") - parser.add_argument("-p", "--parent", dest="parent", - required=False, help="Create a monitor nested to parent") - parser.add_argument("-c", "--container", dest="container", - action="store_true", required=False, - help="Create an empty monitor to be used as a container") - params = parser.parse_args() - - if not is_container(): - print("Opening and parsing the dot file %s" % params.dot_file) - try: - monitor=dot2k(params.dot_file, params.monitor_type, vars(params)) - except Exception as e: - print('Error: '+ str(e)) - print("Sorry : :-(") - sys.exit(1) - - print("Writing the monitor into the directory %s" % monitor.name) - monitor.print_files() - print("Almost done, checklist") - if not is_container(): - print(" - Edit the %s/%s.c to add the instrumentation" % (monitor.name, monitor.name)) - print(monitor.fill_tracepoint_tooltip()) - print(monitor.fill_makefile_tooltip()) - print(monitor.fill_kconfig_tooltip()) - print(monitor.fill_monitor_tooltip()) diff --git a/tools/verification/models/rtapp/pagefault.ltl b/tools/verification/models/rtapp/pagefault.ltl new file mode 100644 index 000000000000..d7ce62102733 --- /dev/null +++ b/tools/verification/models/rtapp/pagefault.ltl @@ -0,0 +1 @@ +RULE = always (RT imply not PAGEFAULT) diff --git a/tools/verification/models/rtapp/sleep.ltl b/tools/verification/models/rtapp/sleep.ltl new file mode 100644 index 000000000000..6379bbeb6212 --- /dev/null +++ b/tools/verification/models/rtapp/sleep.ltl @@ -0,0 +1,22 @@ +RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST)) + +RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD) + and ((not WAKE) until RT_FRIENDLY_WAKE) + +RT_VALID_SLEEP_REASON = FUTEX_WAIT + or RT_FRIENDLY_NANOSLEEP + +RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP + and NANOSLEEP_TIMER_ABSTIME + and (NANOSLEEP_CLOCK_MONOTONIC or NANOSLEEP_CLOCK_TAI) + +RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO + or WOKEN_BY_HARDIRQ + or WOKEN_BY_NMI + or ABORT_SLEEP + or KTHREAD_SHOULD_STOP + +ALLOWLIST = BLOCK_ON_RT_MUTEX + or FUTEX_LOCK_PI + or TASK_IS_RCU + or TASK_IS_MIGRATION diff --git a/tools/verification/models/sched/nrp.dot b/tools/verification/models/sched/nrp.dot new file mode 100644 index 000000000000..77bb64669416 --- /dev/null +++ b/tools/verification/models/sched/nrp.dot @@ -0,0 +1,29 @@ +digraph state_automaton { + center = true; + size = "7,11"; + {node [shape = doublecircle] "any_thread_running"}; + {node [shape = circle] "any_thread_running"}; + {node [shape = circle] "nested_preempt"}; + {node [shape = plaintext, style=invis, label=""] "__init_preempt_irq"}; + {node [shape = circle] "preempt_irq"}; + {node [shape = circle] "rescheduling"}; + "__init_preempt_irq" -> "preempt_irq"; + "any_thread_running" [label = "any_thread_running", color = green3]; + "any_thread_running" -> "any_thread_running" [ label = "schedule_entry\nirq_entry" ]; + "any_thread_running" -> "rescheduling" [ label = "sched_need_resched" ]; + "nested_preempt" [label = "nested_preempt"]; + "nested_preempt" -> "any_thread_running" [ label = "schedule_entry_preempt\nschedule_entry" ]; + "nested_preempt" -> "nested_preempt" [ label = "irq_entry" ]; + "nested_preempt" -> "preempt_irq" [ label = "sched_need_resched" ]; + "preempt_irq" [label = "preempt_irq"]; + "preempt_irq" -> "nested_preempt" [ label = "schedule_entry_preempt\nschedule_entry" ]; + "preempt_irq" -> "preempt_irq" [ label = "irq_entry\nsched_need_resched" ]; + "rescheduling" [label = "rescheduling"]; + "rescheduling" -> "any_thread_running" [ label = "schedule_entry_preempt\nschedule_entry" ]; + "rescheduling" -> "preempt_irq" [ label = "irq_entry" ]; + "rescheduling" -> "rescheduling" [ label = "sched_need_resched" ]; + { rank = min ; + "__init_preempt_irq"; + "preempt_irq"; + } +} diff --git a/tools/verification/models/sched/opid.dot b/tools/verification/models/sched/opid.dot new file mode 100644 index 000000000000..840052f6952b --- /dev/null +++ b/tools/verification/models/sched/opid.dot @@ -0,0 +1,35 @@ +digraph state_automaton { + center = true; + size = "7,11"; + {node [shape = plaintext, style=invis, label=""] "__init_disabled"}; + {node [shape = circle] "disabled"}; + {node [shape = doublecircle] "enabled"}; + {node [shape = circle] "enabled"}; + {node [shape = circle] "in_irq"}; + {node [shape = circle] "irq_disabled"}; + {node [shape = circle] "preempt_disabled"}; + "__init_disabled" -> "disabled"; + "disabled" [label = "disabled"]; + "disabled" -> "disabled" [ label = "sched_need_resched\nsched_waking\nirq_entry" ]; + "disabled" -> "irq_disabled" [ label = "preempt_enable" ]; + "disabled" -> "preempt_disabled" [ label = "irq_enable" ]; + "enabled" [label = "enabled", color = green3]; + "enabled" -> "enabled" [ label = "preempt_enable" ]; + "enabled" -> "irq_disabled" [ label = "irq_disable" ]; + "enabled" -> "preempt_disabled" [ label = "preempt_disable" ]; + "in_irq" [label = "in_irq"]; + "in_irq" -> "enabled" [ label = "irq_enable" ]; + "in_irq" -> "in_irq" [ label = "sched_need_resched\nsched_waking\nirq_entry" ]; + "irq_disabled" [label = "irq_disabled"]; + "irq_disabled" -> "disabled" [ label = "preempt_disable" ]; + "irq_disabled" -> "enabled" [ label = "irq_enable" ]; + "irq_disabled" -> "in_irq" [ label = "irq_entry" ]; + "irq_disabled" -> "irq_disabled" [ label = "sched_need_resched" ]; + "preempt_disabled" [label = "preempt_disabled"]; + "preempt_disabled" -> "disabled" [ label = "irq_disable" ]; + "preempt_disabled" -> "enabled" [ label = "preempt_enable" ]; + { rank = min ; + "__init_disabled"; + "disabled"; + } +} diff --git a/tools/verification/models/sched/sncid.dot b/tools/verification/models/sched/sncid.dot deleted file mode 100644 index 072851721b50..000000000000 --- a/tools/verification/models/sched/sncid.dot +++ /dev/null @@ -1,18 +0,0 @@ -digraph state_automaton { - center = true; - size = "7,11"; - {node [shape = plaintext, style=invis, label=""] "__init_can_sched"}; - {node [shape = ellipse] "can_sched"}; - {node [shape = plaintext] "can_sched"}; - {node [shape = plaintext] "cant_sched"}; - "__init_can_sched" -> "can_sched"; - "can_sched" [label = "can_sched", color = green3]; - "can_sched" -> "can_sched" [ label = "schedule_entry\nschedule_exit" ]; - "can_sched" -> "cant_sched" [ label = "irq_disable" ]; - "cant_sched" [label = "cant_sched"]; - "cant_sched" -> "can_sched" [ label = "irq_enable" ]; - { rank = min ; - "__init_can_sched"; - "can_sched"; - } -} diff --git a/tools/verification/models/sched/sssw.dot b/tools/verification/models/sched/sssw.dot new file mode 100644 index 000000000000..4994c3e876be --- /dev/null +++ b/tools/verification/models/sched/sssw.dot @@ -0,0 +1,30 @@ +digraph state_automaton { + center = true; + size = "7,11"; + {node [shape = plaintext, style=invis, label=""] "__init_runnable"}; + {node [shape = doublecircle] "runnable"}; + {node [shape = circle] "runnable"}; + {node [shape = circle] "signal_wakeup"}; + {node [shape = circle] "sleepable"}; + {node [shape = circle] "sleeping"}; + "__init_runnable" -> "runnable"; + "runnable" [label = "runnable", color = green3]; + "runnable" -> "runnable" [ label = "sched_set_state_runnable\nsched_wakeup\nsched_switch_in\nsched_switch_yield\nsched_switch_preempt\nsignal_deliver" ]; + "runnable" -> "sleepable" [ label = "sched_set_state_sleepable" ]; + "runnable" -> "sleeping" [ label = "sched_switch_blocking" ]; + "signal_wakeup" [label = "signal_wakeup"]; + "signal_wakeup" -> "runnable" [ label = "signal_deliver" ]; + "signal_wakeup" -> "signal_wakeup" [ label = "sched_switch_in\nsched_switch_preempt\nsched_switch_yield\nsched_wakeup" ]; + "signal_wakeup" -> "sleepable" [ label = "sched_set_state_sleepable" ]; + "sleepable" [label = "sleepable"]; + "sleepable" -> "runnable" [ label = "sched_set_state_runnable\nsched_wakeup" ]; + "sleepable" -> "signal_wakeup" [ label = "sched_switch_yield" ]; + "sleepable" -> "sleepable" [ label = "sched_set_state_sleepable\nsched_switch_in\nsched_switch_preempt\nsignal_deliver" ]; + "sleepable" -> "sleeping" [ label = "sched_switch_suspend\nsched_switch_blocking" ]; + "sleeping" [label = "sleeping"]; + "sleeping" -> "runnable" [ label = "sched_wakeup" ]; + { rank = min ; + "__init_runnable"; + "runnable"; + } +} diff --git a/tools/verification/models/sched/sts.dot b/tools/verification/models/sched/sts.dot new file mode 100644 index 000000000000..8f5f38be04d5 --- /dev/null +++ b/tools/verification/models/sched/sts.dot @@ -0,0 +1,38 @@ +digraph state_automaton { + center = true; + size = "7,11"; + {node [shape = plaintext, style=invis, label=""] "__init_can_sched"}; + {node [shape = doublecircle] "can_sched"}; + {node [shape = circle] "can_sched"}; + {node [shape = circle] "cant_sched"}; + {node [shape = circle] "disable_to_switch"}; + {node [shape = circle] "enable_to_exit"}; + {node [shape = circle] "in_irq"}; + {node [shape = circle] "scheduling"}; + {node [shape = circle] "switching"}; + "__init_can_sched" -> "can_sched"; + "can_sched" [label = "can_sched", color = green3]; + "can_sched" -> "cant_sched" [ label = "irq_disable" ]; + "can_sched" -> "scheduling" [ label = "schedule_entry" ]; + "cant_sched" [label = "cant_sched"]; + "cant_sched" -> "can_sched" [ label = "irq_enable" ]; + "cant_sched" -> "cant_sched" [ label = "irq_entry" ]; + "disable_to_switch" [label = "disable_to_switch"]; + "disable_to_switch" -> "enable_to_exit" [ label = "irq_enable" ]; + "disable_to_switch" -> "in_irq" [ label = "irq_entry" ]; + "disable_to_switch" -> "switching" [ label = "sched_switch" ]; + "enable_to_exit" [label = "enable_to_exit"]; + "enable_to_exit" -> "can_sched" [ label = "schedule_exit" ]; + "enable_to_exit" -> "enable_to_exit" [ label = "irq_disable\nirq_entry\nirq_enable" ]; + "in_irq" [label = "in_irq"]; + "in_irq" -> "in_irq" [ label = "irq_entry" ]; + "in_irq" -> "scheduling" [ label = "irq_enable" ]; + "scheduling" [label = "scheduling"]; + "scheduling" -> "disable_to_switch" [ label = "irq_disable" ]; + "switching" [label = "switching"]; + "switching" -> "enable_to_exit" [ label = "irq_enable" ]; + { rank = min ; + "__init_can_sched"; + "can_sched"; + } +} diff --git a/tools/verification/models/sched/tss.dot b/tools/verification/models/sched/tss.dot deleted file mode 100644 index 7dfa1d9121bb..000000000000 --- a/tools/verification/models/sched/tss.dot +++ /dev/null @@ -1,18 +0,0 @@ -digraph state_automaton { - center = true; - size = "7,11"; - {node [shape = plaintext] "sched"}; - {node [shape = plaintext, style=invis, label=""] "__init_thread"}; - {node [shape = ellipse] "thread"}; - {node [shape = plaintext] "thread"}; - "__init_thread" -> "thread"; - "sched" [label = "sched"]; - "sched" -> "sched" [ label = "sched_switch" ]; - "sched" -> "thread" [ label = "schedule_exit" ]; - "thread" [label = "thread", color = green3]; - "thread" -> "sched" [ label = "schedule_entry" ]; - { rank = min ; - "__init_thread"; - "thread"; - } -} diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c index c0dcee795c0d..4bb746ea6e17 100644 --- a/tools/verification/rv/src/in_kernel.c +++ b/tools/verification/rv/src/in_kernel.c @@ -431,7 +431,7 @@ ikm_event_handler(struct trace_seq *s, struct tep_record *record, if (config_has_id && (config_my_pid == id)) return 0; - else if (config_my_pid && (config_my_pid == pid)) + else if (config_my_pid == pid) return 0; tep_print_event(trace_event->tep, s, record, "%16s-%-8d [%.3d] ", @@ -734,7 +734,7 @@ static int parse_arguments(char *monitor_name, int argc, char **argv) config_reactor = optarg; break; case 's': - config_my_pid = 0; + config_my_pid = -1; break; case 't': config_trace = 1; diff --git a/tools/verification/rv/src/rv.c b/tools/verification/rv/src/rv.c index 239de054d1e0..b8fe24a87d97 100644 --- a/tools/verification/rv/src/rv.c +++ b/tools/verification/rv/src/rv.c @@ -191,6 +191,7 @@ int main(int argc, char **argv) * and exit. */ signal(SIGINT, stop_rv); + signal(SIGTERM, stop_rv); rv_mon(argc - 1, &argv[1]); } diff --git a/tools/verification/rvgen/.gitignore b/tools/verification/rvgen/.gitignore new file mode 100644 index 000000000000..1e288a076560 --- /dev/null +++ b/tools/verification/rvgen/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +parser.out +parsetab.py diff --git a/tools/verification/rvgen/Makefile b/tools/verification/rvgen/Makefile new file mode 100644 index 000000000000..cfc4056c1e87 --- /dev/null +++ b/tools/verification/rvgen/Makefile @@ -0,0 +1,27 @@ +INSTALL=install + +prefix ?= /usr +bindir ?= $(prefix)/bin +mandir ?= $(prefix)/share/man +srcdir ?= $(prefix)/src + +PYLIB ?= $(shell python3 -c 'import sysconfig; print (sysconfig.get_path("purelib"))') + +.PHONY: all +all: + +.PHONY: clean +clean: + +.PHONY: install +install: + $(INSTALL) rvgen/automata.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/automata.py + $(INSTALL) rvgen/dot2c.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/dot2c.py + $(INSTALL) dot2c -D -m 755 $(DESTDIR)$(bindir)/ + $(INSTALL) rvgen/dot2k.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/dot2k.py + $(INSTALL) rvgen/container.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/container.py + $(INSTALL) rvgen/generator.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/generator.py + $(INSTALL) rvgen/ltl2ba.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/ltl2ba.py + $(INSTALL) rvgen/ltl2k.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/ltl2k.py + $(INSTALL) __main__.py -D -m 755 $(DESTDIR)$(bindir)/rvgen + cp -rp rvgen/templates $(DESTDIR)$(PYLIB)/rvgen/ diff --git a/tools/verification/rvgen/__main__.py b/tools/verification/rvgen/__main__.py new file mode 100644 index 000000000000..fa6fc1f4de2f --- /dev/null +++ b/tools/verification/rvgen/__main__.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira +# +# dot2k: transform dot files into a monitor for the Linux kernel. +# +# For further information, see: +# Documentation/trace/rv/da_monitor_synthesis.rst + +if __name__ == '__main__': + from rvgen.dot2k import dot2k + from rvgen.generator import Monitor + from rvgen.container import Container + from rvgen.ltl2k import ltl2k + import argparse + import sys + + parser = argparse.ArgumentParser(description='Generate kernel rv monitor') + parser.add_argument("-D", "--description", dest="description", required=False) + parser.add_argument("-a", "--auto_patch", dest="auto_patch", + action="store_true", required=False, + help="Patch the kernel in place") + + subparsers = parser.add_subparsers(dest="subcmd", required=True) + + monitor_parser = subparsers.add_parser("monitor") + monitor_parser.add_argument('-n', "--model_name", dest="model_name") + monitor_parser.add_argument("-p", "--parent", dest="parent", + required=False, help="Create a monitor nested to parent") + monitor_parser.add_argument('-c', "--class", dest="monitor_class", + help="Monitor class, either \"da\" or \"ltl\"") + monitor_parser.add_argument('-s', "--spec", dest="spec", help="Monitor specification file") + monitor_parser.add_argument('-t', "--monitor_type", dest="monitor_type", + help=f"Available options: {', '.join(Monitor.monitor_types.keys())}") + + container_parser = subparsers.add_parser("container") + container_parser.add_argument('-n', "--model_name", dest="model_name", required=True) + + params = parser.parse_args() + + try: + if params.subcmd == "monitor": + print("Opening and parsing the specification file %s" % params.spec) + if params.monitor_class == "da": + monitor = dot2k(params.spec, params.monitor_type, vars(params)) + elif params.monitor_class == "ltl": + monitor = ltl2k(params.spec, params.monitor_type, vars(params)) + else: + print("Unknown monitor class:", params.monitor_class) + sys.exit(1) + else: + monitor = Container(vars(params)) + except Exception as e: + print('Error: '+ str(e)) + print("Sorry : :-(") + sys.exit(1) + + print("Writing the monitor into the directory %s" % monitor.name) + monitor.print_files() + print("Almost done, checklist") + if params.subcmd == "monitor": + print(" - Edit the %s/%s.c to add the instrumentation" % (monitor.name, monitor.name)) + print(monitor.fill_tracepoint_tooltip()) + print(monitor.fill_makefile_tooltip()) + print(monitor.fill_kconfig_tooltip()) + print(monitor.fill_monitor_tooltip()) diff --git a/tools/verification/dot2/dot2c b/tools/verification/rvgen/dot2c similarity index 97% rename from tools/verification/dot2/dot2c rename to tools/verification/rvgen/dot2c index 3fe89ab88b65..bf0c67c5b66c 100644 --- a/tools/verification/dot2/dot2c +++ b/tools/verification/rvgen/dot2c @@ -14,7 +14,7 @@ # Documentation/trace/rv/deterministic_automata.rst if __name__ == '__main__': - from dot2 import dot2c + from rvgen import dot2c import argparse import sys diff --git a/tools/verification/dot2/automata.py b/tools/verification/rvgen/rvgen/automata.py similarity index 100% rename from tools/verification/dot2/automata.py rename to tools/verification/rvgen/rvgen/automata.py diff --git a/tools/verification/rvgen/rvgen/container.py b/tools/verification/rvgen/rvgen/container.py new file mode 100644 index 000000000000..51f188530b4d --- /dev/null +++ b/tools/verification/rvgen/rvgen/container.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira +# +# Generator for runtime verification monitor container + +from . import generator + + +class Container(generator.RVGenerator): + template_dir = "container" + + def __init__(self, extra_params={}): + super().__init__(extra_params) + self.name = extra_params.get("model_name") + self.main_h = self._read_template_file("main.h") + + def fill_model_h(self): + main_h = self.main_h + main_h = main_h.replace("%%MODEL_NAME%%", self.name) + return main_h + + def fill_kconfig_tooltip(self): + """Override to produce a marker for this container in the Kconfig""" + container_marker = self._kconfig_marker(self.name) + "\n" + result = super().fill_kconfig_tooltip() + if self.auto_patch: + self._patch_file("Kconfig", + self._kconfig_marker(), container_marker) + return result + return result + container_marker diff --git a/tools/verification/dot2/dot2c.py b/tools/verification/rvgen/rvgen/dot2c.py similarity index 92% rename from tools/verification/dot2/dot2c.py rename to tools/verification/rvgen/rvgen/dot2c.py index fa2816ac7b61..b9b6f14cc536 100644 --- a/tools/verification/dot2/dot2c.py +++ b/tools/verification/rvgen/rvgen/dot2c.py @@ -13,7 +13,7 @@ # For further information, see: # Documentation/trace/rv/deterministic_automata.rst -from dot2.automata import Automata +from .automata import Automata class Dot2c(Automata): enum_suffix = "" @@ -152,28 +152,30 @@ class Dot2c(Automata): max_state_name = max(self.states, key = len).__len__() return max(max_state_name, self.invalid_state_str.__len__()) - def __get_state_string_length(self): - maxlen = self.__get_max_strlen_of_states() + self.enum_suffix.__len__() - return "%" + str(maxlen) + "s" - def get_aut_init_function(self): nr_states = self.states.__len__() nr_events = self.events.__len__() buff = [] - strformat = self.__get_state_string_length() - + maxlen = self.__get_max_strlen_of_states() + len(self.enum_suffix) + tab_braces = 2 * 8 + 2 + 1 # "\t\t{ " ... "}" + comma_space = 2 # ", " count last comma here + linetoolong = tab_braces + (maxlen + comma_space) * nr_events > self.line_length for x in range(nr_states): - line = "\t\t{ " + line = "\t\t{\n" if linetoolong else "\t\t{ " for y in range(nr_events): next_state = self.function[x][y] if next_state != self.invalid_state_str: next_state = self.function[x][y] + self.enum_suffix - if y != nr_events-1: - line = line + strformat % next_state + ", " + if linetoolong: + line += "\t\t\t%s" % next_state else: - line = line + strformat % next_state + " }," + line += "%*s" % (maxlen, next_state) + if y != nr_events-1: + line += ",\n" if linetoolong else ", " + else: + line += "\n\t\t}," if linetoolong else " }," buff.append(line) return self.__buff_to_string(buff) diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/rvgen/rvgen/dot2k.py new file mode 100644 index 000000000000..ed0a3c901106 --- /dev/null +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira +# +# dot2k: transform dot files into a monitor for the Linux kernel. +# +# For further information, see: +# Documentation/trace/rv/da_monitor_synthesis.rst + +from .dot2c import Dot2c +from .generator import Monitor + + +class dot2k(Monitor, Dot2c): + template_dir = "dot2k" + + def __init__(self, file_path, MonitorType, extra_params={}): + self.monitor_type = MonitorType + Monitor.__init__(self, extra_params) + Dot2c.__init__(self, file_path, extra_params.get("model_name")) + self.enum_suffix = "_%s" % self.name + + def fill_monitor_type(self): + return self.monitor_type.upper() + + def fill_tracepoint_handlers_skel(self): + buff = [] + for event in self.events: + buff.append("static void handle_%s(void *data, /* XXX: fill header */)" % event) + buff.append("{") + handle = "handle_event" + if self.is_start_event(event): + buff.append("\t/* XXX: validate that this event always leads to the initial state */") + handle = "handle_start_event" + elif self.is_start_run_event(event): + buff.append("\t/* XXX: validate that this event is only valid in the initial state */") + handle = "handle_start_run_event" + if self.monitor_type == "per_task": + buff.append("\tstruct task_struct *p = /* XXX: how do I get p? */;"); + buff.append("\tda_%s_%s(p, %s%s);" % (handle, self.name, event, self.enum_suffix)); + else: + buff.append("\tda_%s_%s(%s%s);" % (handle, self.name, event, self.enum_suffix)); + buff.append("}") + buff.append("") + return '\n'.join(buff) + + def fill_tracepoint_attach_probe(self): + buff = [] + for event in self.events: + buff.append("\trv_attach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_%s);" % (self.name, event)) + return '\n'.join(buff) + + def fill_tracepoint_detach_helper(self): + buff = [] + for event in self.events: + buff.append("\trv_detach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_%s);" % (self.name, event)) + return '\n'.join(buff) + + def fill_model_h_header(self): + buff = [] + buff.append("/* SPDX-License-Identifier: GPL-2.0 */") + buff.append("/*") + buff.append(" * Automatically generated C representation of %s automaton" % (self.name)) + buff.append(" * For further information about this format, see kernel documentation:") + buff.append(" * Documentation/trace/rv/deterministic_automata.rst") + buff.append(" */") + buff.append("") + + return buff + + def fill_model_h(self): + # + # Adjust the definition names + # + self.enum_states_def = "states_%s" % self.name + self.enum_events_def = "events_%s" % self.name + self.struct_automaton_def = "automaton_%s" % self.name + self.var_automaton_def = "automaton_%s" % self.name + + buff = self.fill_model_h_header() + buff += self.format_model() + + return '\n'.join(buff) + + def fill_monitor_class_type(self): + if self.monitor_type == "per_task": + return "DA_MON_EVENTS_ID" + return "DA_MON_EVENTS_IMPLICIT" + + def fill_monitor_class(self): + if self.monitor_type == "per_task": + return "da_monitor_id" + return "da_monitor" + + def fill_tracepoint_args_skel(self, tp_type): + buff = [] + tp_args_event = [ + ("char *", "state"), + ("char *", "event"), + ("char *", "next_state"), + ("bool ", "final_state"), + ] + tp_args_error = [ + ("char *", "state"), + ("char *", "event"), + ] + tp_args_id = ("int ", "id") + tp_args = tp_args_event if tp_type == "event" else tp_args_error + if self.monitor_type == "per_task": + tp_args.insert(0, tp_args_id) + tp_proto_c = ", ".join([a+b for a,b in tp_args]) + tp_args_c = ", ".join([b for a,b in tp_args]) + buff.append(" TP_PROTO(%s)," % tp_proto_c) + buff.append(" TP_ARGS(%s)" % tp_args_c) + return '\n'.join(buff) + + def fill_main_c(self): + main_c = super().fill_main_c() + + min_type = self.get_minimun_type() + nr_events = len(self.events) + monitor_type = self.fill_monitor_type() + + main_c = main_c.replace("%%MIN_TYPE%%", min_type) + main_c = main_c.replace("%%NR_EVENTS%%", str(nr_events)) + main_c = main_c.replace("%%MONITOR_TYPE%%", monitor_type) + + return main_c diff --git a/tools/verification/dot2/dot2k.py b/tools/verification/rvgen/rvgen/generator.py similarity index 51% rename from tools/verification/dot2/dot2k.py rename to tools/verification/rvgen/rvgen/generator.py index 745d35a4a379..3441385c1177 100644 --- a/tools/verification/dot2/dot2k.py +++ b/tools/verification/rvgen/rvgen/generator.py @@ -3,74 +3,27 @@ # # Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira # -# dot2k: transform dot files into a monitor for the Linux kernel. -# -# For further information, see: -# Documentation/trace/rv/da_monitor_synthesis.rst +# Abtract class for generating kernel runtime verification monitors from specification file -from dot2.dot2c import Dot2c import platform import os -class dot2k(Dot2c): - monitor_types = { "global" : 1, "per_cpu" : 2, "per_task" : 3 } - monitor_templates_dir = "dot2/dot2k_templates/" + +class RVGenerator: rv_dir = "kernel/trace/rv" - monitor_type = "per_cpu" - def __init__(self, file_path, MonitorType, extra_params={}): - self.container = extra_params.get("container") + def __init__(self, extra_params={}): + self.name = extra_params.get("model_name") self.parent = extra_params.get("parent") - self.__fill_rv_templates_dir() - - if self.container: - if file_path: - raise ValueError("A container does not require a dot file") - if MonitorType: - raise ValueError("A container does not require a monitor type") - if self.parent: - raise ValueError("A container cannot have a parent") - self.name = extra_params.get("model_name") - self.events = [] - self.states = [] - self.main_c = self.__read_file(self.monitor_templates_dir + "main_container.c") - self.main_h = self.__read_file(self.monitor_templates_dir + "main_container.h") - else: - super().__init__(file_path, extra_params.get("model_name")) - - self.monitor_type = self.monitor_types.get(MonitorType) - if self.monitor_type is None: - raise ValueError("Unknown monitor type: %s" % MonitorType) - self.monitor_type = MonitorType - self.main_c = self.__read_file(self.monitor_templates_dir + "main.c") - self.trace_h = self.__read_file(self.monitor_templates_dir + "trace.h") - self.kconfig = self.__read_file(self.monitor_templates_dir + "Kconfig") - self.enum_suffix = "_%s" % self.name + self.abs_template_dir = \ + os.path.join(os.path.dirname(__file__), "templates", self.template_dir) + self.main_c = self._read_template_file("main.c") + self.kconfig = self._read_template_file("Kconfig") self.description = extra_params.get("description", self.name) or "auto-generated" self.auto_patch = extra_params.get("auto_patch") if self.auto_patch: self.__fill_rv_kernel_dir() - def __fill_rv_templates_dir(self): - - if os.path.exists(self.monitor_templates_dir): - return - - if platform.system() != "Linux": - raise OSError("I can only run on Linux.") - - kernel_path = "/lib/modules/%s/build/tools/verification/dot2/dot2k_templates/" % (platform.release()) - - if os.path.exists(kernel_path): - self.monitor_templates_dir = kernel_path - return - - if os.path.exists("/usr/share/dot2/dot2k_templates/"): - self.monitor_templates_dir = "/usr/share/dot2/dot2k_templates/" - return - - raise FileNotFoundError("Could not find the template directory, do you have the kernel source installed?") - def __fill_rv_kernel_dir(self): # first try if we are running in the kernel tree root @@ -97,7 +50,7 @@ class dot2k(Dot2c): raise FileNotFoundError("Could not find the rv directory, do you have the kernel source installed?") - def __read_file(self, path): + def _read_file(self, path): try: fd = open(path, 'r') except OSError: @@ -108,17 +61,15 @@ class dot2k(Dot2c): fd.close() return content - def __buff_to_string(self, buff): - string = "" - - for line in buff: - string = string + line + "\n" - - # cut off the last \n - return string[:-1] - - def fill_monitor_type(self): - return self.monitor_type.upper() + def _read_template_file(self, file): + try: + path = os.path.join(self.abs_template_dir, file) + return self._read_file(path) + except Exception: + # Specific template file not found. Try the generic template file in the template/ + # directory, which is one level up + path = os.path.join(self.abs_template_dir, "..", file) + return self._read_file(path) def fill_parent(self): return "&rv_%s" % self.parent if self.parent else "NULL" @@ -129,53 +80,23 @@ class dot2k(Dot2c): return "" def fill_tracepoint_handlers_skel(self): - buff = [] - for event in self.events: - buff.append("static void handle_%s(void *data, /* XXX: fill header */)" % event) - buff.append("{") - handle = "handle_event" - if self.is_start_event(event): - buff.append("\t/* XXX: validate that this event always leads to the initial state */") - handle = "handle_start_event" - elif self.is_start_run_event(event): - buff.append("\t/* XXX: validate that this event is only valid in the initial state */") - handle = "handle_start_run_event" - if self.monitor_type == "per_task": - buff.append("\tstruct task_struct *p = /* XXX: how do I get p? */;"); - buff.append("\tda_%s_%s(p, %s%s);" % (handle, self.name, event, self.enum_suffix)); - else: - buff.append("\tda_%s_%s(%s%s);" % (handle, self.name, event, self.enum_suffix)); - buff.append("}") - buff.append("") - return self.__buff_to_string(buff) + return "NotImplemented" def fill_tracepoint_attach_probe(self): - buff = [] - for event in self.events: - buff.append("\trv_attach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_%s);" % (self.name, event)) - return self.__buff_to_string(buff) + return "NotImplemented" def fill_tracepoint_detach_helper(self): - buff = [] - for event in self.events: - buff.append("\trv_detach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_%s);" % (self.name, event)) - return self.__buff_to_string(buff) + return "NotImplemented" def fill_main_c(self): main_c = self.main_c - monitor_type = self.fill_monitor_type() - min_type = self.get_minimun_type() - nr_events = len(self.events) tracepoint_handlers = self.fill_tracepoint_handlers_skel() tracepoint_attach = self.fill_tracepoint_attach_probe() tracepoint_detach = self.fill_tracepoint_detach_helper() parent = self.fill_parent() parent_include = self.fill_include_parent() - main_c = main_c.replace("%%MONITOR_TYPE%%", monitor_type) - main_c = main_c.replace("%%MIN_TYPE%%", min_type) main_c = main_c.replace("%%MODEL_NAME%%", self.name) - main_c = main_c.replace("%%NR_EVENTS%%", str(nr_events)) main_c = main_c.replace("%%TRACEPOINT_HANDLERS_SKEL%%", tracepoint_handlers) main_c = main_c.replace("%%TRACEPOINT_ATTACH%%", tracepoint_attach) main_c = main_c.replace("%%TRACEPOINT_DETACH%%", tracepoint_detach) @@ -185,63 +106,17 @@ class dot2k(Dot2c): return main_c - def fill_model_h_header(self): - buff = [] - buff.append("/* SPDX-License-Identifier: GPL-2.0 */") - buff.append("/*") - buff.append(" * Automatically generated C representation of %s automaton" % (self.name)) - buff.append(" * For further information about this format, see kernel documentation:") - buff.append(" * Documentation/trace/rv/deterministic_automata.rst") - buff.append(" */") - buff.append("") - - return buff - def fill_model_h(self): - # - # Adjust the definition names - # - self.enum_states_def = "states_%s" % self.name - self.enum_events_def = "events_%s" % self.name - self.struct_automaton_def = "automaton_%s" % self.name - self.var_automaton_def = "automaton_%s" % self.name - - buff = self.fill_model_h_header() - buff += self.format_model() - - return self.__buff_to_string(buff) + return "NotImplemented" def fill_monitor_class_type(self): - if self.monitor_type == "per_task": - return "DA_MON_EVENTS_ID" - return "DA_MON_EVENTS_IMPLICIT" + return "NotImplemented" def fill_monitor_class(self): - if self.monitor_type == "per_task": - return "da_monitor_id" - return "da_monitor" + return "NotImplemented" def fill_tracepoint_args_skel(self, tp_type): - buff = [] - tp_args_event = [ - ("char *", "state"), - ("char *", "event"), - ("char *", "next_state"), - ("bool ", "final_state"), - ] - tp_args_error = [ - ("char *", "state"), - ("char *", "event"), - ] - tp_args_id = ("int ", "id") - tp_args = tp_args_event if tp_type == "event" else tp_args_error - if self.monitor_type == "per_task": - tp_args.insert(0, tp_args_id) - tp_proto_c = ", ".join([a+b for a,b in tp_args]) - tp_args_c = ", ".join([b for a,b in tp_args]) - buff.append(" TP_PROTO(%s)," % tp_proto_c) - buff.append(" TP_ARGS(%s)" % tp_args_c) - return self.__buff_to_string(buff) + return "NotImplemented" def fill_monitor_deps(self): buff = [] @@ -249,21 +124,7 @@ class dot2k(Dot2c): if self.parent: buff.append(" depends on RV_MON_%s" % self.parent.upper()) buff.append(" default y") - return self.__buff_to_string(buff) - - def fill_trace_h(self): - trace_h = self.trace_h - monitor_class = self.fill_monitor_class() - monitor_class_type = self.fill_monitor_class_type() - tracepoint_args_skel_event = self.fill_tracepoint_args_skel("event") - tracepoint_args_skel_error = self.fill_tracepoint_args_skel("error") - trace_h = trace_h.replace("%%MODEL_NAME%%", self.name) - trace_h = trace_h.replace("%%MODEL_NAME_UP%%", self.name.upper()) - trace_h = trace_h.replace("%%MONITOR_CLASS%%", monitor_class) - trace_h = trace_h.replace("%%MONITOR_CLASS_TYPE%%", monitor_class_type) - trace_h = trace_h.replace("%%TRACEPOINT_ARGS_SKEL_EVENT%%", tracepoint_args_skel_event) - trace_h = trace_h.replace("%%TRACEPOINT_ARGS_SKEL_ERROR%%", tracepoint_args_skel_error) - return trace_h + return '\n'.join(buff) def fill_kconfig(self): kconfig = self.kconfig @@ -276,21 +137,17 @@ class dot2k(Dot2c): kconfig = kconfig.replace("%%MONITOR_DEPS%%", monitor_deps) return kconfig - def fill_main_container_h(self): - main_h = self.main_h - main_h = main_h.replace("%%MODEL_NAME%%", self.name) - return main_h - - def __patch_file(self, file, marker, line): + def _patch_file(self, file, marker, line): + assert self.auto_patch file_to_patch = os.path.join(self.rv_dir, file) - content = self.__read_file(file_to_patch) + content = self._read_file(file_to_patch) content = content.replace(marker, line + "\n" + marker) self.__write_file(file_to_patch, content) def fill_tracepoint_tooltip(self): monitor_class_type = self.fill_monitor_class_type() if self.auto_patch: - self.__patch_file("rv_trace.h", + self._patch_file("rv_trace.h", "// Add new monitors based on CONFIG_%s here" % monitor_class_type, "#include " % (self.name, self.name)) return " - Patching %s/rv_trace.h, double check the result" % self.rv_dir @@ -300,10 +157,15 @@ Add this line where other tracepoints are included and %s is defined: #include """ % (self.rv_dir, monitor_class_type, self.name, self.name) + def _kconfig_marker(self, container=None) -> str: + return "# Add new %smonitors here" % (container + " " + if container else "") + def fill_kconfig_tooltip(self): if self.auto_patch: - self.__patch_file("Kconfig", - "# Add new monitors here", + # monitors with a container should stay together in the Kconfig + self._patch_file("Kconfig", + self._kconfig_marker(self.parent), "source \"kernel/trace/rv/monitors/%s/Kconfig\"" % (self.name)) return " - Patching %s/Kconfig, double check the result" % self.rv_dir @@ -316,7 +178,7 @@ source \"kernel/trace/rv/monitors/%s/Kconfig\" name = self.name name_up = name.upper() if self.auto_patch: - self.__patch_file("Makefile", + self._patch_file("Makefile", "# Add new monitors here", "obj-$(CONFIG_RV_MON_%s) += monitors/%s/%s.o" % (name_up, name, name)) return " - Patching %s/Makefile, double check the result" % self.rv_dir @@ -352,7 +214,7 @@ obj-$(CONFIG_RV_MON_%s) += monitors/%s/%s.o file.close() - def __create_file(self, file_name, content): + def _create_file(self, file_name, content): path = "%s/%s" % (self.name, file_name) if self.auto_patch: path = os.path.join(self.rv_dir, "monitors", path) @@ -370,20 +232,39 @@ obj-$(CONFIG_RV_MON_%s) += monitors/%s/%s.o self.__create_directory() path = "%s.c" % self.name - self.__create_file(path, main_c) + self._create_file(path, main_c) - if self.container: - main_h = self.fill_main_container_h() - path = "%s.h" % self.name - self.__create_file(path, main_h) - else: - model_h = self.fill_model_h() - path = "%s.h" % self.name - self.__create_file(path, model_h) - - trace_h = self.fill_trace_h() - path = "%s_trace.h" % self.name - self.__create_file(path, trace_h) + model_h = self.fill_model_h() + path = "%s.h" % self.name + self._create_file(path, model_h) kconfig = self.fill_kconfig() - self.__create_file("Kconfig", kconfig) + self._create_file("Kconfig", kconfig) + + +class Monitor(RVGenerator): + monitor_types = { "global" : 1, "per_cpu" : 2, "per_task" : 3 } + + def __init__(self, extra_params={}): + super().__init__(extra_params) + self.trace_h = self._read_template_file("trace.h") + + def fill_trace_h(self): + trace_h = self.trace_h + monitor_class = self.fill_monitor_class() + monitor_class_type = self.fill_monitor_class_type() + tracepoint_args_skel_event = self.fill_tracepoint_args_skel("event") + tracepoint_args_skel_error = self.fill_tracepoint_args_skel("error") + trace_h = trace_h.replace("%%MODEL_NAME%%", self.name) + trace_h = trace_h.replace("%%MODEL_NAME_UP%%", self.name.upper()) + trace_h = trace_h.replace("%%MONITOR_CLASS%%", monitor_class) + trace_h = trace_h.replace("%%MONITOR_CLASS_TYPE%%", monitor_class_type) + trace_h = trace_h.replace("%%TRACEPOINT_ARGS_SKEL_EVENT%%", tracepoint_args_skel_event) + trace_h = trace_h.replace("%%TRACEPOINT_ARGS_SKEL_ERROR%%", tracepoint_args_skel_error) + return trace_h + + def print_files(self): + super().print_files() + trace_h = self.fill_trace_h() + path = "%s_trace.h" % self.name + self._create_file(path, trace_h) diff --git a/tools/verification/rvgen/rvgen/ltl2ba.py b/tools/verification/rvgen/rvgen/ltl2ba.py new file mode 100644 index 000000000000..f14e6760ac3d --- /dev/null +++ b/tools/verification/rvgen/rvgen/ltl2ba.py @@ -0,0 +1,566 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Implementation based on +# Gerth, R., Peled, D., Vardi, M.Y., Wolper, P. (1996). +# Simple On-the-fly Automatic Verification of Linear Temporal Logic. +# https://doi.org/10.1007/978-0-387-34892-6_1 +# With extra optimizations + +from ply.lex import lex +from ply.yacc import yacc + +# Grammar: +# ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl +# +# Operands (opd): +# true, false, user-defined names +# +# Unary Operators (unop): +# always +# eventually +# next +# not +# +# Binary Operators (binop): +# until +# and +# or +# imply +# equivalent + +tokens = ( + 'AND', + 'OR', + 'IMPLY', + 'UNTIL', + 'ALWAYS', + 'EVENTUALLY', + 'NEXT', + 'VARIABLE', + 'LITERAL', + 'NOT', + 'LPAREN', + 'RPAREN', + 'ASSIGN', +) + +t_AND = r'and' +t_OR = r'or' +t_IMPLY = r'imply' +t_UNTIL = r'until' +t_ALWAYS = r'always' +t_NEXT = r'next' +t_EVENTUALLY = r'eventually' +t_VARIABLE = r'[A-Z_0-9]+' +t_LITERAL = r'true|false' +t_NOT = r'not' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_ASSIGN = r'=' +t_ignore_COMMENT = r'\#.*' +t_ignore = ' \t\n' + +def t_error(t): + raise ValueError(f"Illegal character '{t.value[0]}'") + +lexer = lex() + +class GraphNode: + uid = 0 + + def __init__(self, incoming: set['GraphNode'], new, old, _next): + self.init = False + self.outgoing = set() + self.labels = set() + self.incoming = incoming.copy() + self.new = new.copy() + self.old = old.copy() + self.next = _next.copy() + self.id = GraphNode.uid + GraphNode.uid += 1 + + def expand(self, node_set): + if not self.new: + for nd in node_set: + if nd.old == self.old and nd.next == self.next: + nd.incoming |= self.incoming + return node_set + + new_current_node = GraphNode({self}, self.next, set(), set()) + return new_current_node.expand({self} | node_set) + n = self.new.pop() + return n.expand(self, node_set) + + def __lt__(self, other): + return self.id < other.id + +class ASTNode: + uid = 1 + + def __init__(self, op): + self.op = op + self.id = ASTNode.uid + ASTNode.uid += 1 + + def __hash__(self): + return hash(self.op) + + def __eq__(self, other): + return self is other + + def __iter__(self): + yield self + yield from self.op + + def negate(self): + self.op = self.op.negate() + return self + + def expand(self, node, node_set): + return self.op.expand(self, node, node_set) + + def __str__(self): + if isinstance(self.op, Literal): + return str(self.op.value) + if isinstance(self.op, Variable): + return self.op.name.lower() + return "val" + str(self.id) + + def normalize(self): + # Get rid of: + # - ALWAYS + # - EVENTUALLY + # - IMPLY + # And move all the NOT to be inside + self.op = self.op.normalize() + return self + +class BinaryOp: + op_str = "not_supported" + + def __init__(self, left: ASTNode, right: ASTNode): + self.left = left + self.right = right + + def __hash__(self): + return hash((self.left, self.right)) + + def __iter__(self): + yield from self.left + yield from self.right + + def normalize(self): + raise NotImplementedError + + def negate(self): + raise NotImplementedError + + def _is_temporal(self): + raise NotImplementedError + + def is_temporal(self): + if self.left.op.is_temporal(): + return True + if self.right.op.is_temporal(): + return True + return self._is_temporal() + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + raise NotImplementedError + +class AndOp(BinaryOp): + op_str = '&&' + + def normalize(self): + return self + + def negate(self): + return OrOp(self.left.negate(), self.right.negate()) + + def _is_temporal(self): + return False + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + if not n.op.is_temporal(): + node.old.add(n) + return node.expand(node_set) + + tmp = GraphNode(node.incoming, + node.new | ({n.op.left, n.op.right} - node.old), + node.old | {n}, + node.next) + return tmp.expand(node_set) + +class OrOp(BinaryOp): + op_str = '||' + + def normalize(self): + return self + + def negate(self): + return AndOp(self.left.negate(), self.right.negate()) + + def _is_temporal(self): + return False + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + if not n.op.is_temporal(): + node.old |= {n} + return node.expand(node_set) + + node1 = GraphNode(node.incoming, + node.new | ({n.op.left} - node.old), + node.old | {n}, + node.next) + node2 = GraphNode(node.incoming, + node.new | ({n.op.right} - node.old), + node.old | {n}, + node.next) + return node2.expand(node1.expand(node_set)) + +class UntilOp(BinaryOp): + def normalize(self): + return self + + def negate(self): + return VOp(self.left.negate(), self.right.negate()) + + def _is_temporal(self): + return True + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + node1 = GraphNode(node.incoming, + node.new | ({n.op.left} - node.old), + node.old | {n}, + node.next | {n}) + node2 = GraphNode(node.incoming, + node.new | ({n.op.right} - node.old), + node.old | {n}, + node.next) + return node2.expand(node1.expand(node_set)) + +class VOp(BinaryOp): + def normalize(self): + return self + + def negate(self): + return UntilOp(self.left.negate(), self.right.negate()) + + def _is_temporal(self): + return True + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + node1 = GraphNode(node.incoming, + node.new | ({n.op.right} - node.old), + node.old | {n}, + node.next | {n}) + node2 = GraphNode(node.incoming, + node.new | ({n.op.left, n.op.right} - node.old), + node.old | {n}, + node.next) + return node2.expand(node1.expand(node_set)) + +class ImplyOp(BinaryOp): + def normalize(self): + # P -> Q === !P | Q + return OrOp(self.left.negate(), self.right) + + def _is_temporal(self): + return False + + def negate(self): + # !(P -> Q) === !(!P | Q) === P & !Q + return AndOp(self.left, self.right.negate()) + +class UnaryOp: + def __init__(self, child: ASTNode): + self.child = child + + def __iter__(self): + yield from self.child + + def __hash__(self): + return hash(self.child) + + def normalize(self): + raise NotImplementedError + + def _is_temporal(self): + raise NotImplementedError + + def is_temporal(self): + if self.child.op.is_temporal(): + return True + return self._is_temporal() + + def negate(self): + raise NotImplementedError + +class EventuallyOp(UnaryOp): + def __str__(self): + return "eventually " + str(self.child) + + def normalize(self): + # <>F == true U F + return UntilOp(ASTNode(Literal(True)), self.child) + + def _is_temporal(self): + return True + + def negate(self): + # !<>F == [](!F) + return AlwaysOp(self.child.negate()).normalize() + +class AlwaysOp(UnaryOp): + def normalize(self): + # []F === !(true U !F) == false V F + new = ASTNode(Literal(False)) + return VOp(new, self.child) + + def _is_temporal(self): + return True + + def negate(self): + # ![]F == <>(!F) + return EventuallyOp(self.child.negate()).normalize() + +class NextOp(UnaryOp): + def normalize(self): + return self + + def _is_temporal(self): + return True + + def negate(self): + # not (next A) == next (not A) + self.child = self.child.negate() + return self + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + tmp = GraphNode(node.incoming, + node.new, + node.old | {n}, + node.next | {n.op.child}) + return tmp.expand(node_set) + +class NotOp(UnaryOp): + def __str__(self): + return "!" + str(self.child) + + def normalize(self): + return self.child.op.negate() + + def negate(self): + return self.child.op + + def _is_temporal(self): + return False + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + for f in node.old: + if n.op.child is f: + return node_set + node.old |= {n} + return node.expand(node_set) + +class Variable: + def __init__(self, name: str): + self.name = name + + def __hash__(self): + return hash(self.name) + + def __iter__(self): + yield from () + + def negate(self): + new = ASTNode(self) + return NotOp(new) + + def normalize(self): + return self + + def is_temporal(self): + return False + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + for f in node.old: + if isinstance(f, NotOp) and f.op.child is n: + return node_set + node.old |= {n} + return node.expand(node_set) + +class Literal: + def __init__(self, value: bool): + self.value = value + + def __iter__(self): + yield from () + + def __hash__(self): + return hash(self.value) + + def __str__(self): + if self.value: + return "true" + return "false" + + def negate(self): + self.value = not self.value + return self + + def normalize(self): + return self + + def is_temporal(self): + return False + + @staticmethod + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: + if not n.op.value: + return node_set + node.old |= {n} + return node.expand(node_set) + +def p_spec(p): + ''' + spec : assign + | assign spec + ''' + if len(p) == 3: + p[2].append(p[1]) + p[0] = p[2] + else: + p[0] = [p[1]] + +def p_assign(p): + ''' + assign : VARIABLE ASSIGN ltl + ''' + p[0] = (p[1], p[3]) + +def p_ltl(p): + ''' + ltl : opd + | binop + | unop + ''' + p[0] = p[1] + +def p_opd(p): + ''' + opd : VARIABLE + | LITERAL + | LPAREN ltl RPAREN + ''' + if p[1] == "true": + p[0] = ASTNode(Literal(True)) + elif p[1] == "false": + p[0] = ASTNode(Literal(False)) + elif p[1] == '(': + p[0] = p[2] + else: + p[0] = ASTNode(Variable(p[1])) + +def p_unop(p): + ''' + unop : ALWAYS ltl + | EVENTUALLY ltl + | NEXT ltl + | NOT ltl + ''' + if p[1] == "always": + op = AlwaysOp(p[2]) + elif p[1] == "eventually": + op = EventuallyOp(p[2]) + elif p[1] == "next": + op = NextOp(p[2]) + elif p[1] == "not": + op = NotOp(p[2]) + else: + raise ValueError(f"Invalid unary operator {p[1]}") + + p[0] = ASTNode(op) + +def p_binop(p): + ''' + binop : opd UNTIL ltl + | opd AND ltl + | opd OR ltl + | opd IMPLY ltl + ''' + if p[2] == "and": + op = AndOp(p[1], p[3]) + elif p[2] == "until": + op = UntilOp(p[1], p[3]) + elif p[2] == "or": + op = OrOp(p[1], p[3]) + elif p[2] == "imply": + op = ImplyOp(p[1], p[3]) + else: + raise ValueError(f"Invalid binary operator {p[2]}") + + p[0] = ASTNode(op) + +parser = yacc() + +def parse_ltl(s: str) -> ASTNode: + spec = parser.parse(s) + + rule = None + subexpr = {} + + for assign in spec: + if assign[0] == "RULE": + rule = assign[1] + else: + subexpr[assign[0]] = assign[1] + + if rule is None: + raise ValueError("Please define your specification in the \"RULE = \" format") + + for node in rule: + if not isinstance(node.op, Variable): + continue + replace = subexpr.get(node.op.name) + if replace is not None: + node.op = replace.op + + return rule + +def create_graph(s: str): + atoms = set() + + ltl = parse_ltl(s) + for c in ltl: + c.normalize() + if isinstance(c.op, Variable): + atoms.add(c.op.name) + + init = GraphNode(set(), set(), set(), set()) + head = GraphNode({init}, {ltl}, set(), set()) + graph = sorted(head.expand(set())) + + for i, node in enumerate(graph): + # The id assignment during graph generation has gaps. Reassign them + node.id = i + + for incoming in node.incoming: + if incoming is init: + node.init = True + else: + incoming.outgoing.add(node) + for o in node.old: + if not o.op.is_temporal(): + node.labels.add(str(o)) + + return sorted(atoms), graph, ltl diff --git a/tools/verification/rvgen/rvgen/ltl2k.py b/tools/verification/rvgen/rvgen/ltl2k.py new file mode 100644 index 000000000000..b075f98d50c4 --- /dev/null +++ b/tools/verification/rvgen/rvgen/ltl2k.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only + +from pathlib import Path +from . import generator +from . import ltl2ba + +COLUMN_LIMIT = 100 + +def line_len(line: str) -> int: + tabs = line.count('\t') + return tabs * 7 + len(line) + +def break_long_line(line: str, indent='') -> list[str]: + result = [] + while line_len(line) > COLUMN_LIMIT: + i = line[:COLUMN_LIMIT - line_len(line)].rfind(' ') + result.append(line[:i]) + line = indent + line[i + 1:] + if line: + result.append(line) + return result + +def build_condition_string(node: ltl2ba.GraphNode): + if not node.labels: + return "(true)" + + result = "(" + + first = True + for label in sorted(node.labels): + if not first: + result += " && " + result += label + first = False + + result += ")" + + return result + +def abbreviate_atoms(atoms: list[str]) -> list[str]: + def shorten(s: str) -> str: + skip = ["is", "by", "or", "and"] + return '_'.join([x[:2] for x in s.lower().split('_') if x not in skip]) + + abbrs = [] + for atom in atoms: + for i in range(len(atom), -1, -1): + if sum(a.startswith(atom[:i]) for a in atoms) > 1: + break + share = atom[:i] + unique = atom[i:] + abbrs.append((shorten(share) + shorten(unique))) + return abbrs + +class ltl2k(generator.Monitor): + template_dir = "ltl2k" + + def __init__(self, file_path, MonitorType, extra_params={}): + if MonitorType != "per_task": + raise NotImplementedError("Only per_task monitor is supported for LTL") + super().__init__(extra_params) + with open(file_path) as f: + self.atoms, self.ba, self.ltl = ltl2ba.create_graph(f.read()) + self.atoms_abbr = abbreviate_atoms(self.atoms) + self.name = extra_params.get("model_name") + if not self.name: + self.name = Path(file_path).stem + + def _fill_states(self) -> str: + buf = [ + "enum ltl_buchi_state {", + ] + + for node in self.ba: + buf.append("\tS%i," % node.id) + buf.append("\tRV_NUM_BA_STATES") + buf.append("};") + buf.append("static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);") + return buf + + def _fill_atoms(self): + buf = ["enum ltl_atom {"] + for a in sorted(self.atoms): + buf.append("\tLTL_%s," % a) + buf.append("\tLTL_NUM_ATOM") + buf.append("};") + buf.append("static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);") + return buf + + def _fill_atoms_to_string(self): + buf = [ + "static const char *ltl_atom_str(enum ltl_atom atom)", + "{", + "\tstatic const char *const names[] = {" + ] + + for name in self.atoms_abbr: + buf.append("\t\t\"%s\"," % name) + + buf.extend([ + "\t};", + "", + "\treturn names[atom];", + "}" + ]) + return buf + + def _fill_atom_values(self, required_values): + buf = [] + for node in self.ltl: + if str(node) not in required_values: + continue + + if isinstance(node.op, ltl2ba.AndOp): + buf.append("\tbool %s = %s && %s;" % (node, node.op.left, node.op.right)) + required_values |= {str(node.op.left), str(node.op.right)} + elif isinstance(node.op, ltl2ba.OrOp): + buf.append("\tbool %s = %s || %s;" % (node, node.op.left, node.op.right)) + required_values |= {str(node.op.left), str(node.op.right)} + elif isinstance(node.op, ltl2ba.NotOp): + buf.append("\tbool %s = !%s;" % (node, node.op.child)) + required_values.add(str(node.op.child)) + + for atom in self.atoms: + if atom.lower() not in required_values: + continue + buf.append("\tbool %s = test_bit(LTL_%s, mon->atoms);" % (atom.lower(), atom)) + + buf.reverse() + + buf2 = [] + for line in buf: + buf2.extend(break_long_line(line, "\t ")) + return buf2 + + def _fill_transitions(self): + buf = [ + "static void", + "ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)", + "{" + ] + + required_values = set() + for node in self.ba: + for o in sorted(node.outgoing): + required_values |= o.labels + + buf.extend(self._fill_atom_values(required_values)) + buf.extend([ + "", + "\tswitch (state) {" + ]) + + for node in self.ba: + buf.append("\tcase S%i:" % node.id) + + for o in sorted(node.outgoing): + line = "\t\tif " + indent = "\t\t " + + line += build_condition_string(o) + lines = break_long_line(line, indent) + buf.extend(lines) + + buf.append("\t\t\t__set_bit(S%i, next);" % o.id) + buf.append("\t\tbreak;") + buf.extend([ + "\t}", + "}" + ]) + + return buf + + def _fill_start(self): + buf = [ + "static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)", + "{" + ] + + required_values = set() + for node in self.ba: + if node.init: + required_values |= node.labels + + buf.extend(self._fill_atom_values(required_values)) + buf.append("") + + for node in self.ba: + if not node.init: + continue + + line = "\tif " + indent = "\t " + + line += build_condition_string(node) + lines = break_long_line(line, indent) + buf.extend(lines) + + buf.append("\t\t__set_bit(S%i, mon->states);" % node.id) + buf.append("}") + return buf + + def fill_tracepoint_handlers_skel(self): + buff = [] + buff.append("static void handle_example_event(void *data, /* XXX: fill header */)") + buff.append("{") + buff.append("\tltl_atom_update(task, LTL_%s, true/false);" % self.atoms[0]) + buff.append("}") + buff.append("") + return '\n'.join(buff) + + def fill_tracepoint_attach_probe(self): + return "\trv_attach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_example_event);" \ + % self.name + + def fill_tracepoint_detach_helper(self): + return "\trv_detach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_sample_event);" \ + % self.name + + def fill_atoms_init(self): + buff = [] + for a in self.atoms: + buff.append("\tltl_atom_set(mon, LTL_%s, true/false);" % a) + return '\n'.join(buff) + + def fill_model_h(self): + buf = [ + "/* SPDX-License-Identifier: GPL-2.0 */", + "", + "/*", + " * C implementation of Buchi automaton, automatically generated by", + " * tools/verification/rvgen from the linear temporal logic specification.", + " * For further information, see kernel documentation:", + " * Documentation/trace/rv/linear_temporal_logic.rst", + " */", + "", + "#include ", + "", + "#define MONITOR_NAME " + self.name, + "" + ] + + buf.extend(self._fill_atoms()) + buf.append('') + + buf.extend(self._fill_atoms_to_string()) + buf.append('') + + buf.extend(self._fill_states()) + buf.append('') + + buf.extend(self._fill_start()) + buf.append('') + + buf.extend(self._fill_transitions()) + buf.append('') + + return '\n'.join(buf) + + def fill_monitor_class_type(self): + return "LTL_MON_EVENTS_ID" + + def fill_monitor_class(self): + return "ltl_monitor_id" + + def fill_main_c(self): + main_c = super().fill_main_c() + main_c = main_c.replace("%%ATOMS_INIT%%", self.fill_atoms_init()) + + return main_c diff --git a/tools/verification/dot2/dot2k_templates/Kconfig b/tools/verification/rvgen/rvgen/templates/Kconfig similarity index 100% rename from tools/verification/dot2/dot2k_templates/Kconfig rename to tools/verification/rvgen/rvgen/templates/Kconfig diff --git a/tools/verification/rvgen/rvgen/templates/container/Kconfig b/tools/verification/rvgen/rvgen/templates/container/Kconfig new file mode 100644 index 000000000000..a606111949c2 --- /dev/null +++ b/tools/verification/rvgen/rvgen/templates/container/Kconfig @@ -0,0 +1,5 @@ +config RV_MON_%%MODEL_NAME_UP%% + depends on RV + bool "%%MODEL_NAME%% monitor" + help + %%DESCRIPTION%% diff --git a/tools/verification/dot2/dot2k_templates/main_container.c b/tools/verification/rvgen/rvgen/templates/container/main.c similarity index 92% rename from tools/verification/dot2/dot2k_templates/main_container.c rename to tools/verification/rvgen/rvgen/templates/container/main.c index 89fc17cf8958..7d9b2f95c7e9 100644 --- a/tools/verification/dot2/dot2k_templates/main_container.c +++ b/tools/verification/rvgen/rvgen/templates/container/main.c @@ -21,8 +21,7 @@ struct rv_monitor rv_%%MODEL_NAME%% = { static int __init register_%%MODEL_NAME%%(void) { - rv_register_monitor(&rv_%%MODEL_NAME%%, NULL); - return 0; + return rv_register_monitor(&rv_%%MODEL_NAME%%, NULL); } static void __exit unregister_%%MODEL_NAME%%(void) diff --git a/tools/verification/dot2/dot2k_templates/main_container.h b/tools/verification/rvgen/rvgen/templates/container/main.h similarity index 100% rename from tools/verification/dot2/dot2k_templates/main_container.h rename to tools/verification/rvgen/rvgen/templates/container/main.h diff --git a/tools/verification/dot2/dot2k_templates/main.c b/tools/verification/rvgen/rvgen/templates/dot2k/main.c similarity index 96% rename from tools/verification/dot2/dot2k_templates/main.c rename to tools/verification/rvgen/rvgen/templates/dot2k/main.c index 83044a20c89a..e0fd1134bd85 100644 --- a/tools/verification/dot2/dot2k_templates/main.c +++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c @@ -74,8 +74,7 @@ static struct rv_monitor rv_%%MODEL_NAME%% = { static int __init register_%%MODEL_NAME%%(void) { - rv_register_monitor(&rv_%%MODEL_NAME%%, %%PARENT%%); - return 0; + return rv_register_monitor(&rv_%%MODEL_NAME%%, %%PARENT%%); } static void __exit unregister_%%MODEL_NAME%%(void) diff --git a/tools/verification/dot2/dot2k_templates/trace.h b/tools/verification/rvgen/rvgen/templates/dot2k/trace.h similarity index 100% rename from tools/verification/dot2/dot2k_templates/trace.h rename to tools/verification/rvgen/rvgen/templates/dot2k/trace.h diff --git a/tools/verification/rvgen/rvgen/templates/ltl2k/main.c b/tools/verification/rvgen/rvgen/templates/ltl2k/main.c new file mode 100644 index 000000000000..f85d076fbf78 --- /dev/null +++ b/tools/verification/rvgen/rvgen/templates/ltl2k/main.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "%%MODEL_NAME%%" + +/* + * XXX: include required tracepoint headers, e.g., + * #include + */ +#include +%%INCLUDE_PARENT%% + +/* + * This is the self-generated part of the monitor. Generally, there is no need + * to touch this section. + */ +#include "%%MODEL_NAME%%.h" +#include + +static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon) +{ + /* + * This is called everytime the Buchi automaton is triggered. + * + * This function could be used to fetch the atomic propositions which + * are expensive to trace. It is possible only if the atomic proposition + * does not need to be updated at precise time. + * + * It is recommended to use tracepoints and ltl_atom_update() instead. + */ +} + +static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) +{ + /* + * This should initialize as many atomic propositions as possible. + * + * @task_creation indicates whether the task is being created. This is + * false if the task is already running before the monitor is enabled. + */ +%%ATOMS_INIT%% +} + +/* + * This is the instrumentation part of the monitor. + * + * This is the section where manual work is required. Here the kernel events + * are translated into model's event. + */ +%%TRACEPOINT_HANDLERS_SKEL%% +static int enable_%%MODEL_NAME%%(void) +{ + int retval; + + retval = ltl_monitor_init(); + if (retval) + return retval; + +%%TRACEPOINT_ATTACH%% + + return 0; +} + +static void disable_%%MODEL_NAME%%(void) +{ +%%TRACEPOINT_DETACH%% + + ltl_monitor_destroy(); +} + +/* + * This is the monitor register section. + */ +static struct rv_monitor rv_%%MODEL_NAME%% = { + .name = "%%MODEL_NAME%%", + .description = "%%DESCRIPTION%%", + .enable = enable_%%MODEL_NAME%%, + .disable = disable_%%MODEL_NAME%%, +}; + +static int __init register_%%MODEL_NAME%%(void) +{ + return rv_register_monitor(&rv_%%MODEL_NAME%%, %%PARENT%%); +} + +static void __exit unregister_%%MODEL_NAME%%(void) +{ + rv_unregister_monitor(&rv_%%MODEL_NAME%%); +} + +module_init(register_%%MODEL_NAME%%); +module_exit(unregister_%%MODEL_NAME%%); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(/* TODO */); +MODULE_DESCRIPTION("%%MODEL_NAME%%: %%DESCRIPTION%%"); diff --git a/tools/verification/rvgen/rvgen/templates/ltl2k/trace.h b/tools/verification/rvgen/rvgen/templates/ltl2k/trace.h new file mode 100644 index 000000000000..49394c4b0f1c --- /dev/null +++ b/tools/verification/rvgen/rvgen/templates/ltl2k/trace.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Snippet to be included in rv_trace.h + */ + +#ifdef CONFIG_RV_MON_%%MODEL_NAME_UP%% +DEFINE_EVENT(event_%%MONITOR_CLASS%%, event_%%MODEL_NAME%%, + TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next), + TP_ARGS(task, states, atoms, next)); +DEFINE_EVENT(error_%%MONITOR_CLASS%%, error_%%MODEL_NAME%%, + TP_PROTO(struct task_struct *task), + TP_ARGS(task)); +#endif /* CONFIG_RV_MON_%%MODEL_NAME_UP%% */ diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c index d14ffc7513ee..02bc6b00d76c 100644 --- a/virt/kvm/dirty_ring.c +++ b/virt/kvm/dirty_ring.c @@ -55,9 +55,6 @@ static void kvm_reset_dirty_gfn(struct kvm *kvm, u32 slot, u64 offset, u64 mask) struct kvm_memory_slot *memslot; int as_id, id; - if (!mask) - return; - as_id = slot >> 16; id = (u16)slot; @@ -105,19 +102,38 @@ static inline bool kvm_dirty_gfn_harvested(struct kvm_dirty_gfn *gfn) return smp_load_acquire(&gfn->flags) & KVM_DIRTY_GFN_F_RESET; } -int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring) +int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring, + int *nr_entries_reset) { + /* + * To minimize mmu_lock contention, batch resets for harvested entries + * whose gfns are in the same slot, and are within N frame numbers of + * each other, where N is the number of bits in an unsigned long. For + * simplicity, process the current set of entries when the next entry + * can't be included in the batch. + * + * Track the current batch slot, the gfn offset into the slot for the + * batch, and the bitmask of gfns that need to be reset (relative to + * offset). Note, the offset may be adjusted backwards, e.g. so that + * a sequence of gfns X, X-1, ... X-N-1 can be batched. + */ u32 cur_slot, next_slot; u64 cur_offset, next_offset; - unsigned long mask; - int count = 0; + unsigned long mask = 0; struct kvm_dirty_gfn *entry; - bool first_round = true; - /* This is only needed to make compilers happy */ - cur_slot = cur_offset = mask = 0; + /* + * Ensure concurrent calls to KVM_RESET_DIRTY_RINGS are serialized, + * e.g. so that KVM fully resets all entries processed by a given call + * before returning to userspace. Holding slots_lock also protects + * the various memslot accesses. + */ + lockdep_assert_held(&kvm->slots_lock); + + while (likely((*nr_entries_reset) < INT_MAX)) { + if (signal_pending(current)) + return -EINTR; - while (true) { entry = &ring->dirty_gfns[ring->reset_index & (ring->size - 1)]; if (!kvm_dirty_gfn_harvested(entry)) @@ -130,35 +146,64 @@ int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring) kvm_dirty_gfn_set_invalid(entry); ring->reset_index++; - count++; - /* - * Try to coalesce the reset operations when the guest is - * scanning pages in the same slot. - */ - if (!first_round && next_slot == cur_slot) { - s64 delta = next_offset - cur_offset; + (*nr_entries_reset)++; - if (delta >= 0 && delta < BITS_PER_LONG) { - mask |= 1ull << delta; - continue; + if (mask) { + /* + * While the size of each ring is fixed, it's possible + * for the ring to be constantly re-dirtied/harvested + * while the reset is in-progress (the hard limit exists + * only to guard against the count becoming negative). + */ + cond_resched(); + + /* + * Try to coalesce the reset operations when the guest + * is scanning pages in the same slot. + */ + if (next_slot == cur_slot) { + s64 delta = next_offset - cur_offset; + + if (delta >= 0 && delta < BITS_PER_LONG) { + mask |= 1ull << delta; + continue; + } + + /* Backwards visit, careful about overflows! */ + if (delta > -BITS_PER_LONG && delta < 0 && + (mask << -delta >> -delta) == mask) { + cur_offset = next_offset; + mask = (mask << -delta) | 1; + continue; + } } - /* Backwards visit, careful about overflows! */ - if (delta > -BITS_PER_LONG && delta < 0 && - (mask << -delta >> -delta) == mask) { - cur_offset = next_offset; - mask = (mask << -delta) | 1; - continue; - } + /* + * Reset the slot for all the harvested entries that + * have been gathered, but not yet fully processed. + */ + kvm_reset_dirty_gfn(kvm, cur_slot, cur_offset, mask); } - kvm_reset_dirty_gfn(kvm, cur_slot, cur_offset, mask); + + /* + * The current slot was reset or this is the first harvested + * entry, (re)initialize the metadata. + */ cur_slot = next_slot; cur_offset = next_offset; mask = 1; - first_round = false; } - kvm_reset_dirty_gfn(kvm, cur_slot, cur_offset, mask); + /* + * Perform a final reset if there are harvested entries that haven't + * been processed, which is guaranteed if at least one harvested was + * found. The loop only performs a reset when the "next" entry can't + * be batched with the "current" entry(s), and that reset processes the + * _current_ entry(s); i.e. the last harvested entry, a.k.a. next, will + * always be left pending. + */ + if (mask) + kvm_reset_dirty_gfn(kvm, cur_slot, cur_offset, mask); /* * The request KVM_REQ_DIRTY_RING_SOFT_FULL will be cleared @@ -167,7 +212,7 @@ int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring) trace_kvm_dirty_ring_reset(ring); - return count; + return 0; } void kvm_dirty_ring_push(struct kvm_vcpu *vcpu, u32 slot, u64 offset) diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 11e5d1e3f12e..6b1133a6617f 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -204,6 +204,11 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned mode, int sync, void *key) int ret = 0; if (flags & EPOLLIN) { + /* + * WARNING: Do NOT take irqfds.lock in any path except EPOLLHUP, + * as KVM holds irqfds.lock when registering the irqfd with the + * eventfd. + */ u64 cnt; eventfd_ctx_do_read(irqfd->eventfd, &cnt); @@ -225,6 +230,11 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned mode, int sync, void *key) /* The eventfd is closing, detach from KVM */ unsigned long iflags; + /* + * Taking irqfds.lock is safe here, as KVM holds a reference to + * the eventfd when registering the irqfd, i.e. this path can't + * be reached while kvm_irqfd_add() is running. + */ spin_lock_irqsave(&kvm->irqfds.lock, iflags); /* @@ -245,22 +255,14 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned mode, int sync, void *key) return ret; } -static void -irqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh, - poll_table *pt) -{ - struct kvm_kernel_irqfd *irqfd = - container_of(pt, struct kvm_kernel_irqfd, pt); - add_wait_queue_priority(wqh, &irqfd->wait); -} - -/* Must be called under irqfds.lock */ static void irqfd_update(struct kvm *kvm, struct kvm_kernel_irqfd *irqfd) { struct kvm_kernel_irq_routing_entry *e; struct kvm_kernel_irq_routing_entry entries[KVM_NR_IRQCHIPS]; int n_entries; + lockdep_assert_held(&kvm->irqfds.lock); + n_entries = kvm_irq_map_gsi(kvm, entries, irqfd->gsi); write_seqcount_begin(&irqfd->irq_entry_sc); @@ -274,6 +276,63 @@ static void irqfd_update(struct kvm *kvm, struct kvm_kernel_irqfd *irqfd) write_seqcount_end(&irqfd->irq_entry_sc); } +struct kvm_irqfd_pt { + struct kvm_kernel_irqfd *irqfd; + struct kvm *kvm; + poll_table pt; + int ret; +}; + +static void kvm_irqfd_register(struct file *file, wait_queue_head_t *wqh, + poll_table *pt) +{ + struct kvm_irqfd_pt *p = container_of(pt, struct kvm_irqfd_pt, pt); + struct kvm_kernel_irqfd *irqfd = p->irqfd; + struct kvm *kvm = p->kvm; + + /* + * Note, irqfds.lock protects the irqfd's irq_entry, i.e. its routing, + * and irqfds.items. It does NOT protect registering with the eventfd. + */ + spin_lock_irq(&kvm->irqfds.lock); + + /* + * Initialize the routing information prior to adding the irqfd to the + * eventfd's waitqueue, as irqfd_wakeup() can be invoked as soon as the + * irqfd is registered. + */ + irqfd_update(kvm, irqfd); + + /* + * Add the irqfd as a priority waiter on the eventfd, with a custom + * wake-up handler, so that KVM *and only KVM* is notified whenever the + * underlying eventfd is signaled. + */ + init_waitqueue_func_entry(&irqfd->wait, irqfd_wakeup); + + /* + * Temporarily lie to lockdep about holding irqfds.lock to avoid a + * false positive regarding potential deadlock with irqfd_wakeup() + * (see irqfd_wakeup() for details). + * + * Adding to the wait queue will fail if there is already a priority + * waiter, i.e. if the eventfd is associated with another irqfd (in any + * VM). Note, kvm_irqfd_deassign() waits for all in-flight shutdown + * jobs to complete, i.e. ensures the irqfd has been removed from the + * eventfd's waitqueue before returning to userspace. + */ + spin_release(&kvm->irqfds.lock.dep_map, _RET_IP_); + p->ret = add_wait_queue_priority_exclusive(wqh, &irqfd->wait); + spin_acquire(&kvm->irqfds.lock.dep_map, 0, 0, _RET_IP_); + if (p->ret) + goto out; + + list_add_tail(&irqfd->list, &kvm->irqfds.items); + +out: + spin_unlock_irq(&kvm->irqfds.lock); +} + #if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS) void __attribute__((weak)) kvm_arch_irq_bypass_stop( struct irq_bypass_consumer *cons) @@ -285,26 +344,20 @@ void __attribute__((weak)) kvm_arch_irq_bypass_start( { } -int __attribute__((weak)) kvm_arch_update_irqfd_routing( - struct kvm *kvm, unsigned int host_irq, - uint32_t guest_irq, bool set) +void __weak kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd, + struct kvm_kernel_irq_routing_entry *old, + struct kvm_kernel_irq_routing_entry *new) { - return 0; -} -bool __attribute__((weak)) kvm_arch_irqfd_route_changed( - struct kvm_kernel_irq_routing_entry *old, - struct kvm_kernel_irq_routing_entry *new) -{ - return true; } #endif static int kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) { - struct kvm_kernel_irqfd *irqfd, *tmp; + struct kvm_kernel_irqfd *irqfd; struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL; + struct kvm_irqfd_pt irqfd_pt; int ret; __poll_t events; int idx; @@ -390,57 +443,54 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) } /* - * Install our own custom wake-up handling so we are notified via - * a callback whenever someone signals the underlying eventfd + * Set the irqfd routing and add it to KVM's list before registering + * the irqfd with the eventfd, so that the routing information is valid + * and stays valid, e.g. if there are GSI routing changes, prior to + * making the irqfd visible, i.e. before it might be signaled. + * + * Note, holding SRCU ensures a stable read of routing information, and + * also prevents irqfd_shutdown() from freeing the irqfd before it's + * fully initialized. */ - init_waitqueue_func_entry(&irqfd->wait, irqfd_wakeup); - init_poll_funcptr(&irqfd->pt, irqfd_ptable_queue_proc); - - spin_lock_irq(&kvm->irqfds.lock); - - ret = 0; - list_for_each_entry(tmp, &kvm->irqfds.items, list) { - if (irqfd->eventfd != tmp->eventfd) - continue; - /* This fd is used for another irq already. */ - ret = -EBUSY; - spin_unlock_irq(&kvm->irqfds.lock); - goto fail; - } - idx = srcu_read_lock(&kvm->irq_srcu); - irqfd_update(kvm, irqfd); - - list_add_tail(&irqfd->list, &kvm->irqfds.items); - - spin_unlock_irq(&kvm->irqfds.lock); /* - * Check if there was an event already pending on the eventfd - * before we registered, and trigger it as if we didn't miss it. + * Register the irqfd with the eventfd by polling on the eventfd, and + * simultaneously and the irqfd to KVM's list. If there was en event + * pending on the eventfd prior to registering, manually trigger IRQ + * injection. */ - events = vfs_poll(fd_file(f), &irqfd->pt); + irqfd_pt.irqfd = irqfd; + irqfd_pt.kvm = kvm; + init_poll_funcptr(&irqfd_pt.pt, kvm_irqfd_register); + + events = vfs_poll(fd_file(f), &irqfd_pt.pt); + + ret = irqfd_pt.ret; + if (ret) + goto fail_poll; if (events & EPOLLIN) schedule_work(&irqfd->inject); #if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS) if (kvm_arch_has_irq_bypass()) { - irqfd->consumer.token = (void *)irqfd->eventfd; irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer; irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer; irqfd->consumer.stop = kvm_arch_irq_bypass_stop; irqfd->consumer.start = kvm_arch_irq_bypass_start; - ret = irq_bypass_register_consumer(&irqfd->consumer); + ret = irq_bypass_register_consumer(&irqfd->consumer, irqfd->eventfd); if (ret) - pr_info("irq bypass consumer (token %p) registration fails: %d\n", - irqfd->consumer.token, ret); + pr_info("irq bypass consumer (eventfd %p) registration fails: %d\n", + irqfd->eventfd, ret); } #endif srcu_read_unlock(&kvm->irq_srcu, idx); return 0; +fail_poll: + srcu_read_unlock(&kvm->irq_srcu, idx); fail: if (irqfd->resampler) irqfd_resampler_shutdown(irqfd); @@ -617,13 +667,8 @@ void kvm_irq_routing_update(struct kvm *kvm) irqfd_update(kvm, irqfd); #if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS) - if (irqfd->producer && - kvm_arch_irqfd_route_changed(&old, &irqfd->irq_entry)) { - int ret = kvm_arch_update_irqfd_routing( - irqfd->kvm, irqfd->producer->irq, - irqfd->gsi, 1); - WARN_ON(ret); - } + if (irqfd->producer) + kvm_arch_update_irqfd_routing(irqfd, &old, &irqfd->irq_entry); #endif } diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index b2aa6bf24d3a..7d85cc33c0bb 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -382,23 +382,12 @@ static const struct address_space_operations kvm_gmem_aops = { #endif }; -static int kvm_gmem_getattr(struct mnt_idmap *idmap, const struct path *path, - struct kstat *stat, u32 request_mask, - unsigned int query_flags) -{ - struct inode *inode = path->dentry->d_inode; - - generic_fillattr(idmap, request_mask, inode, stat); - return 0; -} - static int kvm_gmem_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { return -EINVAL; } static const struct inode_operations kvm_gmem_iops = { - .getattr = kvm_gmem_getattr, .setattr = kvm_gmem_setattr, }; diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c index 162d8ed889f2..6ccabfd32287 100644 --- a/virt/kvm/irqchip.c +++ b/virt/kvm/irqchip.c @@ -222,8 +222,6 @@ int kvm_set_irq_routing(struct kvm *kvm, kvm_arch_irq_routing_update(kvm); mutex_unlock(&kvm->irq_lock); - kvm_arch_post_irq_routing_update(kvm); - synchronize_srcu_expedited(&kvm->irq_srcu); new = old; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index eec82775c5bf..6c07dd423458 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2558,9 +2558,11 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, entry = attributes ? xa_mk_value(attributes) : NULL; + trace_kvm_vm_set_mem_attributes(start, end, attributes); + mutex_lock(&kvm->slots_lock); - /* Nothing to do if the entire range as the desired attributes. */ + /* Nothing to do if the entire range has the desired attributes. */ if (kvm_range_has_memory_attributes(kvm, start, end, ~0, attributes)) goto out_unlock; @@ -2572,6 +2574,8 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, r = xa_reserve(&kvm->mem_attr_array, i, GFP_KERNEL_ACCOUNT); if (r) goto out_unlock; + + cond_resched(); } kvm_handle_gfn_range(kvm, &pre_set_range); @@ -2580,6 +2584,7 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, r = xa_err(xa_store(&kvm->mem_attr_array, i, entry, GFP_KERNEL_ACCOUNT)); KVM_BUG_ON(r, kvm); + cond_resched(); } kvm_handle_gfn_range(kvm, &post_set_range); @@ -4962,15 +4967,18 @@ static int kvm_vm_ioctl_reset_dirty_pages(struct kvm *kvm) { unsigned long i; struct kvm_vcpu *vcpu; - int cleared = 0; + int cleared = 0, r; if (!kvm->dirty_ring_size) return -EINVAL; mutex_lock(&kvm->slots_lock); - kvm_for_each_vcpu(i, vcpu, kvm) - cleared += kvm_dirty_ring_reset(vcpu->kvm, &vcpu->dirty_ring); + kvm_for_each_vcpu(i, vcpu, kvm) { + r = kvm_dirty_ring_reset(vcpu->kvm, &vcpu->dirty_ring, &cleared); + if (r) + break; + } mutex_unlock(&kvm->slots_lock); diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 196a102e34fb..be50514bbd11 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -175,7 +175,6 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd) kvf->file = get_file(filp); list_add_tail(&kvf->node, &kv->file_list); - kvm_arch_start_assignment(dev->kvm); kvm_vfio_file_set_kvm(kvf->file, dev->kvm); kvm_vfio_update_coherency(dev); @@ -205,7 +204,6 @@ static int kvm_vfio_file_del(struct kvm_device *dev, unsigned int fd) continue; list_del(&kvf->node); - kvm_arch_end_assignment(dev->kvm); #ifdef CONFIG_SPAPR_TCE_IOMMU kvm_spapr_tce_release_vfio_group(dev->kvm, kvf); #endif @@ -336,7 +334,6 @@ static void kvm_vfio_release(struct kvm_device *dev) fput(kvf->file); list_del(&kvf->node); kfree(kvf); - kvm_arch_end_assignment(dev->kvm); } kvm_vfio_update_coherency(dev); diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c index 28fda42e471b..62c160200be9 100644 --- a/virt/lib/irqbypass.c +++ b/virt/lib/irqbypass.c @@ -22,8 +22,8 @@ MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("IRQ bypass manager utility module"); -static LIST_HEAD(producers); -static LIST_HEAD(consumers); +static DEFINE_XARRAY(producers); +static DEFINE_XARRAY(consumers); static DEFINE_MUTEX(lock); /* @lock must be held when calling connect */ @@ -51,6 +51,10 @@ static int __connect(struct irq_bypass_producer *prod, if (prod->start) prod->start(prod); + if (!ret) { + prod->consumer = cons; + cons->producer = prod; + } return ret; } @@ -72,56 +76,49 @@ static void __disconnect(struct irq_bypass_producer *prod, cons->start(cons); if (prod->start) prod->start(prod); + + prod->consumer = NULL; + cons->producer = NULL; } /** * irq_bypass_register_producer - register IRQ bypass producer * @producer: pointer to producer structure + * @eventfd: pointer to the eventfd context associated with the producer + * @irq: Linux IRQ number of the underlying producer device * - * Add the provided IRQ producer to the list of producers and connect - * with any matching token found on the IRQ consumers list. + * Add the provided IRQ producer to the set of producers and connect with the + * consumer with a matching eventfd, if one exists. */ -int irq_bypass_register_producer(struct irq_bypass_producer *producer) +int irq_bypass_register_producer(struct irq_bypass_producer *producer, + struct eventfd_ctx *eventfd, int irq) { - struct irq_bypass_producer *tmp; + unsigned long index = (unsigned long)eventfd; struct irq_bypass_consumer *consumer; int ret; - if (!producer->token) + if (WARN_ON_ONCE(producer->eventfd)) return -EINVAL; - might_sleep(); + producer->irq = irq; - if (!try_module_get(THIS_MODULE)) - return -ENODEV; + guard(mutex)(&lock); - mutex_lock(&lock); + ret = xa_insert(&producers, index, producer, GFP_KERNEL); + if (ret) + return ret; - list_for_each_entry(tmp, &producers, node) { - if (tmp->token == producer->token) { - ret = -EBUSY; - goto out_err; + consumer = xa_load(&consumers, index); + if (consumer) { + ret = __connect(producer, consumer); + if (ret) { + WARN_ON_ONCE(xa_erase(&producers, index) != producer); + return ret; } } - list_for_each_entry(consumer, &consumers, node) { - if (consumer->token == producer->token) { - ret = __connect(producer, consumer); - if (ret) - goto out_err; - break; - } - } - - list_add(&producer->node, &producers); - - mutex_unlock(&lock); - + producer->eventfd = eventfd; return 0; -out_err: - mutex_unlock(&lock); - module_put(THIS_MODULE); - return ret; } EXPORT_SYMBOL_GPL(irq_bypass_register_producer); @@ -129,95 +126,65 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_producer); * irq_bypass_unregister_producer - unregister IRQ bypass producer * @producer: pointer to producer structure * - * Remove a previously registered IRQ producer from the list of producers - * and disconnect it from any connected IRQ consumer. + * Remove a previously registered IRQ producer (note, it's safe to call this + * even if registration was unsuccessful). Disconnect from the associated + * consumer, if one exists. */ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer) { - struct irq_bypass_producer *tmp; - struct irq_bypass_consumer *consumer; + unsigned long index = (unsigned long)producer->eventfd; - if (!producer->token) + if (!producer->eventfd) return; - might_sleep(); + guard(mutex)(&lock); - if (!try_module_get(THIS_MODULE)) - return; /* nothing in the list anyway */ + if (producer->consumer) + __disconnect(producer, producer->consumer); - mutex_lock(&lock); - - list_for_each_entry(tmp, &producers, node) { - if (tmp->token != producer->token) - continue; - - list_for_each_entry(consumer, &consumers, node) { - if (consumer->token == producer->token) { - __disconnect(producer, consumer); - break; - } - } - - list_del(&producer->node); - module_put(THIS_MODULE); - break; - } - - mutex_unlock(&lock); - - module_put(THIS_MODULE); + WARN_ON_ONCE(xa_erase(&producers, index) != producer); + producer->eventfd = NULL; } EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer); /** * irq_bypass_register_consumer - register IRQ bypass consumer * @consumer: pointer to consumer structure + * @eventfd: pointer to the eventfd context associated with the consumer * - * Add the provided IRQ consumer to the list of consumers and connect - * with any matching token found on the IRQ producer list. + * Add the provided IRQ consumer to the set of consumers and connect with the + * producer with a matching eventfd, if one exists. */ -int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer) +int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer, + struct eventfd_ctx *eventfd) { - struct irq_bypass_consumer *tmp; + unsigned long index = (unsigned long)eventfd; struct irq_bypass_producer *producer; int ret; - if (!consumer->token || - !consumer->add_producer || !consumer->del_producer) + if (WARN_ON_ONCE(consumer->eventfd)) return -EINVAL; - might_sleep(); + if (!consumer->add_producer || !consumer->del_producer) + return -EINVAL; - if (!try_module_get(THIS_MODULE)) - return -ENODEV; + guard(mutex)(&lock); - mutex_lock(&lock); + ret = xa_insert(&consumers, index, consumer, GFP_KERNEL); + if (ret) + return ret; - list_for_each_entry(tmp, &consumers, node) { - if (tmp->token == consumer->token || tmp == consumer) { - ret = -EBUSY; - goto out_err; + producer = xa_load(&producers, index); + if (producer) { + ret = __connect(producer, consumer); + if (ret) { + WARN_ON_ONCE(xa_erase(&consumers, index) != consumer); + return ret; } } - list_for_each_entry(producer, &producers, node) { - if (producer->token == consumer->token) { - ret = __connect(producer, consumer); - if (ret) - goto out_err; - break; - } - } - - list_add(&consumer->node, &consumers); - - mutex_unlock(&lock); - + consumer->eventfd = eventfd; return 0; -out_err: - mutex_unlock(&lock); - module_put(THIS_MODULE); - return ret; } EXPORT_SYMBOL_GPL(irq_bypass_register_consumer); @@ -225,42 +192,23 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_consumer); * irq_bypass_unregister_consumer - unregister IRQ bypass consumer * @consumer: pointer to consumer structure * - * Remove a previously registered IRQ consumer from the list of consumers - * and disconnect it from any connected IRQ producer. + * Remove a previously registered IRQ consumer (note, it's safe to call this + * even if registration was unsuccessful). Disconnect from the associated + * producer, if one exists. */ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer) { - struct irq_bypass_consumer *tmp; - struct irq_bypass_producer *producer; + unsigned long index = (unsigned long)consumer->eventfd; - if (!consumer->token) + if (!consumer->eventfd) return; - might_sleep(); + guard(mutex)(&lock); - if (!try_module_get(THIS_MODULE)) - return; /* nothing in the list anyway */ + if (consumer->producer) + __disconnect(consumer->producer, consumer); - mutex_lock(&lock); - - list_for_each_entry(tmp, &consumers, node) { - if (tmp != consumer) - continue; - - list_for_each_entry(producer, &producers, node) { - if (producer->token == consumer->token) { - __disconnect(producer, consumer); - break; - } - } - - list_del(&consumer->node); - module_put(THIS_MODULE); - break; - } - - mutex_unlock(&lock); - - module_put(THIS_MODULE); + WARN_ON_ONCE(xa_erase(&consumers, index) != consumer); + consumer->eventfd = NULL; } EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);